From c4e8d54f595b01d30e9b2793413d44f83deb7450 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 15 Mar 2011 15:29:21 -0700 Subject: [PATCH 0001/2556] dcache.c: create helper function for duplicated functionality commit c826cb7dfce80512c26c984350077a25046bd215 upstream. This creates a helper function for he "try to ascend into the parent directory" case, which was written out in triplicate before. With all the locking and subtle sequence number stuff, we really don't want to duplicate that kind of code. Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/dcache.c | 88 ++++++++++++++++++++++------------------------------- 1 file changed, 37 insertions(+), 51 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 611ffe928c03c..361882a14ccbc 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1011,6 +1011,34 @@ void shrink_dcache_for_umount(struct super_block *sb) } } +/* + * This tries to ascend one level of parenthood, but + * we can race with renaming, so we need to re-check + * the parenthood after dropping the lock and check + * that the sequence number still matches. + */ +static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq) +{ + struct dentry *new = old->d_parent; + + rcu_read_lock(); + spin_unlock(&old->d_lock); + spin_lock(&new->d_lock); + + /* + * might go back up the wrong parent if we have had a rename + * or deletion + */ + if (new != old->d_parent || + (!locked && read_seqretry(&rename_lock, seq))) { + spin_unlock(&new->d_lock); + new = NULL; + } + rcu_read_unlock(); + return new; +} + + /* * Search for at least 1 mount point in the dentry's subdirs. * We descend to the next level whenever the d_subdirs @@ -1066,24 +1094,10 @@ int have_submounts(struct dentry *parent) * All done at this level ... ascend and resume the search. */ if (this_parent != parent) { - struct dentry *tmp; - struct dentry *child; - - tmp = this_parent->d_parent; - rcu_read_lock(); - spin_unlock(&this_parent->d_lock); - child = this_parent; - this_parent = tmp; - spin_lock(&this_parent->d_lock); - /* might go back up the wrong parent if we have had a rename - * or deletion */ - if (this_parent != child->d_parent || - (!locked && read_seqretry(&rename_lock, seq))) { - spin_unlock(&this_parent->d_lock); - rcu_read_unlock(); + struct dentry *child = this_parent; + this_parent = try_to_ascend(this_parent, locked, seq); + if (!this_parent) goto rename_retry; - } - rcu_read_unlock(); next = child->d_u.d_child.next; goto resume; } @@ -1181,24 +1195,10 @@ static int select_parent(struct dentry * parent) * All done at this level ... ascend and resume the search. */ if (this_parent != parent) { - struct dentry *tmp; - struct dentry *child; - - tmp = this_parent->d_parent; - rcu_read_lock(); - spin_unlock(&this_parent->d_lock); - child = this_parent; - this_parent = tmp; - spin_lock(&this_parent->d_lock); - /* might go back up the wrong parent if we have had a rename - * or deletion */ - if (this_parent != child->d_parent || - (!locked && read_seqretry(&rename_lock, seq))) { - spin_unlock(&this_parent->d_lock); - rcu_read_unlock(); + struct dentry *child = this_parent; + this_parent = try_to_ascend(this_parent, locked, seq); + if (!this_parent) goto rename_retry; - } - rcu_read_unlock(); next = child->d_u.d_child.next; goto resume; } @@ -2942,28 +2942,14 @@ void d_genocide(struct dentry *root) spin_unlock(&dentry->d_lock); } if (this_parent != root) { - struct dentry *tmp; - struct dentry *child; - - tmp = this_parent->d_parent; + struct dentry *child = this_parent; if (!(this_parent->d_flags & DCACHE_GENOCIDE)) { this_parent->d_flags |= DCACHE_GENOCIDE; this_parent->d_count--; } - rcu_read_lock(); - spin_unlock(&this_parent->d_lock); - child = this_parent; - this_parent = tmp; - spin_lock(&this_parent->d_lock); - /* might go back up the wrong parent if we have had a rename - * or deletion */ - if (this_parent != child->d_parent || - (!locked && read_seqretry(&rename_lock, seq))) { - spin_unlock(&this_parent->d_lock); - rcu_read_unlock(); + this_parent = try_to_ascend(this_parent, locked, seq); + if (!this_parent) goto rename_retry; - } - rcu_read_unlock(); next = child->d_u.d_child.next; goto resume; } From 2c048b5dbd657781e9a37e8efa5c42d65c119057 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 15 Mar 2011 13:36:43 -0400 Subject: [PATCH 0002/2556] VFS: Fix the nfs sillyrename regression in kernel 2.6.38 commit c83ce989cb5ff86575821992ea82c4df5c388ebc upstream. The new vfs locking scheme introduced in 2.6.38 breaks NFS sillyrename because the latter relies on being able to determine the parent directory of the dentry in the ->iput() callback in order to send the appropriate unlink rpc call. Looking at the code that cares about races with dput(), there doesn't seem to be anything that specifically uses d_parent as a test for whether or not there is a race: - __d_lookup_rcu(), __d_lookup() all test for d_hashed() after d_parent - shrink_dcache_for_umount() is safe since nothing else can rearrange the dentries in that super block. - have_submount(), select_parent() and d_genocide() can test for a deletion if we set the DCACHE_DISCONNECTED flag when the dentry is removed from the parent's d_subdirs list. Signed-off-by: Trond Myklebust Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/dcache.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/dcache.c b/fs/dcache.c index 361882a14ccbc..a39fe47c466f7 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -296,8 +296,12 @@ static struct dentry *d_kill(struct dentry *dentry, struct dentry *parent) __releases(parent->d_lock) __releases(dentry->d_inode->i_lock) { - dentry->d_parent = NULL; list_del(&dentry->d_u.d_child); + /* + * Inform try_to_ascend() that we are no longer attached to the + * dentry tree + */ + dentry->d_flags |= DCACHE_DISCONNECTED; if (parent) spin_unlock(&parent->d_lock); dentry_iput(dentry); @@ -1030,6 +1034,7 @@ static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq * or deletion */ if (new != old->d_parent || + (old->d_flags & DCACHE_DISCONNECTED) || (!locked && read_seqretry(&rename_lock, seq))) { spin_unlock(&new->d_lock); new = NULL; From 1883e22e58194ef67349ff23333defebaaafa530 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 10 Feb 2011 21:26:13 -0500 Subject: [PATCH 0003/2556] ftrace: Fix memory leak with function graph and cpu hotplug commit 868baf07b1a259f5f3803c1dc2777b6c358f83cf upstream. When the fuction graph tracer starts, it needs to make a special stack for each task to save the real return values of the tasks. All running tasks have this stack created, as well as any new tasks. On CPU hot plug, the new idle task will allocate a stack as well when init_idle() is called. The problem is that cpu hotplug does not create a new idle_task. Instead it uses the idle task that existed when the cpu went down. ftrace_graph_init_task() will add a new ret_stack to the task that is given to it. Because a clone will make the task have a stack of its parent it does not check if the task's ret_stack is already NULL or not. When the CPU hotplug code starts a CPU up again, it will allocate a new stack even though one already existed for it. The solution is to treat the idle_task specially. In fact, the function_graph code already does, just not at init_idle(). Instead of using the ftrace_graph_init_task() for the idle task, which that function expects the task to be a clone, have a separate ftrace_graph_init_idle_task(). Also, we will create a per_cpu ret_stack that is used by the idle task. When we call ftrace_graph_init_idle_task() it will check if the idle task's ret_stack is NULL, if it is, then it will assign it the per_cpu ret_stack. Reported-by: Benjamin Herrenschmidt Suggested-by: Peter Zijlstra Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- include/linux/ftrace.h | 2 ++ kernel/sched.c | 2 +- kernel/trace/ftrace.c | 52 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index dcd6a7c3a4358..ca29e03c1fac0 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -428,6 +428,7 @@ extern void unregister_ftrace_graph(void); extern void ftrace_graph_init_task(struct task_struct *t); extern void ftrace_graph_exit_task(struct task_struct *t); +extern void ftrace_graph_init_idle_task(struct task_struct *t, int cpu); static inline int task_curr_ret_stack(struct task_struct *t) { @@ -451,6 +452,7 @@ static inline void unpause_graph_tracing(void) static inline void ftrace_graph_init_task(struct task_struct *t) { } static inline void ftrace_graph_exit_task(struct task_struct *t) { } +static inline void ftrace_graph_init_idle_task(struct task_struct *t, int cpu) { } static inline int register_ftrace_graph(trace_func_graph_ret_t retfunc, trace_func_graph_ent_t entryfunc) diff --git a/kernel/sched.c b/kernel/sched.c index 42eab5a8437d7..c164920c8ce8c 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -5572,7 +5572,7 @@ void __cpuinit init_idle(struct task_struct *idle, int cpu) * The idle tasks have their own, simple scheduling class: */ idle->sched_class = &idle_sched_class; - ftrace_graph_init_task(idle); + ftrace_graph_init_idle_task(idle, cpu); } /* diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index f3dadae83883e..888b611897d37 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -3328,7 +3328,7 @@ static int start_graph_tracing(void) /* The cpu_boot init_task->ret_stack will never be freed */ for_each_online_cpu(cpu) { if (!idle_task(cpu)->ret_stack) - ftrace_graph_init_task(idle_task(cpu)); + ftrace_graph_init_idle_task(idle_task(cpu), cpu); } do { @@ -3418,6 +3418,49 @@ void unregister_ftrace_graph(void) mutex_unlock(&ftrace_lock); } +static DEFINE_PER_CPU(struct ftrace_ret_stack *, idle_ret_stack); + +static void +graph_init_task(struct task_struct *t, struct ftrace_ret_stack *ret_stack) +{ + atomic_set(&t->tracing_graph_pause, 0); + atomic_set(&t->trace_overrun, 0); + t->ftrace_timestamp = 0; + /* make curr_ret_stack visable before we add the ret_stack */ + smp_wmb(); + t->ret_stack = ret_stack; +} + +/* + * Allocate a return stack for the idle task. May be the first + * time through, or it may be done by CPU hotplug online. + */ +void ftrace_graph_init_idle_task(struct task_struct *t, int cpu) +{ + t->curr_ret_stack = -1; + /* + * The idle task has no parent, it either has its own + * stack or no stack at all. + */ + if (t->ret_stack) + WARN_ON(t->ret_stack != per_cpu(idle_ret_stack, cpu)); + + if (ftrace_graph_active) { + struct ftrace_ret_stack *ret_stack; + + ret_stack = per_cpu(idle_ret_stack, cpu); + if (!ret_stack) { + ret_stack = kmalloc(FTRACE_RETFUNC_DEPTH + * sizeof(struct ftrace_ret_stack), + GFP_KERNEL); + if (!ret_stack) + return; + per_cpu(idle_ret_stack, cpu) = ret_stack; + } + graph_init_task(t, ret_stack); + } +} + /* Allocate a return stack for newly created task */ void ftrace_graph_init_task(struct task_struct *t) { @@ -3433,12 +3476,7 @@ void ftrace_graph_init_task(struct task_struct *t) GFP_KERNEL); if (!ret_stack) return; - atomic_set(&t->tracing_graph_pause, 0); - atomic_set(&t->trace_overrun, 0); - t->ftrace_timestamp = 0; - /* make curr_ret_stack visable before we add the ret_stack */ - smp_wmb(); - t->ret_stack = ret_stack; + graph_init_task(t, ret_stack); } } From d59c7a3611a0873d10d86fd287ded8ced69c875c Mon Sep 17 00:00:00 2001 From: Kamal Mostafa Date: Thu, 3 Feb 2011 17:38:04 -0800 Subject: [PATCH 0004/2556] x86: Fix panic when handling "mem={invalid}" param commit 77eed821accf5dd962b1f13bed0680e217e49112 upstream. Avoid removing all of memory and panicing when "mem={invalid}" is specified, e.g. mem=blahblah, mem=0, or mem=nopentium (on platforms other than x86_32). Signed-off-by: Kamal Mostafa BugLink: http://bugs.launchpad.net/bugs/553464 Cc: Yinghai Lu Cc: Len Brown Cc: Rafael J. Wysocki LKML-Reference: <1296783486-23033-1-git-send-email-kamal@canonical.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/e820.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index 294f26da0c0ce..55a59d889dbd4 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c @@ -856,6 +856,9 @@ static int __init parse_memopt(char *p) userdef = 1; mem_size = memparse(p, &p); + /* don't remove all of memory when handling "mem={invalid}" param */ + if (mem_size == 0) + return -EINVAL; e820_remove_range(mem_size, ULLONG_MAX - mem_size, E820_RAM, 1); return 0; From 82c6db7f818d189a0341813b931d7de641d67532 Mon Sep 17 00:00:00 2001 From: Kamal Mostafa Date: Thu, 3 Feb 2011 17:38:05 -0800 Subject: [PATCH 0005/2556] x86: Emit "mem=nopentium ignored" warning when not supported commit 9a6d44b9adb777ca9549e88cd55bd8f2673c52a2 upstream. Emit warning when "mem=nopentium" is specified on any arch other than x86_32 (the only that arch supports it). Signed-off-by: Kamal Mostafa BugLink: http://bugs.launchpad.net/bugs/553464 Cc: Yinghai Lu Cc: Len Brown Cc: Rafael J. Wysocki LKML-Reference: <1296783486-23033-2-git-send-email-kamal@canonical.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/e820.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index 55a59d889dbd4..0b5e2b546566f 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c @@ -847,12 +847,15 @@ static int __init parse_memopt(char *p) if (!p) return -EINVAL; -#ifdef CONFIG_X86_32 if (!strcmp(p, "nopentium")) { +#ifdef CONFIG_X86_32 setup_clear_cpu_cap(X86_FEATURE_PSE); return 0; - } +#else + printk(KERN_WARNING "mem=nopentium ignored! (only supported on x86_32)\n"); + return -EINVAL; #endif + } userdef = 1; mem_size = memparse(p, &p); From a3980437d49751ccc496f16ef1a75f81338855e1 Mon Sep 17 00:00:00 2001 From: Seth Heasley Date: Fri, 11 Mar 2011 11:57:42 -0800 Subject: [PATCH 0006/2556] ahci: AHCI mode SATA patch for Intel Patsburg SATA RAID controller commit 64a3903d0885879ba8706a8bcf71c5e3e7664db2 upstream. This patch adds an updated SATA RAID DeviceID for the Intel Patsburg PCH. Signed-off-by: Seth Heasley Signed-off-by: Jeff Garzik Signed-off-by: Greg Kroah-Hartman --- drivers/ata/ahci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index b8d96ce37fc99..2010681896d8b 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -260,6 +260,7 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x1d02), board_ahci }, /* PBG AHCI */ { PCI_VDEVICE(INTEL, 0x1d04), board_ahci }, /* PBG RAID */ { PCI_VDEVICE(INTEL, 0x1d06), board_ahci }, /* PBG RAID */ + { PCI_VDEVICE(INTEL, 0x2826), board_ahci }, /* PBG RAID */ { PCI_VDEVICE(INTEL, 0x2323), board_ahci }, /* DH89xxCC AHCI */ /* JMicron 360/1/3/5/6, match class to avoid IDE function */ From bfa65173414a6a2128b9f7c8f2e873a8a7e36d66 Mon Sep 17 00:00:00 2001 From: Per Jessen Date: Tue, 8 Feb 2011 13:54:32 +0100 Subject: [PATCH 0007/2556] ahci: recognize Marvell 88se9125 PCIe SATA 6.0 Gb/s controller commit 467b41c688c79d1b5e076fbdf082f9cd5d6a000c upstream. Recognize Marvell 88SE9125 PCIe SATA 6.0 Gb/s controller. Signed-off-by: Per Jessen Signed-off-by: Jeff Garzik Signed-off-by: Greg Kroah-Hartman --- drivers/ata/ahci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 2010681896d8b..34e08f63b6889 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -384,6 +384,8 @@ static const struct pci_device_id ahci_pci_tbl[] = { .class = PCI_CLASS_STORAGE_SATA_AHCI, .class_mask = 0xffffff, .driver_data = board_ahci_yes_fbs }, /* 88se9128 */ + { PCI_DEVICE(0x1b4b, 0x9125), + .driver_data = board_ahci_yes_fbs }, /* 88se9125 */ /* Promise */ { PCI_VDEVICE(PROMISE, 0x3f20), board_ahci }, /* PDC42819 */ From bcefd0d7f34b989c254197bd57c52c848d5ca1cf Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 24 Feb 2011 19:30:37 +0100 Subject: [PATCH 0008/2556] libata: fix hotplug for drivers which don't implement LPM commit eb0e85e36b971ec31610eda7e3ff5c11c1c44785 upstream. ata_eh_analyze_serror() suppresses hotplug notifications if LPM is being used because LPM generates spurious hotplug events. It compared whether link->lpm_policy was different from ATA_LPM_MAX_POWER to determine whether LPM is enabled; however, this is incorrect as for drivers which don't implement LPM, lpm_policy is always ATA_LPM_UNKNOWN. This disabled hotplug detection for all drivers which don't implement LPM. Fix it by comparing whether lpm_policy is greater than ATA_LPM_MAX_POWER. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-eh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 17a637877d031..e16850e8d2f8a 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1618,7 +1618,7 @@ static void ata_eh_analyze_serror(struct ata_link *link) * host links. For disabled PMP links, only N bit is * considered as X bit is left at 1 for link plugging. */ - if (link->lpm_policy != ATA_LPM_MAX_POWER) + if (link->lpm_policy > ATA_LPM_MAX_POWER) hotplug_mask = 0; /* hotplug doesn't work w/ LPM */ else if (!(link->flags & ATA_LFLAG_DISABLED) || ata_is_host_link(link)) hotplug_mask = SERR_PHYRDY_CHG | SERR_DEV_XCHG; From 4d94af00d818c4bfe9ca1f051ab25d9e6b5720c0 Mon Sep 17 00:00:00 2001 From: Sean Hefty Date: Wed, 23 Feb 2011 08:11:32 -0800 Subject: [PATCH 0009/2556] RDMA/cma: Fix crash in request handlers commit 25ae21a10112875763c18b385624df713a288a05 upstream. Doug Ledford and Red Hat reported a crash when running the rdma_cm on a real-time OS. The crash has the following call trace: cm_process_work cma_req_handler cma_disable_callback rdma_create_id kzalloc init_completion cma_get_net_info cma_save_net_info cma_any_addr cma_zero_addr rdma_translate_ip rdma_copy_addr cma_acquire_dev rdma_addr_get_sgid ib_find_cached_gid cma_attach_to_dev ucma_event_handler kzalloc ib_copy_ah_attr_to_user cma_comp [ preempted ] cma_write copy_from_user ucma_destroy_id copy_from_user _ucma_find_context ucma_put_ctx ucma_free_ctx rdma_destroy_id cma_exch cma_cancel_operation rdma_node_get_transport rt_mutex_slowunlock bad_area_nosemaphore oops_enter They were able to reproduce the crash multiple times with the following details: Crash seems to always happen on the: mutex_unlock(&conn_id->handler_mutex); as conn_id looks to have been freed during this code path. An examination of the code shows that a race exists in the request handlers. When a new connection request is received, the rdma_cm allocates a new connection identifier. This identifier has a single reference count on it. If a user calls rdma_destroy_id() from another thread after receiving a callback, rdma_destroy_id will proceed to destroy the id and free the associated memory. However, the request handlers may still be in the process of running. When control returns to the request handlers, they can attempt to access the newly created identifiers. Fix this by holding a reference on the newly created rdma_cm_id until the request handler is through accessing it. Signed-off-by: Sean Hefty Acked-by: Doug Ledford Signed-off-by: Roland Dreier Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/core/cma.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 6884da24fde1e..e450c5a877276 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -1210,6 +1210,11 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event) cm_id->context = conn_id; cm_id->cm_handler = cma_ib_handler; + /* + * Protect against the user destroying conn_id from another thread + * until we're done accessing it. + */ + atomic_inc(&conn_id->refcount); ret = conn_id->id.event_handler(&conn_id->id, &event); if (!ret) { /* @@ -1222,8 +1227,10 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event) ib_send_cm_mra(cm_id, CMA_CM_MRA_SETTING, NULL, 0); mutex_unlock(&lock); mutex_unlock(&conn_id->handler_mutex); + cma_deref_id(conn_id); goto out; } + cma_deref_id(conn_id); /* Destroy the CM ID by returning a non-zero value. */ conn_id->cm_id.ib = NULL; @@ -1425,17 +1432,25 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id, event.param.conn.private_data_len = iw_event->private_data_len; event.param.conn.initiator_depth = attr.max_qp_init_rd_atom; event.param.conn.responder_resources = attr.max_qp_rd_atom; + + /* + * Protect against the user destroying conn_id from another thread + * until we're done accessing it. + */ + atomic_inc(&conn_id->refcount); ret = conn_id->id.event_handler(&conn_id->id, &event); if (ret) { /* User wants to destroy the CM ID */ conn_id->cm_id.iw = NULL; cma_exch(conn_id, CMA_DESTROYING); mutex_unlock(&conn_id->handler_mutex); + cma_deref_id(conn_id); rdma_destroy_id(&conn_id->id); goto out; } mutex_unlock(&conn_id->handler_mutex); + cma_deref_id(conn_id); out: if (dev) From cd970182abeff0e63d85674d090fb59855b89124 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 16 Mar 2011 08:04:07 -0700 Subject: [PATCH 0010/2556] Increase OSF partition limit from 8 to 18 commit 34d211a2d5df4984a35b18d8ccacbe1d10abb067 upstream. It turns out that while a maximum of 8 partitions may be what people "should" have had, you can actually fit up to 18 entries(*) in a sector. And some people clearly were taking advantage of that, like Michael Cree, who had ten partitions on one of his OSF disks. (*) The OSF partition data starts at byte offset 64 in the first sector, and the array of 16-byte partition entries start at offset 148 in the on-disk partition structure. Reported-by: Michael Cree Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/partitions/osf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/partitions/osf.c b/fs/partitions/osf.c index be03a0b08b47a..764b86a01965a 100644 --- a/fs/partitions/osf.c +++ b/fs/partitions/osf.c @@ -10,7 +10,7 @@ #include "check.h" #include "osf.h" -#define MAX_OSF_PARTITIONS 8 +#define MAX_OSF_PARTITIONS 18 int osf_partition(struct parsed_partitions *state) { From 6fee8c663b4c7253b4ee68ae2680c8c4cb25a986 Mon Sep 17 00:00:00 2001 From: Sean Hefty Date: Wed, 23 Feb 2011 08:17:40 -0800 Subject: [PATCH 0011/2556] IB/cm: Bump reference count on cm_id before invoking callback commit 29963437a48475036353b95ab142bf199adb909e upstream. When processing a SIDR REQ, the ib_cm allocates a new cm_id. The refcount of the cm_id is initialized to 1. However, cm_process_work will decrement the refcount after invoking all callbacks. The result is that the cm_id will end up with refcount set to 0 by the end of the sidr req handler. If a user tries to destroy the cm_id, the destruction will proceed, under the incorrect assumption that no other threads are referencing the cm_id. This can lead to a crash when the cm callback thread tries to access the cm_id. This problem was noticed as part of a larger investigation with kernel crashes in the rdma_cm when running on a real time OS. Signed-off-by: Sean Hefty Acked-by: Doug Ledford Signed-off-by: Roland Dreier Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/core/cm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index 64e0903091a86..1d9616be41922 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -2989,6 +2989,7 @@ static int cm_sidr_req_handler(struct cm_work *work) goto out; /* No match. */ } atomic_inc(&cur_cm_id_priv->refcount); + atomic_inc(&cm_id_priv->refcount); spin_unlock_irq(&cm.lock); cm_id_priv->id.cm_handler = cur_cm_id_priv->id.cm_handler; From b030eac99b99ba92dd6d3bcf641c3fa219b2a73e Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Tue, 15 Mar 2011 15:31:37 +0100 Subject: [PATCH 0012/2556] x86, quirk: Fix SB600 revision check commit 1d3e09a304e6c4e004ca06356578b171e8735d3c upstream. Commit 7f74f8f28a2bd9db9404f7d364e2097a0c42cc12 (x86 quirk: Fix polarity for IRQ0 pin2 override on SB800 systems) introduced a regression. It removed some SB600 specific code to determine the revision ID without adapting a corresponding revision ID check for SB600. See this mail thread: http://marc.info/?l=linux-kernel&m=129980296006380&w=2 This patch adapts the corresponding check to cover all SB600 revisions. Tested-by: Wang Lei Signed-off-by: Andreas Herrmann Cc: Andrew Morton LKML-Reference: <20110315143137.GD29499@alberich.amd.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/early-quirks.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/early-quirks.c b/arch/x86/kernel/early-quirks.c index 9efbdcc56425b..3755ef4943905 100644 --- a/arch/x86/kernel/early-quirks.c +++ b/arch/x86/kernel/early-quirks.c @@ -159,7 +159,12 @@ static void __init ati_bugs_contd(int num, int slot, int func) if (rev >= 0x40) acpi_fix_pin2_polarity = 1; - if (rev > 0x13) + /* + * SB600: revisions 0x11, 0x12, 0x13, 0x14, ... + * SB700: revisions 0x39, 0x3a, ... + * SB800: revisions 0x40, 0x41, ... + */ + if (rev >= 0x39) return; if (acpi_use_timer_override) From 1bb392f30a34dfb18d72750fd9898f71b9ed7923 Mon Sep 17 00:00:00 2001 From: "Steven J. Magnani" Date: Thu, 10 Feb 2011 12:12:13 -0600 Subject: [PATCH 0013/2556] microblaze: Fix /dev/zero corruption from __clear_user() commit 6f3946b421395ff853bc0bcdab9c26b50ebbba8f upstream. A userland read of more than PAGE_SIZE bytes from /dev/zero results in (a) not all of the bytes returned being zero, and (b) memory corruption due to zeroing of bytes beyond the user buffer. This is caused by improper constraints on the assembly __clear_user function. The constrints don't indicate to the compiler that the pointer argument is modified. Since the function is inline, this results in double-incrementing of the pointer when __clear_user() is invoked through a multi-page read() of /dev/zero. Signed-off-by: Steven J. Magnani Acked-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- arch/microblaze/include/asm/uaccess.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/microblaze/include/asm/uaccess.h b/arch/microblaze/include/asm/uaccess.h index d840f4a2d3c92..5bb95a11880d2 100644 --- a/arch/microblaze/include/asm/uaccess.h +++ b/arch/microblaze/include/asm/uaccess.h @@ -120,16 +120,16 @@ static inline unsigned long __must_check __clear_user(void __user *to, { /* normal memset with two words to __ex_table */ __asm__ __volatile__ ( \ - "1: sb r0, %2, r0;" \ + "1: sb r0, %1, r0;" \ " addik %0, %0, -1;" \ " bneid %0, 1b;" \ - " addik %2, %2, 1;" \ + " addik %1, %1, 1;" \ "2: " \ __EX_TABLE_SECTION \ ".word 1b,2b;" \ ".previous;" \ - : "=r"(n) \ - : "0"(n), "r"(to) + : "=r"(n), "=r"(to) \ + : "0"(n), "1"(to) ); return n; } From 41fa53b856d81a90f41a26135369fb8f4f901214 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Thu, 3 Mar 2011 11:01:37 -0500 Subject: [PATCH 0014/2556] x86: stop_machine_text_poke() should issue sync_core() commit 0e00f7aed6af21fc09b2a94d28bc34e449bd3a53 upstream. Intel Archiecture Software Developer's Manual section 7.1.3 specifies that a core serializing instruction such as "cpuid" should be executed on _each_ core before the new instruction is made visible. Failure to do so can lead to unspecified behavior (Intel XMC erratas include General Protection Fault in the list), so we should avoid this at all cost. This problem can affect modified code executed by interrupt handlers after interrupt are re-enabled at the end of stop_machine, because no core serializing instruction is executed between the code modification and the moment interrupts are reenabled. Because stop_machine_text_poke performs the text modification from the first CPU decrementing stop_machine_first, modified code executed in thread context is also affected by this problem. To explain why, we have to split the CPUs in two categories: the CPU that initiates the text modification (calls text_poke_smp) and all the others. The scheduler, executed on all other CPUs after stop_machine, issues an "iret" core serializing instruction, and therefore handles core serialization for all these CPUs. However, the text modification initiator can continue its execution on the same thread and access the modified text without any scheduler call. Given that the CPU that initiates the code modification is not guaranteed to be the one actually performing the code modification, it falls into the XMC errata. Q: Isn't this executed from an IPI handler, which will return with IRET (a serializing instruction) anyway? A: No, now stop_machine uses per-cpu workqueue, so that handler will be executed from worker threads. There is no iret anymore. Signed-off-by: Mathieu Desnoyers LKML-Reference: <20110303160137.GB1590@Krystal> Reviewed-by: Masami Hiramatsu Cc: Arjan van de Ven Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Andrew Morton Cc: Andi Kleen Cc: Frederic Weisbecker Signed-off-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/alternative.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index 7038b95d363f2..4db35544de738 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c @@ -620,7 +620,12 @@ static int __kprobes stop_machine_text_poke(void *data) flush_icache_range((unsigned long)p->addr, (unsigned long)p->addr + p->len); } - + /* + * Intel Archiecture Software Developer's Manual section 7.1.3 specifies + * that a core serializing instruction such as "cpuid" should be + * executed on _each_ core before the new instruction is made visible. + */ + sync_core(); return 0; } From 7cd4ad7e560c8dfa66859dd0d96cb349ee26b6fa Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 2 Mar 2011 16:54:24 +0900 Subject: [PATCH 0015/2556] TOMOYO: Fix memory leak upon file open. commit eae61f3c829439f8f9121b5cd48a14be04df451f upstream. In tomoyo_check_open_permission() since 2.6.36, TOMOYO was by error recalculating already calculated pathname when checking allow_rewrite permission. As a result, memory will leak whenever a file is opened for writing without O_APPEND flag. Also, performance will degrade because TOMOYO is calculating pathname regardless of profile configuration. This patch fixes the leak and performance degrade. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris Signed-off-by: Greg Kroah-Hartman --- security/tomoyo/file.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 9d32f182301ee..cb09f1fce9109 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -927,7 +927,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, struct path *path, const int flag) { const u8 acc_mode = ACC_MODE(flag); - int error = -ENOMEM; + int error = 0; struct tomoyo_path_info buf; struct tomoyo_request_info r; int idx; @@ -938,9 +938,6 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, buf.name = NULL; r.mode = TOMOYO_CONFIG_DISABLED; idx = tomoyo_read_lock(); - if (!tomoyo_get_realpath(&buf, path)) - goto out; - error = 0; /* * If the filename is specified by "deny_rewrite" keyword, * we need to check "allow_rewrite" permission when the filename is not From 7d77d22137391037be35d11cbf881133cc07b430 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 5 Feb 2011 10:08:21 +0000 Subject: [PATCH 0016/2556] drm/i915: Replace vblank PM QoS with "Interrupt-Based AGPBUSY#" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 8692d00e996ed2a6560702623e5cb646da0f9767 upstream. I stumbled over this magic bit in the gen3 INSTPM: Bit11 Interrupt-Based AGPBUSY# Enable: ‘0’ = Pending GMCH interrupts will not cause AGPBUSY# assertion. ‘1’ = Pending GMCH interrupts will cause AGPBUSY# assertion and hence can cause the CPU to exit C3. There is no suppression of cacheable writes. Note that in either case in C3 the interrupts are not lost. They will be forwarded to the ICH when the GMCH is out of C3. Signed-off-by: Chris Wilson Tested-by: Daniel Vetter Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/i915_irq.c | 9 +++++++++ drivers/gpu/drm/i915/i915_reg.h | 5 ++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 8a9e08bf1cf74..2347bc16d7fbe 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1377,7 +1377,12 @@ int i915_enable_vblank(struct drm_device *dev, int pipe) else i915_enable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE); + + /* maintain vblank delivery even in deep C-states */ + if (dev_priv->info->gen == 3) + I915_WRITE(INSTPM, INSTPM_AGPBUSY_DIS << 16); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); + return 0; } @@ -1390,6 +1395,10 @@ void i915_disable_vblank(struct drm_device *dev, int pipe) unsigned long irqflags; spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + if (dev_priv->info->gen == 3) + I915_WRITE(INSTPM, + INSTPM_AGPBUSY_DIS << 16 | INSTPM_AGPBUSY_DIS); + if (HAS_PCH_SPLIT(dev)) ironlake_disable_display_irq(dev_priv, (pipe == 0) ? DE_PIPEA_VBLANK: DE_PIPEB_VBLANK); diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 2abe240dae583..12c547a5ca13e 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -405,9 +405,12 @@ #define I915_ERROR_INSTRUCTION (1<<0) #define INSTPM 0x020c0 #define INSTPM_SELF_EN (1<<12) /* 915GM only */ +#define INSTPM_AGPBUSY_DIS (1<<11) /* gen3: when disabled, pending interrupts + will not assert AGPBUSY# and will only + be delivered when out of C3. */ #define ACTHD 0x020c8 #define FW_BLC 0x020d8 -#define FW_BLC2 0x020dc +#define FW_BLC2 0x020dc #define FW_BLC_SELF 0x020e0 /* 915+ only */ #define FW_BLC_SELF_EN_MASK (1<<31) #define FW_BLC_SELF_FIFO_MASK (1<<16) /* 945 only */ From 8901c28523089f357a3c75ba0f085a03e823914d Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 14 Mar 2011 23:18:00 -0400 Subject: [PATCH 0017/2556] drm/radeon/kms: fix typo in atom overscan setup commit 942b0e95c34f1ba432d08e1c0288ed032d32c3b2 upstream. Typo in the aspect scale setup. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/atombios_crtc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index a4e5e53e0a627..4a5a73bcf460a 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -61,8 +61,8 @@ static void atombios_overscan_setup(struct drm_crtc *crtc, args.usOverscanLeft = cpu_to_le16((adjusted_mode->crtc_hdisplay - (a2 / mode->crtc_vdisplay)) / 2); args.usOverscanRight = cpu_to_le16((adjusted_mode->crtc_hdisplay - (a2 / mode->crtc_vdisplay)) / 2); } else if (a2 > a1) { - args.usOverscanLeft = cpu_to_le16((adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2); - args.usOverscanRight = cpu_to_le16((adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2); + args.usOverscanTop = cpu_to_le16((adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2); + args.usOverscanBottom = cpu_to_le16((adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2); } break; case RMX_FULL: From d65298f49793478b76a0783233a054bc61cd16ac Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 15 Mar 2011 11:40:00 +0000 Subject: [PATCH 0018/2556] drm: Hold the mode mutex whilst probing for sysfs status commit 007c80a5497a3f9c8393960ec6e6efd30955dcb1 upstream. As detect will use hw registers and may modify structures, it needs to be serialised by use of the dev->mode_config.mutex. Make it so. Otherwise, we may cause random crashes as the sysfs file is queried whilst a concurrent hotplug poll is being run. For example: [ 1189.189626] BUG: unable to handle kernel NULL pointer dereference at 00000100 [ 1189.189821] IP: [] intel_tv_detect_type+0xa2/0x203 [i915] [ 1189.190020] *pde = 00000000 [ 1189.190104] Oops: 0000 [#1] SMP [ 1189.190209] last sysfs file: /sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-SVIDEO-1/status [ 1189.190412] Modules linked in: mperf cpufreq_conservative cpufreq_userspace cpufreq_powersave cpufreq_stats decnet uinput fuse loop joydev snd_hd a_codec_realtek snd_hda_intel snd_hda_codec snd_hwdep snd_pcm_oss snd_mixer_oss snd_pcm i915 snd_seq_midi snd_rawmidi snd_seq_midi_event snd_seq drm_kms_helper snd_timer uvcvideo d rm snd_seq_device eeepc_laptop tpm_tis usbhid videodev i2c_algo_bit v4l1_compat snd sparse_keymap i2c_core hid serio_raw tpm psmouse evdev tpm_bios rfkill shpchp ac processor rng_c ore battery video power_supply soundcore pci_hotplug button output snd_page_alloc usb_storage uas ext3 jbd mbcache sd_mod crc_t10dif ata_generic ahci libahci ata_piix libata uhci_h cd ehci_hcd scsi_mod usbcore thermal atl2 thermal_sys nls_base [last unloaded: scsi_wait_scan] [ 1189.192007] [ 1189.192007] Pid: 1464, comm: upowerd Not tainted 2.6.37-2-686 #1 ASUSTeK Computer INC. 701/701 [ 1189.192007] EIP: 0060:[] EFLAGS: 00010246 CPU: 0 [ 1189.192007] EIP is at intel_tv_detect_type+0xa2/0x203 [i915] [ 1189.192007] EAX: 00000000 EBX: dca74000 ECX: e0f68004 EDX: 00068004 [ 1189.192007] ESI: dd110c00 EDI: 400c0c37 EBP: dca7429c ESP: de365e2c [ 1189.192007] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 [ 1189.192007] Process upowerd (pid: 1464, ti=de364000 task=dcc8acb0 task.ti=de364000) [ 1189.192007] Stack: Mar 15 03:43:23 hostname kernel: [ 1189.192007] e0c2cda4 70000000 400c0c30 00000000 dd111000 de365e54 de365f24 dd110c00 [ 1189.192007] e0c22203 01000000 00000003 00000000 00000000 00000000 00000000 4353544e [ 1189.192007] 30383420 00000069 00000000 00000000 00000000 00000000 00000000 00000000 [ 1189.192007] Call Trace: Mar 15 03:43:23 hostname kernel: [ 1189.192007] [] ? intel_tv_detect+0x89/0x12d [i915] [ 1189.192007] [] ? status_show+0x0/0x2f [drm] [ 1189.192007] [] ? status_show+0x14/0x2f [drm] [Digression: what is upowerd doing reading those power hungry files?] Reported-by: Paul Menzel Signed-off-by: Chris Wilson Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/drm_sysfs.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 85da4c40694cc..2eee8e016b385 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -158,8 +158,15 @@ static ssize_t status_show(struct device *device, { struct drm_connector *connector = to_drm_connector(device); enum drm_connector_status status; + int ret; + + ret = mutex_lock_interruptible(&connector->dev->mode_config.mutex); + if (ret) + return ret; status = connector->funcs->detect(connector, true); + mutex_unlock(&connector->dev->mode_config.mutex); + return snprintf(buf, PAGE_SIZE, "%s\n", drm_get_connector_status_name(status)); } From d1fd5e3acfed51de3e0ece46bb23faa73e6ab4c5 Mon Sep 17 00:00:00 2001 From: Senthil Balasubramanian Date: Wed, 22 Dec 2010 19:17:18 +0530 Subject: [PATCH 0019/2556] ath9k_hw: read and backup AR_WA register value even before chip reset on. commit 0a8d7cb0c8182df7a28ad719780071178c386f0f upstream. We need to read and backup AR_WA register value permanently and reading this after the chip is awakened results in this register being zeroed out. This seems to fix the ASPM with L1 enabled issue that we have observed. The laptop becomes very slow and hangs mostly with ASPM L1 enabled without this fix. Signed-off-by: Senthil Balasubramanian Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/hw.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 9f01e50d5cda7..d3ea6dc094c63 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -495,6 +495,15 @@ static int __ath9k_hw_init(struct ath_hw *ah) if (ah->hw_version.devid == AR5416_AR9100_DEVID) ah->hw_version.macVersion = AR_SREV_VERSION_9100; + /* + * Read back AR_WA into a permanent copy and set bits 14 and 17. + * We need to do this to avoid RMW of this register. We cannot + * read the reg when chip is asleep. + */ + ah->WARegVal = REG_READ(ah, AR_WA); + ah->WARegVal |= (AR_WA_D3_L1_DISABLE | + AR_WA_ASPM_TIMER_BASED_DISABLE); + if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { ath_err(common, "Couldn't reset chip\n"); return -EIO; @@ -563,14 +572,6 @@ static int __ath9k_hw_init(struct ath_hw *ah) ath9k_hw_init_mode_regs(ah); - /* - * Read back AR_WA into a permanent copy and set bits 14 and 17. - * We need to do this to avoid RMW of this register. We cannot - * read the reg when chip is asleep. - */ - ah->WARegVal = REG_READ(ah, AR_WA); - ah->WARegVal |= (AR_WA_D3_L1_DISABLE | - AR_WA_ASPM_TIMER_BASED_DISABLE); if (ah->is_pciexpress) ath9k_hw_configpcipowersave(ah, 0, 0); From ef9d94d459e5fcf1f8183bf04afd61e2a9e22104 Mon Sep 17 00:00:00 2001 From: Senthil Balasubramanian Date: Wed, 22 Dec 2010 21:14:20 +0530 Subject: [PATCH 0020/2556] ath9k_hw: Fix incorrect macversion and macrev checks commit ac45c12dfb3f727a5a7a3332ed9c11b4a5ab287e upstream. There are few places where we are checking for macversion and revsions before RTC is powered ON. However we are reading the macversion and revisions only after RTC is powered ON and so both macversion and revisions are actully zero and this leads to incorrect srev checks Incorrect srev checks can cause registers to be configured wrongly and can cause unexpected behavior. Fixing this seems to address the ASPM issue that we have observed. The laptop becomes very slow and hangs mostly with ASPM L1 enabled without this fix. fix this by reading the macversion and revisisons even before we start using them. There is no reason why should we delay reading this info until RTC is powered on as this is just a register information. Signed-off-by: Senthil Balasubramanian Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/hw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index d3ea6dc094c63..7c0a7c48eea3b 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -504,6 +504,8 @@ static int __ath9k_hw_init(struct ath_hw *ah) ah->WARegVal |= (AR_WA_D3_L1_DISABLE | AR_WA_ASPM_TIMER_BASED_DISABLE); + ath9k_hw_read_revisions(ah); + if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { ath_err(common, "Couldn't reset chip\n"); return -EIO; @@ -1083,8 +1085,6 @@ static bool ath9k_hw_set_reset_power_on(struct ath_hw *ah) return false; } - ath9k_hw_read_revisions(ah); - return ath9k_hw_set_reset(ah, ATH9K_RESET_WARM); } From fd190dfb402d76c46392a13f990988f1158ca8ce Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 9 Mar 2011 01:48:12 +0100 Subject: [PATCH 0021/2556] ath9k: remove support for the FIF_PROMISC_IN_BSS filter flag commit 2e286947f1294239527c11f9f466ddce6466455b upstream. The hardware rx filter flag triggered by FIF_PROMISC_IN_BSS is overly broad and covers even frames with PHY errors. When this flag is enabled, this message shows up frequently during scanning or hardware resets: ath: Could not stop RX, we could be confusing the DMA engine when we start RX up Since promiscuous mode is usually not particularly useful, yet enabled by default by bridging (either used normally in 4-addr mode, or with hacks for various virtualization software), we should sacrifice it for better reliability during normal operation. This patch leaves it enabled if there are active monitor mode interfaces, since it's very useful for debugging. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/recv.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index b2497b8601e5b..3867a2e8de39c 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -439,9 +439,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc) * mode interface or when in monitor mode. AP mode does not need this * since it receives all in-BSS frames anyway. */ - if (((sc->sc_ah->opmode != NL80211_IFTYPE_AP) && - (sc->rx.rxfilter & FIF_PROMISC_IN_BSS)) || - (sc->sc_ah->is_monitoring)) + if (sc->sc_ah->is_monitoring) rfilt |= ATH9K_RX_FILTER_PROM; if (sc->rx.rxfilter & FIF_CONTROL) From ffe6f167d5ee3d799c090a05cef0b5013fbebb44 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 25 Jan 2011 14:15:11 +0000 Subject: [PATCH 0022/2556] serial: mrst_max3110: make buffer larger commit d8653d305ef66861c91fa7455fb8038460a7274c upstream. This is used to store the spi_device ->modalias so they have to be the same size. SPI_NAME_SIZE is 32. Signed-off-by: Dan Carpenter Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mrst_max3110.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index b62857bf2fdbd..37e13c3d91d99 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -51,7 +51,7 @@ struct uart_max3110 { struct uart_port port; struct spi_device *spi; - char name[24]; + char name[SPI_NAME_SIZE]; wait_queue_head_t wq; struct task_struct *main_thread; From 102965a0c525751b781021eed046ada76e65475b Mon Sep 17 00:00:00 2001 From: Yin Kangkai Date: Wed, 9 Feb 2011 11:34:20 +0800 Subject: [PATCH 0023/2556] serial: also set the uartclk value in resume after goes to highspeed commit 95926d2db6256e08d06b753752a0d903a0580acc upstream. For any reason if the NS16550A was not work in high speed mode (e.g. we hold NS16550A from going to high speed mode in autoconfig_16550a()), now we are resume from suspend, we should also set the uartclk to the correct value. Otherwise it is still the old 1843200 and that will bring issues. CC: Greg Kroah-Hartman CC: David Woodhouse CC: linux-kernel@vger.kernel.org Signed-off-by: Yin Kangkai Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index 3975df6f7fdba..c10a6a909c764 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -3036,6 +3036,7 @@ void serial8250_resume_port(int line) serial_outp(up, 0x04, tmp); serial_outp(up, UART_LCR, 0); + up->port.uartclk = 921600*16; } uart_resume_port(&serial8250_reg, &up->port); } From 2ac31a250c7d0cf642cc183bcf55e571ada867f6 Mon Sep 17 00:00:00 2001 From: Yin Kangkai Date: Wed, 9 Feb 2011 11:35:18 +0800 Subject: [PATCH 0024/2556] serial: change the divisor latch only when prescalar actually changed commit 0d0389e5414c8950b1613e8bdc74289cde3d6d98 upstream. In 8250.c original ns16550 autoconfig code, we change the divisor latch when we goto to high speed mode, we're assuming the previous speed is legacy. This some times is not true. For example in a system with both CONFIG_SERIAL_8250 and CONFIG_SERIAL_8250_PNP set, in this case, the code (autoconfig) will be called twice, one in serial8250_init/probe() and the other is from serial_pnp_probe. When serial_pnp_probe calls the autoconfig for NS16550A, it's already in high speed mode, change the divisor latch (quot << 3) in this case will make the UART console garbled. CC: Greg Kroah-Hartman CC: David Woodhouse CC: linux-kernel@vger.kernel.org Signed-off-by: Yin Kangkai Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index c10a6a909c764..b3b881bc4712f 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -954,6 +954,23 @@ static int broken_efr(struct uart_8250_port *up) return 0; } +static inline int ns16550a_goto_highspeed(struct uart_8250_port *up) +{ + unsigned char status; + + status = serial_in(up, 0x04); /* EXCR2 */ +#define PRESL(x) ((x) & 0x30) + if (PRESL(status) == 0x10) { + /* already in high speed mode */ + return 0; + } else { + status &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */ + status |= 0x10; /* 1.625 divisor for baud_base --> 921600 */ + serial_outp(up, 0x04, status); + } + return 1; +} + /* * We know that the chip has FIFOs. Does it have an EFR? The * EFR is located in the same register position as the IIR and @@ -1025,12 +1042,8 @@ static void autoconfig_16550a(struct uart_8250_port *up) quot = serial_dl_read(up); quot <<= 3; - status1 = serial_in(up, 0x04); /* EXCR2 */ - status1 &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */ - status1 |= 0x10; /* 1.625 divisor for baud_base --> 921600 */ - serial_outp(up, 0x04, status1); - - serial_dl_write(up, quot); + if (ns16550a_goto_highspeed(up)) + serial_dl_write(up, quot); serial_outp(up, UART_LCR, 0); @@ -3025,15 +3038,10 @@ void serial8250_resume_port(int line) struct uart_8250_port *up = &serial8250_ports[line]; if (up->capabilities & UART_NATSEMI) { - unsigned char tmp; - /* Ensure it's still in high speed mode */ serial_outp(up, UART_LCR, 0xE0); - tmp = serial_in(up, 0x04); /* EXCR2 */ - tmp &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */ - tmp |= 0x10; /* 1.625 divisor for baud_base --> 921600 */ - serial_outp(up, 0x04, tmp); + ns16550a_goto_highspeed(up); serial_outp(up, UART_LCR, 0); up->port.uartclk = 921600*16; From 366a91fbe3eeaf11c7bc380e854c7096bcb976dd Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 23 Feb 2011 15:28:18 -0500 Subject: [PATCH 0025/2556] USB: serial drivers need to use larger bulk-in buffers commit 969e3033ae7733a0af8f7742ca74cd16c0857e71 upstream. When a driver doesn't know how much data a device is going to send, the buffer size should be at least as big as the endpoint's maxpacket value. The serial drivers don't follow this rule; many of them request only 256-byte bulk-in buffers. As a result, they suffer overflow errors if a high-speed device wants to send a lot of data, because high-speed bulk endpoints are required to have a maxpacket size of 512. This patch (as1450) fixes the problem by using the driver's bulk_in_size value as a minimum, always allocating buffers no smaller than the endpoint's maxpacket size. Signed-off-by: Alan Stern Tested-by: Flynn Marquardt Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 5 ++--- include/linux/usb/serial.h | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 546a52179becb..2ff90a9c8f474 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -911,9 +911,8 @@ int usb_serial_probe(struct usb_interface *interface, dev_err(&interface->dev, "No free urbs available\n"); goto probe_error; } - buffer_size = serial->type->bulk_in_size; - if (!buffer_size) - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = max_t(int, serial->type->bulk_in_size, + le16_to_cpu(endpoint->wMaxPacketSize)); port->bulk_in_size = buffer_size; port->bulk_in_endpointAddress = endpoint->bEndpointAddress; port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index c9049139a7a5a..45f3b9db42582 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -191,7 +191,8 @@ static inline void usb_set_serial_data(struct usb_serial *serial, void *data) * @id_table: pointer to a list of usb_device_id structures that define all * of the devices this structure can support. * @num_ports: the number of different ports this device will have. - * @bulk_in_size: bytes to allocate for bulk-in buffer (0 = end-point size) + * @bulk_in_size: minimum number of bytes to allocate for bulk-in buffer + * (0 = end-point size) * @bulk_out_size: bytes to allocate for bulk-out buffer (0 = end-point size) * @calc_num_ports: pointer to a function to determine how many ports this * device has dynamically. It will be called after the probe() From 0a04fbe5bb3d98d3ee553e9fec49ac9e16cc3217 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 28 Feb 2011 10:34:06 +0100 Subject: [PATCH 0026/2556] USB: serial/kobil_sct, fix potential tty NULL dereference commit 6960f40a954619857e7095a6179eef896f297077 upstream. Make sure that we check the return value of tty_port_tty_get. Sometimes it may return NULL and we later dereference that. The only place here is in kobil_read_int_callback, so fix it. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/kobil_sct.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index bd5bd8589e04c..b382d9a0274d5 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -372,7 +372,7 @@ static void kobil_read_int_callback(struct urb *urb) } tty = tty_port_tty_get(&port->port); - if (urb->actual_length) { + if (tty && urb->actual_length) { /* BEGIN DEBUG */ /* From c576af5495e8a02a7a748f1127cf9079c53c7061 Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Wed, 9 Mar 2011 09:19:48 +0000 Subject: [PATCH 0027/2556] USB: serial: option: Apply OPTION_BLACKLIST_SENDSETUP also for ZTE MF626 commit 7a89e4cb9cdaba92f5fbc509945cf4e3c48db4e2 upstream. On https://bugs.launchpad.net/ubuntu/+source/linux/+bug/636091, one of the cases reported is a big timeout on option_send_setup, which causes some side effects as tty_lock is held. Looks like some of ZTE MF626 devices also don't like the RTS/DTR setting in option_send_setup, like with 4G XS Stick W14. The reporter confirms which this it solves the long freezes in his system. Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 5f46838dfee5d..75c7f456eed52 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -652,7 +652,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0028, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0029, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0030, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff, + 0xff, 0xff), .driver_info = (kernel_ulong_t)&four_g_w14_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0032, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0033, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0034, 0xff, 0xff, 0xff) }, From 1bc826c80dbd4a542557f3b1bef322e3e1b730ef Mon Sep 17 00:00:00 2001 From: wangyanqing Date: Fri, 11 Mar 2011 06:24:38 -0800 Subject: [PATCH 0028/2556] USB: serial: ch341: add new id commit d0781383038e983a63843a9a6a067ed781db89c1 upstream. I picked up a new DAK-780EX(professional digitl reverb/mix system), which use CH341T chipset to communication with computer on 3/2011 and the CH341T's vendor code is 1a86 Looking up the CH341T's vendor and product id's I see: 1a86 QinHeng Electronics 5523 CH341 in serial mode, usb to serial port converter CH341T,CH341 are the products of the same company, maybe have some common hardware, and I test the ch341.c works well with CH341T Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ch341.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 7b8815ddf3688..14ac87ee9251b 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -75,6 +75,7 @@ static int debug; static const struct usb_device_id id_table[] = { { USB_DEVICE(0x4348, 0x5523) }, { USB_DEVICE(0x1a86, 0x7523) }, + { USB_DEVICE(0x1a86, 0x5523) }, { }, }; MODULE_DEVICE_TABLE(usb, id_table); From 9396b825a10a1f25f60085f49e2cde42a4bd39ee Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 24 Feb 2011 14:49:00 -0500 Subject: [PATCH 0029/2556] staging: winbond: needs for msleep and friends commit cecf826df8648c843ea8db63b1f82c154a74db36 upstream. linux/delay.h is pulled in somehow on x86 but not on ia64 or powerpc. This fixes a build failure on those arches since they use [mu]delay. Signed-off-by: Jeff Mahoney Signed-off-by: Greg Kroah-Hartman --- drivers/staging/winbond/core.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/winbond/core.h b/drivers/staging/winbond/core.h index d7b3aca5ddeba..6160b2fab833f 100644 --- a/drivers/staging/winbond/core.h +++ b/drivers/staging/winbond/core.h @@ -3,6 +3,7 @@ #include #include +#include #include "wbhal.h" #include "mto.h" From 9d0a3d6acaca7004f2800d50eb44ccaca4257aee Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Fri, 11 Mar 2011 18:29:06 -0600 Subject: [PATCH 0030/2556] staging: tidspbridge: protect dmm_map properly commit ab42abf33a3efdf754710a0a513c00c40854cd61 upstream. We need to protect not only the dmm_map list, but the individual map_obj's, otherwise, we might be building the scatter-gather list with garbage. So, use the existing proc_lock for that. I observed race conditions which caused kernel panics while running stress tests, also, Tuomas Kulve found it happening quite often in Gumstix Over. This patch fixes those. Cc: Tuomas Kulve Signed-off-by: Felipe Contreras Signed-off-by: Omar Ramirez Luna Signed-off-by: Greg Kroah-Hartman --- drivers/staging/tidspbridge/rmgr/proc.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/staging/tidspbridge/rmgr/proc.c b/drivers/staging/tidspbridge/rmgr/proc.c index b47d7aa747b1d..e2fe165bd797b 100644 --- a/drivers/staging/tidspbridge/rmgr/proc.c +++ b/drivers/staging/tidspbridge/rmgr/proc.c @@ -781,12 +781,14 @@ int proc_begin_dma(void *hprocessor, void *pmpu_addr, u32 ul_size, (u32)pmpu_addr, ul_size, dir); + mutex_lock(&proc_lock); + /* find requested memory are in cached mapping information */ map_obj = find_containing_mapping(pr_ctxt, (u32) pmpu_addr, ul_size); if (!map_obj) { pr_err("%s: find_containing_mapping failed\n", __func__); status = -EFAULT; - goto err_out; + goto no_map; } if (memory_give_ownership(map_obj, (u32) pmpu_addr, ul_size, dir)) { @@ -795,6 +797,8 @@ int proc_begin_dma(void *hprocessor, void *pmpu_addr, u32 ul_size, status = -EFAULT; } +no_map: + mutex_unlock(&proc_lock); err_out: return status; @@ -819,21 +823,24 @@ int proc_end_dma(void *hprocessor, void *pmpu_addr, u32 ul_size, (u32)pmpu_addr, ul_size, dir); + mutex_lock(&proc_lock); + /* find requested memory are in cached mapping information */ map_obj = find_containing_mapping(pr_ctxt, (u32) pmpu_addr, ul_size); if (!map_obj) { pr_err("%s: find_containing_mapping failed\n", __func__); status = -EFAULT; - goto err_out; + goto no_map; } if (memory_regain_ownership(map_obj, (u32) pmpu_addr, ul_size, dir)) { pr_err("%s: InValid address parameters %p %x\n", __func__, pmpu_addr, ul_size); status = -EFAULT; - goto err_out; } +no_map: + mutex_unlock(&proc_lock); err_out: return status; } @@ -1726,9 +1733,8 @@ int proc_un_map(void *hprocessor, void *map_addr, (p_proc_object->hbridge_context, va_align, size_align); } - mutex_unlock(&proc_lock); if (status) - goto func_end; + goto unmap_failed; /* * A successful unmap should be followed by removal of map_obj @@ -1737,6 +1743,9 @@ int proc_un_map(void *hprocessor, void *map_addr, */ remove_mapping_information(pr_ctxt, (u32) map_addr, size_align); +unmap_failed: + mutex_unlock(&proc_lock); + func_end: dev_dbg(bridge, "%s: hprocessor: 0x%p map_addr: 0x%p status: 0x%x\n", __func__, hprocessor, map_addr, status); From 9d072975c751913f52a135235469f83645ee6528 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 28 Feb 2011 23:36:09 -0600 Subject: [PATCH 0031/2556] rtl8187: Change rate-control feedback commit 6410db593e8c1b2b79a2f18554310d6da9415584 upstream. The driver for the RTL8187L chips returns IEEE80211_TX_STAT_ACK for all packets, even if the maximum number of retries was exhausted. In addition it fails to setup max_rates in the ieee80211_hw struct, This behavior may be responsible for the problems noted in Bug 14168. As the bug is very old, testers have not been found, and I do not have the case where the indicated signal is less than -70 dBm. Signed-off-by: Larry Finger Acked-by: Hin-Tak Leung Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/rtl818x/rtl8187/dev.c | 25 +++++++++++++++---- .../net/wireless/rtl818x/rtl8187/rtl8187.h | 2 ++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c index 6b82cac37ee33..2bb5297655cee 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -871,23 +871,35 @@ static void rtl8187_work(struct work_struct *work) /* The RTL8187 returns the retry count through register 0xFFFA. In * addition, it appears to be a cumulative retry count, not the * value for the current TX packet. When multiple TX entries are - * queued, the retry count will be valid for the last one in the queue. - * The "error" should not matter for purposes of rate setting. */ + * waiting in the queue, the retry count will be the total for all. + * The "error" may matter for purposes of rate setting, but there is + * no other choice with this hardware. + */ struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, work.work); struct ieee80211_tx_info *info; struct ieee80211_hw *dev = priv->dev; static u16 retry; u16 tmp; + u16 avg_retry; + int length; mutex_lock(&priv->conf_mutex); tmp = rtl818x_ioread16(priv, (__le16 *)0xFFFA); + length = skb_queue_len(&priv->b_tx_status.queue); + if (unlikely(!length)) + length = 1; + if (unlikely(tmp < retry)) + tmp = retry; + avg_retry = (tmp - retry) / length; while (skb_queue_len(&priv->b_tx_status.queue) > 0) { struct sk_buff *old_skb; old_skb = skb_dequeue(&priv->b_tx_status.queue); info = IEEE80211_SKB_CB(old_skb); - info->status.rates[0].count = tmp - retry + 1; + info->status.rates[0].count = avg_retry + 1; + if (info->status.rates[0].count > RETRY_COUNT) + info->flags &= ~IEEE80211_TX_STAT_ACK; ieee80211_tx_status_irqsafe(dev, old_skb); } retry = tmp; @@ -933,8 +945,8 @@ static int rtl8187_start(struct ieee80211_hw *dev) rtl818x_iowrite32(priv, &priv->map->TX_CONF, RTL818X_TX_CONF_HW_SEQNUM | RTL818X_TX_CONF_DISREQQSIZE | - (7 << 8 /* short retry limit */) | - (7 << 0 /* long retry limit */) | + (RETRY_COUNT << 8 /* short retry limit */) | + (RETRY_COUNT << 0 /* long retry limit */) | (7 << 21 /* MAX TX DMA */)); rtl8187_init_urbs(dev); rtl8187b_init_status_urb(dev); @@ -1378,6 +1390,9 @@ static int __devinit rtl8187_probe(struct usb_interface *intf, dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_RX_INCLUDES_FCS; + /* Initialize rate-control variables */ + dev->max_rates = 1; + dev->max_rate_tries = RETRY_COUNT; eeprom.data = dev; eeprom.register_read = rtl8187_eeprom_register_read; diff --git a/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h index 0d7b1423f77b4..f1cc90751dbf5 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h +++ b/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h @@ -35,6 +35,8 @@ #define RFKILL_MASK_8187_89_97 0x2 #define RFKILL_MASK_8198 0x4 +#define RETRY_COUNT 7 + struct rtl8187_rx_info { struct urb *urb; struct ieee80211_hw *dev; From 679a1ea67eeabcedac628dfd2573c9a1946ab2e7 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 8 Feb 2011 21:07:40 +0100 Subject: [PATCH 0032/2556] USB: isp1760: Implement solution for erratum 2 commit b14e840d04dba211fbdc930247e379085623eacd upstream. The document says: |2.1 Problem description | When at least two USB devices are simultaneously running, it is observed that | sometimes the INT corresponding to one of the USB devices stops occurring. This may | be observed sometimes with USB-to-serial or USB-to-network devices. | The problem is not noticed when only USB mass storage devices are running. |2.2 Implication | This issue is because of the clearing of the respective Done Map bit on reading the ATL | PTD Done Map register when an INT is generated by another PTD completion, but is not | found set on that read access. In this situation, the respective Done Map bit will remain | reset and no further INT will be asserted so the data transfer corresponding to that USB | device will stop. |2.3 Workaround | An SOF INT can be used instead of an ATL INT with polling on Done bits. A time-out can | be implemented and if a certain Done bit is never set, verification of the PTD completion | can be done by reading PTD contents (valid bit). | This is a proven workaround implemented in software. Russell King run into this with an USB-to-serial converter. This patch implements his suggestion to enable the high frequent SOF interrupt only at the time we have ATL packages queued. It goes even one step further and enables the SOF interrupt only if we have more than one ATL packet queued at the same time. Tested-by: Russell King Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1760-hcd.c | 22 ++++++++++++++++------ drivers/usb/host/isp1760-hcd.h | 1 + 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index bdba8c5d844aa..c470cc83dbb01 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -33,6 +33,7 @@ struct isp1760_hcd { struct inter_packet_info atl_ints[32]; struct inter_packet_info int_ints[32]; struct memory_chunk memory_pool[BLOCKS]; + u32 atl_queued; /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 @@ -850,6 +851,11 @@ static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, skip_map &= ~queue_entry; isp1760_writel(skip_map, hcd->regs + HC_ATL_PTD_SKIPMAP_REG); + priv->atl_queued++; + if (priv->atl_queued == 2) + isp1760_writel(INTERRUPT_ENABLE_SOT_MASK, + hcd->regs + HC_INTERRUPT_ENABLE); + buffstatus = isp1760_readl(hcd->regs + HC_BUFFER_STATUS_REG); buffstatus |= ATL_BUFFER; isp1760_writel(buffstatus, hcd->regs + HC_BUFFER_STATUS_REG); @@ -992,6 +998,7 @@ static void do_atl_int(struct usb_hcd *usb_hcd) u32 dw3; status = 0; + priv->atl_queued--; queue_entry = __ffs(done_map); done_map &= ~(1 << queue_entry); @@ -1054,11 +1061,6 @@ static void do_atl_int(struct usb_hcd *usb_hcd) * device is not able to send data fast enough. * This happens mostly on slower hardware. */ - printk(KERN_NOTICE "Reloading ptd %p/%p... qh %p read: " - "%d of %zu done: %08x cur: %08x\n", qtd, - urb, qh, PTD_XFERRED_LENGTH(dw3), - qtd->length, done_map, - (1 << queue_entry)); /* RL counter = ERR counter */ dw3 &= ~(0xf << 19); @@ -1086,6 +1088,11 @@ static void do_atl_int(struct usb_hcd *usb_hcd) priv_write_copy(priv, (u32 *)&ptd, usb_hcd->regs + atl_regs, sizeof(ptd)); + priv->atl_queued++; + if (priv->atl_queued == 2) + isp1760_writel(INTERRUPT_ENABLE_SOT_MASK, + usb_hcd->regs + HC_INTERRUPT_ENABLE); + buffstatus = isp1760_readl(usb_hcd->regs + HC_BUFFER_STATUS_REG); buffstatus |= ATL_BUFFER; @@ -1191,6 +1198,9 @@ static void do_atl_int(struct usb_hcd *usb_hcd) skip_map = isp1760_readl(usb_hcd->regs + HC_ATL_PTD_SKIPMAP_REG); } + if (priv->atl_queued <= 1) + isp1760_writel(INTERRUPT_ENABLE_MASK, + usb_hcd->regs + HC_INTERRUPT_ENABLE); } static void do_intl_int(struct usb_hcd *usb_hcd) @@ -1770,7 +1780,7 @@ static irqreturn_t isp1760_irq(struct usb_hcd *usb_hcd) goto leave; isp1760_writel(imask, usb_hcd->regs + HC_INTERRUPT_REG); - if (imask & HC_ATL_INT) + if (imask & (HC_ATL_INT | HC_SOT_INT)) do_atl_int(usb_hcd); if (imask & HC_INTL_INT) diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 6931ef5c96509..612bce5dce03c 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -69,6 +69,7 @@ void deinit_kmem_cache(void); #define HC_INTERRUPT_ENABLE 0x314 #define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT | HC_EOT_INT) +#define INTERRUPT_ENABLE_SOT_MASK (HC_INTL_INT | HC_SOT_INT | HC_EOT_INT) #define HC_ISO_INT (1 << 9) #define HC_ATL_INT (1 << 8) From a6b2dd27d8d97c83b7f0c70c8813f96801f85c00 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 11 Jan 2011 12:26:48 -0500 Subject: [PATCH 0033/2556] ehci: Check individual port status registers on resume commit 294d95f2cbc2aef5346258f216cd9df570e271a5 upstream. If a device plug/unplug is detected on an ATI SB700 USB controller in D3, it appears to set the port status register but not the controller status register. As a result we'll fail to detect the plug event. Check the port status register on resume as well in order to catch this case. Signed-off-by: Matthew Garrett Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hub.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 8a515f0d59880..72ae77c7b7c08 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -106,6 +106,27 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) ehci->owned_ports = 0; } +static int ehci_port_change(struct ehci_hcd *ehci) +{ + int i = HCS_N_PORTS(ehci->hcs_params); + + /* First check if the controller indicates a change event */ + + if (ehci_readl(ehci, &ehci->regs->status) & STS_PCD) + return 1; + + /* + * Not all controllers appear to update this while going from D3 to D0, + * so check the individual port status registers as well + */ + + while (i--) + if (ehci_readl(ehci, &ehci->regs->port_status[i]) & PORT_CSC) + return 1; + + return 0; +} + static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, bool suspending, bool do_wakeup) { @@ -173,7 +194,7 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, } /* Does the root hub have a port wakeup pending? */ - if (!suspending && (ehci_readl(ehci, &ehci->regs->status) & STS_PCD)) + if (!suspending && ehci_port_change(ehci)) usb_hcd_resume_root_hub(ehci_to_hcd(ehci)); spin_unlock_irqrestore(&ehci->lock, flags); From ca81d7760eb900e6f42b60e52b8a3efc88780e37 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 7 Mar 2011 11:11:52 -0500 Subject: [PATCH 0034/2556] USB: move usbcore away from hcd->state commit 9b37596a2e860404503a3f2a6513db60c296bfdc upstream. The hcd->state variable is a disaster. It's not clearly owned by either usbcore or the host controller drivers, and they both change it from time to time, potentially stepping on each other's toes. It's not protected by any locks. And there's no mechanism to prevent it from going through an invalid transition. This patch (as1451) takes a first step toward fixing these problems. As it turns out, usbcore uses hcd->state for essentially only two things: checking whether the controller's root hub is running and checking whether the controller has died. Therefore the patch adds two new atomic bitflags to the hcd structure, to store these pieces of information. The new flags are used only by usbcore, and a private spinlock prevents invalid combinations (a dead controller's root hub cannot be running). The patch does not change the places where usbcore sets hcd->state, since HCDs may depend on them. Furthermore, there is one place in usb_hcd_irq() where usbcore still must use hcd->state: An HCD's interrupt handler can implicitly indicate that the controller died by setting hcd->state to HC_STATE_HALT. Nevertheless, the new code is a big improvement over the current code. The patch makes one other change. The hcd_bus_suspend() and hcd_bus_resume() routines now check first whether the host controller has died; if it has then they return immediately without calling the HCD's bus_suspend or bus_resume methods. This fixes the major problem reported in Bugzilla #29902: The system fails to suspend after a host controller dies during system resume. Signed-off-by: Alan Stern Tested-by: Alex Terekhov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 13 +++++---- drivers/usb/core/hcd.c | 55 ++++++++++++++++++++++++++++---------- include/linux/usb/hcd.h | 4 +++ 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index f71e8e307e0f0..d37088591d9af 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -363,8 +363,7 @@ static int check_root_hub_suspended(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); struct usb_hcd *hcd = pci_get_drvdata(pci_dev); - if (!(hcd->state == HC_STATE_SUSPENDED || - hcd->state == HC_STATE_HALT)) { + if (HCD_RH_RUNNING(hcd)) { dev_warn(dev, "Root hub is not suspended\n"); return -EBUSY; } @@ -386,7 +385,7 @@ static int suspend_common(struct device *dev, bool do_wakeup) if (retval) return retval; - if (hcd->driver->pci_suspend) { + if (hcd->driver->pci_suspend && !HCD_DEAD(hcd)) { /* Optimization: Don't suspend if a root-hub wakeup is * pending and it would cause the HCD to wake up anyway. */ @@ -427,7 +426,7 @@ static int resume_common(struct device *dev, int event) struct usb_hcd *hcd = pci_get_drvdata(pci_dev); int retval; - if (hcd->state != HC_STATE_SUSPENDED) { + if (HCD_RH_RUNNING(hcd)) { dev_dbg(dev, "can't resume, not suspended!\n"); return 0; } @@ -442,7 +441,7 @@ static int resume_common(struct device *dev, int event) clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); - if (hcd->driver->pci_resume) { + if (hcd->driver->pci_resume && !HCD_DEAD(hcd)) { if (event != PM_EVENT_AUTO_RESUME) wait_for_companions(pci_dev, hcd); @@ -475,10 +474,10 @@ static int hcd_pci_suspend_noirq(struct device *dev) pci_save_state(pci_dev); - /* If the root hub is HALTed rather than SUSPENDed, + /* If the root hub is dead rather than suspended, * disallow remote wakeup. */ - if (hcd->state == HC_STATE_HALT) + if (HCD_DEAD(hcd)) device_set_wakeup_enable(dev, 0); dev_dbg(dev, "wakeup: %d\n", device_may_wakeup(dev)); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index e935f71d7a346..c34a935c7a379 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -983,7 +983,7 @@ static int register_root_hub(struct usb_hcd *hcd) spin_unlock_irq (&hcd_root_hub_lock); /* Did the HC die before the root hub was registered? */ - if (hcd->state == HC_STATE_HALT) + if (HCD_DEAD(hcd) || hcd->state == HC_STATE_HALT) usb_hc_died (hcd); /* This time clean up */ } @@ -1089,13 +1089,10 @@ int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb) * Check the host controller's state and add the URB to the * endpoint's queue. */ - switch (hcd->state) { - case HC_STATE_RUNNING: - case HC_STATE_RESUMING: + if (HCD_RH_RUNNING(hcd)) { urb->unlinked = 0; list_add_tail(&urb->urb_list, &urb->ep->urb_list); - break; - default: + } else { rc = -ESHUTDOWN; goto done; } @@ -1913,7 +1910,7 @@ int usb_hcd_get_frame_number (struct usb_device *udev) { struct usb_hcd *hcd = bus_to_hcd(udev->bus); - if (!HC_IS_RUNNING (hcd->state)) + if (!HCD_RH_RUNNING(hcd)) return -ESHUTDOWN; return hcd->driver->get_frame_number (hcd); } @@ -1930,9 +1927,15 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) dev_dbg(&rhdev->dev, "bus %s%s\n", (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "suspend"); + if (HCD_DEAD(hcd)) { + dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "suspend"); + return 0; + } + if (!hcd->driver->bus_suspend) { status = -ENOENT; } else { + clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); hcd->state = HC_STATE_QUIESCING; status = hcd->driver->bus_suspend(hcd); } @@ -1940,7 +1943,12 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) usb_set_device_state(rhdev, USB_STATE_SUSPENDED); hcd->state = HC_STATE_SUSPENDED; } else { - hcd->state = old_state; + spin_lock_irq(&hcd_root_hub_lock); + if (!HCD_DEAD(hcd)) { + set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); + hcd->state = old_state; + } + spin_unlock_irq(&hcd_root_hub_lock); dev_dbg(&rhdev->dev, "bus %s fail, err %d\n", "suspend", status); } @@ -1955,9 +1963,13 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) dev_dbg(&rhdev->dev, "usb %s%s\n", (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume"); + if (HCD_DEAD(hcd)) { + dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "resume"); + return 0; + } if (!hcd->driver->bus_resume) return -ENOENT; - if (hcd->state == HC_STATE_RUNNING) + if (HCD_RH_RUNNING(hcd)) return 0; hcd->state = HC_STATE_RESUMING; @@ -1966,10 +1978,15 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) if (status == 0) { /* TRSMRCY = 10 msec */ msleep(10); - usb_set_device_state(rhdev, rhdev->actconfig - ? USB_STATE_CONFIGURED - : USB_STATE_ADDRESS); - hcd->state = HC_STATE_RUNNING; + spin_lock_irq(&hcd_root_hub_lock); + if (!HCD_DEAD(hcd)) { + usb_set_device_state(rhdev, rhdev->actconfig + ? USB_STATE_CONFIGURED + : USB_STATE_ADDRESS); + set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); + hcd->state = HC_STATE_RUNNING; + } + spin_unlock_irq(&hcd_root_hub_lock); } else { hcd->state = old_state; dev_dbg(&rhdev->dev, "bus %s fail, err %d\n", @@ -2080,7 +2097,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd) */ local_irq_save(flags); - if (unlikely(hcd->state == HC_STATE_HALT || !HCD_HW_ACCESSIBLE(hcd))) { + if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd))) { rc = IRQ_NONE; } else if (hcd->driver->irq(hcd) == IRQ_NONE) { rc = IRQ_NONE; @@ -2114,6 +2131,8 @@ void usb_hc_died (struct usb_hcd *hcd) dev_err (hcd->self.controller, "HC died; cleaning up\n"); spin_lock_irqsave (&hcd_root_hub_lock, flags); + clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); + set_bit(HCD_FLAG_DEAD, &hcd->flags); if (hcd->rh_registered) { clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); @@ -2256,6 +2275,12 @@ int usb_add_hcd(struct usb_hcd *hcd, */ device_init_wakeup(&rhdev->dev, 1); + /* HCD_FLAG_RH_RUNNING doesn't matter until the root hub is + * registered. But since the controller can die at any time, + * let's initialize the flag before touching the hardware. + */ + set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); + /* "reset" is misnamed; its role is now one-time init. the controller * should already have been reset (and boot firmware kicked off etc). */ @@ -2323,6 +2348,7 @@ int usb_add_hcd(struct usb_hcd *hcd, return retval; error_create_attr_group: + clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); if (HC_IS_RUNNING(hcd->state)) hcd->state = HC_STATE_QUIESCING; spin_lock_irq(&hcd_root_hub_lock); @@ -2375,6 +2401,7 @@ void usb_remove_hcd(struct usb_hcd *hcd) usb_get_dev(rhdev); sysfs_remove_group(&rhdev->dev.kobj, &usb_bus_attr_group); + clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); if (HC_IS_RUNNING (hcd->state)) hcd->state = HC_STATE_QUIESCING; diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index a854fe89484e4..f21f5996fa61f 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -99,6 +99,8 @@ struct usb_hcd { #define HCD_FLAG_POLL_RH 2 /* poll for rh status? */ #define HCD_FLAG_POLL_PENDING 3 /* status has changed? */ #define HCD_FLAG_WAKEUP_PENDING 4 /* root hub is resuming? */ +#define HCD_FLAG_RH_RUNNING 5 /* root hub is running? */ +#define HCD_FLAG_DEAD 6 /* controller has died? */ /* The flags can be tested using these macros; they are likely to * be slightly faster than test_bit(). @@ -108,6 +110,8 @@ struct usb_hcd { #define HCD_POLL_RH(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_RH)) #define HCD_POLL_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_PENDING)) #define HCD_WAKEUP_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_WAKEUP_PENDING)) +#define HCD_RH_RUNNING(hcd) ((hcd)->flags & (1U << HCD_FLAG_RH_RUNNING)) +#define HCD_DEAD(hcd) ((hcd)->flags & (1U << HCD_FLAG_DEAD)) /* Flags that get set only during HCD registration or removal. */ unsigned rh_registered:1;/* is root hub registered? */ From 5fb7c16628c599f9598ebda9403af1c122f1911e Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 23 Feb 2011 15:46:42 -0800 Subject: [PATCH 0035/2556] xhci: Update internal dequeue pointers after stalls. commit bf161e85fb153c0dd5a95faca73fd6a9d237c389 upstream. When an endpoint stalls, the xHCI driver must move the endpoint ring's dequeue pointer past the stalled transfer. To do that, the driver issues a Set TR Dequeue Pointer command, which will complete some time later. Takashi was having issues with USB 1.1 audio devices that stalled, and his analysis of the code was that the old code would not update the xHCI driver's ring dequeue pointer after the command completes. However, the dequeue pointer is set in xhci_find_new_dequeue_state(), just before the set command is issued to the hardware. Setting the dequeue pointer before the Set TR Dequeue Pointer command completes is a dangerous thing to do, since the xHCI hardware can fail the command. Instead, store the new dequeue pointer in the xhci_virt_ep structure, and update the ring's dequeue pointer when the Set TR dequeue pointer command completes. While we're at it, make sure we can't queue another Set TR Dequeue Command while the first one is still being processed. This just won't work with the internal xHCI state code. I'm still not sure if this is the right thing to do, since we might have a case where a driver queues multiple URBs to a control ring, one of the URBs Stalls, and then the driver tries to cancel the second URB. There may be a race condition there where the xHCI driver might try to issue multiple Set TR Dequeue Pointer commands, but I would have to think very hard about how the Stop Endpoint and cancellation code works. Keep the fix simple until when/if we run into that case. This patch should be queued to kernels all the way back to 2.6.31. Signed-off-by: Sarah Sharp Tested-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 29 ++++++++++++++++++++++++++--- drivers/usb/host/xhci.h | 9 +++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 3289bf4832c9a..699676d1df888 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -506,9 +506,6 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, addr = xhci_trb_virt_to_dma(state->new_deq_seg, state->new_deq_ptr); xhci_dbg(xhci, "New dequeue pointer = 0x%llx (DMA)\n", (unsigned long long) addr); - xhci_dbg(xhci, "Setting dequeue pointer in internal ring state.\n"); - ep_ring->dequeue = state->new_deq_ptr; - ep_ring->deq_seg = state->new_deq_seg; } static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, @@ -951,9 +948,26 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, } else { xhci_dbg(xhci, "Successful Set TR Deq Ptr cmd, deq = @%08llx\n", ep_ctx->deq); + if (xhci_trb_virt_to_dma(dev->eps[ep_index].queued_deq_seg, + dev->eps[ep_index].queued_deq_ptr) == + (ep_ctx->deq & ~(EP_CTX_CYCLE_MASK))) { + /* Update the ring's dequeue segment and dequeue pointer + * to reflect the new position. + */ + ep_ring->deq_seg = dev->eps[ep_index].queued_deq_seg; + ep_ring->dequeue = dev->eps[ep_index].queued_deq_ptr; + } else { + xhci_warn(xhci, "Mismatch between completed Set TR Deq " + "Ptr command & xHCI internal state.\n"); + xhci_warn(xhci, "ep deq seg = %p, deq ptr = %p\n", + dev->eps[ep_index].queued_deq_seg, + dev->eps[ep_index].queued_deq_ptr); + } } dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING; + dev->eps[ep_index].queued_deq_seg = NULL; + dev->eps[ep_index].queued_deq_ptr = NULL; /* Restart any rings with pending URBs */ ring_doorbell_for_active_rings(xhci, slot_id, ep_index); } @@ -3229,6 +3243,7 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id, u32 trb_ep_index = EP_ID_FOR_TRB(ep_index); u32 trb_stream_id = STREAM_ID_FOR_TRB(stream_id); u32 type = TRB_TYPE(TRB_SET_DEQ); + struct xhci_virt_ep *ep; addr = xhci_trb_virt_to_dma(deq_seg, deq_ptr); if (addr == 0) { @@ -3237,6 +3252,14 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id, deq_seg, deq_ptr); return 0; } + ep = &xhci->devs[slot_id]->eps[ep_index]; + if ((ep->ep_state & SET_DEQ_PENDING)) { + xhci_warn(xhci, "WARN Cannot submit Set TR Deq Ptr\n"); + xhci_warn(xhci, "A Set TR Deq Ptr command is pending.\n"); + return 0; + } + ep->queued_deq_seg = deq_seg; + ep->queued_deq_ptr = deq_ptr; return queue_command(xhci, lower_32_bits(addr) | cycle_state, upper_32_bits(addr), trb_stream_id, trb_slot_id | trb_ep_index | type, false); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 7f127df6dd553..62bc1bc2bd72e 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -644,6 +644,9 @@ struct xhci_ep_ctx { #define AVG_TRB_LENGTH_FOR_EP(p) ((p) & 0xffff) #define MAX_ESIT_PAYLOAD_FOR_EP(p) (((p) & 0xffff) << 16) +/* deq bitmasks */ +#define EP_CTX_CYCLE_MASK (1 << 0) + /** * struct xhci_input_control_context @@ -746,6 +749,12 @@ struct xhci_virt_ep { struct timer_list stop_cmd_timer; int stop_cmds_pending; struct xhci_hcd *xhci; + /* Dequeue pointer and dequeue segment for a submitted Set TR Dequeue + * command. We'll need to update the ring's dequeue segment and dequeue + * pointer after the command completes. + */ + struct xhci_segment *queued_deq_seg; + union xhci_trb *queued_deq_ptr; /* * Sometimes the xHC can not process isochronous endpoint ring quickly * enough, and it will miss some isoc tds on the ring and generate From 24dfb5f904c1478c1ce62f91d654ccc3bfd420dd Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 23 Feb 2011 18:12:29 -0800 Subject: [PATCH 0036/2556] xhci: Fix cycle bit calculation during stall handling. commit 01a1fdb9a7afa5e3c14c9316d6f380732750b4e4 upstream. When an endpoint stalls, we need to update the xHCI host's internal dequeue pointer to move it past the stalled transfer. This includes updating the cycle bit (TRB ownership bit) if we have moved the dequeue pointer past a link TRB with the toggle cycle bit set. When we're trying to find the new dequeue segment, find_trb_seg() is supposed to keep track of whether we've passed any link TRBs with the toggle cycle bit set. However, this while loop's body while (cur_seg->trbs > trb || &cur_seg->trbs[TRBS_PER_SEGMENT - 1] < trb) { Will never get executed if the ring only contains one segment. find_trb_seg() will return immediately, without updating the new cycle bit. Since find_trb_seg() has no idea where in the segment the TD that stalled was, make the caller, xhci_find_new_dequeue_state(), check for this special case and update the cycle bit accordingly. This patch should be queued to kernels all the way back to 2.6.31. Signed-off-by: Sarah Sharp Tested-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 699676d1df888..d3f0406f6f20a 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -500,6 +500,20 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, state->new_cycle_state = ~(state->new_cycle_state) & 0x1; next_trb(xhci, ep_ring, &state->new_deq_seg, &state->new_deq_ptr); + /* + * If there is only one segment in a ring, find_trb_seg()'s while loop + * will not run, and it will return before it has a chance to see if it + * needs to toggle the cycle bit. It can't tell if the stalled transfer + * ended just before the link TRB on a one-segment ring, or if the TD + * wrapped around the top of the ring, because it doesn't have the TD in + * question. Look for the one-segment case where stalled TRB's address + * is greater than the new dequeue pointer address. + */ + if (ep_ring->first_seg == ep_ring->first_seg->next && + state->new_deq_ptr < dev->eps[ep_index].stopped_trb) + state->new_cycle_state ^= 0x1; + xhci_dbg(xhci, "Cycle state = 0x%x\n", state->new_cycle_state); + /* Don't update the ring cycle state for the producer (us). */ xhci_dbg(xhci, "New dequeue segment = %p (virtual)\n", state->new_deq_seg); From eefd60cea1fb86b8c46778e19753c924b60fadf7 Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Mon, 28 Feb 2011 18:11:27 -0800 Subject: [PATCH 0037/2556] USB: Add support for SuperSpeed isoc endpoints commit 500132a0f26ad7d9916102193cbc6c1b1becb373 upstream. Use the Mult and bMaxBurst values from the endpoint companion descriptor to calculate the max length of an isoc transfer. Add USB_SS_MULT macro to access Mult field of bmAttributes, at Sarah's suggestion. This patch should be queued for the 2.6.36 and 2.6.37 stable trees, since those were the first kernels to have isochronous support for SuperSpeed devices. Signed-off-by: Paul Zimmerman Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/urb.c | 11 ++++++++++- include/linux/usb/ch9.h | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index c14fc082864f1..ae334b067c13e 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -366,7 +366,16 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) if (xfertype == USB_ENDPOINT_XFER_ISOC) { int n, len; - /* FIXME SuperSpeed isoc endpoints have up to 16 bursts */ + /* SuperSpeed isoc endpoints have up to 16 bursts of up to + * 3 packets each + */ + if (dev->speed == USB_SPEED_SUPER) { + int burst = 1 + ep->ss_ep_comp.bMaxBurst; + int mult = USB_SS_MULT(ep->ss_ep_comp.bmAttributes); + max *= burst; + max *= mult; + } + /* "high bandwidth" mode, 1-3 packets/uframe? */ if (dev->speed == USB_SPEED_HIGH) { int mult = 1 + ((max >> 11) & 0x03); diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index ab461948b579e..76d896cc842e9 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -585,6 +585,8 @@ struct usb_ss_ep_comp_descriptor { #define USB_DT_SS_EP_COMP_SIZE 6 /* Bits 4:0 of bmAttributes if this is a bulk endpoint */ #define USB_SS_MAX_STREAMS(p) (1 << (p & 0x1f)) +/* Bits 1:0 of bmAttributes if this is an isoc endpoint */ +#define USB_SS_MULT(p) (1 + ((p) & 0x3)) /*-------------------------------------------------------------------------*/ From 3e3aecb4096a92f2dd8ac1abdabf4dd68218d67e Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 4 Mar 2011 13:37:50 +0100 Subject: [PATCH 0038/2556] ALSA: HDA: Enable surround and subwoofer on Lenovo Ideapad Y530 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 32eea3884debb65ec1da633bc5df5aee23879865 upstream. The pin config values would change the association instead of the sequence, this commit fixes that up. Tested-by: Bartłomiej Żogała Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4261bb8eec1d5..8caf593fcf20e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10748,6 +10748,7 @@ static struct alc_config_preset alc882_presets[] = { */ enum { PINFIX_ABIT_AW9D_MAX, + PINFIX_LENOVO_Y530, PINFIX_PB_M5210, PINFIX_ACER_ASPIRE_7736, }; @@ -10762,6 +10763,14 @@ static const struct alc_fixup alc882_fixups[] = { { } } }, + [PINFIX_LENOVO_Y530] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x15, 0x99130112 }, /* rear int speakers */ + { 0x16, 0x99130111 }, /* subwoofer */ + { } + } + }, [PINFIX_PB_M5210] = { .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { @@ -10777,6 +10786,7 @@ static const struct alc_fixup alc882_fixups[] = { static struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", PINFIX_PB_M5210), + SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", PINFIX_LENOVO_Y530), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX), SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736), {} From 8a805f1e7f3aea14039f7d57836e5f6ea993f37e Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 4 Mar 2011 14:08:30 +0100 Subject: [PATCH 0039/2556] ALSA: HDA: Fix volume control naming for surround speakers on Realtek auto-parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ebbeb3d6aa22433c218da6f29fd7b3ebc89b87ea upstream. When more than one pair of internal speakers is present, allow names according to their channels. Tested-by: Bartłomiej Żogała Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8caf593fcf20e..8c61566bdc583 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5151,7 +5151,9 @@ static const char *alc_get_line_out_pfx(const struct auto_pin_cfg *cfg, switch (cfg->line_out_type) { case AUTO_PIN_SPEAKER_OUT: - return "Speaker"; + if (cfg->line_outs == 1) + return "Speaker"; + break; case AUTO_PIN_HP_OUT: return "Headphone"; default: From f7ff876d0fba7e28c10c78a5004c350f816b8a6e Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 4 Mar 2011 14:22:25 +0100 Subject: [PATCH 0040/2556] ALSA: HDA: Fixup unnecessary volume control index on Realtek ALC88x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 7e59e097c09b82760bb0fe08b0fa2b704d76c3f4 upstream. Without this change, a volume control named "Surround" or "Side" would get an unnecessary index, causing it to be ignored by the vmaster and PulseAudio. Tested-by: Bartłomiej Żogała Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8c61566bdc583..ffb05bd54badc 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5207,16 +5207,19 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, return err; } else { const char *name = pfx; - if (!name) + int index = i; + if (!name) { name = chname[i]; + index = 0; + } err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - name, i, + name, index, HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); if (err < 0) return err; err = __add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - name, i, + name, index, HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); if (err < 0) From 11052185a6eb08d7eb8b52a840f33c665226c8f1 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 4 Mar 2011 16:54:52 +0100 Subject: [PATCH 0041/2556] ALSA: HDA: Realtek ALC88x: Do not over-initialize speakers and hp that are primary outputs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 0a3fabe30e1a3b2037a12b863b8c45fffce38ee9 upstream. Do not initialize again the what has already been initialized as multi outs, as this breaks surround speakers. Tested-by: Bartłomiej Żogała Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 37 ++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ffb05bd54badc..2a1c4124f300b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10844,23 +10844,28 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec) hda_nid_t pin, dac; int i; - for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) { - pin = spec->autocfg.hp_pins[i]; - if (!pin) - break; - dac = spec->multiout.hp_nid; - if (!dac) - dac = spec->multiout.dac_nids[0]; /* to front */ - alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); + if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) { + for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) { + pin = spec->autocfg.hp_pins[i]; + if (!pin) + break; + dac = spec->multiout.hp_nid; + if (!dac) + dac = spec->multiout.dac_nids[0]; /* to front */ + alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); + } } - for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) { - pin = spec->autocfg.speaker_pins[i]; - if (!pin) - break; - dac = spec->multiout.extra_out_nid[0]; - if (!dac) - dac = spec->multiout.dac_nids[0]; /* to front */ - alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); + + if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) { + for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) { + pin = spec->autocfg.speaker_pins[i]; + if (!pin) + break; + dac = spec->multiout.extra_out_nid[0]; + if (!dac) + dac = spec->multiout.dac_nids[0]; /* to front */ + alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); + } } } From 6f1d6832db2fbade524f37cb23d246b45cb01e2a Mon Sep 17 00:00:00 2001 From: Vitaliy Kulikov Date: Wed, 9 Mar 2011 19:47:43 -0600 Subject: [PATCH 0042/2556] ALSA: hda - fix digital mic selection in mixer on 92HD8X codecs commit 094a42452abd5564429045e210281c6d22e67fca upstream. When the mux for digital mic is different from the mux for other mics, the current auto-parser doesn't handle them in a right way but provides only one mic. This patch fixes the issue. Signed-off-by: Vitaliy Kulikov Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_sigmatel.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index bd7b123f64407..052062dd523ba 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -757,7 +757,7 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e struct sigmatel_spec *spec = codec->spec; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); const struct hda_input_mux *imux = spec->input_mux; - unsigned int idx, prev_idx; + unsigned int idx, prev_idx, didx; idx = ucontrol->value.enumerated.item[0]; if (idx >= imux->num_items) @@ -769,7 +769,8 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e snd_hda_codec_write_cache(codec, spec->mux_nids[adc_idx], 0, AC_VERB_SET_CONNECT_SEL, imux->items[idx].index); - if (prev_idx >= spec->num_analog_muxes) { + if (prev_idx >= spec->num_analog_muxes && + spec->mux_nids[adc_idx] != spec->dmux_nids[adc_idx]) { imux = spec->dinput_mux; /* 0 = analog */ snd_hda_codec_write_cache(codec, @@ -779,9 +780,13 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e } } else { imux = spec->dinput_mux; + /* first dimux item is hardcoded to select analog imux, + * so lets skip it + */ + didx = idx - spec->num_analog_muxes + 1; snd_hda_codec_write_cache(codec, spec->dmux_nids[adc_idx], 0, AC_VERB_SET_CONNECT_SEL, - imux->items[idx - 1].index); + imux->items[didx].index); } spec->cur_mux[adc_idx] = idx; return 1; From 24449f05b663f1984ee6ecdba5a0d2ff2a1e0462 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Mar 2011 12:51:11 +0100 Subject: [PATCH 0043/2556] ALSA: hda - Initialize special cases for input src in init phase commit 584c0c4c359bdac37d94157f8d7fc513d26c8328 upstream. Currently some special handling for the unusual case like dual-ADCs or a single-input-src is done in the tree-parse time in set_capture_mixer(). But this setup could be overwritten by static init verbs. This patch moves the initialization into the init phase so that such input-src setup won't be lost. Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2a1c4124f300b..acd209958ea24 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -394,6 +394,7 @@ struct alc_spec { /* other flags */ unsigned int no_analog :1; /* digital I/O only */ unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */ + unsigned int single_input_src:1; int init_amp; int codec_variant; /* flag for other variants */ @@ -3919,6 +3920,8 @@ static struct hda_amp_list alc880_lg_loopbacks[] = { * Common callbacks */ +static void alc_init_special_input_src(struct hda_codec *codec); + static int alc_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -3929,6 +3932,7 @@ static int alc_init(struct hda_codec *codec) for (i = 0; i < spec->num_init_verbs; i++) snd_hda_sequence_write(codec, spec->init_verbs[i]); + alc_init_special_input_src(codec); if (spec->init_hook) spec->init_hook(codec); @@ -5590,6 +5594,7 @@ static void fixup_single_adc(struct hda_codec *codec) spec->capsrc_nids += i; spec->adc_nids += i; spec->num_adc_nids = 1; + spec->single_input_src = 1; } } @@ -5601,6 +5606,16 @@ static void fixup_dual_adc_switch(struct hda_codec *codec) init_capsrc_for_pin(codec, spec->int_mic.pin); } +/* initialize some special cases for input sources */ +static void alc_init_special_input_src(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + if (spec->dual_adc_switch) + fixup_dual_adc_switch(codec); + else if (spec->single_input_src) + init_capsrc_for_pin(codec, spec->autocfg.inputs[0].pin); +} + static void set_capture_mixer(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -5616,7 +5631,7 @@ static void set_capture_mixer(struct hda_codec *codec) int mux = 0; int num_adcs = spec->num_adc_nids; if (spec->dual_adc_switch) - fixup_dual_adc_switch(codec); + num_adcs = 1; else if (spec->auto_mic) fixup_automic_adc(codec); else if (spec->input_mux) { @@ -5625,8 +5640,6 @@ static void set_capture_mixer(struct hda_codec *codec) else if (spec->input_mux->num_items == 1) fixup_single_adc(codec); } - if (spec->dual_adc_switch) - num_adcs = 1; spec->cap_mixer = caps[mux][num_adcs - 1]; } } From 7d15e96db9a833956b345622a7cc465534a209c5 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Wed, 9 Mar 2011 18:38:57 +0100 Subject: [PATCH 0044/2556] HID: hid-magicmouse: Correct touch orientation direction commit 2d9ca4e9f393d81d8f37ed37505aecbf3a5e1bd6 upstream. The magic trackpad and mouse both report touch orientation in opposite direction to the bcm5974 driver and what is written in Documents/input/multi-touch-protocol.txt. This patch reverts the direction, so that all in-kernel devices with this feature behave the same way. Since no known application has been utilizing this information yet, it seems appropriate also for stable. Cc: Michael Poole Signed-off-by: Henrik Rydberg Acked-by: Chase Douglas Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-magicmouse.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 698e6459fd0b3..318cc40df92d6 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -258,7 +258,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda input_report_abs(input, ABS_MT_TRACKING_ID, id); input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major << 2); input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor << 2); - input_report_abs(input, ABS_MT_ORIENTATION, orientation); + input_report_abs(input, ABS_MT_ORIENTATION, -orientation); input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); @@ -397,7 +397,7 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, 15, 0, 0); input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 4, 0); input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 4, 0); - input_set_abs_params(input, ABS_MT_ORIENTATION, -32, 31, 1, 0); + input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0); /* Note: Touch Y position from the device is inverted relative * to how pointer motion is reported (and relative to how USB From 49cb4dc7383640e12e10bc1a3128ab36fe92220c Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Wed, 16 Mar 2011 14:13:53 -0300 Subject: [PATCH 0045/2556] HID: add support for Ortek PKB-1700 commit 270fdc0748bd3f7b625caff985f2fcf8e2185ec7 upstream. As reported on http://ubuntuforums.org/showthread.php?t=1594007 the PKB-1700 needs same special handling as WKB-2000. This change is originally based on patch posted by user asmoore82 on the Ubuntu forums. Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/Kconfig | 4 ++-- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-ortek.c | 3 ++- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 2560f01c1a632..3a07b4da4eca2 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -319,10 +319,10 @@ config HID_NTRIG Support for N-Trig touch screen. config HID_ORTEK - tristate "Ortek WKB-2000 wireless keyboard and mouse trackpad" + tristate "Ortek PKB-1700/WKB-2000 wireless keyboard and mouse trackpad" depends on USB_HID ---help--- - Support for Ortek WKB-2000 wireless keyboard + mouse trackpad. + Support for Ortek PKB-1700/WKB-2000 wireless keyboard + mouse trackpad. config HID_PANTHERLORD tristate "Pantherlord/GreenAsia game controller" diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index d678cf3d33d5e..295e2487102bf 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1400,6 +1400,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_16) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_17) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) }, { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 92a0d61a7379c..5f5cc9100aff9 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -466,6 +466,7 @@ #define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 #define USB_VENDOR_ID_ORTEK 0x05a4 +#define USB_DEVICE_ID_ORTEK_PKB1700 0x1700 #define USB_DEVICE_ID_ORTEK_WKB2000 0x2000 #define USB_VENDOR_ID_PANJIT 0x134c diff --git a/drivers/hid/hid-ortek.c b/drivers/hid/hid-ortek.c index e90edfc63051b..ad6faa6c0ceac 100644 --- a/drivers/hid/hid-ortek.c +++ b/drivers/hid/hid-ortek.c @@ -1,5 +1,5 @@ /* - * HID driver for Ortek WKB-2000 (wireless keyboard + mouse trackpad). + * HID driver for Ortek PKB-1700/WKB-2000 (wireless keyboard + mouse trackpad). * Fixes LogicalMaximum error in USB report description, see * http://bugzilla.kernel.org/show_bug.cgi?id=14787 * @@ -30,6 +30,7 @@ static __u8 *ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc, } static const struct hid_device_id ortek_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) }, { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, { } }; From f9a0641774ddd24d99b97975e33bc323e33ecd25 Mon Sep 17 00:00:00 2001 From: "Brandeburg, Jesse" Date: Mon, 14 Feb 2011 09:05:02 -0800 Subject: [PATCH 0046/2556] PCI: remove quirk for pre-production systems commit b99af4b002e4908d1a5cdaf424529bdf1dc69768 upstream. Revert commit 7eb93b175d4de9438a4b0af3a94a112cb5266944 Author: Yu Zhao Date: Fri Apr 3 15:18:11 2009 +0800 PCI: SR-IOV quirk for Intel 82576 NIC If BIOS doesn't allocate resources for the SR-IOV BARs, zero the Flash BAR and program the SR-IOV BARs to use the old Flash Memory Space. Please refer to Intel 82576 Gigabit Ethernet Controller Datasheet section 7.9.2.14.2 for details. http://download.intel.com/design/network/datashts/82576_Datasheet.pdf Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes This quirk was added before SR-IOV was in production and now all machines that originally had this issue alreayd have bios updates to correct the issue. The quirk itself is no longer needed and in fact causes bugs if run. Remove it. Signed-off-by: Jesse Brandeburg CC: Yu Zhao CC: Jesse Barnes Signed-off-by: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- drivers/pci/quirks.c | 52 -------------------------------------------- 1 file changed, 52 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 53a786fd0d40c..ff01bfb3cc291 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2618,58 +2618,6 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x4375, #endif /* CONFIG_PCI_MSI */ -#ifdef CONFIG_PCI_IOV - -/* - * For Intel 82576 SR-IOV NIC, if BIOS doesn't allocate resources for the - * SR-IOV BARs, zero the Flash BAR and program the SR-IOV BARs to use the - * old Flash Memory Space. - */ -static void __devinit quirk_i82576_sriov(struct pci_dev *dev) -{ - int pos, flags; - u32 bar, start, size; - - if (PAGE_SIZE > 0x10000) - return; - - flags = pci_resource_flags(dev, 0); - if ((flags & PCI_BASE_ADDRESS_SPACE) != - PCI_BASE_ADDRESS_SPACE_MEMORY || - (flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) != - PCI_BASE_ADDRESS_MEM_TYPE_32) - return; - - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); - if (!pos) - return; - - pci_read_config_dword(dev, pos + PCI_SRIOV_BAR, &bar); - if (bar & PCI_BASE_ADDRESS_MEM_MASK) - return; - - start = pci_resource_start(dev, 1); - size = pci_resource_len(dev, 1); - if (!start || size != 0x400000 || start & (size - 1)) - return; - - pci_resource_flags(dev, 1) = 0; - pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, 0); - pci_write_config_dword(dev, pos + PCI_SRIOV_BAR, start); - pci_write_config_dword(dev, pos + PCI_SRIOV_BAR + 12, start + size / 2); - - dev_info(&dev->dev, "use Flash Memory Space for SR-IOV BARs\n"); -} -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10c9, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10e6, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10e7, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10e8, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x150a, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x150d, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1518, quirk_i82576_sriov); - -#endif /* CONFIG_PCI_IOV */ - /* Allow manual resource allocation for PCI hotplug bridges * via pci=hpmemsize=nnM and pci=hpiosize=nnM parameters. For * some PCI-PCI hotplug bridges, like PLX 6254 (former HINT HB6), From 679b8fe6cc6ee420931d19fa63ccc066af1e4c88 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 28 Feb 2011 10:45:09 +0100 Subject: [PATCH 0047/2556] PCI: add more checking to ICH region quirks commit cdb9755849fbaf2bb9c0a009ba5baa817a0f152d upstream. Per ICH4 and ICH6 specs, ACPI and GPIO regions are valid iff ACPI_EN and GPIO_EN bits are set to 1. Add checks for these bits into the quirks prior to the region creation. While at it, name the constants by macros. Signed-off-by: Jiri Slaby Cc: Bjorn Helgaas Cc: "David S. Miller" Cc: Thomas Renninger Signed-off-by: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- drivers/pci/quirks.c | 45 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index ff01bfb3cc291..9e23912c97ac9 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -533,6 +533,17 @@ static void __devinit quirk_piix4_acpi(struct pci_dev *dev) DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, quirk_piix4_acpi); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3, quirk_piix4_acpi); +#define ICH_PMBASE 0x40 +#define ICH_ACPI_CNTL 0x44 +#define ICH4_ACPI_EN 0x10 +#define ICH6_ACPI_EN 0x80 +#define ICH4_GPIOBASE 0x58 +#define ICH4_GPIO_CNTL 0x5c +#define ICH4_GPIO_EN 0x10 +#define ICH6_GPIOBASE 0x48 +#define ICH6_GPIO_CNTL 0x4c +#define ICH6_GPIO_EN 0x10 + /* * ICH4, ICH4-M, ICH5, ICH5-M ACPI: Three IO regions pointed to by longwords at * 0x40 (128 bytes of ACPI, GPIO & TCO registers) @@ -541,12 +552,21 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3, qui static void __devinit quirk_ich4_lpc_acpi(struct pci_dev *dev) { u32 region; + u8 enable; - pci_read_config_dword(dev, 0x40, ®ion); - quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, "ICH4 ACPI/GPIO/TCO"); + pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable); + if (enable & ICH4_ACPI_EN) { + pci_read_config_dword(dev, ICH_PMBASE, ®ion); + quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, + "ICH4 ACPI/GPIO/TCO"); + } - pci_read_config_dword(dev, 0x58, ®ion); - quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES+1, "ICH4 GPIO"); + pci_read_config_byte(dev, ICH4_GPIO_CNTL, &enable); + if (enable & ICH4_GPIO_EN) { + pci_read_config_dword(dev, ICH4_GPIOBASE, ®ion); + quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES + 1, + "ICH4 GPIO"); + } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, quirk_ich4_lpc_acpi); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0, quirk_ich4_lpc_acpi); @@ -562,12 +582,21 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, qui static void __devinit ich6_lpc_acpi_gpio(struct pci_dev *dev) { u32 region; + u8 enable; - pci_read_config_dword(dev, 0x40, ®ion); - quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, "ICH6 ACPI/GPIO/TCO"); + pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable); + if (enable & ICH6_ACPI_EN) { + pci_read_config_dword(dev, ICH_PMBASE, ®ion); + quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, + "ICH6 ACPI/GPIO/TCO"); + } - pci_read_config_dword(dev, 0x48, ®ion); - quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES+1, "ICH6 GPIO"); + pci_read_config_byte(dev, ICH6_GPIO_CNTL, &enable); + if (enable & ICH4_GPIO_EN) { + pci_read_config_dword(dev, ICH6_GPIOBASE, ®ion); + quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES + 1, + "ICH6 GPIO"); + } } static void __devinit ich6_lpc_generic_decode(struct pci_dev *dev, unsigned reg, const char *name, int dynsize) From dc3ea91330b3943f9e16f6709f1097ca819316b2 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 28 Feb 2011 10:45:10 +0100 Subject: [PATCH 0048/2556] PCI: do not create quirk I/O regions below PCIBIOS_MIN_IO for ICH commit 87e3dc3855430bd254370afc79f2ed92250f5b7c upstream. Some broken BIOSes on ICH4 chipset report an ACPI region which is in conflict with legacy IDE ports when ACPI is disabled. Even though the regions overlap, IDE ports are working correctly (we cannot find out the decoding rules on chipsets). So the only problem is the reported region itself, if we don't reserve the region in the quirk everything works as expected. This patch avoids reserving any quirk regions below PCIBIOS_MIN_IO which is 0x1000. Some regions might be (and are by a fast google query) below this border, but the only difference is that they won't be reserved anymore. They should still work though the same as before. The conflicts look like (1f.0 is bridge, 1f.1 is IDE ctrl): pci 0000:00:1f.1: address space collision: [io 0x0170-0x0177] conflicts with 0000:00:1f.0 [io 0x0100-0x017f] At 0x0100 a 128 bytes long ACPI region is reported in the quirk for ICH4. ata_piix then fails to find disks because the IDE legacy ports are zeroed: ata_piix 0000:00:1f.1: device not available (can't reserve [io 0x0000-0x0007]) References: https://bugzilla.novell.com/show_bug.cgi?id=558740 Signed-off-by: Jiri Slaby Cc: Bjorn Helgaas Cc: "David S. Miller" Cc: Thomas Renninger Signed-off-by: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- drivers/pci/quirks.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 9e23912c97ac9..bd80f63784635 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -554,18 +554,30 @@ static void __devinit quirk_ich4_lpc_acpi(struct pci_dev *dev) u32 region; u8 enable; + /* + * The check for PCIBIOS_MIN_IO is to ensure we won't create a conflict + * with low legacy (and fixed) ports. We don't know the decoding + * priority and can't tell whether the legacy device or the one created + * here is really at that address. This happens on boards with broken + * BIOSes. + */ + pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable); if (enable & ICH4_ACPI_EN) { pci_read_config_dword(dev, ICH_PMBASE, ®ion); - quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, - "ICH4 ACPI/GPIO/TCO"); + region &= PCI_BASE_ADDRESS_IO_MASK; + if (region >= PCIBIOS_MIN_IO) + quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, + "ICH4 ACPI/GPIO/TCO"); } pci_read_config_byte(dev, ICH4_GPIO_CNTL, &enable); if (enable & ICH4_GPIO_EN) { pci_read_config_dword(dev, ICH4_GPIOBASE, ®ion); - quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES + 1, - "ICH4 GPIO"); + region &= PCI_BASE_ADDRESS_IO_MASK; + if (region >= PCIBIOS_MIN_IO) + quirk_io_region(dev, region, 64, + PCI_BRIDGE_RESOURCES + 1, "ICH4 GPIO"); } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, quirk_ich4_lpc_acpi); @@ -587,15 +599,19 @@ static void __devinit ich6_lpc_acpi_gpio(struct pci_dev *dev) pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable); if (enable & ICH6_ACPI_EN) { pci_read_config_dword(dev, ICH_PMBASE, ®ion); - quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, - "ICH6 ACPI/GPIO/TCO"); + region &= PCI_BASE_ADDRESS_IO_MASK; + if (region >= PCIBIOS_MIN_IO) + quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, + "ICH6 ACPI/GPIO/TCO"); } pci_read_config_byte(dev, ICH6_GPIO_CNTL, &enable); if (enable & ICH4_GPIO_EN) { pci_read_config_dword(dev, ICH6_GPIOBASE, ®ion); - quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES + 1, - "ICH6 GPIO"); + region &= PCI_BASE_ADDRESS_IO_MASK; + if (region >= PCIBIOS_MIN_IO) + quirk_io_region(dev, region, 64, + PCI_BRIDGE_RESOURCES + 1, "ICH6 GPIO"); } } From f8cd8789d24ead2e737aae898ab8d5ee0291bb75 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 13 Jan 2011 19:47:56 +0000 Subject: [PATCH 0049/2556] PCI: sysfs: Fix failure path for addition of "vpd" attribute commit 0f12a4e29368a9476076515881d9ef4e5876c6e2 upstream. Commit 280c73d ("PCI: centralize the capabilities code in pci-sysfs.c") changed the initialisation of the "rom" and "vpd" attributes, and made the failure path for the "vpd" attribute incorrect. We must free the new attribute structure (attr), but instead we currently free dev->vpd->attr. That will normally be NULL, resulting in a memory leak, but it might be a stale pointer, resulting in a double-free. Found by inspection; compile-tested only. Signed-off-by: Ben Hutchings Signed-off-by: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci-sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index ea25e5bfcf238..c85438a367d53 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1088,7 +1088,7 @@ static int pci_create_capabilities_sysfs(struct pci_dev *dev) attr->write = write_vpd_attr; retval = sysfs_create_bin_file(&dev->dev.kobj, attr); if (retval) { - kfree(dev->vpd->attr); + kfree(attr); return retval; } dev->vpd->attr = attr; From f97aa1f43ed9e241a05f7e4d2e4afedf97356f7a Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Thu, 17 Mar 2011 18:32:24 -0400 Subject: [PATCH 0050/2556] ALSA: sound/pci/asihpi: check adapter index in hpi_ioctl commit 4a122c10fbfe9020df469f0f669da129c5757671 upstream. The user-supplied index into the adapters array needs to be checked, or an out-of-bounds kernel pointer could be accessed and used, leading to potentially exploitable memory corruption. Signed-off-by: Dan Rosenberg Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/asihpi/hpioctl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index 22dbd91811a4e..448dd01943f50 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -155,6 +155,11 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) goto out; } + if (hm->h.adapter_index >= HPI_MAX_ADAPTERS) { + err = -EINVAL; + goto out; + } + pa = &adapters[hm->h.adapter_index]; hr->h.size = 0; if (hm->h.object == HPI_OBJ_SUBSYSTEM) { From 74d5036bb343ce72af45f8bdcbd5dd87459d1216 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Mar 2011 07:31:53 +0100 Subject: [PATCH 0051/2556] ALSA: aloop - Fix possible IRQ lock inversion commit 98d21df431ad55281e1abf780f8d51e3391900b2 upstream. loopback_pos_update() can be called in the timer callback, thus the lock held should be irq-safe. Otherwise you'll get AB/BA deadlock together with substream->self_group.lock. Reported-and-tested-by: Knut Petersen Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/drivers/aloop.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 12b44b0b67771..a0da7755fceaa 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -482,8 +482,9 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable) cable->streams[SNDRV_PCM_STREAM_CAPTURE]; unsigned long delta_play = 0, delta_capt = 0; unsigned int running; + unsigned long flags; - spin_lock(&cable->lock); + spin_lock_irqsave(&cable->lock, flags); running = cable->running ^ cable->pause; if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { delta_play = jiffies - dpcm_play->last_jiffies; @@ -495,10 +496,8 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable) dpcm_capt->last_jiffies += delta_capt; } - if (delta_play == 0 && delta_capt == 0) { - spin_unlock(&cable->lock); - return running; - } + if (delta_play == 0 && delta_capt == 0) + goto unlock; if (delta_play > delta_capt) { loopback_bytepos_update(dpcm_play, delta_play - delta_capt, @@ -510,14 +509,14 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable) delta_capt = delta_play; } - if (delta_play == 0 && delta_capt == 0) { - spin_unlock(&cable->lock); - return running; - } + if (delta_play == 0 && delta_capt == 0) + goto unlock; + /* note delta_capt == delta_play at this moment */ loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY); loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY); - spin_unlock(&cable->lock); + unlock: + spin_unlock_irqrestore(&cable->lock, flags); return running; } From 5794354c837af7dbbe7793d689198c446ab68bfd Mon Sep 17 00:00:00 2001 From: Przemyslaw Bruski Date: Sun, 13 Mar 2011 16:18:56 +0100 Subject: [PATCH 0052/2556] ALSA: ctxfi - Fix incorrect SPDIF status bit mask commit 4c1847e884efddcc3ede371f7839e5e65b25c34d upstream. SPDIF status mask creation was incorrect. Signed-off-by: Przemyslaw Bruski Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/ctxfi/ctatc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 1bff80cde0a2f..b9321544c31c4 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -869,7 +869,7 @@ spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm) mutex_lock(&atc->atc_mutex); dao->ops->get_spos(dao, &status); if (((status >> 24) & IEC958_AES3_CON_FS) != iec958_con_fs) { - status &= ((~IEC958_AES3_CON_FS) << 24); + status &= ~(IEC958_AES3_CON_FS << 24); status |= (iec958_con_fs << 24); dao->ops->set_spos(dao, status); dao->ops->commit_write(dao); From f13d689d83102b120af29b659ade12da22ab717f Mon Sep 17 00:00:00 2001 From: Przemyslaw Bruski Date: Sun, 13 Mar 2011 16:18:57 +0100 Subject: [PATCH 0053/2556] ALSA: ctxfi - Fix SPDIF status retrieval commit f164753a263bfd2daaf3e0273b179de7e099c57d upstream. SDPIF status retrieval always returned the default settings instead of the actual ones. Signed-off-by: Przemyslaw Bruski Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/ctxfi/ctmixer.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index 15c1e7271ea88..c3519ff42fbbf 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -566,19 +566,6 @@ static int ct_spdif_get_mask(struct snd_kcontrol *kcontrol, return 0; } -static int ct_spdif_default_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - unsigned int status = SNDRV_PCM_DEFAULT_CON_SPDIF; - - ucontrol->value.iec958.status[0] = (status >> 0) & 0xff; - ucontrol->value.iec958.status[1] = (status >> 8) & 0xff; - ucontrol->value.iec958.status[2] = (status >> 16) & 0xff; - ucontrol->value.iec958.status[3] = (status >> 24) & 0xff; - - return 0; -} - static int ct_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -586,6 +573,10 @@ static int ct_spdif_get(struct snd_kcontrol *kcontrol, unsigned int status; atc->spdif_out_get_status(atc, &status); + + if (status == 0) + status = SNDRV_PCM_DEFAULT_CON_SPDIF; + ucontrol->value.iec958.status[0] = (status >> 0) & 0xff; ucontrol->value.iec958.status[1] = (status >> 8) & 0xff; ucontrol->value.iec958.status[2] = (status >> 16) & 0xff; @@ -629,7 +620,7 @@ static struct snd_kcontrol_new iec958_default_ctl = { .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), .count = 1, .info = ct_spdif_info, - .get = ct_spdif_default_get, + .get = ct_spdif_get, .put = ct_spdif_put, .private_value = MIXER_IEC958_DEFAULT }; From 44154ce1db1501e54a0da6f6ad2d49c9c2ed3bb0 Mon Sep 17 00:00:00 2001 From: Przemyslaw Bruski Date: Sun, 13 Mar 2011 16:18:58 +0100 Subject: [PATCH 0054/2556] ALSA: ctxfi - Clear input settings before initialization commit efed5f26664f93991c929d5bb343e65f900d72bc upstream. Clear input settings before initialization. Signed-off-by: Przemyslaw Bruski Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/ctxfi/ctdaio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index af56eb949bded..47d9ea97de02e 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -176,6 +176,7 @@ static int dao_set_left_input(struct dao *dao, struct rsc *input) if (!entry) return -ENOMEM; + dao->ops->clear_left_input(dao); /* Program master and conjugate resources */ input->ops->master(input); daio->rscl.ops->master(&daio->rscl); @@ -204,6 +205,7 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input) if (!entry) return -ENOMEM; + dao->ops->clear_right_input(dao); /* Program master and conjugate resources */ input->ops->master(input); daio->rscr.ops->master(&daio->rscr); From 1502ccf37a1cafc8ff1f45f8999854b6210abf91 Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Fri, 11 Mar 2011 11:05:38 +0200 Subject: [PATCH 0055/2556] ASoC: PXA: Z2: Fix codec pin name commit 64c25a92e865f06ad8782fbdaa1e2a97d50acf73 upstream. MONO was renamed to MONO1. Signed-off-by: Vasily Khoruzhick Acked-by: Liam Girdwood Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/pxa/z2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c index 3ceaef68e01de..838a0d540c5a3 100644 --- a/sound/soc/pxa/z2.c +++ b/sound/soc/pxa/z2.c @@ -147,7 +147,7 @@ static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_disable_pin(dapm, "LINPUT3"); snd_soc_dapm_disable_pin(dapm, "RINPUT3"); snd_soc_dapm_disable_pin(dapm, "OUT3"); - snd_soc_dapm_disable_pin(dapm, "MONO"); + snd_soc_dapm_disable_pin(dapm, "MONO1"); /* Add z2 specific widgets */ snd_soc_dapm_new_controls(dapm, wm8750_dapm_widgets, From 97272fc63f9c067b9c932e5b289f934044615603 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 2 Mar 2011 15:52:51 -0800 Subject: [PATCH 0056/2556] target: Fix volume size misreporting for volumes > 2TB commit 904f0bc482201fa86e75c330d79dfd11be494cf8 upstream. the target infrastructure fails to send the correct conventional size to READ_CAPACITY that force a retry with READ_CAPACITY_16, which reads the capacity for devices > 2TB. Fix by adding the correct return to trigger RC(16). Reported-by: Ben Jarvis Signed-off-by: Signed-off-by: Nicholas A. Bellinger Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_cdb.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index 366080baf4743..7f19c8b7b84c7 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -667,7 +667,13 @@ target_emulate_readcapacity(struct se_cmd *cmd) { struct se_device *dev = SE_DEV(cmd); unsigned char *buf = cmd->t_task->t_task_buf; - u32 blocks = dev->transport->get_blocks(dev); + unsigned long long blocks_long = dev->transport->get_blocks(dev); + u32 blocks; + + if (blocks_long >= 0x00000000ffffffff) + blocks = 0xffffffff; + else + blocks = (u32)blocks_long; buf[0] = (blocks >> 24) & 0xff; buf[1] = (blocks >> 16) & 0xff; From 0b67526b4e76946e2b2e2da4cf8175ad78ae2879 Mon Sep 17 00:00:00 2001 From: Joseph Gruher Date: Wed, 5 Jan 2011 16:00:21 -0500 Subject: [PATCH 0057/2556] scsi_dh_alua: fix deadlock in stpg_endio commit ed0f36bc5719b25659b637f80ceea85494b84502 upstream. The use of blk_execute_rq_nowait() implies __blk_put_request() is needed in stpg_endio() rather than blk_put_request() -- blk_finish_request() is called with queue lock already held. Signed-off-by: Joseph Gruher Signed-off-by: Ilgu Hong Signed-off-by: Mike Snitzer Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/device_handler/scsi_dh_alua.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 6b729324b8d37..30f2b333ccbdb 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -285,7 +285,8 @@ static void stpg_endio(struct request *req, int error) print_alua_state(h->state)); } done: - blk_put_request(req); + req->end_io_data = NULL; + __blk_put_request(req->q, req); if (h->callback_fn) { h->callback_fn(h->callback_data, err); h->callback_fn = h->callback_data = NULL; From bac39af7892a94d7212ad9c5d269a6ac6d1d5fde Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 15 Mar 2011 19:56:30 -0400 Subject: [PATCH 0058/2556] SUNRPC: Ensure we always run the tk_callback before tk_action commit e020c6800c9621a77223bf2c1ff68180e41e8ebf upstream. This fixes a race in which the task->tk_callback() puts the rpc_task to sleep, setting a new callback. Under certain circumstances, the current code may end up executing the task->tk_action before it gets round to the callback. Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/sched.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 59e599498e37f..17c3e3aade292 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -637,14 +637,12 @@ static void __rpc_execute(struct rpc_task *task) save_callback = task->tk_callback; task->tk_callback = NULL; save_callback(task); - } - - /* - * Perform the next FSM step. - * tk_action may be NULL when the task has been killed - * by someone else. - */ - if (!RPC_IS_QUEUED(task)) { + } else { + /* + * Perform the next FSM step. + * tk_action may be NULL when the task has been killed + * by someone else. + */ if (task->tk_action == NULL) break; task->tk_action(task); From ba3ffdc68b4c80fe4cc42bdae6040eca1067ebb2 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Thu, 17 Mar 2011 18:54:23 +0300 Subject: [PATCH 0059/2556] RPC: killing RPC tasks races fixed commit 8e26de238fd794c8ea56a5c98bf67c40cfeb051d upstream. RPC task RPC_TASK_QUEUED bit is set must be checked before trying to wake up task rpc_killall_tasks() because task->tk_waitqueue can not be set (equal to NULL). Also, as Trond Myklebust mentioned, such approach (instead of checking tk_waitqueue to NULL) allows us to "optimise away the call to rpc_wake_up_queued_task() altogether for those tasks that aren't queued". Here is an example of dereferencing of tk_waitqueue equal to NULL: CPU 0 CPU 1 CPU 2 -------------------- --------------------- -------------------------- nfs4_run_open_task rpc_run_task rpc_execute rpc_set_active rpc_make_runnable (waiting) rpc_async_schedule nfs4_open_prepare nfs_wait_on_sequence nfs_umount_begin rpc_killall_tasks rpc_wake_up_task rpc_wake_up_queued_task spin_lock(tk_waitqueue == NULL) BUG() rpc_sleep_on spin_lock(&q->lock) __rpc_sleep_on task->tk_waitqueue = q Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/clnt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 57d344cf2256b..35d046b588f3f 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -436,7 +436,9 @@ void rpc_killall_tasks(struct rpc_clnt *clnt) if (!(rovr->tk_flags & RPC_TASK_KILLED)) { rovr->tk_flags |= RPC_TASK_KILLED; rpc_exit(rovr, -EIO); - rpc_wake_up_queued_task(rovr->tk_waitqueue, rovr); + if (RPC_IS_QUEUED(rovr)) + rpc_wake_up_queued_task(rovr->tk_waitqueue, + rovr); } } spin_unlock(&clnt->cl_lock); From 8ee8d7d4b511543c5368a23474d4cff231288ec3 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 7 Mar 2011 21:27:08 +0100 Subject: [PATCH 0060/2556] perf: Fix the software events state check commit 91b2f482e62ad0d444222253026a5cbca28c4ab9 upstream. Fix the mistakenly inverted check of events state. Signed-off-by: Frederic Weisbecker Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Stephane Eranian LKML-Reference: <1299529629-18280-1-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/perf_event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 656222fcf767e..853705f6d2943 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -4567,7 +4567,7 @@ static int perf_exclude_event(struct perf_event *event, struct pt_regs *regs) { if (event->hw.state & PERF_HES_STOPPED) - return 0; + return 1; if (regs) { if (event->attr.exclude_user && user_mode(regs)) From 0a5bdee8013fd9ac665185f47fc0b00e000c7d85 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 7 Mar 2011 21:27:09 +0100 Subject: [PATCH 0061/2556] perf: Handle stopped state with tracepoints commit a0f7d0f7fc02465bb9758501f611f63381792996 upstream. We toggle the state from start and stop callbacks but actually don't check it when the event triggers. Do it so that these callbacks actually work. Signed-off-by: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Stephane Eranian Signed-off-by: Peter Zijlstra LKML-Reference: <1299529629-18280-2-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/perf_event.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 853705f6d2943..b22a2ef75c86f 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -4923,6 +4923,8 @@ static int perf_tp_event_match(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) { + if (event->hw.state & PERF_HES_STOPPED) + return 0; /* * All tracepoints are from kernel-space. */ From c1ccb911a49d7bd7f5b8233c51b1f5a40ddaba47 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 9 Mar 2011 14:38:42 +1100 Subject: [PATCH 0062/2556] perf, powerpc: Handle events that raise an exception without overflowing commit 0837e3242c73566fc1c0196b4ec61779c25ffc93 upstream. Events on POWER7 can roll back if a speculative event doesn't eventually complete. Unfortunately in some rare cases they will raise a performance monitor exception. We need to catch this to ensure we reset the PMC. In all cases the PMC will be 256 or less cycles from overflow. Signed-off-by: Anton Blanchard Signed-off-by: Peter Zijlstra LKML-Reference: <20110309143842.6c22845e@kryten> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/include/asm/reg.h | 1 + arch/powerpc/kernel/perf_event.c | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 125fc1ad665d0..7626fa78e1f8b 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -880,6 +880,7 @@ #define PV_970 0x0039 #define PV_POWER5 0x003A #define PV_POWER5p 0x003B +#define PV_POWER7 0x003F #define PV_970FX 0x003C #define PV_630 0x0040 #define PV_630p 0x0041 diff --git a/arch/powerpc/kernel/perf_event.c b/arch/powerpc/kernel/perf_event.c index ab6f6beadb572..97e0ae414940e 100644 --- a/arch/powerpc/kernel/perf_event.c +++ b/arch/powerpc/kernel/perf_event.c @@ -1269,6 +1269,28 @@ unsigned long perf_instruction_pointer(struct pt_regs *regs) return ip; } +static bool pmc_overflow(unsigned long val) +{ + if ((int)val < 0) + return true; + + /* + * Events on POWER7 can roll back if a speculative event doesn't + * eventually complete. Unfortunately in some rare cases they will + * raise a performance monitor exception. We need to catch this to + * ensure we reset the PMC. In all cases the PMC will be 256 or less + * cycles from overflow. + * + * We only do this if the first pass fails to find any overflowing + * PMCs because a user might set a period of less than 256 and we + * don't want to mistakenly reset them. + */ + if (__is_processor(PV_POWER7) && ((0x80000000 - val) <= 256)) + return true; + + return false; +} + /* * Performance monitor interrupt stuff */ @@ -1316,7 +1338,7 @@ static void perf_event_interrupt(struct pt_regs *regs) if (is_limited_pmc(i + 1)) continue; val = read_pmc(i + 1); - if ((int)val < 0) + if (pmc_overflow(val)) write_pmc(i + 1, 0); } } From 33159d91241b8585c73fa52bf7044be0d7f80e13 Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Tue, 15 Mar 2011 19:16:40 -0700 Subject: [PATCH 0063/2556] perf tools: Version incorrect with some versions of grep commit 58d406ed6a5f1ca4bc1dba5390b718c67847fa5f upstream. Some versions of grep don't treat '\s' properly. When building perf on such systems and using a kernel tarball the perf version is unable to be determined from the main kernel Makefile and the user is left with a version of '..'. Replacing the use of '\s' with '[[:space:]]', which should work in all grep versions, gives a usable version number. Reported-by: Tapan Dhimant Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tapan Dhimant Cc: linux-kernel@vger.kernel.org LKML-Reference: <1300241800-30281-1-git-send-email-johunt@akamai.com> Signed-off-by: Josh Hunt Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/PERF-VERSION-GEN | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index 97d76562a1a09..26d4d3fd6deb2 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN @@ -23,10 +23,10 @@ if test -d ../../.git -o -f ../../.git && then VN=$(echo "$VN" | sed -e 's/-/./g'); else - eval `grep '^VERSION\s*=' ../../Makefile|tr -d ' '` - eval `grep '^PATCHLEVEL\s*=' ../../Makefile|tr -d ' '` - eval `grep '^SUBLEVEL\s*=' ../../Makefile|tr -d ' '` - eval `grep '^EXTRAVERSION\s*=' ../../Makefile|tr -d ' '` + eval $(grep '^VERSION[[:space:]]*=' ../../Makefile|tr -d ' ') + eval $(grep '^PATCHLEVEL[[:space:]]*=' ../../Makefile|tr -d ' ') + eval $(grep '^SUBLEVEL[[:space:]]*=' ../../Makefile|tr -d ' ') + eval $(grep '^EXTRAVERSION[[:space:]]*=' ../../Makefile|tr -d ' ') VN="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}${EXTRAVERSION}" fi From 7f59045ccf26f8ead7947a29eb1cc91a1776cbf1 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Fri, 4 Mar 2011 16:04:08 -0600 Subject: [PATCH 0064/2556] ext3: Always set dx_node's fake_dirent explicitly. commit d7433142b63d727b5a217c37b1a1468b116a9771 upstream. (crossport of 1f7bebb9e911d870fa8f997ddff838e82b5715ea by Andreas Schlick ) When ext3_dx_add_entry() has to split an index node, it has to ensure that name_len of dx_node's fake_dirent is also zero, because otherwise e2fsck won't recognise it as an intermediate htree node and consider the htree to be corrupted. Signed-off-by: Eric Sandeen Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/ext3/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index b27ba71810ecd..75c968eaf90d2 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -1540,8 +1540,8 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry, goto cleanup; node2 = (struct dx_node *)(bh2->b_data); entries2 = node2->entries; + memset(&node2->fake, 0, sizeof(struct fake_dirent)); node2->fake.rec_len = ext3_rec_len_to_disk(sb->s_blocksize); - node2->fake.inode = 0; BUFFER_TRACE(frame->bh, "get_write_access"); err = ext3_journal_get_write_access(handle, frame->bh); if (err) From cb8385e61fb736ef6748d305d868b28a9f649ef1 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Tue, 15 Mar 2011 13:27:16 -0600 Subject: [PATCH 0065/2556] call_function_many: fix list delete vs add race commit e6cd1e07a185d5f9b0aa75e020df02d3c1c44940 upstream. Peter pointed out there was nothing preventing the list_del_rcu in smp_call_function_interrupt from running before the list_add_rcu in smp_call_function_many. Fix this by not setting refs until we have gotten the lock for the list. Take advantage of the wmb in list_add_rcu to save an explicit additional one. I tried to force this race with a udelay before the lock & list_add and by mixing all 64 online cpus with just 3 random cpus in the mask, but was unsuccessful. Still, inspection shows a valid race, and the fix is a extension of the existing protection window in the current code. Reported-by: Peter Zijlstra Signed-off-by: Milton Miller Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/smp.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/kernel/smp.c b/kernel/smp.c index 9910744f0856c..aaeee20c56344 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -491,14 +491,15 @@ void smp_call_function_many(const struct cpumask *mask, cpumask_clear_cpu(this_cpu, data->cpumask); /* - * To ensure the interrupt handler gets an complete view - * we order the cpumask and refs writes and order the read - * of them in the interrupt handler. In addition we may - * only clear our own cpu bit from the mask. + * We reuse the call function data without waiting for any grace + * period after some other cpu removes it from the global queue. + * This means a cpu might find our data block as it is writen. + * The interrupt handler waits until it sees refs filled out + * while its cpu mask bit is set; here we may only clear our + * own cpu mask bit, and must wait to set refs until we are sure + * previous writes are complete and we have obtained the lock to + * add the element to the queue. */ - smp_wmb(); - - atomic_set(&data->refs, cpumask_weight(data->cpumask)); raw_spin_lock_irqsave(&call_function.lock, flags); /* @@ -507,6 +508,11 @@ void smp_call_function_many(const struct cpumask *mask, * will not miss any other list entries: */ list_add_rcu(&data->csd.list, &call_function.queue); + /* + * We rely on the wmb() in list_add_rcu to order the writes + * to func, data, and cpumask before this write to refs. + */ + atomic_set(&data->refs, cpumask_weight(data->cpumask)); raw_spin_unlock_irqrestore(&call_function.lock, flags); /* From 4718af584c9db2b2002a89b856462cedaaf3d7da Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Tue, 15 Mar 2011 13:27:16 -0600 Subject: [PATCH 0066/2556] call_function_many: add missing ordering commit 45a5791920ae643eafc02e2eedef1a58e341b736 upstream. Paul McKenney's review pointed out two problems with the barriers in the 2.6.38 update to the smp call function many code. First, a barrier that would force the func and info members of data to be visible before their consumption in the interrupt handler was missing. This can be solved by adding a smp_wmb between setting the func and info members and setting setting the cpumask; this will pair with the existing and required smp_rmb ordering the cpumask read before the read of refs. This placement avoids the need a second smp_rmb in the interrupt handler which would be executed on each of the N cpus executing the call request. (I was thinking this barrier was present but was not). Second, the previous write to refs (establishing the zero that we the interrupt handler was testing from all cpus) was performed by a third party cpu. This would invoke transitivity which, as a recient or concurrent addition to memory-barriers.txt now explicitly states, would require a full smp_mb(). However, we know the cpumask will only be set by one cpu (the data owner) and any preivous iteration of the mask would have cleared by the reading cpu. By redundantly writing refs to 0 on the owning cpu before the smp_wmb, the write to refs will follow the same path as the writes that set the cpumask, which in turn allows us to keep the barrier in the interrupt handler a smp_rmb instead of promoting it to a smp_mb (which will be be executed by N cpus for each of the possible M elements on the list). I moved and expanded the comment about our (ab)use of the rcu list primitives for the concurrent walk earlier into this function. I considered moving the first two paragraphs to the queue list head and lock, but felt it would have been too disconected from the code. Cc: Paul McKinney Signed-off-by: Milton Miller Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/smp.c | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/kernel/smp.c b/kernel/smp.c index aaeee20c56344..7c6ded5effd96 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -483,23 +483,42 @@ void smp_call_function_many(const struct cpumask *mask, data = &__get_cpu_var(cfd_data); csd_lock(&data->csd); - BUG_ON(atomic_read(&data->refs) || !cpumask_empty(data->cpumask)); - data->csd.func = func; - data->csd.info = info; - cpumask_and(data->cpumask, mask, cpu_online_mask); - cpumask_clear_cpu(this_cpu, data->cpumask); + /* This BUG_ON verifies our reuse assertions and can be removed */ + BUG_ON(atomic_read(&data->refs) || !cpumask_empty(data->cpumask)); /* + * The global call function queue list add and delete are protected + * by a lock, but the list is traversed without any lock, relying + * on the rcu list add and delete to allow safe concurrent traversal. * We reuse the call function data without waiting for any grace * period after some other cpu removes it from the global queue. - * This means a cpu might find our data block as it is writen. - * The interrupt handler waits until it sees refs filled out - * while its cpu mask bit is set; here we may only clear our - * own cpu mask bit, and must wait to set refs until we are sure - * previous writes are complete and we have obtained the lock to - * add the element to the queue. + * This means a cpu might find our data block as it is being + * filled out. + * + * We hold off the interrupt handler on the other cpu by + * ordering our writes to the cpu mask vs our setting of the + * refs counter. We assert only the cpu owning the data block + * will set a bit in cpumask, and each bit will only be cleared + * by the subject cpu. Each cpu must first find its bit is + * set and then check that refs is set indicating the element is + * ready to be processed, otherwise it must skip the entry. + * + * On the previous iteration refs was set to 0 by another cpu. + * To avoid the use of transitivity, set the counter to 0 here + * so the wmb will pair with the rmb in the interrupt handler. */ + atomic_set(&data->refs, 0); /* convert 3rd to 1st party write */ + + data->csd.func = func; + data->csd.info = info; + + /* Ensure 0 refs is visible before mask. Also orders func and info */ + smp_wmb(); + + /* We rely on the "and" being processed before the store */ + cpumask_and(data->cpumask, mask, cpu_online_mask); + cpumask_clear_cpu(this_cpu, data->cpumask); raw_spin_lock_irqsave(&call_function.lock, flags); /* @@ -509,8 +528,9 @@ void smp_call_function_many(const struct cpumask *mask, */ list_add_rcu(&data->csd.list, &call_function.queue); /* - * We rely on the wmb() in list_add_rcu to order the writes - * to func, data, and cpumask before this write to refs. + * We rely on the wmb() in list_add_rcu to complete our writes + * to the cpumask before this write to refs, which indicates + * data is on the list and is ready to be processed. */ atomic_set(&data->refs, cpumask_weight(data->cpumask)); raw_spin_unlock_irqrestore(&call_function.lock, flags); From d1342fed517f89ad4c3d956d9e214d72c06bb7c1 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Tue, 15 Mar 2011 13:27:17 -0600 Subject: [PATCH 0067/2556] smp_call_function_many: handle concurrent clearing of mask commit 723aae25d5cdb09962901d36d526b44d4be1051c upstream. Mike Galbraith reported finding a lockup ("perma-spin bug") where the cpumask passed to smp_call_function_many was cleared by other cpu(s) while a cpu was preparing its call_data block, resulting in no cpu to clear the last ref and unlock the block. Having cpus clear their bit asynchronously could be useful on a mask of cpus that might have a translation context, or cpus that need a push to complete an rcu window. Instead of adding a BUG_ON and requiring yet another cpumask copy, just detect the race and handle it. Note: arch_send_call_function_ipi_mask must still handle an empty cpumask because the data block is globally visible before the that arch callback is made. And (obviously) there are no guarantees to which cpus are notified if the mask is changed during the call; only cpus that were online and had their mask bit set during the whole call are guaranteed to be called. Reported-by: Mike Galbraith Reported-by: Jan Beulich Acked-by: Jan Beulich Signed-off-by: Milton Miller Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/smp.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/kernel/smp.c b/kernel/smp.c index 7c6ded5effd96..954548906afbe 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -450,7 +450,7 @@ void smp_call_function_many(const struct cpumask *mask, { struct call_function_data *data; unsigned long flags; - int cpu, next_cpu, this_cpu = smp_processor_id(); + int refs, cpu, next_cpu, this_cpu = smp_processor_id(); /* * Can deadlock when called with interrupts disabled. @@ -461,7 +461,7 @@ void smp_call_function_many(const struct cpumask *mask, WARN_ON_ONCE(cpu_online(this_cpu) && irqs_disabled() && !oops_in_progress && !early_boot_irqs_disabled); - /* So, what's a CPU they want? Ignoring this one. */ + /* Try to fastpath. So, what's a CPU they want? Ignoring this one. */ cpu = cpumask_first_and(mask, cpu_online_mask); if (cpu == this_cpu) cpu = cpumask_next_and(cpu, mask, cpu_online_mask); @@ -519,6 +519,13 @@ void smp_call_function_many(const struct cpumask *mask, /* We rely on the "and" being processed before the store */ cpumask_and(data->cpumask, mask, cpu_online_mask); cpumask_clear_cpu(this_cpu, data->cpumask); + refs = cpumask_weight(data->cpumask); + + /* Some callers race with other cpus changing the passed mask */ + if (unlikely(!refs)) { + csd_unlock(&data->csd); + return; + } raw_spin_lock_irqsave(&call_function.lock, flags); /* @@ -532,7 +539,7 @@ void smp_call_function_many(const struct cpumask *mask, * to the cpumask before this write to refs, which indicates * data is on the list and is ready to be processed. */ - atomic_set(&data->refs, cpumask_weight(data->cpumask)); + atomic_set(&data->refs, refs); raw_spin_unlock_irqrestore(&call_function.lock, flags); /* From bf59a586cad3de1d4419312826d4b759a4da0921 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Wed, 16 Mar 2011 11:37:29 +0800 Subject: [PATCH 0068/2556] x86: Flush TLB if PGD entry is changed in i386 PAE mode commit 4981d01eada5354d81c8929d5b2836829ba3df7b upstream. According to intel CPU manual, every time PGD entry is changed in i386 PAE mode, we need do a full TLB flush. Current code follows this and there is comment for this too in the code. But current code misses the multi-threaded case. A changed page table might be used by several CPUs, every such CPU should flush TLB. Usually this isn't a problem, because we prepopulate all PGD entries at process fork. But when the process does munmap and follows new mmap, this issue will be triggered. When it happens, some CPUs keep doing page faults: http://marc.info/?l=linux-kernel&m=129915020508238&w=2 Reported-by: Yasunori Goto Tested-by: Yasunori Goto Reviewed-by: Rik van Riel Signed-off-by: Shaohua Li Cc: Mallick Asit K Cc: Linus Torvalds Cc: Andrew Morton Cc: linux-mm LKML-Reference: <1300246649.2337.95.camel@sli10-conroe> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/pgtable-3level.h | 11 +++-------- arch/x86/mm/pgtable.c | 3 +-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/arch/x86/include/asm/pgtable-3level.h b/arch/x86/include/asm/pgtable-3level.h index 94b979d1b58db..effff47a3c828 100644 --- a/arch/x86/include/asm/pgtable-3level.h +++ b/arch/x86/include/asm/pgtable-3level.h @@ -69,8 +69,6 @@ static inline void native_pmd_clear(pmd_t *pmd) static inline void pud_clear(pud_t *pudp) { - unsigned long pgd; - set_pud(pudp, __pud(0)); /* @@ -79,13 +77,10 @@ static inline void pud_clear(pud_t *pudp) * section 8.1: in PAE mode we explicitly have to flush the * TLB via cr3 if the top-level pgd is changed... * - * Make sure the pud entry we're updating is within the - * current pgd to avoid unnecessary TLB flushes. + * Currently all places where pud_clear() is called either have + * flush_tlb_mm() followed or don't need TLB flush (x86_64 code or + * pud_clear_bad()), so we don't need TLB flush here. */ - pgd = read_cr3(); - if (__pa(pudp) >= pgd && __pa(pudp) < - (pgd + sizeof(pgd_t)*PTRS_PER_PGD)) - write_cr3(pgd); } #ifdef CONFIG_SMP diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c index 0113d19c8aa60..8573b83a63d03 100644 --- a/arch/x86/mm/pgtable.c +++ b/arch/x86/mm/pgtable.c @@ -168,8 +168,7 @@ void pud_populate(struct mm_struct *mm, pud_t *pudp, pmd_t *pmd) * section 8.1: in PAE mode we explicitly have to flush the * TLB via cr3 if the top-level pgd is changed... */ - if (mm == current->active_mm) - write_cr3(read_cr3()); + flush_tlb_mm(mm); } #else /* !CONFIG_X86_PAE */ From 552c26d16dbb5ec82755492fd5eab42d6d07990d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 11 Mar 2011 00:27:34 -0800 Subject: [PATCH 0069/2556] HID: ACRUX - activate the device immediately after binding commit 0ae43810976bc969ee158510c4acbe70ed136e61 upstream. This device does not tolerate delayed opening and goes into a coma if we try to that. Ubuntu even has a crutch for udev that opened the device upon seeing it for the first time, but it did not work if we happened to boot with the device attached, since by the time userspace got around opening the device it was too late. Let's start the device immediately to deal with this issue. Reported-by: Sergei Kolzun Signed-off-by: Dmitry Torokhov Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/Kconfig | 10 ++++++++-- drivers/hid/Makefile | 2 +- drivers/hid/hid-axff.c | 31 ++++++++++++++++++++++++++++--- drivers/hid/hid-core.c | 2 -- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 3a07b4da4eca2..ae445b1dffaa2 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -68,9 +68,15 @@ config HID_A4TECH ---help--- Support for A4 tech X5 and WOP-35 / Trust 450L mice. -config HID_ACRUX_FF - tristate "ACRUX force feedback" +config HID_ACRUX + tristate "ACRUX game controller support" depends on USB_HID + ---help--- + Say Y here if you want to enable support for ACRUX game controllers. + +config HID_ACRUX_FF + tristate "ACRUX force feedback support" + depends on HID_ACRUX select INPUT_FF_MEMLESS ---help--- Say Y here if you want to enable force feedback support for ACRUX diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 6efc2a0370ad1..13e624867d193 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -27,7 +27,7 @@ endif obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o -obj-$(CONFIG_HID_ACRUX_FF) += hid-axff.o +obj-$(CONFIG_HID_ACRUX) += hid-axff.o obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_BELKIN) += hid-belkin.o obj-$(CONFIG_HID_CANDO) += hid-cando.o diff --git a/drivers/hid/hid-axff.c b/drivers/hid/hid-axff.c index e5b961d6ff220..b4554288de00b 100644 --- a/drivers/hid/hid-axff.c +++ b/drivers/hid/hid-axff.c @@ -33,6 +33,8 @@ #include #include "hid-ids.h" + +#ifdef CONFIG_HID_ACRUX_FF #include "usbhid/usbhid.h" struct axff_device { @@ -109,6 +111,12 @@ static int axff_init(struct hid_device *hid) kfree(axff); return error; } +#else +static inline int axff_init(struct hid_device *hid) +{ + return 0; +} +#endif static int ax_probe(struct hid_device *hdev, const struct hid_device_id *id) { @@ -139,9 +147,25 @@ static int ax_probe(struct hid_device *hdev, const struct hid_device_id *id) error); } + /* + * We need to start polling device right away, otherwise + * it will go into a coma. + */ + error = hid_hw_open(hdev); + if (error) { + dev_err(&hdev->dev, "hw open failed\n"); + return error; + } + return 0; } +static void ax_remove(struct hid_device *hdev) +{ + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + static const struct hid_device_id ax_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802), }, { } @@ -149,9 +173,10 @@ static const struct hid_device_id ax_devices[] = { MODULE_DEVICE_TABLE(hid, ax_devices); static struct hid_driver ax_driver = { - .name = "acrux", - .id_table = ax_devices, - .probe = ax_probe, + .name = "acrux", + .id_table = ax_devices, + .probe = ax_probe, + .remove = ax_remove, }; static int __init ax_init(void) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 295e2487102bf..ee27b108c4c78 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1256,9 +1256,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) }, { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) }, { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) }, -#if defined(CONFIG_HID_ACRUX_FF) || defined(CONFIG_HID_ACRUX_FF_MODULE) { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) }, -#endif { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, From 666ede5c887f316d2796366a450ddbe97cabe9e5 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Thu, 24 Feb 2011 19:30:59 +0100 Subject: [PATCH 0070/2556] HID: Do not create input devices for feature reports commit f635bd11c8d332d917fb9a4cad3071b2357d5b2a upstream. When the multi input quirk is set, there is a new input device created for every feature report. Since the idea is to present features per hid device, not per input device, revert back to the original report loop and change the feature_mapping() callback to not take the input device as argument. Signed-off-by: Henrik Rydberg Tested-by: Benjamin Tissoires Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-input.c | 30 +++++++++++++++++++++--------- drivers/hid/hid-multitouch.c | 2 +- include/linux/hid.h | 2 +- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 7f552bfad32c0..ebcc02a1c1c94 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -290,14 +290,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel goto ignore; } - if (field->report_type == HID_FEATURE_REPORT) { - if (device->driver->feature_mapping) { - device->driver->feature_mapping(device, hidinput, field, - usage); - } - goto ignore; - } - if (device->driver->input_mapping) { int ret = device->driver->input_mapping(device, hidinput, field, usage, &bit, &max); @@ -835,6 +827,24 @@ static void hidinput_close(struct input_dev *dev) hid_hw_close(hid); } +static void report_features(struct hid_device *hid) +{ + struct hid_driver *drv = hid->driver; + struct hid_report_enum *rep_enum; + struct hid_report *rep; + int i, j; + + if (!drv->feature_mapping) + return; + + rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) + for (i = 0; i < rep->maxfield; i++) + for (j = 0; j < rep->field[i]->maxusage; j++) + drv->feature_mapping(hid, rep->field[i], + rep->field[i]->usage + j); +} + /* * Register the input device; print a message. * Configure the input layer interface @@ -863,7 +873,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) return -1; } - for (k = HID_INPUT_REPORT; k <= HID_FEATURE_REPORT; k++) { + report_features(hid); + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { if (k == HID_OUTPUT_REPORT && hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) continue; diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 07d3183fdde50..2bbc9545f5ccd 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -122,7 +122,7 @@ struct mt_class mt_classes[] = { { } }; -static void mt_feature_mapping(struct hid_device *hdev, struct hid_input *hi, +static void mt_feature_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { if (usage->hid == HID_DG_INPUTMODE) { diff --git a/include/linux/hid.h b/include/linux/hid.h index d91c25e253c87..fc5faf60f6df4 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -638,7 +638,7 @@ struct hid_driver { struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max); void (*feature_mapping)(struct hid_device *hdev, - struct hid_input *hidinput, struct hid_field *field, + struct hid_field *field, struct hid_usage *usage); #ifdef CONFIG_PM int (*suspend)(struct hid_device *hdev, pm_message_t message); From 719fbc55236af185b65f0a125cb633e124089ce4 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 7 Feb 2011 19:28:01 +0100 Subject: [PATCH 0071/2556] fix per-cpu flag problem in the cpu affinity checkers commit 9804c9eaeacfe78651052c5ddff31099f60ef78c upstream. The CHECK_IRQ_PER_CPU is wrong, it should be checking irq_to_desc(irq)->status not just irq. Signed-off-by: Thomas Gleixner Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- arch/parisc/kernel/irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/parisc/kernel/irq.c b/arch/parisc/kernel/irq.c index d7d94b845dc2c..3948f1dd455aa 100644 --- a/arch/parisc/kernel/irq.c +++ b/arch/parisc/kernel/irq.c @@ -108,7 +108,7 @@ int cpu_check_affinity(unsigned int irq, const struct cpumask *dest) int cpu_dest; /* timer and ipi have to always be received on all CPUs */ - if (CHECK_IRQ_PER_CPU(irq)) { + if (CHECK_IRQ_PER_CPU(irq_to_desc(irq)->status)) { /* Bad linux design decision. The mask has already * been set; we must reset it */ cpumask_setall(irq_desc[irq].affinity); From 0616af64f42d534de2440d35cb25cd21a820a473 Mon Sep 17 00:00:00 2001 From: Andy Botting Date: Sat, 12 Mar 2011 20:27:22 -0800 Subject: [PATCH 0072/2556] Input: bcm5974 - add support for MacBookPro8 commit 47340bd9fefb571888836da942b5aee0e85e959c upstream. This patch add multitouch support for the MacBookPro8,1 and MacBookPro8,2 models. Signed-off-by: Andy Botting Signed-off-by: Henrik Rydberg Acked-by: Jiri Kosina Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-apple.c | 6 ++++++ drivers/hid/hid-core.c | 6 ++++++ drivers/hid/hid-ids.h | 3 +++ drivers/input/mouse/bcm5974.c | 20 ++++++++++++++++++++ 4 files changed, 35 insertions(+) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 61aa712333927..b85744fe84647 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -481,6 +481,12 @@ static const struct hid_device_id apple_devices[] = { .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI), + .driver_data = APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ISO), + .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_JIS), + .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ee27b108c4c78..9477b2a46f494 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1300,6 +1300,9 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_JIS) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) }, @@ -1800,6 +1803,9 @@ static const struct hid_device_id hid_mouse_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, { } diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 5f5cc9100aff9..090bf488196e1 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -103,6 +103,9 @@ #define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI 0x0242 #define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO 0x0243 #define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS 0x0244 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI 0x0245 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO 0x0246 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS 0x0247 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI 0x0239 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO 0x023a #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index ee82851afe3ee..318531424848f 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -63,6 +63,10 @@ #define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI 0x0242 #define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO 0x0243 #define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS 0x0244 +/* Macbook8 (unibody, March 2011) */ +#define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI 0x0245 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO 0x0246 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS 0x0247 #define BCM5974_DEVICE(prod) { \ .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \ @@ -96,6 +100,10 @@ static const struct usb_device_id bcm5974_table[] = { BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI), BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO), BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS), + /* MacbookPro8 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_JIS), /* Terminating entry */ {} }; @@ -274,6 +282,18 @@ static const struct bcm5974_config bcm5974_config_table[] = { { DIM_X, DIM_X / SN_COORD, -4616, 5112 }, { DIM_Y, DIM_Y / SN_COORD, -142, 5234 } }, + { + USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING5_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING5_JIS, + HAS_INTEGRATED_BUTTON, + 0x84, sizeof(struct bt_data), + 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, + { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, + { DIM_X, DIM_X / SN_COORD, -4415, 5050 }, + { DIM_Y, DIM_Y / SN_COORD, -55, 6680 } + }, {} }; From 97d2a699c3059f99d997625b1685e4cf935e5579 Mon Sep 17 00:00:00 2001 From: Roman Fietze Date: Sun, 20 Mar 2011 14:50:52 +0100 Subject: [PATCH 0073/2556] i2c: Fix typo in instantiating-devices document commit 6ced9e6b3901af4ab6ac0a11231402c888286ea6 upstream. The struct i2c_board_info member holding the name is "type", not "name". Signed-off-by: Roman Fietze Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- Documentation/i2c/instantiating-devices | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/i2c/instantiating-devices b/Documentation/i2c/instantiating-devices index 87da405a85979..9edb75d8c9b94 100644 --- a/Documentation/i2c/instantiating-devices +++ b/Documentation/i2c/instantiating-devices @@ -100,7 +100,7 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev) (...) i2c_adap = i2c_get_adapter(2); memset(&i2c_info, 0, sizeof(struct i2c_board_info)); - strlcpy(i2c_info.name, "isp1301_pnx", I2C_NAME_SIZE); + strlcpy(i2c_info.type, "isp1301_pnx", I2C_NAME_SIZE); isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info, normal_i2c, NULL); i2c_put_adapter(i2c_adap); From c552063d351bfd322fc0610e1724e7c901790d5e Mon Sep 17 00:00:00 2001 From: Manoj Iyer Date: Fri, 11 Feb 2011 16:25:31 -0600 Subject: [PATCH 0074/2556] mmc: sdhci: Add Ricoh e823 PCI ID commit 5fd11c0754fa069b6aba64b65734aa2fb193552d upstream. Signed-off-by: Manoj Iyer Signed-off-by: Chris Ball Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-pci.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 0dc905b20eee1..f7e622c539baa 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -546,6 +546,14 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc, }, + { + .vendor = PCI_VENDOR_ID_RICOH, + .device = 0xe823, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc, + }, + { .vendor = PCI_VENDOR_ID_ENE, .device = PCI_DEVICE_ID_ENE_CB712_SD, From 17ae6803cbb5224b475ffeb8eaab18c949442b17 Mon Sep 17 00:00:00 2001 From: Stefan Nilsson XK Date: Tue, 1 Mar 2011 14:41:04 +0100 Subject: [PATCH 0075/2556] mmc: sdio: remember new card RCA when redetecting card commit 0aab3995485b8a994bf29a995a008c9ea4a28054 upstream. During redetection of a SDIO card, a request for a new card RCA was submitted to the card, but was then overwritten by the old RCA. This caused the card to be deselected instead of selected when using the incorrect RCA. This bug's been present since the "oldcard" handling was introduced in 2.6.32. Signed-off-by: Stefan Nilsson XK Reviewed-by: Ulf Hansson Reviewed-by: Pawel Wieczorkiewicz Signed-off-by: Linus Walleij Signed-off-by: Chris Ball Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/core/sdio.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index ebc62ad4cc567..fbe1ea4750267 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -395,6 +395,14 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, if (err) goto remove; + /* + * Update oldcard with the new RCA received from the SDIO + * device -- we're doing this so that it's updated in the + * "card" struct when oldcard overwrites that later. + */ + if (oldcard) + oldcard->rca = card->rca; + mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); } From 4a68b18f8a4da0b70b49972f205ab2cd4b886a96 Mon Sep 17 00:00:00 2001 From: Alexander van Heukelum Date: Fri, 11 Mar 2011 21:59:38 +0100 Subject: [PATCH 0076/2556] x86, binutils, xen: Fix another wrong size directive commit 371c394af27ab7d1e58a66bc19d9f1f3ac1f67b4 upstream. The latest binutils (2.21.0.20110302/Ubuntu) breaks the build yet another time, under CONFIG_XEN=y due to a .size directive that refers to a slightly differently named (hence, to the now very strict and unforgiving assembler, non-existent) symbol. [ mingo: This unnecessary build breakage caused by new binutils version 2.21 gets escallated back several kernel releases spanning several years of Linux history, affecting over 130,000 upstream kernel commits (!), on CONFIG_XEN=y 64-bit kernels (i.e. essentially affecting all major Linux distro kernel configs). Git annotate tells us that this slight debug symbol code mismatch bug has been introduced in 2008 in commit 3d75e1b8: 3d75e1b8 (Jeremy Fitzhardinge 2008-07-08 15:06:49 -0700 1231) ENTRY(xen_do_hypervisor_callback) # do_hypervisor_callback(struct *pt_regs) The 'bug' is just a slight assymetry in ENTRY()/END() debug-symbols sequences, with lots of assembly code between the ENTRY() and the END(): ENTRY(xen_do_hypervisor_callback) # do_hypervisor_callback(struct *pt_regs) ... END(do_hypervisor_callback) Human reviewers almost never catch such small mismatches, and binutils never even warned about it either. This new binutils version thus breaks the Xen build on all upstream kernels since v2.6.27, out of the blue. This makes a straightforward Git bisection of all 64-bit Xen-enabled kernels impossible on such binutils, for a bisection window of over hundred thousand historic commits. (!) This is a major fail on the side of binutils and binutils needs to turn this show-stopper build failure into a warning ASAP. ] Signed-off-by: Alexander van Heukelum Cc: Jeremy Fitzhardinge Cc: Jan Beulich Cc: H.J. Lu Cc: Linus Torvalds Cc: Andrew Morton Cc: "H. Peter Anvin" Cc: Kees Cook LKML-Reference: <1299877178-26063-1-git-send-email-heukelum@fastmail.fm> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/entry_64.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index aed1ffbeb0c9b..bbd5c80cb09b0 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -1248,7 +1248,7 @@ ENTRY(xen_do_hypervisor_callback) # do_hypervisor_callback(struct *pt_regs) decl PER_CPU_VAR(irq_count) jmp error_exit CFI_ENDPROC -END(do_hypervisor_callback) +END(xen_do_hypervisor_callback) /* * Hypervisor uses this for application faults while it executes. From de988be1f66ecc6ea96c11dac2e4c5bc44de9615 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sun, 13 Mar 2011 20:06:59 +0000 Subject: [PATCH 0077/2556] davinci: DM644x EVM: register MUSB device earlier commit 60d97a840175d3becb2e6de36537a5cdfc0ec3a9 upstream. The MUSB driver doesn't see its platform device on DM644x EVM board anymore since commit 73b089b052a69020b953312a624a6e1eb5b81fab (usb: musb: split davinci to its own platform_driver) because the new probe is called as subsys_initcall() now, and the device is registered later than that by the board code. Move the registration to davinci_evm_init() -- it's safe to do so because the MUSB core device still gets initialized as fs_initcall() -- which is late enough for the I2C GPIO expander (which controls VBUS) to be initialized. Signed-off-by: Sergei Shtylyov Acked-by: Felipe Balbi Tested-by: Sekhar Nori Signed-off-by: Kevin Hilman Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-davinci/board-dm644x-evm.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 0ca90b834586d..556bbd468db3d 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -440,11 +440,6 @@ evm_u35_setup(struct i2c_client *client, int gpio, unsigned ngpio, void *c) gpio_request(gpio + 7, "nCF_SEL"); gpio_direction_output(gpio + 7, 1); - /* irlml6401 switches over 1A, in under 8 msec; - * now it can be managed by nDRV_VBUS ... - */ - davinci_setup_usb(1000, 8); - return 0; } @@ -705,6 +700,9 @@ static __init void davinci_evm_init(void) davinci_serial_init(&uart_config); dm644x_init_asp(&dm644x_evm_snd_data); + /* irlml6401 switches over 1A, in under 8 msec */ + davinci_setup_usb(1000, 8); + soc_info->emac_pdata->phy_id = DM644X_EVM_PHY_ID; /* Register the fixup for PHY on DaVinci */ phy_register_fixup_for_uid(LXT971_PHY_ID, LXT971_PHY_MASK, From 61493eba2918294ee62855d2943aa563aa301c0d Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Mar 2011 17:59:35 +0100 Subject: [PATCH 0078/2556] hwmon: (sht15) Fix integer overflow in humidity calculation commit ccd32e735de7a941906e093f8dca924bb05c5794 upstream. An integer overflow occurs in the calculation of RHlinear when the relative humidity is greater than around 30%. The consequence is a subtle (but noticeable) error in the resulting humidity measurement. Signed-off-by: Vivien Didelot Signed-off-by: Jean Delvare Cc: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/sht15.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index a610e7880fb3e..38a41d27f3da1 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -333,11 +333,11 @@ static inline int sht15_calc_humid(struct sht15_data *data) const int c1 = -4; const int c2 = 40500; /* x 10 ^ -6 */ - const int c3 = -2800; /* x10 ^ -9 */ + const int c3 = -28; /* x 10 ^ -7 */ RHlinear = c1*1000 + c2 * data->val_humid/1000 - + (data->val_humid * data->val_humid * c3)/1000000; + + (data->val_humid * data->val_humid * c3) / 10000; return (temp - 25000) * (10000 + 80 * data->val_humid) / 1000000 + RHlinear; } From 24d6a9d07f7da6c635530688a43086ea293bf9dc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 23 Mar 2011 13:04:47 -0700 Subject: [PATCH 0079/2556] Linux 2.6.38.1 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d6592b63c8cb6..167ef453a8cd5 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 38 -EXTRAVERSION = +EXTRAVERSION = .1 NAME = Flesh-Eating Bats with Fangs # *DOCUMENTATION* From 448b2ec2975a3eb8b5774e9a1b019afa3735d78a Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 23 Mar 2011 08:35:07 +0100 Subject: [PATCH 0080/2556] ALSA: HDA: Realtek: Avoid unnecessary volume control index on Surround/Side commit 5a8826463c19b0d1a2fc60b2adac0ec318047844 upstream. Similar to commit 7e59e097c09b82760bb0fe08b0fa2b704d76c3f4, this patch avoids unnecessary volume control indices for more Realtek auto-parsers, e g the ALC66x family, on the "Surround" and "Side" controls. These indices cause these volume controls to be ignored by PulseAudio and vmaster and should be removed whenever possible. Reported-by: Jan Losinski Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index acd209958ea24..c2eb6a7c2b3c8 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -16085,9 +16085,12 @@ static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec, return err; } else { const char *name = pfx; - if (!name) + int index = i; + if (!name) { name = chname[i]; - err = __alc861_create_out_sw(codec, name, nid, i, 3); + index = 0; + } + err = __alc861_create_out_sw(codec, name, nid, index, 3); if (err < 0) return err; } @@ -17238,16 +17241,19 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, return err; } else { const char *name = pfx; - if (!name) + int index = i; + if (!name) { name = chname[i]; + index = 0; + } err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - name, i, + name, index, HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT)); if (err < 0) return err; err = __add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - name, i, + name, index, HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT)); if (err < 0) @@ -19296,12 +19302,15 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, return err; } else { const char *name = pfx; - if (!name) + int index = i; + if (!name) { name = chname[i]; - err = __alc662_add_vol_ctl(spec, name, nid, i, 3); + index = 0; + } + err = __alc662_add_vol_ctl(spec, name, nid, index, 3); if (err < 0) return err; - err = __alc662_add_sw_ctl(spec, name, mix, i, 3); + err = __alc662_add_sw_ctl(spec, name, mix, index, 3); if (err < 0) return err; } From 4b6dcbe3594e810f051c33a803ec527b5c9a4da5 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Tue, 22 Mar 2011 16:21:38 +0800 Subject: [PATCH 0081/2556] ALSA: hda - VIA: Fix stereo mixer recording no sound issue commit bff5fbf50bd498c217994bd2d41a53ac3141185a upstream. Modify function via_mux_enum_put() to fix stereo mixer recording no sound issue. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_via.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 63b0054200a87..5bc9bd983e865 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1101,6 +1101,7 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + int ret; if (!spec->mux_nids[adc_idx]) return -EINVAL; @@ -1109,12 +1110,14 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol, AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - /* update jack power state */ - set_jack_power_state(codec); - return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, + ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]); + /* update jack power state */ + set_jack_power_state(codec); + + return ret; } static int via_independent_hp_info(struct snd_kcontrol *kcontrol, From 889301944d15ad5f98654ff40595a35ef15dab77 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Tue, 22 Mar 2011 16:22:37 +0800 Subject: [PATCH 0082/2556] ALSA: hda - VIA: Fix independent headphone no sound issue commit ce0e5a9e81fbb153ee15ca60246c6722f07fc546 upstream. Modify via_independent_hp_put() function to support VT1718S and VT1812 codecs, and fix independent headphone no sound issue. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_via.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 5bc9bd983e865..2f605e593957f 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1191,8 +1191,16 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, /* Get Independent Mode index of headphone pin widget */ spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel ? 1 : 0; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); + if (spec->codec_type == VT1718S) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0); + else + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, pinsel); + if (spec->codec_type == VT1812) + snd_hda_codec_write(codec, 0x35, 0, + AC_VERB_SET_CONNECT_SEL, pinsel); if (spec->multiout.hp_nid && spec->multiout.hp_nid != spec->multiout.dac_nids[HDA_FRONT]) snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, @@ -1211,6 +1219,8 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, activate_ctl(codec, "Headphone Playback Switch", spec->hp_independent_mode); } + /* update jack power state */ + set_jack_power_state(codec); return 0; } From a7c0c978bf3db15fcac86d6c1e026130201f5ce4 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Tue, 22 Mar 2011 16:23:23 +0800 Subject: [PATCH 0083/2556] ALSA: hda - VIA: Add missing support for VT1718S in A-A path commit ab657e0cacc39d88145871c6a3c844597c02d406 upstream. Modify mute_aa_path() function to support VT1718S codec. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_via.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 2f605e593957f..23e58f0904695 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1323,6 +1323,11 @@ static void mute_aa_path(struct hda_codec *codec, int mute) start_idx = 2; end_idx = 4; break; + case VT1718S: + nid_mixer = 0x21; + start_idx = 1; + end_idx = 3; + break; default: return; } From 9621c276448508db8e3f218872c2dcd6c053210b Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Tue, 22 Mar 2011 16:24:10 +0800 Subject: [PATCH 0084/2556] ALSA: hda - VIA: Fix invalid A-A path volume adjust issue commit 169222813eec8403c76394fb7b35ecab98e3c607 upstream. Modify vt_auto_create_analog_input_ctls() function to fix invalid a-a path volume adjust issue for VT1708S, VT1702 and VT1716S codecs. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_via.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 23e58f0904695..299a18b735a3e 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -2456,7 +2456,14 @@ static int vt_auto_create_analog_input_ctls(struct hda_codec *codec, else type_idx = 0; label = hda_get_autocfg_input_label(codec, cfg, i); - err = via_new_analog_input(spec, label, type_idx, idx, cap_nid); + if (spec->codec_type == VT1708S || + spec->codec_type == VT1702 || + spec->codec_type == VT1716S) + err = via_new_analog_input(spec, label, type_idx, + idx+1, cap_nid); + else + err = via_new_analog_input(spec, label, type_idx, + idx, cap_nid); if (err < 0) return err; snd_hda_add_imux_item(imux, label, idx, NULL); From ac66c28dc30d7778b80110552fd5cd82b7bd21d6 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Tue, 22 Mar 2011 16:25:03 +0800 Subject: [PATCH 0085/2556] ALSA: hda - VIA: Fix codec type for VT1708BCE at the right timing commit 0341ccd7557fecafe6a79c55158670cf629d269e upstream. Add get_codec_type() in via_new_spec() function to make sure getting correct codec type before building mixer controls. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_via.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 299a18b735a3e..269bb365752b4 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -159,6 +159,7 @@ struct via_spec { #endif }; +static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); static struct via_spec * via_new_spec(struct hda_codec *codec) { struct via_spec *spec; @@ -169,6 +170,10 @@ static struct via_spec * via_new_spec(struct hda_codec *codec) codec->spec = spec; spec->codec = codec; + spec->codec_type = get_codec_type(codec); + /* VT1708BCE & VT1708S are almost same */ + if (spec->codec_type == VT1708BCE) + spec->codec_type = VT1708S; return spec; } @@ -2203,10 +2208,6 @@ static int via_init(struct hda_codec *codec) for (i = 0; i < spec->num_iverbs; i++) snd_hda_sequence_write(codec, spec->init_verbs[i]); - spec->codec_type = get_codec_type(codec); - if (spec->codec_type == VT1708BCE) - spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost - same */ /* Lydia Add for EAPD enable */ if (!spec->dig_in_nid) { /* No Digital In connection */ if (spec->dig_in_pin) { From ab0190c746a2d9f830c9f25507d78b4e19c5f219 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Tue, 22 Mar 2011 16:26:36 +0800 Subject: [PATCH 0086/2556] ALSA: hda - VIA: Fix VT1708 can't build up Headphone control issue commit ee3c35c0827de02de414d08b2ddcbb910c2263ab upstream. Since VT1708 didn't support the control of getting connection number, building of headphone control will fail in via_hp_build() function. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_via.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 269bb365752b4..acc45798eeb43 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1266,9 +1266,12 @@ static int via_hp_build(struct hda_codec *codec) break; } - nums = snd_hda_get_connections(codec, nid, conn, HDA_MAX_CONNECTIONS); - if (nums <= 1) - return 0; + if (spec->codec_type != VT1708) { + nums = snd_hda_get_connections(codec, nid, + conn, HDA_MAX_CONNECTIONS); + if (nums <= 1) + return 0; + } knew = via_clone_control(spec, &via_hp_mixer[0]); if (knew == NULL) From e53d1c2854cf47801783569ed156076d6c2b2b3e Mon Sep 17 00:00:00 2001 From: Marek Belisko Date: Wed, 9 Mar 2011 21:46:20 +0100 Subject: [PATCH 0087/2556] ASoC: mini2440: Fix uda134x codec problem. commit a110f4ef810ee29d810876df725f41d66629733e upstream. ASoC audio for mini2440 platform in current kenrel doesn't work. First problem is samsung_asoc_dma device is missing in initialization. Next problem is with codec. Codec is initialized but never probed because no platform_device exist for codec driver. It leads to errors during codec binding to asoc dai. Next problem was platform data which was passed from board to asoc main driver but not passed to codec when called codec_soc_probe(). Following patch should fix issues. But not sure if in correct way. Please review. Signed-off-by: Marek Belisko Acked-by: Liam Girdwood Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-s3c2440/mach-mini2440.c | 7 +++++++ sound/soc/codecs/uda134x.c | 3 ++- sound/soc/samsung/s3c24xx_uda134x.c | 3 ++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-s3c2440/mach-mini2440.c b/arch/arm/mach-s3c2440/mach-mini2440.c index f62bb4c793bdd..7c3fb071ddd69 100644 --- a/arch/arm/mach-s3c2440/mach-mini2440.c +++ b/arch/arm/mach-s3c2440/mach-mini2440.c @@ -506,6 +506,11 @@ static struct i2c_board_info mini2440_i2c_devs[] __initdata = { }, }; +static struct platform_device uda1340_codec = { + .name = "uda134x-codec", + .id = -1, +}; + static struct platform_device *mini2440_devices[] __initdata = { &s3c_device_ohci, &s3c_device_wdt, @@ -521,7 +526,9 @@ static struct platform_device *mini2440_devices[] __initdata = { &s3c_device_nand, &s3c_device_sdi, &s3c_device_iis, + &uda1340_codec, &mini2440_audio, + &samsung_asoc_dma, }; static void __init mini2440_map_io(void) diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index e76847a9438b0..48ffd406a71d1 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -486,7 +486,8 @@ static struct snd_soc_dai_driver uda134x_dai = { static int uda134x_soc_probe(struct snd_soc_codec *codec) { struct uda134x_priv *uda134x; - struct uda134x_platform_data *pd = dev_get_drvdata(codec->card->dev); + struct uda134x_platform_data *pd = codec->card->dev->platform_data; + int ret; printk(KERN_INFO "UDA134X SoC Audio Codec\n"); diff --git a/sound/soc/samsung/s3c24xx_uda134x.c b/sound/soc/samsung/s3c24xx_uda134x.c index 2c09e93dd566f..86f1dc434e9f0 100644 --- a/sound/soc/samsung/s3c24xx_uda134x.c +++ b/sound/soc/samsung/s3c24xx_uda134x.c @@ -226,7 +226,7 @@ static struct snd_soc_ops s3c24xx_uda134x_ops = { static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = { .name = "UDA134X", .stream_name = "UDA134X", - .codec_name = "uda134x-hifi", + .codec_name = "uda134x-codec", .codec_dai_name = "uda134x-hifi", .cpu_dai_name = "s3c24xx-iis", .ops = &s3c24xx_uda134x_ops, @@ -321,6 +321,7 @@ static int s3c24xx_uda134x_probe(struct platform_device *pdev) platform_set_drvdata(s3c24xx_uda134x_snd_device, &snd_soc_s3c24xx_uda134x); + platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x)); ret = platform_device_add(s3c24xx_uda134x_snd_device); if (ret) { printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n"); From 3bc0f0aa75f0b70c022681060b699e3ba27d34d1 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 17 Mar 2011 07:34:32 +0000 Subject: [PATCH 0088/2556] ethtool: Compat handling for struct ethtool_rxnfc commit 3a7da39d165e0c363c294feec119db1427032afd upstream. This structure was accidentally defined such that its layout can differ between 32-bit and 64-bit processes. Add compat structure definitions and an ioctl wrapper function. Signed-off-by: Ben Hutchings Acked-by: Alexander Duyck Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- include/linux/ethtool.h | 34 ++++++++++++ net/socket.c | 114 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 141 insertions(+), 7 deletions(-) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 1908929204a9e..a3c1874171c1c 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -13,6 +13,9 @@ #ifndef _LINUX_ETHTOOL_H #define _LINUX_ETHTOOL_H +#ifdef __KERNEL__ +#include +#endif #include #include @@ -449,6 +452,37 @@ struct ethtool_rxnfc { __u32 rule_locs[0]; }; +#ifdef __KERNEL__ +#ifdef CONFIG_COMPAT + +struct compat_ethtool_rx_flow_spec { + u32 flow_type; + union { + struct ethtool_tcpip4_spec tcp_ip4_spec; + struct ethtool_tcpip4_spec udp_ip4_spec; + struct ethtool_tcpip4_spec sctp_ip4_spec; + struct ethtool_ah_espip4_spec ah_ip4_spec; + struct ethtool_ah_espip4_spec esp_ip4_spec; + struct ethtool_usrip4_spec usr_ip4_spec; + struct ethhdr ether_spec; + u8 hdata[72]; + } h_u, m_u; + compat_u64 ring_cookie; + u32 location; +}; + +struct compat_ethtool_rxnfc { + u32 cmd; + u32 flow_type; + compat_u64 data; + struct compat_ethtool_rx_flow_spec fs; + u32 rule_cnt; + u32 rule_locs[0]; +}; + +#endif /* CONFIG_COMPAT */ +#endif /* __KERNEL__ */ + /** * struct ethtool_rxfh_indir - command to get or set RX flow hash indirection * @cmd: Specific command number - %ETHTOOL_GRXFHINDIR or %ETHTOOL_SRXFHINDIR diff --git a/net/socket.c b/net/socket.c index ac2219f90d5dd..29c7df0ed3f8f 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2583,23 +2583,123 @@ static int dev_ifconf(struct net *net, struct compat_ifconf __user *uifc32) static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32) { + struct compat_ethtool_rxnfc __user *compat_rxnfc; + bool convert_in = false, convert_out = false; + size_t buf_size = ALIGN(sizeof(struct ifreq), 8); + struct ethtool_rxnfc __user *rxnfc; struct ifreq __user *ifr; + u32 rule_cnt = 0, actual_rule_cnt; + u32 ethcmd; u32 data; - void __user *datap; + int ret; + + if (get_user(data, &ifr32->ifr_ifru.ifru_data)) + return -EFAULT; - ifr = compat_alloc_user_space(sizeof(*ifr)); + compat_rxnfc = compat_ptr(data); - if (copy_in_user(&ifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ)) + if (get_user(ethcmd, &compat_rxnfc->cmd)) return -EFAULT; - if (get_user(data, &ifr32->ifr_ifru.ifru_data)) + /* Most ethtool structures are defined without padding. + * Unfortunately struct ethtool_rxnfc is an exception. + */ + switch (ethcmd) { + default: + break; + case ETHTOOL_GRXCLSRLALL: + /* Buffer size is variable */ + if (get_user(rule_cnt, &compat_rxnfc->rule_cnt)) + return -EFAULT; + if (rule_cnt > KMALLOC_MAX_SIZE / sizeof(u32)) + return -ENOMEM; + buf_size += rule_cnt * sizeof(u32); + /* fall through */ + case ETHTOOL_GRXRINGS: + case ETHTOOL_GRXCLSRLCNT: + case ETHTOOL_GRXCLSRULE: + convert_out = true; + /* fall through */ + case ETHTOOL_SRXCLSRLDEL: + case ETHTOOL_SRXCLSRLINS: + buf_size += sizeof(struct ethtool_rxnfc); + convert_in = true; + break; + } + + ifr = compat_alloc_user_space(buf_size); + rxnfc = (void *)ifr + ALIGN(sizeof(struct ifreq), 8); + + if (copy_in_user(&ifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ)) return -EFAULT; - datap = compat_ptr(data); - if (put_user(datap, &ifr->ifr_ifru.ifru_data)) + if (put_user(convert_in ? rxnfc : compat_ptr(data), + &ifr->ifr_ifru.ifru_data)) return -EFAULT; - return dev_ioctl(net, SIOCETHTOOL, ifr); + if (convert_in) { + /* We expect there to be holes between fs.m_u and + * fs.ring_cookie and at the end of fs, but nowhere else. + */ + BUILD_BUG_ON(offsetof(struct compat_ethtool_rxnfc, fs.m_u) + + sizeof(compat_rxnfc->fs.m_u) != + offsetof(struct ethtool_rxnfc, fs.m_u) + + sizeof(rxnfc->fs.m_u)); + BUILD_BUG_ON( + offsetof(struct compat_ethtool_rxnfc, fs.location) - + offsetof(struct compat_ethtool_rxnfc, fs.ring_cookie) != + offsetof(struct ethtool_rxnfc, fs.location) - + offsetof(struct ethtool_rxnfc, fs.ring_cookie)); + + if (copy_in_user(rxnfc, compat_rxnfc, + (void *)(&rxnfc->fs.m_u + 1) - + (void *)rxnfc) || + copy_in_user(&rxnfc->fs.ring_cookie, + &compat_rxnfc->fs.ring_cookie, + (void *)(&rxnfc->fs.location + 1) - + (void *)&rxnfc->fs.ring_cookie) || + copy_in_user(&rxnfc->rule_cnt, &compat_rxnfc->rule_cnt, + sizeof(rxnfc->rule_cnt))) + return -EFAULT; + } + + ret = dev_ioctl(net, SIOCETHTOOL, ifr); + if (ret) + return ret; + + if (convert_out) { + if (copy_in_user(compat_rxnfc, rxnfc, + (const void *)(&rxnfc->fs.m_u + 1) - + (const void *)rxnfc) || + copy_in_user(&compat_rxnfc->fs.ring_cookie, + &rxnfc->fs.ring_cookie, + (const void *)(&rxnfc->fs.location + 1) - + (const void *)&rxnfc->fs.ring_cookie) || + copy_in_user(&compat_rxnfc->rule_cnt, &rxnfc->rule_cnt, + sizeof(rxnfc->rule_cnt))) + return -EFAULT; + + if (ethcmd == ETHTOOL_GRXCLSRLALL) { + /* As an optimisation, we only copy the actual + * number of rules that the underlying + * function returned. Since Mallory might + * change the rule count in user memory, we + * check that it is less than the rule count + * originally given (as the user buffer size), + * which has been range-checked. + */ + if (get_user(actual_rule_cnt, &rxnfc->rule_cnt)) + return -EFAULT; + if (actual_rule_cnt < rule_cnt) + rule_cnt = actual_rule_cnt; + if (copy_in_user(&compat_rxnfc->rule_locs[0], + &rxnfc->rule_locs[0], + rule_cnt * sizeof(u32))) + return -EFAULT; + } + } + + return 0; } static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32) From 341814070b8ff05d451550db0f2cb809c35db8d5 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Mon, 14 Feb 2011 17:46:21 +0200 Subject: [PATCH 0089/2556] Revert "slab: Fix missing DEBUG_SLAB last user" commit 3ff84a7f36554b257cd57325b1a7c1fa4b49fbe3 upstream. This reverts commit 5c5e3b33b7cb959a401f823707bee006caadd76e. The commit breaks ARM thusly: | Mount-cache hash table entries: 512 | slab error in verify_redzone_free(): cache `idr_layer_cache': memory outside object was overwritten | Backtrace: | [] (dump_backtrace+0x0/0x110) from [] (dump_stack+0x18/0x1c) | [] (dump_stack+0x0/0x1c) from [] (__slab_error+0x28/0x30) | [] (__slab_error+0x0/0x30) from [] (cache_free_debugcheck+0x1c0/0x2b8) | [] (cache_free_debugcheck+0x0/0x2b8) from [] (kmem_cache_free+0x3c/0xc0) | [] (kmem_cache_free+0x0/0xc0) from [] (ida_get_new_above+0x19c/0x1c0) | [] (ida_get_new_above+0x0/0x1c0) from [] (alloc_vfsmnt+0x54/0x144) | [] (alloc_vfsmnt+0x0/0x144) from [] (vfs_kern_mount+0x30/0xec) | [] (vfs_kern_mount+0x0/0xec) from [] (kern_mount_data+0x1c/0x20) | [] (kern_mount_data+0x0/0x20) from [] (sysfs_init+0x68/0xc8) | [] (sysfs_init+0x0/0xc8) from [] (mnt_init+0x90/0x1b0) | [] (mnt_init+0x0/0x1b0) from [] (vfs_caches_init+0x100/0x140) | [] (vfs_caches_init+0x0/0x140) from [] (start_kernel+0x2e8/0x368) | [] (start_kernel+0x0/0x368) from [] (__enable_mmu+0x0/0x2c) | c0113268: redzone 1:0xd84156c5c032b3ac, redzone 2:0xd84156c5635688c0. | slab error in cache_alloc_debugcheck_after(): cache `idr_layer_cache': double free, or memory outside object was overwritten | ... | c011307c: redzone 1:0x9f91102ffffffff, redzone 2:0x9f911029d74e35b | slab: Internal list corruption detected in cache 'idr_layer_cache'(24), slabp c0113000(16). Hexdump: | | 000: 20 4f 10 c0 20 4f 10 c0 7c 00 00 00 7c 30 11 c0 | 010: 10 00 00 00 10 00 00 00 00 00 c9 17 fe ff ff ff | 020: fe ff ff ff fe ff ff ff fe ff ff ff fe ff ff ff | 030: fe ff ff ff fe ff ff ff fe ff ff ff fe ff ff ff | 040: fe ff ff ff fe ff ff ff fe ff ff ff fe ff ff ff | 050: fe ff ff ff fe ff ff ff fe ff ff ff 11 00 00 00 | 060: 12 00 00 00 13 00 00 00 14 00 00 00 15 00 00 00 | 070: 16 00 00 00 17 00 00 00 c0 88 56 63 | kernel BUG at /home/rmk/git/linux-2.6-rmk/mm/slab.c:2928! Reference: https://lkml.org/lkml/2011/2/7/238 Reported-and-analyzed-by: Russell King Signed-off-by: Pekka Enberg Signed-off-by: Greg Kroah-Hartman --- mm/slab.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mm/slab.c b/mm/slab.c index 37961d1f584fe..4c6e2e31ced05 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -2288,8 +2288,8 @@ kmem_cache_create (const char *name, size_t size, size_t align, if (ralign < align) { ralign = align; } - /* disable debug if not aligning with REDZONE_ALIGN */ - if (ralign & (__alignof__(unsigned long long) - 1)) + /* disable debug if necessary */ + if (ralign > __alignof__(unsigned long long)) flags &= ~(SLAB_RED_ZONE | SLAB_STORE_USER); /* * 4) Store it. @@ -2315,8 +2315,8 @@ kmem_cache_create (const char *name, size_t size, size_t align, */ if (flags & SLAB_RED_ZONE) { /* add space for red zone words */ - cachep->obj_offset += align; - size += align + sizeof(unsigned long long); + cachep->obj_offset += sizeof(unsigned long long); + size += 2 * sizeof(unsigned long long); } if (flags & SLAB_STORE_USER) { /* user store requires one word storage behind the end of From 715335d9edcad02a3502a609290229ce179a62a1 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Tue, 22 Mar 2011 16:35:10 -0700 Subject: [PATCH 0090/2556] aio: wake all waiters when destroying ctx commit e91f90bb0bb10be9cc8efd09a3cf4ecffcad0db1 upstream. The test program below will hang because io_getevents() uses add_wait_queue_exclusive(), which means the wake_up() in io_destroy() only wakes up one of the threads. Fix this by using wake_up_all() in the aio code paths where we want to make sure no one gets stuck. // t.c -- compile with gcc -lpthread -laio t.c #include #include #include #include static const int nthr = 2; void *getev(void *ctx) { struct io_event ev; io_getevents(ctx, 1, 1, &ev, NULL); printf("io_getevents returned\n"); return NULL; } int main(int argc, char *argv[]) { io_context_t ctx = 0; pthread_t thread[nthr]; int i; io_setup(1024, &ctx); for (i = 0; i < nthr; ++i) pthread_create(&thread[i], NULL, getev, ctx); sleep(1); io_destroy(ctx); for (i = 0; i < nthr; ++i) pthread_join(thread[i], NULL); return 0; } Signed-off-by: Roland Dreier Reviewed-by: Jeff Moyer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/aio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/aio.c b/fs/aio.c index 26869cde39535..88f0ed5144252 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -520,7 +520,7 @@ static inline void really_put_req(struct kioctx *ctx, struct kiocb *req) ctx->reqs_active--; if (unlikely(!ctx->reqs_active && ctx->dead)) - wake_up(&ctx->wait); + wake_up_all(&ctx->wait); } static void aio_fput_routine(struct work_struct *data) @@ -1229,7 +1229,7 @@ static void io_destroy(struct kioctx *ioctx) * by other CPUs at this point. Right now, we rely on the * locking done by the above calls to ensure this consistency. */ - wake_up(&ioctx->wait); + wake_up_all(&ioctx->wait); put_ioctx(ioctx); /* once for the lookup */ } From 9432a739a5063919c14c15c722c653a16d797474 Mon Sep 17 00:00:00 2001 From: Phil Carmody Date: Tue, 22 Mar 2011 16:30:13 -0700 Subject: [PATCH 0091/2556] cgroups: if you list_empty() a head then don't list_del() it commit 8d2587970b8bdf7c8d9208e3f4bb93182aef1a0f upstream. list_del() leaves poison in the prev and next pointers. The next list_empty() will compare those poisons, and say the list isn't empty. Any list operations that assume the node is on a list because of such a check will be fooled into dereferencing poison. One needs to INIT the node after the del, and fortunately there's already a wrapper for that - list_del_init(). Some of the dels are followed by deallocations, so can be ignored, and one can be merged with an add to make a move. Apart from that, I erred on the side of caution in making nodes list_empty()-queriable. Signed-off-by: Phil Carmody Reviewed-by: Paul Menage Cc: Li Zefan Acked-by: Kirill A. Shutemov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/cgroup.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/kernel/cgroup.c b/kernel/cgroup.c index b24d7027b83c2..bcc73369ac20b 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1813,10 +1813,8 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk) /* Update the css_set linked lists if we're using them */ write_lock(&css_set_lock); - if (!list_empty(&tsk->cg_list)) { - list_del(&tsk->cg_list); - list_add(&tsk->cg_list, &newcg->tasks); - } + if (!list_empty(&tsk->cg_list)) + list_move(&tsk->cg_list, &newcg->tasks); write_unlock(&css_set_lock); for_each_subsys(root, ss) { @@ -3655,12 +3653,12 @@ static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry) spin_lock(&release_list_lock); set_bit(CGRP_REMOVED, &cgrp->flags); if (!list_empty(&cgrp->release_list)) - list_del(&cgrp->release_list); + list_del_init(&cgrp->release_list); spin_unlock(&release_list_lock); cgroup_lock_hierarchy(cgrp->root); /* delete this cgroup from parent->children */ - list_del(&cgrp->sibling); + list_del_init(&cgrp->sibling); cgroup_unlock_hierarchy(cgrp->root); d = dget(cgrp->dentry); @@ -3879,7 +3877,7 @@ void cgroup_unload_subsys(struct cgroup_subsys *ss) subsys[ss->subsys_id] = NULL; /* remove subsystem from rootnode's list of subsystems */ - list_del(&ss->sibling); + list_del_init(&ss->sibling); /* * disentangle the css from all css_sets attached to the dummytop. as @@ -4253,7 +4251,7 @@ void cgroup_exit(struct task_struct *tsk, int run_callbacks) if (!list_empty(&tsk->cg_list)) { write_lock(&css_set_lock); if (!list_empty(&tsk->cg_list)) - list_del(&tsk->cg_list); + list_del_init(&tsk->cg_list); write_unlock(&css_set_lock); } From 3d46e7eeddf8d661b7c8f63589f302a13fd9c9a1 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Tue, 22 Mar 2011 16:33:43 -0700 Subject: [PATCH 0092/2556] shmem: let shared anonymous be nonlinear again commit bee4c36a5cf5c9f63ce1d7372aa62045fbd16d47 upstream. Up to 2.6.22, you could use remap_file_pages(2) on a tmpfs file or a shared mapping of /dev/zero or a shared anonymous mapping. In 2.6.23 we disabled it by default, but set VM_CAN_NONLINEAR to enable it on safe mappings. We made sure to set it in shmem_mmap() for tmpfs files, but missed it in shmem_zero_setup() for the others. Fix that at last. Reported-by: Kenny Simpson Signed-off-by: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/shmem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/shmem.c b/mm/shmem.c index 5ee67c9906022..5ac23d570f965 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2791,5 +2791,6 @@ int shmem_zero_setup(struct vm_area_struct *vma) fput(vma->vm_file); vma->vm_file = file; vma->vm_ops = &shmem_vm_ops; + vma->vm_flags |= VM_CAN_NONLINEAR; return 0; } From 9bae11d385c93f6e6b86c839b1e45a997186fc19 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 22 Mar 2011 16:30:08 -0700 Subject: [PATCH 0093/2556] mm: swap: unlock swapfile inode mutex before closing file on bad swapfiles commit 52c50567d8ab0a0a87f12cceaa4194967854f0bd upstream. If an administrator tries to swapon a file backed by NFS, the inode mutex is taken (as it is for any swapfile) but later identified to be a bad swapfile due to the lack of bmap and tries to cleanup. During cleanup, an attempt is made to close the file but with inode->i_mutex still held. Closing an NFS file syncs it which tries to acquire the inode mutex leading to deadlock. If lockdep is enabled the following appears on the console; ============================================= [ INFO: possible recursive locking detected ] 2.6.38-rc8-autobuild #1 --------------------------------------------- swapon/2192 is trying to acquire lock: (&sb->s_type->i_mutex_key#13){+.+.+.}, at: vfs_fsync_range+0x47/0x7c but task is already holding lock: (&sb->s_type->i_mutex_key#13){+.+.+.}, at: sys_swapon+0x28d/0xae7 other info that might help us debug this: 1 lock held by swapon/2192: #0: (&sb->s_type->i_mutex_key#13){+.+.+.}, at: sys_swapon+0x28d/0xae7 stack backtrace: Pid: 2192, comm: swapon Not tainted 2.6.38-rc8-autobuild #1 Call Trace: __lock_acquire+0x2eb/0x1623 find_get_pages_tag+0x14a/0x174 pagevec_lookup_tag+0x25/0x2e vfs_fsync_range+0x47/0x7c lock_acquire+0xd3/0x100 vfs_fsync_range+0x47/0x7c nfs_flush_one+0x0/0xdf [nfs] mutex_lock_nested+0x40/0x2b1 vfs_fsync_range+0x47/0x7c vfs_fsync_range+0x47/0x7c vfs_fsync+0x1c/0x1e nfs_file_flush+0x64/0x69 [nfs] filp_close+0x43/0x72 sys_swapon+0xa39/0xae7 sysret_check+0x2e/0x69 system_call_fastpath+0x16/0x1b This patch releases the mutex if its held before calling filep_close() so swapon fails as expected without deadlock when the swapfile is backed by NFS. If accepted for 2.6.39, it should also be considered a -stable candidate for 2.6.38 and 2.6.37. Signed-off-by: Mel Gorman Acked-by: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/swapfile.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mm/swapfile.c b/mm/swapfile.c index 0341c5700e346..6d6d28c0a72f6 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -2149,8 +2149,13 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) p->flags = 0; spin_unlock(&swap_lock); vfree(swap_map); - if (swap_file) + if (swap_file) { + if (did_down) { + mutex_unlock(&inode->i_mutex); + did_down = 0; + } filp_close(swap_file, NULL); + } out: if (page && !IS_ERR(page)) { kunmap(page); From 1a0a10e49acd5526dda6c49021dc55011ec9578b Mon Sep 17 00:00:00 2001 From: Andrea Arcangeli Date: Tue, 22 Mar 2011 16:30:38 -0700 Subject: [PATCH 0094/2556] mm: compaction: prevent kswapd compacting memory to reduce CPU usage commit d527caf22e48480b102c7c6ee5b9ba12170148f7 upstream. This patch reverts 5a03b051 ("thp: use compaction in kswapd for GFP_ATOMIC order > 0") due to reports stating that kswapd CPU usage was higher and IRQs were being disabled more frequently. This was reported at http://www.spinics.net/linux/fedora/alsa-user/msg09885.html. Without this patch applied, CPU usage by kswapd hovers around the 20% mark according to the tester (Arthur Marsh: http://www.spinics.net/linux/fedora/alsa-user/msg09899.html). With this patch applied, it's around 2%. The problem is not related to THP which specifies __GFP_NO_KSWAPD but is triggered by high-order allocations hitting the low watermark for their order and waking kswapd on kernels with CONFIG_COMPACTION set. The most common trigger for this is network cards configured for jumbo frames but it's also possible it'll be triggered by fork-heavy workloads (order-1) and some wireless cards which depend on order-1 allocations. The symptoms for the user will be high CPU usage by kswapd in low-memory situations which could be confused with another writeback problem. While a patch like 5a03b051 may be reintroduced in the future, this patch plays it safe for now and reverts it. [mel@csn.ul.ie: Beefed up the changelog] Signed-off-by: Andrea Arcangeli Signed-off-by: Mel Gorman Reported-by: Arthur Marsh Tested-by: Arthur Marsh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- include/linux/compaction.h | 9 ++------- mm/compaction.c | 24 +++--------------------- mm/vmscan.c | 18 +----------------- 3 files changed, 6 insertions(+), 45 deletions(-) diff --git a/include/linux/compaction.h b/include/linux/compaction.h index dfa2ed4c0d26a..cc9f7a4286490 100644 --- a/include/linux/compaction.h +++ b/include/linux/compaction.h @@ -11,9 +11,6 @@ /* The full zone was compacted */ #define COMPACT_COMPLETE 3 -#define COMPACT_MODE_DIRECT_RECLAIM 0 -#define COMPACT_MODE_KSWAPD 1 - #ifdef CONFIG_COMPACTION extern int sysctl_compact_memory; extern int sysctl_compaction_handler(struct ctl_table *table, int write, @@ -28,8 +25,7 @@ extern unsigned long try_to_compact_pages(struct zonelist *zonelist, bool sync); extern unsigned long compaction_suitable(struct zone *zone, int order); extern unsigned long compact_zone_order(struct zone *zone, int order, - gfp_t gfp_mask, bool sync, - int compact_mode); + gfp_t gfp_mask, bool sync); /* Do not skip compaction more than 64 times */ #define COMPACT_MAX_DEFER_SHIFT 6 @@ -74,8 +70,7 @@ static inline unsigned long compaction_suitable(struct zone *zone, int order) } static inline unsigned long compact_zone_order(struct zone *zone, int order, - gfp_t gfp_mask, bool sync, - int compact_mode) + gfp_t gfp_mask, bool sync) { return COMPACT_CONTINUE; } diff --git a/mm/compaction.c b/mm/compaction.c index 8be430b812def..dcb058bd76c42 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -42,8 +42,6 @@ struct compact_control { unsigned int order; /* order a direct compactor needs */ int migratetype; /* MOVABLE, RECLAIMABLE etc */ struct zone *zone; - - int compact_mode; }; static unsigned long release_freepages(struct list_head *freelist) @@ -397,10 +395,7 @@ static int compact_finished(struct zone *zone, return COMPACT_COMPLETE; /* Compaction run is not finished if the watermark is not met */ - if (cc->compact_mode != COMPACT_MODE_KSWAPD) - watermark = low_wmark_pages(zone); - else - watermark = high_wmark_pages(zone); + watermark = low_wmark_pages(zone); watermark += (1 << cc->order); if (!zone_watermark_ok(zone, cc->order, watermark, 0, 0)) @@ -413,15 +408,6 @@ static int compact_finished(struct zone *zone, if (cc->order == -1) return COMPACT_CONTINUE; - /* - * Generating only one page of the right order is not enough - * for kswapd, we must continue until we're above the high - * watermark as a pool for high order GFP_ATOMIC allocations - * too. - */ - if (cc->compact_mode == COMPACT_MODE_KSWAPD) - return COMPACT_CONTINUE; - /* Direct compactor: Is a suitable page free? */ for (order = cc->order; order < MAX_ORDER; order++) { /* Job done if page is free of the right migratetype */ @@ -543,8 +529,7 @@ static int compact_zone(struct zone *zone, struct compact_control *cc) unsigned long compact_zone_order(struct zone *zone, int order, gfp_t gfp_mask, - bool sync, - int compact_mode) + bool sync) { struct compact_control cc = { .nr_freepages = 0, @@ -553,7 +538,6 @@ unsigned long compact_zone_order(struct zone *zone, .migratetype = allocflags_to_migratetype(gfp_mask), .zone = zone, .sync = sync, - .compact_mode = compact_mode, }; INIT_LIST_HEAD(&cc.freepages); INIT_LIST_HEAD(&cc.migratepages); @@ -599,8 +583,7 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist, nodemask) { int status; - status = compact_zone_order(zone, order, gfp_mask, sync, - COMPACT_MODE_DIRECT_RECLAIM); + status = compact_zone_order(zone, order, gfp_mask, sync); rc = max(status, rc); /* If a normal allocation would succeed, stop compacting */ @@ -631,7 +614,6 @@ static int compact_node(int nid) .nr_freepages = 0, .nr_migratepages = 0, .order = -1, - .compact_mode = COMPACT_MODE_DIRECT_RECLAIM, }; zone = &pgdat->node_zones[zoneid]; diff --git a/mm/vmscan.c b/mm/vmscan.c index 6771ea70bfe7e..3b4a41d724894 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2397,7 +2397,6 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, * cause too much scanning of the lower zones. */ for (i = 0; i <= end_zone; i++) { - int compaction; struct zone *zone = pgdat->node_zones + i; int nr_slab; @@ -2428,24 +2427,9 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, sc.nr_reclaimed += reclaim_state->reclaimed_slab; total_scanned += sc.nr_scanned; - compaction = 0; - if (order && - zone_watermark_ok(zone, 0, - high_wmark_pages(zone), - end_zone, 0) && - !zone_watermark_ok(zone, order, - high_wmark_pages(zone), - end_zone, 0)) { - compact_zone_order(zone, - order, - sc.gfp_mask, false, - COMPACT_MODE_KSWAPD); - compaction = 1; - } - if (zone->all_unreclaimable) continue; - if (!compaction && nr_slab == 0 && + if (nr_slab == 0 && !zone_reclaimable(zone)) zone->all_unreclaimable = 1; /* From 1163d4ca61ff2544f66b2663c6050f5f00c01fc1 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 22 Mar 2011 16:30:09 -0700 Subject: [PATCH 0095/2556] oom: prevent unnecessary oom kills or kernel panics commit 3a5dda7a17cf3706f79b86293f29db02d61e0d48 upstream. This patch prevents unnecessary oom kills or kernel panics by reverting two commits: 495789a5 (oom: make oom_score to per-process value) cef1d352 (oom: multi threaded process coredump don't make deadlock) First, 495789a5 (oom: make oom_score to per-process value) ignores the fact that all threads in a thread group do not necessarily exit at the same time. It is imperative that select_bad_process() detect threads that are in the exit path, specifically those with PF_EXITING set, to prevent needlessly killing additional tasks. If a process is oom killed and the thread group leader exits, select_bad_process() cannot detect the other threads that are PF_EXITING by iterating over only processes. Thus, it currently chooses another task unnecessarily for oom kill or panics the machine when nothing else is eligible. By iterating over threads instead, it is possible to detect threads that are exiting and nominate them for oom kill so they get access to memory reserves. Second, cef1d352 (oom: multi threaded process coredump don't make deadlock) erroneously avoids making the oom killer a no-op when an eligible thread other than current isfound to be exiting. We want to detect this situation so that we may allow that exiting thread time to exit and free its memory; if it is able to exit on its own, that should free memory so current is no loner oom. If it is not able to exit on its own, the oom killer will nominate it for oom kill which, in this case, only means it will get access to memory reserves. Without this change, it is easy for the oom killer to unnecessarily target tasks when all threads of a victim don't exit before the thread group leader or, in the worst case, panic the machine. Signed-off-by: David Rientjes Cc: KOSAKI Motohiro Cc: KAMEZAWA Hiroyuki Cc: Oleg Nesterov Cc: Hugh Dickins Cc: Andrey Vagin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/oom_kill.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mm/oom_kill.c b/mm/oom_kill.c index 7dcca55ede7ca..b5a7b5f46e7ac 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -292,11 +292,11 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, unsigned long totalpages, struct mem_cgroup *mem, const nodemask_t *nodemask) { - struct task_struct *p; + struct task_struct *g, *p; struct task_struct *chosen = NULL; *ppoints = 0; - for_each_process(p) { + do_each_thread(g, p) { unsigned int points; if (oom_unkillable_task(p, mem, nodemask)) @@ -324,7 +324,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, * the process of exiting and releasing its resources. * Otherwise we could get an easy OOM deadlock. */ - if (thread_group_empty(p) && (p->flags & PF_EXITING) && p->mm) { + if ((p->flags & PF_EXITING) && p->mm) { if (p != current) return ERR_PTR(-1UL); @@ -337,7 +337,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, chosen = p; *ppoints = points; } - } + } while_each_thread(g, p); return chosen; } From d00451caef12248e498ba15cc9ba04192a66f3e3 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Tue, 22 Mar 2011 16:30:11 -0700 Subject: [PATCH 0096/2556] oom: skip zombies when iterating tasklist commit 30e2b41f20b6238f51e7cffb879c7a0f0073f5fe upstream. We shouldn't defer oom killing if a thread has already detached its ->mm and still has TIF_MEMDIE set. Memory needs to be freed, so find kill other threads that pin the same ->mm or find another task to kill. Signed-off-by: Andrey Vagin Signed-off-by: David Rientjes Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/oom_kill.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mm/oom_kill.c b/mm/oom_kill.c index b5a7b5f46e7ac..d7f345e47e73a 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -299,6 +299,8 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, do_each_thread(g, p) { unsigned int points; + if (!p->mm) + continue; if (oom_unkillable_task(p, mem, nodemask)) continue; @@ -324,7 +326,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, * the process of exiting and releasing its resources. * Otherwise we could get an easy OOM deadlock. */ - if ((p->flags & PF_EXITING) && p->mm) { + if (p->flags & PF_EXITING) { if (p != current) return ERR_PTR(-1UL); From cb5f27be88b36c0b91f8a24ad8295de3a0164d08 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 22 Mar 2011 16:30:12 -0700 Subject: [PATCH 0097/2556] oom: avoid deferring oom killer if exiting task is being traced commit edd45544c6f09550df0a5491aa8a07af24767e73 upstream. The oom killer naturally defers killing anything if it finds an eligible task that is already exiting and has yet to detach its ->mm. This avoids unnecessarily killing tasks when one is already in the exit path and may free enough memory that the oom killer is no longer needed. This is detected by PF_EXITING since threads that have already detached its ->mm are no longer considered at all. The problem with always deferring when a thread is PF_EXITING, however, is that it may never actually exit when being traced, specifically if another task is tracing it with PTRACE_O_TRACEEXIT. The oom killer does not want to defer in this case since there is no guarantee that thread will ever exit without intervention. This patch will now only defer the oom killer when a thread is PF_EXITING and no ptracer has stopped its progress in the exit path. It also ensures that a child is sacrificed for the chosen parent only if it has a different ->mm as the comment implies: this ensures that the thread group leader is always targeted appropriately. Signed-off-by: David Rientjes Reported-by: Oleg Nesterov Cc: KOSAKI Motohiro Cc: KAMEZAWA Hiroyuki Cc: Hugh Dickins Cc: Andrey Vagin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/oom_kill.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/mm/oom_kill.c b/mm/oom_kill.c index d7f345e47e73a..33b58615072cc 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -31,6 +31,7 @@ #include #include #include +#include int sysctl_panic_on_oom; int sysctl_oom_kill_allocating_task; @@ -316,22 +317,29 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, if (test_tsk_thread_flag(p, TIF_MEMDIE)) return ERR_PTR(-1UL); - /* - * This is in the process of releasing memory so wait for it - * to finish before killing some other task by mistake. - * - * However, if p is the current task, we allow the 'kill' to - * go ahead if it is exiting: this will simply set TIF_MEMDIE, - * which will allow it to gain access to memory reserves in - * the process of exiting and releasing its resources. - * Otherwise we could get an easy OOM deadlock. - */ if (p->flags & PF_EXITING) { - if (p != current) - return ERR_PTR(-1UL); - - chosen = p; - *ppoints = 1000; + /* + * If p is the current task and is in the process of + * releasing memory, we allow the "kill" to set + * TIF_MEMDIE, which will allow it to gain access to + * memory reserves. Otherwise, it may stall forever. + * + * The loop isn't broken here, however, in case other + * threads are found to have already been oom killed. + */ + if (p == current) { + chosen = p; + *ppoints = 1000; + } else { + /* + * If this task is not being ptraced on exit, + * then wait for it to finish before killing + * some other task unnecessarily. + */ + if (!(task_ptrace(p->group_leader) & + PT_TRACE_EXIT)) + return ERR_PTR(-1UL); + } } points = oom_badness(p, mem, nodemask, totalpages); @@ -493,6 +501,8 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, list_for_each_entry(child, &t->children, sibling) { unsigned int child_points; + if (child->mm == p->mm) + continue; /* * oom_badness() returns 0 if the thread is unkillable */ From 9677d54e1263dfff0ca00d4039aebc4c1e63ba4b Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Mon, 28 Feb 2011 16:20:11 +0000 Subject: [PATCH 0098/2556] PCI hotplug: acpiphp: set current_state to D0 in register_slot commit 47e9037ac16637cd7f12b8790ea7ce6680e42168 upstream. If a device doesn't support power management (pm_cap == 0) but it is acpi_pci_power_manageable() because there is a _PS0 method declared for it and _EJ0 is also declared for the slot then nobody is going to set current_state = PCI_D0 for this device. This is what I think it is happening: pci_enable_device | __pci_enable_device_flags /* here we do not set current_state because !pm_cap */ | do_pci_enable_device | pci_set_power_state | __pci_start_power_transition | pci_platform_power_transition /* platform_pci_power_manageable() calls acpi_pci_power_manageable that * returns true */ | platform_pci_set_power_state /* acpi_pci_set_power_state gets called and does nothing because the * acpi device has _EJ0, see the comment "If the ACPI device has _EJ0, * ignore the device" */ at this point if we refer to the commit message that introduced the comment above (10b3dcae0f275e2546e55303d64ddbb58cec7599), it is up to the hotplug driver to set the state to D0. However AFAICT the pci hotplug driver never does, in fact drivers/pci/hotplug/acpiphp_glue.c:register_slot sets the slot flags to (SLOT_ENABLED | SLOT_POWEREDON) but it does not set the pci device current state to PCI_D0. So my proposed fix is also to set current_state = PCI_D0 in register_slot. Comments are very welcome. Signed-off-by: Stefano Stabellini Signed-off-by: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- drivers/pci/hotplug/acpiphp_glue.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index cb23aa2ebf96b..e610cfe4f07bc 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -212,6 +212,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) pdev = pci_get_slot(pbus, PCI_DEVFN(device, function)); if (pdev) { + pdev->current_state = PCI_D0; slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON); pci_dev_put(pdev); } From 5ea5254aa0ad269cfbd2875c973ef25ab5b5e9db Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Wed, 16 Mar 2011 22:11:46 -0700 Subject: [PATCH 0099/2556] Input: xen-kbdfront - advertise either absolute or relative coordinates commit 8c3c283e6bf463ab498d6e7823aff6c4762314b6 upstream. A virtualized display device is usually viewed with the vncviewer application, either by 'xm vnc domU' or with vncviewer localhost:port. vncviewer and the RFB protocol provides absolute coordinates to the virtual display. These coordinates are either passed through to a PV guest or converted to relative coordinates for a HVM guest. A PV guest receives these coordinates and passes them to the kernels evdev driver. There it can be picked up by applications such as the xorg-input drivers. Using absolute coordinates avoids issues such as guest mouse pointer not tracking host mouse pointer due to wrong mouse acceleration settings in the guests X display. Advertise either absolute or relative coordinates to the input system and the evdev driver, depending on what dom0 provides. The xorg-input driver prefers relative coordinates even if a devices provides both. Signed-off-by: Olaf Hering Signed-off-by: Stefano Stabellini Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/xen-kbdfront.c | 44 ++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c index 7f85a862ad116..53e62732ee96f 100644 --- a/drivers/input/xen-kbdfront.c +++ b/drivers/input/xen-kbdfront.c @@ -110,7 +110,7 @@ static irqreturn_t input_handler(int rq, void *dev_id) static int __devinit xenkbd_probe(struct xenbus_device *dev, const struct xenbus_device_id *id) { - int ret, i; + int ret, i, abs; struct xenkbd_info *info; struct input_dev *kbd, *ptr; @@ -128,6 +128,11 @@ static int __devinit xenkbd_probe(struct xenbus_device *dev, if (!info->page) goto error_nomem; + if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-abs-pointer", "%d", &abs) < 0) + abs = 0; + if (abs) + xenbus_printf(XBT_NIL, dev->nodename, "request-abs-pointer", "1"); + /* keyboard */ kbd = input_allocate_device(); if (!kbd) @@ -137,11 +142,12 @@ static int __devinit xenkbd_probe(struct xenbus_device *dev, kbd->id.bustype = BUS_PCI; kbd->id.vendor = 0x5853; kbd->id.product = 0xffff; - kbd->evbit[0] = BIT(EV_KEY); + + __set_bit(EV_KEY, kbd->evbit); for (i = KEY_ESC; i < KEY_UNKNOWN; i++) - set_bit(i, kbd->keybit); + __set_bit(i, kbd->keybit); for (i = KEY_OK; i < KEY_MAX; i++) - set_bit(i, kbd->keybit); + __set_bit(i, kbd->keybit); ret = input_register_device(kbd); if (ret) { @@ -160,12 +166,20 @@ static int __devinit xenkbd_probe(struct xenbus_device *dev, ptr->id.bustype = BUS_PCI; ptr->id.vendor = 0x5853; ptr->id.product = 0xfffe; - ptr->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS); + + if (abs) { + __set_bit(EV_ABS, ptr->evbit); + input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0); + input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0); + } else { + input_set_capability(ptr, EV_REL, REL_X); + input_set_capability(ptr, EV_REL, REL_Y); + } + input_set_capability(ptr, EV_REL, REL_WHEEL); + + __set_bit(EV_KEY, ptr->evbit); for (i = BTN_LEFT; i <= BTN_TASK; i++) - set_bit(i, ptr->keybit); - ptr->relbit[0] = BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL); - input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0); - input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0); + __set_bit(i, ptr->keybit); ret = input_register_device(ptr); if (ret) { @@ -272,7 +286,7 @@ static void xenkbd_backend_changed(struct xenbus_device *dev, enum xenbus_state backend_state) { struct xenkbd_info *info = dev_get_drvdata(&dev->dev); - int ret, val; + int val; switch (backend_state) { case XenbusStateInitialising: @@ -285,16 +299,6 @@ static void xenkbd_backend_changed(struct xenbus_device *dev, case XenbusStateInitWait: InitWait: - ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend, - "feature-abs-pointer", "%d", &val); - if (ret < 0) - val = 0; - if (val) { - ret = xenbus_printf(XBT_NIL, info->xbdev->nodename, - "request-abs-pointer", "1"); - if (ret) - pr_warning("can't request abs-pointer\n"); - } xenbus_switch_state(dev, XenbusStateConnected); break; From 40e499c3a70aeb4d3eb090782d6c899e0fbb3895 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 18 Feb 2011 11:32:40 +0000 Subject: [PATCH 0100/2556] xen: set max_pfn_mapped to the last pfn mapped commit 14988a4d350ce3b41ecad4f63c4f44c56f5ae34d upstream. Do not set max_pfn_mapped to the end of the initial memory mappings, that also contain pages that don't belong in pfn space (like the mfn list). Set max_pfn_mapped to the last real pfn mapped in the initial memory mappings that is the pfn backing _end. Signed-off-by: Stefano Stabellini Acked-by: Konrad Rzeszutek Wilk LKML-Reference: Signed-off-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/xen/mmu.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index f6089421147a0..60205625e47e0 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -1651,9 +1651,6 @@ static __init void xen_map_identity_early(pmd_t *pmd, unsigned long max_pfn) for (pteidx = 0; pteidx < PTRS_PER_PTE; pteidx++, pfn++) { pte_t pte; - if (pfn > max_pfn_mapped) - max_pfn_mapped = pfn; - if (!pte_none(pte_page[pteidx])) continue; @@ -1711,6 +1708,12 @@ __init pgd_t *xen_setup_kernel_pagetable(pgd_t *pgd, pud_t *l3; pmd_t *l2; + /* max_pfn_mapped is the last pfn mapped in the initial memory + * mappings. Considering that on Xen after the kernel mappings we + * have the mappings of some pages that don't exist in pfn space, we + * set max_pfn_mapped to the last real pfn mapped. */ + max_pfn_mapped = PFN_DOWN(__pa(xen_start_info->mfn_list)); + /* Zap identity mapping */ init_level4_pgt[0] = __pgd(0); @@ -1815,9 +1818,7 @@ __init pgd_t *xen_setup_kernel_pagetable(pgd_t *pgd, initial_kernel_pmd = extend_brk(sizeof(pmd_t) * PTRS_PER_PMD, PAGE_SIZE); - max_pfn_mapped = PFN_DOWN(__pa(xen_start_info->pt_base) + - xen_start_info->nr_pt_frames * PAGE_SIZE + - 512*1024); + max_pfn_mapped = PFN_DOWN(__pa(xen_start_info->mfn_list)); kernel_pmd = m2v(pgd[KERNEL_PGD_BOUNDARY].pgd); memcpy(initial_kernel_pmd, kernel_pmd, sizeof(pmd_t) * PTRS_PER_PMD); From c93f57068049deb9907ca15ba2501bc5e382e513 Mon Sep 17 00:00:00 2001 From: Julien Tinnes Date: Fri, 18 Mar 2011 15:05:21 -0700 Subject: [PATCH 0101/2556] Prevent rt_sigqueueinfo and rt_tgsigqueueinfo from spoofing the signal code commit da48524eb20662618854bb3df2db01fc65f3070c upstream. Userland should be able to trust the pid and uid of the sender of a signal if the si_code is SI_TKILL. Unfortunately, the kernel has historically allowed sigqueueinfo() to send any si_code at all (as long as it was negative - to distinguish it from kernel-generated signals like SIGILL etc), so it could spoof a SI_TKILL with incorrect siginfo values. Happily, it looks like glibc has always set si_code to the appropriate SI_QUEUE, so there are probably no actual user code that ever uses anything but the appropriate SI_QUEUE flag. So just tighten the check for si_code (we used to allow any negative value), and add a (one-time) warning in case there are binaries out there that might depend on using other si_code values. Signed-off-by: Julien Tinnes Acked-by: Oleg Nesterov Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/signal.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/kernel/signal.c b/kernel/signal.c index 4e3cff10fdced..31751868de885 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2421,9 +2421,13 @@ SYSCALL_DEFINE3(rt_sigqueueinfo, pid_t, pid, int, sig, return -EFAULT; /* Not even root can pretend to send signals from the kernel. - Nor can they impersonate a kill(), which adds source info. */ - if (info.si_code >= 0) + * Nor can they impersonate a kill()/tgkill(), which adds source info. + */ + if (info.si_code != SI_QUEUE) { + /* We used to allow any < 0 si_code */ + WARN_ON_ONCE(info.si_code < 0); return -EPERM; + } info.si_signo = sig; /* POSIX.1b doesn't mention process groups. */ @@ -2437,9 +2441,13 @@ long do_rt_tgsigqueueinfo(pid_t tgid, pid_t pid, int sig, siginfo_t *info) return -EINVAL; /* Not even root can pretend to send signals from the kernel. - Nor can they impersonate a kill(), which adds source info. */ - if (info->si_code >= 0) + * Nor can they impersonate a kill()/tgkill(), which adds source info. + */ + if (info->si_code != SI_QUEUE) { + /* We used to allow any < 0 si_code */ + WARN_ON_ONCE(info->si_code < 0); return -EPERM; + } info->si_signo = sig; return do_send_specific(tgid, pid, sig, info); From b62a6839fdf5cb733559164da4f5242ab6de8e06 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 22 Mar 2011 10:23:28 +0100 Subject: [PATCH 0102/2556] ALSA: HDA: Fix internal mic on Dell E5420/E5520 This is a fixup for the 2.6.38 kernel, as the issue is being resolved by upstream commits 699d899560cd7e72da39231e584412e7ac8114a4 and 094a42452abd5564429045e210281c6d22e67fca - which are too invasive to reach 2.6.38. Instead make pin fixes as a workaround. BugLink: http://bugs.launchpad.net/bugs/740055 Tested-by: Kent Baxley Signed-off-by: David Henningsson Acked-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 052062dd523ba..856611909c413 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -94,6 +94,7 @@ enum { STAC_92HD83XXX_REF, STAC_92HD83XXX_PWR_REF, STAC_DELL_S14, + STAC_DELL_E5520M, STAC_92HD83XXX_HP, STAC_HP_DV7_4000, STAC_92HD83XXX_MODELS @@ -1657,6 +1658,13 @@ static unsigned int dell_s14_pin_configs[10] = { 0x40f000f0, 0x40f000f0, }; +/* Switch int mic from 0x20 to 0x11 */ +static unsigned int dell_e5520m_pin_configs[10] = { + 0x04a11020, 0x0421101f, 0x400000f0, 0x90170110, + 0x23011050, 0x23a1102e, 0x400000f3, 0xd5a30130, + 0x400000f0, 0x40f000f0, +}; + static unsigned int hp_dv7_4000_pin_configs[10] = { 0x03a12050, 0x0321201f, 0x40f000f0, 0x90170110, 0x40f000f0, 0x40f000f0, 0x90170110, 0xd5a30140, @@ -1667,6 +1675,7 @@ static unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = { [STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs, [STAC_92HD83XXX_PWR_REF] = ref92hd83xxx_pin_configs, [STAC_DELL_S14] = dell_s14_pin_configs, + [STAC_DELL_E5520M] = dell_e5520m_pin_configs, [STAC_HP_DV7_4000] = hp_dv7_4000_pin_configs, }; @@ -1675,6 +1684,7 @@ static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { [STAC_92HD83XXX_REF] = "ref", [STAC_92HD83XXX_PWR_REF] = "mic-ref", [STAC_DELL_S14] = "dell-s14", + [STAC_DELL_E5520M] = "dell-e5520m", [STAC_92HD83XXX_HP] = "hp", [STAC_HP_DV7_4000] = "hp-dv7-4000", }; @@ -1687,6 +1697,14 @@ static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { "DFI LanParty", STAC_92HD83XXX_REF), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba, "unknown Dell", STAC_DELL_S14), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x049a, + "Dell E5520", STAC_DELL_E5520M), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x049b, + "Dell E5420", STAC_DELL_E5520M), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x04eb, + "Dell E5420m", STAC_DELL_E5520M), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x04ec, + "Dell E5520m", STAC_DELL_E5520M), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x3600, "HP", STAC_92HD83XXX_HP), {} /* terminator */ From 582d8b9e2879512507b8a9409f85fb1394e1c53e Mon Sep 17 00:00:00 2001 From: Andrea Arcangeli Date: Fri, 18 Mar 2011 00:16:35 +0100 Subject: [PATCH 0103/2556] mm: PageBuddy and mapcount robustness commit ef2b4b95a63a1d23958dcb99eb2c6898eddc87d0 upstream. Change the _mapcount value indicating PageBuddy from -2 to -128 for more robusteness against page_mapcount() undeflows. Use reset_page_mapcount instead of __ClearPageBuddy in bad_page to ignore the previous retval of PageBuddy(). Signed-off-by: Andrea Arcangeli Reported-by: Hugh Dickins Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- include/linux/mm.h | 11 +++++++++-- mm/page_alloc.c | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index f6385fc17ad46..c67adb4ea5ebe 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -402,16 +402,23 @@ static inline void init_page_count(struct page *page) /* * PageBuddy() indicate that the page is free and in the buddy system * (see mm/page_alloc.c). + * + * PAGE_BUDDY_MAPCOUNT_VALUE must be <= -2 but better not too close to + * -2 so that an underflow of the page_mapcount() won't be mistaken + * for a genuine PAGE_BUDDY_MAPCOUNT_VALUE. -128 can be created very + * efficiently by most CPU architectures. */ +#define PAGE_BUDDY_MAPCOUNT_VALUE (-128) + static inline int PageBuddy(struct page *page) { - return atomic_read(&page->_mapcount) == -2; + return atomic_read(&page->_mapcount) == PAGE_BUDDY_MAPCOUNT_VALUE; } static inline void __SetPageBuddy(struct page *page) { VM_BUG_ON(atomic_read(&page->_mapcount) != -1); - atomic_set(&page->_mapcount, -2); + atomic_set(&page->_mapcount, PAGE_BUDDY_MAPCOUNT_VALUE); } static inline void __ClearPageBuddy(struct page *page) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index cdef1d4b4e474..28280375327f7 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -286,7 +286,7 @@ static void bad_page(struct page *page) /* Don't complain about poisoned pages */ if (PageHWPoison(page)) { - __ClearPageBuddy(page); + reset_page_mapcount(page); /* remove PageBuddy */ return; } @@ -317,7 +317,7 @@ static void bad_page(struct page *page) dump_stack(); out: /* Leave bad fields for debug, except PageBuddy could make trouble */ - __ClearPageBuddy(page); + reset_page_mapcount(page); /* remove PageBuddy */ add_taint(TAINT_BAD_PAGE); } From 358c9b3df4e0c7be10217dfeebdccf1125a1b217 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Sat, 26 Feb 2011 22:40:19 +0200 Subject: [PATCH 0104/2556] ext3: skip orphan cleanup on rocompat fs commit ce654b37f87980d95f339080e4c3bdb2370bdf22 upstream. Orphan cleanup is currently executed even if the file system has some number of unknown ROCOMPAT features, which deletes inodes and frees blocks, which could be very bad for some RO_COMPAT features. This patch skips the orphan cleanup if it contains readonly compatible features not known by this ext3 implementation, which would prevent the fs from being mounted (or remounted) readwrite. Signed-off-by: Amir Goldstein Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/ext3/super.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 85c8cc8f24732..0d62f29f213a1 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -1464,6 +1464,13 @@ static void ext3_orphan_cleanup (struct super_block * sb, return; } + /* Check if feature set allows readwrite operations */ + if (EXT3_HAS_RO_COMPAT_FEATURE(sb, ~EXT3_FEATURE_RO_COMPAT_SUPP)) { + ext3_msg(sb, KERN_INFO, "Skipping orphan cleanup due to " + "unknown ROCOMPAT features"); + return; + } + if (EXT3_SB(sb)->s_mount_state & EXT3_ERROR_FS) { if (es->s_last_orphan) jbd_debug(1, "Errors on filesystem, " From 96f8c528c9112a89b03928bd4ab5173ba35a4fd4 Mon Sep 17 00:00:00 2001 From: Sedat Dilek Date: Tue, 8 Mar 2011 22:39:24 +0100 Subject: [PATCH 0105/2556] x86: Fix binutils-2.21 symbol related build failures commit 2ae9d293b14d17f35eff624272cfecac7979a2ee upstream. [only 1/2 of the upstream commit was needed for stable - gkh] New binutils version 2.21.0.20110302-1 started checking that the symbol parameter to the .size directive matches the entry name's symbol parameter, unearthing two mismatches: AS arch/x86/kernel/acpi/wakeup_rm.o arch/x86/kernel/acpi/wakeup_rm.S: Assembler messages: arch/x86/kernel/acpi/wakeup_rm.S:12: Error: .size expression with symbol `wakeup_code_start' does not evaluate to a constant arch/x86/kernel/entry_32.S: Assembler messages: arch/x86/kernel/entry_32.S:1421: Error: .size expression with symbol `apf_page_fault' does not evaluate to a constant The problem was discovered while using Debian's binutils (2.21.0.20110302-1) and experimenting with binutils from upstream. Thanks Alexander and H.J. for the vital help. Signed-off-by: Sedat Dilek Cc: Alexander van Heukelum Cc: H.J. Lu Cc: Len Brown Cc: Pavel Machek Cc: Rafael J. Wysocki LKML-Reference: <1299620364-21644-1-git-send-email-sedat.dilek@gmail.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/entry_32.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index c8b4efad7ebb0..9ca3b0e343e5d 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S @@ -1413,7 +1413,7 @@ ENTRY(async_page_fault) CFI_ADJUST_CFA_OFFSET 4 jmp error_code CFI_ENDPROC -END(apf_page_fault) +END(async_page_fault) #endif /* From bd118a66bddf45265832b8185fa177b3d5fd0200 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Wed, 23 Mar 2011 16:43:11 -0700 Subject: [PATCH 0106/2556] sysctl: restrict write access to dmesg_restrict commit bfdc0b497faa82a0ba2f9dddcf109231dd519fcc upstream. When dmesg_restrict is set to 1 CAP_SYS_ADMIN is needed to read the kernel ring buffer. But a root user without CAP_SYS_ADMIN is able to reset dmesg_restrict to 0. This is an issue when e.g. LXC (Linux Containers) are used and complete user space is running without CAP_SYS_ADMIN. A unprivileged and jailed root user can bypass the dmesg_restrict protection. With this patch writing to dmesg_restrict is only allowed when root has CAP_SYS_ADMIN. Signed-off-by: Richard Weinberger Acked-by: Dan Rosenberg Acked-by: Serge E. Hallyn Cc: Eric Paris Cc: Kees Cook Cc: James Morris Cc: Eugene Teo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/sysctl.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 4eed0af5d1442..443fd2081a74a 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -169,6 +169,11 @@ static int proc_taint(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); #endif +#ifdef CONFIG_PRINTK +static int proc_dmesg_restrict(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); +#endif + #ifdef CONFIG_MAGIC_SYSRQ /* Note: sysrq code uses it's own private copy */ static int __sysrq_enabled = SYSRQ_DEFAULT_ENABLE; @@ -713,7 +718,7 @@ static struct ctl_table kern_table[] = { .data = &kptr_restrict, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec_minmax, + .proc_handler = proc_dmesg_restrict, .extra1 = &zero, .extra2 = &two, }, @@ -2397,6 +2402,17 @@ static int proc_taint(struct ctl_table *table, int write, return err; } +#ifdef CONFIG_PRINTK +static int proc_dmesg_restrict(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + if (write && !capable(CAP_SYS_ADMIN)) + return -EPERM; + + return proc_dointvec_minmax(table, write, buffer, lenp, ppos); +} +#endif + struct do_proc_dointvec_minmax_conv_param { int *min; int *max; From cf81eb6a73d27afd234e8072a11dad71c968953b Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Wed, 23 Mar 2011 16:42:50 -0700 Subject: [PATCH 0107/2556] procfs: fix /proc//maps heap check commit 0db0c01b53a1a421513f91573241aabafb87802a upstream. The current code fails to print the "[heap]" marking if the heap is split into multiple mappings. Fix the check so that the marking is displayed in all possible cases: 1. vma matches exactly the heap 2. the heap vma is merged e.g. with bss 3. the heap vma is splitted e.g. due to locked pages Test cases. In all cases, the process should have mapping(s) with [heap] marking: (1) vma matches exactly the heap #include #include #include int main (void) { if (sbrk(4096) != (void *)-1) { printf("check /proc/%d/maps\n", (int)getpid()); while (1) sleep(1); } return 0; } # ./test1 check /proc/553/maps [1] + Stopped ./test1 # cat /proc/553/maps | head -4 00008000-00009000 r-xp 00000000 01:00 3113640 /test1 00010000-00011000 rw-p 00000000 01:00 3113640 /test1 00011000-00012000 rw-p 00000000 00:00 0 [heap] 4006f000-40070000 rw-p 00000000 00:00 0 (2) the heap vma is merged #include #include #include char foo[4096] = "foo"; char bar[4096]; int main (void) { if (sbrk(4096) != (void *)-1) { printf("check /proc/%d/maps\n", (int)getpid()); while (1) sleep(1); } return 0; } # ./test2 check /proc/556/maps [2] + Stopped ./test2 # cat /proc/556/maps | head -4 00008000-00009000 r-xp 00000000 01:00 3116312 /test2 00010000-00012000 rw-p 00000000 01:00 3116312 /test2 00012000-00014000 rw-p 00000000 00:00 0 [heap] 4004a000-4004b000 rw-p 00000000 00:00 0 (3) the heap vma is splitted (this fails without the patch) #include #include #include #include int main (void) { if ((sbrk(4096) != (void *)-1) && !mlockall(MCL_FUTURE) && (sbrk(4096) != (void *)-1)) { printf("check /proc/%d/maps\n", (int)getpid()); while (1) sleep(1); } return 0; } # ./test3 check /proc/559/maps [1] + Stopped ./test3 # cat /proc/559/maps|head -4 00008000-00009000 r-xp 00000000 01:00 3119108 /test3 00010000-00011000 rw-p 00000000 01:00 3119108 /test3 00011000-00012000 rw-p 00000000 00:00 0 [heap] 00012000-00013000 rw-p 00000000 00:00 0 [heap] It looks like the bug has been there forever, and since it only results in some information missing from a procfile, it does not fulfil the -stable "critical issue" criteria. Signed-off-by: Aaro Koskinen Reviewed-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/proc/task_mmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 60b914860f815..f269ee673c8bf 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -249,8 +249,8 @@ static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma) const char *name = arch_vma_name(vma); if (!name) { if (mm) { - if (vma->vm_start <= mm->start_brk && - vma->vm_end >= mm->brk) { + if (vma->vm_start <= mm->brk && + vma->vm_end >= mm->start_brk) { name = "[heap]"; } else if (vma->vm_start <= mm->start_stack && vma->vm_end >= mm->start_stack) { From edccdb2bd8f5cbc029820f923e4cbb737a36753c Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 23 Mar 2011 16:42:53 -0700 Subject: [PATCH 0108/2556] proc: protect mm start_code/end_code in /proc/pid/stat commit 5883f57ca0008ffc93e09cbb9847a1928e50c6f3 upstream. While mm->start_stack was protected from cross-uid viewing (commit f83ce3e6b02d5 ("proc: avoid information leaks to non-privileged processes")), the start_code and end_code values were not. This would allow the text location of a PIE binary to leak, defeating ASLR. Note that the value "1" is used instead of "0" for a protected value since "ps", "killall", and likely other readers of /proc/pid/stat, take start_code of "0" to mean a kernel thread and will misbehave. Thanks to Brad Spengler for pointing this out. Addresses CVE-2011-0726 Signed-off-by: Kees Cook Cc: Alexey Dobriyan Cc: David Howells Cc: Eugene Teo Cc: Martin Schwidefsky Cc: Brad Spengler Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/proc/array.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/proc/array.c b/fs/proc/array.c index 7c99c1cf7e5c4..5e4f776b0917a 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -489,8 +489,8 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, vsize, mm ? get_mm_rss(mm) : 0, rsslim, - mm ? mm->start_code : 0, - mm ? mm->end_code : 0, + mm ? (permitted ? mm->start_code : 1) : 0, + mm ? (permitted ? mm->end_code : 1) : 0, (permitted && mm) ? mm->start_stack : 0, esp, eip, From 4676dbc1af6205dc136e0d5b2a5fe40d3c2c025d Mon Sep 17 00:00:00 2001 From: Henry Nestler Date: Sun, 20 Feb 2011 20:50:56 +0000 Subject: [PATCH 0109/2556] fbcon: Bugfix soft cursor detection in Tile Blitting commit d6244bc0ed0c52a795e6f4dcab3886daf3e74fac upstream. Use mask 0x10 for "soft cursor" detection on in function tile_cursor. (Tile Blitting Operation in framebuffer console). The old mask 0x01 for vc_cursor_type detects CUR_NONE, CUR_LOWER_THIRD and every second mode value as "software cursor". This hides the cursor for these modes (cursor.mode = 0). But, only CUR_NONE or "software cursor" should hide the cursor. See also 0x10 in functions add_softcursor, bit_cursor and cw_cursor. Signed-off-by: Henry Nestler Signed-off-by: Paul Mundt Signed-off-by: Greg Kroah-Hartman --- drivers/video/console/tileblit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/console/tileblit.c b/drivers/video/console/tileblit.c index 0056a41e5c35c..15e8e1a89c45d 100644 --- a/drivers/video/console/tileblit.c +++ b/drivers/video/console/tileblit.c @@ -83,7 +83,7 @@ static void tile_cursor(struct vc_data *vc, struct fb_info *info, int mode, int softback_lines, int fg, int bg) { struct fb_tilecursor cursor; - int use_sw = (vc->vc_cursor_type & 0x01); + int use_sw = (vc->vc_cursor_type & 0x10); cursor.sx = vc->vc_x; cursor.sy = vc->vc_y; From 197b4ba19cd79baf1213ed3f557e147f151a37f2 Mon Sep 17 00:00:00 2001 From: Mi Jinlong Date: Fri, 18 Feb 2011 09:08:31 +0800 Subject: [PATCH 0110/2556] nfsd41: modify the members value of nfsd4_op_flags commit 5ece3cafbd88d4da5c734e1810c4a2e6474b57b2 upstream. The members of nfsd4_op_flags, (ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS) equals to ALLOWED_AS_FIRST_OP, maybe that's not what we want. OP_PUTROOTFH with op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS, can't appears as the first operation with out SEQUENCE ops. This patch modify the wrong value of ALLOWED_WITHOUT_FH etc which was introduced by f9bb94c4. Reviewed-by: Benny Halevy Signed-off-by: Mi Jinlong Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4proc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index db52546143d12..5fcb1396a7e32 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -984,8 +984,8 @@ typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *, void *); enum nfsd4_op_flags { ALLOWED_WITHOUT_FH = 1 << 0, /* No current filehandle required */ - ALLOWED_ON_ABSENT_FS = 2 << 0, /* ops processed on absent fs */ - ALLOWED_AS_FIRST_OP = 3 << 0, /* ops reqired first in compound */ + ALLOWED_ON_ABSENT_FS = 1 << 1, /* ops processed on absent fs */ + ALLOWED_AS_FIRST_OP = 1 << 2, /* ops reqired first in compound */ }; struct nfsd4_operation { From fa70942de94f80025ef55d510192b697798390c6 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 2 Mar 2011 23:48:33 -0500 Subject: [PATCH 0111/2556] nfsd4: minor nfs4state.c reshuffling commit 529d7b2a7fa31e9f7d08bc790d232c3cbe64fa24 upstream. Minor cleanup in preparation for a bugfix--moving some code to avoid forward references, etc. No change in functionality. Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4state.c | 125 +++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 64 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 7b566ec14e183..50c157cfa8eac 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -316,64 +316,6 @@ static struct list_head unconf_id_hashtbl[CLIENT_HASH_SIZE]; static struct list_head client_lru; static struct list_head close_lru; -static void unhash_generic_stateid(struct nfs4_stateid *stp) -{ - list_del(&stp->st_hash); - list_del(&stp->st_perfile); - list_del(&stp->st_perstateowner); -} - -static void free_generic_stateid(struct nfs4_stateid *stp) -{ - put_nfs4_file(stp->st_file); - kmem_cache_free(stateid_slab, stp); -} - -static void release_lock_stateid(struct nfs4_stateid *stp) -{ - struct file *file; - - unhash_generic_stateid(stp); - file = find_any_file(stp->st_file); - if (file) - locks_remove_posix(file, (fl_owner_t)stp->st_stateowner); - free_generic_stateid(stp); -} - -static void unhash_lockowner(struct nfs4_stateowner *sop) -{ - struct nfs4_stateid *stp; - - list_del(&sop->so_idhash); - list_del(&sop->so_strhash); - list_del(&sop->so_perstateid); - while (!list_empty(&sop->so_stateids)) { - stp = list_first_entry(&sop->so_stateids, - struct nfs4_stateid, st_perstateowner); - release_lock_stateid(stp); - } -} - -static void release_lockowner(struct nfs4_stateowner *sop) -{ - unhash_lockowner(sop); - nfs4_put_stateowner(sop); -} - -static void -release_stateid_lockowners(struct nfs4_stateid *open_stp) -{ - struct nfs4_stateowner *lock_sop; - - while (!list_empty(&open_stp->st_lockowners)) { - lock_sop = list_entry(open_stp->st_lockowners.next, - struct nfs4_stateowner, so_perstateid); - /* list_del(&open_stp->st_lockowners); */ - BUG_ON(lock_sop->so_is_open_owner); - release_lockowner(lock_sop); - } -} - /* * We store the NONE, READ, WRITE, and BOTH bits separately in the * st_{access,deny}_bmap field of the stateid, in order to track not @@ -446,6 +388,64 @@ static int nfs4_access_bmap_to_omode(struct nfs4_stateid *stp) return nfs4_access_to_omode(access); } +static void unhash_generic_stateid(struct nfs4_stateid *stp) +{ + list_del(&stp->st_hash); + list_del(&stp->st_perfile); + list_del(&stp->st_perstateowner); +} + +static void free_generic_stateid(struct nfs4_stateid *stp) +{ + put_nfs4_file(stp->st_file); + kmem_cache_free(stateid_slab, stp); +} + +static void release_lock_stateid(struct nfs4_stateid *stp) +{ + struct file *file; + + unhash_generic_stateid(stp); + file = find_any_file(stp->st_file); + if (file) + locks_remove_posix(file, (fl_owner_t)stp->st_stateowner); + free_generic_stateid(stp); +} + +static void unhash_lockowner(struct nfs4_stateowner *sop) +{ + struct nfs4_stateid *stp; + + list_del(&sop->so_idhash); + list_del(&sop->so_strhash); + list_del(&sop->so_perstateid); + while (!list_empty(&sop->so_stateids)) { + stp = list_first_entry(&sop->so_stateids, + struct nfs4_stateid, st_perstateowner); + release_lock_stateid(stp); + } +} + +static void release_lockowner(struct nfs4_stateowner *sop) +{ + unhash_lockowner(sop); + nfs4_put_stateowner(sop); +} + +static void +release_stateid_lockowners(struct nfs4_stateid *open_stp) +{ + struct nfs4_stateowner *lock_sop; + + while (!list_empty(&open_stp->st_lockowners)) { + lock_sop = list_entry(open_stp->st_lockowners.next, + struct nfs4_stateowner, so_perstateid); + /* list_del(&open_stp->st_lockowners); */ + BUG_ON(lock_sop->so_is_open_owner); + release_lockowner(lock_sop); + } +} + static void release_open_stateid(struct nfs4_stateid *stp) { int oflag = nfs4_access_bmap_to_omode(stp); @@ -3765,7 +3765,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct file_lock conflock; __be32 status = 0; unsigned int strhashval; - unsigned int cmd; int err; dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n", @@ -3852,8 +3851,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, filp = find_readable_file(lock_stp->st_file); } file_lock.fl_type = F_RDLCK; - cmd = F_SETLK; - break; + break; case NFS4_WRITE_LT: case NFS4_WRITEW_LT: if (find_writeable_file(lock_stp->st_file)) { @@ -3861,8 +3859,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, filp = find_writeable_file(lock_stp->st_file); } file_lock.fl_type = F_WRLCK; - cmd = F_SETLK; - break; + break; default: status = nfserr_inval; goto out; @@ -3886,7 +3883,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, * Note: locks.c uses the BKL to protect the inode's lock list. */ - err = vfs_lock_file(filp, cmd, &file_lock, &conflock); + err = vfs_lock_file(filp, F_SETLK, &file_lock, &conflock); switch (-err) { case 0: /* success! */ update_stateid(&lock_stp->st_stateid); From 35e2bd6cbfd407bc64351eb1b82fc5fcabbfa684 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 2 Mar 2011 18:01:35 -0500 Subject: [PATCH 0112/2556] nfsd4: fix struct file leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 0997b173609b9229ece28941c118a2a9b278796e upstream. Make sure we properly reference count the struct files that a lock depends on, and release them when the lock stateid is released. This fixes a major leak of struct files when using locking over nfsv4. Reported-by: Rick Koshi Tested-by: Ivo Přikryl Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4state.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 50c157cfa8eac..f0e448a512c63 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -397,6 +397,9 @@ static void unhash_generic_stateid(struct nfs4_stateid *stp) static void free_generic_stateid(struct nfs4_stateid *stp) { + int oflag = nfs4_access_bmap_to_omode(stp); + + nfs4_file_put_access(stp->st_file, oflag); put_nfs4_file(stp->st_file); kmem_cache_free(stateid_slab, stp); } @@ -448,11 +451,8 @@ release_stateid_lockowners(struct nfs4_stateid *open_stp) static void release_open_stateid(struct nfs4_stateid *stp) { - int oflag = nfs4_access_bmap_to_omode(stp); - unhash_generic_stateid(stp); release_stateid_lockowners(stp); - nfs4_file_put_access(stp->st_file, oflag); free_generic_stateid(stp); } @@ -3735,6 +3735,7 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc stp->st_stateid.si_stateownerid = sop->so_id; stp->st_stateid.si_fileid = fp->fi_id; stp->st_stateid.si_generation = 0; + stp->st_access_bmap = 0; stp->st_deny_bmap = open_stp->st_deny_bmap; stp->st_openstp = open_stp; @@ -3749,6 +3750,17 @@ check_lock_length(u64 offset, u64 length) LOFF_OVERFLOW(offset, length))); } +static void get_lock_access(struct nfs4_stateid *lock_stp, u32 access) +{ + struct nfs4_file *fp = lock_stp->st_file; + int oflag = nfs4_access_to_omode(access); + + if (test_bit(access, &lock_stp->st_access_bmap)) + return; + nfs4_file_get_access(fp, oflag); + __set_bit(access, &lock_stp->st_access_bmap); +} + /* * LOCK operation */ @@ -3846,18 +3858,16 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, switch (lock->lk_type) { case NFS4_READ_LT: case NFS4_READW_LT: - if (find_readable_file(lock_stp->st_file)) { - nfs4_get_vfs_file(rqstp, fp, &cstate->current_fh, NFS4_SHARE_ACCESS_READ); - filp = find_readable_file(lock_stp->st_file); - } + filp = find_readable_file(lock_stp->st_file); + if (filp) + get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ); file_lock.fl_type = F_RDLCK; break; case NFS4_WRITE_LT: case NFS4_WRITEW_LT: - if (find_writeable_file(lock_stp->st_file)) { - nfs4_get_vfs_file(rqstp, fp, &cstate->current_fh, NFS4_SHARE_ACCESS_WRITE); - filp = find_writeable_file(lock_stp->st_file); - } + filp = find_writeable_file(lock_stp->st_file); + if (filp) + get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE); file_lock.fl_type = F_WRLCK; break; default: From c94fc399adb5206b7634567fc84df2d9c2e110c5 Mon Sep 17 00:00:00 2001 From: Mi Jinlong Date: Fri, 11 Mar 2011 12:13:55 +0800 Subject: [PATCH 0113/2556] nfsd: wrong index used in inner loop commit 5a02ab7c3c4580f94d13c683721039855b67cda6 upstream. We must not use dummy for index. After the first index, READ32(dummy) will change dummy!!!! Signed-off-by: Mi Jinlong [bfields@redhat.com: Trond points out READ_BUF alone is sufficient.] Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4xdr.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 615f0a9f06008..c6766af00d983 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1142,7 +1142,7 @@ nfsd4_decode_create_session(struct nfsd4_compoundargs *argp, u32 dummy; char *machine_name; - int i, j; + int i; int nr_secflavs; READ_BUF(16); @@ -1215,8 +1215,6 @@ nfsd4_decode_create_session(struct nfsd4_compoundargs *argp, READ_BUF(4); READ32(dummy); READ_BUF(dummy * 4); - for (j = 0; j < dummy; ++j) - READ32(dummy); break; case RPC_AUTH_GSS: dprintk("RPC_AUTH_GSS callback secflavor " @@ -1232,7 +1230,6 @@ nfsd4_decode_create_session(struct nfsd4_compoundargs *argp, READ_BUF(4); READ32(dummy); READ_BUF(dummy); - p += XDR_QUADLEN(dummy); break; default: dprintk("Illegal callback secflavor\n"); From fa957c169f0d20b9178d9a1b4eff55525d254d4d Mon Sep 17 00:00:00 2001 From: Stephan Lachowsky Date: Thu, 27 Jan 2011 23:04:33 -0300 Subject: [PATCH 0114/2556] uvcvideo: Fix uvc_fixup_video_ctrl() format search commit 38a66824d96de8aeeb915e6f46f0d3fe55828eb1 upstream. The scheme used to index format in uvc_fixup_video_ctrl() is not robust: format index is based on descriptor ordering, which does not necessarily match bFormatIndex ordering. Searching for first matching format will prevent uvc_fixup_video_ctrl() from using the wrong format/frame to make adjustments. Signed-off-by: Stephan Lachowsky Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/video/uvc/uvc_video.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index 5673d673504b8..545c0294813d9 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -89,15 +89,19 @@ int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl) { - struct uvc_format *format; + struct uvc_format *format = NULL; struct uvc_frame *frame = NULL; unsigned int i; - if (ctrl->bFormatIndex <= 0 || - ctrl->bFormatIndex > stream->nformats) - return; + for (i = 0; i < stream->nformats; ++i) { + if (stream->format[i].index == ctrl->bFormatIndex) { + format = &stream->format[i]; + break; + } + } - format = &stream->format[ctrl->bFormatIndex - 1]; + if (format == NULL) + return; for (i = 0; i < format->nframes; ++i) { if (format->frame[i].bFrameIndex == ctrl->bFrameIndex) { From 94e57b398cdb6c4c311da73c4c369eb86f3cad66 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 23 Feb 2011 11:19:17 -0300 Subject: [PATCH 0115/2556] uvcvideo: Fix descriptor parsing for video output devices commit 4093a5c4a3f59cba1a085bbf87b6ffdddc5a443d upstream. Commit 4057ac6ca9a77c4275b34b5925ab5c99557913b1 V4L/DVB (13505): uvcvideo: Refactor chain scan broke output terminals parsing. Fix it. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/video/uvc/uvc_driver.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index a1e9dfb52f698..6459b8cba2238 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -1264,6 +1264,14 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, break; + case UVC_OTT_VENDOR_SPECIFIC: + case UVC_OTT_DISPLAY: + case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" OT %d", entity->id); + + break; + case UVC_TT_STREAMING: if (UVC_ENTITY_IS_ITERM(entity)) { if (uvc_trace_param & UVC_TRACE_PROBE) From a65a0507b92ab78164cb5da23c9bad093d7ef2b7 Mon Sep 17 00:00:00 2001 From: Phil Edworthy Date: Fri, 18 Mar 2011 14:16:31 +0000 Subject: [PATCH 0116/2556] sh: Fix ptrace fpu state initialisation commit c49b6ecf0870e78fa40497cd8b142915c1d5c7c9 upstream. Commit 0ea820cf introduced the PTRACE_GETFPREGS/SETFPREGS cmds, but gdb-server still accesses the FPU state using the PTRACE_PEEKUSR/POKEUSR commands. In this case, xstate was not initialised. Signed-off-by: Phil Edworthy Signed-off-by: Paul Mundt Signed-off-by: Greg Kroah-Hartman --- arch/sh/kernel/ptrace_32.c | 6 ++++++ arch/sh/kernel/ptrace_64.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index 90a15d29feebd..b53664ef53e84 100644 --- a/arch/sh/kernel/ptrace_32.c +++ b/arch/sh/kernel/ptrace_32.c @@ -392,6 +392,9 @@ long arch_ptrace(struct task_struct *child, long request, tmp = 0; } else { unsigned long index; + ret = init_fpu(child); + if (ret) + break; index = addr - offsetof(struct user, fpu); tmp = ((unsigned long *)child->thread.xstate) [index >> 2]; @@ -423,6 +426,9 @@ long arch_ptrace(struct task_struct *child, long request, else if (addr >= offsetof(struct user, fpu) && addr < offsetof(struct user, u_fpvalid)) { unsigned long index; + ret = init_fpu(child); + if (ret) + break; index = addr - offsetof(struct user, fpu); set_stopped_child_used_math(child); ((unsigned long *)child->thread.xstate) diff --git a/arch/sh/kernel/ptrace_64.c b/arch/sh/kernel/ptrace_64.c index 4436eacddb153..c8f97649f354b 100644 --- a/arch/sh/kernel/ptrace_64.c +++ b/arch/sh/kernel/ptrace_64.c @@ -403,6 +403,9 @@ long arch_ptrace(struct task_struct *child, long request, else if ((addr >= offsetof(struct user, fpu)) && (addr < offsetof(struct user, u_fpvalid))) { unsigned long index; + ret = init_fpu(child); + if (ret) + break; index = addr - offsetof(struct user, fpu); tmp = get_fpu_long(child, index); } else if (addr == offsetof(struct user, u_fpvalid)) { @@ -442,6 +445,9 @@ long arch_ptrace(struct task_struct *child, long request, else if ((addr >= offsetof(struct user, fpu)) && (addr < offsetof(struct user, u_fpvalid))) { unsigned long index; + ret = init_fpu(child); + if (ret) + break; index = addr - offsetof(struct user, fpu); ret = put_fpu_long(child, index, data); } From e30d586e1ec3a54ab8f573aa47f64cd5b5d29029 Mon Sep 17 00:00:00 2001 From: David Engraf Date: Wed, 23 Mar 2011 11:35:42 +0000 Subject: [PATCH 0117/2556] sh: Fix ptrace hw_breakpoint handling commit fb7f045ace0624f1e59a7db8497e460bd54b1cbc upstream. Since commit 34d0b5af50a063cded842716633501b38ff815fb it is no longer possible to debug an application using singlestep. The old commit converted singlestep handling via ptrace to hw_breakpoints. The hw_breakpoint is disabled when an event is triggered and not re-enabled again. This patch re-enables the existing hw_breakpoint before the existing breakpoint is reused. Signed-off-by: David Engraf Signed-off-by: Paul Mundt Signed-off-by: Greg Kroah-Hartman --- arch/sh/kernel/ptrace_32.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index b53664ef53e84..2130ca674e9bd 100644 --- a/arch/sh/kernel/ptrace_32.c +++ b/arch/sh/kernel/ptrace_32.c @@ -101,6 +101,8 @@ static int set_single_step(struct task_struct *tsk, unsigned long addr) attr = bp->attr; attr.bp_addr = addr; + /* reenable breakpoint */ + attr.disabled = false; err = modify_user_hw_breakpoint(bp, &attr); if (unlikely(err)) return err; From 247a28d9277bfadf22c4b1afd9e28a24b7dbf912 Mon Sep 17 00:00:00 2001 From: Michal Sojka Date: Tue, 15 Mar 2011 16:41:47 +0100 Subject: [PATCH 0118/2556] USB: Do not pass negative length to snoop_urb() commit 9d02b42614149ebccf12c9c580601ed01bd83070 upstream. When `echo Y > /sys/module/usbcore/parameters/usbfs_snoop` and usb_control_msg() returns error, a lot of kernel memory is dumped to dmesg until unhandled kernel paging request occurs. Signed-off-by: Michal Sojka Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index a7131ad630f9a..37518dfdeb987 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -802,7 +802,7 @@ static int proc_control(struct dev_state *ps, void __user *arg) tbuf, ctrl.wLength, tmo); usb_lock_device(dev); snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE, - tbuf, i); + tbuf, max(i, 0)); if ((i > 0) && ctrl.wLength) { if (copy_to_user(ctrl.data, tbuf, i)) { free_page((unsigned long)tbuf); From 34c7eeaa6eb1118451e5ab474e89ef36f92be777 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 16 Mar 2011 10:57:15 -0400 Subject: [PATCH 0119/2556] ehci-hcd: Bug fix: don't set a QH's Halt bit commit b5a3b3d985493c173925907adfebf3edab236fe7 upstream. This patch (as1453) fixes a long-standing bug in the ehci-hcd driver. There is no need to set the Halt bit in the overlay region for an unlinked or blocked QH. Contrary to what the comment says, setting the Halt bit does not cause the QH to be patched later; that decision (made in qh_refresh()) depends only on whether the QH is currently pointing to a valid qTD. Likewise, setting the Halt bit does not prevent completions from activating the QH while it is "stopped"; they are prevented by the fact that qh_completions() temporarily changes qh->qh_state to QH_STATE_COMPLETING. On the other hand, there are circumstances in which the QH will be reactivated _without_ being patched; this happens after an URB beyond the head of the queue is unlinked. Setting the Halt bit will then cause the hardware to see the QH with both the Active and Halt bits set, an invalid combination that will prevent the queue from advancing and may even crash some controllers. Apparently the only reason this hasn't been reported before is that unlinking URBs from the middle of a running queue is quite uncommon. However Test 17, recently added to the usbtest driver, does exactly this, and it confirms the presence of the bug. In short, there is no reason to set the Halt bit for an unlinked or blocked QH, and there is a very good reason not to set it. Therefore the code that sets it is removed. Signed-off-by: Alan Stern Tested-by: Andiry Xu CC: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-q.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 233c288e3f931..5add8b5ddda88 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -315,7 +315,6 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) int stopped; unsigned count = 0; u8 state; - const __le32 halt = HALT_BIT(ehci); struct ehci_qh_hw *hw = qh->hw; if (unlikely (list_empty (&qh->qtd_list))) @@ -422,7 +421,6 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) && !(qtd->hw_alt_next & EHCI_LIST_END(ehci))) { stopped = 1; - goto halt; } /* stop scanning when we reach qtds the hc is using */ @@ -456,16 +454,6 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) */ ehci_clear_tt_buffer(ehci, qh, urb, token); } - - /* force halt for unlinked or blocked qh, so we'll - * patch the qh later and so that completions can't - * activate it while we "know" it's stopped. - */ - if ((halt & hw->hw_token) == 0) { -halt: - hw->hw_token |= halt; - wmb (); - } } /* unless we already know the urb's status, collect qtd status From 2829da91a8c9b67d6cc373bd1504147004a9822f Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 22 Mar 2011 14:43:37 -0400 Subject: [PATCH 0120/2556] usb: musb: blackfin: fix typo in platform driver name commit 417ddf86c8c499fada439b8ee89bb4c6f282ed6c upstream. The modularization of the Blackfin driver set the name to "musb-blackfin" in all the boards, but "musb-bfin" in the driver itself. Since the driver file name uses "blackfin", change the driver to "musb-blackfin". This is also easier as it's only one file to change. Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/blackfin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 9d49d1cd7ce23..5da9ef8ff3113 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -548,7 +548,7 @@ static struct dev_pm_ops bfin_pm_ops = { static struct platform_driver bfin_driver = { .remove = __exit_p(bfin_remove), .driver = { - .name = "musb-bfin", + .name = "musb-blackfin", .pm = DEV_PM_OPS, }, }; From b33656e8a5d018daddae0febd6bb4dee233dccaa Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Mon, 21 Mar 2011 14:06:31 -0400 Subject: [PATCH 0121/2556] usb: musb: blackfin: fix typo in new dev_pm_ops struct commit 8f7e7b87ec7c3202941ef2770bacd353ab93368b upstream. Signed-off-by: Bob Liu Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/blackfin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 5da9ef8ff3113..7843333b61993 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -540,7 +540,7 @@ static struct dev_pm_ops bfin_pm_ops = { .resume = bfin_resume, }; -#define DEV_PM_OPS &bfin_pm_op, +#define DEV_PM_OPS &bfin_pm_ops #else #define DEV_PM_OPS NULL #endif From c56bb079e6e775e10a64fe95cd121d2a6c113b66 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Mon, 21 Mar 2011 14:06:32 -0400 Subject: [PATCH 0122/2556] usb: musb: blackfin: fix typo in new bfin_musb_vbus_status func commit 45567c28d29a8766a67c53f898d502aef71b7ef0 upstream. The common code has a "get" in the middle, but each implementation does not have it. Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/blackfin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 7843333b61993..52312e8af213a 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -322,7 +322,7 @@ static void bfin_musb_try_idle(struct musb *musb, unsigned long timeout) mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); } -static int bfin_musb_get_vbus_status(struct musb *musb) +static int bfin_musb_vbus_status(struct musb *musb) { return 0; } From 0d84e0118b1273805ed7c37eba896be532066498 Mon Sep 17 00:00:00 2001 From: Peter Holik Date: Fri, 18 Mar 2011 18:47:44 +0100 Subject: [PATCH 0123/2556] USB: uss720 fixup refcount position commit adaa3c6342b249548ea830fe8e02aa5b45be8688 upstream. My testprog do a lot of bitbang - after hours i got following warning and my machine lockups: WARNING: at /build/buildd/linux-2.6.38/lib/kref.c:34 After debugging uss720 driver i discovered that the completion callback was called before usb_submit_urb returns. The callback frees the request structure that is krefed on return by usb_submit_urb. Signed-off-by: Peter Holik Acked-by: Thomas Sailer Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/uss720.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index f7a2057380321..8b1d94a769140 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -177,12 +177,11 @@ static struct uss720_async_request *submit_async_request(struct parport_uss720_p spin_lock_irqsave(&priv->asynclock, flags); list_add_tail(&rq->asynclist, &priv->asynclist); spin_unlock_irqrestore(&priv->asynclock, flags); + kref_get(&rq->ref_count); ret = usb_submit_urb(rq->urb, mem_flags); - if (!ret) { - kref_get(&rq->ref_count); + if (!ret) return rq; - } - kref_put(&rq->ref_count, destroy_async); + destroy_async(&rq->ref_count); err("submit_async_request submit_urb failed with %d", ret); return NULL; } From b2c85b73dbbe02deabb845a0c495956a01670a37 Mon Sep 17 00:00:00 2001 From: Robert Lukassen Date: Wed, 16 Mar 2011 12:13:34 +0100 Subject: [PATCH 0124/2556] USB: Fix 'bad dma' problem on WDM device disconnect commit 878b753e32ca765cd346a5d3038d630178ec78ff upstream. In the WDM class driver a disconnect event leads to calls to usb_free_coherent to put back two USB DMA buffers allocated earlier. The call to usb_free_coherent uses a different size parameter (desc->wMaxCommand) than the corresponding call to usb_alloc_coherent (desc->bMaxPacketSize0). When a disconnect event occurs, this leads to 'bad dma' complaints from usb core because the USB DMA buffer is being pushed back to the 'buffer-2048' pool from which it has not been allocated. This patch against the most recent linux-2.6 kernel ensures that the parameters used by usb_alloc_coherent & usb_free_coherent calls in cdc-wdm.c match. Signed-off-by: Robert Lukassen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-wdm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 47085e5879abb..a97c018dd4198 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -281,7 +281,7 @@ static void cleanup(struct wdm_device *desc) desc->sbuf, desc->validity->transfer_dma); usb_free_coherent(interface_to_usbdev(desc->intf), - desc->wMaxCommand, + desc->bMaxPacketSize0, desc->inbuf, desc->response->transfer_dma); kfree(desc->orq); From 4f798939ccff5624f0d1976093e1282692687e8e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:09 +0100 Subject: [PATCH 0125/2556] USB: cdc-acm: fix memory corruption / panic commit 23b80550e2aa61d0ba3af98b831b9195be0db9ee upstream. Prevent read urbs from being resubmitted from tasklet after port close. The receive tasklet was not disabled on port close, which could lead to corruption of receive lists on consecutive port open. In particular, read urbs could be re-submitted before port open, added to free list in open, and then added a second time to the free list in the completion handler. cdc-acm.c: Entering acm_tty_open. cdc-acm.c: acm_control_msg: rq: 0x22 val: 0x3 len: 0x0 result: 0 cdc-acm.c: Entering acm_rx_tasklet cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da280, rcv 0xf57fbc24, buf 0xf57fbd64 cdc-acm.c: set line: 115200 0 0 8 cdc-acm.c: acm_control_msg: rq: 0x20 val: 0x0 len: 0x7 result: 7 cdc-acm.c: acm_tty_close cdc-acm.c: acm_port_down cdc-acm.c: acm_control_msg: rq: 0x22 val: 0x0 len: 0x0 result: 0 cdc-acm.c: acm_ctrl_irq - urb shutting down with status: -2 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da300, rcv 0xf57fbc10, buf 0xf57fbd50 cdc-acm.c: Entering acm_read_bulk with status -2 cdc_acm 4-1:1.1: Aborting, acm not ready cdc-acm.c: Entering acm_read_bulk with status -2 cdc_acm 4-1:1.1: Aborting, acm not ready cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da380, rcv 0xf57fbbfc, buf 0xf57fbd3c cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da400, rcv 0xf57fbbe8, buf 0xf57fbd28 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da480, rcv 0xf57fbbd4, buf 0xf57fbd14 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da900, rcv 0xf57fbbc0, buf 0xf57fbd00 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da980, rcv 0xf57fbbac, buf 0xf57fbcec cdc-acm.c: acm_rx_tasklet: sending urb 0xf50daa00, rcv 0xf57fbb98, buf 0xf57fbcd8 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50daa80, rcv 0xf57fbb84, buf 0xf57fbcc4 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50dab00, rcv 0xf57fbb70, buf 0xf57fbcb0 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50dab80, rcv 0xf57fbb5c, buf 0xf57fbc9c cdc-acm.c: acm_rx_tasklet: sending urb 0xf50dac00, rcv 0xf57fbb48, buf 0xf57fbc88 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50dac80, rcv 0xf57fbb34, buf 0xf57fbc74 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50dad00, rcv 0xf57fbb20, buf 0xf57fbc60 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50dad80, rcv 0xf57fbb0c, buf 0xf57fbc4c cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da880, rcv 0xf57fbaf8, buf 0xf57fbc38 cdc-acm.c: Entering acm_tty_open. cdc-acm.c: acm_control_msg: rq: 0x22 val: 0x3 len: 0x0 result: 0 cdc-acm.c: Entering acm_rx_tasklet cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da280, rcv 0xf57fbc24, buf 0xf57fbd64 cdc-acm.c: Entering acm_tty_write to write 3 bytes, cdc-acm.c: Get 3 bytes... cdc-acm.c: acm_write_start susp_count: 0 cdc-acm.c: Entering acm_read_bulk with status 0 ------------[ cut here ]------------ WARNING: at /home/johan/src/linux/linux-2.6/lib/list_debug.c:57 list_del+0x10c/0x120() Hardware name: Vostro 1520 list_del corruption. next->prev should be f57fbc10, but was f57fbaf8 Modules linked in: cdc_acm Pid: 3, comm: ksoftirqd/0 Not tainted 2.6.37+ #39 Call Trace: [] warn_slowpath_common+0x72/0xa0 [] ? list_del+0x10c/0x120 [] ? list_del+0x10c/0x120 [] warn_slowpath_fmt+0x33/0x40 [] list_del+0x10c/0x120 [] acm_rx_tasklet+0xef/0x3e0 [cdc_acm] [] ? net_rps_action_and_irq_enable+0x6d/0x80 [] tasklet_action+0xe6/0x140 [] __do_softirq+0xaf/0x210 [] ? __do_softirq+0x0/0x210 [] ? run_ksoftirqd+0x8a/0x1c0 [] ? run_ksoftirqd+0x0/0x1c0 [] ? kthread+0x74/0x80 [] ? kthread+0x0/0x80 [] ? kernel_thread_helper+0x6/0x10 ---[ end trace efd9a11434f0082e ]--- ------------[ cut here ]------------ WARNING: at /home/johan/src/linux/linux-2.6/lib/list_debug.c:57 list_del+0x10c/0x120() Hardware name: Vostro 1520 list_del corruption. next->prev should be f57fbd50, but was f57fbdb0 Modules linked in: cdc_acm Pid: 3, comm: ksoftirqd/0 Tainted: G W 2.6.37+ #39 Call Trace: [] warn_slowpath_common+0x72/0xa0 [] ? list_del+0x10c/0x120 [] ? list_del+0x10c/0x120 [] warn_slowpath_fmt+0x33/0x40 [] list_del+0x10c/0x120 [] acm_rx_tasklet+0x106/0x3e0 [cdc_acm] [] ? net_rps_action_and_irq_enable+0x6d/0x80 [] tasklet_action+0xe6/0x140 [] __do_softirq+0xaf/0x210 [] ? __do_softirq+0x0/0x210 [] ? run_ksoftirqd+0x8a/0x1c0 [] ? run_ksoftirqd+0x0/0x1c0 [] ? kthread+0x74/0x80 [] ? kthread+0x0/0x80 [] ? kernel_thread_helper+0x6/0x10 ---[ end trace efd9a11434f0082f ]--- cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da300, rcv 0xf57fbc10, buf 0xf57fbd50 cdc-acm.c: disconnected from network cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da380, rcv 0xf57fbbfc, buf 0xf57fbd3c cdc-acm.c: Entering acm_rx_tasklet ------------[ cut here ]------------ WARNING: at /home/johan/src/linux/linux-2.6/lib/list_debug.c:48 list_del+0xd5/0x120() Hardware name: Vostro 1520 list_del corruption, next is LIST_POISON1 (00100100) Modules linked in: cdc_acm Pid: 3, comm: ksoftirqd/0 Tainted: G W 2.6.37+ #39 Call Trace: [] warn_slowpath_common+0x72/0xa0 [] ? list_del+0xd5/0x120 [] ? list_del+0xd5/0x120 [] warn_slowpath_fmt+0x33/0x40 [] list_del+0xd5/0x120 [] acm_rx_tasklet+0x2dc/0x3e0 [cdc_acm] [] ? trace_hardirqs_on+0xb/0x10 [] ? tasklet_action+0x60/0x140 [] tasklet_action+0xe6/0x140 [] __do_softirq+0xaf/0x210 [] ? __do_softirq+0x0/0x210 [] ? run_ksoftirqd+0x8a/0x1c0 [] ? run_ksoftirqd+0x0/0x1c0 [] ? kthread+0x74/0x80 [] ? kthread+0x0/0x80 [] ? kernel_thread_helper+0x6/0x10 ---[ end trace efd9a11434f00830 ]--- BUG: unable to handle kernel paging request at 00200200 IP: [] list_del+0x1d/0x120 *pde = 00000000 Oops: 0000 [#1] PREEMPT SMP last sysfs file: /sys/devices/pci0000:00/0000:00:1a.1/usb4/4-1/4-1:1.0/tty/ttyACM0/uevent Modules linked in: cdc_acm Pid: 3, comm: ksoftirqd/0 Tainted: G W 2.6.37+ #39 0T816J/Vostro 1520 EIP: 0060:[] EFLAGS: 00010046 CPU: 0 EIP is at list_del+0x1d/0x120 EAX: f57fbd3c EBX: f57fb800 ECX: ffff8000 EDX: 00200200 ESI: f57fbe90 EDI: f57fbd3c EBP: f600bf54 ESP: f600bf3c DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 Process ksoftirqd/0 (pid: 3, ti=f600a000 task=f60791c0 task.ti=f6082000) Stack: c1527e84 00000030 c1527e54 00100100 f57fb800 f57fbd3c f600bf98 f8051fac f8053104 f8052b94 f600bf6c c106dbab f600bf80 00000286 f60791c0 c1042b30 f57fbda8 f57f5800 f57fbdb0 f57fbd80 f57fbe7c c1656b04 00000000 f600bfb0 Call Trace: [] ? acm_rx_tasklet+0x2dc/0x3e0 [cdc_acm] [] ? trace_hardirqs_on+0xb/0x10 [] ? tasklet_action+0x60/0x140 [] ? tasklet_action+0xe6/0x140 [] ? __do_softirq+0xaf/0x210 [] ? __do_softirq+0x0/0x210 [] ? run_ksoftirqd+0x8a/0x1c0 [] ? run_ksoftirqd+0x0/0x1c0 [] ? kthread+0x74/0x80 [] ? kthread+0x0/0x80 [] ? kernel_thread_helper+0x6/0x10 Code: ff 48 14 e9 57 ff ff ff 90 90 90 90 90 90 55 89 e5 83 ec 18 81 38 00 01 10 00 0f 84 9c 00 00 00 8b 50 04 81 fa 00 02 20 00 74 33 <8b> 12 39 d0 75 5c 8b 10 8b 4a 04 39 c8 0f 85 b5 00 00 00 8b 48 EIP: [] list_del+0x1d/0x120 SS:ESP 0068:f600bf3c CR2: 0000000000200200 ---[ end trace efd9a11434f00831 ]--- Kernel panic - not syncing: Fatal exception in interrupt Pid: 3, comm: ksoftirqd/0 Tainted: G D W 2.6.37+ #39 Call Trace: [] ? printk+0x1d/0x24 [] panic+0x66/0x15c [] oops_end+0x8f/0x90 [] no_context+0xc6/0x160 [] __bad_area_nosemaphore+0x98/0x140 [] ? release_console_sem+0x1d8/0x210 [] bad_area_nosemaphore+0x17/0x20 [] do_page_fault+0x279/0x420 [] ? show_trace+0x1f/0x30 [] ? printk+0x1d/0x24 [] ? do_page_fault+0x0/0x420 [] error_code+0x5f/0x64 [] ? select_task_rq_fair+0x37b/0x6a0 [] ? do_page_fault+0x0/0x420 [] ? list_del+0x1d/0x120 [] acm_rx_tasklet+0x2dc/0x3e0 [cdc_acm] [] ? trace_hardirqs_on+0xb/0x10 [] ? tasklet_action+0x60/0x140 [] tasklet_action+0xe6/0x140 [] __do_softirq+0xaf/0x210 [] ? __do_softirq+0x0/0x210 [] ? run_ksoftirqd+0x8a/0x1c0 [] ? run_ksoftirqd+0x0/0x1c0 [] ? kthread+0x74/0x80 [] ? kthread+0x0/0x80 [] ? kernel_thread_helper+0x6/0x10 panic occurred, switching back to text console ------------[ cut here ]------------ Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 4ab49d4eebf4a..805916675fbc5 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -646,8 +646,10 @@ static void acm_port_down(struct acm *acm) usb_kill_urb(acm->ctrlurb); for (i = 0; i < ACM_NW; i++) usb_kill_urb(acm->wb[i].urb); + tasklet_disable(&acm->urb_task); for (i = 0; i < nr; i++) usb_kill_urb(acm->ru[i].urb); + tasklet_enable(&acm->urb_task); acm->control->needs_remote_wakeup = 0; usb_autopm_put_interface(acm->control); } From c2bf2aa8c59305072aff51fbaa011079f9062c90 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:10 +0100 Subject: [PATCH 0126/2556] USB: cdc-acm: fix potential null-pointer dereference commit 15e5bee33ffc11d0e5c6f819a65e7881c5c407be upstream. Must check return value of tty_port_tty_get. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 805916675fbc5..fcdd41e0429bb 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -533,6 +533,8 @@ static void acm_softint(struct work_struct *work) if (!ACM_READY(acm)) return; tty = tty_port_tty_get(&acm->port); + if (!tty) + return; tty_wakeup(tty); tty_kref_put(tty); } From 98f637b2ec791ddec58d90f9706feac2c5011ed8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:11 +0100 Subject: [PATCH 0127/2556] USB: cdc-acm: fix potential null-pointer dereference on disconnect commit 7e7797e7f6f7bfab73fca02c65e40eaa5bb9000c upstream. Fix potential null-pointer exception on disconnect introduced by commit 11ea859d64b69a747d6b060b9ed1520eab1161fe (USB: additional power savings for cdc-acm devices that support remote wakeup). Only access acm->dev after making sure it is non-null in control urb completion handler. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index fcdd41e0429bb..30bb8d053c2ee 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -297,6 +297,8 @@ static void acm_ctrl_irq(struct urb *urb) if (!ACM_READY(acm)) goto exit; + usb_mark_last_busy(acm->dev); + data = (unsigned char *)(dr + 1); switch (dr->bNotificationType) { case USB_CDC_NOTIFY_NETWORK_CONNECTION: @@ -336,7 +338,6 @@ static void acm_ctrl_irq(struct urb *urb) break; } exit: - usb_mark_last_busy(acm->dev); retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with " From 305c88ab06302e186aba8376a2b5c579a5c92291 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 18 Mar 2011 08:29:36 -0400 Subject: [PATCH 0128/2556] fix deadlock in pivot_root() commit 27cb1572e3e6bb1f8cf6bb3d74c914a87b131792 upstream. Don't hold vfsmount_lock over the loop traversing ->mnt_parent; do check_mnt(new.mnt) under namespace_sem instead; combined with namespace_sem held over all that code it'll guarantee the stability of ->mnt_parent chain all the way to the root. Doing check_mnt() outside of namespace_sem in case of pivot_root() is wrong anyway. Signed-off-by: Al Viro Signed-off-by: Greg Kroah-Hartman --- fs/namespace.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index d1edf26025dcb..445534be02432 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2469,9 +2469,6 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, error = user_path_dir(new_root, &new); if (error) goto out0; - error = -EINVAL; - if (!check_mnt(new.mnt)) - goto out1; error = user_path_dir(put_old, &old); if (error) @@ -2491,7 +2488,7 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, IS_MNT_SHARED(new.mnt->mnt_parent) || IS_MNT_SHARED(root.mnt->mnt_parent)) goto out2; - if (!check_mnt(root.mnt)) + if (!check_mnt(root.mnt) || !check_mnt(new.mnt)) goto out2; error = -ENOENT; if (cant_mount(old.dentry)) @@ -2515,19 +2512,19 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, goto out2; /* not attached */ /* make sure we can reach put_old from new_root */ tmp = old.mnt; - br_write_lock(vfsmount_lock); if (tmp != new.mnt) { for (;;) { if (tmp->mnt_parent == tmp) - goto out3; /* already mounted on put_old */ + goto out2; /* already mounted on put_old */ if (tmp->mnt_parent == new.mnt) break; tmp = tmp->mnt_parent; } if (!is_subdir(tmp->mnt_mountpoint, new.dentry)) - goto out3; + goto out2; } else if (!is_subdir(old.dentry, new.dentry)) - goto out3; + goto out2; + br_write_lock(vfsmount_lock); detach_mnt(new.mnt, &parent_path); detach_mnt(root.mnt, &root_parent); /* mount old root on put_old */ @@ -2550,9 +2547,6 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, path_put(&new); out0: return error; -out3: - br_write_unlock(vfsmount_lock); - goto out2; } static void __init init_mount_tree(void) From 857754c982ffd7af7683cb5d39288db65c990bad Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 17 Mar 2011 11:13:12 +0100 Subject: [PATCH 0129/2556] fs: assign sb->s_bdi to default_backing_dev_info if the bdi is going away commit 95f28604a65b1c40b6c6cd95e58439cd7ded3add upstream. We don't have proper reference counting for this yet, so we run into cases where the device is pulled and we OOPS on flushing the fs data. This happens even though the dirty inodes have already been migrated to the default_backing_dev_info. Reported-by: Torsten Hilbrich Tested-by: Torsten Hilbrich Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- fs/super.c | 2 ++ fs/sync.c | 4 ++-- mm/backing-dev.c | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/super.c b/fs/super.c index 7e9dd4cc2c011..0d89e93f654e1 100644 --- a/fs/super.c +++ b/fs/super.c @@ -71,6 +71,7 @@ static struct super_block *alloc_super(struct file_system_type *type) #else INIT_LIST_HEAD(&s->s_files); #endif + s->s_bdi = &default_backing_dev_info; INIT_LIST_HEAD(&s->s_instances); INIT_HLIST_BL_HEAD(&s->s_anon); INIT_LIST_HEAD(&s->s_inodes); @@ -1003,6 +1004,7 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void } BUG_ON(!mnt->mnt_sb); WARN_ON(!mnt->mnt_sb->s_bdi); + WARN_ON(mnt->mnt_sb->s_bdi == &default_backing_dev_info); mnt->mnt_sb->s_flags |= MS_BORN; error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata); diff --git a/fs/sync.c b/fs/sync.c index ba76b9623e7e8..412dc89163d31 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -33,7 +33,7 @@ static int __sync_filesystem(struct super_block *sb, int wait) * This should be safe, as we require bdi backing to actually * write out data in the first place */ - if (!sb->s_bdi || sb->s_bdi == &noop_backing_dev_info) + if (sb->s_bdi == &noop_backing_dev_info) return 0; if (sb->s_qcop && sb->s_qcop->quota_sync) @@ -79,7 +79,7 @@ EXPORT_SYMBOL_GPL(sync_filesystem); static void sync_one_sb(struct super_block *sb, void *arg) { - if (!(sb->s_flags & MS_RDONLY) && sb->s_bdi) + if (!(sb->s_flags & MS_RDONLY)) __sync_filesystem(sb, *(int *)arg); } /* diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 027100d30227f..8e4ed884f198b 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -604,7 +604,7 @@ static void bdi_prune_sb(struct backing_dev_info *bdi) spin_lock(&sb_lock); list_for_each_entry(sb, &super_blocks, s_list) { if (sb->s_bdi == bdi) - sb->s_bdi = NULL; + sb->s_bdi = &default_backing_dev_info; } spin_unlock(&sb_lock); } From ff518ea26654e05d325d996f6e3a7f5f569cc2d5 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 18 Feb 2011 11:30:30 +0000 Subject: [PATCH 0130/2556] x86: Cleanup highmap after brk is concluded commit e5f15b45ddf3afa2bbbb10c7ea34fb32b6de0a0e upstream. Now cleanup_highmap actually is in two steps: one is early in head64.c and only clears above _end; a second one is in init_memory_mapping() and tries to clean from _brk_end to _end. It should check if those boundaries are PMD_SIZE aligned but currently does not. Also init_memory_mapping() is called several times for numa or memory hotplug, so we really should not handle initial kernel mappings there. This patch moves cleanup_highmap() down after _brk_end is settled so we can do everything in one step. Also we honor max_pfn_mapped in the implementation of cleanup_highmap. Signed-off-by: Yinghai Lu Signed-off-by: Stefano Stabellini LKML-Reference: Signed-off-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/head64.c | 3 --- arch/x86/kernel/setup.c | 5 +++++ arch/x86/mm/init.c | 19 ------------------- arch/x86/mm/init_64.c | 11 ++++++----- 4 files changed, 11 insertions(+), 27 deletions(-) diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index 2d2673c28aff2..5655c2272adb8 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c @@ -77,9 +77,6 @@ void __init x86_64_start_kernel(char * real_mode_data) /* Make NULL pointers segfault */ zap_identity_mappings(); - /* Cleanup the over mapped high alias */ - cleanup_highmap(); - max_pfn_mapped = KERNEL_IMAGE_SIZE >> PAGE_SHIFT; for (i = 0; i < NUM_EXCEPTION_VECTORS; i++) { diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index d3cfe26c0252a..e543fe9311e4a 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -297,6 +297,9 @@ static void __init init_gbpages(void) static inline void init_gbpages(void) { } +static void __init cleanup_highmap(void) +{ +} #endif static void __init reserve_brk(void) @@ -922,6 +925,8 @@ void __init setup_arch(char **cmdline_p) */ reserve_brk(); + cleanup_highmap(); + memblock.current_limit = get_max_mapped(); memblock_x86_fill(); diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 947f42abe820e..f13ff3a226736 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -279,25 +279,6 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, load_cr3(swapper_pg_dir); #endif -#ifdef CONFIG_X86_64 - if (!after_bootmem && !start) { - pud_t *pud; - pmd_t *pmd; - - mmu_cr4_features = read_cr4(); - - /* - * _brk_end cannot change anymore, but it and _end may be - * located on different 2M pages. cleanup_highmap(), however, - * can only consider _end when it runs, so destroy any - * mappings beyond _brk_end here. - */ - pud = pud_offset(pgd_offset_k(_brk_end), _brk_end); - pmd = pmd_offset(pud, _brk_end - 1); - while (++pmd <= pmd_offset(pud, (unsigned long)_end - 1)) - pmd_clear(pmd); - } -#endif __flush_tlb_all(); if (!after_bootmem && e820_table_end > e820_table_start) diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index c14a5422e1522..68f9921ae9c16 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -51,6 +51,7 @@ #include #include #include +#include static int __init parse_direct_gbpages_off(char *arg) { @@ -293,18 +294,18 @@ void __init init_extra_mapping_uc(unsigned long phys, unsigned long size) * to the compile time generated pmds. This results in invalid pmds up * to the point where we hit the physaddr 0 mapping. * - * We limit the mappings to the region from _text to _end. _end is - * rounded up to the 2MB boundary. This catches the invalid pmds as + * We limit the mappings to the region from _text to _brk_end. _brk_end + * is rounded up to the 2MB boundary. This catches the invalid pmds as * well, as they are located before _text: */ void __init cleanup_highmap(void) { unsigned long vaddr = __START_KERNEL_map; - unsigned long end = roundup((unsigned long)_end, PMD_SIZE) - 1; + unsigned long vaddr_end = __START_KERNEL_map + (max_pfn_mapped << PAGE_SHIFT); + unsigned long end = roundup((unsigned long)_brk_end, PMD_SIZE) - 1; pmd_t *pmd = level2_kernel_pgt; - pmd_t *last_pmd = pmd + PTRS_PER_PMD; - for (; pmd < last_pmd; pmd++, vaddr += PMD_SIZE) { + for (; vaddr + PMD_SIZE - 1 < vaddr_end; pmd++, vaddr += PMD_SIZE) { if (pmd_none(*pmd)) continue; if (vaddr < (unsigned long) _text || vaddr > end) From de3da06e17ed82edfefef6cca8116548828f45b4 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 8 Feb 2011 13:55:21 +1000 Subject: [PATCH 0131/2556] drm: check for modesetting on modeset ioctls commit fb3b06c8a1fd1a80298f13b738ab38ef8c73baff upstream. Noticed this while working on some other things, helps if we check for modeset enabled on modesetting ioctls. Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/drm_crtc.c | 51 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 654faa803dcbc..6a5371b8fe9af 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1073,6 +1073,9 @@ int drm_mode_getresources(struct drm_device *dev, void *data, uint32_t __user *encoder_id; struct drm_mode_group *mode_group; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); /* @@ -1244,6 +1247,9 @@ int drm_mode_getcrtc(struct drm_device *dev, struct drm_mode_object *obj; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, crtc_resp->crtc_id, @@ -1312,6 +1318,9 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, uint64_t __user *prop_values; uint32_t __user *encoder_ptr; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id); @@ -1431,6 +1440,9 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, struct drm_encoder *encoder; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, enc_resp->encoder_id, DRM_MODE_OBJECT_ENCODER); @@ -1486,6 +1498,9 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, int ret = 0; int i; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, crtc_req->crtc_id, DRM_MODE_OBJECT_CRTC); @@ -1603,6 +1618,9 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, struct drm_crtc *crtc; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + if (!req->flags) { DRM_ERROR("no operation set\n"); return -EINVAL; @@ -1667,6 +1685,9 @@ int drm_mode_addfb(struct drm_device *dev, struct drm_framebuffer *fb; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + if ((config->min_width > r->width) || (r->width > config->max_width)) { DRM_ERROR("mode new framebuffer width not within limits\n"); return -EINVAL; @@ -1724,6 +1745,9 @@ int drm_mode_rmfb(struct drm_device *dev, int ret = 0; int found = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); /* TODO check that we realy get a framebuffer back. */ @@ -1780,6 +1804,9 @@ int drm_mode_getfb(struct drm_device *dev, struct drm_framebuffer *fb; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { @@ -1813,6 +1840,9 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, int num_clips; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { @@ -1996,6 +2026,9 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev, struct drm_mode_modeinfo *umode = &mode_cmd->mode; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); @@ -2042,6 +2075,9 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev, struct drm_mode_modeinfo *umode = &mode_cmd->mode; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); @@ -2211,6 +2247,9 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, uint64_t __user *values_ptr; uint32_t __user *blob_length_ptr; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); if (!obj) { @@ -2333,6 +2372,9 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, int ret = 0; void *blob_ptr; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB); if (!obj) { @@ -2393,6 +2435,9 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev, int ret = -EINVAL; int i; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); @@ -2509,6 +2554,9 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, int size; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { @@ -2560,6 +2608,9 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, int size; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { From d449b2a924941d023fb9dc1ad397c5b4b740aba4 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 14 Mar 2011 15:11:24 +0000 Subject: [PATCH 0132/2556] drm/i915: Disable pagefaults along execbuffer relocation fast path commit d4aeee776017b6da6dcd12f453cd82a3c951a0dc upstream. Along the fast path for relocation handling, we attempt to copy directly from the user data structures whilst holding our mutex. This causes lockdep to warn about circular lock dependencies if we need to pagefault the user pages. [Since when handling a page fault on a mmapped bo, we need to acquire the struct mutex whilst already holding the mm semaphore, it is then verboten to acquire the mm semaphore when already holding the struct mutex. The likelihood of the user passing in the relocations contained in a GTT mmaped bo is low, but conceivable for extreme pathology.] In order to force the mm to return EFAULT rather than handle the pagefault, we therefore need to disable pagefaults across the relocation fast path. Signed-off-by: Chris Wilson Reviewed-by: Daniel Vetter Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 50ab1614571c7..ded73a6007a8f 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -388,6 +388,10 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, uint32_t __iomem *reloc_entry; void __iomem *reloc_page; + /* We can't wait for rendering with pagefaults disabled */ + if (obj->active && in_atomic()) + return -EFAULT; + ret = i915_gem_object_set_to_gtt_domain(obj, 1); if (ret) return ret; @@ -461,15 +465,24 @@ i915_gem_execbuffer_relocate(struct drm_device *dev, struct list_head *objects) { struct drm_i915_gem_object *obj; - int ret; - + int ret = 0; + + /* This is the fast path and we cannot handle a pagefault whilst + * holding the struct mutex lest the user pass in the relocations + * contained within a mmaped bo. For in such a case we, the page + * fault handler would call i915_gem_fault() and we would try to + * acquire the struct mutex again. Obviously this is bad and so + * lockdep complains vehemently. + */ + pagefault_disable(); list_for_each_entry(obj, objects, exec_list) { ret = i915_gem_execbuffer_relocate_object(obj, eb); if (ret) - return ret; + break; } + pagefault_enable(); - return 0; + return ret; } static int From 07e8926d65b0d393a67cdbc61a81515e714cc702 Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Thu, 17 Mar 2011 13:45:12 +0000 Subject: [PATCH 0133/2556] drm/i915: Prevent racy removal of request from client list commit 09bfa51773c1e90f13000dc2fc0c4b84047009bc upstream. When i915_gem_retire_requests_ring calls i915_gem_request_remove_from_client, the client_list for that request may already be removed in i915_gem_release. So we may call twice list_del(&request->client_list), resulting in an oops like this report: [126167.230394] BUG: unable to handle kernel paging request at 00100104 [126167.230699] IP: [] i915_gem_retire_requests_ring+0xd4/0x240 [i915] [126167.231042] *pdpt = 00000000314c1001 *pde = 0000000000000000 [126167.231314] Oops: 0002 [#1] SMP [126167.231471] last sysfs file: /sys/devices/LNXSYSTM:00/device:00/PNP0C0A:00/power_supply/BAT1/current_now [126167.231901] Modules linked in: snd_seq_dummy nls_utf8 isofs btrfs zlib_deflate libcrc32c ufs qnx4 hfsplus hfs minix ntfs vfat msdos fat jfs xfs exportfs reiserfs cryptd aes_i586 aes_generic binfmt_misc vboxnetadp vboxnetflt vboxdrv parport_pc ppdev snd_hda_codec_hdmi snd_hda_codec_conexant snd_hda_intel snd_hda_codec snd_hwdep arc4 snd_pcm snd_seq_midi snd_rawmidi snd_seq_midi_event snd_seq uvcvideo videodev snd_timer snd_seq_device joydev iwlagn iwlcore mac80211 snd cfg80211 soundcore i915 drm_kms_helper snd_page_alloc psmouse drm serio_raw i2c_algo_bit video lp parport usbhid hid sky2 sdhci_pci ahci sdhci libahci [126167.232018] [126167.232018] Pid: 1101, comm: Xorg Not tainted 2.6.38-6-generic-pae #34-Ubuntu Gateway MC7833U / [126167.232018] EIP: 0060:[] EFLAGS: 00213246 CPU: 0 [126167.232018] EIP is at i915_gem_retire_requests_ring+0xd4/0x240 [i915] [126167.232018] EAX: 00200200 EBX: f1ac25b0 ECX: 00000040 EDX: 00100100 [126167.232018] ESI: f1a2801c EDI: e87fc060 EBP: ef4d7dd8 ESP: ef4d7db0 [126167.232018] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 [126167.232018] Process Xorg (pid: 1101, ti=ef4d6000 task=f1ba6500 task.ti=ef4d6000) [126167.232018] Stack: [126167.232018] f1a28000 f1a2809c f1a28094 0058bd97 f1aa2400 f1a2801c 0058bd7b 0058bd85 [126167.232018] f1a2801c f1a28000 ef4d7e38 f8c2e995 ef4d7e30 ef4d7e60 c14d1ebc f6b3a040 [126167.232018] f1522cc0 000000db 00000000 f1ba6500 ffffffa1 00000000 00000001 f1a29214 [126167.232018] Call Trace: Unfortunately the call trace reported was cut, but looking at debug symbols the crash is at __list_del, when probably list_del is called twice on the same request->client_list, as the dereferenced value is LIST_POISON1 + 4, and by looking more at the debug symbols before list_del call it should have being called by i915_gem_request_remove_from_client And as I can see in the code, it seems we indeed have the possibility to remove a request->client_list twice, which would cause the above, because we do list_del(&request->client_list) on both i915_gem_request_remove_from_client and i915_gem_release As Chris Wilson pointed out, it's indeed the case: "(...) I had thought that the actual insertion/deletion was serialised under the struct mutex and the intention of the spinlock was to protect the unlocked list traversal during throttling. However, I missed that i915_gem_release() is also called without struct mutex and so we do need the double check for i915_gem_request_remove_from_client()." This change does the required check to avoid the duplicate remove of request->client_list. Bugzilla: http://bugs.launchpad.net/bugs/733780 Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Chris Wilson Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/i915_gem.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 36e66cc5225eb..729c95a8fe6ea 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1749,8 +1749,10 @@ i915_gem_request_remove_from_client(struct drm_i915_gem_request *request) return; spin_lock(&file_priv->mm.lock); - list_del(&request->client_list); - request->file_priv = NULL; + if (request->file_priv) { + list_del(&request->client_list); + request->file_priv = NULL; + } spin_unlock(&file_priv->mm.lock); } From 8ee663ceb3d0ee74a746800f3919baf5a621ead9 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 17 Mar 2011 22:33:33 +0000 Subject: [PATCH 0134/2556] drm: Fix use-after-free in drm_gem_vm_close() commit b74ad5ae14def5e81ad0be3dddb96e485b861b1b upstream. As we may release the last reference, we need to store the device in a local variable in order to unlock afterwards. [ 60.140768] BUG: unable to handle kernel paging request at 6b6b6b9f [ 60.140973] IP: [] __mutex_unlock_slowpath+0x5a/0x111 [ 60.141014] *pdpt = 0000000024a54001 *pde = 0000000000000000 [ 60.141014] Oops: 0002 [#1] PREEMPT SMP [ 60.141014] last sysfs file: /sys/devices/LNXSYSTM:00/device:00/PNP0A08:00/PNP0C0A:00/power_supply/BAT0/voltage_now [ 60.141014] Modules linked in: uvcvideo ath9k pegasus ath9k_common ath9k_hw hid_egalax ath3k joydev asus_laptop sparse_keymap battery input_polldev [ 60.141014] [ 60.141014] Pid: 771, comm: meego-ux-daemon Not tainted 2.6.37.2-7.1 #1 EXOPC EXOPG06411/EXOPG06411 [ 60.141014] EIP: 0060:[] EFLAGS: 00010046 CPU: 0 [ 60.141014] EIP is at __mutex_unlock_slowpath+0x5a/0x111 [ 60.141014] EAX: 00000100 EBX: 6b6b6b9b ECX: e9b4a1b0 EDX: e4a4e580 [ 60.141014] ESI: db162558 EDI: 00000246 EBP: e480be50 ESP: e480be44 [ 60.141014] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 [ 60.141014] Process meego-ux-daemon (pid: 771, ti=e480a000 task=e9b4a1b0 task.ti=e480a000) [ 60.141014] Stack: [ 60.141014] e4a4e580 db162558 f5a2f838 e480be58 c1536dd0 e480be68 c125ab1b db162558 [ 60.141014] db1624e0 e480be78 c10ba071 db162558 f760241c e480be94 c10bb0bc 000155fe [ 60.141014] f760241c f5a2f838 f5a2f8c8 00000000 e480bea4 c1037c24 00000000 f5a2f838 [ 60.141014] Call Trace: [ 60.141014] [] ? mutex_unlock+0x8/0xa [ 60.141014] [] ? drm_gem_vm_close+0x39/0x3d [ 60.141014] [] ? remove_vma+0x2d/0x58 [ 60.141014] [] ? exit_mmap+0x126/0x13f [ 60.141014] [] ? mmput+0x37/0x9a [ 60.141014] [] ? exec_mmap+0x178/0x19c [ 60.141014] [] ? _raw_spin_unlock+0x1d/0x36 [ 60.141014] [] ? flush_old_exec+0x42/0x75 [ 60.141014] [] ? load_elf_binary+0x32a/0x922 [ 60.141014] [] ? search_binary_handler+0x200/0x2ea [ 60.141014] [] ? search_binary_handler+0x159/0x2ea [ 60.141014] [] ? load_elf_binary+0x0/0x922 [ 60.141014] [] ? do_execve+0x1ff/0x2e6 [ 60.141014] [] ? sys_execve+0x2d/0x55 [ 60.141014] [] ? ptregs_execve+0x12/0x18 [ 60.141014] [] ? sysenter_do_call+0x12/0x3c [ 60.141014] [] ? init_centaur+0x9c/0x1ba [ 60.141014] Code: c1 00 75 0f ba 38 01 00 00 b8 8c 3a 6c c1 e8 cc 2e b0 ff 9c 58 8d 74 26 00 89 c7 fa 90 8d 74 26 00 e8 d2 b4 b2 ff b8 00 01 00 00 66 0f c1 43 04 38 e0 74 07 f3 90 8a 43 04 eb f5 83 3d 64 ef [ 60.141014] EIP: [] __mutex_unlock_slowpath+0x5a/0x111 SS:ESP 0068:e480be44 [ 60.141014] CR2: 000000006b6b6b9f Reported-by: Rusty Lynch Signed-off-by: Chris Wilson Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/drm_gem.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index ea1c4b019ebf9..c3c78eefc538d 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -498,11 +498,12 @@ EXPORT_SYMBOL(drm_gem_vm_open); void drm_gem_vm_close(struct vm_area_struct *vma) { struct drm_gem_object *obj = vma->vm_private_data; + struct drm_device *dev = obj->dev; - mutex_lock(&obj->dev->struct_mutex); + mutex_lock(&dev->struct_mutex); drm_vm_close_locked(vma); drm_gem_object_unreference(obj); - mutex_unlock(&obj->dev->struct_mutex); + mutex_unlock(&dev->struct_mutex); } EXPORT_SYMBOL(drm_gem_vm_close); From 3b7a3e87e21e4d0a7d231bdab2b00af84a89106b Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 22 Mar 2011 01:46:12 -0400 Subject: [PATCH 0135/2556] drm/radeon/kms: prefer legacy pll algo for tv-out commit 64146f8b2af1ba77fe3c21d9d6d7213b9bb72b40 upstream. ntsc seems to work fine with either algo, some pal TVs seem pickier. Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=30832 Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/atombios_crtc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 4a5a73bcf460a..e967cc869b3b2 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -957,7 +957,11 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode /* adjust pixel clock as needed */ adjusted_clock = atombios_adjust_pll(crtc, mode, pll, ss_enabled, &ss); - if (ASIC_IS_AVIVO(rdev)) + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) + /* TV seems to prefer the legacy algo on some boards */ + radeon_compute_pll_legacy(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div, + &ref_div, &post_div); + else if (ASIC_IS_AVIVO(rdev)) radeon_compute_pll_avivo(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div, &ref_div, &post_div); else From 0b62527f8258ce04782469e4521b43d4dd5fc5f0 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 23 Mar 2011 08:10:10 +0000 Subject: [PATCH 0136/2556] drm/radeon/kms: fix hardcoded EDID handling commit fafcf94e2b5732d1e13b440291c53115d2b172e9 upstream. On some servers there is a hardcoded EDID provided in the vbios so that the driver will always see a display connected even if something like a KVM prevents traditional means like DDC or load detection from working properly. Also most server boards with DVI are not actually DVI, but DVO connected to a virtual KVM service processor. If we fail to detect a monitor via DDC or load detection and a hardcoded EDID is available, use it. Additionally, when using the hardcoded EDID, use a copy of it rather than the actual one stored in the driver as the detect() and get_modes() functions may free it if DDC is successful. This fixes the virtual KVM on several internal servers. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_combios.c | 21 +++++++++++---- drivers/gpu/drm/radeon/radeon_connectors.c | 30 ++++++++++++++++++++-- drivers/gpu/drm/radeon/radeon_mode.h | 1 + 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index cf7c8d5b4ec24..cf602e2d0718e 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -448,7 +448,7 @@ static uint16_t combios_get_table_offset(struct drm_device *dev, bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) { - int edid_info; + int edid_info, size; struct edid *edid; unsigned char *raw; edid_info = combios_get_table_offset(rdev->ddev, COMBIOS_HARDCODED_EDID_TABLE); @@ -456,11 +456,12 @@ bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) return false; raw = rdev->bios + edid_info; - edid = kmalloc(EDID_LENGTH * (raw[0x7e] + 1), GFP_KERNEL); + size = EDID_LENGTH * (raw[0x7e] + 1); + edid = kmalloc(size, GFP_KERNEL); if (edid == NULL) return false; - memcpy((unsigned char *)edid, raw, EDID_LENGTH * (raw[0x7e] + 1)); + memcpy((unsigned char *)edid, raw, size); if (!drm_edid_is_valid(edid)) { kfree(edid); @@ -468,6 +469,7 @@ bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) } rdev->mode_info.bios_hardcoded_edid = edid; + rdev->mode_info.bios_hardcoded_edid_size = size; return true; } @@ -475,8 +477,17 @@ bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) struct edid * radeon_bios_get_hardcoded_edid(struct radeon_device *rdev) { - if (rdev->mode_info.bios_hardcoded_edid) - return rdev->mode_info.bios_hardcoded_edid; + struct edid *edid; + + if (rdev->mode_info.bios_hardcoded_edid) { + edid = kmalloc(rdev->mode_info.bios_hardcoded_edid_size, GFP_KERNEL); + if (edid) { + memcpy((unsigned char *)edid, + (unsigned char *)rdev->mode_info.bios_hardcoded_edid, + rdev->mode_info.bios_hardcoded_edid_size); + return edid; + } + } return NULL; } diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 22b7e3dc0eca4..d83338b11cdb0 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -629,6 +629,8 @@ static int radeon_vga_mode_valid(struct drm_connector *connector, static enum drm_connector_status radeon_vga_detect(struct drm_connector *connector, bool force) { + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct drm_encoder *encoder; struct drm_encoder_helper_funcs *encoder_funcs; @@ -679,6 +681,17 @@ radeon_vga_detect(struct drm_connector *connector, bool force) if (ret == connector_status_connected) ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true); + + /* RN50 and some RV100 asics in servers often have a hardcoded EDID in the + * vbios to deal with KVMs. If we have one and are not able to detect a monitor + * by other means, assume the CRT is connected and use that EDID. + */ + if ((!rdev->is_atom_bios) && + (ret == connector_status_disconnected) && + rdev->mode_info.bios_hardcoded_edid_size) { + ret = connector_status_connected; + } + radeon_connector_update_scratch_regs(connector, ret); return ret; } @@ -790,6 +803,8 @@ static int radeon_dvi_get_modes(struct drm_connector *connector) static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connector, bool force) { + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct drm_encoder *encoder = NULL; struct drm_encoder_helper_funcs *encoder_funcs; @@ -829,8 +844,6 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) * you don't really know what's connected to which port as both are digital. */ if (radeon_connector->shared_ddc && (ret == connector_status_connected)) { - struct drm_device *dev = connector->dev; - struct radeon_device *rdev = dev->dev_private; struct drm_connector *list_connector; struct radeon_connector *list_radeon_connector; list_for_each_entry(list_connector, &dev->mode_config.connector_list, head) { @@ -895,6 +908,19 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true); } + /* RN50 and some RV100 asics in servers often have a hardcoded EDID in the + * vbios to deal with KVMs. If we have one and are not able to detect a monitor + * by other means, assume the DFP is connected and use that EDID. In most + * cases the DVI port is actually a virtual KVM port connected to the service + * processor. + */ + if ((!rdev->is_atom_bios) && + (ret == connector_status_disconnected) && + rdev->mode_info.bios_hardcoded_edid_size) { + radeon_connector->use_digital = true; + ret = connector_status_connected; + } + out: /* updated in get modes as well since we need to know if it's analog or digital */ radeon_connector_update_scratch_regs(connector, ret); diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index a670caaee29e5..8c134db007d9b 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -239,6 +239,7 @@ struct radeon_mode_info { struct drm_property *underscan_vborder_property; /* hardcoded DFP edid from BIOS */ struct edid *bios_hardcoded_edid; + int bios_hardcoded_edid_size; /* pointer to fbdev info structure */ struct radeon_fbdev *rfbdev; From 144a4ea712041e818df28a2bea81431143164da8 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 15 Mar 2011 14:37:10 +0100 Subject: [PATCH 0137/2556] perf: Fix tear-down of inherited group events commit 38b435b16c36b0d863efcf3f07b34a6fac9873fd upstream. When destroying inherited events, we need to destroy groups too, otherwise the event iteration in perf_event_exit_task_context() will miss group siblings and we leak events with all the consequences. Reported-and-tested-by: Vince Weaver Signed-off-by: Peter Zijlstra LKML-Reference: <1300196470.2203.61.camel@twins> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/perf_event.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/kernel/perf_event.c b/kernel/perf_event.c index b22a2ef75c86f..ad02feadb6b02 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -6115,17 +6115,20 @@ __perf_event_exit_task(struct perf_event *child_event, struct perf_event_context *child_ctx, struct task_struct *child) { - struct perf_event *parent_event; + if (child_event->parent) { + raw_spin_lock_irq(&child_ctx->lock); + perf_group_detach(child_event); + raw_spin_unlock_irq(&child_ctx->lock); + } perf_event_remove_from_context(child_event); - parent_event = child_event->parent; /* - * It can happen that parent exits first, and has events + * It can happen that the parent exits first, and has events * that are still around due to the child reference. These - * events need to be zapped - but otherwise linger. + * events need to be zapped. */ - if (parent_event) { + if (child_event->parent) { sync_child_event(child_event, child); free_event(child_event); } From 172af058996db9c3dc6230d966295ed338dc3538 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 21 Mar 2011 15:37:01 -0400 Subject: [PATCH 0138/2556] NFS: Fix a hang/infinite loop in nfs_wb_page() commit b8413f98f997bb3ed7327e6d7117e7e91ce010c3 upstream. When one of the two waits in nfs_commit_inode() is interrupted, it returns a non-negative value, which causes nfs_wb_page() to think that the operation was successful causing it to busy-loop rather than exiting. It also causes nfs_file_fsync() to incorrectly report the file as being successfully committed to disk. This patch fixes both problems by ensuring that we return an error if the attempts to wait fail. Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- fs/nfs/write.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 42b92d7a9cc4e..b5fcbf7da6fc3 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1214,13 +1214,17 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) static int nfs_commit_set_lock(struct nfs_inode *nfsi, int may_wait) { + int ret; + if (!test_and_set_bit(NFS_INO_COMMIT, &nfsi->flags)) return 1; - if (may_wait && !out_of_line_wait_on_bit_lock(&nfsi->flags, - NFS_INO_COMMIT, nfs_wait_bit_killable, - TASK_KILLABLE)) - return 1; - return 0; + if (!may_wait) + return 0; + ret = out_of_line_wait_on_bit_lock(&nfsi->flags, + NFS_INO_COMMIT, + nfs_wait_bit_killable, + TASK_KILLABLE); + return (ret < 0) ? ret : 1; } static void nfs_commit_clear_lock(struct nfs_inode *nfsi) @@ -1396,9 +1400,10 @@ int nfs_commit_inode(struct inode *inode, int how) { LIST_HEAD(head); int may_wait = how & FLUSH_SYNC; - int res = 0; + int res; - if (!nfs_commit_set_lock(NFS_I(inode), may_wait)) + res = nfs_commit_set_lock(NFS_I(inode), may_wait); + if (res <= 0) goto out_mark_dirty; spin_lock(&inode->i_lock); res = nfs_scan_commit(inode, &head, 0, 0); @@ -1407,12 +1412,14 @@ int nfs_commit_inode(struct inode *inode, int how) int error = nfs_commit_list(inode, &head, how); if (error < 0) return error; - if (may_wait) - wait_on_bit(&NFS_I(inode)->flags, NFS_INO_COMMIT, - nfs_wait_bit_killable, - TASK_KILLABLE); - else + if (!may_wait) goto out_mark_dirty; + error = wait_on_bit(&NFS_I(inode)->flags, + NFS_INO_COMMIT, + nfs_wait_bit_killable, + TASK_KILLABLE); + if (error < 0) + return error; } else nfs_commit_clear_lock(NFS_I(inode)); return res; From 59887b744b4b8f3a68d31b2b29a4084bbe3a6e07 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 22 Mar 2011 18:40:10 -0400 Subject: [PATCH 0139/2556] SUNRPC: Never reuse the socket port after an xs_close() commit 246408dcd5dfeef2df437ccb0ef4d6ee87805f58 upstream. If we call xs_close(), we're in one of two situations: - Autoclose, which means we don't expect to resend a request - bind+connect failed, which probably means the port is in use Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/xprtsock.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index be96d429b475f..1e336a06d3e63 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -710,6 +710,8 @@ static void xs_reset_transport(struct sock_xprt *transport) if (sk == NULL) return; + transport->srcport = 0; + write_lock_bh(&sk->sk_callback_lock); transport->inet = NULL; transport->sock = NULL; From c9da0558d0b82ac448d12af27c6f743b86aecf66 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 18 Nov 2010 20:52:55 -0500 Subject: [PATCH 0140/2556] fs: call security_d_instantiate in d_obtain_alias V2 commit 24ff6663ccfdaf088dfa7acae489cb11ed4f43c4 upstream. While trying to track down some NFS problems with BTRFS, I kept noticing I was getting -EACCESS for no apparent reason. Eric Paris and printk() helped me figure out that it was SELinux that was giving me grief, with the following denial type=AVC msg=audit(1290013638.413:95): avc: denied { 0x800000 } for pid=1772 comm="nfsd" name="" dev=sda1 ino=256 scontext=system_u:system_r:kernel_t:s0 tcontext=system_u:object_r:unlabeled_t:s0 tclass=file Turns out this is because in d_obtain_alias if we can't find an alias we create one and do all the normal instantiation stuff, but we don't do the security_d_instantiate. Usually we are protected from getting a hashed dentry that hasn't yet run security_d_instantiate() by the parent's i_mutex, but obviously this isn't an option there, so in order to deal with the case that a second thread comes in and finds our new dentry before we get to run security_d_instantiate(), we go ahead and call it if we find a dentry already. Eric assures me that this is ok as the code checks to see if the dentry has been initialized already so calling security_d_instantiate() against the same dentry multiple times is ok. With this patch I'm no longer getting errant -EACCESS values. Signed-off-by: Josef Bacik Signed-off-by: Al Viro Cc: Chuck Ebbert Signed-off-by: Greg Kroah-Hartman --- fs/dcache.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/dcache.c b/fs/dcache.c index a39fe47c466f7..1baddc1cec48f 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1612,10 +1612,13 @@ struct dentry *d_obtain_alias(struct inode *inode) __bit_spin_unlock(0, (unsigned long *)&tmp->d_sb->s_anon.first); spin_unlock(&tmp->d_lock); spin_unlock(&inode->i_lock); + security_d_instantiate(tmp, inode); return tmp; out_iput: + if (res && !IS_ERR(res)) + security_d_instantiate(res, inode); iput(inode); return res; } From 0dc58dee18c289cbbaa6fb4b5746d6b503ae5642 Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Wed, 2 Mar 2011 13:42:05 +0100 Subject: [PATCH 0141/2556] dcdbas: force SMI to happen when expected commit dd65c736d1b5312c80c88a64bf521db4959eded5 upstream. The dcdbas driver can do an I/O write to cause a SMI to occur. The SMI handler looks at certain registers and memory locations, so the SMI needs to happen immediately. On some systems I/O writes are posted, though, causing the SMI to happen well after the "outb" occurred, which causes random failures. Following the "outb" with an "inb" forces the write to go through even if it is posted. Signed-off-by: Stuart Hayes Acked-by: Doug Warzecha Cc: Chuck Ebbert Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/dcdbas.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c index 69ad529d92fbb..ea5ac2dc12337 100644 --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c @@ -268,8 +268,10 @@ int dcdbas_smi_request(struct smi_cmd *smi_cmd) } /* generate SMI */ + /* inb to force posted write through and make SMI happen now */ asm volatile ( - "outb %b0,%w1" + "outb %b0,%w1\n" + "inb %w1" : /* no output args */ : "a" (smi_cmd->command_code), "d" (smi_cmd->command_address), From 5dae5f51c3c95958381e88f561efd5bc9b7d1bf5 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Mon, 28 Feb 2011 00:53:45 -0500 Subject: [PATCH 0142/2556] ext4: skip orphan cleanup if fs has unknown ROCOMPAT features commit d39195c33bb1b5fdcb0f416e8a0b34bfdb07a027 upstream. Orphan cleanup is currently executed even if the file system has some number of unknown ROCOMPAT features, which deletes inodes and frees blocks, which could be very bad for some RO_COMPAT features, especially the SNAPSHOT feature. This patch skips the orphan cleanup if it contains readonly compatible features not known by this ext4 implementation, which would prevent the fs from being mounted (or remounted) readwrite. Signed-off-by: Amir Goldstein Signed-off-by: "Theodore Ts'o" Signed-off-by: Greg Kroah-Hartman --- fs/ext4/super.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index f6a318f836b2c..4381efee3dbc1 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -75,6 +75,7 @@ static void ext4_write_super(struct super_block *sb); static int ext4_freeze(struct super_block *sb); static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data); +static int ext4_feature_set_ok(struct super_block *sb, int readonly); static void ext4_destroy_lazyinit_thread(void); static void ext4_unregister_li_request(struct super_block *sb); static void ext4_clear_request_list(void); @@ -2120,6 +2121,13 @@ static void ext4_orphan_cleanup(struct super_block *sb, return; } + /* Check if feature set would not allow a r/w mount */ + if (!ext4_feature_set_ok(sb, 0)) { + ext4_msg(sb, KERN_INFO, "Skipping orphan cleanup due to " + "unknown ROCOMPAT features"); + return; + } + if (EXT4_SB(sb)->s_mount_state & EXT4_ERROR_FS) { if (es->s_last_orphan) jbd_debug(1, "Errors on filesystem, " From cf6013b4a767169ca105edec2735ef7ff8d9b403 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 27 Mar 2011 11:37:20 -0700 Subject: [PATCH 0143/2556] Linux 2.6.38.2 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 167ef453a8cd5..6c155251f7228 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 38 -EXTRAVERSION = .1 +EXTRAVERSION = .2 NAME = Flesh-Eating Bats with Fangs # *DOCUMENTATION* From fe179eefe08cb77a39f66bc0c6f3ab75ee5a3a47 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Tue, 15 Sep 2009 13:50:31 -0700 Subject: [PATCH 0144/2556] [ARM] msm: move fixed iomappings and VMALLOC_END to 0xF8000000 This increases the total virtual footprint available for the 1:1 physical map and the vmalloc space from 256MB to 896MB, which should much improve things for hardware with 256-512MB of ram, discontiguous ram banks, etc. Change-Id: Idf720ba5983bbaa82595520f88c2dcad157dabfb Signed-off-by: Brian Swetland Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-trout.h | 2 +- .../mach-msm/include/mach/msm_iomap-7x00.h | 18 ++++++----- .../mach-msm/include/mach/msm_iomap-7x30.h | 30 +++++++++---------- .../mach-msm/include/mach/msm_iomap-8x50.h | 24 +++++++-------- arch/arm/mach-msm/include/mach/vmalloc.h | 3 +- 5 files changed, 40 insertions(+), 37 deletions(-) diff --git a/arch/arm/mach-msm/board-trout.h b/arch/arm/mach-msm/board-trout.h index 651851c3e1dd1..287c9471adfa1 100644 --- a/arch/arm/mach-msm/board-trout.h +++ b/arch/arm/mach-msm/board-trout.h @@ -58,7 +58,7 @@ #define TROUT_4_TP_LS_EN 19 #define TROUT_5_TP_LS_EN 1 -#define TROUT_CPLD_BASE 0xE8100000 +#define TROUT_CPLD_BASE 0xFA000000 #define TROUT_CPLD_START 0x98000000 #define TROUT_CPLD_SIZE SZ_4K diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h b/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h index cfff0e74f128f..7978c6f77f126 100644 --- a/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h +++ b/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h @@ -43,11 +43,11 @@ #define IOMEM(x) ((void __force __iomem *)(x)) #endif -#define MSM_VIC_BASE IOMEM(0xE0000000) +#define MSM_VIC_BASE IOMEM(0xF8000000) #define MSM_VIC_PHYS 0xC0000000 #define MSM_VIC_SIZE SZ_4K -#define MSM_CSR_BASE IOMEM(0xE0001000) +#define MSM_CSR_BASE IOMEM(0xF8001000) #define MSM_CSR_PHYS 0xC0100000 #define MSM_CSR_SIZE SZ_4K @@ -55,23 +55,23 @@ #define MSM_GPT_BASE MSM_CSR_BASE #define MSM_GPT_SIZE SZ_4K -#define MSM_DMOV_BASE IOMEM(0xE0002000) +#define MSM_DMOV_BASE IOMEM(0xF8002000) #define MSM_DMOV_PHYS 0xA9700000 #define MSM_DMOV_SIZE SZ_4K -#define MSM_GPIO1_BASE IOMEM(0xE0003000) +#define MSM_GPIO1_BASE IOMEM(0xF8003000) #define MSM_GPIO1_PHYS 0xA9200000 #define MSM_GPIO1_SIZE SZ_4K -#define MSM_GPIO2_BASE IOMEM(0xE0004000) +#define MSM_GPIO2_BASE IOMEM(0xF8004000) #define MSM_GPIO2_PHYS 0xA9300000 #define MSM_GPIO2_SIZE SZ_4K -#define MSM_CLK_CTL_BASE IOMEM(0xE0005000) +#define MSM_CLK_CTL_BASE IOMEM(0xF8005000) #define MSM_CLK_CTL_PHYS 0xA8600000 #define MSM_CLK_CTL_SIZE SZ_4K -#define MSM_SHARED_RAM_BASE IOMEM(0xE0100000) +#define MSM_SHARED_RAM_BASE IOMEM(0xF8100000) #define MSM_SHARED_RAM_PHYS 0x01F00000 #define MSM_SHARED_RAM_SIZE SZ_1M @@ -85,7 +85,7 @@ #define MSM_UART3_SIZE SZ_4K #ifdef CONFIG_MSM_DEBUG_UART -#define MSM_DEBUG_UART_BASE 0xE1000000 +#define MSM_DEBUG_UART_BASE 0xF9000000 #if CONFIG_MSM_DEBUG_UART == 1 #define MSM_DEBUG_UART_PHYS MSM_UART1_PHYS #elif CONFIG_MSM_DEBUG_UART == 2 @@ -123,9 +123,11 @@ #define MSM_MDP_PHYS 0xAA200000 #define MSM_MDP_SIZE 0x000F0000 +#define MSM_MDC_BASE IOMEM(0xF8200000) #define MSM_MDC_PHYS 0xAA500000 #define MSM_MDC_SIZE SZ_1M +#define MSM_AD5_BASE IOMEM(0xF8300000) #define MSM_AD5_PHYS 0xAC000000 #define MSM_AD5_SIZE (SZ_1M*13) diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h b/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h index 0fd7b68ca1141..96c2e8c2cbd0e 100644 --- a/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h +++ b/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h @@ -35,11 +35,11 @@ * */ -#define MSM_VIC_BASE IOMEM(0xE0000000) +#define MSM_VIC_BASE IOMEM(0xF8000000) #define MSM_VIC_PHYS 0xC0080000 #define MSM_VIC_SIZE SZ_4K -#define MSM_CSR_BASE IOMEM(0xE0001000) +#define MSM_CSR_BASE IOMEM(0xF8001000) #define MSM_CSR_PHYS 0xC0100000 #define MSM_CSR_SIZE SZ_4K @@ -50,43 +50,43 @@ #define MSM_GPT_BASE (MSM_TMR_BASE + 0x4) #define MSM_DGT_BASE (MSM_TMR_BASE + 0x24) -#define MSM_DMOV_BASE IOMEM(0xE0002000) +#define MSM_DMOV_BASE IOMEM(0xF8002000) #define MSM_DMOV_PHYS 0xAC400000 #define MSM_DMOV_SIZE SZ_4K -#define MSM_GPIO1_BASE IOMEM(0xE0003000) +#define MSM_GPIO1_BASE IOMEM(0xF8003000) #define MSM_GPIO1_PHYS 0xAC001000 #define MSM_GPIO1_SIZE SZ_4K -#define MSM_GPIO2_BASE IOMEM(0xE0004000) +#define MSM_GPIO2_BASE IOMEM(0xF8004000) #define MSM_GPIO2_PHYS 0xAC101000 #define MSM_GPIO2_SIZE SZ_4K -#define MSM_CLK_CTL_BASE IOMEM(0xE0005000) +#define MSM_CLK_CTL_BASE IOMEM(0xF8005000) #define MSM_CLK_CTL_PHYS 0xAB800000 #define MSM_CLK_CTL_SIZE SZ_4K -#define MSM_CLK_CTL_SH2_BASE IOMEM(0xE0006000) +#define MSM_CLK_CTL_SH2_BASE IOMEM(0xF8006000) #define MSM_CLK_CTL_SH2_PHYS 0xABA01000 #define MSM_CLK_CTL_SH2_SIZE SZ_4K -#define MSM_ACC_BASE IOMEM(0xE0007000) +#define MSM_ACC_BASE IOMEM(0xF8007000) #define MSM_ACC_PHYS 0xC0101000 #define MSM_ACC_SIZE SZ_4K -#define MSM_SAW_BASE IOMEM(0xE0008000) +#define MSM_SAW_BASE IOMEM(0xF8008000) #define MSM_SAW_PHYS 0xC0102000 #define MSM_SAW_SIZE SZ_4K -#define MSM_GCC_BASE IOMEM(0xE0009000) +#define MSM_GCC_BASE IOMEM(0xF8009000) #define MSM_GCC_PHYS 0xC0182000 #define MSM_GCC_SIZE SZ_4K -#define MSM_TCSR_BASE IOMEM(0xE000A000) +#define MSM_TCSR_BASE IOMEM(0xF800A000) #define MSM_TCSR_PHYS 0xAB600000 #define MSM_TCSR_SIZE SZ_4K -#define MSM_SHARED_RAM_BASE IOMEM(0xE0100000) +#define MSM_SHARED_RAM_BASE IOMEM(0xF8100000) #define MSM_SHARED_RAM_PHYS 0x00100000 #define MSM_SHARED_RAM_SIZE SZ_1M @@ -100,7 +100,7 @@ #define MSM_UART3_SIZE SZ_4K #ifdef CONFIG_MSM_DEBUG_UART -#define MSM_DEBUG_UART_BASE 0xE1000000 +#define MSM_DEBUG_UART_BASE 0xF9000000 #if CONFIG_MSM_DEBUG_UART == 1 #define MSM_DEBUG_UART_PHYS MSM_UART1_PHYS #elif CONFIG_MSM_DEBUG_UART == 2 @@ -111,11 +111,11 @@ #define MSM_DEBUG_UART_SIZE SZ_4K #endif -#define MSM_MDC_BASE IOMEM(0xE0200000) +#define MSM_MDC_BASE IOMEM(0xF8200000) #define MSM_MDC_PHYS 0xAA500000 #define MSM_MDC_SIZE SZ_1M -#define MSM_AD5_BASE IOMEM(0xE0300000) +#define MSM_AD5_BASE IOMEM(0xF8300000) #define MSM_AD5_PHYS 0xA7000000 #define MSM_AD5_SIZE (SZ_1M*13) diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h b/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h index acc819eb76e56..a2b0b0bfdbfe2 100644 --- a/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h +++ b/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h @@ -35,11 +35,11 @@ * */ -#define MSM_VIC_BASE IOMEM(0xE0000000) +#define MSM_VIC_BASE IOMEM(0xF8000000) #define MSM_VIC_PHYS 0xAC000000 #define MSM_VIC_SIZE SZ_4K -#define MSM_CSR_BASE IOMEM(0xE0001000) +#define MSM_CSR_BASE IOMEM(0xF8001000) #define MSM_CSR_PHYS 0xAC100000 #define MSM_CSR_SIZE SZ_4K @@ -50,27 +50,27 @@ #define MSM_GPT_BASE MSM_TMR_BASE #define MSM_DGT_BASE (MSM_TMR_BASE + 0x10) -#define MSM_DMOV_BASE IOMEM(0xE0002000) +#define MSM_DMOV_BASE IOMEM(0xF8002000) #define MSM_DMOV_PHYS 0xA9700000 #define MSM_DMOV_SIZE SZ_4K -#define MSM_GPIO1_BASE IOMEM(0xE0003000) +#define MSM_GPIO1_BASE IOMEM(0xF8003000) #define MSM_GPIO1_PHYS 0xA9000000 #define MSM_GPIO1_SIZE SZ_4K -#define MSM_GPIO2_BASE IOMEM(0xE0004000) +#define MSM_GPIO2_BASE IOMEM(0xF8004000) #define MSM_GPIO2_PHYS 0xA9100000 #define MSM_GPIO2_SIZE SZ_4K -#define MSM_CLK_CTL_BASE IOMEM(0xE0005000) +#define MSM_CLK_CTL_BASE IOMEM(0xF8005000) #define MSM_CLK_CTL_PHYS 0xA8600000 #define MSM_CLK_CTL_SIZE SZ_4K -#define MSM_SIRC_BASE IOMEM(0xE1006000) +#define MSM_SIRC_BASE IOMEM(0xF8006000) #define MSM_SIRC_PHYS 0xAC200000 #define MSM_SIRC_SIZE SZ_4K -#define MSM_SCPLL_BASE IOMEM(0xE1007000) +#define MSM_SCPLL_BASE IOMEM(0xF8007000) #define MSM_SCPLL_PHYS 0xA8800000 #define MSM_SCPLL_SIZE SZ_4K @@ -80,7 +80,7 @@ #define MSM_SMI_BASE 0x00000000 #endif -#define MSM_SHARED_RAM_BASE IOMEM(0xE0100000) +#define MSM_SHARED_RAM_BASE IOMEM(0xF8100000) #define MSM_SHARED_RAM_PHYS (MSM_SMI_BASE + 0x00100000) #define MSM_SHARED_RAM_SIZE SZ_1M @@ -94,7 +94,7 @@ #define MSM_UART3_SIZE SZ_4K #ifdef CONFIG_MSM_DEBUG_UART -#define MSM_DEBUG_UART_BASE 0xE1000000 +#define MSM_DEBUG_UART_BASE 0xF9000000 #if CONFIG_MSM_DEBUG_UART == 1 #define MSM_DEBUG_UART_PHYS MSM_UART1_PHYS #elif CONFIG_MSM_DEBUG_UART == 2 @@ -105,11 +105,11 @@ #define MSM_DEBUG_UART_SIZE SZ_4K #endif -#define MSM_MDC_BASE IOMEM(0xE0200000) +#define MSM_MDC_BASE IOMEM(0xF8200000) #define MSM_MDC_PHYS 0xAA500000 #define MSM_MDC_SIZE SZ_1M -#define MSM_AD5_BASE IOMEM(0xE0300000) +#define MSM_AD5_BASE IOMEM(0xF8300000) #define MSM_AD5_PHYS 0xAC000000 #define MSM_AD5_SIZE (SZ_1M*13) diff --git a/arch/arm/mach-msm/include/mach/vmalloc.h b/arch/arm/mach-msm/include/mach/vmalloc.h index d138448eff16d..8d7bc7c4bb25b 100644 --- a/arch/arm/mach-msm/include/mach/vmalloc.h +++ b/arch/arm/mach-msm/include/mach/vmalloc.h @@ -16,7 +16,8 @@ #ifndef __ASM_ARCH_MSM_VMALLOC_H #define __ASM_ARCH_MSM_VMALLOC_H -#define VMALLOC_END 0xd0000000UL +/* IO devices are mapped at 0xF8000000 and above */ +#define VMALLOC_END 0xf8000000UL #endif From 48c8005ab6ed4a6188e0fce5452d093d29ee0257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 18 Jun 2010 18:59:46 -0700 Subject: [PATCH 0145/2556] [ARM] msm: Add missing devices and clock assignments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I9dad298eb07cd85a0efb6335ba176261f829c7a3 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/clock.h | 1 + arch/arm/mach-msm/devices-msm7x00.c | 135 +++- arch/arm/mach-msm/devices-msm7x30.c | 582 +++++++++++++++++- arch/arm/mach-msm/devices-qsd8x50.c | 526 +++++++++++++++- arch/arm/mach-msm/include/mach/dma.h | 13 + .../mach-msm/include/mach/msm_iomap-7x00.h | 23 +- .../mach-msm/include/mach/msm_iomap-7x30.h | 54 ++ .../mach-msm/include/mach/msm_iomap-8x50.h | 33 +- arch/arm/mach-msm/io.c | 2 +- 9 files changed, 1324 insertions(+), 45 deletions(-) diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h index c270b552ed135..5db5c3e9c928f 100644 --- a/arch/arm/mach-msm/clock.h +++ b/arch/arm/mach-msm/clock.h @@ -32,6 +32,7 @@ #define CLKFLAG_AUTO_OFF 0x00000200 #define CLKFLAG_MIN 0x00000400 #define CLKFLAG_MAX 0x00000800 +#define CLKFLAG_SHARED 0x00001000 struct clk_ops { int (*enable)(unsigned id); diff --git a/arch/arm/mach-msm/devices-msm7x00.c b/arch/arm/mach-msm/devices-msm7x00.c index fb548a8a21dbb..dfaa35c3b1085 100644 --- a/arch/arm/mach-msm/devices-msm7x00.c +++ b/arch/arm/mach-msm/devices-msm7x00.c @@ -15,9 +15,12 @@ #include #include +#include +#include #include #include +#include #include "devices.h" #include @@ -88,6 +91,92 @@ struct platform_device msm_device_uart3 = { .resource = resources_uart3, }; +static struct resource msm_uart1_dm_resources[] = { + { + .start = MSM_UART1DM_PHYS, + .end = MSM_UART1DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART1DM_IRQ, + .end = INT_UART1DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART1DM_RX, + .end = INT_UART1DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART1_TX_CHAN, + .end = DMOV_HSUART1_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART1_TX_CRCI, + .end = DMOV_HSUART1_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm1_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm1 = { + .name = "msm_serial_hs", + .id = 0, + .num_resources = ARRAY_SIZE(msm_uart1_dm_resources), + .resource = msm_uart1_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm1_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource msm_uart2_dm_resources[] = { + { + .start = MSM_UART2DM_PHYS, + .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART2DM_IRQ, + .end = INT_UART2DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART2DM_RX, + .end = INT_UART2DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART2_TX_CHAN, + .end = DMOV_HSUART2_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART2_TX_CRCI, + .end = DMOV_HSUART2_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm2_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm2 = { + .name = "msm_serial_hs", + .id = 1, + .num_resources = ARRAY_SIZE(msm_uart2_dm_resources), + .resource = msm_uart2_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm2_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + static struct resource resources_i2c[] = { { .start = MSM_I2C_PHYS, @@ -414,24 +503,52 @@ struct platform_device msm_device_mdp = { .resource = resources_mdp, }; +static struct resource resources_tssc[] = { + { + .start = MSM_TSSC_PHYS, + .end = MSM_TSSC_PHYS + MSM_TSSC_SIZE - 1, + .name = "tssc", + .flags = IORESOURCE_MEM, + }, + { + .start = INT_TCHSCRN1, + .end = INT_TCHSCRN1, + .name = "tssc1", + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING, + }, + { + .start = INT_TCHSCRN2, + .end = INT_TCHSCRN2, + .name = "tssc2", + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING, + }, +}; + +struct platform_device msm_device_touchscreen = { + .name = "msm_touchscreen", + .id = 0, + .num_resources = ARRAY_SIZE(resources_tssc), + .resource = resources_tssc, +}; + struct clk msm_clocks_7x01a[] = { CLK_PCOM("adm_clk", ADM_CLK, NULL, 0), CLK_PCOM("adsp_clk", ADSP_CLK, NULL, 0), - CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, 0), + CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN), CLK_PCOM("ebi2_clk", EBI2_CLK, NULL, 0), CLK_PCOM("ecodec_clk", ECODEC_CLK, NULL, 0), - CLK_PCOM("emdh_clk", EMDH_CLK, NULL, OFF), - CLK_PCOM("gp_clk", GP_CLK, NULL, 0), + CLK_PCOM("mddi_clk", EMDH_CLK, &msm_device_mddi1.dev, OFF), + CLK_PCOM("gp_clk", GP_CLK, NULL, 0), CLK_PCOM("grp_clk", GRP_3D_CLK, NULL, OFF), CLK_PCOM("i2c_clk", I2C_CLK, &msm_device_i2c.dev, 0), CLK_PCOM("icodec_rx_clk", ICODEC_RX_CLK, NULL, 0), CLK_PCOM("icodec_tx_clk", ICODEC_TX_CLK, NULL, 0), CLK_PCOM("imem_clk", IMEM_CLK, NULL, OFF), CLK_PCOM("mdc_clk", MDC_CLK, NULL, 0), - CLK_PCOM("mdp_clk", MDP_CLK, NULL, OFF), + CLK_PCOM("mdp_clk", MDP_CLK, &msm_device_mdp.dev, OFF), CLK_PCOM("pbus_clk", PBUS_CLK, NULL, 0), CLK_PCOM("pcm_clk", PCM_CLK, NULL, 0), - CLK_PCOM("mddi_clk", PMDH_CLK, NULL, OFF | CLK_MINMAX), + CLK_PCOM("mddi_clk", PMDH_CLK, &msm_device_mddi0.dev, OFF | CLK_MINMAX), CLK_PCOM("sdac_clk", SDAC_CLK, NULL, OFF), CLK_PCOM("sdc_clk", SDC1_CLK, &msm_device_sdc1.dev, OFF), CLK_PCOM("sdc_pclk", SDC1_P_CLK, &msm_device_sdc1.dev, OFF), @@ -446,14 +563,14 @@ struct clk msm_clocks_7x01a[] = { CLK_PCOM("tv_dac_clk", TV_DAC_CLK, NULL, 0), CLK_PCOM("tv_enc_clk", TV_ENC_CLK, NULL, 0), CLK_PCOM("uart_clk", UART1_CLK, &msm_device_uart1.dev, OFF), - CLK_PCOM("uart_clk", UART2_CLK, &msm_device_uart2.dev, 0), + CLK_PCOM("uart_clk", UART2_CLK, &msm_device_uart2.dev, OFF), CLK_PCOM("uart_clk", UART3_CLK, &msm_device_uart3.dev, OFF), - CLK_PCOM("uart1dm_clk", UART1DM_CLK, NULL, OFF), - CLK_PCOM("uart2dm_clk", UART2DM_CLK, NULL, 0), + CLK_PCOM("uartdm_clk", UART1DM_CLK, &msm_device_uart_dm1.dev, OFF), + CLK_PCOM("uartdm_clk", UART2DM_CLK, &msm_device_uart_dm2.dev, OFF), CLK_PCOM("usb_hs_clk", USB_HS_CLK, &msm_device_hsusb.dev, OFF), CLK_PCOM("usb_hs_pclk", USB_HS_P_CLK, &msm_device_hsusb.dev, OFF), CLK_PCOM("usb_otg_clk", USB_OTG_CLK, NULL, 0), - CLK_PCOM("vdc_clk", VDC_CLK, NULL, OFF ), + CLK_PCOM("vdc_clk", VDC_CLK, NULL, OFF | CLK_MINMAX), CLK_PCOM("vfe_clk", VFE_CLK, NULL, OFF), CLK_PCOM("vfe_mdc_clk", VFE_MDC_CLK, NULL, OFF), }; diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c index 4e9a0ab3e9377..4d6c118890aec 100644 --- a/arch/arm/mach-msm/devices-msm7x30.c +++ b/arch/arm/mach-msm/devices-msm7x30.c @@ -23,6 +23,7 @@ #include #include "devices.h" +#include "proc_comm.h" #include "smd_private.h" #include @@ -31,6 +32,19 @@ #include +static struct resource resources_uart1[] = { + { + .start = INT_UART1, + .end = INT_UART1, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART1_PHYS, + .end = MSM_UART1_PHYS + MSM_UART1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + static struct resource resources_uart2[] = { { .start = INT_UART2, @@ -44,6 +58,26 @@ static struct resource resources_uart2[] = { }, }; +static struct resource resources_uart3[] = { + { + .start = INT_UART3, + .end = INT_UART3, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART3_PHYS, + .end = MSM_UART3_PHYS + MSM_UART3_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_uart1 = { + .name = "msm_serial", + .id = 0, + .num_resources = ARRAY_SIZE(resources_uart1), + .resource = resources_uart1, +}; + struct platform_device msm_device_uart2 = { .name = "msm_serial", .id = 1, @@ -51,11 +85,510 @@ struct platform_device msm_device_uart2 = { .resource = resources_uart2, }; +struct platform_device msm_device_uart3 = { + .name = "msm_serial", + .id = 2, + .num_resources = ARRAY_SIZE(resources_uart3), + .resource = resources_uart3, +}; + struct platform_device msm_device_smd = { .name = "msm_smd", .id = -1, }; +static struct resource msm_uart1_dm_resources[] = { + { + .start = MSM_UART1DM_PHYS, + .end = MSM_UART1DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART1DM_IRQ, + .end = INT_UART1DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART1DM_RX, + .end = INT_UART1DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART1_TX_CHAN, + .end = DMOV_HSUART1_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART1_TX_CRCI, + .end = DMOV_HSUART1_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm1_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm1 = { + .name = "msm_serial_hs", + .id = 0, + .num_resources = ARRAY_SIZE(msm_uart1_dm_resources), + .resource = msm_uart1_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm1_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource msm_uart2_dm_resources[] = { + { + .start = MSM_UART2DM_PHYS, + .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART2DM_IRQ, + .end = INT_UART2DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART2DM_RX, + .end = INT_UART2DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART2_TX_CHAN, + .end = DMOV_HSUART2_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART2_TX_CRCI, + .end = DMOV_HSUART2_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm2_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm2 = { + .name = "msm_serial_hs", + .id = 1, + .num_resources = ARRAY_SIZE(msm_uart2_dm_resources), + .resource = msm_uart2_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm2_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource resources_i2c[] = { + { + .start = MSM_I2C_PHYS, + .end = MSM_I2C_PHYS + MSM_I2C_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_PWB_I2C, + .end = INT_PWB_I2C, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_i2c = { + .name = "msm_i2c", + .id = 0, + .num_resources = ARRAY_SIZE(resources_i2c), + .resource = resources_i2c, +}; + +static struct resource resources_i2c2[] = { + { + .start = MSM_I2C_2_PHYS, + .end = MSM_I2C_2_PHYS + MSM_I2C_2_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_PWB_I2C_2, + .end = INT_PWB_I2C_2, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_i2c2 = { + .name = "msm_i2c", + .id = 1, + .num_resources = ARRAY_SIZE(resources_i2c2), + .resource = resources_i2c2, +}; + +static struct resource resources_qup[] = { + { + .name = "qup_phys_addr", + .start = MSM_QUP_PHYS, + .end = MSM_QUP_PHYS + MSM_QUP_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "gsbi_qup_i2c_addr", + .start = MSM_GSBI_QUP_I2C_PHYS, + .end = MSM_GSBI_QUP_I2C_PHYS + MSM_GSBI_QUP_I2C_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "qup_in_intr", + .start = INT_PWB_QUP_IN, + .end = INT_PWB_QUP_IN, + .flags = IORESOURCE_IRQ, + }, + { + .name = "qup_out_intr", + .start = INT_PWB_QUP_OUT, + .end = INT_PWB_QUP_OUT, + .flags = IORESOURCE_IRQ, + }, + { + .name = "qup_err_intr", + .start = INT_PWB_QUP_ERR, + .end = INT_PWB_QUP_ERR, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_qup_i2c = { + .name = "qup_i2c", + .id = 4, + .num_resources = ARRAY_SIZE(resources_qup), + .resource = resources_qup, +}; + +struct flash_platform_data msm_nand_data = { + .parts = NULL, + .nr_parts = 0, +}; + +static struct resource resources_nand[] = { + [0] = { + .start = 7, + .end = 7, + .flags = IORESOURCE_DMA, + }, +}; + +struct platform_device msm_device_nand = { + .name = "msm_nand", + .id = -1, + .num_resources = ARRAY_SIZE(resources_nand), + .resource = resources_nand, + .dev = { + .platform_data = &msm_nand_data, + }, +}; + +static struct resource resources_sdc1[] = { + { + .start = MSM_SDC1_PHYS, + .end = MSM_SDC1_PHYS + MSM_SDC1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC1_0, + .end = INT_SDC1_0, + .flags = IORESOURCE_IRQ, + .name = "cmd_irq", + }, + { + .start = INT_SDC1_1, + .end = INT_SDC1_1, + .flags = IORESOURCE_IRQ, + .name = "pio_irq", + }, + { + .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, + .name = "status_irq" + }, + { + .start = 8, + .end = 8, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource resources_sdc2[] = { + { + .start = MSM_SDC2_PHYS, + .end = MSM_SDC2_PHYS + MSM_SDC2_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC2_0, + .end = INT_SDC2_0, + .flags = IORESOURCE_IRQ, + .name = "cmd_irq", + }, + { + .start = INT_SDC2_1, + .end = INT_SDC2_1, + .flags = IORESOURCE_IRQ, + .name = "pio_irq", + }, + { + .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, + .name = "status_irq" + }, + { + .start = 8, + .end = 8, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource resources_sdc3[] = { + { + .start = MSM_SDC3_PHYS, + .end = MSM_SDC3_PHYS + MSM_SDC3_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC3_0, + .end = INT_SDC3_0, + .flags = IORESOURCE_IRQ, + .name = "cmd_irq", + }, + { + .start = INT_SDC3_1, + .end = INT_SDC3_1, + .flags = IORESOURCE_IRQ, + .name = "pio_irq", + }, + { + .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, + .name = "status_irq" + }, + { + .start = 8, + .end = 8, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource resources_sdc4[] = { + { + .start = MSM_SDC4_PHYS, + .end = MSM_SDC4_PHYS + MSM_SDC4_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC4_0, + .end = INT_SDC4_0, + .flags = IORESOURCE_IRQ, + .name = "cmd_irq", + }, + { + .start = INT_SDC4_1, + .end = INT_SDC4_1, + .flags = IORESOURCE_IRQ, + .name = "pio_irq", + }, + { + .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, + .name = "status_irq" + }, + { + .start = 8, + .end = 8, + .flags = IORESOURCE_DMA, + }, +}; + +struct platform_device msm_device_sdc1 = { + .name = "msm_sdcc", + .id = 1, + .num_resources = ARRAY_SIZE(resources_sdc1), + .resource = resources_sdc1, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc2 = { + .name = "msm_sdcc", + .id = 2, + .num_resources = ARRAY_SIZE(resources_sdc2), + .resource = resources_sdc2, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc3 = { + .name = "msm_sdcc", + .id = 3, + .num_resources = ARRAY_SIZE(resources_sdc3), + .resource = resources_sdc3, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc4 = { + .name = "msm_sdcc", + .id = 4, + .num_resources = ARRAY_SIZE(resources_sdc4), + .resource = resources_sdc4, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct platform_device *msm_sdcc_devices[] __initdata = { + &msm_device_sdc1, + &msm_device_sdc2, + &msm_device_sdc3, + &msm_device_sdc4, +}; + +int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, + unsigned int stat_irq, unsigned long stat_irq_flags) +{ + struct platform_device *pdev; + struct resource *res; + + if (controller < 1 || controller > 4) + return -EINVAL; + + pdev = msm_sdcc_devices[controller-1]; + pdev->dev.platform_data = plat; + + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "status_irq"); + if (!res) + return -EINVAL; + else if (stat_irq) { + res->start = res->end = stat_irq; + res->flags &= ~IORESOURCE_DISABLED; + res->flags |= stat_irq_flags; + } + + return platform_device_register(pdev); +} + +static struct resource resources_mddi0[] = { + { + .start = MSM_PMDH_PHYS, + .end = MSM_PMDH_PHYS + MSM_PMDH_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_MDDI_PRI, + .end = INT_MDDI_PRI, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource resources_mddi1[] = { + { + .start = MSM_EMDH_PHYS, + .end = MSM_EMDH_PHYS + MSM_EMDH_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_MDDI_EXT, + .end = INT_MDDI_EXT, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_mddi0 = { + .name = "msm_mddi", + .id = 0, + .num_resources = ARRAY_SIZE(resources_mddi0), + .resource = resources_mddi0, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_mddi1 = { + .name = "msm_mddi", + .id = 1, + .num_resources = ARRAY_SIZE(resources_mddi1), + .resource = resources_mddi1, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct resource resources_mdp[] = { + { + .start = MSM_MDP_PHYS, + .end = MSM_MDP_PHYS + MSM_MDP_SIZE - 1, + .name = "mdp", + .flags = IORESOURCE_MEM + }, + { + .start = INT_MDP, + .end = INT_MDP, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_mdp = { + .name = "msm_mdp", + .id = 0, + .num_resources = ARRAY_SIZE(resources_mdp), + .resource = resources_mdp, +}; + + +static struct resource resources_ssbi_pmic[] = { + { + .start = MSM_PMIC_SSBI_PHYS, + .end = MSM_PMIC_SSBI_PHYS + MSM_PMIC_SSBI_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct platform_device msm_device_ssbi_pmic = { + .name = "msm_ssbi", + .id = -1, + .resource = resources_ssbi_pmic, + .num_resources = ARRAY_SIZE(resources_ssbi_pmic), +}; + +static struct resource resources_spi[] = { + { + .start = MSM_SPI_PHYS, + .end = MSM_SPI_PHYS + MSM_SPI_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SPI_INPUT, + .end = INT_SPI_INPUT, + .name = "irq_in", + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_SPI_OUTPUT, + .end = INT_SPI_OUTPUT, + .name = "irq_out", + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_SPI_ERROR, + .end = INT_SPI_ERROR, + .name = "irq_err", + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_spi = { + .name = "msm_spi", + .id = 0, + .num_resources = ARRAY_SIZE(resources_spi), + .resource = resources_spi, +}; + static struct resource resources_otg[] = { { .start = MSM_HSUSB_PHYS, @@ -134,8 +667,8 @@ struct clk msm_clocks_7x30[] = { CLK_PCOM("camif_pad_pclk", CAMIF_PAD_P_CLK, NULL, OFF), CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN), CLK_PCOM("ecodec_clk", ECODEC_CLK, NULL, 0), - CLK_PCOM("emdh_clk", EMDH_CLK, NULL, OFF | CLK_MINMAX), - CLK_PCOM("emdh_pclk", EMDH_P_CLK, NULL, OFF), + CLK_PCOM("emdh_clk", EMDH_CLK, &msm_device_mddi1.dev, OFF | CLK_MINMAX), + CLK_PCOM("emdh_pclk", EMDH_P_CLK, &msm_device_mddi1.dev, OFF), CLK_PCOM("gp_clk", GP_CLK, NULL, 0), CLK_PCOM("grp_2d_clk", GRP_2D_CLK, NULL, 0), CLK_PCOM("grp_2d_pclk", GRP_2D_P_CLK, NULL, 0), @@ -144,44 +677,58 @@ struct clk msm_clocks_7x30[] = { CLK_7X30S("grp_src_clk", GRP_3D_SRC_CLK, GRP_3D_CLK, NULL, 0), CLK_PCOM("hdmi_clk", HDMI_CLK, NULL, 0), CLK_PCOM("imem_clk", IMEM_CLK, NULL, OFF), + CLK_PCOM("i2c_clk", I2C_CLK, &msm_device_i2c.dev, OFF), + CLK_PCOM("i2c_clk", I2C_2_CLK, &msm_device_i2c2.dev, OFF), CLK_PCOM("jpeg_clk", JPEG_CLK, NULL, OFF), CLK_PCOM("jpeg_pclk", JPEG_P_CLK, NULL, OFF), CLK_PCOM("lpa_codec_clk", LPA_CODEC_CLK, NULL, 0), CLK_PCOM("lpa_core_clk", LPA_CORE_CLK, NULL, 0), CLK_PCOM("lpa_pclk", LPA_P_CLK, NULL, 0), CLK_PCOM("mdc_clk", MDC_CLK, NULL, 0), - CLK_PCOM("mddi_clk", PMDH_CLK, NULL, OFF | CLK_MINMAX), - CLK_PCOM("mddi_pclk", PMDH_P_CLK, NULL, 0), - CLK_PCOM("mdp_clk", MDP_CLK, NULL, OFF), - CLK_PCOM("mdp_pclk", MDP_P_CLK, NULL, 0), - CLK_PCOM("mdp_lcdc_pclk_clk", MDP_LCDC_PCLK_CLK, NULL, 0), - CLK_PCOM("mdp_lcdc_pad_pclk_clk", MDP_LCDC_PAD_PCLK_CLK, NULL, 0), - CLK_PCOM("mdp_vsync_clk", MDP_VSYNC_CLK, NULL, 0), + CLK_PCOM("mddi_clk", PMDH_CLK, &msm_device_mddi0.dev, OFF | CLK_MINMAX), + CLK_PCOM("mddi_pclk", PMDH_P_CLK, &msm_device_mddi0.dev, OFF | CLK_MINMAX), + CLK_PCOM("mdp_clk", MDP_CLK, &msm_device_mdp.dev, OFF), + CLK_PCOM("mdp_pclk", MDP_P_CLK, &msm_device_mdp.dev, OFF), + CLK_PCOM("lcdc_pclk_clk", MDP_LCDC_PCLK_CLK, &msm_device_mdp.dev, 0), + CLK_PCOM("lcdc_pad_pclk_clk", MDP_LCDC_PAD_PCLK_CLK, &msm_device_mdp.dev, 0), + CLK_PCOM("mdp_vsync_clk", MDP_VSYNC_CLK, &msm_device_mdp.dev, 0), CLK_PCOM("mfc_clk", MFC_CLK, NULL, 0), CLK_PCOM("mfc_div2_clk", MFC_DIV2_CLK, NULL, 0), CLK_PCOM("mfc_pclk", MFC_P_CLK, NULL, 0), CLK_PCOM("mi2s_m_clk", MI2S_M_CLK, NULL, 0), CLK_PCOM("mi2s_s_clk", MI2S_S_CLK, NULL, 0), - CLK_PCOM("mi2s_codec_rx_m_clk", MI2S_CODEC_RX_M_CLK, NULL, 0), - CLK_PCOM("mi2s_codec_rx_s_clk", MI2S_CODEC_RX_S_CLK, NULL, 0), - CLK_PCOM("mi2s_codec_tx_m_clk", MI2S_CODEC_TX_M_CLK, NULL, 0), - CLK_PCOM("mi2s_codec_tx_s_clk", MI2S_CODEC_TX_S_CLK, NULL, 0), + CLK_PCOM("mi2s_codec_rx_mclk", MI2S_CODEC_RX_M_CLK, NULL, 0), + CLK_PCOM("mi2s_codec_rx_sclk", MI2S_CODEC_RX_S_CLK, NULL, 0), + CLK_PCOM("mi2s_codec_tx_mclk", MI2S_CODEC_TX_M_CLK, NULL, 0), + CLK_PCOM("mi2s_codec_tx_sclk", MI2S_CODEC_TX_S_CLK, NULL, 0), CLK_PCOM("pbus_clk", PBUS_CLK, NULL, CLK_MIN), CLK_PCOM("pcm_clk", PCM_CLK, NULL, 0), CLK_PCOM("rotator_clk", AXI_ROTATOR_CLK, NULL, 0), CLK_PCOM("rotator_imem_clk", ROTATOR_IMEM_CLK, NULL, OFF), CLK_PCOM("rotator_pclk", ROTATOR_P_CLK, NULL, OFF), CLK_PCOM("sdac_clk", SDAC_CLK, NULL, OFF), - CLK_PCOM("spi_clk", SPI_CLK, NULL, 0), - CLK_PCOM("spi_pclk", SPI_P_CLK, NULL, 0), + CLK_PCOM("sdc_clk", SDC1_CLK, &msm_device_sdc1.dev, OFF), + CLK_PCOM("sdc_pclk", SDC1_P_CLK, &msm_device_sdc1.dev, OFF), + CLK_PCOM("sdc_clk", SDC2_CLK, &msm_device_sdc2.dev, OFF), + CLK_PCOM("sdc_pclk", SDC2_P_CLK, &msm_device_sdc2.dev, OFF), + CLK_PCOM("sdc_clk", SDC3_CLK, &msm_device_sdc3.dev, OFF), + CLK_PCOM("sdc_pclk", SDC3_P_CLK, &msm_device_sdc3.dev, OFF), + CLK_PCOM("sdc_clk", SDC4_CLK, &msm_device_sdc4.dev, OFF), + CLK_PCOM("sdc_pclk", SDC4_P_CLK, &msm_device_sdc4.dev, OFF), + CLK_PCOM("spi_clk", SPI_CLK, &msm_device_spi.dev, 0), + CLK_PCOM("spi_pclk", SPI_P_CLK, &msm_device_spi.dev, 0), CLK_7X30S("tv_src_clk", TV_CLK, TV_ENC_CLK, NULL, 0), CLK_PCOM("tv_dac_clk", TV_DAC_CLK, NULL, 0), CLK_PCOM("tv_enc_clk", TV_ENC_CLK, NULL, 0), - CLK_PCOM("uart_clk", UART2_CLK, &msm_device_uart2.dev, 0), + CLK_PCOM("uart_clk", UART1_CLK, &msm_device_uart1.dev, OFF), + CLK_PCOM("uart_clk", UART2_CLK, &msm_device_uart2.dev, OFF), + CLK_PCOM("uart_clk", UART3_CLK, &msm_device_uart3.dev, OFF), + CLK_PCOM("uartdm_clk", UART1DM_CLK, &msm_device_uart_dm1.dev, OFF), + CLK_PCOM("uartdm_clk", UART2DM_CLK, &msm_device_uart_dm2.dev, OFF), CLK_PCOM("usb_phy_clk", USB_PHY_CLK, NULL, 0), CLK_PCOM("usb_hs_clk", USB_HS_CLK, NULL, OFF), CLK_PCOM("usb_hs_pclk", USB_HS_P_CLK, NULL, OFF), - CLK_PCOM("usb_hs_core_clk", USB_HS_CORE_CLK, NULL, OFF), + CLK_PCOM("usb_hs_core_clk", USB_HS_CORE_CLK, &msm_device_hsusb.dev, OFF), CLK_PCOM("usb_hs2_clk", USB_HS2_CLK, NULL, OFF), CLK_PCOM("usb_hs2_pclk", USB_HS2_P_CLK, NULL, OFF), CLK_PCOM("usb_hs2_core_clk", USB_HS2_CORE_CLK, NULL, OFF), @@ -194,6 +741,7 @@ struct clk msm_clocks_7x30[] = { CLK_PCOM("vfe_mdc_clk", VFE_MDC_CLK, NULL, 0), CLK_PCOM("vfe_pclk", VFE_P_CLK, NULL, OFF), CLK_PCOM("vpe_clk", VPE_CLK, NULL, 0), + CLK_PCOM("qup_clk", QUP_I2C_CLK, &msm_device_qup_i2c.dev, OFF), /* 7x30 v2 hardware only. */ CLK_PCOM("csi_clk", CSI0_CLK, NULL, 0), diff --git a/arch/arm/mach-msm/devices-qsd8x50.c b/arch/arm/mach-msm/devices-qsd8x50.c index a4b798f20ccb7..062e2d753bb0c 100644 --- a/arch/arm/mach-msm/devices-qsd8x50.c +++ b/arch/arm/mach-msm/devices-qsd8x50.c @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -23,11 +24,40 @@ #include #include "devices.h" +#include "proc_comm.h" #include +#include +#include #include +static struct resource resources_uart1[] = { + { + .start = INT_UART1, + .end = INT_UART1, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART1_PHYS, + .end = MSM_UART1_PHYS + MSM_UART1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct resource resources_uart2[] = { + { + .start = INT_UART2, + .end = INT_UART2, + .flags = IORESOURCE_IRQ, + }, + { + .start = MSM_UART2_PHYS, + .end = MSM_UART2_PHYS + MSM_UART2_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + static struct resource resources_uart3[] = { { .start = INT_UART3, @@ -41,6 +71,20 @@ static struct resource resources_uart3[] = { }, }; +struct platform_device msm_device_uart1 = { + .name = "msm_serial", + .id = 0, + .num_resources = ARRAY_SIZE(resources_uart1), + .resource = resources_uart1, +}; + +struct platform_device msm_device_uart2 = { + .name = "msm_serial", + .id = 1, + .num_resources = ARRAY_SIZE(resources_uart2), + .resource = resources_uart2, +}; + struct platform_device msm_device_uart3 = { .name = "msm_serial", .id = 2, @@ -53,6 +97,455 @@ struct platform_device msm_device_smd = { .id = -1, }; +static struct resource msm_uart1_dm_resources[] = { + { + .start = MSM_UART1DM_PHYS, + .end = MSM_UART1DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART1DM_IRQ, + .end = INT_UART1DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART1DM_RX, + .end = INT_UART1DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART1_TX_CHAN, + .end = DMOV_HSUART1_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART1_TX_CRCI, + .end = DMOV_HSUART1_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm1_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm1 = { + .name = "msm_serial_hs", + .id = 0, + .num_resources = ARRAY_SIZE(msm_uart1_dm_resources), + .resource = msm_uart1_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm1_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource msm_uart2_dm_resources[] = { + { + .start = MSM_UART2DM_PHYS, + .end = MSM_UART2DM_PHYS + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_UART2DM_IRQ, + .end = INT_UART2DM_IRQ, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_UART2DM_RX, + .end = INT_UART2DM_RX, + .flags = IORESOURCE_IRQ, + }, + { + .start = DMOV_HSUART2_TX_CHAN, + .end = DMOV_HSUART2_RX_CHAN, + .name = "uartdm_channels", + .flags = IORESOURCE_DMA, + }, + { + .start = DMOV_HSUART2_TX_CRCI, + .end = DMOV_HSUART2_RX_CRCI, + .name = "uartdm_crci", + .flags = IORESOURCE_DMA, + }, +}; + +static u64 msm_uart_dm2_dma_mask = DMA_BIT_MASK(32); + +struct platform_device msm_device_uart_dm2 = { + .name = "msm_serial_hs", + .id = 1, + .num_resources = ARRAY_SIZE(msm_uart2_dm_resources), + .resource = msm_uart2_dm_resources, + .dev = { + .dma_mask = &msm_uart_dm2_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static struct resource resources_i2c[] = { + { + .start = MSM_I2C_PHYS, + .end = MSM_I2C_PHYS + MSM_I2C_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_PWB_I2C, + .end = INT_PWB_I2C, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_i2c = { + .name = "msm_i2c", + .id = 0, + .num_resources = ARRAY_SIZE(resources_i2c), + .resource = resources_i2c, +}; + +struct flash_platform_data msm_nand_data = { + .parts = NULL, + .nr_parts = 0, +}; + +static struct resource resources_nand[] = { + [0] = { + .start = 7, + .end = 7, + .flags = IORESOURCE_DMA, + }, +}; + +struct platform_device msm_device_nand = { + .name = "msm_nand", + .id = -1, + .num_resources = ARRAY_SIZE(resources_nand), + .resource = resources_nand, + .dev = { + .platform_data = &msm_nand_data, + }, +}; + +struct platform_device msm_device_smd = { + .name = "msm_smd", + .id = -1, +}; + +static struct resource resources_sdc1[] = { + { + .start = MSM_SDC1_PHYS, + .end = MSM_SDC1_PHYS + MSM_SDC1_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC1_0, + .end = INT_SDC1_0, + .flags = IORESOURCE_IRQ, + .name = "cmd_irq", + }, + { + .start = INT_SDC1_1, + .end = INT_SDC1_1, + .flags = IORESOURCE_IRQ, + .name = "pio_irq", + }, + { + .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, + .name = "status_irq" + }, + { + .start = 8, + .end = 8, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource resources_sdc2[] = { + { + .start = MSM_SDC2_PHYS, + .end = MSM_SDC2_PHYS + MSM_SDC2_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC2_0, + .end = INT_SDC2_0, + .flags = IORESOURCE_IRQ, + .name = "cmd_irq", + }, + { + .start = INT_SDC2_1, + .end = INT_SDC2_1, + .flags = IORESOURCE_IRQ, + .name = "pio_irq", + }, + { + .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, + .name = "status_irq" + }, + { + .start = 8, + .end = 8, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource resources_sdc3[] = { + { + .start = MSM_SDC3_PHYS, + .end = MSM_SDC3_PHYS + MSM_SDC3_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC3_0, + .end = INT_SDC3_0, + .flags = IORESOURCE_IRQ, + .name = "cmd_irq", + }, + { + .start = INT_SDC3_1, + .end = INT_SDC3_1, + .flags = IORESOURCE_IRQ, + .name = "pio_irq", + }, + { + .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, + .name = "status_irq" + }, + { + .start = 8, + .end = 8, + .flags = IORESOURCE_DMA, + }, +}; + +static struct resource resources_sdc4[] = { + { + .start = MSM_SDC4_PHYS, + .end = MSM_SDC4_PHYS + MSM_SDC4_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SDC4_0, + .end = INT_SDC4_0, + .flags = IORESOURCE_IRQ, + .name = "cmd_irq", + }, + { + .start = INT_SDC4_1, + .end = INT_SDC4_1, + .flags = IORESOURCE_IRQ, + .name = "pio_irq", + }, + { + .flags = IORESOURCE_IRQ | IORESOURCE_DISABLED, + .name = "status_irq" + }, + { + .start = 8, + .end = 8, + .flags = IORESOURCE_DMA, + }, +}; + +struct platform_device msm_device_sdc1 = { + .name = "msm_sdcc", + .id = 1, + .num_resources = ARRAY_SIZE(resources_sdc1), + .resource = resources_sdc1, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc2 = { + .name = "msm_sdcc", + .id = 2, + .num_resources = ARRAY_SIZE(resources_sdc2), + .resource = resources_sdc2, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc3 = { + .name = "msm_sdcc", + .id = 3, + .num_resources = ARRAY_SIZE(resources_sdc3), + .resource = resources_sdc3, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_sdc4 = { + .name = "msm_sdcc", + .id = 4, + .num_resources = ARRAY_SIZE(resources_sdc4), + .resource = resources_sdc4, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct platform_device *msm_sdcc_devices[] __initdata = { + &msm_device_sdc1, + &msm_device_sdc2, + &msm_device_sdc3, + &msm_device_sdc4, +}; + +int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, + unsigned int stat_irq, unsigned long stat_irq_flags) +{ + struct platform_device *pdev; + struct resource *res; + + if (controller < 1 || controller > 4) + return -EINVAL; + + pdev = msm_sdcc_devices[controller-1]; + pdev->dev.platform_data = plat; + + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "status_irq"); + if (!res) + return -EINVAL; + else if (stat_irq) { + res->start = res->end = stat_irq; + res->flags &= ~IORESOURCE_DISABLED; + res->flags |= stat_irq_flags; + } + + return platform_device_register(pdev); +} + +static struct resource resources_mddi0[] = { + { + .start = MSM_PMDH_PHYS, + .end = MSM_PMDH_PHYS + MSM_PMDH_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_MDDI_PRI, + .end = INT_MDDI_PRI, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource resources_mddi1[] = { + { + .start = MSM_EMDH_PHYS, + .end = MSM_EMDH_PHYS + MSM_EMDH_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_MDDI_EXT, + .end = INT_MDDI_EXT, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_mddi0 = { + .name = "msm_mddi", + .id = 0, + .num_resources = ARRAY_SIZE(resources_mddi0), + .resource = resources_mddi0, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +struct platform_device msm_device_mddi1 = { + .name = "msm_mddi", + .id = 1, + .num_resources = ARRAY_SIZE(resources_mddi1), + .resource = resources_mddi1, + .dev = { + .coherent_dma_mask = 0xffffffff, + }, +}; + +static struct resource resources_mdp[] = { + { + .start = MSM_MDP_PHYS, + .end = MSM_MDP_PHYS + MSM_MDP_SIZE - 1, + .name = "mdp", + .flags = IORESOURCE_MEM + }, + { + .start = INT_MDP, + .end = INT_MDP, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_mdp = { + .name = "msm_mdp", + .id = 0, + .num_resources = ARRAY_SIZE(resources_mdp), + .resource = resources_mdp, +}; + +static struct resource resources_tssc[] = { + { + .start = MSM_TSSC_PHYS, + .end = MSM_TSSC_PHYS + MSM_TSSC_SIZE - 1, + .name = "tssc", + .flags = IORESOURCE_MEM, + }, + { + .start = INT_TCHSCRN1, + .end = INT_TCHSCRN1, + .name = "tssc1", + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING, + }, + { + .start = INT_TCHSCRN2, + .end = INT_TCHSCRN2, + .name = "tssc2", + .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING, + }, +}; + +struct platform_device msm_device_touchscreen = { + .name = "msm_touchscreen", + .id = 0, + .num_resources = ARRAY_SIZE(resources_tssc), + .resource = resources_tssc, +}; + +static struct resource resources_spi[] = { + { + .start = MSM_SPI_PHYS, + .end = MSM_SPI_PHYS + MSM_SPI_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_SPI_INPUT, + .end = INT_SPI_INPUT, + .name = "irq_in", + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_SPI_OUTPUT, + .end = INT_SPI_OUTPUT, + .name = "irq_out", + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_SPI_ERROR, + .end = INT_SPI_ERROR, + .name = "irq_err", + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_spi = { + .name = "msm_spi", + .id = 0, + .num_resources = ARRAY_SIZE(resources_spi), + .resource = resources_spi, +}; + static struct resource resources_otg[] = { { .start = MSM_HSUSB_PHYS, @@ -126,32 +619,45 @@ struct platform_device msm_device_hsusb_host = { struct clk msm_clocks_8x50[] = { CLK_PCOM("adm_clk", ADM_CLK, NULL, 0), - CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN), + CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN | CLKFLAG_SHARED), CLK_PCOM("ebi2_clk", EBI2_CLK, NULL, 0), CLK_PCOM("ecodec_clk", ECODEC_CLK, NULL, 0), - CLK_PCOM("emdh_clk", EMDH_CLK, NULL, OFF | CLK_MINMAX), + CLK_PCOM("mddi_clk", EMDH_CLK, &msm_device_mddi1.dev, OFF | CLK_MINMAX), CLK_PCOM("gp_clk", GP_CLK, NULL, 0), CLK_PCOM("grp_clk", GRP_3D_CLK, NULL, 0), + CLK_PCOM("i2c_clk", I2C_CLK, &msm_device_i2c.dev, 0), CLK_PCOM("icodec_rx_clk", ICODEC_RX_CLK, NULL, 0), CLK_PCOM("icodec_tx_clk", ICODEC_TX_CLK, NULL, 0), CLK_PCOM("imem_clk", IMEM_CLK, NULL, OFF), CLK_PCOM("mdc_clk", MDC_CLK, NULL, 0), - CLK_PCOM("mddi_clk", PMDH_CLK, NULL, OFF | CLK_MINMAX), - CLK_PCOM("mdp_clk", MDP_CLK, NULL, OFF), - CLK_PCOM("mdp_lcdc_pclk_clk", MDP_LCDC_PCLK_CLK, NULL, 0), - CLK_PCOM("mdp_lcdc_pad_pclk_clk", MDP_LCDC_PAD_PCLK_CLK, NULL, 0), - CLK_PCOM("mdp_vsync_clk", MDP_VSYNC_CLK, NULL, 0), + CLK_PCOM("mddi_clk", PMDH_CLK, &msm_device_mddi0.dev, OFF | CLK_MINMAX), + CLK_PCOM("mdp_clk", MDP_CLK, &msm_device_mdp.dev, OFF), + CLK_PCOM("lcdc_pclk_clk", MDP_LCDC_PCLK_CLK, &msm_device_mdp.dev, 0), + CLK_PCOM("lcdc_pad_pclk_clk", MDP_LCDC_PAD_PCLK_CLK, &msm_device_mdp.dev, 0), + CLK_PCOM("mdp_vsync_clk", MDP_VSYNC_CLK, &msm_device_mdp.dev, 0), CLK_PCOM("pbus_clk", PBUS_CLK, NULL, CLK_MIN), CLK_PCOM("pcm_clk", PCM_CLK, NULL, 0), CLK_PCOM("sdac_clk", SDAC_CLK, NULL, OFF), - CLK_PCOM("spi_clk", SPI_CLK, NULL, 0), + CLK_PCOM("spi_clk", SPI_CLK, &msm_device_spi.dev, 0), + CLK_PCOM("sdc_clk", SDC1_CLK, &msm_device_sdc1.dev, OFF), + CLK_PCOM("sdc_pclk", SDC1_P_CLK, &msm_device_sdc1.dev, OFF), + CLK_PCOM("sdc_clk", SDC2_CLK, &msm_device_sdc2.dev, OFF), + CLK_PCOM("sdc_pclk", SDC2_P_CLK, &msm_device_sdc2.dev, OFF), + CLK_PCOM("sdc_clk", SDC3_CLK, &msm_device_sdc3.dev, OFF), + CLK_PCOM("sdc_pclk", SDC3_P_CLK, &msm_device_sdc3.dev, OFF), + CLK_PCOM("sdc_clk", SDC4_CLK, &msm_device_sdc4.dev, OFF), + CLK_PCOM("sdc_pclk", SDC4_P_CLK, &msm_device_sdc4.dev, OFF), CLK_PCOM("tsif_clk", TSIF_CLK, NULL, 0), CLK_PCOM("tsif_ref_clk", TSIF_REF_CLK, NULL, 0), CLK_PCOM("tv_dac_clk", TV_DAC_CLK, NULL, 0), CLK_PCOM("tv_enc_clk", TV_ENC_CLK, NULL, 0), + CLK_PCOM("uart_clk", UART1_CLK, &msm_device_uart1.dev, OFF), + CLK_PCOM("uart_clk", UART2_CLK, &msm_device_uart2.dev, OFF), CLK_PCOM("uart_clk", UART3_CLK, &msm_device_uart3.dev, OFF), - CLK_PCOM("usb_hs_clk", USB_HS_CLK, NULL, OFF), - CLK_PCOM("usb_hs_pclk", USB_HS_P_CLK, NULL, OFF), + CLK_PCOM("uartdm_clk", UART1DM_CLK, &msm_device_uart_dm1.dev, OFF), + CLK_PCOM("uartdm_clk", UART2DM_CLK, &msm_device_uart_dm2.dev, OFF), + CLK_PCOM("usb_hs_clk", USB_HS_CLK, &msm_device_hsusb.dev, OFF), + CLK_PCOM("usb_hs_pclk", USB_HS_P_CLK, &msm_device_hsusb.dev, OFF), CLK_PCOM("usb_otg_clk", USB_OTG_CLK, NULL, 0), CLK_PCOM("vdc_clk", VDC_CLK, NULL, OFF | CLK_MIN), CLK_PCOM("vfe_clk", VFE_CLK, NULL, OFF), diff --git a/arch/arm/mach-msm/include/mach/dma.h b/arch/arm/mach-msm/include/mach/dma.h index 05583f5695244..0902682ad636d 100644 --- a/arch/arm/mach-msm/include/mach/dma.h +++ b/arch/arm/mach-msm/include/mach/dma.h @@ -108,6 +108,19 @@ int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr) { return -EIO; } #define DMOV_USB_CHAN 11 +#define DMOV_HSUART1_TX_CHAN 4 +#define DMOV_HSUART1_TX_CRCI 8 + +#define DMOV_HSUART1_RX_CHAN 9 +#define DMOV_HSUART1_RX_CRCI 9 + +#define DMOV_HSUART2_TX_CHAN 4 +#define DMOV_HSUART2_TX_CRCI 14 + +#define DMOV_HSUART2_RX_CHAN 11 +#define DMOV_HSUART2_RX_CRCI 15 + + /* no client rate control ifc (eg, ram) */ #define DMOV_NONE_CRCI 0 diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h b/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h index 7978c6f77f126..571391b605bbb 100644 --- a/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h +++ b/arch/arm/mach-msm/include/mach/msm_iomap-7x00.h @@ -51,9 +51,12 @@ #define MSM_CSR_PHYS 0xC0100000 #define MSM_CSR_SIZE SZ_4K -#define MSM_GPT_PHYS MSM_CSR_PHYS -#define MSM_GPT_BASE MSM_CSR_BASE -#define MSM_GPT_SIZE SZ_4K +#define MSM_TMR_PHYS MSM_CSR_PHYS +#define MSM_TMR_BASE MSM_CSR_BASE +#define MSM_TMR_SIZE SZ_4K + +#define MSM_GPT_BASE MSM_TMR_BASE +#define MSM_DGT_BASE (MSM_TMR_BASE + 0x10) #define MSM_DMOV_BASE IOMEM(0xF8002000) #define MSM_DMOV_PHYS 0xA9700000 @@ -108,6 +111,9 @@ #define MSM_SDC4_PHYS 0xA0700000 #define MSM_SDC4_SIZE SZ_4K +#define MSM_NAND_PHYS 0xA0A00000 +#define MSM_NAND_SIZE SZ_4K + #define MSM_I2C_PHYS 0xA9900000 #define MSM_I2C_SIZE SZ_4K @@ -131,6 +137,17 @@ #define MSM_AD5_PHYS 0xAC000000 #define MSM_AD5_SIZE (SZ_1M*13) +#define MSM_VFE_PHYS 0xA0F00000 +#define MSM_VFE_SIZE SZ_1M + +#define MSM_UART1DM_PHYS 0xA0200000 +#define MSM_UART2DM_PHYS 0xA0300000 + +#define MSM_SSBI_PHYS 0xA8100000 +#define MSM_SSBI_SIZE SZ_4K + +#define MSM_TSSC_PHYS 0xAA300000 +#define MSM_TSSC_SIZE SZ_4K #if defined(CONFIG_ARCH_MSM7X30) #define MSM_GCC_BASE IOMEM(0xF8009000) diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h b/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h index 96c2e8c2cbd0e..9166b4c12a33a 100644 --- a/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h +++ b/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h @@ -122,4 +122,58 @@ #define MSM_HSUSB_PHYS 0xA3600000 #define MSM_HSUSB_SIZE SZ_1K +#define MSM_VFE_PHYS 0xA0F00000 +#define MSM_VFE_SIZE SZ_1M + +#define MSM_I2C_SIZE SZ_4K +#define MSM_I2C_PHYS 0xACD00000 + +#define MSM_I2C_2_PHYS 0xACF00000 +#define MSM_I2C_2_SIZE SZ_4K + +#define MSM_QUP_PHYS 0xA8301000 +#define MSM_QUP_SIZE SZ_4K + +#define MSM_GSBI_QUP_I2C_PHYS 0xA8300000 +#define MSM_GSBI_QUP_I2C_SIZE 4 + +#define MSM_PMDH_PHYS 0xAD600000 +#define MSM_PMDH_SIZE SZ_4K + +#define MSM_EMDH_PHYS 0xAD700000 +#define MSM_EMDH_SIZE SZ_4K + +#define MSM_MDP_PHYS 0xA3F00000 +#define MSM_MDP_SIZE 0x000F0000 + +#define MSM_UART1DM_PHYS 0xA0200000 +#define MSM_UART2DM_PHYS 0xA0300000 + +#define MSM_TSSC_PHYS 0xAA300000 +#define MSM_TSSC_SIZE SZ_4K + +#define MSM_SDC1_PHYS 0xA0400000 +#define MSM_SDC1_SIZE SZ_4K + +#define MSM_SDC2_PHYS 0xA0500000 +#define MSM_SDC2_SIZE SZ_4K + +#define MSM_SDC3_PHYS 0xA3000000 +#define MSM_SDC3_SIZE SZ_4K + +#define MSM_SDC4_PHYS 0xA3100000 +#define MSM_SDC4_SIZE SZ_4K + +#define MSM_NAND_PHYS 0xA0200000 +#define MSM_NAND_SIZE SZ_4K + +#define MSM_PMIC_SSBI_PHYS 0xAD900000 +#define MSM_PMIC_SSBI_SIZE SZ_4K + +#define MSM_GPU_REG_PHYS 0xA3500000 +#define MSM_GPU_REG_SIZE SZ_128K + +#define MSM_SPI_PHYS 0xA8000000 +#define MSM_SPI_SIZE SZ_4K + #endif diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h b/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h index a2b0b0bfdbfe2..ed772ddc70d91 100644 --- a/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h +++ b/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h @@ -74,6 +74,10 @@ #define MSM_SCPLL_PHYS 0xA8800000 #define MSM_SCPLL_SIZE SZ_4K +#define MSM_TCSR_BASE IOMEM(0xF8008000) +#define MSM_TCSR_PHYS 0xA8700000 +#define MSM_TCSR_SIZE SZ_4K + #ifdef CONFIG_MSM_SOC_REV_A #define MSM_SMI_BASE 0xE0000000 #else @@ -113,6 +117,8 @@ #define MSM_AD5_PHYS 0xAC000000 #define MSM_AD5_SIZE (SZ_1M*13) +#define MSM_VFE_PHYS 0xA0F00000 +#define MSM_VFE_SIZE SZ_1M #define MSM_I2C_SIZE SZ_4K #define MSM_I2C_PHYS 0xA9900000 @@ -120,8 +126,17 @@ #define MSM_HSUSB_PHYS 0xA0800000 #define MSM_HSUSB_SIZE SZ_1K -#define MSM_NAND_PHYS 0xA0A00000 +#define MSM_PMDH_PHYS 0xAA600000 +#define MSM_PMDH_SIZE SZ_4K + +#define MSM_EMDH_PHYS 0xAA700000 +#define MSM_EMDH_SIZE SZ_4K + +#define MSM_MDP_PHYS 0xAA200000 +#define MSM_MDP_SIZE 0x000F0000 +#define MSM_NAND_PHYS 0xA0A00000 +#define MSM_NAND_SIZE SZ_4K #define MSM_TSIF_PHYS (0xa0100000) #define MSM_TSIF_SIZE (0x200) @@ -131,17 +146,25 @@ #define MSM_UART1DM_PHYS 0xA0200000 #define MSM_UART2DM_PHYS 0xA0900000 +#define MSM_TSSC_PHYS 0xAA300000 +#define MSM_TSSC_SIZE SZ_4K -#define MSM_SDC1_PHYS 0xA0400000 +#define MSM_SDC1_PHYS 0xA0300000 #define MSM_SDC1_SIZE SZ_4K -#define MSM_SDC2_PHYS 0xA0500000 +#define MSM_SDC2_PHYS 0xA0400000 #define MSM_SDC2_SIZE SZ_4K -#define MSM_SDC3_PHYS 0xA0600000 +#define MSM_SDC3_PHYS 0xA0500000 #define MSM_SDC3_SIZE SZ_4K -#define MSM_SDC4_PHYS 0xA0700000 +#define MSM_SDC4_PHYS 0xA0600000 #define MSM_SDC4_SIZE SZ_4K +#define MSM_GPU_REG_PHYS 0xA0000000 +#define MSM_GPU_REG_SIZE 0x00020000 + +#define MSM_SPI_PHYS 0xA1200000 +#define MSM_SPI_SIZE SZ_4K + #endif diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c index 1260007a9dd1a..e2796f56b7b9c 100644 --- a/arch/arm/mach-msm/io.c +++ b/arch/arm/mach-msm/io.c @@ -40,7 +40,7 @@ static struct map_desc msm_io_desc[] __initdata = { MSM_DEVICE(VIC), MSM_DEVICE(CSR), - MSM_DEVICE(GPT), + MSM_DEVICE(TMR), MSM_DEVICE(DMOV), MSM_DEVICE(GPIO1), MSM_DEVICE(GPIO2), From 12602ccfc06559ee77b3eed59fed07ba01346386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 18 Jun 2010 20:42:20 -0700 Subject: [PATCH 0146/2556] [ARM] msm: gpio: Add GPIO_CFG_ prefix to GPIO_CFG argument to avoid conflict with proc_comm.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I1b04a45ee9106465ef08d294210f48bb03624951 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/include/mach/gpio.h | 51 +++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/arch/arm/mach-msm/include/mach/gpio.h b/arch/arm/mach-msm/include/mach/gpio.h index 36ad50d3bfaa8..65796f24666a4 100644 --- a/arch/arm/mach-msm/include/mach/gpio.h +++ b/arch/arm/mach-msm/include/mach/gpio.h @@ -18,6 +18,57 @@ #include +/* GPIO TLMM (Top Level Multiplexing) Definitions */ + +/* GPIO TLMM: Function -- GPIO specific */ + +/* GPIO TLMM: Direction */ +enum { + GPIO_CFG_INPUT, + GPIO_CFG_OUTPUT, +}; + +/* GPIO TLMM: Pullup/Pulldown */ +enum { + GPIO_CFG_NO_PULL, + GPIO_CFG_PULL_DOWN, + GPIO_CFG_KEEPER, + GPIO_CFG_PULL_UP, +}; + +/* GPIO TLMM: Drive Strength */ +enum { + GPIO_CFG_2MA, + GPIO_CFG_4MA, + GPIO_CFG_6MA, + GPIO_CFG_8MA, + GPIO_CFG_10MA, + GPIO_CFG_12MA, + GPIO_CFG_14MA, + GPIO_CFG_16MA, +}; + +enum { + GPIO_CFG_ENABLE, + GPIO_CFG_DISABLE, +}; + +#define GPIO_CFG(gpio, func, dir, pull, drvstr) \ + ((((gpio) & 0x3FF) << 4) | \ + ((func) & 0xf) | \ + (((dir) & 0x1) << 14) | \ + (((pull) & 0x3) << 15) | \ + (((drvstr) & 0xF) << 17)) + +/** + * extract GPIO pin from bit-field used for gpio_tlmm_config + */ +#define GPIO_PIN(gpio_cfg) (((gpio_cfg) >> 4) & 0x3ff) +#define GPIO_FUNC(gpio_cfg) (((gpio_cfg) >> 0) & 0xf) +#define GPIO_DIR(gpio_cfg) (((gpio_cfg) >> 14) & 0x1) +#define GPIO_PULL(gpio_cfg) (((gpio_cfg) >> 15) & 0x3) +#define GPIO_DRVSTR(gpio_cfg) (((gpio_cfg) >> 17) & 0xf) + #define gpio_get_value __gpio_get_value #define gpio_set_value __gpio_set_value #define gpio_cansleep __gpio_cansleep From fa6afc9122cdba510aed8b6e5bc25faf835eb831 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Mon, 12 Jul 2010 16:17:34 -0700 Subject: [PATCH 0147/2556] [ARM] msm: fix build warning. Change-Id: I9b80ae029207360bb101bdedfa10d951fac9ba87 Signed-off-by: Nick Pelly --- arch/arm/mach-msm/clock-pcom.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/clock-pcom.h b/arch/arm/mach-msm/clock-pcom.h index 17d027b235011..18dcdd53a7690 100644 --- a/arch/arm/mach-msm/clock-pcom.h +++ b/arch/arm/mach-msm/clock-pcom.h @@ -137,6 +137,7 @@ struct clk_ops; extern struct clk_ops clk_ops_pcom; +enum clk_reset_action; int pc_clk_reset(unsigned id, enum clk_reset_action action); From b23c22961b71ba250bdba52cacf1bb6684490870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 15 Sep 2008 16:45:23 -0700 Subject: [PATCH 0148/2556] [ARM] mtd: msm nand driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Id29ce26e050fdb2fe95c7f8df0eb1a3a6a9c1e66 Signed-off-by: Brian Swetland Signed-off-by: Arve Hjønnevåg --- drivers/mtd/devices/Kconfig | 7 + drivers/mtd/devices/Makefile | 1 + drivers/mtd/devices/msm_nand.c | 1350 ++++++++++++++++++++++++++++++++ drivers/mtd/devices/msm_nand.h | 71 ++ 4 files changed, 1429 insertions(+) create mode 100644 drivers/mtd/devices/msm_nand.c create mode 100644 drivers/mtd/devices/msm_nand.h diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 35081ce77fbdd..4644a7b4810d7 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -49,6 +49,13 @@ config MTD_MS02NV say M here and read . The module will be called ms02-nv. +config MTD_MSM_NAND + tristate "MSM NAND Device Support" + depends on MTD && ARCH_MSM + default y + help + Support for some NAND chips connected to the MSM NAND controller. + config MTD_DATAFLASH tristate "Support for AT45xxx DataFlash" depends on SPI_MASTER && EXPERIMENTAL diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index f3226b1d38fca..fe959e82250bd 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_MTD_SLRAM) += slram.o obj-$(CONFIG_MTD_PHRAM) += phram.o obj-$(CONFIG_MTD_PMC551) += pmc551.o obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o +obj-$(CONFIG_MTD_MSM_NAND) += msm_nand.o obj-$(CONFIG_MTD_MTDRAM) += mtdram.o obj-$(CONFIG_MTD_LART) += lart.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c new file mode 100644 index 0000000000000..04721a24a23a4 --- /dev/null +++ b/drivers/mtd/devices/msm_nand.c @@ -0,0 +1,1350 @@ +/* drivers/mtd/devices/msm_nand.c + * + * Copyright (C) 2007 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 +#include +#include +#include +#include + +#include +#include + +#include + +#define MSM_NAND_BASE 0xA0A00000 +#include "msm_nand.h" + +#define MSM_NAND_DMA_BUFFER_SIZE SZ_4K +#define MSM_NAND_DMA_BUFFER_SLOTS \ + (MSM_NAND_DMA_BUFFER_SIZE / (sizeof(((atomic_t *)0)->counter) * 8)) + +#define SUPPORT_WRONG_ECC_CONFIG 1 + +#define VERBOSE 0 + +struct msm_nand_chip { + struct device *dev; + wait_queue_head_t wait_queue; + atomic_t dma_buffer_busy; + unsigned dma_channel; + uint8_t *dma_buffer; + dma_addr_t dma_addr; + unsigned CFG0, CFG1; +#if SUPPORT_WRONG_ECC_CONFIG + uint32_t ecc_buf_cfg; + uint32_t saved_ecc_buf_cfg; +#endif +}; + +#define CFG1_WIDE_FLASH (1U << 1) + +/* TODO: move datamover code out */ + +#define SRC_CRCI_NAND_CMD CMD_SRC_CRCI(DMOV_NAND_CRCI_CMD) +#define DST_CRCI_NAND_CMD CMD_DST_CRCI(DMOV_NAND_CRCI_CMD) +#define SRC_CRCI_NAND_DATA CMD_SRC_CRCI(DMOV_NAND_CRCI_DATA) +#define DST_CRCI_NAND_DATA CMD_DST_CRCI(DMOV_NAND_CRCI_DATA) + +#define msm_virt_to_dma(chip, vaddr) \ + ((void)(*(vaddr)), (chip)->dma_addr + \ + ((uint8_t *)(vaddr) - (chip)->dma_buffer)) + +/** + * msm_nand_oob_64 - oob info for large (2KB) page + */ +static struct nand_ecclayout msm_nand_oob_64 = { + .eccbytes = 40, + .eccpos = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + }, + .oobavail = 16, + .oobfree = { + {30, 16}, + } +}; + +static void *msm_nand_get_dma_buffer(struct msm_nand_chip *chip, size_t size) +{ + unsigned int bitmask, free_bitmask, old_bitmask; + unsigned int need_mask, current_need_mask; + int free_index; + + need_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOTS)) - 1; + bitmask = atomic_read(&chip->dma_buffer_busy); + free_bitmask = ~bitmask; + do { + free_index = __ffs(free_bitmask); + current_need_mask = need_mask << free_index; + if ((bitmask & current_need_mask) == 0) { + old_bitmask = + atomic_cmpxchg(&chip->dma_buffer_busy, + bitmask, + bitmask | current_need_mask); + if (old_bitmask == bitmask) + return chip->dma_buffer + + free_index * MSM_NAND_DMA_BUFFER_SLOTS; + free_bitmask = 0; /* force return */ + } + /* current free range was too small, clear all free bits */ + /* below the top busy bit within current_need_mask */ + free_bitmask &= + ~(~0U >> (32 - fls(bitmask & current_need_mask))); + } while (free_bitmask); + + return NULL; +} + +static void msm_nand_release_dma_buffer(struct msm_nand_chip *chip, + void *buffer, size_t size) +{ + int index; + unsigned int used_mask; + + used_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOTS)) - 1; + index = ((uint8_t *)buffer - chip->dma_buffer) / + MSM_NAND_DMA_BUFFER_SLOTS; + atomic_sub(used_mask << index, &chip->dma_buffer_busy); + + wake_up(&chip->wait_queue); +} + +uint32_t flash_read_id(struct msm_nand_chip *chip) +{ + struct { + dmov_s cmd[5]; + unsigned cmdptr; + unsigned data[5]; + } *dma_buffer; + uint32_t rv; + + wait_event(chip->wait_queue, + (dma_buffer = msm_nand_get_dma_buffer( + chip, sizeof(*dma_buffer)))); + + dma_buffer->data[0] = 0 | 4; + dma_buffer->data[1] = NAND_CMD_FETCH_ID; + dma_buffer->data[2] = 1; + dma_buffer->data[3] = 0xeeeeeeee; + dma_buffer->data[4] = 0xeeeeeeee; + BUILD_BUG_ON(4 != ARRAY_SIZE(dma_buffer->data) - 1); + + dma_buffer->cmd[0].cmd = 0 | CMD_OCB; + dma_buffer->cmd[0].src = msm_virt_to_dma(chip, &dma_buffer->data[0]); + dma_buffer->cmd[0].dst = NAND_FLASH_CHIP_SELECT; + dma_buffer->cmd[0].len = 4; + + dma_buffer->cmd[1].cmd = DST_CRCI_NAND_CMD; + dma_buffer->cmd[1].src = msm_virt_to_dma(chip, &dma_buffer->data[1]); + dma_buffer->cmd[1].dst = NAND_FLASH_CMD; + dma_buffer->cmd[1].len = 4; + + dma_buffer->cmd[2].cmd = 0; + dma_buffer->cmd[2].src = msm_virt_to_dma(chip, &dma_buffer->data[2]); + dma_buffer->cmd[2].dst = NAND_EXEC_CMD; + dma_buffer->cmd[2].len = 4; + + dma_buffer->cmd[3].cmd = SRC_CRCI_NAND_DATA; + dma_buffer->cmd[3].src = NAND_FLASH_STATUS; + dma_buffer->cmd[3].dst = msm_virt_to_dma(chip, &dma_buffer->data[3]); + dma_buffer->cmd[3].len = 4; + + dma_buffer->cmd[4].cmd = CMD_OCU | CMD_LC; + dma_buffer->cmd[4].src = NAND_READ_ID; + dma_buffer->cmd[4].dst = msm_virt_to_dma(chip, &dma_buffer->data[4]); + dma_buffer->cmd[4].len = 4; + BUILD_BUG_ON(4 != ARRAY_SIZE(dma_buffer->cmd) - 1); + + dma_buffer->cmdptr = + (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; + + msm_dmov_exec_cmd( + chip->dma_channel, DMOV_CMD_PTR_LIST | + DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + + pr_info("status: %x\n", dma_buffer->data[3]); + pr_info("nandid: %x maker %02x device %02x\n", + dma_buffer->data[4], dma_buffer->data[4] & 0xff, + (dma_buffer->data[4] >> 8) & 0xff); + rv = dma_buffer->data[4]; + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + return rv; +} + +int flash_read_config(struct msm_nand_chip *chip) +{ + struct { + dmov_s cmd[2]; + unsigned cmdptr; + unsigned cfg0; + unsigned cfg1; + } *dma_buffer; + + wait_event(chip->wait_queue, + (dma_buffer = msm_nand_get_dma_buffer( + chip, sizeof(*dma_buffer)))); + dma_buffer->cfg0 = 0; + dma_buffer->cfg1 = 0; + + dma_buffer->cmd[0].cmd = CMD_OCB; + dma_buffer->cmd[0].src = NAND_DEV0_CFG0; + dma_buffer->cmd[0].dst = msm_virt_to_dma(chip, &dma_buffer->cfg0); + dma_buffer->cmd[0].len = 4; + + dma_buffer->cmd[1].cmd = CMD_OCU | CMD_LC; + dma_buffer->cmd[1].src = NAND_DEV0_CFG1; + dma_buffer->cmd[1].dst = msm_virt_to_dma(chip, &dma_buffer->cfg1); + dma_buffer->cmd[1].len = 4; + BUILD_BUG_ON(1 != ARRAY_SIZE(dma_buffer->cmd) - 1); + + dma_buffer->cmdptr = + (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; + + msm_dmov_exec_cmd( + chip->dma_channel, DMOV_CMD_PTR_LIST | + DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + + chip->CFG0 = dma_buffer->cfg0; + chip->CFG1 = dma_buffer->cfg1; + pr_info("read CFG0 = %x, CFG1 = %x\n", chip->CFG0, chip->CFG1); + chip->CFG0 = (3 << 6) /* 4 codeword per page for 2k nand */ + | (516 << 9) /* 516 user data bytes */ + | (10 << 19) /* 10 parity bytes */ + | (5 << 27) /* 5 address cycles */ + | (1 << 30) /* Read status before data */ + | (1 << 31) /* Send read cmd */ + /* 0 spare bytes for 16 bit nand or 1 spare bytes for 8 bit */ + | ((chip->CFG1 & CFG1_WIDE_FLASH) ? (0 << 23) : (1 << 23)); + chip->CFG1 = (0 << 0) /* Enable ecc */ + | (7 << 2) /* 8 recovery cycles */ + | (0 << 5) /* Allow CS deassertion */ + | (465 << 6) /* Bad block marker location */ + | (0 << 16) /* Bad block in user data area */ + | (2 << 17) /* 6 cycle tWB/tRB */ + | (chip->CFG1 & CFG1_WIDE_FLASH); /* preserve wide flag */ + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + + if ((chip->CFG0 == 0) || (chip->CFG1 == 0)) + return -1; + + return 0; +} + +unsigned flash_rd_reg(struct msm_nand_chip *chip, unsigned addr) +{ + struct { + dmov_s cmd; + unsigned cmdptr; + unsigned data; + } *dma_buffer; + unsigned rv; + + wait_event(chip->wait_queue, + (dma_buffer = msm_nand_get_dma_buffer( + chip, sizeof(*dma_buffer)))); + + dma_buffer->cmd.cmd = CMD_LC; + dma_buffer->cmd.src = addr; + dma_buffer->cmd.dst = msm_virt_to_dma(chip, &dma_buffer->data); + dma_buffer->cmd.len = 4; + + dma_buffer->cmdptr = + (msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP; + dma_buffer->data = 0xeeeeeeee; + + msm_dmov_exec_cmd( + chip->dma_channel, DMOV_CMD_PTR_LIST | + DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + rv = dma_buffer->data; + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + + return rv; +} + +void flash_wr_reg(struct msm_nand_chip *chip, unsigned addr, unsigned val) +{ + struct { + dmov_s cmd; + unsigned cmdptr; + unsigned data; + } *dma_buffer; + + wait_event(chip->wait_queue, + (dma_buffer = msm_nand_get_dma_buffer( + chip, sizeof(*dma_buffer)))); + + dma_buffer->cmd.cmd = CMD_LC; + dma_buffer->cmd.src = msm_virt_to_dma(chip, &dma_buffer->data); + dma_buffer->cmd.dst = addr; + dma_buffer->cmd.len = 4; + + dma_buffer->cmdptr = + (msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP; + dma_buffer->data = val; + + msm_dmov_exec_cmd( + chip->dma_channel, DMOV_CMD_PTR_LIST | + DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); +} + +static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct msm_nand_chip *chip = mtd->priv; + + struct { + dmov_s cmd[4 * 5 + 3]; + unsigned cmdptr; + struct { + uint32_t cmd; + uint32_t addr0; + uint32_t addr1; + uint32_t chipsel; + uint32_t cfg0; + uint32_t cfg1; + uint32_t exec; +#if SUPPORT_WRONG_ECC_CONFIG + uint32_t ecccfg; + uint32_t ecccfg_restore; +#endif + struct { + uint32_t flash_status; + uint32_t buffer_status; + } result[4]; + } data; + } *dma_buffer; + dmov_s *cmd; + unsigned n; + unsigned page = from / 2048; + uint32_t oob_len = ops->ooblen; + uint32_t sectordatasize; + uint32_t sectoroobsize; + int err, pageerr, rawerr; + dma_addr_t data_dma_addr = 0; + dma_addr_t oob_dma_addr = 0; + dma_addr_t data_dma_addr_curr = 0; + dma_addr_t oob_dma_addr_curr = 0; + uint32_t oob_col = 0; + unsigned page_count; + unsigned pages_read = 0; + unsigned start_sector = 0; + uint32_t ecc_errors; + uint32_t total_ecc_errors = 0; + + if (from & (mtd->writesize - 1)) { + pr_err("%s: unsupported from, 0x%llx\n", + __func__, from); + return -EINVAL; + } + if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { + /* when ops->datbuf is NULL, ops->len may refer to ooblen */ + pr_err("%s: unsupported ops->len, %d\n", + __func__, ops->len); + return -EINVAL; + } + if (ops->ooblen != 0 && ops->ooboffs != 0) { + pr_err("%s: unsupported ops->ooboffs, %d\n", + __func__, ops->ooboffs); + return -EINVAL; + } + + if (ops->oobbuf && !ops->datbuf && ops->mode == MTD_OOB_AUTO) + start_sector = 3; + + if (ops->oobbuf && !ops->datbuf) + page_count = ops->ooblen / ((ops->mode == MTD_OOB_AUTO) ? + mtd->oobavail : mtd->oobsize); + else + page_count = ops->len / mtd->writesize; + +#if 0 /* yaffs reads more oob data than it needs */ + if (ops->ooblen >= sectoroobsize * 4) { + pr_err("%s: unsupported ops->ooblen, %d\n", + __func__, ops->ooblen); + return -EINVAL; + } +#endif + +#if VERBOSE + pr_info("msm_nand_read_oob %llx %p %x %p %x\n", + from, ops->datbuf, ops->len, ops->oobbuf, ops->ooblen); +#endif + if (ops->datbuf) { + /* memset(ops->datbuf, 0x55, ops->len); */ + data_dma_addr_curr = data_dma_addr = + dma_map_single(chip->dev, ops->datbuf, ops->len, + DMA_FROM_DEVICE); + if (dma_mapping_error(chip->dev, data_dma_addr)) { + pr_err("msm_nand_read_oob: failed to get dma addr " + "for %p\n", ops->datbuf); + return -EIO; + } + } + if (ops->oobbuf) { + memset(ops->oobbuf, 0xff, ops->ooblen); + oob_dma_addr_curr = oob_dma_addr = + dma_map_single(chip->dev, ops->oobbuf, + ops->ooblen, DMA_BIDIRECTIONAL); + if (dma_mapping_error(chip->dev, oob_dma_addr)) { + pr_err("msm_nand_read_oob: failed to get dma addr " + "for %p\n", ops->oobbuf); + err = -EIO; + goto err_dma_map_oobbuf_failed; + } + } + + wait_event(chip->wait_queue, + (dma_buffer = msm_nand_get_dma_buffer( + chip, sizeof(*dma_buffer)))); + + oob_col = start_sector * 0x210; + if (chip->CFG1 & CFG1_WIDE_FLASH) + oob_col >>= 1; + + err = 0; + while (page_count-- > 0) { + cmd = dma_buffer->cmd; + + /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ + dma_buffer->data.cmd = NAND_CMD_PAGE_READ_ECC; + dma_buffer->data.addr0 = (page << 16) | oob_col; + /* qc example is (page >> 16) && 0xff !? */ + dma_buffer->data.addr1 = (page >> 16) & 0xff; + /* flash0 + undoc bit */ + dma_buffer->data.chipsel = 0 | 4; + + + dma_buffer->data.cfg0 = + (chip->CFG0 & ~(7U << 6)) | ((3U - start_sector) << 6); + dma_buffer->data.cfg1 = chip->CFG1; + + /* GO bit for the EXEC register */ + dma_buffer->data.exec = 1; + + + BUILD_BUG_ON(4 != ARRAY_SIZE(dma_buffer->data.result)); + + for (n = start_sector; n < 4; n++) { + /* flash + buffer status return words */ + dma_buffer->data.result[n].flash_status = 0xeeeeeeee; + dma_buffer->data.result[n].buffer_status = 0xeeeeeeee; + + /* block on cmd ready, then + * write CMD / ADDR0 / ADDR1 / CHIPSEL + * regs in a burst + */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); + cmd->dst = NAND_FLASH_CMD; + if (n == start_sector) + cmd->len = 16; + else + cmd->len = 4; + cmd++; + + if (n == start_sector) { + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cfg0); + cmd->dst = NAND_DEV0_CFG0; + cmd->len = 8; + cmd++; +#if SUPPORT_WRONG_ECC_CONFIG + if (chip->saved_ecc_buf_cfg != + chip->ecc_buf_cfg) { + dma_buffer->data.ecccfg = + chip->ecc_buf_cfg; + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.ecccfg); + cmd->dst = NAND_EBI2_ECC_BUF_CFG; + cmd->len = 4; + cmd++; + } +#endif + } + + /* kick the execute register */ + cmd->cmd = 0; + cmd->src = + msm_virt_to_dma(chip, &dma_buffer->data.exec); + cmd->dst = NAND_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* block on data ready, then + * read the status register + */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = NAND_FLASH_STATUS; + cmd->dst = msm_virt_to_dma(chip, + &dma_buffer->data.result[n]); + /* NAND_FLASH_STATUS + NAND_BUFFER_STATUS */ + cmd->len = 8; + cmd++; + + /* read data block + * (only valid if status says success) + */ + if (ops->datbuf) { + sectordatasize = (n < 3) ? 516 : 500; + cmd->cmd = 0; + cmd->src = NAND_FLASH_BUFFER; + cmd->dst = data_dma_addr_curr; + data_dma_addr_curr += sectordatasize; + cmd->len = sectordatasize; + cmd++; + } + + if (ops->oobbuf && + (n == 3 || ops->mode != MTD_OOB_AUTO)) { + cmd->cmd = 0; + if (n == 3) { + cmd->src = NAND_FLASH_BUFFER + 500; + sectoroobsize = 16; + if (ops->mode != MTD_OOB_AUTO) + sectoroobsize += 10; + } else { + cmd->src = NAND_FLASH_BUFFER + 516; + sectoroobsize = 10; + } + + cmd->dst = oob_dma_addr_curr; + if (sectoroobsize < oob_len) + cmd->len = sectoroobsize; + else + cmd->len = oob_len; + oob_dma_addr_curr += cmd->len; + oob_len -= cmd->len; + if (cmd->len > 0) + cmd++; + } + } +#if SUPPORT_WRONG_ECC_CONFIG + if (chip->saved_ecc_buf_cfg != chip->ecc_buf_cfg) { + dma_buffer->data.ecccfg_restore = + chip->saved_ecc_buf_cfg; + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.ecccfg_restore); + cmd->dst = NAND_EBI2_ECC_BUF_CFG; + cmd->len = 4; + cmd++; + } +#endif + + BUILD_BUG_ON(4 * 5 + 3 != ARRAY_SIZE(dma_buffer->cmd)); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmd[0].cmd |= CMD_OCB; + cmd[-1].cmd |= CMD_OCU | CMD_LC; + + dma_buffer->cmdptr = + (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) + | CMD_PTR_LP; + + msm_dmov_exec_cmd( + chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR( + msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + + /* if any of the writes failed (0x10), or there + * was a protection violation (0x100), we lose + */ + pageerr = rawerr = 0; + for (n = start_sector; n < 4; n++) { + if (dma_buffer->data.result[n].flash_status & 0x110) { + rawerr = -EIO; + break; + } + } + if (rawerr) { + if (ops->datbuf) { + uint8_t *datbuf = + ops->datbuf + pages_read * 2048; + for (n = 0; n < 2048; n++) { + /* empty blocks read 0x54 at + * these offsets + */ + if (n % 516 == 3 && datbuf[n] == 0x54) + datbuf[n] = 0xff; + if (datbuf[n] != 0xff) { + pageerr = rawerr; + break; + } + } + } + if (ops->oobbuf) { + for (n = 0; n < ops->ooblen; n++) { + if (ops->oobbuf[n] != 0xff) { + pageerr = rawerr; + break; + } + } + } + } + if (pageerr) { + for (n = start_sector; n < 4; n++) { + if (dma_buffer->data.result[n].buffer_status & 0x8) { + mtd->ecc_stats.failed++; /* not thread safe */ + pageerr = -EBADMSG; + break; + } + } + } + if (!rawerr) { /* check for corretable errors */ + for (n = start_sector; n < 4; n++) { + ecc_errors = dma_buffer->data. + result[n].buffer_status & 0x7; + if (ecc_errors) { + total_ecc_errors += ecc_errors; + mtd->ecc_stats.corrected += ecc_errors; /* not thread safe */ + if (ecc_errors > 1) + pageerr = -EUCLEAN; + } + } + } + if (pageerr && (pageerr != -EUCLEAN || err == 0)) + err = pageerr; + +#if VERBOSE + if (rawerr && !pageerr) { + pr_err("msm_nand_read_oob %llx %x %x empty page\n", + (loff_t)page * mtd->writesize, ops->len, + ops->ooblen); + } else { + pr_info("status: %x %x %x %x %x %x %x %x\n", + dma_buffer->data.result[0].flash_status, + dma_buffer->data.result[0].buffer_status, + dma_buffer->data.result[1].flash_status, + dma_buffer->data.result[1].buffer_status, + dma_buffer->data.result[2].flash_status, + dma_buffer->data.result[2].buffer_status, + dma_buffer->data.result[3].flash_status, + dma_buffer->data.result[3].buffer_status); + } +#endif + if (err && err != -EUCLEAN && err != -EBADMSG) + break; + pages_read++; + page++; + } + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + + if (ops->oobbuf) { + dma_unmap_single(chip->dev, oob_dma_addr, + ops->ooblen, DMA_FROM_DEVICE); + } +err_dma_map_oobbuf_failed: + if (ops->datbuf) { + dma_unmap_single(chip->dev, data_dma_addr, + ops->len, DMA_FROM_DEVICE); + } + + ops->retlen = mtd->writesize * pages_read; + ops->oobretlen = ops->ooblen - oob_len; + if (err) + pr_err("msm_nand_read_oob %llx %x %x failed %d, corrected %d\n", + from, ops->datbuf ? ops->len : 0, ops->ooblen, err, + total_ecc_errors); + return err; +} + +static int +msm_nand_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + int ret; + struct mtd_oob_ops ops; + + /* printk("msm_nand_read %llx %x\n", from, len); */ + + ops.mode = MTD_OOB_PLACE; + ops.len = len; + ops.retlen = 0; + ops.ooblen = 0; + ops.datbuf = buf; + ops.oobbuf = NULL; + ret = msm_nand_read_oob(mtd, from, &ops); + *retlen = ops.retlen; + return ret; +} + +static int +msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) +{ + struct msm_nand_chip *chip = mtd->priv; + struct { + dmov_s cmd[4 * 5 + 3]; + unsigned cmdptr; + struct { + uint32_t cmd; + uint32_t addr0; + uint32_t addr1; + uint32_t chipsel; + uint32_t cfg0; + uint32_t cfg1; + uint32_t exec; +#if SUPPORT_WRONG_ECC_CONFIG + uint32_t ecccfg; + uint32_t ecccfg_restore; +#endif + uint32_t flash_status[4]; + } data; + } *dma_buffer; + dmov_s *cmd; + unsigned n; + unsigned page = to / 2048; + uint32_t oob_len = ops->ooblen; + uint32_t sectordatawritesize; + int err; + dma_addr_t data_dma_addr = 0; + dma_addr_t oob_dma_addr = 0; + dma_addr_t data_dma_addr_curr = 0; + dma_addr_t oob_dma_addr_curr = 0; + unsigned page_count; + unsigned pages_written = 0; + + if (to & (mtd->writesize - 1)) { + pr_err("%s: unsupported to, 0x%llx\n", __func__, to); + return -EINVAL; + } + if (ops->ooblen != 0 && ops->mode != MTD_OOB_AUTO) { + pr_err("%s: unsupported ops->mode, %d\n", + __func__, ops->mode); + return -EINVAL; + } + + if (ops->datbuf == NULL) { + pr_err("%s: unsupported ops->datbuf == NULL\n", __func__); + return -EINVAL; + } + if ((ops->len % mtd->writesize) != 0) { + pr_err("%s: unsupported ops->len, %d\n", + __func__, ops->len); + return -EINVAL; + } +#if 0 /* yaffs writes more oob data than it needs */ + if (ops->ooblen >= sectoroobsize * 4) { + pr_err("%s: unsupported ops->ooblen, %d\n", + __func__, ops->ooblen); + return -EINVAL; + } +#endif + if (ops->ooblen != 0 && ops->ooboffs != 0) { + pr_err("%s: unsupported ops->ooboffs, %d\n", + __func__, ops->ooboffs); + return -EINVAL; + } + + if (ops->datbuf) { + data_dma_addr_curr = data_dma_addr = + dma_map_single(chip->dev, ops->datbuf, + ops->len, DMA_TO_DEVICE); + if (dma_mapping_error(chip->dev, data_dma_addr)) { + pr_err("msm_nand_write_oob: failed to get dma addr " + "for %p\n", ops->datbuf); + return -EIO; + } + } + if (ops->oobbuf) { + oob_dma_addr_curr = oob_dma_addr = + dma_map_single(chip->dev, ops->oobbuf, + ops->ooblen, DMA_TO_DEVICE); + if (dma_mapping_error(chip->dev, oob_dma_addr)) { + pr_err("msm_nand_write_oob: failed to get dma addr " + "for %p\n", ops->oobbuf); + err = -EIO; + goto err_dma_map_oobbuf_failed; + } + } + + page_count = ops->len / mtd->writesize; + + wait_event(chip->wait_queue, (dma_buffer = + msm_nand_get_dma_buffer(chip, sizeof(*dma_buffer)))); + + while (page_count-- > 0) { + cmd = dma_buffer->cmd; + + /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ + dma_buffer->data.cmd = NAND_CMD_PRG_PAGE; + dma_buffer->data.addr0 = page << 16; + dma_buffer->data.addr1 = (page >> 16) & 0xff; + dma_buffer->data.chipsel = 0 | 4; /* flash0 + undoc bit */ + + dma_buffer->data.cfg0 = chip->CFG0; + dma_buffer->data.cfg1 = chip->CFG1; + + /* GO bit for the EXEC register */ + dma_buffer->data.exec = 1; + + BUILD_BUG_ON(4 != ARRAY_SIZE(dma_buffer->data.flash_status)); + + for (n = 0; n < 4; n++) { + /* status return words */ + dma_buffer->data.flash_status[n] = 0xeeeeeeee; + /* block on cmd ready, then + * write CMD / ADDR0 / ADDR1 / CHIPSEL regs in a burst + */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = + msm_virt_to_dma(chip, &dma_buffer->data.cmd); + cmd->dst = NAND_FLASH_CMD; + if (n == 0) + cmd->len = 16; + else + cmd->len = 4; + cmd++; + + if (n == 0) { + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cfg0); + cmd->dst = NAND_DEV0_CFG0; + cmd->len = 8; + cmd++; +#if SUPPORT_WRONG_ECC_CONFIG + if (chip->saved_ecc_buf_cfg != + chip->ecc_buf_cfg) { + dma_buffer->data.ecccfg = + chip->ecc_buf_cfg; + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.ecccfg); + cmd->dst = NAND_EBI2_ECC_BUF_CFG; + cmd->len = 4; + cmd++; + } +#endif + } + + /* write data block */ + sectordatawritesize = (n < 3) ? 516 : 500; + cmd->cmd = 0; + cmd->src = data_dma_addr_curr; + data_dma_addr_curr += sectordatawritesize; + cmd->dst = NAND_FLASH_BUFFER; + cmd->len = sectordatawritesize; + cmd++; + + if (ops->oobbuf) { + if (n == 3) { + cmd->cmd = 0; + cmd->src = oob_dma_addr_curr; + cmd->dst = NAND_FLASH_BUFFER + 500; + if (16 < oob_len) + cmd->len = 16; + else + cmd->len = oob_len; + oob_dma_addr_curr += cmd->len; + oob_len -= cmd->len; + if (cmd->len > 0) + cmd++; + } + if (ops->mode != MTD_OOB_AUTO) { + /* skip ecc bytes in oobbuf */ + if (oob_len < 10) { + oob_dma_addr_curr += 10; + oob_len -= 10; + } else { + oob_dma_addr_curr += oob_len; + oob_len = 0; + } + } + } + + /* kick the execute register */ + cmd->cmd = 0; + cmd->src = + msm_virt_to_dma(chip, &dma_buffer->data.exec); + cmd->dst = NAND_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* block on data ready, then + * read the status register + */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = NAND_FLASH_STATUS; + cmd->dst = msm_virt_to_dma(chip, + &dma_buffer->data.flash_status[n]); + cmd->len = 4; + cmd++; + } +#if SUPPORT_WRONG_ECC_CONFIG + if (chip->saved_ecc_buf_cfg != chip->ecc_buf_cfg) { + dma_buffer->data.ecccfg_restore = + chip->saved_ecc_buf_cfg; + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.ecccfg_restore); + cmd->dst = NAND_EBI2_ECC_BUF_CFG; + cmd->len = 4; + cmd++; + } +#endif + dma_buffer->cmd[0].cmd |= CMD_OCB; + cmd[-1].cmd |= CMD_OCU | CMD_LC; + BUILD_BUG_ON(4 * 5 + 3 != ARRAY_SIZE(dma_buffer->cmd)); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmdptr = + (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | + CMD_PTR_LP; + + msm_dmov_exec_cmd(chip->dma_channel, + DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR( + msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + + /* if any of the writes failed (0x10), or there was a + * protection violation (0x100), or the program success + * bit (0x80) is unset, we lose + */ + err = 0; + for (n = 0; n < 4; n++) { + if (dma_buffer->data.flash_status[n] & 0x110) { + err = -EIO; + break; + } + if (!(dma_buffer->data.flash_status[n] & 0x80)) { + err = -EIO; + break; + } + } + +#if VERBOSE + pr_info("write page %d: status: %x %x %x %x\n", page, + dma_buffer->data.flash_status[0], + dma_buffer->data.flash_status[1], + dma_buffer->data.flash_status[2], + dma_buffer->data.flash_status[3]); +#endif + if (err) + break; + pages_written++; + page++; + } + ops->retlen = mtd->writesize * pages_written; + ops->oobretlen = ops->ooblen - oob_len; + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + + if (ops->oobbuf) + dma_unmap_single(chip->dev, oob_dma_addr, + ops->ooblen, DMA_TO_DEVICE); +err_dma_map_oobbuf_failed: + if (ops->datbuf) + dma_unmap_single(chip->dev, data_dma_addr, 2048, DMA_TO_DEVICE); + if (err) + pr_err("msm_nand_write_oob %llx %x %x failed %d\n", + to, ops->len, ops->ooblen, err); + return err; +} + +static int msm_nand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + int ret; + struct mtd_oob_ops ops; + + ops.mode = MTD_OOB_PLACE; + ops.len = len; + ops.retlen = 0; + ops.ooblen = 0; + ops.datbuf = (uint8_t *)buf; + ops.oobbuf = NULL; + ret = msm_nand_write_oob(mtd, to, &ops); + *retlen = ops.retlen; + return ret; +} + +static int +msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + int err; + struct msm_nand_chip *chip = mtd->priv; + struct { + dmov_s cmd[4]; + unsigned cmdptr; + unsigned data[8]; + } *dma_buffer; + unsigned page = instr->addr / 2048; + + if (instr->addr & (mtd->erasesize - 1)) { + pr_err("%s: unsupported erase address, 0x%llx\n", + __func__, instr->addr); + return -EINVAL; + } + if (instr->len != mtd->erasesize) { + pr_err("%s: unsupported erase len, %lld\n", + __func__, instr->len); + return -EINVAL; + } + + wait_event(chip->wait_queue, + (dma_buffer = msm_nand_get_dma_buffer( + chip, sizeof(*dma_buffer)))); + + dma_buffer->data[0] = NAND_CMD_BLOCK_ERASE; + dma_buffer->data[1] = page; + dma_buffer->data[2] = 0; + dma_buffer->data[3] = 0 | 4; + dma_buffer->data[4] = 1; + dma_buffer->data[5] = 0xeeeeeeee; + dma_buffer->data[6] = chip->CFG0 & (~(7 << 6)); /* CW_PER_PAGE = 0 */ + dma_buffer->data[7] = chip->CFG1; + BUILD_BUG_ON(7 != ARRAY_SIZE(dma_buffer->data) - 1); + + dma_buffer->cmd[0].cmd = DST_CRCI_NAND_CMD | CMD_OCB; + dma_buffer->cmd[0].src = msm_virt_to_dma(chip, &dma_buffer->data[0]); + dma_buffer->cmd[0].dst = NAND_FLASH_CMD; + dma_buffer->cmd[0].len = 16; + + dma_buffer->cmd[1].cmd = 0; + dma_buffer->cmd[1].src = msm_virt_to_dma(chip, &dma_buffer->data[6]); + dma_buffer->cmd[1].dst = NAND_DEV0_CFG0; + dma_buffer->cmd[1].len = 8; + + dma_buffer->cmd[2].cmd = 0; + dma_buffer->cmd[2].src = msm_virt_to_dma(chip, &dma_buffer->data[4]); + dma_buffer->cmd[2].dst = NAND_EXEC_CMD; + dma_buffer->cmd[2].len = 4; + + dma_buffer->cmd[3].cmd = SRC_CRCI_NAND_DATA | CMD_OCU | CMD_LC;; + dma_buffer->cmd[3].src = NAND_FLASH_STATUS; + dma_buffer->cmd[3].dst = msm_virt_to_dma(chip, &dma_buffer->data[5]); + dma_buffer->cmd[3].len = 4; + + BUILD_BUG_ON(3 != ARRAY_SIZE(dma_buffer->cmd) - 1); + + dma_buffer->cmdptr = + (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; + + msm_dmov_exec_cmd( + chip->dma_channel, DMOV_CMD_PTR_LIST | + DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + + /* we fail if there was an operation error, a mpu error, or the + * erase success bit was not set. + */ + + if (dma_buffer->data[5] & 0x110 || !(dma_buffer->data[5] & 0x80)) + err = -EIO; + else + err = 0; + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + if (err) { + pr_err("%s: erase failed, 0x%llx\n", __func__, instr->addr); + instr->fail_addr = instr->addr; + instr->state = MTD_ERASE_FAILED; + } else { + instr->state = MTD_ERASE_DONE; + instr->fail_addr = 0xffffffff; + mtd_erase_callback(instr); + } + return err; +} + +static int +msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ + /* Check for invalid offset */ + if (ofs > mtd->size) + return -EINVAL; + + return 0; +} + + +static int +msm_nand_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + /* struct msm_nand_chip *this = mtd->priv; */ + int ret; + + ret = msm_nand_block_isbad(mtd, ofs); + if (ret) { + /* If it was bad already, return success and do nothing */ + if (ret > 0) + return 0; + return ret; + } + + return -EIO; +} + +/** + * msm_nand_suspend - [MTD Interface] Suspend the msm_nand flash + * @param mtd MTD device structure + */ +static int msm_nand_suspend(struct mtd_info *mtd) +{ + return 0; +} + +/** + * msm_nand_resume - [MTD Interface] Resume the msm_nand flash + * @param mtd MTD device structure + */ +static void msm_nand_resume(struct mtd_info *mtd) +{ +} + +/** + * msm_nand_scan - [msm_nand Interface] Scan for the msm_nand device + * @param mtd MTD device structure + * @param maxchips Number of chips to scan for + * + * This fills out all the not initialized function pointers + * with the defaults. + * The flash ID is read and the mtd/chip structures are + * filled with the appropriate values. + */ +int msm_nand_scan(struct mtd_info *mtd, int maxchips) +{ + unsigned n; + struct msm_nand_chip *chip = mtd->priv; + uint32_t flash_id; + + + if (flash_read_config(chip)) { + pr_err("ERRROR: could not save CFG0 & CFG1 state\n"); + return -ENODEV; + } + pr_info("CFG0 = %x, CFG1 = %x\n", chip->CFG0, chip->CFG1); + pr_info("CFG0: cw/page=%d ud_sz=%d ecc_sz=%d spare_sz=%d " + "num_addr_cycles=%d\n", (chip->CFG0 >> 6) & 7, + (chip->CFG0 >> 9) & 0x3ff, (chip->CFG0 >> 19) & 15, + (chip->CFG0 >> 23) & 15, (chip->CFG0 >> 27) & 7); + + pr_info("NAND_READ_ID = %x\n", flash_rd_reg(chip, NAND_READ_ID)); + flash_wr_reg(chip, NAND_READ_ID, 0x12345678); + + flash_id = flash_read_id(chip); + + n = flash_rd_reg(chip, NAND_DEV0_CFG0); + pr_info("CFG0: cw/page=%d ud_sz=%d ecc_sz=%d spare_sz=%d\n", + (n >> 6) & 7, (n >> 9) & 0x3ff, (n >> 19) & 15, + (n >> 23) & 15); + + n = flash_rd_reg(chip, NAND_DEV_CMD1); + pr_info("DEV_CMD1: %x\n", n); + + n = flash_rd_reg(chip, NAND_EBI2_ECC_BUF_CFG); + pr_info(KERN_INFO "NAND_EBI2_ECC_BUF_CFG: %x\n", n); + +#if SUPPORT_WRONG_ECC_CONFIG + chip->ecc_buf_cfg = 0x203; + chip->saved_ecc_buf_cfg = n; +#endif + + if ((flash_id & 0xffff) == 0xaaec) /* 2Gbit Samsung chip */ + mtd->size = 256 << 20; /* * num_chips */ + else if (flash_id == 0x5580baad) /* 2Gbit Hynix chip */ + mtd->size = 256 << 20; /* * num_chips */ + else if (flash_id == 0x5510baad) /* 2Gbit Hynix chip */ + mtd->size = 256 << 20; /* * num_chips */ + pr_info("flash_id: %x size %llx\n", flash_id, mtd->size); + + mtd->writesize = 2048; + mtd->oobsize = msm_nand_oob_64.eccbytes + msm_nand_oob_64.oobavail; + mtd->oobavail = msm_nand_oob_64.oobavail; + mtd->erasesize = mtd->writesize << 6; /* TODO: check */ + mtd->ecclayout = &msm_nand_oob_64; + + /* Fill in remaining MTD driver data */ + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; + /* mtd->ecctype = MTD_ECC_SW; */ + mtd->erase = msm_nand_erase; + mtd->point = NULL; + mtd->unpoint = NULL; + mtd->read = msm_nand_read; + mtd->write = msm_nand_write; + mtd->read_oob = msm_nand_read_oob; + mtd->write_oob = msm_nand_write_oob; + /* mtd->sync = msm_nand_sync; */ + mtd->lock = NULL; + /* mtd->unlock = msm_nand_unlock; */ + mtd->suspend = msm_nand_suspend; + mtd->resume = msm_nand_resume; + mtd->block_isbad = msm_nand_block_isbad; + mtd->block_markbad = msm_nand_block_markbad; + mtd->owner = THIS_MODULE; + + /* Unlock whole block */ + /* msm_nand_unlock_all(mtd); */ + + /* return this->scan_bbt(mtd); */ + return 0; +} +EXPORT_SYMBOL_GPL(msm_nand_scan); + +/** + * msm_nand_release - [msm_nand Interface] Free resources held by the msm_nand device + * @param mtd MTD device structure + */ +void msm_nand_release(struct mtd_info *mtd) +{ + /* struct msm_nand_chip *this = mtd->priv; */ + +#ifdef CONFIG_MTD_PARTITIONS + /* Deregister partitions */ + del_mtd_partitions(mtd); +#endif + /* Deregister the device */ + del_mtd_device(mtd); +} +EXPORT_SYMBOL_GPL(msm_nand_release); + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probes[] = { "cmdlinepart", NULL, }; +#endif + +struct msm_nand_info { + struct mtd_info mtd; + struct mtd_partition *parts; + struct msm_nand_chip msm_nand; +}; + +static int __devinit msm_nand_probe(struct platform_device *pdev) +{ + struct msm_nand_info *info; + struct flash_platform_data *pdata = pdev->dev.platform_data; + int err; + + if (pdev->num_resources != 1) { + pr_err("invalid num_resources"); + return -ENODEV; + } + if (pdev->resource[0].flags != IORESOURCE_DMA) { + pr_err("invalid resource type"); + return -ENODEV; + } + + info = kzalloc(sizeof(struct msm_nand_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->msm_nand.dev = &pdev->dev; + + init_waitqueue_head(&info->msm_nand.wait_queue); + + info->msm_nand.dma_channel = pdev->resource[0].start; + /* this currently fails if dev is passed in */ + info->msm_nand.dma_buffer = + dma_alloc_coherent(/*dev*/ NULL, MSM_NAND_DMA_BUFFER_SIZE, + &info->msm_nand.dma_addr, GFP_KERNEL); + if (info->msm_nand.dma_buffer == NULL) { + err = -ENOMEM; + goto out_free_info; + } + + pr_info("allocated dma buffer at %p, dma_addr %x\n", + info->msm_nand.dma_buffer, info->msm_nand.dma_addr); + + info->mtd.name = dev_name(&pdev->dev); + info->mtd.priv = &info->msm_nand; + info->mtd.owner = THIS_MODULE; + + if (msm_nand_scan(&info->mtd, 1)) { + err = -ENXIO; + goto out_free_dma_buffer; + } + +#ifdef CONFIG_MTD_PARTITIONS + err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); + if (err > 0) + add_mtd_partitions(&info->mtd, info->parts, err); + else if (err <= 0 && pdata && pdata->parts) + add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); + else +#endif + err = add_mtd_device(&info->mtd); + + dev_set_drvdata(&pdev->dev, info); + + return 0; + +out_free_dma_buffer: + dma_free_coherent(/*dev*/ NULL, SZ_4K, info->msm_nand.dma_buffer, + info->msm_nand.dma_addr); +out_free_info: + kfree(info); + + return err; +} + +static int __devexit msm_nand_remove(struct platform_device *pdev) +{ + struct msm_nand_info *info = dev_get_drvdata(&pdev->dev); + + dev_set_drvdata(&pdev->dev, NULL); + + if (info) { +#ifdef CONFIG_MTD_PARTITIONS + if (info->parts) + del_mtd_partitions(&info->mtd); + else +#endif + del_mtd_device(&info->mtd); + + msm_nand_release(&info->mtd); + dma_free_coherent(/*dev*/ NULL, SZ_4K, + info->msm_nand.dma_buffer, + info->msm_nand.dma_addr); + kfree(info); + } + + return 0; +} + +#define DRIVER_NAME "msm_nand" + +static struct platform_driver msm_nand_driver = { + .probe = msm_nand_probe, + .remove = __devexit_p(msm_nand_remove), + .driver = { + .name = DRIVER_NAME, + } +}; + +MODULE_ALIAS(DRIVER_NAME); + +static int __init msm_nand_init(void) +{ + return platform_driver_register(&msm_nand_driver); +} + +static void __exit msm_nand_exit(void) +{ + platform_driver_unregister(&msm_nand_driver); +} + +module_init(msm_nand_init); +module_exit(msm_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("msm_nand flash driver code"); diff --git a/drivers/mtd/devices/msm_nand.h b/drivers/mtd/devices/msm_nand.h new file mode 100644 index 0000000000000..b5e6293e929ab --- /dev/null +++ b/drivers/mtd/devices/msm_nand.h @@ -0,0 +1,71 @@ +/* drivers/mtd/devices/msm_nand.h + * + * Copyright (C) 2007 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 __DRIVERS_MTD_DEVICES_MSM_NAND_H +#define __DRIVERS_MTD_DEVICES_MSM_NAND_H + +#include + +#define NAND_REG(off) (MSM_NAND_BASE + (off)) + +#define NAND_FLASH_CMD NAND_REG(0x0000) +#define NAND_ADDR0 NAND_REG(0x0004) +#define NAND_ADDR1 NAND_REG(0x0008) +#define NAND_FLASH_CHIP_SELECT NAND_REG(0x000C) +#define NAND_EXEC_CMD NAND_REG(0x0010) +#define NAND_FLASH_STATUS NAND_REG(0x0014) +#define NAND_BUFFER_STATUS NAND_REG(0x0018) +#define NAND_DEV0_CFG0 NAND_REG(0x0020) +#define NAND_DEV0_CFG1 NAND_REG(0x0024) +#define NAND_DEV1_CFG0 NAND_REG(0x0030) +#define NAND_DEV1_CFG1 NAND_REG(0x0034) +#define NAND_READ_ID NAND_REG(0x0040) +#define NAND_READ_STATUS NAND_REG(0x0044) +#define NAND_CONFIG_DATA NAND_REG(0x0050) +#define NAND_CONFIG NAND_REG(0x0054) +#define NAND_CONFIG_MODE NAND_REG(0x0058) +#define NAND_CONFIG_STATUS NAND_REG(0x0060) +#define NAND_MACRO1_REG NAND_REG(0x0064) +#define NAND_XFR_STEP1 NAND_REG(0x0070) +#define NAND_XFR_STEP2 NAND_REG(0x0074) +#define NAND_XFR_STEP3 NAND_REG(0x0078) +#define NAND_XFR_STEP4 NAND_REG(0x007C) +#define NAND_XFR_STEP5 NAND_REG(0x0080) +#define NAND_XFR_STEP6 NAND_REG(0x0084) +#define NAND_XFR_STEP7 NAND_REG(0x0088) +#define NAND_DEV_CMD0 NAND_REG(0x00A0) +#define NAND_DEV_CMD1 NAND_REG(0x00A4) +#define NAND_DEV_CMD2 NAND_REG(0x00A8) +#define NAND_DEV_CMD_VLD NAND_REG(0x00AC) +#define NAND_EBI2_MISR_SIG_REG NAND_REG(0x00B0) +#define NAND_EBI2_ECC_BUF_CFG NAND_REG(0x00F0) +#define NAND_FLASH_BUFFER NAND_REG(0x0100) + +/* device commands */ + +#define NAND_CMD_SOFT_RESET 0x01 +#define NAND_CMD_PAGE_READ 0x32 +#define NAND_CMD_PAGE_READ_ECC 0x33 +#define NAND_CMD_PAGE_READ_ALL 0x34 +#define NAND_CMD_SEQ_PAGE_READ 0x15 +#define NAND_CMD_PRG_PAGE 0x36 +#define NAND_CMD_PRG_PAGE_ECC 0x37 +#define NAND_CMD_PRG_PAGE_ALL 0x39 +#define NAND_CMD_BLOCK_ERASE 0x3A +#define NAND_CMD_FETCH_ID 0x0B +#define NAND_CMD_STATUS 0x0C +#define NAND_CMD_RESET 0x0D + +#endif From a04172900e61c8300a478ba2500c1ef8b9577c04 Mon Sep 17 00:00:00 2001 From: Thomas Tsai Date: Wed, 18 Feb 2009 08:30:54 -0800 Subject: [PATCH 0149/2556] [ARM] msm: nand: Add support for 4 Gbit Hynix / Samsung parts Signed-off-by: San Mehat [ARM] msm: nand: Fix style violations Signed-off-by: San Mehat mtd: msm_nand: Remove HTC debug code. Signed-off-by: Dima Zavin --- drivers/mtd/devices/msm_nand.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index 04721a24a23a4..8bc3986d49aa8 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -14,7 +14,6 @@ */ #include -#include #include #include #include @@ -22,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -606,8 +607,10 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, } if (pageerr) { for (n = start_sector; n < 4; n++) { - if (dma_buffer->data.result[n].buffer_status & 0x8) { - mtd->ecc_stats.failed++; /* not thread safe */ + if (dma_buffer->data.result[n].buffer_status & + 0x8) { + /* not thread safe */ + mtd->ecc_stats.failed++; pageerr = -EBADMSG; break; } @@ -619,7 +622,8 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, result[n].buffer_status & 0x7; if (ecc_errors) { total_ecc_errors += ecc_errors; - mtd->ecc_stats.corrected += ecc_errors; /* not thread safe */ + /* not thread safe */ + mtd->ecc_stats.corrected += ecc_errors; if (ecc_errors > 1) pageerr = -EUCLEAN; } @@ -1160,12 +1164,22 @@ int msm_nand_scan(struct mtd_info *mtd, int maxchips) chip->saved_ecc_buf_cfg = n; #endif - if ((flash_id & 0xffff) == 0xaaec) /* 2Gbit Samsung chip */ - mtd->size = 256 << 20; /* * num_chips */ - else if (flash_id == 0x5580baad) /* 2Gbit Hynix chip */ - mtd->size = 256 << 20; /* * num_chips */ - else if (flash_id == 0x5510baad) /* 2Gbit Hynix chip */ - mtd->size = 256 << 20; /* * num_chips */ + if ((flash_id & 0xffff) == 0xaaec) /* 2Gbit Samsung chip */ + mtd->size = 256 << 20; /* * num_chips */ + else if (flash_id == 0x5580baad) /* 2Gbit Hynix chip */ + mtd->size = 256 << 20; /* * num_chips */ + else if (flash_id == 0x5510baad) /* 2Gbit Hynix chip */ + mtd->size = 256 << 20; /* * num_chips */ + + if ((flash_id & 0xffff) == 0xacec) /* 4G/1Gbit Samsung chip */ + mtd->size = 512 << 20; /* * num_chips */ + else if (flash_id == 0x5510bcad) /* 4Gbit Hynix chip */ + mtd->size = 512 << 20; /* * num_chips */ + else if ((flash_id & 0xffff) == 0xbcec) /* 4Gbit Samsung chip */ + mtd->size = 512 << 20; /* * num_chips */ + else if (flash_id == 0x5590bc2c) /* 4Gbit Micron chip */ + mtd->size = 512 << 20; /* * num_chips */ + pr_info("flash_id: %x size %llx\n", flash_id, mtd->size); mtd->writesize = 2048; From 0d0e26ff5515ff31e4a20615adf885d8087289af Mon Sep 17 00:00:00 2001 From: San Mehat Date: Thu, 19 Feb 2009 14:13:20 -0800 Subject: [PATCH 0150/2556] [ARM] msm: nand: Add support for 2Gbit Micron parts Signed-off-by: San Mehat --- drivers/mtd/devices/msm_nand.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index 8bc3986d49aa8..6e8a24ddae454 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -1179,6 +1179,8 @@ int msm_nand_scan(struct mtd_info *mtd, int maxchips) mtd->size = 512 << 20; /* * num_chips */ else if (flash_id == 0x5590bc2c) /* 4Gbit Micron chip */ mtd->size = 512 << 20; /* * num_chips */ + else if (flash_id == 0x5580ba2c) /* 2Gbit Micron chip */ + mtd->size = 256 << 20; pr_info("flash_id: %x size %llx\n", flash_id, mtd->size); From c6c3c628feffded448169fbe6af501b613f63ed6 Mon Sep 17 00:00:00 2001 From: Murali Palnati Date: Wed, 25 Mar 2009 20:04:02 +0530 Subject: [PATCH 0151/2556] msm-nand: Add raw mode support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for reading and writing a raw page (Data + ECC bytes) from the flash. Acked-by: David Brown Signed-off-by: Arve Hjønnevåg --- drivers/mtd/devices/msm_nand.c | 123 ++++++++++++++++++++++++--------- 1 file changed, 90 insertions(+), 33 deletions(-) diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index 6e8a24ddae454..815507b66c953 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -38,6 +38,9 @@ #define SUPPORT_WRONG_ECC_CONFIG 1 +#define NAND_CFG0_RAW 0xA80420C0 +#define NAND_CFG1_RAW 0x5045D + #define VERBOSE 0 struct msm_nand_chip { @@ -360,13 +363,23 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, __func__, from); return -EINVAL; } - if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { - /* when ops->datbuf is NULL, ops->len may refer to ooblen */ - pr_err("%s: unsupported ops->len, %d\n", - __func__, ops->len); - return -EINVAL; + if (ops->mode != MTD_OOB_RAW) { + if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { + /* when ops->datbuf is NULL, ops->len can be ooblen */ + pr_err("%s: unsupported ops->len, %d\n", + __func__, ops->len); + return -EINVAL; + } + } else { + if (ops->datbuf != NULL && + (ops->len % (mtd->writesize + mtd->oobsize)) != 0) { + pr_err("%s: unsupported ops->len," + " %d for MTD_OOB_RAW\n", __func__, ops->len); + return -EINVAL; + } } - if (ops->ooblen != 0 && ops->ooboffs != 0) { + + if (ops->mode != MTD_OOB_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { pr_err("%s: unsupported ops->ooboffs, %d\n", __func__, ops->ooboffs); return -EINVAL; @@ -378,8 +391,10 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, if (ops->oobbuf && !ops->datbuf) page_count = ops->ooblen / ((ops->mode == MTD_OOB_AUTO) ? mtd->oobavail : mtd->oobsize); - else + else if (ops->mode != MTD_OOB_RAW) page_count = ops->len / mtd->writesize; + else + page_count = ops->len / (mtd->writesize + mtd->oobsize); #if 0 /* yaffs reads more oob data than it needs */ if (ops->ooblen >= sectoroobsize * 4) { @@ -430,7 +445,18 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, cmd = dma_buffer->cmd; /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ - dma_buffer->data.cmd = NAND_CMD_PAGE_READ_ECC; + if (ops->mode != MTD_OOB_RAW) { + dma_buffer->data.cmd = NAND_CMD_PAGE_READ_ECC; + dma_buffer->data.cfg0 = + (chip->CFG0 & ~(7U << 6)) | ((3U - start_sector) << 6); + dma_buffer->data.cfg1 = chip->CFG1; + } else { + dma_buffer->data.cmd = NAND_CMD_PAGE_READ; + dma_buffer->data.cfg0 = NAND_CFG0_RAW; + dma_buffer->data.cfg1 = NAND_CFG1_RAW | + (chip->CFG1 & CFG1_WIDE_FLASH); + } + dma_buffer->data.addr0 = (page << 16) | oob_col; /* qc example is (page >> 16) && 0xff !? */ dma_buffer->data.addr1 = (page >> 16) & 0xff; @@ -438,10 +464,6 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, dma_buffer->data.chipsel = 0 | 4; - dma_buffer->data.cfg0 = - (chip->CFG0 & ~(7U << 6)) | ((3U - start_sector) << 6); - dma_buffer->data.cfg1 = chip->CFG1; - /* GO bit for the EXEC register */ dma_buffer->data.exec = 1; @@ -511,7 +533,11 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, * (only valid if status says success) */ if (ops->datbuf) { - sectordatasize = (n < 3) ? 516 : 500; + if (ops->mode != MTD_OOB_RAW) + sectordatasize = (n < 3) ? 516 : 500; + else + sectordatasize = 528; + cmd->cmd = 0; cmd->src = NAND_FLASH_BUFFER; cmd->dst = data_dma_addr_curr; @@ -581,7 +607,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, } } if (rawerr) { - if (ops->datbuf) { + if (ops->datbuf && ops->mode != MTD_OOB_RAW) { uint8_t *datbuf = ops->datbuf + pages_read * 2048; for (n = 0; n < 2048; n++) { @@ -666,7 +692,11 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, ops->len, DMA_FROM_DEVICE); } - ops->retlen = mtd->writesize * pages_read; + if (ops->mode != MTD_OOB_RAW) + ops->retlen = mtd->writesize * pages_read; + else + ops->retlen = (mtd->writesize + mtd->oobsize) * + pages_read; ops->oobretlen = ops->ooblen - oob_len; if (err) pr_err("msm_nand_read_oob %llx %x %x failed %d, corrected %d\n", @@ -734,21 +764,31 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) pr_err("%s: unsupported to, 0x%llx\n", __func__, to); return -EINVAL; } - if (ops->ooblen != 0 && ops->mode != MTD_OOB_AUTO) { - pr_err("%s: unsupported ops->mode, %d\n", - __func__, ops->mode); - return -EINVAL; + + if (ops->mode != MTD_OOB_RAW) { + if (ops->ooblen != 0 && ops->mode != MTD_OOB_AUTO) { + pr_err("%s: unsupported ops->mode,%d\n", + __func__, ops->mode); + return -EINVAL; + } + if ((ops->len % mtd->writesize) != 0) { + pr_err("%s: unsupported ops->len, %d\n", + __func__, ops->len); + return -EINVAL; + } + } else { + if ((ops->len % (mtd->writesize + mtd->oobsize)) != 0) { + pr_err("%s: unsupported ops->len, " + "%d for MTD_OOB_RAW mode\n", + __func__, ops->len); + return -EINVAL; + } } if (ops->datbuf == NULL) { pr_err("%s: unsupported ops->datbuf == NULL\n", __func__); return -EINVAL; } - if ((ops->len % mtd->writesize) != 0) { - pr_err("%s: unsupported ops->len, %d\n", - __func__, ops->len); - return -EINVAL; - } #if 0 /* yaffs writes more oob data than it needs */ if (ops->ooblen >= sectoroobsize * 4) { pr_err("%s: unsupported ops->ooblen, %d\n", @@ -756,7 +796,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) return -EINVAL; } #endif - if (ops->ooblen != 0 && ops->ooboffs != 0) { + if (ops->mode != MTD_OOB_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { pr_err("%s: unsupported ops->ooboffs, %d\n", __func__, ops->ooboffs); return -EINVAL; @@ -783,8 +823,10 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) goto err_dma_map_oobbuf_failed; } } - - page_count = ops->len / mtd->writesize; + if (ops->mode != MTD_OOB_RAW) + page_count = ops->len / mtd->writesize; + else + page_count = ops->len / (mtd->writesize + mtd->oobsize); wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer(chip, sizeof(*dma_buffer)))); @@ -792,14 +834,21 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) while (page_count-- > 0) { cmd = dma_buffer->cmd; - /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ + /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ + if (ops->mode != MTD_OOB_RAW) { + dma_buffer->data.cfg0 = chip->CFG0; + dma_buffer->data.cfg1 = chip->CFG1; + } else { + dma_buffer->data.cfg0 = NAND_CFG0_RAW; + dma_buffer->data.cfg1 = NAND_CFG1_RAW | + (chip->CFG1 & CFG1_WIDE_FLASH); + } + dma_buffer->data.cmd = NAND_CMD_PRG_PAGE; dma_buffer->data.addr0 = page << 16; dma_buffer->data.addr1 = (page >> 16) & 0xff; dma_buffer->data.chipsel = 0 | 4; /* flash0 + undoc bit */ - dma_buffer->data.cfg0 = chip->CFG0; - dma_buffer->data.cfg1 = chip->CFG1; /* GO bit for the EXEC register */ dma_buffer->data.exec = 1; @@ -845,7 +894,11 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) } /* write data block */ - sectordatawritesize = (n < 3) ? 516 : 500; + if (ops->mode != MTD_OOB_RAW) + sectordatawritesize = (n < 3) ? 516 : 500; + else + sectordatawritesize = 528; + cmd->cmd = 0; cmd->src = data_dma_addr_curr; data_dma_addr_curr += sectordatawritesize; @@ -949,7 +1002,11 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) pages_written++; page++; } - ops->retlen = mtd->writesize * pages_written; + if (ops->mode != MTD_OOB_RAW) + ops->retlen = mtd->writesize * pages_written; + else + ops->retlen = (mtd->writesize + mtd->oobsize) * pages_written; + ops->oobretlen = ops->ooblen - oob_len; msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); @@ -1185,7 +1242,7 @@ int msm_nand_scan(struct mtd_info *mtd, int maxchips) pr_info("flash_id: %x size %llx\n", flash_id, mtd->size); mtd->writesize = 2048; - mtd->oobsize = msm_nand_oob_64.eccbytes + msm_nand_oob_64.oobavail; + mtd->oobsize = 64; mtd->oobavail = msm_nand_oob_64.oobavail; mtd->erasesize = mtd->writesize << 6; /* TODO: check */ mtd->ecclayout = &msm_nand_oob_64; From ec2761cc3aa25e85c0a7f8ebe0043d2c8e4cc291 Mon Sep 17 00:00:00 2001 From: Murali Palnati Date: Wed, 25 Mar 2009 20:04:37 +0530 Subject: [PATCH 0152/2556] msm-nand: Add bad block marking/checking support. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support to check if a block is bad by reading the bad block marker. For marking a block as bad, write all 0's to the first page of the block which results in the bad block marker being set to 0 as well. Acked-by: David Brown Removed config option since some factory badblocks are marked in a way that does not prevent erase. Signed-off-by: Arve Hjønnevåg --- drivers/mtd/devices/msm_nand.c | 150 +++++++++++++++++++++++++++++++-- 1 file changed, 141 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index 815507b66c953..22810becec77f 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -1131,29 +1131,161 @@ msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) static int msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) { + struct msm_nand_chip *chip = mtd->priv; + int ret; + struct { + dmov_s cmd[5]; + unsigned cmdptr; + struct { + uint32_t cmd; + uint32_t addr0; + uint32_t addr1; + uint32_t chipsel; + uint32_t cfg0; + uint32_t cfg1; + uint32_t exec; + uint32_t ecccfg; + struct { + uint32_t flash_status; + uint32_t buffer_status; + } result; + } data; + } *dma_buffer; + dmov_s *cmd; + uint8_t *buf; + unsigned page = ofs / 2048; + /* Check for invalid offset */ if (ofs > mtd->size) return -EINVAL; + if (ofs & (mtd->erasesize - 1)) { + pr_err("%s: unsupported block address, 0x%x\n", + __func__, (uint32_t)ofs); + return -EINVAL; + } - return 0; + wait_event(chip->wait_queue, + (dma_buffer = msm_nand_get_dma_buffer(chip , + sizeof(*dma_buffer) + 4))); + buf = (uint8_t *)dma_buffer + sizeof(*dma_buffer); + + /* Read 4 bytes starting from the bad block marker location + * in the last code word of the page + */ + + cmd = dma_buffer->cmd; + + dma_buffer->data.cmd = NAND_CMD_PAGE_READ; + dma_buffer->data.cfg0 = NAND_CFG0_RAW & ~(7U << 6); + dma_buffer->data.cfg1 = NAND_CFG1_RAW | (chip->CFG1 & CFG1_WIDE_FLASH); + + if (chip->CFG1 & CFG1_WIDE_FLASH) + dma_buffer->data.addr0 = (page << 16) | (0x630 >> 1); + else + dma_buffer->data.addr0 = (page << 16) | 0x630; + + dma_buffer->data.addr1 = (page >> 16) & 0xff; + dma_buffer->data.chipsel = 0 | 4; + + dma_buffer->data.exec = 1; + + dma_buffer->data.result.flash_status = 0xeeeeeeee; + dma_buffer->data.result.buffer_status = 0xeeeeeeee; + + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); + cmd->dst = NAND_FLASH_CMD; + cmd->len = 16; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); + cmd->dst = NAND_DEV0_CFG0; + cmd->len = 8; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); + cmd->dst = NAND_EXEC_CMD; + cmd->len = 4; + cmd++; + + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = NAND_FLASH_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result); + cmd->len = 8; + cmd++; + + cmd->cmd = 0; + cmd->src = NAND_FLASH_BUFFER + 464; + cmd->dst = msm_virt_to_dma(chip, buf); + cmd->len = 4; + cmd++; + + BUILD_BUG_ON(5 != ARRAY_SIZE(dma_buffer->cmd)); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmd[0].cmd |= CMD_OCB; + cmd[-1].cmd |= CMD_OCU | CMD_LC; + + dma_buffer->cmdptr = (msm_virt_to_dma(chip, + dma_buffer->cmd) >> 3) | CMD_PTR_LP; + + msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST | + DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + + ret = 0; + if (dma_buffer->data.result.flash_status & 0x110) + ret = -EIO; + + if (!ret) { + /* Check for bad block marker byte */ + if (chip->CFG1 & CFG1_WIDE_FLASH) { + if (buf[0] != 0xFF || buf[1] != 0xFF) + ret = 1; + } else { + if (buf[0] != 0xFF) + ret = 1; + } + } + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer) + 4); + return ret; } static int msm_nand_block_markbad(struct mtd_info *mtd, loff_t ofs) { - /* struct msm_nand_chip *this = mtd->priv; */ + struct mtd_oob_ops ops; int ret; + uint8_t *buf; - ret = msm_nand_block_isbad(mtd, ofs); - if (ret) { - /* If it was bad already, return success and do nothing */ - if (ret > 0) - return 0; - return ret; + /* Check for invalid offset */ + if (ofs > mtd->size) + return -EINVAL; + if (ofs & (mtd->erasesize - 1)) { + pr_err("%s: unsupported block address, 0x%x\n", + __func__, (uint32_t)ofs); + return -EINVAL; } - return -EIO; + /* + Write all 0s to the first page + This will set the BB marker to 0 + */ + + /* Use the already existing zero page */ + buf = page_address(ZERO_PAGE()); + + ops.mode = MTD_OOB_RAW; + ops.len = mtd->writesize + mtd->oobsize; + ops.retlen = 0; + ops.ooblen = 0; + ops.datbuf = buf; + ops.oobbuf = NULL; + ret = msm_nand_write_oob(mtd, ofs, &ops); + + return ret; } /** From 782f8a43d03859b90bf82d2d3feb990221015299 Mon Sep 17 00:00:00 2001 From: Brent DeGraaf Date: Tue, 11 Aug 2009 16:53:46 -0400 Subject: [PATCH 0153/2556] msm-nand: Add dma_sync_single* calls in msm_nand_read_oob. When empty pages are read, certain bytes in the buffer that is read are set 0x54 instead of 0xFF. This is due to the ECC error correction logic of the nand controller. CPU needs to correct these bytes back to 0xFF and it needs to access these bytes while the buffer is still owned by the device. This can be achieved if dma_sync_single_for_cpu and dma_sync_single_for_device are called before & after the CPU access to the buffer owned by the device. Signed-off-by: Brent DeGraaf --- drivers/mtd/devices/msm_nand.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index 22810becec77f..b8167b819f57a 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -610,6 +610,11 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, if (ops->datbuf && ops->mode != MTD_OOB_RAW) { uint8_t *datbuf = ops->datbuf + pages_read * 2048; + + dma_sync_single_for_cpu(chip->dev, + data_dma_addr_curr-mtd->writesize, + mtd->writesize, DMA_BIDIRECTIONAL); + for (n = 0; n < 2048; n++) { /* empty blocks read 0x54 at * these offsets @@ -621,6 +626,11 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, break; } } + + dma_sync_single_for_device(chip->dev, + data_dma_addr_curr-mtd->writesize, + mtd->writesize, DMA_BIDIRECTIONAL); + } if (ops->oobbuf) { for (n = 0; n < ops->ooblen; n++) { From 283ed3efd240afa767179c4a57aefaeff8d724ef Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 7 Dec 2009 19:44:20 -0800 Subject: [PATCH 0154/2556] [ARM] mtd: msm_nand: Clear the flash_status register after write/erase The OP_ERR bit gets set in the status register after an error. There's a HW bug where it's possible for this bit to not get cleared after the subsequent successful operations, and cause the software to incorrectly flag them as failures. Change-Id: I60abc4a7b7a4c3a26fa1abf81c1688ca9d3fc380 Signed-off-by: Dima Zavin --- drivers/mtd/devices/msm_nand.c | 43 +++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index b8167b819f57a..5251494d31df7 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -740,7 +740,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { struct msm_nand_chip *chip = mtd->priv; struct { - dmov_s cmd[4 * 5 + 3]; + dmov_s cmd[4 * 6 + 3]; unsigned cmdptr; struct { uint32_t cmd; @@ -755,6 +755,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) uint32_t ecccfg_restore; #endif uint32_t flash_status[4]; + uint32_t zeroes; } data; } *dma_buffer; dmov_s *cmd; @@ -858,6 +859,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) dma_buffer->data.addr0 = page << 16; dma_buffer->data.addr1 = (page >> 16) & 0xff; dma_buffer->data.chipsel = 0 | 4; /* flash0 + undoc bit */ + dma_buffer->data.zeroes = 0; /* GO bit for the EXEC register */ @@ -959,6 +961,15 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) &dma_buffer->data.flash_status[n]); cmd->len = 4; cmd++; + + /* clear the status register in case the OP_ERR is set + * due to the write, to work around a h/w bug */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.zeroes); + cmd->dst = NAND_FLASH_STATUS; + cmd->len = 4; + cmd++; } #if SUPPORT_WRONG_ECC_CONFIG if (chip->saved_ecc_buf_cfg != chip->ecc_buf_cfg) { @@ -974,7 +985,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) #endif dma_buffer->cmd[0].cmd |= CMD_OCB; cmd[-1].cmd |= CMD_OCU | CMD_LC; - BUILD_BUG_ON(4 * 5 + 3 != ARRAY_SIZE(dma_buffer->cmd)); + BUILD_BUG_ON(4 * 6 + 3 != ARRAY_SIZE(dma_buffer->cmd)); BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | @@ -991,6 +1002,9 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) err = 0; for (n = 0; n < 4; n++) { if (dma_buffer->data.flash_status[n] & 0x110) { + if (dma_buffer->data.flash_status[n] & 0x10) + pr_err("msm_nand: critical write error," + " 0x%x(%d)\n", page, n); err = -EIO; break; } @@ -1056,9 +1070,9 @@ msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) int err; struct msm_nand_chip *chip = mtd->priv; struct { - dmov_s cmd[4]; + dmov_s cmd[5]; unsigned cmdptr; - unsigned data[8]; + unsigned data[9]; } *dma_buffer; unsigned page = instr->addr / 2048; @@ -1085,7 +1099,8 @@ msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) dma_buffer->data[5] = 0xeeeeeeee; dma_buffer->data[6] = chip->CFG0 & (~(7 << 6)); /* CW_PER_PAGE = 0 */ dma_buffer->data[7] = chip->CFG1; - BUILD_BUG_ON(7 != ARRAY_SIZE(dma_buffer->data) - 1); + dma_buffer->data[8] = 0; + BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data) - 1); dma_buffer->cmd[0].cmd = DST_CRCI_NAND_CMD | CMD_OCB; dma_buffer->cmd[0].src = msm_virt_to_dma(chip, &dma_buffer->data[0]); @@ -1102,12 +1117,19 @@ msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) dma_buffer->cmd[2].dst = NAND_EXEC_CMD; dma_buffer->cmd[2].len = 4; - dma_buffer->cmd[3].cmd = SRC_CRCI_NAND_DATA | CMD_OCU | CMD_LC;; + dma_buffer->cmd[3].cmd = SRC_CRCI_NAND_DATA; dma_buffer->cmd[3].src = NAND_FLASH_STATUS; dma_buffer->cmd[3].dst = msm_virt_to_dma(chip, &dma_buffer->data[5]); dma_buffer->cmd[3].len = 4; - BUILD_BUG_ON(3 != ARRAY_SIZE(dma_buffer->cmd) - 1); + /* clear the status register in case the OP_ERR is set + * due to the write, to work around a h/w bug */ + dma_buffer->cmd[4].cmd = CMD_OCU | CMD_LC; + dma_buffer->cmd[4].src = msm_virt_to_dma(chip, &dma_buffer->data[8]); + dma_buffer->cmd[4].dst = NAND_FLASH_STATUS; + dma_buffer->cmd[4].len = 4; + + BUILD_BUG_ON(4 != ARRAY_SIZE(dma_buffer->cmd) - 1); dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; @@ -1120,9 +1142,12 @@ msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) * erase success bit was not set. */ - if (dma_buffer->data[5] & 0x110 || !(dma_buffer->data[5] & 0x80)) + if (dma_buffer->data[5] & 0x110 || !(dma_buffer->data[5] & 0x80)) { + if (dma_buffer->data[5] & 0x10) + pr_warning("msm_nand: critical erase error, 0x%llx\n", + instr->addr); err = -EIO; - else + } else err = 0; msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); From 305885336d1179b49a6a32aa692533025b5ee8f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 26 Jan 2010 17:24:08 -0800 Subject: [PATCH 0155/2556] [ARM] mtd: msm_nand: Use dma_map_page instead of dma_map_single. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dma_map_single does not work for highmem or vmalloced memory. Also fix the unmap length if writing more then one nand page. Change-Id: I29501b6e01415bb0497a24e1b1fe8d426d338bb1 Signed-off-by: Arve Hjønnevåg --- drivers/mtd/devices/msm_nand.c | 47 +++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index 5251494d31df7..23ea6306572fe 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -314,6 +314,22 @@ void flash_wr_reg(struct msm_nand_chip *chip, unsigned addr, unsigned val) msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); } +static dma_addr_t +msm_nand_dma_map(struct device *dev, void *addr, size_t size, + enum dma_data_direction dir) +{ + struct page *page; + unsigned long offset = (unsigned long)addr & ~PAGE_MASK; + if (virt_addr_valid(addr)) + page = virt_to_page(addr); + else { + if (WARN_ON(size + offset > PAGE_SIZE)) + return ~0; + page = vmalloc_to_page(addr); + } + return dma_map_page(dev, page, offset, size, dir); +} + static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { @@ -411,8 +427,8 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, if (ops->datbuf) { /* memset(ops->datbuf, 0x55, ops->len); */ data_dma_addr_curr = data_dma_addr = - dma_map_single(chip->dev, ops->datbuf, ops->len, - DMA_FROM_DEVICE); + msm_nand_dma_map(chip->dev, ops->datbuf, ops->len, + DMA_FROM_DEVICE); if (dma_mapping_error(chip->dev, data_dma_addr)) { pr_err("msm_nand_read_oob: failed to get dma addr " "for %p\n", ops->datbuf); @@ -422,8 +438,8 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, if (ops->oobbuf) { memset(ops->oobbuf, 0xff, ops->ooblen); oob_dma_addr_curr = oob_dma_addr = - dma_map_single(chip->dev, ops->oobbuf, - ops->ooblen, DMA_BIDIRECTIONAL); + msm_nand_dma_map(chip->dev, ops->oobbuf, + ops->ooblen, DMA_BIDIRECTIONAL); if (dma_mapping_error(chip->dev, oob_dma_addr)) { pr_err("msm_nand_read_oob: failed to get dma addr " "for %p\n", ops->oobbuf); @@ -693,13 +709,13 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); if (ops->oobbuf) { - dma_unmap_single(chip->dev, oob_dma_addr, - ops->ooblen, DMA_FROM_DEVICE); + dma_unmap_page(chip->dev, oob_dma_addr, + ops->ooblen, DMA_FROM_DEVICE); } err_dma_map_oobbuf_failed: if (ops->datbuf) { - dma_unmap_single(chip->dev, data_dma_addr, - ops->len, DMA_FROM_DEVICE); + dma_unmap_page(chip->dev, data_dma_addr, + ops->len, DMA_FROM_DEVICE); } if (ops->mode != MTD_OOB_RAW) @@ -815,8 +831,8 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) if (ops->datbuf) { data_dma_addr_curr = data_dma_addr = - dma_map_single(chip->dev, ops->datbuf, - ops->len, DMA_TO_DEVICE); + msm_nand_dma_map(chip->dev, ops->datbuf, + ops->len, DMA_TO_DEVICE); if (dma_mapping_error(chip->dev, data_dma_addr)) { pr_err("msm_nand_write_oob: failed to get dma addr " "for %p\n", ops->datbuf); @@ -825,8 +841,8 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) } if (ops->oobbuf) { oob_dma_addr_curr = oob_dma_addr = - dma_map_single(chip->dev, ops->oobbuf, - ops->ooblen, DMA_TO_DEVICE); + msm_nand_dma_map(chip->dev, ops->oobbuf, + ops->ooblen, DMA_TO_DEVICE); if (dma_mapping_error(chip->dev, oob_dma_addr)) { pr_err("msm_nand_write_oob: failed to get dma addr " "for %p\n", ops->oobbuf); @@ -1036,11 +1052,12 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); if (ops->oobbuf) - dma_unmap_single(chip->dev, oob_dma_addr, - ops->ooblen, DMA_TO_DEVICE); + dma_unmap_page(chip->dev, oob_dma_addr, + ops->ooblen, DMA_TO_DEVICE); err_dma_map_oobbuf_failed: if (ops->datbuf) - dma_unmap_single(chip->dev, data_dma_addr, 2048, DMA_TO_DEVICE); + dma_unmap_page(chip->dev, data_dma_addr, + ops->len, DMA_TO_DEVICE); if (err) pr_err("msm_nand_write_oob %llx %x %x failed %d\n", to, ops->len, ops->ooblen, err); From 9954d552b3b7a31235f5d5bd6e0e917817c6b866 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 10 Feb 2010 15:42:03 -0800 Subject: [PATCH 0156/2556] [ARM] mtd: msm_nand: Convert all NAND_ defines to MSM_NAND_ Needed to prevent collisions with defines in Change-Id: I2c82b6517947b65a5750752fe173c43d888a42e9 Signed-off-by: Dima Zavin --- drivers/mtd/devices/msm_nand.c | 104 ++++++++++++++++----------------- drivers/mtd/devices/msm_nand.h | 90 ++++++++++++++-------------- 2 files changed, 97 insertions(+), 97 deletions(-) diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index 23ea6306572fe..dcdecd54172d4 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -38,8 +38,8 @@ #define SUPPORT_WRONG_ECC_CONFIG 1 -#define NAND_CFG0_RAW 0xA80420C0 -#define NAND_CFG1_RAW 0x5045D +#define MSM_NAND_CFG0_RAW 0xA80420C0 +#define MSM_NAND_CFG1_RAW 0x5045D #define VERBOSE 0 @@ -146,7 +146,7 @@ uint32_t flash_read_id(struct msm_nand_chip *chip) chip, sizeof(*dma_buffer)))); dma_buffer->data[0] = 0 | 4; - dma_buffer->data[1] = NAND_CMD_FETCH_ID; + dma_buffer->data[1] = MSM_NAND_CMD_FETCH_ID; dma_buffer->data[2] = 1; dma_buffer->data[3] = 0xeeeeeeee; dma_buffer->data[4] = 0xeeeeeeee; @@ -154,26 +154,26 @@ uint32_t flash_read_id(struct msm_nand_chip *chip) dma_buffer->cmd[0].cmd = 0 | CMD_OCB; dma_buffer->cmd[0].src = msm_virt_to_dma(chip, &dma_buffer->data[0]); - dma_buffer->cmd[0].dst = NAND_FLASH_CHIP_SELECT; + dma_buffer->cmd[0].dst = MSM_NAND_FLASH_CHIP_SELECT; dma_buffer->cmd[0].len = 4; dma_buffer->cmd[1].cmd = DST_CRCI_NAND_CMD; dma_buffer->cmd[1].src = msm_virt_to_dma(chip, &dma_buffer->data[1]); - dma_buffer->cmd[1].dst = NAND_FLASH_CMD; + dma_buffer->cmd[1].dst = MSM_NAND_FLASH_CMD; dma_buffer->cmd[1].len = 4; dma_buffer->cmd[2].cmd = 0; dma_buffer->cmd[2].src = msm_virt_to_dma(chip, &dma_buffer->data[2]); - dma_buffer->cmd[2].dst = NAND_EXEC_CMD; + dma_buffer->cmd[2].dst = MSM_NAND_EXEC_CMD; dma_buffer->cmd[2].len = 4; dma_buffer->cmd[3].cmd = SRC_CRCI_NAND_DATA; - dma_buffer->cmd[3].src = NAND_FLASH_STATUS; + dma_buffer->cmd[3].src = MSM_NAND_FLASH_STATUS; dma_buffer->cmd[3].dst = msm_virt_to_dma(chip, &dma_buffer->data[3]); dma_buffer->cmd[3].len = 4; dma_buffer->cmd[4].cmd = CMD_OCU | CMD_LC; - dma_buffer->cmd[4].src = NAND_READ_ID; + dma_buffer->cmd[4].src = MSM_NAND_READ_ID; dma_buffer->cmd[4].dst = msm_virt_to_dma(chip, &dma_buffer->data[4]); dma_buffer->cmd[4].len = 4; BUILD_BUG_ON(4 != ARRAY_SIZE(dma_buffer->cmd) - 1); @@ -210,12 +210,12 @@ int flash_read_config(struct msm_nand_chip *chip) dma_buffer->cfg1 = 0; dma_buffer->cmd[0].cmd = CMD_OCB; - dma_buffer->cmd[0].src = NAND_DEV0_CFG0; + dma_buffer->cmd[0].src = MSM_NAND_DEV0_CFG0; dma_buffer->cmd[0].dst = msm_virt_to_dma(chip, &dma_buffer->cfg0); dma_buffer->cmd[0].len = 4; dma_buffer->cmd[1].cmd = CMD_OCU | CMD_LC; - dma_buffer->cmd[1].src = NAND_DEV0_CFG1; + dma_buffer->cmd[1].src = MSM_NAND_DEV0_CFG1; dma_buffer->cmd[1].dst = msm_virt_to_dma(chip, &dma_buffer->cfg1); dma_buffer->cmd[1].len = 4; BUILD_BUG_ON(1 != ARRAY_SIZE(dma_buffer->cmd) - 1); @@ -462,14 +462,14 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ if (ops->mode != MTD_OOB_RAW) { - dma_buffer->data.cmd = NAND_CMD_PAGE_READ_ECC; + dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ_ECC; dma_buffer->data.cfg0 = (chip->CFG0 & ~(7U << 6)) | ((3U - start_sector) << 6); dma_buffer->data.cfg1 = chip->CFG1; } else { - dma_buffer->data.cmd = NAND_CMD_PAGE_READ; - dma_buffer->data.cfg0 = NAND_CFG0_RAW; - dma_buffer->data.cfg1 = NAND_CFG1_RAW | + dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; + dma_buffer->data.cfg0 = MSM_NAND_CFG0_RAW; + dma_buffer->data.cfg1 = MSM_NAND_CFG1_RAW | (chip->CFG1 & CFG1_WIDE_FLASH); } @@ -497,7 +497,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, */ cmd->cmd = DST_CRCI_NAND_CMD; cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); - cmd->dst = NAND_FLASH_CMD; + cmd->dst = MSM_NAND_FLASH_CMD; if (n == start_sector) cmd->len = 16; else @@ -508,7 +508,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, cmd->cmd = 0; cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); - cmd->dst = NAND_DEV0_CFG0; + cmd->dst = MSM_NAND_DEV0_CFG0; cmd->len = 8; cmd++; #if SUPPORT_WRONG_ECC_CONFIG @@ -519,7 +519,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, cmd->cmd = 0; cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.ecccfg); - cmd->dst = NAND_EBI2_ECC_BUF_CFG; + cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG; cmd->len = 4; cmd++; } @@ -530,7 +530,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, cmd->cmd = 0; cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); - cmd->dst = NAND_EXEC_CMD; + cmd->dst = MSM_NAND_EXEC_CMD; cmd->len = 4; cmd++; @@ -538,10 +538,10 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, * read the status register */ cmd->cmd = SRC_CRCI_NAND_DATA; - cmd->src = NAND_FLASH_STATUS; + cmd->src = MSM_NAND_FLASH_STATUS; cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result[n]); - /* NAND_FLASH_STATUS + NAND_BUFFER_STATUS */ + /* MSM_NAND_FLASH_STATUS + MSM_NAND_BUFFER_STATUS */ cmd->len = 8; cmd++; @@ -555,7 +555,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, sectordatasize = 528; cmd->cmd = 0; - cmd->src = NAND_FLASH_BUFFER; + cmd->src = MSM_NAND_FLASH_BUFFER; cmd->dst = data_dma_addr_curr; data_dma_addr_curr += sectordatasize; cmd->len = sectordatasize; @@ -566,12 +566,12 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, (n == 3 || ops->mode != MTD_OOB_AUTO)) { cmd->cmd = 0; if (n == 3) { - cmd->src = NAND_FLASH_BUFFER + 500; + cmd->src = MSM_NAND_FLASH_BUFFER + 500; sectoroobsize = 16; if (ops->mode != MTD_OOB_AUTO) sectoroobsize += 10; } else { - cmd->src = NAND_FLASH_BUFFER + 516; + cmd->src = MSM_NAND_FLASH_BUFFER + 516; sectoroobsize = 10; } @@ -593,7 +593,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, cmd->cmd = 0; cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.ecccfg_restore); - cmd->dst = NAND_EBI2_ECC_BUF_CFG; + cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG; cmd->len = 4; cmd++; } @@ -866,12 +866,12 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) dma_buffer->data.cfg0 = chip->CFG0; dma_buffer->data.cfg1 = chip->CFG1; } else { - dma_buffer->data.cfg0 = NAND_CFG0_RAW; - dma_buffer->data.cfg1 = NAND_CFG1_RAW | + dma_buffer->data.cfg0 = MSM_NAND_CFG0_RAW; + dma_buffer->data.cfg1 = MSM_NAND_CFG1_RAW | (chip->CFG1 & CFG1_WIDE_FLASH); } - dma_buffer->data.cmd = NAND_CMD_PRG_PAGE; + dma_buffer->data.cmd = MSM_NAND_CMD_PRG_PAGE; dma_buffer->data.addr0 = page << 16; dma_buffer->data.addr1 = (page >> 16) & 0xff; dma_buffer->data.chipsel = 0 | 4; /* flash0 + undoc bit */ @@ -892,7 +892,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) cmd->cmd = DST_CRCI_NAND_CMD; cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); - cmd->dst = NAND_FLASH_CMD; + cmd->dst = MSM_NAND_FLASH_CMD; if (n == 0) cmd->len = 16; else @@ -903,7 +903,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) cmd->cmd = 0; cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); - cmd->dst = NAND_DEV0_CFG0; + cmd->dst = MSM_NAND_DEV0_CFG0; cmd->len = 8; cmd++; #if SUPPORT_WRONG_ECC_CONFIG @@ -914,7 +914,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) cmd->cmd = 0; cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.ecccfg); - cmd->dst = NAND_EBI2_ECC_BUF_CFG; + cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG; cmd->len = 4; cmd++; } @@ -930,7 +930,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) cmd->cmd = 0; cmd->src = data_dma_addr_curr; data_dma_addr_curr += sectordatawritesize; - cmd->dst = NAND_FLASH_BUFFER; + cmd->dst = MSM_NAND_FLASH_BUFFER; cmd->len = sectordatawritesize; cmd++; @@ -938,7 +938,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) if (n == 3) { cmd->cmd = 0; cmd->src = oob_dma_addr_curr; - cmd->dst = NAND_FLASH_BUFFER + 500; + cmd->dst = MSM_NAND_FLASH_BUFFER + 500; if (16 < oob_len) cmd->len = 16; else @@ -964,7 +964,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) cmd->cmd = 0; cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); - cmd->dst = NAND_EXEC_CMD; + cmd->dst = MSM_NAND_EXEC_CMD; cmd->len = 4; cmd++; @@ -972,7 +972,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) * read the status register */ cmd->cmd = SRC_CRCI_NAND_DATA; - cmd->src = NAND_FLASH_STATUS; + cmd->src = MSM_NAND_FLASH_STATUS; cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status[n]); cmd->len = 4; @@ -983,7 +983,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) cmd->cmd = 0; cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.zeroes); - cmd->dst = NAND_FLASH_STATUS; + cmd->dst = MSM_NAND_FLASH_STATUS; cmd->len = 4; cmd++; } @@ -994,7 +994,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) cmd->cmd = 0; cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.ecccfg_restore); - cmd->dst = NAND_EBI2_ECC_BUF_CFG; + cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG; cmd->len = 4; cmd++; } @@ -1108,7 +1108,7 @@ msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) (dma_buffer = msm_nand_get_dma_buffer( chip, sizeof(*dma_buffer)))); - dma_buffer->data[0] = NAND_CMD_BLOCK_ERASE; + dma_buffer->data[0] = MSM_NAND_CMD_BLOCK_ERASE; dma_buffer->data[1] = page; dma_buffer->data[2] = 0; dma_buffer->data[3] = 0 | 4; @@ -1121,21 +1121,21 @@ msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) dma_buffer->cmd[0].cmd = DST_CRCI_NAND_CMD | CMD_OCB; dma_buffer->cmd[0].src = msm_virt_to_dma(chip, &dma_buffer->data[0]); - dma_buffer->cmd[0].dst = NAND_FLASH_CMD; + dma_buffer->cmd[0].dst = MSM_NAND_FLASH_CMD; dma_buffer->cmd[0].len = 16; dma_buffer->cmd[1].cmd = 0; dma_buffer->cmd[1].src = msm_virt_to_dma(chip, &dma_buffer->data[6]); - dma_buffer->cmd[1].dst = NAND_DEV0_CFG0; + dma_buffer->cmd[1].dst = MSM_NAND_DEV0_CFG0; dma_buffer->cmd[1].len = 8; dma_buffer->cmd[2].cmd = 0; dma_buffer->cmd[2].src = msm_virt_to_dma(chip, &dma_buffer->data[4]); - dma_buffer->cmd[2].dst = NAND_EXEC_CMD; + dma_buffer->cmd[2].dst = MSM_NAND_EXEC_CMD; dma_buffer->cmd[2].len = 4; dma_buffer->cmd[3].cmd = SRC_CRCI_NAND_DATA; - dma_buffer->cmd[3].src = NAND_FLASH_STATUS; + dma_buffer->cmd[3].src = MSM_NAND_FLASH_STATUS; dma_buffer->cmd[3].dst = msm_virt_to_dma(chip, &dma_buffer->data[5]); dma_buffer->cmd[3].len = 4; @@ -1143,7 +1143,7 @@ msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) * due to the write, to work around a h/w bug */ dma_buffer->cmd[4].cmd = CMD_OCU | CMD_LC; dma_buffer->cmd[4].src = msm_virt_to_dma(chip, &dma_buffer->data[8]); - dma_buffer->cmd[4].dst = NAND_FLASH_STATUS; + dma_buffer->cmd[4].dst = MSM_NAND_FLASH_STATUS; dma_buffer->cmd[4].len = 4; BUILD_BUG_ON(4 != ARRAY_SIZE(dma_buffer->cmd) - 1); @@ -1227,9 +1227,9 @@ msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) cmd = dma_buffer->cmd; - dma_buffer->data.cmd = NAND_CMD_PAGE_READ; - dma_buffer->data.cfg0 = NAND_CFG0_RAW & ~(7U << 6); - dma_buffer->data.cfg1 = NAND_CFG1_RAW | (chip->CFG1 & CFG1_WIDE_FLASH); + dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; + dma_buffer->data.cfg0 = MSM_NAND_CFG0_RAW & ~(7U << 6); + dma_buffer->data.cfg1 = MSM_NAND_CFG1_RAW | (chip->CFG1 & CFG1_WIDE_FLASH); if (chip->CFG1 & CFG1_WIDE_FLASH) dma_buffer->data.addr0 = (page << 16) | (0x630 >> 1); @@ -1246,30 +1246,30 @@ msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) cmd->cmd = DST_CRCI_NAND_CMD; cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); - cmd->dst = NAND_FLASH_CMD; + cmd->dst = MSM_NAND_FLASH_CMD; cmd->len = 16; cmd++; cmd->cmd = 0; cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); - cmd->dst = NAND_DEV0_CFG0; + cmd->dst = MSM_NAND_DEV0_CFG0; cmd->len = 8; cmd++; cmd->cmd = 0; cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); - cmd->dst = NAND_EXEC_CMD; + cmd->dst = MSM_NAND_EXEC_CMD; cmd->len = 4; cmd++; cmd->cmd = SRC_CRCI_NAND_DATA; - cmd->src = NAND_FLASH_STATUS; + cmd->src = MSM_NAND_FLASH_STATUS; cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result); cmd->len = 8; cmd++; cmd->cmd = 0; - cmd->src = NAND_FLASH_BUFFER + 464; + cmd->src = MSM_NAND_FLASH_BUFFER + 464; cmd->dst = msm_virt_to_dma(chip, buf); cmd->len = 4; cmd++; @@ -1394,10 +1394,10 @@ int msm_nand_scan(struct mtd_info *mtd, int maxchips) (n >> 6) & 7, (n >> 9) & 0x3ff, (n >> 19) & 15, (n >> 23) & 15); - n = flash_rd_reg(chip, NAND_DEV_CMD1); + n = flash_rd_reg(chip, MSM_NAND_DEV_CMD1); pr_info("DEV_CMD1: %x\n", n); - n = flash_rd_reg(chip, NAND_EBI2_ECC_BUF_CFG); + n = flash_rd_reg(chip, MSM_NAND_EBI2_ECC_BUF_CFG); pr_info(KERN_INFO "NAND_EBI2_ECC_BUF_CFG: %x\n", n); #if SUPPORT_WRONG_ECC_CONFIG diff --git a/drivers/mtd/devices/msm_nand.h b/drivers/mtd/devices/msm_nand.h index b5e6293e929ab..18a0a06c9288a 100644 --- a/drivers/mtd/devices/msm_nand.h +++ b/drivers/mtd/devices/msm_nand.h @@ -18,54 +18,54 @@ #include -#define NAND_REG(off) (MSM_NAND_BASE + (off)) +#define MSM_NAND_REG(off) (MSM_NAND_BASE + (off)) -#define NAND_FLASH_CMD NAND_REG(0x0000) -#define NAND_ADDR0 NAND_REG(0x0004) -#define NAND_ADDR1 NAND_REG(0x0008) -#define NAND_FLASH_CHIP_SELECT NAND_REG(0x000C) -#define NAND_EXEC_CMD NAND_REG(0x0010) -#define NAND_FLASH_STATUS NAND_REG(0x0014) -#define NAND_BUFFER_STATUS NAND_REG(0x0018) -#define NAND_DEV0_CFG0 NAND_REG(0x0020) -#define NAND_DEV0_CFG1 NAND_REG(0x0024) -#define NAND_DEV1_CFG0 NAND_REG(0x0030) -#define NAND_DEV1_CFG1 NAND_REG(0x0034) -#define NAND_READ_ID NAND_REG(0x0040) -#define NAND_READ_STATUS NAND_REG(0x0044) -#define NAND_CONFIG_DATA NAND_REG(0x0050) -#define NAND_CONFIG NAND_REG(0x0054) -#define NAND_CONFIG_MODE NAND_REG(0x0058) -#define NAND_CONFIG_STATUS NAND_REG(0x0060) -#define NAND_MACRO1_REG NAND_REG(0x0064) -#define NAND_XFR_STEP1 NAND_REG(0x0070) -#define NAND_XFR_STEP2 NAND_REG(0x0074) -#define NAND_XFR_STEP3 NAND_REG(0x0078) -#define NAND_XFR_STEP4 NAND_REG(0x007C) -#define NAND_XFR_STEP5 NAND_REG(0x0080) -#define NAND_XFR_STEP6 NAND_REG(0x0084) -#define NAND_XFR_STEP7 NAND_REG(0x0088) -#define NAND_DEV_CMD0 NAND_REG(0x00A0) -#define NAND_DEV_CMD1 NAND_REG(0x00A4) -#define NAND_DEV_CMD2 NAND_REG(0x00A8) -#define NAND_DEV_CMD_VLD NAND_REG(0x00AC) -#define NAND_EBI2_MISR_SIG_REG NAND_REG(0x00B0) -#define NAND_EBI2_ECC_BUF_CFG NAND_REG(0x00F0) -#define NAND_FLASH_BUFFER NAND_REG(0x0100) +#define MSM_NAND_FLASH_CMD MSM_NAND_REG(0x0000) +#define MSM_NAND_ADDR0 MSM_NAND_REG(0x0004) +#define MSM_NAND_ADDR1 MSM_NAND_REG(0x0008) +#define MSM_NAND_FLASH_CHIP_SELECT MSM_NAND_REG(0x000C) +#define MSM_NAND_EXEC_CMD MSM_NAND_REG(0x0010) +#define MSM_NAND_FLASH_STATUS MSM_NAND_REG(0x0014) +#define MSM_NAND_BUFFER_STATUS MSM_NAND_REG(0x0018) +#define MSM_NAND_DEV0_CFG0 MSM_NAND_REG(0x0020) +#define MSM_NAND_DEV0_CFG1 MSM_NAND_REG(0x0024) +#define MSM_NAND_DEV1_CFG0 MSM_NAND_REG(0x0030) +#define MSM_NAND_DEV1_CFG1 MSM_NAND_REG(0x0034) +#define MSM_NAND_READ_ID MSM_NAND_REG(0x0040) +#define MSM_NAND_READ_STATUS MSM_NAND_REG(0x0044) +#define MSM_NAND_CONFIG_DATA MSM_NAND_REG(0x0050) +#define MSM_NAND_CONFIG MSM_NAND_REG(0x0054) +#define MSM_NAND_CONFIG_MODE MSM_NAND_REG(0x0058) +#define MSM_NAND_CONFIG_STATUS MSM_NAND_REG(0x0060) +#define MSM_NAND_MACRO1_REG MSM_NAND_REG(0x0064) +#define MSM_NAND_XFR_STEP1 MSM_NAND_REG(0x0070) +#define MSM_NAND_XFR_STEP2 MSM_NAND_REG(0x0074) +#define MSM_NAND_XFR_STEP3 MSM_NAND_REG(0x0078) +#define MSM_NAND_XFR_STEP4 MSM_NAND_REG(0x007C) +#define MSM_NAND_XFR_STEP5 MSM_NAND_REG(0x0080) +#define MSM_NAND_XFR_STEP6 MSM_NAND_REG(0x0084) +#define MSM_NAND_XFR_STEP7 MSM_NAND_REG(0x0088) +#define MSM_NAND_DEV_CMD0 MSM_NAND_REG(0x00A0) +#define MSM_NAND_DEV_CMD1 MSM_NAND_REG(0x00A4) +#define MSM_NAND_DEV_CMD2 MSM_NAND_REG(0x00A8) +#define MSM_NAND_DEV_CMD_VLD MSM_NAND_REG(0x00AC) +#define MSM_NAND_EBI2_MISR_SIG_REG MSM_NAND_REG(0x00B0) +#define MSM_NAND_EBI2_ECC_BUF_CFG MSM_NAND_REG(0x00F0) +#define MSM_NAND_FLASH_BUFFER MSM_NAND_REG(0x0100) /* device commands */ -#define NAND_CMD_SOFT_RESET 0x01 -#define NAND_CMD_PAGE_READ 0x32 -#define NAND_CMD_PAGE_READ_ECC 0x33 -#define NAND_CMD_PAGE_READ_ALL 0x34 -#define NAND_CMD_SEQ_PAGE_READ 0x15 -#define NAND_CMD_PRG_PAGE 0x36 -#define NAND_CMD_PRG_PAGE_ECC 0x37 -#define NAND_CMD_PRG_PAGE_ALL 0x39 -#define NAND_CMD_BLOCK_ERASE 0x3A -#define NAND_CMD_FETCH_ID 0x0B -#define NAND_CMD_STATUS 0x0C -#define NAND_CMD_RESET 0x0D +#define MSM_NAND_CMD_SOFT_RESET 0x01 +#define MSM_NAND_CMD_PAGE_READ 0x32 +#define MSM_NAND_CMD_PAGE_READ_ECC 0x33 +#define MSM_NAND_CMD_PAGE_READ_ALL 0x34 +#define MSM_NAND_CMD_SEQ_PAGE_READ 0x15 +#define MSM_NAND_CMD_PRG_PAGE 0x36 +#define MSM_NAND_CMD_PRG_PAGE_ECC 0x37 +#define MSM_NAND_CMD_PRG_PAGE_ALL 0x39 +#define MSM_NAND_CMD_BLOCK_ERASE 0x3A +#define MSM_NAND_CMD_FETCH_ID 0x0B +#define MSM_NAND_CMD_STATUS 0x0C +#define MSM_NAND_CMD_RESET 0x0D #endif From 06980a7bde9c9a3f8a965f6bf879e2170a7d3afa Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 10 Feb 2010 15:36:51 -0800 Subject: [PATCH 0157/2556] [ARM] mtd: msm_nand: convert to using the flash_ids table for nand device info Rather than hard code yet another table of nand device ids, use the list that is part of the nand subsystem. Also, use the data in the extended device id info to properly set the device topology, instead of hardcoding. Change-Id: I3fd1493dabaae401ce6f33d0e6059c4296bb26e1 Signed-off-by: Dima Zavin --- drivers/mtd/devices/Kconfig | 1 + drivers/mtd/devices/msm_nand.c | 150 +++++++++++++++++++-------------- 2 files changed, 89 insertions(+), 62 deletions(-) diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 4644a7b4810d7..03fe3e3d3294b 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -52,6 +52,7 @@ config MTD_MS02NV config MTD_MSM_NAND tristate "MSM NAND Device Support" depends on MTD && ARCH_MSM + select MTD_NAND_IDS default y help Support for some NAND chips connected to the MSM NAND controller. diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index dcdecd54172d4..c79d6bb22933c 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -185,11 +186,8 @@ uint32_t flash_read_id(struct msm_nand_chip *chip) chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); - pr_info("status: %x\n", dma_buffer->data[3]); - pr_info("nandid: %x maker %02x device %02x\n", - dma_buffer->data[4], dma_buffer->data[4] & 0xff, - (dma_buffer->data[4] >> 8) & 0xff); rv = dma_buffer->data[4]; + pr_info("msn_nand: nandid %x status %x\n", rv, dma_buffer->data[3]); msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); return rv; } @@ -229,22 +227,10 @@ int flash_read_config(struct msm_nand_chip *chip) chip->CFG0 = dma_buffer->cfg0; chip->CFG1 = dma_buffer->cfg1; - pr_info("read CFG0 = %x, CFG1 = %x\n", chip->CFG0, chip->CFG1); - chip->CFG0 = (3 << 6) /* 4 codeword per page for 2k nand */ - | (516 << 9) /* 516 user data bytes */ - | (10 << 19) /* 10 parity bytes */ - | (5 << 27) /* 5 address cycles */ - | (1 << 30) /* Read status before data */ - | (1 << 31) /* Send read cmd */ - /* 0 spare bytes for 16 bit nand or 1 spare bytes for 8 bit */ - | ((chip->CFG1 & CFG1_WIDE_FLASH) ? (0 << 23) : (1 << 23)); - chip->CFG1 = (0 << 0) /* Enable ecc */ - | (7 << 2) /* 8 recovery cycles */ - | (0 << 5) /* Allow CS deassertion */ - | (465 << 6) /* Bad block marker location */ - | (0 << 16) /* Bad block in user data area */ - | (2 << 17) /* 6 cycle tWB/tRB */ - | (chip->CFG1 & CFG1_WIDE_FLASH); /* preserve wide flag */ + pr_info("msm_nand: read CFG0 = %x CFG1 = %x\n", chip->CFG0, chip->CFG1); + pr_info("msm_nand: CFG0 cw/page=%d ud_sz=%d ecc_sz=%d spare_sz=%d\n", + (chip->CFG0 >> 6) & 7, (chip->CFG0 >> 9) & 0x3ff, + (chip->CFG0 >> 19) & 15, (chip->CFG0 >> 23) & 15); msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); @@ -1357,6 +1343,7 @@ static void msm_nand_resume(struct mtd_info *mtd) { } + /** * msm_nand_scan - [msm_nand Interface] Scan for the msm_nand device * @param mtd MTD device structure @@ -1372,65 +1359,104 @@ int msm_nand_scan(struct mtd_info *mtd, int maxchips) unsigned n; struct msm_nand_chip *chip = mtd->priv; uint32_t flash_id; - + uint32_t manid; + uint32_t devid; + uint32_t devcfg; + uint32_t busw16; + struct nand_flash_dev *flashdev = NULL; + struct nand_manufacturers *flashman = NULL; if (flash_read_config(chip)) { pr_err("ERRROR: could not save CFG0 & CFG1 state\n"); return -ENODEV; } - pr_info("CFG0 = %x, CFG1 = %x\n", chip->CFG0, chip->CFG1); - pr_info("CFG0: cw/page=%d ud_sz=%d ecc_sz=%d spare_sz=%d " - "num_addr_cycles=%d\n", (chip->CFG0 >> 6) & 7, - (chip->CFG0 >> 9) & 0x3ff, (chip->CFG0 >> 19) & 15, - (chip->CFG0 >> 23) & 15, (chip->CFG0 >> 27) & 7); - - pr_info("NAND_READ_ID = %x\n", flash_rd_reg(chip, NAND_READ_ID)); - flash_wr_reg(chip, NAND_READ_ID, 0x12345678); + pr_info("msm_nand: NAND_READ_ID = %x\n", + flash_rd_reg(chip, MSM_NAND_READ_ID)); + flash_wr_reg(chip, MSM_NAND_READ_ID, 0x12345678); flash_id = flash_read_id(chip); + manid = flash_id & 0xff; + devid = (flash_id >> 8) & 0xff; + devcfg = (flash_id >> 24) & 0xff; + + for (n = 0; !flashman && nand_manuf_ids[n].id; ++n) + if (nand_manuf_ids[n].id == manid) + flashman = &nand_manuf_ids[n]; + for (n = 0; !flashdev && nand_flash_ids[n].id; ++n) + if (nand_flash_ids[n].id == devid) + flashdev = &nand_flash_ids[n]; + if (!flashdev || !flashman) { + pr_err("ERROR: unknown nand device manuf=%x devid=%x\n", + manid, devid); + return -ENOENT; + } - n = flash_rd_reg(chip, NAND_DEV0_CFG0); - pr_info("CFG0: cw/page=%d ud_sz=%d ecc_sz=%d spare_sz=%d\n", - (n >> 6) & 7, (n >> 9) & 0x3ff, (n >> 19) & 15, - (n >> 23) & 15); + if (!flashdev->pagesize) { + mtd->erasesize = (64 * 1024) << ((devcfg >> 4) & 0x3); + mtd->writesize = 1024 << (devcfg & 0x3); + mtd->oobsize = (8 << ((devcfg >> 2) & 1)) * + (mtd->writesize / 512); + busw16 = devcfg & (1 << 6) ? CFG1_WIDE_FLASH : 0; + } else { + mtd->writesize = flashdev->pagesize; + mtd->erasesize = flashdev->erasesize; + mtd->oobsize = flashdev->pagesize / 32; + busw16 = flashdev->options & NAND_BUSWIDTH_16 ? + CFG1_WIDE_FLASH : 0; + } + mtd->size = flashdev->chipsize << 20; + pr_info("msm_nand: manuf %s (0x%x) device 0x%x blocksz %x pagesz %x " + "size %llx\n", flashman->name, flashman->id, flashdev->id, + mtd->erasesize, mtd->writesize, mtd->size); + + if (mtd->writesize != 2048) { + pr_err("%s: Unsupported page size (%d)\n", __func__, + mtd->writesize); + return -EINVAL; + } + + if (mtd->oobsize == 64) { + mtd->ecclayout = &msm_nand_oob_64; + } else { + pr_err("%s: Unsupported oob size (%d)\n", __func__, + mtd->oobsize); + return -EINVAL; + } + mtd->oobavail = mtd->ecclayout->oobavail; + + chip->CFG0 = (3 << 6) /* 4 codeword per page for 2k nand */ + | (516 << 9) /* 516 user data bytes */ + | (10 << 19) /* 10 parity bytes */ + | (5 << 27) /* 5 address cycles */ + | (1 << 30) /* Read status before data */ + | (1 << 31) /* Send read cmd */ + /* 0 spare bytes for 16 bit nand or 1 spare bytes for 8 bit */ + | ((busw16 & CFG1_WIDE_FLASH) ? (0 << 23) : (1 << 23)); + chip->CFG1 = (0 << 0) /* Enable ecc */ + | (7 << 2) /* 8 recovery cycles */ + | (0 << 5) /* Allow CS deassertion */ + | (465 << 6) /* Bad block marker location */ + | (0 << 16) /* Bad block in user data area */ + | (2 << 17) /* 6 cycle tWB/tRB */ + | (busw16 & CFG1_WIDE_FLASH); /* preserve wide flag */ + + pr_info("msm_nand: save CFG0 = %x CFG1 = %x\n", chip->CFG0, chip->CFG1); + pr_info("msm_nand: CFG0: cw/page=%d ud_sz=%d ecc_sz=%d spare_sz=%d " + "num_addr_cycles=%d\n", (chip->CFG0 >> 6) & 7, + (chip->CFG0 >> 9) & 0x3ff, (chip->CFG0 >> 19) & 15, + (chip->CFG0 >> 23) & 15, (chip->CFG0 >> 27) & 7); n = flash_rd_reg(chip, MSM_NAND_DEV_CMD1); - pr_info("DEV_CMD1: %x\n", n); + pr_info("msm_nand: DEV_CMD1: %x\n", n); n = flash_rd_reg(chip, MSM_NAND_EBI2_ECC_BUF_CFG); - pr_info(KERN_INFO "NAND_EBI2_ECC_BUF_CFG: %x\n", n); + pr_info("msm_nand: NAND_EBI2_ECC_BUF_CFG: %x\n", n); #if SUPPORT_WRONG_ECC_CONFIG chip->ecc_buf_cfg = 0x203; chip->saved_ecc_buf_cfg = n; #endif - if ((flash_id & 0xffff) == 0xaaec) /* 2Gbit Samsung chip */ - mtd->size = 256 << 20; /* * num_chips */ - else if (flash_id == 0x5580baad) /* 2Gbit Hynix chip */ - mtd->size = 256 << 20; /* * num_chips */ - else if (flash_id == 0x5510baad) /* 2Gbit Hynix chip */ - mtd->size = 256 << 20; /* * num_chips */ - - if ((flash_id & 0xffff) == 0xacec) /* 4G/1Gbit Samsung chip */ - mtd->size = 512 << 20; /* * num_chips */ - else if (flash_id == 0x5510bcad) /* 4Gbit Hynix chip */ - mtd->size = 512 << 20; /* * num_chips */ - else if ((flash_id & 0xffff) == 0xbcec) /* 4Gbit Samsung chip */ - mtd->size = 512 << 20; /* * num_chips */ - else if (flash_id == 0x5590bc2c) /* 4Gbit Micron chip */ - mtd->size = 512 << 20; /* * num_chips */ - else if (flash_id == 0x5580ba2c) /* 2Gbit Micron chip */ - mtd->size = 256 << 20; - - pr_info("flash_id: %x size %llx\n", flash_id, mtd->size); - - mtd->writesize = 2048; - mtd->oobsize = 64; - mtd->oobavail = msm_nand_oob_64.oobavail; - mtd->erasesize = mtd->writesize << 6; /* TODO: check */ - mtd->ecclayout = &msm_nand_oob_64; - /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; @@ -1519,7 +1545,7 @@ static int __devinit msm_nand_probe(struct platform_device *pdev) goto out_free_info; } - pr_info("allocated dma buffer at %p, dma_addr %x\n", + pr_info("msm_nand: allocated dma buffer at %p, dma_addr %x\n", info->msm_nand.dma_buffer, info->msm_nand.dma_addr); info->mtd.name = dev_name(&pdev->dev); From 0da7abeeb9e26e9fd653452f576f1df382baa66b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 7 Apr 2010 22:55:22 -0700 Subject: [PATCH 0158/2556] [ARM] mtd: msm_nand: Move empty page check after dma_unmap_page. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a crash in dma_sync_single_for_cpu when reading a flash page with an uncorrectable error into a highmem page. Change-Id: Ie0d9e80f1efb2d7cc4d95315127f94fb6b89e699 Signed-off-by: Arve Hjønnevåg --- drivers/mtd/devices/msm_nand.c | 165 +++++++++++++++++---------------- 1 file changed, 87 insertions(+), 78 deletions(-) diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index c79d6bb22933c..e294750b11d97 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -316,6 +316,44 @@ msm_nand_dma_map(struct device *dev, void *addr, size_t size, return dma_map_page(dev, page, offset, size, dir); } +static int msm_nand_check_empty(struct mtd_info *mtd, struct mtd_oob_ops *ops, + unsigned long *uncorrected) +{ + unsigned int p, n, end; + uint8_t *datbuf = ops->datbuf; + uint8_t *oobbuf = ops->oobbuf; + size_t oobsize; + int page_count; + + if (ops->mode == MTD_OOB_RAW) + return false; + + page_count = ops->retlen / mtd->writesize; + oobsize = (ops->mode == MTD_OOB_AUTO) ? mtd->oobavail : mtd->oobsize; + + for_each_set_bit(p, uncorrected, page_count) { + if (datbuf) { + datbuf = ops->datbuf + p * mtd->writesize; + for (n = 0; n < mtd->writesize; n++) { + /* empty blocks read 0x54 at these offsets */ + if (datbuf[n] != ((n % 516 == 3) ? 0x54 : 0xff)) + return false; + } + } + if (oobbuf) { + n = p * oobsize; + end = min(n + oobsize, ops->oobretlen); + for(; n < end; n++) + if (oobbuf[n] != 0xff) + return false; + } + if (ops->datbuf) + for (n = 3; n < mtd->writesize; n+= 516) + datbuf[n] = 0xff; + } + return true; +} + static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { @@ -348,7 +386,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, uint32_t oob_len = ops->ooblen; uint32_t sectordatasize; uint32_t sectoroobsize; - int err, pageerr, rawerr; + int err, pageerr; dma_addr_t data_dma_addr = 0; dma_addr_t oob_dma_addr = 0; dma_addr_t data_dma_addr_curr = 0; @@ -358,7 +396,10 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, unsigned pages_read = 0; unsigned start_sector = 0; uint32_t ecc_errors; - uint32_t total_ecc_errors = 0; + uint32_t total_corrected = 0; + uint32_t total_uncorrected = 0; + unsigned long uncorrected_noalloc = 0; + unsigned long *uncorrected = &uncorrected_noalloc; if (from & (mtd->writesize - 1)) { pr_err("%s: unsupported from, 0x%llx\n", @@ -433,6 +474,14 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, goto err_dma_map_oobbuf_failed; } } + if (BITS_TO_LONGS(page_count) > 1) { + uncorrected = kzalloc(BITS_TO_LONGS(page_count) * sizeof(long), + GFP_NOIO); + if (!uncorrected) { + err = -ENOMEM; + goto err_alloc_uncorrected_failed; + } + } wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer( @@ -601,91 +650,42 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, /* if any of the writes failed (0x10), or there * was a protection violation (0x100), we lose */ - pageerr = rawerr = 0; + pageerr = 0; + ecc_errors = 0; for (n = start_sector; n < 4; n++) { - if (dma_buffer->data.result[n].flash_status & 0x110) { - rawerr = -EIO; + if (dma_buffer->data.result[n].buffer_status & 0x8) { + total_uncorrected++; + uncorrected[BIT_WORD(pages_read)] |= + BIT_MASK(pages_read); + pageerr = -EBADMSG; break; } - } - if (rawerr) { - if (ops->datbuf && ops->mode != MTD_OOB_RAW) { - uint8_t *datbuf = - ops->datbuf + pages_read * 2048; - - dma_sync_single_for_cpu(chip->dev, - data_dma_addr_curr-mtd->writesize, - mtd->writesize, DMA_BIDIRECTIONAL); - - for (n = 0; n < 2048; n++) { - /* empty blocks read 0x54 at - * these offsets - */ - if (n % 516 == 3 && datbuf[n] == 0x54) - datbuf[n] = 0xff; - if (datbuf[n] != 0xff) { - pageerr = rawerr; - break; - } - } - - dma_sync_single_for_device(chip->dev, - data_dma_addr_curr-mtd->writesize, - mtd->writesize, DMA_BIDIRECTIONAL); - - } - if (ops->oobbuf) { - for (n = 0; n < ops->ooblen; n++) { - if (ops->oobbuf[n] != 0xff) { - pageerr = rawerr; - break; - } - } + if (dma_buffer->data.result[n].flash_status & 0x110) { + pageerr = -EIO; + break; } + ecc_errors += + dma_buffer->data.result[n].buffer_status & 0x7; } - if (pageerr) { - for (n = start_sector; n < 4; n++) { - if (dma_buffer->data.result[n].buffer_status & - 0x8) { - /* not thread safe */ - mtd->ecc_stats.failed++; - pageerr = -EBADMSG; - break; - } - } - } - if (!rawerr) { /* check for corretable errors */ - for (n = start_sector; n < 4; n++) { - ecc_errors = dma_buffer->data. - result[n].buffer_status & 0x7; - if (ecc_errors) { - total_ecc_errors += ecc_errors; - /* not thread safe */ - mtd->ecc_stats.corrected += ecc_errors; - if (ecc_errors > 1) - pageerr = -EUCLEAN; - } - } + if (!pageerr && ecc_errors) { + total_corrected += ecc_errors; + /* not thread safe */ + mtd->ecc_stats.corrected += ecc_errors; + pageerr = -EUCLEAN; } if (pageerr && (pageerr != -EUCLEAN || err == 0)) err = pageerr; #if VERBOSE - if (rawerr && !pageerr) { - pr_err("msm_nand_read_oob %llx %x %x empty page\n", - (loff_t)page * mtd->writesize, ops->len, - ops->ooblen); - } else { - pr_info("status: %x %x %x %x %x %x %x %x\n", - dma_buffer->data.result[0].flash_status, - dma_buffer->data.result[0].buffer_status, - dma_buffer->data.result[1].flash_status, - dma_buffer->data.result[1].buffer_status, - dma_buffer->data.result[2].flash_status, - dma_buffer->data.result[2].buffer_status, - dma_buffer->data.result[3].flash_status, - dma_buffer->data.result[3].buffer_status); - } + pr_info("status: %x %x %x %x %x %x %x %x\n", + dma_buffer->data.result[0].flash_status, + dma_buffer->data.result[0].buffer_status, + dma_buffer->data.result[1].flash_status, + dma_buffer->data.result[1].buffer_status, + dma_buffer->data.result[2].flash_status, + dma_buffer->data.result[2].buffer_status, + dma_buffer->data.result[3].flash_status, + dma_buffer->data.result[3].buffer_status); #endif if (err && err != -EUCLEAN && err != -EBADMSG) break; @@ -694,6 +694,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, } msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); +err_alloc_uncorrected_failed: if (ops->oobbuf) { dma_unmap_page(chip->dev, oob_dma_addr, ops->ooblen, DMA_FROM_DEVICE); @@ -710,10 +711,18 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, ops->retlen = (mtd->writesize + mtd->oobsize) * pages_read; ops->oobretlen = ops->ooblen - oob_len; + + if (err == -EBADMSG && msm_nand_check_empty(mtd, ops, uncorrected)) + err = 0; + else if (total_uncorrected) + mtd->ecc_stats.failed += total_uncorrected; /* not threadsafe */ + if (uncorrected != &uncorrected_noalloc) + kfree(uncorrected); + if (err) pr_err("msm_nand_read_oob %llx %x %x failed %d, corrected %d\n", from, ops->datbuf ? ops->len : 0, ops->ooblen, err, - total_ecc_errors); + total_corrected); return err; } From d4d4d88d502f2984c5d8f5b96ee0c5260e5a4c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 16 Sep 2008 16:44:25 -0700 Subject: [PATCH 0159/2556] msm_nand: Don't return -EUCLEAN for single error corrections. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change was lost by: Move empty page check after dma_unmap_page. Single bit errors are frequent. If we don't correct more than one symbol per sector, return zero so yaffs does not retire the block. Change-Id: If3b7b219babe026e5c52387186a8513f9b0d03fb Signed-off-by: Arve Hjønnevåg --- drivers/mtd/devices/msm_nand.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index e294750b11d97..7c6ef1a60eb38 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -395,7 +395,8 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, unsigned page_count; unsigned pages_read = 0; unsigned start_sector = 0; - uint32_t ecc_errors; + uint32_t sector_corrected; + uint32_t page_corrected; uint32_t total_corrected = 0; uint32_t total_uncorrected = 0; unsigned long uncorrected_noalloc = 0; @@ -651,7 +652,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, * was a protection violation (0x100), we lose */ pageerr = 0; - ecc_errors = 0; + page_corrected = 0; for (n = start_sector; n < 4; n++) { if (dma_buffer->data.result[n].buffer_status & 0x8) { total_uncorrected++; @@ -664,14 +665,16 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, pageerr = -EIO; break; } - ecc_errors += + sector_corrected = dma_buffer->data.result[n].buffer_status & 0x7; + page_corrected += sector_corrected; + if (sector_corrected > 1) + pageerr = -EUCLEAN; } - if (!pageerr && ecc_errors) { - total_corrected += ecc_errors; + if ((!pageerr && page_corrected) || pageerr == -EUCLEAN) { + total_corrected += page_corrected; /* not thread safe */ - mtd->ecc_stats.corrected += ecc_errors; - pageerr = -EUCLEAN; + mtd->ecc_stats.corrected += page_corrected; } if (pageerr && (pageerr != -EUCLEAN || err == 0)) err = pageerr; From b72ac76255aadaa7f4768448561f7caba09631ba Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 12 Feb 2010 01:22:17 -0800 Subject: [PATCH 0160/2556] mtd: msm_nand: round up, not down, the page count when only reading OOB Change-Id: I4e352aba0463c822f5be6d89753a09fc8b3efb59 Signed-off-by: Dima Zavin --- drivers/mtd/devices/msm_nand.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index 7c6ef1a60eb38..4ae24d1bdbdbd 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -432,10 +432,11 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, if (ops->oobbuf && !ops->datbuf && ops->mode == MTD_OOB_AUTO) start_sector = 3; - if (ops->oobbuf && !ops->datbuf) - page_count = ops->ooblen / ((ops->mode == MTD_OOB_AUTO) ? - mtd->oobavail : mtd->oobsize); - else if (ops->mode != MTD_OOB_RAW) + if (ops->oobbuf && !ops->datbuf) { + unsigned tmpoobsz = (ops->mode == MTD_OOB_AUTO) ? + mtd->oobavail : mtd->oobsize; + page_count = DIV_ROUND_UP(ops->ooblen, tmpoobsz); + } else if (ops->mode != MTD_OOB_RAW) page_count = ops->len / mtd->writesize; else page_count = ops->len / (mtd->writesize + mtd->oobsize); From a2bac657e008efc540f126534e27413b385cdfaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Sat, 1 Dec 2007 18:47:49 -0800 Subject: [PATCH 0161/2556] [ARM] msm: gpio support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide an implementation of the generic gpio API for the MSM family. An extended API provides single-step configuration and access to edge detect status. Signed-off-by: Brian Swetland Signed-off-by: Arve Hjønnevåg [ARM] msm: generic_gpio: Don't pass GFP_KERNEL to kmalloc when holding a spinlock Signed-off-by: Arve Hjønnevåg [ARM] msm: gpio: Don't call generic_handle_irq with interrupts enabled Signed-off-by: Arve Hjønnevåg [ARM] msm: add irq values and vic support for scorpion Signed-off-by: Brian Swetland [ARM] msm: add gpio support for QSD QSD adds two more GPIO banks. Signed-off-by: Dima Zavin [ARM] msm: fix check on gpio smem allocation GPIO data is fully packed in SMEM, unlike on the SoC where there is empty space in some GPIO registers. Signed-off-by: Brian Swetland [ARM] msm: Remove extended gpio api. Change-Id: Ia3ebf9f50661514d3771dddf94e932995bd2b10f Signed-off-by: Arve Hjønnevåg [ARM] msm: generic_gpio: Use gpiolib. register_gpio_chip now call gpiochip_add with wrapper functions that hold a spinlock while calling the old generic_gpio gpio_chip functions. Change-Id: Ica8f705db97d3b18c98a695ebb28c3fcf58598df Signed-off-by: Arve Hjønnevåg ARM: msm: gpio: add msm7x30 support Change-Id: I7d6cac7d1fc6ed64646025754820b026576094d4 Signed-off-by: Dima Zavin [ARM] msm: gpio: Resolve checkpatch complaints. Resolve checkpatch errors and warnings in msmgpio, since being checkpatch-clean is a prerequisite to successful upstream submission of sources to kernel.org. Change-Id: I76922f2df4a84929e8b87c277c2a55a914ea689e Signed-off-by: Gregory Bean [ARM] msm: gpio: make msmgpio gpiolib-native. Remove the now-obsolete generic_chip layer and convert msmgpio to a native gpiolib implementation. Change-Id: Idf8bfe96c65da3df82023cfd377c9627d5acac85 Signed-off-by: Gregory Bean [ARM] msm: retire generic_gpio.c and gpio_chip.h Now that gpiolib is used by all targets, the need for a generic shim to the gpio code has vanished. Retire generic_gpio.c and gpio_chip.h. Change-Id: I622f7a797d0990c673f20112fdfd73e3f766d102 Signed-off-by: Gregory Bean msm: gpio: fix compilation outside of android-msm-2.6.35-wip. Make small changes to msmgpio to allow it to compile cleanly on CAF and upstream trees: - add defines for missing architectures - standardize some header includes - remove unused gpio utility functions Signed-off-by: Gregory Bean msm: gpio: prefix gpio defines with MSM_. Avoid namespace clashes by prefixing all gpio defines with MSM_, in response to upstream code review feedback. Cc: H Hartley Sweeten Signed-off-by: Gregory Bean msm: gpio: replace chip definitions with macro. Replace the gpio_chip definitions with a macro, in response to upstream-submission review feedback from: hartleys@visionengravers.com ryan@bluewatersys.com Signed-off-by: Gregory Bean msm: gpio: upgrade variable names. Replace one-letter variable names for non-loop-counter variables with more meaningful names, as per upstream code review feedback from ben-linux@fluff.org. Signed-off-by: Gregory Bean --- arch/arm/Kconfig | 2 + arch/arm/mach-msm/gpio.c | 123 +++++++++++++++++++++ arch/arm/mach-msm/include/mach/board.h | 1 - arch/arm/mach-msm/include/mach/irqs-7x00.h | 1 + arch/arm/mach-msm/include/mach/irqs-7x30.h | 1 + arch/arm/mach-msm/include/mach/irqs.h | 8 +- arch/arm/mach-msm/include/mach/sirc.h | 2 - 7 files changed, 133 insertions(+), 5 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 166efa2a19cd9..bacb0cdbd4ead 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -616,7 +616,9 @@ config ARCH_PXA config ARCH_MSM bool "Qualcomm MSM" + select ARCH_REQUIRE_GPIOLIB select HAVE_CLK + select GENERIC_GPIO select GENERIC_CLOCKEVENTS select ARCH_REQUIRE_GPIOLIB help diff --git a/arch/arm/mach-msm/gpio.c b/arch/arm/mach-msm/gpio.c index 176af9dcb8ee4..ba3e970aa52f5 100644 --- a/arch/arm/mach-msm/gpio.c +++ b/arch/arm/mach-msm/gpio.c @@ -25,6 +25,9 @@ #define FIRST_GPIO_IRQ MSM_GPIO_TO_INT(0) +#include "proc_comm.h" +#include "smd_private.h" + #define MSM_GPIO_BANK(bank, first, last) \ { \ .regs = { \ @@ -50,6 +53,14 @@ } \ } +enum { + GPIO_DEBUG_SLEEP = 1U << 0, +}; + +static int msm_gpio_debug_mask; +module_param_named(debug_mask, msm_gpio_debug_mask, int, + S_IRUGO | S_IWUSR | S_IWGRP); + #define MSM_GPIO_BROKEN_INT_CLEAR 1 struct msm_gpio_regs { @@ -345,6 +356,118 @@ static struct irq_chip msm_gpio_irq_chip = { .irq_set_type = msm_gpio_irq_set_type, }; +#define NUM_GPIO_SMEM_BANKS 6 +#define GPIO_SMEM_NUM_GROUPS 2 +#define GPIO_SMEM_MAX_PC_INTERRUPTS 8 +struct tramp_gpio_smem { + uint16_t num_fired[GPIO_SMEM_NUM_GROUPS]; + uint16_t fired[GPIO_SMEM_NUM_GROUPS][GPIO_SMEM_MAX_PC_INTERRUPTS]; + uint32_t enabled[NUM_GPIO_SMEM_BANKS]; + uint32_t detection[NUM_GPIO_SMEM_BANKS]; + uint32_t polarity[NUM_GPIO_SMEM_BANKS]; +}; + +static void msm_gpio_sleep_int(unsigned long arg) +{ + int i, j; + struct tramp_gpio_smem *smem_gpio; + + BUILD_BUG_ON(NR_GPIO_IRQS > NUM_GPIO_SMEM_BANKS * 32); + + smem_gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*smem_gpio)); + if (smem_gpio == NULL) + return; + + local_irq_disable(); + for (i = 0; i < GPIO_SMEM_NUM_GROUPS; i++) { + int count = smem_gpio->num_fired[i]; + for (j = 0; j < count; j++) { + /* TODO: Check mask */ + generic_handle_irq( + MSM_GPIO_TO_INT(smem_gpio->fired[i][j])); + } + } + local_irq_enable(); +} + +static DECLARE_TASKLET(msm_gpio_sleep_int_tasklet, msm_gpio_sleep_int, 0); + +void msm_gpio_enter_sleep(int from_idle) +{ + int i; + struct tramp_gpio_smem *smem_gpio; + + smem_gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*smem_gpio)); + + if (smem_gpio) { + for (i = 0; i < ARRAY_SIZE(smem_gpio->enabled); i++) { + smem_gpio->enabled[i] = 0; + smem_gpio->detection[i] = 0; + smem_gpio->polarity[i] = 0; + } + } + + for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) { + writel(msm_gpio_chips[i].int_enable[!from_idle], + msm_gpio_chips[i].regs.int_en); + if (smem_gpio) { + uint32_t tmp; + int start, index, shiftl, shiftr; + start = msm_gpio_chips[i].chip.base; + index = start / 32; + shiftl = start % 32; + shiftr = 32 - shiftl; + tmp = msm_gpio_chips[i].int_enable[!from_idle]; + smem_gpio->enabled[index] |= tmp << shiftl; + smem_gpio->enabled[index+1] |= tmp >> shiftr; + smem_gpio->detection[index] |= + readl(msm_gpio_chips[i].regs.int_edge) << + shiftl; + smem_gpio->detection[index+1] |= + readl(msm_gpio_chips[i].regs.int_edge) >> + shiftr; + smem_gpio->polarity[index] |= + readl(msm_gpio_chips[i].regs.int_pos) << shiftl; + smem_gpio->polarity[index+1] |= + readl(msm_gpio_chips[i].regs.int_pos) >> shiftr; + } + } + + if (smem_gpio) { + if (msm_gpio_debug_mask & GPIO_DEBUG_SLEEP) + for (i = 0; i < ARRAY_SIZE(smem_gpio->enabled); i++) { + printk("msm_gpio_enter_sleep gpio %d-%d: enable" + " %08x, edge %08x, polarity %08x\n", + i * 32, i * 32 + 31, + smem_gpio->enabled[i], + smem_gpio->detection[i], + smem_gpio->polarity[i]); + } + for (i = 0; i < GPIO_SMEM_NUM_GROUPS; i++) + smem_gpio->num_fired[i] = 0; + } +} + +void msm_gpio_exit_sleep(void) +{ + int i; + struct tramp_gpio_smem *smem_gpio; + + smem_gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*smem_gpio)); + + for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) { + writel(msm_gpio_chips[i].int_enable[0], + msm_gpio_chips[i].regs.int_en); + } + + if (smem_gpio && (smem_gpio->num_fired[0] || smem_gpio->num_fired[1])) { + if (msm_gpio_debug_mask & GPIO_DEBUG_SLEEP) + printk(KERN_INFO "gpio: fired %x %x\n", + smem_gpio->num_fired[0], smem_gpio->num_fired[1]); + tasklet_schedule(&msm_gpio_sleep_int_tasklet); + } +} + static int __init msm_init_gpio(void) { int i, j = 0; diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index 6abf4a6eadc19..624051a4672b0 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -40,7 +40,6 @@ extern struct sys_timer msm_timer; void __init msm_add_devices(void); void __init msm_map_common_io(void); void __init msm_init_irq(void); -void __init msm_init_gpio(void); void __init msm_clock_init(struct clk *clock_tbl, unsigned num_clocks); void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *); int __init msm_add_sdcc(unsigned int controller, diff --git a/arch/arm/mach-msm/include/mach/irqs-7x00.h b/arch/arm/mach-msm/include/mach/irqs-7x00.h index f1fe70612fe9b..65e18d3940709 100644 --- a/arch/arm/mach-msm/include/mach/irqs-7x00.h +++ b/arch/arm/mach-msm/include/mach/irqs-7x00.h @@ -71,5 +71,6 @@ #define NR_MSM_IRQS 64 #define NR_GPIO_IRQS 122 #define NR_BOARD_IRQS 64 +#define NR_SIRC_IRQS 0 #endif diff --git a/arch/arm/mach-msm/include/mach/irqs-7x30.h b/arch/arm/mach-msm/include/mach/irqs-7x30.h index 67c5396514feb..3102add310ee1 100644 --- a/arch/arm/mach-msm/include/mach/irqs-7x30.h +++ b/arch/arm/mach-msm/include/mach/irqs-7x30.h @@ -157,6 +157,7 @@ #define INT_NAND_OP_DONE INT_EBI2_OP_DONE #define NR_MSM_IRQS 128 +#define NR_SIRC_IRQS 0 #define NR_GPIO_IRQS 182 #define PMIC8058_IRQ_BASE (NR_MSM_IRQS + NR_GPIO_IRQS) #define NR_PMIC8058_GPIO_IRQS 40 diff --git a/arch/arm/mach-msm/include/mach/irqs.h b/arch/arm/mach-msm/include/mach/irqs.h index 8679a45647447..e1d9267948e6b 100644 --- a/arch/arm/mach-msm/include/mach/irqs.h +++ b/arch/arm/mach-msm/include/mach/irqs.h @@ -19,6 +19,10 @@ #define MSM_IRQ_BIT(irq) (1 << ((irq) & 31)) +#define FIRST_SIRC_IRQ (NR_MSM_IRQS) +#define FIRST_GPIO_IRQ (NR_MSM_IRQS + NR_SIRC_IRQS) +#define FIRST_BOARD_IRQ (NR_MSM_IRQS + NR_SIRC_IRQS + NR_GPIO_IRQS) + #if defined(CONFIG_ARCH_MSM7X30) #include "irqs-7x30.h" #elif defined(CONFIG_ARCH_QSD8X50) @@ -32,8 +36,8 @@ #error "Unknown architecture specification" #endif -#define NR_IRQS (NR_MSM_IRQS + NR_GPIO_IRQS + NR_BOARD_IRQS) -#define MSM_GPIO_TO_INT(n) (NR_MSM_IRQS + (n)) +#define NR_IRQS (NR_MSM_IRQS + NR_SIRC_IRQS + NR_GPIO_IRQS + NR_BOARD_IRQS) +#define MSM_GPIO_TO_INT(n) (FIRST_GPIO_IRQ + (n)) #define MSM_INT_TO_REG(base, irq) (base + irq / 32) #endif diff --git a/arch/arm/mach-msm/include/mach/sirc.h b/arch/arm/mach-msm/include/mach/sirc.h index 7281337ee28db..452c38daeee55 100644 --- a/arch/arm/mach-msm/include/mach/sirc.h +++ b/arch/arm/mach-msm/include/mach/sirc.h @@ -56,8 +56,6 @@ void msm_sirc_exit_sleep(void); * Secondary interrupt controller interrupts */ -#define FIRST_SIRC_IRQ (NR_MSM_IRQS + NR_GPIO_IRQS) - #define INT_UART1 (FIRST_SIRC_IRQ + 0) #define INT_UART2 (FIRST_SIRC_IRQ + 1) #define INT_UART3 (FIRST_SIRC_IRQ + 2) From bc77a5b8cbaf4a4c6b3fce10df66c86517bb528b Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 25 Mar 2010 19:40:14 -0700 Subject: [PATCH 0162/2556] [ARM] msm: add a define for first board gpio Change-Id: Idcf270a15c5803476229e6a55ed315b61e1e7134 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/include/mach/gpio.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/mach-msm/include/mach/gpio.h b/arch/arm/mach-msm/include/mach/gpio.h index 65796f24666a4..15feb02750a88 100644 --- a/arch/arm/mach-msm/include/mach/gpio.h +++ b/arch/arm/mach-msm/include/mach/gpio.h @@ -17,6 +17,9 @@ #define __ASM_ARCH_MSM_GPIO_H #include +#include + +#define FIRST_BOARD_GPIO NR_GPIO_IRQS /* GPIO TLMM (Top Level Multiplexing) Definitions */ From 6a8f11b956a252643a90d5cc3bd3cb61570759a7 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 23 Jun 2010 19:54:36 -0700 Subject: [PATCH 0163/2556] [ARM] msm: remove poorly placed pmic8058 irq defs from irqs-7x30.h Change-Id: I507b8bdbb68a13d93dfdc585765e96b8cb7abf36 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/include/mach/irqs-7x30.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/irqs-7x30.h b/arch/arm/mach-msm/include/mach/irqs-7x30.h index 3102add310ee1..f5f66b97fc91b 100644 --- a/arch/arm/mach-msm/include/mach/irqs-7x30.h +++ b/arch/arm/mach-msm/include/mach/irqs-7x30.h @@ -159,13 +159,6 @@ #define NR_MSM_IRQS 128 #define NR_SIRC_IRQS 0 #define NR_GPIO_IRQS 182 -#define PMIC8058_IRQ_BASE (NR_MSM_IRQS + NR_GPIO_IRQS) -#define NR_PMIC8058_GPIO_IRQS 40 -#define NR_PMIC8058_MPP_IRQS 12 -#define NR_PMIC8058_MISC_IRQS 8 -#define NR_PMIC8058_IRQS (NR_PMIC8058_GPIO_IRQS +\ - NR_PMIC8058_MPP_IRQS +\ - NR_PMIC8058_MISC_IRQS) -#define NR_BOARD_IRQS NR_PMIC8058_IRQS +#define NR_BOARD_IRQS 64 #endif /* __ASM_ARCH_MSM_IRQS_7X30_H */ From 18bf452b717a1cb28492fa9ec445f87d5a823e7b Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 13 May 2010 10:31:21 -0700 Subject: [PATCH 0164/2556] [ARM] msm: msm7x30: add an alias for gpu irq name Change-Id: I313a1d4bebfd04e0e02cafcb568c52d8dc22525f Signed-off-by: Dima Zavin --- arch/arm/mach-msm/include/mach/irqs-7x30.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/include/mach/irqs-7x30.h b/arch/arm/mach-msm/include/mach/irqs-7x30.h index f5f66b97fc91b..c564576cfdf33 100644 --- a/arch/arm/mach-msm/include/mach/irqs-7x30.h +++ b/arch/arm/mach-msm/include/mach/irqs-7x30.h @@ -155,6 +155,7 @@ #define INT_MDDI_CLIENT INT_MDC #define INT_NAND_WR_ER_DONE INT_EBI2_WR_ER_DONE #define INT_NAND_OP_DONE INT_EBI2_OP_DONE +#define INT_GRAPHICS INT_GRP_3D #define NR_MSM_IRQS 128 #define NR_SIRC_IRQS 0 From 3198ec87d69f8499fcec0be8ebe860f3a0416623 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Sat, 1 Dec 2007 19:10:02 -0800 Subject: [PATCH 0165/2556] [ARM] msm: shared memory tty interface Provides a simple tty interface to the shared memory "virtual serial" streams. Signed-off-by: Brian Swetland --- arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/smd_tty.c | 208 ++++++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 arch/arm/mach-msm/smd_tty.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 94195c190e13a..0ccb53b1fb4ff 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_MSM_PROC_COMM) += proc_comm.o clock-pcom.o vreg.o obj-$(CONFIG_MSM_PROC_COMM) += clock.o obj-$(CONFIG_ARCH_QSD8X50) += sirc.o obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o +obj-$(CONFIG_MSM_SMD) += smd_tty.o obj-$(CONFIG_MSM_SMD) += last_radio_log.o obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o diff --git a/arch/arm/mach-msm/smd_tty.c b/arch/arm/mach-msm/smd_tty.c new file mode 100644 index 0000000000000..f40944958d440 --- /dev/null +++ b/arch/arm/mach-msm/smd_tty.c @@ -0,0 +1,208 @@ +/* arch/arm/mach-msm/smd_tty.c + * + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland + * + * 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 + +#define MAX_SMD_TTYS 32 + +static DEFINE_MUTEX(smd_tty_lock); + +struct smd_tty_info { + smd_channel_t *ch; + struct tty_struct *tty; + int open_count; +}; + +static struct smd_tty_info smd_tty[MAX_SMD_TTYS]; + + +static void smd_tty_notify(void *priv, unsigned event) +{ + unsigned char *ptr; + int avail; + struct smd_tty_info *info = priv; + struct tty_struct *tty = info->tty; + + if (!tty) + return; + + if (event != SMD_EVENT_DATA) + return; + + for (;;) { + if (test_bit(TTY_THROTTLED, &tty->flags)) break; + avail = smd_read_avail(info->ch); + if (avail == 0) break; + + avail = tty_prepare_flip_string(tty, &ptr, avail); + + if (smd_read(info->ch, ptr, avail) != avail) { + /* shouldn't be possible since we're in interrupt + ** context here and nobody else could 'steal' our + ** characters. + */ + printk(KERN_ERR "OOPS - smd_tty_buffer mismatch?!"); + } + + tty_flip_buffer_push(tty); + } + + /* XXX only when writable and necessary */ + tty_wakeup(tty); +} + +static int smd_tty_open(struct tty_struct *tty, struct file *f) +{ + int res = 0; + int n = tty->index; + struct smd_tty_info *info; + const char *name; + + if (n == 0) { + name = "SMD_DS"; + } else if (n == 27) { + name = "SMD_GPSNMEA"; + } else { + return -ENODEV; + } + + info = smd_tty + n; + + mutex_lock(&smd_tty_lock); + tty->driver_data = info; + + if (info->open_count++ == 0) { + info->tty = tty; + if (info->ch) { + smd_kick(info->ch); + } else { + res = smd_open(name, &info->ch, info, smd_tty_notify); + } + } + mutex_unlock(&smd_tty_lock); + + return res; +} + +static void smd_tty_close(struct tty_struct *tty, struct file *f) +{ + struct smd_tty_info *info = tty->driver_data; + + if (info == 0) + return; + + mutex_lock(&smd_tty_lock); + if (--info->open_count == 0) { + info->tty = 0; + tty->driver_data = 0; + if (info->ch) { + smd_close(info->ch); + info->ch = 0; + } + } + mutex_unlock(&smd_tty_lock); +} + +static int smd_tty_write(struct tty_struct *tty, const unsigned char *buf, int len) +{ + struct smd_tty_info *info = tty->driver_data; + int avail; + + /* if we're writing to a packet channel we will + ** never be able to write more data than there + ** is currently space for + */ + avail = smd_write_avail(info->ch); + if (len > avail) + len = avail; + + return smd_write(info->ch, buf, len); +} + +static int smd_tty_write_room(struct tty_struct *tty) +{ + struct smd_tty_info *info = tty->driver_data; + return smd_write_avail(info->ch); +} + +static int smd_tty_chars_in_buffer(struct tty_struct *tty) +{ + struct smd_tty_info *info = tty->driver_data; + return smd_read_avail(info->ch); +} + +static void smd_tty_unthrottle(struct tty_struct *tty) +{ + struct smd_tty_info *info = tty->driver_data; + smd_kick(info->ch); +} + +static struct tty_operations smd_tty_ops = { + .open = smd_tty_open, + .close = smd_tty_close, + .write = smd_tty_write, + .write_room = smd_tty_write_room, + .chars_in_buffer = smd_tty_chars_in_buffer, + .unthrottle = smd_tty_unthrottle, +}; + +static struct tty_driver *smd_tty_driver; + +static int __init smd_tty_init(void) +{ + int ret; + + smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS); + if (smd_tty_driver == 0) + return -ENOMEM; + + smd_tty_driver->owner = THIS_MODULE; + smd_tty_driver->driver_name = "smd_tty_driver"; + smd_tty_driver->name = "smd"; + smd_tty_driver->major = 0; + smd_tty_driver->minor_start = 0; + smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + smd_tty_driver->subtype = SERIAL_TYPE_NORMAL; + smd_tty_driver->init_termios = tty_std_termios; + smd_tty_driver->init_termios.c_iflag = 0; + smd_tty_driver->init_termios.c_oflag = 0; + smd_tty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD; + smd_tty_driver->init_termios.c_lflag = 0; + smd_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(smd_tty_driver, &smd_tty_ops); + + ret = tty_register_driver(smd_tty_driver); + if (ret) return ret; + + /* this should be dynamic */ + tty_register_device(smd_tty_driver, 0, 0); + tty_register_device(smd_tty_driver, 27, 0); + + return 0; +} + +module_init(smd_tty_init); From 0b66df60f55dc7b3fe467da56e8fff39bcf36f60 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Tue, 9 Feb 2010 22:52:45 -0800 Subject: [PATCH 0166/2556] [ARM] msm: shared memory qmi interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QMI control stream is used to configure the virtual ethernet network interface to the baseband processor / cellular network. Publish qmi0, qmi1, and qmi2 (which control the rmnet0, rmnet1, and rmnet2 interfaces). Signed-off-by: Brian Swetland [ARM] msm: smd_qmi: Add a wakelock with a timeout to prevent sleep after receiving a qmi notification. Signed-off-by: Arve Hjønnevåg [ARM] msm: smd_qmi: Get auth_type from apn parameter to qmi_network_up function. auth_type was hardcoded as 3. Parse this from the "apn" parameter. --- arch/arm/mach-msm/Makefile | 2 +- arch/arm/mach-msm/smd_qmi.c | 860 ++++++++++++++++++++++++++++++++++++ 2 files changed, 861 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-msm/smd_qmi.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 0ccb53b1fb4ff..af5df753aec6e 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -17,7 +17,7 @@ obj-$(CONFIG_MSM_PROC_COMM) += proc_comm.o clock-pcom.o vreg.o obj-$(CONFIG_MSM_PROC_COMM) += clock.o obj-$(CONFIG_ARCH_QSD8X50) += sirc.o obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o -obj-$(CONFIG_MSM_SMD) += smd_tty.o +obj-$(CONFIG_MSM_SMD) += smd_tty.o smd_qmi.o obj-$(CONFIG_MSM_SMD) += last_radio_log.o obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o diff --git a/arch/arm/mach-msm/smd_qmi.c b/arch/arm/mach-msm/smd_qmi.c new file mode 100644 index 0000000000000..50411df6179c9 --- /dev/null +++ b/arch/arm/mach-msm/smd_qmi.c @@ -0,0 +1,860 @@ +/* arch/arm/mach-msm/smd_qmi.c + * + * QMI Control Driver -- Manages network data connections. + * + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland + * + * 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 + +#define QMI_CTL 0x00 +#define QMI_WDS 0x01 +#define QMI_DMS 0x02 +#define QMI_NAS 0x03 + +#define QMI_RESULT_SUCCESS 0x0000 +#define QMI_RESULT_FAILURE 0x0001 + +struct qmi_msg { + unsigned char service; + unsigned char client_id; + unsigned short txn_id; + unsigned short type; + unsigned short size; + unsigned char *tlv; +}; + +#define qmi_ctl_client_id 0 + +#define STATE_OFFLINE 0 +#define STATE_QUERYING 1 +#define STATE_ONLINE 2 + +struct qmi_ctxt { + struct miscdevice misc; + + struct mutex lock; + + unsigned char ctl_txn_id; + unsigned char wds_client_id; + unsigned short wds_txn_id; + + unsigned wds_busy; + unsigned wds_handle; + unsigned state_dirty; + unsigned state; + + unsigned char addr[4]; + unsigned char mask[4]; + unsigned char gateway[4]; + unsigned char dns1[4]; + unsigned char dns2[4]; + + smd_channel_t *ch; + const char *ch_name; + struct wake_lock wake_lock; + + struct work_struct open_work; + struct work_struct read_work; +}; + +static struct qmi_ctxt *qmi_minor_to_ctxt(unsigned n); + +static void qmi_read_work(struct work_struct *ws); +static void qmi_open_work(struct work_struct *work); + +void qmi_ctxt_init(struct qmi_ctxt *ctxt, unsigned n) +{ + mutex_init(&ctxt->lock); + INIT_WORK(&ctxt->read_work, qmi_read_work); + INIT_WORK(&ctxt->open_work, qmi_open_work); + wake_lock_init(&ctxt->wake_lock, WAKE_LOCK_SUSPEND, ctxt->misc.name); + ctxt->ctl_txn_id = 1; + ctxt->wds_txn_id = 1; + ctxt->wds_busy = 1; + ctxt->state = STATE_OFFLINE; + +} + +static struct workqueue_struct *qmi_wq; + +static int verbose = 0; + +/* anyone waiting for a state change waits here */ +static DECLARE_WAIT_QUEUE_HEAD(qmi_wait_queue); + + +static void qmi_dump_msg(struct qmi_msg *msg, const char *prefix) +{ + unsigned sz, n; + unsigned char *x; + + if (!verbose) + return; + + printk(KERN_INFO + "qmi: %s: svc=%02x cid=%02x tid=%04x type=%04x size=%04x\n", + prefix, msg->service, msg->client_id, + msg->txn_id, msg->type, msg->size); + + x = msg->tlv; + sz = msg->size; + + while (sz >= 3) { + sz -= 3; + + n = x[1] | (x[2] << 8); + if (n > sz) + break; + + printk(KERN_INFO "qmi: %s: tlv: %02x %04x { ", + prefix, x[0], n); + x += 3; + sz -= n; + while (n-- > 0) + printk("%02x ", *x++); + printk("}\n"); + } +} + +int qmi_add_tlv(struct qmi_msg *msg, + unsigned type, unsigned size, const void *data) +{ + unsigned char *x = msg->tlv + msg->size; + + x[0] = type; + x[1] = size; + x[2] = size >> 8; + + memcpy(x + 3, data, size); + + msg->size += (size + 3); + + return 0; +} + +/* Extract a tagged item from a qmi message buffer, +** taking care not to overrun the buffer. +*/ +static int qmi_get_tlv(struct qmi_msg *msg, + unsigned type, unsigned size, void *data) +{ + unsigned char *x = msg->tlv; + unsigned len = msg->size; + unsigned n; + + while (len >= 3) { + len -= 3; + + /* size of this item */ + n = x[1] | (x[2] << 8); + if (n > len) + break; + + if (x[0] == type) { + if (n != size) + return -1; + memcpy(data, x + 3, size); + return 0; + } + + x += (n + 3); + len -= n; + } + + return -1; +} + +static unsigned qmi_get_status(struct qmi_msg *msg, unsigned *error) +{ + unsigned short status[2]; + if (qmi_get_tlv(msg, 0x02, sizeof(status), status)) { + *error = 0; + return QMI_RESULT_FAILURE; + } else { + *error = status[1]; + return status[0]; + } +} + +/* 0x01 */ +#define QMUX_HEADER 13 + +/* should be >= HEADER + FOOTER */ +#define QMUX_OVERHEAD 16 + +static int qmi_send(struct qmi_ctxt *ctxt, struct qmi_msg *msg) +{ + unsigned char *data; + unsigned hlen; + unsigned len; + int r; + + qmi_dump_msg(msg, "send"); + + if (msg->service == QMI_CTL) { + hlen = QMUX_HEADER - 1; + } else { + hlen = QMUX_HEADER; + } + + /* QMUX length is total header + total payload - IFC selector */ + len = hlen + msg->size - 1; + if (len > 0xffff) + return -1; + + data = msg->tlv - hlen; + + /* prepend encap and qmux header */ + *data++ = 0x01; /* ifc selector */ + + /* qmux header */ + *data++ = len; + *data++ = len >> 8; + *data++ = 0x00; /* flags: client */ + *data++ = msg->service; + *data++ = msg->client_id; + + /* qmi header */ + *data++ = 0x00; /* flags: send */ + *data++ = msg->txn_id; + if (msg->service != QMI_CTL) + *data++ = msg->txn_id >> 8; + + *data++ = msg->type; + *data++ = msg->type >> 8; + *data++ = msg->size; + *data++ = msg->size >> 8; + + /* len + 1 takes the interface selector into account */ + r = smd_write(ctxt->ch, msg->tlv - hlen, len + 1); + + if (r != len) { + return -1; + } else { + return 0; + } +} + +static void qmi_process_ctl_msg(struct qmi_ctxt *ctxt, struct qmi_msg *msg) +{ + unsigned err; + if (msg->type == 0x0022) { + unsigned char n[2]; + if (qmi_get_status(msg, &err)) + return; + if (qmi_get_tlv(msg, 0x01, sizeof(n), n)) + return; + if (n[0] == QMI_WDS) { + printk(KERN_INFO + "qmi: ctl: wds use client_id 0x%02x\n", n[1]); + ctxt->wds_client_id = n[1]; + ctxt->wds_busy = 0; + } + } +} + +static int qmi_network_get_profile(struct qmi_ctxt *ctxt); + +static void swapaddr(unsigned char *src, unsigned char *dst) +{ + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; +} + +static unsigned char zero[4]; +static void qmi_read_runtime_profile(struct qmi_ctxt *ctxt, struct qmi_msg *msg) +{ + unsigned char tmp[4]; + unsigned r; + + r = qmi_get_tlv(msg, 0x1e, 4, tmp); + swapaddr(r ? zero : tmp, ctxt->addr); + r = qmi_get_tlv(msg, 0x21, 4, tmp); + swapaddr(r ? zero : tmp, ctxt->mask); + r = qmi_get_tlv(msg, 0x20, 4, tmp); + swapaddr(r ? zero : tmp, ctxt->gateway); + r = qmi_get_tlv(msg, 0x15, 4, tmp); + swapaddr(r ? zero : tmp, ctxt->dns1); + r = qmi_get_tlv(msg, 0x16, 4, tmp); + swapaddr(r ? zero : tmp, ctxt->dns2); +} + +static void qmi_process_unicast_wds_msg(struct qmi_ctxt *ctxt, + struct qmi_msg *msg) +{ + unsigned err; + switch (msg->type) { + case 0x0021: + if (qmi_get_status(msg, &err)) { + printk(KERN_ERR + "qmi: wds: network stop failed (%04x)\n", err); + } else { + printk(KERN_INFO + "qmi: wds: network stopped\n"); + ctxt->state = STATE_OFFLINE; + ctxt->state_dirty = 1; + } + break; + case 0x0020: + if (qmi_get_status(msg, &err)) { + printk(KERN_ERR + "qmi: wds: network start failed (%04x)\n", err); + } else if (qmi_get_tlv(msg, 0x01, sizeof(ctxt->wds_handle), &ctxt->wds_handle)) { + printk(KERN_INFO + "qmi: wds no handle?\n"); + } else { + printk(KERN_INFO + "qmi: wds: got handle 0x%08x\n", + ctxt->wds_handle); + } + break; + case 0x002D: + printk("qmi: got network profile\n"); + if (ctxt->state == STATE_QUERYING) { + qmi_read_runtime_profile(ctxt, msg); + ctxt->state = STATE_ONLINE; + ctxt->state_dirty = 1; + } + break; + default: + printk(KERN_ERR "qmi: unknown msg type 0x%04x\n", msg->type); + } + ctxt->wds_busy = 0; +} + +static void qmi_process_broadcast_wds_msg(struct qmi_ctxt *ctxt, + struct qmi_msg *msg) +{ + if (msg->type == 0x0022) { + unsigned char n[2]; + if (qmi_get_tlv(msg, 0x01, sizeof(n), n)) + return; + switch (n[0]) { + case 1: + printk(KERN_INFO "qmi: wds: DISCONNECTED\n"); + ctxt->state = STATE_OFFLINE; + ctxt->state_dirty = 1; + break; + case 2: + printk(KERN_INFO "qmi: wds: CONNECTED\n"); + ctxt->state = STATE_QUERYING; + ctxt->state_dirty = 1; + qmi_network_get_profile(ctxt); + break; + case 3: + printk(KERN_INFO "qmi: wds: SUSPENDED\n"); + ctxt->state = STATE_OFFLINE; + ctxt->state_dirty = 1; + } + } else { + printk(KERN_ERR "qmi: unknown bcast msg type 0x%04x\n", msg->type); + } +} + +static void qmi_process_wds_msg(struct qmi_ctxt *ctxt, + struct qmi_msg *msg) +{ + printk("wds: %04x @ %02x\n", msg->type, msg->client_id); + if (msg->client_id == ctxt->wds_client_id) { + qmi_process_unicast_wds_msg(ctxt, msg); + } else if (msg->client_id == 0xff) { + qmi_process_broadcast_wds_msg(ctxt, msg); + } else { + printk(KERN_ERR + "qmi_process_wds_msg client id 0x%02x unknown\n", + msg->client_id); + } +} + +static void qmi_process_qmux(struct qmi_ctxt *ctxt, + unsigned char *buf, unsigned sz) +{ + struct qmi_msg msg; + + /* require a full header */ + if (sz < 5) + return; + + /* require a size that matches the buffer size */ + if (sz != (buf[0] | (buf[1] << 8))) + return; + + /* only messages from a service (bit7=1) are allowed */ + if (buf[2] != 0x80) + return; + + msg.service = buf[3]; + msg.client_id = buf[4]; + + /* annoyingly, CTL messages have a shorter TID */ + if (buf[3] == 0) { + if (sz < 7) + return; + msg.txn_id = buf[6]; + buf += 7; + sz -= 7; + } else { + if (sz < 8) + return; + msg.txn_id = buf[6] | (buf[7] << 8); + buf += 8; + sz -= 8; + } + + /* no type and size!? */ + if (sz < 4) + return; + sz -= 4; + + msg.type = buf[0] | (buf[1] << 8); + msg.size = buf[2] | (buf[3] << 8); + msg.tlv = buf + 4; + + if (sz != msg.size) + return; + + qmi_dump_msg(&msg, "recv"); + + mutex_lock(&ctxt->lock); + switch (msg.service) { + case QMI_CTL: + qmi_process_ctl_msg(ctxt, &msg); + break; + case QMI_WDS: + qmi_process_wds_msg(ctxt, &msg); + break; + default: + printk(KERN_ERR "qmi: msg from unknown svc 0x%02x\n", + msg.service); + break; + } + mutex_unlock(&ctxt->lock); + + wake_up(&qmi_wait_queue); +} + +#define QMI_MAX_PACKET (256 + QMUX_OVERHEAD) + +static void qmi_read_work(struct work_struct *ws) +{ + struct qmi_ctxt *ctxt = container_of(ws, struct qmi_ctxt, read_work); + struct smd_channel *ch = ctxt->ch; + unsigned char buf[QMI_MAX_PACKET]; + int sz; + + for (;;) { + sz = smd_cur_packet_size(ch); + if (sz == 0) + break; + if (sz < smd_read_avail(ch)) + break; + if (sz > QMI_MAX_PACKET) { + smd_read(ch, 0, sz); + continue; + } + if (smd_read(ch, buf, sz) != sz) { + printk(KERN_ERR "qmi: not enough data?!\n"); + continue; + } + + /* interface selector must be 1 */ + if (buf[0] != 0x01) + continue; + + qmi_process_qmux(ctxt, buf + 1, sz - 1); + } +} + +static int qmi_request_wds_cid(struct qmi_ctxt *ctxt); + +static void qmi_open_work(struct work_struct *ws) +{ + struct qmi_ctxt *ctxt = container_of(ws, struct qmi_ctxt, open_work); + mutex_lock(&ctxt->lock); + qmi_request_wds_cid(ctxt); + mutex_unlock(&ctxt->lock); +} + +static void qmi_notify(void *priv, unsigned event) +{ + struct qmi_ctxt *ctxt = priv; + + switch (event) { + case SMD_EVENT_DATA: { + int sz; + sz = smd_cur_packet_size(ctxt->ch); + if ((sz > 0) && (sz <= smd_read_avail(ctxt->ch))) { + wake_lock_timeout(&ctxt->wake_lock, HZ / 2); + queue_work(qmi_wq, &ctxt->read_work); + } + break; + } + case SMD_EVENT_OPEN: + printk(KERN_INFO "qmi: smd opened\n"); + queue_work(qmi_wq, &ctxt->open_work); + break; + case SMD_EVENT_CLOSE: + printk(KERN_INFO "qmi: smd closed\n"); + break; + } +} + +static int qmi_request_wds_cid(struct qmi_ctxt *ctxt) +{ + unsigned char data[64 + QMUX_OVERHEAD]; + struct qmi_msg msg; + unsigned char n; + + msg.service = QMI_CTL; + msg.client_id = qmi_ctl_client_id; + msg.txn_id = ctxt->ctl_txn_id; + msg.type = 0x0022; + msg.size = 0; + msg.tlv = data + QMUX_HEADER; + + ctxt->ctl_txn_id += 2; + + n = QMI_WDS; + qmi_add_tlv(&msg, 0x01, 0x01, &n); + + return qmi_send(ctxt, &msg); +} + +static int qmi_network_get_profile(struct qmi_ctxt *ctxt) +{ + unsigned char data[96 + QMUX_OVERHEAD]; + struct qmi_msg msg; + + msg.service = QMI_WDS; + msg.client_id = ctxt->wds_client_id; + msg.txn_id = ctxt->wds_txn_id; + msg.type = 0x002D; + msg.size = 0; + msg.tlv = data + QMUX_HEADER; + + ctxt->wds_txn_id += 2; + + return qmi_send(ctxt, &msg); +} + +static int qmi_network_up(struct qmi_ctxt *ctxt, char *apn) +{ + unsigned char data[96 + QMUX_OVERHEAD]; + struct qmi_msg msg; + char *auth_type; + char *user; + char *pass; + + for (user = apn; *user; user++) { + if (*user == ' ') { + *user++ = 0; + break; + } + } + for (pass = user; *pass; pass++) { + if (*pass == ' ') { + *pass++ = 0; + break; + } + } + + for (auth_type = pass; *auth_type; auth_type++) { + if (*auth_type == ' ') { + *auth_type++ = 0; + break; + } + } + + msg.service = QMI_WDS; + msg.client_id = ctxt->wds_client_id; + msg.txn_id = ctxt->wds_txn_id; + msg.type = 0x0020; + msg.size = 0; + msg.tlv = data + QMUX_HEADER; + + ctxt->wds_txn_id += 2; + + qmi_add_tlv(&msg, 0x14, strlen(apn), apn); + if (*auth_type) + qmi_add_tlv(&msg, 0x16, strlen(auth_type), auth_type); + if (*user) { + if (!*auth_type) { + unsigned char x; + x = 3; + qmi_add_tlv(&msg, 0x16, 1, &x); + } + qmi_add_tlv(&msg, 0x17, strlen(user), user); + if (*pass) + qmi_add_tlv(&msg, 0x18, strlen(pass), pass); + } + return qmi_send(ctxt, &msg); +} + +static int qmi_network_down(struct qmi_ctxt *ctxt) +{ + unsigned char data[16 + QMUX_OVERHEAD]; + struct qmi_msg msg; + + msg.service = QMI_WDS; + msg.client_id = ctxt->wds_client_id; + msg.txn_id = ctxt->wds_txn_id; + msg.type = 0x0021; + msg.size = 0; + msg.tlv = data + QMUX_HEADER; + + ctxt->wds_txn_id += 2; + + qmi_add_tlv(&msg, 0x01, sizeof(ctxt->wds_handle), &ctxt->wds_handle); + + return qmi_send(ctxt, &msg); +} + +static int qmi_print_state(struct qmi_ctxt *ctxt, char *buf, int max) +{ + int i; + char *statename; + + if (ctxt->state == STATE_ONLINE) { + statename = "up"; + } else if (ctxt->state == STATE_OFFLINE) { + statename = "down"; + } else { + statename = "busy"; + } + + i = scnprintf(buf, max, "STATE=%s\n", statename); + i += scnprintf(buf + i, max - i, "CID=%d\n",ctxt->wds_client_id); + + if (ctxt->state != STATE_ONLINE){ + return i; + } + + i += scnprintf(buf + i, max - i, "ADDR=%d.%d.%d.%d\n", + ctxt->addr[0], ctxt->addr[1], ctxt->addr[2], ctxt->addr[3]); + i += scnprintf(buf + i, max - i, "MASK=%d.%d.%d.%d\n", + ctxt->mask[0], ctxt->mask[1], ctxt->mask[2], ctxt->mask[3]); + i += scnprintf(buf + i, max - i, "GATEWAY=%d.%d.%d.%d\n", + ctxt->gateway[0], ctxt->gateway[1], ctxt->gateway[2], + ctxt->gateway[3]); + i += scnprintf(buf + i, max - i, "DNS1=%d.%d.%d.%d\n", + ctxt->dns1[0], ctxt->dns1[1], ctxt->dns1[2], ctxt->dns1[3]); + i += scnprintf(buf + i, max - i, "DNS2=%d.%d.%d.%d\n", + ctxt->dns2[0], ctxt->dns2[1], ctxt->dns2[2], ctxt->dns2[3]); + + return i; +} + +static ssize_t qmi_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct qmi_ctxt *ctxt = fp->private_data; + char msg[256]; + int len; + int r; + + mutex_lock(&ctxt->lock); + for (;;) { + if (ctxt->state_dirty) { + ctxt->state_dirty = 0; + len = qmi_print_state(ctxt, msg, 256); + break; + } + mutex_unlock(&ctxt->lock); + r = wait_event_interruptible(qmi_wait_queue, ctxt->state_dirty); + if (r < 0) + return r; + mutex_lock(&ctxt->lock); + } + mutex_unlock(&ctxt->lock); + + if (len > count) + len = count; + + if (copy_to_user(buf, msg, len)) + return -EFAULT; + + return len; +} + + +static ssize_t qmi_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct qmi_ctxt *ctxt = fp->private_data; + unsigned char cmd[64]; + int len; + int r; + + if (count < 1) + return 0; + + len = count > 63 ? 63 : count; + + if (copy_from_user(cmd, buf, len)) + return -EFAULT; + + cmd[len] = 0; + + /* lazy */ + if (cmd[len-1] == '\n') { + cmd[len-1] = 0; + len--; + } + + if (!strncmp(cmd, "verbose", 7)) { + verbose = 1; + } else if (!strncmp(cmd, "terse", 5)) { + verbose = 0; + } else if (!strncmp(cmd, "poll", 4)) { + ctxt->state_dirty = 1; + wake_up(&qmi_wait_queue); + } else if (!strncmp(cmd, "down", 4)) { +retry_down: + mutex_lock(&ctxt->lock); + if (ctxt->wds_busy) { + mutex_unlock(&ctxt->lock); + r = wait_event_interruptible(qmi_wait_queue, !ctxt->wds_busy); + if (r < 0) + return r; + goto retry_down; + } + ctxt->wds_busy = 1; + qmi_network_down(ctxt); + mutex_unlock(&ctxt->lock); + } else if (!strncmp(cmd, "up:", 3)) { +retry_up: + mutex_lock(&ctxt->lock); + if (ctxt->wds_busy) { + mutex_unlock(&ctxt->lock); + r = wait_event_interruptible(qmi_wait_queue, !ctxt->wds_busy); + if (r < 0) + return r; + goto retry_up; + } + ctxt->wds_busy = 1; + qmi_network_up(ctxt, cmd+3); + mutex_unlock(&ctxt->lock); + } else { + return -EINVAL; + } + + return count; +} + +static int qmi_open(struct inode *ip, struct file *fp) +{ + struct qmi_ctxt *ctxt = qmi_minor_to_ctxt(MINOR(ip->i_rdev)); + int r = 0; + + if (!ctxt) { + printk(KERN_ERR "unknown qmi misc %d\n", MINOR(ip->i_rdev)); + return -ENODEV; + } + + fp->private_data = ctxt; + + mutex_lock(&ctxt->lock); + if (ctxt->ch == 0) + r = smd_open(ctxt->ch_name, &ctxt->ch, ctxt, qmi_notify); + if (r == 0) + wake_up(&qmi_wait_queue); + mutex_unlock(&ctxt->lock); + + return r; +} + +static int qmi_release(struct inode *ip, struct file *fp) +{ + return 0; +} + +static struct file_operations qmi_fops = { + .owner = THIS_MODULE, + .read = qmi_read, + .write = qmi_write, + .open = qmi_open, + .release = qmi_release, +}; + +static struct qmi_ctxt qmi_device0 = { + .ch_name = "SMD_DATA5_CNTL", + .misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "qmi0", + .fops = &qmi_fops, + } +}; +static struct qmi_ctxt qmi_device1 = { + .ch_name = "SMD_DATA6_CNTL", + .misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "qmi1", + .fops = &qmi_fops, + } +}; +static struct qmi_ctxt qmi_device2 = { + .ch_name = "SMD_DATA7_CNTL", + .misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "qmi2", + .fops = &qmi_fops, + } +}; + +static struct qmi_ctxt *qmi_minor_to_ctxt(unsigned n) +{ + if (n == qmi_device0.misc.minor) + return &qmi_device0; + if (n == qmi_device1.misc.minor) + return &qmi_device1; + if (n == qmi_device2.misc.minor) + return &qmi_device2; + return 0; +} + +static int __init qmi_init(void) +{ + int ret; + + qmi_wq = create_singlethread_workqueue("qmi"); + if (qmi_wq == 0) + return -ENOMEM; + + qmi_ctxt_init(&qmi_device0, 0); + qmi_ctxt_init(&qmi_device1, 1); + qmi_ctxt_init(&qmi_device2, 2); + + ret = misc_register(&qmi_device0.misc); + if (ret == 0) + ret = misc_register(&qmi_device1.misc); + if (ret == 0) + ret = misc_register(&qmi_device2.misc); + return ret; +} + +module_init(qmi_init); From f03bf256e5c6bd55e9657276925e7cddcc87bdb5 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Sat, 1 Dec 2007 19:14:34 -0800 Subject: [PATCH 0167/2556] [ARM] msm: shared memory rpc router The RPC router provides access to RPC services running on the baseband core from kernel and userspace. Change-Id: I2cb543bccfeac602090f7a272b6902018a3da593 Signed-off-by: Brian Swetland Signed-off-by: San Mehat Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/Kconfig | 9 + arch/arm/mach-msm/Makefile | 4 + .../arm/mach-msm/include/mach/msm_rpcrouter.h | 145 ++ arch/arm/mach-msm/smd_rpcrouter.c | 1166 +++++++++++++++++ arch/arm/mach-msm/smd_rpcrouter.h | 190 +++ arch/arm/mach-msm/smd_rpcrouter_device.c | 340 +++++ arch/arm/mach-msm/smd_rpcrouter_servers.c | 213 +++ include/linux/msm_rpcrouter.h | 44 + 8 files changed, 2111 insertions(+) create mode 100644 arch/arm/mach-msm/include/mach/msm_rpcrouter.h create mode 100644 arch/arm/mach-msm/smd_rpcrouter.c create mode 100644 arch/arm/mach-msm/smd_rpcrouter.h create mode 100644 arch/arm/mach-msm/smd_rpcrouter_device.c create mode 100644 arch/arm/mach-msm/smd_rpcrouter_servers.c create mode 100644 include/linux/msm_rpcrouter.h diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 5d3d9ade12fba..f46be881562a8 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -177,4 +177,13 @@ config IOMMU_API config MSM_SCM bool + +config MSM_ONCRPCROUTER + depends on MSM_SMD + default y + bool "MSM ONCRPC router support" + help + Support for the MSM ONCRPC router for communication between + the ARM9 and ARM11 + endif diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index af5df753aec6e..a1eaa0634602d 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -24,6 +24,10 @@ obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_SMP) += headsmp.o platsmp.o +obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter.o +obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_device.o +obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o + obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o diff --git a/arch/arm/mach-msm/include/mach/msm_rpcrouter.h b/arch/arm/mach-msm/include/mach/msm_rpcrouter.h new file mode 100644 index 0000000000000..8f53d2d7e23df --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_rpcrouter.h @@ -0,0 +1,145 @@ +/** include/asm-arm/arch-msm/msm_rpcrouter.h + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007 QUALCOMM Incorporated + * Author: San Mehat + * + * 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 __ASM__ARCH_MSM_RPCROUTER_H +#define __ASM__ARCH_MSM_RPCROUTER_H + +#include +#include +#include + +struct msm_rpc_endpoint; + +struct rpcsvr_platform_device +{ + struct platform_device base; + uint32_t prog; + uint32_t vers; +}; + +#define RPC_DATA_IN 0 +/* + * Structures for sending / receiving direct RPC requests + * XXX: Any cred/verif lengths > 0 not supported + */ + +struct rpc_request_hdr +{ + uint32_t xid; + uint32_t type; /* 0 */ + uint32_t rpc_vers; /* 2 */ + uint32_t prog; + uint32_t vers; + uint32_t procedure; + uint32_t cred_flavor; + uint32_t cred_length; + uint32_t verf_flavor; + uint32_t verf_length; +}; + +typedef struct +{ + uint32_t low; + uint32_t high; +} rpc_reply_progmismatch_data; + +typedef struct +{ +} rpc_denied_reply_hdr; + +typedef struct +{ + uint32_t verf_flavor; + uint32_t verf_length; + uint32_t accept_stat; +#define RPC_ACCEPTSTAT_SUCCESS 0 +#define RPC_ACCEPTSTAT_PROG_UNAVAIL 1 +#define RPC_ACCEPTSTAT_PROG_MISMATCH 2 +#define RPC_ACCEPTSTAT_PROC_UNAVAIL 3 +#define RPC_ACCEPTSTAT_GARBAGE_ARGS 4 +#define RPC_ACCEPTSTAT_SYSTEM_ERR 5 +#define RPC_ACCEPTSTAT_PROG_LOCKED 6 + /* + * Following data is dependant on accept_stat + * If ACCEPTSTAT == PROG_MISMATCH then there is a + * 'rpc_reply_progmismatch_data' structure following the header. + * Otherwise the data is procedure specific + */ +} rpc_accepted_reply_hdr; + +struct rpc_reply_hdr +{ + uint32_t xid; + uint32_t type; + uint32_t reply_stat; +#define RPCMSG_REPLYSTAT_ACCEPTED 0 +#define RPCMSG_REPLYSTAT_DENIED 1 + union { + rpc_accepted_reply_hdr acc_hdr; + rpc_denied_reply_hdr dny_hdr; + } data; +}; + +/* flags for msm_rpc_connect() */ +#define MSM_RPC_UNINTERRUPTIBLE 0x0001 + +/* use IS_ERR() to check for failure */ +struct msm_rpc_endpoint *msm_rpc_open(void); +struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned flags); + +int msm_rpc_close(struct msm_rpc_endpoint *ept); +int msm_rpc_write(struct msm_rpc_endpoint *ept, + void *data, int len); +int msm_rpc_read(struct msm_rpc_endpoint *ept, + void **data, unsigned len, long timeout); +void msm_rpc_setup_req(struct rpc_request_hdr *hdr, + uint32_t prog, uint32_t vers, uint32_t proc); +int msm_rpc_register_server(struct msm_rpc_endpoint *ept, + uint32_t prog, uint32_t vers); +int msm_rpc_unregister_server(struct msm_rpc_endpoint *ept, + uint32_t prog, uint32_t vers); + +/* simple blocking rpc call + * + * request is mandatory and must have a rpc_request_hdr + * at the start. The header will be filled out for you. + * + * reply provides a buffer for replies of reply_max_size + */ +int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc, + void *request, int request_size, + void *reply, int reply_max_size, + long timeout); +int msm_rpc_call(struct msm_rpc_endpoint *ept, uint32_t proc, + void *request, int request_size, + long timeout); + +struct msm_rpc_server +{ + struct list_head list; + uint32_t flags; + + uint32_t prog; + uint32_t vers; + + int (*rpc_call)(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len); +}; + +int msm_rpc_create_server(struct msm_rpc_server *server); + +#endif diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c new file mode 100644 index 0000000000000..5bd9e98b27d1d --- /dev/null +++ b/arch/arm/mach-msm/smd_rpcrouter.c @@ -0,0 +1,1166 @@ +/* arch/arm/mach-msm/smd_rpcrouter.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007 QUALCOMM Incorporated + * Author: San Mehat + * + * 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. + * + */ + +/* TODO: fragmentation for large writes */ +/* TODO: handle cases where smd_write() will tempfail due to full fifo */ +/* TODO: thread priority? schedule a work to bump it? */ +/* TODO: maybe make server_list_lock a mutex */ +/* TODO: pool fragments to avoid kmalloc/kfree churn */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "smd_rpcrouter.h" + +#define TRACE_R2R_MSG 0 +#define TRACE_R2R_RAW 0 +#define TRACE_RPC_MSG 0 + +#define MSM_RPCROUTER_DEBUG 0 +#define MSM_RPCROUTER_DEBUG_PKT 0 +#define MSM_RPCROUTER_R2R_DEBUG 0 +#define DUMP_ALL_RECEIVED_HEADERS 0 + +#define DIAG(x...) printk("[RR] ERROR " x) + +#if MSM_RPCROUTER_DEBUG +#define D(x...) printk(x) +#else +#define D(x...) do {} while (0) +#endif + +#if TRACE_R2R_MSG +#define RR(x...) printk("[RR] "x) +#else +#define RR(x...) do {} while (0) +#endif + +#if TRACE_RPC_MSG +#define IO(x...) printk("[RPC] "x) +#else +#define IO(x...) do {} while (0) +#endif + +static LIST_HEAD(local_endpoints); +static LIST_HEAD(remote_endpoints); + +static LIST_HEAD(server_list); + +static smd_channel_t *smd_channel; +static int initialized; +static wait_queue_head_t newserver_wait; +static wait_queue_head_t smd_wait; + +static DEFINE_SPINLOCK(local_endpoints_lock); +static DEFINE_SPINLOCK(remote_endpoints_lock); +static DEFINE_SPINLOCK(server_list_lock); +static DEFINE_SPINLOCK(smd_lock); + +static struct workqueue_struct *rpcrouter_workqueue; + +static atomic_t next_xid = ATOMIC_INIT(1); +static uint8_t next_pacmarkid; + +static void do_read_data(struct work_struct *work); +static void do_create_pdevs(struct work_struct *work); +static void do_create_rpcrouter_pdev(struct work_struct *work); + +static DECLARE_WORK(work_read_data, do_read_data); +static DECLARE_WORK(work_create_pdevs, do_create_pdevs); +static DECLARE_WORK(work_create_rpcrouter_pdev, do_create_rpcrouter_pdev); + +#define RR_STATE_IDLE 0 +#define RR_STATE_HEADER 1 +#define RR_STATE_BODY 2 +#define RR_STATE_ERROR 3 + +struct rr_context { + struct rr_packet *pkt; + uint8_t *ptr; + uint32_t state; /* current assembly state */ + uint32_t count; /* bytes needed in this state */ +}; + +struct rr_context the_rr_context; + +static struct platform_device rpcrouter_pdev = { + .name = "oncrpc_router", + .id = -1, +}; + + +static int rpcrouter_send_control_msg(union rr_control_msg *msg) +{ + struct rr_header hdr; + unsigned long flags; + int need; + + if (!(msg->cmd == RPCROUTER_CTRL_CMD_HELLO) && !initialized) { + printk(KERN_ERR "rpcrouter_send_control_msg(): Warning, " + "router not initialized\n"); + return -EINVAL; + } + + hdr.version = RPCROUTER_VERSION; + hdr.type = msg->cmd; + hdr.src_pid = RPCROUTER_PID_LOCAL; + hdr.src_cid = RPCROUTER_ROUTER_ADDRESS; + hdr.confirm_rx = 0; + hdr.size = sizeof(*msg); + hdr.dst_pid = 0; + hdr.dst_cid = RPCROUTER_ROUTER_ADDRESS; + + /* TODO: what if channel is full? */ + + need = sizeof(hdr) + hdr.size; + spin_lock_irqsave(&smd_lock, flags); + while (smd_write_avail(smd_channel) < need) { + spin_unlock_irqrestore(&smd_lock, flags); + msleep(250); + spin_lock_irqsave(&smd_lock, flags); + } + smd_write(smd_channel, &hdr, sizeof(hdr)); + smd_write(smd_channel, msg, hdr.size); + spin_unlock_irqrestore(&smd_lock, flags); + return 0; +} + +static struct rr_server *rpcrouter_create_server(uint32_t pid, + uint32_t cid, + uint32_t prog, + uint32_t ver) +{ + struct rr_server *server; + unsigned long flags; + int rc; + + server = kmalloc(sizeof(struct rr_server), GFP_KERNEL); + if (!server) + return ERR_PTR(-ENOMEM); + + memset(server, 0, sizeof(struct rr_server)); + server->pid = pid; + server->cid = cid; + server->prog = prog; + server->vers = ver; + + spin_lock_irqsave(&server_list_lock, flags); + list_add_tail(&server->list, &server_list); + spin_unlock_irqrestore(&server_list_lock, flags); + + if (pid == RPCROUTER_PID_REMOTE) { + rc = msm_rpcrouter_create_server_cdev(server); + if (rc < 0) + goto out_fail; + } + return server; +out_fail: + spin_lock_irqsave(&server_list_lock, flags); + list_del(&server->list); + spin_unlock_irqrestore(&server_list_lock, flags); + kfree(server); + return ERR_PTR(rc); +} + +static void rpcrouter_destroy_server(struct rr_server *server) +{ + unsigned long flags; + + spin_lock_irqsave(&server_list_lock, flags); + list_del(&server->list); + spin_unlock_irqrestore(&server_list_lock, flags); + device_destroy(msm_rpcrouter_class, server->device_number); + kfree(server); +} + +static struct rr_server *rpcrouter_lookup_server(uint32_t prog, + uint32_t ver) +{ + struct rr_server *server; + unsigned long flags; + + spin_lock_irqsave(&server_list_lock, flags); + list_for_each_entry(server, &server_list, list) { + if (server->prog == prog + && server->vers == ver) { + spin_unlock_irqrestore(&server_list_lock, flags); + return server; + } + } + spin_unlock_irqrestore(&server_list_lock, flags); + return NULL; +} + +static struct rr_server *rpcrouter_lookup_server_by_dev(dev_t dev) +{ + struct rr_server *server; + unsigned long flags; + + spin_lock_irqsave(&server_list_lock, flags); + list_for_each_entry(server, &server_list, list) { + if (server->device_number == dev) { + spin_unlock_irqrestore(&server_list_lock, flags); + return server; + } + } + spin_unlock_irqrestore(&server_list_lock, flags); + return NULL; +} + + +struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev) +{ + struct msm_rpc_endpoint *ept; + unsigned long flags; + + ept = kmalloc(sizeof(struct msm_rpc_endpoint), GFP_KERNEL); + if (!ept) + return NULL; + memset(ept, 0, sizeof(struct msm_rpc_endpoint)); + + /* mark no reply outstanding */ + ept->reply_pid = 0xffffffff; + + ept->cid = (uint32_t) ept; + ept->pid = RPCROUTER_PID_LOCAL; + ept->dev = dev; + + if ((dev != msm_rpcrouter_devno) && (dev != MKDEV(0, 0))) { + struct rr_server *srv; + /* + * This is a userspace client which opened + * a program/ver devicenode. Bind the client + * to that destination + */ + srv = rpcrouter_lookup_server_by_dev(dev); + /* TODO: bug? really? */ + BUG_ON(!srv); + + ept->dst_pid = srv->pid; + ept->dst_cid = srv->cid; + ept->dst_prog = cpu_to_be32(srv->prog); + ept->dst_vers = cpu_to_be32(srv->vers); + } else { + /* mark not connected */ + ept->dst_pid = 0xffffffff; + } + + init_waitqueue_head(&ept->wait_q); + INIT_LIST_HEAD(&ept->read_q); + spin_lock_init(&ept->read_q_lock); + INIT_LIST_HEAD(&ept->incomplete); + + spin_lock_irqsave(&local_endpoints_lock, flags); + list_add_tail(&ept->list, &local_endpoints); + spin_unlock_irqrestore(&local_endpoints_lock, flags); + return ept; +} + +int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept) +{ + int rc; + union rr_control_msg msg; + + msg.cmd = RPCROUTER_CTRL_CMD_REMOVE_CLIENT; + msg.cli.pid = ept->pid; + msg.cli.cid = ept->cid; + + RR("x REMOVE_CLIENT id=%d:%08x\n", ept->pid, ept->cid); + rc = rpcrouter_send_control_msg(&msg); + if (rc < 0) + return rc; + + list_del(&ept->list); + kfree(ept); + return 0; +} + +static int rpcrouter_create_remote_endpoint(uint32_t cid) +{ + struct rr_remote_endpoint *new_c; + unsigned long flags; + + new_c = kmalloc(sizeof(struct rr_remote_endpoint), GFP_KERNEL); + if (!new_c) + return -ENOMEM; + memset(new_c, 0, sizeof(struct rr_remote_endpoint)); + + new_c->cid = cid; + new_c->pid = RPCROUTER_PID_REMOTE; + init_waitqueue_head(&new_c->quota_wait); + spin_lock_init(&new_c->quota_lock); + + spin_lock_irqsave(&remote_endpoints_lock, flags); + list_add_tail(&new_c->list, &remote_endpoints); + spin_unlock_irqrestore(&remote_endpoints_lock, flags); + return 0; +} + +static struct msm_rpc_endpoint *rpcrouter_lookup_local_endpoint(uint32_t cid) +{ + struct msm_rpc_endpoint *ept; + unsigned long flags; + + spin_lock_irqsave(&local_endpoints_lock, flags); + list_for_each_entry(ept, &local_endpoints, list) { + if (ept->cid == cid) { + spin_unlock_irqrestore(&local_endpoints_lock, flags); + return ept; + } + } + spin_unlock_irqrestore(&local_endpoints_lock, flags); + return NULL; +} + +static struct rr_remote_endpoint *rpcrouter_lookup_remote_endpoint(uint32_t cid) +{ + struct rr_remote_endpoint *ept; + unsigned long flags; + + spin_lock_irqsave(&remote_endpoints_lock, flags); + list_for_each_entry(ept, &remote_endpoints, list) { + if (ept->cid == cid) { + spin_unlock_irqrestore(&remote_endpoints_lock, flags); + return ept; + } + } + spin_unlock_irqrestore(&remote_endpoints_lock, flags); + return NULL; +} + +static int process_control_msg(union rr_control_msg *msg, int len) +{ + union rr_control_msg ctl; + struct rr_server *server; + struct rr_remote_endpoint *r_ept; + int rc = 0; + unsigned long flags; + + if (len != sizeof(*msg)) { + printk(KERN_ERR "rpcrouter: r2r msg size %d != %d\n", + len, sizeof(*msg)); + return -EINVAL; + } + + switch (msg->cmd) { + case RPCROUTER_CTRL_CMD_HELLO: + RR("o HELLO\n"); + + RR("x HELLO\n"); + memset(&ctl, 0, sizeof(ctl)); + ctl.cmd = RPCROUTER_CTRL_CMD_HELLO; + rpcrouter_send_control_msg(&ctl); + + initialized = 1; + + /* Send list of servers one at a time */ + ctl.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER; + + /* TODO: long time to hold a spinlock... */ + spin_lock_irqsave(&server_list_lock, flags); + list_for_each_entry(server, &server_list, list) { + ctl.srv.pid = server->pid; + ctl.srv.cid = server->cid; + ctl.srv.prog = server->prog; + ctl.srv.vers = server->vers; + + RR("x NEW_SERVER id=%d:%08x prog=%08x:%d\n", + server->pid, server->cid, + server->prog, server->vers); + + rpcrouter_send_control_msg(&ctl); + } + spin_unlock_irqrestore(&server_list_lock, flags); + + queue_work(rpcrouter_workqueue, &work_create_rpcrouter_pdev); + break; + + case RPCROUTER_CTRL_CMD_RESUME_TX: + RR("o RESUME_TX id=%d:%08x\n", msg->cli.pid, msg->cli.cid); + + r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.cid); + if (!r_ept) { + printk(KERN_ERR + "rpcrouter: Unable to resume client\n"); + break; + } + spin_lock_irqsave(&r_ept->quota_lock, flags); + r_ept->tx_quota_cntr = 0; + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + wake_up(&r_ept->quota_wait); + break; + + case RPCROUTER_CTRL_CMD_NEW_SERVER: + RR("o NEW_SERVER id=%d:%08x prog=%08x:%d\n", + msg->srv.pid, msg->srv.cid, msg->srv.prog, msg->srv.vers); + + server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers); + + if (!server) { + server = rpcrouter_create_server( + msg->srv.pid, msg->srv.cid, + msg->srv.prog, msg->srv.vers); + if (!server) + return -ENOMEM; + /* + * XXX: Verify that its okay to add the + * client to our remote client list + * if we get a NEW_SERVER notification + */ + if (!rpcrouter_lookup_remote_endpoint(msg->srv.cid)) { + rc = rpcrouter_create_remote_endpoint( + msg->srv.cid); + if (rc < 0) + printk(KERN_ERR + "rpcrouter:Client create" + "error (%d)\n", rc); + } + schedule_work(&work_create_pdevs); + wake_up(&newserver_wait); + } else { + if ((server->pid == msg->srv.pid) && + (server->cid == msg->srv.cid)) { + printk(KERN_ERR "rpcrouter: Duplicate svr\n"); + } else { + server->pid = msg->srv.pid; + server->cid = msg->srv.cid; + } + } + break; + + case RPCROUTER_CTRL_CMD_REMOVE_SERVER: + RR("o REMOVE_SERVER prog=%08x:%d\n", + msg->srv.prog, msg->srv.vers); + server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers); + if (server) + rpcrouter_destroy_server(server); + break; + + case RPCROUTER_CTRL_CMD_REMOVE_CLIENT: + RR("o REMOVE_CLIENT id=%d:%08x\n", msg->cli.pid, msg->cli.cid); + if (msg->cli.pid != RPCROUTER_PID_REMOTE) { + printk(KERN_ERR + "rpcrouter: Denying remote removal of " + "local client\n"); + break; + } + r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.cid); + if (r_ept) { + spin_lock_irqsave(&remote_endpoints_lock, flags); + list_del(&r_ept->list); + spin_unlock_irqrestore(&remote_endpoints_lock, flags); + kfree(r_ept); + } + + /* Notify local clients of this event */ + printk(KERN_ERR "rpcrouter: LOCAL NOTIFICATION NOT IMP\n"); + rc = -ENOSYS; + + break; + default: + RR("o UNKNOWN(%08x)\n", msg->cmd); + rc = -ENOSYS; + } + + return rc; +} + +static void do_create_rpcrouter_pdev(struct work_struct *work) +{ + platform_device_register(&rpcrouter_pdev); +} + +static void do_create_pdevs(struct work_struct *work) +{ + unsigned long flags; + struct rr_server *server; + + /* TODO: race if destroyed while being registered */ + spin_lock_irqsave(&server_list_lock, flags); + list_for_each_entry(server, &server_list, list) { + if (server->pid == RPCROUTER_PID_REMOTE) { + if (server->pdev_name[0] == 0) { + spin_unlock_irqrestore(&server_list_lock, + flags); + msm_rpcrouter_create_server_pdev(server); + schedule_work(&work_create_pdevs); + return; + } + } + } + spin_unlock_irqrestore(&server_list_lock, flags); +} + +static void rpcrouter_smdnotify(void *_dev, unsigned event) +{ + if (event != SMD_EVENT_DATA) + return; + + wake_up(&smd_wait); +} + +static void *rr_malloc(unsigned sz) +{ + void *ptr = kmalloc(sz, GFP_KERNEL); + if (ptr) + return ptr; + + printk(KERN_ERR "rpcrouter: kmalloc of %d failed, retrying...\n", sz); + do { + ptr = kmalloc(sz, GFP_KERNEL); + } while (!ptr); + + return ptr; +} + +/* TODO: deal with channel teardown / restore */ +static int rr_read(void *data, int len) +{ + int rc; + unsigned long flags; +// printk("rr_read() %d\n", len); + for(;;) { + spin_lock_irqsave(&smd_lock, flags); + if (smd_read_avail(smd_channel) >= len) { + rc = smd_read(smd_channel, data, len); + spin_unlock_irqrestore(&smd_lock, flags); + if (rc == len) + return 0; + else + return -EIO; + } + spin_unlock_irqrestore(&smd_lock, flags); + +// printk("rr_read: waiting (%d)\n", len); + wait_event(smd_wait, smd_read_avail(smd_channel) >= len); + } + return 0; +} + +static uint32_t r2r_buf[RPCROUTER_MSGSIZE_MAX]; + +static void do_read_data(struct work_struct *work) +{ + struct rr_header hdr; + struct rr_packet *pkt; + struct rr_fragment *frag; + struct msm_rpc_endpoint *ept; + uint32_t pm, mid; + unsigned long flags; + + if (rr_read(&hdr, sizeof(hdr))) + goto fail_io; + +#if TRACE_R2R_RAW + RR("- ver=%d type=%d src=%d:%08x crx=%d siz=%d dst=%d:%08x\n", + hdr.version, hdr.type, hdr.src_pid, hdr.src_cid, + hdr.confirm_rx, hdr.size, hdr.dst_pid, hdr.dst_cid); +#endif + + if (hdr.version != RPCROUTER_VERSION) { + DIAG("version %d != %d\n", hdr.version, RPCROUTER_VERSION); + goto fail_data; + } + if (hdr.size > RPCROUTER_MSGSIZE_MAX) { + DIAG("msg size %d > max %d\n", hdr.size, RPCROUTER_MSGSIZE_MAX); + goto fail_data; + } + + if (hdr.dst_cid == RPCROUTER_ROUTER_ADDRESS) { + if (rr_read(r2r_buf, hdr.size)) + goto fail_io; + process_control_msg((void*) r2r_buf, hdr.size); + goto done; + } + + if (hdr.size < sizeof(pm)) { + DIAG("runt packet (no pacmark)\n"); + goto fail_data; + } + if (rr_read(&pm, sizeof(pm))) + goto fail_io; + + hdr.size -= sizeof(pm); + + frag = rr_malloc(hdr.size + sizeof(*frag)); + frag->next = NULL; + frag->length = hdr.size; + if (rr_read(frag->data, hdr.size)) + goto fail_io; + + ept = rpcrouter_lookup_local_endpoint(hdr.dst_cid); + if (!ept) { + DIAG("no local ept for cid %08x\n", hdr.dst_cid); + kfree(frag); + goto done; + } + + /* See if there is already a partial packet that matches our mid + * and if so, append this fragment to that packet. + */ + mid = PACMARK_MID(pm); + list_for_each_entry(pkt, &ept->incomplete, list) { + if (pkt->mid == mid) { + pkt->last->next = frag; + pkt->last = frag; + pkt->length += frag->length; + if (PACMARK_LAST(pm)) { + list_del(&pkt->list); + goto packet_complete; + } + goto done; + } + } + + /* This mid is new -- create a packet for it, and put it on + * the incomplete list if this fragment is not a last fragment, + * otherwise put it on the read queue. + */ + pkt = rr_malloc(sizeof(struct rr_packet)); + pkt->first = frag; + pkt->last = frag; + memcpy(&pkt->hdr, &hdr, sizeof(hdr)); + pkt->mid = mid; + pkt->length = frag->length; + if (!PACMARK_LAST(pm)) { + list_add_tail(&pkt->list, &ept->incomplete); + goto done; + } + +packet_complete: + spin_lock_irqsave(&ept->read_q_lock, flags); + list_add_tail(&pkt->list, &ept->read_q); + wake_up(&ept->wait_q); + spin_unlock_irqrestore(&ept->read_q_lock, flags); +done: + if (hdr.confirm_rx) { + union rr_control_msg msg; + + msg.cmd = RPCROUTER_CTRL_CMD_RESUME_TX; + msg.cli.pid = hdr.dst_pid; + msg.cli.cid = hdr.dst_cid; + + RR("x RESUME_TX id=%d:%08x\n", msg.cli.pid, msg.cli.cid); + rpcrouter_send_control_msg(&msg); + } + + queue_work(rpcrouter_workqueue, &work_read_data); + return; + +fail_io: +fail_data: + printk(KERN_ERR "rpc_router has died\n"); +} + +void msm_rpc_setup_req(struct rpc_request_hdr *hdr, uint32_t prog, + uint32_t vers, uint32_t proc) +{ + memset(hdr, 0, sizeof(struct rpc_request_hdr)); + hdr->xid = cpu_to_be32(atomic_add_return(1, &next_xid)); + hdr->rpc_vers = cpu_to_be32(2); + hdr->prog = cpu_to_be32(prog); + hdr->vers = cpu_to_be32(vers); + hdr->procedure = cpu_to_be32(proc); +} + +struct msm_rpc_endpoint *msm_rpc_open(void) +{ + struct msm_rpc_endpoint *ept; + + ept = msm_rpcrouter_create_local_endpoint(MKDEV(0, 0)); + if (ept == NULL) + return ERR_PTR(-ENOMEM); + + return ept; +} + +int msm_rpc_close(struct msm_rpc_endpoint *ept) +{ + return msm_rpcrouter_destroy_local_endpoint(ept); +} + +int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count) +{ + struct rr_header hdr; + uint32_t pacmark; + struct rpc_request_hdr *rq = buffer; + struct rr_remote_endpoint *r_ept; + unsigned long flags; + int needed; + DEFINE_WAIT(__wait); + + /* TODO: fragmentation for large outbound packets */ + if (count > (RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t)) || !count) + return -EINVAL; + + /* snoop the RPC packet and enforce permissions */ + + /* has to have at least the xid and type fields */ + if (count < (sizeof(uint32_t) * 2)) { + printk(KERN_ERR "rr_write: rejecting runt packet\n"); + return -EINVAL; + } + + if (rq->type == 0) { + /* RPC CALL */ + if (count < (sizeof(uint32_t) * 6)) { + printk(KERN_ERR + "rr_write: rejecting runt call packet\n"); + return -EINVAL; + } + if (ept->dst_pid == 0xffffffff) { + printk(KERN_ERR "rr_write: not connected\n"); + return -ENOTCONN; + } + if ((ept->dst_prog != rq->prog) || + (ept->dst_vers != rq->vers)) { + printk(KERN_ERR + "rr_write: cannot write to %08x:%d " + "(bound to %08x:%d)\n", + be32_to_cpu(rq->prog), be32_to_cpu(rq->vers), + be32_to_cpu(ept->dst_prog), + be32_to_cpu(ept->dst_vers)); + return -EINVAL; + } + hdr.dst_pid = ept->dst_pid; + hdr.dst_cid = ept->dst_cid; + IO("CALL to %08x:%d @ %d:%08x (%d bytes)\n", + be32_to_cpu(rq->prog), be32_to_cpu(rq->vers), + ept->dst_pid, ept->dst_cid, count); + } else { + /* RPC REPLY */ + /* TODO: locking */ + if (ept->reply_pid == 0xffffffff) { + printk(KERN_ERR + "rr_write: rejecting unexpected reply\n"); + return -EINVAL; + } + if (ept->reply_xid != rq->xid) { + printk(KERN_ERR + "rr_write: rejecting packet w/ bad xid\n"); + return -EINVAL; + } + + hdr.dst_pid = ept->reply_pid; + hdr.dst_cid = ept->reply_cid; + + /* consume this reply */ + ept->reply_pid = 0xffffffff; + + IO("REPLY to xid=%d @ %d:%08x (%d bytes)\n", + be32_to_cpu(rq->xid), hdr.dst_pid, hdr.dst_cid, count); + } + + r_ept = rpcrouter_lookup_remote_endpoint(hdr.dst_cid); + + if (!r_ept) { + printk(KERN_ERR + "msm_rpc_write(): No route to ept " + "[PID %x CID %x]\n", hdr.dst_pid, hdr.dst_cid); + return -EHOSTUNREACH; + } + + /* Create routing header */ + hdr.type = RPCROUTER_CTRL_CMD_DATA; + hdr.version = RPCROUTER_VERSION; + hdr.src_pid = ept->pid; + hdr.src_cid = ept->cid; + hdr.confirm_rx = 0; + hdr.size = count + sizeof(uint32_t); + + for (;;) { + prepare_to_wait(&r_ept->quota_wait, &__wait, + TASK_INTERRUPTIBLE); + spin_lock_irqsave(&r_ept->quota_lock, flags); + if (r_ept->tx_quota_cntr < RPCROUTER_DEFAULT_RX_QUOTA) + break; + if (signal_pending(current) && + (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) + break; + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + schedule(); + } + finish_wait(&r_ept->quota_wait, &__wait); + + if (signal_pending(current) && + (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) { + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + return -ERESTARTSYS; + } + r_ept->tx_quota_cntr++; + if (r_ept->tx_quota_cntr == RPCROUTER_DEFAULT_RX_QUOTA) + hdr.confirm_rx = 1; + + /* bump pacmark while interrupts disabled to avoid race + * probably should be atomic op instead + */ + pacmark = PACMARK(count, ++next_pacmarkid, 1); + + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + + spin_lock_irqsave(&smd_lock, flags); + + needed = sizeof(hdr) + hdr.size; + while (smd_write_avail(smd_channel) < needed) { + spin_unlock_irqrestore(&smd_lock, flags); + msleep(250); + spin_lock_irqsave(&smd_lock, flags); + } + + /* TODO: deal with full fifo */ + smd_write(smd_channel, &hdr, sizeof(hdr)); + smd_write(smd_channel, &pacmark, sizeof(pacmark)); + smd_write(smd_channel, buffer, count); + + spin_unlock_irqrestore(&smd_lock, flags); + + return count; +} + +/* + * NOTE: It is the responsibility of the caller to kfree buffer + */ +int msm_rpc_read(struct msm_rpc_endpoint *ept, void **buffer, + unsigned user_len, long timeout) +{ + struct rr_fragment *frag, *next; + char *buf; + int rc; + + rc = __msm_rpc_read(ept, &frag, user_len, timeout); + if (rc <= 0) + return rc; + + /* single-fragment messages conveniently can be + * returned as-is (the buffer is at the front) + */ + if (frag->next == 0) { + *buffer = (void*) frag; + return rc; + } + + /* multi-fragment messages, we have to do it the + * hard way, which is rather disgusting right now + */ + buf = rr_malloc(rc); + *buffer = buf; + + while (frag != NULL) { + memcpy(buf, frag->data, frag->length); + next = frag->next; + buf += frag->length; + kfree(frag); + frag = next; + } + + return rc; +} + +int msm_rpc_call(struct msm_rpc_endpoint *ept, uint32_t proc, + void *_request, int request_size, + long timeout) +{ + return msm_rpc_call_reply(ept, proc, + _request, request_size, + NULL, 0, timeout); +} + +int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc, + void *_request, int request_size, + void *_reply, int reply_size, + long timeout) +{ + struct rpc_request_hdr *req = _request; + struct rpc_reply_hdr *reply; + int rc; + + if (request_size < sizeof(*req)) + return -ETOOSMALL; + + if (ept->dst_pid == 0xffffffff) + return -ENOTCONN; + + memset(req, 0, sizeof(*req)); + req->xid = cpu_to_be32(atomic_add_return(1, &next_xid)); + req->rpc_vers = cpu_to_be32(2); + req->prog = ept->dst_prog; + req->vers = ept->dst_vers; + req->procedure = cpu_to_be32(proc); + + rc = msm_rpc_write(ept, req, request_size); + if (rc < 0) + return rc; + + for (;;) { + rc = msm_rpc_read(ept, (void*) &reply, -1, timeout); + if (rc < 0) + return rc; + if (rc < (3 * sizeof(uint32_t))) { + rc = -EIO; + break; + } + /* we should not get CALL packets -- ignore them */ + if (reply->type == 0) { + kfree(reply); + continue; + } + /* If an earlier call timed out, we could get the (no + * longer wanted) reply for it. Ignore replies that + * we don't expect + */ + if (reply->xid != req->xid) { + kfree(reply); + continue; + } + if (reply->reply_stat != 0) { + rc = -EPERM; + break; + } + if (reply->data.acc_hdr.accept_stat != 0) { + rc = -EINVAL; + break; + } + if (_reply == NULL) { + rc = 0; + break; + } + if (rc > reply_size) { + rc = -ENOMEM; + } else { + memcpy(_reply, reply, rc); + } + break; + } + kfree(reply); + return rc; +} + + +static inline int ept_packet_available(struct msm_rpc_endpoint *ept) +{ + unsigned long flags; + int ret; + spin_lock_irqsave(&ept->read_q_lock, flags); + ret = !list_empty(&ept->read_q); + spin_unlock_irqrestore(&ept->read_q_lock, flags); + return ret; +} + +int __msm_rpc_read(struct msm_rpc_endpoint *ept, + struct rr_fragment **frag_ret, + unsigned len, long timeout) +{ + struct rr_packet *pkt; + struct rpc_request_hdr *rq; + DEFINE_WAIT(__wait); + unsigned long flags; + int rc; + + IO("READ on ept %p\n", ept); + + if (ept->flags & MSM_RPC_UNINTERRUPTIBLE) { + if (timeout < 0) { + wait_event(ept->wait_q, ept_packet_available(ept)); + } else { + rc = wait_event_timeout( + ept->wait_q, ept_packet_available(ept), + timeout); + if (rc == 0) + return -ETIMEDOUT; + } + } else { + if (timeout < 0) { + rc = wait_event_interruptible( + ept->wait_q, ept_packet_available(ept)); + if (rc < 0) + return rc; + } else { + rc = wait_event_interruptible_timeout( + ept->wait_q, ept_packet_available(ept), + timeout); + if (rc == 0) + return -ETIMEDOUT; + } + } + + spin_lock_irqsave(&ept->read_q_lock, flags); + if (list_empty(&ept->read_q)) { + spin_unlock_irqrestore(&ept->read_q_lock, flags); + return -EAGAIN; + } + pkt = list_first_entry(&ept->read_q, struct rr_packet, list); + if (pkt->length > len) { + spin_unlock_irqrestore(&ept->read_q_lock, flags); + return -ETOOSMALL; + } + list_del(&pkt->list); + spin_unlock_irqrestore(&ept->read_q_lock, flags); + + rc = pkt->length; + + *frag_ret = pkt->first; + rq = (void*) pkt->first->data; + if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 0)) { + /* RPC CALL */ + if (ept->reply_pid != 0xffffffff) { + printk(KERN_WARNING + "rr_read: lost previous reply xid...\n"); + } + /* TODO: locking? */ + ept->reply_pid = pkt->hdr.src_pid; + ept->reply_cid = pkt->hdr.src_cid; + ept->reply_xid = rq->xid; + } + + kfree(pkt); + + IO("READ on ept %p (%d bytes)\n", ept, rc); + return rc; +} + +struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned flags) +{ + struct msm_rpc_endpoint *ept; + struct rr_server *server; + + server = rpcrouter_lookup_server(prog, vers); + if (!server) + return ERR_PTR(-EHOSTUNREACH); + + ept = msm_rpc_open(); + if (IS_ERR(ept)) + return ept; + + ept->flags = flags; + ept->dst_pid = server->pid; + ept->dst_cid = server->cid; + ept->dst_prog = cpu_to_be32(prog); + ept->dst_vers = cpu_to_be32(vers); + + return ept; +} + +/* TODO: permission check? */ +int msm_rpc_register_server(struct msm_rpc_endpoint *ept, + uint32_t prog, uint32_t vers) +{ + int rc; + union rr_control_msg msg; + struct rr_server *server; + + server = rpcrouter_create_server(ept->pid, ept->cid, + prog, vers); + if (!server) + return -ENODEV; + + msg.srv.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER; + msg.srv.pid = ept->pid; + msg.srv.cid = ept->cid; + msg.srv.prog = prog; + msg.srv.vers = vers; + + RR("x NEW_SERVER id=%d:%08x prog=%08x:%d\n", + ept->pid, ept->cid, prog, vers); + + rc = rpcrouter_send_control_msg(&msg); + if (rc < 0) + return rc; + + return 0; +} + +/* TODO: permission check -- disallow unreg of somebody else's server */ +int msm_rpc_unregister_server(struct msm_rpc_endpoint *ept, + uint32_t prog, uint32_t vers) +{ + struct rr_server *server; + server = rpcrouter_lookup_server(prog, vers); + + if (!server) + return -ENOENT; + rpcrouter_destroy_server(server); + return 0; +} + +static int msm_rpcrouter_probe(struct platform_device *pdev) +{ + int rc; + + /* Initialize what we need to start processing */ + INIT_LIST_HEAD(&local_endpoints); + INIT_LIST_HEAD(&remote_endpoints); + + init_waitqueue_head(&newserver_wait); + init_waitqueue_head(&smd_wait); + + rpcrouter_workqueue = create_singlethread_workqueue("rpcrouter"); + if (!rpcrouter_workqueue) + return -ENOMEM; + + rc = msm_rpcrouter_init_devices(); + if (rc < 0) + goto fail_destroy_workqueue; + + /* Open up SMD channel 2 */ + initialized = 0; + rc = smd_open("SMD_RPCCALL", &smd_channel, NULL, rpcrouter_smdnotify); + if (rc < 0) + goto fail_remove_devices; + + queue_work(rpcrouter_workqueue, &work_read_data); + return 0; + +fail_remove_devices: + msm_rpcrouter_exit_devices(); +fail_destroy_workqueue: + destroy_workqueue(rpcrouter_workqueue); + return rc; +} + +static struct platform_driver msm_smd_channel2_driver = { + .probe = msm_rpcrouter_probe, + .driver = { + .name = "SMD_RPCCALL", + .owner = THIS_MODULE, + }, +}; + +static int __init rpcrouter_init(void) +{ + return platform_driver_register(&msm_smd_channel2_driver); +} + +module_init(rpcrouter_init); +MODULE_DESCRIPTION("MSM RPC Router"); +MODULE_AUTHOR("San Mehat "); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/smd_rpcrouter.h b/arch/arm/mach-msm/smd_rpcrouter.h new file mode 100644 index 0000000000000..9438ca3780c92 --- /dev/null +++ b/arch/arm/mach-msm/smd_rpcrouter.h @@ -0,0 +1,190 @@ +/** arch/arm/mach-msm/smd_rpcrouter.h + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007 QUALCOMM Incorporated + * Author: San Mehat + * + * 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 _ARCH_ARM_MACH_MSM_SMD_RPCROUTER_H +#define _ARCH_ARM_MACH_MSM_SMD_RPCROUTER_H + +#include +#include +#include +#include + +#include +#include + +#include + +/* definitions for the R2R wire protcol */ + +#define RPCROUTER_VERSION 1 +#define RPCROUTER_PROCESSORS_MAX 4 +#define RPCROUTER_MSGSIZE_MAX 512 + +#define RPCROUTER_CLIENT_BCAST_ID 0xffffffff +#define RPCROUTER_ROUTER_ADDRESS 0xfffffffe + +#define RPCROUTER_PID_LOCAL 1 +#define RPCROUTER_PID_REMOTE 0 + +#define RPCROUTER_CTRL_CMD_DATA 1 +#define RPCROUTER_CTRL_CMD_HELLO 2 +#define RPCROUTER_CTRL_CMD_BYE 3 +#define RPCROUTER_CTRL_CMD_NEW_SERVER 4 +#define RPCROUTER_CTRL_CMD_REMOVE_SERVER 5 +#define RPCROUTER_CTRL_CMD_REMOVE_CLIENT 6 +#define RPCROUTER_CTRL_CMD_RESUME_TX 7 +#define RPCROUTER_CTRL_CMD_EXIT 8 + +#define RPCROUTER_DEFAULT_RX_QUOTA 5 + +union rr_control_msg { + uint32_t cmd; + struct { + uint32_t cmd; + uint32_t prog; + uint32_t vers; + uint32_t pid; + uint32_t cid; + } srv; + struct { + uint32_t cmd; + uint32_t pid; + uint32_t cid; + } cli; +}; + +struct rr_header { + uint32_t version; + uint32_t type; + uint32_t src_pid; + uint32_t src_cid; + uint32_t confirm_rx; + uint32_t size; + uint32_t dst_pid; + uint32_t dst_cid; +}; + +/* internals */ + +#define RPCROUTER_MAX_REMOTE_SERVERS 100 + +struct rr_fragment { + unsigned char data[RPCROUTER_MSGSIZE_MAX]; + uint32_t length; + struct rr_fragment *next; +}; + +struct rr_packet { + struct list_head list; + struct rr_fragment *first; + struct rr_fragment *last; + struct rr_header hdr; + uint32_t mid; + uint32_t length; +}; + +#define PACMARK_LAST(n) ((n) & 0x80000000) +#define PACMARK_MID(n) (((n) >> 16) & 0xFF) +#define PACMARK_LEN(n) ((n) & 0xFFFF) + +static inline uint32_t PACMARK(uint32_t len, uint32_t mid, uint32_t last) +{ + return (len & 0xFFFF) | ((mid & 0xFF) << 16) | ((!!last) << 31); +} + +struct rr_server { + struct list_head list; + + uint32_t pid; + uint32_t cid; + uint32_t prog; + uint32_t vers; + + dev_t device_number; + struct cdev cdev; + struct device *device; + struct rpcsvr_platform_device p_device; + char pdev_name[32]; +}; + +struct rr_remote_endpoint { + uint32_t pid; + uint32_t cid; + + int tx_quota_cntr; + spinlock_t quota_lock; + wait_queue_head_t quota_wait; + + struct list_head list; +}; + +struct msm_rpc_endpoint { + struct list_head list; + + /* incomplete packets waiting for assembly */ + struct list_head incomplete; + + /* complete packets waiting to be read */ + struct list_head read_q; + spinlock_t read_q_lock; + wait_queue_head_t wait_q; + unsigned flags; + + /* endpoint address */ + uint32_t pid; + uint32_t cid; + + /* bound remote address + * if not connected (dst_pid == 0xffffffff) RPC_CALL writes fail + * RPC_CALLs must be to the prog/vers below or they will fail + */ + uint32_t dst_pid; + uint32_t dst_cid; + uint32_t dst_prog; /* be32 */ + uint32_t dst_vers; /* be32 */ + + /* reply remote address + * if reply_pid == 0xffffffff, none available + * RPC_REPLY writes may only go to the pid/cid/xid of the + * last RPC_CALL we received. + */ + uint32_t reply_pid; + uint32_t reply_cid; + uint32_t reply_xid; /* be32 */ + + /* device node if this endpoint is accessed via userspace */ + dev_t dev; +}; + +/* shared between smd_rpcrouter*.c */ + +int __msm_rpc_read(struct msm_rpc_endpoint *ept, + struct rr_fragment **frag, + unsigned len, long timeout); + +struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev); +int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept); + +int msm_rpcrouter_create_server_cdev(struct rr_server *server); +int msm_rpcrouter_create_server_pdev(struct rr_server *server); + +int msm_rpcrouter_init_devices(void); +void msm_rpcrouter_exit_devices(void); + +extern dev_t msm_rpcrouter_devno; +extern struct class *msm_rpcrouter_class; +#endif diff --git a/arch/arm/mach-msm/smd_rpcrouter_device.c b/arch/arm/mach-msm/smd_rpcrouter_device.c new file mode 100644 index 0000000000000..56a15984f9f1b --- /dev/null +++ b/arch/arm/mach-msm/smd_rpcrouter_device.c @@ -0,0 +1,340 @@ +/* arch/arm/mach-msm/smd_rpcrouter_device.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007 QUALCOMM Incorporated + * Author: San Mehat + * + * 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 +#include "smd_rpcrouter.h" + +/* Next minor # available for a remote server */ +static int next_minor = 1; + +struct class *msm_rpcrouter_class; +dev_t msm_rpcrouter_devno; + +static struct cdev rpcrouter_cdev; +static struct device *rpcrouter_device; + +static int rpcrouter_open(struct inode *inode, struct file *filp) +{ + int rc; + struct msm_rpc_endpoint *ept; + + rc = nonseekable_open(inode, filp); + if (rc < 0) + return rc; + + ept = msm_rpcrouter_create_local_endpoint(inode->i_rdev); + if (!ept) + return -ENOMEM; + + filp->private_data = ept; + return 0; +} + +static int rpcrouter_release(struct inode *inode, struct file *filp) +{ + struct msm_rpc_endpoint *ept; + ept = (struct msm_rpc_endpoint *) filp->private_data; + + return msm_rpcrouter_destroy_local_endpoint(ept); +} + +static ssize_t rpcrouter_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct msm_rpc_endpoint *ept; + struct rr_fragment *frag, *next; + int rc; + + ept = (struct msm_rpc_endpoint *) filp->private_data; + + rc = __msm_rpc_read(ept, &frag, count, -1); + if (rc < 0) + return rc; + + count = rc; + + while (frag != NULL) { + if (copy_to_user(buf, frag->data, frag->length)) { + printk(KERN_ERR + "rpcrouter: could not copy all read data to user!\n"); + rc = -EFAULT; + } + buf += frag->length; + next = frag->next; + kfree(frag); + frag = next; + } + + return rc; +} + +static ssize_t rpcrouter_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct msm_rpc_endpoint *ept; + int rc = 0; + void *k_buffer; + + ept = (struct msm_rpc_endpoint *) filp->private_data; + + if (count > RPCROUTER_MSGSIZE_MAX) + return -EINVAL; + + k_buffer = kmalloc(count, GFP_KERNEL); + if (!k_buffer) + return -ENOMEM; + + if (copy_from_user(k_buffer, buf, count)) { + rc = -EFAULT; + goto write_out_free; + } + + rc = msm_rpc_write(ept, k_buffer, count); + if (rc < 0) + goto write_out_free; + + rc = count; +write_out_free: + kfree(k_buffer); + return rc; +} + +static unsigned int rpcrouter_poll(struct file *filp, + struct poll_table_struct *wait) +{ + struct msm_rpc_endpoint *ept; + unsigned mask = 0; + ept = (struct msm_rpc_endpoint *) filp->private_data; + + /* If there's data already in the read queue, return POLLIN. + * Else, wait for the requested amount of time, and check again. + */ + if (!list_empty(&ept->read_q)) + mask |= POLLIN; + + if (!mask) { + poll_wait(filp, &ept->wait_q, wait); + if (!list_empty(&ept->read_q)) + mask |= POLLIN; + } + + return mask; +} + +static long rpcrouter_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct msm_rpc_endpoint *ept; + struct rpcrouter_ioctl_server_args server_args; + int rc; + uint32_t n; + + ept = (struct msm_rpc_endpoint *) filp->private_data; + switch (cmd) { + + case RPC_ROUTER_IOCTL_GET_VERSION: + n = RPC_ROUTER_VERSION_V1; + rc = put_user(n, (unsigned int *) arg); + break; + + case RPC_ROUTER_IOCTL_GET_MTU: + /* the pacmark word reduces the actual payload + * possible per message + */ + n = RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t); + rc = put_user(n, (unsigned int *) arg); + break; + + case RPC_ROUTER_IOCTL_REGISTER_SERVER: + rc = copy_from_user(&server_args, (void *) arg, + sizeof(server_args)); + if (rc < 0) + break; + msm_rpc_register_server(ept, + server_args.prog, + server_args.vers); + break; + + case RPC_ROUTER_IOCTL_UNREGISTER_SERVER: + rc = copy_from_user(&server_args, (void *) arg, + sizeof(server_args)); + if (rc < 0) + break; + + msm_rpc_unregister_server(ept, + server_args.prog, + server_args.vers); + break; + + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static struct file_operations rpcrouter_server_fops = { + .owner = THIS_MODULE, + .open = rpcrouter_open, + .release = rpcrouter_release, + .read = rpcrouter_read, + .write = rpcrouter_write, + .poll = rpcrouter_poll, + .unlocked_ioctl = rpcrouter_ioctl, +}; + +static struct file_operations rpcrouter_router_fops = { + .owner = THIS_MODULE, + .open = rpcrouter_open, + .release = rpcrouter_release, + .read = rpcrouter_read, + .write = rpcrouter_write, + .poll = rpcrouter_poll, + .unlocked_ioctl = rpcrouter_ioctl, +}; + +int msm_rpcrouter_create_server_cdev(struct rr_server *server) +{ + int rc; + + if (next_minor == RPCROUTER_MAX_REMOTE_SERVERS) { + printk(KERN_ERR + "rpcrouter: Minor numbers exhausted - Increase " + "RPCROUTER_MAX_REMOTE_SERVERS\n"); + return -ENOBUFS; + } + server->device_number = + MKDEV(MAJOR(msm_rpcrouter_devno), next_minor++); + + server->device = + device_create(msm_rpcrouter_class, rpcrouter_device, + server->device_number, NULL, "%.8x:%.8x", + server->prog, server->vers); + if (IS_ERR(server->device)) { + printk(KERN_ERR + "rpcrouter: Unable to create device (%ld)\n", + PTR_ERR(server->device)); + return PTR_ERR(server->device);; + } + + cdev_init(&server->cdev, &rpcrouter_server_fops); + server->cdev.owner = THIS_MODULE; + + rc = cdev_add(&server->cdev, server->device_number, 1); + if (rc < 0) { + printk(KERN_ERR + "rpcrouter: Unable to add chrdev (%d)\n", rc); + device_destroy(msm_rpcrouter_class, server->device_number); + return rc; + } + return 0; +} + +int msm_rpcrouter_create_server_pdev(struct rr_server *server) +{ + sprintf(server->pdev_name, "rs%.8x:%.8x", + server->prog, server->vers); + + server->p_device.base.id = -1; + server->p_device.base.name = server->pdev_name; + + server->p_device.prog = server->prog; + server->p_device.vers = server->vers; + + platform_device_register(&server->p_device.base); + return 0; +} + +int msm_rpcrouter_init_devices(void) +{ + int rc; + int major; + + /* Create the device nodes */ + msm_rpcrouter_class = class_create(THIS_MODULE, "oncrpc"); + if (IS_ERR(msm_rpcrouter_class)) { + rc = -ENOMEM; + printk(KERN_ERR + "rpcrouter: failed to create oncrpc class\n"); + goto fail; + } + + rc = alloc_chrdev_region(&msm_rpcrouter_devno, 0, + RPCROUTER_MAX_REMOTE_SERVERS + 1, + "oncrpc"); + if (rc < 0) { + printk(KERN_ERR + "rpcrouter: Failed to alloc chardev region (%d)\n", rc); + goto fail_destroy_class; + } + + major = MAJOR(msm_rpcrouter_devno); + rpcrouter_device = device_create(msm_rpcrouter_class, NULL, + msm_rpcrouter_devno, NULL, "%.8x:%d", + 0, 0); + if (IS_ERR(rpcrouter_device)) { + rc = -ENOMEM; + goto fail_unregister_cdev_region; + } + + cdev_init(&rpcrouter_cdev, &rpcrouter_router_fops); + rpcrouter_cdev.owner = THIS_MODULE; + + rc = cdev_add(&rpcrouter_cdev, msm_rpcrouter_devno, 1); + if (rc < 0) + goto fail_destroy_device; + + return 0; + +fail_destroy_device: + device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno); +fail_unregister_cdev_region: + unregister_chrdev_region(msm_rpcrouter_devno, + RPCROUTER_MAX_REMOTE_SERVERS + 1); +fail_destroy_class: + class_destroy(msm_rpcrouter_class); +fail: + return rc; +} + +void msm_rpcrouter_exit_devices(void) +{ + cdev_del(&rpcrouter_cdev); + device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno); + unregister_chrdev_region(msm_rpcrouter_devno, + RPCROUTER_MAX_REMOTE_SERVERS + 1); + class_destroy(msm_rpcrouter_class); +} + diff --git a/arch/arm/mach-msm/smd_rpcrouter_servers.c b/arch/arm/mach-msm/smd_rpcrouter_servers.c new file mode 100644 index 0000000000000..77e43273d08ba --- /dev/null +++ b/arch/arm/mach-msm/smd_rpcrouter_servers.c @@ -0,0 +1,213 @@ +/* arch/arm/mach-msm/rpc_servers.c + * + * Copyright (C) 2007 Google, Inc. + * Author: Iliyan Malchev + * + * 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 "smd_rpcrouter.h" + +static struct msm_rpc_endpoint *endpoint; + +#define FLAG_REGISTERED 0x0001 + +static LIST_HEAD(rpc_server_list); +static DEFINE_MUTEX(rpc_server_list_lock); +static int rpc_servers_active; + +static void rpc_server_register(struct msm_rpc_server *server) +{ + int rc; + rc = msm_rpc_register_server(endpoint, server->prog, server->vers); + if (rc < 0) + printk(KERN_ERR "[rpcserver] error registering %p @ %08x:%d\n", + server, server->prog, server->vers); +} + +static struct msm_rpc_server *rpc_server_find(uint32_t prog, uint32_t vers) +{ + struct msm_rpc_server *server; + + mutex_lock(&rpc_server_list_lock); + list_for_each_entry(server, &rpc_server_list, list) { + if ((server->prog == prog) && (server->vers == vers)) { + mutex_unlock(&rpc_server_list_lock); + return server; + } + } + mutex_unlock(&rpc_server_list_lock); + return NULL; +} + +static void rpc_server_register_all(void) +{ + struct msm_rpc_server *server; + + mutex_lock(&rpc_server_list_lock); + list_for_each_entry(server, &rpc_server_list, list) { + if (!(server->flags & FLAG_REGISTERED)) { + rpc_server_register(server); + server->flags |= FLAG_REGISTERED; + } + } + mutex_unlock(&rpc_server_list_lock); +} + +int msm_rpc_create_server(struct msm_rpc_server *server) +{ + /* make sure we're in a sane state first */ + server->flags = 0; + INIT_LIST_HEAD(&server->list); + + mutex_lock(&rpc_server_list_lock); + list_add(&server->list, &rpc_server_list); + if (rpc_servers_active) { + rpc_server_register(server); + server->flags |= FLAG_REGISTERED; + } + mutex_unlock(&rpc_server_list_lock); + + return 0; +} + +static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client, + uint32_t xid, uint32_t accept_status) +{ + int rc = 0; + uint8_t reply_buf[sizeof(struct rpc_reply_hdr)]; + struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf; + + reply->xid = cpu_to_be32(xid); + reply->type = cpu_to_be32(1); /* reply */ + reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); + + reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status); + reply->data.acc_hdr.verf_flavor = 0; + reply->data.acc_hdr.verf_length = 0; + + rc = msm_rpc_write(endpoint, reply_buf, sizeof(reply_buf)); + if (rc < 0) + printk(KERN_ERR + "%s: could not write response: %d\n", + __FUNCTION__, rc); + + return rc; +} + +static int rpc_servers_thread(void *data) +{ + void *buffer; + struct rpc_request_hdr *req; + struct msm_rpc_server *server; + int rc; + + for (;;) { + rc = msm_rpc_read(endpoint, &buffer, -1, -1); + if (rc < 0) { + printk(KERN_ERR "%s: could not read: %d\n", + __FUNCTION__, rc); + break; + } + req = (struct rpc_request_hdr *)buffer; + + req->type = be32_to_cpu(req->type); + req->xid = be32_to_cpu(req->xid); + req->rpc_vers = be32_to_cpu(req->rpc_vers); + req->prog = be32_to_cpu(req->prog); + req->vers = be32_to_cpu(req->vers); + req->procedure = be32_to_cpu(req->procedure); + + server = rpc_server_find(req->prog, req->vers); + + if (req->rpc_vers != 2) + continue; + if (req->type != 0) + continue; + if (!server) { + rpc_send_accepted_void_reply( + endpoint, req->xid, + RPC_ACCEPTSTAT_PROG_UNAVAIL); + continue; + } + + rc = server->rpc_call(server, req, rc); + + switch (rc) { + case 0: + rpc_send_accepted_void_reply( + endpoint, req->xid, + RPC_ACCEPTSTAT_SUCCESS); + break; + default: + rpc_send_accepted_void_reply( + endpoint, req->xid, + RPC_ACCEPTSTAT_PROG_UNAVAIL); + break; + } + + kfree(buffer); + } + + do_exit(0); +} + +static int rpcservers_probe(struct platform_device *pdev) +{ + endpoint = msm_rpc_open(); + if (IS_ERR(endpoint)) + return PTR_ERR(endpoint); + + /* we're online -- register any servers installed beforehand */ + rpc_servers_active = 1; + rpc_server_register_all(); + + /* start the kernel thread */ + kthread_run(rpc_servers_thread, NULL, "krpcserversd"); + + return 0; +} + +static struct platform_driver rpcservers_driver = { + .probe = rpcservers_probe, + .driver = { + .name = "oncrpc_router", + .owner = THIS_MODULE, + }, +}; + +static int __init rpc_servers_init(void) +{ + return platform_driver_register(&rpcservers_driver); +} + +module_init(rpc_servers_init); + +MODULE_DESCRIPTION("MSM RPC Servers"); +MODULE_AUTHOR("Iliyan Malchev "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/msm_rpcrouter.h b/include/linux/msm_rpcrouter.h new file mode 100644 index 0000000000000..3e2adc0948645 --- /dev/null +++ b/include/linux/msm_rpcrouter.h @@ -0,0 +1,44 @@ +/* include/linux/msm_rpcrouter.h + * + * Copyright (c) QUALCOMM Incorporated + * Copyright (C) 2007 Google, Inc. + * Author: San Mehat + * + * 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_MSM_RPCROUTER_H +#define __LINUX_MSM_RPCROUTER_H + +#include +#include + +#define RPC_ROUTER_VERSION_V1 0x00010000 + +struct rpcrouter_ioctl_server_args { + uint32_t prog; + uint32_t vers; +}; + +#define RPC_ROUTER_IOCTL_MAGIC (0xC1) + +#define RPC_ROUTER_IOCTL_GET_VERSION \ + _IOR(RPC_ROUTER_IOCTL_MAGIC, 0, unsigned int) + +#define RPC_ROUTER_IOCTL_GET_MTU \ + _IOR(RPC_ROUTER_IOCTL_MAGIC, 1, unsigned int) + +#define RPC_ROUTER_IOCTL_REGISTER_SERVER \ + _IOWR(RPC_ROUTER_IOCTL_MAGIC, 2, unsigned int) + +#define RPC_ROUTER_IOCTL_UNREGISTER_SERVER \ + _IOWR(RPC_ROUTER_IOCTL_MAGIC, 3, unsigned int) + +#endif From 7d09c36d0d734739a4cdfa32ea50e237acb08624 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Sun, 25 May 2008 22:05:07 -0700 Subject: [PATCH 0168/2556] [ARM] msm: provide AMSS version selection mechanism Some drivers have dependencies on AMSS versions that are not easily resolved at runtime. Provide a mechanism for selecting the AMSS version so they can be compiled appropriately. Signed-off-by: Brian Swetland --- arch/arm/mach-msm/Kconfig | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index f46be881562a8..4c388a098a410 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -66,6 +66,27 @@ config HAS_MSM_DEBUG_UART_PHYS config MSM_VIC bool +config MSM_AMSS_VERSION + int + default 6210 if MSM_AMSS_VERSION_6210 + default 6220 if MSM_AMSS_VERSION_6220 + default 6225 if MSM_AMSS_VERSION_6225 + +choice + prompt "AMSS modem firmware version" + + default MSM_AMSS_VERSION_6225 + + config MSM_AMSS_VERSION_6210 + bool "6.2.10" + + config MSM_AMSS_VERSION_6220 + bool "6.2.20" + + config MSM_AMSS_VERSION_6225 + bool "6.2.20 + New ADSP" +endchoice + menu "Qualcomm MSM Board Type" config MACH_HALIBUT From 7c48ece9056a020db7c47fc4258e32b62e0e227f Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Mon, 31 Dec 2007 13:11:28 -0800 Subject: [PATCH 0169/2556] [ARM] msm_rpc: support rpc servers - time_remote_mtoa server receives notification of time bases - dog_keepalive server to handle watchdog events Signed-off-by: Iliyan Malchev Signed-off-by: San Mehat Signed-off-by: Brian Swetland --- arch/arm/mach-msm/Kconfig | 7 ++ arch/arm/mach-msm/Makefile | 2 + arch/arm/mach-msm/rpc_server_dog_keepalive.c | 62 ++++++++++++++++ arch/arm/mach-msm/rpc_server_time_remote.c | 75 ++++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 arch/arm/mach-msm/rpc_server_dog_keepalive.c create mode 100644 arch/arm/mach-msm/rpc_server_time_remote.c diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 4c388a098a410..ecef5fad20654 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -207,4 +207,11 @@ config MSM_ONCRPCROUTER Support for the MSM ONCRPC router for communication between the ARM9 and ARM11 +config MSM_RPCSERVERS + depends on MSM_ONCRPCROUTER + default y + bool "Kernel side RPC server bundle" + help + none + endif diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index a1eaa0634602d..4bda7f9af19d5 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -27,6 +27,8 @@ obj-$(CONFIG_SMP) += headsmp.o platsmp.o obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter.o obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_device.o obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o +obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_dog_keepalive.o +obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_time_remote.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o diff --git a/arch/arm/mach-msm/rpc_server_dog_keepalive.c b/arch/arm/mach-msm/rpc_server_dog_keepalive.c new file mode 100644 index 0000000000000..a04a380e1ff00 --- /dev/null +++ b/arch/arm/mach-msm/rpc_server_dog_keepalive.c @@ -0,0 +1,62 @@ +/* arch/arm/mach-msm/rpc_server_dog_keepalive.c + * + * Copyright (C) 2007 Google, Inc. + * Author: Iliyan Malchev + * + * 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 + +/* dog_keepalive server definitions */ + +#define DOG_KEEPALIVE_PROG 0x30000015 +#define RPC_DOG_KEEPALIVE_NULL 0 + +#if CONFIG_MSM_AMSS_VERSION==6210 +#define DOG_KEEPALIVE_VERS 0 +#define RPC_DOG_KEEPALIVE_BEACON 1 +#elif (CONFIG_MSM_AMSS_VERSION==6220) || (CONFIG_MSM_AMSS_VERSION==6225) +#define DOG_KEEPALIVE_VERS 0x731fa727 +#define RPC_DOG_KEEPALIVE_BEACON 2 +#else +#error "Unsupported AMSS version" +#endif + +static int handle_rpc_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + switch (req->procedure) { + case RPC_DOG_KEEPALIVE_NULL: + return 0; + case RPC_DOG_KEEPALIVE_BEACON: + printk(KERN_INFO "DOG KEEPALIVE PING\n"); + return 0; + default: + return -ENODEV; + } +} + +static struct msm_rpc_server rpc_server = { + .prog = DOG_KEEPALIVE_PROG, + .vers = DOG_KEEPALIVE_VERS, + .rpc_call = handle_rpc_call, +}; + +static int __init rpc_server_init(void) +{ + return msm_rpc_create_server(&rpc_server); +} + + +module_init(rpc_server_init); diff --git a/arch/arm/mach-msm/rpc_server_time_remote.c b/arch/arm/mach-msm/rpc_server_time_remote.c new file mode 100644 index 0000000000000..a833f83dfedb2 --- /dev/null +++ b/arch/arm/mach-msm/rpc_server_time_remote.c @@ -0,0 +1,75 @@ +/* arch/arm/mach-msm/rpc_server_time_remote.c + * + * Copyright (C) 2007 Google, Inc. + * Author: Iliyan Malchev + * + * 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 + +/* time_remote_mtoa server definitions. */ + +#define TIME_REMOTE_MTOA_PROG 0x3000005d +#define RPC_TIME_REMOTE_MTOA_NULL 0 +#define RPC_TIME_TOD_SET_APPS_BASES 2 + +#if CONFIG_MSM_AMSS_VERSION==6210 +#define TIME_REMOTE_MTOA_VERS 0 +#elif (CONFIG_MSM_AMSS_VERSION==6220) || (CONFIG_MSM_AMSS_VERSION==6225) +#define TIME_REMOTE_MTOA_VERS 0x9202a8e4 +#else +#error "Unknown AMSS version" +#endif + +struct rpc_time_tod_set_apps_bases_args { + uint32_t tick; + uint64_t stamp; +}; + +static int handle_rpc_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + switch (req->procedure) { + case RPC_TIME_REMOTE_MTOA_NULL: + return 0; + + case RPC_TIME_TOD_SET_APPS_BASES: { + struct rpc_time_tod_set_apps_bases_args *args; + args = (struct rpc_time_tod_set_apps_bases_args *)(req + 1); + args->tick = be32_to_cpu(args->tick); + args->stamp = be64_to_cpu(args->stamp); + printk(KERN_INFO "RPC_TIME_TOD_SET_APPS_BASES:\n" + "\ttick = %d\n" + "\tstamp = %lld\n", + args->tick, args->stamp); + return 0; + } + default: + return -ENODEV; + } +} + +static struct msm_rpc_server rpc_server = { + .prog = TIME_REMOTE_MTOA_PROG, + .vers = TIME_REMOTE_MTOA_VERS, + .rpc_call = handle_rpc_call, +}; + +static int __init rpc_server_init(void) +{ + return msm_rpc_create_server(&rpc_server); +} + + +module_init(rpc_server_init); From 11f670f64cfea0009ad33118a9ced27fb4cb6345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 2 May 2008 15:30:32 -0700 Subject: [PATCH 0170/2556] HACK: [ARM] msm: Timer fixes. FOR TESTING ONLY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HACK: Replaced upstream with out version for testing of 38. Should revisit all the sync code with new hardware. Restart timer when entering idle if it appears to have missed. If the timer should have fired more than a millisecond ago, we assume that the interrupt was lost and reprogram the timer. Sync the GP timer with the other core. Get the time from the other core after low power idle. Return time until next alarm when entering idle. Add config option to select default timer. Fix shift value so multiplier does not overflow. Add workarounds for some hardware bugs. Add sched_clock. Increase min_delta_ns so that we don't get stuck if running at low speed. Limit number of alarm-already-expired messages. Change-Id: I46a5d8919d38c5b279b54812a96331c00551cf9d Signed-off-by: Brian Swetland Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/Kconfig | 15 ++ arch/arm/mach-msm/timer.c | 363 +++++++++++++++++++++++++++----------- 2 files changed, 276 insertions(+), 102 deletions(-) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index ecef5fad20654..7c3d4919cdf69 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -184,6 +184,21 @@ config MSM_SMD_PKG3 config MSM_PROC_COMM bool +choice + prompt "Default Timer" + default MSM7X00A_USE_GP_TIMER + + config MSM7X00A_USE_GP_TIMER + bool "GP Timer" + help + Low resolution timer that allows power collapse from idle. + + config MSM7X00A_USE_DG_TIMER + bool "DG Timer" + help + High resolution timer. +endchoice + config MSM_SMD bool diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index c105d28b53e38..c257f3eaa6522 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -25,6 +25,14 @@ #include #include +#include "smd_private.h" + +enum { + MSM_TIMER_DEBUG_SYNC = 1U << 0, +}; +static int msm_timer_debug_mask; +module_param_named(debug_mask, msm_timer_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + #ifndef MSM_DGT_BASE #define MSM_DGT_BASE (MSM_GPT_BASE + 0x10) #endif @@ -47,19 +55,6 @@ enum { #define GPT_HZ 32768 -enum timer_location { - LOCAL_TIMER = 0, - GLOBAL_TIMER = 1, -}; - -#ifdef MSM_TMR0_BASE -#define MSM_TMR_GLOBAL (MSM_TMR0_BASE - MSM_TMR_BASE) -#else -#define MSM_TMR_GLOBAL 0 -#endif - -#define MSM_GLOBAL_TIMER MSM_CLOCK_DGT - #if defined(CONFIG_ARCH_QSD8X50) #define DGT_HZ (19200000 / 4) /* 19.2 MHz / 4 by default */ #define MSM_DGT_SHIFT (0) @@ -71,6 +66,14 @@ enum timer_location { #define MSM_DGT_SHIFT (5) #endif +static int msm_timer_ready; + +enum { + MSM_CLOCK_FLAGS_UNSTABLE_COUNT = 1U << 0, + MSM_CLOCK_FLAGS_ODD_MATCH_WRITE = 1U << 1, + MSM_CLOCK_FLAGS_DELAYED_WRITE_POST = 1U << 2, +}; + struct msm_clock { struct clock_event_device clockevent; struct clocksource clocksource; @@ -78,81 +81,272 @@ struct msm_clock { void __iomem *regbase; uint32_t freq; uint32_t shift; - void __iomem *global_counter; - void __iomem *local_counter; + uint32_t flags; + uint32_t write_delay; + uint32_t last_set; + uint32_t offset; + uint32_t alarm_vtime; + uint32_t smem_offset; + uint32_t smem_in_sync; }; - enum { MSM_CLOCK_GPT, MSM_CLOCK_DGT, - NR_TIMERS, }; - - static struct msm_clock msm_clocks[]; -static struct clock_event_device *local_clock_event; +static struct msm_clock *msm_active_clock; static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) { struct clock_event_device *evt = dev_id; - if (smp_processor_id() != 0) - evt = local_clock_event; - if (evt->event_handler == NULL) - return IRQ_HANDLED; evt->event_handler(evt); return IRQ_HANDLED; } -static cycle_t msm_read_timer_count(struct clocksource *cs) +static uint32_t msm_read_timer_count(struct msm_clock *clock) { - struct msm_clock *clk = container_of(cs, struct msm_clock, clocksource); + uint32_t t1, t2; + int loop_count = 0; + + t1 = readl(clock->regbase + TIMER_COUNT_VAL); + if (!(clock->flags & MSM_CLOCK_FLAGS_UNSTABLE_COUNT)) + return t1; + while (1) { + t2 = readl(clock->regbase + TIMER_COUNT_VAL); + if (t1 == t2) + return t1; + if (loop_count++ > 10) { + printk(KERN_ERR "msm_read_timer_count timer %s did not" + "stabilize %u != %u\n", clock->clockevent.name, + t2, t1); + return t2; + } + t1 = t2; + } +} - return readl(clk->global_counter); +static cycle_t msm_gpt_read(struct clocksource *cs) +{ + struct msm_clock *clock = &msm_clocks[MSM_CLOCK_GPT]; + return msm_read_timer_count(clock) + clock->offset; } -static struct msm_clock *clockevent_to_clock(struct clock_event_device *evt) +static cycle_t msm_dgt_read(struct clocksource *cs) { -#ifdef CONFIG_SMP - int i; - for (i = 0; i < NR_TIMERS; i++) - if (evt == &(msm_clocks[i].clockevent)) - return &msm_clocks[i]; - return &msm_clocks[MSM_GLOBAL_TIMER]; -#else - return container_of(evt, struct msm_clock, clockevent); -#endif + struct msm_clock *clock = &msm_clocks[MSM_CLOCK_DGT]; + return (msm_read_timer_count(clock) + clock->offset) >> MSM_DGT_SHIFT; } static int msm_timer_set_next_event(unsigned long cycles, struct clock_event_device *evt) { - struct msm_clock *clock = clockevent_to_clock(evt); - uint32_t now = readl(clock->local_counter); - uint32_t alarm = now + (cycles << clock->shift); - + int i; + struct msm_clock *clock; + uint32_t now; + uint32_t alarm; + int late; + + clock = container_of(evt, struct msm_clock, clockevent); + now = msm_read_timer_count(clock); + alarm = now + (cycles << clock->shift); + if (clock->flags & MSM_CLOCK_FLAGS_ODD_MATCH_WRITE) + while (now == clock->last_set) + now = msm_read_timer_count(clock); writel(alarm, clock->regbase + TIMER_MATCH_VAL); + if (clock->flags & MSM_CLOCK_FLAGS_DELAYED_WRITE_POST) { + /* read the counter four extra times to make sure write posts + before reading the time */ + for (i = 0; i < 4; i++) + readl(clock->regbase + TIMER_COUNT_VAL); + } + now = msm_read_timer_count(clock); + clock->last_set = now; + clock->alarm_vtime = alarm + clock->offset; + late = now - alarm; + if (late >= (int)(-clock->write_delay << clock->shift) && late < DGT_HZ*5) { + static int print_limit = 10; + if (print_limit > 0) { + print_limit--; + printk(KERN_NOTICE "msm_timer_set_next_event(%lu) " + "clock %s, alarm already expired, now %x, " + "alarm %x, late %d%s\n", + cycles, clock->clockevent.name, now, alarm, late, + print_limit ? "" : " stop printing"); + } + return -ETIME; + } return 0; } static void msm_timer_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) { - struct msm_clock *clock = clockevent_to_clock(evt); - + struct msm_clock *clock; + clock = container_of(evt, struct msm_clock, clockevent); switch (mode) { case CLOCK_EVT_MODE_RESUME: case CLOCK_EVT_MODE_PERIODIC: break; case CLOCK_EVT_MODE_ONESHOT: + msm_active_clock = clock; writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE); break; case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: + msm_active_clock = NULL; + clock->smem_in_sync = 0; writel(0, clock->regbase + TIMER_ENABLE); break; } } +static uint32_t msm_timer_sync_smem_clock(int exit_sleep) +{ + struct msm_clock *clock = &msm_clocks[MSM_CLOCK_GPT]; + uint32_t *smem_clock; + uint32_t smem_clock_val; + s64 timeout; + s64 entry_time; + uint32_t last_state; + uint32_t state; + uint32_t new_offset; + + smem_clock = smem_alloc(SMEM_SMEM_SLOW_CLOCK_VALUE, + sizeof(uint32_t)); + + if (smem_clock == NULL) { + printk(KERN_ERR "no smem clock\n"); + return 0; + } + + if (!exit_sleep && clock->smem_in_sync) + return 0; + + last_state = state = smsm_get_state(); + if (*smem_clock) { + printk(KERN_INFO "get_smem_clock: invalid start state %x " + "clock %u\n", state, *smem_clock); + smsm_change_state(SMSM_TIMEWAIT, SMSM_TIMEINIT); + entry_time = ktime_to_ns(ktime_get()); + timeout = ktime_to_ns(ktime_get()) + NSEC_PER_MSEC * 10; + while (*smem_clock != 0 && ktime_to_ns(ktime_get()) < timeout) + ; + if (*smem_clock) { + printk(KERN_INFO "get_smem_clock: timeout still " + "invalid state %x clock %u in %lld ns\n", + state, *smem_clock, + ktime_to_ns(ktime_get()) - entry_time); + return 0; + } + } + entry_time = ktime_to_ns(ktime_get()); + timeout = ktime_to_ns(ktime_get()) + NSEC_PER_MSEC * 10; + smsm_change_state(SMSM_TIMEINIT, SMSM_TIMEWAIT); + do { + smem_clock_val = *smem_clock; + state = smsm_get_state(); + if (state != last_state) { + last_state = state; + printk(KERN_INFO "get_smem_clock: state %x clock %u\n", + state, smem_clock_val); + } + } while (smem_clock_val == 0 && ktime_to_ns(ktime_get()) < timeout); + if (smem_clock_val) { + new_offset = smem_clock_val - msm_read_timer_count(clock); + writel(TIMER_ENABLE_EN, MSM_GPT_BASE + TIMER_ENABLE); + if (clock->offset + clock->smem_offset != new_offset) { + if (exit_sleep) + clock->offset = new_offset - clock->smem_offset; + else + clock->smem_offset = new_offset - clock->offset; + clock->smem_in_sync = 1; + if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC) + printk(KERN_INFO "get_smem_clock: state %x " + "clock %u new offset %u+%u\n", + state, smem_clock_val, + clock->offset, clock->smem_offset); + } + } else { + printk(KERN_INFO "get_smem_clock: timeout state %x clock %u " + "in %lld ns\n", state, *smem_clock, + ktime_to_ns(ktime_get()) - entry_time); + } + smsm_change_state(SMSM_TIMEWAIT, SMSM_TIMEINIT); + entry_time = ktime_to_ns(ktime_get()); + timeout = ktime_to_ns(ktime_get()) + NSEC_PER_MSEC * 10; + while (*smem_clock != 0 && ktime_to_ns(ktime_get()) < timeout) + ; + if (*smem_clock) + printk(KERN_INFO "get_smem_clock: exit timeout state %x " + "clock %u in %lld ns\n", state, *smem_clock, + ktime_to_ns(ktime_get()) - entry_time); + return smem_clock_val; +} + +static void msm_timer_reactivate_alarm(struct msm_clock *clock) +{ + long alarm_delta = clock->alarm_vtime - clock->offset - + msm_read_timer_count(clock); + if (alarm_delta < (long)clock->write_delay + 4) + alarm_delta = clock->write_delay + 4; + while (msm_timer_set_next_event(alarm_delta, &clock->clockevent)) + ; +} + +int64_t msm_timer_enter_idle(void) +{ + struct msm_clock *clock = msm_active_clock; + uint32_t alarm; + uint32_t count; + int32_t delta; + + if (clock != &msm_clocks[MSM_CLOCK_GPT]) + return 0; + + msm_timer_sync_smem_clock(0); + + count = msm_read_timer_count(clock); + alarm = readl(clock->regbase + TIMER_MATCH_VAL); + delta = alarm - count; + if (delta <= -(int32_t)((clock->freq << clock->shift) >> 10)) { + /* timer should have triggered 1ms ago */ + printk(KERN_ERR "msm_timer_enter_idle: timer late %d, " + "reprogram it\n", delta); + msm_timer_reactivate_alarm(clock); + } + if (delta <= 0) + return 0; + return clocksource_cyc2ns((alarm - count) >> clock->shift, + clock->clocksource.mult, clock->clocksource.shift); +} + +void msm_timer_exit_idle(int low_power) +{ + struct msm_clock *clock = msm_active_clock; + uint32_t smem_clock; + + if (!low_power || clock != &msm_clocks[MSM_CLOCK_GPT]) + return; + + if (!(readl(clock->regbase + TIMER_ENABLE) & TIMER_ENABLE_EN)) + smem_clock = msm_timer_sync_smem_clock(1); + msm_timer_reactivate_alarm(clock); +} + +unsigned long long sched_clock(void) +{ + if (msm_timer_ready) + return ktime_to_ns(ktime_get()); + else + return 0; +} + +#ifdef CONFIG_MSM7X00A_USE_GP_TIMER + #define DG_TIMER_RATING 100 +#else + #define DG_TIMER_RATING 300 +#endif + static struct msm_clock msm_clocks[] = { [MSM_CLOCK_GPT] = { .clockevent = { @@ -166,42 +360,48 @@ static struct msm_clock msm_clocks[] = { .clocksource = { .name = "gp_timer", .rating = 200, - .read = msm_read_timer_count, + .read = msm_gpt_read, .mask = CLOCKSOURCE_MASK(32), + .shift = 17, .flags = CLOCK_SOURCE_IS_CONTINUOUS, }, .irq = { .name = "gp_timer", - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING, + .flags = IRQF_DISABLED | IRQF_TIMER | + IRQF_TRIGGER_RISING, .handler = msm_timer_interrupt, .dev_id = &msm_clocks[0].clockevent, .irq = INT_GP_TIMER_EXP }, .regbase = MSM_GPT_BASE, .freq = GPT_HZ, - .local_counter = MSM_GPT_BASE + TIMER_COUNT_VAL, - .global_counter = MSM_GPT_BASE + TIMER_COUNT_VAL + - MSM_TMR_GLOBAL, + .flags = + MSM_CLOCK_FLAGS_UNSTABLE_COUNT | + MSM_CLOCK_FLAGS_ODD_MATCH_WRITE | + MSM_CLOCK_FLAGS_DELAYED_WRITE_POST, + .write_delay = 9, }, [MSM_CLOCK_DGT] = { .clockevent = { .name = "dg_timer", .features = CLOCK_EVT_FEAT_ONESHOT, .shift = 32 + MSM_DGT_SHIFT, - .rating = 300, + .rating = DG_TIMER_RATING, .set_next_event = msm_timer_set_next_event, .set_mode = msm_timer_set_mode, }, .clocksource = { .name = "dg_timer", - .rating = 300, - .read = msm_read_timer_count, - .mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)), + .rating = DG_TIMER_RATING, + .read = msm_dgt_read, + .mask = CLOCKSOURCE_MASK((32-MSM_DGT_SHIFT)), + .shift = 24 - MSM_DGT_SHIFT, .flags = CLOCK_SOURCE_IS_CONTINUOUS, }, .irq = { .name = "dg_timer", - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING, + .flags = IRQF_DISABLED | IRQF_TIMER | + IRQF_TRIGGER_RISING, .handler = msm_timer_interrupt, .dev_id = &msm_clocks[1].clockevent, .irq = INT_DEBUG_TIMER_EXP @@ -209,9 +409,7 @@ static struct msm_clock msm_clocks[] = { .regbase = MSM_DGT_BASE, .freq = DGT_HZ >> MSM_DGT_SHIFT, .shift = MSM_DGT_SHIFT, - .local_counter = MSM_DGT_BASE + TIMER_COUNT_VAL, - .global_counter = MSM_DGT_BASE + TIMER_COUNT_VAL + - MSM_TMR_GLOBAL, + .write_delay = 2, } }; @@ -220,7 +418,7 @@ static void __init msm_timer_init(void) int i; int res; -#ifdef CONFIG_ARCH_MSM_SCORPIONMP +#ifdef CONFIG_ARCH_MSM8X60 writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL); #endif @@ -236,11 +434,13 @@ static void __init msm_timer_init(void) /* allow at least 10 seconds to notice that the timer wrapped */ ce->max_delta_ns = clockevent_delta2ns(0xf0000000 >> clock->shift, ce); - /* 4 gets rounded down to 3 */ - ce->min_delta_ns = clockevent_delta2ns(4, ce); + /* ticks gets rounded down by one */ + ce->min_delta_ns = + clockevent_delta2ns(clock->write_delay + 4, ce); ce->cpumask = cpumask_of(0); - res = clocksource_register_hz(cs, clock->freq); + cs->mult = clocksource_hz2mult(clock->freq, cs->shift); + res = clocksource_register(cs); if (res) printk(KERN_ERR "msm_timer_init: clocksource_register " "failed for %s\n", cs->name); @@ -251,51 +451,10 @@ static void __init msm_timer_init(void) "failed for %s\n", cs->name); clockevents_register_device(ce); + msm_timer_ready = 1; } } -#ifdef CONFIG_SMP -void __cpuinit local_timer_setup(struct clock_event_device *evt) -{ - struct msm_clock *clock = &msm_clocks[MSM_GLOBAL_TIMER]; - - /* Use existing clock_event for cpu 0 */ - if (!smp_processor_id()) - return; - - writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL); - - if (!local_clock_event) { - writel(0, clock->regbase + TIMER_ENABLE); - writel(0, clock->regbase + TIMER_CLEAR); - writel(~0, clock->regbase + TIMER_MATCH_VAL); - } - evt->irq = clock->irq.irq; - evt->name = "local_timer"; - evt->features = CLOCK_EVT_FEAT_ONESHOT; - evt->rating = clock->clockevent.rating; - evt->set_mode = msm_timer_set_mode; - evt->set_next_event = msm_timer_set_next_event; - evt->shift = clock->clockevent.shift; - evt->mult = div_sc(clock->freq, NSEC_PER_SEC, evt->shift); - evt->max_delta_ns = - clockevent_delta2ns(0xf0000000 >> clock->shift, evt); - evt->min_delta_ns = clockevent_delta2ns(4, evt); - - local_clock_event = evt; - - gic_enable_ppi(clock->irq.irq); - - clockevents_register_device(evt); -} - -inline int local_timer_ack(void) -{ - return 1; -} - -#endif - struct sys_timer msm_timer = { .init = msm_timer_init }; From 57c677d1c1e9d8a083224cf4145e747d71b1bd21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 11 Dec 2008 20:10:39 -0800 Subject: [PATCH 0171/2556] [ARM] msm: timer: Don't call ktime_get from msm_timer_sync_smem_clock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/timer.c | 47 +++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index c257f3eaa6522..a33045397d7eb 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -200,13 +200,19 @@ static void msm_timer_set_mode(enum clock_event_mode mode, } } +static inline int check_timeout(struct msm_clock *clock, uint32_t timeout) +{ + return (int32_t)(msm_read_timer_count(clock) - timeout) <= 0; +} + static uint32_t msm_timer_sync_smem_clock(int exit_sleep) { struct msm_clock *clock = &msm_clocks[MSM_CLOCK_GPT]; uint32_t *smem_clock; uint32_t smem_clock_val; - s64 timeout; - s64 entry_time; + uint32_t timeout; + uint32_t entry_time; + uint32_t timeout_delta; uint32_t last_state; uint32_t state; uint32_t new_offset; @@ -222,25 +228,27 @@ static uint32_t msm_timer_sync_smem_clock(int exit_sleep) if (!exit_sleep && clock->smem_in_sync) return 0; + timeout_delta = (clock->freq >> (7 - clock->shift)); /* 7.8ms */ + last_state = state = smsm_get_state(); if (*smem_clock) { printk(KERN_INFO "get_smem_clock: invalid start state %x " "clock %u\n", state, *smem_clock); smsm_change_state(SMSM_TIMEWAIT, SMSM_TIMEINIT); - entry_time = ktime_to_ns(ktime_get()); - timeout = ktime_to_ns(ktime_get()) + NSEC_PER_MSEC * 10; - while (*smem_clock != 0 && ktime_to_ns(ktime_get()) < timeout) + entry_time = msm_read_timer_count(clock); + timeout = entry_time + timeout_delta; + while (*smem_clock != 0 && check_timeout(clock, timeout)) ; if (*smem_clock) { printk(KERN_INFO "get_smem_clock: timeout still " - "invalid state %x clock %u in %lld ns\n", + "invalid state %x clock %u in %d ticks\n", state, *smem_clock, - ktime_to_ns(ktime_get()) - entry_time); + msm_read_timer_count(clock) - entry_time); return 0; } } - entry_time = ktime_to_ns(ktime_get()); - timeout = ktime_to_ns(ktime_get()) + NSEC_PER_MSEC * 10; + entry_time = msm_read_timer_count(clock); + timeout = entry_time + timeout_delta; smsm_change_state(SMSM_TIMEINIT, SMSM_TIMEWAIT); do { smem_clock_val = *smem_clock; @@ -250,10 +258,9 @@ static uint32_t msm_timer_sync_smem_clock(int exit_sleep) printk(KERN_INFO "get_smem_clock: state %x clock %u\n", state, smem_clock_val); } - } while (smem_clock_val == 0 && ktime_to_ns(ktime_get()) < timeout); + } while (smem_clock_val == 0 && check_timeout(clock, timeout)); if (smem_clock_val) { new_offset = smem_clock_val - msm_read_timer_count(clock); - writel(TIMER_ENABLE_EN, MSM_GPT_BASE + TIMER_ENABLE); if (clock->offset + clock->smem_offset != new_offset) { if (exit_sleep) clock->offset = new_offset - clock->smem_offset; @@ -268,18 +275,18 @@ static uint32_t msm_timer_sync_smem_clock(int exit_sleep) } } else { printk(KERN_INFO "get_smem_clock: timeout state %x clock %u " - "in %lld ns\n", state, *smem_clock, - ktime_to_ns(ktime_get()) - entry_time); + "in %d ticks\n", state, *smem_clock, + msm_read_timer_count(clock) - entry_time); } smsm_change_state(SMSM_TIMEWAIT, SMSM_TIMEINIT); - entry_time = ktime_to_ns(ktime_get()); - timeout = ktime_to_ns(ktime_get()) + NSEC_PER_MSEC * 10; - while (*smem_clock != 0 && ktime_to_ns(ktime_get()) < timeout) + entry_time = msm_read_timer_count(clock); + timeout = entry_time + timeout_delta; + while (*smem_clock != 0 && check_timeout(clock, timeout)) ; if (*smem_clock) printk(KERN_INFO "get_smem_clock: exit timeout state %x " - "clock %u in %lld ns\n", state, *smem_clock, - ktime_to_ns(ktime_get()) - entry_time); + "clock %u in %d ticks\n", state, *smem_clock, + msm_read_timer_count(clock) - entry_time); return smem_clock_val; } @@ -328,8 +335,10 @@ void msm_timer_exit_idle(int low_power) if (!low_power || clock != &msm_clocks[MSM_CLOCK_GPT]) return; - if (!(readl(clock->regbase + TIMER_ENABLE) & TIMER_ENABLE_EN)) + if (!(readl(clock->regbase + TIMER_ENABLE) & TIMER_ENABLE_EN)) { + writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE); smem_clock = msm_timer_sync_smem_clock(1); + } msm_timer_reactivate_alarm(clock); } From d729d2e793296a7964765972cdc0a184f85e0bfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 11 Dec 2008 19:34:10 -0800 Subject: [PATCH 0172/2556] [ARM] msm: Prevent clocksource read from jumping back after power collapse. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the clock events device is shut down or we are idle, don't read from the hardware, but return a cached value. It would be better to trigger this from the clocksource api, but there is no hook to enable or disable a clocksource. Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/timer.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index a33045397d7eb..8659ccab30b1c 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -88,6 +88,8 @@ struct msm_clock { uint32_t alarm_vtime; uint32_t smem_offset; uint32_t smem_in_sync; + cycle_t stopped_tick; + int stopped; }; enum { MSM_CLOCK_GPT, @@ -128,12 +130,17 @@ static uint32_t msm_read_timer_count(struct msm_clock *clock) static cycle_t msm_gpt_read(struct clocksource *cs) { struct msm_clock *clock = &msm_clocks[MSM_CLOCK_GPT]; - return msm_read_timer_count(clock) + clock->offset; + if (clock->stopped) + return clock->stopped_tick; + else + return msm_read_timer_count(clock) + clock->offset; } static cycle_t msm_dgt_read(struct clocksource *cs) { struct msm_clock *clock = &msm_clocks[MSM_CLOCK_DGT]; + if (clock->stopped) + return clock->stopped_tick; return (msm_read_timer_count(clock) + clock->offset) >> MSM_DGT_SHIFT; } @@ -182,12 +189,18 @@ static void msm_timer_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) { struct msm_clock *clock; + unsigned long irq_flags; + clock = container_of(evt, struct msm_clock, clockevent); + local_irq_save(irq_flags); + switch (mode) { case CLOCK_EVT_MODE_RESUME: case CLOCK_EVT_MODE_PERIODIC: break; case CLOCK_EVT_MODE_ONESHOT: + clock->stopped = 0; + clock->offset = -msm_read_timer_count(clock) + clock->stopped_tick; msm_active_clock = clock; writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE); break; @@ -195,9 +208,13 @@ static void msm_timer_set_mode(enum clock_event_mode mode, case CLOCK_EVT_MODE_SHUTDOWN: msm_active_clock = NULL; clock->smem_in_sync = 0; + clock->stopped = 1; + clock->stopped_tick = (msm_read_timer_count(clock) + + clock->offset) >> clock->shift; writel(0, clock->regbase + TIMER_ENABLE); break; } + local_irq_restore(irq_flags); } static inline int check_timeout(struct msm_clock *clock, uint32_t timeout) @@ -313,6 +330,8 @@ int64_t msm_timer_enter_idle(void) msm_timer_sync_smem_clock(0); count = msm_read_timer_count(clock); + if (clock->stopped++ == 0) + clock->stopped_tick = (count + clock->offset) >> clock->shift; alarm = readl(clock->regbase + TIMER_MATCH_VAL); delta = alarm - count; if (delta <= -(int32_t)((clock->freq << clock->shift) >> 10)) { @@ -332,14 +351,17 @@ void msm_timer_exit_idle(int low_power) struct msm_clock *clock = msm_active_clock; uint32_t smem_clock; - if (!low_power || clock != &msm_clocks[MSM_CLOCK_GPT]) + if (clock != &msm_clocks[MSM_CLOCK_GPT]) return; - if (!(readl(clock->regbase + TIMER_ENABLE) & TIMER_ENABLE_EN)) { - writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE); - smem_clock = msm_timer_sync_smem_clock(1); + if (low_power) { + if (!(readl(clock->regbase + TIMER_ENABLE) & TIMER_ENABLE_EN)) { + writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE); + smem_clock = msm_timer_sync_smem_clock(1); + } + msm_timer_reactivate_alarm(clock); } - msm_timer_reactivate_alarm(clock); + clock->stopped--; } unsigned long long sched_clock(void) From a3d905f89568b111c7ce41009872d04aa1cf0753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 10 Dec 2008 16:01:22 -0800 Subject: [PATCH 0173/2556] [ARM] msm: Don't call ktime_get from sched_clock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With some kernel debug options sched_clock gets called with xtime_lock locked for write resulting in a deadlock. Signed-off-by: Arve Hjønnevåg [ARM] msm: timer: Build on 2.6.32. Change-Id: I265f985487ed08329a5c675be57c74c472f8b158 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/timer.c | 43 ++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index 8659ccab30b1c..b9e1be1561766 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -66,8 +66,6 @@ enum { #define MSM_DGT_SHIFT (5) #endif -static int msm_timer_ready; - enum { MSM_CLOCK_FLAGS_UNSTABLE_COUNT = 1U << 0, MSM_CLOCK_FLAGS_ODD_MATCH_WRITE = 1U << 1, @@ -366,10 +364,42 @@ void msm_timer_exit_idle(int low_power) unsigned long long sched_clock(void) { - if (msm_timer_ready) - return ktime_to_ns(ktime_get()); - else - return 0; + static cycle_t saved_ticks; + static int saved_ticks_valid; + static unsigned long long base; + static unsigned long long last_result; + + unsigned long irq_flags; + static cycle_t last_ticks; + cycle_t ticks; + static unsigned long long result; + struct clocksource *cs; + struct msm_clock *clock = msm_active_clock; + + local_irq_save(irq_flags); + if (clock) { + cs = &clock->clocksource; + + last_ticks = saved_ticks; + saved_ticks = ticks = cs->read(cs); + if (!saved_ticks_valid) { + saved_ticks_valid = 1; + last_ticks = ticks; + base -= clocksource_cyc2ns(ticks, cs->mult, cs->shift); + } + if (ticks < last_ticks) { + base += clocksource_cyc2ns(cs->mask, + cs->mult, cs->shift); + base += clocksource_cyc2ns(1, cs->mult, cs->shift); + } + last_result = result = + clocksource_cyc2ns(ticks, cs->mult, cs->shift) + base; + } else { + base = result = last_result; + saved_ticks_valid = 0; + } + local_irq_restore(irq_flags); + return result; } #ifdef CONFIG_MSM7X00A_USE_GP_TIMER @@ -482,7 +512,6 @@ static void __init msm_timer_init(void) "failed for %s\n", cs->name); clockevents_register_device(ce); - msm_timer_ready = 1; } } From 9b1e4a0fd8c9c8cf35ab9336af92f2caf30b7680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 4 Dec 2009 22:53:27 -0800 Subject: [PATCH 0174/2556] [ARM] msm: timer: Fix sleep duration from idle. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't read TIMER_MATCH_VAL. This register may return an old value which would cause the calculated sleep duration to be wrong. Change-Id: Iaf079b28b47a677d485135912aa8b66b183cacf4 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index b9e1be1561766..32c20e50ef4d4 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -330,7 +330,7 @@ int64_t msm_timer_enter_idle(void) count = msm_read_timer_count(clock); if (clock->stopped++ == 0) clock->stopped_tick = (count + clock->offset) >> clock->shift; - alarm = readl(clock->regbase + TIMER_MATCH_VAL); + alarm = clock->alarm_vtime - clock->offset; delta = alarm - count; if (delta <= -(int32_t)((clock->freq << clock->shift) >> 10)) { /* timer should have triggered 1ms ago */ From 39fd51bdf2a02b1807f786b0f4bf6407b033bf92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 24 Jun 2009 17:30:34 -0700 Subject: [PATCH 0175/2556] [ARM] msm: timer: wait for timer reset to take effect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I45a4c51144bdad8d2a5cc51fbe4a24f175080d49 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/timer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index 32c20e50ef4d4..8101ca4a84434 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -490,6 +490,7 @@ static void __init msm_timer_init(void) writel(0, clock->regbase + TIMER_ENABLE); writel(0, clock->regbase + TIMER_CLEAR); writel(~0, clock->regbase + TIMER_MATCH_VAL); + while (msm_read_timer_count(clock)) ; /* wait for clock to clear */ ce->mult = div_sc(clock->freq, NSEC_PER_SEC, ce->shift); /* allow at least 10 seconds to notice that the timer wrapped */ From 66a08aaf8c27f29cd849de8e7061b1457f61f544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 22 Oct 2008 21:32:21 -0700 Subject: [PATCH 0176/2556] [ARM] msm: irq: Add sleep support. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add shadow registers for type and polarity and restore them in resume. Add wakeup support to main interrupt controller. Mask interrupt as soon as disable_irq is called. This is needed in some cases to allow power collapse from idle. Change-Id: I9ccc31cae34d055d721ad4af0bd37eae0c739dbe Signed-off-by: Brian Swetland Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/irq.c | 306 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 299 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c index 0b27d899f40e7..8cf563dbed11b 100644 --- a/arch/arm/mach-msm/irq.c +++ b/arch/arm/mach-msm/irq.c @@ -26,6 +26,18 @@ #include +#include "smd_private.h" + +enum { + IRQ_DEBUG_SLEEP_INT_TRIGGER = 1U << 0, + IRQ_DEBUG_SLEEP_INT = 1U << 1, + IRQ_DEBUG_SLEEP_ABORT = 1U << 2, + IRQ_DEBUG_SLEEP = 1U << 3, + IRQ_DEBUG_SLEEP_REQUEST = 1U << 4, +}; +static int msm_irq_debug_mask; +module_param_named(debug_mask, msm_irq_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + #define VIC_REG(off) (MSM_VIC_BASE + (off)) #define VIC_INT_SELECT0 VIC_REG(0x0000) /* 1: FIQ, 0: IRQ */ @@ -64,6 +76,66 @@ #define VIC_VECTPRIORITY(n) VIC_REG(0x0200+((n) * 4)) #define VIC_VECTADDR(n) VIC_REG(0x0400+((n) * 4)) +static uint32_t msm_irq_smsm_wake_enable[2]; +static struct { + uint32_t int_en[2]; + uint32_t int_type; + uint32_t int_polarity; +} msm_irq_shadow_reg[2]; +static uint32_t msm_irq_idle_disable[2]; + +#define SMSM_FAKE_IRQ (0xff) +static uint8_t msm_irq_to_smsm[NR_MSM_IRQS] = { + [INT_MDDI_EXT] = 1, + [INT_MDDI_PRI] = 2, + [INT_MDDI_CLIENT] = 3, + [INT_USB_OTG] = 4, + + [INT_PWB_I2C] = 5, + [INT_SDC1_0] = 6, + [INT_SDC1_1] = 7, + [INT_SDC2_0] = 8, + + [INT_SDC2_1] = 9, + [INT_ADSP_A9_A11] = 10, + [INT_UART1] = 11, + [INT_UART2] = 12, + + [INT_UART3] = 13, + [INT_UART1_RX] = 14, + [INT_UART2_RX] = 15, + [INT_UART3_RX] = 16, + + [INT_UART1DM_IRQ] = 17, + [INT_UART1DM_RX] = 18, + [INT_KEYSENSE] = 19, + [INT_AD_HSSD] = 20, + + [INT_NAND_WR_ER_DONE] = 21, + [INT_NAND_OP_DONE] = 22, + [INT_TCHSCRN1] = 23, + [INT_TCHSCRN2] = 24, + + [INT_TCHSCRN_SSBI] = 25, + [INT_USB_HS] = 26, + [INT_UART2DM_RX] = 27, + [INT_UART2DM_IRQ] = 28, + + [INT_SDC4_1] = 29, + [INT_SDC4_0] = 30, + [INT_SDC3_1] = 31, + [INT_SDC3_0] = 32, + + /* fake wakeup interrupts */ + [INT_GPIO_GROUP1] = SMSM_FAKE_IRQ, + [INT_GPIO_GROUP2] = SMSM_FAKE_IRQ, + [INT_A9_M2A_0] = SMSM_FAKE_IRQ, + [INT_A9_M2A_1] = SMSM_FAKE_IRQ, + [INT_A9_M2A_5] = SMSM_FAKE_IRQ, + [INT_GP_TIMER_EXP] = SMSM_FAKE_IRQ, + [INT_DEBUG_TIMER_EXP] = SMSM_FAKE_IRQ, +}; + static void msm_irq_ack(struct irq_data *d) { void __iomem *reg = VIC_INT_CLEAR0 + ((d->irq & 32) ? 4 : 0); @@ -73,44 +145,264 @@ static void msm_irq_ack(struct irq_data *d) static void msm_irq_mask(struct irq_data *d) { void __iomem *reg = VIC_INT_ENCLEAR0 + ((d->irq & 32) ? 4 : 0); - writel(1 << (d->irq & 31), reg); + unsigned index = (d->irq >> 5) & 1; + uint32_t mask = 1UL << (d->irq & 31); + int smsm_irq = msm_irq_to_smsm[d->irq]; + + msm_irq_shadow_reg[index].int_en[0] &= ~mask; + writel(mask, reg); + if (smsm_irq == 0) + msm_irq_idle_disable[index] &= ~mask; + else { + mask = 1UL << (smsm_irq - 1); + msm_irq_smsm_wake_enable[0] &= ~mask; + } } static void msm_irq_unmask(struct irq_data *d) { void __iomem *reg = VIC_INT_ENSET0 + ((d->irq & 32) ? 4 : 0); - writel(1 << (d->irq & 31), reg); + unsigned index = (d->irq >> 5) & 1; + uint32_t mask = 1UL << (d->irq & 31); + int smsm_irq = msm_irq_to_smsm[d->irq]; + + msm_irq_shadow_reg[index].int_en[0] |= mask; + writel(mask, reg); + + if (smsm_irq == 0) + msm_irq_idle_disable[index] |= mask; + else { + mask = 1UL << (smsm_irq - 1); + msm_irq_smsm_wake_enable[0] |= mask; + } } static int msm_irq_set_wake(struct irq_data *d, unsigned int on) { - return -EINVAL; + unsigned irq = d->irq; + unsigned index = (irq >> 5) & 1; + uint32_t mask = 1UL << (irq & 31); + int smsm_irq = msm_irq_to_smsm[irq]; + + if (smsm_irq == 0) { + printk(KERN_ERR "msm_irq_set_wake: bad wakeup irq %d\n", irq); + return -EINVAL; + } + if (on) + msm_irq_shadow_reg[index].int_en[1] |= mask; + else + msm_irq_shadow_reg[index].int_en[1] &= ~mask; + + if (smsm_irq == SMSM_FAKE_IRQ) + return 0; + + mask = 1UL << (smsm_irq - 1); + if (on) + msm_irq_smsm_wake_enable[1] |= mask; + else + msm_irq_smsm_wake_enable[1] &= ~mask; + return 0; } static int msm_irq_set_type(struct irq_data *d, unsigned int flow_type) { void __iomem *treg = VIC_INT_TYPE0 + ((d->irq & 32) ? 4 : 0); void __iomem *preg = VIC_INT_POLARITY0 + ((d->irq & 32) ? 4 : 0); + unsigned index = (d->irq >> 5) & 1; int b = 1 << (d->irq & 31); + uint32_t polarity; + uint32_t type; + polarity = msm_irq_shadow_reg[index].int_polarity; if (flow_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW)) - writel(readl(preg) | b, preg); + polarity |= b; if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH)) - writel(readl(preg) & (~b), preg); + polarity &= ~b; + writel(polarity, preg); + msm_irq_shadow_reg[index].int_polarity = polarity; + type = msm_irq_shadow_reg[index].int_type; if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { - writel(readl(treg) | b, treg); + type |= b; irq_desc[d->irq].handle_irq = handle_edge_irq; } if (flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW)) { - writel(readl(treg) & (~b), treg); + type &= ~b; irq_desc[d->irq].handle_irq = handle_level_irq; } + writel(type, treg); + msm_irq_shadow_reg[index].int_type = type; return 0; } +int msm_irq_pending(void) +{ + return readl(VIC_IRQ_STATUS0) || readl(VIC_IRQ_STATUS1); +} + +int msm_irq_idle_sleep_allowed(void) +{ + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_REQUEST) + printk(KERN_INFO "msm_irq_idle_sleep_allowed: disable %x %x\n", + msm_irq_idle_disable[0], msm_irq_idle_disable[1]); + return !(msm_irq_idle_disable[0] || msm_irq_idle_disable[1]); +} + +/* If arm9_wake is set: pass control to the other core. + * If from_idle is not set: disable non-wakeup interrupts. + */ +void msm_irq_enter_sleep1(bool arm9_wake, int from_idle) +{ + struct smsm_interrupt_info int_info; + if (arm9_wake) { + int_info.aArm_en_mask = msm_irq_smsm_wake_enable[!from_idle]; + int_info.aArm_interrupts_pending = 0; + smsm_set_interrupt_info(&int_info); + } +} + +int msm_irq_enter_sleep2(bool arm9_wake, int from_idle) +{ + int limit = 10; + uint32_t pending0, pending1; + + if (from_idle && !arm9_wake) + return 0; + + /* edge triggered interrupt may get lost if this mode is used */ + WARN_ON_ONCE(!arm9_wake && !from_idle); + + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + printk(KERN_INFO "msm_irq_enter_sleep change irq, pend %x %x\n", + readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1)); + pending0 = readl(VIC_IRQ_STATUS0); + pending1 = readl(VIC_IRQ_STATUS1); + pending0 &= msm_irq_shadow_reg[0].int_en[!from_idle]; + /* Clear INT_A9_M2A_5 since requesting sleep triggers it */ + pending0 &= ~(1U << INT_A9_M2A_5); + pending1 &= msm_irq_shadow_reg[1].int_en[!from_idle]; + if (pending0 || pending1) { + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_ABORT) + printk(KERN_INFO "msm_irq_enter_sleep2 abort %x %x\n", + pending0, pending1); + return -EAGAIN; + } + + writel(0, VIC_INT_EN0); + writel(0, VIC_INT_EN1); + + while (limit-- > 0) { + int pend_irq; + int irq = readl(VIC_IRQ_VEC_RD); + if (irq == -1) + break; + pend_irq = readl(VIC_IRQ_VEC_PEND_RD); + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT) + printk(KERN_INFO "msm_irq_enter_sleep cleared " + "int %d (%d)\n", irq, pend_irq); + } + + if (arm9_wake) { + msm_irq_set_type(irq_get_irq_data(INT_A9_M2A_6), + IRQF_TRIGGER_RISING); + writel(1U << INT_A9_M2A_6, VIC_INT_ENSET0); + } else { + writel(msm_irq_shadow_reg[0].int_en[1], VIC_INT_ENSET0); + writel(msm_irq_shadow_reg[1].int_en[1], VIC_INT_ENSET1); + } + return 0; +} + +void msm_irq_exit_sleep1(void) +{ + struct smsm_interrupt_info *int_info; + int i; + + msm_irq_ack(irq_get_irq_data(INT_A9_M2A_6)); + for (i = 0; i < 2; i++) { + writel(msm_irq_shadow_reg[i].int_type, VIC_INT_TYPE0 + i * 4); + writel(msm_irq_shadow_reg[i].int_polarity, VIC_INT_POLARITY0 + i * 4); + writel(msm_irq_shadow_reg[i].int_en[0], VIC_INT_EN0 + i * 4); + } + writel(1, VIC_INT_MASTEREN); + int_info = smem_alloc(SMEM_SMSM_INT_INFO, sizeof(*int_info)); + if (int_info == NULL) { + printk(KERN_ERR "msm_irq_exit_sleep \n"); + return; + } + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + printk(KERN_INFO "msm_irq_exit_sleep1 %x %x %x now %x %x\n", + int_info->aArm_en_mask, + int_info->aArm_interrupts_pending, + int_info->aArm_wakeup_reason, + readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1)); +} + +void msm_irq_exit_sleep2(void) +{ + int i; + uint32_t pending; + struct smsm_interrupt_info *int_info; + int_info = smem_alloc(SMEM_SMSM_INT_INFO, sizeof(*int_info)); + if (int_info == NULL) { + printk(KERN_ERR "msm_irq_exit_sleep \n"); + return; + } + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + printk(KERN_INFO "msm_irq_exit_sleep2 %x %x %x now %x %x\n", + int_info->aArm_en_mask, + int_info->aArm_interrupts_pending, + int_info->aArm_wakeup_reason, + readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1)); + pending = int_info->aArm_interrupts_pending; + for (i = 0; pending && i < ARRAY_SIZE(msm_irq_to_smsm); i++) { + unsigned reg_offset = (i & 32) ? 4 : 0; + uint32_t reg_mask = 1UL << (i & 31); + int smsm_irq = msm_irq_to_smsm[i]; + uint32_t smsm_mask; + if (smsm_irq == 0) + continue; + smsm_mask = 1U << (smsm_irq - 1); + if (!(pending & smsm_mask)) + continue; + pending &= ~smsm_mask; + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT) + printk(KERN_INFO "msm_irq_exit_sleep2: irq %d " + "still pending %x now %x %x\n", i, pending, + readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1)); +#if 0 /* debug intetrrupt trigger */ + if (readl(VIC_IRQ_STATUS0 + reg_offset) & reg_mask) + writel(reg_mask, VIC_INT_CLEAR0 + reg_offset); +#endif + if (readl(VIC_IRQ_STATUS0 + reg_offset) & reg_mask) + continue; + writel(reg_mask, VIC_SOFTINT0 + reg_offset); + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT_TRIGGER) + printk(KERN_INFO "msm_irq_exit_sleep2: irq %d need " + "trigger, now %x %x\n", i, + readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1)); + } +} + +void msm_irq_exit_sleep3(void) +{ + struct smsm_interrupt_info *int_info; + int_info = smem_alloc(SMEM_SMSM_INT_INFO, sizeof(*int_info)); + if (int_info == NULL) { + printk(KERN_ERR "msm_irq_exit_sleep \n"); + return; + } + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + printk(KERN_INFO "msm_irq_exit_sleep3 %x %x %x now %x %x " + "state %x\n", int_info->aArm_en_mask, + int_info->aArm_interrupts_pending, + int_info->aArm_wakeup_reason, readl(VIC_IRQ_STATUS0), + readl(VIC_IRQ_STATUS1), smsm_get_state()); +} + static struct irq_chip msm_irq_chip = { .name = "msm", + .irq_disable = msm_irq_mask, .irq_ack = msm_irq_ack, .irq_mask = msm_irq_mask, .irq_unmask = msm_irq_unmask, From 949c4877127b22b54089abce8f807e0fcfb2c6e7 Mon Sep 17 00:00:00 2001 From: "Mike A. Chan" Date: Wed, 23 Jul 2008 18:55:01 -0700 Subject: [PATCH 0177/2556] [ARM] msm: clock: Cpu clk stepping. Jump in 256mhz increments, acpu_freq_tbl has up/down fields to specify next target freq if greater than 256mhz. Less loop spinning. Signed-off-by: Mike A. Chan Signed-off-by: San Mehat --- arch/arm/mach-msm/board-halibut.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm/mach-msm/board-halibut.c b/arch/arm/mach-msm/board-halibut.c index 75dabb16c8023..37047eadece0f 100644 --- a/arch/arm/mach-msm/board-halibut.c +++ b/arch/arm/mach-msm/board-halibut.c @@ -73,8 +73,17 @@ static void __init halibut_init_irq(void) msm_init_irq(); } +static struct msm_acpu_clock_platform_data halibut_clock_data = { + .acpu_switch_time_us = 50, + .max_speed_delta_khz = 256000, + .vdd_switch_time_us = 62, + .power_collapse_khz = 19200000, + .wait_for_irq_khz = 128000000, +}; + static void __init halibut_init(void) { + msm_acpu_clock_init(&halibut_clock_data); platform_add_devices(devices, ARRAY_SIZE(devices)); } From 88cccbf00362df19cf15ce967e85e43edff03e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 22 Oct 2008 20:46:57 -0700 Subject: [PATCH 0178/2556] [ARM] msm: power management - add wakeup support and implement suspend to ram as wait for interrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enter low power states from idle. Add idle statistics. If enabled /proc/msm_pm_stats reports time spent in different idle states. Spin waiting for interrupts for a while before entering a lower power state. Signed-off-by: Brian Swetland Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/Kconfig | 107 +++++++ arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/idle.S | 71 ++++- arch/arm/mach-msm/pm.c | 552 +++++++++++++++++++++++++++++++++++++ arch/arm/mach-msm/pm.h | 31 +++ 5 files changed, 753 insertions(+), 9 deletions(-) create mode 100644 arch/arm/mach-msm/pm.c create mode 100644 arch/arm/mach-msm/pm.h diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 7c3d4919cdf69..f61fcd53597f0 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -199,6 +199,113 @@ choice High resolution timer. endchoice +choice + prompt "Suspend sleep mode" + default MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND + help + Allows overriding the sleep mode used. Leave at power + collapse suspend unless the arm9 image has problems. + + config MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND + bool "Power collapse suspend" + help + Lowest sleep state. Returns through reset vector. + + config MSM7X00A_SLEEP_MODE_POWER_COLLAPSE + bool "Power collapse" + help + Sleep state that returns through reset vector. + + config MSM7X00A_SLEEP_MODE_APPS_SLEEP + bool "Apps Sleep" + + config MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT + bool "Ramp down cpu clock and wait for interrupt" + + config MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT + bool "Wait for interrupt" +endchoice + +config MSM7X00A_SLEEP_MODE + int + default 0 if MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND + default 1 if MSM7X00A_SLEEP_MODE_POWER_COLLAPSE + default 2 if MSM7X00A_SLEEP_MODE_APPS_SLEEP + default 3 if MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT + default 4 if MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT + +choice + prompt "Idle sleep mode" + default MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE + help + Allows overriding the sleep mode used from idle. Leave at power + collapse suspend unless the arm9 image has problems. + + config MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND + bool "Power collapse suspend" + help + Lowest sleep state. Returns through reset vector. + + config MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE + bool "Power collapse" + help + Sleep state that returns through reset vector. + + config MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP + bool "Apps Sleep" + + config MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT + bool "Ramp down cpu clock and wait for interrupt" + + config MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT + bool "Wait for interrupt" +endchoice + +config MSM7X00A_IDLE_SLEEP_MODE + int + default 0 if MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND + default 1 if MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE + default 2 if MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP + default 3 if MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT + default 4 if MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT + +config MSM7X00A_IDLE_SLEEP_MIN_TIME + int "Minimum idle time before sleep" + default 20000000 + help + Minimum idle time in nanoseconds before entering low power mode. + +config MSM7X00A_IDLE_SPIN_TIME + int "Idle spin time before cpu ramp down" + default 80000 + help + Spin time in nanoseconds before ramping down cpu clock and entering + any low power state. + +menuconfig MSM_IDLE_STATS + bool "Collect idle statistics" + default y + help + Collect idle statistics and export them in proc/msm_pm_stats. + +if MSM_IDLE_STATS + +config MSM_IDLE_STATS_FIRST_BUCKET + int "First bucket time" + default 62500 + help + Upper time limit in nanosconds of first bucket. + +config MSM_IDLE_STATS_BUCKET_SHIFT + int "Bucket shift" + default 2 + +config MSM_IDLE_STATS_BUCKET_COUNT + int "Bucket count" + default 10 + +endif # MSM_IDLE_STATS + config MSM_SMD bool diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 4bda7f9af19d5..0b53e77349021 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_device.o obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_dog_keepalive.o obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_time_remote.o +obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o diff --git a/arch/arm/mach-msm/idle.S b/arch/arm/mach-msm/idle.S index 6a94f05271377..588eb072da822 100644 --- a/arch/arm/mach-msm/idle.S +++ b/arch/arm/mach-msm/idle.S @@ -1,9 +1,9 @@ -/* arch/arm/mach-msm/include/mach/idle.S +/* arch/arm/mach-msm/idle.S * - * Idle processing for MSM7K - work around bugs with SWFI. + * Idle processing for MSM7X00A - work around bugs with SWFI. * * Copyright (c) 2007 QUALCOMM Incorporated. - * Copyright (C) 2007 Google, Inc. + * Copyright (C) 2007 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 @@ -14,23 +14,76 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - */ - + */ + #include #include -ENTRY(arch_idle) -#ifdef CONFIG_MSM7X00A_IDLE - mrc p15, 0, r1, c1, c0, 0 /* read current CR */ +ENTRY(msm_pm_collapse) + ldr r0, =saved_state + stmia r0!, {r4-r14} + mrc p15, 0, r1, c1, c0, 0 /* MMU control */ + mrc p15, 0, r2, c2, c0, 0 /* ttb */ + mrc p15, 0, r3, c3, c0, 0 /* dacr */ + mrc p15, 0, ip, c13, c0, 1 /* context ID */ + stmia r0!, {r1-r3, ip} + /* fall though */ +ENTRY(msm_arch_idle) +#if defined(CONFIG_MSM_FIQ_SUPPORT) + cpsid f +#endif + mrc p15, 0, r1, c1, c0, 0 /* read current CR */ bic r0, r1, #(1 << 2) /* clear dcache bit */ bic r0, r0, #(1 << 12) /* clear icache bit */ mcr p15, 0, r0, c1, c0, 0 /* disable d/i cache */ - mov r0, #0 /* prepare wfi value */ + mov r0, #0 /* prepare wfi value */ /* also used as return value from msm_pm_collapse */ mcr p15, 0, r0, c7, c10, 0 /* flush the cache */ mcr p15, 0, r0, c7, c10, 4 /* memory barrier */ mcr p15, 0, r0, c7, c0, 4 /* wait for interrupt */ mcr p15, 0, r1, c1, c0, 0 /* restore d/i cache */ +#if defined(CONFIG_MSM_FIQ_SUPPORT) + cpsie f +#endif + mov pc, lr + +ENTRY(msm_pm_collapse_exit) +#if 0 /* serial debug */ + mov r0, #0x80000016 + mcr p15, 0, r0, c15, c2, 4 + mov r0, #0xA9000000 + add r0, r0, #0x00A00000 /* UART1 */ + /*add r0, r0, #0x00C00000*/ /* UART3 */ + mov r1, #'A' + str r1, [r0, #0x00C] #endif + ldr r1, =saved_state_end + ldr r2, =msm_pm_collapse_exit + adr r3, msm_pm_collapse_exit + add r1, r1, r3 + sub r1, r1, r2 + ldmdb r1!, {r2-r5} + mcr p15, 0, r4, c3, c0, 0 /* dacr */ + mcr p15, 0, r3, c2, c0, 0 /* ttb */ + mcr p15, 0, r5, c13, c0, 1 /* context ID */ + ldmdb r1!, {r4-r14} + mov r0, #1 + + mcr p15, 0, r2, c1, c0, 0 /* MMU control */ mov pc, lr + nop + nop + nop + nop + nop +1: b 1b + + + .data + +saved_state: + .space 4 * 11 /* r4-14 */ + .space 4 * 4 /* cp15 */ +saved_state_end: + diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c new file mode 100644 index 0000000000000..17de9f20e4086 --- /dev/null +++ b/arch/arm/mach-msm/pm.c @@ -0,0 +1,552 @@ +/* arch/arm/mach-msm/pm.c + * + * MSM Power Management Routines + * + * Copyright (C) 2007 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 +#include +#include +#include +#include +#include +#include +#include + +#include "smd_private.h" +#include "acpuclock.h" +#include "proc_comm.h" +#ifdef CONFIG_HAS_WAKELOCK +#include +#endif + +enum { + MSM_PM_DEBUG_SUSPEND = 1U << 0, + MSM_PM_DEBUG_POWER_COLLAPSE = 1U << 1, + MSM_PM_DEBUG_STATE = 1U << 2, + MSM_PM_DEBUG_CLOCK = 1U << 3, + MSM_PM_DEBUG_RESET_VECTOR = 1U << 4, + MSM_PM_DEBUG_SMSM_STATE = 1U << 5, + MSM_PM_DEBUG_IDLE = 1U << 6, +}; +static int msm_pm_debug_mask; +module_param_named(debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + +enum { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND, + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_PM_SLEEP_MODE_APPS_SLEEP, + MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT, + MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT, +}; +static int msm_pm_sleep_mode = CONFIG_MSM7X00A_SLEEP_MODE; +module_param_named(sleep_mode, msm_pm_sleep_mode, int, S_IRUGO | S_IWUSR | S_IWGRP); +static int msm_pm_idle_sleep_mode = CONFIG_MSM7X00A_IDLE_SLEEP_MODE; +module_param_named(idle_sleep_mode, msm_pm_idle_sleep_mode, int, S_IRUGO | S_IWUSR | S_IWGRP); +static int msm_pm_idle_sleep_min_time = CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME; +module_param_named(idle_sleep_min_time, msm_pm_idle_sleep_min_time, int, S_IRUGO | S_IWUSR | S_IWGRP); +static int msm_pm_idle_spin_time = CONFIG_MSM7X00A_IDLE_SPIN_TIME; +module_param_named(idle_spin_time, msm_pm_idle_spin_time, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define A11S_CLK_SLEEP_EN (MSM_CSR_BASE + 0x11c) +#define A11S_PWRDOWN (MSM_CSR_BASE + 0x440) +#define A11S_STANDBY_CTL (MSM_CSR_BASE + 0x108) +#define A11RAMBACKBIAS (MSM_CSR_BASE + 0x508) + +int msm_pm_collapse(void); +int msm_arch_idle(void); +void msm_pm_collapse_exit(void); + +int64_t msm_timer_enter_idle(void); +void msm_timer_exit_idle(int low_power); +int msm_irq_idle_sleep_allowed(void); +int msm_irq_pending(void); + +static uint32_t *msm_pm_reset_vector; + +static uint32_t msm_pm_max_sleep_time; + +#ifdef CONFIG_MSM_IDLE_STATS +enum msm_pm_time_stats_id { + MSM_PM_STAT_REQUESTED_IDLE, + MSM_PM_STAT_IDLE_SPIN, + MSM_PM_STAT_IDLE_WFI, + MSM_PM_STAT_IDLE_SLEEP, + MSM_PM_STAT_IDLE_FAILED_SLEEP, + MSM_PM_STAT_NOT_IDLE, + MSM_PM_STAT_COUNT +}; + +static struct msm_pm_time_stats { + const char *name; + int bucket[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT]; + int64_t min_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT]; + int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT]; + int count; + int64_t total_time; +} msm_pm_stats[MSM_PM_STAT_COUNT] = { + [MSM_PM_STAT_REQUESTED_IDLE].name = "idle-request", + [MSM_PM_STAT_IDLE_SPIN].name = "idle-spin", + [MSM_PM_STAT_IDLE_WFI].name = "idle-wfi", + [MSM_PM_STAT_IDLE_SLEEP].name = "idle-sleep", + [MSM_PM_STAT_IDLE_FAILED_SLEEP].name = "idle-failed-sleep", + [MSM_PM_STAT_NOT_IDLE].name = "not-idle", +}; + +static void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t) +{ + int i; + int64_t bt; + msm_pm_stats[id].total_time += t; + msm_pm_stats[id].count++; + bt = t; + do_div(bt, CONFIG_MSM_IDLE_STATS_FIRST_BUCKET); + if (bt < 1ULL << (CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT * + (CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1))) + i = DIV_ROUND_UP(fls((uint32_t)bt), + CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT); + else + i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; + msm_pm_stats[id].bucket[i]++; + if (t < msm_pm_stats[id].min_time[i] || !msm_pm_stats[id].max_time[i]) + msm_pm_stats[id].min_time[i] = t; + if (t > msm_pm_stats[id].max_time[i]) + msm_pm_stats[id].max_time[i] = t; +} +#endif + +static int +msm_pm_wait_state(uint32_t wait_state_all_set, uint32_t wait_state_all_clear, + uint32_t wait_state_any_set, uint32_t wait_state_any_clear) +{ + int i; + uint32_t state; + + for (i = 0; i < 100000; i++) { + state = smsm_get_state(); + if (((state & wait_state_all_set) == wait_state_all_set) && + ((~state & wait_state_all_clear) == wait_state_all_clear) && + (wait_state_any_set == 0 || (state & wait_state_any_set) || + wait_state_any_clear == 0 || (state & wait_state_any_clear))) + return 0; + } + printk(KERN_ERR "msm_pm_wait_state(%x, %x, %x, %x) failed %x\n", + wait_state_all_set, wait_state_all_clear, + wait_state_any_set, wait_state_any_clear, state); + return -ETIMEDOUT; +} + +static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) +{ + uint32_t saved_vector[2]; + int collapsed; + void msm_irq_enter_sleep1(bool arm9_wake, int from_idle); + int msm_irq_enter_sleep2(bool arm9_wake, int from_idle); + void msm_irq_exit_sleep1(void); + void msm_irq_exit_sleep2(void); + void msm_irq_exit_sleep3(void); + void msm_gpio_enter_sleep(int from_idle); + void msm_gpio_exit_sleep(void); + void smd_sleep_exit(void); + uint32_t enter_state; + uint32_t enter_wait_set = 0; + uint32_t enter_wait_clear = 0; + uint32_t exit_state; + uint32_t exit_wait_clear = 0; + uint32_t exit_wait_set = 0; + unsigned long pm_saved_acpu_clk_rate = 0; + int ret; + int rv = -EINTR; + + if (msm_pm_debug_mask & MSM_PM_DEBUG_SUSPEND) + printk(KERN_INFO "msm_sleep(): mode %d delay %u idle %d\n", + sleep_mode, sleep_delay, from_idle); + + switch (sleep_mode) { + case MSM_PM_SLEEP_MODE_POWER_COLLAPSE: + enter_state = SMSM_PWRC; + enter_wait_set = SMSM_RSA; + exit_state = SMSM_WFPI; + exit_wait_clear = SMSM_RSA; + break; + case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND: + enter_state = SMSM_PWRC_SUSPEND; + enter_wait_set = SMSM_RSA; + exit_state = SMSM_WFPI; + exit_wait_clear = SMSM_RSA; + break; + case MSM_PM_SLEEP_MODE_APPS_SLEEP: + enter_state = SMSM_SLEEP; + exit_state = SMSM_SLEEPEXIT; + exit_wait_set = SMSM_SLEEPEXIT; + break; + default: + enter_state = 0; + exit_state = 0; + } + + msm_irq_enter_sleep1(!!enter_state, from_idle); + msm_gpio_enter_sleep(from_idle); + + if (enter_state) { + if (sleep_delay == 0 && sleep_mode >= MSM_PM_SLEEP_MODE_APPS_SLEEP) + sleep_delay = 192000*5; /* APPS_SLEEP does not allow infinite timeout */ + smsm_set_sleep_duration(sleep_delay); + ret = smsm_change_state(SMSM_RUN, enter_state); + if (ret) { + printk(KERN_ERR "msm_sleep(): smsm_change_state %x failed\n", enter_state); + enter_state = 0; + exit_state = 0; + } + ret = msm_pm_wait_state(enter_wait_set, enter_wait_clear, 0, 0); + if (ret) { + printk(KERN_INFO "msm_sleep(): msm_pm_wait_state failed, %x\n", smsm_get_state()); + goto enter_failed; + } + } + if (msm_irq_enter_sleep2(!!enter_state, from_idle)) + goto enter_failed; + + if (enter_state) { + writel(0x1f, A11S_CLK_SLEEP_EN); + writel(1, A11S_PWRDOWN); + + writel(0, A11S_STANDBY_CTL); + writel(0, A11RAMBACKBIAS); + + if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) + printk(KERN_INFO "msm_sleep(): enter " + "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, " + "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN), + readl(A11S_PWRDOWN), smsm_get_state()); + } + + if (sleep_mode <= MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT) { + pm_saved_acpu_clk_rate = acpuclk_power_collapse(); + if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK) + printk(KERN_INFO "msm_sleep(): %ld enter power collapse" + "\n", pm_saved_acpu_clk_rate); + if (pm_saved_acpu_clk_rate == 0) + goto ramp_down_failed; + } + if (sleep_mode < MSM_PM_SLEEP_MODE_APPS_SLEEP) { + if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) + smsm_print_sleep_info(); + saved_vector[0] = msm_pm_reset_vector[0]; + saved_vector[1] = msm_pm_reset_vector[1]; + msm_pm_reset_vector[0] = 0xE51FF004; /* ldr pc, 4 */ + msm_pm_reset_vector[1] = virt_to_phys(msm_pm_collapse_exit); + if (msm_pm_debug_mask & MSM_PM_DEBUG_RESET_VECTOR) + printk(KERN_INFO "msm_sleep(): vector %x %x -> " + "%x %x\n", saved_vector[0], saved_vector[1], + msm_pm_reset_vector[0], msm_pm_reset_vector[1]); + collapsed = msm_pm_collapse(); + msm_pm_reset_vector[0] = saved_vector[0]; + msm_pm_reset_vector[1] = saved_vector[1]; + if (collapsed) { + cpu_init(); + local_fiq_enable(); + rv = 0; + } + if (msm_pm_debug_mask & MSM_PM_DEBUG_POWER_COLLAPSE) + printk(KERN_INFO "msm_pm_collapse(): returned %d\n", + collapsed); + if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) + smsm_print_sleep_info(); + } else { + msm_arch_idle(); + rv = 0; + } + + if (sleep_mode <= MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT) { + if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK) + printk(KERN_INFO "msm_sleep(): exit power collapse %ld" + "\n", pm_saved_acpu_clk_rate); + if (acpuclk_set_rate(pm_saved_acpu_clk_rate, 1) < 0) + printk(KERN_ERR "msm_sleep(): clk_set_rate %ld " + "failed\n", pm_saved_acpu_clk_rate); + } + if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) + printk(KERN_INFO "msm_sleep(): exit A11S_CLK_SLEEP_EN %x, " + "A11S_PWRDOWN %x, smsm_get_state %x\n", + readl(A11S_CLK_SLEEP_EN), readl(A11S_PWRDOWN), + smsm_get_state()); +ramp_down_failed: + msm_irq_exit_sleep1(); +enter_failed: + if (enter_state) { + writel(0x00, A11S_CLK_SLEEP_EN); + writel(0, A11S_PWRDOWN); + smsm_change_state(enter_state, exit_state); + msm_pm_wait_state(exit_wait_set, exit_wait_clear, 0, 0); + if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) + printk(KERN_INFO "msm_sleep(): sleep exit " + "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, " + "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN), + readl(A11S_PWRDOWN), smsm_get_state()); + if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) + smsm_print_sleep_info(); + } + msm_irq_exit_sleep2(); + if (enter_state) { + smsm_change_state(exit_state, SMSM_RUN); + msm_pm_wait_state(SMSM_RUN, 0, 0, 0); + if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) + printk(KERN_INFO "msm_sleep(): sleep exit " + "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, " + "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN), + readl(A11S_PWRDOWN), smsm_get_state()); + } + msm_irq_exit_sleep3(); + msm_gpio_exit_sleep(); + smd_sleep_exit(); + return rv; +} + +void arch_idle(void) +{ + int ret; + int spin; + int64_t sleep_time; + int low_power = 0; +#ifdef CONFIG_MSM_IDLE_STATS + int64_t t1; + static int64_t t2; + int exit_stat; +#endif + int allow_sleep = + msm_pm_idle_sleep_mode < MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT && +#ifdef CONFIG_HAS_WAKELOCK + !has_wake_lock(WAKE_LOCK_IDLE) && +#endif + msm_irq_idle_sleep_allowed(); + if (msm_pm_reset_vector == NULL) + return; + + sleep_time = msm_timer_enter_idle(); +#ifdef CONFIG_MSM_IDLE_STATS + t1 = ktime_to_ns(ktime_get()); + msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - t2); + msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, sleep_time); +#endif + if (msm_pm_debug_mask & MSM_PM_DEBUG_IDLE) + printk(KERN_INFO "arch_idle: sleep time %llu, allow_sleep %d\n", + sleep_time, allow_sleep); + spin = msm_pm_idle_spin_time >> 10; + while (spin-- > 0) { + if (msm_irq_pending()) { +#ifdef CONFIG_MSM_IDLE_STATS + exit_stat = MSM_PM_STAT_IDLE_SPIN; +#endif + goto abort_idle; + } + udelay(1); + } + if (sleep_time < msm_pm_idle_sleep_min_time || !allow_sleep) { + unsigned long saved_rate; + saved_rate = acpuclk_wait_for_irq(); + if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK) + printk(KERN_DEBUG "arch_idle: clk %ld -> swfi\n", + saved_rate); + if (saved_rate) + msm_arch_idle(); + else + while (!msm_irq_pending()) + udelay(1); + if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK) + printk(KERN_DEBUG "msm_sleep: clk swfi -> %ld\n", + saved_rate); + if (saved_rate && acpuclk_set_rate(saved_rate, 1) < 0) + printk(KERN_ERR "msm_sleep(): clk_set_rate %ld " + "failed\n", saved_rate); +#ifdef CONFIG_MSM_IDLE_STATS + exit_stat = MSM_PM_STAT_IDLE_WFI; +#endif + } else { + low_power = 1; + do_div(sleep_time, NSEC_PER_SEC / 32768); + if (sleep_time > 0x6DDD000) { + printk("sleep_time too big %lld\n", sleep_time); + sleep_time = 0x6DDD000; + } + ret = msm_sleep(msm_pm_idle_sleep_mode, sleep_time, 1); +#ifdef CONFIG_MSM_IDLE_STATS + if (ret) + exit_stat = MSM_PM_STAT_IDLE_FAILED_SLEEP; + else + exit_stat = MSM_PM_STAT_IDLE_SLEEP; +#endif + } +abort_idle: + msm_timer_exit_idle(low_power); +#ifdef CONFIG_MSM_IDLE_STATS + t2 = ktime_to_ns(ktime_get()); + msm_pm_add_stat(exit_stat, t2 - t1); +#endif +} + +static int msm_pm_enter(suspend_state_t state) +{ + msm_sleep(msm_pm_sleep_mode, msm_pm_max_sleep_time, 0); + return 0; +} + +static struct platform_suspend_ops msm_pm_ops = { + .enter = msm_pm_enter, + .valid = suspend_valid_only_mem, +}; + +static uint32_t restart_reason = 0x776655AA; + +static void msm_pm_power_off(void) +{ + msm_proc_comm(PCOM_POWER_DOWN, 0, 0); + for (;;) ; +} + +static void msm_pm_restart(char str) +{ + /* If there's a hard reset hook and the restart_reason + * is the default, prefer that to the (slower) proc_comm + * reset command. + */ + if ((restart_reason == 0x776655AA) && msm_hw_reset_hook) { + msm_hw_reset_hook(); + } else { + msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0); + } + for (;;) ; +} + +static int msm_reboot_call(struct notifier_block *this, unsigned long code, void *_cmd) +{ + if((code == SYS_RESTART) && _cmd) { + char *cmd = _cmd; + if (!strcmp(cmd, "bootloader")) { + restart_reason = 0x77665500; + } else if (!strcmp(cmd, "recovery")) { + restart_reason = 0x77665502; + } else if (!strcmp(cmd, "eraseflash")) { + restart_reason = 0x776655EF; + } else if (!strncmp(cmd, "oem-", 4)) { + unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff; + restart_reason = 0x6f656d00 | code; + } else { + restart_reason = 0x77665501; + } + } + return NOTIFY_DONE; +} + +static struct notifier_block msm_reboot_notifier = +{ + .notifier_call = msm_reboot_call, +}; + +#ifdef CONFIG_MSM_IDLE_STATS +static int msm_pm_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + int i, j; + char *p = page; + + for (i = 0; i < ARRAY_SIZE(msm_pm_stats); i++) { + int64_t bucket_time; + int64_t s; + uint32_t ns; + s = msm_pm_stats[i].total_time; + ns = do_div(s, NSEC_PER_SEC); + p += sprintf(p, + "%s:\n" + " count: %7d\n" + " total_time: %lld.%09u\n", + msm_pm_stats[i].name, + msm_pm_stats[i].count, + s, ns); + bucket_time = CONFIG_MSM_IDLE_STATS_FIRST_BUCKET; + for (j = 0; j < CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; j++) { + s = bucket_time; + ns = do_div(s, NSEC_PER_SEC); + p += sprintf(p, " <%2lld.%09u: %7d (%lld-%lld)\n", + s, ns, msm_pm_stats[i].bucket[j], + msm_pm_stats[i].min_time[j], + msm_pm_stats[i].max_time[j]); + bucket_time <<= CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT; + } + p += sprintf(p, " >=%2lld.%09u: %7d (%lld-%lld)\n", + s, ns, msm_pm_stats[i].bucket[j], + msm_pm_stats[i].min_time[j], + msm_pm_stats[i].max_time[j]); + } + *start = page + off; + + len = p - page; + if (len > off) + len -= off; + else + len = 0; + + return len < count ? len : count; +} +#endif + +void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns) +{ + int64_t max_sleep_time_bs = max_sleep_time_ns; + + /* Convert from ns -> BS units */ + do_div(max_sleep_time_bs, NSEC_PER_SEC / 32768); + + if (max_sleep_time_bs > 0x6DDD000) + msm_pm_max_sleep_time = (uint32_t) 0x6DDD000; + else + msm_pm_max_sleep_time = (uint32_t) max_sleep_time_bs; + + if (msm_pm_debug_mask & MSM_PM_DEBUG_SUSPEND) + printk("%s: Requested %lldns (%lldbs), Giving %ubs\n", + __func__, max_sleep_time_ns, + max_sleep_time_bs, + msm_pm_max_sleep_time); +} +EXPORT_SYMBOL(msm_pm_set_max_sleep_time); + +static int __init msm_pm_init(void) +{ + pm_power_off = msm_pm_power_off; + arm_pm_restart = msm_pm_restart; + msm_pm_max_sleep_time = 0; + + register_reboot_notifier(&msm_reboot_notifier); + + msm_pm_reset_vector = ioremap(0, PAGE_SIZE); + if (msm_pm_reset_vector == NULL) { + printk(KERN_ERR "msm_pm_init: failed to map reset vector\n"); + return -ENODEV; + } + + suspend_set_ops(&msm_pm_ops); + +#ifdef CONFIG_MSM_IDLE_STATS + create_proc_read_entry("msm_pm_stats", S_IRUGO, + NULL, msm_pm_read_proc, NULL); +#endif + return 0; +} + +__initcall(msm_pm_init); diff --git a/arch/arm/mach-msm/pm.h b/arch/arm/mach-msm/pm.h new file mode 100644 index 0000000000000..159a7a610d752 --- /dev/null +++ b/arch/arm/mach-msm/pm.h @@ -0,0 +1,31 @@ +/* arch/arm/mach-msm/pm.h + * + * Copyright (C) 2007 Google, Inc. + * Author: San Mehat + * + * 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 __ARCH_ARM_MACH_MSM_PM_H +#define __ARCH_ARM_MACH_MSM_PM_H + +#include + +#define A11S_CLK_SLEEP_EN_ADDR MSM_CSR_BASE + 0x11c + +#define CLK_SLEEP_EN_ARM11_CORE 0x01 +#define CLK_SLEEP_EN_ARM11_AHB 0x02 +#define CLK_SLEEP_EN_ID_BRIDGE 0x04 +#define CLK_SLEEP_EN_DMA_BRIDGE 0x08 +#define CLK_SLEEP_EN_PBUS 0x10 +#define CLK_SLEEP_EN_DEBUG_TIME 0x20 +#define CLK_SLEEP_EN_GP_TIMER 0x40 +#endif From 3dd61cbcc5beff169d7467d5a85916a590e40e2e Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Sat, 1 Dec 2007 19:54:04 -0800 Subject: [PATCH 0179/2556] [ARM] msm: pick up partition table from bootloader via private ATAG It's the wrong way to do this (according to rmk), but we've got bootloader code that's working this way, so here's support for it. Signed-off-by: Brian Swetland --- arch/arm/mach-msm/Makefile | 2 + arch/arm/mach-msm/nand_partitions.c | 87 +++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 arch/arm/mach-msm/nand_partitions.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 0b53e77349021..60f2df1ab24fc 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -4,6 +4,8 @@ obj-y += acpuclock-arm11.o obj-y += dma.o endif +obj-y += nand_partitions.o + ifdef CONFIG_MSM_VIC obj-y += irq-vic.o else diff --git a/arch/arm/mach-msm/nand_partitions.c b/arch/arm/mach-msm/nand_partitions.c new file mode 100644 index 0000000000000..482e6958504b4 --- /dev/null +++ b/arch/arm/mach-msm/nand_partitions.c @@ -0,0 +1,87 @@ +/* arch/arm/mach-msm/nand_partitions.c + * + * Code to extract partition information from ATAG set up by the + * bootloader. + * + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland + * + * 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 + +/* configuration tags specific to msm */ + +#define ATAG_MSM_PARTITION 0x4d534D70 /* MSMp */ + +struct msm_ptbl_entry +{ + char name[16]; + __u32 offset; + __u32 size; + __u32 flags; +}; + +#define MSM_MAX_PARTITIONS 8 + +static struct mtd_partition msm_nand_partitions[MSM_MAX_PARTITIONS]; +static char msm_nand_names[MSM_MAX_PARTITIONS * 16]; + +extern struct flash_platform_data msm_nand_data; + +static int __init parse_tag_msm_partition(const struct tag *tag) +{ + struct mtd_partition *ptn = msm_nand_partitions; + char *name = msm_nand_names; + struct msm_ptbl_entry *entry = (void *) &tag->u; + unsigned count, n; + + count = (tag->hdr.size - 2) / + (sizeof(struct msm_ptbl_entry) / sizeof(__u32)); + + if (count > MSM_MAX_PARTITIONS) + count = MSM_MAX_PARTITIONS; + + for (n = 0; n < count; n++) { + memcpy(name, entry->name, 15); + name[15] = 0; + + ptn->name = name; + ptn->offset = entry->offset * 64 * 2048; + ptn->size = entry->size * 64 * 2048; + + name += 16; + entry++; + ptn++; + } + + msm_nand_data.nr_parts = count; + msm_nand_data.parts = msm_nand_partitions; + + return 0; +} + +__tagtable(ATAG_MSM_PARTITION, parse_tag_msm_partition); From 6d5d567612ddf09e966680b78d61b7f1f6ac18f8 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Tue, 9 Feb 2010 21:41:24 -0800 Subject: [PATCH 0180/2556] [ARM] msm: driver for MSM7X00A on-chip I2C controller. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit San: Re-write driver to write/read to/from buffer in IRQ context Signed-off-by: Arve Hjønnevåg Signed-off-by: San Mehat [ARM] msm: I2C: Send stop command to finish i2c transfer on timeout Signed-off-by: San Mehat [ARM] msm: i2c: Try to read the requested number of bytes. If the requested read length is two or greater, this should always work. It the read length is one, an extra byte may be read if the interrupt handler is not called in time. The previous version always read an extra byte. Signed-off-by: Arve Hjønnevåg [ARM] msm: i2c: Try to recover if the i2c bus is busy after a failed transfer. If the bus is busy, switch the i2c pins to gpio mode and drive the bus manually. This will clear the busy condition with some clients. This adds a function, msm_set_i2c_mux, to change i2c mux settings since we do not have mux api. Signed-off-by: Arve Hjønnevåg [ARM] msm: i2c: Perform entire i2c transfer at interrupt context. Signed-off-by: Arve Hjønnevåg [ARM] msm: i2c: Prevent infinite reads. The contoller sometimes ignores our stop request. Send a new stop request before flushing every other byte. Signed-off-by: Arve Hjønnevåg [ARM] msm: i2c: Improve error recovery - Reduce bus busy timeout. - Call recovery function if the bus is busy at the start of a transaction. - Wait for bus busy to clear after a transaction. - Move read stop on timeout to recover function. Signed-off-by: Arve Hjønnevåg [ARM] msm: i2c: Always read at least two bytes If we tell the controller to stop at the same time as it finished reading the first byte, it may ignore the stop request, loose bus access or fail to start the next read or write. We now always read an extra byte if a single byte read was requested. Signed-off-by: Arve Hjønnevåg [ARM] msm: i2c: Only send STOP for last for the last write message in the group. This allows drivers to perform a write followed by a read with a repeated start. A repeated start after a read is still not possible. Signed-off-by: Arve Hjønnevåg [ARM] msm: i2c: Send another byte if we are still the bus master in msm_i2c_recover_bus_busy. If a transfer timed out before we told the controller to stop writing the controller may be waiting for more data to write. Write 0xff as the last byte, after switching the lines to gpio mode, to trigger a failure. Signed-off-by: Arve Hjønnevåg [ARM] msm: i2c: Work around i2c controller bug. When writing I2C_WRITE_DATA right before the i2c clock line is scheduled to go high again, the controller does not stretch the clock so the data setup time is within the i2c spec. Change-Id: Ibfa35a036454ac39a8bb94b0d1fc220976ace88f Signed-off-by: Arve Hjønnevåg msm: i2c: only complain if the bus is busy at the start of a transaction Signed-off-by: Brian Swetland [ARM] msm: i2c: do the pin mux config at initialization time. Signed-off-by: Dima Zavin [ARM] msm: i2c: dump the values of scl/sda at the start of recover_bus_busy Signed-off-by: Dima Zavin [ARM] msm: i2c: Only enable clock during transfer. The arm9 no longer ignores our clock requests when we enter sleep mode. Always leaving the i2c clock on will prevent entering low power mode. Signed-off-by: Arve Hjønnevåg [ARM] msm: i2c: Don't depend on i2c interrupt wakeup. The other core may use the i2c controller while we are power collpased which makes the i2c interrupt unusable as a wakeup source. Hold a wakelock while i2c is active and disable the interrupt when it is idle. Signed-off-by: Arve Hjønnevåg i2c: busses: msm: Only acquire wakelock if xfer active in suspend Now i2c transfers do not abort suspend, as it did previously. Change-Id: Ib908d64a7daa3b0cb76b08e3441f68c42758b5fa Signed-off-by: Mike Chan --- arch/arm/mach-msm/devices-msm7x00.c | 25 + arch/arm/mach-msm/devices-msm7x30.c | 24 + arch/arm/mach-msm/devices-qsd8x50.c | 24 + arch/arm/mach-msm/include/mach/system.h | 3 + drivers/i2c/busses/Kconfig | 8 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-msm.c | 603 ++++++++++++++++++++++++ 7 files changed, 688 insertions(+) create mode 100644 drivers/i2c/busses/i2c-msm.c diff --git a/arch/arm/mach-msm/devices-msm7x00.c b/arch/arm/mach-msm/devices-msm7x00.c index dfaa35c3b1085..19e4dce17a06b 100644 --- a/arch/arm/mach-msm/devices-msm7x00.c +++ b/arch/arm/mach-msm/devices-msm7x00.c @@ -22,6 +22,7 @@ #include #include #include "devices.h" +#include "proc_comm.h" #include #include @@ -197,6 +198,30 @@ struct platform_device msm_device_i2c = { .resource = resources_i2c, }; +#define GPIO_I2C_CLK 60 +#define GPIO_I2C_DAT 61 +void msm_set_i2c_mux(bool gpio, int *gpio_clk, int *gpio_dat) +{ + unsigned id; + if (gpio) { + id = PCOM_GPIO_CFG(GPIO_I2C_CLK, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + id = PCOM_GPIO_CFG(GPIO_I2C_DAT, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + *gpio_clk = GPIO_I2C_CLK; + *gpio_dat = GPIO_I2C_DAT; + } else { + id = PCOM_GPIO_CFG(GPIO_I2C_CLK, 1, GPIO_INPUT, + GPIO_NO_PULL, GPIO_8MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + id = PCOM_GPIO_CFG(GPIO_I2C_DAT , 1, GPIO_INPUT, + GPIO_NO_PULL, GPIO_8MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + static struct resource resources_hsusb[] = { { .start = MSM_HSUSB_PHYS, diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c index 4d6c118890aec..5fc05b23f796f 100644 --- a/arch/arm/mach-msm/devices-msm7x30.c +++ b/arch/arm/mach-msm/devices-msm7x30.c @@ -203,6 +203,30 @@ struct platform_device msm_device_i2c = { .resource = resources_i2c, }; +#define GPIO_I2C_CLK 70 +#define GPIO_I2C_DAT 71 +void msm_set_i2c_mux(bool gpio, int *gpio_clk, int *gpio_dat) +{ + unsigned id; + if (gpio) { + id = PCOM_GPIO_CFG(GPIO_I2C_CLK, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + id = PCOM_GPIO_CFG(GPIO_I2C_DAT, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + *gpio_clk = GPIO_I2C_CLK; + *gpio_dat = GPIO_I2C_DAT; + } else { + id = PCOM_GPIO_CFG(GPIO_I2C_CLK, 1, GPIO_INPUT, + GPIO_NO_PULL, GPIO_8MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + id = PCOM_GPIO_CFG(GPIO_I2C_DAT , 1, GPIO_INPUT, + GPIO_NO_PULL, GPIO_8MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + static struct resource resources_i2c2[] = { { .start = MSM_I2C_2_PHYS, diff --git a/arch/arm/mach-msm/devices-qsd8x50.c b/arch/arm/mach-msm/devices-qsd8x50.c index 062e2d753bb0c..313b366f63ea7 100644 --- a/arch/arm/mach-msm/devices-qsd8x50.c +++ b/arch/arm/mach-msm/devices-qsd8x50.c @@ -203,6 +203,30 @@ struct platform_device msm_device_i2c = { .resource = resources_i2c, }; +#define GPIO_I2C_CLK 95 +#define GPIO_I2C_DAT 96 +void msm_set_i2c_mux(bool gpio, int *gpio_clk, int *gpio_dat) +{ + unsigned id; + if (gpio) { + id = PCOM_GPIO_CFG(GPIO_I2C_CLK, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + id = PCOM_GPIO_CFG(GPIO_I2C_DAT, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + *gpio_clk = GPIO_I2C_CLK; + *gpio_dat = GPIO_I2C_DAT; + } else { + id = PCOM_GPIO_CFG(GPIO_I2C_CLK, 1, GPIO_INPUT, + GPIO_NO_PULL, GPIO_8MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + id = PCOM_GPIO_CFG(GPIO_I2C_DAT , 1, GPIO_INPUT, + GPIO_NO_PULL, GPIO_8MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + struct flash_platform_data msm_nand_data = { .parts = NULL, .nr_parts = 0, diff --git a/arch/arm/mach-msm/include/mach/system.h b/arch/arm/mach-msm/include/mach/system.h index d2e83f42ba165..cdb049d74aa72 100644 --- a/arch/arm/mach-msm/include/mach/system.h +++ b/arch/arm/mach-msm/include/mach/system.h @@ -26,3 +26,6 @@ static inline void arch_reset(char mode, const char *cmd) * PSHOLD line on the PMIC to hard reset the system */ extern void (*msm_hw_reset_hook)(void); + +void msm_set_i2c_mux(bool gpio, int *gpio_clk, int *gpio_dat); + diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 113505a6434ed..78a35c66695ce 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -442,6 +442,14 @@ config I2C_MPC This driver can also be built as a module. If so, the module will be called i2c-mpc. +config I2C_MSM + tristate "MSM" + depends on I2C && ARCH_MSM + default y + help + If you say yes to this option, support will be included for the + built-in I2C interface on the MSM7X00A family processors. + config I2C_MV64XXX tristate "Marvell mv64xxx I2C Controller" depends on (MV64X60 || PLAT_ORION) && EXPERIMENTAL diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 9d2d0ec7fb232..ede592bd89370 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_I2C_INTEL_MID) += i2c-intel-mid.o obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o +obj-$(CONFIG_I2C_MSM) += i2c-msm.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o obj-$(CONFIG_I2C_NUC900) += i2c-nuc900.o diff --git a/drivers/i2c/busses/i2c-msm.c b/drivers/i2c/busses/i2c-msm.c new file mode 100644 index 0000000000000..57a7999b4cb4f --- /dev/null +++ b/drivers/i2c/busses/i2c-msm.c @@ -0,0 +1,603 @@ +/* drivers/i2c/busses/i2c-msm.c + * + * Copyright (C) 2007 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 +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG 0 + +enum { + I2C_WRITE_DATA = 0x00, + I2C_CLK_CTL = 0x04, + I2C_STATUS = 0x08, + I2C_READ_DATA = 0x0c, + I2C_INTERFACE_SELECT = 0x10, + + I2C_WRITE_DATA_DATA_BYTE = 0xff, + I2C_WRITE_DATA_ADDR_BYTE = 1U << 8, + I2C_WRITE_DATA_LAST_BYTE = 1U << 9, + + I2C_CLK_CTL_FS_DIVIDER_VALUE = 0xff, + I2C_CLK_CTL_HS_DIVIDER_VALUE = 7U << 8, + + I2C_STATUS_WR_BUFFER_FULL = 1U << 0, + I2C_STATUS_RD_BUFFER_FULL = 1U << 1, + I2C_STATUS_BUS_ERROR = 1U << 2, + I2C_STATUS_PACKET_NACKED = 1U << 3, + I2C_STATUS_ARB_LOST = 1U << 4, + I2C_STATUS_INVALID_WRITE = 1U << 5, + I2C_STATUS_FAILED = 3U << 6, + I2C_STATUS_BUS_ACTIVE = 1U << 8, + I2C_STATUS_BUS_MASTER = 1U << 9, + I2C_STATUS_ERROR_MASK = 0xfc, + + I2C_INTERFACE_SELECT_INTF_SELECT = 1U << 0, + I2C_INTERFACE_SELECT_SCL = 1U << 8, + I2C_INTERFACE_SELECT_SDA = 1U << 9, +}; + +struct msm_i2c_dev { + struct device *dev; + void __iomem *base; /* virtual */ + int irq; + struct clk *clk; + struct i2c_adapter adapter; + + spinlock_t lock; + + struct i2c_msg *msg; + int rem; + int pos; + int cnt; + int ret; + bool need_flush; + int flush_cnt; + void *complete; + struct wake_lock wakelock; + bool is_suspended; +}; + +#if DEBUG +static void +dump_status(uint32_t status) +{ + printk("STATUS (0x%.8x): ", status); + if (status & I2C_STATUS_BUS_MASTER) + printk("MST "); + if (status & I2C_STATUS_BUS_ACTIVE) + printk("ACT "); + if (status & I2C_STATUS_INVALID_WRITE) + printk("INV_WR "); + if (status & I2C_STATUS_ARB_LOST) + printk("ARB_LST "); + if (status & I2C_STATUS_PACKET_NACKED) + printk("NAK "); + if (status & I2C_STATUS_BUS_ERROR) + printk("BUS_ERR "); + if (status & I2C_STATUS_RD_BUFFER_FULL) + printk("RD_FULL "); + if (status & I2C_STATUS_WR_BUFFER_FULL) + printk("WR_FULL "); + if (status & I2C_STATUS_FAILED) + printk("FAIL 0x%x", (status & I2C_STATUS_FAILED)); + printk("\n"); +} +#endif + +static void msm_i2c_write_delay(struct msm_i2c_dev *dev) +{ + /* If scl is still high we have >4us (for 100kbps) to write the data + * register before we risk hitting a bug where the controller releases + * scl to soon after driving sda low. Writing the data after the + * scheduled release time for scl also avoids the bug. + */ + if (readl(dev->base + I2C_INTERFACE_SELECT) & I2C_INTERFACE_SELECT_SCL) + return; + udelay(6); +} + +static bool msm_i2c_fill_write_buffer(struct msm_i2c_dev *dev) +{ + uint16_t val; + if (dev->pos < 0) { + val = I2C_WRITE_DATA_ADDR_BYTE | dev->msg->addr << 1; + if (dev->msg->flags & I2C_M_RD) + val |= 1; + if (dev->rem == 1 && dev->msg->len == 0) + val |= I2C_WRITE_DATA_LAST_BYTE; + msm_i2c_write_delay(dev); + writel(val, dev->base + I2C_WRITE_DATA); + dev->pos++; + return true; + } + + if (dev->msg->flags & I2C_M_RD) + return false; + + if (!dev->cnt) + return false; + + /* Ready to take a byte */ + val = dev->msg->buf[dev->pos]; + if (dev->cnt == 1 && dev->rem == 1) + val |= I2C_WRITE_DATA_LAST_BYTE; + + msm_i2c_write_delay(dev); + writel(val, dev->base + I2C_WRITE_DATA); + dev->pos++; + dev->cnt--; + return true; +} + +static void msm_i2c_read_buffer(struct msm_i2c_dev *dev) +{ + /* + * Theres something in the FIFO. + * Are we expecting data or flush crap? + */ + if ((dev->msg->flags & I2C_M_RD) && dev->pos >= 0 && dev->cnt) { + switch (dev->cnt) { + case 1: + if (dev->pos != 0) + break; + dev->need_flush = true; + /* fall-trough */ + case 2: + writel(I2C_WRITE_DATA_LAST_BYTE, + dev->base + I2C_WRITE_DATA); + } + dev->msg->buf[dev->pos] = readl(dev->base + I2C_READ_DATA); + dev->cnt--; + dev->pos++; + } else { /* FLUSH */ + if (dev->flush_cnt & 1) { + /* + * Stop requests are sometimes ignored, but writing + * more than one request generates a write error. + */ + writel(I2C_WRITE_DATA_LAST_BYTE, + dev->base + I2C_WRITE_DATA); + } + readl(dev->base + I2C_READ_DATA); + if (dev->need_flush) + dev->need_flush = false; + else + dev->flush_cnt++; + } +} + +static void msm_i2c_interrupt_locked(struct msm_i2c_dev *dev) +{ + uint32_t status = readl(dev->base + I2C_STATUS); + bool not_done = true; + +#if DEBUG + dump_status(status); +#endif + if (!dev->msg) { + dev_err(dev->dev, + "IRQ but nothing to do!, status %x\n", status); + return; + } + if (status & I2C_STATUS_ERROR_MASK) + goto out_err; + + if (!(status & I2C_STATUS_WR_BUFFER_FULL)) + not_done = msm_i2c_fill_write_buffer(dev); + if (status & I2C_STATUS_RD_BUFFER_FULL) + msm_i2c_read_buffer(dev); + + if (dev->pos >= 0 && dev->cnt == 0) { + if (dev->rem > 1) { + dev->rem--; + dev->msg++; + dev->pos = -1; + dev->cnt = dev->msg->len; + } else if (!not_done && !dev->need_flush) + goto out_complete; + } + return; + +out_err: + dev_err(dev->dev, "error, status %x\n", status); + dev->ret = -EIO; +out_complete: + complete(dev->complete); +} + +static irqreturn_t +msm_i2c_interrupt(int irq, void *devid) +{ + struct msm_i2c_dev *dev = devid; + + spin_lock(&dev->lock); + msm_i2c_interrupt_locked(dev); + spin_unlock(&dev->lock); + + return IRQ_HANDLED; +} + +static int +msm_i2c_poll_notbusy(struct msm_i2c_dev *dev, int warn) +{ + uint32_t retries = 0; + + while (retries != 200) { + uint32_t status = readl(dev->base + I2C_STATUS); + + if (!(status & I2C_STATUS_BUS_ACTIVE)) { + if (retries && warn) + dev_warn(dev->dev, + "Warning bus was busy (%d)\n", retries); + return 0; + } + if (retries++ > 100) + msleep(10); + } + dev_err(dev->dev, "Error waiting for notbusy\n"); + return -ETIMEDOUT; +} + +static int +msm_i2c_recover_bus_busy(struct msm_i2c_dev *dev) +{ + int i; + uint32_t status = readl(dev->base + I2C_STATUS); + int gpio_clk, gpio_dat; + bool gpio_clk_status = false; + + if (!(status & (I2C_STATUS_BUS_ACTIVE | I2C_STATUS_WR_BUFFER_FULL))) + return 0; + + msm_set_i2c_mux(true, &gpio_clk, &gpio_dat); + + if (status & I2C_STATUS_RD_BUFFER_FULL) { + dev_warn(dev->dev, "Read buffer full, status %x, intf %x\n", + status, readl(dev->base + I2C_INTERFACE_SELECT)); + writel(I2C_WRITE_DATA_LAST_BYTE, dev->base + I2C_WRITE_DATA); + readl(dev->base + I2C_READ_DATA); + } + else if (status & I2C_STATUS_BUS_MASTER) { + dev_warn(dev->dev, "Still the bus master, status %x, intf %x\n", + status, readl(dev->base + I2C_INTERFACE_SELECT)); + writel(I2C_WRITE_DATA_LAST_BYTE | 0xff, + dev->base + I2C_WRITE_DATA); + } + + dev_warn(dev->dev, "i2c_scl: %d, i2c_sda: %d\n", + gpio_get_value(gpio_clk), gpio_get_value(gpio_dat)); + + for (i = 0; i < 9; i++) { + if (gpio_get_value(gpio_dat) && gpio_clk_status) + break; + gpio_direction_output(gpio_clk, 0); + udelay(5); + gpio_direction_output(gpio_dat, 0); + udelay(5); + gpio_direction_input(gpio_clk); + udelay(5); + if (!gpio_get_value(gpio_clk)) + udelay(20); + if (!gpio_get_value(gpio_clk)) + msleep(10); + gpio_clk_status = gpio_get_value(gpio_clk); + gpio_direction_input(gpio_dat); + udelay(5); + } + msm_set_i2c_mux(false, NULL, NULL); + udelay(10); + + status = readl(dev->base + I2C_STATUS); + if (!(status & I2C_STATUS_BUS_ACTIVE)) { + dev_info(dev->dev, "Bus busy cleared after %d clock cycles, " + "status %x, intf %x\n", + i, status, readl(dev->base + I2C_INTERFACE_SELECT)); + return 0; + } + + dev_warn(dev->dev, "Bus still busy, status %x, intf %x\n", + status, readl(dev->base + I2C_INTERFACE_SELECT)); + return -EBUSY; +} + + +static int +msm_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + DECLARE_COMPLETION_ONSTACK(complete); + struct msm_i2c_dev *dev = i2c_get_adapdata(adap); + int ret; + long timeout; + unsigned long flags; + + /* + * If there is an i2c_xfer after driver has been suspended, + * grab wakelock to abort suspend. + */ + if (dev->is_suspended) + wake_lock(&dev->wakelock); + clk_enable(dev->clk); + enable_irq(dev->irq); + + ret = msm_i2c_poll_notbusy(dev, 1); + if (ret) { + ret = msm_i2c_recover_bus_busy(dev); + if (ret) + goto err; + } + + spin_lock_irqsave(&dev->lock, flags); + if (dev->flush_cnt) { + dev_warn(dev->dev, "%d unrequested bytes read\n", + dev->flush_cnt); + } + dev->msg = msgs; + dev->rem = num; + dev->pos = -1; + dev->ret = num; + dev->need_flush = false; + dev->flush_cnt = 0; + dev->cnt = msgs->len; + dev->complete = &complete; + + msm_i2c_interrupt_locked(dev); + spin_unlock_irqrestore(&dev->lock, flags); + + /* + * Now that we've setup the xfer, the ISR will transfer the data + * and wake us up with dev->err set if there was an error + */ + + timeout = wait_for_completion_timeout(&complete, HZ); + msm_i2c_poll_notbusy(dev, 0); /* Read may not have stopped in time */ + + spin_lock_irqsave(&dev->lock, flags); + if (dev->flush_cnt) { + dev_warn(dev->dev, "%d unrequested bytes read\n", + dev->flush_cnt); + } + ret = dev->ret; + dev->complete = NULL; + dev->msg = NULL; + dev->rem = 0; + dev->pos = 0; + dev->ret = 0; + dev->flush_cnt = 0; + dev->cnt = 0; + spin_unlock_irqrestore(&dev->lock, flags); + + if (!timeout) { + dev_err(dev->dev, "Transaction timed out\n"); + ret = -ETIMEDOUT; + } + + if (ret < 0) { + dev_err(dev->dev, "Error during data xfer (%d)\n", ret); + msm_i2c_recover_bus_busy(dev); + } +err: + disable_irq(dev->irq); + clk_disable(dev->clk); + if (dev->is_suspended) + wake_unlock(&dev->wakelock); + + return ret; +} + +static u32 +msm_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); +} + +static const struct i2c_algorithm msm_i2c_algo = { + .master_xfer = msm_i2c_xfer, + .functionality = msm_i2c_func, +}; + +static int +msm_i2c_probe(struct platform_device *pdev) +{ + struct msm_i2c_dev *dev; + struct resource *mem, *irq, *ioarea; + int ret; + int fs_div; + int hs_div; + int i2c_clk; + int clk_ctl; + int target_clk; + struct clk *clk; + + printk(KERN_INFO "msm_i2c_probe\n"); + + /* NOTE: driver uses the static register mapping */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "no irq resource?\n"); + return -ENODEV; + } + + ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1, + pdev->name); + if (!ioarea) { + dev_err(&pdev->dev, "I2C region already claimed\n"); + return -EBUSY; + } + clk = clk_get(&pdev->dev, "i2c_clk"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Could not get clock\n"); + ret = PTR_ERR(clk); + goto err_clk_get_failed; + } + + dev = kzalloc(sizeof(struct msm_i2c_dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err_alloc_dev_failed; + } + + dev->dev = &pdev->dev; + dev->irq = irq->start; + dev->clk = clk; + dev->base = ioremap(mem->start, (mem->end - mem->start) + 1); + if (!dev->base) { + ret = -ENOMEM; + goto err_ioremap_failed; + } + + spin_lock_init(&dev->lock); + wake_lock_init(&dev->wakelock, WAKE_LOCK_SUSPEND, "i2c"); + platform_set_drvdata(pdev, dev); + + msm_set_i2c_mux(false, NULL, NULL); + + clk_enable(clk); + + /* I2C_HS_CLK = I2C_CLK/(3*(HS_DIVIDER_VALUE+1) */ + /* I2C_FS_CLK = I2C_CLK/(2*(FS_DIVIDER_VALUE+3) */ + /* FS_DIVIDER_VALUE = ((I2C_CLK / I2C_FS_CLK) / 2) - 3 */ + i2c_clk = 19200000; /* input clock */ + target_clk = 100000; + /* target_clk = 200000; */ + fs_div = ((i2c_clk / target_clk) / 2) - 3; + hs_div = 3; + clk_ctl = ((hs_div & 0x7) << 8) | (fs_div & 0xff); + writel(clk_ctl, dev->base + I2C_CLK_CTL); + printk(KERN_INFO "msm_i2c_probe: clk_ctl %x, %d Hz\n", + clk_ctl, i2c_clk / (2 * ((clk_ctl & 0xff) + 3))); + clk_disable(clk); + + i2c_set_adapdata(&dev->adapter, dev); + dev->adapter.algo = &msm_i2c_algo; + strncpy(dev->adapter.name, + "MSM I2C adapter", + sizeof(dev->adapter.name)); + + dev->adapter.nr = pdev->id; + ret = i2c_add_numbered_adapter(&dev->adapter); + if (ret) { + dev_err(&pdev->dev, "i2c_add_adapter failed\n"); + goto err_i2c_add_adapter_failed; + } + + ret = request_irq(dev->irq, msm_i2c_interrupt, + IRQF_DISABLED | IRQF_TRIGGER_RISING, pdev->name, dev); + if (ret) { + dev_err(&pdev->dev, "request_irq failed\n"); + goto err_request_irq_failed; + } + disable_irq(dev->irq); + return 0; + +/* free_irq(dev->irq, dev); */ +err_request_irq_failed: + i2c_del_adapter(&dev->adapter); +err_i2c_add_adapter_failed: + iounmap(dev->base); +err_ioremap_failed: + kfree(dev); +err_alloc_dev_failed: + clk_put(clk); +err_clk_get_failed: + release_mem_region(mem->start, (mem->end - mem->start) + 1); + return ret; +} + +static int +msm_i2c_remove(struct platform_device *pdev) +{ + struct msm_i2c_dev *dev = platform_get_drvdata(pdev); + struct resource *mem; + + platform_set_drvdata(pdev, NULL); + enable_irq(dev->irq); + free_irq(dev->irq, dev); + i2c_del_adapter(&dev->adapter); + wake_lock_destroy(&dev->wakelock); + clk_put(dev->clk); + iounmap(dev->base); + kfree(dev); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, (mem->end - mem->start) + 1); + return 0; +} + +static int msm_i2c_suspend_noirq(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct msm_i2c_dev *dev = platform_get_drvdata(pdev); + + /* Block to allow any i2c_xfers to finish */ + i2c_lock_adapter(&dev->adapter); + dev->is_suspended = true; + i2c_unlock_adapter(&dev->adapter); + return 0; +} + +static int msm_i2c_resume_noirq(struct device *device) { + struct platform_device *pdev = to_platform_device(device); + struct msm_i2c_dev *dev = platform_get_drvdata(pdev); + + /* Block to allow any i2c_xfers to finish */ + i2c_lock_adapter(&dev->adapter); + dev->is_suspended = false; + i2c_unlock_adapter(&dev->adapter); + return 0; +} + +static struct dev_pm_ops msm_i2c_pm_ops = { + .suspend_noirq = msm_i2c_suspend_noirq, + .resume_noirq = msm_i2c_resume_noirq, +}; + +static struct platform_driver msm_i2c_driver = { + .probe = msm_i2c_probe, + .remove = msm_i2c_remove, + .driver = { + .name = "msm_i2c", + .owner = THIS_MODULE, + .pm = &msm_i2c_pm_ops, + }, +}; + +/* I2C may be needed to bring up other drivers */ +static int __init +msm_i2c_init_driver(void) +{ + return platform_driver_register(&msm_i2c_driver); +} +subsys_initcall(msm_i2c_init_driver); + +static void __exit msm_i2c_exit_driver(void) +{ + platform_driver_unregister(&msm_i2c_driver); +} +module_exit(msm_i2c_exit_driver); + From 8b01dfb752e2b2f77ee3c2518e1fd161fdc81d62 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Tue, 9 Feb 2010 22:46:19 -0800 Subject: [PATCH 0181/2556] [ARM] msm: driver for rmnet virtual ethernet interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MSM7X00A baseband makes up to 3 "virtual ethernet" channels available, which allow ethernet packets to be exchanged with the cellular network, once an appropriate data connection is established. Signed-off-by: Brian Swetland Signed-off-by: San Mehat [ARM] msm_rmnet: HACK: do not count ARP packets The android network traffic watchdog is tricked into thinking that data traffic is working at times when it isn't, due to ARP traffic between the apps and modem processor. Don't count ARP packets in link statistics to avoid this problem. [ARM] msm: rmnet: Add stat tracking for number of radio wakeups occur. There are two paramaters that appear for all rmnet devices. rmnet0 for example: /sys/devices/virtual/net/rmnet0/timeout (RW) /sys/devices/virtual/net/rmnet0/wakeups (RO) timeout is configured by userspace for the proper network timeout values wakeups is the number of radio wakeups that occured. By default timeout is zero which means the stats are disabled. MSM_RMNET_DEBUG must be set. Signed-off-by: Mike Chan [ARM] msm: rmnet: Track wakeups due to xmit/rcv instead of globally. Signed-off-by: Mike Chan [ARM] msm: rmnet: Different stat timeouts when screen is on/off. Timeout for modem powerdown can differ when the screen is on/off. Allow timeout to change via early suspend/resume hooks. Signed-off-by: Mike Chan [ARM] msm: rmnet: Lock a wakelock for half a second when receiving data. Signed-off-by: Arve Hjønnevåg [ARM] msm: rmnet: Tracks total awake time when the rmnet is active. Exports data in /sys/devices/virtual/net/rmnet0/awake_time_ms in time expressed as ms awake. Signed-off-by: Mike Chan [ARM] msm: rmnet: Fix compilation issue when MSM_RMNET_DEBUG is not set. Signed-off-by: Dima Zavin msm_rmnet: fix to build on 2.6.32 Change-Id: Ic6a4903dd12ea83723354d00f639ae2f9375167f msm_rmnet: ensure packet writes are atomic Use the smd_write_atomic() function to prevent concurrent packet writes to the transport from stepping on each other. Signed-off-by: Brian Swetland --- drivers/net/Kconfig | 15 ++ drivers/net/Makefile | 2 + drivers/net/msm_rmnet.c | 466 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 483 insertions(+) create mode 100644 drivers/net/msm_rmnet.c diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 773ce72ac77a5..6c5a9ee224311 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -3404,6 +3404,21 @@ config NETCONSOLE If you want to log kernel messages over the network, enable this. See for details. +config MSM_RMNET + tristate "MSM RMNET Virtual Network Device" + depends on ARCH_MSM + default y + help + Virtual ethernet interface for MSM RMNET transport. + +config MSM_RMNET_DEBUG + bool "MSM RMNET debug interface" + depends on MSM_RMNET + default n + help + Debug stats on wakeup counts. + + config NETCONSOLE_DYNAMIC bool "Dynamic reconfiguration of logging targets" depends on NETCONSOLE && SYSFS && CONFIGFS_FS diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 430d42fd53bbd..97f6a28eb8dd3 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -294,6 +294,8 @@ obj-$(CONFIG_NETCONSOLE) += netconsole.o obj-$(CONFIG_FS_ENET) += fs_enet/ obj-$(CONFIG_NETXEN_NIC) += netxen/ + +obj-$(CONFIG_MSM_RMNET) += msm_rmnet.o obj-$(CONFIG_NIU) += niu.o obj-$(CONFIG_VIRTIO_NET) += virtio_net.o obj-$(CONFIG_SFC) += sfc/ diff --git a/drivers/net/msm_rmnet.c b/drivers/net/msm_rmnet.c new file mode 100644 index 0000000000000..8a8173aca709a --- /dev/null +++ b/drivers/net/msm_rmnet.c @@ -0,0 +1,466 @@ +/* linux/drivers/net/msm_rmnet.c + * + * Virtual Ethernet Interface for MSM7K Networking + * + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland + * + * 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 + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +#include + +/* XXX should come from smd headers */ +#define SMD_PORT_ETHER0 11 +#define POLL_DELAY 1000000 /* 1 second delay interval */ + +struct rmnet_private +{ + smd_channel_t *ch; + struct net_device_stats stats; + const char *chname; + struct wake_lock wake_lock; +#ifdef CONFIG_MSM_RMNET_DEBUG + ktime_t last_packet; + short active_countdown; /* Number of times left to check */ + short restart_count; /* Number of polls seems so far */ + unsigned long wakeups_xmit; + unsigned long wakeups_rcv; + unsigned long timeout_us; + unsigned long awake_time_ms; + struct delayed_work work; +#endif +}; + +static int count_this_packet(void *_hdr, int len) +{ + struct ethhdr *hdr = _hdr; + + if (len >= ETH_HLEN && hdr->h_proto == htons(ETH_P_ARP)) + return 0; + + return 1; +} + +#ifdef CONFIG_MSM_RMNET_DEBUG +static int in_suspend; +static unsigned long timeout_us; +static struct workqueue_struct *rmnet_wq; + +static void do_check_active(struct work_struct *work) +{ + struct rmnet_private *p = + container_of(work, struct rmnet_private, work.work); + + /* + * Soft timers do not wake the cpu from suspend. + * If we are in suspend, do_check_active is only called once at the + * timeout time instead of polling at POLL_DELAY interval. Otherwise the + * cpu will sleeps and the timer can fire much much later than POLL_DELAY + * casuing a skew in time calculations. + */ + if (in_suspend) { + /* + * Assume for N packets sent durring this session, they are + * uniformly distributed durring the timeout window. + */ + int tmp = p->timeout_us * 2 - + (p->timeout_us / (p->active_countdown + 1)); + tmp /= 1000; + p->awake_time_ms += tmp; + + p->active_countdown = p->restart_count = 0; + return; + } + + /* + * Poll if not in suspend, since this gives more accurate tracking of + * rmnet sessions. + */ + p->restart_count++; + if (--p->active_countdown == 0) { + p->awake_time_ms += p->restart_count * POLL_DELAY / 1000; + p->restart_count = 0; + } else { + queue_delayed_work(rmnet_wq, &p->work, + usecs_to_jiffies(POLL_DELAY)); + } +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +/* + * If early suspend is enabled then we specify two timeout values, + * screen on (default), and screen is off. + */ +static unsigned long timeout_suspend_us; +static struct device *rmnet0; + +/* Set timeout in us when the screen is off. */ +static ssize_t timeout_suspend_store(struct device *d, struct device_attribute *attr, const char *buf, size_t n) +{ + timeout_suspend_us = simple_strtoul(buf, NULL, 10); + return n; +} + +static ssize_t timeout_suspend_show(struct device *d, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%lu\n", (unsigned long) timeout_suspend_us); +} + +static DEVICE_ATTR(timeout_suspend, 0664, timeout_suspend_show, timeout_suspend_store); + +static void rmnet_early_suspend(struct early_suspend *handler) { + if (rmnet0) { + struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0)); + p->timeout_us = timeout_suspend_us; + } + in_suspend = 1; +} + +static void rmnet_late_resume(struct early_suspend *handler) { + if (rmnet0) { + struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0)); + p->timeout_us = timeout_us; + } + in_suspend = 0; +} + +static struct early_suspend rmnet_power_suspend = { + .suspend = rmnet_early_suspend, + .resume = rmnet_late_resume, +}; + +static int __init rmnet_late_init(void) +{ + register_early_suspend(&rmnet_power_suspend); + return 0; +} + +late_initcall(rmnet_late_init); +#endif + +/* Returns 1 if packet caused rmnet to wakeup, 0 otherwise. */ +static int rmnet_cause_wakeup(struct rmnet_private *p) { + int ret = 0; + ktime_t now; + if (p->timeout_us == 0) /* Check if disabled */ + return 0; + + /* Start timer on a wakeup packet */ + if (p->active_countdown == 0) { + ret = 1; + now = ktime_get_real(); + p->last_packet = now; + if (in_suspend) + queue_delayed_work(rmnet_wq, &p->work, + usecs_to_jiffies(p->timeout_us)); + else + queue_delayed_work(rmnet_wq, &p->work, + usecs_to_jiffies(POLL_DELAY)); + } + + if (in_suspend) + p->active_countdown++; + else + p->active_countdown = p->timeout_us / POLL_DELAY; + + return ret; +} + +static ssize_t wakeups_xmit_show(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct rmnet_private *p = netdev_priv(to_net_dev(d)); + return sprintf(buf, "%lu\n", p->wakeups_xmit); +} + +DEVICE_ATTR(wakeups_xmit, 0444, wakeups_xmit_show, NULL); + +static ssize_t wakeups_rcv_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct rmnet_private *p = netdev_priv(to_net_dev(d)); + return sprintf(buf, "%lu\n", p->wakeups_rcv); +} + +DEVICE_ATTR(wakeups_rcv, 0444, wakeups_rcv_show, NULL); + +/* Set timeout in us. */ +static ssize_t timeout_store(struct device *d, struct device_attribute *attr, + const char *buf, size_t n) +{ +#ifndef CONFIG_HAS_EARLYSUSPEND + struct rmnet_private *p = netdev_priv(to_net_dev(d)); + p->timeout_us = timeout_us = simple_strtoul(buf, NULL, 10); +#else +/* If using early suspend/resume hooks do not write the value on store. */ + timeout_us = simple_strtoul(buf, NULL, 10); +#endif + return n; +} + +static ssize_t timeout_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct rmnet_private *p = netdev_priv(to_net_dev(d)); + p = netdev_priv(to_net_dev(d)); + return sprintf(buf, "%lu\n", timeout_us); +} + +DEVICE_ATTR(timeout, 0664, timeout_show, timeout_store); + +/* Show total radio awake time in ms */ +static ssize_t awake_time_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct rmnet_private *p = netdev_priv(to_net_dev(d)); + return sprintf(buf, "%lu\n", p->awake_time_ms); +} +DEVICE_ATTR(awake_time_ms, 0444, awake_time_show, NULL); + +#endif + +/* Called in soft-irq context */ +static void smd_net_data_handler(unsigned long arg) +{ + struct net_device *dev = (struct net_device *) arg; + struct rmnet_private *p = netdev_priv(dev); + struct sk_buff *skb; + void *ptr = 0; + int sz; + + for (;;) { + sz = smd_cur_packet_size(p->ch); + if (sz == 0) break; + if (smd_read_avail(p->ch) < sz) break; + + if (sz > 1514) { + pr_err("rmnet_recv() discarding %d len\n", sz); + ptr = 0; + } else { + skb = dev_alloc_skb(sz + NET_IP_ALIGN); + if (skb == NULL) { + pr_err("rmnet_recv() cannot allocate skb\n"); + } else { + skb->dev = dev; + skb_reserve(skb, NET_IP_ALIGN); + ptr = skb_put(skb, sz); + wake_lock_timeout(&p->wake_lock, HZ / 2); + if (smd_read(p->ch, ptr, sz) != sz) { + pr_err("rmnet_recv() smd lied about avail?!"); + ptr = 0; + dev_kfree_skb_irq(skb); + } else { + skb->protocol = eth_type_trans(skb, dev); + if (count_this_packet(ptr, skb->len)) { +#ifdef CONFIG_MSM_RMNET_DEBUG + p->wakeups_rcv += + rmnet_cause_wakeup(p); +#endif + p->stats.rx_packets++; + p->stats.rx_bytes += skb->len; + } + netif_rx(skb); + } + continue; + } + } + if (smd_read(p->ch, ptr, sz) != sz) + pr_err("rmnet_recv() smd lied about avail?!"); + } +} + +static DECLARE_TASKLET(smd_net_data_tasklet, smd_net_data_handler, 0); + +static void smd_net_notify(void *_dev, unsigned event) +{ + if (event != SMD_EVENT_DATA) + return; + + smd_net_data_tasklet.data = (unsigned long) _dev; + + tasklet_schedule(&smd_net_data_tasklet); +} + +static int rmnet_open(struct net_device *dev) +{ + int r; + struct rmnet_private *p = netdev_priv(dev); + + pr_info("rmnet_open()\n"); + if (!p->ch) { + r = smd_open(p->chname, &p->ch, dev, smd_net_notify); + + if (r < 0) + return -ENODEV; + } + + netif_start_queue(dev); + return 0; +} + +static int rmnet_stop(struct net_device *dev) +{ + pr_info("rmnet_stop()\n"); + netif_stop_queue(dev); + return 0; +} + +static int rmnet_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct rmnet_private *p = netdev_priv(dev); + smd_channel_t *ch = p->ch; + + if (smd_write_atomic(ch, skb->data, skb->len) != skb->len) { + pr_err("rmnet fifo full, dropping packet\n"); + } else { + if (count_this_packet(skb->data, skb->len)) { + p->stats.tx_packets++; + p->stats.tx_bytes += skb->len; +#ifdef CONFIG_MSM_RMNET_DEBUG + p->wakeups_xmit += rmnet_cause_wakeup(p); +#endif + } + } + + dev_kfree_skb_irq(skb); + return 0; +} + +static struct net_device_stats *rmnet_get_stats(struct net_device *dev) +{ + struct rmnet_private *p = netdev_priv(dev); + return &p->stats; +} + +static void rmnet_set_multicast_list(struct net_device *dev) +{ +} + +static void rmnet_tx_timeout(struct net_device *dev) +{ + pr_info("rmnet_tx_timeout()\n"); +} + +static struct net_device_ops rmnet_ops = { + .ndo_open = rmnet_open, + .ndo_stop = rmnet_stop, + .ndo_start_xmit = rmnet_xmit, + .ndo_get_stats = rmnet_get_stats, + .ndo_set_multicast_list = rmnet_set_multicast_list, + .ndo_tx_timeout = rmnet_tx_timeout, +}; + +static void __init rmnet_setup(struct net_device *dev) +{ + dev->netdev_ops = &rmnet_ops; + + dev->watchdog_timeo = 20; /* ??? */ + + ether_setup(dev); + + //dev->change_mtu = 0; /* ??? */ + + random_ether_addr(dev->dev_addr); +} + + +static const char *ch_name[3] = { + "SMD_DATA5", + "SMD_DATA6", + "SMD_DATA7", +}; + +static int __init rmnet_init(void) +{ + int ret; + struct device *d; + struct net_device *dev; + struct rmnet_private *p; + unsigned n; + +#ifdef CONFIG_MSM_RMNET_DEBUG + timeout_us = 0; +#ifdef CONFIG_HAS_EARLYSUSPEND + timeout_suspend_us = 0; +#endif +#endif + +#ifdef CONFIG_MSM_RMNET_DEBUG + rmnet_wq = create_workqueue("rmnet"); +#endif + + for (n = 0; n < 3; n++) { + dev = alloc_netdev(sizeof(struct rmnet_private), + "rmnet%d", rmnet_setup); + + if (!dev) + return -ENOMEM; + + d = &(dev->dev); + p = netdev_priv(dev); + p->chname = ch_name[n]; + wake_lock_init(&p->wake_lock, WAKE_LOCK_SUSPEND, ch_name[n]); +#ifdef CONFIG_MSM_RMNET_DEBUG + p->timeout_us = timeout_us; + p->awake_time_ms = p->wakeups_xmit = p->wakeups_rcv = 0; + p->active_countdown = p->restart_count = 0; + INIT_DELAYED_WORK_DEFERRABLE(&p->work, do_check_active); +#endif + + ret = register_netdev(dev); + if (ret) { + free_netdev(dev); + return ret; + } + +#ifdef CONFIG_MSM_RMNET_DEBUG + if (device_create_file(d, &dev_attr_timeout)) + continue; + if (device_create_file(d, &dev_attr_wakeups_xmit)) + continue; + if (device_create_file(d, &dev_attr_wakeups_rcv)) + continue; + if (device_create_file(d, &dev_attr_awake_time_ms)) + continue; +#ifdef CONFIG_HAS_EARLYSUSPEND + if (device_create_file(d, &dev_attr_timeout_suspend)) + continue; + + /* Only care about rmnet0 for suspend/resume tiemout hooks. */ + if (n == 0) + rmnet0 = d; +#endif +#endif + } + return 0; +} + +module_init(rmnet_init); From 135d7e8a14d533c26ae2a86f0ac6ce25638a00e3 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 31 Mar 2008 20:38:24 -0700 Subject: [PATCH 0182/2556] [ARM] msm: RTC driver for Qualcomm MSM7K chipsets Create a virtual RTC alarm which restricts max suspend sleep time. Since the QCT RTC alarm doesn't work properly (yet), we need to clamp the max suspend sleep time if there is an alarm set for the future. This ensures that the device will be awake when the virtual RTC alarm needs to be triggered. Signed-off-by: San Mehat --- drivers/rtc/Kconfig | 7 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-msm7x00a.c | 273 +++++++++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 drivers/rtc/rtc-msm7x00a.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 79b2a635dc3b8..04df80a29c46a 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -707,6 +707,13 @@ config RTC_DRV_DAVINCI This driver can also be built as a module. If so, the module will be called rtc-davinci. +config RTC_DRV_MSM7X00A + tristate "MSM7X00A" + depends on ARCH_MSM + default y + help + RTC driver for Qualcomm MSM7K chipsets + config RTC_DRV_OMAP tristate "TI OMAP1" depends on ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 || ARCH_DAVINCI_DA8XX diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 9b5b85e6e998b..a0cdff96eaf5c 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o +obj-$(CONFIG_RTC_DRV_MSM7X00A) += rtc-msm7x00a.o obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o diff --git a/drivers/rtc/rtc-msm7x00a.c b/drivers/rtc/rtc-msm7x00a.c new file mode 100644 index 0000000000000..25d73ebbbf5c9 --- /dev/null +++ b/drivers/rtc/rtc-msm7x00a.c @@ -0,0 +1,273 @@ +/* drivers/rtc/rtc-msm7x00a.c + * + * Copyright (C) 2008 Google, Inc. + * Author: San Mehat + * + * 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 + +extern void msm_pm_set_max_sleep_time(int64_t sleep_time_ns); + +#define APP_TIMEREMOTE_PDEV_NAME "rs30000048:0da5b528" + +#define TIMEREMOTE_PROCEEDURE_SET_JULIAN 6 +#define TIMEREMOTE_PROCEEDURE_GET_JULIAN 7 + +struct rpc_time_julian { + uint32_t year; + uint32_t month; + uint32_t day; + uint32_t hour; + uint32_t minute; + uint32_t second; + uint32_t day_of_week; +}; + +static struct msm_rpc_endpoint *ep; +static struct rtc_device *rtc; +static unsigned long rtcalarm_time; + +static int +msmrtc_timeremote_set_time(struct device *dev, struct rtc_time *tm) +{ + int rc; + + struct timeremote_set_julian_req { + struct rpc_request_hdr hdr; + uint32_t opt_arg; + + struct rpc_time_julian time; + } req; + + struct timeremote_set_julian_rep { + struct rpc_reply_hdr hdr; + } rep; + + if (tm->tm_year < 1900) + tm->tm_year += 1900; + + if (tm->tm_year < 1970) + return -EINVAL; + +#if 0 + printk(KERN_DEBUG "%s: %.2u/%.2u/%.4u %.2u:%.2u:%.2u (%.2u)\n", + __func__, tm->tm_mon, tm->tm_mday, tm->tm_year, + tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday); +#endif + + req.opt_arg = cpu_to_be32(1); + req.time.year = cpu_to_be32(tm->tm_year); + req.time.month = cpu_to_be32(tm->tm_mon + 1); + req.time.day = cpu_to_be32(tm->tm_mday); + req.time.hour = cpu_to_be32(tm->tm_hour); + req.time.minute = cpu_to_be32(tm->tm_min); + req.time.second = cpu_to_be32(tm->tm_sec); + req.time.day_of_week = cpu_to_be32(tm->tm_wday); + + + rc = msm_rpc_call_reply(ep, TIMEREMOTE_PROCEEDURE_SET_JULIAN, + &req, sizeof(req), + &rep, sizeof(rep), + 5 * HZ); + return rc; +} + +static int +msmrtc_timeremote_read_time(struct device *dev, struct rtc_time *tm) +{ + int rc; + + struct timeremote_get_julian_req { + struct rpc_request_hdr hdr; + uint32_t julian_time_not_null; + } req; + + struct timeremote_get_julian_rep { + struct rpc_reply_hdr hdr; + uint32_t opt_arg; + struct rpc_time_julian time; + } rep; + + req.julian_time_not_null = cpu_to_be32(1); + + rc = msm_rpc_call_reply(ep, TIMEREMOTE_PROCEEDURE_GET_JULIAN, + &req, sizeof(req), + &rep, sizeof(rep), + 5 * HZ); + if (rc < 0) + return rc; + + if (!be32_to_cpu(rep.opt_arg)) { + printk(KERN_ERR "%s: No data from RTC\n", __func__); + return -ENODATA; + } + + tm->tm_year = be32_to_cpu(rep.time.year); + tm->tm_mon = be32_to_cpu(rep.time.month); + tm->tm_mday = be32_to_cpu(rep.time.day); + tm->tm_hour = be32_to_cpu(rep.time.hour); + tm->tm_min = be32_to_cpu(rep.time.minute); + tm->tm_sec = be32_to_cpu(rep.time.second); + tm->tm_wday = be32_to_cpu(rep.time.day_of_week); + +#if 0 + printk(KERN_DEBUG "%s: %.2u/%.2u/%.4u %.2u:%.2u:%.2u (%.2u)\n", + __func__, tm->tm_mon, tm->tm_mday, tm->tm_year, + tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday); +#endif + + tm->tm_year -= 1900; /* RTC layer expects years to start at 1900 */ + tm->tm_mon--; /* RTC layer expects mons to be 0 based */ + + if (rtc_valid_tm(tm) < 0) { + dev_err(dev, "retrieved date/time is not valid.\n"); + rtc_time_to_tm(0, tm); + } + + return 0; +} + + +static int +msmrtc_virtual_alarm_set(struct device *dev, struct rtc_wkalrm *a) +{ + unsigned long now = get_seconds(); + + if (!a->enabled) { + rtcalarm_time = 0; + return 0; + } else + rtc_tm_to_time(&a->time, &rtcalarm_time); + + if (now > rtcalarm_time) { + printk(KERN_ERR "%s: Attempt to set alarm in the past\n", + __func__); + rtcalarm_time = 0; + return -EINVAL; + } + + return 0; +} + +static struct rtc_class_ops msm_rtc_ops = { + .read_time = msmrtc_timeremote_read_time, + .set_time = msmrtc_timeremote_set_time, + .set_alarm = msmrtc_virtual_alarm_set, +}; + +static void +msmrtc_alarmtimer_expired(unsigned long _data) +{ + printk(KERN_DEBUG "%s: Generating alarm event (src %lu)\n", + rtc->name, _data); + + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); + rtcalarm_time = 0; +} + +static int +msmrtc_probe(struct platform_device *pdev) +{ + struct rpcsvr_platform_device *rdev = + container_of(pdev, struct rpcsvr_platform_device, base); + + ep = msm_rpc_connect(rdev->prog, rdev->vers, 0); + if (IS_ERR(ep)) { + printk(KERN_ERR "%s: init rpc failed! rc = %ld\n", + __func__, PTR_ERR(ep)); + return PTR_ERR(ep); + } + + rtc = rtc_device_register("msm_rtc", + &pdev->dev, + &msm_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtc)) { + printk(KERN_ERR "%s: Can't register RTC device (%ld)\n", + pdev->name, PTR_ERR(rtc)); + return PTR_ERR(rtc); + } + return 0; +} + + +static unsigned long msmrtc_get_seconds(void) +{ + struct rtc_time tm; + unsigned long now; + + msmrtc_timeremote_read_time(NULL, &tm); + rtc_tm_to_time(&tm, &now); + return now; +} + +static int +msmrtc_suspend(struct platform_device *dev, pm_message_t state) +{ + if (rtcalarm_time) { + unsigned long now = msmrtc_get_seconds(); + int diff = rtcalarm_time - now; + if (diff <= 0) { + msmrtc_alarmtimer_expired(1); + msm_pm_set_max_sleep_time(0); + return 0; + } + msm_pm_set_max_sleep_time((int64_t) ((int64_t) diff * NSEC_PER_SEC)); + } else + msm_pm_set_max_sleep_time(0); + return 0; +} + +static int +msmrtc_resume(struct platform_device *dev) +{ + if (rtcalarm_time) { + unsigned long now = msmrtc_get_seconds(); + int diff = rtcalarm_time - now; + if (diff <= 0) + msmrtc_alarmtimer_expired(2); + } + return 0; +} + +static struct platform_driver msmrtc_driver = { + .probe = msmrtc_probe, + .suspend = msmrtc_suspend, + .resume = msmrtc_resume, + .driver = { + .name = APP_TIMEREMOTE_PDEV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init msmrtc_init(void) +{ + rtcalarm_time = 0; + return platform_driver_register(&msmrtc_driver); +} + +module_init(msmrtc_init); + +MODULE_DESCRIPTION("RTC driver for Qualcomm MSM7x00a chipsets"); +MODULE_AUTHOR("San Mehat "); +MODULE_LICENSE("GPL"); From 7997ce4214c5c9b6e667d057f1e60d40f6b85524 Mon Sep 17 00:00:00 2001 From: "Mike A. Chan" Date: Wed, 29 Oct 2008 13:18:57 -0700 Subject: [PATCH 0183/2556] [ARM] msm: clock: Simple cpufreq scaling based on screen ON/OFF Signed-off-by: Mike A. Chan --- arch/arm/mach-msm/Kconfig | 19 ++++++++++++++++ arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/cpufreq.c | 43 +++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 arch/arm/mach-msm/cpufreq.c diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index f61fcd53597f0..1d1575650c0bf 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -336,4 +336,23 @@ config MSM_RPCSERVERS help none +menuconfig MSM_CPU_FREQ_SCREEN + depends on HAS_EARLYSUSPEND + default n + bool "Enable simple cpu frequency scaling" + help + Simple cpufreq scaling based on screen ON/OFF. + +if MSM_CPU_FREQ_SCREEN + +config MSM_CPU_FREQ_SCREEN_OFF + int "Screen off cpu frequency" + default 245760 + +config MSM_CPU_FREQ_SCREEN_ON + int "Screen on cpu frequency" + default 384000 + +endif # MSM_CPU_FREQ_SCREEN + endif diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 60f2df1ab24fc..e30988a04e5bd 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_dog_keepalive.o obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_time_remote.o obj-$(CONFIG_PM) += pm.o +obj-$(CONFIG_MSM_CPU_FREQ_SCREEN) += cpufreq.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c new file mode 100644 index 0000000000000..efa46a0c9fa5b --- /dev/null +++ b/arch/arm/mach-msm/cpufreq.c @@ -0,0 +1,43 @@ +/* arch/arm/mach-msm/cpufreq.c + * + * MSM architecture cpufreq driver + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007 QUALCOMM Incorporated + * Author: Mike A. Chan + * + * 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 "acpuclock.h" + +static void msm_early_suspend(struct early_suspend *handler) { + acpuclk_set_rate(CONFIG_MSM_CPU_FREQ_SCREEN_OFF * 1000, 0); +} + +static void msm_late_resume(struct early_suspend *handler) { + acpuclk_set_rate(CONFIG_MSM_CPU_FREQ_SCREEN_ON * 1000, 0); +} + +static struct early_suspend msm_power_suspend = { + .suspend = msm_early_suspend, + .resume = msm_late_resume, +}; + +static int __init clock_late_init(void) +{ + register_early_suspend(&msm_power_suspend); + return 0; +} + +late_initcall(clock_late_init); From d68a165cf093f292fd870902dbc87d15e6359721 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Sat, 1 Dec 2007 19:56:52 -0800 Subject: [PATCH 0184/2556] [ARM] msm: hw3d register access driver Needed by the userspace OpenGL|ES library. Signed-off-by: Brian Swetland Signed-off-by: Rebecca Schultz --- arch/arm/mach-msm/Kconfig | 7 ++ arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/hw3d.c | 213 +++++++++++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 arch/arm/mach-msm/hw3d.c diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 1d1575650c0bf..b6636238fa169 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -355,4 +355,11 @@ config MSM_CPU_FREQ_SCREEN_ON endif # MSM_CPU_FREQ_SCREEN +config MSM_HW3D + tristate "MSM Hardware 3D Register Driver" + default y + help + Provides access to registers needed by the userspace OpenGL|ES + library. + endif diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index e30988a04e5bd..ead0cc01e3b02 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_device.o obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_dog_keepalive.o obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_time_remote.o +obj-$(CONFIG_MSM_HW3D) += hw3d.o obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_MSM_CPU_FREQ_SCREEN) += cpufreq.o diff --git a/arch/arm/mach-msm/hw3d.c b/arch/arm/mach-msm/hw3d.c new file mode 100644 index 0000000000000..b013b57077af9 --- /dev/null +++ b/arch/arm/mach-msm/hw3d.c @@ -0,0 +1,213 @@ +/* arch/arm/mach-msm/hw3d.c + * + * Register/Interrupt access for userspace 3D library. + * + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland + * + * 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 + +static DEFINE_SPINLOCK(hw3d_lock); +static DECLARE_WAIT_QUEUE_HEAD(hw3d_queue); +static int hw3d_pending; +static int hw3d_disabled; + +static struct clk *grp_clk; +static struct clk *imem_clk; +DECLARE_MUTEX(hw3d_sem); +static unsigned int hw3d_granted; +static struct file *hw3d_granted_file; + +static irqreturn_t hw3d_irq_handler(int irq, void *data) +{ + unsigned long flags; + + spin_lock_irqsave(&hw3d_lock, flags); + if (!hw3d_disabled) { + disable_irq(INT_GRAPHICS); + hw3d_disabled = 1; + } + hw3d_pending = 1; + spin_unlock_irqrestore(&hw3d_lock, flags); + + wake_up(&hw3d_queue); + + return IRQ_HANDLED; +} + +static void hw3d_disable_interrupt(void) +{ + unsigned long flags; + spin_lock_irqsave(&hw3d_lock, flags); + if (!hw3d_disabled) { + disable_irq(INT_GRAPHICS); + hw3d_disabled = 1; + } + spin_unlock_irqrestore(&hw3d_lock, flags); +} + +static long hw3d_wait_for_interrupt(void) +{ + unsigned long flags; + int ret; + + for (;;) { + spin_lock_irqsave(&hw3d_lock, flags); + if (hw3d_pending) { + hw3d_pending = 0; + spin_unlock_irqrestore(&hw3d_lock, flags); + return 0; + } + if (hw3d_disabled) { + hw3d_disabled = 0; + enable_irq(INT_GRAPHICS); + } + spin_unlock_irqrestore(&hw3d_lock, flags); + + ret = wait_event_interruptible(hw3d_queue, hw3d_pending); + if (ret < 0) { + hw3d_disable_interrupt(); + return ret; + } + } + + return 0; +} + +#define HW3D_REGS_LEN 0x100000 + +static long hw3d_revoke_gpu(struct file *file) +{ + int ret = 0; + unsigned long user_start, user_len; + struct pmem_region region = {.offset = 0x0, .len = HW3D_REGS_LEN}; + + down(&hw3d_sem); + if (!hw3d_granted) { + goto end; + } + /* revoke the pmem region completely */ + if ((ret = pmem_remap(®ion, file, PMEM_UNMAP))) + goto end; + get_pmem_user_addr(file, &user_start, &user_len); + /* reset the gpu */ + clk_disable(grp_clk); + clk_disable(imem_clk); + hw3d_granted = 0; +end: + up(&hw3d_sem); + return ret; +} + +static long hw3d_grant_gpu(struct file *file) +{ + int ret = 0; + struct pmem_region region = {.offset = 0x0, .len = HW3D_REGS_LEN}; + + down(&hw3d_sem); + if (hw3d_granted) { + ret = -1; + goto end; + } + /* map the registers */ + if ((ret = pmem_remap(®ion, file, PMEM_MAP))) + goto end; + clk_enable(grp_clk); + clk_enable(imem_clk); + hw3d_granted = 1; + hw3d_granted_file = file; +end: + up(&hw3d_sem); + return ret; +} + +static int hw3d_release(struct inode *inode, struct file *file) +{ + down(&hw3d_sem); + /* if the gpu is in use, and its inuse by the file that was released */ + if (hw3d_granted && (file == hw3d_granted_file)) { + clk_disable(grp_clk); + clk_disable(imem_clk); + hw3d_granted = 0; + hw3d_granted_file = NULL; + } + up(&hw3d_sem); + return 0; +} + +static long hw3d_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case HW3D_REVOKE_GPU: + return hw3d_revoke_gpu(file); + break; + case HW3D_GRANT_GPU: + return hw3d_grant_gpu(file); + break; + case HW3D_WAIT_FOR_INTERRUPT: + return hw3d_wait_for_interrupt(); + break; + default: + return -EINVAL; + } + return 0; +} + +static struct android_pmem_platform_data pmem_data = { + .name = "hw3d", + .start = 0xA0000000, + .size = 0x100000, + .no_allocator = 1, + .cached = 0, +}; + +static int __init hw3d_init(void) +{ + int ret; + + grp_clk = clk_get(NULL, "grp_clk"); + if (IS_ERR(grp_clk)) + return PTR_ERR(grp_clk); + + imem_clk = clk_get(NULL, "imem_clk"); + if (IS_ERR(imem_clk)) { + clk_put(grp_clk); + return PTR_ERR(imem_clk); + } + ret = request_irq(INT_GRAPHICS, hw3d_irq_handler, + IRQF_TRIGGER_HIGH, "hw3d", 0); + if (ret) { + clk_put(grp_clk); + clk_put(imem_clk); + return ret; + } + hw3d_disable_interrupt(); + hw3d_granted = 0; + + return pmem_setup(&pmem_data, hw3d_ioctl, hw3d_release); +} + +device_initcall(hw3d_init); From d7c1b827e15d22dd76cabe86eac3e735e8e42e85 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Wed, 19 Dec 2007 18:42:50 -0800 Subject: [PATCH 0185/2556] [ARM] msm: add ADSP driver Provide necessary access to aDSP memory from userspace. Change-Id: I23765f649c579c5e52327df278f276e2d317dcd1 Signed-off-by: Iliyan Malchev Signed-off-by: Brian Swetland --- arch/arm/mach-msm/Kconfig | 6 + arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/include/mach/msm_adsp.h | 48 + .../include/mach/qdsp5/qdsp5audplaycmdi.h | 71 ++ .../include/mach/qdsp5/qdsp5audplaymsg.h | 57 ++ .../include/mach/qdsp5/qdsp5audppcmdi.h | 880 +++++++++++++++++ .../include/mach/qdsp5/qdsp5audppmsg.h | 308 ++++++ .../include/mach/qdsp5/qdsp5audpreproccmdi.h | 256 +++++ .../include/mach/qdsp5/qdsp5audpreprocmsg.h | 85 ++ .../include/mach/qdsp5/qdsp5audreccmdi.h | 176 ++++ .../include/mach/qdsp5/qdsp5audrecmsg.h | 127 +++ .../include/mach/qdsp5/qdsp5jpegcmdi.h | 377 ++++++++ .../include/mach/qdsp5/qdsp5jpegmsg.h | 177 ++++ .../include/mach/qdsp5/qdsp5lpmcmdi.h | 82 ++ .../mach-msm/include/mach/qdsp5/qdsp5lpmmsg.h | 80 ++ .../include/mach/qdsp5/qdsp5vdeccmdi.h | 235 +++++ .../include/mach/qdsp5/qdsp5vdecmsg.h | 107 ++ .../include/mach/qdsp5/qdsp5vfecmdi.h | 910 ++++++++++++++++++ .../mach-msm/include/mach/qdsp5/qdsp5vfemsg.h | 290 ++++++ arch/arm/mach-msm/io.c | 2 + arch/arm/mach-msm/qdsp5/Makefile | 12 + arch/arm/mach-msm/qdsp5/adsp.c | 902 +++++++++++++++++ arch/arm/mach-msm/qdsp5/adsp.h | 303 ++++++ arch/arm/mach-msm/qdsp5/adsp_6210.c | 283 ++++++ arch/arm/mach-msm/qdsp5/adsp_6220.c | 284 ++++++ arch/arm/mach-msm/qdsp5/adsp_6225.c | 322 +++++++ arch/arm/mach-msm/qdsp5/adsp_driver.c | 615 ++++++++++++ .../mach-msm/qdsp5/adsp_jpeg_patch_event.c | 31 + .../arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c | 181 ++++ arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c | 65 ++ .../arm/mach-msm/qdsp5/adsp_vfe_patch_event.c | 54 ++ arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c | 239 +++++ .../mach-msm/qdsp5/adsp_video_verify_cmd.c | 151 +++ arch/arm/mach-msm/qdsp5/audio_in.c | 750 +++++++++++++++ arch/arm/mach-msm/qdsp5/audio_mp3.c | 577 +++++++++++ arch/arm/mach-msm/qdsp5/audio_out.c | 841 ++++++++++++++++ arch/arm/mach-msm/qdsp5/audmgr.c | 306 ++++++ arch/arm/mach-msm/qdsp5/audmgr.h | 211 ++++ arch/arm/mach-msm/qdsp5/audpp.c | 367 +++++++ arch/arm/mach-msm/qdsp5/evlog.h | 133 +++ include/linux/msm_adsp.h | 77 ++ include/linux/msm_audio.h | 52 + 42 files changed, 11031 insertions(+) create mode 100644 arch/arm/mach-msm/include/mach/msm_adsp.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaycmdi.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaymsg.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppcmdi.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppmsg.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreproccmdi.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreprocmsg.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp5/qdsp5audreccmdi.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp5/qdsp5audrecmsg.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegcmdi.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegmsg.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmcmdi.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmmsg.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdeccmdi.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdecmsg.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfecmdi.h create mode 100644 arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfemsg.h create mode 100644 arch/arm/mach-msm/qdsp5/Makefile create mode 100644 arch/arm/mach-msm/qdsp5/adsp.c create mode 100644 arch/arm/mach-msm/qdsp5/adsp.h create mode 100644 arch/arm/mach-msm/qdsp5/adsp_6210.c create mode 100644 arch/arm/mach-msm/qdsp5/adsp_6220.c create mode 100644 arch/arm/mach-msm/qdsp5/adsp_6225.c create mode 100644 arch/arm/mach-msm/qdsp5/adsp_driver.c create mode 100644 arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c create mode 100644 arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c create mode 100644 arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c create mode 100644 arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c create mode 100644 arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c create mode 100644 arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c create mode 100644 arch/arm/mach-msm/qdsp5/audio_in.c create mode 100644 arch/arm/mach-msm/qdsp5/audio_mp3.c create mode 100644 arch/arm/mach-msm/qdsp5/audio_out.c create mode 100644 arch/arm/mach-msm/qdsp5/audmgr.c create mode 100644 arch/arm/mach-msm/qdsp5/audmgr.h create mode 100644 arch/arm/mach-msm/qdsp5/audpp.c create mode 100644 arch/arm/mach-msm/qdsp5/evlog.h create mode 100644 include/linux/msm_adsp.h create mode 100644 include/linux/msm_audio.h diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index b6636238fa169..2d9796a71753c 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -362,4 +362,10 @@ config MSM_HW3D Provides access to registers needed by the userspace OpenGL|ES library. +config MSM_ADSP + tristate "MSM ADSP driver" + default y + help + Provides access to registers needed by the userspace aDSP library. + endif diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index ead0cc01e3b02..dd6b6403e5e59 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_device.o obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_dog_keepalive.o obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_time_remote.o +obj-$(CONFIG_MSM_ADSP) += qdsp5/ obj-$(CONFIG_MSM_HW3D) += hw3d.o obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_MSM_CPU_FREQ_SCREEN) += cpufreq.o diff --git a/arch/arm/mach-msm/include/mach/msm_adsp.h b/arch/arm/mach-msm/include/mach/msm_adsp.h new file mode 100644 index 0000000000000..d157c251276be --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_adsp.h @@ -0,0 +1,48 @@ +/* include/asm-arm/arch-msm/msm_adsp.h + * + * Copyright (C) 2008 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 __ASM__ARCH_MSM_ADSP_H +#define __ASM__ARCH_MSM_ADSP_H + +struct msm_adsp_module; + +struct msm_adsp_ops { + /* event is called from interrupt context when a message + * arrives from the DSP. Use the provided function pointer + * to copy the message into a local buffer. Do NOT call + * it multiple times. + */ + void (*event)(void *driver_data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)); +}; + +/* Get, Put, Enable, and Disable are synchronous and must only + * be called from thread context. Enable and Disable will block + * up to one second in the event of a fatal DSP error but are + * much faster otherwise. + */ +int msm_adsp_get(const char *name, struct msm_adsp_module **module, + struct msm_adsp_ops *ops, void *driver_data); +void msm_adsp_put(struct msm_adsp_module *module); +int msm_adsp_enable(struct msm_adsp_module *module); +int msm_adsp_disable(struct msm_adsp_module *module); + +/* Write is safe to call from interrupt context. + */ +int msm_adsp_write(struct msm_adsp_module *module, + unsigned queue_id, + void *data, size_t len); + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaycmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaycmdi.h new file mode 100644 index 0000000000000..439fe2cea8c78 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaycmdi.h @@ -0,0 +1,71 @@ +#ifndef QDSP5AUDPLAYCMDI_H +#define QDSP5AUDPLAYCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + Q D S P 5 A U D I O P L A Y T A S K C O M M A N D S + +GENERAL DESCRIPTION + Command Interface for AUDPLAYTASK on QDSP5 + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + + audplay_cmd_dec_data_avail + Send buffer to AUDPLAY task + + +Copyright(c) 1992 - 2009 by QUALCOMM, Incorporated. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audplaycmdi.h#2 $ + +===========================================================================*/ + +#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL 0x0000 +#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_LEN \ + sizeof(audplay_cmd_bitstream_data_avail) + +/* Type specification of dec_data_avail message sent to AUDPLAYTASK +*/ +typedef struct { + /*command ID*/ + unsigned int cmd_id; + + /* Decoder ID for which message is being sent */ + unsigned int decoder_id; + + /* Start address of data in ARM global memory */ + unsigned int buf_ptr; + + /* Number of 16-bit words of bit-stream data contiguously available at the + * above-mentioned address + */ + unsigned int buf_size; + + /* Partition number used by audPlayTask to communicate with DSP's RTOS + * kernel + */ + unsigned int partition_number; + +} __attribute__((packed)) audplay_cmd_bitstream_data_avail; + +#endif /* QDSP5AUDPLAYCMD_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaymsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaymsg.h new file mode 100644 index 0000000000000..74c79d3268269 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaymsg.h @@ -0,0 +1,57 @@ +#ifndef QDSP5AUDPLAYMSG_H +#define QDSP5AUDPLAYMSG_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + Q D S P 5 A U D I O P L A Y T A S K M S G + +GENERAL DESCRIPTION + Message sent by AUDPLAY task + +REFERENCES + None + + +Copyright(c) 1992 - 2009 by QUALCOMM, Incorporated. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audplaymsg.h#3 $ + +===========================================================================*/ +#define AUDPLAY_MSG_DEC_NEEDS_DATA 0x0001 +#define AUDPLAY_MSG_DEC_NEEDS_DATA_MSG_LEN \ + sizeof(audplay_msg_dec_needs_data) + +typedef struct{ + /* reserved*/ + unsigned int dec_id; + + /*The read pointer offset of external memory till which bitstream + has been dmed in*/ + unsigned int adecDataReadPtrOffset; + + /* The buffer size of external memory. */ + unsigned int adecDataBufSize; + + unsigned int bitstream_free_len; + unsigned int bitstream_write_ptr; + unsigned int bitstarem_buf_start; + unsigned int bitstream_buf_len; +} __attribute__((packed)) audplay_msg_dec_needs_data; +#endif /* QDSP5AUDPLAYMSG_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppcmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppcmdi.h new file mode 100644 index 0000000000000..104d2068ca5a9 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppcmdi.h @@ -0,0 +1,880 @@ +#ifndef QDSP5AUDPPCMDI_H +#define QDSP5AUDPPCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + A U D I O P O S T P R O C E S S I N G I N T E R N A L C O M M A N D S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by AUDPP Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audppcmdi.h#2 $ + +===========================================================================*/ + +/* + * ARM to AUDPPTASK Commands + * + * ARM uses three command queues to communicate with AUDPPTASK + * 1)uPAudPPCmd1Queue : Used for more frequent and shorter length commands + * Location : MEMA + * Buffer Size : 6 words + * No of buffers in a queue : 20 for gaming audio and 5 for other images + * 2)uPAudPPCmd2Queue : Used for commands which are not much lengthier + * Location : MEMA + * Buffer Size : 23 + * No of buffers in a queue : 2 + * 3)uPAudOOCmd3Queue : Used for lengthier and more frequent commands + * Location : MEMA + * Buffer Size : 145 + * No of buffers in a queue : 3 + */ + +/* + * Commands Related to uPAudPPCmd1Queue + */ + +/* + * Command Structure to enable or disable the active decoders + */ + +#define AUDPP_CMD_CFG_DEC_TYPE 0x0001 +#define AUDPP_CMD_CFG_DEC_TYPE_LEN sizeof(audpp_cmd_cfg_dec_type) + +/* Enable the decoder */ +#define AUDPP_CMD_DEC_TYPE_M 0x000F + +#define AUDPP_CMD_ENA_DEC_V 0x4000 +#define AUDPP_CMD_DIS_DEC_V 0x0000 +#define AUDPP_CMD_DEC_STATE_M 0x4000 + +#define AUDPP_CMD_UPDATDE_CFG_DEC 0x8000 +#define AUDPP_CMD_DONT_UPDATE_CFG_DEC 0x0000 + + +/* Type specification of cmd_cfg_dec */ + +typedef struct { + unsigned short cmd_id; + unsigned short dec0_cfg; + unsigned short dec1_cfg; + unsigned short dec2_cfg; + unsigned short dec3_cfg; + unsigned short dec4_cfg; +} __attribute__((packed)) audpp_cmd_cfg_dec_type; + +/* + * Command Structure to Pause , Resume and flushes the selected audio decoders + */ + +#define AUDPP_CMD_DEC_CTRL 0x0002 +#define AUDPP_CMD_DEC_CTRL_LEN sizeof(audpp_cmd_dec_ctrl); + +/* Decoder control commands for pause, resume and flush */ +#define AUDPP_CMD_FLUSH_V 0x2000 + +#define AUDPP_CMD_PAUSE_V 0x4000 +#define AUDPP_CMD_RESUME_V 0x0000 + +#define AUDPP_CMD_UPDATE_V 0x8000 +#define AUDPP_CMD_IGNORE_V 0x0000 + + +/* Type Spec for decoder control command*/ + +typedef struct { + unsigned short cmd_id; + unsigned short dec0_ctrl; + unsigned short dec1_ctrl; + unsigned short dec2_ctrl; + unsigned short dec3_ctrl; + unsigned short dec4_ctrl; +} __attribute__((packed)) audpp_cmd_dec_ctrl; + +/* + * Command Structure to Configure the AVSync FeedBack Mechanism + */ + +#define AUDPP_CMD_AVSYNC 0x0003 +#define AUDPP_CMD_AVSYNC_LEN sizeof(audpp_cmd_avsync) + +typedef struct { + unsigned short cmd_id; + unsigned short object_number; + unsigned short interrupt_interval_lsw; + unsigned short interrupt_interval_msw; +} __attribute__((packed)) audpp_cmd_avsync; + +/* + * Command Structure to enable or disable(sleep) the AUDPPTASK + */ + +#define AUDPP_CMD_CFG 0x0004 +#define AUDPP_CMD_CFG_LEN sizeof(audpp_cmd_cfg) + +#define AUDPP_CMD_CFG_SLEEP 0x0000 +#define AUDPP_CMD_CFG_ENABLE 0xFFFF + +typedef struct { + unsigned short cmd_id; + unsigned short cfg; +} __attribute__((packed)) audpp_cmd_cfg; + +/* + * Command Structure to Inject or drop the specified no of samples + */ + +#define AUDPP_CMD_ADJUST_SAMP 0x0005 +#define AUDPP_CMD_ADJUST_SAMP_LEN sizeof(audpp_cmd_adjust_samp) + +#define AUDPP_CMD_SAMP_DROP -1 +#define AUDPP_CMD_SAMP_INSERT 0x0001 + +#define AUDPP_CMD_NUM_SAMPLES 0x0001 + +typedef struct { + unsigned short cmd_id; + unsigned short object_no; + signed short sample_insert_or_drop; + unsigned short num_samples; +} __attribute__((packed)) audpp_cmd_adjust_samp; + +/* + * Command Structure to Configure AVSync Feedback Mechanism + */ + +#define AUDPP_CMD_AVSYNC_CMD_2 0x0006 +#define AUDPP_CMD_AVSYNC_CMD_2_LEN sizeof(audpp_cmd_avsync_cmd_2) + +typedef struct { + unsigned short cmd_id; + unsigned short object_number; + unsigned short interrupt_interval_lsw; + unsigned short interrupt_interval_msw; + unsigned short sample_counter_dlsw; + unsigned short sample_counter_dmsw; + unsigned short sample_counter_msw; + unsigned short byte_counter_dlsw; + unsigned short byte_counter_dmsw; + unsigned short byte_counter_msw; +} __attribute__((packed)) audpp_cmd_avsync_cmd_2; + +/* + * Command Structure to Configure AVSync Feedback Mechanism + */ + +#define AUDPP_CMD_AVSYNC_CMD_3 0x0007 +#define AUDPP_CMD_AVSYNC_CMD_3_LEN sizeof(audpp_cmd_avsync_cmd_3) + +typedef struct { + unsigned short cmd_id; + unsigned short object_number; + unsigned short interrupt_interval_lsw; + unsigned short interrupt_interval_msw; + unsigned short sample_counter_dlsw; + unsigned short sample_counter_dmsw; + unsigned short sample_counter_msw; + unsigned short byte_counter_dlsw; + unsigned short byte_counter_dmsw; + unsigned short byte_counter_msw; +} __attribute__((packed)) audpp_cmd_avsync_cmd_3; + + +/* + * Commands Related to uPAudPPCmd2Queue + */ + +/* + * Command Structure to configure Per decoder Parameters (Common) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS 0x0000 +#define AUDPP_CMD_CFG_ADEC_PARAMS_COMMON_LEN \ + sizeof(audpp_cmd_cfg_adec_params_common) + +#define AUDPP_CMD_STATUS_MSG_FLAG_ENA_FCM 0x4000 +#define AUDPP_CMD_STATUS_MSG_FLAG_DIS_FCM 0x0000 + +#define AUDPP_CMD_STATUS_MSG_FLAG_ENA_DCM 0x8000 +#define AUDPP_CMD_STATUS_MSG_FLAG_DIS_DCM 0x0000 + +/* Sampling frequency*/ +#define AUDPP_CMD_SAMP_RATE_96000 0x0000 +#define AUDPP_CMD_SAMP_RATE_88200 0x0001 +#define AUDPP_CMD_SAMP_RATE_64000 0x0002 +#define AUDPP_CMD_SAMP_RATE_48000 0x0003 +#define AUDPP_CMD_SAMP_RATE_44100 0x0004 +#define AUDPP_CMD_SAMP_RATE_32000 0x0005 +#define AUDPP_CMD_SAMP_RATE_24000 0x0006 +#define AUDPP_CMD_SAMP_RATE_22050 0x0007 +#define AUDPP_CMD_SAMP_RATE_16000 0x0008 +#define AUDPP_CMD_SAMP_RATE_12000 0x0009 +#define AUDPP_CMD_SAMP_RATE_11025 0x000A +#define AUDPP_CMD_SAMP_RATE_8000 0x000B + + +/* + * Type specification of cmd_adec_cfg sent to all decoder + */ + +typedef struct { + unsigned short cmd_id; + unsigned short length; + unsigned short dec_id; + unsigned short status_msg_flag; + unsigned short decoder_frame_counter_msg_period; + unsigned short input_sampling_frequency; +} __attribute__((packed)) audpp_cmd_cfg_adec_params_common; + +/* + * Command Structure to configure Per decoder Parameters (Wav) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_WAV_LEN \ + sizeof(audpp_cmd_cfg_adec_params_wav) + + +#define AUDPP_CMD_WAV_STEREO_CFG_MONO 0x0001 +#define AUDPP_CMD_WAV_STEREO_CFG_STEREO 0x0002 + +#define AUDPP_CMD_WAV_PCM_WIDTH_8 0x0000 +#define AUDPP_CMD_WAV_PCM_WIDTH_16 0x0001 +#define AUDPP_CMD_WAV_PCM_WIDTH_32 0x0002 + +typedef struct { + audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; + unsigned short pcm_width; + unsigned short sign; +} __attribute__((packed)) audpp_cmd_cfg_adec_params_wav; + +/* + * Command Structure to configure Per decoder Parameters (ADPCM) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_ADPCM_LEN \ + sizeof(audpp_cmd_cfg_adec_params_adpcm) + + +#define AUDPP_CMD_ADPCM_STEREO_CFG_MONO 0x0001 +#define AUDPP_CMD_ADPCM_STEREO_CFG_STEREO 0x0002 + +typedef struct { + audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; + unsigned short block_size; +} __attribute__((packed)) audpp_cmd_cfg_adec_params_adpcm; + +/* + * Command Structure to configure Per decoder Parameters (MP3) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_MP3_LEN \ + sizeof(audpp_cmd_cfg_adec_params_mp3) + +typedef struct { + audpp_cmd_cfg_adec_params_common common; +} __attribute__((packed)) audpp_cmd_cfg_adec_params_mp3; + + +/* + * Command Structure to configure Per decoder Parameters (AAC) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_AAC_LEN \ + sizeof(audpp_cmd_cfg_adec_params_aac) + + +#define AUDPP_CMD_AAC_FORMAT_ADTS -1 +#define AUDPP_CMD_AAC_FORMAT_RAW 0x0000 +#define AUDPP_CMD_AAC_FORMAT_PSUEDO_RAW 0x0001 + +#define AUDPP_CMD_AAC_AUDIO_OBJECT_LC 0x0002 +#define AUDPP_CMD_AAC_AUDIO_OBJECT_LTP 0x0004 +#define AUDPP_CMD_AAC_AUDIO_OBJECT_ERLC 0x0011 + +#define AUDPP_CMD_AAC_SBR_ON_FLAG_ON 0x0001 +#define AUDPP_CMD_AAC_SBR_ON_FLAG_OFF 0x0000 + +#define AUDPP_CMD_AAC_SBR_PS_ON_FLAG_ON 0x0001 +#define AUDPP_CMD_AAC_SBR_PS_ON_FLAG_OFF 0x0000 + +typedef struct { + audpp_cmd_cfg_adec_params_common common; + signed short format; + unsigned short audio_object; + unsigned short ep_config; + unsigned short aac_section_data_resilience_flag; + unsigned short aac_scalefactor_data_resilience_flag; + unsigned short aac_spectral_data_resilience_flag; + unsigned short sbr_on_flag; + unsigned short sbr_ps_on_flag; + unsigned short dual_mono_mode; + unsigned short channel_configuration; +} __attribute__((packed)) audpp_cmd_cfg_adec_params_aac; + +/* + * Command Structure to configure the HOST PCM interface + */ + +#define AUDPP_CMD_PCM_INTF 0x0001 +#define AUDPP_CMD_PCM_INTF_2 0x0002 +#define AUDPP_CMD_PCM_INTF_LEN sizeof(audpp_cmd_pcm_intf) + +#define AUDPP_CMD_PCM_INTF_MONO_V 0x0001 +#define AUDPP_CMD_PCM_INTF_STEREO_V 0x0002 + +/* These two values differentiate the two types of commands that could be issued + * Interface configuration command and Buffer update command */ + +#define AUDPP_CMD_PCM_INTF_CONFIG_CMD_V 0x0000 +#define AUDPP_CMD_PCM_INTF_BUFFER_CMD_V -1 + +#define AUDPP_CMD_PCM_INTF_RX_ENA_M 0x000F +#define AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V 0x0008 +#define AUDPP_CMD_PCM_INTF_RX_ENA_DSPTOARM_V 0x0004 + +/* These flags control the enabling and disabling of the interface together + * with host interface bit mask. */ + +#define AUDPP_CMD_PCM_INTF_ENA_V -1 +#define AUDPP_CMD_PCM_INTF_DIS_V 0x0000 + + +#define AUDPP_CMD_PCM_INTF_FULL_DUPLEX 0x0 +#define AUDPP_CMD_PCM_INTF_HALF_DUPLEX_TODSP 0x1 + + +#define AUDPP_CMD_PCM_INTF_OBJECT_NUM 0x5 +#define AUDPP_CMD_PCM_INTF_COMMON_OBJECT_NUM 0x6 + + +typedef struct { + unsigned short cmd_id; + unsigned short object_num; + signed short config; + unsigned short intf_type; + + /* DSP -> ARM Configuration */ + unsigned short read_buf1LSW; + unsigned short read_buf1MSW; + unsigned short read_buf1_len; + + unsigned short read_buf2LSW; + unsigned short read_buf2MSW; + unsigned short read_buf2_len; + /* 0:HOST_PCM_INTF disable + ** 0xFFFF: HOST_PCM_INTF enable + */ + signed short dsp_to_arm_flag; + unsigned short partition_number; + + /* ARM -> DSP Configuration */ + unsigned short write_buf1LSW; + unsigned short write_buf1MSW; + unsigned short write_buf1_len; + + unsigned short write_buf2LSW; + unsigned short write_buf2MSW; + unsigned short write_buf2_len; + + /* 0:HOST_PCM_INTF disable + ** 0xFFFF: HOST_PCM_INTF enable + */ + signed short arm_to_rx_flag; + unsigned short weight_decoder_to_rx; + unsigned short weight_arm_to_rx; + + unsigned short partition_number_arm_to_dsp; + unsigned short sample_rate; + unsigned short channel_mode; +} __attribute__((packed)) audpp_cmd_pcm_intf; + +/* + ** BUFFER UPDATE COMMAND + */ +#define AUDPP_CMD_PCM_INTF_SEND_BUF_PARAMS_LEN \ + sizeof(audpp_cmd_pcm_intf_send_buffer) + +typedef struct { + unsigned short cmd_id; + unsigned short host_pcm_object; + /* set config = 0xFFFF for configuration*/ + signed short config; + unsigned short intf_type; + unsigned short dsp_to_arm_buf_id; + unsigned short arm_to_dsp_buf_id; + unsigned short arm_to_dsp_buf_len; +} __attribute__((packed)) audpp_cmd_pcm_intf_send_buffer; + + +/* + * Commands Related to uPAudPPCmd3Queue + */ + +/* + * Command Structure to configure post processing params (Commmon) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS 0x0000 +#define AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN \ + sizeof(audpp_cmd_cfg_object_params_common) + +#define AUDPP_CMD_OBJ0_UPDATE 0x0001 +#define AUDPP_CMD_OBJ0_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_OBJ1_UPDATE 0x0001 +#define AUDPP_CMD_OBJ1_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_OBJ2_UPDATE 0x0001 +#define AUDPP_CMD_OBJ2_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_OBJ3_UPDATE 0x0001 +#define AUDPP_CMD_OBJ3_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_OBJ4_UPDATE 0x0001 +#define AUDPP_CMD_OBJ4_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_HPCM_UPDATE 0x0001 +#define AUDPP_CMD_HPCM_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_COMMON_CFG_UPDATE 0x0001 +#define AUDPP_CMD_COMMON_CFG_DONT_UPDATE 0x0000 + +typedef struct { + unsigned short cmd_id; + unsigned short obj0_cfg; + unsigned short obj1_cfg; + unsigned short obj2_cfg; + unsigned short obj3_cfg; + unsigned short obj4_cfg; + unsigned short host_pcm_obj_cfg; + unsigned short comman_cfg; + unsigned short command_type; +} __attribute__((packed)) audpp_cmd_cfg_object_params_common; + +/* + * Command Structure to configure post processing params (Volume) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_VOLUME_LEN \ + sizeof(audpp_cmd_cfg_object_params_volume) + +typedef struct { + audpp_cmd_cfg_object_params_common common; + unsigned short volume; + unsigned short pan; +} __attribute__((packed)) audpp_cmd_cfg_object_params_volume; + +/* + * Command Structure to configure post processing params (PCM Filter) --DOUBT + */ + +typedef struct { + unsigned short numerator_b0_filter_lsw; + unsigned short numerator_b0_filter_msw; + unsigned short numerator_b1_filter_lsw; + unsigned short numerator_b1_filter_msw; + unsigned short numerator_b2_filter_lsw; + unsigned short numerator_b2_filter_msw; +} __attribute__((packed)) numerator; + +typedef struct { + unsigned short denominator_a0_filter_lsw; + unsigned short denominator_a0_filter_msw; + unsigned short denominator_a1_filter_lsw; + unsigned short denominator_a1_filter_msw; +} __attribute__((packed)) denominator; + +typedef struct { + unsigned short shift_factor_0; +} __attribute__((packed)) shift_factor; + +typedef struct { + unsigned short pan_filter_0; +} __attribute__((packed)) pan; + +typedef struct { + numerator numerator_filter; + denominator denominator_filter; + shift_factor shift_factor_filter; + pan pan_filter; +} __attribute__((packed)) filter_1; + +typedef struct { + numerator numerator_filter[2]; + denominator denominator_filter[2]; + shift_factor shift_factor_filter[2]; + pan pan_filter[2]; +} __attribute__((packed)) filter_2; + +typedef struct { + numerator numerator_filter[3]; + denominator denominator_filter[3]; + shift_factor shift_factor_filter[3]; + pan pan_filter[3]; +} __attribute__((packed)) filter_3; + +typedef struct { + numerator numerator_filter[4]; + denominator denominator_filter[4]; + shift_factor shift_factor_filter[4]; + pan pan_filter[4]; +} __attribute__((packed)) filter_4; + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_PCM_LEN \ + sizeof(audpp_cmd_cfg_object_params_pcm) + + +typedef struct { + audpp_cmd_cfg_object_params_common common; + unsigned short active_flag; + unsigned short num_bands; + union { + filter_1 filter_1_params; + filter_2 filter_2_params; + filter_3 filter_3_params; + filter_4 filter_4_params; + } __attribute__((packed)) params_filter; +} __attribute__((packed)) audpp_cmd_cfg_object_params_pcm; + + +/* + * Command Structure to configure post processing parameters (equalizer) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_EQALIZER_LEN \ + sizeof(audpp_cmd_cfg_object_params_eqalizer) + +typedef struct { + unsigned short numerator_coeff_0_lsw; + unsigned short numerator_coeff_0_msw; + unsigned short numerator_coeff_1_lsw; + unsigned short numerator_coeff_1_msw; + unsigned short numerator_coeff_2_lsw; + unsigned short numerator_coeff_2_msw; +} __attribute__((packed)) eq_numerator; + +typedef struct { + unsigned short denominator_coeff_0_lsw; + unsigned short denominator_coeff_0_msw; + unsigned short denominator_coeff_1_lsw; + unsigned short denominator_coeff_1_msw; +} __attribute__((packed)) eq_denominator; + +typedef struct { + unsigned short shift_factor; +} __attribute__((packed)) eq_shiftfactor; + +typedef struct { + eq_numerator numerator; + eq_denominator denominator; + eq_shiftfactor shiftfactor; +} __attribute__((packed)) eq_coeff_1; + +typedef struct { + eq_numerator numerator[2]; + eq_denominator denominator[2]; + eq_shiftfactor shiftfactor[2]; +} __attribute__((packed)) eq_coeff_2; + +typedef struct { + eq_numerator numerator[3]; + eq_denominator denominator[3]; + eq_shiftfactor shiftfactor[3]; +} __attribute__((packed)) eq_coeff_3; + +typedef struct { + eq_numerator numerator[4]; + eq_denominator denominator[4]; + eq_shiftfactor shiftfactor[4]; +} __attribute__((packed)) eq_coeff_4; + +typedef struct { + eq_numerator numerator[5]; + eq_denominator denominator[5]; + eq_shiftfactor shiftfactor[5]; +} __attribute__((packed)) eq_coeff_5; + +typedef struct { + eq_numerator numerator[6]; + eq_denominator denominator[6]; + eq_shiftfactor shiftfactor[6]; +} __attribute__((packed)) eq_coeff_6; + +typedef struct { + eq_numerator numerator[7]; + eq_denominator denominator[7]; + eq_shiftfactor shiftfactor[7]; +} __attribute__((packed)) eq_coeff_7; + +typedef struct { + eq_numerator numerator[8]; + eq_denominator denominator[8]; + eq_shiftfactor shiftfactor[8]; +} __attribute__((packed)) eq_coeff_8; + +typedef struct { + eq_numerator numerator[9]; + eq_denominator denominator[9]; + eq_shiftfactor shiftfactor[9]; +} __attribute__((packed)) eq_coeff_9; + +typedef struct { + eq_numerator numerator[10]; + eq_denominator denominator[10]; + eq_shiftfactor shiftfactor[10]; +} __attribute__((packed)) eq_coeff_10; + +typedef struct { + eq_numerator numerator[11]; + eq_denominator denominator[11]; + eq_shiftfactor shiftfactor[11]; +} __attribute__((packed)) eq_coeff_11; + +typedef struct { + eq_numerator numerator[12]; + eq_denominator denominator[12]; + eq_shiftfactor shiftfactor[12]; +} __attribute__((packed)) eq_coeff_12; + + +typedef struct { + audpp_cmd_cfg_object_params_common common; + unsigned short eq_flag; + unsigned short num_bands; + union { + eq_coeff_1 eq_coeffs_1; + eq_coeff_2 eq_coeffs_2; + eq_coeff_3 eq_coeffs_3; + eq_coeff_4 eq_coeffs_4; + eq_coeff_5 eq_coeffs_5; + eq_coeff_6 eq_coeffs_6; + eq_coeff_7 eq_coeffs_7; + eq_coeff_8 eq_coeffs_8; + eq_coeff_9 eq_coeffs_9; + eq_coeff_10 eq_coeffs_10; + eq_coeff_11 eq_coeffs_11; + eq_coeff_12 eq_coeffs_12; + } __attribute__((packed)) eq_coeff; +} __attribute__((packed)) audpp_cmd_cfg_object_params_eqalizer; + + +/* + * Command Structure to configure post processing parameters (ADRC) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_ADRC_LEN \ + sizeof(audpp_cmd_cfg_object_params_adrc) + + +#define AUDPP_CMD_ADRC_FLAG_DIS 0x0000 +#define AUDPP_CMD_ADRC_FLAG_ENA -1 + +typedef struct { + audpp_cmd_cfg_object_params_common common; + signed short adrc_flag; + unsigned short compression_th; + unsigned short compression_slope; + unsigned short rms_time; + unsigned short attack_const_lsw; + unsigned short attack_const_msw; + unsigned short release_const_lsw; + unsigned short release_const_msw; + unsigned short adrc_system_delay; +} __attribute__((packed)) audpp_cmd_cfg_object_params_adrc; + +/* + * Command Structure to configure post processing parameters(Spectrum Analizer) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_SPECTRAM_LEN \ + sizeof(audpp_cmd_cfg_object_params_spectram) + + +typedef struct { + audpp_cmd_cfg_object_params_common common; + unsigned short sample_interval; + unsigned short num_coeff; +} __attribute__((packed)) audpp_cmd_cfg_object_params_spectram; + +/* + * Command Structure to configure post processing parameters (QConcert) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_QCONCERT_LEN \ + sizeof(audpp_cmd_cfg_object_params_qconcert) + + +#define AUDPP_CMD_QCON_ENA_FLAG_ENA -1 +#define AUDPP_CMD_QCON_ENA_FLAG_DIS 0x0000 + +#define AUDPP_CMD_QCON_OP_MODE_HEADPHONE -1 +#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_FRONT 0x0000 +#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_SIDE 0x0001 +#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_DESKTOP 0x0002 + +#define AUDPP_CMD_QCON_GAIN_UNIT 0x7FFF +#define AUDPP_CMD_QCON_GAIN_SIX_DB 0x4027 + + +#define AUDPP_CMD_QCON_EXPANSION_MAX 0x7FFF + + +typedef struct { + audpp_cmd_cfg_object_params_common common; + signed short enable_flag; + signed short output_mode; + signed short gain; + signed short expansion; + signed short delay; + unsigned short stages_per_mode; +} __attribute__((packed)) audpp_cmd_cfg_object_params_qconcert; + +/* + * Command Structure to configure post processing parameters (Side Chain) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_SIDECHAIN_LEN \ + sizeof(audpp_cmd_cfg_object_params_sidechain) + + +#define AUDPP_CMD_SIDECHAIN_ACTIVE_FLAG_DIS 0x0000 +#define AUDPP_CMD_SIDECHAIN_ACTIVE_FLAG_ENA -1 + +typedef struct { + audpp_cmd_cfg_object_params_common common; + signed short active_flag; + unsigned short num_bands; + union { + filter_1 filter_1_params; + filter_2 filter_2_params; + filter_3 filter_3_params; + filter_4 filter_4_params; + } __attribute__((packed)) params_filter; +} __attribute__((packed)) audpp_cmd_cfg_object_params_sidechain; + + +/* + * Command Structure to configure post processing parameters (QAFX) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_QAFX_LEN \ + sizeof(audpp_cmd_cfg_object_params_qafx) + +#define AUDPP_CMD_QAFX_ENA_DISA 0x0000 +#define AUDPP_CMD_QAFX_ENA_ENA_CFG -1 +#define AUDPP_CMD_QAFX_ENA_DIS_CFG 0x0001 + +#define AUDPP_CMD_QAFX_CMD_TYPE_ENV 0x0100 +#define AUDPP_CMD_QAFX_CMD_TYPE_OBJ 0x0010 +#define AUDPP_CMD_QAFX_CMD_TYPE_QUERY 0x1000 + +#define AUDPP_CMD_QAFX_CMDS_ENV_OP_MODE 0x0100 +#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_POS 0x0101 +#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_ORI 0x0102 +#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_VEL 0X0103 +#define AUDPP_CMD_QAFX_CMDS_ENV_ENV_RES 0x0107 + +#define AUDPP_CMD_QAFX_CMDS_OBJ_SAMP_FREQ 0x0010 +#define AUDPP_CMD_QAFX_CMDS_OBJ_VOL 0x0011 +#define AUDPP_CMD_QAFX_CMDS_OBJ_DIST 0x0012 +#define AUDPP_CMD_QAFX_CMDS_OBJ_POS 0x0013 +#define AUDPP_CMD_QAFX_CMDS_OBJ_VEL 0x0014 + + +typedef struct { + audpp_cmd_cfg_object_params_common common; + signed short enable; + unsigned short command_type; + unsigned short num_commands; + unsigned short commands; +} __attribute__((packed)) audpp_cmd_cfg_object_params_qafx; + +/* + * Command Structure to enable , disable or configure the reverberation effect + * (Common) + */ + +#define AUDPP_CMD_REVERB_CONFIG 0x0001 +#define AUDPP_CMD_REVERB_CONFIG_COMMON_LEN \ + sizeof(audpp_cmd_reverb_config_common) + +#define AUDPP_CMD_ENA_ENA 0xFFFF +#define AUDPP_CMD_ENA_DIS 0x0000 +#define AUDPP_CMD_ENA_CFG 0x0001 + +#define AUDPP_CMD_CMD_TYPE_ENV 0x0104 +#define AUDPP_CMD_CMD_TYPE_OBJ 0x0015 +#define AUDPP_CMD_CMD_TYPE_QUERY 0x1000 + + +typedef struct { + unsigned short cmd_id; + unsigned short enable; + unsigned short cmd_type; +} __attribute__((packed)) audpp_cmd_reverb_config_common; + +/* + * Command Structure to enable , disable or configure the reverberation effect + * (ENV-0x0104) + */ + +#define AUDPP_CMD_REVERB_CONFIG_ENV_104_LEN \ + sizeof(audpp_cmd_reverb_config_env_104) + +typedef struct { + audpp_cmd_reverb_config_common common; + unsigned short env_gain; + unsigned short decay_msw; + unsigned short decay_lsw; + unsigned short decay_timeratio_msw; + unsigned short decay_timeratio_lsw; + unsigned short delay_time; + unsigned short reverb_gain; + unsigned short reverb_delay; +} __attribute__((packed)) audpp_cmd_reverb_config_env_104; + +/* + * Command Structure to enable , disable or configure the reverberation effect + * (ENV-0x0015) + */ + +#define AUDPP_CMD_REVERB_CONFIG_ENV_15_LEN \ + sizeof(audpp_cmd_reverb_config_env_15) + +typedef struct { + audpp_cmd_reverb_config_common common; + unsigned short object_num; + unsigned short absolute_gain; +} __attribute__((packed)) audpp_cmd_reverb_config_env_15; + + +#endif /* QDSP5AUDPPCMDI_H */ + diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppmsg.h new file mode 100644 index 0000000000000..98f952b7fd89c --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppmsg.h @@ -0,0 +1,308 @@ +#ifndef QDSP5AUDPPMSG_H +#define QDSP5AUDPPMSG_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + Q D S P 5 A U D I O P O S T P R O C E S S I N G M S G + +GENERAL DESCRIPTION + Messages sent by AUDPPTASK to ARM + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright(c) 1992 - 2009 by QUALCOMM, Incorporated. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + + $Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audppmsg.h#4 $ + +===========================================================================*/ + +/* + * AUDPPTASK uses audPPuPRlist to send messages to the ARM + * Location : MEMA + * Buffer Size : 45 + * No of Buffers in a queue : 5 for gaming audio and 1 for other images + */ + +/* + * MSG to Informs the ARM os Success/Failure of bringing up the decoder + */ + +#define AUDPP_MSG_STATUS_MSG 0x0001 +#define AUDPP_MSG_STATUS_MSG_LEN \ + sizeof(audpp_msg_status_msg) + +#define AUDPP_MSG_STATUS_SLEEP 0x0000 +#define AUDPP_MSG__STATUS_INIT 0x0001 +#define AUDPP_MSG_MSG_STATUS_CFG 0x0002 +#define AUDPP_MSG_STATUS_PLAY 0x0003 + +#define AUDPP_MSG_REASON_MIPS 0x0000 +#define AUDPP_MSG_REASON_MEM 0x0001 + +typedef struct{ + unsigned short dec_id; + unsigned short status; + unsigned short reason; +} __attribute__((packed)) audpp_msg_status_msg; + +/* + * MSG to communicate the spectrum analyzer output bands to the ARM + */ +#define AUDPP_MSG_SPA_BANDS 0x0002 +#define AUDPP_MSG_SPA_BANDS_LEN \ + sizeof(audpp_msg_spa_bands) + +typedef struct { + unsigned short current_object; + unsigned short spa_band_1; + unsigned short spa_band_2; + unsigned short spa_band_3; + unsigned short spa_band_4; + unsigned short spa_band_5; + unsigned short spa_band_6; + unsigned short spa_band_7; + unsigned short spa_band_8; + unsigned short spa_band_9; + unsigned short spa_band_10; + unsigned short spa_band_11; + unsigned short spa_band_12; + unsigned short spa_band_13; + unsigned short spa_band_14; + unsigned short spa_band_15; + unsigned short spa_band_16; + unsigned short spa_band_17; + unsigned short spa_band_18; + unsigned short spa_band_19; + unsigned short spa_band_20; + unsigned short spa_band_21; + unsigned short spa_band_22; + unsigned short spa_band_23; + unsigned short spa_band_24; + unsigned short spa_band_25; + unsigned short spa_band_26; + unsigned short spa_band_27; + unsigned short spa_band_28; + unsigned short spa_band_29; + unsigned short spa_band_30; + unsigned short spa_band_31; + unsigned short spa_band_32; +} __attribute__((packed)) audpp_msg_spa_bands; + +/* + * MSG to communicate the PCM I/O buffer status to ARM + */ +#define AUDPP_MSG_HOST_PCM_INTF_MSG 0x0003 +#define AUDPP_MSG_HOST_PCM_INTF_MSG_LEN \ + sizeof(audpp_msg_host_pcm_intf_msg) + +#define AUDPP_MSG_HOSTPCM_ID_TX_ARM 0x0000 +#define AUDPP_MSG_HOSTPCM_ID_ARM_TX 0x0001 +#define AUDPP_MSG_HOSTPCM_ID_RX_ARM 0x0002 +#define AUDPP_MSG_HOSTPCM_ID_ARM_RX 0x0003 + +#define AUDPP_MSG_SAMP_FREQ_INDX_96000 0x0000 +#define AUDPP_MSG_SAMP_FREQ_INDX_88200 0x0001 +#define AUDPP_MSG_SAMP_FREQ_INDX_64000 0x0002 +#define AUDPP_MSG_SAMP_FREQ_INDX_48000 0x0003 +#define AUDPP_MSG_SAMP_FREQ_INDX_44100 0x0004 +#define AUDPP_MSG_SAMP_FREQ_INDX_32000 0x0005 +#define AUDPP_MSG_SAMP_FREQ_INDX_24000 0x0006 +#define AUDPP_MSG_SAMP_FREQ_INDX_22050 0x0007 +#define AUDPP_MSG_SAMP_FREQ_INDX_16000 0x0008 +#define AUDPP_MSG_SAMP_FREQ_INDX_12000 0x0009 +#define AUDPP_MSG_SAMP_FREQ_INDX_11025 0x000A +#define AUDPP_MSG_SAMP_FREQ_INDX_8000 0x000B + +#define AUDPP_MSG_CHANNEL_MODE_MONO 0x0001 +#define AUDPP_MSG_CHANNEL_MODE_STEREO 0x0002 + +typedef struct{ + unsigned short obj_num; + unsigned short numbers_of_samples; + unsigned short host_pcm_id; + unsigned short buf_indx; + unsigned short samp_freq_indx; + unsigned short channel_mode; +} __attribute__((packed)) audpp_msg_host_pcm_intf_msg; + + +/* + * MSG to communicate 3D position of the source and listener , source volume + * source rolloff, source orientation + */ + +#define AUDPP_MSG_QAFX_POS 0x0004 +#define AUDPP_MSG_QAFX_POS_LEN \ + sizeof(audpp_msg_qafx_pos) + +typedef struct { + unsigned short current_object; + unsigned short x_pos_lis_msw; + unsigned short x_pos_lis_lsw; + unsigned short y_pos_lis_msw; + unsigned short y_pos_lis_lsw; + unsigned short z_pos_lis_msw; + unsigned short z_pos_lis_lsw; + unsigned short x_fwd_msw; + unsigned short x_fwd_lsw; + unsigned short y_fwd_msw; + unsigned short y_fwd_lsw; + unsigned short z_fwd_msw; + unsigned short z_fwd_lsw; + unsigned short x_up_msw; + unsigned short x_up_lsw; + unsigned short y_up_msw; + unsigned short y_up_lsw; + unsigned short z_up_msw; + unsigned short z_up_lsw; + unsigned short x_vel_lis_msw; + unsigned short x_vel_lis_lsw; + unsigned short y_vel_lis_msw; + unsigned short y_vel_lis_lsw; + unsigned short z_vel_lis_msw; + unsigned short z_vel_lis_lsw; + unsigned short threed_enable_flag; + unsigned short volume; + unsigned short x_pos_source_msw; + unsigned short x_pos_source_lsw; + unsigned short y_pos_source_msw; + unsigned short y_pos_source_lsw; + unsigned short z_pos_source_msw; + unsigned short z_pos_source_lsw; + unsigned short max_dist_0_msw; + unsigned short max_dist_0_lsw; + unsigned short min_dist_0_msw; + unsigned short min_dist_0_lsw; + unsigned short roll_off_factor; + unsigned short mute_after_max_flag; + unsigned short x_vel_source_msw; + unsigned short x_vel_source_lsw; + unsigned short y_vel_source_msw; + unsigned short y_vel_source_lsw; + unsigned short z_vel_source_msw; + unsigned short z_vel_source_lsw; +} __attribute__((packed)) audpp_msg_qafx_pos; + +/* + * MSG to provide AVSYNC feedback from DSP to ARM + */ + +#define AUDPP_MSG_AVSYNC_MSG 0x0005 +#define AUDPP_MSG_AVSYNC_MSG_LEN \ + sizeof(audpp_msg_avsync_msg) + +typedef struct { + unsigned short active_flag; + unsigned short num_samples_counter0_HSW; + unsigned short num_samples_counter0_MSW; + unsigned short num_samples_counter0_LSW; + unsigned short num_bytes_counter0_HSW; + unsigned short num_bytes_counter0_MSW; + unsigned short num_bytes_counter0_LSW; + unsigned short samp_freq_obj_0; + unsigned short samp_freq_obj_1; + unsigned short samp_freq_obj_2; + unsigned short samp_freq_obj_3; + unsigned short samp_freq_obj_4; + unsigned short samp_freq_obj_5; + unsigned short samp_freq_obj_6; + unsigned short samp_freq_obj_7; + unsigned short samp_freq_obj_8; + unsigned short samp_freq_obj_9; + unsigned short samp_freq_obj_10; + unsigned short samp_freq_obj_11; + unsigned short samp_freq_obj_12; + unsigned short samp_freq_obj_13; + unsigned short samp_freq_obj_14; + unsigned short samp_freq_obj_15; + unsigned short num_samples_counter4_HSW; + unsigned short num_samples_counter4_MSW; + unsigned short num_samples_counter4_LSW; + unsigned short num_bytes_counter4_HSW; + unsigned short num_bytes_counter4_MSW; + unsigned short num_bytes_counter4_LSW; +} __attribute__((packed)) audpp_msg_avsync_msg; + +/* + * MSG to provide PCM DMA Missed feedback from the DSP to ARM + */ + +#define AUDPP_MSG_PCMDMAMISSED 0x0006 +#define AUDPP_MSG_PCMDMAMISSED_LEN \ + sizeof(audpp_msg_pcmdmamissed); + +typedef struct{ + /* + ** Bit 0 0 = PCM DMA not missed for object 0 + ** 1 = PCM DMA missed for object0 + ** Bit 1 0 = PCM DMA not missed for object 1 + ** 1 = PCM DMA missed for object1 + ** Bit 2 0 = PCM DMA not missed for object 2 + ** 1 = PCM DMA missed for object2 + ** Bit 3 0 = PCM DMA not missed for object 3 + ** 1 = PCM DMA missed for object3 + ** Bit 4 0 = PCM DMA not missed for object 4 + ** 1 = PCM DMA missed for object4 + */ + unsigned short pcmdmamissed; +} __attribute__((packed)) audpp_msg_pcmdmamissed; + +/* + * MSG to AUDPP enable or disable feedback form DSP to ARM + */ + +#define AUDPP_MSG_CFG_MSG 0x0007 +#define AUDPP_MSG_CFG_MSG_LEN \ + sizeof(audpp_msg_cfg_msg) + +#define AUDPP_MSG_ENA_ENA 0xFFFF +#define AUDPP_MSG_ENA_DIS 0x0000 + +typedef struct{ + /* Enabled - 0xffff + ** Disabled - 0 + */ + unsigned short enabled; +} __attribute__((packed)) audpp_msg_cfg_msg; + +/* + * MSG to communicate the reverb per object volume + */ + +#define AUDPP_MSG_QREVERB_VOLUME 0x0008 +#define AUDPP_MSG_QREVERB_VOLUME_LEN \ + sizeof(audpp_msg_qreverb_volume) + + +typedef struct { + unsigned short obj_0_gain; + unsigned short obj_1_gain; + unsigned short obj_2_gain; + unsigned short obj_3_gain; + unsigned short obj_4_gain; + unsigned short hpcm_obj_volume; +} __attribute__((packed)) audpp_msg_qreverb_volume; + + +#endif /* QDSP5AUDPPMSG_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreproccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreproccmdi.h new file mode 100644 index 0000000000000..1beb5de230e33 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreproccmdi.h @@ -0,0 +1,256 @@ +#ifndef QDSP5AUDPREPROCCMDI_H +#define QDSP5AUDPREPROCCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + A U D I O P R E P R O C E S S I N G I N T E R N A L C O M M A N D S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by AUDPREPROC Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audpreproccmdi.h#2 $ + +===========================================================================*/ + +/* + * AUDIOPREPROC COMMANDS: + * ARM uses uPAudPreProcCmdQueue to communicate with AUDPREPROCTASK + * Location : MEMB + * Buffer size : 51 + * Number of buffers in a queue : 3 + */ + +/* + * Command to configure the parameters of AGC + */ + +#define AUDPREPROC_CMD_CFG_AGC_PARAMS 0x0000 +#define AUDPREPROC_CMD_CFG_AGC_PARAMS_LEN \ + sizeof(audpreproc_cmd_cfg_agc_params) + +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_SLOPE 0x0009 +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_TH 0x000A +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_SLOPE 0x000B +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_TH 0x000C +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_AIG_FLAG 0x000D +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_STATIC_GAIN 0x000E +#define AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG 0x000F + +#define AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA -1 +#define AUDPREPROC_CMD_TX_AGC_ENA_FLAG_DIS 0x0000 + +#define AUDPREPROC_CMD_ADP_GAIN_FLAG_ENA_ADP_GAIN -1 +#define AUDPREPROC_CMD_ADP_GAIN_FLAG_ENA_STATIC_GAIN 0x0000 + +#define AUDPREPROC_CMD_PARAM_MASK_RMS_TAY 0x0004 +#define AUDPREPROC_CMD_PARAM_MASK_RELEASEK 0x0005 +#define AUDPREPROC_CMD_PARAM_MASK_DELAY 0x0006 +#define AUDPREPROC_CMD_PARAM_MASK_ATTACKK 0x0007 +#define AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_SLOW 0x0008 +#define AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_FAST 0x0009 +#define AUDPREPROC_CMD_PARAM_MASK_AIG_RELEASEK 0x000A +#define AUDPREPROC_CMD_PARAM_MASK_AIG_MIN 0x000B +#define AUDPREPROC_CMD_PARAM_MASK_AIG_MAX 0x000C +#define AUDPREPROC_CMD_PARAM_MASK_LEAK_UP 0x000D +#define AUDPREPROC_CMD_PARAM_MASK_LEAK_DOWN 0x000E +#define AUDPREPROC_CMD_PARAM_MASK_AIG_ATTACKK 0x000F + +typedef struct { + unsigned short cmd_id; + unsigned short tx_agc_param_mask; + unsigned short tx_agc_enable_flag; + unsigned short static_gain; + signed short adaptive_gain_flag; + unsigned short expander_th; + unsigned short expander_slope; + unsigned short compressor_th; + unsigned short compressor_slope; + unsigned short param_mask; + unsigned short aig_attackk; + unsigned short aig_leak_down; + unsigned short aig_leak_up; + unsigned short aig_max; + unsigned short aig_min; + unsigned short aig_releasek; + unsigned short aig_leakrate_fast; + unsigned short aig_leakrate_slow; + unsigned short attackk_msw; + unsigned short attackk_lsw; + unsigned short delay; + unsigned short releasek_msw; + unsigned short releasek_lsw; + unsigned short rms_tav; +} __attribute__((packed)) audpreproc_cmd_cfg_agc_params; + + +/* + * Command to configure the params of Advanved AGC + */ + +#define AUDPREPROC_CMD_CFG_AGC_PARAMS_2 0x0001 +#define AUDPREPROC_CMD_CFG_AGC_PARAMS_2_LEN \ + sizeof(audpreproc_cmd_cfg_agc_params_2) + +#define AUDPREPROC_CMD_2_TX_AGC_ENA_FLAG_ENA -1; +#define AUDPREPROC_CMD_2_TX_AGC_ENA_FLAG_DIS 0x0000; + +typedef struct { + unsigned short cmd_id; + unsigned short agc_param_mask; + signed short tx_agc_enable_flag; + unsigned short comp_static_gain; + unsigned short exp_th; + unsigned short exp_slope; + unsigned short comp_th; + unsigned short comp_slope; + unsigned short comp_rms_tav; + unsigned short comp_samp_mask; + unsigned short comp_attackk_msw; + unsigned short comp_attackk_lsw; + unsigned short comp_releasek_msw; + unsigned short comp_releasek_lsw; + unsigned short comp_delay; + unsigned short comp_makeup_gain; +} __attribute__((packed)) audpreproc_cmd_cfg_agc_params_2; + +/* + * Command to configure params for ns + */ + +#define AUDPREPROC_CMD_CFG_NS_PARAMS 0x0002 +#define AUDPREPROC_CMD_CFG_NS_PARAMS_LEN \ + sizeof(audpreproc_cmd_cfg_ns_params) + +#define AUDPREPROC_CMD_EC_MODE_NEW_NLMS_ENA 0x0001 +#define AUDPREPROC_CMD_EC_MODE_NEW_NLMS_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_DES_ENA 0x0002 +#define AUDPREPROC_CMD_EC_MODE_NEW_DES_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_NS_ENA 0x0004 +#define AUDPREPROC_CMD_EC_MODE_NEW_NS_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_CNI_ENA 0x0008 +#define AUDPREPROC_CMD_EC_MODE_NEW_CNI_DIS 0x0000 + +#define AUDPREPROC_CMD_EC_MODE_NEW_NLES_ENA 0x0010 +#define AUDPREPROC_CMD_EC_MODE_NEW_NLES_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_HB_ENA 0x0020 +#define AUDPREPROC_CMD_EC_MODE_NEW_HB_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_VA_ENA 0x0040 +#define AUDPREPROC_CMD_EC_MODE_NEW_VA_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_PCD_ENA 0x0080 +#define AUDPREPROC_CMD_EC_MODE_NEW_PCD_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_FEHI_ENA 0x0100 +#define AUDPREPROC_CMD_EC_MODE_NEW_FEHI_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_NEHI_ENA 0x0200 +#define AUDPREPROC_CMD_EC_MODE_NEW_NEHI_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_NLPP_ENA 0x0400 +#define AUDPREPROC_CMD_EC_MODE_NEW_NLPP_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_FNE_ENA 0x0800 +#define AUDPREPROC_CMD_EC_MODE_NEW_FNE_DIS 0x0000 +#define AUDPREPROC_CMD_EC_MODE_NEW_PRENLMS_ENA 0x1000 +#define AUDPREPROC_CMD_EC_MODE_NEW_PRENLMS_DIS 0x0000 + +typedef struct { + unsigned short cmd_id; + unsigned short ec_mode_new; + unsigned short dens_gamma_n; + unsigned short dens_nfe_block_size; + unsigned short dens_limit_ns; + unsigned short dens_limit_ns_d; + unsigned short wb_gamma_e; + unsigned short wb_gamma_n; +} __attribute__((packed)) audpreproc_cmd_cfg_ns_params; + +/* + * Command to configure parameters for IIR tuning filter + */ + +#define AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS 0x0003 +#define AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS_LEN \ + sizeof(audpreproc_cmd_cfg_iir_tuning_filter_params) + +#define AUDPREPROC_CMD_IIR_ACTIVE_FLAG_DIS 0x0000 +#define AUDPREPROC_CMD_IIR_ACTIVE_FLAG_ENA 0x0001 + +typedef struct { + unsigned short cmd_id; + unsigned short active_flag; + unsigned short num_bands; + unsigned short numerator_coeff_b0_filter0_lsw; + unsigned short numerator_coeff_b0_filter0_msw; + unsigned short numerator_coeff_b1_filter0_lsw; + unsigned short numerator_coeff_b1_filter0_msw; + unsigned short numerator_coeff_b2_filter0_lsw; + unsigned short numerator_coeff_b2_filter0_msw; + unsigned short numerator_coeff_b0_filter1_lsw; + unsigned short numerator_coeff_b0_filter1_msw; + unsigned short numerator_coeff_b1_filter1_lsw; + unsigned short numerator_coeff_b1_filter1_msw; + unsigned short numerator_coeff_b2_filter1_lsw; + unsigned short numerator_coeff_b2_filter1_msw; + unsigned short numerator_coeff_b0_filter2_lsw; + unsigned short numerator_coeff_b0_filter2_msw; + unsigned short numerator_coeff_b1_filter2_lsw; + unsigned short numerator_coeff_b1_filter2_msw; + unsigned short numerator_coeff_b2_filter2_lsw; + unsigned short numerator_coeff_b2_filter2_msw; + unsigned short numerator_coeff_b0_filter3_lsw; + unsigned short numerator_coeff_b0_filter3_msw; + unsigned short numerator_coeff_b1_filter3_lsw; + unsigned short numerator_coeff_b1_filter3_msw; + unsigned short numerator_coeff_b2_filter3_lsw; + unsigned short numerator_coeff_b2_filter3_msw; + unsigned short denominator_coeff_a0_filter0_lsw; + unsigned short denominator_coeff_a0_filter0_msw; + unsigned short denominator_coeff_a1_filter0_lsw; + unsigned short denominator_coeff_a1_filter0_msw; + unsigned short denominator_coeff_a0_filter1_lsw; + unsigned short denominator_coeff_a0_filter1_msw; + unsigned short denominator_coeff_a1_filter1_lsw; + unsigned short denominator_coeff_a1_filter1_msw; + unsigned short denominator_coeff_a0_filter2_lsw; + unsigned short denominator_coeff_a0_filter2_msw; + unsigned short denominator_coeff_a1_filter2_lsw; + unsigned short denominator_coeff_a1_filter2_msw; + unsigned short denominator_coeff_a0_filter3_lsw; + unsigned short denominator_coeff_a0_filter3_msw; + unsigned short denominator_coeff_a1_filter3_lsw; + unsigned short denominator_coeff_a1_filter3_msw; + + unsigned short shift_factor_filter0; + unsigned short shift_factor_filter1; + unsigned short shift_factor_filter2; + unsigned short shift_factor_filter3; + + unsigned short channel_selected0; + unsigned short channel_selected1; + unsigned short channel_selected2; + unsigned short channel_selected3; +} __attribute__((packed))audpreproc_cmd_cfg_iir_tuning_filter_params; + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreprocmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreprocmsg.h new file mode 100644 index 0000000000000..da8c00e5f6db6 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreprocmsg.h @@ -0,0 +1,85 @@ +#ifndef QDSP5AUDPREPROCMSG_H +#define QDSP5AUDPREPROCMSG_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + A U D I O P R E P R O C E S S I N G M E S S A G E S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of messages + that are rcvd by AUDPREPROC Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + + $Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audpreprocmsg.h#3 $ + +===========================================================================*/ + +/* + * ADSPREPROCTASK Messages + * AUDPREPROCTASK uses audPreProcUpRlist to communicate with ARM + * Location : MEMA + * Message Length : 2 + */ + +/* + * Message to indicate particular feature has been enabled or disabled + */ + + +#define AUDPREPROC_MSG_CMD_CFG_DONE_MSG 0x0000 +#define AUDPREPROC_MSG_CMD_CFG_DONE_MSG_LEN \ + sizeof(audpreproc_msg_cmd_cfg_done_msg) + +#define AUDPREPROC_MSG_TYPE_AGC 0x0000 +#define AUDPREPROC_MSG_TYPE_NOISE_REDUCTION 0x0001 +#define AUDPREPROC_MSG_TYPE_IIR_FILTER 0x0002 + + +#define AUDPREPROC_MSG_STATUS_FLAG_ENA -1 +#define AUDPREPROC_MSG_STATUS_FLAG_DIS 0x0000 + +typedef struct { + unsigned short type; + signed short status_flag; +} __attribute__((packed)) audpreproc_msg_cmd_cfg_done_msg; + + +/* + * Message to indicate particular feature has selected for wrong samp freq + */ + +#define AUDPREPROC_MSG_ERROR_MSG_ID 0x0001 +#define AUDPREPROC_MSG_ERROR_MSG_ID_LEN \ + sizeof(audpreproc_msg_error_msg_id) + +#define AUDPREPROC_MSG_ERR_INDEX_NS 0x0000 + +typedef struct { + unsigned short err_index; +} __attribute__((packed)) audpreproc_msg_error_msg_id; + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audreccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audreccmdi.h new file mode 100644 index 0000000000000..401152dcb7821 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audreccmdi.h @@ -0,0 +1,176 @@ +#ifndef QDSP5AUDRECCMDI_H +#define QDSP5AUDRECCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + A U D I O R E C O R D I N T E R N A L C O M M A N D S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by AUDREC Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ + +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + + $Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audreccmdi.h#3 $ + +============================================================================*/ + +/* + * AUDRECTASK COMMANDS + * ARM uses 2 queues to communicate with the AUDRECTASK + * 1.uPAudRecCmdQueue + * Location :MEMC + * Buffer Size : 8 + * No of Buffers in a queue : 3 + * 2.audRecUpBitStreamQueue + * Location : MEMC + * Buffer Size : 4 + * No of buffers in a queue : 2 + */ + +/* + * Commands on uPAudRecCmdQueue + */ + +/* + * Command to initiate and terminate the audio recording section + */ + +#define AUDREC_CMD_CFG 0x0000 +#define AUDREC_CMD_CFG_LEN sizeof(audrec_cmd_cfg) + +#define AUDREC_CMD_TYPE_0_INDEX_WAV 0x0000 +#define AUDREC_CMD_TYPE_0_INDEX_AAC 0x0001 + +#define AUDREC_CMD_TYPE_0_ENA 0x4000 +#define AUDREC_CMD_TYPE_0_DIS 0x0000 + +#define AUDREC_CMD_TYPE_0_NOUPDATE 0x0000 +#define AUDREC_CMD_TYPE_0_UPDATE 0x8000 + +#define AUDREC_CMD_TYPE_1_INDEX_SBC 0x0002 + +#define AUDREC_CMD_TYPE_1_ENA 0x4000 +#define AUDREC_CMD_TYPE_1_DIS 0x0000 + +#define AUDREC_CMD_TYPE_1_NOUPDATE 0x0000 +#define AUDREC_CMD_TYPE_1_UPDATE 0x8000 + +typedef struct { + unsigned short cmd_id; + unsigned short type_0; + unsigned short type_1; +} __attribute__((packed)) audrec_cmd_cfg; + + +/* + * Command to configure the recording parameters for RecType0(AAC/WAV) encoder + */ + +#define AUDREC_CMD_AREC0PARAM_CFG 0x0001 +#define AUDREC_CMD_AREC0PARAM_CFG_LEN \ + sizeof(audrec_cmd_arec0param_cfg) + +#define AUDREC_CMD_SAMP_RATE_INDX_8000 0x000B +#define AUDREC_CMD_SAMP_RATE_INDX_11025 0x000A +#define AUDREC_CMD_SAMP_RATE_INDX_12000 0x0009 +#define AUDREC_CMD_SAMP_RATE_INDX_16000 0x0008 +#define AUDREC_CMD_SAMP_RATE_INDX_22050 0x0007 +#define AUDREC_CMD_SAMP_RATE_INDX_24000 0x0006 +#define AUDREC_CMD_SAMP_RATE_INDX_32000 0x0005 +#define AUDREC_CMD_SAMP_RATE_INDX_44100 0x0004 +#define AUDREC_CMD_SAMP_RATE_INDX_48000 0x0003 + +#define AUDREC_CMD_STEREO_MODE_MONO 0x0000 +#define AUDREC_CMD_STEREO_MODE_STEREO 0x0001 + +typedef struct { + unsigned short cmd_id; + unsigned short ptr_to_extpkt_buffer_msw; + unsigned short ptr_to_extpkt_buffer_lsw; + unsigned short buf_len; + unsigned short samp_rate_index; + unsigned short stereo_mode; + unsigned short rec_quality; +} __attribute__((packed)) audrec_cmd_arec0param_cfg; + +/* + * Command to configure the recording parameters for RecType1(SBC) encoder + */ + +#define AUDREC_CMD_AREC1PARAM_CFG 0x0002 +#define AUDREC_CMD_AREC1PARAM_CFG_LEN \ + sizeof(audrec_cmd_arec1param_cfg) + +#define AUDREC_CMD_PARAM_BUF_BLOCKS_4 0x0000 +#define AUDREC_CMD_PARAM_BUF_BLOCKS_8 0x0001 +#define AUDREC_CMD_PARAM_BUF_BLOCKS_12 0x0002 +#define AUDREC_CMD_PARAM_BUF_BLOCKS_16 0x0003 + +#define AUDREC_CMD_PARAM_BUF_SUB_BANDS_8 0x0010 +#define AUDREC_CMD_PARAM_BUF_MODE_MONO 0x0000 +#define AUDREC_CMD_PARAM_BUF_MODE_DUAL 0x0040 +#define AUDREC_CMD_PARAM_BUF_MODE_STEREO 0x0050 +#define AUDREC_CMD_PARAM_BUF_MODE_JSTEREO 0x0060 +#define AUDREC_CMD_PARAM_BUF_LOUDNESS 0x0000 +#define AUDREC_CMD_PARAM_BUF_SNR 0x0100 +#define AUDREC_CMD_PARAM_BUF_BASIC_VER 0x0000 + +typedef struct { + unsigned short cmd_id; + unsigned short ptr_to_extpkt_buffer_msw; + unsigned short ptr_to_extpkt_buffer_lsw; + unsigned short buf_len; + unsigned short param_buf; + unsigned short bit_rate_0; + unsigned short bit_rate_1; +} __attribute__((packed)) audrec_cmd_arec1param_cfg; + + +/* + * Commands on audRecUpBitStreamQueue + */ + +/* + * Command to indicate the current packet read count + */ + +#define AUDREC_CMD_PACKET_EXT_PTR 0x0000 +#define AUDREC_CMD_PACKET_EXT_PTR_LEN \ + sizeof(audrec_cmd_packet_ext_ptr) + +#define AUDREC_CMD_TYPE_0 0x0000 +#define AUDREC_CMD_TYPE_1 0x0001 + +typedef struct { + unsigned short cmd_id; + unsigned short type; + unsigned short curr_rec_count_msw; + unsigned short curr_rec_count_lsw; +} __attribute__((packed)) audrec_cmd_packet_ext_ptr; + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audrecmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audrecmsg.h new file mode 100644 index 0000000000000..fe4ff924e29df --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audrecmsg.h @@ -0,0 +1,127 @@ +#ifndef QDSP5AUDRECMSGI_H +#define QDSP5AUDRECMSGI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + A U D I O R E C O R D M E S S A G E S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of messages + that are sent by AUDREC Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ + +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + + $Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audrecmsg.h#3 $ + +============================================================================*/ + +/* + * AUDRECTASK MESSAGES + * AUDRECTASK uses audRecUpRlist to communicate with ARM + * Location : MEMC + * Buffer size : 4 + * No of buffers in a queue : 2 + */ + +/* + * Message to notify that config command is done + */ + +#define AUDREC_MSG_CMD_CFG_DONE_MSG 0x0002 +#define AUDREC_MSG_CMD_CFG_DONE_MSG_LEN \ + sizeof(audrec_msg_cmd_cfg_done_msg) + + +#define AUDREC_MSG_CFG_DONE_TYPE_0_ENA 0x4000 +#define AUDREC_MSG_CFG_DONE_TYPE_0_DIS 0x0000 + +#define AUDREC_MSG_CFG_DONE_TYPE_0_NO_UPDATE 0x0000 +#define AUDREC_MSG_CFG_DONE_TYPE_0_UPDATE 0x8000 + +#define AUDREC_MSG_CFG_DONE_TYPE_1_ENA 0x4000 +#define AUDREC_MSG_CFG_DONE_TYPE_1_DIS 0x0000 + +#define AUDREC_MSG_CFG_DONE_TYPE_1_NO_UPDATE 0x0000 +#define AUDREC_MSG_CFG_DONE_TYPE_1_UPDATE 0x8000 + +typedef struct { + unsigned short type_0; + unsigned short type_1; +} __attribute__((packed))audrec_msg_cmd_cfg_done_msg; + + +/* + * Message to notify arec0/1 cfg done and recording params revd by task + */ + +#define AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG 0x0003 +#define AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG_LEN \ + sizeof(audrec_msg_cmd_arec_param_cfg_done_msg) + +#define AUDREC_MSG_AREC_PARAM_TYPE_0 0x0000 +#define AUDREC_MSG_AREC_PARAM_TYPE_1 0x0001 + +typedef struct { + unsigned short type; +} __attribute__((packed))audrec_msg_cmd_arec_param_cfg_done_msg; + + +/* + * Message to notify no more buffers are available in ext mem to DME + */ + +#define AUDREC_MSG_FATAL_ERR_MSG 0x0004 +#define AUDREC_MSG_FATAL_ERR_MSG_LEN \ + sizeof(audrec_msg_fatal_err_msg) + +#define AUDREC_MSG_FATAL_ERR_TYPE_0 0x0000 +#define AUDREC_MSG_FATAL_ERR_TYPE_1 0x0001 + +typedef struct { + unsigned short type; +} __attribute__((packed))audrec_msg_fatal_err_msg; + +/* + * Message to notify DME deliverd the encoded pkt to ext pkt buffer + */ + +#define AUDREC_MSG_PACKET_READY_MSG 0x0005 +#define AUDREC_MSG_PACKET_READY_MSG_LEN \ + sizeof(audrec_msg_packet_ready_msg) + +#define AUDREC_MSG_PACKET_READY_TYPE_0 0x0000 +#define AUDREC_MSG_PACKET_READY_TYPE_1 0x0001 + +typedef struct { + unsigned short type; + unsigned short pkt_counter_msw; + unsigned short pkt_counter_lsw; + unsigned short pkt_read_cnt_msw; + unsigned short pkt_read_cnt_lsw; +} __attribute__((packed))audrec_msg_packet_ready_msg; + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegcmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegcmdi.h new file mode 100644 index 0000000000000..92d7398ccdb93 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegcmdi.h @@ -0,0 +1,377 @@ +#ifndef QDSP5VIDJPEGCMDI_H +#define QDSP5VIDJPEGCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + J P E G I N T E R N A L C O M M A N D S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by JPEG Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5jpegcmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: +when who what, where, why +-------- --- ---------------------------------------------------------- +06/09/08 sv initial version +===========================================================================*/ + +/* + * ARM to JPEG configuration commands are passed through the + * uPJpegCfgCmdQueue + */ + +/* + * Command to configure JPEG Encoder + */ + +#define JPEG_CMD_ENC_CFG 0x0000 +#define JPEG_CMD_ENC_CFG_LEN sizeof(jpeg_cmd_enc_cfg) + +#define JPEG_CMD_ENC_PROCESS_CFG_OP_ROTATION_0 0x0000 +#define JPEG_CMD_ENC_PROCESS_CFG_OP_ROTATION_90 0x0100 +#define JPEG_CMD_ENC_PROCESS_CFG_OP_ROTATION_180 0x0200 +#define JPEG_CMD_ENC_PROCESS_CFG_OP_ROTATION_270 0x0300 +#define JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_M 0x0003 +#define JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_H2V2 0x0000 +#define JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_H2V1 0x0001 +#define JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_H1V2 0x0002 + +#define JPEG_CMD_IP_SIZE_CFG_LUMA_HEIGHT_M 0x0000FFFF +#define JPEG_CMD_IP_SIZE_CFG_LUMA_WIDTH_M 0xFFFF0000 +#define JPEG_CMD_ENC_UPSAMP_IP_SIZE_CFG_ENA 0x0001 +#define JPEG_CMD_ENC_UPSAMP_IP_SIZE_CFG_DIS 0x0000 + +#define JPEG_CMD_FRAG_SIZE_LUMA_HEIGHT_M 0xFFFF + +typedef struct { + unsigned int cmd_id; + unsigned int process_cfg; + unsigned int ip_size_cfg; + unsigned int op_size_cfg; + unsigned int frag_cfg; + unsigned int frag_cfg_part[16]; + + unsigned int part_num; + + unsigned int op_buf_0_cfg_part1; + unsigned int op_buf_0_cfg_part2; + unsigned int op_buf_1_cfg_part1; + unsigned int op_buf_1_cfg_part2; + + unsigned int luma_qunt_table[32]; + unsigned int chroma_qunt_table[32]; + + unsigned int upsamp_ip_size_cfg; + unsigned int upsamp_ip_frame_off; + unsigned int upsamp_pp_filter_coeff[64]; +} __attribute__((packed)) jpeg_cmd_enc_cfg; + +/* + * Command to configure JPEG Decoder + */ + +#define JPEG_CMD_DEC_CFG 0x0001 +#define JPEG_CMD_DEC_CFG_LEN sizeof(jpeg_cmd_dec_cfg) + +#define JPEG_CMD_DEC_OP_DATA_FORMAT_M 0x0001 +#define JPEG_CMD_DEC_OP_DATA_FORMAT_H2V2 0x0000 +#define JPEG_CMD_DEC_OP_DATA_FORMAT_H2V1 0x0001 + +#define JPEG_CMD_DEC_OP_DATA_FORMAT_SCALE_FACTOR_8 0x000000 +#define JPEG_CMD_DEC_OP_DATA_FORMAT_SCALE_FACTOR_4 0x010000 +#define JPEG_CMD_DEC_OP_DATA_FORMAT_SCALE_FACTOR_2 0x020000 +#define JPEG_CMD_DEC_OP_DATA_FORMAT_SCALE_FACTOR_1 0x030000 + +#define JPEG_CMD_DEC_IP_STREAM_BUF_CFG_PART3_NOT_FINAL 0x0000 +#define JPEG_CMD_DEC_IP_STREAM_BUF_CFG_PART3_FINAL 0x0001 + + +typedef struct { + unsigned int cmd_id; + unsigned int img_dimension_cfg; + unsigned int op_data_format; + unsigned int restart_interval; + unsigned int ip_buf_partition_num; + unsigned int ip_stream_buf_cfg_part1; + unsigned int ip_stream_buf_cfg_part2; + unsigned int ip_stream_buf_cfg_part3; + unsigned int op_stream_buf_0_cfg_part1; + unsigned int op_stream_buf_0_cfg_part2; + unsigned int op_stream_buf_0_cfg_part3; + unsigned int op_stream_buf_1_cfg_part1; + unsigned int op_stream_buf_1_cfg_part2; + unsigned int op_stream_buf_1_cfg_part3; + unsigned int luma_qunt_table_0_3; + unsigned int luma_qunt_table_4_7; + unsigned int luma_qunt_table_8_11; + unsigned int luma_qunt_table_12_15; + unsigned int luma_qunt_table_16_19; + unsigned int luma_qunt_table_20_23; + unsigned int luma_qunt_table_24_27; + unsigned int luma_qunt_table_28_31; + unsigned int luma_qunt_table_32_35; + unsigned int luma_qunt_table_36_39; + unsigned int luma_qunt_table_40_43; + unsigned int luma_qunt_table_44_47; + unsigned int luma_qunt_table_48_51; + unsigned int luma_qunt_table_52_55; + unsigned int luma_qunt_table_56_59; + unsigned int luma_qunt_table_60_63; + unsigned int chroma_qunt_table_0_3; + unsigned int chroma_qunt_table_4_7; + unsigned int chroma_qunt_table_8_11; + unsigned int chroma_qunt_table_12_15; + unsigned int chroma_qunt_table_16_19; + unsigned int chroma_qunt_table_20_23; + unsigned int chroma_qunt_table_24_27; + unsigned int chroma_qunt_table_28_31; + unsigned int chroma_qunt_table_32_35; + unsigned int chroma_qunt_table_36_39; + unsigned int chroma_qunt_table_40_43; + unsigned int chroma_qunt_table_44_47; + unsigned int chroma_qunt_table_48_51; + unsigned int chroma_qunt_table_52_55; + unsigned int chroma_qunt_table_56_59; + unsigned int chroma_qunt_table_60_63; + unsigned int luma_dc_hm_code_cnt_table_0_3; + unsigned int luma_dc_hm_code_cnt_table_4_7; + unsigned int luma_dc_hm_code_cnt_table_8_11; + unsigned int luma_dc_hm_code_cnt_table_12_15; + unsigned int luma_dc_hm_code_val_table_0_3; + unsigned int luma_dc_hm_code_val_table_4_7; + unsigned int luma_dc_hm_code_val_table_8_11; + unsigned int chroma_dc_hm_code_cnt_table_0_3; + unsigned int chroma_dc_hm_code_cnt_table_4_7; + unsigned int chroma_dc_hm_code_cnt_table_8_11; + unsigned int chroma_dc_hm_code_cnt_table_12_15; + unsigned int chroma_dc_hm_code_val_table_0_3; + unsigned int chroma_dc_hm_code_val_table_4_7; + unsigned int chroma_dc_hm_code_val_table_8_11; + unsigned int luma_ac_hm_code_cnt_table_0_3; + unsigned int luma_ac_hm_code_cnt_table_4_7; + unsigned int luma_ac_hm_code_cnt_table_8_11; + unsigned int luma_ac_hm_code_cnt_table_12_15; + unsigned int luma_ac_hm_code_val_table_0_3; + unsigned int luma_ac_hm_code_val_table_4_7; + unsigned int luma_ac_hm_code_val_table_8_11; + unsigned int luma_ac_hm_code_val_table_12_15; + unsigned int luma_ac_hm_code_val_table_16_19; + unsigned int luma_ac_hm_code_val_table_20_23; + unsigned int luma_ac_hm_code_val_table_24_27; + unsigned int luma_ac_hm_code_val_table_28_31; + unsigned int luma_ac_hm_code_val_table_32_35; + unsigned int luma_ac_hm_code_val_table_36_39; + unsigned int luma_ac_hm_code_val_table_40_43; + unsigned int luma_ac_hm_code_val_table_44_47; + unsigned int luma_ac_hm_code_val_table_48_51; + unsigned int luma_ac_hm_code_val_table_52_55; + unsigned int luma_ac_hm_code_val_table_56_59; + unsigned int luma_ac_hm_code_val_table_60_63; + unsigned int luma_ac_hm_code_val_table_64_67; + unsigned int luma_ac_hm_code_val_table_68_71; + unsigned int luma_ac_hm_code_val_table_72_75; + unsigned int luma_ac_hm_code_val_table_76_79; + unsigned int luma_ac_hm_code_val_table_80_83; + unsigned int luma_ac_hm_code_val_table_84_87; + unsigned int luma_ac_hm_code_val_table_88_91; + unsigned int luma_ac_hm_code_val_table_92_95; + unsigned int luma_ac_hm_code_val_table_96_99; + unsigned int luma_ac_hm_code_val_table_100_103; + unsigned int luma_ac_hm_code_val_table_104_107; + unsigned int luma_ac_hm_code_val_table_108_111; + unsigned int luma_ac_hm_code_val_table_112_115; + unsigned int luma_ac_hm_code_val_table_116_119; + unsigned int luma_ac_hm_code_val_table_120_123; + unsigned int luma_ac_hm_code_val_table_124_127; + unsigned int luma_ac_hm_code_val_table_128_131; + unsigned int luma_ac_hm_code_val_table_132_135; + unsigned int luma_ac_hm_code_val_table_136_139; + unsigned int luma_ac_hm_code_val_table_140_143; + unsigned int luma_ac_hm_code_val_table_144_147; + unsigned int luma_ac_hm_code_val_table_148_151; + unsigned int luma_ac_hm_code_val_table_152_155; + unsigned int luma_ac_hm_code_val_table_156_159; + unsigned int luma_ac_hm_code_val_table_160_161; + unsigned int chroma_ac_hm_code_cnt_table_0_3; + unsigned int chroma_ac_hm_code_cnt_table_4_7; + unsigned int chroma_ac_hm_code_cnt_table_8_11; + unsigned int chroma_ac_hm_code_cnt_table_12_15; + unsigned int chroma_ac_hm_code_val_table_0_3; + unsigned int chroma_ac_hm_code_val_table_4_7; + unsigned int chroma_ac_hm_code_val_table_8_11; + unsigned int chroma_ac_hm_code_val_table_12_15; + unsigned int chroma_ac_hm_code_val_table_16_19; + unsigned int chroma_ac_hm_code_val_table_20_23; + unsigned int chroma_ac_hm_code_val_table_24_27; + unsigned int chroma_ac_hm_code_val_table_28_31; + unsigned int chroma_ac_hm_code_val_table_32_35; + unsigned int chroma_ac_hm_code_val_table_36_39; + unsigned int chroma_ac_hm_code_val_table_40_43; + unsigned int chroma_ac_hm_code_val_table_44_47; + unsigned int chroma_ac_hm_code_val_table_48_51; + unsigned int chroma_ac_hm_code_val_table_52_55; + unsigned int chroma_ac_hm_code_val_table_56_59; + unsigned int chroma_ac_hm_code_val_table_60_63; + unsigned int chroma_ac_hm_code_val_table_64_67; + unsigned int chroma_ac_hm_code_val_table_68_71; + unsigned int chroma_ac_hm_code_val_table_72_75; + unsigned int chroma_ac_hm_code_val_table_76_79; + unsigned int chroma_ac_hm_code_val_table_80_83; + unsigned int chroma_ac_hm_code_val_table_84_87; + unsigned int chroma_ac_hm_code_val_table_88_91; + unsigned int chroma_ac_hm_code_val_table_92_95; + unsigned int chroma_ac_hm_code_val_table_96_99; + unsigned int chroma_ac_hm_code_val_table_100_103; + unsigned int chroma_ac_hm_code_val_table_104_107; + unsigned int chroma_ac_hm_code_val_table_108_111; + unsigned int chroma_ac_hm_code_val_table_112_115; + unsigned int chroma_ac_hm_code_val_table_116_119; + unsigned int chroma_ac_hm_code_val_table_120_123; + unsigned int chroma_ac_hm_code_val_table_124_127; + unsigned int chroma_ac_hm_code_val_table_128_131; + unsigned int chroma_ac_hm_code_val_table_132_135; + unsigned int chroma_ac_hm_code_val_table_136_139; + unsigned int chroma_ac_hm_code_val_table_140_143; + unsigned int chroma_ac_hm_code_val_table_144_147; + unsigned int chroma_ac_hm_code_val_table_148_151; + unsigned int chroma_ac_hm_code_val_table_152_155; + unsigned int chroma_ac_hm_code_val_table_156_159; + unsigned int chroma_ac_hm_code_val_table_160_161; +} __attribute__((packed)) jpeg_cmd_dec_cfg; + + +/* + * ARM to JPEG configuration commands are passed through the + * uPJpegActionCmdQueue + */ + +/* + * Command to start the encode process + */ + +#define JPEG_CMD_ENC_ENCODE 0x0000 +#define JPEG_CMD_ENC_ENCODE_LEN sizeof(jpeg_cmd_enc_encode) + + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) jpeg_cmd_enc_encode; + + +/* + * Command to transition from current state of encoder to IDLE state + */ + +#define JPEG_CMD_ENC_IDLE 0x0001 +#define JPEG_CMD_ENC_IDLE_LEN sizeof(jpeg_cmd_enc_idle) + + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) jpeg_cmd_enc_idle; + + +/* + * Command to inform the encoder that another buffer is ready + */ + +#define JPEG_CMD_ENC_OP_CONSUMED 0x0002 +#define JPEG_CMD_ENC_OP_CONSUMED_LEN sizeof(jpeg_cmd_enc_op_consumed) + + +typedef struct { + unsigned int cmd_id; + unsigned int op_buf_addr; + unsigned int op_buf_size; +} __attribute__((packed)) jpeg_cmd_enc_op_consumed; + + +/* + * Command to start the decoding process + */ + +#define JPEG_CMD_DEC_DECODE 0x0003 +#define JPEG_CMD_DEC_DECODE_LEN sizeof(jpeg_cmd_dec_decode) + + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) jpeg_cmd_dec_decode; + + +/* + * Command to transition from the current state of decoder to IDLE + */ + +#define JPEG_CMD_DEC_IDLE 0x0004 +#define JPEG_CMD_DEC_IDLE_LEN sizeof(jpeg_cmd_dec_idle) + + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) jpeg_cmd_dec_idle; + + +/* + * Command to inform that an op buffer is ready for use + */ + +#define JPEG_CMD_DEC_OP_CONSUMED 0x0005 +#define JPEG_CMD_DEC_OP_CONSUMED_LEN sizeof(jpeg_cmd_dec_op_consumed) + + +typedef struct { + unsigned int cmd_id; + unsigned int luma_op_buf_addr; + unsigned int luma_op_buf_size; + unsigned int chroma_op_buf_addr; +} __attribute__((packed)) jpeg_cmd_dec_op_consumed; + + +/* + * Command to pass a new ip buffer to the jpeg decoder + */ + +#define JPEG_CMD_DEC_IP 0x0006 +#define JPEG_CMD_DEC_IP_LEN sizeof(jpeg_cmd_dec_ip_len) + +#define JPEG_CMD_EOI_INDICATOR_NOT_END 0x0000 +#define JPEG_CMD_EOI_INDICATOR_END 0x0001 + +typedef struct { + unsigned int cmd_id; + unsigned int ip_buf_addr; + unsigned int ip_buf_size; + unsigned int eoi_indicator; +} __attribute__((packed)) jpeg_cmd_dec_ip; + + + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegmsg.h new file mode 100644 index 0000000000000..c9f0e1ab80a3b --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegmsg.h @@ -0,0 +1,177 @@ +#ifndef QDSP5VIDJPEGMSGI_H +#define QDSP5VIDJPEGMSGI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + J P E G I N T E R N A L M E S S A G E S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of messages + that are sent by JPEG Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5jpegmsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + +when who what, where, why +-------- --- ---------------------------------------------------------- +05/10/08 sv initial version +===========================================================================*/ + +/* + * Messages from JPEG task to ARM through jpeguPMsgQueue + */ + +/* + * Message is ACK for CMD_JPEGE_ENCODE cmd + */ + +#define JPEG_MSG_ENC_ENCODE_ACK 0x0000 +#define JPEG_MSG_ENC_ENCODE_ACK_LEN \ + sizeof(jpeg_msg_enc_encode_ack) + +typedef struct { +} __attribute__((packed)) jpeg_msg_enc_encode_ack; + + +/* + * Message informs the up when op buffer is ready for consumption and + * when encoding is complete or errors + */ + +#define JPEG_MSG_ENC_OP_PRODUCED 0x0001 +#define JPEG_MSG_ENC_OP_PRODUCED_LEN \ + sizeof(jpeg_msg_enc_op_produced) + +#define JPEG_MSGOP_OP_BUF_STATUS_ENC_DONE_PROGRESS 0x0000 +#define JPEG_MSGOP_OP_BUF_STATUS_ENC_DONE_COMPLETE 0x0001 +#define JPEG_MSGOP_OP_BUF_STATUS_ENC_ERR 0x10000 + +typedef struct { + unsigned int op_buf_addr; + unsigned int op_buf_size; + unsigned int op_buf_status; +} __attribute__((packed)) jpeg_msg_enc_op_produced; + + +/* + * Message to ack CMD_JPEGE_IDLE + */ + +#define JPEG_MSG_ENC_IDLE_ACK 0x0002 +#define JPEG_MSG_ENC_IDLE_ACK_LEN sizeof(jpeg_msg_enc_idle_ack) + + +typedef struct { +} __attribute__ ((packed)) jpeg_msg_enc_idle_ack; + + +/* + * Message to indicate the illegal command + */ + +#define JPEG_MSG_ENC_ILLEGAL_COMMAND 0x0003 +#define JPEG_MSG_ENC_ILLEGAL_COMMAND_LEN \ + sizeof(jpeg_msg_enc_illegal_command) + +typedef struct { + unsigned int status; +} __attribute__((packed)) jpeg_msg_enc_illegal_command; + + +/* + * Message to ACK CMD_JPEGD_DECODE + */ + +#define JPEG_MSG_DEC_DECODE_ACK 0x0004 +#define JPEG_MSG_DEC_DECODE_ACK_LEN \ + sizeof(jpeg_msg_dec_decode_ack) + + +typedef struct { +} __attribute__((packed)) jpeg_msg_dec_decode_ack; + + +/* + * Message to inform up that an op buffer is ready for consumption and when + * decoding is complete or an error occurs + */ + +#define JPEG_MSG_DEC_OP_PRODUCED 0x0005 +#define JPEG_MSG_DEC_OP_PRODUCED_LEN \ + sizeof(jpeg_msg_dec_op_produced) + +#define JPEG_MSG_DEC_OP_BUF_STATUS_PROGRESS 0x0000 +#define JPEG_MSG_DEC_OP_BUF_STATUS_DONE 0x0001 + +typedef struct { + unsigned int luma_op_buf_addr; + unsigned int chroma_op_buf_addr; + unsigned int num_mcus; + unsigned int op_buf_status; +} __attribute__((packed)) jpeg_msg_dec_op_produced; + +/* + * Message to ack CMD_JPEGD_IDLE cmd + */ + +#define JPEG_MSG_DEC_IDLE_ACK 0x0006 +#define JPEG_MSG_DEC_IDLE_ACK_LEN sizeof(jpeg_msg_dec_idle_ack) + + +typedef struct { +} __attribute__((packed)) jpeg_msg_dec_idle_ack; + + +/* + * Message to indicate illegal cmd was received + */ + +#define JPEG_MSG_DEC_ILLEGAL_COMMAND 0x0007 +#define JPEG_MSG_DEC_ILLEGAL_COMMAND_LEN \ + sizeof(jpeg_msg_dec_illegal_command) + + +typedef struct { + unsigned int status; +} __attribute__((packed)) jpeg_msg_dec_illegal_command; + +/* + * Message to request up for the next segment of ip bit stream + */ + +#define JPEG_MSG_DEC_IP_REQUEST 0x0008 +#define JPEG_MSG_DEC_IP_REQUEST_LEN \ + sizeof(jpeg_msg_dec_ip_request) + + +typedef struct { +} __attribute__((packed)) jpeg_msg_dec_ip_request; + + + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmcmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmcmdi.h new file mode 100644 index 0000000000000..a54b4bae8a312 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmcmdi.h @@ -0,0 +1,82 @@ +#ifndef QDSP5LPMCMDI_H +#define QDSP5LPMCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + L P M I N T E R N A L C O M M A N D S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by LPM Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5lpmcmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + +when who what, where, why +-------- --- ---------------------------------------------------------- +06/12/08 sv initial version +===========================================================================*/ + + +/* + * Command to start LPM processing based on the config params + */ + +#define LPM_CMD_START 0x0000 +#define LPM_CMD_START_LEN sizeof(lpm_cmd_start) + +#define LPM_CMD_SPATIAL_FILTER_PART_OPMODE_0 0x00000000 +#define LPM_CMD_SPATIAL_FILTER_PART_OPMODE_1 0x00010000 +typedef struct { + unsigned int cmd_id; + unsigned int ip_data_cfg_part1; + unsigned int ip_data_cfg_part2; + unsigned int ip_data_cfg_part3; + unsigned int ip_data_cfg_part4; + unsigned int op_data_cfg_part1; + unsigned int op_data_cfg_part2; + unsigned int op_data_cfg_part3; + unsigned int spatial_filter_part[32]; +} __attribute__((packed)) lpm_cmd_start; + + + +/* + * Command to stop LPM processing + */ + +#define LPM_CMD_IDLE 0x0001 +#define LPM_CMD_IDLE_LEN sizeof(lpm_cmd_idle) + +typedef struct { + unsigned int cmd_id; +} __attribute__((packed)) lpm_cmd_idle; + + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmmsg.h new file mode 100644 index 0000000000000..4f9f13231490b --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmmsg.h @@ -0,0 +1,80 @@ +#ifndef QDSP5LPMMSGI_H +#define QDSP5LPMMSGI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + L P M I N T E R N A L M E S S A G E S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by LPM Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5lpmmsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + +when who what, where, why +-------- --- ---------------------------------------------------------- +06/12/08 sv initial version +===========================================================================*/ + +/* + * Message to acknowledge CMD_LPM_IDLE command + */ + +#define LPM_MSG_IDLE_ACK 0x0000 +#define LPM_MSG_IDLE_ACK_LEN sizeof(lpm_msg_idle_ack) + +typedef struct { +} __attribute__((packed)) lpm_msg_idle_ack; + + +/* + * Message to acknowledge CMD_LPM_START command + */ + + +#define LPM_MSG_START_ACK 0x0001 +#define LPM_MSG_START_ACK_LEN sizeof(lpm_msg_start_ack) + + +typedef struct { +} __attribute__((packed)) lpm_msg_start_ack; + + +/* + * Message to notify the ARM that LPM processing is complete + */ + +#define LPM_MSG_DONE 0x0002 +#define LPM_MSG_DONE_LEN sizeof(lpm_msg_done) + +typedef struct { +} __attribute__((packed)) lpm_msg_done; + + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdeccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdeccmdi.h new file mode 100644 index 0000000000000..cb4622adf659e --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdeccmdi.h @@ -0,0 +1,235 @@ +#ifndef QDSP5VIDDECCMDI_H +#define QDSP5VIDDECCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + V I D E O D E C O D E R I N T E R N A L C O M M A N D S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by VIDDEC Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vdeccmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + +when who what, where, why +-------- --- ---------------------------------------------------------- +05/10/08 ac initial version +===========================================================================*/ + + +/* + * Command to inform VIDDEC that new subframe packet is ready + */ + +#define VIDDEC_CMD_SUBFRAME_PKT 0x0000 +#define VIDDEC_CMD_SUBFRAME_PKT_LEN \ + sizeof(viddec_cmd_subframe_pkt) + +#define VIDDEC_CMD_SF_INFO_1_DM_DMA_STATS_EXCHANGE_FLAG_DM 0x0000 +#define VIDDEC_CMD_SF_INFO_1_DM_DMA_STATS_EXCHANGE_FLAG_DMA 0x0001 + +#define VIDDEC_CMD_SF_INFO_0_SUBFRAME_CONTI 0x0000 +#define VIDDEC_CMD_SF_INFO_0_SUBFRAME_FIRST 0x0001 +#define VIDDEC_CMD_SF_INFO_0_SUBFRAME_LAST 0x0002 +#define VIDDEC_CMD_SF_INFO_0_SUBFRAME_FIRST_AND_LAST 0x0003 + +#define VIDDEC_CMD_CODEC_SELECTION_WORD_MPEG_4 0x0000 +#define VIDDEC_CMD_CODEC_SELECTION_WORD_H_263_P0 0x0001 +#define VIDDEC_CMD_CODEC_SELECTION_WORD_H_264 0x0002 +#define VIDDEC_CMD_CODEC_SELECTION_WORD_H_263_p3 0x0003 +#define VIDDEC_CMD_CODEC_SELECTION_WORD_RV9 0x0004 +#define VIDDEC_CMD_CODEC_SELECTION_WORD_WMV9 0x0005 +#define VIDDEC_CMD_CODEC_SELECTION_WORD_SMCDB 0x0006 +#define VIDDEC_CMD_CODEC_SELECTION_WORD_QFRE 0x0007 +#define VIDDEC_CMD_CODEC_SELECTION_WORD_VLD 0x0008 + +typedef struct { + unsigned short cmd_id; + unsigned short packet_seq_number; + unsigned short codec_instance_id; + unsigned short subframe_packet_size_high; + unsigned short subframe_packet_size_low; + unsigned short subframe_packet_high; + unsigned short subframe_packet_low; + unsigned short subframe_packet_partition; + unsigned short statistics_packet_size_high; + unsigned short statistics_packet_size_low; + unsigned short statistics_packet_high; + unsigned short statistics_packet_low; + unsigned short statistics_partition; + unsigned short subframe_info_1; + unsigned short subframe_info_0; + unsigned short codec_selection_word; + unsigned short num_mbs; +} __attribute__((packed)) viddec_cmd_subframe_pkt; + + +/* + * Command to inform VIDDEC task that post processing is required for the frame + */ + +#define VIDDEC_CMD_PP_ENABLE 0x0001 +#define VIDDEC_CMD_PP_ENABLE_LEN \ + sizeof(viddec_cmd_pp_enable) + +#define VIDDEC_CMD_PP_INFO_0_DM_DMA_LS_EXCHANGE_FLAG_DM 0x0000 +#define VIDDEC_CMD_PP_INFO_0_DM_DMA_LS_EXCHANGE_FLAG_DMA 0x0001 + +typedef struct { + unsigned short cmd_id; + unsigned short packet_seq_num; + unsigned short codec_instance_id; + unsigned short postproc_info_0; + unsigned short codec_selection_word; + unsigned short pp_output_addr_high; + unsigned short pp_output_addr_low; + unsigned short postproc_info_1; + unsigned short load_sharing_packet_size_high; + unsigned short load_sharing_packet_size_low; + unsigned short load_sharing_packet_high; + unsigned short load_sharing_packet_low; + unsigned short load_sharing_partition; + unsigned short pp_param_0; + unsigned short pp_param_1; + unsigned short pp_param_2; + unsigned short pp_param_3; +} __attribute__((packed)) viddec_cmd_pp_enable; + + +/* + * FRAME Header Packet : It is at the start of new frame + */ + +#define VIDDEC_CMD_FRAME_HEADER_PACKET 0x0002 +#define VIDDEC_CMD_FRAME_HEADER_PACKET_LEN \ + sizeof(viddec_cmd_frame_header_packet) + +#define VIDDEC_CMD_FRAME_INFO_0_ERROR_SKIP 0x0000 +#define VIDDEC_CMD_FRAME_INFO_0_ERROR_BLACK 0x0800 + +typedef struct { + unsigned short packet_id; + unsigned short x_dimension; + unsigned short y_dimension; + unsigned short line_width; + unsigned short frame_info_0; + unsigned short frame_buffer_0_high; + unsigned short frame_buffer_0_low; + unsigned short frame_buffer_1_high; + unsigned short frame_buffer_1_low; + unsigned short frame_buffer_2_high; + unsigned short frame_buffer_2_low; + unsigned short frame_buffer_3_high; + unsigned short frame_buffer_3_low; + unsigned short frame_buffer_4_high; + unsigned short frame_buffer_4_low; + unsigned short frame_buffer_5_high; + unsigned short frame_buffer_5_low; + unsigned short frame_buffer_6_high; + unsigned short frame_buffer_6_low; + unsigned short frame_buffer_7_high; + unsigned short frame_buffer_7_low; + unsigned short frame_buffer_8_high; + unsigned short frame_buffer_8_low; + unsigned short frame_buffer_9_high; + unsigned short frame_buffer_9_low; + unsigned short frame_buffer_10_high; + unsigned short frame_buffer_10_low; + unsigned short frame_buffer_11_high; + unsigned short frame_buffer_11_low; + unsigned short frame_buffer_12_high; + unsigned short frame_buffer_12_low; + unsigned short frame_buffer_13_high; + unsigned short frame_buffer_13_low; + unsigned short frame_buffer_14_high; + unsigned short frame_buffer_14_low; + unsigned short frame_buffer_15_high; + unsigned short frame_buffer_15_low; + unsigned short output_frame_buffer_high; + unsigned short output_frame_buffer_low; + unsigned short end_of_packet_marker; +} __attribute__((packed)) viddec_cmd_frame_header_packet; + + +/* + * SLICE HEADER PACKET + * I-Slice and P-Slice + */ + +#define VIDDEC_CMD_SLICE_HEADER_PKT_ISLICE 0x0003 +#define VIDDEC_CMD_SLICE_HEADER_PKT_ISLICE_LEN \ + sizeof(viddec_cmd_slice_header_pkt_islice) + +#define VIDDEC_CMD_ISLICE_INFO_1_MOD_SLICE_TYPE_PSLICE 0x0000 +#define VIDDEC_CMD_ISLICE_INFO_1_MOD_SLICE_TYPE_BSLICE 0x0100 +#define VIDDEC_CMD_ISLICE_INFO_1_MOD_SLICE_TYPE_ISLICE 0x0200 +#define VIDDEC_CMD_ISLICE_INFO_1_MOD_SLICE_TYPE_SPSLICE 0x0300 +#define VIDDEC_CMD_ISLICE_INFO_1_MOD_SLICE_TYPE_SISLICE 0x0400 +#define VIDDEC_CMD_ISLICE_INFO_1_NOPADDING 0x0000 +#define VIDDEC_CMD_ISLICE_INFO_1_PADDING 0x0800 + +#define VIDDEC_CMD_ISLICE_EOP_MARKER 0x7FFF + +typedef struct { + unsigned short cmd_id; + unsigned short packet_id; + unsigned short slice_info_0; + unsigned short slice_info_1; + unsigned short slice_info_2; + unsigned short num_bytes_in_rbsp_high; + unsigned short num_bytes_in_rbsp_low; + unsigned short num_bytes_in_rbsp_consumed; + unsigned short end_of_packet_marker; +} __attribute__((packed)) viddec_cmd_slice_header_pkt_islice; + + +#define VIDDEC_CMD_SLICE_HEADER_PKT_PSLICE 0x0003 +#define VIDDEC_CMD_SLICE_HEADER_PKT_PSLICE_LEN \ + sizeof(viddec_cmd_slice_header_pkt_pslice) + + +typedef struct { + unsigned short cmd_id; + unsigned short packet_id; + unsigned short slice_info_0; + unsigned short slice_info_1; + unsigned short slice_info_2; + unsigned short slice_info_3; + unsigned short refidx_l0_map_tab_info_0; + unsigned short refidx_l0_map_tab_info_1; + unsigned short refidx_l0_map_tab_info_2; + unsigned short refidx_l0_map_tab_info_3; + unsigned short num_bytes_in_rbsp_high; + unsigned short num_bytes_in_rbsp_low; + unsigned short num_bytes_in_rbsp_consumed; + unsigned short end_of_packet_marker; +} __attribute__((packed)) viddec_cmd_slice_header_pkt_pslice; + + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdecmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdecmsg.h new file mode 100644 index 0000000000000..74168b5e6c0b2 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdecmsg.h @@ -0,0 +1,107 @@ +#ifndef QDSP5VIDDECMSGI_H +#define QDSP5VIDDECMSGI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + V I D E O D E C O D E R I N T E R N A L M E S S A G E S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of messages + that are sent by VIDDEC Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vdecmsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + +when who what, where, why +-------- --- ---------------------------------------------------------- +05/10/08 ac initial version +===========================================================================*/ + +/* + * Message to inform ARM which VDEC_SUBFRAME_PKT_CMD processed by VIDDEC TASK + */ + +#define VIDDEC_MSG_SUBF_DONE 0x0000 +#define VIDDEC_MSG_SUBF_DONE_LEN \ + sizeof(viddec_msg_subf_done) + +typedef struct { + unsigned short packet_seq_number; + unsigned short codec_instance_id; +} __attribute__((packed)) viddec_msg_subf_done; + + +/* + * Message to inform ARM one frame has been decoded + */ + +#define VIDDEC_MSG_FRAME_DONE 0x0001 +#define VIDDEC_MSG_FRAME_DONE_LEN \ + sizeof(viddec_msg_frame_done) + +typedef struct { + unsigned short packet_seq_number; + unsigned short codec_instance_id; +} __attribute__((packed)) viddec_msg_frame_done; + + +/* + * Message to inform ARM that post processing frame has been decoded + */ + +#define VIDDEC_MSG_PP_ENABLE_CMD_DONE 0x0002 +#define VIDDEC_MSG_PP_ENABLE_CMD_DONE_LEN \ + sizeof(viddec_msg_pp_enable_cmd_done) + +typedef struct { + unsigned short packet_seq_number; + unsigned short codec_instance_id; +} __attribute__((packed)) viddec_msg_pp_enable_cmd_done; + + +/* + * Message to inform ARM that one post processing frame has been decoded + */ + + +#define VIDDEC_MSG_PP_FRAME_DONE 0x0003 +#define VIDDEC_MSG_PP_FRAME_DONE_LEN \ + sizeof(viddec_msg_pp_frame_done) + +#define VIDDEC_MSG_DISP_WORTHY_DISP 0x0000 +#define VIDDEC_MSG_DISP_WORTHY_DISP_NONE 0xFFFF + + +typedef struct { + unsigned short packet_seq_number; + unsigned short codec_instance_id; + unsigned short display_worthy; +} __attribute__((packed)) viddec_msg_pp_frame_done; + + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfecmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfecmdi.h new file mode 100644 index 0000000000000..12c0937670727 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfecmdi.h @@ -0,0 +1,910 @@ +#ifndef QDSP5VFECMDI_H +#define QDSP5VFECMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + V F E I N T E R N A L C O M M A N D S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by VFE Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vfecmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + +when who what, where, why +-------- --- ---------------------------------------------------------- +06/12/08 sv initial version +===========================================================================*/ + +/****************************************************************************** + * Commands through vfeCommandScaleQueue + *****************************************************************************/ + +/* + * Command to program scaler for op1 . max op of scaler is VGA + */ + + +#define VFE_CMD_SCALE_OP1_CFG 0x0000 +#define VFE_CMD_SCALE_OP1_CFG_LEN \ + sizeof(vfe_cmd_scale_op1_cfg) + +#define VFE_CMD_SCALE_OP1_SEL_IP_SEL_Y_STANDARD 0x0000 +#define VFE_CMD_SCALE_OP1_SEL_IP_SEL_Y_CASCADED 0x0001 +#define VFE_CMD_SCALE_OP1_SEL_H_Y_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP1_SEL_H_Y_SCALER_ENA 0x0002 +#define VFE_CMD_SCALE_OP1_SEL_H_PP_Y_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP1_SEL_H_PP_Y_SCALER_ENA 0x0004 +#define VFE_CMD_SCALE_OP1_SEL_V_Y_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP1_SEL_V_Y_SCALER_ENA 0x0008 +#define VFE_CMD_SCALE_OP1_SEL_V_PP_Y_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP1_SEL_V_PP_Y_SCALER_ENA 0x0010 +#define VFE_CMD_SCALE_OP1_SEL_IP_SEL_CBCR_STANDARD 0x0000 +#define VFE_CMD_SCALE_OP1_SEL_IP_SEL_CBCR_CASCADED 0x0020 +#define VFE_CMD_SCALE_OP1_SEL_H_CBCR_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP1_SEL_H_CBCR_SCALER_ENA 0x0040 +#define VFE_CMD_SCALE_OP1_SEL_V_CBCR_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP1_SEL_V_CBCR_SCALER_ENA 0x0080 + +#define VFE_CMD_OP1_PP_Y_SCALER_CFG_PART1_DONT_LOAD_COEFFS 0x80000000 +#define VFE_CMD_OP1_PP_Y_SCALER_CFG_PART1_LOAD_COEFFS 0x80000000 + +typedef struct { + unsigned int cmd_id; + unsigned int scale_op1_sel; + unsigned int y_scaler_cfg_part1; + unsigned int y_scaler_cfg_part2; + unsigned int cbcr_scaler_cfg_part1; + unsigned int cbcr_scaler_cfg_part2; + unsigned int cbcr_scaler_cfg_part3; + unsigned int pp_y_scaler_cfg_part1; + unsigned int pp_y_scaler_cfg_part2; + unsigned int y_scaler_v_coeff_bank_part1[16]; + unsigned int y_scaler_v_coeff_bank_part2[16]; + unsigned int y_scaler_h_coeff_bank_part1[16]; + unsigned int y_scaler_h_coeff_bank_part2[16]; +} __attribute__((packed)) vfe_cmd_scale_op1_cfg; + + +/* + * Command to program scaler for op2 + */ + +#define VFE_CMD_SCALE_OP2_CFG 0x0001 +#define VFE_CMD_SCALE_OP2_CFG_LEN \ + sizeof(vfe_cmd_scale_op2_cfg) + +#define VFE_CMD_SCALE_OP2_SEL_IP_SEL_Y_STANDARD 0x0000 +#define VFE_CMD_SCALE_OP2_SEL_IP_SEL_Y_CASCADED 0x0001 +#define VFE_CMD_SCALE_OP2_SEL_H_Y_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP2_SEL_H_Y_SCALER_ENA 0x0002 +#define VFE_CMD_SCALE_OP2_SEL_H_PP_Y_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP2_SEL_H_PP_Y_SCALER_ENA 0x0004 +#define VFE_CMD_SCALE_OP2_SEL_V_Y_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP2_SEL_V_Y_SCALER_ENA 0x0008 +#define VFE_CMD_SCALE_OP2_SEL_V_PP_Y_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP2_SEL_V_PP_Y_SCALER_ENA 0x0010 +#define VFE_CMD_SCALE_OP2_SEL_IP_SEL_CBCR_STANDARD 0x0000 +#define VFE_CMD_SCALE_OP2_SEL_IP_SEL_CBCR_CASCADED 0x0020 +#define VFE_CMD_SCALE_OP2_SEL_H_CBCR_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP2_SEL_H_CBCR_SCALER_ENA 0x0040 +#define VFE_CMD_SCALE_OP2_SEL_V_CBCR_SCALER_DIS 0x0000 +#define VFE_CMD_SCALE_OP2_SEL_V_CBCR_SCALER_ENA 0x0080 + +#define VFE_CMD_OP2_PP_Y_SCALER_CFG_PART1_DONT_LOAD_COEFFS 0x80000000 +#define VFE_CMD_OP2_PP_Y_SCALER_CFG_PART1_LOAD_COEFFS 0x80000000 + +typedef struct { + unsigned int cmd_id; + unsigned int scale_op2_sel; + unsigned int y_scaler_cfg_part1; + unsigned int y_scaler_cfg_part2; + unsigned int cbcr_scaler_cfg_part1; + unsigned int cbcr_scaler_cfg_part2; + unsigned int cbcr_scaler_cfg_part3; + unsigned int pp_y_scaler_cfg_part1; + unsigned int pp_y_scaler_cfg_part2; + unsigned int y_scaler_v_coeff_bank_part1[16]; + unsigned int y_scaler_v_coeff_bank_part2[16]; + unsigned int y_scaler_h_coeff_bank_part1[16]; + unsigned int y_scaler_h_coeff_bank_part2[16]; +} __attribute__((packed)) vfe_cmd_scale_op2_cfg; + + +/****************************************************************************** + * Commands through vfeCommandTableQueue + *****************************************************************************/ + +/* + * Command to program the AXI ip paths + */ + +#define VFE_CMD_AXI_IP_CFG 0x0000 +#define VFE_CMD_AXI_IP_CFG_LEN sizeof(vfe_cmd_axi_ip_cfg) + +#define VFE_CMD_IP_SEL_IP_FORMAT_8 0x0000 +#define VFE_CMD_IP_SEL_IP_FORMAT_10 0x0001 +#define VFE_CMD_IP_SEL_IP_FORMAT_12 0x0002 + +typedef struct { + unsigned int cmd_id; + unsigned int ip_sel; + unsigned int ip_cfg_part1; + unsigned int ip_cfg_part2; + unsigned int ip_unpack_cfg_part[6]; + unsigned int ip_buf_addr[8]; +} __attribute__ ((packed)) vfe_cmd_axi_ip_cfg; + + +/* + * Command to program axi op paths + */ + +#define VFE_CMD_AXI_OP_CFG 0x0001 +#define VFE_CMD_AXI_OP_CFG_LEN sizeof(vfe_cmd_axi_op_cfg) + +#define VFE_CMD_OP_SEL_OP1 0x0000 +#define VFE_CMD_OP_SEL_OP2 0x0001 +#define VFE_CMD_OP_SEL_OP1_OP2 0x0002 +#define VFE_CMD_OP_SEL_CTOA 0x0003 +#define VFE_CMD_OP_SEL_CTOA_OP1 0x0004 +#define VFE_CMD_OP_SEL_CTOA_OP2 0x0005 +#define VFE_CMD_OP_SEL_OP_FORMAT_8 0x0000 +#define VFE_CMD_OP_SEL_OP_FORMAT_10 0x0008 +#define VFE_CMD_OP_SEL_OP_FORMAT_12 0x0010 + + +typedef struct { + unsigned int cmd_id; + unsigned int op_sel; + unsigned int op1_y_cfg_part1; + unsigned int op1_y_cfg_part2; + unsigned int op1_cbcr_cfg_part1; + unsigned int op1_cbcr_cfg_part2; + unsigned int op2_y_cfg_part1; + unsigned int op2_y_cfg_part2; + unsigned int op2_cbcr_cfg_part1; + unsigned int op2_cbcr_cfg_part2; + unsigned int op1_buf1_addr[16]; + unsigned int op2_buf1_addr[16]; +} __attribute__((packed)) vfe_cmd_axi_op_cfg; + + + + +/* + * Command to program the roll off correction module + */ + +#define VFE_CMD_ROLLOFF_CFG 0x0002 +#define VFE_CMD_ROLLOFF_CFG_LEN \ + sizeof(vfe_cmd_rolloff_cfg) + + +typedef struct { + unsigned int cmd_id; + unsigned int correction_opt_center_pos; + unsigned int radius_square_entry[32]; + unsigned int red_table_entry[32]; + unsigned int green_table_entry[32]; + unsigned int blue_table_entry[32]; +} __attribute__((packed)) vfe_cmd_rolloff_cfg; + +/* + * Command to program RGB gamma table + */ + +#define VFE_CMD_RGB_GAMMA_CFG 0x0003 +#define VFE_CMD_RGB_GAMMA_CFG_LEN \ + sizeof(vfe_cmd_rgb_gamma_cfg) + +#define VFE_CMD_RGB_GAMMA_SEL_LINEAR 0x0000 +#define VFE_CMD_RGB_GAMMA_SEL_PW_LINEAR 0x0001 +typedef struct { + unsigned int cmd_id; + unsigned int rgb_gamma_sel; + unsigned int rgb_gamma_entry[256]; +} __attribute__((packed)) vfe_cmd_rgb_gamma_cfg; + + +/* + * Command to program luma gamma table for the noise reduction path + */ + +#define VFE_CMD_Y_GAMMA_CFG 0x0004 +#define VFE_CMD_Y_GAMMA_CFG_LEN \ + sizeof(vfe_cmd_y_gamma_cfg) + +#define VFE_CMD_Y_GAMMA_SEL_LINEAR 0x0000 +#define VFE_CMD_Y_GAMMA_SEL_PW_LINEAR 0x0001 + +typedef struct { + unsigned int cmd_id; + unsigned int y_gamma_sel; + unsigned int y_gamma_entry[256]; +} __attribute__((packed)) vfe_cmd_y_gamma_cfg; + + + +/****************************************************************************** + * Commands through vfeCommandQueue + *****************************************************************************/ + +/* + * Command to reset the VFE to a known good state.All previously programmed + * Params will be lost + */ + + +#define VFE_CMD_RESET 0x0000 +#define VFE_CMD_RESET_LEN sizeof(vfe_cmd_reset) + + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) vfe_cmd_reset; + + +/* + * Command to start VFE processing based on the config params + */ + + +#define VFE_CMD_START 0x0001 +#define VFE_CMD_START_LEN sizeof(vfe_cmd_start) + +#define VFE_CMD_STARTUP_PARAMS_SRC_CAMIF 0x0000 +#define VFE_CMD_STARTUP_PARAMS_SRC_AXI 0x0001 +#define VFE_CMD_STARTUP_PARAMS_MODE_CONTINUOUS 0x0000 +#define VFE_CMD_STARTUP_PARAMS_MODE_SNAPSHOT 0x0002 + +#define VFE_CMD_IMAGE_PL_BLACK_LVL_CORR_DIS 0x0000 +#define VFE_CMD_IMAGE_PL_BLACK_LVL_CORR_ENA 0x0001 +#define VFE_CMD_IMAGE_PL_ROLLOFF_CORR_DIS 0x0000 +#define VFE_CMD_IMAGE_PL_ROLLOFF_CORR_ENA 0x0002 +#define VFE_CMD_IMAGE_PL_WHITE_BAL_DIS 0x0000 +#define VFE_CMD_IMAGE_PL_WHITE_BAL_ENA 0x0004 +#define VFE_CMD_IMAGE_PL_RGB_GAMMA_DIS 0x0000 +#define VFE_CMD_IMAGE_PL_RGB_GAMMA_ENA 0x0008 +#define VFE_CMD_IMAGE_PL_LUMA_NOISE_RED_PATH_DIS 0x0000 +#define VFE_CMD_IMAGE_PL_LUMA_NOISE_RED_PATH_ENA 0x0010 +#define VFE_CMD_IMAGE_PL_ADP_FILTER_DIS 0x0000 +#define VFE_CMD_IMAGE_PL_ADP_FILTER_ENA 0x0020 +#define VFE_CMD_IMAGE_PL_CHROMA_SAMP_DIS 0x0000 +#define VFE_CMD_IMAGE_PL_CHROMA_SAMP_ENA 0x0040 + + +typedef struct { + unsigned int cmd_id; + unsigned int startup_params; + unsigned int image_pipeline; + unsigned int frame_dimension; +} __attribute__((packed)) vfe_cmd_start; + + +/* + * Command to halt all processing + */ + +#define VFE_CMD_STOP 0x0002 +#define VFE_CMD_STOP_LEN sizeof(vfe_cmd_stop) + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) vfe_cmd_stop; + + +/* + * Command to commit the params that have been programmed to take + * effect on the next frame + */ + +#define VFE_CMD_UPDATE 0x0003 +#define VFE_CMD_UPDATE_LEN sizeof(vfe_cmd_update) + + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) vfe_cmd_update; + + +/* + * Command to program CAMIF module + */ + +#define VFE_CMD_CAMIF_CFG 0x0004 +#define VFE_CMD_CAMIF_CFG_LEN sizeof(vfe_cmd_camif_cfg) + +#define VFE_CMD_CFG_VSYNC_SYNC_EDGE_HIGH 0x0000 +#define VFE_CMD_CFG_VSYNC_SYNC_EDGE_LOW 0x0002 +#define VFE_CMD_CFG_HSYNC_SYNC_EDGE_HIGH 0x0000 +#define VFE_CMD_CFG_HSYNC_SYNC_EDGE_LOW 0x0004 +#define VFE_CMD_CFG_SYNC_MODE_APS 0x0000 +#define VFE_CMD_CFG_SYNC_MODE_EFS 0X0008 +#define VFE_CMD_CFG_SYNC_MODE_ELS 0x0010 +#define VFE_CMD_CFG_SYNC_MODE_RVD 0x0018 +#define VFE_CMD_CFG_VFE_SUBSAMP_EN_DIS 0x0000 +#define VFE_CMD_CFG_VFE_SUBSAMP_EN_ENA 0x0020 +#define VFE_CMD_CFG_BUS_SUBSAMP_EN_DIS 0x0000 +#define VFE_CMD_CFG_BUS_SUBSAMP_EN_ENA 0x0080 +#define VFE_CMD_CFG_IRQ_SUBSAMP_EN_DIS 0x0000 +#define VFE_CMD_CFG_IRQ_SUBSAMP_EN_ENA 0x0800 + +#define VFE_CMD_SUBSAMP2_CFG_PIXEL_SKIP_16 0x0000 +#define VFE_CMD_SUBSAMP2_CFG_PIXEL_SKIP_12 0x0010 + +#define VFE_CMD_EPOCH_IRQ_1_DIS 0x0000 +#define VFE_CMD_EPOCH_IRQ_1_ENA 0x4000 +#define VFE_CMD_EPOCH_IRQ_2_DIS 0x0000 +#define VFE_CMD_EPOCH_IRQ_2_ENA 0x8000 + +typedef struct { + unsigned int cmd_id; + unsigned int cfg; + unsigned int efs_cfg; + unsigned int frame_cfg; + unsigned int window_width_cfg; + unsigned int window_height_cfg; + unsigned int subsamp1_cfg; + unsigned int subsamp2_cfg; + unsigned int epoch_irq; +} __attribute__((packed)) vfe_cmd_camif_cfg; + + + +/* + * Command to program the black level module + */ + +#define VFE_CMD_BLACK_LVL_CFG 0x0005 +#define VFE_CMD_BLACK_LVL_CFG_LEN sizeof(vfe_cmd_black_lvl_cfg) + +#define VFE_CMD_BL_SEL_MANUAL 0x0000 +#define VFE_CMD_BL_SEL_AUTO 0x0001 + +typedef struct { + unsigned int cmd_id; + unsigned int black_lvl_sel; + unsigned int cfg_part[3]; +} __attribute__((packed)) vfe_cmd_black_lvl_cfg; + + +/* + * Command to program the active region by cropping the region of interest + */ + +#define VFE_CMD_ACTIVE_REGION_CFG 0x0006 +#define VFE_CMD_ACTIVE_REGION_CFG_LEN \ + sizeof(vfe_cmd_active_region_cfg) + + +typedef struct { + unsigned int cmd_id; + unsigned int cfg_part1; + unsigned int cfg_part2; +} __attribute__((packed)) vfe_cmd_active_region_cfg; + + + +/* + * Command to program the defective pixel correction(DPC) , + * adaptive bayer filter (ABF) and demosaic modules + */ + +#define VFE_CMD_DEMOSAIC_CFG 0x0007 +#define VFE_CMD_DEMOSAIC_CFG_LEN sizeof(vfe_cmd_demosaic_cfg) + +#define VFE_CMD_DEMOSAIC_PART1_ABF_EN_DIS 0x0000 +#define VFE_CMD_DEMOSAIC_PART1_ABF_EN_ENA 0x0001 +#define VFE_CMD_DEMOSAIC_PART1_DPC_EN_DIS 0x0000 +#define VFE_CMD_DEMOSAIC_PART1_DPC_EN_ENA 0x0002 +#define VFE_CMD_DEMOSAIC_PART1_FORCE_ABF_OFF 0x0000 +#define VFE_CMD_DEMOSAIC_PART1_FORCE_ABF_ON 0x0004 +#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_1 0x00000000 +#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_2 0x10000000 +#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_4 0x20000000 +#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_8 0x30000000 +#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_1_2 0x50000000 +#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_1_4 0x60000000 +#define VFE_CMD_DEMOSAIC_PART1_SLOPE_SHIFT_1_8 0x70000000 + +typedef struct { + unsigned int cmd_id; + unsigned int demosaic_part1; + unsigned int demosaic_part2; + unsigned int demosaic_part3; + unsigned int demosaic_part4; + unsigned int demosaic_part5; +} __attribute__((packed)) vfe_cmd_demosaic_cfg; + + +/* + * Command to program the ip format + */ + +#define VFE_CMD_IP_FORMAT_CFG 0x0008 +#define VFE_CMD_IP_FORMAT_CFG_LEN \ + sizeof(vfe_cmd_ip_format_cfg) + +#define VFE_CMD_IP_FORMAT_SEL_RGRG 0x0000 +#define VFE_CMD_IP_FORMAT_SEL_GRGR 0x0001 +#define VFE_CMD_IP_FORMAT_SEL_BGBG 0x0002 +#define VFE_CMD_IP_FORMAT_SEL_GBGB 0x0003 +#define VFE_CMD_IP_FORMAT_SEL_YCBYCR 0x0004 +#define VFE_CMD_IP_FORMAT_SEL_YCRYCB 0x0005 +#define VFE_CMD_IP_FORMAT_SEL_CBYCRY 0x0006 +#define VFE_CMD_IP_FORMAT_SEL_CRYCBY 0x0007 +#define VFE_CMD_IP_FORMAT_SEL_NO_CHROMA 0x0000 +#define VFE_CMD_IP_FORMAT_SEL_CHROMA 0x0008 + + +typedef struct { + unsigned int cmd_id; + unsigned int ip_format_sel; + unsigned int balance_gains_part1; + unsigned int balance_gains_part2; +} __attribute__((packed)) vfe_cmd_ip_format_cfg; + + + +/* + * Command to program max and min allowed op values + */ + +#define VFE_CMD_OP_CLAMP_CFG 0x0009 +#define VFE_CMD_OP_CLAMP_CFG_LEN \ + sizeof(vfe_cmd_op_clamp_cfg) + +typedef struct { + unsigned int cmd_id; + unsigned int op_clamp_max; + unsigned int op_clamp_min; +} __attribute__((packed)) vfe_cmd_op_clamp_cfg; + + +/* + * Command to program chroma sub sample module + */ + +#define VFE_CMD_CHROMA_SUBSAMPLE_CFG 0x000A +#define VFE_CMD_CHROMA_SUBSAMPLE_CFG_LEN \ + sizeof(vfe_cmd_chroma_subsample_cfg) + +#define VFE_CMD_CHROMA_SUBSAMP_SEL_H_INTERESTIAL_SAMPS 0x0000 +#define VFE_CMD_CHROMA_SUBSAMP_SEL_H_COSITED_SAMPS 0x0001 +#define VFE_CMD_CHROMA_SUBSAMP_SEL_V_INTERESTIAL_SAMPS 0x0000 +#define VFE_CMD_CHROMA_SUBSAMP_SEL_V_COSITED_SAMPS 0x0002 +#define VFE_CMD_CHROMA_SUBSAMP_SEL_H_SUBSAMP_DIS 0x0000 +#define VFE_CMD_CHROMA_SUBSAMP_SEL_H_SUBSAMP_ENA 0x0004 +#define VFE_CMD_CHROMA_SUBSAMP_SEL_V_SUBSAMP_DIS 0x0000 +#define VFE_CMD_CHROMA_SUBSAMP_SEL_V_SUBSAMP_ENA 0x0008 + +typedef struct { + unsigned int cmd_id; + unsigned int chroma_subsamp_sel; +} __attribute__((packed)) vfe_cmd_chroma_subsample_cfg; + + +/* + * Command to program the white balance module + */ + +#define VFE_CMD_WHITE_BALANCE_CFG 0x000B +#define VFE_CMD_WHITE_BALANCE_CFG_LEN \ + sizeof(vfe_cmd_white_balance_cfg) + +typedef struct { + unsigned int cmd_id; + unsigned int white_balance_gains; +} __attribute__((packed)) vfe_cmd_white_balance_cfg; + + +/* + * Command to program the color processing module + */ + +#define VFE_CMD_COLOR_PROCESS_CFG 0x000C +#define VFE_CMD_COLOR_PROCESS_CFG_LEN \ + sizeof(vfe_cmd_color_process_cfg) + +#define VFE_CMD_COLOR_CORRE_PART7_Q7_FACTORS 0x0000 +#define VFE_CMD_COLOR_CORRE_PART7_Q8_FACTORS 0x0001 +#define VFE_CMD_COLOR_CORRE_PART7_Q9_FACTORS 0x0002 +#define VFE_CMD_COLOR_CORRE_PART7_Q10_FACTORS 0x0003 + +typedef struct { + unsigned int cmd_id; + unsigned int color_correction_part1; + unsigned int color_correction_part2; + unsigned int color_correction_part3; + unsigned int color_correction_part4; + unsigned int color_correction_part5; + unsigned int color_correction_part6; + unsigned int color_correction_part7; + unsigned int chroma_enhance_part1; + unsigned int chroma_enhance_part2; + unsigned int chroma_enhance_part3; + unsigned int chroma_enhance_part4; + unsigned int chroma_enhance_part5; + unsigned int luma_calc_part1; + unsigned int luma_calc_part2; +} __attribute__((packed)) vfe_cmd_color_process_cfg; + + +/* + * Command to program adaptive filter module + */ + +#define VFE_CMD_ADP_FILTER_CFG 0x000D +#define VFE_CMD_ADP_FILTER_CFG_LEN \ + sizeof(vfe_cmd_adp_filter_cfg) + +#define VFE_CMD_ASF_CFG_PART_SMOOTH_FILTER_DIS 0x0000 +#define VFE_CMD_ASF_CFG_PART_SMOOTH_FILTER_ENA 0x0001 +#define VFE_CMD_ASF_CFG_PART_NO_SHARP_MODE 0x0000 +#define VFE_CMD_ASF_CFG_PART_SINGLE_FILTER 0x0002 +#define VFE_CMD_ASF_CFG_PART_DUAL_FILTER 0x0004 +#define VFE_CMD_ASF_CFG_PART_SHARP_MODE 0x0007 + +typedef struct { + unsigned int cmd_id; + unsigned int asf_cfg_part[7]; +} __attribute__((packed)) vfe_cmd_adp_filter_cfg; + + +/* + * Command to program for frame skip pattern for op1 and op2 + */ + +#define VFE_CMD_FRAME_SKIP_CFG 0x000E +#define VFE_CMD_FRAME_SKIP_CFG_LEN \ + sizeof(vfe_cmd_frame_skip_cfg) + +typedef struct { + unsigned int cmd_id; + unsigned int frame_skip_pattern_op1; + unsigned int frame_skip_pattern_op2; +} __attribute__((packed)) vfe_cmd_frame_skip_cfg; + + +/* + * Command to program field-of-view crop for digital zoom + */ + +#define VFE_CMD_FOV_CROP 0x000F +#define VFE_CMD_FOV_CROP_LEN sizeof(vfe_cmd_fov_crop) + +typedef struct { + unsigned int cmd_id; + unsigned int fov_crop_part1; + unsigned int fov_crop_part2; +} __attribute__((packed)) vfe_cmd_fov_crop; + + + +/* + * Command to program auto focus(AF) statistics module + */ + +#define VFE_CMD_STATS_AUTOFOCUS_CFG 0x0010 +#define VFE_CMD_STATS_AUTOFOCUS_CFG_LEN \ + sizeof(vfe_cmd_stats_autofocus_cfg) + +#define VFE_CMD_AF_STATS_SEL_STATS_DIS 0x0000 +#define VFE_CMD_AF_STATS_SEL_STATS_ENA 0x0001 +#define VFE_CMD_AF_STATS_SEL_PRI_FIXED 0x0000 +#define VFE_CMD_AF_STATS_SEL_PRI_VAR 0x0002 +#define VFE_CMD_AF_STATS_CFG_PART_METRIC_SUM 0x00000000 +#define VFE_CMD_AF_STATS_CFG_PART_METRIC_MAX 0x00200000 + +typedef struct { + unsigned int cmd_id; + unsigned int af_stats_sel; + unsigned int af_stats_cfg_part[8]; + unsigned int af_stats_op_buf_hdr; + unsigned int af_stats_op_buf[3]; +} __attribute__((packed)) vfe_cmd_stats_autofocus_cfg; + + +/* + * Command to program White balance(wb) and exposure (exp) + * statistics module + */ + +#define VFE_CMD_STATS_WB_EXP_CFG 0x0011 +#define VFE_CMD_STATS_WB_EXP_CFG_LEN \ + sizeof(vfe_cmd_stats_wb_exp_cfg) + +#define VFE_CMD_WB_EXP_STATS_SEL_STATS_DIS 0x0000 +#define VFE_CMD_WB_EXP_STATS_SEL_STATS_ENA 0x0001 +#define VFE_CMD_WB_EXP_STATS_SEL_PRI_FIXED 0x0000 +#define VFE_CMD_WB_EXP_STATS_SEL_PRI_VAR 0x0002 + +#define VFE_CMD_WB_EXP_STATS_CFG_PART1_EXP_REG_8_8 0x0000 +#define VFE_CMD_WB_EXP_STATS_CFG_PART1_EXP_REG_16_16 0x0001 +#define VFE_CMD_WB_EXP_STATS_CFG_PART1_EXP_SREG_8_8 0x0000 +#define VFE_CMD_WB_EXP_STATS_CFG_PART1_EXP_SREG_4_4 0x0002 + +typedef struct { + unsigned int cmd_id; + unsigned int wb_exp_stats_sel; + unsigned int wb_exp_stats_cfg_part1; + unsigned int wb_exp_stats_cfg_part2; + unsigned int wb_exp_stats_cfg_part3; + unsigned int wb_exp_stats_cfg_part4; + unsigned int wb_exp_stats_op_buf_hdr; + unsigned int wb_exp_stats_op_buf[3]; +} __attribute__((packed)) vfe_cmd_stats_wb_exp_cfg; + + +/* + * Command to program histogram(hg) stats module + */ + +#define VFE_CMD_STATS_HG_CFG 0x0012 +#define VFE_CMD_STATS_HG_CFG_LEN \ + sizeof(vfe_cmd_stats_hg_cfg) + +#define VFE_CMD_HG_STATS_SEL_PRI_FIXED 0x0000 +#define VFE_CMD_HG_STATS_SEL_PRI_VAR 0x0002 + +typedef struct { + unsigned int cmd_id; + unsigned int hg_stats_sel; + unsigned int hg_stats_cfg_part1; + unsigned int hg_stats_cfg_part2; + unsigned int hg_stats_op_buf_hdr; + unsigned int hg_stats_op_buf; +} __attribute__((packed)) vfe_cmd_stats_hg_cfg; + + +/* + * Command to acknowledge last MSG_VFE_OP1 message + */ + +#define VFE_CMD_OP1_ACK 0x0013 +#define VFE_CMD_OP1_ACK_LEN sizeof(vfe_cmd_op1_ack) + +typedef struct { + unsigned int cmd_id; + unsigned int op1_buf_y_addr; + unsigned int op1_buf_cbcr_addr; +} __attribute__((packed)) vfe_cmd_op1_ack; + + + +/* + * Command to acknowledge last MSG_VFE_OP2 message + */ + +#define VFE_CMD_OP2_ACK 0x0014 +#define VFE_CMD_OP2_ACK_LEN sizeof(vfe_cmd_op2_ack) + +typedef struct { + unsigned int cmd_id; + unsigned int op2_buf_y_addr; + unsigned int op2_buf_cbcr_addr; +} __attribute__((packed)) vfe_cmd_op2_ack; + + + +/* + * Command to acknowledge MSG_VFE_STATS_AUTOFOCUS msg + */ + +#define VFE_CMD_STATS_AF_ACK 0x0015 +#define VFE_CMD_STATS_AF_ACK_LEN sizeof(vfe_cmd_stats_af_ack) + + +typedef struct { + unsigned int cmd_id; + unsigned int af_stats_op_buf; +} __attribute__((packed)) vfe_cmd_stats_af_ack; + + +/* + * Command to acknowledge MSG_VFE_STATS_WB_EXP msg + */ + +#define VFE_CMD_STATS_WB_EXP_ACK 0x0016 +#define VFE_CMD_STATS_WB_EXP_ACK_LEN sizeof(vfe_cmd_stats_wb_exp_ack) + +typedef struct { + unsigned int cmd_id; + unsigned int wb_exp_stats_op_buf; +} __attribute__((packed)) vfe_cmd_stats_wb_exp_ack; + + +/* + * Command to acknowledge MSG_VFE_EPOCH1 message + */ + +#define VFE_CMD_EPOCH1_ACK 0x0017 +#define VFE_CMD_EPOCH1_ACK_LEN sizeof(vfe_cmd_epoch1_ack) + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) vfe_cmd_epoch1_ack; + + +/* + * Command to acknowledge MSG_VFE_EPOCH2 message + */ + +#define VFE_CMD_EPOCH2_ACK 0x0018 +#define VFE_CMD_EPOCH2_ACK_LEN sizeof(vfe_cmd_epoch2_ack) + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) vfe_cmd_epoch2_ack; + + + +/* + * Command to configure, enable or disable synchronous timer1 + */ + +#define VFE_CMD_SYNC_TIMER1_CFG 0x0019 +#define VFE_CMD_SYNC_TIMER1_CFG_LEN \ + sizeof(vfe_cmd_sync_timer1_cfg) + +#define VFE_CMD_SYNC_T1_CFG_PART1_TIMER_DIS 0x0000 +#define VFE_CMD_SYNC_T1_CFG_PART1_TIMER_ENA 0x0001 +#define VFE_CMD_SYNC_T1_CFG_PART1_POL_HIGH 0x0000 +#define VFE_CMD_SYNC_T1_CFG_PART1_POL_LOW 0x0002 + +typedef struct { + unsigned int cmd_id; + unsigned int sync_t1_cfg_part1; + unsigned int sync_t1_h_sync_countdown; + unsigned int sync_t1_pclk_countdown; + unsigned int sync_t1_duration; +} __attribute__((packed)) vfe_cmd_sync_timer1_cfg; + + +/* + * Command to configure, enable or disable synchronous timer1 + */ + +#define VFE_CMD_SYNC_TIMER2_CFG 0x001A +#define VFE_CMD_SYNC_TIMER2_CFG_LEN \ + sizeof(vfe_cmd_sync_timer2_cfg) + +#define VFE_CMD_SYNC_T2_CFG_PART1_TIMER_DIS 0x0000 +#define VFE_CMD_SYNC_T2_CFG_PART1_TIMER_ENA 0x0001 +#define VFE_CMD_SYNC_T2_CFG_PART1_POL_HIGH 0x0000 +#define VFE_CMD_SYNC_T2_CFG_PART1_POL_LOW 0x0002 + +typedef struct { + unsigned int cmd_id; + unsigned int sync_t2_cfg_part1; + unsigned int sync_t2_h_sync_countdown; + unsigned int sync_t2_pclk_countdown; + unsigned int sync_t2_duration; +} __attribute__((packed)) vfe_cmd_sync_timer2_cfg; + + +/* + * Command to configure and start asynchronous timer1 + */ + +#define VFE_CMD_ASYNC_TIMER1_START 0x001B +#define VFE_CMD_ASYNC_TIMER1_START_LEN \ + sizeof(vfe_cmd_async_timer1_start) + +#define VFE_CMD_ASYNC_T1_POLARITY_A_HIGH 0x0000 +#define VFE_CMD_ASYNC_T1_POLARITY_A_LOW 0x0001 +#define VFE_CMD_ASYNC_T1_POLARITY_B_HIGH 0x0000 +#define VFE_CMD_ASYNC_T1_POLARITY_B_LOW 0x0002 + +typedef struct { + unsigned int cmd_id; + unsigned int async_t1a_cfg; + unsigned int async_t1b_cfg; + unsigned int async_t1_polarity; +} __attribute__((packed)) vfe_cmd_async_timer1_start; + + +/* + * Command to configure and start asynchronous timer2 + */ + +#define VFE_CMD_ASYNC_TIMER2_START 0x001C +#define VFE_CMD_ASYNC_TIMER2_START_LEN \ + sizeof(vfe_cmd_async_timer2_start) + +#define VFE_CMD_ASYNC_T2_POLARITY_A_HIGH 0x0000 +#define VFE_CMD_ASYNC_T2_POLARITY_A_LOW 0x0001 +#define VFE_CMD_ASYNC_T2_POLARITY_B_HIGH 0x0000 +#define VFE_CMD_ASYNC_T2_POLARITY_B_LOW 0x0002 + +typedef struct { + unsigned int cmd_id; + unsigned int async_t2a_cfg; + unsigned int async_t2b_cfg; + unsigned int async_t2_polarity; +} __attribute__((packed)) vfe_cmd_async_timer2_start; + + +/* + * Command to program partial configurations of auto focus(af) + */ + +#define VFE_CMD_STATS_AF_UPDATE 0x001D +#define VFE_CMD_STATS_AF_UPDATE_LEN \ + sizeof(vfe_cmd_stats_af_update) + +#define VFE_CMD_AF_UPDATE_PART1_WINDOW_ONE 0x00000000 +#define VFE_CMD_AF_UPDATE_PART1_WINDOW_MULTI 0x80000000 + +typedef struct { + unsigned int cmd_id; + unsigned int af_update_part1; + unsigned int af_update_part2; +} __attribute__((packed)) vfe_cmd_stats_af_update; + + +/* + * Command to program partial cfg of wb and exp + */ + +#define VFE_CMD_STATS_WB_EXP_UPDATE 0x001E +#define VFE_CMD_STATS_WB_EXP_UPDATE_LEN \ + sizeof(vfe_cmd_stats_wb_exp_update) + +#define VFE_CMD_WB_EXP_UPDATE_PART1_REGIONS_8_8 0x0000 +#define VFE_CMD_WB_EXP_UPDATE_PART1_REGIONS_16_16 0x0001 +#define VFE_CMD_WB_EXP_UPDATE_PART1_SREGIONS_8_8 0x0000 +#define VFE_CMD_WB_EXP_UPDATE_PART1_SREGIONS_4_4 0x0002 + +typedef struct { + unsigned int cmd_id; + unsigned int wb_exp_update_part1; + unsigned int wb_exp_update_part2; + unsigned int wb_exp_update_part3; + unsigned int wb_exp_update_part4; +} __attribute__((packed)) vfe_cmd_stats_wb_exp_update; + + + +/* + * Command to re program the CAMIF FRAME CONFIG settings + */ + +#define VFE_CMD_UPDATE_CAMIF_FRAME_CFG 0x001F +#define VFE_CMD_UPDATE_CAMIF_FRAME_CFG_LEN \ + sizeof(vfe_cmd_update_camif_frame_cfg) + +typedef struct { + unsigned int cmd_id; + unsigned int camif_frame_cfg; +} __attribute__((packed)) vfe_cmd_update_camif_frame_cfg; + + +#endif diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfemsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfemsg.h new file mode 100644 index 0000000000000..98d0aa9f5ca96 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfemsg.h @@ -0,0 +1,290 @@ +#ifndef QDSP5VFEMSGI_H +#define QDSP5VFEMSGI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + V F E I N T E R N A L M E S S A G E S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are sent by VFE Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. + +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. + +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vfemsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + +when who what, where, why +-------- --- ---------------------------------------------------------- +06/12/08 sv initial version +===========================================================================*/ + + +/* + * Message to acknowledge CMD_VFE_REST command + */ + +#define VFE_MSG_RESET_ACK 0x0000 +#define VFE_MSG_RESET_ACK_LEN sizeof(vfe_msg_reset_ack) + +typedef struct { +} __attribute__((packed)) vfe_msg_reset_ack; + + +/* + * Message to acknowledge CMD_VFE_START command + */ + +#define VFE_MSG_START_ACK 0x0001 +#define VFE_MSG_START_ACK_LEN sizeof(vfe_msg_start_ack) + +typedef struct { +} __attribute__((packed)) vfe_msg_start_ack; + +/* + * Message to acknowledge CMD_VFE_STOP command + */ + +#define VFE_MSG_STOP_ACK 0x0002 +#define VFE_MSG_STOP_ACK_LEN sizeof(vfe_msg_stop_ack) + +typedef struct { +} __attribute__((packed)) vfe_msg_stop_ack; + + +/* + * Message to acknowledge CMD_VFE_UPDATE command + */ + +#define VFE_MSG_UPDATE_ACK 0x0003 +#define VFE_MSG_UPDATE_ACK_LEN sizeof(vfe_msg_update_ack) + +typedef struct { +} __attribute__((packed)) vfe_msg_update_ack; + + +/* + * Message to notify the ARM that snapshot processing is complete + * and that the VFE is now STATE_VFE_IDLE + */ + +#define VFE_MSG_SNAPSHOT_DONE 0x0004 +#define VFE_MSG_SNAPSHOT_DONE_LEN \ + sizeof(vfe_msg_snapshot_done) + +typedef struct { +} __attribute__((packed)) vfe_msg_snapshot_done; + + + +/* + * Message to notify ARM that illegal cmd was received and + * system is in the IDLE state + */ + +#define VFE_MSG_ILLEGAL_CMD 0x0005 +#define VFE_MSG_ILLEGAL_CMD_LEN \ + sizeof(vfe_msg_illegal_cmd) + +typedef struct { + unsigned int status; +} __attribute__((packed)) vfe_msg_illegal_cmd; + + +/* + * Message to notify ARM that op1 buf is full and ready + */ + +#define VFE_MSG_OP1 0x0006 +#define VFE_MSG_OP1_LEN sizeof(vfe_msg_op1) + +typedef struct { + unsigned int op1_buf_y_addr; + unsigned int op1_buf_cbcr_addr; + unsigned int black_level_even_col; + unsigned int black_level_odd_col; + unsigned int defect_pixels_detected; + unsigned int asf_max_edge; +} __attribute__((packed)) vfe_msg_op1; + + +/* + * Message to notify ARM that op2 buf is full and ready + */ + +#define VFE_MSG_OP2 0x0007 +#define VFE_MSG_OP2_LEN sizeof(vfe_msg_op2) + +typedef struct { + unsigned int op2_buf_y_addr; + unsigned int op2_buf_cbcr_addr; + unsigned int black_level_even_col; + unsigned int black_level_odd_col; + unsigned int defect_pixels_detected; + unsigned int asf_max_edge; +} __attribute__((packed)) vfe_msg_op2; + + +/* + * Message to notify ARM that autofocus(af) stats are ready + */ + +#define VFE_MSG_STATS_AF 0x0008 +#define VFE_MSG_STATS_AF_LEN sizeof(vfe_msg_stats_af) + +typedef struct { + unsigned int af_stats_op_buffer; +} __attribute__((packed)) vfe_msg_stats_af; + + +/* + * Message to notify ARM that white balance(wb) and exposure (exp) + * stats are ready + */ + +#define VFE_MSG_STATS_WB_EXP 0x0009 +#define VFE_MSG_STATS_WB_EXP_LEN \ + sizeof(vfe_msg_stats_wb_exp) + +typedef struct { + unsigned int wb_exp_stats_op_buf; +} __attribute__((packed)) vfe_msg_stats_wb_exp; + + +/* + * Message to notify the ARM that histogram(hg) stats are ready + */ + +#define VFE_MSG_STATS_HG 0x000A +#define VFE_MSG_STATS_HG_LEN sizeof(vfe_msg_stats_hg) + +typedef struct { + unsigned int hg_stats_op_buf; +} __attribute__((packed)) vfe_msg_stats_hg; + + +/* + * Message to notify the ARM that epoch1 event occurred in the CAMIF + */ + +#define VFE_MSG_EPOCH1 0x000B +#define VFE_MSG_EPOCH1_LEN sizeof(vfe_msg_epoch1) + +typedef struct { +} __attribute__((packed)) vfe_msg_epoch1; + + +/* + * Message to notify the ARM that epoch2 event occurred in the CAMIF + */ + +#define VFE_MSG_EPOCH2 0x000C +#define VFE_MSG_EPOCH2_LEN sizeof(vfe_msg_epoch2) + +typedef struct { +} __attribute__((packed)) vfe_msg_epoch2; + + +/* + * Message to notify the ARM that sync timer1 op is completed + */ + +#define VFE_MSG_SYNC_T1_DONE 0x000D +#define VFE_MSG_SYNC_T1_DONE_LEN sizeof(vfe_msg_sync_t1_done) + +typedef struct { +} __attribute__((packed)) vfe_msg_sync_t1_done; + + +/* + * Message to notify the ARM that sync timer2 op is completed + */ + +#define VFE_MSG_SYNC_T2_DONE 0x000E +#define VFE_MSG_SYNC_T2_DONE_LEN sizeof(vfe_msg_sync_t2_done) + +typedef struct { +} __attribute__((packed)) vfe_msg_sync_t2_done; + + +/* + * Message to notify the ARM that async t1 operation completed + */ + +#define VFE_MSG_ASYNC_T1_DONE 0x000F +#define VFE_MSG_ASYNC_T1_DONE_LEN sizeof(vfe_msg_async_t1_done) + +typedef struct { +} __attribute__((packed)) vfe_msg_async_t1_done; + + + +/* + * Message to notify the ARM that async t2 operation completed + */ + +#define VFE_MSG_ASYNC_T2_DONE 0x0010 +#define VFE_MSG_ASYNC_T2_DONE_LEN sizeof(vfe_msg_async_t2_done) + +typedef struct { +} __attribute__((packed)) vfe_msg_async_t2_done; + + + +/* + * Message to notify the ARM that an error has occurred + */ + +#define VFE_MSG_ERROR 0x0011 +#define VFE_MSG_ERROR_LEN sizeof(vfe_msg_error) + +#define VFE_MSG_ERR_COND_NO_CAMIF_ERR 0x0000 +#define VFE_MSG_ERR_COND_CAMIF_ERR 0x0001 +#define VFE_MSG_ERR_COND_OP1_Y_NO_BUS_OF 0x0000 +#define VFE_MSG_ERR_COND_OP1_Y_BUS_OF 0x0002 +#define VFE_MSG_ERR_COND_OP1_CBCR_NO_BUS_OF 0x0000 +#define VFE_MSG_ERR_COND_OP1_CBCR_BUS_OF 0x0004 +#define VFE_MSG_ERR_COND_OP2_Y_NO_BUS_OF 0x0000 +#define VFE_MSG_ERR_COND_OP2_Y_BUS_OF 0x0008 +#define VFE_MSG_ERR_COND_OP2_CBCR_NO_BUS_OF 0x0000 +#define VFE_MSG_ERR_COND_OP2_CBCR_BUS_OF 0x0010 +#define VFE_MSG_ERR_COND_AF_NO_BUS_OF 0x0000 +#define VFE_MSG_ERR_COND_AF_BUS_OF 0x0020 +#define VFE_MSG_ERR_COND_WB_EXP_NO_BUS_OF 0x0000 +#define VFE_MSG_ERR_COND_WB_EXP_BUS_OF 0x0040 +#define VFE_MSG_ERR_COND_NO_AXI_ERR 0x0000 +#define VFE_MSG_ERR_COND_AXI_ERR 0x0080 + +#define VFE_MSG_CAMIF_STS_IDLE 0x0000 +#define VFE_MSG_CAMIF_STS_CAPTURE_DATA 0x0001 + +typedef struct { + unsigned int err_cond; + unsigned int camif_sts; +} __attribute__((packed)) vfe_msg_error; + + +#endif diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c index e2796f56b7b9c..a3f343003e4a9 100644 --- a/arch/arm/mach-msm/io.c +++ b/arch/arm/mach-msm/io.c @@ -45,6 +45,8 @@ static struct map_desc msm_io_desc[] __initdata = { MSM_DEVICE(GPIO1), MSM_DEVICE(GPIO2), MSM_DEVICE(CLK_CTL), + MSM_DEVICE(AD5), + MSM_DEVICE(MDC), #ifdef CONFIG_MSM_DEBUG_UART MSM_DEVICE(DEBUG_UART), #endif diff --git a/arch/arm/mach-msm/qdsp5/Makefile b/arch/arm/mach-msm/qdsp5/Makefile new file mode 100644 index 0000000000000..97461e88be402 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/Makefile @@ -0,0 +1,12 @@ + +obj-$(CONFIG_MSM_AMSS_VERSION_6210) += adsp_6210.o +obj-$(CONFIG_MSM_AMSS_VERSION_6220) += adsp_6220.o +obj-$(CONFIG_MSM_AMSS_VERSION_6225) += adsp_6225.o + +obj-y += adsp.o adsp_driver.o +obj-y += adsp_video_verify_cmd.o +obj-y += adsp_jpeg_verify_cmd.o adsp_jpeg_patch_event.o +obj-y += adsp_vfe_verify_cmd.o adsp_vfe_patch_event.o +obj-y += adsp_lpm_verify_cmd.o +obj-y += audio_out.o audio_in.o audio_mp3.o audmgr.o audpp.o + diff --git a/arch/arm/mach-msm/qdsp5/adsp.c b/arch/arm/mach-msm/qdsp5/adsp.c new file mode 100644 index 0000000000000..9c521b3b60d36 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp.c @@ -0,0 +1,902 @@ +/* arch/arm/mach-msm/qdsp5/adsp.c + * + * Register/Interrupt access for userspace aDSP library. + * + * Copyright (c) 2008 QUALCOMM Incorporated + * Copyright (C) 2008 Google, Inc. + * Author: Iliyan Malchev + * + * 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. + * + */ + +/* TODO: + * - move shareable rpc code outside of adsp.c + * - general solution for virt->phys patchup + * - queue IDs should be relative to modules + * - disallow access to non-associated queues + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct wake_lock adsp_wake_lock; +static inline void prevent_suspend(void) +{ + wake_lock(&adsp_wake_lock); +} +static inline void allow_suspend(void) +{ + wake_unlock(&adsp_wake_lock); +} + +#include +#include +#include "adsp.h" + +#define INT_ADSP INT_ADSP_A9_A11 + +static struct adsp_info adsp_info; +static struct msm_rpc_endpoint *rpc_cb_server_client; +static struct msm_adsp_module *adsp_modules; +static int adsp_open_count; +static DEFINE_MUTEX(adsp_open_lock); + +/* protect interactions with the ADSP command/message queue */ +static spinlock_t adsp_cmd_lock; + +static uint32_t current_image = -1; + +void adsp_set_image(struct adsp_info *info, uint32_t image) +{ + current_image = image; +} + +uint32_t adsp_get_module(struct adsp_info *info, uint32_t task) +{ + return info->task_to_module[current_image][task]; +} + +uint32_t adsp_get_queue_offset(struct adsp_info *info, uint32_t queue_id) +{ + return info->queue_offset[current_image][queue_id]; +} + +static int rpc_adsp_rtos_app_to_modem(uint32_t cmd, uint32_t module, + struct msm_adsp_module *adsp_module) +{ + int rc; + struct rpc_adsp_rtos_app_to_modem_args_t rpc_req; + struct rpc_reply_hdr *rpc_rsp; + + msm_rpc_setup_req(&rpc_req.hdr, + RPC_ADSP_RTOS_ATOM_PROG, + RPC_ADSP_RTOS_ATOM_VERS, + RPC_ADSP_RTOS_APP_TO_MODEM_PROC); + + rpc_req.gotit = cpu_to_be32(1); + rpc_req.cmd = cpu_to_be32(cmd); + rpc_req.proc_id = cpu_to_be32(RPC_ADSP_RTOS_PROC_APPS); + rpc_req.module = cpu_to_be32(module); + rc = msm_rpc_write(adsp_module->rpc_client, &rpc_req, sizeof(rpc_req)); + if (rc < 0) { + pr_err("adsp: could not send RPC request: %d\n", rc); + return rc; + } + + rc = msm_rpc_read(adsp_module->rpc_client, + (void **)&rpc_rsp, -1, (5*HZ)); + if (rc < 0) { + pr_err("adsp: error receiving RPC reply: %d (%d)\n", + rc, -ERESTARTSYS); + return rc; + } + + if (be32_to_cpu(rpc_rsp->reply_stat) != RPCMSG_REPLYSTAT_ACCEPTED) { + pr_err("adsp: RPC call was denied!\n"); + kfree(rpc_rsp); + return -EPERM; + } + + if (be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat) != + RPC_ACCEPTSTAT_SUCCESS) { + pr_err("adsp error: RPC call was not successful (%d)\n", + be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat)); + kfree(rpc_rsp); + return -EINVAL; + } + + kfree(rpc_rsp); + return 0; +} + +struct msm_adsp_module *find_adsp_module_by_id( + struct adsp_info *info, uint32_t id) +{ + if (id > info->max_module_id) + return NULL; + else + return info->id_to_module[id]; +} + +static struct msm_adsp_module *find_adsp_module_by_name( + struct adsp_info *info, const char *name) +{ + unsigned n; + for (n = 0; n < info->module_count; n++) + if (!strcmp(name, adsp_modules[n].name)) + return adsp_modules + n; + return NULL; +} + +static int adsp_rpc_init(struct msm_adsp_module *adsp_module) +{ + adsp_module->rpc_client = msm_rpc_connect( + RPC_ADSP_RTOS_ATOM_PROG, + RPC_ADSP_RTOS_ATOM_VERS, + MSM_RPC_UNINTERRUPTIBLE); + + if (IS_ERR(adsp_module->rpc_client)) { + int rc = PTR_ERR(adsp_module->rpc_client); + adsp_module->rpc_client = 0; + pr_err("adsp: could not open rpc client: %d\n", rc); + return rc; + } + + return 0; +} + +int msm_adsp_get(const char *name, struct msm_adsp_module **out, + struct msm_adsp_ops *ops, void *driver_data) +{ + struct msm_adsp_module *module; + int rc = 0; + + module = find_adsp_module_by_name(&adsp_info, name); + if (!module) + return -ENODEV; + + mutex_lock(&module->lock); + pr_info("adsp: opening module %s\n", module->name); + if (module->open_count++ == 0 && module->clk) + clk_enable(module->clk); + + mutex_lock(&adsp_open_lock); + if (adsp_open_count++ == 0) { + enable_irq(INT_ADSP); + prevent_suspend(); + } + mutex_unlock(&adsp_open_lock); + + if (module->ops) { + rc = -EBUSY; + goto done; + } + + rc = adsp_rpc_init(module); + if (rc) + goto done; + + module->ops = ops; + module->driver_data = driver_data; + *out = module; + rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_REGISTER_APP, + module->id, module); + if (rc) { + module->ops = NULL; + module->driver_data = NULL; + *out = NULL; + pr_err("adsp: REGISTER_APP failed\n"); + goto done; + } + + pr_info("adsp: module %s has been registered\n", module->name); + +done: + mutex_lock(&adsp_open_lock); + if (rc && --adsp_open_count == 0) { + disable_irq(INT_ADSP); + allow_suspend(); + } + if (rc && --module->open_count == 0 && module->clk) + clk_disable(module->clk); + mutex_unlock(&adsp_open_lock); + mutex_unlock(&module->lock); + return rc; +} + +static int msm_adsp_disable_locked(struct msm_adsp_module *module); + +void msm_adsp_put(struct msm_adsp_module *module) +{ + unsigned long flags; + + mutex_lock(&module->lock); + if (--module->open_count == 0 && module->clk) + clk_disable(module->clk); + if (module->ops) { + pr_info("adsp: closing module %s\n", module->name); + + /* lock to ensure a dsp event cannot be delivered + * during or after removal of the ops and driver_data + */ + spin_lock_irqsave(&adsp_cmd_lock, flags); + module->ops = NULL; + module->driver_data = NULL; + spin_unlock_irqrestore(&adsp_cmd_lock, flags); + + if (module->state != ADSP_STATE_DISABLED) { + pr_info("adsp: disabling module %s\n", module->name); + msm_adsp_disable_locked(module); + } + + msm_rpc_close(module->rpc_client); + module->rpc_client = 0; + if (--adsp_open_count == 0) { + disable_irq(INT_ADSP); + allow_suspend(); + pr_info("adsp: disable interrupt\n"); + } + } else { + pr_info("adsp: module %s is already closed\n", module->name); + } + mutex_unlock(&module->lock); +} + +/* this should be common code with rpc_servers.c */ +static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client, + uint32_t xid, uint32_t accept_status) +{ + int rc = 0; + uint8_t reply_buf[sizeof(struct rpc_reply_hdr)]; + struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf; + + reply->xid = cpu_to_be32(xid); + reply->type = cpu_to_be32(1); /* reply */ + reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); + + reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status); + reply->data.acc_hdr.verf_flavor = 0; + reply->data.acc_hdr.verf_length = 0; + + rc = msm_rpc_write(rpc_cb_server_client, reply_buf, sizeof(reply_buf)); + if (rc < 0) + pr_err("adsp: could not write RPC response: %d\n", rc); + return rc; +} + +int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, + void *cmd_buf, size_t cmd_size) +{ + uint32_t ctrl_word; + uint32_t dsp_q_addr; + uint32_t dsp_addr; + uint32_t cmd_id = 0; + int cnt = 0; + int ret_status = 0; + unsigned long flags; + struct adsp_info *info = module->info; + + spin_lock_irqsave(&adsp_cmd_lock, flags); + + if (module->state != ADSP_STATE_ENABLED) { + spin_unlock_irqrestore(&adsp_cmd_lock, flags); + pr_err("adsp: module %s not enabled before write\n", + module->name); + return -ENODEV; + } + dsp_q_addr = adsp_get_queue_offset(info, dsp_queue_addr); + dsp_q_addr &= ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M; + + /* Poll until the ADSP is ready to accept a command. + * Wait for 100us, return error if it's not responding. + * If this returns an error, we need to disable ALL modules and + * then retry. + */ + while (((ctrl_word = readl(info->write_ctrl)) & + ADSP_RTOS_WRITE_CTRL_WORD_READY_M) != + ADSP_RTOS_WRITE_CTRL_WORD_READY_V) { + if (cnt > 10) { + pr_err("adsp: timeout waiting for DSP write ready\n"); + ret_status = -EIO; + goto fail; + } + pr_warning("adsp: waiting for DSP write ready\n"); + udelay(10); + cnt++; + } + + /* Set the mutex bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M); + ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V; + + /* Clear the command bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M); + + /* Set the queue address bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M); + ctrl_word |= dsp_q_addr; + + writel(ctrl_word, info->write_ctrl); + + /* Generate an interrupt to the DSP. This notifies the DSP that + * we are about to send a command on this particular queue. The + * DSP will in response change its state. + */ + writel(1, info->send_irq); + + /* Poll until the adsp responds to the interrupt; this does not + * generate an interrupt from the adsp. This should happen within + * 5ms. + */ + cnt = 0; + while ((readl(info->write_ctrl) & + ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M) == + ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V) { + if (cnt > 5) { + pr_err("adsp: timeout waiting for adsp ack\n"); + ret_status = -EIO; + goto fail; + } + mdelay(1); + cnt++; + } + + /* Read the ctrl word */ + ctrl_word = readl(info->write_ctrl); + + if ((ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M) != + ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V) { + ret_status = -EIO; + pr_err("adsp: failed to write queue %x, retry\n", dsp_q_addr); + goto fail; + } else { + /* No error */ + /* Get the DSP buffer address */ + dsp_addr = (ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M) + + (uint32_t)MSM_AD5_BASE; + + if (dsp_addr < (uint32_t)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) { + uint16_t *buf_ptr = (uint16_t *) cmd_buf; + uint16_t *dsp_addr16 = (uint16_t *)dsp_addr; + cmd_size /= sizeof(uint16_t); + + /* Save the command ID */ + cmd_id = (uint32_t) buf_ptr[0]; + + /* Copy the command to DSP memory */ + cmd_size++; + while (--cmd_size) + *dsp_addr16++ = *buf_ptr++; + } else { + uint32_t *buf_ptr = (uint32_t *) cmd_buf; + uint32_t *dsp_addr32 = (uint32_t *)dsp_addr; + cmd_size /= sizeof(uint32_t); + + /* Save the command ID */ + cmd_id = buf_ptr[0]; + + cmd_size++; + while (--cmd_size) + *dsp_addr32++ = *buf_ptr++; + } + + /* Set the mutex bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M); + ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V; + + /* Set the command bits to write done */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M); + ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V; + + /* Set the queue address bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M); + ctrl_word |= dsp_q_addr; + + writel(ctrl_word, info->write_ctrl); + + /* Generate an interrupt to the DSP. It does not respond with + * an interrupt, and we do not need to wait for it to + * acknowledge, because it will hold the mutex lock until it's + * ready to receive more commands again. + */ + writel(1, info->send_irq); + + module->num_commands++; + } /* Ctrl word status bits were 00, no error in the ctrl word */ + +fail: + spin_unlock_irqrestore(&adsp_cmd_lock, flags); + return ret_status; +} + +static void handle_adsp_rtos_mtoa_app(struct rpc_request_hdr *req) +{ + struct rpc_adsp_rtos_modem_to_app_args_t *args = + (struct rpc_adsp_rtos_modem_to_app_args_t *)req; + uint32_t event = be32_to_cpu(args->event); + uint32_t proc_id = be32_to_cpu(args->proc_id); + uint32_t module_id = be32_to_cpu(args->module); + uint32_t image = be32_to_cpu(args->image); + struct msm_adsp_module *module; + + pr_info("adsp: rpc event=%d, proc_id=%d, module=%d, image=%d\n", + event, proc_id, module_id, image); + + module = find_adsp_module_by_id(&adsp_info, module_id); + if (!module) { + pr_err("adsp: module %d is not supported!\n", module_id); + rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, + RPC_ACCEPTSTAT_GARBAGE_ARGS); + return; + } + + mutex_lock(&module->lock); + switch (event) { + case RPC_ADSP_RTOS_MOD_READY: + pr_info("adsp: module %s: READY\n", module->name); + module->state = ADSP_STATE_ENABLED; + wake_up(&module->state_wait); + adsp_set_image(module->info, image); + break; + case RPC_ADSP_RTOS_MOD_DISABLE: + pr_info("adsp: module %s: DISABLED\n", module->name); + module->state = ADSP_STATE_DISABLED; + wake_up(&module->state_wait); + break; + case RPC_ADSP_RTOS_SERVICE_RESET: + pr_info("adsp: module %s: SERVICE_RESET\n", module->name); + module->state = ADSP_STATE_DISABLED; + wake_up(&module->state_wait); + break; + case RPC_ADSP_RTOS_CMD_SUCCESS: + pr_info("adsp: module %s: CMD_SUCCESS\n", module->name); + break; + case RPC_ADSP_RTOS_CMD_FAIL: + pr_info("adsp: module %s: CMD_FAIL\n", module->name); + break; + default: + pr_info("adsp: unknown event %d\n", event); + rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, + RPC_ACCEPTSTAT_GARBAGE_ARGS); + goto done; + } + rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, + RPC_ACCEPTSTAT_SUCCESS); +done: + mutex_unlock(&module->lock); +} + +static int handle_adsp_rtos_mtoa(struct rpc_request_hdr *req) +{ + switch (req->procedure) { + case RPC_ADSP_RTOS_MTOA_NULL_PROC: + rpc_send_accepted_void_reply(rpc_cb_server_client, + req->xid, + RPC_ACCEPTSTAT_SUCCESS); + break; + case RPC_ADSP_RTOS_MODEM_TO_APP_PROC: + handle_adsp_rtos_mtoa_app(req); + break; + default: + pr_err("adsp: unknowned proc %d\n", req->procedure); + rpc_send_accepted_void_reply( + rpc_cb_server_client, req->xid, + RPC_ACCEPTSTAT_PROC_UNAVAIL); + break; + } + return 0; +} + +/* this should be common code with rpc_servers.c */ +static int adsp_rpc_thread(void *data) +{ + void *buffer; + struct rpc_request_hdr *req; + int rc, exit = 0; + + do { + rc = msm_rpc_read(rpc_cb_server_client, &buffer, -1, -1); + if (rc < 0) { + pr_err("adsp: could not read rpc: %d\n", rc); + break; + } + req = (struct rpc_request_hdr *)buffer; + + req->type = be32_to_cpu(req->type); + req->xid = be32_to_cpu(req->xid); + req->rpc_vers = be32_to_cpu(req->rpc_vers); + req->prog = be32_to_cpu(req->prog); + req->vers = be32_to_cpu(req->vers); + req->procedure = be32_to_cpu(req->procedure); + + if (req->type != 0) + goto bad_rpc; + if (req->rpc_vers != 2) + goto bad_rpc; + if (req->prog != RPC_ADSP_RTOS_MTOA_PROG) + goto bad_rpc; + if (req->vers != RPC_ADSP_RTOS_MTOA_VERS) + goto bad_rpc; + + handle_adsp_rtos_mtoa(req); + kfree(buffer); + continue; + +bad_rpc: + pr_err("adsp: bogus rpc from modem\n"); + kfree(buffer); + } while (!exit); + do_exit(0); +} + +static size_t read_event_size; +static void *read_event_addr; + +static void read_event_16(void *buf, size_t len) +{ + uint16_t *dst = buf; + uint16_t *src = read_event_addr; + len /= 2; + if (len > read_event_size) + len = read_event_size; + while (len--) + *dst++ = *src++; +} + +static void read_event_32(void *buf, size_t len) +{ + uint32_t *dst = buf; + uint32_t *src = read_event_addr; + len /= 2; + if (len > read_event_size) + len = read_event_size; + while (len--) + *dst++ = *src++; +} + +static int adsp_rtos_read_ctrl_word_cmd_tast_to_h_v( + struct adsp_info *info, void *dsp_addr) +{ + struct msm_adsp_module *module; + unsigned rtos_task_id; + unsigned msg_id; + unsigned msg_length; + void (*func)(void *, size_t); + + if (dsp_addr >= (void *)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) { + uint32_t *dsp_addr32 = dsp_addr; + uint32_t tmp = *dsp_addr32++; + rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8; + msg_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M); + read_event_size = tmp >> 16; + read_event_addr = dsp_addr32; + msg_length = read_event_size * sizeof(uint32_t); + func = read_event_32; + } else { + uint16_t *dsp_addr16 = dsp_addr; + uint16_t tmp = *dsp_addr16++; + rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8; + msg_id = tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M; + read_event_size = *dsp_addr16++; + read_event_addr = dsp_addr16; + msg_length = read_event_size * sizeof(uint16_t); + func = read_event_16; + } + + if (rtos_task_id > info->max_task_id) { + pr_err("adsp: bogus task id %d\n", rtos_task_id); + return 0; + } + module = find_adsp_module_by_id(info, + adsp_get_module(info, rtos_task_id)); + + if (!module) { + pr_err("adsp: no module for task id %d\n", rtos_task_id); + return 0; + } + + module->num_events++; + + if (!module->ops) { + pr_err("adsp: module %s is not open\n", module->name); + return 0; + } + + module->ops->event(module->driver_data, msg_id, msg_length, func); + return 0; +} + +static int adsp_get_event(struct adsp_info *info) +{ + uint32_t ctrl_word; + uint32_t ready; + void *dsp_addr; + uint32_t cmd_type; + int cnt; + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&adsp_cmd_lock, flags); + + /* Whenever the DSP has a message, it updates this control word + * and generates an interrupt. When we receive the interrupt, we + * read this register to find out what ADSP task the command is + * comming from. + * + * The ADSP should *always* be ready on the first call, but the + * irq handler calls us in a loop (to handle back-to-back command + * processing), so we give the DSP some time to return to the + * ready state. The DSP will not issue another IRQ for events + * pending between the first IRQ and the event queue being drained, + * unfortunately. + */ + + for (cnt = 0; cnt < 10; cnt++) { + ctrl_word = readl(info->read_ctrl); + + if ((ctrl_word & ADSP_RTOS_READ_CTRL_WORD_FLAG_M) == + ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V) + goto ready; + + udelay(10); + } + pr_warning("adsp: not ready after 100uS\n"); + rc = -EBUSY; + goto done; + +ready: + /* Here we check to see if there are pending messages. If there are + * none, we siply return -EAGAIN to indicate that there are no more + * messages pending. + */ + ready = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_READY_M; + if ((ready != ADSP_RTOS_READ_CTRL_WORD_READY_V) && + (ready != ADSP_RTOS_READ_CTRL_WORD_CONT_V)) { + rc = -EAGAIN; + goto done; + } + + /* DSP says that there are messages waiting for the host to read */ + + /* Get the Command Type */ + cmd_type = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M; + + /* Get the DSP buffer address */ + dsp_addr = (void *)((ctrl_word & + ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M) + + (uint32_t)MSM_AD5_BASE); + + /* We can only handle Task-to-Host messages */ + if (cmd_type != ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V) { + pr_err("adsp: unknown dsp cmd_type %d\n", cmd_type); + rc = -EIO; + goto done; + } + + adsp_rtos_read_ctrl_word_cmd_tast_to_h_v(info, dsp_addr); + + ctrl_word = readl(info->read_ctrl); + ctrl_word &= ~ADSP_RTOS_READ_CTRL_WORD_READY_M; + + /* Write ctrl word to the DSP */ + writel(ctrl_word, info->read_ctrl); + + /* Generate an interrupt to the DSP */ + writel(1, info->send_irq); + +done: + spin_unlock_irqrestore(&adsp_cmd_lock, flags); + return rc; +} + +static irqreturn_t adsp_irq_handler(int irq, void *data) +{ + struct adsp_info *info = &adsp_info; + int cnt = 0; + for (cnt = 0; cnt < 10; cnt++) + if (adsp_get_event(info) < 0) + break; + if (cnt > info->event_backlog_max) + info->event_backlog_max = cnt; + info->events_received += cnt; + if (cnt == 10) + pr_err("adsp: too many (%d) events for single irq!\n", cnt); + return IRQ_HANDLED; +} + +int msm_adsp_enable(struct msm_adsp_module *module) +{ + int rc = 0; + + pr_info("msm_adsp_enable() '%s'\n", module->name); + + mutex_lock(&module->lock); + switch (module->state) { + case ADSP_STATE_DISABLED: + rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_ENABLE, + module->id, module); + if (rc) + break; + module->state = ADSP_STATE_ENABLING; + mutex_unlock(&module->lock); + rc = wait_event_timeout(module->state_wait, + module->state != ADSP_STATE_ENABLING, + 1 * HZ); + mutex_lock(&module->lock); + if (module->state == ADSP_STATE_ENABLED) { + rc = 0; + } else { + pr_err("adsp: module '%s' enable timed out\n", + module->name); + rc = -ETIMEDOUT; + } + break; + case ADSP_STATE_ENABLING: + pr_warning("adsp: module '%s' enable in progress\n", + module->name); + break; + case ADSP_STATE_ENABLED: + pr_warning("adsp: module '%s' already enabled\n", + module->name); + break; + case ADSP_STATE_DISABLING: + pr_err("adsp: module '%s' disable in progress\n", + module->name); + rc = -EBUSY; + break; + } + mutex_unlock(&module->lock); + return rc; +} + +static int msm_adsp_disable_locked(struct msm_adsp_module *module) +{ + int rc = 0; + + switch (module->state) { + case ADSP_STATE_DISABLED: + pr_warning("adsp: module '%s' already disabled\n", + module->name); + break; + case ADSP_STATE_ENABLING: + case ADSP_STATE_ENABLED: + rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_DISABLE, + module->id, module); + module->state = ADSP_STATE_DISABLED; + } + return rc; +} + +int msm_adsp_disable(struct msm_adsp_module *module) +{ + int rc; + pr_info("msm_adsp_disable() '%s'\n", module->name); + mutex_lock(&module->lock); + rc = msm_adsp_disable_locked(module); + mutex_unlock(&module->lock); + return rc; +} + +static int msm_adsp_probe(struct platform_device *pdev) +{ + unsigned count; + int rc, i; + + wake_lock_init(&adsp_wake_lock, WAKE_LOCK_SUSPEND, "adsp"); + + rc = adsp_init_info(&adsp_info); + if (rc) + return rc; + adsp_info.send_irq += MSM_AD5_BASE; + adsp_info.read_ctrl += MSM_AD5_BASE; + adsp_info.write_ctrl += MSM_AD5_BASE; + count = adsp_info.module_count; + + adsp_modules = kzalloc( + (sizeof(struct msm_adsp_module) + sizeof(void *)) * + count, GFP_KERNEL); + if (!adsp_modules) + return -ENOMEM; + + adsp_info.id_to_module = (void *) (adsp_modules + count); + + spin_lock_init(&adsp_cmd_lock); + + rc = request_irq(INT_ADSP, adsp_irq_handler, IRQF_TRIGGER_RISING, + "adsp", 0); + if (rc < 0) + goto fail_request_irq; + disable_irq(INT_ADSP); + + rpc_cb_server_client = msm_rpc_open(); + if (IS_ERR(rpc_cb_server_client)) { + rpc_cb_server_client = NULL; + rc = PTR_ERR(rpc_cb_server_client); + pr_err("adsp: could not create rpc server (%d)\n", rc); + goto fail_rpc_open; + } + + rc = msm_rpc_register_server(rpc_cb_server_client, + RPC_ADSP_RTOS_MTOA_PROG, + RPC_ADSP_RTOS_MTOA_VERS); + if (rc) { + pr_err("adsp: could not register callback server (%d)\n", rc); + goto fail_rpc_register; + } + + /* start the kernel thread to process the callbacks */ + kthread_run(adsp_rpc_thread, NULL, "kadspd"); + + for (i = 0; i < count; i++) { + struct msm_adsp_module *mod = adsp_modules + i; + mutex_init(&mod->lock); + init_waitqueue_head(&mod->state_wait); + mod->info = &adsp_info; + mod->name = adsp_info.module[i].name; + mod->id = adsp_info.module[i].id; + if (adsp_info.module[i].clk_name) + mod->clk = clk_get(NULL, adsp_info.module[i].clk_name); + else + mod->clk = NULL; + if (mod->clk && adsp_info.module[i].clk_rate) + clk_set_rate(mod->clk, adsp_info.module[i].clk_rate); + mod->verify_cmd = adsp_info.module[i].verify_cmd; + mod->patch_event = adsp_info.module[i].patch_event; + INIT_HLIST_HEAD(&mod->pmem_regions); + mod->pdev.name = adsp_info.module[i].pdev_name; + mod->pdev.id = -1; + adsp_info.id_to_module[mod->id] = mod; + platform_device_register(&mod->pdev); + } + + msm_adsp_publish_cdevs(adsp_modules, count); + + return 0; + +fail_rpc_register: + msm_rpc_close(rpc_cb_server_client); + rpc_cb_server_client = NULL; +fail_rpc_open: + enable_irq(INT_ADSP); + free_irq(INT_ADSP, 0); +fail_request_irq: + kfree(adsp_modules); + return rc; +} + +static struct platform_driver msm_adsp_driver = { + .probe = msm_adsp_probe, + .driver = { +#if CONFIG_MSM_AMSS_VERSION == 6210 + .name = "rs3000000a:20f17fd3", +#elif (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225) + .name = "rs3000000a:71d1094b", +#else +#error "Unknown AMSS version" +#endif + .owner = THIS_MODULE, + }, +}; + +static int __init adsp_init(void) +{ + return platform_driver_register(&msm_adsp_driver); +} + +device_initcall(adsp_init); diff --git a/arch/arm/mach-msm/qdsp5/adsp.h b/arch/arm/mach-msm/qdsp5/adsp.h new file mode 100644 index 0000000000000..ffe1faf4db21b --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp.h @@ -0,0 +1,303 @@ +/* arch/arm/mach-msm/qdsp5/adsp.h + * + * Copyright (c) 2008 QUALCOMM Incorporated + * Copyright (C) 2008 Google, Inc. + * Author: Iliyan Malchev + * + * 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 _ARCH_ARM_MACH_MSM_ADSP_H +#define _ARCH_ARM_MACH_MSM_ADSP_H + +#include +#include +#include +#include + +int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr, + unsigned long len); +int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr, + unsigned long *kvaddr, unsigned long len); +int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr); + +int adsp_vfe_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); +int adsp_jpeg_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); +int adsp_lpm_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); +int adsp_video_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); + +struct adsp_event; + +int adsp_vfe_patch_event(struct msm_adsp_module *module, + struct adsp_event *event); + +int adsp_jpeg_patch_event(struct msm_adsp_module *module, + struct adsp_event *event); + + +struct adsp_module_info { + const char *name; + const char *pdev_name; + uint32_t id; + const char *clk_name; + unsigned long clk_rate; + int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *, + size_t); + int (*patch_event) (struct msm_adsp_module*, struct adsp_event *); +}; + +#define ADSP_EVENT_MAX_SIZE 496 + +struct adsp_event { + struct list_head list; + uint32_t size; /* always in bytes */ + uint16_t msg_id; + uint16_t type; /* 0 for msgs (from aDSP), 1 for events (from ARM9) */ + int is16; /* always 0 (msg is 32-bit) when the event type is 1(ARM9) */ + union { + uint16_t msg16[ADSP_EVENT_MAX_SIZE / 2]; + uint32_t msg32[ADSP_EVENT_MAX_SIZE / 4]; + } data; +}; + +struct adsp_info { + uint32_t send_irq; + uint32_t read_ctrl; + uint32_t write_ctrl; + + uint32_t max_msg16_size; + uint32_t max_msg32_size; + + uint32_t max_task_id; + uint32_t max_module_id; + uint32_t max_queue_id; + uint32_t max_image_id; + + /* for each image id, a map of queue id to offset */ + uint32_t **queue_offset; + + /* for each image id, a map of task id to module id */ + uint32_t **task_to_module; + + /* for each module id, map of module id to module */ + struct msm_adsp_module **id_to_module; + + uint32_t module_count; + struct adsp_module_info *module; + + /* stats */ + uint32_t events_received; + uint32_t event_backlog_max; +}; + +#define RPC_ADSP_RTOS_ATOM_PROG 0x3000000a +#define RPC_ADSP_RTOS_MTOA_PROG 0x3000000b +#define RPC_ADSP_RTOS_ATOM_NULL_PROC 0 +#define RPC_ADSP_RTOS_MTOA_NULL_PROC 0 +#define RPC_ADSP_RTOS_APP_TO_MODEM_PROC 2 +#define RPC_ADSP_RTOS_MODEM_TO_APP_PROC 2 + +#if CONFIG_MSM_AMSS_VERSION == 6210 +#define RPC_ADSP_RTOS_ATOM_VERS 0x20f17fd3 +#define RPC_ADSP_RTOS_MTOA_VERS 0x75babbd6 +#elif (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225) +#define RPC_ADSP_RTOS_ATOM_VERS 0x71d1094b +#define RPC_ADSP_RTOS_MTOA_VERS 0xee3a9966 +#else +#error "Unknown AMSS version" +#endif + +enum rpc_adsp_rtos_proc_type { + RPC_ADSP_RTOS_PROC_NONE = 0, + RPC_ADSP_RTOS_PROC_MODEM = 1, + RPC_ADSP_RTOS_PROC_APPS = 2, +}; + +enum { + RPC_ADSP_RTOS_CMD_REGISTER_APP, + RPC_ADSP_RTOS_CMD_ENABLE, + RPC_ADSP_RTOS_CMD_DISABLE, + RPC_ADSP_RTOS_CMD_KERNEL_COMMAND, + RPC_ADSP_RTOS_CMD_16_COMMAND, + RPC_ADSP_RTOS_CMD_32_COMMAND, + RPC_ADSP_RTOS_CMD_DISABLE_EVENT_RSP, + RPC_ADSP_RTOS_CMD_REMOTE_EVENT, + RPC_ADSP_RTOS_CMD_SET_STATE, +}; + +enum rpc_adsp_rtos_mod_status_type { + RPC_ADSP_RTOS_MOD_READY, + RPC_ADSP_RTOS_MOD_DISABLE, + RPC_ADSP_RTOS_SERVICE_RESET, + RPC_ADSP_RTOS_CMD_FAIL, + RPC_ADSP_RTOS_CMD_SUCCESS, +}; + +struct rpc_adsp_rtos_app_to_modem_args_t { + struct rpc_request_hdr hdr; + uint32_t gotit; /* if 1, the next elements are present */ + uint32_t cmd; /* e.g., RPC_ADSP_RTOS_CMD_REGISTER_APP */ + uint32_t proc_id; /* e.g., RPC_ADSP_RTOS_PROC_APPS */ + uint32_t module; /* e.g., QDSP_MODULE_AUDPPTASK */ +}; + +struct rpc_adsp_rtos_modem_to_app_args_t { + struct rpc_request_hdr hdr; + uint32_t gotit; /* if 1, the next elements are present */ + uint32_t event; /* e.g., RPC_ADSP_RTOS_CMD_REGISTER_APP */ + uint32_t proc_id; /* e.g., RPC_ADSP_RTOS_PROC_APPS */ + uint32_t module; /* e.g., QDSP_MODULE_AUDPPTASK */ + uint32_t image; /* RPC_QDSP_IMAGE_GAUDIO */ +}; + +#define ADSP_STATE_DISABLED 0 +#define ADSP_STATE_ENABLING 1 +#define ADSP_STATE_ENABLED 2 +#define ADSP_STATE_DISABLING 3 + +struct msm_adsp_module { + struct mutex lock; + const char *name; + unsigned id; + struct adsp_info *info; + + struct msm_rpc_endpoint *rpc_client; + struct msm_adsp_ops *ops; + void *driver_data; + + /* statistics */ + unsigned num_commands; + unsigned num_events; + + wait_queue_head_t state_wait; + unsigned state; + + struct platform_device pdev; + struct clk *clk; + int open_count; + + struct mutex pmem_regions_lock; + struct hlist_head pmem_regions; + int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *, + size_t); + int (*patch_event) (struct msm_adsp_module*, struct adsp_event *); +}; + +extern void msm_adsp_publish_cdevs(struct msm_adsp_module *, unsigned); +extern int adsp_init_info(struct adsp_info *info); +extern struct msm_adsp_module *find_adsp_module_by_id(struct adsp_info *info, uint32_t id); + + +/* Command Queue Indexes */ +#define QDSP_lpmCommandQueue 0 +#define QDSP_mpuAfeQueue 1 +#define QDSP_mpuGraphicsCmdQueue 2 +#define QDSP_mpuModmathCmdQueue 3 +#define QDSP_mpuVDecCmdQueue 4 +#define QDSP_mpuVDecPktQueue 5 +#define QDSP_mpuVEncCmdQueue 6 +#define QDSP_rxMpuDecCmdQueue 7 +#define QDSP_rxMpuDecPktQueue 8 +#define QDSP_txMpuEncQueue 9 +#define QDSP_uPAudPPCmd1Queue 10 +#define QDSP_uPAudPPCmd2Queue 11 +#define QDSP_uPAudPPCmd3Queue 12 +#define QDSP_uPAudPlay0BitStreamCtrlQueue 13 +#define QDSP_uPAudPlay1BitStreamCtrlQueue 14 +#define QDSP_uPAudPlay2BitStreamCtrlQueue 15 +#define QDSP_uPAudPlay3BitStreamCtrlQueue 16 +#define QDSP_uPAudPlay4BitStreamCtrlQueue 17 +#define QDSP_uPAudPreProcCmdQueue 18 +#define QDSP_uPAudRecBitStreamQueue 19 +#define QDSP_uPAudRecCmdQueue 20 +#define QDSP_uPJpegActionCmdQueue 21 +#define QDSP_uPJpegCfgCmdQueue 22 +#define QDSP_uPVocProcQueue 23 +#define QDSP_vfeCommandQueue 24 +#define QDSP_vfeCommandScaleQueue 25 +#define QDSP_vfeCommandTableQueue 26 +#define QDSP_QUEUE_MAX 26 + +/* Value to indicate that a queue is not defined for a particular image */ +#define QDSP_RTOS_NO_QUEUE 0xffffffff + +/* + * Constants used to communicate with the ADSP RTOS + */ +#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M 0x80000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V 0x80000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_AVAIL_V 0x00000000U + +#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_M 0x70000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_REQ_V 0x00000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V 0x10000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_NO_CMD_V 0x70000000U + +#define ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M 0x0E000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V 0x00000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_NO_FREE_BUF_V 0x02000000U + +#define ADSP_RTOS_WRITE_CTRL_WORD_KERNEL_FLG_M 0x01000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_MSG_WRITE_V 0x00000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_V 0x01000000U + +#define ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU +#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_ID_M 0x00FFFFFFU + +/* Combination of MUTEX and CMD bits to check if the DSP is busy */ +#define ADSP_RTOS_WRITE_CTRL_WORD_READY_M 0xF0000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_READY_V 0x70000000U + +/* RTOS to Host processor command mask values */ +#define ADSP_RTOS_READ_CTRL_WORD_FLAG_M 0x80000000U +#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_WAIT_V 0x00000000U +#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V 0x80000000U + +#define ADSP_RTOS_READ_CTRL_WORD_CMD_M 0x60000000U +#define ADSP_RTOS_READ_CTRL_WORD_READ_DONE_V 0x00000000U +#define ADSP_RTOS_READ_CTRL_WORD_READ_REQ_V 0x20000000U +#define ADSP_RTOS_READ_CTRL_WORD_NO_CMD_V 0x60000000U + +/* Combination of FLAG and COMMAND bits to check if MSG ready */ +#define ADSP_RTOS_READ_CTRL_WORD_READY_M 0xE0000000U +#define ADSP_RTOS_READ_CTRL_WORD_READY_V 0xA0000000U +#define ADSP_RTOS_READ_CTRL_WORD_CONT_V 0xC0000000U +#define ADSP_RTOS_READ_CTRL_WORD_DONE_V 0xE0000000U + +#define ADSP_RTOS_READ_CTRL_WORD_STATUS_M 0x18000000U +#define ADSP_RTOS_READ_CTRL_WORD_NO_ERR_V 0x00000000U + +#define ADSP_RTOS_READ_CTRL_WORD_IN_PROG_M 0x04000000U +#define ADSP_RTOS_READ_CTRL_WORD_NO_READ_IN_PROG_V 0x00000000U +#define ADSP_RTOS_READ_CTRL_WORD_READ_IN_PROG_V 0x04000000U + +#define ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M 0x03000000U +#define ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V 0x00000000U +#define ADSP_RTOS_READ_CTRL_WORD_CMD_KRNL_TO_H_V 0x01000000U +#define ADSP_RTOS_READ_CTRL_WORD_CMD_H_TO_KRNL_CFM_V 0x02000000U + +#define ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU + +#define ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M 0x000000FFU +#define ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M 0x0000FF00U + +/* Base address of DSP and DSP hardware registers */ +#define QDSP_RAMC_OFFSET 0x400000 + +#endif diff --git a/arch/arm/mach-msm/qdsp5/adsp_6210.c b/arch/arm/mach-msm/qdsp5/adsp_6210.c new file mode 100644 index 0000000000000..628c2477a1f8f --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_6210.c @@ -0,0 +1,283 @@ +/* arch/arm/mach-msm/qdsp5/adsp_6210.h + * + * Copyright (c) 2008 QUALCOMM Incorporated. + * + * 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 "adsp.h" + +/* Firmware modules */ +typedef enum { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_VIDEO_AAC_VOC, + QDSP_MODULE_PCM_DEC, + QDSP_MODULE_AUDIO_DEC_MP3, + QDSP_MODULE_AUDIO_DEC_AAC, + QDSP_MODULE_AUDIO_DEC_WMA, + QDSP_MODULE_HOSTPCM, + QDSP_MODULE_DTMF, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_SBC_ENC, + QDSP_MODULE_VOC, + QDSP_MODULE_VOC_PCM, + QDSP_MODULE_VOCENCTASK, + QDSP_MODULE_VOCDECTASK, + QDSP_MODULE_VOICEPROCTASK, + QDSP_MODULE_VIDEOENCTASK, + QDSP_MODULE_VFETASK, + QDSP_MODULE_WAV_ENC, + QDSP_MODULE_AACLC_ENC, + QDSP_MODULE_VIDEO_AMR, + QDSP_MODULE_VOC_AMR, + QDSP_MODULE_VOC_EVRC, + QDSP_MODULE_VOC_13K, + QDSP_MODULE_VOC_FGV, + QDSP_MODULE_DIAGTASK, + QDSP_MODULE_JPEGTASK, + QDSP_MODULE_LPMTASK, + QDSP_MODULE_QCAMTASK, + QDSP_MODULE_MODMATHTASK, + QDSP_MODULE_AUDPLAY2TASK, + QDSP_MODULE_AUDPLAY3TASK, + QDSP_MODULE_AUDPLAY4TASK, + QDSP_MODULE_GRAPHICSTASK, + QDSP_MODULE_MIDI, + QDSP_MODULE_GAUDIO, + QDSP_MODULE_VDEC_LP_MODE, + QDSP_MODULE_MAX, +} qdsp_module_type; + +#define QDSP_RTOS_MAX_TASK_ID 19U + +/* Table of modules indexed by task ID for the GAUDIO image */ +static qdsp_module_type qdsp_gaudio_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_AUDPLAY2TASK, + QDSP_MODULE_AUDPLAY3TASK, + QDSP_MODULE_AUDPLAY4TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_GRAPHICSTASK, + QDSP_MODULE_MAX +}; + +/* Queue offset table indexed by queue ID for the GAUDIO image */ +static uint32_t qdsp_gaudio_queue_offset_table[] = { + QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */ + 0x3be, /* QDSP_mpuAfeQueue */ + 0x3ee, /* QDSP_mpuGraphicsCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */ + 0x3c2, /* QDSP_uPAudPPCmd1Queue */ + 0x3c6, /* QDSP_uPAudPPCmd2Queue */ + 0x3ca, /* QDSP_uPAudPPCmd3Queue */ + 0x3da, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + 0x3de, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + 0x3e2, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + 0x3e6, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + 0x3ea, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x3ce, /* QDSP_uPAudPreProcCmdQueue */ + 0x3d6, /* QDSP_uPAudRecBitStreamQueue */ + 0x3d2, /* QDSP_uPAudRecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */ + QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */ +}; + +/* Table of modules indexed by task ID for the COMBO image */ +static qdsp_module_type qdsp_combo_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_VOCDECTASK, + QDSP_MODULE_VOCENCTASK, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_VIDEOENCTASK, + QDSP_MODULE_VOICEPROCTASK, + QDSP_MODULE_VFETASK, + QDSP_MODULE_JPEGTASK, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_LPMTASK, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MODMATHTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX +}; + +/* Queue offset table indexed by queue ID for the COMBO image */ +static uint32_t qdsp_combo_queue_offset_table[] = { + 0x585, /* QDSP_lpmCommandQueue */ + 0x52d, /* QDSP_mpuAfeQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */ + 0x541, /* QDSP_mpuModmathCmdQueue */ + 0x555, /* QDSP_mpuVDecCmdQueue */ + 0x559, /* QDSP_mpuVDecPktQueue */ + 0x551, /* QDSP_mpuVEncCmdQueue */ + 0x535, /* QDSP_rxMpuDecCmdQueue */ + 0x539, /* QDSP_rxMpuDecPktQueue */ + 0x53d, /* QDSP_txMpuEncQueue */ + 0x55d, /* QDSP_uPAudPPCmd1Queue */ + 0x561, /* QDSP_uPAudPPCmd2Queue */ + 0x565, /* QDSP_uPAudPPCmd3Queue */ + 0x575, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + 0x579, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x569, /* QDSP_uPAudPreProcCmdQueue */ + 0x571, /* QDSP_uPAudRecBitStreamQueue */ + 0x56d, /* QDSP_uPAudRecCmdQueue */ + 0x581, /* QDSP_uPJpegActionCmdQueue */ + 0x57d, /* QDSP_uPJpegCfgCmdQueue */ + 0x531, /* QDSP_uPVocProcQueue */ + 0x545, /* QDSP_vfeCommandQueue */ + 0x54d, /* QDSP_vfeCommandScaleQueue */ + 0x549 /* QDSP_vfeCommandTableQueue */ +}; + +/* Table of modules indexed by task ID for the QTV_LP image */ +static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX +}; + +/* Queue offset table indexed by queue ID for the QTV_LP image */ +static uint32_t qdsp_qtv_lp_queue_offset_table[] = { + QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */ + 0x40c, /* QDSP_mpuAfeQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */ + 0x410, /* QDSP_mpuVDecCmdQueue */ + 0x414, /* QDSP_mpuVDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */ + 0x41c, /* QDSP_uPAudPPCmd1Queue */ + 0x420, /* QDSP_uPAudPPCmd2Queue */ + 0x424, /* QDSP_uPAudPPCmd3Queue */ + 0x430, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x418, /* QDSP_uPAudPreProcCmdQueue */ + 0x42c, /* QDSP_uPAudRecBitStreamQueue */ + 0x428, /* QDSP_uPAudRecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */ + QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */ +}; + +/* Tables to convert tasks to modules */ +static uint32_t *qdsp_task_to_module[] = { + qdsp_combo_task_to_module_table, + qdsp_gaudio_task_to_module_table, + qdsp_qtv_lp_task_to_module_table, +}; + +/* Tables to retrieve queue offsets */ +static uint32_t *qdsp_queue_offset_table[] = { + qdsp_combo_queue_offset_table, + qdsp_gaudio_queue_offset_table, + qdsp_qtv_lp_queue_offset_table, +}; + +#define QDSP_MODULE(n) \ + { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n } + +static struct adsp_module_info module_info[] = { + QDSP_MODULE(AUDPPTASK), + QDSP_MODULE(AUDRECTASK), + QDSP_MODULE(AUDPREPROCTASK), + QDSP_MODULE(VFETASK), + QDSP_MODULE(QCAMTASK), + QDSP_MODULE(LPMTASK), + QDSP_MODULE(JPEGTASK), + QDSP_MODULE(VIDEOTASK), + QDSP_MODULE(VDEC_LP_MODE), +}; + +int adsp_init_info(struct adsp_info *info) +{ + info->send_irq = 0x00c00200; + info->read_ctrl = 0x00400038; + info->write_ctrl = 0x00400034; + + info->max_msg16_size = 193; + info->max_msg32_size = 8; + + info->max_task_id = 16; + info->max_module_id = QDSP_MODULE_MAX - 1; + info->max_queue_id = QDSP_QUEUE_MAX; + info->max_image_id = 2; + info->queue_offset = qdsp_queue_offset_table; + info->task_to_module = qdsp_task_to_module; + + info->module_count = ARRAY_SIZE(module_info); + info->module = module_info; + return 0; +} diff --git a/arch/arm/mach-msm/qdsp5/adsp_6220.c b/arch/arm/mach-msm/qdsp5/adsp_6220.c new file mode 100644 index 0000000000000..c4c5a55271582 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_6220.c @@ -0,0 +1,284 @@ +/* arch/arm/mach-msm/qdsp5/adsp_6220.h + * + * Copyright (c) 2008 QUALCOMM Incorporated. + * + * 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 "adsp.h" + +/* Firmware modules */ +typedef enum { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_VIDEO_AAC_VOC, + QDSP_MODULE_PCM_DEC, + QDSP_MODULE_AUDIO_DEC_MP3, + QDSP_MODULE_AUDIO_DEC_AAC, + QDSP_MODULE_AUDIO_DEC_WMA, + QDSP_MODULE_HOSTPCM, + QDSP_MODULE_DTMF, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_SBC_ENC, + QDSP_MODULE_VOC, + QDSP_MODULE_VOC_PCM, + QDSP_MODULE_VOCENCTASK, + QDSP_MODULE_VOCDECTASK, + QDSP_MODULE_VOICEPROCTASK, + QDSP_MODULE_VIDEOENCTASK, + QDSP_MODULE_VFETASK, + QDSP_MODULE_WAV_ENC, + QDSP_MODULE_AACLC_ENC, + QDSP_MODULE_VIDEO_AMR, + QDSP_MODULE_VOC_AMR, + QDSP_MODULE_VOC_EVRC, + QDSP_MODULE_VOC_13K, + QDSP_MODULE_VOC_FGV, + QDSP_MODULE_DIAGTASK, + QDSP_MODULE_JPEGTASK, + QDSP_MODULE_LPMTASK, + QDSP_MODULE_QCAMTASK, + QDSP_MODULE_MODMATHTASK, + QDSP_MODULE_AUDPLAY2TASK, + QDSP_MODULE_AUDPLAY3TASK, + QDSP_MODULE_AUDPLAY4TASK, + QDSP_MODULE_GRAPHICSTASK, + QDSP_MODULE_MIDI, + QDSP_MODULE_GAUDIO, + QDSP_MODULE_VDEC_LP_MODE, + QDSP_MODULE_MAX, +} qdsp_module_type; + +#define QDSP_RTOS_MAX_TASK_ID 19U + +/* Table of modules indexed by task ID for the GAUDIO image */ +static qdsp_module_type qdsp_gaudio_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_AUDPLAY2TASK, + QDSP_MODULE_AUDPLAY3TASK, + QDSP_MODULE_AUDPLAY4TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_GRAPHICSTASK, + QDSP_MODULE_MAX +}; + +/* Queue offset table indexed by queue ID for the GAUDIO image */ +static uint32_t qdsp_gaudio_queue_offset_table[] = { + QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */ + 0x3f0, /* QDSP_mpuAfeQueue */ + 0x420, /* QDSP_mpuGraphicsCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */ + 0x3f4, /* QDSP_uPAudPPCmd1Queue */ + 0x3f8, /* QDSP_uPAudPPCmd2Queue */ + 0x3fc, /* QDSP_uPAudPPCmd3Queue */ + 0x40c, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + 0x410, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + 0x414, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + 0x418, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + 0x41c, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x400, /* QDSP_uPAudPreProcCmdQueue */ + 0x408, /* QDSP_uPAudRecBitStreamQueue */ + 0x404, /* QDSP_uPAudRecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */ + QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */ +}; + +/* Table of modules indexed by task ID for the COMBO image */ +static qdsp_module_type qdsp_combo_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_VOCDECTASK, + QDSP_MODULE_VOCENCTASK, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_VIDEOENCTASK, + QDSP_MODULE_VOICEPROCTASK, + QDSP_MODULE_VFETASK, + QDSP_MODULE_JPEGTASK, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_LPMTASK, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MODMATHTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX +}; + +/* Queue offset table indexed by queue ID for the COMBO image */ +static uint32_t qdsp_combo_queue_offset_table[] = { + 0x6f2, /* QDSP_lpmCommandQueue */ + 0x69e, /* QDSP_mpuAfeQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */ + 0x6b2, /* QDSP_mpuModmathCmdQueue */ + 0x6c6, /* QDSP_mpuVDecCmdQueue */ + 0x6ca, /* QDSP_mpuVDecPktQueue */ + 0x6c2, /* QDSP_mpuVEncCmdQueue */ + 0x6a6, /* QDSP_rxMpuDecCmdQueue */ + 0x6aa, /* QDSP_rxMpuDecPktQueue */ + 0x6ae, /* QDSP_txMpuEncQueue */ + 0x6ce, /* QDSP_uPAudPPCmd1Queue */ + 0x6d2, /* QDSP_uPAudPPCmd2Queue */ + 0x6d6, /* QDSP_uPAudPPCmd3Queue */ + 0x6e6, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x6da, /* QDSP_uPAudPreProcCmdQueue */ + 0x6e2, /* QDSP_uPAudRecBitStreamQueue */ + 0x6de, /* QDSP_uPAudRecCmdQueue */ + 0x6ee, /* QDSP_uPJpegActionCmdQueue */ + 0x6ea, /* QDSP_uPJpegCfgCmdQueue */ + 0x6a2, /* QDSP_uPVocProcQueue */ + 0x6b6, /* QDSP_vfeCommandQueue */ + 0x6be, /* QDSP_vfeCommandScaleQueue */ + 0x6ba /* QDSP_vfeCommandTableQueue */ +}; + +/* Table of modules indexed by task ID for the QTV_LP image */ +static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX +}; + +/* Queue offset table indexed by queue ID for the QTV_LP image */ +static uint32_t qdsp_qtv_lp_queue_offset_table[] = { + QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */ + 0x430, /* QDSP_mpuAfeQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */ + 0x434, /* QDSP_mpuVDecCmdQueue */ + 0x438, /* QDSP_mpuVDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */ + 0x440, /* QDSP_uPAudPPCmd1Queue */ + 0x444, /* QDSP_uPAudPPCmd2Queue */ + 0x448, /* QDSP_uPAudPPCmd3Queue */ + 0x454, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x43c, /* QDSP_uPAudPreProcCmdQueue */ + 0x450, /* QDSP_uPAudRecBitStreamQueue */ + 0x44c, /* QDSP_uPAudRecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */ + QDSP_RTOS_NO_QUEUE /* QDSP_vfeCommandTableQueue */ +}; + +/* Tables to convert tasks to modules */ +static qdsp_module_type *qdsp_task_to_module[] = { + qdsp_combo_task_to_module_table, + qdsp_gaudio_task_to_module_table, + qdsp_qtv_lp_task_to_module_table, +}; + +/* Tables to retrieve queue offsets */ +static uint32_t *qdsp_queue_offset_table[] = { + qdsp_combo_queue_offset_table, + qdsp_gaudio_queue_offset_table, + qdsp_qtv_lp_queue_offset_table, +}; + +#define QDSP_MODULE(n) \ + { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n } + +static struct adsp_module_info module_info[] = { + QDSP_MODULE(AUDPLAY0TASK), + QDSP_MODULE(AUDPPTASK), + QDSP_MODULE(AUDPREPROCTASK), + QDSP_MODULE(AUDRECTASK), + QDSP_MODULE(VFETASK), + QDSP_MODULE(QCAMTASK), + QDSP_MODULE(LPMTASK), + QDSP_MODULE(JPEGTASK), + QDSP_MODULE(VIDEOTASK), + QDSP_MODULE(VDEC_LP_MODE), +}; + +int adsp_init_info(struct adsp_info *info) +{ + info->send_irq = 0x00c00200; + info->read_ctrl = 0x00400038; + info->write_ctrl = 0x00400034; + + info->max_msg16_size = 193; + info->max_msg32_size = 8; + + info->max_task_id = 16; + info->max_module_id = QDSP_MODULE_MAX - 1; + info->max_queue_id = QDSP_QUEUE_MAX; + info->max_image_id = 2; + info->queue_offset = qdsp_queue_offset_table; + info->task_to_module = qdsp_task_to_module; + + info->module_count = ARRAY_SIZE(module_info); + info->module = module_info; + return 0; +} diff --git a/arch/arm/mach-msm/qdsp5/adsp_6225.c b/arch/arm/mach-msm/qdsp5/adsp_6225.c new file mode 100644 index 0000000000000..ceb9aa02719ef --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_6225.c @@ -0,0 +1,322 @@ +/* arch/arm/mach-msm/qdsp5/adsp_6225.h + * + * Copyright (c) 2008 QUALCOMM Incorporated. + * + * 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 "adsp.h" + +/* Firmware modules */ +typedef enum { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_VIDEO_AAC_VOC, + QDSP_MODULE_PCM_DEC, + QDSP_MODULE_AUDIO_DEC_MP3, + QDSP_MODULE_AUDIO_DEC_AAC, + QDSP_MODULE_AUDIO_DEC_WMA, + QDSP_MODULE_HOSTPCM, + QDSP_MODULE_DTMF, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_SBC_ENC, + QDSP_MODULE_VOC, + QDSP_MODULE_VOC_PCM, + QDSP_MODULE_VOCENCTASK, + QDSP_MODULE_VOCDECTASK, + QDSP_MODULE_VOICEPROCTASK, + QDSP_MODULE_VIDEOENCTASK, + QDSP_MODULE_VFETASK, + QDSP_MODULE_WAV_ENC, + QDSP_MODULE_AACLC_ENC, + QDSP_MODULE_VIDEO_AMR, + QDSP_MODULE_VOC_AMR, + QDSP_MODULE_VOC_EVRC, + QDSP_MODULE_VOC_13K, + QDSP_MODULE_VOC_FGV, + QDSP_MODULE_DIAGTASK, + QDSP_MODULE_JPEGTASK, + QDSP_MODULE_LPMTASK, + QDSP_MODULE_QCAMTASK, + QDSP_MODULE_MODMATHTASK, + QDSP_MODULE_AUDPLAY2TASK, + QDSP_MODULE_AUDPLAY3TASK, + QDSP_MODULE_AUDPLAY4TASK, + QDSP_MODULE_GRAPHICSTASK, + QDSP_MODULE_MIDI, + QDSP_MODULE_GAUDIO, + QDSP_MODULE_VDEC_LP_MODE, + QDSP_MODULE_MAX, +} qdsp_module_type; + +#define QDSP_RTOS_MAX_TASK_ID 30U + +/* Table of modules indexed by task ID for the GAUDIO image */ +static qdsp_module_type qdsp_gaudio_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_AUDPLAY2TASK, + QDSP_MODULE_AUDPLAY3TASK, + QDSP_MODULE_AUDPLAY4TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_GRAPHICSTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, +}; + +/* Queue offset table indexed by queue ID for the GAUDIO image */ +static uint32_t qdsp_gaudio_queue_offset_table[] = { + QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */ + 0x3f0, /* QDSP_mpuAfeQueue */ + 0x420, /* QDSP_mpuGraphicsCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */ + 0x3f4, /* QDSP_uPAudPPCmd1Queue */ + 0x3f8, /* QDSP_uPAudPPCmd2Queue */ + 0x3fc, /* QDSP_uPAudPPCmd3Queue */ + 0x40c, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + 0x410, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + 0x414, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + 0x418, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + 0x41c, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x400, /* QDSP_uPAudPreProcCmdQueue */ + 0x408, /* QDSP_uPAudRecBitStreamQueue */ + 0x404, /* QDSP_uPAudRecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandTableQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPDiagQueue */ +}; + +/* Table of modules indexed by task ID for the COMBO image */ +static qdsp_module_type qdsp_combo_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_VOCDECTASK, + QDSP_MODULE_VOCENCTASK, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_VIDEOENCTASK, + QDSP_MODULE_VOICEPROCTASK, + QDSP_MODULE_VFETASK, + QDSP_MODULE_JPEGTASK, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_AUDPLAY1TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_LPMTASK, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MODMATHTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_DIAGTASK, + QDSP_MODULE_MAX, +}; + +/* Queue offset table indexed by queue ID for the COMBO image */ +static uint32_t qdsp_combo_queue_offset_table[] = { + 0x714, /* QDSP_lpmCommandQueue */ + 0x6bc, /* QDSP_mpuAfeQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */ + 0x6d0, /* QDSP_mpuModmathCmdQueue */ + 0x6e8, /* QDSP_mpuVDecCmdQueue */ + 0x6ec, /* QDSP_mpuVDecPktQueue */ + 0x6e4, /* QDSP_mpuVEncCmdQueue */ + 0x6c4, /* QDSP_rxMpuDecCmdQueue */ + 0x6c8, /* QDSP_rxMpuDecPktQueue */ + 0x6cc, /* QDSP_txMpuEncQueue */ + 0x6f0, /* QDSP_uPAudPPCmd1Queue */ + 0x6f4, /* QDSP_uPAudPPCmd2Queue */ + 0x6f8, /* QDSP_uPAudPPCmd3Queue */ + 0x708, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x6fc, /* QDSP_uPAudPreProcCmdQueue */ + 0x704, /* QDSP_uPAudRecBitStreamQueue */ + 0x700, /* QDSP_uPAudRecCmdQueue */ + 0x710, /* QDSP_uPJpegActionCmdQueue */ + 0x70c, /* QDSP_uPJpegCfgCmdQueue */ + 0x6c0, /* QDSP_uPVocProcQueue */ + 0x6d8, /* QDSP_vfeCommandQueue */ + 0x6e0, /* QDSP_vfeCommandScaleQueue */ + 0x6dc, /* QDSP_vfeCommandTableQueue */ + 0x6d4, /* QDSP_uPDiagQueue */ +}; + +/* Table of modules indexed by task ID for the QTV_LP image */ +static qdsp_module_type qdsp_qtv_lp_task_to_module_table[] = { + QDSP_MODULE_KERNEL, + QDSP_MODULE_AFETASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_VIDEOTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDPPTASK, + QDSP_MODULE_AUDPLAY0TASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_AUDRECTASK, + QDSP_MODULE_AUDPREPROCTASK, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, + QDSP_MODULE_MAX, +}; + +/* Queue offset table indexed by queue ID for the QTV_LP image */ +static uint32_t qdsp_qtv_lp_queue_offset_table[] = { + QDSP_RTOS_NO_QUEUE, /* QDSP_lpmCommandQueue */ + 0x3fe, /* QDSP_mpuAfeQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuGraphicsCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuModmathCmdQueue */ + 0x402, /* QDSP_mpuVDecCmdQueue */ + 0x406, /* QDSP_mpuVDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_mpuVEncCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_rxMpuDecPktQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_txMpuEncQueue */ + 0x40e, /* QDSP_uPAudPPCmd1Queue */ + 0x412, /* QDSP_uPAudPPCmd2Queue */ + 0x416, /* QDSP_uPAudPPCmd3Queue */ + 0x422, /* QDSP_uPAudPlay0BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay1BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay2BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay3BitStreamCtrlQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPAudPlay4BitStreamCtrlQueue */ + 0x40a, /* QDSP_uPAudPreProcCmdQueue */ + 0x41e, /* QDSP_uPAudRecBitStreamQueue */ + 0x41a, /* QDSP_uPAudRecCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegActionCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPJpegCfgCmdQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPVocProcQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandScaleQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_vfeCommandTableQueue */ + QDSP_RTOS_NO_QUEUE, /* QDSP_uPDiagQueue */ +}; + +/* Tables to convert tasks to modules */ +static qdsp_module_type *qdsp_task_to_module[] = { + qdsp_combo_task_to_module_table, + qdsp_gaudio_task_to_module_table, + qdsp_qtv_lp_task_to_module_table, +}; + +/* Tables to retrieve queue offsets */ +static uint32_t *qdsp_queue_offset_table[] = { + qdsp_combo_queue_offset_table, + qdsp_gaudio_queue_offset_table, + qdsp_qtv_lp_queue_offset_table, +}; + +#define QDSP_MODULE(n, clkname, clkrate, verify_cmd_func, patch_event_func) \ + { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n, \ + .clk_name=clkname, .clk_rate=clkrate, \ + .verify_cmd = verify_cmd_func, .patch_event = patch_event_func } + +static struct adsp_module_info module_info[] = { + QDSP_MODULE(AUDPLAY0TASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDPPTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDRECTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDPREPROCTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(VFETASK, "vfe_clk", 0, adsp_vfe_verify_cmd, adsp_vfe_patch_event), + QDSP_MODULE(QCAMTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(LPMTASK, NULL, 0, adsp_lpm_verify_cmd, NULL), + QDSP_MODULE(JPEGTASK, "vdc_clk", 0, adsp_jpeg_verify_cmd, adsp_jpeg_patch_event), + QDSP_MODULE(VIDEOTASK, "vdc_clk", 96000000, adsp_video_verify_cmd, NULL), + QDSP_MODULE(VDEC_LP_MODE, NULL, 0, NULL, NULL), +}; + +int adsp_init_info(struct adsp_info *info) +{ + info->send_irq = 0x00c00200; + info->read_ctrl = 0x00400038; + info->write_ctrl = 0x00400034; + + info->max_msg16_size = 193; + info->max_msg32_size = 8; + + info->max_task_id = 16; + info->max_module_id = QDSP_MODULE_MAX - 1; + info->max_queue_id = QDSP_QUEUE_MAX; + info->max_image_id = 2; + info->queue_offset = qdsp_queue_offset_table; + info->task_to_module = qdsp_task_to_module; + + info->module_count = ARRAY_SIZE(module_info); + info->module = module_info; + return 0; +} diff --git a/arch/arm/mach-msm/qdsp5/adsp_driver.c b/arch/arm/mach-msm/qdsp5/adsp_driver.c new file mode 100644 index 0000000000000..343e0cfde1ef0 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_driver.c @@ -0,0 +1,615 @@ +/* arch/arm/mach-msm/qdsp5/adsp_driver.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Iliyan Malchev + * + * 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 "adsp.h" + +#include +#include + +struct adsp_pmem_info { + int fd; + void *vaddr; +}; + +struct adsp_pmem_region { + struct hlist_node list; + void *vaddr; + unsigned long paddr; + unsigned long kvaddr; + unsigned long len; + struct file *file; +}; + +struct adsp_device { + struct msm_adsp_module *module; + + spinlock_t event_queue_lock; + wait_queue_head_t event_wait; + struct list_head event_queue; + int abort; + + const char *name; + struct device *device; + struct cdev cdev; +}; + +static struct adsp_device *inode_to_device(struct inode *inode); + +#define __CONTAINS(r, v, l) ({ \ + typeof(r) __r = r; \ + typeof(v) __v = v; \ + typeof(v) __e = __v + l; \ + int res = __v >= __r->vaddr && \ + __e <= __r->vaddr + __r->len; \ + res; \ +}) + +#define CONTAINS(r1, r2) ({ \ + typeof(r2) __r2 = r2; \ + __CONTAINS(r1, __r2->vaddr, __r2->len); \ +}) + +#define IN_RANGE(r, v) ({ \ + typeof(r) __r = r; \ + typeof(v) __vv = v; \ + int res = ((__vv >= __r->vaddr) && \ + (__vv < (__r->vaddr + __r->len))); \ + res; \ +}) + +#define OVERLAPS(r1, r2) ({ \ + typeof(r1) __r1 = r1; \ + typeof(r2) __r2 = r2; \ + typeof(__r2->vaddr) __v = __r2->vaddr; \ + typeof(__v) __e = __v + __r2->len; \ + int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ + res; \ +}) + +static int adsp_pmem_check(struct msm_adsp_module *module, + void *vaddr, unsigned long len) +{ + struct adsp_pmem_region *region_elt; + struct hlist_node *node; + struct adsp_pmem_region t = { .vaddr = vaddr, .len = len }; + + hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) { + if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) || + OVERLAPS(region_elt, &t)) { + printk(KERN_ERR "adsp: module %s:" + " region (vaddr %p len %ld)" + " clashes with registered region" + " (vaddr %p paddr %p len %ld)\n", + module->name, + vaddr, len, + region_elt->vaddr, + (void *)region_elt->paddr, + region_elt->len); + return -EINVAL; + } + } + + return 0; +} + +static int adsp_pmem_add(struct msm_adsp_module *module, + struct adsp_pmem_info *info) +{ + unsigned long paddr, kvaddr, len; + struct file *file; + struct adsp_pmem_region *region; + int rc = -EINVAL; + + mutex_lock(&module->pmem_regions_lock); + region = kmalloc(sizeof(*region), GFP_KERNEL); + if (!region) { + rc = -ENOMEM; + goto end; + } + INIT_HLIST_NODE(®ion->list); + if (get_pmem_file(info->fd, &paddr, &kvaddr, &len, &file)) { + kfree(region); + goto end; + } + + if ((rc = adsp_pmem_check(module, info->vaddr, len)) < 0) { + put_pmem_file(file); + kfree(region); + goto end; + } + + region->vaddr = info->vaddr; + region->paddr = paddr; + region->kvaddr = kvaddr; + region->len = len; + region->file = file; + + hlist_add_head(®ion->list, &module->pmem_regions); +end: + mutex_unlock(&module->pmem_regions_lock); + return rc; +} + +static int adsp_pmem_lookup_vaddr(struct msm_adsp_module *module, void **addr, + unsigned long len, struct adsp_pmem_region** region) +{ + struct hlist_node *node; + void *vaddr = *addr; + struct adsp_pmem_region *region_elt; + + int match_count = 0; + + *region = NULL; + + /* returns physical address or zero */ + hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) { + if (vaddr >= region_elt->vaddr && + vaddr < region_elt->vaddr + region_elt->len && + vaddr + len <= region_elt->vaddr + region_elt->len) { + /* offset since we could pass vaddr inside a registerd + * pmem buffer + */ + + match_count++; + if (!*region) *region = region_elt; + } + } + + if (match_count > 1) { + printk(KERN_ERR "adsp: module %s: multiple hits for vaddr %p, len %ld\n", + module->name, vaddr, len); + hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) { + if (vaddr >= region_elt->vaddr && + vaddr < region_elt->vaddr + region_elt->len && + vaddr + len <= region_elt->vaddr + region_elt->len) + printk(KERN_ERR "\t%p, %ld --> %p\n", region_elt->vaddr, + region_elt->len, (void *)region_elt->paddr); + } + } + + return *region ? 0 : -1; +} + +int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr, + unsigned long *kvaddr, unsigned long len) +{ + struct adsp_pmem_region *region; + void *vaddr = *addr; + unsigned long *paddr = (unsigned long *)addr; + int ret; + + ret = adsp_pmem_lookup_vaddr(module, addr, len, ®ion); + if (ret) { + printk(KERN_ERR "adsp: not patching %s (paddr & kvaddr)," + " lookup (%p, %ld) failed\n", + module->name, vaddr, len); + return ret; + } + *paddr = region->paddr + (vaddr - region->vaddr); + *kvaddr = region->kvaddr + (vaddr - region->vaddr); + return 0; +} + +int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr, + unsigned long len) +{ + struct adsp_pmem_region *region; + void *vaddr = *addr; + unsigned long *paddr = (unsigned long *)addr; + int ret; + + ret = adsp_pmem_lookup_vaddr(module, addr, len, ®ion); + if (ret) { + printk(KERN_ERR "adsp: not patching %s, lookup (%p, %ld) failed\n", + module->name, vaddr, len); + return ret; + } + + *paddr = region->paddr + (vaddr - region->vaddr); + return 0; +} + +static int adsp_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size) +{ + /* call the per module verifier */ + if (module->verify_cmd) + return module->verify_cmd(module, queue_id, cmd_data, + cmd_size); + else + printk(KERN_INFO "adsp: no packet verifying function " + "for task %s\n", module->name); + return 0; +} + +static long adsp_write_cmd(struct adsp_device *adev, void __user *arg) +{ + struct adsp_command_t cmd; + unsigned char buf[256]; + void *cmd_data; + long rc; + + if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd))) + return -EFAULT; + + if (cmd.len > 256) { + cmd_data = kmalloc(cmd.len, GFP_USER); + if (!cmd_data) + return -ENOMEM; + } else { + cmd_data = buf; + } + + if (copy_from_user(cmd_data, (void __user *)(cmd.data), cmd.len)) { + rc = -EFAULT; + goto end; + } + + mutex_lock(&adev->module->pmem_regions_lock); + if (adsp_verify_cmd(adev->module, cmd.queue, cmd_data, cmd.len)) { + printk(KERN_ERR "module %s: verify failed.\n", + adev->module->name); + rc = -EINVAL; + goto end; + } + rc = msm_adsp_write(adev->module, cmd.queue, cmd_data, cmd.len); +end: + mutex_unlock(&adev->module->pmem_regions_lock); + + if (cmd.len > 256) + kfree(cmd_data); + + return rc; +} + +static int adsp_events_pending(struct adsp_device *adev) +{ + unsigned long flags; + int yes; + spin_lock_irqsave(&adev->event_queue_lock, flags); + yes = !list_empty(&adev->event_queue); + spin_unlock_irqrestore(&adev->event_queue_lock, flags); + return yes || adev->abort; +} + +static int adsp_pmem_lookup_paddr(struct msm_adsp_module *module, void **addr, + struct adsp_pmem_region** region) +{ + struct hlist_node *node; + unsigned long paddr = (unsigned long)(*addr); + struct adsp_pmem_region *region_elt; + + hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) { + if (paddr >= region_elt->paddr && + paddr < region_elt->paddr + region_elt->len) { + *region = region_elt; + return 0; + } + } + return -1; +} + +int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr) +{ + struct adsp_pmem_region *region; + unsigned long paddr = (unsigned long)(*addr); + unsigned long *vaddr = (unsigned long *)addr; + int ret; + + ret = adsp_pmem_lookup_paddr(module, addr, ®ion); + if (ret) { + printk(KERN_ERR "adsp: not patching %s, paddr %p lookup failed\n", + module->name, vaddr); + return ret; + } + + *vaddr = (unsigned long)region->vaddr + (paddr - region->paddr); + return 0; +} + +static int adsp_patch_event(struct msm_adsp_module *module, + struct adsp_event *event) +{ + /* call the per-module msg verifier */ + if (module->patch_event) + return module->patch_event(module, event); + return 0; +} + +static long adsp_get_event(struct adsp_device *adev, void __user *arg) +{ + unsigned long flags; + struct adsp_event *data = NULL; + struct adsp_event_t evt; + int timeout; + long rc = 0; + + if (copy_from_user(&evt, arg, sizeof(struct adsp_event_t))) + return -EFAULT; + + timeout = (int)evt.timeout_ms; + + if (timeout > 0) { + rc = wait_event_interruptible_timeout( + adev->event_wait, adsp_events_pending(adev), + msecs_to_jiffies(timeout)); + if (rc == 0) + return -ETIMEDOUT; + } else { + rc = wait_event_interruptible( + adev->event_wait, adsp_events_pending(adev)); + } + if (rc < 0) + return rc; + + if (adev->abort) + return -ENODEV; + + spin_lock_irqsave(&adev->event_queue_lock, flags); + if (!list_empty(&adev->event_queue)) { + data = list_first_entry(&adev->event_queue, + struct adsp_event, list); + list_del(&data->list); + } + spin_unlock_irqrestore(&adev->event_queue_lock, flags); + + if (!data) + return -EAGAIN; + + /* DSP messages are type 0; they may contain physical addresses */ + if (data->type == 0) + adsp_patch_event(adev->module, data); + + /* map adsp_event --> adsp_event_t */ + if (evt.len < data->size) { + rc = -ETOOSMALL; + goto end; + } + if (copy_to_user((void *)(evt.data), data->data.msg16, data->size)) { + rc = -EFAULT; + goto end; + } + + evt.type = data->type; /* 0 --> from aDSP, 1 --> from ARM9 */ + evt.msg_id = data->msg_id; + evt.flags = data->is16; + evt.len = data->size; + if (copy_to_user(arg, &evt, sizeof(evt))) + rc = -EFAULT; +end: + kfree(data); + return rc; +} + +static long adsp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct adsp_device *adev = filp->private_data; + + switch (cmd) { + case ADSP_IOCTL_ENABLE: + return msm_adsp_enable(adev->module); + + case ADSP_IOCTL_DISABLE: + return msm_adsp_disable(adev->module); + + case ADSP_IOCTL_DISABLE_EVENT_RSP: + return 0; + + case ADSP_IOCTL_DISABLE_ACK: + pr_err("adsp: ADSP_IOCTL_DISABLE_ACK is not implemented.\n"); + break; + + case ADSP_IOCTL_WRITE_COMMAND: + return adsp_write_cmd(adev, (void __user *) arg); + + case ADSP_IOCTL_GET_EVENT: + return adsp_get_event(adev, (void __user *) arg); + + case ADSP_IOCTL_REGISTER_PMEM: { + struct adsp_pmem_info info; + if (copy_from_user(&info, (void *) arg, sizeof(info))) + return -EFAULT; + return adsp_pmem_add(adev->module, &info); + } + + case ADSP_IOCTL_ABORT_EVENT_READ: + adev->abort = 1; + wake_up(&adev->event_wait); + break; + + default: + break; + } + return -EINVAL; +} + +static int adsp_release(struct inode *inode, struct file *filp) +{ + struct adsp_device *adev = filp->private_data; + struct msm_adsp_module *module = adev->module; + struct hlist_node *node, *tmp; + struct adsp_pmem_region *region; + + pr_info("adsp_release() '%s'\n", adev->name); + + /* clear module before putting it to avoid race with open() */ + adev->module = NULL; + + mutex_lock(&module->pmem_regions_lock); + hlist_for_each_safe(node, tmp, &module->pmem_regions) { + region = hlist_entry(node, struct adsp_pmem_region, list); + hlist_del(node); + put_pmem_file(region->file); + kfree(region); + } + mutex_unlock(&module->pmem_regions_lock); + BUG_ON(!hlist_empty(&module->pmem_regions)); + + msm_adsp_put(module); + return 0; +} + +static void adsp_event(void *driver_data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct adsp_device *adev = driver_data; + struct adsp_event *event; + unsigned long flags; + + if (len > ADSP_EVENT_MAX_SIZE) { + pr_err("adsp_event: event too large (%d bytes)\n", len); + return; + } + + event = kmalloc(sizeof(*event), GFP_ATOMIC); + if (!event) { + pr_err("adsp_event: cannot allocate buffer\n"); + return; + } + + event->type = 0; + event->is16 = 0; + event->msg_id = id; + event->size = len; + + getevent(event->data.msg16, len); + + spin_lock_irqsave(&adev->event_queue_lock, flags); + list_add_tail(&event->list, &adev->event_queue); + spin_unlock_irqrestore(&adev->event_queue_lock, flags); + wake_up(&adev->event_wait); +} + +static struct msm_adsp_ops adsp_ops = { + .event = adsp_event, +}; + +static int adsp_open(struct inode *inode, struct file *filp) +{ + struct adsp_device *adev; + int rc; + + rc = nonseekable_open(inode, filp); + if (rc < 0) + return rc; + + adev = inode_to_device(inode); + if (!adev) + return -ENODEV; + + pr_info("adsp_open() name = '%s'\n", adev->name); + + rc = msm_adsp_get(adev->name, &adev->module, &adsp_ops, adev); + if (rc) + return rc; + + pr_info("adsp_open() module '%s' adev %p\n", adev->name, adev); + filp->private_data = adev; + adev->abort = 0; + INIT_HLIST_HEAD(&adev->module->pmem_regions); + mutex_init(&adev->module->pmem_regions_lock); + + return 0; +} + +static unsigned adsp_device_count; +static struct adsp_device *adsp_devices; + +static struct adsp_device *inode_to_device(struct inode *inode) +{ + unsigned n = MINOR(inode->i_rdev); + if (n < adsp_device_count) { + if (adsp_devices[n].device) + return adsp_devices + n; + } + return NULL; +} + +static dev_t adsp_devno; +static struct class *adsp_class; + +static struct file_operations adsp_fops = { + .owner = THIS_MODULE, + .open = adsp_open, + .unlocked_ioctl = adsp_ioctl, + .release = adsp_release, +}; + +static void adsp_create(struct adsp_device *adev, const char *name, + struct device *parent, dev_t devt) +{ + struct device *dev; + int rc; + + dev = device_create(adsp_class, parent, devt, "%s", name); + if (IS_ERR(dev)) + return; + + init_waitqueue_head(&adev->event_wait); + INIT_LIST_HEAD(&adev->event_queue); + spin_lock_init(&adev->event_queue_lock); + + cdev_init(&adev->cdev, &adsp_fops); + adev->cdev.owner = THIS_MODULE; + + rc = cdev_add(&adev->cdev, devt, 1); + if (rc < 0) { + device_destroy(adsp_class, devt); + } else { + adev->device = dev; + adev->name = name; + } +} + +void msm_adsp_publish_cdevs(struct msm_adsp_module *modules, unsigned n) +{ + int rc; + + adsp_devices = kzalloc(sizeof(struct adsp_device) * n, GFP_KERNEL); + if (!adsp_devices) + return; + + adsp_class = class_create(THIS_MODULE, "adsp"); + if (IS_ERR(adsp_class)) + goto fail_create_class; + + rc = alloc_chrdev_region(&adsp_devno, 0, n, "adsp"); + if (rc < 0) + goto fail_alloc_region; + + adsp_device_count = n; + for (n = 0; n < adsp_device_count; n++) { + adsp_create(adsp_devices + n, + modules[n].name, &modules[n].pdev.dev, + MKDEV(MAJOR(adsp_devno), n)); + } + + return; + +fail_alloc_region: + class_unregister(adsp_class); +fail_create_class: + kfree(adsp_devices); +} diff --git a/arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c b/arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c new file mode 100644 index 0000000000000..4f493edb6c946 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c @@ -0,0 +1,31 @@ +/* arch/arm/mach-msm/qdsp5/adsp_jpeg_patch_event.c + * + * Verification code for aDSP JPEG events. + * + * Copyright (c) 2008 QUALCOMM Incorporated + * Copyright (C) 2008 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 "adsp.h" + +int adsp_jpeg_patch_event(struct msm_adsp_module *module, + struct adsp_event *event) +{ + if (event->msg_id == JPEG_MSG_ENC_OP_PRODUCED) { + jpeg_msg_enc_op_produced *op = (jpeg_msg_enc_op_produced *)event->data.msg16; + return adsp_pmem_paddr_fixup(module, (void **)&op->op_buf_addr); + } + + return 0; +} diff --git a/arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c new file mode 100644 index 0000000000000..f770c141a4140 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c @@ -0,0 +1,181 @@ +/* arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c + * + * Verification code for aDSP JPEG packets from userspace. + * + * Copyright (c) 2008 QUALCOMM Incorporated + * Copyright (C) 2008 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 "adsp.h" + +static uint32_t dec_fmt; + +static inline void get_sizes(jpeg_cmd_enc_cfg *cmd, uint32_t *luma_size, + uint32_t *chroma_size) +{ + uint32_t fmt, luma_width, luma_height; + + fmt = cmd->process_cfg & JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_M; + luma_width = (cmd->ip_size_cfg & JPEG_CMD_IP_SIZE_CFG_LUMA_WIDTH_M) + >> 16; + luma_height = cmd->ip_size_cfg & JPEG_CMD_FRAG_SIZE_LUMA_HEIGHT_M; + *luma_size = luma_width * luma_height; + if (fmt == JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_H2V2) + *chroma_size = *luma_size/2; + else + *chroma_size = *luma_size; +} + +static inline int verify_jpeg_cmd_enc_cfg(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + jpeg_cmd_enc_cfg *cmd = (jpeg_cmd_enc_cfg *)cmd_data; + uint32_t luma_size, chroma_size; + int i, num_frags; + + if (cmd_size != sizeof(jpeg_cmd_enc_cfg)) { + printk(KERN_ERR "adsp: module %s: JPEG ENC CFG invalid cmd_size %d\n", + module->name, cmd_size); + return -1; + } + + get_sizes(cmd, &luma_size, &chroma_size); + num_frags = (cmd->process_cfg >> 10) & 0xf; + for (i = 0; i < num_frags; i += 2) { + if (adsp_pmem_fixup(module, (void **)(&cmd->frag_cfg_part[i]), luma_size) || + adsp_pmem_fixup(module, (void **)(&cmd->frag_cfg_part[i+1]), chroma_size)) + return -1; + } + + if (adsp_pmem_fixup(module, (void **)&cmd->op_buf_0_cfg_part1, + cmd->op_buf_0_cfg_part2) || + adsp_pmem_fixup(module, (void **)&cmd->op_buf_1_cfg_part1, + cmd->op_buf_1_cfg_part2)) + return -1; + return 0; +} + +static inline int verify_jpeg_cmd_dec_cfg(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + jpeg_cmd_dec_cfg *cmd = (jpeg_cmd_dec_cfg *)cmd_data; + uint32_t div; + + if (cmd_size != sizeof(jpeg_cmd_dec_cfg)) { + printk(KERN_ERR "adsp: module %s: JPEG DEC CFG invalid cmd_size %d\n", + module->name, cmd_size); + return -1; + } + + if (adsp_pmem_fixup(module, (void **)&cmd->ip_stream_buf_cfg_part1, + cmd->ip_stream_buf_cfg_part2) || + adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_0_cfg_part1, + cmd->op_stream_buf_0_cfg_part2) || + adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_1_cfg_part1, + cmd->op_stream_buf_1_cfg_part2)) + return -1; + dec_fmt = cmd->op_data_format & + JPEG_CMD_DEC_OP_DATA_FORMAT_M; + div = (dec_fmt == JPEG_CMD_DEC_OP_DATA_FORMAT_H2V2) ? 2 : 1; + if (adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_0_cfg_part3, + cmd->op_stream_buf_0_cfg_part2 / div) || + adsp_pmem_fixup(module, (void **)&cmd->op_stream_buf_1_cfg_part3, + cmd->op_stream_buf_1_cfg_part2 / div)) + return -1; + return 0; +} + +static int verify_jpeg_cfg_cmd(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + uint32_t cmd_id = ((uint32_t *)cmd_data)[0]; + switch(cmd_id) { + case JPEG_CMD_ENC_CFG: + return verify_jpeg_cmd_enc_cfg(module, cmd_data, cmd_size); + case JPEG_CMD_DEC_CFG: + return verify_jpeg_cmd_dec_cfg(module, cmd_data, cmd_size); + default: + if (cmd_id > 1) { + printk(KERN_ERR "adsp: module %s: invalid JPEG CFG cmd_id %d\n", module->name, cmd_id); + return -1; + } + } + return 0; +} + +static int verify_jpeg_action_cmd(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + uint32_t cmd_id = ((uint32_t *)cmd_data)[0]; + switch (cmd_id) { + case JPEG_CMD_ENC_OP_CONSUMED: + { + jpeg_cmd_enc_op_consumed *cmd = + (jpeg_cmd_enc_op_consumed *)cmd_data; + + if (cmd_size != sizeof(jpeg_cmd_enc_op_consumed)) { + printk(KERN_ERR "adsp: module %s: JPEG_CMD_ENC_OP_CONSUMED invalid size %d\n", + module->name, cmd_size); + return -1; + } + + if (adsp_pmem_fixup(module, (void **)&cmd->op_buf_addr, + cmd->op_buf_size)) + return -1; + } + break; + case JPEG_CMD_DEC_OP_CONSUMED: + { + uint32_t div; + jpeg_cmd_dec_op_consumed *cmd = + (jpeg_cmd_dec_op_consumed *)cmd_data; + + if (cmd_size != sizeof(jpeg_cmd_enc_op_consumed)) { + printk(KERN_ERR "adsp: module %s: JPEG_CMD_DEC_OP_CONSUMED invalid size %d\n", + module->name, cmd_size); + return -1; + } + + div = (dec_fmt == JPEG_CMD_DEC_OP_DATA_FORMAT_H2V2) ? 2 : 1; + if (adsp_pmem_fixup(module, (void **)&cmd->luma_op_buf_addr, + cmd->luma_op_buf_size) || + adsp_pmem_fixup(module, (void **)&cmd->chroma_op_buf_addr, + cmd->luma_op_buf_size / div)) + return -1; + } + break; + default: + if (cmd_id > 7) { + printk(KERN_ERR "adsp: module %s: invalid cmd_id %d\n", + module->name, cmd_id); + return -1; + } + } + return 0; +} + +int adsp_jpeg_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size) +{ + switch(queue_id) { + case QDSP_uPJpegCfgCmdQueue: + return verify_jpeg_cfg_cmd(module, cmd_data, cmd_size); + case QDSP_uPJpegActionCmdQueue: + return verify_jpeg_action_cmd(module, cmd_data, cmd_size); + default: + return -1; + } +} + diff --git a/arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c new file mode 100644 index 0000000000000..1e23ef392700f --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c @@ -0,0 +1,65 @@ +/* arch/arm/mach-msm/qdsp5/adsp_lpm_verify_cmd.c + * + * Verificion code for aDSP LPM packets from userspace. + * + * Copyright (c) 2008 QUALCOMM Incorporated + * Copyright (C) 2008 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 "adsp.h" + +int adsp_lpm_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size) +{ + uint32_t cmd_id, col_height, input_row_incr, output_row_incr, + input_size, output_size; + uint32_t size_mask = 0x0fff; + lpm_cmd_start *cmd; + + if (queue_id != QDSP_lpmCommandQueue) { + printk(KERN_ERR "adsp: module %s: wrong queue id %d\n", + module->name, queue_id); + return -1; + } + + cmd = (lpm_cmd_start *)cmd_data; + cmd_id = cmd->cmd_id; + + if (cmd_id == LPM_CMD_START) { + if (cmd_size != sizeof(lpm_cmd_start)) { + printk(KERN_ERR "adsp: module %s: wrong size %d, expect %d\n", + module->name, cmd_size, sizeof(lpm_cmd_start)); + return -1; + } + col_height = cmd->ip_data_cfg_part1 & size_mask; + input_row_incr = cmd->ip_data_cfg_part2 & size_mask; + output_row_incr = cmd->op_data_cfg_part1 & size_mask; + input_size = col_height * input_row_incr; + output_size = col_height * output_row_incr; + if ((cmd->ip_data_cfg_part4 && adsp_pmem_fixup(module, + (void **)(&cmd->ip_data_cfg_part4), + input_size)) || + (cmd->op_data_cfg_part3 && adsp_pmem_fixup(module, + (void **)(&cmd->op_data_cfg_part3), + output_size))) + return -1; + } else if (cmd_id > 1) { + printk(KERN_ERR "adsp: module %s: invalid cmd_id %d\n", + module->name, cmd_id); + return -1; + } + return 0; +} + diff --git a/arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c b/arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c new file mode 100644 index 0000000000000..8f09ed237174b --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c @@ -0,0 +1,54 @@ +/* arch/arm/mach-msm/qdsp5/adsp_vfe_patch_event.c + * + * Verification code for aDSP VFE packets from userspace. + * + * Copyright (c) 2008 QUALCOMM Incorporated + * Copyright (C) 2008 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 "adsp.h" + +static int patch_op_event(struct msm_adsp_module *module, + struct adsp_event *event) +{ + vfe_msg_op1 *op = (vfe_msg_op1 *)event->data.msg16; + if (adsp_pmem_paddr_fixup(module, (void **)&op->op1_buf_y_addr) || + adsp_pmem_paddr_fixup(module, (void **)&op->op1_buf_cbcr_addr)) + return -1; + return 0; +} + +static int patch_af_wb_event(struct msm_adsp_module *module, + struct adsp_event *event) +{ + vfe_msg_stats_wb_exp *af = (vfe_msg_stats_wb_exp *)event->data.msg16; + return adsp_pmem_paddr_fixup(module, (void **)&af->wb_exp_stats_op_buf); +} + +int adsp_vfe_patch_event(struct msm_adsp_module *module, + struct adsp_event *event) +{ + switch(event->msg_id) { + case VFE_MSG_OP1: + case VFE_MSG_OP2: + return patch_op_event(module, event); + case VFE_MSG_STATS_AF: + case VFE_MSG_STATS_WB_EXP: + return patch_af_wb_event(module, event); + default: + break; + } + + return 0; +} diff --git a/arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c new file mode 100644 index 0000000000000..d1f3fa8a635cd --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c @@ -0,0 +1,239 @@ +/* arch/arm/mach-msm/qdsp5/adsp_vfe_verify_cmd.c + * + * Verification code for aDSP VFE packets from userspace. + * + * Copyright (c) 2008 QUALCOMM Incorporated + * Copyright (C) 2008 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 "adsp.h" + +static uint32_t size1_y, size2_y, size1_cbcr, size2_cbcr; +static uint32_t af_size = 4228; +static uint32_t awb_size = 8196; + +static inline int verify_cmd_op_ack(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + vfe_cmd_op1_ack *cmd = (vfe_cmd_op1_ack *)cmd_data; + void **addr_y = (void **)&cmd->op1_buf_y_addr; + void **addr_cbcr = (void **)(&cmd->op1_buf_cbcr_addr); + + if (cmd_size != sizeof(vfe_cmd_op1_ack)) + return -1; + if ((*addr_y && adsp_pmem_fixup(module, addr_y, size1_y)) || + (*addr_cbcr && adsp_pmem_fixup(module, addr_cbcr, size1_cbcr))) + return -1; + return 0; +} + +static inline int verify_cmd_stats_autofocus_cfg(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + int i; + vfe_cmd_stats_autofocus_cfg *cmd = + (vfe_cmd_stats_autofocus_cfg *)cmd_data; + + if (cmd_size != sizeof(vfe_cmd_stats_autofocus_cfg)) + return -1; + + for (i = 0; i < 3; i++) { + void **addr = (void **)(&cmd->af_stats_op_buf[i]); + if (*addr && adsp_pmem_fixup(module, addr, af_size)) + return -1; + } + return 0; +} + +static inline int verify_cmd_stats_wb_exp_cfg(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + vfe_cmd_stats_wb_exp_cfg *cmd = + (vfe_cmd_stats_wb_exp_cfg *)cmd_data; + int i; + + if (cmd_size != sizeof(vfe_cmd_stats_wb_exp_cfg)) + return -1; + + for (i = 0; i < 3; i++) { + void **addr = (void **)(&cmd->wb_exp_stats_op_buf[i]); + if (*addr && adsp_pmem_fixup(module, addr, awb_size)) + return -1; + } + return 0; +} + +static inline int verify_cmd_stats_af_ack(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + vfe_cmd_stats_af_ack *cmd = (vfe_cmd_stats_af_ack *)cmd_data; + void **addr = (void **)&cmd->af_stats_op_buf; + + if (cmd_size != sizeof(vfe_cmd_stats_af_ack)) + return -1; + + if (*addr && adsp_pmem_fixup(module, addr, af_size)) + return -1; + return 0; +} + +static inline int verify_cmd_stats_wb_exp_ack(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + vfe_cmd_stats_wb_exp_ack *cmd = + (vfe_cmd_stats_wb_exp_ack *)cmd_data; + void **addr = (void **)&cmd->wb_exp_stats_op_buf; + + if (cmd_size != sizeof(vfe_cmd_stats_wb_exp_ack)) + return -1; + + if (*addr && adsp_pmem_fixup(module, addr, awb_size)) + return -1; + return 0; +} + +static int verify_vfe_command(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + uint32_t cmd_id = ((uint32_t *)cmd_data)[0]; + switch (cmd_id) { + case VFE_CMD_OP1_ACK: + return verify_cmd_op_ack(module, cmd_data, cmd_size); + case VFE_CMD_OP2_ACK: + return verify_cmd_op_ack(module, cmd_data, cmd_size); + case VFE_CMD_STATS_AUTOFOCUS_CFG: + return verify_cmd_stats_autofocus_cfg(module, cmd_data, + cmd_size); + case VFE_CMD_STATS_WB_EXP_CFG: + return verify_cmd_stats_wb_exp_cfg(module, cmd_data, cmd_size); + case VFE_CMD_STATS_AF_ACK: + return verify_cmd_stats_af_ack(module, cmd_data, cmd_size); + case VFE_CMD_STATS_WB_EXP_ACK: + return verify_cmd_stats_wb_exp_ack(module, cmd_data, cmd_size); + default: + if (cmd_id > 29) { + printk(KERN_ERR "adsp: module %s: invalid VFE command id %d\n", module->name, cmd_id); + return -1; + } + } + return 0; +} + +static int verify_vfe_command_scale(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + uint32_t cmd_id = ((uint32_t *)cmd_data)[0]; + // FIXME: check the size + if (cmd_id > 1) { + printk(KERN_ERR "adsp: module %s: invalid VFE SCALE command id %d\n", module->name, cmd_id); + return -1; + } + return 0; +} + + +static uint32_t get_size(uint32_t hw) +{ + uint32_t height, width; + uint32_t height_mask = 0x3ffc; + uint32_t width_mask = 0x3ffc000; + + height = (hw & height_mask) >> 2; + width = (hw & width_mask) >> 14 ; + return height * width; +} + +static int verify_vfe_command_table(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + uint32_t cmd_id = ((uint32_t *)cmd_data)[0]; + int i; + + switch (cmd_id) { + case VFE_CMD_AXI_IP_CFG: + { + vfe_cmd_axi_ip_cfg *cmd = (vfe_cmd_axi_ip_cfg *)cmd_data; + uint32_t size; + if (cmd_size != sizeof(vfe_cmd_axi_ip_cfg)) { + printk(KERN_ERR "adsp: module %s: invalid VFE TABLE (VFE_CMD_AXI_IP_CFG) command size %d\n", + module->name, cmd_size); + return -1; + } + size = get_size(cmd->ip_cfg_part2); + + for (i = 0; i < 8; i++) { + void **addr = (void **) + &cmd->ip_buf_addr[i]; + if (*addr && adsp_pmem_fixup(module, addr, size)) + return -1; + } + } + case VFE_CMD_AXI_OP_CFG: + { + vfe_cmd_axi_op_cfg *cmd = (vfe_cmd_axi_op_cfg *)cmd_data; + void **addr1_y, **addr2_y, **addr1_cbcr, **addr2_cbcr; + + if (cmd_size != sizeof(vfe_cmd_axi_op_cfg)) { + printk(KERN_ERR "adsp: module %s: invalid VFE TABLE (VFE_CMD_AXI_OP_CFG) command size %d\n", + module->name, cmd_size); + return -1; + } + size1_y = get_size(cmd->op1_y_cfg_part2); + size1_cbcr = get_size(cmd->op1_cbcr_cfg_part2); + size2_y = get_size(cmd->op2_y_cfg_part2); + size2_cbcr = get_size(cmd->op2_cbcr_cfg_part2); + for (i = 0; i < 8; i++) { + addr1_y = (void **)(&cmd->op1_buf1_addr[2*i]); + addr1_cbcr = (void **)(&cmd->op1_buf1_addr[2*i+1]); + addr2_y = (void **)(&cmd->op2_buf1_addr[2*i]); + addr2_cbcr = (void **)(&cmd->op2_buf1_addr[2*i+1]); +/* + printk("module %s: [%d] %p %p %p %p\n", + module->name, i, + *addr1_y, *addr1_cbcr, *addr2_y, *addr2_cbcr); +*/ + if ((*addr1_y && adsp_pmem_fixup(module, addr1_y, size1_y)) || + (*addr1_cbcr && adsp_pmem_fixup(module, addr1_cbcr, size1_cbcr)) || + (*addr2_y && adsp_pmem_fixup(module, addr2_y, size2_y)) || + (*addr2_cbcr && adsp_pmem_fixup(module, addr2_cbcr, size2_cbcr))) + return -1; + } + } + default: + if (cmd_id > 4) { + printk(KERN_ERR "adsp: module %s: invalid VFE TABLE command id %d\n", + module->name, cmd_id); + return -1; + } + } + return 0; +} + +int adsp_vfe_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size) +{ + switch (queue_id) { + case QDSP_vfeCommandQueue: + return verify_vfe_command(module, cmd_data, cmd_size); + case QDSP_vfeCommandScaleQueue: + return verify_vfe_command_scale(module, cmd_data, cmd_size); + case QDSP_vfeCommandTableQueue: + return verify_vfe_command_table(module, cmd_data, cmd_size); + default: + printk(KERN_ERR "adsp: module %s: unknown queue id %d\n", + module->name, queue_id); + return -1; + } +} diff --git a/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c new file mode 100644 index 0000000000000..cae242fa15e9a --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c @@ -0,0 +1,151 @@ +/* arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c + * + * Verificion code for aDSP VDEC packets from userspace. + * + * Copyright (c) 2008 QUALCOMM Incorporated + * Copyright (C) 2008 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 + +#define ADSP_DEBUG_MSGS 0 +#if ADSP_DEBUG_MSGS +#define DLOG(fmt,args...) \ + do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \ + ##args); } \ + while (0) +#else +#define DLOG(x...) do {} while (0) +#endif + + +#include +#include "adsp.h" + +static inline void *high_low_short_to_ptr(unsigned short high, + unsigned short low) +{ + return (void *)((((unsigned long)high) << 16) | ((unsigned long)low)); +} + +static inline void ptr_to_high_low_short(void *ptr, unsigned short *high, + unsigned short *low) +{ + *high = (unsigned short)((((unsigned long)ptr) >> 16) & 0xffff); + *low = (unsigned short)((unsigned long)ptr & 0xffff); +} + +static int pmem_fixup_high_low(unsigned short *high, + unsigned short *low, + unsigned short size_high, + unsigned short size_low, + struct msm_adsp_module *module, + unsigned long *addr, unsigned long *size) +{ + void *phys_addr; + unsigned long phys_size; + unsigned long kvaddr; + + phys_addr = high_low_short_to_ptr(*high, *low); + phys_size = (unsigned long)high_low_short_to_ptr(size_high, size_low); + DLOG("virt %x %x\n", phys_addr, phys_size); + if (adsp_pmem_fixup_kvaddr(module, &phys_addr, &kvaddr, phys_size)) { + DLOG("ah%x al%x sh%x sl%x addr %x size %x\n", + *high, *low, size_high, size_low, phys_addr, phys_size); + return -1; + } + ptr_to_high_low_short(phys_addr, high, low); + DLOG("phys %x %x\n", phys_addr, phys_size); + if (addr) + *addr = kvaddr; + if (size) + *size = phys_size; + return 0; +} + +static int verify_vdec_pkt_cmd(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + unsigned short cmd_id = ((unsigned short *)cmd_data)[0]; + viddec_cmd_subframe_pkt *pkt; + unsigned long subframe_pkt_addr; + unsigned long subframe_pkt_size; + viddec_cmd_frame_header_packet *frame_header_pkt; + int i; + unsigned short *frame_buffer_high, *frame_buffer_low; + unsigned long frame_buffer_size; + unsigned short frame_buffer_size_high, frame_buffer_size_low; + + DLOG("cmd_size %d cmd_id %d cmd_data %x\n", cmd_size, cmd_id, cmd_data); + if (cmd_id != VIDDEC_CMD_SUBFRAME_PKT) { + printk(KERN_INFO "adsp_video:unknown video packet %u\n", + cmd_id); + return 0; + } + if (cmd_size < sizeof(viddec_cmd_subframe_pkt)) + return -1; + + pkt = (viddec_cmd_subframe_pkt *)cmd_data; + + if (pmem_fixup_high_low(&(pkt->subframe_packet_high), + &(pkt->subframe_packet_low), + pkt->subframe_packet_size_high, + pkt->subframe_packet_size_low, + module, + &subframe_pkt_addr, + &subframe_pkt_size)) + return -1; + + /* deref those ptrs and check if they are a frame header packet */ + frame_header_pkt = (viddec_cmd_frame_header_packet *)subframe_pkt_addr; + + if (frame_header_pkt->packet_id != (unsigned short)0xB201) + return 0; + frame_buffer_high = &frame_header_pkt->frame_buffer_0_high; + frame_buffer_low = &frame_header_pkt->frame_buffer_0_low; + frame_buffer_size = (frame_header_pkt->x_dimension * + frame_header_pkt->y_dimension * 3) / 2; + ptr_to_high_low_short((void *)frame_buffer_size, + &frame_buffer_size_high, + &frame_buffer_size_low); + for (i = 0; i < 8; i++) { + if (pmem_fixup_high_low(frame_buffer_high, frame_buffer_low, + frame_buffer_size_high, + frame_buffer_size_low, + module, + NULL, NULL)) + return -1; + frame_buffer_high += 2; + frame_buffer_low += 2; + } + if (pmem_fixup_high_low(&frame_header_pkt->output_frame_buffer_high, + &frame_header_pkt->output_frame_buffer_low, + frame_buffer_size_high, + frame_buffer_size_low, module, NULL, NULL)) + return -1; + return 0; +} + +int adsp_video_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size) +{ + switch (queue_id) { + case QDSP_mpuVDecPktQueue: + DLOG("\n"); + return verify_vdec_pkt_cmd(module, cmd_data, cmd_size); + default: + printk(KERN_INFO "unknown video queue %u\n", queue_id); + return 0; + } +} + diff --git a/arch/arm/mach-msm/qdsp5/audio_in.c b/arch/arm/mach-msm/qdsp5/audio_in.c new file mode 100644 index 0000000000000..a9b301313a473 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_in.c @@ -0,0 +1,750 @@ +/* arch/arm/mach-msm/qdsp5/audio_in.c + * + * pcm audio input device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * 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 "audmgr.h" + +#include +#include +#include +#include + +/* for queue ids - should be relative to module number*/ +#include "adsp.h" + +/* FRAME_NUM must be a power of two */ +#define FRAME_NUM (8) +#define FRAME_SIZE (2052 * 2) +#define MONO_DATA_SIZE (2048) +#define STEREO_DATA_SIZE (MONO_DATA_SIZE * 2) +#define DMASZ (FRAME_SIZE * FRAME_NUM) + +struct buffer { + void *data; + uint32_t size; + uint32_t read; + uint32_t addr; +}; + +struct audio_in { + struct buffer in[FRAME_NUM]; + + spinlock_t dsp_lock; + + atomic_t in_bytes; + + struct mutex lock; + struct mutex read_lock; + wait_queue_head_t wait; + + struct msm_adsp_module *audpre; + struct msm_adsp_module *audrec; + + /* configuration to use on next enable */ + uint32_t samp_rate; + uint32_t channel_mode; + uint32_t buffer_size; /* 2048 for mono, 4096 for stereo */ + uint32_t type; /* 0 for PCM ,1 for AAC */ + uint32_t dsp_cnt; + uint32_t in_head; /* next buffer dsp will write */ + uint32_t in_tail; /* next buffer read() will read */ + uint32_t in_count; /* number of buffers available to read() */ + + unsigned short samp_rate_index; + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + + /* audpre settings */ + audpreproc_cmd_cfg_agc_params tx_agc_cfg; + audpreproc_cmd_cfg_ns_params ns_cfg; + /* For different sample rate, the coeff might be different. * + * All the coeff should be passed from user space */ + audpreproc_cmd_cfg_iir_tuning_filter_params iir_cfg; +}; + +static int audio_in_dsp_enable(struct audio_in *audio, int enable); +static int audio_in_encoder_config(struct audio_in *audio); +static int audio_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt); +static void audio_flush(struct audio_in *audio); + +static unsigned convert_dsp_samp_index(unsigned index) +{ + switch (index) { + case 48000: return AUDREC_CMD_SAMP_RATE_INDX_48000; + case 44100: return AUDREC_CMD_SAMP_RATE_INDX_44100; + case 32000: return AUDREC_CMD_SAMP_RATE_INDX_32000; + case 24000: return AUDREC_CMD_SAMP_RATE_INDX_24000; + case 22050: return AUDREC_CMD_SAMP_RATE_INDX_22050; + case 16000: return AUDREC_CMD_SAMP_RATE_INDX_16000; + case 12000: return AUDREC_CMD_SAMP_RATE_INDX_12000; + case 11025: return AUDREC_CMD_SAMP_RATE_INDX_11025; + case 8000: return AUDREC_CMD_SAMP_RATE_INDX_8000; + default: return AUDREC_CMD_SAMP_RATE_INDX_11025; + } +} + +static unsigned convert_samp_rate(unsigned hz) +{ + switch (hz) { + case 48000: return RPC_AUD_DEF_SAMPLE_RATE_48000; + case 44100: return RPC_AUD_DEF_SAMPLE_RATE_44100; + case 32000: return RPC_AUD_DEF_SAMPLE_RATE_32000; + case 24000: return RPC_AUD_DEF_SAMPLE_RATE_24000; + case 22050: return RPC_AUD_DEF_SAMPLE_RATE_22050; + case 16000: return RPC_AUD_DEF_SAMPLE_RATE_16000; + case 12000: return RPC_AUD_DEF_SAMPLE_RATE_12000; + case 11025: return RPC_AUD_DEF_SAMPLE_RATE_11025; + case 8000: return RPC_AUD_DEF_SAMPLE_RATE_8000; + default: return RPC_AUD_DEF_SAMPLE_RATE_11025; + } +} + +static unsigned convert_samp_index(unsigned index) +{ + switch (index) { + case RPC_AUD_DEF_SAMPLE_RATE_48000: return 48000; + case RPC_AUD_DEF_SAMPLE_RATE_44100: return 44100; + case RPC_AUD_DEF_SAMPLE_RATE_32000: return 32000; + case RPC_AUD_DEF_SAMPLE_RATE_24000: return 24000; + case RPC_AUD_DEF_SAMPLE_RATE_22050: return 22050; + case RPC_AUD_DEF_SAMPLE_RATE_16000: return 16000; + case RPC_AUD_DEF_SAMPLE_RATE_12000: return 12000; + case RPC_AUD_DEF_SAMPLE_RATE_11025: return 11025; + case RPC_AUD_DEF_SAMPLE_RATE_8000: return 8000; + default: return 11025; + } +} + +/* must be called with audio->lock held */ +static int audio_in_enable(struct audio_in *audio) +{ + struct audmgr_config cfg; + int rc; + + if (audio->enabled) + return 0; + + cfg.tx_rate = audio->samp_rate; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.def_method = RPC_AUD_DEF_METHOD_RECORD; + if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV) + cfg.codec = RPC_AUD_DEF_CODEC_PCM; + else + cfg.codec = RPC_AUD_DEF_CODEC_AAC; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + + if (msm_adsp_enable(audio->audpre)) { + pr_err("audrec: msm_adsp_enable(audpre) failed\n"); + return -ENODEV; + } + if (msm_adsp_enable(audio->audrec)) { + pr_err("audrec: msm_adsp_enable(audrec) failed\n"); + return -ENODEV; + } + + audio->enabled = 1; + audio_in_dsp_enable(audio, 1); + + return 0; +} + +/* must be called with audio->lock held */ +static int audio_in_disable(struct audio_in *audio) +{ + if (audio->enabled) { + audio->enabled = 0; + + audio_in_dsp_enable(audio, 0); + + wake_up(&audio->wait); + + msm_adsp_disable(audio->audrec); + msm_adsp_disable(audio->audpre); + audmgr_disable(&audio->audmgr); + } + return 0; +} + +/* ------------------- dsp --------------------- */ +static void audpre_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + uint16_t msg[2]; + getevent(msg, sizeof(msg)); + + switch (id) { + case AUDPREPROC_MSG_CMD_CFG_DONE_MSG: + pr_info("audpre: type %d, status_flag %d\n", msg[0], msg[1]); + break; + case AUDPREPROC_MSG_ERROR_MSG_ID: + pr_info("audpre: err_index %d\n", msg[0]); + default: + pr_err("audpre: unknown event %d\n", id); + } +} + +struct audio_frame +{ + uint16_t count_low; + uint16_t count_high; + uint16_t bytes; + uint16_t unknown; + unsigned char samples[]; +} __attribute__((packed)); + +static void audio_in_get_dsp_frames(struct audio_in *audio) +{ + struct audio_frame *frame; + uint32_t index; + unsigned long flags; + + if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV) { + index = audio->in_head; + + /* XXX check for bogus frame size? */ + + frame = (void*) (((char *)audio->in[index].data) - sizeof(*frame)); + + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = frame->bytes; + + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + else + audio->in_count++; + + audio_dsp_read_buffer(audio, audio->dsp_cnt++); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + + wake_up(&audio->wait); + } else { + /* TODO AAC not supported yet. */ + } +} + +static void audrec_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct audio_in *audio = data; + uint16_t msg[3]; + getevent(msg, sizeof(msg)); + + switch (id) { + case AUDREC_MSG_CMD_CFG_DONE_MSG: + if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_UPDATE) { + if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_ENA) { + pr_info("audpre: CFG ENABLED\n"); + audio_in_encoder_config(audio); + } else { + pr_info("audrec: CFG SLEEP\n"); + audio->running = 0; + } + } else { + pr_info("audrec: CMD_CFG_DONE %x\n", msg[0]); + } + break; + case AUDREC_MSG_CMD_AREC_PARAM_CFG_DONE_MSG: { + pr_info("audrec: PARAM CFG DONE\n"); + audio->running = 1; + break; + } + case AUDREC_MSG_FATAL_ERR_MSG: + pr_err("audrec: ERROR %x\n", msg[0]); + break; + case AUDREC_MSG_PACKET_READY_MSG: + //REC_DBG("type %x, count %d", msg[0], (msg[1] | (msg[2] << 16))); + audio_in_get_dsp_frames(audio); + break; + default: + pr_err("audrec: unknown event %d\n", id); + } +} + +struct msm_adsp_ops audpre_adsp_ops = { + .event = audpre_dsp_event, +}; + +struct msm_adsp_ops audrec_adsp_ops = { + .event = audrec_dsp_event, +}; + + +#define audio_send_queue_pre(audio,cmd,len) \ + msm_adsp_write(audio->audpre, QDSP_uPAudPreProcCmdQueue, cmd, len) +#define audio_send_queue_recbs(audio,cmd,len) \ + msm_adsp_write(audio->audrec, QDSP_uPAudRecBitStreamQueue, cmd, len) +#define audio_send_queue_rec(audio,cmd,len) \ + msm_adsp_write(audio->audrec, QDSP_uPAudRecCmdQueue, cmd, len) + +#if 0 /* not wired up yet */ +static int audio_enable_tx_agc(struct audio_in *audio, int enable) +{ + audpreproc_cmd_cfg_agc_params cmd; + + memset(&cmd, 0, sizeof(cmd)); + + if (enable) { + /* cmd.tx_agc_param_mask = 0xFE00 from sample code */ + audio->tx_agc_cfg.tx_agc_param_mask = + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_SLOPE) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_TH) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_SLOPE) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_TH) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_AIG_FLAG) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_STATIC_GAIN) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG); + audio->tx_agc_cfg.tx_agc_enable_flag = + AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA; + /* cmd.param_mask = 0xFFF0 from sample code */ + audio->tx_agc_cfg.param_mask = + (1 << AUDPREPROC_CMD_PARAM_MASK_RMS_TAY) | + (1 << AUDPREPROC_CMD_PARAM_MASK_RELEASEK) | + (1 << AUDPREPROC_CMD_PARAM_MASK_DELAY) | + (1 << AUDPREPROC_CMD_PARAM_MASK_ATTACKK) | + (1 << AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_SLOW) | + (1 << AUDPREPROC_CMD_PARAM_MASK_LEAKRATE_FAST) | + (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_RELEASEK) | + (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_MIN) | + (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_MAX) | + (1 << AUDPREPROC_CMD_PARAM_MASK_LEAK_UP) | + (1 << AUDPREPROC_CMD_PARAM_MASK_LEAK_DOWN) | + (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_ATTACKK); + } else { + audio->tx_agc_cfg.tx_agc_param_mask = + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG); + audio->tx_agc_cfg.tx_agc_enable_flag = + AUDPREPROC_CMD_TX_AGC_ENA_FLAG_DIS; + } + cmd = audio->tx_agc_cfg; + + return audio_send_queue_pre(audio, &cmd, sizeof(cmd)); +} + +static int audio_enable_ns(struct audio_in *audio, int enable) +{ + audpreproc_cmd_cfg_ns_params cmd; + + if (enable) { + /* cmd.ec_mode_new is fixed as 0x0064 when enable from sample code */ + audio->ns_cfg.ec_mode_new = + AUDPREPROC_CMD_EC_MODE_NEW_NS_ENA | + AUDPREPROC_CMD_EC_MODE_NEW_HB_ENA | + AUDPREPROC_CMD_EC_MODE_NEW_VA_ENA; + } else { + audio->ns_cfg.ec_mode_new = + AUDPREPROC_CMD_EC_MODE_NEW_NLMS_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_DES_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_NS_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_CNI_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_NLES_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_HB_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_VA_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_PCD_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_FEHI_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_NEHI_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_NLPP_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_FNE_DIS | + AUDPREPROC_CMD_EC_MODE_NEW_PRENLMS_DIS; + } + cmd = audio->ns_cfg; + + return audio_send_queue_pre(audio, &cmd, sizeof(cmd)); +} + +static int audio_enable_iir(struct audio_in *audio, int enable) +{ + audpreproc_cmd_cfg_iir_tuning_filter_params cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd = audio->iir_cfg; + + if (enable) + /* cmd.active_flag is 0xFFFF from sample code but 0x0001 here */ + cmd.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_ENA; + else + cmd.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_DIS; + + return audio_send_queue_pre(audio, &cmd, sizeof(cmd)); +} +#endif + +static int audio_in_dsp_enable(struct audio_in *audio, int enable) +{ + audrec_cmd_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_CFG; + cmd.type_0 = enable ? AUDREC_CMD_TYPE_0_ENA : AUDREC_CMD_TYPE_0_DIS; + cmd.type_0 |= (AUDREC_CMD_TYPE_0_UPDATE | audio->type); + cmd.type_1 = 0; + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audio_in_encoder_config(struct audio_in *audio) +{ + audrec_cmd_arec0param_cfg cmd; + uint16_t *data = (void *) audio->data; + unsigned n; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_AREC0PARAM_CFG; + cmd.ptr_to_extpkt_buffer_msw = audio->phys >> 16; + cmd.ptr_to_extpkt_buffer_lsw = audio->phys; + cmd.buf_len = FRAME_NUM; /* Both WAV and AAC use 8 frames */ + cmd.samp_rate_index = audio->samp_rate_index; + cmd.stereo_mode = audio->channel_mode; /* 0 for mono, 1 for stereo */ + + /* FIXME have no idea why cmd.rec_quality is fixed + * as 0x1C00 from sample code + */ + cmd.rec_quality = 0x1C00; + + /* prepare buffer pointers: + * Mono: 1024 samples + 4 halfword header + * Stereo: 2048 samples + 4 halfword header + */ + for (n = 0; n < FRAME_NUM; n++) { + audio->in[n].data = data + 4; + data += (4 + (audio->channel_mode ? 2048 : 1024)); + } + + return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); +} + +static int audio_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt) +{ + audrec_cmd_packet_ext_ptr cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDREC_CMD_PACKET_EXT_PTR; + cmd.type = AUDREC_CMD_TYPE_0; /* Both WAV and AAC use AUDREC_CMD_TYPE_0 */ + cmd.curr_rec_count_msw = read_cnt >> 16; + cmd.curr_rec_count_lsw = read_cnt; + + return audio_send_queue_recbs(audio, &cmd, sizeof(cmd)); +} + +/* ------------------- device --------------------- */ + +static void audio_flush(struct audio_in *audio) +{ + int i; + + audio->dsp_cnt = 0; + audio->in_head = 0; + audio->in_tail = 0; + audio->in_count = 0; + for (i = 0; i < FRAME_NUM; i++) { + audio->in[i].size = 0; + audio->in[i].read = 0; + } +} + +static long audio_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio_in *audio = file->private_data; + int rc; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->in_bytes); + if (copy_to_user((void*) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + rc = audio_in_enable(audio); + break; + case AUDIO_STOP: + rc = audio_in_disable(audio); + audio->stopped = 1; + break; + case AUDIO_FLUSH: + if (audio->stopped) { + /* Make sure we're stopped and we wake any threads + * that might be blocked holding the read_lock. + * While audio->stopped read threads will always + * exit immediately. + */ + wake_up(&audio->wait); + mutex_lock(&audio->read_lock); + audio_flush(audio); + mutex_unlock(&audio->read_lock); + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config cfg; + if (copy_from_user(&cfg, (void*) arg, sizeof(cfg))) { + rc = -EFAULT; + break; + } + if (cfg.channel_count == 1) { + cfg.channel_count = AUDREC_CMD_STEREO_MODE_MONO; + } else if (cfg.channel_count == 2) { + cfg.channel_count = AUDREC_CMD_STEREO_MODE_STEREO; + } else { + rc = -EINVAL; + break; + } + + if (cfg.type == 0) { + cfg.type = AUDREC_CMD_TYPE_0_INDEX_WAV; + } else if (cfg.type == 1) { + /* TODO AAC not supported */ + //cfg.type = AUDREC_CMD_TYPE_0_INDEX_AAC; + rc = -EINVAL; + break; + } else { + rc = -EINVAL; + break; + } + audio->samp_rate = convert_samp_rate(cfg.sample_rate); + audio->samp_rate_index = convert_dsp_samp_index(cfg.sample_rate); + audio->channel_mode = cfg.channel_count; + audio->buffer_size = + audio->channel_mode ? STEREO_DATA_SIZE : MONO_DATA_SIZE; + audio->type = cfg.type; + rc = 0; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config cfg; + cfg.buffer_size = audio->buffer_size; + cfg.buffer_count = FRAME_NUM; + cfg.sample_rate = convert_samp_index(audio->samp_rate); + if (audio->channel_mode == AUDREC_CMD_STEREO_MODE_MONO) { + cfg.channel_count = 1; + } else { + cfg.channel_count = 2; + } + + if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV) { + cfg.type = 0; + } else { + cfg.type = 1; + } + cfg.unused[0] = 0; + cfg.unused[1] = 0; + cfg.unused[2] = 0; + if (copy_to_user((void*) arg, &cfg, sizeof(cfg))) { + rc = -EFAULT; + } else { + rc = 0; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audio_in_read(struct file *file, char __user *buf, size_t count, loff_t *pos) +{ + struct audio_in *audio = file->private_data; + unsigned long flags; + const char __user *start = buf; + void *data; + uint32_t index; + uint32_t size; + int rc = 0; + + mutex_lock(&audio->read_lock); + while (count > 0) { + rc = wait_event_interruptible( + audio->wait, (audio->in_count > 0) || audio->stopped); + if (rc < 0) + break; + + if (audio->stopped) { + rc = -EBUSY; + break; + } + + index = audio->in_tail; + data = (uint8_t *) audio->in[index].data; + size = audio->in[index].size; + if (count >= size) { + if (copy_to_user(buf, data, size)) { + rc = -EFAULT; + break; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + if (index != audio->in_tail) { + /* overrun -- data is invalid and we need to retry */ + spin_unlock_irqrestore(&audio->dsp_lock, flags); + continue; + } + audio->in[index].size = 0; + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + audio->in_count--; + spin_unlock_irqrestore(&audio->dsp_lock, flags); + count -= size; + buf += size; + } else { + pr_err("audio_in: short read\n"); + break; + } + } + mutex_unlock(&audio->read_lock); + + if (buf > start) + return buf - start; + + return rc; +} + +static ssize_t audio_in_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) +{ + return -EINVAL; +} + +static int audio_in_release(struct inode *inode, struct file *file) +{ + struct audio_in *audio = file->private_data; + + mutex_lock(&audio->lock); + audio_in_disable(audio); + audio_flush(audio); + msm_adsp_put(audio->audrec); + msm_adsp_put(audio->audpre); + audio->audrec = NULL; + audio->audpre = NULL; + audio->opened = 0; + mutex_unlock(&audio->lock); + return 0; +} + +struct audio_in the_audio_in; + +static int audio_in_open(struct inode *inode, struct file *file) +{ + struct audio_in *audio = &the_audio_in; + int rc; + + mutex_lock(&audio->lock); + if (audio->opened) { + rc = -EBUSY; + goto done; + } + + if (!audio->data) { + audio->data = dma_alloc_coherent(NULL, DMASZ, + &audio->phys, GFP_KERNEL); + if (!audio->data) { + rc = -ENOMEM; + goto done; + } + } + + /* Settings will be re-config at AUDIO_SET_CONFIG, + * but at least we need to have initial config + */ + audio->samp_rate = RPC_AUD_DEF_SAMPLE_RATE_11025; + audio->samp_rate_index = AUDREC_CMD_SAMP_RATE_INDX_11025; + audio->channel_mode = AUDREC_CMD_STEREO_MODE_MONO; + audio->buffer_size = MONO_DATA_SIZE; + audio->type = AUDREC_CMD_TYPE_0_INDEX_WAV; + audio->tx_agc_cfg.cmd_id = AUDPREPROC_CMD_CFG_AGC_PARAMS; + audio->ns_cfg.cmd_id = AUDPREPROC_CMD_CFG_NS_PARAMS; + audio->iir_cfg.cmd_id = AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS; + + rc = audmgr_open(&audio->audmgr); + if (rc) + goto done; + rc = msm_adsp_get("AUDPREPROCTASK", &audio->audpre, &audpre_adsp_ops, audio); + if (rc) + goto done; + rc = msm_adsp_get("AUDRECTASK", &audio->audrec, &audrec_adsp_ops, audio); + if (rc) + goto done; + + audio->dsp_cnt = 0; + audio->stopped = 0; + + audio_flush(audio); + + file->private_data = audio; + audio->opened = 1; + rc = 0; +done: + mutex_unlock(&audio->lock); + return rc; +} + +static struct file_operations audio_fops = { + .owner = THIS_MODULE, + .open = audio_in_open, + .release = audio_in_release, + .read = audio_in_read, + .write = audio_in_write, + .unlocked_ioctl = audio_in_ioctl, +}; + +struct miscdevice audio_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_in", + .fops = &audio_fops, +}; + +static int __init audio_in_init(void) +{ + mutex_init(&the_audio_in.lock); + mutex_init(&the_audio_in.read_lock); + spin_lock_init(&the_audio_in.dsp_lock); + init_waitqueue_head(&the_audio_in.wait); + return misc_register(&audio_in_misc); +} + +device_initcall(audio_in_init); diff --git a/arch/arm/mach-msm/qdsp5/audio_mp3.c b/arch/arm/mach-msm/qdsp5/audio_mp3.c new file mode 100644 index 0000000000000..b09ca7911dd8d --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_mp3.c @@ -0,0 +1,577 @@ +/* arch/arm/mach-msm/qdsp5/audio_mp3.c + * + * mp3 audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * 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 "audmgr.h" + +#include +#include +#include +#include + +/* for queue ids - should be relative to module number*/ +#include "adsp.h" + +#define BUFSZ 32768 +#define DMASZ (BUFSZ * 2) + +#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF +#define AUDDEC_DEC_MP3 2 + +/* Decoder status received from AUDPPTASK */ +typedef enum { + AUDPP_DEC_STATUS_SLEEP, + AUDPP_DEC_STATUS_INIT, + AUDPP_DEC_STATUS_CFG, + AUDPP_DEC_STATUS_PLAY +} audpp_dec_status_type; + +struct buffer { + void *data; + unsigned size; + unsigned used; + unsigned addr; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t wait; + + struct msm_adsp_module *audplay; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + unsigned volume; + + uint16_t dec_id; + uint32_t read_ptr_offset; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audplay_send_data(struct audio *audio, unsigned needed); + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + pr_info("audio_enable()\n"); + + if (audio->enabled) + return 0; + + audio->out_tail = 0; + audio->out_needed = 0; + + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK; + cfg.codec = RPC_AUD_DEF_CODEC_MP3; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + + if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) { + pr_err("audio: audpp_enable() failed\n"); + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + if (msm_adsp_enable(audio->audplay)) { + pr_err("audio: msm_adsp_enable(audplay) failed\n"); + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + pr_info("audio_disable()\n"); + if (audio->enabled) { + audio->enabled = 0; + auddec_dsp_config(audio, 0); + wake_up(&audio->wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + } + return 0; +} + +/* ------------------- dsp --------------------- */ + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audplay_send_data(audio, 1); + break; + default: + pr_err("unexpected message from decoder \n"); + break; + } +} + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG: { + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: + pr_info("decoder status: sleep \n"); + break; + case AUDPP_DEC_STATUS_INIT: { + pr_info("decoder status: init \n"); + audpp_cmd_cfg_adec_params(audio); + break; + } + case AUDPP_DEC_STATUS_CFG: + pr_info("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + pr_info("decoder status: play \n"); + break; + default: + pr_err("unknown decoder status \n"); + break; + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + pr_info("audio_dsp_event: CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_set_volume_and_pan(audio->dec_id, audio->volume, 0); + audpp_avsync(audio->dec_id, 22050); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + pr_info("audio_dsp_event: CFG_MSG DISABLE\n"); + audpp_avsync(audio->dec_id, 0); + audio->running = 0; + } else { + pr_err("audio_dsp_event: CFG_MSG %d?\n", msg[0]); + } + break; + default: + pr_err("audio_dsp_event: UNKNOWN (%d)\n", id); + } + +} + + +struct msm_adsp_ops audplay_adsp_ops = { + .event = audplay_dsp_event, +}; + + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + audpp_cmd_cfg_dec_type cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | + AUDDEC_DEC_MP3; + else + cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_DIS_DEC_V; + + return audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + audpp_cmd_cfg_adec_params_mp3 cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_MP3_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + audplay_cmd_bitstream_data_avail cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL; + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len/2; + cmd.partition_number = 0; + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audplay_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + audplay_dsp_send_data_avail(audio, audio->out_tail, frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } +done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audio_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->stopped = 0; + atomic_set(&audio->out_bytes, 0); +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = audpp_avsync_byte_count(audio->dec_id); + stats.sample_count = audpp_avsync_sample_count(audio->dec_id); + if (copy_to_user((void *) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + if (cmd == AUDIO_SET_VOLUME) { + unsigned long flags; + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->volume = arg; + if (audio->running) + audpp_set_volume_and_pan(audio->dec_id, arg, 0); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + rc = audio_enable(audio); + break; + case AUDIO_STOP: + rc = audio_disable(audio); + audio->stopped = 1; + break; + case AUDIO_FLUSH: + if (audio->stopped) { + /* Make sure we're stopped and we wake any threads + * that might be blocked holding the write_lock. + * While audio->stopped write threads will always + * exit immediately. + */ + wake_up(&audio->wait); + mutex_lock(&audio->write_lock); + audio_flush(audio); + mutex_unlock(&audio->write_lock); + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void *) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count == 1) { + config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count = AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + break; + } + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + rc = 0; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) { + config.channel_count = 1; + } else { + config.channel_count = 2; + } + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + config.unused[3] = 0; + if (copy_to_user((void *) arg, &config, sizeof(config))) { + rc = -EFAULT; + } else { + rc = 0; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audio_read(struct file *file, char __user *buf, size_t count, loff_t *pos) +{ + return -EINVAL; +} + +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + int rc = 0; + + if (count & 1) + return -EINVAL; + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + rc = wait_event_interruptible(audio->wait, + (frame->used == 0) || (audio->stopped)); + if (rc < 0) + break; + if (audio->stopped) { + rc = -EBUSY; + break; + } + xfer = (count > frame->size) ? frame->size : count; + if (copy_from_user(frame->data, buf, xfer)) { + rc = -EFAULT; + break; + } + + frame->used = xfer; + audio->out_head ^= 1; + count -= xfer; + buf += xfer; + + audplay_send_data(audio, 0); + + } + mutex_unlock(&audio->write_lock); + if (buf > start) + return buf - start; + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + mutex_lock(&audio->lock); + audio_disable(audio); + audio_flush(audio); + msm_adsp_put(audio->audplay); + audio->audplay = NULL; + audio->opened = 0; + mutex_unlock(&audio->lock); + return 0; +} + +struct audio the_mp3_audio; + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = &the_mp3_audio; + int rc; + + mutex_lock(&audio->lock); + + if (audio->opened) { + pr_err("audio: busy\n"); + rc = -EBUSY; + goto done; + } + + if (!audio->data) { + audio->data = dma_alloc_coherent(NULL, DMASZ, + &audio->phys, GFP_KERNEL); + if (!audio->data) { + pr_err("audio: could not allocate DMA buffers\n"); + rc = -ENOMEM; + goto done; + } + } + + rc = audmgr_open(&audio->audmgr); + if (rc) + goto done; + + rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay, &audplay_adsp_ops, audio); + if (rc) { + pr_err("audio: failed to get audplay0 dsp module\n"); + goto done; + } + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + audio->dec_id = 0; + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = BUFSZ; + + audio->out[1].data = audio->data + BUFSZ; + audio->out[1].addr = audio->phys + BUFSZ; + audio->out[1].size = BUFSZ; + + audio->volume = 0x2000; /* Q13 1.0 */ + + audio_flush(audio); + + file->private_data = audio; + audio->opened = 1; + rc = 0; +done: + mutex_unlock(&audio->lock); + return rc; +} + +static struct file_operations audio_mp3_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .read = audio_read, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, +}; + +struct miscdevice audio_mp3_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_mp3", + .fops = &audio_mp3_fops, +}; + +static int __init audio_init(void) +{ + mutex_init(&the_mp3_audio.lock); + mutex_init(&the_mp3_audio.write_lock); + spin_lock_init(&the_mp3_audio.dsp_lock); + init_waitqueue_head(&the_mp3_audio.wait); + return misc_register(&audio_mp3_misc); +} + +device_initcall(audio_init); diff --git a/arch/arm/mach-msm/qdsp5/audio_out.c b/arch/arm/mach-msm/qdsp5/audio_out.c new file mode 100644 index 0000000000000..d4bc1052884f4 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_out.c @@ -0,0 +1,841 @@ +/* arch/arm/mach-msm/qdsp5/audio_out.c + * + * pcm audio output device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * 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 "audmgr.h" + +#include +#include + +#include "evlog.h" + +#define LOG_AUDIO_EVENTS 1 +#define LOG_AUDIO_FAULTS 0 + +enum { + EV_NULL, + EV_OPEN, + EV_WRITE, + EV_RETURN, + EV_IOCTL, + EV_WRITE_WAIT, + EV_WAIT_EVENT, + EV_FILL_BUFFER, + EV_SEND_BUFFER, + EV_DSP_EVENT, + EV_ENABLE, +}; + +#if (LOG_AUDIO_EVENTS != 1) +static inline void LOG(unsigned id, unsigned arg) {} +#else +static const char *pcm_log_strings[] = { + "NULL", + "OPEN", + "WRITE", + "RETURN", + "IOCTL", + "WRITE_WAIT", + "WAIT_EVENT", + "FILL_BUFFER", + "SEND_BUFFER", + "DSP_EVENT", + "ENABLE", +}; + +DECLARE_LOG(pcm_log, 64, pcm_log_strings); + +static int __init _pcm_log_init(void) +{ + return ev_log_init(&pcm_log); +} +module_init(_pcm_log_init); + +#define LOG(id,arg) ev_log_write(&pcm_log, id, arg) +#endif + + + + + +#define BUFSZ (960 * 5) +#define DMASZ (BUFSZ * 2) + +#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000 +#define AUDPP_CMD_EQ_FLAG_DIS 0x0000 +#define AUDPP_CMD_EQ_FLAG_ENA -1 +#define AUDPP_CMD_IIR_FLAG_DIS 0x0000 +#define AUDPP_CMD_IIR_FLAG_ENA -1 + +#define AUDPP_CMD_IIR_TUNING_FILTER 1 +#define AUDPP_CMD_EQUALIZER 2 +#define AUDPP_CMD_ADRC 3 + +#define ADRC_ENABLE 0x0001 +#define EQ_ENABLE 0x0002 +#define IIR_ENABLE 0x0004 + +struct adrc_filter { + uint16_t compression_th; + uint16_t compression_slope; + uint16_t rms_time; + uint16_t attack_const_lsw; + uint16_t attack_const_msw; + uint16_t release_const_lsw; + uint16_t release_const_msw; + uint16_t adrc_system_delay; +}; + +struct eqalizer { + uint16_t num_bands; + uint16_t eq_params[132]; +}; + +struct rx_iir_filter { + uint16_t num_bands; + uint16_t iir_params[48]; +}; + +typedef struct { + audpp_cmd_cfg_object_params_common common; + uint16_t eq_flag; + uint16_t num_bands; + uint16_t eq_params[132]; +} audpp_cmd_cfg_object_params_eq; + +typedef struct { + audpp_cmd_cfg_object_params_common common; + uint16_t active_flag; + uint16_t num_bands; + uint16_t iir_params[48]; +} audpp_cmd_cfg_object_params_rx_iir; + +struct buffer { + void *data; + unsigned size; + unsigned used; + unsigned addr; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t wait; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + uint32_t out_weight; + uint32_t out_buffer_size; + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + unsigned volume; + + struct wake_lock wakelock; + struct wake_lock idlelock; + + int adrc_enable; + struct adrc_filter adrc; + + int eq_enable; + struct eqalizer eq; + + int rx_iir_enable; + struct rx_iir_filter iir; +}; + +static void audio_prevent_sleep(struct audio *audio) +{ + printk(KERN_INFO "++++++++++++++++++++++++++++++\n"); + wake_lock(&audio->wakelock); + wake_lock(&audio->idlelock); +} + +static void audio_allow_sleep(struct audio *audio) +{ + wake_unlock(&audio->wakelock); + wake_unlock(&audio->idlelock); + printk(KERN_INFO "------------------------------\n"); +} + +static int audio_dsp_out_enable(struct audio *audio, int yes); +static int audio_dsp_send_buffer(struct audio *audio, unsigned id, unsigned len); +static int audio_dsp_set_adrc(struct audio *audio); +static int audio_dsp_set_eq(struct audio *audio); +static int audio_dsp_set_rx_iir(struct audio *audio); + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + pr_info("audio_enable()\n"); + + if (audio->enabled) + return 0; + + /* refuse to start if we're not ready */ + if (!audio->out[0].used || !audio->out[1].used) + return -EIO; + + /* we start buffers 0 and 1, so buffer 0 will be the + * next one the dsp will want + */ + audio->out_tail = 0; + audio->out_needed = 0; + + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_HOST_PCM; + cfg.codec = RPC_AUD_DEF_CODEC_PCM; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + audio_prevent_sleep(audio); + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) { + audio_allow_sleep(audio); + return rc; + } + + if (audpp_enable(-1, audio_dsp_event, audio)) { + pr_err("audio: audpp_enable() failed\n"); + audmgr_disable(&audio->audmgr); + audio_allow_sleep(audio); + return -ENODEV; + } + + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + pr_info("audio_disable()\n"); + if (audio->enabled) { + audio->enabled = 0; + audio_dsp_out_enable(audio, 0); + + audpp_disable(-1, audio); + + wake_up(&audio->wait); + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + audio_allow_sleep(audio); + } + return 0; +} + +/* ------------------- dsp --------------------- */ +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + struct buffer *frame; + unsigned long flags; + + LOG(EV_DSP_EVENT, id); + switch (id) { + case AUDPP_MSG_HOST_PCM_INTF_MSG: { + unsigned id = msg[2]; + unsigned idx = msg[3] - 1; + + /* pr_info("audio_dsp_event: HOST_PCM id %d idx %d\n", id, idx); */ + if (id != AUDPP_MSG_HOSTPCM_ID_ARM_RX) { + pr_err("bogus id\n"); + break; + } + if (idx > 1) { + pr_err("bogus buffer idx\n"); + break; + } + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (audio->running) { + atomic_add(audio->out[idx].used, &audio->out_bytes); + audio->out[idx].used = 0; + + frame = audio->out + audio->out_tail; + if (frame->used) { + audio_dsp_send_buffer( + audio, audio->out_tail, frame->used); + audio->out_tail ^= 1; + } else { + audio->out_needed++; + } + wake_up(&audio->wait); + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); + break; + } + case AUDPP_MSG_PCMDMAMISSED: + pr_info("audio_dsp_event: PCMDMAMISSED %d\n", msg[0]); + break; + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + LOG(EV_ENABLE, 1); + pr_info("audio_dsp_event: CFG_MSG ENABLE\n"); + audio->out_needed = 0; + audio->running = 1; + audpp_set_volume_and_pan(5, audio->volume, 0); + audio_dsp_set_adrc(audio); + audio_dsp_set_eq(audio); + audio_dsp_set_rx_iir(audio); + audio_dsp_out_enable(audio, 1); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + LOG(EV_ENABLE, 0); + pr_info("audio_dsp_event: CFG_MSG DISABLE\n"); + audio->running = 0; + } else { + pr_err("audio_dsp_event: CFG_MSG %d?\n", msg[0]); + } + break; + default: + pr_err("audio_dsp_event: UNKNOWN (%d)\n", id); + } +} + +static int audio_dsp_out_enable(struct audio *audio, int yes) +{ + audpp_cmd_pcm_intf cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_PCM_INTF_2; + cmd.object_num = AUDPP_CMD_PCM_INTF_OBJECT_NUM; + cmd.config = AUDPP_CMD_PCM_INTF_CONFIG_CMD_V; + cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V; + + if (yes) { + cmd.write_buf1LSW = audio->out[0].addr; + cmd.write_buf1MSW = audio->out[0].addr >> 16; + cmd.write_buf1_len = audio->out[0].size; + cmd.write_buf2LSW = audio->out[1].addr; + cmd.write_buf2MSW = audio->out[1].addr >> 16; + cmd.write_buf2_len = audio->out[1].size; + cmd.arm_to_rx_flag = AUDPP_CMD_PCM_INTF_ENA_V; + cmd.weight_decoder_to_rx = audio->out_weight; + cmd.weight_arm_to_rx = 1; + cmd.partition_number_arm_to_dsp = 0; + cmd.sample_rate = audio->out_sample_rate; + cmd.channel_mode = audio->out_channel_mode; + } + + return audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static int audio_dsp_send_buffer(struct audio *audio, unsigned idx, unsigned len) +{ + audpp_cmd_pcm_intf_send_buffer cmd; + + cmd.cmd_id = AUDPP_CMD_PCM_INTF_2; + cmd.host_pcm_object = AUDPP_CMD_PCM_INTF_OBJECT_NUM; + cmd.config = AUDPP_CMD_PCM_INTF_BUFFER_CMD_V; + cmd.intf_type = AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V; + cmd.dsp_to_arm_buf_id = 0; + cmd.arm_to_dsp_buf_id = idx + 1; + cmd.arm_to_dsp_buf_len = len; + + LOG(EV_SEND_BUFFER, idx); + return audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static int audio_dsp_set_adrc(struct audio *audio) +{ + audpp_cmd_cfg_object_params_adrc cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_CMD_ADRC; + + if (audio->adrc_enable) { + cmd.adrc_flag = AUDPP_CMD_ADRC_FLAG_ENA; + cmd.compression_th = audio->adrc.compression_th; + cmd.compression_slope = audio->adrc.compression_slope; + cmd.rms_time = audio->adrc.rms_time; + cmd.attack_const_lsw = audio->adrc.attack_const_lsw; + cmd.attack_const_msw = audio->adrc.attack_const_msw; + cmd.release_const_lsw = audio->adrc.release_const_lsw; + cmd.release_const_msw = audio->adrc.release_const_msw; + cmd.adrc_system_delay = audio->adrc.adrc_system_delay; + } else { + cmd.adrc_flag = AUDPP_CMD_ADRC_FLAG_DIS; + } + return audpp_send_queue3(&cmd, sizeof(cmd)); +} + +static int audio_dsp_set_eq(struct audio *audio) +{ + audpp_cmd_cfg_object_params_eq cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_CMD_EQUALIZER; + + if (audio->eq_enable) { + cmd.eq_flag = AUDPP_CMD_EQ_FLAG_ENA; + cmd.num_bands = audio->eq.num_bands; + memcpy(&cmd.eq_params, audio->eq.eq_params, + sizeof(audio->eq.eq_params)); + } else { + cmd.eq_flag = AUDPP_CMD_EQ_FLAG_DIS; + } + return audpp_send_queue3(&cmd, sizeof(cmd)); +} + +static int audio_dsp_set_rx_iir(struct audio *audio) +{ + audpp_cmd_cfg_object_params_rx_iir cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.comman_cfg = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd.common.command_type = AUDPP_CMD_IIR_TUNING_FILTER; + + if (audio->rx_iir_enable) { + cmd.active_flag = AUDPP_CMD_IIR_FLAG_ENA; + cmd.num_bands = audio->iir.num_bands; + memcpy(&cmd.iir_params, audio->iir.iir_params, + sizeof(audio->iir.iir_params)); + } else { + cmd.active_flag = AUDPP_CMD_IIR_FLAG_DIS; + } + + return audpp_send_queue3(&cmd, sizeof(cmd)); +} + +/* ------------------- device --------------------- */ + +static int audio_enable_adrc(struct audio *audio, int enable) +{ + if (audio->adrc_enable != enable) { + audio->adrc_enable = enable; + if (audio->running) + audio_dsp_set_adrc(audio); + } + return 0; +} + +static int audio_enable_eq(struct audio *audio, int enable) +{ + if (audio->eq_enable != enable) { + audio->eq_enable = enable; + if (audio->running) + audio_dsp_set_eq(audio); + } + return 0; +} + +static int audio_enable_rx_iir(struct audio *audio, int enable) +{ + if (audio->rx_iir_enable != enable) { + audio->rx_iir_enable = enable; + if (audio->running) + audio_dsp_set_rx_iir(audio); + } + return 0; +} + +static void audio_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->stopped = 0; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = atomic_read(&audio->out_bytes); + if (copy_to_user((void*) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + if (cmd == AUDIO_SET_VOLUME) { + unsigned long flags; + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->volume = arg; + if (audio->running) + audpp_set_volume_and_pan(6, arg, 0); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } + + LOG(EV_IOCTL, cmd); + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + rc = audio_enable(audio); + break; + case AUDIO_STOP: + rc = audio_disable(audio); + audio->stopped = 1; + break; + case AUDIO_FLUSH: + if (audio->stopped) { + /* Make sure we're stopped and we wake any threads + * that might be blocked holding the write_lock. + * While audio->stopped write threads will always + * exit immediately. + */ + wake_up(&audio->wait); + mutex_lock(&audio->write_lock); + audio_flush(audio); + mutex_unlock(&audio->write_lock); + } + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void*) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count == 1) { + config.channel_count = AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count= AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + break; + } + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + rc = 0; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) { + config.channel_count = 1; + } else { + config.channel_count = 2; + } + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + config.unused[3] = 0; + if (copy_to_user((void*) arg, &config, sizeof(config))) { + rc = -EFAULT; + } else { + rc = 0; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audio_read(struct file *file, char __user *buf, size_t count, loff_t *pos) +{ + return -EINVAL; +} + +static inline int rt_policy(int policy) +{ + if (unlikely(policy == SCHED_FIFO) || unlikely(policy == SCHED_RR)) + return 1; + return 0; +} + +static inline int task_has_rt_policy(struct task_struct *p) +{ + return rt_policy(p->policy); +} + +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct sched_param s = { .sched_priority = 1 }; + struct audio *audio = file->private_data; + unsigned long flags; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + int old_prio = current->rt_priority; + int old_policy = current->policy; + int cap_nice = cap_raised(current->cap_effective, CAP_SYS_NICE); + int rc = 0; + + LOG(EV_WRITE, count | (audio->running << 28) | (audio->stopped << 24)); + + /* just for this write, set us real-time */ + if (!task_has_rt_policy(current)) { + cap_raise(current->cap_effective, CAP_SYS_NICE); + sched_setscheduler(current, SCHED_RR, &s); + } + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + + LOG(EV_WAIT_EVENT, 0); + rc = wait_event_interruptible(audio->wait, + (frame->used == 0) || (audio->stopped)); + LOG(EV_WAIT_EVENT, 1); + + if (rc < 0) + break; + if (audio->stopped) { + rc = -EBUSY; + break; + } + xfer = count > frame->size ? frame->size : count; + if (copy_from_user(frame->data, buf, xfer)) { + rc = -EFAULT; + break; + } + frame->used = xfer; + audio->out_head ^= 1; + count -= xfer; + buf += xfer; + + spin_lock_irqsave(&audio->dsp_lock, flags); + LOG(EV_FILL_BUFFER, audio->out_head ^ 1); + frame = audio->out + audio->out_tail; + if (frame->used && audio->out_needed) { + audio_dsp_send_buffer(audio, audio->out_tail, frame->used); + audio->out_tail ^= 1; + audio->out_needed--; + } + spin_unlock_irqrestore(&audio->dsp_lock, flags); + } + + mutex_unlock(&audio->write_lock); + + /* restore scheduling policy and priority */ + if (!rt_policy(old_policy)) { + struct sched_param v = { .sched_priority = old_prio }; + sched_setscheduler(current, old_policy, &v); + if (likely(!cap_nice)) + cap_lower(current->cap_effective, CAP_SYS_NICE); + } + + LOG(EV_RETURN,(buf > start) ? (buf - start) : rc); + if (buf > start) + return buf - start; + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + LOG(EV_OPEN, 0); + mutex_lock(&audio->lock); + audio_disable(audio); + audio_flush(audio); + audio->opened = 0; + mutex_unlock(&audio->lock); + return 0; +} + +struct audio the_audio; + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = &the_audio; + int rc; + + mutex_lock(&audio->lock); + + if (audio->opened) { + pr_err("audio: busy\n"); + rc = -EBUSY; + goto done; + } + + if (!audio->data) { + audio->data = dma_alloc_coherent(NULL, DMASZ, + &audio->phys, GFP_KERNEL); + if (!audio->data) { + pr_err("audio: could not allocate DMA buffers\n"); + rc = -ENOMEM; + goto done; + } + } + + rc = audmgr_open(&audio->audmgr); + if (rc) + goto done; + + audio->out_buffer_size = BUFSZ; + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + audio->out_weight = 100; + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = BUFSZ; + + audio->out[1].data = audio->data + BUFSZ; + audio->out[1].addr = audio->phys + BUFSZ; + audio->out[1].size = BUFSZ; + + audio->volume = 0x2000; + + audio_flush(audio); + + file->private_data = audio; + audio->opened = 1; + rc = 0; + LOG(EV_OPEN, 1); +done: + mutex_unlock(&audio->lock); + return rc; +} + +static long audpp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = 0, enable; + uint16_t enable_mask; + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_ENABLE_AUDPP: + if (copy_from_user(&enable_mask, (void *) arg, sizeof(enable_mask))) + goto out_fault; + + enable = (enable_mask & ADRC_ENABLE)? 1 : 0; + audio_enable_adrc(audio, enable); + enable = (enable_mask & EQ_ENABLE)? 1 : 0; + audio_enable_eq(audio, enable); + enable = (enable_mask & IIR_ENABLE)? 1 : 0; + audio_enable_rx_iir(audio, enable); + break; + + case AUDIO_SET_ADRC: + if (copy_from_user(&audio->adrc, (void*) arg, sizeof(audio->adrc))) + goto out_fault; + break; + + case AUDIO_SET_EQ: + if (copy_from_user(&audio->eq, (void*) arg, sizeof(audio->eq))) + goto out_fault; + break; + + case AUDIO_SET_RX_IIR: + if (copy_from_user(&audio->iir, (void*) arg, sizeof(audio->iir))) + goto out_fault; + break; + + default: + rc = -EINVAL; + } + + goto out; + + out_fault: + rc = -EFAULT; + out: + mutex_unlock(&audio->lock); + return rc; +} + +static int audpp_open(struct inode *inode, struct file *file) +{ + struct audio *audio = &the_audio; + + file->private_data = audio; + return 0; +} + +static struct file_operations audio_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .read = audio_read, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, +}; + +static struct file_operations audpp_fops = { + .owner = THIS_MODULE, + .open = audpp_open, + .unlocked_ioctl = audpp_ioctl, +}; + +struct miscdevice audio_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_out", + .fops = &audio_fops, +}; + +struct miscdevice audpp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_ctl", + .fops = &audpp_fops, +}; + +static int __init audio_init(void) +{ + mutex_init(&the_audio.lock); + mutex_init(&the_audio.write_lock); + spin_lock_init(&the_audio.dsp_lock); + init_waitqueue_head(&the_audio.wait); + wake_lock_init(&the_audio.wakelock, WAKE_LOCK_SUSPEND, "audio_pcm"); + wake_lock_init(&the_audio.idlelock, WAKE_LOCK_IDLE, "audio_pcm_idle"); + return (misc_register(&audio_misc) || misc_register(&audpp_misc)); +} + +device_initcall(audio_init); diff --git a/arch/arm/mach-msm/qdsp5/audmgr.c b/arch/arm/mach-msm/qdsp5/audmgr.c new file mode 100644 index 0000000000000..dd99413e2ffca --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audmgr.c @@ -0,0 +1,306 @@ +/* arch/arm/mach-msm/qdsp5/audmgr.c + * + * interface to "audmgr" service on the baseband cpu + * + * Copyright (C) 2008 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 +#include + +#include +#include + +#include "audmgr.h" + +#define STATE_CLOSED 0 +#define STATE_DISABLED 1 +#define STATE_ENABLING 2 +#define STATE_ENABLED 3 +#define STATE_DISABLING 4 +#define STATE_KILLED 5 + +static void rpc_ack(struct msm_rpc_endpoint *ept, uint32_t xid) +{ + uint32_t rep[6]; + + rep[0] = cpu_to_be32(xid); + rep[1] = cpu_to_be32(1); + rep[2] = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); + rep[3] = cpu_to_be32(RPC_ACCEPTSTAT_SUCCESS); + rep[4] = 0; + rep[5] = 0; + + msm_rpc_write(ept, rep, sizeof(rep)); +} + +static void process_audmgr_callback(struct audmgr *am, + struct rpc_audmgr_cb_func_ptr *args, + int len) +{ + if (len < (sizeof(uint32_t) * 3)) + return; + if (be32_to_cpu(args->set_to_one) != 1) + return; + + switch (be32_to_cpu(args->status)){ + case RPC_AUDMGR_STATUS_READY: + if (len < sizeof(uint32_t) * 4) + break; + am->handle = args->u.handle; + pr_info("audmgr: rpc READY handle=0x%08x\n", am->handle); + break; + case RPC_AUDMGR_STATUS_CODEC_CONFIG: { + uint32_t volume; + if (len < sizeof(uint32_t) * 4) + break; + volume = be32_to_cpu(args->u.volume); + pr_info("audmgr: rpc CODEC_CONFIG volume=0x%08x\n", volume); + am->state = STATE_ENABLED; + wake_up(&am->wait); + break; + } + case RPC_AUDMGR_STATUS_PENDING: + pr_err("audmgr: PENDING?\n"); + break; + case RPC_AUDMGR_STATUS_SUSPEND: + pr_err("audmgr: SUSPEND?\n"); + break; + case RPC_AUDMGR_STATUS_FAILURE: + pr_err("audmgr: FAILURE\n"); + break; + case RPC_AUDMGR_STATUS_VOLUME_CHANGE: + pr_err("audmgr: VOLUME_CHANGE?\n"); + break; + case RPC_AUDMGR_STATUS_DISABLED: + pr_err("audmgr: DISABLED\n"); + am->state = STATE_DISABLED; + wake_up(&am->wait); + break; + case RPC_AUDMGR_STATUS_ERROR: + pr_err("audmgr: ERROR?\n"); + break; + default: + break; + } +} + +static void process_rpc_request(uint32_t proc, uint32_t xid, + void *data, int len, void *private) +{ + struct audmgr *am = private; + uint32_t *x = data; + + if (0) { + int n = len / 4; + pr_info("rpc_call proc %d:", proc); + while (n--) + printk(" %08x", be32_to_cpu(*x++)); + printk("\n"); + } + + if (proc == AUDMGR_CB_FUNC_PTR) { + process_audmgr_callback(am, data, len); + } else { + pr_err("audmgr: unknown rpc proc %d\n", proc); + } + rpc_ack(am->ept, xid); +} + +#define RPC_TYPE_REQUEST 0 +#define RPC_TYPE_REPLY 1 + +#define RPC_VERSION 2 + +#define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2) +#define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr)) +#define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3) +#define RPC_REPLY_SZ (sizeof(uint32_t) * 6) + +static int audmgr_rpc_thread(void *data) +{ + struct audmgr *am = data; + struct rpc_request_hdr *hdr = NULL; + uint32_t type; + int len; + + pr_info("audmgr_rpc_thread() start\n"); + + while (!kthread_should_stop()) { + if (hdr) { + kfree(hdr); + hdr = NULL; + } + len = msm_rpc_read(am->ept, (void **) &hdr, -1, -1); + if (len < 0) { + pr_err("audmgr: rpc read failed (%d)\n", len); + break; + } + if (len < RPC_COMMON_HDR_SZ) + continue; + + type = be32_to_cpu(hdr->type); + if (type == RPC_TYPE_REPLY) { + struct rpc_reply_hdr *rep = (void*) hdr; + uint32_t status; + if (len < RPC_REPLY_HDR_SZ) + continue; + status = be32_to_cpu(rep->reply_stat); + if (status == RPCMSG_REPLYSTAT_ACCEPTED) { + status = be32_to_cpu(rep->data.acc_hdr.accept_stat); + pr_info("audmgr: rpc_reply status %d\n", status); + } else { + pr_info("audmgr: rpc_reply denied!\n"); + } + /* process reply */ + continue; + } + + if (len < RPC_REQUEST_HDR_SZ) + continue; + + process_rpc_request(be32_to_cpu(hdr->procedure), + be32_to_cpu(hdr->xid), + (void*) (hdr + 1), + len - sizeof(*hdr), + data); + } + pr_info("audmgr_rpc_thread() exit\n"); + if (hdr) { + kfree(hdr); + hdr = NULL; + } + am->task = NULL; + wake_up(&am->wait); + return 0; +} + +struct audmgr_enable_msg { + struct rpc_request_hdr hdr; + struct rpc_audmgr_enable_client_args args; +}; + +struct audmgr_disable_msg { + struct rpc_request_hdr hdr; + uint32_t handle; +}; + +int audmgr_open(struct audmgr *am) +{ + int rc; + + if (am->state != STATE_CLOSED) + return 0; + + am->ept = msm_rpc_connect(AUDMGR_PROG, AUDMGR_VERS, + MSM_RPC_UNINTERRUPTIBLE); + init_waitqueue_head(&am->wait); + + if (IS_ERR(am->ept)) { + rc = PTR_ERR(am->ept); + am->ept = NULL; + pr_err("audmgr: failed to connect to audmgr svc\n"); + return rc; + } + + am->task = kthread_run(audmgr_rpc_thread, am, "audmgr_rpc"); + if (IS_ERR(am->task)) { + rc = PTR_ERR(am->task); + am->task = NULL; + msm_rpc_close(am->ept); + am->ept = NULL; + return rc; + } + + am->state = STATE_DISABLED; + return 0; +} + +int audmgr_close(struct audmgr *am) +{ + return -EBUSY; +} + +int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg) +{ + struct audmgr_enable_msg msg; + int rc; + + if (am->state == STATE_ENABLED) + return 0; + if (am->state == STATE_ENABLING) + goto wait_for_it; + +try_again: + if (am->state == STATE_DISABLING) + pr_err("audmgr: state is DISABLING in enable?\n"); + am->state = STATE_ENABLING; + + msg.args.set_to_one = cpu_to_be32(1); + msg.args.tx_sample_rate = cpu_to_be32(cfg->tx_rate); + msg.args.rx_sample_rate = cpu_to_be32(cfg->rx_rate); + msg.args.def_method = cpu_to_be32(cfg->def_method); + msg.args.codec_type = cpu_to_be32(cfg->codec); + msg.args.snd_method = cpu_to_be32(cfg->snd_method); + msg.args.cb_func = cpu_to_be32(0x11111111); + msg.args.client_data = cpu_to_be32(0x11223344); + + msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, AUDMGR_VERS, + AUDMGR_ENABLE_CLIENT); + + rc = msm_rpc_write(am->ept, &msg, sizeof(msg)); + if (rc < 0) + return rc; + +wait_for_it: + rc = wait_event_timeout(am->wait, am->state != STATE_ENABLING, 15 * HZ); + if (rc == 0) { + pr_err("audmgr: ARM9 did not reply to RPC\n"); + BUG(); + } + if (am->state == STATE_ENABLED) + return 0; + if (am->state == STATE_DISABLED) { + pr_err("audmgr: DISABLED WHILE ENABLING. retrying...\n"); + goto try_again; + } + pr_err("audmgr: unexpected state %d while enabling?!\n", am->state); + return -ENODEV; +} + +int audmgr_disable(struct audmgr *am) +{ + struct audmgr_disable_msg msg; + int rc; + + if (am->state == STATE_DISABLED) + return 0; + if (am->state == STATE_DISABLING) + return 0; + + msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, AUDMGR_VERS, + AUDMGR_DISABLE_CLIENT); + msg.handle = am->handle; + + am->state = STATE_DISABLING; + + rc = msm_rpc_write(am->ept, &msg, sizeof(msg)); + if (rc < 0) + return rc; + + return 0; +} diff --git a/arch/arm/mach-msm/qdsp5/audmgr.h b/arch/arm/mach-msm/qdsp5/audmgr.h new file mode 100644 index 0000000000000..69f4e05213e45 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audmgr.h @@ -0,0 +1,211 @@ +/* arch/arm/mach-msm/qdsp5/audmgr.h + * + * Copyright 2008 (c) QUALCOMM Incorporated. + * Copyright (C) 2008 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 _AUDIO_RPC_H_ +#define _AUDIO_RPC_H_ + +enum rpc_aud_def_sample_rate_type { + RPC_AUD_DEF_SAMPLE_RATE_NONE, + RPC_AUD_DEF_SAMPLE_RATE_8000, + RPC_AUD_DEF_SAMPLE_RATE_11025, + RPC_AUD_DEF_SAMPLE_RATE_12000, + RPC_AUD_DEF_SAMPLE_RATE_16000, + RPC_AUD_DEF_SAMPLE_RATE_22050, + RPC_AUD_DEF_SAMPLE_RATE_24000, + RPC_AUD_DEF_SAMPLE_RATE_32000, + RPC_AUD_DEF_SAMPLE_RATE_44100, + RPC_AUD_DEF_SAMPLE_RATE_48000, + RPC_AUD_DEF_SAMPLE_RATE_MAX, +}; + +enum rpc_aud_def_method_type { + RPC_AUD_DEF_METHOD_NONE, + RPC_AUD_DEF_METHOD_KEY_BEEP, + RPC_AUD_DEF_METHOD_PLAYBACK, + RPC_AUD_DEF_METHOD_VOICE, + RPC_AUD_DEF_METHOD_RECORD, + RPC_AUD_DEF_METHOD_HOST_PCM, + RPC_AUD_DEF_METHOD_MIDI_OUT, + RPC_AUD_DEF_METHOD_RECORD_SBC, + RPC_AUD_DEF_METHOD_DTMF_RINGER, + RPC_AUD_DEF_METHOD_MAX, +}; + +enum rpc_aud_def_codec_type { + RPC_AUD_DEF_CODEC_NONE, + RPC_AUD_DEF_CODEC_DTMF, + RPC_AUD_DEF_CODEC_MIDI, + RPC_AUD_DEF_CODEC_MP3, + RPC_AUD_DEF_CODEC_PCM, + RPC_AUD_DEF_CODEC_AAC, + RPC_AUD_DEF_CODEC_WMA, + RPC_AUD_DEF_CODEC_RA, + RPC_AUD_DEF_CODEC_ADPCM, + RPC_AUD_DEF_CODEC_GAUDIO, + RPC_AUD_DEF_CODEC_VOC_EVRC, + RPC_AUD_DEF_CODEC_VOC_13K, + RPC_AUD_DEF_CODEC_VOC_4GV_NB, + RPC_AUD_DEF_CODEC_VOC_AMR, + RPC_AUD_DEF_CODEC_VOC_EFR, + RPC_AUD_DEF_CODEC_VOC_FR, + RPC_AUD_DEF_CODEC_VOC_HR, + RPC_AUD_DEF_CODEC_VOC, + RPC_AUD_DEF_CODEC_SBC, + RPC_AUD_DEF_CODEC_VOC_PCM, + RPC_AUD_DEF_CODEC_AMR_WB, + RPC_AUD_DEF_CODEC_AMR_WB_PLUS, + RPC_AUD_DEF_CODEC_MAX, +}; + +enum rpc_snd_method_type { + RPC_SND_METHOD_VOICE = 0, + RPC_SND_METHOD_KEY_BEEP, + RPC_SND_METHOD_MESSAGE, + RPC_SND_METHOD_RING, + RPC_SND_METHOD_MIDI, + RPC_SND_METHOD_AUX, + RPC_SND_METHOD_MAX, +}; + +enum rpc_voc_codec_type { + RPC_VOC_CODEC_DEFAULT, + RPC_VOC_CODEC_ON_CHIP_0 = RPC_VOC_CODEC_DEFAULT, + RPC_VOC_CODEC_ON_CHIP_1, + RPC_VOC_CODEC_STEREO_HEADSET, + RPC_VOC_CODEC_ON_CHIP_AUX, + RPC_VOC_CODEC_BT_OFF_BOARD, + RPC_VOC_CODEC_BT_A2DP, + RPC_VOC_CODEC_OFF_BOARD, + RPC_VOC_CODEC_SDAC, + RPC_VOC_CODEC_RX_EXT_SDAC_TX_INTERNAL, + RPC_VOC_CODEC_IN_STEREO_SADC_OUT_MONO_HANDSET, + RPC_VOC_CODEC_IN_STEREO_SADC_OUT_STEREO_HEADSET, + RPC_VOC_CODEC_TX_INT_SADC_RX_EXT_AUXPCM, + RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_MONO_HANDSET, + RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_STEREO_HEADSET, + RPC_VOC_CODEC_TTY_ON_CHIP_1, + RPC_VOC_CODEC_TTY_OFF_BOARD, + RPC_VOC_CODEC_TTY_VCO, + RPC_VOC_CODEC_TTY_HCO, + RPC_VOC_CODEC_ON_CHIP_0_DUAL_MIC, + RPC_VOC_CODEC_MAX, + RPC_VOC_CODEC_NONE, +}; + +enum rpc_audmgr_status_type { + RPC_AUDMGR_STATUS_READY, + RPC_AUDMGR_STATUS_CODEC_CONFIG, + RPC_AUDMGR_STATUS_PENDING, + RPC_AUDMGR_STATUS_SUSPEND, + RPC_AUDMGR_STATUS_FAILURE, + RPC_AUDMGR_STATUS_VOLUME_CHANGE, + RPC_AUDMGR_STATUS_DISABLED, + RPC_AUDMGR_STATUS_ERROR, +}; + +struct rpc_audmgr_enable_client_args { + uint32_t set_to_one; + uint32_t tx_sample_rate; + uint32_t rx_sample_rate; + uint32_t def_method; + uint32_t codec_type; + uint32_t snd_method; + + uint32_t cb_func; + uint32_t client_data; +}; + +#define AUDMGR_ENABLE_CLIENT 2 +#define AUDMGR_DISABLE_CLIENT 3 +#define AUDMGR_SUSPEND_EVENT_RSP 4 +#define AUDMGR_REGISTER_OPERATION_LISTENER 5 +#define AUDMGR_UNREGISTER_OPERATION_LISTENER 6 +#define AUDMGR_REGISTER_CODEC_LISTENER 7 +#define AUDMGR_GET_RX_SAMPLE_RATE 8 +#define AUDMGR_GET_TX_SAMPLE_RATE 9 +#define AUDMGR_SET_DEVICE_MODE 10 + +#if CONFIG_MSM_AMSS_VERSION < 6220 +#define AUDMGR_PROG_VERS "rs30000013:46255756" +#define AUDMGR_PROG 0x30000013 +#define AUDMGR_VERS 0x46255756 +#else +#define AUDMGR_PROG_VERS "rs30000013:e94e8f0c" +#define AUDMGR_PROG 0x30000013 +#define AUDMGR_VERS 0xe94e8f0c +#endif + +struct rpc_audmgr_cb_func_ptr { + uint32_t cb_id; + uint32_t set_to_one; + uint32_t status; + union { + uint32_t handle; + uint32_t volume; + + } u; +}; + +#define AUDMGR_CB_FUNC_PTR 1 +#define AUDMGR_OPR_LSTNR_CB_FUNC_PTR 2 +#define AUDMGR_CODEC_LSTR_FUNC_PTR 3 + +#if CONFIG_MSM_AMSS_VERSION < 6220 +#define AUDMGR_CB_PROG_VERS "rs31000013:5fa922a9" +#define AUDMGR_CB_PROG 0x31000013 +#define AUDMGR_CB_VERS 0x5fa922a9 +#else +#define AUDMGR_CB_PROG_VERS "rs31000013:21570ba7" +#define AUDMGR_CB_PROG 0x31000013 +#define AUDMGR_CB_VERS 0x21570ba7 +#endif + +struct audmgr { + wait_queue_head_t wait; + uint32_t handle; + struct msm_rpc_endpoint *ept; + struct task_struct *task; + int state; +}; + +struct audmgr_config { + uint32_t tx_rate; + uint32_t rx_rate; + uint32_t def_method; + uint32_t codec; + uint32_t snd_method; +}; + +int audmgr_open(struct audmgr *am); +int audmgr_close(struct audmgr *am); +int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg); +int audmgr_disable(struct audmgr *am); + +typedef void (*audpp_event_func)(void *private, unsigned id, uint16_t *msg); + +int audpp_enable(int id, audpp_event_func func, void *private); +void audpp_disable(int id, void *private); + +int audpp_send_queue1(void *cmd, unsigned len); +int audpp_send_queue2(void *cmd, unsigned len); +int audpp_send_queue3(void *cmd, unsigned len); + +int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan); +void audpp_avsync(int id, unsigned rate); +unsigned audpp_avsync_sample_count(int id); +unsigned audpp_avsync_byte_count(int id); + +#endif diff --git a/arch/arm/mach-msm/qdsp5/audpp.c b/arch/arm/mach-msm/qdsp5/audpp.c new file mode 100644 index 0000000000000..8400df7e9cb4c --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audpp.c @@ -0,0 +1,367 @@ + +/* arch/arm/mach-msm/qdsp5/audpp.c + * + * common code to deal with the AUDPP dsp task (audio postproc) + * + * Copyright (C) 2008 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 +#include +#include + +#include "audmgr.h" + +#include +#include + +/* for queue ids - should be relative to module number*/ +#include "adsp.h" + +#include "evlog.h" + + +enum { + EV_NULL, + EV_ENABLE, + EV_DISABLE, + EV_EVENT, + EV_DATA, +}; + +static const char *dsp_log_strings[] = { + "NULL", + "ENABLE", + "DISABLE", + "EVENT", + "DATA", +}; + +DECLARE_LOG(dsp_log, 64, dsp_log_strings); + +static int __init _dsp_log_init(void) +{ + return ev_log_init(&dsp_log); +} +module_init(_dsp_log_init); +#define LOG(id,arg) ev_log_write(&dsp_log, id, arg) + +static DEFINE_MUTEX(audpp_lock); + +#define CH_COUNT 5 + +struct audpp_state +{ + struct msm_adsp_module *mod; + audpp_event_func func[6]; + void *private[6]; + struct mutex *lock; + unsigned open_count; + unsigned enabled; + + /* which channels are actually enabled */ + unsigned avsync_mask; + + /* flags, 48 bits sample/bytes counter per channel */ + uint16_t avsync[CH_COUNT * 6 + 1]; +}; + +struct audpp_state the_audpp_state = { + .lock = &audpp_lock, +}; + +int audpp_send_queue1(void *cmd, unsigned len) +{ + return msm_adsp_write(the_audpp_state.mod, + QDSP_uPAudPPCmd1Queue, cmd, len); +} + +int audpp_send_queue2(void *cmd, unsigned len) +{ + return msm_adsp_write(the_audpp_state.mod, + QDSP_uPAudPPCmd2Queue, cmd, len); +} + +int audpp_send_queue3(void *cmd, unsigned len) +{ + return msm_adsp_write(the_audpp_state.mod, + QDSP_uPAudPPCmd3Queue, cmd, len); +} + +static int audpp_dsp_config(int enable) +{ + audpp_cmd_cfg cmd; + + cmd.cmd_id = AUDPP_CMD_CFG; + cmd.cfg = enable ? AUDPP_CMD_CFG_ENABLE : AUDPP_CMD_CFG_SLEEP; + + return audpp_send_queue1(&cmd, sizeof(cmd)); +} + + +static void audpp_broadcast(struct audpp_state *audpp, unsigned id, uint16_t *msg) +{ + unsigned n; + for (n = 0; n < 6; n++) { + if (audpp->func[n]) + audpp->func[n](audpp->private[n], id, msg); + } +} + +static void audpp_dsp_event(void *data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + struct audpp_state *audpp = data; + uint16_t msg[8]; + + if (id == AUDPP_MSG_AVSYNC_MSG) { + getevent(audpp->avsync, sizeof(audpp->avsync)); + + /* mask off any channels we're not watching to avoid + * cases where we might get one last update after + * disabling avsync and end up in an odd state when + * we next read... + */ + audpp->avsync[0] &= audpp->avsync_mask; + return; + } + + getevent(msg, sizeof(msg)); + + LOG(EV_EVENT, (id << 16) | msg[0]); + LOG(EV_DATA, (msg[1] << 16) | msg[2]); + + switch (id) { + case AUDPP_MSG_STATUS_MSG: { + unsigned cid = msg[0]; + pr_info("audpp: status %d %d %d\n", cid, msg[1], msg[2]); + if ((cid < 5) && audpp->func[cid]) + audpp->func[cid](audpp->private[cid], id, msg); + break; + } + case AUDPP_MSG_HOST_PCM_INTF_MSG: + if (audpp->func[5]) + audpp->func[5](audpp->private[5], id, msg); + break; + case AUDPP_MSG_PCMDMAMISSED: + pr_err("audpp: DMA missed\n"); + break; + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + pr_info("audpp: ENABLE\n"); + audpp->enabled = 1; + audpp_broadcast(audpp, id, msg); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + pr_info("audpp: DISABLE\n"); + audpp->enabled = 0; + audpp_broadcast(audpp, id, msg); + } else { + pr_err("audpp: invalid config msg %d\n", msg[0]); + } + break; + } +} + +static struct msm_adsp_ops adsp_ops = { + .event = audpp_dsp_event, +}; + +static void audpp_fake_event(struct audpp_state *audpp, int id, + unsigned event, unsigned arg) +{ + uint16_t msg[1]; + msg[0] = arg; + audpp->func[id](audpp->private[id], event, msg); +} + +int audpp_enable(int id, audpp_event_func func, void *private) +{ + struct audpp_state *audpp = &the_audpp_state; + int res = 0; + + if (id < -1 || id > 4) + return -EINVAL; + + if (id == -1) + id = 5; + + mutex_lock(audpp->lock); + if (audpp->func[id]) { + res = -EBUSY; + goto out; + } + + audpp->func[id] = func; + audpp->private[id] = private; + + LOG(EV_ENABLE, 1); + if (audpp->open_count++ == 0) { + pr_info("audpp: enable\n"); + res = msm_adsp_get("AUDPPTASK", &audpp->mod, &adsp_ops, audpp); + if (res < 0) { + pr_err("audpp: cannot open AUDPPTASK\n"); + audpp->open_count = 0; + audpp->func[id] = NULL; + audpp->private[id] = NULL; + goto out; + } + LOG(EV_ENABLE, 2); + msm_adsp_enable(audpp->mod); + audpp_dsp_config(1); + } else { + unsigned long flags; + local_irq_save(flags); + if (audpp->enabled) + audpp_fake_event(audpp, id, + AUDPP_MSG_CFG_MSG, + AUDPP_MSG_ENA_ENA); + local_irq_restore(flags); + } + + res = 0; +out: + mutex_unlock(audpp->lock); + return res; +} + +void audpp_disable(int id, void *private) +{ + struct audpp_state *audpp = &the_audpp_state; + unsigned long flags; + + if (id < -1 || id > 4) + return; + + if (id == -1) + id = 5; + + mutex_lock(audpp->lock); + LOG(EV_DISABLE, 1); + if (!audpp->func[id]) + goto out; + if (audpp->private[id] != private) + goto out; + + local_irq_save(flags); + audpp_fake_event(audpp, id, AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_DIS); + audpp->func[id] = NULL; + audpp->private[id] = NULL; + local_irq_restore(flags); + + if (--audpp->open_count == 0) { + pr_info("audpp: disable\n"); + LOG(EV_DISABLE, 2); + audpp_dsp_config(0); + msm_adsp_disable(audpp->mod); + msm_adsp_put(audpp->mod); + audpp->mod = NULL; + } +out: + mutex_unlock(audpp->lock); +} + + +#define BAD_ID(id) ((id < 0) || (id >= CH_COUNT)) + +void audpp_avsync(int id, unsigned rate) +{ + unsigned long flags; + audpp_cmd_avsync cmd; + + if (BAD_ID(id)) + return; + + local_irq_save(flags); + if (rate) + the_audpp_state.avsync_mask |= (1 << id); + else + the_audpp_state.avsync_mask &= (~(1 << id)); + the_audpp_state.avsync[0] &= the_audpp_state.avsync_mask; + local_irq_restore(flags); + + cmd.cmd_id = AUDPP_CMD_AVSYNC; + cmd.object_number = id; + cmd.interrupt_interval_lsw = rate; + cmd.interrupt_interval_msw = rate >> 16; + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +unsigned audpp_avsync_sample_count(int id) +{ + uint16_t *avsync = the_audpp_state.avsync; + unsigned val; + unsigned long flags; + unsigned mask; + + if (BAD_ID(id)) + return 0; + + mask = 1 << id; + id = id * 6 + 2; + local_irq_save(flags); + if (avsync[0] & mask) + val = (avsync[id] << 16) | avsync[id]; + else + val = 0; + local_irq_restore(flags); + + return val; +} + +unsigned audpp_avsync_byte_count(int id) +{ + uint16_t *avsync = the_audpp_state.avsync; + unsigned val; + unsigned long flags; + unsigned mask; + + if (BAD_ID(id)) + return 0; + + mask = 1 << id; + id = id * 6 + 5; + local_irq_save(flags); + if (avsync[0] & mask) + val = (avsync[id] << 16) | avsync[id]; + else + val = 0; + local_irq_restore(flags); + + return val; +} + +#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000 +#define AUDPP_CMD_VOLUME_PAN 0 + +int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan) +{ + /* cmd, obj_cfg[7], cmd_type, volume, pan */ + uint16_t cmd[11]; + + if (id > 6) + return -EINVAL; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = AUDPP_CMD_CFG_OBJECT_PARAMS; + cmd[1 + id] = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd[8] = AUDPP_CMD_VOLUME_PAN; + cmd[9] = volume; + cmd[10] = pan; + + return audpp_send_queue3(cmd, sizeof(cmd)); +} diff --git a/arch/arm/mach-msm/qdsp5/evlog.h b/arch/arm/mach-msm/qdsp5/evlog.h new file mode 100644 index 0000000000000..5c0edf1e9feef --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/evlog.h @@ -0,0 +1,133 @@ +/* arch/arm/mach-msm/qdsp5/evlog.h + * + * simple event log debugging facility + * + * Copyright (C) 2008 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 + +#define EV_LOG_ENTRY_NAME(n) n##_entry + +#define DECLARE_LOG(_name, _size, _str) \ +static struct ev_entry EV_LOG_ENTRY_NAME(_name)[_size]; \ +static struct ev_log _name = { \ + .name = #_name, \ + .strings = _str, \ + .num_strings = ARRAY_SIZE(_str), \ + .entry = EV_LOG_ENTRY_NAME(_name), \ + .max = ARRAY_SIZE(EV_LOG_ENTRY_NAME(_name)), \ +} + +struct ev_entry { + ktime_t when; + uint32_t id; + uint32_t arg; +}; + +struct ev_log { + struct ev_entry *entry; + unsigned max; + unsigned next; + unsigned fault; + const char **strings; + unsigned num_strings; + const char *name; +}; + +static char ev_buf[4096]; + +static ssize_t ev_log_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct ev_log *log = file->private_data; + struct ev_entry *entry; + unsigned long flags; + int size = 0; + unsigned n, id, max; + ktime_t now, t; + + max = log->max; + now = ktime_get(); + local_irq_save(flags); + n = (log->next - 1) & (max - 1); + entry = log->entry; + while (n != log->next) { + t = ktime_sub(now, entry[n].when); + id = entry[n].id; + if (id) { + const char *str; + if (id < log->num_strings) + str = log->strings[id]; + else + str = "UNKNOWN"; + size += scnprintf(ev_buf + size, 4096 - size, + "%8d.%03d %08x %s\n", + t.tv.sec, t.tv.nsec / 1000000, + entry[n].arg, str); + } + n = (n - 1) & (max - 1); + } + log->fault = 0; + local_irq_restore(flags); + return simple_read_from_buffer(buf, count, ppos, ev_buf, size); +} + +static void ev_log_write(struct ev_log *log, unsigned id, unsigned arg) +{ + struct ev_entry *entry; + unsigned long flags; + local_irq_save(flags); + + if (log->fault) { + if (log->fault == 1) + goto done; + log->fault--; + } + + entry = log->entry + log->next; + entry->when = ktime_get(); + entry->id = id; + entry->arg = arg; + log->next = (log->next + 1) & (log->max - 1); +done: + local_irq_restore(flags); +} + +static void ev_log_freeze(struct ev_log *log, unsigned count) +{ + unsigned long flags; + local_irq_save(flags); + log->fault = count; + local_irq_restore(flags); +} + +static int ev_log_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations ev_log_ops = { + .read = ev_log_read, + .open = ev_log_open, +}; + +static int ev_log_init(struct ev_log *log) +{ + debugfs_create_file(log->name, 0444, 0, log, &ev_log_ops); + return 0; +} + diff --git a/include/linux/msm_adsp.h b/include/linux/msm_adsp.h new file mode 100644 index 0000000000000..46c41640d7987 --- /dev/null +++ b/include/linux/msm_adsp.h @@ -0,0 +1,77 @@ +/* include/linux/msm_adsp.h + * + * Copyright (c) QUALCOMM Incorporated + * Copyright (C) 2007 Google, Inc. + * Author: Iliyan Malchev + * + * 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_MSM_ADSP_H +#define __LINUX_MSM_ADSP_H + +#include +#include +#include + +#define ADSP_IOCTL_MAGIC 'q' + +/* ADSP_IOCTL_WRITE_COMMAND */ +struct adsp_command_t { + uint16_t queue; + uint32_t len; /* bytes */ + uint8_t *data; +}; + +/* ADSP_IOCTL_GET_EVENT */ +struct adsp_event_t { + uint16_t type; /* 1 == event (RPC), 0 == message (adsp) */ + uint32_t timeout_ms; /* -1 for infinite, 0 for immediate return */ + uint16_t msg_id; + uint16_t flags; /* 1 == 16--bit event, 0 == 32-bit event */ + uint32_t len; /* size in, number of bytes out */ + uint8_t *data; +}; + +#define ADSP_IOCTL_ENABLE \ + _IOR(ADSP_IOCTL_MAGIC, 1, unsigned) + +#define ADSP_IOCTL_DISABLE \ + _IOR(ADSP_IOCTL_MAGIC, 2, unsigned) + +#define ADSP_IOCTL_DISABLE_ACK \ + _IOR(ADSP_IOCTL_MAGIC, 3, unsigned) + +#define ADSP_IOCTL_WRITE_COMMAND \ + _IOR(ADSP_IOCTL_MAGIC, 4, struct adsp_command_t *) + +#define ADSP_IOCTL_GET_EVENT \ + _IOWR(ADSP_IOCTL_MAGIC, 5, struct adsp_event_data_t *) + +#define ADSP_IOCTL_DISABLE_EVENT_RSP \ + _IOR(ADSP_IOCTL_MAGIC, 10, unsigned) + +#define ADSP_IOCTL_REGISTER_PMEM \ + _IOW(ADSP_IOCTL_MAGIC, 13, unsigned) + +#define ADSP_IOCTL_UNREGISTER_PMEM \ + _IOW(ADSP_IOCTL_MAGIC, 14, unsigned) + +/* Cause any further GET_EVENT ioctls to fail (-ENODEV) + * until the device is closed and reopened. Useful for + * terminating event dispatch threads + */ +#define ADSP_IOCTL_ABORT_EVENT_READ \ + _IOW(ADSP_IOCTL_MAGIC, 15, unsigned) + +#define ADSP_IOCTL_LINK_TASK \ + _IOW(ADSP_IOCTL_MAGIC, 16, unsigned) + +#endif diff --git a/include/linux/msm_audio.h b/include/linux/msm_audio.h new file mode 100644 index 0000000000000..df600c9d4c5e6 --- /dev/null +++ b/include/linux/msm_audio.h @@ -0,0 +1,52 @@ +/* include/linux/msm_audio.h + * + * Copyright (C) 2008 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_MSM_AUDIO_H +#define __LINUX_MSM_AUDIO_H + +#include +#include +#include + +#define AUDIO_IOCTL_MAGIC 'a' + +#define AUDIO_START _IOW(AUDIO_IOCTL_MAGIC, 0, unsigned) +#define AUDIO_STOP _IOW(AUDIO_IOCTL_MAGIC, 1, unsigned) +#define AUDIO_FLUSH _IOW(AUDIO_IOCTL_MAGIC, 2, unsigned) +#define AUDIO_GET_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 3, unsigned) +#define AUDIO_SET_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 4, unsigned) +#define AUDIO_GET_STATS _IOR(AUDIO_IOCTL_MAGIC, 5, unsigned) +#define AUDIO_ENABLE_AUDPP _IOW(AUDIO_IOCTL_MAGIC, 6, unsigned) +#define AUDIO_SET_ADRC _IOW(AUDIO_IOCTL_MAGIC, 7, unsigned) +#define AUDIO_SET_EQ _IOW(AUDIO_IOCTL_MAGIC, 8, unsigned) +#define AUDIO_SET_RX_IIR _IOW(AUDIO_IOCTL_MAGIC, 9, unsigned) +#define AUDIO_SET_VOLUME _IOW(AUDIO_IOCTL_MAGIC, 10, unsigned) + +struct msm_audio_config { + uint32_t buffer_size; + uint32_t buffer_count; + uint32_t channel_count; + uint32_t sample_rate; + uint32_t type; + uint32_t unused[3]; +}; + +struct msm_audio_stats { + uint32_t byte_count; + uint32_t sample_count; + uint32_t unused[2]; +}; + +#endif From 1e013005826102dc45fc636067120d6421bef633 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Tue, 9 Feb 2010 21:35:33 -0800 Subject: [PATCH 0186/2556] [ARM] msm: enable installation of an FIQ handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also add 8k fiq stack. Signed-off-by: Brian Swetland Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/Kconfig | 4 + arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/fiq_glue.S | 112 +++++++++++++++++++++++++++ arch/arm/mach-msm/include/mach/fiq.h | 34 ++++++++ arch/arm/mach-msm/irq.c | 111 +++++++++++++++++++++++++- arch/arm/mach-msm/pm.c | 2 + 6 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 arch/arm/mach-msm/fiq_glue.S create mode 100644 arch/arm/mach-msm/include/mach/fiq.h diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 2d9796a71753c..db60444f6d15b 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -306,6 +306,10 @@ config MSM_IDLE_STATS_BUCKET_COUNT endif # MSM_IDLE_STATS +config MSM_FIQ_SUPPORT + default y + bool "Enable installation of an FIQ handler." + config MSM_SMD bool diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index dd6b6403e5e59..c8cffca6c37ca 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_ARCH_MSM8X60) += clock-dummy.o iommu.o iommu_dev.o devices-msm8x60- obj-$(CONFIG_MSM_PROC_COMM) += proc_comm.o clock-pcom.o vreg.o obj-$(CONFIG_MSM_PROC_COMM) += clock.o obj-$(CONFIG_ARCH_QSD8X50) += sirc.o +obj-$(CONFIG_MSM_FIQ_SUPPORT) += fiq_glue.o obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o obj-$(CONFIG_MSM_SMD) += smd_tty.o smd_qmi.o obj-$(CONFIG_MSM_SMD) += last_radio_log.o diff --git a/arch/arm/mach-msm/fiq_glue.S b/arch/arm/mach-msm/fiq_glue.S new file mode 100644 index 0000000000000..df1c7084fe115 --- /dev/null +++ b/arch/arm/mach-msm/fiq_glue.S @@ -0,0 +1,112 @@ +/* arch/arm/mach-msm/fiq_glue.S + * + * Copyright (C) 2008 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 + + .text + + .global fiq_glue_end + + /* fiq stack: r0-r15,cpsr,spsr of interrupted mode */ + +ENTRY(fiq_glue) + /* store pc, cpsr from previous mode */ + mrs r12, spsr + sub r11, lr, #4 + subs r10, #1 + bne nested_fiq + + stmfd sp!, {r11-r12, lr} + + /* store r8-r14 from previous mode */ + sub sp, sp, #(7 * 4) + stmia sp, {r8-r14}^ + nop + + /* store r0-r7 from previous mode */ + stmfd sp!, {r0-r7} + + /* setup func(data,regs) arguments */ + mov r0, r9 + mov r1, sp + mov r3, r8 + + mov r7, sp + + /* Get sp and lr from non-user modes */ + and r4, r12, #MODE_MASK + cmp r4, #USR_MODE + beq fiq_from_usr_mode + + mov r7, sp + orr r4, r4, #(PSR_I_BIT | PSR_F_BIT) + msr cpsr_c, r4 + str sp, [r7, #(4 * 13)] + str lr, [r7, #(4 * 14)] + mrs r5, spsr + str r5, [r7, #(4 * 17)] + + cmp r4, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT) + /* use fiq stack if we reenter this mode */ + subne sp, r7, #(4 * 3) + +fiq_from_usr_mode: + msr cpsr_c, #(SVC_MODE | PSR_I_BIT | PSR_F_BIT) + mov r2, sp + sub sp, r7, #12 + stmfd sp!, {r2, ip, lr} + /* call func(data,regs) */ + blx r3 + ldmfd sp, {r2, ip, lr} + mov sp, r2 + + /* restore/discard saved state */ + cmp r4, #USR_MODE + beq fiq_from_usr_mode_exit + + msr cpsr_c, r4 + ldr sp, [r7, #(4 * 13)] + ldr lr, [r7, #(4 * 14)] + msr spsr_cxsf, r5 + +fiq_from_usr_mode_exit: + msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT) + + ldmfd sp!, {r0-r7} + add sp, sp, #(7 * 4) + ldmfd sp!, {r11-r12, lr} +exit_fiq: + msr spsr_cxsf, r12 + add r10, #1 + movs pc, r11 + +nested_fiq: + orr r12, r12, #(PSR_F_BIT) + b exit_fiq + +fiq_glue_end: + +ENTRY(fiq_glue_setup) /* func, data, sp */ + mrs r3, cpsr + msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT) + movs r8, r0 + mov r9, r1 + mov sp, r2 + moveq r10, #0 + movne r10, #1 + msr cpsr_c, r3 + bx lr + diff --git a/arch/arm/mach-msm/include/mach/fiq.h b/arch/arm/mach-msm/include/mach/fiq.h new file mode 100644 index 0000000000000..64a0ea90c25bd --- /dev/null +++ b/arch/arm/mach-msm/include/mach/fiq.h @@ -0,0 +1,34 @@ +/* linux/include/asm-arm/arch-msm/irqs.h + * + * Copyright (C) 2008 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 __ASM_ARCH_MSM_FIQ_H +#define __ASM_ARCH_MSM_FIQ_H + +/* cause an interrupt to be an FIQ instead of a regular IRQ */ +void msm_fiq_select(int number); +void msm_fiq_unselect(int number); + +/* enable/disable an interrupt that is an FIQ (not safe from FIQ context) */ +void msm_fiq_enable(int number); +void msm_fiq_disable(int number); + +/* install an FIQ handler */ +int msm_fiq_set_handler(void (*func)(void *data, void *regs, void *svc_sp), + void *data); + +/* cause an edge triggered interrupt to fire (safe from FIQ context */ +void msm_trigger_irq(int number); + +#endif diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c index 8cf563dbed11b..4ed6b20cf7948 100644 --- a/arch/arm/mach-msm/irq.c +++ b/arch/arm/mach-msm/irq.c @@ -16,15 +16,20 @@ #include #include #include +#include #include #include #include #include #include +#include +#include + #include #include +#include #include "smd_private.h" @@ -81,6 +86,7 @@ static struct { uint32_t int_en[2]; uint32_t int_type; uint32_t int_polarity; + uint32_t int_select; } msm_irq_shadow_reg[2]; static uint32_t msm_irq_idle_disable[2]; @@ -323,8 +329,9 @@ void msm_irq_exit_sleep1(void) writel(msm_irq_shadow_reg[i].int_type, VIC_INT_TYPE0 + i * 4); writel(msm_irq_shadow_reg[i].int_polarity, VIC_INT_POLARITY0 + i * 4); writel(msm_irq_shadow_reg[i].int_en[0], VIC_INT_EN0 + i * 4); + writel(msm_irq_shadow_reg[i].int_select, VIC_INT_SELECT0 + i * 4); } - writel(1, VIC_INT_MASTEREN); + writel(3, VIC_INT_MASTEREN); int_info = smem_alloc(SMEM_SMSM_INT_INFO, sizeof(*int_info)); if (int_info == NULL) { printk(KERN_ERR "msm_irq_exit_sleep \n"); @@ -434,7 +441,7 @@ void __init msm_init_irq(void) writel(0, VIC_CONFIG); /* enable interrupt controller */ - writel(1, VIC_INT_MASTEREN); + writel(3, VIC_INT_MASTEREN); for (n = 0; n < NR_MSM_IRQS; n++) { set_irq_chip(n, &msm_irq_chip); @@ -442,3 +449,103 @@ void __init msm_init_irq(void) set_irq_flags(n, IRQF_VALID); } } + +#if defined(CONFIG_MSM_FIQ_SUPPORT) +void msm_trigger_irq(int irq) +{ + void __iomem *reg = VIC_SOFTINT0 + ((irq & 32) ? 4 : 0); + uint32_t mask = 1UL << (irq & 31); + writel(mask, reg); +} + +void msm_fiq_enable(int irq) +{ + unsigned long flags; + local_irq_save(flags); + msm_irq_unmask(irq); + local_irq_restore(flags); +} + +void msm_fiq_disable(int irq) +{ + unsigned long flags; + local_irq_save(flags); + msm_irq_mask(irq); + local_irq_restore(flags); +} + +void msm_fiq_select(int irq) +{ + void __iomem *reg = VIC_INT_SELECT0 + ((irq & 32) ? 4 : 0); + unsigned index = (irq >> 5) & 1; + uint32_t mask = 1UL << (irq & 31); + unsigned long flags; + + local_irq_save(flags); + msm_irq_shadow_reg[index].int_select |= mask; + writel(msm_irq_shadow_reg[index].int_select, reg); + local_irq_restore(flags); +} + +void msm_fiq_unselect(int irq) +{ + void __iomem *reg = VIC_INT_SELECT0 + ((irq & 32) ? 4 : 0); + unsigned index = (irq >> 5) & 1; + uint32_t mask = 1UL << (irq & 31); + unsigned long flags; + + local_irq_save(flags); + msm_irq_shadow_reg[index].int_select &= (!mask); + writel(msm_irq_shadow_reg[index].int_select, reg); + local_irq_restore(flags); +} +/* set_fiq_handler originally from arch/arm/kernel/fiq.c */ +static void set_fiq_handler(void *start, unsigned int length) +{ +#if defined(CONFIG_CPU_USE_DOMAINS) + memcpy((void *)0xffff001c, start, length); +#else + memcpy(vectors_page + 0x1c, start, length); +#endif + flush_icache_range(0xffff001c, 0xffff001c + length); + if (!vectors_high()) + flush_icache_range(0x1c, 0x1c + length); +} + +extern unsigned char fiq_glue, fiq_glue_end; + +static void (*fiq_func)(void *data, void *regs, void *svc_sp); +static void *fiq_data; +static void *fiq_stack; + +void fiq_glue_setup(void *func, void *data, void *sp); + +int msm_fiq_set_handler(void (*func)(void *data, void *regs, void *svc_sp), + void *data) +{ + unsigned long flags; + int ret = -ENOMEM; + + if (!fiq_stack) + fiq_stack = kzalloc(THREAD_SIZE, GFP_KERNEL); + if (!fiq_stack) + return -ENOMEM; + + local_irq_save(flags); + if (fiq_func == 0) { + fiq_func = func; + fiq_data = data; + fiq_glue_setup(func, data, fiq_stack + THREAD_START_SP); + set_fiq_handler(&fiq_glue, (&fiq_glue_end - &fiq_glue)); + ret = 0; + } + local_irq_restore(flags); + return ret; +} + +void msm_fiq_exit_sleep(void) +{ + if (fiq_stack) + fiq_glue_setup(fiq_func, fiq_data, fiq_stack + THREAD_START_SP); +} +#endif diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index 17de9f20e4086..fc1e11873b579 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -160,6 +160,7 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) void msm_irq_exit_sleep1(void); void msm_irq_exit_sleep2(void); void msm_irq_exit_sleep3(void); + void msm_fiq_exit_sleep(void); void msm_gpio_enter_sleep(int from_idle); void msm_gpio_exit_sleep(void); void smd_sleep_exit(void); @@ -260,6 +261,7 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) msm_pm_reset_vector[1] = saved_vector[1]; if (collapsed) { cpu_init(); + msm_fiq_exit_sleep(); local_fiq_enable(); rv = 0; } From 7bdf087c9ba3c7b68f98008430892ab4ff4cd993 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Tue, 9 Feb 2010 21:36:00 -0800 Subject: [PATCH 0187/2556] [ARM] msm_serial_debugger: fiq-mode serial debugger support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This provides the ability to handle simple debug commands from an fiq-context uart based debugger. kmsg - dump printk log pc - show current PC and mode regs - show current registers Other commands are handed off to an IRQ handler which calls the kernel_debugger() routine provided by KERNEL_DEBUGGER_CORE. Signed-off-by: Brian Swetland [ARM] msm_serial_debugger: Better interoperability with shared serial pins. Introduce debug_enable, debugger will not echo back RX characters until a CR is received. Other modules can turn off debug_enable via msm_serial_debug_enable() interface. Flush when doing TX in FIQ context. Turn off interrupts and flush when doing TX in IRQ context. Send a \r with every \n sent by the kernel debugger when processing console messages. Signed-off-by: Brian Swetland [ARM] msm_serial_debugger: irqs command to dump irq count Signed-off-by: Brian Swetland [ARM] msm: msm_serial_debugger: Add irq status to 'irqs' fiq debug command. Also clean up some alignment Signed-off-by: Nick Pelly [ARM] msm: fiq_debugger: Add ability to enable / disable debugger at runtime Signed-off-by: San Mehat [ARM] msm_serial_debugger: Support 19.2MHz clock on scorpion. Signed-off-by: Arve Hjønnevåg [ARM] msm_serial_debugger: Add wakeup irq and disable uart clock when idle The serial debugger is now inactive by default so we can enter low power modes. Hit enter twice to activate it for 5 seconds. Signed-off-by: Arve Hjønnevåg [ARM] msm_serial_debugger: Keep uart clock on when CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE is enabled Change-Id: I8c4e3c77d429a8f6fde068672d51e750e0f14c1b Signed-off-by: Arve Hjønnevåg msm_serial_debugger: fix to build without CONFIG_PREEMPT Change-Id: I71e115a26142cccd809aa979dfa9541f842ae680 [ARM] msm: serial_debugger: move the clock disable to after we enable the fiq If the uart fifo is not empty when we enable the fiq, the handler will try to empty it and hang since the clock disabled prior to enabling the fiq. Signed-off-by: Dima Zavin [ARM] msm_serial_debugger: Add option to keep serial debugger active from boot. If msm_serial_debugger.no_sleep=1 is added to the kernel command line, or MSM_SERIAL_DEBUGGER_NO_SLEEP is selected in the config, the serial debugger is activated on boot and stays active until it receives a sleep command. Change-Id: Ibf84435af8203360ee808fd903dd6322cf5d9d17 Signed-off-by: Arve Hjønnevåg [ARM] msm_serial_debugger: Fix startup when no_sleep is set Change-Id: I7e55567d723e30d3e998d625aa7a53f896b55d61 [ARM] msm_serial_debugger: Add option to never disable wakeup IRQ. This works better if the radio ignores the uart clock request while power collapsed. Change-Id: Ib0989e714e883b3667c9ecc4cfd1ebfe014a35df Signed-off-by: Arve Hjønnevåg [ARM] msm: Fix register dump in fiq debugger Change-Id: Iff5cd48291c9b09aace30220c4229c157a7db1d0 Signed-off-by: Arve Hjønnevåg [ARM] msm_serial_debugger: Add some debugger commands Add allregs to dump registers for all modes. Add bt to get a stackstrace. Change-Id: Ia85e72b6c8243eba38a04cf4f6cc9cba5342a6de Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/Kconfig | 36 + .../include/mach/msm_serial_debugger.h | 26 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/msm_serial_debugger.c | 686 ++++++++++++++++++ 4 files changed, 749 insertions(+) create mode 100644 arch/arm/mach-msm/include/mach/msm_serial_debugger.h create mode 100644 drivers/tty/serial/msm_serial_debugger.c diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index db60444f6d15b..680ae9a32c2e8 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -310,6 +310,42 @@ config MSM_FIQ_SUPPORT default y bool "Enable installation of an FIQ handler." +config MSM_SERIAL_DEBUGGER + select MSM_FIQ_SUPPORT + select KERNEL_DEBUGGER_CORE + default n + bool "FIQ Mode Serial Debugger" + help + The FIQ serial debugger can accept commands even when the + kernel is unresponsive due to being stuck with interrupts + disabled. Depends on the kernel debugger core in drivers/misc. + +config MSM_SERIAL_DEBUGGER_NO_SLEEP + depends on MSM_SERIAL_DEBUGGER + default n + bool "Keep serial debugger active" + help + Enables the serial debugger at boot. Passing + msm_serial_debugger.no_sleep on the kernel commandline will + override this config option. + +config MSM_SERIAL_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON + depends on MSM_SERIAL_DEBUGGER + default n + bool "Don't disable wakeup IRQ when debugger is active" + help + Don't disable the wakeup irq when enabling the uart clock. This will + cause extra interrupts, but it makes the serial debugger usable with + radio builds that ignore the uart clock request in power collapse. + +config MSM_SERIAL_DEBUGGER_CONSOLE + depends on MSM_SERIAL_DEBUGGER + default n + bool "Console on FIQ Serial Debugger port" + help + Enables a console so that printk messages are displayed on + the debugger serial port as the occur. + config MSM_SMD bool diff --git a/arch/arm/mach-msm/include/mach/msm_serial_debugger.h b/arch/arm/mach-msm/include/mach/msm_serial_debugger.h new file mode 100644 index 0000000000000..f490b1be4f21a --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_serial_debugger.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2009 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 __ASM_ARCH_MSM_SERIAL_DEBUGGER_H +#define __ASM_ARCH_MSM_SERIAL_DEBUGGER_H + +#if defined(CONFIG_MSM_SERIAL_DEBUGGER) +void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq, int wakeup_irq); +#else +static inline void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq, int wakeup_irq) {} +#endif + +#endif diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 8ea92e9c73b04..5117e18bf8eae 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o obj-$(CONFIG_SERIAL_MSM) += msm_serial.o +obj-$(CONFIG_MSM_SERIAL_DEBUGGER) += msm_serial_debugger.o obj-$(CONFIG_SERIAL_NETX) += netx-serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o diff --git a/drivers/tty/serial/msm_serial_debugger.c b/drivers/tty/serial/msm_serial_debugger.c new file mode 100644 index 0000000000000..a355e58f90d1c --- /dev/null +++ b/drivers/tty/serial/msm_serial_debugger.c @@ -0,0 +1,686 @@ +/* + * drivers/serial/msm_serial_debuger.c + * + * Serial Debugger Interface for MSM7K + * + * Copyright (C) 2008 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "msm_serial.h" + +#include + +static void sleep_timer_expired(unsigned long); + +static unsigned int debug_port_base; +static int debug_signal_irq; +static struct clk *debug_clk; +static bool debug_clk_enabled; +static bool ignore_next_wakeup_irq; +#ifdef CONFIG_MSM_SERIAL_DEBUGGER_NO_SLEEP +static int no_sleep = true; +#else +static int no_sleep; +#endif +static DEFINE_TIMER(sleep_timer, sleep_timer_expired, 0, 0); +static int debug_enable; +static int debugger_enable; +static struct wake_lock debugger_wake_lock; +static struct { + unsigned int base; + int irq; + struct device *clk_device; + int signal_irq; + int wakeup_irq; +} init_data; + +module_param(no_sleep, bool, 0644); + +#ifdef CONFIG_MSM_SERIAL_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON +static inline void enable_wakeup_irq(unsigned int irq) {} +static inline void disable_wakeup_irq(unsigned int irq) {} +#else +static inline void enable_wakeup_irq(unsigned int irq) {enable_irq(irq);} +static inline void disable_wakeup_irq(unsigned int irq) + {disable_irq_nosync(irq);} +#endif + + +static inline void msm_write(unsigned int val, unsigned int off) +{ + __raw_writel(val, debug_port_base + off); +} + +static inline unsigned int msm_read(unsigned int off) +{ + return __raw_readl(debug_port_base + off); +} + +static void debug_port_init(void) +{ + /* reset everything */ + msm_write(UART_CR_CMD_RESET_RX, UART_CR); + msm_write(UART_CR_CMD_RESET_TX, UART_CR); + msm_write(UART_CR_CMD_RESET_ERR, UART_CR); + msm_write(UART_CR_CMD_RESET_BREAK_INT, UART_CR); + msm_write(UART_CR_CMD_RESET_CTS, UART_CR); + msm_write(UART_CR_CMD_SET_RFR, UART_CR); + + /* setup clock dividers */ +#ifdef CONFIG_ARCH_MSM_SCORPION + if (clk_get_rate(debug_clk) == 19200000) { + /* clock is TCXO (19.2MHz) */ + msm_write(0x06, UART_MREG); + msm_write(0xF1, UART_NREG); + msm_write(0x0F, UART_DREG); + msm_write(0x1A, UART_MNDREG); + } else +#endif + { + /* clock must be TCXO/4 */ + msm_write(0xC0, UART_MREG); + msm_write(0xB2, UART_NREG); + msm_write(0x7D, UART_DREG); + msm_write(0x1C, UART_MNDREG); + } + + msm_write(UART_CSR_115200, UART_CSR); + + /* rx interrupt on every character -- keep it simple */ + msm_write(0, UART_RFWR); + + /* enable TX and RX */ + msm_write(0x05, UART_CR); + + /* enable RX interrupt */ + msm_write(UART_IMR_RXLEV, UART_IMR); +} + +static inline int debug_getc(void) +{ + if (msm_read(UART_SR) & UART_SR_RX_READY) { + return msm_read(UART_RF); + } else { + return -1; + } +} + +static inline void debug_putc(unsigned int c) +{ + while (!(msm_read(UART_SR) & UART_SR_TX_READY)) ; + msm_write(c, UART_TF); +} + +static inline void debug_flush(void) +{ + while (!(msm_read(UART_SR) & UART_SR_TX_EMPTY)) ; +} + +static void debug_puts(char *s) +{ + unsigned c; + while ((c = *s++)) { + if (c == '\n') + debug_putc('\r'); + debug_putc(c); + } +} + +static void debug_prompt(void) +{ + debug_puts("debug> "); +} + +int log_buf_copy(char *dest, int idx, int len); +static void dump_kernel_log(void) +{ + char buf[1024]; + int idx = 0; + int ret; + int saved_oip; + + /* setting oops_in_progress prevents log_buf_copy() + * from trying to take a spinlock which will make it + * very unhappy in some cases... + */ + saved_oip = oops_in_progress; + oops_in_progress = 1; + for (;;) { + ret = log_buf_copy(buf, idx, 1023); + if (ret <= 0) + break; + buf[ret] = 0; + debug_puts(buf); + idx += ret; + } + oops_in_progress = saved_oip; +} + +static char *mode_name(unsigned cpsr) +{ + switch (cpsr & MODE_MASK) { + case USR_MODE: return "USR"; + case FIQ_MODE: return "FIQ"; + case IRQ_MODE: return "IRQ"; + case SVC_MODE: return "SVC"; + case ABT_MODE: return "ABT"; + case UND_MODE: return "UND"; + case SYSTEM_MODE: return "SYS"; + default: return "???"; + } +} + +#define DEBUG_MAX 64 +static char debug_cmd[DEBUG_MAX]; +static int debug_busy; +static int debug_abort; + +static int debug_printf(void *cookie, const char *fmt, ...) +{ + char buf[256]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + debug_puts(buf); + return debug_abort; +} + +/* Safe outside fiq context */ +static int debug_printf_nfiq(void *cookie, const char *fmt, ...) +{ + char buf[256]; + va_list ap; + unsigned long irq_flags; + + va_start(ap, fmt); + vsnprintf(buf, 128, fmt, ap); + va_end(ap); + + local_irq_save(irq_flags); + debug_puts(buf); + debug_flush(); + local_irq_restore(irq_flags); + return debug_abort; +} + +#define dprintf(fmt...) debug_printf(0, fmt) + +unsigned int last_irqs[NR_IRQS]; + +static void dump_regs(unsigned *regs) +{ + dprintf(" r0 %08x r1 %08x r2 %08x r3 %08x\n", + regs[0], regs[1], regs[2], regs[3]); + dprintf(" r4 %08x r5 %08x r6 %08x r7 %08x\n", + regs[4], regs[5], regs[6], regs[7]); + dprintf(" r8 %08x r9 %08x r10 %08x r11 %08x mode %s\n", + regs[8], regs[9], regs[10], regs[11], + mode_name(regs[16])); + if ((regs[16] & MODE_MASK) == USR_MODE) + dprintf(" ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", + regs[12], regs[13], regs[14], regs[15], regs[16]); + else + dprintf(" ip %08x sp %08x lr %08x pc %08x cpsr %08x " + "spsr %08x\n", regs[12], regs[13], regs[14], regs[15], + regs[16], regs[17]); +} + +struct mode_regs { + unsigned long sp_svc; + unsigned long lr_svc; + unsigned long spsr_svc; + + unsigned long sp_abt; + unsigned long lr_abt; + unsigned long spsr_abt; + + unsigned long sp_und; + unsigned long lr_und; + unsigned long spsr_und; + + unsigned long sp_irq; + unsigned long lr_irq; + unsigned long spsr_irq; + + unsigned long r8_fiq; + unsigned long r9_fiq; + unsigned long r10_fiq; + unsigned long r11_fiq; + unsigned long r12_fiq; + unsigned long sp_fiq; + unsigned long lr_fiq; + unsigned long spsr_fiq; +}; + +void __naked get_mode_regs(struct mode_regs *regs) +{ + asm volatile ( + "mrs r1, cpsr\n" + "msr cpsr_c, #0xd3 @(SVC_MODE | PSR_I_BIT | PSR_F_BIT)\n" + "stmia r0!, {r13 - r14}\n" + "mrs r2, spsr\n" + "msr cpsr_c, #0xd7 @(ABT_MODE | PSR_I_BIT | PSR_F_BIT)\n" + "stmia r0!, {r2, r13 - r14}\n" + "mrs r2, spsr\n" + "msr cpsr_c, #0xdb @(UND_MODE | PSR_I_BIT | PSR_F_BIT)\n" + "stmia r0!, {r2, r13 - r14}\n" + "mrs r2, spsr\n" + "msr cpsr_c, #0xd2 @(IRQ_MODE | PSR_I_BIT | PSR_F_BIT)\n" + "stmia r0!, {r2, r13 - r14}\n" + "mrs r2, spsr\n" + "msr cpsr_c, #0xd1 @(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)\n" + "stmia r0!, {r2, r8 - r14}\n" + "mrs r2, spsr\n" + "stmia r0!, {r2}\n" + "msr cpsr_c, r1\n" + "bx lr\n"); +} + + +static void dump_allregs(unsigned *regs) +{ + struct mode_regs mode_regs; + dump_regs(regs); + get_mode_regs(&mode_regs); + dprintf(" svc: sp %08x lr %08x spsr %08x\n", + mode_regs.sp_svc, mode_regs.lr_svc, mode_regs.spsr_svc); + dprintf(" abt: sp %08x lr %08x spsr %08x\n", + mode_regs.sp_abt, mode_regs.lr_abt, mode_regs.spsr_abt); + dprintf(" und: sp %08x lr %08x spsr %08x\n", + mode_regs.sp_und, mode_regs.lr_und, mode_regs.spsr_und); + dprintf(" irq: sp %08x lr %08x spsr %08x\n", + mode_regs.sp_irq, mode_regs.lr_irq, mode_regs.spsr_irq); + dprintf(" fiq: r8 %08x r9 %08x r10 %08x r11 %08x r12 %08x\n", + mode_regs.r8_fiq, mode_regs.r9_fiq, mode_regs.r10_fiq, + mode_regs.r11_fiq, mode_regs.r12_fiq); + dprintf(" fiq: sp %08x lr %08x spsr %08x\n", + mode_regs.sp_fiq, mode_regs.lr_fiq, mode_regs.spsr_fiq); +} + +static void dump_irqs(void) +{ + int n; + dprintf("irqnr total since-last status name\n"); + for (n = 1; n < NR_IRQS; n++) { + struct irqaction *act = irq_desc[n].action; + if (!act && !kstat_irqs(n)) + continue; + dprintf("%5d: %10u %11u %8x %s\n", n, + kstat_irqs(n), + kstat_irqs(n) - last_irqs[n], + irq_desc[n].status, + (act && act->name) ? act->name : "???"); + last_irqs[n] = kstat_irqs(n); + } +} + +static int report_trace(struct stackframe *frame, void *d) +{ + unsigned int *depth = d; + + if (*depth) { + dprintf(" pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n", + frame->pc, frame->pc, frame->lr, frame->lr, + frame->sp, frame->fp); + (*depth)--; + return 0; + } + dprintf(" ...\n"); + + return *depth == 0; +} + +struct frame_tail { + struct frame_tail *fp; + unsigned long sp; + unsigned long lr; +} __attribute__((packed)); + +static struct frame_tail *user_backtrace(struct frame_tail *tail) +{ + struct frame_tail buftail[2]; + + /* Also check accessibility of one struct frame_tail beyond */ + if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) { + dprintf(" invalid frame pointer %p\n", tail); + return NULL; + } + if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) { + dprintf(" failed to copy frame pointer %p\n", tail); + return NULL; + } + + dprintf(" %p\n", buftail[0].lr); + + /* frame pointers should strictly progress back up the stack + * (towards higher addresses) */ + if (tail >= buftail[0].fp) + return NULL; + + return buftail[0].fp-1; +} + +void dump_stacktrace(struct pt_regs * const regs, unsigned int depth, void *ssp) +{ + struct frame_tail *tail; + struct thread_info *real_thread_info = (struct thread_info *) + ((unsigned long)ssp & ~(THREAD_SIZE - 1)); + + *current_thread_info() = *real_thread_info; + + if (!current) + dprintf("current NULL\n"); + else + dprintf("pid: %d comm: %s\n", current->pid, current->comm); + dump_regs((unsigned *)regs); + + if (!user_mode(regs)) { + struct stackframe frame; + frame.fp = regs->ARM_fp; + frame.sp = regs->ARM_sp; + frame.lr = regs->ARM_lr; + frame.pc = regs->ARM_pc; + dprintf(" pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n", + regs->ARM_pc, regs->ARM_pc, regs->ARM_lr, regs->ARM_lr, + regs->ARM_sp, regs->ARM_fp); + walk_stackframe(&frame, report_trace, &depth); + return; + } + + tail = ((struct frame_tail *) regs->ARM_fp) - 1; + while (depth-- && tail && !((unsigned long) tail & 3)) + tail = user_backtrace(tail); +} + +static void debug_exec(const char *cmd, unsigned *regs, void *svc_sp) +{ + if (!strcmp(cmd, "pc")) { + dprintf(" pc %08x cpsr %08x mode %s\n", + regs[15], regs[16], mode_name(regs[16])); + } else if (!strcmp(cmd, "regs")) { + dump_regs(regs); + } else if (!strcmp(cmd, "allregs")) { + dump_allregs(regs); + } else if (!strcmp(cmd, "bt")) { + dump_stacktrace((struct pt_regs *)regs, 100, svc_sp); + } else if (!strcmp(cmd, "reboot")) { + if (msm_hw_reset_hook) + msm_hw_reset_hook(); + } else if (!strcmp(cmd, "irqs")) { + dump_irqs(); + } else if (!strcmp(cmd, "kmsg")) { + dump_kernel_log(); + } else if (!strcmp(cmd, "version")) { + dprintf("%s\n", linux_banner); + } else if (!strcmp(cmd, "sleep")) { + no_sleep = false; + } else if (!strcmp(cmd, "nosleep")) { + no_sleep = true; + } else { + if (debug_busy) { + dprintf("command processor busy. trying to abort.\n"); + debug_abort = -1; + } else { + strcpy(debug_cmd, cmd); + debug_busy = 1; + } + msm_trigger_irq(debug_signal_irq); + return; + } + debug_prompt(); +} + +static void sleep_timer_expired(unsigned long data) +{ + if (debug_clk_enabled && !no_sleep) { + if (debug_enable) { + debug_enable = 0; + debug_printf_nfiq(NULL, + "suspending fiq debugger\n"); + } + ignore_next_wakeup_irq = true; + clk_disable(debug_clk); + debug_clk_enabled = false; + enable_wakeup_irq(init_data.wakeup_irq); + set_irq_wake(init_data.wakeup_irq, 1); + } + wake_unlock(&debugger_wake_lock); +} + +static irqreturn_t wakeup_irq_handler(int irq, void *dev) +{ + if (ignore_next_wakeup_irq) + ignore_next_wakeup_irq = false; + else if (!debug_clk_enabled) { + wake_lock(&debugger_wake_lock); + clk_enable(debug_clk); + debug_clk_enabled = true; + set_irq_wake(irq, 0); + disable_wakeup_irq(irq); + mod_timer(&sleep_timer, jiffies + HZ / 2); + } + return IRQ_HANDLED; +} + +static irqreturn_t debug_irq(int irq, void *dev) +{ + if (!no_sleep) { + wake_lock(&debugger_wake_lock); + mod_timer(&sleep_timer, jiffies + HZ * 5); + } + if (debug_busy) { + struct kdbg_ctxt ctxt; + + ctxt.printf = debug_printf_nfiq; + kernel_debugger(&ctxt, debug_cmd); + debug_prompt(); + + debug_busy = 0; + } + return IRQ_HANDLED; +} + +static char debug_buf[DEBUG_MAX]; +static int debug_count; + +static void debug_fiq(void *data, void *regs, void *svc_sp) +{ + int c; + static int last_c; + + while ((c = debug_getc()) != -1) { + if (!debug_enable) { + if ((c == 13) || (c == 10)) { + debug_enable = true; + debug_count = 0; + debug_prompt(); + } + } else if ((c >= ' ') && (c < 127)) { + if (debug_count < (DEBUG_MAX - 1)) { + debug_buf[debug_count++] = c; + debug_putc(c); + } + } else if ((c == 8) || (c == 127)) { + if (debug_count > 0) { + debug_count--; + debug_putc(8); + debug_putc(' '); + debug_putc(8); + } + } else if ((c == 13) || (c == 10)) { + if (c == '\r' || (c == '\n' && last_c != '\r')) { + debug_putc('\r'); + debug_putc('\n'); + } + if (debug_count) { + debug_buf[debug_count] = 0; + debug_count = 0; + debug_exec(debug_buf, regs, svc_sp); + } else { + debug_prompt(); + } + } + last_c = c; + } + debug_flush(); + if (debug_enable && !no_sleep) + msm_trigger_irq(debug_signal_irq); /* poke sleep timer */ +} + +#if defined(CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE) +static void debug_console_write(struct console *co, + const char *s, unsigned int count) +{ + unsigned long irq_flags; + + /* disable irq's while TXing outside of FIQ context */ + local_irq_save(irq_flags); + while (count--) { + if (*s == '\n') + debug_putc('\r'); + debug_putc(*s++); + } + debug_flush(); + local_irq_restore(irq_flags); +} + +static struct console msm_serial_debug_console = { + .name = "debug_console", + .write = debug_console_write, + .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED, +}; +#endif + +void msm_serial_debug_enable(int enable) { + debug_enable = enable; +} + +void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq, int wakeup_irq) +{ + int ret; + void *port; + + debug_clk = clk_get(clk_device, "uart_clk"); + if (!debug_clk) + return; + + port = ioremap(base, 4096); + if (!port) + return; + + wake_lock_init(&debugger_wake_lock, WAKE_LOCK_SUSPEND, "serial-debug"); + + init_data.base = base; + init_data.irq = irq; + init_data.clk_device = clk_device; + init_data.signal_irq = signal_irq; + init_data.wakeup_irq = wakeup_irq; + debug_port_base = (unsigned int) port; + debug_signal_irq = signal_irq; + clk_enable(debug_clk); + debug_port_init(); + + debug_printf_nfiq(NULL, "\n", + no_sleep ? "" : "twice "); + ignore_next_wakeup_irq = !no_sleep; + + msm_fiq_select(irq); + msm_fiq_set_handler(debug_fiq, 0); + msm_fiq_enable(irq); + clk_disable(debug_clk); + + ret = request_irq(signal_irq, debug_irq, + IRQF_TRIGGER_RISING, "debug", 0); + if (ret) + printk(KERN_ERR + "serial_debugger: could not install signal_irq"); + + ret = set_irq_wake(wakeup_irq, 1); + if (ret) + pr_err("serial_debugger: could not enable wakeup\n"); + ret = request_irq(wakeup_irq, wakeup_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_DISABLED, + "debug-wakeup", 0); + if (ret) + pr_err("serial_debugger: could not install wakeup irq\n"); + if (no_sleep) + wakeup_irq_handler(wakeup_irq, 0); + +#if defined(CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE) + register_console(&msm_serial_debug_console); + clk_enable(debug_clk); +#endif + debugger_enable = 1; +} +static int msm_serial_debug_remove(const char *val, struct kernel_param *kp) +{ + int ret; + static int pre_stat = 1; + ret = param_set_bool(val, kp); + if (ret) + return ret; + + if (pre_stat == *(int *)kp->arg) + return 0; + + pre_stat = *(int *)kp->arg; + + if (*(int *)kp->arg) { + msm_serial_debug_init(init_data.base, init_data.irq, + init_data.clk_device, init_data.signal_irq, + init_data.wakeup_irq); + printk(KERN_INFO "enable FIQ serial debugger\n"); + return 0; + } + +#if defined(CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE) + unregister_console(&msm_serial_debug_console); + clk_disable(debug_clk); +#endif + free_irq(init_data.wakeup_irq, 0); + free_irq(init_data.signal_irq, 0); + msm_fiq_set_handler(NULL, 0); + msm_fiq_disable(init_data.irq); + msm_fiq_unselect(init_data.irq); + if (debug_clk_enabled) + clk_disable(debug_clk); + wake_lock_destroy(&debugger_wake_lock); + printk(KERN_INFO "disable FIQ serial debugger\n"); + return 0; +} +module_param_call(enable, msm_serial_debug_remove, param_get_bool, + &debugger_enable, S_IWUSR | S_IRUGO); From fdec84255aecd3b0e6dfb6503b47a4018d0accdf Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Mon, 7 Apr 2008 17:59:14 -0700 Subject: [PATCH 0188/2556] [ARM] msm: halibut: support fiq mode kernel debugger if configured Signed-off-by: Brian Swetland --- arch/arm/mach-msm/board-halibut.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm/mach-msm/board-halibut.c b/arch/arm/mach-msm/board-halibut.c index 37047eadece0f..2377461b20d58 100644 --- a/arch/arm/mach-msm/board-halibut.c +++ b/arch/arm/mach-msm/board-halibut.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -58,7 +59,9 @@ static struct platform_device smc91x_device = { }; static struct platform_device *devices[] __initdata = { +#if !defined(CONFIG_MSM_SERIAL_DEBUGGER) &msm_device_uart3, +#endif &msm_device_smd, &msm_device_nand, &msm_device_hsusb, @@ -81,8 +84,15 @@ static struct msm_acpu_clock_platform_data halibut_clock_data = { .wait_for_irq_khz = 128000000, }; +void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq); + static void __init halibut_init(void) { +#if defined(CONFIG_MSM_SERIAL_DEBUGGER) + msm_serial_debug_init(MSM_UART3_PHYS, INT_UART3, + &msm_device_uart3.dev, 1); +#endif msm_acpu_clock_init(&halibut_clock_data); platform_add_devices(devices, ARRAY_SIZE(devices)); } From b31387a1732cc68eb20ed2022df6e84babbad622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Sat, 1 Dec 2007 19:01:30 -0800 Subject: [PATCH 0189/2556] [ARM] msm: add keypad for halibut board Signed-off-by: Brian Swetland --- arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/board-halibut-keypad.c | 117 +++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 arch/arm/mach-msm/board-halibut-keypad.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index c8cffca6c37ca..cbfca54f4a5bd 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_MSM_CPU_FREQ_SCREEN) += cpufreq.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o +obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o obj-$(CONFIG_ARCH_QSD8X50) += board-qsd8x50.o devices-qsd8x50.o obj-$(CONFIG_ARCH_MSM8X60) += board-msm8x60.o diff --git a/arch/arm/mach-msm/board-halibut-keypad.c b/arch/arm/mach-msm/board-halibut-keypad.c new file mode 100644 index 0000000000000..46bf666c8f417 --- /dev/null +++ b/arch/arm/mach-msm/board-halibut-keypad.c @@ -0,0 +1,117 @@ +/* linux/arch/arm/mach-msm/board-halibut-keypad.c + * + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland + * + * 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 + +#define SCAN_FUNCTION_KEYS 0 + +static unsigned int halibut_row_gpios[] = { + 31, 32, 33, 34, 35, 41 +#if SCAN_FUNCTION_KEYS + , 42 +#endif +}; + +static unsigned int halibut_col_gpios[] = { 36, 37, 38, 39, 40 }; + +#define KEYMAP_INDEX(row, col) ((row)*ARRAY_SIZE(halibut_col_gpios) + (col)) + +static const unsigned short halibut_keymap[ARRAY_SIZE(halibut_col_gpios) * ARRAY_SIZE(halibut_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_5, + [KEYMAP_INDEX(0, 1)] = KEY_9, + [KEYMAP_INDEX(0, 2)] = 229, /* SOFT1 */ + [KEYMAP_INDEX(0, 3)] = KEY_6, + [KEYMAP_INDEX(0, 4)] = KEY_LEFT, + + [KEYMAP_INDEX(1, 0)] = KEY_0, + [KEYMAP_INDEX(1, 1)] = KEY_RIGHT, + [KEYMAP_INDEX(1, 2)] = KEY_1, + [KEYMAP_INDEX(1, 3)] = 228, /* KEY_SHARP */ + [KEYMAP_INDEX(1, 4)] = KEY_SEND, + + [KEYMAP_INDEX(2, 0)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(2, 1)] = KEY_HOME, /* FA */ + [KEYMAP_INDEX(2, 2)] = KEY_F8, /* QCHT */ + [KEYMAP_INDEX(2, 3)] = KEY_F6, /* R+ */ + [KEYMAP_INDEX(2, 4)] = KEY_F7, /* R- */ + + [KEYMAP_INDEX(3, 0)] = KEY_UP, + [KEYMAP_INDEX(3, 1)] = KEY_CLEAR, + [KEYMAP_INDEX(3, 2)] = KEY_4, + [KEYMAP_INDEX(3, 3)] = KEY_MUTE, /* SPKR */ + [KEYMAP_INDEX(3, 4)] = KEY_2, + + [KEYMAP_INDEX(4, 0)] = 230, /* SOFT2 */ + [KEYMAP_INDEX(4, 1)] = 232, /* KEY_CENTER */ + [KEYMAP_INDEX(4, 2)] = KEY_DOWN, + [KEYMAP_INDEX(4, 3)] = KEY_BACK, /* FB */ + [KEYMAP_INDEX(4, 4)] = KEY_8, + + [KEYMAP_INDEX(5, 0)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */ + [KEYMAP_INDEX(5, 2)] = KEY_MAIL, /* MESG */ + [KEYMAP_INDEX(5, 3)] = KEY_3, + [KEYMAP_INDEX(5, 4)] = KEY_7, + +#if SCAN_FUNCTION_KEYS + [KEYMAP_INDEX(6, 0)] = KEY_F5, + [KEYMAP_INDEX(6, 1)] = KEY_F4, + [KEYMAP_INDEX(6, 2)] = KEY_F3, + [KEYMAP_INDEX(6, 3)] = KEY_F2, + [KEYMAP_INDEX(6, 4)] = KEY_F1 +#endif +}; + +static struct gpio_event_matrix_info halibut_matrix_info = { + .info.func = gpio_event_matrix_func, + .keymap = halibut_keymap, + .output_gpios = halibut_row_gpios, + .input_gpios = halibut_col_gpios, + .noutputs = ARRAY_SIZE(halibut_row_gpios), + .ninputs = ARRAY_SIZE(halibut_col_gpios), + .settle_time.tv.nsec = 0, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE | GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/ +}; + +struct gpio_event_info *halibut_keypad_info[] = { + &halibut_matrix_info.info +}; + +static struct gpio_event_platform_data halibut_keypad_data = { + .name = "halibut_keypad", + .info = halibut_keypad_info, + .info_count = ARRAY_SIZE(halibut_keypad_info) +}; + +static struct platform_device halibut_keypad_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &halibut_keypad_data, + }, +}; + +static int __init halibut_init_keypad(void) +{ + if (!machine_is_halibut()) + return 0; + return platform_device_register(&halibut_keypad_device); +} + +device_initcall(halibut_init_keypad); From 209a2175a890749113ba248e2c141c3888e97c49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 4 Feb 2008 17:14:40 -0800 Subject: [PATCH 0190/2556] [ARM] msm: Add FFA keymap to halibut. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add board_halibut.ffa=1 to the kernel command line to use it. Signed-off-by: Brian Swetland Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/board-halibut-keypad.c | 62 +++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-halibut-keypad.c b/arch/arm/mach-msm/board-halibut-keypad.c index 46bf666c8f417..49c1075627d32 100644 --- a/arch/arm/mach-msm/board-halibut-keypad.c +++ b/arch/arm/mach-msm/board-halibut-keypad.c @@ -18,7 +18,12 @@ #include #include -#define SCAN_FUNCTION_KEYS 0 +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "board_halibut." +static int halibut_ffa; +module_param_named(ffa, halibut_ffa, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define SCAN_FUNCTION_KEYS 0 /* don't turn this on without updating the ffa support */ static unsigned int halibut_row_gpios[] = { 31, 32, 33, 34, 35, 41 @@ -29,6 +34,21 @@ static unsigned int halibut_row_gpios[] = { static unsigned int halibut_col_gpios[] = { 36, 37, 38, 39, 40 }; +/* FFA: + 36: KEYSENSE_N(0) + 37: KEYSENSE_N(1) + 38: KEYSENSE_N(2) + 39: KEYSENSE_N(3) + 40: KEYSENSE_N(4) + + 31: KYPD_17 + 32: KYPD_15 + 33: KYPD_13 + 34: KYPD_11 + 35: KYPD_9 + 41: KYPD_MEMO +*/ + #define KEYMAP_INDEX(row, col) ((row)*ARRAY_SIZE(halibut_col_gpios) + (col)) static const unsigned short halibut_keymap[ARRAY_SIZE(halibut_col_gpios) * ARRAY_SIZE(halibut_row_gpios)] = { @@ -77,6 +97,44 @@ static const unsigned short halibut_keymap[ARRAY_SIZE(halibut_col_gpios) * ARRAY #endif }; +static const unsigned short halibut_keymap_ffa[ARRAY_SIZE(halibut_col_gpios) * ARRAY_SIZE(halibut_row_gpios)] = { + /*[KEYMAP_INDEX(0, 0)] = ,*/ + /*[KEYMAP_INDEX(0, 1)] = ,*/ + [KEYMAP_INDEX(0, 2)] = KEY_1, + [KEYMAP_INDEX(0, 3)] = KEY_SEND, + [KEYMAP_INDEX(0, 4)] = KEY_LEFT, + + [KEYMAP_INDEX(1, 0)] = KEY_3, + [KEYMAP_INDEX(1, 1)] = KEY_RIGHT, + [KEYMAP_INDEX(1, 2)] = KEY_VOLUMEUP, + /*[KEYMAP_INDEX(1, 3)] = ,*/ + [KEYMAP_INDEX(1, 4)] = KEY_6, + + [KEYMAP_INDEX(2, 0)] = KEY_HOME, /* A */ + [KEYMAP_INDEX(2, 1)] = KEY_BACK, /* B */ + [KEYMAP_INDEX(2, 2)] = KEY_0, + [KEYMAP_INDEX(2, 3)] = 228, /* KEY_SHARP */ + [KEYMAP_INDEX(2, 4)] = KEY_9, + + [KEYMAP_INDEX(3, 0)] = KEY_UP, + [KEYMAP_INDEX(3, 1)] = 232, /* KEY_CENTER */ /* i */ + [KEYMAP_INDEX(3, 2)] = KEY_4, + /*[KEYMAP_INDEX(3, 3)] = ,*/ + [KEYMAP_INDEX(3, 4)] = KEY_2, + + [KEYMAP_INDEX(4, 0)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(4, 1)] = KEY_SOUND, + [KEYMAP_INDEX(4, 2)] = KEY_DOWN, + [KEYMAP_INDEX(4, 3)] = KEY_8, + [KEYMAP_INDEX(4, 4)] = KEY_5, + + /*[KEYMAP_INDEX(5, 0)] = ,*/ + [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */ + [KEYMAP_INDEX(5, 2)] = 230, /*SOFT2*/ /* 2 */ + [KEYMAP_INDEX(5, 3)] = KEY_MENU, /* 1 */ + [KEYMAP_INDEX(5, 4)] = KEY_7, +}; + static struct gpio_event_matrix_info halibut_matrix_info = { .info.func = gpio_event_matrix_func, .keymap = halibut_keymap, @@ -111,6 +169,8 @@ static int __init halibut_init_keypad(void) { if (!machine_is_halibut()) return 0; + if (halibut_ffa) + halibut_matrix_info.keymap = halibut_keymap_ffa; return platform_device_register(&halibut_keypad_device); } From 0ec52bd14e3c6c2a54cca7b03b6718b11f302ff6 Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Date: Mon, 29 Sep 2008 17:32:13 -0700 Subject: [PATCH 0191/2556] [ARM] msm: trout: Add trout (aka Dream/G1) machine target and some defines Signed-off-by: Rebecca Schultz Signed-off-by: Mike Chan Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/board-trout.c | 652 ++++++++++++++++++++++++++++++-- 1 file changed, 618 insertions(+), 34 deletions(-) diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 73f1460665421..df357be8eb8f9 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -1,7 +1,6 @@ -/* linux/arch/arm/mach-msm/board-trout.c +/* arch/arm/mach-msm/board-trout.c * - * Copyright (C) 2009 Google, Inc. - * Author: Brian Swetland + * Copyright (C) 2008 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 @@ -17,56 +16,633 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include <../../../drivers/staging/android/timed_gpio.h> +#include +#include +#include +#include +#ifdef CONFIG_USB_FUNCTION +#include +#endif + +#include +#include +#include #include #include #include +#include +#include +#include +#include + +#include +#include #include +#include +#include +#include + +#include +#include + +#include "board-trout.h" + +#include "gpio_chip.h" + #include -#include -#include +#ifdef CONFIG_USB_FUNCTION +#include +#endif +#include "proc_comm.h" #include "devices.h" -#include "board-trout.h" + +void msm_init_irq(void); +void msm_init_gpio(void); + +struct trout_axis_info { + struct gpio_event_axis_info info; + uint16_t in_state; + uint16_t out_state; +}; +static bool nav_just_on; +static int nav_on_jiffies; + +uint16_t trout_axis_map(struct gpio_event_axis_info *info, uint16_t in) +{ + struct trout_axis_info *ai = container_of(info, struct trout_axis_info, info); + uint16_t out = ai->out_state; + + if (nav_just_on) { + if (jiffies == nav_on_jiffies || jiffies == nav_on_jiffies + 1) + goto ignore; + nav_just_on = 0; + } + if((ai->in_state ^ in) & 1) + out--; + if((ai->in_state ^ in) & 2) + out++; + ai->out_state = out; +ignore: + ai->in_state = in; + return out; +} + +int trout_nav_power(const struct gpio_event_platform_data *pdata, bool on) +{ + gpio_set_value(TROUT_GPIO_JOG_EN, on); + if (on) { + nav_just_on = 1; + nav_on_jiffies = jiffies; + } + return 0; +} + +static uint32_t trout_4_x_axis_gpios[] = { + TROUT_4_BALL_LEFT_0, TROUT_4_BALL_RIGHT_0 +}; +static uint32_t trout_5_x_axis_gpios[] = { + TROUT_5_BALL_LEFT_0, TROUT_5_BALL_RIGHT_0 +}; + +static struct trout_axis_info trout_x_axis = { + .info = { + .info.func = gpio_event_axis_func, + .count = ARRAY_SIZE(trout_5_x_axis_gpios), + .type = EV_REL, + .code = REL_X, + .decoded_size = 1U << ARRAY_SIZE(trout_5_x_axis_gpios), + .map = trout_axis_map, + .gpio = trout_5_x_axis_gpios, + .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION /*| GPIOEAF_PRINT_RAW | GPIOEAF_PRINT_EVENT */ + } +}; + +static uint32_t trout_4_y_axis_gpios[] = { + TROUT_4_BALL_UP_0, TROUT_4_BALL_DOWN_0 +}; +static uint32_t trout_5_y_axis_gpios[] = { + TROUT_5_BALL_UP_0, TROUT_5_BALL_DOWN_0 +}; + +static struct trout_axis_info trout_y_axis = { + .info = { + .info.func = gpio_event_axis_func, + .count = ARRAY_SIZE(trout_5_y_axis_gpios), + .type = EV_REL, + .code = REL_Y, + .decoded_size = 1U << ARRAY_SIZE(trout_5_y_axis_gpios), + .map = trout_axis_map, + .gpio = trout_5_y_axis_gpios, + .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION /*| GPIOEAF_PRINT_RAW | GPIOEAF_PRINT_EVENT */ + } +}; + +static struct gpio_event_direct_entry trout_nav_buttons[] = { + { TROUT_GPIO_NAVI_ACT_N, BTN_MOUSE } +}; + +static struct gpio_event_input_info trout_nav_button_info = { + .info.func = gpio_event_input_func, + .flags = 0, + .type = EV_KEY, + .keymap = trout_nav_buttons, + .keymap_size = ARRAY_SIZE(trout_nav_buttons) +}; + +static struct gpio_event_info *trout_nav_info[] = { + &trout_x_axis.info.info, + &trout_y_axis.info.info, + &trout_nav_button_info.info +}; + +static struct gpio_event_platform_data trout_nav_data = { + .name = "trout-nav", + .info = trout_nav_info, + .info_count = ARRAY_SIZE(trout_nav_info), + .power = trout_nav_power, +}; + +static struct platform_device trout_nav_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = 2, + .dev = { + .platform_data = &trout_nav_data, + }, +}; + +static int trout_ts_power(int on) +{ + int tp_ls_gpio = system_rev < 5 ? TROUT_4_TP_LS_EN : TROUT_5_TP_LS_EN; + if (on) { + gpio_set_value(TROUT_GPIO_TP_I2C_PULL, 1); + gpio_set_value(TROUT_GPIO_TP_EN, 1); + /* touchscreen must be powered before we enable i2c pullup */ + msleep(2); + /* enable touch panel level shift */ + gpio_set_value(tp_ls_gpio, 1); + msleep(2); + } + else { + gpio_set_value(tp_ls_gpio, 0); + gpio_set_value(TROUT_GPIO_TP_EN, 0); + gpio_set_value(TROUT_GPIO_TP_I2C_PULL, 0); + } + return 0; +} + +static struct synaptics_i2c_rmi_platform_data trout_ts_data[] = { + { + .version = 0x010c, + .power = trout_ts_power, + .flags = SYNAPTICS_FLIP_Y | SYNAPTICS_SNAP_TO_INACTIVE_EDGE, + .inactive_left = -100 * 0x10000 / 4334, + .inactive_right = -100 * 0x10000 / 4334, + .inactive_top = -40 * 0x10000 / 6696, + .inactive_bottom = -40 * 0x10000 / 6696, + .snap_left_on = 300 * 0x10000 / 4334, + .snap_left_off = 310 * 0x10000 / 4334, + .snap_right_on = 300 * 0x10000 / 4334, + .snap_right_off = 310 * 0x10000 / 4334, + .snap_top_on = 100 * 0x10000 / 6696, + .snap_top_off = 110 * 0x10000 / 6696, + .snap_bottom_on = 100 * 0x10000 / 6696, + .snap_bottom_off = 110 * 0x10000 / 6696, + }, + { + .flags = SYNAPTICS_FLIP_Y | SYNAPTICS_SNAP_TO_INACTIVE_EDGE, + .inactive_left = ((4674 - 4334) / 2 + 200) * 0x10000 / 4334, + .inactive_right = ((4674 - 4334) / 2 + 200) * 0x10000 / 4334, + .inactive_top = ((6946 - 6696) / 2) * 0x10000 / 6696, + .inactive_bottom = ((6946 - 6696) / 2) * 0x10000 / 6696, + } +}; + +static struct akm8976_platform_data compass_platform_data = { + .reset = TROUT_GPIO_COMPASS_RST_N, + .clk_on = TROUT_GPIO_COMPASS_32K_EN, + .intr = TROUT_GPIO_COMPASS_IRQ, +}; + +static struct i2c_board_info i2c_devices[] = { + { + I2C_BOARD_INFO(SYNAPTICS_I2C_RMI_NAME, 0x20), + .platform_data = trout_ts_data, + .irq = TROUT_GPIO_TO_INT(TROUT_GPIO_TP_ATT_N) + }, + { + I2C_BOARD_INFO("akm8976", 0x1C), + .platform_data = &compass_platform_data, + .irq = TROUT_GPIO_TO_INT(TROUT_GPIO_COMPASS_IRQ), + }, + { + I2C_BOARD_INFO("pca963x", 0x62), + }, +}; + +static struct android_pmem_platform_data android_pmem_pdata = { + .name = "pmem", + .start = MSM_PMEM_MDP_BASE, + .size = MSM_PMEM_MDP_SIZE, + .no_allocator = 1, + .cached = 1, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .start = MSM_PMEM_ADSP_BASE, + .size = MSM_PMEM_ADSP_SIZE, + .no_allocator = 0, + .cached = 0, +}; + +static struct android_pmem_platform_data android_pmem_camera_pdata = { + .name = "pmem_camera", + .start = MSM_PMEM_CAMERA_BASE, + .size = MSM_PMEM_CAMERA_SIZE, + .no_allocator = 0, + .cached = 0, +}; + +static struct android_pmem_platform_data android_pmem_gpu0_pdata = { + .name = "pmem_gpu0", + .start = MSM_PMEM_GPU0_BASE, + .size = MSM_PMEM_GPU0_SIZE, + .no_allocator = 1, + .cached = 0, + .buffered = 1, +}; + +static struct android_pmem_platform_data android_pmem_gpu1_pdata = { + .name = "pmem_gpu1", + .start = MSM_PMEM_GPU1_BASE, + .size = MSM_PMEM_GPU1_SIZE, + .no_allocator = 1, + .cached = 0, + .buffered = 1, +}; + +static struct platform_device android_pmem_device = { + .name = "android_pmem", + .id = 0, + .dev = { .platform_data = &android_pmem_pdata }, +}; + +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 1, + .dev = { .platform_data = &android_pmem_adsp_pdata }, +}; + +static struct platform_device android_pmem_gpu0_device = { + .name = "android_pmem", + .id = 2, + .dev = { .platform_data = &android_pmem_gpu0_pdata }, +}; + +static struct platform_device android_pmem_gpu1_device = { + .name = "android_pmem", + .id = 3, + .dev = { .platform_data = &android_pmem_gpu1_pdata }, +}; + +static struct platform_device android_pmem_camera_device = { + .name = "android_pmem", + .id = 4, + .dev = { .platform_data = &android_pmem_camera_pdata }, +}; + +static struct timed_gpio timed_gpios[] = { + { + .name = "vibrator", + .gpio = TROUT_GPIO_HAPTIC_PWM, + .max_timeout = 15000, + }, + { + .name = "flash", + .gpio = TROUT_GPIO_FLASH_EN, + .max_timeout = 400, + }, +}; + +static struct timed_gpio_platform_data timed_gpio_data = { + .num_gpios = ARRAY_SIZE(timed_gpios), + .gpios = timed_gpios, +}; + +static struct platform_device android_timed_gpios = { + .name = "timed-gpio", + .id = -1, + .dev = { + .platform_data = &timed_gpio_data, + }, +}; + +static struct gpio_led android_led_list[] = { + { + .name = "spotlight", + .gpio = TROUT_GPIO_SPOTLIGHT_EN, + }, + { + .name = "keyboard-backlight", + .gpio = TROUT_GPIO_QTKEY_LED_EN, + }, + { + .name = "button-backlight", + .gpio = TROUT_GPIO_UI_LED_EN, + }, +}; + +static struct gpio_led_platform_data android_leds_data = { + .num_leds = ARRAY_SIZE(android_led_list), + .leds = android_led_list, +}; + +static struct platform_device android_leds = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &android_leds_data, + }, +}; + +static struct gpio_switch_platform_data sd_door_switch_data = { + .name = "sd-door", + .gpio = TROUT_GPIO_SD_DOOR_N, + .state_on = "open", + .state_off = "closed", +}; + +static struct platform_device sd_door_switch = { + .name = "switch-gpio", + .id = -1, + .dev = { + .platform_data = &sd_door_switch_data, + }, +}; + +#ifdef CONFIG_USB_FUNCTION + +/* adjust eye diagram, disable vbusvalid interrupts */ +static int trout_phy_init_seq[] = { 0x40, 0x31, 0x1D, 0x0D, 0x1D, 0x10, -1 }; + +static void trout_phy_reset(void) +{ + gpio_set_value(TROUT_GPIO_USB_PHY_RST_N, 0); + mdelay(10); + gpio_set_value(TROUT_GPIO_USB_PHY_RST_N, 1); + mdelay(10); +} + +static char *trout_usb_functions[] = { +#if defined(CONFIG_USB_FUNCTION_MASS_STORAGE) || defined(CONFIG_USB_FUNCTION_UMS) + "usb_mass_storage", +#endif +#ifdef CONFIG_USB_FUNCTION_ADB + "adb", +#endif +}; + +static struct msm_hsusb_product trout_usb_products[] = { + { + .product_id = 0x0c01, + .functions = 0x00000001, /* "usb_mass_storage" only */ + }, + { + .product_id = 0x0c02, + .functions = 0x00000003, /* "usb_mass_storage" and "adb" */ + }, +}; + +static struct msm_hsusb_platform_data msm_hsusb_pdata = { + .phy_reset = trout_phy_reset, + .phy_init_seq = trout_phy_init_seq, + .vendor_id = 0x0bb4, + .product_id = 0x0c02, + .version = 0x0100, + .product_name = "Android Phone", + .manufacturer_name = "HTC", + + .functions = trout_usb_functions, + .num_functions = ARRAY_SIZE(trout_usb_functions), + .products = trout_usb_products, + .num_products = ARRAY_SIZE(trout_usb_products), +}; + +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .buf_size = 16384, + .vendor = "HTC ", + .product = "Android Phone ", + .release = 0x0100, +}; + +static struct platform_device usb_mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &mass_storage_pdata, + }, +}; +#endif + +static struct resource trout_ram_console_resource[] = { + { + .start = MSM_RAM_CONSOLE_BASE, + .end = MSM_RAM_CONSOLE_BASE + MSM_RAM_CONSOLE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device trout_ram_console_device = { + .name = "ram_console", + .id = -1, + .num_resources = ARRAY_SIZE(trout_ram_console_resource), + .resource = trout_ram_console_resource, +}; extern int trout_init_mmc(unsigned int); static struct platform_device *devices[] __initdata = { - &msm_device_uart3, &msm_device_smd, &msm_device_nand, - &msm_device_hsusb, &msm_device_i2c, + &msm_device_uart1, +#if !defined(CONFIG_MSM_SERIAL_DEBUGGER) && !defined(CONFIG_TROUT_H2W) + &msm_device_uart3, +#endif +#ifdef CONFIG_USB_FUNCTION + &msm_device_hsusb, + &usb_mass_storage_device, +#endif + &trout_nav_device, + &android_leds, + &sd_door_switch, + &android_timed_gpios, + &android_pmem_device, + &android_pmem_adsp_device, + &android_pmem_gpu0_device, + &android_pmem_gpu1_device, + &android_pmem_camera_device, + &trout_ram_console_device, }; extern struct sys_timer msm_timer; static void __init trout_init_irq(void) { + printk("trout_init_irq()\n"); msm_init_irq(); } -static void __init trout_fixup(struct machine_desc *desc, struct tag *tags, - char **cmdline, struct meminfo *mi) +static uint cpld_iset; +static uint cpld_charger_en; +static uint cpld_usb_h2w_sw; +static uint opt_disable_uart3; + +module_param_named(iset, cpld_iset, uint, 0); +module_param_named(charger_en, cpld_charger_en, uint, 0); +module_param_named(usb_h2w_sw, cpld_usb_h2w_sw, uint, 0); +module_param_named(disable_uart3, opt_disable_uart3, uint, 0); + +static int trout_bluetooth_power_on; + +static void bluetooth_set_power(int on) { - mi->nr_banks = 1; - mi->bank[0].start = PHYS_OFFSET; - mi->bank[0].size = (101*1024*1024); + if (on) { + gpio_set_value(TROUT_GPIO_BT_32K_EN, 1); + udelay(10); + gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_HIGH); + } else { + gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_LOW); + gpio_set_value(TROUT_GPIO_BT_32K_EN, 0); + } +} + +static int bluetooth_set_power_on(const char *val, struct kernel_param *kp) +{ + int ret; + ret = param_set_bool(val, kp); + if (!ret) + bluetooth_set_power(trout_bluetooth_power_on); + return ret; } +module_param_call(bluetooth_power_on, bluetooth_set_power_on, param_get_bool, + &trout_bluetooth_power_on, S_IWUSR | S_IRUGO); + +static int __init trout_serialno_setup(char *str) +{ +#ifdef CONFIG_USB_FUNCTION + msm_hsusb_pdata.serial_number = str; +#endif + return 1; +} + +__setup("androidboot.serialno=", trout_serialno_setup); + +static void trout_reset(void) +{ + gpio_set_value(TROUT_GPIO_PS_HOLD, 0); +} + +static uint32_t gpio_table[] = { + /* BLUETOOTH */ + PCOM_GPIO_CFG(43, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RTS */ + PCOM_GPIO_CFG(44, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* CTS */ + PCOM_GPIO_CFG(45, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RX */ + PCOM_GPIO_CFG(46, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* TX */ +}; + +static void config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for(n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +static void __init config_gpios(void) +{ + config_gpio_table(gpio_table, ARRAY_SIZE(gpio_table)); +} + +void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq); + +static struct msm_acpu_clock_platform_data trout_clock_data = { + .acpu_switch_time_us = 20, + .max_speed_delta_khz = 256000, + .vdd_switch_time_us = 62, + .power_collapse_khz = 19200000, + .wait_for_irq_khz = 128000000, +}; + static void __init trout_init(void) { - int rc; + printk("trout_init() revision=%d\n", system_rev); - platform_add_devices(devices, ARRAY_SIZE(devices)); + /* + * Setup common MSM GPIOS + */ + config_gpios(); + + msm_hw_reset_hook = trout_reset; + + msm_acpu_clock_init(&trout_clock_data); -#ifdef CONFIG_MMC - rc = trout_init_mmc(system_rev); - if (rc) - printk(KERN_CRIT "%s: MMC init failure (%d)\n", __func__, rc); + /* adjust GPIOs based on bootloader request */ + printk("trout_init: cpld_usb_hw2_sw = %d\n", cpld_usb_h2w_sw); + gpio_set_value(TROUT_GPIO_USB_H2W_SW, cpld_usb_h2w_sw); + +#if defined(CONFIG_MSM_SERIAL_DEBUGGER) + if (!opt_disable_uart3) + msm_serial_debug_init(MSM_UART3_PHYS, INT_UART3, + &msm_device_uart3.dev, 1); +#endif + + /* gpio_configure(108, IRQF_TRIGGER_LOW); */ + +#if defined(CONFIG_LL_DEBUG_UART1) + /* H2W pins <-> UART1 */ + gpio_set_value(TROUT_GPIO_H2W_SEL0, 1); + gpio_set_value(TROUT_GPIO_H2W_SEL1, 0); +#else + /* H2W pins <-> UART3, Bluetooth <-> UART1 */ + gpio_set_value(TROUT_GPIO_H2W_SEL0, 0); + gpio_set_value(TROUT_GPIO_H2W_SEL1, 1); #endif + /* Init bluetooth clock and shutdown pin */ + bluetooth_set_power(trout_bluetooth_power_on); + + /* put the AF VCM in powerdown mode to avoid noise */ + gpio_set_value(TROUT_GPIO_VCM_PWDN, 1); + mdelay(100); + + if (system_rev < 5) { + trout_x_axis.info.gpio = trout_4_x_axis_gpios; + trout_y_axis.info.gpio = trout_4_y_axis_gpios; + } + + msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; + + platform_add_devices(devices, ARRAY_SIZE(devices)); + i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); + + /* SD card door should wake the device */ + set_irq_wake(TROUT_GPIO_TO_INT(TROUT_GPIO_SD_DOOR_N), 1); } static struct map_desc trout_io_desc[] __initdata = { @@ -78,26 +654,34 @@ static struct map_desc trout_io_desc[] __initdata = { } }; +static void __init trout_fixup(struct machine_desc *desc, struct tag *tags, + char **cmdline, struct meminfo *mi) +{ + mi->nr_banks=1; + mi->bank[0].start = PHYS_OFFSET; + mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); + mi->bank[0].size = (101*1024*1024); +} + static void __init trout_map_io(void) { + printk("trout_init_map_io()\n"); msm_map_common_io(); iotable_init(trout_io_desc, ARRAY_SIZE(trout_io_desc)); - -#ifdef CONFIG_MSM_DEBUG_UART3 - /* route UART3 to the "H2W" extended usb connector */ - writeb(0x80, TROUT_CPLD_BASE + 0x00); -#endif - msm_clock_init(msm_clocks_7x01a, msm_num_clocks_7x01a); } -MACHINE_START(TROUT, "HTC Dream") -#ifdef CONFIG_MSM_DEBUG_UART -#endif - .boot_params = 0x10000100, - .fixup = trout_fixup, - .map_io = trout_map_io, - .init_irq = trout_init_irq, - .init_machine = trout_init, - .timer = &msm_timer, +MACHINE_START(TROUT, "trout") +/* Maintainer: Brian Swetland */ + +/* this is broken... can we just opt out of specifying something here? */ + .phys_io = 0x80000000, + .io_pg_offst = ((0x80000000) >> 18) & 0xfffc, + + .boot_params = 0x10000100, + .fixup = trout_fixup, + .map_io = trout_map_io, + .init_irq = trout_init_irq, + .init_machine = trout_init, + .timer = &msm_timer, MACHINE_END From 28183970573f2ff53ecf9ec73485475290840bf5 Mon Sep 17 00:00:00 2001 From: Hou-Kun Chen Date: Mon, 29 Sep 2008 18:54:46 -0700 Subject: [PATCH 0192/2556] amk8976: Adding support for AKM8976 compass driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rebecca Schultz i2c: akm8976: Add proper probe function + provide hardware information Signed-off-by: Farmer Tseng Signed-off-by: San Mehat akm8976: Fix for 2.6.32 Signed-off-by: Arve Hjønnevåg --- drivers/i2c/chips/Kconfig | 15 + drivers/i2c/chips/Makefile | 18 + drivers/i2c/chips/akm8976.c | 1132 +++++++++++++++++++++++++++++++++++ include/linux/akm8976.h | 90 +++ 4 files changed, 1255 insertions(+) create mode 100644 drivers/i2c/chips/Kconfig create mode 100644 drivers/i2c/chips/Makefile create mode 100644 drivers/i2c/chips/akm8976.c create mode 100644 include/linux/akm8976.h diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig new file mode 100644 index 0000000000000..26d4725d8b01a --- /dev/null +++ b/drivers/i2c/chips/Kconfig @@ -0,0 +1,15 @@ +# +# Miscellaneous I2C chip drivers configuration +# +# *** DEPRECATED! Do not add new entries! See Makefile *** +# + +menu "Miscellaneous I2C Chip support" + +config SENSORS_AKM8976 + tristate "AKM8976 Compass Driver" + depends on I2C + help + AKM8976 Compass Driver implemented by HTC. + +endmenu diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile new file mode 100644 index 0000000000000..6b5a4ff97c6c1 --- /dev/null +++ b/drivers/i2c/chips/Makefile @@ -0,0 +1,18 @@ +# +# Makefile for miscellaneous I2C chip drivers. +# +# Do not add new drivers to this directory! It is DEPRECATED. +# +# Device drivers are better grouped according to the functionality they +# implement rather than to the bus they are connected to. In particular: +# * Hardware monitoring chip drivers go to drivers/hwmon +# * RTC chip drivers go to drivers/rtc +# * I/O expander drivers go to drivers/gpio +# + +obj-$(CONFIG_SENSORS_AKM8976) += akm8976.o + +ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) +EXTRA_CFLAGS += -DDEBUG +endif + diff --git a/drivers/i2c/chips/akm8976.c b/drivers/i2c/chips/akm8976.c new file mode 100644 index 0000000000000..ee99ae83a1c29 --- /dev/null +++ b/drivers/i2c/chips/akm8976.c @@ -0,0 +1,1132 @@ +/* drivers/i2c/chips/akm8976.c - akm8976 compass driver + * + * Copyright (C) 2007-2008 HTC Corporation. + * Author: Hou-Kun Chen + * + * 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 + +#define DEBUG 0 +#define MAX_FAILURE_COUNT 10 + +static struct i2c_client *this_client; + +struct akm8976_data { + struct input_dev *input_dev; + struct work_struct work; +}; + +/* Addresses to scan -- protected by sense_data_mutex */ +static char sense_data[RBUFF_SIZE + 1]; +static struct mutex sense_data_mutex; + +static DECLARE_WAIT_QUEUE_HEAD(data_ready_wq); +static DECLARE_WAIT_QUEUE_HEAD(open_wq); + +static char cspec_num; +static atomic_t cspec_frq; + +static atomic_t data_ready; +static atomic_t open_count; +static atomic_t open_flag; +static atomic_t reserve_open_flag; + +static atomic_t m_flag; +static atomic_t a_flag; +static atomic_t t_flag; +static atomic_t mv_flag; + +static int pffd_mode = 0; +static int failure_count = 0; + +static short akmd_delay = 0; + +static atomic_t suspend_flag = ATOMIC_INIT(0); + +static struct akm8976_platform_data *pdata; +static int revision = -1; +/* AKM HW info */ +static ssize_t gsensor_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + + sprintf(buf, "AK8976A_%#x\n", revision); + ret = strlen(buf) + 1; + + return ret; +} + +static DEVICE_ATTR(vendor, 0444, gsensor_vendor_show, NULL); + +static struct kobject *android_gsensor_kobj; + +static int gsensor_sysfs_init(void) +{ + int ret ; + + android_gsensor_kobj = kobject_create_and_add("android_gsensor", NULL); + if (android_gsensor_kobj == NULL) { + printk(KERN_ERR + "AKM8976 gsensor_sysfs_init:"\ + "subsystem_register failed\n"); + ret = -ENOMEM; + goto err; + } + + ret = sysfs_create_file(android_gsensor_kobj, &dev_attr_vendor.attr); + if (ret) { + printk(KERN_ERR + "AKM8976 gsensor_sysfs_init:"\ + "sysfs_create_group failed\n"); + goto err4; + } + + return 0 ; +err4: + kobject_del(android_gsensor_kobj); +err: + return ret ; +} + +/* following are the sysfs callback functions */ + +#define config_ctrl_reg(name,address) \ +static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + return sprintf(buf, "%u\n", i2c_smbus_read_byte_data(client,address)); \ +} \ +static ssize_t name##_store(struct device *dev, struct device_attribute *attr, \ + const char *buf,size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + unsigned long val = simple_strtoul(buf, NULL, 10); \ + if (val > 0xff) \ + return -EINVAL; \ + i2c_smbus_write_byte_data(client,address, val); \ + return count; \ +} \ +static DEVICE_ATTR(name, S_IWUSR | S_IRUGO, name##_show, name##_store) + +config_ctrl_reg(ms1, AKECS_REG_MS1); +config_ctrl_reg(ms2, AKECS_REG_MS2); +config_ctrl_reg(ms3, AKECS_REG_MS3); + +static int AKI2C_RxData(char *rxData, int length) +{ + struct i2c_msg msgs[] = { + { + .addr = this_client->addr, + .flags = 0, + .len = 1, + .buf = rxData, + }, + { + .addr = this_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = rxData, + }, + }; + + if (i2c_transfer(this_client->adapter, msgs, 2) < 0) { + printk(KERN_ERR "AKM8976 AKI2C_RxData: transfer error\n"); + return -EIO; + } else + return 0; +} + +static int AKI2C_TxData(char *txData, int length) +{ + + struct i2c_msg msg[] = { + { + .addr = this_client->addr, + .flags = 0, + .len = length, + .buf = txData, + }, + }; + + if (i2c_transfer(this_client->adapter, msg, 1) < 0) { + printk(KERN_ERR "AKM8976 AKI2C_TxData: transfer error\n"); + return -EIO; + } else + return 0; +} + +static int AKECS_Init(void) +{ + char buffer[4]; + + cspec_num = CSPEC_SEQ_NUM; + atomic_set(&cspec_frq, CSPEC_SFRQ_32); + + /* Prepare data */ + buffer[0] = AKECS_REG_MS2; + buffer[1] = ((CSPEC_AINT << 7) | + (cspec_num << 5) | + (atomic_read(&cspec_frq) << 4) | + (CSPEC_MCS << 1) | (CSPEC_MKS)); + buffer[2] = (CSPEC_INTEN << 2); + + return AKI2C_TxData(buffer, 3); +} + +static void AKECS_Reset(void) +{ + gpio_set_value(pdata->reset, 0); + udelay(120); + gpio_set_value(pdata->reset, 1); +} + +static int AKECS_StartMeasure(void) +{ + char buffer[2]; + int ret; + + buffer[0] = AKECS_REG_MS2; + buffer[1] = ((CSPEC_AINT << 7) | + (cspec_num << 5) | + (atomic_read(&cspec_frq) << 4) | + (CSPEC_MCS << 1) | (CSPEC_MKS)); + + /* Set data */ + ret = AKI2C_TxData(buffer, 2); + if (ret < 0) + return ret; + + /* Set measure mode */ + buffer[0] = AKECS_REG_MS1; + buffer[1] = AKECS_MODE_MEASURE; + + /* Set data */ + return AKI2C_TxData(buffer, 2); +} + +static int AKECS_StartPFFD(void) +{ + char buffer[2]; + int ret; + + /* Set PFFD mode */ + buffer[0] = AKECS_REG_MS1; + buffer[1] = AKECS_MODE_PFFD; + /* Set data */ + ret = AKI2C_TxData(buffer, 2); + if (ret < 0) + return ret; + + ret = gpio_direction_output(pdata->clk_on, 1); + if (ret < 0) + return ret; + + pffd_mode = 1; + return ret; +} + +static int AKECS_PowerDown(void) +{ + char buffer[2]; + int ret; + + /* Set powerdown mode */ + buffer[0] = AKECS_REG_MS1; + buffer[1] = AKECS_MODE_POWERDOWN; + /* Set data */ + ret = AKI2C_TxData(buffer, 2); + if (ret < 0) + return ret; + + /* Dummy read for clearing INT pin */ + buffer[0] = AKECS_REG_TMPS; + /* Read data */ + ret = AKI2C_RxData(buffer, 1); + if (ret < 0) + return ret; + + if (pffd_mode == 1) { + pffd_mode = 0; + ret = gpio_direction_output(pdata->clk_on, 0); + } + return ret; +} + +static int AKECS_StartE2PRead(void) +{ + char buffer[2]; + + /* Set measure mode */ + buffer[0] = AKECS_REG_MS1; + buffer[1] = AKECS_MODE_E2P_READ; + /* Set data */ + return AKI2C_TxData(buffer, 2); +} + +static int AKECS_GetData(void) +{ + char buffer[RBUFF_SIZE + 1]; + int ret; + + memset(buffer, 0, RBUFF_SIZE + 1); + buffer[0] = AKECS_REG_ST; + ret = AKI2C_RxData(buffer, 32); + if (ret < 0) + return ret; + + mutex_lock(&sense_data_mutex); + memcpy(sense_data, buffer, sizeof(buffer)); + atomic_set(&data_ready, 1); + wake_up(&data_ready_wq); + mutex_unlock(&sense_data_mutex); + + return 0; +} + +static int AKECS_SetMode(char mode) +{ + int ret, status; + char buffer[1]; + + if (mode == AKECS_MODE_MEASURE_SNG) { + /* Check INT pin before mode setting */ + status = gpio_get_value(pdata->intr); + if (status) { + printk(KERN_INFO + "AKM8976 AKECS_SetMode:"\ + "dummy read to reset INT pin \n"); + buffer[0] = AKECS_REG_TMPS; + ret = AKI2C_RxData(buffer, 1); + if (ret < 0) + return ret; + status = gpio_get_value(pdata->intr); + printk(KERN_INFO + "AKM8976 AKECS_SetMode:"\ + "after dummy read, status = %d \n", + status); + } + } + + switch (mode) { + case AKECS_MODE_MEASURE_SNG: + cspec_num = CSPEC_SNG_NUM; + ret = AKECS_StartMeasure(); + break; + case AKECS_MODE_MEASURE_SEQ: + cspec_num = CSPEC_SEQ_NUM; + ret = AKECS_StartMeasure(); + break; + case AKECS_MODE_PFFD: + ret = AKECS_StartPFFD(); + break; + case AKECS_MODE_E2P_READ: + ret = AKECS_StartE2PRead(); + break; + case AKECS_MODE_POWERDOWN: + ret = AKECS_PowerDown(); + break; + default: + return -EINVAL; + } + + /* wait at least 300us after changing mode */ + msleep(1); + return ret; +} + +static int AKECS_TransRBuff(char *rbuf, int size) +{ + wait_event_interruptible_timeout(data_ready_wq, + atomic_read(&data_ready), 1000); + + if (!atomic_read(&data_ready)) { + if (!atomic_read(&suspend_flag)) { + printk(KERN_ERR + "AKM8976 AKECS_TransRBUFF: Data not ready\n"); + failure_count++; + if (failure_count >= MAX_FAILURE_COUNT) { + printk(KERN_ERR + "AKM8976 AKECS_TransRBUFF:"\ + "successive %d failure.\n", + failure_count); + atomic_set(&open_flag, -1); + wake_up(&open_wq); + failure_count = 0; + } + } + return -1; + } + + if ((sense_data[0] & 0x02) == 0x02) { + printk(KERN_ERR "AKM8976 AKECS_TransRBUFF: Data error\n"); + return -1; + } + + mutex_lock(&sense_data_mutex); + memcpy(&rbuf[1], &sense_data[1], size); + atomic_set(&data_ready, 0); + mutex_unlock(&sense_data_mutex); + + + failure_count = 0; + return 0; +} + +static int AKECS_Set_PERST(void) +{ + char buffer[2]; + + buffer[0] = AKECS_REG_MS3; + buffer[1] = ((CSPEC_INTEN << 2) | 0x01); + + /* Set data */ + return AKI2C_TxData(buffer, 2); +} + +static int AKECS_Set_G0RST(void) +{ + char buffer[2]; + + buffer[0] = AKECS_REG_MS3; + buffer[1] = ((CSPEC_INTEN << 2) | 0x02); + + /* Set data */ + return AKI2C_TxData(buffer, 2); +} + +static void AKECS_Report_Value(short *rbuf) +{ + struct akm8976_data *data = i2c_get_clientdata(this_client); +#if DEBUG + printk(KERN_INFO + "AKECS_Report_Value: yaw = %d, pitch = %d, roll = %d\n", + rbuf[0], rbuf[1], rbuf[2]); + printk(KERN_INFO + " tmp = %d, m_stat= %d, g_stat=%d\n", + rbuf[3], rbuf[4], rbuf[5]); + printk(KERN_INFO + " G_Sensor: x = %d LSB, y = %d LSB, z = %d LSB\n", + rbuf[6], rbuf[7], rbuf[8]); +#endif + /* Report magnetic sensor information */ + if (atomic_read(&m_flag)) { + input_report_abs(data->input_dev, ABS_RX, rbuf[0]); + input_report_abs(data->input_dev, ABS_RY, rbuf[1]); + input_report_abs(data->input_dev, ABS_RZ, rbuf[2]); + input_report_abs(data->input_dev, ABS_RUDDER, rbuf[4]); + } + + /* Report acceleration sensor information */ + if (atomic_read(&a_flag)) { + input_report_abs(data->input_dev, ABS_X, rbuf[6]); + input_report_abs(data->input_dev, ABS_Y, rbuf[7]); + input_report_abs(data->input_dev, ABS_Z, rbuf[8]); + input_report_abs(data->input_dev, ABS_WHEEL, rbuf[5]); + } + + /* Report temperature information */ + if (atomic_read(&t_flag)) { + input_report_abs(data->input_dev, ABS_THROTTLE, rbuf[3]); + } + + if (atomic_read(&mv_flag)) { + input_report_abs(data->input_dev, ABS_HAT0X, rbuf[9]); + input_report_abs(data->input_dev, ABS_HAT0Y, rbuf[10]); + input_report_abs(data->input_dev, ABS_BRAKE, rbuf[11]); + } + + input_sync(data->input_dev); +} + +static void AKECS_Report_StepCount(short count) +{ + struct akm8976_data *data = i2c_get_clientdata(this_client); +#if DEBUG + printk(KERN_INFO"AKECS_Report_StepCount: %d \n", count); +#endif + + /* Report pedometer information */ + input_report_abs(data->input_dev, ABS_GAS, count); + input_sync(data->input_dev); +} + +static int AKECS_GetOpenStatus(void) +{ + wait_event_interruptible(open_wq, (atomic_read(&open_flag) != 0)); + return atomic_read(&open_flag); +} + +static int AKECS_GetCloseStatus(void) +{ + wait_event_interruptible(open_wq, (atomic_read(&open_flag) <= 0)); + return atomic_read(&open_flag); +} + +static void AKECS_CloseDone(void) +{ + atomic_set(&m_flag, 1); + atomic_set(&a_flag, 1); + atomic_set(&t_flag, 1); + atomic_set(&mv_flag, 1); +} + +static int akm_aot_open(struct inode *inode, struct file *file) +{ + int ret = -1; + if (atomic_cmpxchg(&open_count, 0, 1) == 0) { + if (atomic_cmpxchg(&open_flag, 0, 1) == 0) { + atomic_set(&reserve_open_flag, 1); + wake_up(&open_wq); + ret = 0; + } + } + return ret; +} + +static int akm_aot_release(struct inode *inode, struct file *file) +{ + atomic_set(&reserve_open_flag, 0); + atomic_set(&open_flag, 0); + atomic_set(&open_count, 0); + wake_up(&open_wq); + return 0; +} + +static int +akm_aot_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + short flag; + + switch (cmd) { + case ECS_IOCTL_APP_SET_MFLAG: + case ECS_IOCTL_APP_SET_AFLAG: + case ECS_IOCTL_APP_SET_TFLAG: + case ECS_IOCTL_APP_SET_MVFLAG: + if (copy_from_user(&flag, argp, sizeof(flag))) + return -EFAULT; + if (flag < 0 || flag > 1) + return -EINVAL; + break; + case ECS_IOCTL_APP_SET_DELAY: + if (copy_from_user(&flag, argp, sizeof(flag))) + return -EFAULT; + break; + default: + break; + } + + switch (cmd) { + case ECS_IOCTL_APP_SET_MFLAG: + atomic_set(&m_flag, flag); + break; + case ECS_IOCTL_APP_GET_MFLAG: + flag = atomic_read(&m_flag); + break; + case ECS_IOCTL_APP_SET_AFLAG: + atomic_set(&a_flag, flag); + break; + case ECS_IOCTL_APP_GET_AFLAG: + flag = atomic_read(&a_flag); + break; + case ECS_IOCTL_APP_SET_TFLAG: + atomic_set(&t_flag, flag); + break; + case ECS_IOCTL_APP_GET_TFLAG: + flag = atomic_read(&t_flag); + break; + case ECS_IOCTL_APP_SET_MVFLAG: + atomic_set(&mv_flag, flag); + break; + case ECS_IOCTL_APP_GET_MVFLAG: + flag = atomic_read(&mv_flag); + break; + case ECS_IOCTL_APP_SET_DELAY: + akmd_delay = flag; + break; + case ECS_IOCTL_APP_GET_DELAY: + flag = akmd_delay; + break; + default: + return -ENOTTY; + } + + switch (cmd) { + case ECS_IOCTL_APP_GET_MFLAG: + case ECS_IOCTL_APP_GET_AFLAG: + case ECS_IOCTL_APP_GET_TFLAG: + case ECS_IOCTL_APP_GET_MVFLAG: + case ECS_IOCTL_APP_GET_DELAY: + if (copy_to_user(argp, &flag, sizeof(flag))) + return -EFAULT; + break; + default: + break; + } + + return 0; +} + +static int akm_pffd_open(struct inode *inode, struct file *file) +{ + int ret = -1; + if (atomic_cmpxchg(&open_count, 0, 1) == 0) { + if (atomic_cmpxchg(&open_flag, 0, 2) == 0) { + atomic_set(&reserve_open_flag, 2); + wake_up(&open_wq); + ret = 0; + } + } + return ret; +} + +static int akm_pffd_release(struct inode *inode, struct file *file) +{ + atomic_set(&reserve_open_flag, 0); + atomic_set(&open_flag, 0); + atomic_set(&open_count, 0); + wake_up(&open_wq); + return 0; +} + +static int +akm_pffd_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + short flag; + int ret; + + switch (cmd) { + case ECS_IOCTL_APP_SET_DELAY: + if (copy_from_user(&flag, argp, sizeof(flag))) + return -EFAULT; + break; + default: + break; + } + + switch (cmd) { + case ECS_IOCTL_APP_RESET_PEDOMETER: + ret = AKECS_Set_PERST(); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_APP_SET_DELAY: + akmd_delay = flag; + break; + case ECS_IOCTL_APP_GET_DELAY: + flag = akmd_delay; + break; + default: + return -ENOTTY; + } + + switch (cmd) { + case ECS_IOCTL_APP_GET_DELAY: + if (copy_to_user(argp, &flag, sizeof(flag))) + return -EFAULT; + break; + default: + break; + } + + return 0; +} + +static int akmd_open(struct inode *inode, struct file *file) +{ + return nonseekable_open(inode, file); +} + +static int akmd_release(struct inode *inode, struct file *file) +{ + AKECS_CloseDone(); + return 0; +} + +static int +akmd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + + void __user *argp = (void __user *)arg; + + char msg[RBUFF_SIZE + 1], rwbuf[5], numfrq[2]; + int ret = -1, status; + short mode, value[12], step_count, delay; + char *pbuffer = 0; + + switch (cmd) { + case ECS_IOCTL_READ: + case ECS_IOCTL_WRITE: + if (copy_from_user(&rwbuf, argp, sizeof(rwbuf))) + return -EFAULT; + break; + case ECS_IOCTL_SET_MODE: + if (copy_from_user(&mode, argp, sizeof(mode))) + return -EFAULT; + break; + case ECS_IOCTL_SET_YPR: + if (copy_from_user(&value, argp, sizeof(value))) + return -EFAULT; + break; + case ECS_IOCTL_SET_STEP_CNT: + if (copy_from_user(&step_count, argp, sizeof(step_count))) + return -EFAULT; + break; + default: + break; + } + + switch (cmd) { + case ECS_IOCTL_INIT: + ret = AKECS_Init(); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_RESET: + AKECS_Reset(); + break; + case ECS_IOCTL_READ: + if (rwbuf[0] < 1) + return -EINVAL; + ret = AKI2C_RxData(&rwbuf[1], rwbuf[0]); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_WRITE: + if (rwbuf[0] < 2) + return -EINVAL; + ret = AKI2C_TxData(&rwbuf[1], rwbuf[0]); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_SET_MODE: + ret = AKECS_SetMode((char)mode); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_GETDATA: + ret = AKECS_TransRBuff(msg, RBUFF_SIZE); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_GET_NUMFRQ: + numfrq[0] = cspec_num; + numfrq[1] = atomic_read(&cspec_frq); + break; + case ECS_IOCTL_SET_PERST: + ret = AKECS_Set_PERST(); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_SET_G0RST: + ret = AKECS_Set_G0RST(); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_SET_YPR: + AKECS_Report_Value(value); + break; + case ECS_IOCTL_GET_OPEN_STATUS: + status = AKECS_GetOpenStatus(); + break; + case ECS_IOCTL_GET_CLOSE_STATUS: + status = AKECS_GetCloseStatus(); + break; + case ECS_IOCTL_SET_STEP_CNT: + AKECS_Report_StepCount(step_count); + break; + case ECS_IOCTL_GET_CALI_DATA: + pbuffer = get_akm_cal_ram(); + break; + case ECS_IOCTL_GET_DELAY: + delay = akmd_delay; + break; + default: + return -ENOTTY; + } + + switch (cmd) { + case ECS_IOCTL_READ: + if (copy_to_user(argp, &rwbuf, sizeof(rwbuf))) + return -EFAULT; + break; + case ECS_IOCTL_GETDATA: + if (copy_to_user(argp, &msg, sizeof(msg))) + return -EFAULT; + break; + case ECS_IOCTL_GET_NUMFRQ: + if (copy_to_user(argp, &numfrq, sizeof(numfrq))) + return -EFAULT; + break; + case ECS_IOCTL_GET_OPEN_STATUS: + case ECS_IOCTL_GET_CLOSE_STATUS: + if (copy_to_user(argp, &status, sizeof(status))) + return -EFAULT; + break; + case ECS_IOCTL_GET_CALI_DATA: + if (copy_to_user(argp, pbuffer, MAX_CALI_SIZE)) + return -EFAULT; + break; + case ECS_IOCTL_GET_DELAY: + if (copy_to_user(argp, &delay, sizeof(delay))) + return -EFAULT; + break; + default: + break; + } + + return 0; +} + +static void akm_work_func(struct work_struct *work) +{ + if (AKECS_GetData() < 0) + printk(KERN_ERR "AKM8976 akm_work_func: Get data failed\n"); + enable_irq(this_client->irq); +} + +static irqreturn_t akm8976_interrupt(int irq, void *dev_id) +{ + struct akm8976_data *data = dev_id; + disable_irq_nosync(this_client->irq); + schedule_work(&data->work); + return IRQ_HANDLED; +} + +static int akm8976_init_client(struct i2c_client *client) +{ + struct akm8976_data *data; + int ret; + + data = i2c_get_clientdata(client); + + mutex_init(&sense_data_mutex); + + ret = request_irq(client->irq, akm8976_interrupt, IRQF_TRIGGER_HIGH, + "akm8976", data); + + if (ret < 0) { + printk(KERN_ERR "akm8976_init_client: request irq failed\n"); + goto err; + } + + pdata = client->dev.platform_data; + if (pdata == NULL) { + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (pdata == NULL) { + ret = -ENOMEM; + goto err_alloc_data_failed; + } else { + pdata->reset = ECS_RST; + pdata->clk_on = ECS_CLK_ON; + pdata->intr = ECS_INTR; + } + } + + ret = gpio_request(pdata->reset, "akm8976"); + if (ret < 0) { + printk(KERN_ERR + "akm8976_init_client: request reset gpio failed\n"); + goto err_free_irq; + } + ret = gpio_direction_output(pdata->reset, 1); + if (ret < 0) { + printk(KERN_ERR + "akm8976_init_client: request reset gpio failed\n"); + goto err_free_gpio; + } + + ret = gpio_request(pdata->clk_on, "akm8976"); + if (ret < 0) { + printk(KERN_ERR + "akm8976_init_client: request clock gpio failed\n"); + goto err_free_gpio; + } + + ret = gpio_direction_output(pdata->clk_on, 0); + if (ret < 0) { + printk(KERN_ERR + "akm8976_init_client: request clock gpio failed\n"); + goto err_free_gpio_2; + } + + init_waitqueue_head(&data_ready_wq); + init_waitqueue_head(&open_wq); + + /* As default, report all information */ + atomic_set(&m_flag, 1); + atomic_set(&a_flag, 1); + atomic_set(&t_flag, 1); + atomic_set(&mv_flag, 1); + + return 0; + +err_free_gpio_2: + gpio_free(pdata->clk_on); +err_free_gpio: + gpio_free(pdata->reset); +err_free_irq: + free_irq(client->irq, 0); +err_alloc_data_failed: +err: + return ret; +} + +static struct file_operations akmd_fops = { + .owner = THIS_MODULE, + .open = akmd_open, + .release = akmd_release, + .ioctl = akmd_ioctl, +}; + +static struct file_operations akm_aot_fops = { + .owner = THIS_MODULE, + .open = akm_aot_open, + .release = akm_aot_release, + .ioctl = akm_aot_ioctl, +}; + +static struct file_operations akm_pffd_fops = { + .owner = THIS_MODULE, + .open = akm_pffd_open, + .release = akm_pffd_release, + .ioctl = akm_pffd_ioctl, +}; + +static struct miscdevice akm_aot_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "akm8976_aot", + .fops = &akm_aot_fops, +}; + +static struct miscdevice akm_pffd_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "akm8976_pffd", + .fops = &akm_pffd_fops, +}; + +static struct miscdevice akmd_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "akm8976_daemon", + .fops = &akmd_fops, +}; + +static int akm8976_probe( + struct i2c_client *client, const struct i2c_device_id *id) +{ + struct akm8976_data *akm; + int err; + char rxData[2]; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + err = -ENODEV; + goto exit_check_functionality_failed; + } + + akm = kzalloc(sizeof(struct akm8976_data), GFP_KERNEL); + if (!akm) { + err = -ENOMEM; + goto exit_alloc_data_failed; + } + + INIT_WORK(&akm->work, akm_work_func); + i2c_set_clientdata(client, akm); + akm8976_init_client(client); + this_client = client; + + /* Set EEPROM access mode */ + err = AKECS_StartE2PRead(); + if (err < 0) + goto exit_input_dev_alloc_failed; + /* Read ETS from EEPROM */ + rxData[0] = 0x42; + err = AKI2C_RxData(rxData, 1); + if (err < 0) + goto exit_input_dev_alloc_failed; + revision = (0x03 & (rxData[0] >> 6)); + + /* Set Power down mode */ + err = AKECS_PowerDown(); + if (err < 0) + goto exit_input_dev_alloc_failed; + + akm->input_dev = input_allocate_device(); + + if (!akm->input_dev) { + err = -ENOMEM; + printk(KERN_ERR + "akm8976_probe: Failed to allocate input device\n"); + goto exit_input_dev_alloc_failed; + } + + set_bit(EV_ABS, akm->input_dev->evbit); + /* yaw */ + input_set_abs_params(akm->input_dev, ABS_RX, 0, 360, 0, 0); + /* pitch */ + input_set_abs_params(akm->input_dev, ABS_RY, -180, 180, 0, 0); + /* roll */ + input_set_abs_params(akm->input_dev, ABS_RZ, -90, 90, 0, 0); + /* x-axis acceleration */ + input_set_abs_params(akm->input_dev, ABS_X, -1872, 1872, 0, 0); + /* y-axis acceleration */ + input_set_abs_params(akm->input_dev, ABS_Y, -1872, 1872, 0, 0); + /* z-axis acceleration */ + input_set_abs_params(akm->input_dev, ABS_Z, -1872, 1872, 0, 0); + /* temparature */ + input_set_abs_params(akm->input_dev, ABS_THROTTLE, -30, 85, 0, 0); + /* status of magnetic sensor */ + input_set_abs_params(akm->input_dev, ABS_RUDDER, -32768, 3, 0, 0); + /* status of acceleration sensor */ + input_set_abs_params(akm->input_dev, ABS_WHEEL, -32768, 3, 0, 0); + /* step count */ + input_set_abs_params(akm->input_dev, ABS_GAS, 0, 65535, 0, 0); + /* x-axis of raw magnetic vector */ + input_set_abs_params(akm->input_dev, ABS_HAT0X, -2048, 2032, 0, 0); + /* y-axis of raw magnetic vector */ + input_set_abs_params(akm->input_dev, ABS_HAT0Y, -2048, 2032, 0, 0); + /* z-axis of raw magnetic vector */ + input_set_abs_params(akm->input_dev, ABS_BRAKE, -2048, 2032, 0, 0); + + akm->input_dev->name = "compass"; + + err = input_register_device(akm->input_dev); + + if (err) { + printk(KERN_ERR + "akm8976_probe: Unable to register input device: %s\n", + akm->input_dev->name); + goto exit_input_register_device_failed; + } + + err = misc_register(&akmd_device); + if (err) { + printk(KERN_ERR + "akm8976_probe: akmd_device register failed\n"); + goto exit_misc_device_register_failed; + } + + err = misc_register(&akm_aot_device); + if (err) { + printk(KERN_ERR + "akm8976_probe: akm_aot_device register failed\n"); + goto exit_misc_device_register_failed; + } + + err = misc_register(&akm_pffd_device); + if (err) { + printk(KERN_ERR + "akm8976_probe: akm_pffd_device register failed\n"); + goto exit_misc_device_register_failed; + } + + err = device_create_file(&client->dev, &dev_attr_ms1); + err = device_create_file(&client->dev, &dev_attr_ms2); + err = device_create_file(&client->dev, &dev_attr_ms3); + + gsensor_sysfs_init(); + + return 0; + +exit_misc_device_register_failed: +exit_input_register_device_failed: + input_free_device(akm->input_dev); +exit_input_dev_alloc_failed: + kfree(akm); +exit_alloc_data_failed: +exit_check_functionality_failed: + return err; +} + +static int akm8976_remove(struct i2c_client *client) +{ + struct akm8976_data *akm = i2c_get_clientdata(client); + free_irq(client->irq, akm); + input_unregister_device(akm->input_dev); + kfree(akm); + return 0; +} + +static int akm8976_suspend(struct i2c_client *client, pm_message_t mesg) +{ + atomic_set(&suspend_flag, 1); + if (atomic_read(&open_flag) == 2) + AKECS_SetMode(AKECS_MODE_POWERDOWN); + + atomic_set(&reserve_open_flag, atomic_read(&open_flag)); + atomic_set(&open_flag, 0); + wake_up(&open_wq); + disable_irq(this_client->irq); + return 0; +} + +static int akm8976_resume(struct i2c_client *client) +{ + enable_irq(this_client->irq); + if (atomic_read(&open_flag) == 2) + AKECS_SetMode(AKECS_MODE_PFFD); + atomic_set(&suspend_flag, 0); + atomic_set(&open_flag, atomic_read(&reserve_open_flag)); + wake_up(&open_wq); + return 0; +} + +static const struct i2c_device_id akm8976_id[] = { + { "akm8976", 0 }, + { } +}; + +static struct i2c_driver akm8976_driver = { + .probe = akm8976_probe, + .remove = akm8976_remove, + .suspend = akm8976_suspend, + .resume = akm8976_resume, + .id_table = akm8976_id, + .driver = { + .name = "akm8976", + }, +}; + +static int __init akm8976_init(void) +{ + printk(KERN_INFO "AKM8976A compass driver: init\n"); + return i2c_add_driver(&akm8976_driver); +} + +static void __exit akm8976_exit(void) +{ + i2c_del_driver(&akm8976_driver); +} + +module_init(akm8976_init); +module_exit(akm8976_exit); + +MODULE_AUTHOR("Hou-Kun Chen "); +MODULE_DESCRIPTION("AKM8976A compass driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/akm8976.h b/include/linux/akm8976.h new file mode 100644 index 0000000000000..8f6a2bbc8b94c --- /dev/null +++ b/include/linux/akm8976.h @@ -0,0 +1,90 @@ +/* + * Definitions for akm8976 compass chip. + */ +#ifndef AKM8976_H +#define AKM8976_H + +#include + +/* Compass device dependent definition */ +#define AKECS_MODE_MEASURE 0x00 /* Starts measurement. Please use AKECS_MODE_MEASURE_SNG */ + /* or AKECS_MODE_MEASURE_SEQ instead of this. */ +#define AKECS_MODE_PFFD 0x01 /* Start pedometer and free fall detect. */ +#define AKECS_MODE_E2P_READ 0x02 /* E2P access mode (read). */ +#define AKECS_MODE_POWERDOWN 0x03 /* Power down mode */ + +#define AKECS_MODE_MEASURE_SNG 0x10 /* Starts single measurement */ +#define AKECS_MODE_MEASURE_SEQ 0x11 /* Starts sequential measurement */ + +/* Default register settings */ +#define CSPEC_AINT 0x01 /* Amplification for acceleration sensor */ +#define CSPEC_SNG_NUM 0x01 /* Single measurement mode */ +#define CSPEC_SEQ_NUM 0x02 /* Sequential measurement mode */ +#define CSPEC_SFRQ_32 0x00 /* Measurement frequency: 32Hz */ +#define CSPEC_SFRQ_64 0x01 /* Measurement frequency: 64Hz */ +#define CSPEC_MCS 0x07 /* Clock frequency */ +#define CSPEC_MKS 0x01 /* Clock type: CMOS level */ +#define CSPEC_INTEN 0x01 /* Interruption pin enable: Enable */ + +#define RBUFF_SIZE 31 /* Rx buffer size */ +#define MAX_CALI_SIZE 0x1000U /* calibration buffer size */ + +/* AK8976A register address */ +#define AKECS_REG_ST 0xC0 +#define AKECS_REG_TMPS 0xC1 +#define AKECS_REG_MS1 0xE0 +#define AKECS_REG_MS2 0xE1 +#define AKECS_REG_MS3 0xE2 + +#define AKMIO 0xA1 + +/* IOCTLs for AKM library */ +#define ECS_IOCTL_INIT _IO(AKMIO, 0x01) +#define ECS_IOCTL_WRITE _IOW(AKMIO, 0x02, char[5]) +#define ECS_IOCTL_READ _IOWR(AKMIO, 0x03, char[5]) +#define ECS_IOCTL_RESET _IO(AKMIO, 0x04) +#define ECS_IOCTL_INT_STATUS _IO(AKMIO, 0x05) +#define ECS_IOCTL_FFD_STATUS _IO(AKMIO, 0x06) +#define ECS_IOCTL_SET_MODE _IOW(AKMIO, 0x07, short) +#define ECS_IOCTL_GETDATA _IOR(AKMIO, 0x08, char[RBUFF_SIZE+1]) +#define ECS_IOCTL_GET_NUMFRQ _IOR(AKMIO, 0x09, char[2]) +#define ECS_IOCTL_SET_PERST _IO(AKMIO, 0x0A) +#define ECS_IOCTL_SET_G0RST _IO(AKMIO, 0x0B) +#define ECS_IOCTL_SET_YPR _IOW(AKMIO, 0x0C, short[12]) +#define ECS_IOCTL_GET_OPEN_STATUS _IOR(AKMIO, 0x0D, int) +#define ECS_IOCTL_GET_CLOSE_STATUS _IOR(AKMIO, 0x0E, int) +#define ECS_IOCTL_GET_CALI_DATA _IOR(AKMIO, 0x0F, char[MAX_CALI_SIZE]) +#define ECS_IOCTL_GET_DELAY _IOR(AKMIO, 0x30, short) + +/* IOCTLs for APPs */ +#define ECS_IOCTL_APP_SET_MODE _IOW(AKMIO, 0x10, short) +#define ECS_IOCTL_APP_SET_MFLAG _IOW(AKMIO, 0x11, short) +#define ECS_IOCTL_APP_GET_MFLAG _IOW(AKMIO, 0x12, short) +#define ECS_IOCTL_APP_SET_AFLAG _IOW(AKMIO, 0x13, short) +#define ECS_IOCTL_APP_GET_AFLAG _IOR(AKMIO, 0x14, short) +#define ECS_IOCTL_APP_SET_TFLAG _IOR(AKMIO, 0x15, short) +#define ECS_IOCTL_APP_GET_TFLAG _IOR(AKMIO, 0x16, short) +#define ECS_IOCTL_APP_RESET_PEDOMETER _IO(AKMIO, 0x17) +#define ECS_IOCTL_APP_SET_DELAY _IOW(AKMIO, 0x18, short) +#define ECS_IOCTL_APP_GET_DELAY ECS_IOCTL_GET_DELAY +#define ECS_IOCTL_APP_SET_MVFLAG _IOW(AKMIO, 0x19, short) /* Set raw magnetic vector flag */ +#define ECS_IOCTL_APP_GET_MVFLAG _IOR(AKMIO, 0x1A, short) /* Get raw magnetic vector flag */ + +/* IOCTLs for pedometer */ +#define ECS_IOCTL_SET_STEP_CNT _IOW(AKMIO, 0x20, short) + +/* Default GPIO setting */ +#define ECS_RST 146 /*MISC4, bit2 */ +#define ECS_CLK_ON 155 /*MISC5, bit3 */ +#define ECS_INTR 161 /*INT2, bit1 */ + +struct akm8976_platform_data { + int reset; + int clk_on; + int intr; +}; + +extern char *get_akm_cal_ram(void); + +#endif + From 715e1396640815d8a577a2f2a0cc8fc779714e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 6 Apr 2009 20:38:22 -0700 Subject: [PATCH 0193/2556] [ARM] msm: Update defconfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit defconfig cleanup also disable VT Enable MMC and FAT filesystems in config Add NLS codepage to defconfig for FAT filesystems Enable MMC_PARANOID_SD_INIT in defconfig Enable CONFIG_MMC_EMBEDDED_SDIO Enable USB mass storage. bluetooth: Compile Bluetooth support. Enable ondemand cpufreq scaling. trout: Add CONFIG_PACKET for proper DHCP client work enable CONFIG_SERIAL_MSM_HS and CONFIG_SERIAL_MSM_HS_UART1 Add DEBUG_FS, DEBUG_VM, and DEBUG_SG to defconfig Disable SYSFS_DEPRECATED. Add devmapper and crypto support with AES/Twofish ciphers Add EXT2 / EXT3 support to defconfig Compile uinput support. Add LED triggers. Add printk timestamps. Add timer debug. Remove mousedev. Remove rtc interface. Turn off CONFIG_RFKILL_PM We do not want to power down the bluetooth chip in suspend. Disable MMC_BLOCK_BOUNCE in defconfig Enable CONFIG_INPUT_KEYCHORD Enable uid_stat in defconfig. Enable CONFIG_TIMED_OUTPUT Enable EXT2/EXT3/devmapper/crypto/aes/twofish support in defconfig Add LOOP support to defconfig Replace CONFIG_TROUT_H2W -> CONFIG_HTC_HEADSET in defconfig Adjust governor polling frequency. Add HTC_PWRSINK to defconfig Enable MACH_SAPPHIRE, TOUCHSCREEN_ELAN_I2C_8232, LEDS_CPLD in defconfig Disable OPROFILE in defconfig Regenerate 2.6.29 defconfig Enable cgroups in defconfig Update msm_defconfig to enable ipsec related options. Add CONFIG_TUN to defconfig Enable new camera implementation Disable some debug options. Enabled PPPOLAC for L2TP VPN connection. Enabled PPP_MPPE for PPTP VPN connection. Enable PPPoPNS for VPN and remove unused IPsec options. Enable CONFIG_MMC_BLOCK_DEFERRED_RESUME Enable IPV6 in defconfig Enable various debug options + disable swap Enable cpu_freq_stat Switch from USB function to USB gadget drivers. Enable CONFIG_MEDIA_SUPPORT Enable CONFIG_USB_ANDROID_RNDIS in defconfig Enable netfilter options needed for iptables in defconfig. Set CONFIG_DEBUG_BUGVERBOSE Enable CONFIG_USB_ANDROID_RNDIS_WCEIS Switch to CFQ, set mmap_min_addr Increase mmap_min_addr to 32k Enable CBQ network scheduling Disable debug options Switch to the Hierarchical Token Buckets (HTB) packet scheduling algorithm. Signed-off-by: San Mehat Signed-off-by: Mike Chan Signed-off-by: Mike Lockwood Signed-off-by: Nick Pelly Signed-off-by: Arve Hjønnevåg Signed-off-by: Chung-yih Wang Signed-off-by: Iliyan Malchev --- arch/arm/configs/msm_defconfig | 227 ++++++++++++++++++++++++++++++--- 1 file changed, 212 insertions(+), 15 deletions(-) mode change 100644 => 100755 arch/arm/configs/msm_defconfig diff --git a/arch/arm/configs/msm_defconfig b/arch/arm/configs/msm_defconfig old mode 100644 new mode 100755 index 2b8f7affc1eb5..9cef7e4a6c73a --- a/arch/arm/configs/msm_defconfig +++ b/arch/arm/configs/msm_defconfig @@ -1,72 +1,269 @@ CONFIG_EXPERIMENTAL=y +# CONFIG_SWAP is not set CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_DEV_INITRD=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EMBEDDED=y +# CONFIG_SYSCTL_SYSCALL is not set +# CONFIG_ELF_CORE is not set +CONFIG_ASHMEM=y CONFIG_SLAB=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_IOSCHED_DEADLINE is not set -# CONFIG_IOSCHED_CFQ is not set CONFIG_ARCH_MSM=y CONFIG_MACH_HALIBUT=y +CONFIG_MACH_TROUT=y +CONFIG_HTC_HEADSET=y +CONFIG_MSM_SERIAL_DEBUGGER=y +CONFIG_WIFI_CONTROL_FUNC=y +CONFIG_WIFI_MEM_PREALLOC=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_PREEMPT=y CONFIG_AEABI=y # CONFIG_OABI_COMPAT is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y CONFIG_PM=y +CONFIG_WAKELOCK=y CONFIG_NET=y +CONFIG_PACKET=y CONFIG_UNIX=y +CONFIG_NET_KEY=y CONFIG_INET=y -# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +CONFIG_INET_ESP=y # CONFIG_INET_XFRM_MODE_TUNNEL is not set # CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set # CONFIG_INET_DIAG is not set -# CONFIG_IPV6 is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_DEBUG=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_LED=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_OWNER=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +CONFIG_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_LL=y +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +# CONFIG_FIRMWARE_IN_KERNEL is not set CONFIG_MTD=y CONFIG_MTD_PARTITIONS=y CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_UID_STAT=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +CONFIG_DM_UEVENT=y CONFIG_NETDEVICES=y +CONFIG_IFB=y CONFIG_DUMMY=y +CONFIG_TUN=y CONFIG_NET_ETHERNET=y CONFIG_SMC91X=y CONFIG_PPP=y CONFIG_PPP_ASYNC=y CONFIG_PPP_DEFLATE=y CONFIG_PPP_BSDCOMP=y -# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_PPP_MPPE=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +CONFIG_MSM_RMNET_DEBUG=y +# CONFIG_INPUT_MOUSEDEV is not set CONFIG_INPUT_EVDEV=y -# CONFIG_KEYBOARD_ATKBD is not set +CONFIG_INPUT_KEYRESET=y +# CONFIG_INPUT_KEYBOARD is not set # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ELAN_I2C_8232=y +CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y # CONFIG_SERIO is not set -CONFIG_VT_HW_CONSOLE_BINDING=y +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set CONFIG_SERIAL_MSM=y -CONFIG_SERIAL_MSM_CONSOLE=y +CONFIG_SERIAL_MSM_RX_WAKEUP=y +CONFIG_SERIAL_MSM_HS=y # CONFIG_LEGACY_PTYS is not set # CONFIG_HW_RANDOM is not set CONFIG_I2C=y +CONFIG_POWER_SUPPLY=y # CONFIG_HWMON is not set +CONFIG_MEDIA_SUPPORT=y +CONFIG_VIDEO_DEV=y +# CONFIG_VIDEO_ALLOW_V4L1 is not set +CONFIG_MSM_CAMERA=y +CONFIG_MT9T013=y +CONFIG_DAB=y CONFIG_VIDEO_OUTPUT_CONTROL=y CONFIG_FB=y -CONFIG_FB_MODE_HELPERS=y -CONFIG_FB_TILEBLITTING=y -CONFIG_FB_MSM=y -# CONFIG_VGA_CONSOLE is not set -CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_ANDROID=y +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +CONFIG_USB_ANDROID_RNDIS=y +CONFIG_USB_ANDROID_RNDIS_WCEIS=y +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +CONFIG_MMC_MSM7X00A=y CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_SLEEP=y +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +# CONFIG_DNOTIFY is not set CONFIG_INOTIFY=y +CONFIG_VFAT_FS=y CONFIG_TMPFS=y +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y CONFIG_DEBUG_KERNEL=y CONFIG_SCHEDSTATS=y -CONFIG_DEBUG_MUTEXES=y -CONFIG_DEBUG_SPINLOCK_SLEEP=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_PREEMPT is not set CONFIG_DEBUG_INFO=y -CONFIG_DEBUG_LL=y +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_TWOFISH=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set From bbe78f4502c40574b0875389f64a02efbafa3898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 29 Sep 2008 11:16:12 -0700 Subject: [PATCH 0194/2556] HACK: [ARM] msm: trout: CPLD GPIO driver -- take from 2.6.35 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I6ce3bfaa953c6af2934727befc2f3696b2e44a2c Signed-off-by: Rebecca Schultz Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/board-trout-gpio.c | 260 +++++++++++++++------------ 1 file changed, 146 insertions(+), 114 deletions(-) diff --git a/arch/arm/mach-msm/board-trout-gpio.c b/arch/arm/mach-msm/board-trout-gpio.c index a604ec1e44bf2..0a06de6717ca1 100644 --- a/arch/arm/mach-msm/board-trout-gpio.c +++ b/arch/arm/mach-msm/board-trout-gpio.c @@ -1,164 +1,147 @@ -/* - * linux/arch/arm/mach-msm/gpio.c +/* arch/arm/mach-msm/board-trout-gpio.c * - * Copyright (C) 2005 HP Labs * Copyright (C) 2008 Google, Inc. - * Copyright (C) 2009 Pavel Machek * - * 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 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 "board-trout.h" +#include "gpio_chip.h" +static uint8_t trout_cpld_shadow[4] = { + [0] = 0x40, // for serial debug, low current + [1] = 0x04, // I2C_PULL + [3] = 0x04, // mmdi 32k en +}; static uint8_t trout_int_mask[2] = { - [0] = 0xff, /* mask all interrupts */ - [1] = 0xff, + [0] = 0xff, /* mask all interrupts */ + [1] = 0xff, }; static uint8_t trout_sleep_int_mask[] = { - [0] = 0xff, - [1] = 0xff, -}; - -struct msm_gpio_chip { - struct gpio_chip chip; - void __iomem *reg; /* Base of register bank */ - u8 shadow; + [0] = 0xff, + [1] = 0xff, }; +static int trout_suspended; -#define to_msm_gpio_chip(c) container_of(c, struct msm_gpio_chip, chip) - -static int msm_gpiolib_get(struct gpio_chip *chip, unsigned offset) +static int trout_gpio_read(struct gpio_chip *chip, unsigned n) { - struct msm_gpio_chip *msm_gpio = to_msm_gpio_chip(chip); - unsigned mask = 1 << offset; - - return !!(readb(msm_gpio->reg) & mask); + uint8_t b; + int reg; + if (n >= TROUT_GPIO_VIRTUAL_BASE) + n += TROUT_GPIO_VIRTUAL_TO_REAL_OFFSET; + b = 1U << (n & 7); + reg = (n & 0x78) >> 2; // assumes base is 128 + return !!(readb(TROUT_CPLD_BASE + reg) & b); } -static void msm_gpiolib_set(struct gpio_chip *chip, unsigned offset, int val) +int trout_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on) { - struct msm_gpio_chip *msm_gpio = to_msm_gpio_chip(chip); - unsigned mask = 1 << offset; - - if (val) - msm_gpio->shadow |= mask; - else - msm_gpio->shadow &= ~mask; + uint8_t b = 1U << (n & 7); + int reg = (n & 0x78) >> 2; // assumes base is 128 + unsigned long flags; + uint8_t reg_val; - writeb(msm_gpio->shadow, msm_gpio->reg); -} + if ((reg >> 1) >= ARRAY_SIZE(trout_cpld_shadow)) { + printk(KERN_ERR "trout_gpio_write called on input %d\n", n); + return -ENOTSUPP; + } -static int msm_gpiolib_direction_input(struct gpio_chip *chip, - unsigned offset) -{ - msm_gpiolib_set(chip, offset, 0); + local_irq_save(flags); + if(on) + reg_val = trout_cpld_shadow[reg >> 1] |= b; + else + reg_val = trout_cpld_shadow[reg >> 1] &= ~b; + writeb(reg_val, TROUT_CPLD_BASE + reg); + local_irq_restore(flags); return 0; } -static int msm_gpiolib_direction_output(struct gpio_chip *chip, - unsigned offset, int val) +static int trout_gpio_configure(struct gpio_chip *chip, unsigned int gpio, unsigned long flags) { - msm_gpiolib_set(chip, offset, val); + if(flags & (GPIOF_OUTPUT_LOW | GPIOF_OUTPUT_HIGH)) + trout_gpio_write(chip, gpio, flags & GPIOF_OUTPUT_HIGH); return 0; } -static int trout_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +static int trout_gpio_get_irq_num(struct gpio_chip *chip, unsigned int gpio, unsigned int *irqp, unsigned long *irqnumflagsp) { - struct msm_gpio_chip *msm_gpio = to_msm_gpio_chip(chip); - - return TROUT_GPIO_TO_INT(offset + chip->base); + if ((gpio < TROUT_GPIO_BANK0_FIRST_INT_SOURCE || + gpio > TROUT_GPIO_BANK0_LAST_INT_SOURCE) && + (gpio < TROUT_GPIO_BANK1_FIRST_INT_SOURCE || + gpio > TROUT_GPIO_BANK1_LAST_INT_SOURCE)) + return -ENOENT; + *irqp = TROUT_GPIO_TO_INT(gpio); + if(irqnumflagsp) + *irqnumflagsp = 0; + return 0; } -#define TROUT_GPIO_BANK(name, reg_num, base_gpio, shadow_val) \ - { \ - .chip = { \ - .label = name, \ - .direction_input = msm_gpiolib_direction_input,\ - .direction_output = msm_gpiolib_direction_output, \ - .get = msm_gpiolib_get, \ - .set = msm_gpiolib_set, \ - .to_irq = trout_gpio_to_irq, \ - .base = base_gpio, \ - .ngpio = 8, \ - }, \ - .reg = (void *) reg_num + TROUT_CPLD_BASE, \ - .shadow = shadow_val, \ - } - -static struct msm_gpio_chip msm_gpio_banks[] = { -#if defined(CONFIG_MSM_DEBUG_UART1) - /* H2W pins <-> UART1 */ - TROUT_GPIO_BANK("MISC2", 0x00, TROUT_GPIO_MISC2_BASE, 0x40), -#else - /* H2W pins <-> UART3, Bluetooth <-> UART1 */ - TROUT_GPIO_BANK("MISC2", 0x00, TROUT_GPIO_MISC2_BASE, 0x80), -#endif - /* I2C pull */ - TROUT_GPIO_BANK("MISC3", 0x02, TROUT_GPIO_MISC3_BASE, 0x04), - TROUT_GPIO_BANK("MISC4", 0x04, TROUT_GPIO_MISC4_BASE, 0), - /* mmdi 32k en */ - TROUT_GPIO_BANK("MISC5", 0x06, TROUT_GPIO_MISC5_BASE, 0x04), - TROUT_GPIO_BANK("INT2", 0x08, TROUT_GPIO_INT2_BASE, 0), - TROUT_GPIO_BANK("MISC1", 0x0a, TROUT_GPIO_MISC1_BASE, 0), - TROUT_GPIO_BANK("VIRTUAL", 0x12, TROUT_GPIO_VIRTUAL_BASE, 0), -}; - -static void trout_gpio_irq_ack(struct irq_data *d) +static void trout_gpio_irq_ack(unsigned int irq) { - int bank = TROUT_INT_TO_BANK(d->irq); - uint8_t mask = TROUT_INT_TO_MASK(d->irq); + int bank = TROUT_INT_TO_BANK(irq); + uint8_t mask = TROUT_INT_TO_MASK(irq); int reg = TROUT_BANK_TO_STAT_REG(bank); - /*printk(KERN_INFO "trout_gpio_irq_ack irq %d\n", d->irq);*/ + /*printk(KERN_INFO "trout_gpio_irq_ack irq %d\n", irq);*/ writeb(mask, TROUT_CPLD_BASE + reg); } -static void trout_gpio_irq_mask(struct irq_data *d) +static void trout_gpio_irq_mask(unsigned int irq) { unsigned long flags; uint8_t reg_val; - int bank = TROUT_INT_TO_BANK(d->irq); - uint8_t mask = TROUT_INT_TO_MASK(d->irq); + int bank = TROUT_INT_TO_BANK(irq); + uint8_t mask = TROUT_INT_TO_MASK(irq); int reg = TROUT_BANK_TO_MASK_REG(bank); local_irq_save(flags); reg_val = trout_int_mask[bank] |= mask; /*printk(KERN_INFO "trout_gpio_irq_mask irq %d => %d:%02x\n", - d->irq, bank, reg_val);*/ - writeb(reg_val, TROUT_CPLD_BASE + reg); + irq, bank, reg_val);*/ + if (!trout_suspended) + writeb(reg_val, TROUT_CPLD_BASE + reg); local_irq_restore(flags); } -static void trout_gpio_irq_unmask(struct irq_data *d) +static void trout_gpio_irq_unmask(unsigned int irq) { unsigned long flags; uint8_t reg_val; - int bank = TROUT_INT_TO_BANK(d->irq); - uint8_t mask = TROUT_INT_TO_MASK(d->irq); + int bank = TROUT_INT_TO_BANK(irq); + uint8_t mask = TROUT_INT_TO_MASK(irq); int reg = TROUT_BANK_TO_MASK_REG(bank); local_irq_save(flags); reg_val = trout_int_mask[bank] &= ~mask; /*printk(KERN_INFO "trout_gpio_irq_unmask irq %d => %d:%02x\n", - d->irq, bank, reg_val);*/ - writeb(reg_val, TROUT_CPLD_BASE + reg); + irq, bank, reg_val);*/ + if (!trout_suspended) + writeb(reg_val, TROUT_CPLD_BASE + reg); local_irq_restore(flags); } -int trout_gpio_irq_set_wake(struct irq_data *d, unsigned int on) +int trout_gpio_irq_set_wake(unsigned int irq, unsigned int on) { unsigned long flags; - int bank = TROUT_INT_TO_BANK(d->irq); - uint8_t mask = TROUT_INT_TO_MASK(d->irq); + int bank = TROUT_INT_TO_BANK(irq); + uint8_t mask = TROUT_INT_TO_MASK(irq); local_irq_save(flags); if(on) @@ -198,38 +181,87 @@ static void trout_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) } int_base += TROUT_INT_BANK0_COUNT; } - desc->irq_data.chip->irq_ack(&desc->irq_data); + desc->chip->ack(irq); +} + +static int trout_sysdev_suspend(struct sys_device *dev, pm_message_t state) +{ + trout_suspended = 1; + writeb(trout_sleep_int_mask[0], + TROUT_CPLD_BASE + TROUT_GPIO_INT_MASK0_REG); + writeb(trout_sleep_int_mask[1], + TROUT_CPLD_BASE + TROUT_GPIO_INT_MASK1_REG); + writeb(trout_sleep_int_mask[0], + TROUT_CPLD_BASE + TROUT_GPIO_INT_STAT0_REG); + writeb(trout_sleep_int_mask[1], + TROUT_CPLD_BASE + TROUT_GPIO_INT_STAT1_REG); + return 0; +} + +int trout_sysdev_resume(struct sys_device *dev) +{ + writeb(trout_int_mask[0], TROUT_CPLD_BASE + TROUT_GPIO_INT_MASK0_REG); + writeb(trout_int_mask[1], TROUT_CPLD_BASE + TROUT_GPIO_INT_MASK1_REG); + trout_suspended = 0; + return 0; } static struct irq_chip trout_gpio_irq_chip = { - .name = "troutgpio", - .irq_ack = trout_gpio_irq_ack, - .irq_mask = trout_gpio_irq_mask, - .irq_unmask = trout_gpio_irq_unmask, - .irq_set_wake = trout_gpio_irq_set_wake, + .name = "troutgpio", + .ack = trout_gpio_irq_ack, + .mask = trout_gpio_irq_mask, + .unmask = trout_gpio_irq_unmask, + .set_wake = trout_gpio_irq_set_wake, + //.set_type = trout_gpio_irq_set_type, }; -/* - * Called from the processor-specific init to enable GPIO pin support. - */ -int __init trout_init_gpio(void) +static struct gpio_chip trout_gpio_chip = { + .start = TROUT_GPIO_START, + .end = TROUT_GPIO_END, + .configure = trout_gpio_configure, + .get_irq_num = trout_gpio_get_irq_num, + .read = trout_gpio_read, + .write = trout_gpio_write, +// .read_detect_status = trout_gpio_read_detect_status, +// .clear_detect_status = trout_gpio_clear_detect_status +}; + +struct sysdev_class trout_sysdev_class = { + .name = "troutgpio_irq", + .suspend = trout_sysdev_suspend, + .resume = trout_sysdev_resume, +}; + +static struct sys_device trout_irq_device = { + .cls = &trout_sysdev_class, +}; + +static int __init trout_init_gpio(void) { int i; + + if (!machine_is_trout()) + return 0; + + for(i = 0; i < ARRAY_SIZE(trout_cpld_shadow); i++) + writeb(trout_cpld_shadow[i], TROUT_CPLD_BASE + i * 2); + for(i = TROUT_INT_START; i <= TROUT_INT_END; i++) { set_irq_chip(i, &trout_gpio_irq_chip); set_irq_handler(i, handle_edge_irq); set_irq_flags(i, IRQF_VALID); } - for (i = 0; i < ARRAY_SIZE(msm_gpio_banks); i++) - gpiochip_add(&msm_gpio_banks[i].chip); + register_gpio_chip(&trout_gpio_chip); set_irq_type(MSM_GPIO_TO_INT(17), IRQF_TRIGGER_HIGH); set_irq_chained_handler(MSM_GPIO_TO_INT(17), trout_gpio_irq_handler); set_irq_wake(MSM_GPIO_TO_INT(17), 1); + if(sysdev_class_register(&trout_sysdev_class) == 0) + sysdev_register(&trout_irq_device); + return 0; } postcore_initcall(trout_init_gpio); - From a47e76752b425ec542dd87723c2fc9dcb69851ae Mon Sep 17 00:00:00 2001 From: Gregory Bean Date: Sat, 14 Aug 2010 17:07:53 -0700 Subject: [PATCH 0195/2556] [ARM] msm: Convert trout gpio to gpiolib. Change-Id: If38ad9a17c759994d0a92d480864781b8b0250ff Signed-off-by: Gregory Bean --- arch/arm/mach-msm/board-trout-gpio.c | 43 +++++++++++++--------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/arch/arm/mach-msm/board-trout-gpio.c b/arch/arm/mach-msm/board-trout-gpio.c index 0a06de6717ca1..6f421c0c95ff6 100644 --- a/arch/arm/mach-msm/board-trout-gpio.c +++ b/arch/arm/mach-msm/board-trout-gpio.c @@ -18,13 +18,12 @@ #include #include #include +#include #include -#include #include #include "board-trout.h" -#include "gpio_chip.h" static uint8_t trout_cpld_shadow[4] = { [0] = 0x40, // for serial debug, low current @@ -41,8 +40,9 @@ static uint8_t trout_sleep_int_mask[] = { }; static int trout_suspended; -static int trout_gpio_read(struct gpio_chip *chip, unsigned n) +static int trout_gpio_get(struct gpio_chip *chip, unsigned offset) { + unsigned n = chip->base + offset; uint8_t b; int reg; if (n >= TROUT_GPIO_VIRTUAL_BASE) @@ -52,8 +52,9 @@ static int trout_gpio_read(struct gpio_chip *chip, unsigned n) return !!(readb(TROUT_CPLD_BASE + reg) & b); } -int trout_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on) +static void trout_gpio_set(struct gpio_chip *chip, unsigned offset, int on) { + unsigned n = chip->base + offset; uint8_t b = 1U << (n & 7); int reg = (n & 0x78) >> 2; // assumes base is 128 unsigned long flags; @@ -61,7 +62,7 @@ int trout_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on) if ((reg >> 1) >= ARRAY_SIZE(trout_cpld_shadow)) { printk(KERN_ERR "trout_gpio_write called on input %d\n", n); - return -ENOTSUPP; + return; } local_irq_save(flags); @@ -71,27 +72,25 @@ int trout_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on) reg_val = trout_cpld_shadow[reg >> 1] &= ~b; writeb(reg_val, TROUT_CPLD_BASE + reg); local_irq_restore(flags); - return 0; } -static int trout_gpio_configure(struct gpio_chip *chip, unsigned int gpio, unsigned long flags) +static int trout_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) { - if(flags & (GPIOF_OUTPUT_LOW | GPIOF_OUTPUT_HIGH)) - trout_gpio_write(chip, gpio, flags & GPIOF_OUTPUT_HIGH); + trout_gpio_set(chip, offset, value); return 0; } -static int trout_gpio_get_irq_num(struct gpio_chip *chip, unsigned int gpio, unsigned int *irqp, unsigned long *irqnumflagsp) +static int trout_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { + unsigned gpio = chip->base + offset; + if ((gpio < TROUT_GPIO_BANK0_FIRST_INT_SOURCE || gpio > TROUT_GPIO_BANK0_LAST_INT_SOURCE) && (gpio < TROUT_GPIO_BANK1_FIRST_INT_SOURCE || gpio > TROUT_GPIO_BANK1_LAST_INT_SOURCE)) return -ENOENT; - *irqp = TROUT_GPIO_TO_INT(gpio); - if(irqnumflagsp) - *irqnumflagsp = 0; - return 0; + return TROUT_GPIO_TO_INT(gpio); } static void trout_gpio_irq_ack(unsigned int irq) @@ -216,14 +215,12 @@ static struct irq_chip trout_gpio_irq_chip = { }; static struct gpio_chip trout_gpio_chip = { - .start = TROUT_GPIO_START, - .end = TROUT_GPIO_END, - .configure = trout_gpio_configure, - .get_irq_num = trout_gpio_get_irq_num, - .read = trout_gpio_read, - .write = trout_gpio_write, -// .read_detect_status = trout_gpio_read_detect_status, -// .clear_detect_status = trout_gpio_clear_detect_status + .base = TROUT_GPIO_START, + .ngpio = TROUT_GPIO_END - TROUT_GPIO_START + 1, + .direction_output = trout_gpio_direction_output, + .get = trout_gpio_get, + .set = trout_gpio_set, + .to_irq = trout_gpio_to_irq, }; struct sysdev_class trout_sysdev_class = { @@ -252,7 +249,7 @@ static int __init trout_init_gpio(void) set_irq_flags(i, IRQF_VALID); } - register_gpio_chip(&trout_gpio_chip); + gpiochip_add(&trout_gpio_chip); set_irq_type(MSM_GPIO_TO_INT(17), IRQF_TRIGGER_HIGH); set_irq_chained_handler(MSM_GPIO_TO_INT(17), trout_gpio_irq_handler); From 8ac4fc3af67c28b75c35670eea905bcd65918ae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 29 Sep 2008 17:44:12 -0700 Subject: [PATCH 0196/2556] [ARM] msm: trout: platform specific keypad driver Signed-off-by: Rebecca Schultz --- arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/board-trout-keypad.c | 345 +++++++++++++++++++++++++ 2 files changed, 346 insertions(+) create mode 100644 arch/arm/mach-msm/board-trout-keypad.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index cbfca54f4a5bd..c38ce01fe42a7 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_MSM_CPU_FREQ_SCREEN) += cpufreq.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o +obj-$(CONFIG_MACH_TROUT) += board-trout-keypad.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o diff --git a/arch/arm/mach-msm/board-trout-keypad.c b/arch/arm/mach-msm/board-trout-keypad.c new file mode 100644 index 0000000000000..0299d0686de9f --- /dev/null +++ b/arch/arm/mach-msm/board-trout-keypad.c @@ -0,0 +1,345 @@ +/* arch/arm/mach-msm/board-trout-keypad.c + * + * Copyright (C) 2008 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 + +#include "board-trout.h" + +static char *keycaps = "--qwerty"; +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "board_trout." +module_param_named(keycaps, keycaps, charp, 0); + + +static unsigned int trout_col_gpios[] = { 35, 34, 33, 32, 31, 23, 30, 78 }; +static unsigned int trout_row_gpios[] = { 42, 41, 40, 39, 38, 37, 36 }; + +#define KEYMAP_INDEX(col, row) ((col)*ARRAY_SIZE(trout_row_gpios) + (row)) + +static const unsigned short trout_keymap[ARRAY_SIZE(trout_col_gpios) * ARRAY_SIZE(trout_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_BACK, + [KEYMAP_INDEX(0, 1)] = KEY_HOME, +// [KEYMAP_INDEX(0, 2)] = KEY_, + [KEYMAP_INDEX(0, 3)] = KEY_BACKSPACE, + [KEYMAP_INDEX(0, 4)] = KEY_ENTER, + [KEYMAP_INDEX(0, 5)] = KEY_RIGHTALT, + [KEYMAP_INDEX(0, 6)] = KEY_P, + + [KEYMAP_INDEX(1, 0)] = KEY_MENU, +// [KEYMAP_INDEX(1, 0)] = 229, // SOFT1 + [KEYMAP_INDEX(1, 1)] = KEY_SEND, + [KEYMAP_INDEX(1, 2)] = KEY_END, + [KEYMAP_INDEX(1, 3)] = KEY_LEFTALT, + [KEYMAP_INDEX(1, 4)] = KEY_A, + [KEYMAP_INDEX(1, 5)] = KEY_LEFTSHIFT, + [KEYMAP_INDEX(1, 6)] = KEY_Q, + + [KEYMAP_INDEX(2, 0)] = KEY_U, + [KEYMAP_INDEX(2, 1)] = KEY_7, + [KEYMAP_INDEX(2, 2)] = KEY_K, + [KEYMAP_INDEX(2, 3)] = KEY_J, + [KEYMAP_INDEX(2, 4)] = KEY_M, + [KEYMAP_INDEX(2, 5)] = KEY_SLASH, + [KEYMAP_INDEX(2, 6)] = KEY_8, + + [KEYMAP_INDEX(3, 0)] = KEY_5, + [KEYMAP_INDEX(3, 1)] = KEY_6, + [KEYMAP_INDEX(3, 2)] = KEY_B, + [KEYMAP_INDEX(3, 3)] = KEY_H, + [KEYMAP_INDEX(3, 4)] = KEY_N, + [KEYMAP_INDEX(3, 5)] = KEY_SPACE, + [KEYMAP_INDEX(3, 6)] = KEY_Y, + + [KEYMAP_INDEX(4, 0)] = KEY_4, + [KEYMAP_INDEX(4, 1)] = KEY_R, + [KEYMAP_INDEX(4, 2)] = KEY_V, + [KEYMAP_INDEX(4, 3)] = KEY_G, + [KEYMAP_INDEX(4, 4)] = KEY_C, + //[KEYMAP_INDEX(4, 5)] = KEY_, + [KEYMAP_INDEX(4, 6)] = KEY_T, + + [KEYMAP_INDEX(5, 0)] = KEY_2, + [KEYMAP_INDEX(5, 1)] = KEY_W, + [KEYMAP_INDEX(5, 2)] = KEY_COMPOSE, + [KEYMAP_INDEX(5, 3)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(5, 4)] = KEY_S, + [KEYMAP_INDEX(5, 5)] = KEY_Z, + [KEYMAP_INDEX(5, 6)] = KEY_1, + + [KEYMAP_INDEX(6, 0)] = KEY_I, + [KEYMAP_INDEX(6, 1)] = KEY_0, + [KEYMAP_INDEX(6, 2)] = KEY_O, + [KEYMAP_INDEX(6, 3)] = KEY_L, + [KEYMAP_INDEX(6, 4)] = KEY_DOT, + [KEYMAP_INDEX(6, 5)] = KEY_COMMA, + [KEYMAP_INDEX(6, 6)] = KEY_9, + + [KEYMAP_INDEX(7, 0)] = KEY_3, + [KEYMAP_INDEX(7, 1)] = KEY_E, + [KEYMAP_INDEX(7, 2)] = KEY_EMAIL, // @ + [KEYMAP_INDEX(7, 3)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(7, 4)] = KEY_X, + [KEYMAP_INDEX(7, 5)] = KEY_F, + [KEYMAP_INDEX(7, 6)] = KEY_D +}; + +static unsigned int trout_col_gpios_evt2[] = { 35, 34, 33, 32, 31, 23, 30, 109 }; +static unsigned int trout_row_gpios_evt2[] = { 42, 41, 40, 39, 38, 37, 36 }; + +static const unsigned short trout_keymap_evt2_1[ARRAY_SIZE(trout_col_gpios) * ARRAY_SIZE(trout_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_BACK, + [KEYMAP_INDEX(0, 1)] = KEY_HOME, +// [KEYMAP_INDEX(0, 2)] = KEY_, + [KEYMAP_INDEX(0, 3)] = KEY_BACKSPACE, + [KEYMAP_INDEX(0, 4)] = KEY_ENTER, + [KEYMAP_INDEX(0, 5)] = KEY_RIGHTSHIFT, + [KEYMAP_INDEX(0, 6)] = KEY_P, + + [KEYMAP_INDEX(1, 0)] = KEY_MENU, + [KEYMAP_INDEX(1, 1)] = KEY_SEND, +// [KEYMAP_INDEX(1, 2)] = KEY_, + [KEYMAP_INDEX(1, 3)] = KEY_LEFTSHIFT, + [KEYMAP_INDEX(1, 4)] = KEY_A, + [KEYMAP_INDEX(1, 5)] = KEY_COMPOSE, + [KEYMAP_INDEX(1, 6)] = KEY_Q, + + [KEYMAP_INDEX(2, 0)] = KEY_U, + [KEYMAP_INDEX(2, 1)] = KEY_7, + [KEYMAP_INDEX(2, 2)] = KEY_K, + [KEYMAP_INDEX(2, 3)] = KEY_J, + [KEYMAP_INDEX(2, 4)] = KEY_M, + [KEYMAP_INDEX(2, 5)] = KEY_SLASH, + [KEYMAP_INDEX(2, 6)] = KEY_8, + + [KEYMAP_INDEX(3, 0)] = KEY_5, + [KEYMAP_INDEX(3, 1)] = KEY_6, + [KEYMAP_INDEX(3, 2)] = KEY_B, + [KEYMAP_INDEX(3, 3)] = KEY_H, + [KEYMAP_INDEX(3, 4)] = KEY_N, + [KEYMAP_INDEX(3, 5)] = KEY_SPACE, + [KEYMAP_INDEX(3, 6)] = KEY_Y, + + [KEYMAP_INDEX(4, 0)] = KEY_4, + [KEYMAP_INDEX(4, 1)] = KEY_R, + [KEYMAP_INDEX(4, 2)] = KEY_V, + [KEYMAP_INDEX(4, 3)] = KEY_G, + [KEYMAP_INDEX(4, 4)] = KEY_C, +// [KEYMAP_INDEX(4, 5)] = KEY_, + [KEYMAP_INDEX(4, 6)] = KEY_T, + + [KEYMAP_INDEX(5, 0)] = KEY_2, + [KEYMAP_INDEX(5, 1)] = KEY_W, + [KEYMAP_INDEX(5, 2)] = KEY_LEFTALT, + [KEYMAP_INDEX(5, 3)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(5, 4)] = KEY_S, + [KEYMAP_INDEX(5, 5)] = KEY_Z, + [KEYMAP_INDEX(5, 6)] = KEY_1, + + [KEYMAP_INDEX(6, 0)] = KEY_I, + [KEYMAP_INDEX(6, 1)] = KEY_0, + [KEYMAP_INDEX(6, 2)] = KEY_O, + [KEYMAP_INDEX(6, 3)] = KEY_L, + [KEYMAP_INDEX(6, 4)] = KEY_COMMA, + [KEYMAP_INDEX(6, 5)] = KEY_DOT, + [KEYMAP_INDEX(6, 6)] = KEY_9, + + [KEYMAP_INDEX(7, 0)] = KEY_3, + [KEYMAP_INDEX(7, 1)] = KEY_E, + [KEYMAP_INDEX(7, 2)] = KEY_EMAIL, // @ + [KEYMAP_INDEX(7, 3)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(7, 4)] = KEY_X, + [KEYMAP_INDEX(7, 5)] = KEY_F, + [KEYMAP_INDEX(7, 6)] = KEY_D +}; + +static const unsigned short trout_keymap_evt2_2[ARRAY_SIZE(trout_col_gpios) * ARRAY_SIZE(trout_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_BACK, + [KEYMAP_INDEX(0, 1)] = KEY_HOME, +// [KEYMAP_INDEX(0, 2)] = KEY_, + [KEYMAP_INDEX(0, 3)] = KEY_BACKSPACE, + [KEYMAP_INDEX(0, 4)] = KEY_ENTER, + [KEYMAP_INDEX(0, 5)] = KEY_RIGHTSHIFT, + [KEYMAP_INDEX(0, 6)] = KEY_P, + + [KEYMAP_INDEX(1, 0)] = KEY_MENU, /* external menu key */ + [KEYMAP_INDEX(1, 1)] = KEY_SEND, +// [KEYMAP_INDEX(1, 2)] = KEY_, + [KEYMAP_INDEX(1, 3)] = KEY_LEFTSHIFT, + [KEYMAP_INDEX(1, 4)] = KEY_A, + [KEYMAP_INDEX(1, 5)] = KEY_F1, /* qwerty menu key */ + [KEYMAP_INDEX(1, 6)] = KEY_Q, + + [KEYMAP_INDEX(2, 0)] = KEY_U, + [KEYMAP_INDEX(2, 1)] = KEY_7, + [KEYMAP_INDEX(2, 2)] = KEY_K, + [KEYMAP_INDEX(2, 3)] = KEY_J, + [KEYMAP_INDEX(2, 4)] = KEY_M, + [KEYMAP_INDEX(2, 5)] = KEY_DOT, + [KEYMAP_INDEX(2, 6)] = KEY_8, + + [KEYMAP_INDEX(3, 0)] = KEY_5, + [KEYMAP_INDEX(3, 1)] = KEY_6, + [KEYMAP_INDEX(3, 2)] = KEY_B, + [KEYMAP_INDEX(3, 3)] = KEY_H, + [KEYMAP_INDEX(3, 4)] = KEY_N, + [KEYMAP_INDEX(3, 5)] = KEY_SPACE, + [KEYMAP_INDEX(3, 6)] = KEY_Y, + + [KEYMAP_INDEX(4, 0)] = KEY_4, + [KEYMAP_INDEX(4, 1)] = KEY_R, + [KEYMAP_INDEX(4, 2)] = KEY_V, + [KEYMAP_INDEX(4, 3)] = KEY_G, + [KEYMAP_INDEX(4, 4)] = KEY_C, + [KEYMAP_INDEX(4, 5)] = KEY_EMAIL, // @ + [KEYMAP_INDEX(4, 6)] = KEY_T, + + [KEYMAP_INDEX(5, 0)] = KEY_2, + [KEYMAP_INDEX(5, 1)] = KEY_W, + [KEYMAP_INDEX(5, 2)] = KEY_LEFTALT, + [KEYMAP_INDEX(5, 3)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(5, 4)] = KEY_S, + [KEYMAP_INDEX(5, 5)] = KEY_Z, + [KEYMAP_INDEX(5, 6)] = KEY_1, + + [KEYMAP_INDEX(6, 0)] = KEY_I, + [KEYMAP_INDEX(6, 1)] = KEY_0, + [KEYMAP_INDEX(6, 2)] = KEY_O, + [KEYMAP_INDEX(6, 3)] = KEY_L, + [KEYMAP_INDEX(6, 4)] = KEY_COMMA, + [KEYMAP_INDEX(6, 5)] = KEY_RIGHTALT, + [KEYMAP_INDEX(6, 6)] = KEY_9, + + [KEYMAP_INDEX(7, 0)] = KEY_3, + [KEYMAP_INDEX(7, 1)] = KEY_E, + [KEYMAP_INDEX(7, 2)] = KEY_COMPOSE, + [KEYMAP_INDEX(7, 3)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(7, 4)] = KEY_X, + [KEYMAP_INDEX(7, 5)] = KEY_F, + [KEYMAP_INDEX(7, 6)] = KEY_D +}; + +static struct gpio_event_matrix_info trout_keypad_matrix_info = { + .info.func = gpio_event_matrix_func, + .keymap = trout_keymap, + .output_gpios = trout_col_gpios, + .input_gpios = trout_row_gpios, + .noutputs = ARRAY_SIZE(trout_col_gpios), + .ninputs = ARRAY_SIZE(trout_row_gpios), + .settle_time.tv.nsec = 40 * NSEC_PER_USEC, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_REMOVE_PHANTOM_KEYS |GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/ +}; + +static struct gpio_event_direct_entry trout_keypad_nav_map[] = { + { TROUT_POWER_KEY, KEY_POWER }, + { TROUT_GPIO_CAM_BTN_STEP1_N, KEY_CAMERA-1 }, //steal KEY_HP + { TROUT_GPIO_CAM_BTN_STEP2_N, KEY_CAMERA }, +}; + +static struct gpio_event_direct_entry trout_keypad_nav_map_evt2[] = { + { TROUT_POWER_KEY, KEY_END }, + { TROUT_GPIO_CAM_BTN_STEP1_N, KEY_CAMERA-1 }, //steal KEY_HP + { TROUT_GPIO_CAM_BTN_STEP2_N, KEY_CAMERA }, +}; + +static struct gpio_event_input_info trout_keypad_nav_info = { + .info.func = gpio_event_input_func, + .flags = 0, + .type = EV_KEY, + .keymap = trout_keypad_nav_map, + .keymap_size = ARRAY_SIZE(trout_keypad_nav_map) +}; + +static struct gpio_event_direct_entry trout_keypad_switch_map[] = { + { TROUT_GPIO_SLIDING_DET, SW_LID } +}; + +static struct gpio_event_input_info trout_keypad_switch_info = { + .info.func = gpio_event_input_func, + .flags = 0, + .type = EV_SW, + .keymap = trout_keypad_switch_map, + .keymap_size = ARRAY_SIZE(trout_keypad_switch_map) +}; + +static struct gpio_event_info *trout_keypad_info[] = { + &trout_keypad_matrix_info.info, + &trout_keypad_nav_info.info, + &trout_keypad_switch_info.info, +}; + +static struct gpio_event_platform_data trout_keypad_data = { + .name = "trout-keypad", + .info = trout_keypad_info, + .info_count = ARRAY_SIZE(trout_keypad_info) +}; + +static struct platform_device trout_keypad_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = 0, + .dev = { + .platform_data = &trout_keypad_data, + }, +}; + +static int __init trout_init_keypad(void) +{ + if (!machine_is_trout()) + return 0; + + switch (system_rev) { + case 0: + /* legacy default keylayout */ + break; + case 1: + /* v1 has a new keyboard layout */ + trout_keypad_matrix_info.keymap = trout_keymap_evt2_1; + trout_keypad_matrix_info.output_gpios = trout_col_gpios_evt2; + trout_keypad_matrix_info.input_gpios = trout_row_gpios_evt2; + + /* v1 has new direct keys */ + trout_keypad_nav_info.keymap = trout_keypad_nav_map_evt2; + trout_keypad_nav_info.keymap_size = ARRAY_SIZE(trout_keypad_nav_map_evt2); + + /* userspace needs to know about these changes as well */ + trout_keypad_data.name = "trout-keypad-v2"; + break; + default: /* 2, 3, 4 currently */ + /* v2 has a new keyboard layout */ + trout_keypad_matrix_info.keymap = trout_keymap_evt2_2; + trout_keypad_matrix_info.output_gpios = trout_col_gpios_evt2; + trout_keypad_matrix_info.input_gpios = trout_row_gpios_evt2; + + /* v2 has new direct keys */ + trout_keypad_nav_info.keymap = trout_keypad_nav_map_evt2; + trout_keypad_nav_info.keymap_size = ARRAY_SIZE(trout_keypad_nav_map_evt2); + + /* userspace needs to know about these changes as well */ + if (!strcmp(keycaps, "qwertz")) { + trout_keypad_data.name = "trout-keypad-qwertz"; + } else { + trout_keypad_data.name = "trout-keypad-v3"; + } + break; + } + return platform_device_register(&trout_keypad_device); +} + +device_initcall(trout_init_keypad); + From 6283abf0cd18c838ecbc4bd713dfc0bc8bf374b2 Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Date: Mon, 29 Sep 2008 17:51:38 -0700 Subject: [PATCH 0197/2556] [ARM] msm: trout: obtain calibration data for the compass sensor Extracts compass calibraton from the ATAGs set up by the bootloader Signed-off-by: Rebecca Schultz --- arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/htc_akm_cal.c | 64 +++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 arch/arm/mach-msm/htc_akm_cal.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index c38ce01fe42a7..9b788868718f8 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_MSM_CPU_FREQ_SCREEN) += cpufreq.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout-keypad.o +obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o diff --git a/arch/arm/mach-msm/htc_akm_cal.c b/arch/arm/mach-msm/htc_akm_cal.c new file mode 100644 index 0000000000000..943083fe0fbe8 --- /dev/null +++ b/arch/arm/mach-msm/htc_akm_cal.c @@ -0,0 +1,64 @@ +/* arch/arm/mach-msm/htc_akm_cal.c + * + * Code to extract compass calibration information from ATAG set up + * by the bootloader. + * + * Copyright (C) 2007-2008 HTC Corporation + * Author: Farmer Tseng + * + * 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 + +/* configuration tags specific to AKM8976 */ +#define ATAG_AKM8976 0x89768976 /* AKM8976 */ + +#define MAX_CALI_SIZE 0x1000U + +static char akm_cal_ram[MAX_CALI_SIZE]; + +char *get_akm_cal_ram(void) +{ + return(akm_cal_ram); +} +EXPORT_SYMBOL(get_akm_cal_ram); + +static int __init parse_tag_akm(const struct tag *tag) +{ + unsigned char *dptr = (unsigned char *)(&tag->u); + unsigned size; + + size = min((tag->hdr.size - 2) * sizeof(__u32), MAX_CALI_SIZE); + + printk(KERN_INFO "AKM Data size = %d , 0x%x, size = %d\n", + tag->hdr.size, tag->hdr.tag, size); + +#ifdef ATAG_COMPASS_DEBUG + unsigned i; + unsigned char *ptr; + + ptr = dptr; + printk(KERN_INFO + "AKM Data size = %d , 0x%x\n", + tag->hdr.size, tag->hdr.tag); + for (i = 0; i < size; i++) + printk(KERN_INFO "%02x ", *ptr++); +#endif + memcpy((void *)akm_cal_ram, (void *)dptr, size); + return 0; +} + +__tagtable(ATAG_AKM8976, parse_tag_akm); From a3ddb7aa778c1fa86760308d659a2bd094986e5a Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Mon, 29 Sep 2008 17:55:10 -0700 Subject: [PATCH 0198/2556] [ARM] msm: trout: Code to extract wifi calibration data from ATAGs Signed-off-by: Rebecca Schultz --- arch/arm/mach-msm/Makefile | 2 +- arch/arm/mach-msm/htc_wifi_nvs.c | 56 ++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-msm/htc_wifi_nvs.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 9b788868718f8..9d9d01c7668f9 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -40,7 +40,7 @@ obj-$(CONFIG_MSM_CPU_FREQ_SCREEN) += cpufreq.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout-keypad.o -obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o +obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o htc_wifi_nvs.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o diff --git a/arch/arm/mach-msm/htc_wifi_nvs.c b/arch/arm/mach-msm/htc_wifi_nvs.c new file mode 100644 index 0000000000000..95b8c3bbae9d8 --- /dev/null +++ b/arch/arm/mach-msm/htc_wifi_nvs.c @@ -0,0 +1,56 @@ +/* arch/arm/mach-msm/htc_wifi_nvs.c + * + * Code to extract WiFi calibration information from ATAG set up + * by the bootloader. + * + * Copyright (C) 2008 Google, Inc. + * Author: Dmitry Shmidt + * + * 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 + +/* configuration tags specific to msm */ +#define ATAG_MSM_WIFI 0x57494649 /* MSM WiFi */ + +#define MAX_NVS_SIZE 0x800U +static unsigned char wifi_nvs_ram[MAX_NVS_SIZE]; + +unsigned char *get_wifi_nvs_ram( void ) +{ + return( wifi_nvs_ram ); +} +EXPORT_SYMBOL(get_wifi_nvs_ram); + +static int __init parse_tag_msm_wifi(const struct tag *tag) +{ + unsigned char *dptr = (unsigned char *)(&tag->u); + unsigned size; + + size = min((tag->hdr.size - 2) * sizeof(__u32), MAX_NVS_SIZE); +#ifdef ATAG_MSM_WIFI_DEBUG + unsigned i; + + printk("WiFi Data size = %d , 0x%x\n", tag->hdr.size, tag->hdr.tag); + for(i=0;( i < size );i++) { + printk("%02x ", *dptr++); + } +#endif + memcpy( (void *)wifi_nvs_ram, (void *)dptr, size ); + return 0; +} + +__tagtable(ATAG_MSM_WIFI, parse_tag_msm_wifi); From 1045b13f86a5fbbe0ba36aa6c02e50016874be29 Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Date: Mon, 29 Sep 2008 17:58:03 -0700 Subject: [PATCH 0199/2556] [ARM] msm: trout: htc acoustic driver Allows userspace to set acoustic settings via memory mapped region Signed-off-by: Rebecca Schultz --- arch/arm/mach-msm/Makefile | 2 +- arch/arm/mach-msm/htc_acoustic.c | 99 ++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-msm/htc_acoustic.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 9d9d01c7668f9..e63c988786758 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -40,7 +40,7 @@ obj-$(CONFIG_MSM_CPU_FREQ_SCREEN) += cpufreq.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout-keypad.o -obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o htc_wifi_nvs.o +obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o diff --git a/arch/arm/mach-msm/htc_acoustic.c b/arch/arm/mach-msm/htc_acoustic.c new file mode 100644 index 0000000000000..9fb92f588c0b8 --- /dev/null +++ b/arch/arm/mach-msm/htc_acoustic.c @@ -0,0 +1,99 @@ +/* arch/arm/mach-msm/htc_acoustic.c + * + * Copyright (C) 2007-2008 HTC Corporation + * Author: Laurence Chen + * + * 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 + +#define HTC_ACOUSTIC_TABLE_BASE_PHY_ADDR_START (0x01FE0000) +#define HTC_ACOUSTIC_TABLE_SIZE (0x10000) + +#define ACOUSTICE(x...) printk(KERN_ERR "[ACOUSTIC] " x) + +static int acoustic_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long pgoff; + size_t size = vma->vm_end - vma->vm_start; + + if (vma->vm_pgoff != 0) + return -EINVAL; + + if (size <= HTC_ACOUSTIC_TABLE_SIZE) + pgoff = HTC_ACOUSTIC_TABLE_BASE_PHY_ADDR_START >> PAGE_SHIFT; + else + return -EINVAL; + + vma->vm_flags |= VM_IO | VM_RESERVED; + + if (io_remap_pfn_range(vma, vma->vm_start, pgoff, + size, vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} + +static int acoustic_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int acoustic_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static struct file_operations acoustic_fops = { + .owner = THIS_MODULE, + .open = acoustic_open, + .release = acoustic_release, + .mmap = acoustic_mmap, +}; + +static struct miscdevice acoustic_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "htc-acoustic", + .fops = &acoustic_fops, +}; + +static int __init acoustic_init(void) +{ + int ret; + + ret = misc_register(&acoustic_misc); + if (ret < 0) { + ACOUSTICE("failed to register misc device!\n"); + return ret; + } + + return 0; +} + +static void __exit acoustic_exit(void) +{ + int ret; + + ret = misc_deregister(&acoustic_misc); + if (ret < 0) + ACOUSTICE("failed to unregister misc device!\n"); +} + +module_init(acoustic_init); +module_exit(acoustic_exit); + +MODULE_AUTHOR("Laurence Chen "); +MODULE_DESCRIPTION("HTC acoustic driver"); +MODULE_LICENSE("GPL"); From 12863e74140bc9dd25162221bc61a59ffacaeb66 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Mon, 29 Sep 2008 18:19:37 -0700 Subject: [PATCH 0200/2556] [ARM] msm: trout: htc2wire driver to support htc headsets Signed-off-by: Rebecca Schultz --- arch/arm/mach-msm/Kconfig | 8 + arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/board-trout-h2w.c | 548 ++++++++++++++++++++++++++++ 3 files changed, 557 insertions(+) create mode 100644 arch/arm/mach-msm/board-trout-h2w.c diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 680ae9a32c2e8..c3f0741790ae2 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -184,6 +184,14 @@ config MSM_SMD_PKG3 config MSM_PROC_COMM bool +config TROUT_H2W + tristate "HTC 2 Wire detection driver" + default n + help + Provides support for detecting HTC 2 wire devices, such as wired + headset, on the trout platform. Can be used with the msm serial + debugger, but not with serial console. + choice prompt "Default Timer" default MSM7X00A_USE_GP_TIMER diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index e63c988786758..312772a7bf273 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o d obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout-keypad.o obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o +obj-$(CONFIG_TROUT_H2W) += board-trout-h2w.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o diff --git a/arch/arm/mach-msm/board-trout-h2w.c b/arch/arm/mach-msm/board-trout-h2w.c new file mode 100644 index 0000000000000..f8282b4e4899f --- /dev/null +++ b/arch/arm/mach-msm/board-trout-h2w.c @@ -0,0 +1,548 @@ +/* + * H2W device detection driver. + * + * Copyright (C) 2008 HTC Corporation. + * Copyright (C) 2008 Google, Inc. + * + * Authors: + * Laurence Chen + * Nick Pelly + * + * 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. + * + */ + +/* For detecting HTC 2 Wire devices, such as wired headset. + + Logically, the H2W driver is always present, and H2W state (hi->state) + indicates what is currently plugged into the H2W interface. + + When the headset is plugged in, CABLE_IN1 is pulled low. When the headset + button is pressed, CABLE_IN2 is pulled low. These two lines are shared with + the TX and RX (respectively) of UART3 - used for serial debugging. + + This headset driver keeps the CPLD configured as UART3 for as long as + possible, so that we can do serial FIQ debugging even when the kernel is + locked and this driver no longer runs. So it only configures the CPLD to + GPIO while the headset is plugged in, and for 10ms during detection work. + + Unfortunately we can't leave the CPLD as UART3 while a headset is plugged + in, UART3 is pullup on TX but the headset is pull-down, causing a 55 mA + drain on trout. + + The headset detection work involves setting CPLD to GPIO, and then pulling + CABLE_IN1 high with a stronger pullup than usual. A H2W headset will still + pull this line low, whereas other attachments such as a serial console + would get pulled up by this stronger pullup. + + Headset insertion/removal causes UEvent's to be sent, and + /sys/class/switch/h2w/state to be updated. + + Button presses are interpreted as input event (KEY_MEDIA). Button presses + are ignored if the headset is plugged in, so the buttons on 11 pin -> 3.5mm + jack adapters do not work until a headset is plugged into the adapter. This + is to avoid serial RX traffic causing spurious button press events. + + We tend to check the status of CABLE_IN1 a few more times than strictly + necessary during headset detection, to avoid spurious headset insertion + events caused by serial debugger TX traffic. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-trout.h" + +#ifdef CONFIG_DEBUG_TROUT_H2W +#define H2W_DBG(fmt, arg...) printk(KERN_INFO "[H2W] %s " fmt "\n", __FUNCTION__, ## arg) +#else +#define H2W_DBG(fmt, arg...) do {} while (0) +#endif + +static struct workqueue_struct *g_detection_work_queue; +static void detection_work(struct work_struct *work); +static DECLARE_WORK(g_detection_work, detection_work); +enum { + NO_DEVICE = 0, + HTC_HEADSET = 1, +}; + +enum { + UART3 = 0, + GPIO = 1, +}; + +struct h2w_info { + struct switch_dev sdev; + struct input_dev *input; + + atomic_t btn_state; + int ignore_btn; + + unsigned int irq; + unsigned int irq_btn; + + struct hrtimer timer; + ktime_t debounce_time; + + struct hrtimer btn_timer; + ktime_t btn_debounce_time; +}; +static struct h2w_info *hi; + +static ssize_t trout_h2w_print_name(struct switch_dev *sdev, char *buf) +{ + switch (switch_get_state(&hi->sdev)) { + case NO_DEVICE: + return sprintf(buf, "No Device\n"); + case HTC_HEADSET: + return sprintf(buf, "Headset\n"); + } + return -EINVAL; +} + +static void configure_cpld(int route) +{ + H2W_DBG(" route = %s", route == UART3 ? "UART3" : "GPIO"); + switch (route) { + case UART3: + gpio_set_value(TROUT_GPIO_H2W_SEL0, 0); + gpio_set_value(TROUT_GPIO_H2W_SEL1, 1); + break; + case GPIO: + gpio_set_value(TROUT_GPIO_H2W_SEL0, 0); + gpio_set_value(TROUT_GPIO_H2W_SEL1, 0); + break; + } +} + +static void button_pressed(void) +{ + H2W_DBG(""); + atomic_set(&hi->btn_state, 1); + input_report_key(hi->input, KEY_MEDIA, 1); + input_sync(hi->input); +} + +static void button_released(void) +{ + H2W_DBG(""); + atomic_set(&hi->btn_state, 0); + input_report_key(hi->input, KEY_MEDIA, 0); + input_sync(hi->input); +} + +#ifdef CONFIG_MSM_SERIAL_DEBUGGER +extern void msm_serial_debug_enable(int); +#endif + +static void insert_headset(void) +{ + unsigned long irq_flags; + + H2W_DBG(""); + + switch_set_state(&hi->sdev, HTC_HEADSET); + configure_cpld(GPIO); + +#ifdef CONFIG_MSM_SERIAL_DEBUGGER + msm_serial_debug_enable(false); +#endif + + + /* On some non-standard headset adapters (usually those without a + * button) the btn line is pulled down at the same time as the detect + * line. We can check here by sampling the button line, if it is + * low then it is probably a bad adapter so ignore the button. + * If the button is released then we stop ignoring the button, so that + * the user can recover from the situation where a headset is plugged + * in with button held down. + */ + hi->ignore_btn = !gpio_get_value(TROUT_GPIO_CABLE_IN2); + + /* Enable button irq */ + local_irq_save(irq_flags); + enable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + + hi->debounce_time = ktime_set(0, 20000000); /* 20 ms */ +} + +static void remove_headset(void) +{ + unsigned long irq_flags; + + H2W_DBG(""); + + switch_set_state(&hi->sdev, NO_DEVICE); + configure_cpld(UART3); + + /* Disable button */ + local_irq_save(irq_flags); + disable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + + if (atomic_read(&hi->btn_state)) + button_released(); + + hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ +} + +static void detection_work(struct work_struct *work) +{ + unsigned long irq_flags; + int clk, cable_in1; + + H2W_DBG(""); + + if (gpio_get_value(TROUT_GPIO_CABLE_IN1) != 0) { + /* Headset not plugged in */ + if (switch_get_state(&hi->sdev) == HTC_HEADSET) + remove_headset(); + return; + } + + /* Something plugged in, lets make sure its a headset */ + + /* Switch CPLD to GPIO to do detection */ + configure_cpld(GPIO); + /* Disable headset interrupt while detecting.*/ + local_irq_save(irq_flags); + disable_irq(hi->irq); + local_irq_restore(irq_flags); + + /* Set GPIO_CABLE_IN1 as output high */ + gpio_direction_output(TROUT_GPIO_CABLE_IN1, 1); + /* Delay 10ms for pin stable. */ + msleep(10); + /* Save H2W_CLK */ + clk = gpio_get_value(TROUT_GPIO_H2W_CLK_GPI); + /* Set GPIO_CABLE_IN1 as input */ + gpio_direction_input(TROUT_GPIO_CABLE_IN1); + + /* Restore IRQs */ + local_irq_save(irq_flags); + enable_irq(hi->irq); + local_irq_restore(irq_flags); + + cable_in1 = gpio_get_value(TROUT_GPIO_CABLE_IN1); + + if (cable_in1 == 0 && clk == 0) { + if (switch_get_state(&hi->sdev) == NO_DEVICE) + insert_headset(); + } else { + configure_cpld(UART3); + H2W_DBG("CABLE_IN1 was low, but not a headset " + "(recent cable_in1 = %d, clk = %d)", cable_in1, clk); + } +} + +static enum hrtimer_restart button_event_timer_func(struct hrtimer *data) +{ + H2W_DBG(""); + + if (switch_get_state(&hi->sdev) == HTC_HEADSET) { + if (gpio_get_value(TROUT_GPIO_CABLE_IN2)) { + if (hi->ignore_btn) + hi->ignore_btn = 0; + else if (atomic_read(&hi->btn_state)) + button_released(); + } else { + if (!hi->ignore_btn && !atomic_read(&hi->btn_state)) + button_pressed(); + } + } + + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart detect_event_timer_func(struct hrtimer *data) +{ + H2W_DBG(""); + + queue_work(g_detection_work_queue, &g_detection_work); + return HRTIMER_NORESTART; +} + +static irqreturn_t detect_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + H2W_DBG(""); + do { + value1 = gpio_get_value(TROUT_GPIO_CABLE_IN1); + set_irq_type(hi->irq, value1 ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(TROUT_GPIO_CABLE_IN1); + } while (value1 != value2 && retry_limit-- > 0); + + H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit)); + + if ((switch_get_state(&hi->sdev) == NO_DEVICE) ^ value2) { + if (switch_get_state(&hi->sdev) == HTC_HEADSET) + hi->ignore_btn = 1; + /* Do the rest of the work in timer context */ + hrtimer_start(&hi->timer, hi->debounce_time, HRTIMER_MODE_REL); + } + + return IRQ_HANDLED; +} + +static irqreturn_t button_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + H2W_DBG(""); + do { + value1 = gpio_get_value(TROUT_GPIO_CABLE_IN2); + set_irq_type(hi->irq_btn, value1 ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(TROUT_GPIO_CABLE_IN2); + } while (value1 != value2 && retry_limit-- > 0); + + H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit)); + + hrtimer_start(&hi->btn_timer, hi->btn_debounce_time, HRTIMER_MODE_REL); + + return IRQ_HANDLED; +} + +#if defined(CONFIG_DEBUG_FS) +static void h2w_debug_set(void *data, u64 val) +{ + switch_set_state(&hi->sdev, (int)val); +} + +static u64 h2w_debug_get(void *data) +{ + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(h2w_debug_fops, h2w_debug_get, h2w_debug_set, "%llu\n"); +static int __init h2w_debug_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("h2w", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("state", 0644, dent, NULL, &h2w_debug_fops); + + return 0; +} + +device_initcall(h2w_debug_init); +#endif + +static int trout_h2w_probe(struct platform_device *pdev) +{ + int ret; + unsigned long irq_flags; + + printk(KERN_INFO "H2W: Registering H2W (headset) driver\n"); + hi = kzalloc(sizeof(struct h2w_info), GFP_KERNEL); + if (!hi) + return -ENOMEM; + + atomic_set(&hi->btn_state, 0); + hi->ignore_btn = 0; + + hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ + hi->btn_debounce_time = ktime_set(0, 10000000); /* 10 ms */ + hi->sdev.name = "h2w"; + hi->sdev.print_name = trout_h2w_print_name; + + ret = switch_dev_register(&hi->sdev); + if (ret < 0) + goto err_switch_dev_register; + + g_detection_work_queue = create_workqueue("detection"); + if (g_detection_work_queue == NULL) { + ret = -ENOMEM; + goto err_create_work_queue; + } + + ret = gpio_request(TROUT_GPIO_CABLE_IN1, "h2w_detect"); + if (ret < 0) + goto err_request_detect_gpio; + + ret = gpio_request(TROUT_GPIO_CABLE_IN2, "h2w_button"); + if (ret < 0) + goto err_request_button_gpio; + + ret = gpio_direction_input(TROUT_GPIO_CABLE_IN1); + if (ret < 0) + goto err_set_detect_gpio; + + ret = gpio_direction_input(TROUT_GPIO_CABLE_IN2); + if (ret < 0) + goto err_set_button_gpio; + + hi->irq = gpio_to_irq(TROUT_GPIO_CABLE_IN1); + if (hi->irq < 0) { + ret = hi->irq; + goto err_get_h2w_detect_irq_num_failed; + } + + hi->irq_btn = gpio_to_irq(TROUT_GPIO_CABLE_IN2); + if (hi->irq_btn < 0) { + ret = hi->irq_btn; + goto err_get_button_irq_num_failed; + } + + /* Set CPLD MUX to H2W <-> CPLD GPIO */ + configure_cpld(UART3); + /* Set the CPLD connected H2W GPIO's to input */ + gpio_set_value(TROUT_GPIO_H2W_CLK_DIR, 0); + gpio_set_value(TROUT_GPIO_H2W_DAT_DIR, 0); + + hrtimer_init(&hi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hi->timer.function = detect_event_timer_func; + hrtimer_init(&hi->btn_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hi->btn_timer.function = button_event_timer_func; + + local_irq_save(irq_flags); + + ret = request_irq(hi->irq, detect_irq_handler, + IRQF_TRIGGER_LOW, "h2w_detect", NULL); + if (ret < 0) + goto err_request_detect_irq; + + ret = request_irq(hi->irq_btn, button_irq_handler, + IRQF_TRIGGER_LOW, "h2w_button", NULL); + if (ret < 0) + goto err_request_h2w_headset_button_irq; + + ret = set_irq_wake(hi->irq, 1); + if (ret < 0) + goto err_request_input_dev; + ret = set_irq_wake(hi->irq_btn, 1); + if (ret < 0) + goto err_request_input_dev; + + /* Disable button until plugged in */ + disable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + + hi->input = input_allocate_device(); + if (!hi->input) { + ret = -ENOMEM; + goto err_request_input_dev; + } + + hi->input->name = "h2w headset"; + hi->input->evbit[0] = BIT_MASK(EV_KEY); + hi->input->keybit[BIT_WORD(KEY_MEDIA)] = BIT_MASK(KEY_MEDIA); + + ret = input_register_device(hi->input); + if (ret < 0) + goto err_register_input_dev; + + return 0; + +err_register_input_dev: + input_free_device(hi->input); +err_request_input_dev: + free_irq(hi->irq_btn, 0); +err_request_h2w_headset_button_irq: + free_irq(hi->irq, 0); +err_request_detect_irq: + local_irq_restore(irq_flags); +err_get_button_irq_num_failed: +err_get_h2w_detect_irq_num_failed: +err_set_button_gpio: +err_set_detect_gpio: + gpio_free(TROUT_GPIO_CABLE_IN2); +err_request_button_gpio: + gpio_free(TROUT_GPIO_CABLE_IN1); +err_request_detect_gpio: + destroy_workqueue(g_detection_work_queue); +err_create_work_queue: + switch_dev_unregister(&hi->sdev); +err_switch_dev_register: + printk(KERN_ERR "H2W: Failed to register driver\n"); + + return ret; +} + +static int trout_h2w_remove(struct platform_device *pdev) +{ + H2W_DBG(""); + if (switch_get_state(&hi->sdev)) + remove_headset(); + input_unregister_device(hi->input); + gpio_free(TROUT_GPIO_CABLE_IN2); + gpio_free(TROUT_GPIO_CABLE_IN1); + free_irq(hi->irq_btn, 0); + free_irq(hi->irq, 0); + destroy_workqueue(g_detection_work_queue); + switch_dev_unregister(&hi->sdev); + + return 0; +} + +static struct platform_device trout_h2w_device = { + .name = "trout-h2w", +}; + +static struct platform_driver trout_h2w_driver = { + .probe = trout_h2w_probe, + .remove = trout_h2w_remove, + .driver = { + .name = "trout-h2w", + .owner = THIS_MODULE, + }, +}; + +static int __init trout_h2w_init(void) +{ + int ret; + H2W_DBG(""); + ret = platform_driver_register(&trout_h2w_driver); + if (ret) + return ret; + return platform_device_register(&trout_h2w_device); +} + +static void __exit trout_h2w_exit(void) +{ + platform_device_unregister(&trout_h2w_device); + platform_driver_unregister(&trout_h2w_driver); +} + +module_init(trout_h2w_init); +module_exit(trout_h2w_exit); + +MODULE_AUTHOR("Laurence Chen "); +MODULE_DESCRIPTION("HTC 2 Wire detection driver for trout"); +MODULE_LICENSE("GPL"); From 77cbcf97d56d67b2611f149a0f699cce2fddfb83 Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Date: Tue, 30 Sep 2008 10:26:07 -0700 Subject: [PATCH 0201/2556] mt9t013: MT9T013 HTC camera sensor driver Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-halibut.c | 9 + arch/arm/mach-msm/board-trout.c | 72 +- arch/arm/mach-msm/include/mach/board.h | 6 + drivers/i2c/chips/Kconfig | 7 + drivers/i2c/chips/Makefile | 1 + drivers/i2c/chips/mt9t013.c | 1249 ++++++++++++++++++++++++ include/linux/mt9t013.h | 140 +++ 7 files changed, 1483 insertions(+), 1 deletion(-) create mode 100644 drivers/i2c/chips/mt9t013.c create mode 100644 include/linux/mt9t013.h diff --git a/arch/arm/mach-msm/board-halibut.c b/arch/arm/mach-msm/board-halibut.c index 2377461b20d58..f738db6ef7260 100644 --- a/arch/arm/mach-msm/board-halibut.c +++ b/arch/arm/mach-msm/board-halibut.c @@ -35,6 +35,7 @@ #include #include +#include #include "devices.h" @@ -58,6 +59,13 @@ static struct platform_device smc91x_device = { .resource = smc91x_resources, }; +static struct i2c_board_info i2c_devices[] = { + { + I2C_BOARD_INFO("mt9t013", 0x78>>1), + /* .irq = TROUT_GPIO_TO_INT(TROUT_GPIO_CAM_BTN_STEP1_N), */ + }, +}; + static struct platform_device *devices[] __initdata = { #if !defined(CONFIG_MSM_SERIAL_DEBUGGER) &msm_device_uart3, @@ -95,6 +103,7 @@ static void __init halibut_init(void) #endif msm_acpu_clock_init(&halibut_clock_data); platform_add_devices(devices, ARRAY_SIZE(devices)); + i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); } static void __init halibut_fixup(struct machine_desc *desc, struct tag *tags, diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index df357be8eb8f9..38ddbee8ce298 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -239,6 +239,10 @@ static struct i2c_board_info i2c_devices[] = { .platform_data = trout_ts_data, .irq = TROUT_GPIO_TO_INT(TROUT_GPIO_TP_ATT_N) }, + { + I2C_BOARD_INFO("elan-touch", 0x10), + .irq = TROUT_GPIO_TO_INT(TROUT_GPIO_TP_ATT_N), + }, { I2C_BOARD_INFO("akm8976", 0x1C), .platform_data = &compass_platform_data, @@ -247,6 +251,10 @@ static struct i2c_board_info i2c_devices[] = { { I2C_BOARD_INFO("pca963x", 0x62), }, + { + I2C_BOARD_INFO("mt9t013", 0x6C >> 1), + /* .irq = TROUT_GPIO_TO_INT(TROUT_GPIO_CAM_BTN_STEP1_N), */ + }, }; static struct android_pmem_platform_data android_pmem_pdata = { @@ -470,7 +478,18 @@ static struct platform_device trout_ram_console_device = { .resource = trout_ram_console_resource, }; -extern int trout_init_mmc(unsigned int); +static struct msm_camera_device_platform_data msm_camera_device = { + .sensor_reset = 108, + .sensor_pwd = 85, + .vcm_pwd = TROUT_GPIO_VCM_PWDN, +}; + +static struct platform_device trout_camera = { + .name = "camera", + .dev = { + .platform_data = &msm_camera_device, + }, +}; static struct platform_device *devices[] __initdata = { &msm_device_smd, @@ -494,6 +513,7 @@ static struct platform_device *devices[] __initdata = { &android_pmem_gpu1_device, &android_pmem_camera_device, &trout_ram_console_device, + &trout_camera, }; extern struct sys_timer msm_timer; @@ -563,6 +583,43 @@ static uint32_t gpio_table[] = { PCOM_GPIO_CFG(46, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* TX */ }; + +static uint32_t camera_off_gpio_table[] = { + /* CAMERA */ + PCOM_GPIO_CFG(2, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* PCLK */ + PCOM_GPIO_CFG(13, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* HSYNC_IN */ + PCOM_GPIO_CFG(14, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* VSYNC_IN */ + PCOM_GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* MCLK */ +}; + +static uint32_t camera_on_gpio_table[] = { + /* CAMERA */ + PCOM_GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_16MA), /* PCLK */ + PCOM_GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */ + PCOM_GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */ + PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_16MA), /* MCLK */ +}; + static void config_gpio_table(uint32_t *table, int len) { int n; @@ -573,9 +630,22 @@ static void config_gpio_table(uint32_t *table, int len) } } +void config_camera_on_gpios(void) +{ + config_gpio_table(camera_on_gpio_table, + ARRAY_SIZE(camera_on_gpio_table)); +} + +void config_camera_off_gpios(void) +{ + config_gpio_table(camera_off_gpio_table, + ARRAY_SIZE(camera_off_gpio_table)); +} + static void __init config_gpios(void) { config_gpio_table(gpio_table, ARRAY_SIZE(gpio_table)); + config_camera_off_gpios(); } void msm_serial_debug_init(unsigned int base, int irq, diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index 624051a4672b0..2e6bd1fe86833 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -31,6 +31,12 @@ struct msm_acpu_clock_platform_data unsigned long wait_for_irq_khz; }; +struct msm_camera_device_platform_data{ + int sensor_reset; + int sensor_pwd; + int vcm_pwd; +}; + struct clk; extern struct sys_timer msm_timer; diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index 26d4725d8b01a..d9624838e3925 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -12,4 +12,11 @@ config SENSORS_AKM8976 help AKM8976 Compass Driver implemented by HTC. +config SENSORS_MT9T013 + tristate "MT9T013 Camera Driver" + depends on I2C + default y + help + MT9T013 Camera Driver implemented by HTC. + endmenu diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index 6b5a4ff97c6c1..8a5334778389f 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -11,6 +11,7 @@ # obj-$(CONFIG_SENSORS_AKM8976) += akm8976.o +obj-$(CONFIG_SENSORS_MT9T013) += mt9t013.o ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) EXTRA_CFLAGS += -DDEBUG diff --git a/drivers/i2c/chips/mt9t013.c b/drivers/i2c/chips/mt9t013.c new file mode 100644 index 0000000000000..8d94b3e22796a --- /dev/null +++ b/drivers/i2c/chips/mt9t013.c @@ -0,0 +1,1249 @@ +/* + * Copyright (C) 2007-2008 HTC Corporation. + * + * 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 +#include +#include +#include +#include /* define ioctls */ +#include + +#define ALLOW_USPACE_RW 0 + +static const uint32_t fps_divider = 1; + +#define AF_I2C_ID 0x18 /* actuator's slave address */ + +extern void config_camera_on_gpios(void); +extern void config_camera_off_gpios(void); + +static struct i2c_client *pclient; + +/* we need this to set the clock rate */ +static struct clk *vfe_clk; + +/* camif clocks */ +static struct clk *vfe_mdc_clk; +static struct clk *mdc_clk; + +static int mdc_clk_enabled; +static int vfe_mdc_clk_enabled; +static int vfe_clk_enabled; +static int opened; +static int pclk_set; + +static const struct mt9t013_reg_pat mt9t013_reg_pattern = { .reg = { + { /* preview 2x2 binning 20fps, pclk MHz, MCLK 24MHz */ + 10, //vt_pix_clk_div REG=0x0300 // update get_snapshot_fps if this change + 1, //vt_sys_clk_div REG=0x0302 // update get_snapshot_fps if this change + 3,//2, //pre_pll_clk_div REG=0x0304 // update get_snapshot_fps if this change + 80,//40, //pll_multiplier REG=0x0306 60 for 30fps preview, 40 for 20fps preview + 10, //op_pix_clk_div REG=0x0308 + 1, //op_sys_clk_div REG=0x030A + 16, //scale_m REG=0x0404 + 0x0111, //row_speed REG=0x3016 + 8, //x_addr_start REG=0x3004 + 2053, //x_addr_end REG=0x3008 + 8, //y_addr_start REG=0x3002 + 1541, //y_addr_end REG=0x3006 + 0x046C, //read_mode REG=0x3040 + 1024, //x_output_size REG=0x034C + 768, //y_output_size REG=0x034E + 3540,//2616, //line_length_pck REG=0x300C + 861,//916, //frame_length_lines REG=0x300A + 16, //coarse_integration_time REG=0x3012 + 1461 //fine_integration_time REG=0x3014 + }, + { /* snapshot */ + 10, //vt_pix_clk_div REG=0x0300 // update get_snapshot_fps if this change + 1, //vt_sys_clk_div REG=0x0302 // update get_snapshot_fps if this change + 3,//2, //pre_pll_clk_div REG=0x0304 // update get_snapshot_fps if this change + 80,//40, //pll_multiplier REG=0x0306 50 for 15fps snapshot, 40 for 10fps snapshot + 10, //op_pix_clk_div REG=0x0308 + 1, //op_sys_clk_div REG=0x030A + 16, //scale_m REG=0x0404 + 0x0111, //row_speed REG=0x3016 + 8, //0, //x_addr_start REG=0x3004 + 2073, //2061, //x_addr_end REG=0x3008 + 8, //2, //y_addr_start REG=0x3002 + 1551, //1545, //y_addr_end REG=0x3006 + 0x0024, //read_mode REG=0x3040 + 2066, //x_output_size REG=0x034C + 1544, //y_output_size REG=0x034E + 4800,//2952, //line_length_pck REG=0x300C + 1629, //frame_length_lines REG=0x300A + 16, //coarse_integration_time REG=0x3012 + 733 //fine_integration_time REG=0x3014 + } +}}; + +#define MT9T013_MU3M0VC_REG_MODEL_ID 0x0000 +#define MT9T013_MU3M0VC_MODEL_ID 0x2600 +#define REG_GROUPED_PARAMETER_HOLD 0x0104 +#define GROUPED_PARAMETER_HOLD 0x0100 +#define GROUPED_PARAMETER_UPDATE 0x0000 +#define REG_COARSE_INTEGRATION_TIME 0x3012 +#define REG_VT_PIX_CLK_DIV 0x0300 +#define REG_VT_SYS_CLK_DIV 0x0302 +#define REG_PRE_PLL_CLK_DIV 0x0304 +#define REG_PLL_MULTIPLIER 0x0306 +#define REG_OP_PIX_CLK_DIV 0x0308 +#define REG_OP_SYS_CLK_DIV 0x030A +#define REG_SCALE_M 0x0404 +#define REG_FRAME_LENGTH_LINES 0x300A +#define REG_LINE_LENGTH_PCK 0x300C +#define REG_X_ADDR_START 0x3004 +#define REG_Y_ADDR_START 0x3002 +#define REG_X_ADDR_END 0x3008 +#define REG_Y_ADDR_END 0x3006 +#define REG_X_OUTPUT_SIZE 0x034C +#define REG_Y_OUTPUT_SIZE 0x034E +#define REG_FINE_INTEGRATION_TIME 0x3014 +#define REG_ROW_SPEED 0x3016 +#define MT9T013_REG_RESET_REGISTER 0x301A +#define MT9T013_RESET_REGISTER_PWON 0x10CC /*enable paralled and start streaming*/ +#define MT9T013_RESET_REGISTER_PWOFF 0x1008 //0x10C8 /*stop streaming*/ +#define REG_READ_MODE 0x3040 +#define REG_GLOBAL_GAIN 0x305E +#define REG_TEST_PATTERN_MODE 0x3070 + +static struct wake_lock mt9t013_wake_lock; + +static inline void init_suspend(void) +{ + wake_lock_init(&mt9t013_wake_lock, WAKE_LOCK_IDLE, "mt9t013"); +} + +static inline void deinit_suspend(void) +{ + wake_lock_destroy(&mt9t013_wake_lock); +} + +static inline void prevent_suspend(void) +{ + wake_lock(&mt9t013_wake_lock); +} + +static inline void allow_suspend(void) +{ + wake_unlock(&mt9t013_wake_lock); +} + +#define CLK_GET(clk) do { \ + if (!clk) { \ + clk = clk_get(NULL, #clk); \ + printk(KERN_INFO \ + "mt9t013: clk_get(%s): %p\n", #clk, clk); \ + } \ +} while(0) + +DECLARE_MUTEX(sem); + +static struct msm_camera_device_platform_data *cam; + +#define out_dword(addr, val) \ + (*((volatile unsigned long *)(addr)) = ((unsigned long)(val))) + +#define out_dword_masked_ns(io, mask, val, current_reg_content) \ + (void) out_dword(io, ((current_reg_content & (uint32_t)(~(mask))) | \ + ((uint32_t)((val) & (mask))))) + +#define __inpdw(port) (*((volatile uint32_t *) (port))) +#define in_dword_masked(addr, mask) (__inpdw(addr) & (uint32_t)mask ) + +#define HWIO_MDDI_CAMIF_CFG_ADDR MSM_MDC_BASE +#define HWIO_MDDI_CAMIF_CFG_RMSK 0x1fffff +#define HWIO_MDDI_CAMIF_CFG_IN \ + in_dword_masked(HWIO_MDDI_CAMIF_CFG_ADDR, HWIO_MDDI_CAMIF_CFG_RMSK) + +#define HWIO_MDDI_CAMIF_CFG_OUTM(m,v) \ + out_dword_masked_ns(HWIO_MDDI_CAMIF_CFG_ADDR,m,v,HWIO_MDDI_CAMIF_CFG_IN); +#define __msmhwio_outm(hwiosym, mask, val) HWIO_##hwiosym##_OUTM(mask, val) +#define HWIO_OUTM(hwiosym, mask, val) __msmhwio_outm(hwiosym, mask, val) + +#define HWIO_MDDI_CAMIF_CFG_CAM_SEL_BMSK 0x2 +#define HWIO_MDDI_CAMIF_CFG_CAM_PCLK_SRC_SEL_BMSK 0x60000 +#define HWIO_MDDI_CAMIF_CFG_CAM_PCLK_INVERT_BMSK 0x80000 +#define HWIO_MDDI_CAMIF_CFG_CAM_PAD_REG_SW_RESET_BMSK 0x100000 + +#define HWIO_MDDI_CAMIF_CFG_CAM_SEL_SHFT 0x1 +#define HWIO_MDDI_CAMIF_CFG_CAM_PCLK_SRC_SEL_SHFT 0x11 +#define HWIO_MDDI_CAMIF_CFG_CAM_PCLK_INVERT_SHFT 0x13 +#define HWIO_MDDI_CAMIF_CFG_CAM_PAD_REG_SW_RESET_SHFT 0x14 + +#define __msmhwio_shft(hwio_regsym, hwio_fldsym) HWIO_##hwio_regsym##_##hwio_fldsym##_SHFT +#define HWIO_SHFT(hwio_regsym, hwio_fldsym) __msmhwio_shft(hwio_regsym, hwio_fldsym) + +#define __msmhwio_fmsk(hwio_regsym, hwio_fldsym) HWIO_##hwio_regsym##_##hwio_fldsym##_BMSK +#define HWIO_FMSK(hwio_regsym, hwio_fldsym) __msmhwio_fmsk(hwio_regsym, hwio_fldsym) + +#define HWIO_APPS_RESET_ADDR (MSM_CLK_CTL_BASE + 0x00000210) +#define HWIO_APPS_RESET_RMSK 0x1fff +#define HWIO_APPS_RESET_VFE_BMSK 1 +#define HWIO_APPS_RESET_VFE_SHFT 0 +#define HWIO_APPS_RESET_IN in_dword_masked(HWIO_APPS_RESET_ADDR, HWIO_APPS_RESET_RMSK) +#define HWIO_APPS_RESET_OUTM(m,v) out_dword_masked_ns(HWIO_APPS_RESET_ADDR,m,v,HWIO_APPS_RESET_IN) + +struct mt9t013_data { + struct work_struct work; +}; + +static DECLARE_WAIT_QUEUE_HEAD(g_data_ready_wait_queue); + +static int mt9t013_i2c_sensor_init(struct mt9t013_init *init); +static int mt9t013_i2c_sensor_setting(unsigned long arg); +static int mt9t013_i2c_exposure_gain(uint16_t line, uint16_t gain); +static int mt9t013_i2c_move_focus(uint16_t position); +static int mt9t013_i2c_set_default_focus(uint8_t step); +static int mt9t013_i2c_power_up(void); +static int mt9t013_i2c_power_down(void); +static int mt9t013_camif_pad_reg_reset(void); +static int mt9t013_lens_power(int on); + +int mt_i2c_lens_tx_data(unsigned char slave_addr, char* txData, int length) +{ + int rc; + struct i2c_msg msg[] = { + { + .addr = slave_addr, + .flags = 0, + .len = length, + .buf = txData, + }, + }; + +#if 0 + { + int i; + /* printk(KERN_INFO "mt_i2c_lens_tx_data: af i2c client addr = %x," + " register addr = 0x%02x%02x:\n", slave_addr, txData[0], txData[1]); + */ + for (i = 0; i < length - 2; i++) + printk(KERN_INFO "\tdata[%d]: 0x%02x\n", i, txData[i+2]); + } +#endif + + rc = i2c_transfer(pclient->adapter, msg, 1); + if (rc < 0) { + printk(KERN_ERR "mt_i2c_lens_tx_data: i2c_transfer error %d\n", rc); + return rc; + } + return 0; +} + +static int mt9t013_i2c_lens_write(unsigned char slave_addr, unsigned char u_addr, unsigned char u_data) +{ + unsigned char buf[2] = { u_addr, u_data }; + return mt_i2c_lens_tx_data(slave_addr, buf, sizeof(buf)); +} + +static int mt_i2c_rx_data(char* rxData, int length) +{ + int rc; + struct i2c_msg msgs[] = { + { + .addr = pclient->addr, + .flags = 0, + .len = 2, + .buf = rxData, + }, + { + .addr = pclient->addr, + .flags = I2C_M_RD, + .len = length, + .buf = rxData, + }, + }; + + rc = i2c_transfer(pclient->adapter, msgs, 2); + if (rc < 0) { + printk(KERN_ERR "mt9t013: mt_i2c_rx_data error %d\n", rc); + return rc; + } +#if 0 + else { + int i; + for (i = 0; i < length; i++) + printk(KERN_INFO "\tdata[%d]: 0x%02x\n", i, rxData[i]); + } +#endif + + return 0; +} + +int mt_i2c_tx_data(char* txData, int length) +{ + int rc; + + struct i2c_msg msg[] = { + { + .addr = pclient->addr, + .flags = 0, + .len = length, + .buf = txData, + }, + }; + + rc = i2c_transfer(pclient->adapter, msg, 1); + if (rc < 0) { + printk(KERN_ERR "mt9t013: mt_i2c_tx_data error %d\n", rc); + return rc; + } + return 0; +} + +static int mt9t013_i2c_write(unsigned short u_addr, unsigned short u_data) +{ + int rc; + unsigned char buf[4]; + + buf[0] = (u_addr & 0xFF00) >> 8; + buf[1] = u_addr & 0x00FF; + buf[2] = (u_data & 0xFF00) >> 8; + buf[3] = u_data & 0x00FF; + + rc = mt_i2c_tx_data(buf, sizeof(buf)); + if(rc < 0) + printk(KERN_ERR "mt9t013: txdata error %d add:0x%02x data:0x%02x\n", + rc, u_addr, u_data); + return rc; +} + +static int mt9t013_i2c_read(unsigned short u_addr, unsigned short *pu_data) +{ + int rc; + unsigned char buf[2]; + + buf[0] = (u_addr & 0xFF00)>>8; + buf[1] = (u_addr & 0x00FF); + rc = mt_i2c_rx_data(buf, 2); + if (!rc) + *pu_data = buf[0]<<8 | buf[1]; + else printk(KERN_ERR "mt9t013: i2c read failed\n"); + return rc; +} + +static int msm_camio_clk_enable (int clk_type) +{ + struct clk *clk = NULL; + int *enabled = NULL; + + switch (clk_type) { + case CAMIO_VFE_MDC_CLK: + CLK_GET(vfe_mdc_clk); + clk = vfe_mdc_clk; + enabled = &vfe_mdc_clk_enabled; + break; + case CAMIO_MDC_CLK: + CLK_GET(mdc_clk); + clk = mdc_clk; + enabled = &mdc_clk_enabled; + break; + default: + break; + } + + if (clk != NULL && !*enabled) { + int rc = clk_enable(clk); + *enabled = !rc; + return rc; + } + + return -EINVAL; +} + +static int msm_camio_clk_disable(int clk_type) +{ + int rc = 0; + struct clk *clk = NULL; + int *enabled = NULL; + + switch (clk_type) { + case CAMIO_VFE_MDC_CLK: + clk = vfe_mdc_clk; + enabled = &vfe_mdc_clk_enabled; + break; + case CAMIO_MDC_CLK: + clk = mdc_clk; + enabled = &mdc_clk_enabled; + break; + default: + rc = -1; + break; + } + + if (clk != NULL && *enabled) { + clk_disable(clk); + *enabled = 0; + return 0; + } + + return -EINVAL; +} + +static int msm_camio_vfe_clk_enable(void) +{ + CLK_GET(vfe_clk); + if (vfe_clk && !vfe_clk_enabled) { + vfe_clk_enabled = !clk_enable(vfe_clk); + printk(KERN_INFO "mt9t013: enable vfe_clk\n"); + } + return vfe_clk_enabled ? 0 : -EIO; +} + +static int msm_camio_clk_rate_set(int rate) +{ + int rc = msm_camio_vfe_clk_enable(); + if (!rc && vfe_clk_enabled) + rc = clk_set_rate(vfe_clk, rate); + return rc; +} + +static int clk_select(int internal) +{ + int rc = -EIO; + printk(KERN_INFO "mt9t013: clk select %d\n", internal); + CLK_GET(vfe_clk); + if (vfe_clk != NULL) { + extern int clk_set_flags(struct clk *clk, unsigned long flags); + rc = clk_set_flags(vfe_clk, 0x00000100 << internal); + if (!rc && internal) rc = msm_camio_vfe_clk_enable(); + } + return rc; +} + +static void mt9t013_sensor_init(void) +{ + int ret; + printk(KERN_INFO "mt9t013: init\n"); + if (!pclient) + return; + + /*pull hi reset*/ + printk(KERN_INFO "mt9t013: mt9t013_register_init\n"); + ret = gpio_request(cam->sensor_reset, "mt9t013"); + if (!ret) { + gpio_direction_output(cam->sensor_reset, 1); + printk(KERN_INFO "mt9t013: camera sensor_reset set as 1\n"); + } else + printk(KERN_ERR "mt9t013 error: request gpio %d failed: " + "%d\n", cam->sensor_reset, ret); + mdelay(2); + + /* pull down power down */ + ret = gpio_request(cam->sensor_pwd, "mt9t013"); + if (!ret || ret == -EBUSY) + gpio_direction_output(cam->sensor_pwd, 0); + else printk(KERN_ERR "mt913t013 error: request gpio %d failed: " + "%d\n", cam->sensor_pwd, ret); + gpio_free(cam->sensor_pwd); + + /* enable clk */ + msm_camio_clk_enable(CAMIO_VFE_MDC_CLK); + msm_camio_clk_enable(CAMIO_MDC_CLK); + + /* reset CAMIF */ + mt9t013_camif_pad_reg_reset(); + + /* set mclk */ + ret = msm_camio_clk_rate_set(24000000); + if(ret < 0) + printk(KERN_ERR "camio clk rate select error\n"); + mdelay(2); + + /* enable gpio */ + config_camera_on_gpios(); + /* delay 2 ms */ + mdelay(2); + + /* reset sensor sequency */ + gpio_direction_output(cam->sensor_reset, 0); + mdelay(2); + gpio_direction_output(cam->sensor_reset, 1); + gpio_free(cam->sensor_reset); + mdelay(2); + + printk(KERN_INFO "mt9t013: camera sensor init sequence done\n"); +} + +#define CLK_DISABLE_AND_PUT(clk) do { \ + if (clk) { \ + if (clk##_enabled) { \ + printk(KERN_INFO "mt9t013: disabling "#clk"\n");\ + clk_disable(clk); \ + clk##_enabled = 0; \ + } \ + printk(KERN_INFO \ + "mt9t013: clk_put(%s): %p\n", #clk, clk); \ + clk_put(clk); \ + clk = NULL; \ + } \ +} while(0) + +static void mt9t013_sensor_suspend(void) +{ + printk(KERN_INFO "mt9t013: camera sensor suspend sequence\n"); + if (!pclient) { + return; + } + /*disable clk*/ + msm_camio_clk_disable(CAMIO_VFE_MDC_CLK); + msm_camio_clk_disable(CAMIO_MDC_CLK); + CLK_DISABLE_AND_PUT(vfe_clk); /* this matches clk_select(1) */ + /* disable gpios */ + config_camera_off_gpios(); + printk(KERN_INFO "mt9t013: camera sensor suspend sequence done\n"); +} + +static int mt9t013_open(struct inode *ip, struct file *fp) +{ + int rc = -EBUSY; + down(&sem); + printk(KERN_INFO "mt9t013: open\n"); + if (!opened) { + printk(KERN_INFO "mt9t013: prevent collapse on idle\n"); + prevent_suspend(); + config_camera_on_gpios(); + opened = 1; + rc = 0; + } + up(&sem); + return rc; +} + +static int mt9t013_release(struct inode *ip, struct file *fp) +{ + int rc = -EBADF; + printk(KERN_INFO "mt9t013: release\n"); + down(&sem); + if (opened) { + printk(KERN_INFO "mt9t013: release clocks\n"); + CLK_DISABLE_AND_PUT(mdc_clk); + CLK_DISABLE_AND_PUT(vfe_mdc_clk); + CLK_DISABLE_AND_PUT(vfe_clk); + mt9t013_lens_power(0); + mt9t013_i2c_power_down(); + config_camera_off_gpios(); + printk(KERN_INFO "mt9t013: allow collapse on idle\n"); + allow_suspend(); + rc = pclk_set = opened = 0; + } + up(&sem); + return rc; +} + +#undef CLK_DISABLE_AND_PUT + +#define CHECK() ({ \ + if (!mdc_clk_enabled || !vfe_mdc_clk_enabled) { \ + printk(KERN_ERR "mt9t013 error: one or more clocks" \ + " are NULL.\n"); \ + rc = -EIO; \ + } \ + !rc; }) + +static int mt9t013_camif_pad_reg_reset(void) +{ + int rc = clk_select(1); + if(rc < 0) { + printk(KERN_ERR "mt9t013 error switching to internal clock\n"); + return rc; + } + HWIO_OUTM (MDDI_CAMIF_CFG, + HWIO_FMSK (MDDI_CAMIF_CFG, CAM_SEL) | + HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PCLK_SRC_SEL) | + HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PCLK_INVERT), + 1 << HWIO_SHFT (MDDI_CAMIF_CFG, CAM_SEL) | + 3 << HWIO_SHFT (MDDI_CAMIF_CFG, CAM_PCLK_SRC_SEL) | + 0 << HWIO_SHFT (MDDI_CAMIF_CFG, CAM_PCLK_INVERT)); + msleep(10); + HWIO_OUTM (MDDI_CAMIF_CFG, + HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PAD_REG_SW_RESET), + 1 << HWIO_SHFT (MDDI_CAMIF_CFG, + CAM_PAD_REG_SW_RESET)); + msleep(10); + HWIO_OUTM (MDDI_CAMIF_CFG, + HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PAD_REG_SW_RESET), + 0 << HWIO_SHFT (MDDI_CAMIF_CFG, + CAM_PAD_REG_SW_RESET)); + msleep(10); + rc = clk_select(0); /* external */ + if(rc < 0) { + printk(KERN_ERR "mt9t013 error switching to external clock\n"); + return rc; + } + + return rc; +} + +#if ALLOW_USPACE_RW +#define COPY_FROM_USER(size) ({ \ + if (copy_from_user(rwbuf, argp, size)) rc = -EFAULT; \ + !rc; }) +#endif + +static long mt9t013_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int rc = 0; + +#if ALLOW_USPACE_RW + unsigned short addr = 0; + unsigned short data = 0; + char rwbuf[4]; +#endif + + down(&sem); + + switch(cmd) { +#if ALLOW_USPACE_RW + case MT9T013_I2C_IOCTL_W: + if (/* CHECK() && */ COPY_FROM_USER(4)) { + addr = *((unsigned short *)rwbuf); + data = *((unsigned short *)(rwbuf+2)); + rc = mt9t013_i2c_write(addr, data); + } + else printk("mt9t013: write: err %d\n", rc); + break; + + case MT9T013_I2C_IOCTL_R: + if (/* CHECK() && */ COPY_FROM_USER(4)) { + addr = *((unsigned short*) rwbuf); + rc = mt9t013_i2c_read(addr, (unsigned short *)(rwbuf+2)); + if (!rc) { + if (copy_to_user(argp, rwbuf, 4)) { + printk("mt9t013: read: err writeback -EFAULT\n"); + rc = -EFAULT; + } + } + } + else printk("mt9t013: read: err %d\n", rc); + break; + + case MT9T013_I2C_IOCTL_AF_W: + if (/* CHECK() && */ COPY_FROM_USER(3)) + rc = mt9t013_i2c_lens_write(*rwbuf, *(rwbuf + 1), *(rwbuf + 2)); + else printk("mt9t013: af write: err %d\n", rc); + break; +#endif /* ALLOW_USPACE_RW */ + + case MT9T013_I2C_IOCTL_CAMIF_PAD_REG_RESET: + printk(KERN_INFO "mt9t013: CAMIF_PAD_REG_RESET\n"); + if (CHECK()) + rc = mt9t013_camif_pad_reg_reset(); + break; + + case MT9T013_I2C_IOCTL_CAMIF_PAD_REG_RESET_2: + printk(KERN_INFO "mt9t013: CAMIF_PAD_REG_RESET_2 (pclk_set %d)\n", + pclk_set); + if (!pclk_set) + rc = -EIO; + else if (CHECK()) { + HWIO_OUTM (MDDI_CAMIF_CFG, + HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PAD_REG_SW_RESET), + 1 << HWIO_SHFT (MDDI_CAMIF_CFG, + CAM_PAD_REG_SW_RESET)); + msleep(10); + HWIO_OUTM (MDDI_CAMIF_CFG, + HWIO_FMSK (MDDI_CAMIF_CFG, CAM_PAD_REG_SW_RESET), + 0 << HWIO_SHFT (MDDI_CAMIF_CFG, + CAM_PAD_REG_SW_RESET)); + msleep(10); + } + break; + + case MT9T013_I2C_IOCTL_CAMIF_APPS_RESET: + printk(KERN_INFO "mt9t013: CAMIF_APPS_RESET\n"); + if (CHECK()) { + rc = clk_select(1); + if(rc < 0) { + printk(KERN_ERR "mt9t013 error switching to internal clock\n"); + break; + } + HWIO_OUTM (APPS_RESET, + HWIO_FMSK(APPS_RESET,VFE), + 1 << HWIO_SHFT(APPS_RESET,VFE)); + udelay(10); + HWIO_OUTM (APPS_RESET, + HWIO_FMSK(APPS_RESET,VFE), + 0 << HWIO_SHFT(APPS_RESET,VFE)); + udelay(10); + rc = clk_select(0); /* external */ + if(rc < 0) { + printk(KERN_ERR "mt9t013 error switching to external clock\n"); + break; + } + } + break; + + case CAMERA_LENS_POWER_ON: + rc = mt9t013_lens_power(1); + break; + + case CAMERA_LENS_POWER_OFF: + rc = mt9t013_lens_power(0); + break; + + case MT9T013_I2C_IOCTL_CLK_ENABLE: + printk(KERN_INFO "mt9t013: clk enable %ld\n", arg); + rc = msm_camio_clk_enable(arg); + break; + + case MT9T013_I2C_IOCTL_CLK_DISABLE: + printk(KERN_INFO "mt9t013: clk disable %ld\n", arg); + rc = msm_camio_clk_disable(arg); + break; + + case MT9T013_I2C_IOCTL_CLK_SELECT: + printk(KERN_INFO "mt9t013: clk select %ld\n", arg); + rc = clk_select(!!arg); + break; + + case MT9T013_I2C_IOCTL_CLK_FREQ_PROG: + printk(KERN_INFO "mt9t013: clk rate select %ld\n", arg); + rc = msm_camio_clk_rate_set(arg); + break; + + case MT9T013_I2C_IOCTL_GET_REGISTERS: + printk(KERN_INFO "mt9t013: get registers\n"); + if (copy_to_user(argp, &mt9t013_reg_pattern.reg, sizeof(mt9t013_reg_pattern.reg))) + rc = -EFAULT; + break; + + case MT9T013_I2C_IOCTL_SENSOR_SETTING: + printk(KERN_INFO "mt9t013: sensor setting 0x%lx\n", arg); + rc = mt9t013_i2c_sensor_setting(arg); + break; + + case MT9T013_I2C_IOCTL_EXPOSURE_GAIN: { + struct mt9t013_exposure_gain exp; + if (copy_from_user(&exp, argp, sizeof(exp))) { + printk(KERN_ERR "mt9t013: (exposure gain) invalid user pointer\n"); + rc = -EFAULT; + break; + } + rc = mt9t013_i2c_exposure_gain(exp.line, exp.gain); + } + break; + + case MT9T013_I2C_IOCTL_MOVE_FOCUS: + printk(KERN_INFO "mt9t013: move focus %ld\n", arg); + rc = mt9t013_i2c_move_focus((uint16_t)arg); + break; + + case MT9T013_I2C_IOCTL_SET_DEFAULT_FOCUS: + printk(KERN_INFO "mt9t013: set default focus %ld\n", arg); + rc = mt9t013_i2c_set_default_focus((uint8_t)arg); + break; + + case MT9T013_I2C_IOCTL_POWER_DOWN: + rc = mt9t013_i2c_power_down(); + break; + + case MT9T013_I2C_IOCTL_INIT: { + struct mt9t013_init init; + printk(KERN_INFO "mt9t013: init\n"); + if (copy_from_user(&init, argp, sizeof(init))) { + printk(KERN_ERR "mt9t013: (init) invalid user pointer\n"); + rc = -EFAULT; + break; + } + rc = mt9t013_i2c_sensor_init(&init); + if (copy_to_user(argp, &init, sizeof(init))) + rc = -EFAULT; + } + break; + + case CAMERA_CONFIGURE_GPIOS: + case CAMERA_UNCONFIGURE_GPIOS: + break; + + default: + printk(KERN_INFO "mt9t013: unknown ioctl %d\n", cmd); + break; + } + + up(&sem); + + return rc; +} + +#undef CHECK + +static int mt9t013_lens_power(int on) +{ + int rc; + printk(KERN_INFO "mt9t013: lens power %d\n", on); + rc = gpio_request(cam->vcm_pwd, "mt9t013"); + if (!rc) + gpio_direction_output(cam->vcm_pwd, !on); + else printk(KERN_ERR "mt9t013 error: request gpio %d failed:" + " %d\n", cam->vcm_pwd, rc); + gpio_free(cam->vcm_pwd); + return rc; +} + +#define I2C_WRITE(reg,data) if (!mt9t013_i2c_write(reg, data) < 0) return -EIO +#define MT9T013_MU3M0VC_RESET_DELAY_MSECS 66 + +static int mt9t013_i2c_sensor_init(struct mt9t013_init *init) +{ + int rc; + + /* RESET the sensor via I2C register */ + I2C_WRITE(MT9T013_REG_RESET_REGISTER, 0x10cc & 0xfffe); + msleep(MT9T013_MU3M0VC_RESET_DELAY_MSECS); + + if ((rc = mt9t013_i2c_read(MT9T013_MU3M0VC_REG_MODEL_ID, &init->chipid)) < 0) { + printk(KERN_ERR "mt9t013: could not read chip id: %d\n", rc); + return rc; + } + printk(KERN_INFO "mt9t013: chip id: %d\n", init->chipid); + + if (init->chipid != MT9T013_MU3M0VC_MODEL_ID) { + printk(KERN_INFO "mt9t013: chip id %d is invalid\n", + init->chipid); + return -EINVAL; + } + + I2C_WRITE(0x306E, 0x9080); + I2C_WRITE(0x301A, 0x10CC); + I2C_WRITE(0x3064, 0x0805); + msleep(MT9T013_MU3M0VC_RESET_DELAY_MSECS); + + if ((rc = mt9t013_i2c_sensor_setting(CAMSENSOR_REG_INIT | + ((init->preview ? 0 : 1) << 1))) < 0) { + printk(KERN_INFO "mt9t013: failed to configure the sensor\n"); + return rc; + } + + mt9t013_i2c_power_up(); + + return 0; +} + +static int mt9t013_mu3m0vc_set_lc(void) +{ + /* lens shading 85% TL84 */ + I2C_WRITE(0x360A, 0x0290); // P_RD_P0Q0 + I2C_WRITE(0x360C, 0xC92D); // P_RD_P0Q1 + I2C_WRITE(0x360E, 0x0771); // P_RD_P0Q2 + I2C_WRITE(0x3610, 0xE38C); // P_RD_P0Q3 + I2C_WRITE(0x3612, 0xD74F); // P_RD_P0Q4 + I2C_WRITE(0x364A, 0x168C); // P_RD_P1Q0 + I2C_WRITE(0x364C, 0xCACB); // P_RD_P1Q1 + I2C_WRITE(0x364E, 0x8C4C); // P_RD_P1Q2 + I2C_WRITE(0x3650, 0x0BEA); // P_RD_P1Q3 + I2C_WRITE(0x3652, 0xDC0F); // P_RD_P1Q4 + I2C_WRITE(0x368A, 0x70B0); // P_RD_P2Q0 + I2C_WRITE(0x368C, 0x200B); // P_RD_P2Q1 + I2C_WRITE(0x368E, 0x30B2); // P_RD_P2Q2 + I2C_WRITE(0x3690, 0xD04F); // P_RD_P2Q3 + I2C_WRITE(0x3692, 0xACF5); // P_RD_P2Q4 + I2C_WRITE(0x36CA, 0xF7C9); // P_RD_P3Q0 + I2C_WRITE(0x36CC, 0x2AED); // P_RD_P3Q1 + I2C_WRITE(0x36CE, 0xA652); // P_RD_P3Q2 + I2C_WRITE(0x36D0, 0x8192); // P_RD_P3Q3 + I2C_WRITE(0x36D2, 0x3A15); // P_RD_P3Q4 + I2C_WRITE(0x370A, 0xDA30); // P_RD_P4Q0 + I2C_WRITE(0x370C, 0x2E2F); // P_RD_P4Q1 + I2C_WRITE(0x370E, 0xBB56); // P_RD_P4Q2 + I2C_WRITE(0x3710, 0x8195); // P_RD_P4Q3 + I2C_WRITE(0x3712, 0x02F9); // P_RD_P4Q4 + I2C_WRITE(0x3600, 0x0230); // P_GR_P0Q0 + I2C_WRITE(0x3602, 0x58AD); // P_GR_P0Q1 + I2C_WRITE(0x3604, 0x18D1); // P_GR_P0Q2 + I2C_WRITE(0x3606, 0x260D); // P_GR_P0Q3 + I2C_WRITE(0x3608, 0xF530); // P_GR_P0Q4 + I2C_WRITE(0x3640, 0x17EB); // P_GR_P1Q0 + I2C_WRITE(0x3642, 0x3CAB); // P_GR_P1Q1 + I2C_WRITE(0x3644, 0x87CE); // P_GR_P1Q2 + I2C_WRITE(0x3646, 0xC02E); // P_GR_P1Q3 + I2C_WRITE(0x3648, 0xF48F); // P_GR_P1Q4 + I2C_WRITE(0x3680, 0x5350); // P_GR_P2Q0 + I2C_WRITE(0x3682, 0x7EAF); // P_GR_P2Q1 + I2C_WRITE(0x3684, 0x4312); // P_GR_P2Q2 + I2C_WRITE(0x3686, 0xC652); // P_GR_P2Q3 + I2C_WRITE(0x3688, 0xBC15); // P_GR_P2Q4 + I2C_WRITE(0x36C0, 0xB8AD); // P_GR_P3Q0 + I2C_WRITE(0x36C2, 0xBDCD); // P_GR_P3Q1 + I2C_WRITE(0x36C4, 0xE4B2); // P_GR_P3Q2 + I2C_WRITE(0x36C6, 0xB50F); // P_GR_P3Q3 + I2C_WRITE(0x36C8, 0x5B95); // P_GR_P3Q4 + I2C_WRITE(0x3700, 0xFC90); // P_GR_P4Q0 + I2C_WRITE(0x3702, 0x8C51); // P_GR_P4Q1 + I2C_WRITE(0x3704, 0xCED6); // P_GR_P4Q2 + I2C_WRITE(0x3706, 0xB594); // P_GR_P4Q3 + I2C_WRITE(0x3708, 0x0A39); // P_GR_P4Q4 + I2C_WRITE(0x3614, 0x0230); // P_BL_P0Q0 + I2C_WRITE(0x3616, 0x160D); // P_BL_P0Q1 + I2C_WRITE(0x3618, 0x08D1); // P_BL_P0Q2 + I2C_WRITE(0x361A, 0x98AB); // P_BL_P0Q3 + I2C_WRITE(0x361C, 0xEA50); // P_BL_P0Q4 + I2C_WRITE(0x3654, 0xB4EA); // P_BL_P1Q0 + I2C_WRITE(0x3656, 0xEA6C); // P_BL_P1Q1 + I2C_WRITE(0x3658, 0xFE08); // P_BL_P1Q2 + I2C_WRITE(0x365A, 0x2C6E); // P_BL_P1Q3 + I2C_WRITE(0x365C, 0xEB0E); // P_BL_P1Q4 + I2C_WRITE(0x3694, 0x6DF0); // P_BL_P2Q0 + I2C_WRITE(0x3696, 0x3ACF); // P_BL_P2Q1 + I2C_WRITE(0x3698, 0x3E0F); // P_BL_P2Q2 + I2C_WRITE(0x369A, 0xB2B1); // P_BL_P2Q3 + I2C_WRITE(0x369C, 0xC374); // P_BL_P2Q4 + I2C_WRITE(0x36D4, 0xF2AA); // P_BL_P3Q0 + I2C_WRITE(0x36D6, 0x8CCC); // P_BL_P3Q1 + I2C_WRITE(0x36D8, 0xDEF2); // P_BL_P3Q2 + I2C_WRITE(0x36DA, 0xFA11); // P_BL_P3Q3 + I2C_WRITE(0x36DC, 0x42F5); // P_BL_P3Q4 + I2C_WRITE(0x3714, 0xF4F1); // P_BL_P4Q0 + I2C_WRITE(0x3716, 0xF6F0); // P_BL_P4Q1 + I2C_WRITE(0x3718, 0x8FD6); // P_BL_P4Q2 + I2C_WRITE(0x371A, 0xEA14); // P_BL_P4Q3 + I2C_WRITE(0x371C, 0x6338); // P_BL_P4Q4 + I2C_WRITE(0x361E, 0x0350); // P_GB_P0Q0 + I2C_WRITE(0x3620, 0x91AE); // P_GB_P0Q1 + I2C_WRITE(0x3622, 0x0571); // P_GB_P0Q2 + I2C_WRITE(0x3624, 0x100D); // P_GB_P0Q3 + I2C_WRITE(0x3626, 0xCA70); // P_GB_P0Q4 + I2C_WRITE(0x365E, 0xE6CB); // P_GB_P1Q0 + I2C_WRITE(0x3660, 0x50ED); // P_GB_P1Q1 + I2C_WRITE(0x3662, 0x3DAE); // P_GB_P1Q2 + I2C_WRITE(0x3664, 0xAA4F); // P_GB_P1Q3 + I2C_WRITE(0x3666, 0xDC50); // P_GB_P1Q4 + I2C_WRITE(0x369E, 0x5470); // P_GB_P2Q0 + I2C_WRITE(0x36A0, 0x1F6E); // P_GB_P2Q1 + I2C_WRITE(0x36A2, 0x6671); // P_GB_P2Q2 + I2C_WRITE(0x36A4, 0xC010); // P_GB_P2Q3 + I2C_WRITE(0x36A6, 0x8DF5); // P_GB_P2Q4 + I2C_WRITE(0x36DE, 0x0B0C); // P_GB_P3Q0 + I2C_WRITE(0x36E0, 0x84CE); // P_GB_P3Q1 + I2C_WRITE(0x36E2, 0x8493); // P_GB_P3Q2 + I2C_WRITE(0x36E4, 0xA610); // P_GB_P3Q3 + I2C_WRITE(0x36E6, 0x50B5); // P_GB_P3Q4 + I2C_WRITE(0x371E, 0x9651); // P_GB_P4Q0 + I2C_WRITE(0x3720, 0x1EAB); // P_GB_P4Q1 + I2C_WRITE(0x3722, 0xAF76); // P_GB_P4Q2 + I2C_WRITE(0x3724, 0xE4F4); // P_GB_P4Q3 + I2C_WRITE(0x3726, 0x79F8); // P_GB_P4Q4 + I2C_WRITE(0x3782, 0x0410); // Original LC 2 // POLY_ORIGIN_C + I2C_WRITE(0x3784, 0x0320); // POLY_ORIGIN_R + I2C_WRITE(0x3780, 0x8000); // POLY_SC_ENABLE + + return 0; +} + +static int mt9t013_set_pclk(int rt, int div_adj) +{ + int rc; + if ((rc = mt9t013_i2c_power_down()) < 0) return rc; + I2C_WRITE(REG_VT_PIX_CLK_DIV, mt9t013_reg_pattern.reg[rt].vt_pix_clk_div); + I2C_WRITE(REG_VT_SYS_CLK_DIV, mt9t013_reg_pattern.reg[rt].vt_sys_clk_div); + I2C_WRITE(REG_PRE_PLL_CLK_DIV, mt9t013_reg_pattern.reg[rt].pre_pll_clk_div * div_adj); + I2C_WRITE(REG_PLL_MULTIPLIER, mt9t013_reg_pattern.reg[rt].pll_multiplier); + I2C_WRITE(REG_OP_PIX_CLK_DIV, mt9t013_reg_pattern.reg[rt].op_pix_clk_div); + I2C_WRITE(REG_OP_SYS_CLK_DIV, mt9t013_reg_pattern.reg[rt].op_sys_clk_div); + if ((rc = mt9t013_i2c_power_up()) < 0) return rc; + pclk_set = 1; + return 0; +} + +static int mt9t013_i2c_sensor_setting(unsigned long arg) +{ + uint32_t update = arg & 1; + uint32_t rt = (arg & 2) >> 1; + + if (rt > 1 || update > 1) { + printk(KERN_ERR "mt9t013: invalid values %d of rt or %d of update\n", + rt, update); + return -EINVAL; + } + + switch (update) { + case CAMSENSOR_REG_UPDATE_PERIODIC: { + uint16_t pclk_div_adj = arg >> 16; + + printk(KERN_INFO "CAMSENSOR_REG_UPDATE_PERIODIC (rt %d)\n", rt); + + if (!pclk_div_adj || pclk_div_adj > 2) { + printk(KERN_ERR "mt9t013: invalid value %d of pclk_div_adj\n", + pclk_div_adj); + return -EINVAL; + } + + if (mt9t013_set_pclk(rt, pclk_div_adj) < 0) + return -EIO; + + I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD); + I2C_WRITE(REG_ROW_SPEED, mt9t013_reg_pattern.reg[rt].row_speed); + I2C_WRITE(REG_X_ADDR_START, mt9t013_reg_pattern.reg[rt].x_addr_start); + I2C_WRITE(REG_X_ADDR_END, mt9t013_reg_pattern.reg[rt].x_addr_end); + I2C_WRITE(REG_Y_ADDR_START, mt9t013_reg_pattern.reg[rt].y_addr_start); + I2C_WRITE(REG_Y_ADDR_END, mt9t013_reg_pattern.reg[rt].y_addr_end); + I2C_WRITE(REG_READ_MODE, mt9t013_reg_pattern.reg[rt].read_mode); + I2C_WRITE(REG_SCALE_M, mt9t013_reg_pattern.reg[rt].scale_m); + I2C_WRITE(REG_X_OUTPUT_SIZE, mt9t013_reg_pattern.reg[rt].x_output_size); + I2C_WRITE(REG_Y_OUTPUT_SIZE, mt9t013_reg_pattern.reg[rt].y_output_size); + I2C_WRITE(REG_LINE_LENGTH_PCK, mt9t013_reg_pattern.reg[rt].line_length_pck); + I2C_WRITE(REG_FRAME_LENGTH_LINES, (uint16_t) (mt9t013_reg_pattern.reg[rt].frame_length_lines * fps_divider)); + I2C_WRITE(REG_COARSE_INTEGRATION_TIME, mt9t013_reg_pattern.reg[rt].coarse_integration_time); + I2C_WRITE(REG_FINE_INTEGRATION_TIME, mt9t013_reg_pattern.reg[rt].fine_integration_time); + I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE); + } + break; + + case CAMSENSOR_REG_INIT: + printk(KERN_INFO "CAMSENSOR_REG_INIT (rt %d)\n", rt); + + if (mt9t013_set_pclk(rt, 1) < 0) return -EIO; + + I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD); + + /* additional power saving mode ok around 38.2MHz */ + I2C_WRITE(0x3084, 0x2409); + I2C_WRITE(0x3092, 0x0A49); + I2C_WRITE(0x3094, 0x4949); + I2C_WRITE(0x3096, 0x4949); + + /* set preview or snapshot mode */ + I2C_WRITE(REG_ROW_SPEED, mt9t013_reg_pattern.reg[rt].row_speed); + I2C_WRITE(REG_X_ADDR_START, mt9t013_reg_pattern.reg[rt].x_addr_start); + I2C_WRITE(REG_X_ADDR_END, mt9t013_reg_pattern.reg[rt].x_addr_end); + I2C_WRITE(REG_Y_ADDR_START, mt9t013_reg_pattern.reg[rt].y_addr_start); + I2C_WRITE(REG_Y_ADDR_END, mt9t013_reg_pattern.reg[rt].y_addr_end); + I2C_WRITE(REG_READ_MODE, mt9t013_reg_pattern.reg[rt].read_mode); + I2C_WRITE(REG_SCALE_M, mt9t013_reg_pattern.reg[rt].scale_m); + I2C_WRITE(REG_X_OUTPUT_SIZE, mt9t013_reg_pattern.reg[rt].x_output_size); + I2C_WRITE(REG_Y_OUTPUT_SIZE, mt9t013_reg_pattern.reg[rt].y_output_size); + I2C_WRITE(REG_LINE_LENGTH_PCK, mt9t013_reg_pattern.reg[rt].line_length_pck); + I2C_WRITE(REG_FRAME_LENGTH_LINES, mt9t013_reg_pattern.reg[rt].frame_length_lines); + I2C_WRITE(REG_COARSE_INTEGRATION_TIME, mt9t013_reg_pattern.reg[rt].coarse_integration_time); + I2C_WRITE(REG_FINE_INTEGRATION_TIME, mt9t013_reg_pattern.reg[rt].fine_integration_time); + + I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE); + + /* load lens shading */ + I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD); + if(mt9t013_mu3m0vc_set_lc() < 0) return -EIO; + I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int mt9t013_i2c_exposure_gain(uint16_t line, uint16_t gain) +{ + static const uint16_t max_legal_gain = 0x01FF; + + if (gain > max_legal_gain) gain = max_legal_gain; + + gain |= 0x200; /* set digital gain */ + + I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD); + I2C_WRITE(REG_GLOBAL_GAIN, gain); + I2C_WRITE(REG_COARSE_INTEGRATION_TIME, line); + I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE); + return 0; +} + +#define I2C_AF_WRITE(command, data) if (mt9t013_i2c_lens_write(AF_I2C_ID >> 1, command, data) < 0) return -EIO; + +static int mt9t013_i2c_move_focus(uint16_t position) +{ + uint8_t code_val_msb = (position >> 2) | ((position << 4) >> 6); + uint8_t code_val_lsb = (position & 0x03) << 6; + + I2C_AF_WRITE(code_val_msb, code_val_lsb); + return 0; +} + +static int mt9t013_i2c_set_default_focus(uint8_t step) +{ + I2C_AF_WRITE(0x01, step); + return 0; +} + +static int powered; + +static int mt9t013_i2c_power_up(void) +{ + printk(KERN_INFO "mt9t013: power up\n"); + if (powered) { + printk(KERN_INFO "mt9t013: already powered up\n"); + return 0; + } + I2C_WRITE(MT9T013_REG_RESET_REGISTER, MT9T013_RESET_REGISTER_PWON); + mdelay(5); + powered = 1; + return 0; +} + +static int mt9t013_i2c_power_down(void) +{ + printk(KERN_INFO "mt9t013: power down\n"); + if (!powered) { + printk(KERN_INFO "mt9t013: already powered down\n"); + return 0; + } + I2C_WRITE(MT9T013_REG_RESET_REGISTER, MT9T013_RESET_REGISTER_PWOFF); + mdelay(5); + powered = pclk_set = 0; + return 0; +} + +#undef I2C_WRITE +#undef I2C_AF_WRITE + +static int mt9t013_init_client(struct i2c_client *client) +{ + /* Initialize the MT9T013 Chip */ + init_waitqueue_head(&g_data_ready_wait_queue); + return 0; +} + +static struct file_operations mt9t013_fops = { + .owner = THIS_MODULE, + .open = mt9t013_open, + .release = mt9t013_release, + .unlocked_ioctl = mt9t013_ioctl, +}; + +static struct miscdevice mt9t013_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "mt9t013", + .fops = &mt9t013_fops, +}; + + +static int mt9t013_probe( + struct i2c_client *client, const struct i2c_device_id *id) +{ + struct mt9t013_data *mt; + int err = 0; + printk(KERN_INFO "mt9t013: probe\n"); + if(!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + goto exit_check_functionality_failed; + + if(!(mt = kzalloc( sizeof(struct mt9t013_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit_alloc_data_failed; + } + + i2c_set_clientdata(client, mt); + mt9t013_init_client(client); + pclient = client; + mt9t013_sensor_init(); + mt9t013_sensor_suspend(); + + /* Register a misc device */ + err = misc_register(&mt9t013_device); + if(err) { + printk(KERN_ERR "mt9t013_probe: misc_register failed \n"); + goto exit_misc_device_register_failed; + } + init_suspend(); + return 0; + +exit_misc_device_register_failed: +exit_alloc_data_failed: +exit_check_functionality_failed: + + return err; +} + + +static int mt9t013_remove(struct i2c_client *client) +{ + struct mt9t013_data *mt = i2c_get_clientdata(client); + free_irq(client->irq, mt); + deinit_suspend(); + pclient = NULL; + misc_deregister(&mt9t013_device); + kfree(mt); + return 0; +} + +static const struct i2c_device_id mt9t013_id[] = { + { "mt9t013", 0 }, + { } +}; + +static struct i2c_driver mt9t013_driver = { + .probe = mt9t013_probe, + .remove = mt9t013_remove, + .id_table = mt9t013_id, + .driver = { + .name = "mt9t013", + }, +}; + + +static int mt9t013_plat_probe(struct platform_device *pdev __attribute__((unused))) +{ + int rc = -EFAULT; + + if(pdev->dev.platform_data) + { + printk(KERN_INFO "pdev->dev.platform_data is not NULL\n"); + cam=pdev->dev.platform_data; + rc = i2c_add_driver(&mt9t013_driver); + } + return rc; +} + +static struct platform_driver mt9t013_plat_driver = { + .probe = mt9t013_plat_probe, + .driver = { + .name = "camera", + .owner = THIS_MODULE, + }, +}; + +static int __init mt9t013_init(void) +{ + return platform_driver_register(&mt9t013_plat_driver); +} + +static void __exit mt9t013_exit(void) +{ + i2c_del_driver(&mt9t013_driver); +} + +module_init(mt9t013_init); +module_exit(mt9t013_exit); + +MODULE_AUTHOR("Kidd Chen"); +MODULE_DESCRIPTION("MT9T013 Driver"); +MODULE_LICENSE("GPL"); + diff --git a/include/linux/mt9t013.h b/include/linux/mt9t013.h new file mode 100644 index 0000000000000..7b7811eae8fb1 --- /dev/null +++ b/include/linux/mt9t013.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2007-2008 HTC Corporation. + * + * 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 CAMERA_MT9T013_H +#define CAMERA_MT9T013_H +#include +#include +#include +#include + +/************************************************************* +* IOCTL define +*************************************************************/ + +#define MT9T013_I2C_IOCTL_MAGIC 'm' + +#define MT9T013_I2C_IOCTL_W \ + _IOW(MT9T013_I2C_IOCTL_MAGIC, 0, unsigned) + +#define MT9T013_I2C_IOCTL_R \ + _IOR(MT9T013_I2C_IOCTL_MAGIC, 1, unsigned) + +#define MT9T013_I2C_IOCTL_AF_W \ + _IOW(MT9T013_I2C_IOCTL_MAGIC, 2, unsigned) + +#define MT9T013_I2C_IOCTL_CAMIF_PAD_REG_RESET \ + _IO(MT9T013_I2C_IOCTL_MAGIC, 3) + +#define MT9T013_I2C_IOCTL_CAMIF_PAD_REG_RESET_2 \ + _IO(MT9T013_I2C_IOCTL_MAGIC, 4) + +#define CAMERA_CONFIGURE_GPIOS \ + _IO(MT9T013_I2C_IOCTL_MAGIC, 7) + +#define CAMERA_UNCONFIGURE_GPIOS \ + _IO(MT9T013_I2C_IOCTL_MAGIC, 8) + +#define CAMERA_LENS_POWER_ON \ + _IO(MT9T013_I2C_IOCTL_MAGIC, 9) + +#define CAMERA_LENS_POWER_OFF \ + _IO(MT9T013_I2C_IOCTL_MAGIC, 10) + +#define MT9T013_I2C_IOCTL_CAMIF_APPS_RESET \ + _IO(MT9T013_I2C_IOCTL_MAGIC, 11) + +/* Replacement ioctls() for the clkrgm_sec RPCs. */ + +#define CAMIO_VFE_MDC_CLK 1 /* enable, disable */ +#define CAMIO_MDC_CLK 2 /* enable, disable */ +#define CAMIO_VFE_CLK 3 /* clk_select, freq_prog */ + +#define MT9T013_I2C_IOCTL_CLK_ENABLE \ + _IOW(MT9T013_I2C_IOCTL_MAGIC, 12, unsigned) + +#define MT9T013_I2C_IOCTL_CLK_DISABLE \ + _IOW(MT9T013_I2C_IOCTL_MAGIC, 13, unsigned) + +#define MT9T013_I2C_IOCTL_CLK_SELECT \ + _IOW(MT9T013_I2C_IOCTL_MAGIC, 14, unsigned) + +#define MT9T013_I2C_IOCTL_CLK_FREQ_PROG \ + _IOW(MT9T013_I2C_IOCTL_MAGIC, 15, unsigned) + +#define CAMSENSOR_REG_INIT 0<<0 +#define CAMSENSOR_REG_UPDATE_PERIODIC 1<<0 +#define CAMSENSOR_TYPE_PREVIEW 0<<1 +#define CAMSENSOR_TYPE_SNAPSHOT 1<<1 + +#define MT9T013_I2C_IOCTL_SENSOR_SETTING \ + _IOW(MT9T013_I2C_IOCTL_MAGIC, 16, uint32_t) + +struct mt9t013_reg_struct +{ + uint16_t vt_pix_clk_div; /* 0x0300 */ + uint16_t vt_sys_clk_div; /* 0x0302 */ + uint16_t pre_pll_clk_div; /* 0x0304 */ + uint16_t pll_multiplier; /* 0x0306 */ + uint16_t op_pix_clk_div; /* 0x0308 */ + uint16_t op_sys_clk_div; /* 0x030A */ + uint16_t scale_m; /* 0x0404 */ + uint16_t row_speed; /* 0x3016 */ + uint16_t x_addr_start; /* 0x3004 */ + uint16_t x_addr_end; /* 0x3008 */ + uint16_t y_addr_start; /* 0x3002 */ + uint16_t y_addr_end; /* 0x3006 */ + uint16_t read_mode; /* 0x3040 */ + uint16_t x_output_size ; /* 0x034C */ + uint16_t y_output_size; /* 0x034E */ + uint16_t line_length_pck; /* 0x300C */ + uint16_t frame_length_lines; /* 0x300A */ + uint16_t coarse_integration_time; /* 0x3012 */ + uint16_t fine_integration_time; /* 0x3014 */ +}; + +struct mt9t013_reg_pat { + struct mt9t013_reg_struct reg[2]; +}; + +#define MT9T013_I2C_IOCTL_GET_REGISTERS \ + _IOR(MT9T013_I2C_IOCTL_MAGIC, 17, struct mt9t013_reg_pat *) + +struct mt9t013_exposure_gain { + uint16_t gain; + uint16_t line; +}; + +#define MT9T013_I2C_IOCTL_EXPOSURE_GAIN \ + _IOW(MT9T013_I2C_IOCTL_MAGIC, 18, struct exposure_gain *) + +#define MT9T013_I2C_IOCTL_MOVE_FOCUS \ + _IOW(MT9T013_I2C_IOCTL_MAGIC, 19, uint32_t) + +#define MT9T013_I2C_IOCTL_SET_DEFAULT_FOCUS \ + _IOW(MT9T013_I2C_IOCTL_MAGIC, 20, uint32_t) + +#define MT9T013_I2C_IOCTL_POWER_DOWN \ + _IO(MT9T013_I2C_IOCTL_MAGIC, 21) + +struct mt9t013_init { + int preview; /* in: 1 for preview, 0 for capture */ + uint16_t chipid; /* out: chip id */ +}; + +#define MT9T013_I2C_IOCTL_INIT \ + _IOWR(MT9T013_I2C_IOCTL_MAGIC, 22, struct mt9t013_init *) + +#endif + From b08960604e316f074407e9c0d56a5af17361a436 Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Date: Tue, 30 Sep 2008 13:01:17 -0700 Subject: [PATCH 0202/2556] elan_8232_i2c: Adding Elan 8232 I2C touchscreen driver Signed-off-by: Rebecca Schultz --- drivers/input/touchscreen/Kconfig | 4 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/elan8232_i2c.c | 480 +++++++++++++++++++++++ 3 files changed, 485 insertions(+) create mode 100644 drivers/input/touchscreen/elan8232_i2c.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 4210c3ed7cd49..90fa1813af955 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -202,6 +202,10 @@ config TOUCHSCREEN_GUNZE To compile this driver as a module, choose M here: the module will be called gunze. +config TOUCHSCREEN_ELAN_I2C_8232 + tristate "Elan 8232 I2C touchscreen" + depends on I2C + config TOUCHSCREEN_ELO tristate "Elo serial touchscreens" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index a13a1d48d367a..d0da12eb237c6 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o +obj-$(CONFIG_TOUCHSCREEN_ELAN_I2C_8232) += elan8232_i2c.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o diff --git a/drivers/input/touchscreen/elan8232_i2c.c b/drivers/input/touchscreen/elan8232_i2c.c new file mode 100644 index 0000000000000..7c07d8431621c --- /dev/null +++ b/drivers/input/touchscreen/elan8232_i2c.c @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2007-2008 HTC Corporation. + * + * 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 + +#define EKT8232NAME "elan-touch" + +#define ELAN_TS_ABS_X_MIN 32 +#define ELAN_TS_ABS_X_MAX 352 +#define ELAN_TS_ABS_Y_MIN 32 +#define ELAN_TS_ABS_Y_MAX 544 +#define ELAN_TS_FUZZ 0 +#define ELAN_TS_FLAT 0 + +enum { + STATE_DEEP_SLEEP = 0, + STATE_NORMAL = 1U, + STATE_MASK = 0x08, + cmd_reponse_packet = 0x52, + read_cmd_packet = 0x53, + write_cmd_packet = 0x54, + hello_packet = 0x55, + idx_coordinate_packet = 0x5a, +}; + +static struct ekt8232_data { + int use_irq; + struct hrtimer timer; + struct work_struct work; + struct i2c_client *client; + struct input_dev *input; + wait_queue_head_t wait; +} ekt_data; + +/* Though the writing-clients suggest to use SMBus level + * communication instead of plain i2c communication. + * But msm_i2c_algo registers master_xfer only. + */ + +static int __hello_packet_handler(struct i2c_client *client) +{ + int rc; + uint8_t buf_recv[4] = { 0 }; + + rc = i2c_master_recv(client, buf_recv, 4); + if (rc != 4) { + dev_err(&client->dev, + "%s: get hello packet failed!, rc = %d\n", + __FUNCTION__, rc); + return rc; + } else { + int i; + dev_dbg(&client->dev, + "dump hello packet: %0x, %0x, %0x, %0x\n", + buf_recv[0], buf_recv[1], buf_recv[2], buf_recv[3]); + + for (i = 0; i < 4; i++) + if (buf_recv[i] != hello_packet) + return -EINVAL; + } + + return 0; +} + +static int __fw_packet_handler(struct i2c_client *client) +{ + int rc; + int major, minor; + uint8_t cmd[] = { read_cmd_packet, 0x00, 0x00, 0x01 }; + uint8_t buf_recv[4] = { 0 }; + + rc = i2c_master_send(client, cmd, sizeof(cmd)); + if (rc != sizeof(cmd)) { + dev_err(&client->dev, + "%s: i2c_master_send failed\n", __FUNCTION__); + return -EINVAL; + } + msleep(50); + + rc = i2c_master_recv((struct i2c_client *)client, buf_recv, 4); + major = ((buf_recv[1] & 0x0f) << 4) | ((buf_recv[2] & 0xf0) >> 4); + minor = ((buf_recv[2] & 0x0f) << 4) | ((buf_recv[3] & 0xf0) >> 4); + dev_dbg(&client->dev, + "%s: firmware version: %d.%d\n", __FUNCTION__, major, minor); + + return 0; +} + +static int __set_report_type(struct i2c_client *client) +{ + return 0; +} + +static int __parse_xy(uint8_t *data, uint16_t *x, uint16_t *y) +{ + *x = (data[0] & 0xf0); + *x <<= 4; + *x |= data[1]; + + *y = (data[0] & 0x0f); + *y <<= 8; + *y |= data[2]; + + return 0; +} + +/* ekt8232_ts_init -- hand shaking with touch panel + * + * 1. recv hello packet + * 2. check its' firmware version + * 3. set up sensitivity, report rate, ... + */ +static int ekt8232_ts_init(struct i2c_client *client) +{ + int rc; + + rc = __hello_packet_handler(client); + if (rc < 0) + goto hand_shake_failed; + dev_dbg(&client->dev, "%s: hello packet got.\n", __FUNCTION__); + + rc = __fw_packet_handler(client); + if (rc < 0) + goto hand_shake_failed; + dev_dbg(&client->dev, "%s: firmware checking done.\n", __FUNCTION__); + + rc = __set_report_type(client); + if (rc < 0) + goto hand_shake_failed; + dev_dbg(&client->dev, + "%s: channging operating mode done.\n", __FUNCTION__); + +hand_shake_failed: + return rc; +} + +static void ekt8232_work_func(struct work_struct *work) +{ + int rc; + uint8_t buf[8] = { 0 }; + struct i2c_client *client = ekt_data.client; + + rc = i2c_master_recv(client, buf, 8); + if (rc != 8) { + dev_err(&client->dev, + "%s: i2c_master_recv error?! \n", __FUNCTION__); + goto done; + } + + switch (buf[0]) { + case idx_coordinate_packet: { + uint16_t x1, x2, y1, y2; + uint8_t finger_stat; + + __parse_xy(&buf[1], &x1, &y1); + finger_stat = buf[7] >> 1; + dev_dbg(&client->dev, + "x1 = %d, y1 = %d, finger status = %d\n", + x1, y1, finger_stat); + if (finger_stat != 0) { + input_report_abs(ekt_data.input, ABS_X, x1); + input_report_abs(ekt_data.input, ABS_Y, + ELAN_TS_ABS_Y_MAX - y1); + } + input_report_key(ekt_data.input, BTN_TOUCH, finger_stat); + input_report_key(ekt_data.input, BTN_2, finger_stat == 2); + + if (finger_stat > 1) { + __parse_xy(&buf[4], &x2, &y2); + dev_dbg(&client->dev, "x2 = %d, y2 = %d\n", x2, y2); + input_report_abs(ekt_data.input, ABS_HAT0X, x2); + input_report_abs(ekt_data.input, ABS_HAT0Y, + ELAN_TS_ABS_Y_MAX - y2); + } + input_sync(ekt_data.input); + break; + } + default: + dev_err(&client->dev, + "%s: Unknown packet type: %0x\n", + __FUNCTION__, buf[0]); + break; + } + +done: + if (ekt_data.use_irq) + enable_irq(ekt_data.client->irq); +} + +static irqreturn_t ekt8232_ts_interrupt(int irq, void *dev_id) +{ + disable_irq(irq); + schedule_work(&ekt_data.work); + + return IRQ_HANDLED; +} + +static enum hrtimer_restart ekt8232_ts_timer_func(struct hrtimer *timer) +{ + schedule_work(&ekt_data.work); + hrtimer_start(&ekt_data.timer, ktime_set(0, 12500000), + HRTIMER_MODE_REL); + + return HRTIMER_NORESTART; +} + +static int __init ekt8232_register_input(struct input_dev *input) +{ + dev_dbg(&input->dev, "%s: enter\n", __FUNCTION__); + + input->name = EKT8232NAME; + input->id.bustype = BUS_I2C; + + input->evbit[0] = + BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input, ABS_X, + ELAN_TS_ABS_X_MIN, ELAN_TS_ABS_X_MAX, + ELAN_TS_FUZZ, ELAN_TS_FLAT); + input_set_abs_params(input, ABS_Y, + ELAN_TS_ABS_Y_MIN, ELAN_TS_ABS_Y_MAX, + ELAN_TS_FUZZ, ELAN_TS_FLAT); + input_set_abs_params(input, ABS_HAT0X, + ELAN_TS_ABS_X_MIN, ELAN_TS_ABS_X_MAX, + ELAN_TS_FUZZ, ELAN_TS_FLAT); + input_set_abs_params(input, ABS_HAT0Y, + ELAN_TS_ABS_Y_MIN, ELAN_TS_ABS_Y_MAX, + ELAN_TS_FUZZ, ELAN_TS_FLAT); + + return input_register_device(input); +} + +static int ekt8232_probe( + struct i2c_client *client, const struct i2c_device_id *id) +{ + int err = 0, retry = 10; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, + "No supported i2c func what we need?!!\n"); + return -ENOTSUPP; + } + + ekt_data.client = client; + strlcpy(client->name, EKT8232NAME, I2C_NAME_SIZE); + i2c_set_clientdata(client, &ekt_data); + INIT_WORK(&ekt_data.work, ekt8232_work_func); + init_waitqueue_head(&ekt_data.wait); + + ekt_data.input = input_allocate_device(); + if (ekt_data.input == NULL) + return -ENOMEM; + + /* Actually, we are missing the first time interrupt here. + * So, we need to fake we are getting the interrupt for hello packet. + */ + do { + err = ekt8232_ts_init(client); + if (err >= 0) + break; + msleep(100); + } while (retry--); + + if (err < 0) { + dev_dbg(&client->dev, + "looks like it's not Elan, so..i'll quit\n"); + err = -ENODEV; + goto fail; + } + + err = ekt8232_register_input(ekt_data.input); + if (err < 0) { + dev_err(&client->dev, + "%s: register to input system failed, err = %d\n", + __FUNCTION__, err); + goto fail; + } + /* CPLD does not accept configuring interrupt type */ + if (client->irq) { + err = request_irq(client->irq, ekt8232_ts_interrupt, 0, + EKT8232NAME, &ekt_data); + if (err < 0) { + dev_err(&client->dev, + "%s(%s): Can't allocate irq %d\n", + __FILE__, __FUNCTION__, client->irq); + ekt_data.use_irq = 0; + free_irq(client->irq, &ekt_data); + } else { + ekt_data.use_irq = 1; + } + } + if (!ekt_data.use_irq) { + hrtimer_init(&ekt_data.timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ekt_data.timer.function = ekt8232_ts_timer_func; + hrtimer_start(&ekt_data.timer, ktime_set(1, 0), + HRTIMER_MODE_REL); + } + return 0; + +fail: + input_free_device(ekt_data.input); + return err; +} + +static int ekt8232_remove(struct i2c_client *client) +{ + struct ekt8232_data *tp = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "%s: enter.\n", __FUNCTION__); + + input_unregister_device(tp->input); + + free_irq(client->irq, tp); + + return 0; +} +static int ekt8232_get_power_state(struct i2c_client *client) +{ + uint8_t cmd[] = { read_cmd_packet, 0x50, 0x00, 0x01 }; + uint8_t power_state, buf_recv[4] = { 0 }; + + dev_dbg(&client->dev, "%s: enter.\n", __FUNCTION__); + + if ((i2c_master_send(client, cmd, 4)) != 4) { + dev_err(&client->dev, + "%s: i2c_master_send failed\n", __FUNCTION__); + return -EINVAL; + } + msleep(50); + + if ((i2c_master_recv(client, buf_recv, 4)) != 4) { + dev_err(&client->dev, + "%s: i2c_master_send failed\n", __FUNCTION__); + return -EINVAL; + } + + if (buf_recv[0] != cmd_reponse_packet) { + dev_err(&client->dev, + "%s: unknown packet got ?!\n", __FUNCTION__); + return -EINVAL; + } else { + power_state = buf_recv[1]; + dev_dbg(&client->dev, + "dump repsponse: %0x\n", power_state); + + power_state = (power_state & STATE_MASK) >> 3; + dev_dbg(&client->dev, "power state = %s\n", + power_state == STATE_DEEP_SLEEP ? + "Deep Sleep" : "Normal/Idle"); + + return power_state; + } +} + +static int ekt8232_set_power_state(struct i2c_client *client, int state) +{ + uint8_t cmd[] = { write_cmd_packet, 0x50, 0x00, 0x01}; + + dev_dbg(&client->dev, "%s: enter.\n", __FUNCTION__); + + cmd[1] |= (state << 3); + + dev_dbg(&client->dev, + "dump cmd: %02x, %02x, %02x, %02x\n", + cmd[0], cmd[1], cmd[2], cmd[3]); + + if ((i2c_master_send(client, cmd, sizeof(cmd))) != sizeof(cmd)) { + dev_err(&client->dev, + "%s: i2c_master_send failed\n", __FUNCTION__); + return -EINVAL; + } + return 0; +} + +#ifdef CONFIG_PM +/* + * ekt8232_suspend -- suspend the ekt8232 panel + * + * 1. Send Power State Packet to get the present state + * 2. If it is not in *Deep Sleep*, send Power State Packet + * to change that. + */ +static int ekt8232_suspend(struct i2c_client *client, pm_message_t mesg) +{ + int rc = 0; + + dev_dbg(&client->dev, + "%s: enter. irq = %d\n", __FUNCTION__, client->irq); + + cancel_work_sync(&ekt_data.work); + + disable_irq(client->irq); + + rc = ekt8232_set_power_state(client, STATE_DEEP_SLEEP); + + return rc; +} + +static int ekt8232_resume(struct i2c_client *client) +{ + int rc = 0; + + dev_dbg(&client->dev, + "%s: enter. irq = %d\n", __FUNCTION__, client->irq); + + ekt8232_set_power_state(client, STATE_NORMAL); + + rc = wait_event_timeout(ekt_data.wait, + ((rc = ekt8232_get_power_state(client)) == STATE_NORMAL), HZ); + if (rc <= 0) { + dev_err(&client->dev, "Oops, can not wake touch panel up!!!\n"); + return -ETIMEDOUT; + } + + dev_dbg(&client->dev, + "%s: done. enable irq = %d\n", __FUNCTION__, client->irq); + + enable_irq(client->irq); + + return rc; +} +#else +#define ekt8232_suspend NULL +#define ekt8232_resume NULL +#endif + +static const struct i2c_device_id ekt8232_id[] = { + { EKT8232NAME, 0 }, + { } +}; + +static struct i2c_driver ekt8232_driver = { + .probe = ekt8232_probe, + .remove = ekt8232_remove, + .suspend = ekt8232_suspend, + .resume = ekt8232_resume, + .id_table = ekt8232_id, + .driver = { + .name = EKT8232NAME, + }, +}; + +static int __init ekt8232_init(void) +{ + return i2c_add_driver(&ekt8232_driver); +} + +static void __exit ekt8232_exit(void) +{ + i2c_del_driver(&ekt8232_driver); +} + +module_init(ekt8232_init); +module_exit(ekt8232_exit); + +MODULE_AUTHOR("Shan-Fu Chiou , " + "Jay Tu "); +MODULE_DESCRIPTION("ELAN ekt8232 driver"); +MODULE_LICENSE("GPL"); From 918eae0ddc36be673b971ef198a6338d5b1a8f5b Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Thu, 27 Mar 2008 18:18:01 -0700 Subject: [PATCH 0203/2556] [ARM] msm: add hook for vbus state notification The msm_hsusb_set_vbus_state() function allows the board file, battery/charger/power code, etc to inform the USB driver about the presence of VBUS. The driver now puts the USB client controller online when VBUS is present and suspends the PHY when it goes away. Signed-off-by: Brian Swetland --- arch/arm/mach-msm/include/mach/board.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index 2e6bd1fe86833..1036ecfeda515 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -52,4 +52,10 @@ int __init msm_add_sdcc(unsigned int controller, struct msm_mmc_platform_data *plat, unsigned int stat_irq, unsigned long stat_irq_flags); +#if defined(CONFIG_USB_FUNCTION_MSM_HSUSB) +void msm_hsusb_set_vbus_state(int online); +#else +static inline void msm_hsusb_set_vbus_state(int online) {} +#endif + #endif From d540a9f06b0e8116d8dac34bee50f54d3b0a4083 Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Date: Mon, 29 Sep 2008 18:24:57 -0700 Subject: [PATCH 0204/2556] [ARM] trout: htc platform battery driver Uses rpc to communicate with battery state/charging service on the baseband processor. Signed-off-by: Rebecca Schultz --- arch/arm/mach-msm/Kconfig | 5 + arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/htc_battery.c | 769 ++++++++++++++++++++++++++++++++ 3 files changed, 775 insertions(+) create mode 100644 arch/arm/mach-msm/htc_battery.c diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index c3f0741790ae2..2de8ff5880ed9 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -192,6 +192,11 @@ config TROUT_H2W headset, on the trout platform. Can be used with the msm serial debugger, but not with serial console. +config TROUT_BATTCHG + depends on MACH_TROUT && POWER_SUPPLY + default y + bool "Trout battery / charger driver" + choice prompt "Default Timer" default MSM7X00A_USE_GP_TIMER diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 312772a7bf273..bd6f9233d1359 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o b obj-$(CONFIG_MACH_TROUT) += board-trout-keypad.o obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o obj-$(CONFIG_TROUT_H2W) += board-trout-h2w.o +obj-$(CONFIG_TROUT_BATTCHG) += htc_battery.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o diff --git a/arch/arm/mach-msm/htc_battery.c b/arch/arm/mach-msm/htc_battery.c new file mode 100644 index 0000000000000..7320edbff1a9f --- /dev/null +++ b/arch/arm/mach-msm/htc_battery.c @@ -0,0 +1,769 @@ +/* arch/arm/mach-msm/htc_battery.c + * + * Copyright (C) 2008 HTC Corporation. + * Copyright (C) 2008 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 +#include +#include +#include +#include +#include +#include + +static struct wake_lock vbus_wake_lock; + +#define TRACE_BATT 0 + +#if TRACE_BATT +#define BATT(x...) printk(KERN_INFO "[BATT] " x) +#else +#define BATT(x...) do {} while (0) +#endif + +/* rpc related */ +#define APP_BATT_PDEV_NAME "rs30100001:00000000" +#define APP_BATT_PROG 0x30100001 +#define APP_BATT_VER 0 +#define HTC_PROCEDURE_BATTERY_NULL 0 +#define HTC_PROCEDURE_GET_BATT_LEVEL 1 +#define HTC_PROCEDURE_GET_BATT_INFO 2 +#define HTC_PROCEDURE_GET_CABLE_STATUS 3 +#define HTC_PROCEDURE_SET_BATT_DELTA 4 + +/* module debugger */ +#define HTC_BATTERY_DEBUG 1 +#define BATTERY_PREVENTION 1 + +/* Enable this will shut down if no battery */ +#define ENABLE_BATTERY_DETECTION 0 + +#define GPIO_BATTERY_DETECTION 21 +#define GPIO_BATTERY_CHARGER_EN 128 + +/* Charge current selection */ +#define GPIO_BATTERY_CHARGER_CURRENT 129 + +typedef enum { + DISABLE = 0, + ENABLE_SLOW_CHG, + ENABLE_FAST_CHG +} batt_ctl_t; + +/* This order is the same as htc_power_supplies[] + * And it's also the same as htc_cable_status_update() + */ +typedef enum { + CHARGER_BATTERY = 0, + CHARGER_USB, + CHARGER_AC +} charger_type_t; + +struct battery_info_reply { + u32 batt_id; /* Battery ID from ADC */ + u32 batt_vol; /* Battery voltage from ADC */ + u32 batt_temp; /* Battery Temperature (C) from formula and ADC */ + u32 batt_current; /* Battery current from ADC */ + u32 level; /* formula */ + u32 charging_source; /* 0: no cable, 1:usb, 2:AC */ + u32 charging_enabled; /* 0: Disable, 1: Enable */ + u32 full_bat; /* Full capacity of battery (mAh) */ +}; + +struct htc_battery_info { + int present; + unsigned long update_time; + + /* lock to protect the battery info */ + struct mutex lock; + + /* lock held while calling the arm9 to query the battery info */ + struct mutex rpc_lock; + struct battery_info_reply rep; +}; + +static struct msm_rpc_endpoint *endpoint; + +static struct htc_battery_info htc_batt_info; + +static unsigned int cache_time = 1000; + +static int htc_battery_initial = 0; + +static enum power_supply_property htc_battery_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CAPACITY, +}; + +static enum power_supply_property htc_power_properties[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static char *supply_list[] = { + "battery", +}; + +/* HTC dedicated attributes */ +static ssize_t htc_battery_show_property(struct device *dev, + struct device_attribute *attr, + char *buf); + +static int htc_power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val); + +static int htc_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val); + +static struct power_supply htc_power_supplies[] = { + { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = htc_battery_properties, + .num_properties = ARRAY_SIZE(htc_battery_properties), + .get_property = htc_battery_get_property, + }, + { + .name = "usb", + .type = POWER_SUPPLY_TYPE_USB, + .supplied_to = supply_list, + .num_supplicants = ARRAY_SIZE(supply_list), + .properties = htc_power_properties, + .num_properties = ARRAY_SIZE(htc_power_properties), + .get_property = htc_power_get_property, + }, + { + .name = "ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .supplied_to = supply_list, + .num_supplicants = ARRAY_SIZE(supply_list), + .properties = htc_power_properties, + .num_properties = ARRAY_SIZE(htc_power_properties), + .get_property = htc_power_get_property, + }, +}; + + +/* -------------------------------------------------------------------------- */ + +#if defined(CONFIG_DEBUG_FS) +int htc_battery_set_charging(batt_ctl_t ctl); +static int batt_debug_set(void *data, u64 val) +{ + return htc_battery_set_charging((batt_ctl_t) val); +} + +static int batt_debug_get(void *data, u64 *val) +{ + return -ENOSYS; +} + +DEFINE_SIMPLE_ATTRIBUTE(batt_debug_fops, batt_debug_get, batt_debug_set, "%llu\n"); +static int __init batt_debug_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("htc_battery", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("charger_state", 0644, dent, NULL, &batt_debug_fops); + + return 0; +} + +device_initcall(batt_debug_init); +#endif + +static int init_batt_gpio(void) +{ + if (gpio_request(GPIO_BATTERY_DETECTION, "batt_detect") < 0) + goto gpio_failed; + if (gpio_request(GPIO_BATTERY_CHARGER_EN, "charger_en") < 0) + goto gpio_failed; + if (gpio_request(GPIO_BATTERY_CHARGER_CURRENT, "charge_current") < 0) + goto gpio_failed; + + return 0; + +gpio_failed: + return -EINVAL; + +} + +/* + * battery_charging_ctrl - battery charing control. + * @ctl: battery control command + * + */ +static int battery_charging_ctrl(batt_ctl_t ctl) +{ + int result = 0; + + switch (ctl) { + case DISABLE: + BATT("charger OFF\n"); + /* 0 for enable; 1 disable */ + result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 1); + break; + case ENABLE_SLOW_CHG: + BATT("charger ON (SLOW)\n"); + result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 0); + result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0); + break; + case ENABLE_FAST_CHG: + BATT("charger ON (FAST)\n"); + result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 1); + result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0); + break; + default: + printk(KERN_ERR "Not supported battery ctr called.!\n"); + result = -EINVAL; + break; + } + + return result; +} + +int htc_battery_set_charging(batt_ctl_t ctl) +{ + int rc; + + if ((rc = battery_charging_ctrl(ctl)) < 0) + goto result; + + if (!htc_battery_initial) { + htc_batt_info.rep.charging_enabled = ctl & 0x3; + } else { + mutex_lock(&htc_batt_info.lock); + htc_batt_info.rep.charging_enabled = ctl & 0x3; + mutex_unlock(&htc_batt_info.lock); + } +result: + return rc; +} + +int htc_battery_status_update(u32 curr_level) +{ + int notify; + if (!htc_battery_initial) + return 0; + + mutex_lock(&htc_batt_info.lock); + notify = (htc_batt_info.rep.level != curr_level); + htc_batt_info.rep.level = curr_level; + mutex_unlock(&htc_batt_info.lock); + + if (notify) + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + return 0; +} + +int htc_cable_status_update(int status) +{ + int rc = 0; + unsigned source; + + if (!htc_battery_initial) + return 0; + + mutex_lock(&htc_batt_info.lock); + switch(status) { + case CHARGER_BATTERY: + BATT("cable NOT PRESENT\n"); + htc_batt_info.rep.charging_source = CHARGER_BATTERY; + break; + case CHARGER_USB: + BATT("cable USB\n"); + htc_batt_info.rep.charging_source = CHARGER_USB; + break; + case CHARGER_AC: + BATT("cable AC\n"); + htc_batt_info.rep.charging_source = CHARGER_AC; + break; + default: + printk(KERN_ERR "%s: Not supported cable status received!\n", + __FUNCTION__); + rc = -EINVAL; + } + source = htc_batt_info.rep.charging_source; + mutex_unlock(&htc_batt_info.lock); + + msm_hsusb_set_vbus_state(source == CHARGER_USB); + if (source == CHARGER_USB) { + wake_lock(&vbus_wake_lock); + } else { + /* give userspace some time to see the uevent and update + * LED state or whatnot... + */ + wake_lock_timeout(&vbus_wake_lock, HZ / 2); + } + + /* if the power source changes, all power supplies may change state */ + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + power_supply_changed(&htc_power_supplies[CHARGER_USB]); + power_supply_changed(&htc_power_supplies[CHARGER_AC]); + + return rc; +} + +static int htc_get_batt_info(struct battery_info_reply *buffer) +{ + struct rpc_request_hdr req; + + struct htc_get_batt_info_rep { + struct rpc_reply_hdr hdr; + struct battery_info_reply info; + } rep; + + int rc; + + if (buffer == NULL) + return -EINVAL; + + rc = msm_rpc_call_reply(endpoint, HTC_PROCEDURE_GET_BATT_INFO, + &req, sizeof(req), + &rep, sizeof(rep), + 5 * HZ); + if ( rc < 0 ) + return rc; + + mutex_lock(&htc_batt_info.lock); + buffer->batt_id = be32_to_cpu(rep.info.batt_id); + buffer->batt_vol = be32_to_cpu(rep.info.batt_vol); + buffer->batt_temp = be32_to_cpu(rep.info.batt_temp); + buffer->batt_current = be32_to_cpu(rep.info.batt_current); + buffer->level = be32_to_cpu(rep.info.level); + buffer->charging_source = be32_to_cpu(rep.info.charging_source); + buffer->charging_enabled = be32_to_cpu(rep.info.charging_enabled); + buffer->full_bat = be32_to_cpu(rep.info.full_bat); + mutex_unlock(&htc_batt_info.lock); + + return 0; +} + +#if 0 +static int htc_get_cable_status(void) +{ + + struct rpc_request_hdr req; + + struct htc_get_cable_status_rep { + struct rpc_reply_hdr hdr; + int status; + } rep; + + int rc; + + rc = msm_rpc_call_reply(endpoint, HTC_PROCEDURE_GET_CABLE_STATUS, + &req, sizeof(req), + &rep, sizeof(rep), + 5 * HZ); + if (rc < 0) + return rc; + + return be32_to_cpu(rep.status); +} +#endif + +/* -------------------------------------------------------------------------- */ +static int htc_power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + charger_type_t charger; + + mutex_lock(&htc_batt_info.lock); + charger = htc_batt_info.rep.charging_source; + mutex_unlock(&htc_batt_info.lock); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if (psy->type == POWER_SUPPLY_TYPE_MAINS) + val->intval = (charger == CHARGER_AC ? 1 : 0); + else if (psy->type == POWER_SUPPLY_TYPE_USB) + val->intval = (charger == CHARGER_USB ? 1 : 0); + else + val->intval = 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int htc_battery_get_charging_status(void) +{ + u32 level; + charger_type_t charger; + int ret; + + mutex_lock(&htc_batt_info.lock); + charger = htc_batt_info.rep.charging_source; + + switch (charger) { + case CHARGER_BATTERY: + ret = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case CHARGER_USB: + case CHARGER_AC: + level = htc_batt_info.rep.level; + if (level == 100) + ret = POWER_SUPPLY_STATUS_FULL; + else + ret = POWER_SUPPLY_STATUS_CHARGING; + break; + default: + ret = POWER_SUPPLY_STATUS_UNKNOWN; + } + mutex_unlock(&htc_batt_info.lock); + return ret; +} + +static int htc_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = htc_battery_get_charging_status(); + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = htc_batt_info.present; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_CAPACITY: + mutex_lock(&htc_batt_info.lock); + val->intval = htc_batt_info.rep.level; + mutex_unlock(&htc_batt_info.lock); + break; + default: + return -EINVAL; + } + + return 0; +} + +#define HTC_BATTERY_ATTR(_name) \ +{ \ + .attr = { .name = #_name, .mode = S_IRUGO, .owner = THIS_MODULE }, \ + .show = htc_battery_show_property, \ + .store = NULL, \ +} + +static struct device_attribute htc_battery_attrs[] = { + HTC_BATTERY_ATTR(batt_id), + HTC_BATTERY_ATTR(batt_vol), + HTC_BATTERY_ATTR(batt_temp), + HTC_BATTERY_ATTR(batt_current), + HTC_BATTERY_ATTR(charging_source), + HTC_BATTERY_ATTR(charging_enabled), + HTC_BATTERY_ATTR(full_bat), +}; + +enum { + BATT_ID = 0, + BATT_VOL, + BATT_TEMP, + BATT_CURRENT, + CHARGING_SOURCE, + CHARGING_ENABLED, + FULL_BAT, +}; + +static int htc_rpc_set_delta(unsigned delta) +{ + struct set_batt_delta_req { + struct rpc_request_hdr hdr; + uint32_t data; + } req; + + req.data = cpu_to_be32(delta); + return msm_rpc_call(endpoint, HTC_PROCEDURE_SET_BATT_DELTA, + &req, sizeof(req), 5 * HZ); +} + + +static ssize_t htc_battery_set_delta(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + unsigned long delta = 0; + + delta = simple_strtoul(buf, NULL, 10); + + if (delta > 100) + return -EINVAL; + + mutex_lock(&htc_batt_info.rpc_lock); + rc = htc_rpc_set_delta(delta); + mutex_unlock(&htc_batt_info.rpc_lock); + if (rc < 0) + return rc; + return count; +} + +static struct device_attribute htc_set_delta_attrs[] = { + __ATTR(delta, S_IWUSR | S_IWGRP, NULL, htc_battery_set_delta), +}; + +static int htc_battery_create_attrs(struct device * dev) +{ + int i, j, rc; + + for (i = 0; i < ARRAY_SIZE(htc_battery_attrs); i++) { + rc = device_create_file(dev, &htc_battery_attrs[i]); + if (rc) + goto htc_attrs_failed; + } + + for (j = 0; j < ARRAY_SIZE(htc_set_delta_attrs); j++) { + rc = device_create_file(dev, &htc_set_delta_attrs[j]); + if (rc) + goto htc_delta_attrs_failed; + } + + goto succeed; + +htc_attrs_failed: + while (i--) + device_remove_file(dev, &htc_battery_attrs[i]); +htc_delta_attrs_failed: + while (j--) + device_remove_file(dev, &htc_set_delta_attrs[i]); +succeed: + return rc; +} + +static ssize_t htc_battery_show_property(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int i = 0; + const ptrdiff_t off = attr - htc_battery_attrs; + + /* rpc lock is used to prevent two threads from calling + * into the get info rpc at the same time + */ + + mutex_lock(&htc_batt_info.rpc_lock); + /* check cache time to decide if we need to update */ + if (htc_batt_info.update_time && + time_before(jiffies, htc_batt_info.update_time + + msecs_to_jiffies(cache_time))) + goto dont_need_update; + + if (htc_get_batt_info(&htc_batt_info.rep) < 0) { + printk(KERN_ERR "%s: rpc failed!!!\n", __FUNCTION__); + } else { + htc_batt_info.update_time = jiffies; + } +dont_need_update: + mutex_unlock(&htc_batt_info.rpc_lock); + + mutex_lock(&htc_batt_info.lock); + switch (off) { + case BATT_ID: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.batt_id); + break; + case BATT_VOL: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.batt_vol); + break; + case BATT_TEMP: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.batt_temp); + break; + case BATT_CURRENT: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.batt_current); + break; + case CHARGING_SOURCE: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.charging_source); + break; + case CHARGING_ENABLED: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.charging_enabled); + break; + case FULL_BAT: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.full_bat); + break; + default: + i = -EINVAL; + } + mutex_unlock(&htc_batt_info.lock); + + return i; +} + +static int htc_battery_probe(struct platform_device *pdev) +{ + int i, rc; + + /* init battery gpio */ + if ((rc = init_batt_gpio()) < 0) { + printk(KERN_ERR "%s: init battery gpio failed!\n", __FUNCTION__); + return rc; + } + + /* init structure data member */ + htc_batt_info.update_time = jiffies; + htc_batt_info.present = gpio_get_value(GPIO_BATTERY_DETECTION); + + /* init rpc */ + endpoint = msm_rpc_connect(APP_BATT_PROG, APP_BATT_VER, 0); + if (IS_ERR(endpoint)) { + printk(KERN_ERR "%s: init rpc failed! rc = %ld\n", + __FUNCTION__, PTR_ERR(endpoint)); + return rc; + } + + /* init power supplier framework */ + for (i = 0; i < ARRAY_SIZE(htc_power_supplies); i++) { + rc = power_supply_register(&pdev->dev, &htc_power_supplies[i]); + if (rc) + printk(KERN_ERR "Failed to register power supply (%d)\n", rc); + } + + /* create htc detail attributes */ + htc_battery_create_attrs(htc_power_supplies[CHARGER_BATTERY].dev); + + /* After battery driver gets initialized, send rpc request to inquiry + * the battery status in case of we lost some info + */ + htc_battery_initial = 1; + + mutex_lock(&htc_batt_info.rpc_lock); + if (htc_get_batt_info(&htc_batt_info.rep) < 0) + printk(KERN_ERR "%s: get info failed\n", __FUNCTION__); + + htc_cable_status_update(htc_batt_info.rep.charging_source); + battery_charging_ctrl(htc_batt_info.rep.charging_enabled ? + ENABLE_SLOW_CHG : DISABLE); + + if (htc_rpc_set_delta(1) < 0) + printk(KERN_ERR "%s: set delta failed\n", __FUNCTION__); + htc_batt_info.update_time = jiffies; + mutex_unlock(&htc_batt_info.rpc_lock); + + if (htc_batt_info.rep.charging_enabled == 0) + battery_charging_ctrl(DISABLE); + + return 0; +} + +static struct platform_driver htc_battery_driver = { + .probe = htc_battery_probe, + .driver = { + .name = APP_BATT_PDEV_NAME, + .owner = THIS_MODULE, + }, +}; + +/* batt_mtoa server definitions */ +#define BATT_MTOA_PROG 0x30100000 +#define BATT_MTOA_VERS 0 +#define RPC_BATT_MTOA_NULL 0 +#define RPC_BATT_MTOA_SET_CHARGING_PROC 1 +#define RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC 2 +#define RPC_BATT_MTOA_LEVEL_UPDATE_PROC 3 + +struct rpc_batt_mtoa_set_charging_args { + int enable; +}; + +struct rpc_batt_mtoa_cable_status_update_args { + int status; +}; + +struct rpc_dem_battery_update_args { + uint32_t level; +}; + +static int handle_battery_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + switch (req->procedure) { + case RPC_BATT_MTOA_NULL: + return 0; + + case RPC_BATT_MTOA_SET_CHARGING_PROC: { + struct rpc_batt_mtoa_set_charging_args *args; + args = (struct rpc_batt_mtoa_set_charging_args *)(req + 1); + args->enable = be32_to_cpu(args->enable); + BATT("set_charging: enable=%d\n",args->enable); + htc_battery_set_charging(args->enable); + return 0; + } + case RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC: { + struct rpc_batt_mtoa_cable_status_update_args *args; + args = (struct rpc_batt_mtoa_cable_status_update_args *)(req + 1); + args->status = be32_to_cpu(args->status); + BATT("cable_status_update: status=%d\n",args->status); + htc_cable_status_update(args->status); + return 0; + } + case RPC_BATT_MTOA_LEVEL_UPDATE_PROC: { + struct rpc_dem_battery_update_args *args; + args = (struct rpc_dem_battery_update_args *)(req + 1); + args->level = be32_to_cpu(args->level); + BATT("dem_battery_update: level=%d\n",args->level); + htc_battery_status_update(args->level); + return 0; + } + default: + printk(KERN_ERR "%s: program 0x%08x:%d: unknown procedure %d\n", + __FUNCTION__, req->prog, req->vers, req->procedure); + return -ENODEV; + } +} + +static struct msm_rpc_server battery_server = { + .prog = BATT_MTOA_PROG, + .vers = BATT_MTOA_VERS, + .rpc_call = handle_battery_call, +}; + +static int __init htc_battery_init(void) +{ + wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present"); + mutex_init(&htc_batt_info.lock); + mutex_init(&htc_batt_info.rpc_lock); + msm_rpc_create_server(&battery_server); + platform_driver_register(&htc_battery_driver); + return 0; +} + +module_init(htc_battery_init); +MODULE_DESCRIPTION("HTC Battery Driver"); +MODULE_LICENSE("GPL"); + From 4d25282e561bd8a41fdbd4058aa6322f36d45d8f Mon Sep 17 00:00:00 2001 From: Thomas Tsai Date: Thu, 26 Feb 2009 11:41:54 -0800 Subject: [PATCH 0205/2556] [ARM] htc_battery: Add support for Sapphire charging + China Mobile AC adapter Signed-off-by: San Mehat --- arch/arm/mach-msm/htc_battery.c | 114 ++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 28 deletions(-) diff --git a/arch/arm/mach-msm/htc_battery.c b/arch/arm/mach-msm/htc_battery.c index 7320edbff1a9f..bce7c03d891d3 100644 --- a/arch/arm/mach-msm/htc_battery.c +++ b/arch/arm/mach-msm/htc_battery.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include static struct wake_lock vbus_wake_lock; @@ -52,6 +54,11 @@ static struct wake_lock vbus_wake_lock; /* Enable this will shut down if no battery */ #define ENABLE_BATTERY_DETECTION 0 +/* Sapphire pin changes: + * USB_ID (GPIO 90) is renamed to AC_IN (GPIO 30) + * CHARGER_EN (CPLD MISC2 bit[0]) is move to PMIC (MPP_14). + * ISET (CPLD MISC2 bit[1]) is move to PMIC (MPP_13). */ +#define GPIO_SAPPHIRE_USB_ID 30 #define GPIO_BATTERY_DETECTION 21 #define GPIO_BATTERY_CHARGER_EN 128 @@ -74,6 +81,8 @@ typedef enum { CHARGER_AC } charger_type_t; +const char *charger_tags[] = {"none", "USB", "AC"}; + struct battery_info_reply { u32 batt_id; /* Battery ID from ADC */ u32 batt_vol; /* Battery voltage from ADC */ @@ -162,6 +171,12 @@ static struct power_supply htc_power_supplies[] = { }, }; +static void usb_status_notifier_func(int online); +static int g_usb_online; +static struct t_usb_status_notifier usb_status_notifier = { + .name = "htc_battery", + .func = usb_status_notifier_func, +}; /* -------------------------------------------------------------------------- */ @@ -196,6 +211,9 @@ device_initcall(batt_debug_init); static int init_batt_gpio(void) { + if (!machine_is_trout()) + return 0; + if (gpio_request(GPIO_BATTERY_DETECTION, "batt_detect") < 0) goto gpio_failed; if (gpio_request(GPIO_BATTERY_CHARGER_EN, "charger_en") < 0) @@ -219,6 +237,10 @@ static int battery_charging_ctrl(batt_ctl_t ctl) { int result = 0; + /* The charing operations are move to A9 in Sapphire. */ + if (!machine_is_trout()) + return result; + switch (ctl) { case DISABLE: BATT("charger OFF\n"); @@ -281,35 +303,35 @@ int htc_battery_status_update(u32 curr_level) int htc_cable_status_update(int status) { int rc = 0; - unsigned source; + unsigned last_source; if (!htc_battery_initial) return 0; + if (status < CHARGER_BATTERY || status > CHARGER_AC) { + BATT("%s: Not supported cable status received!\n", __func__); + return -EINVAL; + } mutex_lock(&htc_batt_info.lock); - switch(status) { - case CHARGER_BATTERY: - BATT("cable NOT PRESENT\n"); - htc_batt_info.rep.charging_source = CHARGER_BATTERY; - break; - case CHARGER_USB: - BATT("cable USB\n"); - htc_batt_info.rep.charging_source = CHARGER_USB; - break; - case CHARGER_AC: - BATT("cable AC\n"); + /* A9 reports USB charging when helf AC cable in and China AC charger. */ + /* Work arround: notify userspace AC charging first, + and notify USB charging again when receiving usb connected notificaiton from usb driver. */ + last_source = htc_batt_info.rep.charging_source; + if (status == CHARGER_USB && g_usb_online == 0) htc_batt_info.rep.charging_source = CHARGER_AC; - break; - default: - printk(KERN_ERR "%s: Not supported cable status received!\n", - __FUNCTION__); - rc = -EINVAL; + else { + htc_batt_info.rep.charging_source = status; + /* usb driver will not notify usb offline. */ + if (status == CHARGER_BATTERY && g_usb_online == 1) + g_usb_online = 0; } - source = htc_batt_info.rep.charging_source; - mutex_unlock(&htc_batt_info.lock); - msm_hsusb_set_vbus_state(source == CHARGER_USB); - if (source == CHARGER_USB) { + /* TODO: Don't call usb driver again with the same cable status. */ + msm_hsusb_set_vbus_state(status == CHARGER_USB); + + if (htc_batt_info.rep.charging_source != last_source) { + if (htc_batt_info.rep.charging_source == CHARGER_USB || + htc_batt_info.rep.charging_source == CHARGER_AC) { wake_lock(&vbus_wake_lock); } else { /* give userspace some time to see the uevent and update @@ -317,15 +339,40 @@ int htc_cable_status_update(int status) */ wake_lock_timeout(&vbus_wake_lock, HZ / 2); } - - /* if the power source changes, all power supplies may change state */ + if (htc_batt_info.rep.charging_source == CHARGER_BATTERY || last_source == CHARGER_BATTERY) power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + if (htc_batt_info.rep.charging_source == CHARGER_USB || last_source == CHARGER_USB) power_supply_changed(&htc_power_supplies[CHARGER_USB]); + if (htc_batt_info.rep.charging_source == CHARGER_AC || last_source == CHARGER_AC) power_supply_changed(&htc_power_supplies[CHARGER_AC]); + } + mutex_unlock(&htc_batt_info.lock); return rc; } +/* A9 reports USB charging when helf AC cable in and China AC charger. */ +/* Work arround: notify userspace AC charging first, +and notify USB charging again when receiving usb connected notificaiton from usb driver. */ +static void usb_status_notifier_func(int online) +{ + mutex_lock(&htc_batt_info.lock); + + BATT("%s: online=%d, g_usb_online=%d\n", __func__, online, g_usb_online); + + if (g_usb_online != online) { + g_usb_online = online; + if (online && htc_batt_info.rep.charging_source == CHARGER_AC) { + mutex_unlock(&htc_batt_info.lock); + htc_cable_status_update(CHARGER_USB); + mutex_lock(&htc_batt_info.lock); + } else if (online) { + BATT("warning: usb connected but charging source=%d\n", htc_batt_info.rep.charging_source); + } + } + mutex_unlock(&htc_batt_info.lock); +} + static int htc_get_batt_info(struct battery_info_reply *buffer) { struct rpc_request_hdr req; @@ -353,7 +400,8 @@ static int htc_get_batt_info(struct battery_info_reply *buffer) buffer->batt_temp = be32_to_cpu(rep.info.batt_temp); buffer->batt_current = be32_to_cpu(rep.info.batt_current); buffer->level = be32_to_cpu(rep.info.level); - buffer->charging_source = be32_to_cpu(rep.info.charging_source); + /* Move the rules of charging_source to cable_status_update. */ + /* buffer->charging_source = be32_to_cpu(rep.info.charging_source); */ buffer->charging_enabled = be32_to_cpu(rep.info.charging_enabled); buffer->full_bat = be32_to_cpu(rep.info.full_bat); mutex_unlock(&htc_batt_info.lock); @@ -637,8 +685,11 @@ static int htc_battery_probe(struct platform_device *pdev) /* init structure data member */ htc_batt_info.update_time = jiffies; - htc_batt_info.present = gpio_get_value(GPIO_BATTERY_DETECTION); - + /* A9 will shutdown the phone if battery is pluged out, so this value is always 1. + htc_batt_info.present = gpio_get_value(GPIO_TROUT_MBAT_IN); + */ + htc_batt_info.present = 1; + /* init rpc */ endpoint = msm_rpc_connect(APP_BATT_PROG, APP_BATT_VER, 0); if (IS_ERR(endpoint)) { @@ -663,21 +714,27 @@ static int htc_battery_probe(struct platform_device *pdev) htc_battery_initial = 1; mutex_lock(&htc_batt_info.rpc_lock); + htc_batt_info.rep.charging_source = CHARGER_BATTERY; if (htc_get_batt_info(&htc_batt_info.rep) < 0) printk(KERN_ERR "%s: get info failed\n", __FUNCTION__); - htc_cable_status_update(htc_batt_info.rep.charging_source); +#if 0 + /* A9 will send cable status RPC first, remove this code. */ battery_charging_ctrl(htc_batt_info.rep.charging_enabled ? ENABLE_SLOW_CHG : DISABLE); +#endif if (htc_rpc_set_delta(1) < 0) printk(KERN_ERR "%s: set delta failed\n", __FUNCTION__); htc_batt_info.update_time = jiffies; mutex_unlock(&htc_batt_info.rpc_lock); +#if 0 + /* A11 shouldn't control charger here, let A9 do it. */ if (htc_batt_info.rep.charging_enabled == 0) battery_charging_ctrl(DISABLE); - +#endif + return 0; } @@ -758,6 +815,7 @@ static int __init htc_battery_init(void) wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present"); mutex_init(&htc_batt_info.lock); mutex_init(&htc_batt_info.rpc_lock); + usb_register_notifier(&usb_status_notifier); msm_rpc_create_server(&battery_server); platform_driver_register(&htc_battery_driver); return 0; From 50f4ea98d577d7c9fef2e3bf2b263e0599acc5fe Mon Sep 17 00:00:00 2001 From: San Mehat Date: Thu, 26 Feb 2009 11:43:14 -0800 Subject: [PATCH 0206/2556] [ARM] htc_battery: Remove some #if 0s Signed-off-by: San Mehat --- arch/arm/mach-msm/htc_battery.c | 36 --------------------------------- 1 file changed, 36 deletions(-) diff --git a/arch/arm/mach-msm/htc_battery.c b/arch/arm/mach-msm/htc_battery.c index bce7c03d891d3..cfdd12d2204f6 100644 --- a/arch/arm/mach-msm/htc_battery.c +++ b/arch/arm/mach-msm/htc_battery.c @@ -409,30 +409,6 @@ static int htc_get_batt_info(struct battery_info_reply *buffer) return 0; } -#if 0 -static int htc_get_cable_status(void) -{ - - struct rpc_request_hdr req; - - struct htc_get_cable_status_rep { - struct rpc_reply_hdr hdr; - int status; - } rep; - - int rc; - - rc = msm_rpc_call_reply(endpoint, HTC_PROCEDURE_GET_CABLE_STATUS, - &req, sizeof(req), - &rep, sizeof(rep), - 5 * HZ); - if (rc < 0) - return rc; - - return be32_to_cpu(rep.status); -} -#endif - /* -------------------------------------------------------------------------- */ static int htc_power_get_property(struct power_supply *psy, enum power_supply_property psp, @@ -718,23 +694,11 @@ static int htc_battery_probe(struct platform_device *pdev) if (htc_get_batt_info(&htc_batt_info.rep) < 0) printk(KERN_ERR "%s: get info failed\n", __FUNCTION__); -#if 0 - /* A9 will send cable status RPC first, remove this code. */ - battery_charging_ctrl(htc_batt_info.rep.charging_enabled ? - ENABLE_SLOW_CHG : DISABLE); -#endif - if (htc_rpc_set_delta(1) < 0) printk(KERN_ERR "%s: set delta failed\n", __FUNCTION__); htc_batt_info.update_time = jiffies; mutex_unlock(&htc_batt_info.rpc_lock); -#if 0 - /* A11 shouldn't control charger here, let A9 do it. */ - if (htc_batt_info.rep.charging_enabled == 0) - battery_charging_ctrl(DISABLE); -#endif - return 0; } From d70bf0894d110427431e0a3c9d7517e5ba455624 Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Tue, 10 Mar 2009 19:15:11 -0700 Subject: [PATCH 0207/2556] [ARM] msm: battery: Enable BATT_TRACE. BATT logging macro now pretty prints wall time timestamps. Signed-off-by: Mike Chan [ARM] msm: battery: Disable BATT_TRACE. Signed-off-by: Mike Chan --- arch/arm/mach-msm/htc_battery.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-msm/htc_battery.c b/arch/arm/mach-msm/htc_battery.c index cfdd12d2204f6..deb5a12081d18 100644 --- a/arch/arm/mach-msm/htc_battery.c +++ b/arch/arm/mach-msm/htc_battery.c @@ -33,7 +33,18 @@ static struct wake_lock vbus_wake_lock; #define TRACE_BATT 0 #if TRACE_BATT -#define BATT(x...) printk(KERN_INFO "[BATT] " x) +#include + +#define BATT(x...) do { \ +struct timespec ts; \ +struct rtc_time tm; \ +getnstimeofday(&ts); \ +rtc_time_to_tm(ts.tv_sec, &tm); \ +printk(KERN_INFO "[BATT] " x); \ +printk(" at %lld (%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", \ +ktime_to_ns(ktime_get()), tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \ +tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \ +} while (0) #else #define BATT(x...) do {} while (0) #endif @@ -243,17 +254,17 @@ static int battery_charging_ctrl(batt_ctl_t ctl) switch (ctl) { case DISABLE: - BATT("charger OFF\n"); + BATT("charger OFF"); /* 0 for enable; 1 disable */ result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 1); break; case ENABLE_SLOW_CHG: - BATT("charger ON (SLOW)\n"); + BATT("charger ON (SLOW)"); result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 0); result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0); break; case ENABLE_FAST_CHG: - BATT("charger ON (FAST)\n"); + BATT("charger ON (FAST)"); result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 1); result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0); break; @@ -309,7 +320,7 @@ int htc_cable_status_update(int status) return 0; if (status < CHARGER_BATTERY || status > CHARGER_AC) { - BATT("%s: Not supported cable status received!\n", __func__); + BATT("%s: Not supported cable status received!", __func__); return -EINVAL; } mutex_lock(&htc_batt_info.lock); @@ -358,7 +369,7 @@ static void usb_status_notifier_func(int online) { mutex_lock(&htc_batt_info.lock); - BATT("%s: online=%d, g_usb_online=%d\n", __func__, online, g_usb_online); + BATT("%s: online=%d, g_usb_online=%d", __func__, online, g_usb_online); if (g_usb_online != online) { g_usb_online = online; @@ -367,7 +378,7 @@ static void usb_status_notifier_func(int online) htc_cable_status_update(CHARGER_USB); mutex_lock(&htc_batt_info.lock); } else if (online) { - BATT("warning: usb connected but charging source=%d\n", htc_batt_info.rep.charging_source); + BATT("warning: usb connected but charging source=%d", htc_batt_info.rep.charging_source); } } mutex_unlock(&htc_batt_info.lock); @@ -741,7 +752,7 @@ static int handle_battery_call(struct msm_rpc_server *server, struct rpc_batt_mtoa_set_charging_args *args; args = (struct rpc_batt_mtoa_set_charging_args *)(req + 1); args->enable = be32_to_cpu(args->enable); - BATT("set_charging: enable=%d\n",args->enable); + BATT("set_charging: enable=%d",args->enable); htc_battery_set_charging(args->enable); return 0; } @@ -749,7 +760,7 @@ static int handle_battery_call(struct msm_rpc_server *server, struct rpc_batt_mtoa_cable_status_update_args *args; args = (struct rpc_batt_mtoa_cable_status_update_args *)(req + 1); args->status = be32_to_cpu(args->status); - BATT("cable_status_update: status=%d\n",args->status); + BATT("cable_status_update: status=%d",args->status); htc_cable_status_update(args->status); return 0; } @@ -757,7 +768,7 @@ static int handle_battery_call(struct msm_rpc_server *server, struct rpc_dem_battery_update_args *args; args = (struct rpc_dem_battery_update_args *)(req + 1); args->level = be32_to_cpu(args->level); - BATT("dem_battery_update: level=%d\n",args->level); + BATT("dem_battery_update: level=%d",args->level); htc_battery_status_update(args->level); return 0; } From cf5e7e37f0eadc0091d301a093da58f27ba94b2c Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Date: Wed, 29 Oct 2008 16:57:59 -0700 Subject: [PATCH 0208/2556] HACK: [ARM] msm: trout: Add msm_fb support to trout architecture -- taken from android-2.6.35 tree Change-Id: Ief434164df59e17741def60a390d03578248d7b4 --- arch/arm/mach-msm/board-trout-panel.c | 405 ++++++++++++++++++++++++-- 1 file changed, 377 insertions(+), 28 deletions(-) diff --git a/arch/arm/mach-msm/board-trout-panel.c b/arch/arm/mach-msm/board-trout-panel.c index 729bb49a44caf..e81374c30875a 100644 --- a/arch/arm/mach-msm/board-trout-panel.c +++ b/arch/arm/mach-msm/board-trout-panel.c @@ -16,6 +16,9 @@ #include #include +#ifdef CONFIG_TROUT_PWRSINK +#include +#endif #include "board-trout.h" #include "proc_comm.h" @@ -23,6 +26,72 @@ #define TROUT_DEFAULT_BACKLIGHT_BRIGHTNESS 255 +static struct clk *gp_clk; +static int trout_backlight_off; +static int trout_backlight_brightness = TROUT_DEFAULT_BACKLIGHT_BRIGHTNESS; +static int trout_new_backlight = 1; +static uint8_t trout_backlight_last_level = 33; +static DEFINE_MUTEX(trout_backlight_lock); + +static void trout_set_backlight_level(uint8_t level) +{ +#ifdef CONFIG_TROUT_PWRSINK + unsigned percent = ((int)level * 100) / 255; +#endif + + if (trout_new_backlight) { + unsigned long flags; + int i = 0; + level = (int)level * 34 / 256; + + if (trout_backlight_last_level == level) + return; + + if (level == 0) { + gpio_set_value(27, 0); + msleep(2); + } else { + local_irq_save(flags); + if (trout_backlight_last_level == 0) { + gpio_set_value(27, 1); + udelay(40); + trout_backlight_last_level = 33; + } + i = (trout_backlight_last_level - level + 33) % 33; + while (i-- > 0) { + gpio_set_value(27, 0); + udelay(1); + gpio_set_value(27, 1); + udelay(1); + } + local_irq_restore(flags); + } + trout_backlight_last_level = level; + } + else { + if(level) { + clk_enable(gp_clk); + writel((1U << 16) | (~level & 0xffff), + MSM_CLK_CTL_BASE + 0x58); + /* Going directly to a 100% duty cycle does not + * seem to work */ + if(level == 255) { + writel((~127 << 16) | 0xb20, + MSM_CLK_CTL_BASE + 0x5c); + udelay(1); + } + writel((~127 << 16) | 0xb58, MSM_CLK_CTL_BASE + 0x5c); + } + else { + writel(0x0, MSM_CLK_CTL_BASE + 0x5c); + clk_disable(gp_clk); + } + } +#ifdef CONFIG_TROUT_PWRSINK + trout_pwrsink_set(PWRSINK_BACKLIGHT, percent); +#endif +} + #define MDDI_CLIENT_CORE_BASE 0x108000 #define LCD_CONTROL_BLOCK_BASE 0x110000 #define SPI_BLOCK_BASE 0x120000 @@ -147,7 +216,9 @@ static struct mddi_table mddi_toshiba_init_table[] = { { DPRUN, 0x00000001 }, { 1, 14 }, /* msleep 14 */ { SYSCKENA, 0x00000001 }, + //{ CLKENB, 0x000000EF }, { CLKENB, 0x0000A1EF }, /* # SYS.CLKENB # Enable clocks for each module (without DCLK , i2cCLK) */ + //{ CLKENB, 0x000025CB }, /* Clock enable register */ { GPIODATA, 0x02000200 }, /* # GPI .GPIODATA # GPIO2(RESET_LCD_N) set to 0 , GPIO3(eDRAM_Power) set to 0 */ { GPIODIR, 0x000030D }, /* 24D # GPI .GPIODIR # Select direction of GPIO port (0,2,3,6,9 output) */ @@ -166,20 +237,136 @@ static struct mddi_table mddi_toshiba_init_table[] = { { DRAMPWR, 0x00000001 }, /* eDRAM power */ }; -#define GPIOSEL_VWAKEINT (1U << 0) -#define INTMASK_VWAKEOUT (1U << 0) +static struct mddi_table mddi_toshiba_panel_init_table[] = { + { SRST, 0x00000003 }, /* FIFO/LCDC not reset */ + { PORT_ENB, 0x00000001 }, /* Enable sync. Port */ + { START, 0x00000000 }, /* To stop operation */ + //{ START, 0x00000001 }, /* To start operation */ + { PORT, 0x00000004 }, /* Polarity of VS/HS/DE. */ + { CMN, 0x00000000 }, + { GAMMA, 0x00000000 }, /* No Gamma correction */ + { INTFLG, 0x00000000 }, /* VSYNC interrupt flag clear/status */ + { INTMSK, 0x00000000 }, /* VSYNC interrupt mask is off. */ + { MPLFBUF, 0x00000000 }, /* Select frame buffer's base address. */ + { HDE_LEFT, 0x00000000 }, /* The value of HDE_LEFT. */ + { VDE_TOP, 0x00000000 }, /* The value of VDE_TPO. */ + { PXL, 0x00000001 }, /* 1. RGB666 */ + /* 2. Data is valid from 1st frame of beginning. */ + { HDE_START, 0x00000006 }, /* HDE_START= 14 PCLK */ + { HDE_SIZE, 0x0000009F }, /* HDE_SIZE=320 PCLK */ + { HSW, 0x00000004 }, /* HSW= 10 PCLK */ + { VSW, 0x00000001 }, /* VSW=2 HCYCLE */ + { VDE_START, 0x00000003 }, /* VDE_START=4 HCYCLE */ + { VDE_SIZE, 0x000001DF }, /* VDE_SIZE=480 HCYCLE */ + { WAKEUP, 0x000001e2 }, /* Wakeup position in VSYNC mode. */ + { WSYN_DLY, 0x00000000 }, /* Wakeup position in VSIN mode. */ + { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */ + { CLKENB, 0x000025CB }, /* Clock enable register */ + + { SSICTL, 0x00000170 }, /* SSI control register */ + { SSITIME, 0x00000250 }, /* SSI timing control register */ + { SSICTL, 0x00000172 }, /* SSI control register */ +}; -static struct clk *gp_clk; -static int trout_new_backlight = 1; -static struct vreg *vreg_mddi_1v5; -static struct vreg *vreg_lcm_2v85; +static struct mddi_table mddi_sharp_init_table[] = { + { VCYCLE, 0x000001eb }, + { HCYCLE, 0x000000ae }, + { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */ + { GPIODATA, 0x00040000 }, /* GPIO2 low */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { 1, 1 }, /* msleep 1 */ + { GPIODATA, 0x00040004 }, /* GPIO2 high */ + { 1, 10 }, /* msleep 10 */ + SPI_WRITE(0x5f, 0x01) + SPI_WRITE1(0x11) + { 1, 200 }, /* msleep 200 */ + SPI_WRITE1(0x29) + SPI_WRITE1(0xde) + { START, 0x00000001 }, /* To start operation */ +}; -static void trout_process_mddi_table(struct msm_mddi_client_data *client_data, +static struct mddi_table mddi_sharp_deinit_table[] = { + { 1, 200 }, /* msleep 200 */ + SPI_WRITE(0x10, 0x1) + { 1, 100 }, /* msleep 100 */ + { GPIODATA, 0x00040004 }, /* GPIO2 high */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { GPIODATA, 0x00040000 }, /* GPIO2 low */ + { 1, 10 }, /* msleep 10 */ +}; + +static struct mddi_table mddi_tpo_init_table[] = { + { VCYCLE, 0x000001e5 }, + { HCYCLE, 0x000000ac }, + { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */ + { 0, 20 }, /* udelay 20 */ + { GPIODATA, 0x00000004 }, /* GPIO2 high */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { 0, 20 }, /* udelay 20 */ + + SPI_WRITE(0x08, 0x01) + { 0, 500 }, /* udelay 500 */ + SPI_WRITE(0x08, 0x00) + SPI_WRITE(0x02, 0x00) + SPI_WRITE(0x03, 0x04) + SPI_WRITE(0x04, 0x0e) + SPI_WRITE(0x09, 0x02) + SPI_WRITE(0x0b, 0x08) + SPI_WRITE(0x0c, 0x53) + SPI_WRITE(0x0d, 0x01) + SPI_WRITE(0x0e, 0xe0) + SPI_WRITE(0x0f, 0x01) + SPI_WRITE(0x10, 0x58) + SPI_WRITE(0x20, 0x1e) + SPI_WRITE(0x21, 0x0a) + SPI_WRITE(0x22, 0x0a) + SPI_WRITE(0x23, 0x1e) + SPI_WRITE(0x25, 0x32) + SPI_WRITE(0x26, 0x00) + SPI_WRITE(0x27, 0xac) + SPI_WRITE(0x29, 0x06) + SPI_WRITE(0x2a, 0xa4) + SPI_WRITE(0x2b, 0x45) + SPI_WRITE(0x2c, 0x45) + SPI_WRITE(0x2d, 0x15) + SPI_WRITE(0x2e, 0x5a) + SPI_WRITE(0x2f, 0xff) + SPI_WRITE(0x30, 0x6b) + SPI_WRITE(0x31, 0x0d) + SPI_WRITE(0x32, 0x48) + SPI_WRITE(0x33, 0x82) + SPI_WRITE(0x34, 0xbd) + SPI_WRITE(0x35, 0xe7) + SPI_WRITE(0x36, 0x18) + SPI_WRITE(0x37, 0x94) + SPI_WRITE(0x38, 0x01) + SPI_WRITE(0x39, 0x5d) + SPI_WRITE(0x3a, 0xae) + SPI_WRITE(0x3b, 0xff) + SPI_WRITE(0x07, 0x09) + { 0, 10 }, /* udelay 10 */ + { START, 0x00000001 }, /* To start operation */ +}; + +static struct mddi_table mddi_tpo_deinit_table[] = { + SPI_WRITE(0x07, 0x19) + { START, 0x00000000 }, /* To stop operation */ + { GPIODATA, 0x00040004 }, /* GPIO2 high */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { GPIODATA, 0x00040000 }, /* GPIO2 low */ + { 0, 5 }, /* usleep 5 */ +}; + + +#define GPIOSEL_VWAKEINT (1U << 0) +#define INTMASK_VWAKEOUT (1U << 0) + +static void trout_process_mddi_table(struct msm_mddi_client_data *cdata, struct mddi_table *table, size_t count) { int i; - for (i = 0; i < count; i++) { + for(i = 0; i < count; i++) { uint32_t reg = table[i].reg; uint32_t value = table[i].value; @@ -188,35 +375,187 @@ static void trout_process_mddi_table(struct msm_mddi_client_data *client_data, else if (reg == 1) msleep(value); else - client_data->remote_write(client_data, value, reg); + cdata->remote_write(cdata, value, reg); } } -static int trout_mddi_toshiba_client_init( - struct msm_mddi_bridge_platform_data *bridge_data, - struct msm_mddi_client_data *client_data) +static struct vreg *vreg_mddi_1v5; +static struct vreg *vreg_lcm_2v85; + +static void trout_mddi_power_client(struct msm_mddi_client_data *cdata, + int on) +{ + unsigned id, on_off; + if(on) { + on_off = 0; + id = PM_VREG_PDOWN_MDDI_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + vreg_enable(vreg_mddi_1v5); + mdelay(5); // delay time >5ms and <10ms + gpio_set_value(V_VDDE2E_VDD2_GPIO, 1); + gpio_set_value(TROUT_GPIO_MDDI_32K_EN, 1); + msleep(3); + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + vreg_enable(vreg_lcm_2v85); + msleep(3); + gpio_set_value(MDDI_RST_N, 1); + msleep(10); + } else { + gpio_set_value(TROUT_GPIO_MDDI_32K_EN, 0); + gpio_set_value(MDDI_RST_N, 0); + msleep(10); + vreg_disable(vreg_lcm_2v85); + on_off = 1; + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + msleep(5); + gpio_set_value(V_VDDE2E_VDD2_GPIO, 0); + msleep(200); + vreg_disable(vreg_mddi_1v5); + id = PM_VREG_PDOWN_MDDI_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + } +} + +static int trout_mddi_toshiba_client_init(struct msm_mddi_client_data *cdata) { int panel_id; - client_data->auto_hibernate(client_data, 0); - trout_process_mddi_table(client_data, mddi_toshiba_init_table, + cdata->auto_hibernate(cdata, 0); + trout_process_mddi_table(cdata, mddi_toshiba_init_table, ARRAY_SIZE(mddi_toshiba_init_table)); - client_data->auto_hibernate(client_data, 1); - panel_id = (client_data->remote_read(client_data, GPIODATA) >> 4) & 3; + cdata->auto_hibernate(cdata, 1); + panel_id = (cdata->remote_read(cdata, GPIODATA) >> 4) & 3; if (panel_id > 1) { - printk(KERN_WARNING "unknown panel id at mddi_enable\n"); + printk("unknown panel id at mddi_enable\n"); return -1; } return 0; } -static int trout_mddi_toshiba_client_uninit( - struct msm_mddi_bridge_platform_data *bridge_data, - struct msm_mddi_client_data *client_data) +static int trout_mddi_toshiba_client_uninit(struct msm_mddi_client_data *cdata) +{ + return 0; +} + +static int trout_mddi_panel_unblank(struct msm_panel_data *panel_data) +{ + struct msm_mddi_panel_info *panel = container_of(panel_data, + struct msm_mddi_panel_info, panel_data); + struct msm_mddi_client_data *mddi_client = panel->client_data; + + int panel_id, ret = 0; + + trout_set_backlight_level(0); + mddi_client->auto_hibernate(mddi_client, 0); + trout_process_mddi_table(mddi_client, mddi_toshiba_panel_init_table, + ARRAY_SIZE(mddi_toshiba_panel_init_table)); + panel_id = (mddi_client->remote_read(mddi_client, GPIODATA) >> 4) & 3; + switch(panel_id) { + case 0: + printk("init sharp panel\n"); + trout_process_mddi_table(mddi_client, + mddi_sharp_init_table, + ARRAY_SIZE(mddi_sharp_init_table)); + break; + case 1: + printk("init tpo panel\n"); + trout_process_mddi_table(mddi_client, + mddi_tpo_init_table, + ARRAY_SIZE(mddi_tpo_init_table)); + break; + default: + printk("unknown panel_id: %d\n", panel_id); + ret = -1; + }; + mutex_lock(&trout_backlight_lock); + trout_set_backlight_level(trout_backlight_brightness); + trout_backlight_off = 0; + mutex_unlock(&trout_backlight_lock); + mddi_client->auto_hibernate(mddi_client, 1); + // reenable vsync + mddi_client->remote_write(mddi_client, GPIOSEL_VWAKEINT, + GPIOSEL); + mddi_client->remote_write(mddi_client, INTMASK_VWAKEOUT, + INTMASK); + return ret; + +} + +static int trout_mddi_panel_blank(struct msm_panel_data *panel_data) +{ + struct msm_mddi_panel_info *panel = container_of(panel_data, + struct msm_mddi_panel_info, panel_data); + struct msm_mddi_client_data *mddi_client = panel->client_data; + int panel_id, ret = 0; + + panel_id = (mddi_client->remote_read(mddi_client, GPIODATA) >> 4) & 3; + mddi_client->auto_hibernate(mddi_client, 0); + switch(panel_id) { + case 0: + printk("deinit sharp panel\n"); + trout_process_mddi_table(mddi_client, + mddi_sharp_deinit_table, + ARRAY_SIZE(mddi_sharp_deinit_table)); + break; + case 1: + printk("deinit tpo panel\n"); + trout_process_mddi_table(mddi_client, + mddi_tpo_deinit_table, + ARRAY_SIZE(mddi_tpo_deinit_table)); + break; + default: + printk("unknown panel_id: %d\n", panel_id); + ret = -1; + }; + mddi_client->auto_hibernate(mddi_client,1); + mutex_lock(&trout_backlight_lock); + trout_set_backlight_level(0); + trout_backlight_off = 1; + mutex_unlock(&trout_backlight_lock); + mddi_client->remote_write(mddi_client, 0, SYSCLKENA); + mddi_client->remote_write(mddi_client, 1, DPSUS); + + return ret; +} + +static void trout_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) +{ + mutex_lock(&trout_backlight_lock); + trout_backlight_brightness = value; + if(!trout_backlight_off) + trout_set_backlight_level(trout_backlight_brightness); + mutex_unlock(&trout_backlight_lock); +} + +static struct led_classdev trout_backlight_led = { + .name = "lcd-backlight", + .brightness = TROUT_DEFAULT_BACKLIGHT_BRIGHTNESS, + .brightness_set = trout_brightness_set, +}; + +static int trout_backlight_probe(struct platform_device *pdev) +{ + led_classdev_register(&pdev->dev, &trout_backlight_led); + return 0; +} + +static int trout_backlight_remove(struct platform_device *pdev) { + led_classdev_unregister(&trout_backlight_led); return 0; } +static struct platform_driver trout_backlight_driver = { + .probe = trout_backlight_probe, + .remove = trout_backlight_remove, + .driver = { + .name = "trout-backlight", + .owner = THIS_MODULE, + }, +}; + static struct resource resources_msm_fb[] = { { .start = MSM_FB_BASE, @@ -225,20 +564,21 @@ static struct resource resources_msm_fb[] = { }, }; -struct msm_mddi_bridge_platform_data toshiba_client_data = { +struct msm_mddi_toshiba_client_data toshiba_client_data = { .init = trout_mddi_toshiba_client_init, .uninit = trout_mddi_toshiba_client_uninit, + .blank = trout_mddi_panel_blank, + .unblank = trout_mddi_panel_unblank, .fb_data = { .xres = 320, .yres = 480, - .width = 45, - .height = 67, .output_format = 0, }, }; -static struct msm_mddi_platform_data mddi_pdata = { +struct msm_mddi_platform_data mddi_pdata = { .clk_rate = 122880000, + .power_client = trout_mddi_power_client, .fb_resource = resources_msm_fb, .num_clients = 1, .client_platform_data = { @@ -252,12 +592,16 @@ static struct msm_mddi_platform_data mddi_pdata = { }, }; +static struct platform_device trout_backlight = { + .name = "trout-backlight", +}; + int __init trout_init_panel(void) { int rc; - if (!machine_is_trout()) - return 0; + if (!machine_is_trout()) + return 0; vreg_mddi_1v5 = vreg_get(0, "gp2"); if (IS_ERR(vreg_mddi_1v5)) return PTR_ERR(vreg_mddi_1v5); @@ -270,7 +614,8 @@ int __init trout_init_panel(void) uint32_t config = PCOM_GPIO_CFG(27, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA); msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, 0); - } else { + } + else { uint32_t config = PCOM_GPIO_CFG(27, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA); msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, 0); @@ -291,7 +636,11 @@ int __init trout_init_panel(void) if (rc) return rc; msm_device_mddi0.dev.platform_data = &mddi_pdata; - return platform_device_register(&msm_device_mddi0); + rc = platform_device_register(&msm_device_mddi0); + if (rc) + return rc; + platform_device_register(&trout_backlight); + return platform_driver_register(&trout_backlight_driver); } device_initcall(trout_init_panel); From 4198beac45c1857c764f7ac3e69bd06396b42ff4 Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Date: Wed, 10 Sep 2008 16:45:02 -0700 Subject: [PATCH 0209/2556] [ARM] msm: Add software pte bits to allow userspace mapping of device memory Memory between 0x88000000 and 0xD0000000 must be accessed with PTE_EXT_TEX(2) These bits have to be reset when mapping to userspace or these io regions can't be mapped to userspace. The new L_PTE_DEVICE setting indicates that the memroy in question is MT_DEVICE_NONSHARED. Signed-off-by: Rebecca Schultz Signed-off-by: Rebecca Schultz --- arch/arm/include/asm/pgtable.h | 15 ++++++++++++- arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/include/mach/memory.h | 2 ++ arch/arm/mach-msm/memory.c | 29 +++++++++++++++++++++++++ drivers/char/mem.c | 2 +- 5 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 arch/arm/mach-msm/memory.c diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h index ebcb6432f45f8..1c898fcf80105 100644 --- a/arch/arm/include/asm/pgtable.h +++ b/arch/arm/include/asm/pgtable.h @@ -22,6 +22,7 @@ #include #include +#include #include /* @@ -232,6 +233,9 @@ extern pgprot_t pgprot_kernel; #define pgprot_writecombine(prot) \ __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE) +#define pgprot_device(prot) \ + __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_DEV_NONSHARED) + #ifdef CONFIG_ARM_DMA_MEM_BUFFERABLE #define pgprot_dmacoherent(prot) \ __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE | L_PTE_XN) @@ -468,8 +472,17 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) * remap a physical page `pfn' of size `size' with page protection `prot' * into virtual address `from' */ + + +#ifndef HAS_ARCH_IO_REMAP_PFN_RANGE #define io_remap_pfn_range(vma,from,pfn,size,prot) \ - remap_pfn_range(vma, from, pfn, size, prot) + remap_pfn_range(vma,from,pfn,size,prot) +#else +extern int arch_io_remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t prot); +#define io_remap_pfn_range(vma,from,pfn,size,prot) \ + arch_io_remap_pfn_range(vma,from,pfn,size,prot) +#endif + #define pgtable_cache_init() do { } while (0) diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index bd6f9233d1359..6867ed303ad28 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -1,4 +1,5 @@ obj-y += io.o idle.o timer.o +obj-y += memory.o ifndef CONFIG_ARCH_MSM8X60 obj-y += acpuclock-arm11.o obj-y += dma.o diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h index 070e17d237f19..acb045b700794 100644 --- a/arch/arm/mach-msm/include/mach/memory.h +++ b/arch/arm/mach-msm/include/mach/memory.h @@ -29,5 +29,7 @@ #define PHYS_OFFSET UL(0x10000000) #endif +#define HAS_ARCH_IO_REMAP_PFN_RANGE + #endif diff --git a/arch/arm/mach-msm/memory.c b/arch/arm/mach-msm/memory.c new file mode 100644 index 0000000000000..3be79d8f9de78 --- /dev/null +++ b/arch/arm/mach-msm/memory.c @@ -0,0 +1,29 @@ +/* arch/arm/mach-msm/memory.c + * + * Copyright (C) 2007 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 + +int arch_io_remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, + unsigned long pfn, unsigned long size, pgprot_t prot) +{ + unsigned long pfn_addr = pfn << PAGE_SHIFT; + if ((pfn_addr >= 0x88000000) && (pfn_addr < 0xD0000000)) { + prot = pgprot_device(prot); + printk("remapping device %lx\n", prot); + } + return remap_pfn_range(vma, addr, pfn, size, prot); +} diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 238d5fa66a36b..103a68cf590b1 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -329,7 +329,7 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma) vma->vm_ops = &mmap_mem_ops; /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */ - if (remap_pfn_range(vma, + if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, size, From e4bb253ade0307c45a629fd6a323abd55cd2c8d5 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Mon, 30 Jun 2008 17:22:43 -0700 Subject: [PATCH 0210/2556] [ARM] msm: msm_serial: add uart clock control interface This interface allows tty clients to request uart clock on/off to reduce power usage. Signed-off-by: Brian Swetland --- drivers/tty/serial/Kconfig | 8 ++ drivers/tty/serial/msm_serial.c | 163 +++++++++++++++++++++++++++++++- 2 files changed, 170 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 2b8334601c8b5..4e581cca5ffce 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1329,6 +1329,14 @@ config SERIAL_VT8500_CONSOLE depends on SERIAL_VT8500=y select SERIAL_CORE_CONSOLE +config SERIAL_MSM_CLOCK_CONTROL + bool "Allow tty clients to make clock requests to msm uarts." + depends on SERIAL_MSM=y + default y + help + Provides an interface for tty clients to request the msm uart clock + to be turned on or off for power savings. + config SERIAL_NETX tristate "NetX serial port support" depends on ARM && ARCH_NETX diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 8e43a7b69e646..2995736c52280 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -34,44 +34,146 @@ #include "msm_serial.h" +#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL +enum msm_clk_states_e { + MSM_CLK_PORT_OFF, /* uart port not in use */ + MSM_CLK_OFF, /* clock enabled */ + MSM_CLK_REQUEST_OFF, /* disable after TX flushed */ + MSM_CLK_ON, /* clock disabled */ +}; +#endif + struct msm_port { struct uart_port uart; char name[16]; struct clk *clk; unsigned int imr; +#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL + enum msm_clk_states_e clk_state; + struct hrtimer clk_off_timer; + ktime_t clk_off_delay; +#endif }; static void msm_stop_tx(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); + clk_enable(msm_port->clk); + msm_port->imr &= ~UART_IMR_TXLEV; msm_write(port, msm_port->imr, UART_IMR); + + clk_disable(msm_port->clk); } static void msm_start_tx(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); + clk_enable(msm_port->clk); + msm_port->imr |= UART_IMR_TXLEV; msm_write(port, msm_port->imr, UART_IMR); + + clk_disable(msm_port->clk); } static void msm_stop_rx(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); + clk_enable(msm_port->clk); + msm_port->imr &= ~(UART_IMR_RXLEV | UART_IMR_RXSTALE); msm_write(port, msm_port->imr, UART_IMR); + + clk_disable(msm_port->clk); } static void msm_enable_ms(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); + clk_enable(msm_port->clk); + msm_port->imr |= UART_IMR_DELTA_CTS; msm_write(port, msm_port->imr, UART_IMR); + + clk_disable(msm_port->clk); +} + +#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL +/* turn clock off if TX buffer is empty, otherwise reschedule */ +static enum hrtimer_restart msm_serial_clock_off(struct hrtimer *timer) { + struct msm_port *msm_port = container_of(timer, struct msm_port, + clk_off_timer); + struct uart_port *port = &msm_port->uart; + struct circ_buf *xmit = &port->info->xmit; + unsigned long flags; + int ret = HRTIMER_NORESTART; + + spin_lock_irqsave(&port->lock, flags); + + if (msm_port->clk_state == MSM_CLK_REQUEST_OFF) { + if (uart_circ_empty(xmit)) { + struct msm_port *msm_port = UART_TO_MSM(port); + clk_disable(msm_port->clk); + msm_port->clk_state = MSM_CLK_OFF; + } else { + hrtimer_forward_now(timer, msm_port->clk_off_delay); + ret = HRTIMER_RESTART; + } + } + + spin_unlock_irqrestore(&port->lock, flags); + + return HRTIMER_NORESTART; +} + +/* request to turn off uart clock once pending TX is flushed */ +void msm_serial_clock_request_off(struct uart_port *port) { + unsigned long flags; + struct msm_port *msm_port = UART_TO_MSM(port); + + spin_lock_irqsave(&port->lock, flags); + if (msm_port->clk_state == MSM_CLK_ON) { + msm_port->clk_state = MSM_CLK_REQUEST_OFF; + /* turn off TX later. unfortunately not all msm uart's have a + * TXDONE available, and TXLEV does not wait until completely + * flushed, so a timer this is our only option + */ + hrtimer_start(&msm_port->clk_off_timer, + msm_port->clk_off_delay, HRTIMER_MODE_REL); + } + spin_unlock_irqrestore(&port->lock, flags); +} + +/* request to immediately turn on uart clock. + * ignored if there is a pending off request, unless force = 1. + */ +void msm_serial_clock_on(struct uart_port *port, int force) { + unsigned long flags; + struct msm_port *msm_port = UART_TO_MSM(port); + + spin_lock_irqsave(&port->lock, flags); + + switch (msm_port->clk_state) { + case MSM_CLK_OFF: + clk_enable(msm_port->clk); + force = 1; + case MSM_CLK_REQUEST_OFF: + if (force) { + msm_port->clk_state = MSM_CLK_ON; + } + break; + case MSM_CLK_ON: break; + case MSM_CLK_PORT_OFF: break; + } + + spin_unlock_irqrestore(&port->lock, flags); } +#endif static void handle_rx(struct uart_port *port) { @@ -148,6 +250,14 @@ static void handle_tx(struct uart_port *port) sent_tx = 1; } +#ifdef MSM_SERIAL_CONTROL_CLOCK + if (sent_tx && msm_port->clk_state == MSM_CLK_REQUEST_OFF) + /* new TX - restart the timer */ + if (hrtimer_try_to_cancel(msm_port->clk_off_timer) == 1) + hrtimer_start(msm_port->clk_timer, + msm_port->clk_off_delay); +#endif + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); } @@ -166,6 +276,7 @@ static irqreturn_t msm_irq(int irq, void *dev_id) unsigned int misr; spin_lock(&port->lock); + clk_enable(msm_port->clk); misr = msm_read(port, UART_MISR); msm_write(port, 0, UART_IMR); /* disable interrupt */ @@ -177,6 +288,7 @@ static irqreturn_t msm_irq(int irq, void *dev_id) handle_delta_cts(port); msm_write(port, msm_port->imr, UART_IMR); /* restore interrupt */ + clk_disable(msm_port->clk); spin_unlock(&port->lock); return IRQ_HANDLED; @@ -184,7 +296,14 @@ static irqreturn_t msm_irq(int irq, void *dev_id) static unsigned int msm_tx_empty(struct uart_port *port) { - return (msm_read(port, UART_SR) & UART_SR_TX_EMPTY) ? TIOCSER_TEMT : 0; + unsigned int ret; + struct msm_port *msm_port = UART_TO_MSM(port); + + clk_enable(msm_port->clk); + ret = (msm_read(port, UART_SR) & UART_SR_TX_EMPTY) ? TIOCSER_TEMT : 0; + clk_disable(msm_port->clk); + + return ret; } static unsigned int msm_get_mctrl(struct uart_port *port) @@ -195,6 +314,9 @@ static unsigned int msm_get_mctrl(struct uart_port *port) static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl) { unsigned int mr; + struct msm_port *msm_port = UART_TO_MSM(port); + + clk_enable(msm_port->clk); mr = msm_read(port, UART_MR1); @@ -206,14 +328,22 @@ static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl) mr |= UART_MR1_RX_RDY_CTL; msm_write(port, mr, UART_MR1); } + + clk_disable(msm_port->clk); } static void msm_break_ctl(struct uart_port *port, int break_ctl) { + struct msm_port *msm_port = UART_TO_MSM(port); + + clk_enable(msm_port->clk); + if (break_ctl) msm_write(port, UART_CR_CMD_START_BREAK, UART_CR); else msm_write(port, UART_CR_CMD_STOP_BREAK, UART_CR); + + clk_disable(msm_port->clk); } static int msm_set_baud_rate(struct uart_port *port, unsigned int baud) @@ -307,6 +437,10 @@ static void msm_init_clock(struct uart_port *port) struct msm_port *msm_port = UART_TO_MSM(port); clk_enable(msm_port->clk); +#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL + msm_port->clk_state = MSM_CLK_ON; +#endif + msm_serial_set_mnd_regs(port); } @@ -363,12 +497,22 @@ static void msm_shutdown(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); + clk_enable(msm_port->clk); + msm_port->imr = 0; msm_write(port, 0, UART_IMR); /* disable interrupts */ clk_disable(msm_port->clk); free_irq(port->irq, port); + +#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL + if (msm_port->clk_state != MSM_CLK_OFF) + clk_disable(msm_port->clk); + msm_port->clk_state = MSM_CLK_PORT_OFF; +#else + clk_disable(msm_port->clk); +#endif } static void msm_set_termios(struct uart_port *port, struct ktermios *termios, @@ -376,8 +520,10 @@ static void msm_set_termios(struct uart_port *port, struct ktermios *termios, { unsigned long flags; unsigned int baud, mr; + struct msm_port *msm_port = UART_TO_MSM(port); spin_lock_irqsave(&port->lock, flags); + clk_enable(msm_port->clk); /* calculate and set baud rate */ baud = uart_get_baud_rate(port, termios, old, 300, 115200); @@ -443,6 +589,7 @@ static void msm_set_termios(struct uart_port *port, struct ktermios *termios, uart_update_timeout(port, termios->c_cflag, baud); + clk_disable(msm_port->clk); spin_unlock_irqrestore(&port->lock, flags); } @@ -510,6 +657,7 @@ static int msm_verify_port(struct uart_port *port, struct serial_struct *ser) static void msm_power(struct uart_port *port, unsigned int state, unsigned int oldstate) { +#ifndef CONFIG_SERIAL_MSM_CLOCK_CONTROL struct msm_port *msm_port = UART_TO_MSM(port); switch (state) { @@ -522,6 +670,7 @@ static void msm_power(struct uart_port *port, unsigned int state, default: printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state); } +#endif } static struct uart_ops msm_uart_pops = { @@ -602,7 +751,9 @@ static void msm_console_write(struct console *co, const char *s, msm_port = UART_TO_MSM(port); spin_lock(&port->lock); + clk_enable(msm_port->clk); uart_console_write(port, s, count, msm_console_putchar); + clk_disable(msm_port->clk); spin_unlock(&port->lock); } @@ -704,6 +855,16 @@ static int __init msm_serial_probe(struct platform_device *pdev) platform_set_drvdata(pdev, port); + if (unlikely(set_irq_wake(port->irq, 1))) + return -ENXIO; + +#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL + msm_port->clk_state = MSM_CLK_PORT_OFF; + hrtimer_init(&msm_port->clk_off_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + msm_port->clk_off_timer.function = msm_serial_clock_off; + msm_port->clk_off_delay = ktime_set(0, 1000000); /* 1 ms */ +#endif + return uart_add_one_port(&msm_uart_driver, port); } From 3f749292a59a07820ec6ef6802a0e03a00d74929 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Mon, 30 Jun 2008 17:26:45 -0700 Subject: [PATCH 0211/2556] [ARM] msm: msm_serial: option to wakeup uart on GPIO RX traffic. Signed-off-by: Brian Swetland msm_serial: fix to build on 2.6.32 Change-Id: Ib97cd6d7e127573ebf4eae7f2a4a215e03dcb74f --- drivers/tty/serial/Kconfig | 8 +++++ drivers/tty/serial/msm_serial.c | 63 +++++++++++++++++++++++++++++---- 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 4e581cca5ffce..4cf30347fd8d0 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1337,6 +1337,14 @@ config SERIAL_MSM_CLOCK_CONTROL Provides an interface for tty clients to request the msm uart clock to be turned on or off for power savings. +config SERIAL_MSM_RX_WAKEUP + bool "Wakeup the msm uart clock on GPIO activity." + depends on SERIAL_MSM_CLOCK_CONTROL + default n + help + Requires SERIAL_MSM_CLOCK_CONTROL. Wake up the uart while the uart + clock is off, using a wakeup GPIO. + config SERIAL_NETX tristate "NetX serial port support" depends on ARM && ARCH_NETX diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 2995736c52280..254939e1fe583 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -109,7 +109,7 @@ static enum hrtimer_restart msm_serial_clock_off(struct hrtimer *timer) { struct msm_port *msm_port = container_of(timer, struct msm_port, clk_off_timer); struct uart_port *port = &msm_port->uart; - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; unsigned long flags; int ret = HRTIMER_NORESTART; @@ -141,7 +141,7 @@ void msm_serial_clock_request_off(struct uart_port *port) { msm_port->clk_state = MSM_CLK_REQUEST_OFF; /* turn off TX later. unfortunately not all msm uart's have a * TXDONE available, and TXLEV does not wait until completely - * flushed, so a timer this is our only option + * flushed, so a timer is our only option */ hrtimer_start(&msm_port->clk_off_timer, msm_port->clk_off_delay, HRTIMER_MODE_REL); @@ -164,6 +164,7 @@ void msm_serial_clock_on(struct uart_port *port, int force) { force = 1; case MSM_CLK_REQUEST_OFF: if (force) { + hrtimer_try_to_cancel(&msm_port->clk_off_timer); msm_port->clk_state = MSM_CLK_ON; } break; @@ -175,6 +176,34 @@ void msm_serial_clock_on(struct uart_port *port, int force) { } #endif +#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP +#define WAKE_UP_IND 0x32 +static irqreturn_t msm_rx_irq(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct msm_port *msm_port = UART_TO_MSM(port); + int inject_wakeup = 0; + + spin_lock(&port->lock); + + if (msm_port->clk_state == MSM_CLK_OFF) + inject_wakeup = 1; + + msm_serial_clock_on(port, 0); + + /* we missed an rx while asleep - it must be a wakeup indicator + */ + if (inject_wakeup) { + struct tty_struct *tty = port->state->port.tty; + tty_insert_flip_char(tty, WAKE_UP_IND, TTY_NORMAL); + tty_flip_buffer_push(tty); + } + + spin_unlock(&port->lock); + return IRQ_HANDLED; +} +#endif + static void handle_rx(struct uart_port *port) { struct tty_struct *tty = port->state->port.tty; @@ -250,12 +279,12 @@ static void handle_tx(struct uart_port *port) sent_tx = 1; } -#ifdef MSM_SERIAL_CONTROL_CLOCK +#ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL if (sent_tx && msm_port->clk_state == MSM_CLK_REQUEST_OFF) /* new TX - restart the timer */ - if (hrtimer_try_to_cancel(msm_port->clk_off_timer) == 1) - hrtimer_start(msm_port->clk_timer, - msm_port->clk_off_delay); + if (hrtimer_try_to_cancel(&msm_port->clk_off_timer) == 1) + hrtimer_start(&msm_port->clk_off_timer, + msm_port->clk_off_delay, HRTIMER_MODE_REL); #endif if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -490,6 +519,17 @@ static int msm_startup(struct uart_port *port) UART_IMR_CURRENT_CTS; msm_write(port, msm_port->imr, UART_IMR); +#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP + /* Apply the RX GPIO wake irq workaround to the bluetooth uart */ + if (port->line == 0) { /* BT is serial device 0 */ + ret = request_irq(MSM_GPIO_TO_INT(45), msm_rx_irq, + IRQF_TRIGGER_FALLING, "msm_serial0_rx", + port); + if (unlikely(ret)) + return ret; + } +#endif + return 0; } @@ -506,6 +546,11 @@ static void msm_shutdown(struct uart_port *port) free_irq(port->irq, port); +#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP + if (port->line == 0) + free_irq(MSM_GPIO_TO_INT(45), port); +#endif + #ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL if (msm_port->clk_state != MSM_CLK_OFF) clk_disable(msm_port->clk); @@ -858,6 +903,12 @@ static int __init msm_serial_probe(struct platform_device *pdev) if (unlikely(set_irq_wake(port->irq, 1))) return -ENXIO; +#ifdef CONFIG_SERIAL_MSM_RX_WAKEUP + if (port->line == 0) /* BT is serial device 0 */ + if (unlikely(set_irq_wake(MSM_GPIO_TO_INT(45), 1))) + return -ENXIO; +#endif + #ifdef CONFIG_SERIAL_MSM_CLOCK_CONTROL msm_port->clk_state = MSM_CLK_PORT_OFF; hrtimer_init(&msm_port->clk_off_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); From 006c4234dda9888cd581efb551ddf9dbd0055b8f Mon Sep 17 00:00:00 2001 From: "Mike A. Chan" Date: Fri, 7 Nov 2008 12:39:29 -0800 Subject: [PATCH 0212/2556] [ARM] msm: clock: Adding ondemand cpufreq scaling. Signed-off-by: Mike Chan --- arch/arm/Kconfig | 1 + arch/arm/mach-msm/Kconfig | 39 +++++++++++++++++---- arch/arm/mach-msm/Makefile | 2 +- arch/arm/mach-msm/cpufreq.c | 68 +++++++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 7 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index bacb0cdbd4ead..cb0429c9dd250 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -616,6 +616,7 @@ config ARCH_PXA config ARCH_MSM bool "Qualcomm MSM" + select ARCH_HAS_CPUFREQ select ARCH_REQUIRE_GPIOLIB select HAVE_CLK select GENERIC_GPIO diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 2de8ff5880ed9..cffdcefb7768e 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -389,12 +389,26 @@ config MSM_RPCSERVERS help none -menuconfig MSM_CPU_FREQ_SCREEN - depends on HAS_EARLYSUSPEND - default n - bool "Enable simple cpu frequency scaling" - help - Simple cpufreq scaling based on screen ON/OFF. +config MSM_CPU_FREQ + bool + default y if MSM_CPU_FREQ_ONDEMAND || MSM_CPU_FREQ_SCREEN + +choice + prompt "Cpufreq mode" + default MSM_CPU_FREQ_ONDEMAND + default MSM_CPU_FREQ_SCREEN + + config MSM_CPU_FREQ_ONDEMAND + depends on CPU_FREQ_DEFAULT_GOV_ONDEMAND + bool "Enable ONDEMAND cpufreq govoner for" + + config MSM_CPU_FREQ_SCREEN + depends on HAS_EARLYSUSPEND + bool "Enable simple cpu frequency scaling" + help + Simple cpufreq scaling based on screen ON/OFF. + +endchoice if MSM_CPU_FREQ_SCREEN @@ -408,6 +422,19 @@ config MSM_CPU_FREQ_SCREEN_ON endif # MSM_CPU_FREQ_SCREEN + +if MSM_CPU_FREQ_ONDEMAND + +config MSM_CPU_FREQ_ONDEMAND_MAX + int "Max" + default 384000 + +config MSM_CPU_FREQ_ONDEMAND_MIN + int "Min" + default 245760 + +endif # MSM_CPU_FREQ_ONDEMAND + config MSM_HW3D tristate "MSM Hardware 3D Register Driver" default y diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 6867ed303ad28..35facc065aae2 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -36,7 +36,7 @@ obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_time_remote.o obj-$(CONFIG_MSM_ADSP) += qdsp5/ obj-$(CONFIG_MSM_HW3D) += hw3d.o obj-$(CONFIG_PM) += pm.o -obj-$(CONFIG_MSM_CPU_FREQ_SCREEN) += cpufreq.o +obj-$(CONFIG_MSM_CPU_FREQ) += cpufreq.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c index efa46a0c9fa5b..821d2be89fd38 100644 --- a/arch/arm/mach-msm/cpufreq.c +++ b/arch/arm/mach-msm/cpufreq.c @@ -21,6 +21,11 @@ #include #include "acpuclock.h" +#ifdef CONFIG_MSM_CPU_FREQ_ONDEMAND +#include +#endif + +#ifdef CONFIG_MSM_CPU_FREQ_SCREEN static void msm_early_suspend(struct early_suspend *handler) { acpuclk_set_rate(CONFIG_MSM_CPU_FREQ_SCREEN_OFF * 1000, 0); } @@ -41,3 +46,66 @@ static int __init clock_late_init(void) } late_initcall(clock_late_init); +#elif defined(CONFIG_MSM_CPU_FREQ_ONDEMAND) + +static int msm_cpufreq_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int freq; + switch(relation) { + /* Lowest value at or above target frequency. */ + case CPUFREQ_RELATION_L: + /* Highest value at or below target frequency. */ + case CPUFREQ_RELATION_H: + if (target_freq < CONFIG_MSM_CPU_FREQ_ONDEMAND_MAX) + freq = CONFIG_MSM_CPU_FREQ_ONDEMAND_MIN; + else + freq = CONFIG_MSM_CPU_FREQ_ONDEMAND_MAX; + break; + default: + return -EINVAL; + } +#ifdef CONFIG_CPU_FREQ_DEBUG + printk("msm_cpufreq_target %d r %d (%d-%d) selected %d\n", target_freq, + relation, policy->min, policy->max, freq); +#endif + acpuclk_set_rate(freq * 1000, 0); + return 0; +} + +static int msm_cpufreq_verify(struct cpufreq_policy *policy) +{ + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); +return 0; +} + +static int __init msm_cpufreq_init(struct cpufreq_policy *policy) +{ + policy->cur = acpuclk_get_rate(); + policy->min = CONFIG_MSM_CPU_FREQ_ONDEMAND_MIN; + policy->max = CONFIG_MSM_CPU_FREQ_ONDEMAND_MAX; + policy->cpuinfo.min_freq = CONFIG_MSM_CPU_FREQ_ONDEMAND_MIN; + policy->cpuinfo.max_freq = CONFIG_MSM_CPU_FREQ_ONDEMAND_MAX; + policy->cpuinfo.transition_latency = + acpuclk_get_switch_time() * NSEC_PER_USEC; + return 0; +} + +static struct cpufreq_driver msm_cpufreq_driver = { + /* lps calculstaions are handled here. */ + .flags = CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS, + .init = msm_cpufreq_init, + .verify = msm_cpufreq_verify, + .target = msm_cpufreq_target, + .name = "msm", +}; + +static int __init msm_cpufreq_register(void) +{ + return cpufreq_register_driver(&msm_cpufreq_driver); +} + +device_initcall(msm_cpufreq_register); +#endif From b48423acccb575664d9fb27d1fd17ce31d3acf5f Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 10 Nov 2008 17:57:27 -0800 Subject: [PATCH 0213/2556] [ARM] trout_mmc: Initial MMC support for Trout on 2.6.27 Signed-off-by: San Mehat trout: mmc: Re-enable WIFI SDIO code Signed-off-by: San Mehat trout: mmc: Re-enable wifi debugfs code Signed-off-by: San Mehat trout: mmc: Add maxblksize specifier to embedded data for wifi Signed-off-by: San Mehat --- arch/arm/mach-msm/board-trout-mmc.c | 247 +++++++++++++++++++++++++++- arch/arm/mach-msm/board-trout.c | 8 + 2 files changed, 252 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/board-trout-mmc.c b/arch/arm/mach-msm/board-trout-mmc.c index 44be8464657b3..c45f9df944005 100644 --- a/arch/arm/mach-msm/board-trout-mmc.c +++ b/arch/arm/mach-msm/board-trout-mmc.c @@ -26,6 +26,8 @@ #define DEBUG_SDSLOT_VDD 1 +extern int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat); + /* ---- COMMON ---- */ static void config_gpio_table(uint32_t *table, int len) { @@ -160,27 +162,266 @@ static unsigned int trout_sdslot_status(struct device *dev) | MMC_VDD_25_26 | MMC_VDD_26_27 | MMC_VDD_27_28 \ | MMC_VDD_28_29 | MMC_VDD_29_30 -static struct msm_mmc_platform_data trout_sdslot_data = { +static struct mmc_platform_data trout_sdslot_data = { .ocr_mask = TROUT_MMC_VDD, + .status_irq = TROUT_GPIO_TO_INT(TROUT_GPIO_SDMC_CD_N), .status = trout_sdslot_status, .translate_vdd = trout_sdslot_switchvdd, }; +/* ---- WIFI ---- */ + +static uint32_t wifi_on_gpio_table[] = { + PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(29, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +static uint32_t wifi_off_gpio_table[] = { + PCOM_GPIO_CFG(51, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(56, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(29, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +static struct vreg *vreg_wifi_osc; /* WIFI 32khz oscilator */ +static int trout_wifi_cd = 0; /* WIFI virtual 'card detect' status */ + +static struct sdio_embedded_func wifi_func = { + .f_class = SDIO_CLASS_WLAN, + .f_maxblksize = 512, +}; + +static struct embedded_sdio_data trout_wifi_emb_data = { + .cis = { + .vendor = 0x104c, + .device = 0x9066, + .blksize = 512, + /*.max_dtr = 24000000, Max of chip - no worky on Trout */ + .max_dtr = 20000000, + }, + .cccr = { + .multi_block = 0, + .low_speed = 0, + .wide_bus = 1, + .high_power = 0, + .high_speed = 0, + }, + .funcs = &wifi_func, + .num_funcs = 1, +}; + +static void (*wifi_status_cb)(int card_present, void *dev_id); +static void *wifi_status_cb_devid; + +static int trout_wifi_status_register(void (*callback)(int card_present, void *dev_id), void *dev_id) +{ + if (wifi_status_cb) + return -EAGAIN; + wifi_status_cb = callback; + wifi_status_cb_devid = dev_id; + return 0; +} + +static unsigned int trout_wifi_status(struct device *dev) +{ + return trout_wifi_cd; +} + +static struct mmc_platform_data trout_wifi_data = { + .ocr_mask = MMC_VDD_28_29, + .status = trout_wifi_status, + .register_status_notify = trout_wifi_status_register, + .embedded_sdio = &trout_wifi_emb_data, +}; + +void trout_wifi_set_carddetect(int val) +{ + printk("%s: %d\n", __func__, val); + trout_wifi_cd = val; + if (wifi_status_cb) { + wifi_status_cb(val, wifi_status_cb_devid); + } else + printk(KERN_WARNING "%s: Nobody to notify\n", __func__); +} +EXPORT_SYMBOL(trout_wifi_set_carddetect); + +static int trout_wifi_power_state; + +int trout_wifi_power(int on) +{ + int rc; + + printk("%s: %d\n", __func__, on); + + if (on) { + config_gpio_table(wifi_on_gpio_table, + ARRAY_SIZE(wifi_on_gpio_table)); + rc = vreg_enable(vreg_wifi_osc); + if (rc) + return rc; + } else { + config_gpio_table(wifi_off_gpio_table, + ARRAY_SIZE(wifi_off_gpio_table)); + } + gpio_set_value( TROUT_GPIO_MAC_32K_EN, on); + mdelay(100); + gpio_set_value( TROUT_GPIO_WIFI_EN, on); + mdelay(100); + if (!on) { + vreg_disable(vreg_wifi_osc); + } + trout_wifi_power_state = on; + return 0; +} +EXPORT_SYMBOL(trout_wifi_power); + +static int trout_wifi_reset_state; +void trout_wifi_reset(int on) +{ + printk("%s: %d\n", __func__, on); + gpio_set_value( TROUT_GPIO_WIFI_PA_RESETX, !on ); + trout_wifi_reset_state = on; + mdelay(50); +} +EXPORT_SYMBOL(trout_wifi_reset); + int __init trout_init_mmc(unsigned int sys_rev) { + wifi_status_cb = NULL; + sdslot_vreg_enabled = 0; vreg_sdslot = vreg_get(0, "gp6"); if (IS_ERR(vreg_sdslot)) return PTR_ERR(vreg_sdslot); + vreg_wifi_osc = vreg_get(0, "mmc"); + if (IS_ERR(vreg_wifi_osc)) + return PTR_ERR(vreg_wifi_osc); + set_irq_wake(TROUT_GPIO_TO_INT(TROUT_GPIO_SDMC_CD_N), 1); + msm_add_sdcc(1, &trout_wifi_data); + if (!opt_disable_sdcard) - msm_add_sdcc(2, &trout_sdslot_data, - TROUT_GPIO_TO_INT(TROUT_GPIO_SDMC_CD_N), 0); + msm_add_sdcc(2, &trout_sdslot_data); else printk(KERN_INFO "trout: SD-Card interface disabled\n"); return 0; } +#if defined(CONFIG_DEBUG_FS) +static int troutmmc_dbg_wifi_reset_set(void *data, u64 val) +{ + trout_wifi_reset((int) val); + return 0; +} + +static int troutmmc_dbg_wifi_reset_get(void *data, u64 *val) +{ + *val = trout_wifi_reset_state; + return 0; +} + +static int troutmmc_dbg_wifi_cd_set(void *data, u64 val) +{ + trout_wifi_set_carddetect((int) val); + return 0; +} + +static int troutmmc_dbg_wifi_cd_get(void *data, u64 *val) +{ + *val = trout_wifi_cd; + return 0; +} + +static int troutmmc_dbg_wifi_pwr_set(void *data, u64 val) +{ + trout_wifi_power((int) val); + return 0; +} + +static int troutmmc_dbg_wifi_pwr_get(void *data, u64 *val) +{ + + *val = trout_wifi_power_state; + return 0; +} + +static int troutmmc_dbg_sd_pwr_set(void *data, u64 val) +{ + trout_sdslot_switchvdd(NULL, (unsigned int) val); + return 0; +} + +static int troutmmc_dbg_sd_pwr_get(void *data, u64 *val) +{ + *val = sdslot_vdd; + return 0; +} + +static int troutmmc_dbg_sd_cd_set(void *data, u64 val) +{ + return -ENOSYS; +} + +static int troutmmc_dbg_sd_cd_get(void *data, u64 *val) +{ + *val = trout_sdslot_status(NULL); + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(troutmmc_dbg_wifi_reset_fops, + troutmmc_dbg_wifi_reset_get, + troutmmc_dbg_wifi_reset_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(troutmmc_dbg_wifi_cd_fops, + troutmmc_dbg_wifi_cd_get, + troutmmc_dbg_wifi_cd_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(troutmmc_dbg_wifi_pwr_fops, + troutmmc_dbg_wifi_pwr_get, + troutmmc_dbg_wifi_pwr_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(troutmmc_dbg_sd_pwr_fops, + troutmmc_dbg_sd_pwr_get, + troutmmc_dbg_sd_pwr_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(troutmmc_dbg_sd_cd_fops, + troutmmc_dbg_sd_cd_get, + troutmmc_dbg_sd_cd_set, "%llu\n"); + +static int __init troutmmc_dbg_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("troutmmc_dbg", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("wifi_reset", 0644, dent, NULL, + &troutmmc_dbg_wifi_reset_fops); + debugfs_create_file("wifi_cd", 0644, dent, NULL, + &troutmmc_dbg_wifi_cd_fops); + debugfs_create_file("wifi_pwr", 0644, dent, NULL, + &troutmmc_dbg_wifi_pwr_fops); + + debugfs_create_file("sd_pwr", 0644, dent, NULL, + &troutmmc_dbg_sd_pwr_fops); + debugfs_create_file("sd_cd", 0644, dent, NULL, + &troutmmc_dbg_sd_cd_fops); + + return 0; +} + +device_initcall(troutmmc_dbg_init); + +#endif diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 38ddbee8ce298..9b2bf05780785 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -69,6 +69,8 @@ void msm_init_irq(void); void msm_init_gpio(void); +extern int trout_init_mmc(unsigned int); + struct trout_axis_info { struct gpio_event_axis_info info; uint16_t in_state; @@ -661,6 +663,8 @@ static struct msm_acpu_clock_platform_data trout_clock_data = { static void __init trout_init(void) { + int rc; + printk("trout_init() revision=%d\n", system_rev); /* @@ -708,6 +712,10 @@ static void __init trout_init(void) msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; + rc = trout_init_mmc(system_rev); + if (rc) + printk(KERN_CRIT "%s: MMC init failure (%d)\n", __func__, rc); + platform_add_devices(devices, ARRAY_SIZE(devices)); i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); From 67b925de6ae842cd237aff487d91d16a9cc4fe29 Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Date: Wed, 1 Oct 2008 10:43:10 -0700 Subject: [PATCH 0214/2556] [ARM] msm: trout: Adds trout Power Sink Driver Signed-off-by: Rebecca Schultz --- arch/arm/mach-msm/Kconfig | 5 + arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/board-trout-gpio.c | 15 ++ arch/arm/mach-msm/board-trout-mmc.c | 3 + arch/arm/mach-msm/board-trout.c | 68 ++++++++ arch/arm/mach-msm/htc_pwrsink.c | 147 ++++++++++++++++++ .../arm/mach-msm/include/mach/trout_pwrsink.h | 58 +++++++ arch/arm/mach-msm/qdsp5/audio_out.c | 4 + 8 files changed, 301 insertions(+) create mode 100644 arch/arm/mach-msm/htc_pwrsink.c create mode 100644 arch/arm/mach-msm/include/mach/trout_pwrsink.h diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index cffdcefb7768e..f194b624a76ab 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -197,6 +197,11 @@ config TROUT_BATTCHG default y bool "Trout battery / charger driver" +config TROUT_PWRSINK + depends on MSM_SMD + default y + bool "Trout Power Sink Driver" + choice prompt "Default Timer" default MSM7X00A_USE_GP_TIMER diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 35facc065aae2..0563d276c4ddc 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_MACH_TROUT) += board-trout-keypad.o obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o obj-$(CONFIG_TROUT_H2W) += board-trout-h2w.o obj-$(CONFIG_TROUT_BATTCHG) += htc_battery.o +obj-$(CONFIG_TROUT_PWRSINK) += htc_pwrsink.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o diff --git a/arch/arm/mach-msm/board-trout-gpio.c b/arch/arm/mach-msm/board-trout-gpio.c index 6f421c0c95ff6..7e331b0974b76 100644 --- a/arch/arm/mach-msm/board-trout-gpio.c +++ b/arch/arm/mach-msm/board-trout-gpio.c @@ -23,6 +23,8 @@ #include #include +#include + #include "board-trout.h" static uint8_t trout_cpld_shadow[4] = { @@ -52,6 +54,18 @@ static int trout_gpio_get(struct gpio_chip *chip, unsigned offset) return !!(readb(TROUT_CPLD_BASE + reg) & b); } +static void update_pwrsink(unsigned gpio, unsigned on) +{ + switch(gpio) { + case TROUT_GPIO_UI_LED_EN: + trout_pwrsink_set(PWRSINK_LED_BUTTON, on ? 100 : 0); + break; + case TROUT_GPIO_QTKEY_LED_EN: + trout_pwrsink_set(PWRSINK_LED_KEYBOARD, on ? 100 : 0); + break; + } +} + static void trout_gpio_set(struct gpio_chip *chip, unsigned offset, int on) { unsigned n = chip->base + offset; @@ -66,6 +80,7 @@ static void trout_gpio_set(struct gpio_chip *chip, unsigned offset, int on) } local_irq_save(flags); + update_pwrsink(n, on); if(on) reg_val = trout_cpld_shadow[reg >> 1] |= b; else diff --git a/arch/arm/mach-msm/board-trout-mmc.c b/arch/arm/mach-msm/board-trout-mmc.c index c45f9df944005..41ae8721913e1 100644 --- a/arch/arm/mach-msm/board-trout-mmc.c +++ b/arch/arm/mach-msm/board-trout-mmc.c @@ -15,6 +15,7 @@ #include #include +#include #include @@ -267,9 +268,11 @@ int trout_wifi_power(int on) rc = vreg_enable(vreg_wifi_osc); if (rc) return rc; + trout_pwrsink_set(PWRSINK_WIFI, 70); } else { config_gpio_table(wifi_off_gpio_table, ARRAY_SIZE(wifi_off_gpio_table)); + trout_pwrsink_set(PWRSINK_WIFI, 0); } gpio_set_value( TROUT_GPIO_MAC_32K_EN, on); mdelay(100); diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 9b2bf05780785..ed9f0bd214605 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -63,6 +63,8 @@ #include #endif +#include + #include "proc_comm.h" #include "devices.h" @@ -493,6 +495,67 @@ static struct platform_device trout_camera = { }, }; +static struct pwr_sink trout_pwrsink_table[] = { + { + .id = PWRSINK_AUDIO, + .ua_max = 90000, + }, + { + .id = PWRSINK_BACKLIGHT, + .ua_max = 128000, + }, + { + .id = PWRSINK_LED_BUTTON, + .ua_max = 17000, + }, + { + .id = PWRSINK_LED_KEYBOARD, + .ua_max = 22000, + }, + { + .id = PWRSINK_GP_CLK, + .ua_max = 30000, + }, + { + .id = PWRSINK_BLUETOOTH, + .ua_max = 15000, + }, + { + .id = PWRSINK_CAMERA, + .ua_max = 0, + }, + { + .id = PWRSINK_SDCARD, + .ua_max = 0, + }, + { + .id = PWRSINK_VIDEO, + .ua_max = 0, + }, + { + .id = PWRSINK_WIFI, + .ua_max = 200000, + }, + { + .id = PWRSINK_SYSTEM_LOAD, + .ua_max = 63000, + .percent_util = 100, + }, +}; + +static struct pwr_sink_platform_data trout_pwrsink_data = { + .num_sinks = ARRAY_SIZE(trout_pwrsink_table), + .sinks = trout_pwrsink_table, +}; + +static struct platform_device trout_pwr_sink = { + .name = "trout_pwrsink", + .id = -1, + .dev = { + .platform_data = &trout_pwrsink_data, + }, +}; + static struct platform_device *devices[] __initdata = { &msm_device_smd, &msm_device_nand, @@ -516,6 +579,9 @@ static struct platform_device *devices[] __initdata = { &android_pmem_camera_device, &trout_ram_console_device, &trout_camera, +#if defined(CONFIG_TROUT_PWRSINK) + &trout_pwr_sink, +#endif }; extern struct sys_timer msm_timer; @@ -544,9 +610,11 @@ static void bluetooth_set_power(int on) gpio_set_value(TROUT_GPIO_BT_32K_EN, 1); udelay(10); gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_HIGH); + trout_pwrsink_set(PWRSINK_BLUETOOTH, 100); } else { gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_LOW); gpio_set_value(TROUT_GPIO_BT_32K_EN, 0); + trout_pwrsink_set(PWRSINK_BLUETOOTH, 0); } } diff --git a/arch/arm/mach-msm/htc_pwrsink.c b/arch/arm/mach-msm/htc_pwrsink.c new file mode 100644 index 0000000000000..6ce6fb4737736 --- /dev/null +++ b/arch/arm/mach-msm/htc_pwrsink.c @@ -0,0 +1,147 @@ +/* arch/arm/mach-msm/htc_pwrsink.c + * + * Copyright (C) 2008 HTC Corporation + * Copyright (C) 2008 Google, Inc. + * Author: San Mehat + * Kant Kang + * Eiven Peng + * + * 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 "smd_private.h" + +static int initialized = 0; +static struct pwr_sink *sink_array[PWRSINK_LAST + 1]; +static DEFINE_SPINLOCK(sink_lock); +static unsigned long total_sink; +static uint32_t *smem_total_sink; + +int trout_pwrsink_set(pwrsink_id_type id, unsigned percent_utilized) +{ + unsigned long flags; + + if (!smem_total_sink) + smem_total_sink = smem_alloc(SMEM_ID_VENDOR0, sizeof(uint32_t)); + + if (!initialized) + return -EAGAIN; + + if (id < 0 || id > PWRSINK_LAST) + return -EINVAL; + + spin_lock_irqsave(&sink_lock, flags); + + if (!sink_array[id]) { + spin_unlock_irqrestore(&sink_lock, flags); + return -ENOENT; + } + + if (sink_array[id]->percent_util == percent_utilized) { + spin_unlock_irqrestore(&sink_lock, flags); + return 0; + } + + total_sink -= (sink_array[id]->ua_max * + sink_array[id]->percent_util / 100); + sink_array[id]->percent_util = percent_utilized; + total_sink += (sink_array[id]->ua_max * + sink_array[id]->percent_util / 100); + + if (smem_total_sink) + *smem_total_sink = total_sink / 1000; + + pr_debug("trout_pwrsink: ID %d, Util %d%%, Total %lu uA %s\n", + id, percent_utilized, total_sink, + smem_total_sink ? "SET" : ""); + + spin_unlock_irqrestore(&sink_lock, flags); + + return 0; +} +EXPORT_SYMBOL(trout_pwrsink_set); + +void trout_pwrsink_suspend_early(struct early_suspend *h) +{ + trout_pwrsink_set(PWRSINK_SYSTEM_LOAD, 70); +} + +int trout_pwrsink_suspend_late(struct platform_device *pdev, pm_message_t state) +{ + trout_pwrsink_set(PWRSINK_SYSTEM_LOAD, 13); + return 0; +} + +int trout_pwrsink_resume_early(struct platform_device *pdev) +{ + trout_pwrsink_set(PWRSINK_SYSTEM_LOAD, 70); + return 0; +} + +void trout_pwrsink_resume_late(struct early_suspend *h) +{ + trout_pwrsink_set(PWRSINK_SYSTEM_LOAD, 100); +} + +struct early_suspend trout_pwrsink_early_suspend = { + .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, + .suspend = trout_pwrsink_suspend_early, + .resume = trout_pwrsink_resume_late, +}; + +static int __init trout_pwrsink_probe(struct platform_device *pdev) +{ + struct pwr_sink_platform_data *pdata = pdev->dev.platform_data; + int i; + + if (!pdata) + return -EINVAL; + + total_sink = 0; + for (i = 0; i < pdata->num_sinks; i++) { + sink_array[pdata->sinks[i].id] = &pdata->sinks[i]; + total_sink += (pdata->sinks[i].ua_max * pdata->sinks[i].percent_util / 100); + } + + initialized = 1; + + register_early_suspend(&trout_pwrsink_early_suspend); + + return 0; +} + +static struct platform_driver trout_pwrsink_driver = { + .probe = trout_pwrsink_probe, + .suspend_late = trout_pwrsink_suspend_late, + .resume_early = trout_pwrsink_resume_early, + .driver = { + .name = "trout_pwrsink", + .owner = THIS_MODULE, + }, +}; + +static int __init trout_pwrsink_init(void) +{ + initialized = 0; + memset(sink_array, 0, sizeof(sink_array)); + return platform_driver_register(&trout_pwrsink_driver); +} + +module_init(trout_pwrsink_init); + diff --git a/arch/arm/mach-msm/include/mach/trout_pwrsink.h b/arch/arm/mach-msm/include/mach/trout_pwrsink.h new file mode 100644 index 0000000000000..265b329ac0cf8 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/trout_pwrsink.h @@ -0,0 +1,58 @@ +/* include/asm/mach-msm/trout_pwrsink.h + * + * Copyright (C) 2007 Google, Inc. + * Copyright (C) 2008 HTC Corporation. + * + * 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 _ARCH_ARM_MACH_MSM_TROUT_PWRSINK_H_ +#define _ARCH_ARM_MACH_MSM_TROUT_PWRSINK_H_ + +typedef enum { + PWRSINK_SYSTEM_LOAD = 0, + PWRSINK_AUDIO, + PWRSINK_BACKLIGHT, + PWRSINK_LED_BUTTON, + PWRSINK_LED_KEYBOARD, + PWRSINK_GP_CLK, + PWRSINK_BLUETOOTH, + PWRSINK_CAMERA, + PWRSINK_SDCARD, + PWRSINK_VIDEO, + PWRSINK_WIFI, + + PWRSINK_LAST = PWRSINK_WIFI, + PWRSINK_INVALID +} pwrsink_id_type; + +struct pwr_sink { + pwrsink_id_type id; + unsigned ua_max; + unsigned percent_util; +}; + +struct pwr_sink_platform_data { + unsigned num_sinks; + struct pwr_sink *sinks; +}; + +#ifndef CONFIG_TROUT_PWRSINK +static inline int trout_pwrsink_set(pwrsink_id_type id, unsigned percent) +{ +printk("%s:STUB!\n", __func__); + return 0; +} +#else +extern int trout_pwrsink_set(pwrsink_id_type id, unsigned percent); +#endif + +#endif + diff --git a/arch/arm/mach-msm/qdsp5/audio_out.c b/arch/arm/mach-msm/qdsp5/audio_out.c index d4bc1052884f4..84dcaaa11daca 100644 --- a/arch/arm/mach-msm/qdsp5/audio_out.c +++ b/arch/arm/mach-msm/qdsp5/audio_out.c @@ -38,6 +38,8 @@ #include #include +#include + #include "evlog.h" #define LOG_AUDIO_EVENTS 1 @@ -258,6 +260,7 @@ static int audio_enable(struct audio *audio) } audio->enabled = 1; + trout_pwrsink_set(PWRSINK_AUDIO, 100); return 0; } @@ -686,6 +689,7 @@ static int audio_release(struct inode *inode, struct file *file) audio_flush(audio); audio->opened = 0; mutex_unlock(&audio->lock); + trout_pwrsink_set(PWRSINK_AUDIO, 0); return 0; } From 484905d7a0c72cc344a36b174b1639a5a5c355af Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Mon, 17 Nov 2008 19:16:13 -0800 Subject: [PATCH 0215/2556] [ARM] msm: trout: bluetooth: Use rfkill API to turn bluetooth chipset on/off. --- arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/board-trout-rfkill.c | 93 ++++++++++++++++++++++++++ arch/arm/mach-msm/board-trout.c | 37 ++-------- 3 files changed, 100 insertions(+), 31 deletions(-) create mode 100644 arch/arm/mach-msm/board-trout-rfkill.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 0563d276c4ddc..86df415416c3a 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o d obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout-keypad.o obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o +obj-$(CONFIG_MACH_TROUT) += board-trout-rfkill.o obj-$(CONFIG_TROUT_H2W) += board-trout-h2w.o obj-$(CONFIG_TROUT_BATTCHG) += htc_battery.o obj-$(CONFIG_TROUT_PWRSINK) += htc_pwrsink.o diff --git a/arch/arm/mach-msm/board-trout-rfkill.c b/arch/arm/mach-msm/board-trout-rfkill.c new file mode 100644 index 0000000000000..5212431dda82a --- /dev/null +++ b/arch/arm/mach-msm/board-trout-rfkill.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2008 Google, Inc. + * Author: Nick Pelly + * + * 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. + * + */ + +/* Control bluetooth power for trout platform */ + +#include +#include +#include +#include +#include +#include + +#include "board-trout.h" + +void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state); + +static struct rfkill *bt_rfk; +static const char bt_name[] = "brf6300"; + +static int bluetooth_set_power(void *data, enum rfkill_state state) +{ + switch (state) { + case RFKILL_STATE_UNBLOCKED: + gpio_set_value(TROUT_GPIO_BT_32K_EN, 1); + udelay(10); + gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_HIGH); + break; + case RFKILL_STATE_SOFT_BLOCKED: + gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_LOW); + gpio_set_value(TROUT_GPIO_BT_32K_EN, 0); + break; + default: + printk(KERN_ERR "bad bluetooth rfkill state %d\n", state); + } + return 0; +} + +static int __init trout_rfkill_probe(struct platform_device *pdev) +{ + int rc = 0; + + /* default to bluetooth off */ + rfkill_switch_all(RFKILL_TYPE_BLUETOOTH, RFKILL_STATE_SOFT_BLOCKED); + bluetooth_set_power(NULL, RFKILL_STATE_SOFT_BLOCKED); + + bt_rfk = rfkill_allocate(&pdev->dev, RFKILL_TYPE_BLUETOOTH); + if (!bt_rfk) + return -ENOMEM; + + bt_rfk->name = bt_name; + bt_rfk->state = RFKILL_STATE_SOFT_BLOCKED; + /* userspace cannot take exclusive control */ + bt_rfk->user_claim_unsupported = 1; + bt_rfk->user_claim = 0; + bt_rfk->data = NULL; // user data + bt_rfk->toggle_radio = bluetooth_set_power; + + rc = rfkill_register(bt_rfk); + + if (rc) + rfkill_free(bt_rfk); + return rc; +} + +static struct platform_driver trout_rfkill_driver = { + .probe = trout_rfkill_probe, + .driver = { + .name = "trout_rfkill", + .owner = THIS_MODULE, + }, +}; + +static int __init trout_rfkill_init(void) +{ + return platform_driver_register(&trout_rfkill_driver); +} + +module_init(trout_rfkill_init); +MODULE_DESCRIPTION("trout rfkill"); +MODULE_AUTHOR("Nick Pelly "); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index ed9f0bd214605..f1c3f0664672a 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -556,6 +556,11 @@ static struct platform_device trout_pwr_sink = { }, }; +static struct platform_device trout_rfkill = { + .name = "trout_rfkill", + .id = -1, +}; + static struct platform_device *devices[] __initdata = { &msm_device_smd, &msm_device_nand, @@ -579,6 +584,7 @@ static struct platform_device *devices[] __initdata = { &android_pmem_camera_device, &trout_ram_console_device, &trout_camera, + &trout_rfkill, #if defined(CONFIG_TROUT_PWRSINK) &trout_pwr_sink, #endif @@ -602,34 +608,6 @@ module_param_named(charger_en, cpld_charger_en, uint, 0); module_param_named(usb_h2w_sw, cpld_usb_h2w_sw, uint, 0); module_param_named(disable_uart3, opt_disable_uart3, uint, 0); -static int trout_bluetooth_power_on; - -static void bluetooth_set_power(int on) -{ - if (on) { - gpio_set_value(TROUT_GPIO_BT_32K_EN, 1); - udelay(10); - gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_HIGH); - trout_pwrsink_set(PWRSINK_BLUETOOTH, 100); - } else { - gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_LOW); - gpio_set_value(TROUT_GPIO_BT_32K_EN, 0); - trout_pwrsink_set(PWRSINK_BLUETOOTH, 0); - } -} - -static int bluetooth_set_power_on(const char *val, struct kernel_param *kp) -{ - int ret; - ret = param_set_bool(val, kp); - if (!ret) - bluetooth_set_power(trout_bluetooth_power_on); - return ret; -} - -module_param_call(bluetooth_power_on, bluetooth_set_power_on, param_get_bool, - &trout_bluetooth_power_on, S_IWUSR | S_IRUGO); - static int __init trout_serialno_setup(char *str) { #ifdef CONFIG_USB_FUNCTION @@ -766,9 +744,6 @@ static void __init trout_init(void) gpio_set_value(TROUT_GPIO_H2W_SEL1, 1); #endif - /* Init bluetooth clock and shutdown pin */ - bluetooth_set_power(trout_bluetooth_power_on); - /* put the AF VCM in powerdown mode to avoid noise */ gpio_set_value(TROUT_GPIO_VCM_PWDN, 1); mdelay(100); From 9fb1882d4682ba8db2c5e5ed99f4144c2058fa83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 18 Nov 2008 14:58:08 -0800 Subject: [PATCH 0216/2556] [ARM] msm: Add MSM_DEBUG_UART option. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/board-trout.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index f1c3f0664672a..d3abe2b8e3683 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -786,7 +786,6 @@ static void __init trout_fixup(struct machine_desc *desc, struct tag *tags, static void __init trout_map_io(void) { - printk("trout_init_map_io()\n"); msm_map_common_io(); iotable_init(trout_io_desc, ARRAY_SIZE(trout_io_desc)); msm_clock_init(msm_clocks_7x01a, msm_num_clocks_7x01a); @@ -794,11 +793,10 @@ static void __init trout_map_io(void) MACHINE_START(TROUT, "trout") /* Maintainer: Brian Swetland */ - -/* this is broken... can we just opt out of specifying something here? */ - .phys_io = 0x80000000, - .io_pg_offst = ((0x80000000) >> 18) & 0xfffc, - +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif .boot_params = 0x10000100, .fixup = trout_fixup, .map_io = trout_map_io, From 145d6f6c5102db06beaed1950c7f6c6245013939 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Fri, 14 Nov 2008 12:19:45 -0500 Subject: [PATCH 0217/2556] msm_serial_hs: HSUART driver from Qualcomm Change-Id: Ifa6ed55a47bd4a7b31f01d138487ca8031d2ff9a Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/devices.h | 3 + arch/arm/mach-msm/dma.c | 17 + arch/arm/mach-msm/include/mach/dma.h | 4 + drivers/tty/serial/Kconfig | 22 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/msm_serial_hs.c | 1532 ++++++++++++++++++++++ drivers/tty/serial/msm_serial_hs.h | 34 + drivers/tty/serial/msm_serial_hs_hwreg.h | 146 +++ 8 files changed, 1759 insertions(+) create mode 100644 drivers/tty/serial/msm_serial_hs.c create mode 100644 drivers/tty/serial/msm_serial_hs.h create mode 100644 drivers/tty/serial/msm_serial_hs_hwreg.h diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h index 87c70bfce2bde..b7206c80ab051 100644 --- a/arch/arm/mach-msm/devices.h +++ b/arch/arm/mach-msm/devices.h @@ -22,6 +22,9 @@ extern struct platform_device msm_device_uart1; extern struct platform_device msm_device_uart2; extern struct platform_device msm_device_uart3; +extern struct platform_device msm_device_uart_dm1; +extern struct platform_device msm_device_uart_dm2; + extern struct platform_device msm_device_sdc1; extern struct platform_device msm_device_sdc2; extern struct platform_device msm_device_sdc3; diff --git a/arch/arm/mach-msm/dma.c b/arch/arm/mach-msm/dma.c index 02cae5e2951c2..4b1f5293439ed 100644 --- a/arch/arm/mach-msm/dma.c +++ b/arch/arm/mach-msm/dma.c @@ -1,6 +1,8 @@ /* linux/arch/arm/mach-msm/dma.c * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008 QUALCOMM Incorporated. + * Copyright (c) 2008 QUALCOMM USA, INC. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -51,6 +53,7 @@ void msm_dmov_stop_cmd(unsigned id, struct msm_dmov_cmd *cmd, int graceful) { writel((graceful << 31), DMOV_FLUSH0(id)); } +EXPORT_SYMBOL(msm_dmov_stop_cmd); void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd) { @@ -89,6 +92,20 @@ void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd) } spin_unlock_irqrestore(&msm_dmov_lock, irq_flags); } +EXPORT_SYMBOL(msm_dmov_enqueue_cmd); + +void msm_dmov_flush(unsigned int id) +{ + unsigned long irq_flags; + spin_lock_irqsave(&msm_dmov_lock, irq_flags); + /* XXX not checking if flush cmd sent already */ + if (!list_empty(&active_commands[id])) { + PRINT_IO("msm_dmov_flush(%d), send flush cmd\n", id); + writel(DMOV_FLUSH_TYPE, DMOV_FLUSH0(id)); + } + spin_unlock_irqrestore(&msm_dmov_lock, irq_flags); +} +EXPORT_SYMBOL(msm_dmov_flush); struct msm_dmov_exec_cmdptr_cmd { struct msm_dmov_cmd dmov_cmd; diff --git a/arch/arm/mach-msm/include/mach/dma.h b/arch/arm/mach-msm/include/mach/dma.h index 0902682ad636d..87fb28ff64819 100644 --- a/arch/arm/mach-msm/include/mach/dma.h +++ b/arch/arm/mach-msm/include/mach/dma.h @@ -1,6 +1,8 @@ /* linux/include/asm-arm/arch-msm/dma.h * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008 QUALCOMM Incorporated. + * Copyright (c) 2008 QUALCOMM USA, INC. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -35,6 +37,7 @@ struct msm_dmov_cmd { #ifndef CONFIG_ARCH_MSM8X60 void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd); void msm_dmov_stop_cmd(unsigned id, struct msm_dmov_cmd *cmd, int graceful); +void msm_dmov_flush(unsigned int id); int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr); #else static inline @@ -77,6 +80,7 @@ int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr) { return -EIO; } #define DMOV_FLUSH3(ch) DMOV_SD_AARM(0x140, ch) #define DMOV_FLUSH4(ch) DMOV_SD_AARM(0x180, ch) #define DMOV_FLUSH5(ch) DMOV_SD_AARM(0x1C0, ch) +#define DMOV_FLUSH_TYPE (1 << 31) #define DMOV_STATUS(ch) DMOV_SD_AARM(0x200, ch) #define DMOV_STATUS_RSLT_COUNT(n) (((n) >> 29)) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 4cf30347fd8d0..b22f54a8176bc 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1345,6 +1345,28 @@ config SERIAL_MSM_RX_WAKEUP Requires SERIAL_MSM_CLOCK_CONTROL. Wake up the uart while the uart clock is off, using a wakeup GPIO. +config SERIAL_MSM_HS + tristate "MSM UART High Speed: Serial Driver" + depends on ARM && ARCH_MSM + select SERIAL_CORE + default n + help + Select this module to enable MSM high speed UART driver. + +config SERIAL_MSM_HS_UART1 + bool "Enable High Speed UART1" + depends on SERIAL_MSM_HS + default y + help + This option enables the UART 1 High Speed port in MSM UART driver. + +config SERIAL_MSM_HS_UART2 + bool "Enable High Speed UART2" + depends on SERIAL_MSM_HS + default n + help + This option enables the UART 2 High Speed port in MSM UART driver. + config SERIAL_NETX tristate "NetX serial port support" depends on ARM && ARCH_NETX diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 5117e18bf8eae..176699d75c0eb 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -77,6 +77,7 @@ obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o obj-$(CONFIG_SERIAL_MSM) += msm_serial.o obj-$(CONFIG_MSM_SERIAL_DEBUGGER) += msm_serial_debugger.o +obj-$(CONFIG_SERIAL_MSM_HS) += msm_serial_hs.o obj-$(CONFIG_SERIAL_NETX) += netx-serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c new file mode 100644 index 0000000000000..305658e77c047 --- /dev/null +++ b/drivers/tty/serial/msm_serial_hs.c @@ -0,0 +1,1532 @@ +/* drivers/serial/msm_serial_hs.c + * + * MSM High speed uart driver + * + * Copyright (c) 2007-2008 QUALCOMM Incorporated. + * Copyright (c) 2008 QUALCOMM USA, INC. + * + * All source code in this file is licensed under the following license + * except where indicated. + * + * 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, you can find it at http://www.fsf.org + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "msm_serial_hs_hwreg.h" +#include "msm_serial_hs.h" + +struct msm_uartdm_dmov_cb { + struct msm_dmov_cmd xfer; + struct msm_uartdm_port *msm_uport; +}; + +struct msm_uartdm_tx { + unsigned int tx_ready_int_en; + struct msm_uartdm_dmov_cb xfer; + /* Simple command buffer. Allocated and freed by driver. */ + dmov_box *command_ptr; + u32 *command_ptr_ptr; + dma_addr_t mapped_cmd_ptr; + struct workqueue_struct *tx_wq; + int tx_count; +}; + +struct msm_uartdm_rx { + unsigned int rx_ready_int_en; + struct msm_uartdm_dmov_cb xfer; + /* Simple command buffer. Allocated and freed by driver. */ + dmov_box *command_ptr; + u32 *command_ptr_ptr; + dma_addr_t mapped_cmd_ptr; + struct workqueue_struct *rx_wq; + struct completion complete; + dma_addr_t rbuffer; + unsigned char *buffer; + struct dma_pool *rx_pool; +}; + +struct msm_uartdm_port { + struct uart_port base; + unsigned long imr_reg; /* shadow value of UARTDM_IMR */ + struct clk *clk_id; + atomic_t open_cnt; + struct msm_uartdm_tx tx_desc; + struct msm_uartdm_rx rx_desc; + struct work_struct rx_data_worker; + struct work_struct tx_data_worker; + int dma_tx_channel; + int dma_rx_channel; + int dma_tx_crci; + int dma_rx_crci; +}; + +/* DM burst size (in bytes) (Constant) */ +#define MSM_UARTDM_BURST_SIZE 16 +#define UARTDM_TX_BUF_SIZE UART_XMIT_SIZE +#define UARTDM_RX_BUF_SIZE 512 + +/* cable status polling time value 250 msec */ +#define RX_GPIO_TIMER ((1000 * HZ)/1000) +/* RX Cable connect denounce */ +#define DEBOUNCE_RX_GPIO 3 + +#define UARTDM_MAX_COUNT 2 + +static struct msm_uartdm_port q_uart_port[UARTDM_MAX_COUNT]; +static struct platform_driver msm_uartdm_pd; +static struct uart_driver msm_uartdm_driver; +static struct uart_ops msm_uartdm_ops; +static char *uartdm_clks[] = { "uart1dm_clk", "uart2dm_clk" }; + +#if 1 +#define TRACE() do {} while (0) + +#define TRACE_DEBUG(fmt,args...) do {} while (0) + +#define TRACE_ERR(fmt,args...) do {} while (0) +#else +#define TRACE() printk(KERN_ALERT "%s() :%d\n", __FUNCTION__ , __LINE__) + +#define TRACE_DEBUG(fmt,args...) \ + printk(KERN_ALERT "%s() :%d " fmt "\n", \ + __FUNCTION__ , __LINE__ , ##args) + +#define TRACE_ERR(fmt,args...) \ + printk(KERN_ERR "%s() :%d " fmt "\n", __FUNCTION__ , __LINE__ , ##args) +#endif + +static inline unsigned int uport_read32(struct uart_port *uport, + unsigned int offset) +{ + return (ioread32((void *)((unsigned int)uport->mapbase + offset))); +} + +static inline void uport_write32(struct uart_port *uport, unsigned int offset, + unsigned int value) +{ + iowrite32(value, (void *)((unsigned int)uport->mapbase + offset)); +} + +/* Stub routine, current not needed (Used for power management) */ +static int msm_uartdm_wake(struct uart_port *uport, unsigned int state) +{ + return 0; +} + +/* Stubbed for future uses. (Called when system issues shutdown command) */ +static void msm_uartdm_pd_shutdown(struct platform_device *pdev) +{ + TRACE(); +} + +/* Called by msm_uart_remove to do port specific shutdown */ +static void msm_uartdm_remove_port(struct uart_port *uport) +{ + struct msm_uartdm_port *msm_uport; + + msm_uport = (struct msm_uartdm_port *)uport; + + TRACE(); + + uart_remove_one_port(&msm_uartdm_driver, uport); + clk_put(msm_uport->clk_id); + + /* Free the cable detect IRQ */ + free_irq(uport->irq, uport); + + /* Free the tx resources */ + kfree(msm_uport->tx_desc.command_ptr); + kfree(msm_uport->tx_desc.command_ptr_ptr); + + /* Free the rx resources */ + kfree(msm_uport->rx_desc.command_ptr); + kfree(msm_uport->rx_desc.command_ptr_ptr); + + dma_pool_free(msm_uport->rx_desc.rx_pool, msm_uport->rx_desc.buffer, + msm_uport->rx_desc.rbuffer); + dma_pool_destroy(msm_uport->rx_desc.rx_pool); +} + +/* + * When platform_driver_unregister() is called, this remove function of the + * driver will be called. + */ +static int msm_uartdm_remove(struct platform_device *pdev) +{ + int i; + struct uart_port *uport = NULL; + + for (i = 0; i < UARTDM_MAX_COUNT; i++) { + if (0 != q_uart_port[i].base.type) { + uport = (struct uart_port *)&(q_uart_port[i].base); + msm_uartdm_remove_port(uport); + }; + } + + return 0; +} + +static void msm_uartdm_release_port(struct uart_port *port) +{ + iounmap((void *)(port->mapbase)); +} + +/* Called during system suspend to do port specific suspend */ +static void msm_uartdm_suspend_port(struct uart_port *uport) +{ + struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; + unsigned long data; + + /* ready to sleep */ + if (atomic_read(&(msm_uport->open_cnt))) { + data = uport_read32(uport, UARTDM_ISR_ADDR); + if (data & UARTDM_ISR_CURRENT_CTS_BMSK) { + data = uport_read32(uport, UARTDM_MR1_ADDR); + data &= ~(UARTDM_MR1_CTS_CTL_BMSK); + uport_write32(uport, UARTDM_MR1_ADDR, data); + } + } + + /* ready to sleep */ + uart_suspend_port(&msm_uartdm_driver, uport); +} + +/* + * This suspend function is called when interrupt is enabled. + * You can sleep or do something, but be short. + */ +static int msm_uartdm_suspend(struct platform_device *pdev, pm_message_t state) +{ + int i; + struct uart_port *uport = NULL; + TRACE(); + + for (i = 0; i < UARTDM_MAX_COUNT; i++) { + if (q_uart_port[i].base.type) { + uport = (struct uart_port *)&(q_uart_port[i].base); + msm_uartdm_suspend_port(uport); + }; + } + + return 0; +} + +/* + * This suspend_late function is called when interrupt is disabled. You can't + * sleep here. This is mainly used to finish the unfinished job from suspend + * function. + * + * Note: The time between suspend and suspend_late is undeterministic. + */ +static int msm_uartdm_suspend_late(struct platform_device *pdev, + pm_message_t state) +{ + return 0; +} + +/* + * This resume_early function is called when interrupt is + * disabled. You can't sleep here. This is mainly used to turn + * on the things at a early time without being interrupted. + * + * Note: The time between resume_early and resume is undeterministic. + * + */ +static int msm_uartdm_resume_early(struct platform_device *pdev) +{ + return 0; +} + +static int msm_uartdm_resume_port(struct uart_port *uport) +{ + uart_resume_port(&msm_uartdm_driver, uport); + return 0; +} + +/* This resume function is called when interrupt is enabled. */ +static int msm_uartdm_resume(struct platform_device *pdev) +{ + int i; + struct uart_port *uport = NULL; + TRACE(); + + for (i = 0; i < UARTDM_MAX_COUNT; i++) { + if (q_uart_port[i].base.type) { + uport = (struct uart_port *)&(q_uart_port[i].base); + msm_uartdm_resume_port(uport); + }; + } + return 0; +} + +static int msm_uartdm_ioctl(struct uart_port *uport, unsigned int cmd, + unsigned long arg) +{ + return -ENOIOCTLCMD; +} + +static int msm_uartdm_enable_clk(struct uart_port *uport) +{ + int ret; + + /* Set up the MREG/NREG/DREG/MNDREG */ + struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; + ret = clk_enable(msm_uport->clk_id); + if (ret) { + printk(KERN_ERR "Error could not turn on UART clk\n"); + return ret; + } + + ret = clk_set_rate(msm_uport->clk_id, uport->uartclk); + if (ret) { + printk(KERN_WARNING "Error setting clock rate on UART\n"); + return ret; + } + + return 0; +} + +static inline void msm_uartdm_disable_clk(struct uart_port *uport) +{ + struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; + clk_disable(msm_uport->clk_id); +} + +/* Enable and Disable clocks (Used for power management) */ +static void msm_uartdm_pm(struct uart_port *uport, unsigned int state, + unsigned int oldstate) +{ + if (state == 3) { + msm_uartdm_disable_clk(uport); + } else if (state == 0) { + if (msm_uartdm_enable_clk(uport)) { + /* Clock not enabled */ + BUG_ON(1); + } + } +} + +/* + * programs the UARTDM_CSR register with correct bit rates + * + * Interrupts should be disabled before we are called, as + * we modify Set Baud rate + * Set receive stale interrupt level, dependant on Bit Rate + * Goal is to have around 8 ms before indicate stale. + * roundup (((Bit Rate * .008) / 10) + 1 + */ +static void msm_uartdm_set_bps(struct uart_port *uport, unsigned int bps) +{ + struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; + unsigned long rxstale; + unsigned long data; + + switch (bps) { + case 300: + uport_write32(uport, UARTDM_CSR_ADDR, 0x00); + rxstale = 1; + break; + case 600: + uport_write32(uport, UARTDM_CSR_ADDR, 0x11); + rxstale = 1; + break; + case 1200: + uport_write32(uport, UARTDM_CSR_ADDR, 0x22); + rxstale = 1; + break; + case 2400: + uport_write32(uport, UARTDM_CSR_ADDR, 0x33); + rxstale = 1; + break; + case 4800: + uport_write32(uport, UARTDM_CSR_ADDR, 0x44); + rxstale = 1; + break; + case 9600: + uport_write32(uport, UARTDM_CSR_ADDR, 0x55); + rxstale = 2; + break; + case 14400: + uport_write32(uport, UARTDM_CSR_ADDR, 0x66); + rxstale = 3; + break; + case 19200: + uport_write32(uport, UARTDM_CSR_ADDR, 0x77); + rxstale = 4; + break; + case 28800: + uport_write32(uport, UARTDM_CSR_ADDR, 0x88); + rxstale = 6; + break; + case 38400: + uport_write32(uport, UARTDM_CSR_ADDR, 0x99); + rxstale = 8; + break; + case 57600: + uport_write32(uport, UARTDM_CSR_ADDR, 0xaa); + rxstale = 16; + break; + case 76800: + uport_write32(uport, UARTDM_CSR_ADDR, 0xbb); + rxstale = 16; + break; + case 115200: + uport_write32(uport, UARTDM_CSR_ADDR, 0xcc); + rxstale = 31; + break; + case 230400: + uport_write32(uport, UARTDM_CSR_ADDR, 0xee); + rxstale = 31; + break; + case 460800: + uport_write32(uport, UARTDM_CSR_ADDR, 0xff); + rxstale = 31; + break; + case 4000000: + case 3686400: + case 3200000: + case 3500000: + case 3000000: + case 2500000: + case 1500000: + case 1152000: + case 1000000: + case 921600: + uport_write32(uport, UARTDM_CSR_ADDR, 0xff); + rxstale = 31; + break; + default: + uport_write32(uport, UARTDM_CSR_ADDR, 0xff); + /* default to 9600 */ + bps = 9600; + rxstale = 2; + break; + } + if (bps > 460800) { + uport->uartclk = bps * 16; + } else { + uport->uartclk = 7372800; + } + if (clk_set_rate(msm_uport->clk_id, uport->uartclk)) { + printk(KERN_WARNING "Error setting clock rate on UART\n"); + return; + } + + data = rxstale & UARTDM_IPR_STALE_LSB_BMSK; + data |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2); + + uport_write32(uport, UARTDM_IPR_ADDR, data); +} + +/* + * termios : new ktermios + * oldtermios: old ktermios previous setting + * + * Configure the serial port + */ +static void msm_uartdm_set_termios(struct uart_port *uport, + struct ktermios *termios, + struct ktermios *oldtermios) +{ + unsigned int bps; + unsigned long data; + unsigned int c_cflag = termios->c_cflag; + struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; + + TRACE(); + + /* 300 is the minimum baud support by the driver */ + bps = uart_get_baud_rate(uport, termios, oldtermios, 200, 4000000); + + /* Temporary remapping 200 BAUD to 3.2 mbps */ + if (bps == 200) + bps = 3200000; + + msm_uartdm_set_bps(uport, bps); + + data = uport_read32(uport, UARTDM_MR2_ADDR); + data &= ~UARTDM_MR2_PARITY_MODE_BMSK; + /* set parity */ + if (PARENB == (c_cflag & PARENB)) { + if (PARODD == (c_cflag & PARODD)) { + data |= ODD_PARITY; + } else if (CMSPAR == (c_cflag & CMSPAR)) { + data |= SPACE_PARITY; + } else { + data |= EVEN_PARITY; + } + } + + /* Set bits per char */ + data &= ~UARTDM_MR2_BITS_PER_CHAR_BMSK; + + switch (c_cflag & CSIZE) { + case CS5: + data |= FIVE_BPC; + break; + case CS6: + data |= SIX_BPC; + break; + case CS7: + data |= SEVEN_BPC; + break; + default: + data |= EIGHT_BPC; + break; + + } + /* stop bits */ + if (c_cflag & CSTOPB) { + data |= STOP_BIT_TWO; + } else { + /* otherwise 1 stop bit */ + data |= STOP_BIT_ONE; + } + data |= UARTDM_MR2_ERROR_MODE_BMSK; + /* write parity/bits per char/stop bit configuration */ + uport_write32(uport, UARTDM_MR2_ADDR, data); + + /* Configure HW flow control */ + data = uport_read32(uport, UARTDM_MR1_ADDR); + + data &= ~(UARTDM_MR1_CTS_CTL_BMSK | UARTDM_MR1_RX_RDY_CTL_BMSK); + + if (c_cflag & CRTSCTS) { + data |= UARTDM_MR1_CTS_CTL_BMSK; + data |= UARTDM_MR1_RX_RDY_CTL_BMSK; + } + + uport_write32(uport, UARTDM_MR1_ADDR, data); + + uport->ignore_status_mask = termios->c_iflag & INPCK; + uport->ignore_status_mask |= termios->c_iflag & IGNPAR; + uport->read_status_mask = (termios->c_cflag & CREAD); + + uport_write32(uport, UARTDM_IMR_ADDR, 0); + + /* Set Transmit software time out */ + uart_update_timeout(uport, c_cflag, bps); + + flush_workqueue(msm_uport->rx_desc.rx_wq); + uport_write32(uport, UARTDM_CR_ADDR, RESET_RX); + uport_write32(uport, UARTDM_CR_ADDR, RESET_TX); + + init_completion(&(msm_uport->rx_desc.complete)); + msm_dmov_flush(msm_uport->dma_rx_channel); + + uport_write32(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); +} + +/* + * Standard API, Transmitter + * Any character in the transmit shift register is sent + */ +static unsigned int msm_uartdm_tx_empty(struct uart_port *uport) +{ + unsigned int data; + + data = uport_read32(uport, UARTDM_SR_ADDR); + if (UARTDM_SR_TXEMT_BMSK == (data & UARTDM_SR_TXEMT_BMSK)) { + return TIOCSER_TEMT; + } else { + return 0; + } +} + +/* + * Standard API, Stop transmitter. + * Any character in the transmit shift register is sent as + * well as the current data mover transfer . + */ +static void msm_uartdm_stop_tx(struct uart_port *uport) +{ + struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; + + TRACE(); + msm_uport->tx_desc.tx_ready_int_en = 0; +} + +/* + * Standard API, Stop receiver as soon as possible. + * + * Function immediately terminates the operation of the + * channel receiver and any incoming characters are lost. None + * of the receiver status bits are affected by this command and + * characters that are already in the receive FIFO there. + */ +static void msm_uartdm_stop_rx(struct uart_port *uport) +{ + struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; + struct msm_uartdm_rx *rx_desc_ptr; + + TRACE(); + + rx_desc_ptr = &(msm_uport->rx_desc); + + /* Disable the receiver */ + uport_write32(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_DISABLE_BMSK); + if (rx_desc_ptr->rx_ready_int_en == 1) { + init_completion(&(rx_desc_ptr->complete)); + rx_desc_ptr->rx_ready_int_en = 0; + msm_dmov_flush(msm_uport->dma_rx_channel); + } +} + +/* Transmit the next chunk of data */ +static void msm_uartdm_submit_tx(struct uart_port *uport) +{ + struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; + struct circ_buf *tx_buf = &uport->info->xmit; + int tx_count; + int left; + struct msm_uartdm_tx *tx_desc_ptr; + struct msm_dmov_cmd *tx_xfer_ptr = NULL; + + tx_desc_ptr = &(msm_uport->tx_desc); + + TRACE(); + if (uart_circ_empty(tx_buf) || uport->info->port.tty->stopped) { + msm_uartdm_stop_tx(uport); + return; + } + + tx_count = uart_circ_chars_pending(tx_buf); + + if (UARTDM_TX_BUF_SIZE < tx_count) + tx_count = UARTDM_TX_BUF_SIZE; + + left = UART_XMIT_SIZE - tx_buf->tail; + + if (tx_count > left) + tx_count = left; + + + tx_desc_ptr->command_ptr->num_rows = (((tx_count + 15) >> 4) << 16) | + ((tx_count + 15) >> 4); + tx_desc_ptr->command_ptr->src_row_addr = + (u32) dma_map_single(NULL, &(tx_buf->buf [tx_buf->tail]), + tx_count, DMA_TO_DEVICE); + + tx_desc_ptr->mapped_cmd_ptr = dma_map_single(NULL, + tx_desc_ptr->command_ptr, + (sizeof(dmov_box)), + DMA_TO_DEVICE); + + *(tx_desc_ptr->command_ptr_ptr) = CMD_PTR_LP | + DMOV_CMD_ADDR(tx_desc_ptr->mapped_cmd_ptr); + + tx_xfer_ptr = (struct msm_dmov_cmd *)&tx_desc_ptr->xfer; + + tx_xfer_ptr->cmdptr = DMOV_CMD_ADDR(dma_map_single(NULL, + tx_desc_ptr-> + command_ptr_ptr, + sizeof(u32 *), + DMA_TO_DEVICE)); + + /* Save tx_count to use in Callback */ + tx_desc_ptr->tx_count = tx_count; + uport_write32(uport, UARTDM_NCF_TX_ADDR, tx_count); + + /* Disable the tx_ready interrupt */ + msm_uport->imr_reg &= ~UARTDM_ISR_TX_READY_BMSK; + uport_write32(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + msm_dmov_enqueue_cmd(msm_uport->dma_tx_channel, tx_xfer_ptr); +} + +/* Start to receive the next chunk of data */ +static void msm_uart_start_rx(struct uart_port *uport) +{ + + struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; + struct msm_uartdm_rx *rx_desc_ptr; + struct msm_dmov_cmd *rx_xfer_ptr; + + TRACE(); + rx_desc_ptr = &(msm_uport->rx_desc); + rx_desc_ptr->command_ptr->num_rows = ((UARTDM_RX_BUF_SIZE >> 4) << 16) | + (UARTDM_RX_BUF_SIZE >> 4); + + + rx_desc_ptr->command_ptr->dst_row_addr = rx_desc_ptr->rbuffer; + + rx_desc_ptr->mapped_cmd_ptr = dma_map_single(NULL, + rx_desc_ptr->command_ptr, + (sizeof(dmov_box)), + DMA_TO_DEVICE); + + *(rx_desc_ptr->command_ptr_ptr) = CMD_PTR_LP | + DMOV_CMD_ADDR(rx_desc_ptr->mapped_cmd_ptr); + + rx_xfer_ptr = (struct msm_dmov_cmd *)&rx_desc_ptr->xfer; + rx_xfer_ptr->cmdptr = DMOV_CMD_ADDR(dma_map_single(NULL, + rx_desc_ptr-> + command_ptr_ptr, + sizeof(u32 *), + DMA_TO_DEVICE)); + + init_completion(&(rx_desc_ptr->complete)); + msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel, rx_xfer_ptr); + + uport_write32(uport, UARTDM_CR_ADDR, RESET_STALE_INT); + + uport_write32(uport, UARTDM_DMRX_ADDR, UARTDM_RX_BUF_SIZE); + uport_write32(uport, UARTDM_CR_ADDR, STALE_EVENT_ENABLE); +} + +/* + * This is a workqueue handler to complete DMA tx + * transactions and submit new transactions + */ +void msm_uart_tx_workqueue_handler(struct work_struct *work) +{ + struct msm_uartdm_port *msm_uport = NULL; + struct uart_port *uport = NULL; + struct msm_uartdm_tx *tx_desc_ptr = NULL; + struct circ_buf *tx_buf = NULL; + struct msm_dmov_cmd *tx_xfer_ptr = NULL; + + TRACE(); + msm_uport = container_of(work, struct msm_uartdm_port, tx_data_worker); + if (msm_uport) { + uport = (struct uart_port *)msm_uport; + tx_buf = &uport->info->xmit; + tx_desc_ptr = &(msm_uport->tx_desc); + + /* DMA is done un-map memmory addresses */ + tx_xfer_ptr = (struct msm_dmov_cmd *)&tx_desc_ptr->xfer; + dma_unmap_single(NULL, (dma_addr_t) tx_xfer_ptr->cmdptr, + sizeof(u32 *), DMA_TO_DEVICE); + + dma_unmap_single(NULL, tx_desc_ptr->mapped_cmd_ptr, + (sizeof(dmov_box)), DMA_TO_DEVICE); + + dma_unmap_single(NULL, + (dma_addr_t) tx_desc_ptr->command_ptr-> + src_row_addr, tx_desc_ptr->tx_count, + DMA_TO_DEVICE); + + tx_buf->tail = (tx_buf->tail + tx_desc_ptr->tx_count) & + (~UART_XMIT_SIZE); + + uport->icount.tx += tx_desc_ptr->tx_count; + if (msm_uport->tx_desc.tx_ready_int_en) + msm_uartdm_submit_tx(uport); + + if (uart_circ_chars_pending(tx_buf) < WAKEUP_CHARS) + uart_write_wakeup(uport); + } +} + +/* + * This is a workqueue handler to complete DMA rx + * transactions and start another rx transaction + */ +void msm_uart_rx_workqueue_handler(struct work_struct *work) +{ + struct msm_uartdm_port *msm_uport = NULL; + struct msm_uartdm_rx *rx_desc_ptr = NULL; + struct uart_port *uport = NULL; + struct tty_struct *tty = NULL; + struct msm_dmov_cmd *rx_xfer_ptr = NULL; + + int retval; + int rx_count; + + TRACE(); + + msm_uport = container_of(work, struct msm_uartdm_port, rx_data_worker); + if (msm_uport) { + uport = (struct uart_port *)msm_uport; + tty = uport->info->port.tty; + rx_desc_ptr = &(msm_uport->rx_desc); + rx_xfer_ptr = (struct msm_dmov_cmd *)&rx_desc_ptr->xfer; + + /* DMA is done un-map memmory addresses */ + dma_unmap_single(NULL, (dma_addr_t) rx_xfer_ptr->cmdptr, + sizeof(u32 *), DMA_TO_DEVICE); + + dma_unmap_single(NULL, rx_desc_ptr->mapped_cmd_ptr, + (sizeof(dmov_box)), DMA_TO_DEVICE); + + rx_count = uport_read32(uport, UARTDM_RX_TOTAL_SNAP_ADDR); + + /* Ignore Parity Errors */ + + if (0 != (uport->read_status_mask & CREAD)) { + retval = tty_insert_flip_string(tty, + rx_desc_ptr->buffer, + rx_count); + BUG_ON(retval != rx_count); + } + + tty_flip_buffer_push(tty); + + if (1 == rx_desc_ptr->rx_ready_int_en) + msm_uart_start_rx(uport); + complete(&(rx_desc_ptr->complete)); + } +} + +/* Enable the transmitter Interrupt */ +static void msm_uartdm_start_tx(struct uart_port *uport) +{ + struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; + TRACE(); + if (0 == msm_uport->tx_desc.tx_ready_int_en) { + msm_uport->tx_desc.tx_ready_int_en = 1; + msm_uartdm_submit_tx(uport); + } +} + +/* + * This routine is called when we are done with a DMA transfer + * + * This routine is registered with Data mover when we set + * up a Data Mover transfer. It is called from Data mover ISR + * when the DMA transfer is done. + */ +static void msm_uartdm_dmov_tx_callback(struct msm_dmov_cmd *cmd_ptr, + unsigned int result, + struct msm_dmov_errdata *err) +{ + struct msm_uartdm_dmov_cb *xfer; + struct msm_uartdm_port *msm_uport; + struct uart_port *uport; + struct msm_uartdm_tx *tx_desc_ptr; + + xfer = (struct msm_uartdm_dmov_cb *)cmd_ptr; + msm_uport = xfer->msm_uport; + uport = (struct uart_port *)msm_uport; + + TRACE(); + tx_desc_ptr = &(msm_uport->tx_desc); + + /* DMA did not finish properly error out */ + BUG_ON(result != 0x80000002); + + msm_uport->imr_reg |= UARTDM_ISR_TX_READY_BMSK; + uport_write32(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); +} + +/* + * This routine is called when we are done with a DMA transfer or the + * a flush has been sent to the data mover driver. + * + * This routine is registered with Data mover when we set up a Data Mover + * transfer. It is called from Data mover ISR when the DMA transfer is done. + */ +static void msm_uartdm_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, + unsigned int result, + struct msm_dmov_errdata *err) +{ + struct msm_uartdm_dmov_cb *xfer; + struct msm_uartdm_port *msm_uport; + struct uart_port *uport; + struct msm_uartdm_rx *rx_desc_ptr; + struct tty_struct *tty; + unsigned long status; + int retval; + unsigned int error_f = 0; + + TRACE(); + + xfer = (struct msm_uartdm_dmov_cb *)cmd_ptr; + msm_uport = xfer->msm_uport; + uport = (struct uart_port *)msm_uport; + tty = uport->info->port.tty; + + rx_desc_ptr = &(msm_uport->rx_desc); + + uport_write32(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); + + status = uport_read32(uport, UARTDM_SR_ADDR); + + /* overflow is not connect to data in a FIFO */ + if (unlikely((status & UARTDM_SR_OVERRUN_BMSK) && + (uport->read_status_mask & CREAD))) { + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + uport->icount.buf_overrun++; + error_f = 1; + } + + if (!(uport->ignore_status_mask & INPCK)) + status = status & (~UARTDM_SR_PAR_FRAME_BMSK); + + if (unlikely(status & UARTDM_SR_PAR_FRAME_BMSK)) { + /*Can not tell difference between parity & frame error */ + uport->icount.parity++; + error_f = 1; + if (uport->ignore_status_mask & IGNPAR) + tty_insert_flip_char(tty, 0, TTY_PARITY); + } + + if (error_f) + uport_write32(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS); + + retval = queue_work(rx_desc_ptr->rx_wq, &(msm_uport->rx_data_worker)); + + /* Failed to queue DMA transfer */ + BUG_ON(0 == retval); +} + +/* + * Standard API, Current states of modem control inputs + * + * Since CTS can be handled entirely by HARDWARE we always + * indicate clear to send and count on the TX FIFO to block when + * it fills up. + * + * - TIOCM_DCD + * - TIOCM_CTS + * - TIOCM_DSR + * - TIOCM_RI + * (Unsupported) DCD and DSR will return them high. RI will return low. + */ +static unsigned int msm_uartdm_get_mctrl(struct uart_port *uport) +{ + unsigned int ret; + + ret = TIOCM_DSR | TIOCM_CAR | TIOCM_CTS; + return ret; +} + +/* + * Standard API, Set or clear RFR_signal + * + * Set RFR high, (Indicate we are not ready for data), we disable auto + * ready for receiving and then set RFR_N high. To set RFR to low we just turn + * back auto ready for receiving and it should lower RFR signal + * when hardware is ready + * + */ +static void msm_uartdm_set_mctrl(struct uart_port *uport, unsigned int mctrl) +{ + unsigned int set_rts; + unsigned int data = 0; + + TRACE(); + + /* RTS is active low */ + + if (TIOCM_RTS & mctrl) { + set_rts = 0; + } else { + set_rts = 1; + } + + data = uport_read32(uport, UARTDM_MR1_ADDR); + if (set_rts) { + /*disable auto ready-for-receiving */ + data &= ~UARTDM_MR1_RX_RDY_CTL_BMSK; + uport_write32(uport, UARTDM_MR1_ADDR, data); + /* set RFR_N to high */ + uport_write32(uport, UARTDM_CR_ADDR, RFR_HIGH); + } else { + /* Enable auto ready-for-receiving */ + data |= UARTDM_MR1_RX_RDY_CTL_BMSK; + uport_write32(uport, UARTDM_MR1_ADDR, data); + } +} + +/* Standard API, Enable modem status (CTS) interrupt */ +static void msm_uartdm_enable_ms(struct uart_port *uport) +{ + struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; + + /* Enable DELTA_CTS Interrupt */ + msm_uport->imr_reg |= UARTDM_ISR_DELTA_CTS_BMSK; + uport_write32(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); +} + +/* + * Standard API, Break Signal + * + * Control the transmission of a break signal. ctl eq 0 => break + * signal terminate ctl ne 0 => start break signal + */ +static void msm_uartdm_break_ctl(struct uart_port *uport, int ctl) +{ + if (ctl) { + uport_write32(uport, UARTDM_CR_ADDR, START_BREAK); + } else { + uport_write32(uport, UARTDM_CR_ADDR, STOP_BREAK); + } +} + +/* Need to be non-zero value or port will not init. */ +static void msm_uartdm_config_port(struct uart_port *uport, int flags) +{ + /* Set port type to any non-zero value */ + if (flags & UART_CONFIG_TYPE) + uport->type = 1; +} + +/* Handle CTS changes (Called from interrupt handler) */ +static void msm_uartdm_handle_delta_cts(struct uart_port *uport) +{ + /* clear interrupt */ + uport_write32(uport, UARTDM_CR_ADDR, RESET_CTS); + uport->icount.cts++; + /* clear the IOCTL TIOCMIWAIT if called */ + wake_up_interruptible(&uport->info->delta_msr_wait); +} + +static irqreturn_t msm_uartdm_isr(int irq, void *dev) +{ + struct uart_port *uport = (struct uart_port *)dev; + unsigned long isr_status; + struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; + u32 retval; + struct msm_uartdm_tx *tx_desc_ptr; + tx_desc_ptr = &(msm_uport->tx_desc); + + isr_status = uport_read32(uport, UARTDM_MISR_ADDR); + + /* Mask UART interrupts */ + disable_irq(uport->irq); + TRACE(); + + /* Stale rx interrupt */ + if (isr_status & UARTDM_ISR_RXSTALE_BMSK) { + uport_write32(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); + uport_write32(uport, UARTDM_CR_ADDR, RESET_STALE_INT); + + msm_dmov_flush(msm_uport->dma_rx_channel); + } + /* tx ready interrupt */ + if (isr_status & UARTDM_ISR_TX_READY_BMSK) { + /*Clear TX Ready */ + uport_write32(uport, UARTDM_CR_ADDR, CLEAR_TX_READY); + retval = queue_work(tx_desc_ptr->tx_wq, + &(msm_uport->tx_data_worker)); + BUG_ON(1 != retval); + } + + /* Change in CTS interrupt */ + if (isr_status & UARTDM_ISR_DELTA_CTS_BMSK) + msm_uartdm_handle_delta_cts(uport); + + enable_irq(uport->irq); + return IRQ_HANDLED; +} + +static const char *msm_uartdm_type(struct uart_port *port) +{ + return ("MSM UART"); +} + +/* + * Called by the upper layer when port is open. + * + * - Initializes the port + * - Hook the ISR + * - Enable Receive Interrupt + */ +static int msm_uartdm_startup(struct uart_port *uport) +{ + int rfr_level; + unsigned int data; + struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; + struct msm_uartdm_tx *tx_desc_ptr; + struct msm_uartdm_rx *rx_desc_ptr; + struct msm_dmov_cmd *tx_xfer_ptr; + struct msm_dmov_cmd *rx_xfer_ptr; + + TRACE(); + atomic_inc(&(msm_uport->open_cnt)); + + tx_desc_ptr = &(msm_uport->tx_desc); + rx_desc_ptr = &(msm_uport->rx_desc); + + if (uport->fifosize > 16) { + rfr_level = uport->fifosize - 16; + } else { + rfr_level = uport->fifosize; + } + + /* Set auto RFR Level */ + data = uport_read32(uport, UARTDM_MR1_ADDR); + data &= (~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK); + data &= (~UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK); + data |= (UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK & (rfr_level << 2)); + data |= (UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK & rfr_level); + uport_write32(uport, UARTDM_MR1_ADDR, data); + + /* Make sure RXSTALE count is non-zero */ + data = uport_read32(uport, UARTDM_IPR_ADDR); + if (!data) { + data |= 0x1f & UARTDM_IPR_STALE_LSB_BMSK; + uport_write32(uport, UARTDM_IPR_ADDR, data); + } + + /* Enable Data Mover Mode */ + data = UARTDM_TX_DM_EN_BMSK | UARTDM_RX_DM_EN_BMSK; + uport_write32(uport, UARTDM_DMEN_ADDR, data); + + /* Reset TX */ + uport_write32(uport, UARTDM_CR_ADDR, RESET_TX); + + uport_write32(uport, UARTDM_CR_ADDR, RESET_RX); + uport_write32(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS); + uport_write32(uport, UARTDM_CR_ADDR, RESET_BREAK_INT); + uport_write32(uport, UARTDM_CR_ADDR, RESET_STALE_INT); + uport_write32(uport, UARTDM_CR_ADDR, RESET_CTS); + uport_write32(uport, UARTDM_CR_ADDR, RFR_LOW); + /* Turn on Uart Receiver */ + uport_write32(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_EN_BMSK); + + /* Turn on Uart Transmitter */ + uport_write32(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_EN_BMSK); + + /* Initialize the tx */ + tx_desc_ptr->tx_ready_int_en = 0; + + tx_desc_ptr->xfer.msm_uport = msm_uport; + tx_xfer_ptr = (struct msm_dmov_cmd *)&tx_desc_ptr->xfer; + tx_xfer_ptr->complete_func = msm_uartdm_dmov_tx_callback; + + tx_desc_ptr->command_ptr->cmd = CMD_LC | + CMD_DST_CRCI(msm_uport->dma_tx_crci) | CMD_MODE_BOX; + + tx_desc_ptr->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16) + | (MSM_UARTDM_BURST_SIZE); + + tx_desc_ptr->command_ptr->row_offset = (MSM_UARTDM_BURST_SIZE << 16); + + tx_desc_ptr->command_ptr->dst_row_addr = MSM_UART1DM_PHYS + UARTDM_TF_ADDR; + + + /* Turn on Uart Receive */ + + rx_desc_ptr->xfer.msm_uport = msm_uport; + rx_xfer_ptr = (struct msm_dmov_cmd *)&rx_desc_ptr->xfer; + rx_xfer_ptr->complete_func = msm_uartdm_dmov_rx_callback; + + rx_desc_ptr->command_ptr->cmd = CMD_LC | + CMD_SRC_CRCI(msm_uport->dma_rx_crci) | CMD_MODE_BOX; + + rx_desc_ptr->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16) + | (MSM_UARTDM_BURST_SIZE); + rx_desc_ptr->command_ptr->src_row_addr = MSM_UART1DM_PHYS + UARTDM_RF_ADDR; + + rx_desc_ptr->command_ptr->row_offset = MSM_UARTDM_BURST_SIZE; + + rx_desc_ptr->command_ptr->src_row_addr = (unsigned int)uport->membase + + UARTDM_RF_ADDR; + + + rx_desc_ptr->rx_wq = create_singlethread_workqueue("rx_wq"); + INIT_WORK(&(msm_uport->rx_data_worker), msm_uart_rx_workqueue_handler); + + tx_desc_ptr->tx_wq = create_singlethread_workqueue("tx_wq"); + INIT_WORK(&(msm_uport->tx_data_worker), msm_uart_tx_workqueue_handler); + + rx_desc_ptr->rx_ready_int_en = 1; + + msm_uport->imr_reg |= UARTDM_ISR_RXSTALE_BMSK; + /* Enable reading the current CTS, no harm even if CTS is ignored */ + msm_uport->imr_reg |= UARTDM_ISR_CURRENT_CTS_BMSK; + + enable_irq(uport->irq); + uport_write32(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + + msm_uart_start_rx(uport); + + return 0; +} + +/* Initializes uart_port data structure and adds the port to the driver */ +int uartdm_init_port(struct uart_driver *udriver, struct uart_port *uport, + unsigned long base_addr, unsigned long phy_base_addr, + unsigned long irq_num, unsigned long fifo_size, char *desc) +{ + int ret; + struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; + struct msm_uartdm_tx *tx_desc_ptr; + struct msm_uartdm_rx *rx_desc_ptr; + + TRACE(); + + atomic_set(&(msm_uport->open_cnt), 0); + rx_desc_ptr = &(msm_uport->rx_desc); + tx_desc_ptr = &(msm_uport->tx_desc); + uport->iotype = UPIO_MEM; + uport->irq = irq_num; + uport->fifosize = fifo_size; + + uport->membase = (void *)phy_base_addr; + /* base_addr is a virtual address */ + uport->mapbase = base_addr; + + uport->ops = &msm_uartdm_ops; + uport->flags = UPF_BOOT_AUTOCONF; + ((struct msm_uartdm_port *)uport)->imr_reg = 0x0; + + /* Perform the hardware initialization */ + + msm_uport->clk_id = clk_get(NULL, desc); + + ret = msm_uartdm_enable_clk(uport); + if (ret) { + printk(KERN_ERR "Error could not enable clk"); + return ret; + } + + ret = request_irq(uport->irq, msm_uartdm_isr, + (IRQF_TRIGGER_HIGH), "msm_uartdm", uport); + + /* irq will be enabled when port is opened */ + disable_irq(uport->irq); + + /* Failed to Hook ISR error Out */ + if (ret) { + printk(KERN_ERR "Failed to hook up main ISR \n"); + return ret; + } + + /* Allocate the command pointer. Needs to be 64 bit aligned */ + tx_desc_ptr->command_ptr = kmalloc((sizeof(dmov_box)), + GFP_KERNEL | __GFP_DMA); + + tx_desc_ptr->command_ptr_ptr = kmalloc((sizeof(void *)), + GFP_KERNEL | __GFP_DMA); + + if ((tx_desc_ptr->command_ptr == NULL) || + (tx_desc_ptr->command_ptr_ptr == NULL)) + return -ENOMEM; + + rx_desc_ptr->rx_pool = dma_pool_create("rx_buffer_pool", NULL, + UARTDM_RX_BUF_SIZE, 16, 0); + + rx_desc_ptr->buffer = dma_pool_alloc(rx_desc_ptr->rx_pool, GFP_KERNEL, + &(rx_desc_ptr->rbuffer)); + + /* Allocate the command pointer. Needs to be 64 bit aligned */ + rx_desc_ptr->command_ptr = kmalloc((sizeof(dmov_box)), + GFP_KERNEL | __GFP_DMA); + + rx_desc_ptr->command_ptr_ptr = kmalloc((sizeof(void *)), + GFP_KERNEL | __GFP_DMA); + + if ((rx_desc_ptr->command_ptr == NULL) || + (rx_desc_ptr->command_ptr_ptr == NULL) || + (rx_desc_ptr->rx_pool == NULL) || (rx_desc_ptr->buffer == NULL)) + return -ENOMEM; + + /* configure the CR Protection to Enable */ + uport_write32(uport, UARTDM_CR_ADDR, CR_PROTECTION_EN); + + msm_uartdm_disable_clk(uport); + + ret = uart_add_one_port(udriver, uport); + if (ret) { + printk(KERN_ERR "uartdm_init_port Failed to add port \n"); + return ret; + } + return 0; +} + +static int msm_uartdm_probe(struct platform_device *pdev) +{ + int ret = 0; + int i; + struct uart_port *uport; + struct msm_uartdm_port *msm_uport; + unsigned int base_addr = 0; + unsigned long phy_base_addr = 0; + + /* Interrupt from UARTDM controller */ + unsigned int uartdm_irq = 0; + + TRACE_DEBUG("plaform device ID = %d", pdev->id); + + if (pdev->id != 1) { + TRACE_ERR(" Invalid plaform device ID = %d\n", pdev->id); + + return -EINVAL; + } + + if ((pdev->resource == NULL) || (pdev->num_resources != 4)) { + TRACE_ERR(" Invalid num of resources pdev->resource = %u" + "pdev->num_resources = %d \n", + (unsigned int)(pdev->resource), pdev->num_resources); + + return -ENXIO; + } + + /* Init all UARTS as non-configured */ + for (i = 0; i < UARTDM_MAX_COUNT; i++) { + q_uart_port[i].base.type = 0; + + q_uart_port[i].dma_tx_channel = 0; + q_uart_port[i].dma_rx_channel = 0; + + q_uart_port[i].dma_tx_crci = 0; + q_uart_port[i].dma_rx_crci = 0; + + /* Default Clock */ + q_uart_port[i].base.uartclk = 7372800; + } + + uport = (struct uart_port *)&(q_uart_port[pdev->id - 1]); + msm_uport = &(q_uart_port[pdev->id - 1]); + + for (i = 0; i < pdev->num_resources; i++) { + + if (pdev->resource[i].flags & IORESOURCE_MEM) { + + phy_base_addr = pdev->resource[i].start; + base_addr = + (unsigned int)ioremap(phy_base_addr, PAGE_SIZE); + if (!base_addr) { + TRACE_ERR("No Memory\n"); + return -ENOMEM; + } + TRACE_DEBUG("phy_base_addr = %08x," + " virt base_addr = %08x ", + phy_base_addr, base_addr); + + } + if (pdev->resource[i].flags & IORESOURCE_IRQ) { + uartdm_irq = pdev->resource[i].start; + + TRACE_DEBUG("uartdm_irq = %d ", uartdm_irq); + } + if (pdev->resource[i].flags & IORESOURCE_DMA) { + if (!strcmp(pdev->resource[i].name, "uartdm_chanels")) { + msm_uport->dma_tx_channel = + pdev->resource[i].start; + + msm_uport->dma_rx_channel = + pdev->resource[i].end; + + TRACE_DEBUG("dma_tx_channel = %d," + " dma_rx_channel = %d ", + msm_uport->dma_tx_channel, + msm_uport->dma_rx_channel); + } + if (!strcmp(pdev->resource[i].name, "uartdm_crci")) { + msm_uport->dma_tx_crci = + pdev->resource[i].start; + msm_uport->dma_rx_crci = pdev->resource[i].end; + + TRACE_DEBUG("dma_tx_crci = %d, " + "dma_rx_crci= %d ", + msm_uport->dma_tx_crci, + msm_uport->dma_rx_crci); + } + } + } + + if (!uartdm_irq || !msm_uport->dma_tx_channel || + !msm_uport->dma_rx_channel || !msm_uport->dma_tx_crci || + !msm_uport->dma_rx_crci) { + + TRACE_ERR("Invalid irq, or DM channels or DM CRCIs"); + return -ENXIO; + } + + /* FIFO size for UARTDM is 64 */ + + uport->dev = &(pdev->dev); + + ret = uartdm_init_port(&msm_uartdm_driver, uport, base_addr, + phy_base_addr, uartdm_irq, 64, + uartdm_clks[pdev->id - 1]); + + if (ret != 0) { + printk(KERN_ERR "msm_uartdm_probe failed on port %d \n", + pdev->id); + uart_unregister_driver(&msm_uartdm_driver); + return ret; + } + + return ret; +} + +static int __init msm_uartdm_init(void) +{ + int ret = 0; + TRACE(); + ret = uart_register_driver(&msm_uartdm_driver); + if (ret) { + printk(KERN_ERR "msm_uart_init failed to load module \n"); + return ret; + } + ret = platform_driver_register(&msm_uartdm_pd); + if (ret) { + printk(KERN_ERR "msm_uart_init failed to load module \n"); + uart_unregister_driver(&msm_uartdm_driver); + return ret; + } + printk(KERN_INFO "msm_uart_hs module loaded!\n"); + return ret; +} + +/* + * Called by the upper layer when port is closed. + * - Disables the port + * - Unhook the ISR + */ +static void msm_uartdm_shutdown(struct uart_port *uport) +{ + struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; + struct msm_uartdm_rx *rx_desc_ptr; + struct msm_uartdm_tx *tx_desc_ptr; + + rx_desc_ptr = &(msm_uport->rx_desc); + + tx_desc_ptr = &(msm_uport->tx_desc); + TRACE(); + + atomic_dec(&(msm_uport->open_cnt)); + /* Disable the transmitter */ + uport_write32(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_DISABLE_BMSK); + /* Disable the receiver */ + uport_write32(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_DISABLE_BMSK); + + /* Free the interrupt */ + disable_irq(uport->irq); + msm_uport->imr_reg = 0; + uport_write32(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + wait_for_completion(&rx_desc_ptr->complete); + flush_workqueue(msm_uport->rx_desc.rx_wq); + destroy_workqueue(rx_desc_ptr->rx_wq); + + flush_workqueue(msm_uport->tx_desc.tx_wq); + destroy_workqueue(tx_desc_ptr->tx_wq); +} + +static void __exit msm_uartdm_exit(void) +{ + printk(KERN_INFO "msm_uart_hs module removed!\n"); + platform_driver_unregister(&msm_uartdm_pd); + + uart_unregister_driver(&msm_uartdm_driver); +} + +/* for ttyHS0 pass 0 as line parameter */ +unsigned int msm_uart_hs_tx_empty(int line) +{ + struct uart_port *uport; + unsigned int data; + uport = (struct uart_port *)&(q_uart_port[line]); + + data = uport_read32(uport, UARTDM_SR_ADDR); + if (UARTDM_SR_TXEMT_BMSK == (data & UARTDM_SR_TXEMT_BMSK)) { + return 1; + } else { + return 0; + } +} +EXPORT_SYMBOL(msm_uart_hs_tx_empty); + +/* + * To be used by a kernel module so that it can turn UART on and off to save + * power.It is up to the upper layer to manage if it is correct to do. + */ +void msm_uart_hs_safe_pm(int line, enum msm_uart_hs_pm_e mode) +{ + struct uart_port *uport; + uport = (struct uart_port *)&(q_uart_port[line]); + + switch (mode) { + case MSM_UART_HS_POWER_DOWN: + disable_irq(uport->irq); + msm_uartdm_disable_clk(uport); + break; + case MSM_UART_HS_POWER_UP: + if (msm_uartdm_enable_clk(uport)) + BUG_ON(1); + enable_irq(uport->irq); + break; + default: + break; + } +} +EXPORT_SYMBOL(msm_uart_hs_safe_pm); + +static struct platform_driver msm_uartdm_pd = { + .probe = msm_uartdm_probe, + .remove = msm_uartdm_remove, + .suspend = msm_uartdm_suspend, + .suspend_late = msm_uartdm_suspend_late, + .resume_early = msm_uartdm_resume_early, + .resume = msm_uartdm_resume, + .shutdown = msm_uartdm_pd_shutdown, + .driver = { + /* Driver name must match the device name used + in platform_devices for uarts_hs devices . + */ + .name = "msm_serial_hs", + }, +}; + +static struct uart_driver msm_uartdm_driver = { + .owner = THIS_MODULE, + .driver_name = "msm_uartdm", + .dev_name = "ttyHS", + .major = 0, + .minor = 0, + .nr = UARTDM_MAX_COUNT, + .cons = 0, +}; + +static struct uart_ops msm_uartdm_ops = { + .tx_empty = msm_uartdm_tx_empty, + .set_mctrl = msm_uartdm_set_mctrl, + .get_mctrl = msm_uartdm_get_mctrl, + .stop_tx = msm_uartdm_stop_tx, + .start_tx = msm_uartdm_start_tx, + .stop_rx = msm_uartdm_stop_rx, + .enable_ms = msm_uartdm_enable_ms, + .break_ctl = msm_uartdm_break_ctl, + .startup = msm_uartdm_startup, + .shutdown = msm_uartdm_shutdown, + .set_termios = msm_uartdm_set_termios, + .pm = msm_uartdm_pm, + .set_wake = msm_uartdm_wake, + .type = msm_uartdm_type, + .config_port = msm_uartdm_config_port, + .ioctl = msm_uartdm_ioctl, + .release_port = msm_uartdm_release_port, +}; + +module_init(msm_uartdm_init); +module_exit(msm_uartdm_exit); +MODULE_DESCRIPTION("High Speed UART Driver for the MSM chipset"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/msm_serial_hs.h b/drivers/tty/serial/msm_serial_hs.h new file mode 100644 index 0000000000000..fe17eeccdb0a4 --- /dev/null +++ b/drivers/tty/serial/msm_serial_hs.h @@ -0,0 +1,34 @@ +/* drivers/serial/msm_serial_hs.h + * + * MSM High speed uart driver + * + * Copyright (c) 2007-2008 QUALCOMM Incorporated. + * Copyright (c) 2008 QUALCOMM USA, INC. + * + * All source code in this file is licensed under the following license + * except where indicated. + * + * 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, you can find it at http://www.fsf.org + */ + +#ifndef __MSM_UART_HS_H__ +#define __MSM_UART_HS_H__ + +enum msm_uart_hs_pm_e { + MSM_UART_HS_POWER_DOWN, + MSM_UART_HS_POWER_UP +}; +unsigned int msm_uart_hs_tx_empty(int line); +void msm_uart_hs_safe_pm(int line, enum msm_uart_hs_pm_e mode); + +#endif diff --git a/drivers/tty/serial/msm_serial_hs_hwreg.h b/drivers/tty/serial/msm_serial_hs_hwreg.h new file mode 100644 index 0000000000000..c3450153ad28e --- /dev/null +++ b/drivers/tty/serial/msm_serial_hs_hwreg.h @@ -0,0 +1,146 @@ +/* drivers/serial/msm_serial_hs_hwreg.h + * + * Copyright (c) 2007-2008 QUALCOMM Incorporated. + * Copyright (c) 2008 QUALCOMM USA, INC. + * + * All source code in this file is licensed under the following license + * except where indicated. + * + * 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, you can find it at http://www.fsf.org + */ + +#ifndef MSM_UART_HS_HWREG_H +#define MSM_UART_HS_HWREG_H + +#define UARTDM_MR1_ADDR 0x0 +#define UARTDM_MR2_ADDR 0x4 + +/* write only register */ +#define UARTDM_CSR_ADDR 0x8 + +/* write only register */ +#define UARTDM_TF_ADDR 0x70 +#define UARTDM_TF2_ADDR 0x74 +#define UARTDM_TF3_ADDR 0x78 +#define UARTDM_TF4_ADDR 0x7C + +/* write only register */ +#define UARTDM_CR_ADDR 0x10 +/* write only register */ +#define UARTDM_IMR_ADDR 0x14 + +#define UARTDM_IPR_ADDR 0x18 +#define UARTDM_TFWR_ADDR 0x1c +#define UARTDM_RFWR_ADDR 0x20 +#define UARTDM_HCR_ADDR 0x24 +#define UARTDM_DMRX_ADDR 0x34 +#define UARTDM_IRDA_ADDR 0x38 +#define UARTDM_DMEN_ADDR 0x3c + +/* UART_DM_NO_CHARS_FOR_TX */ +#define UARTDM_NCF_TX_ADDR 0x40 + +#define UARTDM_BADR_ADDR 0x44 + +#define UARTDM_SIM_CFG_ADDR 0x80 + +/* Read Only register */ +#define UARTDM_SR_ADDR 0x8 + +/* Read Only register */ +#define UARTDM_RF_ADDR 0x70 +#define UARTDM_RF2_ADDR 0x74 +#define UARTDM_RF3_ADDR 0x78 +#define UARTDM_RF4_ADDR 0x7C + +/* Read Only register */ +#define UARTDM_MISR_ADDR 0x10 + +/* Read Only register */ +#define UARTDM_ISR_ADDR 0x14 +#define UARTDM_RX_TOTAL_SNAP_ADDR 0x38 + +#define UARTDM_RXFS_ADDR 0x50 + +/* Register field Mask Mapping */ +#define UARTDM_SR_TXEMT_BMSK 0x8 + +#define UARTDM_SR_TXRDY_BMSK 0x4 +#define UARTDM_SR_RXRDY_BMSK 0x1 +#define UARTDM_SR_OVERRUN_BMSK 0x10 +#define UARTDM_SR_PAR_FRAME_BMSK 0x20 + +#define UARTDM_CR_TX_DISABLE_BMSK 0x8 +#define UARTDM_CR_RX_DISABLE_BMSK 0x2 +#define UARTDM_CR_TX_EN_BMSK 0x4 +#define UARTDM_CR_RX_EN_BMSK 0x1 + +/* UARTDM_CR channel_comman bit value (register field is bits 8:4) */ +#define RESET_RX 0x10 +#define RESET_TX 0x20 +#define RESET_ERROR_STATUS 0x30 +#define RESET_BREAK_INT 0x40 +#define START_BREAK 0x50 +#define STOP_BREAK 0x60 +#define RESET_CTS 0x70 +#define RESET_STALE_INT 0x80 +#define RFR_LOW 0xD0 +#define RFR_HIGH 0xE0 +#define CR_PROTECTION_EN 0x100 +#define STALE_EVENT_ENABLE 0x500 +#define STALE_EVENT_DISABLE 0x600 +#define FORCE_STALE_EVENT 0x400 +#define CLEAR_TX_READY 0x300 +#define RESET_TX_ERROR 0x800 +#define RESET_TX_DONE 0x810 + +#define UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK 0xffffff00 +#define UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK 0x3f +#define UARTDM_MR1_CTS_CTL_BMSK 0x40 +#define UARTDM_MR1_RX_RDY_CTL_BMSK 0x80 + +#define UARTDM_MR2_ERROR_MODE_BMSK 0x40 +#define UARTDM_MR2_BITS_PER_CHAR_BMSK 0x30 + +/* bits per character configuration */ +#define FIVE_BPC (0 << 4) +#define SIX_BPC (1 << 4) +#define SEVEN_BPC (2 << 4) +#define EIGHT_BPC (3 << 4) + +#define UARTDM_MR2_STOP_BIT_LEN_BMSK 0xc +#define STOP_BIT_ONE (1 << 2) +#define STOP_BIT_TWO (3 << 2) + +#define UARTDM_MR2_PARITY_MODE_BMSK 0x3 + +/* Parity configuration */ +#define NO_PARITY 0x0 +#define EVEN_PARITY 0x1 +#define ODD_PARITY 0x2 +#define SPACE_PARITY 0x3 + +#define UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK 0xffffff80 +#define UARTDM_IPR_STALE_LSB_BMSK 0x1f + +/* These can be used for both ISR and IMR register */ +#define UARTDM_ISR_CURRENT_CTS_BMSK 0x40 +#define UARTDM_ISR_DELTA_CTS_BMSK 0x20 +#define UARTDM_ISR_TX_READY_BMSK 0x80 +#define UARTDM_ISR_RXSTALE_BMSK 0x8 + +/* Field definitions for UART_DM_DMEN*/ +#define UARTDM_TX_DM_EN_BMSK 0x1 +#define UARTDM_RX_DM_EN_BMSK 0x2 + +#endif /* MSM_UART_HWREG_H */ From 811bf243985273e8204f042efb1309dc32102b9f Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Mon, 1 Dec 2008 22:41:17 -0800 Subject: [PATCH 0218/2556] [ARM] msm_serial_hs: Redo hs uart driver for MSM7k - Do not use workqueues - Use dma_sync instead of dma_map in rx and tx callbacks - Fix bug where last RX is repeated into tty on shutdown - Fix membase / mapbase mix-up - Fix shutdown and remove paths. Tested as module - UART2 now works - Use container_of and correct types instead of casts - Use msm_hs_ prefix for local functions and structs - Remove the non-static pm interface - Acquire uart spinlock in irq handler, dma callback and set_termios - Use waitqueue instead of completion - Use get_resource helpers - Remove orphanned Kconfig options - Use new clockname/device node key for clocks - Plenty more misc cleanup Signed-off-by: Nick Pelly --- drivers/tty/serial/Kconfig | 14 - drivers/tty/serial/msm_serial_hs.c | 1347 ++++++++++------------------ 2 files changed, 478 insertions(+), 883 deletions(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index b22f54a8176bc..a5628cedcf226 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1353,20 +1353,6 @@ config SERIAL_MSM_HS help Select this module to enable MSM high speed UART driver. -config SERIAL_MSM_HS_UART1 - bool "Enable High Speed UART1" - depends on SERIAL_MSM_HS - default y - help - This option enables the UART 1 High Speed port in MSM UART driver. - -config SERIAL_MSM_HS_UART2 - bool "Enable High Speed UART2" - depends on SERIAL_MSM_HS - default n - help - This option enables the UART 2 High Speed port in MSM UART driver. - config SERIAL_NETX tristate "NetX serial port support" depends on ARM && ARCH_NETX diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 305658e77c047..b94677222cb29 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -1,22 +1,23 @@ /* drivers/serial/msm_serial_hs.c * - * MSM High speed uart driver + * MSM 7k High speed uart driver * * Copyright (c) 2007-2008 QUALCOMM Incorporated. * Copyright (c) 2008 QUALCOMM USA, INC. - * + * Copyright (c) 2008 Nick Pelly + * * All source code in this file is licensed under the following license * except where indicated. - * + * * 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, you can find it at http://www.fsf.org */ @@ -29,288 +30,165 @@ #include #include #include +#include +#include #include #include #include #include #include #include -#include -#include +#include #include -#include #include #include #include #include #include "msm_serial_hs_hwreg.h" -#include "msm_serial_hs.h" -struct msm_uartdm_dmov_cb { - struct msm_dmov_cmd xfer; - struct msm_uartdm_port *msm_uport; +enum flush_reason { + FLUSH_NONE, + FLUSH_DATA_READY, + FLUSH_DATA_INVALID, /* values after this indicate invalid data */ + FLUSH_TERMIOS = FLUSH_DATA_INVALID, + FLUSH_STOP, + FLUSH_SHUTDOWN, }; -struct msm_uartdm_tx { +struct msm_hs_tx { unsigned int tx_ready_int_en; - struct msm_uartdm_dmov_cb xfer; - /* Simple command buffer. Allocated and freed by driver. */ + struct msm_dmov_cmd xfer; dmov_box *command_ptr; u32 *command_ptr_ptr; dma_addr_t mapped_cmd_ptr; - struct workqueue_struct *tx_wq; + dma_addr_t mapped_cmd_ptr_ptr; int tx_count; + dma_addr_t dma_base; }; -struct msm_uartdm_rx { - unsigned int rx_ready_int_en; - struct msm_uartdm_dmov_cb xfer; - /* Simple command buffer. Allocated and freed by driver. */ +struct msm_hs_rx { + enum flush_reason flush; + struct msm_dmov_cmd xfer; + dma_addr_t cmdptr_dmaaddr; dmov_box *command_ptr; u32 *command_ptr_ptr; dma_addr_t mapped_cmd_ptr; - struct workqueue_struct *rx_wq; - struct completion complete; + wait_queue_head_t wait; dma_addr_t rbuffer; unsigned char *buffer; - struct dma_pool *rx_pool; + struct dma_pool *pool; }; -struct msm_uartdm_port { - struct uart_port base; - unsigned long imr_reg; /* shadow value of UARTDM_IMR */ - struct clk *clk_id; +struct msm_hs_port { + struct uart_port uport; + unsigned long imr_reg; /* shadow value of UARTDM_IMR */ + struct clk *clk; atomic_t open_cnt; - struct msm_uartdm_tx tx_desc; - struct msm_uartdm_rx rx_desc; - struct work_struct rx_data_worker; - struct work_struct tx_data_worker; + struct msm_hs_tx tx; + struct msm_hs_rx rx; + int dma_tx_channel; int dma_rx_channel; int dma_tx_crci; int dma_rx_crci; }; -/* DM burst size (in bytes) (Constant) */ -#define MSM_UARTDM_BURST_SIZE 16 +#define MSM_UARTDM_BURST_SIZE 16 /* DM burst size (in bytes) */ #define UARTDM_TX_BUF_SIZE UART_XMIT_SIZE #define UARTDM_RX_BUF_SIZE 512 -/* cable status polling time value 250 msec */ -#define RX_GPIO_TIMER ((1000 * HZ)/1000) -/* RX Cable connect denounce */ -#define DEBOUNCE_RX_GPIO 3 - -#define UARTDM_MAX_COUNT 2 +#define UARTDM_NR 2 -static struct msm_uartdm_port q_uart_port[UARTDM_MAX_COUNT]; -static struct platform_driver msm_uartdm_pd; -static struct uart_driver msm_uartdm_driver; -static struct uart_ops msm_uartdm_ops; -static char *uartdm_clks[] = { "uart1dm_clk", "uart2dm_clk" }; +static struct msm_hs_port q_uart_port[UARTDM_NR]; +static struct platform_driver msm_serial_hs_platform_driver; +static struct uart_driver msm_hs_driver; +static struct uart_ops msm_hs_ops; -#if 1 -#define TRACE() do {} while (0) +#define UARTDM_TO_MSM(uart_port) \ + container_of((uart_port), struct msm_hs_port, uport) -#define TRACE_DEBUG(fmt,args...) do {} while (0) - -#define TRACE_ERR(fmt,args...) do {} while (0) -#else -#define TRACE() printk(KERN_ALERT "%s() :%d\n", __FUNCTION__ , __LINE__) - -#define TRACE_DEBUG(fmt,args...) \ - printk(KERN_ALERT "%s() :%d " fmt "\n", \ - __FUNCTION__ , __LINE__ , ##args) - -#define TRACE_ERR(fmt,args...) \ - printk(KERN_ERR "%s() :%d " fmt "\n", __FUNCTION__ , __LINE__ , ##args) -#endif - -static inline unsigned int uport_read32(struct uart_port *uport, - unsigned int offset) +static inline unsigned int msm_hs_read(struct uart_port *uport, + unsigned int offset) { - return (ioread32((void *)((unsigned int)uport->mapbase + offset))); + return ioread32(uport->membase + offset); } -static inline void uport_write32(struct uart_port *uport, unsigned int offset, +static inline void msm_hs_write(struct uart_port *uport, unsigned int offset, unsigned int value) { - iowrite32(value, (void *)((unsigned int)uport->mapbase + offset)); + iowrite32(value, uport->membase + offset); } -/* Stub routine, current not needed (Used for power management) */ -static int msm_uartdm_wake(struct uart_port *uport, unsigned int state) +static void msm_hs_release_port(struct uart_port *port) { - return 0; } -/* Stubbed for future uses. (Called when system issues shutdown command) */ -static void msm_uartdm_pd_shutdown(struct platform_device *pdev) +static int msm_hs_request_port(struct uart_port *port) { - TRACE(); + return 0; } -/* Called by msm_uart_remove to do port specific shutdown */ -static void msm_uartdm_remove_port(struct uart_port *uport) +static int __devexit msm_hs_remove(struct platform_device *pdev) { - struct msm_uartdm_port *msm_uport; - - msm_uport = (struct msm_uartdm_port *)uport; - - TRACE(); - uart_remove_one_port(&msm_uartdm_driver, uport); - clk_put(msm_uport->clk_id); + struct msm_hs_port *msm_uport; + struct device *dev; - /* Free the cable detect IRQ */ - free_irq(uport->irq, uport); - - /* Free the tx resources */ - kfree(msm_uport->tx_desc.command_ptr); - kfree(msm_uport->tx_desc.command_ptr_ptr); - - /* Free the rx resources */ - kfree(msm_uport->rx_desc.command_ptr); - kfree(msm_uport->rx_desc.command_ptr_ptr); - - dma_pool_free(msm_uport->rx_desc.rx_pool, msm_uport->rx_desc.buffer, - msm_uport->rx_desc.rbuffer); - dma_pool_destroy(msm_uport->rx_desc.rx_pool); -} - -/* - * When platform_driver_unregister() is called, this remove function of the - * driver will be called. - */ -static int msm_uartdm_remove(struct platform_device *pdev) -{ - int i; - struct uart_port *uport = NULL; - - for (i = 0; i < UARTDM_MAX_COUNT; i++) { - if (0 != q_uart_port[i].base.type) { - uport = (struct uart_port *)&(q_uart_port[i].base); - msm_uartdm_remove_port(uport); - }; + if (pdev->id < 0 || pdev->id >= UARTDM_NR) { + printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id); + return -EINVAL; } - return 0; -} + msm_uport = &q_uart_port[pdev->id]; + dev = msm_uport->uport.dev; -static void msm_uartdm_release_port(struct uart_port *port) -{ - iounmap((void *)(port->mapbase)); -} - -/* Called during system suspend to do port specific suspend */ -static void msm_uartdm_suspend_port(struct uart_port *uport) -{ - struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; - unsigned long data; - - /* ready to sleep */ - if (atomic_read(&(msm_uport->open_cnt))) { - data = uport_read32(uport, UARTDM_ISR_ADDR); - if (data & UARTDM_ISR_CURRENT_CTS_BMSK) { - data = uport_read32(uport, UARTDM_MR1_ADDR); - data &= ~(UARTDM_MR1_CTS_CTL_BMSK); - uport_write32(uport, UARTDM_MR1_ADDR, data); - } - } + dma_unmap_single(dev, msm_uport->rx.mapped_cmd_ptr, sizeof(dmov_box), + DMA_TO_DEVICE); + dma_pool_free(msm_uport->rx.pool, msm_uport->rx.buffer, + msm_uport->rx.rbuffer); + dma_pool_destroy(msm_uport->rx.pool); - /* ready to sleep */ - uart_suspend_port(&msm_uartdm_driver, uport); -} + dma_unmap_single(dev, msm_uport->rx.cmdptr_dmaaddr, sizeof(u32 *), + DMA_TO_DEVICE); + dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr_ptr, sizeof(u32 *), + DMA_TO_DEVICE); + dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr, sizeof(dmov_box), + DMA_TO_DEVICE); -/* - * This suspend function is called when interrupt is enabled. - * You can sleep or do something, but be short. - */ -static int msm_uartdm_suspend(struct platform_device *pdev, pm_message_t state) -{ - int i; - struct uart_port *uport = NULL; - TRACE(); - - for (i = 0; i < UARTDM_MAX_COUNT; i++) { - if (q_uart_port[i].base.type) { - uport = (struct uart_port *)&(q_uart_port[i].base); - msm_uartdm_suspend_port(uport); - }; - } + uart_remove_one_port(&msm_hs_driver, &msm_uport->uport); + clk_put(msm_uport->clk); - return 0; -} + /* Free the cable detect IRQ */ + free_irq(msm_uport->uport.irq, &msm_uport->uport); -/* - * This suspend_late function is called when interrupt is disabled. You can't - * sleep here. This is mainly used to finish the unfinished job from suspend - * function. - * - * Note: The time between suspend and suspend_late is undeterministic. - */ -static int msm_uartdm_suspend_late(struct platform_device *pdev, - pm_message_t state) -{ - return 0; -} + /* Free the tx resources */ + kfree(msm_uport->tx.command_ptr); + kfree(msm_uport->tx.command_ptr_ptr); -/* - * This resume_early function is called when interrupt is - * disabled. You can't sleep here. This is mainly used to turn - * on the things at a early time without being interrupted. - * - * Note: The time between resume_early and resume is undeterministic. - * - */ -static int msm_uartdm_resume_early(struct platform_device *pdev) -{ - return 0; -} + /* Free the rx resources */ + kfree(msm_uport->rx.command_ptr); + kfree(msm_uport->rx.command_ptr_ptr); -static int msm_uartdm_resume_port(struct uart_port *uport) -{ - uart_resume_port(&msm_uartdm_driver, uport); - return 0; -} + iounmap(msm_uport->uport.membase); -/* This resume function is called when interrupt is enabled. */ -static int msm_uartdm_resume(struct platform_device *pdev) -{ - int i; - struct uart_port *uport = NULL; - TRACE(); - - for (i = 0; i < UARTDM_MAX_COUNT; i++) { - if (q_uart_port[i].base.type) { - uport = (struct uart_port *)&(q_uart_port[i].base); - msm_uartdm_resume_port(uport); - }; - } return 0; } -static int msm_uartdm_ioctl(struct uart_port *uport, unsigned int cmd, - unsigned long arg) -{ - return -ENOIOCTLCMD; -} - -static int msm_uartdm_enable_clk(struct uart_port *uport) +static int msm_hs_enable_clk(struct uart_port *uport) { int ret; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); /* Set up the MREG/NREG/DREG/MNDREG */ - struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; - ret = clk_enable(msm_uport->clk_id); + ret = clk_enable(msm_uport->clk); if (ret) { printk(KERN_ERR "Error could not turn on UART clk\n"); return ret; } - ret = clk_set_rate(msm_uport->clk_id, uport->uartclk); + ret = clk_set_rate(msm_uport->clk, uport->uartclk); if (ret) { printk(KERN_WARNING "Error setting clock rate on UART\n"); return ret; @@ -319,23 +197,25 @@ static int msm_uartdm_enable_clk(struct uart_port *uport) return 0; } -static inline void msm_uartdm_disable_clk(struct uart_port *uport) +static inline void msm_hs_disable_clk(struct uart_port *uport) { - struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; - clk_disable(msm_uport->clk_id); + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + clk_disable(msm_uport->clk); } /* Enable and Disable clocks (Used for power management) */ -static void msm_uartdm_pm(struct uart_port *uport, unsigned int state, +static void msm_hs_pm(struct uart_port *uport, unsigned int state, unsigned int oldstate) { - if (state == 3) { - msm_uartdm_disable_clk(uport); - } else if (state == 0) { - if (msm_uartdm_enable_clk(uport)) { - /* Clock not enabled */ - BUG_ON(1); - } + switch (state) { + case 0: + msm_hs_enable_clk(uport); + break; + case 3: + msm_hs_disable_clk(uport); + break; + default: + printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state); } } @@ -348,71 +228,72 @@ static void msm_uartdm_pm(struct uart_port *uport, unsigned int state, * Goal is to have around 8 ms before indicate stale. * roundup (((Bit Rate * .008) / 10) + 1 */ -static void msm_uartdm_set_bps(struct uart_port *uport, unsigned int bps) +static void msm_hs_set_bps(struct uart_port *uport, + unsigned int bps) { - struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; unsigned long rxstale; unsigned long data; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); switch (bps) { case 300: - uport_write32(uport, UARTDM_CSR_ADDR, 0x00); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x00); rxstale = 1; break; case 600: - uport_write32(uport, UARTDM_CSR_ADDR, 0x11); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x11); rxstale = 1; break; case 1200: - uport_write32(uport, UARTDM_CSR_ADDR, 0x22); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x22); rxstale = 1; break; case 2400: - uport_write32(uport, UARTDM_CSR_ADDR, 0x33); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x33); rxstale = 1; break; case 4800: - uport_write32(uport, UARTDM_CSR_ADDR, 0x44); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x44); rxstale = 1; break; case 9600: - uport_write32(uport, UARTDM_CSR_ADDR, 0x55); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x55); rxstale = 2; break; case 14400: - uport_write32(uport, UARTDM_CSR_ADDR, 0x66); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x66); rxstale = 3; break; case 19200: - uport_write32(uport, UARTDM_CSR_ADDR, 0x77); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x77); rxstale = 4; break; case 28800: - uport_write32(uport, UARTDM_CSR_ADDR, 0x88); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x88); rxstale = 6; break; case 38400: - uport_write32(uport, UARTDM_CSR_ADDR, 0x99); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0x99); rxstale = 8; break; case 57600: - uport_write32(uport, UARTDM_CSR_ADDR, 0xaa); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xaa); rxstale = 16; break; case 76800: - uport_write32(uport, UARTDM_CSR_ADDR, 0xbb); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xbb); rxstale = 16; break; case 115200: - uport_write32(uport, UARTDM_CSR_ADDR, 0xcc); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xcc); rxstale = 31; break; case 230400: - uport_write32(uport, UARTDM_CSR_ADDR, 0xee); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xee); rxstale = 31; break; case 460800: - uport_write32(uport, UARTDM_CSR_ADDR, 0xff); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xff); rxstale = 31; break; case 4000000: @@ -425,11 +306,11 @@ static void msm_uartdm_set_bps(struct uart_port *uport, unsigned int bps) case 1152000: case 1000000: case 921600: - uport_write32(uport, UARTDM_CSR_ADDR, 0xff); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xff); rxstale = 31; break; default: - uport_write32(uport, UARTDM_CSR_ADDR, 0xff); + msm_hs_write(uport, UARTDM_CSR_ADDR, 0xff); /* default to 9600 */ bps = 9600; rxstale = 2; @@ -440,7 +321,7 @@ static void msm_uartdm_set_bps(struct uart_port *uport, unsigned int bps) } else { uport->uartclk = 7372800; } - if (clk_set_rate(msm_uport->clk_id, uport->uartclk)) { + if (clk_set_rate(msm_uport->clk, uport->uartclk)) { printk(KERN_WARNING "Error setting clock rate on UART\n"); return; } @@ -448,7 +329,7 @@ static void msm_uartdm_set_bps(struct uart_port *uport, unsigned int bps) data = rxstale & UARTDM_IPR_STALE_LSB_BMSK; data |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2); - uport_write32(uport, UARTDM_IPR_ADDR, data); + msm_hs_write(uport, UARTDM_IPR_ADDR, data); } /* @@ -457,16 +338,17 @@ static void msm_uartdm_set_bps(struct uart_port *uport, unsigned int bps) * * Configure the serial port */ -static void msm_uartdm_set_termios(struct uart_port *uport, +static void msm_hs_set_termios(struct uart_port *uport, struct ktermios *termios, struct ktermios *oldtermios) { unsigned int bps; unsigned long data; + unsigned long flags; unsigned int c_cflag = termios->c_cflag; - struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; - - TRACE(); + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + spin_lock_irqsave(&uport->lock, flags); /* 300 is the minimum baud support by the driver */ bps = uart_get_baud_rate(uport, termios, oldtermios, 200, 4000000); @@ -475,9 +357,9 @@ static void msm_uartdm_set_termios(struct uart_port *uport, if (bps == 200) bps = 3200000; - msm_uartdm_set_bps(uport, bps); + msm_hs_set_bps(uport, bps); - data = uport_read32(uport, UARTDM_MR2_ADDR); + data = msm_hs_read(uport, UARTDM_MR2_ADDR); data &= ~UARTDM_MR2_PARITY_MODE_BMSK; /* set parity */ if (PARENB == (c_cflag & PARENB)) { @@ -506,7 +388,6 @@ static void msm_uartdm_set_termios(struct uart_port *uport, default: data |= EIGHT_BPC; break; - } /* stop bits */ if (c_cflag & CSTOPB) { @@ -517,10 +398,10 @@ static void msm_uartdm_set_termios(struct uart_port *uport, } data |= UARTDM_MR2_ERROR_MODE_BMSK; /* write parity/bits per char/stop bit configuration */ - uport_write32(uport, UARTDM_MR2_ADDR, data); + msm_hs_write(uport, UARTDM_MR2_ADDR, data); /* Configure HW flow control */ - data = uport_read32(uport, UARTDM_MR1_ADDR); + data = msm_hs_read(uport, UARTDM_MR1_ADDR); data &= ~(UARTDM_MR1_CTS_CTL_BMSK | UARTDM_MR1_RX_RDY_CTL_BMSK); @@ -529,41 +410,43 @@ static void msm_uartdm_set_termios(struct uart_port *uport, data |= UARTDM_MR1_RX_RDY_CTL_BMSK; } - uport_write32(uport, UARTDM_MR1_ADDR, data); + msm_hs_write(uport, UARTDM_MR1_ADDR, data); uport->ignore_status_mask = termios->c_iflag & INPCK; uport->ignore_status_mask |= termios->c_iflag & IGNPAR; uport->read_status_mask = (termios->c_cflag & CREAD); - uport_write32(uport, UARTDM_IMR_ADDR, 0); + msm_hs_write(uport, UARTDM_IMR_ADDR, 0); /* Set Transmit software time out */ uart_update_timeout(uport, c_cflag, bps); - flush_workqueue(msm_uport->rx_desc.rx_wq); - uport_write32(uport, UARTDM_CR_ADDR, RESET_RX); - uport_write32(uport, UARTDM_CR_ADDR, RESET_TX); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX); - init_completion(&(msm_uport->rx_desc.complete)); - msm_dmov_flush(msm_uport->dma_rx_channel); + if (msm_uport->rx.flush == FLUSH_NONE) { + msm_uport->rx.flush = FLUSH_TERMIOS; + msm_dmov_flush(msm_uport->dma_rx_channel); + } - uport_write32(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + + spin_unlock_irqrestore(&uport->lock, flags); } /* * Standard API, Transmitter * Any character in the transmit shift register is sent */ -static unsigned int msm_uartdm_tx_empty(struct uart_port *uport) +static unsigned int msm_hs_tx_empty(struct uart_port *uport) { unsigned int data; - data = uport_read32(uport, UARTDM_SR_ADDR); - if (UARTDM_SR_TXEMT_BMSK == (data & UARTDM_SR_TXEMT_BMSK)) { + data = msm_hs_read(uport, UARTDM_SR_ADDR); + if (UARTDM_SR_TXEMT_BMSK == (data & UARTDM_SR_TXEMT_BMSK)) return TIOCSER_TEMT; - } else { - return 0; - } + + return 0; } /* @@ -571,12 +454,11 @@ static unsigned int msm_uartdm_tx_empty(struct uart_port *uport) * Any character in the transmit shift register is sent as * well as the current data mover transfer . */ -static void msm_uartdm_stop_tx(struct uart_port *uport) +static void msm_hs_stop_tx(struct uart_port *uport) { - struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - TRACE(); - msm_uport->tx_desc.tx_ready_int_en = 0; + msm_uport->tx.tx_ready_int_en = 0; } /* @@ -587,39 +469,30 @@ static void msm_uartdm_stop_tx(struct uart_port *uport) * of the receiver status bits are affected by this command and * characters that are already in the receive FIFO there. */ -static void msm_uartdm_stop_rx(struct uart_port *uport) +static void msm_hs_stop_rx(struct uart_port *uport) { - struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; - struct msm_uartdm_rx *rx_desc_ptr; - - TRACE(); - - rx_desc_ptr = &(msm_uport->rx_desc); + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); /* Disable the receiver */ - uport_write32(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_DISABLE_BMSK); - if (rx_desc_ptr->rx_ready_int_en == 1) { - init_completion(&(rx_desc_ptr->complete)); - rx_desc_ptr->rx_ready_int_en = 0; + msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_DISABLE_BMSK); + if (msm_uport->rx.flush == FLUSH_NONE) msm_dmov_flush(msm_uport->dma_rx_channel); - } + if (msm_uport->rx.flush != FLUSH_SHUTDOWN) + msm_uport->rx.flush = FLUSH_STOP; } /* Transmit the next chunk of data */ -static void msm_uartdm_submit_tx(struct uart_port *uport) +static void msm_hs_submit_tx(struct uart_port *uport) { - struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; - struct circ_buf *tx_buf = &uport->info->xmit; - int tx_count; int left; - struct msm_uartdm_tx *tx_desc_ptr; - struct msm_dmov_cmd *tx_xfer_ptr = NULL; - - tx_desc_ptr = &(msm_uport->tx_desc); + int tx_count; + dma_addr_t src_addr; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + struct msm_hs_tx *tx = &msm_uport->tx; + struct circ_buf *tx_buf = &msm_uport->uport.info->xmit; - TRACE(); if (uart_circ_empty(tx_buf) || uport->info->port.tty->stopped) { - msm_uartdm_stop_tx(uport); + msm_hs_stop_tx(uport); return; } @@ -633,181 +506,53 @@ static void msm_uartdm_submit_tx(struct uart_port *uport) if (tx_count > left) tx_count = left; + src_addr = tx->dma_base + tx_buf->tail; + dma_sync_single_for_device(uport->dev, src_addr, tx_count, + DMA_TO_DEVICE); - tx_desc_ptr->command_ptr->num_rows = (((tx_count + 15) >> 4) << 16) | - ((tx_count + 15) >> 4); - tx_desc_ptr->command_ptr->src_row_addr = - (u32) dma_map_single(NULL, &(tx_buf->buf [tx_buf->tail]), - tx_count, DMA_TO_DEVICE); + tx->command_ptr->num_rows = (((tx_count + 15) >> 4) << 16) | + ((tx_count + 15) >> 4); + tx->command_ptr->src_row_addr = src_addr; - tx_desc_ptr->mapped_cmd_ptr = dma_map_single(NULL, - tx_desc_ptr->command_ptr, - (sizeof(dmov_box)), - DMA_TO_DEVICE); + dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr, + sizeof(dmov_box), DMA_TO_DEVICE); - *(tx_desc_ptr->command_ptr_ptr) = CMD_PTR_LP | - DMOV_CMD_ADDR(tx_desc_ptr->mapped_cmd_ptr); + *tx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(tx->mapped_cmd_ptr); - tx_xfer_ptr = (struct msm_dmov_cmd *)&tx_desc_ptr->xfer; - - tx_xfer_ptr->cmdptr = DMOV_CMD_ADDR(dma_map_single(NULL, - tx_desc_ptr-> - command_ptr_ptr, - sizeof(u32 *), - DMA_TO_DEVICE)); + dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr, + sizeof(u32 *), DMA_TO_DEVICE); /* Save tx_count to use in Callback */ - tx_desc_ptr->tx_count = tx_count; - uport_write32(uport, UARTDM_NCF_TX_ADDR, tx_count); + tx->tx_count = tx_count; + msm_hs_write(uport, UARTDM_NCF_TX_ADDR, tx_count); /* Disable the tx_ready interrupt */ msm_uport->imr_reg &= ~UARTDM_ISR_TX_READY_BMSK; - uport_write32(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); - msm_dmov_enqueue_cmd(msm_uport->dma_tx_channel, tx_xfer_ptr); + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + msm_dmov_enqueue_cmd(msm_uport->dma_tx_channel, &tx->xfer); } /* Start to receive the next chunk of data */ -static void msm_uart_start_rx(struct uart_port *uport) +static void msm_hs_start_rx(struct uart_port *uport) { + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; - struct msm_uartdm_rx *rx_desc_ptr; - struct msm_dmov_cmd *rx_xfer_ptr; - - TRACE(); - rx_desc_ptr = &(msm_uport->rx_desc); - rx_desc_ptr->command_ptr->num_rows = ((UARTDM_RX_BUF_SIZE >> 4) << 16) | - (UARTDM_RX_BUF_SIZE >> 4); - - - rx_desc_ptr->command_ptr->dst_row_addr = rx_desc_ptr->rbuffer; + msm_uport->rx.flush = FLUSH_NONE; + msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel, &msm_uport->rx.xfer); - rx_desc_ptr->mapped_cmd_ptr = dma_map_single(NULL, - rx_desc_ptr->command_ptr, - (sizeof(dmov_box)), - DMA_TO_DEVICE); - - *(rx_desc_ptr->command_ptr_ptr) = CMD_PTR_LP | - DMOV_CMD_ADDR(rx_desc_ptr->mapped_cmd_ptr); - - rx_xfer_ptr = (struct msm_dmov_cmd *)&rx_desc_ptr->xfer; - rx_xfer_ptr->cmdptr = DMOV_CMD_ADDR(dma_map_single(NULL, - rx_desc_ptr-> - command_ptr_ptr, - sizeof(u32 *), - DMA_TO_DEVICE)); - - init_completion(&(rx_desc_ptr->complete)); - msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel, rx_xfer_ptr); - - uport_write32(uport, UARTDM_CR_ADDR, RESET_STALE_INT); - - uport_write32(uport, UARTDM_DMRX_ADDR, UARTDM_RX_BUF_SIZE); - uport_write32(uport, UARTDM_CR_ADDR, STALE_EVENT_ENABLE); -} - -/* - * This is a workqueue handler to complete DMA tx - * transactions and submit new transactions - */ -void msm_uart_tx_workqueue_handler(struct work_struct *work) -{ - struct msm_uartdm_port *msm_uport = NULL; - struct uart_port *uport = NULL; - struct msm_uartdm_tx *tx_desc_ptr = NULL; - struct circ_buf *tx_buf = NULL; - struct msm_dmov_cmd *tx_xfer_ptr = NULL; - - TRACE(); - msm_uport = container_of(work, struct msm_uartdm_port, tx_data_worker); - if (msm_uport) { - uport = (struct uart_port *)msm_uport; - tx_buf = &uport->info->xmit; - tx_desc_ptr = &(msm_uport->tx_desc); - - /* DMA is done un-map memmory addresses */ - tx_xfer_ptr = (struct msm_dmov_cmd *)&tx_desc_ptr->xfer; - dma_unmap_single(NULL, (dma_addr_t) tx_xfer_ptr->cmdptr, - sizeof(u32 *), DMA_TO_DEVICE); - - dma_unmap_single(NULL, tx_desc_ptr->mapped_cmd_ptr, - (sizeof(dmov_box)), DMA_TO_DEVICE); - - dma_unmap_single(NULL, - (dma_addr_t) tx_desc_ptr->command_ptr-> - src_row_addr, tx_desc_ptr->tx_count, - DMA_TO_DEVICE); - - tx_buf->tail = (tx_buf->tail + tx_desc_ptr->tx_count) & - (~UART_XMIT_SIZE); - - uport->icount.tx += tx_desc_ptr->tx_count; - if (msm_uport->tx_desc.tx_ready_int_en) - msm_uartdm_submit_tx(uport); - - if (uart_circ_chars_pending(tx_buf) < WAKEUP_CHARS) - uart_write_wakeup(uport); - } -} - -/* - * This is a workqueue handler to complete DMA rx - * transactions and start another rx transaction - */ -void msm_uart_rx_workqueue_handler(struct work_struct *work) -{ - struct msm_uartdm_port *msm_uport = NULL; - struct msm_uartdm_rx *rx_desc_ptr = NULL; - struct uart_port *uport = NULL; - struct tty_struct *tty = NULL; - struct msm_dmov_cmd *rx_xfer_ptr = NULL; - - int retval; - int rx_count; - - TRACE(); - - msm_uport = container_of(work, struct msm_uartdm_port, rx_data_worker); - if (msm_uport) { - uport = (struct uart_port *)msm_uport; - tty = uport->info->port.tty; - rx_desc_ptr = &(msm_uport->rx_desc); - rx_xfer_ptr = (struct msm_dmov_cmd *)&rx_desc_ptr->xfer; - - /* DMA is done un-map memmory addresses */ - dma_unmap_single(NULL, (dma_addr_t) rx_xfer_ptr->cmdptr, - sizeof(u32 *), DMA_TO_DEVICE); - - dma_unmap_single(NULL, rx_desc_ptr->mapped_cmd_ptr, - (sizeof(dmov_box)), DMA_TO_DEVICE); - - rx_count = uport_read32(uport, UARTDM_RX_TOTAL_SNAP_ADDR); - - /* Ignore Parity Errors */ - - if (0 != (uport->read_status_mask & CREAD)) { - retval = tty_insert_flip_string(tty, - rx_desc_ptr->buffer, - rx_count); - BUG_ON(retval != rx_count); - } - - tty_flip_buffer_push(tty); - - if (1 == rx_desc_ptr->rx_ready_int_en) - msm_uart_start_rx(uport); - complete(&(rx_desc_ptr->complete)); - } + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT); + msm_hs_write(uport, UARTDM_DMRX_ADDR, UARTDM_RX_BUF_SIZE); + msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_ENABLE); } /* Enable the transmitter Interrupt */ -static void msm_uartdm_start_tx(struct uart_port *uport) +static void msm_hs_start_tx(struct uart_port *uport) { - struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; - TRACE(); - if (0 == msm_uport->tx_desc.tx_ready_int_en) { - msm_uport->tx_desc.tx_ready_int_en = 1; - msm_uartdm_submit_tx(uport); + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + if (msm_uport->tx.tx_ready_int_en == 0) { + msm_uport->tx.tx_ready_int_en = 1; + msm_hs_submit_tx(uport); } } @@ -818,27 +563,18 @@ static void msm_uartdm_start_tx(struct uart_port *uport) * up a Data Mover transfer. It is called from Data mover ISR * when the DMA transfer is done. */ -static void msm_uartdm_dmov_tx_callback(struct msm_dmov_cmd *cmd_ptr, +static void msm_hs_dmov_tx_callback(struct msm_dmov_cmd *cmd_ptr, unsigned int result, struct msm_dmov_errdata *err) { - struct msm_uartdm_dmov_cb *xfer; - struct msm_uartdm_port *msm_uport; - struct uart_port *uport; - struct msm_uartdm_tx *tx_desc_ptr; + struct msm_hs_port *msm_uport; - xfer = (struct msm_uartdm_dmov_cb *)cmd_ptr; - msm_uport = xfer->msm_uport; - uport = (struct uart_port *)msm_uport; + WARN_ON(result != 0x80000002); /* DMA did not finish properly */ - TRACE(); - tx_desc_ptr = &(msm_uport->tx_desc); - - /* DMA did not finish properly error out */ - BUG_ON(result != 0x80000002); + msm_uport = container_of(cmd_ptr, struct msm_hs_port, tx.xfer); msm_uport->imr_reg |= UARTDM_ISR_TX_READY_BMSK; - uport_write32(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + msm_hs_write(&msm_uport->uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); } /* @@ -848,31 +584,30 @@ static void msm_uartdm_dmov_tx_callback(struct msm_dmov_cmd *cmd_ptr, * This routine is registered with Data mover when we set up a Data Mover * transfer. It is called from Data mover ISR when the DMA transfer is done. */ -static void msm_uartdm_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, +static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, unsigned int result, struct msm_dmov_errdata *err) { - struct msm_uartdm_dmov_cb *xfer; - struct msm_uartdm_port *msm_uport; - struct uart_port *uport; - struct msm_uartdm_rx *rx_desc_ptr; - struct tty_struct *tty; - unsigned long status; int retval; + int rx_count; + unsigned long status; unsigned int error_f = 0; + unsigned long flags; + unsigned int flush; + struct tty_struct *tty; + struct uart_port *uport; + struct msm_hs_port *msm_uport; - TRACE(); + msm_uport = container_of(cmd_ptr, struct msm_hs_port, rx.xfer); + uport = &msm_uport->uport; - xfer = (struct msm_uartdm_dmov_cb *)cmd_ptr; - msm_uport = xfer->msm_uport; - uport = (struct uart_port *)msm_uport; - tty = uport->info->port.tty; + spin_lock_irqsave(&uport->lock, flags); - rx_desc_ptr = &(msm_uport->rx_desc); + tty = uport->info->port.tty; //npell info->tty ?? - uport_write32(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); + msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); - status = uport_read32(uport, UARTDM_SR_ADDR); + status = msm_hs_read(uport, UARTDM_SR_ADDR); /* overflow is not connect to data in a FIFO */ if (unlikely((status & UARTDM_SR_OVERRUN_BMSK) && @@ -883,10 +618,10 @@ static void msm_uartdm_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, } if (!(uport->ignore_status_mask & INPCK)) - status = status & (~UARTDM_SR_PAR_FRAME_BMSK); + status = status & ~(UARTDM_SR_PAR_FRAME_BMSK); if (unlikely(status & UARTDM_SR_PAR_FRAME_BMSK)) { - /*Can not tell difference between parity & frame error */ + /* Can not tell difference between parity & frame error */ uport->icount.parity++; error_f = 1; if (uport->ignore_status_mask & IGNPAR) @@ -894,12 +629,35 @@ static void msm_uartdm_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, } if (error_f) - uport_write32(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS); + + flush = msm_uport->rx.flush; + if (flush == FLUSH_TERMIOS) + msm_hs_start_rx(uport); + if (flush == FLUSH_STOP) + msm_uport->rx.flush = FLUSH_SHUTDOWN; + if (flush >= FLUSH_DATA_INVALID) { + spin_unlock_irqrestore(&uport->lock, flags); + return; + } + + rx_count = msm_hs_read(uport, UARTDM_RX_TOTAL_SNAP_ADDR); + + if (0 != (uport->read_status_mask & CREAD)) { + retval = tty_insert_flip_string(tty, msm_uport->rx.buffer, + rx_count); + BUG_ON(retval != rx_count); + } + + msm_hs_start_rx(uport); - retval = queue_work(rx_desc_ptr->rx_wq, &(msm_uport->rx_data_worker)); + /* + * Drop the lock here since tty_flip_buffer_push() might end + * up calling uart_start(), which takes the lock. + */ + spin_unlock_irqrestore(&uport->lock, flags); - /* Failed to queue DMA transfer */ - BUG_ON(0 == retval); + tty_flip_buffer_push(tty); } /* @@ -915,12 +673,9 @@ static void msm_uartdm_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, * - TIOCM_RI * (Unsupported) DCD and DSR will return them high. RI will return low. */ -static unsigned int msm_uartdm_get_mctrl(struct uart_port *uport) +static unsigned int msm_hs_get_mctrl(struct uart_port *uport) { - unsigned int ret; - - ret = TIOCM_DSR | TIOCM_CAR | TIOCM_CTS; - return ret; + return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS; } /* @@ -930,45 +685,37 @@ static unsigned int msm_uartdm_get_mctrl(struct uart_port *uport) * ready for receiving and then set RFR_N high. To set RFR to low we just turn * back auto ready for receiving and it should lower RFR signal * when hardware is ready - * */ -static void msm_uartdm_set_mctrl(struct uart_port *uport, unsigned int mctrl) +static void msm_hs_set_mctrl(struct uart_port *uport, unsigned int mctrl) { unsigned int set_rts; - unsigned int data = 0; - - TRACE(); + unsigned int data; /* RTS is active low */ + set_rts = TIOCM_RTS & mctrl ? 0 : 1; - if (TIOCM_RTS & mctrl) { - set_rts = 0; - } else { - set_rts = 1; - } - - data = uport_read32(uport, UARTDM_MR1_ADDR); + data = msm_hs_read(uport, UARTDM_MR1_ADDR); if (set_rts) { /*disable auto ready-for-receiving */ data &= ~UARTDM_MR1_RX_RDY_CTL_BMSK; - uport_write32(uport, UARTDM_MR1_ADDR, data); + msm_hs_write(uport, UARTDM_MR1_ADDR, data); /* set RFR_N to high */ - uport_write32(uport, UARTDM_CR_ADDR, RFR_HIGH); + msm_hs_write(uport, UARTDM_CR_ADDR, RFR_HIGH); } else { /* Enable auto ready-for-receiving */ data |= UARTDM_MR1_RX_RDY_CTL_BMSK; - uport_write32(uport, UARTDM_MR1_ADDR, data); + msm_hs_write(uport, UARTDM_MR1_ADDR, data); } } /* Standard API, Enable modem status (CTS) interrupt */ -static void msm_uartdm_enable_ms(struct uart_port *uport) +static void msm_hs_enable_ms(struct uart_port *uport) { - struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); /* Enable DELTA_CTS Interrupt */ msm_uport->imr_reg |= UARTDM_ISR_DELTA_CTS_BMSK; - uport_write32(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); } /* @@ -977,75 +724,85 @@ static void msm_uartdm_enable_ms(struct uart_port *uport) * Control the transmission of a break signal. ctl eq 0 => break * signal terminate ctl ne 0 => start break signal */ -static void msm_uartdm_break_ctl(struct uart_port *uport, int ctl) +static void msm_hs_break_ctl(struct uart_port *uport, int ctl) { - if (ctl) { - uport_write32(uport, UARTDM_CR_ADDR, START_BREAK); - } else { - uport_write32(uport, UARTDM_CR_ADDR, STOP_BREAK); - } + msm_hs_write(uport, UARTDM_CR_ADDR, ctl ? START_BREAK : STOP_BREAK); } -/* Need to be non-zero value or port will not init. */ -static void msm_uartdm_config_port(struct uart_port *uport, int flags) +static void msm_hs_config_port(struct uart_port *uport, int flags) { - /* Set port type to any non-zero value */ - if (flags & UART_CONFIG_TYPE) - uport->type = 1; + if (flags & UART_CONFIG_TYPE) { + uport->type = PORT_MSM; + msm_hs_request_port(uport); + } } /* Handle CTS changes (Called from interrupt handler) */ -static void msm_uartdm_handle_delta_cts(struct uart_port *uport) +static void msm_hs_handle_delta_cts(struct uart_port *uport) { /* clear interrupt */ - uport_write32(uport, UARTDM_CR_ADDR, RESET_CTS); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_CTS); uport->icount.cts++; /* clear the IOCTL TIOCMIWAIT if called */ wake_up_interruptible(&uport->info->delta_msr_wait); } -static irqreturn_t msm_uartdm_isr(int irq, void *dev) +static irqreturn_t msm_hs_isr(int irq, void *dev) { - struct uart_port *uport = (struct uart_port *)dev; + unsigned long flags; unsigned long isr_status; - struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; - u32 retval; - struct msm_uartdm_tx *tx_desc_ptr; - tx_desc_ptr = &(msm_uport->tx_desc); + struct msm_hs_port *msm_uport = (struct msm_hs_port *)dev; + struct uart_port *uport = &msm_uport->uport; + struct circ_buf *tx_buf = &uport->info->xmit; + struct msm_hs_tx *tx = &msm_uport->tx; + struct msm_hs_rx *rx = &msm_uport->rx; - isr_status = uport_read32(uport, UARTDM_MISR_ADDR); + spin_lock_irqsave(&uport->lock, flags); + + isr_status = msm_hs_read(uport, UARTDM_MISR_ADDR); /* Mask UART interrupts */ disable_irq(uport->irq); - TRACE(); /* Stale rx interrupt */ if (isr_status & UARTDM_ISR_RXSTALE_BMSK) { - uport_write32(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); - uport_write32(uport, UARTDM_CR_ADDR, RESET_STALE_INT); + msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT); - msm_dmov_flush(msm_uport->dma_rx_channel); + if (rx->flush == FLUSH_NONE) { + rx->flush = FLUSH_DATA_READY; + msm_dmov_flush(msm_uport->dma_rx_channel); + } } /* tx ready interrupt */ if (isr_status & UARTDM_ISR_TX_READY_BMSK) { - /*Clear TX Ready */ - uport_write32(uport, UARTDM_CR_ADDR, CLEAR_TX_READY); - retval = queue_work(tx_desc_ptr->tx_wq, - &(msm_uport->tx_data_worker)); - BUG_ON(1 != retval); + /* Clear TX Ready */ + msm_hs_write(uport, UARTDM_CR_ADDR, CLEAR_TX_READY); + + /* Complete DMA TX transactions and submit new transactions */ + tx_buf->tail = (tx_buf->tail + tx->tx_count) & ~UART_XMIT_SIZE; + + uport->icount.tx += tx->tx_count; + if (tx->tx_ready_int_en) + msm_hs_submit_tx(uport); + + if (uart_circ_chars_pending(tx_buf) < WAKEUP_CHARS) + uart_write_wakeup(uport); } /* Change in CTS interrupt */ if (isr_status & UARTDM_ISR_DELTA_CTS_BMSK) - msm_uartdm_handle_delta_cts(uport); + msm_hs_handle_delta_cts(uport); enable_irq(uport->irq); + spin_unlock_irqrestore(&uport->lock, flags); + return IRQ_HANDLED; } -static const char *msm_uartdm_type(struct uart_port *port) +static const char *msm_hs_type(struct uart_port *port) { - return ("MSM UART"); + return ("MSM HS UART"); } /* @@ -1055,344 +812,255 @@ static const char *msm_uartdm_type(struct uart_port *port) * - Hook the ISR * - Enable Receive Interrupt */ -static int msm_uartdm_startup(struct uart_port *uport) +static int msm_hs_startup(struct uart_port *uport) { int rfr_level; unsigned int data; - struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; - struct msm_uartdm_tx *tx_desc_ptr; - struct msm_uartdm_rx *rx_desc_ptr; - struct msm_dmov_cmd *tx_xfer_ptr; - struct msm_dmov_cmd *rx_xfer_ptr; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + struct circ_buf *tx_buf = &uport->info->xmit; + struct msm_hs_tx *tx = &msm_uport->tx; + struct msm_hs_rx *rx = &msm_uport->rx; - TRACE(); atomic_inc(&(msm_uport->open_cnt)); - tx_desc_ptr = &(msm_uport->tx_desc); - rx_desc_ptr = &(msm_uport->rx_desc); + rfr_level = uport->fifosize; + if (rfr_level > 16) + rfr_level -= 16; - if (uport->fifosize > 16) { - rfr_level = uport->fifosize - 16; - } else { - rfr_level = uport->fifosize; - } + tx->dma_base = dma_map_single(uport->dev, tx_buf->buf, UART_XMIT_SIZE, + DMA_TO_DEVICE); /* Set auto RFR Level */ - data = uport_read32(uport, UARTDM_MR1_ADDR); - data &= (~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK); - data &= (~UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK); + data = msm_hs_read(uport, UARTDM_MR1_ADDR); + data &= ~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK; + data &= ~UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK; data |= (UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK & (rfr_level << 2)); data |= (UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK & rfr_level); - uport_write32(uport, UARTDM_MR1_ADDR, data); + msm_hs_write(uport, UARTDM_MR1_ADDR, data); /* Make sure RXSTALE count is non-zero */ - data = uport_read32(uport, UARTDM_IPR_ADDR); + data = msm_hs_read(uport, UARTDM_IPR_ADDR); if (!data) { data |= 0x1f & UARTDM_IPR_STALE_LSB_BMSK; - uport_write32(uport, UARTDM_IPR_ADDR, data); + msm_hs_write(uport, UARTDM_IPR_ADDR, data); } /* Enable Data Mover Mode */ data = UARTDM_TX_DM_EN_BMSK | UARTDM_RX_DM_EN_BMSK; - uport_write32(uport, UARTDM_DMEN_ADDR, data); + msm_hs_write(uport, UARTDM_DMEN_ADDR, data); /* Reset TX */ - uport_write32(uport, UARTDM_CR_ADDR, RESET_TX); - - uport_write32(uport, UARTDM_CR_ADDR, RESET_RX); - uport_write32(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS); - uport_write32(uport, UARTDM_CR_ADDR, RESET_BREAK_INT); - uport_write32(uport, UARTDM_CR_ADDR, RESET_STALE_INT); - uport_write32(uport, UARTDM_CR_ADDR, RESET_CTS); - uport_write32(uport, UARTDM_CR_ADDR, RFR_LOW); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_BREAK_INT); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT); + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_CTS); + msm_hs_write(uport, UARTDM_CR_ADDR, RFR_LOW); /* Turn on Uart Receiver */ - uport_write32(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_EN_BMSK); + msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_EN_BMSK); /* Turn on Uart Transmitter */ - uport_write32(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_EN_BMSK); + msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_EN_BMSK); /* Initialize the tx */ - tx_desc_ptr->tx_ready_int_en = 0; + tx->tx_ready_int_en = 0; - tx_desc_ptr->xfer.msm_uport = msm_uport; - tx_xfer_ptr = (struct msm_dmov_cmd *)&tx_desc_ptr->xfer; - tx_xfer_ptr->complete_func = msm_uartdm_dmov_tx_callback; + tx->xfer.complete_func = msm_hs_dmov_tx_callback; - tx_desc_ptr->command_ptr->cmd = CMD_LC | + tx->command_ptr->cmd = CMD_LC | CMD_DST_CRCI(msm_uport->dma_tx_crci) | CMD_MODE_BOX; - tx_desc_ptr->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16) - | (MSM_UARTDM_BURST_SIZE); + tx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16) + | (MSM_UARTDM_BURST_SIZE); - tx_desc_ptr->command_ptr->row_offset = (MSM_UARTDM_BURST_SIZE << 16); + tx->command_ptr->row_offset = (MSM_UARTDM_BURST_SIZE << 16); - tx_desc_ptr->command_ptr->dst_row_addr = MSM_UART1DM_PHYS + UARTDM_TF_ADDR; + tx->command_ptr->dst_row_addr = + msm_uport->uport.mapbase + UARTDM_TF_ADDR; /* Turn on Uart Receive */ + rx->xfer.complete_func = msm_hs_dmov_rx_callback; - rx_desc_ptr->xfer.msm_uport = msm_uport; - rx_xfer_ptr = (struct msm_dmov_cmd *)&rx_desc_ptr->xfer; - rx_xfer_ptr->complete_func = msm_uartdm_dmov_rx_callback; - - rx_desc_ptr->command_ptr->cmd = CMD_LC | + rx->command_ptr->cmd = CMD_LC | CMD_SRC_CRCI(msm_uport->dma_rx_crci) | CMD_MODE_BOX; - rx_desc_ptr->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16) - | (MSM_UARTDM_BURST_SIZE); - rx_desc_ptr->command_ptr->src_row_addr = MSM_UART1DM_PHYS + UARTDM_RF_ADDR; - - rx_desc_ptr->command_ptr->row_offset = MSM_UARTDM_BURST_SIZE; - - rx_desc_ptr->command_ptr->src_row_addr = (unsigned int)uport->membase + - UARTDM_RF_ADDR; - - - rx_desc_ptr->rx_wq = create_singlethread_workqueue("rx_wq"); - INIT_WORK(&(msm_uport->rx_data_worker), msm_uart_rx_workqueue_handler); - - tx_desc_ptr->tx_wq = create_singlethread_workqueue("tx_wq"); - INIT_WORK(&(msm_uport->tx_data_worker), msm_uart_tx_workqueue_handler); - - rx_desc_ptr->rx_ready_int_en = 1; + rx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16) + | (MSM_UARTDM_BURST_SIZE); + rx->command_ptr->row_offset = MSM_UARTDM_BURST_SIZE; + rx->command_ptr->src_row_addr = uport->mapbase + UARTDM_RF_ADDR; msm_uport->imr_reg |= UARTDM_ISR_RXSTALE_BMSK; /* Enable reading the current CTS, no harm even if CTS is ignored */ msm_uport->imr_reg |= UARTDM_ISR_CURRENT_CTS_BMSK; + msm_hs_start_rx(uport); enable_irq(uport->irq); - uport_write32(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); - - msm_uart_start_rx(uport); + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); return 0; } -/* Initializes uart_port data structure and adds the port to the driver */ -int uartdm_init_port(struct uart_driver *udriver, struct uart_port *uport, - unsigned long base_addr, unsigned long phy_base_addr, - unsigned long irq_num, unsigned long fifo_size, char *desc) +/* Initialize tx and rx data structures */ +static int uartdm_init_port(struct uart_port *uport) { - int ret; - struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; - struct msm_uartdm_tx *tx_desc_ptr; - struct msm_uartdm_rx *rx_desc_ptr; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + struct msm_hs_tx *tx = &msm_uport->tx; + struct msm_hs_rx *rx = &msm_uport->rx; - TRACE(); - - atomic_set(&(msm_uport->open_cnt), 0); - rx_desc_ptr = &(msm_uport->rx_desc); - tx_desc_ptr = &(msm_uport->tx_desc); - uport->iotype = UPIO_MEM; - uport->irq = irq_num; - uport->fifosize = fifo_size; - - uport->membase = (void *)phy_base_addr; - /* base_addr is a virtual address */ - uport->mapbase = base_addr; - - uport->ops = &msm_uartdm_ops; - uport->flags = UPF_BOOT_AUTOCONF; - ((struct msm_uartdm_port *)uport)->imr_reg = 0x0; + /* Allocate the command pointer. Needs to be 64 bit aligned */ + tx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA); - /* Perform the hardware initialization */ + tx->command_ptr_ptr = kmalloc(sizeof(u32 *), GFP_KERNEL | __GFP_DMA); - msm_uport->clk_id = clk_get(NULL, desc); + if (!tx->command_ptr || !tx->command_ptr_ptr) + return -ENOMEM; - ret = msm_uartdm_enable_clk(uport); - if (ret) { - printk(KERN_ERR "Error could not enable clk"); - return ret; - } + tx->mapped_cmd_ptr = dma_map_single(uport->dev, tx->command_ptr, + sizeof(dmov_box), DMA_TO_DEVICE); + tx->mapped_cmd_ptr_ptr = dma_map_single(uport->dev, + tx->command_ptr_ptr, + sizeof(u32 *), DMA_TO_DEVICE); + tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr); - ret = request_irq(uport->irq, msm_uartdm_isr, - (IRQF_TRIGGER_HIGH), "msm_uartdm", uport); + init_waitqueue_head(&rx->wait); - /* irq will be enabled when port is opened */ - disable_irq(uport->irq); + rx->pool = dma_pool_create("rx_buffer_pool", uport->dev, + UARTDM_RX_BUF_SIZE, 16, 0); - /* Failed to Hook ISR error Out */ - if (ret) { - printk(KERN_ERR "Failed to hook up main ISR \n"); - return ret; - } + rx->buffer = dma_pool_alloc(rx->pool, GFP_KERNEL, &rx->rbuffer); /* Allocate the command pointer. Needs to be 64 bit aligned */ - tx_desc_ptr->command_ptr = kmalloc((sizeof(dmov_box)), - GFP_KERNEL | __GFP_DMA); + rx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA); - tx_desc_ptr->command_ptr_ptr = kmalloc((sizeof(void *)), - GFP_KERNEL | __GFP_DMA); + rx->command_ptr_ptr = kmalloc(sizeof(u32 *), GFP_KERNEL | __GFP_DMA); - if ((tx_desc_ptr->command_ptr == NULL) || - (tx_desc_ptr->command_ptr_ptr == NULL)) + if (!rx->command_ptr || !rx->command_ptr_ptr || !rx->pool || + !rx->buffer) return -ENOMEM; - rx_desc_ptr->rx_pool = dma_pool_create("rx_buffer_pool", NULL, - UARTDM_RX_BUF_SIZE, 16, 0); + rx->command_ptr->num_rows = ((UARTDM_RX_BUF_SIZE >> 4) << 16) | + (UARTDM_RX_BUF_SIZE >> 4); - rx_desc_ptr->buffer = dma_pool_alloc(rx_desc_ptr->rx_pool, GFP_KERNEL, - &(rx_desc_ptr->rbuffer)); + rx->command_ptr->dst_row_addr = rx->rbuffer; - /* Allocate the command pointer. Needs to be 64 bit aligned */ - rx_desc_ptr->command_ptr = kmalloc((sizeof(dmov_box)), - GFP_KERNEL | __GFP_DMA); + rx->mapped_cmd_ptr = dma_map_single(uport->dev, rx->command_ptr, + sizeof(dmov_box), DMA_TO_DEVICE); - rx_desc_ptr->command_ptr_ptr = kmalloc((sizeof(void *)), - GFP_KERNEL | __GFP_DMA); + *rx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(rx->mapped_cmd_ptr); - if ((rx_desc_ptr->command_ptr == NULL) || - (rx_desc_ptr->command_ptr_ptr == NULL) || - (rx_desc_ptr->rx_pool == NULL) || (rx_desc_ptr->buffer == NULL)) - return -ENOMEM; + rx->cmdptr_dmaaddr = dma_map_single(uport->dev, rx->command_ptr_ptr, + sizeof(u32 *), DMA_TO_DEVICE); + rx->xfer.cmdptr = DMOV_CMD_ADDR(rx->cmdptr_dmaaddr); - /* configure the CR Protection to Enable */ - uport_write32(uport, UARTDM_CR_ADDR, CR_PROTECTION_EN); - - msm_uartdm_disable_clk(uport); - - ret = uart_add_one_port(udriver, uport); - if (ret) { - printk(KERN_ERR "uartdm_init_port Failed to add port \n"); - return ret; - } return 0; } -static int msm_uartdm_probe(struct platform_device *pdev) +static int __init msm_hs_probe(struct platform_device *pdev) { - int ret = 0; - int i; + int ret; struct uart_port *uport; - struct msm_uartdm_port *msm_uport; - unsigned int base_addr = 0; - unsigned long phy_base_addr = 0; - - /* Interrupt from UARTDM controller */ - unsigned int uartdm_irq = 0; - - TRACE_DEBUG("plaform device ID = %d", pdev->id); - - if (pdev->id != 1) { - TRACE_ERR(" Invalid plaform device ID = %d\n", pdev->id); + struct msm_hs_port *msm_uport; + struct resource *resource; + if (pdev->id < 0 || pdev->id >= UARTDM_NR) { + printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id); return -EINVAL; } - if ((pdev->resource == NULL) || (pdev->num_resources != 4)) { - TRACE_ERR(" Invalid num of resources pdev->resource = %u" - "pdev->num_resources = %d \n", - (unsigned int)(pdev->resource), pdev->num_resources); - - return -ENXIO; - } - - /* Init all UARTS as non-configured */ - for (i = 0; i < UARTDM_MAX_COUNT; i++) { - q_uart_port[i].base.type = 0; - - q_uart_port[i].dma_tx_channel = 0; - q_uart_port[i].dma_rx_channel = 0; + msm_uport = &q_uart_port[pdev->id]; + uport = &msm_uport->uport; - q_uart_port[i].dma_tx_crci = 0; - q_uart_port[i].dma_rx_crci = 0; + uport->dev = &pdev->dev; - /* Default Clock */ - q_uart_port[i].base.uartclk = 7372800; - } + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!resource)) + return -ENXIO; + uport->mapbase = resource->start; /* virtual address */ - uport = (struct uart_port *)&(q_uart_port[pdev->id - 1]); - msm_uport = &(q_uart_port[pdev->id - 1]); + uport->membase = ioremap(uport->mapbase, PAGE_SIZE); + if (unlikely(!uport->membase)) + return -ENOMEM; - for (i = 0; i < pdev->num_resources; i++) { + uport->irq = platform_get_irq(pdev, 0); + if (unlikely(uport->irq < 0)) + return -ENXIO; - if (pdev->resource[i].flags & IORESOURCE_MEM) { + resource = platform_get_resource_byname(pdev, IORESOURCE_DMA, + "uartdm_channels"); + if (unlikely(!resource)) + return -ENXIO; + msm_uport->dma_tx_channel = resource->start; + msm_uport->dma_rx_channel = resource->end; + + resource = platform_get_resource_byname(pdev, IORESOURCE_DMA, + "uartdm_crci"); + if (unlikely(!resource)) + return -ENXIO; + msm_uport->dma_tx_crci = resource->start; + msm_uport->dma_rx_crci = resource->end; - phy_base_addr = pdev->resource[i].start; - base_addr = - (unsigned int)ioremap(phy_base_addr, PAGE_SIZE); - if (!base_addr) { - TRACE_ERR("No Memory\n"); - return -ENOMEM; - } - TRACE_DEBUG("phy_base_addr = %08x," - " virt base_addr = %08x ", - phy_base_addr, base_addr); + uport->iotype = UPIO_MEM; + uport->fifosize = 64; + uport->ops = &msm_hs_ops; + uport->flags = UPF_BOOT_AUTOCONF; + uport->uartclk = 7372800; + msm_uport->imr_reg = 0x0; + msm_uport->clk = clk_get(&pdev->dev, "uartdm_clk"); + if (IS_ERR(msm_uport->clk)) + return PTR_ERR(msm_uport->clk); - } - if (pdev->resource[i].flags & IORESOURCE_IRQ) { - uartdm_irq = pdev->resource[i].start; + atomic_set(&msm_uport->open_cnt, 0); - TRACE_DEBUG("uartdm_irq = %d ", uartdm_irq); - } - if (pdev->resource[i].flags & IORESOURCE_DMA) { - if (!strcmp(pdev->resource[i].name, "uartdm_chanels")) { - msm_uport->dma_tx_channel = - pdev->resource[i].start; - - msm_uport->dma_rx_channel = - pdev->resource[i].end; - - TRACE_DEBUG("dma_tx_channel = %d," - " dma_rx_channel = %d ", - msm_uport->dma_tx_channel, - msm_uport->dma_rx_channel); - } - if (!strcmp(pdev->resource[i].name, "uartdm_crci")) { - msm_uport->dma_tx_crci = - pdev->resource[i].start; - msm_uport->dma_rx_crci = pdev->resource[i].end; - - TRACE_DEBUG("dma_tx_crci = %d, " - "dma_rx_crci= %d ", - msm_uport->dma_tx_crci, - msm_uport->dma_rx_crci); - } - } - } + ret = msm_hs_enable_clk(uport); + if (unlikely(ret)) + return ret; - if (!uartdm_irq || !msm_uport->dma_tx_channel || - !msm_uport->dma_rx_channel || !msm_uport->dma_tx_crci || - !msm_uport->dma_rx_crci) { + ret = request_irq(uport->irq, msm_hs_isr, IRQF_TRIGGER_HIGH, + "msm_uartdm", msm_uport); + if (unlikely(ret)) + return ret; - TRACE_ERR("Invalid irq, or DM channels or DM CRCIs"); - return -ENXIO; - } + disable_irq(uport->irq); /* irq will be enabled when port is opened */ - /* FIFO size for UARTDM is 64 */ + ret = uartdm_init_port(uport); + if (unlikely(ret)) + return ret; - uport->dev = &(pdev->dev); + /* configure the CR Protection to Enable */ + msm_hs_write(uport, UARTDM_CR_ADDR, CR_PROTECTION_EN); - ret = uartdm_init_port(&msm_uartdm_driver, uport, base_addr, - phy_base_addr, uartdm_irq, 64, - uartdm_clks[pdev->id - 1]); + msm_hs_disable_clk(uport); - if (ret != 0) { - printk(KERN_ERR "msm_uartdm_probe failed on port %d \n", - pdev->id); - uart_unregister_driver(&msm_uartdm_driver); - return ret; - } + uport->line = pdev->id; + ret = uart_add_one_port(&msm_hs_driver, uport); return ret; } -static int __init msm_uartdm_init(void) +static int __init msm_serial_hs_init(void) { - int ret = 0; - TRACE(); - ret = uart_register_driver(&msm_uartdm_driver); - if (ret) { - printk(KERN_ERR "msm_uart_init failed to load module \n"); + int ret; + int i; + + /* Init all UARTS as non-configured */ + for (i = 0; i < UARTDM_NR; i++) + q_uart_port[i].uport.type = PORT_UNKNOWN; + + ret = uart_register_driver(&msm_hs_driver); + if (unlikely(ret)) { + printk(KERN_ERR "%s failed to load\n", __FUNCTION__); return ret; } - ret = platform_driver_register(&msm_uartdm_pd); + ret = platform_driver_register(&msm_serial_hs_platform_driver); if (ret) { - printk(KERN_ERR "msm_uart_init failed to load module \n"); - uart_unregister_driver(&msm_uartdm_driver); + printk(KERN_ERR "%s failed to load\n", __FUNCTION__); + uart_unregister_driver(&msm_hs_driver); return ret; } - printk(KERN_INFO "msm_uart_hs module loaded!\n"); + + printk(KERN_INFO "msm_serial_hs module loaded\n"); return ret; } @@ -1401,132 +1069,73 @@ static int __init msm_uartdm_init(void) * - Disables the port * - Unhook the ISR */ -static void msm_uartdm_shutdown(struct uart_port *uport) +static void msm_hs_shutdown(struct uart_port *uport) { - struct msm_uartdm_port *msm_uport = (struct msm_uartdm_port *)uport; - struct msm_uartdm_rx *rx_desc_ptr; - struct msm_uartdm_tx *tx_desc_ptr; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - rx_desc_ptr = &(msm_uport->rx_desc); + BUG_ON(msm_uport->rx.flush < FLUSH_STOP); - tx_desc_ptr = &(msm_uport->tx_desc); - TRACE(); - - atomic_dec(&(msm_uport->open_cnt)); + atomic_dec(&msm_uport->open_cnt); /* Disable the transmitter */ - uport_write32(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_DISABLE_BMSK); + msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_DISABLE_BMSK); /* Disable the receiver */ - uport_write32(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_DISABLE_BMSK); + msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_DISABLE_BMSK); /* Free the interrupt */ disable_irq(uport->irq); msm_uport->imr_reg = 0; - uport_write32(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); - wait_for_completion(&rx_desc_ptr->complete); - flush_workqueue(msm_uport->rx_desc.rx_wq); - destroy_workqueue(rx_desc_ptr->rx_wq); - - flush_workqueue(msm_uport->tx_desc.tx_wq); - destroy_workqueue(tx_desc_ptr->tx_wq); -} + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); -static void __exit msm_uartdm_exit(void) -{ - printk(KERN_INFO "msm_uart_hs module removed!\n"); - platform_driver_unregister(&msm_uartdm_pd); + wait_event(msm_uport->rx.wait, msm_uport->rx.flush == FLUSH_SHUTDOWN); - uart_unregister_driver(&msm_uartdm_driver); + dma_unmap_single(msm_uport->uport.dev, msm_uport->tx.dma_base, + UART_XMIT_SIZE, DMA_TO_DEVICE); } -/* for ttyHS0 pass 0 as line parameter */ -unsigned int msm_uart_hs_tx_empty(int line) +static void __exit msm_serial_hs_exit(void) { - struct uart_port *uport; - unsigned int data; - uport = (struct uart_port *)&(q_uart_port[line]); - - data = uport_read32(uport, UARTDM_SR_ADDR); - if (UARTDM_SR_TXEMT_BMSK == (data & UARTDM_SR_TXEMT_BMSK)) { - return 1; - } else { - return 0; - } + printk(KERN_INFO "msm_serial_hs module removed\n"); + platform_driver_unregister(&msm_serial_hs_platform_driver); + uart_unregister_driver(&msm_hs_driver); } -EXPORT_SYMBOL(msm_uart_hs_tx_empty); - -/* - * To be used by a kernel module so that it can turn UART on and off to save - * power.It is up to the upper layer to manage if it is correct to do. - */ -void msm_uart_hs_safe_pm(int line, enum msm_uart_hs_pm_e mode) -{ - struct uart_port *uport; - uport = (struct uart_port *)&(q_uart_port[line]); - switch (mode) { - case MSM_UART_HS_POWER_DOWN: - disable_irq(uport->irq); - msm_uartdm_disable_clk(uport); - break; - case MSM_UART_HS_POWER_UP: - if (msm_uartdm_enable_clk(uport)) - BUG_ON(1); - enable_irq(uport->irq); - break; - default: - break; - } -} -EXPORT_SYMBOL(msm_uart_hs_safe_pm); - -static struct platform_driver msm_uartdm_pd = { - .probe = msm_uartdm_probe, - .remove = msm_uartdm_remove, - .suspend = msm_uartdm_suspend, - .suspend_late = msm_uartdm_suspend_late, - .resume_early = msm_uartdm_resume_early, - .resume = msm_uartdm_resume, - .shutdown = msm_uartdm_pd_shutdown, +static struct platform_driver msm_serial_hs_platform_driver = { + .probe = msm_hs_probe, + .remove = msm_hs_remove, .driver = { - /* Driver name must match the device name used - in platform_devices for uarts_hs devices . - */ .name = "msm_serial_hs", }, }; -static struct uart_driver msm_uartdm_driver = { +static struct uart_driver msm_hs_driver = { .owner = THIS_MODULE, - .driver_name = "msm_uartdm", + .driver_name = "msm_serial_hs", .dev_name = "ttyHS", - .major = 0, - .minor = 0, - .nr = UARTDM_MAX_COUNT, + .nr = UARTDM_NR, .cons = 0, }; -static struct uart_ops msm_uartdm_ops = { - .tx_empty = msm_uartdm_tx_empty, - .set_mctrl = msm_uartdm_set_mctrl, - .get_mctrl = msm_uartdm_get_mctrl, - .stop_tx = msm_uartdm_stop_tx, - .start_tx = msm_uartdm_start_tx, - .stop_rx = msm_uartdm_stop_rx, - .enable_ms = msm_uartdm_enable_ms, - .break_ctl = msm_uartdm_break_ctl, - .startup = msm_uartdm_startup, - .shutdown = msm_uartdm_shutdown, - .set_termios = msm_uartdm_set_termios, - .pm = msm_uartdm_pm, - .set_wake = msm_uartdm_wake, - .type = msm_uartdm_type, - .config_port = msm_uartdm_config_port, - .ioctl = msm_uartdm_ioctl, - .release_port = msm_uartdm_release_port, +static struct uart_ops msm_hs_ops = { + .tx_empty = msm_hs_tx_empty, + .set_mctrl = msm_hs_set_mctrl, + .get_mctrl = msm_hs_get_mctrl, + .stop_tx = msm_hs_stop_tx, + .start_tx = msm_hs_start_tx, + .stop_rx = msm_hs_stop_rx, + .enable_ms = msm_hs_enable_ms, + .break_ctl = msm_hs_break_ctl, + .startup = msm_hs_startup, + .shutdown = msm_hs_shutdown, + .set_termios = msm_hs_set_termios, + .pm = msm_hs_pm, + .type = msm_hs_type, + .config_port = msm_hs_config_port, + .release_port = msm_hs_release_port, + .request_port = msm_hs_request_port, }; -module_init(msm_uartdm_init); -module_exit(msm_uartdm_exit); +module_init(msm_serial_hs_init); +module_exit(msm_serial_hs_exit); MODULE_DESCRIPTION("High Speed UART Driver for the MSM chipset"); -MODULE_VERSION("1.0"); +MODULE_VERSION("1.1"); MODULE_LICENSE("GPL v2"); From c293646009ee0554d3d7e7dc744b5a87e08797df Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Tue, 18 Nov 2008 20:48:17 -0500 Subject: [PATCH 0219/2556] trout: Support for msm_serial_hs driver. Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/board-trout.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index d3abe2b8e3683..66c12da9b0faa 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -569,6 +569,10 @@ static struct platform_device *devices[] __initdata = { #if !defined(CONFIG_MSM_SERIAL_DEBUGGER) && !defined(CONFIG_TROUT_H2W) &msm_device_uart3, #endif +#ifdef CONFIG_SERIAL_MSM_HS + &msm_device_uart_dm1, + &msm_device_uart_dm2, +#endif #ifdef CONFIG_USB_FUNCTION &msm_device_hsusb, &usb_mass_storage_device, @@ -625,10 +629,17 @@ static void trout_reset(void) static uint32_t gpio_table[] = { /* BLUETOOTH */ +#ifdef CONFIG_SERIAL_MSM_HS + PCOM_GPIO_CFG(43, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RTS */ + PCOM_GPIO_CFG(44, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* CTS */ + PCOM_GPIO_CFG(45, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RX */ + PCOM_GPIO_CFG(46, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* TX */ +#else PCOM_GPIO_CFG(43, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RTS */ PCOM_GPIO_CFG(44, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* CTS */ PCOM_GPIO_CFG(45, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RX */ PCOM_GPIO_CFG(46, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* TX */ +#endif }; From a139082a661a439ceaed79619aa28230784b5f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 21 Nov 2008 21:40:42 -0800 Subject: [PATCH 0220/2556] [ARM] msm: trout: Add reset key combo. Reset on SEND-MENU-END if the trackball is not pressed. --- arch/arm/mach-msm/board-trout.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 66c12da9b0faa..c7d23968160cc 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include <../../../drivers/staging/android/timed_gpio.h> @@ -184,6 +185,26 @@ static struct platform_device trout_nav_device = { }, }; +static int trout_reset_keys_up[] = { + BTN_MOUSE, + 0 +}; + +static struct keyreset_platform_data trout_reset_keys_pdata = { + .keys_up = trout_reset_keys_up, + .keys_down = { + KEY_SEND, + KEY_MENU, + KEY_END, + 0 + }, +}; + +struct platform_device trout_reset_keys_device = { + .name = KEYRESET_NAME, + .dev.platform_data = &trout_reset_keys_pdata, +}; + static int trout_ts_power(int on) { int tp_ls_gpio = system_rev < 5 ? TROUT_4_TP_LS_EN : TROUT_5_TP_LS_EN; @@ -578,6 +599,7 @@ static struct platform_device *devices[] __initdata = { &usb_mass_storage_device, #endif &trout_nav_device, + &trout_reset_keys_device, &android_leds, &sd_door_switch, &android_timed_gpios, From b3bd5b179ecc0c51507f22c366e68bf3a05e886c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 2 Dec 2008 19:39:51 -0800 Subject: [PATCH 0221/2556] [ARM] msm: Setup default cpld values before writing them. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a bug where low level serial debug on uart3 did not work between trout_init_gpio and trout_init. Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/board-trout-gpio.c | 33 +++++++++++++++++++++++----- arch/arm/mach-msm/board-trout.c | 20 ----------------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/arch/arm/mach-msm/board-trout-gpio.c b/arch/arm/mach-msm/board-trout-gpio.c index 7e331b0974b76..d1caedb8de610 100644 --- a/arch/arm/mach-msm/board-trout-gpio.c +++ b/arch/arm/mach-msm/board-trout-gpio.c @@ -27,8 +27,20 @@ #include "board-trout.h" +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "board_trout." + +static uint cpld_usb_h2w_sw; +module_param_named(usb_h2w_sw, cpld_usb_h2w_sw, uint, 0); + static uint8_t trout_cpld_shadow[4] = { +#if defined(CONFIG_MSM_DEBUG_UART1) + /* H2W pins <-> UART1 */ [0] = 0x40, // for serial debug, low current +#else + /* H2W pins <-> UART3, Bluetooth <-> UART1 */ + [0] = 0x80, // for serial debug, low current +#endif [1] = 0x04, // I2C_PULL [3] = 0x04, // mmdi 32k en }; @@ -66,10 +78,20 @@ static void update_pwrsink(unsigned gpio, unsigned on) } } +static uint8_t trout_gpio_write_shadow(unsigned n, unsigned on) +{ + uint8_t b = 1U << (n & 7); + int reg = (n & 0x78) >> 2; // assumes base is 128 + + if(on) + return trout_cpld_shadow[reg >> 1] |= b; + else + return trout_cpld_shadow[reg >> 1] &= ~b; +} + static void trout_gpio_set(struct gpio_chip *chip, unsigned offset, int on) { unsigned n = chip->base + offset; - uint8_t b = 1U << (n & 7); int reg = (n & 0x78) >> 2; // assumes base is 128 unsigned long flags; uint8_t reg_val; @@ -81,10 +103,7 @@ static void trout_gpio_set(struct gpio_chip *chip, unsigned offset, int on) local_irq_save(flags); update_pwrsink(n, on); - if(on) - reg_val = trout_cpld_shadow[reg >> 1] |= b; - else - reg_val = trout_cpld_shadow[reg >> 1] &= ~b; + reg_val = trout_gpio_write_shadow(n, on); writeb(reg_val, TROUT_CPLD_BASE + reg); local_irq_restore(flags); } @@ -255,6 +274,10 @@ static int __init trout_init_gpio(void) if (!machine_is_trout()) return 0; + /* adjust GPIOs based on bootloader request */ + pr_info("trout_init_gpio: cpld_usb_hw2_sw = %d\n", cpld_usb_h2w_sw); + trout_gpio_write_shadow(TROUT_GPIO_USB_H2W_SW, cpld_usb_h2w_sw); + for(i = 0; i < ARRAY_SIZE(trout_cpld_shadow); i++) writeb(trout_cpld_shadow[i], TROUT_CPLD_BASE + i * 2); diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index c7d23968160cc..5ba886f0bc699 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -624,14 +624,8 @@ static void __init trout_init_irq(void) msm_init_irq(); } -static uint cpld_iset; -static uint cpld_charger_en; -static uint cpld_usb_h2w_sw; static uint opt_disable_uart3; -module_param_named(iset, cpld_iset, uint, 0); -module_param_named(charger_en, cpld_charger_en, uint, 0); -module_param_named(usb_h2w_sw, cpld_usb_h2w_sw, uint, 0); module_param_named(disable_uart3, opt_disable_uart3, uint, 0); static int __init trout_serialno_setup(char *str) @@ -755,10 +749,6 @@ static void __init trout_init(void) msm_acpu_clock_init(&trout_clock_data); - /* adjust GPIOs based on bootloader request */ - printk("trout_init: cpld_usb_hw2_sw = %d\n", cpld_usb_h2w_sw); - gpio_set_value(TROUT_GPIO_USB_H2W_SW, cpld_usb_h2w_sw); - #if defined(CONFIG_MSM_SERIAL_DEBUGGER) if (!opt_disable_uart3) msm_serial_debug_init(MSM_UART3_PHYS, INT_UART3, @@ -767,16 +757,6 @@ static void __init trout_init(void) /* gpio_configure(108, IRQF_TRIGGER_LOW); */ -#if defined(CONFIG_LL_DEBUG_UART1) - /* H2W pins <-> UART1 */ - gpio_set_value(TROUT_GPIO_H2W_SEL0, 1); - gpio_set_value(TROUT_GPIO_H2W_SEL1, 0); -#else - /* H2W pins <-> UART3, Bluetooth <-> UART1 */ - gpio_set_value(TROUT_GPIO_H2W_SEL0, 0); - gpio_set_value(TROUT_GPIO_H2W_SEL1, 1); -#endif - /* put the AF VCM in powerdown mode to avoid noise */ gpio_set_value(TROUT_GPIO_VCM_PWDN, 1); mdelay(100); From 3774387839c0b18b843be392f70769ec850684e8 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Mon, 1 Dec 2008 22:22:13 -0800 Subject: [PATCH 0222/2556] [ARM] msm: Fix bugs preventing modular build Signed-off-by: Nick Pelly --- arch/arm/mach-msm/dma.c | 2 +- arch/arm/mach-msm/io.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/dma.c b/arch/arm/mach-msm/dma.c index 4b1f5293439ed..cfb78c407a3ed 100644 --- a/arch/arm/mach-msm/dma.c +++ b/arch/arm/mach-msm/dma.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #define MSM_DMOV_CHANNEL_COUNT 16 @@ -285,4 +286,3 @@ static int __init msm_init_datamover(void) } arch_initcall(msm_init_datamover); - diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c index a3f343003e4a9..4ef6faa8565d3 100644 --- a/arch/arm/mach-msm/io.c +++ b/arch/arm/mach-msm/io.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include From 17f8d96531f09231fb5c70801a1c8816ff73b9b8 Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Wed, 3 Dec 2008 11:06:41 -0800 Subject: [PATCH 0223/2556] [ARM] msm: clock: Support for freq table in speed selection. Change/disable full range of frequencies from /sys in userspace. Signed-off-by: Mike Chan --- arch/arm/mach-msm/cpufreq.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c index 821d2be89fd38..2e5f119160a05 100644 --- a/arch/arm/mach-msm/cpufreq.c +++ b/arch/arm/mach-msm/cpufreq.c @@ -52,25 +52,21 @@ static int msm_cpufreq_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) { - unsigned int freq; - switch(relation) { - /* Lowest value at or above target frequency. */ - case CPUFREQ_RELATION_L: - /* Highest value at or below target frequency. */ - case CPUFREQ_RELATION_H: - if (target_freq < CONFIG_MSM_CPU_FREQ_ONDEMAND_MAX) - freq = CONFIG_MSM_CPU_FREQ_ONDEMAND_MIN; - else - freq = CONFIG_MSM_CPU_FREQ_ONDEMAND_MAX; - break; - default: - return -EINVAL; + int index; + struct cpufreq_frequency_table *table = + cpufreq_frequency_get_table(smp_processor_id()); + + if (cpufreq_frequency_table_target(policy, table, target_freq, relation, + &index)) { + pr_err("cpufreq: invalid target_freq: %d\n", target_freq); + return -EINVAL; } + #ifdef CONFIG_CPU_FREQ_DEBUG printk("msm_cpufreq_target %d r %d (%d-%d) selected %d\n", target_freq, - relation, policy->min, policy->max, freq); + relation, policy->min, policy->max, table[index].frequency); #endif - acpuclk_set_rate(freq * 1000, 0); + acpuclk_set_rate(table[index].frequency * 1000, 0); return 0; } @@ -83,11 +79,17 @@ return 0; static int __init msm_cpufreq_init(struct cpufreq_policy *policy) { + struct cpufreq_frequency_table *table = + cpufreq_frequency_get_table(smp_processor_id()); + policy->cur = acpuclk_get_rate(); + if (cpufreq_frequency_table_cpuinfo(policy, table)) { + policy->cpuinfo.min_freq = CONFIG_MSM_CPU_FREQ_ONDEMAND_MIN; + policy->cpuinfo.max_freq = CONFIG_MSM_CPU_FREQ_ONDEMAND_MAX; + } policy->min = CONFIG_MSM_CPU_FREQ_ONDEMAND_MIN; policy->max = CONFIG_MSM_CPU_FREQ_ONDEMAND_MAX; - policy->cpuinfo.min_freq = CONFIG_MSM_CPU_FREQ_ONDEMAND_MIN; - policy->cpuinfo.max_freq = CONFIG_MSM_CPU_FREQ_ONDEMAND_MAX; + policy->cpuinfo.transition_latency = acpuclk_get_switch_time() * NSEC_PER_USEC; return 0; From 4c7642a21f652fdd8592feb3b14732df2fc70c7b Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 3 Dec 2008 20:08:31 -0800 Subject: [PATCH 0224/2556] [ARM] msm: trout: Remove uart2dm from board file (its not in use). Signed-off-by: Nick Pelly --- arch/arm/mach-msm/board-trout.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 5ba886f0bc699..f9d8cb97af51d 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -592,7 +592,6 @@ static struct platform_device *devices[] __initdata = { #endif #ifdef CONFIG_SERIAL_MSM_HS &msm_device_uart_dm1, - &msm_device_uart_dm2, #endif #ifdef CONFIG_USB_FUNCTION &msm_device_hsusb, From 31be38b7726084062927abf2c39fab9051379f2b Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 1 Dec 2008 13:01:03 -0500 Subject: [PATCH 0225/2556] [ARM] msm: msm72k_udc: gadget driver for msm7k Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/include/mach/board.h | 2 +- drivers/usb/gadget/Kconfig | 18 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/gadget_chips.h | 8 + drivers/usb/gadget/msm72k_udc.c | 1503 ++++++++++++++++++++++++ drivers/usb/gadget/msm72k_udc.h | 189 +++ 6 files changed, 1720 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/gadget/msm72k_udc.c create mode 100644 drivers/usb/gadget/msm72k_udc.h diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index 1036ecfeda515..a4df72b5e64b4 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -52,7 +52,7 @@ int __init msm_add_sdcc(unsigned int controller, struct msm_mmc_platform_data *plat, unsigned int stat_irq, unsigned long stat_irq_flags); -#if defined(CONFIG_USB_FUNCTION_MSM_HSUSB) +#if defined(CONFIG_USB_FUNCTION_MSM_HSUSB) || defined(CONFIG_USB_MSM_72K) void msm_hsusb_set_vbus_state(int online); #else static inline void msm_hsusb_set_vbus_state(int online) {} diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 1eeae810a65ae..ad68beef36e4d 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -563,6 +563,24 @@ config USB_CI13XXX_MSM # LAST -- dummy/emulated controller # +config USB_GADGET_MSM_72K + boolean "MSM 72K Device Controller" + depends on ARCH_MSM + select USB_GADGET_SELECTED + select USB_GADGET_DUALSPEED + help + USB gadget driver for Qualcomm MSM 72K architecture. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "msm72k" and force all + gadget drivers to also be dynamically linked. + +config USB_MSM_72K + tristate + depends on USB_GADGET_MSM_72K + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_DUMMY_HCD boolean "Dummy HCD (DEVELOPMENT)" depends on USB=y || (USB=m && USB_GADGET=m) diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index aa4669477a0d3..cab00d4733d75 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_USB_EG20T) += pch_udc.o obj-$(CONFIG_USB_PXA_U2O) += mv_udc.o mv_udc-y := mv_udc_core.o mv_udc_phy.o obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o +obj-$(CONFIG_USB_MSM_72K) += msm72k_udc.o # # USB gadget drivers diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index 5c2720d64ffa4..4d1412f259371 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -126,6 +126,12 @@ #define gadget_is_ci13xxx_pci(g) 0 #endif +#ifdef CONFIG_USB_GADGET_MSM_72K +#define gadget_is_msm72k(g) !strcmp("msm72k_udc", (g)->name) +#else +#define gadget_is_msm72k(g) 0 +#endif + // CONFIG_USB_GADGET_SX2 // CONFIG_USB_GADGET_AU1X00 // ... @@ -215,6 +221,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x27; else if (gadget_is_ci13xxx_msm(gadget)) return 0x28; + else if (gadget_is_msm72k(gadget)) + return 0x29; return -ENOENT; } diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c new file mode 100644 index 0000000000000..a2f1afc7c280a --- /dev/null +++ b/drivers/usb/gadget/msm72k_udc.c @@ -0,0 +1,1503 @@ +/* + * Driver for HighSpeed USB Client Controller in MSM7K + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * Brian Swetland + * + * 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 + +static const char driver_name[] = "msm72k_udc"; + +/* #define DEBUG */ +/* #define VERBOSE */ +#include "msm72k_udc.h" + +#define MSM_USB_BASE ((unsigned) ui->addr) + +#define DRIVER_DESC "MSM 72K USB Peripheral Controller" + +#define EPT_FLAG_IN 0x0001 + +#define SETUP_BUF_SIZE 4096 + + +static const char *const ep_name[] = { + "ep0out", "ep1out", "ep2out", "ep3out", + "ep4out", "ep5out", "ep6out", "ep7out", + "ep8out", "ep9out", "ep10out", "ep11out", + "ep12out", "ep13out", "ep14out", "ep15out", + "ep0in", "ep1in", "ep2in", "ep3in", + "ep4in", "ep5in", "ep6in", "ep7in", + "ep8in", "ep9in", "ep10in", "ep11in", + "ep12in", "ep13in", "ep14in", "ep15in" +}; + +/* current state of VBUS */ +static int vbus; + +struct msm_request { + struct usb_request req; + + /* saved copy of req.complete */ + void (*gadget_complete)(struct usb_ep *ep, + struct usb_request *req); + + + struct usb_info *ui; + struct msm_request *next; + + unsigned busy:1; + unsigned live:1; + unsigned alloced:1; + unsigned dead:1; + + dma_addr_t dma; + dma_addr_t item_dma; + + struct ept_queue_item *item; +}; + +#define to_msm_request(r) container_of(r, struct msm_request, req) +#define to_msm_endpoint(r) container_of(r, struct msm_endpoint, ep) + +struct msm_endpoint { + struct usb_ep ep; + struct usb_info *ui; + struct msm_request *req; /* head of pending requests */ + struct msm_request *last; + unsigned flags; + + /* bit number (0-31) in various status registers + ** as well as the index into the usb_info's array + ** of all endpoints + */ + unsigned char bit; + unsigned char num; + + /* pointers to DMA transfer list area */ + /* these are allocated from the usb_info dma space */ + struct ept_queue_head *head; +}; + +static void usb_do_work(struct work_struct *w); + + +#define USB_STATE_IDLE 0 +#define USB_STATE_ONLINE 1 +#define USB_STATE_OFFLINE 2 + +#define USB_FLAG_START 0x0001 +#define USB_FLAG_VBUS_ONLINE 0x0002 +#define USB_FLAG_VBUS_OFFLINE 0x0004 +#define USB_FLAG_RESET 0x0008 + +struct usb_info { + /* lock for register/queue/device state changes */ + spinlock_t lock; + + /* single request used for handling setup transactions */ + struct usb_request *setup_req; + + struct platform_device *pdev; + int irq; + void *addr; + + unsigned state; + unsigned flags; + + unsigned online:1; + unsigned running:1; + + struct dma_pool *pool; + + /* dma page to back the queue heads and items */ + unsigned char *buf; + dma_addr_t dma; + + struct ept_queue_head *head; + + /* used for allocation */ + unsigned next_item; + unsigned next_ifc_num; + + /* endpoints are ordered based on their status bits, + ** so they are OUT0, OUT1, ... OUT15, IN0, IN1, ... IN15 + */ + struct msm_endpoint ept[32]; + + int *phy_init_seq; + void (*phy_reset)(void); + + struct work_struct work; + unsigned phy_status; + unsigned phy_fail_count; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + +#define ep0out ept[0] +#define ep0in ept[16] + + struct clk *clk; + struct clk *pclk; +}; + +static const struct usb_ep_ops msm72k_ep_ops; + + +static void flush_endpoint(struct msm_endpoint *ept); + +#if 0 +static unsigned ulpi_read(struct usb_info *ui, unsigned reg) +{ + unsigned timeout = 100000; + + /* initiate read operation */ + writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), + USB_ULPI_VIEWPORT); + + /* wait for completion */ + while ((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout)) + ; + + if (timeout == 0) { + ERROR("ulpi_read: timeout %08x\n", readl(USB_ULPI_VIEWPORT)); + return 0xffffffff; + } + return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT)); +} +#endif + +static void ulpi_write(struct usb_info *ui, unsigned val, unsigned reg) +{ + unsigned timeout = 10000; + + /* initiate write operation */ + writel(ULPI_RUN | ULPI_WRITE | + ULPI_ADDR(reg) | ULPI_DATA(val), + USB_ULPI_VIEWPORT); + + /* wait for completion */ + while ((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout)) + ; + + if (timeout == 0) + ERROR("ulpi_write: timeout\n"); +} + +static void ulpi_init(struct usb_info *ui) +{ + int *seq = ui->phy_init_seq; + + if (!seq) + return; + + while (seq[0] >= 0) { + INFO("ulpi: write 0x%02x to 0x%02x\n", seq[0], seq[1]); + ulpi_write(ui, seq[0], seq[1]); + seq += 2; + } +} + +static void init_endpoints(struct usb_info *ui) +{ + unsigned n; + + for (n = 0; n < 32; n++) { + struct msm_endpoint *ept = ui->ept + n; + + ept->ui = ui; + ept->bit = n; + ept->num = n & 15; + ept->ep.name = ep_name[n]; + ept->ep.ops = &msm72k_ep_ops; + + if (ept->bit > 15) { + /* IN endpoint */ + ept->head = ui->head + (ept->num << 1) + 1; + ept->flags = EPT_FLAG_IN; + } else { + /* OUT endpoint */ + ept->head = ui->head + (ept->num << 1); + ept->flags = 0; + } + + } +} + +static void configure_endpoints(struct usb_info *ui) +{ + unsigned n; + unsigned cfg; + + for (n = 0; n < 32; n++) { + struct msm_endpoint *ept = ui->ept + n; + + cfg = CONFIG_MAX_PKT(ept->ep.maxpacket) | CONFIG_ZLT; + + if (ept->bit == 0) + /* ep0 out needs interrupt-on-setup */ + cfg |= CONFIG_IOS; + + ept->head->config = cfg; + ept->head->next = TERMINATE; + + if (ept->ep.maxpacket) + INFO("ept #%d %s max:%d head:%p bit:%d\n", + ept->num, + (ept->flags & EPT_FLAG_IN) ? "in" : "out", + ept->ep.maxpacket, ept->head, ept->bit); + } +} + +struct usb_request *usb_ept_alloc_req(struct msm_endpoint *ept, + unsigned bufsize, gfp_t gfp_flags) +{ + struct usb_info *ui = ept->ui; + struct msm_request *req; + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + goto fail1; + + req->item = dma_pool_alloc(ui->pool, gfp_flags, &req->item_dma); + if (!req->item) + goto fail2; + + if (bufsize) { + req->req.buf = kmalloc(bufsize, gfp_flags); + if (!req->req.buf) + goto fail3; + req->alloced = 1; + } + + return &req->req; + +fail3: + dma_pool_free(ui->pool, req->item, req->item_dma); +fail2: + kfree(req); +fail1: + return 0; +} + +static void do_free_req(struct usb_info *ui, struct msm_request *req) +{ + if (req->alloced) + kfree(req->req.buf); + + dma_pool_free(ui->pool, req->item, req->item_dma); + kfree(req); +} + + +static void usb_ept_enable(struct msm_endpoint *ept, int yes) +{ + struct usb_info *ui = ept->ui; + int in = ept->flags & EPT_FLAG_IN; + unsigned n; + + n = readl(USB_ENDPTCTRL(ept->num)); + + if (in) { + n = (n & (~CTRL_TXT_MASK)) | CTRL_TXT_BULK; + if (yes) + n |= CTRL_TXE | CTRL_TXR; + else + n &= (~CTRL_TXE); + } else { + n = (n & (~CTRL_RXT_MASK)) | CTRL_RXT_BULK; + if (yes) + n |= CTRL_RXE | CTRL_RXR; + else + n &= ~(CTRL_RXE); + } + writel(n, USB_ENDPTCTRL(ept->num)); + +#if 1 + INFO("ept %d %s %s\n", + ept->num, in ? "in" : "out", yes ? "enabled" : "disabled"); +#endif +} + +static void usb_ept_start(struct msm_endpoint *ept) +{ + struct usb_info *ui = ept->ui; + struct msm_request *req = ept->req; + + BUG_ON(req->live); + + /* link the hw queue head to the request's transaction item */ + ept->head->next = req->item_dma; + ept->head->info = 0; + + /* start the endpoint */ + writel(1 << ept->bit, USB_ENDPTPRIME); + + /* mark this chain of requests as live */ + while (req) { + req->live = 1; + req = req->next; + } +} + +int usb_ept_queue_xfer(struct msm_endpoint *ept, struct usb_request *_req) +{ + unsigned long flags; + struct msm_request *req = to_msm_request(_req); + struct msm_request *last; + struct usb_info *ui = ept->ui; + struct ept_queue_item *item = req->item; + unsigned length = req->req.length; + + if (length > 0x4000) + return -EMSGSIZE; + + spin_lock_irqsave(&ui->lock, flags); + + if (req->busy) { + req->req.status = -EBUSY; + spin_unlock_irqrestore(&ui->lock, flags); + INFO("usb_ept_queue_xfer() tried to queue busy request\n"); + return -EBUSY; + } + + if (!ui->online && (ept->num != 0)) { + req->req.status = -ESHUTDOWN; + spin_unlock_irqrestore(&ui->lock, flags); + INFO("usb_ept_queue_xfer() called while offline\n"); + return -ESHUTDOWN; + } + + req->busy = 1; + req->live = 0; + req->next = 0; + req->req.status = -EBUSY; + + req->dma = dma_map_single(NULL, req->req.buf, length, + (ept->flags & EPT_FLAG_IN) ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + + /* prepare the transaction descriptor item for the hardware */ + item->next = TERMINATE; + item->info = INFO_BYTES(length) | INFO_IOC | INFO_ACTIVE; + item->page0 = req->dma; + item->page1 = (req->dma + 0x1000) & 0xfffff000; + item->page2 = (req->dma + 0x2000) & 0xfffff000; + item->page3 = (req->dma + 0x3000) & 0xfffff000; + + /* Add the new request to the end of the queue */ + last = ept->last; + if (last) { + /* Already requests in the queue. add us to the + * end, but let the completion interrupt actually + * start things going, to avoid hw issues + */ + last->next = req; + + /* only modify the hw transaction next pointer if + * that request is not live + */ + if (!last->live) + last->item->next = req->item_dma; + } else { + /* queue was empty -- kick the hardware */ + ept->req = req; + usb_ept_start(ept); + } + ept->last = req; + + spin_unlock_irqrestore(&ui->lock, flags); + return 0; +} + +/* --- endpoint 0 handling --- */ + +static void ep0_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct msm_request *r = to_msm_request(req); + struct msm_endpoint *ept = to_msm_endpoint(ep); + struct usb_info *ui = ept->ui; + + req->complete = r->gadget_complete; + r->gadget_complete = 0; + if (req->complete) + req->complete(&ui->ep0in.ep, req); +} + +static void ep0_queue_ack_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct msm_endpoint *ept = to_msm_endpoint(ep); + + /* queue up the receive of the ACK response from the host */ + if (req->status == 0) { + struct usb_info *ui = ept->ui; + req->length = 0; + req->complete = ep0_complete; + usb_ept_queue_xfer(&ui->ep0out, req); + } else + ep0_complete(ep, req); +} + +static void ep0_setup_ack(struct usb_info *ui) +{ + struct usb_request *req = ui->setup_req; + req->length = 0; + req->complete = 0; + usb_ept_queue_xfer(&ui->ep0in, req); +} + +static void ep0_setup_stall(struct usb_info *ui) +{ + writel((1<<16) | (1<<0), USB_ENDPTCTRL(0)); +} + +static void ep0_setup_send(struct usb_info *ui, unsigned length) +{ + struct usb_request *req = ui->setup_req; + struct msm_request *r = to_msm_request(req); + struct msm_endpoint *ept = &ui->ep0in; + + req->length = length; + req->complete = ep0_queue_ack_complete; + r->gadget_complete = 0; + usb_ept_queue_xfer(ept, req); +} + +static void handle_setup(struct usb_info *ui) +{ + struct usb_ctrlrequest ctl; + struct usb_request *req = ui->setup_req; + int ret; + + memcpy(&ctl, ui->ep0out.head->setup_data, sizeof(ctl)); + writel(EPT_RX(0), USB_ENDPTSETUPSTAT); + + /* any pending ep0 transactions must be canceled */ + flush_endpoint(&ui->ep0out); + flush_endpoint(&ui->ep0in); + + INFO("setup: type=%02x req=%02x val=%04x idx=%04x len=%04x\n", + ctl.bRequestType, ctl.bRequest, ctl.wValue, + ctl.wIndex, ctl.wLength); + + if (ctl.bRequestType == (USB_DIR_IN | USB_TYPE_STANDARD)) { + if (ctl.bRequest == USB_REQ_GET_STATUS) { + if (ctl.wLength == 2) { + memset(req->buf, 0, 2); + ep0_setup_send(ui, 2); + return; + } + } + } + if (ctl.bRequestType == + (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)) { + if (ctl.bRequest == USB_REQ_CLEAR_FEATURE) { + if ((ctl.wValue == 0) && (ctl.wLength == 0)) { + unsigned num = ctl.wIndex & 0x0f; + + if (num != 0) { + if (ctl.wIndex & 0x80) + num += 16; + + usb_ept_enable(ui->ept + num, 1); + ep0_setup_ack(ui); + return; + } + } + } + } + if (ctl.bRequestType == (USB_DIR_OUT | USB_TYPE_STANDARD)) { + if (ctl.bRequest == USB_REQ_SET_CONFIGURATION) + ui->online = !!ctl.wValue; + else if (ctl.bRequest == USB_REQ_SET_ADDRESS) { + /* write address delayed (will take effect + ** after the next IN txn) + */ + writel((ctl.wValue << 25) | (1 << 24), USB_DEVICEADDR); + goto ack; + } + } + + /* delegate if we get here */ + if (ui->driver) { + ret = ui->driver->setup(&ui->gadget, &ctl); + if (ret >= 0) + return; + } + + /* stall ep0 on error */ + ep0_setup_stall(ui); + return; + +ack: + ep0_setup_ack(ui); +} + +static void handle_endpoint(struct usb_info *ui, unsigned bit) +{ + struct msm_endpoint *ept = ui->ept + bit; + struct msm_request *req; + unsigned long flags; + unsigned info; + + /* + INFO("handle_endpoint() %d %s req=%p(%08x)\n", + ept->num, (ept->flags & EPT_FLAG_IN) ? "in" : "out", + ept->req, ept->req ? ept->req->item_dma : 0); + */ + + /* expire all requests that are no longer active */ + spin_lock_irqsave(&ui->lock, flags); + while ((req = ept->req)) { + info = req->item->info; + + /* if we've processed all live requests, time to + * restart the hardware on the next non-live request + */ + if (!req->live) { + usb_ept_start(ept); + break; + } + + /* if the transaction is still in-flight, stop here */ + if (info & INFO_ACTIVE) + break; + + /* advance ept queue to the next request */ + ept->req = req->next; + if (ept->req == 0) + ept->last = 0; + + dma_unmap_single(NULL, req->dma, req->req.length, + (ept->flags & EPT_FLAG_IN) ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (info & (INFO_HALTED | INFO_BUFFER_ERROR | INFO_TXN_ERROR)) { + /* XXX pass on more specific error code */ + req->req.status = -EIO; + req->req.actual = 0; + INFO("msm72k_udc: ept %d %s error. info=%08x\n", + ept->num, + (ept->flags & EPT_FLAG_IN) ? "in" : "out", + info); + } else { + req->req.status = 0; + req->req.actual = + req->req.length - ((info >> 16) & 0x7FFF); + } + req->busy = 0; + req->live = 0; + if (req->dead) + do_free_req(ui, req); + + if (req->req.complete) { + spin_unlock_irqrestore(&ui->lock, flags); + req->req.complete(&ept->ep, &req->req); + spin_lock_irqsave(&ui->lock, flags); + } + } + spin_unlock_irqrestore(&ui->lock, flags); +} + +static void flush_endpoint_hw(struct usb_info *ui, unsigned bits) +{ + /* flush endpoint, canceling transactions + ** - this can take a "large amount of time" (per databook) + ** - the flush can fail in some cases, thus we check STAT + ** and repeat if we're still operating + ** (does the fact that this doesn't use the tripwire matter?!) + */ + do { + writel(bits, USB_ENDPTFLUSH); + while (readl(USB_ENDPTFLUSH) & bits) + udelay(100); + } while (readl(USB_ENDPTSTAT) & bits); +} + +static void flush_endpoint_sw(struct msm_endpoint *ept) +{ + struct usb_info *ui = ept->ui; + struct msm_request *req; + unsigned long flags; + + /* inactive endpoints have nothing to do here */ + if (ept->ep.maxpacket == 0) + return; + + /* put the queue head in a sane state */ + ept->head->info = 0; + ept->head->next = TERMINATE; + + /* cancel any pending requests */ + spin_lock_irqsave(&ui->lock, flags); + req = ept->req; + ept->req = 0; + ept->last = 0; + while (req != 0) { + req->busy = 0; + req->live = 0; + req->req.status = -ECONNRESET; + req->req.actual = 0; + if (req->req.complete) { + spin_unlock_irqrestore(&ui->lock, flags); + req->req.complete(&ept->ep, &req->req); + spin_lock_irqsave(&ui->lock, flags); + } + if (req->dead) + do_free_req(ui, req); + req = req->next; + } + spin_unlock_irqrestore(&ui->lock, flags); +} + +static void flush_endpoint(struct msm_endpoint *ept) +{ + flush_endpoint_hw(ept->ui, (1 << ept->bit)); + flush_endpoint_sw(ept); +} + +static void flush_all_endpoints(struct usb_info *ui) +{ + unsigned n; + + flush_endpoint_hw(ui, 0xffffffff); + + for (n = 0; n < 32; n++) + flush_endpoint_sw(ui->ept + n); +} + + +static irqreturn_t usb_interrupt(int irq, void *data) +{ + struct usb_info *ui = data; + unsigned n; + + n = readl(USB_USBSTS); + writel(n, USB_USBSTS); + + /* somehow we got an IRQ while in the reset sequence: ignore it */ + if (ui->running == 0) + return IRQ_HANDLED; + + if (n & STS_PCI) { + switch (readl(USB_PORTSC) & PORTSC_PSPD_MASK) { + case PORTSC_PSPD_FS: + INFO("msm72k_udc: portchange USB_SPEED_FULL\n"); + ui->gadget.speed = USB_SPEED_FULL; + break; + case PORTSC_PSPD_LS: + INFO("msm72k_udc: portchange USB_SPEED_LOW\n"); + ui->gadget.speed = USB_SPEED_LOW; + break; + case PORTSC_PSPD_HS: + INFO("msm72k_udc: portchange USB_SPEED_HIGH\n"); + ui->gadget.speed = USB_SPEED_HIGH; + break; + } + } + + if (n & STS_URI) { + INFO("msm72k_udc: reset\n"); + + writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT); + writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE); + writel(0xffffffff, USB_ENDPTFLUSH); + writel(0, USB_ENDPTCTRL(1)); + + if (ui->online != 0) { + /* marking us offline will cause ept queue attempts + ** to fail + */ + ui->online = 0; + + flush_all_endpoints(ui); + + /* XXX: we can't seem to detect going offline, + * XXX: so deconfigure on reset for the time being + */ + if (ui->driver) { + printk(KERN_INFO "usb: notify offline\n"); + ui->driver->disconnect(&ui->gadget); + } + } + } + + if (n & STS_SLI) + INFO("msm72k_udc: suspend\n"); + + if (n & STS_UI) { + n = readl(USB_ENDPTSETUPSTAT); + if (n & EPT_RX(0)) + handle_setup(ui); + + n = readl(USB_ENDPTCOMPLETE); + writel(n, USB_ENDPTCOMPLETE); + while (n) { + unsigned bit = __ffs(n); + handle_endpoint(ui, bit); + n = n & (~(1 << bit)); + } + } + return IRQ_HANDLED; +} + +static void usb_prepare(struct usb_info *ui) +{ + spin_lock_init(&ui->lock); + + memset(ui->buf, 0, 4096); + ui->head = (void *) (ui->buf + 0); + + /* only important for reset/reinit */ + memset(ui->ept, 0, sizeof(ui->ept)); + ui->next_item = 0; + ui->next_ifc_num = 0; + + init_endpoints(ui); + + ui->ep0in.ep.maxpacket = 64; + ui->ep0out.ep.maxpacket = 64; + + ui->setup_req = + usb_ept_alloc_req(&ui->ep0in, SETUP_BUF_SIZE, GFP_KERNEL); + + INIT_WORK(&ui->work, usb_do_work); +} + +static void usb_suspend_phy(struct usb_info *ui) +{ + /* clear VBusValid and SessionEnd rising interrupts */ + ulpi_write(ui, (1 << 1) | (1 << 3), 0x0f); + /* clear VBusValid and SessionEnd falling interrupts */ + ulpi_write(ui, (1 << 1) | (1 << 3), 0x12); + /* disable interface protect circuit to drop current consumption */ + ulpi_write(ui, (1 << 7), 0x08); + /* clear the SuspendM bit -> suspend the PHY */ + ulpi_write(ui, 1 << 6, 0x06); +} + +static void usb_reset(struct usb_info *ui) +{ + unsigned long flags; + INFO("msm72k_udc: reset controller\n"); + + spin_lock_irqsave(&ui->lock, flags); + ui->running = 0; + spin_unlock_irqrestore(&ui->lock, flags); + +#if 0 + /* we should flush and shutdown cleanly if already running */ + writel(0xffffffff, USB_ENDPTFLUSH); + msleep(2); +#endif + + /* RESET */ + writel(2, USB_USBCMD); + msleep(10); + + if (ui->phy_reset) + ui->phy_reset(); + + /* INCR4 BURST mode */ + writel(0x01, USB_SBUSCFG); + + /* select DEVICE mode */ + writel(0x12, USB_USBMODE); + msleep(1); + + /* select ULPI phy */ + writel(0x80000000, USB_PORTSC); + + ulpi_init(ui); + + writel(ui->dma, USB_ENDPOINTLISTADDR); + + configure_endpoints(ui); + + /* marking us offline will cause ept queue attempts to fail */ + ui->online = 0; + + /* terminate any pending transactions */ + flush_all_endpoints(ui); + + if (ui->driver) { + printk(KERN_INFO "usb: notify offline\n"); + ui->driver->disconnect(&ui->gadget); + } + + /* enable interrupts */ + writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR); + + /* go to RUN mode (D+ pullup enable) */ + writel(0x00080001, USB_USBCMD); + + spin_lock_irqsave(&ui->lock, flags); + ui->running = 1; + spin_unlock_irqrestore(&ui->lock, flags); +} + +static void usb_start(struct usb_info *ui) +{ + unsigned long flags; + + spin_lock_irqsave(&ui->lock, flags); + ui->flags |= USB_FLAG_START; + schedule_work(&ui->work); + spin_unlock_irqrestore(&ui->lock, flags); +} + +static struct usb_info *the_usb_info; + +static int usb_free(struct usb_info *ui, int ret) +{ + INFO("usb_free(%d)\n", ret); + + if (ui->irq) + free_irq(ui->irq, 0); + if (ui->pool) + dma_pool_destroy(ui->pool); + if (ui->dma) + dma_free_coherent(&ui->pdev->dev, 4096, ui->buf, ui->dma); + if (ui->addr) + iounmap(ui->addr); + if (ui->clk) + clk_put(ui->clk); + if (ui->pclk) + clk_put(ui->pclk); + kfree(ui); + return ret; +} + +static void usb_do_work_check_vbus(struct usb_info *ui) +{ + unsigned long iflags; + + spin_lock_irqsave(&ui->lock, iflags); + if (vbus) + ui->flags |= USB_FLAG_VBUS_ONLINE; + else + ui->flags |= USB_FLAG_VBUS_OFFLINE; + spin_unlock_irqrestore(&ui->lock, iflags); +} + +static void usb_do_work(struct work_struct *w) +{ + struct usb_info *ui = container_of(w, struct usb_info, work); + unsigned long iflags; + unsigned flags, _vbus; + + for (;;) { + spin_lock_irqsave(&ui->lock, iflags); + flags = ui->flags; + ui->flags = 0; + _vbus = vbus; + spin_unlock_irqrestore(&ui->lock, iflags); + + /* give up if we have nothing to do */ + if (flags == 0) + break; + + switch (ui->state) { + case USB_STATE_IDLE: + if (flags & USB_FLAG_START) { + pr_info("msm72k_udc: IDLE -> ONLINE\n"); + clk_enable(ui->clk); + clk_enable(ui->pclk); + usb_reset(ui); + + ui->state = USB_STATE_ONLINE; + usb_do_work_check_vbus(ui); + } + break; + case USB_STATE_ONLINE: + /* If at any point when we were online, we received + * the signal to go offline, we must honor it + */ + if (flags & USB_FLAG_VBUS_OFFLINE) { + pr_info("msm72k_udc: ONLINE -> OFFLINE\n"); + + /* synchronize with irq context */ + spin_lock_irqsave(&ui->lock, iflags); + ui->running = 0; + ui->online = 0; + spin_unlock_irqrestore(&ui->lock, iflags); + + /* terminate any transactions, etc */ + flush_all_endpoints(ui); + + if (ui->driver) { + printk(KERN_INFO "usb: notify offline\n"); + ui->driver->disconnect(&ui->gadget); + } + + /* power down phy, clock down usb */ + spin_lock_irqsave(&ui->lock, iflags); + usb_suspend_phy(ui); + clk_disable(ui->pclk); + clk_disable(ui->clk); + spin_unlock_irqrestore(&ui->lock, iflags); + + ui->state = USB_STATE_OFFLINE; + usb_do_work_check_vbus(ui); + break; + } + if (flags & USB_FLAG_RESET) { + pr_info("msm72k_udc: ONLINE -> RESET\n"); + usb_reset(ui); + pr_info("msm72k_udc: RESET -> ONLINE\n"); + break; + } + break; + case USB_STATE_OFFLINE: + /* If we were signaled to go online and vbus is still + * present when we received the signal, go online. + */ + if ((flags & USB_FLAG_VBUS_ONLINE) && _vbus) { + pr_info("msm72k_udc: OFFLINE -> ONLINE\n"); + clk_enable(ui->clk); + clk_enable(ui->pclk); + usb_reset(ui); + + ui->state = USB_STATE_ONLINE; + usb_do_work_check_vbus(ui); + } + break; + } + } +} + +/* FIXME - the callers of this function should use a gadget API instead. + * This is called from htc_battery.c and board-halibut.c + * WARNING - this can get called before this driver is initialized. + */ +void msm_hsusb_set_vbus_state(int online) +{ + unsigned long flags; + struct usb_info *ui = the_usb_info; + + if (ui) { + spin_lock_irqsave(&ui->lock, flags); + if (vbus != online) { + vbus = online; + if (online) + ui->flags |= USB_FLAG_VBUS_ONLINE; + else + ui->flags |= USB_FLAG_VBUS_OFFLINE; + schedule_work(&ui->work); + } + spin_unlock_irqrestore(&ui->lock, flags); + } else { + printk(KERN_ERR "msm_hsusb_set_vbus_state called before driver initialized\n"); + vbus = online; + } +} + +#if defined(CONFIG_DEBUG_FS) + +void usb_function_reenumerate(void) +{ + struct usb_info *ui = the_usb_info; + + /* disable and re-enable the D+ pullup */ + INFO("msm72k_udc: disable pullup\n"); + writel(0x00080000, USB_USBCMD); + + msleep(10); + + INFO("msm72k_udc: enable pullup\n"); + writel(0x00080001, USB_USBCMD); +} + +static char debug_buffer[PAGE_SIZE]; + +static ssize_t debug_read_status(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct usb_info *ui = file->private_data; + char *buf = debug_buffer; + unsigned long flags; + struct msm_endpoint *ept; + struct msm_request *req; + int n; + int i = 0; + + spin_lock_irqsave(&ui->lock, flags); + + i += scnprintf(buf + i, PAGE_SIZE - i, + "regs: setup=%08x prime=%08x stat=%08x done=%08x\n", + readl(USB_ENDPTSETUPSTAT), + readl(USB_ENDPTPRIME), + readl(USB_ENDPTSTAT), + readl(USB_ENDPTCOMPLETE)); + i += scnprintf(buf + i, PAGE_SIZE - i, + "regs: cmd=%08x sts=%08x intr=%08x port=%08x\n\n", + readl(USB_USBCMD), + readl(USB_USBSTS), + readl(USB_USBINTR), + readl(USB_PORTSC)); + + + for (n = 0; n < 32; n++) { + ept = ui->ept + n; + if (ept->ep.maxpacket == 0) + continue; + + i += scnprintf(buf + i, PAGE_SIZE - i, + "ept%d %s cfg=%08x active=%08x next=%08x info=%08x\n", + ept->num, (ept->flags & EPT_FLAG_IN) ? "in " : "out", + ept->head->config, ept->head->active, + ept->head->next, ept->head->info); + + for (req = ept->req; req; req = req->next) + i += scnprintf(buf + i, PAGE_SIZE - i, + " req @%08x next=%08x info=%08x page0=%08x %c %c\n", + req->item_dma, req->item->next, + req->item->info, req->item->page0, + req->busy ? 'B' : ' ', + req->live ? 'L' : ' '); + } + + i += scnprintf(buf + i, PAGE_SIZE - i, + "phy failure count: %d\n", ui->phy_fail_count); + + spin_unlock_irqrestore(&ui->lock, flags); + + return simple_read_from_buffer(ubuf, count, ppos, buf, i); +} + +static ssize_t debug_write_reset(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct usb_info *ui = file->private_data; + unsigned long flags; + + spin_lock_irqsave(&ui->lock, flags); + ui->flags |= USB_FLAG_RESET; + schedule_work(&ui->work); + spin_unlock_irqrestore(&ui->lock, flags); + + return count; +} + +static ssize_t debug_write_cycle(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + usb_function_reenumerate(); + return count; +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +const struct file_operations debug_stat_ops = { + .open = debug_open, + .read = debug_read_status, +}; + +const struct file_operations debug_reset_ops = { + .open = debug_open, + .write = debug_write_reset, +}; + +const struct file_operations debug_cycle_ops = { + .open = debug_open, + .write = debug_write_cycle, +}; + +static void usb_debugfs_init(struct usb_info *ui) +{ + struct dentry *dent; + dent = debugfs_create_dir("usb", 0); + if (IS_ERR(dent)) + return; + + debugfs_create_file("status", 0444, dent, ui, &debug_stat_ops); + debugfs_create_file("reset", 0222, dent, ui, &debug_reset_ops); + debugfs_create_file("cycle", 0222, dent, ui, &debug_cycle_ops); +} +#else +static void usb_debugfs_init(struct usb_info *ui) {} +#endif + +static int +msm72k_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ + struct msm_endpoint *ept = to_msm_endpoint(_ep); + + _ep->maxpacket = le16_to_cpu(desc->wMaxPacketSize); + usb_ept_enable(ept, 1); + return 0; +} + +static int msm72k_disable(struct usb_ep *_ep) +{ + struct msm_endpoint *ept = to_msm_endpoint(_ep); + + usb_ept_enable(ept, 0); + return 0; +} + +static struct usb_request * +msm72k_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + return usb_ept_alloc_req(to_msm_endpoint(_ep), 0, gfp_flags); +} + +static void +msm72k_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct msm_request *req = to_msm_request(_req); + struct msm_endpoint *ept = to_msm_endpoint(_ep); + struct usb_info *ui = ept->ui; + unsigned long flags; + int dead = 0; + + spin_lock_irqsave(&ui->lock, flags); + /* defer freeing resources if request is still busy */ + if (req->busy) + dead = req->dead = 1; + spin_unlock_irqrestore(&ui->lock, flags); + + /* if req->dead, then we will clean up when the request finishes */ + if (!dead) + do_free_req(ui, req); +} + +static int +msm72k_queue(struct usb_ep *_ep, struct usb_request *req, gfp_t gfp_flags) +{ + struct msm_endpoint *ep = to_msm_endpoint(_ep); + struct usb_info *ui = ep->ui; + + if (ep == &ui->ep0in) { + struct msm_request *r = to_msm_request(req); + r->gadget_complete = req->complete; + /* ep0_queue_ack_complete queue a receive for ACK before + ** calling req->complete + */ + req->complete = ep0_queue_ack_complete; + } + return usb_ept_queue_xfer(ep, req); +} + +static int msm72k_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct msm_endpoint *ep = to_msm_endpoint(_ep); + struct msm_request *req = to_msm_request(_req); + struct usb_info *ui = ep->ui; + + struct msm_request *cur, *prev; + unsigned long flags; + + if (!_ep || !_req) + return -EINVAL; + + spin_lock_irqsave(&ui->lock, flags); + cur = ep->req; + prev = NULL; + + while (cur != 0) { + if (cur == req) { + req->busy = 0; + req->live = 0; + req->req.status = -ECONNRESET; + req->req.actual = 0; + if (req->req.complete) { + spin_unlock_irqrestore(&ui->lock, flags); + req->req.complete(&ep->ep, &req->req); + spin_lock_irqsave(&ui->lock, flags); + } + if (req->dead) + do_free_req(ui, req); + /* remove from linked list */ + if (prev) + prev->next = cur->next; + else + ep->req = cur->next; + prev = cur; + /* break from loop */ + cur = NULL; + } else + cur = cur->next; + } + spin_unlock_irqrestore(&ui->lock, flags); + + return 0; +} + +static int +msm72k_set_halt(struct usb_ep *_ep, int value) +{ + return -EOPNOTSUPP; +} + +static int +msm72k_fifo_status(struct usb_ep *_ep) +{ + return -EOPNOTSUPP; +} + +static void +msm72k_fifo_flush(struct usb_ep *_ep) +{ + flush_endpoint(to_msm_endpoint(_ep)); +} + +static const struct usb_ep_ops msm72k_ep_ops = { + .enable = msm72k_enable, + .disable = msm72k_disable, + + .alloc_request = msm72k_alloc_request, + .free_request = msm72k_free_request, + + .queue = msm72k_queue, + .dequeue = msm72k_dequeue, + + .set_halt = msm72k_set_halt, + .fifo_status = msm72k_fifo_status, + .fifo_flush = msm72k_fifo_flush, +}; + +static int msm72k_get_frame(struct usb_gadget *_gadget) +{ + struct usb_info *ui = container_of(_gadget, struct usb_info, gadget); + + /* frame number is in bits 13:3 */ + return (readl(USB_FRINDEX) >> 3) & 0x000007FF; +} + +/* VBUS reporting logically comes from a transceiver */ +static int msm72k_udc_vbus_session(struct usb_gadget *_gadget, int is_active) +{ + msm_hsusb_set_vbus_state(is_active); + return 0; +} + +/* drivers may have software control over D+ pullup */ +static int msm72k_pullup(struct usb_gadget *_gadget, int is_active) +{ + struct usb_info *ui = container_of(_gadget, struct usb_info, gadget); + + if (is_active) + writel(0x00080001, USB_USBCMD); + else + writel(0x00080000, USB_USBCMD); + + return 0; +} + +static const struct usb_gadget_ops msm72k_ops = { + .get_frame = msm72k_get_frame, + .vbus_session = msm72k_udc_vbus_session, + .pullup = msm72k_pullup, +}; + +static int msm72k_probe(struct platform_device *pdev) +{ + struct resource *res; + struct usb_info *ui; + int irq; + int ret; + + INFO("msm72k_probe\n"); + ui = kzalloc(sizeof(struct usb_info), GFP_KERNEL); + if (!ui) + return -ENOMEM; + + ui->pdev = pdev; + + if (pdev->dev.platform_data) { + struct msm_hsusb_platform_data *pdata = pdev->dev.platform_data; + ui->phy_reset = pdata->phy_reset; + ui->phy_init_seq = pdata->phy_init_seq; + } + + irq = platform_get_irq(pdev, 0); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res || (irq < 0)) + return usb_free(ui, -ENODEV); + + ui->addr = ioremap(res->start, 4096); + if (!ui->addr) + return usb_free(ui, -ENOMEM); + + ui->buf = dma_alloc_coherent(&pdev->dev, 4096, &ui->dma, GFP_KERNEL); + if (!ui->buf) + return usb_free(ui, -ENOMEM); + + ui->pool = dma_pool_create("msm72k_udc", NULL, 32, 32, 0); + if (!ui->pool) + return usb_free(ui, -ENOMEM); + + INFO("msm72k_probe() io=%p, irq=%d, dma=%p(%x)\n", + ui->addr, irq, ui->buf, ui->dma); + + ui->clk = clk_get(&pdev->dev, "usb_hs_clk"); + if (IS_ERR(ui->clk)) + return usb_free(ui, PTR_ERR(ui->clk)); + + ui->pclk = clk_get(&pdev->dev, "usb_hs_pclk"); + if (IS_ERR(ui->pclk)) + return usb_free(ui, PTR_ERR(ui->pclk)); + + ret = request_irq(irq, usb_interrupt, 0, pdev->name, ui); + if (ret) + return usb_free(ui, ret); + enable_irq_wake(irq); + ui->irq = irq; + + ui->gadget.ops = &msm72k_ops; + ui->gadget.is_dualspeed = 1; + device_initialize(&ui->gadget.dev); + strcpy(ui->gadget.dev.bus_id, "gadget"); + ui->gadget.dev.parent = &pdev->dev; + ui->gadget.dev.dma_mask = pdev->dev.dma_mask; + + the_usb_info = ui; + + usb_debugfs_init(ui); + + usb_prepare(ui); + + return 0; +} + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct usb_info *ui = the_usb_info; + int retval, n; + + if (!driver + || driver->speed < USB_SPEED_FULL + || !driver->bind + || !driver->disconnect + || !driver->setup) + return -EINVAL; + if (!ui) + return -ENODEV; + if (ui->driver) + return -EBUSY; + + /* first hook up the driver ... */ + ui->driver = driver; + ui->gadget.dev.driver = &driver->driver; + ui->gadget.name = driver_name; + INIT_LIST_HEAD(&ui->gadget.ep_list); + ui->gadget.ep0 = &ui->ep0in.ep; + INIT_LIST_HEAD(&ui->gadget.ep0->ep_list); + ui->gadget.speed = USB_SPEED_UNKNOWN; + + for (n = 1; n < 16; n++) { + struct msm_endpoint *ept = ui->ept + n; + list_add_tail(&ept->ep.ep_list, &ui->gadget.ep_list); + ept->ep.maxpacket = 512; + } + for (n = 17; n < 32; n++) { + struct msm_endpoint *ept = ui->ept + n; + list_add_tail(&ept->ep.ep_list, &ui->gadget.ep_list); + ept->ep.maxpacket = 512; + } + + retval = device_add(&ui->gadget.dev); + if (retval) + goto fail; + + retval = driver->bind(&ui->gadget); + if (retval) { + INFO("bind to driver %s --> error %d\n", + driver->driver.name, retval); + device_del(&ui->gadget.dev); + goto fail; + } + + INFO("msm72k_udc: registered gadget driver '%s'\n", + driver->driver.name); + usb_start(ui); + + return 0; + +fail: + ui->driver = NULL; + ui->gadget.dev.driver = NULL; + return retval; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct usb_info *dev = the_usb_info; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver || !driver->unbind) + return -EINVAL; + + driver->unbind(&dev->gadget); + dev->gadget.dev.driver = NULL; + dev->driver = NULL; + + device_del(&dev->gadget.dev); + + VDEBUG("unregistered gadget driver '%s'\n", driver->driver.name); + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + + +static struct platform_driver usb_driver = { + .probe = msm72k_probe, + .driver = { .name = "msm_hsusb", }, +}; + +static int __init init(void) +{ + return platform_driver_register(&usb_driver); +} +module_init(init); + +static void __exit cleanup(void) +{ + platform_driver_unregister(&usb_driver); +} +module_exit(cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Mike Lockwood, Brian Swetland"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/msm72k_udc.h b/drivers/usb/gadget/msm72k_udc.h new file mode 100644 index 0000000000000..160d359c8c037 --- /dev/null +++ b/drivers/usb/gadget/msm72k_udc.h @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland + * + * 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_USB_GADGET_MSM72K_UDC_H__ +#define __LINUX_USB_GADGET_MSM72K_UDC_H__ + +/*-------------------------------------------------------------------------*/ + +#define xprintk(level, fmt, args...) \ + printk(level "%s: " fmt , driver_name , ## args) + +#ifdef DEBUG +#undef DEBUG +#define DEBUG(fmt, args...) \ + xprintk(KERN_DEBUG , fmt , ## args) +#else +#define DEBUG(fmt,args...) \ + do { } while (0) +#endif /* DEBUG */ + +#ifdef VERBOSE +#define VDEBUG DEBUG +#else +#define VDEBUG(fmt,args...) \ + do { } while (0) +#endif /* VERBOSE */ + +#define ERROR(fmt,args...) \ + xprintk(KERN_ERR , fmt , ## args) +#define INFO(fmt,args...) \ + xprintk(KERN_INFO , fmt , ## args) + +/*-------------------------------------------------------------------------*/ + + +#define USB_ID (MSM_USB_BASE + 0x0000) +#define USB_HWGENERAL (MSM_USB_BASE + 0x0004) +#define USB_HWHOST (MSM_USB_BASE + 0x0008) +#define USB_HWDEVICE (MSM_USB_BASE + 0x000C) +#define USB_HWTXBUF (MSM_USB_BASE + 0x0010) +#define USB_HWRXBUF (MSM_USB_BASE + 0x0014) +#define USB_SBUSCFG (MSM_USB_BASE + 0x0090) + +#define USB_CAPLENGTH (MSM_USB_BASE + 0x0100) /* 8 bit */ +#define USB_HCIVERSION (MSM_USB_BASE + 0x0102) /* 16 bit */ +#define USB_HCSPARAMS (MSM_USB_BASE + 0x0104) +#define USB_HCCPARAMS (MSM_USB_BASE + 0x0108) +#define USB_DCIVERSION (MSM_USB_BASE + 0x0120) /* 16 bit */ +#define USB_USBCMD (MSM_USB_BASE + 0x0140) +#define USB_USBSTS (MSM_USB_BASE + 0x0144) +#define USB_USBINTR (MSM_USB_BASE + 0x0148) +#define USB_FRINDEX (MSM_USB_BASE + 0x014C) +#define USB_DEVICEADDR (MSM_USB_BASE + 0x0154) +#define USB_ENDPOINTLISTADDR (MSM_USB_BASE + 0x0158) +#define USB_BURSTSIZE (MSM_USB_BASE + 0x0160) +#define USB_TXFILLTUNING (MSM_USB_BASE + 0x0164) +#define USB_ULPI_VIEWPORT (MSM_USB_BASE + 0x0170) +#define USB_ENDPTNAK (MSM_USB_BASE + 0x0178) +#define USB_ENDPTNAKEN (MSM_USB_BASE + 0x017C) +#define USB_PORTSC (MSM_USB_BASE + 0x0184) +#define USB_OTGSC (MSM_USB_BASE + 0x01A4) +#define USB_USBMODE (MSM_USB_BASE + 0x01A8) +#define USB_ENDPTSETUPSTAT (MSM_USB_BASE + 0x01AC) +#define USB_ENDPTPRIME (MSM_USB_BASE + 0x01B0) +#define USB_ENDPTFLUSH (MSM_USB_BASE + 0x01B4) +#define USB_ENDPTSTAT (MSM_USB_BASE + 0x01B8) +#define USB_ENDPTCOMPLETE (MSM_USB_BASE + 0x01BC) +#define USB_ENDPTCTRL(n) (MSM_USB_BASE + 0x01C0 + (4 * (n))) + + +#define USBCMD_RESET 2 +#define USBCMD_ATTACH 1 +#define USBCMD_ATDTW (1 << 14) + +#define USBMODE_DEVICE 2 +#define USBMODE_HOST 3 + +struct ept_queue_head { + unsigned config; + unsigned active; /* read-only */ + + unsigned next; + unsigned info; + unsigned page0; + unsigned page1; + unsigned page2; + unsigned page3; + unsigned page4; + unsigned reserved_0; + + unsigned char setup_data[8]; + + unsigned reserved_1; + unsigned reserved_2; + unsigned reserved_3; + unsigned reserved_4; +}; + +#define CONFIG_MAX_PKT(n) ((n) << 16) +#define CONFIG_ZLT (1 << 29) /* stop on zero-len xfer */ +#define CONFIG_IOS (1 << 15) /* IRQ on setup */ + +struct ept_queue_item { + unsigned next; + unsigned info; + unsigned page0; + unsigned page1; + unsigned page2; + unsigned page3; + unsigned page4; + unsigned reserved; +}; + +#define TERMINATE 1 + +#define INFO_BYTES(n) ((n) << 16) +#define INFO_IOC (1 << 15) +#define INFO_ACTIVE (1 << 7) +#define INFO_HALTED (1 << 6) +#define INFO_BUFFER_ERROR (1 << 5) +#define INFO_TXN_ERROR (1 << 3) + + +#define STS_NAKI (1 << 16) /* */ +#define STS_SLI (1 << 8) /* R/WC - suspend state entered */ +#define STS_SRI (1 << 7) /* R/WC - SOF recv'd */ +#define STS_URI (1 << 6) /* R/WC - RESET recv'd - write to clear */ +#define STS_FRI (1 << 3) /* R/WC - Frame List Rollover */ +#define STS_PCI (1 << 2) /* R/WC - Port Change Detect */ +#define STS_UEI (1 << 1) /* R/WC - USB Error */ +#define STS_UI (1 << 0) /* R/WC - USB Transaction Complete */ + + +/* bits used in all the endpoint status registers */ +#define EPT_TX(n) (1 << ((n) + 16)) +#define EPT_RX(n) (1 << (n)) + + +#define CTRL_TXE (1 << 23) +#define CTRL_TXR (1 << 22) +#define CTRL_TXI (1 << 21) +#define CTRL_TXD (1 << 17) +#define CTRL_TXS (1 << 16) +#define CTRL_RXE (1 << 7) +#define CTRL_RXR (1 << 6) +#define CTRL_RXI (1 << 5) +#define CTRL_RXD (1 << 1) +#define CTRL_RXS (1 << 0) + +#define CTRL_TXT_MASK (3 << 18) +#define CTRL_TXT_CTRL (0 << 18) +#define CTRL_TXT_ISOCH (1 << 18) +#define CTRL_TXT_BULK (2 << 18) +#define CTRL_TXT_INT (3 << 18) + +#define CTRL_RXT_MASK (3 << 2) +#define CTRL_RXT_CTRL (0 << 2) +#define CTRL_RXT_ISOCH (1 << 2) +#define CTRL_RXT_BULK (2 << 2) +#define CTRL_RXT_INT (3 << 2) + +#define ULPI_WAKEUP (1 << 31) +#define ULPI_RUN (1 << 30) +#define ULPI_WRITE (1 << 29) +#define ULPI_READ (0 << 29) +#define ULPI_STATE_NORMAL (1 << 27) +#define ULPI_ADDR(n) (((n) & 255) << 16) +#define ULPI_DATA(n) ((n) & 255) +#define ULPI_DATA_READ(n) (((n) >> 8) & 255) + +/* USB_PORTSC bits for determining port speed */ +#define PORTSC_PSPD_FS (0 << 26) +#define PORTSC_PSPD_LS (1 << 26) +#define PORTSC_PSPD_HS (2 << 26) +#define PORTSC_PSPD_MASK (3 << 26) + +#endif /* __LINUX_USB_GADGET_MSM72K_UDC_H__ */ From ed44c8a92d1a84bf55dfbccfbc5de47beec40a78 Mon Sep 17 00:00:00 2001 From: "Krishna, Vamsi" Date: Fri, 6 Feb 2009 19:28:33 +0530 Subject: [PATCH 0226/2556] [ARM] msm: usb: gadget: support for ep0 out requests Support for transfer on ep0-out endpoint. CDC/ACM requries this feature to process set line coding commands. Acked-by: Allam, Suresh Reddy --- drivers/usb/gadget/msm72k_udc.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index a2f1afc7c280a..f7502d84e8c77 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -173,6 +173,8 @@ struct usb_info { struct clk *clk; struct clk *pclk; + + unsigned int ep0_dir; }; static const struct usb_ep_ops msm72k_ep_ops; @@ -467,7 +469,10 @@ static void ep0_queue_ack_complete(struct usb_ep *ep, struct usb_request *req) struct usb_info *ui = ept->ui; req->length = 0; req->complete = ep0_complete; - usb_ept_queue_xfer(&ui->ep0out, req); + if (ui->ep0_dir == USB_DIR_IN) + usb_ept_queue_xfer(&ui->ep0out, req); + else + usb_ept_queue_xfer(&ui->ep0in, req); } else ep0_complete(ep, req); } @@ -506,6 +511,11 @@ static void handle_setup(struct usb_info *ui) memcpy(&ctl, ui->ep0out.head->setup_data, sizeof(ctl)); writel(EPT_RX(0), USB_ENDPTSETUPSTAT); + if (ctl.bRequestType & USB_DIR_IN) + ui->ep0_dir = USB_DIR_IN; + else + ui->ep0_dir = USB_DIR_OUT; + /* any pending ep0 transactions must be canceled */ flush_endpoint(&ui->ep0out); flush_endpoint(&ui->ep0in); @@ -1208,12 +1218,17 @@ msm72k_queue(struct usb_ep *_ep, struct usb_request *req, gfp_t gfp_flags) if (ep == &ui->ep0in) { struct msm_request *r = to_msm_request(req); + if (!req->length) + goto ep_queue_done; r->gadget_complete = req->complete; /* ep0_queue_ack_complete queue a receive for ACK before ** calling req->complete */ req->complete = ep0_queue_ack_complete; + if (ui->ep0_dir == USB_DIR_OUT) + ep = &ui->ep0out; } +ep_queue_done: return usb_ept_queue_xfer(ep, req); } From 53fd0254b2bc591b42d2e247dfea03eb3f478038 Mon Sep 17 00:00:00 2001 From: "Krishna, Vamsi" Date: Fri, 6 Feb 2009 19:28:34 +0530 Subject: [PATCH 0227/2556] [ARM] msm: usb: gadget: Send ACK handshake to CLEAR_FEATURE req for EP0 When device STALL's control endpoint, host sends CLEAR_FEATURE request to clear STALL on endpoint-0. For Endpoint-0, hardware clears STALL immediately after receiving SETUP token on EP0, hence software does not need to explicitly clear the STALL for EP0 in hardware, but, software must need to configure Hardware to send ACK handshake to CLEAR_FEATURE request." Acked-by: Allam, Suresh Reddy --- drivers/usb/gadget/msm72k_udc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index f7502d84e8c77..eee23470cc85c 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -544,9 +544,8 @@ static void handle_setup(struct usb_info *ui) num += 16; usb_ept_enable(ui->ept + num, 1); - ep0_setup_ack(ui); - return; } + goto ack; } } } From 89cffc527f24cdd2b57a60a9a7b8c4134c79f059 Mon Sep 17 00:00:00 2001 From: "Krishna, Vamsi" Date: Fri, 6 Feb 2009 19:28:35 +0530 Subject: [PATCH 0228/2556] [ARM] msm: usb: gadget: STALL support for endpoint If device does not support any control request or non-control endpoint requests, device can stall that particular endpoint. Add support to stall endpoint. Acked-by: Allam, Suresh Reddy --- drivers/usb/gadget/msm72k_udc.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index eee23470cc85c..94fe8df0ae3de 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -1279,7 +1279,34 @@ static int msm72k_dequeue(struct usb_ep *_ep, struct usb_request *_req) static int msm72k_set_halt(struct usb_ep *_ep, int value) { - return -EOPNOTSUPP; + struct msm_endpoint *ept = to_msm_endpoint(_ep); + struct usb_info *ui = ept->ui; + unsigned int in = ept->flags & EPT_FLAG_IN; + unsigned int n; + unsigned long flags; + + spin_lock_irqsave(&ui->lock, flags); + n = readl(USB_ENDPTCTRL(ept->num)); + + if (in) { + if (value) + n |= CTRL_TXS; + else { + n &= ~CTRL_TXS; + n |= CTRL_TXR; + } + } else { + if (value) + n |= CTRL_RXS; + else { + n &= ~CTRL_RXS; + n |= CTRL_RXR; + } + } + writel(n, USB_ENDPTCTRL(ept->num)); + spin_unlock_irqrestore(&ui->lock, flags); + + return 0; } static int From 63b8680cc7b1aca2032bc0d3f8334ffab05439b8 Mon Sep 17 00:00:00 2001 From: "Krishna, Vamsi" Date: Fri, 6 Feb 2009 19:28:36 +0530 Subject: [PATCH 0229/2556] [ARM] msm: usb: gadget: clear the stall bit on CLEAR FEATURE cmd CLEAR FEATURE command on an endpoint was enabling endpoint instead of clearing the stall bit. Acked-by: Allam, Suresh Reddy --- drivers/usb/gadget/msm72k_udc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 94fe8df0ae3de..1cf46d48abe91 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -180,6 +180,7 @@ struct usb_info { static const struct usb_ep_ops msm72k_ep_ops; +static int msm72k_set_halt(struct usb_ep *_ep, int value); static void flush_endpoint(struct msm_endpoint *ept); #if 0 @@ -540,10 +541,13 @@ static void handle_setup(struct usb_info *ui) unsigned num = ctl.wIndex & 0x0f; if (num != 0) { + struct msm_endpoint *ept; + if (ctl.wIndex & 0x80) num += 16; + ept = &ui->ep0out + num; - usb_ept_enable(ui->ept + num, 1); + msm72k_set_halt(&ept->ep, 0); } goto ack; } From 8f2dba8a834a177c9f310387a2b1a2d0455edcea Mon Sep 17 00:00:00 2001 From: "Krishna, Vamsi" Date: Fri, 6 Feb 2009 19:28:37 +0530 Subject: [PATCH 0230/2556] [ARM] msm: usb: gadget: add support for interrupt endpoint type Add support for interrupt endpoint. CDC/ACM requires interrupt endpoint support to work as modem. Acked-by: Allam, Suresh Reddy --- drivers/usb/gadget/msm72k_udc.c | 23 ++++++++++++++--------- drivers/usb/gadget/msm72k_udc.h | 2 ++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 1cf46d48abe91..f4444a687fa1e 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -327,7 +327,8 @@ static void do_free_req(struct usb_info *ui, struct msm_request *req) } -static void usb_ept_enable(struct msm_endpoint *ept, int yes) +static void usb_ept_enable(struct msm_endpoint *ept, int yes, + unsigned char ep_type) { struct usb_info *ui = ept->ui; int in = ept->flags & EPT_FLAG_IN; @@ -336,16 +337,18 @@ static void usb_ept_enable(struct msm_endpoint *ept, int yes) n = readl(USB_ENDPTCTRL(ept->num)); if (in) { - n = (n & (~CTRL_TXT_MASK)) | CTRL_TXT_BULK; - if (yes) + if (yes) { + n = (n & (~CTRL_TXT_MASK)) | + (ep_type << CTRL_TXT_EP_TYPE_SHIFT); n |= CTRL_TXE | CTRL_TXR; - else + } else n &= (~CTRL_TXE); } else { - n = (n & (~CTRL_RXT_MASK)) | CTRL_RXT_BULK; - if (yes) + if (yes) { + n = (n & (~CTRL_RXT_MASK)) | + (ep_type << CTRL_RXT_EP_TYPE_SHIFT); n |= CTRL_RXE | CTRL_RXR; - else + } else n &= ~(CTRL_RXE); } writel(n, USB_ENDPTCTRL(ept->num)); @@ -1173,9 +1176,11 @@ static int msm72k_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) { struct msm_endpoint *ept = to_msm_endpoint(_ep); + unsigned char ep_type = + desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; _ep->maxpacket = le16_to_cpu(desc->wMaxPacketSize); - usb_ept_enable(ept, 1); + usb_ept_enable(ept, 1, ep_type); return 0; } @@ -1183,7 +1188,7 @@ static int msm72k_disable(struct usb_ep *_ep) { struct msm_endpoint *ept = to_msm_endpoint(_ep); - usb_ept_enable(ept, 0); + usb_ept_enable(ept, 0, 0); return 0; } diff --git a/drivers/usb/gadget/msm72k_udc.h b/drivers/usb/gadget/msm72k_udc.h index 160d359c8c037..5cf8a78971c2b 100644 --- a/drivers/usb/gadget/msm72k_udc.h +++ b/drivers/usb/gadget/msm72k_udc.h @@ -164,12 +164,14 @@ struct ept_queue_item { #define CTRL_TXT_ISOCH (1 << 18) #define CTRL_TXT_BULK (2 << 18) #define CTRL_TXT_INT (3 << 18) +#define CTRL_TXT_EP_TYPE_SHIFT 18 #define CTRL_RXT_MASK (3 << 2) #define CTRL_RXT_CTRL (0 << 2) #define CTRL_RXT_ISOCH (1 << 2) #define CTRL_RXT_BULK (2 << 2) #define CTRL_RXT_INT (3 << 2) +#define CTRL_RXT_EP_TYPE_SHIFT 2 #define ULPI_WAKEUP (1 << 31) #define ULPI_RUN (1 << 30) From 3c11d9ed5194d228fceea27784a8c0e63816d989 Mon Sep 17 00:00:00 2001 From: "Krishna, Vamsi" Date: Fri, 6 Feb 2009 19:28:38 +0530 Subject: [PATCH 0231/2556] [ARM] msm: usb: gadget: support usb protocol SET FEATURE cmd USBCV-CH9 requires to support SET FEATURE command to be supported. SET FEATURE command can be used to set the HALT on an endpoint and to enable/disbale remote wakeup. Currently we are supporting only SET FETURE on endpoint only. Acked-by: Allam, Suresh Reddy --- drivers/usb/gadget/msm72k_udc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index f4444a687fa1e..a2850897fd873 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -539,7 +539,8 @@ static void handle_setup(struct usb_info *ui) } if (ctl.bRequestType == (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)) { - if (ctl.bRequest == USB_REQ_CLEAR_FEATURE) { + if ((ctl.bRequest == USB_REQ_CLEAR_FEATURE) || + (ctl.bRequest == USB_REQ_SET_FEATURE)) { if ((ctl.wValue == 0) && (ctl.wLength == 0)) { unsigned num = ctl.wIndex & 0x0f; @@ -550,7 +551,10 @@ static void handle_setup(struct usb_info *ui) num += 16; ept = &ui->ep0out + num; - msm72k_set_halt(&ept->ep, 0); + if (ctl.bRequest == USB_REQ_SET_FEATURE) + msm72k_set_halt(&ept->ep, 1); + else + msm72k_set_halt(&ept->ep, 0); } goto ack; } From 37c2723455668bb60fc98d92539c6ec644bdc30e Mon Sep 17 00:00:00 2001 From: "Krishna, Vamsi" Date: Fri, 6 Feb 2009 19:28:39 +0530 Subject: [PATCH 0232/2556] [ARM] msm: usb: gadget: support usb protocol GET STATUS cmd USBCV-CH9 requires support for GET STATUS command which can be used to get status of device, endpoint or interface. It is used by USBCV to verify if previous SET command was successful or not. Acked-by: Allam, Suresh Reddy --- drivers/usb/gadget/msm72k_udc.c | 55 ++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index a2850897fd873..77ce63eb6761d 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -183,6 +183,18 @@ static const struct usb_ep_ops msm72k_ep_ops; static int msm72k_set_halt(struct usb_ep *_ep, int value); static void flush_endpoint(struct msm_endpoint *ept); +static int usb_ep_get_stall(struct msm_endpoint *ept) +{ + unsigned int n; + struct usb_info *ui = ept->ui; + + n = readl(USB_ENDPTCTRL(ept->num)); + if (ept->flags & EPT_FLAG_IN) + return (CTRL_TXS & n) ? 1 : 0; + else + return (CTRL_RXS & n) ? 1 : 0; +} + #if 0 static unsigned ulpi_read(struct usb_info *ui, unsigned reg) { @@ -528,13 +540,47 @@ static void handle_setup(struct usb_info *ui) ctl.bRequestType, ctl.bRequest, ctl.wValue, ctl.wIndex, ctl.wLength); - if (ctl.bRequestType == (USB_DIR_IN | USB_TYPE_STANDARD)) { + if ((ctl.bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) == + (USB_DIR_IN | USB_TYPE_STANDARD)) { if (ctl.bRequest == USB_REQ_GET_STATUS) { - if (ctl.wLength == 2) { + if (ctl.wLength != 2) + goto stall; + switch (ctl.bRequestType & USB_RECIP_MASK) { + case USB_RECIP_ENDPOINT: + { + struct msm_endpoint *ept; + unsigned num = + ctl.wIndex & USB_ENDPOINT_NUMBER_MASK; + u16 temp = 0; + + if (num == 0) { + memset(req->buf, 0, 2); + break; + } + if (ctl.wIndex & USB_ENDPOINT_DIR_MASK) + num += 16; + ept = &ui->ep0out + num; + temp = usb_ep_get_stall(ept); + temp = temp << USB_ENDPOINT_HALT; + memcpy(req->buf, &temp, 2); + break; + } + case USB_RECIP_DEVICE: + { + u16 temp = 0; + + temp = 1 << USB_DEVICE_SELF_POWERED; + memcpy(req->buf, &temp, 2); + break; + } + case USB_RECIP_INTERFACE: memset(req->buf, 0, 2); - ep0_setup_send(ui, 2); - return; + break; + default: + goto stall; } + ep0_setup_send(ui, 2); + return; } } if (ctl.bRequestType == @@ -579,6 +625,7 @@ static void handle_setup(struct usb_info *ui) return; } +stall: /* stall ep0 on error */ ep0_setup_stall(ui); return; From 9030e91344a948b501b0a398f575a9eb43199431 Mon Sep 17 00:00:00 2001 From: "Krishna, Vamsi" Date: Fri, 6 Feb 2009 19:28:40 +0530 Subject: [PATCH 0233/2556] [ARM] msm: usb: gadget: usb electrical test modes support Add support for TEST_SE0_NAK, TEST_J, TEST_K, TEST_PKT electrical test modes. To facilitate compliance testing, high-speed capable usb devices must support above mentioned test modes. Acked-by: Allam, Suresh Reddy --- drivers/usb/gadget/msm72k_udc.c | 52 ++++++++++++++++++++++++++++++++- drivers/usb/gadget/msm72k_udc.h | 10 +++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 77ce63eb6761d..903cd79d4ca65 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -175,6 +175,7 @@ struct usb_info { struct clk *pclk; unsigned int ep0_dir; + u16 test_mode; }; static const struct usb_ep_ops msm72k_ep_ops; @@ -493,11 +494,47 @@ static void ep0_queue_ack_complete(struct usb_ep *ep, struct usb_request *req) ep0_complete(ep, req); } +static void ep0_setup_ack_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct msm_endpoint *ept = to_msm_endpoint(ep); + struct usb_info *ui = ept->ui; + unsigned int temp; + + if (!ui->test_mode) + return; + + switch (ui->test_mode) { + case J_TEST: + pr_info("usb electrical test mode: (J)\n"); + temp = readl(USB_PORTSC) & (~PORTSC_PTC); + writel(temp | PORTSC_PTC_J_STATE, USB_PORTSC); + break; + + case K_TEST: + pr_info("usb electrical test mode: (K)\n"); + temp = readl(USB_PORTSC) & (~PORTSC_PTC); + writel(temp | PORTSC_PTC_K_STATE, USB_PORTSC); + break; + + case SE0_NAK_TEST: + pr_info("usb electrical test mode: (SE0-NAK)\n"); + temp = readl(USB_PORTSC) & (~PORTSC_PTC); + writel(temp | PORTSC_PTC_SE0_NAK, USB_PORTSC); + break; + + case TST_PKT_TEST: + pr_info("usb electrical test mode: (TEST_PKT)\n"); + temp = readl(USB_PORTSC) & (~PORTSC_PTC); + writel(temp | PORTSC_PTC_TST_PKT, USB_PORTSC); + break; + } +} + static void ep0_setup_ack(struct usb_info *ui) { struct usb_request *req = ui->setup_req; req->length = 0; - req->complete = 0; + req->complete = ep0_setup_ack_complete; usb_ept_queue_xfer(&ui->ep0in, req); } @@ -615,6 +652,19 @@ static void handle_setup(struct usb_info *ui) */ writel((ctl.wValue << 25) | (1 << 24), USB_DEVICEADDR); goto ack; + } else if (ctl.bRequest == USB_REQ_SET_FEATURE) { + switch (ctl.wValue) { + case USB_DEVICE_TEST_MODE: + switch (ctl.wIndex) { + case J_TEST: + case K_TEST: + case SE0_NAK_TEST: + case TST_PKT_TEST: + ui->test_mode = ctl.wIndex; + goto ack; + } + goto stall; + } } } diff --git a/drivers/usb/gadget/msm72k_udc.h b/drivers/usb/gadget/msm72k_udc.h index 5cf8a78971c2b..6843db46dab04 100644 --- a/drivers/usb/gadget/msm72k_udc.h +++ b/drivers/usb/gadget/msm72k_udc.h @@ -188,4 +188,14 @@ struct ept_queue_item { #define PORTSC_PSPD_HS (2 << 26) #define PORTSC_PSPD_MASK (3 << 26) +/* test mode support */ +#define J_TEST (0x0100) +#define K_TEST (0x0200) +#define SE0_NAK_TEST (0x0300) +#define TST_PKT_TEST (0x0400) +#define PORTSC_PTC (0xf << 16) +#define PORTSC_PTC_J_STATE (0x01 << 16) +#define PORTSC_PTC_K_STATE (0x02 << 16) +#define PORTSC_PTC_SE0_NAK (0x03 << 16) +#define PORTSC_PTC_TST_PKT (0x04 << 16) #endif /* __LINUX_USB_GADGET_MSM72K_UDC_H__ */ From 1c4f0a7e7d1b39cdba7854c98ce91ba027634353 Mon Sep 17 00:00:00 2001 From: "Krishna, Vamsi" Date: Fri, 6 Feb 2009 19:28:41 +0530 Subject: [PATCH 0234/2556] [ARM] msm: usb: gadget: support remote wakeup GET/SET/CLEAR features Support GET/SET/CLEAR features on remote wakeup so that host can read, enable or disable remote wakeup support Acked-by: Allam, Suresh Reddy --- drivers/usb/gadget/msm72k_udc.c | 38 +++++++++++++++++++++++++++++++++ drivers/usb/gadget/msm72k_udc.h | 3 +++ 2 files changed, 41 insertions(+) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 903cd79d4ca65..ab1d46f4e142f 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -38,6 +38,7 @@ #include #include +#include static const char driver_name[] = "msm72k_udc"; @@ -176,6 +177,8 @@ struct usb_info { unsigned int ep0_dir; u16 test_mode; + + u8 remote_wakeup; }; static const struct usb_ep_ops msm72k_ep_ops; @@ -607,6 +610,8 @@ static void handle_setup(struct usb_info *ui) u16 temp = 0; temp = 1 << USB_DEVICE_SELF_POWERED; + temp |= (ui->remote_wakeup << + USB_DEVICE_REMOTE_WAKEUP); memcpy(req->buf, &temp, 2); break; } @@ -664,7 +669,14 @@ static void handle_setup(struct usb_info *ui) goto ack; } goto stall; + case USB_DEVICE_REMOTE_WAKEUP: + ui->remote_wakeup = 1; + goto ack; } + } else if ((ctl.bRequest == USB_REQ_CLEAR_FEATURE) && + (ctl.wValue == USB_DEVICE_REMOTE_WAKEUP)) { + ui->remote_wakeup = 0; + goto ack; } } @@ -1474,10 +1486,36 @@ static int msm72k_pullup(struct usb_gadget *_gadget, int is_active) return 0; } +static int msm72k_wakeup(struct usb_gadget *_gadget) +{ + struct usb_info *ui = container_of(_gadget, struct usb_info, gadget); + unsigned long flags; + + if (!ui->remote_wakeup) { + pr_err("%s: remote wakeup not supported\n", __func__); + return -ENOTSUPP; + } + + if (!ui->online) { + pr_err("%s: device is not configured\n", __func__); + return -ENODEV; + } + + spin_lock_irqsave(&ui->lock, flags); + if ((readl(USB_PORTSC) & PORTSC_SUSP) == PORTSC_SUSP) { + pr_info("%s: enabling force resume\n", __func__); + writel(readl(USB_PORTSC) | PORTSC_FPR, USB_PORTSC); + } + spin_unlock_irqrestore(&ui->lock, flags); + + return 0; +} + static const struct usb_gadget_ops msm72k_ops = { .get_frame = msm72k_get_frame, .vbus_session = msm72k_udc_vbus_session, .pullup = msm72k_pullup, + .wakeup = msm72k_wakeup, }; static int msm72k_probe(struct platform_device *pdev) diff --git a/drivers/usb/gadget/msm72k_udc.h b/drivers/usb/gadget/msm72k_udc.h index 6843db46dab04..8042b3135dbf2 100644 --- a/drivers/usb/gadget/msm72k_udc.h +++ b/drivers/usb/gadget/msm72k_udc.h @@ -187,6 +187,9 @@ struct ept_queue_item { #define PORTSC_PSPD_LS (1 << 26) #define PORTSC_PSPD_HS (2 << 26) #define PORTSC_PSPD_MASK (3 << 26) +/* suspend and remote wakeup */ +#define PORTSC_FPR (1 << 6) +#define PORTSC_SUSP (1 << 7) /* test mode support */ #define J_TEST (0x0100) From 045f03cb14f7ff1c2ba1efe243e433a062c2c3e4 Mon Sep 17 00:00:00 2001 From: "Krishna, Vamsi" Date: Tue, 10 Feb 2009 21:17:07 +0530 Subject: [PATCH 0235/2556] [ARM] msm: usb: gadget: Add support to initiate Remote Wakeup through Sysfs Add support to initiate Remote Wakeup through sysfs interface. Writing data to this file results in Remote Wakeup Signed-off-by: Mike Lockwood --- drivers/usb/gadget/msm72k_udc.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index ab1d46f4e142f..457f34bf4b20a 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -1518,6 +1518,17 @@ static const struct usb_gadget_ops msm72k_ops = { .wakeup = msm72k_wakeup, }; +static ssize_t usb_remote_wakeup(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_info *ui = the_usb_info; + + msm72k_wakeup(&ui->gadget); + + return count; +} +static DEVICE_ATTR(wakeup, S_IWUSR, 0, usb_remote_wakeup); + static int msm72k_probe(struct platform_device *pdev) { struct resource *res; @@ -1636,6 +1647,11 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) goto fail; } + /* create sysfs node for remote wakeup */ + retval = device_create_file(&ui->gadget.dev, &dev_attr_wakeup); + if (retval != 0) + INFO("failed to create sysfs entry: (wakeup) error: (%d)\n", + retval); INFO("msm72k_udc: registered gadget driver '%s'\n", driver->driver.name); usb_start(ui); @@ -1658,6 +1674,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) if (!driver || driver != dev->driver || !driver->unbind) return -EINVAL; + device_remove_file(&dev->gadget.dev, &dev_attr_wakeup); driver->unbind(&dev->gadget); dev->gadget.dev.driver = NULL; dev->driver = NULL; From 25f5780ac29502edb8d68ec04230dcf9e60ba446 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Sat, 14 Mar 2009 15:04:09 +0530 Subject: [PATCH 0236/2556] [ARM] msm: usb: gadget: Save and restore otgsc register at h/w reset As otgsc register is used by transceiver to read the ID pin status and for interrupts like ID pin change and vbus(B session valid) changes. The state of this register is cleared once the link is reset and has to be restored to the previous state the transceiver is aware of. Hence save the state before the reset and restore the same after reset. Patch provided by Qualcomm Signed-off-by: Mike Lockwood --- drivers/usb/gadget/msm72k_udc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 457f34bf4b20a..71789e110180f 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -942,6 +942,8 @@ static void usb_suspend_phy(struct usb_info *ui) static void usb_reset(struct usb_info *ui) { unsigned long flags; + unsigned otgsc; + INFO("msm72k_udc: reset controller\n"); spin_lock_irqsave(&ui->lock, flags); @@ -954,6 +956,8 @@ static void usb_reset(struct usb_info *ui) msleep(2); #endif + otgsc = readl(USB_OTGSC); + /* RESET */ writel(2, USB_USBCMD); msleep(10); @@ -989,6 +993,7 @@ static void usb_reset(struct usb_info *ui) } /* enable interrupts */ + writel(otgsc, USB_OTGSC); writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR); /* go to RUN mode (D+ pullup enable) */ From a65e37b662c16cfbbf1fdb710251f8a2ecbf90ab Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Sat, 14 Mar 2009 15:04:10 +0530 Subject: [PATCH 0237/2556] [ARM] msm: usb: gadget: Add check to enable pullup if gadget and VBUS are valid Pullup on D+ should be enabled when both vbus and gadget are valid. Patch provided by Qualcomm Signed-off-by: Mike Lockwood --- drivers/usb/gadget/msm72k_udc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 71789e110180f..8c8e793a30f62 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -1483,9 +1483,10 @@ static int msm72k_pullup(struct usb_gadget *_gadget, int is_active) { struct usb_info *ui = container_of(_gadget, struct usb_info, gadget); - if (is_active) - writel(0x00080001, USB_USBCMD); - else + if (is_active) { + if (vbus && ui->driver) + writel(0x00080001, USB_USBCMD); + } else writel(0x00080000, USB_USBCMD); return 0; From 7f46c1a34ed36228a75e33a734e6d00753240367 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Sat, 14 Mar 2009 15:04:11 +0530 Subject: [PATCH 0238/2556] [ARM] msm: usb: gadget: Disable pullup when vbus is off Patch provided by Qualcomm Signed-off-by: Mike Lockwood --- drivers/usb/gadget/msm72k_udc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 8c8e793a30f62..ca76ea1d14f66 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -1088,6 +1088,7 @@ static void usb_do_work(struct work_struct *w) spin_lock_irqsave(&ui->lock, iflags); ui->running = 0; ui->online = 0; + msm72k_pullup(&ui->gadget, 0); spin_unlock_irqrestore(&ui->lock, iflags); /* terminate any transactions, etc */ From bdb13b2ae2593a91aec5a42c757b4310b0fc7518 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Sat, 14 Mar 2009 15:04:12 +0530 Subject: [PATCH 0239/2556] [ARM] msm: usb: gadget: Call _pullup function to enable pullup after reset Pullup needs to enabled only when vbus is valid and gadget driver is registered. Reset function was enabling pullup directly w/o any checks. Call enable pullup function which performs the required checks before enabling the pullup. Patch provided by Qualcomm Signed-off-by: Mike Lockwood --- drivers/usb/gadget/msm72k_udc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index ca76ea1d14f66..839da9d6e6550 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -184,6 +184,7 @@ struct usb_info { static const struct usb_ep_ops msm72k_ep_ops; +static int msm72k_pullup(struct usb_gadget *_gadget, int is_active); static int msm72k_set_halt(struct usb_ep *_ep, int value); static void flush_endpoint(struct msm_endpoint *ept); @@ -997,7 +998,7 @@ static void usb_reset(struct usb_info *ui) writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR); /* go to RUN mode (D+ pullup enable) */ - writel(0x00080001, USB_USBCMD); + msm72k_pullup(&ui->gadget, 1); spin_lock_irqsave(&ui->lock, flags); ui->running = 1; From 97bc3a2ab3b3eabce136935083f320ee2bf914e3 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Sat, 14 Mar 2009 15:04:13 +0530 Subject: [PATCH 0240/2556] [ARM] msm: usb: gadget: Move udc header file to platform folder As the same header file will be used by OTG, Host and gadget device Move header file to a shared location(platform-folder) Patch provided by Qualcomm Signed-off-by: Mike Lockwood --- .../arm/mach-msm/include/mach/msm_hsusb_hw.h | 0 drivers/usb/gadget/msm72k_udc.c | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename drivers/usb/gadget/msm72k_udc.h => arch/arm/mach-msm/include/mach/msm_hsusb_hw.h (100%) diff --git a/drivers/usb/gadget/msm72k_udc.h b/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h similarity index 100% rename from drivers/usb/gadget/msm72k_udc.h rename to arch/arm/mach-msm/include/mach/msm_hsusb_hw.h diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 839da9d6e6550..aaba273459476 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -39,12 +39,12 @@ #include #include #include +#include static const char driver_name[] = "msm72k_udc"; /* #define DEBUG */ /* #define VERBOSE */ -#include "msm72k_udc.h" #define MSM_USB_BASE ((unsigned) ui->addr) From 223d093a4357a16fa30eb9e225f531217a98206e Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Sat, 14 Mar 2009 15:04:14 +0530 Subject: [PATCH 0241/2556] [ARM] msm: usb: gadget: Do not suspend the phy when VBUS is off When vbus is off driver tries to put the phy in suspend mode. As we dont have complete infrastructure for LPM yet; Dont put phy in LPM LPM as we will not be able to wakeup from the same. LPM support will be implemented in a separate patch Patch provided by Qualcomm Signed-off-by: Mike Lockwood --- drivers/usb/gadget/msm72k_udc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index aaba273459476..9ae42d5a77ad9 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -936,8 +936,6 @@ static void usb_suspend_phy(struct usb_info *ui) ulpi_write(ui, (1 << 1) | (1 << 3), 0x12); /* disable interface protect circuit to drop current consumption */ ulpi_write(ui, (1 << 7), 0x08); - /* clear the SuspendM bit -> suspend the PHY */ - ulpi_write(ui, 1 << 6, 0x06); } static void usb_reset(struct usb_info *ui) From 48b6d133478cfa676241f356001caeaada622496 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 24 Nov 2008 19:27:15 -0500 Subject: [PATCH 0242/2556] [ARM] msm: trout: Refactor msm_hsusb platform data for msm72k_udc driver. Signed-off-by: Mike Lockwood [ARM] msm: hsusb: Properly initialize the internal phy This procedure is necessary for internal USB PHY on qsd8x50. Change-Id: I979f38596c980ca5d5440bb4045da664d8e8f328 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-trout.c | 60 ---------------------- arch/arm/mach-msm/include/mach/msm_hsusb.h | 35 +++++++++++++ 2 files changed, 35 insertions(+), 60 deletions(-) create mode 100644 arch/arm/mach-msm/include/mach/msm_hsusb.h diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index f9d8cb97af51d..aefb7d7cfdcc8 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -28,9 +28,6 @@ #include #include #include -#ifdef CONFIG_USB_FUNCTION -#include -#endif #include @@ -60,9 +57,7 @@ #include "gpio_chip.h" #include -#ifdef CONFIG_USB_FUNCTION #include -#endif #include @@ -423,8 +418,6 @@ static struct platform_device sd_door_switch = { }, }; -#ifdef CONFIG_USB_FUNCTION - /* adjust eye diagram, disable vbusvalid interrupts */ static int trout_phy_init_seq[] = { 0x40, 0x31, 0x1D, 0x0D, 0x1D, 0x10, -1 }; @@ -436,57 +429,10 @@ static void trout_phy_reset(void) mdelay(10); } -static char *trout_usb_functions[] = { -#if defined(CONFIG_USB_FUNCTION_MASS_STORAGE) || defined(CONFIG_USB_FUNCTION_UMS) - "usb_mass_storage", -#endif -#ifdef CONFIG_USB_FUNCTION_ADB - "adb", -#endif -}; - -static struct msm_hsusb_product trout_usb_products[] = { - { - .product_id = 0x0c01, - .functions = 0x00000001, /* "usb_mass_storage" only */ - }, - { - .product_id = 0x0c02, - .functions = 0x00000003, /* "usb_mass_storage" and "adb" */ - }, -}; - static struct msm_hsusb_platform_data msm_hsusb_pdata = { .phy_reset = trout_phy_reset, .phy_init_seq = trout_phy_init_seq, - .vendor_id = 0x0bb4, - .product_id = 0x0c02, - .version = 0x0100, - .product_name = "Android Phone", - .manufacturer_name = "HTC", - - .functions = trout_usb_functions, - .num_functions = ARRAY_SIZE(trout_usb_functions), - .products = trout_usb_products, - .num_products = ARRAY_SIZE(trout_usb_products), -}; - -static struct usb_mass_storage_platform_data mass_storage_pdata = { - .nluns = 1, - .buf_size = 16384, - .vendor = "HTC ", - .product = "Android Phone ", - .release = 0x0100, -}; - -static struct platform_device usb_mass_storage_device = { - .name = "usb_mass_storage", - .id = -1, - .dev = { - .platform_data = &mass_storage_pdata, - }, }; -#endif static struct resource trout_ram_console_resource[] = { { @@ -593,10 +539,7 @@ static struct platform_device *devices[] __initdata = { #ifdef CONFIG_SERIAL_MSM_HS &msm_device_uart_dm1, #endif -#ifdef CONFIG_USB_FUNCTION &msm_device_hsusb, - &usb_mass_storage_device, -#endif &trout_nav_device, &trout_reset_keys_device, &android_leds, @@ -629,9 +572,6 @@ module_param_named(disable_uart3, opt_disable_uart3, uint, 0); static int __init trout_serialno_setup(char *str) { -#ifdef CONFIG_USB_FUNCTION - msm_hsusb_pdata.serial_number = str; -#endif return 1; } diff --git a/arch/arm/mach-msm/include/mach/msm_hsusb.h b/arch/arm/mach-msm/include/mach/msm_hsusb.h new file mode 100644 index 0000000000000..534db4e440cb8 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_hsusb.h @@ -0,0 +1,35 @@ +/* linux/include/asm-arm/arch-msm/hsusb.h + * + * Copyright (C) 2008 Google, Inc. + * Author: Brian Swetland + * + * 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 __ASM_ARCH_MSM_HSUSB_H +#define __ASM_ARCH_MSM_HSUSB_H + +#include + +/* platform device data for msm_hsusb driver */ + +struct msm_hsusb_platform_data { + /* hard reset the ULPI PHY */ + void (*phy_reset)(void); + + /* (de)assert the reset to the usb core */ + void (*hw_reset)(bool enable); + + /* val, reg pairs terminated by -1 */ + int *phy_init_seq; +}; + +#endif From 95eb6ba6fc1a8b6e5037a170b3c238f227741116 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Fri, 28 Nov 2008 22:15:05 -0500 Subject: [PATCH 0243/2556] [ARM] msm: trout: Support for android_usb composite USB gadget driver. Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/board-trout.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index aefb7d7cfdcc8..2a403a4e1d19a 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -28,6 +28,9 @@ #include #include #include +#ifdef CONFIG_USB_ANDROID +#include +#endif #include @@ -434,6 +437,26 @@ static struct msm_hsusb_platform_data msm_hsusb_pdata = { .phy_init_seq = trout_phy_init_seq, }; +#ifdef CONFIG_USB_ANDROID +static struct android_usb_platform_data android_usb_pdata = { + .vendor_id = 0x0bb4, + .product_id = 0x0c01, + .adb_product_id = 0x0c02, + .version = 0x0100, + .product_name = "Android Phone", + .manufacturer_name = "HTC", + .nluns = 1, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; +#endif + static struct resource trout_ram_console_resource[] = { { .start = MSM_RAM_CONSOLE_BASE, @@ -540,6 +563,9 @@ static struct platform_device *devices[] __initdata = { &msm_device_uart_dm1, #endif &msm_device_hsusb, +#ifdef CONFIG_USB_ANDROID + &android_usb_device, +#endif &trout_nav_device, &trout_reset_keys_device, &android_leds, @@ -572,6 +598,9 @@ module_param_named(disable_uart3, opt_disable_uart3, uint, 0); static int __init trout_serialno_setup(char *str) { +#ifdef CONFIG_USB_ANDROID + android_usb_pdata.serial_number = str; +#endif return 1; } From d8b6a379a690ccdce2f87a4195b1aa35bad64650 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Mon, 8 Dec 2008 14:48:32 -0800 Subject: [PATCH 0244/2556] [ARM] msm: adsp: verify VIDEOTASK MPEG-4 commands Signed-off-by: Iliyan Malchev --- .../mach-msm/qdsp5/adsp_video_verify_cmd.c | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c index cae242fa15e9a..fdad0551d237f 100644 --- a/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c +++ b/arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c @@ -80,14 +80,14 @@ static int verify_vdec_pkt_cmd(struct msm_adsp_module *module, unsigned long subframe_pkt_addr; unsigned long subframe_pkt_size; viddec_cmd_frame_header_packet *frame_header_pkt; - int i; + int i, num_addr, skip; unsigned short *frame_buffer_high, *frame_buffer_low; unsigned long frame_buffer_size; unsigned short frame_buffer_size_high, frame_buffer_size_low; DLOG("cmd_size %d cmd_id %d cmd_data %x\n", cmd_size, cmd_id, cmd_data); if (cmd_id != VIDDEC_CMD_SUBFRAME_PKT) { - printk(KERN_INFO "adsp_video:unknown video packet %u\n", + printk(KERN_INFO "adsp_video: unknown video packet %u\n", cmd_id); return 0; } @@ -108,8 +108,18 @@ static int verify_vdec_pkt_cmd(struct msm_adsp_module *module, /* deref those ptrs and check if they are a frame header packet */ frame_header_pkt = (viddec_cmd_frame_header_packet *)subframe_pkt_addr; - if (frame_header_pkt->packet_id != (unsigned short)0xB201) + switch (frame_header_pkt->packet_id) { + case 0xB201: /* h.264 */ + num_addr = skip = 8; + break; + case 0x4D01: /* mpeg-4 and h.263 */ + num_addr = 3; + skip = 0; + break; + default: return 0; + } + frame_buffer_high = &frame_header_pkt->frame_buffer_0_high; frame_buffer_low = &frame_header_pkt->frame_buffer_0_low; frame_buffer_size = (frame_header_pkt->x_dimension * @@ -117,7 +127,7 @@ static int verify_vdec_pkt_cmd(struct msm_adsp_module *module, ptr_to_high_low_short((void *)frame_buffer_size, &frame_buffer_size_high, &frame_buffer_size_low); - for (i = 0; i < 8; i++) { + for (i = 0; i < num_addr; i++) { if (pmem_fixup_high_low(frame_buffer_high, frame_buffer_low, frame_buffer_size_high, frame_buffer_size_low, @@ -127,8 +137,10 @@ static int verify_vdec_pkt_cmd(struct msm_adsp_module *module, frame_buffer_high += 2; frame_buffer_low += 2; } - if (pmem_fixup_high_low(&frame_header_pkt->output_frame_buffer_high, - &frame_header_pkt->output_frame_buffer_low, + /* Patch the output buffer. */ + frame_buffer_high += 2*skip; + frame_buffer_low += 2*skip; + if (pmem_fixup_high_low(frame_buffer_high, frame_buffer_low, frame_buffer_size_high, frame_buffer_size_low, module, NULL, NULL)) return -1; From ec6e51dd3558ee23c0d7bae664333a6f054766d7 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Mon, 8 Dec 2008 15:17:13 -0800 Subject: [PATCH 0245/2556] [ARM] msm: adsp: verify VIDEOENCTASK commands Signed-off-by: Iliyan Malchev --- .../include/mach/qdsp5/qdsp5venccmdi.h | 212 ++++++++++++++++ arch/arm/mach-msm/qdsp5/Makefile | 1 + arch/arm/mach-msm/qdsp5/adsp.h | 31 ++- arch/arm/mach-msm/qdsp5/adsp_6225.c | 13 +- .../mach-msm/qdsp5/adsp_videoenc_verify_cmd.c | 235 ++++++++++++++++++ 5 files changed, 475 insertions(+), 17 deletions(-) create mode 100755 arch/arm/mach-msm/include/mach/qdsp5/qdsp5venccmdi.h create mode 100644 arch/arm/mach-msm/qdsp5/adsp_videoenc_verify_cmd.c diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5venccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5venccmdi.h new file mode 100755 index 0000000000000..4cbe789ac7c96 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5venccmdi.h @@ -0,0 +1,212 @@ +#ifndef QDSP5VIDENCCMDI_H +#define QDSP5VIDENCCMDI_H + +/*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====* + + V I D E O E N C O D E R I N T E R N A L C O M M A N D S + +GENERAL DESCRIPTION + This file contains defintions of format blocks of commands + that are accepted by VIDENC Task + +REFERENCES + None + +EXTERNALIZED FUNCTIONS + None + +Copyright(c) 2008 by QUALCOMM, Incorporated. +*====*====*====*====*====*====*====*====*====*====*====*====*====*====*====*/ +/*=========================================================================== + + EDIT HISTORY FOR FILE + +This section contains comments describing changes made to this file. +Notice that changes are listed in reverse chronological order. + +Revision History: + +when who what, where, why +-------- --- ---------------------------------------------------------- +09/25/08 umeshp initial version +===========================================================================*/ + + #define VIDENC_CMD_CFG 0x0000 + #define VIDENC_CMD_ACTIVE 0x0001 + #define VIDENC_CMD_IDLE 0x0002 + #define VIDENC_CMD_FRAME_START 0x0003 + #define VIDENC_CMD_STATUS_QUERY 0x0004 + #define VIDENC_CMD_RC_CFG 0x0005 + #define VIDENC_CMD_DIS_CFG 0x0006 + #define VIDENC_CMD_DIS 0x0007 + #define VIDENC_CMD_INTRA_REFRESH 0x0008 + #define VIDENC_CMD_DIGITAL_ZOOM 0x0009 + + +/* + * Command to pass the frame message information to VIDENC + */ + + +#define VIDENC_CMD_FRAME_START_LEN \ + sizeof(videnc_cmd_frame_start) + +typedef struct { + unsigned short cmd_id; + unsigned short frame_info; + unsigned short frame_rho_budget_word_high; + unsigned short frame_rho_budget_word_low; + unsigned short input_luma_addr_high; + unsigned short input_luma_addr_low; + unsigned short input_chroma_addr_high; + unsigned short input_chroma_addr_low; + unsigned short ref_vop_buf_ptr_high; + unsigned short ref_vop_buf_ptr_low; + unsigned short enc_pkt_buf_ptr_high; + unsigned short enc_pkt_buf_ptr_low; + unsigned short enc_pkt_buf_size_high; + unsigned short enc_pkt_buf_size_low; + unsigned short unfilt_recon_vop_buf_ptr_high; + unsigned short unfilt_recon_vop_buf_ptr_low; + unsigned short filt_recon_vop_buf_ptr_high; + unsigned short filt_recon_vop_buf_ptr_low; +} __attribute__((packed)) videnc_cmd_frame_start; + +/* + * Command to pass the frame-level digital stabilization parameters to VIDENC + */ + + +#define VIDENC_CMD_DIS_LEN \ + sizeof(videnc_cmd_dis) + +typedef struct { + unsigned short cmd_id; + unsigned short vfe_out_prev_luma_addr_high; + unsigned short vfe_out_prev_luma_addr_low; + unsigned short stabilization_info; +} __attribute__((packed)) videnc_cmd_dis; + +/* + * Command to pass the codec related parameters to VIDENC + */ + + +#define VIDENC_CMD_CFG_LEN \ + sizeof(videnc_cmd_cfg) + +typedef struct { + unsigned short cmd_id; + unsigned short cfg_info_0; + unsigned short cfg_info_1; + unsigned short four_mv_threshold; + unsigned short ise_fse_mv_cost_fac; + unsigned short venc_frame_dim; + unsigned short venc_DM_partition; +} __attribute__((packed)) videnc_cmd_cfg; + +/* + * Command to start the video encoding + */ + + +#define VIDENC_CMD_ACTIVE_LEN \ + sizeof(videnc_cmd_active) + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) videnc_cmd_active; + +/* + * Command to stop the video encoding + */ + + +#define VIDENC_CMD_IDLE_LEN \ + sizeof(videnc_cmd_idle) + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) videnc_cmd_idle; + +/* + * Command to query staus of VIDENC + */ + + +#define VIDENC_CMD_STATUS_QUERY_LEN \ + sizeof(videnc_cmd_status_query) + +typedef struct { + unsigned short cmd_id; +} __attribute__((packed)) videnc_cmd_status_query; + +/* + * Command to set rate control for a frame + */ + + +#define VIDENC_CMD_RC_CFG_LEN \ + sizeof(videnc_cmd_rc_cfg) + +typedef struct { + unsigned short cmd_id; + unsigned short max_frame_qp_delta; + unsigned short max_min_frame_qp; +} __attribute__((packed)) videnc_cmd_rc_cfg; + +/* + * Command to set intra-refreshing + */ + + +#define VIDENC_CMD_INTRA_REFRESH_LEN \ + sizeof(videnc_cmd_intra_refresh) + +typedef struct { + unsigned short cmd_id; + unsigned short num_mb_refresh; + unsigned short mb_index[15]; +} __attribute__((packed)) videnc_cmd_intra_refresh; + +/* + * Command to pass digital zoom information to the VIDENC + */ +#define VIDENC_CMD_DIGITAL_ZOOM_LEN \ + sizeof(videnc_cmd_digital_zoom) + +typedef struct { + unsigned short cmd_id; + unsigned short digital_zoom_en; + unsigned short luma_frame_shift_X; + unsigned short luma_frame_shift_Y; + unsigned short up_ip_luma_rows; + unsigned short up_ip_luma_cols; + unsigned short up_ip_chroma_rows; + unsigned short up_ip_chroma_cols; + unsigned short luma_ph_incr_V_low; + unsigned short luma_ph_incr_V_high; + unsigned short luma_ph_incr_H_low; + unsigned short luma_ph_incr_H_high; + unsigned short chroma_ph_incr_V_low; + unsigned short chroma_ph_incr_V_high; + unsigned short chroma_ph_incr_H_low; + unsigned short chroma_ph_incr_H_high; +} __attribute__((packed)) videnc_cmd_digital_zoom; + +/* + * Command to configure digital stabilization parameters + */ + +#define VIDENC_CMD_DIS_CFG_LEN \ + sizeof(videnc_cmd_dis_cfg) + +typedef struct { + unsigned short cmd_id; + unsigned short image_stab_subf_start_row_col; + unsigned short image_stab_subf_dim; + unsigned short image_stab_info_0; +} __attribute__((packed)) videnc_cmd_dis_cfg; + + +#endif diff --git a/arch/arm/mach-msm/qdsp5/Makefile b/arch/arm/mach-msm/qdsp5/Makefile index 97461e88be402..7bd5ce575a52f 100644 --- a/arch/arm/mach-msm/qdsp5/Makefile +++ b/arch/arm/mach-msm/qdsp5/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_MSM_AMSS_VERSION_6225) += adsp_6225.o obj-y += adsp.o adsp_driver.o obj-y += adsp_video_verify_cmd.o +obj-y += adsp_videoenc_verify_cmd.o obj-y += adsp_jpeg_verify_cmd.o adsp_jpeg_patch_event.o obj-y += adsp_vfe_verify_cmd.o adsp_vfe_patch_event.o obj-y += adsp_lpm_verify_cmd.o diff --git a/arch/arm/mach-msm/qdsp5/adsp.h b/arch/arm/mach-msm/qdsp5/adsp.h index ffe1faf4db21b..2b67ef47d3c71 100644 --- a/arch/arm/mach-msm/qdsp5/adsp.h +++ b/arch/arm/mach-msm/qdsp5/adsp.h @@ -41,6 +41,10 @@ int adsp_lpm_verify_cmd(struct msm_adsp_module *module, int adsp_video_verify_cmd(struct msm_adsp_module *module, unsigned int queue_id, void *cmd_data, size_t cmd_size); +int adsp_videoenc_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); + struct adsp_event; @@ -48,7 +52,7 @@ int adsp_vfe_patch_event(struct msm_adsp_module *module, struct adsp_event *event); int adsp_jpeg_patch_event(struct msm_adsp_module *module, - struct adsp_event *event); + struct adsp_event *event); struct adsp_module_info { @@ -59,21 +63,21 @@ struct adsp_module_info { unsigned long clk_rate; int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *, size_t); - int (*patch_event) (struct msm_adsp_module*, struct adsp_event *); + int (*patch_event) (struct msm_adsp_module*, struct adsp_event *); }; #define ADSP_EVENT_MAX_SIZE 496 struct adsp_event { - struct list_head list; - uint32_t size; /* always in bytes */ - uint16_t msg_id; - uint16_t type; /* 0 for msgs (from aDSP), 1 for events (from ARM9) */ - int is16; /* always 0 (msg is 32-bit) when the event type is 1(ARM9) */ - union { - uint16_t msg16[ADSP_EVENT_MAX_SIZE / 2]; - uint32_t msg32[ADSP_EVENT_MAX_SIZE / 4]; - } data; + struct list_head list; + uint32_t size; /* always in bytes */ + uint16_t msg_id; + uint16_t type; /* 0 for msgs (from aDSP), 1 for events (from ARM9) */ + int is16; /* always 0 (msg is 32-bit) when the event type is 1(ARM9) */ + union { + uint16_t msg16[ADSP_EVENT_MAX_SIZE / 2]; + uint32_t msg32[ADSP_EVENT_MAX_SIZE / 4]; + } data; }; struct adsp_info { @@ -196,12 +200,13 @@ struct msm_adsp_module { struct hlist_head pmem_regions; int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *, size_t); - int (*patch_event) (struct msm_adsp_module*, struct adsp_event *); + int (*patch_event) (struct msm_adsp_module*, struct adsp_event *); }; extern void msm_adsp_publish_cdevs(struct msm_adsp_module *, unsigned); extern int adsp_init_info(struct adsp_info *info); -extern struct msm_adsp_module *find_adsp_module_by_id(struct adsp_info *info, uint32_t id); +extern struct msm_adsp_module *find_adsp_module_by_id(struct adsp_info *info, + uint32_t id); /* Command Queue Indexes */ diff --git a/arch/arm/mach-msm/qdsp5/adsp_6225.c b/arch/arm/mach-msm/qdsp5/adsp_6225.c index ceb9aa02719ef..6806cc12be7d7 100644 --- a/arch/arm/mach-msm/qdsp5/adsp_6225.c +++ b/arch/arm/mach-msm/qdsp5/adsp_6225.c @@ -284,7 +284,7 @@ static uint32_t *qdsp_queue_offset_table[] = { #define QDSP_MODULE(n, clkname, clkrate, verify_cmd_func, patch_event_func) \ { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n, \ - .clk_name=clkname, .clk_rate=clkrate, \ + .clk_name = clkname, .clk_rate = clkrate, \ .verify_cmd = verify_cmd_func, .patch_event = patch_event_func } static struct adsp_module_info module_info[] = { @@ -292,12 +292,17 @@ static struct adsp_module_info module_info[] = { QDSP_MODULE(AUDPPTASK, NULL, 0, NULL, NULL), QDSP_MODULE(AUDRECTASK, NULL, 0, NULL, NULL), QDSP_MODULE(AUDPREPROCTASK, NULL, 0, NULL, NULL), - QDSP_MODULE(VFETASK, "vfe_clk", 0, adsp_vfe_verify_cmd, adsp_vfe_patch_event), + QDSP_MODULE(VFETASK, "vfe_clk", 0, adsp_vfe_verify_cmd, + adsp_vfe_patch_event), QDSP_MODULE(QCAMTASK, NULL, 0, NULL, NULL), QDSP_MODULE(LPMTASK, NULL, 0, adsp_lpm_verify_cmd, NULL), - QDSP_MODULE(JPEGTASK, "vdc_clk", 0, adsp_jpeg_verify_cmd, adsp_jpeg_patch_event), - QDSP_MODULE(VIDEOTASK, "vdc_clk", 96000000, adsp_video_verify_cmd, NULL), + QDSP_MODULE(JPEGTASK, "vdc_clk", 0, adsp_jpeg_verify_cmd, + adsp_jpeg_patch_event), + QDSP_MODULE(VIDEOTASK, "vdc_clk", 96000000, + adsp_video_verify_cmd, NULL), QDSP_MODULE(VDEC_LP_MODE, NULL, 0, NULL, NULL), + QDSP_MODULE(VIDEOENCTASK, "vdc_clk", 96000000, + adsp_videoenc_verify_cmd, NULL), }; int adsp_init_info(struct adsp_info *info) diff --git a/arch/arm/mach-msm/qdsp5/adsp_videoenc_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_videoenc_verify_cmd.c new file mode 100644 index 0000000000000..ee3744950523b --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_videoenc_verify_cmd.c @@ -0,0 +1,235 @@ +/* arch/arm/mach-msm/qdsp5/adsp_video_verify_cmd.c + * + * Verificion code for aDSP VENC packets from userspace. + * + * Copyright (c) 2008 QUALCOMM Incorporated + * Copyright (C) 2008 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 + +#define ADSP_DEBUG_MSGS 0 +#if ADSP_DEBUG_MSGS +#define DLOG(fmt,args...) \ + do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \ + ##args); } \ + while (0) +#else +#define DLOG(x...) do {} while (0) +#endif + +#include +#include "adsp.h" + + +static unsigned short x_dimension, y_dimension; + +static inline void *high_low_short_to_ptr(unsigned short high, + unsigned short low) +{ + return (void *)((((unsigned long)high) << 16) | ((unsigned long)low)); +} + +static inline void ptr_to_high_low_short(void *ptr, unsigned short *high, + unsigned short *low) +{ + *high = (unsigned short)((((unsigned long)ptr) >> 16) & 0xffff); + *low = (unsigned short)((unsigned long)ptr & 0xffff); +} + +static int pmem_fixup_high_low(unsigned short *high, + unsigned short *low, + unsigned short size_high, + unsigned short size_low, + struct msm_adsp_module *module, + unsigned long *addr, unsigned long *size) +{ + void *phys_addr; + unsigned long phys_size; + unsigned long kvaddr; + + phys_addr = high_low_short_to_ptr(*high, *low); + phys_size = (unsigned long)high_low_short_to_ptr(size_high, size_low); + DLOG("virt %x %x\n", phys_addr, phys_size); + if (adsp_pmem_fixup_kvaddr(module, &phys_addr, &kvaddr, phys_size)) { + DLOG("ah%x al%x sh%x sl%x addr %x size %x\n", + *high, *low, size_high, size_low, phys_addr, phys_size); + return -1; + } + ptr_to_high_low_short(phys_addr, high, low); + DLOG("phys %x %x\n", phys_addr, phys_size); + if (addr) + *addr = kvaddr; + if (size) + *size = phys_size; + return 0; +} + +static int verify_venc_cmd(struct msm_adsp_module *module, + void *cmd_data, size_t cmd_size) +{ + unsigned short cmd_id = ((unsigned short *)cmd_data)[0]; + unsigned long frame_buf_size, luma_buf_size, chroma_buf_size; + unsigned short frame_buf_size_high, frame_buf_size_low; + unsigned short luma_buf_size_high, luma_buf_size_low; + unsigned short chroma_buf_size_high, chroma_buf_size_low; + videnc_cmd_cfg *config_cmd; + videnc_cmd_frame_start *frame_cmd; + videnc_cmd_dis *dis_cmd; + + DLOG("cmd_size %d cmd_id %d cmd_data %x\n", cmd_size, cmd_id, cmd_data); + switch (cmd_id) { + case VIDENC_CMD_ACTIVE: + if (cmd_size < sizeof(videnc_cmd_active)) + return -1; + break; + case VIDENC_CMD_IDLE: + if (cmd_size < sizeof(videnc_cmd_idle)) + return -1; + x_dimension = y_dimension = 0; + break; + case VIDENC_CMD_STATUS_QUERY: + if (cmd_size < sizeof(videnc_cmd_status_query)) + return -1; + break; + case VIDENC_CMD_RC_CFG: + if (cmd_size < sizeof(videnc_cmd_rc_cfg)) + return -1; + break; + case VIDENC_CMD_INTRA_REFRESH: + if (cmd_size < sizeof(videnc_cmd_intra_refresh)) + return -1; + break; + case VIDENC_CMD_DIGITAL_ZOOM: + if (cmd_size < sizeof(videnc_cmd_digital_zoom)) + return -1; + break; + case VIDENC_CMD_DIS_CFG: + if (cmd_size < sizeof(videnc_cmd_dis_cfg)) + return -1; + break; + case VIDENC_CMD_CFG: + if (cmd_size < sizeof(videnc_cmd_cfg)) + return -1; + config_cmd = (videnc_cmd_cfg *)cmd_data; + x_dimension = ((config_cmd->venc_frame_dim) & 0xFF00)>>8; + x_dimension = x_dimension*16; + y_dimension = (config_cmd->venc_frame_dim) & 0xFF; + y_dimension = y_dimension * 16; + break; + case VIDENC_CMD_FRAME_START: + if (cmd_size < sizeof(videnc_cmd_frame_start)) + return -1; + frame_cmd = (videnc_cmd_frame_start *)cmd_data; + luma_buf_size = x_dimension * y_dimension; + chroma_buf_size = luma_buf_size>>1; + frame_buf_size = luma_buf_size + chroma_buf_size; + ptr_to_high_low_short((void *)luma_buf_size, + &luma_buf_size_high, + &luma_buf_size_low); + ptr_to_high_low_short((void *)chroma_buf_size, + &chroma_buf_size_high, + &chroma_buf_size_low); + ptr_to_high_low_short((void *)frame_buf_size, + &frame_buf_size_high, + &frame_buf_size_low); + /* Address of raw Y data. */ + if (pmem_fixup_high_low(&frame_cmd->input_luma_addr_high, + &frame_cmd->input_luma_addr_low, + luma_buf_size_high, + luma_buf_size_low, + module, + NULL, NULL)) + return -1; + /* Address of raw CbCr data */ + if (pmem_fixup_high_low(&frame_cmd->input_chroma_addr_high, + &frame_cmd->input_chroma_addr_low, + chroma_buf_size_high, + chroma_buf_size_low, + module, + NULL, NULL)) + return -1; + /* Reference VOP */ + if (pmem_fixup_high_low(&frame_cmd->ref_vop_buf_ptr_high, + &frame_cmd->ref_vop_buf_ptr_low, + frame_buf_size_high, + frame_buf_size_low, + module, + NULL, NULL)) + return -1; + /* Encoded Packet Address */ + if (pmem_fixup_high_low(&frame_cmd->enc_pkt_buf_ptr_high, + &frame_cmd->enc_pkt_buf_ptr_low, + frame_cmd->enc_pkt_buf_size_high, + frame_cmd->enc_pkt_buf_size_low, + module, + NULL, NULL)) + return -1; + /* Unfiltered VOP Buffer Address */ + if (pmem_fixup_high_low( + &frame_cmd->unfilt_recon_vop_buf_ptr_high, + &frame_cmd->unfilt_recon_vop_buf_ptr_low, + frame_buf_size_high, + frame_buf_size_low, + module, + NULL, NULL)) + return -1; + /* Filtered VOP Buffer Address */ + if (pmem_fixup_high_low(&frame_cmd->filt_recon_vop_buf_ptr_high, + &frame_cmd->filt_recon_vop_buf_ptr_low, + frame_buf_size_high, + frame_buf_size_low, + module, + NULL, NULL)) + return -1; + break; + case VIDENC_CMD_DIS: + if (cmd_size < sizeof(videnc_cmd_dis)) + return -1; + dis_cmd = (videnc_cmd_dis *)cmd_data; + luma_buf_size = x_dimension * y_dimension; + ptr_to_high_low_short((void *)luma_buf_size, + &luma_buf_size_high, + &luma_buf_size_low); + /* Prev VFE Luma Output Address */ + if (pmem_fixup_high_low(&dis_cmd->vfe_out_prev_luma_addr_high, + &dis_cmd->vfe_out_prev_luma_addr_low, + luma_buf_size_high, + luma_buf_size_low, + module, + NULL, NULL)) + return -1; + break; + default: + printk(KERN_INFO "adsp_video:unknown encoder video command %u\n", + cmd_id); + return 0; + } + + return 0; +} + + +int adsp_videoenc_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size) +{ + switch (queue_id) { + case QDSP_mpuVEncCmdQueue: + DLOG("\n"); + return verify_venc_cmd(module, cmd_data, cmd_size); + default: + printk(KERN_INFO "unknown video queue %u\n", queue_id); + return 0; + } +} + From 62c4ef4b0d79ee28fffeb2b12752d4defdea373e Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Mon, 8 Dec 2008 15:23:46 -0800 Subject: [PATCH 0246/2556] [ARM] msm: adsp: fix an off-by-one bug Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/qdsp5/adsp_driver.c | 34 ++++++++++++++++----------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-msm/qdsp5/adsp_driver.c b/arch/arm/mach-msm/qdsp5/adsp_driver.c index 343e0cfde1ef0..a70cec86b9415 100644 --- a/arch/arm/mach-msm/qdsp5/adsp_driver.c +++ b/arch/arm/mach-msm/qdsp5/adsp_driver.c @@ -82,7 +82,7 @@ static struct adsp_device *inode_to_device(struct inode *inode); typeof(r1) __r1 = r1; \ typeof(r2) __r2 = r2; \ typeof(__r2->vaddr) __v = __r2->vaddr; \ - typeof(__v) __e = __v + __r2->len; \ + typeof(__v) __e = __v + __r2->len - 1; \ int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \ res; \ }) @@ -133,7 +133,8 @@ static int adsp_pmem_add(struct msm_adsp_module *module, goto end; } - if ((rc = adsp_pmem_check(module, info->vaddr, len)) < 0) { + rc = adsp_pmem_check(module, info->vaddr, len); + if (rc < 0) { put_pmem_file(file); kfree(region); goto end; @@ -152,7 +153,7 @@ static int adsp_pmem_add(struct msm_adsp_module *module, } static int adsp_pmem_lookup_vaddr(struct msm_adsp_module *module, void **addr, - unsigned long len, struct adsp_pmem_region** region) + unsigned long len, struct adsp_pmem_region **region) { struct hlist_node *node; void *vaddr = *addr; @@ -172,19 +173,24 @@ static int adsp_pmem_lookup_vaddr(struct msm_adsp_module *module, void **addr, */ match_count++; - if (!*region) *region = region_elt; + if (!*region) + *region = region_elt; } } if (match_count > 1) { - printk(KERN_ERR "adsp: module %s: multiple hits for vaddr %p, len %ld\n", - module->name, vaddr, len); - hlist_for_each_entry(region_elt, node, &module->pmem_regions, list) { + printk(KERN_ERR "adsp: module %s: " + "multiple hits for vaddr %p, len %ld\n", + module->name, vaddr, len); + hlist_for_each_entry(region_elt, node, + &module->pmem_regions, list) { if (vaddr >= region_elt->vaddr && vaddr < region_elt->vaddr + region_elt->len && vaddr + len <= region_elt->vaddr + region_elt->len) - printk(KERN_ERR "\t%p, %ld --> %p\n", region_elt->vaddr, - region_elt->len, (void *)region_elt->paddr); + printk(KERN_ERR "\t%p, %ld --> %p\n", + region_elt->vaddr, + region_elt->len, + (void *)region_elt->paddr); } } @@ -295,7 +301,7 @@ static int adsp_events_pending(struct adsp_device *adev) } static int adsp_pmem_lookup_paddr(struct msm_adsp_module *module, void **addr, - struct adsp_pmem_region** region) + struct adsp_pmem_region **region) { struct hlist_node *node; unsigned long paddr = (unsigned long)(*addr); @@ -332,10 +338,10 @@ int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr) static int adsp_patch_event(struct msm_adsp_module *module, struct adsp_event *event) { - /* call the per-module msg verifier */ - if (module->patch_event) - return module->patch_event(module, event); - return 0; + /* call the per-module msg verifier */ + if (module->patch_event) + return module->patch_event(module, event); + return 0; } static long adsp_get_event(struct adsp_device *adev, void __user *arg) From ea2507ba3b1eb874ad27eca104108bed113d8a18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 10 Dec 2008 19:45:39 -0800 Subject: [PATCH 0247/2556] [ARM] msm: trout-h2w: Don't call request_irq with interrupts disabled. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/board-trout-h2w.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-msm/board-trout-h2w.c b/arch/arm/mach-msm/board-trout-h2w.c index f8282b4e4899f..66b82707a3b31 100644 --- a/arch/arm/mach-msm/board-trout-h2w.c +++ b/arch/arm/mach-msm/board-trout-h2w.c @@ -367,7 +367,6 @@ device_initcall(h2w_debug_init); static int trout_h2w_probe(struct platform_device *pdev) { int ret; - unsigned long irq_flags; printk(KERN_INFO "H2W: Registering H2W (headset) driver\n"); hi = kzalloc(sizeof(struct h2w_info), GFP_KERNEL); @@ -431,13 +430,13 @@ static int trout_h2w_probe(struct platform_device *pdev) hrtimer_init(&hi->btn_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hi->btn_timer.function = button_event_timer_func; - local_irq_save(irq_flags); - ret = request_irq(hi->irq, detect_irq_handler, IRQF_TRIGGER_LOW, "h2w_detect", NULL); if (ret < 0) goto err_request_detect_irq; + /* Disable button until plugged in */ + set_irq_flags(hi->irq_btn, IRQF_VALID | IRQF_NOAUTOEN); ret = request_irq(hi->irq_btn, button_irq_handler, IRQF_TRIGGER_LOW, "h2w_button", NULL); if (ret < 0) @@ -450,10 +449,6 @@ static int trout_h2w_probe(struct platform_device *pdev) if (ret < 0) goto err_request_input_dev; - /* Disable button until plugged in */ - disable_irq(hi->irq_btn); - local_irq_restore(irq_flags); - hi->input = input_allocate_device(); if (!hi->input) { ret = -ENOMEM; @@ -477,7 +472,6 @@ static int trout_h2w_probe(struct platform_device *pdev) err_request_h2w_headset_button_irq: free_irq(hi->irq, 0); err_request_detect_irq: - local_irq_restore(irq_flags); err_get_button_irq_num_failed: err_get_h2w_detect_irq_num_failed: err_set_button_gpio: From 462d34c27d974f8cefab68f12dcc03f2ec9ca62c Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Tue, 9 Dec 2008 09:38:21 -0800 Subject: [PATCH 0248/2556] [ARM] msm: msm_serial_hs: Wakeup on uart1dm irq Other minor fixes: disable_irq in the isr is redundant because of spinlock, removed. Removed a stray debug comment. Signed-off-by: Nick Pelly --- drivers/tty/serial/msm_serial_hs.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index b94677222cb29..742c833447630 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -603,7 +603,7 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, spin_lock_irqsave(&uport->lock, flags); - tty = uport->info->port.tty; //npell info->tty ?? + tty = uport->info->port.tty; msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); @@ -761,9 +761,6 @@ static irqreturn_t msm_hs_isr(int irq, void *dev) isr_status = msm_hs_read(uport, UARTDM_MISR_ADDR); - /* Mask UART interrupts */ - disable_irq(uport->irq); - /* Stale rx interrupt */ if (isr_status & UARTDM_ISR_RXSTALE_BMSK) { msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); @@ -794,7 +791,6 @@ static irqreturn_t msm_hs_isr(int irq, void *dev) if (isr_status & UARTDM_ISR_DELTA_CTS_BMSK) msm_hs_handle_delta_cts(uport); - enable_irq(uport->irq); spin_unlock_irqrestore(&uport->lock, flags); return IRQ_HANDLED; @@ -987,6 +983,9 @@ static int __init msm_hs_probe(struct platform_device *pdev) if (unlikely(uport->irq < 0)) return -ENXIO; + if (unlikely(set_irq_wake(uport->irq, 1))) + return -ENXIO; + resource = platform_get_resource_byname(pdev, IORESOURCE_DMA, "uartdm_channels"); if (unlikely(!resource)) From 9aa2d0c376a8b429778c90ce86a5105c26a60e4e Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 11 Dec 2008 15:22:57 -0800 Subject: [PATCH 0249/2556] [ARM] msm: msm_serial_hs: Add optional low power wakeup. Low power wakeup is configured using msm_serial_hs_platform_data. This allows us to clk_enable() the uart on another interrupt (for example a gpio rx line). It also allows us to inject an char into the rx tty layer when this occurs. This is useful for uart attached devices that have low power modes independant of the linux CPU, for example bluetooth chip. Currently the interface to control low power mode is through msm_hs_request_clock_on() msm_hs_request_clock_off() Ideally we want to use the tty/serial layer but we do not have a good solution to this yet. A few other misc changes in here - Use _locked convention for functions that hold spinlock - request_irq() and free_irq() in startup/shutdown not probe/release - Use BIT(x) helper in the hw_reg header. Add missing bitmasks. Signed-off-by: Nick Pelly --- .../arm/mach-msm/include/mach/msm_serial_hs.h | 27 ++ drivers/tty/serial/msm_serial_hs.c | 421 ++++++++++++++---- drivers/tty/serial/msm_serial_hs_hwreg.h | 37 +- 3 files changed, 391 insertions(+), 94 deletions(-) create mode 100644 arch/arm/mach-msm/include/mach/msm_serial_hs.h diff --git a/arch/arm/mach-msm/include/mach/msm_serial_hs.h b/arch/arm/mach-msm/include/mach/msm_serial_hs.h new file mode 100644 index 0000000000000..0337deb8358d3 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_serial_hs.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2008 Google, Inc. + * Author: Nick Pelly + * + * 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 __ASM_ARCH_MSM_SERIAL_HS_H +#define __ASM_ARCH_MSM_SERIAL_HS_H + +/* Optional platform device data for msm_serial_hs driver. + * Used to configure low power wakeup */ +struct msm_serial_hs_platform_data { + int wakeup_irq; /* wakeup irq */ + /* bool: inject char into rx tty on wakeup */ + unsigned char inject_rx_on_wakeup; + char rx_to_inject; +}; + +#endif diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 742c833447630..f169082bb2a49 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -4,7 +4,8 @@ * * Copyright (c) 2007-2008 QUALCOMM Incorporated. * Copyright (c) 2008 QUALCOMM USA, INC. - * Copyright (c) 2008 Nick Pelly + * Copyright (c) 2008 Google Inc. + * Modified: Nick Pelly * * All source code in this file is licensed under the following license * except where indicated. @@ -22,6 +23,10 @@ * along with this program; if not, you can find it at http://www.fsf.org */ +/* This driver has optional support for low power wakeup on a rx gpio. This is + * useful for peripherals that send unsolicited RX such as Bluetooth. + */ + #include #include @@ -43,8 +48,10 @@ #include #include #include + #include #include +#include #include "msm_serial_hs_hwreg.h" @@ -57,8 +64,16 @@ enum flush_reason { FLUSH_SHUTDOWN, }; +enum msm_hs_clk_states_e { + MSM_HS_CLK_PORT_OFF, /* port not in use */ + MSM_HS_CLK_OFF, /* clock disabled */ + MSM_HS_CLK_REQUEST_OFF, /* disable after TX flushed */ + MSM_HS_CLK_ON, /* clock disabled */ +}; + struct msm_hs_tx { - unsigned int tx_ready_int_en; + unsigned int tx_ready_int_en; /* ok to dma more tx */ + unsigned int dma_in_flight; /* tx dma in progress */ struct msm_dmov_cmd xfer; dmov_box *command_ptr; u32 *command_ptr_ptr; @@ -81,11 +96,20 @@ struct msm_hs_rx { struct dma_pool *pool; }; +/* optional low power wakeup, typically on a GPIO RX irq */ +struct msm_hs_wakeup { + int irq; /* < 0 indicates low power wakeup disabled */ + unsigned char ignore; /* bool */ + + /* bool: inject char into rx tty on wakeup */ + unsigned char inject_rx; + char rx_to_inject; +}; + struct msm_hs_port { struct uart_port uport; unsigned long imr_reg; /* shadow value of UARTDM_IMR */ struct clk *clk; - atomic_t open_cnt; struct msm_hs_tx tx; struct msm_hs_rx rx; @@ -93,6 +117,12 @@ struct msm_hs_port { int dma_rx_channel; int dma_tx_crci; int dma_rx_crci; + + struct hrtimer clk_off_timer; /* to poll TXEMT before clock off */ + ktime_t clk_off_delay; + enum msm_hs_clk_states_e clk_state; + + struct msm_hs_wakeup wakeup; }; #define MSM_UARTDM_BURST_SIZE 16 /* DM burst size (in bytes) */ @@ -109,6 +139,11 @@ static struct uart_ops msm_hs_ops; #define UARTDM_TO_MSM(uart_port) \ container_of((uart_port), struct msm_hs_port, uport) +static inline unsigned int use_low_power_wakeup(struct msm_hs_port *msm_uport) +{ + return (msm_uport->wakeup.irq >= 0); +} + static inline unsigned int msm_hs_read(struct uart_port *uport, unsigned int offset) { @@ -160,9 +195,6 @@ static int __devexit msm_hs_remove(struct platform_device *pdev) uart_remove_one_port(&msm_hs_driver, &msm_uport->uport); clk_put(msm_uport->clk); - /* Free the cable detect IRQ */ - free_irq(msm_uport->uport.irq, &msm_uport->uport); - /* Free the tx resources */ kfree(msm_uport->tx.command_ptr); kfree(msm_uport->tx.command_ptr_ptr); @@ -176,43 +208,42 @@ static int __devexit msm_hs_remove(struct platform_device *pdev) return 0; } -static int msm_hs_enable_clk(struct uart_port *uport) +static int msm_hs_init_clk_locked(struct uart_port *uport) { int ret; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - /* Set up the MREG/NREG/DREG/MNDREG */ ret = clk_enable(msm_uport->clk); if (ret) { printk(KERN_ERR "Error could not turn on UART clk\n"); return ret; } + /* Set up the MREG/NREG/DREG/MNDREG */ ret = clk_set_rate(msm_uport->clk, uport->uartclk); if (ret) { printk(KERN_WARNING "Error setting clock rate on UART\n"); return ret; } + msm_uport->clk_state = MSM_HS_CLK_ON; return 0; } -static inline void msm_hs_disable_clk(struct uart_port *uport) -{ - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - clk_disable(msm_uport->clk); -} - /* Enable and Disable clocks (Used for power management) */ static void msm_hs_pm(struct uart_port *uport, unsigned int state, - unsigned int oldstate) + unsigned int oldstate) { + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + if (use_low_power_wakeup(msm_uport)) + return; + switch (state) { case 0: - msm_hs_enable_clk(uport); + clk_enable(msm_uport->clk); break; case 3: - msm_hs_disable_clk(uport); + clk_disable(msm_uport->clk); break; default: printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state); @@ -228,7 +259,7 @@ static void msm_hs_pm(struct uart_port *uport, unsigned int state, * Goal is to have around 8 ms before indicate stale. * roundup (((Bit Rate * .008) / 10) + 1 */ -static void msm_hs_set_bps(struct uart_port *uport, +static void msm_hs_set_bps_locked(struct uart_port *uport, unsigned int bps) { unsigned long rxstale; @@ -349,6 +380,7 @@ static void msm_hs_set_termios(struct uart_port *uport, struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); spin_lock_irqsave(&uport->lock, flags); + clk_enable(msm_uport->clk); /* 300 is the minimum baud support by the driver */ bps = uart_get_baud_rate(uport, termios, oldtermios, 200, 4000000); @@ -357,7 +389,7 @@ static void msm_hs_set_termios(struct uart_port *uport, if (bps == 200) bps = 3200000; - msm_hs_set_bps(uport, bps); + msm_hs_set_bps_locked(uport, bps); data = msm_hs_read(uport, UARTDM_MR2_ADDR); data &= ~UARTDM_MR2_PARITY_MODE_BMSK; @@ -431,6 +463,7 @@ static void msm_hs_set_termios(struct uart_port *uport, msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + clk_disable(msm_uport->clk); spin_unlock_irqrestore(&uport->lock, flags); } @@ -441,12 +474,18 @@ static void msm_hs_set_termios(struct uart_port *uport, static unsigned int msm_hs_tx_empty(struct uart_port *uport) { unsigned int data; + unsigned int ret = 0; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + clk_enable(msm_uport->clk); data = msm_hs_read(uport, UARTDM_SR_ADDR); - if (UARTDM_SR_TXEMT_BMSK == (data & UARTDM_SR_TXEMT_BMSK)) - return TIOCSER_TEMT; + if (data & UARTDM_SR_TXEMT_BMSK) + ret = TIOCSER_TEMT; - return 0; + clk_disable(msm_uport->clk); + + return ret; } /* @@ -454,7 +493,7 @@ static unsigned int msm_hs_tx_empty(struct uart_port *uport) * Any character in the transmit shift register is sent as * well as the current data mover transfer . */ -static void msm_hs_stop_tx(struct uart_port *uport) +static void msm_hs_stop_tx_locked(struct uart_port *uport) { struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); @@ -469,20 +508,24 @@ static void msm_hs_stop_tx(struct uart_port *uport) * of the receiver status bits are affected by this command and * characters that are already in the receive FIFO there. */ -static void msm_hs_stop_rx(struct uart_port *uport) +static void msm_hs_stop_rx_locked(struct uart_port *uport) { struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + clk_enable(msm_uport->clk); + /* Disable the receiver */ msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_DISABLE_BMSK); if (msm_uport->rx.flush == FLUSH_NONE) msm_dmov_flush(msm_uport->dma_rx_channel); if (msm_uport->rx.flush != FLUSH_SHUTDOWN) msm_uport->rx.flush = FLUSH_STOP; + + clk_disable(msm_uport->clk); } /* Transmit the next chunk of data */ -static void msm_hs_submit_tx(struct uart_port *uport) +static void msm_hs_submit_tx_locked(struct uart_port *uport) { int left; int tx_count; @@ -492,10 +535,12 @@ static void msm_hs_submit_tx(struct uart_port *uport) struct circ_buf *tx_buf = &msm_uport->uport.info->xmit; if (uart_circ_empty(tx_buf) || uport->info->port.tty->stopped) { - msm_hs_stop_tx(uport); + msm_hs_stop_tx_locked(uport); return; } + tx->dma_in_flight = 1; + tx_count = uart_circ_chars_pending(tx_buf); if (UARTDM_TX_BUF_SIZE < tx_count) @@ -533,7 +578,7 @@ static void msm_hs_submit_tx(struct uart_port *uport) } /* Start to receive the next chunk of data */ -static void msm_hs_start_rx(struct uart_port *uport) +static void msm_hs_start_rx_locked(struct uart_port *uport) { struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); @@ -546,14 +591,18 @@ static void msm_hs_start_rx(struct uart_port *uport) } /* Enable the transmitter Interrupt */ -static void msm_hs_start_tx(struct uart_port *uport) +static void msm_hs_start_tx_locked(struct uart_port *uport ) { struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + clk_enable(msm_uport->clk); + if (msm_uport->tx.tx_ready_int_en == 0) { msm_uport->tx.tx_ready_int_en = 1; - msm_hs_submit_tx(uport); + msm_hs_submit_tx_locked(uport); } + + clk_disable(msm_uport->clk); } /* @@ -567,14 +616,20 @@ static void msm_hs_dmov_tx_callback(struct msm_dmov_cmd *cmd_ptr, unsigned int result, struct msm_dmov_errdata *err) { + unsigned long flags; struct msm_hs_port *msm_uport; WARN_ON(result != 0x80000002); /* DMA did not finish properly */ - msm_uport = container_of(cmd_ptr, struct msm_hs_port, tx.xfer); + spin_lock_irqsave(&msm_uport->uport.lock, flags); + clk_enable(msm_uport->clk); + msm_uport->imr_reg |= UARTDM_ISR_TX_READY_BMSK; msm_hs_write(&msm_uport->uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + + clk_disable(msm_uport->clk); + spin_unlock_irqrestore(&msm_uport->uport.lock, flags); } /* @@ -602,6 +657,7 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, uport = &msm_uport->uport; spin_lock_irqsave(&uport->lock, flags); + clk_enable(msm_uport->clk); tty = uport->info->port.tty; @@ -633,10 +689,11 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, flush = msm_uport->rx.flush; if (flush == FLUSH_TERMIOS) - msm_hs_start_rx(uport); + msm_hs_start_rx_locked(uport); if (flush == FLUSH_STOP) msm_uport->rx.flush = FLUSH_SHUTDOWN; if (flush >= FLUSH_DATA_INVALID) { + clk_disable(msm_uport->clk); spin_unlock_irqrestore(&uport->lock, flags); return; } @@ -649,12 +706,13 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, BUG_ON(retval != rx_count); } - msm_hs_start_rx(uport); + msm_hs_start_rx_locked(uport); /* * Drop the lock here since tty_flip_buffer_push() might end * up calling uart_start(), which takes the lock. */ + clk_disable(msm_uport->clk); spin_unlock_irqrestore(&uport->lock, flags); tty_flip_buffer_push(tty); @@ -673,7 +731,7 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, * - TIOCM_RI * (Unsupported) DCD and DSR will return them high. RI will return low. */ -static unsigned int msm_hs_get_mctrl(struct uart_port *uport) +static unsigned int msm_hs_get_mctrl_locked(struct uart_port *uport) { return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS; } @@ -686,10 +744,14 @@ static unsigned int msm_hs_get_mctrl(struct uart_port *uport) * back auto ready for receiving and it should lower RFR signal * when hardware is ready */ -static void msm_hs_set_mctrl(struct uart_port *uport, unsigned int mctrl) +static void msm_hs_set_mctrl_locked(struct uart_port *uport, + unsigned int mctrl) { unsigned int set_rts; unsigned int data; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + clk_enable(msm_uport->clk); /* RTS is active low */ set_rts = TIOCM_RTS & mctrl ? 0 : 1; @@ -706,16 +768,23 @@ static void msm_hs_set_mctrl(struct uart_port *uport, unsigned int mctrl) data |= UARTDM_MR1_RX_RDY_CTL_BMSK; msm_hs_write(uport, UARTDM_MR1_ADDR, data); } + + clk_disable(msm_uport->clk); } /* Standard API, Enable modem status (CTS) interrupt */ -static void msm_hs_enable_ms(struct uart_port *uport) +static void msm_hs_enable_ms_locked(struct uart_port *uport) { struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + clk_enable(msm_uport->clk); + /* Enable DELTA_CTS Interrupt */ msm_uport->imr_reg |= UARTDM_ISR_DELTA_CTS_BMSK; msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + + clk_disable(msm_uport->clk); + } /* @@ -726,27 +795,98 @@ static void msm_hs_enable_ms(struct uart_port *uport) */ static void msm_hs_break_ctl(struct uart_port *uport, int ctl) { + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + clk_enable(msm_uport->clk); msm_hs_write(uport, UARTDM_CR_ADDR, ctl ? START_BREAK : STOP_BREAK); + clk_disable(msm_uport->clk); } -static void msm_hs_config_port(struct uart_port *uport, int flags) +static void msm_hs_config_port(struct uart_port *uport, int cfg_flags) { - if (flags & UART_CONFIG_TYPE) { + unsigned long flags; + + spin_lock_irqsave(&uport->lock, flags); + if (cfg_flags & UART_CONFIG_TYPE) { uport->type = PORT_MSM; msm_hs_request_port(uport); } + spin_unlock_irqrestore(&uport->lock, flags); } /* Handle CTS changes (Called from interrupt handler) */ static void msm_hs_handle_delta_cts(struct uart_port *uport) { + unsigned long flags; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + spin_lock_irqsave(&uport->lock, flags); + clk_enable(msm_uport->clk); + /* clear interrupt */ msm_hs_write(uport, UARTDM_CR_ADDR, RESET_CTS); uport->icount.cts++; + + clk_disable(msm_uport->clk); + spin_unlock_irqrestore(&uport->lock, flags); + /* clear the IOCTL TIOCMIWAIT if called */ wake_up_interruptible(&uport->info->delta_msr_wait); } +/* check if the TX path is flushed, and if so clock off + * returns 0 did not clock off, need to retry (still sending final byte) + * -1 did not clock off, do not retry + * 1 if we clocked off + */ +static int msm_hs_check_clock_off_locked(struct uart_port *uport) +{ + unsigned long sr_status; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + struct circ_buf *tx_buf = &uport->info->xmit; + + /* Cancel if tx tty buffer is not empty, dma is in flight, + * or tx fifo is not empty */ + if (msm_uport->clk_state != MSM_HS_CLK_REQUEST_OFF || + !uart_circ_empty(tx_buf) || msm_uport->tx.dma_in_flight || + msm_uport->imr_reg & UARTDM_ISR_TXLEV_BMSK) { + return -1; + } + + /* Make sure the uart is finished with the last byte */ + sr_status = msm_hs_read(uport, UARTDM_SR_ADDR); + if (!(sr_status & UARTDM_SR_TXEMT_BMSK)) + return 0; /* retry */ + + /* we really want to clock off */ + clk_disable(msm_uport->clk); + msm_uport->clk_state = MSM_HS_CLK_OFF; + if (use_low_power_wakeup(msm_uport)) { + msm_uport->wakeup.ignore = 1; + enable_irq(msm_uport->wakeup.irq); + } + return 1; +} + +static enum hrtimer_restart msm_hs_clk_off_retry(struct hrtimer *timer) { + unsigned long flags; + int ret = HRTIMER_NORESTART; + struct msm_hs_port *msm_uport = container_of(timer, struct msm_hs_port, + clk_off_timer); + struct uart_port *uport = &msm_uport->uport; + + spin_lock_irqsave(&uport->lock, flags); + + if (!msm_hs_check_clock_off_locked(uport)) { + hrtimer_forward_now(timer, msm_uport->clk_off_delay); + ret = HRTIMER_RESTART; + } + + spin_unlock_irqrestore(&uport->lock, flags); + + return ret; +} + static irqreturn_t msm_hs_isr(int irq, void *dev) { unsigned long flags; @@ -775,17 +915,34 @@ static irqreturn_t msm_hs_isr(int irq, void *dev) if (isr_status & UARTDM_ISR_TX_READY_BMSK) { /* Clear TX Ready */ msm_hs_write(uport, UARTDM_CR_ADDR, CLEAR_TX_READY); + + if (msm_uport->clk_state == MSM_HS_CLK_REQUEST_OFF) { + msm_uport->imr_reg |= UARTDM_ISR_TXLEV_BMSK; + msm_hs_write(uport, UARTDM_IMR_ADDR, + msm_uport->imr_reg); + } /* Complete DMA TX transactions and submit new transactions */ tx_buf->tail = (tx_buf->tail + tx->tx_count) & ~UART_XMIT_SIZE; + tx->dma_in_flight = 0; + uport->icount.tx += tx->tx_count; if (tx->tx_ready_int_en) - msm_hs_submit_tx(uport); + msm_hs_submit_tx_locked(uport); if (uart_circ_chars_pending(tx_buf) < WAKEUP_CHARS) uart_write_wakeup(uport); } + if (isr_status & UARTDM_ISR_TXLEV_BMSK) { + /* TX FIFO is empty */ + msm_uport->imr_reg &= ~UARTDM_ISR_TXLEV_BMSK; + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + if (!msm_hs_check_clock_off_locked(uport)) + hrtimer_start(&msm_uport->clk_off_timer, + msm_uport->clk_off_delay, + HRTIMER_MODE_REL); + } /* Change in CTS interrupt */ if (isr_status & UARTDM_ISR_DELTA_CTS_BMSK) @@ -796,29 +953,97 @@ static irqreturn_t msm_hs_isr(int irq, void *dev) return IRQ_HANDLED; } +/* request to turn off uart clock once pending TX is flushed */ +void msm_hs_request_clock_off(struct uart_port *uport) { + unsigned long flags; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + spin_lock_irqsave(&uport->lock, flags); + if (msm_uport->clk_state == MSM_HS_CLK_ON) { + msm_uport->clk_state = MSM_HS_CLK_REQUEST_OFF; + msm_uport->imr_reg |= UARTDM_ISR_TXLEV_BMSK; + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + } + spin_unlock_irqrestore(&uport->lock, flags); +} + +static void msm_hs_request_clock_on_locked(struct uart_port *uport) { + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + switch (msm_uport->clk_state) { + case MSM_HS_CLK_OFF: + clk_enable(msm_uport->clk); + disable_irq(msm_uport->wakeup.irq); + case MSM_HS_CLK_REQUEST_OFF: + hrtimer_try_to_cancel(&msm_uport->clk_off_timer); + msm_uport->clk_state = MSM_HS_CLK_ON; + break; + case MSM_HS_CLK_ON: break; + case MSM_HS_CLK_PORT_OFF: break; + } +} + +void msm_hs_request_clock_on(struct uart_port *uport) { + unsigned long flags; + spin_lock_irqsave(&uport->lock, flags); + msm_hs_request_clock_on_locked(uport); + spin_unlock_irqrestore(&uport->lock, flags); +} + +static irqreturn_t msm_hs_wakeup_isr(int irq, void *dev) +{ + unsigned int wakeup = 0; + unsigned long flags; + struct msm_hs_port *msm_uport = (struct msm_hs_port *)dev; + struct uart_port *uport = &msm_uport->uport; + struct tty_struct *tty = NULL; + + spin_lock_irqsave(&uport->lock, flags); + if (msm_uport->clk_state == MSM_HS_CLK_OFF) { + /* ignore the first irq - it is a pending irq that occured + * before enable_irq() */ + if (msm_uport->wakeup.ignore) + msm_uport->wakeup.ignore = 0; + else + wakeup = 1; + } + + if (wakeup) { + /* the uart was clocked off during an rx, wake up and + * optionally inject char into tty rx */ + msm_hs_request_clock_on_locked(uport); + if (msm_uport->wakeup.inject_rx) { + tty = uport->info->port.tty; + tty_insert_flip_char(tty, + msm_uport->wakeup.rx_to_inject, + TTY_NORMAL); + } + } + + spin_unlock_irqrestore(&uport->lock, flags); + + if (wakeup && msm_uport->wakeup.inject_rx) + tty_flip_buffer_push(tty); + return IRQ_HANDLED; +} + static const char *msm_hs_type(struct uart_port *port) { return ("MSM HS UART"); } -/* - * Called by the upper layer when port is open. - * - * - Initializes the port - * - Hook the ISR - * - Enable Receive Interrupt - */ +/* Called when port is opened */ static int msm_hs_startup(struct uart_port *uport) { + int ret; int rfr_level; + unsigned long flags; unsigned int data; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); struct circ_buf *tx_buf = &uport->info->xmit; struct msm_hs_tx *tx = &msm_uport->tx; struct msm_hs_rx *rx = &msm_uport->rx; - atomic_inc(&(msm_uport->open_cnt)); - rfr_level = uport->fifosize; if (rfr_level > 16) rfr_level -= 16; @@ -826,6 +1051,11 @@ static int msm_hs_startup(struct uart_port *uport) tx->dma_base = dma_map_single(uport->dev, tx_buf->buf, UART_XMIT_SIZE, DMA_TO_DEVICE); + /* turn on uart clk */ + ret = msm_hs_init_clk_locked(uport); + if (unlikely(ret)) + return ret; + /* Set auto RFR Level */ data = msm_hs_read(uport, UARTDM_MR1_ADDR); data &= ~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK; @@ -861,6 +1091,7 @@ static int msm_hs_startup(struct uart_port *uport) /* Initialize the tx */ tx->tx_ready_int_en = 0; + tx->dma_in_flight = 0; tx->xfer.complete_func = msm_hs_dmov_tx_callback; @@ -887,14 +1118,34 @@ static int msm_hs_startup(struct uart_port *uport) rx->command_ptr->row_offset = MSM_UARTDM_BURST_SIZE; rx->command_ptr->src_row_addr = uport->mapbase + UARTDM_RF_ADDR; + msm_uport->imr_reg |= UARTDM_ISR_RXSTALE_BMSK; /* Enable reading the current CTS, no harm even if CTS is ignored */ msm_uport->imr_reg |= UARTDM_ISR_CURRENT_CTS_BMSK; - msm_hs_start_rx(uport); - enable_irq(uport->irq); + msm_hs_write(uport, UARTDM_TFWR_ADDR, 0); /* TXLEV on empty TX fifo */ + + + ret = request_irq(uport->irq, msm_hs_isr, IRQF_TRIGGER_HIGH, + "msm_hs_uart", msm_uport); + if (unlikely(ret)) + return ret; + if (use_low_power_wakeup(msm_uport)) { + ret = request_irq(msm_uport->wakeup.irq, msm_hs_wakeup_isr, + IRQF_TRIGGER_FALLING, + "msm_hs_wakeup", msm_uport); + if (unlikely(ret)) + return ret; + disable_irq(msm_uport->wakeup.irq); + } + + spin_lock_irqsave(&uport->lock, flags); + + msm_hs_start_rx_locked(uport); msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + spin_unlock_irqrestore(&uport->lock, flags); + return 0; } @@ -959,6 +1210,7 @@ static int __init msm_hs_probe(struct platform_device *pdev) struct uart_port *uport; struct msm_hs_port *msm_uport; struct resource *resource; + struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data; if (pdev->id < 0 || pdev->id >= UARTDM_NR) { printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id); @@ -982,10 +1234,23 @@ static int __init msm_hs_probe(struct platform_device *pdev) uport->irq = platform_get_irq(pdev, 0); if (unlikely(uport->irq < 0)) return -ENXIO; - if (unlikely(set_irq_wake(uport->irq, 1))) return -ENXIO; + if (pdata == NULL) + msm_uport->wakeup.irq = -1; + else { + msm_uport->wakeup.irq = pdata->wakeup_irq; + msm_uport->wakeup.ignore = 1; + msm_uport->wakeup.inject_rx = pdata->inject_rx_on_wakeup; + msm_uport->wakeup.rx_to_inject = pdata->rx_to_inject; + + if (unlikely(msm_uport->wakeup.irq < 0)) + return -ENXIO; + if (unlikely(set_irq_wake(msm_uport->wakeup.irq, 1))) + return -ENXIO; + } + resource = platform_get_resource_byname(pdev, IORESOURCE_DMA, "uartdm_channels"); if (unlikely(!resource)) @@ -1010,19 +1275,6 @@ static int __init msm_hs_probe(struct platform_device *pdev) if (IS_ERR(msm_uport->clk)) return PTR_ERR(msm_uport->clk); - atomic_set(&msm_uport->open_cnt, 0); - - ret = msm_hs_enable_clk(uport); - if (unlikely(ret)) - return ret; - - ret = request_irq(uport->irq, msm_hs_isr, IRQF_TRIGGER_HIGH, - "msm_uartdm", msm_uport); - if (unlikely(ret)) - return ret; - - disable_irq(uport->irq); /* irq will be enabled when port is opened */ - ret = uartdm_init_port(uport); if (unlikely(ret)) return ret; @@ -1030,12 +1282,14 @@ static int __init msm_hs_probe(struct platform_device *pdev) /* configure the CR Protection to Enable */ msm_hs_write(uport, UARTDM_CR_ADDR, CR_PROTECTION_EN); - msm_hs_disable_clk(uport); + msm_uport->clk_state = MSM_HS_CLK_PORT_OFF; + hrtimer_init(&msm_uport->clk_off_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + msm_uport->clk_off_timer.function = msm_hs_clk_off_retry; + msm_uport->clk_off_delay = ktime_set(0, 1000000); /* 1ms */ uport->line = pdev->id; - ret = uart_add_one_port(&msm_hs_driver, uport); - - return ret; + return uart_add_one_port(&msm_hs_driver, uport); } static int __init msm_serial_hs_init(void) @@ -1070,25 +1324,38 @@ static int __init msm_serial_hs_init(void) */ static void msm_hs_shutdown(struct uart_port *uport) { + unsigned long flags; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); BUG_ON(msm_uport->rx.flush < FLUSH_STOP); - atomic_dec(&msm_uport->open_cnt); + spin_lock_irqsave(&uport->lock, flags); + clk_enable(msm_uport->clk); + /* Disable the transmitter */ msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_DISABLE_BMSK); /* Disable the receiver */ msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_DISABLE_BMSK); /* Free the interrupt */ - disable_irq(uport->irq); + free_irq(uport->irq, msm_uport); + if (use_low_power_wakeup(msm_uport)) + free_irq(msm_uport->wakeup.irq, msm_uport); + msm_uport->imr_reg = 0; msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); wait_event(msm_uport->rx.wait, msm_uport->rx.flush == FLUSH_SHUTDOWN); - dma_unmap_single(msm_uport->uport.dev, msm_uport->tx.dma_base, + clk_disable(msm_uport->clk); /* to balance local clk_enable() */ + if (msm_uport->clk_state != MSM_HS_CLK_OFF) + clk_disable(msm_uport->clk); /* to balance clk_state */ + msm_uport->clk_state = MSM_HS_CLK_PORT_OFF; + + dma_unmap_single(uport->dev, msm_uport->tx.dma_base, UART_XMIT_SIZE, DMA_TO_DEVICE); + + spin_unlock_irqrestore(&uport->lock, flags); } static void __exit msm_serial_hs_exit(void) @@ -1116,12 +1383,12 @@ static struct uart_driver msm_hs_driver = { static struct uart_ops msm_hs_ops = { .tx_empty = msm_hs_tx_empty, - .set_mctrl = msm_hs_set_mctrl, - .get_mctrl = msm_hs_get_mctrl, - .stop_tx = msm_hs_stop_tx, - .start_tx = msm_hs_start_tx, - .stop_rx = msm_hs_stop_rx, - .enable_ms = msm_hs_enable_ms, + .set_mctrl = msm_hs_set_mctrl_locked, + .get_mctrl = msm_hs_get_mctrl_locked, + .stop_tx = msm_hs_stop_tx_locked, + .start_tx = msm_hs_start_tx_locked, + .stop_rx = msm_hs_stop_rx_locked, + .enable_ms = msm_hs_enable_ms_locked, .break_ctl = msm_hs_break_ctl, .startup = msm_hs_startup, .shutdown = msm_hs_shutdown, @@ -1136,5 +1403,5 @@ static struct uart_ops msm_hs_ops = { module_init(msm_serial_hs_init); module_exit(msm_serial_hs_exit); MODULE_DESCRIPTION("High Speed UART Driver for the MSM chipset"); -MODULE_VERSION("1.1"); +MODULE_VERSION("1.2"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/msm_serial_hs_hwreg.h b/drivers/tty/serial/msm_serial_hs_hwreg.h index c3450153ad28e..df596781a5ab8 100644 --- a/drivers/tty/serial/msm_serial_hs_hwreg.h +++ b/drivers/tty/serial/msm_serial_hs_hwreg.h @@ -19,8 +19,8 @@ * along with this program; if not, you can find it at http://www.fsf.org */ -#ifndef MSM_UART_HS_HWREG_H -#define MSM_UART_HS_HWREG_H +#ifndef MSM_SERIAL_HS_HWREG_H +#define MSM_SERIAL_HS_HWREG_H #define UARTDM_MR1_ADDR 0x0 #define UARTDM_MR2_ADDR 0x4 @@ -73,17 +73,16 @@ #define UARTDM_RXFS_ADDR 0x50 /* Register field Mask Mapping */ -#define UARTDM_SR_TXEMT_BMSK 0x8 +#define UARTDM_SR_PAR_FRAME_BMSK BIT(5) +#define UARTDM_SR_OVERRUN_BMSK BIT(4) +#define UARTDM_SR_TXEMT_BMSK BIT(3) +#define UARTDM_SR_TXRDY_BMSK BIT(2) +#define UARTDM_SR_RXRDY_BMSK BIT(0) -#define UARTDM_SR_TXRDY_BMSK 0x4 -#define UARTDM_SR_RXRDY_BMSK 0x1 -#define UARTDM_SR_OVERRUN_BMSK 0x10 -#define UARTDM_SR_PAR_FRAME_BMSK 0x20 - -#define UARTDM_CR_TX_DISABLE_BMSK 0x8 -#define UARTDM_CR_RX_DISABLE_BMSK 0x2 -#define UARTDM_CR_TX_EN_BMSK 0x4 -#define UARTDM_CR_RX_EN_BMSK 0x1 +#define UARTDM_CR_TX_DISABLE_BMSK BIT(3) +#define UARTDM_CR_RX_DISABLE_BMSK BIT(1) +#define UARTDM_CR_TX_EN_BMSK BIT(2) +#define UARTDM_CR_RX_EN_BMSK BIT(0) /* UARTDM_CR channel_comman bit value (register field is bits 8:4) */ #define RESET_RX 0x10 @@ -134,13 +133,17 @@ #define UARTDM_IPR_STALE_LSB_BMSK 0x1f /* These can be used for both ISR and IMR register */ -#define UARTDM_ISR_CURRENT_CTS_BMSK 0x40 -#define UARTDM_ISR_DELTA_CTS_BMSK 0x20 -#define UARTDM_ISR_TX_READY_BMSK 0x80 -#define UARTDM_ISR_RXSTALE_BMSK 0x8 +#define UARTDM_ISR_TX_READY_BMSK BIT(7) +#define UARTDM_ISR_CURRENT_CTS_BMSK BIT(6) +#define UARTDM_ISR_DELTA_CTS_BMSK BIT(5) +#define UARTDM_ISR_RXLEV_BMSK BIT(4) +#define UARTDM_ISR_RXSTALE_BMSK BIT(3) +#define UARTDM_ISR_RXBREAK_BMSK BIT(2) +#define UARTDM_ISR_RXHUNT_BMSK BIT(1) +#define UARTDM_ISR_TXLEV_BMSK BIT(0) /* Field definitions for UART_DM_DMEN*/ #define UARTDM_TX_DM_EN_BMSK 0x1 #define UARTDM_RX_DM_EN_BMSK 0x2 -#endif /* MSM_UART_HWREG_H */ +#endif /* MSM_SERIAL_HS_HWREG_H */ From fd01ff09327b33425dc7bd7a7e3aeccfa6ccfa7e Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 11 Dec 2008 17:53:10 -0800 Subject: [PATCH 0250/2556] [bluetooth] Use msm_serial_hs clock controls during bt chip low power Signed-off-by: Nick Pelly --- drivers/bluetooth/hci_ll.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c index 38595e782d02e..bd2954b851954 100644 --- a/drivers/bluetooth/hci_ll.c +++ b/drivers/bluetooth/hci_ll.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -86,6 +87,28 @@ struct ll_struct { struct sk_buff_head tx_wait_q; /* HCILL wait queue */ }; +#ifdef CONFIG_SERIAL_MSM_HS +void msm_hs_request_clock_off(struct uart_port *uport); +void msm_hs_request_clock_on(struct uart_port *uport); + +static void __ll_msm_serial_clock_on(struct tty_struct *tty) { + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->uart_port; + + msm_hs_request_clock_on(port); +} + +static void __ll_msm_serial_clock_request_off(struct tty_struct *tty) { + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->uart_port; + + msm_hs_request_clock_off(port); +} +#else +static inline void __ll_msm_serial_clock_on(struct tty_struct *tty) {} +static inline void __ll_msm_serial_clock_request_off(struct tty_struct *tty) {} +#endif + /* * Builds and sends an HCILL command packet. * These are very simple packets with only 1 cmd byte @@ -217,6 +240,10 @@ static void ll_device_want_to_wakeup(struct hci_uart *hu) BT_DBG("dual wake-up-indication"); /* deliberate fall-through - do not add break */ case HCILL_ASLEEP: + /* Make sure clock is on - we may have turned clock off since + * receiving the wake up indicator + */ + __ll_msm_serial_clock_on(hu->tty); /* acknowledge device wake up */ if (send_hcill_cmd(HCILL_WAKE_UP_ACK, hu) < 0) { BT_ERR("cannot acknowledge device wake up"); @@ -270,6 +297,11 @@ static void ll_device_want_to_sleep(struct hci_uart *hu) /* actually send the sleep ack packet */ hci_uart_tx_wakeup(hu); + + spin_lock_irqsave(&ll->hcill_lock, flags); + if (ll->hcill_state == HCILL_ASLEEP) + __ll_msm_serial_clock_request_off(hu->tty); + spin_unlock_irqrestore(&ll->hcill_lock, flags); } /* @@ -321,6 +353,7 @@ static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb) break; case HCILL_ASLEEP: BT_DBG("device asleep, waking up and queueing packet"); + __ll_msm_serial_clock_on(hu->tty); /* save packet for later */ skb_queue_tail(&ll->tx_wait_q, skb); /* awake device */ From c508bbc3c4187954c290bde787fdb9eb96472e2c Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 11 Dec 2008 16:22:52 -0800 Subject: [PATCH 0251/2556] [ARM] msm: trout: Use low power wakeup on the bluetooth uart. Wakeup on GPIO 45 (UART 1 RX). Send character 0x32 (HCILL wake up indicator). Signed-off-by: Nick Pelly --- arch/arm/mach-msm/board-trout.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 2a403a4e1d19a..92c9283185928 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -61,7 +61,7 @@ #include #include - +#include #include #include "proc_comm.h" @@ -702,6 +702,14 @@ static struct msm_acpu_clock_platform_data trout_clock_data = { .wait_for_irq_khz = 128000000, }; +#ifdef CONFIG_SERIAL_MSM_HS +static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { + .wakeup_irq = MSM_GPIO_TO_INT(45), + .inject_rx_on_wakeup = 1, + .rx_to_inject = 0x32, +}; +#endif + static void __init trout_init(void) { int rc; @@ -736,6 +744,10 @@ static void __init trout_init(void) msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; +#ifdef CONFIG_SERIAL_MSM_HS + msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; +#endif + rc = trout_init_mmc(system_rev); if (rc) printk(KERN_CRIT "%s: MMC init failure (%d)\n", __func__, rc); From 467e2419d64a73b6da5ab55e855d76e9ded4f9d6 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 17 Dec 2008 10:43:57 -0800 Subject: [PATCH 0252/2556] [ARM] msm: msm_serial_hs: Hold wakelock while rx dma in progress. Use RXLEV with watermark level of 0 to acquire wakelock. Signed-off-by: Nick Pelly --- drivers/tty/serial/msm_serial_hs.c | 39 +++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index f169082bb2a49..6bea63c9381b8 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -94,6 +95,7 @@ struct msm_hs_rx { dma_addr_t rbuffer; unsigned char *buffer; struct dma_pool *pool; + struct wake_lock wake_lock; }; /* optional low power wakeup, typically on a GPIO RX irq */ @@ -192,6 +194,8 @@ static int __devexit msm_hs_remove(struct platform_device *pdev) dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr, sizeof(dmov_box), DMA_TO_DEVICE); + wake_lock_destroy(&msm_uport->rx.wake_lock); + uart_remove_one_port(&msm_hs_driver, &msm_uport->uport); clk_put(msm_uport->clk); @@ -457,6 +461,7 @@ static void msm_hs_set_termios(struct uart_port *uport, msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX); if (msm_uport->rx.flush == FLUSH_NONE) { + wake_lock(&msm_uport->rx.wake_lock); msm_uport->rx.flush = FLUSH_TERMIOS; msm_dmov_flush(msm_uport->dma_rx_channel); } @@ -516,8 +521,10 @@ static void msm_hs_stop_rx_locked(struct uart_port *uport) /* Disable the receiver */ msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_DISABLE_BMSK); - if (msm_uport->rx.flush == FLUSH_NONE) + if (msm_uport->rx.flush == FLUSH_NONE) { + wake_lock(&msm_uport->rx.wake_lock); msm_dmov_flush(msm_uport->dma_rx_channel); + } if (msm_uport->rx.flush != FLUSH_SHUTDOWN) msm_uport->rx.flush = FLUSH_STOP; @@ -588,6 +595,8 @@ static void msm_hs_start_rx_locked(struct uart_port *uport) msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT); msm_hs_write(uport, UARTDM_DMRX_ADDR, UARTDM_RX_BUF_SIZE); msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_ENABLE); + msm_uport->imr_reg |= UARTDM_ISR_RXLEV_BMSK; + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); } /* Enable the transmitter Interrupt */ @@ -692,11 +701,8 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, msm_hs_start_rx_locked(uport); if (flush == FLUSH_STOP) msm_uport->rx.flush = FLUSH_SHUTDOWN; - if (flush >= FLUSH_DATA_INVALID) { - clk_disable(msm_uport->clk); - spin_unlock_irqrestore(&uport->lock, flags); - return; - } + if (flush >= FLUSH_DATA_INVALID) + goto out; rx_count = msm_hs_read(uport, UARTDM_RX_TOTAL_SNAP_ADDR); @@ -708,14 +714,16 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, msm_hs_start_rx_locked(uport); - /* - * Drop the lock here since tty_flip_buffer_push() might end - * up calling uart_start(), which takes the lock. - */ +out: clk_disable(msm_uport->clk); + /* release wakelock in 500ms, not immediately, because higher layers + * don't always take wakelocks when they should */ + wake_lock_timeout(&msm_uport->rx.wake_lock, HZ / 2); + /* tty_flip_buffer_push() might call msm_hs_start(), so unlock */ spin_unlock_irqrestore(&uport->lock, flags); - tty_flip_buffer_push(tty); + if (flush < FLUSH_DATA_INVALID) + tty_flip_buffer_push(tty); } /* @@ -901,6 +909,12 @@ static irqreturn_t msm_hs_isr(int irq, void *dev) isr_status = msm_hs_read(uport, UARTDM_MISR_ADDR); + /* Uart RX starting */ + if (isr_status & UARTDM_ISR_RXLEV_BMSK) { + wake_lock(&rx->wake_lock); /* hold wakelock while rx dma */ + msm_uport->imr_reg &= ~UARTDM_ISR_RXLEV_BMSK; + msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + } /* Stale rx interrupt */ if (isr_status & UARTDM_ISR_RXSTALE_BMSK) { msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); @@ -1141,8 +1155,8 @@ static int msm_hs_startup(struct uart_port *uport) spin_lock_irqsave(&uport->lock, flags); + msm_hs_write(uport, UARTDM_RFWR_ADDR, 0); msm_hs_start_rx_locked(uport); - msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); spin_unlock_irqrestore(&uport->lock, flags); @@ -1172,6 +1186,7 @@ static int uartdm_init_port(struct uart_port *uport) tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr); init_waitqueue_head(&rx->wait); + wake_lock_init(&rx->wake_lock, WAKE_LOCK_SUSPEND, "msm_serial_hs_rx"); rx->pool = dma_pool_create("rx_buffer_pool", uport->dev, UARTDM_RX_BUF_SIZE, 16, 0); From 705eb28015bcb59a15416cf04cc3ce7cf79dbde6 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Fri, 20 Mar 2009 13:53:00 -0700 Subject: [PATCH 0253/2556] [ARM] msm: msm_serial_hs: Disable datamover when clocking off uart. Signed-off-by: Nick Pelly --- drivers/tty/serial/msm_serial_hs.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 6bea63c9381b8..9fef8574e2380 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -60,7 +60,7 @@ enum flush_reason { FLUSH_NONE, FLUSH_DATA_READY, FLUSH_DATA_INVALID, /* values after this indicate invalid data */ - FLUSH_TERMIOS = FLUSH_DATA_INVALID, + FLUSH_IGNORE = FLUSH_DATA_INVALID, FLUSH_STOP, FLUSH_SHUTDOWN, }; @@ -462,7 +462,7 @@ static void msm_hs_set_termios(struct uart_port *uport, if (msm_uport->rx.flush == FLUSH_NONE) { wake_lock(&msm_uport->rx.wake_lock); - msm_uport->rx.flush = FLUSH_TERMIOS; + msm_uport->rx.flush = FLUSH_IGNORE; msm_dmov_flush(msm_uport->dma_rx_channel); } @@ -520,7 +520,6 @@ static void msm_hs_stop_rx_locked(struct uart_port *uport) clk_enable(msm_uport->clk); /* Disable the receiver */ - msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_DISABLE_BMSK); if (msm_uport->rx.flush == FLUSH_NONE) { wake_lock(&msm_uport->rx.wake_lock); msm_dmov_flush(msm_uport->dma_rx_channel); @@ -697,7 +696,7 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, msm_hs_write(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS); flush = msm_uport->rx.flush; - if (flush == FLUSH_TERMIOS) + if (flush == FLUSH_IGNORE) msm_hs_start_rx_locked(uport); if (flush == FLUSH_STOP) msm_uport->rx.flush = FLUSH_SHUTDOWN; @@ -866,6 +865,12 @@ static int msm_hs_check_clock_off_locked(struct uart_port *uport) if (!(sr_status & UARTDM_SR_TXEMT_BMSK)) return 0; /* retry */ + if (msm_uport->rx.flush != FLUSH_SHUTDOWN) { + if (msm_uport->rx.flush == FLUSH_NONE) + msm_hs_stop_rx_locked(uport); + return 0; /* come back later to really clock off */ + } + /* we really want to clock off */ clk_disable(msm_uport->clk); msm_uport->clk_state = MSM_HS_CLK_OFF; @@ -988,8 +993,13 @@ static void msm_hs_request_clock_on_locked(struct uart_port *uport) { case MSM_HS_CLK_OFF: clk_enable(msm_uport->clk); disable_irq(msm_uport->wakeup.irq); + /* fall-through */ case MSM_HS_CLK_REQUEST_OFF: hrtimer_try_to_cancel(&msm_uport->clk_off_timer); + if (msm_uport->rx.flush == FLUSH_SHUTDOWN) + msm_hs_start_rx_locked(uport); + if (msm_uport->rx.flush == FLUSH_STOP) + msm_uport->rx.flush = FLUSH_IGNORE; msm_uport->clk_state = MSM_HS_CLK_ON; break; case MSM_HS_CLK_ON: break; From 37014e77d0b7f8f9256777b42334cfe24aa9310d Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Tue, 9 Jun 2009 13:28:50 -0700 Subject: [PATCH 0254/2556] [ARM] msm: msm_serial_hs: Update comments for 8k support Signed-off-by: Nick Pelly --- drivers/tty/serial/msm_serial_hs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 9fef8574e2380..35eebbea3b589 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -1,6 +1,6 @@ /* drivers/serial/msm_serial_hs.c * - * MSM 7k High speed uart driver + * MSM 7k/8k High speed uart driver * * Copyright (c) 2007-2008 QUALCOMM Incorporated. * Copyright (c) 2008 QUALCOMM USA, INC. From a11a31f676bbe0eeedf3aac08d7519dbf58bae0a Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 18 Dec 2008 12:40:04 -0800 Subject: [PATCH 0255/2556] [ARM] trout: Add abstract wifi control functions support --- arch/arm/mach-msm/Kconfig | 10 ++++ arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/board-trout-mmc.c | 26 ++++++---- arch/arm/mach-msm/board-trout-wifi.c | 74 ++++++++++++++++++++++++++++ arch/arm/mach-msm/board-trout.c | 30 +++++++++++ 5 files changed, 132 insertions(+), 9 deletions(-) create mode 100644 arch/arm/mach-msm/board-trout-wifi.c diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index f194b624a76ab..d56fbd0693244 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -453,4 +453,14 @@ config MSM_ADSP help Provides access to registers needed by the userspace aDSP library. +config WIFI_CONTROL_FUNC + bool "Enable WiFi control function abstraction" + help + Enables Power/Reset/Carddetect function abstraction + +config WIFI_MEM_PREALLOC + depends on WIFI_CONTROL_FUNC + bool "Preallocate memory for WiFi buffers" + help + Preallocates memory buffers for WiFi driver endif diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 86df415416c3a..8fd5877389af2 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o b obj-$(CONFIG_MACH_TROUT) += board-trout-keypad.o obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o obj-$(CONFIG_MACH_TROUT) += board-trout-rfkill.o +obj-$(CONFIG_MACH_TROUT) += board-trout-wifi.o obj-$(CONFIG_TROUT_H2W) += board-trout-h2w.o obj-$(CONFIG_TROUT_BATTCHG) += htc_battery.o obj-$(CONFIG_TROUT_PWRSINK) += htc_pwrsink.o diff --git a/arch/arm/mach-msm/board-trout-mmc.c b/arch/arm/mach-msm/board-trout-mmc.c index 41ae8721913e1..789a5135caed7 100644 --- a/arch/arm/mach-msm/board-trout-mmc.c +++ b/arch/arm/mach-msm/board-trout-mmc.c @@ -236,14 +236,7 @@ static unsigned int trout_wifi_status(struct device *dev) return trout_wifi_cd; } -static struct mmc_platform_data trout_wifi_data = { - .ocr_mask = MMC_VDD_28_29, - .status = trout_wifi_status, - .register_status_notify = trout_wifi_status_register, - .embedded_sdio = &trout_wifi_emb_data, -}; - -void trout_wifi_set_carddetect(int val) +int trout_wifi_set_carddetect(int val) { printk("%s: %d\n", __func__, val); trout_wifi_cd = val; @@ -251,8 +244,11 @@ void trout_wifi_set_carddetect(int val) wifi_status_cb(val, wifi_status_cb_devid); } else printk(KERN_WARNING "%s: Nobody to notify\n", __func__); + return 0; } +#ifndef CONFIG_WIFI_CONTROL_FUNC EXPORT_SYMBOL(trout_wifi_set_carddetect); +#endif static int trout_wifi_power_state; @@ -284,17 +280,29 @@ int trout_wifi_power(int on) trout_wifi_power_state = on; return 0; } +#ifndef CONFIG_WIFI_CONTROL_FUNC EXPORT_SYMBOL(trout_wifi_power); +#endif static int trout_wifi_reset_state; -void trout_wifi_reset(int on) +int trout_wifi_reset(int on) { printk("%s: %d\n", __func__, on); gpio_set_value( TROUT_GPIO_WIFI_PA_RESETX, !on ); trout_wifi_reset_state = on; mdelay(50); + return 0; } +#ifndef CONFIG_WIFI_CONTROL_FUNC EXPORT_SYMBOL(trout_wifi_reset); +#endif + +static struct mmc_platform_data trout_wifi_data = { + .ocr_mask = MMC_VDD_28_29, + .status = trout_wifi_status, + .register_status_notify = trout_wifi_status_register, + .embedded_sdio = &trout_wifi_emb_data, +}; int __init trout_init_mmc(unsigned int sys_rev) { diff --git a/arch/arm/mach-msm/board-trout-wifi.c b/arch/arm/mach-msm/board-trout-wifi.c new file mode 100644 index 0000000000000..51b26a4053695 --- /dev/null +++ b/arch/arm/mach-msm/board-trout-wifi.c @@ -0,0 +1,74 @@ +/* arch/arm/mach-msm/board-trout-wifi.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Dmitry Shmidt + * + * 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. + * + */ + +#ifdef CONFIG_WIFI_CONTROL_FUNC +#include +#include +#include +#include +#include +#include + +extern int trout_wifi_set_carddetect(int val); +extern int trout_wifi_power(int on); +extern int trout_wifi_reset(int on); + +#ifdef CONFIG_WIFI_MEM_PREALLOC +typedef struct wifi_mem_prealloc_struct { + void *mem_ptr; + unsigned long size; +} wifi_mem_prealloc_t; + +static wifi_mem_prealloc_t wifi_mem_array[WMPA_NUMBER_OF_SECTIONS] = { + { NULL, (WMPA_SECTION_SIZE_0 + WMPA_SECTION_HEADER) }, + { NULL, (WMPA_SECTION_SIZE_1 + WMPA_SECTION_HEADER) }, + { NULL, (WMPA_SECTION_SIZE_2 + WMPA_SECTION_HEADER) } +}; + +static void *trout_wifi_mem_prealloc(int section, unsigned long size) +{ + if( (section < 0) || (section >= WMPA_NUMBER_OF_SECTIONS) ) + return NULL; + if( wifi_mem_array[section].size < size ) + return NULL; + return wifi_mem_array[section].mem_ptr; +} + +int __init trout_init_wifi_mem( void ) +{ + int i; + + for(i=0;( i < WMPA_NUMBER_OF_SECTIONS );i++) { + wifi_mem_array[i].mem_ptr = vmalloc(wifi_mem_array[i].size); + if( wifi_mem_array[i].mem_ptr == NULL ) + return -ENOMEM; + } + return 0; +} +#endif + +struct wifi_platform_data trout_wifi_control = { + .set_power = trout_wifi_power, + .set_reset = trout_wifi_reset, + .set_carddetect = trout_wifi_set_carddetect, +#ifdef CONFIG_WIFI_MEM_PREALLOC + .mem_prealloc = trout_wifi_mem_prealloc, +#else + .mem_prealloc = NULL, +#endif +}; + +#endif diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 92c9283185928..2df009c06ed91 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -63,6 +63,9 @@ #include #include #include +#ifdef CONFIG_WIFI_CONTROL_FUNC +#include +#endif #include "proc_comm.h" #include "devices.h" @@ -71,6 +74,12 @@ void msm_init_irq(void); void msm_init_gpio(void); extern int trout_init_mmc(unsigned int); +#ifdef CONFIG_WIFI_CONTROL_FUNC +#ifdef CONFIG_WIFI_MEM_PREALLOC +extern int trout_init_wifi_mem(void); +#endif +extern struct wifi_platform_data trout_wifi_control; +#endif struct trout_axis_info { struct gpio_event_axis_info info; @@ -551,6 +560,18 @@ static struct platform_device trout_rfkill = { .id = -1, }; +#ifdef CONFIG_WIFI_CONTROL_FUNC +static struct platform_device trout_wifi = { + .name = "msm_wifi", + .id = 1, + .num_resources = 0, + .resource = NULL, + .dev = { + .platform_data = &trout_wifi_control, + }, +}; +#endif + static struct platform_device *devices[] __initdata = { &msm_device_smd, &msm_device_nand, @@ -579,6 +600,9 @@ static struct platform_device *devices[] __initdata = { &trout_ram_console_device, &trout_camera, &trout_rfkill, +#ifdef CONFIG_WIFI_CONTROL_FUNC + &trout_wifi, +#endif #if defined(CONFIG_TROUT_PWRSINK) &trout_pwr_sink, #endif @@ -752,6 +776,12 @@ static void __init trout_init(void) if (rc) printk(KERN_CRIT "%s: MMC init failure (%d)\n", __func__, rc); +#ifdef CONFIG_WIFI_MEM_PREALLOC + rc = trout_init_wifi_mem(); + if (rc) + printk(KERN_CRIT "%s: WiFi Memory init failure (%d)\n", __func__, rc); +#endif + platform_add_devices(devices, ARRAY_SIZE(devices)); i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); From 27761c007a187ef521d511e7e294303fb82f67fe Mon Sep 17 00:00:00 2001 From: San Mehat Date: Wed, 17 Dec 2008 13:45:12 -0800 Subject: [PATCH 0256/2556] [ARM] msm: audio: Move allocation of DMA buffer to driver init Signed-off-by: San Mehat --- arch/arm/mach-msm/qdsp5/audio_in.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-msm/qdsp5/audio_in.c b/arch/arm/mach-msm/qdsp5/audio_in.c index a9b301313a473..b4e3ce7ea33c5 100644 --- a/arch/arm/mach-msm/qdsp5/audio_in.c +++ b/arch/arm/mach-msm/qdsp5/audio_in.c @@ -679,15 +679,6 @@ static int audio_in_open(struct inode *inode, struct file *file) goto done; } - if (!audio->data) { - audio->data = dma_alloc_coherent(NULL, DMASZ, - &audio->phys, GFP_KERNEL); - if (!audio->data) { - rc = -ENOMEM; - goto done; - } - } - /* Settings will be re-config at AUDIO_SET_CONFIG, * but at least we need to have initial config */ @@ -740,6 +731,14 @@ struct miscdevice audio_in_misc = { static int __init audio_in_init(void) { + the_audio_in.data = dma_alloc_coherent(NULL, DMASZ, + &the_audio_in.phys, GFP_KERNEL); + if (!the_audio_in.data) { + printk(KERN_ERR "%s: Unable to allocate DMA buffer\n", + __func__); + return -ENOMEM; + } + mutex_init(&the_audio_in.lock); mutex_init(&the_audio_in.read_lock); spin_lock_init(&the_audio_in.dsp_lock); From f6bc60163cc631c0069ca4b4522e31c5ddb860de Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Fri, 19 Dec 2008 13:04:31 -0800 Subject: [PATCH 0257/2556] [ARM] msm: audio: add a driver for the SND interface Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/include/mach/board.h | 12 +- arch/arm/mach-msm/qdsp5/Makefile | 1 + arch/arm/mach-msm/qdsp5/snd.c | 300 +++++++++++++++++++++++++ include/linux/msm_audio.h | 47 +++- 4 files changed, 358 insertions(+), 2 deletions(-) create mode 100644 arch/arm/mach-msm/qdsp5/snd.c diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index a4df72b5e64b4..318caa5a50230 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -31,12 +31,22 @@ struct msm_acpu_clock_platform_data unsigned long wait_for_irq_khz; }; -struct msm_camera_device_platform_data{ +struct msm_camera_device_platform_data { int sensor_reset; int sensor_pwd; int vcm_pwd; }; +struct snd_endpoint { + int id; + const char *name; +}; + +struct msm_snd_endpoints { + struct snd_endpoint *endpoints; + unsigned num; +}; + struct clk; extern struct sys_timer msm_timer; diff --git a/arch/arm/mach-msm/qdsp5/Makefile b/arch/arm/mach-msm/qdsp5/Makefile index 7bd5ce575a52f..30343066b13c0 100644 --- a/arch/arm/mach-msm/qdsp5/Makefile +++ b/arch/arm/mach-msm/qdsp5/Makefile @@ -10,4 +10,5 @@ obj-y += adsp_jpeg_verify_cmd.o adsp_jpeg_patch_event.o obj-y += adsp_vfe_verify_cmd.o adsp_vfe_patch_event.o obj-y += adsp_lpm_verify_cmd.o obj-y += audio_out.o audio_in.o audio_mp3.o audmgr.o audpp.o +obj-y += snd.o diff --git a/arch/arm/mach-msm/qdsp5/snd.c b/arch/arm/mach-msm/qdsp5/snd.c new file mode 100644 index 0000000000000..5377176260cd1 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/snd.c @@ -0,0 +1,300 @@ +/* arch/arm/mach-msm/qdsp5/snd.c + * + * interface to "snd" service on the baseband cpu + * + * Copyright (C) 2008 HTC Corporation + * + * 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 + +struct snd_ctxt { + struct mutex lock; + int opened; + struct msm_rpc_endpoint *ept; + struct msm_snd_endpoints *snd_epts; +}; + +static struct snd_ctxt the_snd; + +#define RPC_SND_PROG 0x30000002 +#define RPC_SND_CB_PROG 0x31000002 +#if CONFIG_MSM_AMSS_VERSION == 6210 +#define RPC_SND_VERS 0x94756085 /* 2490720389 */ +#elif (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225) +#define RPC_SND_VERS 0xaa2b1a44 /* 2854951492 */ +#endif + +#define SND_SET_DEVICE_PROC 2 +#define SND_SET_VOLUME_PROC 3 + +struct rpc_snd_set_device_args { + uint32_t device; + uint32_t ear_mute; + uint32_t mic_mute; + + uint32_t cb_func; + uint32_t client_data; +}; + +struct rpc_snd_set_volume_args { + uint32_t device; + uint32_t method; + uint32_t volume; + + uint32_t cb_func; + uint32_t client_data; +}; + +struct snd_set_device_msg { + struct rpc_request_hdr hdr; + struct rpc_snd_set_device_args args; +}; + +struct snd_set_volume_msg { + struct rpc_request_hdr hdr; + struct rpc_snd_set_volume_args args; +}; + +struct snd_endpoint *get_snd_endpoints(int *size); + +static inline int check_device(struct snd_ctxt *snd, int device) +{ + int cnt; + for (cnt = 0; cnt < snd->snd_epts->num; cnt++) + if (device == snd->snd_epts->endpoints[cnt].id) + return 0; + return -EINVAL; +} + +static inline int check_mute(int mute) +{ + return (mute == SND_MUTE_MUTED || + mute == SND_MUTE_UNMUTED) ? 0 : -EINVAL; +} + +static int get_endpoint(struct snd_ctxt *snd, unsigned long arg) +{ + int rc = 0, index; + struct msm_snd_endpoint ept; + + if (copy_from_user(&ept, (void __user *)arg, sizeof(ept))) { + pr_err("snd_ioctl get endpoint: invalid read pointer.\n"); + return -EFAULT; + } + + index = ept.id; + if (index < 0 || index >= snd->snd_epts->num) { + pr_err("snd_ioctl get endpoint: invalid index!\n"); + return -EINVAL; + } + + ept.id = snd->snd_epts->endpoints[index].id; + strncpy(ept.name, + snd->snd_epts->endpoints[index].name, + sizeof(ept.name)); + + if (copy_to_user((void __user *)arg, &ept, sizeof(ept))) { + pr_err("snd_ioctl get endpoint: invalid write pointer.\n"); + rc = -EFAULT; + } + + return rc; +} + +static long snd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct snd_set_device_msg dmsg; + struct snd_set_volume_msg vmsg; + struct msm_snd_device_config dev; + struct msm_snd_volume_config vol; + struct snd_ctxt *snd = file->private_data; + int rc = 0; + + mutex_lock(&snd->lock); + switch (cmd) { + case SND_SET_DEVICE: + if (copy_from_user(&dev, (void __user *) arg, sizeof(dev))) { + pr_err("snd_ioctl set device: invalid pointer.\n"); + rc = -EFAULT; + break; + } + + + rc = check_device(snd, dev.device); + if (rc < 0) { + pr_err("snd_ioctl set device: invalid device.\n"); + rc = -EINVAL; + break; + } + + dmsg.args.device = cpu_to_be32(dev.device); + dmsg.args.ear_mute = cpu_to_be32(dev.ear_mute); + dmsg.args.mic_mute = cpu_to_be32(dev.mic_mute); + if (check_mute(dev.ear_mute) < 0 || + check_mute(dev.mic_mute) < 0) { + pr_err("snd_ioctl set device: invalid mute status.\n"); + rc = -EINVAL; + break; + } + dmsg.args.cb_func = -1; + dmsg.args.client_data = 0; + + pr_info("snd_set_device %d %d %d\n", dev.device, + dev.ear_mute, dev.mic_mute); + + rc = msm_rpc_call(snd->ept, + SND_SET_DEVICE_PROC, + &dmsg, sizeof(dmsg), 5 * HZ); + break; + + case SND_SET_VOLUME: + if (copy_from_user(&vol, (void __user *) arg, sizeof(vol))) { + pr_err("snd_ioctl set volume: invalid pointer.\n"); + rc = -EFAULT; + break; + } + + rc = check_device(snd, vol.device); + if (rc < 0) { + pr_err("snd_ioctl set volume: invalid device.\n"); + rc = -EINVAL; + break; + } + + vmsg.args.device = cpu_to_be32(vol.device); + vmsg.args.method = cpu_to_be32(vol.method); + if (vol.method != SND_METHOD_VOICE) { + pr_err("snd_ioctl set volume: invalid method.\n"); + rc = -EINVAL; + break; + } + + vmsg.args.volume = cpu_to_be32(vol.volume); + vmsg.args.cb_func = -1; + vmsg.args.client_data = 0; + + pr_info("snd_set_volume %d %d %d\n", vol.device, + vol.method, vol.volume); + + rc = msm_rpc_call(snd->ept, + SND_SET_VOLUME_PROC, + &vmsg, sizeof(vmsg), 5 * HZ); + break; + + case SND_GET_NUM_ENDPOINTS: + if (copy_to_user((void __user *)arg, + &snd->snd_epts->num, sizeof(unsigned))) { + pr_err("snd_ioctl get endpoint: invalid pointer.\n"); + rc = -EFAULT; + } + break; + + case SND_GET_ENDPOINT: + rc = get_endpoint(snd, arg); + break; + + default: + pr_err("snd_ioctl unknown command.\n"); + rc = -EINVAL; + break; + } + mutex_unlock(&snd->lock); + + return rc; +} + +static int snd_release(struct inode *inode, struct file *file) +{ + struct snd_ctxt *snd = file->private_data; + + mutex_lock(&snd->lock); + snd->opened = 0; + mutex_unlock(&snd->lock); + return 0; +} + +static int snd_open(struct inode *inode, struct file *file) +{ + struct snd_ctxt *snd = &the_snd; + int rc = 0; + + mutex_lock(&snd->lock); + if (snd->opened == 0) { + if (snd->ept == NULL) { + snd->ept = msm_rpc_connect(RPC_SND_PROG, RPC_SND_VERS, + MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(snd->ept)) { + rc = PTR_ERR(snd->ept); + snd->ept = NULL; + pr_err("snd: failed to connect snd svc\n"); + goto err; + } + } + file->private_data = snd; + snd->opened = 1; + } else { + pr_err("snd already opened.\n"); + rc = -EBUSY; + } + +err: + mutex_unlock(&snd->lock); + return rc; +} + +static struct file_operations snd_fops = { + .owner = THIS_MODULE, + .open = snd_open, + .release = snd_release, + .unlocked_ioctl = snd_ioctl, +}; + +struct miscdevice snd_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_snd", + .fops = &snd_fops, +}; + +static int snd_probe(struct platform_device *pdev) +{ + struct snd_ctxt *snd = &the_snd; + mutex_init(&snd->lock); + snd->snd_epts = (struct msm_snd_endpoints *)pdev->dev.platform_data; + return misc_register(&snd_misc); +} + +static struct platform_driver snd_plat_driver = { + .probe = snd_probe, + .driver = { + .name = "msm_snd", + .owner = THIS_MODULE, + }, +}; + +static int __init snd_init(void) +{ + return platform_driver_register(&snd_plat_driver); +} + +module_init(snd_init); diff --git a/include/linux/msm_audio.h b/include/linux/msm_audio.h index df600c9d4c5e6..cd2165b1b3b74 100644 --- a/include/linux/msm_audio.h +++ b/include/linux/msm_audio.h @@ -20,6 +20,8 @@ #include #include +/* PCM Audio */ + #define AUDIO_IOCTL_MAGIC 'a' #define AUDIO_START _IOW(AUDIO_IOCTL_MAGIC, 0, unsigned) @@ -49,4 +51,47 @@ struct msm_audio_stats { uint32_t unused[2]; }; -#endif +/* Audio routing */ + +#define SND_IOCTL_MAGIC 's' + +#define SND_MUTE_UNMUTED 0 +#define SND_MUTE_MUTED 1 + +struct msm_snd_device_config { + uint32_t device; + uint32_t ear_mute; + uint32_t mic_mute; +}; + +#define SND_SET_DEVICE _IOW(SND_IOCTL_MAGIC, 2, struct msm_device_config *) + +#define SND_METHOD_VOICE 0 + +struct msm_snd_volume_config { + uint32_t device; + uint32_t method; + uint32_t volume; +}; + +#define SND_SET_VOLUME _IOW(SND_IOCTL_MAGIC, 3, struct msm_snd_volume_config *) + +/* Returns the number of SND endpoints supported. */ + +#define SND_GET_NUM_ENDPOINTS _IOR(SND_IOCTL_MAGIC, 4, unsigned *) + +struct msm_snd_endpoint { + int id; /* input and output */ + char name[64]; /* output only */ +}; + +/* Takes an index between 0 and one less than the number returned by + * SND_GET_NUM_ENDPOINTS, and returns the SND index and name of a + * SND endpoint. On input, the .id field contains the number of the + * endpoint, and on exit it contains the SND index, while .name contains + * the description of the endpoint. + */ + +#define SND_GET_ENDPOINT _IOWR(SND_IOCTL_MAGIC, 5, struct msm_snd_endpoint *) + +#endif /* __LINUX_MSM_AUDIO_H */ From 3246dd8bfd2c57af24352602aa44e53a2fcdf749 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Fri, 19 Dec 2008 13:30:47 -0800 Subject: [PATCH 0258/2556] [ARM] msm: halibut: add list of SND endpoints Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-halibut.c | 32 +++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-msm/board-halibut.c b/arch/arm/mach-msm/board-halibut.c index f738db6ef7260..ffd5d966e1719 100644 --- a/arch/arm/mach-msm/board-halibut.c +++ b/arch/arm/mach-msm/board-halibut.c @@ -60,10 +60,33 @@ static struct platform_device smc91x_device = { }; static struct i2c_board_info i2c_devices[] = { - { + { I2C_BOARD_INFO("mt9t013", 0x78>>1), /* .irq = TROUT_GPIO_TO_INT(TROUT_GPIO_CAM_BTN_STEP1_N), */ - }, + }, +}; + +#define SND(desc, num) { .name = #desc, .id = num } +static struct snd_endpoint snd_endpoints_list[] = { + SND(HANDSET, 0), + SND(HEADSET, 2), + SND(SPEAKER, 6), + SND(BT, 12), + SND(CURRENT, 25), +}; +#undef SND + +static struct msm_snd_endpoints halibut_snd_endpoints = { + .endpoints = snd_endpoints_list, + .num = sizeof(snd_endpoints_list) / sizeof(struct snd_endpoint) +}; + +static struct platform_device halibut_snd = { + .name = "msm_snd", + .id = -1, + .dev = { + .platform_data = &halibut_snd_endpoints + }, }; static struct platform_device *devices[] __initdata = { @@ -75,6 +98,7 @@ static struct platform_device *devices[] __initdata = { &msm_device_hsusb, &msm_device_i2c, &smc91x_device, + &halibut_snd, }; extern struct sys_timer msm_timer; @@ -93,7 +117,7 @@ static struct msm_acpu_clock_platform_data halibut_clock_data = { }; void msm_serial_debug_init(unsigned int base, int irq, - struct device *clk_device, int signal_irq); + struct device *clk_device, int signal_irq); static void __init halibut_init(void) { @@ -109,7 +133,7 @@ static void __init halibut_init(void) static void __init halibut_fixup(struct machine_desc *desc, struct tag *tags, char **cmdline, struct meminfo *mi) { - mi->nr_banks=1; + mi->nr_banks = 1; mi->bank[0].start = PHYS_OFFSET; mi->bank[0].size = (101*1024*1024); } From ec234d753fb150897f7e3f34d0b49bcb3c2e2848 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Fri, 19 Dec 2008 13:35:06 -0800 Subject: [PATCH 0259/2556] [ARM] msm: trout: add list of SND endpoints Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-trout.c | 62 +++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 2df009c06ed91..08e5512450b55 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -54,6 +54,7 @@ #include #include +#include #include "board-trout.h" @@ -572,6 +573,66 @@ static struct platform_device trout_wifi = { }; #endif +#define SND(desc,num) { .name = #desc, .id = num } +static struct snd_endpoint snd_endpoints_list[] = { + SND(HANDSET, 0), + SND(SPEAKER, 1), + SND(HEADSET, 2), + SND(BT, 3), + SND(HEADSET_AND_SPEAKER, 10), + SND(CURRENT, 256), + + /* Bluetooth accessories. */ + + SND(BH_S100, 12), + SND(BH_M100, 13), + SND(Motorola_H500, 14), + SND(Nokia_HS_36W, 15), + SND(PLT_510vD, 16), + SND(M2500_Plantronics, 17), + SND(Nokia_HDW_3, 18), + SND(HBH_608, 19), + SND(HBH_DS970, 20), + SND(iTech_BlueBAND, 21), + SND(Nokia_BH_800, 22), + SND(Motorola_H700, 23), + SND(HTC_BH_M200, 24), + SND(Jabra_JX10, 25), + SND(Plantronics_320, 26), + SND(Plantronics_640, 27), + SND(Jabra_BT500, 28), + SND(Motorola_HT820, 29), + SND(HBH_IV840, 30), + SND(Plantronics_6XX, 31), + SND(Plantronics_3XX, 32), + SND(HBH_PV710, 33), + SND(Motorola_H670, 34), + SND(HBM_300, 35), + SND(Nokia_BH_208, 36), + SND(Samsung_WEP410, 37), + SND(Jabra_BT8010, 38), + SND(Motorola_S9, 39), + SND(Jabra_BT620s, 40), + SND(Nokia_BH_902, 41), + SND(HBH_DS220, 42), + SND(HBH_DS980, 43), + SND(BT_EC_OFF, 44), +}; +#undef SND + +static struct msm_snd_endpoints trout_snd_endpoints = { + .endpoints = snd_endpoints_list, + .num = ARRAY_SIZE(snd_endpoints_list), +}; + +static struct platform_device trout_snd = { + .name = "msm_snd", + .id = -1, + .dev = { + .platform_data = &trout_snd_endpoints, + }, +}; + static struct platform_device *devices[] __initdata = { &msm_device_smd, &msm_device_nand, @@ -606,6 +667,7 @@ static struct platform_device *devices[] __initdata = { #if defined(CONFIG_TROUT_PWRSINK) &trout_pwr_sink, #endif + &trout_snd, }; extern struct sys_timer msm_timer; From 135e27ee4daa71ead51fe8c7fde50e88695dacc6 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 23 Dec 2008 16:05:31 -0800 Subject: [PATCH 0260/2556] [ARM] msm: trout: amend the names of Bluetooth devices Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-trout.c | 80 ++++++++++++++++----------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 08e5512450b55..e3c11ef307780 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -573,50 +573,50 @@ static struct platform_device trout_wifi = { }; #endif -#define SND(desc,num) { .name = #desc, .id = num } +#define SND(num, desc) { .name = desc, .id = num } static struct snd_endpoint snd_endpoints_list[] = { - SND(HANDSET, 0), - SND(SPEAKER, 1), - SND(HEADSET, 2), - SND(BT, 3), - SND(HEADSET_AND_SPEAKER, 10), - SND(CURRENT, 256), + SND(0, "HANDSET"), + SND(1, "SPEAKER"), + SND(2, "HEADSET"), + SND(3, "BT"), + SND(44, "BT_EC_OFF"), + SND(10, "HEADSET_AND_SPEAKER"), + SND(256, "CURRENT"), /* Bluetooth accessories. */ - SND(BH_S100, 12), - SND(BH_M100, 13), - SND(Motorola_H500, 14), - SND(Nokia_HS_36W, 15), - SND(PLT_510vD, 16), - SND(M2500_Plantronics, 17), - SND(Nokia_HDW_3, 18), - SND(HBH_608, 19), - SND(HBH_DS970, 20), - SND(iTech_BlueBAND, 21), - SND(Nokia_BH_800, 22), - SND(Motorola_H700, 23), - SND(HTC_BH_M200, 24), - SND(Jabra_JX10, 25), - SND(Plantronics_320, 26), - SND(Plantronics_640, 27), - SND(Jabra_BT500, 28), - SND(Motorola_HT820, 29), - SND(HBH_IV840, 30), - SND(Plantronics_6XX, 31), - SND(Plantronics_3XX, 32), - SND(HBH_PV710, 33), - SND(Motorola_H670, 34), - SND(HBM_300, 35), - SND(Nokia_BH_208, 36), - SND(Samsung_WEP410, 37), - SND(Jabra_BT8010, 38), - SND(Motorola_S9, 39), - SND(Jabra_BT620s, 40), - SND(Nokia_BH_902, 41), - SND(HBH_DS220, 42), - SND(HBH_DS980, 43), - SND(BT_EC_OFF, 44), + SND(12, "HTC BH S100"), + SND(13, "HTC BH M100"), + SND(14, "Motorola H500"), + SND(15, "Nokia HS-36W"), + SND(16, "PLT 510v.D"), + SND(17, "M2500 by Plantronics"), + SND(18, "Nokia HDW-3"), + SND(19, "HBH-608"), + SND(20, "HBH-DS970"), + SND(21, "i.Tech BlueBAND"), + SND(22, "Nokia BH-800"), + SND(23, "Motorola H700"), + SND(24, "HTC BH M200"), + SND(25, "Jabra JX10"), + SND(26, "320Plantronics"), + SND(27, "640Plantronics"), + SND(28, "Jabra BT500"), + SND(29, "Motorola HT820"), + SND(30, "HBH-IV840"), + SND(31, "6XXPlantronics"), + SND(32, "3XXPlantronics"), + SND(33, "HBH-PV710"), + SND(34, "Motorola H670"), + SND(35, "HBM-300"), + SND(36, "Nokia BH-208"), + SND(37, "Samsung WEP410"), + SND(38, "Jabra BT8010"), + SND(39, "Motorola S9"), + SND(40, "Jabra BT620s"), + SND(41, "Nokia BH-902"), + SND(42, "HBH-DS220"), + SND(43, "HBH-DS980"), }; #undef SND From 51fe6b46fa558732594a1b1a1ffacf0aaefc128b Mon Sep 17 00:00:00 2001 From: Laurence Chen Date: Thu, 8 Jan 2009 11:22:27 +0800 Subject: [PATCH 0261/2556] [ARM] msm: qdsp: Fix no in call voice after video record This is due to QDSP Module definitions that are not correct --- arch/arm/mach-msm/qdsp5/adsp_6225.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/qdsp5/adsp_6225.c b/arch/arm/mach-msm/qdsp5/adsp_6225.c index 6806cc12be7d7..5078afbb1a8c4 100644 --- a/arch/arm/mach-msm/qdsp5/adsp_6225.c +++ b/arch/arm/mach-msm/qdsp5/adsp_6225.c @@ -33,7 +33,8 @@ typedef enum { QDSP_MODULE_AUDRECTASK, QDSP_MODULE_AUDPREPROCTASK, QDSP_MODULE_SBC_ENC, - QDSP_MODULE_VOC, + QDSP_MODULE_VOC_UMTS, + QDSP_MODULE_VOC_CDMA, QDSP_MODULE_VOC_PCM, QDSP_MODULE_VOCENCTASK, QDSP_MODULE_VOCDECTASK, From f79f4ef3692d19493dead1de53d5b66cb5daa3f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 16 Jan 2009 15:55:29 -0800 Subject: [PATCH 0262/2556] [ARM] msm: qdsp5: Fix 2.6.29 build. --- arch/arm/mach-msm/qdsp5/audio_out.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-msm/qdsp5/audio_out.c b/arch/arm/mach-msm/qdsp5/audio_out.c index 84dcaaa11daca..de6e01563e9cc 100644 --- a/arch/arm/mach-msm/qdsp5/audio_out.c +++ b/arch/arm/mach-msm/qdsp5/audio_out.c @@ -616,14 +616,16 @@ static ssize_t audio_write(struct file *file, const char __user *buf, size_t xfer; int old_prio = current->rt_priority; int old_policy = current->policy; - int cap_nice = cap_raised(current->cap_effective, CAP_SYS_NICE); + int cap_nice = cap_raised(current_cap(), CAP_SYS_NICE); int rc = 0; LOG(EV_WRITE, count | (audio->running << 28) | (audio->stopped << 24)); /* just for this write, set us real-time */ if (!task_has_rt_policy(current)) { - cap_raise(current->cap_effective, CAP_SYS_NICE); + struct cred *new = prepare_creds(); + cap_raise(new->cap_effective, CAP_SYS_NICE); + commit_creds(new); sched_setscheduler(current, SCHED_RR, &s); } @@ -669,8 +671,12 @@ static ssize_t audio_write(struct file *file, const char __user *buf, if (!rt_policy(old_policy)) { struct sched_param v = { .sched_priority = old_prio }; sched_setscheduler(current, old_policy, &v); - if (likely(!cap_nice)) - cap_lower(current->cap_effective, CAP_SYS_NICE); + if (likely(!cap_nice)) { + struct cred *new = prepare_creds(); + cap_lower(new->cap_effective, CAP_SYS_NICE); + commit_creds(new); + sched_setscheduler(current, SCHED_RR, &s); + } } LOG(EV_RETURN,(buf > start) ? (buf - start) : rc); From 1b60d2db74a7d79a9e25c217dca5218cc1c83091 Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Zavin Date: Fri, 23 Jan 2009 14:00:46 -0800 Subject: [PATCH 0263/2556] [ARM] msm: trout: Add panel width and height to board file for dpi calculation Signed-off-by: Rebecca Schultz Zavin --- arch/arm/mach-msm/board-trout-panel.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-msm/board-trout-panel.c b/arch/arm/mach-msm/board-trout-panel.c index e81374c30875a..8b350d1a382c9 100644 --- a/arch/arm/mach-msm/board-trout-panel.c +++ b/arch/arm/mach-msm/board-trout-panel.c @@ -572,6 +572,8 @@ struct msm_mddi_toshiba_client_data toshiba_client_data = { .fb_data = { .xres = 320, .yres = 480, + .width = 45, + .height = 67, .output_format = 0, }, }; From 416e0f78766937489cd9061ffbb4a037a7d3f195 Mon Sep 17 00:00:00 2001 From: Ben Cheng Date: Fri, 6 Feb 2009 16:10:34 -0800 Subject: [PATCH 0264/2556] [ARM] msm: oprofile: Enable OProfile and preserve performance counters. Signed-off-by: Ben Cheng --- arch/arm/mach-msm/idle.S | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/idle.S b/arch/arm/mach-msm/idle.S index 588eb072da822..d4c3e81747312 100644 --- a/arch/arm/mach-msm/idle.S +++ b/arch/arm/mach-msm/idle.S @@ -27,6 +27,13 @@ ENTRY(msm_pm_collapse) mrc p15, 0, r3, c3, c0, 0 /* dacr */ mrc p15, 0, ip, c13, c0, 1 /* context ID */ stmia r0!, {r1-r3, ip} +#if defined(CONFIG_OPROFILE) + mrc p15, 0, r1, c15, c12, 0 /* pmnc */ + mrc p15, 0, r2, c15, c12, 1 /* ccnt */ + mrc p15, 0, r3, c15, c12, 2 /* pmn0 */ + mrc p15, 0, ip, c15, c12, 3 /* pmn1 */ + stmia r0!, {r1-r3, ip} +#endif /* fall though */ ENTRY(msm_arch_idle) #if defined(CONFIG_MSM_FIQ_SUPPORT) @@ -63,6 +70,13 @@ ENTRY(msm_pm_collapse_exit) adr r3, msm_pm_collapse_exit add r1, r1, r3 sub r1, r1, r2 +#if defined(CONFIG_OPROFILE) + ldmdb r1!, {r2-r5} + mcr p15, 0, r3, c15, c12, 1 /* ccnt */ + mcr p15, 0, r4, c15, c12, 2 /* pmn0 */ + mcr p15, 0, r5, c15, c12, 3 /* pmn1 */ + mcr p15, 0, r2, c15, c12, 0 /* pmnc */ +#endif ldmdb r1!, {r2-r5} mcr p15, 0, r4, c3, c0, 0 /* dacr */ mcr p15, 0, r3, c2, c0, 0 /* ttb */ @@ -84,6 +98,9 @@ ENTRY(msm_pm_collapse_exit) saved_state: .space 4 * 11 /* r4-14 */ - .space 4 * 4 /* cp15 */ + .space 4 * 4 /* cp15 - MMU control, ttb, dacr, context ID */ +#if defined(CONFIG_OPROFILE) + .space 4 * 4 /* more cp15 - pmnc, ccnt, pmn0, pmn1 */ +#endif saved_state_end: From df7b7299382bca308e5c3ba7e5e46cf722268ec0 Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Thu, 12 Feb 2009 18:24:11 -0800 Subject: [PATCH 0265/2556] [ARM] msm: trout: Fix touchscreen power on/off sequence. Level shifter gpio was not set to be an output. Signed-off-by: Mike Chan --- arch/arm/mach-msm/board-trout.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index e3c11ef307780..1164eed6a25e7 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -227,6 +227,7 @@ static int trout_ts_power(int on) } else { gpio_set_value(tp_ls_gpio, 0); + udelay(50); gpio_set_value(TROUT_GPIO_TP_EN, 0); gpio_set_value(TROUT_GPIO_TP_I2C_PULL, 0); } @@ -809,6 +810,9 @@ static void __init trout_init(void) msm_hw_reset_hook = trout_reset; + gpio_direction_output(system_rev < 5 ? + TROUT_4_TP_LS_EN : TROUT_5_TP_LS_EN, 0); + msm_acpu_clock_init(&trout_clock_data); #if defined(CONFIG_MSM_SERIAL_DEBUGGER) From ba909e4529fc4fdf6dda62df032650f5832f30ae Mon Sep 17 00:00:00 2001 From: HK Chen Date: Mon, 9 Feb 2009 19:22:21 -0800 Subject: [PATCH 0266/2556] [ARM] msm: audmgr: avoid leaking audio manager handles A race condition would allow audio manager handles to be leaked in situations where the driver was rapidly closed and reopened, eventually leading to the baseband being incapable of creating a new handle, and then a panic and reboot. Signed-off-by: Brian Swetland --- arch/arm/mach-msm/qdsp5/audmgr.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-msm/qdsp5/audmgr.c b/arch/arm/mach-msm/qdsp5/audmgr.c index dd99413e2ffca..fbebc8fb0f4e3 100644 --- a/arch/arm/mach-msm/qdsp5/audmgr.c +++ b/arch/arm/mach-msm/qdsp5/audmgr.c @@ -61,7 +61,7 @@ static void process_audmgr_callback(struct audmgr *am, case RPC_AUDMGR_STATUS_READY: if (len < sizeof(uint32_t) * 4) break; - am->handle = args->u.handle; + am->handle = be32_to_cpu(args->u.handle); pr_info("audmgr: rpc READY handle=0x%08x\n", am->handle); break; case RPC_AUDMGR_STATUS_CODEC_CONFIG: { @@ -242,10 +242,7 @@ int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg) if (am->state == STATE_ENABLED) return 0; - if (am->state == STATE_ENABLING) - goto wait_for_it; -try_again: if (am->state == STATE_DISABLING) pr_err("audmgr: state is DISABLING in enable?\n"); am->state = STATE_ENABLING; @@ -266,18 +263,14 @@ int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg) if (rc < 0) return rc; -wait_for_it: rc = wait_event_timeout(am->wait, am->state != STATE_ENABLING, 15 * HZ); if (rc == 0) { - pr_err("audmgr: ARM9 did not reply to RPC\n"); + pr_err("audmgr_enable: ARM9 did not reply to RPC am->state = %d\n", am->state); BUG(); } if (am->state == STATE_ENABLED) return 0; - if (am->state == STATE_DISABLED) { - pr_err("audmgr: DISABLED WHILE ENABLING. retrying...\n"); - goto try_again; - } + pr_err("audmgr: unexpected state %d while enabling?!\n", am->state); return -ENODEV; } @@ -289,12 +282,10 @@ int audmgr_disable(struct audmgr *am) if (am->state == STATE_DISABLED) return 0; - if (am->state == STATE_DISABLING) - return 0; msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, AUDMGR_VERS, AUDMGR_DISABLE_CLIENT); - msg.handle = am->handle; + msg.handle = cpu_to_be32(am->handle); am->state = STATE_DISABLING; @@ -302,5 +293,15 @@ int audmgr_disable(struct audmgr *am) if (rc < 0) return rc; - return 0; + rc = wait_event_timeout(am->wait, am->state != STATE_DISABLING, 15 * HZ); + if (rc == 0) { + pr_err("audmgr_disable: ARM9 did not reply to RPC am->state = %d\n", am->state); + BUG(); + } + + if (am->state == STATE_DISABLED) + return 0; + + pr_err("audmgr: unexpected state %d while disabling?!\n", am->state); + return -ENODEV; } From d189faf244170287e98ac7e6a61fa329695965dc Mon Sep 17 00:00:00 2001 From: Thomas Tsai Date: Tue, 17 Feb 2009 14:00:09 -0800 Subject: [PATCH 0267/2556] [ARM] msm: htc_headset: Import new headset driver from HTC Change-Id: I3ad2979209989ab7e831feea34cb6466fc1159fe Signed-off-by: San Mehat --- arch/arm/mach-msm/htc_headset.c | 1247 ++++++++++++++++++ arch/arm/mach-msm/include/mach/htc_headset.h | 173 +++ 2 files changed, 1420 insertions(+) create mode 100644 arch/arm/mach-msm/htc_headset.c create mode 100644 arch/arm/mach-msm/include/mach/htc_headset.h diff --git a/arch/arm/mach-msm/htc_headset.c b/arch/arm/mach-msm/htc_headset.c new file mode 100644 index 0000000000000..803deac20a7d2 --- /dev/null +++ b/arch/arm/mach-msm/htc_headset.c @@ -0,0 +1,1247 @@ +/* + * H2W device detection driver. + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC, Inc. + * + * Authors: + * Laurence Chen + * Nick Pelly + * Thomas Tsai + * Farmer Tseng + * + * 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; version 2 of the License. + */ + +/* For detecting HTC 2 Wire devices, such as wired headset. + + Logically, the H2W driver is always present, and H2W state (hi->state) + indicates what is currently plugged into the H2W interface. + + When the headset is plugged in, CABLE_IN1 is pulled low. When the headset + button is pressed, CABLE_IN2 is pulled low. These two lines are shared with + the TX and RX (respectively) of UART3 - used for serial debugging. + + This headset driver keeps the CPLD configured as UART3 for as long as + possible, so that we can do serial FIQ debugging even when the kernel is + locked and this driver no longer runs. So it only configures the CPLD to + GPIO while the headset is plugged in, and for 10ms during detection work. + + Unfortunately we can't leave the CPLD as UART3 while a headset is plugged + in, UART3 is pullup on TX but the headset is pull-down, causing a 55 mA + drain on trout. + + The headset detection work involves setting CPLD to GPIO, and then pulling + CABLE_IN1 high with a stronger pullup than usual. A H2W headset will still + pull this line low, whereas other attachments such as a serial console + would get pulled up by this stronger pullup. + + Headset insertion/removal causes UEvent's to be sent, and + /sys/class/switch/h2w/state to be updated. + + Button presses are interpreted as input event (KEY_MEDIA). Button presses + are ignored if the headset is plugged in, so the buttons on 11 pin -> 3.5mm + jack adapters do not work until a headset is plugged into the adapter. This + is to avoid serial RX traffic causing spurious button press events. + + We tend to check the status of CABLE_IN1 a few more times than strictly + necessary during headset detection, to avoid spurious headset insertion + events caused by serial debugger TX traffic. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define H2WI(fmt, arg...) \ + printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg) +#define H2WE(fmt, arg...) \ + printk(KERN_ERR "[H2W] %s " fmt "\r\n", __func__, ## arg) + +#ifdef CONFIG_DEBUG_H2W +#define H2W_DBG(fmt, arg...) printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg) +#else +#define H2W_DBG(fmt, arg...) do {} while (0) +#endif + +static struct workqueue_struct *g_detection_work_queue; +static void detection_work(struct work_struct *work); +static DECLARE_WORK(g_detection_work, detection_work); + +struct h2w_info { + struct switch_dev sdev; + struct input_dev *input; + struct mutex mutex_lock; + + atomic_t btn_state; + int ignore_btn; + + unsigned int irq; + unsigned int irq_btn; + + int cable_in1; + int cable_in2; + int h2w_clk; + int h2w_data; + int debug_uart; + + void (*config_cpld) (int); + void (*init_cpld) (void); + /* for h2w */ + void (*set_dat)(int); + void (*set_clk)(int); + void (*set_dat_dir)(int); + void (*set_clk_dir)(int); + int (*get_dat)(void); + int (*get_clk)(void); + + int htc_headset_flag; + + struct hrtimer timer; + ktime_t debounce_time; + + struct hrtimer btn_timer; + ktime_t btn_debounce_time; + + H2W_INFO h2w_info; + H2W_SPEED speed; + struct vreg *vreg_h2w; +}; +static struct h2w_info *hi; + +static ssize_t h2w_print_name(struct switch_dev *sdev, char *buf) +{ + switch (switch_get_state(&hi->sdev)) { + case H2W_NO_DEVICE: + return sprintf(buf, "No Device\n"); + case H2W_HTC_HEADSET: + return sprintf(buf, "Headset\n"); + } + return -EINVAL; +} + +static void button_pressed(void) +{ + H2W_DBG("button_pressed \n"); + atomic_set(&hi->btn_state, 1); + input_report_key(hi->input, KEY_MEDIA, 1); + input_sync(hi->input); +} + +static void button_released(void) +{ + H2W_DBG("button_released \n"); + atomic_set(&hi->btn_state, 0); + input_report_key(hi->input, KEY_MEDIA, 0); + input_sync(hi->input); +} + +/***************** + * H2W proctocol * + *****************/ +static inline void h2w_begin_command(void) +{ + /* Disable H2W interrupt */ + set_irq_type(hi->irq_btn, IRQF_TRIGGER_HIGH); + disable_irq(hi->irq); + disable_irq(hi->irq_btn); + + /* Set H2W_CLK as output low */ + hi->set_clk(0); + hi->set_clk_dir(1); +} + +static inline void h2w_end_command(void) +{ + /* Set H2W_CLK as input */ + hi->set_clk_dir(0); + + /* Enable H2W interrupt */ + enable_irq(hi->irq); + enable_irq(hi->irq_btn); + set_irq_type(hi->irq_btn, IRQF_TRIGGER_RISING); +} + +/* + * One bit write data + * ________ + * SCLK O ______| |______O(L) + * + * + * SDAT I + */ +static inline void one_clock_write(unsigned short flag) +{ + if (flag) + hi->set_dat(1); + else + hi->set_dat(0); + + udelay(hi->speed); + hi->set_clk(1); + udelay(hi->speed); + hi->set_clk(0); +} + +/* + * One bit write data R/W bit + * ________ + * SCLK ______| |______O(L) + * 1----> 1-----> + * 2-------> ______ + * SDAT I + * O(H/L) + */ +static inline void one_clock_write_RWbit(unsigned short flag) +{ + if (flag) + hi->set_dat(1); + else + hi->set_dat(0); + + udelay(hi->speed); + hi->set_clk(1); + udelay(hi->speed); + hi->set_clk(0); + hi->set_dat_dir(0); + udelay(hi->speed); +} + +/* + * H2W Reset + * ___________ + * SCLK O(L)______| |___O(L) + * 1----> + * 4-->1-->1-->1us--> + * ____ + * SDAT O(L)________ | |_______O(L) + * + * H2w reset command needs to be issued before every access + */ +static inline void h2w_reset(void) +{ + /* Set H2W_DAT as output low */ + hi->set_dat(0); + hi->set_dat_dir(1); + + udelay(hi->speed); + hi->set_clk(1); + udelay(4 * hi->speed); + hi->set_dat(1); + udelay(hi->speed); + hi->set_dat(0); + udelay(hi->speed); + hi->set_clk(0); + udelay(hi->speed); +} + +/* + * H2W Start + * ___________ + * SCLK O(L)______| |___O(L) + * 1----> + * 2----------->1--> + * + * SDAT O(L)______________________O(L) + */ +static inline void h2w_start(void) +{ + udelay(hi->speed); + hi->set_clk(1); + udelay(2 * hi->speed); + hi->set_clk(0); + udelay(hi->speed); +} + +/* + * H2W Ack + * __________ + * SCLK _____| |_______O(L) + * 1----> 1------> + * 2---------> + * ________________________ + * SDAT become Input mode here I + */ +static inline int h2w_ack(void) +{ + int retry_times = 0; + +ack_resend: + if (retry_times == MAX_ACK_RESEND_TIMES) + return -1; + + udelay(hi->speed); + hi->set_clk(1); + udelay(2 * hi->speed); + + if (!hi->get_dat()) { + retry_times++; + hi->set_clk(0); + udelay(hi->speed); + goto ack_resend; + } + + hi->set_clk(0); + udelay(hi->speed); + return 0; +} + +/* + * One bit read data + * ________ + * SCLK ______| |______O(L) + * 2----> 2-----> + * 2-------> + * SDAT I + */ +static unsigned char h2w_readc(void) +{ + unsigned char h2w_read_data = 0x0; + int index; + + for (index = 0; index < 8; index++) { + hi->set_clk(0); + udelay(hi->speed); + hi->set_clk(1); + udelay(hi->speed); + if (hi->get_dat()) + h2w_read_data |= (1 << (7 - index)); + } + hi->set_clk(0); + udelay(hi->speed); + + return h2w_read_data; +} + +static int h2w_readc_cmd(H2W_ADDR address) +{ + int ret = -1, retry_times = 0; + unsigned char read_data; + +read_resend: + if (retry_times == MAX_HOST_RESEND_TIMES) + goto err_read; + + h2w_reset(); + h2w_start(); + /* Write address */ + one_clock_write(address & 0x1000); + one_clock_write(address & 0x0800); + one_clock_write(address & 0x0400); + one_clock_write(address & 0x0200); + one_clock_write(address & 0x0100); + one_clock_write(address & 0x0080); + one_clock_write(address & 0x0040); + one_clock_write(address & 0x0020); + one_clock_write(address & 0x0010); + one_clock_write(address & 0x0008); + one_clock_write(address & 0x0004); + one_clock_write(address & 0x0002); + one_clock_write(address & 0x0001); + one_clock_write_RWbit(1); + if (h2w_ack() < 0) { + H2W_DBG("Addr NO ACK(%d).\n", retry_times); + retry_times++; + hi->set_clk(0); + mdelay(RESEND_DELAY); + goto read_resend; + } + + read_data = h2w_readc(); + + if (h2w_ack() < 0) { + H2W_DBG("Data NO ACK(%d).\n", retry_times); + retry_times++; + hi->set_clk(0); + mdelay(RESEND_DELAY); + goto read_resend; + } + ret = (int)read_data; + +err_read: + if (ret < 0) + H2WE("NO ACK.\n"); + + return ret; +} + +static int h2w_writec_cmd(H2W_ADDR address, unsigned char data) +{ + int ret = -1; + int retry_times = 0; + +write_resend: + if (retry_times == MAX_HOST_RESEND_TIMES) + goto err_write; + + h2w_reset(); + h2w_start(); + + /* Write address */ + one_clock_write(address & 0x1000); + one_clock_write(address & 0x0800); + one_clock_write(address & 0x0400); + one_clock_write(address & 0x0200); + one_clock_write(address & 0x0100); + one_clock_write(address & 0x0080); + one_clock_write(address & 0x0040); + one_clock_write(address & 0x0020); + one_clock_write(address & 0x0010); + one_clock_write(address & 0x0008); + one_clock_write(address & 0x0004); + one_clock_write(address & 0x0002); + one_clock_write(address & 0x0001); + one_clock_write_RWbit(0); + if (h2w_ack() < 0) { + H2W_DBG("Addr NO ACK(%d).\n", retry_times); + retry_times++; + hi->set_clk(0); + mdelay(RESEND_DELAY); + goto write_resend; + } + + /* Write data */ + hi->set_dat_dir(1); + one_clock_write(data & 0x0080); + one_clock_write(data & 0x0040); + one_clock_write(data & 0x0020); + one_clock_write(data & 0x0010); + one_clock_write(data & 0x0008); + one_clock_write(data & 0x0004); + one_clock_write(data & 0x0002); + one_clock_write_RWbit(data & 0x0001); + if (h2w_ack() < 0) { + H2W_DBG("Data NO ACK(%d).\n", retry_times); + retry_times++; + hi->set_clk(0); + mdelay(RESEND_DELAY); + goto write_resend; + } + ret = 0; + +err_write: + if (ret < 0) + H2WE("NO ACK.\n"); + + return ret; +} + +static int h2w_get_fnkey(void) +{ + int ret; + h2w_begin_command(); + ret = h2w_readc_cmd(H2W_FNKEY_UPDOWN); + h2w_end_command(); + return ret; +} + +static int h2w_dev_init(H2W_INFO *ph2w_info) +{ + int ret = -1; + unsigned char ascr0 = 0; + int h2w_sys = 0, maxgpadd = 0, maxadd = 0, key = 0; + + hi->speed = H2W_50KHz; + h2w_begin_command(); + + /* read H2W_SYSTEM */ + h2w_sys = h2w_readc_cmd(H2W_SYSTEM); + if (h2w_sys == -1) { + H2WE("read H2W_SYSTEM(0x0000) failed.\n"); + goto err_plugin; + } + ph2w_info->ACC_CLASS = (h2w_sys & 0x03); + ph2w_info->AUDIO_DEVICE = (h2w_sys & 0x04) > 0 ? 1 : 0; + ph2w_info->HW_REV = (h2w_sys & 0x18) >> 3; + ph2w_info->SLEEP_PR = (h2w_sys & 0x20) >> 5; + ph2w_info->CLK_SP = (h2w_sys & 0xC0) >> 6; + + /* enter init mode */ + if (h2w_writec_cmd(H2W_ASCR0, H2W_ASCR_DEVICE_INI) < 0) { + H2WE("write H2W_ASCR0(0x0002) failed.\n"); + goto err_plugin; + } + udelay(10); + + /* read H2W_MAX_GP_ADD */ + maxgpadd = h2w_readc_cmd(H2W_MAX_GP_ADD); + if (maxgpadd == -1) { + H2WE("write H2W_MAX_GP_ADD(0x0001) failed.\n"); + goto err_plugin; + } + ph2w_info->CLK_SP += (maxgpadd & 0x60) >> 3; + ph2w_info->MAX_GP_ADD = (maxgpadd & 0x1F); + + /* read key group */ + if (ph2w_info->MAX_GP_ADD >= 1) { + ph2w_info->KEY_MAXADD = h2w_readc_cmd(H2W_KEY_MAXADD); + if (ph2w_info->KEY_MAXADD == -1) + goto err_plugin; + if (ph2w_info->KEY_MAXADD >= 1) { + key = h2w_readc_cmd(H2W_ASCII_DOWN); + if (key < 0) + goto err_plugin; + ph2w_info->ASCII_DOWN = (key == 0xFF) ? 1 : 0; + } + if (ph2w_info->KEY_MAXADD >= 2) { + key = h2w_readc_cmd(H2W_ASCII_UP); + if (key == -1) + goto err_plugin; + ph2w_info->ASCII_UP = (key == 0xFF) ? 1 : 0; + } + if (ph2w_info->KEY_MAXADD >= 3) { + key = h2w_readc_cmd(H2W_FNKEY_UPDOWN); + if (key == -1) + goto err_plugin; + ph2w_info->FNKEY_UPDOWN = (key == 0xFF) ? 1 : 0; + } + if (ph2w_info->KEY_MAXADD >= 4) { + key = h2w_readc_cmd(H2W_KD_STATUS); + if (key == -1) + goto err_plugin; + ph2w_info->KD_STATUS = (key == 0x01) ? 1 : 0; + } + } + + /* read led group */ + if (ph2w_info->MAX_GP_ADD >= 2) { + ph2w_info->LED_MAXADD = h2w_readc_cmd(H2W_LED_MAXADD); + if (ph2w_info->LED_MAXADD == -1) + goto err_plugin; + if (ph2w_info->LED_MAXADD >= 1) { + key = h2w_readc_cmd(H2W_LEDCT0); + if (key == -1) + goto err_plugin; + ph2w_info->LEDCT0 = (key == 0x02) ? 1 : 0; + } + } + + /* read group 3, 4, 5 */ + if (ph2w_info->MAX_GP_ADD >= 3) { + maxadd = h2w_readc_cmd(H2W_CRDL_MAXADD); + if (maxadd == -1) + goto err_plugin; + } + if (ph2w_info->MAX_GP_ADD >= 4) { + maxadd = h2w_readc_cmd(H2W_CARKIT_MAXADD); + if (maxadd == -1) + goto err_plugin; + } + if (ph2w_info->MAX_GP_ADD >= 5) { + maxadd = h2w_readc_cmd(H2W_USBHOST_MAXADD); + if (maxadd == -1) + goto err_plugin; + } + + /* read medical group */ + if (ph2w_info->MAX_GP_ADD >= 6) { + ph2w_info->MED_MAXADD = h2w_readc_cmd(H2W_MED_MAXADD); + if (ph2w_info->MED_MAXADD == -1) + goto err_plugin; + if (ph2w_info->MED_MAXADD >= 1) { + key = h2w_readc_cmd(H2W_MED_CONTROL); + if (key == -1) + goto err_plugin; + ph2w_info->DATA_EN = (key & 0x01); + ph2w_info->AP_EN = (key & 0x02) >> 1; + ph2w_info->AP_ID = (key & 0x1c) >> 2; + } + if (ph2w_info->MED_MAXADD >= 2) { + key = h2w_readc_cmd(H2W_MED_IN_DATA); + if (key == -1) + goto err_plugin; + } + } + + if (ph2w_info->AUDIO_DEVICE) + ascr0 = H2W_ASCR_AUDIO_IN | H2W_ASCR_ACT_EN; + else + ascr0 = H2W_ASCR_ACT_EN; + + if (h2w_writec_cmd(H2W_ASCR0, ascr0) < 0) + goto err_plugin; + udelay(10); + + ret = 0; + + /* adjust speed */ + if (ph2w_info->MAX_GP_ADD == 2) { + /* Remote control */ + hi->speed = H2W_250KHz; + } else if (ph2w_info->MAX_GP_ADD == 6) { + if (ph2w_info->MED_MAXADD >= 1) { + key = h2w_readc_cmd(H2W_MED_CONTROL); + if (key == -1) + goto err_plugin; + ph2w_info->DATA_EN = (key & 0x01); + ph2w_info->AP_EN = (key & 0x02) >> 1; + ph2w_info->AP_ID = (key & 0x1c) >> 2; + } + } + +err_plugin: + h2w_end_command(); + + return ret; +} + +static inline void h2w_dev_power_on(int on) +{ + if (!hi->vreg_h2w) + return; + + if (on) + vreg_enable(hi->vreg_h2w); + else + vreg_disable(hi->vreg_h2w); +} + +static int h2w_dev_detect(void) +{ + int ret = -1; + int retry_times; + + for (retry_times = 5; retry_times; retry_times--) { + /* Enable H2W Power */ + h2w_dev_power_on(1); + msleep(100); + memset(&hi->h2w_info, 0, sizeof(H2W_INFO)); + if (h2w_dev_init(&hi->h2w_info) < 0) { + h2w_dev_power_on(0); + msleep(100); + } else if (hi->h2w_info.MAX_GP_ADD == 2) { + ret = 0; + break; + } else { + printk(KERN_INFO "h2w_detect: detect error(%d)\n" + , hi->h2w_info.MAX_GP_ADD); + h2w_dev_power_on(0); + msleep(100); + } + printk(KERN_INFO "h2w_detect(%d)\n" + , hi->h2w_info.MAX_GP_ADD); + } + H2W_DBG("h2w_detect:(%d)\n", retry_times); + return ret; +} + +static void remove_headset(void) +{ + unsigned long irq_flags; + + H2W_DBG(""); + + mutex_lock(&hi->mutex_lock); + switch_set_state(&hi->sdev, switch_get_state(&hi->sdev) & + ~(BIT_HEADSET | BIT_HEADSET_NO_MIC)); + mutex_unlock(&hi->mutex_lock); + hi->init_cpld(); + + /* Disable button */ + switch (hi->htc_headset_flag) { + case H2W_HTC_HEADSET: + local_irq_save(irq_flags); + disable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + + if (atomic_read(&hi->btn_state)) + button_released(); + break; + case H2W_DEVICE: + h2w_dev_power_on(0); + set_irq_type(hi->irq_btn, IRQF_TRIGGER_LOW); + disable_irq(hi->irq_btn); + /* 10ms (5-15 with 10ms tick) */ + hi->btn_debounce_time = ktime_set(0, 10000000); + hi->set_clk_dir(0); + hi->set_dat_dir(0); + break; + } + + hi->htc_headset_flag = 0; + hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ + +} + +#ifdef CONFIG_MSM_SERIAL_DEBUGGER +extern void msm_serial_debug_enable(int); +#endif + +static void insert_headset(int type) +{ + unsigned long irq_flags; + int state; + + H2W_DBG(""); + + hi->htc_headset_flag = type; + state = BIT_HEADSET | BIT_HEADSET_NO_MIC; + + state = switch_get_state(&hi->sdev); + state &= ~(BIT_HEADSET_NO_MIC | BIT_HEADSET); + switch (type) { + case H2W_HTC_HEADSET: + printk(KERN_INFO "insert_headset H2W_HTC_HEADSET\n"); + state |= BIT_HEADSET; + hi->ignore_btn = !gpio_get_value(hi->cable_in2); + /* Enable button irq */ + local_irq_save(irq_flags); + enable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + hi->debounce_time = ktime_set(0, 200000000); /* 20 ms */ + break; + case H2W_DEVICE: + if (h2w_dev_detect() < 0) { + printk(KERN_INFO "H2W_DEVICE -- Non detect\n"); + remove_headset(); + } else { + printk(KERN_INFO "H2W_DEVICE -- detect\n"); + hi->btn_debounce_time = ktime_set(0, 0); + local_irq_save(irq_flags); + enable_irq(hi->irq_btn); + set_irq_type(hi->irq_btn, IRQF_TRIGGER_RISING); + local_irq_restore(irq_flags); + state |= BIT_HEADSET; + } + break; + case H2W_USB_CRADLE: + state |= BIT_HEADSET_NO_MIC; + break; + case H2W_UART_DEBUG: + hi->config_cpld(hi->debug_uart); + printk(KERN_INFO "switch to H2W_UART_DEBUG\n"); + default: + return; + } + mutex_lock(&hi->mutex_lock); + switch_set_state(&hi->sdev, state); + mutex_unlock(&hi->mutex_lock); + +#ifdef CONFIG_MSM_SERIAL_DEBUGGER + msm_serial_debug_enable(false); +#endif + +} +#if 0 +static void remove_headset(void) +{ + unsigned long irq_flags; + + H2W_DBG(""); + + switch_set_state(&hi->sdev, H2W_NO_DEVICE); + + hi->init_cpld(); + + /* Disable button */ + local_irq_save(irq_flags); + disable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + + if (atomic_read(&hi->btn_state)) + button_released(); + + hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ +} +#endif +static int is_accessary_pluged_in(void) +{ + int type = 0; + int clk1 = 0, dat1 = 0, clk2 = 0, dat2 = 0, clk3 = 0, dat3 = 0; + + /* Step1: save H2W_CLK and H2W_DAT */ + /* Delay 10ms for pin stable. */ + msleep(10); + clk1 = gpio_get_value(hi->h2w_clk); + dat1 = gpio_get_value(hi->h2w_data); + + /* + * Step2: set GPIO_CABLE_IN1 as output high and GPIO_CABLE_IN2 as + * input + */ + gpio_direction_output(hi->cable_in1, 1); + gpio_direction_input(hi->cable_in2); + /* Delay 10ms for pin stable. */ + msleep(10); + /* Step 3: save H2W_CLK and H2W_DAT */ + clk2 = gpio_get_value(hi->h2w_clk); + dat2 = gpio_get_value(hi->h2w_data); + + /* + * Step 4: set GPIO_CABLE_IN1 as input and GPIO_CABLE_IN2 as output + * high + */ + gpio_direction_input(hi->cable_in1); + gpio_direction_output(hi->cable_in2, 1); + /* Delay 10ms for pin stable. */ + msleep(10); + /* Step 5: save H2W_CLK and H2W_DAT */ + clk3 = gpio_get_value(hi->h2w_clk); + dat3 = gpio_get_value(hi->h2w_data); + + /* Step 6: set both GPIO_CABLE_IN1 and GPIO_CABLE_IN2 as input */ + gpio_direction_input(hi->cable_in1); + gpio_direction_input(hi->cable_in2); + + H2W_DBG("(%d,%d) (%d,%d) (%d,%d)\n", + clk1, dat1, clk2, dat2, clk3, dat3); + + if ((clk1 == 0) && (dat1 == 1) && + (clk2 == 0) && (dat2 == 1) && + (clk3 == 0) && (dat3 == 1)) + type = H2W_HTC_HEADSET; + else if ((clk1 == 0) && (dat1 == 0) && + (clk2 == 0) && (dat2 == 0) && + (clk3 == 0) && (dat3 == 0)) + type = NORMAL_HEARPHONE; + else if ((clk1 == 0) && (dat1 == 0) && + (clk2 == 1) && (dat2 == 0) && + (clk3 == 0) && (dat3 == 1)) + type = H2W_DEVICE; + else if ((clk1 == 0) && (dat1 == 0) && + (clk2 == 1) && (dat2 == 1) && + (clk3 == 1) && (dat3 == 1)) + type = H2W_USB_CRADLE; + else if ((clk1 == 0) && (dat1 == 1) && + (clk2 == 1) && (dat2 == 1) && + (clk3 == 0) && (dat3 == 1)) + type = H2W_UART_DEBUG; + else + type = H2W_NO_DEVICE; + + return type; +} + + +static void detection_work(struct work_struct *work) +{ + unsigned long irq_flags; + int type; + + H2W_DBG(""); + + if (gpio_get_value(hi->cable_in1) != 0) { + /* Headset not plugged in */ + if (switch_get_state(&hi->sdev) != H2W_NO_DEVICE) + remove_headset(); + return; + } + + /* Something plugged in, lets make sure its a headset */ + + /* Switch CPLD to GPIO to do detection */ + hi->config_cpld(H2W_GPIO); + + /* Disable headset interrupt while detecting.*/ + local_irq_save(irq_flags); + disable_irq(hi->irq); + local_irq_restore(irq_flags); + + /* Something plugged in, lets make sure its a headset */ + type = is_accessary_pluged_in(); + + /* Restore IRQs */ + local_irq_save(irq_flags); + enable_irq(hi->irq); + local_irq_restore(irq_flags); + + insert_headset(type); +} + +static enum hrtimer_restart button_event_timer_func(struct hrtimer *data) +{ + int key, press, keyname, h2w_key = 1; + + H2W_DBG(""); + + if (switch_get_state(&hi->sdev) == H2W_HTC_HEADSET) { + switch (hi->htc_headset_flag) { + case H2W_HTC_HEADSET: + if (gpio_get_value(hi->cable_in2)) { + if (hi->ignore_btn) + hi->ignore_btn = 0; + else if (atomic_read(&hi->btn_state)) + button_released(); + } else { + if (!hi->ignore_btn && + !atomic_read(&hi->btn_state)) + button_pressed(); + } + break; + case H2W_DEVICE: + if ((hi->get_dat() == 1) && (hi->get_clk() == 1)) { + /* Don't do anything because H2W pull out. */ + H2WE("Remote Control pull out.\n"); + } else { + key = h2w_get_fnkey(); + press = (key > 0x7F) ? 0 : 1; + keyname = key & 0x7F; + /* H2WI("key = %d, press = %d, + keyname = %d \n", + key, press, keyname); */ + switch (keyname) { + case H2W_KEY_PLAY: + H2WI("H2W_KEY_PLAY"); + key = KEY_PLAYPAUSE; + break; + case H2W_KEY_FORWARD: + H2WI("H2W_KEY_FORWARD"); + key = KEY_NEXTSONG; + break; + case H2W_KEY_BACKWARD: + H2WI("H2W_KEY_BACKWARD"); + key = KEY_PREVIOUSSONG; + break; + case H2W_KEY_VOLUP: + H2WI("H2W_KEY_VOLUP"); + key = KEY_VOLUMEUP; + break; + case H2W_KEY_VOLDOWN: + H2WI("H2W_KEY_VOLDOWN"); + key = KEY_VOLUMEDOWN; + break; + case H2W_KEY_PICKUP: + H2WI("H2W_KEY_PICKUP"); + key = KEY_SEND; + break; + case H2W_KEY_HANGUP: + H2WI("H2W_KEY_HANGUP"); + key = KEY_END; + break; + case H2W_KEY_MUTE: + H2WI("H2W_KEY_MUTE"); + key = KEY_MUTE; + break; + case H2W_KEY_HOLD: + H2WI("H2W_KEY_HOLD"); + break; + default: + H2WI("default"); + h2w_key = 0; + } + if (h2w_key) { + if (press) + H2WI("Press\n"); + else + H2WI("Release\n"); + input_report_key(hi->input, key, press); + } + } + break; + } /* end switch */ + } + + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart detect_event_timer_func(struct hrtimer *data) +{ + H2W_DBG(""); + + queue_work(g_detection_work_queue, &g_detection_work); + return HRTIMER_NORESTART; +} + +static irqreturn_t detect_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + H2W_DBG(""); + set_irq_type(hi->irq_btn, IRQF_TRIGGER_LOW); + do { + value1 = gpio_get_value(hi->cable_in1); + set_irq_type(hi->irq, value1 ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(hi->cable_in1); + } while (value1 != value2 && retry_limit-- > 0); + + H2W_DBG("value2 = %d (%d retries), device=%d", + value2, (10-retry_limit), switch_get_state(&hi->sdev)); + + if ((switch_get_state(&hi->sdev) == H2W_NO_DEVICE) ^ value2) { + if (switch_get_state(&hi->sdev) == H2W_HTC_HEADSET) + hi->ignore_btn = 1; + /* Do the rest of the work in timer context */ + hrtimer_start(&hi->timer, hi->debounce_time, HRTIMER_MODE_REL); + } + + return IRQ_HANDLED; +} + +static irqreturn_t button_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + H2W_DBG(""); + do { + value1 = gpio_get_value(hi->cable_in2); + if (hi->htc_headset_flag != H2W_DEVICE) + set_irq_type(hi->irq_btn, value1 ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(hi->cable_in2); + } while (value1 != value2 && retry_limit-- > 0); + + H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit)); + + hrtimer_start(&hi->btn_timer, hi->btn_debounce_time, HRTIMER_MODE_REL); + + return IRQ_HANDLED; +} + +#if defined(CONFIG_DEBUG_FS) +static int h2w_debug_set(void *data, u64 val) +{ + mutex_lock(&hi->mutex_lock); + switch_set_state(&hi->sdev, (int)val); + mutex_unlock(&hi->mutex_lock); + return 0; +} + +static int h2w_debug_get(void *data, u64 *val) +{ + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(h2w_debug_fops, h2w_debug_get, h2w_debug_set, "%llu\n"); +static int __init h2w_debug_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("h2w", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("state", 0644, dent, NULL, &h2w_debug_fops); + + return 0; +} + +device_initcall(h2w_debug_init); +#endif + +static int h2w_probe(struct platform_device *pdev) +{ + int ret; + struct h2w_platform_data *pdata = pdev->dev.platform_data; + + printk(KERN_INFO "H2W: Registering H2W (headset) driver\n"); + hi = kzalloc(sizeof(struct h2w_info), GFP_KERNEL); + if (!hi) + return -ENOMEM; + + atomic_set(&hi->btn_state, 0); + hi->ignore_btn = 0; + + hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ + hi->btn_debounce_time = ktime_set(0, 10000000); /* 10 ms */ + + hi->htc_headset_flag = 0; + hi->cable_in1 = pdata->cable_in1; + hi->cable_in2 = pdata->cable_in2; + hi->h2w_clk = pdata->h2w_clk; + hi->h2w_data = pdata->h2w_data; + hi->debug_uart = pdata->debug_uart; + hi->config_cpld = pdata->config_cpld; + hi->init_cpld = pdata->init_cpld; + hi->set_dat = pdata->set_dat; + hi->set_clk = pdata->set_clk; + hi->set_dat_dir = pdata->set_dat_dir; + hi->set_clk_dir = pdata->set_clk_dir; + hi->get_dat = pdata->get_dat; + hi->get_clk = pdata->get_clk; + hi->speed = H2W_50KHz; + /* obtain needed VREGs */ + if (pdata->power_name) + hi->vreg_h2w = vreg_get(0, pdata->power_name); + + mutex_init(&hi->mutex_lock); + + hi->sdev.name = "h2w"; + hi->sdev.print_name = h2w_print_name; + + ret = switch_dev_register(&hi->sdev); + if (ret < 0) + goto err_switch_dev_register; + + g_detection_work_queue = create_workqueue("detection"); + if (g_detection_work_queue == NULL) { + ret = -ENOMEM; + goto err_create_work_queue; + } + + ret = gpio_request(hi->cable_in1, "h2w_detect"); + if (ret < 0) + goto err_request_detect_gpio; + + ret = gpio_request(hi->cable_in2, "h2w_button"); + if (ret < 0) + goto err_request_button_gpio; + + ret = gpio_direction_input(hi->cable_in1); + if (ret < 0) + goto err_set_detect_gpio; + + ret = gpio_direction_input(hi->cable_in2); + if (ret < 0) + goto err_set_button_gpio; + + hi->irq = gpio_to_irq(hi->cable_in1); + if (hi->irq < 0) { + ret = hi->irq; + goto err_get_h2w_detect_irq_num_failed; + } + + hi->irq_btn = gpio_to_irq(hi->cable_in2); + if (hi->irq_btn < 0) { + ret = hi->irq_btn; + goto err_get_button_irq_num_failed; + } + + /* Set CPLD MUX to H2W <-> CPLD GPIO */ + hi->init_cpld(); + + hrtimer_init(&hi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hi->timer.function = detect_event_timer_func; + hrtimer_init(&hi->btn_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hi->btn_timer.function = button_event_timer_func; + + ret = request_irq(hi->irq, detect_irq_handler, + IRQF_TRIGGER_LOW, "h2w_detect", NULL); + if (ret < 0) + goto err_request_detect_irq; + + /* Disable button until plugged in */ + set_irq_flags(hi->irq_btn, IRQF_VALID | IRQF_NOAUTOEN); + ret = request_irq(hi->irq_btn, button_irq_handler, + IRQF_TRIGGER_LOW, "h2w_button", NULL); + if (ret < 0) + goto err_request_h2w_headset_button_irq; + + ret = set_irq_wake(hi->irq, 1); + if (ret < 0) + goto err_request_input_dev; + + ret = set_irq_wake(hi->irq_btn, 1); + if (ret < 0) + goto err_request_input_dev; + + + + hi->input = input_allocate_device(); + if (!hi->input) { + ret = -ENOMEM; + goto err_request_input_dev; + } + + hi->input->name = "h2w headset"; + set_bit(EV_SYN, hi->input->evbit); + set_bit(EV_KEY, hi->input->evbit); + set_bit(KEY_MEDIA, hi->input->keybit); + set_bit(KEY_NEXTSONG, hi->input->keybit); + set_bit(KEY_PLAYPAUSE, hi->input->keybit); + set_bit(KEY_PREVIOUSSONG, hi->input->keybit); + set_bit(KEY_MUTE, hi->input->keybit); + set_bit(KEY_VOLUMEUP, hi->input->keybit); + set_bit(KEY_VOLUMEDOWN, hi->input->keybit); + set_bit(KEY_END, hi->input->keybit); + set_bit(KEY_SEND, hi->input->keybit); + + ret = input_register_device(hi->input); + if (ret < 0) + goto err_register_input_dev; + + return 0; + +err_register_input_dev: + input_free_device(hi->input); +err_request_input_dev: + free_irq(hi->irq_btn, 0); +err_request_h2w_headset_button_irq: + free_irq(hi->irq, 0); +err_request_detect_irq: +err_get_button_irq_num_failed: +err_get_h2w_detect_irq_num_failed: +err_set_button_gpio: +err_set_detect_gpio: + gpio_free(hi->cable_in2); +err_request_button_gpio: + gpio_free(hi->cable_in1); +err_request_detect_gpio: + destroy_workqueue(g_detection_work_queue); +err_create_work_queue: + switch_dev_unregister(&hi->sdev); +err_switch_dev_register: + printk(KERN_ERR "H2W: Failed to register driver\n"); + + return ret; +} + +static int h2w_remove(struct platform_device *pdev) +{ + H2W_DBG(""); + if (switch_get_state(&hi->sdev)) + remove_headset(); + input_unregister_device(hi->input); + gpio_free(hi->cable_in2); + gpio_free(hi->cable_in1); + free_irq(hi->irq_btn, 0); + free_irq(hi->irq, 0); + destroy_workqueue(g_detection_work_queue); + switch_dev_unregister(&hi->sdev); + + return 0; +} + + +static struct platform_driver h2w_driver = { + .probe = h2w_probe, + .remove = h2w_remove, + .driver = { + .name = "h2w", + .owner = THIS_MODULE, + }, +}; + +static int __init h2w_init(void) +{ + H2W_DBG(""); + return platform_driver_register(&h2w_driver); +} + +static void __exit h2w_exit(void) +{ + platform_driver_unregister(&h2w_driver); +} + +module_init(h2w_init); +module_exit(h2w_exit); + +MODULE_AUTHOR("Laurence Chen "); +MODULE_DESCRIPTION("HTC 2 Wire detection driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/include/mach/htc_headset.h b/arch/arm/mach-msm/include/mach/htc_headset.h new file mode 100644 index 0000000000000..2f4c18db26253 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/htc_headset.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2008 HTC, Inc. + * Copyright (C) 2008 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 __ASM_ARCH_HTC_HEADSET_H +#define __ASM_ARCH_HTC_HEADSET_H + +struct h2w_platform_data { + char *power_name; + int cable_in1; + int cable_in2; + int h2w_clk; + int h2w_data; + int debug_uart; + void (*config_cpld)(int); + void (*init_cpld)(void); + void (*set_dat)(int); + void (*set_clk)(int); + void (*set_dat_dir)(int); + void (*set_clk_dir)(int); + int (*get_dat)(void); + int (*get_clk)(void); +}; + +#define BIT_HEADSET (1 << 0) +#define BIT_HEADSET_NO_MIC (1 << 1) +#define BIT_TTY (1 << 2) +#define BIT_FM_HEADSET (1 << 3) +#define BIT_FM_SPEAKER (1 << 4) + +enum { + H2W_NO_DEVICE = 0, + H2W_HTC_HEADSET = 1, +/* H2W_TTY_DEVICE = 2,*/ + NORMAL_HEARPHONE= 2, + H2W_DEVICE = 3, + H2W_USB_CRADLE = 4, + H2W_UART_DEBUG = 5, +}; + +enum { + H2W_GPIO = 0, + H2W_UART1 = 1, + H2W_UART3 = 2, + H2W_BT = 3 +}; + +#define RESEND_DELAY (3) /* ms */ +#define MAX_ACK_RESEND_TIMES (6) /* follow spec */ +#define MAX_HOST_RESEND_TIMES (3) /* follow spec */ +#define MAX_HYGEIA_RESEND_TIMES (5) + +#define H2W_ASCR_DEVICE_INI (0x01) +#define H2W_ASCR_ACT_EN (0x02) +#define H2W_ASCR_PHONE_IN (0x04) +#define H2W_ASCR_RESET (0x08) +#define H2W_ASCR_AUDIO_IN (0x10) + +#define H2W_LED_OFF (0x0) +#define H2W_LED_BKL (0x1) +#define H2W_LED_MTL (0x2) + +typedef enum { + /* === system group 0x0000~0x00FF === */ + /* (R) Accessory type register */ + H2W_SYSTEM = 0x0000, + /* (R) Maximum group address */ + H2W_MAX_GP_ADD = 0x0001, + /* (R/W) Accessory system control register0 */ + H2W_ASCR0 = 0x0002, + + /* === key group 0x0100~0x01FF === */ + /* (R) Key group maximum sub address */ + H2W_KEY_MAXADD = 0x0100, + /* (R) ASCII key press down flag */ + H2W_ASCII_DOWN = 0x0101, + /* (R) ASCII key release up flag */ + H2W_ASCII_UP = 0x0102, + /* (R) Function key status flag */ + H2W_FNKEY_UPDOWN = 0x0103, + /* (R/W) Key device status */ + H2W_KD_STATUS = 0x0104, + + /* === led group 0x0200~0x02FF === */ + /* (R) LED group maximum sub address */ + H2W_LED_MAXADD = 0x0200, + /* (R/W) LED control register0 */ + H2W_LEDCT0 = 0x0201, + + /* === crdl group 0x0300~0x03FF === */ + /* (R) Cardle group maximum sub address */ + H2W_CRDL_MAXADD = 0x0300, + /* (R/W) Cardle group function control register0 */ + H2W_CRDLCT0 = 0x0301, + + /* === car kit group 0x0400~0x04FF === */ + H2W_CARKIT_MAXADD = 0x0400, + + /* === usb host group 0x0500~0x05FF === */ + H2W_USBHOST_MAXADD = 0x0500, + + /* === medical group 0x0600~0x06FF === */ + H2W_MED_MAXADD = 0x0600, + H2W_MED_CONTROL = 0x0601, + H2W_MED_IN_DATA = 0x0602, +} H2W_ADDR; + + +typedef struct H2W_INFO { + /* system group */ + unsigned char CLK_SP; + int SLEEP_PR; + unsigned char HW_REV; + int AUDIO_DEVICE; + unsigned char ACC_CLASS; + unsigned char MAX_GP_ADD; + + /* key group */ + int KEY_MAXADD; + int ASCII_DOWN; + int ASCII_UP; + int FNKEY_UPDOWN; + int KD_STATUS; + + /* led group */ + int LED_MAXADD; + int LEDCT0; + + /* medical group */ + int MED_MAXADD; + unsigned char AP_ID; + unsigned char AP_EN; + unsigned char DATA_EN; +} H2W_INFO; + +typedef enum { + H2W_500KHz = 1, + H2W_250KHz = 2, + H2W_166KHz = 3, + H2W_125KHz = 4, + H2W_100KHz = 5, + H2W_83KHz = 6, + H2W_71KHz = 7, + H2W_62KHz = 8, + H2W_55KHz = 9, + H2W_50KHz = 10, +} H2W_SPEED; + +typedef enum { + H2W_KEY_INVALID = -1, + H2W_KEY_PLAY = 0, + H2W_KEY_FORWARD = 1, + H2W_KEY_BACKWARD = 2, + H2W_KEY_VOLUP = 3, + H2W_KEY_VOLDOWN = 4, + H2W_KEY_PICKUP = 5, + H2W_KEY_HANGUP = 6, + H2W_KEY_MUTE = 7, + H2W_KEY_HOLD = 8, + H2W_NUM_KEYFUNC = 9, +} KEYFUNC; +#endif From 349d3997b49cc6a36e5d007750743bd7f7340442 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Tue, 17 Feb 2009 14:03:22 -0800 Subject: [PATCH 0268/2556] [ARM] msm: trout: Switch to new headset driver in trout Signed-off-by: San Mehat --- arch/arm/mach-msm/Kconfig | 2 +- arch/arm/mach-msm/Makefile | 3 +- arch/arm/mach-msm/board-trout-h2w.c | 542 ---------------------------- arch/arm/mach-msm/board-trout.c | 47 +++ 4 files changed, 50 insertions(+), 544 deletions(-) delete mode 100644 arch/arm/mach-msm/board-trout-h2w.c diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index d56fbd0693244..3c73de9f0471e 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -184,7 +184,7 @@ config MSM_SMD_PKG3 config MSM_PROC_COMM bool -config TROUT_H2W +config HTC_HEADSET tristate "HTC 2 Wire detection driver" default n help diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 8fd5877389af2..3a013e3566897 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -44,7 +44,6 @@ obj-$(CONFIG_MACH_TROUT) += board-trout-keypad.o obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o obj-$(CONFIG_MACH_TROUT) += board-trout-rfkill.o obj-$(CONFIG_MACH_TROUT) += board-trout-wifi.o -obj-$(CONFIG_TROUT_H2W) += board-trout-h2w.o obj-$(CONFIG_TROUT_BATTCHG) += htc_battery.o obj-$(CONFIG_TROUT_PWRSINK) += htc_pwrsink.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o @@ -61,3 +60,5 @@ obj-y += gpio-v2.o else obj-y += gpio.o endif + +obj-$(CONFIG_HTC_HEADSET) += htc_headset.o diff --git a/arch/arm/mach-msm/board-trout-h2w.c b/arch/arm/mach-msm/board-trout-h2w.c deleted file mode 100644 index 66b82707a3b31..0000000000000 --- a/arch/arm/mach-msm/board-trout-h2w.c +++ /dev/null @@ -1,542 +0,0 @@ -/* - * H2W device detection driver. - * - * Copyright (C) 2008 HTC Corporation. - * Copyright (C) 2008 Google, Inc. - * - * Authors: - * Laurence Chen - * Nick Pelly - * - * 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. - * - */ - -/* For detecting HTC 2 Wire devices, such as wired headset. - - Logically, the H2W driver is always present, and H2W state (hi->state) - indicates what is currently plugged into the H2W interface. - - When the headset is plugged in, CABLE_IN1 is pulled low. When the headset - button is pressed, CABLE_IN2 is pulled low. These two lines are shared with - the TX and RX (respectively) of UART3 - used for serial debugging. - - This headset driver keeps the CPLD configured as UART3 for as long as - possible, so that we can do serial FIQ debugging even when the kernel is - locked and this driver no longer runs. So it only configures the CPLD to - GPIO while the headset is plugged in, and for 10ms during detection work. - - Unfortunately we can't leave the CPLD as UART3 while a headset is plugged - in, UART3 is pullup on TX but the headset is pull-down, causing a 55 mA - drain on trout. - - The headset detection work involves setting CPLD to GPIO, and then pulling - CABLE_IN1 high with a stronger pullup than usual. A H2W headset will still - pull this line low, whereas other attachments such as a serial console - would get pulled up by this stronger pullup. - - Headset insertion/removal causes UEvent's to be sent, and - /sys/class/switch/h2w/state to be updated. - - Button presses are interpreted as input event (KEY_MEDIA). Button presses - are ignored if the headset is plugged in, so the buttons on 11 pin -> 3.5mm - jack adapters do not work until a headset is plugged into the adapter. This - is to avoid serial RX traffic causing spurious button press events. - - We tend to check the status of CABLE_IN1 a few more times than strictly - necessary during headset detection, to avoid spurious headset insertion - events caused by serial debugger TX traffic. -*/ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "board-trout.h" - -#ifdef CONFIG_DEBUG_TROUT_H2W -#define H2W_DBG(fmt, arg...) printk(KERN_INFO "[H2W] %s " fmt "\n", __FUNCTION__, ## arg) -#else -#define H2W_DBG(fmt, arg...) do {} while (0) -#endif - -static struct workqueue_struct *g_detection_work_queue; -static void detection_work(struct work_struct *work); -static DECLARE_WORK(g_detection_work, detection_work); -enum { - NO_DEVICE = 0, - HTC_HEADSET = 1, -}; - -enum { - UART3 = 0, - GPIO = 1, -}; - -struct h2w_info { - struct switch_dev sdev; - struct input_dev *input; - - atomic_t btn_state; - int ignore_btn; - - unsigned int irq; - unsigned int irq_btn; - - struct hrtimer timer; - ktime_t debounce_time; - - struct hrtimer btn_timer; - ktime_t btn_debounce_time; -}; -static struct h2w_info *hi; - -static ssize_t trout_h2w_print_name(struct switch_dev *sdev, char *buf) -{ - switch (switch_get_state(&hi->sdev)) { - case NO_DEVICE: - return sprintf(buf, "No Device\n"); - case HTC_HEADSET: - return sprintf(buf, "Headset\n"); - } - return -EINVAL; -} - -static void configure_cpld(int route) -{ - H2W_DBG(" route = %s", route == UART3 ? "UART3" : "GPIO"); - switch (route) { - case UART3: - gpio_set_value(TROUT_GPIO_H2W_SEL0, 0); - gpio_set_value(TROUT_GPIO_H2W_SEL1, 1); - break; - case GPIO: - gpio_set_value(TROUT_GPIO_H2W_SEL0, 0); - gpio_set_value(TROUT_GPIO_H2W_SEL1, 0); - break; - } -} - -static void button_pressed(void) -{ - H2W_DBG(""); - atomic_set(&hi->btn_state, 1); - input_report_key(hi->input, KEY_MEDIA, 1); - input_sync(hi->input); -} - -static void button_released(void) -{ - H2W_DBG(""); - atomic_set(&hi->btn_state, 0); - input_report_key(hi->input, KEY_MEDIA, 0); - input_sync(hi->input); -} - -#ifdef CONFIG_MSM_SERIAL_DEBUGGER -extern void msm_serial_debug_enable(int); -#endif - -static void insert_headset(void) -{ - unsigned long irq_flags; - - H2W_DBG(""); - - switch_set_state(&hi->sdev, HTC_HEADSET); - configure_cpld(GPIO); - -#ifdef CONFIG_MSM_SERIAL_DEBUGGER - msm_serial_debug_enable(false); -#endif - - - /* On some non-standard headset adapters (usually those without a - * button) the btn line is pulled down at the same time as the detect - * line. We can check here by sampling the button line, if it is - * low then it is probably a bad adapter so ignore the button. - * If the button is released then we stop ignoring the button, so that - * the user can recover from the situation where a headset is plugged - * in with button held down. - */ - hi->ignore_btn = !gpio_get_value(TROUT_GPIO_CABLE_IN2); - - /* Enable button irq */ - local_irq_save(irq_flags); - enable_irq(hi->irq_btn); - local_irq_restore(irq_flags); - - hi->debounce_time = ktime_set(0, 20000000); /* 20 ms */ -} - -static void remove_headset(void) -{ - unsigned long irq_flags; - - H2W_DBG(""); - - switch_set_state(&hi->sdev, NO_DEVICE); - configure_cpld(UART3); - - /* Disable button */ - local_irq_save(irq_flags); - disable_irq(hi->irq_btn); - local_irq_restore(irq_flags); - - if (atomic_read(&hi->btn_state)) - button_released(); - - hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ -} - -static void detection_work(struct work_struct *work) -{ - unsigned long irq_flags; - int clk, cable_in1; - - H2W_DBG(""); - - if (gpio_get_value(TROUT_GPIO_CABLE_IN1) != 0) { - /* Headset not plugged in */ - if (switch_get_state(&hi->sdev) == HTC_HEADSET) - remove_headset(); - return; - } - - /* Something plugged in, lets make sure its a headset */ - - /* Switch CPLD to GPIO to do detection */ - configure_cpld(GPIO); - /* Disable headset interrupt while detecting.*/ - local_irq_save(irq_flags); - disable_irq(hi->irq); - local_irq_restore(irq_flags); - - /* Set GPIO_CABLE_IN1 as output high */ - gpio_direction_output(TROUT_GPIO_CABLE_IN1, 1); - /* Delay 10ms for pin stable. */ - msleep(10); - /* Save H2W_CLK */ - clk = gpio_get_value(TROUT_GPIO_H2W_CLK_GPI); - /* Set GPIO_CABLE_IN1 as input */ - gpio_direction_input(TROUT_GPIO_CABLE_IN1); - - /* Restore IRQs */ - local_irq_save(irq_flags); - enable_irq(hi->irq); - local_irq_restore(irq_flags); - - cable_in1 = gpio_get_value(TROUT_GPIO_CABLE_IN1); - - if (cable_in1 == 0 && clk == 0) { - if (switch_get_state(&hi->sdev) == NO_DEVICE) - insert_headset(); - } else { - configure_cpld(UART3); - H2W_DBG("CABLE_IN1 was low, but not a headset " - "(recent cable_in1 = %d, clk = %d)", cable_in1, clk); - } -} - -static enum hrtimer_restart button_event_timer_func(struct hrtimer *data) -{ - H2W_DBG(""); - - if (switch_get_state(&hi->sdev) == HTC_HEADSET) { - if (gpio_get_value(TROUT_GPIO_CABLE_IN2)) { - if (hi->ignore_btn) - hi->ignore_btn = 0; - else if (atomic_read(&hi->btn_state)) - button_released(); - } else { - if (!hi->ignore_btn && !atomic_read(&hi->btn_state)) - button_pressed(); - } - } - - return HRTIMER_NORESTART; -} - -static enum hrtimer_restart detect_event_timer_func(struct hrtimer *data) -{ - H2W_DBG(""); - - queue_work(g_detection_work_queue, &g_detection_work); - return HRTIMER_NORESTART; -} - -static irqreturn_t detect_irq_handler(int irq, void *dev_id) -{ - int value1, value2; - int retry_limit = 10; - - H2W_DBG(""); - do { - value1 = gpio_get_value(TROUT_GPIO_CABLE_IN1); - set_irq_type(hi->irq, value1 ? - IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); - value2 = gpio_get_value(TROUT_GPIO_CABLE_IN1); - } while (value1 != value2 && retry_limit-- > 0); - - H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit)); - - if ((switch_get_state(&hi->sdev) == NO_DEVICE) ^ value2) { - if (switch_get_state(&hi->sdev) == HTC_HEADSET) - hi->ignore_btn = 1; - /* Do the rest of the work in timer context */ - hrtimer_start(&hi->timer, hi->debounce_time, HRTIMER_MODE_REL); - } - - return IRQ_HANDLED; -} - -static irqreturn_t button_irq_handler(int irq, void *dev_id) -{ - int value1, value2; - int retry_limit = 10; - - H2W_DBG(""); - do { - value1 = gpio_get_value(TROUT_GPIO_CABLE_IN2); - set_irq_type(hi->irq_btn, value1 ? - IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); - value2 = gpio_get_value(TROUT_GPIO_CABLE_IN2); - } while (value1 != value2 && retry_limit-- > 0); - - H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit)); - - hrtimer_start(&hi->btn_timer, hi->btn_debounce_time, HRTIMER_MODE_REL); - - return IRQ_HANDLED; -} - -#if defined(CONFIG_DEBUG_FS) -static void h2w_debug_set(void *data, u64 val) -{ - switch_set_state(&hi->sdev, (int)val); -} - -static u64 h2w_debug_get(void *data) -{ - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(h2w_debug_fops, h2w_debug_get, h2w_debug_set, "%llu\n"); -static int __init h2w_debug_init(void) -{ - struct dentry *dent; - - dent = debugfs_create_dir("h2w", 0); - if (IS_ERR(dent)) - return PTR_ERR(dent); - - debugfs_create_file("state", 0644, dent, NULL, &h2w_debug_fops); - - return 0; -} - -device_initcall(h2w_debug_init); -#endif - -static int trout_h2w_probe(struct platform_device *pdev) -{ - int ret; - - printk(KERN_INFO "H2W: Registering H2W (headset) driver\n"); - hi = kzalloc(sizeof(struct h2w_info), GFP_KERNEL); - if (!hi) - return -ENOMEM; - - atomic_set(&hi->btn_state, 0); - hi->ignore_btn = 0; - - hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ - hi->btn_debounce_time = ktime_set(0, 10000000); /* 10 ms */ - hi->sdev.name = "h2w"; - hi->sdev.print_name = trout_h2w_print_name; - - ret = switch_dev_register(&hi->sdev); - if (ret < 0) - goto err_switch_dev_register; - - g_detection_work_queue = create_workqueue("detection"); - if (g_detection_work_queue == NULL) { - ret = -ENOMEM; - goto err_create_work_queue; - } - - ret = gpio_request(TROUT_GPIO_CABLE_IN1, "h2w_detect"); - if (ret < 0) - goto err_request_detect_gpio; - - ret = gpio_request(TROUT_GPIO_CABLE_IN2, "h2w_button"); - if (ret < 0) - goto err_request_button_gpio; - - ret = gpio_direction_input(TROUT_GPIO_CABLE_IN1); - if (ret < 0) - goto err_set_detect_gpio; - - ret = gpio_direction_input(TROUT_GPIO_CABLE_IN2); - if (ret < 0) - goto err_set_button_gpio; - - hi->irq = gpio_to_irq(TROUT_GPIO_CABLE_IN1); - if (hi->irq < 0) { - ret = hi->irq; - goto err_get_h2w_detect_irq_num_failed; - } - - hi->irq_btn = gpio_to_irq(TROUT_GPIO_CABLE_IN2); - if (hi->irq_btn < 0) { - ret = hi->irq_btn; - goto err_get_button_irq_num_failed; - } - - /* Set CPLD MUX to H2W <-> CPLD GPIO */ - configure_cpld(UART3); - /* Set the CPLD connected H2W GPIO's to input */ - gpio_set_value(TROUT_GPIO_H2W_CLK_DIR, 0); - gpio_set_value(TROUT_GPIO_H2W_DAT_DIR, 0); - - hrtimer_init(&hi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - hi->timer.function = detect_event_timer_func; - hrtimer_init(&hi->btn_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - hi->btn_timer.function = button_event_timer_func; - - ret = request_irq(hi->irq, detect_irq_handler, - IRQF_TRIGGER_LOW, "h2w_detect", NULL); - if (ret < 0) - goto err_request_detect_irq; - - /* Disable button until plugged in */ - set_irq_flags(hi->irq_btn, IRQF_VALID | IRQF_NOAUTOEN); - ret = request_irq(hi->irq_btn, button_irq_handler, - IRQF_TRIGGER_LOW, "h2w_button", NULL); - if (ret < 0) - goto err_request_h2w_headset_button_irq; - - ret = set_irq_wake(hi->irq, 1); - if (ret < 0) - goto err_request_input_dev; - ret = set_irq_wake(hi->irq_btn, 1); - if (ret < 0) - goto err_request_input_dev; - - hi->input = input_allocate_device(); - if (!hi->input) { - ret = -ENOMEM; - goto err_request_input_dev; - } - - hi->input->name = "h2w headset"; - hi->input->evbit[0] = BIT_MASK(EV_KEY); - hi->input->keybit[BIT_WORD(KEY_MEDIA)] = BIT_MASK(KEY_MEDIA); - - ret = input_register_device(hi->input); - if (ret < 0) - goto err_register_input_dev; - - return 0; - -err_register_input_dev: - input_free_device(hi->input); -err_request_input_dev: - free_irq(hi->irq_btn, 0); -err_request_h2w_headset_button_irq: - free_irq(hi->irq, 0); -err_request_detect_irq: -err_get_button_irq_num_failed: -err_get_h2w_detect_irq_num_failed: -err_set_button_gpio: -err_set_detect_gpio: - gpio_free(TROUT_GPIO_CABLE_IN2); -err_request_button_gpio: - gpio_free(TROUT_GPIO_CABLE_IN1); -err_request_detect_gpio: - destroy_workqueue(g_detection_work_queue); -err_create_work_queue: - switch_dev_unregister(&hi->sdev); -err_switch_dev_register: - printk(KERN_ERR "H2W: Failed to register driver\n"); - - return ret; -} - -static int trout_h2w_remove(struct platform_device *pdev) -{ - H2W_DBG(""); - if (switch_get_state(&hi->sdev)) - remove_headset(); - input_unregister_device(hi->input); - gpio_free(TROUT_GPIO_CABLE_IN2); - gpio_free(TROUT_GPIO_CABLE_IN1); - free_irq(hi->irq_btn, 0); - free_irq(hi->irq, 0); - destroy_workqueue(g_detection_work_queue); - switch_dev_unregister(&hi->sdev); - - return 0; -} - -static struct platform_device trout_h2w_device = { - .name = "trout-h2w", -}; - -static struct platform_driver trout_h2w_driver = { - .probe = trout_h2w_probe, - .remove = trout_h2w_remove, - .driver = { - .name = "trout-h2w", - .owner = THIS_MODULE, - }, -}; - -static int __init trout_h2w_init(void) -{ - int ret; - H2W_DBG(""); - ret = platform_driver_register(&trout_h2w_driver); - if (ret) - return ret; - return platform_device_register(&trout_h2w_device); -} - -static void __exit trout_h2w_exit(void) -{ - platform_device_unregister(&trout_h2w_device); - platform_driver_unregister(&trout_h2w_driver); -} - -module_init(trout_h2w_init); -module_exit(trout_h2w_exit); - -MODULE_AUTHOR("Laurence Chen "); -MODULE_DESCRIPTION("HTC 2 Wire detection driver for trout"); -MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 1164eed6a25e7..6fbb591dfb87e 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -64,6 +64,9 @@ #include #include #include +#ifdef CONFIG_HTC_HEADSET +#include +#endif #ifdef CONFIG_WIFI_CONTROL_FUNC #include #endif @@ -432,6 +435,47 @@ static struct platform_device sd_door_switch = { }, }; +#ifdef CONFIG_HTC_HEADSET +static void h2w_config_cpld(int route) +{ + switch (route) { + case H2W_UART3: + gpio_set_value(TROUT_GPIO_H2W_SEL0, 0); + gpio_set_value(TROUT_GPIO_H2W_SEL1, 1); + break; + case H2W_GPIO: + gpio_set_value(TROUT_GPIO_H2W_SEL0, 0); + gpio_set_value(TROUT_GPIO_H2W_SEL1, 0); + break; + } +} + +static void h2w_init_cpld(void) +{ + h2w_config_cpld(H2W_UART3); + gpio_set_value(TROUT_GPIO_H2W_CLK_DIR, 0); + gpio_set_value(TROUT_GPIO_H2W_DAT_DIR, 0); +} + +static struct h2w_platform_data trout_h2w_data = { + .cable_in1 = TROUT_GPIO_CABLE_IN1, + .cable_in2 = TROUT_GPIO_CABLE_IN2, + .h2w_clk = TROUT_GPIO_H2W_CLK_GPI, + .h2w_data = TROUT_GPIO_H2W_DAT_GPI, + .debug_uart = H2W_UART3, + .config_cpld = h2w_config_cpld, + .init_cpld = h2w_init_cpld, +}; + +static struct platform_device trout_h2w = { + .name = "h2w", + .id = -1, + .dev = { + .platform_data = &trout_h2w_data, + }, +}; +#endif + /* adjust eye diagram, disable vbusvalid interrupts */ static int trout_phy_init_seq[] = { 0x40, 0x31, 0x1D, 0x0D, 0x1D, 0x10, -1 }; @@ -665,6 +709,9 @@ static struct platform_device *devices[] __initdata = { #ifdef CONFIG_WIFI_CONTROL_FUNC &trout_wifi, #endif +#ifdef CONFIG_HTC_HEADSET + &trout_h2w, +#endif #if defined(CONFIG_TROUT_PWRSINK) &trout_pwr_sink, #endif From 5db006835df2f75a7f87bc9756319a3a74b3ae2d Mon Sep 17 00:00:00 2001 From: Laurence Chen Date: Wed, 11 Feb 2009 13:29:46 -0800 Subject: [PATCH 0269/2556] [ARM] msm: audio_in: add audpre parameters Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/qdsp5/audio_in.c | 330 +++++++++++++++++++++++------ include/linux/msm_audio.h | 4 + 2 files changed, 275 insertions(+), 59 deletions(-) diff --git a/arch/arm/mach-msm/qdsp5/audio_in.c b/arch/arm/mach-msm/qdsp5/audio_in.c index b4e3ce7ea33c5..b3311761c757b 100644 --- a/arch/arm/mach-msm/qdsp5/audio_in.c +++ b/arch/arm/mach-msm/qdsp5/audio_in.c @@ -50,6 +50,35 @@ #define STEREO_DATA_SIZE (MONO_DATA_SIZE * 2) #define DMASZ (FRAME_SIZE * FRAME_NUM) +#define AGC_PARAM_SIZE (20) +#define NS_PARAM_SIZE (6) +#define IIR_PARAM_SIZE (48) +#define DEBUG (0) + +#define AGC_ENABLE 0x0001 +#define NS_ENABLE 0x0002 +#define IIR_ENABLE 0x0004 + +struct tx_agc_config { + uint16_t agc_params[AGC_PARAM_SIZE]; +}; + +struct ns_config { + uint16_t ns_params[NS_PARAM_SIZE]; +}; + +struct tx_iir_filter { + uint16_t num_bands; + uint16_t iir_params[IIR_PARAM_SIZE]; +}; + +struct audpre_cmd_iir_config_type { + uint16_t cmd_id; + uint16_t active_flag; + uint16_t num_bands; + uint16_t iir_params[IIR_PARAM_SIZE]; +}; + struct buffer { void *data; uint32_t size; @@ -95,17 +124,23 @@ struct audio_in { int stopped; /* set when stopped, cleared on flush */ /* audpre settings */ - audpreproc_cmd_cfg_agc_params tx_agc_cfg; - audpreproc_cmd_cfg_ns_params ns_cfg; - /* For different sample rate, the coeff might be different. * - * All the coeff should be passed from user space */ - audpreproc_cmd_cfg_iir_tuning_filter_params iir_cfg; + int agc_enable; + struct tx_agc_config agc; + + int ns_enable; + struct ns_config ns; + + int iir_enable; + struct tx_iir_filter iir; }; static int audio_in_dsp_enable(struct audio_in *audio, int enable); static int audio_in_encoder_config(struct audio_in *audio); static int audio_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt); static void audio_flush(struct audio_in *audio); +static int audio_dsp_set_agc(struct audio_in *audio); +static int audio_dsp_set_ns(struct audio_in *audio); +static int audio_dsp_set_tx_iir(struct audio_in *audio); static unsigned convert_dsp_samp_index(unsigned index) { @@ -242,31 +277,27 @@ static void audio_in_get_dsp_frames(struct audio_in *audio) uint32_t index; unsigned long flags; - if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV) { - index = audio->in_head; + index = audio->in_head; - /* XXX check for bogus frame size? */ + /* XXX check for bogus frame size? */ - frame = (void*) (((char *)audio->in[index].data) - sizeof(*frame)); + frame = (void *) (((char *)audio->in[index].data) - sizeof(*frame)); - spin_lock_irqsave(&audio->dsp_lock, flags); - audio->in[index].size = frame->bytes; + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->in[index].size = frame->bytes; - audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); + audio->in_head = (audio->in_head + 1) & (FRAME_NUM - 1); - /* If overflow, move the tail index foward. */ - if (audio->in_head == audio->in_tail) - audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); - else - audio->in_count++; + /* If overflow, move the tail index foward. */ + if (audio->in_head == audio->in_tail) + audio->in_tail = (audio->in_tail + 1) & (FRAME_NUM - 1); + else + audio->in_count++; - audio_dsp_read_buffer(audio, audio->dsp_cnt++); - spin_unlock_irqrestore(&audio->dsp_lock, flags); + audio_dsp_read_buffer(audio, audio->dsp_cnt++); + spin_unlock_irqrestore(&audio->dsp_lock, flags); - wake_up(&audio->wait); - } else { - /* TODO AAC not supported yet. */ - } + wake_up(&audio->wait); } static void audrec_dsp_event(void *data, unsigned id, size_t len, @@ -281,6 +312,9 @@ static void audrec_dsp_event(void *data, unsigned id, size_t len, if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_UPDATE) { if (msg[0] & AUDREC_MSG_CFG_DONE_TYPE_0_ENA) { pr_info("audpre: CFG ENABLED\n"); + audio_dsp_set_agc(audio); + audio_dsp_set_ns(audio); + audio_dsp_set_tx_iir(audio); audio_in_encoder_config(audio); } else { pr_info("audrec: CFG SLEEP\n"); @@ -323,16 +357,16 @@ struct msm_adsp_ops audrec_adsp_ops = { #define audio_send_queue_rec(audio,cmd,len) \ msm_adsp_write(audio->audrec, QDSP_uPAudRecCmdQueue, cmd, len) -#if 0 /* not wired up yet */ -static int audio_enable_tx_agc(struct audio_in *audio, int enable) +static int audio_dsp_set_agc(struct audio_in *audio) { audpreproc_cmd_cfg_agc_params cmd; memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPREPROC_CMD_CFG_AGC_PARAMS; - if (enable) { + if (audio->agc_enable) { /* cmd.tx_agc_param_mask = 0xFE00 from sample code */ - audio->tx_agc_cfg.tx_agc_param_mask = + cmd.tx_agc_param_mask = (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_SLOPE) | (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_TH) | (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_SLOPE) | @@ -340,10 +374,12 @@ static int audio_enable_tx_agc(struct audio_in *audio, int enable) (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_AIG_FLAG) | (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_STATIC_GAIN) | (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG); - audio->tx_agc_cfg.tx_agc_enable_flag = + cmd.tx_agc_enable_flag = AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA; + memcpy(&cmd.static_gain, &audio->agc.agc_params[0], + sizeof(uint16_t) * 6); /* cmd.param_mask = 0xFFF0 from sample code */ - audio->tx_agc_cfg.param_mask = + cmd.param_mask = (1 << AUDPREPROC_CMD_PARAM_MASK_RMS_TAY) | (1 << AUDPREPROC_CMD_PARAM_MASK_RELEASEK) | (1 << AUDPREPROC_CMD_PARAM_MASK_DELAY) | @@ -356,29 +392,61 @@ static int audio_enable_tx_agc(struct audio_in *audio, int enable) (1 << AUDPREPROC_CMD_PARAM_MASK_LEAK_UP) | (1 << AUDPREPROC_CMD_PARAM_MASK_LEAK_DOWN) | (1 << AUDPREPROC_CMD_PARAM_MASK_AIG_ATTACKK); + memcpy(&cmd.aig_attackk, &audio->agc.agc_params[6], + sizeof(uint16_t) * 14); + } else { - audio->tx_agc_cfg.tx_agc_param_mask = + cmd.tx_agc_param_mask = (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG); - audio->tx_agc_cfg.tx_agc_enable_flag = + cmd.tx_agc_enable_flag = AUDPREPROC_CMD_TX_AGC_ENA_FLAG_DIS; } - cmd = audio->tx_agc_cfg; - +#if DEBUG + pr_info("cmd_id = 0x%04x\n", cmd.cmd_id); + pr_info("tx_agc_param_mask = 0x%04x\n", cmd.tx_agc_param_mask); + pr_info("tx_agc_enable_flag = 0x%04x\n", cmd.tx_agc_enable_flag); + pr_info("static_gain = 0x%04x\n", cmd.static_gain); + pr_info("adaptive_gain_flag = 0x%04x\n", cmd.adaptive_gain_flag); + pr_info("expander_th = 0x%04x\n", cmd.expander_th); + pr_info("expander_slope = 0x%04x\n", cmd.expander_slope); + pr_info("compressor_th = 0x%04x\n", cmd.compressor_th); + pr_info("compressor_slope = 0x%04x\n", cmd.compressor_slope); + pr_info("param_mask = 0x%04x\n", cmd.param_mask); + pr_info("aig_attackk = 0x%04x\n", cmd.aig_attackk); + pr_info("aig_leak_down = 0x%04x\n", cmd.aig_leak_down); + pr_info("aig_leak_up = 0x%04x\n", cmd.aig_leak_up); + pr_info("aig_max = 0x%04x\n", cmd.aig_max); + pr_info("aig_min = 0x%04x\n", cmd.aig_min); + pr_info("aig_releasek = 0x%04x\n", cmd.aig_releasek); + pr_info("aig_leakrate_fast = 0x%04x\n", cmd.aig_leakrate_fast); + pr_info("aig_leakrate_slow = 0x%04x\n", cmd.aig_leakrate_slow); + pr_info("attackk_msw = 0x%04x\n", cmd.attackk_msw); + pr_info("attackk_lsw = 0x%04x\n", cmd.attackk_lsw); + pr_info("delay = 0x%04x\n", cmd.delay); + pr_info("releasek_msw = 0x%04x\n", cmd.releasek_msw); + pr_info("releasek_lsw = 0x%04x\n", cmd.releasek_lsw); + pr_info("rms_tav = 0x%04x\n", cmd.rms_tav); +#endif return audio_send_queue_pre(audio, &cmd, sizeof(cmd)); } -static int audio_enable_ns(struct audio_in *audio, int enable) +static int audio_dsp_set_ns(struct audio_in *audio) { audpreproc_cmd_cfg_ns_params cmd; - if (enable) { + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPREPROC_CMD_CFG_NS_PARAMS; + + if (audio->ns_enable) { /* cmd.ec_mode_new is fixed as 0x0064 when enable from sample code */ - audio->ns_cfg.ec_mode_new = + cmd.ec_mode_new = AUDPREPROC_CMD_EC_MODE_NEW_NS_ENA | AUDPREPROC_CMD_EC_MODE_NEW_HB_ENA | AUDPREPROC_CMD_EC_MODE_NEW_VA_ENA; + memcpy(&cmd.dens_gamma_n, &audio->ns.ns_params, + sizeof(audio->ns.ns_params)); } else { - audio->ns_cfg.ec_mode_new = + cmd.ec_mode_new = AUDPREPROC_CMD_EC_MODE_NEW_NLMS_DIS | AUDPREPROC_CMD_EC_MODE_NEW_DES_DIS | AUDPREPROC_CMD_EC_MODE_NEW_NS_DIS | @@ -393,27 +461,40 @@ static int audio_enable_ns(struct audio_in *audio, int enable) AUDPREPROC_CMD_EC_MODE_NEW_FNE_DIS | AUDPREPROC_CMD_EC_MODE_NEW_PRENLMS_DIS; } - cmd = audio->ns_cfg; - +#if DEBUG + pr_info("cmd_id = 0x%04x\n", cmd.cmd_id); + pr_info("ec_mode_new = 0x%04x\n", cmd.ec_mode_new); + pr_info("dens_gamma_n = 0x%04x\n", cmd.dens_gamma_n); + pr_info("dens_nfe_block_size = 0x%04x\n", cmd.dens_nfe_block_size); + pr_info("dens_limit_ns = 0x%04x\n", cmd.dens_limit_ns); + pr_info("dens_limit_ns_d = 0x%04x\n", cmd.dens_limit_ns_d); + pr_info("wb_gamma_e = 0x%04x\n", cmd.wb_gamma_e); + pr_info("wb_gamma_n = 0x%04x\n", cmd.wb_gamma_n); +#endif return audio_send_queue_pre(audio, &cmd, sizeof(cmd)); } -static int audio_enable_iir(struct audio_in *audio, int enable) +static int audio_dsp_set_tx_iir(struct audio_in *audio) { - audpreproc_cmd_cfg_iir_tuning_filter_params cmd; + struct audpre_cmd_iir_config_type cmd; memset(&cmd, 0, sizeof(cmd)); - cmd = audio->iir_cfg; + cmd.cmd_id = AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS; - if (enable) - /* cmd.active_flag is 0xFFFF from sample code but 0x0001 here */ + if (audio->iir_enable) { cmd.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_ENA; - else + cmd.num_bands = audio->iir.num_bands; + memcpy(&cmd.iir_params, &audio->iir.iir_params, + sizeof(audio->iir.iir_params)); + } else { cmd.active_flag = AUDPREPROC_CMD_IIR_ACTIVE_FLAG_DIS; - + } +#if DEBUG + pr_info("cmd_id = 0x%04x\n", cmd.cmd_id); + pr_info("active_flag = 0x%04x\n", cmd.active_flag); +#endif return audio_send_queue_pre(audio, &cmd, sizeof(cmd)); } -#endif static int audio_in_dsp_enable(struct audio_in *audio, int enable) { @@ -453,7 +534,10 @@ static int audio_in_encoder_config(struct audio_in *audio) */ for (n = 0; n < FRAME_NUM; n++) { audio->in[n].data = data + 4; - data += (4 + (audio->channel_mode ? 2048 : 1024)); + if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV) + data += (4 + (audio->channel_mode ? 2048 : 1024)); + else + data += (4 + 768); } return audio_send_queue_rec(audio, &cmd, sizeof(cmd)); @@ -474,6 +558,33 @@ static int audio_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt) /* ------------------- device --------------------- */ +static void audio_enable_agc(struct audio_in *audio, int enable) +{ + if (audio->agc_enable != enable) { + audio->agc_enable = enable; + if (audio->running) + audio_dsp_set_agc(audio); + } +} + +static void audio_enable_ns(struct audio_in *audio, int enable) +{ + if (audio->ns_enable != enable) { + audio->ns_enable = enable; + if (audio->running) + audio_dsp_set_ns(audio); + } +} + +static void audio_enable_tx_iir(struct audio_in *audio, int enable) +{ + if (audio->iir_enable != enable) { + audio->iir_enable = enable; + if (audio->running) + audio_dsp_set_tx_iir(audio); + } +} + static void audio_flush(struct audio_in *audio) { int i; @@ -540,10 +651,7 @@ static long audio_in_ioctl(struct file *file, unsigned int cmd, unsigned long ar if (cfg.type == 0) { cfg.type = AUDREC_CMD_TYPE_0_INDEX_WAV; } else if (cfg.type == 1) { - /* TODO AAC not supported */ - //cfg.type = AUDREC_CMD_TYPE_0_INDEX_AAC; - rc = -EINVAL; - break; + cfg.type = AUDREC_CMD_TYPE_0_INDEX_AAC; } else { rc = -EINVAL; break; @@ -632,6 +740,8 @@ static ssize_t audio_in_read(struct file *file, char __user *buf, size_t count, spin_unlock_irqrestore(&audio->dsp_lock, flags); count -= size; buf += size; + if (audio->type == AUDREC_CMD_TYPE_0_INDEX_AAC) + break; } else { pr_err("audio_in: short read\n"); break; @@ -645,7 +755,10 @@ static ssize_t audio_in_read(struct file *file, char __user *buf, size_t count, return rc; } -static ssize_t audio_in_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) +static ssize_t audio_in_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *pos) { return -EINVAL; } @@ -687,17 +800,16 @@ static int audio_in_open(struct inode *inode, struct file *file) audio->channel_mode = AUDREC_CMD_STEREO_MODE_MONO; audio->buffer_size = MONO_DATA_SIZE; audio->type = AUDREC_CMD_TYPE_0_INDEX_WAV; - audio->tx_agc_cfg.cmd_id = AUDPREPROC_CMD_CFG_AGC_PARAMS; - audio->ns_cfg.cmd_id = AUDPREPROC_CMD_CFG_NS_PARAMS; - audio->iir_cfg.cmd_id = AUDPREPROC_CMD_CFG_IIR_TUNING_FILTER_PARAMS; rc = audmgr_open(&audio->audmgr); if (rc) goto done; - rc = msm_adsp_get("AUDPREPROCTASK", &audio->audpre, &audpre_adsp_ops, audio); + rc = msm_adsp_get("AUDPREPROCTASK", &audio->audpre, + &audpre_adsp_ops, audio); if (rc) goto done; - rc = msm_adsp_get("AUDRECTASK", &audio->audrec, &audrec_adsp_ops, audio); + rc = msm_adsp_get("AUDRECTASK", &audio->audrec, + &audrec_adsp_ops, audio); if (rc) goto done; @@ -714,6 +826,87 @@ static int audio_in_open(struct inode *inode, struct file *file) return rc; } +static long audpre_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio_in *audio = file->private_data; + int rc = 0, enable; + uint16_t enable_mask; +#if DEBUG + int i; +#endif + + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_ENABLE_AUDPRE: { + if (copy_from_user(&enable_mask, (void *) arg, + sizeof(enable_mask))) + goto out_fault; + + enable = (enable_mask & AGC_ENABLE) ? 1 : 0; + audio_enable_agc(audio, enable); + enable = (enable_mask & NS_ENABLE) ? 1 : 0; + audio_enable_ns(audio, enable); + enable = (enable_mask & IIR_ENABLE) ? 1 : 0; + audio_enable_tx_iir(audio, enable); + break; + } + case AUDIO_SET_AGC: { + if (copy_from_user(&audio->agc, (void *) arg, + sizeof(audio->agc))) + goto out_fault; +#if DEBUG + pr_info("set agc\n"); + for (i = 0; i < AGC_PARAM_SIZE; i++) \ + pr_info("agc_params[%d] = 0x%04x\n", i, + audio->agc.agc_params[i]); +#endif + break; + } + case AUDIO_SET_NS: { + if (copy_from_user(&audio->ns, (void *) arg, + sizeof(audio->ns))) + goto out_fault; +#if DEBUG + pr_info("set ns\n"); + for (i = 0; i < NS_PARAM_SIZE; i++) \ + pr_info("ns_params[%d] = 0x%04x\n", + i, audio->ns.ns_params[i]); +#endif + break; + } + case AUDIO_SET_TX_IIR: { + if (copy_from_user(&audio->iir, (void *) arg, + sizeof(audio->iir))) + goto out_fault; +#if DEBUG + pr_info("set iir\n"); + pr_info("iir.num_bands = 0x%04x\n", audio->iir.num_bands); + for (i = 0; i < IIR_PARAM_SIZE; i++) \ + pr_info("iir_params[%d] = 0x%04x\n", + i, audio->iir.iir_params[i]); +#endif + break; + } + default: + rc = -EINVAL; + } + + goto out; + +out_fault: + rc = -EFAULT; +out: + mutex_unlock(&audio->lock); + return rc; +} + +static int audpre_open(struct inode *inode, struct file *file) +{ + struct audio_in *audio = &the_audio_in; + file->private_data = audio; + return 0; +} + static struct file_operations audio_fops = { .owner = THIS_MODULE, .open = audio_in_open, @@ -723,14 +916,27 @@ static struct file_operations audio_fops = { .unlocked_ioctl = audio_in_ioctl, }; +static struct file_operations audpre_fops = { + .owner = THIS_MODULE, + .open = audpre_open, + .unlocked_ioctl = audpre_ioctl, +}; + struct miscdevice audio_in_misc = { .minor = MISC_DYNAMIC_MINOR, .name = "msm_pcm_in", .fops = &audio_fops, }; +struct miscdevice audpre_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_audpre", + .fops = &audpre_fops, +}; + static int __init audio_in_init(void) { + int rc; the_audio_in.data = dma_alloc_coherent(NULL, DMASZ, &the_audio_in.phys, GFP_KERNEL); if (!the_audio_in.data) { @@ -743,7 +949,13 @@ static int __init audio_in_init(void) mutex_init(&the_audio_in.read_lock); spin_lock_init(&the_audio_in.dsp_lock); init_waitqueue_head(&the_audio_in.wait); - return misc_register(&audio_in_misc); + rc = misc_register(&audio_in_misc); + if (!rc) { + rc = misc_register(&audpre_misc); + if (rc < 0) + misc_deregister(&audio_in_misc); + } + return rc; } device_initcall(audio_in_init); diff --git a/include/linux/msm_audio.h b/include/linux/msm_audio.h index cd2165b1b3b74..9da6ccc20b3a1 100644 --- a/include/linux/msm_audio.h +++ b/include/linux/msm_audio.h @@ -35,6 +35,10 @@ #define AUDIO_SET_EQ _IOW(AUDIO_IOCTL_MAGIC, 8, unsigned) #define AUDIO_SET_RX_IIR _IOW(AUDIO_IOCTL_MAGIC, 9, unsigned) #define AUDIO_SET_VOLUME _IOW(AUDIO_IOCTL_MAGIC, 10, unsigned) +#define AUDIO_ENABLE_AUDPRE _IOW(AUDIO_IOCTL_MAGIC, 11, unsigned) +#define AUDIO_SET_AGC _IOW(AUDIO_IOCTL_MAGIC, 12, unsigned) +#define AUDIO_SET_NS _IOW(AUDIO_IOCTL_MAGIC, 13, unsigned) +#define AUDIO_SET_TX_IIR _IOW(AUDIO_IOCTL_MAGIC, 14, unsigned) struct msm_audio_config { uint32_t buffer_size; From 4f75530971b84a140844ecfbd8fd4072ba4aeacf Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Sat, 14 Feb 2009 13:33:24 -0800 Subject: [PATCH 0270/2556] [ARM] msm: htc_acoustic: use smem and rpc to write acoustic table Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/htc_acoustic.c | 212 +++++++++++++++++++++++++------ 1 file changed, 176 insertions(+), 36 deletions(-) diff --git a/arch/arm/mach-msm/htc_acoustic.c b/arch/arm/mach-msm/htc_acoustic.c index 9fb92f588c0b8..3de71dddb5893 100644 --- a/arch/arm/mach-msm/htc_acoustic.c +++ b/arch/arm/mach-msm/htc_acoustic.c @@ -13,54 +13,205 @@ * GNU General Public License for more details. * */ - +#include #include #include #include #include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "smd_private.h" + +#define ACOUSTIC_IOCTL_MAGIC 'p' +#define ACOUSTIC_ARM11_DONE _IOW(ACOUSTIC_IOCTL_MAGIC, 22, unsigned int) + +#define HTCRPOG 0x30100002 +#define HTCVERS 0 +#define ONCRPC_SET_MIC_BIAS_PROC (1) +#define ONCRPC_ACOUSTIC_INIT_PROC (5) +#define ONCRPC_ALLOC_ACOUSTIC_MEM_PROC (6) + +#define HTC_ACOUSTIC_TABLE_SIZE (0x10000) + +#define D(fmt, args...) printk(KERN_INFO "htc-acoustic: "fmt, ##args) +#define E(fmt, args...) printk(KERN_ERR "htc-acoustic: "fmt, ##args) + +struct set_smem_req { + struct rpc_request_hdr hdr; + uint32_t size; +}; + +struct set_smem_rep { + struct rpc_reply_hdr hdr; + int n; +}; + +struct set_acoustic_req { + struct rpc_request_hdr hdr; +}; -#define HTC_ACOUSTIC_TABLE_BASE_PHY_ADDR_START (0x01FE0000) -#define HTC_ACOUSTIC_TABLE_SIZE (0x10000) +struct set_acoustic_rep { + struct rpc_reply_hdr hdr; + int n; +}; -#define ACOUSTICE(x...) printk(KERN_ERR "[ACOUSTIC] " x) +static uint32_t htc_acoustic_vir_addr; +static struct msm_rpc_endpoint *endpoint; +static struct mutex api_lock; static int acoustic_mmap(struct file *file, struct vm_area_struct *vma) { - unsigned long pgoff; - size_t size = vma->vm_end - vma->vm_start; - - if (vma->vm_pgoff != 0) - return -EINVAL; - - if (size <= HTC_ACOUSTIC_TABLE_SIZE) - pgoff = HTC_ACOUSTIC_TABLE_BASE_PHY_ADDR_START >> PAGE_SHIFT; - else - return -EINVAL; + unsigned long pgoff, delta; + int rc = -EINVAL; + size_t size; + + D("mmap\n"); + + mutex_lock(&api_lock); + + size = vma->vm_end - vma->vm_start; + if (vma->vm_pgoff != 0) { + E("mmap failed: page offset %lx\n", vma->vm_pgoff); + goto done; + } + + if (!htc_acoustic_vir_addr) { + E("mmap failed: smem region not allocated\n"); + rc = -EIO; + goto done; + } + + pgoff = MSM_SHARED_RAM_PHYS + + (htc_acoustic_vir_addr - (uint32_t)MSM_SHARED_RAM_BASE); + delta = PAGE_ALIGN(pgoff) - pgoff; + + if (size + delta > HTC_ACOUSTIC_TABLE_SIZE) { + E("mmap failed: size %d\n", size); + goto done; + } + + pgoff += delta; vma->vm_flags |= VM_IO | VM_RESERVED; - if (io_remap_pfn_range(vma, vma->vm_start, pgoff, - size, vma->vm_page_prot)) - return -EAGAIN; - - return 0; + rc = io_remap_pfn_range(vma, vma->vm_start, pgoff >> PAGE_SHIFT, + size, vma->vm_page_prot); + + if (rc < 0) + E("mmap failed: remap error %d\n", rc); + +done: mutex_unlock(&api_lock); + return rc; } static int acoustic_open(struct inode *inode, struct file *file) { - return 0; + int rc = -EIO; + struct set_smem_req req_smem; + struct set_smem_rep rep_smem; + + D("open\n"); + + mutex_lock(&api_lock); + + if (!htc_acoustic_vir_addr) { + if (endpoint == NULL) { + endpoint = msm_rpc_connect(HTCRPOG, HTCVERS, 0); + if (IS_ERR(endpoint)) { + E("init rpc failed! rc = %ld\n", + PTR_ERR(endpoint)); + endpoint = NULL; + goto done; + } + } + + req_smem.size = cpu_to_be32(HTC_ACOUSTIC_TABLE_SIZE); + rc = msm_rpc_call_reply(endpoint, + ONCRPC_ALLOC_ACOUSTIC_MEM_PROC, + &req_smem, sizeof(req_smem), + &rep_smem, sizeof(rep_smem), + 5 * HZ); + + if (rep_smem.n != 0 || rc < 0) { + E("open failed: ALLOC_ACOUSTIC_MEM_PROC error %d.\n", + rc); + goto done; + } + htc_acoustic_vir_addr = + (uint32_t)smem_alloc(SMEM_ID_VENDOR1, + HTC_ACOUSTIC_TABLE_SIZE); + if (!htc_acoustic_vir_addr) { + E("open failed: smem_alloc error\n"); + goto done; + } + } + + rc = 0; +done: + mutex_unlock(&api_lock); + return rc; } static int acoustic_release(struct inode *inode, struct file *file) { + D("release\n"); + return 0; +} + +static long acoustic_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int rc, reply_value; + struct set_acoustic_req req; + struct set_acoustic_rep rep; + + D("ioctl\n"); + + mutex_lock(&api_lock); + + switch (cmd) { + case ACOUSTIC_ARM11_DONE: + D("ioctl: ACOUSTIC_ARM11_DONE called %d.\n", current->pid); + rc = msm_rpc_call_reply(endpoint, + ONCRPC_ACOUSTIC_INIT_PROC, &req, + sizeof(req), &rep, sizeof(rep), + 5 * HZ); + + reply_value = be32_to_cpu(rep.n); + if (reply_value != 0 || rc < 0) { + E("ioctl failed: ONCRPC_ACOUSTIC_INIT_PROC "\ + "error %d.\n", rc); + if (rc >= 0) + rc = -EIO; + break; + } + D("ioctl: ONCRPC_ACOUSTIC_INIT_PROC success.\n"); + break; + default: + E("ioctl: invalid command\n"); + rc = -EINVAL; + } + + mutex_unlock(&api_lock); return 0; } - + + static struct file_operations acoustic_fops = { .owner = THIS_MODULE, .open = acoustic_open, .release = acoustic_release, .mmap = acoustic_mmap, + .unlocked_ioctl = acoustic_ioctl, }; static struct miscdevice acoustic_misc = { @@ -71,29 +222,18 @@ static struct miscdevice acoustic_misc = { static int __init acoustic_init(void) { - int ret; - - ret = misc_register(&acoustic_misc); - if (ret < 0) { - ACOUSTICE("failed to register misc device!\n"); - return ret; - } - - return 0; + mutex_init(&api_lock); + return misc_register(&acoustic_misc); } static void __exit acoustic_exit(void) { - int ret; - - ret = misc_deregister(&acoustic_misc); - if (ret < 0) - ACOUSTICE("failed to unregister misc device!\n"); + misc_deregister(&acoustic_misc); } module_init(acoustic_init); module_exit(acoustic_exit); - + MODULE_AUTHOR("Laurence Chen "); MODULE_DESCRIPTION("HTC acoustic driver"); MODULE_LICENSE("GPL"); From 64cca3feb5e4644e7641ea7a1dacae22f0c28f6a Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 17 Feb 2009 14:35:27 -0800 Subject: [PATCH 0271/2556] [ARM] msm: htc_pwrsink: Generify driver Signed-off-by: San Mehat --- arch/arm/mach-msm/Kconfig | 4 +- arch/arm/mach-msm/Makefile | 2 +- arch/arm/mach-msm/htc_pwrsink.c | 194 +++++++++++++++--- arch/arm/mach-msm/include/mach/htc_pwrsink.h | 88 ++++++++ .../arm/mach-msm/include/mach/trout_pwrsink.h | 58 ------ 5 files changed, 255 insertions(+), 91 deletions(-) create mode 100644 arch/arm/mach-msm/include/mach/htc_pwrsink.h delete mode 100644 arch/arm/mach-msm/include/mach/trout_pwrsink.h diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 3c73de9f0471e..ba1cd7fd70b66 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -197,10 +197,10 @@ config TROUT_BATTCHG default y bool "Trout battery / charger driver" -config TROUT_PWRSINK +config HTC_PWRSINK depends on MSM_SMD default y - bool "Trout Power Sink Driver" + bool "HTC Power Sink Driver" choice prompt "Default Timer" diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 3a013e3566897..3a7cef7409c81 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -45,7 +45,6 @@ obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o obj-$(CONFIG_MACH_TROUT) += board-trout-rfkill.o obj-$(CONFIG_MACH_TROUT) += board-trout-wifi.o obj-$(CONFIG_TROUT_BATTCHG) += htc_battery.o -obj-$(CONFIG_TROUT_PWRSINK) += htc_pwrsink.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o @@ -61,4 +60,5 @@ else obj-y += gpio.o endif +obj-$(CONFIG_HTC_PWRSINK) += htc_pwrsink.o obj-$(CONFIG_HTC_HEADSET) += htc_headset.o diff --git a/arch/arm/mach-msm/htc_pwrsink.c b/arch/arm/mach-msm/htc_pwrsink.c index 6ce6fb4737736..2ec2c7f4bb1b2 100644 --- a/arch/arm/mach-msm/htc_pwrsink.c +++ b/arch/arm/mach-msm/htc_pwrsink.c @@ -23,17 +23,28 @@ #include #include #include -#include +#include #include "smd_private.h" -static int initialized = 0; +enum { + PWRSINK_DEBUG_CURR_CHANGE = 1U << 0, + PWRSINK_DEBUG_CURR_CHANGE_AUDIO = 1U << 1, +}; +static int pwrsink_debug_mask; +module_param_named(debug_mask, pwrsink_debug_mask, int, + S_IRUGO | S_IWUSR | S_IWGRP); + +static int initialized; +static unsigned audio_path = 1; /* HTC_SND_DEVICE_SPEAKER = 1 */ +static struct pwr_sink_audio audio_sink_array[PWRSINK_AUDIO_LAST + 1]; static struct pwr_sink *sink_array[PWRSINK_LAST + 1]; static DEFINE_SPINLOCK(sink_lock); +static DEFINE_SPINLOCK(audio_sink_lock); static unsigned long total_sink; static uint32_t *smem_total_sink; -int trout_pwrsink_set(pwrsink_id_type id, unsigned percent_utilized) +int htc_pwrsink_set(pwrsink_id_type id, unsigned percent_utilized) { unsigned long flags; @@ -67,7 +78,7 @@ int trout_pwrsink_set(pwrsink_id_type id, unsigned percent_utilized) if (smem_total_sink) *smem_total_sink = total_sink / 1000; - pr_debug("trout_pwrsink: ID %d, Util %d%%, Total %lu uA %s\n", + pr_debug("htc_pwrsink: ID %d, Util %d%%, Total %lu uA %s\n", id, percent_utilized, total_sink, smem_total_sink ? "SET" : ""); @@ -75,73 +86,196 @@ int trout_pwrsink_set(pwrsink_id_type id, unsigned percent_utilized) return 0; } -EXPORT_SYMBOL(trout_pwrsink_set); +EXPORT_SYMBOL(htc_pwrsink_set); -void trout_pwrsink_suspend_early(struct early_suspend *h) +static void compute_audio_current(void) { - trout_pwrsink_set(PWRSINK_SYSTEM_LOAD, 70); + /* unsigned long flags; */ + unsigned max_percent = 0; + int i, active_audio_sinks = 0; + pwrsink_audio_id_type last_active_audio_sink = 0; + + /* Make sure this segment will be spinlocked + before computing by calling function. */ + /* spin_lock_irqsave(&audio_sink_lock, flags); */ + for (i = 0; i <= PWRSINK_AUDIO_LAST; ++i) { + max_percent = (audio_sink_array[i].percent > max_percent) ? + audio_sink_array[i].percent : max_percent; + if (audio_sink_array[i].percent > 0) { + active_audio_sinks++; + last_active_audio_sink = i; + } + } + if (active_audio_sinks == 0) + htc_pwrsink_set(PWRSINK_AUDIO, 0); + else if (active_audio_sinks == 1) { + pwrsink_audio_id_type laas = last_active_audio_sink; + /* TODO: add volume and routing path current. */ + if (audio_path == 1) /* Speaker */ + htc_pwrsink_set(PWRSINK_AUDIO, + audio_sink_array[laas].percent); + else + htc_pwrsink_set(PWRSINK_AUDIO, + audio_sink_array[laas].percent * 9 / 10); + } else if (active_audio_sinks > 1) { + /* TODO: add volume and routing path current. */ + if (audio_path == 1) /* Speaker */ + htc_pwrsink_set(PWRSINK_AUDIO, max_percent); + else + htc_pwrsink_set(PWRSINK_AUDIO, max_percent * 9 / 10); + } + /* spin_unlock_irqrestore(&audio_sink_lock, flags); */ + + if (pwrsink_debug_mask & PWRSINK_DEBUG_CURR_CHANGE_AUDIO) + pr_info("%s: active_audio_sinks=%d, audio_path=%d\n", __func__, + active_audio_sinks, audio_path); } -int trout_pwrsink_suspend_late(struct platform_device *pdev, pm_message_t state) +int htc_pwrsink_audio_set(pwrsink_audio_id_type id, unsigned percent_utilized) { - trout_pwrsink_set(PWRSINK_SYSTEM_LOAD, 13); + unsigned long flags; + + if (id < 0 || id > PWRSINK_AUDIO_LAST) + return -EINVAL; + + if (pwrsink_debug_mask & PWRSINK_DEBUG_CURR_CHANGE_AUDIO) + pr_info("%s: id=%d, percent=%d, percent_old=%d\n", __func__, + id, percent_utilized, audio_sink_array[id].percent); + + spin_lock_irqsave(&audio_sink_lock, flags); + if (audio_sink_array[id].percent == percent_utilized) { + spin_unlock_irqrestore(&audio_sink_lock, flags); + return 0; + } + audio_sink_array[id].percent = percent_utilized; + spin_unlock_irqrestore(&audio_sink_lock, flags); + compute_audio_current(); return 0; } +EXPORT_SYMBOL(htc_pwrsink_audio_set); -int trout_pwrsink_resume_early(struct platform_device *pdev) +int htc_pwrsink_audio_volume_set(pwrsink_audio_id_type id, unsigned volume) { - trout_pwrsink_set(PWRSINK_SYSTEM_LOAD, 70); + unsigned long flags; + + if (id < 0 || id > PWRSINK_AUDIO_LAST) + return -EINVAL; + + if (pwrsink_debug_mask & PWRSINK_DEBUG_CURR_CHANGE_AUDIO) + pr_info("%s: id=%d, volume=%d, volume_old=%d\n", __func__, + id, volume, audio_sink_array[id].volume); + + spin_lock_irqsave(&audio_sink_lock, flags); + if (audio_sink_array[id].volume == volume) { + spin_unlock_irqrestore(&audio_sink_lock, flags); + return 0; + } + audio_sink_array[id].volume = volume; + spin_unlock_irqrestore(&audio_sink_lock, flags); + compute_audio_current(); return 0; } +EXPORT_SYMBOL(htc_pwrsink_audio_volume_set); -void trout_pwrsink_resume_late(struct early_suspend *h) +int htc_pwrsink_audio_path_set(unsigned path) { - trout_pwrsink_set(PWRSINK_SYSTEM_LOAD, 100); + unsigned long flags; + + if (pwrsink_debug_mask & PWRSINK_DEBUG_CURR_CHANGE_AUDIO) + pr_info("%s: path=%d, path_old=%d\n", + __func__, path, audio_path); + + spin_lock_irqsave(&audio_sink_lock, flags); + if (audio_path == path) { + spin_unlock_irqrestore(&audio_sink_lock, flags); + return 0; + } + audio_path = path; + spin_unlock_irqrestore(&audio_sink_lock, flags); + compute_audio_current(); + return 0; +} +EXPORT_SYMBOL(htc_pwrsink_audio_path_set); + +void htc_pwrsink_suspend_early(struct early_suspend *h) +{ + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 70); +} + +int htc_pwrsink_suspend_late(struct platform_device *pdev, pm_message_t state) +{ + struct pwr_sink_platform_data *pdata = pdev->dev.platform_data; + + if (pdata && pdata->suspend_late) + pdata->suspend_late(pdev, state); + else + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 13); + return 0; } -struct early_suspend trout_pwrsink_early_suspend = { +int htc_pwrsink_resume_early(struct platform_device *pdev) +{ + struct pwr_sink_platform_data *pdata = pdev->dev.platform_data; + + if (pdata && pdata->resume_early) + pdata->resume_early(pdev); + else + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 70); + return 0; +} + +void htc_pwrsink_resume_late(struct early_suspend *h) +{ + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 100); +} + +struct early_suspend htc_pwrsink_early_suspend = { .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, - .suspend = trout_pwrsink_suspend_early, - .resume = trout_pwrsink_resume_late, + .suspend = htc_pwrsink_suspend_early, + .resume = htc_pwrsink_resume_late, }; -static int __init trout_pwrsink_probe(struct platform_device *pdev) +static int __init htc_pwrsink_probe(struct platform_device *pdev) { struct pwr_sink_platform_data *pdata = pdev->dev.platform_data; int i; - + if (!pdata) return -EINVAL; total_sink = 0; for (i = 0; i < pdata->num_sinks; i++) { sink_array[pdata->sinks[i].id] = &pdata->sinks[i]; - total_sink += (pdata->sinks[i].ua_max * pdata->sinks[i].percent_util / 100); + total_sink += (pdata->sinks[i].ua_max * + pdata->sinks[i].percent_util / 100); } initialized = 1; - register_early_suspend(&trout_pwrsink_early_suspend); - + if (pdata->suspend_early) + htc_pwrsink_early_suspend.suspend = pdata->suspend_early; + if (pdata->resume_late) + htc_pwrsink_early_suspend.resume = pdata->resume_late; + register_early_suspend(&htc_pwrsink_early_suspend); + return 0; } -static struct platform_driver trout_pwrsink_driver = { - .probe = trout_pwrsink_probe, - .suspend_late = trout_pwrsink_suspend_late, - .resume_early = trout_pwrsink_resume_early, +static struct platform_driver htc_pwrsink_driver = { + .probe = htc_pwrsink_probe, + .suspend_late = htc_pwrsink_suspend_late, + .resume_early = htc_pwrsink_resume_early, .driver = { - .name = "trout_pwrsink", + .name = "htc_pwrsink", .owner = THIS_MODULE, }, }; -static int __init trout_pwrsink_init(void) +static int __init htc_pwrsink_init(void) { initialized = 0; memset(sink_array, 0, sizeof(sink_array)); - return platform_driver_register(&trout_pwrsink_driver); + return platform_driver_register(&htc_pwrsink_driver); } -module_init(trout_pwrsink_init); - +module_init(htc_pwrsink_init); diff --git a/arch/arm/mach-msm/include/mach/htc_pwrsink.h b/arch/arm/mach-msm/include/mach/htc_pwrsink.h new file mode 100644 index 0000000000000..d82c21273a45b --- /dev/null +++ b/arch/arm/mach-msm/include/mach/htc_pwrsink.h @@ -0,0 +1,88 @@ +/* include/asm/mach-msm/htc_pwrsink.h + * + * Copyright (C) 2007 Google, Inc. + * Copyright (C) 2008 HTC Corporation. + * + * 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 _ARCH_ARM_MACH_MSM_HTC_PWRSINK_H_ +#define _ARCH_ARM_MACH_MSM_HTC_PWRSINK_H_ + +#include +#include + +typedef enum { + PWRSINK_AUDIO_PCM = 0, + PWRSINK_AUDIO_MP3, + PWRSINK_AUDIO_AAC, + + PWRSINK_AUDIO_LAST = PWRSINK_AUDIO_AAC, + PWRSINK_AUDIO_INVALID +} pwrsink_audio_id_type; + +struct pwr_sink_audio { + unsigned volume; + unsigned percent; +}; + +typedef enum { + PWRSINK_SYSTEM_LOAD = 0, + PWRSINK_AUDIO, + PWRSINK_BACKLIGHT, + PWRSINK_LED_BUTTON, + PWRSINK_LED_KEYBOARD, + PWRSINK_GP_CLK, + PWRSINK_BLUETOOTH, + PWRSINK_CAMERA, + PWRSINK_SDCARD, + PWRSINK_VIDEO, + PWRSINK_WIFI, + + PWRSINK_LAST = PWRSINK_WIFI, + PWRSINK_INVALID +} pwrsink_id_type; + +struct pwr_sink { + pwrsink_id_type id; + unsigned ua_max; + unsigned percent_util; +}; + +struct pwr_sink_platform_data { + unsigned num_sinks; + struct pwr_sink *sinks; + int (*suspend_late)(struct platform_device *, pm_message_t state); + int (*resume_early)(struct platform_device *); + void (*suspend_early)(struct early_suspend *); + void (*resume_late)(struct early_suspend *); +}; + +#ifndef CONFIG_HTC_PWRSINK +static inline int htc_pwrsink_set(pwrsink_id_type id, unsigned percent) +{ + printk(KERN_DEBUG "%s:STUB!\n", __func__); + return 0; +} +static inline int htc_pwrsink_audio_set(pwrsink_audio_id_type id, + unsigned percent_utilized) { return 0; } +static inline int htc_pwrsink_audio_volume_set( + pwrsink_audio_id_type id, unsigned volume) { return 0; } +static inline int htc_pwrsink_audio_path_set(unsigned path) { return 0; } +#else +extern int htc_pwrsink_set(pwrsink_id_type id, unsigned percent); +extern int htc_pwrsink_audio_set(pwrsink_audio_id_type id, + unsigned percent_utilized); +extern int htc_pwrsink_audio_volume_set(pwrsink_audio_id_type id, + unsigned volume); +extern int htc_pwrsink_audio_path_set(unsigned path); +#endif + +#endif diff --git a/arch/arm/mach-msm/include/mach/trout_pwrsink.h b/arch/arm/mach-msm/include/mach/trout_pwrsink.h deleted file mode 100644 index 265b329ac0cf8..0000000000000 --- a/arch/arm/mach-msm/include/mach/trout_pwrsink.h +++ /dev/null @@ -1,58 +0,0 @@ -/* include/asm/mach-msm/trout_pwrsink.h - * - * Copyright (C) 2007 Google, Inc. - * Copyright (C) 2008 HTC Corporation. - * - * 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 _ARCH_ARM_MACH_MSM_TROUT_PWRSINK_H_ -#define _ARCH_ARM_MACH_MSM_TROUT_PWRSINK_H_ - -typedef enum { - PWRSINK_SYSTEM_LOAD = 0, - PWRSINK_AUDIO, - PWRSINK_BACKLIGHT, - PWRSINK_LED_BUTTON, - PWRSINK_LED_KEYBOARD, - PWRSINK_GP_CLK, - PWRSINK_BLUETOOTH, - PWRSINK_CAMERA, - PWRSINK_SDCARD, - PWRSINK_VIDEO, - PWRSINK_WIFI, - - PWRSINK_LAST = PWRSINK_WIFI, - PWRSINK_INVALID -} pwrsink_id_type; - -struct pwr_sink { - pwrsink_id_type id; - unsigned ua_max; - unsigned percent_util; -}; - -struct pwr_sink_platform_data { - unsigned num_sinks; - struct pwr_sink *sinks; -}; - -#ifndef CONFIG_TROUT_PWRSINK -static inline int trout_pwrsink_set(pwrsink_id_type id, unsigned percent) -{ -printk("%s:STUB!\n", __func__); - return 0; -} -#else -extern int trout_pwrsink_set(pwrsink_id_type id, unsigned percent); -#endif - -#endif - From f9a31e5a0e04f110a343f3abe06aecb34a864b54 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Wed, 18 Feb 2009 10:45:48 -0800 Subject: [PATCH 0272/2556] [ARM] msm: audio: Switch driver to new htc_pwrsink reporting Signed-off-by: San Mehat --- arch/arm/mach-msm/qdsp5/audio_out.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/qdsp5/audio_out.c b/arch/arm/mach-msm/qdsp5/audio_out.c index de6e01563e9cc..e6fcacbf4223a 100644 --- a/arch/arm/mach-msm/qdsp5/audio_out.c +++ b/arch/arm/mach-msm/qdsp5/audio_out.c @@ -38,7 +38,7 @@ #include #include -#include +#include #include "evlog.h" @@ -260,7 +260,7 @@ static int audio_enable(struct audio *audio) } audio->enabled = 1; - trout_pwrsink_set(PWRSINK_AUDIO, 100); + htc_pwrsink_set(PWRSINK_AUDIO, 100); return 0; } @@ -695,7 +695,7 @@ static int audio_release(struct inode *inode, struct file *file) audio_flush(audio); audio->opened = 0; mutex_unlock(&audio->lock); - trout_pwrsink_set(PWRSINK_AUDIO, 0); + htc_pwrsink_set(PWRSINK_AUDIO, 0); return 0; } From ca39a7398de4a557bb62e0599c7ce3f44a26c2c2 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 17 Feb 2009 14:42:56 -0800 Subject: [PATCH 0273/2556] [ARM]: trout: Switch from trout_pwrsink -> htc_pwrsink Signed-off-by: San Mehat --- arch/arm/mach-msm/board-trout-gpio.c | 6 +++--- arch/arm/mach-msm/board-trout-mmc.c | 6 +++--- arch/arm/mach-msm/board-trout-panel.c | 10 ++-------- arch/arm/mach-msm/board-trout.c | 12 ++++++++---- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/arch/arm/mach-msm/board-trout-gpio.c b/arch/arm/mach-msm/board-trout-gpio.c index d1caedb8de610..4446a922e0b9e 100644 --- a/arch/arm/mach-msm/board-trout-gpio.c +++ b/arch/arm/mach-msm/board-trout-gpio.c @@ -23,7 +23,7 @@ #include #include -#include +#include #include "board-trout.h" @@ -70,10 +70,10 @@ static void update_pwrsink(unsigned gpio, unsigned on) { switch(gpio) { case TROUT_GPIO_UI_LED_EN: - trout_pwrsink_set(PWRSINK_LED_BUTTON, on ? 100 : 0); + htc_pwrsink_set(PWRSINK_LED_BUTTON, on ? 100 : 0); break; case TROUT_GPIO_QTKEY_LED_EN: - trout_pwrsink_set(PWRSINK_LED_KEYBOARD, on ? 100 : 0); + htc_pwrsink_set(PWRSINK_LED_KEYBOARD, on ? 100 : 0); break; } } diff --git a/arch/arm/mach-msm/board-trout-mmc.c b/arch/arm/mach-msm/board-trout-mmc.c index 789a5135caed7..4054c5ddcdbcf 100644 --- a/arch/arm/mach-msm/board-trout-mmc.c +++ b/arch/arm/mach-msm/board-trout-mmc.c @@ -15,7 +15,7 @@ #include #include -#include +#include #include @@ -264,11 +264,11 @@ int trout_wifi_power(int on) rc = vreg_enable(vreg_wifi_osc); if (rc) return rc; - trout_pwrsink_set(PWRSINK_WIFI, 70); + htc_pwrsink_set(PWRSINK_WIFI, 70); } else { config_gpio_table(wifi_off_gpio_table, ARRAY_SIZE(wifi_off_gpio_table)); - trout_pwrsink_set(PWRSINK_WIFI, 0); + htc_pwrsink_set(PWRSINK_WIFI, 0); } gpio_set_value( TROUT_GPIO_MAC_32K_EN, on); mdelay(100); diff --git a/arch/arm/mach-msm/board-trout-panel.c b/arch/arm/mach-msm/board-trout-panel.c index 8b350d1a382c9..900b8b1a6f76f 100644 --- a/arch/arm/mach-msm/board-trout-panel.c +++ b/arch/arm/mach-msm/board-trout-panel.c @@ -16,9 +16,7 @@ #include #include -#ifdef CONFIG_TROUT_PWRSINK -#include -#endif +#include #include "board-trout.h" #include "proc_comm.h" @@ -35,9 +33,7 @@ static DEFINE_MUTEX(trout_backlight_lock); static void trout_set_backlight_level(uint8_t level) { -#ifdef CONFIG_TROUT_PWRSINK unsigned percent = ((int)level * 100) / 255; -#endif if (trout_new_backlight) { unsigned long flags; @@ -87,9 +83,7 @@ static void trout_set_backlight_level(uint8_t level) clk_disable(gp_clk); } } -#ifdef CONFIG_TROUT_PWRSINK - trout_pwrsink_set(PWRSINK_BACKLIGHT, percent); -#endif + htc_pwrsink_set(PWRSINK_BACKLIGHT, percent); } #define MDDI_CLIENT_CORE_BASE 0x108000 diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 6fbb591dfb87e..7bcdffb94e53f 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -63,7 +63,7 @@ #include #include #include -#include +#include #ifdef CONFIG_HTC_HEADSET #include #endif @@ -591,12 +591,16 @@ static struct pwr_sink trout_pwrsink_table[] = { static struct pwr_sink_platform_data trout_pwrsink_data = { .num_sinks = ARRAY_SIZE(trout_pwrsink_table), .sinks = trout_pwrsink_table, + .suspend_late = NULL, + .resume_early = NULL, + .suspend_early = NULL, + .resume_late = NULL, }; static struct platform_device trout_pwr_sink = { - .name = "trout_pwrsink", + .name = "htc_pwrsink", .id = -1, - .dev = { + .dev = { .platform_data = &trout_pwrsink_data, }, }; @@ -712,7 +716,7 @@ static struct platform_device *devices[] __initdata = { #ifdef CONFIG_HTC_HEADSET &trout_h2w, #endif -#if defined(CONFIG_TROUT_PWRSINK) +#ifdef CONFIG_HTC_PWRSINK &trout_pwr_sink, #endif &trout_snd, From 50c2d80377a092b6706cde6caadd4ae0c7d52197 Mon Sep 17 00:00:00 2001 From: Thomas Tsai Date: Tue, 17 Feb 2009 15:14:34 -0800 Subject: [PATCH 0274/2556] [ARM] msm: board: Add new camera sensor and camera platform device interface headers Signed-off-by: San Mehat --- arch/arm/mach-msm/include/mach/board.h | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index 318caa5a50230..f8f084aba54ac 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -31,10 +31,27 @@ struct msm_acpu_clock_platform_data unsigned long wait_for_irq_khz; }; -struct msm_camera_device_platform_data { +struct msm_camera_sensor_info { int sensor_reset; int sensor_pwd; int vcm_pwd; + const char *sensor_name; +}; + + +struct msm_camera_platform_data{ + void (*camera_gpio_on) (void); + void (*camera_gpio_off)(void); + uint8_t snum; + struct msm_camera_sensor_info *sinfo; +}; + +struct msm_camera_device_platform_data{ + int sensor_reset; + int sensor_pwd; + int vcm_pwd; + void (*config_gpio_on) (void); + void (*config_gpio_off)(void); }; struct snd_endpoint { From a2a3e0c8b76110a4f7f1a24b95702dc3206fbda7 Mon Sep 17 00:00:00 2001 From: Thomas Tsai Date: Tue, 17 Feb 2009 15:16:30 -0800 Subject: [PATCH 0275/2556] [ARM] msm: vibrator: Initial support for MSM PMIC vibrator Signed-off-by: San Mehat --- arch/arm/mach-msm/msm_vibrator.c | 137 +++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 arch/arm/mach-msm/msm_vibrator.c diff --git a/arch/arm/mach-msm/msm_vibrator.c b/arch/arm/mach-msm/msm_vibrator.c new file mode 100644 index 0000000000000..f4da4363aa988 --- /dev/null +++ b/arch/arm/mach-msm/msm_vibrator.c @@ -0,0 +1,137 @@ +/* include/asm/mach-msm/htc_pwrsink.h + * + * Copyright (C) 2008 HTC Corporation. + * Copyright (C) 2007 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 <../../../drivers/staging/android/timed_output.h> +#include + +#include + +#define PM_LIBPROG 0x30000061 +#if (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225) +#define PM_LIBVERS 0xfb837d0b +#else +#define PM_LIBVERS 0x10001 +#endif + +#define HTC_PROCEDURE_SET_VIB_ON_OFF 21 +#define PMIC_VIBRATOR_LEVEL (3000) + +static struct work_struct work_vibrator_on; +static struct work_struct work_vibrator_off; +static struct hrtimer vibe_timer; + +static void set_pmic_vibrator(int on) +{ + static struct msm_rpc_endpoint *vib_endpoint; + struct set_vib_on_off_req { + struct rpc_request_hdr hdr; + uint32_t data; + } req; + + if (!vib_endpoint) { + vib_endpoint = msm_rpc_connect(PM_LIBPROG, PM_LIBVERS, 0); + if (IS_ERR(vib_endpoint)) { + printk(KERN_ERR "init vib rpc failed!\n"); + vib_endpoint = 0; + return; + } + } + + + if (on) + req.data = cpu_to_be32(PMIC_VIBRATOR_LEVEL); + else + req.data = cpu_to_be32(0); + + msm_rpc_call(vib_endpoint, HTC_PROCEDURE_SET_VIB_ON_OFF, &req, + sizeof(req), 5 * HZ); +} + +static void pmic_vibrator_on(struct work_struct *work) +{ + set_pmic_vibrator(1); +} + +static void pmic_vibrator_off(struct work_struct *work) +{ + set_pmic_vibrator(0); +} + +static void timed_vibrator_on(struct timed_output_dev *sdev) +{ + schedule_work(&work_vibrator_on); +} + +static void timed_vibrator_off(struct timed_output_dev *sdev) +{ + schedule_work(&work_vibrator_off); +} + +static void vibrator_enable(struct timed_output_dev *dev, int value) +{ + hrtimer_cancel(&vibe_timer); + + if (value == 0) + timed_vibrator_off(dev); + else { + value = (value > 15000 ? 15000 : value); + + timed_vibrator_on(dev); + + hrtimer_start(&vibe_timer, + ktime_set(value / 1000, (value % 1000) * 1000000), + HRTIMER_MODE_REL); + } +} + +static int vibrator_get_time(struct timed_output_dev *dev) +{ + if (hrtimer_active(&vibe_timer)) { + ktime_t r = hrtimer_get_remaining(&vibe_timer); + return r.tv.sec * 1000 + r.tv.nsec / 1000000; + } else + return 0; +} + +static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) +{ + timed_vibrator_off(NULL); + return HRTIMER_NORESTART; +} + +static struct timed_output_dev pmic_vibrator = { + .name = "vibrator", + .get_time = vibrator_get_time, + .enable = vibrator_enable, +}; + +void __init msm_init_pmic_vibrator(void) +{ + INIT_WORK(&work_vibrator_on, pmic_vibrator_on); + INIT_WORK(&work_vibrator_off, pmic_vibrator_off); + + hrtimer_init(&vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + vibe_timer.function = vibrator_timer_func; + + timed_output_dev_register(&pmic_vibrator); +} + +MODULE_DESCRIPTION("timed output pmic vibrator device"); +MODULE_LICENSE("GPL"); + From a0357504207f40598c259a7a76fddd59eb8ec48a Mon Sep 17 00:00:00 2001 From: Farmer Tseng Date: Tue, 17 Feb 2009 15:26:25 -0800 Subject: [PATCH 0276/2556] [ARM] elan8232: Enhance ELAN touch support for multiple panels Signed-off-by: San Mehat --- drivers/input/touchscreen/elan8232_i2c.c | 751 +++++++++++++++++------ include/linux/elan_i2c.h | 17 + 2 files changed, 567 insertions(+), 201 deletions(-) create mode 100644 include/linux/elan_i2c.h diff --git a/drivers/input/touchscreen/elan8232_i2c.c b/drivers/input/touchscreen/elan8232_i2c.c index 7c07d8431621c..4870b22b11539 100644 --- a/drivers/input/touchscreen/elan8232_i2c.c +++ b/drivers/input/touchscreen/elan8232_i2c.c @@ -21,15 +21,16 @@ #include #include #include +#include +#include +#include -#define EKT8232NAME "elan-touch" -#define ELAN_TS_ABS_X_MIN 32 -#define ELAN_TS_ABS_X_MAX 352 -#define ELAN_TS_ABS_Y_MIN 32 -#define ELAN_TS_ABS_Y_MAX 544 +static const char EKT8232NAME[] = "elan-touch"; + #define ELAN_TS_FUZZ 0 #define ELAN_TS_FLAT 0 +#define IDX_PACKET_SIZE 9 enum { STATE_DEEP_SLEEP = 0, @@ -39,33 +40,163 @@ enum { read_cmd_packet = 0x53, write_cmd_packet = 0x54, hello_packet = 0x55, + enable_int = 0xa6, + disable_int = 0x56, idx_coordinate_packet = 0x5a, }; +enum { + idx_finger_width = 7, + idx_finger_state = 8, +}; + +static struct workqueue_struct *elan_wq; + static struct ekt8232_data { + int intr_gpio; int use_irq; + /* delete when finish migration */ + int fw_ver; struct hrtimer timer; struct work_struct work; struct i2c_client *client; struct input_dev *input; wait_queue_head_t wait; + int (*power)(int on); + struct early_suspend early_suspend; } ekt_data; -/* Though the writing-clients suggest to use SMBus level - * communication instead of plain i2c communication. - * But msm_i2c_algo registers master_xfer only. - */ +#ifdef CONFIG_HAS_EARLYSUSPEND +static void elan_ts_early_suspend(struct early_suspend *h); +static void elan_ts_late_resume(struct early_suspend *h); +#endif + +static ssize_t touch_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + + sprintf(buf, "%s_%#x\n", EKT8232NAME, ekt_data.fw_ver); + ret = strlen(buf) + 1; + + return ret; +} + +static DEVICE_ATTR(vendor, 0444, touch_vendor_show, NULL); + +static struct kobject *android_touch_kobj; + +static int touch_sysfs_init(void) +{ + int ret ; + + android_touch_kobj = kobject_create_and_add("android_touch", NULL) ; + if (android_touch_kobj == NULL) { + printk(KERN_INFO + "touch_sysfs_init: subsystem_register failed\n"); + ret = -ENOMEM; + goto err; + } + + ret = sysfs_create_file(android_touch_kobj, &dev_attr_vendor.attr); + if (ret) { + printk(KERN_INFO + "touch_sysfs_init: sysfs_create_group failed\n"); + goto err4; + } + + return 0 ; +err4: + kobject_del(android_touch_kobj); +err: + return ret ; +} + +static int ekt8232_detect_int_level(void) +{ + unsigned v; + v = gpio_get_value(ekt_data.intr_gpio); + /* printk("ekt8232_detect_int_level: v = %0x\n", v); */ + return v; +} + +static int __ekt8232_poll(struct i2c_client *client) +{ + int status = 0, retry = 10; + + do { + status = ekt8232_detect_int_level(); + dev_dbg(&client->dev, "%s: status = %d\n", __func__, status); + retry--; + mdelay(20); + } while (status == 1 && retry > 0); + + dev_dbg(&client->dev, "%s: poll interrupt status %s\n", + __func__, status == 1 ? "high" : "low"); + return (status == 0 ? 0 : -ETIMEDOUT); +} + +static int ekt8232_poll(struct i2c_client *client) +{ + return __ekt8232_poll(client); +} + +static int ekt8232_get_data(struct i2c_client *client, uint8_t *cmd, + uint8_t *buf, size_t size, int sleep) +{ + int rc; + unsigned time_out = msecs_to_jiffies(10); + + dev_dbg(&client->dev, "%s: enter.\n", __func__); + + if (buf == NULL) + return -EINVAL; + + if ((i2c_master_send(client, cmd, 4)) != 4) { + dev_err(&client->dev, + "%s: i2c_master_send failed\n", __func__); + return -EINVAL; + } + + if (sleep == 1) { + rc = wait_event_timeout(ekt_data.wait, + i2c_master_recv(client, buf, size) == size && + buf[0] == cmd_reponse_packet, time_out); + if (rc == 0) { + dev_err(&client->dev, + "%s: i2c_master_recv failed\n", __func__); + return -ETIMEDOUT; + } + } else { + rc = ekt8232_poll(client); + if (rc < 0) + return -EINVAL; + else { + if (i2c_master_recv(client, buf, size) != size || + buf[0] != cmd_reponse_packet) + return -EINVAL; + } + } + + return 0; +} static int __hello_packet_handler(struct i2c_client *client) { int rc; uint8_t buf_recv[4] = { 0 }; + rc = ekt8232_poll(client); + if (rc < 0) { + dev_err(&client->dev, "%s: failed!\n", __func__); + return -EINVAL; + } + rc = i2c_master_recv(client, buf_recv, 4); if (rc != 4) { dev_err(&client->dev, "%s: get hello packet failed!, rc = %d\n", - __FUNCTION__, rc); + __func__, rc); return rc; } else { int i; @@ -88,20 +219,18 @@ static int __fw_packet_handler(struct i2c_client *client) uint8_t cmd[] = { read_cmd_packet, 0x00, 0x00, 0x01 }; uint8_t buf_recv[4] = { 0 }; - rc = i2c_master_send(client, cmd, sizeof(cmd)); - if (rc != sizeof(cmd)) { - dev_err(&client->dev, - "%s: i2c_master_send failed\n", __FUNCTION__); - return -EINVAL; - } - msleep(50); + rc = ekt8232_get_data(client, cmd, buf_recv, 4, 0); + if (rc < 0) + return rc; - rc = i2c_master_recv((struct i2c_client *)client, buf_recv, 4); major = ((buf_recv[1] & 0x0f) << 4) | ((buf_recv[2] & 0xf0) >> 4); minor = ((buf_recv[2] & 0x0f) << 4) | ((buf_recv[3] & 0xf0) >> 4); - dev_dbg(&client->dev, - "%s: firmware version: %d.%d\n", __FUNCTION__, major, minor); + /* delete after migration */ + ekt_data.fw_ver = major << 8 | minor; + + printk(KERN_INFO "%s: firmware version: 0x%x\n", + __func__, ekt_data.fw_ver); return 0; } @@ -110,7 +239,7 @@ static int __set_report_type(struct i2c_client *client) return 0; } -static int __parse_xy(uint8_t *data, uint16_t *x, uint16_t *y) +static inline int ekt8232_parse_xy(uint8_t *data, uint16_t *x, uint16_t *y) { *x = (data[0] & 0xf0); *x <<= 4; @@ -136,129 +265,291 @@ static int ekt8232_ts_init(struct i2c_client *client) rc = __hello_packet_handler(client); if (rc < 0) goto hand_shake_failed; - dev_dbg(&client->dev, "%s: hello packet got.\n", __FUNCTION__); + dev_dbg(&client->dev, "%s: hello packet got.\n", __func__); rc = __fw_packet_handler(client); if (rc < 0) goto hand_shake_failed; - dev_dbg(&client->dev, "%s: firmware checking done.\n", __FUNCTION__); + dev_dbg(&client->dev, "%s: firmware checking done.\n", __func__); rc = __set_report_type(client); if (rc < 0) goto hand_shake_failed; dev_dbg(&client->dev, - "%s: channging operating mode done.\n", __FUNCTION__); + "%s: channging operating mode done.\n", __func__); + + if (ekt_data.fw_ver == 0x103) { + uint8_t cmd[4] = {0x5c, 0x10, 0x00, 0x01}; + if ((i2c_master_send(client, cmd, 4)) != 4) { + dev_err(&client->dev, + "%s: set adc failed\n", __func__); + } + cmd[0] = 0x54; + cmd[0] = 0x43; + cmd[0] = 0x00; + cmd[0] = 0x01; + if ((i2c_master_send(client, cmd, 4)) != 4) { + dev_err(&client->dev, + "%s: set gain failed\n", __func__); + } + } hand_shake_failed: return rc; } -static void ekt8232_work_func(struct work_struct *work) +static int ekt8232_set_power_state(struct i2c_client *client, int state) { - int rc; - uint8_t buf[8] = { 0 }; - struct i2c_client *client = ekt_data.client; + uint8_t cmd[] = {write_cmd_packet, 0x50, 0x00, 0x01}; + + dev_dbg(&client->dev, "%s: enter.\n", __func__); + + cmd[1] |= (state << 3); + + dev_dbg(&client->dev, + "dump cmd: %02x, %02x, %02x, %02x\n", + cmd[0], cmd[1], cmd[2], cmd[3]); - rc = i2c_master_recv(client, buf, 8); - if (rc != 8) { + if ((i2c_master_send(client, cmd, sizeof(cmd))) != sizeof(cmd)) { dev_err(&client->dev, - "%s: i2c_master_recv error?! \n", __FUNCTION__); - goto done; + "%s: i2c_master_send failed\n", __func__); + return -EINVAL; } + return 0; +} + +static int ekt8232_get_power_state(struct i2c_client *client) +{ + int rc = 0; + uint8_t cmd[] = { read_cmd_packet, 0x50, 0x00, 0x01 }; + uint8_t buf[4], power_state; + + rc = ekt8232_get_data(client, cmd, buf, 4, 0); + if (rc) + return rc; + else { + power_state = buf[1]; + dev_dbg(&client->dev, "dump repsponse: %0x\n", power_state); + + power_state = (power_state & STATE_MASK) >> 3; + dev_dbg(&client->dev, "power state = %s\n", + power_state == STATE_DEEP_SLEEP ? + "Deep Sleep" : "Normal/Idle"); + return power_state; + } +} + +static int ekt8232_recv_data(struct i2c_client *client, uint8_t *buf) +{ + int rc, bytes_to_recv = IDX_PACKET_SIZE; + int retry = 5; + + if (ekt_data.fw_ver == 0x101) + bytes_to_recv = 8; + + if (buf == NULL) + return -EINVAL; + + memset(buf, 0, bytes_to_recv); + rc = i2c_master_recv(client, buf, bytes_to_recv); + + if (rc != bytes_to_recv) { + dev_err(&client->dev, + "%s: i2c_master_recv error?! \n", __func__); + /* power off level shift */ + ekt_data.power(0); + msleep(5); + /* power on level shift */ + ekt_data.power(1); + /* re-initial */ + if (ekt_data.fw_ver > 0x101) { + msleep(100); + rc = ekt8232_ts_init(client); + } else { + do { + rc = ekt8232_set_power_state(client, + STATE_NORMAL); + + rc = ekt8232_get_power_state(client); + if (rc != STATE_NORMAL) + dev_err(&client->dev, + "%s: wake up tp failed! \ + err = %d\n", + __func__, rc); + else + break; + } while (--retry); + } + if (ekt8232_detect_int_level() == 0) + queue_work(elan_wq, &ekt_data.work); + return -EINVAL; + } + + return rc; +} + +static inline void ekt8232_parse_width(uint8_t data, uint8_t *w1, uint8_t *w2) +{ + *w1 = *w2 = 0; + *w1 = (data & 0xf0) >> 4; + *w2 = data & 0x0f; +} + +static void ekt8232_report_data(struct i2c_client *client, uint8_t *buf) +{ + static unsigned report_time; + unsigned report_time2; + switch (buf[0]) { case idx_coordinate_packet: { uint16_t x1, x2, y1, y2; - uint8_t finger_stat; + uint8_t finger_stat, w1 = 1, w2 = 1; + + ekt8232_parse_xy(&buf[1], &x1, &y1); + if (ekt_data.fw_ver == 0x101) { + finger_stat = buf[7] >> 1; + } else { + ekt8232_parse_width(buf[idx_finger_width], &w1, &w2); + finger_stat = buf[idx_finger_state] >> 1; + } - __parse_xy(&buf[1], &x1, &y1); - finger_stat = buf[7] >> 1; - dev_dbg(&client->dev, - "x1 = %d, y1 = %d, finger status = %d\n", - x1, y1, finger_stat); if (finger_stat != 0) { input_report_abs(ekt_data.input, ABS_X, x1); - input_report_abs(ekt_data.input, ABS_Y, - ELAN_TS_ABS_Y_MAX - y1); + if (ekt_data.fw_ver == 0x101) + input_report_abs(ekt_data.input, ABS_Y, + (544 - 1) - y1); + else + input_report_abs(ekt_data.input, ABS_Y, y1); + /* only report finger width at y */ + input_report_abs(ekt_data.input, ABS_TOOL_WIDTH, w2); } + + dev_dbg(&client->dev, + "x1 = %d, y1 = %d, \ + w1 = %d, w2 = %d, finger status = %d\n", + x1, y1, w1, w2, finger_stat); + + input_report_abs(ekt_data.input, ABS_PRESSURE, 100); input_report_key(ekt_data.input, BTN_TOUCH, finger_stat); input_report_key(ekt_data.input, BTN_2, finger_stat == 2); if (finger_stat > 1) { - __parse_xy(&buf[4], &x2, &y2); + ekt8232_parse_xy(&buf[4], &x2, &y2); dev_dbg(&client->dev, "x2 = %d, y2 = %d\n", x2, y2); input_report_abs(ekt_data.input, ABS_HAT0X, x2); - input_report_abs(ekt_data.input, ABS_HAT0Y, - ELAN_TS_ABS_Y_MAX - y2); + input_report_abs(ekt_data.input, ABS_HAT0Y, y2); } input_sync(ekt_data.input); break; } default: dev_err(&client->dev, - "%s: Unknown packet type: %0x\n", - __FUNCTION__, buf[0]); + "%s: Unknown packet type: %0x\n", __func__, buf[0]); break; } -done: - if (ekt_data.use_irq) - enable_irq(ekt_data.client->irq); + report_time2 = jiffies; + dev_dbg(&client->dev, + "report time = %d\n", + jiffies_to_msecs(report_time2 - report_time)); + + report_time = report_time2; + +} + +static void ekt8232_work_func(struct work_struct *work) +{ + int rc; + uint8_t buf[IDX_PACKET_SIZE] = { 0 }; + struct i2c_client *client = ekt_data.client; + + /* dev_dbg(&client->dev, "%s: enter. \n", __func__); */ + + /* this means that we have already serviced it */ + if (ekt8232_detect_int_level()) + return; + + rc = ekt8232_recv_data(client, buf); + if (rc < 0) + return; + + ekt8232_report_data(client, buf); } static irqreturn_t ekt8232_ts_interrupt(int irq, void *dev_id) { - disable_irq(irq); - schedule_work(&ekt_data.work); + /* the queue_work has spin_lock protection */ + /* disable_irq(irq); */ + queue_work(elan_wq, &ekt_data.work); return IRQ_HANDLED; } static enum hrtimer_restart ekt8232_ts_timer_func(struct hrtimer *timer) { - schedule_work(&ekt_data.work); - hrtimer_start(&ekt_data.timer, ktime_set(0, 12500000), + queue_work(elan_wq, &ekt_data.work); + hrtimer_start(&ekt_data.timer, + ktime_set(0, 12500000), HRTIMER_MODE_REL); return HRTIMER_NORESTART; } -static int __init ekt8232_register_input(struct input_dev *input) +static int ekt8232_register_interrupt(struct i2c_client *client) { - dev_dbg(&input->dev, "%s: enter\n", __FUNCTION__); - - input->name = EKT8232NAME; - input->id.bustype = BUS_I2C; - - input->evbit[0] = - BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); - - input_set_abs_params(input, ABS_X, - ELAN_TS_ABS_X_MIN, ELAN_TS_ABS_X_MAX, - ELAN_TS_FUZZ, ELAN_TS_FLAT); - input_set_abs_params(input, ABS_Y, - ELAN_TS_ABS_Y_MIN, ELAN_TS_ABS_Y_MAX, - ELAN_TS_FUZZ, ELAN_TS_FLAT); - input_set_abs_params(input, ABS_HAT0X, - ELAN_TS_ABS_X_MIN, ELAN_TS_ABS_X_MAX, - ELAN_TS_FUZZ, ELAN_TS_FLAT); - input_set_abs_params(input, ABS_HAT0Y, - ELAN_TS_ABS_Y_MIN, ELAN_TS_ABS_Y_MAX, - ELAN_TS_FUZZ, ELAN_TS_FLAT); - - return input_register_device(input); + int err = 0; + + if (client->irq) { + ekt_data.use_irq = 1; + + err = request_irq(client->irq, ekt8232_ts_interrupt, 0, + EKT8232NAME, &ekt_data); + if (err < 0) { + dev_err(&client->dev, + "%s(%s): Can't allocate irq %d\n", + __FILE__, __func__, client->irq); + ekt_data.use_irq = 0; + } + } + + if (!ekt_data.use_irq) { + hrtimer_init(&ekt_data.timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ekt_data.timer.function = ekt8232_ts_timer_func; + hrtimer_start(&ekt_data.timer, ktime_set(1, 0), + HRTIMER_MODE_REL); + } + + dev_dbg(&client->dev, + "elan starts in %s mode.\n", + ekt_data.use_irq == 1 ? "interrupt":"polling"); + return 0; } static int ekt8232_probe( struct i2c_client *client, const struct i2c_device_id *id) { - int err = 0, retry = 10; + int err = 0; + struct elan_i2c_platform_data *pdata; + int x_max, y_max; + uint8_t x_resolution_cmd[] = { read_cmd_packet, 0x60, 0x00, 0x01 }; + uint8_t y_resolution_cmd[] = { read_cmd_packet, 0x63, 0x00, 0x01 }; + uint8_t buf_recv[4] = { 0 }; + elan_wq = create_singlethread_workqueue("elan_wq"); + if (!elan_wq) { + err = -ENOMEM; + goto fail; + } + + printk(KERN_INFO "ekt8232_probe enter.\n"); + dev_dbg(&client->dev, "ekt8232_probe enter.\n"); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_err(&client->dev, "No supported i2c func what we need?!!\n"); - return -ENOTSUPP; + err = -ENOTSUPP; + goto fail; } ekt_data.client = client; @@ -268,58 +559,130 @@ static int ekt8232_probe( init_waitqueue_head(&ekt_data.wait); ekt_data.input = input_allocate_device(); - if (ekt_data.input == NULL) - return -ENOMEM; + if (ekt_data.input == NULL) { + err = -ENOMEM; + goto fail; + } - /* Actually, we are missing the first time interrupt here. - * So, we need to fake we are getting the interrupt for hello packet. - */ - do { - err = ekt8232_ts_init(client); - if (err >= 0) - break; - msleep(100); - } while (retry--); + pdata = client->dev.platform_data; + if (likely(pdata != NULL)) { + ekt_data.intr_gpio = + ((struct elan_i2c_platform_data *)pdata)->intr_gpio; + ekt_data.power = + ((struct elan_i2c_platform_data *)pdata)->power; + ekt_data.power(1); + dev_info(&client->dev, "touch panel is powered on. \n"); + mdelay(500); /* elan will be ready after about 500 ms */ + } else { + dev_err(&client->dev, "without platform data??!!\n"); + } + err = ekt8232_ts_init(client); if (err < 0) { - dev_dbg(&client->dev, - "looks like it's not Elan, so..i'll quit\n"); + printk(KERN_INFO "looks like it's not Elan, so..i'll quit\n"); err = -ENODEV; goto fail; } - err = ekt8232_register_input(ekt_data.input); - if (err < 0) { - dev_err(&client->dev, - "%s: register to input system failed, err = %d\n", - __FUNCTION__, err); - goto fail; + if (pdata) { + while (pdata->version > ekt_data.fw_ver) { + printk(KERN_INFO "ekt8232_probe: old tp detected, " + "panel version = 0x%x\n", + ekt_data.fw_ver); + pdata++; + } } - /* CPLD does not accept configuring interrupt type */ - if (client->irq) { - err = request_irq(client->irq, ekt8232_ts_interrupt, 0, - EKT8232NAME, &ekt_data); + printk(KERN_INFO "ekt8232_register_input\n"); + + ekt_data.input->name = EKT8232NAME; + ekt_data.input->id.bustype = BUS_I2C; + set_bit(EV_SYN, ekt_data.input->evbit); + set_bit(EV_KEY, ekt_data.input->evbit); + set_bit(BTN_TOUCH, ekt_data.input->keybit); + set_bit(BTN_2, ekt_data.input->keybit); + set_bit(EV_ABS, ekt_data.input->evbit); + + if (ekt_data.fw_ver >= 0x104) { + err = ekt8232_get_data(ekt_data.client, x_resolution_cmd, + buf_recv, 4, 0); if (err < 0) { dev_err(&client->dev, - "%s(%s): Can't allocate irq %d\n", - __FILE__, __FUNCTION__, client->irq); - ekt_data.use_irq = 0; - free_irq(client->irq, &ekt_data); - } else { - ekt_data.use_irq = 1; + "%s: get x resolution failed, err = %d\n", + __func__, err); + goto fail; } + + x_max = ((buf_recv[3] & 0xf0) << 4) | ((buf_recv[2] & 0xff)); + printk(KERN_INFO "ekt8232_probe: x_max: %d\n", x_max); + + err = ekt8232_get_data(ekt_data.client, y_resolution_cmd, + buf_recv, 4, 0); + if (err < 0) { + dev_err(&client->dev, + "%s: get y resolution failed, err = %d\n", + __func__, err); + goto fail; + } + + y_max = ((buf_recv[3] & 0xf0) << 4) | ((buf_recv[2] & 0xff)); + printk(KERN_INFO "ekt8232_probe: y_max: %d\n", y_max); + input_set_abs_params(ekt_data.input, ABS_X, + pdata->abs_x_min, x_max, + ELAN_TS_FUZZ, ELAN_TS_FLAT); + input_set_abs_params(ekt_data.input, ABS_Y, + pdata->abs_y_min, y_max, + ELAN_TS_FUZZ, ELAN_TS_FLAT); + input_set_abs_params(ekt_data.input, ABS_HAT0X, + pdata->abs_x_min, x_max, + ELAN_TS_FUZZ, ELAN_TS_FLAT); + input_set_abs_params(ekt_data.input, ABS_HAT0Y, + pdata->abs_y_min, y_max, + ELAN_TS_FUZZ, ELAN_TS_FLAT); + } else { + input_set_abs_params(ekt_data.input, ABS_X, + pdata->abs_x_min, pdata->abs_x_max, + ELAN_TS_FUZZ, ELAN_TS_FLAT); + input_set_abs_params(ekt_data.input, ABS_Y, + pdata->abs_y_min, pdata->abs_y_max, + ELAN_TS_FUZZ, ELAN_TS_FLAT); + input_set_abs_params(ekt_data.input, ABS_HAT0X, + pdata->abs_x_min, pdata->abs_x_max, + ELAN_TS_FUZZ, ELAN_TS_FLAT); + input_set_abs_params(ekt_data.input, ABS_HAT0Y, + pdata->abs_y_min, pdata->abs_y_max, + ELAN_TS_FUZZ, ELAN_TS_FLAT); + input_set_abs_params(ekt_data.input, ABS_PRESSURE, 0, 255, + ELAN_TS_FUZZ, ELAN_TS_FLAT); + input_set_abs_params(ekt_data.input, ABS_TOOL_WIDTH, 1, 8, + 1, ELAN_TS_FLAT); } - if (!ekt_data.use_irq) { - hrtimer_init(&ekt_data.timer, - CLOCK_MONOTONIC, HRTIMER_MODE_REL); - ekt_data.timer.function = ekt8232_ts_timer_func; - hrtimer_start(&ekt_data.timer, ktime_set(1, 0), - HRTIMER_MODE_REL); + + err = input_register_device(ekt_data.input); + if (err < 0) { + dev_err(&client->dev, + "%s: input_register_device failed, err = %d\n", + __func__, err); + goto fail; } + + ekt8232_register_interrupt(ekt_data.client); + + /* checking the interrupt to avoid missing any interrupt */ + if (ekt8232_detect_int_level() == 0) + ekt8232_ts_interrupt(client->irq, NULL); +#ifdef CONFIG_HAS_EARLYSUSPEND + ekt_data.early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ekt_data.early_suspend.suspend = elan_ts_early_suspend; + ekt_data.early_suspend.resume = elan_ts_late_resume; + register_early_suspend(&ekt_data.early_suspend); +#endif + touch_sysfs_init(); return 0; fail: input_free_device(ekt_data.input); + if (elan_wq) + destroy_workqueue(elan_wq); return err; } @@ -327,137 +690,123 @@ static int ekt8232_remove(struct i2c_client *client) { struct ekt8232_data *tp = i2c_get_clientdata(client); - dev_dbg(&client->dev, "%s: enter.\n", __FUNCTION__); + if (elan_wq) + destroy_workqueue(elan_wq); - input_unregister_device(tp->input); + dev_dbg(&client->dev, "%s: enter.\n", __func__); - free_irq(client->irq, tp); + input_unregister_device(tp->input); + if (ekt_data.use_irq) + free_irq(client->irq, tp); + else + hrtimer_cancel(&ekt_data.timer); return 0; } -static int ekt8232_get_power_state(struct i2c_client *client) + +static int ekt8232_suspend(struct i2c_client *client, pm_message_t mesg) { - uint8_t cmd[] = { read_cmd_packet, 0x50, 0x00, 0x01 }; - uint8_t power_state, buf_recv[4] = { 0 }; + uint8_t cmd[4]; + int rc = 0; - dev_dbg(&client->dev, "%s: enter.\n", __FUNCTION__); + dev_dbg(&client->dev, "%s: enter. irq = %d\n", __func__, client->irq); - if ((i2c_master_send(client, cmd, 4)) != 4) { - dev_err(&client->dev, - "%s: i2c_master_send failed\n", __FUNCTION__); - return -EINVAL; - } - msleep(50); + cancel_work_sync(&ekt_data.work); - if ((i2c_master_recv(client, buf_recv, 4)) != 4) { + rc = ekt8232_set_power_state(client, STATE_DEEP_SLEEP); +/* + rc = ekt8232_get_power_state(client); + if (rc < 0 || rc != STATE_DEEP_SLEEP) dev_err(&client->dev, - "%s: i2c_master_send failed\n", __FUNCTION__); - return -EINVAL; + "%s: put tp into sleep failed, err = %d!\n", + __func__, rc); +*/ + /* disable tp interrupt */ + if (ekt_data.fw_ver > 0x101) { + memset(cmd, disable_int, 4); + if ((i2c_master_send(client, cmd, sizeof(cmd))) != sizeof(cmd)) + dev_err(&client->dev, + "%s: tp disable interrupt failed\n", __func__); } - if (buf_recv[0] != cmd_reponse_packet) { - dev_err(&client->dev, - "%s: unknown packet got ?!\n", __FUNCTION__); - return -EINVAL; - } else { - power_state = buf_recv[1]; - dev_dbg(&client->dev, - "dump repsponse: %0x\n", power_state); + /* power off level shift */ + ekt_data.power(0); - power_state = (power_state & STATE_MASK) >> 3; - dev_dbg(&client->dev, "power state = %s\n", - power_state == STATE_DEEP_SLEEP ? - "Deep Sleep" : "Normal/Idle"); - - return power_state; - } + return 0; } -static int ekt8232_set_power_state(struct i2c_client *client, int state) +static int ekt8232_resume(struct i2c_client *client) { - uint8_t cmd[] = { write_cmd_packet, 0x50, 0x00, 0x01}; + int rc = 0, retry = 5; - dev_dbg(&client->dev, "%s: enter.\n", __FUNCTION__); + dev_dbg(&client->dev, + "%s: enter. irq = %d\n", __func__, client->irq); - cmd[1] |= (state << 3); + disable_irq(client->irq); - dev_dbg(&client->dev, - "dump cmd: %02x, %02x, %02x, %02x\n", - cmd[0], cmd[1], cmd[2], cmd[3]); + /* power on level shift */ + ekt_data.power(1); - if ((i2c_master_send(client, cmd, sizeof(cmd))) != sizeof(cmd)) { - dev_err(&client->dev, - "%s: i2c_master_send failed\n", __FUNCTION__); - return -EINVAL; + /* re-initial */ + if (ekt_data.fw_ver > 0x101) { + msleep(500); + rc = ekt8232_ts_init(client); + } else { + do { + rc = ekt8232_set_power_state(client, STATE_NORMAL); + rc = ekt8232_get_power_state(client); + if (rc != STATE_NORMAL) + dev_err(&client->dev, + "%s: wake up tp failed! err = %d\n", + __func__, rc); + else + break; + } while (--retry); } - return 0; -} -#ifdef CONFIG_PM -/* - * ekt8232_suspend -- suspend the ekt8232 panel - * - * 1. Send Power State Packet to get the present state - * 2. If it is not in *Deep Sleep*, send Power State Packet - * to change that. - */ -static int ekt8232_suspend(struct i2c_client *client, pm_message_t mesg) -{ - int rc = 0; + enable_irq(client->irq); - dev_dbg(&client->dev, - "%s: enter. irq = %d\n", __FUNCTION__, client->irq); + if (ekt8232_detect_int_level() == 0) + ekt8232_ts_interrupt(client->irq, NULL); - cancel_work_sync(&ekt_data.work); + return 0; +} - disable_irq(client->irq); - rc = ekt8232_set_power_state(client, STATE_DEEP_SLEEP); +#ifdef CONFIG_HAS_EARLYSUSPEND +static void elan_ts_early_suspend(struct early_suspend *h) +{ + struct i2c_client *client = ekt_data.client; - return rc; + dev_dbg(&client->dev, "%s enter.\n", __func__); + ekt8232_suspend(client, PMSG_SUSPEND); } -static int ekt8232_resume(struct i2c_client *client) +static void elan_ts_late_resume(struct early_suspend *h) { - int rc = 0; - - dev_dbg(&client->dev, - "%s: enter. irq = %d\n", __FUNCTION__, client->irq); - - ekt8232_set_power_state(client, STATE_NORMAL); - - rc = wait_event_timeout(ekt_data.wait, - ((rc = ekt8232_get_power_state(client)) == STATE_NORMAL), HZ); - if (rc <= 0) { - dev_err(&client->dev, "Oops, can not wake touch panel up!!!\n"); - return -ETIMEDOUT; - } - - dev_dbg(&client->dev, - "%s: done. enable irq = %d\n", __FUNCTION__, client->irq); - - enable_irq(client->irq); + struct i2c_client *client = ekt_data.client; - return rc; + dev_dbg(&client->dev, "%s enter.\n", __func__); + ekt8232_resume(client); } -#else -#define ekt8232_suspend NULL -#define ekt8232_resume NULL #endif -static const struct i2c_device_id ekt8232_id[] = { - { EKT8232NAME, 0 }, +/* -------------------------------------------------------------------- */ +static const struct i2c_device_id ekt8232_ts_id[] = { + { ELAN_8232_I2C_NAME, 0 }, { } }; static struct i2c_driver ekt8232_driver = { .probe = ekt8232_probe, .remove = ekt8232_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND .suspend = ekt8232_suspend, .resume = ekt8232_resume, - .id_table = ekt8232_id, +#endif + .id_table = ekt8232_ts_id, .driver = { - .name = EKT8232NAME, + .name = ELAN_8232_I2C_NAME, }, }; diff --git a/include/linux/elan_i2c.h b/include/linux/elan_i2c.h new file mode 100644 index 0000000000000..41a993613891f --- /dev/null +++ b/include/linux/elan_i2c.h @@ -0,0 +1,17 @@ +#ifndef ELAN_I2C_H +#define ELAN_I2C_H + +#define ELAN_8232_I2C_NAME "elan-touch" + +struct elan_i2c_platform_data { + uint16_t version; + int abs_x_min; + int abs_x_max; + int abs_y_min; + int abs_y_max; + int intr_gpio; + int (*power)(int on); +}; + +#endif + From b7fe1088596f342e52fac6987965f959df79916d Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Wed, 18 Feb 2009 08:59:28 -0800 Subject: [PATCH 0277/2556] [ARM] msm: Camera: clean up and fix camera sensor driver Signed-off-by: San Mehat --- arch/arm/mach-msm/board-trout.c | 8 +- arch/arm/mach-msm/include/mach/board.h | 17 +- drivers/i2c/chips/mt9t013.c | 253 ++++++++++++++++--------- 3 files changed, 170 insertions(+), 108 deletions(-) diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 7bcdffb94e53f..6442b744be7d8 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -527,10 +527,14 @@ static struct platform_device trout_ram_console_device = { .resource = trout_ram_console_resource, }; +static void config_camera_on_gpios(void); +static void config_camera_off_gpios(void); static struct msm_camera_device_platform_data msm_camera_device = { .sensor_reset = 108, .sensor_pwd = 85, .vcm_pwd = TROUT_GPIO_VCM_PWDN, + .config_gpio_on = config_camera_on_gpios, + .config_gpio_off = config_camera_off_gpios, }; static struct platform_device trout_camera = { @@ -811,13 +815,13 @@ static void config_gpio_table(uint32_t *table, int len) } } -void config_camera_on_gpios(void) +static void config_camera_on_gpios(void) { config_gpio_table(camera_on_gpio_table, ARRAY_SIZE(camera_on_gpio_table)); } -void config_camera_off_gpios(void) +static void config_camera_off_gpios(void) { config_gpio_table(camera_off_gpio_table, ARRAY_SIZE(camera_off_gpio_table)); diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index f8f084aba54ac..54069810b0bfb 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -31,22 +31,7 @@ struct msm_acpu_clock_platform_data unsigned long wait_for_irq_khz; }; -struct msm_camera_sensor_info { - int sensor_reset; - int sensor_pwd; - int vcm_pwd; - const char *sensor_name; -}; - - -struct msm_camera_platform_data{ - void (*camera_gpio_on) (void); - void (*camera_gpio_off)(void); - uint8_t snum; - struct msm_camera_sensor_info *sinfo; -}; - -struct msm_camera_device_platform_data{ +struct msm_camera_device_platform_data { int sensor_reset; int sensor_pwd; int vcm_pwd; diff --git a/drivers/i2c/chips/mt9t013.c b/drivers/i2c/chips/mt9t013.c index 8d94b3e22796a..11b9e081d3dcc 100644 --- a/drivers/i2c/chips/mt9t013.c +++ b/drivers/i2c/chips/mt9t013.c @@ -16,25 +16,26 @@ #include #include #include -#include #include -#include #include #include #include #include -#include #include -#include -#include -#include -#include #include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include /* define ioctls */ -#include + #define ALLOW_USPACE_RW 0 @@ -42,9 +43,6 @@ static const uint32_t fps_divider = 1; #define AF_I2C_ID 0x18 /* actuator's slave address */ -extern void config_camera_on_gpios(void); -extern void config_camera_off_gpios(void); - static struct i2c_client *pclient; /* we need this to set the clock rate */ @@ -62,46 +60,54 @@ static int pclk_set; static const struct mt9t013_reg_pat mt9t013_reg_pattern = { .reg = { { /* preview 2x2 binning 20fps, pclk MHz, MCLK 24MHz */ - 10, //vt_pix_clk_div REG=0x0300 // update get_snapshot_fps if this change - 1, //vt_sys_clk_div REG=0x0302 // update get_snapshot_fps if this change - 3,//2, //pre_pll_clk_div REG=0x0304 // update get_snapshot_fps if this change - 80,//40, //pll_multiplier REG=0x0306 60 for 30fps preview, 40 for 20fps preview - 10, //op_pix_clk_div REG=0x0308 - 1, //op_sys_clk_div REG=0x030A - 16, //scale_m REG=0x0404 - 0x0111, //row_speed REG=0x3016 - 8, //x_addr_start REG=0x3004 - 2053, //x_addr_end REG=0x3008 - 8, //y_addr_start REG=0x3002 - 1541, //y_addr_end REG=0x3006 - 0x046C, //read_mode REG=0x3040 - 1024, //x_output_size REG=0x034C - 768, //y_output_size REG=0x034E - 3540,//2616, //line_length_pck REG=0x300C - 861,//916, //frame_length_lines REG=0x300A - 16, //coarse_integration_time REG=0x3012 - 1461 //fine_integration_time REG=0x3014 + 10, /*vt_pix_clk_div REG=0x0300*/ + /*update get_snapshot_fps if this change*/ + 1, /*vt_sys_clk_div REG=0x0302*/ + /*update get_snapshot_fps if this change*/ + 3, /*2, pre_pll_clk_div REG=0x0304*/ + /*update get_snapshot_fps if this change*/ + 80, /*40, pll_multiplier REG=0x0306*/ + /*60 for 30fps preview, 40 for 20fps preview*/ + 10, /*op_pix_clk_div REG=0x0308*/ + 1, /*op_sys_clk_div REG=0x030A*/ + 16, /*scale_m REG=0x0404*/ + 0x0111, /*row_speed REG=0x3016*/ + 8, /*x_addr_start REG=0x3004*/ + 2053, /*x_addr_end REG=0x3008*/ + 8, /*y_addr_start REG=0x3002*/ + 1541, /*y_addr_end REG=0x3006*/ + 0x046C, /*read_mode REG=0x3040*/ + 1024, /*x_output_size REG=0x034C*/ + 768, /*y_output_size REG=0x034E*/ + 3540, /*2616, line_length_pck REG=0x300C*/ + 861, /*916, frame_length_lines REG=0x300A*/ + 16, /*coarse_integration_time REG=0x3012*/ + 1461 /*fine_integration_time REG=0x3014*/ }, { /* snapshot */ - 10, //vt_pix_clk_div REG=0x0300 // update get_snapshot_fps if this change - 1, //vt_sys_clk_div REG=0x0302 // update get_snapshot_fps if this change - 3,//2, //pre_pll_clk_div REG=0x0304 // update get_snapshot_fps if this change - 80,//40, //pll_multiplier REG=0x0306 50 for 15fps snapshot, 40 for 10fps snapshot - 10, //op_pix_clk_div REG=0x0308 - 1, //op_sys_clk_div REG=0x030A - 16, //scale_m REG=0x0404 - 0x0111, //row_speed REG=0x3016 - 8, //0, //x_addr_start REG=0x3004 - 2073, //2061, //x_addr_end REG=0x3008 - 8, //2, //y_addr_start REG=0x3002 - 1551, //1545, //y_addr_end REG=0x3006 - 0x0024, //read_mode REG=0x3040 - 2066, //x_output_size REG=0x034C - 1544, //y_output_size REG=0x034E - 4800,//2952, //line_length_pck REG=0x300C - 1629, //frame_length_lines REG=0x300A - 16, //coarse_integration_time REG=0x3012 - 733 //fine_integration_time REG=0x3014 + 10, /*vt_pix_clk_div REG=0x0300*/ + /*update get_snapshot_fps if this change*/ + 1, /*vt_sys_clk_div REG=0x0302*/ + /*update get_snapshot_fps if this change*/ + 3, /*2, pre_pll_clk_div REG=0x0304*/ + /*update get_snapshot_fps if this change*/ + 80, /*40, pll_multiplier REG=0x0306*/ + /*50 for 15fps snapshot, 40 for 10fps snapshot*/ + 10, /*op_pix_clk_div REG=0x0308*/ + 1, /*op_sys_clk_div REG=0x030A*/ + 16, /*scale_m REG=0x0404*/ + 0x0111, /*row_speed REG=0x3016*/ + 8, /*0, x_addr_start REG=0x3004*/ + 2063, /*2061, x_addr_end REG=0x3008*/ + 8, /*2, y_addr_start REG=0x3002*/ + 1551, /*1545, y_addr_end REG=0x3006*/ + 0x0024, /*read_mode REG=0x3040*/ + 2063, /*output_size REG=0x034C*/ + 1544, /*y_output_size REG=0x034E*/ + 4800, /*2952, line_length_pck REG=0x300C*/ + 1629, /*frame_length_lines REG=0x300A*/ + 16, /*coarse_integration_time REG=0x3012*/ + 733 /*fine_integration_time REG=0x3014*/ } }}; @@ -480,7 +486,7 @@ static void mt9t013_sensor_init(void) mdelay(2); /* enable gpio */ - config_camera_on_gpios(); + cam->config_gpio_on(); /* delay 2 ms */ mdelay(2); @@ -519,7 +525,7 @@ static void mt9t013_sensor_suspend(void) msm_camio_clk_disable(CAMIO_MDC_CLK); CLK_DISABLE_AND_PUT(vfe_clk); /* this matches clk_select(1) */ /* disable gpios */ - config_camera_off_gpios(); + cam->config_gpio_off(); printk(KERN_INFO "mt9t013: camera sensor suspend sequence done\n"); } @@ -531,7 +537,7 @@ static int mt9t013_open(struct inode *ip, struct file *fp) if (!opened) { printk(KERN_INFO "mt9t013: prevent collapse on idle\n"); prevent_suspend(); - config_camera_on_gpios(); + cam->config_gpio_on(); opened = 1; rc = 0; } @@ -551,7 +557,7 @@ static int mt9t013_release(struct inode *ip, struct file *fp) CLK_DISABLE_AND_PUT(vfe_clk); mt9t013_lens_power(0); mt9t013_i2c_power_down(); - config_camera_off_gpios(); + cam->config_gpio_off(); printk(KERN_INFO "mt9t013: allow collapse on idle\n"); allow_suspend(); rc = pclk_set = opened = 0; @@ -630,8 +636,8 @@ static long mt9t013_ioctl(struct file *filp, unsigned int cmd, unsigned long arg addr = *((unsigned short *)rwbuf); data = *((unsigned short *)(rwbuf+2)); rc = mt9t013_i2c_write(addr, data); - } - else printk("mt9t013: write: err %d\n", rc); + } else + printk(KERN_ERR "mt9t013: write: err %d\n", rc); break; case MT9T013_I2C_IOCTL_R: @@ -640,18 +646,20 @@ static long mt9t013_ioctl(struct file *filp, unsigned int cmd, unsigned long arg rc = mt9t013_i2c_read(addr, (unsigned short *)(rwbuf+2)); if (!rc) { if (copy_to_user(argp, rwbuf, 4)) { - printk("mt9t013: read: err writeback -EFAULT\n"); + printk(KERN_ERR "mt9t013: read: err " \ + "writeback -EFAULT\n"); rc = -EFAULT; } } - } - else printk("mt9t013: read: err %d\n", rc); + } else + printk(KERN_ERR "mt9t013: read: err %d\n", rc); break; case MT9T013_I2C_IOCTL_AF_W: if (/* CHECK() && */ COPY_FROM_USER(3)) rc = mt9t013_i2c_lens_write(*rwbuf, *(rwbuf + 1), *(rwbuf + 2)); - else printk("mt9t013: af write: err %d\n", rc); + else + printk(KERN_ERR "mt9t013: af write: err %d\n", rc); break; #endif /* ALLOW_USPACE_RW */ @@ -726,7 +734,7 @@ static long mt9t013_ioctl(struct file *filp, unsigned int cmd, unsigned long arg printk(KERN_INFO "mt9t013: clk select %ld\n", arg); rc = clk_select(!!arg); break; - + case MT9T013_I2C_IOCTL_CLK_FREQ_PROG: printk(KERN_INFO "mt9t013: clk rate select %ld\n", arg); rc = msm_camio_clk_rate_set(arg); @@ -1007,7 +1015,18 @@ static int mt9t013_i2c_sensor_setting(unsigned long arg) I2C_WRITE(REG_X_ADDR_END, mt9t013_reg_pattern.reg[rt].x_addr_end); I2C_WRITE(REG_Y_ADDR_START, mt9t013_reg_pattern.reg[rt].y_addr_start); I2C_WRITE(REG_Y_ADDR_END, mt9t013_reg_pattern.reg[rt].y_addr_end); - I2C_WRITE(REG_READ_MODE, mt9t013_reg_pattern.reg[rt].read_mode); + + if (machine_is_sapphire()) { + if (rt == 0) { + I2C_WRITE(REG_READ_MODE, 0x046F); + } else { + I2C_WRITE(REG_READ_MODE, 0x0027); + } + } else { + I2C_WRITE(REG_READ_MODE, + mt9t013_reg_pattern.reg[rt].read_mode); + } + I2C_WRITE(REG_SCALE_M, mt9t013_reg_pattern.reg[rt].scale_m); I2C_WRITE(REG_X_OUTPUT_SIZE, mt9t013_reg_pattern.reg[rt].x_output_size); I2C_WRITE(REG_Y_OUTPUT_SIZE, mt9t013_reg_pattern.reg[rt].y_output_size); @@ -1033,12 +1052,28 @@ static int mt9t013_i2c_sensor_setting(unsigned long arg) I2C_WRITE(0x3096, 0x4949); /* set preview or snapshot mode */ - I2C_WRITE(REG_ROW_SPEED, mt9t013_reg_pattern.reg[rt].row_speed); - I2C_WRITE(REG_X_ADDR_START, mt9t013_reg_pattern.reg[rt].x_addr_start); - I2C_WRITE(REG_X_ADDR_END, mt9t013_reg_pattern.reg[rt].x_addr_end); - I2C_WRITE(REG_Y_ADDR_START, mt9t013_reg_pattern.reg[rt].y_addr_start); - I2C_WRITE(REG_Y_ADDR_END, mt9t013_reg_pattern.reg[rt].y_addr_end); - I2C_WRITE(REG_READ_MODE, mt9t013_reg_pattern.reg[rt].read_mode); + I2C_WRITE(REG_ROW_SPEED, + mt9t013_reg_pattern.reg[rt].row_speed); + I2C_WRITE(REG_X_ADDR_START, + mt9t013_reg_pattern.reg[rt].x_addr_start); + I2C_WRITE(REG_X_ADDR_END, + mt9t013_reg_pattern.reg[rt].x_addr_end); + I2C_WRITE(REG_Y_ADDR_START, + mt9t013_reg_pattern.reg[rt].y_addr_start); + I2C_WRITE(REG_Y_ADDR_END, + mt9t013_reg_pattern.reg[rt].y_addr_end); + + if (machine_is_sapphire()) { + if (rt == 0) { + I2C_WRITE(REG_READ_MODE, 0x046F); + } else { + I2C_WRITE(REG_READ_MODE, 0x0027); + } + } else { + I2C_WRITE(REG_READ_MODE, + mt9t013_reg_pattern.reg[rt].read_mode); + } + I2C_WRITE(REG_SCALE_M, mt9t013_reg_pattern.reg[rt].scale_m); I2C_WRITE(REG_X_OUTPUT_SIZE, mt9t013_reg_pattern.reg[rt].x_output_size); I2C_WRITE(REG_Y_OUTPUT_SIZE, mt9t013_reg_pattern.reg[rt].y_output_size); @@ -1070,10 +1105,10 @@ static int mt9t013_i2c_exposure_gain(uint16_t line, uint16_t gain) gain |= 0x200; /* set digital gain */ - I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD); + /*I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD);*/ I2C_WRITE(REG_GLOBAL_GAIN, gain); I2C_WRITE(REG_COARSE_INTEGRATION_TIME, line); - I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE); + /*I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE);*/ return 0; } @@ -1130,7 +1165,7 @@ static int mt9t013_init_client(struct i2c_client *client) /* Initialize the MT9T013 Chip */ init_waitqueue_head(&g_data_ready_wait_queue); return 0; -} +} static struct file_operations mt9t013_fops = { .owner = THIS_MODULE, @@ -1145,6 +1180,50 @@ static struct miscdevice mt9t013_device = { .fops = &mt9t013_fops, }; +static const char *MT9T013Vendor = "micron"; +static const char *MT9T013NAME = "mt9t013"; +static const char *MT9T013Size = "3M"; + + + +static ssize_t sensor_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + + sprintf(buf, "%s %s %s\n", MT9T013Vendor, MT9T013NAME, MT9T013Size); + ret = strlen(buf) + 1; + + return ret; +} + +static DEVICE_ATTR(sensor, 0444, sensor_vendor_show, NULL); + + +static struct kobject *android_mt9t013 = NULL; + +static int mt9t013_sysfs_init(void) +{ + int ret ; + printk(KERN_INFO "mt9t013:kobject creat and add\n"); + android_mt9t013 = kobject_create_and_add("android_camera", NULL); + if (android_mt9t013 == NULL) { + printk(KERN_INFO "mt9t013_sysfs_init: subsystem_register " \ + "failed\n"); + ret = -ENOMEM; + return ret ; + } + printk(KERN_INFO "mt9t013:sysfs_create_file\n"); + ret = sysfs_create_file(android_mt9t013, &dev_attr_sensor.attr); + if (ret) { + printk(KERN_INFO "mt9t013_sysfs_init: sysfs_create_file " \ + "failed\n"); + kobject_del(android_mt9t013); + } + return 0 ; +} + + static int mt9t013_probe( struct i2c_client *client, const struct i2c_device_id *id) @@ -1152,9 +1231,10 @@ static int mt9t013_probe( struct mt9t013_data *mt; int err = 0; printk(KERN_INFO "mt9t013: probe\n"); + if(!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) goto exit_check_functionality_failed; - + if(!(mt = kzalloc( sizeof(struct mt9t013_data), GFP_KERNEL))) { err = -ENOMEM; goto exit_alloc_data_failed; @@ -1173,6 +1253,7 @@ static int mt9t013_probe( goto exit_misc_device_register_failed; } init_suspend(); + mt9t013_sysfs_init(); return 0; exit_misc_device_register_failed: @@ -1208,18 +1289,17 @@ static struct i2c_driver mt9t013_driver = { }, }; - static int mt9t013_plat_probe(struct platform_device *pdev __attribute__((unused))) { - int rc = -EFAULT; - - if(pdev->dev.platform_data) - { - printk(KERN_INFO "pdev->dev.platform_data is not NULL\n"); - cam=pdev->dev.platform_data; - rc = i2c_add_driver(&mt9t013_driver); - } - return rc; + int rc = -EFAULT; + + if(pdev->dev.platform_data) + { + printk(KERN_INFO "pdev->dev.platform_data is not NULL\n"); + cam = pdev->dev.platform_data; + rc = i2c_add_driver(&mt9t013_driver); + } + return rc; } static struct platform_driver mt9t013_plat_driver = { @@ -1232,18 +1312,11 @@ static struct platform_driver mt9t013_plat_driver = { static int __init mt9t013_init(void) { - return platform_driver_register(&mt9t013_plat_driver); -} - -static void __exit mt9t013_exit(void) -{ - i2c_del_driver(&mt9t013_driver); + return platform_driver_register(&mt9t013_plat_driver); } module_init(mt9t013_init); -module_exit(mt9t013_exit); MODULE_AUTHOR("Kidd Chen"); MODULE_DESCRIPTION("MT9T013 Driver"); MODULE_LICENSE("GPL"); - From 5029505dd37dd41f6fb2ab700981879b58e4cdf2 Mon Sep 17 00:00:00 2001 From: horng chuang Date: Wed, 18 Feb 2009 18:09:08 +0800 Subject: [PATCH 0278/2556] mt9t013: Fix the call sequence in function mt9t013_release(),3M camera sensor. mt9t013_i2c_power_down() should be called before closing MCLK, otherwise I2C_WRITE will always fail. Signed-off-by: San Mehat --- drivers/i2c/chips/mt9t013.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) mode change 100644 => 100755 drivers/i2c/chips/mt9t013.c diff --git a/drivers/i2c/chips/mt9t013.c b/drivers/i2c/chips/mt9t013.c old mode 100644 new mode 100755 index 11b9e081d3dcc..882552e9a84cf --- a/drivers/i2c/chips/mt9t013.c +++ b/drivers/i2c/chips/mt9t013.c @@ -552,12 +552,19 @@ static int mt9t013_release(struct inode *ip, struct file *fp) down(&sem); if (opened) { printk(KERN_INFO "mt9t013: release clocks\n"); + + + /* mt9t013_i2c_power_down() should be called before closing MCLK */ + /* otherwise I2C_WRITE will always fail */ + mt9t013_i2c_power_down(); + CLK_DISABLE_AND_PUT(mdc_clk); CLK_DISABLE_AND_PUT(vfe_mdc_clk); CLK_DISABLE_AND_PUT(vfe_clk); mt9t013_lens_power(0); - mt9t013_i2c_power_down(); + cam->config_gpio_off(); + printk(KERN_INFO "mt9t013: allow collapse on idle\n"); allow_suspend(); rc = pclk_set = opened = 0; @@ -1146,12 +1153,28 @@ static int mt9t013_i2c_power_up(void) static int mt9t013_i2c_power_down(void) { + int i = 0, try_more = 100; + printk(KERN_INFO "mt9t013: power down\n"); if (!powered) { printk(KERN_INFO "mt9t013: already powered down\n"); return 0; } - I2C_WRITE(MT9T013_REG_RESET_REGISTER, MT9T013_RESET_REGISTER_PWOFF); + + /* I2C_WRITE(MT9T013_REG_RESET_REGISTER, MT9T013_RESET_REGISTER_PWOFF); */ + /* Modified by Horng for more tries while I2C write fail */ + /* -------------------------------------------------------------------- */ + while(mt9t013_i2c_write(MT9T013_REG_RESET_REGISTER, MT9T013_RESET_REGISTER_PWOFF) < 0) + { + if (i >= try_more) + return -EIO; + else { + i++; + printk(KERN_INFO "mt9p012: in mt9p012_i2c_power_down() call mt9p012_i2c_write() failed !!! (try %d times)\n", i); + mdelay(i+5); + } + } + /* -------------------------------------------------------------------- */ mdelay(5); powered = pclk_set = 0; return 0; From 13da3029a712a12fa0db5eda267878af790000c9 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Thu, 12 Mar 2009 15:08:42 -0700 Subject: [PATCH 0279/2556] [ARM] msm: mt9t013: add mode field to exposure gain ioctl Signed-off-by: Iliyan Malchev --- drivers/i2c/chips/mt9t013.c | 12 +++++++++--- include/linux/mt9t013.h | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/chips/mt9t013.c b/drivers/i2c/chips/mt9t013.c index 882552e9a84cf..744a02f88e77d 100755 --- a/drivers/i2c/chips/mt9t013.c +++ b/drivers/i2c/chips/mt9t013.c @@ -226,7 +226,8 @@ static DECLARE_WAIT_QUEUE_HEAD(g_data_ready_wait_queue); static int mt9t013_i2c_sensor_init(struct mt9t013_init *init); static int mt9t013_i2c_sensor_setting(unsigned long arg); -static int mt9t013_i2c_exposure_gain(uint16_t line, uint16_t gain); +static int mt9t013_i2c_exposure_gain(uint32_t mode, uint16_t line, + uint16_t gain); static int mt9t013_i2c_move_focus(uint16_t position); static int mt9t013_i2c_set_default_focus(uint8_t step); static int mt9t013_i2c_power_up(void); @@ -765,7 +766,7 @@ static long mt9t013_ioctl(struct file *filp, unsigned int cmd, unsigned long arg rc = -EFAULT; break; } - rc = mt9t013_i2c_exposure_gain(exp.line, exp.gain); + rc = mt9t013_i2c_exposure_gain(exp.mode, exp.line, exp.gain); } break; @@ -1104,7 +1105,8 @@ static int mt9t013_i2c_sensor_setting(unsigned long arg) return 0; } -static int mt9t013_i2c_exposure_gain(uint16_t line, uint16_t gain) +static int mt9t013_i2c_exposure_gain(uint32_t mode, uint16_t line, + uint16_t gain) { static const uint16_t max_legal_gain = 0x01FF; @@ -1116,6 +1118,10 @@ static int mt9t013_i2c_exposure_gain(uint16_t line, uint16_t gain) I2C_WRITE(REG_GLOBAL_GAIN, gain); I2C_WRITE(REG_COARSE_INTEGRATION_TIME, line); /*I2C_WRITE(REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE);*/ + if (mode == 1) { + /* RESET REGISTER RESTART */ + I2C_WRITE(MT9T013_REG_RESET_REGISTER, 0x10cc|0x0002); + } return 0; } diff --git a/include/linux/mt9t013.h b/include/linux/mt9t013.h index 7b7811eae8fb1..543923a136007 100644 --- a/include/linux/mt9t013.h +++ b/include/linux/mt9t013.h @@ -114,6 +114,7 @@ struct mt9t013_reg_pat { struct mt9t013_exposure_gain { uint16_t gain; uint16_t line; + uint32_t mode; }; #define MT9T013_I2C_IOCTL_EXPOSURE_GAIN \ From b4cc316aeaadcc2bfc6aac86b84ed6174455b7a5 Mon Sep 17 00:00:00 2001 From: Thomas Tsai Date: Wed, 18 Feb 2009 09:11:04 -0800 Subject: [PATCH 0280/2556] leds: Add CPLD driver for LEDS Signed-off-by: San Mehat leds: leds-cpld: Remove unused brightness setting Signed-off-by: San Mehat leds: cpld: Make state change debugging conditional Signed-off-by: Dima Zavin --- drivers/leds/Kconfig | 7 + drivers/leds/Makefile | 1 + drivers/leds/leds-cpld.c | 405 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 413 insertions(+) create mode 100644 drivers/leds/leds-cpld.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index bf1aebd1acea3..9bc10860ba31e 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -210,6 +210,13 @@ config LEDS_LP5523 Driver provides direct control via LED class and interface for programming the engines. +config LEDS_CPLD + tristate "LED Support for CPLD connected LEDs" + default y + depends on LEDS_CLASS + help + This option enables support for the LEDs connected to CPLD + config LEDS_CLEVO_MAIL tristate "Mail LED on Clevo notebook" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 5ca5c92220f83..3efece22e57af 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o +obj-$(CONFIG_LEDS_CPLD) += leds-cpld.o obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o diff --git a/drivers/leds/leds-cpld.c b/drivers/leds/leds-cpld.c new file mode 100644 index 0000000000000..eab004c3d75c5 --- /dev/null +++ b/drivers/leds/leds-cpld.c @@ -0,0 +1,405 @@ +/* include/asm/mach-msm/leds-cpld.c + * + * Copyright (C) 2008 HTC Corporation. + * + * Author: Farmer Tseng + * + * 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 + +#define DEBUG_LED_CHANGE 0 + +static int _g_cpld_led_addr; + +struct CPLD_LED_data { + spinlock_t data_lock; + struct led_classdev leds[4]; /* blue, green, red */ +}; + +static ssize_t led_blink_solid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct CPLD_LED_data *CPLD_LED; + int idx = 2; + uint8_t reg_val; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + ssize_t ret = 0; + + if (!strcmp(led_cdev->name, "red")) + idx = 0; + else if (!strcmp(led_cdev->name, "green")) + idx = 1; + else + idx = 2; + + CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]); + + spin_lock(&CPLD_LED->data_lock); + reg_val = readb(_g_cpld_led_addr); + reg_val = reg_val >> (2 * idx + 1); + reg_val &= 0x1; + spin_unlock(&CPLD_LED->data_lock); + + /* no lock needed for this */ + sprintf(buf, "%u\n", reg_val); + ret = strlen(buf) + 1; + + return ret; +} + +static ssize_t led_blink_solid_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct CPLD_LED_data *CPLD_LED; + int idx = 2; + uint8_t reg_val; + char *after; + unsigned long state; + ssize_t ret = -EINVAL; + size_t count; + + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + if (!strcmp(led_cdev->name, "red")) + idx = 0; + else if (!strcmp(led_cdev->name, "green")) + idx = 1; + else + idx = 2; + + CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]); + + state = simple_strtoul(buf, &after, 10); + + count = after - buf; + + if (*after && isspace(*after)) + count++; + + if (count == size) { + ret = count; + spin_lock(&CPLD_LED->data_lock); + reg_val = readb(_g_cpld_led_addr); + if (state) + reg_val |= 1 << (2 * idx + 1); + else + reg_val &= ~(1 << (2 * idx + 1)); + + writeb(reg_val, _g_cpld_led_addr); + spin_unlock(&CPLD_LED->data_lock); + } + + return ret; +} + +static DEVICE_ATTR(blink, 0644, led_blink_solid_show, led_blink_solid_store); + +static ssize_t cpldled_blink_all_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint8_t reg_val; + struct CPLD_LED_data *CPLD_LED = dev_get_drvdata(dev); + ssize_t ret = 0; + + spin_lock(&CPLD_LED->data_lock); + reg_val = readb(_g_cpld_led_addr); + reg_val &= 0x2A; + if (reg_val == 0x2A) + reg_val = 1; + else + reg_val = 0; + spin_unlock(&CPLD_LED->data_lock); + + /* no lock needed for this */ + sprintf(buf, "%u\n", reg_val); + ret = strlen(buf) + 1; + + return ret; +} + +static ssize_t cpldled_blink_all_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + uint8_t reg_val; + char *after; + unsigned long state; + ssize_t ret = -EINVAL; + size_t count; + struct CPLD_LED_data *CPLD_LED = dev_get_drvdata(dev); + + state = simple_strtoul(buf, &after, 10); + + count = after - buf; + + if (*after && isspace(*after)) + count++; + + if (count == size) { + ret = count; + spin_lock(&CPLD_LED->data_lock); + reg_val = readb(_g_cpld_led_addr); + if (state) + reg_val |= 0x2A; + else + reg_val &= ~0x2A; + + writeb(reg_val, _g_cpld_led_addr); + spin_unlock(&CPLD_LED->data_lock); + } + + return ret; +} + +static struct device_attribute dev_attr_blink_all = { + .attr = { + .name = "blink", + .mode = 0644, + }, + .show = cpldled_blink_all_show, + .store = cpldled_blink_all_store, +}; + +static void led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct CPLD_LED_data *CPLD_LED; + int idx = 2; + struct led_classdev *led; + uint8_t reg_val; + + if (!strcmp(led_cdev->name, "jogball-backlight")) { + if (brightness > 7) + reg_val = 1; + else + reg_val = brightness; + writeb(0, _g_cpld_led_addr + 0x8); + writeb(reg_val, _g_cpld_led_addr + 0x8); +#if DEBUG_LED_CHANGE + printk(KERN_INFO "LED change: jogball backlight = %d \n", + reg_val); +#endif + return; + } else if (!strcmp(led_cdev->name, "red")) { + idx = 0; + } else if (!strcmp(led_cdev->name, "green")) { + idx = 1; + } else { + idx = 2; + } + + CPLD_LED = container_of(led_cdev, struct CPLD_LED_data, leds[idx]); + spin_lock(&CPLD_LED->data_lock); + reg_val = readb(_g_cpld_led_addr); + led = &CPLD_LED->leds[idx]; + + if (led->brightness > LED_OFF) + reg_val |= 1 << (2 * idx); + else + reg_val &= ~(1 << (2 * idx)); + + writeb(reg_val, _g_cpld_led_addr); +#if DEBUG_LED_CHANGE + printk(KERN_INFO "LED change: %s = %d \n", led_cdev->name, led->brightness); +#endif + spin_unlock(&CPLD_LED->data_lock); +} + +static ssize_t cpldled_grpfreq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", 0); +} + +static ssize_t cpldled_grpfreq_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return 0; +} + +static DEVICE_ATTR(grpfreq, 0644, cpldled_grpfreq_show, cpldled_grpfreq_store); + +static ssize_t cpldled_grppwm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", 0); +} + +static ssize_t cpldled_grppwm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return 0; +} + +static DEVICE_ATTR(grppwm, 0644, cpldled_grppwm_show, cpldled_grppwm_store); + +static int CPLD_LED_probe(struct platform_device *pdev) +{ + int ret = 0; + int i, j; + struct resource *res; + struct CPLD_LED_data *CPLD_LED; + + CPLD_LED = kzalloc(sizeof(struct CPLD_LED_data), GFP_KERNEL); + if (CPLD_LED == NULL) { + printk(KERN_ERR "CPLD_LED_probe: no memory for device\n"); + ret = -ENOMEM; + goto err_alloc_failed; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENOMEM; + goto err_alloc_failed; + } + + _g_cpld_led_addr = res->start; + if (!_g_cpld_led_addr) { + ret = -ENOMEM; + goto err_alloc_failed; + } + + memset(CPLD_LED, 0, sizeof(struct CPLD_LED_data)); + writeb(0x00, _g_cpld_led_addr); + + CPLD_LED->leds[0].name = "red"; + CPLD_LED->leds[0].brightness_set = led_brightness_set; + + CPLD_LED->leds[1].name = "green"; + CPLD_LED->leds[1].brightness_set = led_brightness_set; + + CPLD_LED->leds[2].name = "blue"; + CPLD_LED->leds[2].brightness_set = led_brightness_set; + + CPLD_LED->leds[3].name = "jogball-backlight"; + CPLD_LED->leds[3].brightness_set = led_brightness_set; + + spin_lock_init(&CPLD_LED->data_lock); + + for (i = 0; i < 4; i++) { /* red, green, blue jogball */ + ret = led_classdev_register(&pdev->dev, &CPLD_LED->leds[i]); + if (ret) { + printk(KERN_ERR + "CPLD_LED: led_classdev_register failed\n"); + goto err_led_classdev_register_failed; + } + } + + for (i = 0; i < 3; i++) { + ret = + device_create_file(CPLD_LED->leds[i].dev, &dev_attr_blink); + if (ret) { + printk(KERN_ERR + "CPLD_LED: device_create_file failed\n"); + goto err_out_attr_blink; + } + } + + dev_set_drvdata(&pdev->dev, CPLD_LED); + ret = device_create_file(&pdev->dev, &dev_attr_blink_all); + if (ret) { + printk(KERN_ERR + "CPLD_LED: create dev_attr_blink_all failed\n"); + goto err_out_attr_blink; + } + ret = device_create_file(&pdev->dev, &dev_attr_grppwm); + if (ret) { + printk(KERN_ERR + "CPLD_LED: create dev_attr_grppwm failed\n"); + goto err_out_attr_grppwm; + } + ret = device_create_file(&pdev->dev, &dev_attr_grpfreq); + if (ret) { + printk(KERN_ERR + "CPLD_LED: create dev_attr_grpfreq failed\n"); + goto err_out_attr_grpfreq; + } + + return 0; + +err_out_attr_grpfreq: + device_remove_file(&pdev->dev, &dev_attr_grppwm); +err_out_attr_grppwm: + device_remove_file(&pdev->dev, &dev_attr_blink_all); +err_out_attr_blink: + for (j = 0; j < i; j++) + device_remove_file(CPLD_LED->leds[j].dev, &dev_attr_blink); + i = 3; + +err_led_classdev_register_failed: + for (j = 0; j < i; j++) + led_classdev_unregister(&CPLD_LED->leds[j]); + +err_alloc_failed: + kfree(CPLD_LED); + + return ret; +} + +static int __devexit CPLD_LED_remove(struct platform_device *pdev) +{ + struct CPLD_LED_data *CPLD_LED; + int i; + + CPLD_LED = platform_get_drvdata(pdev); + + for (i = 0; i < 3; i++) { + device_remove_file(CPLD_LED->leds[i].dev, &dev_attr_blink); + led_classdev_unregister(&CPLD_LED->leds[i]); + } + + device_remove_file(&pdev->dev, &dev_attr_blink_all); + device_remove_file(&pdev->dev, &dev_attr_grppwm); + device_remove_file(&pdev->dev, &dev_attr_grpfreq); + + kfree(CPLD_LED); + return 0; +} + +static struct platform_driver CPLD_LED_driver = { + .probe = CPLD_LED_probe, + .remove = __devexit_p(CPLD_LED_remove), + .driver = { + .name = "leds-cpld", + .owner = THIS_MODULE, + }, +}; + +static int __init CPLD_LED_init(void) +{ + return platform_driver_register(&CPLD_LED_driver); +} + +static void __exit CPLD_LED_exit(void) +{ + platform_driver_unregister(&CPLD_LED_driver); +} + +MODULE_AUTHOR("Farmer Tseng"); +MODULE_DESCRIPTION("CPLD_LED driver"); +MODULE_LICENSE("GPL"); + +module_init(CPLD_LED_init); +module_exit(CPLD_LED_exit); From f0e3fad41c60279cf0852afd8c10969d6c4bcbd3 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Wed, 18 Feb 2009 09:16:50 -0800 Subject: [PATCH 0281/2556] [ARM] sapphire: Initial import of Sapphire board support Signed-off-by: Thomas Tsai Signed-off-by: San Mehat [ARM] msm: sapphire: gpio: Initialization should be postcore_init, not arch_init Signed-off-by: San Mehat --- arch/arm/mach-msm/Kconfig | 5 + arch/arm/mach-msm/Makefile | 6 + arch/arm/mach-msm/board-sapphire-gpio.c | 326 ++++++ arch/arm/mach-msm/board-sapphire-h2w.c | 545 ++++++++++ arch/arm/mach-msm/board-sapphire-keypad.c | 122 +++ arch/arm/mach-msm/board-sapphire-mmc.c | 479 +++++++++ arch/arm/mach-msm/board-sapphire-panel.c | 656 ++++++++++++ arch/arm/mach-msm/board-sapphire-rfkill.c | 99 ++ arch/arm/mach-msm/board-sapphire-wifi.c | 74 ++ arch/arm/mach-msm/board-sapphire.c | 1069 +++++++++++++++++++- arch/arm/mach-msm/board-sapphire.h | 219 ++++ arch/arm/mach-msm/devices_htc.c | 434 ++++++++ arch/arm/mach-msm/include/mach/board_htc.h | 78 ++ 13 files changed, 4106 insertions(+), 6 deletions(-) create mode 100644 arch/arm/mach-msm/board-sapphire-gpio.c create mode 100644 arch/arm/mach-msm/board-sapphire-h2w.c create mode 100644 arch/arm/mach-msm/board-sapphire-keypad.c create mode 100644 arch/arm/mach-msm/board-sapphire-mmc.c create mode 100644 arch/arm/mach-msm/board-sapphire-panel.c create mode 100644 arch/arm/mach-msm/board-sapphire-rfkill.c create mode 100644 arch/arm/mach-msm/board-sapphire-wifi.c create mode 100644 arch/arm/mach-msm/board-sapphire.h create mode 100644 arch/arm/mach-msm/devices_htc.c create mode 100644 arch/arm/mach-msm/include/mach/board_htc.h diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index ba1cd7fd70b66..f391c760a098a 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -184,6 +184,11 @@ config MSM_SMD_PKG3 config MSM_PROC_COMM bool +config MACH_SAPPHIRE + depends on ARCH_MSM + default y + bool "Sapphire" + config HTC_HEADSET tristate "HTC 2 Wire detection driver" default n diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 3a7cef7409c81..967f25b97f37b 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -6,6 +6,7 @@ obj-y += dma.o endif obj-y += nand_partitions.o +obj-y += devices_htc.o ifdef CONFIG_MSM_VIC obj-y += irq-vic.o @@ -45,6 +46,11 @@ obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o obj-$(CONFIG_MACH_TROUT) += board-trout-rfkill.o obj-$(CONFIG_MACH_TROUT) += board-trout-wifi.o obj-$(CONFIG_TROUT_BATTCHG) += htc_battery.o +obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire.o board-sapphire-gpio.o +obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o +obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-mmc.o board-sapphire-wifi.o +obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-rfkill.o msm_vibrator.o +obj-$(CONFIG_MACH_SAPPHIRE) += devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o diff --git a/arch/arm/mach-msm/board-sapphire-gpio.c b/arch/arm/mach-msm/board-sapphire-gpio.c new file mode 100644 index 0000000000000..375440c5afb9d --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-gpio.c @@ -0,0 +1,326 @@ +/* arch/arm/mach-msm/board-sapphire-gpio.c + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai + * + * 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 "gpio_chip.h" +#include "board-sapphire.h" + +#ifdef DEBUG_SAPPHIRE_GPIO +#define DBG(fmt, arg...) printk(KERN_INFO "%s: " fmt "\n", __func__, ## arg) +#else +#define DBG(fmt, arg...) do {} while (0) +#endif + +#define SAPPHIRE_CPLD_INT_STATUS (SAPPHIRE_CPLD_BASE + 0x0E) +#define SAPPHIRE_CPLD_INT_LEVEL (SAPPHIRE_CPLD_BASE + 0x08) +#define SAPPHIRE_CPLD_INT_MASK (SAPPHIRE_CPLD_BASE + 0x0C) + +/*CPLD misc reg offset*/ +static const int _g_CPLD_MISCn_Offset[] = { 0x0A, /*misc1 reg*/ + 0x00, /*misc2 reg*/ + 0x02, /*misc3 reg*/ + 0x04, /*misc4 reg*/ + 0x06}; /*misc5 reg*/ +/*CPLD INT Bank*/ +/*BANK0: int1 status, int2 level, int3 mask*/ +static const int _g_INT_BANK_Offset[][3] = {{0x0E, 0x08, 0x0C} }; + +static uint8_t sapphire_cpld_initdata[4] = { + [0] = 0x80, /* for serial debug UART3, low current misc2*/ + [1] = 0x34, /* jog & tp enable, I2C pull misc3*/ + [3] = 0x04, /* mmdi 32k en misc5*/ +}; + +/*save current working int mask, so the value can be restored after resume. +Sapphire has only bank0.*/ +static uint8_t sapphire_int_mask[] = { + [0] = 0xfb, /* enable all interrupts, bit 2 is not used */ +}; + +/*Sleep have to prepare the wake up source in advance. +default to disable all wakeup sources when suspend.*/ +static uint8_t sapphire_sleep_int_mask[] = { + [0] = 0x00, /* bit2 is not used */ +}; + +static int sapphire_suspended; + +static int sapphire_gpio_read(struct gpio_chip *chip, unsigned n) +{ + if (n < SAPPHIRE_GPIO_INT_B0_BASE) /*MISCn*/ + return !!(readb(CPLD_GPIO_REG(n)) & CPLD_GPIO_BIT_POS_MASK(n)); + else if (n <= SAPPHIRE_GPIO_END) /*gpio n is INT pin*/ + return !!(readb(CPLD_INT_LEVEL_REG_G(n)) & + CPLD_GPIO_BIT_POS_MASK(n)); + return 0; +} + +/*CPLD Write only register :MISC2, MISC3, MISC4, MISC5 => reg=0,2,4,6 +Reading from write-only registers is undefined, so the writing value +should be kept in shadow for later usage.*/ +int sapphire_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on) +{ + unsigned long flags; + uint8_t reg_val; + if (n > SAPPHIRE_GPIO_END) + return -1; + + local_irq_save(flags); + reg_val = readb(CPLD_GPIO_REG(n)); + if (on) + reg_val |= CPLD_GPIO_BIT_POS_MASK(n); + else + reg_val &= ~CPLD_GPIO_BIT_POS_MASK(n); + writeb(reg_val, CPLD_GPIO_REG(n)); + + DBG("gpio=%d, l=0x%x\r\n", n, readb(SAPPHIRE_CPLD_INT_LEVEL)); + + local_irq_restore(flags); + + return 0; +} + +static int sapphire_gpio_configure(struct gpio_chip *chip, unsigned int gpio, + unsigned long flags) +{ + if (flags & (GPIOF_OUTPUT_LOW | GPIOF_OUTPUT_HIGH)) + sapphire_gpio_write(chip, gpio, flags & GPIOF_OUTPUT_HIGH); + + DBG("gpio=%d, l=0x%x\r\n", gpio, readb(SAPPHIRE_CPLD_INT_LEVEL)); + + return 0; +} + +static int sapphire_gpio_get_irq_num(struct gpio_chip *chip, unsigned int gpio, + unsigned int *irqp, unsigned long *irqnumflagsp) +{ + DBG("gpio=%d, l=0x%x\r\n", gpio, readb(SAPPHIRE_CPLD_INT_LEVEL)); + DBG("SAPPHIRE_GPIO_INT_B0_BASE=%d, SAPPHIRE_GPIO_LAST_INT=%d\r\n", + SAPPHIRE_GPIO_INT_B0_BASE, SAPPHIRE_GPIO_LAST_INT); + if ((gpio < SAPPHIRE_GPIO_INT_B0_BASE) || + (gpio > SAPPHIRE_GPIO_LAST_INT)) + return -ENOENT; + *irqp = SAPPHIRE_GPIO_TO_INT(gpio); + DBG("*irqp=%d\r\n", *irqp); + if (irqnumflagsp) + *irqnumflagsp = 0; + return 0; +} + +/*write 1 to clear INT status bit.*/ +static void sapphire_gpio_irq_ack(unsigned int irq) +{ + /*write 1 to clear*/ + writeb(SAPPHIRE_INT_BIT_MASK(irq), CPLD_INT_STATUS_REG(irq)); +} + +/*unmask/enable the INT +static void sapphire_gpio_irq_unmask(unsigned int irq)*/ +static void sapphire_gpio_irq_enable(unsigned int irq) +{ + unsigned long flags; + uint8_t reg_val; + + local_irq_save(flags); /*disabling all interrupts*/ + + reg_val = readb(CPLD_INT_MASK_REG(irq)) | SAPPHIRE_INT_BIT_MASK(irq); + DBG("(irq=%d,0x%x, 0x%x)\r\n", irq, CPLD_INT_MASK_REG(irq), + SAPPHIRE_INT_BIT_MASK(irq)); + DBG("sapphire_suspended=%d\r\n", sapphire_suspended); + /*printk(KERN_INFO "sapphire_gpio_irq_mask irq %d => %d:%02x\n", + irq, bank, reg_val);*/ + if (!sapphire_suspended) + writeb(reg_val, CPLD_INT_MASK_REG(irq)); + + reg_val = readb(CPLD_INT_MASK_REG(irq)); + DBG("reg_val= 0x%x\r\n", reg_val); + DBG("l=0x%x\r\n", readb(SAPPHIRE_CPLD_INT_LEVEL)); + + local_irq_restore(flags); /*restore the interrupts*/ +} + +/*mask/disable INT +static void sapphire_gpio_irq_mask(unsigned int irq)*/ +static void sapphire_gpio_irq_disable(unsigned int irq) +{ + unsigned long flags; + uint8_t reg_val; + + local_irq_save(flags); + reg_val = readb(CPLD_INT_MASK_REG(irq)) & ~SAPPHIRE_INT_BIT_MASK(irq); + /*CPLD INT MASK is r/w now.*/ + + /*printk(KERN_INFO "sapphire_gpio_irq_unmask irq %d => %d:%02x\n", + irq, bank, reg_val);*/ + DBG("(%d,0x%x, 0x%x, 0x%x)\r\n", irq, reg_val, CPLD_INT_MASK_REG(irq), + SAPPHIRE_INT_BIT_MASK(irq)); + DBG("sapphire_suspended=%d\r\n", sapphire_suspended); + if (!sapphire_suspended) + writeb(reg_val, CPLD_INT_MASK_REG(irq)); + + reg_val = readb(CPLD_INT_MASK_REG(irq)); + DBG("reg_val= 0x%x\r\n", reg_val); + DBG("l=0x%x\r\n", readb(SAPPHIRE_CPLD_INT_LEVEL)); + + local_irq_restore(flags); +} + +/*preparing enable/disable wake source before sleep*/ +int sapphire_gpio_irq_set_wake(unsigned int irq, unsigned int on) +{ + unsigned long flags; + uint8_t mask = SAPPHIRE_INT_BIT_MASK(irq); + + local_irq_save(flags); + + if (on) /*wake on -> mask the bit*/ + sapphire_sleep_int_mask[CPLD_INT_TO_BANK(irq)] |= mask; + else /*no wake -> unmask the bit*/ + sapphire_sleep_int_mask[CPLD_INT_TO_BANK(irq)] &= ~mask; + local_irq_restore(flags); + return 0; +} + +/*Sapphire has only one INT Bank.*/ +static void sapphire_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + int j; + unsigned v; + int int_base = SAPPHIRE_INT_START; + + v = readb(SAPPHIRE_CPLD_INT_STATUS); /*INT1 status reg, BANK0*/ + + for (j = 0; j < 8 ; j++) { /*8 bit per bank*/ + if (v & (1U << j)) { /*got the INT Bit*/ + DBG("generic_handle_irq j=0x%x\r\n", j); + generic_handle_irq(int_base + j); + } + } + + desc->chip->ack(irq); /*clear CPLD INT in SOC side.*/ + DBG("irq=%d, l=0x%x\r\n", irq, readb(SAPPHIRE_CPLD_INT_LEVEL)); +} + +/*Save current working sources before sleep, so we can restore it after + * resume.*/ +static int sapphire_sysdev_suspend(struct sys_device *dev, pm_message_t state) +{ + sapphire_suspended = 1; + /*save current masking*/ + sapphire_int_mask[0] = readb(SAPPHIRE_CPLD_BASE + + SAPPHIRE_GPIO_INT_B0_MASK_REG); + + /*set waking source before sleep.*/ + writeb(sapphire_sleep_int_mask[0], + SAPPHIRE_CPLD_BASE + SAPPHIRE_GPIO_INT_B0_MASK_REG); + + return 0; +} + +/*All the registers will be kept till a power loss...*/ +int sapphire_sysdev_resume(struct sys_device *dev) +{ + /*restore the working mask saved before sleep*/ + writeb(sapphire_int_mask[0], SAPPHIRE_CPLD_BASE + + SAPPHIRE_GPIO_INT_B0_MASK_REG); + sapphire_suspended = 0; + return 0; +} + +/** + * linux/irq.h :: struct irq_chip + * @enable: enable the interrupt (defaults to chip->unmask if NULL) + * @disable: disable the interrupt (defaults to chip->mask if NULL) + * @ack: start of a new interrupt + * @mask: mask an interrupt source + * @mask_ack: ack and mask an interrupt source + * @unmask: unmask an interrupt source + */ +static struct irq_chip sapphire_gpio_irq_chip = { + .name = "sapphiregpio", + .ack = sapphire_gpio_irq_ack, + .mask = sapphire_gpio_irq_disable, /*sapphire_gpio_irq_mask,*/ + .unmask = sapphire_gpio_irq_enable, /*sapphire_gpio_irq_unmask,*/ + .set_wake = sapphire_gpio_irq_set_wake, + /*.set_type = sapphire_gpio_irq_set_type,*/ +}; + +/*Thomas:For CPLD*/ +static struct gpio_chip sapphire_gpio_chip = { + .start = SAPPHIRE_GPIO_START, + .end = SAPPHIRE_GPIO_END, + .configure = sapphire_gpio_configure, + .get_irq_num = sapphire_gpio_get_irq_num, + .read = sapphire_gpio_read, + .write = sapphire_gpio_write, +/* .read_detect_status = sapphire_gpio_read_detect_status, + .clear_detect_status = sapphire_gpio_clear_detect_status */ +}; + +struct sysdev_class sapphire_sysdev_class = { + .name = "sapphiregpio_irq", + .suspend = sapphire_sysdev_suspend, + .resume = sapphire_sysdev_resume, +}; + +static struct sys_device sapphire_irq_device = { + .cls = &sapphire_sysdev_class, +}; + +int sapphire_init_gpio(void) +{ + int i; + if (!machine_is_sapphire()) + return 0; + + DBG("%d,%d\r\n", SAPPHIRE_INT_START, SAPPHIRE_INT_END); + DBG("NR_MSM_IRQS=%d, NR_GPIO_IRQS=%d\r\n", NR_MSM_IRQS, NR_GPIO_IRQS); + for (i = SAPPHIRE_INT_START; i <= SAPPHIRE_INT_END; i++) { + set_irq_chip(i, &sapphire_gpio_irq_chip); + set_irq_handler(i, handle_edge_irq); + set_irq_flags(i, IRQF_VALID); + } + + register_gpio_chip(&sapphire_gpio_chip); + + /*setup CPLD INT connecting to SOC's gpio 17 */ + set_irq_type(MSM_GPIO_TO_INT(17), IRQF_TRIGGER_HIGH); + set_irq_chained_handler(MSM_GPIO_TO_INT(17), sapphire_gpio_irq_handler); + set_irq_wake(MSM_GPIO_TO_INT(17), 1); + + if (sysdev_class_register(&sapphire_sysdev_class) == 0) + sysdev_register(&sapphire_irq_device); + + return 0; +} + +int sapphire_init_cpld(unsigned int sys_rev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sapphire_cpld_initdata); i++) + writeb(sapphire_cpld_initdata[i], SAPPHIRE_CPLD_BASE + i * 2); + return 0; +} + +postcore_initcall(sapphire_init_gpio); diff --git a/arch/arm/mach-msm/board-sapphire-h2w.c b/arch/arm/mach-msm/board-sapphire-h2w.c new file mode 100644 index 0000000000000..aa83e216974dc --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-h2w.c @@ -0,0 +1,545 @@ +/* + * H2W device detection driver. + * + * Copyright (C) 2008 HTC Corporation. + * Copyright (C) 2008 Google, Inc. + * + * Authors: + * Laurence Chen + * Nick Pelly + * + * 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. + * + */ + +/* For detecting HTC 2 Wire devices, such as wired headset. + + Logically, the H2W driver is always present, and H2W state (hi->state) + indicates what is currently plugged into the H2W interface. + + When the headset is plugged in, CABLE_IN1 is pulled low. When the headset + button is pressed, CABLE_IN2 is pulled low. These two lines are shared with + the TX and RX (respectively) of UART3 - used for serial debugging. + + This headset driver keeps the CPLD configured as UART3 for as long as + possible, so that we can do serial FIQ debugging even when the kernel is + locked and this driver no longer runs. So it only configures the CPLD to + GPIO while the headset is plugged in, and for 10ms during detection work. + + Unfortunately we can't leave the CPLD as UART3 while a headset is plugged + in, UART3 is pullup on TX but the headset is pull-down, causing a 55 mA + drain on sapphire. + + The headset detection work involves setting CPLD to GPIO, and then pulling + CABLE_IN1 high with a stronger pullup than usual. A H2W headset will still + pull this line low, whereas other attachments such as a serial console + would get pulled up by this stronger pullup. + + Headset insertion/removal causes UEvent's to be sent, and + /sys/class/switch/h2w/state to be updated. + + Button presses are interpreted as input event (KEY_MEDIA). Button presses + are ignored if the headset is plugged in, so the buttons on 11 pin -> 3.5mm + jack adapters do not work until a headset is plugged into the adapter. This + is to avoid serial RX traffic causing spurious button press events. + + We tend to check the status of CABLE_IN1 a few more times than strictly + necessary during headset detection, to avoid spurious headset insertion + events caused by serial debugger TX traffic. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "board-sapphire.h" + +#ifdef CONFIG_DEBUG_SAPPHIRE_H2W +#define H2W_DBG(fmt, arg...) printk(KERN_INFO "[H2W] %s " fmt "\n", __FUNCTION__, ## arg) +#else +#define H2W_DBG(fmt, arg...) do {} while (0) +#endif + +static struct workqueue_struct *g_detection_work_queue; +static void detection_work(struct work_struct *work); +static DECLARE_WORK(g_detection_work, detection_work); +enum { + NO_DEVICE = 0, + HTC_HEADSET = 1, +}; + +enum { + UART3 = 0, + GPIO = 1, +}; + +struct h2w_info { + struct switch_dev sdev; + struct input_dev *input; + + atomic_t btn_state; + int ignore_btn; + + unsigned int irq; + unsigned int irq_btn; + + struct hrtimer timer; + ktime_t debounce_time; + + struct hrtimer btn_timer; + ktime_t btn_debounce_time; +}; +static struct h2w_info *hi; + +static ssize_t sapphire_h2w_print_name(struct switch_dev *sdev, char *buf) +{ + switch (switch_get_state(&hi->sdev)) { + case NO_DEVICE: + return sprintf(buf, "No Device\n"); + case HTC_HEADSET: + return sprintf(buf, "Headset\n"); + } + return -EINVAL; +} + +static void configure_cpld(int route) +{ + H2W_DBG(" route = %s", route == UART3 ? "UART3" : "GPIO"); + switch (route) { + case UART3: + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 1); + break; + case GPIO: + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 0); + break; + } +} + +static void button_pressed(void) +{ + H2W_DBG(""); + atomic_set(&hi->btn_state, 1); + input_report_key(hi->input, KEY_MEDIA, 1); + input_sync(hi->input); +} + +static void button_released(void) +{ + H2W_DBG(""); + atomic_set(&hi->btn_state, 0); + input_report_key(hi->input, KEY_MEDIA, 0); + input_sync(hi->input); +} + +#ifdef CONFIG_MSM_SERIAL_DEBUGGER +extern void msm_serial_debug_enable(int); +#endif + +static void insert_headset(void) +{ + unsigned long irq_flags; + + H2W_DBG(""); + + switch_set_state(&hi->sdev, HTC_HEADSET); + configure_cpld(GPIO); + +#ifdef CONFIG_MSM_SERIAL_DEBUGGER + msm_serial_debug_enable(false); +#endif + + + /* On some non-standard headset adapters (usually those without a + * button) the btn line is pulled down at the same time as the detect + * line. We can check here by sampling the button line, if it is + * low then it is probably a bad adapter so ignore the button. + * If the button is released then we stop ignoring the button, so that + * the user can recover from the situation where a headset is plugged + * in with button held down. + */ + hi->ignore_btn = !gpio_get_value(SAPPHIRE_GPIO_CABLE_IN2); + + /* Enable button irq */ + local_irq_save(irq_flags); + enable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + + hi->debounce_time = ktime_set(0, 20000000); /* 20 ms */ +} + +static void remove_headset(void) +{ + unsigned long irq_flags; + + H2W_DBG(""); + + switch_set_state(&hi->sdev, NO_DEVICE); + configure_cpld(UART3); + + /* Disable button */ + local_irq_save(irq_flags); + disable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + + if (atomic_read(&hi->btn_state)) + button_released(); + + hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ +} + +static void detection_work(struct work_struct *work) +{ + unsigned long irq_flags; + int clk, cable_in1; + + H2W_DBG(""); + + if (gpio_get_value(SAPPHIRE_GPIO_CABLE_IN1) != 0) { + /* Headset not plugged in */ + if (switch_get_state(&hi->sdev) == HTC_HEADSET) + remove_headset(); + return; + } + + /* Something plugged in, lets make sure its a headset */ + + /* Switch CPLD to GPIO to do detection */ + configure_cpld(GPIO); + /* Disable headset interrupt while detecting.*/ + local_irq_save(irq_flags); + disable_irq(hi->irq); + local_irq_restore(irq_flags); + + /* Set GPIO_CABLE_IN1 as output high */ + gpio_direction_output(SAPPHIRE_GPIO_CABLE_IN1, 1); + /* Delay 10ms for pin stable. */ + msleep(10); + /* Save H2W_CLK */ + clk = gpio_get_value(SAPPHIRE_GPIO_H2W_CLK_GPI); + /* Set GPIO_CABLE_IN1 as input */ + gpio_direction_input(SAPPHIRE_GPIO_CABLE_IN1); + + /* Restore IRQs */ + local_irq_save(irq_flags); + enable_irq(hi->irq); + local_irq_restore(irq_flags); + + cable_in1 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN1); + + if (cable_in1 == 0 && clk == 0) { + if (switch_get_state(&hi->sdev) == NO_DEVICE) + insert_headset(); + } else { + configure_cpld(UART3); + H2W_DBG("CABLE_IN1 was low, but not a headset " + "(recent cable_in1 = %d, clk = %d)", cable_in1, clk); + } +} + +static enum hrtimer_restart button_event_timer_func(struct hrtimer *data) +{ + H2W_DBG(""); + + if (switch_get_state(&hi->sdev) == HTC_HEADSET) { + if (gpio_get_value(SAPPHIRE_GPIO_CABLE_IN2)) { + if (hi->ignore_btn) + hi->ignore_btn = 0; + else if (atomic_read(&hi->btn_state)) + button_released(); + } else { + if (!hi->ignore_btn && !atomic_read(&hi->btn_state)) + button_pressed(); + } + } + + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart detect_event_timer_func(struct hrtimer *data) +{ + H2W_DBG(""); + + queue_work(g_detection_work_queue, &g_detection_work); + return HRTIMER_NORESTART; +} + +static irqreturn_t detect_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + H2W_DBG(""); + do { + value1 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN1); + set_irq_type(hi->irq, value1 ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN1); + } while (value1 != value2 && retry_limit-- > 0); + + H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit)); + + if ((switch_get_state(&hi->sdev) == NO_DEVICE) ^ value2) { + if (switch_get_state(&hi->sdev) == HTC_HEADSET) + hi->ignore_btn = 1; + /* Do the rest of the work in timer context */ + hrtimer_start(&hi->timer, hi->debounce_time, HRTIMER_MODE_REL); + } + + return IRQ_HANDLED; +} + +static irqreturn_t button_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + H2W_DBG(""); + do { + value1 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN2); + set_irq_type(hi->irq_btn, value1 ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN2); + } while (value1 != value2 && retry_limit-- > 0); + + H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit)); + + hrtimer_start(&hi->btn_timer, hi->btn_debounce_time, HRTIMER_MODE_REL); + + return IRQ_HANDLED; +} + +#if defined(CONFIG_DEBUG_FS) +static void h2w_debug_set(void *data, u64 val) +{ + switch_set_state(&hi->sdev, (int)val); +} + +static u64 h2w_debug_get(void *data) +{ + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(h2w_debug_fops, h2w_debug_get, h2w_debug_set, "%llu\n"); +static int __init h2w_debug_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("h2w", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("state", 0644, dent, NULL, &h2w_debug_fops); + + return 0; +} + +device_initcall(h2w_debug_init); +#endif + +static int sapphire_h2w_probe(struct platform_device *pdev) +{ + int ret; + unsigned long irq_flags; + + printk(KERN_INFO "H2W: Registering H2W (headset) driver\n"); + hi = kzalloc(sizeof(struct h2w_info), GFP_KERNEL); + if (!hi) + return -ENOMEM; + + atomic_set(&hi->btn_state, 0); + hi->ignore_btn = 0; + + hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ + hi->btn_debounce_time = ktime_set(0, 10000000); /* 10 ms */ + hi->sdev.name = "h2w"; + hi->sdev.print_name = sapphire_h2w_print_name; + + ret = switch_dev_register(&hi->sdev); + if (ret < 0) + goto err_switch_dev_register; + + g_detection_work_queue = create_workqueue("detection"); + if (g_detection_work_queue == NULL) { + ret = -ENOMEM; + goto err_create_work_queue; + } + + ret = gpio_request(SAPPHIRE_GPIO_CABLE_IN1, "h2w_detect"); + if (ret < 0) + goto err_request_detect_gpio; + + ret = gpio_request(SAPPHIRE_GPIO_CABLE_IN2, "h2w_button"); + if (ret < 0) + goto err_request_button_gpio; + + ret = gpio_direction_input(SAPPHIRE_GPIO_CABLE_IN1); + if (ret < 0) + goto err_set_detect_gpio; + + ret = gpio_direction_input(SAPPHIRE_GPIO_CABLE_IN2); + if (ret < 0) + goto err_set_button_gpio; + + hi->irq = gpio_to_irq(SAPPHIRE_GPIO_CABLE_IN1); + if (hi->irq < 0) { + ret = hi->irq; + goto err_get_h2w_detect_irq_num_failed; + } + + hi->irq_btn = gpio_to_irq(SAPPHIRE_GPIO_CABLE_IN2); + if (hi->irq_btn < 0) { + ret = hi->irq_btn; + goto err_get_button_irq_num_failed; + } + + /* Set CPLD MUX to H2W <-> CPLD GPIO */ + configure_cpld(UART3); + /* Set the CPLD connected H2W GPIO's to input */ + gpio_set_value(SAPPHIRE_GPIO_H2W_CLK_DIR, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_DAT_DIR, 0); + + hrtimer_init(&hi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hi->timer.function = detect_event_timer_func; + hrtimer_init(&hi->btn_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hi->btn_timer.function = button_event_timer_func; + + ret = request_irq(hi->irq, detect_irq_handler, + IRQF_TRIGGER_LOW, "h2w_detect", NULL); + if (ret < 0) + goto err_request_detect_irq; + + /* Disable button until plugged in */ + set_irq_flags(hi->irq_btn, IRQF_VALID | IRQF_NOAUTOEN); + ret = request_irq(hi->irq_btn, button_irq_handler, + IRQF_TRIGGER_LOW, "h2w_button", NULL); + if (ret < 0) + goto err_request_h2w_headset_button_irq; + + ret = set_irq_wake(hi->irq, 1); + if (ret < 0) + goto err_request_input_dev; + ret = set_irq_wake(hi->irq_btn, 1); + if (ret < 0) + goto err_request_input_dev; + + hi->input = input_allocate_device(); + if (!hi->input) { + ret = -ENOMEM; + goto err_request_input_dev; + } + + hi->input->name = "h2w headset"; + hi->input->evbit[0] = BIT_MASK(EV_KEY); + hi->input->keybit[BIT_WORD(KEY_MEDIA)] = BIT_MASK(KEY_MEDIA); + + ret = input_register_device(hi->input); + if (ret < 0) + goto err_register_input_dev; + + return 0; + +err_register_input_dev: + input_free_device(hi->input); +err_request_input_dev: + free_irq(hi->irq_btn, 0); +err_request_h2w_headset_button_irq: + free_irq(hi->irq, 0); +err_request_detect_irq: +err_get_button_irq_num_failed: +err_get_h2w_detect_irq_num_failed: +err_set_button_gpio: +err_set_detect_gpio: + gpio_free(SAPPHIRE_GPIO_CABLE_IN2); +err_request_button_gpio: + gpio_free(SAPPHIRE_GPIO_CABLE_IN1); +err_request_detect_gpio: + destroy_workqueue(g_detection_work_queue); +err_create_work_queue: + switch_dev_unregister(&hi->sdev); +err_switch_dev_register: + printk(KERN_ERR "H2W: Failed to register driver\n"); + + return ret; +} + +static int sapphire_h2w_remove(struct platform_device *pdev) +{ + H2W_DBG(""); + if (switch_get_state(&hi->sdev)) + remove_headset(); + input_unregister_device(hi->input); + gpio_free(SAPPHIRE_GPIO_CABLE_IN2); + gpio_free(SAPPHIRE_GPIO_CABLE_IN1); + free_irq(hi->irq_btn, 0); + free_irq(hi->irq, 0); + destroy_workqueue(g_detection_work_queue); + switch_dev_unregister(&hi->sdev); + + return 0; +} + +static struct platform_device sapphire_h2w_device = { + .name = "sapphire-h2w", +}; + +static struct platform_driver sapphire_h2w_driver = { + .probe = sapphire_h2w_probe, + .remove = sapphire_h2w_remove, + .driver = { + .name = "sapphire-h2w", + .owner = THIS_MODULE, + }, +}; + +static int __init sapphire_h2w_init(void) +{ + if (!machine_is_sapphire()) + return 0; + int ret; + H2W_DBG(""); + ret = platform_driver_register(&sapphire_h2w_driver); + if (ret) + return ret; + return platform_device_register(&sapphire_h2w_device); +} + +static void __exit sapphire_h2w_exit(void) +{ + platform_device_unregister(&sapphire_h2w_device); + platform_driver_unregister(&sapphire_h2w_driver); +} + +module_init(sapphire_h2w_init); +module_exit(sapphire_h2w_exit); + +MODULE_AUTHOR("Laurence Chen "); +MODULE_DESCRIPTION("HTC 2 Wire detection driver for sapphire"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-sapphire-keypad.c b/arch/arm/mach-msm/board-sapphire-keypad.c new file mode 100644 index 0000000000000..14f12e5e865c4 --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-keypad.c @@ -0,0 +1,122 @@ +/* arch/arm/mach-msm/board-sapphire-keypad.c + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai + * + * 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 "gpio_chip.h" +#include "board-sapphire.h" +static char *keycaps = "--qwerty"; +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "board_sapphire." +module_param_named(keycaps, keycaps, charp, 0); + + +static unsigned int sapphire_col_gpios[] = { 35, 34 }; + +/* KP_MKIN2 (GPIO40) is not used? */ +static unsigned int sapphire_row_gpios[] = { 42, 41 }; + +#define KEYMAP_INDEX(col, row) ((col)*ARRAY_SIZE(sapphire_row_gpios) + (row)) + +/*scan matrix key*/ +/* HOME(up) + MENU (down)*/ +static const unsigned short sapphire_keymap1[ARRAY_SIZE(sapphire_col_gpios) * + ARRAY_SIZE(sapphire_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_BACK, + [KEYMAP_INDEX(0, 1)] = KEY_MENU, + + [KEYMAP_INDEX(1, 0)] = KEY_HOME, + [KEYMAP_INDEX(1, 1)] = KEY_SEND, +}; + +/* MENU(up) + HOME (down)*/ +static const unsigned short sapphire_keymap0[ARRAY_SIZE(sapphire_col_gpios) * + ARRAY_SIZE(sapphire_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_BACK, + [KEYMAP_INDEX(0, 1)] = KEY_HOME, + + [KEYMAP_INDEX(1, 0)] = KEY_MENU, + [KEYMAP_INDEX(1, 1)] = KEY_SEND, +}; + +static struct gpio_event_matrix_info sapphire_keypad_matrix_info = { + .info.func = gpio_event_matrix_func, + .keymap = sapphire_keymap1, + .output_gpios = sapphire_col_gpios, + .input_gpios = sapphire_row_gpios, + .noutputs = ARRAY_SIZE(sapphire_col_gpios), + .ninputs = ARRAY_SIZE(sapphire_row_gpios), + .settle_time.tv.nsec = 40 * NSEC_PER_USEC, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .debounce_delay.tv.nsec = 50 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | + GPIOKPF_REMOVE_PHANTOM_KEYS | + GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/ +}; + +static struct gpio_event_direct_entry sapphire_keypad_nav_map[] = { + { SAPPHIRE_POWER_KEY, KEY_END }, + { SAPPHIRE_VOLUME_UP, KEY_VOLUMEUP }, + { SAPPHIRE_VOLUME_DOWN, KEY_VOLUMEDOWN }, +}; + +static struct gpio_event_input_info sapphire_keypad_nav_info = { + .info.func = gpio_event_input_func, + .flags = 0, + .type = EV_KEY, + .keymap = sapphire_keypad_nav_map, + .debounce_time.tv.nsec = 20 * NSEC_PER_MSEC, + .keymap_size = ARRAY_SIZE(sapphire_keypad_nav_map) +}; + +static struct gpio_event_info *sapphire_keypad_info[] = { + &sapphire_keypad_matrix_info.info, + &sapphire_keypad_nav_info.info, +}; + +static struct gpio_event_platform_data sapphire_keypad_data = { + .name = "sapphire-keypad", + .info = sapphire_keypad_info, + .info_count = ARRAY_SIZE(sapphire_keypad_info) +}; + +static struct platform_device sapphire_keypad_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = 0, + .dev = { + .platform_data = &sapphire_keypad_data, + }, +}; + +static int __init sapphire_init_keypad(void) +{ + if (!machine_is_sapphire()) + return 0; + + switch (sapphire_get_hwid()) { + case 0: + sapphire_keypad_matrix_info.keymap = sapphire_keymap0; + break; + default: + sapphire_keypad_matrix_info.keymap = sapphire_keymap1; + break; + } + return platform_device_register(&sapphire_keypad_device); +} + +device_initcall(sapphire_init_keypad); + diff --git a/arch/arm/mach-msm/board-sapphire-mmc.c b/arch/arm/mach-msm/board-sapphire-mmc.c new file mode 100644 index 0000000000000..ff2a5fdc6c140 --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-mmc.c @@ -0,0 +1,479 @@ +/* linux/arch/arm/mach-msm/board-sapphire-mmc.c + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai + * + * 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 "devices.h" +#include "gpio_chip.h" +#include "board-sapphire.h" +#include "proc_comm.h" + +#define DEBUG_SDSLOT_VDD 1 + +extern int msm_add_sdcc(unsigned int controller, + struct mmc_platform_data *plat); + +/* ---- COMMON ---- */ +static void config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for (n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +/* ---- SDCARD ---- */ + +static uint32_t sdcard_on_gpio_table[] = { + PCOM_GPIO_CFG(62, 2, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(63, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(64, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ +}; + +static uint32_t sdcard_off_gpio_table[] = { + PCOM_GPIO_CFG(62, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(63, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(64, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ +}; + +static uint opt_disable_sdcard; + +static int __init sapphire_disablesdcard_setup(char *str) +{ + int cal = simple_strtol(str, NULL, 0); + + opt_disable_sdcard = cal; + return 1; +} + +__setup("board_sapphire.disable_sdcard=", sapphire_disablesdcard_setup); + +static struct vreg *vreg_sdslot; /* SD slot power */ + +struct mmc_vdd_xlat { + int mask; + int level; +}; + +static struct mmc_vdd_xlat mmc_vdd_table[] = { + { MMC_VDD_165_195, 1800 }, + { MMC_VDD_20_21, 2050 }, + { MMC_VDD_21_22, 2150 }, + { MMC_VDD_22_23, 2250 }, + { MMC_VDD_23_24, 2350 }, + { MMC_VDD_24_25, 2450 }, + { MMC_VDD_25_26, 2550 }, + { MMC_VDD_26_27, 2650 }, + { MMC_VDD_27_28, 2750 }, + { MMC_VDD_28_29, 2850 }, + { MMC_VDD_29_30, 2950 }, +}; + +static unsigned int sdslot_vdd = 0xffffffff; +static unsigned int sdslot_vreg_enabled; + +static uint32_t sapphire_sdslot_switchvdd(struct device *dev, unsigned int vdd) +{ + int i, rc; + + BUG_ON(!vreg_sdslot); + + if (vdd == sdslot_vdd) + return 0; + + sdslot_vdd = vdd; + + if (vdd == 0) { +#if DEBUG_SDSLOT_VDD + printk(KERN_DEBUG "%s: Disabling SD slot power\n", __func__); +#endif + config_gpio_table(sdcard_off_gpio_table, + ARRAY_SIZE(sdcard_off_gpio_table)); + vreg_disable(vreg_sdslot); + sdslot_vreg_enabled = 0; + return 0; + } + + if (!sdslot_vreg_enabled) { + rc = vreg_enable(vreg_sdslot); + if (rc) { + printk(KERN_ERR "%s: Error enabling vreg (%d)\n", + __func__, rc); + } + config_gpio_table(sdcard_on_gpio_table, + ARRAY_SIZE(sdcard_on_gpio_table)); + sdslot_vreg_enabled = 1; + } + + for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) { + if (mmc_vdd_table[i].mask == (1 << vdd)) { +#if DEBUG_SDSLOT_VDD + printk(KERN_DEBUG "%s: Setting level to %u\n", + __func__, mmc_vdd_table[i].level); +#endif + rc = vreg_set_level(vreg_sdslot, + mmc_vdd_table[i].level); + if (rc) { + printk(KERN_ERR + "%s: Error setting vreg level (%d)\n", + __func__, rc); + } + return 0; + } + } + + printk(KERN_ERR "%s: Invalid VDD %d specified\n", __func__, vdd); + return 0; +} + +static unsigned int sapphire_sdslot_status(struct device *dev) +{ + unsigned int status; + + status = (unsigned int) gpio_get_value(SAPPHIRE_GPIO_SDMC_CD_N); + return !status; +} + +#define SAPPHIRE_MMC_VDD (MMC_VDD_165_195 | MMC_VDD_20_21 | MMC_VDD_21_22 \ + | MMC_VDD_22_23 | MMC_VDD_23_24 | MMC_VDD_24_25 \ + | MMC_VDD_25_26 | MMC_VDD_26_27 | MMC_VDD_27_28 \ + | MMC_VDD_28_29 | MMC_VDD_29_30) + +static struct mmc_platform_data sapphire_sdslot_data = { + .ocr_mask = SAPPHIRE_MMC_VDD, + .status_irq = SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_SDMC_CD_N), + .status = sapphire_sdslot_status, + .translate_vdd = sapphire_sdslot_switchvdd, +}; + +/* ---- WIFI ---- */ + +static uint32_t wifi_on_gpio_table[] = { + PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(29, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +static uint32_t wifi_off_gpio_table[] = { + PCOM_GPIO_CFG(51, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(56, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(29, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +static struct vreg *vreg_wifi_osc; /* WIFI 32khz oscilator */ +static int sapphire_wifi_cd = 0; /* WIFI virtual 'card detect' status */ + +static struct sdio_embedded_func wifi_func = { + .f_class = SDIO_CLASS_WLAN, + .f_maxblksize = 512, +}; + +static struct embedded_sdio_data sapphire_wifi_emb_data = { + .cis = { + .vendor = 0x104c, + .device = 0x9066, + .blksize = 512, + .max_dtr = 20000000, + }, + .cccr = { + .multi_block = 0, + .low_speed = 0, + .wide_bus = 1, + .high_power = 0, + .high_speed = 0, + }, + .funcs = &wifi_func, + .num_funcs = 1, +}; + +static void (*wifi_status_cb)(int card_present, void *dev_id); +static void *wifi_status_cb_devid; + +static int sapphire_wifi_status_register(void (*callback)(int card_present, + void *dev_id), + void *dev_id) +{ + if (wifi_status_cb) + return -EAGAIN; + wifi_status_cb = callback; + wifi_status_cb_devid = dev_id; + return 0; +} + +static unsigned int sapphire_wifi_status(struct device *dev) +{ + return sapphire_wifi_cd; +} + +int sapphire_wifi_set_carddetect(int val) +{ + printk(KERN_DEBUG "%s: %d\n", __func__, val); + sapphire_wifi_cd = val; + if (wifi_status_cb) + wifi_status_cb(val, wifi_status_cb_devid); + else + printk(KERN_WARNING "%s: Nobody to notify\n", __func__); + return 0; +} +#ifndef CONFIG_WIFI_CONTROL_FUNC +EXPORT_SYMBOL(sapphire_wifi_set_carddetect); +#endif + +static int sapphire_wifi_power_state; +static int sapphire_bt_power_state; + +int sapphire_wifi_power(int on) +{ + int rc; + + printk(KERN_DEBUG "%s: %d\n", __func__, on); + + if (on) { + config_gpio_table(wifi_on_gpio_table, + ARRAY_SIZE(wifi_on_gpio_table)); + rc = vreg_enable(vreg_wifi_osc); + if (rc) + return rc; + htc_pwrsink_set(PWRSINK_WIFI, 70); + } else { + config_gpio_table(wifi_off_gpio_table, + ARRAY_SIZE(wifi_off_gpio_table)); + htc_pwrsink_set(PWRSINK_WIFI, 0); + } + gpio_set_value(SAPPHIRE_GPIO_MAC_32K_EN, on); + mdelay(100); + gpio_set_value(SAPPHIRE_GPIO_WIFI_EN, on); + mdelay(100); + if (!on) + vreg_disable(vreg_wifi_osc); + sapphire_wifi_power_state = on; + return 0; +} +#ifndef CONFIG_WIFI_CONTROL_FUNC +EXPORT_SYMBOL(sapphire_wifi_power); +#endif + +/* Eenable VREG_MMC pin to turn on fastclock oscillator : colin */ +int sapphire_bt_fastclock_power(int on) +{ + int rc; + + printk(KERN_DEBUG "sapphire_bt_fastclock_power on = %d\n", on); + if (vreg_wifi_osc) { + if (on) { + rc = vreg_enable(vreg_wifi_osc); + printk(KERN_DEBUG "BT vreg_enable vreg_mmc, rc=%d\n", + rc); + if (rc) { + printk("Error turn sapphire_bt_fastclock_power rc=%d\n", rc); + return rc; + } + } else { + if (!sapphire_wifi_power_state) { + vreg_disable(vreg_wifi_osc); + printk(KERN_DEBUG "BT disable vreg_wifi_osc.\n"); + } else + printk(KERN_DEBUG "BT shouldn't disable vreg_wifi_osc. WiFi is using it!!\n"); + } + } + sapphire_bt_power_state = on; + return 0; +} +EXPORT_SYMBOL(sapphire_bt_fastclock_power); + +static int sapphire_wifi_reset_state; +void sapphire_wifi_reset(int on) +{ + printk(KERN_DEBUG "%s: %d\n", __func__, on); + gpio_set_value(SAPPHIRE_GPIO_WIFI_PA_RESETX, !on); + sapphire_wifi_reset_state = on; + mdelay(50); +} +#ifndef CONFIG_WIFI_CONTROL_FUNC +EXPORT_SYMBOL(sapphire_wifi_reset); +#endif + +static struct mmc_platform_data sapphire_wifi_data = { + .ocr_mask = MMC_VDD_28_29, + .status = sapphire_wifi_status, + .register_status_notify = sapphire_wifi_status_register, + .embedded_sdio = &sapphire_wifi_emb_data, +}; + +int __init sapphire_init_mmc(unsigned int sys_rev) +{ + wifi_status_cb = NULL; + + sdslot_vreg_enabled = 0; + + vreg_sdslot = vreg_get(0, "gp6"); + if (IS_ERR(vreg_sdslot)) + return PTR_ERR(vreg_sdslot); + vreg_wifi_osc = vreg_get(0, "mmc"); + if (IS_ERR(vreg_wifi_osc)) + return PTR_ERR(vreg_wifi_osc); + + set_irq_wake(SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_SDMC_CD_N), 1); + + msm_add_sdcc(1, &sapphire_wifi_data); + + if (!opt_disable_sdcard) + msm_add_sdcc(2, &sapphire_sdslot_data); + else + printk(KERN_INFO "sapphire: SD-Card interface disabled\n"); + return 0; +} + +#if defined(CONFIG_DEBUG_FS) +static int sapphiremmc_dbg_wifi_reset_set(void *data, u64 val) +{ + sapphire_wifi_reset((int) val); + return 0; +} + +static int sapphiremmc_dbg_wifi_reset_get(void *data, u64 *val) +{ + *val = sapphire_wifi_reset_state; + return 0; +} + +static int sapphiremmc_dbg_wifi_cd_set(void *data, u64 val) +{ + sapphire_wifi_set_carddetect((int) val); + return 0; +} + +static int sapphiremmc_dbg_wifi_cd_get(void *data, u64 *val) +{ + *val = sapphire_wifi_cd; + return 0; +} + +static int sapphiremmc_dbg_wifi_pwr_set(void *data, u64 val) +{ + sapphire_wifi_power((int) val); + return 0; +} + +static int sapphiremmc_dbg_wifi_pwr_get(void *data, u64 *val) +{ + + *val = sapphire_wifi_power_state; + return 0; +} + +static int sapphiremmc_dbg_sd_pwr_set(void *data, u64 val) +{ + sapphire_sdslot_switchvdd(NULL, (unsigned int) val); + return 0; +} + +static int sapphiremmc_dbg_sd_pwr_get(void *data, u64 *val) +{ + *val = sdslot_vdd; + return 0; +} + +static int sapphiremmc_dbg_sd_cd_set(void *data, u64 val) +{ + return -ENOSYS; +} + +static int sapphiremmc_dbg_sd_cd_get(void *data, u64 *val) +{ + *val = sapphire_sdslot_status(NULL); + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_wifi_reset_fops, + sapphiremmc_dbg_wifi_reset_get, + sapphiremmc_dbg_wifi_reset_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_wifi_cd_fops, + sapphiremmc_dbg_wifi_cd_get, + sapphiremmc_dbg_wifi_cd_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_wifi_pwr_fops, + sapphiremmc_dbg_wifi_pwr_get, + sapphiremmc_dbg_wifi_pwr_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_sd_pwr_fops, + sapphiremmc_dbg_sd_pwr_get, + sapphiremmc_dbg_sd_pwr_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_sd_cd_fops, + sapphiremmc_dbg_sd_cd_get, + sapphiremmc_dbg_sd_cd_set, "%llu\n"); + +static int __init sapphiremmc_dbg_init(void) +{ + struct dentry *dent; + + if (!machine_is_sapphire()) + return 0; + + dent = debugfs_create_dir("sapphiremmc_dbg", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("wifi_reset", 0644, dent, NULL, + &sapphiremmc_dbg_wifi_reset_fops); + debugfs_create_file("wifi_cd", 0644, dent, NULL, + &sapphiremmc_dbg_wifi_cd_fops); + debugfs_create_file("wifi_pwr", 0644, dent, NULL, + &sapphiremmc_dbg_wifi_pwr_fops); + + debugfs_create_file("sd_pwr", 0644, dent, NULL, + &sapphiremmc_dbg_sd_pwr_fops); + debugfs_create_file("sd_cd", 0644, dent, NULL, + &sapphiremmc_dbg_sd_cd_fops); + + return 0; +} + +device_initcall(sapphiremmc_dbg_init); + +#endif diff --git a/arch/arm/mach-msm/board-sapphire-panel.c b/arch/arm/mach-msm/board-sapphire-panel.c new file mode 100644 index 0000000000000..24933f3eab52e --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-panel.c @@ -0,0 +1,656 @@ +/* linux/arch/arm/mach-msm/board-sapphire-panel.c + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai + * + * 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 "gpio_chip.h" +#include "board-sapphire.h" +#include "proc_comm.h" +#include "devices.h" + +enum sapphire_panel_type { + SAPPHIRE_PANEL_SHARP = 0, + SAPPHIRE_PANEL_TOPPOLY, + NUM_OF_SAPPHIRE_PANELS, +}; +static int g_panel_id = -1 ; + +#define SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS 132 + +static int sapphire_backlight_off; +static int sapphire_backlight_brightness = + SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS; + +static uint8_t sapphire_backlight_last_level = 33; +static DEFINE_MUTEX(sapphire_backlight_lock); + +/* Divid dimming level into 12 sections, and restrict maximum level to 27 */ +#define DIMMING_STEPS 12 +static unsigned dimming_levels[NUM_OF_SAPPHIRE_PANELS][DIMMING_STEPS] = { + {0, 1, 2, 3, 6, 9, 11, 13, 16, 19, 22, 25}, /* Sharp */ + {0, 1, 2, 4, 7, 10, 13, 15, 18, 21, 24, 27}, /* Toppolly */ +}; +static unsigned pwrsink_percents[] = {0, 6, 8, 15, 26, 34, 46, 54, 65, 77, 87, + 100}; + +static void sapphire_set_backlight_level(uint8_t level) +{ + unsigned dimming_factor = 255/DIMMING_STEPS + 1 ; + unsigned percent = ((int)level * 100) / 255; + unsigned long flags; + int i = 0; + + printk(KERN_INFO "level=%d, new level=dimming_levels[%d]=%d\n", + level, level/dimming_factor, + dimming_levels[g_panel_id][level/dimming_factor]); + percent = pwrsink_percents[level/dimming_factor]; + level = dimming_levels[g_panel_id][level/dimming_factor]; + + if (sapphire_backlight_last_level == level) + return; + + if (level == 0) { + gpio_set_value(27, 0); + msleep(2); + } else { + local_irq_save(flags); + if (sapphire_backlight_last_level == 0) { + gpio_set_value(27, 1); + udelay(40); + sapphire_backlight_last_level = 33; + } + i = (sapphire_backlight_last_level - level + 33) % 33; + while (i-- > 0) { + gpio_set_value(27, 0); + udelay(1); + gpio_set_value(27, 1); + udelay(1); + } + local_irq_restore(flags); + } + sapphire_backlight_last_level = level; + htc_pwrsink_set(PWRSINK_BACKLIGHT, percent); +} + +#define MDDI_CLIENT_CORE_BASE 0x108000 +#define LCD_CONTROL_BLOCK_BASE 0x110000 +#define SPI_BLOCK_BASE 0x120000 +#define I2C_BLOCK_BASE 0x130000 +#define PWM_BLOCK_BASE 0x140000 +#define GPIO_BLOCK_BASE 0x150000 +#define SYSTEM_BLOCK1_BASE 0x160000 +#define SYSTEM_BLOCK2_BASE 0x170000 + + +#define DPSUS (MDDI_CLIENT_CORE_BASE|0x24) +#define SYSCLKENA (MDDI_CLIENT_CORE_BASE|0x2C) +#define PWM0OFF (PWM_BLOCK_BASE|0x1C) + +#define V_VDDE2E_VDD2_GPIO 0 +#define V_VDDE2E_VDD2_GPIO_5M 89 +#define MDDI_RST_N 82 + +#define MDDICAP0 (MDDI_CLIENT_CORE_BASE|0x00) +#define MDDICAP1 (MDDI_CLIENT_CORE_BASE|0x04) +#define MDDICAP2 (MDDI_CLIENT_CORE_BASE|0x08) +#define MDDICAP3 (MDDI_CLIENT_CORE_BASE|0x0C) +#define MDCAPCHG (MDDI_CLIENT_CORE_BASE|0x10) +#define MDCRCERC (MDDI_CLIENT_CORE_BASE|0x14) +#define TTBUSSEL (MDDI_CLIENT_CORE_BASE|0x18) +#define DPSET0 (MDDI_CLIENT_CORE_BASE|0x1C) +#define DPSET1 (MDDI_CLIENT_CORE_BASE|0x20) +#define DPSUS (MDDI_CLIENT_CORE_BASE|0x24) +#define DPRUN (MDDI_CLIENT_CORE_BASE|0x28) +#define SYSCKENA (MDDI_CLIENT_CORE_BASE|0x2C) +#define TESTMODE (MDDI_CLIENT_CORE_BASE|0x30) +#define FIFOMONI (MDDI_CLIENT_CORE_BASE|0x34) +#define INTMONI (MDDI_CLIENT_CORE_BASE|0x38) +#define MDIOBIST (MDDI_CLIENT_CORE_BASE|0x3C) +#define MDIOPSET (MDDI_CLIENT_CORE_BASE|0x40) +#define BITMAP0 (MDDI_CLIENT_CORE_BASE|0x44) +#define BITMAP1 (MDDI_CLIENT_CORE_BASE|0x48) +#define BITMAP2 (MDDI_CLIENT_CORE_BASE|0x4C) +#define BITMAP3 (MDDI_CLIENT_CORE_BASE|0x50) +#define BITMAP4 (MDDI_CLIENT_CORE_BASE|0x54) + +#define SRST (LCD_CONTROL_BLOCK_BASE|0x00) +#define PORT_ENB (LCD_CONTROL_BLOCK_BASE|0x04) +#define START (LCD_CONTROL_BLOCK_BASE|0x08) +#define PORT (LCD_CONTROL_BLOCK_BASE|0x0C) +#define CMN (LCD_CONTROL_BLOCK_BASE|0x10) +#define GAMMA (LCD_CONTROL_BLOCK_BASE|0x14) +#define INTFLG (LCD_CONTROL_BLOCK_BASE|0x18) +#define INTMSK (LCD_CONTROL_BLOCK_BASE|0x1C) +#define MPLFBUF (LCD_CONTROL_BLOCK_BASE|0x20) +#define HDE_LEFT (LCD_CONTROL_BLOCK_BASE|0x24) +#define VDE_TOP (LCD_CONTROL_BLOCK_BASE|0x28) +#define PXL (LCD_CONTROL_BLOCK_BASE|0x30) +#define HCYCLE (LCD_CONTROL_BLOCK_BASE|0x34) +#define HSW (LCD_CONTROL_BLOCK_BASE|0x38) +#define HDE_START (LCD_CONTROL_BLOCK_BASE|0x3C) +#define HDE_SIZE (LCD_CONTROL_BLOCK_BASE|0x40) +#define VCYCLE (LCD_CONTROL_BLOCK_BASE|0x44) +#define VSW (LCD_CONTROL_BLOCK_BASE|0x48) +#define VDE_START (LCD_CONTROL_BLOCK_BASE|0x4C) +#define VDE_SIZE (LCD_CONTROL_BLOCK_BASE|0x50) +#define WAKEUP (LCD_CONTROL_BLOCK_BASE|0x54) +#define WSYN_DLY (LCD_CONTROL_BLOCK_BASE|0x58) +#define REGENB (LCD_CONTROL_BLOCK_BASE|0x5C) +#define VSYNIF (LCD_CONTROL_BLOCK_BASE|0x60) +#define WRSTB (LCD_CONTROL_BLOCK_BASE|0x64) +#define RDSTB (LCD_CONTROL_BLOCK_BASE|0x68) +#define ASY_DATA (LCD_CONTROL_BLOCK_BASE|0x6C) +#define ASY_DATB (LCD_CONTROL_BLOCK_BASE|0x70) +#define ASY_DATC (LCD_CONTROL_BLOCK_BASE|0x74) +#define ASY_DATD (LCD_CONTROL_BLOCK_BASE|0x78) +#define ASY_DATE (LCD_CONTROL_BLOCK_BASE|0x7C) +#define ASY_DATF (LCD_CONTROL_BLOCK_BASE|0x80) +#define ASY_DATG (LCD_CONTROL_BLOCK_BASE|0x84) +#define ASY_DATH (LCD_CONTROL_BLOCK_BASE|0x88) +#define ASY_CMDSET (LCD_CONTROL_BLOCK_BASE|0x8C) + +#define SSICTL (SPI_BLOCK_BASE|0x00) +#define SSITIME (SPI_BLOCK_BASE|0x04) +#define SSITX (SPI_BLOCK_BASE|0x08) +#define SSIRX (SPI_BLOCK_BASE|0x0C) +#define SSIINTC (SPI_BLOCK_BASE|0x10) +#define SSIINTS (SPI_BLOCK_BASE|0x14) +#define SSIDBG1 (SPI_BLOCK_BASE|0x18) +#define SSIDBG2 (SPI_BLOCK_BASE|0x1C) +#define SSIID (SPI_BLOCK_BASE|0x20) + +#define WKREQ (SYSTEM_BLOCK1_BASE|0x00) +#define CLKENB (SYSTEM_BLOCK1_BASE|0x04) +#define DRAMPWR (SYSTEM_BLOCK1_BASE|0x08) +#define INTMASK (SYSTEM_BLOCK1_BASE|0x0C) +#define GPIOSEL (SYSTEM_BLOCK2_BASE|0x00) + +#define GPIODATA (GPIO_BLOCK_BASE|0x00) +#define GPIODIR (GPIO_BLOCK_BASE|0x04) +#define GPIOIS (GPIO_BLOCK_BASE|0x08) +#define GPIOIBE (GPIO_BLOCK_BASE|0x0C) +#define GPIOIEV (GPIO_BLOCK_BASE|0x10) +#define GPIOIE (GPIO_BLOCK_BASE|0x14) +#define GPIORIS (GPIO_BLOCK_BASE|0x18) +#define GPIOMIS (GPIO_BLOCK_BASE|0x1C) +#define GPIOIC (GPIO_BLOCK_BASE|0x20) +#define GPIOOMS (GPIO_BLOCK_BASE|0x24) +#define GPIOPC (GPIO_BLOCK_BASE|0x28) +#define GPIOID (GPIO_BLOCK_BASE|0x30) + +#define SPI_WRITE(reg, val) \ + { SSITX, 0x00010000 | (((reg) & 0xff) << 8) | ((val) & 0xff) }, \ + { 0, 5 }, + +#define SPI_WRITE1(reg) \ + { SSITX, (reg) & 0xff }, \ + { 0, 5 }, + +struct mddi_table { + uint32_t reg; + uint32_t value; +}; +static struct mddi_table mddi_toshiba_init_table[] = { + { DPSET0, 0x09e90046 }, + { DPSET1, 0x00000118 }, + { DPSUS, 0x00000000 }, + { DPRUN, 0x00000001 }, + { 1, 14 }, /* msleep 14 */ + { SYSCKENA, 0x00000001 }, + /*{ CLKENB, 0x000000EF } */ + { CLKENB, 0x0000A1EF }, /* # SYS.CLKENB # Enable clocks for each module (without DCLK , i2cCLK) */ + /*{ CLKENB, 0x000025CB }, Clock enable register */ + + { GPIODATA, 0x02000200 }, /* # GPI .GPIODATA # GPIO2(RESET_LCD_N) set to 0 , GPIO3(eDRAM_Power) set to 0 */ + { GPIODIR, 0x000030D }, /* 24D # GPI .GPIODIR # Select direction of GPIO port (0,2,3,6,9 output) */ + { GPIOSEL, 0/*0x00000173*/}, /* # SYS.GPIOSEL # GPIO port multiplexing control */ + { GPIOPC, 0x03C300C0 }, /* # GPI .GPIOPC # GPIO2,3 PD cut */ + { WKREQ, 0x00000000 }, /* # SYS.WKREQ # Wake-up request event is VSYNC alignment */ + + { GPIOIBE, 0x000003FF }, + { GPIOIS, 0x00000000 }, + { GPIOIC, 0x000003FF }, + { GPIOIE, 0x00000000 }, + + { GPIODATA, 0x00040004 }, /* # GPI .GPIODATA # eDRAM VD supply */ + { 1, 1 }, /* msleep 1 */ + { GPIODATA, 0x02040004 }, /* # GPI .GPIODATA # eDRAM VD supply */ + { DRAMPWR, 0x00000001 }, /* eDRAM power */ +}; + +static struct mddi_table mddi_toshiba_panel_init_table[] = { + { SRST, 0x00000003 }, /* FIFO/LCDC not reset */ + { PORT_ENB, 0x00000001 }, /* Enable sync. Port */ + { START, 0x00000000 }, /* To stop operation */ + /*{ START, 0x00000001 }, To start operation */ + { PORT, 0x00000004 }, /* Polarity of VS/HS/DE. */ + { CMN, 0x00000000 }, + { GAMMA, 0x00000000 }, /* No Gamma correction */ + { INTFLG, 0x00000000 }, /* VSYNC interrupt flag clear/status */ + { INTMSK, 0x00000000 }, /* VSYNC interrupt mask is off. */ + { MPLFBUF, 0x00000000 }, /* Select frame buffer's base address. */ + { HDE_LEFT, 0x00000000 }, /* The value of HDE_LEFT. */ + { VDE_TOP, 0x00000000 }, /* The value of VDE_TPO. */ + { PXL, 0x00000001 }, /* 1. RGB666 */ + /* 2. Data is valid from 1st frame of beginning. */ + { HDE_START, 0x00000006 }, /* HDE_START= 14 PCLK */ + { HDE_SIZE, 0x0000009F }, /* HDE_SIZE=320 PCLK */ + { HSW, 0x00000004 }, /* HSW= 10 PCLK */ + { VSW, 0x00000001 }, /* VSW=2 HCYCLE */ + { VDE_START, 0x00000003 }, /* VDE_START=4 HCYCLE */ + { VDE_SIZE, 0x000001DF }, /* VDE_SIZE=480 HCYCLE */ + { WAKEUP, 0x000001e2 }, /* Wakeup position in VSYNC mode. */ + { WSYN_DLY, 0x00000000 }, /* Wakeup position in VSIN mode. */ + { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */ + { CLKENB, 0x000025CB }, /* Clock enable register */ + + { SSICTL, 0x00000170 }, /* SSI control register */ + { SSITIME, 0x00000250 }, /* SSI timing control register */ + { SSICTL, 0x00000172 }, /* SSI control register */ +}; + + +static struct mddi_table mddi_sharp_init_table[] = { + { VCYCLE, 0x000001eb }, + { HCYCLE, 0x000000ae }, + { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */ + { GPIODATA, 0x00040000 }, /* GPIO2 low */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { 1, 1 }, /* msleep 1 */ + { GPIODATA, 0x00040004 }, /* GPIO2 high */ + { 1, 10 }, /* msleep 10 */ + SPI_WRITE(0x5f, 0x01) + SPI_WRITE1(0x11) + { 1, 200 }, /* msleep 200 */ + SPI_WRITE1(0x29) + SPI_WRITE1(0xde) + { START, 0x00000001 }, /* To start operation */ +}; + +static struct mddi_table mddi_sharp_deinit_table[] = { + { 1, 200 }, /* msleep 200 */ + SPI_WRITE(0x10, 0x1) + { 1, 100 }, /* msleep 100 */ + { GPIODATA, 0x00040004 }, /* GPIO2 high */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { GPIODATA, 0x00040000 }, /* GPIO2 low */ + { 1, 10 }, /* msleep 10 */ +}; + +static struct mddi_table mddi_tpo_init_table[] = { + { VCYCLE, 0x000001e5 }, + { HCYCLE, 0x000000ac }, + { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */ + { 0, 20 }, /* udelay 20 */ + { GPIODATA, 0x00000004 }, /* GPIO2 high */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { 0, 20 }, /* udelay 20 */ + + SPI_WRITE(0x08, 0x01) + { 0, 500 }, /* udelay 500 */ + SPI_WRITE(0x08, 0x00) + SPI_WRITE(0x02, 0x00) + SPI_WRITE(0x03, 0x04) + SPI_WRITE(0x04, 0x0e) + SPI_WRITE(0x09, 0x02) + SPI_WRITE(0x0b, 0x08) + SPI_WRITE(0x0c, 0x53) + SPI_WRITE(0x0d, 0x01) + SPI_WRITE(0x0e, 0xe0) + SPI_WRITE(0x0f, 0x01) + SPI_WRITE(0x10, 0x58) + SPI_WRITE(0x20, 0x1e) + SPI_WRITE(0x21, 0x0a) + SPI_WRITE(0x22, 0x0a) + SPI_WRITE(0x23, 0x1e) + SPI_WRITE(0x25, 0x32) + SPI_WRITE(0x26, 0x00) + SPI_WRITE(0x27, 0xac) + SPI_WRITE(0x29, 0x06) + SPI_WRITE(0x2a, 0xa4) + SPI_WRITE(0x2b, 0x45) + SPI_WRITE(0x2c, 0x45) + SPI_WRITE(0x2d, 0x15) + SPI_WRITE(0x2e, 0x5a) + SPI_WRITE(0x2f, 0xff) + SPI_WRITE(0x30, 0x6b) + SPI_WRITE(0x31, 0x0d) + SPI_WRITE(0x32, 0x48) + SPI_WRITE(0x33, 0x82) + SPI_WRITE(0x34, 0xbd) + SPI_WRITE(0x35, 0xe7) + SPI_WRITE(0x36, 0x18) + SPI_WRITE(0x37, 0x94) + SPI_WRITE(0x38, 0x01) + SPI_WRITE(0x39, 0x5d) + SPI_WRITE(0x3a, 0xae) + SPI_WRITE(0x3b, 0xff) + SPI_WRITE(0x07, 0x09) + { 0, 10 }, /* udelay 10 */ + { START, 0x00000001 }, /* To start operation */ +}; + +static struct mddi_table mddi_tpo_deinit_table[] = { + SPI_WRITE(0x07, 0x19) + { START, 0x00000000 }, /* To stop operation */ + { GPIODATA, 0x00040004 }, /* GPIO2 high */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { GPIODATA, 0x00040000 }, /* GPIO2 low */ + { 0, 5 }, /* usleep 5 */ +}; + + +#define GPIOSEL_VWAKEINT (1U << 0) +#define INTMASK_VWAKEOUT (1U << 0) + +static void sapphire_process_mddi_table( + struct msm_mddi_client_data *client_data, + struct mddi_table *table, size_t count) +{ + int i; + for (i = 0; i < count; i++) { + uint32_t reg = table[i].reg; + uint32_t value = table[i].value; + + if (reg == 0) + udelay(value); + else if (reg == 1) + msleep(value); + else + client_data->remote_write(client_data, value, reg); + } +} + +static struct vreg *vreg_lcm_2v85; + +static void sapphire_mddi_power_client(struct msm_mddi_client_data *client_data, + int on) +{ + unsigned id, on_off; + printk(KERN_INFO "sapphire_mddi_client_power:%d\r\n", on); + if (on) { + on_off = 0; + id = PM_VREG_PDOWN_MDDI_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + + gpio_set_value(SAPPHIRE_MDDI_1V5_EN, 1); + mdelay(5); /* delay time >5ms and <10ms */ + + if (is_12pin_camera()) + gpio_set_value(V_VDDE2E_VDD2_GPIO_5M, 1); + else + gpio_set_value(V_VDDE2E_VDD2_GPIO, 1); + + gpio_set_value(SAPPHIRE_GPIO_MDDI_32K_EN, 1); + msleep(3); + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + vreg_enable(vreg_lcm_2v85); + msleep(3); + gpio_set_value(MDDI_RST_N, 1); + msleep(10); + } else { + gpio_set_value(SAPPHIRE_GPIO_MDDI_32K_EN, 0); + gpio_set_value(MDDI_RST_N, 0); + msleep(10); + vreg_disable(vreg_lcm_2v85); + on_off = 1; + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + msleep(5); + if (is_12pin_camera()) + gpio_set_value(V_VDDE2E_VDD2_GPIO_5M, 0); + else + gpio_set_value(V_VDDE2E_VDD2_GPIO, 0); + + msleep(200); + gpio_set_value(SAPPHIRE_MDDI_1V5_EN, 0); + id = PM_VREG_PDOWN_MDDI_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + } +} + +static int sapphire_mddi_toshiba_client_init( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + int panel_id; + + client_data->auto_hibernate(client_data, 0); + sapphire_process_mddi_table(client_data, mddi_toshiba_init_table, + ARRAY_SIZE(mddi_toshiba_init_table)); + client_data->auto_hibernate(client_data, 1); + g_panel_id = panel_id = + (client_data->remote_read(client_data, GPIODATA) >> 4) & 3; + if (panel_id > 1) { + printk(KERN_ERR "unknown panel id at mddi_enable\n"); + return -1; + } + return 0; +} + +static int sapphire_mddi_toshiba_client_uninit( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + return 0; +} + +static int sapphire_mddi_panel_unblank( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + int panel_id, ret = 0; + + sapphire_set_backlight_level(0); + client_data->auto_hibernate(client_data, 0); + sapphire_process_mddi_table(client_data, mddi_toshiba_panel_init_table, + ARRAY_SIZE(mddi_toshiba_panel_init_table)); + panel_id = (client_data->remote_read(client_data, GPIODATA) >> 4) & 3; + switch (panel_id) { + case 0: + printk(KERN_DEBUG "init sharp panel\n"); + sapphire_process_mddi_table(client_data, + mddi_sharp_init_table, + ARRAY_SIZE(mddi_sharp_init_table)); + break; + case 1: + printk(KERN_DEBUG "init tpo panel\n"); + sapphire_process_mddi_table(client_data, + mddi_tpo_init_table, + ARRAY_SIZE(mddi_tpo_init_table)); + break; + default: + printk(KERN_DEBUG "unknown panel_id: %d\n", panel_id); + ret = -1; + }; + mutex_lock(&sapphire_backlight_lock); + sapphire_set_backlight_level(sapphire_backlight_brightness); + sapphire_backlight_off = 0; + mutex_unlock(&sapphire_backlight_lock); + client_data->auto_hibernate(client_data, 1); + /* reenable vsync */ + client_data->remote_write(client_data, GPIOSEL_VWAKEINT, + GPIOSEL); + client_data->remote_write(client_data, INTMASK_VWAKEOUT, + INTMASK); + return ret; + +} + +static int sapphire_mddi_panel_blank( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + int panel_id, ret = 0; + + panel_id = (client_data->remote_read(client_data, GPIODATA) >> 4) & 3; + client_data->auto_hibernate(client_data, 0); + switch (panel_id) { + case 0: + printk(KERN_DEBUG "deinit sharp panel\n"); + sapphire_process_mddi_table(client_data, + mddi_sharp_deinit_table, + ARRAY_SIZE(mddi_sharp_deinit_table)); + break; + case 1: + printk(KERN_DEBUG "deinit tpo panel\n"); + sapphire_process_mddi_table(client_data, + mddi_tpo_deinit_table, + ARRAY_SIZE(mddi_tpo_deinit_table)); + break; + default: + printk(KERN_DEBUG "unknown panel_id: %d\n", panel_id); + ret = -1; + }; + client_data->auto_hibernate(client_data, 1); + mutex_lock(&sapphire_backlight_lock); + sapphire_set_backlight_level(0); + sapphire_backlight_off = 1; + mutex_unlock(&sapphire_backlight_lock); + client_data->remote_write(client_data, 0, SYSCLKENA); + client_data->remote_write(client_data, 1, DPSUS); + + return ret; +} + +static void sapphire_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) +{ + mutex_lock(&sapphire_backlight_lock); + sapphire_backlight_brightness = value; + if (!sapphire_backlight_off) + sapphire_set_backlight_level(sapphire_backlight_brightness); + mutex_unlock(&sapphire_backlight_lock); +} + +static struct led_classdev sapphire_backlight_led = { + .name = "lcd-backlight", + .brightness = SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS, + .brightness_set = sapphire_brightness_set, +}; + +static int sapphire_backlight_probe(struct platform_device *pdev) +{ + led_classdev_register(&pdev->dev, &sapphire_backlight_led); + return 0; +} + +static int sapphire_backlight_remove(struct platform_device *pdev) +{ + led_classdev_unregister(&sapphire_backlight_led); + return 0; +} + +static struct platform_driver sapphire_backlight_driver = { + .probe = sapphire_backlight_probe, + .remove = sapphire_backlight_remove, + .driver = { + .name = "sapphire-backlight", + .owner = THIS_MODULE, + }, +}; + +static struct resource resources_msm_fb[] = { + { + .start = SMI64_MSM_FB_BASE, + .end = SMI64_MSM_FB_BASE + SMI64_MSM_FB_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct msm_mddi_bridge_platform_data toshiba_client_data = { + .init = sapphire_mddi_toshiba_client_init, + .uninit = sapphire_mddi_toshiba_client_uninit, + .blank = sapphire_mddi_panel_blank, + .unblank = sapphire_mddi_panel_unblank, + .fb_data = { + .xres = 320, + .yres = 480, + .width = 45, + .height = 67, + .output_format = 0, + }, +}; + +static struct msm_mddi_platform_data mddi_pdata = { + .clk_rate = 122880000, + .power_client = sapphire_mddi_power_client, + .fb_resource = resources_msm_fb, + .num_clients = 1, + .client_platform_data = { + { + .product_id = (0xd263 << 16 | 0), + .name = "mddi_c_d263_0000", + .id = 0, + .client_data = &toshiba_client_data, + .clk_rate = 0, + }, + }, +}; + +static struct platform_device sapphire_backlight = { + .name = "sapphire-backlight", +}; + +int __init sapphire_init_panel(void) +{ + int rc = -1; + uint32_t config = PCOM_GPIO_CFG(27, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA); /* GPIO27 */ + + if (!machine_is_sapphire()) + return 0; + + /* checking board as soon as possible */ + printk("sapphire_init_panel:machine_is_sapphire=%d, machine_arch_type=%d, MACH_TYPE_SAPPHIRE=%d\r\n", machine_is_sapphire(), machine_arch_type, MACH_TYPE_SAPPHIRE); + if (!machine_is_sapphire()) + return 0; + + vreg_lcm_2v85 = vreg_get(0, "gp4"); + if (IS_ERR(vreg_lcm_2v85)) + return PTR_ERR(vreg_lcm_2v85); + + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, 0); + + /* setup FB by SMI size */ + if (sapphire_get_smi_size() == 32) { + resources_msm_fb[0].start = SMI32_MSM_FB_BASE; + resources_msm_fb[0].end = SMI32_MSM_FB_BASE + SMI32_MSM_FB_SIZE - 1; + } + + rc = platform_device_register(&msm_device_mdp); + if (rc) + return rc; + msm_device_mddi0.dev.platform_data = &mddi_pdata; + rc = platform_device_register(&msm_device_mddi0); + if (rc) + return rc; + platform_device_register(&sapphire_backlight); + return platform_driver_register(&sapphire_backlight_driver); +} + +device_initcall(sapphire_init_panel); diff --git a/arch/arm/mach-msm/board-sapphire-rfkill.c b/arch/arm/mach-msm/board-sapphire-rfkill.c new file mode 100644 index 0000000000000..135ffe82c328d --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-rfkill.c @@ -0,0 +1,99 @@ +/* linux/arch/arm/mach-msm/board-sapphire-rfkill.c + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai + * + * 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. +*/ + +/* Control bluetooth power for sapphire platform */ + +#include +#include +#include +#include +#include +#include +#include +#include "gpio_chip.h" +#include "board-sapphire.h" + +void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state); + +static struct rfkill *bt_rfk; +static const char bt_name[] = "brf6300"; + +extern int sapphire_bt_fastclock_power(int on); + +static int bluetooth_set_power(void *data, enum rfkill_state state) +{ + switch (state) { + case RFKILL_STATE_UNBLOCKED: + sapphire_bt_fastclock_power(1); + gpio_set_value(SAPPHIRE_GPIO_BT_32K_EN, 1); + udelay(10); + gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_HIGH); + break; + case RFKILL_STATE_SOFT_BLOCKED: + gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_LOW); + gpio_set_value(SAPPHIRE_GPIO_BT_32K_EN, 0); + sapphire_bt_fastclock_power(0); + break; + default: + printk(KERN_ERR "bad bluetooth rfkill state %d\n", state); + } + return 0; +} + +static int __init sapphire_rfkill_probe(struct platform_device *pdev) +{ + int rc = 0; + + /* default to bluetooth off */ + rfkill_switch_all(RFKILL_TYPE_BLUETOOTH, RFKILL_STATE_SOFT_BLOCKED); + bluetooth_set_power(NULL, RFKILL_STATE_SOFT_BLOCKED); + + bt_rfk = rfkill_allocate(&pdev->dev, RFKILL_TYPE_BLUETOOTH); + if (!bt_rfk) + return -ENOMEM; + + bt_rfk->name = bt_name; + bt_rfk->state = RFKILL_STATE_SOFT_BLOCKED; + /* userspace cannot take exclusive control */ + bt_rfk->user_claim_unsupported = 1; + bt_rfk->user_claim = 0; + bt_rfk->data = NULL; /* user data */ + bt_rfk->toggle_radio = bluetooth_set_power; + + rc = rfkill_register(bt_rfk); + + if (rc) + rfkill_free(bt_rfk); + return rc; +} + +static struct platform_driver sapphire_rfkill_driver = { + .probe = sapphire_rfkill_probe, + .driver = { + .name = "sapphire_rfkill", + .owner = THIS_MODULE, + }, +}; + +static int __init sapphire_rfkill_init(void) +{ + if (!machine_is_sapphire()) + return 0; + return platform_driver_register(&sapphire_rfkill_driver); +} + +module_init(sapphire_rfkill_init); +MODULE_DESCRIPTION("sapphire rfkill"); +MODULE_AUTHOR("Nick Pelly "); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-sapphire-wifi.c b/arch/arm/mach-msm/board-sapphire-wifi.c new file mode 100644 index 0000000000000..43f827c60f13e --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-wifi.c @@ -0,0 +1,74 @@ +/* arch/arm/mach-msm/board-sapphire-wifi.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Dmitry Shmidt + * + * 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. + * + */ + +#ifdef CONFIG_WIFI_CONTROL_FUNC +#include +#include +#include +#include +#include +#include + +extern int sapphire_wifi_set_carddetect(int val); +extern int sapphire_wifi_power(int on); +extern int sapphire_wifi_reset(int on); + +#ifdef CONFIG_WIFI_MEM_PREALLOC +typedef struct wifi_mem_prealloc_struct { + void *mem_ptr; + unsigned long size; +} wifi_mem_prealloc_t; + +static wifi_mem_prealloc_t wifi_mem_array[WMPA_NUMBER_OF_SECTIONS] = { + { NULL, (WMPA_SECTION_SIZE_0 + WMPA_SECTION_HEADER) }, + { NULL, (WMPA_SECTION_SIZE_1 + WMPA_SECTION_HEADER) }, + { NULL, (WMPA_SECTION_SIZE_2 + WMPA_SECTION_HEADER) } +}; + +static void *sapphire_wifi_mem_prealloc(int section, unsigned long size) +{ + if ((section < 0) || (section >= WMPA_NUMBER_OF_SECTIONS)) + return NULL; + if (wifi_mem_array[section].size < size) + return NULL; + return wifi_mem_array[section].mem_ptr; +} + +int __init sapphire_init_wifi_mem (void) +{ + int i; + + for (i = 0; (i < WMPA_NUMBER_OF_SECTIONS); i++) { + wifi_mem_array[i].mem_ptr = vmalloc(wifi_mem_array[i].size); + if (wifi_mem_array[i].mem_ptr == NULL) + return -ENOMEM; + } + return 0; +} +#endif + +struct wifi_platform_data sapphire_wifi_control = { + .set_power = sapphire_wifi_power, + .set_reset = sapphire_wifi_reset, + .set_carddetect = sapphire_wifi_set_carddetect, +#ifdef CONFIG_WIFI_MEM_PREALLOC + .mem_prealloc = sapphire_wifi_mem_prealloc, +#else + .mem_prealloc = NULL, +#endif +}; + +#endif diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index 8919ffb171960..b1321c0f23656 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -17,8 +17,23 @@ #include #include #include +#include #include +#include +#include +#include +#include +#include +#include +#include #include +#include +#ifdef CONFIG_USB_FUNCTION +#include +#endif +#ifdef CONFIG_USB_ANDROID +#include +#endif #include @@ -31,41 +46,1021 @@ #include #include #include -#include #include #include #include +#include #include #include +#include +#include + + #include "gpio_chip.h" #include "board-sapphire.h" + +#include +#include +#include +#include +#include + +#ifdef CONFIG_WIFI_CONTROL_FUNC +#ifdef CONFIG_WIFI_MEM_PREALLOC +extern int sapphire_init_wifi_mem(void); +#endif +extern struct wifi_platform_data sapphire_wifi_control; +#endif + #include "proc_comm.h" #include "devices.h" void msm_init_irq(void); void msm_init_gpio(void); +void msm_init_pmic_vibrator(void); + +extern int sapphire_init_mmc(unsigned int); + +struct sapphire_axis_info { + struct gpio_event_axis_info info; + uint16_t in_state; + uint16_t out_state; + uint16_t temp_state; + uint16_t threshold; +}; +static bool nav_just_on; +static int nav_on_jiffies; +static int smi_sz = 64; +static unsigned int hwid = 0; +static unsigned int skuid = 0; +static unsigned engineerid = (0x01 << 1); /* default is 3M sensor */ + +uint16_t sapphire_axis_map(struct gpio_event_axis_info *info, uint16_t in) +{ + struct sapphire_axis_info *ai = container_of(info, struct sapphire_axis_info, info); + uint16_t out = ai->out_state; + + if (nav_just_on) { + if (jiffies == nav_on_jiffies || jiffies == nav_on_jiffies + 1) + goto ignore; + nav_just_on = 0; + } + if ((ai->in_state ^ in) & 1) + out--; + if ((ai->in_state ^ in) & 2) + out++; + ai->out_state = out; +ignore: + ai->in_state = in; + if (ai->out_state - ai->temp_state == ai->threshold) { + ai->temp_state++; + ai->out_state = ai->temp_state; + } else if (ai->temp_state - ai->out_state == ai->threshold) { + ai->temp_state--; + ai->out_state = ai->temp_state; + } else if (abs(ai->out_state - ai->temp_state) > ai->threshold) + ai->temp_state = ai->out_state; + + return ai->temp_state; +} + +int sapphire_nav_power(const struct gpio_event_platform_data *pdata, bool on) +{ + gpio_set_value(SAPPHIRE_GPIO_JOG_EN, on); + if (on) { + nav_just_on = 1; + nav_on_jiffies = jiffies; + } + return 0; +} + +static uint32_t sapphire_x_axis_gpios[] = { + SAPPHIRE_BALL_LEFT_0, SAPPHIRE_BALL_RIGHT_0 +}; + +static struct sapphire_axis_info sapphire_x_axis = { + .threshold = 2, + .info = { + .info.func = gpio_event_axis_func, + .count = ARRAY_SIZE(sapphire_x_axis_gpios), + .type = EV_REL, + .code = REL_X, + .decoded_size = 1U << ARRAY_SIZE(sapphire_x_axis_gpios), + .map = sapphire_axis_map, + .gpio = sapphire_x_axis_gpios, + .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION /*| GPIOEAF_PRINT_RAW | GPIOEAF_PRINT_EVENT */ + } +}; + +static uint32_t sapphire_y_axis_gpios[] = { + SAPPHIRE_BALL_UP_0, SAPPHIRE_BALL_DOWN_0 +}; + +static struct sapphire_axis_info sapphire_y_axis = { + .threshold = 2, + .info = { + .info.func = gpio_event_axis_func, + .count = ARRAY_SIZE(sapphire_y_axis_gpios), + .type = EV_REL, + .code = REL_Y, + .decoded_size = 1U << ARRAY_SIZE(sapphire_y_axis_gpios), + .map = sapphire_axis_map, + .gpio = sapphire_y_axis_gpios, + .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION /*| GPIOEAF_PRINT_RAW | GPIOEAF_PRINT_EVENT */ + } +}; + +static struct gpio_event_direct_entry sapphire_nav_buttons[] = { + { SAPPHIRE_GPIO_NAVI_ACT_N, BTN_MOUSE }, + { SAPPHIRE_GPIO_SEARCH_ACT_N, KEY_COMPOSE }, /* CPLD Key Search */ +}; + +static struct gpio_event_input_info sapphire_nav_button_info = { + .info.func = gpio_event_input_func, + .flags = GPIOEDF_PRINT_KEYS | GPIOEDF_PRINT_KEY_DEBOUNCE, + .poll_time.tv.nsec = 40 * NSEC_PER_MSEC, + .type = EV_KEY, + .keymap = sapphire_nav_buttons, + .keymap_size = ARRAY_SIZE(sapphire_nav_buttons) +}; + +static struct gpio_event_info *sapphire_nav_info[] = { + &sapphire_x_axis.info.info, + &sapphire_y_axis.info.info, + &sapphire_nav_button_info.info +}; + +static struct gpio_event_platform_data sapphire_nav_data = { + .name = "sapphire-nav", + .info = sapphire_nav_info, + .info_count = ARRAY_SIZE(sapphire_nav_info), + .power = sapphire_nav_power, +}; + +static struct platform_device sapphire_nav_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = 2, + .dev = { + .platform_data = &sapphire_nav_data, + }, +}; + +static int sapphire_reset_keys_up[] = { + BTN_MOUSE, + 0 +}; + +static struct keyreset_platform_data sapphire_reset_keys_pdata = { + .keys_up = sapphire_reset_keys_up, + .keys_down = { + KEY_SEND, + KEY_MENU, + KEY_END, + 0 + }, +}; + +struct platform_device sapphire_reset_keys_device = { + .name = KEYRESET_NAME, + .dev.platform_data = &sapphire_reset_keys_pdata, +}; + +static int sapphire_ts_power(int on) +{ + int gpio_tp_ls_en = SAPPHIRE_TP_LS_EN; + + if (is_12pin_camera()) + gpio_tp_ls_en = SAPPHIRE20_TP_LS_EN; + + if (on) { + sapphire_gpio_write(NULL, SAPPHIRE_GPIO_TP_EN, 1); + /* touchscreen must be powered before we enable i2c pullup */ + msleep(2); + /* enable touch panel level shift */ + gpio_set_value(gpio_tp_ls_en, 1); + msleep(2); + } else { + gpio_set_value(gpio_tp_ls_en, 0); + sapphire_gpio_write(NULL, SAPPHIRE_GPIO_TP_EN, 0); + } + + return 0; +} + +static struct synaptics_i2c_rmi_platform_data sapphire_ts_data[] = { +{ + .version = 0x0101, + .power = sapphire_ts_power, + .flags = SYNAPTICS_FLIP_Y | SYNAPTICS_SNAP_TO_INACTIVE_EDGE, + .inactive_left = -50 * 0x10000 / 4334, + .inactive_right = -50 * 0x10000 / 4334, + .inactive_top = -40 * 0x10000 / 6696, + .inactive_bottom = -40 * 0x10000 / 6696, + .snap_left_on = 50 * 0x10000 / 4334, + .snap_left_off = 60 * 0x10000 / 4334, + .snap_right_on = 50 * 0x10000 / 4334, + .snap_right_off = 60 * 0x10000 / 4334, + .snap_top_on = 100 * 0x10000 / 6696, + .snap_top_off = 110 * 0x10000 / 6696, + .snap_bottom_on = 100 * 0x10000 / 6696, + .snap_bottom_off = 110 * 0x10000 / 6696, + }, + { + .flags = SYNAPTICS_FLIP_Y | SYNAPTICS_SNAP_TO_INACTIVE_EDGE, + .inactive_left = ((4674 - 4334) / 2 + 200) * 0x10000 / 4334, + .inactive_right = ((4674 - 4334) / 2 + 200) * 0x10000 / 4334, + .inactive_top = ((6946 - 6696) / 2) * 0x10000 / 6696, + .inactive_bottom = ((6946 - 6696) / 2) * 0x10000 / 6696, + } +}; + +static struct akm8976_platform_data compass_platform_data = { + .reset = SAPPHIRE_GPIO_COMPASS_RST_N, + .clk_on = SAPPHIRE_GPIO_COMPASS_32K_EN, + .intr = SAPPHIRE_GPIO_COMPASS_IRQ, +}; + +static struct elan_i2c_platform_data elan_i2c_data[] = { + { + .version = 0x104, + .abs_x_min = 0, + .abs_y_min = 0, + .intr_gpio = SAPPHIRE_GPIO_TP_ATT_N, + .power = sapphire_ts_power, + }, + { + .version = 0x103, + .abs_x_min = 0, + .abs_x_max = 512 * 2, + .abs_y_min = 0, + .abs_y_max = 896 * 2, + .intr_gpio = SAPPHIRE_GPIO_TP_ATT_N, + .power = sapphire_ts_power, + }, + { + .version = 0x102, + .abs_x_min = 0, + .abs_x_max = 384, + .abs_y_min = 0, + .abs_y_max = 576, + .intr_gpio = SAPPHIRE_GPIO_TP_ATT_N, + .power = sapphire_ts_power, + }, + { + .version = 0x101, + .abs_x_min = 32 + 1, + .abs_x_max = 352 - 1, + .abs_y_min = 32 + 1, + .abs_y_max = 544 - 1, + .intr_gpio = SAPPHIRE_GPIO_TP_ATT_N, + .power = sapphire_ts_power, + } +}; + +static struct msm_camera_device_platform_data msm_camera_device_mt9t013 = { + .sensor_reset = 108, + .sensor_pwd = 85, + .vcm_pwd = SAPPHIRE_GPIO_VCM_PWDN, + .config_gpio_on = config_sapphire_camera_on_gpios, + .config_gpio_off = config_sapphire_camera_off_gpios, +}; + +static struct platform_device sapphire_camera = { + .name = "camera", + .dev = { + .platform_data = &msm_camera_device_mt9t013, + }, +}; + +static struct i2c_board_info i2c_devices[] = { + { + I2C_BOARD_INFO(SYNAPTICS_I2C_RMI_NAME, 0x20), + .platform_data = sapphire_ts_data, + .irq = SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_TP_ATT_N) + }, + { + I2C_BOARD_INFO(ELAN_8232_I2C_NAME, 0x10), + .platform_data = &elan_i2c_data, + .irq = SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_TP_ATT_N), + }, + { + I2C_BOARD_INFO("akm8976", 0x1C), + .platform_data = &compass_platform_data, + .irq = SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_COMPASS_IRQ), + }, + { + I2C_BOARD_INFO("mt9t013", 0x6C >> 1), + .platform_data = &msm_camera_device_mt9t013, + }, +}; + +#ifdef CONFIG_LEDS_CPLD +static struct resource cpldled_resources[] = { + { + .start = SAPPHIRE_CPLD_LED_BASE, + .end = SAPPHIRE_CPLD_LED_BASE + SAPPHIRE_CPLD_LED_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device android_CPLD_leds = { + .name = "leds-cpld", + .id = -1, + .num_resources = ARRAY_SIZE(cpldled_resources), + .resource = cpldled_resources, +}; +#endif + +#if 0 +static struct gpio_led android_led_list[] = { + { + .name = "button-backlight", + .gpio = SAPPHIRE_GPIO_APKEY_LED_EN, + }, +}; + +static struct gpio_led_platform_data android_leds_data = { + .num_leds = ARRAY_SIZE(android_led_list), + .leds = android_led_list, +}; + +static struct platform_device android_leds = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &android_leds_data, + }, +}; +#endif + +#ifdef CONFIG_HTC_HEADSET +/* RTS/CTS to GPO/GPI. */ +static uint32_t uart1_on_gpio_table[] = { + /* allenou, uart hs test, 2008/11/18 */ + #ifdef CONFIG_SERIAL_MSM_HS + /* RTS */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_RTS, 2, + GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + /* CTS */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_CTS, 2, + GPIO_INPUT, GPIO_PULL_UP, GPIO_8MA), + #else + /* RTS */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_RTS, 1, + GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), + /* CTS */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_CTS, 1, + GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), + #endif +}; + +/* RTS,CTS to BT. */ +static uint32_t uart1_off_gpio_table[] = { + /* RTS */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_RTS, 0, + GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + /* CTS */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_CTS, 0, + GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), +}; + +/* Sapphire: Switch between UART3 and GPIO */ +static uint32_t uart3_on_gpio_table[] = { + /* RX */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART3_RX, 1, + GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), + /* TX */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART3_TX, 1, + GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), +}; + +/* set TX,RX to GPI */ +static uint32_t uart3_off_gpi_table[] = { + /* RX, H2W DATA */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_H2W_DATA, 0, + GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), + /* TX, H2W CLK */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_H2W_CLK, 0, + GPIO_INPUT, GPIO_KEEPER, GPIO_2MA), +}; + +static int sapphire_h2w_path = H2W_GPIO; + +static void h2w_config_cpld(int route) +{ + switch (route) { + case H2W_UART1: + /* Make sure uart1 funtion pin opened. */ + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart1_on_gpio_table+0, 0); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart1_on_gpio_table+1, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 1); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 0); + sapphire_h2w_path = H2W_UART1; + printk(KERN_INFO "H2W route = H2W-UART1, BT-X, UART3-X \n"); + break; + case H2W_BT: + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 1); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 1); + /* UART1 RTS/CTS to GPO/GPI. */ + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart1_off_gpio_table+0, 0); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart1_off_gpio_table+1, 0); + sapphire_h2w_path = H2W_BT; + printk(KERN_INFO "H2W route = H2W-BT, UART1-X, UART3-X \n"); + break; + case H2W_UART3: + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart3_on_gpio_table+0, 0); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart3_on_gpio_table+1, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 1); + /* Make sure uart1 funtion pin opened. */ + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart1_on_gpio_table+0, 0); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart1_on_gpio_table+1, 0); + sapphire_h2w_path = H2W_UART3; + printk(KERN_INFO "H2W route = H2W-UART3, BT-UART1 \n"); + break; + case H2W_GPIO: /*H2W_UART3 TX,RX are changed to H2W_GPIO */ + default: + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 0); + /* Set the CPLD connected H2W GPIO's to input */ + gpio_set_value(SAPPHIRE_GPIO_H2W_CLK_DIR, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_DAT_DIR, 0); + /* TX,RX GPI first. */ + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart3_off_gpi_table+0, 0); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart3_off_gpi_table+1, 0); + /* Make sure uart1 funtion pin opened. */ + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart1_on_gpio_table+0, 0); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart1_on_gpio_table+1, 0); + sapphire_h2w_path = H2W_GPIO; + printk(KERN_INFO "H2W route = H2W-GPIO, BT-UART1 \n"); + break; + } +} + +static void h2w_init_cpld(void) +{ + h2w_config_cpld(H2W_GPIO); +} + +static void set_h2w_dat(int n) +{ + gpio_set_value(SAPPHIRE_GPIO_H2W_DATA, n); +} + +static void set_h2w_clk(int n) +{ + gpio_set_value(SAPPHIRE_GPIO_H2W_CLK, n); +} + +static void set_h2w_dat_dir(int n) +{ + if (n == 0) /* input */ + gpio_direction_input(SAPPHIRE_GPIO_H2W_DATA); + else + gpio_configure(SAPPHIRE_GPIO_H2W_DATA, GPIOF_DRIVE_OUTPUT); + + gpio_set_value(SAPPHIRE_GPIO_H2W_DAT_DIR, n); + +} + +static void set_h2w_clk_dir(int n) +{ + if (n == 0) /* input */ + gpio_direction_input(SAPPHIRE_GPIO_H2W_CLK); + else + gpio_configure(SAPPHIRE_GPIO_H2W_CLK, GPIOF_DRIVE_OUTPUT); + + gpio_set_value(SAPPHIRE_GPIO_H2W_CLK_DIR, n); +} + +static int get_h2w_dat(void) +{ + return gpio_get_value(SAPPHIRE_GPIO_H2W_DATA); +} + +static int get_h2w_clk(void) +{ + return gpio_get_value(SAPPHIRE_GPIO_H2W_CLK); +} + +static int set_h2w_path(const char *val, struct kernel_param *kp) +{ + int ret = -EINVAL; + + ret = param_set_int(val, kp); + if (ret) + return ret; + + switch (sapphire_h2w_path) { + case H2W_GPIO: + case H2W_UART1: + case H2W_UART3: + case H2W_BT: + break; + default: + sapphire_h2w_path = -1; + return -EINVAL; + } + + h2w_config_cpld(sapphire_h2w_path); + return ret; +} +module_param_call(h2w_path, set_h2w_path, param_get_int, + &sapphire_h2w_path, S_IWUSR | S_IRUGO); + + +static struct h2w_platform_data sapphire_h2w_data = { + .power_name = "wlan", + .cable_in1 = SAPPHIRE_GPIO_CABLE_IN1, + .cable_in2 = SAPPHIRE_GPIO_CABLE_IN2, + .h2w_clk = SAPPHIRE_GPIO_H2W_CLK, + .h2w_data = SAPPHIRE_GPIO_H2W_DATA, + .debug_uart = H2W_UART3, + .config_cpld = h2w_config_cpld, + .init_cpld = h2w_init_cpld, + .set_dat = set_h2w_dat, + .set_clk = set_h2w_clk, + .set_dat_dir = set_h2w_dat_dir, + .set_clk_dir = set_h2w_clk_dir, + .get_dat = get_h2w_dat, + .get_clk = get_h2w_clk, +}; + +static struct platform_device sapphire_h2w = { + .name = "h2w", + .id = -1, + .dev = { + .platform_data = &sapphire_h2w_data, + }, +}; +#endif + +#ifdef CONFIG_USB_FUNCTION +static void sapphire_phy_reset(void) +{ + gpio_set_value(SAPPHIRE_GPIO_USB_PHY_RST_N, 0); + mdelay(10); + gpio_set_value(SAPPHIRE_GPIO_USB_PHY_RST_N, 1); + mdelay(10); +} +#endif + +static struct pwr_sink sapphire_pwrsink_table[] = { + { + .id = PWRSINK_AUDIO, + .ua_max = 100000, + }, + { + .id = PWRSINK_BACKLIGHT, + .ua_max = 125000, + }, + { + .id = PWRSINK_LED_BUTTON, + .ua_max = 0, + }, + { + .id = PWRSINK_LED_KEYBOARD, + .ua_max = 0, + }, + { + .id = PWRSINK_GP_CLK, + .ua_max = 0, + }, + { + .id = PWRSINK_BLUETOOTH, + .ua_max = 15000, + }, + { + .id = PWRSINK_CAMERA, + .ua_max = 0, + }, + { + .id = PWRSINK_SDCARD, + .ua_max = 0, + }, + { + .id = PWRSINK_VIDEO, + .ua_max = 0, + }, + { + .id = PWRSINK_WIFI, + .ua_max = 200000, + }, + { + .id = PWRSINK_SYSTEM_LOAD, + .ua_max = 100000, + .percent_util = 38, + }, +}; + +static struct pwr_sink_platform_data sapphire_pwrsink_data = { + .num_sinks = ARRAY_SIZE(sapphire_pwrsink_table), + .sinks = sapphire_pwrsink_table, + .suspend_late = NULL, + .resume_early = NULL, + .suspend_early = NULL, + .resume_late = NULL, +}; + +static struct platform_device sapphire_pwr_sink = { + .name = "htc_pwrsink", + .id = -1, + .dev = { + .platform_data = &sapphire_pwrsink_data, + }, +}; + +static struct platform_device sapphire_rfkill = { + .name = "sapphire_rfkill", + .id = -1, +}; + +static struct msm_pmem_setting pmem_setting_32 = { + .pmem_start = SMI32_MSM_PMEM_MDP_BASE, + .pmem_size = SMI32_MSM_PMEM_MDP_SIZE, + .pmem_adsp_start = SMI32_MSM_PMEM_ADSP_BASE, + .pmem_adsp_size = SMI32_MSM_PMEM_ADSP_SIZE, + .pmem_gpu0_start = MSM_PMEM_GPU0_BASE, + .pmem_gpu0_size = MSM_PMEM_GPU0_SIZE, + .pmem_gpu1_start = MSM_PMEM_GPU1_BASE, + .pmem_gpu1_size = MSM_PMEM_GPU1_SIZE, + .pmem_camera_start = 0, + .pmem_camera_size = 0, + .ram_console_start = MSM_RAM_CONSOLE_BASE, + .ram_console_size = MSM_RAM_CONSOLE_SIZE, +}; + +static struct msm_pmem_setting pmem_setting_64 = { + .pmem_start = SMI64_MSM_PMEM_MDP_BASE, + .pmem_size = SMI64_MSM_PMEM_MDP_SIZE, + .pmem_adsp_start = SMI64_MSM_PMEM_ADSP_BASE, + .pmem_adsp_size = SMI64_MSM_PMEM_ADSP_SIZE, + .pmem_gpu0_start = MSM_PMEM_GPU0_BASE, + .pmem_gpu0_size = MSM_PMEM_GPU0_SIZE, + .pmem_gpu1_start = MSM_PMEM_GPU1_BASE, + .pmem_gpu1_size = MSM_PMEM_GPU1_SIZE, + .pmem_camera_start = SMI64_MSM_PMEM_CAMERA_BASE, + .pmem_camera_size = SMI64_MSM_PMEM_CAMERA_SIZE, + .ram_console_start = MSM_RAM_CONSOLE_BASE, + .ram_console_size = MSM_RAM_CONSOLE_SIZE, +}; + +#ifdef CONFIG_WIFI_CONTROL_FUNC +static struct platform_device sapphire_wifi = { + .name = "msm_wifi", + .id = 1, + .num_resources = 0, + .resource = NULL, + .dev = { + .platform_data = &sapphire_wifi_control, + }, +}; +#endif + +#define SND(num, desc) { .name = desc, .id = num } +static struct snd_endpoint snd_endpoints_list[] = { + SND(0, "HANDSET"), + SND(1, "SPEAKER"), + SND(2, "HEADSET"), + SND(3, "BT"), + SND(44, "BT_EC_OFF"), + SND(10, "HEADSET_AND_SPEAKER"), + SND(256, "CURRENT"), + + /* Bluetooth accessories. */ + + SND(12, "HTC BH S100"), + SND(13, "HTC BH M100"), + SND(14, "Motorola H500"), + SND(15, "Nokia HS-36W"), + SND(16, "PLT 510v.D"), + SND(17, "M2500 by Plantronics"), + SND(18, "Nokia HDW-3"), + SND(19, "HBH-608"), + SND(20, "HBH-DS970"), + SND(21, "i.Tech BlueBAND"), + SND(22, "Nokia BH-800"), + SND(23, "Motorola H700"), + SND(24, "HTC BH M200"), + SND(25, "Jabra JX10"), + SND(26, "320Plantronics"), + SND(27, "640Plantronics"), + SND(28, "Jabra BT500"), + SND(29, "Motorola HT820"), + SND(30, "HBH-IV840"), + SND(31, "6XXPlantronics"), + SND(32, "3XXPlantronics"), + SND(33, "HBH-PV710"), + SND(34, "Motorola H670"), + SND(35, "HBM-300"), + SND(36, "Nokia BH-208"), + SND(37, "Samsung WEP410"), + SND(38, "Jabra BT8010"), + SND(39, "Motorola S9"), + SND(40, "Jabra BT620s"), + SND(41, "Nokia BH-902"), + SND(42, "HBH-DS220"), + SND(43, "HBH-DS980"), +}; +#undef SND + +static struct msm_snd_endpoints sapphire_snd_endpoints = { + .endpoints = snd_endpoints_list, + .num = ARRAY_SIZE(snd_endpoints_list), +}; + +static struct platform_device sapphire_snd = { + .name = "msm_snd", + .id = -1, + .dev = { + .platform_data = &sapphire_snd_endpoints, + }, +}; static struct platform_device *devices[] __initdata = { &msm_device_smd, - &msm_device_dmov, &msm_device_nand, + &msm_device_i2c, &msm_device_uart1, +#if !defined(CONFIG_MSM_SERIAL_DEBUGGER) && !defined(CONFIG_TROUT_H2W) &msm_device_uart3, +#endif +#ifdef CONFIG_SERIAL_MSM_HS + &msm_device_uart_dm1, +#endif + &sapphire_nav_device, + &sapphire_reset_keys_device, +#ifdef CONFIG_LEDS_CPLD + &android_CPLD_leds, +#endif +#ifdef CONFIG_HTC_HEADSET + &sapphire_h2w, +#endif + &sapphire_rfkill, +#ifdef CONFIG_WIFI_CONTROL_FUNC + &sapphire_wifi, +#endif + +#ifdef CONFIG_HTC_PWRSINK + &sapphire_pwr_sink, +#endif + &sapphire_snd, + &sapphire_camera, }; extern struct sys_timer msm_timer; static void __init sapphire_init_irq(void) { + printk(KERN_DEBUG "sapphire_init_irq()\n"); msm_init_irq(); } +static uint cpld_iset; +static uint cpld_charger_en; +static uint cpld_usb_h2w_sw; +static uint opt_disable_uart3; + +module_param_named(iset, cpld_iset, uint, 0); +module_param_named(charger_en, cpld_charger_en, uint, 0); +module_param_named(usb_h2w_sw, cpld_usb_h2w_sw, uint, 0); +module_param_named(disable_uart3, opt_disable_uart3, uint, 0); + +static void sapphire_reset(void) +{ + gpio_set_value(SAPPHIRE_GPIO_PS_HOLD, 0); +} + +static uint32_t gpio_table[] = { + /* BLUETOOTH */ +#ifdef CONFIG_SERIAL_MSM_HS + PCOM_GPIO_CFG(43, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RTS */ + PCOM_GPIO_CFG(44, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* CTS */ + PCOM_GPIO_CFG(45, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RX */ + PCOM_GPIO_CFG(46, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* TX */ +#else + PCOM_GPIO_CFG(43, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RTS */ + PCOM_GPIO_CFG(44, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* CTS */ + PCOM_GPIO_CFG(45, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RX */ + PCOM_GPIO_CFG(46, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* TX */ +#endif +}; + + +static uint32_t camera_off_gpio_table[] = { + /* CAMERA */ + PCOM_GPIO_CFG(2, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* PCLK */ + PCOM_GPIO_CFG(13, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* HSYNC_IN */ + PCOM_GPIO_CFG(14, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* VSYNC_IN */ + PCOM_GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* MCLK */ +}; + +static uint32_t camera_on_gpio_table[] = { + /* CAMERA */ + PCOM_GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_16MA), /* PCLK */ + PCOM_GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */ + PCOM_GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */ + PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_16MA), /* MCLK */ +}; + +static uint32_t camera_off_gpio_12pins_table[] = { + /* CAMERA */ + PCOM_GPIO_CFG(0, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(1, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(2, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* PCLK */ + PCOM_GPIO_CFG(13, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* HSYNC_IN */ + PCOM_GPIO_CFG(14, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* VSYNC_IN */ + PCOM_GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* MCLK */ +}; + +static uint32_t camera_on_gpio_12pins_table[] = { + /* CAMERA */ + PCOM_GPIO_CFG(0, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT0 */ + PCOM_GPIO_CFG(1, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT1 */ + PCOM_GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_16MA), /* PCLK */ + PCOM_GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */ + PCOM_GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */ + PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_16MA), /* MCLK */ +}; + +static void config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for (n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +void config_sapphire_camera_on_gpios(void) +{ + /*Add for judage it's 10 pins or 12 pins platform ----->*/ + if (is_12pin_camera()) { + config_gpio_table(camera_on_gpio_12pins_table, + ARRAY_SIZE(camera_on_gpio_12pins_table)); + } else { + config_gpio_table(camera_on_gpio_table, + ARRAY_SIZE(camera_on_gpio_table)); + } + /*End Of Add for judage it's 10 pins or 12 pins platform*/ +} + +void config_sapphire_camera_off_gpios(void) +{ + /*Add for judage it's 10 pins or 12 pins platform ----->*/ + if (is_12pin_camera()) { + config_gpio_table(camera_off_gpio_12pins_table, + ARRAY_SIZE(camera_off_gpio_12pins_table)); + } else { + config_gpio_table(camera_off_gpio_table, + ARRAY_SIZE(camera_off_gpio_table)); + } + /*End Of Add for judage it's 10 pins or 12 pins platform*/ +} + +static void __init config_gpios(void) +{ + config_gpio_table(gpio_table, ARRAY_SIZE(gpio_table)); + config_sapphire_camera_off_gpios(); +} + +void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq); + +static struct msm_acpu_clock_platform_data sapphire_clock_data = { + .acpu_switch_time_us = 20, + .max_speed_delta_khz = 256000, + .vdd_switch_time_us = 62, + .power_collapse_khz = 19200000, + .wait_for_irq_khz = 128000000, +}; + +#ifdef CONFIG_SERIAL_MSM_HS +static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { + .wakeup_irq = MSM_GPIO_TO_INT(45), + .inject_rx_on_wakeup = 1, + .rx_to_inject = 0x32, +}; +#endif + static void __init sapphire_init(void) { + int rc; + int i; + printk("sapphire_init() revision=%d\n", system_rev); + + /* + * Setup common MSM GPIOS + */ + config_gpios(); + + msm_hw_reset_hook = sapphire_reset; + + msm_acpu_clock_init(&sapphire_clock_data); + + /* adjust GPIOs based on bootloader request */ + printk("sapphire_init: cpld_usb_hw2_sw = %d\n", cpld_usb_h2w_sw); + gpio_set_value(SAPPHIRE_GPIO_USB_H2W_SW, cpld_usb_h2w_sw); + +#if defined(CONFIG_MSM_SERIAL_DEBUGGER) + if (!opt_disable_uart3) + msm_serial_debug_init(MSM_UART3_PHYS, INT_UART3, + &msm_device_uart3.dev, 1); +#endif + + /* gpio_configure(108, IRQF_TRIGGER_LOW); */ + + /* H2W pins <-> UART3, Bluetooth <-> UART1 */ + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 1); + /* put the AF VCM in powerdown mode to avoid noise */ + if (sapphire_is_5M_camera()) + sapphire_gpio_write(NULL, SAPPHIRE_GPIO_VCM_PWDN, 0); + else + sapphire_gpio_write(NULL, SAPPHIRE_GPIO_VCM_PWDN, 1); + mdelay(100); + +#ifdef CONFIG_SERIAL_MSM_HS + msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; +#endif + msm_add_usb_devices(sapphire_phy_reset); + + if (32 == smi_sz) + msm_add_mem_devices(&pmem_setting_32); + else + msm_add_mem_devices(&pmem_setting_64); + + rc = sapphire_init_mmc(system_rev); + if (rc) + printk(KERN_CRIT "%s: MMC init failure (%d)\n", __func__, rc); + +#ifdef CONFIG_WIFI_MEM_PREALLOC + rc = sapphire_init_wifi_mem(); + if (rc) { + printk(KERN_CRIT "%s: WiFi memory init failure (%d)\n", + __func__, rc); + } +#endif + msm_init_pmic_vibrator(); + platform_add_devices(devices, ARRAY_SIZE(devices)); + i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); } static struct map_desc sapphire_io_desc[] __initdata = { @@ -77,10 +1072,69 @@ static struct map_desc sapphire_io_desc[] __initdata = { } }; + +unsigned int sapphire_get_hwid(void) +{ + printk(KERN_DEBUG "sapphire_get_hwid=0x%x\r\n", hwid); + + return hwid; +} + +unsigned int sapphire_get_skuid(void) +{ + printk(KERN_DEBUG "sapphire_get_skuid=0x%x\r\n", skuid); + + return skuid; +} + +unsigned sapphire_engineerid(void) +{ + printk(KERN_DEBUG "sapphire_engineerid=0x%x\r\n", engineerid); + + return engineerid; +} + +int sapphire_is_5M_camera(void) +{ + int ret = 0; + if (sapphire_get_skuid() == 0x1FF00 && !(sapphire_engineerid() & 0x02)) + ret = 1; + else if (sapphire_get_skuid() == 0x20100 && !(sapphire_engineerid() & 0x02)) + ret = 1; + printk(KERN_DEBUG "sapphire_is_5M_camera=%d\n", ret); + return ret; +} + +/* it can support 3M and 5M sensor */ +unsigned int is_12pin_camera(void) +{ + unsigned int ret = 0; + + if (sapphire_get_skuid() == 0x1FF00 || sapphire_get_skuid() == 0x20100) + ret = 1; + else + ret = 0; + printk(KERN_DEBUG "is_12pin_camera=%d\r\n", ret); + return ret; +} + +int sapphire_get_smi_size(void) +{ + printk(KERN_DEBUG "get_smi_size=%d\r\n", smi_sz); + return smi_sz; +} + static void __init sapphire_fixup(struct machine_desc *desc, struct tag *tags, char **cmdline, struct meminfo *mi) { - int smi_sz = parse_tag_smi((const struct tag *)tags); + smi_sz = parse_tag_smi((const struct tag *)tags); + printk("sapphire_fixup:smisize=%d\n", smi_sz); + hwid = parse_tag_hwid((const struct tag *)tags); + printk("sapphire_fixup:hwid=0x%x\n", hwid); + skuid = parse_tag_skuid((const struct tag *)tags); + printk("sapphire_fixup:skuid=0x%x\n", skuid); + engineerid = parse_tag_engineerid((const struct tag *)tags); + printk("sapphire_fixup:engineerid=0x%x\n", engineerid); mi->nr_banks = 1; mi->bank[0].start = PHYS_OFFSET; @@ -90,9 +1144,12 @@ static void __init sapphire_fixup(struct machine_desc *desc, struct tag *tags, } else if (smi_sz == 64) { mi->bank[0].size = (101*1024*1024); } else { - /* Give a default value when not get smi size */ + printk(KERN_ERR "can not get smi size\n"); + + /*Give a default value when not get smi size*/ smi_sz = 64; mi->bank[0].size = (101*1024*1024); + printk(KERN_ERR "use default : smisize=%d\n", smi_sz); } } @@ -100,14 +1157,14 @@ static void __init sapphire_map_io(void) { msm_map_common_io(); iotable_init(sapphire_io_desc, ARRAY_SIZE(sapphire_io_desc)); - msm_clock_init(); + msm_clock_init(msm_clocks_7x01a, msm_num_clocks_7x01a); } MACHINE_START(SAPPHIRE, "sapphire") /* Maintainer: Brian Swetland */ #ifdef CONFIG_MSM_DEBUG_UART #endif - .boot_params = PHYS_OFFSET + 0x100, + .boot_params = 0x10000100, .fixup = sapphire_fixup, .map_io = sapphire_map_io, .init_irq = sapphire_init_irq, diff --git a/arch/arm/mach-msm/board-sapphire.h b/arch/arm/mach-msm/board-sapphire.h new file mode 100644 index 0000000000000..3327dfeafde0b --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire.h @@ -0,0 +1,219 @@ +/* linux/arch/arm/mach-msm/board-sapphire.h + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai + * + * 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 __ARCH_ARM_MACH_MSM_BOARD_SAPPHIRE_H +#define __ARCH_ARM_MACH_MSM_BOARD_SAPPHIRE_H + +#include + +#define MSM_SMI_BASE 0x00000000 +#define MSM_SMI_SIZE 0x00800000 + +#define MSM_EBI_BASE 0x10000000 +#define MSM_EBI_SIZE 0x06e00000 + +#define MSM_PMEM_GPU0_BASE 0x00000000 +#define MSM_PMEM_GPU0_SIZE 0x00700000 + +#define SMI64_MSM_PMEM_MDP_BASE 0x02000000 +#define SMI64_MSM_PMEM_MDP_SIZE 0x00800000 + +#define SMI64_MSM_PMEM_ADSP_BASE 0x02800000 +#define SMI64_MSM_PMEM_ADSP_SIZE 0x00800000 + +#define SMI64_MSM_PMEM_CAMERA_BASE 0x03000000 +#define SMI64_MSM_PMEM_CAMERA_SIZE 0x01000000 + +#define SMI64_MSM_FB_BASE 0x00700000 +#define SMI64_MSM_FB_SIZE 0x00100000 + +#define SMI64_MSM_LINUX_BASE MSM_EBI_BASE +#define SMI64_MSM_LINUX_SIZE 0x06500000 + + +#define SMI32_MSM_LINUX_BASE MSM_EBI_BASE +#define SMI32_MSM_LINUX_SIZE 0x5400000 + +#define SMI32_MSM_PMEM_MDP_BASE SMI32_MSM_LINUX_BASE + SMI32_MSM_LINUX_SIZE +#define SMI32_MSM_PMEM_MDP_SIZE 0x800000 + +#define SMI32_MSM_PMEM_ADSP_BASE SMI32_MSM_PMEM_MDP_BASE + SMI32_MSM_PMEM_MDP_SIZE +#define SMI32_MSM_PMEM_ADSP_SIZE 0x800000 + +#define SMI32_MSM_FB_BASE SMI32_MSM_PMEM_ADSP_BASE + SMI32_MSM_PMEM_ADSP_SIZE +#define SMI32_MSM_FB_SIZE 0x9b000 + + +#define MSM_PMEM_GPU1_SIZE 0x800000 +#define MSM_PMEM_GPU1_BASE MSM_RAM_CONSOLE_BASE - MSM_PMEM_GPU1_SIZE + +#define MSM_RAM_CONSOLE_BASE MSM_EBI_BASE + 0x6d00000 +#define MSM_RAM_CONSOLE_SIZE 128 * SZ_1K + +#if (SMI32_MSM_FB_BASE + SMI32_MSM_FB_SIZE) >= (MSM_PMEM_GPU1_BASE) +#error invalid memory map +#endif + +#if (SMI64_MSM_FB_BASE + SMI64_MSM_FB_SIZE) >= (MSM_PMEM_GPU1_BASE) +#error invalid memory map +#endif + +#define DECLARE_MSM_IOMAP +#include + +/* +** SOC GPIO +*/ +#define SAPPHIRE_BALL_UP_0 94 +#define SAPPHIRE_BALL_LEFT_0 18 +#define SAPPHIRE_BALL_DOWN_0 49 +#define SAPPHIRE_BALL_RIGHT_0 19 + +#define SAPPHIRE_POWER_KEY 20 +#define SAPPHIRE_VOLUME_UP 36 +#define SAPPHIRE_VOLUME_DOWN 39 + +#define SAPPHIRE_GPIO_PS_HOLD (25) +#define SAPPHIRE_MDDI_1V5_EN (28) +#define SAPPHIRE_BL_PWM (27) +#define SAPPHIRE_TP_LS_EN (1) +#define SAPPHIRE20_TP_LS_EN (88) + +/* H2W */ +#define SAPPHIRE_GPIO_CABLE_IN1 (83) +#define SAPPHIRE_GPIO_CABLE_IN2 (37) +#define SAPPHIRE_GPIO_UART3_RX (86) +#define SAPPHIRE_GPIO_UART3_TX (87) +#define SAPPHIRE_GPIO_H2W_DATA (86) +#define SAPPHIRE_GPIO_H2W_CLK (87) + +#define SAPPHIRE_GPIO_UART1_RTS (43) +#define SAPPHIRE_GPIO_UART1_CTS (44) + +/* +** CPLD GPIO +** +** Sapphire Altera CPLD can keep the registers value and +** doesn't need a shadow to backup. +**/ +#define SAPPHIRE_CPLD_BASE 0xE8100000 /* VA */ +#define SAPPHIRE_CPLD_START 0x98000000 /* PA */ +#define SAPPHIRE_CPLD_SIZE SZ_4K + +#define SAPPHIRE_GPIO_START (128) /* Pseudo GPIO number */ + +/* Sapphire has one INT BANK only. */ +#define SAPPHIRE_GPIO_INT_B0_MASK_REG (0x0c) /*INT3 MASK*/ +#define SAPPHIRE_GPIO_INT_B0_STAT_REG (0x0e) /*INT1 STATUS*/ + +/* LED control register */ +#define SAPPHIRE_CPLD_LED_BASE (SAPPHIRE_CPLD_BASE + 0x10) /* VA */ +#define SAPPHIRE_CPLD_LED_START (SAPPHIRE_CPLD_START + 0x10) /* PA */ +#define SAPPHIRE_CPLD_LED_SIZE 0x08 + +/* MISCn: GPO pin to Enable/Disable some functions. */ +#define SAPPHIRE_GPIO_MISC1_BASE (SAPPHIRE_GPIO_START + 0x00) +#define SAPPHIRE_GPIO_MISC2_BASE (SAPPHIRE_GPIO_START + 0x08) +#define SAPPHIRE_GPIO_MISC3_BASE (SAPPHIRE_GPIO_START + 0x10) +#define SAPPHIRE_GPIO_MISC4_BASE (SAPPHIRE_GPIO_START + 0x18) +#define SAPPHIRE_GPIO_MISC5_BASE (SAPPHIRE_GPIO_START + 0x20) + +/* INT BANK0: INT1: int status, INT2: int level, INT3: int Mask */ +#define SAPPHIRE_GPIO_INT_B0_BASE (SAPPHIRE_GPIO_START + 0x28) + +/* MISCn GPIO: */ +#define SAPPHIRE_GPIO_CPLD128_VER_0 (SAPPHIRE_GPIO_MISC1_BASE + 4) +#define SAPPHIRE_GPIO_CPLD128_VER_1 (SAPPHIRE_GPIO_MISC1_BASE + 5) +#define SAPPHIRE_GPIO_CPLD128_VER_2 (SAPPHIRE_GPIO_MISC1_BASE + 6) +#define SAPPHIRE_GPIO_CPLD128_VER_3 (SAPPHIRE_GPIO_MISC1_BASE + 7) + +#define SAPPHIRE_GPIO_H2W_DAT_DIR (SAPPHIRE_GPIO_MISC2_BASE + 2) +#define SAPPHIRE_GPIO_H2W_CLK_DIR (SAPPHIRE_GPIO_MISC2_BASE + 3) +#define SAPPHIRE_GPIO_H2W_SEL0 (SAPPHIRE_GPIO_MISC2_BASE + 6) +#define SAPPHIRE_GPIO_H2W_SEL1 (SAPPHIRE_GPIO_MISC2_BASE + 7) + +#define SAPPHIRE_GPIO_I2C_PULL (SAPPHIRE_GPIO_MISC3_BASE + 2) +#define SAPPHIRE_GPIO_TP_EN (SAPPHIRE_GPIO_MISC3_BASE + 4) +#define SAPPHIRE_GPIO_JOG_EN (SAPPHIRE_GPIO_MISC3_BASE + 5) +#define SAPPHIRE_GPIO_JOG_LED_EN (SAPPHIRE_GPIO_MISC3_BASE + 6) +#define SAPPHIRE_GPIO_APKEY_LED_EN (SAPPHIRE_GPIO_MISC3_BASE + 7) + +#define SAPPHIRE_GPIO_VCM_PWDN (SAPPHIRE_GPIO_MISC4_BASE + 0) +#define SAPPHIRE_GPIO_USB_H2W_SW (SAPPHIRE_GPIO_MISC4_BASE + 1) +#define SAPPHIRE_GPIO_COMPASS_RST_N (SAPPHIRE_GPIO_MISC4_BASE + 2) +#define SAPPHIRE_GPIO_USB_PHY_RST_N (SAPPHIRE_GPIO_MISC4_BASE + 5) +#define SAPPHIRE_GPIO_WIFI_PA_RESETX (SAPPHIRE_GPIO_MISC4_BASE + 6) +#define SAPPHIRE_GPIO_WIFI_EN (SAPPHIRE_GPIO_MISC4_BASE + 7) + +#define SAPPHIRE_GPIO_BT_32K_EN (SAPPHIRE_GPIO_MISC5_BASE + 0) +#define SAPPHIRE_GPIO_MAC_32K_EN (SAPPHIRE_GPIO_MISC5_BASE + 1) +#define SAPPHIRE_GPIO_MDDI_32K_EN (SAPPHIRE_GPIO_MISC5_BASE + 2) +#define SAPPHIRE_GPIO_COMPASS_32K_EN (SAPPHIRE_GPIO_MISC5_BASE + 3) + +/* INT STATUS/LEVEL/MASK : INT GPIO should be the last. */ +#define SAPPHIRE_GPIO_NAVI_ACT_N (SAPPHIRE_GPIO_INT_B0_BASE + 0) +#define SAPPHIRE_GPIO_COMPASS_IRQ (SAPPHIRE_GPIO_INT_B0_BASE + 1) +#define SAPPHIRE_GPIO_SEARCH_ACT_N (SAPPHIRE_GPIO_INT_B0_BASE + 2) +#define SAPPHIRE_GPIO_AUD_HSMIC_DET_N (SAPPHIRE_GPIO_INT_B0_BASE + 3) +#define SAPPHIRE_GPIO_SDMC_CD_N (SAPPHIRE_GPIO_INT_B0_BASE + 4) +#define SAPPHIRE_GPIO_CAM_BTN_STEP1_N (SAPPHIRE_GPIO_INT_B0_BASE + 5) +#define SAPPHIRE_GPIO_CAM_BTN_STEP2_N (SAPPHIRE_GPIO_INT_B0_BASE + 6) +#define SAPPHIRE_GPIO_TP_ATT_N (SAPPHIRE_GPIO_INT_B0_BASE + 7) + +#define SAPPHIRE_GPIO_END SAPPHIRE_GPIO_TP_ATT_N +#define SAPPHIRE_GPIO_LAST_INT (SAPPHIRE_GPIO_TP_ATT_N) + +/* Bit position in the CPLD MISCn by the CPLD GPIOn: only bit0-7 is used. */ +#define CPLD_GPIO_BIT_POS_MASK(n) (1U << ((n) & 7)) +#define CPLD_GPIO_REG_OFFSET(n) _g_CPLD_MISCn_Offset[((n)-SAPPHIRE_GPIO_START) >> 3] +#define CPLD_GPIO_REG(n) (CPLD_GPIO_REG_OFFSET(n) + SAPPHIRE_CPLD_BASE) + +/* +** CPLD INT Start +*/ +#define SAPPHIRE_INT_START (NR_MSM_IRQS + NR_GPIO_IRQS) /* pseudo number for CPLD INT */ +/* Using INT status/Bank0 for GPIO to INT */ +#define SAPPHIRE_GPIO_TO_INT(n) ((n-SAPPHIRE_GPIO_INT_B0_BASE) + SAPPHIRE_INT_START) +#define SAPPHIRE_INT_END (SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_END)) + +/* get the INT reg by GPIO number */ +#define CPLD_INT_GPIO_TO_BANK(n) (((n)-SAPPHIRE_GPIO_INT_B0_BASE) >> 3) +#define CPLD_INT_STATUS_REG_OFFSET_G(n) _g_INT_BANK_Offset[CPLD_INT_GPIO_TO_BANK(n)][0] +#define CPLD_INT_LEVEL_REG_OFFSET_G(n) _g_INT_BANK_Offset[CPLD_INT_GPIO_TO_BANK(n)][1] +#define CPLD_INT_MASK_REG_OFFSET_G(n) _g_INT_BANK_Offset[CPLD_INT_GPIO_TO_BANK(n)][2] +#define CPLD_INT_STATUS_REG_G(n) (SAPPHIRE_CPLD_BASE + CPLD_INT_STATUS_REG_OFFSET_G(n)) +#define CPLD_INT_LEVEL_REG_G(n) (SAPPHIRE_CPLD_BASE + CPLD_INT_LEVEL_REG_OFFSET_G(n)) +#define CPLD_INT_MASK_REG_G(n) (SAPPHIRE_CPLD_BASE + CPLD_INT_MASK_REG_OFFSET_G(n)) + +/* get the INT reg by INT number */ +#define CPLD_INT_TO_BANK(i) ((i-SAPPHIRE_INT_START) >> 3) +#define CPLD_INT_STATUS_REG_OFFSET(i) _g_INT_BANK_Offset[CPLD_INT_TO_BANK(i)][0] +#define CPLD_INT_LEVEL_REG_OFFSET(i) _g_INT_BANK_Offset[CPLD_INT_TO_BANK(i)][1] +#define CPLD_INT_MASK_REG_OFFSET(i) _g_INT_BANK_Offset[CPLD_INT_TO_BANK(i)][2] +#define CPLD_INT_STATUS_REG(i) (SAPPHIRE_CPLD_BASE + CPLD_INT_STATUS_REG_OFFSET(i)) +#define CPLD_INT_LEVEL_REG(i) (SAPPHIRE_CPLD_BASE + CPLD_INT_LEVEL_REG_OFFSET(i)) +#define CPLD_INT_MASK_REG(i) (SAPPHIRE_CPLD_BASE + CPLD_INT_MASK_REG_OFFSET(i) ) + +/* return the bit mask by INT number */ +#define SAPPHIRE_INT_BIT_MASK(i) (1U << ((i - SAPPHIRE_INT_START) & 7)) + +void config_sapphire_camera_on_gpios(void); +void config_sapphire_camera_off_gpios(void); +int sapphire_get_smi_size(void); +unsigned int sapphire_get_hwid(void); +unsigned int sapphire_get_skuid(void); +unsigned int is_12pin_camera(void); +int sapphire_is_5M_camera(void); +int sapphire_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on); + +#endif /* GUARD */ diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c new file mode 100644 index 0000000000000..dd6a26fbf16e7 --- /dev/null +++ b/arch/arm/mach-msm/devices_htc.c @@ -0,0 +1,434 @@ +/* linux/arch/arm/mach-msm/devices.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai + * + * 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 "gpio_chip.h" +#include "devices.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *df_serialno = "000000000000"; + +#if 0 +struct platform_device *devices[] __initdata = { + &msm_device_nand, + &msm_device_smd, + &msm_device_i2c, +}; + +void __init msm_add_devices(void) +{ + platform_add_devices(devices, ARRAY_SIZE(devices)); +} +#endif + +#define HSUSB_API_INIT_PHY_PROC 2 +#define HSUSB_API_PROG 0x30000064 +#define HSUSB_API_VERS 0x10001 +static void internal_phy_reset(void) +{ + struct msm_rpc_endpoint *usb_ep; + int rc; + struct hsusb_phy_start_req { + struct rpc_request_hdr hdr; + } req; + + printk(KERN_INFO "msm_hsusb_phy_reset\n"); + + usb_ep = msm_rpc_connect(HSUSB_API_PROG, HSUSB_API_VERS, 0); + if (IS_ERR(usb_ep)) { + printk(KERN_ERR "%s: init rpc failed! error: %ld\n", + __func__, PTR_ERR(usb_ep)); + goto close; + } + rc = msm_rpc_call(usb_ep, HSUSB_API_INIT_PHY_PROC, + &req, sizeof(req), 5 * HZ); + if (rc < 0) + printk(KERN_ERR "%s: rpc call failed! (%d)\n", __func__, rc); + +close: + msm_rpc_close(usb_ep); +} + +/* adjust eye diagram, disable vbusvalid interrupts */ +static int hsusb_phy_init_seq[] = { 0x40, 0x31, 0x1D, 0x0D, 0x1D, 0x10, -1 }; + +static char *usb_functions[] = { +#if defined(CONFIG_USB_FUNCTION_MASS_STORAGE) || defined(CONFIG_USB_FUNCTION_UMS) + "usb_mass_storage", +#endif +#ifdef CONFIG_USB_FUNCTION_ADB + "adb", +#endif +}; + +static struct msm_hsusb_product usb_products[] = { + { + .product_id = 0x0c01, + .functions = 0x00000041, /* usb_mass_storage */ + }, + { + .product_id = 0x0c02, + .functions = 0x00000043, /* usb_mass_storage + adb */ + }, +}; + +#ifdef CONFIG_USB_FUNCTION +struct msm_hsusb_platform_data msm_hsusb_pdata = { + .phy_reset = internal_phy_reset, + .phy_init_seq = hsusb_phy_init_seq, + .vendor_id = 0x0bb4, + .product_id = 0x0c02, + .version = 0x0100, + .product_name = "Android Phone", + .manufacturer_name = "HTC", + + .functions = usb_functions, + .num_functions = ARRAY_SIZE(usb_functions), + .products = usb_products, + .num_products = ARRAY_SIZE(usb_products), +}; + +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .buf_size = 16384, + .vendor = "HTC ", + .product = "Android Phone ", + .release = 0x0100, +}; + +static struct platform_device usb_mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &mass_storage_pdata, + }, +}; + +#ifdef CONFIG_USB_ANDROID +static struct android_usb_platform_data android_usb_pdata = { + .vendor_id = 0x0bb4, + .product_id = 0x0c01, + .adb_product_id = 0x0c02, + .version = 0x0100, + .product_name = "Android Phone", + .manufacturer_name = "HTC", + .nluns = 1, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; +#endif + +void __init msm_add_usb_devices(void (*phy_reset) (void)) +{ + if (phy_reset) + msm_hsusb_pdata.phy_reset = phy_reset; + /* setup */ + msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; + platform_device_register(&msm_device_hsusb); +#ifdef CONFIG_USB_FUNCTION_MASS_STORAGE + platform_device_register(&usb_mass_storage_device); +#endif +} +#endif + +static struct android_pmem_platform_data pmem_pdata = { + .name = "pmem", + .no_allocator = 1, + .cached = 1, +}; + +static struct android_pmem_platform_data pmem_adsp_pdata = { + .name = "pmem_adsp", + .no_allocator = 0, + .cached = 0, +}; + +static struct android_pmem_platform_data pmem_camera_pdata = { + .name = "pmem_camera", + .no_allocator = 0, + .cached = 0, +}; + +static struct android_pmem_platform_data pmem_gpu0_pdata = { + .name = "pmem_gpu0", + .no_allocator = 1, + .cached = 0, + .buffered = 1, +}; + +static struct android_pmem_platform_data pmem_gpu1_pdata = { + .name = "pmem_gpu1", + .no_allocator = 1, + .cached = 0, + .buffered = 1, +}; + +static struct platform_device pmem_device = { + .name = "android_pmem", + .id = 0, + .dev = { .platform_data = &pmem_pdata }, +}; + +static struct platform_device pmem_adsp_device = { + .name = "android_pmem", + .id = 1, + .dev = { .platform_data = &pmem_adsp_pdata }, +}; + +static struct platform_device pmem_gpu0_device = { + .name = "android_pmem", + .id = 2, + .dev = { .platform_data = &pmem_gpu0_pdata }, +}; + +static struct platform_device pmem_gpu1_device = { + .name = "android_pmem", + .id = 3, + .dev = { .platform_data = &pmem_gpu1_pdata }, +}; + +static struct platform_device pmem_camera_device = { + .name = "android_pmem", + .id = 4, + .dev = { .platform_data = &pmem_camera_pdata }, +}; + +static struct resource ram_console_resource[] = { + { + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device ram_console_device = { + .name = "ram_console", + .id = -1, + .num_resources = ARRAY_SIZE(ram_console_resource), + .resource = ram_console_resource, +}; + +void __init msm_add_mem_devices(struct msm_pmem_setting *setting) +{ + if (setting->pmem_size) { + pmem_pdata.start = setting->pmem_start; + pmem_pdata.size = setting->pmem_size; + platform_device_register(&pmem_device); + } + + if (setting->pmem_adsp_size) { + pmem_adsp_pdata.start = setting->pmem_adsp_start; + pmem_adsp_pdata.size = setting->pmem_adsp_size; + platform_device_register(&pmem_adsp_device); + } + + if (setting->pmem_gpu0_size) { + pmem_gpu0_pdata.start = setting->pmem_gpu0_start; + pmem_gpu0_pdata.size = setting->pmem_gpu0_size; + platform_device_register(&pmem_gpu0_device); + } + + if (setting->pmem_gpu1_size) { + pmem_gpu1_pdata.start = setting->pmem_gpu1_start; + pmem_gpu1_pdata.size = setting->pmem_gpu1_size; + platform_device_register(&pmem_gpu1_device); + } + + if (setting->pmem_camera_size) { + pmem_camera_pdata.start = setting->pmem_camera_start; + pmem_camera_pdata.size = setting->pmem_camera_size; + platform_device_register(&pmem_camera_device); + } + + if (setting->ram_console_size) { + ram_console_resource[0].start = setting->ram_console_start; + ram_console_resource[0].end = setting->ram_console_start + + setting->ram_console_size - 1; + platform_device_register(&ram_console_device); + } +} + +#define PM_LIBPROG 0x30000061 +#if (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225) +#define PM_LIBVERS 0xfb837d0b +#else +#define PM_LIBVERS 0x10001 +#endif + +#if 0 +static struct platform_device *msm_serial_devices[] __initdata = { + &msm_device_uart1, + &msm_device_uart2, + &msm_device_uart3, + #ifdef CONFIG_SERIAL_MSM_HS + &msm_device_uart_dm1, + &msm_device_uart_dm2, + #endif +}; + +int __init msm_add_serial_devices(unsigned num) +{ + if (num > MSM_SERIAL_NUM) + return -EINVAL; + + return platform_device_register(msm_serial_devices[num]); +} +#endif + +#define ATAG_SMI 0x4d534D71 +/* setup calls mach->fixup, then parse_tags, parse_cmdline + * We need to setup meminfo in mach->fixup, so this function + * will need to traverse each tag to find smi tag. + */ +int __init parse_tag_smi(const struct tag *tags) +{ + int smi_sz = 0, find = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_SMI) { + printk(KERN_DEBUG "find the smi tag\n"); + find = 1; + break; + } + } + if (!find) + return -1; + + printk(KERN_DEBUG "parse_tag_smi: smi size = %d\n", t->u.mem.size); + smi_sz = t->u.mem.size; + return smi_sz; +} +__tagtable(ATAG_SMI, parse_tag_smi); + + +#define ATAG_HWID 0x4d534D72 +int __init parse_tag_hwid(const struct tag *tags) +{ + int hwid = 0, find = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_HWID) { + printk(KERN_DEBUG "find the hwid tag\n"); + find = 1; + break; + } + } + + if (find) + hwid = t->u.revision.rev; + printk(KERN_DEBUG "parse_tag_hwid: hwid = 0x%x\n", hwid); + return hwid; +} +__tagtable(ATAG_HWID, parse_tag_hwid); + +#define ATAG_SKUID 0x4d534D73 +int __init parse_tag_skuid(const struct tag *tags) +{ + int skuid = 0, find = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_SKUID) { + printk(KERN_DEBUG "find the skuid tag\n"); + find = 1; + break; + } + } + + if (find) + skuid = t->u.revision.rev; + printk(KERN_DEBUG "parse_tag_skuid: hwid = 0x%x\n", skuid); + return skuid; +} +__tagtable(ATAG_SKUID, parse_tag_skuid); + +#define ATAG_ENGINEERID 0x4d534D75 +int __init parse_tag_engineerid(const struct tag *tags) +{ + int engineerid = 0, find = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_ENGINEERID) { + printk(KERN_DEBUG "find the engineer tag\n"); + find = 1; + break; + } + } + + if (find) + engineerid = t->u.revision.rev; + printk(KERN_DEBUG "parse_tag_engineerid: hwid = 0x%x\n", engineerid); + return engineerid; +} +__tagtable(ATAG_ENGINEERID, parse_tag_engineerid); + +static int mfg_mode; +int __init board_mfg_mode_init(char *s) +{ + if (!strcmp(s, "normal")) + mfg_mode = 0; + else if (!strcmp(s, "factory2")) + mfg_mode = 1; + else if (!strcmp(s, "recovery")) + mfg_mode = 2; + else if (!strcmp(s, "charge")) + mfg_mode = 3; + + return 1; +} +__setup("androidboot.mode=", board_mfg_mode_init); + + +int board_mfg_mode(void) +{ + return mfg_mode; +} + +static int __init board_serialno_setup(char *serialno) +{ + if (board_mfg_mode() || !strlen(serialno)) + msm_hsusb_pdata.serial_number = df_serialno; + else + msm_hsusb_pdata.serial_number = serialno; + return 1; +} + +__setup("androidboot.serialno=", board_serialno_setup); diff --git a/arch/arm/mach-msm/include/mach/board_htc.h b/arch/arm/mach-msm/include/mach/board_htc.h new file mode 100644 index 0000000000000..b537c91b957a0 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/board_htc.h @@ -0,0 +1,78 @@ +/* arch/arm/mach-msm/include/mach/BOARD_HTC.h + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai + * + * 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 __ASM_ARCH_MSM_BOARD_HTC_H +#define __ASM_ARCH_MSM_BOARD_HTC_H + +#include +#include +#include + +struct msm_pmem_setting{ + resource_size_t pmem_start; + resource_size_t pmem_size; + resource_size_t pmem_adsp_start; + resource_size_t pmem_adsp_size; + resource_size_t pmem_gpu0_start; + resource_size_t pmem_gpu0_size; + resource_size_t pmem_gpu1_start; + resource_size_t pmem_gpu1_size; + resource_size_t pmem_camera_start; + resource_size_t pmem_camera_size; + resource_size_t ram_console_start; + resource_size_t ram_console_size; +}; + +enum { + MSM_SERIAL_UART1 = 0, + MSM_SERIAL_UART2, + MSM_SERIAL_UART3, +#ifdef CONFIG_SERIAL_MSM_HS + MSM_SERIAL_UART1DM, + MSM_SERIAL_UART2DM, +#endif + MSM_SERIAL_NUM, +}; + + +/* common init routines for use by arch/arm/mach-msm/board-*.c */ + +void __init msm_add_usb_devices(void (*phy_reset) (void)); +void __init msm_add_mem_devices(struct msm_pmem_setting *setting); +void __init msm_init_pmic_vibrator(void); + +struct mmc_platform_data; +int __init msm_add_sdcc_devices(unsigned int controller, struct mmc_platform_data *plat); +int __init msm_add_serial_devices(unsigned uart); + +#if defined(CONFIG_USB_FUNCTION_MSM_HSUSB) +/* START: add USB connected notify function */ +struct t_usb_status_notifier{ + struct list_head notifier_link; + const char *name; + void (*func)(int online); +}; + int usb_register_notifier(struct t_usb_status_notifier *); + static LIST_HEAD(g_lh_usb_notifier_list); +/* END: add USB connected notify function */ +#endif + +int __init board_mfg_mode(void); +int __init parse_tag_smi(const struct tag *tags); +int __init parse_tag_hwid(const struct tag * tags); +int __init parse_tag_skuid(const struct tag * tags); +int parse_tag_engineerid(const struct tag * tags); + +char *board_serialno(void); + +#endif From 77c82bee541a425d9ec92bc3ef436d734376f289 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Tue, 17 Feb 2009 21:58:54 -0500 Subject: [PATCH 0282/2556] [ARM] msm: sapphire: Fix rounding in sapphire_set_backlight_level Previously all values less 22 turned the backlight completely off Now 0 is off and values 1 - 21 are the lowest dim value Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/board-sapphire-panel.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire-panel.c b/arch/arm/mach-msm/board-sapphire-panel.c index 24933f3eab52e..9129f4cdcb5fa 100644 --- a/arch/arm/mach-msm/board-sapphire-panel.c +++ b/arch/arm/mach-msm/board-sapphire-panel.c @@ -49,7 +49,7 @@ static int sapphire_backlight_brightness = static uint8_t sapphire_backlight_last_level = 33; static DEFINE_MUTEX(sapphire_backlight_lock); -/* Divid dimming level into 12 sections, and restrict maximum level to 27 */ +/* Divide dimming level into 12 sections, and restrict maximum level to 27 */ #define DIMMING_STEPS 12 static unsigned dimming_levels[NUM_OF_SAPPHIRE_PANELS][DIMMING_STEPS] = { {0, 1, 2, 3, 6, 9, 11, 13, 16, 19, 22, 25}, /* Sharp */ @@ -60,16 +60,16 @@ static unsigned pwrsink_percents[] = {0, 6, 8, 15, 26, 34, 46, 54, 65, 77, 87, static void sapphire_set_backlight_level(uint8_t level) { - unsigned dimming_factor = 255/DIMMING_STEPS + 1 ; - unsigned percent = ((int)level * 100) / 255; + unsigned dimming_factor = 255/DIMMING_STEPS + 1; + int index = (level + dimming_factor - 1) / dimming_factor; + unsigned percent; unsigned long flags; int i = 0; printk(KERN_INFO "level=%d, new level=dimming_levels[%d]=%d\n", - level, level/dimming_factor, - dimming_levels[g_panel_id][level/dimming_factor]); - percent = pwrsink_percents[level/dimming_factor]; - level = dimming_levels[g_panel_id][level/dimming_factor]; + level, index, dimming_levels[g_panel_id][index]); + percent = pwrsink_percents[index]; + level = dimming_levels[g_panel_id][index]; if (sapphire_backlight_last_level == level) return; From 1325774fcfb888e3d462f7d87ff70c8f9b1c13a0 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Fri, 20 Feb 2009 15:56:06 -0500 Subject: [PATCH 0283/2556] [ARM] msm: htc: Clean up USB support in trout and sapphire board files Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/board-sapphire.c | 9 ----- arch/arm/mach-msm/board-trout.c | 50 +---------------------- arch/arm/mach-msm/devices_htc.c | 64 +++++------------------------- 3 files changed, 13 insertions(+), 110 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index b1321c0f23656..a0af1054c75c9 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -28,12 +28,6 @@ #include #include #include -#ifdef CONFIG_USB_FUNCTION -#include -#endif -#ifdef CONFIG_USB_ANDROID -#include -#endif #include @@ -64,7 +58,6 @@ #include #include -#include #include #include @@ -610,7 +603,6 @@ static struct platform_device sapphire_h2w = { }; #endif -#ifdef CONFIG_USB_FUNCTION static void sapphire_phy_reset(void) { gpio_set_value(SAPPHIRE_GPIO_USB_PHY_RST_N, 0); @@ -618,7 +610,6 @@ static void sapphire_phy_reset(void) gpio_set_value(SAPPHIRE_GPIO_USB_PHY_RST_N, 1); mdelay(10); } -#endif static struct pwr_sink sapphire_pwrsink_table[] = { { diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 6442b744be7d8..35e1dc82811fe 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -28,9 +28,6 @@ #include #include #include -#ifdef CONFIG_USB_ANDROID -#include -#endif #include @@ -61,7 +58,7 @@ #include "gpio_chip.h" #include -#include +#include #include #include #ifdef CONFIG_HTC_HEADSET @@ -476,9 +473,6 @@ static struct platform_device trout_h2w = { }; #endif -/* adjust eye diagram, disable vbusvalid interrupts */ -static int trout_phy_init_seq[] = { 0x40, 0x31, 0x1D, 0x0D, 0x1D, 0x10, -1 }; - static void trout_phy_reset(void) { gpio_set_value(TROUT_GPIO_USB_PHY_RST_N, 0); @@ -487,31 +481,6 @@ static void trout_phy_reset(void) mdelay(10); } -static struct msm_hsusb_platform_data msm_hsusb_pdata = { - .phy_reset = trout_phy_reset, - .phy_init_seq = trout_phy_init_seq, -}; - -#ifdef CONFIG_USB_ANDROID -static struct android_usb_platform_data android_usb_pdata = { - .vendor_id = 0x0bb4, - .product_id = 0x0c01, - .adb_product_id = 0x0c02, - .version = 0x0100, - .product_name = "Android Phone", - .manufacturer_name = "HTC", - .nluns = 1, -}; - -static struct platform_device android_usb_device = { - .name = "android_usb", - .id = -1, - .dev = { - .platform_data = &android_usb_pdata, - }, -}; -#endif - static struct resource trout_ram_console_resource[] = { { .start = MSM_RAM_CONSOLE_BASE, @@ -696,10 +665,6 @@ static struct platform_device *devices[] __initdata = { #endif #ifdef CONFIG_SERIAL_MSM_HS &msm_device_uart_dm1, -#endif - &msm_device_hsusb, -#ifdef CONFIG_USB_ANDROID - &android_usb_device, #endif &trout_nav_device, &trout_reset_keys_device, @@ -738,16 +703,6 @@ static uint opt_disable_uart3; module_param_named(disable_uart3, opt_disable_uart3, uint, 0); -static int __init trout_serialno_setup(char *str) -{ -#ifdef CONFIG_USB_ANDROID - android_usb_pdata.serial_number = str; -#endif - return 1; -} - -__setup("androidboot.serialno=", trout_serialno_setup); - static void trout_reset(void) { gpio_set_value(TROUT_GPIO_PS_HOLD, 0); @@ -887,11 +842,10 @@ static void __init trout_init(void) trout_y_axis.info.gpio = trout_4_y_axis_gpios; } - msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; - #ifdef CONFIG_SERIAL_MSM_HS msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; #endif + msm_add_usb_devices(trout_phy_reset); rc = trout_init_mmc(system_rev); if (rc) diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c index dd6a26fbf16e7..5e88f8e1ce83b 100644 --- a/arch/arm/mach-msm/devices_htc.c +++ b/arch/arm/mach-msm/devices_htc.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -83,56 +84,9 @@ static void internal_phy_reset(void) /* adjust eye diagram, disable vbusvalid interrupts */ static int hsusb_phy_init_seq[] = { 0x40, 0x31, 0x1D, 0x0D, 0x1D, 0x10, -1 }; -static char *usb_functions[] = { -#if defined(CONFIG_USB_FUNCTION_MASS_STORAGE) || defined(CONFIG_USB_FUNCTION_UMS) - "usb_mass_storage", -#endif -#ifdef CONFIG_USB_FUNCTION_ADB - "adb", -#endif -}; - -static struct msm_hsusb_product usb_products[] = { - { - .product_id = 0x0c01, - .functions = 0x00000041, /* usb_mass_storage */ - }, - { - .product_id = 0x0c02, - .functions = 0x00000043, /* usb_mass_storage + adb */ - }, -}; - -#ifdef CONFIG_USB_FUNCTION struct msm_hsusb_platform_data msm_hsusb_pdata = { .phy_reset = internal_phy_reset, .phy_init_seq = hsusb_phy_init_seq, - .vendor_id = 0x0bb4, - .product_id = 0x0c02, - .version = 0x0100, - .product_name = "Android Phone", - .manufacturer_name = "HTC", - - .functions = usb_functions, - .num_functions = ARRAY_SIZE(usb_functions), - .products = usb_products, - .num_products = ARRAY_SIZE(usb_products), -}; - -static struct usb_mass_storage_platform_data mass_storage_pdata = { - .nluns = 1, - .buf_size = 16384, - .vendor = "HTC ", - .product = "Android Phone ", - .release = 0x0100, -}; - -static struct platform_device usb_mass_storage_device = { - .name = "usb_mass_storage", - .id = -1, - .dev = { - .platform_data = &mass_storage_pdata, - }, }; #ifdef CONFIG_USB_ANDROID @@ -157,16 +111,15 @@ static struct platform_device android_usb_device = { void __init msm_add_usb_devices(void (*phy_reset) (void)) { + /* setup */ if (phy_reset) msm_hsusb_pdata.phy_reset = phy_reset; - /* setup */ msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; platform_device_register(&msm_device_hsusb); -#ifdef CONFIG_USB_FUNCTION_MASS_STORAGE - platform_device_register(&usb_mass_storage_device); +#ifdef CONFIG_USB_ANDROID + platform_device_register(&android_usb_device); #endif } -#endif static struct android_pmem_platform_data pmem_pdata = { .name = "pmem", @@ -424,10 +377,15 @@ int board_mfg_mode(void) static int __init board_serialno_setup(char *serialno) { + char *str; + if (board_mfg_mode() || !strlen(serialno)) - msm_hsusb_pdata.serial_number = df_serialno; + str = df_serialno; else - msm_hsusb_pdata.serial_number = serialno; + str = serialno; +#ifdef CONFIG_USB_ANDROID + android_usb_pdata.serial_number = str; +#endif return 1; } From 68060095112c437981bcfea5db0fad9010cdd6e1 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Sat, 21 Feb 2009 16:24:31 -0500 Subject: [PATCH 0284/2556] [ARM] asm: trout: remove duplicate code from board-trout.c pmem, adsp, camera, gpu0, gpu1 and ram_console platform devices are now initialized in devices_htc.c Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/board-trout.c | 110 +++++--------------------------- 1 file changed, 17 insertions(+), 93 deletions(-) diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 35e1dc82811fe..d26deddea3434 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -291,78 +291,6 @@ static struct i2c_board_info i2c_devices[] = { }, }; -static struct android_pmem_platform_data android_pmem_pdata = { - .name = "pmem", - .start = MSM_PMEM_MDP_BASE, - .size = MSM_PMEM_MDP_SIZE, - .no_allocator = 1, - .cached = 1, -}; - -static struct android_pmem_platform_data android_pmem_adsp_pdata = { - .name = "pmem_adsp", - .start = MSM_PMEM_ADSP_BASE, - .size = MSM_PMEM_ADSP_SIZE, - .no_allocator = 0, - .cached = 0, -}; - -static struct android_pmem_platform_data android_pmem_camera_pdata = { - .name = "pmem_camera", - .start = MSM_PMEM_CAMERA_BASE, - .size = MSM_PMEM_CAMERA_SIZE, - .no_allocator = 0, - .cached = 0, -}; - -static struct android_pmem_platform_data android_pmem_gpu0_pdata = { - .name = "pmem_gpu0", - .start = MSM_PMEM_GPU0_BASE, - .size = MSM_PMEM_GPU0_SIZE, - .no_allocator = 1, - .cached = 0, - .buffered = 1, -}; - -static struct android_pmem_platform_data android_pmem_gpu1_pdata = { - .name = "pmem_gpu1", - .start = MSM_PMEM_GPU1_BASE, - .size = MSM_PMEM_GPU1_SIZE, - .no_allocator = 1, - .cached = 0, - .buffered = 1, -}; - -static struct platform_device android_pmem_device = { - .name = "android_pmem", - .id = 0, - .dev = { .platform_data = &android_pmem_pdata }, -}; - -static struct platform_device android_pmem_adsp_device = { - .name = "android_pmem", - .id = 1, - .dev = { .platform_data = &android_pmem_adsp_pdata }, -}; - -static struct platform_device android_pmem_gpu0_device = { - .name = "android_pmem", - .id = 2, - .dev = { .platform_data = &android_pmem_gpu0_pdata }, -}; - -static struct platform_device android_pmem_gpu1_device = { - .name = "android_pmem", - .id = 3, - .dev = { .platform_data = &android_pmem_gpu1_pdata }, -}; - -static struct platform_device android_pmem_camera_device = { - .name = "android_pmem", - .id = 4, - .dev = { .platform_data = &android_pmem_camera_pdata }, -}; - static struct timed_gpio timed_gpios[] = { { .name = "vibrator", @@ -481,21 +409,6 @@ static void trout_phy_reset(void) mdelay(10); } -static struct resource trout_ram_console_resource[] = { - { - .start = MSM_RAM_CONSOLE_BASE, - .end = MSM_RAM_CONSOLE_BASE + MSM_RAM_CONSOLE_SIZE - 1, - .flags = IORESOURCE_MEM, - } -}; - -static struct platform_device trout_ram_console_device = { - .name = "ram_console", - .id = -1, - .num_resources = ARRAY_SIZE(trout_ram_console_resource), - .resource = trout_ram_console_resource, -}; - static void config_camera_on_gpios(void); static void config_camera_off_gpios(void); static struct msm_camera_device_platform_data msm_camera_device = { @@ -583,6 +496,21 @@ static struct platform_device trout_rfkill = { .id = -1, }; +static struct msm_pmem_setting pmem_setting = { + .pmem_start = MSM_PMEM_MDP_BASE, + .pmem_size = MSM_PMEM_MDP_SIZE, + .pmem_adsp_start = MSM_PMEM_ADSP_BASE, + .pmem_adsp_size = MSM_PMEM_ADSP_SIZE, + .pmem_gpu0_start = MSM_PMEM_GPU0_BASE, + .pmem_gpu0_size = MSM_PMEM_GPU0_SIZE, + .pmem_gpu1_start = MSM_PMEM_GPU1_BASE, + .pmem_gpu1_size = MSM_PMEM_GPU1_SIZE, + .pmem_camera_start = MSM_PMEM_CAMERA_BASE, + .pmem_camera_size = MSM_PMEM_CAMERA_SIZE, + .ram_console_start = MSM_RAM_CONSOLE_BASE, + .ram_console_size = MSM_RAM_CONSOLE_SIZE, +}; + #ifdef CONFIG_WIFI_CONTROL_FUNC static struct platform_device trout_wifi = { .name = "msm_wifi", @@ -671,12 +599,6 @@ static struct platform_device *devices[] __initdata = { &android_leds, &sd_door_switch, &android_timed_gpios, - &android_pmem_device, - &android_pmem_adsp_device, - &android_pmem_gpu0_device, - &android_pmem_gpu1_device, - &android_pmem_camera_device, - &trout_ram_console_device, &trout_camera, &trout_rfkill, #ifdef CONFIG_WIFI_CONTROL_FUNC @@ -847,6 +769,8 @@ static void __init trout_init(void) #endif msm_add_usb_devices(trout_phy_reset); + msm_add_mem_devices(&pmem_setting); + rc = trout_init_mmc(system_rev); if (rc) printk(KERN_CRIT "%s: MMC init failure (%d)\n", __func__, rc); From 5fd7b35916accdda8d8062cec62cdc115400a192 Mon Sep 17 00:00:00 2001 From: Farmer Tseng Date: Mon, 23 Feb 2009 17:55:30 +0800 Subject: [PATCH 0285/2556] [ARM] msm: sapphire: Fix touchscreen power on/off sequence. Signed-off-by: Farmer Tseng Signed-off-by: San Mehat --- arch/arm/mach-msm/board-sapphire.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index a0af1054c75c9..cb53cf574c6d7 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -233,10 +233,11 @@ static int sapphire_ts_power(int on) /* touchscreen must be powered before we enable i2c pullup */ msleep(2); /* enable touch panel level shift */ - gpio_set_value(gpio_tp_ls_en, 1); + gpio_direction_output(gpio_tp_ls_en, 1); msleep(2); } else { - gpio_set_value(gpio_tp_ls_en, 0); + gpio_direction_output(gpio_tp_ls_en, 0); + udelay(50); sapphire_gpio_write(NULL, SAPPHIRE_GPIO_TP_EN, 0); } From 7f0aa958c94f28d345325f0d1cb60fb659c0a735 Mon Sep 17 00:00:00 2001 From: Thomas Tsai Date: Wed, 18 Feb 2009 16:16:17 +0800 Subject: [PATCH 0286/2556] [ARM] msm: sapphire: Add support for button backlight. Signed-off-by: San Mehat --- arch/arm/mach-msm/board-sapphire.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index cb53cf574c6d7..e4ac2a6732e09 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -368,7 +368,6 @@ static struct platform_device android_CPLD_leds = { }; #endif -#if 0 static struct gpio_led android_led_list[] = { { .name = "button-backlight", @@ -388,7 +387,6 @@ static struct platform_device android_leds = { .platform_data = &android_leds_data, }, }; -#endif #ifdef CONFIG_HTC_HEADSET /* RTS/CTS to GPO/GPI. */ @@ -797,6 +795,7 @@ static struct platform_device *devices[] __initdata = { #endif &sapphire_nav_device, &sapphire_reset_keys_device, + &android_leds, #ifdef CONFIG_LEDS_CPLD &android_CPLD_leds, #endif From 735a952e55c64765491444a4b8e238141d9f17c5 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 22 Dec 2008 20:24:17 -0800 Subject: [PATCH 0287/2556] [ARM] msm: acpuclock: Couple minimum AXI freq to the current CPU freq. This patch comes from qualcomm with some minor changes by mike@android.com. Change-Id: I211a2768fec5010309f5314d3eed7976cb78e326 Signed-off-by: Mike Chan --- arch/arm/mach-msm/acpuclock-arm11.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/mach-msm/acpuclock-arm11.c b/arch/arm/mach-msm/acpuclock-arm11.c index 7ffbd987eb5d8..1466f6cda4d77 100644 --- a/arch/arm/mach-msm/acpuclock-arm11.c +++ b/arch/arm/mach-msm/acpuclock-arm11.c @@ -418,6 +418,13 @@ int acpuclk_set_rate(unsigned long rate, int for_power_collapse) pr_err("Setting AXI min rate failed!\n"); } + /* Change the AXI bus frequency if we can. */ + if (strt_s->axiclk_khz != tgt_s->axiclk_khz) { + rc = clk_set_rate(ebi1_clk, tgt_s->axiclk_khz * 1000); + if (rc < 0) + pr_err("Setting AXI min rate failed!\n"); + } + /* Drop VDD level if we can. */ if (tgt_s->vdd < strt_s->vdd) { if (acpuclk_set_vdd_level(tgt_s->vdd) < 0) From 6b2a425cb91128892253119e725f1a6deb02cf1c Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Tue, 6 Jan 2009 17:09:30 -0700 Subject: [PATCH 0288/2556] [ARM] msm: Move cpufreq code from acpuclk.c to cpufreq.c The acpuclk.c has function calls used to notify the cpufreq framework when the acpuclk frequency is changed. These notifications are cpufreq specific and independent of acpu clk. Signed-off-by: Mike Chan --- arch/arm/mach-msm/cpufreq.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c index 2e5f119160a05..729391c64424d 100644 --- a/arch/arm/mach-msm/cpufreq.c +++ b/arch/arm/mach-msm/cpufreq.c @@ -53,6 +53,7 @@ static int msm_cpufreq_target(struct cpufreq_policy *policy, unsigned int relation) { int index; + struct cpufreq_freqs freqs; struct cpufreq_frequency_table *table = cpufreq_frequency_get_table(smp_processor_id()); @@ -66,7 +67,12 @@ static int msm_cpufreq_target(struct cpufreq_policy *policy, printk("msm_cpufreq_target %d r %d (%d-%d) selected %d\n", target_freq, relation, policy->min, policy->max, table[index].frequency); #endif + freqs.old = policy->cur; + freqs.new = table[index].frequency; + freqs.cpu = smp_processor_id(); + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); acpuclk_set_rate(table[index].frequency * 1000, 0); + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); return 0; } @@ -74,7 +80,7 @@ static int msm_cpufreq_verify(struct cpufreq_policy *policy) { cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); -return 0; + return 0; } static int __init msm_cpufreq_init(struct cpufreq_policy *policy) @@ -96,7 +102,7 @@ static int __init msm_cpufreq_init(struct cpufreq_policy *policy) } static struct cpufreq_driver msm_cpufreq_driver = { - /* lps calculstaions are handled here. */ + /* lps calculations are handled here. */ .flags = CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS, .init = msm_cpufreq_init, .verify = msm_cpufreq_verify, From 7155a2a390bf58dedc2a067fcc2d0ea382ad2e74 Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Mon, 5 Jan 2009 23:00:02 -0800 Subject: [PATCH 0289/2556] [ARM] msm: acpuclock: Enable PLLs of stepping freqs when necessary. Signed-off-by: Mike Chan --- arch/arm/mach-msm/acpuclock-arm11.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/arch/arm/mach-msm/acpuclock-arm11.c b/arch/arm/mach-msm/acpuclock-arm11.c index 1466f6cda4d77..7ffbd987eb5d8 100644 --- a/arch/arm/mach-msm/acpuclock-arm11.c +++ b/arch/arm/mach-msm/acpuclock-arm11.c @@ -418,13 +418,6 @@ int acpuclk_set_rate(unsigned long rate, int for_power_collapse) pr_err("Setting AXI min rate failed!\n"); } - /* Change the AXI bus frequency if we can. */ - if (strt_s->axiclk_khz != tgt_s->axiclk_khz) { - rc = clk_set_rate(ebi1_clk, tgt_s->axiclk_khz * 1000); - if (rc < 0) - pr_err("Setting AXI min rate failed!\n"); - } - /* Drop VDD level if we can. */ if (tgt_s->vdd < strt_s->vdd) { if (acpuclk_set_vdd_level(tgt_s->vdd) < 0) From 3260b8bbbf1748cb4033e4df189553d63be63cdc Mon Sep 17 00:00:00 2001 From: San Mehat Date: Thu, 26 Feb 2009 11:01:58 -0800 Subject: [PATCH 0290/2556] [ARM] msm: snd: Checking the device number is no longer required Signed-off-by: San Mehat --- arch/arm/mach-msm/qdsp5/snd.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/arch/arm/mach-msm/qdsp5/snd.c b/arch/arm/mach-msm/qdsp5/snd.c index 5377176260cd1..4332ba67a1843 100644 --- a/arch/arm/mach-msm/qdsp5/snd.c +++ b/arch/arm/mach-msm/qdsp5/snd.c @@ -78,15 +78,6 @@ struct snd_set_volume_msg { struct snd_endpoint *get_snd_endpoints(int *size); -static inline int check_device(struct snd_ctxt *snd, int device) -{ - int cnt; - for (cnt = 0; cnt < snd->snd_epts->num; cnt++) - if (device == snd->snd_epts->endpoints[cnt].id) - return 0; - return -EINVAL; -} - static inline int check_mute(int mute) { return (mute == SND_MUTE_MUTED || @@ -140,14 +131,6 @@ static long snd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } - - rc = check_device(snd, dev.device); - if (rc < 0) { - pr_err("snd_ioctl set device: invalid device.\n"); - rc = -EINVAL; - break; - } - dmsg.args.device = cpu_to_be32(dev.device); dmsg.args.ear_mute = cpu_to_be32(dev.ear_mute); dmsg.args.mic_mute = cpu_to_be32(dev.mic_mute); @@ -175,13 +158,6 @@ static long snd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } - rc = check_device(snd, vol.device); - if (rc < 0) { - pr_err("snd_ioctl set volume: invalid device.\n"); - rc = -EINVAL; - break; - } - vmsg.args.device = cpu_to_be32(vol.device); vmsg.args.method = cpu_to_be32(vol.method); if (vol.method != SND_METHOD_VOICE) { From dedc93298efe30c1d54bad5c049281fa251c96bc Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 2 Mar 2009 09:25:58 -0800 Subject: [PATCH 0291/2556] rtc: msm_rtc: Conditionalize some debugging Signed-off-by: San Mehat --- drivers/rtc/rtc-msm7x00a.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/rtc/rtc-msm7x00a.c b/drivers/rtc/rtc-msm7x00a.c index 25d73ebbbf5c9..a2f8ecd7201e7 100644 --- a/drivers/rtc/rtc-msm7x00a.c +++ b/drivers/rtc/rtc-msm7x00a.c @@ -26,6 +26,8 @@ #include +#define RTC_DEBUG 0 + extern void msm_pm_set_max_sleep_time(int64_t sleep_time_ns); #define APP_TIMEREMOTE_PDEV_NAME "rs30000048:0da5b528" @@ -69,7 +71,7 @@ msmrtc_timeremote_set_time(struct device *dev, struct rtc_time *tm) if (tm->tm_year < 1970) return -EINVAL; -#if 0 +#if RTC_DEBUG printk(KERN_DEBUG "%s: %.2u/%.2u/%.4u %.2u:%.2u:%.2u (%.2u)\n", __func__, tm->tm_mon, tm->tm_mday, tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday); @@ -130,7 +132,7 @@ msmrtc_timeremote_read_time(struct device *dev, struct rtc_time *tm) tm->tm_sec = be32_to_cpu(rep.time.second); tm->tm_wday = be32_to_cpu(rep.time.day_of_week); -#if 0 +#if RTC_DEBUG printk(KERN_DEBUG "%s: %.2u/%.2u/%.4u %.2u:%.2u:%.2u (%.2u)\n", __func__, tm->tm_mon, tm->tm_mday, tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday); @@ -178,9 +180,10 @@ static struct rtc_class_ops msm_rtc_ops = { static void msmrtc_alarmtimer_expired(unsigned long _data) { +#if RTC_DEBUG printk(KERN_DEBUG "%s: Generating alarm event (src %lu)\n", rtc->name, _data); - +#endif rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); rtcalarm_time = 0; } From 22eca0d0ea6ed08d0724f4ae7f1f850b8ee24490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 23 Mar 2009 19:17:14 -0700 Subject: [PATCH 0292/2556] [ARM] msm: smd_tty: Lock a wakelock for half a second when receiving data. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/smd_tty.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/mach-msm/smd_tty.c b/arch/arm/mach-msm/smd_tty.c index f40944958d440..2edd9d1ec2dc9 100644 --- a/arch/arm/mach-msm/smd_tty.c +++ b/arch/arm/mach-msm/smd_tty.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,7 @@ static DEFINE_MUTEX(smd_tty_lock); struct smd_tty_info { smd_channel_t *ch; struct tty_struct *tty; + struct wake_lock wake_lock; int open_count; }; @@ -67,6 +69,7 @@ static void smd_tty_notify(void *priv, unsigned event) printk(KERN_ERR "OOPS - smd_tty_buffer mismatch?!"); } + wake_lock_timeout(&info->wake_lock, HZ / 2); tty_flip_buffer_push(tty); } @@ -92,6 +95,7 @@ static int smd_tty_open(struct tty_struct *tty, struct file *f) info = smd_tty + n; mutex_lock(&smd_tty_lock); + wake_lock_init(&info->wake_lock, WAKE_LOCK_SUSPEND, name); tty->driver_data = info; if (info->open_count++ == 0) { @@ -118,6 +122,7 @@ static void smd_tty_close(struct tty_struct *tty, struct file *f) if (--info->open_count == 0) { info->tty = 0; tty->driver_data = 0; + wake_lock_destroy(&info->wake_lock); if (info->ch) { smd_close(info->ch); info->ch = 0; From 7da0367e42d53da62e3489e92bf60c329d53fd54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 23 Mar 2009 19:24:56 -0700 Subject: [PATCH 0293/2556] [ARM] msm: smd_rpcrouter_servers: Hold a wakelock while calling rpc_call. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/smd_rpcrouter_servers.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/mach-msm/smd_rpcrouter_servers.c b/arch/arm/mach-msm/smd_rpcrouter_servers.c index 77e43273d08ba..9587e1c466b8e 100644 --- a/arch/arm/mach-msm/smd_rpcrouter_servers.c +++ b/arch/arm/mach-msm/smd_rpcrouter_servers.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -40,6 +41,7 @@ static struct msm_rpc_endpoint *endpoint; static LIST_HEAD(rpc_server_list); static DEFINE_MUTEX(rpc_server_list_lock); static int rpc_servers_active; +static struct wake_lock rpc_servers_wake_lock; static void rpc_server_register(struct msm_rpc_server *server) { @@ -128,6 +130,10 @@ static int rpc_servers_thread(void *data) int rc; for (;;) { + wake_unlock(&rpc_servers_wake_lock); + rc = wait_event_interruptible(endpoint->wait_q, + !list_empty(&endpoint->read_q)); + wake_lock(&rpc_servers_wake_lock); rc = msm_rpc_read(endpoint, &buffer, -1, -1); if (rc < 0) { printk(KERN_ERR "%s: could not read: %d\n", @@ -203,6 +209,7 @@ static struct platform_driver rpcservers_driver = { static int __init rpc_servers_init(void) { + wake_lock_init(&rpc_servers_wake_lock, WAKE_LOCK_SUSPEND, "rpc_server"); return platform_driver_register(&rpcservers_driver); } From 6429aab85318427fc17bf654fde782dfd2e74320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 23 Mar 2009 19:27:45 -0700 Subject: [PATCH 0294/2556] [ARM] msm: smd_rpcrouter: Hold a wakelock while while reading a packet, and while packets are queued. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/smd_rpcrouter.c | 14 ++++++++++++++ arch/arm/mach-msm/smd_rpcrouter.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c index 5bd9e98b27d1d..b1899898a7dda 100644 --- a/arch/arm/mach-msm/smd_rpcrouter.c +++ b/arch/arm/mach-msm/smd_rpcrouter.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +88,8 @@ static DEFINE_SPINLOCK(server_list_lock); static DEFINE_SPINLOCK(smd_lock); static struct workqueue_struct *rpcrouter_workqueue; +static struct wake_lock rpcrouter_wake_lock; +static int rpcrouter_need_len; static atomic_t next_xid = ATOMIC_INIT(1); static uint8_t next_pacmarkid; @@ -278,6 +281,7 @@ struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev) init_waitqueue_head(&ept->wait_q); INIT_LIST_HEAD(&ept->read_q); spin_lock_init(&ept->read_q_lock); + wake_lock_init(&ept->read_q_wake_lock, WAKE_LOCK_SUSPEND, "rpc_read"); INIT_LIST_HEAD(&ept->incomplete); spin_lock_irqsave(&local_endpoints_lock, flags); @@ -300,6 +304,7 @@ int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept) if (rc < 0) return rc; + wake_lock_destroy(&ept->read_q_wake_lock); list_del(&ept->list); kfree(ept); return 0; @@ -526,6 +531,8 @@ static void rpcrouter_smdnotify(void *_dev, unsigned event) if (event != SMD_EVENT_DATA) return; + if (smd_read_avail(smd_channel) >= rpcrouter_need_len) + wake_lock(&rpcrouter_wake_lock); wake_up(&smd_wait); } @@ -559,6 +566,8 @@ static int rr_read(void *data, int len) else return -EIO; } + rpcrouter_need_len = len; + wake_unlock(&rpcrouter_wake_lock); spin_unlock_irqrestore(&smd_lock, flags); // printk("rr_read: waiting (%d)\n", len); @@ -659,6 +668,7 @@ static void do_read_data(struct work_struct *work) packet_complete: spin_lock_irqsave(&ept->read_q_lock, flags); + wake_lock(&ept->read_q_wake_lock); list_add_tail(&pkt->list, &ept->read_q); wake_up(&ept->wait_q); spin_unlock_irqrestore(&ept->read_q_lock, flags); @@ -680,6 +690,7 @@ static void do_read_data(struct work_struct *work) fail_io: fail_data: printk(KERN_ERR "rpc_router has died\n"); + wake_unlock(&rpcrouter_wake_lock); } void msm_rpc_setup_req(struct rpc_request_hdr *hdr, uint32_t prog, @@ -1024,6 +1035,8 @@ int __msm_rpc_read(struct msm_rpc_endpoint *ept, return -ETOOSMALL; } list_del(&pkt->list); + if (list_empty(&ept->read_q)) + wake_unlock(&ept->read_q_wake_lock); spin_unlock_irqrestore(&ept->read_q_lock, flags); rc = pkt->length; @@ -1122,6 +1135,7 @@ static int msm_rpcrouter_probe(struct platform_device *pdev) init_waitqueue_head(&newserver_wait); init_waitqueue_head(&smd_wait); + wake_lock_init(&rpcrouter_wake_lock, WAKE_LOCK_SUSPEND, "SMD_RPCCALL"); rpcrouter_workqueue = create_singlethread_workqueue("rpcrouter"); if (!rpcrouter_workqueue) diff --git a/arch/arm/mach-msm/smd_rpcrouter.h b/arch/arm/mach-msm/smd_rpcrouter.h index 9438ca3780c92..9de837daa5cba 100644 --- a/arch/arm/mach-msm/smd_rpcrouter.h +++ b/arch/arm/mach-msm/smd_rpcrouter.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -141,6 +142,7 @@ struct msm_rpc_endpoint { /* complete packets waiting to be read */ struct list_head read_q; spinlock_t read_q_lock; + struct wake_lock read_q_wake_lock; wait_queue_head_t wait_q; unsigned flags; From e0659026d57f25a8423700782c4f83c836cfc57b Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 23 Apr 2009 18:18:11 -0700 Subject: [PATCH 0295/2556] [ARM] msm: Clean up trout and sapphire rfkill drivers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use rfkill_set_default() instead of private rfkill_switch_all(). Add remove path. Misc cleanup. Build on 2.6.32 Don't use gpio_configure. Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/board-sapphire-rfkill.c | 58 +++++++++++++---------- arch/arm/mach-msm/board-trout-rfkill.c | 56 ++++++++++++---------- 2 files changed, 64 insertions(+), 50 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire-rfkill.c b/arch/arm/mach-msm/board-sapphire-rfkill.c index 135ffe82c328d..a3e2a64d0ed1b 100644 --- a/arch/arm/mach-msm/board-sapphire-rfkill.c +++ b/arch/arm/mach-msm/board-sapphire-rfkill.c @@ -24,62 +24,64 @@ #include "gpio_chip.h" #include "board-sapphire.h" -void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state); - static struct rfkill *bt_rfk; static const char bt_name[] = "brf6300"; extern int sapphire_bt_fastclock_power(int on); -static int bluetooth_set_power(void *data, enum rfkill_state state) +static int bluetooth_set_power(void *data, bool blocked) { - switch (state) { - case RFKILL_STATE_UNBLOCKED: + if (!blocked) { sapphire_bt_fastclock_power(1); gpio_set_value(SAPPHIRE_GPIO_BT_32K_EN, 1); udelay(10); - gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_HIGH); - break; - case RFKILL_STATE_SOFT_BLOCKED: - gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_LOW); + gpio_direction_output(101, 1); + } else { + gpio_direction_output(101, 0); gpio_set_value(SAPPHIRE_GPIO_BT_32K_EN, 0); sapphire_bt_fastclock_power(0); - break; - default: - printk(KERN_ERR "bad bluetooth rfkill state %d\n", state); } return 0; } -static int __init sapphire_rfkill_probe(struct platform_device *pdev) +static struct rfkill_ops sapphire_rfkill_ops = { + .set_block = bluetooth_set_power, +}; + +static int sapphire_rfkill_probe(struct platform_device *pdev) { int rc = 0; + bool default_state = true; /* off */ - /* default to bluetooth off */ - rfkill_switch_all(RFKILL_TYPE_BLUETOOTH, RFKILL_STATE_SOFT_BLOCKED); - bluetooth_set_power(NULL, RFKILL_STATE_SOFT_BLOCKED); + bluetooth_set_power(NULL, default_state); - bt_rfk = rfkill_allocate(&pdev->dev, RFKILL_TYPE_BLUETOOTH); + bt_rfk = rfkill_alloc(bt_name, &pdev->dev, RFKILL_TYPE_BLUETOOTH, + &sapphire_rfkill_ops, NULL); if (!bt_rfk) return -ENOMEM; - bt_rfk->name = bt_name; - bt_rfk->state = RFKILL_STATE_SOFT_BLOCKED; /* userspace cannot take exclusive control */ - bt_rfk->user_claim_unsupported = 1; - bt_rfk->user_claim = 0; - bt_rfk->data = NULL; /* user data */ - bt_rfk->toggle_radio = bluetooth_set_power; + + rfkill_set_states(bt_rfk, default_state, default_state); rc = rfkill_register(bt_rfk); if (rc) - rfkill_free(bt_rfk); + rfkill_destroy(bt_rfk); return rc; } +static int sapphire_rfkill_remove(struct platform_device *dev) +{ + rfkill_unregister(bt_rfk); + rfkill_destroy(bt_rfk); + + return 0; +} + static struct platform_driver sapphire_rfkill_driver = { .probe = sapphire_rfkill_probe, + .remove = sapphire_rfkill_remove, .driver = { .name = "sapphire_rfkill", .owner = THIS_MODULE, @@ -88,12 +90,16 @@ static struct platform_driver sapphire_rfkill_driver = { static int __init sapphire_rfkill_init(void) { - if (!machine_is_sapphire()) - return 0; return platform_driver_register(&sapphire_rfkill_driver); } +static void __exit sapphire_rfkill_exit(void) +{ + platform_driver_unregister(&sapphire_rfkill_driver); +} + module_init(sapphire_rfkill_init); +module_exit(sapphire_rfkill_exit); MODULE_DESCRIPTION("sapphire rfkill"); MODULE_AUTHOR("Nick Pelly "); MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-trout-rfkill.c b/arch/arm/mach-msm/board-trout-rfkill.c index 5212431dda82a..1a0d9bef0246c 100644 --- a/arch/arm/mach-msm/board-trout-rfkill.c +++ b/arch/arm/mach-msm/board-trout-rfkill.c @@ -24,58 +24,60 @@ #include "board-trout.h" -void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state); - static struct rfkill *bt_rfk; static const char bt_name[] = "brf6300"; -static int bluetooth_set_power(void *data, enum rfkill_state state) +static int bluetooth_set_power(void *data, bool blocked) { - switch (state) { - case RFKILL_STATE_UNBLOCKED: + if (!blocked) { gpio_set_value(TROUT_GPIO_BT_32K_EN, 1); udelay(10); - gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_HIGH); - break; - case RFKILL_STATE_SOFT_BLOCKED: - gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_LOW); + gpio_direction_output(101, 1); + } else { + gpio_direction_output(101, 0); gpio_set_value(TROUT_GPIO_BT_32K_EN, 0); - break; - default: - printk(KERN_ERR "bad bluetooth rfkill state %d\n", state); } return 0; } -static int __init trout_rfkill_probe(struct platform_device *pdev) +static struct rfkill_ops trout_rfkill_ops = { + .set_block = bluetooth_set_power, +}; + +static int trout_rfkill_probe(struct platform_device *pdev) { int rc = 0; + bool default_state = true; /* off */ - /* default to bluetooth off */ - rfkill_switch_all(RFKILL_TYPE_BLUETOOTH, RFKILL_STATE_SOFT_BLOCKED); - bluetooth_set_power(NULL, RFKILL_STATE_SOFT_BLOCKED); + bluetooth_set_power(NULL, default_state); - bt_rfk = rfkill_allocate(&pdev->dev, RFKILL_TYPE_BLUETOOTH); + bt_rfk = rfkill_alloc(bt_name, &pdev->dev, RFKILL_TYPE_BLUETOOTH, + &trout_rfkill_ops, NULL); if (!bt_rfk) return -ENOMEM; - bt_rfk->name = bt_name; - bt_rfk->state = RFKILL_STATE_SOFT_BLOCKED; + rfkill_set_states(bt_rfk, default_state, default_state); + /* userspace cannot take exclusive control */ - bt_rfk->user_claim_unsupported = 1; - bt_rfk->user_claim = 0; - bt_rfk->data = NULL; // user data - bt_rfk->toggle_radio = bluetooth_set_power; rc = rfkill_register(bt_rfk); if (rc) - rfkill_free(bt_rfk); + rfkill_destroy(bt_rfk); return rc; } +static int trout_rfkill_remove(struct platform_device *dev) +{ + rfkill_unregister(bt_rfk); + rfkill_destroy(bt_rfk); + + return 0; +} + static struct platform_driver trout_rfkill_driver = { .probe = trout_rfkill_probe, + .remove = trout_rfkill_remove, .driver = { .name = "trout_rfkill", .owner = THIS_MODULE, @@ -87,7 +89,13 @@ static int __init trout_rfkill_init(void) return platform_driver_register(&trout_rfkill_driver); } +static void __exit trout_rfkill_exit(void) +{ + platform_driver_unregister(&trout_rfkill_driver); +} + module_init(trout_rfkill_init); +module_exit(trout_rfkill_exit); MODULE_DESCRIPTION("trout rfkill"); MODULE_AUTHOR("Nick Pelly "); MODULE_LICENSE("GPL"); From 7df06b70ea448ca27d888fde2012f1c65fe05c6d Mon Sep 17 00:00:00 2001 From: Farmer Tseng Date: Tue, 24 Feb 2009 12:18:43 -0800 Subject: [PATCH 0296/2556] [ARM] msm: htc_headset: Add support for multi-function cable with 3.5mm headset Signed-off-by: San Mehat --- arch/arm/mach-msm/htc_acoustic.c | 45 +++- arch/arm/mach-msm/htc_headset.c | 225 +++++++++++++------ arch/arm/mach-msm/include/mach/htc_headset.h | 7 +- 3 files changed, 196 insertions(+), 81 deletions(-) diff --git a/arch/arm/mach-msm/htc_acoustic.c b/arch/arm/mach-msm/htc_acoustic.c index 3de71dddb5893..79e603bb249dc 100644 --- a/arch/arm/mach-msm/htc_acoustic.c +++ b/arch/arm/mach-msm/htc_acoustic.c @@ -67,6 +67,39 @@ struct set_acoustic_rep { static uint32_t htc_acoustic_vir_addr; static struct msm_rpc_endpoint *endpoint; static struct mutex api_lock; +static struct mutex rpc_connect_mutex; + +static int is_rpc_connect(void) +{ + mutex_lock(&rpc_connect_mutex); + if (endpoint == NULL) { + endpoint = msm_rpc_connect(HTCRPOG, HTCVERS, 0); + if (IS_ERR(endpoint)) { + pr_err("%s: init rpc failed! rc = %ld\n", + __func__, PTR_ERR(endpoint)); + mutex_unlock(&rpc_connect_mutex); + return 0; + } + } + mutex_unlock(&rpc_connect_mutex); + return 1; +} + +int turn_mic_bias_on(int on) +{ + struct mic_bias_req { + struct rpc_request_hdr hdr; + uint32_t on; + } req; + + if (!is_rpc_connect()) + return -1; + + req.on = cpu_to_be32(on); + return msm_rpc_call(endpoint, ONCRPC_SET_MIC_BIAS_PROC, + &req, sizeof(req), 5 * HZ); +} +EXPORT_SYMBOL(turn_mic_bias_on); static int acoustic_mmap(struct file *file, struct vm_area_struct *vma) { @@ -124,15 +157,8 @@ static int acoustic_open(struct inode *inode, struct file *file) mutex_lock(&api_lock); if (!htc_acoustic_vir_addr) { - if (endpoint == NULL) { - endpoint = msm_rpc_connect(HTCRPOG, HTCVERS, 0); - if (IS_ERR(endpoint)) { - E("init rpc failed! rc = %ld\n", - PTR_ERR(endpoint)); - endpoint = NULL; - goto done; - } - } + if (!is_rpc_connect()) + goto done; req_smem.size = cpu_to_be32(HTC_ACOUSTIC_TABLE_SIZE); rc = msm_rpc_call_reply(endpoint, @@ -223,6 +249,7 @@ static struct miscdevice acoustic_misc = { static int __init acoustic_init(void) { mutex_init(&api_lock); + mutex_init(&rpc_connect_mutex); return misc_register(&acoustic_misc); } diff --git a/arch/arm/mach-msm/htc_headset.c b/arch/arm/mach-msm/htc_headset.c index 803deac20a7d2..f9a00b79f9d1c 100644 --- a/arch/arm/mach-msm/htc_headset.c +++ b/arch/arm/mach-msm/htc_headset.c @@ -102,12 +102,14 @@ struct h2w_info { unsigned int irq; unsigned int irq_btn; + unsigned int irq_btn_35mm; int cable_in1; int cable_in2; int h2w_clk; int h2w_data; int debug_uart; + int headset_mic_35mm; void (*config_cpld) (int); void (*init_cpld) (void); @@ -120,6 +122,7 @@ struct h2w_info { int (*get_clk)(void); int htc_headset_flag; + int btn_11pin_35mm_flag; struct hrtimer timer; ktime_t debounce_time; @@ -127,6 +130,9 @@ struct h2w_info { struct hrtimer btn_timer; ktime_t btn_debounce_time; + struct hrtimer btn35mm_timer; + ktime_t btn35mm_debounce_time; + H2W_INFO h2w_info; H2W_SPEED speed; struct vreg *vreg_h2w; @@ -146,7 +152,7 @@ static ssize_t h2w_print_name(struct switch_dev *sdev, char *buf) static void button_pressed(void) { - H2W_DBG("button_pressed \n"); + printk(KERN_INFO "[H2W] button_pressed\n"); atomic_set(&hi->btn_state, 1); input_report_key(hi->input, KEY_MEDIA, 1); input_sync(hi->input); @@ -154,7 +160,7 @@ static void button_pressed(void) static void button_released(void) { - H2W_DBG("button_released \n"); + printk(KERN_INFO "[H2W] button_released\n"); atomic_set(&hi->btn_state, 0); input_report_key(hi->input, KEY_MEDIA, 0); input_sync(hi->input); @@ -186,14 +192,6 @@ static inline void h2w_end_command(void) set_irq_type(hi->irq_btn, IRQF_TRIGGER_RISING); } -/* - * One bit write data - * ________ - * SCLK O ______| |______O(L) - * - * - * SDAT I - */ static inline void one_clock_write(unsigned short flag) { if (flag) @@ -207,15 +205,6 @@ static inline void one_clock_write(unsigned short flag) hi->set_clk(0); } -/* - * One bit write data R/W bit - * ________ - * SCLK ______| |______O(L) - * 1----> 1-----> - * 2-------> ______ - * SDAT I - * O(H/L) - */ static inline void one_clock_write_RWbit(unsigned short flag) { if (flag) @@ -231,17 +220,6 @@ static inline void one_clock_write_RWbit(unsigned short flag) udelay(hi->speed); } -/* - * H2W Reset - * ___________ - * SCLK O(L)______| |___O(L) - * 1----> - * 4-->1-->1-->1us--> - * ____ - * SDAT O(L)________ | |_______O(L) - * - * H2w reset command needs to be issued before every access - */ static inline void h2w_reset(void) { /* Set H2W_DAT as output low */ @@ -259,15 +237,6 @@ static inline void h2w_reset(void) udelay(hi->speed); } -/* - * H2W Start - * ___________ - * SCLK O(L)______| |___O(L) - * 1----> - * 2----------->1--> - * - * SDAT O(L)______________________O(L) - */ static inline void h2w_start(void) { udelay(hi->speed); @@ -277,15 +246,6 @@ static inline void h2w_start(void) udelay(hi->speed); } -/* - * H2W Ack - * __________ - * SCLK _____| |_______O(L) - * 1----> 1------> - * 2---------> - * ________________________ - * SDAT become Input mode here I - */ static inline int h2w_ack(void) { int retry_times = 0; @@ -310,14 +270,6 @@ static inline int h2w_ack(void) return 0; } -/* - * One bit read data - * ________ - * SCLK ______| |______O(L) - * 2----> 2-----> - * 2-------> - * SDAT I - */ static unsigned char h2w_readc(void) { unsigned char h2w_read_data = 0x0; @@ -670,6 +622,17 @@ static void remove_headset(void) if (atomic_read(&hi->btn_state)) button_released(); + printk(KERN_INFO "remove htc headset\n"); + break; + case NORMAL_HEARPHONE: + if (hi->btn_11pin_35mm_flag) { + disable_irq(hi->irq_btn_35mm); + turn_mic_bias_on(0); + hi->btn_11pin_35mm_flag = 0; + if (atomic_read(&hi->btn_state)) + button_released(); + } + printk(KERN_INFO "remove 11pin 3.5mm headset\n"); break; case H2W_DEVICE: h2w_dev_power_on(0); @@ -679,6 +642,7 @@ static void remove_headset(void) hi->btn_debounce_time = ktime_set(0, 10000000); hi->set_clk_dir(0); hi->set_dat_dir(0); + printk(KERN_INFO "remove h2w device\n"); break; } @@ -714,7 +678,44 @@ static void insert_headset(int type) local_irq_restore(irq_flags); hi->debounce_time = ktime_set(0, 200000000); /* 20 ms */ break; + case NORMAL_HEARPHONE: + if (hi->headset_mic_35mm) { + /* support 3.5mm earphone with mic */ + printk(KERN_INFO "11pin_3.5mm_headset plug in\n"); + /* Turn On Mic Bias */ + turn_mic_bias_on(1); + /* Wait pin be stable */ + msleep(200); + /* Detect headset with or without microphone */ + if (gpio_get_value(hi->headset_mic_35mm)) { + /* without microphone */ + turn_mic_bias_on(0); + state |= BIT_HEADSET_NO_MIC; + printk(KERN_INFO + "11pin_3.5mm without microphone\n"); + } else { /* with microphone */ + state |= BIT_HEADSET; + /* Enable button irq */ + if (!hi->btn_11pin_35mm_flag) { + set_irq_type(hi->irq_btn_35mm, + IRQF_TRIGGER_HIGH); + enable_irq(hi->irq_btn_35mm); + hi->btn_11pin_35mm_flag = 1; + } + printk(KERN_INFO + "11pin_3.5mm with microphone\n"); + } + } else /* not support 3.5mm earphone with mic */ + state |= BIT_HEADSET_NO_MIC; + hi->debounce_time = ktime_set(0, 500000000); /* 500 ms */ + break; case H2W_DEVICE: + printk(KERN_INFO "insert_headset H2W_DEVICE\n"); + if (!hi->set_dat) { + printk(KERN_INFO "Don't support H2W_DEVICE\n"); + hi->htc_headset_flag = 0; + return; + } if (h2w_dev_detect() < 0) { printk(KERN_INFO "H2W_DEVICE -- Non detect\n"); remove_headset(); @@ -729,11 +730,12 @@ static void insert_headset(int type) } break; case H2W_USB_CRADLE: + printk(KERN_INFO "insert_headset USB_CRADLE\n"); state |= BIT_HEADSET_NO_MIC; break; case H2W_UART_DEBUG: - hi->config_cpld(hi->debug_uart); printk(KERN_INFO "switch to H2W_UART_DEBUG\n"); + hi->config_cpld(hi->debug_uart); default: return; } @@ -807,7 +809,7 @@ static int is_accessary_pluged_in(void) gpio_direction_input(hi->cable_in1); gpio_direction_input(hi->cable_in2); - H2W_DBG("(%d,%d) (%d,%d) (%d,%d)\n", + H2WI("(%d,%d) (%d,%d) (%d,%d)", clk1, dat1, clk2, dat2, clk3, dat3); if ((clk1 == 0) && (dat1 == 1) && @@ -872,25 +874,47 @@ static void detection_work(struct work_struct *work) insert_headset(type); } +void headset_button_event(int is_press) +{ + if (!is_press) { + if (hi->ignore_btn) + hi->ignore_btn = 0; + else if (atomic_read(&hi->btn_state)) + button_released(); + } else { + if (!hi->ignore_btn && !atomic_read(&hi->btn_state)) + button_pressed(); + } +} + +static enum hrtimer_restart button_35mm_event_timer_func(struct hrtimer *data) +{ + if (gpio_get_value(hi->headset_mic_35mm)) { + headset_button_event(1); + /* 10 ms */ + hi->btn35mm_debounce_time = ktime_set(0, 10000000); + } else { + headset_button_event(0); + /* 100 ms */ + hi->btn35mm_debounce_time = ktime_set(0, 100000000); + } + + return HRTIMER_NORESTART; +} + static enum hrtimer_restart button_event_timer_func(struct hrtimer *data) { int key, press, keyname, h2w_key = 1; H2W_DBG(""); - if (switch_get_state(&hi->sdev) == H2W_HTC_HEADSET) { + if (switch_get_state(&hi->sdev) & BIT_HEADSET) { switch (hi->htc_headset_flag) { case H2W_HTC_HEADSET: - if (gpio_get_value(hi->cable_in2)) { - if (hi->ignore_btn) - hi->ignore_btn = 0; - else if (atomic_read(&hi->btn_state)) - button_released(); - } else { - if (!hi->ignore_btn && - !atomic_read(&hi->btn_state)) - button_pressed(); - } + if (!gpio_get_value(hi->cable_in2)) + headset_button_event(1); /* press */ + else + headset_button_event(0); break; case H2W_DEVICE: if ((hi->get_dat() == 1) && (hi->get_clk() == 1)) { @@ -984,7 +1008,7 @@ static irqreturn_t detect_irq_handler(int irq, void *dev_id) value2, (10-retry_limit), switch_get_state(&hi->sdev)); if ((switch_get_state(&hi->sdev) == H2W_NO_DEVICE) ^ value2) { - if (switch_get_state(&hi->sdev) == H2W_HTC_HEADSET) + if (switch_get_state(&hi->sdev) & BIT_HEADSET) hi->ignore_btn = 1; /* Do the rest of the work in timer context */ hrtimer_start(&hi->timer, hi->debounce_time, HRTIMER_MODE_REL); @@ -1014,6 +1038,29 @@ static irqreturn_t button_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static irqreturn_t button_35mm_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + H2W_DBG(""); + do { + value1 = gpio_get_value(hi->headset_mic_35mm); + set_irq_type(hi->irq_btn_35mm, value1 ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(hi->headset_mic_35mm); + } while (value1 != value2 && retry_limit-- > 0); + + H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit)); + + hrtimer_start(&hi->btn35mm_timer, + hi->btn35mm_debounce_time, + HRTIMER_MODE_REL); + + return IRQ_HANDLED; + +} + #if defined(CONFIG_DEBUG_FS) static int h2w_debug_set(void *data, u64 val) { @@ -1060,13 +1107,16 @@ static int h2w_probe(struct platform_device *pdev) hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ hi->btn_debounce_time = ktime_set(0, 10000000); /* 10 ms */ + hi->btn35mm_debounce_time = ktime_set(0, 50000000); /* 50 ms */ hi->htc_headset_flag = 0; + hi->btn_11pin_35mm_flag = 0; hi->cable_in1 = pdata->cable_in1; hi->cable_in2 = pdata->cable_in2; hi->h2w_clk = pdata->h2w_clk; hi->h2w_data = pdata->h2w_data; hi->debug_uart = pdata->debug_uart; + hi->headset_mic_35mm = pdata->headset_mic_35mm; hi->config_cpld = pdata->config_cpld; hi->init_cpld = pdata->init_cpld; hi->set_dat = pdata->set_dat; @@ -1095,6 +1145,28 @@ static int h2w_probe(struct platform_device *pdev) goto err_create_work_queue; } + if (hi->headset_mic_35mm) { + ret = gpio_request(hi->headset_mic_35mm, "3.5mm_mic_detect"); + if (ret < 0) + goto err_request_35mm_mic_detect_gpio; + + ret = gpio_direction_input(hi->headset_mic_35mm); + if (ret < 0) + goto err_set_35mm_mic_detect_gpio; + + hi->irq_btn_35mm = gpio_to_irq(hi->headset_mic_35mm); + if (hi->irq_btn_35mm < 0) { + ret = hi->irq_btn_35mm; + goto err_request_btn_35mm_irq; + } + set_irq_flags(hi->irq_btn_35mm, IRQF_VALID | IRQF_NOAUTOEN); + ret = request_irq(hi->irq_btn_35mm, + button_35mm_irq_handler, + IRQF_TRIGGER_HIGH, "35mm_button", NULL); + if (ret < 0) + goto err_request_btn_35mm_irq; + } + ret = gpio_request(hi->cable_in1, "h2w_detect"); if (ret < 0) goto err_request_detect_gpio; @@ -1130,6 +1202,8 @@ static int h2w_probe(struct platform_device *pdev) hi->timer.function = detect_event_timer_func; hrtimer_init(&hi->btn_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hi->btn_timer.function = button_event_timer_func; + hrtimer_init(&hi->btn35mm_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hi->btn35mm_timer.function = button_35mm_event_timer_func; ret = request_irq(hi->irq, detect_irq_handler, IRQF_TRIGGER_LOW, "h2w_detect", NULL); @@ -1193,6 +1267,13 @@ static int h2w_probe(struct platform_device *pdev) err_request_button_gpio: gpio_free(hi->cable_in1); err_request_detect_gpio: + if (hi->headset_mic_35mm) + free_irq(hi->irq_btn_35mm, 0); +err_request_btn_35mm_irq: +err_set_35mm_mic_detect_gpio: + if (hi->headset_mic_35mm) + gpio_free(hi->headset_mic_35mm); +err_request_35mm_mic_detect_gpio: destroy_workqueue(g_detection_work_queue); err_create_work_queue: switch_dev_unregister(&hi->sdev); @@ -1212,6 +1293,10 @@ static int h2w_remove(struct platform_device *pdev) gpio_free(hi->cable_in1); free_irq(hi->irq_btn, 0); free_irq(hi->irq, 0); + if (hi->headset_mic_35mm) { + gpio_free(hi->headset_mic_35mm); + free_irq(hi->irq_btn_35mm, 0); + } destroy_workqueue(g_detection_work_queue); switch_dev_unregister(&hi->sdev); diff --git a/arch/arm/mach-msm/include/mach/htc_headset.h b/arch/arm/mach-msm/include/mach/htc_headset.h index 2f4c18db26253..7fdc85994173f 100644 --- a/arch/arm/mach-msm/include/mach/htc_headset.h +++ b/arch/arm/mach-msm/include/mach/htc_headset.h @@ -23,6 +23,7 @@ struct h2w_platform_data { int h2w_clk; int h2w_data; int debug_uart; + int headset_mic_35mm; void (*config_cpld)(int); void (*init_cpld)(void); void (*set_dat)(int); @@ -42,9 +43,8 @@ struct h2w_platform_data { enum { H2W_NO_DEVICE = 0, H2W_HTC_HEADSET = 1, -/* H2W_TTY_DEVICE = 2,*/ NORMAL_HEARPHONE= 2, - H2W_DEVICE = 3, + H2W_DEVICE = 3, H2W_USB_CRADLE = 4, H2W_UART_DEBUG = 5, }; @@ -170,4 +170,7 @@ typedef enum { H2W_KEY_HOLD = 8, H2W_NUM_KEYFUNC = 9, } KEYFUNC; + +extern int turn_mic_bias_on(int on); + #endif From e91d67954e4f6c8cc7b4ffc3262acaa3453fb790 Mon Sep 17 00:00:00 2001 From: Farmer Tseng Date: Tue, 24 Feb 2009 12:19:54 -0800 Subject: [PATCH 0297/2556] [ARM] msm: sapphire: Add 3.5mm headset mic data to h2w_data Signed-off-by: San Mehat --- arch/arm/mach-msm/board-sapphire.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index e4ac2a6732e09..967b2ca8ac455 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -582,6 +582,7 @@ static struct h2w_platform_data sapphire_h2w_data = { .cable_in2 = SAPPHIRE_GPIO_CABLE_IN2, .h2w_clk = SAPPHIRE_GPIO_H2W_CLK, .h2w_data = SAPPHIRE_GPIO_H2W_DATA, + .headset_mic_35mm = SAPPHIRE_GPIO_AUD_HSMIC_DET_N, .debug_uart = H2W_UART3, .config_cpld = h2w_config_cpld, .init_cpld = h2w_init_cpld, @@ -993,7 +994,6 @@ static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { static void __init sapphire_init(void) { int rc; - int i; printk("sapphire_init() revision=%d\n", system_rev); /* From 341a13b553de159abfc4b450610582c7c8f89264 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Wed, 25 Feb 2009 14:29:01 -0500 Subject: [PATCH 0298/2556] [ARM] msm: sapphire: Fix overflow when setting LCD brightness to max value. Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/board-sapphire-panel.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-msm/board-sapphire-panel.c b/arch/arm/mach-msm/board-sapphire-panel.c index 9129f4cdcb5fa..7fcbbdb14675a 100644 --- a/arch/arm/mach-msm/board-sapphire-panel.c +++ b/arch/arm/mach-msm/board-sapphire-panel.c @@ -66,6 +66,8 @@ static void sapphire_set_backlight_level(uint8_t level) unsigned long flags; int i = 0; + if (index >= DIMMING_STEPS) + index = DIMMING_STEPS - 1; printk(KERN_INFO "level=%d, new level=dimming_levels[%d]=%d\n", level, index, dimming_levels[g_panel_id][index]); percent = pwrsink_percents[index]; From 1dacfd449b0b441a9455a44114a2d636f35b2e0f Mon Sep 17 00:00:00 2001 From: Thomas Tsai Date: Tue, 3 Mar 2009 21:03:48 +0800 Subject: [PATCH 0299/2556] [ARM] msm: sapphire: new button layout : [home menu back search] for new system_rev 0x80. --- arch/arm/mach-msm/board-sapphire-keypad.c | 14 ++++++++++++-- arch/arm/mach-msm/board-sapphire.c | 14 +++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) mode change 100644 => 100755 arch/arm/mach-msm/board-sapphire-keypad.c mode change 100644 => 100755 arch/arm/mach-msm/board-sapphire.c diff --git a/arch/arm/mach-msm/board-sapphire-keypad.c b/arch/arm/mach-msm/board-sapphire-keypad.c old mode 100644 new mode 100755 index 14f12e5e865c4..5c8fc37649a3c --- a/arch/arm/mach-msm/board-sapphire-keypad.c +++ b/arch/arm/mach-msm/board-sapphire-keypad.c @@ -33,6 +33,15 @@ static unsigned int sapphire_row_gpios[] = { 42, 41 }; #define KEYMAP_INDEX(col, row) ((col)*ARRAY_SIZE(sapphire_row_gpios) + (row)) /*scan matrix key*/ +/* HOME(up) MENU (up) Back Search */ +static const unsigned short sapphire_keymap2[ARRAY_SIZE(sapphire_col_gpios) * ARRAY_SIZE(sapphire_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_COMPOSE, + [KEYMAP_INDEX(0, 1)] = KEY_BACK, + + [KEYMAP_INDEX(1, 0)] = KEY_MENU, + [KEYMAP_INDEX(1, 1)] = KEY_SEND, +}; + /* HOME(up) + MENU (down)*/ static const unsigned short sapphire_keymap1[ARRAY_SIZE(sapphire_col_gpios) * ARRAY_SIZE(sapphire_row_gpios)] = { @@ -55,7 +64,7 @@ static const unsigned short sapphire_keymap0[ARRAY_SIZE(sapphire_col_gpios) * static struct gpio_event_matrix_info sapphire_keypad_matrix_info = { .info.func = gpio_event_matrix_func, - .keymap = sapphire_keymap1, + .keymap = sapphire_keymap2, .output_gpios = sapphire_col_gpios, .input_gpios = sapphire_row_gpios, .noutputs = ARRAY_SIZE(sapphire_col_gpios), @@ -112,7 +121,8 @@ static int __init sapphire_init_keypad(void) sapphire_keypad_matrix_info.keymap = sapphire_keymap0; break; default: - sapphire_keypad_matrix_info.keymap = sapphire_keymap1; + if(system_rev != 0x80) + sapphire_keypad_matrix_info.keymap = sapphire_keymap1; break; } return platform_device_register(&sapphire_keypad_device); diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c old mode 100644 new mode 100755 index 967b2ca8ac455..1d497f654afd9 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -171,13 +171,18 @@ static struct gpio_event_direct_entry sapphire_nav_buttons[] = { { SAPPHIRE_GPIO_SEARCH_ACT_N, KEY_COMPOSE }, /* CPLD Key Search */ }; +static struct gpio_event_direct_entry sapphire_nav_buttons2[] = { + { SAPPHIRE_GPIO_NAVI_ACT_N, BTN_MOUSE }, + { SAPPHIRE_GPIO_SEARCH_ACT_N, KEY_HOME }, // CPLD Key Home +}; + static struct gpio_event_input_info sapphire_nav_button_info = { .info.func = gpio_event_input_func, .flags = GPIOEDF_PRINT_KEYS | GPIOEDF_PRINT_KEY_DEBOUNCE, .poll_time.tv.nsec = 40 * NSEC_PER_MSEC, .type = EV_KEY, - .keymap = sapphire_nav_buttons, - .keymap_size = ARRAY_SIZE(sapphire_nav_buttons) + .keymap = sapphire_nav_buttons2, + .keymap_size = ARRAY_SIZE(sapphire_nav_buttons2) }; static struct gpio_event_info *sapphire_nav_info[] = { @@ -994,7 +999,7 @@ static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { static void __init sapphire_init(void) { int rc; - printk("sapphire_init() revision=%d\n", system_rev); + printk("sapphire_init() revision = 0x%X\n", system_rev); /* * Setup common MSM GPIOS @@ -1050,6 +1055,9 @@ static void __init sapphire_init(void) #endif msm_init_pmic_vibrator(); + if(system_rev != 0x80) + sapphire_nav_button_info.keymap = sapphire_nav_buttons; + platform_add_devices(devices, ARRAY_SIZE(devices)); i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); } From ba46341092f31f247297081aca60e15b1b9969f1 Mon Sep 17 00:00:00 2001 From: allen_ou Date: Fri, 6 Mar 2009 12:28:21 +0800 Subject: [PATCH 0300/2556] [ARM] msm: sapphire: Wifi and BT share the same external osc at sapphire_wifi_power() Signed-off-by: San Mehat --- arch/arm/mach-msm/board-sapphire-mmc.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) mode change 100644 => 100755 arch/arm/mach-msm/board-sapphire-mmc.c diff --git a/arch/arm/mach-msm/board-sapphire-mmc.c b/arch/arm/mach-msm/board-sapphire-mmc.c old mode 100644 new mode 100755 index ff2a5fdc6c140..6b5589e31431e --- a/arch/arm/mach-msm/board-sapphire-mmc.c +++ b/arch/arm/mach-msm/board-sapphire-mmc.c @@ -262,8 +262,8 @@ int sapphire_wifi_set_carddetect(int val) EXPORT_SYMBOL(sapphire_wifi_set_carddetect); #endif -static int sapphire_wifi_power_state; -static int sapphire_bt_power_state; +int sapphire_wifi_power_state=0; +int sapphire_bt_power_state=0; int sapphire_wifi_power(int on) { @@ -287,8 +287,15 @@ int sapphire_wifi_power(int on) mdelay(100); gpio_set_value(SAPPHIRE_GPIO_WIFI_EN, on); mdelay(100); - if (!on) + if (!on) { + if(!sapphire_bt_power_state) + { vreg_disable(vreg_wifi_osc); + printk("WiFi disable vreg_wifi_osc.\n"); + } + else + printk("WiFi shouldn't disable vreg_wifi_osc. BT is using it!!\n"); + } sapphire_wifi_power_state = on; return 0; } From 46d59df0e4146f5b86447c39306cce61a20728d3 Mon Sep 17 00:00:00 2001 From: Thomas Tsai Date: Tue, 10 Mar 2009 12:34:29 +0800 Subject: [PATCH 0301/2556] [ARM] msm: sapphire: Set search key to be a wakeup source. Signed-off-by: San Mehat --- arch/arm/mach-msm/board-sapphire.c | 50 ++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 9 deletions(-) mode change 100755 => 100644 arch/arm/mach-msm/board-sapphire.c diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c old mode 100755 new mode 100644 index 1d497f654afd9..499c110697f6e --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -168,12 +168,6 @@ static struct sapphire_axis_info sapphire_y_axis = { static struct gpio_event_direct_entry sapphire_nav_buttons[] = { { SAPPHIRE_GPIO_NAVI_ACT_N, BTN_MOUSE }, - { SAPPHIRE_GPIO_SEARCH_ACT_N, KEY_COMPOSE }, /* CPLD Key Search */ -}; - -static struct gpio_event_direct_entry sapphire_nav_buttons2[] = { - { SAPPHIRE_GPIO_NAVI_ACT_N, BTN_MOUSE }, - { SAPPHIRE_GPIO_SEARCH_ACT_N, KEY_HOME }, // CPLD Key Home }; static struct gpio_event_input_info sapphire_nav_button_info = { @@ -181,8 +175,8 @@ static struct gpio_event_input_info sapphire_nav_button_info = { .flags = GPIOEDF_PRINT_KEYS | GPIOEDF_PRINT_KEY_DEBOUNCE, .poll_time.tv.nsec = 40 * NSEC_PER_MSEC, .type = EV_KEY, - .keymap = sapphire_nav_buttons2, - .keymap_size = ARRAY_SIZE(sapphire_nav_buttons2) + .keymap = sapphire_nav_buttons, + .keymap_size = ARRAY_SIZE(sapphire_nav_buttons) }; static struct gpio_event_info *sapphire_nav_info[] = { @@ -206,6 +200,43 @@ static struct platform_device sapphire_nav_device = { }, }; +/* a new search button to be a wake-up source */ +static struct gpio_event_direct_entry sapphire_search_button_v1[] = { + { SAPPHIRE_GPIO_SEARCH_ACT_N, KEY_COMPOSE }, /* CPLD Key Search*/ +}; + +static struct gpio_event_direct_entry sapphire_search_button_v2[] = { + { SAPPHIRE_GPIO_SEARCH_ACT_N, KEY_HOME }, // CPLD Key Home +}; + +static struct gpio_event_input_info sapphire_search_button_info = { + .info.func = gpio_event_input_func, + //.flags = GPIOEDF_PRINT_KEYS | GPIOEDF_PRINT_KEY_DEBOUNCE, + .flags = 0, + .poll_time.tv.nsec = 40 * NSEC_PER_MSEC, + .type = EV_KEY, + .keymap = sapphire_search_button_v2, + .keymap_size = ARRAY_SIZE(sapphire_search_button_v2) +}; + +static struct gpio_event_info *sapphire_search_info[] = { + &sapphire_search_button_info.info +}; + +static struct gpio_event_platform_data sapphire_search_button_data = { + .name = "sapphire-nav-button", + .info = sapphire_search_info, + .info_count = ARRAY_SIZE(sapphire_search_info), +}; + +static struct platform_device sapphire_search_button_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = 1, + .dev = { + .platform_data = &sapphire_search_button_data, + }, +}; + static int sapphire_reset_keys_up[] = { BTN_MOUSE, 0 @@ -800,6 +831,7 @@ static struct platform_device *devices[] __initdata = { &msm_device_uart_dm1, #endif &sapphire_nav_device, + &sapphire_search_button_device, &sapphire_reset_keys_device, &android_leds, #ifdef CONFIG_LEDS_CPLD @@ -1056,7 +1088,7 @@ static void __init sapphire_init(void) msm_init_pmic_vibrator(); if(system_rev != 0x80) - sapphire_nav_button_info.keymap = sapphire_nav_buttons; + sapphire_search_button_info.keymap = sapphire_search_button_v1; platform_add_devices(devices, ARRAY_SIZE(devices)); i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); From 34f11be013272a7945c48d214f5224e14952d929 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Wed, 11 Mar 2009 10:39:26 -0700 Subject: [PATCH 0302/2556] [ARM] msm: sapphire: Add non-linear transform for panel brightness differences Signed-off-by: San Mehat --- arch/arm/mach-msm/board-sapphire-panel.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire-panel.c b/arch/arm/mach-msm/board-sapphire-panel.c index 7fcbbdb14675a..34ab52da9490d 100644 --- a/arch/arm/mach-msm/board-sapphire-panel.c +++ b/arch/arm/mach-msm/board-sapphire-panel.c @@ -40,7 +40,10 @@ enum sapphire_panel_type { }; static int g_panel_id = -1 ; -#define SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS 132 +#define SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS 132 +#define GOOGLE_DEFAULT_BACKLIGHT_BRIGHTNESS 102 +#define SDBB SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS +#define GDBB GOOGLE_DEFAULT_BACKLIGHT_BRIGHTNESS static int sapphire_backlight_off; static int sapphire_backlight_brightness = @@ -61,17 +64,22 @@ static unsigned pwrsink_percents[] = {0, 6, 8, 15, 26, 34, 46, 54, 65, 77, 87, static void sapphire_set_backlight_level(uint8_t level) { unsigned dimming_factor = 255/DIMMING_STEPS + 1; - int index = (level + dimming_factor - 1) / dimming_factor; + int index, new_level ; unsigned percent; unsigned long flags; int i = 0; - if (index >= DIMMING_STEPS) - index = DIMMING_STEPS - 1; - printk(KERN_INFO "level=%d, new level=dimming_levels[%d]=%d\n", - level, index, dimming_levels[g_panel_id][index]); - percent = pwrsink_percents[index]; - level = dimming_levels[g_panel_id][index]; + /* Non-linear transform for the difference between two + * kind of default backlight settings. + */ + new_level = level<=GDBB ? + level*SDBB/GDBB : (SDBB + (level-GDBB)*(255-SDBB) / (255-GDBB)) ; + index = new_level/dimming_factor ; + + printk(KERN_INFO "level=%d, new level=%d, dimming_levels[%d]=%d\n", + level, new_level, index, dimming_levels[g_panel_id][index]); + percent = pwrsink_percents[index]; + level = dimming_levels[g_panel_id][index]; if (sapphire_backlight_last_level == level) return; From d240791bdd2b52c8939737708dfc8ca37d1080b2 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 13 Mar 2009 15:05:15 -0700 Subject: [PATCH 0303/2556] [ARM] msm: Only include HTC devices for HTC specific boards. Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 967f25b97f37b..98528237bffa4 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -6,7 +6,6 @@ obj-y += dma.o endif obj-y += nand_partitions.o -obj-y += devices_htc.o ifdef CONFIG_MSM_VIC obj-y += irq-vic.o @@ -45,12 +44,13 @@ obj-$(CONFIG_MACH_TROUT) += board-trout-keypad.o obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o obj-$(CONFIG_MACH_TROUT) += board-trout-rfkill.o obj-$(CONFIG_MACH_TROUT) += board-trout-wifi.o +obj-$(CONFIG_MACH_TROUT) += devices_htc.o obj-$(CONFIG_TROUT_BATTCHG) += htc_battery.o obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire.o board-sapphire-gpio.o obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-mmc.o board-sapphire-wifi.o obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-rfkill.o msm_vibrator.o -obj-$(CONFIG_MACH_SAPPHIRE) += devices-msm7x00.o +obj-$(CONFIG_MACH_SAPPHIRE) += devices-msm7x00.o devices_htc.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o From b45036498a367d1b2691f9ea3388e187825e8cd3 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Tue, 17 Mar 2009 21:42:33 -0400 Subject: [PATCH 0304/2556] [ARM] msm: sapphire: Fix race conditions in vibrator driver. Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/msm_vibrator.c | 55 +++++++++++++------------------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/arch/arm/mach-msm/msm_vibrator.c b/arch/arm/mach-msm/msm_vibrator.c index f4da4363aa988..5abd436361ee5 100644 --- a/arch/arm/mach-msm/msm_vibrator.c +++ b/arch/arm/mach-msm/msm_vibrator.c @@ -22,19 +22,20 @@ #include -#define PM_LIBPROG 0x30000061 +#define PM_LIBPROG 0x30000061 #if (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225) -#define PM_LIBVERS 0xfb837d0b +#define PM_LIBVERS 0xfb837d0b #else -#define PM_LIBVERS 0x10001 +#define PM_LIBVERS 0x10001 #endif #define HTC_PROCEDURE_SET_VIB_ON_OFF 21 #define PMIC_VIBRATOR_LEVEL (3000) -static struct work_struct work_vibrator_on; -static struct work_struct work_vibrator_off; +static struct work_struct vibrator_work; static struct hrtimer vibe_timer; +static spinlock_t vibe_lock; +static int vibe_state; static void set_pmic_vibrator(int on) { @@ -53,7 +54,6 @@ static void set_pmic_vibrator(int on) } } - if (on) req.data = cpu_to_be32(PMIC_VIBRATOR_LEVEL); else @@ -63,41 +63,30 @@ static void set_pmic_vibrator(int on) sizeof(req), 5 * HZ); } -static void pmic_vibrator_on(struct work_struct *work) -{ - set_pmic_vibrator(1); -} - -static void pmic_vibrator_off(struct work_struct *work) -{ - set_pmic_vibrator(0); -} - -static void timed_vibrator_on(struct timed_output_dev *sdev) +static void update_vibrator(struct work_struct *work) { - schedule_work(&work_vibrator_on); -} - -static void timed_vibrator_off(struct timed_output_dev *sdev) -{ - schedule_work(&work_vibrator_off); + set_pmic_vibrator(vibe_state); } static void vibrator_enable(struct timed_output_dev *dev, int value) { + unsigned long flags; + + spin_lock_irqsave(&vibe_lock, flags); hrtimer_cancel(&vibe_timer); if (value == 0) - timed_vibrator_off(dev); + vibe_state = 0; else { value = (value > 15000 ? 15000 : value); - - timed_vibrator_on(dev); - + vibe_state = 1; hrtimer_start(&vibe_timer, - ktime_set(value / 1000, (value % 1000) * 1000000), - HRTIMER_MODE_REL); + ktime_set(value / 1000, (value % 1000) * 1000000), + HRTIMER_MODE_REL); } + spin_unlock_irqrestore(&vibe_lock, flags); + + schedule_work(&vibrator_work); } static int vibrator_get_time(struct timed_output_dev *dev) @@ -111,7 +100,8 @@ static int vibrator_get_time(struct timed_output_dev *dev) static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) { - timed_vibrator_off(NULL); + vibe_state = 0; + schedule_work(&vibrator_work); return HRTIMER_NORESTART; } @@ -123,9 +113,10 @@ static struct timed_output_dev pmic_vibrator = { void __init msm_init_pmic_vibrator(void) { - INIT_WORK(&work_vibrator_on, pmic_vibrator_on); - INIT_WORK(&work_vibrator_off, pmic_vibrator_off); + INIT_WORK(&vibrator_work, update_vibrator); + spin_lock_init(&vibe_lock); + vibe_state = 0; hrtimer_init(&vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); vibe_timer.function = vibrator_timer_func; From bf264247b5cbe55fd6a058a92e588e8109402598 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 19 Mar 2009 09:15:05 -0700 Subject: [PATCH 0305/2556] [ARM] msm: sapphire: dream: pwrsink adjustments --- arch/arm/mach-msm/board-sapphire.c | 30 ++++++++++++++++++++++++++---- arch/arm/mach-msm/board-trout.c | 8 ++++---- arch/arm/mach-msm/htc_pwrsink.c | 8 ++++---- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index 499c110697f6e..6cdd4551570bf 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -695,13 +695,35 @@ static struct pwr_sink sapphire_pwrsink_table[] = { }, }; +static int sapphire_pwrsink_resume_early(struct platform_device *pdev) +{ + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 7); + return 0; +} + +static void sapphire_pwrsink_resume_late(struct early_suspend *h) +{ + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 38); +} + +static void sapphire_pwrsink_suspend_early(struct early_suspend *h) +{ + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 7); +} + +static int sapphire_pwrsink_suspend_late(struct platform_device *pdev, pm_message_t state) +{ + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 1); + return 0; +} + static struct pwr_sink_platform_data sapphire_pwrsink_data = { .num_sinks = ARRAY_SIZE(sapphire_pwrsink_table), .sinks = sapphire_pwrsink_table, - .suspend_late = NULL, - .resume_early = NULL, - .suspend_early = NULL, - .resume_late = NULL, + .suspend_late = sapphire_pwrsink_suspend_late, + .resume_early = sapphire_pwrsink_resume_early, + .suspend_early = sapphire_pwrsink_suspend_early, + .resume_late = sapphire_pwrsink_resume_late, }; static struct platform_device sapphire_pwr_sink = { diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index d26deddea3434..9b910d478c00d 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -450,7 +450,7 @@ static struct pwr_sink trout_pwrsink_table[] = { { .id = PWRSINK_BLUETOOTH, .ua_max = 15000, - }, + }, { .id = PWRSINK_CAMERA, .ua_max = 0, @@ -458,7 +458,7 @@ static struct pwr_sink trout_pwrsink_table[] = { { .id = PWRSINK_SDCARD, .ua_max = 0, - }, + }, { .id = PWRSINK_VIDEO, .ua_max = 0, @@ -469,8 +469,8 @@ static struct pwr_sink trout_pwrsink_table[] = { }, { .id = PWRSINK_SYSTEM_LOAD, - .ua_max = 63000, - .percent_util = 100, + .ua_max = 100000, + .percent_util = 38, }, }; diff --git a/arch/arm/mach-msm/htc_pwrsink.c b/arch/arm/mach-msm/htc_pwrsink.c index 2ec2c7f4bb1b2..3105a7c56e9a7 100644 --- a/arch/arm/mach-msm/htc_pwrsink.c +++ b/arch/arm/mach-msm/htc_pwrsink.c @@ -199,7 +199,7 @@ EXPORT_SYMBOL(htc_pwrsink_audio_path_set); void htc_pwrsink_suspend_early(struct early_suspend *h) { - htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 70); + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 7); } int htc_pwrsink_suspend_late(struct platform_device *pdev, pm_message_t state) @@ -209,7 +209,7 @@ int htc_pwrsink_suspend_late(struct platform_device *pdev, pm_message_t state) if (pdata && pdata->suspend_late) pdata->suspend_late(pdev, state); else - htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 13); + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 1); return 0; } @@ -220,13 +220,13 @@ int htc_pwrsink_resume_early(struct platform_device *pdev) if (pdata && pdata->resume_early) pdata->resume_early(pdev); else - htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 70); + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 7); return 0; } void htc_pwrsink_resume_late(struct early_suspend *h) { - htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 100); + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 38); } struct early_suspend htc_pwrsink_early_suspend = { From ca6d74d16b98cb9a191bde439aac474deced162a Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Fri, 12 Jun 2009 22:47:44 +0200 Subject: [PATCH 0306/2556] msm: HTC Dream compile fixes Fix compilation of pwrsink with CONFIG_WAKELOCK disabled. Signed-off-by: Pavel Machek --- arch/arm/mach-msm/htc_pwrsink.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/mach-msm/htc_pwrsink.c b/arch/arm/mach-msm/htc_pwrsink.c index 3105a7c56e9a7..3d3c55cb9b413 100644 --- a/arch/arm/mach-msm/htc_pwrsink.c +++ b/arch/arm/mach-msm/htc_pwrsink.c @@ -229,11 +229,13 @@ void htc_pwrsink_resume_late(struct early_suspend *h) htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 38); } +#ifdef CONFIG_WAKELOCK struct early_suspend htc_pwrsink_early_suspend = { .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, .suspend = htc_pwrsink_suspend_early, .resume = htc_pwrsink_resume_late, }; +#endif static int __init htc_pwrsink_probe(struct platform_device *pdev) { @@ -252,10 +254,12 @@ static int __init htc_pwrsink_probe(struct platform_device *pdev) initialized = 1; +#ifdef CONFIG_WAKELOCK if (pdata->suspend_early) htc_pwrsink_early_suspend.suspend = pdata->suspend_early; if (pdata->resume_late) htc_pwrsink_early_suspend.resume = pdata->resume_late; +#endif register_early_suspend(&htc_pwrsink_early_suspend); return 0; From 802729f94d6fd70b3d924d254ab0497aadf9e70b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 10 Dec 2009 16:46:29 -0800 Subject: [PATCH 0307/2556] htc_pwrsink: Fix to build on 2.6.32 Change-Id: I80f71adde17b3a1a99e91055c947a419ab71ba1f --- arch/arm/mach-msm/htc_pwrsink.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-msm/htc_pwrsink.c b/arch/arm/mach-msm/htc_pwrsink.c index 3d3c55cb9b413..12d6182e70136 100644 --- a/arch/arm/mach-msm/htc_pwrsink.c +++ b/arch/arm/mach-msm/htc_pwrsink.c @@ -202,23 +202,23 @@ void htc_pwrsink_suspend_early(struct early_suspend *h) htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 7); } -int htc_pwrsink_suspend_late(struct platform_device *pdev, pm_message_t state) +int htc_pwrsink_suspend_late(struct device *dev) { - struct pwr_sink_platform_data *pdata = pdev->dev.platform_data; + struct pwr_sink_platform_data *pdata = dev_get_platdata(dev); if (pdata && pdata->suspend_late) - pdata->suspend_late(pdev, state); + pdata->suspend_late(to_platform_device(dev), PMSG_SUSPEND); else htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 1); return 0; } -int htc_pwrsink_resume_early(struct platform_device *pdev) +int htc_pwrsink_resume_early(struct device *dev) { - struct pwr_sink_platform_data *pdata = pdev->dev.platform_data; + struct pwr_sink_platform_data *pdata = dev_get_platdata(dev);; if (pdata && pdata->resume_early) - pdata->resume_early(pdev); + pdata->resume_early(to_platform_device(dev)); else htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 7); return 0; @@ -265,13 +265,17 @@ static int __init htc_pwrsink_probe(struct platform_device *pdev) return 0; } +static struct dev_pm_ops htc_pwrsink_pm_ops = { + .suspend_noirq = htc_pwrsink_suspend_late, + .resume_noirq = htc_pwrsink_resume_early, +}; + static struct platform_driver htc_pwrsink_driver = { .probe = htc_pwrsink_probe, - .suspend_late = htc_pwrsink_suspend_late, - .resume_early = htc_pwrsink_resume_early, .driver = { .name = "htc_pwrsink", .owner = THIS_MODULE, + .pm = &htc_pwrsink_pm_ops, }, }; From ed92dc1799fdc61eb3a747603e14c20762ff643b Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 26 Mar 2009 01:07:10 -0700 Subject: [PATCH 0308/2556] [ARM] msm: sapphire: Clean up debug messages Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-sapphire-mmc.c | 2 +- arch/arm/mach-msm/board-sapphire-panel.c | 6 ++++++ arch/arm/mach-msm/board-sapphire.c | 13 ++++--------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire-mmc.c b/arch/arm/mach-msm/board-sapphire-mmc.c index 6b5589e31431e..fbc0fb1ffee29 100755 --- a/arch/arm/mach-msm/board-sapphire-mmc.c +++ b/arch/arm/mach-msm/board-sapphire-mmc.c @@ -35,7 +35,7 @@ #include "board-sapphire.h" #include "proc_comm.h" -#define DEBUG_SDSLOT_VDD 1 +#define DEBUG_SDSLOT_VDD 0 extern int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat); diff --git a/arch/arm/mach-msm/board-sapphire-panel.c b/arch/arm/mach-msm/board-sapphire-panel.c index 34ab52da9490d..6f594ef922cde 100644 --- a/arch/arm/mach-msm/board-sapphire-panel.c +++ b/arch/arm/mach-msm/board-sapphire-panel.c @@ -33,6 +33,8 @@ #include "proc_comm.h" #include "devices.h" +#define DEBUG_SAPPHIRE_PANEL 0 + enum sapphire_panel_type { SAPPHIRE_PANEL_SHARP = 0, SAPPHIRE_PANEL_TOPPOLY, @@ -76,8 +78,10 @@ static void sapphire_set_backlight_level(uint8_t level) level*SDBB/GDBB : (SDBB + (level-GDBB)*(255-SDBB) / (255-GDBB)) ; index = new_level/dimming_factor ; +#if DEBUG_SAPPHIRE_PANEL printk(KERN_INFO "level=%d, new level=%d, dimming_levels[%d]=%d\n", level, new_level, index, dimming_levels[g_panel_id][index]); +#endif percent = pwrsink_percents[index]; level = dimming_levels[g_panel_id][index]; @@ -402,7 +406,9 @@ static void sapphire_mddi_power_client(struct msm_mddi_client_data *client_data, int on) { unsigned id, on_off; +#if DEBUG_SAPPHIRE_PANEL printk(KERN_INFO "sapphire_mddi_client_power:%d\r\n", on); +#endif if (on) { on_off = 0; id = PM_VREG_PDOWN_MDDI_ID; diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index 6cdd4551570bf..429a56f470824 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -1086,6 +1086,9 @@ static void __init sapphire_init(void) sapphire_gpio_write(NULL, SAPPHIRE_GPIO_VCM_PWDN, 1); mdelay(100); + printk(KERN_DEBUG "sapphire_is_5M_camera=%d\n", + sapphire_is_5M_camera()); + printk(KERN_DEBUG "is_12pin_camera=%d\n", is_12pin_camera()); #ifdef CONFIG_SERIAL_MSM_HS msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; #endif @@ -1128,22 +1131,16 @@ static struct map_desc sapphire_io_desc[] __initdata = { unsigned int sapphire_get_hwid(void) { - printk(KERN_DEBUG "sapphire_get_hwid=0x%x\r\n", hwid); - return hwid; } unsigned int sapphire_get_skuid(void) { - printk(KERN_DEBUG "sapphire_get_skuid=0x%x\r\n", skuid); - return skuid; } unsigned sapphire_engineerid(void) { - printk(KERN_DEBUG "sapphire_engineerid=0x%x\r\n", engineerid); - return engineerid; } @@ -1154,7 +1151,6 @@ int sapphire_is_5M_camera(void) ret = 1; else if (sapphire_get_skuid() == 0x20100 && !(sapphire_engineerid() & 0x02)) ret = 1; - printk(KERN_DEBUG "sapphire_is_5M_camera=%d\n", ret); return ret; } @@ -1167,13 +1163,12 @@ unsigned int is_12pin_camera(void) ret = 1; else ret = 0; - printk(KERN_DEBUG "is_12pin_camera=%d\r\n", ret); return ret; } int sapphire_get_smi_size(void) { - printk(KERN_DEBUG "get_smi_size=%d\r\n", smi_sz); + printk(KERN_DEBUG "get_smi_size=%d\n", smi_sz); return smi_sz; } From 31769337fc4730036b2d331374018771ff9cffce Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 30 Mar 2009 09:43:17 -0400 Subject: [PATCH 0309/2556] [ARM] msm: htc: Fix USB serial number support with hboot 0.95.0000 Only use default USB serial number if androidboot.mode is "factory2" Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/devices_htc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c index 5e88f8e1ce83b..b37e262decfd6 100644 --- a/arch/arm/mach-msm/devices_htc.c +++ b/arch/arm/mach-msm/devices_htc.c @@ -379,7 +379,8 @@ static int __init board_serialno_setup(char *serialno) { char *str; - if (board_mfg_mode() || !strlen(serialno)) + /* use default serial number when mode is factory2 */ + if (mfg_mode == 1 || !strlen(serialno)) str = df_serialno; else str = serialno; From f5469cb65b5f8c182f6df5678b0e28253e20c610 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Wed, 27 May 2009 22:01:38 -0700 Subject: [PATCH 0310/2556] [ARM] msm: rpc_router: add support for backwards compatibility Signed-off-by: Iliyan Malchev --- .../arm/mach-msm/include/mach/msm_rpcrouter.h | 36 ++++- arch/arm/mach-msm/smd_rpcrouter.c | 145 +++++++++++++++--- arch/arm/mach-msm/smd_rpcrouter.h | 13 +- arch/arm/mach-msm/smd_rpcrouter_device.c | 51 +++++- include/linux/msm_rpcrouter.h | 3 + 5 files changed, 210 insertions(+), 38 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/msm_rpcrouter.h b/arch/arm/mach-msm/include/mach/msm_rpcrouter.h index 8f53d2d7e23df..9724ece1c97c9 100644 --- a/arch/arm/mach-msm/include/mach/msm_rpcrouter.h +++ b/arch/arm/mach-msm/include/mach/msm_rpcrouter.h @@ -1,7 +1,7 @@ /** include/asm-arm/arch-msm/msm_rpcrouter.h * * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2007 QUALCOMM Incorporated + * Copyright (c) 2007-2009 QUALCOMM Incorporated * Author: San Mehat * * This software is licensed under the terms of the GNU General Public @@ -22,6 +22,35 @@ #include #include +#if CONFIG_MSM_AMSS_VERSION >= 6350 +/* RPC API version structure + * Version bit 31 : 1->hashkey versioning, + * 0->major-minor (backward compatible) versioning + * hashkey versioning: + * Version bits 31-0 hashkey + * major-minor (backward compatible) versioning + * Version bits 30-28 reserved (no match) + * Version bits 27-16 major (must match) + * Version bits 15-0 minor (greater or equal) + */ +#define RPC_VERSION_MODE_MASK 0x80000000 +#define RPC_VERSION_MAJOR_MASK 0x0fff0000 +#define RPC_VERSION_MAJOR_OFFSET 16 +#define RPC_VERSION_MINOR_MASK 0x0000ffff + +#define MSM_RPC_VERS(major, minor) \ + ((uint32_t)((((major) << RPC_VERSION_MAJOR_OFFSET) & \ + RPC_VERSION_MAJOR_MASK) | \ + ((minor) & RPC_VERSION_MINOR_MASK))) +#define MSM_RPC_GET_MAJOR(vers) (((vers) & RPC_VERSION_MAJOR_MASK) >> \ + RPC_VERSION_MAJOR_OFFSET) +#define MSM_RPC_GET_MINOR(vers) ((vers) & RPC_VERSION_MINOR_MASK) +#else +#define MSM_RPC_VERS(major, minor) (major) +#define MSM_RPC_GET_MAJOR(vers) (vers) +#define MSM_RPC_GET_MINOR(vers) 0 +#endif + struct msm_rpc_endpoint; struct rpcsvr_platform_device @@ -99,7 +128,12 @@ struct rpc_reply_hdr /* use IS_ERR() to check for failure */ struct msm_rpc_endpoint *msm_rpc_open(void); +/* Connect with the specified server version */ struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned flags); +uint32_t msm_rpc_get_vers(struct msm_rpc_endpoint *ept); +/* check if server version can handle client requested version */ +int msm_rpc_is_compatible_version(uint32_t server_version, + uint32_t client_version); int msm_rpc_close(struct msm_rpc_endpoint *ept); int msm_rpc_write(struct msm_rpc_endpoint *ept, diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c index b1899898a7dda..7cd3458204de4 100644 --- a/arch/arm/mach-msm/smd_rpcrouter.c +++ b/arch/arm/mach-msm/smd_rpcrouter.c @@ -1,7 +1,7 @@ /* arch/arm/mach-msm/smd_rpcrouter.c * * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2007 QUALCOMM Incorporated + * Copyright (c) 2007-2009 QUALCOMM Incorporated. * Author: San Mehat * * This software is licensed under the terms of the GNU General Public @@ -15,7 +15,6 @@ * */ -/* TODO: fragmentation for large writes */ /* TODO: handle cases where smd_write() will tempfail due to full fifo */ /* TODO: thread priority? schedule a work to bump it? */ /* TODO: maybe make server_list_lock a mutex */ @@ -39,6 +38,9 @@ #include #include #include +#include + +#include #include #include "smd_rpcrouter.h" @@ -46,6 +48,7 @@ #define TRACE_R2R_MSG 0 #define TRACE_R2R_RAW 0 #define TRACE_RPC_MSG 0 +#define TRACE_NOTIFY_MSG 0 #define MSM_RPCROUTER_DEBUG 0 #define MSM_RPCROUTER_DEBUG_PKT 0 @@ -72,6 +75,12 @@ #define IO(x...) do {} while (0) #endif +#if TRACE_NOTIFY_MSG +#define NTFY(x...) printk(KERN_ERR "[NOTIFY] "x) +#else +#define NTFY(x...) do {} while (0) +#endif + static LIST_HEAD(local_endpoints); static LIST_HEAD(remote_endpoints); @@ -206,8 +215,7 @@ static void rpcrouter_destroy_server(struct rr_server *server) kfree(server); } -static struct rr_server *rpcrouter_lookup_server(uint32_t prog, - uint32_t ver) +static struct rr_server *rpcrouter_lookup_server(uint32_t prog, uint32_t ver) { struct rr_server *server; unsigned long flags; @@ -240,7 +248,6 @@ static struct rr_server *rpcrouter_lookup_server_by_dev(dev_t dev) return NULL; } - struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev) { struct msm_rpc_endpoint *ept; @@ -273,9 +280,12 @@ struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev) ept->dst_cid = srv->cid; ept->dst_prog = cpu_to_be32(srv->prog); ept->dst_vers = cpu_to_be32(srv->vers); + + D("Creating local ept %p @ %08x:%08x\n", ept, srv->prog, srv->vers); } else { /* mark not connected */ ept->dst_pid = 0xffffffff; + D("Creating a master local ept %p\n", ept); } init_waitqueue_head(&ept->wait_q); @@ -399,7 +409,7 @@ static int process_control_msg(union rr_control_msg *msg, int len) ctl.srv.prog = server->prog; ctl.srv.vers = server->vers; - RR("x NEW_SERVER id=%d:%08x prog=%08x:%d\n", + RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n", server->pid, server->cid, server->prog, server->vers); @@ -426,7 +436,7 @@ static int process_control_msg(union rr_control_msg *msg, int len) break; case RPCROUTER_CTRL_CMD_NEW_SERVER: - RR("o NEW_SERVER id=%d:%08x prog=%08x:%d\n", + RR("o NEW_SERVER id=%d:%08x prog=%08x:%08x\n", msg->srv.pid, msg->srv.cid, msg->srv.prog, msg->srv.vers); server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers); @@ -633,11 +643,11 @@ static void do_read_data(struct work_struct *work) kfree(frag); goto done; } - + /* See if there is already a partial packet that matches our mid * and if so, append this fragment to that packet. */ - mid = PACMARK_MID(pm); + mid = PACMARK_MID(pm); list_for_each_entry(pkt, &ept->incomplete, list) { if (pkt->mid == mid) { pkt->last->next = frag; @@ -650,7 +660,6 @@ static void do_read_data(struct work_struct *work) goto done; } } - /* This mid is new -- create a packet for it, and put it on * the incomplete list if this fragment is not a last fragment, * otherwise put it on the read queue. @@ -673,13 +682,14 @@ static void do_read_data(struct work_struct *work) wake_up(&ept->wait_q); spin_unlock_irqrestore(&ept->read_q_lock, flags); done: + if (hdr.confirm_rx) { union rr_control_msg msg; msg.cmd = RPCROUTER_CTRL_CMD_RESUME_TX; msg.cli.pid = hdr.dst_pid; msg.cli.cid = hdr.dst_cid; - + RR("x RESUME_TX id=%d:%08x\n", msg.cli.pid, msg.cli.cid); rpcrouter_send_control_msg(&msg); } @@ -719,6 +729,7 @@ int msm_rpc_close(struct msm_rpc_endpoint *ept) { return msm_rpcrouter_destroy_local_endpoint(ept); } +EXPORT_SYMBOL(msm_rpc_close); int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count) { @@ -753,8 +764,15 @@ int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count) printk(KERN_ERR "rr_write: not connected\n"); return -ENOTCONN; } + +#if CONFIG_MSM_AMSS_VERSION >= 6350 if ((ept->dst_prog != rq->prog) || - (ept->dst_vers != rq->vers)) { + !msm_rpc_is_compatible_version( + be32_to_cpu(ept->dst_vers), + be32_to_cpu(rq->vers))) { +#else + if (ept->dst_prog != rq->prog || ept->dst_vers != rq->vers) { +#endif printk(KERN_ERR "rr_write: cannot write to %08x:%d " "(bound to %08x:%d)\n", @@ -765,9 +783,11 @@ int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count) } hdr.dst_pid = ept->dst_pid; hdr.dst_cid = ept->dst_cid; - IO("CALL to %08x:%d @ %d:%08x (%d bytes)\n", + IO("CALL on ept %p to %08x:%08x @ %d:%08x (%d bytes) (xid %x proc %x)\n", + ept, be32_to_cpu(rq->prog), be32_to_cpu(rq->vers), - ept->dst_pid, ept->dst_cid, count); + ept->dst_pid, ept->dst_cid, count, + be32_to_cpu(rq->xid), be32_to_cpu(rq->procedure)); } else { /* RPC REPLY */ /* TODO: locking */ @@ -788,7 +808,8 @@ int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count) /* consume this reply */ ept->reply_pid = 0xffffffff; - IO("REPLY to xid=%d @ %d:%08x (%d bytes)\n", + IO("REPLY on ept %p to xid=%d @ %d:%08x (%d bytes)\n", + ept, be32_to_cpu(rq->xid), hdr.dst_pid, hdr.dst_cid, count); } @@ -835,7 +856,7 @@ int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count) /* bump pacmark while interrupts disabled to avoid race * probably should be atomic op instead */ - pacmark = PACMARK(count, ++next_pacmarkid, 1); + pacmark = PACMARK(count, ++next_pacmarkid, 0, 1); spin_unlock_irqrestore(&r_ept->quota_lock, flags); @@ -857,6 +878,7 @@ int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count) return count; } +EXPORT_SYMBOL(msm_rpc_write); /* * NOTE: It is the responsibility of the caller to kfree buffer @@ -905,6 +927,7 @@ int msm_rpc_call(struct msm_rpc_endpoint *ept, uint32_t proc, _request, request_size, NULL, 0, timeout); } +EXPORT_SYMBOL(msm_rpc_call); int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc, void *_request, int request_size, @@ -921,6 +944,9 @@ int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc, if (ept->dst_pid == 0xffffffff) return -ENOTCONN; + /* We can't use msm_rpc_setup_req() here, because dst_prog and + * dst_vers here are already in BE. + */ memset(req, 0, sizeof(*req)); req->xid = cpu_to_be32(atomic_add_return(1, &next_xid)); req->rpc_vers = cpu_to_be32(2); @@ -947,7 +973,7 @@ int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc, } /* If an earlier call timed out, we could get the (no * longer wanted) reply for it. Ignore replies that - * we don't expect + * we don't expect. */ if (reply->xid != req->xid) { kfree(reply); @@ -975,6 +1001,7 @@ int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc, kfree(reply); return rc; } +EXPORT_SYMBOL(msm_rpc_call_reply); static inline int ept_packet_available(struct msm_rpc_endpoint *ept) @@ -1044,6 +1071,10 @@ int __msm_rpc_read(struct msm_rpc_endpoint *ept, *frag_ret = pkt->first; rq = (void*) pkt->first->data; if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 0)) { + IO("READ on ept %p is a CALL on %08x:%08x proc %d xid %d\n", + ept, be32_to_cpu(rq->prog), be32_to_cpu(rq->vers), + be32_to_cpu(rq->procedure), + be32_to_cpu(rq->xid)); /* RPC CALL */ if (ept->reply_pid != 0xffffffff) { printk(KERN_WARNING @@ -1054,18 +1085,75 @@ int __msm_rpc_read(struct msm_rpc_endpoint *ept, ept->reply_cid = pkt->hdr.src_cid; ept->reply_xid = rq->xid; } +#if TRACE_RPC_MSG + else if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 1)) + IO("READ on ept %p is a REPLY\n", ept); + else IO("READ on ept %p (%d bytes)\n", ept, rc); +#endif kfree(pkt); - - IO("READ on ept %p (%d bytes)\n", ept, rc); return rc; } +#if CONFIG_MSM_AMSS_VERSION >= 6350 +int msm_rpc_is_compatible_version(uint32_t server_version, + uint32_t client_version) +{ + if ((server_version & RPC_VERSION_MODE_MASK) != + (client_version & RPC_VERSION_MODE_MASK)) + return 0; + + if (server_version & RPC_VERSION_MODE_MASK) + return server_version == client_version; + + return ((server_version & RPC_VERSION_MAJOR_MASK) == + (client_version & RPC_VERSION_MAJOR_MASK)) && + ((server_version & RPC_VERSION_MINOR_MASK) >= + (client_version & RPC_VERSION_MINOR_MASK)); +} +EXPORT_SYMBOL(msm_rpc_is_compatible_version); + +static int msm_rpc_get_compatible_server(uint32_t prog, + uint32_t ver, + uint32_t *found_vers) +{ + struct rr_server *server; + unsigned long flags; + if (found_vers == NULL) + return 0; + + spin_lock_irqsave(&server_list_lock, flags); + list_for_each_entry(server, &server_list, list) { + if ((server->prog == prog) && + msm_rpc_is_compatible_version(server->vers, ver)) { + *found_vers = server->vers; + spin_unlock_irqrestore(&server_list_lock, flags); + return 0; + } + } + spin_unlock_irqrestore(&server_list_lock, flags); + return -1; +} +#endif + struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned flags) { struct msm_rpc_endpoint *ept; struct rr_server *server; +#if CONFIG_MSM_AMSS_VERSION >= 6350 + if (!(vers & RPC_VERSION_MODE_MASK)) { + uint32_t found_vers; + if (msm_rpc_get_compatible_server(prog, vers, &found_vers) < 0) + return ERR_PTR(-EHOSTUNREACH); + if (found_vers != vers) { + D("RPC using new version %08x:{%08x --> %08x}\n", + prog, vers, found_vers); + vers = found_vers; + } + } +#endif + server = rpcrouter_lookup_server(prog, vers); if (!server) return ERR_PTR(-EHOSTUNREACH); @@ -1073,7 +1161,7 @@ struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned ept = msm_rpc_open(); if (IS_ERR(ept)) return ept; - + ept->flags = flags; ept->dst_pid = server->pid; ept->dst_cid = server->cid; @@ -1082,6 +1170,13 @@ struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned return ept; } +EXPORT_SYMBOL(msm_rpc_connect); + +uint32_t msm_rpc_get_vers(struct msm_rpc_endpoint *ept) +{ + return be32_to_cpu(ept->dst_vers); +} +EXPORT_SYMBOL(msm_rpc_get_vers); /* TODO: permission check? */ int msm_rpc_register_server(struct msm_rpc_endpoint *ept, @@ -1102,7 +1197,7 @@ int msm_rpc_register_server(struct msm_rpc_endpoint *ept, msg.srv.prog = prog; msg.srv.vers = vers; - RR("x NEW_SERVER id=%d:%08x prog=%08x:%d\n", + RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n", ept->pid, ept->cid, prog, vers); rc = rpcrouter_send_control_msg(&msg); @@ -1154,9 +1249,9 @@ static int msm_rpcrouter_probe(struct platform_device *pdev) queue_work(rpcrouter_workqueue, &work_read_data); return 0; -fail_remove_devices: + fail_remove_devices: msm_rpcrouter_exit_devices(); -fail_destroy_workqueue: + fail_destroy_workqueue: destroy_workqueue(rpcrouter_workqueue); return rc; } @@ -1164,8 +1259,8 @@ static int msm_rpcrouter_probe(struct platform_device *pdev) static struct platform_driver msm_smd_channel2_driver = { .probe = msm_rpcrouter_probe, .driver = { - .name = "SMD_RPCCALL", - .owner = THIS_MODULE, + .name = "SMD_RPCCALL", + .owner = THIS_MODULE, }, }; diff --git a/arch/arm/mach-msm/smd_rpcrouter.h b/arch/arm/mach-msm/smd_rpcrouter.h index 9de837daa5cba..a7416a2ec58cf 100644 --- a/arch/arm/mach-msm/smd_rpcrouter.h +++ b/arch/arm/mach-msm/smd_rpcrouter.h @@ -1,7 +1,7 @@ /** arch/arm/mach-msm/smd_rpcrouter.h * * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2007 QUALCOMM Incorporated + * Copyright (c) 2007-2008 QUALCOMM Incorporated. * Author: San Mehat * * This software is licensed under the terms of the GNU General Public @@ -27,8 +27,6 @@ #include #include -#include - /* definitions for the R2R wire protcol */ #define RPCROUTER_VERSION 1 @@ -102,9 +100,13 @@ struct rr_packet { #define PACMARK_MID(n) (((n) >> 16) & 0xFF) #define PACMARK_LEN(n) ((n) & 0xFFFF) -static inline uint32_t PACMARK(uint32_t len, uint32_t mid, uint32_t last) +static inline uint32_t PACMARK(uint32_t len, uint32_t mid, uint32_t first, + uint32_t last) { - return (len & 0xFFFF) | ((mid & 0xFF) << 16) | ((!!last) << 31); + return (len & 0xFFFF) | + ((mid & 0xFF) << 16) | + ((!!first) << 30) | + ((!!last) << 31); } struct rr_server { @@ -167,6 +169,7 @@ struct msm_rpc_endpoint { uint32_t reply_pid; uint32_t reply_cid; uint32_t reply_xid; /* be32 */ + uint32_t next_pm; /* Pacmark sequence */ /* device node if this endpoint is accessed via userspace */ dev_t dev; diff --git a/arch/arm/mach-msm/smd_rpcrouter_device.c b/arch/arm/mach-msm/smd_rpcrouter_device.c index 56a15984f9f1b..05a122c88d060 100644 --- a/arch/arm/mach-msm/smd_rpcrouter_device.c +++ b/arch/arm/mach-msm/smd_rpcrouter_device.c @@ -1,7 +1,7 @@ /* arch/arm/mach-msm/smd_rpcrouter_device.c * * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2007 QUALCOMM Incorporated + * Copyright (c) 2007-2009 QUALCOMM Incorporated. * Author: San Mehat * * This software is licensed under the terms of the GNU General Public @@ -28,13 +28,16 @@ #include #include #include +#include +#include + #include #include -#include -#include #include "smd_rpcrouter.h" +#define SAFETY_MEM_SIZE 65536 + /* Next minor # available for a remote server */ static int next_minor = 1; @@ -108,7 +111,8 @@ static ssize_t rpcrouter_write(struct file *filp, const char __user *buf, ept = (struct msm_rpc_endpoint *) filp->private_data; - if (count > RPCROUTER_MSGSIZE_MAX) + /* A check for safety, this seems non-standard */ + if (count > SAFETY_MEM_SIZE) return -EINVAL; k_buffer = kmalloc(count, GFP_KERNEL); @@ -140,6 +144,7 @@ static unsigned int rpcrouter_poll(struct file *filp, /* If there's data already in the read queue, return POLLIN. * Else, wait for the requested amount of time, and check again. */ + if (!list_empty(&ept->read_q)) mask |= POLLIN; @@ -157,7 +162,7 @@ static long rpcrouter_ioctl(struct file *filp, unsigned int cmd, { struct msm_rpc_endpoint *ept; struct rpcrouter_ioctl_server_args server_args; - int rc; + int rc = 0; uint32_t n; ept = (struct msm_rpc_endpoint *) filp->private_data; @@ -197,6 +202,11 @@ static long rpcrouter_ioctl(struct file *filp, unsigned int cmd, server_args.vers); break; + case RPC_ROUTER_IOCTL_GET_MINOR_VERSION: + n = MSM_RPC_GET_MINOR(msm_rpc_get_vers(ept)); + rc = put_user(n, (unsigned int *)arg); + break; + default: rc = -EINVAL; break; @@ -228,6 +238,7 @@ static struct file_operations rpcrouter_router_fops = { int msm_rpcrouter_create_server_cdev(struct rr_server *server) { int rc; + uint32_t dev_vers; if (next_minor == RPCROUTER_MAX_REMOTE_SERVERS) { printk(KERN_ERR @@ -235,13 +246,29 @@ int msm_rpcrouter_create_server_cdev(struct rr_server *server) "RPCROUTER_MAX_REMOTE_SERVERS\n"); return -ENOBUFS; } + +#if CONFIG_MSM_AMSS_VERSION >= 6350 + /* Servers with bit 31 set are remote msm servers with hashkey version. + * Servers with bit 31 not set are remote msm servers with + * backwards compatible version type in which case the minor number + * (lower 16 bits) is set to zero. + * + */ + if ((server->vers & RPC_VERSION_MODE_MASK)) + dev_vers = server->vers; + else + dev_vers = server->vers & RPC_VERSION_MAJOR_MASK; +#else + dev_vers = server->vers; +#endif + server->device_number = MKDEV(MAJOR(msm_rpcrouter_devno), next_minor++); server->device = device_create(msm_rpcrouter_class, rpcrouter_device, server->device_number, NULL, "%.8x:%.8x", - server->prog, server->vers); + server->prog, dev_vers); if (IS_ERR(server->device)) { printk(KERN_ERR "rpcrouter: Unable to create device (%ld)\n", @@ -262,10 +289,20 @@ int msm_rpcrouter_create_server_cdev(struct rr_server *server) return 0; } +/* for backward compatible version type (31st bit cleared) + * clearing minor number (lower 16 bits) in device name + * is neccessary for driver binding + */ int msm_rpcrouter_create_server_pdev(struct rr_server *server) { sprintf(server->pdev_name, "rs%.8x:%.8x", - server->prog, server->vers); + server->prog, +#if CONFIG_MSM_AMSS_VERSION >= 6350 + (server->vers & RPC_VERSION_MODE_MASK) ? server->vers : + (server->vers & RPC_VERSION_MAJOR_MASK)); +#else + server->vers); +#endif server->p_device.base.id = -1; server->p_device.base.name = server->pdev_name; diff --git a/include/linux/msm_rpcrouter.h b/include/linux/msm_rpcrouter.h index 3e2adc0948645..62141a04e002f 100644 --- a/include/linux/msm_rpcrouter.h +++ b/include/linux/msm_rpcrouter.h @@ -41,4 +41,7 @@ struct rpcrouter_ioctl_server_args { #define RPC_ROUTER_IOCTL_UNREGISTER_SERVER \ _IOWR(RPC_ROUTER_IOCTL_MAGIC, 3, unsigned int) +#define RPC_ROUTER_IOCTL_GET_MINOR_VERSION \ + _IOW(RPC_ROUTER_IOCTL_MAGIC, 4, unsigned int) + #endif From ce858ff3a1ace8a0ca1a866a7d4e01989d7a0799 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Wed, 27 May 2009 19:34:28 -0700 Subject: [PATCH 0311/2556] [ARM] msm: support for AMSS 6350 Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/Kconfig | 4 + arch/arm/mach-msm/devices_htc.c | 3 +- arch/arm/mach-msm/htc_acoustic.c | 2 +- arch/arm/mach-msm/htc_battery.c | 2 +- arch/arm/mach-msm/include/mach/msm_adsp.h | 1 + .../arm/mach-msm/include/mach/msm_audio_aac.h | 75 ++ .../include/mach/qdsp5/qdsp5audplaycmdi.h | 53 +- .../include/mach/qdsp5/qdsp5audplaymsg.h | 47 +- .../include/mach/qdsp5/qdsp5audppcmdi.h | 188 +-- .../include/mach/qdsp5/qdsp5audppmsg.h | 56 +- .../include/mach/qdsp5/qdsp5audpreproccmdi.h | 6 +- .../include/mach/qdsp5/qdsp5audpreprocmsg.h | 4 +- .../include/mach/qdsp5/qdsp5audreccmdi.h | 10 +- .../include/mach/qdsp5/qdsp5audrecmsg.h | 12 +- .../include/mach/qdsp5/qdsp5jpegcmdi.h | 11 +- .../include/mach/qdsp5/qdsp5jpegmsg.h | 12 +- .../include/mach/qdsp5/qdsp5lpmcmdi.h | 14 +- .../mach-msm/include/mach/qdsp5/qdsp5lpmmsg.h | 12 +- .../include/mach/qdsp5/qdsp5vdeccmdi.h | 14 +- .../include/mach/qdsp5/qdsp5vdecmsg.h | 12 +- .../include/mach/qdsp5/qdsp5venccmdi.h | 102 +- .../include/mach/qdsp5/qdsp5vfecmdi.h | 12 +- .../mach-msm/include/mach/qdsp5/qdsp5vfemsg.h | 18 +- arch/arm/mach-msm/msm_vibrator.c | 2 +- arch/arm/mach-msm/qdsp5/Makefile | 12 +- arch/arm/mach-msm/qdsp5/adsp.c | 96 +- arch/arm/mach-msm/qdsp5/adsp.h | 6 + arch/arm/mach-msm/qdsp5/adsp_driver.c | 42 +- arch/arm/mach-msm/qdsp5/adsp_info.c | 121 ++ .../arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c | 3 +- arch/arm/mach-msm/qdsp5/adsp_new.c | 1105 +++++++++++++++++ arch/arm/mach-msm/qdsp5/adsp_new.h | 360 ++++++ arch/arm/mach-msm/qdsp5/audio_aac.c | 1052 ++++++++++++++++ arch/arm/mach-msm/qdsp5/audio_amrnb.c | 873 +++++++++++++ arch/arm/mach-msm/qdsp5/audio_evrc.c | 845 +++++++++++++ arch/arm/mach-msm/qdsp5/audio_in.c | 80 +- arch/arm/mach-msm/qdsp5/audio_mp3.c | 572 +++++++-- arch/arm/mach-msm/qdsp5/audio_qcelp.c | 856 +++++++++++++ arch/arm/mach-msm/qdsp5/audmgr.c | 33 +- arch/arm/mach-msm/qdsp5/audmgr.h | 12 +- arch/arm/mach-msm/qdsp5/audmgr_new.h | 213 ++++ arch/arm/mach-msm/qdsp5/audpp.c | 126 +- arch/arm/mach-msm/qdsp5/snd.c | 15 +- arch/arm/mach-msm/rpc_server_dog_keepalive.c | 10 +- arch/arm/mach-msm/rpc_server_time_remote.c | 8 +- arch/arm/mach-msm/smd_rpcrouter_servers.c | 18 +- drivers/rtc/rtc-msm7x00a.c | 4 + include/linux/msm_adsp.h | 4 +- include/linux/msm_audio.h | 16 +- 49 files changed, 6645 insertions(+), 509 deletions(-) create mode 100644 arch/arm/mach-msm/include/mach/msm_audio_aac.h create mode 100644 arch/arm/mach-msm/qdsp5/adsp_info.c create mode 100644 arch/arm/mach-msm/qdsp5/adsp_new.c create mode 100644 arch/arm/mach-msm/qdsp5/adsp_new.h create mode 100644 arch/arm/mach-msm/qdsp5/audio_aac.c create mode 100644 arch/arm/mach-msm/qdsp5/audio_amrnb.c create mode 100644 arch/arm/mach-msm/qdsp5/audio_evrc.c create mode 100644 arch/arm/mach-msm/qdsp5/audio_qcelp.c create mode 100644 arch/arm/mach-msm/qdsp5/audmgr_new.h diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index f391c760a098a..ad0bd9681475e 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -71,6 +71,7 @@ config MSM_AMSS_VERSION default 6210 if MSM_AMSS_VERSION_6210 default 6220 if MSM_AMSS_VERSION_6220 default 6225 if MSM_AMSS_VERSION_6225 + default 6350 if MSM_AMSS_VERSION_6350 choice prompt "AMSS modem firmware version" @@ -85,6 +86,9 @@ choice config MSM_AMSS_VERSION_6225 bool "6.2.20 + New ADSP" + + config MSM_AMSS_VERSION_6350 + bool "6.3.50" endchoice menu "Qualcomm MSM Board Type" diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c index b37e262decfd6..b24df91ef0709 100644 --- a/arch/arm/mach-msm/devices_htc.c +++ b/arch/arm/mach-msm/devices_htc.c @@ -55,7 +55,8 @@ void __init msm_add_devices(void) #define HSUSB_API_INIT_PHY_PROC 2 #define HSUSB_API_PROG 0x30000064 -#define HSUSB_API_VERS 0x10001 +#define HSUSB_API_VERS MSM_RPC_VERS(1,1) + static void internal_phy_reset(void) { struct msm_rpc_endpoint *usb_ep; diff --git a/arch/arm/mach-msm/htc_acoustic.c b/arch/arm/mach-msm/htc_acoustic.c index 79e603bb249dc..2360b37f55e28 100644 --- a/arch/arm/mach-msm/htc_acoustic.c +++ b/arch/arm/mach-msm/htc_acoustic.c @@ -35,7 +35,7 @@ #define ACOUSTIC_ARM11_DONE _IOW(ACOUSTIC_IOCTL_MAGIC, 22, unsigned int) #define HTCRPOG 0x30100002 -#define HTCVERS 0 +#define HTCVERS MSM_RPC_VERS(0,0) #define ONCRPC_SET_MIC_BIAS_PROC (1) #define ONCRPC_ACOUSTIC_INIT_PROC (5) #define ONCRPC_ALLOC_ACOUSTIC_MEM_PROC (6) diff --git a/arch/arm/mach-msm/htc_battery.c b/arch/arm/mach-msm/htc_battery.c index deb5a12081d18..1a4face74195a 100644 --- a/arch/arm/mach-msm/htc_battery.c +++ b/arch/arm/mach-msm/htc_battery.c @@ -52,7 +52,7 @@ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \ /* rpc related */ #define APP_BATT_PDEV_NAME "rs30100001:00000000" #define APP_BATT_PROG 0x30100001 -#define APP_BATT_VER 0 +#define APP_BATT_VER MSM_RPC_VERS(0,0) #define HTC_PROCEDURE_BATTERY_NULL 0 #define HTC_PROCEDURE_GET_BATT_LEVEL 1 #define HTC_PROCEDURE_GET_BATT_INFO 2 diff --git a/arch/arm/mach-msm/include/mach/msm_adsp.h b/arch/arm/mach-msm/include/mach/msm_adsp.h index d157c251276be..52280cabf8246 100644 --- a/arch/arm/mach-msm/include/mach/msm_adsp.h +++ b/arch/arm/mach-msm/include/mach/msm_adsp.h @@ -38,6 +38,7 @@ int msm_adsp_get(const char *name, struct msm_adsp_module **module, void msm_adsp_put(struct msm_adsp_module *module); int msm_adsp_enable(struct msm_adsp_module *module); int msm_adsp_disable(struct msm_adsp_module *module); +int adsp_set_clkrate(struct msm_adsp_module *module, unsigned long clk_rate); /* Write is safe to call from interrupt context. */ diff --git a/arch/arm/mach-msm/include/mach/msm_audio_aac.h b/arch/arm/mach-msm/include/mach/msm_audio_aac.h new file mode 100644 index 0000000000000..1aea0faa3d31e --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_audio_aac.h @@ -0,0 +1,75 @@ +/* arch/arm/mach-msm/include/mach/msm_audio_aac.h + * + * Copyright (c) 2009 QUALCOMM USA, 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. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org. + * + */ + +#ifndef __MSM_AUDIO_AAC_H +#define __MSM_AUDIO_AAC_H + +#include + +#define AUDIO_SET_AAC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned) +#define AUDIO_GET_AAC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \ + (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned) + +#define AUDIO_AAC_FORMAT_ADTS -1 +#define AUDIO_AAC_FORMAT_RAW 0x0000 +#define AUDIO_AAC_FORMAT_PSUEDO_RAW 0x0001 +#define AUDIO_AAC_FORMAT_LOAS 0x0002 + +#define AUDIO_AAC_OBJECT_LC 0x0002 +#define AUDIO_AAC_OBJECT_LTP 0x0004 +#define AUDIO_AAC_OBJECT_ERLC 0x0011 + +#define AUDIO_AAC_SEC_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SEC_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SCA_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SCA_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SPEC_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SPEC_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SBR_ON_FLAG_ON 0x0001 +#define AUDIO_AAC_SBR_ON_FLAG_OFF 0x0000 + +#define AUDIO_AAC_SBR_PS_ON_FLAG_ON 0x0001 +#define AUDIO_AAC_SBR_PS_ON_FLAG_OFF 0x0000 + +/* Primary channel on both left and right channels */ +#define AUDIO_AAC_DUAL_MONO_PL_PR 0 +/* Secondary channel on both left and right channels */ +#define AUDIO_AAC_DUAL_MONO_SL_SR 1 +/* Primary channel on right channel and 2nd on left channel */ +#define AUDIO_AAC_DUAL_MONO_SL_PR 2 +/* 2nd channel on right channel and primary on left channel */ +#define AUDIO_AAC_DUAL_MONO_PL_SR 3 + +struct msm_audio_aac_config { + signed short format; + unsigned short audio_object; + unsigned short ep_config; /* 0 ~ 3 useful only obj = ERLC */ + unsigned short aac_section_data_resilience_flag; + unsigned short aac_scalefactor_data_resilience_flag; + unsigned short aac_spectral_data_resilience_flag; + unsigned short sbr_on_flag; + unsigned short sbr_ps_on_flag; + unsigned short dual_mono_mode; + unsigned short channel_configuration; +}; + +#endif /* __MSM_AUDIO_AAC_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaycmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaycmdi.h index 439fe2cea8c78..ece4bc7f9e6eb 100644 --- a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaycmdi.h +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaycmdi.h @@ -47,25 +47,48 @@ Notice that changes are listed in reverse chronological order. /* Type specification of dec_data_avail message sent to AUDPLAYTASK */ typedef struct { - /*command ID*/ - unsigned int cmd_id; + /*command ID*/ + unsigned int cmd_id; - /* Decoder ID for which message is being sent */ - unsigned int decoder_id; + /* Decoder ID for which message is being sent */ + unsigned int decoder_id; - /* Start address of data in ARM global memory */ - unsigned int buf_ptr; + /* Start address of data in ARM global memory */ + unsigned int buf_ptr; - /* Number of 16-bit words of bit-stream data contiguously available at the - * above-mentioned address - */ - unsigned int buf_size; - - /* Partition number used by audPlayTask to communicate with DSP's RTOS - * kernel - */ - unsigned int partition_number; + /* Number of 16-bit words of bit-stream data contiguously available at the + * above-mentioned address. */ + unsigned int buf_size; + /* Partition number used by audPlayTask to communicate with DSP's RTOS + * kernel */ + unsigned int partition_number; } __attribute__((packed)) audplay_cmd_bitstream_data_avail; +#define AUDPLAY_CMD_HPCM_BUF_CFG 0x0003 +#define AUDPLAY_CMD_HPCM_BUF_CFG_LEN \ + sizeof(struct audplay_cmd_hpcm_buf_cfg) + +struct audplay_cmd_hpcm_buf_cfg { + unsigned int cmd_id; + unsigned int hostpcm_config; + unsigned int feedback_frequency; + unsigned int byte_swap; + unsigned int max_buffers; + unsigned int partition_number; +} __attribute__((packed)); + +#define AUDPLAY_CMD_BUFFER_REFRESH 0x0004 +#define AUDPLAY_CMD_BUFFER_REFRESH_LEN \ + sizeof(struct audplay_cmd_buffer_update) + +struct audplay_cmd_buffer_refresh { + unsigned int cmd_id; + unsigned int num_buffers; + unsigned int buf_read_count; + unsigned int buf0_address; + unsigned int buf0_length; + unsigned int buf1_address; + unsigned int buf1_length; +} __attribute__((packed)); #endif /* QDSP5AUDPLAYCMD_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaymsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaymsg.h index 74c79d3268269..c63034b8bf137 100644 --- a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaymsg.h +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audplaymsg.h @@ -11,13 +11,13 @@ GENERAL DESCRIPTION REFERENCES None - + Copyright(c) 1992 - 2009 by QUALCOMM, Incorporated. 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 @@ -30,7 +30,7 @@ GNU General Public License for more details. This section contains comments describing changes made to this file. Notice that changes are listed in reverse chronological order. - + $Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audplaymsg.h#3 $ ===========================================================================*/ @@ -39,19 +39,32 @@ Notice that changes are listed in reverse chronological order. sizeof(audplay_msg_dec_needs_data) typedef struct{ - /* reserved*/ - unsigned int dec_id; - - /*The read pointer offset of external memory till which bitstream - has been dmed in*/ - unsigned int adecDataReadPtrOffset; - - /* The buffer size of external memory. */ - unsigned int adecDataBufSize; - - unsigned int bitstream_free_len; - unsigned int bitstream_write_ptr; - unsigned int bitstarem_buf_start; - unsigned int bitstream_buf_len; + /* reserved*/ + unsigned int dec_id; + + /* The read pointer offset of external memory until which the + * bitstream has been DMAed in. */ + unsigned int adecDataReadPtrOffset; + + /* The buffer size of external memory. */ + unsigned int adecDataBufSize; + + unsigned int bitstream_free_len; + unsigned int bitstream_write_ptr; + unsigned int bitstarem_buf_start; + unsigned int bitstream_buf_len; } __attribute__((packed)) audplay_msg_dec_needs_data; + +#define AUDPLAY_MSG_BUFFER_UPDATE 0x0004 +#define AUDPLAY_MSG_BUFFER_UPDATE_LEN \ + sizeof(struct audplay_msg_buffer_update) + +struct audplay_msg_buffer_update { + unsigned int buffer_write_count; + unsigned int num_of_buffer; + unsigned int buf0_address; + unsigned int buf0_length; + unsigned int buf1_address; + unsigned int buf1_length; +} __attribute__((packed)); #endif /* QDSP5AUDPLAYMSG_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppcmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppcmdi.h index 104d2068ca5a9..8bee9c62980b5 100644 --- a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppcmdi.h +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppcmdi.h @@ -6,7 +6,7 @@ A U D I O P O S T P R O C E S S I N G I N T E R N A L C O M M A N D S GENERAL DESCRIPTION - This file contains defintions of format blocks of commands + This file contains defintions of format blocks of commands that are accepted by AUDPP Task REFERENCES @@ -20,7 +20,7 @@ Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. 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 @@ -33,13 +33,13 @@ GNU General Public License for more details. This section contains comments describing changes made to this file. Notice that changes are listed in reverse chronological order. - -$Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audppcmdi.h#2 $ - + +$Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audppcmdi.h#2 $ + ===========================================================================*/ /* - * ARM to AUDPPTASK Commands + * ARM to AUDPPTASK Commands * * ARM uses three command queues to communicate with AUDPPTASK * 1)uPAudPPCmd1Queue : Used for more frequent and shorter length commands @@ -61,7 +61,7 @@ Notice that changes are listed in reverse chronological order. */ /* - * Command Structure to enable or disable the active decoders + * Command Structure to enable or disable the active decoders */ #define AUDPP_CMD_CFG_DEC_TYPE 0x0001 @@ -79,14 +79,14 @@ Notice that changes are listed in reverse chronological order. /* Type specification of cmd_cfg_dec */ - + typedef struct { - unsigned short cmd_id; - unsigned short dec0_cfg; - unsigned short dec1_cfg; - unsigned short dec2_cfg; - unsigned short dec3_cfg; - unsigned short dec4_cfg; + unsigned short cmd_id; + unsigned short dec0_cfg; + unsigned short dec1_cfg; + unsigned short dec2_cfg; + unsigned short dec3_cfg; + unsigned short dec4_cfg; } __attribute__((packed)) audpp_cmd_cfg_dec_type; /* @@ -94,7 +94,7 @@ typedef struct { */ #define AUDPP_CMD_DEC_CTRL 0x0002 -#define AUDPP_CMD_DEC_CTRL_LEN sizeof(audpp_cmd_dec_ctrl); +#define AUDPP_CMD_DEC_CTRL_LEN sizeof(audpp_cmd_dec_ctrl) /* Decoder control commands for pause, resume and flush */ #define AUDPP_CMD_FLUSH_V 0x2000 @@ -109,12 +109,12 @@ typedef struct { /* Type Spec for decoder control command*/ typedef struct { - unsigned short cmd_id; - unsigned short dec0_ctrl; - unsigned short dec1_ctrl; - unsigned short dec2_ctrl; - unsigned short dec3_ctrl; - unsigned short dec4_ctrl; + unsigned short cmd_id; + unsigned short dec0_ctrl; + unsigned short dec1_ctrl; + unsigned short dec2_ctrl; + unsigned short dec3_ctrl; + unsigned short dec4_ctrl; } __attribute__((packed)) audpp_cmd_dec_ctrl; /* @@ -132,7 +132,7 @@ typedef struct { } __attribute__((packed)) audpp_cmd_avsync; /* - * Command Structure to enable or disable(sleep) the AUDPPTASK + * Command Structure to enable or disable(sleep) the AUDPPTASK */ #define AUDPP_CMD_CFG 0x0004 @@ -142,8 +142,8 @@ typedef struct { #define AUDPP_CMD_CFG_ENABLE 0xFFFF typedef struct { - unsigned short cmd_id; - unsigned short cfg; + unsigned short cmd_id; + unsigned short cfg; } __attribute__((packed)) audpp_cmd_cfg; /* @@ -205,6 +205,15 @@ typedef struct { unsigned short byte_counter_msw; } __attribute__((packed)) audpp_cmd_avsync_cmd_3; +#define AUDPP_CMD_ROUTING_MODE 0x0008 +#define AUDPP_CMD_ROUTING_MODE_LEN \ +sizeof(struct audpp_cmd_routing_mode) + +struct audpp_cmd_routing_mode { + unsigned short cmd_id; + unsigned short object_number; + unsigned short routing_mode; +} __attribute__((packed)); /* * Commands Related to uPAudPPCmd2Queue @@ -239,7 +248,7 @@ typedef struct { #define AUDPP_CMD_SAMP_RATE_8000 0x000B -/* +/* * Type specification of cmd_adec_cfg sent to all decoder */ @@ -260,7 +269,7 @@ typedef struct { sizeof(audpp_cmd_cfg_adec_params_wav) -#define AUDPP_CMD_WAV_STEREO_CFG_MONO 0x0001 +#define AUDPP_CMD_WAV_STEREO_CFG_MONO 0x0001 #define AUDPP_CMD_WAV_STEREO_CFG_STEREO 0x0002 #define AUDPP_CMD_WAV_PCM_WIDTH_8 0x0000 @@ -313,7 +322,8 @@ typedef struct { #define AUDPP_CMD_AAC_FORMAT_ADTS -1 #define AUDPP_CMD_AAC_FORMAT_RAW 0x0000 -#define AUDPP_CMD_AAC_FORMAT_PSUEDO_RAW 0x0001 +#define AUDPP_CMD_AAC_FORMAT_PSUEDO_RAW 0x0001 +#define AUDPP_CMD_AAC_FORMAT_LOAS 0x0002 #define AUDPP_CMD_AAC_AUDIO_OBJECT_LC 0x0002 #define AUDPP_CMD_AAC_AUDIO_OBJECT_LTP 0x0004 @@ -339,6 +349,30 @@ typedef struct { unsigned short channel_configuration; } __attribute__((packed)) audpp_cmd_cfg_adec_params_aac; +/* + * Command Structure to configure Per decoder Parameters (V13K) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_v13k) + + +#define AUDPP_CMD_STEREO_CFG_MONO 0x0001 +#define AUDPP_CMD_STEREO_CFG_STEREO 0x0002 + +struct audpp_cmd_cfg_adec_params_v13k { + audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; +} __attribute__((packed)); + +#define AUDPP_CMD_CFG_ADEC_PARAMS_EVRC_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_evrc) + +struct audpp_cmd_cfg_adec_params_evrc { + audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; +} __attribute__ ((packed)); + /* * Command Structure to configure the HOST PCM interface */ @@ -373,47 +407,47 @@ typedef struct { #define AUDPP_CMD_PCM_INTF_OBJECT_NUM 0x5 #define AUDPP_CMD_PCM_INTF_COMMON_OBJECT_NUM 0x6 - + typedef struct { - unsigned short cmd_id; - unsigned short object_num; - signed short config; - unsigned short intf_type; - - /* DSP -> ARM Configuration */ - unsigned short read_buf1LSW; - unsigned short read_buf1MSW; - unsigned short read_buf1_len; - - unsigned short read_buf2LSW; - unsigned short read_buf2MSW; - unsigned short read_buf2_len; - /* 0:HOST_PCM_INTF disable - ** 0xFFFF: HOST_PCM_INTF enable - */ - signed short dsp_to_arm_flag; - unsigned short partition_number; - - /* ARM -> DSP Configuration */ - unsigned short write_buf1LSW; - unsigned short write_buf1MSW; - unsigned short write_buf1_len; - - unsigned short write_buf2LSW; - unsigned short write_buf2MSW; - unsigned short write_buf2_len; - - /* 0:HOST_PCM_INTF disable - ** 0xFFFF: HOST_PCM_INTF enable - */ - signed short arm_to_rx_flag; - unsigned short weight_decoder_to_rx; - unsigned short weight_arm_to_rx; - - unsigned short partition_number_arm_to_dsp; - unsigned short sample_rate; - unsigned short channel_mode; + unsigned short cmd_id; + unsigned short object_num; + signed short config; + unsigned short intf_type; + + /* DSP -> ARM Configuration */ + unsigned short read_buf1LSW; + unsigned short read_buf1MSW; + unsigned short read_buf1_len; + + unsigned short read_buf2LSW; + unsigned short read_buf2MSW; + unsigned short read_buf2_len; + /* 0:HOST_PCM_INTF disable + ** 0xFFFF: HOST_PCM_INTF enable + */ + signed short dsp_to_arm_flag; + unsigned short partition_number; + + /* ARM -> DSP Configuration */ + unsigned short write_buf1LSW; + unsigned short write_buf1MSW; + unsigned short write_buf1_len; + + unsigned short write_buf2LSW; + unsigned short write_buf2MSW; + unsigned short write_buf2_len; + + /* 0:HOST_PCM_INTF disable + ** 0xFFFF: HOST_PCM_INTF enable + */ + signed short arm_to_rx_flag; + unsigned short weight_decoder_to_rx; + unsigned short weight_arm_to_rx; + + unsigned short partition_number_arm_to_dsp; + unsigned short sample_rate; + unsigned short channel_mode; } __attribute__((packed)) audpp_cmd_pcm_intf; /* @@ -446,25 +480,25 @@ typedef struct { #define AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN \ sizeof(audpp_cmd_cfg_object_params_common) -#define AUDPP_CMD_OBJ0_UPDATE 0x0001 +#define AUDPP_CMD_OBJ0_UPDATE 0x8000 #define AUDPP_CMD_OBJ0_DONT_UPDATE 0x0000 -#define AUDPP_CMD_OBJ1_UPDATE 0x0001 +#define AUDPP_CMD_OBJ1_UPDATE 0x8000 #define AUDPP_CMD_OBJ1_DONT_UPDATE 0x0000 -#define AUDPP_CMD_OBJ2_UPDATE 0x0001 +#define AUDPP_CMD_OBJ2_UPDATE 0x8000 #define AUDPP_CMD_OBJ2_DONT_UPDATE 0x0000 -#define AUDPP_CMD_OBJ3_UPDATE 0x0001 +#define AUDPP_CMD_OBJ3_UPDATE 0x8000 #define AUDPP_CMD_OBJ3_DONT_UPDATE 0x0000 -#define AUDPP_CMD_OBJ4_UPDATE 0x0001 +#define AUDPP_CMD_OBJ4_UPDATE 0x8000 #define AUDPP_CMD_OBJ4_DONT_UPDATE 0x0000 -#define AUDPP_CMD_HPCM_UPDATE 0x0001 +#define AUDPP_CMD_HPCM_UPDATE 0x8000 #define AUDPP_CMD_HPCM_DONT_UPDATE 0x0000 -#define AUDPP_CMD_COMMON_CFG_UPDATE 0x0001 +#define AUDPP_CMD_COMMON_CFG_UPDATE 0x8000 #define AUDPP_CMD_COMMON_CFG_DONT_UPDATE 0x0000 typedef struct { @@ -566,7 +600,7 @@ typedef struct { /* - * Command Structure to configure post processing parameters (equalizer) + * Command Structure to configure post processing parameters (equalizer) */ #define AUDPP_CMD_CFG_OBJECT_PARAMS_EQALIZER_LEN \ @@ -687,7 +721,7 @@ typedef struct { /* - * Command Structure to configure post processing parameters (ADRC) + * Command Structure to configure post processing parameters (ADRC) */ #define AUDPP_CMD_CFG_OBJECT_PARAMS_ADRC_LEN \ @@ -725,7 +759,7 @@ typedef struct { } __attribute__((packed)) audpp_cmd_cfg_object_params_spectram; /* - * Command Structure to configure post processing parameters (QConcert) + * Command Structure to configure post processing parameters (QConcert) */ #define AUDPP_CMD_CFG_OBJECT_PARAMS_QCONCERT_LEN \ @@ -758,7 +792,7 @@ typedef struct { } __attribute__((packed)) audpp_cmd_cfg_object_params_qconcert; /* - * Command Structure to configure post processing parameters (Side Chain) + * Command Structure to configure post processing parameters (Side Chain) */ #define AUDPP_CMD_CFG_OBJECT_PARAMS_SIDECHAIN_LEN \ diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppmsg.h index 98f952b7fd89c..e229df3ffd880 100644 --- a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppmsg.h +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audppmsg.h @@ -6,20 +6,20 @@ Q D S P 5 A U D I O P O S T P R O C E S S I N G M S G GENERAL DESCRIPTION - Messages sent by AUDPPTASK to ARM + Messages sent by AUDPPTASK to ARM REFERENCES None EXTERNALIZED FUNCTIONS - None - + None + Copyright(c) 1992 - 2009 by QUALCOMM, Incorporated. 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 @@ -32,7 +32,7 @@ GNU General Public License for more details. This section contains comments describing changes made to this file. Notice that changes are listed in reverse chronological order. - + $Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audppmsg.h#4 $ ===========================================================================*/ @@ -41,7 +41,7 @@ Notice that changes are listed in reverse chronological order. * AUDPPTASK uses audPPuPRlist to send messages to the ARM * Location : MEMA * Buffer Size : 45 - * No of Buffers in a queue : 5 for gaming audio and 1 for other images + * No of Buffers in a queue : 5 for gaming audio and 1 for other images */ /* @@ -253,19 +253,19 @@ typedef struct { sizeof(audpp_msg_pcmdmamissed); typedef struct{ - /* - ** Bit 0 0 = PCM DMA not missed for object 0 - ** 1 = PCM DMA missed for object0 - ** Bit 1 0 = PCM DMA not missed for object 1 - ** 1 = PCM DMA missed for object1 - ** Bit 2 0 = PCM DMA not missed for object 2 - ** 1 = PCM DMA missed for object2 - ** Bit 3 0 = PCM DMA not missed for object 3 - ** 1 = PCM DMA missed for object3 - ** Bit 4 0 = PCM DMA not missed for object 4 - ** 1 = PCM DMA missed for object4 - */ - unsigned short pcmdmamissed; + /* + ** Bit 0 0 = PCM DMA not missed for object 0 + ** 1 = PCM DMA missed for object0 + ** Bit 1 0 = PCM DMA not missed for object 1 + ** 1 = PCM DMA missed for object1 + ** Bit 2 0 = PCM DMA not missed for object 2 + ** 1 = PCM DMA missed for object2 + ** Bit 3 0 = PCM DMA not missed for object 3 + ** 1 = PCM DMA missed for object3 + ** Bit 4 0 = PCM DMA not missed for object 4 + ** 1 = PCM DMA missed for object4 + */ + unsigned short pcmdmamissed; } __attribute__((packed)) audpp_msg_pcmdmamissed; /* @@ -280,10 +280,10 @@ typedef struct{ #define AUDPP_MSG_ENA_DIS 0x0000 typedef struct{ - /* Enabled - 0xffff - ** Disabled - 0 - */ - unsigned short enabled; + /* Enabled - 0xffff + ** Disabled - 0 + */ + unsigned short enabled; } __attribute__((packed)) audpp_msg_cfg_msg; /* @@ -304,5 +304,15 @@ typedef struct { unsigned short hpcm_obj_volume; } __attribute__((packed)) audpp_msg_qreverb_volume; +#define AUDPP_MSG_ROUTING_ACK 0x0009 +#define AUDPP_MSG_ROUTING_ACK_LEN \ + sizeof(struct audpp_msg_routing_ack) + +struct audpp_msg_routing_ack { + unsigned short dec_id; + unsigned short routing_mode; +} __attribute__((packed)); + +#define AUDPP_MSG_FLUSH_ACK 0x000A #endif /* QDSP5AUDPPMSG_H */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreproccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreproccmdi.h index 1beb5de230e33..cd9d59068561d 100644 --- a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreproccmdi.h +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreproccmdi.h @@ -6,7 +6,7 @@ A U D I O P R E P R O C E S S I N G I N T E R N A L C O M M A N D S GENERAL DESCRIPTION - This file contains defintions of format blocks of commands + This file contains defintions of format blocks of commands that are accepted by AUDPREPROC Task REFERENCES @@ -20,7 +20,7 @@ Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. 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 @@ -34,7 +34,7 @@ GNU General Public License for more details. This section contains comments describing changes made to this file. Notice that changes are listed in reverse chronological order. -$Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audpreproccmdi.h#2 $ +$Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audpreproccmdi.h#2 $ ===========================================================================*/ diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreprocmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreprocmsg.h index da8c00e5f6db6..9187f45a586ed 100644 --- a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreprocmsg.h +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audpreprocmsg.h @@ -6,7 +6,7 @@ A U D I O P R E P R O C E S S I N G M E S S A G E S GENERAL DESCRIPTION - This file contains defintions of format blocks of messages + This file contains defintions of format blocks of messages that are rcvd by AUDPREPROC Task REFERENCES @@ -20,7 +20,7 @@ Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. 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 diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audreccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audreccmdi.h index 401152dcb7821..e88bd5d0af25b 100644 --- a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audreccmdi.h +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audreccmdi.h @@ -6,7 +6,7 @@ A U D I O R E C O R D I N T E R N A L C O M M A N D S GENERAL DESCRIPTION - This file contains defintions of format blocks of commands + This file contains defintions of format blocks of commands that are accepted by AUDREC Task REFERENCES @@ -20,7 +20,7 @@ Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. 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 @@ -34,9 +34,9 @@ GNU General Public License for more details. This section contains comments describing changes made to this file. Notice that changes are listed in reverse chronological order. - + $Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audreccmdi.h#3 $ - + ============================================================================*/ /* @@ -53,7 +53,7 @@ Notice that changes are listed in reverse chronological order. */ /* - * Commands on uPAudRecCmdQueue + * Commands on uPAudRecCmdQueue */ /* diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audrecmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audrecmsg.h index fe4ff924e29df..bb6eb5093cf5e 100644 --- a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audrecmsg.h +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5audrecmsg.h @@ -6,7 +6,7 @@ A U D I O R E C O R D M E S S A G E S GENERAL DESCRIPTION - This file contains defintions of format blocks of messages + This file contains defintions of format blocks of messages that are sent by AUDREC Task REFERENCES @@ -20,7 +20,7 @@ Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. 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 @@ -34,7 +34,7 @@ GNU General Public License for more details. This section contains comments describing changes made to this file. Notice that changes are listed in reverse chronological order. - + $Header: //source/qcom/qct/multimedia2/Audio/drivers/QDSP5Driver/QDSP5Interface/main/latest/qdsp5audrecmsg.h#3 $ ============================================================================*/ @@ -86,7 +86,7 @@ typedef struct { #define AUDREC_MSG_AREC_PARAM_TYPE_1 0x0001 typedef struct { - unsigned short type; + unsigned short type; } __attribute__((packed))audrec_msg_cmd_arec_param_cfg_done_msg; @@ -102,7 +102,7 @@ typedef struct { #define AUDREC_MSG_FATAL_ERR_TYPE_1 0x0001 typedef struct { - unsigned short type; + unsigned short type; } __attribute__((packed))audrec_msg_fatal_err_msg; /* @@ -117,7 +117,7 @@ typedef struct { #define AUDREC_MSG_PACKET_READY_TYPE_1 0x0001 typedef struct { - unsigned short type; + unsigned short type; unsigned short pkt_counter_msw; unsigned short pkt_counter_lsw; unsigned short pkt_read_cnt_msw; diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegcmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegcmdi.h index 92d7398ccdb93..d8170f0b6a6e1 100644 --- a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegcmdi.h +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegcmdi.h @@ -6,7 +6,7 @@ J P E G I N T E R N A L C O M M A N D S GENERAL DESCRIPTION - This file contains defintions of format blocks of commands + This file contains defintions of format blocks of commands that are accepted by JPEG Task REFERENCES @@ -20,7 +20,7 @@ Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. 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 @@ -33,10 +33,9 @@ GNU General Public License for more details. This section contains comments describing changes made to this file. Notice that changes are listed in reverse chronological order. - - -$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5jpegcmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ -Revision History: + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5jpegcmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: when who what, where, why -------- --- ---------------------------------------------------------- 06/09/08 sv initial version diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegmsg.h index c9f0e1ab80a3b..d11aa3fbccb65 100644 --- a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegmsg.h +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5jpegmsg.h @@ -6,7 +6,7 @@ J P E G I N T E R N A L M E S S A G E S GENERAL DESCRIPTION - This file contains defintions of format blocks of messages + This file contains defintions of format blocks of messages that are sent by JPEG Task REFERENCES @@ -20,7 +20,7 @@ Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. 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 @@ -33,10 +33,10 @@ GNU General Public License for more details. This section contains comments describing changes made to this file. Notice that changes are listed in reverse chronological order. - -$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5jpegmsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ -Revision History: - + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5jpegmsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + when who what, where, why -------- --- ---------------------------------------------------------- 05/10/08 sv initial version diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmcmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmcmdi.h index a54b4bae8a312..6c76e2c20cf40 100644 --- a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmcmdi.h +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmcmdi.h @@ -6,7 +6,7 @@ L P M I N T E R N A L C O M M A N D S GENERAL DESCRIPTION - This file contains defintions of format blocks of commands + This file contains defintions of format blocks of commands that are accepted by LPM Task REFERENCES @@ -20,7 +20,7 @@ Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. 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 @@ -33,11 +33,11 @@ GNU General Public License for more details. This section contains comments describing changes made to this file. Notice that changes are listed in reverse chronological order. - - -$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5lpmcmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ -Revision History: - + + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5lpmcmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + when who what, where, why -------- --- ---------------------------------------------------------- 06/12/08 sv initial version diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmmsg.h index 4f9f13231490b..3d1039d6ba427 100644 --- a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmmsg.h +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5lpmmsg.h @@ -6,7 +6,7 @@ L P M I N T E R N A L M E S S A G E S GENERAL DESCRIPTION - This file contains defintions of format blocks of commands + This file contains defintions of format blocks of commands that are accepted by LPM Task REFERENCES @@ -20,7 +20,7 @@ Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. 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 @@ -33,10 +33,10 @@ GNU General Public License for more details. This section contains comments describing changes made to this file. Notice that changes are listed in reverse chronological order. - -$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5lpmmsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ -Revision History: - + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5lpmmsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + when who what, where, why -------- --- ---------------------------------------------------------- 06/12/08 sv initial version diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdeccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdeccmdi.h index cb4622adf659e..3a32ee99c6e4d 100644 --- a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdeccmdi.h +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdeccmdi.h @@ -6,7 +6,7 @@ V I D E O D E C O D E R I N T E R N A L C O M M A N D S GENERAL DESCRIPTION - This file contains defintions of format blocks of commands + This file contains defintions of format blocks of commands that are accepted by VIDDEC Task REFERENCES @@ -20,7 +20,7 @@ Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. 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 @@ -33,10 +33,10 @@ GNU General Public License for more details. This section contains comments describing changes made to this file. Notice that changes are listed in reverse chronological order. - -$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vdeccmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ -Revision History: - + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vdeccmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + when who what, where, why -------- --- ---------------------------------------------------------- 05/10/08 ac initial version @@ -178,7 +178,7 @@ typedef struct { /* - * SLICE HEADER PACKET + * SLICE HEADER PACKET * I-Slice and P-Slice */ diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdecmsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdecmsg.h index 74168b5e6c0b2..c1744c1644dd2 100644 --- a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdecmsg.h +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vdecmsg.h @@ -6,7 +6,7 @@ V I D E O D E C O D E R I N T E R N A L M E S S A G E S GENERAL DESCRIPTION - This file contains defintions of format blocks of messages + This file contains defintions of format blocks of messages that are sent by VIDDEC Task REFERENCES @@ -20,7 +20,7 @@ Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. 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 @@ -33,10 +33,10 @@ GNU General Public License for more details. This section contains comments describing changes made to this file. Notice that changes are listed in reverse chronological order. - -$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vdecmsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ -Revision History: - + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vdecmsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + when who what, where, why -------- --- ---------------------------------------------------------- 05/10/08 ac initial version diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5venccmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5venccmdi.h index 4cbe789ac7c96..819544d186da4 100755 --- a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5venccmdi.h +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5venccmdi.h @@ -52,24 +52,24 @@ when who what, where, why sizeof(videnc_cmd_frame_start) typedef struct { - unsigned short cmd_id; - unsigned short frame_info; - unsigned short frame_rho_budget_word_high; - unsigned short frame_rho_budget_word_low; - unsigned short input_luma_addr_high; - unsigned short input_luma_addr_low; - unsigned short input_chroma_addr_high; - unsigned short input_chroma_addr_low; - unsigned short ref_vop_buf_ptr_high; - unsigned short ref_vop_buf_ptr_low; - unsigned short enc_pkt_buf_ptr_high; - unsigned short enc_pkt_buf_ptr_low; - unsigned short enc_pkt_buf_size_high; - unsigned short enc_pkt_buf_size_low; - unsigned short unfilt_recon_vop_buf_ptr_high; - unsigned short unfilt_recon_vop_buf_ptr_low; - unsigned short filt_recon_vop_buf_ptr_high; - unsigned short filt_recon_vop_buf_ptr_low; + unsigned short cmd_id; + unsigned short frame_info; + unsigned short frame_rho_budget_word_high; + unsigned short frame_rho_budget_word_low; + unsigned short input_luma_addr_high; + unsigned short input_luma_addr_low; + unsigned short input_chroma_addr_high; + unsigned short input_chroma_addr_low; + unsigned short ref_vop_buf_ptr_high; + unsigned short ref_vop_buf_ptr_low; + unsigned short enc_pkt_buf_ptr_high; + unsigned short enc_pkt_buf_ptr_low; + unsigned short enc_pkt_buf_size_high; + unsigned short enc_pkt_buf_size_low; + unsigned short unfilt_recon_vop_buf_ptr_high; + unsigned short unfilt_recon_vop_buf_ptr_low; + unsigned short filt_recon_vop_buf_ptr_high; + unsigned short filt_recon_vop_buf_ptr_low; } __attribute__((packed)) videnc_cmd_frame_start; /* @@ -81,10 +81,10 @@ typedef struct { sizeof(videnc_cmd_dis) typedef struct { - unsigned short cmd_id; - unsigned short vfe_out_prev_luma_addr_high; - unsigned short vfe_out_prev_luma_addr_low; - unsigned short stabilization_info; + unsigned short cmd_id; + unsigned short vfe_out_prev_luma_addr_high; + unsigned short vfe_out_prev_luma_addr_low; + unsigned short stabilization_info; } __attribute__((packed)) videnc_cmd_dis; /* @@ -96,11 +96,11 @@ typedef struct { sizeof(videnc_cmd_cfg) typedef struct { - unsigned short cmd_id; - unsigned short cfg_info_0; - unsigned short cfg_info_1; - unsigned short four_mv_threshold; - unsigned short ise_fse_mv_cost_fac; + unsigned short cmd_id; + unsigned short cfg_info_0; + unsigned short cfg_info_1; + unsigned short four_mv_threshold; + unsigned short ise_fse_mv_cost_fac; unsigned short venc_frame_dim; unsigned short venc_DM_partition; } __attribute__((packed)) videnc_cmd_cfg; @@ -126,7 +126,7 @@ typedef struct { sizeof(videnc_cmd_idle) typedef struct { - unsigned short cmd_id; + unsigned short cmd_id; } __attribute__((packed)) videnc_cmd_idle; /* @@ -138,7 +138,7 @@ typedef struct { sizeof(videnc_cmd_status_query) typedef struct { - unsigned short cmd_id; + unsigned short cmd_id; } __attribute__((packed)) videnc_cmd_status_query; /* @@ -150,7 +150,7 @@ typedef struct { sizeof(videnc_cmd_rc_cfg) typedef struct { - unsigned short cmd_id; + unsigned short cmd_id; unsigned short max_frame_qp_delta; unsigned short max_min_frame_qp; } __attribute__((packed)) videnc_cmd_rc_cfg; @@ -164,7 +164,7 @@ typedef struct { sizeof(videnc_cmd_intra_refresh) typedef struct { - unsigned short cmd_id; + unsigned short cmd_id; unsigned short num_mb_refresh; unsigned short mb_index[15]; } __attribute__((packed)) videnc_cmd_intra_refresh; @@ -176,22 +176,22 @@ typedef struct { sizeof(videnc_cmd_digital_zoom) typedef struct { - unsigned short cmd_id; - unsigned short digital_zoom_en; - unsigned short luma_frame_shift_X; - unsigned short luma_frame_shift_Y; - unsigned short up_ip_luma_rows; - unsigned short up_ip_luma_cols; - unsigned short up_ip_chroma_rows; - unsigned short up_ip_chroma_cols; - unsigned short luma_ph_incr_V_low; - unsigned short luma_ph_incr_V_high; - unsigned short luma_ph_incr_H_low; - unsigned short luma_ph_incr_H_high; - unsigned short chroma_ph_incr_V_low; - unsigned short chroma_ph_incr_V_high; - unsigned short chroma_ph_incr_H_low; - unsigned short chroma_ph_incr_H_high; + unsigned short cmd_id; + unsigned short digital_zoom_en; + unsigned short luma_frame_shift_X; + unsigned short luma_frame_shift_Y; + unsigned short up_ip_luma_rows; + unsigned short up_ip_luma_cols; + unsigned short up_ip_chroma_rows; + unsigned short up_ip_chroma_cols; + unsigned short luma_ph_incr_V_low; + unsigned short luma_ph_incr_V_high; + unsigned short luma_ph_incr_H_low; + unsigned short luma_ph_incr_H_high; + unsigned short chroma_ph_incr_V_low; + unsigned short chroma_ph_incr_V_high; + unsigned short chroma_ph_incr_H_low; + unsigned short chroma_ph_incr_H_high; } __attribute__((packed)) videnc_cmd_digital_zoom; /* @@ -202,10 +202,10 @@ typedef struct { sizeof(videnc_cmd_dis_cfg) typedef struct { - unsigned short cmd_id; - unsigned short image_stab_subf_start_row_col; - unsigned short image_stab_subf_dim; - unsigned short image_stab_info_0; + unsigned short cmd_id; + unsigned short image_stab_subf_start_row_col; + unsigned short image_stab_subf_dim; + unsigned short image_stab_info_0; } __attribute__((packed)) videnc_cmd_dis_cfg; diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfecmdi.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfecmdi.h index 12c0937670727..f76d4e4263e93 100644 --- a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfecmdi.h +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfecmdi.h @@ -6,7 +6,7 @@ V F E I N T E R N A L C O M M A N D S GENERAL DESCRIPTION - This file contains defintions of format blocks of commands + This file contains defintions of format blocks of commands that are accepted by VFE Task REFERENCES @@ -20,7 +20,7 @@ Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. 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 @@ -33,10 +33,10 @@ GNU General Public License for more details. This section contains comments describing changes made to this file. Notice that changes are listed in reverse chronological order. - -$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vfecmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ -Revision History: - + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vfecmdi.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + when who what, where, why -------- --- ---------------------------------------------------------- 06/12/08 sv initial version diff --git a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfemsg.h b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfemsg.h index 98d0aa9f5ca96..0053cfb65ba18 100644 --- a/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfemsg.h +++ b/arch/arm/mach-msm/include/mach/qdsp5/qdsp5vfemsg.h @@ -6,7 +6,7 @@ V F E I N T E R N A L M E S S A G E S GENERAL DESCRIPTION - This file contains defintions of format blocks of commands + This file contains defintions of format blocks of commands that are sent by VFE Task REFERENCES @@ -20,7 +20,7 @@ Copyright(c) 1992 - 2008 by QUALCOMM, Incorporated. 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 @@ -33,10 +33,10 @@ GNU General Public License for more details. This section contains comments describing changes made to this file. Notice that changes are listed in reverse chronological order. - -$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vfemsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ -Revision History: - + +$Header: //source/qcom/qct/multimedia2/AdspSvc/7XXX/qdsp5cmd/video/qdsp5vfemsg.h#2 $ $DateTime: 2008/07/30 10:50:23 $ $Author: pavanr $ +Revision History: + when who what, where, why -------- --- ---------------------------------------------------------- 06/12/08 sv initial version @@ -101,7 +101,7 @@ typedef struct { /* - * Message to notify ARM that illegal cmd was received and + * Message to notify ARM that illegal cmd was received and * system is in the IDLE state */ @@ -128,7 +128,7 @@ typedef struct { unsigned int black_level_odd_col; unsigned int defect_pixels_detected; unsigned int asf_max_edge; -} __attribute__((packed)) vfe_msg_op1; +} __attribute__((packed)) vfe_msg_op1; /* @@ -145,7 +145,7 @@ typedef struct { unsigned int black_level_odd_col; unsigned int defect_pixels_detected; unsigned int asf_max_edge; -} __attribute__((packed)) vfe_msg_op2; +} __attribute__((packed)) vfe_msg_op2; /* diff --git a/arch/arm/mach-msm/msm_vibrator.c b/arch/arm/mach-msm/msm_vibrator.c index 5abd436361ee5..21b024b544c5a 100644 --- a/arch/arm/mach-msm/msm_vibrator.c +++ b/arch/arm/mach-msm/msm_vibrator.c @@ -26,7 +26,7 @@ #if (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225) #define PM_LIBVERS 0xfb837d0b #else -#define PM_LIBVERS 0x10001 +#define PM_LIBVERS MSM_RPC_VERS(1,1) #endif #define HTC_PROCEDURE_SET_VIB_ON_OFF 21 diff --git a/arch/arm/mach-msm/qdsp5/Makefile b/arch/arm/mach-msm/qdsp5/Makefile index 30343066b13c0..cd24c7aa90dd3 100644 --- a/arch/arm/mach-msm/qdsp5/Makefile +++ b/arch/arm/mach-msm/qdsp5/Makefile @@ -1,9 +1,11 @@ +ifeq ($(CONFIG_MSM_AMSS_VERSION_6350),y) +obj-y += adsp_new.o adsp_info.o +obj-y += audio_evrc.o audio_qcelp.o audio_amrnb.o audio_aac.o +else +obj-y += adsp.o adsp_6225.o +endif -obj-$(CONFIG_MSM_AMSS_VERSION_6210) += adsp_6210.o -obj-$(CONFIG_MSM_AMSS_VERSION_6220) += adsp_6220.o -obj-$(CONFIG_MSM_AMSS_VERSION_6225) += adsp_6225.o - -obj-y += adsp.o adsp_driver.o +obj-y += adsp_driver.o obj-y += adsp_video_verify_cmd.o obj-y += adsp_videoenc_verify_cmd.o obj-y += adsp_jpeg_verify_cmd.o adsp_jpeg_patch_event.o diff --git a/arch/arm/mach-msm/qdsp5/adsp.c b/arch/arm/mach-msm/qdsp5/adsp.c index 9c521b3b60d36..32b69efedc3f8 100644 --- a/arch/arm/mach-msm/qdsp5/adsp.c +++ b/arch/arm/mach-msm/qdsp5/adsp.c @@ -74,6 +74,7 @@ uint32_t adsp_get_module(struct adsp_info *info, uint32_t task) uint32_t adsp_get_queue_offset(struct adsp_info *info, uint32_t queue_id) { + BUG_ON(current_image == -1UL); return info->queue_offset[current_image][queue_id]; } @@ -365,60 +366,61 @@ int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, ret_status = -EIO; pr_err("adsp: failed to write queue %x, retry\n", dsp_q_addr); goto fail; + } + + /* Ctrl word status bits are 00, no error in the ctrl word */ + + /* Get the DSP buffer address */ + dsp_addr = (ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M) + + (uint32_t)MSM_AD5_BASE; + + if (dsp_addr < (uint32_t)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) { + uint16_t *buf_ptr = (uint16_t *) cmd_buf; + uint16_t *dsp_addr16 = (uint16_t *)dsp_addr; + cmd_size /= sizeof(uint16_t); + + /* Save the command ID */ + cmd_id = (uint32_t) buf_ptr[0]; + + /* Copy the command to DSP memory */ + cmd_size++; + while (--cmd_size) + *dsp_addr16++ = *buf_ptr++; } else { - /* No error */ - /* Get the DSP buffer address */ - dsp_addr = (ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M) + - (uint32_t)MSM_AD5_BASE; - - if (dsp_addr < (uint32_t)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) { - uint16_t *buf_ptr = (uint16_t *) cmd_buf; - uint16_t *dsp_addr16 = (uint16_t *)dsp_addr; - cmd_size /= sizeof(uint16_t); - - /* Save the command ID */ - cmd_id = (uint32_t) buf_ptr[0]; - - /* Copy the command to DSP memory */ - cmd_size++; - while (--cmd_size) - *dsp_addr16++ = *buf_ptr++; - } else { - uint32_t *buf_ptr = (uint32_t *) cmd_buf; - uint32_t *dsp_addr32 = (uint32_t *)dsp_addr; - cmd_size /= sizeof(uint32_t); + uint32_t *buf_ptr = (uint32_t *) cmd_buf; + uint32_t *dsp_addr32 = (uint32_t *)dsp_addr; + cmd_size /= sizeof(uint32_t); - /* Save the command ID */ - cmd_id = buf_ptr[0]; + /* Save the command ID */ + cmd_id = buf_ptr[0]; - cmd_size++; - while (--cmd_size) - *dsp_addr32++ = *buf_ptr++; - } + cmd_size++; + while (--cmd_size) + *dsp_addr32++ = *buf_ptr++; + } - /* Set the mutex bits */ - ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M); - ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V; + /* Set the mutex bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M); + ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V; - /* Set the command bits to write done */ - ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M); - ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V; + /* Set the command bits to write done */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M); + ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V; - /* Set the queue address bits */ - ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M); - ctrl_word |= dsp_q_addr; + /* Set the queue address bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M); + ctrl_word |= dsp_q_addr; - writel(ctrl_word, info->write_ctrl); + writel(ctrl_word, info->write_ctrl); - /* Generate an interrupt to the DSP. It does not respond with - * an interrupt, and we do not need to wait for it to - * acknowledge, because it will hold the mutex lock until it's - * ready to receive more commands again. - */ - writel(1, info->send_irq); + /* Generate an interrupt to the DSP. It does not respond with + * an interrupt, and we do not need to wait for it to + * acknowledge, because it will hold the mutex lock until it's + * ready to receive more commands again. + */ + writel(1, info->send_irq); - module->num_commands++; - } /* Ctrl word status bits were 00, no error in the ctrl word */ + module->num_commands++; fail: spin_unlock_irqrestore(&adsp_cmd_lock, flags); @@ -508,7 +510,7 @@ static int adsp_rpc_thread(void *data) { void *buffer; struct rpc_request_hdr *req; - int rc, exit = 0; + int rc; do { rc = msm_rpc_read(rpc_cb_server_client, &buffer, -1, -1); @@ -541,7 +543,7 @@ static int adsp_rpc_thread(void *data) bad_rpc: pr_err("adsp: bogus rpc from modem\n"); kfree(buffer); - } while (!exit); + } while (1); do_exit(0); } diff --git a/arch/arm/mach-msm/qdsp5/adsp.h b/arch/arm/mach-msm/qdsp5/adsp.h index 2b67ef47d3c71..e2a12f8ff9979 100644 --- a/arch/arm/mach-msm/qdsp5/adsp.h +++ b/arch/arm/mach-msm/qdsp5/adsp.h @@ -18,6 +18,10 @@ #ifndef _ARCH_ARM_MACH_MSM_ADSP_H #define _ARCH_ARM_MACH_MSM_ADSP_H +#if CONFIG_MSM_AMSS_VERSION>=6350 +#include "adsp_new.h" +#else + #include #include #include @@ -67,6 +71,7 @@ struct adsp_module_info { }; #define ADSP_EVENT_MAX_SIZE 496 +#define EVENT_MSG_ID ((uint16_t)~0) struct adsp_event { struct list_head list; @@ -305,4 +310,5 @@ extern struct msm_adsp_module *find_adsp_module_by_id(struct adsp_info *info, /* Base address of DSP and DSP hardware registers */ #define QDSP_RAMC_OFFSET 0x400000 +#endif /* CONFIG_MSM_AMSS_VERSION>=6350 */ #endif diff --git a/arch/arm/mach-msm/qdsp5/adsp_driver.c b/arch/arm/mach-msm/qdsp5/adsp_driver.c index a70cec86b9415..577cffd2da324 100644 --- a/arch/arm/mach-msm/qdsp5/adsp_driver.c +++ b/arch/arm/mach-msm/qdsp5/adsp_driver.c @@ -393,9 +393,18 @@ static long adsp_get_event(struct adsp_device *adev, void __user *arg) rc = -ETOOSMALL; goto end; } - if (copy_to_user((void *)(evt.data), data->data.msg16, data->size)) { - rc = -EFAULT; - goto end; + if (data->msg_id != EVENT_MSG_ID) { + if (copy_to_user((void *)(evt.data), data->data.msg16, + data->size)) { + rc = -EFAULT; + goto end; + } + } else { + if (copy_to_user((void *)(evt.data), data->data.msg32, + data->size)) { + rc = -EFAULT; + goto end; + } } evt.type = data->type; /* 0 --> from aDSP, 1 --> from ARM9 */ @@ -433,6 +442,15 @@ static long adsp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case ADSP_IOCTL_GET_EVENT: return adsp_get_event(adev, (void __user *) arg); + case ADSP_IOCTL_SET_CLKRATE: { +#if CONFIG_MSM_AMSS_VERSION==6350 + unsigned long clk_rate; + if (copy_from_user(&clk_rate, (void *) arg, sizeof(clk_rate))) + return -EFAULT; + return adsp_set_clkrate(adev->module, clk_rate); +#endif + } + case ADSP_IOCTL_REGISTER_PMEM: { struct adsp_pmem_info info; if (copy_from_user(&info, (void *) arg, sizeof(info))) @@ -495,12 +513,20 @@ static void adsp_event(void *driver_data, unsigned id, size_t len, return; } - event->type = 0; - event->is16 = 0; - event->msg_id = id; - event->size = len; + if (id != EVENT_MSG_ID) { + event->type = 0; + event->is16 = 0; + event->msg_id = id; + event->size = len; - getevent(event->data.msg16, len); + getevent(event->data.msg16, len); + } else { + event->type = 1; + event->is16 = 1; + event->msg_id = id; + event->size = len; + getevent(event->data.msg32, len); + } spin_lock_irqsave(&adev->event_queue_lock, flags); list_add_tail(&event->list, &adev->event_queue); diff --git a/arch/arm/mach-msm/qdsp5/adsp_info.c b/arch/arm/mach-msm/qdsp5/adsp_info.c new file mode 100644 index 0000000000000..b9c77d20b5c4a --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_info.c @@ -0,0 +1,121 @@ +/* arch/arm/mach-msm/adsp_info.c + * + * Copyright (c) 2008 QUALCOMM Incorporated. + * Copyright (c) 2008 QUALCOMM USA, 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 "adsp.h" + +/* Firmware modules */ +#define QDSP_MODULE_KERNEL 0x0106dd4e +#define QDSP_MODULE_AFETASK 0x0106dd6f +#define QDSP_MODULE_AUDPLAY0TASK 0x0106dd70 +#define QDSP_MODULE_AUDPLAY1TASK 0x0106dd71 +#define QDSP_MODULE_AUDPPTASK 0x0106dd72 +#define QDSP_MODULE_VIDEOTASK 0x0106dd73 +#define QDSP_MODULE_VIDEO_AAC_VOC 0x0106dd74 +#define QDSP_MODULE_PCM_DEC 0x0106dd75 +#define QDSP_MODULE_AUDIO_DEC_MP3 0x0106dd76 +#define QDSP_MODULE_AUDIO_DEC_AAC 0x0106dd77 +#define QDSP_MODULE_AUDIO_DEC_WMA 0x0106dd78 +#define QDSP_MODULE_HOSTPCM 0x0106dd79 +#define QDSP_MODULE_DTMF 0x0106dd7a +#define QDSP_MODULE_AUDRECTASK 0x0106dd7b +#define QDSP_MODULE_AUDPREPROCTASK 0x0106dd7c +#define QDSP_MODULE_SBC_ENC 0x0106dd7d +#define QDSP_MODULE_VOC_UMTS 0x0106dd9a +#define QDSP_MODULE_VOC_CDMA 0x0106dd98 +#define QDSP_MODULE_VOC_PCM 0x0106dd7f +#define QDSP_MODULE_VOCENCTASK 0x0106dd80 +#define QDSP_MODULE_VOCDECTASK 0x0106dd81 +#define QDSP_MODULE_VOICEPROCTASK 0x0106dd82 +#define QDSP_MODULE_VIDEOENCTASK 0x0106dd83 +#define QDSP_MODULE_VFETASK 0x0106dd84 +#define QDSP_MODULE_WAV_ENC 0x0106dd85 +#define QDSP_MODULE_AACLC_ENC 0x0106dd86 +#define QDSP_MODULE_VIDEO_AMR 0x0106dd87 +#define QDSP_MODULE_VOC_AMR 0x0106dd88 +#define QDSP_MODULE_VOC_EVRC 0x0106dd89 +#define QDSP_MODULE_VOC_13K 0x0106dd8a +#define QDSP_MODULE_VOC_FGV 0x0106dd8b +#define QDSP_MODULE_DIAGTASK 0x0106dd8c +#define QDSP_MODULE_JPEGTASK 0x0106dd8d +#define QDSP_MODULE_LPMTASK 0x0106dd8e +#define QDSP_MODULE_QCAMTASK 0x0106dd8f +#define QDSP_MODULE_MODMATHTASK 0x0106dd90 +#define QDSP_MODULE_AUDPLAY2TASK 0x0106dd91 +#define QDSP_MODULE_AUDPLAY3TASK 0x0106dd92 +#define QDSP_MODULE_AUDPLAY4TASK 0x0106dd93 +#define QDSP_MODULE_GRAPHICSTASK 0x0106dd94 +#define QDSP_MODULE_MIDI 0x0106dd95 +#define QDSP_MODULE_GAUDIO 0x0106dd96 +#define QDSP_MODULE_VDEC_LP_MODE 0x0106dd97 +#define QDSP_MODULE_MAX 0x7fffffff + + /* DO NOT USE: Force this enum to be a 32bit type to improve speed */ +#define QDSP_MODULE_32BIT_DUMMY 0x10000 + +static uint32_t *qdsp_task_to_module[IMG_MAX]; +static uint32_t *qdsp_queue_offset_table[IMG_MAX]; + +#define QDSP_MODULE(n, clkname, clkrate, verify_cmd_func, patch_event_func) \ + { .name = #n, .pdev_name = "adsp_" #n, .id = QDSP_MODULE_##n, \ + .clk_name = clkname, .clk_rate = clkrate, \ + .verify_cmd = verify_cmd_func, .patch_event = patch_event_func } + +static struct adsp_module_info module_info[] = { + QDSP_MODULE(AUDPLAY0TASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDPPTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDRECTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(AUDPREPROCTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(VFETASK, "vfe_clk", 0, adsp_vfe_verify_cmd, + adsp_vfe_patch_event), + QDSP_MODULE(QCAMTASK, NULL, 0, NULL, NULL), + QDSP_MODULE(LPMTASK, NULL, 0, adsp_lpm_verify_cmd, NULL), + QDSP_MODULE(JPEGTASK, "vdc_clk", 96000000, adsp_jpeg_verify_cmd, + adsp_jpeg_patch_event), + QDSP_MODULE(VIDEOTASK, "vdc_clk", 96000000, + adsp_video_verify_cmd, NULL), + QDSP_MODULE(VDEC_LP_MODE, NULL, 0, NULL, NULL), + QDSP_MODULE(VIDEOENCTASK, "vdc_clk", 96000000, + adsp_videoenc_verify_cmd, NULL), +}; + +int adsp_init_info(struct adsp_info *info) +{ + uint32_t img_num; + + info->send_irq = 0x00c00200; + info->read_ctrl = 0x00400038; + info->write_ctrl = 0x00400034; + + info->max_msg16_size = 193; + info->max_msg32_size = 8; + for (img_num = 0; img_num < IMG_MAX; img_num++) + qdsp_queue_offset_table[img_num] = + &info->init_info_ptr->queue_offsets[img_num][0]; + + for (img_num = 0; img_num < IMG_MAX; img_num++) + qdsp_task_to_module[img_num] = + &info->init_info_ptr->task_to_module_tbl[img_num][0]; + info->max_task_id = 30; + info->max_module_id = QDSP_MODULE_MAX - 1; + info->max_queue_id = QDSP_MAX_NUM_QUEUES; + info->max_image_id = 2; + info->queue_offset = qdsp_queue_offset_table; + info->task_to_module = qdsp_task_to_module; + + info->module_count = ARRAY_SIZE(module_info); + info->module = module_info; + return 0; +} diff --git a/arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c b/arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c index f770c141a4140..b33eba25569c7 100644 --- a/arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c +++ b/arch/arm/mach-msm/qdsp5/adsp_jpeg_verify_cmd.c @@ -29,7 +29,7 @@ static inline void get_sizes(jpeg_cmd_enc_cfg *cmd, uint32_t *luma_size, fmt = cmd->process_cfg & JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_M; luma_width = (cmd->ip_size_cfg & JPEG_CMD_IP_SIZE_CFG_LUMA_WIDTH_M) >> 16; - luma_height = cmd->ip_size_cfg & JPEG_CMD_FRAG_SIZE_LUMA_HEIGHT_M; + luma_height = cmd->frag_cfg & JPEG_CMD_FRAG_SIZE_LUMA_HEIGHT_M; *luma_size = luma_width * luma_height; if (fmt == JPEG_CMD_ENC_PROCESS_CFG_IP_DATA_FORMAT_H2V2) *chroma_size = *luma_size/2; @@ -52,6 +52,7 @@ static inline int verify_jpeg_cmd_enc_cfg(struct msm_adsp_module *module, get_sizes(cmd, &luma_size, &chroma_size); num_frags = (cmd->process_cfg >> 10) & 0xf; + num_frags = ((num_frags == 1) ? num_frags : num_frags * 2); for (i = 0; i < num_frags; i += 2) { if (adsp_pmem_fixup(module, (void **)(&cmd->frag_cfg_part[i]), luma_size) || adsp_pmem_fixup(module, (void **)(&cmd->frag_cfg_part[i+1]), chroma_size)) diff --git a/arch/arm/mach-msm/qdsp5/adsp_new.c b/arch/arm/mach-msm/qdsp5/adsp_new.c new file mode 100644 index 0000000000000..fb7981b8dabf0 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_new.c @@ -0,0 +1,1105 @@ +/* arch/arm/mach-msm/qdsp5/adsp.c + * + * Register/Interrupt access for userspace aDSP library. + * + * Copyright (c) 2008 QUALCOMM Incorporated + * Copyright (C) 2008 Google, Inc. + * Author: Iliyan Malchev + * + * 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. + * + */ + +/* TODO: + * - move shareable rpc code outside of adsp.c + * - general solution for virt->phys patchup + * - queue IDs should be relative to modules + * - disallow access to non-associated queues + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct wake_lock adsp_wake_lock; +static inline void prevent_suspend(void) +{ + wake_lock(&adsp_wake_lock); +} +static inline void allow_suspend(void) +{ + wake_unlock(&adsp_wake_lock); +} + +#include +#include +#include "adsp.h" + +#define INT_ADSP INT_ADSP_A9_A11 + +static struct adsp_info adsp_info; +static struct msm_rpc_endpoint *rpc_cb_server_client; +static struct msm_adsp_module *adsp_modules; +static int adsp_open_count; + +#define RPC_ADSP_RTOS_ATOM_PROG 0x3000000a +#define RPC_ADSP_RTOS_ATOM_VERS MSM_RPC_VERS(1,0) +#define RPC_ADSP_RTOS_MTOA_PROG 0x3000000b +/* WARNING: this must be the actual version: the modem can't figure out that + * this is a backwards-compatible server. */ +#define RPC_ADSP_RTOS_MTOA_VERS MSM_RPC_VERS(2,1) +static DEFINE_MUTEX(adsp_open_lock); + +/* protect interactions with the ADSP command/message queue */ +static spinlock_t adsp_cmd_lock; + +static uint32_t current_image = -1; + +void adsp_set_image(struct adsp_info *info, uint32_t image) +{ + current_image = image; +} + +/* + * Checks whether the module_id is available in the + * module_entries table.If module_id is available returns `0`. + * If module_id is not available returns `-ENXIO`. + */ +static int32_t adsp_validate_module(uint32_t module_id) +{ + uint32_t *ptr; + uint32_t module_index; + uint32_t num_mod_entries; + + ptr = adsp_info.init_info_ptr->module_entries; + num_mod_entries = adsp_info.init_info_ptr->module_table_size; + + for (module_index = 0; module_index < num_mod_entries; module_index++) + if (module_id == ptr[module_index]) + return 0; + + return -ENXIO; +} + +uint32_t adsp_get_module(struct adsp_info *info, uint32_t task) +{ + BUG_ON(current_image == -1UL); + return info->task_to_module[current_image][task]; +} + +uint32_t adsp_get_queue_offset(struct adsp_info *info, uint32_t queue_id) +{ + return info->queue_offset[current_image][queue_id]; +} + +static int rpc_adsp_rtos_app_to_modem(uint32_t cmd, uint32_t module, + struct msm_adsp_module *adsp_module) +{ + int rc; + struct rpc_adsp_rtos_app_to_modem_args_t rpc_req; + struct rpc_reply_hdr *rpc_rsp; + + msm_rpc_setup_req(&rpc_req.hdr, + RPC_ADSP_RTOS_ATOM_PROG, + msm_rpc_get_vers(adsp_module->rpc_client), + RPC_ADSP_RTOS_APP_TO_MODEM_PROC); + + rpc_req.gotit = cpu_to_be32(1); + rpc_req.cmd = cpu_to_be32(cmd); + rpc_req.proc_id = cpu_to_be32(RPC_ADSP_RTOS_PROC_APPS); + rpc_req.module = cpu_to_be32(module); + rc = msm_rpc_write(adsp_module->rpc_client, &rpc_req, sizeof(rpc_req)); + if (rc < 0) { + pr_err("adsp: could not send RPC request: %d\n", rc); + return rc; + } + + rc = msm_rpc_read(adsp_module->rpc_client, + (void **)&rpc_rsp, -1, (5*HZ)); + if (rc < 0) { + pr_err("adsp: error receiving RPC reply: %d (%d)\n", + rc, -ERESTARTSYS); + return rc; + } + + if (be32_to_cpu(rpc_rsp->reply_stat) != RPCMSG_REPLYSTAT_ACCEPTED) { + pr_err("adsp: RPC call was denied!\n"); + kfree(rpc_rsp); + return -EPERM; + } + + if (be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat) != + RPC_ACCEPTSTAT_SUCCESS) { + pr_err("adsp error: RPC call was not successful (%d)\n", + be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat)); + kfree(rpc_rsp); + return -EINVAL; + } + + kfree(rpc_rsp); + return 0; +} + +static int get_module_index(uint32_t id) +{ + int mod_idx; + for (mod_idx = 0; mod_idx < adsp_info.module_count; mod_idx++) + if (adsp_info.module[mod_idx].id == id) + return mod_idx; + + return -ENXIO; +} + +static struct msm_adsp_module *find_adsp_module_by_id( + struct adsp_info *info, uint32_t id) +{ + int mod_idx; + + if (id > info->max_module_id) { + return NULL; + } else { + mod_idx = get_module_index(id); + if (mod_idx < 0) + return NULL; + return info->id_to_module[mod_idx]; + } +} + +static struct msm_adsp_module *find_adsp_module_by_name( + struct adsp_info *info, const char *name) +{ + unsigned n; + for (n = 0; n < info->module_count; n++) + if (!strcmp(name, adsp_modules[n].name)) + return adsp_modules + n; + return NULL; +} + +static int adsp_rpc_init(struct msm_adsp_module *adsp_module) +{ + /* remove the original connect once compatible support is complete */ + adsp_module->rpc_client = msm_rpc_connect( + RPC_ADSP_RTOS_ATOM_PROG, + RPC_ADSP_RTOS_ATOM_VERS, + MSM_RPC_UNINTERRUPTIBLE); + + if (IS_ERR(adsp_module->rpc_client)) { + int rc = PTR_ERR(adsp_module->rpc_client); + adsp_module->rpc_client = 0; + pr_err("adsp: could not open rpc client: %d\n", rc); + return rc; + } + + return 0; +} + +/* + * Send RPC_ADSP_RTOS_CMD_GET_INIT_INFO cmd to ARM9 and get + * queue offsets and module entries (init info) as part of the event. + */ +static void msm_get_init_info(void) +{ + int rc; + struct rpc_adsp_rtos_app_to_modem_args_t rpc_req; + + adsp_info.init_info_rpc_client = msm_rpc_connect( + RPC_ADSP_RTOS_ATOM_PROG, + RPC_ADSP_RTOS_ATOM_VERS, + MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(adsp_info.init_info_rpc_client)) { + rc = PTR_ERR(adsp_info.init_info_rpc_client); + adsp_info.init_info_rpc_client = 0; + pr_err("adsp: could not open rpc client: %d\n", rc); + return; + } + + msm_rpc_setup_req(&rpc_req.hdr, + RPC_ADSP_RTOS_ATOM_PROG, + msm_rpc_get_vers(adsp_info.init_info_rpc_client), + RPC_ADSP_RTOS_APP_TO_MODEM_PROC); + + rpc_req.gotit = cpu_to_be32(1); + rpc_req.cmd = cpu_to_be32(RPC_ADSP_RTOS_CMD_GET_INIT_INFO); + rpc_req.proc_id = cpu_to_be32(RPC_ADSP_RTOS_PROC_APPS); + rpc_req.module = 0; + + rc = msm_rpc_write(adsp_info.init_info_rpc_client, + &rpc_req, sizeof(rpc_req)); + if (rc < 0) + pr_err("adsp: could not send RPC request: %d\n", rc); +} + +int msm_adsp_get(const char *name, struct msm_adsp_module **out, + struct msm_adsp_ops *ops, void *driver_data) +{ + struct msm_adsp_module *module; + int rc = 0; + static uint32_t init_info_cmd_sent; + + if (!init_info_cmd_sent) { + msm_get_init_info(); + init_waitqueue_head(&adsp_info.init_info_wait); + rc = wait_event_timeout(adsp_info.init_info_wait, + adsp_info.init_info_state == ADSP_STATE_INIT_INFO, + 5 * HZ); + if (!rc) { + pr_info("adsp: INIT_INFO failed\n"); + return -ETIMEDOUT; + } + init_info_cmd_sent++; + } + + module = find_adsp_module_by_name(&adsp_info, name); + if (!module) + return -ENODEV; + + mutex_lock(&module->lock); + pr_info("adsp: opening module %s\n", module->name); + if (module->open_count++ == 0 && module->clk) + clk_enable(module->clk); + + mutex_lock(&adsp_open_lock); + if (adsp_open_count++ == 0) { + enable_irq(INT_ADSP); + prevent_suspend(); + } + mutex_unlock(&adsp_open_lock); + + if (module->ops) { + rc = -EBUSY; + goto done; + } + + rc = adsp_rpc_init(module); + if (rc) + goto done; + + module->ops = ops; + module->driver_data = driver_data; + *out = module; + rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_REGISTER_APP, + module->id, module); + if (rc) { + module->ops = NULL; + module->driver_data = NULL; + *out = NULL; + pr_err("adsp: REGISTER_APP failed\n"); + goto done; + } + + pr_info("adsp: module %s has been registered\n", module->name); + +done: + mutex_lock(&adsp_open_lock); + if (rc && --adsp_open_count == 0) { + disable_irq(INT_ADSP); + allow_suspend(); + } + if (rc && --module->open_count == 0 && module->clk) + clk_disable(module->clk); + mutex_unlock(&adsp_open_lock); + mutex_unlock(&module->lock); + return rc; +} +EXPORT_SYMBOL(msm_adsp_get); + +static int msm_adsp_disable_locked(struct msm_adsp_module *module); + +void msm_adsp_put(struct msm_adsp_module *module) +{ + unsigned long flags; + + mutex_lock(&module->lock); + if (--module->open_count == 0 && module->clk) + clk_disable(module->clk); + if (module->ops) { + pr_info("adsp: closing module %s\n", module->name); + + /* lock to ensure a dsp event cannot be delivered + * during or after removal of the ops and driver_data + */ + spin_lock_irqsave(&adsp_cmd_lock, flags); + module->ops = NULL; + module->driver_data = NULL; + spin_unlock_irqrestore(&adsp_cmd_lock, flags); + + if (module->state != ADSP_STATE_DISABLED) { + pr_info("adsp: disabling module %s\n", module->name); + msm_adsp_disable_locked(module); + } + + msm_rpc_close(module->rpc_client); + module->rpc_client = 0; + if (--adsp_open_count == 0) { + disable_irq(INT_ADSP); + allow_suspend(); + pr_info("adsp: disable interrupt\n"); + } + } else { + pr_info("adsp: module %s is already closed\n", module->name); + } + mutex_unlock(&module->lock); +} +EXPORT_SYMBOL(msm_adsp_put); + +/* this should be common code with rpc_servers.c */ +static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client, + uint32_t xid, uint32_t accept_status) +{ + int rc = 0; + uint8_t reply_buf[sizeof(struct rpc_reply_hdr)]; + struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf; + + reply->xid = cpu_to_be32(xid); + reply->type = cpu_to_be32(1); /* reply */ + reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); + + reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status); + reply->data.acc_hdr.verf_flavor = 0; + reply->data.acc_hdr.verf_length = 0; + + rc = msm_rpc_write(rpc_cb_server_client, reply_buf, sizeof(reply_buf)); + if (rc < 0) + pr_err("adsp: could not write RPC response: %d\n", rc); + return rc; +} + +int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, + void *cmd_buf, size_t cmd_size) +{ + uint32_t ctrl_word; + uint32_t dsp_q_addr; + uint32_t dsp_addr; + uint32_t cmd_id = 0; + int cnt = 0; + int ret_status = 0; + unsigned long flags; + struct adsp_info *info = module->info; + + spin_lock_irqsave(&adsp_cmd_lock, flags); + + if (module->state != ADSP_STATE_ENABLED) { + spin_unlock_irqrestore(&adsp_cmd_lock, flags); + pr_err("adsp: module %s not enabled before write\n", + module->name); + return -ENODEV; + } + if (adsp_validate_module(module->id)) { + spin_unlock_irqrestore(&adsp_cmd_lock, flags); + pr_info("adsp: module id validation failed %s %d\n", + module->name, module->id); + return -ENXIO; + } + dsp_q_addr = adsp_get_queue_offset(info, dsp_queue_addr); + dsp_q_addr &= ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M; + + /* Poll until the ADSP is ready to accept a command. + * Wait for 100us, return error if it's not responding. + * If this returns an error, we need to disable ALL modules and + * then retry. + */ + while (((ctrl_word = readl(info->write_ctrl)) & + ADSP_RTOS_WRITE_CTRL_WORD_READY_M) != + ADSP_RTOS_WRITE_CTRL_WORD_READY_V) { + if (cnt > 10) { + pr_err("adsp: timeout waiting for DSP write ready\n"); + ret_status = -EIO; + goto fail; + } + pr_warning("adsp: waiting for DSP write ready\n"); + udelay(10); + cnt++; + } + + /* Set the mutex bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M); + ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V; + + /* Clear the command bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M); + + /* Set the queue address bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M); + ctrl_word |= dsp_q_addr; + + writel(ctrl_word, info->write_ctrl); + + /* Generate an interrupt to the DSP. This notifies the DSP that + * we are about to send a command on this particular queue. The + * DSP will in response change its state. + */ + writel(1, info->send_irq); + + /* Poll until the adsp responds to the interrupt; this does not + * generate an interrupt from the adsp. This should happen within + * 5ms. + */ + cnt = 0; + while ((readl(info->write_ctrl) & + ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M) == + ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V) { + if (cnt > 5) { + pr_err("adsp: timeout waiting for adsp ack\n"); + ret_status = -EIO; + goto fail; + } + mdelay(1); + cnt++; + } + + /* Read the ctrl word */ + ctrl_word = readl(info->write_ctrl); + + if ((ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M) != + ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V) { + ret_status = -EIO; + pr_err("adsp: failed to write queue %x, retry\n", dsp_q_addr); + goto fail; + } + + /* Ctrl word status bits were 00, no error in the ctrl word */ + + /* Get the DSP buffer address */ + dsp_addr = (ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M) + + (uint32_t)MSM_AD5_BASE; + + if (dsp_addr < (uint32_t)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) { + uint16_t *buf_ptr = (uint16_t *) cmd_buf; + uint16_t *dsp_addr16 = (uint16_t *)dsp_addr; + cmd_size /= sizeof(uint16_t); + + /* Save the command ID */ + cmd_id = (uint32_t) buf_ptr[0]; + + /* Copy the command to DSP memory */ + cmd_size++; + while (--cmd_size) + *dsp_addr16++ = *buf_ptr++; + } else { + uint32_t *buf_ptr = (uint32_t *) cmd_buf; + uint32_t *dsp_addr32 = (uint32_t *)dsp_addr; + cmd_size /= sizeof(uint32_t); + + /* Save the command ID */ + cmd_id = buf_ptr[0]; + + cmd_size++; + while (--cmd_size) + *dsp_addr32++ = *buf_ptr++; + } + + /* Set the mutex bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M); + ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V; + + /* Set the command bits to write done */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M); + ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V; + + /* Set the queue address bits */ + ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M); + ctrl_word |= dsp_q_addr; + + writel(ctrl_word, info->write_ctrl); + + /* Generate an interrupt to the DSP. It does not respond with + * an interrupt, and we do not need to wait for it to + * acknowledge, because it will hold the mutex lock until it's + * ready to receive more commands again. + */ + writel(1, info->send_irq); + + module->num_commands++; + +fail: + spin_unlock_irqrestore(&adsp_cmd_lock, flags); + return ret_status; +} +EXPORT_SYMBOL(msm_adsp_write); + +static void *event_addr; +static void read_event(void *buf, size_t len) +{ + uint32_t *dptr = buf; + struct rpc_adsp_rtos_modem_to_app_args_t *sptr; + struct adsp_rtos_mp_mtoa_type *pkt_ptr; + + sptr = event_addr; + pkt_ptr = &sptr->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_packet; + + dptr[0] = be32_to_cpu(sptr->mtoa_pkt.mp_mtoa_header.event); + dptr[1] = be32_to_cpu(pkt_ptr->module); + dptr[2] = be32_to_cpu(pkt_ptr->image); +} + +static void handle_adsp_rtos_mtoa_app(struct rpc_request_hdr *req) +{ + struct rpc_adsp_rtos_modem_to_app_args_t *args = + (struct rpc_adsp_rtos_modem_to_app_args_t *)req; + uint32_t event; + uint32_t proc_id; + uint32_t desc_field; + uint32_t module_id; + uint32_t image; + struct msm_adsp_module *module; + struct adsp_rtos_mp_mtoa_type *pkt_ptr; + struct queue_to_offset_type *qptr; + struct queue_to_offset_type *qtbl; + uint32_t *mptr; + uint32_t *mtbl; + uint32_t q_idx; + uint32_t num_entries; + uint32_t entries_per_image; + struct adsp_rtos_mp_mtoa_init_info_type *iptr; + struct adsp_rtos_mp_mtoa_init_info_type *sptr; + int32_t i_no, e_idx; + + event = be32_to_cpu(args->mtoa_pkt.mp_mtoa_header.event); + proc_id = be32_to_cpu(args->mtoa_pkt.mp_mtoa_header.proc_id); + desc_field = be32_to_cpu(args->mtoa_pkt.desc_field); + + if (desc_field == RPC_ADSP_RTOS_INIT_INFO) { + pr_info("adsp:INIT_INFO Event\n"); + sptr = &args->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_init_packet; + + iptr = adsp_info.init_info_ptr; + iptr->image_count = be32_to_cpu(sptr->image_count); + iptr->num_queue_offsets = be32_to_cpu(sptr->num_queue_offsets); + num_entries = iptr->num_queue_offsets; + qptr = &sptr->queue_offsets_tbl[0][0]; + for (i_no = 0; i_no < iptr->image_count; i_no++) { + qtbl = &iptr->queue_offsets_tbl[i_no][0]; + for (e_idx = 0; e_idx < num_entries; e_idx++) { + qtbl[e_idx].offset = be32_to_cpu(qptr->offset); + qtbl[e_idx].queue = be32_to_cpu(qptr->queue); + q_idx = be32_to_cpu(qptr->queue); + iptr->queue_offsets[i_no][q_idx] = qtbl[e_idx].offset; + qptr++; + } + } + + num_entries = be32_to_cpu(sptr->num_task_module_entries); + iptr->num_task_module_entries = num_entries; + entries_per_image = num_entries / iptr->image_count; + mptr = &sptr->task_to_module_tbl[0][0]; + for (i_no = 0; i_no < iptr->image_count; i_no++) { + mtbl = &iptr->task_to_module_tbl[i_no][0]; + for (e_idx = 0; e_idx < entries_per_image; e_idx++) { + mtbl[e_idx] = be32_to_cpu(*mptr); + mptr++; + } + } + + iptr->module_table_size = be32_to_cpu(sptr->module_table_size); + mptr = &sptr->module_entries[0]; + for (i_no = 0; i_no < iptr->module_table_size; i_no++) + iptr->module_entries[i_no] = be32_to_cpu(mptr[i_no]); + adsp_info.init_info_state = ADSP_STATE_INIT_INFO; + rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, + RPC_ACCEPTSTAT_SUCCESS); + wake_up(&adsp_info.init_info_wait); + + return; + } + + pkt_ptr = &args->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_packet; + module_id = be32_to_cpu(pkt_ptr->module); + image = be32_to_cpu(pkt_ptr->image); + + pr_info("adsp: rpc event=%d, proc_id=%d, module=%d, image=%d\n", + event, proc_id, module_id, image); + + module = find_adsp_module_by_id(&adsp_info, module_id); + if (!module) { + pr_err("adsp: module %d is not supported!\n", module_id); + rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, + RPC_ACCEPTSTAT_GARBAGE_ARGS); + return; + } + + mutex_lock(&module->lock); + switch (event) { + case RPC_ADSP_RTOS_MOD_READY: + pr_info("adsp: module %s: READY\n", module->name); + module->state = ADSP_STATE_ENABLED; + wake_up(&module->state_wait); + adsp_set_image(module->info, image); + break; + case RPC_ADSP_RTOS_MOD_DISABLE: + pr_info("adsp: module %s: DISABLED\n", module->name); + module->state = ADSP_STATE_DISABLED; + wake_up(&module->state_wait); + break; + case RPC_ADSP_RTOS_SERVICE_RESET: + pr_info("adsp: module %s: SERVICE_RESET\n", module->name); + module->state = ADSP_STATE_DISABLED; + wake_up(&module->state_wait); + break; + case RPC_ADSP_RTOS_CMD_SUCCESS: + pr_info("adsp: module %s: CMD_SUCCESS\n", module->name); + break; + case RPC_ADSP_RTOS_CMD_FAIL: + pr_info("adsp: module %s: CMD_FAIL\n", module->name); + break; + case RPC_ADSP_RTOS_DISABLE_FAIL: + pr_info("adsp: module %s: DISABLE_FAIL\n", module->name); + break; + default: + pr_info("adsp: unknown event %d\n", event); + rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, + RPC_ACCEPTSTAT_GARBAGE_ARGS); + mutex_unlock(&module->lock); + return; + } + rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, + RPC_ACCEPTSTAT_SUCCESS); + mutex_unlock(&module->lock); + event_addr = (uint32_t *)req; + module->ops->event(module->driver_data, EVENT_MSG_ID, + EVENT_LEN, read_event); +} + +static int handle_adsp_rtos_mtoa(struct rpc_request_hdr *req) +{ + switch (req->procedure) { + case RPC_ADSP_RTOS_MTOA_NULL_PROC: + rpc_send_accepted_void_reply(rpc_cb_server_client, + req->xid, + RPC_ACCEPTSTAT_SUCCESS); + break; + case RPC_ADSP_RTOS_MODEM_TO_APP_PROC: + handle_adsp_rtos_mtoa_app(req); + break; + default: + pr_err("adsp: unknowned proc %d\n", req->procedure); + rpc_send_accepted_void_reply( + rpc_cb_server_client, req->xid, + RPC_ACCEPTSTAT_PROC_UNAVAIL); + break; + } + return 0; +} + +/* this should be common code with rpc_servers.c */ +static int adsp_rpc_thread(void *data) +{ + void *buffer; + struct rpc_request_hdr *req; + int rc; + + do { + rc = msm_rpc_read(rpc_cb_server_client, &buffer, -1, -1); + if (rc < 0) { + pr_err("adsp: could not read rpc: %d\n", rc); + break; + } + req = (struct rpc_request_hdr *)buffer; + + req->type = be32_to_cpu(req->type); + req->xid = be32_to_cpu(req->xid); + req->rpc_vers = be32_to_cpu(req->rpc_vers); + req->prog = be32_to_cpu(req->prog); + req->vers = be32_to_cpu(req->vers); + req->procedure = be32_to_cpu(req->procedure); + + if (req->type != 0) + goto bad_rpc; + if (req->rpc_vers != 2) + goto bad_rpc; + if (req->prog != RPC_ADSP_RTOS_MTOA_PROG) + goto bad_rpc; + if (req->vers != RPC_ADSP_RTOS_MTOA_VERS) + goto bad_rpc; + + handle_adsp_rtos_mtoa(req); + kfree(buffer); + continue; + +bad_rpc: + pr_err("adsp: bogus rpc from modem\n"); + kfree(buffer); + } while (1); + + do_exit(0); +} + +static size_t read_event_size; +static void *read_event_addr; + +static void read_event_16(void *buf, size_t len) +{ + uint16_t *dst = buf; + uint16_t *src = read_event_addr; + len /= 2; + if (len > read_event_size) + len = read_event_size; + while (len--) + *dst++ = *src++; +} + +static void read_event_32(void *buf, size_t len) +{ + uint32_t *dst = buf; + uint32_t *src = read_event_addr; + len /= 2; + if (len > read_event_size) + len = read_event_size; + while (len--) + *dst++ = *src++; +} + +static int adsp_rtos_read_ctrl_word_cmd_tast_to_h_v( + struct adsp_info *info, void *dsp_addr) +{ + struct msm_adsp_module *module; + unsigned rtos_task_id; + unsigned msg_id; + unsigned msg_length; + void (*func)(void *, size_t); + + if (dsp_addr >= (void *)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) { + uint32_t *dsp_addr32 = dsp_addr; + uint32_t tmp = *dsp_addr32++; + rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8; + msg_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M); + read_event_size = tmp >> 16; + read_event_addr = dsp_addr32; + msg_length = read_event_size * sizeof(uint32_t); + func = read_event_32; + } else { + uint16_t *dsp_addr16 = dsp_addr; + uint16_t tmp = *dsp_addr16++; + rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8; + msg_id = tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M; + read_event_size = *dsp_addr16++; + read_event_addr = dsp_addr16; + msg_length = read_event_size * sizeof(uint16_t); + func = read_event_16; + } + + if (rtos_task_id > info->max_task_id) { + pr_err("adsp: bogus task id %d\n", rtos_task_id); + return 0; + } + module = find_adsp_module_by_id(info, + adsp_get_module(info, rtos_task_id)); + + if (!module) { + pr_err("adsp: no module for task id %d\n", rtos_task_id); + return 0; + } + + module->num_events++; + + if (!module->ops) { + pr_err("adsp: module %s is not open\n", module->name); + return 0; + } + + module->ops->event(module->driver_data, msg_id, msg_length, func); + return 0; +} + +static int adsp_get_event(struct adsp_info *info) +{ + uint32_t ctrl_word; + uint32_t ready; + void *dsp_addr; + uint32_t cmd_type; + int cnt; + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&adsp_cmd_lock, flags); + + /* Whenever the DSP has a message, it updates this control word + * and generates an interrupt. When we receive the interrupt, we + * read this register to find out what ADSP task the command is + * comming from. + * + * The ADSP should *always* be ready on the first call, but the + * irq handler calls us in a loop (to handle back-to-back command + * processing), so we give the DSP some time to return to the + * ready state. The DSP will not issue another IRQ for events + * pending between the first IRQ and the event queue being drained, + * unfortunately. + */ + + for (cnt = 0; cnt < 10; cnt++) { + ctrl_word = readl(info->read_ctrl); + + if ((ctrl_word & ADSP_RTOS_READ_CTRL_WORD_FLAG_M) == + ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V) + goto ready; + + udelay(10); + } + pr_warning("adsp: not ready after 100uS\n"); + rc = -EBUSY; + goto done; + +ready: + /* Here we check to see if there are pending messages. If there are + * none, we siply return -EAGAIN to indicate that there are no more + * messages pending. + */ + ready = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_READY_M; + if ((ready != ADSP_RTOS_READ_CTRL_WORD_READY_V) && + (ready != ADSP_RTOS_READ_CTRL_WORD_CONT_V)) { + rc = -EAGAIN; + goto done; + } + + /* DSP says that there are messages waiting for the host to read */ + + /* Get the Command Type */ + cmd_type = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M; + + /* Get the DSP buffer address */ + dsp_addr = (void *)((ctrl_word & + ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M) + + (uint32_t)MSM_AD5_BASE); + + /* We can only handle Task-to-Host messages */ + if (cmd_type != ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V) { + pr_err("adsp: unknown dsp cmd_type %d\n", cmd_type); + rc = -EIO; + goto done; + } + + adsp_rtos_read_ctrl_word_cmd_tast_to_h_v(info, dsp_addr); + + ctrl_word = readl(info->read_ctrl); + ctrl_word &= ~ADSP_RTOS_READ_CTRL_WORD_READY_M; + + /* Write ctrl word to the DSP */ + writel(ctrl_word, info->read_ctrl); + + /* Generate an interrupt to the DSP */ + writel(1, info->send_irq); + +done: + spin_unlock_irqrestore(&adsp_cmd_lock, flags); + return rc; +} + +static irqreturn_t adsp_irq_handler(int irq, void *data) +{ + struct adsp_info *info = &adsp_info; + int cnt = 0; + for (cnt = 0; cnt < 10; cnt++) + if (adsp_get_event(info) < 0) + break; + if (cnt > info->event_backlog_max) + info->event_backlog_max = cnt; + info->events_received += cnt; + if (cnt == 10) + pr_err("adsp: too many (%d) events for single irq!\n", cnt); + return IRQ_HANDLED; +} + +int adsp_set_clkrate(struct msm_adsp_module *module, unsigned long clk_rate) +{ + if (module->clk && clk_rate) + return clk_set_rate(module->clk, clk_rate); + + return -EINVAL; +} + +int msm_adsp_enable(struct msm_adsp_module *module) +{ + int rc = 0; + + pr_info("msm_adsp_enable() '%s'state[%d] id[%d]\n", + module->name, module->state, module->id); + + mutex_lock(&module->lock); + switch (module->state) { + case ADSP_STATE_DISABLED: + rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_ENABLE, + module->id, module); + if (rc) + break; + module->state = ADSP_STATE_ENABLING; + mutex_unlock(&module->lock); + rc = wait_event_timeout(module->state_wait, + module->state != ADSP_STATE_ENABLING, + 1 * HZ); + mutex_lock(&module->lock); + if (module->state == ADSP_STATE_ENABLED) { + rc = 0; + } else { + pr_err("adsp: module '%s' enable timed out\n", + module->name); + rc = -ETIMEDOUT; + } + break; + case ADSP_STATE_ENABLING: + pr_warning("adsp: module '%s' enable in progress\n", + module->name); + break; + case ADSP_STATE_ENABLED: + pr_warning("adsp: module '%s' already enabled\n", + module->name); + break; + case ADSP_STATE_DISABLING: + pr_err("adsp: module '%s' disable in progress\n", + module->name); + rc = -EBUSY; + break; + } + mutex_unlock(&module->lock); + return rc; +} +EXPORT_SYMBOL(msm_adsp_enable); + +static int msm_adsp_disable_locked(struct msm_adsp_module *module) +{ + int rc = 0; + + switch (module->state) { + case ADSP_STATE_DISABLED: + pr_warning("adsp: module '%s' already disabled\n", + module->name); + break; + case ADSP_STATE_ENABLING: + case ADSP_STATE_ENABLED: + rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_DISABLE, + module->id, module); + module->state = ADSP_STATE_DISABLED; + } + return rc; +} + +int msm_adsp_disable(struct msm_adsp_module *module) +{ + int rc; + pr_info("msm_adsp_disable() '%s'\n", module->name); + mutex_lock(&module->lock); + rc = msm_adsp_disable_locked(module); + mutex_unlock(&module->lock); + return rc; +} +EXPORT_SYMBOL(msm_adsp_disable); + +static int msm_adsp_probe(struct platform_device *pdev) +{ + unsigned count; + int rc, i; + + pr_info("adsp: probe\n"); + + wake_lock_init(&adsp_wake_lock, WAKE_LOCK_SUSPEND, "adsp"); + adsp_info.init_info_ptr = kzalloc( + (sizeof(struct adsp_rtos_mp_mtoa_init_info_type)), GFP_KERNEL); + if (!adsp_info.init_info_ptr) + return -ENOMEM; + + rc = adsp_init_info(&adsp_info); + if (rc) + return rc; + adsp_info.send_irq += (uint32_t) MSM_AD5_BASE; + adsp_info.read_ctrl += (uint32_t) MSM_AD5_BASE; + adsp_info.write_ctrl += (uint32_t) MSM_AD5_BASE; + count = adsp_info.module_count; + + adsp_modules = kzalloc( + (sizeof(struct msm_adsp_module) + sizeof(void *)) * + count, GFP_KERNEL); + if (!adsp_modules) + return -ENOMEM; + + adsp_info.id_to_module = (void *) (adsp_modules + count); + + spin_lock_init(&adsp_cmd_lock); + + rc = request_irq(INT_ADSP, adsp_irq_handler, IRQF_TRIGGER_RISING, + "adsp", 0); + if (rc < 0) + goto fail_request_irq; + disable_irq(INT_ADSP); + + rpc_cb_server_client = msm_rpc_open(); + if (IS_ERR(rpc_cb_server_client)) { + rpc_cb_server_client = NULL; + rc = PTR_ERR(rpc_cb_server_client); + pr_err("adsp: could not create rpc server (%d)\n", rc); + goto fail_rpc_open; + } + + rc = msm_rpc_register_server(rpc_cb_server_client, + RPC_ADSP_RTOS_MTOA_PROG, + RPC_ADSP_RTOS_MTOA_VERS); + if (rc) { + pr_err("adsp: could not register callback server (%d)\n", rc); + goto fail_rpc_register; + } + + /* start the kernel thread to process the callbacks */ + kthread_run(adsp_rpc_thread, NULL, "kadspd"); + + for (i = 0; i < count; i++) { + struct msm_adsp_module *mod = adsp_modules + i; + mutex_init(&mod->lock); + init_waitqueue_head(&mod->state_wait); + mod->info = &adsp_info; + mod->name = adsp_info.module[i].name; + mod->id = adsp_info.module[i].id; + if (adsp_info.module[i].clk_name) + mod->clk = clk_get(NULL, adsp_info.module[i].clk_name); + else + mod->clk = NULL; + if (mod->clk && adsp_info.module[i].clk_rate) + clk_set_rate(mod->clk, adsp_info.module[i].clk_rate); + mod->verify_cmd = adsp_info.module[i].verify_cmd; + mod->patch_event = adsp_info.module[i].patch_event; + INIT_HLIST_HEAD(&mod->pmem_regions); + mod->pdev.name = adsp_info.module[i].pdev_name; + mod->pdev.id = -1; + adsp_info.id_to_module[i] = mod; + platform_device_register(&mod->pdev); + } + + msm_adsp_publish_cdevs(adsp_modules, count); + + return 0; + +fail_rpc_register: + msm_rpc_close(rpc_cb_server_client); + rpc_cb_server_client = NULL; +fail_rpc_open: + enable_irq(INT_ADSP); + free_irq(INT_ADSP, 0); +fail_request_irq: + kfree(adsp_modules); + kfree(adsp_info.init_info_ptr); + return rc; +} + +static struct platform_driver msm_adsp_driver = { + .probe = msm_adsp_probe, + .driver = { + .name = "rs3000000a:00010000", + .owner = THIS_MODULE, + }, +}; + +static int __init adsp_init(void) +{ + return platform_driver_register(&msm_adsp_driver); +} + +device_initcall(adsp_init); diff --git a/arch/arm/mach-msm/qdsp5/adsp_new.h b/arch/arm/mach-msm/qdsp5/adsp_new.h new file mode 100644 index 0000000000000..963c9efe53a47 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/adsp_new.h @@ -0,0 +1,360 @@ +/* arch/arm/mach-msm/qdsp5/adsp.h + * + * Copyright (c) 2008 QUALCOMM Incorporated + * Copyright (C) 2008 Google, Inc. + * Author: Iliyan Malchev + * + * 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 _ARCH_ARM_MACH_MSM_ADSP_NEW_H +#define _ARCH_ARM_MACH_MSM_ADSP_NEW_H + +#include +#include +#include +#include + +int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr, + unsigned long len); +int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr, + unsigned long *kvaddr, unsigned long len); +int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr); + +int adsp_vfe_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); +int adsp_jpeg_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); +int adsp_lpm_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); +int adsp_video_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); +int adsp_videoenc_verify_cmd(struct msm_adsp_module *module, + unsigned int queue_id, void *cmd_data, + size_t cmd_size); + + +struct adsp_event; + +int adsp_vfe_patch_event(struct msm_adsp_module *module, + struct adsp_event *event); + +int adsp_jpeg_patch_event(struct msm_adsp_module *module, + struct adsp_event *event); + + +struct adsp_module_info { + const char *name; + const char *pdev_name; + uint32_t id; + const char *clk_name; + unsigned long clk_rate; + int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *, + size_t); + int (*patch_event) (struct msm_adsp_module*, struct adsp_event *); +}; + +#define ADSP_EVENT_MAX_SIZE 496 + +struct adsp_event { + struct list_head list; + uint32_t size; /* always in bytes */ + uint16_t msg_id; + uint16_t type; /* 0 for msgs (from aDSP), 1 for events (from ARM9) */ + int is16; /* always 0 (msg is 32-bit) when the event type is 1(ARM9) */ + union { + uint16_t msg16[ADSP_EVENT_MAX_SIZE / 2]; + uint32_t msg32[ADSP_EVENT_MAX_SIZE / 4]; + } data; +}; + +struct adsp_info { + uint32_t send_irq; + uint32_t read_ctrl; + uint32_t write_ctrl; + + uint32_t max_msg16_size; + uint32_t max_msg32_size; + + uint32_t max_task_id; + uint32_t max_module_id; + uint32_t max_queue_id; + uint32_t max_image_id; + + /* for each image id, a map of queue id to offset */ + uint32_t **queue_offset; + + /* for each image id, a map of task id to module id */ + uint32_t **task_to_module; + + /* for each module id, map of module id to module */ + struct msm_adsp_module **id_to_module; + + uint32_t module_count; + struct adsp_module_info *module; + + /* stats */ + uint32_t events_received; + uint32_t event_backlog_max; + + /* rpc_client for init_info */ + struct msm_rpc_endpoint *init_info_rpc_client; + struct adsp_rtos_mp_mtoa_init_info_type *init_info_ptr; + wait_queue_head_t init_info_wait; + unsigned init_info_state; +}; + +#define RPC_ADSP_RTOS_ATOM_NULL_PROC 0 +#define RPC_ADSP_RTOS_MTOA_NULL_PROC 0 +#define RPC_ADSP_RTOS_APP_TO_MODEM_PROC 2 +#define RPC_ADSP_RTOS_MODEM_TO_APP_PROC 2 + +enum rpc_adsp_rtos_proc_type { + RPC_ADSP_RTOS_PROC_NONE = 0, + RPC_ADSP_RTOS_PROC_MODEM = 1, + RPC_ADSP_RTOS_PROC_APPS = 2, +}; + +enum { + RPC_ADSP_RTOS_CMD_REGISTER_APP, + RPC_ADSP_RTOS_CMD_ENABLE, + RPC_ADSP_RTOS_CMD_DISABLE, + RPC_ADSP_RTOS_CMD_KERNEL_COMMAND, + RPC_ADSP_RTOS_CMD_16_COMMAND, + RPC_ADSP_RTOS_CMD_32_COMMAND, + RPC_ADSP_RTOS_CMD_DISABLE_EVENT_RSP, + RPC_ADSP_RTOS_CMD_REMOTE_EVENT, + RPC_ADSP_RTOS_CMD_SET_STATE, + RPC_ADSP_RTOS_CMD_REMOTE_INIT_INFO_EVENT, + RPC_ADSP_RTOS_CMD_GET_INIT_INFO, +}; + +enum rpc_adsp_rtos_mod_status_type { + RPC_ADSP_RTOS_MOD_READY, + RPC_ADSP_RTOS_MOD_DISABLE, + RPC_ADSP_RTOS_SERVICE_RESET, + RPC_ADSP_RTOS_CMD_FAIL, + RPC_ADSP_RTOS_CMD_SUCCESS, + RPC_ADSP_RTOS_INIT_INFO, + RPC_ADSP_RTOS_DISABLE_FAIL, +}; + +struct rpc_adsp_rtos_app_to_modem_args_t { + struct rpc_request_hdr hdr; + uint32_t gotit; /* if 1, the next elements are present */ + uint32_t cmd; /* e.g., RPC_ADSP_RTOS_CMD_REGISTER_APP */ + uint32_t proc_id; /* e.g., RPC_ADSP_RTOS_PROC_APPS */ + uint32_t module; /* e.g., QDSP_MODULE_AUDPPTASK */ +}; + +enum qdsp_image_type { + QDSP_IMAGE_COMBO, + QDSP_IMAGE_GAUDIO, + QDSP_IMAGE_QTV_LP, + QDSP_IMAGE_MAX, + /* DO NOT USE: Force this enum to be a 32bit type to improve speed */ + QDSP_IMAGE_32BIT_DUMMY = 0x10000 +}; + +#define EVENT_LEN 12 +#define EVENT_MSG_ID ((uint16_t)~0) + +struct adsp_rtos_mp_mtoa_header_type { + enum rpc_adsp_rtos_mod_status_type event; + enum rpc_adsp_rtos_proc_type proc_id; +}; + +/* ADSP RTOS MP Communications - Modem to APP's Event Info*/ +struct adsp_rtos_mp_mtoa_type { + uint32_t module; + uint32_t image; + uint32_t apps_okts; +}; + +/* ADSP RTOS MP Communications - Modem to APP's Init Info */ +#define IMG_MAX 8 +#define ENTRIES_MAX 64 + +struct queue_to_offset_type { + uint32_t queue; + uint32_t offset; +}; + +struct adsp_rtos_mp_mtoa_init_info_type { + uint32_t image_count; + uint32_t num_queue_offsets; + struct queue_to_offset_type queue_offsets_tbl[IMG_MAX][ENTRIES_MAX]; + uint32_t num_task_module_entries; + uint32_t task_to_module_tbl[IMG_MAX][ENTRIES_MAX]; + + uint32_t module_table_size; + uint32_t module_entries[ENTRIES_MAX]; + /* + * queue_offsets[] is to store only queue_offsets + */ + uint32_t queue_offsets[IMG_MAX][ENTRIES_MAX]; +}; + +struct adsp_rtos_mp_mtoa_s_type { + struct adsp_rtos_mp_mtoa_header_type mp_mtoa_header; + + uint32_t desc_field; + union { + struct adsp_rtos_mp_mtoa_init_info_type mp_mtoa_init_packet; + struct adsp_rtos_mp_mtoa_type mp_mtoa_packet; + } adsp_rtos_mp_mtoa_data; +}; + +struct rpc_adsp_rtos_modem_to_app_args_t { + struct rpc_request_hdr hdr; + uint32_t gotit; /* if 1, the next elements are present */ + struct adsp_rtos_mp_mtoa_s_type mtoa_pkt; +}; + +#define ADSP_STATE_DISABLED 0 +#define ADSP_STATE_ENABLING 1 +#define ADSP_STATE_ENABLED 2 +#define ADSP_STATE_DISABLING 3 +#define ADSP_STATE_INIT_INFO 4 + +struct msm_adsp_module { + struct mutex lock; + const char *name; + unsigned id; + struct adsp_info *info; + + struct msm_rpc_endpoint *rpc_client; + struct msm_adsp_ops *ops; + void *driver_data; + + /* statistics */ + unsigned num_commands; + unsigned num_events; + + wait_queue_head_t state_wait; + unsigned state; + + struct platform_device pdev; + struct clk *clk; + int open_count; + + struct mutex pmem_regions_lock; + struct hlist_head pmem_regions; + int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *, + size_t); + int (*patch_event) (struct msm_adsp_module*, struct adsp_event *); +}; + +extern void msm_adsp_publish_cdevs(struct msm_adsp_module *, unsigned); +extern int adsp_init_info(struct adsp_info *info); + +/* Command Queue Indexes */ +#define QDSP_lpmCommandQueue 0 +#define QDSP_mpuAfeQueue 1 +#define QDSP_mpuGraphicsCmdQueue 2 +#define QDSP_mpuModmathCmdQueue 3 +#define QDSP_mpuVDecCmdQueue 4 +#define QDSP_mpuVDecPktQueue 5 +#define QDSP_mpuVEncCmdQueue 6 +#define QDSP_rxMpuDecCmdQueue 7 +#define QDSP_rxMpuDecPktQueue 8 +#define QDSP_txMpuEncQueue 9 +#define QDSP_uPAudPPCmd1Queue 10 +#define QDSP_uPAudPPCmd2Queue 11 +#define QDSP_uPAudPPCmd3Queue 12 +#define QDSP_uPAudPlay0BitStreamCtrlQueue 13 +#define QDSP_uPAudPlay1BitStreamCtrlQueue 14 +#define QDSP_uPAudPlay2BitStreamCtrlQueue 15 +#define QDSP_uPAudPlay3BitStreamCtrlQueue 16 +#define QDSP_uPAudPlay4BitStreamCtrlQueue 17 +#define QDSP_uPAudPreProcCmdQueue 18 +#define QDSP_uPAudRecBitStreamQueue 19 +#define QDSP_uPAudRecCmdQueue 20 +#define QDSP_uPDiagQueue 21 +#define QDSP_uPJpegActionCmdQueue 22 +#define QDSP_uPJpegCfgCmdQueue 23 +#define QDSP_uPVocProcQueue 24 +#define QDSP_vfeCommandQueue 25 +#define QDSP_vfeCommandScaleQueue 26 +#define QDSP_vfeCommandTableQueue 27 +#define QDSP_MAX_NUM_QUEUES 28 + +/* Value to indicate that a queue is not defined for a particular image */ +#define QDSP_RTOS_NO_QUEUE 0xfffffffe + +/* + * Constants used to communicate with the ADSP RTOS + */ +#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M 0x80000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V 0x80000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_AVAIL_V 0x00000000U + +#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_M 0x70000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_REQ_V 0x00000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V 0x10000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_NO_CMD_V 0x70000000U + +#define ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M 0x0E000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V 0x00000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_NO_FREE_BUF_V 0x02000000U + +#define ADSP_RTOS_WRITE_CTRL_WORD_KERNEL_FLG_M 0x01000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_MSG_WRITE_V 0x00000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_V 0x01000000U + +#define ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU +#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_ID_M 0x00FFFFFFU + +/* Combination of MUTEX and CMD bits to check if the DSP is busy */ +#define ADSP_RTOS_WRITE_CTRL_WORD_READY_M 0xF0000000U +#define ADSP_RTOS_WRITE_CTRL_WORD_READY_V 0x70000000U + +/* RTOS to Host processor command mask values */ +#define ADSP_RTOS_READ_CTRL_WORD_FLAG_M 0x80000000U +#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_WAIT_V 0x00000000U +#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V 0x80000000U + +#define ADSP_RTOS_READ_CTRL_WORD_CMD_M 0x60000000U +#define ADSP_RTOS_READ_CTRL_WORD_READ_DONE_V 0x00000000U +#define ADSP_RTOS_READ_CTRL_WORD_READ_REQ_V 0x20000000U +#define ADSP_RTOS_READ_CTRL_WORD_NO_CMD_V 0x60000000U + +/* Combination of FLAG and COMMAND bits to check if MSG ready */ +#define ADSP_RTOS_READ_CTRL_WORD_READY_M 0xE0000000U +#define ADSP_RTOS_READ_CTRL_WORD_READY_V 0xA0000000U +#define ADSP_RTOS_READ_CTRL_WORD_CONT_V 0xC0000000U +#define ADSP_RTOS_READ_CTRL_WORD_DONE_V 0xE0000000U + +#define ADSP_RTOS_READ_CTRL_WORD_STATUS_M 0x18000000U +#define ADSP_RTOS_READ_CTRL_WORD_NO_ERR_V 0x00000000U + +#define ADSP_RTOS_READ_CTRL_WORD_IN_PROG_M 0x04000000U +#define ADSP_RTOS_READ_CTRL_WORD_NO_READ_IN_PROG_V 0x00000000U +#define ADSP_RTOS_READ_CTRL_WORD_READ_IN_PROG_V 0x04000000U + +#define ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M 0x03000000U +#define ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V 0x00000000U +#define ADSP_RTOS_READ_CTRL_WORD_CMD_KRNL_TO_H_V 0x01000000U +#define ADSP_RTOS_READ_CTRL_WORD_CMD_H_TO_KRNL_CFM_V 0x02000000U + +#define ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU + +#define ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M 0x000000FFU +#define ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M 0x0000FF00U + +/* Base address of DSP and DSP hardware registers */ +#define QDSP_RAMC_OFFSET 0x400000 + +#endif diff --git a/arch/arm/mach-msm/qdsp5/audio_aac.c b/arch/arm/mach-msm/qdsp5/audio_aac.c new file mode 100644 index 0000000000000..1c20f28e7bf04 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_aac.c @@ -0,0 +1,1052 @@ +/* arch/arm/mach-msm/qdsp5/audio_aac.c + * + * aac audio decoder device + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * Copyright (c) 2008-2009 QUALCOMM USA, 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 +#include +#include + +#include + +#include +#include +#include "audmgr.h" + +#include +#include +#include +#include +#include +#include + +/* for queue ids - should be relative to module number*/ +#include "adsp.h" + +#ifdef DEBUG +#define dprintk(format, arg...) \ +printk(KERN_DEBUG format, ## arg) +#else +#define dprintk(format, arg...) do {} while (0) +#endif + +#define BUFSZ 32768 +#define DMASZ (BUFSZ * 2) + +#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF +#define AUDDEC_DEC_AAC 5 + +#define PCM_BUFSZ_MIN 9600 /* Hold one stereo AAC frame */ +#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most + but support 2 buffers currently */ +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + dma_addr_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + + /* configuration to use on next enable */ + uint32_t out_sample_rate; + uint32_t out_channel_mode; + struct msm_audio_aac_config aac_config; + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + + int rflush; /* Read flush */ + int wflush; /* Write flush */ + int opened; + int enabled; + int running; + int stopped; /* set when stopped, cleared on flush */ + int pcm_feedback; + int buf_refresh; + + int reserved; /* A byte is being reserved */ + char rsv_byte; /* Handle odd length user data */ + + unsigned volume; + + uint16_t dec_id; + uint32_t read_ptr_offset; +}; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audplay_send_data(struct audio *audio, unsigned needed); +static void audplay_config_hostpcm(struct audio *audio); +static void audplay_buffer_refresh(struct audio *audio); +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); + +/* must be called with audio->lock held */ +static int audio_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + dprintk("audio_enable()\n"); + + if (audio->enabled) + return 0; + + audio->out_tail = 0; + audio->out_needed = 0; + + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK; + cfg.codec = RPC_AUD_DEF_CODEC_AAC; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + + if (msm_adsp_enable(audio->audplay)) { + pr_err("audio: msm_adsp_enable(audplay) failed\n"); + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) { + pr_err("audio: audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audio_disable(struct audio *audio) +{ + dprintk("audio_disable()\n"); + if (audio->enabled) { + audio->enabled = 0; + auddec_dsp_config(audio, 0); + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + } + return 0; +} + +/* ------------------- dsp --------------------- */ +static void audio_update_pcm_buf_entry(struct audio *audio, uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) + return; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + dprintk("audio_update_pcm_buf_entry: in[%d] ready\n", + audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + + } else { + pr_err + ("audio_update_pcm_buf_entry: expected=%x ret=%x\n" + , audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audplay_buffer_refresh(audio); + } else { + dprintk("audio_update_pcm_buf_entry: read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + dprintk("audplay_dsp_event: msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audplay_send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audio_update_pcm_buf_entry(audio, msg); + break; + + default: + pr_err("unexpected message from decoder \n"); + } +} + +static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: + dprintk("decoder status: sleep \n"); + break; + + case AUDPP_DEC_STATUS_INIT: + dprintk("decoder status: init \n"); + audpp_cmd_cfg_routing_mode(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + dprintk("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + dprintk("decoder status: play \n"); + if (audio->pcm_feedback) { + audplay_config_hostpcm(audio); + audplay_buffer_refresh(audio); + } + break; + default: + pr_err("unknown decoder status \n"); + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + dprintk("audio_dsp_event: CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_set_volume_and_pan(audio->dec_id, audio->volume, + 0); + audpp_avsync(audio->dec_id, 22050); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + dprintk("audio_dsp_event: CFG_MSG DISABLE\n"); + audpp_avsync(audio->dec_id, 0); + audio->running = 0; + } else { + pr_err("audio_dsp_event: CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + dprintk("audio_dsp_event: ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_MSG_FLUSH_ACK: + dprintk("%s: FLUSH_ACK\n", __func__); + audio->wflush = 0; + audio->rflush = 0; + if (audio->pcm_feedback) + audplay_buffer_refresh(audio); + break; + + default: + pr_err("audio_dsp_event: UNKNOWN (%d)\n", id); + } + +} + +struct msm_adsp_ops audplay_adsp_ops_aac = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + audpp_cmd_cfg_dec_type cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AAC; + else + cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V; + + return audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + audpp_cmd_cfg_adec_params_aac cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_AAC_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = audio->out_sample_rate; + cmd.format = audio->aac_config.format; + cmd.audio_object = audio->aac_config.audio_object; + cmd.ep_config = audio->aac_config.ep_config; + cmd.aac_section_data_resilience_flag = + audio->aac_config.aac_section_data_resilience_flag; + cmd.aac_scalefactor_data_resilience_flag = + audio->aac_config.aac_scalefactor_data_resilience_flag; + cmd.aac_spectral_data_resilience_flag = + audio->aac_config.aac_spectral_data_resilience_flag; + cmd.sbr_on_flag = audio->aac_config.sbr_on_flag; + cmd.sbr_ps_on_flag = audio->aac_config.sbr_ps_on_flag; + cmd.channel_configuration = audio->aac_config.channel_configuration; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + dprintk("audpp_cmd_cfg_routing_mode()\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + audplay_cmd_bitstream_data_avail cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL; + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len / 2; + cmd.partition_number = 0; + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audplay_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size - + (audio->in[audio->fill_next].size % 1024); /* AAC frame size */ + refresh_cmd.buf_read_count = 0; + dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n", + refresh_cmd.buf0_address, refresh_cmd.buf0_length); + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audplay_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + dprintk("audplay_config_hostpcm()\n"); + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = audio->pcm_buf_count; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); + +} + +static void audplay_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed && !audio->wflush) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + dprintk("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); +/* printk("frame %d busy\n", audio->out_tail); */ + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audio_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->reserved = 0; + audio->out_needed = 0; + atomic_set(&audio->out_bytes, 0); +} + +static void audio_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + audio->buf_refresh = 0; + audio->read_next = 0; + audio->fill_next = 0; +} + +static int audaac_validate_usr_config(struct msm_audio_aac_config *config) +{ + int ret_val = -1; + + if (config->format != AUDIO_AAC_FORMAT_ADTS && + config->format != AUDIO_AAC_FORMAT_RAW && + config->format != AUDIO_AAC_FORMAT_PSUEDO_RAW && + config->format != AUDIO_AAC_FORMAT_LOAS) + goto done; + + if (config->audio_object != AUDIO_AAC_OBJECT_LC && + config->audio_object != AUDIO_AAC_OBJECT_LTP && + config->audio_object != AUDIO_AAC_OBJECT_ERLC) + goto done; + + if (config->audio_object == AUDIO_AAC_OBJECT_ERLC) { + if (config->ep_config > 3) + goto done; + if (config->aac_scalefactor_data_resilience_flag != + AUDIO_AAC_SCA_DATA_RES_OFF && + config->aac_scalefactor_data_resilience_flag != + AUDIO_AAC_SCA_DATA_RES_ON) + goto done; + if (config->aac_section_data_resilience_flag != + AUDIO_AAC_SEC_DATA_RES_OFF && + config->aac_section_data_resilience_flag != + AUDIO_AAC_SEC_DATA_RES_ON) + goto done; + if (config->aac_spectral_data_resilience_flag != + AUDIO_AAC_SPEC_DATA_RES_OFF && + config->aac_spectral_data_resilience_flag != + AUDIO_AAC_SPEC_DATA_RES_ON) + goto done; + } else { + config->aac_section_data_resilience_flag = + AUDIO_AAC_SEC_DATA_RES_OFF; + config->aac_scalefactor_data_resilience_flag = + AUDIO_AAC_SCA_DATA_RES_OFF; + config->aac_spectral_data_resilience_flag = + AUDIO_AAC_SPEC_DATA_RES_OFF; + } + + if (config->sbr_on_flag != AUDIO_AAC_SBR_ON_FLAG_OFF && + config->sbr_on_flag != AUDIO_AAC_SBR_ON_FLAG_ON) + goto done; + + if (config->sbr_ps_on_flag != AUDIO_AAC_SBR_PS_ON_FLAG_OFF && + config->sbr_ps_on_flag != AUDIO_AAC_SBR_PS_ON_FLAG_ON) + goto done; + + if (config->dual_mono_mode > AUDIO_AAC_DUAL_MONO_PL_SR) + goto done; + + if (config->channel_configuration > 2) + goto done; + + ret_val = 0; + done: + return ret_val; +} + +static void audio_ioport_reset(struct audio *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audio_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audio_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = 0; + + dprintk("audio_ioctl() cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = audpp_avsync_byte_count(audio->dec_id); + stats.sample_count = audpp_avsync_sample_count(audio->dec_id); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + if (cmd == AUDIO_SET_VOLUME) { + unsigned long flags; + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->volume = arg; + if (audio->running) + audpp_set_volume_and_pan(audio->dec_id, arg, 0); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + return 0; + } + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + rc = audio_enable(audio); + break; + case AUDIO_STOP: + rc = audio_disable(audio); + audio->stopped = 1; + audio_ioport_reset(audio); + audio->stopped = 0; + break; + case AUDIO_FLUSH: + dprintk("%s: AUDIO_FLUSH\n", __func__); + audio->rflush = 1; + audio->wflush = 1; + audio_ioport_reset(audio); + if (audio->running) + audpp_flush(audio->dec_id); + else { + audio->rflush = 0; + audio->wflush = 0; + } + break; + + case AUDIO_SET_CONFIG:{ + struct msm_audio_config config; + + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + + if (config.channel_count == 1) { + config.channel_count = + AUDPP_CMD_PCM_INTF_MONO_V; + } else if (config.channel_count == 2) { + config.channel_count = + AUDPP_CMD_PCM_INTF_STEREO_V; + } else { + rc = -EINVAL; + break; + } + + audio->out_sample_rate = config.sample_rate; + audio->out_channel_mode = config.channel_count; + rc = 0; + break; + } + case AUDIO_GET_CONFIG:{ + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = 2; + config.sample_rate = audio->out_sample_rate; + if (audio->out_channel_mode == + AUDPP_CMD_PCM_INTF_MONO_V) { + config.channel_count = 1; + } else { + config.channel_count = 2; + } + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + config.unused[3] = 0; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + + break; + } + case AUDIO_GET_AAC_CONFIG:{ + if (copy_to_user((void *)arg, &audio->aac_config, + sizeof(audio->aac_config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_AAC_CONFIG:{ + struct msm_audio_aac_config usr_config; + + if (copy_from_user + (&usr_config, (void *)arg, + sizeof(usr_config))) { + rc = -EFAULT; + break; + } + + if (audaac_validate_usr_config(&usr_config) == 0) { + audio->aac_config = usr_config; + rc = 0; + } else + rc = -EINVAL; + + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = 0; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + dprintk("ioctl: allocate PCM buffer %d\n", + config.buffer_count * + config.buffer_size); + audio->read_data = + dma_alloc_coherent(NULL, + config.buffer_size * + config.buffer_count, + &audio->read_phys, + GFP_KERNEL); + if (!audio->read_data) { + pr_err("audio_aac: buf alloc fail\n"); + rc = -1; + } else { + uint8_t index; + uint32_t offset = 0; + audio->pcm_feedback = 1; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; + index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + rc = 0; + } + } else { + rc = 0; + } + break; + } + case AUDIO_PAUSE: + dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audio_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (!audio->pcm_feedback) + return 0; /* PCM feedback is not enabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + dprintk("audio_read() %d \n", count); + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next]. + used > 0) || (audio->stopped) + || (audio->rflush)); + + if (rc < 0) + break; + + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver + does not know frame size, read count must be greater + or equal to size of PCM samples */ + dprintk("audio_read: no partial frame done reading\n"); + break; + } else { + dprintk("audio_read: read from in[%d]\n", + audio->read_next); + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + pr_err("audio_read: invalid addr %x \n", + (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + if (audio->in[audio->read_next].used == 0) + break; /* No data ready at this moment + * Exit while loop to prevent + * output thread sleep too long + */ + } + } + + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + dprintk("audio_read: kick start pcm feedback again\n"); + audplay_buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + dprintk("audio_read: read %d bytes\n", rc); + return rc; +} + +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + char *cpy_ptr; + int rc = 0; + unsigned dsize; + + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + cpy_ptr = frame->data; + dsize = 0; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); + if (rc < 0) + break; + if (audio->stopped || audio->wflush) { + rc = -EBUSY; + break; + } + + if (audio->reserved) { + dprintk("%s: append reserved byte %x\n", + __func__, audio->rsv_byte); + *cpy_ptr = audio->rsv_byte; + xfer = (count > (frame->size - 1)) ? + frame->size - 1 : count; + cpy_ptr++; + dsize = 1; + audio->reserved = 0; + } else + xfer = (count > frame->size) ? frame->size : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { + rc = -EFAULT; + break; + } + + dsize += xfer; + if (dsize & 1) { + audio->rsv_byte = ((char *) frame->data)[dsize - 1]; + dprintk("%s: odd length buf reserve last byte %x\n", + __func__, audio->rsv_byte); + audio->reserved = 1; + dsize--; + } + count -= xfer; + buf += xfer; + + if (dsize > 0) { + audio->out_head ^= 1; + frame->used = dsize; + audplay_send_data(audio, 0); + } + } + mutex_unlock(&audio->write_lock); + if (buf > start) + return buf - start; + return rc; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + dprintk("audio_release()\n"); + + mutex_lock(&audio->lock); + audio_disable(audio); + audio_flush(audio); + audio_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audio->audplay = NULL; + audio->opened = 0; + audio->reserved = 0; + dma_free_coherent(NULL, DMASZ, audio->data, audio->phys); + audio->data = NULL; + if (audio->read_data != NULL) { + dma_free_coherent(NULL, + audio->in[0].size * audio->pcm_buf_count, + audio->read_data, audio->read_phys); + audio->read_data = NULL; + } + audio->pcm_feedback = 0; + mutex_unlock(&audio->lock); + return 0; +} + +struct audio the_aac_audio; + +static int audio_open(struct inode *inode, struct file *file) +{ + struct audio *audio = &the_aac_audio; + int rc; + + mutex_lock(&audio->lock); + + if (audio->opened) { + pr_err("audio: busy\n"); + rc = -EBUSY; + goto done; + } + + if (!audio->data) { + audio->data = dma_alloc_coherent(NULL, DMASZ, + &audio->phys, GFP_KERNEL); + if (!audio->data) { + pr_err("audio: could not allocate DMA buffers\n"); + rc = -ENOMEM; + goto done; + } + } + + rc = audmgr_open(&audio->audmgr); + if (rc) + goto done; + + rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay, + &audplay_adsp_ops_aac, audio); + if (rc) { + pr_err("audio: failed to get audplay0 dsp module\n"); + goto done; + } + audio->out_sample_rate = 44100; + audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; + audio->aac_config.format = AUDIO_AAC_FORMAT_ADTS; + audio->aac_config.audio_object = AUDIO_AAC_OBJECT_LC; + audio->aac_config.ep_config = 0; + audio->aac_config.aac_section_data_resilience_flag = + AUDIO_AAC_SEC_DATA_RES_OFF; + audio->aac_config.aac_scalefactor_data_resilience_flag = + AUDIO_AAC_SCA_DATA_RES_OFF; + audio->aac_config.aac_spectral_data_resilience_flag = + AUDIO_AAC_SPEC_DATA_RES_OFF; + audio->aac_config.sbr_on_flag = AUDIO_AAC_SBR_ON_FLAG_ON; + audio->aac_config.sbr_ps_on_flag = AUDIO_AAC_SBR_PS_ON_FLAG_ON; + audio->aac_config.dual_mono_mode = AUDIO_AAC_DUAL_MONO_PL_SR; + audio->aac_config.channel_configuration = 2; + audio->dec_id = 0; + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = BUFSZ; + + audio->out[1].data = audio->data + BUFSZ; + audio->out[1].addr = audio->phys + BUFSZ; + audio->out[1].size = BUFSZ; + + audio->volume = 0x2000; /* Q13 1.0 */ + + audio_flush(audio); + + file->private_data = audio; + audio->opened = 1; + rc = 0; +done: + mutex_unlock(&audio->lock); + return rc; +} + +static struct file_operations audio_aac_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .read = audio_read, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, +}; + +struct miscdevice audio_aac_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_aac", + .fops = &audio_aac_fops, +}; + +static int __init audio_init(void) +{ + mutex_init(&the_aac_audio.lock); + mutex_init(&the_aac_audio.write_lock); + mutex_init(&the_aac_audio.read_lock); + spin_lock_init(&the_aac_audio.dsp_lock); + init_waitqueue_head(&the_aac_audio.write_wait); + init_waitqueue_head(&the_aac_audio.read_wait); + the_aac_audio.read_data = NULL; + return misc_register(&audio_aac_misc); +} + +device_initcall(audio_init); diff --git a/arch/arm/mach-msm/qdsp5/audio_amrnb.c b/arch/arm/mach-msm/qdsp5/audio_amrnb.c new file mode 100644 index 0000000000000..cd818a526f836 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_amrnb.c @@ -0,0 +1,873 @@ +/* linux/arch/arm/mach-msm/qdsp5/audio_amrnb.c + * + * amrnb audio decoder device + * + * Copyright (c) 2008 QUALCOMM USA, INC. + * + * Based on the mp3 native driver in arch/arm/mach-msm/qdsp5/audio_mp3.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * 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, you can find it at http://www.fsf.org + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include "audmgr.h" + +#include +#include +#include +#include + +/* for queue ids - should be relative to module number*/ +#include "adsp.h" + +#define DEBUG +#ifdef DEBUG +#define dprintk(format, arg...) \ +printk(KERN_DEBUG format, ## arg) +#else +#define dprintk(format, arg...) do {} while (0) +#endif + +#define BUFSZ 1024 /* Hold minimum 700ms voice data */ +#define DMASZ (BUFSZ * 2) + +#define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF +#define AUDDEC_DEC_AMRNB 10 + +#define PCM_BUFSZ_MIN 1600 /* 100ms worth of data */ +#define AMRNB_DECODED_FRSZ 320 /* AMR-NB 20ms 8KHz mono PCM size */ +#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most + but support 2 buffers currently */ +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + dma_addr_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + + uint8_t opened:1; + uint8_t enabled:1; + uint8_t running:1; + uint8_t stopped:1; /* set when stopped, cleared on flush */ + uint8_t pcm_feedback:1; + uint8_t buf_refresh:1; + + unsigned volume; + + uint16_t dec_id; + uint32_t read_ptr_offset; +}; + +struct audpp_cmd_cfg_adec_params_amrnb { + audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; +} __attribute__((packed)) ; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audamrnb_send_data(struct audio *audio, unsigned needed); +static void audamrnb_config_hostpcm(struct audio *audio); +static void audamrnb_buffer_refresh(struct audio *audio); +static void audamrnb_dsp_event(void *private, unsigned id, uint16_t *msg); + +/* must be called with audio->lock held */ +static int audamrnb_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + dprintk("audamrnb_enable()\n"); + + if (audio->enabled) + return 0; + + audio->out_tail = 0; + audio->out_needed = 0; + + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK; + cfg.codec = RPC_AUD_DEF_CODEC_AMR_NB; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + + if (msm_adsp_enable(audio->audplay)) { + pr_err("audio: msm_adsp_enable(audplay) failed\n"); + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audamrnb_dsp_event, audio)) { + pr_err("audio: audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audamrnb_disable(struct audio *audio) +{ + dprintk("audamrnb_disable()\n"); + if (audio->enabled) { + audio->enabled = 0; + auddec_dsp_config(audio, 0); + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + } + return 0; +} + +/* ------------------- dsp --------------------- */ +static void audamrnb_update_pcm_buf_entry(struct audio *audio, + uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + dprintk("audamrnb_update_pcm_buf_entry: in[%d] ready\n", + audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + + } else { + pr_err + ("audamrnb_update_pcm_buf_entry: expected=%x ret=%x\n" + , audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audamrnb_buffer_refresh(audio); + } else { + dprintk("audamrnb_update_pcm_buf_entry: read cannot keep up\n"); + audio->buf_refresh = 1; + } + + spin_unlock_irqrestore(&audio->dsp_lock, flags); + wake_up(&audio->read_wait); +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + dprintk("audplay_dsp_event: msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audamrnb_send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audamrnb_update_pcm_buf_entry(audio, msg); + break; + + default: + pr_err("unexpected message from decoder \n"); + } +} + +static void audamrnb_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: + dprintk("decoder status: sleep \n"); + break; + + case AUDPP_DEC_STATUS_INIT: + dprintk("decoder status: init \n"); + audpp_cmd_cfg_routing_mode(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + dprintk("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + dprintk("decoder status: play \n"); + if (audio->pcm_feedback) { + audamrnb_config_hostpcm(audio); + audamrnb_buffer_refresh(audio); + } + break; + default: + pr_err("unknown decoder status \n"); + break; + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + dprintk("audamrnb_dsp_event: CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_set_volume_and_pan(audio->dec_id, audio->volume, + 0); + audpp_avsync(audio->dec_id, 22050); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + dprintk("audamrnb_dsp_event: CFG_MSG DISABLE\n"); + audpp_avsync(audio->dec_id, 0); + audio->running = 0; + } else { + pr_err("audamrnb_dsp_event: CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + dprintk("audamrnb_dsp_event: ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + + default: + pr_err("audamrnb_dsp_event: UNKNOWN (%d)\n", id); + } + +} + +struct msm_adsp_ops audplay_adsp_ops_amrnb = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + audpp_cmd_cfg_dec_type cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_AMRNB; + else + cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V; + + return audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_amrnb cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = 8000; + cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + dprintk("audpp_cmd_cfg_routing_mode()\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + audplay_cmd_bitstream_data_avail cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL; + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len / 2; + cmd.partition_number = 0; + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audamrnb_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size - + (audio->in[audio->fill_next].size % AMRNB_DECODED_FRSZ); + refresh_cmd.buf_read_count = 0; + dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n", + refresh_cmd.buf0_address, refresh_cmd.buf0_length); + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audamrnb_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + dprintk("audamrnb_config_hostpcm()\n"); + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = audio->pcm_buf_count; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); + +} + +static void audamrnb_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); +/* printk("frame %d busy\n", audio->out_tail); */ + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audamrnb_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->stopped = 0; + atomic_set(&audio->out_bytes, 0); +} + +static void audamrnb_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + + audio->read_next = 0; + audio->fill_next = 0; +} + +static long audamrnb_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = 0; + + dprintk("audamrnb_ioctl() cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = audpp_avsync_byte_count(audio->dec_id); + stats.sample_count = audpp_avsync_sample_count(audio->dec_id); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + if (cmd == AUDIO_SET_VOLUME) { + unsigned long flags; + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->volume = arg; + if (audio->running) + audpp_set_volume_and_pan(audio->dec_id, arg, 0); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + return 0; + } + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + rc = audamrnb_enable(audio); + break; + case AUDIO_STOP: + rc = audamrnb_disable(audio); + audio->stopped = 1; + break; + case AUDIO_FLUSH: + if (audio->stopped) { + /* Make sure we're stopped and we wake any threads + * that might be blocked holding the write_lock. + * While audio->stopped write threads will always + * exit immediately. + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audamrnb_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audamrnb_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); + break; + } + + case AUDIO_SET_CONFIG:{ + dprintk("AUDIO_SET_CONFIG not applicable \n"); + break; + } + case AUDIO_GET_CONFIG:{ + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = 2; + config.sample_rate = 8000; + config.channel_count = 1; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + config.unused[3] = 0; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = 0; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + dprintk("audamrnb_ioctl: allocate PCM buf %d\n", + config.buffer_count * + config.buffer_size); + audio->read_data = + dma_alloc_coherent(NULL, + config.buffer_size * + config.buffer_count, + &audio->read_phys, + GFP_KERNEL); + if (!audio->read_data) { + pr_err("audamrnb_ioctl: no mem for pcm buf\n"); + rc = -1; + } else { + uint8_t index; + uint32_t offset = 0; + audio->pcm_feedback = 1; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + rc = 0; + } + } else { + rc = 0; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audamrnb_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (!audio->pcm_feedback) + return 0; /* PCM feedback is not enabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + dprintk("audamrnb_read() %d \n", count); + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next]. + used > 0) || (audio->stopped)); + + if (rc < 0) + break; + + if (audio->stopped) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver does + * not know frame size, read count must be greater or + * equal to size of PCM samples + */ + dprintk("audamrnb_read:read stop - partial frame\n"); + break; + } else { + dprintk("audamrnb_read: read from in[%d]\n", + audio->read_next); + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + pr_err("audamrnb_read: invalid addr %x \n", + (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + } + } + + if (audio->buf_refresh) { + audio->buf_refresh = 0; + dprintk("audamrnb_read: kick start pcm feedback again\n"); + audamrnb_buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + dprintk("audamrnb_read: read %d bytes\n", rc); + return rc; +} + +static ssize_t audamrnb_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + int rc = 0; + + if (count & 1) + return -EINVAL; + dprintk("audamrnb_write() \n"); + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped)); + dprintk("audamrnb_write() buffer available\n"); + if (rc < 0) + break; + if (audio->stopped) { + rc = -EBUSY; + break; + } + xfer = (count > frame->size) ? frame->size : count; + if (copy_from_user(frame->data, buf, xfer)) { + rc = -EFAULT; + break; + } + + frame->used = xfer; + audio->out_head ^= 1; + count -= xfer; + buf += xfer; + + audamrnb_send_data(audio, 0); + + } + mutex_unlock(&audio->write_lock); + if (buf > start) + return buf - start; + return rc; +} + +static int audamrnb_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + dprintk("audamrnb_release()\n"); + + mutex_lock(&audio->lock); + audamrnb_disable(audio); + audamrnb_flush(audio); + audamrnb_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audio->audplay = NULL; + audio->opened = 0; + dma_free_coherent(NULL, DMASZ, audio->data, audio->phys); + audio->data = NULL; + if (audio->read_data != NULL) { + dma_free_coherent(NULL, + audio->in[0].size * audio->pcm_buf_count, + audio->read_data, audio->read_phys); + audio->read_data = NULL; + } + audio->pcm_feedback = 0; + mutex_unlock(&audio->lock); + return 0; +} + +static struct audio the_amrnb_audio; + +static int audamrnb_open(struct inode *inode, struct file *file) +{ + struct audio *audio = &the_amrnb_audio; + int rc; + + mutex_lock(&audio->lock); + + if (audio->opened) { + pr_err("audio: busy\n"); + rc = -EBUSY; + goto done; + } + + if (!audio->data) { + audio->data = dma_alloc_coherent(NULL, DMASZ, + &audio->phys, GFP_KERNEL); + if (!audio->data) { + pr_err("audio: could not allocate DMA buffers\n"); + rc = -ENOMEM; + goto done; + } + } + + rc = audmgr_open(&audio->audmgr); + if (rc) + goto done; + + rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay, + &audplay_adsp_ops_amrnb, audio); + if (rc) { + pr_err("audio: failed to get audplay0 dsp module\n"); + audmgr_disable(&audio->audmgr); + dma_free_coherent(NULL, DMASZ, audio->data, audio->phys); + audio->data = NULL; + goto done; + } + + audio->dec_id = 0; + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = BUFSZ; + + audio->out[1].data = audio->data + BUFSZ; + audio->out[1].addr = audio->phys + BUFSZ; + audio->out[1].size = BUFSZ; + + audio->volume = 0x2000; /* Q13 1.0 */ + + audamrnb_flush(audio); + + file->private_data = audio; + audio->opened = 1; + rc = 0; +done: + mutex_unlock(&audio->lock); + return rc; +} + +static struct file_operations audio_amrnb_fops = { + .owner = THIS_MODULE, + .open = audamrnb_open, + .release = audamrnb_release, + .read = audamrnb_read, + .write = audamrnb_write, + .unlocked_ioctl = audamrnb_ioctl, +}; + +struct miscdevice audio_amrnb_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_amrnb", + .fops = &audio_amrnb_fops, +}; + +static int __init audamrnb_init(void) +{ + mutex_init(&the_amrnb_audio.lock); + mutex_init(&the_amrnb_audio.write_lock); + mutex_init(&the_amrnb_audio.read_lock); + spin_lock_init(&the_amrnb_audio.dsp_lock); + init_waitqueue_head(&the_amrnb_audio.write_wait); + init_waitqueue_head(&the_amrnb_audio.read_wait); + the_amrnb_audio.read_data = NULL; + return misc_register(&audio_amrnb_misc); +} + +static void __exit audamrnb_exit(void) +{ + misc_deregister(&audio_amrnb_misc); +} + +module_init(audamrnb_init); +module_exit(audamrnb_exit); + +MODULE_DESCRIPTION("MSM AMR-NB driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("QUALCOMM Inc"); diff --git a/arch/arm/mach-msm/qdsp5/audio_evrc.c b/arch/arm/mach-msm/qdsp5/audio_evrc.c new file mode 100644 index 0000000000000..4b43e183f9e86 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_evrc.c @@ -0,0 +1,845 @@ +/* arch/arm/mach-msm/audio_evrc.c + * + * Copyright (c) 2008 QUALCOMM USA, INC. + * + * This code also borrows from audio_aac.c, which is + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * 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. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "audmgr.h" + +#include +#include +#include +#include + +#include "adsp.h" + +#ifdef DEBUG +#define dprintk(format, arg...) \ + printk(KERN_DEBUG format, ## arg) +#else +#define dprintk(format, arg...) do {} while (0) +#endif + +/* Hold 30 packets of 24 bytes each*/ +#define BUFSZ 720 +#define DMASZ (BUFSZ * 2) + +#define AUDDEC_DEC_EVRC 12 + +#define PCM_BUFSZ_MIN 1600 /* 100ms worth of data */ +#define PCM_BUF_MAX_COUNT 5 +/* DSP only accepts 5 buffers at most + * but support 2 buffers currently + */ +#define EVRC_DECODED_FRSZ 320 /* EVRC 20ms 8KHz mono PCM size */ + +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; +}; + +struct audio { + struct buffer out[2]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + + atomic_t out_bytes; + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + dma_addr_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* ---- End of Host PCM section */ + + struct msm_adsp_module *audplay; + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + + uint8_t opened:1; + uint8_t enabled:1; + uint8_t running:1; + uint8_t stopped:1; /* set when stopped, cleared on flush */ + uint8_t pcm_feedback:1; + uint8_t buf_refresh:1; + + unsigned volume; + uint16_t dec_id; + uint32_t read_ptr_offset; +}; +static struct audio the_evrc_audio; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audevrc_send_data(struct audio *audio, unsigned needed); +static void audevrc_dsp_event(void *private, unsigned id, uint16_t *msg); +static void audevrc_config_hostpcm(struct audio *audio); +static void audevrc_buffer_refresh(struct audio *audio); + +/* must be called with audio->lock held */ +static int audevrc_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + if (audio->enabled) + return 0; + + audio->out_tail = 0; + audio->out_needed = 0; + + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK; + cfg.codec = RPC_AUD_DEF_CODEC_EVRC; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + + if (msm_adsp_enable(audio->audplay)) { + pr_err("audio: msm_adsp_enable(audplay) failed\n"); + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audevrc_dsp_event, audio)) { + pr_err("audio: audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audevrc_disable(struct audio *audio) +{ + if (audio->enabled) { + audio->enabled = 0; + auddec_dsp_config(audio, 0); + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + } + return 0; +} + +/* ------------------- dsp --------------------- */ + +static void audevrc_update_pcm_buf_entry(struct audio *audio, + uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr + == payload[2 + index * 2]) { + dprintk("audevrc_update_pcm_buf_entry: in[%d] ready\n", + audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + + } else { + pr_err + ("audevrc_update_pcm_buf_entry: expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audevrc_buffer_refresh(audio); + } else { + dprintk("audevrc_update_pcm_buf_entry: read cannot keep up\n"); + audio->buf_refresh = 1; + } + + spin_unlock_irqrestore(&audio->dsp_lock, flags); + wake_up(&audio->read_wait); +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + dprintk("audplay_dsp_event: msg_id=%x\n", id); + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audevrc_send_data(audio, 1); + break; + case AUDPLAY_MSG_BUFFER_UPDATE: + dprintk("audevrc_update_pcm_buf_entry:======> \n"); + audevrc_update_pcm_buf_entry(audio, msg); + break; + default: + pr_err("unexpected message from decoder \n"); + } +} + +static void audevrc_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: + dprintk("decoder status: sleep \n"); + break; + + case AUDPP_DEC_STATUS_INIT: + dprintk("decoder status: init \n"); + audpp_cmd_cfg_routing_mode(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + dprintk("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + dprintk("decoder status: play \n"); + if (audio->pcm_feedback) { + audevrc_config_hostpcm(audio); + audevrc_buffer_refresh(audio); + } + break; + default: + pr_err("unknown decoder status \n"); + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + dprintk("audevrc_dsp_event: CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_set_volume_and_pan(audio->dec_id, audio->volume, + 0); + audpp_avsync(audio->dec_id, 22050); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + dprintk("audevrc_dsp_event: CFG_MSG DISABLE\n"); + audpp_avsync(audio->dec_id, 0); + audio->running = 0; + } else { + pr_err("audevrc_dsp_event: CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + dprintk("audevrc_dsp_event: ROUTING_ACK\n"); + audpp_cmd_cfg_adec_params(audio); + break; + + default: + pr_err("audevrc_dsp_event: UNKNOWN (%d)\n", id); + } + +} + +struct msm_adsp_ops audplay_adsp_ops_evrc = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + audpp_cmd_cfg_dec_type cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_EVRC; + else + cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V; + + return audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_evrc cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = sizeof(cmd); + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = 8000; + cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + dprintk("audpp_cmd_cfg_routing_mode()\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + audplay_cmd_bitstream_data_avail cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL; + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len / 2; + cmd.partition_number = 0; + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audevrc_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size; + + refresh_cmd.buf_read_count = 0; + dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n", + refresh_cmd.buf0_address, refresh_cmd.buf0_length); + audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audevrc_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + dprintk("audevrc_config_hostpcm()\n"); + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = 1; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); + +} + +static void audevrc_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + dprintk("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + dprintk("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } +done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audevrc_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->stopped = 0; + atomic_set(&audio->out_bytes, 0); +} + +static void audevrc_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + + audio->read_next = 0; + audio->fill_next = 0; +} + +static long audevrc_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = 0; + + dprintk("audevrc_ioctl() cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = audpp_avsync_byte_count(audio->dec_id); + stats.sample_count = audpp_avsync_sample_count(audio->dec_id); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + if (cmd == AUDIO_SET_VOLUME) { + unsigned long flags; + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->volume = arg; + if (audio->running) + audpp_set_volume_and_pan(audio->dec_id, arg, 0); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + return 0; + } + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + rc = audevrc_enable(audio); + break; + case AUDIO_STOP: + rc = audevrc_disable(audio); + audio->stopped = 1; + break; + case AUDIO_SET_CONFIG:{ + dprintk("AUDIO_SET_CONFIG not applicable \n"); + break; + } + case AUDIO_GET_CONFIG:{ + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = 2; + config.sample_rate = 8000; + config.channel_count = 1; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + config.unused[3] = 0; + if (copy_to_user((void *)arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = 0; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + dprintk("audevrc_ioctl: allocate PCM buf %d\n", + config.buffer_count * + config.buffer_size); + audio->read_data = + dma_alloc_coherent(NULL, + config.buffer_size * + config.buffer_count, + &audio->read_phys, + GFP_KERNEL); + if (!audio->read_data) { + pr_err + ("audevrc_ioctl: no mem for pcm buf\n"); + rc = -1; + } else { + uint8_t index; + uint32_t offset = 0; + audio->pcm_feedback = 1; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; + index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + rc = 0; + } + } else { + rc = 0; + } + break; + } + case AUDIO_PAUSE: + dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audevrc_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + if (!audio->pcm_feedback) { + return 0; + /* PCM feedback is not enabled. Nothing to read */ + } + mutex_lock(&audio->read_lock); + dprintk("audevrc_read() \n"); + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next]. + used > 0) || (audio->stopped)); + dprintk("audevrc_read() wait terminated \n"); + if (rc < 0) + break; + if (audio->stopped) { + rc = -EBUSY; + break; + } + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver does + * not know frame size, read count must be greater or + * equal to size of PCM samples + */ + dprintk("audevrc_read:read stop - partial frame\n"); + break; + } else { + dprintk("audevrc_read: read from in[%d]\n", + audio->read_next); + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + pr_err("audevrc_read: invalid addr %x \n", + (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + if (audio->in[audio->read_next].used == 0) + break; /* No data ready at this moment + * Exit while loop to prevent + * output thread sleep too long + */ + + } + } + if (audio->buf_refresh) { + audio->buf_refresh = 0; + dprintk("audevrc_read: kick start pcm feedback again\n"); + audevrc_buffer_refresh(audio); + } + mutex_unlock(&audio->read_lock); + if (buf > start) + rc = buf - start; + dprintk("audevrc_read: read %d bytes\n", rc); + return rc; +} + +static ssize_t audevrc_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + int rc = 0; + + if (count & 1) + return -EINVAL; + mutex_lock(&audio->write_lock); + dprintk("audevrc_write() \n"); + while (count > 0) { + frame = audio->out + audio->out_head; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped)); + if (rc < 0) + break; + if (audio->stopped) { + rc = -EBUSY; + break; + } + xfer = (count > frame->size) ? frame->size : count; + if (copy_from_user(frame->data, buf, xfer)) { + rc = -EFAULT; + break; + } + + frame->used = xfer; + audio->out_head ^= 1; + count -= xfer; + buf += xfer; + + audevrc_send_data(audio, 0); + + } + mutex_unlock(&audio->write_lock); + if (buf > start) + return buf - start; + return rc; +} + +static int audevrc_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + dprintk("audevrc_release()\n"); + + mutex_lock(&audio->lock); + audevrc_disable(audio); + audevrc_flush(audio); + audevrc_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audio->audplay = NULL; + audio->opened = 0; + dma_free_coherent(NULL, DMASZ, audio->data, audio->phys); + audio->data = NULL; + if (audio->read_data != NULL) { + dma_free_coherent(NULL, + audio->in[0].size * audio->pcm_buf_count, + audio->read_data, audio->read_phys); + audio->read_data = NULL; + } + audio->pcm_feedback = 0; + mutex_unlock(&audio->lock); + return 0; +} + +static struct audio the_evrc_audio; + +static int audevrc_open(struct inode *inode, struct file *file) +{ + struct audio *audio = &the_evrc_audio; + int rc; + + if (audio->opened) { + pr_err("audio: busy\n"); + return -EBUSY; + } + + /* Acquire Lock */ + mutex_lock(&audio->lock); + + if (!audio->data) { + audio->data = dma_alloc_coherent(NULL, DMASZ, + &audio->phys, GFP_KERNEL); + if (!audio->data) { + pr_err("audio: could not allocate DMA buffers\n"); + rc = -ENOMEM; + goto dma_fail; + } + } + + rc = audmgr_open(&audio->audmgr); + if (rc) + goto audmgr_fail; + + rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay, + &audplay_adsp_ops_evrc, audio); + if (rc) { + pr_err("audio: failed to get audplay0 dsp module\n"); + goto adsp_fail; + } + + audio->dec_id = 0; + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = BUFSZ; + + audio->out[1].data = audio->data + BUFSZ; + audio->out[1].addr = audio->phys + BUFSZ; + audio->out[1].size = BUFSZ; + + audio->volume = 0x3FFF; + + audevrc_flush(audio); + + audio->opened = 1; + file->private_data = audio; + + mutex_unlock(&audio->lock); + return rc; + +adsp_fail: + audmgr_close(&audio->audmgr); +audmgr_fail: + dma_free_coherent(NULL, DMASZ, audio->data, audio->phys); +dma_fail: + mutex_unlock(&audio->lock); + return rc; +} + +static struct file_operations audio_evrc_fops = { + .owner = THIS_MODULE, + .open = audevrc_open, + .release = audevrc_release, + .read = audevrc_read, + .write = audevrc_write, + .unlocked_ioctl = audevrc_ioctl, +}; + +struct miscdevice audio_evrc_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_evrc", + .fops = &audio_evrc_fops, +}; + +static int __init audevrc_init(void) +{ + mutex_init(&the_evrc_audio.lock); + mutex_init(&the_evrc_audio.write_lock); + mutex_init(&the_evrc_audio.read_lock); + spin_lock_init(&the_evrc_audio.dsp_lock); + init_waitqueue_head(&the_evrc_audio.write_wait); + init_waitqueue_head(&the_evrc_audio.read_wait); + the_evrc_audio.read_data = NULL; + return misc_register(&audio_evrc_misc); +} + +static void __exit audevrc_exit(void) +{ + misc_deregister(&audio_evrc_misc); +} + +module_init(audevrc_init); +module_exit(audevrc_exit); + +MODULE_DESCRIPTION("MSM EVRC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("QUALCOMM Inc"); diff --git a/arch/arm/mach-msm/qdsp5/audio_in.c b/arch/arm/mach-msm/qdsp5/audio_in.c index b3311761c757b..35a7f7df06829 100644 --- a/arch/arm/mach-msm/qdsp5/audio_in.c +++ b/arch/arm/mach-msm/qdsp5/audio_in.c @@ -257,13 +257,13 @@ static void audpre_dsp_event(void *data, unsigned id, size_t len, break; case AUDPREPROC_MSG_ERROR_MSG_ID: pr_info("audpre: err_index %d\n", msg[0]); + break; default: pr_err("audpre: unknown event %d\n", id); } } -struct audio_frame -{ +struct audio_frame { uint16_t count_low; uint16_t count_high; uint16_t bytes; @@ -333,7 +333,7 @@ static void audrec_dsp_event(void *data, unsigned id, size_t len, pr_err("audrec: ERROR %x\n", msg[0]); break; case AUDREC_MSG_PACKET_READY_MSG: - //REC_DBG("type %x, count %d", msg[0], (msg[1] | (msg[2] << 16))); +/* REC_DBG("type %x, count %d", msg[0], (msg[1] | (msg[2] << 16))); */ audio_in_get_dsp_frames(audio); break; default: @@ -350,12 +350,13 @@ struct msm_adsp_ops audrec_adsp_ops = { }; -#define audio_send_queue_pre(audio,cmd,len) \ +#define audio_send_queue_pre(audio, cmd, len) \ msm_adsp_write(audio->audpre, QDSP_uPAudPreProcCmdQueue, cmd, len) -#define audio_send_queue_recbs(audio,cmd,len) \ +#define audio_send_queue_recbs(audio, cmd, len) \ msm_adsp_write(audio->audrec, QDSP_uPAudRecBitStreamQueue, cmd, len) -#define audio_send_queue_rec(audio,cmd,len) \ - msm_adsp_write(audio->audrec, QDSP_uPAudRecCmdQueue, cmd, len) +#define audio_send_queue_rec(audio, cmd, len) \ + msm_adsp_write(audio->audrec, \ + QDSP_uPAudRecCmdQueue, cmd, len) static int audio_dsp_set_agc(struct audio_in *audio) { @@ -367,13 +368,13 @@ static int audio_dsp_set_agc(struct audio_in *audio) if (audio->agc_enable) { /* cmd.tx_agc_param_mask = 0xFE00 from sample code */ cmd.tx_agc_param_mask = - (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_SLOPE) | - (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_TH) | - (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_SLOPE) | - (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_TH) | - (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_AIG_FLAG) | - (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_STATIC_GAIN) | - (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG); + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_SLOPE) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_TH) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_SLOPE) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_EXP_TH) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_AIG_FLAG) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_COMP_STATIC_GAIN) | + (1 << AUDPREPROC_CMD_TX_AGC_PARAM_MASK_TX_AGC_ENA_FLAG); cmd.tx_agc_enable_flag = AUDPREPROC_CMD_TX_AGC_ENA_FLAG_ENA; memcpy(&cmd.static_gain, &audio->agc.agc_params[0], @@ -531,12 +532,14 @@ static int audio_in_encoder_config(struct audio_in *audio) /* prepare buffer pointers: * Mono: 1024 samples + 4 halfword header * Stereo: 2048 samples + 4 halfword header + * AAC + * Mono/Stere: 768 + 4 halfword header */ for (n = 0; n < FRAME_NUM; n++) { audio->in[n].data = data + 4; if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV) data += (4 + (audio->channel_mode ? 2048 : 1024)); - else + else if (audio->type == AUDREC_CMD_TYPE_0_INDEX_AAC) data += (4 + 768); } @@ -549,7 +552,8 @@ static int audio_dsp_read_buffer(struct audio_in *audio, uint32_t read_cnt) memset(&cmd, 0, sizeof(cmd)); cmd.cmd_id = AUDREC_CMD_PACKET_EXT_PTR; - cmd.type = AUDREC_CMD_TYPE_0; /* Both WAV and AAC use AUDREC_CMD_TYPE_0 */ + /* Both WAV and AAC use AUDREC_CMD_TYPE_0 */ + cmd.type = AUDREC_CMD_TYPE_0; cmd.curr_rec_count_msw = read_cnt >> 16; cmd.curr_rec_count_lsw = read_cnt; @@ -599,7 +603,8 @@ static void audio_flush(struct audio_in *audio) } } -static long audio_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static long audio_in_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) { struct audio_in *audio = file->private_data; int rc; @@ -607,7 +612,7 @@ static long audio_in_ioctl(struct file *file, unsigned int cmd, unsigned long ar if (cmd == AUDIO_GET_STATS) { struct msm_audio_stats stats; stats.byte_count = atomic_read(&audio->in_bytes); - if (copy_to_user((void*) arg, &stats, sizeof(stats))) + if (copy_to_user((void *) arg, &stats, sizeof(stats))) return -EFAULT; return 0; } @@ -635,7 +640,7 @@ static long audio_in_ioctl(struct file *file, unsigned int cmd, unsigned long ar } case AUDIO_SET_CONFIG: { struct msm_audio_config cfg; - if (copy_from_user(&cfg, (void*) arg, sizeof(cfg))) { + if (copy_from_user(&cfg, (void *) arg, sizeof(cfg))) { rc = -EFAULT; break; } @@ -657,10 +662,12 @@ static long audio_in_ioctl(struct file *file, unsigned int cmd, unsigned long ar break; } audio->samp_rate = convert_samp_rate(cfg.sample_rate); - audio->samp_rate_index = convert_dsp_samp_index(cfg.sample_rate); + audio->samp_rate_index = + convert_dsp_samp_index(cfg.sample_rate); audio->channel_mode = cfg.channel_count; audio->buffer_size = - audio->channel_mode ? STEREO_DATA_SIZE : MONO_DATA_SIZE; + audio->channel_mode ? STEREO_DATA_SIZE + : MONO_DATA_SIZE; audio->type = cfg.type; rc = 0; break; @@ -670,25 +677,21 @@ static long audio_in_ioctl(struct file *file, unsigned int cmd, unsigned long ar cfg.buffer_size = audio->buffer_size; cfg.buffer_count = FRAME_NUM; cfg.sample_rate = convert_samp_index(audio->samp_rate); - if (audio->channel_mode == AUDREC_CMD_STEREO_MODE_MONO) { + if (audio->channel_mode == AUDREC_CMD_STEREO_MODE_MONO) cfg.channel_count = 1; - } else { + else cfg.channel_count = 2; - } - - if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV) { + if (audio->type == AUDREC_CMD_TYPE_0_INDEX_WAV) cfg.type = 0; - } else { + else cfg.type = 1; - } cfg.unused[0] = 0; cfg.unused[1] = 0; cfg.unused[2] = 0; - if (copy_to_user((void*) arg, &cfg, sizeof(cfg))) { + if (copy_to_user((void *) arg, &cfg, sizeof(cfg))) rc = -EFAULT; - } else { + else rc = 0; - } break; } default: @@ -698,7 +701,9 @@ static long audio_in_ioctl(struct file *file, unsigned int cmd, unsigned long ar return rc; } -static ssize_t audio_in_read(struct file *file, char __user *buf, size_t count, loff_t *pos) +static ssize_t audio_in_read(struct file *file, + char __user *buf, + size_t count, loff_t *pos) { struct audio_in *audio = file->private_data; unsigned long flags; @@ -730,7 +735,7 @@ static ssize_t audio_in_read(struct file *file, char __user *buf, size_t count, } spin_lock_irqsave(&audio->dsp_lock, flags); if (index != audio->in_tail) { - /* overrun -- data is invalid and we need to retry */ + /* overrun -- data is invalid and we need to retry */ spin_unlock_irqrestore(&audio->dsp_lock, flags); continue; } @@ -746,6 +751,8 @@ static ssize_t audio_in_read(struct file *file, char __user *buf, size_t count, pr_err("audio_in: short read\n"); break; } + if (audio->type == AUDREC_CMD_TYPE_0_INDEX_AAC) + break; /* AAC only read one frame */ } mutex_unlock(&audio->read_lock); @@ -756,9 +763,8 @@ static ssize_t audio_in_read(struct file *file, char __user *buf, size_t count, } static ssize_t audio_in_write(struct file *file, - const char __user *buf, - size_t count, - loff_t *pos) + const char __user *buf, + size_t count, loff_t *pos) { return -EINVAL; } @@ -809,7 +815,7 @@ static int audio_in_open(struct inode *inode, struct file *file) if (rc) goto done; rc = msm_adsp_get("AUDRECTASK", &audio->audrec, - &audrec_adsp_ops, audio); + &audrec_adsp_ops, audio); if (rc) goto done; diff --git a/arch/arm/mach-msm/qdsp5/audio_mp3.c b/arch/arm/mach-msm/qdsp5/audio_mp3.c index b09ca7911dd8d..657390403a397 100644 --- a/arch/arm/mach-msm/qdsp5/audio_mp3.c +++ b/arch/arm/mach-msm/qdsp5/audio_mp3.c @@ -42,24 +42,37 @@ /* for queue ids - should be relative to module number*/ #include "adsp.h" -#define BUFSZ 32768 -#define DMASZ (BUFSZ * 2) +#ifdef DEBUG +#define dprintk(format, arg...) \ +printk(KERN_DEBUG format, ## arg) +#else +#define dprintk(format, arg...) do {} while (0) +#endif + +/* Size must be power of 2 */ +#define BUFSZ_MAX 32768 +#define BUFSZ_MIN 4096 +#define DMASZ_MAX (BUFSZ_MAX * 2) +#define DMASZ_MIN (BUFSZ_MIN * 2) #define AUDPLAY_INVALID_READ_PTR_OFFSET 0xFFFF #define AUDDEC_DEC_MP3 2 +#define PCM_BUFSZ_MIN 4800 /* Hold one stereo MP3 frame */ +#define PCM_BUF_MAX_COUNT 5 /* DSP only accepts 5 buffers at most + but support 2 buffers currently */ +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 /* Decoder status received from AUDPPTASK */ -typedef enum { - AUDPP_DEC_STATUS_SLEEP, - AUDPP_DEC_STATUS_INIT, - AUDPP_DEC_STATUS_CFG, - AUDPP_DEC_STATUS_PLAY -} audpp_dec_status_type; +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 struct buffer { void *data; unsigned size; - unsigned used; + unsigned used; /* Input usage actual DSP produced PCM size */ unsigned addr; }; @@ -71,12 +84,24 @@ struct audio { uint8_t out_head; uint8_t out_tail; uint8_t out_needed; /* number of buffers the dsp is waiting for */ + unsigned out_dma_sz; atomic_t out_bytes; struct mutex lock; struct mutex write_lock; - wait_queue_head_t wait; + wait_queue_head_t write_wait; + + /* Host PCM section */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + dma_addr_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* ---- End of Host PCM section */ struct msm_adsp_module *audplay; @@ -90,10 +115,18 @@ struct audio { char *data; dma_addr_t phys; + int rflush; /* Read flush */ + int wflush; /* Write flush */ int opened; int enabled; int running; int stopped; /* set when stopped, cleared on flush */ + int pcm_feedback; + int buf_refresh; + + int reserved; /* A byte is being reserved */ + char rsv_byte; /* Handle odd length user data */ + unsigned volume; uint16_t dec_id; @@ -102,8 +135,10 @@ struct audio { static int auddec_dsp_config(struct audio *audio, int enable); static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); static void audplay_send_data(struct audio *audio, unsigned needed); - +static void audplay_config_hostpcm(struct audio *audio); +static void audplay_buffer_refresh(struct audio *audio); static void audio_dsp_event(void *private, unsigned id, uint16_t *msg); /* must be called with audio->lock held */ @@ -130,14 +165,15 @@ static int audio_enable(struct audio *audio) if (rc < 0) return rc; - if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) { - pr_err("audio: audpp_enable() failed\n"); + if (msm_adsp_enable(audio->audplay)) { + pr_err("audio: msm_adsp_enable(audplay) failed\n"); audmgr_disable(&audio->audmgr); return -ENODEV; } - if (msm_adsp_enable(audio->audplay)) { - pr_err("audio: msm_adsp_enable(audplay) failed\n"); + if (audpp_enable(audio->dec_id, audio_dsp_event, audio)) { + pr_err("audio: audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); audmgr_disable(&audio->audmgr); return -ENODEV; } @@ -153,7 +189,8 @@ static int audio_disable(struct audio *audio) if (audio->enabled) { audio->enabled = 0; auddec_dsp_config(audio, 0); - wake_up(&audio->wait); + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); msm_adsp_disable(audio->audplay); audpp_disable(audio->dec_id, audio); audmgr_disable(&audio->audmgr); @@ -163,18 +200,63 @@ static int audio_disable(struct audio *audio) } /* ------------------- dsp --------------------- */ +static void audio_update_pcm_buf_entry(struct audio *audio, uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + if (audio->rflush) { + audio->buf_refresh = 1; + return; + } + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + pr_info("audio_update_pcm_buf_entry: in[%d] ready\n", + audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + + } else { + pr_err + ("audio_update_pcm_buf_entry: expected=%x ret=%x\n" + , audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audplay_buffer_refresh(audio); + } else { + pr_info("audio_update_pcm_buf_entry: read cannot keep up\n"); + audio->buf_refresh = 1; + } + wake_up(&audio->read_wait); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + +} static void audplay_dsp_event(void *data, unsigned id, size_t len, - void (*getevent)(void *ptr, size_t len)) + void (*getevent) (void *ptr, size_t len)) { struct audio *audio = data; uint32_t msg[28]; getevent(msg, sizeof(msg)); + dprintk("audplay_dsp_event: msg_id=%x\n", id); + switch (id) { case AUDPLAY_MSG_DEC_NEEDS_DATA: audplay_send_data(audio, 1); break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audio_update_pcm_buf_entry(audio, msg); + break; + default: pr_err("unexpected message from decoder \n"); break; @@ -186,37 +268,43 @@ static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) struct audio *audio = private; switch (id) { - case AUDPP_MSG_STATUS_MSG: { - unsigned status = msg[1]; - - switch (status) { - case AUDPP_DEC_STATUS_SLEEP: - pr_info("decoder status: sleep \n"); - break; - case AUDPP_DEC_STATUS_INIT: { - pr_info("decoder status: init \n"); - audpp_cmd_cfg_adec_params(audio); - break; - } - case AUDPP_DEC_STATUS_CFG: - pr_info("decoder status: cfg \n"); - break; - case AUDPP_DEC_STATUS_PLAY: - pr_info("decoder status: play \n"); - break; - default: - pr_err("unknown decoder status \n"); - break; + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: + pr_info("decoder status: sleep \n"); + break; + + case AUDPP_DEC_STATUS_INIT: + pr_info("decoder status: init \n"); + audpp_cmd_cfg_routing_mode(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + pr_info("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + pr_info("decoder status: play \n"); + if (audio->pcm_feedback) { + audplay_config_hostpcm(audio); + audplay_buffer_refresh(audio); + } + break; + default: + pr_err("unknown decoder status \n"); + break; + } + break; } - break; - } case AUDPP_MSG_CFG_MSG: if (msg[0] == AUDPP_MSG_ENA_ENA) { pr_info("audio_dsp_event: CFG_MSG ENABLE\n"); auddec_dsp_config(audio, 1); audio->out_needed = 0; audio->running = 1; - audpp_set_volume_and_pan(audio->dec_id, audio->volume, 0); + audpp_set_volume_and_pan(audio->dec_id, audio->volume, + 0); audpp_avsync(audio->dec_id, 22050); } else if (msg[0] == AUDPP_MSG_ENA_DIS) { pr_info("audio_dsp_event: CFG_MSG DISABLE\n"); @@ -226,6 +314,19 @@ static void audio_dsp_event(void *private, unsigned id, uint16_t *msg) pr_err("audio_dsp_event: CFG_MSG %d?\n", msg[0]); } break; + case AUDPP_MSG_ROUTING_ACK: + pr_info("audio_dsp_event: ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + + case AUDPP_MSG_FLUSH_ACK: + dprintk("%s: FLUSH_ACK\n", __func__); + audio->wflush = 0; + audio->rflush = 0; + if (audio->pcm_feedback) + audplay_buffer_refresh(audio); + break; + default: pr_err("audio_dsp_event: UNKNOWN (%d)\n", id); } @@ -239,7 +340,8 @@ struct msm_adsp_ops audplay_adsp_ops = { #define audplay_send_queue0(audio, cmd, len) \ - msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, cmd, len) + msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \ + cmd, len) static int auddec_dsp_config(struct audio *audio, int enable) { @@ -271,6 +373,21 @@ static void audpp_cmd_cfg_adec_params(struct audio *audio) audpp_send_queue2(&cmd, sizeof(cmd)); } +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + pr_info("audpp_cmd_cfg_routing_mode()\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + + audpp_send_queue1(&cmd, sizeof(cmd)); +} + static int audplay_dsp_send_data_avail(struct audio *audio, unsigned idx, unsigned len) { @@ -284,6 +401,36 @@ static int audplay_dsp_send_data_avail(struct audio *audio, return audplay_send_queue0(audio, &cmd, sizeof(cmd)); } +static void audplay_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size - + (audio->in[audio->fill_next].size % 576); /* Mp3 frame size */ + refresh_cmd.buf_read_count = 0; + pr_info("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n", + refresh_cmd.buf0_address, refresh_cmd.buf0_length); + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audplay_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + pr_info("audplay_config_hostpcm()\n"); + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = 1; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); + +} + static void audplay_send_data(struct audio *audio, unsigned needed) { struct buffer *frame; @@ -293,7 +440,12 @@ static void audplay_send_data(struct audio *audio, unsigned needed) if (!audio->running) goto done; - if (needed) { + if (audio->wflush) { + audio->out_needed = 1; + goto done; + } + + if (needed && !audio->wflush) { /* We were called from the callback because the DSP * requested more data. Note that the DSP does want * more data, and if a buffer was in-flight, mark it @@ -303,9 +455,10 @@ static void audplay_send_data(struct audio *audio, unsigned needed) audio->out_needed = 1; frame = audio->out + audio->out_tail; if (frame->used == 0xffffffff) { - frame->used = 0; - audio->out_tail ^= 1; - wake_up(&audio->wait); + dprintk("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); } } @@ -319,10 +472,12 @@ static void audplay_send_data(struct audio *audio, unsigned needed) frame = audio->out + audio->out_tail; if (frame->used) { - BUG_ON(frame->used == 0xffffffff); - audplay_dsp_send_data_avail(audio, audio->out_tail, frame->used); - frame->used = 0xffffffff; - audio->out_needed = 0; + BUG_ON(frame->used == 0xffffffff); + dprintk("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; } } done: @@ -337,14 +492,43 @@ static void audio_flush(struct audio *audio) audio->out[1].used = 0; audio->out_head = 0; audio->out_tail = 0; - audio->stopped = 0; + audio->reserved = 0; atomic_set(&audio->out_bytes, 0); } +static void audio_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + + audio->read_next = 0; + audio->fill_next = 0; +} + +static void audio_ioport_reset(struct audio *audio) +{ + /* Make sure read/write thread are free from + * sleep and knowing that system is not able + * to process io request at the moment + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audio_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audio_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); +} + static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct audio *audio = file->private_data; - int rc; + int rc = 0; + + pr_info("audio_ioctl() cmd = %d\n", cmd); if (cmd == AUDIO_GET_STATS) { struct msm_audio_stats stats; @@ -361,6 +545,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (audio->running) audpp_set_volume_and_pan(audio->dec_id, arg, 0); spin_unlock_irqrestore(&audio->dsp_lock, flags); + return 0; } mutex_lock(&audio->lock); switch (cmd) { @@ -370,19 +555,23 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case AUDIO_STOP: rc = audio_disable(audio); audio->stopped = 1; + audio_ioport_reset(audio); + audio->stopped = 0; break; case AUDIO_FLUSH: - if (audio->stopped) { - /* Make sure we're stopped and we wake any threads - * that might be blocked holding the write_lock. - * While audio->stopped write threads will always - * exit immediately. - */ - wake_up(&audio->wait); - mutex_lock(&audio->write_lock); - audio_flush(audio); - mutex_unlock(&audio->write_lock); + dprintk("%s: AUDIO_FLUSH\n", __func__); + audio->rflush = 1; + audio->wflush = 1; + audio_ioport_reset(audio); + audio->rflush = 0; + audio->wflush = 0; + + if (audio->buf_refresh) { + audio->buf_refresh = 0; + audplay_buffer_refresh(audio); } + break; + case AUDIO_SET_CONFIG: { struct msm_audio_config config; if (copy_from_user(&config, (void *) arg, sizeof(config))) { @@ -404,7 +593,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } case AUDIO_GET_CONFIG: { struct msm_audio_config config; - config.buffer_size = BUFSZ; + config.buffer_size = (audio->out_dma_sz >> 1); config.buffer_count = 2; config.sample_rate = audio->out_sample_rate; if (audio->out_channel_mode == AUDPP_CMD_PCM_INTF_MONO_V) { @@ -423,6 +612,80 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } break; } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + config.pcm_feedback = 0; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + if (copy_from_user + (&config, (void *)arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + pr_info("ioctl: allocate PCM buffer %d\n", + config.buffer_count * + config.buffer_size); + audio->read_data = + dma_alloc_coherent(NULL, + config.buffer_size * + config.buffer_count, + &audio->read_phys, + GFP_KERNEL); + if (!audio->read_data) { + pr_err("audio_mp3: malloc pcm \ + buf failed\n"); + rc = -1; + } else { + uint8_t index; + uint32_t offset = 0; + audio->pcm_feedback = 1; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; + index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + rc = 0; + } + } else { + rc = 0; + } + break; + } + case AUDIO_PAUSE: + dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; default: rc = -EINVAL; } @@ -430,9 +693,81 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return rc; } -static ssize_t audio_read(struct file *file, char __user *buf, size_t count, loff_t *pos) +static ssize_t audio_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) { - return -EINVAL; + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (!audio->pcm_feedback) + return 0; /* PCM feedback disabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + pr_info("audio_read() %d \n", count); + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next]. + used > 0) || (audio->stopped) + || (audio->rflush)); + + if (rc < 0) + break; + + if (audio->stopped || audio->rflush) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since + * driver does not know frame size, read count + * must be greater or equal + * to size of PCM samples + */ + pr_info("audio_read: no partial frame done reading\n"); + break; + } else { + pr_info("audio_read: read from in[%d]\n", + audio->read_next); + if (copy_to_user + (buf, audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + pr_err("audio_read: invalid addr %x \n", + (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + if (audio->in[audio->read_next].used == 0) + break; /* No data ready at this moment + * Exit while loop to prevent + * output thread sleep too long + */ + } + } + + /* don't feed output buffer to HW decoder during flushing + * buffer refresh command will be sent once flush completes + * send buf refresh command here can confuse HW decoder + */ + if (audio->buf_refresh && !audio->rflush) { + audio->buf_refresh = 0; + pr_info("audio_read: kick start pcm feedback again\n"); + audplay_buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + pr_info("audio_read: read %d bytes\n", rc); + return rc; } static ssize_t audio_write(struct file *file, const char __user *buf, @@ -442,35 +777,59 @@ static ssize_t audio_write(struct file *file, const char __user *buf, const char __user *start = buf; struct buffer *frame; size_t xfer; + char *cpy_ptr; int rc = 0; - - if (count & 1) - return -EINVAL; + unsigned dsize; mutex_lock(&audio->write_lock); while (count > 0) { frame = audio->out + audio->out_head; - rc = wait_event_interruptible(audio->wait, - (frame->used == 0) || (audio->stopped)); + cpy_ptr = frame->data; + dsize = 0; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped) + || (audio->wflush)); if (rc < 0) break; - if (audio->stopped) { + if (audio->stopped || audio->wflush) { rc = -EBUSY; break; } - xfer = (count > frame->size) ? frame->size : count; - if (copy_from_user(frame->data, buf, xfer)) { + + if (audio->reserved) { + dprintk("%s: append reserved byte %x\n", + __func__, audio->rsv_byte); + *cpy_ptr = audio->rsv_byte; + xfer = (count > (frame->size - 1)) ? + frame->size - 1 : count; + cpy_ptr++; + dsize = 1; + audio->reserved = 0; + } else + xfer = (count > frame->size) ? frame->size : count; + + if (copy_from_user(cpy_ptr, buf, xfer)) { rc = -EFAULT; break; } - frame->used = xfer; - audio->out_head ^= 1; + dsize += xfer; + if (dsize & 1) { + audio->rsv_byte = ((char *) frame->data)[dsize - 1]; + dprintk("%s: odd length buf reserve last byte %x\n", + __func__, audio->rsv_byte); + audio->reserved = 1; + dsize--; + } count -= xfer; buf += xfer; - audplay_send_data(audio, 0); - + if (dsize > 0) { + audio->out_head ^= 1; + frame->used = dsize; + audplay_send_data(audio, 0); + } } mutex_unlock(&audio->write_lock); if (buf > start) @@ -482,12 +841,25 @@ static int audio_release(struct inode *inode, struct file *file) { struct audio *audio = file->private_data; + dprintk("audio_release()\n"); + mutex_lock(&audio->lock); audio_disable(audio); audio_flush(audio); + audio_flush_pcm_buf(audio); msm_adsp_put(audio->audplay); audio->audplay = NULL; audio->opened = 0; + audio->reserved = 0; + dma_free_coherent(NULL, audio->out_dma_sz, audio->data, audio->phys); + audio->data = NULL; + if (audio->read_data != NULL) { + dma_free_coherent(NULL, + audio->in[0].size * audio->pcm_buf_count, + audio->read_data, audio->read_phys); + audio->read_data = NULL; + } + audio->pcm_feedback = 0; mutex_unlock(&audio->lock); return 0; } @@ -498,6 +870,7 @@ static int audio_open(struct inode *inode, struct file *file) { struct audio *audio = &the_mp3_audio; int rc; + unsigned pmem_sz; mutex_lock(&audio->lock); @@ -507,38 +880,56 @@ static int audio_open(struct inode *inode, struct file *file) goto done; } - if (!audio->data) { - audio->data = dma_alloc_coherent(NULL, DMASZ, + pmem_sz = DMASZ_MAX; + + while (pmem_sz >= DMASZ_MIN) { + audio->data = dma_alloc_coherent(NULL, pmem_sz, &audio->phys, GFP_KERNEL); - if (!audio->data) { + if (audio->data) + break; + else if (pmem_sz == DMASZ_MIN) { pr_err("audio: could not allocate DMA buffers\n"); rc = -ENOMEM; goto done; - } + } else + pmem_sz >>= 1; } + dprintk("%s: allocated %d bytes DMA buffer\n", __func__, pmem_sz); + rc = audmgr_open(&audio->audmgr); - if (rc) + if (rc) { + dma_free_coherent(NULL, pmem_sz, + audio->data, audio->phys); goto done; + } - rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay, &audplay_adsp_ops, audio); + rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay, &audplay_adsp_ops, + audio); if (rc) { pr_err("audio: failed to get audplay0 dsp module\n"); + dma_free_coherent(NULL, pmem_sz, + audio->data, audio->phys); + audmgr_close(&audio->audmgr); goto done; } + + audio->out_dma_sz = pmem_sz; + pmem_sz >>= 1; /* Shift by 1 to get size of ping pong buffer */ + audio->out_sample_rate = 44100; audio->out_channel_mode = AUDPP_CMD_PCM_INTF_STEREO_V; audio->dec_id = 0; audio->out[0].data = audio->data + 0; audio->out[0].addr = audio->phys + 0; - audio->out[0].size = BUFSZ; + audio->out[0].size = pmem_sz; - audio->out[1].data = audio->data + BUFSZ; - audio->out[1].addr = audio->phys + BUFSZ; - audio->out[1].size = BUFSZ; + audio->out[1].data = audio->data + pmem_sz; + audio->out[1].addr = audio->phys + pmem_sz; + audio->out[1].size = pmem_sz; - audio->volume = 0x2000; /* Q13 1.0 */ + audio->volume = 0x2000; /* equal to Q13 number 1.0 Unit Gain */ audio_flush(audio); @@ -569,8 +960,11 @@ static int __init audio_init(void) { mutex_init(&the_mp3_audio.lock); mutex_init(&the_mp3_audio.write_lock); + mutex_init(&the_mp3_audio.read_lock); spin_lock_init(&the_mp3_audio.dsp_lock); - init_waitqueue_head(&the_mp3_audio.wait); + init_waitqueue_head(&the_mp3_audio.write_wait); + init_waitqueue_head(&the_mp3_audio.read_wait); + the_mp3_audio.read_data = NULL; return misc_register(&audio_mp3_misc); } diff --git a/arch/arm/mach-msm/qdsp5/audio_qcelp.c b/arch/arm/mach-msm/qdsp5/audio_qcelp.c new file mode 100644 index 0000000000000..f0f50e36805a3 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audio_qcelp.c @@ -0,0 +1,856 @@ +/* arch/arm/mach-msm/qdsp5/audio_qcelp.c + * + * qcelp 13k audio decoder device + * + * Copyright (c) 2008 QUALCOMM USA, INC. + * + * This code is based in part on audio_mp3.c, which is + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC Corporation + * + * 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. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "audmgr.h" +/* for queue ids - should be relative to module number*/ +#include "adsp.h" + +#ifdef DEBUG +#define dprintk(format, arg...) \ +printk(KERN_DEBUG format, ## arg) +#else +#define dprintk(format, arg...) do {} while (0) +#endif + +#define BUFSZ 1080 /* QCELP 13K Hold 600ms packet data = 36 * 30 */ +#define BUF_COUNT 2 +#define DMASZ (BUFSZ * BUF_COUNT) + +#define PCM_BUFSZ_MIN 1600 /* 100ms worth of data */ +#define PCM_BUF_MAX_COUNT 5 + +#define AUDDEC_DEC_QCELP 9 + +#define ROUTING_MODE_FTRT 1 +#define ROUTING_MODE_RT 2 +/* Decoder status received from AUDPPTASK */ +#define AUDPP_DEC_STATUS_SLEEP 0 +#define AUDPP_DEC_STATUS_INIT 1 +#define AUDPP_DEC_STATUS_CFG 2 +#define AUDPP_DEC_STATUS_PLAY 3 + +struct buffer { + void *data; + unsigned size; + unsigned used; /* Input usage actual DSP produced PCM size */ + unsigned addr; +}; + +struct audio { + struct buffer out[BUF_COUNT]; + + spinlock_t dsp_lock; + + uint8_t out_head; + uint8_t out_tail; + uint8_t out_needed; /* number of buffers the dsp is waiting for */ + + struct mutex lock; + struct mutex write_lock; + wait_queue_head_t write_wait; + + /* Host PCM section - START */ + struct buffer in[PCM_BUF_MAX_COUNT]; + struct mutex read_lock; + wait_queue_head_t read_wait; /* Wait queue for read */ + char *read_data; /* pointer to reader buffer */ + dma_addr_t read_phys; /* physical address of reader buffer */ + uint8_t read_next; /* index to input buffers to be read next */ + uint8_t fill_next; /* index to buffer that DSP should be filling */ + uint8_t pcm_buf_count; /* number of pcm buffer allocated */ + /* Host PCM section - END */ + + struct msm_adsp_module *audplay; + + struct audmgr audmgr; + + /* data allocated for various buffers */ + char *data; + dma_addr_t phys; + + uint8_t opened:1; + uint8_t enabled:1; + uint8_t running:1; + uint8_t stopped:1; /* set when stopped, cleared on flush */ + uint8_t pcm_feedback:1; /* set when non-tunnel mode */ + uint8_t buf_refresh:1; + + unsigned volume; + + uint16_t dec_id; +}; + +static struct audio the_qcelp_audio; + +static int auddec_dsp_config(struct audio *audio, int enable); +static void audpp_cmd_cfg_adec_params(struct audio *audio); +static void audpp_cmd_cfg_routing_mode(struct audio *audio); +static void audqcelp_send_data(struct audio *audio, unsigned needed); +static void audqcelp_config_hostpcm(struct audio *audio); +static void audqcelp_buffer_refresh(struct audio *audio); +static void audqcelp_dsp_event(void *private, unsigned id, uint16_t *msg); + +/* must be called with audio->lock held */ +static int audqcelp_enable(struct audio *audio) +{ + struct audmgr_config cfg; + int rc; + + dprintk("audqcelp_enable()\n"); + + if (audio->enabled) + return 0; + + audio->out_tail = 0; + audio->out_needed = 0; + + cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_NONE; + cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_48000; + cfg.def_method = RPC_AUD_DEF_METHOD_PLAYBACK; + cfg.codec = RPC_AUD_DEF_CODEC_13K; + cfg.snd_method = RPC_SND_METHOD_MIDI; + + rc = audmgr_enable(&audio->audmgr, &cfg); + if (rc < 0) + return rc; + + if (msm_adsp_enable(audio->audplay)) { + pr_err("audio: msm_adsp_enable(audplay) failed\n"); + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + + if (audpp_enable(audio->dec_id, audqcelp_dsp_event, audio)) { + pr_err("audio: audpp_enable() failed\n"); + msm_adsp_disable(audio->audplay); + audmgr_disable(&audio->audmgr); + return -ENODEV; + } + audio->enabled = 1; + return 0; +} + +/* must be called with audio->lock held */ +static int audqcelp_disable(struct audio *audio) +{ + dprintk("audqcelp_disable()\n"); + if (audio->enabled) { + audio->enabled = 0; + auddec_dsp_config(audio, 0); + wake_up(&audio->write_wait); + wake_up(&audio->read_wait); + msm_adsp_disable(audio->audplay); + audpp_disable(audio->dec_id, audio); + audmgr_disable(&audio->audmgr); + audio->out_needed = 0; + } + return 0; +} + +/* ------------------- dsp --------------------- */ +static void audqcelp_update_pcm_buf_entry(struct audio *audio, + uint32_t *payload) +{ + uint8_t index; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + for (index = 0; index < payload[1]; index++) { + if (audio->in[audio->fill_next].addr == + payload[2 + index * 2]) { + dprintk("audqcelp_update_pcm_buf_entry: in[%d] ready\n", + audio->fill_next); + audio->in[audio->fill_next].used = + payload[3 + index * 2]; + if ((++audio->fill_next) == audio->pcm_buf_count) + audio->fill_next = 0; + } else { + pr_err( + "audqcelp_update_pcm_buf_entry: expected=%x ret=%x\n", + audio->in[audio->fill_next].addr, + payload[1 + index * 2]); + break; + } + } + if (audio->in[audio->fill_next].used == 0) { + audqcelp_buffer_refresh(audio); + } else { + dprintk("audqcelp_update_pcm_buf_entry: read cannot keep up\n"); + audio->buf_refresh = 1; + } + + spin_unlock_irqrestore(&audio->dsp_lock, flags); + wake_up(&audio->read_wait); +} + +static void audplay_dsp_event(void *data, unsigned id, size_t len, + void (*getevent) (void *ptr, size_t len)) +{ + struct audio *audio = data; + uint32_t msg[28]; + getevent(msg, sizeof(msg)); + + dprintk("audplay_dsp_event: msg_id=%x\n", id); + + switch (id) { + case AUDPLAY_MSG_DEC_NEEDS_DATA: + audqcelp_send_data(audio, 1); + break; + + case AUDPLAY_MSG_BUFFER_UPDATE: + audqcelp_update_pcm_buf_entry(audio, msg); + break; + + default: + pr_err("unexpected message from decoder \n"); + } +} + +static void audqcelp_dsp_event(void *private, unsigned id, uint16_t *msg) +{ + struct audio *audio = private; + + switch (id) { + case AUDPP_MSG_STATUS_MSG:{ + unsigned status = msg[1]; + + switch (status) { + case AUDPP_DEC_STATUS_SLEEP: + dprintk("decoder status: sleep \n"); + break; + + case AUDPP_DEC_STATUS_INIT: + dprintk("decoder status: init \n"); + audpp_cmd_cfg_routing_mode(audio); + break; + + case AUDPP_DEC_STATUS_CFG: + dprintk("decoder status: cfg \n"); + break; + case AUDPP_DEC_STATUS_PLAY: + dprintk("decoder status: play \n"); + if (audio->pcm_feedback) { + audqcelp_config_hostpcm(audio); + audqcelp_buffer_refresh(audio); + } + break; + default: + pr_err("unknown decoder status \n"); + } + break; + } + case AUDPP_MSG_CFG_MSG: + if (msg[0] == AUDPP_MSG_ENA_ENA) { + dprintk("audqcelp_dsp_event: CFG_MSG ENABLE\n"); + auddec_dsp_config(audio, 1); + audio->out_needed = 0; + audio->running = 1; + audpp_set_volume_and_pan(audio->dec_id, audio->volume, + 0); + audpp_avsync(audio->dec_id, 22050); + } else if (msg[0] == AUDPP_MSG_ENA_DIS) { + dprintk("audqcelp_dsp_event: CFG_MSG DISABLE\n"); + audpp_avsync(audio->dec_id, 0); + audio->running = 0; + } else { + pr_err("audqcelp_dsp_event: CFG_MSG %d?\n", msg[0]); + } + break; + case AUDPP_MSG_ROUTING_ACK: + dprintk("audqcelp_dsp_event: ROUTING_ACK mode=%d\n", msg[1]); + audpp_cmd_cfg_adec_params(audio); + break; + default: + pr_err("audqcelp_dsp_event: UNKNOWN (%d)\n", id); + } + +} + +struct msm_adsp_ops audplay_adsp_ops_qcelp = { + .event = audplay_dsp_event, +}; + +#define audplay_send_queue0(audio, cmd, len) \ + msm_adsp_write(audio->audplay, QDSP_uPAudPlay0BitStreamCtrlQueue, \ + cmd, len) + +static int auddec_dsp_config(struct audio *audio, int enable) +{ + audpp_cmd_cfg_dec_type cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE; + if (enable) + cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | + AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_QCELP; + else + cmd.dec0_cfg = AUDPP_CMD_UPDATDE_CFG_DEC | AUDPP_CMD_DIS_DEC_V; + + return audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_adec_params(struct audio *audio) +{ + struct audpp_cmd_cfg_adec_params_v13k cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN; + cmd.common.dec_id = audio->dec_id; + cmd.common.input_sampling_frequency = 8000; + cmd.stereo_cfg = AUDPP_CMD_PCM_INTF_MONO_V; + + audpp_send_queue2(&cmd, sizeof(cmd)); +} + +static void audpp_cmd_cfg_routing_mode(struct audio *audio) +{ + struct audpp_cmd_routing_mode cmd; + dprintk("audpp_cmd_cfg_routing_mode()\n"); + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_ROUTING_MODE; + cmd.object_number = audio->dec_id; + if (audio->pcm_feedback) + cmd.routing_mode = ROUTING_MODE_FTRT; + else + cmd.routing_mode = ROUTING_MODE_RT; + audpp_send_queue1(&cmd, sizeof(cmd)); +} + +static int audplay_dsp_send_data_avail(struct audio *audio, + unsigned idx, unsigned len) +{ + audplay_cmd_bitstream_data_avail cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL; + cmd.decoder_id = audio->dec_id; + cmd.buf_ptr = audio->out[idx].addr; + cmd.buf_size = len / 2; + cmd.partition_number = 0; + return audplay_send_queue0(audio, &cmd, sizeof(cmd)); +} + +static void audqcelp_buffer_refresh(struct audio *audio) +{ + struct audplay_cmd_buffer_refresh refresh_cmd; + + refresh_cmd.cmd_id = AUDPLAY_CMD_BUFFER_REFRESH; + refresh_cmd.num_buffers = 1; + refresh_cmd.buf0_address = audio->in[audio->fill_next].addr; + refresh_cmd.buf0_length = audio->in[audio->fill_next].size; + refresh_cmd.buf_read_count = 0; + dprintk("audplay_buffer_fresh: buf0_addr=%x buf0_len=%d\n", + refresh_cmd.buf0_address, refresh_cmd.buf0_length); + + (void)audplay_send_queue0(audio, &refresh_cmd, sizeof(refresh_cmd)); +} + +static void audqcelp_config_hostpcm(struct audio *audio) +{ + struct audplay_cmd_hpcm_buf_cfg cfg_cmd; + + dprintk("audqcelp_config_hostpcm()\n"); + cfg_cmd.cmd_id = AUDPLAY_CMD_HPCM_BUF_CFG; + cfg_cmd.max_buffers = audio->pcm_buf_count; + cfg_cmd.byte_swap = 0; + cfg_cmd.hostpcm_config = (0x8000) | (0x4000); + cfg_cmd.feedback_frequency = 1; + cfg_cmd.partition_number = 0; + + (void)audplay_send_queue0(audio, &cfg_cmd, sizeof(cfg_cmd)); +} + +static void audqcelp_send_data(struct audio *audio, unsigned needed) +{ + struct buffer *frame; + unsigned long flags; + + spin_lock_irqsave(&audio->dsp_lock, flags); + if (!audio->running) + goto done; + + if (needed) { + /* We were called from the callback because the DSP + * requested more data. Note that the DSP does want + * more data, and if a buffer was in-flight, mark it + * as available (since the DSP must now be done with + * it). + */ + audio->out_needed = 1; + frame = audio->out + audio->out_tail; + if (frame->used == 0xffffffff) { + dprintk("frame %d free\n", audio->out_tail); + frame->used = 0; + audio->out_tail ^= 1; + wake_up(&audio->write_wait); + } + } + + if (audio->out_needed) { + /* If the DSP currently wants data and we have a + * buffer available, we will send it and reset + * the needed flag. We'll mark the buffer as in-flight + * so that it won't be recycled until the next buffer + * is requested + */ + + frame = audio->out + audio->out_tail; + if (frame->used) { + BUG_ON(frame->used == 0xffffffff); + dprintk("frame %d busy\n", audio->out_tail); + audplay_dsp_send_data_avail(audio, audio->out_tail, + frame->used); + frame->used = 0xffffffff; + audio->out_needed = 0; + } + } + done: + spin_unlock_irqrestore(&audio->dsp_lock, flags); +} + +/* ------------------- device --------------------- */ + +static void audqcelp_flush(struct audio *audio) +{ + audio->out[0].used = 0; + audio->out[1].used = 0; + audio->out_head = 0; + audio->out_tail = 0; + audio->stopped = 0; +} + +static void audqcelp_flush_pcm_buf(struct audio *audio) +{ + uint8_t index; + + for (index = 0; index < PCM_BUF_MAX_COUNT; index++) + audio->in[index].used = 0; + + audio->read_next = 0; + audio->fill_next = 0; +} + +static long audqcelp_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = 0; + + dprintk("audqcelp_ioctl() cmd = %d\n", cmd); + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + stats.byte_count = audpp_avsync_byte_count(audio->dec_id); + stats.sample_count = audpp_avsync_sample_count(audio->dec_id); + if (copy_to_user((void *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + if (cmd == AUDIO_SET_VOLUME) { + unsigned long flags; + spin_lock_irqsave(&audio->dsp_lock, flags); + audio->volume = arg; + if (audio->running) + audpp_set_volume_and_pan(audio->dec_id, arg, 0); + spin_unlock_irqrestore(&audio->dsp_lock, flags); + return 0; + } + mutex_lock(&audio->lock); + switch (cmd) { + case AUDIO_START: + rc = audqcelp_enable(audio); + break; + case AUDIO_STOP: + rc = audqcelp_disable(audio); + audio->stopped = 1; + break; + case AUDIO_FLUSH: + if (audio->stopped) { + /* Make sure we're stopped and we wake any threads + * that might be blocked holding the write_lock. + * While audio->stopped write threads will always + * exit immediately. + */ + wake_up(&audio->write_wait); + mutex_lock(&audio->write_lock); + audqcelp_flush(audio); + mutex_unlock(&audio->write_lock); + wake_up(&audio->read_wait); + mutex_lock(&audio->read_lock); + audqcelp_flush_pcm_buf(audio); + mutex_unlock(&audio->read_lock); + break; + } + break; + case AUDIO_SET_CONFIG: + dprintk("AUDIO_SET_CONFIG not applicable \n"); + break; + case AUDIO_GET_CONFIG:{ + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = BUF_COUNT; + config.sample_rate = 8000; + config.channel_count = 1; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + config.unused[3] = 0; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + + break; + } + case AUDIO_GET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + + config.pcm_feedback = 0; + config.buffer_count = PCM_BUF_MAX_COUNT; + config.buffer_size = PCM_BUFSZ_MIN; + if (copy_to_user((void *)arg, &config, + sizeof(config))) + rc = -EFAULT; + else + rc = 0; + break; + } + case AUDIO_SET_PCM_CONFIG:{ + struct msm_audio_pcm_config config; + + if (copy_from_user(&config, (void *)arg, + sizeof(config))) { + rc = -EFAULT; + break; + } + if ((config.buffer_count > PCM_BUF_MAX_COUNT) || + (config.buffer_count == 1)) + config.buffer_count = PCM_BUF_MAX_COUNT; + + if (config.buffer_size < PCM_BUFSZ_MIN) + config.buffer_size = PCM_BUFSZ_MIN; + + /* Check if pcm feedback is required */ + if ((config.pcm_feedback) && (!audio->read_data)) { + dprintk( + "audqcelp_ioctl: allocate PCM buf %d\n", + config.buffer_count * config.buffer_size); + audio->read_data = dma_alloc_coherent(NULL, + config.buffer_size * config.buffer_count, + &audio->read_phys, GFP_KERNEL); + if (!audio->read_data) { + pr_err( + "audqcelp_ioctl: no mem for pcm buf\n" + ); + rc = -ENOMEM; + } else { + uint8_t index; + uint32_t offset = 0; + + audio->pcm_feedback = 1; + audio->buf_refresh = 0; + audio->pcm_buf_count = + config.buffer_count; + audio->read_next = 0; + audio->fill_next = 0; + + for (index = 0; + index < config.buffer_count; index++) { + audio->in[index].data = + audio->read_data + offset; + audio->in[index].addr = + audio->read_phys + offset; + audio->in[index].size = + config.buffer_size; + audio->in[index].used = 0; + offset += config.buffer_size; + } + rc = 0; + } + } else { + rc = 0; + } + break; + } + case AUDIO_PAUSE: + dprintk("%s: AUDIO_PAUSE %ld\n", __func__, arg); + rc = audpp_pause(audio->dec_id, (int) arg); + break; + default: + rc = -EINVAL; + } + mutex_unlock(&audio->lock); + return rc; +} + +static ssize_t audqcelp_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + int rc = 0; + + if (!audio->pcm_feedback) + return 0; /* PCM feedback is not enabled. Nothing to read */ + + mutex_lock(&audio->read_lock); + dprintk("audqcelp_read() %d \n", count); + while (count > 0) { + rc = wait_event_interruptible(audio->read_wait, + (audio->in[audio->read_next].used > 0) || + (audio->stopped)); + if (rc < 0) + break; + + if (audio->stopped) { + rc = -EBUSY; + break; + } + + if (count < audio->in[audio->read_next].used) { + /* Read must happen in frame boundary. Since driver does + not know frame size, read count must be greater or equal + to size of PCM samples */ + dprintk("audqcelp_read:read stop - partial frame\n"); + break; + } else { + dprintk("audqcelp_read: read from in[%d]\n", + audio->read_next); + if (copy_to_user(buf, + audio->in[audio->read_next].data, + audio->in[audio->read_next].used)) { + pr_err("audqcelp_read: invalid addr %x \n", + (unsigned int)buf); + rc = -EFAULT; + break; + } + count -= audio->in[audio->read_next].used; + buf += audio->in[audio->read_next].used; + audio->in[audio->read_next].used = 0; + if ((++audio->read_next) == audio->pcm_buf_count) + audio->read_next = 0; + } + } + + if (audio->buf_refresh) { + audio->buf_refresh = 0; + dprintk("audqcelp_read: kick start pcm feedback again\n"); + audqcelp_buffer_refresh(audio); + } + + mutex_unlock(&audio->read_lock); + + if (buf > start) + rc = buf - start; + + dprintk("audqcelp_read: read %d bytes\n", rc); + return rc; +} + +static ssize_t audqcelp_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + const char __user *start = buf; + struct buffer *frame; + size_t xfer; + int rc = 0; + + if (count & 1) + return -EINVAL; + dprintk("audqcelp_write() \n"); + mutex_lock(&audio->write_lock); + while (count > 0) { + frame = audio->out + audio->out_head; + rc = wait_event_interruptible(audio->write_wait, + (frame->used == 0) + || (audio->stopped)); + dprintk("audqcelp_write() buffer available\n"); + if (rc < 0) + break; + if (audio->stopped) { + rc = -EBUSY; + break; + } + xfer = (count > frame->size) ? frame->size : count; + if (copy_from_user(frame->data, buf, xfer)) { + rc = -EFAULT; + break; + } + + frame->used = xfer; + audio->out_head ^= 1; + count -= xfer; + buf += xfer; + + audqcelp_send_data(audio, 0); + + } + mutex_unlock(&audio->write_lock); + if (buf > start) + return buf - start; + return rc; +} + +static int audqcelp_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + + dprintk("audqcelp_release()\n"); + + mutex_lock(&audio->lock); + audqcelp_disable(audio); + audqcelp_flush(audio); + audqcelp_flush_pcm_buf(audio); + msm_adsp_put(audio->audplay); + audio->audplay = NULL; + audio->opened = 0; + if (audio->data) + dma_free_coherent(NULL, DMASZ, audio->data, audio->phys); + audio->data = NULL; + if (audio->read_data) { + dma_free_coherent(NULL, + audio->in[0].size * audio->pcm_buf_count, + audio->read_data, audio->read_phys); + audio->read_data = NULL; + } + audio->pcm_feedback = 0; + mutex_unlock(&audio->lock); + return 0; +} + +static int audqcelp_open(struct inode *inode, struct file *file) +{ + struct audio *audio = &the_qcelp_audio; + int rc; + + mutex_lock(&audio->lock); + + if (audio->opened) { + pr_err("audio: busy\n"); + rc = -EBUSY; + goto done; + } + + audio->data = dma_alloc_coherent(NULL, DMASZ, + &audio->phys, GFP_KERNEL); + if (!audio->data) { + pr_err("audio: could not allocate DMA buffers\n"); + rc = -ENOMEM; + goto done; + } + + rc = audmgr_open(&audio->audmgr); + if (rc) + goto err; + + rc = msm_adsp_get("AUDPLAY0TASK", &audio->audplay, + &audplay_adsp_ops_qcelp, audio); + if (rc) { + pr_err("audio: failed to get audplay0 dsp module\n"); + audmgr_close(&audio->audmgr); + goto err; + } + + audio->dec_id = 0; + + audio->out[0].data = audio->data + 0; + audio->out[0].addr = audio->phys + 0; + audio->out[0].size = BUFSZ; + + audio->out[1].data = audio->data + BUFSZ; + audio->out[1].addr = audio->phys + BUFSZ; + audio->out[1].size = BUFSZ; + + audio->volume = 0x2000; /* Q13 1.0 */ + + audqcelp_flush(audio); + + file->private_data = audio; + audio->opened = 1; + rc = 0; +done: + mutex_unlock(&audio->lock); + return rc; +err: + dma_free_coherent(NULL, DMASZ, audio->data, audio->phys); + mutex_unlock(&audio->lock); + return rc; +} + +static struct file_operations audio_qcelp_fops = { + .owner = THIS_MODULE, + .open = audqcelp_open, + .release = audqcelp_release, + .read = audqcelp_read, + .write = audqcelp_write, + .unlocked_ioctl = audqcelp_ioctl, +}; + +struct miscdevice audio_qcelp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_qcelp", + .fops = &audio_qcelp_fops, +}; + +static int __init audqcelp_init(void) +{ + mutex_init(&the_qcelp_audio.lock); + mutex_init(&the_qcelp_audio.write_lock); + mutex_init(&the_qcelp_audio.read_lock); + spin_lock_init(&the_qcelp_audio.dsp_lock); + init_waitqueue_head(&the_qcelp_audio.write_wait); + init_waitqueue_head(&the_qcelp_audio.read_wait); + the_qcelp_audio.read_data = NULL; + return misc_register(&audio_qcelp_misc); +} + +static void __exit audqcelp_exit(void) +{ + misc_deregister(&audio_qcelp_misc); +} + +module_init(audqcelp_init); +module_exit(audqcelp_exit); + +MODULE_DESCRIPTION("MSM QCELP 13K driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("QUALCOMM"); diff --git a/arch/arm/mach-msm/qdsp5/audmgr.c b/arch/arm/mach-msm/qdsp5/audmgr.c index fbebc8fb0f4e3..c1d60b30bb355 100644 --- a/arch/arm/mach-msm/qdsp5/audmgr.c +++ b/arch/arm/mach-msm/qdsp5/audmgr.c @@ -31,8 +31,8 @@ #define STATE_DISABLED 1 #define STATE_ENABLING 2 #define STATE_ENABLED 3 -#define STATE_DISABLING 4 -#define STATE_KILLED 5 +#define STATE_DISABLING 4 +#define STATE_ERROR 5 static void rpc_ack(struct msm_rpc_endpoint *ept, uint32_t xid) { @@ -57,7 +57,7 @@ static void process_audmgr_callback(struct audmgr *am, if (be32_to_cpu(args->set_to_one) != 1) return; - switch (be32_to_cpu(args->status)){ + switch (be32_to_cpu(args->status)) { case RPC_AUDMGR_STATUS_READY: if (len < sizeof(uint32_t) * 4) break; @@ -93,6 +93,8 @@ static void process_audmgr_callback(struct audmgr *am, break; case RPC_AUDMGR_STATUS_ERROR: pr_err("audmgr: ERROR?\n"); + am->state = STATE_ERROR; + wake_up(&am->wait); break; default: break; @@ -113,11 +115,10 @@ static void process_rpc_request(uint32_t proc, uint32_t xid, printk("\n"); } - if (proc == AUDMGR_CB_FUNC_PTR) { + if (proc == AUDMGR_CB_FUNC_PTR) process_audmgr_callback(am, data, len); - } else { + else pr_err("audmgr: unknown rpc proc %d\n", proc); - } rpc_ack(am->ept, xid); } @@ -155,7 +156,7 @@ static int audmgr_rpc_thread(void *data) type = be32_to_cpu(hdr->type); if (type == RPC_TYPE_REPLY) { - struct rpc_reply_hdr *rep = (void*) hdr; + struct rpc_reply_hdr *rep = (void *) hdr; uint32_t status; if (len < RPC_REPLY_HDR_SZ) continue; @@ -175,9 +176,9 @@ static int audmgr_rpc_thread(void *data) process_rpc_request(be32_to_cpu(hdr->procedure), be32_to_cpu(hdr->xid), - (void*) (hdr + 1), + (void *) (hdr + 1), len - sizeof(*hdr), - data); + data); } pr_info("audmgr_rpc_thread() exit\n"); if (hdr) { @@ -206,8 +207,10 @@ int audmgr_open(struct audmgr *am) if (am->state != STATE_CLOSED) return 0; - am->ept = msm_rpc_connect(AUDMGR_PROG, AUDMGR_VERS, - MSM_RPC_UNINTERRUPTIBLE); + am->ept = msm_rpc_connect(AUDMGR_PROG, + AUDMGR_VERS, + MSM_RPC_UNINTERRUPTIBLE); + init_waitqueue_head(&am->wait); if (IS_ERR(am->ept)) { @@ -229,11 +232,13 @@ int audmgr_open(struct audmgr *am) am->state = STATE_DISABLED; return 0; } +EXPORT_SYMBOL(audmgr_open); int audmgr_close(struct audmgr *am) { return -EBUSY; } +EXPORT_SYMBOL(audmgr_close); int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg) { @@ -256,7 +261,7 @@ int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg) msg.args.cb_func = cpu_to_be32(0x11111111); msg.args.client_data = cpu_to_be32(0x11223344); - msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, AUDMGR_VERS, + msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, msm_rpc_get_vers(am->ept), AUDMGR_ENABLE_CLIENT); rc = msm_rpc_write(am->ept, &msg, sizeof(msg)); @@ -274,6 +279,7 @@ int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg) pr_err("audmgr: unexpected state %d while enabling?!\n", am->state); return -ENODEV; } +EXPORT_SYMBOL(audmgr_enable); int audmgr_disable(struct audmgr *am) { @@ -283,7 +289,7 @@ int audmgr_disable(struct audmgr *am) if (am->state == STATE_DISABLED) return 0; - msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, AUDMGR_VERS, + msm_rpc_setup_req(&msg.hdr, AUDMGR_PROG, msm_rpc_get_vers(am->ept), AUDMGR_DISABLE_CLIENT); msg.handle = cpu_to_be32(am->handle); @@ -305,3 +311,4 @@ int audmgr_disable(struct audmgr *am) pr_err("audmgr: unexpected state %d while disabling?!\n", am->state); return -ENODEV; } +EXPORT_SYMBOL(audmgr_disable); diff --git a/arch/arm/mach-msm/qdsp5/audmgr.h b/arch/arm/mach-msm/qdsp5/audmgr.h index 69f4e05213e45..cd2d0c6d7d411 100644 --- a/arch/arm/mach-msm/qdsp5/audmgr.h +++ b/arch/arm/mach-msm/qdsp5/audmgr.h @@ -14,8 +14,12 @@ * */ -#ifndef _AUDIO_RPC_H_ -#define _AUDIO_RPC_H_ +#ifndef _ARCH_ARM_MACH_MSM_AUDMGR_H +#define _ARCH_ARM_MACH_MSM_AUDMGR_H + +#if CONFIG_MSM_AMSS_VERSION==6350 +#include "audmgr_new.h" +#else enum rpc_aud_def_sample_rate_type { RPC_AUD_DEF_SAMPLE_RATE_NONE, @@ -164,11 +168,9 @@ struct rpc_audmgr_cb_func_ptr { #define AUDMGR_CODEC_LSTR_FUNC_PTR 3 #if CONFIG_MSM_AMSS_VERSION < 6220 -#define AUDMGR_CB_PROG_VERS "rs31000013:5fa922a9" #define AUDMGR_CB_PROG 0x31000013 #define AUDMGR_CB_VERS 0x5fa922a9 #else -#define AUDMGR_CB_PROG_VERS "rs31000013:21570ba7" #define AUDMGR_CB_PROG 0x31000013 #define AUDMGR_CB_VERS 0x21570ba7 #endif @@ -203,9 +205,11 @@ int audpp_send_queue1(void *cmd, unsigned len); int audpp_send_queue2(void *cmd, unsigned len); int audpp_send_queue3(void *cmd, unsigned len); +int audpp_pause(unsigned id, int pause); int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan); void audpp_avsync(int id, unsigned rate); unsigned audpp_avsync_sample_count(int id); unsigned audpp_avsync_byte_count(int id); #endif +#endif diff --git a/arch/arm/mach-msm/qdsp5/audmgr_new.h b/arch/arm/mach-msm/qdsp5/audmgr_new.h new file mode 100644 index 0000000000000..43812424ffef7 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5/audmgr_new.h @@ -0,0 +1,213 @@ +/* arch/arm/mach-msm/qdsp5/audmgr.h + * + * Copyright 2008 (c) QUALCOMM Incorporated. + * Copyright (C) 2008 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 _ARCH_ARM_MACH_MSM_AUDMGR_NEW_H +#define _ARCH_ARM_MACH_MSM_AUDMGR_NEW_H + +enum rpc_aud_def_sample_rate_type { + RPC_AUD_DEF_SAMPLE_RATE_NONE, + RPC_AUD_DEF_SAMPLE_RATE_8000, + RPC_AUD_DEF_SAMPLE_RATE_11025, + RPC_AUD_DEF_SAMPLE_RATE_12000, + RPC_AUD_DEF_SAMPLE_RATE_16000, + RPC_AUD_DEF_SAMPLE_RATE_22050, + RPC_AUD_DEF_SAMPLE_RATE_24000, + RPC_AUD_DEF_SAMPLE_RATE_32000, + RPC_AUD_DEF_SAMPLE_RATE_44100, + RPC_AUD_DEF_SAMPLE_RATE_48000, + RPC_AUD_DEF_SAMPLE_RATE_MAX, +}; + +enum rpc_aud_def_method_type { + RPC_AUD_DEF_METHOD_NONE, + RPC_AUD_DEF_METHOD_KEY_BEEP, + RPC_AUD_DEF_METHOD_PLAYBACK, + RPC_AUD_DEF_METHOD_VOICE, + RPC_AUD_DEF_METHOD_RECORD, + RPC_AUD_DEF_METHOD_HOST_PCM, + RPC_AUD_DEF_METHOD_MIDI_OUT, + RPC_AUD_DEF_METHOD_RECORD_SBC, + RPC_AUD_DEF_METHOD_DTMF_RINGER, + RPC_AUD_DEF_METHOD_MAX, +}; + +enum rpc_aud_def_codec_type { + RPC_AUD_DEF_CODEC_NONE, + RPC_AUD_DEF_CODEC_DTMF, + RPC_AUD_DEF_CODEC_MIDI, + RPC_AUD_DEF_CODEC_MP3, + RPC_AUD_DEF_CODEC_PCM, + RPC_AUD_DEF_CODEC_AAC, + RPC_AUD_DEF_CODEC_WMA, + RPC_AUD_DEF_CODEC_RA, + RPC_AUD_DEF_CODEC_ADPCM, + RPC_AUD_DEF_CODEC_GAUDIO, + RPC_AUD_DEF_CODEC_VOC_EVRC, + RPC_AUD_DEF_CODEC_VOC_13K, + RPC_AUD_DEF_CODEC_VOC_4GV_NB, + RPC_AUD_DEF_CODEC_VOC_AMR, + RPC_AUD_DEF_CODEC_VOC_EFR, + RPC_AUD_DEF_CODEC_VOC_FR, + RPC_AUD_DEF_CODEC_VOC_HR, + RPC_AUD_DEF_CODEC_VOC_CDMA, + RPC_AUD_DEF_CODEC_VOC_CDMA_WB, + RPC_AUD_DEF_CODEC_VOC_UMTS, + RPC_AUD_DEF_CODEC_VOC_UMTS_WB, + RPC_AUD_DEF_CODEC_SBC, + RPC_AUD_DEF_CODEC_VOC_PCM, + RPC_AUD_DEF_CODEC_AMR_WB, + RPC_AUD_DEF_CODEC_AMR_WB_PLUS, + RPC_AUD_DEF_CODEC_AAC_BSAC, + RPC_AUD_DEF_CODEC_MAX, + RPC_AUD_DEF_CODEC_AMR_NB, + RPC_AUD_DEF_CODEC_13K, + RPC_AUD_DEF_CODEC_EVRC, + RPC_AUD_DEF_CODEC_MAX_002, +}; + +enum rpc_snd_method_type { + RPC_SND_METHOD_VOICE = 0, + RPC_SND_METHOD_KEY_BEEP, + RPC_SND_METHOD_MESSAGE, + RPC_SND_METHOD_RING, + RPC_SND_METHOD_MIDI, + RPC_SND_METHOD_AUX, + RPC_SND_METHOD_MAX, +}; + +enum rpc_voc_codec_type { + RPC_VOC_CODEC_DEFAULT, + RPC_VOC_CODEC_ON_CHIP_0 = RPC_VOC_CODEC_DEFAULT, + RPC_VOC_CODEC_ON_CHIP_1, + RPC_VOC_CODEC_STEREO_HEADSET, + RPC_VOC_CODEC_ON_CHIP_AUX, + RPC_VOC_CODEC_BT_OFF_BOARD, + RPC_VOC_CODEC_BT_A2DP, + RPC_VOC_CODEC_OFF_BOARD, + RPC_VOC_CODEC_SDAC, + RPC_VOC_CODEC_RX_EXT_SDAC_TX_INTERNAL, + RPC_VOC_CODEC_IN_STEREO_SADC_OUT_MONO_HANDSET, + RPC_VOC_CODEC_IN_STEREO_SADC_OUT_STEREO_HEADSET, + RPC_VOC_CODEC_TX_INT_SADC_RX_EXT_AUXPCM, + RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_MONO_HANDSET, + RPC_VOC_CODEC_EXT_STEREO_SADC_OUT_STEREO_HEADSET, + RPC_VOC_CODEC_TTY_ON_CHIP_1, + RPC_VOC_CODEC_TTY_OFF_BOARD, + RPC_VOC_CODEC_TTY_VCO, + RPC_VOC_CODEC_TTY_HCO, + RPC_VOC_CODEC_ON_CHIP_0_DUAL_MIC, + RPC_VOC_CODEC_MAX, + RPC_VOC_CODEC_NONE, +}; + +enum rpc_audmgr_status_type { + RPC_AUDMGR_STATUS_READY, + RPC_AUDMGR_STATUS_CODEC_CONFIG, + RPC_AUDMGR_STATUS_PENDING, + RPC_AUDMGR_STATUS_SUSPEND, + RPC_AUDMGR_STATUS_FAILURE, + RPC_AUDMGR_STATUS_VOLUME_CHANGE, + RPC_AUDMGR_STATUS_DISABLED, + RPC_AUDMGR_STATUS_ERROR, +}; + +struct rpc_audmgr_enable_client_args { + uint32_t set_to_one; + uint32_t tx_sample_rate; + uint32_t rx_sample_rate; + uint32_t def_method; + uint32_t codec_type; + uint32_t snd_method; + + uint32_t cb_func; + uint32_t client_data; +}; + +#define AUDMGR_ENABLE_CLIENT 2 +#define AUDMGR_DISABLE_CLIENT 3 +#define AUDMGR_SUSPEND_EVENT_RSP 4 +#define AUDMGR_REGISTER_OPERATION_LISTENER 5 +#define AUDMGR_UNREGISTER_OPERATION_LISTENER 6 +#define AUDMGR_REGISTER_CODEC_LISTENER 7 +#define AUDMGR_GET_RX_SAMPLE_RATE 8 +#define AUDMGR_GET_TX_SAMPLE_RATE 9 +#define AUDMGR_SET_DEVICE_MODE 10 + +#define AUDMGR_PROG 0x30000013 +#define AUDMGR_VERS MSM_RPC_VERS(1,0) + +struct rpc_audmgr_cb_func_ptr { + uint32_t cb_id; + uint32_t status; /* Audmgr status */ + uint32_t set_to_one; /* Pointer status (1 = valid, 0 = invalid) */ + uint32_t disc; + /* disc = AUDMGR_STATUS_READY => data=handle + disc = AUDMGR_STATUS_CODEC_CONFIG => data = handle + disc = AUDMGR_STATUS_DISABLED => data =status_disabled + disc = AUDMGR_STATUS_VOLUME_CHANGE => data = volume-change */ + union { + uint32_t handle; + uint32_t volume; + uint32_t status_disabled; + uint32_t volume_change; + } u; +}; + +#define AUDMGR_CB_FUNC_PTR 1 +#define AUDMGR_OPR_LSTNR_CB_FUNC_PTR 2 +#define AUDMGR_CODEC_LSTR_FUNC_PTR 3 + +#define AUDMGR_CB_PROG 0x31000013 +#define AUDMGR_CB_VERS 0xf8e3e2d9 + +struct audmgr { + wait_queue_head_t wait; + uint32_t handle; + struct msm_rpc_endpoint *ept; + struct task_struct *task; + int state; +}; + +struct audmgr_config { + uint32_t tx_rate; + uint32_t rx_rate; + uint32_t def_method; + uint32_t codec; + uint32_t snd_method; +}; + +int audmgr_open(struct audmgr *am); +int audmgr_close(struct audmgr *am); +int audmgr_enable(struct audmgr *am, struct audmgr_config *cfg); +int audmgr_disable(struct audmgr *am); + +typedef void (*audpp_event_func)(void *private, unsigned id, uint16_t *msg); + +int audpp_enable(int id, audpp_event_func func, void *private); +void audpp_disable(int id, void *private); + +int audpp_send_queue1(void *cmd, unsigned len); +int audpp_send_queue2(void *cmd, unsigned len); +int audpp_send_queue3(void *cmd, unsigned len); + +int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan); +int audpp_pause(unsigned id, int pause); +int audpp_flush(unsigned id); +void audpp_avsync(int id, unsigned rate); +unsigned audpp_avsync_sample_count(int id); +unsigned audpp_avsync_byte_count(int id); + +#endif diff --git a/arch/arm/mach-msm/qdsp5/audpp.c b/arch/arm/mach-msm/qdsp5/audpp.c index 8400df7e9cb4c..32c284716475e 100644 --- a/arch/arm/mach-msm/qdsp5/audpp.c +++ b/arch/arm/mach-msm/qdsp5/audpp.c @@ -64,12 +64,13 @@ module_init(_dsp_log_init); static DEFINE_MUTEX(audpp_lock); #define CH_COUNT 5 +#define AUDPP_CLNT_MAX_COUNT 6 +#define AUDPP_AVSYNC_INFO_SIZE 7 -struct audpp_state -{ +struct audpp_state { struct msm_adsp_module *mod; - audpp_event_func func[6]; - void *private[6]; + audpp_event_func func[AUDPP_CLNT_MAX_COUNT]; + void *private[AUDPP_CLNT_MAX_COUNT]; struct mutex *lock; unsigned open_count; unsigned enabled; @@ -78,30 +79,33 @@ struct audpp_state unsigned avsync_mask; /* flags, 48 bits sample/bytes counter per channel */ - uint16_t avsync[CH_COUNT * 6 + 1]; + uint16_t avsync[CH_COUNT * AUDPP_CLNT_MAX_COUNT + 1]; }; struct audpp_state the_audpp_state = { .lock = &audpp_lock, }; -int audpp_send_queue1(void *cmd, unsigned len) +int audpp_send_queue1(void *cmd, unsigned len) { return msm_adsp_write(the_audpp_state.mod, QDSP_uPAudPPCmd1Queue, cmd, len); } +EXPORT_SYMBOL(audpp_send_queue1); -int audpp_send_queue2(void *cmd, unsigned len) +int audpp_send_queue2(void *cmd, unsigned len) { return msm_adsp_write(the_audpp_state.mod, QDSP_uPAudPPCmd2Queue, cmd, len); } +EXPORT_SYMBOL(audpp_send_queue2); -int audpp_send_queue3(void *cmd, unsigned len) +int audpp_send_queue3(void *cmd, unsigned len) { return msm_adsp_write(the_audpp_state.mod, QDSP_uPAudPPCmd3Queue, cmd, len); } +EXPORT_SYMBOL(audpp_send_queue3); static int audpp_dsp_config(int enable) { @@ -113,16 +117,23 @@ static int audpp_dsp_config(int enable) return audpp_send_queue1(&cmd, sizeof(cmd)); } - -static void audpp_broadcast(struct audpp_state *audpp, unsigned id, uint16_t *msg) +static void audpp_broadcast(struct audpp_state *audpp, unsigned id, + uint16_t *msg) { unsigned n; - for (n = 0; n < 6; n++) { + for (n = 0; n < AUDPP_CLNT_MAX_COUNT; n++) { if (audpp->func[n]) - audpp->func[n](audpp->private[n], id, msg); + audpp->func[n] (audpp->private[n], id, msg); } } +static void audpp_notify_clnt(struct audpp_state *audpp, unsigned clnt_id, + unsigned id, uint16_t *msg) +{ + if (clnt_id < AUDPP_CLNT_MAX_COUNT && audpp->func[clnt_id]) + audpp->func[clnt_id] (audpp->private[clnt_id], id, msg); +} + static void audpp_dsp_event(void *data, unsigned id, size_t len, void (*getevent)(void *ptr, size_t len)) { @@ -147,19 +158,20 @@ static void audpp_dsp_event(void *data, unsigned id, size_t len, LOG(EV_DATA, (msg[1] << 16) | msg[2]); switch (id) { - case AUDPP_MSG_STATUS_MSG: { - unsigned cid = msg[0]; - pr_info("audpp: status %d %d %d\n", cid, msg[1], msg[2]); - if ((cid < 5) && audpp->func[cid]) - audpp->func[cid](audpp->private[cid], id, msg); - break; - } + case AUDPP_MSG_STATUS_MSG:{ + unsigned cid = msg[0]; + pr_info("audpp: status %d %d %d\n", cid, msg[1], + msg[2]); + if ((cid < 5) && audpp->func[cid]) + audpp->func[cid] (audpp->private[cid], id, msg); + break; + } case AUDPP_MSG_HOST_PCM_INTF_MSG: if (audpp->func[5]) - audpp->func[5](audpp->private[5], id, msg); + audpp->func[5] (audpp->private[5], id, msg); break; case AUDPP_MSG_PCMDMAMISSED: - pr_err("audpp: DMA missed\n"); + pr_err("audpp: DMA missed obj=%x\n", msg[0]); break; case AUDPP_MSG_CFG_MSG: if (msg[0] == AUDPP_MSG_ENA_ENA) { @@ -174,6 +186,14 @@ static void audpp_dsp_event(void *data, unsigned id, size_t len, pr_err("audpp: invalid config msg %d\n", msg[0]); } break; + case AUDPP_MSG_ROUTING_ACK: + audpp_broadcast(audpp, id, msg); + break; + case AUDPP_MSG_FLUSH_ACK: + audpp_notify_clnt(audpp, msg[0], id, msg); + break; + default: + pr_info("audpp: unhandled msg id %x\n", id); } } @@ -186,7 +206,7 @@ static void audpp_fake_event(struct audpp_state *audpp, int id, { uint16_t msg[1]; msg[0] = arg; - audpp->func[id](audpp->private[id], event, msg); + audpp->func[id] (audpp->private[id], event, msg); } int audpp_enable(int id, audpp_event_func func, void *private) @@ -227,17 +247,17 @@ int audpp_enable(int id, audpp_event_func func, void *private) unsigned long flags; local_irq_save(flags); if (audpp->enabled) - audpp_fake_event(audpp, id, - AUDPP_MSG_CFG_MSG, - AUDPP_MSG_ENA_ENA); + audpp_fake_event(audpp, id, + AUDPP_MSG_CFG_MSG, AUDPP_MSG_ENA_ENA); local_irq_restore(flags); } res = 0; -out: +out: mutex_unlock(audpp->lock); return res; } +EXPORT_SYMBOL(audpp_enable); void audpp_disable(int id, void *private) { @@ -271,10 +291,10 @@ void audpp_disable(int id, void *private) msm_adsp_put(audpp->mod); audpp->mod = NULL; } -out: +out: mutex_unlock(audpp->lock); } - +EXPORT_SYMBOL(audpp_disable); #define BAD_ID(id) ((id < 0) || (id >= CH_COUNT)) @@ -300,6 +320,7 @@ void audpp_avsync(int id, unsigned rate) cmd.interrupt_interval_msw = rate >> 16; audpp_send_queue1(&cmd, sizeof(cmd)); } +EXPORT_SYMBOL(audpp_avsync); unsigned audpp_avsync_sample_count(int id) { @@ -312,16 +333,17 @@ unsigned audpp_avsync_sample_count(int id) return 0; mask = 1 << id; - id = id * 6 + 2; + id = id * AUDPP_AVSYNC_INFO_SIZE + 2; local_irq_save(flags); if (avsync[0] & mask) - val = (avsync[id] << 16) | avsync[id]; + val = (avsync[id] << 16) | avsync[id + 1]; else val = 0; local_irq_restore(flags); return val; } +EXPORT_SYMBOL(audpp_avsync_sample_count); unsigned audpp_avsync_byte_count(int id) { @@ -334,16 +356,17 @@ unsigned audpp_avsync_byte_count(int id) return 0; mask = 1 << id; - id = id * 6 + 5; + id = id * AUDPP_AVSYNC_INFO_SIZE + 5; local_irq_save(flags); if (avsync[0] & mask) - val = (avsync[id] << 16) | avsync[id]; + val = (avsync[id] << 16) | avsync[id + 1]; else val = 0; local_irq_restore(flags); return val; } +EXPORT_SYMBOL(audpp_avsync_byte_count); #define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000 #define AUDPP_CMD_VOLUME_PAN 0 @@ -365,3 +388,42 @@ int audpp_set_volume_and_pan(unsigned id, unsigned volume, int pan) return audpp_send_queue3(cmd, sizeof(cmd)); } +EXPORT_SYMBOL(audpp_set_volume_and_pan); + +int audpp_pause(unsigned id, int pause) +{ + /* pause 1 = pause 0 = resume */ + u16 pause_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)]; + + if (id >= CH_COUNT) + return -EINVAL; + + memset(pause_cmd, 0, sizeof(pause_cmd)); + + pause_cmd[0] = AUDPP_CMD_DEC_CTRL; + if (pause == 1) + pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_PAUSE_V; + else if (pause == 0) + pause_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_RESUME_V; + else + return -EINVAL; + + return audpp_send_queue1(pause_cmd, sizeof(pause_cmd)); +} +EXPORT_SYMBOL(audpp_pause); + +int audpp_flush(unsigned id) +{ + u16 flush_cmd[AUDPP_CMD_DEC_CTRL_LEN / sizeof(unsigned short)]; + + if (id >= CH_COUNT) + return -EINVAL; + + memset(flush_cmd, 0, sizeof(flush_cmd)); + + flush_cmd[0] = AUDPP_CMD_DEC_CTRL; + flush_cmd[1 + id] = AUDPP_CMD_UPDATE_V | AUDPP_CMD_FLUSH_V; + + return audpp_send_queue1(flush_cmd, sizeof(flush_cmd)); +} +EXPORT_SYMBOL(audpp_flush); diff --git a/arch/arm/mach-msm/qdsp5/snd.c b/arch/arm/mach-msm/qdsp5/snd.c index 4332ba67a1843..037d7ffb7e670 100644 --- a/arch/arm/mach-msm/qdsp5/snd.c +++ b/arch/arm/mach-msm/qdsp5/snd.c @@ -37,12 +37,15 @@ struct snd_ctxt { static struct snd_ctxt the_snd; -#define RPC_SND_PROG 0x30000002 -#define RPC_SND_CB_PROG 0x31000002 +#define RPC_SND_PROG 0x30000002 +#define RPC_SND_CB_PROG 0x31000002 #if CONFIG_MSM_AMSS_VERSION == 6210 -#define RPC_SND_VERS 0x94756085 /* 2490720389 */ -#elif (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225) -#define RPC_SND_VERS 0xaa2b1a44 /* 2854951492 */ +#define RPC_SND_VERS 0x94756085 /* 2490720389 */ +#elif (CONFIG_MSM_AMSS_VERSION == 6220) || \ + (CONFIG_MSM_AMSS_VERSION == 6225) +#define RPC_SND_VERS 0xaa2b1a44 /* 2854951492 */ +#elif CONFIG_MSM_AMSS_VERSION == 6350 +#define RPC_SND_VERS MSM_RPC_VERS(1,0) #endif #define SND_SET_DEVICE_PROC 2 @@ -219,7 +222,7 @@ static int snd_open(struct inode *inode, struct file *file) if (snd->opened == 0) { if (snd->ept == NULL) { snd->ept = msm_rpc_connect(RPC_SND_PROG, RPC_SND_VERS, - MSM_RPC_UNINTERRUPTIBLE); + MSM_RPC_UNINTERRUPTIBLE); if (IS_ERR(snd->ept)) { rc = PTR_ERR(snd->ept); snd->ept = NULL; diff --git a/arch/arm/mach-msm/rpc_server_dog_keepalive.c b/arch/arm/mach-msm/rpc_server_dog_keepalive.c index a04a380e1ff00..b23fccfa87e29 100644 --- a/arch/arm/mach-msm/rpc_server_dog_keepalive.c +++ b/arch/arm/mach-msm/rpc_server_dog_keepalive.c @@ -21,17 +21,22 @@ /* dog_keepalive server definitions */ #define DOG_KEEPALIVE_PROG 0x30000015 -#define RPC_DOG_KEEPALIVE_NULL 0 - #if CONFIG_MSM_AMSS_VERSION==6210 #define DOG_KEEPALIVE_VERS 0 #define RPC_DOG_KEEPALIVE_BEACON 1 #elif (CONFIG_MSM_AMSS_VERSION==6220) || (CONFIG_MSM_AMSS_VERSION==6225) #define DOG_KEEPALIVE_VERS 0x731fa727 #define RPC_DOG_KEEPALIVE_BEACON 2 +#elif CONFIG_MSM_AMSS_VERSION==6350 +#define DOG_KEEPALIVE_VERS 0x00010000 +#define RPC_DOG_KEEPALIVE_BEACON 2 #else #error "Unsupported AMSS version" #endif +#define RPC_DOG_KEEPALIVE_NULL 0 + + +/* TODO: Remove server registration with _VERS when modem is upated with _COMP*/ static int handle_rpc_call(struct msm_rpc_server *server, struct rpc_request_hdr *req, unsigned len) @@ -55,6 +60,7 @@ static struct msm_rpc_server rpc_server = { static int __init rpc_server_init(void) { + /* Dual server registration to support backwards compatibility vers */ return msm_rpc_create_server(&rpc_server); } diff --git a/arch/arm/mach-msm/rpc_server_time_remote.c b/arch/arm/mach-msm/rpc_server_time_remote.c index a833f83dfedb2..2f90fc88c3850 100644 --- a/arch/arm/mach-msm/rpc_server_time_remote.c +++ b/arch/arm/mach-msm/rpc_server_time_remote.c @@ -21,16 +21,17 @@ /* time_remote_mtoa server definitions. */ #define TIME_REMOTE_MTOA_PROG 0x3000005d -#define RPC_TIME_REMOTE_MTOA_NULL 0 -#define RPC_TIME_TOD_SET_APPS_BASES 2 - #if CONFIG_MSM_AMSS_VERSION==6210 #define TIME_REMOTE_MTOA_VERS 0 #elif (CONFIG_MSM_AMSS_VERSION==6220) || (CONFIG_MSM_AMSS_VERSION==6225) #define TIME_REMOTE_MTOA_VERS 0x9202a8e4 +#elif CONFIG_MSM_AMSS_VERSION==6350 +#define TIME_REMOTE_MTOA_VERS 0x00010000 #else #error "Unknown AMSS version" #endif +#define RPC_TIME_REMOTE_MTOA_NULL 0 +#define RPC_TIME_TOD_SET_APPS_BASES 2 struct rpc_time_tod_set_apps_bases_args { uint32_t tick; @@ -68,6 +69,7 @@ static struct msm_rpc_server rpc_server = { static int __init rpc_server_init(void) { + /* Dual server registration to support backwards compatibility vers */ return msm_rpc_create_server(&rpc_server); } diff --git a/arch/arm/mach-msm/smd_rpcrouter_servers.c b/arch/arm/mach-msm/smd_rpcrouter_servers.c index 9587e1c466b8e..d340bfa12c4fd 100644 --- a/arch/arm/mach-msm/smd_rpcrouter_servers.c +++ b/arch/arm/mach-msm/smd_rpcrouter_servers.c @@ -25,12 +25,13 @@ #include #include #include -#include #include #include #include #include +#include + #include #include "smd_rpcrouter.h" @@ -58,7 +59,12 @@ static struct msm_rpc_server *rpc_server_find(uint32_t prog, uint32_t vers) mutex_lock(&rpc_server_list_lock); list_for_each_entry(server, &rpc_server_list, list) { - if ((server->prog == prog) && (server->vers == vers)) { + if ((server->prog == prog) && +#if CONFIG_MSM_AMSS_VERSION >= 6350 + msm_rpc_is_compatible_version(server->vers, vers)) { +#else + server->vers == vers) { +#endif mutex_unlock(&rpc_server_list_lock); return server; } @@ -113,7 +119,7 @@ static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client, reply->data.acc_hdr.verf_flavor = 0; reply->data.acc_hdr.verf_length = 0; - rc = msm_rpc_write(endpoint, reply_buf, sizeof(reply_buf)); + rc = msm_rpc_write(client, reply_buf, sizeof(reply_buf)); if (rc < 0) printk(KERN_ERR "%s: could not write response: %d\n", @@ -185,6 +191,8 @@ static int rpc_servers_thread(void *data) static int rpcservers_probe(struct platform_device *pdev) { + struct task_struct *server_thread; + endpoint = msm_rpc_open(); if (IS_ERR(endpoint)) return PTR_ERR(endpoint); @@ -194,7 +202,9 @@ static int rpcservers_probe(struct platform_device *pdev) rpc_server_register_all(); /* start the kernel thread */ - kthread_run(rpc_servers_thread, NULL, "krpcserversd"); + server_thread = kthread_run(rpc_servers_thread, NULL, "krpcserversd"); + if (IS_ERR(server_thread)) + return PTR_ERR(server_thread); return 0; } diff --git a/drivers/rtc/rtc-msm7x00a.c b/drivers/rtc/rtc-msm7x00a.c index a2f8ecd7201e7..03f57cebbeee8 100644 --- a/drivers/rtc/rtc-msm7x00a.c +++ b/drivers/rtc/rtc-msm7x00a.c @@ -30,7 +30,11 @@ extern void msm_pm_set_max_sleep_time(int64_t sleep_time_ns); +#if CONFIG_MSM_AMSS_VERSION >= 6350 +#define APP_TIMEREMOTE_PDEV_NAME "rs30000048:00010000" +#else #define APP_TIMEREMOTE_PDEV_NAME "rs30000048:0da5b528" +#endif #define TIMEREMOTE_PROCEEDURE_SET_JULIAN 6 #define TIMEREMOTE_PROCEEDURE_GET_JULIAN 7 diff --git a/include/linux/msm_adsp.h b/include/linux/msm_adsp.h index 46c41640d7987..c739588b6846d 100644 --- a/include/linux/msm_adsp.h +++ b/include/linux/msm_adsp.h @@ -19,7 +19,6 @@ #include #include -#include #define ADSP_IOCTL_MAGIC 'q' @@ -55,6 +54,9 @@ struct adsp_event_t { #define ADSP_IOCTL_GET_EVENT \ _IOWR(ADSP_IOCTL_MAGIC, 5, struct adsp_event_data_t *) +#define ADSP_IOCTL_SET_CLKRATE \ + _IOR(ADSP_IOCTL_MAGIC, 6, unsigned) + #define ADSP_IOCTL_DISABLE_EVENT_RSP \ _IOR(ADSP_IOCTL_MAGIC, 10, unsigned) diff --git a/include/linux/msm_audio.h b/include/linux/msm_audio.h index 9da6ccc20b3a1..8390eb1e1345d 100644 --- a/include/linux/msm_audio.h +++ b/include/linux/msm_audio.h @@ -39,6 +39,14 @@ #define AUDIO_SET_AGC _IOW(AUDIO_IOCTL_MAGIC, 12, unsigned) #define AUDIO_SET_NS _IOW(AUDIO_IOCTL_MAGIC, 13, unsigned) #define AUDIO_SET_TX_IIR _IOW(AUDIO_IOCTL_MAGIC, 14, unsigned) +#define AUDIO_PAUSE _IOW(AUDIO_IOCTL_MAGIC, 15, unsigned) +#define AUDIO_GET_PCM_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 30, unsigned) +#define AUDIO_SET_PCM_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 31, unsigned) +#define AUDIO_SWITCH_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 32, unsigned) + +#define AUDIO_MAX_COMMON_IOCTL_NUM 100 + +#define AUDIO_MAX_COMMON_IOCTL_NUM 100 struct msm_audio_config { uint32_t buffer_size; @@ -98,4 +106,10 @@ struct msm_snd_endpoint { #define SND_GET_ENDPOINT _IOWR(SND_IOCTL_MAGIC, 5, struct msm_snd_endpoint *) -#endif /* __LINUX_MSM_AUDIO_H */ +struct msm_audio_pcm_config { + uint32_t pcm_feedback; /* 0 - disable > 0 - enable */ + uint32_t buffer_count; /* Number of buffers to allocate */ + uint32_t buffer_size; /* Size of buffer for capturing of + PCM samples */ +}; +#endif From 1fe1fc411f9817fd36434d04efcc75637a7b7cfa Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Wed, 27 May 2009 19:29:13 -0700 Subject: [PATCH 0312/2556] [ARM] msm: move the adsp queues into mach/msm_adsp.h Allows access to the adsp queues to code outside of arch/arm/mach-msm. Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/include/mach/msm_adsp.h | 63 +++++++++++++++++++++++ arch/arm/mach-msm/qdsp5/adsp.h | 31 ----------- arch/arm/mach-msm/qdsp5/adsp_new.h | 31 ----------- 3 files changed, 63 insertions(+), 62 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/msm_adsp.h b/arch/arm/mach-msm/include/mach/msm_adsp.h index 52280cabf8246..a081683328a31 100644 --- a/arch/arm/mach-msm/include/mach/msm_adsp.h +++ b/arch/arm/mach-msm/include/mach/msm_adsp.h @@ -46,4 +46,67 @@ int msm_adsp_write(struct msm_adsp_module *module, unsigned queue_id, void *data, size_t len); +#if CONFIG_MSM_AMSS_VERSION >= 6350 +/* Command Queue Indexes */ +#define QDSP_lpmCommandQueue 0 +#define QDSP_mpuAfeQueue 1 +#define QDSP_mpuGraphicsCmdQueue 2 +#define QDSP_mpuModmathCmdQueue 3 +#define QDSP_mpuVDecCmdQueue 4 +#define QDSP_mpuVDecPktQueue 5 +#define QDSP_mpuVEncCmdQueue 6 +#define QDSP_rxMpuDecCmdQueue 7 +#define QDSP_rxMpuDecPktQueue 8 +#define QDSP_txMpuEncQueue 9 +#define QDSP_uPAudPPCmd1Queue 10 +#define QDSP_uPAudPPCmd2Queue 11 +#define QDSP_uPAudPPCmd3Queue 12 +#define QDSP_uPAudPlay0BitStreamCtrlQueue 13 +#define QDSP_uPAudPlay1BitStreamCtrlQueue 14 +#define QDSP_uPAudPlay2BitStreamCtrlQueue 15 +#define QDSP_uPAudPlay3BitStreamCtrlQueue 16 +#define QDSP_uPAudPlay4BitStreamCtrlQueue 17 +#define QDSP_uPAudPreProcCmdQueue 18 +#define QDSP_uPAudRecBitStreamQueue 19 +#define QDSP_uPAudRecCmdQueue 20 +#define QDSP_uPDiagQueue 21 +#define QDSP_uPJpegActionCmdQueue 22 +#define QDSP_uPJpegCfgCmdQueue 23 +#define QDSP_uPVocProcQueue 24 +#define QDSP_vfeCommandQueue 25 +#define QDSP_vfeCommandScaleQueue 26 +#define QDSP_vfeCommandTableQueue 27 +#define QDSP_MAX_NUM_QUEUES 28 +#else +/* Command Queue Indexes */ +#define QDSP_lpmCommandQueue 0 +#define QDSP_mpuAfeQueue 1 +#define QDSP_mpuGraphicsCmdQueue 2 +#define QDSP_mpuModmathCmdQueue 3 +#define QDSP_mpuVDecCmdQueue 4 +#define QDSP_mpuVDecPktQueue 5 +#define QDSP_mpuVEncCmdQueue 6 +#define QDSP_rxMpuDecCmdQueue 7 +#define QDSP_rxMpuDecPktQueue 8 +#define QDSP_txMpuEncQueue 9 +#define QDSP_uPAudPPCmd1Queue 10 +#define QDSP_uPAudPPCmd2Queue 11 +#define QDSP_uPAudPPCmd3Queue 12 +#define QDSP_uPAudPlay0BitStreamCtrlQueue 13 +#define QDSP_uPAudPlay1BitStreamCtrlQueue 14 +#define QDSP_uPAudPlay2BitStreamCtrlQueue 15 +#define QDSP_uPAudPlay3BitStreamCtrlQueue 16 +#define QDSP_uPAudPlay4BitStreamCtrlQueue 17 +#define QDSP_uPAudPreProcCmdQueue 18 +#define QDSP_uPAudRecBitStreamQueue 19 +#define QDSP_uPAudRecCmdQueue 20 +#define QDSP_uPJpegActionCmdQueue 21 +#define QDSP_uPJpegCfgCmdQueue 22 +#define QDSP_uPVocProcQueue 23 +#define QDSP_vfeCommandQueue 24 +#define QDSP_vfeCommandScaleQueue 25 +#define QDSP_vfeCommandTableQueue 26 +#define QDSP_QUEUE_MAX 26 +#endif + #endif diff --git a/arch/arm/mach-msm/qdsp5/adsp.h b/arch/arm/mach-msm/qdsp5/adsp.h index e2a12f8ff9979..e0b93779308ba 100644 --- a/arch/arm/mach-msm/qdsp5/adsp.h +++ b/arch/arm/mach-msm/qdsp5/adsp.h @@ -213,37 +213,6 @@ extern int adsp_init_info(struct adsp_info *info); extern struct msm_adsp_module *find_adsp_module_by_id(struct adsp_info *info, uint32_t id); - -/* Command Queue Indexes */ -#define QDSP_lpmCommandQueue 0 -#define QDSP_mpuAfeQueue 1 -#define QDSP_mpuGraphicsCmdQueue 2 -#define QDSP_mpuModmathCmdQueue 3 -#define QDSP_mpuVDecCmdQueue 4 -#define QDSP_mpuVDecPktQueue 5 -#define QDSP_mpuVEncCmdQueue 6 -#define QDSP_rxMpuDecCmdQueue 7 -#define QDSP_rxMpuDecPktQueue 8 -#define QDSP_txMpuEncQueue 9 -#define QDSP_uPAudPPCmd1Queue 10 -#define QDSP_uPAudPPCmd2Queue 11 -#define QDSP_uPAudPPCmd3Queue 12 -#define QDSP_uPAudPlay0BitStreamCtrlQueue 13 -#define QDSP_uPAudPlay1BitStreamCtrlQueue 14 -#define QDSP_uPAudPlay2BitStreamCtrlQueue 15 -#define QDSP_uPAudPlay3BitStreamCtrlQueue 16 -#define QDSP_uPAudPlay4BitStreamCtrlQueue 17 -#define QDSP_uPAudPreProcCmdQueue 18 -#define QDSP_uPAudRecBitStreamQueue 19 -#define QDSP_uPAudRecCmdQueue 20 -#define QDSP_uPJpegActionCmdQueue 21 -#define QDSP_uPJpegCfgCmdQueue 22 -#define QDSP_uPVocProcQueue 23 -#define QDSP_vfeCommandQueue 24 -#define QDSP_vfeCommandScaleQueue 25 -#define QDSP_vfeCommandTableQueue 26 -#define QDSP_QUEUE_MAX 26 - /* Value to indicate that a queue is not defined for a particular image */ #define QDSP_RTOS_NO_QUEUE 0xffffffff diff --git a/arch/arm/mach-msm/qdsp5/adsp_new.h b/arch/arm/mach-msm/qdsp5/adsp_new.h index 963c9efe53a47..963b2f8c76a50 100644 --- a/arch/arm/mach-msm/qdsp5/adsp_new.h +++ b/arch/arm/mach-msm/qdsp5/adsp_new.h @@ -260,37 +260,6 @@ struct msm_adsp_module { extern void msm_adsp_publish_cdevs(struct msm_adsp_module *, unsigned); extern int adsp_init_info(struct adsp_info *info); -/* Command Queue Indexes */ -#define QDSP_lpmCommandQueue 0 -#define QDSP_mpuAfeQueue 1 -#define QDSP_mpuGraphicsCmdQueue 2 -#define QDSP_mpuModmathCmdQueue 3 -#define QDSP_mpuVDecCmdQueue 4 -#define QDSP_mpuVDecPktQueue 5 -#define QDSP_mpuVEncCmdQueue 6 -#define QDSP_rxMpuDecCmdQueue 7 -#define QDSP_rxMpuDecPktQueue 8 -#define QDSP_txMpuEncQueue 9 -#define QDSP_uPAudPPCmd1Queue 10 -#define QDSP_uPAudPPCmd2Queue 11 -#define QDSP_uPAudPPCmd3Queue 12 -#define QDSP_uPAudPlay0BitStreamCtrlQueue 13 -#define QDSP_uPAudPlay1BitStreamCtrlQueue 14 -#define QDSP_uPAudPlay2BitStreamCtrlQueue 15 -#define QDSP_uPAudPlay3BitStreamCtrlQueue 16 -#define QDSP_uPAudPlay4BitStreamCtrlQueue 17 -#define QDSP_uPAudPreProcCmdQueue 18 -#define QDSP_uPAudRecBitStreamQueue 19 -#define QDSP_uPAudRecCmdQueue 20 -#define QDSP_uPDiagQueue 21 -#define QDSP_uPJpegActionCmdQueue 22 -#define QDSP_uPJpegCfgCmdQueue 23 -#define QDSP_uPVocProcQueue 24 -#define QDSP_vfeCommandQueue 25 -#define QDSP_vfeCommandScaleQueue 26 -#define QDSP_vfeCommandTableQueue 27 -#define QDSP_MAX_NUM_QUEUES 28 - /* Value to indicate that a queue is not defined for a particular image */ #define QDSP_RTOS_NO_QUEUE 0xfffffffe From 82331e9ebc2abc5e3ef36ec079eeb2bba3c8e2ce Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Wed, 22 Apr 2009 16:26:47 -0700 Subject: [PATCH 0313/2556] [ARM] msm: adsp: move struct adsp_pmem_info to msm_adsp.h Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/qdsp5/adsp_driver.c | 5 ----- include/linux/msm_adsp.h | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-msm/qdsp5/adsp_driver.c b/arch/arm/mach-msm/qdsp5/adsp_driver.c index 577cffd2da324..8197765aae1e8 100644 --- a/arch/arm/mach-msm/qdsp5/adsp_driver.c +++ b/arch/arm/mach-msm/qdsp5/adsp_driver.c @@ -27,11 +27,6 @@ #include #include -struct adsp_pmem_info { - int fd; - void *vaddr; -}; - struct adsp_pmem_region { struct hlist_node list; void *vaddr; diff --git a/include/linux/msm_adsp.h b/include/linux/msm_adsp.h index c739588b6846d..12f219ea63d27 100644 --- a/include/linux/msm_adsp.h +++ b/include/linux/msm_adsp.h @@ -60,6 +60,11 @@ struct adsp_event_t { #define ADSP_IOCTL_DISABLE_EVENT_RSP \ _IOR(ADSP_IOCTL_MAGIC, 10, unsigned) +struct adsp_pmem_info { + int fd; + void *vaddr; +}; + #define ADSP_IOCTL_REGISTER_PMEM \ _IOW(ADSP_IOCTL_MAGIC, 13, unsigned) From 0e7bc24c242a168ddf01edd99e0dec4b79179bf2 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 2 Jun 2009 12:21:56 -0700 Subject: [PATCH 0314/2556] [ARM] halibut: support for 6350 -- enable msm_fb -- add mt9t013 device -- add simple power supply driver -- enable fake battery device Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/board-halibut-panel.c | 73 +++++++ arch/arm/mach-msm/board-halibut.c | 252 +++++++++++++++++++++++- arch/arm/mach-msm/board-halibut.h | 18 ++ arch/arm/mach-msm/board-trout-panel.c | 2 +- arch/arm/mach-msm/fish_battery.c | 145 ++++++++++++++ arch/arm/mach-msm/include/mach/memory.h | 2 + 7 files changed, 490 insertions(+), 3 deletions(-) create mode 100644 arch/arm/mach-msm/board-halibut-panel.c create mode 100644 arch/arm/mach-msm/board-halibut.h create mode 100644 arch/arm/mach-msm/fish_battery.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 98528237bffa4..440f601b7d909 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-rfkill.o msm_vibrator.o obj-$(CONFIG_MACH_SAPPHIRE) += devices-msm7x00.o devices_htc.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o +obj-$(CONFIG_MACH_HALIBUT) += board-halibut-panel.o fish_battery.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o obj-$(CONFIG_ARCH_QSD8X50) += board-qsd8x50.o devices-qsd8x50.o obj-$(CONFIG_ARCH_MSM8X60) += board-msm8x60.o diff --git a/arch/arm/mach-msm/board-halibut-panel.c b/arch/arm/mach-msm/board-halibut-panel.c new file mode 100644 index 0000000000000..a498c65344b52 --- /dev/null +++ b/arch/arm/mach-msm/board-halibut-panel.c @@ -0,0 +1,73 @@ +/* linux/arch/arm/mach-msm/board-halibut-mddi.c +** Author: Brian Swetland +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "proc_comm.h" +#include "devices.h" +#include "board-halibut.h" + +static void halibut_mddi_power_client(struct msm_mddi_client_data *mddi, + int on) +{ +} + +static struct resource resources_msm_fb = { + .start = MSM_FB_BASE, + .end = MSM_FB_BASE + MSM_FB_SIZE - 1, + .flags = IORESOURCE_MEM, +}; + +static struct msm_fb_data fb_data = { + .xres = 800, + .yres = 480, + .output_format = 0, +}; + +static struct msm_mddi_platform_data mddi_pdata = { + .clk_rate = 122880000, + .power_client = halibut_mddi_power_client, + .fb_resource = &resources_msm_fb, + .num_clients = 1, + .client_platform_data = { + { + .product_id = (0x4474 << 16 | 0xc065), + .name = "mddi_c_dummy", + .id = 0, + .client_data = &fb_data, + .clk_rate = 0, + }, + }, +}; + +int __init halibut_init_panel(void) +{ + int rc; + + if (!machine_is_halibut()) + return 0; + + rc = platform_device_register(&msm_device_mdp); + if (rc) + return rc; + + msm_device_mddi0.dev.platform_data = &mddi_pdata; + return platform_device_register(&msm_device_mddi0); +} + +device_initcall(halibut_init_panel); diff --git a/arch/arm/mach-msm/board-halibut.c b/arch/arm/mach-msm/board-halibut.c index ffd5d966e1719..5912972996abc 100644 --- a/arch/arm/mach-msm/board-halibut.c +++ b/arch/arm/mach-msm/board-halibut.c @@ -20,9 +20,11 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -32,12 +34,23 @@ #include #include #include +#include #include #include #include +#include + +#ifdef CONFIG_USB_FUNCTION +#include +#endif +#ifdef CONFIG_USB_ANDROID +#include +#endif #include "devices.h" +#include "board-halibut.h" +#include "proc_comm.h" static struct resource smc91x_resources[] = { [0] = { @@ -62,7 +75,83 @@ static struct platform_device smc91x_device = { static struct i2c_board_info i2c_devices[] = { { I2C_BOARD_INFO("mt9t013", 0x78>>1), - /* .irq = TROUT_GPIO_TO_INT(TROUT_GPIO_CAM_BTN_STEP1_N), */ + }, +}; + +static uint32_t camera_off_gpio_table[] = { + /* parallel CAMERA interfaces */ + PCOM_GPIO_CFG(0, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT0 */ + PCOM_GPIO_CFG(1, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT1 */ + PCOM_GPIO_CFG(2, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* PCLK */ + PCOM_GPIO_CFG(13, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */ + PCOM_GPIO_CFG(14, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */ + PCOM_GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), /* MCLK */ +}; + +static uint32_t camera_on_gpio_table[] = { + /* parallel CAMERA interfaces */ + PCOM_GPIO_CFG(0, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT0 */ + PCOM_GPIO_CFG(1, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT1 */ + PCOM_GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_16MA), /* PCLK */ + PCOM_GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */ + PCOM_GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */ + PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_16MA), /* MCLK */ +}; + +static void config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for (n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +static void config_camera_on_gpios(void) +{ + config_gpio_table(camera_on_gpio_table, + ARRAY_SIZE(camera_on_gpio_table)); +} + +static void config_camera_off_gpios(void) +{ + config_gpio_table(camera_off_gpio_table, + ARRAY_SIZE(camera_off_gpio_table)); +} + +static struct msm_camera_device_platform_data msm_camera_device = { + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 0, + .config_gpio_on = config_camera_on_gpios, + .config_gpio_off = config_camera_off_gpios, +}; + +static struct platform_device halibut_camera = { + .name = "camera", + .dev = { + .platform_data = &msm_camera_device, }, }; @@ -89,6 +178,149 @@ static struct platform_device halibut_snd = { }, }; +static struct android_pmem_platform_data android_pmem_pdata = { + .name = "pmem", + .start = MSM_PMEM_MDP_BASE, + .size = MSM_PMEM_MDP_SIZE, + .no_allocator = 0, + .cached = 1, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .start = MSM_PMEM_ADSP_BASE, + .size = MSM_PMEM_ADSP_SIZE, + .no_allocator = 0, + .cached = 0, +}; + +static struct android_pmem_platform_data android_pmem_gpu0_pdata = { + .name = "pmem_gpu0", + .start = MSM_PMEM_GPU0_BASE, + .size = MSM_PMEM_GPU0_SIZE, + .no_allocator = 1, + .cached = 0, +}; + +static struct android_pmem_platform_data android_pmem_gpu1_pdata = { + .name = "pmem_gpu1", + .start = MSM_PMEM_GPU1_BASE, + .size = MSM_PMEM_GPU1_SIZE, + .no_allocator = 1, + .cached = 0, +}; + +static struct platform_device android_pmem_device = { + .name = "android_pmem", + .id = 0, + .dev = { .platform_data = &android_pmem_pdata }, +}; + +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 1, + .dev = { .platform_data = &android_pmem_adsp_pdata }, +}; + +static struct platform_device android_pmem_gpu0_device = { + .name = "android_pmem", + .id = 2, + .dev = { .platform_data = &android_pmem_gpu0_pdata }, +}; + +static struct platform_device android_pmem_gpu1_device = { + .name = "android_pmem", + .id = 3, + .dev = { .platform_data = &android_pmem_gpu1_pdata }, +}; + + +#ifdef CONFIG_USB_FUNCTION +static char *halibut_usb_functions[] = { +#if defined(CONFIG_USB_FUNCTION_MASS_STORAGE) || \ + defined(CONFIG_USB_FUNCTION_UMS) + "usb_mass_storage", +#endif +#ifdef CONFIG_USB_FUNCTION_ADB + "adb", +#endif +}; + +static struct msm_hsusb_product halibut_usb_products[] = { +{ +.product_id = 0x0c01, +.functions = 0x00000001, /* "usb_mass_storage" only */ +}, +{ +.product_id = 0x0c02, +.functions = 0x00000003, /* "usb_mass_storage" and "adb" */ +}, +}; +#endif + +static int halibut_phy_init_seq[] = { 0x1D, 0x0D, 0x1D, 0x10, -1 }; + +static struct msm_hsusb_platform_data msm_hsusb_pdata = { + .phy_init_seq = halibut_phy_init_seq, +#ifdef CONFIG_USB_FUNCTION + .vendor_id = 0x18d1, + .product_id = 0x0c02, + .version = 0x0100, + .product_name = "Halibut", + .serial_number = "42", + .manufacturer_name = "Qualcomm", + + .functions = halibut_usb_functions, + .num_functions = ARRAY_SIZE(halibut_usb_functions), + .products = halibut_usb_products, + .num_products = ARRAY_SIZE(halibut_usb_products), +#endif +}; + +#ifdef CONFIG_USB_FUNCTION_MASS_STORAGE +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .buf_size = 16384, + .vendor = "Qualcomm", + .product = "Halibut", + .release = 0x0100, +}; + +static struct platform_device usb_mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &mass_storage_pdata, + }, +}; +#endif + +#ifdef CONFIG_USB_ANDROID +static struct android_usb_platform_data android_usb_pdata = { + .vendor_id = 0x18d1, + .product_id = 0x0c01, + .adb_product_id = 0x0c02, + .version = 0x0100, + .serial_number = "42", + .product_name = "Halibutdroid", + .manufacturer_name = "Qualcomm", + .nluns = 1, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; +#endif + + +static struct platform_device fish_battery_device = { + .name = "fish_battery", +}; + static struct platform_device *devices[] __initdata = { #if !defined(CONFIG_MSM_SERIAL_DEBUGGER) &msm_device_uart3, @@ -96,9 +328,21 @@ static struct platform_device *devices[] __initdata = { &msm_device_smd, &msm_device_nand, &msm_device_hsusb, +#ifdef CONFIG_USB_FUNCTION_MASS_STORAGE + &usb_mass_storage_device, +#endif +#ifdef CONFIG_USB_ANDROID + &android_usb_device, +#endif &msm_device_i2c, &smc91x_device, &halibut_snd, + &halibut_camera, + &android_pmem_device, + &android_pmem_adsp_device, + &android_pmem_gpu0_device, + &android_pmem_gpu1_device, + &fish_battery_device, }; extern struct sys_timer msm_timer; @@ -116,7 +360,7 @@ static struct msm_acpu_clock_platform_data halibut_clock_data = { .wait_for_irq_khz = 128000000, }; -void msm_serial_debug_init(unsigned int base, int irq, +extern void msm_serial_debug_init(unsigned int base, int irq, struct device *clk_device, int signal_irq); static void __init halibut_init(void) @@ -125,9 +369,11 @@ static void __init halibut_init(void) msm_serial_debug_init(MSM_UART3_PHYS, INT_UART3, &msm_device_uart3.dev, 1); #endif + msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; msm_acpu_clock_init(&halibut_clock_data); platform_add_devices(devices, ARRAY_SIZE(devices)); i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); + msm_hsusb_set_vbus_state(1); } static void __init halibut_fixup(struct machine_desc *desc, struct tag *tags, @@ -146,6 +392,8 @@ static void __init halibut_map_io(void) MACHINE_START(HALIBUT, "Halibut Board (QCT SURF7200A)") #ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, #endif .boot_params = 0x10000100, .fixup = halibut_fixup, diff --git a/arch/arm/mach-msm/board-halibut.h b/arch/arm/mach-msm/board-halibut.h new file mode 100644 index 0000000000000..d04dcf7599aef --- /dev/null +++ b/arch/arm/mach-msm/board-halibut.h @@ -0,0 +1,18 @@ +/* linux/arch/arm/mach-msm/board-trout.h + * ** Author: Brian Swetland + * */ +#ifndef __ARCH_ARM_MACH_MSM_BOARD_HALIBUT_H +#define __ARCH_ARM_MACH_MSM_BOARD_HALIBUT_H + +#define MSM_PMEM_GPU0_BASE (0x10000000 + 64*SZ_1M) +#define MSM_PMEM_GPU0_SIZE 0x800000 +#define MSM_PMEM_MDP_BASE (MSM_PMEM_GPU0_BASE + MSM_PMEM_GPU0_SIZE) +#define MSM_PMEM_MDP_SIZE 0x800000 +#define MSM_PMEM_ADSP_BASE (MSM_PMEM_MDP_BASE + MSM_PMEM_MDP_SIZE) +#define MSM_PMEM_ADSP_SIZE 0x800000 +#define MSM_PMEM_GPU1_BASE (MSM_PMEM_ADSP_BASE + MSM_PMEM_ADSP_SIZE) +#define MSM_PMEM_GPU1_SIZE 0x800000 +#define MSM_FB_BASE (MSM_PMEM_ADSP_BASE + MSM_PMEM_ADSP_SIZE) +#define MSM_FB_SIZE 0x200000 + +#endif diff --git a/arch/arm/mach-msm/board-trout-panel.c b/arch/arm/mach-msm/board-trout-panel.c index 900b8b1a6f76f..dfd32d03cb414 100644 --- a/arch/arm/mach-msm/board-trout-panel.c +++ b/arch/arm/mach-msm/board-trout-panel.c @@ -572,7 +572,7 @@ struct msm_mddi_toshiba_client_data toshiba_client_data = { }, }; -struct msm_mddi_platform_data mddi_pdata = { +static struct msm_mddi_platform_data mddi_pdata = { .clk_rate = 122880000, .power_client = trout_mddi_power_client, .fb_resource = resources_msm_fb, diff --git a/arch/arm/mach-msm/fish_battery.c b/arch/arm/mach-msm/fish_battery.c new file mode 100644 index 0000000000000..19fbb91fe83ae --- /dev/null +++ b/arch/arm/mach-msm/fish_battery.c @@ -0,0 +1,145 @@ +/* arch/arm/mach-msm/fish_battery.c + * + * Copyright (C) 2008 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. + * + * based on: arch/arm/mach-msm/htc_battery.c + */ + +#include +#include +#include +#include +#include +#include + +static enum power_supply_property fish_battery_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CAPACITY, +}; + +static enum power_supply_property fish_power_properties[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static char *supply_list[] = { + "battery", +}; + +static int fish_power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val); + +static int fish_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val); + +static struct power_supply fish_power_supplies[] = { + { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = fish_battery_properties, + .num_properties = ARRAY_SIZE(fish_battery_properties), + .get_property = fish_battery_get_property, + }, + { + .name = "ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .supplied_to = supply_list, + .num_supplicants = ARRAY_SIZE(supply_list), + .properties = fish_power_properties, + .num_properties = ARRAY_SIZE(fish_power_properties), + .get_property = fish_power_get_property, + }, +}; + +static int fish_power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if (psy->type == POWER_SUPPLY_TYPE_MAINS) + val->intval = 1; + else + val->intval = 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int fish_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = POWER_SUPPLY_STATUS_FULL; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = 100; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int fish_battery_probe(struct platform_device *pdev) +{ + int i; + int rc; + + /* init power supplier framework */ + for (i = 0; i < ARRAY_SIZE(fish_power_supplies); i++) { + rc = power_supply_register(&pdev->dev, &fish_power_supplies[i]); + if (rc) + pr_err("%s: Failed to register power supply (%d)\n", + __func__, rc); + } + + return 0; +} + +static struct platform_driver fish_battery_driver = { + .probe = fish_battery_probe, + .driver = { + .name = "fish_battery", + .owner = THIS_MODULE, + }, +}; + +static int __init fish_battery_init(void) +{ + platform_driver_register(&fish_battery_driver); + return 0; +} + +module_init(fish_battery_init); +MODULE_DESCRIPTION("Qualcomm fish battery driver"); +MODULE_LICENSE("GPL"); + diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h index acb045b700794..dffe9bb01862e 100644 --- a/arch/arm/mach-msm/include/mach/memory.h +++ b/arch/arm/mach-msm/include/mach/memory.h @@ -31,5 +31,7 @@ #define HAS_ARCH_IO_REMAP_PFN_RANGE +#define CONSISTENT_DMA_SIZE (4*SZ_1M) + #endif From 5ecfce5259c9162f1d2734abbf4a2ecaa8948bb8 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Mon, 1 Jun 2009 13:06:05 -0700 Subject: [PATCH 0315/2556] [ARM] msm: board file changes for camera support Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-halibut.c | 135 ++++++++++++++++++-- arch/arm/mach-msm/board-halibut.h | 4 +- arch/arm/mach-msm/board-sapphire.c | 169 +++++++++++++++++-------- arch/arm/mach-msm/board-trout.c | 78 +++++++++--- arch/arm/mach-msm/include/mach/board.h | 27 ++++ 5 files changed, 331 insertions(+), 82 deletions(-) diff --git a/arch/arm/mach-msm/board-halibut.c b/arch/arm/mach-msm/board-halibut.c index 5912972996abc..6161e0db8ccdd 100644 --- a/arch/arm/mach-msm/board-halibut.c +++ b/arch/arm/mach-msm/board-halibut.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -73,11 +74,29 @@ static struct platform_device smc91x_device = { }; static struct i2c_board_info i2c_devices[] = { +#ifdef CONFIG_MT9D112 { - I2C_BOARD_INFO("mt9t013", 0x78>>1), + I2C_BOARD_INFO("mt9d112", 0x78 >> 1), }, +#endif +#ifdef CONFIG_S5K3E2FX + { + I2C_BOARD_INFO("s5k3e2fx", 0x20 >> 1), + }, +#endif +#ifdef CONFIG_MT9P012 + { + I2C_BOARD_INFO("mt9p012", 0x6C >> 1), + }, +#endif +#if defined(CONFIG_MT9T013) || defined(CONFIG_SENSORS_MT9T013) + { + I2C_BOARD_INFO("mt9t013", 0x6C), // 0x78>>1 + }, +#endif }; +#ifdef CONFIG_MSM_CAMERA static uint32_t camera_off_gpio_table[] = { /* parallel CAMERA interfaces */ PCOM_GPIO_CFG(0, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT0 */ @@ -140,20 +159,83 @@ static void config_camera_off_gpios(void) ARRAY_SIZE(camera_off_gpio_table)); } -static struct msm_camera_device_platform_data msm_camera_device = { - .sensor_reset = 89, - .sensor_pwd = 85, - .vcm_pwd = 0, - .config_gpio_on = config_camera_on_gpios, - .config_gpio_off = config_camera_off_gpios, +static struct msm_camera_device_platform_data msm_camera_device_data = { + .camera_gpio_on = config_camera_on_gpios, + .camera_gpio_off = config_camera_off_gpios, + .ioext.mdcphy = MSM_MDC_PHYS, + .ioext.mdcsz = MSM_MDC_SIZE, + .ioext.appphy = MSM_CLK_CTL_PHYS, + .ioext.appsz = MSM_CLK_CTL_SIZE, +}; + +#ifdef CONFIG_MT9D112 +static struct msm_camera_sensor_info msm_camera_sensor_mt9d112_data = { + .sensor_name = "mt9d112", + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 0, + .pdata = &msm_camera_device_data }; -static struct platform_device halibut_camera = { - .name = "camera", - .dev = { - .platform_data = &msm_camera_device, +static struct platform_device msm_camera_sensor_mt9d112 = { + .name = "msm_camera_mt9d112", + .dev = { + .platform_data = &msm_camera_sensor_mt9d112_data, }, }; +#endif + +#ifdef CONFIG_S5K3E2FX +static struct msm_camera_sensor_info msm_camera_sensor_s5k3e2fx_data = { + .sensor_name = "s5k3e2fx", + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 0, + .pdata = &msm_camera_device_data +}; + +static struct platform_device msm_camera_sensor_s5k3e2fx = { + .name = "msm_camera_s5k3e2fx", + .dev = { + .platform_data = &msm_camera_sensor_s5k3e2fx_data, + }, +}; +#endif + +#ifdef CONFIG_MT9P012 +static struct msm_camera_sensor_info msm_camera_sensor_mt9p012_data = { + .sensor_name = "mt9p012", + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 88, + .pdata = &msm_camera_device_data +}; + +static struct platform_device msm_camera_sensor_mt9p012 = { + .name = "msm_camera_mt9p012", + .dev = { + .platform_data = &msm_camera_sensor_mt9p012_data, + }, +}; +#endif + +#ifdef CONFIG_MT9T013 +static struct msm_camera_sensor_info msm_camera_sensor_mt9t013_data = { + .sensor_name = "mt9t013", + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 0, + .pdata = &msm_camera_device_data +}; + +static struct platform_device msm_camera_sensor_mt9t013 = { + .name = "msm_camera_mt9t013", + .dev = { + .platform_data = &msm_camera_sensor_mt9t013_data, + }, +}; +#endif +#endif /*CONFIG_MSM_CAMERA*/ #define SND(desc, num) { .name = #desc, .id = num } static struct snd_endpoint snd_endpoints_list[] = { @@ -186,6 +268,14 @@ static struct android_pmem_platform_data android_pmem_pdata = { .cached = 1, }; +static struct android_pmem_platform_data android_pmem_camera_pdata = { + .name = "pmem_camera", + .start = MSM_PMEM_CAMERA_BASE, + .size = MSM_PMEM_CAMERA_SIZE, + .no_allocator = 1, + .cached = 1, +}; + static struct android_pmem_platform_data android_pmem_adsp_pdata = { .name = "pmem_adsp", .start = MSM_PMEM_ADSP_BASE, @@ -234,6 +324,11 @@ static struct platform_device android_pmem_gpu1_device = { .dev = { .platform_data = &android_pmem_gpu1_pdata }, }; +static struct platform_device android_pmem_camera_device = { + .name = "android_pmem", + .id = 4, + .dev = { .platform_data = &android_pmem_camera_pdata }, +}; #ifdef CONFIG_USB_FUNCTION static char *halibut_usb_functions[] = { @@ -337,11 +432,23 @@ static struct platform_device *devices[] __initdata = { &msm_device_i2c, &smc91x_device, &halibut_snd, - &halibut_camera, +#ifdef CONFIG_MT9T013 + &msm_camera_sensor_mt9t013, +#endif +#ifdef CONFIG_MT9D112 + &msm_camera_sensor_mt9d112, +#endif +#ifdef CONFIG_S5K3E2FX + &msm_camera_sensor_s5k3e2fx, +#endif +#ifdef CONFIG_MT9P012 + &msm_camera_sensor_mt9p012, +#endif &android_pmem_device, &android_pmem_adsp_device, &android_pmem_gpu0_device, &android_pmem_gpu1_device, + &android_pmem_camera_device, &fish_battery_device, }; @@ -371,6 +478,10 @@ static void __init halibut_init(void) #endif msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; msm_acpu_clock_init(&halibut_clock_data); +#ifdef CONFIG_MSM_CAMERA + config_camera_off_gpios(); /* might not be necessary */ +#endif + i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); platform_add_devices(devices, ARRAY_SIZE(devices)); i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); msm_hsusb_set_vbus_state(1); diff --git a/arch/arm/mach-msm/board-halibut.h b/arch/arm/mach-msm/board-halibut.h index d04dcf7599aef..edcdacb34c274 100644 --- a/arch/arm/mach-msm/board-halibut.h +++ b/arch/arm/mach-msm/board-halibut.h @@ -12,7 +12,9 @@ #define MSM_PMEM_ADSP_SIZE 0x800000 #define MSM_PMEM_GPU1_BASE (MSM_PMEM_ADSP_BASE + MSM_PMEM_ADSP_SIZE) #define MSM_PMEM_GPU1_SIZE 0x800000 -#define MSM_FB_BASE (MSM_PMEM_ADSP_BASE + MSM_PMEM_ADSP_SIZE) +#define MSM_FB_BASE (MSM_PMEM_GPU1_BASE + MSM_PMEM_GPU1_SIZE) #define MSM_FB_SIZE 0x200000 +#define MSM_PMEM_CAMERA_BASE (MSM_FB_BASE + MSM_FB_SIZE) +#define MSM_PMEM_CAMERA_SIZE 0xA00000 #endif diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index 429a56f470824..1ecb85283cd13 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -195,23 +195,23 @@ static struct gpio_event_platform_data sapphire_nav_data = { static struct platform_device sapphire_nav_device = { .name = GPIO_EVENT_DEV_NAME, .id = 2, - .dev = { - .platform_data = &sapphire_nav_data, + .dev = { + .platform_data = &sapphire_nav_data, }, }; /* a new search button to be a wake-up source */ static struct gpio_event_direct_entry sapphire_search_button_v1[] = { - { SAPPHIRE_GPIO_SEARCH_ACT_N, KEY_COMPOSE }, /* CPLD Key Search*/ + { SAPPHIRE_GPIO_SEARCH_ACT_N, KEY_COMPOSE }, /* CPLD Key Search*/ }; static struct gpio_event_direct_entry sapphire_search_button_v2[] = { - { SAPPHIRE_GPIO_SEARCH_ACT_N, KEY_HOME }, // CPLD Key Home + { SAPPHIRE_GPIO_SEARCH_ACT_N, KEY_HOME }, /* CPLD Key Home */ }; static struct gpio_event_input_info sapphire_search_button_info = { .info.func = gpio_event_input_func, - //.flags = GPIOEDF_PRINT_KEYS | GPIOEDF_PRINT_KEY_DEBOUNCE, + /* .flags = GPIOEDF_PRINT_KEYS | GPIOEDF_PRINT_KEY_DEBOUNCE, */ .flags = 0, .poll_time.tv.nsec = 40 * NSEC_PER_MSEC, .type = EV_KEY, @@ -232,8 +232,8 @@ static struct gpio_event_platform_data sapphire_search_button_data = { static struct platform_device sapphire_search_button_device = { .name = GPIO_EVENT_DEV_NAME, .id = 1, - .dev = { - .platform_data = &sapphire_search_button_data, + .dev = { + .platform_data = &sapphire_search_button_data, }, }; @@ -350,21 +350,6 @@ static struct elan_i2c_platform_data elan_i2c_data[] = { } }; -static struct msm_camera_device_platform_data msm_camera_device_mt9t013 = { - .sensor_reset = 108, - .sensor_pwd = 85, - .vcm_pwd = SAPPHIRE_GPIO_VCM_PWDN, - .config_gpio_on = config_sapphire_camera_on_gpios, - .config_gpio_off = config_sapphire_camera_off_gpios, -}; - -static struct platform_device sapphire_camera = { - .name = "camera", - .dev = { - .platform_data = &msm_camera_device_mt9t013, - }, -}; - static struct i2c_board_info i2c_devices[] = { { I2C_BOARD_INFO(SYNAPTICS_I2C_RMI_NAME, 0x20), @@ -381,10 +366,23 @@ static struct i2c_board_info i2c_devices[] = { .platform_data = &compass_platform_data, .irq = SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_COMPASS_IRQ), }, +#ifdef CONFIG_MSM_CAMERA +#ifdef CONFIG_MT9P012 + { + I2C_BOARD_INFO("mt9p012", 0x6C >> 1), + }, +#endif +#ifdef CONFIG_MT9T013 + { + I2C_BOARD_INFO("mt9t013", 0x6C), + }, +#endif +#endif/*CONIFIG_MSM_CAMERA*/ +#ifdef CONFIG_SENSORS_MT9T013 { I2C_BOARD_INFO("mt9t013", 0x6C >> 1), - .platform_data = &msm_camera_device_mt9t013, }, +#endif }; #ifdef CONFIG_LEDS_CPLD @@ -420,7 +418,7 @@ static struct platform_device android_leds = { .name = "leds-gpio", .id = -1, .dev = { - .platform_data = &android_leds_data, + .platform_data = &android_leds_data, }, }; @@ -698,25 +696,25 @@ static struct pwr_sink sapphire_pwrsink_table[] = { static int sapphire_pwrsink_resume_early(struct platform_device *pdev) { htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 7); - return 0; + return 0; } - + static void sapphire_pwrsink_resume_late(struct early_suspend *h) { - htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 38); + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 38); } static void sapphire_pwrsink_suspend_early(struct early_suspend *h) { - htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 7); + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 7); } - + static int sapphire_pwrsink_suspend_late(struct platform_device *pdev, pm_message_t state) { - htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 1); - return 0; + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 1); + return 0; } - + static struct pwr_sink_platform_data sapphire_pwrsink_data = { .num_sinks = ARRAY_SIZE(sapphire_pwrsink_table), .sinks = sapphire_pwrsink_table, @@ -841,6 +839,70 @@ static struct platform_device sapphire_snd = { }, }; +#ifdef CONFIG_MSM_CAMERA +void config_sapphire_camera_on_gpios(void); +void config_sapphire_camera_on_gpios(void); +static struct msm_camera_device_platform_data msm_camera_device_data = { + .camera_gpio_on = config_sapphire_camera_on_gpios, + .camera_gpio_off = config_sapphire_camera_off_gpios, + .ioext.mdcphy = MSM_MDC_PHYS, + .ioext.mdcsz = MSM_MDC_SIZE, + .ioext.appphy = MSM_CLK_CTL_PHYS, + .ioext.appsz = MSM_CLK_CTL_SIZE, +}; + +#ifdef CONFIG_MT9T013 +static struct msm_camera_sensor_info msm_camera_sensor_mt9t013_data = { + .sensor_name = "mt9t013", + .sensor_reset = 108, + .sensor_pwd = 85, + .vcm_pwd = SAPPHIRE_GPIO_VCM_PWDN, + .pdata = &msm_camera_device_data +}; + +static struct platform_device msm_camera_sensor_mt9t013 = { + .name = "msm_camera_mt9t013", + .dev = { + .platform_data = &msm_camera_sensor_mt9t013_data, + }, +}; +#endif + +#ifdef CONFIG_MT9P012 +static struct msm_camera_sensor_info msm_camera_sensor_mt9p012_data = { + .sensor_name = "mt9p012", + .sensor_reset = 108, + .sensor_pwd = 85, + .vcm_pwd = SAPPHIRE_GPIO_VCM_PWDN, + .pdata = &msm_camera_device_data +}; + +static struct platform_device msm_camera_sensor_mt9p012 = { + .name = "msm_camera_mt9p012", + .dev = { + .platform_data = &msm_camera_sensor_mt9p012_data, + }, +}; +#endif +#endif/*CONFIG_MSM_CAMERA*/ + +#ifdef CONFIG_SENSORS_MT9T013 +static struct msm_camera_legacy_device_platform_data msm_camera_device_mt9t013 = { + .sensor_reset = 108, + .sensor_pwd = 85, + .vcm_pwd = SAPPHIRE_GPIO_VCM_PWDN, + .config_gpio_on = config_sapphire_camera_on_gpios, + .config_gpio_off = config_sapphire_camera_off_gpios, +}; + +static struct platform_device sapphire_camera = { + .name = "camera", + .dev = { + .platform_data = &msm_camera_device_mt9t013, + }, +}; +#endif + static struct platform_device *devices[] __initdata = { &msm_device_smd, &msm_device_nand, @@ -861,6 +923,9 @@ static struct platform_device *devices[] __initdata = { #endif #ifdef CONFIG_HTC_HEADSET &sapphire_h2w, +#endif +#ifdef CONFIG_MT9T013 + &msm_camera_sensor_mt9t013, #endif &sapphire_rfkill, #ifdef CONFIG_WIFI_CONTROL_FUNC @@ -871,7 +936,9 @@ static struct platform_device *devices[] __initdata = { &sapphire_pwr_sink, #endif &sapphire_snd, +#ifdef CONFIG_SENSORS_MT9T013 &sapphire_camera, +#endif }; extern struct sys_timer msm_timer; @@ -971,22 +1038,22 @@ static uint32_t camera_off_gpio_12pins_table[] = { static uint32_t camera_on_gpio_12pins_table[] = { /* CAMERA */ - PCOM_GPIO_CFG(0, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT0 */ - PCOM_GPIO_CFG(1, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT1 */ - PCOM_GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */ - PCOM_GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */ - PCOM_GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */ - PCOM_GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */ - PCOM_GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */ - PCOM_GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */ - PCOM_GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */ - PCOM_GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */ - PCOM_GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */ - PCOM_GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */ - PCOM_GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_16MA), /* PCLK */ - PCOM_GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */ - PCOM_GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */ - PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_16MA), /* MCLK */ + PCOM_GPIO_CFG(0, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT0 */ + PCOM_GPIO_CFG(1, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT1 */ + PCOM_GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_16MA), /* PCLK */ + PCOM_GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */ + PCOM_GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */ + PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_16MA), /* MCLK */ }; static void config_gpio_table(uint32_t *table, int len) @@ -1004,10 +1071,10 @@ void config_sapphire_camera_on_gpios(void) /*Add for judage it's 10 pins or 12 pins platform ----->*/ if (is_12pin_camera()) { config_gpio_table(camera_on_gpio_12pins_table, - ARRAY_SIZE(camera_on_gpio_12pins_table)); + ARRAY_SIZE(camera_on_gpio_12pins_table)); } else { config_gpio_table(camera_on_gpio_table, - ARRAY_SIZE(camera_on_gpio_table)); + ARRAY_SIZE(camera_on_gpio_table)); } /*End Of Add for judage it's 10 pins or 12 pins platform*/ } @@ -1115,8 +1182,8 @@ static void __init sapphire_init(void) if(system_rev != 0x80) sapphire_search_button_info.keymap = sapphire_search_button_v1; - platform_add_devices(devices, ARRAY_SIZE(devices)); i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); + platform_add_devices(devices, ARRAY_SIZE(devices)); } static struct map_desc sapphire_io_desc[] __initdata = { diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 9b910d478c00d..8b4e8e489244c 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -94,7 +94,7 @@ uint16_t trout_axis_map(struct gpio_event_axis_info *info, uint16_t in) { struct trout_axis_info *ai = container_of(info, struct trout_axis_info, info); uint16_t out = ai->out_state; - + if (nav_just_on) { if (jiffies == nav_on_jiffies || jiffies == nav_on_jiffies + 1) goto ignore; @@ -188,8 +188,8 @@ static struct gpio_event_platform_data trout_nav_data = { static struct platform_device trout_nav_device = { .name = GPIO_EVENT_DEV_NAME, .id = 2, - .dev = { - .platform_data = &trout_nav_data, + .dev = { + .platform_data = &trout_nav_data, }, }; @@ -285,14 +285,20 @@ static struct i2c_board_info i2c_devices[] = { { I2C_BOARD_INFO("pca963x", 0x62), }, +#if defined(CONFIG_MSM_CAMERA) && defined(CONFIG_MT9T013) + { + I2C_BOARD_INFO("mt9t013", 0x6C), + }, +#endif +#ifdef CONFIG_SENSORS_MT9T013 { I2C_BOARD_INFO("mt9t013", 0x6C >> 1), - /* .irq = TROUT_GPIO_TO_INT(TROUT_GPIO_CAM_BTN_STEP1_N), */ }, +#endif }; static struct timed_gpio timed_gpios[] = { - { + { .name = "vibrator", .gpio = TROUT_GPIO_HAPTIC_PWM, .max_timeout = 15000, @@ -313,20 +319,20 @@ static struct platform_device android_timed_gpios = { .name = "timed-gpio", .id = -1, .dev = { - .platform_data = &timed_gpio_data, + .platform_data = &timed_gpio_data, }, }; static struct gpio_led android_led_list[] = { - { + { .name = "spotlight", .gpio = TROUT_GPIO_SPOTLIGHT_EN, }, - { + { .name = "keyboard-backlight", .gpio = TROUT_GPIO_QTKEY_LED_EN, }, - { + { .name = "button-backlight", .gpio = TROUT_GPIO_UI_LED_EN, }, @@ -341,7 +347,7 @@ static struct platform_device android_leds = { .name = "leds-gpio", .id = -1, .dev = { - .platform_data = &android_leds_data, + .platform_data = &android_leds_data, }, }; @@ -356,7 +362,7 @@ static struct platform_device sd_door_switch = { .name = "switch-gpio", .id = -1, .dev = { - .platform_data = &sd_door_switch_data, + .platform_data = &sd_door_switch_data, }, }; @@ -411,7 +417,37 @@ static void trout_phy_reset(void) static void config_camera_on_gpios(void); static void config_camera_off_gpios(void); -static struct msm_camera_device_platform_data msm_camera_device = { + +#ifdef CONFIG_MSM_CAMERA +static struct msm_camera_device_platform_data msm_camera_device_data = { + .camera_gpio_on = config_camera_on_gpios, + .camera_gpio_off = config_camera_off_gpios, + .ioext.mdcphy = MSM_MDC_PHYS, + .ioext.mdcsz = MSM_MDC_SIZE, + .ioext.appphy = MSM_CLK_CTL_PHYS, + .ioext.appsz = MSM_CLK_CTL_SIZE, +}; + +#ifdef CONFIG_MT9T013 +static struct msm_camera_sensor_info msm_camera_sensor_mt9t013_data = { + .sensor_name = "mt9t013", + .sensor_reset = 108, + .sensor_pwd = 85, + .vcm_pwd = TROUT_GPIO_VCM_PWDN, + .pdata = &msm_camera_device_data +}; + +static struct platform_device msm_camera_sensor_mt9t013 = { + .name = "msm_camera_mt9t013", + .dev = { + .platform_data = &msm_camera_sensor_mt9t013_data, + }, +}; +#endif +#endif + +#ifdef CONFIG_SENSORS_MT9T013 +static struct msm_camera_legacy_device_platform_data msm_camera_device_mt9t013 = { .sensor_reset = 108, .sensor_pwd = 85, .vcm_pwd = TROUT_GPIO_VCM_PWDN, @@ -420,11 +456,12 @@ static struct msm_camera_device_platform_data msm_camera_device = { }; static struct platform_device trout_camera = { - .name = "camera", - .dev = { - .platform_data = &msm_camera_device, + .name = "camera", + .dev = { + .platform_data = &msm_camera_device_mt9t013, }, }; +#endif static struct pwr_sink trout_pwrsink_table[] = { { @@ -571,8 +608,8 @@ static struct snd_endpoint snd_endpoints_list[] = { #undef SND static struct msm_snd_endpoints trout_snd_endpoints = { - .endpoints = snd_endpoints_list, - .num = ARRAY_SIZE(snd_endpoints_list), + .endpoints = snd_endpoints_list, + .num = ARRAY_SIZE(snd_endpoints_list), }; static struct platform_device trout_snd = { @@ -599,7 +636,12 @@ static struct platform_device *devices[] __initdata = { &android_leds, &sd_door_switch, &android_timed_gpios, +#ifdef CONFIG_MT9T013 + &msm_camera_sensor_mt9t013, +#endif +#ifdef CONFIG_SENSORS_MT9T013 &trout_camera, +#endif &trout_rfkill, #ifdef CONFIG_WIFI_CONTROL_FUNC &trout_wifi, @@ -798,7 +840,7 @@ static struct map_desc trout_io_desc[] __initdata = { }; static void __init trout_fixup(struct machine_desc *desc, struct tag *tags, - char **cmdline, struct meminfo *mi) + char **cmdline, struct meminfo *mi) { mi->nr_banks=1; mi->bank[0].start = PHYS_OFFSET; diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index 54069810b0bfb..6d81780b34216 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -31,13 +31,40 @@ struct msm_acpu_clock_platform_data unsigned long wait_for_irq_khz; }; +struct msm_camera_io_ext { + uint32_t mdcphy; + uint32_t mdcsz; + uint32_t appphy; + uint32_t appsz; +}; + struct msm_camera_device_platform_data { + void (*camera_gpio_on) (void); + void (*camera_gpio_off)(void); + struct msm_camera_io_ext ioext; +}; + +#ifdef CONFIG_SENSORS_MT9T013 +struct msm_camera_legacy_device_platform_data { int sensor_reset; int sensor_pwd; int vcm_pwd; void (*config_gpio_on) (void); void (*config_gpio_off)(void); }; +#endif + +#define MSM_CAMERA_FLASH_NONE 0 +#define MSM_CAMERA_FLASH_LED 1 + +struct msm_camera_sensor_info { + const char *sensor_name; + int sensor_reset; + int sensor_pwd; + int vcm_pwd; + int mclk; + struct msm_camera_device_platform_data *pdata; +}; struct snd_endpoint { int id; From 851c55f803a453adcba5eb538189917c8119540e Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Wed, 1 Apr 2009 17:29:08 -0700 Subject: [PATCH 0316/2556] [ARM] msm: AMSS >= 6350 swaps VFE_CLK and VFE_MDC_CLK Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/clock-pcom.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/clock-pcom.h b/arch/arm/mach-msm/clock-pcom.h index 18dcdd53a7690..c126fb96135b3 100644 --- a/arch/arm/mach-msm/clock-pcom.h +++ b/arch/arm/mach-msm/clock-pcom.h @@ -72,8 +72,13 @@ #define P_USB_HS_P_CLK 37 /* High speed USB pbus clock */ #define P_USB_OTG_CLK 38 /* Full speed USB clock */ #define P_VDC_CLK 39 /* Video controller clock */ -#define P_VFE_MDC_CLK 40 /* Camera / Video Front End clock */ -#define P_VFE_CLK 41 /* VFE MDDI client clock */ +#if CONFIG_MSM_AMSS_VERSION >= 6350 +#define VFE_MDC_CLK 40 /* VFE MDDI client clock */ +#define VFE_CLK 41 /* Camera / Video Front End clock */ +#else/* For radio code base others */ +#define VFE_MDC_CLK 41 /* VFE MDDI client clock */ +#define VFE_CLK 40 /* Camera / Video Front End clock */ +#endif #define P_MDP_LCDC_PCLK_CLK 42 #define P_MDP_LCDC_PAD_PCLK_CLK 43 #define P_MDP_VSYNC_CLK 44 From 91558472838ff4ef9f7e46f72629f8374d922756 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Mon, 1 Jun 2009 13:02:20 -0700 Subject: [PATCH 0317/2556] [ARM] msm: report ADSP events #ifdef CONFIG_MSM_ADSP_REPORT_EVENTS Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/Kconfig | 8 ++++++++ arch/arm/mach-msm/qdsp5/adsp.c | 20 ++++++++++++++++++++ arch/arm/mach-msm/qdsp5/adsp.h | 3 ++- arch/arm/mach-msm/qdsp5/adsp_new.c | 4 ++++ arch/arm/mach-msm/qdsp5/adsp_new.h | 2 +- 5 files changed, 35 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index ad0bd9681475e..feb3cb323706c 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -462,6 +462,14 @@ config MSM_ADSP help Provides access to registers needed by the userspace aDSP library. +config MSM_ADSP_REPORT_EVENTS + bool "Report modem events from the DSP" + default y + depends on MSM_ADSP + help + Normally, only messages from the aDSP are reported to userspace. + With this option, we report events from the aDSP as well. + config WIFI_CONTROL_FUNC bool "Enable WiFi control function abstraction" help diff --git a/arch/arm/mach-msm/qdsp5/adsp.c b/arch/arm/mach-msm/qdsp5/adsp.c index 32b69efedc3f8..0eb19a890bfe0 100644 --- a/arch/arm/mach-msm/qdsp5/adsp.c +++ b/arch/arm/mach-msm/qdsp5/adsp.c @@ -427,6 +427,19 @@ int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, return ret_status; } +#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS +static void *modem_event_addr; +static void read_modem_event(void *buf, size_t len) +{ + uint32_t *dptr = buf; + struct rpc_adsp_rtos_modem_to_app_args_t *sptr = + modem_event_addr; + dptr[0] = be32_to_cpu(sptr->event); + dptr[1] = be32_to_cpu(sptr->module); + dptr[2] = be32_to_cpu(sptr->image); +} +#endif + static void handle_adsp_rtos_mtoa_app(struct rpc_request_hdr *req) { struct rpc_adsp_rtos_modem_to_app_args_t *args = @@ -482,6 +495,13 @@ static void handle_adsp_rtos_mtoa_app(struct rpc_request_hdr *req) RPC_ACCEPTSTAT_SUCCESS); done: mutex_unlock(&module->lock); +#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS + modem_event_addr = (uint32_t *)req; + module->ops->event(module->driver_data, + EVENT_MSG_ID, + EVENT_LEN, + read_modem_event); +#endif } static int handle_adsp_rtos_mtoa(struct rpc_request_hdr *req) diff --git a/arch/arm/mach-msm/qdsp5/adsp.h b/arch/arm/mach-msm/qdsp5/adsp.h index e0b93779308ba..1b53d9edd86f9 100644 --- a/arch/arm/mach-msm/qdsp5/adsp.h +++ b/arch/arm/mach-msm/qdsp5/adsp.h @@ -71,13 +71,14 @@ struct adsp_module_info { }; #define ADSP_EVENT_MAX_SIZE 496 +#define EVENT_LEN 12 #define EVENT_MSG_ID ((uint16_t)~0) struct adsp_event { struct list_head list; uint32_t size; /* always in bytes */ uint16_t msg_id; - uint16_t type; /* 0 for msgs (from aDSP), 1 for events (from ARM9) */ + uint16_t type; /* 0 for msgs (from aDSP), -1 for events (from ARM9) */ int is16; /* always 0 (msg is 32-bit) when the event type is 1(ARM9) */ union { uint16_t msg16[ADSP_EVENT_MAX_SIZE / 2]; diff --git a/arch/arm/mach-msm/qdsp5/adsp_new.c b/arch/arm/mach-msm/qdsp5/adsp_new.c index fb7981b8dabf0..7f9f48f3d9260 100644 --- a/arch/arm/mach-msm/qdsp5/adsp_new.c +++ b/arch/arm/mach-msm/qdsp5/adsp_new.c @@ -530,6 +530,7 @@ int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, } EXPORT_SYMBOL(msm_adsp_write); +#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS static void *event_addr; static void read_event(void *buf, size_t len) { @@ -544,6 +545,7 @@ static void read_event(void *buf, size_t len) dptr[1] = be32_to_cpu(pkt_ptr->module); dptr[2] = be32_to_cpu(pkt_ptr->image); } +#endif static void handle_adsp_rtos_mtoa_app(struct rpc_request_hdr *req) { @@ -667,9 +669,11 @@ static void handle_adsp_rtos_mtoa_app(struct rpc_request_hdr *req) rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, RPC_ACCEPTSTAT_SUCCESS); mutex_unlock(&module->lock); +#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS event_addr = (uint32_t *)req; module->ops->event(module->driver_data, EVENT_MSG_ID, EVENT_LEN, read_event); +#endif } static int handle_adsp_rtos_mtoa(struct rpc_request_hdr *req) diff --git a/arch/arm/mach-msm/qdsp5/adsp_new.h b/arch/arm/mach-msm/qdsp5/adsp_new.h index 963b2f8c76a50..4940235b0c133 100644 --- a/arch/arm/mach-msm/qdsp5/adsp_new.h +++ b/arch/arm/mach-msm/qdsp5/adsp_new.h @@ -72,7 +72,7 @@ struct adsp_event { struct list_head list; uint32_t size; /* always in bytes */ uint16_t msg_id; - uint16_t type; /* 0 for msgs (from aDSP), 1 for events (from ARM9) */ + uint16_t type; /* 0 for msgs (from aDSP), -1 for events (from ARM9) */ int is16; /* always 0 (msg is 32-bit) when the event type is 1(ARM9) */ union { uint16_t msg16[ADSP_EVENT_MAX_SIZE / 2]; From e9f8777a3e2438741d3dcc861e679233821462a4 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Wed, 8 Apr 2009 13:19:18 -0700 Subject: [PATCH 0318/2556] [ARM] msm: sapphire: add support for 5M camera sensor Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-sapphire.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index 1ecb85283cd13..4617712b60a45 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -926,6 +926,9 @@ static struct platform_device *devices[] __initdata = { #endif #ifdef CONFIG_MT9T013 &msm_camera_sensor_mt9t013, +#endif +#ifdef CONFIG_MT9P012 + &msm_camera_sensor_mt9p012, #endif &sapphire_rfkill, #ifdef CONFIG_WIFI_CONTROL_FUNC From 5d3b88b543b2e33439c8fb5d5e454ce7ad038203 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Tue, 9 Feb 2010 19:10:04 -0800 Subject: [PATCH 0319/2556] [ARM] msm: camera: initial commit Signed-off-by: Iliyan Malchev [ARM] msm: add an idle wakelock to the camera driver Signed-off-by: Iliyan Malchev [ARM] msm: camera: fix a DSP-queue reference in msm_vfe7x.c Signed-off-by: Iliyan Malchev [ARM] msm: remove unnecessary mutex from camera driver Signed-off-by: Iliyan Malchev [ARM] msm: camera: conditionalize CDBG under CONFIG_MSM_CAMERA_DEBUG -- replaced those CDBG that report errors with pr_err -- added macros to report errors when copy_{from,to}_user fails -- simplified msm_pmem_table_add()'s prototype Signed-off-by: Iliyan Malchev [ARM] msm: camera: return the correct value from __msm_get_pic Signed-off-by: Iliyan Malchev [ARM] msm: camera: simplify MSM_CAM_IOCTL_ENABLE_VFE, hook up DISABLE_VFE -- simplified camera_enable_cmd_t to include a fixed-size name field (32 chars) -- enable ioctl MSM_CAM_IOCTL_DISABLE_VFE Signed-off-by: Iliyan Malchev [ARM] msm: camera: add more checks to the pmem-lookup functions -- Added checks for failure for the following functions: msm_pmem_region_lookup, msm_pmem_frame_ptov_lookup, msm_pmem_stats_ptov_lookup, msm_pmem_frame_vtop_lookup, msm_pmem_stats_vtop_lookup -- Added explicit checks for CMD_GENERAL in several cases where vfe_config() is called -- With the exception of msm_config_vfe(), where both CMD_GENERAL and CMD_STATS_DISABLE are accepted, all other non-CMD_GENERAL are rejected as errors. Signed-off-by: Iliyan Malchev [ARM] msm: camera: add support for flash (disabled) + more fixes -- msm_camera.c: -- check for overlap in msm_pmem_table_add() -- fix a copy_to_user bug -- add MSM_CAM_IOCTL_FLASH_LED_CFG -- msm_vfe7x.c: implement vfe_7x_stop() -- renames mt9p012.c --> mt9p012_fox.c -- mt9t013 register update -- v4l2 updates Signed-off-by: Iliyan Malchev [ARM] msm: camera: split device nodes This splits /dev/msm_camera{0,1,...} into /dev/{frame,control,config}{0,1,...}, which allows us to handle the camera separately in user space from a proprietary configuration daemon, and a separate daemon for control (for snapshot, effects, etc) and receiving frames. Signed-off-by: Iliyan Malchev [ARM] msm: camera: unify mutex handling, clean up code Signed-off-by: Iliyan Malchev [ARM] msm: camera: clean up locking of queues in msm_device_t->sync Signed-off-by: Iliyan Malchev [ARM] msm: camera: reduce the number of kmallocs in frame callback -- in msm_vfe_sync(), remove one kmalloc() in the frame callback -- get rid of msm_camvfe_init() -- rename msm_vfe_phy_info --> msm_vfe_phy_info_t Signed-off-by: Iliyan Malchev [ARM] msm: camera: major cleanup -- add MSM_DRAIN_QUEUE macros and use them -- remove all enumerations from msm_camera.h -- add an ioctl to unblock the frame thread -- drain queues selectively for frame and control threads -- add sensor_cfg_data_t::max_steps, which is not used at all right now -- NOTE: sensor_cfg_data_t->max_steps is not currently used Signed-off-by: Iliyan Malchev [ARM] msm: camera: CFG_GET_AF_MAX_STEPS + mt9t013 autofocus support + cleanup -- replaced incorrect EFAULT codes with EIO and EINVAL where appropriate -- fixed some indentation -- added power-on and power-off for autofocus module on mt9t013 sensor -- this needs to be improved: power on and off only when we do AF -- removed superfluous debug from samsung sensor driver (s5k3e2fx) -- in msm_camera.c, commented out error-recovery code in __msm_control -- added CFG_GET_AF_MAX_STEPS support for all camera sensors Signed-off-by: Iliyan Malchev [ARM] msm: camera: ignore timeout from control thread -- ioctl()s from the control thread may time out before the config thread has a chance to react, which will bring the config thread out of sync with the control thread; the best solution for now is to ignore the timeout values. This will have the side effect that bugs in userspace in the config and control threads will cause the control thread to block forever on an ioctl(), unless interrupted by a signal. -- Remove an unnecessary copy from user space for zero-length user buffers. -- Disable msm_camera debug. Signed-off-by: Iliyan Malchev [ARM] msm: camera: allow the control node to be opened multiple times -- created new msm_control_device_t structure for control nodes -- msm_control_device points to an msm_device_t -- moved ctrl_status_q from sync_t into msm_control_device_t -- restored the timeout in msm_control -- moved MSM_CAM_IOCTL_CTRL_CMD_DONE to msm_ioctl_control -- FIXME: the v4l2 frontend -- added a resp_fd to msm_ctrl_cmd_t which the control thread sets so that the config thread knows to which control device to send a MSM_CAM_IOCTL_CTRL_CMD_DONE. Signed-off-by: Iliyan Malchev [ARM] msm: camera: add non-blocking control ioctl Signed-off-by: Iliyan Malchev [ARM] msm: sapphire: camera: fix sensor orientation Signed-off-by: Iliyan Malchev [ARM] msm: camera: move msm_camera.c and msm_v4l2.c under msm/ Signed-off-by: Iliyan Malchev [ARM] msm: camera: rename all instances of struct _t to struct -- There were a number of instances where a (non-typedef'ed) struct or enum was named foo_t instead of foo. -- There were variables that ended on _t. This patch removes the trailing _t from these names. -- Renamed the old struct mm_vfe_resp_t to struct msm_vfe_callback. This is more descriptive and avoids a name clash with the already-extant struct msm_vfe_resp. Signed-off-by: Iliyan Malchev [ARM] msm: mt9t013: fix check for maximum autoexposure value Signed-off-by: Iliyan Malchev [ARM] msm: camera: mt9t013: correct for overexposure on snapshots Signed-off-by: Iliyan Malchev [ARM] msm: camera: replace fields from msm_pmem_region with a msm_pmem_info Signed-off-by: Iliyan Malchev [ARM] msm: camera: allow MSM_CAM_IOCTL_REGISTER_PMEM to work with subranges Allow the user to register a sub-range of a pmem buffer with the camera framework. Signed-off-by: Iliyan Malchev [ARM] msm: camera: MSM_PMEM_THUMBAIL -> MSM_PMEM_THUMBNAIL Signed-off-by: Iliyan Malchev [ARM] msm: camera: fix cropping-buffer length verification Signed-off-by: Iliyan Malchev [ARM] msm: camera: msm_get_stats: return immediately on error Signed-off-by: Iliyan Malchev [ARM] msm: mt9t013: reduce frame rate to 21fps Having the frame rate fixed at ~28fps causes a lot of noise, stripping, and color issues on the 3.2M mt9t013 camera sensor. Change-Id: Iaefc5bcabca88fe4bffc421c1993e28e46ee3561 Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/include/mach/camera.h | 276 ++ drivers/media/video/Kconfig | 25 + drivers/media/video/Makefile | 2 + drivers/media/video/msm/Kconfig | 24 + drivers/media/video/msm/Makefile | 7 + drivers/media/video/msm/msm_camera.c | 2229 ++++++++++++ drivers/media/video/msm/msm_io7x.c | 291 ++ drivers/media/video/msm/msm_io8x.c | 320 ++ drivers/media/video/msm/msm_v4l2.c | 793 +++++ drivers/media/video/msm/msm_vfe7x.c | 702 ++++ drivers/media/video/msm/msm_vfe7x.h | 255 ++ drivers/media/video/msm/msm_vfe8x.c | 757 ++++ drivers/media/video/msm/msm_vfe8x.h | 895 +++++ drivers/media/video/msm/msm_vfe8x_proc.c | 3995 ++++++++++++++++++++++ drivers/media/video/msm/msm_vfe8x_proc.h | 1549 +++++++++ drivers/media/video/msm/mt9d112.c | 761 +++++ drivers/media/video/msm/mt9d112.h | 36 + drivers/media/video/msm/mt9d112_reg.c | 307 ++ drivers/media/video/msm/mt9p012.h | 51 + drivers/media/video/msm/mt9p012_fox.c | 1305 +++++++ drivers/media/video/msm/mt9p012_reg.c | 573 ++++ drivers/media/video/msm/mt9t013.c | 1496 ++++++++ drivers/media/video/msm/mt9t013.h | 48 + drivers/media/video/msm/mt9t013_reg.c | 267 ++ drivers/media/video/msm/s5k3e2fx.c | 1311 +++++++ drivers/media/video/msm/s5k3e2fx.h | 9 + include/media/msm_camera.h | 390 +++ 27 files changed, 18674 insertions(+) create mode 100644 arch/arm/mach-msm/include/mach/camera.h create mode 100644 drivers/media/video/msm/Kconfig create mode 100755 drivers/media/video/msm/Makefile create mode 100644 drivers/media/video/msm/msm_camera.c create mode 100644 drivers/media/video/msm/msm_io7x.c create mode 100644 drivers/media/video/msm/msm_io8x.c create mode 100644 drivers/media/video/msm/msm_v4l2.c create mode 100644 drivers/media/video/msm/msm_vfe7x.c create mode 100644 drivers/media/video/msm/msm_vfe7x.h create mode 100644 drivers/media/video/msm/msm_vfe8x.c create mode 100644 drivers/media/video/msm/msm_vfe8x.h create mode 100644 drivers/media/video/msm/msm_vfe8x_proc.c create mode 100644 drivers/media/video/msm/msm_vfe8x_proc.h create mode 100644 drivers/media/video/msm/mt9d112.c create mode 100644 drivers/media/video/msm/mt9d112.h create mode 100644 drivers/media/video/msm/mt9d112_reg.c create mode 100644 drivers/media/video/msm/mt9p012.h create mode 100644 drivers/media/video/msm/mt9p012_fox.c create mode 100644 drivers/media/video/msm/mt9p012_reg.c create mode 100644 drivers/media/video/msm/mt9t013.c create mode 100644 drivers/media/video/msm/mt9t013.h create mode 100644 drivers/media/video/msm/mt9t013_reg.c create mode 100644 drivers/media/video/msm/s5k3e2fx.c create mode 100644 drivers/media/video/msm/s5k3e2fx.h create mode 100644 include/media/msm_camera.h diff --git a/arch/arm/mach-msm/include/mach/camera.h b/arch/arm/mach-msm/include/mach/camera.h new file mode 100644 index 0000000000000..4897b7d80e169 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/camera.h @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#ifndef __ASM__ARCH_CAMERA_H +#define __ASM__ARCH_CAMERA_H + +#include +#include +#include +#include +#include +#include "linux/types.h" + +#include +#include + +#ifdef CONFIG_MSM_CAMERA_DEBUG +#define CDBG(fmt, args...) printk(KERN_INFO "msm_camera: " fmt, ##args) +#else +#define CDBG(fmt, args...) do { } while (0) +#endif + +#define MSM_CAMERA_MSG 0 +#define MSM_CAMERA_EVT 1 +#define NUM_WB_EXP_NEUTRAL_REGION_LINES 4 +#define NUM_WB_EXP_STAT_OUTPUT_BUFFERS 3 +#define NUM_AUTOFOCUS_MULTI_WINDOW_GRIDS 16 +#define NUM_AF_STAT_OUTPUT_BUFFERS 3 + +enum msm_queue { + MSM_CAM_Q_CTRL, /* control command or control command status */ + MSM_CAM_Q_VFE_EVT, /* adsp event */ + MSM_CAM_Q_VFE_MSG, /* adsp message */ + MSM_CAM_Q_V4L2_REQ, /* v4l2 request */ +}; + +enum vfe_resp_msg { + VFE_EVENT, + VFE_MSG_GENERAL, + VFE_MSG_SNAPSHOT, + VFE_MSG_OUTPUT1, + VFE_MSG_OUTPUT2, + VFE_MSG_STATS_AF, + VFE_MSG_STATS_WE, +}; + +struct msm_vfe_phy_info { + uint32_t sbuf_phy; + uint32_t y_phy; + uint32_t cbcr_phy; +}; + +struct msm_vfe_resp { + enum vfe_resp_msg type; + struct msm_vfe_evt_msg evt_msg; + struct msm_vfe_phy_info phy; + void *extdata; + int32_t extlen; +}; + +struct msm_vfe_callback { + void (*vfe_resp)(struct msm_vfe_resp *, + enum msm_queue, void *syncdata); + void* (*vfe_alloc)(int, void *syncdata); +}; + +struct msm_camvfe_fn { + int (*vfe_init)(struct msm_vfe_callback *, struct platform_device *); + int (*vfe_enable)(struct camera_enable_cmd *); + int (*vfe_config)(struct msm_vfe_cfg_cmd *, void *); + int (*vfe_disable)(struct camera_enable_cmd *, + struct platform_device *dev); + void (*vfe_release)(struct platform_device *); +}; + +struct msm_sensor_ctrl { + int (*s_init)(const struct msm_camera_sensor_info *); + int (*s_release)(void); + int (*s_config)(void __user *); +}; + +struct msm_sync { + /* These two queues are accessed from a process context only. */ + struct hlist_head frame; /* most-frequently accessed */ + struct hlist_head stats; + + /* The message queue is used by the control thread to send commands + * to the config thread, and also by the DSP to send messages to the + * config thread. Thus it is the only queue that is accessed from + * both interrupt and process context. + */ + spinlock_t msg_event_q_lock; + struct list_head msg_event_q; + wait_queue_head_t msg_event_wait; + + /* This queue contains preview frames. It is accessed by the DSP (in + * in interrupt context, and by the frame thread. + */ + spinlock_t prev_frame_q_lock; + struct list_head prev_frame_q; + wait_queue_head_t prev_frame_wait; + int unblock_poll_frame; + + /* This queue contains snapshot frames. It is accessed by the DSP (in + * interrupt context, and by the control thread. + */ + spinlock_t pict_frame_q_lock; + struct list_head pict_frame_q; + wait_queue_head_t pict_frame_wait; + + struct msm_camera_sensor_info *sdata; + struct msm_camvfe_fn vfefn; + struct msm_sensor_ctrl sctrl; + struct wake_lock wake_lock; + struct platform_device *pdev; + uint8_t opencnt; + void *cropinfo; + int croplen; + unsigned pict_pp; + + const char *apps_id; + + struct mutex lock; + struct list_head list; +}; + +#define MSM_APPS_ID_V4L2 "msm_v4l2" +#define MSM_APPS_ID_PROP "msm_qct" + +struct msm_device { + struct msm_sync *sync; /* most-frequently accessed */ + struct device *device; + struct cdev cdev; + /* opened is meaningful only for the config and frame nodes, + * which may be opened only once. + */ + atomic_t opened; +}; + +struct msm_control_device_queue { + spinlock_t ctrl_status_q_lock; + struct list_head ctrl_status_q; + wait_queue_head_t ctrl_status_wait; +}; + +struct msm_control_device { + struct msm_device *pmsm; + + /* This queue used by the config thread to send responses back to the + * control thread. It is accessed only from a process context. + */ + struct msm_control_device_queue ctrl_q; +}; + +/* this structure is used in kernel */ +struct msm_queue_cmd { + struct list_head list; + enum msm_queue type; + void *command; +}; + +struct register_address_value_pair { + uint16_t register_address; + uint16_t register_value; +}; + +struct msm_pmem_region { + struct hlist_node list; + unsigned long paddr; + unsigned long len; + struct file *file; + struct msm_pmem_info info; +}; + +struct axidata { + uint32_t bufnum1; + uint32_t bufnum2; + struct msm_pmem_region *region; +}; + +#ifdef CONFIG_MSM_CAMERA_FLASH +int msm_camera_flash_set_led_state(unsigned led_state); +#else +static inline int msm_camera_flash_set_led_state(unsigned led_state) +{ + return -ENOTSUPP; +} +#endif + +/* Below functions are added for V4L2 kernel APIs */ +struct msm_v4l2_driver { + struct msm_sync *sync; + int (*open)(struct msm_sync *, const char *apps_id); + int (*release)(struct msm_sync *); + int (*ctrl)(struct msm_sync *, struct msm_ctrl_cmd *); + int (*reg_pmem)(struct msm_sync *, struct msm_pmem_info *); + int (*get_frame) (struct msm_sync *, struct msm_frame *); + int (*put_frame) (struct msm_sync *, struct msm_frame *); + int (*get_pict) (struct msm_sync *, struct msm_ctrl_cmd *); + unsigned int (*drv_poll) (struct msm_sync *, struct file *, + struct poll_table_struct *); +}; + +int msm_v4l2_register(struct msm_v4l2_driver *); +int msm_v4l2_unregister(struct msm_v4l2_driver *); + +void msm_camvfe_init(void); +int msm_camvfe_check(void *); +void msm_camvfe_fn_init(struct msm_camvfe_fn *, void *); +int msm_camera_drv_start(struct platform_device *dev, + int (*sensor_probe)(const struct msm_camera_sensor_info *, + struct msm_sensor_ctrl *)); + +enum msm_camio_clk_type { + CAMIO_VFE_MDC_CLK, + CAMIO_MDC_CLK, + CAMIO_VFE_CLK, + CAMIO_VFE_AXI_CLK, + + CAMIO_MAX_CLK +}; + +enum msm_camio_clk_src_type { + MSM_CAMIO_CLK_SRC_INTERNAL, + MSM_CAMIO_CLK_SRC_EXTERNAL, + MSM_CAMIO_CLK_SRC_MAX +}; + +enum msm_s_test_mode { + S_TEST_OFF, + S_TEST_1, + S_TEST_2, + S_TEST_3 +}; + +enum msm_s_resolution { + S_QTR_SIZE, + S_FULL_SIZE, + S_INVALID_SIZE +}; + +enum msm_s_reg_update { + /* Sensor egisters that need to be updated during initialization */ + S_REG_INIT, + /* Sensor egisters that needs periodic I2C writes */ + S_UPDATE_PERIODIC, + /* All the sensor Registers will be updated */ + S_UPDATE_ALL, + /* Not valid update */ + S_UPDATE_INVALID +}; + +enum msm_s_setting { + S_RES_PREVIEW, + S_RES_CAPTURE +}; + +int msm_camio_enable(struct platform_device *dev); + +int msm_camio_clk_enable(enum msm_camio_clk_type clk); +int msm_camio_clk_disable(enum msm_camio_clk_type clk); +int msm_camio_clk_config(uint32_t freq); +void msm_camio_clk_rate_set(int rate); +void msm_camio_clk_axi_rate_set(int rate); + +void msm_camio_camif_pad_reg_reset(void); +void msm_camio_camif_pad_reg_reset_2(void); + +void msm_camio_vfe_blk_reset(void); + +void msm_camio_clk_sel(enum msm_camio_clk_src_type); +void msm_camio_disable(struct platform_device *); +int msm_camio_probe_on(struct platform_device *); +int msm_camio_probe_off(struct platform_device *); +#endif diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index aa021600e9df3..3c383c57d988f 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -950,6 +950,31 @@ config USB_S2255 This driver can be compiled as a module, called s2255drv. endif # V4L_USB_DRIVERS + +comment "Qualcomm MSM Camera And Video" + +menuconfig MSM_CAMERA + bool "Qualcomm MSM camera and video capture support" + depends on ARCH_MSM + help + Say Y here to enable selecting the video adapters for + Qualcomm msm camera and video encoding + +config MSM_CAMERA_DEBUG + bool "Qualcomm MSM camera debugging with printk" + depends on MSM_CAMERA + help + Enable printk() debug for msm camera + +config MSM_CAMERA_FLASH + bool "Qualcomm MSM camera flash support" + depends on MSM_CAMERA + ---help--- + Enable support for LED flash for msm camera + + +source "drivers/media/video/msm/Kconfig" + endif # VIDEO_CAPTURE_DRIVERS menuconfig V4L_MEM2MEM_DRIVERS diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index a509d317e258d..61f43f2bb2943 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -169,6 +169,8 @@ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-y += davinci/ +obj-$(CONFIG_MSM_CAMERA) += msm/ + obj-$(CONFIG_ARCH_OMAP) += omap/ EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/drivers/media/video/msm/Kconfig b/drivers/media/video/msm/Kconfig new file mode 100644 index 0000000000000..00499987ed46b --- /dev/null +++ b/drivers/media/video/msm/Kconfig @@ -0,0 +1,24 @@ +comment "Camera Sensor Selection" +config MT9T013 + bool "Sensor mt9t013 (BAYER 3M)" + depends on MSM_CAMERA + ---help--- + MICRON 3M Bayer Sensor with AutoFocus + +config MT9D112 + bool "Sensor mt9d112 (YUV 2M)" + depends on MSM_CAMERA + ---help--- + MICRON 2M YUV Sensor + +config MT9P012 + bool "Sensor mt9p012 (BAYER 5M)" + depends on MSM_CAMERA + ---help--- + MICRON 5M Bayer Sensor with Autofocus + +config S5K3E2FX + bool "Sensor s5k3e2fx (Samsung 5M)" + depends on MSM_CAMERA + ---help--- + Samsung 5M with Autofocus diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile new file mode 100755 index 0000000000000..4429ae5fcafda --- /dev/null +++ b/drivers/media/video/msm/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_MT9T013) += mt9t013.o mt9t013_reg.o +obj-$(CONFIG_MT9D112) += mt9d112.o mt9d112_reg.o +obj-$(CONFIG_MT9P012) += mt9p012_fox.o mt9p012_reg.o +obj-$(CONFIG_MSM_CAMERA) += msm_camera.o msm_v4l2.o +obj-$(CONFIG_S5K3E2FX) += s5k3e2fx.o +obj-$(CONFIG_ARCH_MSM) += msm_vfe7x.o msm_io7x.o +obj-$(CONFIG_ARCH_QSD) += msm_vfe8x.o msm_vfe8x_proc.o msm_io8x.o diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c new file mode 100644 index 0000000000000..868ac5bc7dbe5 --- /dev/null +++ b/drivers/media/video/msm/msm_camera.c @@ -0,0 +1,2229 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +//FIXME: most allocations need not be GFP_ATOMIC +/* FIXME: management of mutexes */ +/* FIXME: msm_pmem_region_lookup return values */ +/* FIXME: way too many copy to/from user */ +/* FIXME: does region->active mean free */ +/* FIXME: check limits on command lenghts passed from userspace */ +/* FIXME: __msm_release: which queues should we flush when opencnt != 0 */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MSM_MAX_CAMERA_SENSORS 5 + +#define ERR_USER_COPY(to) pr_err("%s(%d): copy %s user\n", \ + __func__, __LINE__, ((to) ? "to" : "from")) +#define ERR_COPY_FROM_USER() ERR_USER_COPY(0) +#define ERR_COPY_TO_USER() ERR_USER_COPY(1) + +static struct class *msm_class; +static dev_t msm_devno; +static LIST_HEAD(msm_sensors); + +#define __CONTAINS(r, v, l, field) ({ \ + typeof(r) __r = r; \ + typeof(v) __v = v; \ + typeof(v) __e = __v + l; \ + int res = __v >= __r->field && \ + __e <= __r->field + __r->len; \ + res; \ +}) + +#define CONTAINS(r1, r2, field) ({ \ + typeof(r2) __r2 = r2; \ + __CONTAINS(r1, __r2->field, __r2->len, field); \ +}) + +#define IN_RANGE(r, v, field) ({ \ + typeof(r) __r = r; \ + typeof(v) __vv = v; \ + int res = ((__vv >= __r->field) && \ + (__vv < (__r->field + __r->len))); \ + res; \ +}) + +#define OVERLAPS(r1, r2, field) ({ \ + typeof(r1) __r1 = r1; \ + typeof(r2) __r2 = r2; \ + typeof(__r2->field) __v = __r2->field; \ + typeof(__v) __e = __v + __r2->len - 1; \ + int res = (IN_RANGE(__r1, __v, field) || \ + IN_RANGE(__r1, __e, field)); \ + res; \ +}) + +#define MSM_DRAIN_QUEUE_NOSYNC(sync, name) do { \ + struct msm_queue_cmd *qcmd = NULL; \ + CDBG("%s: draining queue "#name"\n", __func__); \ + while (!list_empty(&(sync)->name)) { \ + qcmd = list_first_entry(&(sync)->name, \ + struct msm_queue_cmd, list); \ + list_del_init(&qcmd->list); \ + kfree(qcmd); \ + }; \ +} while(0) + +#define MSM_DRAIN_QUEUE(sync, name) do { \ + unsigned long flags; \ + spin_lock_irqsave(&(sync)->name##_lock, flags); \ + MSM_DRAIN_QUEUE_NOSYNC(sync, name); \ + spin_unlock_irqrestore(&(sync)->name##_lock, flags); \ +} while(0) + +static int check_overlap(struct hlist_head *ptype, + unsigned long paddr, + unsigned long len) +{ + struct msm_pmem_region *region; + struct msm_pmem_region t = { .paddr = paddr, .len = len }; + struct hlist_node *node; + + hlist_for_each_entry(region, node, ptype, list) { + if (CONTAINS(region, &t, paddr) || + CONTAINS(&t, region, paddr) || + OVERLAPS(region, &t, paddr)) { + printk(KERN_ERR + " region (PHYS %p len %ld)" + " clashes with registered region" + " (paddr %p len %ld)\n", + (void *)t.paddr, t.len, + (void *)region->paddr, region->len); + return -1; + } + } + + return 0; +} + +static int check_pmem_info(struct msm_pmem_info *info, int len) +{ + if (info->offset < len && + info->offset + info->len <= len && + info->y_off < len && + info->cbcr_off < len) + return 0; + + pr_err("%s: check failed: off %d len %d y %d cbcr %d (total len %d)\n", + __func__, + info->offset, + info->len, + info->y_off, + info->cbcr_off, + len); + return -EINVAL; +} + +static int msm_pmem_table_add(struct hlist_head *ptype, + struct msm_pmem_info *info) +{ + struct file *file; + unsigned long paddr; + unsigned long kvstart; + unsigned long len; + int rc; + struct msm_pmem_region *region; + + rc = get_pmem_file(info->fd, &paddr, &kvstart, &len, &file); + if (rc < 0) { + pr_err("%s: get_pmem_file fd %d error %d\n", + __func__, + info->fd, rc); + return rc; + } + + if (!info->len) + info->len = len; + + rc = check_pmem_info(info, len); + if (rc < 0) + return rc; + + paddr += info->offset; + len = info->len; + + if (check_overlap(ptype, paddr, len) < 0) + return -EINVAL; + + CDBG("%s: type = %d, paddr = 0x%lx, vaddr = 0x%lx\n", + __func__, + info->type, paddr, (unsigned long)info->vaddr); + + region = kmalloc(sizeof(struct msm_pmem_region), GFP_KERNEL); + if (!region) + return -ENOMEM; + + INIT_HLIST_NODE(®ion->list); + + region->paddr = paddr; + region->len = len; + region->file = file; + memcpy(®ion->info, info, sizeof(region->info)); + + hlist_add_head(&(region->list), ptype); + + return 0; +} + +/* return of 0 means failure */ +static uint8_t msm_pmem_region_lookup(struct hlist_head *ptype, + int pmem_type, struct msm_pmem_region *reg, uint8_t maxcount) +{ + struct msm_pmem_region *region; + struct msm_pmem_region *regptr; + struct hlist_node *node, *n; + + uint8_t rc = 0; + + regptr = reg; + + hlist_for_each_entry_safe(region, node, n, ptype, list) { + if (region->info.type == pmem_type && region->info.active) { + *regptr = *region; + rc += 1; + if (rc >= maxcount) + break; + regptr++; + } + } + + return rc; +} + +static int msm_pmem_frame_ptov_lookup(struct msm_sync *sync, + unsigned long pyaddr, + unsigned long pcbcraddr, + struct msm_pmem_info *pmem_info) +{ + struct msm_pmem_region *region; + struct hlist_node *node, *n; + + hlist_for_each_entry_safe(region, node, n, &sync->frame, list) { + if (pyaddr == (region->paddr + region->info.y_off) && + pcbcraddr == (region->paddr + + region->info.cbcr_off) && + region->info.active) { + /* offset since we could pass vaddr inside + * a registerd pmem buffer + */ + memcpy(pmem_info, ®ion->info, sizeof(*pmem_info)); + region->info.active = 0; + return 0; + } + } + + return -EINVAL; +} + +static unsigned long msm_pmem_stats_ptov_lookup(struct msm_sync *sync, + unsigned long addr, int *fd) +{ + struct msm_pmem_region *region; + struct hlist_node *node, *n; + + hlist_for_each_entry_safe(region, node, n, &sync->stats, list) { + if (addr == region->paddr && region->info.active) { + /* offset since we could pass vaddr inside a + * registered pmem buffer */ + *fd = region->info.fd; + region->info.active = 0; + return (unsigned long)(region->info.vaddr); + } + } + + return 0; +} + +static unsigned long msm_pmem_frame_vtop_lookup(struct msm_sync *sync, + unsigned long buffer, + uint32_t yoff, uint32_t cbcroff, int fd) +{ + struct msm_pmem_region *region; + struct hlist_node *node, *n; + + hlist_for_each_entry_safe(region, + node, n, &sync->frame, list) { + if (((unsigned long)(region->info.vaddr) == buffer) && + (region->info.y_off == yoff) && + (region->info.cbcr_off == cbcroff) && + (region->info.fd == fd) && + (region->info.active == 0)) { + region->info.active = 1; + return region->paddr; + } + } + + return 0; +} + +static unsigned long msm_pmem_stats_vtop_lookup( + struct msm_sync *sync, + unsigned long buffer, + int fd) +{ + struct msm_pmem_region *region; + struct hlist_node *node, *n; + + hlist_for_each_entry_safe(region, node, n, &sync->stats, list) { + if (((unsigned long)(region->info.vaddr) == buffer) && + (region->info.fd == fd) && + region->info.active == 0) { + region->info.active = 1; + return region->paddr; + } + } + + return 0; +} + +static int __msm_pmem_table_del(struct msm_sync *sync, + struct msm_pmem_info *pinfo) +{ + int rc = 0; + struct msm_pmem_region *region; + struct hlist_node *node, *n; + + switch (pinfo->type) { + case MSM_PMEM_OUTPUT1: + case MSM_PMEM_OUTPUT2: + case MSM_PMEM_THUMBNAIL: + case MSM_PMEM_MAINIMG: + case MSM_PMEM_RAW_MAINIMG: + hlist_for_each_entry_safe(region, node, n, + &sync->frame, list) { + + if (pinfo->type == region->info.type && + pinfo->vaddr == region->info.vaddr && + pinfo->fd == region->info.fd) { + hlist_del(node); + put_pmem_file(region->file); + kfree(region); + } + } + break; + + case MSM_PMEM_AEC_AWB: + case MSM_PMEM_AF: + hlist_for_each_entry_safe(region, node, n, + &sync->stats, list) { + + if (pinfo->type == region->info.type && + pinfo->vaddr == region->info.vaddr && + pinfo->fd == region->info.fd) { + hlist_del(node); + put_pmem_file(region->file); + kfree(region); + } + } + break; + + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static int msm_pmem_table_del(struct msm_sync *sync, void __user *arg) +{ + struct msm_pmem_info info; + + if (copy_from_user(&info, arg, sizeof(info))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + return __msm_pmem_table_del(sync, &info); +} + +static int __msm_get_frame(struct msm_sync *sync, + struct msm_frame *frame) +{ + unsigned long flags; + int rc = 0; + + struct msm_pmem_info pmem_info; + struct msm_queue_cmd *qcmd = NULL; + struct msm_vfe_phy_info *pphy; + + spin_lock_irqsave(&sync->prev_frame_q_lock, flags); + if (!list_empty(&sync->prev_frame_q)) { + qcmd = list_first_entry(&sync->prev_frame_q, + struct msm_queue_cmd, list); + list_del_init(&qcmd->list); + } + spin_unlock_irqrestore(&sync->prev_frame_q_lock, flags); + + if (!qcmd) { + pr_err("%s: no preview frame.\n", __func__); + return -EAGAIN; + } + + pphy = (struct msm_vfe_phy_info *)(qcmd->command); + + rc = msm_pmem_frame_ptov_lookup(sync, + pphy->y_phy, + pphy->cbcr_phy, + &pmem_info); + + if (rc < 0) { + pr_err("%s: cannot get frame, invalid lookup address " + "y=%x cbcr=%x\n", + __FUNCTION__, + pphy->y_phy, + pphy->cbcr_phy); + goto err; + } + + frame->buffer = (unsigned long)pmem_info.vaddr; + frame->y_off = pmem_info.y_off; + frame->cbcr_off = pmem_info.cbcr_off; + frame->fd = pmem_info.fd; + + CDBG("__msm_get_frame: y=0x%x, cbcr=0x%x, qcmd=0x%x, virt_addr=0x%x\n", + pphy->y_phy, pphy->cbcr_phy, (int) qcmd, (int) frame->buffer); + +err: + kfree(qcmd); + return rc; +} + +static int msm_get_frame(struct msm_sync *sync, void __user *arg) +{ + int rc = 0; + struct msm_frame frame; + + if (copy_from_user(&frame, + arg, + sizeof(struct msm_frame))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + rc = __msm_get_frame(sync, &frame); + if (rc < 0) + return rc; + + if (sync->croplen) { + if (frame.croplen != sync->croplen) { + pr_err("msm_get_frame: invalid frame croplen %d," + "expecting %d\n", + frame.croplen, + sync->croplen); + return -EINVAL; + } + + if (copy_to_user((void *)frame.cropinfo, + sync->cropinfo, + sync->croplen)) { + ERR_COPY_TO_USER(); + return -EFAULT; + } + } + + if (copy_to_user((void *)arg, + &frame, sizeof(struct msm_frame))) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + } + + CDBG("Got frame!!!\n"); + + return rc; +} + +static int msm_enable_vfe(struct msm_sync *sync, void __user *arg) +{ + int rc = -EIO; + struct camera_enable_cmd cfg; + + if (copy_from_user(&cfg, + arg, + sizeof(struct camera_enable_cmd))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + if (sync->vfefn.vfe_enable) + rc = sync->vfefn.vfe_enable(&cfg); + + CDBG("msm_enable_vfe: returned rc = %d\n", rc); + return rc; +} + +static int msm_disable_vfe(struct msm_sync *sync, void __user *arg) +{ + int rc = -EIO; + struct camera_enable_cmd cfg; + + if (copy_from_user(&cfg, + arg, + sizeof(struct camera_enable_cmd))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + if (sync->vfefn.vfe_disable) + rc = sync->vfefn.vfe_disable(&cfg, NULL); + + CDBG("msm_disable_vfe: returned rc = %d\n", rc); + return rc; +} + +static struct msm_queue_cmd* __msm_control(struct msm_sync *sync, + struct msm_control_device_queue *queue, + struct msm_queue_cmd *qcmd, + int timeout) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(&sync->msg_event_q_lock, flags); + list_add_tail(&qcmd->list, &sync->msg_event_q); + /* wake up config thread */ + wake_up(&sync->msg_event_wait); + spin_unlock_irqrestore(&sync->msg_event_q_lock, flags); + + if (!queue) + return NULL; + + /* wait for config status */ + rc = wait_event_interruptible_timeout( + queue->ctrl_status_wait, + !list_empty_careful(&queue->ctrl_status_q), + timeout); + if (list_empty_careful(&queue->ctrl_status_q)) { + if (!rc) + rc = -ETIMEDOUT; + if (rc < 0) { + pr_err("msm_control: wait_event error %d\n", rc); +#if 0 + /* This is a bit scary. If we time out too early, we + * will free qcmd at the end of this function, and the + * dsp may do the same when it does respond, so we + * remove the message from the source queue. + */ + pr_err("%s: error waiting for ctrl_status_q: %d\n", + __func__, rc); + spin_lock_irqsave(&sync->msg_event_q_lock, flags); + list_del_init(&qcmd->list); + spin_unlock_irqrestore(&sync->msg_event_q_lock, flags); +#endif + return ERR_PTR(rc); + } + } + + /* control command status is ready */ + spin_lock_irqsave(&queue->ctrl_status_q_lock, flags); + BUG_ON(list_empty(&queue->ctrl_status_q)); + qcmd = list_first_entry(&queue->ctrl_status_q, + struct msm_queue_cmd, list); + list_del_init(&qcmd->list); + spin_unlock_irqrestore(&queue->ctrl_status_q_lock, flags); + + return qcmd; +} + +static int msm_control(struct msm_control_device *ctrl_pmsm, + int block, + void __user *arg) +{ + int rc = 0; + + struct msm_sync *sync = ctrl_pmsm->pmsm->sync; + struct msm_ctrl_cmd udata, *ctrlcmd; + struct msm_queue_cmd *qcmd = NULL, *qcmd_temp; + + if (copy_from_user(&udata, arg, sizeof(struct msm_ctrl_cmd))) { + ERR_COPY_FROM_USER(); + rc = -EFAULT; + goto end; + } + + qcmd = kmalloc(sizeof(struct msm_queue_cmd) + + sizeof(struct msm_ctrl_cmd) + udata.length, + GFP_KERNEL); + if (!qcmd) { + pr_err("msm_control: cannot allocate buffer\n"); + rc = -ENOMEM; + goto end; + } + + qcmd->type = MSM_CAM_Q_CTRL; + qcmd->command = ctrlcmd = (struct msm_ctrl_cmd *)(qcmd + 1); + *ctrlcmd = udata; + ctrlcmd->value = ctrlcmd + 1; + + if (udata.length) { + if (copy_from_user(ctrlcmd->value, + udata.value, udata.length)) { + ERR_COPY_FROM_USER(); + rc = -EFAULT; + goto end; + } + } + + if (!block) { + /* qcmd will be set to NULL */ + qcmd = __msm_control(sync, NULL, qcmd, 0); + goto end; + } + + qcmd_temp = __msm_control(sync, + &ctrl_pmsm->ctrl_q, + qcmd, MAX_SCHEDULE_TIMEOUT); + + if (IS_ERR(qcmd_temp)) { + rc = PTR_ERR(qcmd_temp); + goto end; + } + qcmd = qcmd_temp; + + if (qcmd->command) { + void __user *to = udata.value; + udata = *(struct msm_ctrl_cmd *)qcmd->command; + if (udata.length > 0) { + if (copy_to_user(to, + udata.value, + udata.length)) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + goto end; + } + } + udata.value = to; + + if (copy_to_user((void *)arg, &udata, + sizeof(struct msm_ctrl_cmd))) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + goto end; + } + } + +end: + /* Note: if we get here as a result of an error, we will free the + * qcmd that we kmalloc() in this function. When we come here as + * a result of a successful completion, we are freeing the qcmd that + * we dequeued from queue->ctrl_status_q. + */ + if (qcmd) + kfree(qcmd); + + CDBG("msm_control: end rc = %d\n", rc); + return rc; +} + +static int msm_get_stats(struct msm_sync *sync, void __user *arg) +{ + unsigned long flags; + int timeout; + int rc = 0; + + struct msm_stats_event_ctrl se; + + struct msm_queue_cmd *qcmd = NULL; + struct msm_ctrl_cmd *ctrl = NULL; + struct msm_vfe_resp *data = NULL; + struct msm_stats_buf stats; + + if (copy_from_user(&se, arg, + sizeof(struct msm_stats_event_ctrl))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + timeout = (int)se.timeout_ms; + + CDBG("msm_get_stats timeout %d\n", timeout); + rc = wait_event_interruptible_timeout( + sync->msg_event_wait, + !list_empty_careful(&sync->msg_event_q), + msecs_to_jiffies(timeout)); + if (list_empty_careful(&sync->msg_event_q)) { + if (rc == 0) + rc = -ETIMEDOUT; + if (rc < 0) { + pr_err("msm_get_stats error %d\n", rc); + return rc; + } + } + CDBG("msm_get_stats returned from wait: %d\n", rc); + + spin_lock_irqsave(&sync->msg_event_q_lock, flags); + BUG_ON(list_empty(&sync->msg_event_q)); + qcmd = list_first_entry(&sync->msg_event_q, + struct msm_queue_cmd, list); + list_del_init(&qcmd->list); + spin_unlock_irqrestore(&sync->msg_event_q_lock, flags); + + CDBG("=== received from DSP === %d\n", qcmd->type); + + switch (qcmd->type) { + case MSM_CAM_Q_VFE_EVT: + case MSM_CAM_Q_VFE_MSG: + data = (struct msm_vfe_resp *)(qcmd->command); + + /* adsp event and message */ + se.resptype = MSM_CAM_RESP_STAT_EVT_MSG; + + /* 0 - msg from aDSP, 1 - event from mARM */ + se.stats_event.type = data->evt_msg.type; + se.stats_event.msg_id = data->evt_msg.msg_id; + se.stats_event.len = data->evt_msg.len; + + CDBG("msm_get_stats, qcmd->type = %d\n", qcmd->type); + CDBG("length = %d\n", se.stats_event.len); + CDBG("msg_id = %d\n", se.stats_event.msg_id); + + if ((data->type == VFE_MSG_STATS_AF) || + (data->type == VFE_MSG_STATS_WE)) { + + stats.buffer = + msm_pmem_stats_ptov_lookup(sync, + data->phy.sbuf_phy, + &(stats.fd)); + if (!stats.buffer) { + pr_err("%s: msm_pmem_stats_ptov_lookup error\n", + __FUNCTION__); + rc = -EINVAL; + goto failure; + } + + if (copy_to_user((void *)(se.stats_event.data), + &stats, + sizeof(struct msm_stats_buf))) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + goto failure; + } + } else if ((data->evt_msg.len > 0) && + (data->type == VFE_MSG_GENERAL)) { + if (copy_to_user((void *)(se.stats_event.data), + data->evt_msg.data, + data->evt_msg.len)) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + goto failure; + } + } else if (data->type == VFE_MSG_OUTPUT1 || + data->type == VFE_MSG_OUTPUT2) { + if (copy_to_user((void *)(se.stats_event.data), + data->extdata, + data->extlen)) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + goto failure; + } + } else if (data->type == VFE_MSG_SNAPSHOT && sync->pict_pp) { + struct msm_postproc buf; + struct msm_pmem_region region; + buf.fmnum = msm_pmem_region_lookup(&sync->frame, + MSM_PMEM_MAINIMG, + ®ion, 1); + if (buf.fmnum == 1) { + buf.fmain.buffer = + (unsigned long)region.info.vaddr; + buf.fmain.y_off = region.info.y_off; + buf.fmain.cbcr_off = region.info.cbcr_off; + buf.fmain.fd = region.info.fd; + } else { + buf.fmnum = msm_pmem_region_lookup(&sync->frame, + MSM_PMEM_RAW_MAINIMG, + ®ion, 1); + if (buf.fmnum == 1) { + buf.fmain.path = MSM_FRAME_PREV_2; + buf.fmain.buffer = + (unsigned long)region. + info.vaddr; + buf.fmain.fd = region.info.fd; + } + else { + pr_err("%s: pmem lookup failed\n", + __func__); + rc = -EINVAL; + goto failure; + } + } + + if (copy_to_user((void *)(se.stats_event.data), &buf, + sizeof(buf))) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + goto failure; + } + CDBG("snapshot copy_to_user!\n"); + } + break; + + case MSM_CAM_Q_CTRL: + /* control command from control thread */ + ctrl = (struct msm_ctrl_cmd *)(qcmd->command); + + CDBG("msm_get_stats, qcmd->type = %d\n", qcmd->type); + CDBG("length = %d\n", ctrl->length); + + if (ctrl->length > 0) { + if (copy_to_user((void *)(se.ctrl_cmd.value), + ctrl->value, + ctrl->length)) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + goto failure; + } + } + + se.resptype = MSM_CAM_RESP_CTRL; + + /* what to control */ + se.ctrl_cmd.type = ctrl->type; + se.ctrl_cmd.length = ctrl->length; + se.ctrl_cmd.resp_fd = ctrl->resp_fd; + break; + + case MSM_CAM_Q_V4L2_REQ: + /* control command from v4l2 client */ + ctrl = (struct msm_ctrl_cmd *)(qcmd->command); + + CDBG("msm_get_stats, qcmd->type = %d\n", qcmd->type); + CDBG("length = %d\n", ctrl->length); + + if (ctrl->length > 0) { + if (copy_to_user((void *)(se.ctrl_cmd.value), + ctrl->value, ctrl->length)) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + goto failure; + } + } + + /* 2 tells config thread this is v4l2 request */ + se.resptype = MSM_CAM_RESP_V4L2; + + /* what to control */ + se.ctrl_cmd.type = ctrl->type; + se.ctrl_cmd.length = ctrl->length; + break; + + default: + rc = -EFAULT; + goto failure; + } /* switch qcmd->type */ + + if (copy_to_user((void *)arg, &se, sizeof(se))) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + } + +failure: + if (qcmd) + kfree(qcmd); + + CDBG("msm_get_stats: %d\n", rc); + return rc; +} + +static int msm_ctrl_cmd_done(struct msm_control_device *ctrl_pmsm, + void __user *arg) +{ + unsigned long flags; + int rc = 0; + + struct msm_ctrl_cmd udata, *ctrlcmd; + struct msm_queue_cmd *qcmd = NULL; + + if (copy_from_user(&udata, arg, sizeof(struct msm_ctrl_cmd))) { + ERR_COPY_FROM_USER(); + rc = -EFAULT; + goto end; + } + + qcmd = kmalloc(sizeof(struct msm_queue_cmd) + + sizeof(struct msm_ctrl_cmd) + udata.length, + GFP_KERNEL); + if (!qcmd) { + rc = -ENOMEM; + goto end; + } + + qcmd->command = ctrlcmd = (struct msm_ctrl_cmd *)(qcmd + 1); + *ctrlcmd = udata; + if (udata.length > 0) { + ctrlcmd->value = ctrlcmd + 1; + if (copy_from_user(ctrlcmd->value, + (void *)udata.value, + udata.length)) { + ERR_COPY_FROM_USER(); + rc = -EFAULT; + kfree(qcmd); + goto end; + } + } + else ctrlcmd->value = NULL; + +end: + CDBG("msm_ctrl_cmd_done: end rc = %d\n", rc); + if (rc == 0) { + /* wake up control thread */ + spin_lock_irqsave(&ctrl_pmsm->ctrl_q.ctrl_status_q_lock, flags); + list_add_tail(&qcmd->list, &ctrl_pmsm->ctrl_q.ctrl_status_q); + wake_up(&ctrl_pmsm->ctrl_q.ctrl_status_wait); + spin_unlock_irqrestore(&ctrl_pmsm->ctrl_q.ctrl_status_q_lock, + flags); + } + + return rc; +} + +static int msm_config_vfe(struct msm_sync *sync, void __user *arg) +{ + struct msm_vfe_cfg_cmd cfgcmd; + struct msm_pmem_region region[8]; + struct axidata axi_data; + void *data = NULL; + int rc = -EIO; + + memset(&axi_data, 0, sizeof(axi_data)); + + if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + switch(cfgcmd.cmd_type) { + case CMD_STATS_ENABLE: + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->stats, + MSM_PMEM_AEC_AWB, ®ion[0], + NUM_WB_EXP_STAT_OUTPUT_BUFFERS); + if (!axi_data.bufnum1) { + pr_err("%s %d: pmem region lookup error\n", + __FUNCTION__, __LINE__); + return -EINVAL; + } + axi_data.region = ®ion[0]; + data = &axi_data; + break; + case CMD_STATS_AF_ENABLE: + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->stats, + MSM_PMEM_AF, ®ion[0], + NUM_AF_STAT_OUTPUT_BUFFERS); + if (!axi_data.bufnum1) { + pr_err("%s %d: pmem region lookup error\n", + __FUNCTION__, __LINE__); + return -EINVAL; + } + axi_data.region = ®ion[0]; + data = &axi_data; + break; + case CMD_GENERAL: + case CMD_STATS_DISABLE: + break; + default: + pr_err("%s: unknown command type %d\n", + __FUNCTION__, cfgcmd.cmd_type); + return -EINVAL; + } + + + if (sync->vfefn.vfe_config) + rc = sync->vfefn.vfe_config(&cfgcmd, data); + + return rc; +} + +static int msm_frame_axi_cfg(struct msm_sync *sync, + struct msm_vfe_cfg_cmd *cfgcmd) +{ + int rc = -EIO; + struct axidata axi_data; + void *data = &axi_data; + struct msm_pmem_region region[8]; + int pmem_type; + + memset(&axi_data, 0, sizeof(axi_data)); + + switch (cfgcmd->cmd_type) { + case CMD_AXI_CFG_OUT1: + pmem_type = MSM_PMEM_OUTPUT1; + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->frame, pmem_type, + ®ion[0], 8); + if (!axi_data.bufnum1) { + pr_err("%s %d: pmem region lookup error\n", + __FUNCTION__, __LINE__); + return -EINVAL; + } + break; + + case CMD_AXI_CFG_OUT2: + pmem_type = MSM_PMEM_OUTPUT2; + axi_data.bufnum2 = + msm_pmem_region_lookup(&sync->frame, pmem_type, + ®ion[0], 8); + if (!axi_data.bufnum2) { + pr_err("%s %d: pmem region lookup error (empty %d)\n", + __FUNCTION__, __LINE__, + hlist_empty(&sync->frame)); + return -EINVAL; + } + break; + + case CMD_AXI_CFG_SNAP_O1_AND_O2: + pmem_type = MSM_PMEM_THUMBNAIL; + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->frame, pmem_type, + ®ion[0], 8); + if (!axi_data.bufnum1) { + pr_err("%s %d: pmem region lookup error\n", + __FUNCTION__, __LINE__); + return -EINVAL; + } + + pmem_type = MSM_PMEM_MAINIMG; + axi_data.bufnum2 = + msm_pmem_region_lookup(&sync->frame, pmem_type, + ®ion[axi_data.bufnum1], 8); + if (!axi_data.bufnum2) { + pr_err("%s %d: pmem region lookup error\n", + __FUNCTION__, __LINE__); + return -EINVAL; + } + break; + + case CMD_RAW_PICT_AXI_CFG: + pmem_type = MSM_PMEM_RAW_MAINIMG; + axi_data.bufnum2 = + msm_pmem_region_lookup(&sync->frame, pmem_type, + ®ion[0], 8); + if (!axi_data.bufnum2) { + pr_err("%s %d: pmem region lookup error\n", + __FUNCTION__, __LINE__); + return -EINVAL; + } + break; + + case CMD_GENERAL: + data = NULL; + break; + + default: + pr_err("%s: unknown command type %d\n", + __FUNCTION__, cfgcmd->cmd_type); + return -EINVAL; + } + + axi_data.region = ®ion[0]; + + /* send the AXI configuration command to driver */ + if (sync->vfefn.vfe_config) + rc = sync->vfefn.vfe_config(cfgcmd, data); + + return rc; +} + +static int msm_get_sensor_info(struct msm_sync *sync, void __user *arg) +{ + int rc = 0; + struct msm_camsensor_info info; + struct msm_camera_sensor_info *sdata; + + if (copy_from_user(&info, + arg, + sizeof(struct msm_camsensor_info))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + sdata = sync->pdev->dev.platform_data; + CDBG("sensor_name %s\n", sdata->sensor_name); + + memcpy(&info.name[0], + sdata->sensor_name, + MAX_SENSOR_NAME); + info.flash_enabled = sdata->flash_type != MSM_CAMERA_FLASH_NONE; + + /* copy back to user space */ + if (copy_to_user((void *)arg, + &info, + sizeof(struct msm_camsensor_info))) { + ERR_COPY_TO_USER(); + rc = -EFAULT; + } + + return rc; +} + +static int __msm_put_frame_buf(struct msm_sync *sync, + struct msm_frame *pb) +{ + unsigned long pphy; + struct msm_vfe_cfg_cmd cfgcmd; + + int rc = -EIO; + + pphy = msm_pmem_frame_vtop_lookup(sync, + pb->buffer, + pb->y_off, pb->cbcr_off, pb->fd); + + if (pphy != 0) { + CDBG("rel: vaddr = 0x%lx, paddr = 0x%lx\n", + pb->buffer, pphy); + cfgcmd.cmd_type = CMD_FRAME_BUF_RELEASE; + cfgcmd.value = (void *)pb; + if (sync->vfefn.vfe_config) + rc = sync->vfefn.vfe_config(&cfgcmd, &pphy); + } else { + pr_err("%s: msm_pmem_frame_vtop_lookup failed\n", + __FUNCTION__); + rc = -EINVAL; + } + + return rc; +} + +static int msm_put_frame_buffer(struct msm_sync *sync, void __user *arg) +{ + struct msm_frame buf_t; + + if (copy_from_user(&buf_t, + arg, + sizeof(struct msm_frame))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + return __msm_put_frame_buf(sync, &buf_t); +} + +static int __msm_register_pmem(struct msm_sync *sync, + struct msm_pmem_info *pinfo) +{ + int rc = 0; + + switch (pinfo->type) { + case MSM_PMEM_OUTPUT1: + case MSM_PMEM_OUTPUT2: + case MSM_PMEM_THUMBNAIL: + case MSM_PMEM_MAINIMG: + case MSM_PMEM_RAW_MAINIMG: + rc = msm_pmem_table_add(&sync->frame, pinfo); + break; + + case MSM_PMEM_AEC_AWB: + case MSM_PMEM_AF: + rc = msm_pmem_table_add(&sync->stats, pinfo); + break; + + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static int msm_register_pmem(struct msm_sync *sync, void __user *arg) +{ + struct msm_pmem_info info; + + if (copy_from_user(&info, arg, sizeof(info))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + return __msm_register_pmem(sync, &info); +} + +static int msm_stats_axi_cfg(struct msm_sync *sync, + struct msm_vfe_cfg_cmd *cfgcmd) +{ + int rc = -EIO; + struct axidata axi_data; + void *data = &axi_data; + + struct msm_pmem_region region[3]; + int pmem_type = MSM_PMEM_MAX; + + memset(&axi_data, 0, sizeof(axi_data)); + + switch (cfgcmd->cmd_type) { + case CMD_STATS_AXI_CFG: + pmem_type = MSM_PMEM_AEC_AWB; + break; + case CMD_STATS_AF_AXI_CFG: + pmem_type = MSM_PMEM_AF; + break; + case CMD_GENERAL: + data = NULL; + break; + default: + pr_err("%s: unknown command type %d\n", + __FUNCTION__, cfgcmd->cmd_type); + return -EINVAL; + } + + if (cfgcmd->cmd_type != CMD_GENERAL) { + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->stats, pmem_type, + ®ion[0], NUM_WB_EXP_STAT_OUTPUT_BUFFERS); + if (!axi_data.bufnum1) { + pr_err("%s %d: pmem region lookup error\n", + __FUNCTION__, __LINE__); + return -EINVAL; + } + axi_data.region = ®ion[0]; + } + + /* send the AEC/AWB STATS configuration command to driver */ + if (sync->vfefn.vfe_config) + rc = sync->vfefn.vfe_config(cfgcmd, &axi_data); + + return rc; +} + +static int msm_put_stats_buffer(struct msm_sync *sync, void __user *arg) +{ + int rc = -EIO; + + struct msm_stats_buf buf; + unsigned long pphy; + struct msm_vfe_cfg_cmd cfgcmd; + + if (copy_from_user(&buf, arg, + sizeof(struct msm_stats_buf))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + CDBG("msm_put_stats_buffer\n"); + pphy = msm_pmem_stats_vtop_lookup(sync, buf.buffer, buf.fd); + + if (pphy != 0) { + if (buf.type == STAT_AEAW) + cfgcmd.cmd_type = CMD_STATS_BUF_RELEASE; + else if (buf.type == STAT_AF) + cfgcmd.cmd_type = CMD_STATS_AF_BUF_RELEASE; + else { + pr_err("%s: invalid buf type %d\n", + __FUNCTION__, + buf.type); + rc = -EINVAL; + goto put_done; + } + + cfgcmd.value = (void *)&buf; + + if (sync->vfefn.vfe_config) { + rc = sync->vfefn.vfe_config(&cfgcmd, &pphy); + if (rc < 0) + pr_err("msm_put_stats_buffer: "\ + "vfe_config err %d\n", rc); + } else + pr_err("msm_put_stats_buffer: vfe_config is NULL\n"); + } else { + pr_err("msm_put_stats_buffer: NULL physical address\n"); + rc = -EINVAL; + } + +put_done: + return rc; +} + +static int msm_axi_config(struct msm_sync *sync, void __user *arg) +{ + struct msm_vfe_cfg_cmd cfgcmd; + + if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + switch (cfgcmd.cmd_type) { + case CMD_AXI_CFG_OUT1: + case CMD_AXI_CFG_OUT2: + case CMD_AXI_CFG_SNAP_O1_AND_O2: + case CMD_RAW_PICT_AXI_CFG: + return msm_frame_axi_cfg(sync, &cfgcmd); + + case CMD_STATS_AXI_CFG: + case CMD_STATS_AF_AXI_CFG: + return msm_stats_axi_cfg(sync, &cfgcmd); + + default: + pr_err("%s: unknown command type %d\n", + __FUNCTION__, + cfgcmd.cmd_type); + return -EINVAL; + } + + return 0; +} + +static int __msm_get_pic(struct msm_sync *sync, struct msm_ctrl_cmd *ctrl) +{ + unsigned long flags; + int rc = 0; + int tm; + + struct msm_queue_cmd *qcmd = NULL; + + tm = (int)ctrl->timeout_ms; + + rc = wait_event_interruptible_timeout( + sync->pict_frame_wait, + !list_empty_careful(&sync->pict_frame_q), + msecs_to_jiffies(tm)); + if (list_empty_careful(&sync->pict_frame_q)) { + if (rc == 0) + return -ETIMEDOUT; + if (rc < 0) { + pr_err("msm_camera_get_picture, rc = %d\n", rc); + return rc; + } + } + + spin_lock_irqsave(&sync->pict_frame_q_lock, flags); + BUG_ON(list_empty(&sync->pict_frame_q)); + qcmd = list_first_entry(&sync->pict_frame_q, + struct msm_queue_cmd, list); + list_del_init(&qcmd->list); + spin_unlock_irqrestore(&sync->pict_frame_q_lock, flags); + + if (qcmd->command != NULL) { + struct msm_ctrl_cmd *q = + (struct msm_ctrl_cmd *)qcmd->command; + ctrl->type = q->type; + ctrl->status = q->status; + } else { + ctrl->type = -1; + ctrl->status = -1; + } + + kfree(qcmd); + return rc; +} + +static int msm_get_pic(struct msm_sync *sync, void __user *arg) +{ + struct msm_ctrl_cmd ctrlcmd_t; + int rc; + + if (copy_from_user(&ctrlcmd_t, + arg, + sizeof(struct msm_ctrl_cmd))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + rc = __msm_get_pic(sync, &ctrlcmd_t); + if (rc < 0) + return rc; + + if (sync->croplen) { + if (ctrlcmd_t.length != sync->croplen) { + pr_err("msm_get_pic: invalid len %d < %d\n", + ctrlcmd_t.length, + sync->croplen); + return -EINVAL; + } + if (copy_to_user(ctrlcmd_t.value, + sync->cropinfo, + sync->croplen)) { + ERR_COPY_TO_USER(); + return -EFAULT; + } + } + + if (copy_to_user((void *)arg, + &ctrlcmd_t, + sizeof(struct msm_ctrl_cmd))) { + ERR_COPY_TO_USER(); + return -EFAULT; + } + return 0; +} + +static int msm_set_crop(struct msm_sync *sync, void __user *arg) +{ + struct crop_info crop; + + if (copy_from_user(&crop, + arg, + sizeof(struct crop_info))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } + + if (!sync->croplen) { + sync->cropinfo = kmalloc(crop.len, GFP_KERNEL); + if (!sync->cropinfo) + return -ENOMEM; + } else if (sync->croplen < crop.len) + return -EINVAL; + + if (copy_from_user(sync->cropinfo, + crop.info, + crop.len)) { + ERR_COPY_FROM_USER(); + kfree(sync->cropinfo); + return -EFAULT; + } + + sync->croplen = crop.len; + + return 0; +} + +static int msm_pict_pp_done(struct msm_sync *sync, void __user *arg) +{ + struct msm_ctrl_cmd udata; + struct msm_ctrl_cmd *ctrlcmd = NULL; + struct msm_queue_cmd *qcmd = NULL; + unsigned long flags; + int rc = 0; + + if (!sync->pict_pp) + return -EINVAL; + + if (copy_from_user(&udata, arg, sizeof(struct msm_ctrl_cmd))) { + ERR_COPY_FROM_USER(); + rc = -EFAULT; + goto pp_fail; + } + + qcmd = kmalloc(sizeof(struct msm_queue_cmd) + + sizeof(struct msm_ctrl_cmd), + GFP_KERNEL); + if (!qcmd) { + rc = -ENOMEM; + goto pp_fail; + } + + qcmd->type = MSM_CAM_Q_VFE_MSG; + qcmd->command = ctrlcmd = (struct msm_ctrl_cmd *)(qcmd + 1); + memset(ctrlcmd, 0, sizeof(struct msm_ctrl_cmd)); + ctrlcmd->type = udata.type; + ctrlcmd->status = udata.status; + + spin_lock_irqsave(&sync->pict_frame_q_lock, flags); + list_add_tail(&qcmd->list, &sync->pict_frame_q); + spin_unlock_irqrestore(&sync->pict_frame_q_lock, flags); + wake_up(&sync->pict_frame_wait); + +pp_fail: + return rc; +} + +static long msm_ioctl_common(struct msm_device *pmsm, + unsigned int cmd, + void __user *argp) +{ + CDBG("msm_ioctl_common\n"); + switch (cmd) { + case MSM_CAM_IOCTL_REGISTER_PMEM: + return msm_register_pmem(pmsm->sync, argp); + case MSM_CAM_IOCTL_UNREGISTER_PMEM: + return msm_pmem_table_del(pmsm->sync, argp); + default: + return -EINVAL; + } +} + +static long msm_ioctl_config(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + int rc = -EINVAL; + void __user *argp = (void __user *)arg; + struct msm_device *pmsm = filep->private_data; + + CDBG("msm_ioctl_config cmd = %d\n", _IOC_NR(cmd)); + + switch (cmd) { + case MSM_CAM_IOCTL_GET_SENSOR_INFO: + rc = msm_get_sensor_info(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_CONFIG_VFE: + /* Coming from config thread for update */ + rc = msm_config_vfe(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_GET_STATS: + /* Coming from config thread wait + * for vfe statistics and control requests */ + rc = msm_get_stats(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_ENABLE_VFE: + /* This request comes from control thread: + * enable either QCAMTASK or VFETASK */ + rc = msm_enable_vfe(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_DISABLE_VFE: + /* This request comes from control thread: + * disable either QCAMTASK or VFETASK */ + rc = msm_disable_vfe(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_VFE_APPS_RESET: + msm_camio_vfe_blk_reset(); + rc = 0; + break; + + case MSM_CAM_IOCTL_RELEASE_STATS_BUFFER: + rc = msm_put_stats_buffer(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_AXI_CONFIG: + rc = msm_axi_config(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_SET_CROP: + rc = msm_set_crop(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_PICT_PP: { + uint8_t enable; + if (copy_from_user(&enable, argp, sizeof(enable))) { + ERR_COPY_FROM_USER(); + rc = -EFAULT; + } else { + pmsm->sync->pict_pp = enable; + rc = 0; + } + break; + } + + case MSM_CAM_IOCTL_PICT_PP_DONE: + rc = msm_pict_pp_done(pmsm->sync, argp); + break; + + case MSM_CAM_IOCTL_SENSOR_IO_CFG: + rc = pmsm->sync->sctrl.s_config(argp); + break; + + case MSM_CAM_IOCTL_FLASH_LED_CFG: { + uint32_t led_state; + if (copy_from_user(&led_state, argp, sizeof(led_state))) { + ERR_COPY_FROM_USER(); + rc = -EFAULT; + } else + rc = msm_camera_flash_set_led_state(led_state); + break; + } + + default: + rc = msm_ioctl_common(pmsm, cmd, argp); + break; + } + + CDBG("msm_ioctl_config cmd = %d DONE\n", _IOC_NR(cmd)); + return rc; +} + +static int msm_unblock_poll_frame(struct msm_sync *); + +static long msm_ioctl_frame(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + int rc = -EINVAL; + void __user *argp = (void __user *)arg; + struct msm_device *pmsm = filep->private_data; + + + switch (cmd) { + case MSM_CAM_IOCTL_GETFRAME: + /* Coming from frame thread to get frame + * after SELECT is done */ + rc = msm_get_frame(pmsm->sync, argp); + break; + case MSM_CAM_IOCTL_RELEASE_FRAME_BUFFER: + rc = msm_put_frame_buffer(pmsm->sync, argp); + break; + case MSM_CAM_IOCTL_UNBLOCK_POLL_FRAME: + rc = msm_unblock_poll_frame(pmsm->sync); + break; + default: + break; + } + + return rc; +} + + +static long msm_ioctl_control(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + int rc = -EINVAL; + void __user *argp = (void __user *)arg; + struct msm_control_device *ctrl_pmsm = filep->private_data; + struct msm_device *pmsm = ctrl_pmsm->pmsm; + + switch (cmd) { + case MSM_CAM_IOCTL_CTRL_COMMAND: + /* Coming from control thread, may need to wait for + * command status */ + rc = msm_control(ctrl_pmsm, 1, argp); + break; + case MSM_CAM_IOCTL_CTRL_COMMAND_2: + /* Sends a message, returns immediately */ + rc = msm_control(ctrl_pmsm, 0, argp); + break; + case MSM_CAM_IOCTL_CTRL_CMD_DONE: + /* Config thread calls the control thread to notify it + * of the result of a MSM_CAM_IOCTL_CTRL_COMMAND. + */ + rc = msm_ctrl_cmd_done(ctrl_pmsm, argp); + break; + case MSM_CAM_IOCTL_GET_PICTURE: + rc = msm_get_pic(pmsm->sync, argp); + break; + default: + rc = msm_ioctl_common(pmsm, cmd, argp); + break; + } + + return rc; +} + +static int __msm_release(struct msm_sync *sync) +{ + struct msm_pmem_region *region; + struct hlist_node *hnode; + struct hlist_node *n; + + mutex_lock(&sync->lock); + if (sync->opencnt) + sync->opencnt--; + + if (!sync->opencnt) { + /* need to clean up system resource */ + if (sync->vfefn.vfe_release) + sync->vfefn.vfe_release(sync->pdev); + + if (sync->cropinfo) { + kfree(sync->cropinfo); + sync->cropinfo = NULL; + sync->croplen = 0; + } + + hlist_for_each_entry_safe(region, hnode, n, + &sync->frame, list) { + hlist_del(hnode); + put_pmem_file(region->file); + kfree(region); + } + + hlist_for_each_entry_safe(region, hnode, n, + &sync->stats, list) { + hlist_del(hnode); + put_pmem_file(region->file); + kfree(region); + } + + MSM_DRAIN_QUEUE(sync, msg_event_q); + MSM_DRAIN_QUEUE(sync, prev_frame_q); + MSM_DRAIN_QUEUE(sync, pict_frame_q); + + sync->sctrl.s_release(); + wake_unlock(&sync->wake_lock); + + sync->apps_id = NULL; + CDBG("msm_release completed!\n"); + } + mutex_unlock(&sync->lock); + + return 0; +} + +static int msm_release_config(struct inode *node, struct file *filep) +{ + int rc; + struct msm_device *pmsm = filep->private_data; + printk("msm_camera: RELEASE %s\n", filep->f_path.dentry->d_name.name); + rc = __msm_release(pmsm->sync); + atomic_set(&pmsm->opened, 0); + return rc; +} + +static int msm_release_control(struct inode *node, struct file *filep) +{ + int rc; + struct msm_control_device *ctrl_pmsm = filep->private_data; + struct msm_device *pmsm = ctrl_pmsm->pmsm; + printk("msm_camera: RELEASE %s\n", filep->f_path.dentry->d_name.name); + rc = __msm_release(pmsm->sync); + if (!rc) { + MSM_DRAIN_QUEUE(&ctrl_pmsm->ctrl_q, ctrl_status_q); + MSM_DRAIN_QUEUE(pmsm->sync, pict_frame_q); + } + kfree(ctrl_pmsm); + return rc; +} + +static int msm_release_frame(struct inode *node, struct file *filep) +{ + int rc; + struct msm_device *pmsm = filep->private_data; + printk("msm_camera: RELEASE %s\n", filep->f_path.dentry->d_name.name); + rc = __msm_release(pmsm->sync); + if (!rc) { + MSM_DRAIN_QUEUE(pmsm->sync, prev_frame_q); + atomic_set(&pmsm->opened, 0); + } + return rc; +} + +static int msm_unblock_poll_frame(struct msm_sync *sync) +{ + unsigned long flags; + CDBG("msm_unblock_poll_frame\n"); + spin_lock_irqsave(&sync->prev_frame_q_lock, flags); + sync->unblock_poll_frame = 1; + wake_up(&sync->prev_frame_wait); + spin_unlock_irqrestore(&sync->prev_frame_q_lock, flags); + return 0; +} + +static unsigned int __msm_poll_frame(struct msm_sync *sync, + struct file *filep, + struct poll_table_struct *pll_table) +{ + int rc = 0; + unsigned long flags; + + poll_wait(filep, &sync->prev_frame_wait, pll_table); + + spin_lock_irqsave(&sync->prev_frame_q_lock, flags); + if (!list_empty_careful(&sync->prev_frame_q)) + /* frame ready */ + rc = POLLIN | POLLRDNORM; + if (sync->unblock_poll_frame) { + CDBG("%s: sync->unblock_poll_frame is true\n", __func__); + rc |= POLLPRI; + sync->unblock_poll_frame = 0; + } + spin_unlock_irqrestore(&sync->prev_frame_q_lock, flags); + + return rc; +} + +static unsigned int msm_poll_frame(struct file *filep, + struct poll_table_struct *pll_table) +{ + struct msm_device *pmsm = filep->private_data; + return __msm_poll_frame(pmsm->sync, filep, pll_table); +} + +/* + * This function executes in interrupt context. + */ + +static void *msm_vfe_sync_alloc(int size, + void *syncdata __attribute__((unused))) +{ + struct msm_queue_cmd *qcmd = + kmalloc(sizeof(struct msm_queue_cmd) + size, GFP_ATOMIC); + return qcmd ? qcmd + 1 : NULL; +} + +/* + * This function executes in interrupt context. + */ + +static void msm_vfe_sync(struct msm_vfe_resp *vdata, + enum msm_queue qtype, void *syncdata) +{ + struct msm_queue_cmd *qcmd = NULL; + struct msm_queue_cmd *qcmd_frame = NULL; + struct msm_vfe_phy_info *fphy; + + unsigned long flags; + struct msm_sync *sync = (struct msm_sync *)syncdata; + if (!sync) { + pr_err("msm_camera: no context in dsp callback.\n"); + return; + } + + qcmd = ((struct msm_queue_cmd *)vdata) - 1; + qcmd->type = qtype; + + if (qtype == MSM_CAM_Q_VFE_MSG) { + switch(vdata->type) { + case VFE_MSG_OUTPUT1: + case VFE_MSG_OUTPUT2: + qcmd_frame = + kmalloc(sizeof(struct msm_queue_cmd) + + sizeof(struct msm_vfe_phy_info), + GFP_ATOMIC); + if (!qcmd_frame) + goto mem_fail; + fphy = (struct msm_vfe_phy_info *)(qcmd_frame + 1); + *fphy = vdata->phy; + + qcmd_frame->type = MSM_CAM_Q_VFE_MSG; + qcmd_frame->command = fphy; + + CDBG("qcmd_frame= 0x%x phy_y= 0x%x, phy_cbcr= 0x%x\n", + (int) qcmd_frame, fphy->y_phy, fphy->cbcr_phy); + + spin_lock_irqsave(&sync->prev_frame_q_lock, flags); + list_add_tail(&qcmd_frame->list, &sync->prev_frame_q); + wake_up(&sync->prev_frame_wait); + spin_unlock_irqrestore(&sync->prev_frame_q_lock, flags); + CDBG("woke up frame thread\n"); + break; + case VFE_MSG_SNAPSHOT: + if (sync->pict_pp) + break; + + CDBG("snapshot pp = %d\n", sync->pict_pp); + qcmd_frame = + kmalloc(sizeof(struct msm_queue_cmd), + GFP_ATOMIC); + if (!qcmd_frame) + goto mem_fail; + qcmd_frame->type = MSM_CAM_Q_VFE_MSG; + qcmd_frame->command = NULL; + spin_lock_irqsave(&sync->pict_frame_q_lock, + flags); + list_add_tail(&qcmd_frame->list, &sync->pict_frame_q); + wake_up(&sync->pict_frame_wait); + spin_unlock_irqrestore(&sync->pict_frame_q_lock, flags); + CDBG("woke up picture thread\n"); + break; + default: + CDBG("%s: qtype = %d not handled\n", + __func__, vdata->type); + break; + } + } + + qcmd->command = (void *)vdata; + CDBG("vdata->type = %d\n", vdata->type); + + spin_lock_irqsave(&sync->msg_event_q_lock, flags); + list_add_tail(&qcmd->list, &sync->msg_event_q); + wake_up(&sync->msg_event_wait); + spin_unlock_irqrestore(&sync->msg_event_q_lock, flags); + CDBG("woke up config thread\n"); + return; + +mem_fail: + kfree(qcmd); +} + +static struct msm_vfe_callback msm_vfe_s = { + .vfe_resp = msm_vfe_sync, + .vfe_alloc = msm_vfe_sync_alloc, +}; + +static int __msm_open(struct msm_sync *sync, const char *const apps_id) +{ + int rc = 0; + + mutex_lock(&sync->lock); + if (sync->apps_id && strcmp(sync->apps_id, apps_id)) { + pr_err("msm_camera(%s): sensor %s is already opened for %s\n", + apps_id, + sync->sdata->sensor_name, + sync->apps_id); + rc = -EBUSY; + goto msm_open_done; + } + + sync->apps_id = apps_id; + + if (!sync->opencnt) { + wake_lock(&sync->wake_lock); + + msm_camvfe_fn_init(&sync->vfefn, sync); + if (sync->vfefn.vfe_init) { + rc = sync->vfefn.vfe_init(&msm_vfe_s, + sync->pdev); + if (rc < 0) { + pr_err("vfe_init failed at %d\n", rc); + goto msm_open_done; + } + rc = sync->sctrl.s_init(sync->sdata); + if (rc < 0) { + pr_err("sensor init failed: %d\n", rc); + goto msm_open_done; + } + } else { + pr_err("no sensor init func\n"); + rc = -ENODEV; + goto msm_open_done; + } + + if (rc >= 0) { + INIT_HLIST_HEAD(&sync->frame); + INIT_HLIST_HEAD(&sync->stats); + sync->unblock_poll_frame = 0; + } + } + sync->opencnt++; + +msm_open_done: + mutex_unlock(&sync->lock); + return rc; +} + +static int msm_open_common(struct inode *inode, struct file *filep, + int once) +{ + int rc; + struct msm_device *pmsm = + container_of(inode->i_cdev, struct msm_device, cdev); + + CDBG("msm_camera: open %s\n", filep->f_path.dentry->d_name.name); + + if (atomic_cmpxchg(&pmsm->opened, 0, 1) && once) { + pr_err("msm_camera: %s is already opened.\n", + filep->f_path.dentry->d_name.name); + return -EBUSY; + } + + rc = nonseekable_open(inode, filep); + if (rc < 0) { + pr_err("msm_open: nonseekable_open error %d\n", rc); + return rc; + } + + rc = __msm_open(pmsm->sync, MSM_APPS_ID_PROP); + if (rc < 0) + return rc; + + filep->private_data = pmsm; + + CDBG("msm_open() open: rc = %d\n", rc); + return rc; +} + +static int msm_open(struct inode *inode, struct file *filep) +{ + return msm_open_common(inode, filep, 1); +} + +static int msm_open_control(struct inode *inode, struct file *filep) +{ + int rc; + + struct msm_control_device *ctrl_pmsm = + kmalloc(sizeof(struct msm_control_device), GFP_KERNEL); + if (!ctrl_pmsm) + return -ENOMEM; + + rc = msm_open_common(inode, filep, 0); + if (rc < 0) + return rc; + + ctrl_pmsm->pmsm = filep->private_data; + filep->private_data = ctrl_pmsm; + spin_lock_init(&ctrl_pmsm->ctrl_q.ctrl_status_q_lock); + INIT_LIST_HEAD(&ctrl_pmsm->ctrl_q.ctrl_status_q); + init_waitqueue_head(&ctrl_pmsm->ctrl_q.ctrl_status_wait); + + CDBG("msm_open() open: rc = %d\n", rc); + return rc; +} + +static int __msm_v4l2_control(struct msm_sync *sync, + struct msm_ctrl_cmd *out) +{ + int rc = 0; + + struct msm_queue_cmd *qcmd = NULL, *rcmd = NULL; + struct msm_ctrl_cmd *ctrl; + struct msm_control_device_queue FIXME; + + /* wake up config thread, 4 is for V4L2 application */ + qcmd = kmalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL); + if (!qcmd) { + pr_err("msm_control: cannot allocate buffer\n"); + rc = -ENOMEM; + goto end; + } + qcmd->type = MSM_CAM_Q_V4L2_REQ; + qcmd->command = out; + + rcmd = __msm_control(sync, &FIXME, qcmd, out->timeout_ms); + if (IS_ERR(rcmd)) { + rc = PTR_ERR(rcmd); + goto end; + } + + ctrl = (struct msm_ctrl_cmd *)(rcmd->command); + /* FIXME: we should just set out->length = ctrl->length; */ + BUG_ON(out->length < ctrl->length); + memcpy(out->value, ctrl->value, ctrl->length); + +end: + if (rcmd) kfree(rcmd); + CDBG("__msm_v4l2_control: end rc = %d\n", rc); + return rc; +} + +static const struct file_operations msm_fops_config = { + .owner = THIS_MODULE, + .open = msm_open, + .unlocked_ioctl = msm_ioctl_config, + .release = msm_release_config, +}; + +static const struct file_operations msm_fops_control = { + .owner = THIS_MODULE, + .open = msm_open_control, + .unlocked_ioctl = msm_ioctl_control, + .release = msm_release_control, +}; + +static const struct file_operations msm_fops_frame = { + .owner = THIS_MODULE, + .open = msm_open, + .unlocked_ioctl = msm_ioctl_frame, + .release = msm_release_frame, + .poll = msm_poll_frame, +}; + +static int msm_setup_cdev(struct msm_device *msm, + int node, + dev_t devno, + const char *suffix, + const struct file_operations *fops) +{ + int rc = -ENODEV; + + struct device *device = + device_create(msm_class, NULL, + devno, NULL, + "%s%d", suffix, node); + + if (IS_ERR(device)) { + rc = PTR_ERR(device); + pr_err("msm_camera: error creating device: %d\n", rc); + return rc; + } + + cdev_init(&msm->cdev, fops); + msm->cdev.owner = THIS_MODULE; + + rc = cdev_add(&msm->cdev, devno, 1); + if (rc < 0) { + pr_err("msm_camera: error adding cdev: %d\n", rc); + device_destroy(msm_class, devno); + return rc; + } + + return rc; +} + +static int msm_tear_down_cdev(struct msm_device *msm, dev_t devno) +{ + cdev_del(&msm->cdev); + device_destroy(msm_class, devno); + return 0; +} + +int msm_v4l2_register(struct msm_v4l2_driver *drv) +{ + /* FIXME: support multiple sensors */ + if (list_empty(&msm_sensors)) + return -ENODEV; + + drv->sync = list_first_entry(&msm_sensors, struct msm_sync, list); + drv->open = __msm_open; + drv->release = __msm_release; + drv->ctrl = __msm_v4l2_control; + drv->reg_pmem = __msm_register_pmem; + drv->get_frame = __msm_get_frame; + drv->put_frame = __msm_put_frame_buf; + drv->get_pict = __msm_get_pic; + drv->drv_poll = __msm_poll_frame; + + return 0; +} +EXPORT_SYMBOL(msm_v4l2_register); + +int msm_v4l2_unregister(struct msm_v4l2_driver *drv) +{ + drv->sync = NULL; + return 0; +} +EXPORT_SYMBOL(msm_v4l2_unregister); + +static int msm_sync_init(struct msm_sync *sync, + struct platform_device *pdev, + int (*sensor_probe)(const struct msm_camera_sensor_info *, + struct msm_sensor_ctrl *)) +{ + int rc = 0; + struct msm_sensor_ctrl sctrl; + sync->sdata = pdev->dev.platform_data; + + spin_lock_init(&sync->msg_event_q_lock); + INIT_LIST_HEAD(&sync->msg_event_q); + init_waitqueue_head(&sync->msg_event_wait); + + spin_lock_init(&sync->prev_frame_q_lock); + INIT_LIST_HEAD(&sync->prev_frame_q); + init_waitqueue_head(&sync->prev_frame_wait); + + spin_lock_init(&sync->pict_frame_q_lock); + INIT_LIST_HEAD(&sync->pict_frame_q); + init_waitqueue_head(&sync->pict_frame_wait); + + wake_lock_init(&sync->wake_lock, WAKE_LOCK_IDLE, "msm_camera"); + + rc = msm_camio_probe_on(pdev); + if (rc < 0) + return rc; + rc = sensor_probe(sync->sdata, &sctrl); + if (rc >= 0) { + sync->pdev = pdev; + sync->sctrl = sctrl; + } + msm_camio_probe_off(pdev); + if (rc < 0) { + pr_err("msm_camera: failed to initialize %s\n", + sync->sdata->sensor_name); + wake_lock_destroy(&sync->wake_lock); + return rc; + } + + sync->opencnt = 0; + mutex_init(&sync->lock); + CDBG("initialized %s\n", sync->sdata->sensor_name); + return rc; +} + +static int msm_sync_destroy(struct msm_sync *sync) +{ + wake_lock_destroy(&sync->wake_lock); + return 0; +} + +static int msm_device_init(struct msm_device *pmsm, + struct msm_sync *sync, + int node) +{ + int dev_num = 3 * node; + int rc = msm_setup_cdev(pmsm, node, + MKDEV(MAJOR(msm_devno), dev_num), + "control", &msm_fops_control); + if (rc < 0) { + pr_err("error creating control node: %d\n", rc); + return rc; + } + + rc = msm_setup_cdev(pmsm + 1, node, + MKDEV(MAJOR(msm_devno), dev_num + 1), + "config", &msm_fops_config); + if (rc < 0) { + pr_err("error creating config node: %d\n", rc); + msm_tear_down_cdev(pmsm, MKDEV(MAJOR(msm_devno), + dev_num)); + return rc; + } + + rc = msm_setup_cdev(pmsm + 2, node, + MKDEV(MAJOR(msm_devno), dev_num + 2), + "frame", &msm_fops_frame); + if (rc < 0) { + pr_err("error creating frame node: %d\n", rc); + msm_tear_down_cdev(pmsm, + MKDEV(MAJOR(msm_devno), dev_num)); + msm_tear_down_cdev(pmsm + 1, + MKDEV(MAJOR(msm_devno), dev_num + 1)); + return rc; + } + + atomic_set(&pmsm[0].opened, 0); + atomic_set(&pmsm[1].opened, 0); + atomic_set(&pmsm[2].opened, 0); + + pmsm[0].sync = sync; + pmsm[1].sync = sync; + pmsm[2].sync = sync; + + return rc; +} + +int msm_camera_drv_start(struct platform_device *dev, + int (*sensor_probe)(const struct msm_camera_sensor_info *, + struct msm_sensor_ctrl *)) +{ + struct msm_device *pmsm = NULL; + struct msm_sync *sync; + int rc = -ENODEV; + static int camera_node; + + if (camera_node >= MSM_MAX_CAMERA_SENSORS) { + pr_err("msm_camera: too many camera sensors\n"); + return rc; + } + + if (!msm_class) { + /* There are three device nodes per sensor */ + rc = alloc_chrdev_region(&msm_devno, 0, + 3 * MSM_MAX_CAMERA_SENSORS, + "msm_camera"); + if (rc < 0) { + pr_err("msm_camera: failed to allocate chrdev: %d\n", + rc); + return rc; + } + + msm_class = class_create(THIS_MODULE, "msm_camera"); + if (IS_ERR(msm_class)) { + rc = PTR_ERR(msm_class); + pr_err("msm_camera: create device class failed: %d\n", + rc); + return rc; + } + } + + pmsm = kzalloc(sizeof(struct msm_device) * 3 + + sizeof(struct msm_sync), GFP_ATOMIC); + if (!pmsm) + return -ENOMEM; + sync = (struct msm_sync *)(pmsm + 3); + + rc = msm_sync_init(sync, dev, sensor_probe); + if (rc < 0) { + kfree(pmsm); + return rc; + } + + CDBG("setting camera node %d\n", camera_node); + rc = msm_device_init(pmsm, sync, camera_node); + if (rc < 0) { + msm_sync_destroy(sync); + kfree(pmsm); + return rc; + } + + camera_node++; + list_add(&sync->list, &msm_sensors); + return rc; +} +EXPORT_SYMBOL(msm_camera_drv_start); diff --git a/drivers/media/video/msm/msm_io7x.c b/drivers/media/video/msm/msm_io7x.c new file mode 100644 index 0000000000000..55c020bb7afa3 --- /dev/null +++ b/drivers/media/video/msm/msm_io7x.c @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2008-2009 QUALCOMM Incorporated + */ + +#include +#include +#include +#include +#include +#include + +#define CAMIF_CFG_RMSK 0x1fffff +#define CAM_SEL_BMSK 0x2 +#define CAM_PCLK_SRC_SEL_BMSK 0x60000 +#define CAM_PCLK_INVERT_BMSK 0x80000 +#define CAM_PAD_REG_SW_RESET_BMSK 0x100000 + +#define EXT_CAM_HSYNC_POL_SEL_BMSK 0x10000 +#define EXT_CAM_VSYNC_POL_SEL_BMSK 0x8000 +#define MDDI_CLK_CHICKEN_BIT_BMSK 0x80 + +#define CAM_SEL_SHFT 0x1 +#define CAM_PCLK_SRC_SEL_SHFT 0x11 +#define CAM_PCLK_INVERT_SHFT 0x13 +#define CAM_PAD_REG_SW_RESET_SHFT 0x14 + +#define EXT_CAM_HSYNC_POL_SEL_SHFT 0x10 +#define EXT_CAM_VSYNC_POL_SEL_SHFT 0xF +#define MDDI_CLK_CHICKEN_BIT_SHFT 0x7 +#define APPS_RESET_OFFSET 0x00000210 + +static struct clk *camio_vfe_mdc_clk; +static struct clk *camio_mdc_clk; +static struct clk *camio_vfe_clk; + +static struct msm_camera_io_ext camio_ext; +static struct resource *appio, *mdcio; +void __iomem *appbase, *mdcbase; + +static struct msm_camera_io_ext camio_ext; +static struct resource *appio, *mdcio; +void __iomem *appbase, *mdcbase; + +extern int clk_set_flags(struct clk *clk, unsigned long flags); + +int msm_camio_clk_enable(enum msm_camio_clk_type clktype) +{ + int rc = -1; + struct clk *clk = NULL; + + switch (clktype) { + case CAMIO_VFE_MDC_CLK: + clk = camio_vfe_mdc_clk = clk_get(NULL, "vfe_mdc_clk"); + break; + + case CAMIO_MDC_CLK: + clk = camio_mdc_clk = clk_get(NULL, "mdc_clk"); + break; + + case CAMIO_VFE_CLK: + clk = camio_vfe_clk = clk_get(NULL, "vfe_clk"); + break; + + default: + break; + } + + if (!IS_ERR(clk)) { + clk_enable(clk); + rc = 0; + } + + return rc; +} + +int msm_camio_clk_disable(enum msm_camio_clk_type clktype) +{ + int rc = -1; + struct clk *clk = NULL; + + switch (clktype) { + case CAMIO_VFE_MDC_CLK: + clk = camio_vfe_mdc_clk; + break; + + case CAMIO_MDC_CLK: + clk = camio_mdc_clk; + break; + + case CAMIO_VFE_CLK: + clk = camio_vfe_clk; + break; + + default: + break; + } + + if (!IS_ERR(clk)) { + clk_disable(clk); + clk_put(clk); + rc = 0; + } + + return rc; +} + +void msm_camio_clk_rate_set(int rate) +{ + struct clk *clk = camio_vfe_clk; + + if (clk != ERR_PTR(-ENOENT)) + clk_set_rate(clk, rate); +} + +int msm_camio_enable(struct platform_device *pdev) +{ + int rc = 0; + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + + camio_ext = camdev->ioext; + + appio = request_mem_region(camio_ext.appphy, + camio_ext.appsz, pdev->name); + if (!appio) { + rc = -EBUSY; + goto enable_fail; + } + + appbase = ioremap(camio_ext.appphy, + camio_ext.appsz); + if (!appbase) { + rc = -ENOMEM; + goto apps_no_mem; + } + + mdcio = request_mem_region(camio_ext.mdcphy, + camio_ext.mdcsz, pdev->name); + if (!mdcio) { + rc = -EBUSY; + goto mdc_busy; + } + + mdcbase = ioremap(camio_ext.mdcphy, + camio_ext.mdcsz); + if (!mdcbase) { + rc = -ENOMEM; + goto mdc_no_mem; + } + + camdev->camera_gpio_on(); + + msm_camio_clk_enable(CAMIO_VFE_CLK); + msm_camio_clk_enable(CAMIO_MDC_CLK); + msm_camio_clk_enable(CAMIO_VFE_MDC_CLK); + return 0; + +mdc_no_mem: + release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz); +mdc_busy: + iounmap(appbase); +apps_no_mem: + release_mem_region(camio_ext.appphy, camio_ext.appsz); +enable_fail: + return rc; +} + +void msm_camio_disable(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + + iounmap(mdcbase); + release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz); + iounmap(appbase); + release_mem_region(camio_ext.appphy, camio_ext.appsz); + + camdev->camera_gpio_off(); + + msm_camio_clk_disable(CAMIO_VFE_CLK); + msm_camio_clk_disable(CAMIO_MDC_CLK); + msm_camio_clk_disable(CAMIO_VFE_MDC_CLK); +} + +void msm_camio_camif_pad_reg_reset(void) +{ + uint32_t reg; + uint32_t mask, value; + + /* select CLKRGM_VFE_SRC_CAM_VFE_SRC: internal source */ + msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_INTERNAL); + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + + mask = CAM_SEL_BMSK | + CAM_PCLK_SRC_SEL_BMSK | + CAM_PCLK_INVERT_BMSK; + + value = 1 << CAM_SEL_SHFT | + 3 << CAM_PCLK_SRC_SEL_SHFT | + 0 << CAM_PCLK_INVERT_SHFT; + + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + mask = CAM_PAD_REG_SW_RESET_BMSK; + value = 1 << CAM_PAD_REG_SW_RESET_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + mask = CAM_PAD_REG_SW_RESET_BMSK; + value = 0 << CAM_PAD_REG_SW_RESET_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); + + msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_EXTERNAL); + mdelay(10); +} + +void msm_camio_vfe_blk_reset(void) +{ + uint32_t val; + + val = readl(appbase + 0x00000210); + val |= 0x1; + writel(val, appbase + 0x00000210); + mdelay(10); + + val = readl(appbase + 0x00000210); + val &= ~0x1; + writel(val, appbase + 0x00000210); + mdelay(10); +} + +void msm_camio_camif_pad_reg_reset_2(void) +{ + uint32_t reg; + uint32_t mask, value; + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + mask = CAM_PAD_REG_SW_RESET_BMSK; + value = 1 << CAM_PAD_REG_SW_RESET_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + mask = CAM_PAD_REG_SW_RESET_BMSK; + value = 0 << CAM_PAD_REG_SW_RESET_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); +} + +void msm_camio_clk_sel(enum msm_camio_clk_src_type srctype) +{ + struct clk *clk = NULL; + + clk = camio_vfe_clk; + + if (clk != NULL && clk != ERR_PTR(-ENOENT)) { + switch (srctype) { + case MSM_CAMIO_CLK_SRC_INTERNAL: + clk_set_flags(clk, 0x00000100 << 1); + break; + + case MSM_CAMIO_CLK_SRC_EXTERNAL: + clk_set_flags(clk, 0x00000100); + break; + + default: + break; + } + } +} + +int msm_camio_probe_on(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + camdev->camera_gpio_on(); + return msm_camio_clk_enable(CAMIO_VFE_CLK); +} + +int msm_camio_probe_off(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + camdev->camera_gpio_off(); + return msm_camio_clk_disable(CAMIO_VFE_CLK); +} diff --git a/drivers/media/video/msm/msm_io8x.c b/drivers/media/video/msm/msm_io8x.c new file mode 100644 index 0000000000000..895161ae2e148 --- /dev/null +++ b/drivers/media/video/msm/msm_io8x.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2008-2009 QUALCOMM Incorporated + */ + +#include +#include +#include +#include +#include +#include + +#define CAMIF_CFG_RMSK 0x1fffff +#define CAM_SEL_BMSK 0x2 +#define CAM_PCLK_SRC_SEL_BMSK 0x60000 +#define CAM_PCLK_INVERT_BMSK 0x80000 +#define CAM_PAD_REG_SW_RESET_BMSK 0x100000 + +#define EXT_CAM_HSYNC_POL_SEL_BMSK 0x10000 +#define EXT_CAM_VSYNC_POL_SEL_BMSK 0x8000 +#define MDDI_CLK_CHICKEN_BIT_BMSK 0x80 + +#define CAM_SEL_SHFT 0x1 +#define CAM_PCLK_SRC_SEL_SHFT 0x11 +#define CAM_PCLK_INVERT_SHFT 0x13 +#define CAM_PAD_REG_SW_RESET_SHFT 0x14 + +#define EXT_CAM_HSYNC_POL_SEL_SHFT 0x10 +#define EXT_CAM_VSYNC_POL_SEL_SHFT 0xF +#define MDDI_CLK_CHICKEN_BIT_SHFT 0x7 +#define APPS_RESET_OFFSET 0x00000210 + +static struct clk *camio_vfe_mdc_clk; +static struct clk *camio_mdc_clk; +static struct clk *camio_vfe_clk; +static struct clk *camio_vfe_axi_clk; +static struct msm_camera_io_ext camio_ext; +static struct resource *appio, *mdcio; +void __iomem *appbase, *mdcbase; + +extern int clk_set_flags(struct clk *clk, unsigned long flags); + +int msm_camio_clk_enable(enum msm_camio_clk_type clktype) +{ + int rc = 0; + struct clk *clk = NULL; + + switch (clktype) { + case CAMIO_VFE_MDC_CLK: + camio_vfe_mdc_clk = + clk = clk_get(NULL, "vfe_mdc_clk"); + break; + + case CAMIO_MDC_CLK: + camio_mdc_clk = + clk = clk_get(NULL, "mdc_clk"); + break; + + case CAMIO_VFE_CLK: + camio_vfe_clk = + clk = clk_get(NULL, "vfe_clk"); + break; + + case CAMIO_VFE_AXI_CLK: + camio_vfe_axi_clk = + clk = clk_get(NULL, "vfe_axi_clk"); + break; + + default: + break; + } + + if (!IS_ERR(clk)) + clk_enable(clk); + else + rc = -1; + + return rc; +} + +int msm_camio_clk_disable(enum msm_camio_clk_type clktype) +{ + int rc = 0; + struct clk *clk = NULL; + + switch (clktype) { + case CAMIO_VFE_MDC_CLK: + clk = camio_vfe_mdc_clk; + break; + + case CAMIO_MDC_CLK: + clk = camio_mdc_clk; + break; + + case CAMIO_VFE_CLK: + clk = camio_vfe_clk; + break; + + case CAMIO_VFE_AXI_CLK: + clk = camio_vfe_axi_clk; + break; + + default: + break; + } + + if (!IS_ERR(clk)) { + clk_disable(clk); + clk_put(clk); + } else + rc = -1; + + return rc; +} + +void msm_camio_clk_rate_set(int rate) +{ + struct clk *clk = camio_vfe_mdc_clk; + + /* TODO: check return */ + clk_set_rate(clk, rate); +} + +int msm_camio_enable(struct platform_device *pdev) +{ + int rc = 0; + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + + camio_ext = camdev->ioext; + + appio = request_mem_region(camio_ext.appphy, + camio_ext.appsz, pdev->name); + if (!appio) { + rc = -EBUSY; + goto enable_fail; + } + + appbase = ioremap(camio_ext.appphy, + camio_ext.appsz); + if (!appbase) { + rc = -ENOMEM; + goto apps_no_mem; + } + + mdcio = request_mem_region(camio_ext.mdcphy, + camio_ext.mdcsz, pdev->name); + if (!mdcio) { + rc = -EBUSY; + goto mdc_busy; + } + + mdcbase = ioremap(camio_ext.mdcphy, + camio_ext.mdcsz); + if (!mdcbase) { + rc = -ENOMEM; + goto mdc_no_mem; + } + + camdev->camera_gpio_on(); + + msm_camio_clk_enable(CAMIO_VFE_CLK); + msm_camio_clk_enable(CAMIO_MDC_CLK); + msm_camio_clk_enable(CAMIO_VFE_MDC_CLK); + msm_camio_clk_enable(CAMIO_VFE_AXI_CLK); + return 0; + +mdc_no_mem: + release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz); +mdc_busy: + iounmap(appbase); +apps_no_mem: + release_mem_region(camio_ext.appphy, camio_ext.appsz); +enable_fail: + return rc; +} + +void msm_camio_disable(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + + iounmap(mdcbase); + release_mem_region(camio_ext.mdcphy, camio_ext.mdcsz); + iounmap(appbase); + release_mem_region(camio_ext.appphy, camio_ext.appsz); + + camdev->camera_gpio_off(); + + msm_camio_clk_disable(CAMIO_VFE_MDC_CLK); + msm_camio_clk_disable(CAMIO_MDC_CLK); + msm_camio_clk_disable(CAMIO_VFE_CLK); + msm_camio_clk_disable(CAMIO_VFE_AXI_CLK); +} + +void msm_camio_camif_pad_reg_reset(void) +{ + uint32_t reg; + uint32_t mask, value; + + /* select CLKRGM_VFE_SRC_CAM_VFE_SRC: internal source */ + msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_INTERNAL); + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + + mask = CAM_SEL_BMSK | + CAM_PCLK_SRC_SEL_BMSK | + CAM_PCLK_INVERT_BMSK | + EXT_CAM_HSYNC_POL_SEL_BMSK | + EXT_CAM_VSYNC_POL_SEL_BMSK | + MDDI_CLK_CHICKEN_BIT_BMSK; + + value = 1 << CAM_SEL_SHFT | + 3 << CAM_PCLK_SRC_SEL_SHFT | + 0 << CAM_PCLK_INVERT_SHFT | + 0 << EXT_CAM_HSYNC_POL_SEL_SHFT | + 0 << EXT_CAM_VSYNC_POL_SEL_SHFT | + 0 << MDDI_CLK_CHICKEN_BIT_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + mask = CAM_PAD_REG_SW_RESET_BMSK; + value = 1 << CAM_PAD_REG_SW_RESET_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + mask = CAM_PAD_REG_SW_RESET_BMSK; + value = 0 << CAM_PAD_REG_SW_RESET_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); + + msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_EXTERNAL); + + mdelay(10); + + /* todo: check return */ + if (camio_vfe_clk) + clk_set_rate(camio_vfe_clk, 96000000); +} + +void msm_camio_vfe_blk_reset(void) +{ + uint32_t val; + + val = readl(appbase + 0x00000210); + val |= 0x1; + writel(val, appbase + 0x00000210); + mdelay(10); + + val = readl(appbase + 0x00000210); + val &= ~0x1; + writel(val, appbase + 0x00000210); + mdelay(10); +} + +void msm_camio_camif_pad_reg_reset_2(void) +{ + uint32_t reg; + uint32_t mask, value; + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + mask = CAM_PAD_REG_SW_RESET_BMSK; + value = 1 << CAM_PAD_REG_SW_RESET_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); + + reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; + mask = CAM_PAD_REG_SW_RESET_BMSK; + value = 0 << CAM_PAD_REG_SW_RESET_SHFT; + writel((reg & (~mask)) | (value & mask), mdcbase); + mdelay(10); +} + +void msm_camio_clk_sel(enum msm_camio_clk_src_type srctype) +{ + struct clk *clk = NULL; + + clk = camio_vfe_clk; + + if (clk != NULL) { + switch (srctype) { + case MSM_CAMIO_CLK_SRC_INTERNAL: + clk_set_flags(clk, 0x00000100 << 1); + break; + + case MSM_CAMIO_CLK_SRC_EXTERNAL: + clk_set_flags(clk, 0x00000100); + break; + + default: + break; + } + } +} + +void msm_camio_clk_axi_rate_set(int rate) +{ + struct clk *clk = camio_vfe_axi_clk; + /* todo: check return */ + clk_set_rate(clk, rate); +} + +int msm_camio_probe_on(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + + camdev->camera_gpio_on(); + return msm_camio_clk_enable(CAMIO_VFE_MDC_CLK); +} + +int msm_camio_probe_off(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + + camdev->camera_gpio_off(); + return msm_camio_clk_disable(CAMIO_VFE_MDC_CLK); +} diff --git a/drivers/media/video/msm/msm_v4l2.c b/drivers/media/video/msm/msm_v4l2.c new file mode 100644 index 0000000000000..744c92b360b7b --- /dev/null +++ b/drivers/media/video/msm/msm_v4l2.c @@ -0,0 +1,793 @@ +/* + * + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/*#include */ + +#define MSM_V4L2_START_SNAPSHOT _IOWR('V', BASE_VIDIOC_PRIVATE+1, \ + struct v4l2_buffer) + +#define MSM_V4L2_GET_PICTURE _IOWR('V', BASE_VIDIOC_PRIVATE+2, \ + struct v4l2_buffer) + +#define MSM_V4L2_DEVICE_NAME "msm_v4l2" + +#define MSM_V4L2_PROC_NAME "msm_v4l2" + +#define MSM_V4L2_DEVNUM_MPEG2 0 +#define MSM_V4L2_DEVNUM_YUV 20 + +/* HVGA-P (portrait) and HVGA-L (landscape) */ +#define MSM_V4L2_WIDTH 480 +#define MSM_V4L2_HEIGHT 320 + +#if 1 +#define D(fmt, args...) printk(KERN_INFO "msm_v4l2: " fmt, ##args) +#else +#define D(fmt, args...) do {} while (0) +#endif + +#define PREVIEW_FRAMES_NUM 4 + +struct msm_v4l2_device { + struct list_head read_queue; + struct v4l2_format current_cap_format; + struct v4l2_format current_pix_format; + struct video_device *pvdev; + struct msm_v4l2_driver *drv; + uint8_t opencnt; + + spinlock_t read_queue_lock; +}; + +static struct msm_v4l2_device *g_pmsm_v4l2_dev; + + +static DEFINE_MUTEX(msm_v4l2_opencnt_lock); + +static int msm_v4l2_open(struct file *f) +{ + int rc = 0; + D("%s\n", __func__); + mutex_lock(&msm_v4l2_opencnt_lock); + if (!g_pmsm_v4l2_dev->opencnt) { + rc = g_pmsm_v4l2_dev->drv->open( + g_pmsm_v4l2_dev->drv->sync, + MSM_APPS_ID_V4L2); + } + g_pmsm_v4l2_dev->opencnt++; + mutex_unlock(&msm_v4l2_opencnt_lock); + return rc; +} + +static int msm_v4l2_release(struct file *f) +{ + int rc = 0; + D("%s\n", __func__); + mutex_lock(&msm_v4l2_opencnt_lock); + if (!g_pmsm_v4l2_dev->opencnt) { + g_pmsm_v4l2_dev->opencnt--; + if (!g_pmsm_v4l2_dev->opencnt) { + rc = g_pmsm_v4l2_dev->drv->release( + g_pmsm_v4l2_dev->drv->sync); + } + } + mutex_unlock(&msm_v4l2_opencnt_lock); + return rc; +} + +static unsigned int msm_v4l2_poll(struct file *f, struct poll_table_struct *w) +{ + return g_pmsm_v4l2_dev->drv->drv_poll(g_pmsm_v4l2_dev->drv->sync, f, w); +} + +static long msm_v4l2_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + struct msm_ctrl_cmd *ctrlcmd; + + D("msm_v4l2_ioctl, cmd = %d, %d\n", cmd, __LINE__); + + switch (cmd) { + case MSM_V4L2_START_SNAPSHOT: + + ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC); + if (!ctrlcmd) { + CDBG("msm_v4l2_ioctl: cannot allocate buffer\n"); + return -ENOMEM; + } + + ctrlcmd->length = 0; + ctrlcmd->value = NULL; + ctrlcmd->timeout_ms = 10000; + + D("msm_v4l2_ioctl, MSM_V4L2_START_SNAPSHOT v4l2 ioctl %d\n", + cmd); + ctrlcmd->type = MSM_V4L2_SNAPSHOT; + return g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, + ctrlcmd); + + case MSM_V4L2_GET_PICTURE: + D("msm_v4l2_ioctl, MSM_V4L2_GET_PICTURE v4l2 ioctl %d\n", cmd); + ctrlcmd = (struct msm_ctrl_cmd *)arg; + return g_pmsm_v4l2_dev->drv->get_pict( + g_pmsm_v4l2_dev->drv->sync, ctrlcmd); + + default: + D("msm_v4l2_ioctl, standard v4l2 ioctl %d\n", cmd); + return video_ioctl2(filep, cmd, arg); + } +} + +static void msm_v4l2_release_dev(struct video_device *d) +{ + D("%s\n", __func__); +} + +static int msm_v4l2_querycap(struct file *f, + void *pctx, struct v4l2_capability *pcaps) +{ + D("%s\n", __func__); + strncpy(pcaps->driver, MSM_APPS_ID_V4L2, sizeof(pcaps->driver)); + strncpy(pcaps->card, + MSM_V4L2_DEVICE_NAME, sizeof(pcaps->card)); + pcaps->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + return 0; +} + +static int msm_v4l2_s_std(struct file *f, void *pctx, v4l2_std_id *pnorm) +{ + D("%s\n", __func__); + return 0; +} + +static int msm_v4l2_queryctrl(struct file *f, + void *pctx, struct v4l2_queryctrl *pqctrl) +{ + int rc = 0; + struct msm_ctrl_cmd *ctrlcmd; + + D("%s\n", __func__); + + ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC); + if (!ctrlcmd) { + CDBG("msm_v4l2_queryctrl: cannot allocate buffer\n"); + return -ENOMEM; + } + + ctrlcmd->type = MSM_V4L2_QUERY_CTRL; + ctrlcmd->length = sizeof(struct v4l2_queryctrl); + ctrlcmd->value = pqctrl; + ctrlcmd->timeout_ms = 10000; + + rc = g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd); + if (rc < 0) + return -1; + + return ctrlcmd->status; +} + +static int msm_v4l2_g_ctrl(struct file *f, void *pctx, struct v4l2_control *c) +{ + int rc = 0; + struct msm_ctrl_cmd *ctrlcmd; + + D("%s\n", __func__); + + ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC); + if (!ctrlcmd) { + CDBG("msm_v4l2_g_ctrl: cannot allocate buffer\n"); + return -ENOMEM; + } + + ctrlcmd->type = MSM_V4L2_GET_CTRL; + ctrlcmd->length = sizeof(struct v4l2_control); + ctrlcmd->value = c; + ctrlcmd->timeout_ms = 10000; + + rc = g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd); + if (rc < 0) + return -1; + + return ctrlcmd->status; +} + +static int msm_v4l2_s_ctrl(struct file *f, void *pctx, struct v4l2_control *c) +{ + int rc = 0; + struct msm_ctrl_cmd *ctrlcmd; + + ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC); + if (!ctrlcmd) { + CDBG("msm_v4l2_s_ctrl: cannot allocate buffer\n"); + return -ENOMEM; + } + + ctrlcmd->type = MSM_V4L2_SET_CTRL; + ctrlcmd->length = sizeof(struct v4l2_control); + ctrlcmd->value = c; + ctrlcmd->timeout_ms = 10000; + + D("%s\n", __func__); + + rc = g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd); + if (rc < 0) + return -1; + + return ctrlcmd->status; +} + +static int msm_v4l2_reqbufs(struct file *f, + void *pctx, struct v4l2_requestbuffers *b) +{ + D("%s\n", __func__); + return 0; +} + +static int msm_v4l2_querybuf(struct file *f, void *pctx, struct v4l2_buffer *pb) +{ + struct msm_pmem_info pmem_buf; +#if 0 + __u32 width = 0; + __u32 height = 0; + __u32 y_size = 0; + __u32 y_pad = 0; + + /* FIXME: g_pmsm_v4l2_dev->current_pix_format.fmt.pix.width; */ + width = 640; + /* FIXME: g_pmsm_v4l2_dev->current_pix_format.fmt.pix.height; */ + height = 480; + + D("%s: width = %d, height = %d\n", __func__, width, height); + + y_size = width * height; + y_pad = y_size % 4; +#endif + + __u32 y_pad = pb->bytesused % 4; + + /* V4L2 videodev will do the copy_from_user. */ + + memset(&pmem_buf, 0, sizeof(struct msm_pmem_info)); + pmem_buf.type = MSM_PMEM_OUTPUT2; + pmem_buf.vaddr = (void *)pb->m.userptr; + pmem_buf.y_off = 0; + pmem_buf.fd = (int)pb->reserved; + /* pmem_buf.cbcr_off = (y_size + y_pad); */ + pmem_buf.cbcr_off = (pb->bytesused + y_pad); + + g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync, &pmem_buf); + + return 0; +} + +static int msm_v4l2_qbuf(struct file *f, void *pctx, struct v4l2_buffer *pb) +{ + /* + __u32 y_size = 0; + __u32 y_pad = 0; + __u32 width = 0; + __u32 height = 0; + */ + + __u32 y_pad = 0; + + struct msm_pmem_info meminfo; + struct msm_frame frame; + static int cnt; + + if ((pb->flags >> 16) & 0x0001) { + /* this is for previwe */ +#if 0 + width = 640; + height = 480; + + /* V4L2 videodev will do the copy_from_user. */ + D("%s: width = %d, height = %d\n", __func__, width, height); + y_size = width * height; + y_pad = y_size % 4; +#endif + + y_pad = pb->bytesused % 4; + + if (pb->type == V4L2_BUF_TYPE_PRIVATE) { + /* this qbuf is actually for releasing */ + + frame.buffer = pb->m.userptr; + frame.y_off = 0; + /* frame.cbcr_off = (y_size + y_pad); */ + frame.cbcr_off = (pb->bytesused + y_pad); + frame.fd = pb->reserved; + + D("V4L2_BUF_TYPE_PRIVATE: pb->bytesused = %d \n", + pb->bytesused); + + g_pmsm_v4l2_dev->drv->put_frame( + g_pmsm_v4l2_dev->drv->sync, + &frame); + + return 0; + } + + D("V4L2_BUF_TYPE_VIDEO_CAPTURE: pb->bytesused = %d \n", + pb->bytesused); + + meminfo.type = MSM_PMEM_OUTPUT2; + meminfo.fd = (int)pb->reserved; + meminfo.vaddr = (void *)pb->m.userptr; + meminfo.y_off = 0; + /* meminfo.cbcr_off = (y_size + y_pad); */ + meminfo.cbcr_off = (pb->bytesused + y_pad); + if (cnt == PREVIEW_FRAMES_NUM - 1) + meminfo.active = 0; + else + meminfo.active = 1; + cnt++; + g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync, + &meminfo); + } else if ((pb->flags) & 0x0001) { + /* this is for snapshot */ + + __u32 y_size = 0; + + if ((pb->flags >> 8) & 0x01) { + + y_size = pb->bytesused; + + meminfo.type = MSM_PMEM_THUMBNAIL; + } else if ((pb->flags >> 9) & 0x01) { + + y_size = pb->bytesused; + + meminfo.type = MSM_PMEM_MAINIMG; + } + + y_pad = y_size % 4; + + meminfo.fd = (int)pb->reserved; + meminfo.vaddr = (void *)pb->m.userptr; + meminfo.y_off = 0; + /* meminfo.cbcr_off = (y_size + y_pad); */ + meminfo.cbcr_off = (y_size + y_pad); + meminfo.active = 1; + g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync, + &meminfo); + } + + return 0; +} + +static int msm_v4l2_dqbuf(struct file *f, void *pctx, struct v4l2_buffer *pb) +{ + struct msm_frame frame; + D("%s\n", __func__); + + /* V4L2 videodev will do the copy_to_user. */ + if (pb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + + D("%s, %d\n", __func__, __LINE__); + + g_pmsm_v4l2_dev->drv->get_frame( + g_pmsm_v4l2_dev->drv->sync, + &frame); + + pb->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + pb->m.userptr = (unsigned long)frame.buffer; /* FIXME */ + pb->reserved = (int)frame.fd; + /* pb->length = (int)frame.cbcr_off; */ + + pb->bytesused = frame.cbcr_off; + + } else if (pb->type == V4L2_BUF_TYPE_PRIVATE) { + __u32 y_pad = pb->bytesused % 4; + + frame.buffer = pb->m.userptr; + frame.y_off = 0; + /* frame.cbcr_off = (y_size + y_pad); */ + frame.cbcr_off = (pb->bytesused + y_pad); + frame.fd = pb->reserved; + + g_pmsm_v4l2_dev->drv->put_frame( + g_pmsm_v4l2_dev->drv->sync, + &frame); + } + + return 0; +} + +static int msm_v4l2_streamon(struct file *f, void *pctx, enum v4l2_buf_type i) +{ + struct msm_ctrl_cmd *ctrlcmd; + + ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC); + if (!ctrlcmd) { + CDBG("msm_v4l2_s_fmt_cap: cannot allocate buffer\n"); + return -ENOMEM; + } + + ctrlcmd->type = MSM_V4L2_STREAM_ON; + ctrlcmd->timeout_ms = 10000; + ctrlcmd->length = 0; + ctrlcmd->value = NULL; + + D("%s\n", __func__); + + g_pmsm_v4l2_dev->drv->ctrl( + g_pmsm_v4l2_dev->drv->sync, + ctrlcmd); + + D("%s after drv->ctrl \n", __func__); + + return 0; +} + +static int msm_v4l2_streamoff(struct file *f, void *pctx, enum v4l2_buf_type i) +{ + struct msm_ctrl_cmd *ctrlcmd; + + ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC); + if (!ctrlcmd) { + CDBG("msm_v4l2_s_fmt_cap: cannot allocate buffer\n"); + return -ENOMEM; + } + + ctrlcmd->type = MSM_V4L2_STREAM_OFF; + ctrlcmd->timeout_ms = 10000; + ctrlcmd->length = 0; + ctrlcmd->value = NULL; + + + D("%s\n", __func__); + + g_pmsm_v4l2_dev->drv->ctrl( + g_pmsm_v4l2_dev->drv->sync, + ctrlcmd); + + return 0; +} + +static int msm_v4l2_enum_fmt_overlay(struct file *f, + void *pctx, struct v4l2_fmtdesc *pfmtdesc) +{ + D("%s\n", __func__); + return 0; +} + +static int msm_v4l2_enum_fmt_cap(struct file *f, + void *pctx, struct v4l2_fmtdesc *pfmtdesc) +{ + D("%s\n", __func__); + + switch (pfmtdesc->index) { + case 0: + pfmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + pfmtdesc->flags = 0; + strncpy(pfmtdesc->description, "YUV 4:2:0", + sizeof(pfmtdesc->description)); + pfmtdesc->pixelformat = V4L2_PIX_FMT_YVU420; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int msm_v4l2_g_fmt_cap(struct file *f, + void *pctx, struct v4l2_format *pfmt) +{ + D("%s\n", __func__); + pfmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + pfmt->fmt.pix.width = MSM_V4L2_WIDTH; + pfmt->fmt.pix.height = MSM_V4L2_HEIGHT; + pfmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YVU420; + pfmt->fmt.pix.field = V4L2_FIELD_ANY; + pfmt->fmt.pix.bytesperline = 0; + pfmt->fmt.pix.sizeimage = 0; + pfmt->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + pfmt->fmt.pix.priv = 0; + return 0; +} + +static int msm_v4l2_s_fmt_cap(struct file *f, + void *pctx, struct v4l2_format *pfmt) +{ + struct msm_ctrl_cmd *ctrlcmd; + + D("%s\n", __func__); + + ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC); + if (!ctrlcmd) { + CDBG("msm_v4l2_s_fmt_cap: cannot allocate buffer\n"); + return -ENOMEM; + } + + ctrlcmd->type = MSM_V4L2_VID_CAP_TYPE; + ctrlcmd->length = sizeof(struct v4l2_format); + ctrlcmd->value = pfmt; + ctrlcmd->timeout_ms = 10000; + + if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -1; + +#if 0 + /* FIXEME */ + if (pfmt->fmt.pix.pixelformat != V4L2_PIX_FMT_YVU420) + return -EINVAL; +#endif + + /* Ok, but check other params, too. */ + +#if 0 + memcpy(&g_pmsm_v4l2_dev->current_pix_format.fmt.pix, pfmt, + sizeof(struct v4l2_format)); +#endif + + g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd); + + return 0; +} + +static int msm_v4l2_g_fmt_overlay(struct file *f, + void *pctx, struct v4l2_format *pfmt) +{ + D("%s\n", __func__); + pfmt->type = V4L2_BUF_TYPE_VIDEO_OVERLAY; + pfmt->fmt.pix.width = MSM_V4L2_WIDTH; + pfmt->fmt.pix.height = MSM_V4L2_HEIGHT; + pfmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YVU420; + pfmt->fmt.pix.field = V4L2_FIELD_ANY; + pfmt->fmt.pix.bytesperline = 0; + pfmt->fmt.pix.sizeimage = 0; + pfmt->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + pfmt->fmt.pix.priv = 0; + return 0; +} + +static int msm_v4l2_s_fmt_overlay(struct file *f, + void *pctx, struct v4l2_format *pfmt) +{ + D("%s\n", __func__); + return 0; +} + +static int msm_v4l2_overlay(struct file *f, void *pctx, unsigned int i) +{ + D("%s\n", __func__); + return 0; +} + +static int msm_v4l2_g_jpegcomp(struct file *f, + void *pctx, struct v4l2_jpegcompression *pcomp) +{ + D("%s\n", __func__); + return 0; +} + +static int msm_v4l2_s_jpegcomp(struct file *f, + void *pctx, struct v4l2_jpegcompression *pcomp) +{ + D("%s\n", __func__); + return 0; +} + +#ifdef CONFIG_PROC_FS +int msm_v4l2_read_proc(char *pbuf, char **start, off_t offset, + int count, int *eof, void *data) +{ + int len = 0; + len += snprintf(pbuf, strlen("stats\n") + 1, "stats\n"); + + if (g_pmsm_v4l2_dev) { + len += snprintf(pbuf, strlen("mode: ") + 1, "mode: "); + + if (g_pmsm_v4l2_dev->current_cap_format.type + == V4L2_BUF_TYPE_VIDEO_CAPTURE) + len += snprintf(pbuf, strlen("capture\n") + 1, + "capture\n"); + else + len += snprintf(pbuf, strlen("unknown\n") + 1, + "unknown\n"); + + len += snprintf(pbuf, 21, "resolution: %dx%d\n", + g_pmsm_v4l2_dev->current_cap_format.fmt.pix. + width, + g_pmsm_v4l2_dev->current_cap_format.fmt.pix. + height); + + len += snprintf(pbuf, + strlen("pixel format: ") + 1, "pixel format: "); + if (g_pmsm_v4l2_dev->current_cap_format.fmt.pix.pixelformat + == V4L2_PIX_FMT_YVU420) + len += snprintf(pbuf, strlen("yvu420\n") + 1, + "yvu420\n"); + else + len += snprintf(pbuf, strlen("unknown\n") + 1, + "unknown\n"); + + len += snprintf(pbuf, strlen("colorspace: ") + 1, + "colorspace: "); + if (g_pmsm_v4l2_dev->current_cap_format.fmt.pix.colorspace + == V4L2_COLORSPACE_JPEG) + len += snprintf(pbuf, strlen("jpeg\n") + 1, "jpeg\n"); + else + len += snprintf(pbuf, strlen("unknown\n") + 1, + "unknown\n"); + } + + *eof = 1; + return len; +} +#endif + +static const struct v4l2_file_operations msm_v4l2_fops = { + .owner = THIS_MODULE, + .open = msm_v4l2_open, + .poll = msm_v4l2_poll, + .release = msm_v4l2_release, + .ioctl = msm_v4l2_ioctl, +}; + +static void msm_v4l2_dev_init(struct msm_v4l2_device *pmsm_v4l2_dev) +{ + pmsm_v4l2_dev->read_queue_lock = + __SPIN_LOCK_UNLOCKED(pmsm_v4l2_dev->read_queue_lock); + INIT_LIST_HEAD(&pmsm_v4l2_dev->read_queue); +} + +static int msm_v4l2_try_fmt_cap(struct file *file, + void *fh, struct v4l2_format *f) +{ + /* FIXME */ + return 0; +} + +static int mm_v4l2_try_fmt_type_private(struct file *file, + void *fh, struct v4l2_format *f) +{ + /* FIXME */ + return 0; +} + +/* + * should the following structure be used instead of the code in the function? + * static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = { + * .vidioc_querycap = .... + * } + */ +static const struct v4l2_ioctl_ops msm_ioctl_ops = { + .vidioc_querycap = msm_v4l2_querycap, + .vidioc_s_std = msm_v4l2_s_std, + + .vidioc_queryctrl = msm_v4l2_queryctrl, + .vidioc_g_ctrl = msm_v4l2_g_ctrl, + .vidioc_s_ctrl = msm_v4l2_s_ctrl, + + .vidioc_reqbufs = msm_v4l2_reqbufs, + .vidioc_querybuf = msm_v4l2_querybuf, + .vidioc_qbuf = msm_v4l2_qbuf, + .vidioc_dqbuf = msm_v4l2_dqbuf, + + .vidioc_streamon = msm_v4l2_streamon, + .vidioc_streamoff = msm_v4l2_streamoff, + + .vidioc_enum_fmt_vid_overlay = msm_v4l2_enum_fmt_overlay, + .vidioc_enum_fmt_vid_cap = msm_v4l2_enum_fmt_cap, + + .vidioc_try_fmt_vid_cap = msm_v4l2_try_fmt_cap, + .vidioc_try_fmt_type_private = mm_v4l2_try_fmt_type_private, + + .vidioc_g_fmt_vid_cap = msm_v4l2_g_fmt_cap, + .vidioc_s_fmt_vid_cap = msm_v4l2_s_fmt_cap, + .vidioc_g_fmt_vid_overlay = msm_v4l2_g_fmt_overlay, + .vidioc_s_fmt_vid_overlay = msm_v4l2_s_fmt_overlay, + .vidioc_overlay = msm_v4l2_overlay, + + .vidioc_g_jpegcomp = msm_v4l2_g_jpegcomp, + .vidioc_s_jpegcomp = msm_v4l2_s_jpegcomp, +}; + +static int msm_v4l2_video_dev_init(struct video_device *pvd) +{ + strncpy(pvd->name, MSM_APPS_ID_V4L2, sizeof(pvd->name)); + pvd->vfl_type = VID_TYPE_CAPTURE; + pvd->fops = &msm_v4l2_fops; + pvd->release = msm_v4l2_release_dev; + pvd->minor = -1; + pvd->ioctl_ops = &msm_ioctl_ops; + return msm_v4l2_register(g_pmsm_v4l2_dev->drv); +} + +static int __init msm_v4l2_init(void) +{ + int rc = -ENOMEM; + struct video_device *pvdev = NULL; + struct msm_v4l2_device *pmsm_v4l2_dev = NULL; + D("%s\n", __func__); + + pvdev = video_device_alloc(); + if (pvdev == NULL) + return rc; + + pmsm_v4l2_dev = + kzalloc(sizeof(struct msm_v4l2_device), GFP_KERNEL); + if (pmsm_v4l2_dev == NULL) { + video_device_release(pvdev); + return rc; + } + + msm_v4l2_dev_init(pmsm_v4l2_dev); + + g_pmsm_v4l2_dev = pmsm_v4l2_dev; + g_pmsm_v4l2_dev->pvdev = pvdev; + + g_pmsm_v4l2_dev->drv = + kzalloc(sizeof(struct msm_v4l2_driver), GFP_KERNEL); + if (!g_pmsm_v4l2_dev->drv) { + video_device_release(pvdev); + kfree(pmsm_v4l2_dev); + return rc; + } + + rc = msm_v4l2_video_dev_init(pvdev); + if (rc < 0) { + video_device_release(pvdev); + kfree(g_pmsm_v4l2_dev->drv); + kfree(pmsm_v4l2_dev); + return rc; + } + + if (video_register_device(pvdev, VFL_TYPE_GRABBER, + MSM_V4L2_DEVNUM_YUV)) { + D("failed to register device\n"); + video_device_release(pvdev); + kfree(g_pmsm_v4l2_dev); + g_pmsm_v4l2_dev = NULL; + return -ENOENT; + } +#ifdef CONFIG_PROC_FS + create_proc_read_entry(MSM_V4L2_PROC_NAME, + 0, NULL, msm_v4l2_read_proc, NULL); +#endif + + return 0; +} + +static void __exit msm_v4l2_exit(void) +{ + struct video_device *pvdev = g_pmsm_v4l2_dev->pvdev; + D("%s\n", __func__); +#ifdef CONFIG_PROC_FS + remove_proc_entry(MSM_V4L2_PROC_NAME, NULL); +#endif + video_unregister_device(pvdev); + video_device_release(pvdev); + + msm_v4l2_unregister(g_pmsm_v4l2_dev->drv); + + kfree(g_pmsm_v4l2_dev->drv); + g_pmsm_v4l2_dev->drv = NULL; + + kfree(g_pmsm_v4l2_dev); + g_pmsm_v4l2_dev = NULL; +} + +module_init(msm_v4l2_init); +module_exit(msm_v4l2_exit); + +MODULE_DESCRIPTION("MSM V4L2 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/msm/msm_vfe7x.c b/drivers/media/video/msm/msm_vfe7x.c new file mode 100644 index 0000000000000..ee282339b33cd --- /dev/null +++ b/drivers/media/video/msm/msm_vfe7x.c @@ -0,0 +1,702 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm_vfe7x.h" + +#define QDSP_CMDQUEUE QDSP_vfeCommandQueue + +#define VFE_RESET_CMD 0 +#define VFE_START_CMD 1 +#define VFE_STOP_CMD 2 +#define VFE_FRAME_ACK 20 +#define STATS_AF_ACK 21 +#define STATS_WE_ACK 22 + +#define MSG_STOP_ACK 1 +#define MSG_SNAPSHOT 2 +#define MSG_OUTPUT1 6 +#define MSG_OUTPUT2 7 +#define MSG_STATS_AF 8 +#define MSG_STATS_WE 9 + +static struct msm_adsp_module *qcam_mod; +static struct msm_adsp_module *vfe_mod; +static struct msm_vfe_callback *resp; +static void *extdata; +static uint32_t extlen; + +struct mutex vfe_lock; +static void *vfe_syncdata; +static uint8_t vfestopped; + +static struct stop_event stopevent; + +static void vfe_7x_convert(struct msm_vfe_phy_info *pinfo, + enum vfe_resp_msg type, + void *data, void **ext, int32_t *elen) +{ + switch (type) { + case VFE_MSG_OUTPUT1: + case VFE_MSG_OUTPUT2: { + pinfo->y_phy = ((struct vfe_endframe *)data)->y_address; + pinfo->cbcr_phy = + ((struct vfe_endframe *)data)->cbcr_address; + + CDBG("vfe_7x_convert, y_phy = 0x%x, cbcr_phy = 0x%x\n", + pinfo->y_phy, pinfo->cbcr_phy); + + ((struct vfe_frame_extra *)extdata)->bl_evencol = + ((struct vfe_endframe *)data)->blacklevelevencolumn; + + ((struct vfe_frame_extra *)extdata)->bl_oddcol = + ((struct vfe_endframe *)data)->blackleveloddcolumn; + + ((struct vfe_frame_extra *)extdata)->g_def_p_cnt = + ((struct vfe_endframe *)data)->greendefectpixelcount; + + ((struct vfe_frame_extra *)extdata)->r_b_def_p_cnt = + ((struct vfe_endframe *)data)->redbluedefectpixelcount; + + *ext = extdata; + *elen = extlen; + } + break; + + case VFE_MSG_STATS_AF: + case VFE_MSG_STATS_WE: + pinfo->sbuf_phy = *(uint32_t *)data; + break; + + default: + break; + } /* switch */ +} + +static void vfe_7x_ops(void *driver_data, unsigned id, size_t len, + void (*getevent)(void *ptr, size_t len)) +{ + uint32_t evt_buf[3]; + struct msm_vfe_resp *rp; + void *data; + + len = (id == (uint16_t)-1) ? 0 : len; + data = resp->vfe_alloc(sizeof(struct msm_vfe_resp) + len, vfe_syncdata); + + if (!data) { + pr_err("rp: cannot allocate buffer\n"); + return; + } + rp = (struct msm_vfe_resp *)data; + rp->evt_msg.len = len; + + if (id == ((uint16_t)-1)) { + /* event */ + rp->type = VFE_EVENT; + rp->evt_msg.type = MSM_CAMERA_EVT; + getevent(evt_buf, sizeof(evt_buf)); + rp->evt_msg.msg_id = evt_buf[0]; + resp->vfe_resp(rp, MSM_CAM_Q_VFE_EVT, vfe_syncdata); + } else { + /* messages */ + rp->evt_msg.type = MSM_CAMERA_MSG; + rp->evt_msg.msg_id = id; + rp->evt_msg.data = rp + 1; + getevent(rp->evt_msg.data, len); + + switch (rp->evt_msg.msg_id) { + case MSG_SNAPSHOT: + rp->type = VFE_MSG_SNAPSHOT; + break; + + case MSG_OUTPUT1: + rp->type = VFE_MSG_OUTPUT1; + vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT1, + rp->evt_msg.data, &(rp->extdata), + &(rp->extlen)); + break; + + case MSG_OUTPUT2: + rp->type = VFE_MSG_OUTPUT2; + vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT2, + rp->evt_msg.data, &(rp->extdata), + &(rp->extlen)); + break; + + case MSG_STATS_AF: + rp->type = VFE_MSG_STATS_AF; + vfe_7x_convert(&(rp->phy), VFE_MSG_STATS_AF, + rp->evt_msg.data, NULL, NULL); + break; + + case MSG_STATS_WE: + rp->type = VFE_MSG_STATS_WE; + vfe_7x_convert(&(rp->phy), VFE_MSG_STATS_WE, + rp->evt_msg.data, NULL, NULL); + + CDBG("MSG_STATS_WE: phy = 0x%x\n", rp->phy.sbuf_phy); + break; + + case MSG_STOP_ACK: + rp->type = VFE_MSG_GENERAL; + stopevent.state = 1; + wake_up(&stopevent.wait); + break; + + + default: + rp->type = VFE_MSG_GENERAL; + break; + } + resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, vfe_syncdata); + } +} + +static struct msm_adsp_ops vfe_7x_sync = { + .event = vfe_7x_ops, +}; + +static int vfe_7x_enable(struct camera_enable_cmd *enable) +{ + int rc = -EFAULT; + + if (!strcmp(enable->name, "QCAMTASK")) + rc = msm_adsp_enable(qcam_mod); + else if (!strcmp(enable->name, "VFETASK")) + rc = msm_adsp_enable(vfe_mod); + + return rc; +} + +static int vfe_7x_disable(struct camera_enable_cmd *enable, + struct platform_device *dev __attribute__((unused))) +{ + int rc = -EFAULT; + + if (!strcmp(enable->name, "QCAMTASK")) + rc = msm_adsp_disable(qcam_mod); + else if (!strcmp(enable->name, "VFETASK")) + rc = msm_adsp_disable(vfe_mod); + + return rc; +} + +static int vfe_7x_stop(void) +{ + int rc = 0; + uint32_t stopcmd = VFE_STOP_CMD; + rc = msm_adsp_write(vfe_mod, QDSP_CMDQUEUE, + &stopcmd, sizeof(uint32_t)); + if (rc < 0) { + CDBG("%s:%d: failed rc = %d \n", __func__, __LINE__, rc); + return rc; + } + + stopevent.state = 0; + rc = wait_event_timeout(stopevent.wait, + stopevent.state != 0, + msecs_to_jiffies(stopevent.timeout)); + + return rc; +} + +static void vfe_7x_release(struct platform_device *pdev) +{ + mutex_lock(&vfe_lock); + vfe_syncdata = NULL; + mutex_unlock(&vfe_lock); + + if (!vfestopped) { + CDBG("%s:%d:Calling vfe_7x_stop()\n", __func__, __LINE__); + vfe_7x_stop(); + } else + vfestopped = 0; + + msm_adsp_disable(qcam_mod); + msm_adsp_disable(vfe_mod); + + msm_adsp_put(qcam_mod); + msm_adsp_put(vfe_mod); + + msm_camio_disable(pdev); + + kfree(extdata); + extlen = 0; +} + +static int vfe_7x_init(struct msm_vfe_callback *presp, + struct platform_device *dev) +{ + int rc = 0; + + init_waitqueue_head(&stopevent.wait); + stopevent.timeout = 200; + stopevent.state = 0; + + if (presp && presp->vfe_resp) + resp = presp; + else + return -EFAULT; + + /* Bring up all the required GPIOs and Clocks */ + rc = msm_camio_enable(dev); + if (rc < 0) + return rc; + + msm_camio_camif_pad_reg_reset(); + + extlen = sizeof(struct vfe_frame_extra); + + extdata = + kmalloc(sizeof(extlen), GFP_ATOMIC); + if (!extdata) { + rc = -ENOMEM; + goto init_fail; + } + + rc = msm_adsp_get("QCAMTASK", &qcam_mod, &vfe_7x_sync, NULL); + if (rc) { + rc = -EBUSY; + goto get_qcam_fail; + } + + rc = msm_adsp_get("VFETASK", &vfe_mod, &vfe_7x_sync, NULL); + if (rc) { + rc = -EBUSY; + goto get_vfe_fail; + } + + return 0; + +get_vfe_fail: + msm_adsp_put(qcam_mod); +get_qcam_fail: + kfree(extdata); +init_fail: + extlen = 0; + return rc; +} + +static int vfe_7x_config_axi(int mode, + struct axidata *ad, struct axiout *ao) +{ + struct msm_pmem_region *regptr; + unsigned long *bptr; + int cnt; + + int rc = 0; + + if (mode == OUTPUT_1 || mode == OUTPUT_1_AND_2) { + regptr = ad->region; + + CDBG("bufnum1 = %d\n", ad->bufnum1); + CDBG("config_axi1: O1, phy = 0x%lx, y_off = %d, cbcr_off =%d\n", + regptr->paddr, regptr->info.y_off, regptr->info.cbcr_off); + + bptr = &ao->output1buffer1_y_phy; + for (cnt = 0; cnt < ad->bufnum1; cnt++) { + *bptr = regptr->paddr + regptr->info.y_off; + bptr++; + *bptr = regptr->paddr + regptr->info.cbcr_off; + + bptr++; + regptr++; + } + + regptr--; + for (cnt = 0; cnt < (8 - ad->bufnum1); cnt++) { + *bptr = regptr->paddr + regptr->info.y_off; + bptr++; + *bptr = regptr->paddr + regptr->info.cbcr_off; + bptr++; + } + } /* if OUTPUT1 or Both */ + + if (mode == OUTPUT_2 || mode == OUTPUT_1_AND_2) { + regptr = &(ad->region[ad->bufnum1]); + + CDBG("bufnum2 = %d\n", ad->bufnum2); + CDBG("config_axi2: O2, phy = 0x%lx, y_off = %d, cbcr_off =%d\n", + regptr->paddr, regptr->info.y_off, regptr->info.cbcr_off); + + bptr = &ao->output2buffer1_y_phy; + for (cnt = 0; cnt < ad->bufnum2; cnt++) { + *bptr = regptr->paddr + regptr->info.y_off; + bptr++; + *bptr = regptr->paddr + regptr->info.cbcr_off; + + bptr++; + regptr++; + } + + regptr--; + for (cnt = 0; cnt < (8 - ad->bufnum2); cnt++) { + *bptr = regptr->paddr + regptr->info.y_off; + bptr++; + *bptr = regptr->paddr + regptr->info.cbcr_off; + bptr++; + } + } + + return rc; +} + +static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) +{ + struct msm_pmem_region *regptr; + unsigned char buf[256]; + + struct vfe_stats_ack sack; + struct axidata *axid; + uint32_t i; + + struct vfe_stats_we_cfg *scfg = NULL; + struct vfe_stats_af_cfg *sfcfg = NULL; + + struct axiout *axio = NULL; + void *cmd_data = NULL; + void *cmd_data_alloc = NULL; + long rc = 0; + struct msm_vfe_command_7k *vfecmd; + + vfecmd = + kmalloc(sizeof(struct msm_vfe_command_7k), + GFP_ATOMIC); + if (!vfecmd) { + pr_err("vfecmd alloc failed!\n"); + return -ENOMEM; + } + + if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE && + cmd->cmd_type != CMD_STATS_BUF_RELEASE && + cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) { + if (copy_from_user(vfecmd, + (void __user *)(cmd->value), + sizeof(struct msm_vfe_command_7k))) { + rc = -EFAULT; + goto config_failure; + } + } + + switch (cmd->cmd_type) { + case CMD_STATS_ENABLE: + case CMD_STATS_AXI_CFG: { + axid = data; + if (!axid) { + rc = -EFAULT; + goto config_failure; + } + + scfg = + kmalloc(sizeof(struct vfe_stats_we_cfg), + GFP_ATOMIC); + if (!scfg) { + rc = -ENOMEM; + goto config_failure; + } + + if (copy_from_user(scfg, + (void __user *)(vfecmd->value), + vfecmd->length)) { + + rc = -EFAULT; + goto config_done; + } + + CDBG("STATS_ENABLE: bufnum = %d, enabling = %d\n", + axid->bufnum1, scfg->wb_expstatsenable); + + if (axid->bufnum1 > 0) { + regptr = axid->region; + + for (i = 0; i < axid->bufnum1; i++) { + + CDBG("STATS_ENABLE, phy = 0x%lx\n", + regptr->paddr); + + scfg->wb_expstatoutputbuffer[i] = + (void *)regptr->paddr; + regptr++; + } + + cmd_data = scfg; + + } else { + rc = -EINVAL; + goto config_done; + } + } + break; + + case CMD_STATS_AF_ENABLE: + case CMD_STATS_AF_AXI_CFG: { + axid = data; + if (!axid) { + rc = -EFAULT; + goto config_failure; + } + + sfcfg = + kmalloc(sizeof(struct vfe_stats_af_cfg), + GFP_ATOMIC); + + if (!sfcfg) { + rc = -ENOMEM; + goto config_failure; + } + + if (copy_from_user(sfcfg, + (void __user *)(vfecmd->value), + vfecmd->length)) { + + rc = -EFAULT; + goto config_done; + } + + CDBG("AF_ENABLE: bufnum = %d, enabling = %d\n", + axid->bufnum1, sfcfg->af_enable); + + if (axid->bufnum1 > 0) { + regptr = axid->region; + + for (i = 0; i < axid->bufnum1; i++) { + + CDBG("STATS_ENABLE, phy = 0x%lx\n", + regptr->paddr); + + sfcfg->af_outbuf[i] = + (void *)regptr->paddr; + + regptr++; + } + + cmd_data = sfcfg; + + } else { + rc = -EINVAL; + goto config_done; + } + } + break; + + case CMD_FRAME_BUF_RELEASE: { + struct msm_frame *b; + unsigned long p; + struct vfe_outputack fack; + if (!data) { + rc = -EFAULT; + goto config_failure; + } + + b = (struct msm_frame *)(cmd->value); + p = *(unsigned long *)data; + + fack.header = VFE_FRAME_ACK; + + fack.output2newybufferaddress = + (void *)(p + b->y_off); + + fack.output2newcbcrbufferaddress = + (void *)(p + b->cbcr_off); + + vfecmd->queue = QDSP_CMDQUEUE; + vfecmd->length = sizeof(struct vfe_outputack); + cmd_data = &fack; + } + break; + + case CMD_SNAP_BUF_RELEASE: + break; + + case CMD_STATS_BUF_RELEASE: { + CDBG("vfe_7x_config: CMD_STATS_BUF_RELEASE\n"); + if (!data) { + rc = -EFAULT; + goto config_failure; + } + + sack.header = STATS_WE_ACK; + sack.bufaddr = (void *)*(uint32_t *)data; + + vfecmd->queue = QDSP_CMDQUEUE; + vfecmd->length = sizeof(struct vfe_stats_ack); + cmd_data = &sack; + } + break; + + case CMD_STATS_AF_BUF_RELEASE: { + CDBG("vfe_7x_config: CMD_STATS_AF_BUF_RELEASE\n"); + if (!data) { + rc = -EFAULT; + goto config_failure; + } + + sack.header = STATS_AF_ACK; + sack.bufaddr = (void *)*(uint32_t *)data; + + vfecmd->queue = QDSP_CMDQUEUE; + vfecmd->length = sizeof(struct vfe_stats_ack); + cmd_data = &sack; + } + break; + + case CMD_GENERAL: + case CMD_STATS_DISABLE: { + if (vfecmd->length > 256) { + cmd_data_alloc = + cmd_data = kmalloc(vfecmd->length, GFP_ATOMIC); + if (!cmd_data) { + rc = -ENOMEM; + goto config_failure; + } + } else + cmd_data = buf; + + if (copy_from_user(cmd_data, + (void __user *)(vfecmd->value), + vfecmd->length)) { + + rc = -EFAULT; + goto config_done; + } + + if (vfecmd->queue == QDSP_CMDQUEUE) { + switch (*(uint32_t *)cmd_data) { + case VFE_RESET_CMD: + msm_camio_vfe_blk_reset(); + msm_camio_camif_pad_reg_reset_2(); + vfestopped = 0; + break; + + case VFE_START_CMD: + msm_camio_camif_pad_reg_reset_2(); + vfestopped = 0; + break; + + case VFE_STOP_CMD: + vfestopped = 1; + goto config_send; + + default: + break; + } + } /* QDSP_CMDQUEUE */ + } + break; + + case CMD_AXI_CFG_OUT1: { + axid = data; + if (!axid) { + rc = -EFAULT; + goto config_failure; + } + + axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); + if (!axio) { + rc = -ENOMEM; + goto config_failure; + } + + if (copy_from_user(axio, (void *)(vfecmd->value), + sizeof(struct axiout))) { + rc = -EFAULT; + goto config_done; + } + + vfe_7x_config_axi(OUTPUT_1, axid, axio); + + cmd_data = axio; + } + break; + + case CMD_AXI_CFG_OUT2: + case CMD_RAW_PICT_AXI_CFG: { + axid = data; + if (!axid) { + rc = -EFAULT; + goto config_failure; + } + + axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); + if (!axio) { + rc = -ENOMEM; + goto config_failure; + } + + if (copy_from_user(axio, (void __user *)(vfecmd->value), + sizeof(struct axiout))) { + rc = -EFAULT; + goto config_done; + } + + vfe_7x_config_axi(OUTPUT_2, axid, axio); + cmd_data = axio; + } + break; + + case CMD_AXI_CFG_SNAP_O1_AND_O2: { + axid = data; + if (!axid) { + rc = -EFAULT; + goto config_failure; + } + + axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); + if (!axio) { + rc = -ENOMEM; + goto config_failure; + } + + if (copy_from_user(axio, (void __user *)(vfecmd->value), + sizeof(struct axiout))) { + rc = -EFAULT; + goto config_done; + } + + vfe_7x_config_axi(OUTPUT_1_AND_2, axid, axio); + + cmd_data = axio; + } + break; + + default: + break; + } /* switch */ + + if (vfestopped) + goto config_done; + +config_send: + CDBG("send adsp command = %d\n", *(uint32_t *)cmd_data); + rc = msm_adsp_write(vfe_mod, vfecmd->queue, + cmd_data, vfecmd->length); + +config_done: + if (cmd_data_alloc != NULL) + kfree(cmd_data_alloc); + +config_failure: + kfree(scfg); + kfree(axio); + kfree(vfecmd); + return rc; +} + +void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data) +{ + mutex_init(&vfe_lock); + fptr->vfe_init = vfe_7x_init; + fptr->vfe_enable = vfe_7x_enable; + fptr->vfe_config = vfe_7x_config; + fptr->vfe_disable = vfe_7x_disable; + fptr->vfe_release = vfe_7x_release; + vfe_syncdata = data; +} diff --git a/drivers/media/video/msm/msm_vfe7x.h b/drivers/media/video/msm/msm_vfe7x.h new file mode 100644 index 0000000000000..be3e9ad8f5248 --- /dev/null +++ b/drivers/media/video/msm/msm_vfe7x.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ +#ifndef __MSM_VFE7X_H__ +#define __MSM_VFE7X_H__ +#include +#include + +struct vfe_frame_extra { + uint32_t bl_evencol; + uint32_t bl_oddcol; + uint16_t g_def_p_cnt; + uint16_t r_b_def_p_cnt; +}; + +struct vfe_endframe { + uint32_t y_address; + uint32_t cbcr_address; + + unsigned int blacklevelevencolumn:23; + uint16_t reserved1:9; + unsigned int blackleveloddcolumn:23; + uint16_t reserved2:9; + + uint16_t greendefectpixelcount:8; + uint16_t reserved3:8; + uint16_t redbluedefectpixelcount:8; + uint16_t reserved4:8; +} __attribute__((packed, aligned(4))); + +struct vfe_outputack { + uint32_t header; + void *output2newybufferaddress; + void *output2newcbcrbufferaddress; +} __attribute__((packed, aligned(4))); + +struct vfe_stats_ack { + uint32_t header; + /* MUST BE 64 bit ALIGNED */ + void *bufaddr; +} __attribute__((packed, aligned(4))); + +/* AXI Output Config Command sent to DSP */ +struct axiout { + uint32_t cmdheader:32; + int outputmode:3; + uint8_t format:2; + uint32_t /* reserved */ : 27; + + /* AXI Output 1 Y Configuration, Part 1 */ + uint32_t out1yimageheight:12; + uint32_t /* reserved */ : 4; + uint32_t out1yimagewidthin64bitwords:10; + uint32_t /* reserved */ : 6; + + /* AXI Output 1 Y Configuration, Part 2 */ + uint8_t out1yburstlen:2; + uint32_t out1ynumrows:12; + uint32_t out1yrowincin64bitincs:12; + uint32_t /* reserved */ : 6; + + /* AXI Output 1 CbCr Configuration, Part 1 */ + uint32_t out1cbcrimageheight:12; + uint32_t /* reserved */ : 4; + uint32_t out1cbcrimagewidthin64bitwords:10; + uint32_t /* reserved */ : 6; + + /* AXI Output 1 CbCr Configuration, Part 2 */ + uint8_t out1cbcrburstlen:2; + uint32_t out1cbcrnumrows:12; + uint32_t out1cbcrrowincin64bitincs:12; + uint32_t /* reserved */ : 6; + + /* AXI Output 2 Y Configuration, Part 1 */ + uint32_t out2yimageheight:12; + uint32_t /* reserved */ : 4; + uint32_t out2yimagewidthin64bitwords:10; + uint32_t /* reserved */ : 6; + + /* AXI Output 2 Y Configuration, Part 2 */ + uint8_t out2yburstlen:2; + uint32_t out2ynumrows:12; + uint32_t out2yrowincin64bitincs:12; + uint32_t /* reserved */ : 6; + + /* AXI Output 2 CbCr Configuration, Part 1 */ + uint32_t out2cbcrimageheight:12; + uint32_t /* reserved */ : 4; + uint32_t out2cbcrimagewidtein64bitwords:10; + uint32_t /* reserved */ : 6; + + /* AXI Output 2 CbCr Configuration, Part 2 */ + uint8_t out2cbcrburstlen:2; + uint32_t out2cbcrnumrows:12; + uint32_t out2cbcrrowincin64bitincs:12; + uint32_t /* reserved */ : 6; + + /* Address configuration: + * output1 phisycal address */ + unsigned long output1buffer1_y_phy; + unsigned long output1buffer1_cbcr_phy; + unsigned long output1buffer2_y_phy; + unsigned long output1buffer2_cbcr_phy; + unsigned long output1buffer3_y_phy; + unsigned long output1buffer3_cbcr_phy; + unsigned long output1buffer4_y_phy; + unsigned long output1buffer4_cbcr_phy; + unsigned long output1buffer5_y_phy; + unsigned long output1buffer5_cbcr_phy; + unsigned long output1buffer6_y_phy; + unsigned long output1buffer6_cbcr_phy; + unsigned long output1buffer7_y_phy; + unsigned long output1buffer7_cbcr_phy; + unsigned long output1buffer8_y_phy; + unsigned long output1buffer8_cbcr_phy; + + /* output2 phisycal address */ + unsigned long output2buffer1_y_phy; + unsigned long output2buffer1_cbcr_phy; + unsigned long output2buffer2_y_phy; + unsigned long output2buffer2_cbcr_phy; + unsigned long output2buffer3_y_phy; + unsigned long output2buffer3_cbcr_phy; + unsigned long output2buffer4_y_phy; + unsigned long output2buffer4_cbcr_phy; + unsigned long output2buffer5_y_phy; + unsigned long output2buffer5_cbcr_phy; + unsigned long output2buffer6_y_phy; + unsigned long output2buffer6_cbcr_phy; + unsigned long output2buffer7_y_phy; + unsigned long output2buffer7_cbcr_phy; + unsigned long output2buffer8_y_phy; + unsigned long output2buffer8_cbcr_phy; +} __attribute__((packed, aligned(4))); + +struct vfe_stats_we_cfg { + uint32_t header; + + /* White Balance/Exposure Statistic Selection */ + uint8_t wb_expstatsenable:1; + uint8_t wb_expstatbuspriorityselection:1; + unsigned int wb_expstatbuspriorityvalue:4; + unsigned int /* reserved */ : 26; + + /* White Balance/Exposure Statistic Configuration, Part 1 */ + uint8_t exposurestatregions:1; + uint8_t exposurestatsubregions:1; + unsigned int /* reserved */ : 14; + + unsigned int whitebalanceminimumy:8; + unsigned int whitebalancemaximumy:8; + + /* White Balance/Exposure Statistic Configuration, Part 2 */ + uint8_t wb_expstatslopeofneutralregionline[ + NUM_WB_EXP_NEUTRAL_REGION_LINES]; + + /* White Balance/Exposure Statistic Configuration, Part 3 */ + unsigned int wb_expstatcrinterceptofneutralregionline2:12; + unsigned int /* reserved */ : 4; + unsigned int wb_expstatcbinterceptofneutralreginnline1:12; + unsigned int /* reserved */ : 4; + + /* White Balance/Exposure Statistic Configuration, Part 4 */ + unsigned int wb_expstatcrinterceptofneutralregionline4:12; + unsigned int /* reserved */ : 4; + unsigned int wb_expstatcbinterceptofneutralregionline3:12; + unsigned int /* reserved */ : 4; + + /* White Balance/Exposure Statistic Output Buffer Header */ + unsigned int wb_expmetricheaderpattern:8; + unsigned int /* reserved */ : 24; + + /* White Balance/Exposure Statistic Output Buffers-MUST + * BE 64 bit ALIGNED */ + void *wb_expstatoutputbuffer[NUM_WB_EXP_STAT_OUTPUT_BUFFERS]; +} __attribute__((packed, aligned(4))); + +struct vfe_stats_af_cfg { + uint32_t header; + + /* Autofocus Statistic Selection */ + uint8_t af_enable:1; + uint8_t af_busprioritysel:1; + unsigned int af_buspriorityval:4; + unsigned int /* reserved */ : 26; + + /* Autofocus Statistic Configuration, Part 1 */ + unsigned int af_singlewinvoffset:12; + unsigned int /* reserved */ : 4; + unsigned int af_singlewinhoffset:12; + unsigned int /* reserved */ : 3; + uint8_t af_winmode:1; + + /* Autofocus Statistic Configuration, Part 2 */ + unsigned int af_singglewinvh:11; + unsigned int /* reserved */ : 5; + unsigned int af_singlewinhw:11; + unsigned int /* reserved */ : 5; + + /* Autofocus Statistic Configuration, Parts 3-6 */ + uint8_t af_multiwingrid[NUM_AUTOFOCUS_MULTI_WINDOW_GRIDS]; + + /* Autofocus Statistic Configuration, Part 7 */ + signed int af_metrichpfcoefa00:5; + signed int af_metrichpfcoefa04:5; + unsigned int af_metricmaxval:11; + uint8_t af_metricsel:1; + unsigned int /* reserved */ : 10; + + /* Autofocus Statistic Configuration, Part 8 */ + signed int af_metrichpfcoefa20:5; + signed int af_metrichpfcoefa21:5; + signed int af_metrichpfcoefa22:5; + signed int af_metrichpfcoefa23:5; + signed int af_metrichpfcoefa24:5; + unsigned int /* reserved */ : 7; + + /* Autofocus Statistic Output Buffer Header */ + unsigned int af_metrichp:8; + unsigned int /* reserved */ : 24; + + /* Autofocus Statistic Output Buffers - MUST BE 64 bit ALIGNED!!! */ + void *af_outbuf[NUM_AF_STAT_OUTPUT_BUFFERS]; +} __attribute__((packed, aligned(4))); /* VFE_StatsAutofocusConfigCmdType */ + +struct msm_camera_frame_msg { + unsigned long output_y_address; + unsigned long output_cbcr_address; + + unsigned int blacklevelevenColumn:23; + uint16_t reserved1:9; + unsigned int blackleveloddColumn:23; + uint16_t reserved2:9; + + uint16_t greendefectpixelcount:8; + uint16_t reserved3:8; + uint16_t redbluedefectpixelcount:8; + uint16_t reserved4:8; +} __attribute__((packed, aligned(4))); + +/* New one for 7k */ +struct msm_vfe_command_7k { + uint16_t queue; + uint16_t length; + void *value; +}; + +struct stop_event { + wait_queue_head_t wait; + int state; + int timeout; +}; + + +#endif /* __MSM_VFE7X_H__ */ diff --git a/drivers/media/video/msm/msm_vfe8x.c b/drivers/media/video/msm/msm_vfe8x.c new file mode 100644 index 0000000000000..e34aaeb7e2643 --- /dev/null +++ b/drivers/media/video/msm/msm_vfe8x.c @@ -0,0 +1,757 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ +#include +#include +#include +#include +#include "msm_vfe8x_proc.h" + +#define ON 1 +#define OFF 0 + +struct mutex vfe_lock; +static void *vfe_syncdata; + +static int vfe_enable(struct camera_enable_cmd *enable) +{ + int rc = 0; + return rc; +} + +static int vfe_disable(struct camera_enable_cmd *enable, + struct platform_device *dev) +{ + int rc = 0; + + vfe_stop(); + + msm_camio_disable(dev); + return rc; +} + +static void vfe_release(struct platform_device *dev) +{ + msm_camio_disable(dev); + vfe_cmd_release(dev); + + mutex_lock(&vfe_lock); + vfe_syncdata = NULL; + mutex_unlock(&vfe_lock); +} + +static void vfe_config_axi(int mode, + struct axidata *ad, struct vfe_cmd_axi_output_config *ao) +{ + struct msm_pmem_region *regptr; + int i, j; + uint32_t *p1, *p2; + + if (mode == OUTPUT_1 || mode == OUTPUT_1_AND_2) { + regptr = ad->region; + for (i = 0; + i < ad->bufnum1; i++) { + + p1 = &(ao->output1.outputY.outFragments[i][0]); + p2 = &(ao->output1.outputCbcr.outFragments[i][0]); + + for (j = 0; + j < ao->output1.fragmentCount; j++) { + + *p1 = regptr->paddr + regptr->info.y_off; + p1++; + + *p2 = regptr->paddr + regptr->info.cbcr_off; + p2++; + } + regptr++; + } + } /* if OUTPUT1 or Both */ + + if (mode == OUTPUT_2 || mode == OUTPUT_1_AND_2) { + + regptr = &(ad->region[ad->bufnum1]); + CDBG("bufnum2 = %d\n", ad->bufnum2); + + for (i = 0; + i < ad->bufnum2; i++) { + + p1 = &(ao->output2.outputY.outFragments[i][0]); + p2 = &(ao->output2.outputCbcr.outFragments[i][0]); + + CDBG("config_axi: O2, phy = 0x%lx, y_off = %d, cbcr_off = %d\n", + regptr->paddr, regptr->info.y_off, regptr->info.cbcr_off); + + for (j = 0; + j < ao->output2.fragmentCount; j++) { + + *p1 = regptr->paddr + regptr->info.y_off; + CDBG("vfe_config_axi: p1 = 0x%x\n", *p1); + p1++; + + *p2 = regptr->paddr + regptr->info.cbcr_off; + CDBG("vfe_config_axi: p2 = 0x%x\n", *p2); + p2++; + } + regptr++; + } + } +} + +static int vfe_proc_general(struct msm_vfe_command_8k *cmd) +{ + int rc = 0; + + CDBG("vfe_proc_general: cmdID = %d\n", cmd->id); + + switch (cmd->id) { + case VFE_CMD_ID_RESET: + msm_camio_vfe_blk_reset(); + msm_camio_camif_pad_reg_reset_2(); + vfe_reset(); + break; + + case VFE_CMD_ID_START: { + struct vfe_cmd_start start; + if (copy_from_user(&start, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + /* msm_camio_camif_pad_reg_reset_2(); */ + msm_camio_camif_pad_reg_reset(); + vfe_start(&start); + } + break; + + case VFE_CMD_ID_CAMIF_CONFIG: { + struct vfe_cmd_camif_config camif; + if (copy_from_user(&camif, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_camif_config(&camif); + } + break; + + case VFE_CMD_ID_BLACK_LEVEL_CONFIG: { + struct vfe_cmd_black_level_config bl; + if (copy_from_user(&bl, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_black_level_config(&bl); + } + break; + + case VFE_CMD_ID_ROLL_OFF_CONFIG: { + struct vfe_cmd_roll_off_config rolloff; + if (copy_from_user(&rolloff, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_roll_off_config(&rolloff); + } + break; + + case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_CONFIG: { + struct vfe_cmd_demux_channel_gain_config demuxc; + if (copy_from_user(&demuxc, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + /* demux is always enabled. */ + vfe_demux_channel_gain_config(&demuxc); + } + break; + + case VFE_CMD_ID_DEMOSAIC_CONFIG: { + struct vfe_cmd_demosaic_config demosaic; + if (copy_from_user(&demosaic, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_demosaic_config(&demosaic); + } + break; + + case VFE_CMD_ID_FOV_CROP_CONFIG: + case VFE_CMD_ID_FOV_CROP_UPDATE: { + struct vfe_cmd_fov_crop_config fov; + if (copy_from_user(&fov, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_fov_crop_config(&fov); + } + break; + + case VFE_CMD_ID_MAIN_SCALER_CONFIG: + case VFE_CMD_ID_MAIN_SCALER_UPDATE: { + struct vfe_cmd_main_scaler_config mainds; + if (copy_from_user(&mainds, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_main_scaler_config(&mainds); + } + break; + + case VFE_CMD_ID_WHITE_BALANCE_CONFIG: + case VFE_CMD_ID_WHITE_BALANCE_UPDATE: { + struct vfe_cmd_white_balance_config wb; + if (copy_from_user(&wb, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_white_balance_config(&wb); + } + break; + + case VFE_CMD_ID_COLOR_CORRECTION_CONFIG: + case VFE_CMD_ID_COLOR_CORRECTION_UPDATE: { + struct vfe_cmd_color_correction_config cc; + if (copy_from_user(&cc, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_color_correction_config(&cc); + } + break; + + case VFE_CMD_ID_LA_CONFIG: { + struct vfe_cmd_la_config la; + if (copy_from_user(&la, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_la_config(&la); + } + break; + + case VFE_CMD_ID_RGB_GAMMA_CONFIG: { + struct vfe_cmd_rgb_gamma_config rgb; + if (copy_from_user(&rgb, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + rc = vfe_rgb_gamma_config(&rgb); + } + break; + + case VFE_CMD_ID_CHROMA_ENHAN_CONFIG: + case VFE_CMD_ID_CHROMA_ENHAN_UPDATE: { + struct vfe_cmd_chroma_enhan_config chrom; + if (copy_from_user(&chrom, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_chroma_enhan_config(&chrom); + } + break; + + case VFE_CMD_ID_CHROMA_SUPPRESSION_CONFIG: + case VFE_CMD_ID_CHROMA_SUPPRESSION_UPDATE: { + struct vfe_cmd_chroma_suppression_config chromsup; + if (copy_from_user(&chromsup, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_chroma_sup_config(&chromsup); + } + break; + + case VFE_CMD_ID_ASF_CONFIG: { + struct vfe_cmd_asf_config asf; + if (copy_from_user(&asf, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_asf_config(&asf); + } + break; + + case VFE_CMD_ID_SCALER2Y_CONFIG: + case VFE_CMD_ID_SCALER2Y_UPDATE: { + struct vfe_cmd_scaler2_config ds2y; + if (copy_from_user(&ds2y, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_scaler2y_config(&ds2y); + } + break; + + case VFE_CMD_ID_SCALER2CbCr_CONFIG: + case VFE_CMD_ID_SCALER2CbCr_UPDATE: { + struct vfe_cmd_scaler2_config ds2cbcr; + if (copy_from_user(&ds2cbcr, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_scaler2cbcr_config(&ds2cbcr); + } + break; + + case VFE_CMD_ID_CHROMA_SUBSAMPLE_CONFIG: { + struct vfe_cmd_chroma_subsample_config sub; + if (copy_from_user(&sub, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_chroma_subsample_config(&sub); + } + break; + + case VFE_CMD_ID_FRAME_SKIP_CONFIG: { + struct vfe_cmd_frame_skip_config fskip; + if (copy_from_user(&fskip, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_frame_skip_config(&fskip); + } + break; + + case VFE_CMD_ID_OUTPUT_CLAMP_CONFIG: { + struct vfe_cmd_output_clamp_config clamp; + if (copy_from_user(&clamp, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_output_clamp_config(&clamp); + } + break; + + /* module update commands */ + case VFE_CMD_ID_BLACK_LEVEL_UPDATE: { + struct vfe_cmd_black_level_config blk; + if (copy_from_user(&blk, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_black_level_update(&blk); + } + break; + + case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_UPDATE: { + struct vfe_cmd_demux_channel_gain_config dmu; + if (copy_from_user(&dmu, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_demux_channel_gain_update(&dmu); + } + break; + + case VFE_CMD_ID_DEMOSAIC_BPC_UPDATE: { + struct vfe_cmd_demosaic_bpc_update demo_bpc; + if (copy_from_user(&demo_bpc, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_demosaic_bpc_update(&demo_bpc); + } + break; + + case VFE_CMD_ID_DEMOSAIC_ABF_UPDATE: { + struct vfe_cmd_demosaic_abf_update demo_abf; + if (copy_from_user(&demo_abf, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_demosaic_abf_update(&demo_abf); + } + break; + + case VFE_CMD_ID_LA_UPDATE: { + struct vfe_cmd_la_config la; + if (copy_from_user(&la, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_la_update(&la); + } + break; + + case VFE_CMD_ID_RGB_GAMMA_UPDATE: { + struct vfe_cmd_rgb_gamma_config rgb; + if (copy_from_user(&rgb, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + rc = vfe_rgb_gamma_update(&rgb); + } + break; + + case VFE_CMD_ID_ASF_UPDATE: { + struct vfe_cmd_asf_update asf; + if (copy_from_user(&asf, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_asf_update(&asf); + } + break; + + case VFE_CMD_ID_FRAME_SKIP_UPDATE: { + struct vfe_cmd_frame_skip_update fskip; + if (copy_from_user(&fskip, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_frame_skip_update(&fskip); + } + break; + + case VFE_CMD_ID_CAMIF_FRAME_UPDATE: { + struct vfe_cmds_camif_frame fup; + if (copy_from_user(&fup, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_camif_frame_update(&fup); + } + break; + + /* stats update commands */ + case VFE_CMD_ID_STATS_AUTOFOCUS_UPDATE: { + struct vfe_cmd_stats_af_update afup; + if (copy_from_user(&afup, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_stats_update_af(&afup); + } + break; + + case VFE_CMD_ID_STATS_WB_EXP_UPDATE: { + struct vfe_cmd_stats_wb_exp_update wbexp; + if (copy_from_user(&wbexp, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_stats_update_wb_exp(&wbexp); + } + break; + + /* control of start, stop, update, etc... */ + case VFE_CMD_ID_STOP: + vfe_stop(); + break; + + case VFE_CMD_ID_GET_HW_VERSION: + break; + + /* stats */ + case VFE_CMD_ID_STATS_SETTING: { + struct vfe_cmd_stats_setting stats; + if (copy_from_user(&stats, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_stats_setting(&stats); + } + break; + + case VFE_CMD_ID_STATS_AUTOFOCUS_START: { + struct vfe_cmd_stats_af_start af; + if (copy_from_user(&af, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_stats_start_af(&af); + } + break; + + case VFE_CMD_ID_STATS_AUTOFOCUS_STOP: + vfe_stats_af_stop(); + break; + + case VFE_CMD_ID_STATS_WB_EXP_START: { + struct vfe_cmd_stats_wb_exp_start awexp; + if (copy_from_user(&awexp, + (void __user *) cmd->value, cmd->length)) + rc = -EFAULT; + + vfe_stats_start_wb_exp(&awexp); + } + break; + + case VFE_CMD_ID_STATS_WB_EXP_STOP: + vfe_stats_wb_exp_stop(); + break; + + case VFE_CMD_ID_ASYNC_TIMER_SETTING: + break; + + case VFE_CMD_ID_UPDATE: + vfe_update(); + break; + + /* test gen */ + case VFE_CMD_ID_TEST_GEN_START: + break; + +/* + acknowledge from upper layer + these are not in general command. + + case VFE_CMD_ID_OUTPUT1_ACK: + break; + case VFE_CMD_ID_OUTPUT2_ACK: + break; + case VFE_CMD_ID_EPOCH1_ACK: + break; + case VFE_CMD_ID_EPOCH2_ACK: + break; + case VFE_CMD_ID_STATS_AUTOFOCUS_ACK: + break; + case VFE_CMD_ID_STATS_WB_EXP_ACK: + break; +*/ + + default: + break; + } /* switch */ + + return rc; +} + +static int vfe_config(struct msm_vfe_cfg_cmd *cmd, void *data) +{ + struct msm_pmem_region *regptr; + struct msm_vfe_command_8k vfecmd; + + uint32_t i; + + void *cmd_data = NULL; + long rc = 0; + + struct vfe_cmd_axi_output_config *axio = NULL; + struct vfe_cmd_stats_setting *scfg = NULL; + + if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE && + cmd->cmd_type != CMD_STATS_BUF_RELEASE) { + + if (copy_from_user(&vfecmd, + (void __user *)(cmd->value), + sizeof(struct msm_vfe_command_8k))) + return -EFAULT; + } + + CDBG("vfe_config: cmdType = %d\n", cmd->cmd_type); + + switch (cmd->cmd_type) { + case CMD_GENERAL: + rc = vfe_proc_general(&vfecmd); + break; + + case CMD_STATS_ENABLE: + case CMD_STATS_AXI_CFG: { + struct axidata *axid; + + axid = data; + if (!axid) + return -EFAULT; + + scfg = + kmalloc(sizeof(struct vfe_cmd_stats_setting), + GFP_ATOMIC); + if (!scfg) + return -ENOMEM; + + if (copy_from_user(scfg, + (void __user *)(vfecmd.value), + vfecmd.length)) { + + kfree(scfg); + return -EFAULT; + } + + regptr = axid->region; + if (axid->bufnum1 > 0) { + for (i = 0; i < axid->bufnum1; i++) { + scfg->awbBuffer[i] = + (uint32_t)(regptr->paddr); + regptr++; + } + } + + if (axid->bufnum2 > 0) { + for (i = 0; i < axid->bufnum2; i++) { + scfg->afBuffer[i] = + (uint32_t)(regptr->paddr); + regptr++; + } + } + + vfe_stats_config(scfg); + } + break; + + case CMD_STATS_AF_AXI_CFG: { + } + break; + + case CMD_FRAME_BUF_RELEASE: { + /* preview buffer release */ + struct msm_frame *b; + unsigned long p; + struct vfe_cmd_output_ack fack; + + if (!data) + return -EFAULT; + + b = (struct msm_frame *)(cmd->value); + p = *(unsigned long *)data; + + b->path = MSM_FRAME_ENC; + + fack.ybufaddr[0] = + (uint32_t)(p + b->y_off); + + fack.chromabufaddr[0] = + (uint32_t)(p + b->cbcr_off); + + if (b->path == MSM_FRAME_PREV_1) + vfe_output1_ack(&fack); + + if (b->path == MSM_FRAME_ENC || + b->path == MSM_FRAME_PREV_2) + vfe_output2_ack(&fack); + } + break; + + case CMD_SNAP_BUF_RELEASE: { + } + break; + + case CMD_STATS_BUF_RELEASE: { + struct vfe_cmd_stats_wb_exp_ack sack; + + if (!data) + return -EFAULT; + + sack.nextWbExpOutputBufferAddr = *(uint32_t *)data; + vfe_stats_wb_exp_ack(&sack); + } + break; + + case CMD_AXI_CFG_OUT1: { + struct axidata *axid; + + axid = data; + if (!axid) + return -EFAULT; + + axio = + kmalloc(sizeof(struct vfe_cmd_axi_output_config), + GFP_ATOMIC); + if (!axio) + return -ENOMEM; + + if (copy_from_user(axio, (void __user *)(vfecmd.value), + sizeof(struct vfe_cmd_axi_output_config))) { + kfree(axio); + return -EFAULT; + } + + vfe_config_axi(OUTPUT_1, axid, axio); + vfe_axi_output_config(axio); + } + break; + + case CMD_AXI_CFG_OUT2: + case CMD_RAW_PICT_AXI_CFG: { + struct axidata *axid; + + axid = data; + if (!axid) + return -EFAULT; + + axio = + kmalloc(sizeof(struct vfe_cmd_axi_output_config), + GFP_ATOMIC); + if (!axio) + return -ENOMEM; + + if (copy_from_user(axio, (void __user *)(vfecmd.value), + sizeof(struct vfe_cmd_axi_output_config))) { + kfree(axio); + return -EFAULT; + } + + vfe_config_axi(OUTPUT_2, axid, axio); + + axio->outputDataSize = 0; + vfe_axi_output_config(axio); + } + break; + + case CMD_AXI_CFG_SNAP_O1_AND_O2: { + struct axidata *axid; + axid = data; + if (!axid) + return -EFAULT; + + axio = + kmalloc(sizeof(struct vfe_cmd_axi_output_config), + GFP_ATOMIC); + if (!axio) + return -ENOMEM; + + if (copy_from_user(axio, (void __user *)(vfecmd.value), + sizeof(struct vfe_cmd_axi_output_config))) { + kfree(axio); + return -EFAULT; + } + + vfe_config_axi(OUTPUT_1_AND_2, + axid, axio); + vfe_axi_output_config(axio); + cmd_data = axio; + } + break; + + default: + break; + } /* switch */ + + kfree(scfg); + + kfree(axio); + +/* + if (cmd->length > 256 && + cmd_data && + (cmd->cmd_type == CMD_GENERAL || + cmd->cmd_type == CMD_STATS_DISABLE)) { + kfree(cmd_data); + } +*/ + return rc; +} + +static int vfe_init(struct msm_vfe_callback *presp, + struct platform_device *dev) +{ + int rc = 0; + + rc = vfe_cmd_init(presp, dev, vfe_syncdata); + if (rc < 0) + return rc; + + /* Bring up all the required GPIOs and Clocks */ + return msm_camio_enable(dev); +} + +void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data) +{ + mutex_init(&vfe_lock); + fptr->vfe_init = vfe_init; + fptr->vfe_enable = vfe_enable; + fptr->vfe_config = vfe_config; + fptr->vfe_disable = vfe_disable; + fptr->vfe_release = vfe_release; + vfe_syncdata = data; +} diff --git a/drivers/media/video/msm/msm_vfe8x.h b/drivers/media/video/msm/msm_vfe8x.h new file mode 100644 index 0000000000000..28a70a9e5ed7a --- /dev/null +++ b/drivers/media/video/msm/msm_vfe8x.h @@ -0,0 +1,895 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ +#ifndef __MSM_VFE8X_H__ +#define __MSM_VFE8X_H__ + +#define TRUE 1 +#define FALSE 0 +#define boolean uint8_t + +enum VFE_STATE { + VFE_STATE_IDLE, + VFE_STATE_ACTIVE +}; + +enum vfe_cmd_id { + /* + *Important! Command_ID are arranged in order. + *Don't change!*/ + VFE_CMD_ID_START, + VFE_CMD_ID_RESET, + + /* bus and camif config */ + VFE_CMD_ID_AXI_INPUT_CONFIG, + VFE_CMD_ID_CAMIF_CONFIG, + VFE_CMD_ID_AXI_OUTPUT_CONFIG, + + /* module config */ + VFE_CMD_ID_BLACK_LEVEL_CONFIG, + VFE_CMD_ID_ROLL_OFF_CONFIG, + VFE_CMD_ID_DEMUX_CHANNEL_GAIN_CONFIG, + VFE_CMD_ID_DEMOSAIC_CONFIG, + VFE_CMD_ID_FOV_CROP_CONFIG, + VFE_CMD_ID_MAIN_SCALER_CONFIG, + VFE_CMD_ID_WHITE_BALANCE_CONFIG, + VFE_CMD_ID_COLOR_CORRECTION_CONFIG, + VFE_CMD_ID_LA_CONFIG, + VFE_CMD_ID_RGB_GAMMA_CONFIG, + VFE_CMD_ID_CHROMA_ENHAN_CONFIG, + VFE_CMD_ID_CHROMA_SUPPRESSION_CONFIG, + VFE_CMD_ID_ASF_CONFIG, + VFE_CMD_ID_SCALER2Y_CONFIG, + VFE_CMD_ID_SCALER2CbCr_CONFIG, + VFE_CMD_ID_CHROMA_SUBSAMPLE_CONFIG, + VFE_CMD_ID_FRAME_SKIP_CONFIG, + VFE_CMD_ID_OUTPUT_CLAMP_CONFIG, + + /* test gen */ + VFE_CMD_ID_TEST_GEN_START, + + VFE_CMD_ID_UPDATE, + + /* ackownledge from upper layer */ + VFE_CMD_ID_OUTPUT1_ACK, + VFE_CMD_ID_OUTPUT2_ACK, + VFE_CMD_ID_EPOCH1_ACK, + VFE_CMD_ID_EPOCH2_ACK, + VFE_CMD_ID_STATS_AUTOFOCUS_ACK, + VFE_CMD_ID_STATS_WB_EXP_ACK, + + /* module update commands */ + VFE_CMD_ID_BLACK_LEVEL_UPDATE, + VFE_CMD_ID_DEMUX_CHANNEL_GAIN_UPDATE, + VFE_CMD_ID_DEMOSAIC_BPC_UPDATE, + VFE_CMD_ID_DEMOSAIC_ABF_UPDATE, + VFE_CMD_ID_FOV_CROP_UPDATE, + VFE_CMD_ID_WHITE_BALANCE_UPDATE, + VFE_CMD_ID_COLOR_CORRECTION_UPDATE, + VFE_CMD_ID_LA_UPDATE, + VFE_CMD_ID_RGB_GAMMA_UPDATE, + VFE_CMD_ID_CHROMA_ENHAN_UPDATE, + VFE_CMD_ID_CHROMA_SUPPRESSION_UPDATE, + VFE_CMD_ID_MAIN_SCALER_UPDATE, + VFE_CMD_ID_SCALER2CbCr_UPDATE, + VFE_CMD_ID_SCALER2Y_UPDATE, + VFE_CMD_ID_ASF_UPDATE, + VFE_CMD_ID_FRAME_SKIP_UPDATE, + VFE_CMD_ID_CAMIF_FRAME_UPDATE, + + /* stats update commands */ + VFE_CMD_ID_STATS_AUTOFOCUS_UPDATE, + VFE_CMD_ID_STATS_WB_EXP_UPDATE, + + /* control of start, stop, update, etc... */ + VFE_CMD_ID_STOP, + VFE_CMD_ID_GET_HW_VERSION, + + /* stats */ + VFE_CMD_ID_STATS_SETTING, + VFE_CMD_ID_STATS_AUTOFOCUS_START, + VFE_CMD_ID_STATS_AUTOFOCUS_STOP, + VFE_CMD_ID_STATS_WB_EXP_START, + VFE_CMD_ID_STATS_WB_EXP_STOP, + + VFE_CMD_ID_ASYNC_TIMER_SETTING, + + /* max id */ + VFE_CMD_ID_MAX +}; + +struct vfe_cmd_hw_version { + uint32_t minorVersion; + uint32_t majorVersion; + uint32_t coreVersion; +}; + +enum VFE_CAMIF_SYNC_EDGE { + VFE_CAMIF_SYNC_EDGE_ActiveHigh, + VFE_CAMIF_SYNC_EDGE_ActiveLow +}; + +enum VFE_CAMIF_SYNC_MODE { + VFE_CAMIF_SYNC_MODE_APS, + VFE_CAMIF_SYNC_MODE_EFS, + VFE_CAMIF_SYNC_MODE_ELS, + VFE_CAMIF_SYNC_MODE_ILLEGAL +}; + +struct vfe_cmds_camif_efs { + uint8_t efsendofline; + uint8_t efsstartofline; + uint8_t efsendofframe; + uint8_t efsstartofframe; +}; + +struct vfe_cmds_camif_frame { + uint16_t pixelsPerLine; + uint16_t linesPerFrame; +}; + +struct vfe_cmds_camif_window { + uint16_t firstpixel; + uint16_t lastpixel; + uint16_t firstline; + uint16_t lastline; +}; + +enum CAMIF_SUBSAMPLE_FRAME_SKIP { + CAMIF_SUBSAMPLE_FRAME_SKIP_0, + CAMIF_SUBSAMPLE_FRAME_SKIP_AllFrames, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_2Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_3Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_4Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_5Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_6Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_7Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_8Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_9Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_10Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_11Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_12Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_13Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_14Frame, + CAMIF_SUBSAMPLE_FRAME_SKIP_ONE_OUT_OF_EVERY_15Frame +}; + +struct vfe_cmds_camif_subsample { + uint16_t pixelskipmask; + uint16_t lineskipmask; + enum CAMIF_SUBSAMPLE_FRAME_SKIP frameskip; + uint8_t frameskipmode; + uint8_t pixelskipwrap; +}; + +struct vfe_cmds_camif_epoch { + uint8_t enable; + uint16_t lineindex; +}; + +struct vfe_cmds_camif_cfg { + enum VFE_CAMIF_SYNC_EDGE vSyncEdge; + enum VFE_CAMIF_SYNC_EDGE hSyncEdge; + enum VFE_CAMIF_SYNC_MODE syncMode; + uint8_t vfeSubSampleEnable; + uint8_t busSubSampleEnable; + uint8_t irqSubSampleEnable; + uint8_t binningEnable; + uint8_t misrEnable; +}; + +struct vfe_cmd_camif_config { + struct vfe_cmds_camif_cfg camifConfig; + struct vfe_cmds_camif_efs EFS; + struct vfe_cmds_camif_frame frame; + struct vfe_cmds_camif_window window; + struct vfe_cmds_camif_subsample subsample; + struct vfe_cmds_camif_epoch epoch1; + struct vfe_cmds_camif_epoch epoch2; +}; + +enum VFE_AXI_OUTPUT_MODE { + VFE_AXI_OUTPUT_MODE_Output1, + VFE_AXI_OUTPUT_MODE_Output2, + VFE_AXI_OUTPUT_MODE_Output1AndOutput2, + VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2, + VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1, + VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2, + VFE_AXI_LAST_OUTPUT_MODE_ENUM +}; + +enum VFE_RAW_WR_PATH_SEL { + VFE_RAW_OUTPUT_DISABLED, + VFE_RAW_OUTPUT_ENC_CBCR_PATH, + VFE_RAW_OUTPUT_VIEW_CBCR_PATH, + VFE_RAW_OUTPUT_PATH_INVALID +}; + +enum VFE_RAW_PIXEL_DATA_SIZE { + VFE_RAW_PIXEL_DATA_SIZE_8BIT, + VFE_RAW_PIXEL_DATA_SIZE_10BIT, + VFE_RAW_PIXEL_DATA_SIZE_12BIT, +}; + +#define VFE_AXI_OUTPUT_BURST_LENGTH 4 +#define VFE_MAX_NUM_FRAGMENTS_PER_FRAME 4 +#define VFE_AXI_OUTPUT_CFG_FRAME_COUNT 3 + +struct vfe_cmds_axi_out_per_component { + uint16_t imageWidth; + uint16_t imageHeight; + uint16_t outRowCount; + uint16_t outRowIncrement; + uint32_t outFragments[VFE_AXI_OUTPUT_CFG_FRAME_COUNT] + [VFE_MAX_NUM_FRAGMENTS_PER_FRAME]; +}; + +struct vfe_cmds_axi_per_output_path { + uint8_t fragmentCount; + struct vfe_cmds_axi_out_per_component outputY; + struct vfe_cmds_axi_out_per_component outputCbcr; +}; + +enum VFE_AXI_BURST_LENGTH { + VFE_AXI_BURST_LENGTH_IS_2 = 2, + VFE_AXI_BURST_LENGTH_IS_4 = 4, + VFE_AXI_BURST_LENGTH_IS_8 = 8, + VFE_AXI_BURST_LENGTH_IS_16 = 16 +}; + +struct vfe_cmd_axi_output_config { + enum VFE_AXI_BURST_LENGTH burstLength; + enum VFE_AXI_OUTPUT_MODE outputMode; + enum VFE_RAW_PIXEL_DATA_SIZE outputDataSize; + struct vfe_cmds_axi_per_output_path output1; + struct vfe_cmds_axi_per_output_path output2; +}; + +struct vfe_cmd_fov_crop_config { + uint8_t enable; + uint16_t firstPixel; + uint16_t lastPixel; + uint16_t firstLine; + uint16_t lastLine; +}; + +struct vfe_cmds_main_scaler_stripe_init { + uint16_t MNCounterInit; + uint16_t phaseInit; +}; + +struct vfe_cmds_scaler_one_dimension { + uint8_t enable; + uint16_t inputSize; + uint16_t outputSize; + uint32_t phaseMultiplicationFactor; + uint8_t interpolationResolution; +}; + +struct vfe_cmd_main_scaler_config { + uint8_t enable; + struct vfe_cmds_scaler_one_dimension hconfig; + struct vfe_cmds_scaler_one_dimension vconfig; + struct vfe_cmds_main_scaler_stripe_init MNInitH; + struct vfe_cmds_main_scaler_stripe_init MNInitV; +}; + +struct vfe_cmd_scaler2_config { + uint8_t enable; + struct vfe_cmds_scaler_one_dimension hconfig; + struct vfe_cmds_scaler_one_dimension vconfig; +}; + +struct vfe_cmd_frame_skip_config { + uint8_t output1Period; + uint32_t output1Pattern; + uint8_t output2Period; + uint32_t output2Pattern; +}; + +struct vfe_cmd_frame_skip_update { + uint32_t output1Pattern; + uint32_t output2Pattern; +}; + +struct vfe_cmd_output_clamp_config { + uint8_t minCh0; + uint8_t minCh1; + uint8_t minCh2; + uint8_t maxCh0; + uint8_t maxCh1; + uint8_t maxCh2; +}; + +struct vfe_cmd_chroma_subsample_config { + uint8_t enable; + uint8_t cropEnable; + uint8_t vsubSampleEnable; + uint8_t hsubSampleEnable; + uint8_t vCosited; + uint8_t hCosited; + uint8_t vCositedPhase; + uint8_t hCositedPhase; + uint16_t cropWidthFirstPixel; + uint16_t cropWidthLastPixel; + uint16_t cropHeightFirstLine; + uint16_t cropHeightLastLine; +}; + +enum VFE_START_INPUT_SOURCE { + VFE_START_INPUT_SOURCE_CAMIF, + VFE_START_INPUT_SOURCE_TESTGEN, + VFE_START_INPUT_SOURCE_AXI, + VFE_START_INPUT_SOURCE_INVALID +}; + +enum VFE_START_OPERATION_MODE { + VFE_START_OPERATION_MODE_CONTINUOUS, + VFE_START_OPERATION_MODE_SNAPSHOT +}; + +enum VFE_START_PIXEL_PATTERN { + VFE_BAYER_RGRGRG, + VFE_BAYER_GRGRGR, + VFE_BAYER_BGBGBG, + VFE_BAYER_GBGBGB, + VFE_YUV_YCbYCr, + VFE_YUV_YCrYCb, + VFE_YUV_CbYCrY, + VFE_YUV_CrYCbY +}; + +enum VFE_BUS_RD_INPUT_PIXEL_PATTERN { + VFE_BAYER_RAW, + VFE_YUV_INTERLEAVED, + VFE_YUV_PSEUDO_PLANAR_Y, + VFE_YUV_PSEUDO_PLANAR_CBCR +}; + +enum VFE_YUV_INPUT_COSITING_MODE { + VFE_YUV_COSITED, + VFE_YUV_INTERPOLATED +}; + +struct vfe_cmd_start { + enum VFE_START_INPUT_SOURCE inputSource; + enum VFE_START_OPERATION_MODE operationMode; + uint8_t snapshotCount; + enum VFE_START_PIXEL_PATTERN pixel; + enum VFE_YUV_INPUT_COSITING_MODE yuvInputCositingMode; +}; + +struct vfe_cmd_output_ack { + uint32_t ybufaddr[VFE_MAX_NUM_FRAGMENTS_PER_FRAME]; + uint32_t chromabufaddr[VFE_MAX_NUM_FRAGMENTS_PER_FRAME]; +}; + +#define VFE_STATS_BUFFER_COUNT 3 + +struct vfe_cmd_stats_setting { + uint16_t frameHDimension; + uint16_t frameVDimension; + uint8_t afBusPrioritySelection; + uint8_t afBusPriority; + uint8_t awbBusPrioritySelection; + uint8_t awbBusPriority; + uint8_t histBusPrioritySelection; + uint8_t histBusPriority; + uint32_t afBuffer[VFE_STATS_BUFFER_COUNT]; + uint32_t awbBuffer[VFE_STATS_BUFFER_COUNT]; + uint32_t histBuffer[VFE_STATS_BUFFER_COUNT]; +}; + +struct vfe_cmd_stats_af_start { + uint8_t enable; + uint8_t windowMode; + uint16_t windowHOffset; + uint16_t windowVOffset; + uint16_t windowWidth; + uint16_t windowHeight; + uint8_t gridForMultiWindows[16]; + uint8_t metricSelection; + int16_t metricMax; + int8_t highPassCoef[7]; + int8_t bufferHeader; +}; + +struct vfe_cmd_stats_af_update { + uint8_t windowMode; + uint16_t windowHOffset; + uint16_t windowVOffset; + uint16_t windowWidth; + uint16_t windowHeight; +}; + +struct vfe_cmd_stats_wb_exp_start { + uint8_t enable; + uint8_t wbExpRegions; + uint8_t wbExpSubRegion; + uint8_t awbYMin; + uint8_t awbYMax; + int8_t awbMCFG[4]; + int16_t awbCCFG[4]; + int8_t axwHeader; +}; + +struct vfe_cmd_stats_wb_exp_update { + uint8_t wbExpRegions; + uint8_t wbExpSubRegion; + int8_t awbYMin; + int8_t awbYMax; + int8_t awbMCFG[4]; + int16_t awbCCFG[4]; +}; + +struct vfe_cmd_stats_af_ack { + uint32_t nextAFOutputBufferAddr; +}; + +struct vfe_cmd_stats_wb_exp_ack { + uint32_t nextWbExpOutputBufferAddr; +}; + +struct vfe_cmd_black_level_config { + uint8_t enable; + uint16_t evenEvenAdjustment; + uint16_t evenOddAdjustment; + uint16_t oddEvenAdjustment; + uint16_t oddOddAdjustment; +}; + +/* 13*1 */ +#define VFE_ROLL_OFF_INIT_TABLE_SIZE 13 +/* 13*16 */ +#define VFE_ROLL_OFF_DELTA_TABLE_SIZE 208 + +struct vfe_cmd_roll_off_config { + uint8_t enable; + uint16_t gridWidth; + uint16_t gridHeight; + uint16_t yDelta; + uint8_t gridXIndex; + uint8_t gridYIndex; + uint16_t gridPixelXIndex; + uint16_t gridPixelYIndex; + uint16_t yDeltaAccum; + uint16_t initTableR[VFE_ROLL_OFF_INIT_TABLE_SIZE]; + uint16_t initTableGr[VFE_ROLL_OFF_INIT_TABLE_SIZE]; + uint16_t initTableB[VFE_ROLL_OFF_INIT_TABLE_SIZE]; + uint16_t initTableGb[VFE_ROLL_OFF_INIT_TABLE_SIZE]; + int16_t deltaTableR[VFE_ROLL_OFF_DELTA_TABLE_SIZE]; + int16_t deltaTableGr[VFE_ROLL_OFF_DELTA_TABLE_SIZE]; + int16_t deltaTableB[VFE_ROLL_OFF_DELTA_TABLE_SIZE]; + int16_t deltaTableGb[VFE_ROLL_OFF_DELTA_TABLE_SIZE]; +}; + +struct vfe_cmd_demux_channel_gain_config { + uint16_t ch0EvenGain; + uint16_t ch0OddGain; + uint16_t ch1Gain; + uint16_t ch2Gain; +}; + +struct vfe_cmds_demosaic_abf { + uint8_t enable; + uint8_t forceOn; + uint8_t shift; + uint16_t lpThreshold; + uint16_t max; + uint16_t min; + uint8_t ratio; +}; + +struct vfe_cmds_demosaic_bpc { + uint8_t enable; + uint16_t fmaxThreshold; + uint16_t fminThreshold; + uint16_t redDiffThreshold; + uint16_t blueDiffThreshold; + uint16_t greenDiffThreshold; +}; + +struct vfe_cmd_demosaic_config { + uint8_t enable; + uint8_t slopeShift; + struct vfe_cmds_demosaic_abf abfConfig; + struct vfe_cmds_demosaic_bpc bpcConfig; +}; + +struct vfe_cmd_demosaic_bpc_update { + struct vfe_cmds_demosaic_bpc bpcUpdate; +}; + +struct vfe_cmd_demosaic_abf_update { + struct vfe_cmds_demosaic_abf abfUpdate; +}; + +struct vfe_cmd_white_balance_config { + uint8_t enable; + uint16_t ch2Gain; + uint16_t ch1Gain; + uint16_t ch0Gain; +}; + +enum VFE_COLOR_CORRECTION_COEF_QFACTOR { + COEF_IS_Q7_SIGNED, + COEF_IS_Q8_SIGNED, + COEF_IS_Q9_SIGNED, + COEF_IS_Q10_SIGNED +}; + +struct vfe_cmd_color_correction_config { + uint8_t enable; + enum VFE_COLOR_CORRECTION_COEF_QFACTOR coefQFactor; + int16_t C0; + int16_t C1; + int16_t C2; + int16_t C3; + int16_t C4; + int16_t C5; + int16_t C6; + int16_t C7; + int16_t C8; + int16_t K0; + int16_t K1; + int16_t K2; +}; + +#define VFE_LA_TABLE_LENGTH 256 +struct vfe_cmd_la_config { + uint8_t enable; + int16_t table[VFE_LA_TABLE_LENGTH]; +}; + +#define VFE_GAMMA_TABLE_LENGTH 256 +enum VFE_RGB_GAMMA_TABLE_SELECT { + RGB_GAMMA_CH0_SELECTED, + RGB_GAMMA_CH1_SELECTED, + RGB_GAMMA_CH2_SELECTED, + RGB_GAMMA_CH0_CH1_SELECTED, + RGB_GAMMA_CH0_CH2_SELECTED, + RGB_GAMMA_CH1_CH2_SELECTED, + RGB_GAMMA_CH0_CH1_CH2_SELECTED +}; + +struct vfe_cmd_rgb_gamma_config { + uint8_t enable; + enum VFE_RGB_GAMMA_TABLE_SELECT channelSelect; + int16_t table[VFE_GAMMA_TABLE_LENGTH]; +}; + +struct vfe_cmd_chroma_enhan_config { + uint8_t enable; + int16_t am; + int16_t ap; + int16_t bm; + int16_t bp; + int16_t cm; + int16_t cp; + int16_t dm; + int16_t dp; + int16_t kcr; + int16_t kcb; + int16_t RGBtoYConversionV0; + int16_t RGBtoYConversionV1; + int16_t RGBtoYConversionV2; + uint8_t RGBtoYConversionOffset; +}; + +struct vfe_cmd_chroma_suppression_config { + uint8_t enable; + uint8_t m1; + uint8_t m3; + uint8_t n1; + uint8_t n3; + uint8_t nn1; + uint8_t mm1; +}; + +struct vfe_cmd_asf_config { + uint8_t enable; + uint8_t smoothFilterEnabled; + uint8_t sharpMode; + uint8_t smoothCoefCenter; + uint8_t smoothCoefSurr; + uint8_t normalizeFactor; + uint8_t sharpK1; + uint8_t sharpK2; + uint8_t sharpThreshE1; + int8_t sharpThreshE2; + int8_t sharpThreshE3; + int8_t sharpThreshE4; + int8_t sharpThreshE5; + int8_t filter1Coefficients[9]; + int8_t filter2Coefficients[9]; + uint8_t cropEnable; + uint16_t cropFirstPixel; + uint16_t cropLastPixel; + uint16_t cropFirstLine; + uint16_t cropLastLine; +}; + +struct vfe_cmd_asf_update { + uint8_t enable; + uint8_t smoothFilterEnabled; + uint8_t sharpMode; + uint8_t smoothCoefCenter; + uint8_t smoothCoefSurr; + uint8_t normalizeFactor; + uint8_t sharpK1; + uint8_t sharpK2; + uint8_t sharpThreshE1; + int8_t sharpThreshE2; + int8_t sharpThreshE3; + int8_t sharpThreshE4; + int8_t sharpThreshE5; + int8_t filter1Coefficients[9]; + int8_t filter2Coefficients[9]; + uint8_t cropEnable; +}; + +enum VFE_TEST_GEN_SYNC_EDGE { + VFE_TEST_GEN_SYNC_EDGE_ActiveHigh, + VFE_TEST_GEN_SYNC_EDGE_ActiveLow +}; + +struct vfe_cmd_test_gen_start { + uint8_t pixelDataSelect; + uint8_t systematicDataSelect; + enum VFE_TEST_GEN_SYNC_EDGE hsyncEdge; + enum VFE_TEST_GEN_SYNC_EDGE vsyncEdge; + uint16_t numFrame; + enum VFE_RAW_PIXEL_DATA_SIZE pixelDataSize; + uint16_t imageWidth; + uint16_t imageHeight; + uint32_t startOfFrameOffset; + uint32_t endOfFrameNOffset; + uint16_t startOfLineOffset; + uint16_t endOfLineNOffset; + uint16_t hbi; + uint8_t vblEnable; + uint16_t vbl; + uint8_t startOfFrameDummyLine; + uint8_t endOfFrameDummyLine; + uint8_t unicolorBarEnable; + uint8_t colorBarsSplitEnable; + uint8_t unicolorBarSelect; + enum VFE_START_PIXEL_PATTERN colorBarsPixelPattern; + uint8_t colorBarsRotatePeriod; + uint16_t testGenRandomSeed; +}; + +struct vfe_cmd_bus_pm_start { + uint8_t output2YWrPmEnable; + uint8_t output2CbcrWrPmEnable; + uint8_t output1YWrPmEnable; + uint8_t output1CbcrWrPmEnable; +}; + +struct vfe_cmd_camif_frame_update { + struct vfe_cmds_camif_frame camifFrame; +}; + +struct vfe_cmd_sync_timer_setting { + uint8_t whichSyncTimer; + uint8_t operation; + uint8_t polarity; + uint16_t repeatCount; + uint16_t hsyncCount; + uint32_t pclkCount; + uint32_t outputDuration; +}; + +struct vfe_cmd_async_timer_setting { + uint8_t whichAsyncTimer; + uint8_t operation; + uint8_t polarity; + uint16_t repeatCount; + uint16_t inactiveCount; + uint32_t activeCount; +}; + +struct vfe_frame_skip_counts { + uint32_t totalFrameCount; + uint32_t output1Count; + uint32_t output2Count; +}; + +enum VFE_AXI_RD_UNPACK_HBI_SEL { + VFE_AXI_RD_HBI_32_CLOCK_CYCLES, + VFE_AXI_RD_HBI_64_CLOCK_CYCLES, + VFE_AXI_RD_HBI_128_CLOCK_CYCLES, + VFE_AXI_RD_HBI_256_CLOCK_CYCLES, + VFE_AXI_RD_HBI_512_CLOCK_CYCLES, + VFE_AXI_RD_HBI_1024_CLOCK_CYCLES, + VFE_AXI_RD_HBI_2048_CLOCK_CYCLES, + VFE_AXI_RD_HBI_4096_CLOCK_CYCLES +}; + +struct vfe_cmd_axi_input_config { + uint32_t fragAddr[4]; + uint8_t totalFragmentCount; + uint16_t ySize; + uint16_t xOffset; + uint16_t xSize; + uint16_t rowIncrement; + uint16_t numOfRows; + enum VFE_AXI_BURST_LENGTH burstLength; + uint8_t unpackPhase; + enum VFE_AXI_RD_UNPACK_HBI_SEL unpackHbi; + enum VFE_RAW_PIXEL_DATA_SIZE pixelSize; + uint8_t padRepeatCountLeft; + uint8_t padRepeatCountRight; + uint8_t padRepeatCountTop; + uint8_t padRepeatCountBottom; + uint8_t padLeftComponentSelectCycle0; + uint8_t padLeftComponentSelectCycle1; + uint8_t padLeftComponentSelectCycle2; + uint8_t padLeftComponentSelectCycle3; + uint8_t padLeftStopCycle0; + uint8_t padLeftStopCycle1; + uint8_t padLeftStopCycle2; + uint8_t padLeftStopCycle3; + uint8_t padRightComponentSelectCycle0; + uint8_t padRightComponentSelectCycle1; + uint8_t padRightComponentSelectCycle2; + uint8_t padRightComponentSelectCycle3; + uint8_t padRightStopCycle0; + uint8_t padRightStopCycle1; + uint8_t padRightStopCycle2; + uint8_t padRightStopCycle3; + uint8_t padTopLineCount; + uint8_t padBottomLineCount; +}; + +struct vfe_interrupt_status { + uint8_t camifErrorIrq; + uint8_t camifSofIrq; + uint8_t camifEolIrq; + uint8_t camifEofIrq; + uint8_t camifEpoch1Irq; + uint8_t camifEpoch2Irq; + uint8_t camifOverflowIrq; + uint8_t ceIrq; + uint8_t regUpdateIrq; + uint8_t resetAckIrq; + uint8_t encYPingpongIrq; + uint8_t encCbcrPingpongIrq; + uint8_t viewYPingpongIrq; + uint8_t viewCbcrPingpongIrq; + uint8_t rdPingpongIrq; + uint8_t afPingpongIrq; + uint8_t awbPingpongIrq; + uint8_t histPingpongIrq; + uint8_t encIrq; + uint8_t viewIrq; + uint8_t busOverflowIrq; + uint8_t afOverflowIrq; + uint8_t awbOverflowIrq; + uint8_t syncTimer0Irq; + uint8_t syncTimer1Irq; + uint8_t syncTimer2Irq; + uint8_t asyncTimer0Irq; + uint8_t asyncTimer1Irq; + uint8_t asyncTimer2Irq; + uint8_t asyncTimer3Irq; + uint8_t axiErrorIrq; + uint8_t violationIrq; + uint8_t anyErrorIrqs; + uint8_t anyOutput1PathIrqs; + uint8_t anyOutput2PathIrqs; + uint8_t anyOutputPathIrqs; + uint8_t anyAsyncTimerIrqs; + uint8_t anySyncTimerIrqs; + uint8_t anyIrqForActiveStatesOnly; +}; + +enum VFE_MESSAGE_ID { + VFE_MSG_ID_RESET_ACK, + VFE_MSG_ID_START_ACK, + VFE_MSG_ID_STOP_ACK, + VFE_MSG_ID_UPDATE_ACK, + VFE_MSG_ID_OUTPUT1, + VFE_MSG_ID_OUTPUT2, + VFE_MSG_ID_SNAPSHOT_DONE, + VFE_MSG_ID_STATS_AUTOFOCUS, + VFE_MSG_ID_STATS_WB_EXP, + VFE_MSG_ID_EPOCH1, + VFE_MSG_ID_EPOCH2, + VFE_MSG_ID_SYNC_TIMER0_DONE, + VFE_MSG_ID_SYNC_TIMER1_DONE, + VFE_MSG_ID_SYNC_TIMER2_DONE, + VFE_MSG_ID_ASYNC_TIMER0_DONE, + VFE_MSG_ID_ASYNC_TIMER1_DONE, + VFE_MSG_ID_ASYNC_TIMER2_DONE, + VFE_MSG_ID_ASYNC_TIMER3_DONE, + VFE_MSG_ID_AF_OVERFLOW, + VFE_MSG_ID_AWB_OVERFLOW, + VFE_MSG_ID_AXI_ERROR, + VFE_MSG_ID_CAMIF_OVERFLOW, + VFE_MSG_ID_VIOLATION, + VFE_MSG_ID_CAMIF_ERROR, + VFE_MSG_ID_BUS_OVERFLOW, +}; + +struct vfe_msg_stats_autofocus { + uint32_t afBuffer; + uint32_t frameCounter; +}; + +struct vfe_msg_stats_wb_exp { + uint32_t awbBuffer; + uint32_t frameCounter; +}; + +struct vfe_frame_bpc_info { + uint32_t greenDefectPixelCount; + uint32_t redBlueDefectPixelCount; +}; + +struct vfe_frame_asf_info { + uint32_t asfMaxEdge; + uint32_t asfHbiCount; +}; + +struct vfe_msg_camif_status { + uint8_t camifState; + uint32_t pixelCount; + uint32_t lineCount; +}; + +struct vfe_bus_pm_per_path { + uint32_t yWrPmStats0; + uint32_t yWrPmStats1; + uint32_t cbcrWrPmStats0; + uint32_t cbcrWrPmStats1; +}; + +struct vfe_bus_performance_monitor { + struct vfe_bus_pm_per_path encPathPmInfo; + struct vfe_bus_pm_per_path viewPathPmInfo; +}; + +struct vfe_irq_thread_msg { + uint32_t vfeIrqStatus; + uint32_t camifStatus; + uint32_t demosaicStatus; + uint32_t asfMaxEdge; + struct vfe_bus_performance_monitor pmInfo; +}; + +struct vfe_msg_output { + uint32_t yBuffer; + uint32_t cbcrBuffer; + struct vfe_frame_bpc_info bpcInfo; + struct vfe_frame_asf_info asfInfo; + uint32_t frameCounter; + struct vfe_bus_pm_per_path pmData; +}; + +struct vfe_message { + enum VFE_MESSAGE_ID _d; + union { + struct vfe_msg_output msgOutput1; + struct vfe_msg_output msgOutput2; + struct vfe_msg_stats_autofocus msgStatsAf; + struct vfe_msg_stats_wb_exp msgStatsWbExp; + struct vfe_msg_camif_status msgCamifError; + struct vfe_bus_performance_monitor msgBusOverflow; + } _u; +}; + +/* New one for 8k */ +struct msm_vfe_command_8k { + int32_t id; + uint16_t length; + void *value; +}; + +struct vfe_frame_extra { + struct vfe_frame_bpc_info bpcInfo; + struct vfe_frame_asf_info asfInfo; + uint32_t frameCounter; + struct vfe_bus_pm_per_path pmData; +}; +#endif /* __MSM_VFE8X_H__ */ diff --git a/drivers/media/video/msm/msm_vfe8x_proc.c b/drivers/media/video/msm/msm_vfe8x_proc.c new file mode 100644 index 0000000000000..bb65013402113 --- /dev/null +++ b/drivers/media/video/msm/msm_vfe8x_proc.c @@ -0,0 +1,3995 @@ +/* +* Copyright (C) 2008-2009 QUALCOMM Incorporated. +*/ +#include +#include +#include +#include +#include +#include +#include +#include "msm_vfe8x_proc.h" +#include + +struct msm_vfe8x_ctrl { + /* bit 1:0 ENC_IRQ_MASK = 0x11: + * generate IRQ when both y and cbcr frame is ready. */ + + /* bit 1:0 VIEW_IRQ_MASK= 0x11: + * generate IRQ when both y and cbcr frame is ready. */ + struct vfe_irq_composite_mask_config vfeIrqCompositeMaskLocal; + struct vfe_module_enable vfeModuleEnableLocal; + struct vfe_camif_cfg_data vfeCamifConfigLocal; + struct vfe_interrupt_mask vfeImaskLocal; + struct vfe_stats_cmd_data vfeStatsCmdLocal; + struct vfe_bus_cfg_data vfeBusConfigLocal; + struct vfe_cmd_bus_pm_start vfeBusPmConfigLocal; + struct vfe_bus_cmd_data vfeBusCmdLocal; + enum vfe_interrupt_name vfeInterruptNameLocal; + uint32_t vfeLaBankSel; + struct vfe_gamma_lut_sel vfeGammaLutSel; + + boolean vfeStartAckPendingFlag; + boolean vfeStopAckPending; + boolean vfeResetAckPending; + boolean vfeUpdateAckPending; + + enum VFE_AXI_OUTPUT_MODE axiOutputMode; + enum VFE_START_OPERATION_MODE vfeOperationMode; + + uint32_t vfeSnapShotCount; + uint32_t vfeRequestedSnapShotCount; + boolean vfeStatsPingPongReloadFlag; + uint32_t vfeFrameId; + + struct vfe_cmd_frame_skip_config vfeFrameSkip; + uint32_t vfeFrameSkipPattern; + uint8_t vfeFrameSkipCount; + uint8_t vfeFrameSkipPeriod; + + boolean vfeTestGenStartFlag; + uint32_t vfeImaskPacked; + uint32_t vfeImaskCompositePacked; + enum VFE_RAW_PIXEL_DATA_SIZE axiInputDataSize; + struct vfe_irq_thread_msg vfeIrqThreadMsgLocal; + + struct vfe_output_path_combo viewPath; + struct vfe_output_path_combo encPath; + struct vfe_frame_skip_counts vfeDroppedFrameCounts; + struct vfe_stats_control afStatsControl; + struct vfe_stats_control awbStatsControl; + + enum VFE_STATE vstate; + + spinlock_t ack_lock; + spinlock_t state_lock; + spinlock_t io_lock; + + struct msm_vfe_callback *resp; + uint32_t extlen; + void *extdata; + + spinlock_t tasklet_lock; + struct list_head tasklet_q; + + int vfeirq; + void __iomem *vfebase; + + void *syncdata; +}; +static struct msm_vfe8x_ctrl *ctrl; +static irqreturn_t vfe_parse_irq(int irq_num, void *data); + +struct isr_queue_cmd { + struct list_head list; + struct vfe_interrupt_status vfeInterruptStatus; + struct vfe_frame_asf_info vfeAsfFrameInfo; + struct vfe_frame_bpc_info vfeBpcFrameInfo; + struct vfe_msg_camif_status vfeCamifStatusLocal; + struct vfe_bus_performance_monitor vfePmData; +}; + +static void vfe_prog_hw(uint8_t *hwreg, + uint32_t *inptr, uint32_t regcnt) +{ + /* unsigned long flags; */ + uint32_t i; + uint32_t *p; + + /* @todo This is causing issues, need further investigate */ + /* spin_lock_irqsave(&ctrl->io_lock, flags); */ + + p = (uint32_t *)(hwreg); + for (i = 0; i < (regcnt >> 2); i++) + writel(*inptr++, p++); + /* *p++ = *inptr++; */ + + /* spin_unlock_irqrestore(&ctrl->io_lock, flags); */ +} + +static void vfe_read_reg_values(uint8_t *hwreg, + uint32_t *dest, uint32_t count) +{ + /* unsigned long flags; */ + uint32_t *temp; + uint32_t i; + + /* @todo This is causing issues, need further investigate */ + /* spin_lock_irqsave(&ctrl->io_lock, flags); */ + + temp = (uint32_t *)(hwreg); + for (i = 0; i < count; i++) + *dest++ = *temp++; + + /* spin_unlock_irqrestore(&ctrl->io_lock, flags); */ +} + +static struct vfe_irqenable vfe_read_irq_mask(void) +{ + /* unsigned long flags; */ + uint32_t *temp; + struct vfe_irqenable rc; + + memset(&rc, 0, sizeof(rc)); + + /* @todo This is causing issues, need further investigate */ + /* spin_lock_irqsave(&ctrl->io_lock, flags); */ + temp = (uint32_t *)(ctrl->vfebase + VFE_IRQ_MASK); + + rc = *((struct vfe_irqenable *)temp); + /* spin_unlock_irqrestore(&ctrl->io_lock, flags); */ + + return rc; +} + +static void +vfe_set_bus_pipo_addr(struct vfe_output_path_combo *vpath, + struct vfe_output_path_combo *epath) +{ + vpath->yPath.hwRegPingAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_VIEW_Y_WR_PING_ADDR); + vpath->yPath.hwRegPongAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_VIEW_Y_WR_PONG_ADDR); + vpath->cbcrPath.hwRegPingAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_VIEW_CBCR_WR_PING_ADDR); + vpath->cbcrPath.hwRegPongAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_VIEW_CBCR_WR_PONG_ADDR); + + epath->yPath.hwRegPingAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_ENC_Y_WR_PING_ADDR); + epath->yPath.hwRegPongAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_ENC_Y_WR_PONG_ADDR); + epath->cbcrPath.hwRegPingAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PING_ADDR); + epath->cbcrPath.hwRegPongAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PONG_ADDR); +} + +static void vfe_axi_output(struct vfe_cmd_axi_output_config *in, + struct vfe_output_path_combo *out1, + struct vfe_output_path_combo *out2, uint16_t out) +{ + struct vfe_axi_out_cfg cmd; + + uint16_t temp; + uint32_t burstLength; + + /* force it to burst length 4, hardware does not support it. */ + burstLength = 1; + + /* AXI Output 2 Y Configuration*/ + /* VFE_BUS_ENC_Y_WR_PING_ADDR */ + cmd.out2YPingAddr = out2->yPath.addressBuffer[0]; + + /* VFE_BUS_ENC_Y_WR_PONG_ADDR */ + cmd.out2YPongAddr = out2->yPath.addressBuffer[1]; + + /* VFE_BUS_ENC_Y_WR_IMAGE_SIZE */ + cmd.out2YImageHeight = in->output2.outputY.imageHeight; + /* convert the image width and row increment to be in + * unit of 64bit (8 bytes) */ + temp = (in->output2.outputY.imageWidth + (out - 1)) / + out; + cmd.out2YImageWidthin64bit = temp; + + /* VFE_BUS_ENC_Y_WR_BUFFER_CFG */ + cmd.out2YBurstLength = burstLength; + cmd.out2YNumRows = in->output2.outputY.outRowCount; + temp = (in->output2.outputY.outRowIncrement + (out - 1)) / + out; + cmd.out2YRowIncrementIn64bit = temp; + + /* AXI Output 2 Cbcr Configuration*/ + /* VFE_BUS_ENC_Cbcr_WR_PING_ADDR */ + cmd.out2CbcrPingAddr = out2->cbcrPath.addressBuffer[0]; + + /* VFE_BUS_ENC_Cbcr_WR_PONG_ADDR */ + cmd.out2CbcrPongAddr = out2->cbcrPath.addressBuffer[1]; + + /* VFE_BUS_ENC_Cbcr_WR_IMAGE_SIZE */ + cmd.out2CbcrImageHeight = in->output2.outputCbcr.imageHeight; + temp = (in->output2.outputCbcr.imageWidth + (out - 1)) / + out; + cmd.out2CbcrImageWidthIn64bit = temp; + + /* VFE_BUS_ENC_Cbcr_WR_BUFFER_CFG */ + cmd.out2CbcrBurstLength = burstLength; + cmd.out2CbcrNumRows = in->output2.outputCbcr.outRowCount; + temp = (in->output2.outputCbcr.outRowIncrement + (out - 1)) / + out; + cmd.out2CbcrRowIncrementIn64bit = temp; + + /* AXI Output 1 Y Configuration */ + /* VFE_BUS_VIEW_Y_WR_PING_ADDR */ + cmd.out1YPingAddr = out1->yPath.addressBuffer[0]; + + /* VFE_BUS_VIEW_Y_WR_PONG_ADDR */ + cmd.out1YPongAddr = out1->yPath.addressBuffer[1]; + + /* VFE_BUS_VIEW_Y_WR_IMAGE_SIZE */ + cmd.out1YImageHeight = in->output1.outputY.imageHeight; + temp = (in->output1.outputY.imageWidth + (out - 1)) / + out; + cmd.out1YImageWidthin64bit = temp; + + /* VFE_BUS_VIEW_Y_WR_BUFFER_CFG */ + cmd.out1YBurstLength = burstLength; + cmd.out1YNumRows = in->output1.outputY.outRowCount; + + temp = + (in->output1.outputY.outRowIncrement + + (out - 1)) / out; + cmd.out1YRowIncrementIn64bit = temp; + + /* AXI Output 1 Cbcr Configuration*/ + cmd.out1CbcrPingAddr = out1->cbcrPath.addressBuffer[0]; + + /* VFE_BUS_VIEW_Cbcr_WR_PONG_ADDR */ + cmd.out1CbcrPongAddr = + out1->cbcrPath.addressBuffer[1]; + + /* VFE_BUS_VIEW_Cbcr_WR_IMAGE_SIZE */ + cmd.out1CbcrImageHeight = in->output1.outputCbcr.imageHeight; + temp = (in->output1.outputCbcr.imageWidth + + (out - 1)) / out; + cmd.out1CbcrImageWidthIn64bit = temp; + + cmd.out1CbcrBurstLength = burstLength; + cmd.out1CbcrNumRows = in->output1.outputCbcr.outRowCount; + temp = + (in->output1.outputCbcr.outRowIncrement + + (out - 1)) / out; + + cmd.out1CbcrRowIncrementIn64bit = temp; + + vfe_prog_hw(ctrl->vfebase + VFE_BUS_ENC_Y_WR_PING_ADDR, + (uint32_t *)&cmd, sizeof(cmd)); +} + +static void vfe_reg_bus_cfg(struct vfe_bus_cfg_data *in) +{ + struct vfe_axi_bus_cfg cmd; + + cmd.stripeRdPathEn = in->stripeRdPathEn; + cmd.encYWrPathEn = in->encYWrPathEn; + cmd.encCbcrWrPathEn = in->encCbcrWrPathEn; + cmd.viewYWrPathEn = in->viewYWrPathEn; + cmd.viewCbcrWrPathEn = in->viewCbcrWrPathEn; + cmd.rawPixelDataSize = (uint32_t)in->rawPixelDataSize; + cmd.rawWritePathSelect = (uint32_t)in->rawWritePathSelect; + + /* program vfe_bus_cfg */ + writel(*((uint32_t *)&cmd), ctrl->vfebase + VFE_BUS_CFG); +} + +static void vfe_reg_camif_config(struct vfe_camif_cfg_data *in) +{ + struct VFE_CAMIFConfigType cfg; + + memset(&cfg, 0, sizeof(cfg)); + + cfg.VSyncEdge = + in->camifCfgFromCmd.vSyncEdge; + + cfg.HSyncEdge = + in->camifCfgFromCmd.hSyncEdge; + + cfg.syncMode = + in->camifCfgFromCmd.syncMode; + + cfg.vfeSubsampleEnable = + in->camifCfgFromCmd.vfeSubSampleEnable; + + cfg.busSubsampleEnable = + in->camifCfgFromCmd.busSubSampleEnable; + + cfg.camif2vfeEnable = + in->camif2OutputEnable; + + cfg.camif2busEnable = + in->camif2BusEnable; + + cfg.irqSubsampleEnable = + in->camifCfgFromCmd.irqSubSampleEnable; + + cfg.binningEnable = + in->camifCfgFromCmd.binningEnable; + + cfg.misrEnable = + in->camifCfgFromCmd.misrEnable; + + /* program camif_config */ + writel(*((uint32_t *)&cfg), ctrl->vfebase + CAMIF_CONFIG); +} + +static void vfe_reg_bus_cmd(struct vfe_bus_cmd_data *in) +{ + struct vfe_buscmd cmd; + memset(&cmd, 0, sizeof(cmd)); + + cmd.stripeReload = in->stripeReload; + cmd.busPingpongReload = in->busPingpongReload; + cmd.statsPingpongReload = in->statsPingpongReload; + + writel(*((uint32_t *)&cmd), ctrl->vfebase + VFE_BUS_CMD); + + CDBG("bus command = 0x%x\n", (*((uint32_t *)&cmd))); + + /* this is needed, as the control bits are pulse based. + * Don't want to reload bus pingpong again. */ + in->busPingpongReload = 0; + in->statsPingpongReload = 0; + in->stripeReload = 0; +} + +static void vfe_reg_module_cfg(struct vfe_module_enable *in) +{ + struct vfe_mod_enable ena; + + memset(&ena, 0, sizeof(ena)); + + ena.blackLevelCorrectionEnable = in->blackLevelCorrectionEnable; + ena.lensRollOffEnable = in->lensRollOffEnable; + ena.demuxEnable = in->demuxEnable; + ena.chromaUpsampleEnable = in->chromaUpsampleEnable; + ena.demosaicEnable = in->demosaicEnable; + ena.statsEnable = in->statsEnable; + ena.cropEnable = in->cropEnable; + ena.mainScalerEnable = in->mainScalerEnable; + ena.whiteBalanceEnable = in->whiteBalanceEnable; + ena.colorCorrectionEnable = in->colorCorrectionEnable; + ena.yHistEnable = in->yHistEnable; + ena.skinToneEnable = in->skinToneEnable; + ena.lumaAdaptationEnable = in->lumaAdaptationEnable; + ena.rgbLUTEnable = in->rgbLUTEnable; + ena.chromaEnhanEnable = in->chromaEnhanEnable; + ena.asfEnable = in->asfEnable; + ena.chromaSuppressionEnable = in->chromaSuppressionEnable; + ena.chromaSubsampleEnable = in->chromaSubsampleEnable; + ena.scaler2YEnable = in->scaler2YEnable; + ena.scaler2CbcrEnable = in->scaler2CbcrEnable; + + writel(*((uint32_t *)&ena), ctrl->vfebase + VFE_MODULE_CFG); +} + +static void vfe_program_dmi_cfg(enum VFE_DMI_RAM_SEL bankSel) +{ + /* set bit 8 for auto increment. */ + uint32_t value = (uint32_t) ctrl->vfebase + VFE_DMI_CFG_DEFAULT; + + value += (uint32_t)bankSel; + /* CDBG("dmi cfg input bank is 0x%x\n", bankSel); */ + + writel(value, ctrl->vfebase + VFE_DMI_CFG); + writel(0, ctrl->vfebase + VFE_DMI_ADDR); +} + +static void vfe_write_lens_roll_off_table( + struct vfe_cmd_roll_off_config *in) +{ + uint16_t i; + uint32_t data; + + uint16_t *initGr = in->initTableGr; + uint16_t *initGb = in->initTableGb; + uint16_t *initB = in->initTableB; + uint16_t *initR = in->initTableR; + + int16_t *pDeltaGr = in->deltaTableGr; + int16_t *pDeltaGb = in->deltaTableGb; + int16_t *pDeltaB = in->deltaTableB; + int16_t *pDeltaR = in->deltaTableR; + + vfe_program_dmi_cfg(ROLLOFF_RAM); + + /* first pack and write init table */ + for (i = 0; i < VFE_ROLL_OFF_INIT_TABLE_SIZE; i++) { + data = (((uint32_t)(*initR)) & 0x0000FFFF) | + (((uint32_t)(*initGr)) << 16); + initR++; + initGr++; + + writel(data, ctrl->vfebase + VFE_DMI_DATA_LO); + + data = (((uint32_t)(*initB)) & 0x0000FFFF) | + (((uint32_t)(*initGr))<<16); + initB++; + initGb++; + + writel(data, ctrl->vfebase + VFE_DMI_DATA_LO); + } + + /* there are gaps between the init table and delta table, + * set the offset for delta table. */ + writel(LENS_ROLL_OFF_DELTA_TABLE_OFFSET, + ctrl->vfebase + VFE_DMI_ADDR); + + /* pack and write delta table */ + for (i = 0; i < VFE_ROLL_OFF_DELTA_TABLE_SIZE; i++) { + data = (((int32_t)(*pDeltaR)) & 0x0000FFFF) | + (((int32_t)(*pDeltaGr))<<16); + pDeltaR++; + pDeltaGr++; + + writel(data, ctrl->vfebase + VFE_DMI_DATA_LO); + + data = (((int32_t)(*pDeltaB)) & 0x0000FFFF) | + (((int32_t)(*pDeltaGb))<<16); + pDeltaB++; + pDeltaGb++; + + writel(data, ctrl->vfebase + VFE_DMI_DATA_LO); + } + + /* After DMI transfer, to make it safe, need to set the + * DMI_CFG to unselect any SRAM + */ + /* unselect the SRAM Bank. */ + writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG); +} + +static void vfe_set_default_reg_values(void) +{ + writel(0x800080, ctrl->vfebase + VFE_DEMUX_GAIN_0); + writel(0x800080, ctrl->vfebase + VFE_DEMUX_GAIN_1); + writel(0xFFFFF, ctrl->vfebase + VFE_CGC_OVERRIDE); + + /* default frame drop period and pattern */ + writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_CFG); + writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_CFG); + writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_PATTERN); + writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_ENC_CBCR_PATTERN); + writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_CFG); + writel(0x1f, ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR_CFG); + writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_PATTERN); + writel(0xFFFFFFFF, ctrl->vfebase + VFE_FRAMEDROP_VIEW_CBCR_PATTERN); + writel(0, ctrl->vfebase + VFE_CLAMP_MIN_CFG); + writel(0xFFFFFF, ctrl->vfebase + VFE_CLAMP_MAX_CFG); +} + +static void vfe_config_demux(uint32_t period, uint32_t even, uint32_t odd) +{ + writel(period, ctrl->vfebase + VFE_DEMUX_CFG); + writel(even, ctrl->vfebase + VFE_DEMUX_EVEN_CFG); + writel(odd, ctrl->vfebase + VFE_DEMUX_ODD_CFG); +} + +static void vfe_pm_stop(void) +{ + writel(VFE_PERFORMANCE_MONITOR_STOP, ctrl->vfebase + VFE_BUS_PM_CMD); +} + +static void vfe_program_bus_rd_irq_en(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_BUS_PINGPONG_IRQ_EN); +} + +static void vfe_camif_go(void) +{ + writel(CAMIF_COMMAND_START, ctrl->vfebase + CAMIF_COMMAND); +} + +static void vfe_camif_stop_immediately(void) +{ + writel(CAMIF_COMMAND_STOP_IMMEDIATELY, ctrl->vfebase + CAMIF_COMMAND); + writel(0, ctrl->vfebase + VFE_CGC_OVERRIDE); +} + +static void vfe_program_reg_update_cmd(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_REG_UPDATE_CMD); +} + +static void vfe_program_bus_cmd(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_BUS_CMD); +} + +static void vfe_program_global_reset_cmd(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_GLOBAL_RESET_CMD); +} + +static void vfe_program_axi_cmd(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_AXI_CMD); +} + +static void vfe_program_irq_composite_mask(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_IRQ_COMPOSITE_MASK); +} + +static inline void vfe_program_irq_mask(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_IRQ_MASK); +} + +static void vfe_program_chroma_upsample_cfg(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_CHROMA_UPSAMPLE_CFG); +} + +static uint32_t vfe_read_axi_status(void) +{ + return readl(ctrl->vfebase + VFE_AXI_STATUS); +} + +static uint32_t vfe_read_pm_status_in_raw_capture(void) +{ + return readl(ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PM_STATS_1); +} + +static void +vfe_set_stats_pingpong_address(struct vfe_stats_control *afControl, + struct vfe_stats_control *awbControl) +{ + afControl->hwRegPingAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR); + afControl->hwRegPongAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR); + + awbControl->hwRegPingAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR); + awbControl->hwRegPongAddress = (uint8_t *) + (ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR); +} + +static uint32_t vfe_read_camif_status(void) +{ + return readl(ctrl->vfebase + CAMIF_STATUS); +} + +static void vfe_program_lut_bank_sel(struct vfe_gamma_lut_sel *in) +{ + struct VFE_GammaLutSelect_ConfigCmdType cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.ch0BankSelect = in->ch0BankSelect; + cmd.ch1BankSelect = in->ch1BankSelect; + cmd.ch2BankSelect = in->ch2BankSelect; + CDBG("VFE gamma lut bank selection is 0x%x\n", *((uint32_t *)&cmd)); + vfe_prog_hw(ctrl->vfebase + VFE_LUT_BANK_SEL, + (uint32_t *)&cmd, sizeof(cmd)); +} + +static void vfe_program_stats_cmd(struct vfe_stats_cmd_data *in) +{ + struct VFE_StatsCmdType stats; + memset(&stats, 0, sizeof(stats)); + + stats.autoFocusEnable = in->autoFocusEnable; + stats.axwEnable = in->axwEnable; + stats.histEnable = in->histEnable; + stats.clearHistEnable = in->clearHistEnable; + stats.histAutoClearEnable = in->histAutoClearEnable; + stats.colorConversionEnable = in->colorConversionEnable; + + writel(*((uint32_t *)&stats), ctrl->vfebase + VFE_STATS_CMD); +} + +static void vfe_pm_start(struct vfe_cmd_bus_pm_start *in) +{ + struct VFE_Bus_Pm_ConfigCmdType cmd; + memset(&cmd, 0, sizeof(struct VFE_Bus_Pm_ConfigCmdType)); + + cmd.output2YWrPmEnable = in->output2YWrPmEnable; + cmd.output2CbcrWrPmEnable = in->output2CbcrWrPmEnable; + cmd.output1YWrPmEnable = in->output1YWrPmEnable; + cmd.output1CbcrWrPmEnable = in->output1CbcrWrPmEnable; + + vfe_prog_hw(ctrl->vfebase + VFE_BUS_PM_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +static void vfe_8k_pm_start(struct vfe_cmd_bus_pm_start *in) +{ + in->output1CbcrWrPmEnable = ctrl->vfeBusConfigLocal.viewCbcrWrPathEn; + in->output1YWrPmEnable = ctrl->vfeBusConfigLocal.viewYWrPathEn; + in->output2CbcrWrPmEnable = ctrl->vfeBusConfigLocal.encCbcrWrPathEn; + in->output2YWrPmEnable = ctrl->vfeBusConfigLocal.encYWrPathEn; + + if (in->output1CbcrWrPmEnable || in->output1YWrPmEnable) + ctrl->viewPath.pmEnabled = TRUE; + + if (in->output2CbcrWrPmEnable || in->output2YWrPmEnable) + ctrl->encPath.pmEnabled = TRUE; + + vfe_pm_start(in); + + writel(VFE_PERFORMANCE_MONITOR_GO, ctrl->vfebase + VFE_BUS_PM_CMD); +} + +static uint32_t vfe_irq_pack(struct vfe_interrupt_mask data) +{ + struct vfe_irqenable packedData; + + memset(&packedData, 0, sizeof(packedData)); + + packedData.camifErrorIrq = data.camifErrorIrq; + packedData.camifSofIrq = data.camifSofIrq; + packedData.camifEolIrq = data.camifEolIrq; + packedData.camifEofIrq = data.camifEofIrq; + packedData.camifEpoch1Irq = data.camifEpoch1Irq; + packedData.camifEpoch2Irq = data.camifEpoch2Irq; + packedData.camifOverflowIrq = data.camifOverflowIrq; + packedData.ceIrq = data.ceIrq; + packedData.regUpdateIrq = data.regUpdateIrq; + packedData.resetAckIrq = data.resetAckIrq; + packedData.encYPingpongIrq = data.encYPingpongIrq; + packedData.encCbcrPingpongIrq = data.encCbcrPingpongIrq; + packedData.viewYPingpongIrq = data.viewYPingpongIrq; + packedData.viewCbcrPingpongIrq = data.viewCbcrPingpongIrq; + packedData.rdPingpongIrq = data.rdPingpongIrq; + packedData.afPingpongIrq = data.afPingpongIrq; + packedData.awbPingpongIrq = data.awbPingpongIrq; + packedData.histPingpongIrq = data.histPingpongIrq; + packedData.encIrq = data.encIrq; + packedData.viewIrq = data.viewIrq; + packedData.busOverflowIrq = data.busOverflowIrq; + packedData.afOverflowIrq = data.afOverflowIrq; + packedData.awbOverflowIrq = data.awbOverflowIrq; + packedData.syncTimer0Irq = data.syncTimer0Irq; + packedData.syncTimer1Irq = data.syncTimer1Irq; + packedData.syncTimer2Irq = data.syncTimer2Irq; + packedData.asyncTimer0Irq = data.asyncTimer0Irq; + packedData.asyncTimer1Irq = data.asyncTimer1Irq; + packedData.asyncTimer2Irq = data.asyncTimer2Irq; + packedData.asyncTimer3Irq = data.asyncTimer3Irq; + packedData.axiErrorIrq = data.axiErrorIrq; + packedData.violationIrq = data.violationIrq; + + return *((uint32_t *)&packedData); +} + +static uint32_t +vfe_irq_composite_pack(struct vfe_irq_composite_mask_config data) +{ + struct VFE_Irq_Composite_MaskType packedData; + + memset(&packedData, 0, sizeof(packedData)); + + packedData.encIrqComMaskBits = data.encIrqComMask; + packedData.viewIrqComMaskBits = data.viewIrqComMask; + packedData.ceDoneSelBits = data.ceDoneSel; + + return *((uint32_t *)&packedData); +} + +static void vfe_addr_convert(struct msm_vfe_phy_info *pinfo, + enum vfe_resp_msg type, void *data, void **ext, int32_t *elen) +{ + switch (type) { + case VFE_MSG_OUTPUT1: { + pinfo->y_phy = + ((struct vfe_message *)data)->_u.msgOutput1.yBuffer; + pinfo->cbcr_phy = + ((struct vfe_message *)data)->_u.msgOutput1.cbcrBuffer; + + ((struct vfe_frame_extra *)ctrl->extdata)->bpcInfo = + ((struct vfe_message *)data)->_u.msgOutput1.bpcInfo; + + ((struct vfe_frame_extra *)ctrl->extdata)->asfInfo = + ((struct vfe_message *)data)->_u.msgOutput1.asfInfo; + + ((struct vfe_frame_extra *)ctrl->extdata)->frameCounter = + ((struct vfe_message *)data)->_u.msgOutput1.frameCounter; + + ((struct vfe_frame_extra *)ctrl->extdata)->pmData = + ((struct vfe_message *)data)->_u.msgOutput1.pmData; + + *ext = ctrl->extdata; + *elen = ctrl->extlen; + } + break; + + case VFE_MSG_OUTPUT2: { + pinfo->y_phy = + ((struct vfe_message *)data)->_u.msgOutput2.yBuffer; + pinfo->cbcr_phy = + ((struct vfe_message *)data)->_u.msgOutput2.cbcrBuffer; + + CDBG("vfe_addr_convert, pinfo->y_phy = 0x%x\n", pinfo->y_phy); + CDBG("vfe_addr_convert, pinfo->cbcr_phy = 0x%x\n", + pinfo->cbcr_phy); + + ((struct vfe_frame_extra *)ctrl->extdata)->bpcInfo = + ((struct vfe_message *)data)->_u.msgOutput2.bpcInfo; + + ((struct vfe_frame_extra *)ctrl->extdata)->asfInfo = + ((struct vfe_message *)data)->_u.msgOutput2.asfInfo; + + ((struct vfe_frame_extra *)ctrl->extdata)->frameCounter = + ((struct vfe_message *)data)->_u.msgOutput2.frameCounter; + + ((struct vfe_frame_extra *)ctrl->extdata)->pmData = + ((struct vfe_message *)data)->_u.msgOutput2.pmData; + + *ext = ctrl->extdata; + *elen = ctrl->extlen; + } + break; + + case VFE_MSG_STATS_AF: + pinfo->sbuf_phy = + ((struct vfe_message *)data)->_u.msgStatsAf.afBuffer; + break; + + case VFE_MSG_STATS_WE: + pinfo->sbuf_phy = + ((struct vfe_message *)data)->_u.msgStatsWbExp.awbBuffer; + break; + + default: + break; + } /* switch */ +} + +static void +vfe_proc_ops(enum VFE_MESSAGE_ID id, void *msg, size_t len) +{ + struct msm_vfe_resp *rp; + + /* In 8k, OUTPUT1 & OUTPUT2 messages arrive before + * SNAPSHOT_DONE. We don't send such messages to user */ + + CDBG("ctrl->vfeOperationMode = %d, msgId = %d\n", + ctrl->vfeOperationMode, id); + + if ((ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT) && + (id == VFE_MSG_ID_OUTPUT1 || id == VFE_MSG_ID_OUTPUT2)) { + return; + } + + rp = ctrl->resp->vfe_alloc(sizeof(struct msm_vfe_resp), ctrl->syncdata); + if (!rp) { + CDBG("rp: cannot allocate buffer\n"); + return; + } + + CDBG("vfe_proc_ops, msgId = %d\n", id); + + rp->evt_msg.type = MSM_CAMERA_MSG; + rp->evt_msg.msg_id = id; + rp->evt_msg.len = len; + rp->evt_msg.data = msg; + + switch (rp->evt_msg.msg_id) { + case VFE_MSG_ID_SNAPSHOT_DONE: + rp->type = VFE_MSG_SNAPSHOT; + break; + + case VFE_MSG_ID_OUTPUT1: + rp->type = VFE_MSG_OUTPUT1; + vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT1, + rp->evt_msg.data, &(rp->extdata), + &(rp->extlen)); + break; + + case VFE_MSG_ID_OUTPUT2: + rp->type = VFE_MSG_OUTPUT2; + vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT2, + rp->evt_msg.data, &(rp->extdata), + &(rp->extlen)); + break; + + case VFE_MSG_ID_STATS_AUTOFOCUS: + rp->type = VFE_MSG_STATS_AF; + vfe_addr_convert(&(rp->phy), VFE_MSG_STATS_AF, + rp->evt_msg.data, NULL, NULL); + break; + + case VFE_MSG_ID_STATS_WB_EXP: + rp->type = VFE_MSG_STATS_WE; + vfe_addr_convert(&(rp->phy), VFE_MSG_STATS_WE, + rp->evt_msg.data, NULL, NULL); + break; + + default: + rp->type = VFE_MSG_GENERAL; + break; + } + + ctrl->resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, ctrl->syncdata); +} + +static void vfe_send_msg_no_payload(enum VFE_MESSAGE_ID id) +{ + struct vfe_message *msg; + + msg = kzalloc(sizeof(msg), GFP_ATOMIC); + if (!msg) + return; + + msg->_d = id; + vfe_proc_ops(id, msg, 0); +} + +static void vfe_send_bus_overflow_msg(void) +{ + struct vfe_message *msg; + msg = + kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); + if (!msg) + return; + + msg->_d = VFE_MSG_ID_BUS_OVERFLOW; +#if 0 + memcpy(&(msg->_u.msgBusOverflow), + &ctrl->vfePmData, sizeof(ctrl->vfePmData)); +#endif + + vfe_proc_ops(VFE_MSG_ID_BUS_OVERFLOW, + msg, sizeof(struct vfe_message)); +} + +static void vfe_send_camif_error_msg(void) +{ +#if 0 + struct vfe_message *msg; + msg = + kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); + if (!msg) + return; + + msg->_d = VFE_MSG_ID_CAMIF_ERROR; + memcpy(&(msg->_u.msgCamifError), + &ctrl->vfeCamifStatusLocal, sizeof(ctrl->vfeCamifStatusLocal)); + + vfe_proc_ops(VFE_MSG_ID_CAMIF_ERROR, + msg, sizeof(struct vfe_message)); +#endif +} + +static void vfe_process_error_irq( + struct vfe_interrupt_status *irqstatus) +{ + /* all possible error irq. Note error irqs are not enabled, it is + * checked only when other interrupts are present. */ + if (irqstatus->afOverflowIrq) + vfe_send_msg_no_payload(VFE_MSG_ID_AF_OVERFLOW); + + if (irqstatus->awbOverflowIrq) + vfe_send_msg_no_payload(VFE_MSG_ID_AWB_OVERFLOW); + + if (irqstatus->axiErrorIrq) + vfe_send_msg_no_payload(VFE_MSG_ID_AXI_ERROR); + + if (irqstatus->busOverflowIrq) + vfe_send_bus_overflow_msg(); + + if (irqstatus->camifErrorIrq) + vfe_send_camif_error_msg(); + + if (irqstatus->camifOverflowIrq) + vfe_send_msg_no_payload(VFE_MSG_ID_CAMIF_OVERFLOW); + + if (irqstatus->violationIrq) + ; +} + +static void vfe_process_camif_sof_irq(void) +{ + /* increment the frame id number. */ + ctrl->vfeFrameId++; + + CDBG("camif_sof_irq, frameId = %d\n", + ctrl->vfeFrameId); + + /* In snapshot mode, if frame skip is programmed, + * need to check it accordingly to stop camif at + * correct frame boundary. For the dropped frames, + * there won't be any output path irqs, but there is + * still SOF irq, which can help us determine when + * to stop the camif. + */ + if (ctrl->vfeOperationMode) { + if ((1 << ctrl->vfeFrameSkipCount) & + ctrl->vfeFrameSkipPattern) { + + ctrl->vfeSnapShotCount--; + if (ctrl->vfeSnapShotCount == 0) + /* terminate vfe pipeline at frame boundary. */ + writel(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY, + ctrl->vfebase + CAMIF_COMMAND); + } + + /* update frame skip counter for bit checking. */ + ctrl->vfeFrameSkipCount++; + if (ctrl->vfeFrameSkipCount == + (ctrl->vfeFrameSkipPeriod + 1)) + ctrl->vfeFrameSkipCount = 0; + } +} + +static int vfe_get_af_pingpong_status(void) +{ + uint32_t busPingPongStatus; + int rc = 0; + + busPingPongStatus = + readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS); + + if ((busPingPongStatus & VFE_AF_PINGPONG_STATUS_BIT) == 0) + return -EFAULT; + + return rc; +} + +static uint32_t vfe_read_af_buf_addr(boolean pipo) +{ + if (pipo == FALSE) + return readl(ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR); + else + return readl(ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR); +} + +static void +vfe_update_af_buf_addr(boolean pipo, uint32_t addr) +{ + if (pipo == FALSE) + writel(addr, ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR); + else + writel(addr, ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR); +} + +static void +vfe_send_af_stats_msg(uint32_t afBufAddress) +{ + /* unsigned long flags; */ + struct vfe_message *msg; + msg = + kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); + if (!msg) + return; + + /* fill message with right content. */ + /* @todo This is causing issues, need further investigate */ + /* spin_lock_irqsave(&ctrl->state_lock, flags); */ + if (ctrl->vstate != VFE_STATE_ACTIVE) + goto af_stats_done; + + msg->_d = VFE_MSG_ID_STATS_AUTOFOCUS; + msg->_u.msgStatsAf.afBuffer = afBufAddress; + msg->_u.msgStatsAf.frameCounter = ctrl->vfeFrameId; + + vfe_proc_ops(VFE_MSG_ID_STATS_AUTOFOCUS, + msg, sizeof(struct vfe_message)); + + ctrl->afStatsControl.ackPending = TRUE; + +af_stats_done: + /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ + return; +} + +static void vfe_process_stats_af_irq(void) +{ + boolean bufferAvailable; + + if (!(ctrl->afStatsControl.ackPending)) { + + /* read hardware status. */ + ctrl->afStatsControl.pingPongStatus = + vfe_get_af_pingpong_status(); + + bufferAvailable = + (ctrl->afStatsControl.pingPongStatus) ^ 1; + + ctrl->afStatsControl.bufToRender = + vfe_read_af_buf_addr(bufferAvailable); + + /* update the same buffer address (ping or pong) */ + vfe_update_af_buf_addr(bufferAvailable, + ctrl->afStatsControl.nextFrameAddrBuf); + + vfe_send_af_stats_msg(ctrl->afStatsControl.bufToRender); + } else + ctrl->afStatsControl.droppedStatsFrameCount++; +} + +static boolean vfe_get_awb_pingpong_status(void) +{ + uint32_t busPingPongStatus; + + busPingPongStatus = + readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS); + + if ((busPingPongStatus & VFE_AWB_PINGPONG_STATUS_BIT) == 0) + return FALSE; + + return TRUE; +} + +static uint32_t +vfe_read_awb_buf_addr(boolean pingpong) +{ + if (pingpong == FALSE) + return readl(ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR); + else + return readl(ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR); +} + +static void vfe_update_awb_buf_addr( + boolean pingpong, uint32_t addr) +{ + if (pingpong == FALSE) + writel(addr, ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR); + else + writel(addr, ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR); +} + +static void vfe_send_awb_stats_msg(uint32_t awbBufAddress) +{ + /* unsigned long flags; */ + struct vfe_message *msg; + + msg = + kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); + if (!msg) + return; + + /* fill message with right content. */ + /* @todo This is causing issues, need further investigate */ + /* spin_lock_irqsave(&ctrl->state_lock, flags); */ + if (ctrl->vstate != VFE_STATE_ACTIVE) + goto awb_stats_done; + + msg->_d = VFE_MSG_ID_STATS_WB_EXP; + msg->_u.msgStatsWbExp.awbBuffer = awbBufAddress; + msg->_u.msgStatsWbExp.frameCounter = ctrl->vfeFrameId; + + vfe_proc_ops(VFE_MSG_ID_STATS_WB_EXP, + msg, sizeof(struct vfe_message)); + + ctrl->awbStatsControl.ackPending = TRUE; + +awb_stats_done: + /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ + return; +} + +static void vfe_process_stats_awb_irq(void) +{ + boolean bufferAvailable; + + if (!(ctrl->awbStatsControl.ackPending)) { + + ctrl->awbStatsControl.pingPongStatus = + vfe_get_awb_pingpong_status(); + + bufferAvailable = (ctrl->awbStatsControl.pingPongStatus) ^ 1; + + ctrl->awbStatsControl.bufToRender = + vfe_read_awb_buf_addr(bufferAvailable); + + vfe_update_awb_buf_addr(bufferAvailable, + ctrl->awbStatsControl.nextFrameAddrBuf); + + vfe_send_awb_stats_msg(ctrl->awbStatsControl.bufToRender); + + } else + ctrl->awbStatsControl.droppedStatsFrameCount++; +} + +static void vfe_process_sync_timer_irq( + struct vfe_interrupt_status *irqstatus) +{ + if (irqstatus->syncTimer0Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_SYNC_TIMER0_DONE); + + if (irqstatus->syncTimer1Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_SYNC_TIMER1_DONE); + + if (irqstatus->syncTimer2Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_SYNC_TIMER2_DONE); +} + +static void vfe_process_async_timer_irq( + struct vfe_interrupt_status *irqstatus) +{ + + if (irqstatus->asyncTimer0Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER0_DONE); + + if (irqstatus->asyncTimer1Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER1_DONE); + + if (irqstatus->asyncTimer2Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER2_DONE); + + if (irqstatus->asyncTimer3Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER3_DONE); +} + +static void vfe_send_violation_msg(void) +{ + vfe_send_msg_no_payload(VFE_MSG_ID_VIOLATION); +} + +static void vfe_send_async_timer_msg(void) +{ + vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER0_DONE); +} + +static void vfe_write_gamma_table(uint8_t channel, + boolean bank, int16_t *pTable) +{ + uint16_t i; + + enum VFE_DMI_RAM_SEL dmiRamSel = NO_MEM_SELECTED; + + switch (channel) { + case 0: + if (bank == 0) + dmiRamSel = RGBLUT_RAM_CH0_BANK0; + else + dmiRamSel = RGBLUT_RAM_CH0_BANK1; + break; + + case 1: + if (bank == 0) + dmiRamSel = RGBLUT_RAM_CH1_BANK0; + else + dmiRamSel = RGBLUT_RAM_CH1_BANK1; + break; + + case 2: + if (bank == 0) + dmiRamSel = RGBLUT_RAM_CH2_BANK0; + else + dmiRamSel = RGBLUT_RAM_CH2_BANK1; + break; + + default: + break; + } + + vfe_program_dmi_cfg(dmiRamSel); + + for (i = 0; i < VFE_GAMMA_TABLE_LENGTH; i++) { + writel((uint32_t)(*pTable), ctrl->vfebase + VFE_DMI_DATA_LO); + pTable++; + } + + /* After DMI transfer, need to set the DMI_CFG to unselect any SRAM + unselect the SRAM Bank. */ + writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG); +} + +static void vfe_prog_hw_testgen_cmd(uint32_t value) +{ + writel(value, ctrl->vfebase + VFE_HW_TESTGEN_CMD); +} + +static inline void vfe_read_irq_status(struct vfe_irq_thread_msg *out) +{ + uint32_t *temp; + + memset(out, 0, sizeof(struct vfe_irq_thread_msg)); + + temp = (uint32_t *)(ctrl->vfebase + VFE_IRQ_STATUS); + out->vfeIrqStatus = readl(temp); + + temp = (uint32_t *)(ctrl->vfebase + CAMIF_STATUS); + out->camifStatus = readl(temp); + writel(0x7, ctrl->vfebase + CAMIF_COMMAND); + writel(0x3, ctrl->vfebase + CAMIF_COMMAND); + CDBG("camifStatus = 0x%x\n", out->camifStatus); + +/* + temp = (uint32_t *)(ctrl->vfebase + VFE_DEMOSAIC_STATUS); + out->demosaicStatus = readl(temp); + + temp = (uint32_t *)(ctrl->vfebase + VFE_ASF_MAX_EDGE); + out->asfMaxEdge = readl(temp); + + temp = (uint32_t *)(ctrl->vfebase + VFE_BUS_ENC_Y_WR_PM_STATS_0); +*/ + +#if 0 + out->pmInfo.encPathPmInfo.yWrPmStats0 = readl(temp++); + out->pmInfo.encPathPmInfo.yWrPmStats1 = readl(temp++); + out->pmInfo.encPathPmInfo.cbcrWrPmStats0 = readl(temp++); + out->pmInfo.encPathPmInfo.cbcrWrPmStats1 = readl(temp++); + out->pmInfo.viewPathPmInfo.yWrPmStats0 = readl(temp++); + out->pmInfo.viewPathPmInfo.yWrPmStats1 = readl(temp++); + out->pmInfo.viewPathPmInfo.cbcrWrPmStats0 = readl(temp++); + out->pmInfo.viewPathPmInfo.cbcrWrPmStats1 = readl(temp); +#endif /* if 0 Jeff */ +} + +static struct vfe_interrupt_status +vfe_parse_interrupt_status(uint32_t irqStatusIn) +{ + struct vfe_irqenable hwstat; + struct vfe_interrupt_status ret; + boolean temp; + + memset(&hwstat, 0, sizeof(hwstat)); + memset(&ret, 0, sizeof(ret)); + + hwstat = *((struct vfe_irqenable *)(&irqStatusIn)); + + ret.camifErrorIrq = hwstat.camifErrorIrq; + ret.camifSofIrq = hwstat.camifSofIrq; + ret.camifEolIrq = hwstat.camifEolIrq; + ret.camifEofIrq = hwstat.camifEofIrq; + ret.camifEpoch1Irq = hwstat.camifEpoch1Irq; + ret.camifEpoch2Irq = hwstat.camifEpoch2Irq; + ret.camifOverflowIrq = hwstat.camifOverflowIrq; + ret.ceIrq = hwstat.ceIrq; + ret.regUpdateIrq = hwstat.regUpdateIrq; + ret.resetAckIrq = hwstat.resetAckIrq; + ret.encYPingpongIrq = hwstat.encYPingpongIrq; + ret.encCbcrPingpongIrq = hwstat.encCbcrPingpongIrq; + ret.viewYPingpongIrq = hwstat.viewYPingpongIrq; + ret.viewCbcrPingpongIrq = hwstat.viewCbcrPingpongIrq; + ret.rdPingpongIrq = hwstat.rdPingpongIrq; + ret.afPingpongIrq = hwstat.afPingpongIrq; + ret.awbPingpongIrq = hwstat.awbPingpongIrq; + ret.histPingpongIrq = hwstat.histPingpongIrq; + ret.encIrq = hwstat.encIrq; + ret.viewIrq = hwstat.viewIrq; + ret.busOverflowIrq = hwstat.busOverflowIrq; + ret.afOverflowIrq = hwstat.afOverflowIrq; + ret.awbOverflowIrq = hwstat.awbOverflowIrq; + ret.syncTimer0Irq = hwstat.syncTimer0Irq; + ret.syncTimer1Irq = hwstat.syncTimer1Irq; + ret.syncTimer2Irq = hwstat.syncTimer2Irq; + ret.asyncTimer0Irq = hwstat.asyncTimer0Irq; + ret.asyncTimer1Irq = hwstat.asyncTimer1Irq; + ret.asyncTimer2Irq = hwstat.asyncTimer2Irq; + ret.asyncTimer3Irq = hwstat.asyncTimer3Irq; + ret.axiErrorIrq = hwstat.axiErrorIrq; + ret.violationIrq = hwstat.violationIrq; + + /* logic OR of any error bits + * although each irq corresponds to a bit, the data type here is a + * boolean already. hence use logic operation. + */ + temp = + ret.camifErrorIrq || + ret.camifOverflowIrq || + ret.afOverflowIrq || + ret.awbPingpongIrq || + ret.busOverflowIrq || + ret.axiErrorIrq || + ret.violationIrq; + + ret.anyErrorIrqs = temp; + + /* logic OR of any output path bits*/ + temp = + ret.encYPingpongIrq || + ret.encCbcrPingpongIrq || + ret.encIrq; + + ret.anyOutput2PathIrqs = temp; + + temp = + ret.viewYPingpongIrq || + ret.viewCbcrPingpongIrq || + ret.viewIrq; + + ret.anyOutput1PathIrqs = temp; + + ret.anyOutputPathIrqs = + ret.anyOutput1PathIrqs || + ret.anyOutput2PathIrqs; + + /* logic OR of any sync timer bits*/ + temp = + ret.syncTimer0Irq || + ret.syncTimer1Irq || + ret.syncTimer2Irq; + + ret.anySyncTimerIrqs = temp; + + /* logic OR of any async timer bits*/ + temp = + ret.asyncTimer0Irq || + ret.asyncTimer1Irq || + ret.asyncTimer2Irq || + ret.asyncTimer3Irq; + + ret.anyAsyncTimerIrqs = temp; + + /* bool for all interrupts that are not allowed in idle state */ + temp = + ret.anyErrorIrqs || + ret.anyOutputPathIrqs || + ret.anySyncTimerIrqs || + ret.regUpdateIrq || + ret.awbPingpongIrq || + ret.afPingpongIrq || + ret.camifSofIrq || + ret.camifEpoch2Irq || + ret.camifEpoch1Irq; + + ret.anyIrqForActiveStatesOnly = + temp; + + return ret; +} + +static struct vfe_frame_asf_info +vfe_get_asf_frame_info(struct vfe_irq_thread_msg *in) +{ + struct vfe_asf_info asfInfoTemp; + struct vfe_frame_asf_info rc; + + memset(&rc, 0, sizeof(rc)); + memset(&asfInfoTemp, 0, sizeof(asfInfoTemp)); + + asfInfoTemp = + *((struct vfe_asf_info *)(&(in->asfMaxEdge))); + + rc.asfHbiCount = asfInfoTemp.HBICount; + rc.asfMaxEdge = asfInfoTemp.maxEdge; + + return rc; +} + +static struct vfe_frame_bpc_info +vfe_get_demosaic_frame_info(struct vfe_irq_thread_msg *in) +{ + struct vfe_bps_info bpcInfoTemp; + struct vfe_frame_bpc_info rc; + + memset(&rc, 0, sizeof(rc)); + memset(&bpcInfoTemp, 0, sizeof(bpcInfoTemp)); + + bpcInfoTemp = + *((struct vfe_bps_info *)(&(in->demosaicStatus))); + + rc.greenDefectPixelCount = + bpcInfoTemp.greenBadPixelCount; + + rc.redBlueDefectPixelCount = + bpcInfoTemp.RedBlueBadPixelCount; + + return rc; +} + +static struct vfe_msg_camif_status +vfe_get_camif_status(struct vfe_irq_thread_msg *in) +{ + struct vfe_camif_stats camifStatusTemp; + struct vfe_msg_camif_status rc; + + memset(&rc, 0, sizeof(rc)); + memset(&camifStatusTemp, 0, sizeof(camifStatusTemp)); + + camifStatusTemp = + *((struct vfe_camif_stats *)(&(in->camifStatus))); + + rc.camifState = (boolean)camifStatusTemp.camifHalt; + rc.lineCount = camifStatusTemp.lineCount; + rc.pixelCount = camifStatusTemp.pixelCount; + + return rc; +} + +static struct vfe_bus_performance_monitor +vfe_get_performance_monitor_data(struct vfe_irq_thread_msg *in) +{ + struct vfe_bus_performance_monitor rc; + memset(&rc, 0, sizeof(rc)); + + rc.encPathPmInfo.yWrPmStats0 = + in->pmInfo.encPathPmInfo.yWrPmStats0; + rc.encPathPmInfo.yWrPmStats1 = + in->pmInfo.encPathPmInfo.yWrPmStats1; + rc.encPathPmInfo.cbcrWrPmStats0 = + in->pmInfo.encPathPmInfo.cbcrWrPmStats0; + rc.encPathPmInfo.cbcrWrPmStats1 = + in->pmInfo.encPathPmInfo.cbcrWrPmStats1; + rc.viewPathPmInfo.yWrPmStats0 = + in->pmInfo.viewPathPmInfo.yWrPmStats0; + rc.viewPathPmInfo.yWrPmStats1 = + in->pmInfo.viewPathPmInfo.yWrPmStats1; + rc.viewPathPmInfo.cbcrWrPmStats0 = + in->pmInfo.viewPathPmInfo.cbcrWrPmStats0; + rc.viewPathPmInfo.cbcrWrPmStats1 = + in->pmInfo.viewPathPmInfo.cbcrWrPmStats1; + + return rc; +} + +static void vfe_process_reg_update_irq(void) +{ + CDBG("vfe_process_reg_update_irq: ackPendingFlag is %d\n", + ctrl->vfeStartAckPendingFlag); + if (ctrl->vfeStartAckPendingFlag == TRUE) { + vfe_send_msg_no_payload(VFE_MSG_ID_START_ACK); + ctrl->vfeStartAckPendingFlag = FALSE; + } else + vfe_send_msg_no_payload(VFE_MSG_ID_UPDATE_ACK); +} + +static void vfe_process_reset_irq(void) +{ + /* unsigned long flags; */ + + /* @todo This is causing issues, need further investigate */ + /* spin_lock_irqsave(&ctrl->state_lock, flags); */ + ctrl->vstate = VFE_STATE_IDLE; + /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ + + if (ctrl->vfeStopAckPending == TRUE) { + ctrl->vfeStopAckPending = FALSE; + vfe_send_msg_no_payload(VFE_MSG_ID_STOP_ACK); + } else { + vfe_set_default_reg_values(); + vfe_send_msg_no_payload(VFE_MSG_ID_RESET_ACK); + } +} + +static void vfe_process_pingpong_irq(struct vfe_output_path *in, + uint8_t fragmentCount) +{ + uint16_t circularIndex; + uint32_t nextFragmentAddr; + + /* get next fragment address from circular buffer */ + circularIndex = (in->fragIndex) % (2 * fragmentCount); + nextFragmentAddr = in->addressBuffer[circularIndex]; + + in->fragIndex = circularIndex + 1; + + /* use next fragment to program hardware ping/pong address. */ + if (in->hwCurrentFlag == ping) { + writel(nextFragmentAddr, in->hwRegPingAddress); + in->hwCurrentFlag = pong; + + } else { + writel(nextFragmentAddr, in->hwRegPongAddress); + in->hwCurrentFlag = ping; + } +} + +static void vfe_send_output2_msg( + struct vfe_msg_output *pPayload) +{ + /* unsigned long flags; */ + struct vfe_message *msg; + + msg = kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); + if (!msg) + return; + + /* fill message with right content. */ + /* @todo This is causing issues, need further investigate */ + /* spin_lock_irqsave(&ctrl->state_lock, flags); */ + if (ctrl->vstate != VFE_STATE_ACTIVE) + goto output2_msg_done; + + msg->_d = VFE_MSG_ID_OUTPUT2; + + memcpy(&(msg->_u.msgOutput2), + (void *)pPayload, sizeof(struct vfe_msg_output)); + + vfe_proc_ops(VFE_MSG_ID_OUTPUT2, + msg, sizeof(struct vfe_message)); + + ctrl->encPath.ackPending = TRUE; + + if (!(ctrl->vfeRequestedSnapShotCount <= 3) && + (ctrl->vfeOperationMode == + VFE_START_OPERATION_MODE_SNAPSHOT)) + ctrl->encPath.ackPending = TRUE; + +output2_msg_done: + /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ + return; +} + +static void vfe_send_output1_msg( + struct vfe_msg_output *pPayload) +{ + /* unsigned long flags; */ + struct vfe_message *msg; + + msg = kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); + if (!msg) + return; + + /* @todo This is causing issues, need further investigate */ + /* spin_lock_irqsave(&ctrl->state_lock, flags); */ + if (ctrl->vstate != VFE_STATE_ACTIVE) + goto output1_msg_done; + + msg->_d = VFE_MSG_ID_OUTPUT1; + memmove(&(msg->_u), + (void *)pPayload, sizeof(struct vfe_msg_output)); + + vfe_proc_ops(VFE_MSG_ID_OUTPUT1, + msg, sizeof(struct vfe_message)); + + ctrl->viewPath.ackPending = TRUE; + + if (!(ctrl->vfeRequestedSnapShotCount <= 3) && + (ctrl->vfeOperationMode == + VFE_START_OPERATION_MODE_SNAPSHOT)) + ctrl->viewPath.ackPending = TRUE; + +output1_msg_done: + /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ + return; +} + +static void vfe_send_output_msg(boolean whichOutputPath, + uint32_t yPathAddr, uint32_t cbcrPathAddr) +{ + struct vfe_msg_output msgPayload; + + msgPayload.yBuffer = yPathAddr; + msgPayload.cbcrBuffer = cbcrPathAddr; + + /* asf info is common for both output1 and output2 */ +#if 0 + msgPayload.asfInfo.asfHbiCount = ctrl->vfeAsfFrameInfo.asfHbiCount; + msgPayload.asfInfo.asfMaxEdge = ctrl->vfeAsfFrameInfo.asfMaxEdge; + + /* demosaic info is common for both output1 and output2 */ + msgPayload.bpcInfo.greenDefectPixelCount = + ctrl->vfeBpcFrameInfo.greenDefectPixelCount; + msgPayload.bpcInfo.redBlueDefectPixelCount = + ctrl->vfeBpcFrameInfo.redBlueDefectPixelCount; +#endif /* if 0 */ + + /* frame ID is common for both paths. */ + msgPayload.frameCounter = ctrl->vfeFrameId; + + if (whichOutputPath) { + /* msgPayload.pmData = ctrl->vfePmData.encPathPmInfo; */ + vfe_send_output2_msg(&msgPayload); + } else { + /* msgPayload.pmData = ctrl->vfePmData.viewPathPmInfo; */ + vfe_send_output1_msg(&msgPayload); + } +} + +static void vfe_process_frame_done_irq_multi_frag( + struct vfe_output_path_combo *in) +{ + uint32_t yAddress, cbcrAddress; + uint16_t idx; + uint32_t *ptrY; + uint32_t *ptrCbcr; + const uint32_t *ptrSrc; + uint8_t i; + + if (!in->ackPending) { + + idx = (in->currentFrame) * (in->fragCount); + + /* Send output message. */ + yAddress = in->yPath.addressBuffer[idx]; + cbcrAddress = in->cbcrPath.addressBuffer[idx]; + + /* copy next frame to current frame. */ + ptrSrc = in->nextFrameAddrBuf; + ptrY = (uint32_t *)&(in->yPath.addressBuffer[idx]); + ptrCbcr = (uint32_t *)&(in->cbcrPath.addressBuffer[idx]); + + /* Copy Y address */ + for (i = 0; i < in->fragCount; i++) + *ptrY++ = *ptrSrc++; + + /* Copy Cbcr address */ + for (i = 0; i < in->fragCount; i++) + *ptrCbcr++ = *ptrSrc++; + + vfe_send_output_msg(in->whichOutputPath, yAddress, cbcrAddress); + + } else { + if (in->whichOutputPath == 0) + ctrl->vfeDroppedFrameCounts.output1Count++; + + if (in->whichOutputPath == 1) + ctrl->vfeDroppedFrameCounts.output2Count++; + } + + /* toggle current frame. */ + in->currentFrame = in->currentFrame^1; + + if (ctrl->vfeOperationMode) + in->snapshotPendingCount--; +} + +static void vfe_process_frame_done_irq_no_frag_io( + struct vfe_output_path_combo *in, uint32_t *pNextAddr, + uint32_t *pdestRenderAddr) +{ + uint32_t busPingPongStatus; + uint32_t tempAddress; + + /* 1. read hw status register. */ + busPingPongStatus = + readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS); + + CDBG("hardware status is 0x%x\n", busPingPongStatus); + + /* 2. determine ping or pong */ + /* use cbcr status */ + busPingPongStatus = busPingPongStatus & (1<<(in->cbcrStatusBit)); + + /* 3. read out address and update address */ + if (busPingPongStatus == 0) { + /* hw is working on ping, render pong buffer */ + /* a. read out pong address */ + /* read out y address. */ + tempAddress = readl(in->yPath.hwRegPongAddress); + + CDBG("pong 1 addr = 0x%x\n", tempAddress); + *pdestRenderAddr++ = tempAddress; + /* read out cbcr address. */ + tempAddress = readl(in->cbcrPath.hwRegPongAddress); + + CDBG("pong 2 addr = 0x%x\n", tempAddress); + *pdestRenderAddr = tempAddress; + + /* b. update pong address */ + writel(*pNextAddr++, in->yPath.hwRegPongAddress); + writel(*pNextAddr, in->cbcrPath.hwRegPongAddress); + } else { + /* hw is working on pong, render ping buffer */ + + /* a. read out ping address */ + tempAddress = readl(in->yPath.hwRegPingAddress); + CDBG("ping 1 addr = 0x%x\n", tempAddress); + *pdestRenderAddr++ = tempAddress; + tempAddress = readl(in->cbcrPath.hwRegPingAddress); + + CDBG("ping 2 addr = 0x%x\n", tempAddress); + *pdestRenderAddr = tempAddress; + + /* b. update ping address */ + writel(*pNextAddr++, in->yPath.hwRegPingAddress); + CDBG("NextAddress = 0x%x\n", *pNextAddr); + writel(*pNextAddr, in->cbcrPath.hwRegPingAddress); + } +} + +static void vfe_process_frame_done_irq_no_frag( + struct vfe_output_path_combo *in) +{ + uint32_t addressToRender[2]; + static uint32_t fcnt; + + if (fcnt++ < 3) + return; + + if (!in->ackPending) { + vfe_process_frame_done_irq_no_frag_io(in, + in->nextFrameAddrBuf, addressToRender); + + /* use addressToRender to send out message. */ + vfe_send_output_msg(in->whichOutputPath, + addressToRender[0], addressToRender[1]); + + } else { + /* ackPending is still there, accumulate dropped frame count. + * These count can be read through ioctrl command. */ + CDBG("waiting frame ACK\n"); + + if (in->whichOutputPath == 0) + ctrl->vfeDroppedFrameCounts.output1Count++; + + if (in->whichOutputPath == 1) + ctrl->vfeDroppedFrameCounts.output2Count++; + } + + /* in case of multishot when upper layer did not ack, there will still + * be a snapshot done msg sent out, even though the number of frames + * sent out may be less than the desired number of frames. snapshot + * done msg would be helpful to indicate that vfe pipeline has stop, + * and in good known state. + */ + if (ctrl->vfeOperationMode) + in->snapshotPendingCount--; +} + +static void vfe_process_output_path_irq( + struct vfe_interrupt_status *irqstatus) +{ + /* unsigned long flags; */ + + /* process the view path interrupts */ + if (irqstatus->anyOutput1PathIrqs) { + if (ctrl->viewPath.multiFrag) { + + if (irqstatus->viewCbcrPingpongIrq) + vfe_process_pingpong_irq( + &(ctrl->viewPath.cbcrPath), + ctrl->viewPath.fragCount); + + if (irqstatus->viewYPingpongIrq) + vfe_process_pingpong_irq( + &(ctrl->viewPath.yPath), + ctrl->viewPath.fragCount); + + if (irqstatus->viewIrq) + vfe_process_frame_done_irq_multi_frag( + &ctrl->viewPath); + + } else { + /* typical case for no fragment, + only frame done irq is enabled. */ + if (irqstatus->viewIrq) + vfe_process_frame_done_irq_no_frag( + &ctrl->viewPath); + } + } + + /* process the encoder path interrupts */ + if (irqstatus->anyOutput2PathIrqs) { + if (ctrl->encPath.multiFrag) { + if (irqstatus->encCbcrPingpongIrq) + vfe_process_pingpong_irq( + &(ctrl->encPath.cbcrPath), + ctrl->encPath.fragCount); + + if (irqstatus->encYPingpongIrq) + vfe_process_pingpong_irq(&(ctrl->encPath.yPath), + ctrl->encPath.fragCount); + + if (irqstatus->encIrq) + vfe_process_frame_done_irq_multi_frag( + &ctrl->encPath); + + } else { + if (irqstatus->encIrq) + vfe_process_frame_done_irq_no_frag( + &ctrl->encPath); + } + } + + if (ctrl->vfeOperationMode) { + if ((ctrl->encPath.snapshotPendingCount == 0) && + (ctrl->viewPath.snapshotPendingCount == 0)) { + + /* @todo This is causing issues, further investigate */ + /* spin_lock_irqsave(&ctrl->state_lock, flags); */ + ctrl->vstate = VFE_STATE_IDLE; + /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ + + vfe_send_msg_no_payload(VFE_MSG_ID_SNAPSHOT_DONE); + vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_STOP); + vfe_pm_stop(); + } + } +} + +static void vfe_do_tasklet(unsigned long data) +{ + unsigned long flags; + + struct isr_queue_cmd *qcmd = NULL; + + CDBG("=== vfe_do_tasklet start === \n"); + + spin_lock_irqsave(&ctrl->tasklet_lock, flags); + qcmd = list_first_entry(&ctrl->tasklet_q, + struct isr_queue_cmd, list); + + if (!qcmd) { + spin_unlock_irqrestore(&ctrl->tasklet_lock, flags); + return; + } + + list_del(&qcmd->list); + spin_unlock_irqrestore(&ctrl->tasklet_lock, flags); + + if (qcmd->vfeInterruptStatus.regUpdateIrq) { + CDBG("irq regUpdateIrq\n"); + vfe_process_reg_update_irq(); + } + + if (qcmd->vfeInterruptStatus.resetAckIrq) { + CDBG("irq resetAckIrq\n"); + vfe_process_reset_irq(); + } + + spin_lock_irqsave(&ctrl->state_lock, flags); + if (ctrl->vstate != VFE_STATE_ACTIVE) { + spin_unlock_irqrestore(&ctrl->state_lock, flags); + return; + } + spin_unlock_irqrestore(&ctrl->state_lock, flags); + +#if 0 + if (qcmd->vfeInterruptStatus.camifEpoch1Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_EPOCH1); + + if (qcmd->vfeInterruptStatus.camifEpoch2Irq) + vfe_send_msg_no_payload(VFE_MSG_ID_EPOCH2); +#endif /* Jeff */ + + /* next, check output path related interrupts. */ + if (qcmd->vfeInterruptStatus.anyOutputPathIrqs) { + CDBG("irq anyOutputPathIrqs\n"); + vfe_process_output_path_irq(&qcmd->vfeInterruptStatus); + } + + if (qcmd->vfeInterruptStatus.afPingpongIrq) + vfe_process_stats_af_irq(); + + if (qcmd->vfeInterruptStatus.awbPingpongIrq) + vfe_process_stats_awb_irq(); + + /* any error irqs*/ + if (qcmd->vfeInterruptStatus.anyErrorIrqs) + vfe_process_error_irq(&qcmd->vfeInterruptStatus); + +#if 0 + if (qcmd->vfeInterruptStatus.anySyncTimerIrqs) + vfe_process_sync_timer_irq(); + + if (qcmd->vfeInterruptStatus.anyAsyncTimerIrqs) + vfe_process_async_timer_irq(); +#endif /* Jeff */ + + if (qcmd->vfeInterruptStatus.camifSofIrq) { + CDBG("irq camifSofIrq\n"); + vfe_process_camif_sof_irq(); + } + + kfree(qcmd); + CDBG("=== vfe_do_tasklet end === \n"); +} + +DECLARE_TASKLET(vfe_tasklet, vfe_do_tasklet, 0); + +static irqreturn_t vfe_parse_irq(int irq_num, void *data) +{ + unsigned long flags; + uint32_t irqStatusLocal; + struct vfe_irq_thread_msg irq; + struct isr_queue_cmd *qcmd; + + CDBG("vfe_parse_irq\n"); + + vfe_read_irq_status(&irq); + + if (irq.vfeIrqStatus == 0) { + CDBG("vfe_parse_irq: irq.vfeIrqStatus is 0\n"); + return IRQ_HANDLED; + } + + qcmd = kzalloc(sizeof(struct isr_queue_cmd), + GFP_ATOMIC); + if (!qcmd) { + CDBG("vfe_parse_irq: qcmd malloc failed!\n"); + return IRQ_HANDLED; + } + + spin_lock_irqsave(&ctrl->ack_lock, flags); + + if (ctrl->vfeStopAckPending) + irqStatusLocal = + (VFE_IMASK_WHILE_STOPPING & irq.vfeIrqStatus); + else + irqStatusLocal = + ((ctrl->vfeImaskPacked | VFE_IMASK_ERROR_ONLY) & + irq.vfeIrqStatus); + + spin_unlock_irqrestore(&ctrl->ack_lock, flags); + + /* first parse the interrupt status to local data structures. */ + qcmd->vfeInterruptStatus = vfe_parse_interrupt_status(irqStatusLocal); + qcmd->vfeAsfFrameInfo = vfe_get_asf_frame_info(&irq); + qcmd->vfeBpcFrameInfo = vfe_get_demosaic_frame_info(&irq); + qcmd->vfeCamifStatusLocal = vfe_get_camif_status(&irq); + qcmd->vfePmData = vfe_get_performance_monitor_data(&irq); + + spin_lock_irqsave(&ctrl->tasklet_lock, flags); + list_add_tail(&qcmd->list, &ctrl->tasklet_q); + spin_unlock_irqrestore(&ctrl->tasklet_lock, flags); + tasklet_schedule(&vfe_tasklet); + + /* clear the pending interrupt of the same kind.*/ + writel(irq.vfeIrqStatus, ctrl->vfebase + VFE_IRQ_CLEAR); + + return IRQ_HANDLED; +} + +int vfe_cmd_init(struct msm_vfe_callback *presp, + struct platform_device *pdev, void *sdata) +{ + struct resource *vfemem, *vfeirq, *vfeio; + int rc; + + vfemem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!vfemem) { + CDBG("no mem resource?\n"); + return -ENODEV; + } + + vfeirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!vfeirq) { + CDBG("no irq resource?\n"); + return -ENODEV; + } + + vfeio = request_mem_region(vfemem->start, + resource_size(vfemem), pdev->name); + if (!vfeio) { + CDBG("VFE region already claimed\n"); + return -EBUSY; + } + + ctrl = + kzalloc(sizeof(struct msm_vfe8x_ctrl), GFP_KERNEL); + if (!ctrl) { + rc = -ENOMEM; + goto cmd_init_failed1; + } + + ctrl->vfeirq = vfeirq->start; + + ctrl->vfebase = + ioremap(vfemem->start, (vfemem->end - vfemem->start) + 1); + if (!ctrl->vfebase) { + rc = -ENOMEM; + goto cmd_init_failed2; + } + + rc = request_irq(ctrl->vfeirq, vfe_parse_irq, + IRQF_TRIGGER_RISING, "vfe", 0); + if (rc < 0) + goto cmd_init_failed2; + + if (presp && presp->vfe_resp) + ctrl->resp = presp; + else { + rc = -EINVAL; + goto cmd_init_failed3; + } + + ctrl->extdata = + kmalloc(sizeof(struct vfe_frame_extra), GFP_KERNEL); + if (!ctrl->extdata) { + rc = -ENOMEM; + goto cmd_init_failed3; + } + + spin_lock_init(&ctrl->ack_lock); + spin_lock_init(&ctrl->state_lock); + spin_lock_init(&ctrl->io_lock); + + ctrl->extlen = sizeof(struct vfe_frame_extra); + + spin_lock_init(&ctrl->tasklet_lock); + INIT_LIST_HEAD(&ctrl->tasklet_q); + + ctrl->syncdata = sdata; + return 0; + +cmd_init_failed3: + disable_irq(ctrl->vfeirq); + free_irq(ctrl->vfeirq, 0); + iounmap(ctrl->vfebase); +cmd_init_failed2: + kfree(ctrl); +cmd_init_failed1: + release_mem_region(vfemem->start, (vfemem->end - vfemem->start) + 1); + return rc; +} + +void vfe_cmd_release(struct platform_device *dev) +{ + struct resource *mem; + + disable_irq(ctrl->vfeirq); + free_irq(ctrl->vfeirq, 0); + + iounmap(ctrl->vfebase); + mem = platform_get_resource(dev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, (mem->end - mem->start) + 1); + + ctrl->extlen = 0; + + kfree(ctrl->extdata); + kfree(ctrl); +} + +void vfe_stats_af_stop(void) +{ + ctrl->vfeStatsCmdLocal.autoFocusEnable = FALSE; + ctrl->vfeImaskLocal.afPingpongIrq = FALSE; +} + +void vfe_stop(void) +{ + boolean vfeAxiBusy; + uint32_t vfeAxiStauts; + + /* for reset hw modules, and send msg when reset_irq comes.*/ + ctrl->vfeStopAckPending = TRUE; + + ctrl->vfeStatsPingPongReloadFlag = FALSE; + vfe_pm_stop(); + + /* disable all interrupts. */ + vfe_program_irq_mask(VFE_DISABLE_ALL_IRQS); + + /* in either continuous or snapshot mode, stop command can be issued + * at any time. + */ + vfe_camif_stop_immediately(); + vfe_program_axi_cmd(AXI_HALT); + vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_STOP); + + vfeAxiBusy = TRUE; + + while (vfeAxiBusy) { + vfeAxiStauts = vfe_read_axi_status(); + if ((vfeAxiStauts & AXI_STATUS_BUSY_MASK) != 0) + vfeAxiBusy = FALSE; + } + + vfe_program_axi_cmd(AXI_HALT_CLEAR); + + /* clear all pending interrupts */ + writel(VFE_CLEAR_ALL_IRQS, ctrl->vfebase + VFE_IRQ_CLEAR); + + /* enable reset_ack and async timer interrupt only while stopping + * the pipeline. + */ + vfe_program_irq_mask(VFE_IMASK_WHILE_STOPPING); + + vfe_program_global_reset_cmd(VFE_RESET_UPON_STOP_CMD); +} + +void vfe_update(void) +{ + ctrl->vfeModuleEnableLocal.statsEnable = + ctrl->vfeStatsCmdLocal.autoFocusEnable | + ctrl->vfeStatsCmdLocal.axwEnable; + + vfe_reg_module_cfg(&ctrl->vfeModuleEnableLocal); + + vfe_program_stats_cmd(&ctrl->vfeStatsCmdLocal); + + ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal); + vfe_program_irq_mask(ctrl->vfeImaskPacked); + + if ((ctrl->vfeModuleEnableLocal.statsEnable == TRUE) && + (ctrl->vfeStatsPingPongReloadFlag == FALSE)) { + ctrl->vfeStatsPingPongReloadFlag = TRUE; + + ctrl->vfeBusCmdLocal.statsPingpongReload = TRUE; + vfe_reg_bus_cmd(&ctrl->vfeBusCmdLocal); + } + + vfe_program_reg_update_cmd(VFE_REG_UPDATE_TRIGGER); +} + +int vfe_rgb_gamma_update(struct vfe_cmd_rgb_gamma_config *in) +{ + int rc = 0; + + ctrl->vfeModuleEnableLocal.rgbLUTEnable = in->enable; + + switch (in->channelSelect) { + case RGB_GAMMA_CH0_SELECTED: + ctrl->vfeGammaLutSel.ch0BankSelect ^= 1; + vfe_write_gamma_table(0, + ctrl->vfeGammaLutSel.ch0BankSelect, in->table); + break; + + case RGB_GAMMA_CH1_SELECTED: + ctrl->vfeGammaLutSel.ch1BankSelect ^= 1; + vfe_write_gamma_table(1, + ctrl->vfeGammaLutSel.ch1BankSelect, in->table); + break; + + case RGB_GAMMA_CH2_SELECTED: + ctrl->vfeGammaLutSel.ch2BankSelect ^= 1; + vfe_write_gamma_table(2, + ctrl->vfeGammaLutSel.ch2BankSelect, in->table); + break; + + case RGB_GAMMA_CH0_CH1_SELECTED: + ctrl->vfeGammaLutSel.ch0BankSelect ^= 1; + ctrl->vfeGammaLutSel.ch1BankSelect ^= 1; + vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect, + in->table); + vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect, + in->table); + break; + + case RGB_GAMMA_CH0_CH2_SELECTED: + ctrl->vfeGammaLutSel.ch0BankSelect ^= 1; + ctrl->vfeGammaLutSel.ch2BankSelect ^= 1; + vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect, + in->table); + vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect, + in->table); + break; + + case RGB_GAMMA_CH1_CH2_SELECTED: + ctrl->vfeGammaLutSel.ch1BankSelect ^= 1; + ctrl->vfeGammaLutSel.ch2BankSelect ^= 1; + vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect, + in->table); + vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect, + in->table); + break; + + case RGB_GAMMA_CH0_CH1_CH2_SELECTED: + ctrl->vfeGammaLutSel.ch0BankSelect ^= 1; + ctrl->vfeGammaLutSel.ch1BankSelect ^= 1; + ctrl->vfeGammaLutSel.ch2BankSelect ^= 1; + vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect, + in->table); + vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect, + in->table); + vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect, + in->table); + break; + + default: + return -EINVAL; + } /* switch */ + + /* update the gammaLutSel register. */ + vfe_program_lut_bank_sel(&ctrl->vfeGammaLutSel); + + return rc; +} + +int vfe_rgb_gamma_config(struct vfe_cmd_rgb_gamma_config *in) +{ + int rc = 0; + + ctrl->vfeModuleEnableLocal.rgbLUTEnable = in->enable; + + switch (in->channelSelect) { + case RGB_GAMMA_CH0_SELECTED: +vfe_write_gamma_table(0, 0, in->table); +break; + + case RGB_GAMMA_CH1_SELECTED: + vfe_write_gamma_table(1, 0, in->table); + break; + + case RGB_GAMMA_CH2_SELECTED: + vfe_write_gamma_table(2, 0, in->table); + break; + + case RGB_GAMMA_CH0_CH1_SELECTED: + vfe_write_gamma_table(0, 0, in->table); + vfe_write_gamma_table(1, 0, in->table); + break; + + case RGB_GAMMA_CH0_CH2_SELECTED: + vfe_write_gamma_table(0, 0, in->table); + vfe_write_gamma_table(2, 0, in->table); + break; + + case RGB_GAMMA_CH1_CH2_SELECTED: + vfe_write_gamma_table(1, 0, in->table); + vfe_write_gamma_table(2, 0, in->table); + break; + + case RGB_GAMMA_CH0_CH1_CH2_SELECTED: + vfe_write_gamma_table(0, 0, in->table); + vfe_write_gamma_table(1, 0, in->table); + vfe_write_gamma_table(2, 0, in->table); + break; + + default: + rc = -EINVAL; + break; + } /* switch */ + + return rc; +} + +void vfe_stats_af_ack(struct vfe_cmd_stats_af_ack *in) +{ + ctrl->afStatsControl.nextFrameAddrBuf = in->nextAFOutputBufferAddr; + ctrl->afStatsControl.ackPending = FALSE; +} + +void vfe_stats_wb_exp_ack(struct vfe_cmd_stats_wb_exp_ack *in) +{ + ctrl->awbStatsControl.nextFrameAddrBuf = in->nextWbExpOutputBufferAddr; + ctrl->awbStatsControl.ackPending = FALSE; +} + +void vfe_output2_ack(struct vfe_cmd_output_ack *in) +{ + const uint32_t *psrc; + uint32_t *pdest; + uint8_t i; + + pdest = ctrl->encPath.nextFrameAddrBuf; + + CDBG("output2_ack: ack addr = 0x%x\n", in->ybufaddr[0]); + + psrc = in->ybufaddr; + for (i = 0; i < ctrl->encPath.fragCount; i++) + *pdest++ = *psrc++; + + psrc = in->chromabufaddr; + for (i = 0; i < ctrl->encPath.fragCount; i++) + *pdest++ = *psrc++; + + ctrl->encPath.ackPending = FALSE; +} + +void vfe_output1_ack(struct vfe_cmd_output_ack *in) +{ + const uint32_t *psrc; + uint32_t *pdest; + uint8_t i; + + pdest = ctrl->viewPath.nextFrameAddrBuf; + + psrc = in->ybufaddr; + for (i = 0; i < ctrl->viewPath.fragCount; i++) + *pdest++ = *psrc++; + + psrc = in->chromabufaddr; + for (i = 0; i < ctrl->viewPath.fragCount; i++) + *pdest++ = *psrc++; + + ctrl->viewPath.ackPending = FALSE; +} + +void vfe_start(struct vfe_cmd_start *in) +{ + unsigned long flags; + uint32_t pmstatus = 0; + boolean rawmode; + uint32_t demperiod = 0; + uint32_t demeven = 0; + uint32_t demodd = 0; + + /* derived from other commands. (camif config, axi output config, + * etc) + */ + struct vfe_cfg hwcfg; + struct vfe_upsample_cfg chromupcfg; + + CDBG("vfe_start operationMode = %d\n", in->operationMode); + + memset(&hwcfg, 0, sizeof(hwcfg)); + memset(&chromupcfg, 0, sizeof(chromupcfg)); + + switch (in->pixel) { + case VFE_BAYER_RGRGRG: + demperiod = 1; + demeven = 0xC9; + demodd = 0xAC; + break; + + case VFE_BAYER_GRGRGR: + demperiod = 1; + demeven = 0x9C; + demodd = 0xCA; + break; + + case VFE_BAYER_BGBGBG: + demperiod = 1; + demeven = 0xCA; + demodd = 0x9C; + break; + + case VFE_BAYER_GBGBGB: + demperiod = 1; + demeven = 0xAC; + demodd = 0xC9; + break; + + case VFE_YUV_YCbYCr: + demperiod = 3; + demeven = 0x9CAC; + demodd = 0x9CAC; + break; + + case VFE_YUV_YCrYCb: + demperiod = 3; + demeven = 0xAC9C; + demodd = 0xAC9C; + break; + + case VFE_YUV_CbYCrY: + demperiod = 3; + demeven = 0xC9CA; + demodd = 0xC9CA; + break; + + case VFE_YUV_CrYCbY: + demperiod = 3; + demeven = 0xCAC9; + demodd = 0xCAC9; + break; + + default: + return; + } + + vfe_config_demux(demperiod, demeven, demodd); + + vfe_program_lut_bank_sel(&ctrl->vfeGammaLutSel); + + /* save variables to local. */ + ctrl->vfeOperationMode = in->operationMode; + if (ctrl->vfeOperationMode == + VFE_START_OPERATION_MODE_SNAPSHOT) { + /* in snapshot mode, initialize snapshot count*/ + ctrl->vfeSnapShotCount = in->snapshotCount; + + /* save the requested count, this is temporarily done, to + help with HJR / multishot. */ + ctrl->vfeRequestedSnapShotCount = ctrl->vfeSnapShotCount; + + CDBG("requested snapshot count = %d\n", ctrl->vfeSnapShotCount); + + /* Assumption is to have the same pattern and period for both + paths, if both paths are used. */ + if (ctrl->viewPath.pathEnabled) { + ctrl->viewPath.snapshotPendingCount = + in->snapshotCount; + + ctrl->vfeFrameSkipPattern = + ctrl->vfeFrameSkip.output1Pattern; + ctrl->vfeFrameSkipPeriod = + ctrl->vfeFrameSkip.output1Period; + } + + if (ctrl->encPath.pathEnabled) { + ctrl->encPath.snapshotPendingCount = + in->snapshotCount; + + ctrl->vfeFrameSkipPattern = + ctrl->vfeFrameSkip.output2Pattern; + ctrl->vfeFrameSkipPeriod = + ctrl->vfeFrameSkip.output2Period; + } + } + + /* enable color conversion for bayer sensor + if stats enabled, need to do color conversion. */ + if (in->pixel <= VFE_BAYER_GBGBGB) + ctrl->vfeStatsCmdLocal.colorConversionEnable = TRUE; + + vfe_program_stats_cmd(&ctrl->vfeStatsCmdLocal); + + if (in->pixel >= VFE_YUV_YCbYCr) + ctrl->vfeModuleEnableLocal.chromaUpsampleEnable = TRUE; + + ctrl->vfeModuleEnableLocal.demuxEnable = TRUE; + + /* if any stats module is enabled, the main bit is enabled. */ + ctrl->vfeModuleEnableLocal.statsEnable = + ctrl->vfeStatsCmdLocal.autoFocusEnable | + ctrl->vfeStatsCmdLocal.axwEnable; + + vfe_reg_module_cfg(&ctrl->vfeModuleEnableLocal); + + /* in case of offline processing, do not need to config camif. Having + * bus output enabled in camif_config register might confuse the + * hardware? + */ + if (in->inputSource != VFE_START_INPUT_SOURCE_AXI) { + vfe_reg_camif_config(&ctrl->vfeCamifConfigLocal); + } else { + /* offline processing, enable axi read */ + ctrl->vfeBusConfigLocal.stripeRdPathEn = TRUE; + ctrl->vfeBusCmdLocal.stripeReload = TRUE; + ctrl->vfeBusConfigLocal.rawPixelDataSize = + ctrl->axiInputDataSize; + } + + vfe_reg_bus_cfg(&ctrl->vfeBusConfigLocal); + + /* directly from start command */ + hwcfg.pixelPattern = in->pixel; + hwcfg.inputSource = in->inputSource; + writel(*(uint32_t *)&hwcfg, ctrl->vfebase + VFE_CFG); + + /* regardless module enabled or not, it does not hurt + * to program the cositing mode. */ + chromupcfg.chromaCositingForYCbCrInputs = + in->yuvInputCositingMode; + + writel(*(uint32_t *)&(chromupcfg), + ctrl->vfebase + VFE_CHROMA_UPSAMPLE_CFG); + + /* MISR to monitor the axi read. */ + writel(0xd8, ctrl->vfebase + VFE_BUS_MISR_MAST_CFG_0); + + /* clear all pending interrupts. */ + writel(VFE_CLEAR_ALL_IRQS, ctrl->vfebase + VFE_IRQ_CLEAR); + + /* define how composite interrupt work. */ + ctrl->vfeImaskCompositePacked = + vfe_irq_composite_pack(ctrl->vfeIrqCompositeMaskLocal); + + vfe_program_irq_composite_mask(ctrl->vfeImaskCompositePacked); + + /* enable all necessary interrupts. */ + ctrl->vfeImaskLocal.camifSofIrq = TRUE; + ctrl->vfeImaskLocal.regUpdateIrq = TRUE; + ctrl->vfeImaskLocal.resetAckIrq = TRUE; + + ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal); + vfe_program_irq_mask(ctrl->vfeImaskPacked); + + /* enable bus performance monitor */ + vfe_8k_pm_start(&ctrl->vfeBusPmConfigLocal); + + /* trigger vfe reg update */ + ctrl->vfeStartAckPendingFlag = TRUE; + + /* write bus command to trigger reload of ping pong buffer. */ + ctrl->vfeBusCmdLocal.busPingpongReload = TRUE; + + if (ctrl->vfeModuleEnableLocal.statsEnable == TRUE) { + ctrl->vfeBusCmdLocal.statsPingpongReload = TRUE; + ctrl->vfeStatsPingPongReloadFlag = TRUE; + } + + writel(VFE_REG_UPDATE_TRIGGER, + ctrl->vfebase + VFE_REG_UPDATE_CMD); + + /* program later than the reg update. */ + vfe_reg_bus_cmd(&ctrl->vfeBusCmdLocal); + + if ((in->inputSource == + VFE_START_INPUT_SOURCE_CAMIF) || + (in->inputSource == + VFE_START_INPUT_SOURCE_TESTGEN)) + writel(CAMIF_COMMAND_START, ctrl->vfebase + CAMIF_COMMAND); + + /* start test gen if it is enabled */ + if (ctrl->vfeTestGenStartFlag == TRUE) { + ctrl->vfeTestGenStartFlag = FALSE; + vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_GO); + } + + CDBG("ctrl->axiOutputMode = %d\n", ctrl->axiOutputMode); + if (ctrl->axiOutputMode == VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2) { + /* raw dump mode */ + rawmode = TRUE; + + while (rawmode) { + pmstatus = + readl(ctrl->vfebase + + VFE_BUS_ENC_CBCR_WR_PM_STATS_1); + + if ((pmstatus & VFE_PM_BUF_MAX_CNT_MASK) != 0) + rawmode = FALSE; + } + + vfe_send_msg_no_payload(VFE_MSG_ID_START_ACK); + ctrl->vfeStartAckPendingFlag = FALSE; + } + + spin_lock_irqsave(&ctrl->state_lock, flags); + ctrl->vstate = VFE_STATE_ACTIVE; + spin_unlock_irqrestore(&ctrl->state_lock, flags); +} + +void vfe_la_update(struct vfe_cmd_la_config *in) +{ + int16_t *pTable; + enum VFE_DMI_RAM_SEL dmiRamSel; + int i; + + pTable = in->table; + ctrl->vfeModuleEnableLocal.lumaAdaptationEnable = in->enable; + + /* toggle the bank to be used. */ + ctrl->vfeLaBankSel ^= 1; + + if (ctrl->vfeLaBankSel == 0) + dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK0; + else + dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK1; + + /* configure the DMI_CFG to select right sram */ + vfe_program_dmi_cfg(dmiRamSel); + + for (i = 0; i < VFE_LA_TABLE_LENGTH; i++) { + writel((uint32_t)(*pTable), ctrl->vfebase + VFE_DMI_DATA_LO); + pTable++; + } + + /* After DMI transfer, to make it safe, need to set + * the DMI_CFG to unselect any SRAM */ + writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG); + writel(ctrl->vfeLaBankSel, ctrl->vfebase + VFE_LA_CFG); +} + +void vfe_la_config(struct vfe_cmd_la_config *in) +{ + uint16_t i; + int16_t *pTable; + enum VFE_DMI_RAM_SEL dmiRamSel; + + pTable = in->table; + ctrl->vfeModuleEnableLocal.lumaAdaptationEnable = in->enable; + + if (ctrl->vfeLaBankSel == 0) + dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK0; + else + dmiRamSel = LUMA_ADAPT_LUT_RAM_BANK1; + + /* configure the DMI_CFG to select right sram */ + vfe_program_dmi_cfg(dmiRamSel); + + for (i = 0; i < VFE_LA_TABLE_LENGTH; i++) { + writel((uint32_t)(*pTable), ctrl->vfebase + VFE_DMI_DATA_LO); + pTable++; + } + + /* After DMI transfer, to make it safe, need to set the + * DMI_CFG to unselect any SRAM */ + writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG); + + /* can only be bank 0 or bank 1 for now. */ + writel(ctrl->vfeLaBankSel, ctrl->vfebase + VFE_LA_CFG); + CDBG("VFE Luma adaptation bank selection is 0x%x\n", + *(uint32_t *)&ctrl->vfeLaBankSel); +} + +void vfe_test_gen_start(struct vfe_cmd_test_gen_start *in) +{ + struct VFE_TestGen_ConfigCmdType cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.numFrame = in->numFrame; + cmd.pixelDataSelect = in->pixelDataSelect; + cmd.systematicDataSelect = in->systematicDataSelect; + cmd.pixelDataSize = (uint32_t)in->pixelDataSize; + cmd.hsyncEdge = (uint32_t)in->hsyncEdge; + cmd.vsyncEdge = (uint32_t)in->vsyncEdge; + cmd.imageWidth = in->imageWidth; + cmd.imageHeight = in->imageHeight; + cmd.sofOffset = in->startOfFrameOffset; + cmd.eofNOffset = in->endOfFrameNOffset; + cmd.solOffset = in->startOfLineOffset; + cmd.eolNOffset = in->endOfLineNOffset; + cmd.hBlankInterval = in->hbi; + cmd.vBlankInterval = in->vbl; + cmd.vBlankIntervalEnable = in->vblEnable; + cmd.sofDummy = in->startOfFrameDummyLine; + cmd.eofDummy = in->endOfFrameDummyLine; + cmd.unicolorBarSelect = in->unicolorBarSelect; + cmd.unicolorBarEnable = in->unicolorBarEnable; + cmd.splitEnable = in->colorBarsSplitEnable; + cmd.pixelPattern = (uint32_t)in->colorBarsPixelPattern; + cmd.rotatePeriod = in->colorBarsRotatePeriod; + cmd.randomSeed = in->testGenRandomSeed; + + vfe_prog_hw(ctrl->vfebase + VFE_HW_TESTGEN_CFG, + (uint32_t *) &cmd, sizeof(cmd)); +} + +void vfe_frame_skip_update(struct vfe_cmd_frame_skip_update *in) +{ + struct VFE_FRAME_SKIP_UpdateCmdType cmd; + + cmd.yPattern = in->output1Pattern; + cmd.cbcrPattern = in->output1Pattern; + vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_PATTERN, + (uint32_t *)&cmd, sizeof(cmd)); + + cmd.yPattern = in->output2Pattern; + cmd.cbcrPattern = in->output2Pattern; + vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_PATTERN, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_frame_skip_config(struct vfe_cmd_frame_skip_config *in) +{ + struct vfe_frame_skip_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeFrameSkip = *in; + + cmd.output2YPeriod = in->output2Period; + cmd.output2CbCrPeriod = in->output2Period; + cmd.output2YPattern = in->output2Pattern; + cmd.output2CbCrPattern = in->output2Pattern; + cmd.output1YPeriod = in->output1Period; + cmd.output1CbCrPeriod = in->output1Period; + cmd.output1YPattern = in->output1Pattern; + cmd.output1CbCrPattern = in->output1Pattern; + + vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_output_clamp_config(struct vfe_cmd_output_clamp_config *in) +{ + struct vfe_output_clamp_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + cmd.yChanMax = in->maxCh0; + cmd.cbChanMax = in->maxCh1; + cmd.crChanMax = in->maxCh2; + + cmd.yChanMin = in->minCh0; + cmd.cbChanMin = in->minCh1; + cmd.crChanMin = in->minCh2; + + vfe_prog_hw(ctrl->vfebase + VFE_CLAMP_MAX_CFG, (uint32_t *)&cmd, + sizeof(cmd)); +} + +void vfe_camif_frame_update(struct vfe_cmds_camif_frame *in) +{ + struct vfe_camifframe_update cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.pixelsPerLine = in->pixelsPerLine; + cmd.linesPerFrame = in->linesPerFrame; + + vfe_prog_hw(ctrl->vfebase + CAMIF_FRAME_CONFIG, (uint32_t *)&cmd, + sizeof(cmd)); +} + +void vfe_color_correction_config( + struct vfe_cmd_color_correction_config *in) +{ + struct vfe_color_correction_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + ctrl->vfeModuleEnableLocal.colorCorrectionEnable = in->enable; + + cmd.c0 = in->C0; + cmd.c1 = in->C1; + cmd.c2 = in->C2; + cmd.c3 = in->C3; + cmd.c4 = in->C4; + cmd.c5 = in->C5; + cmd.c6 = in->C6; + cmd.c7 = in->C7; + cmd.c8 = in->C8; + + cmd.k0 = in->K0; + cmd.k1 = in->K1; + cmd.k2 = in->K2; + + cmd.coefQFactor = in->coefQFactor; + + vfe_prog_hw(ctrl->vfebase + VFE_COLOR_CORRECT_COEFF_0, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_demosaic_abf_update(struct vfe_cmd_demosaic_abf_update *in) +{ +struct vfe_demosaic_cfg cmd; + struct vfe_demosaic_abf_cfg cmdabf; + uint32_t temp; + + memset(&cmd, 0, sizeof(cmd)); + temp = readl(ctrl->vfebase + VFE_DEMOSAIC_CFG); + + cmd = *((struct vfe_demosaic_cfg *)(&temp)); + cmd.abfEnable = in->abfUpdate.enable; + cmd.forceAbfOn = in->abfUpdate.forceOn; + cmd.abfShift = in->abfUpdate.shift; + vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG, + (uint32_t *)&cmd, sizeof(cmd)); + + cmdabf.lpThreshold = in->abfUpdate.lpThreshold; + cmdabf.ratio = in->abfUpdate.ratio; + cmdabf.minValue = in->abfUpdate.min; + cmdabf.maxValue = in->abfUpdate.max; + vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_ABF_CFG_0, + (uint32_t *)&cmdabf, sizeof(cmdabf)); +} + +void vfe_demosaic_bpc_update(struct vfe_cmd_demosaic_bpc_update *in) +{ + struct vfe_demosaic_cfg cmd; + struct vfe_demosaic_bpc_cfg cmdbpc; + uint32_t temp; + + memset(&cmd, 0, sizeof(cmd)); + + temp = readl(ctrl->vfebase + VFE_DEMOSAIC_CFG); + + cmd = *((struct vfe_demosaic_cfg *)(&temp)); + cmd.badPixelCorrEnable = in->bpcUpdate.enable; + cmd.fminThreshold = in->bpcUpdate.fminThreshold; + cmd.fmaxThreshold = in->bpcUpdate.fmaxThreshold; + + vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG, + (uint32_t *)&cmd, sizeof(cmd)); + + cmdbpc.blueDiffThreshold = in->bpcUpdate.blueDiffThreshold; + cmdbpc.redDiffThreshold = in->bpcUpdate.redDiffThreshold; + cmdbpc.greenDiffThreshold = in->bpcUpdate.greenDiffThreshold; + + vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_BPC_CFG_0, + (uint32_t *)&cmdbpc, sizeof(cmdbpc)); +} + +void vfe_demosaic_config(struct vfe_cmd_demosaic_config *in) +{ + struct vfe_demosaic_cfg cmd; + struct vfe_demosaic_bpc_cfg cmd_bpc; + struct vfe_demosaic_abf_cfg cmd_abf; + + memset(&cmd, 0, sizeof(cmd)); + memset(&cmd_bpc, 0, sizeof(cmd_bpc)); + memset(&cmd_abf, 0, sizeof(cmd_abf)); + + ctrl->vfeModuleEnableLocal.demosaicEnable = in->enable; + + cmd.abfEnable = in->abfConfig.enable; + cmd.badPixelCorrEnable = in->bpcConfig.enable; + cmd.forceAbfOn = in->abfConfig.forceOn; + cmd.abfShift = in->abfConfig.shift; + cmd.fminThreshold = in->bpcConfig.fminThreshold; + cmd.fmaxThreshold = in->bpcConfig.fmaxThreshold; + cmd.slopeShift = in->slopeShift; + + vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG, + (uint32_t *)&cmd, sizeof(cmd)); + + cmd_abf.lpThreshold = in->abfConfig.lpThreshold; + cmd_abf.ratio = in->abfConfig.ratio; + cmd_abf.minValue = in->abfConfig.min; + cmd_abf.maxValue = in->abfConfig.max; + + vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_ABF_CFG_0, + (uint32_t *)&cmd_abf, sizeof(cmd_abf)); + + cmd_bpc.blueDiffThreshold = in->bpcConfig.blueDiffThreshold; + cmd_bpc.redDiffThreshold = in->bpcConfig.redDiffThreshold; + cmd_bpc.greenDiffThreshold = in->bpcConfig.greenDiffThreshold; + + vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_BPC_CFG_0, + (uint32_t *)&cmd_bpc, sizeof(cmd_bpc)); +} + +void vfe_demux_channel_gain_update( + struct vfe_cmd_demux_channel_gain_config *in) +{ + struct vfe_demux_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.ch0EvenGain = in->ch0EvenGain; + cmd.ch0OddGain = in->ch0OddGain; + cmd.ch1Gain = in->ch1Gain; + cmd.ch2Gain = in->ch2Gain; + + vfe_prog_hw(ctrl->vfebase + VFE_DEMUX_GAIN_0, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_demux_channel_gain_config( + struct vfe_cmd_demux_channel_gain_config *in) +{ + struct vfe_demux_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.ch0EvenGain = in->ch0EvenGain; + cmd.ch0OddGain = in->ch0OddGain; + cmd.ch1Gain = in->ch1Gain; + cmd.ch2Gain = in->ch2Gain; + + vfe_prog_hw(ctrl->vfebase + VFE_DEMUX_GAIN_0, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_black_level_update(struct vfe_cmd_black_level_config *in) +{ + struct vfe_blacklevel_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + ctrl->vfeModuleEnableLocal.blackLevelCorrectionEnable = in->enable; + + cmd.evenEvenAdjustment = in->evenEvenAdjustment; + cmd.evenOddAdjustment = in->evenOddAdjustment; + cmd.oddEvenAdjustment = in->oddEvenAdjustment; + cmd.oddOddAdjustment = in->oddOddAdjustment; + + vfe_prog_hw(ctrl->vfebase + VFE_BLACK_EVEN_EVEN_VALUE, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_black_level_config(struct vfe_cmd_black_level_config *in) +{ + struct vfe_blacklevel_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.blackLevelCorrectionEnable = in->enable; + + cmd.evenEvenAdjustment = in->evenEvenAdjustment; + cmd.evenOddAdjustment = in->evenOddAdjustment; + cmd.oddEvenAdjustment = in->oddEvenAdjustment; + cmd.oddOddAdjustment = in->oddOddAdjustment; + + vfe_prog_hw(ctrl->vfebase + VFE_BLACK_EVEN_EVEN_VALUE, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_asf_update(struct vfe_cmd_asf_update *in) +{ + struct vfe_asf_update cmd; + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.asfEnable = in->enable; + + cmd.smoothEnable = in->smoothFilterEnabled; + cmd.sharpMode = in->sharpMode; + cmd.smoothCoeff1 = in->smoothCoefCenter; + cmd.smoothCoeff0 = in->smoothCoefSurr; + cmd.cropEnable = in->cropEnable; + cmd.sharpThresholdE1 = in->sharpThreshE1; + cmd.sharpDegreeK1 = in->sharpK1; + cmd.sharpDegreeK2 = in->sharpK2; + cmd.normalizeFactor = in->normalizeFactor; + cmd.sharpThresholdE2 = in->sharpThreshE2; + cmd.sharpThresholdE3 = in->sharpThreshE3; + cmd.sharpThresholdE4 = in->sharpThreshE4; + cmd.sharpThresholdE5 = in->sharpThreshE5; + cmd.F1Coeff0 = in->filter1Coefficients[0]; + cmd.F1Coeff1 = in->filter1Coefficients[1]; + cmd.F1Coeff2 = in->filter1Coefficients[2]; + cmd.F1Coeff3 = in->filter1Coefficients[3]; + cmd.F1Coeff4 = in->filter1Coefficients[4]; + cmd.F1Coeff5 = in->filter1Coefficients[5]; + cmd.F1Coeff6 = in->filter1Coefficients[6]; + cmd.F1Coeff7 = in->filter1Coefficients[7]; + cmd.F1Coeff8 = in->filter1Coefficients[8]; + cmd.F2Coeff0 = in->filter2Coefficients[0]; + cmd.F2Coeff1 = in->filter2Coefficients[1]; + cmd.F2Coeff2 = in->filter2Coefficients[2]; + cmd.F2Coeff3 = in->filter2Coefficients[3]; + cmd.F2Coeff4 = in->filter2Coefficients[4]; + cmd.F2Coeff5 = in->filter2Coefficients[5]; + cmd.F2Coeff6 = in->filter2Coefficients[6]; + cmd.F2Coeff7 = in->filter2Coefficients[7]; + cmd.F2Coeff8 = in->filter2Coefficients[8]; + + vfe_prog_hw(ctrl->vfebase + VFE_ASF_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_asf_config(struct vfe_cmd_asf_config *in) +{ + struct vfe_asf_update cmd; + struct vfe_asfcrop_cfg cmd2; + + memset(&cmd, 0, sizeof(cmd)); + memset(&cmd2, 0, sizeof(cmd2)); + + ctrl->vfeModuleEnableLocal.asfEnable = in->enable; + + cmd.smoothEnable = in->smoothFilterEnabled; + cmd.sharpMode = in->sharpMode; + cmd.smoothCoeff0 = in->smoothCoefCenter; + cmd.smoothCoeff1 = in->smoothCoefSurr; + cmd.cropEnable = in->cropEnable; + cmd.sharpThresholdE1 = in->sharpThreshE1; + cmd.sharpDegreeK1 = in->sharpK1; + cmd.sharpDegreeK2 = in->sharpK2; + cmd.normalizeFactor = in->normalizeFactor; + cmd.sharpThresholdE2 = in->sharpThreshE2; + cmd.sharpThresholdE3 = in->sharpThreshE3; + cmd.sharpThresholdE4 = in->sharpThreshE4; + cmd.sharpThresholdE5 = in->sharpThreshE5; + cmd.F1Coeff0 = in->filter1Coefficients[0]; + cmd.F1Coeff1 = in->filter1Coefficients[1]; + cmd.F1Coeff2 = in->filter1Coefficients[2]; + cmd.F1Coeff3 = in->filter1Coefficients[3]; + cmd.F1Coeff4 = in->filter1Coefficients[4]; + cmd.F1Coeff5 = in->filter1Coefficients[5]; + cmd.F1Coeff6 = in->filter1Coefficients[6]; + cmd.F1Coeff7 = in->filter1Coefficients[7]; + cmd.F1Coeff8 = in->filter1Coefficients[8]; + cmd.F2Coeff0 = in->filter2Coefficients[0]; + cmd.F2Coeff1 = in->filter2Coefficients[1]; + cmd.F2Coeff2 = in->filter2Coefficients[2]; + cmd.F2Coeff3 = in->filter2Coefficients[3]; + cmd.F2Coeff4 = in->filter2Coefficients[4]; + cmd.F2Coeff5 = in->filter2Coefficients[5]; + cmd.F2Coeff6 = in->filter2Coefficients[6]; + cmd.F2Coeff7 = in->filter2Coefficients[7]; + cmd.F2Coeff8 = in->filter2Coefficients[8]; + + vfe_prog_hw(ctrl->vfebase + VFE_ASF_CFG, + (uint32_t *)&cmd, sizeof(cmd)); + + cmd2.firstLine = in->cropFirstLine; + cmd2.lastLine = in->cropLastLine; + cmd2.firstPixel = in->cropFirstPixel; + cmd2.lastPixel = in->cropLastPixel; + + vfe_prog_hw(ctrl->vfebase + VFE_ASF_CROP_WIDTH_CFG, + (uint32_t *)&cmd2, sizeof(cmd2)); +} + +void vfe_white_balance_config(struct vfe_cmd_white_balance_config *in) +{ + struct vfe_wb_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.whiteBalanceEnable = + in->enable; + + cmd.ch0Gain = in->ch0Gain; + cmd.ch1Gain = in->ch1Gain; + cmd.ch2Gain = in->ch2Gain; + + vfe_prog_hw(ctrl->vfebase + VFE_WB_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_chroma_sup_config(struct vfe_cmd_chroma_suppression_config *in) +{ + struct vfe_chroma_suppress_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.chromaSuppressionEnable = in->enable; + + cmd.m1 = in->m1; + cmd.m3 = in->m3; + cmd.n1 = in->n1; + cmd.n3 = in->n3; + cmd.mm1 = in->mm1; + cmd.nn1 = in->nn1; + + vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_SUPPRESS_CFG_0, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_roll_off_config(struct vfe_cmd_roll_off_config *in) +{ + struct vfe_rolloff_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.lensRollOffEnable = in->enable; + + cmd.gridWidth = in->gridWidth; + cmd.gridHeight = in->gridHeight; + cmd.yDelta = in->yDelta; + cmd.gridX = in->gridXIndex; + cmd.gridY = in->gridYIndex; + cmd.pixelX = in->gridPixelXIndex; + cmd.pixelY = in->gridPixelYIndex; + cmd.yDeltaAccum = in->yDeltaAccum; + + vfe_prog_hw(ctrl->vfebase + VFE_ROLLOFF_CFG_0, + (uint32_t *)&cmd, sizeof(cmd)); + + vfe_write_lens_roll_off_table(in); +} + +void vfe_chroma_subsample_config( + struct vfe_cmd_chroma_subsample_config *in) +{ + struct vfe_chromasubsample_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.chromaSubsampleEnable = in->enable; + + cmd.hCositedPhase = in->hCositedPhase; + cmd.vCositedPhase = in->vCositedPhase; + cmd.hCosited = in->hCosited; + cmd.vCosited = in->vCosited; + cmd.hsubSampleEnable = in->hsubSampleEnable; + cmd.vsubSampleEnable = in->vsubSampleEnable; + cmd.cropEnable = in->cropEnable; + cmd.cropWidthLastPixel = in->cropWidthLastPixel; + cmd.cropWidthFirstPixel = in->cropWidthFirstPixel; + cmd.cropHeightLastLine = in->cropHeightLastLine; + cmd.cropHeightFirstLine = in->cropHeightFirstLine; + + vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_SUBSAMPLE_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_chroma_enhan_config(struct vfe_cmd_chroma_enhan_config *in) +{ + struct vfe_chroma_enhance_cfg cmd; + struct vfe_color_convert_cfg cmd2; + + memset(&cmd, 0, sizeof(cmd)); + memset(&cmd2, 0, sizeof(cmd2)); + + ctrl->vfeModuleEnableLocal.chromaEnhanEnable = in->enable; + + cmd.ap = in->ap; + cmd.am = in->am; + cmd.bp = in->bp; + cmd.bm = in->bm; + cmd.cp = in->cp; + cmd.cm = in->cm; + cmd.dp = in->dp; + cmd.dm = in->dm; + cmd.kcb = in->kcb; + cmd.kcr = in->kcr; + + cmd2.v0 = in->RGBtoYConversionV0; + cmd2.v1 = in->RGBtoYConversionV1; + cmd2.v2 = in->RGBtoYConversionV2; + cmd2.ConvertOffset = in->RGBtoYConversionOffset; + + vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_ENHAN_A, + (uint32_t *)&cmd, sizeof(cmd)); + + vfe_prog_hw(ctrl->vfebase + VFE_COLOR_CONVERT_COEFF_0, + (uint32_t *)&cmd2, sizeof(cmd2)); +} + +void vfe_scaler2cbcr_config(struct vfe_cmd_scaler2_config *in) +{ + struct vfe_scaler2_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.scaler2CbcrEnable = in->enable; + + cmd.hEnable = in->hconfig.enable; + cmd.vEnable = in->vconfig.enable; + cmd.inWidth = in->hconfig.inputSize; + cmd.outWidth = in->hconfig.outputSize; + cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor; + cmd.horizInterResolution = in->hconfig.interpolationResolution; + cmd.inHeight = in->vconfig.inputSize; + cmd.outHeight = in->vconfig.outputSize; + cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor; + cmd.vertInterResolution = in->vconfig.interpolationResolution; + + vfe_prog_hw(ctrl->vfebase + VFE_SCALE_CBCR_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_scaler2y_config(struct vfe_cmd_scaler2_config *in) +{ + struct vfe_scaler2_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.scaler2YEnable = in->enable; + + cmd.hEnable = in->hconfig.enable; + cmd.vEnable = in->vconfig.enable; + cmd.inWidth = in->hconfig.inputSize; + cmd.outWidth = in->hconfig.outputSize; + cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor; + cmd.horizInterResolution = in->hconfig.interpolationResolution; + cmd.inHeight = in->vconfig.inputSize; + cmd.outHeight = in->vconfig.outputSize; + cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor; + cmd.vertInterResolution = in->vconfig.interpolationResolution; + + vfe_prog_hw(ctrl->vfebase + VFE_SCALE_Y_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_main_scaler_config(struct vfe_cmd_main_scaler_config *in) +{ + struct vfe_main_scaler_cfg cmd; + + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.mainScalerEnable = in->enable; + + cmd.hEnable = in->hconfig.enable; + cmd.vEnable = in->vconfig.enable; + cmd.inWidth = in->hconfig.inputSize; + cmd.outWidth = in->hconfig.outputSize; + cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor; + cmd.horizInterResolution = in->hconfig.interpolationResolution; + cmd.horizMNInit = in->MNInitH.MNCounterInit; + cmd.horizPhaseInit = in->MNInitH.phaseInit; + cmd.inHeight = in->vconfig.inputSize; + cmd.outHeight = in->vconfig.outputSize; + cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor; + cmd.vertInterResolution = in->vconfig.interpolationResolution; + cmd.vertMNInit = in->MNInitV.MNCounterInit; + cmd.vertPhaseInit = in->MNInitV.phaseInit; + + vfe_prog_hw(ctrl->vfebase + VFE_SCALE_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_stats_wb_exp_stop(void) +{ + ctrl->vfeStatsCmdLocal.axwEnable = FALSE; + ctrl->vfeImaskLocal.awbPingpongIrq = FALSE; +} + +void vfe_stats_update_wb_exp(struct vfe_cmd_stats_wb_exp_update *in) +{ + struct vfe_statsawb_update cmd; + struct vfe_statsawbae_update cmd2; + + memset(&cmd, 0, sizeof(cmd)); + memset(&cmd2, 0, sizeof(cmd2)); + + cmd.m1 = in->awbMCFG[0]; + cmd.m2 = in->awbMCFG[1]; + cmd.m3 = in->awbMCFG[2]; + cmd.m4 = in->awbMCFG[3]; + cmd.c1 = in->awbCCFG[0]; + cmd.c2 = in->awbCCFG[1]; + cmd.c3 = in->awbCCFG[2]; + cmd.c4 = in->awbCCFG[3]; + vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWB_MCFG, + (uint32_t *)&cmd, sizeof(cmd)); + + cmd2.aeRegionCfg = in->wbExpRegions; + cmd2.aeSubregionCfg = in->wbExpSubRegion; + cmd2.awbYMin = in->awbYMin; + cmd2.awbYMax = in->awbYMax; + vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWBAE_CFG, + (uint32_t *)&cmd2, sizeof(cmd2)); +} + +void vfe_stats_update_af(struct vfe_cmd_stats_af_update *in) +{ + struct vfe_statsaf_update cmd; + memset(&cmd, 0, sizeof(cmd)); + + cmd.windowVOffset = in->windowVOffset; + cmd.windowHOffset = in->windowHOffset; + cmd.windowMode = in->windowMode; + cmd.windowHeight = in->windowHeight; + cmd.windowWidth = in->windowWidth; + + vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_stats_start_wb_exp(struct vfe_cmd_stats_wb_exp_start *in) +{ + struct vfe_statsawb_update cmd; + struct vfe_statsawbae_update cmd2; + struct vfe_statsaxw_hdr_cfg cmd3; + + ctrl->vfeStatsCmdLocal.axwEnable = in->enable; + ctrl->vfeImaskLocal.awbPingpongIrq = TRUE; + + memset(&cmd, 0, sizeof(cmd)); + memset(&cmd2, 0, sizeof(cmd2)); + memset(&cmd3, 0, sizeof(cmd3)); + + cmd.m1 = in->awbMCFG[0]; + cmd.m2 = in->awbMCFG[1]; + cmd.m3 = in->awbMCFG[2]; + cmd.m4 = in->awbMCFG[3]; + cmd.c1 = in->awbCCFG[0]; + cmd.c2 = in->awbCCFG[1]; + cmd.c3 = in->awbCCFG[2]; + cmd.c4 = in->awbCCFG[3]; + vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWB_MCFG, + (uint32_t *)&cmd, sizeof(cmd)); + + cmd2.aeRegionCfg = in->wbExpRegions; + cmd2.aeSubregionCfg = in->wbExpSubRegion; + cmd2.awbYMin = in->awbYMin; + cmd2.awbYMax = in->awbYMax; + vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWBAE_CFG, + (uint32_t *)&cmd2, sizeof(cmd2)); + + cmd3.axwHeader = in->axwHeader; + vfe_prog_hw(ctrl->vfebase + VFE_STATS_AXW_HEADER, + (uint32_t *)&cmd3, sizeof(cmd3)); +} + +void vfe_stats_start_af(struct vfe_cmd_stats_af_start *in) +{ + struct vfe_statsaf_update cmd; + struct vfe_statsaf_cfg cmd2; + + memset(&cmd, 0, sizeof(cmd)); + memset(&cmd2, 0, sizeof(cmd2)); + +ctrl->vfeStatsCmdLocal.autoFocusEnable = in->enable; +ctrl->vfeImaskLocal.afPingpongIrq = TRUE; + + cmd.windowVOffset = in->windowVOffset; + cmd.windowHOffset = in->windowHOffset; + cmd.windowMode = in->windowMode; + cmd.windowHeight = in->windowHeight; + cmd.windowWidth = in->windowWidth; + + vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_CFG, + (uint32_t *)&cmd, sizeof(cmd)); + + cmd2.a00 = in->highPassCoef[0]; + cmd2.a04 = in->highPassCoef[1]; + cmd2.a20 = in->highPassCoef[2]; + cmd2.a21 = in->highPassCoef[3]; + cmd2.a22 = in->highPassCoef[4]; + cmd2.a23 = in->highPassCoef[5]; + cmd2.a24 = in->highPassCoef[6]; + cmd2.fvMax = in->metricMax; + cmd2.fvMetric = in->metricSelection; + cmd2.afHeader = in->bufferHeader; + cmd2.entry00 = in->gridForMultiWindows[0]; + cmd2.entry01 = in->gridForMultiWindows[1]; + cmd2.entry02 = in->gridForMultiWindows[2]; + cmd2.entry03 = in->gridForMultiWindows[3]; + cmd2.entry10 = in->gridForMultiWindows[4]; + cmd2.entry11 = in->gridForMultiWindows[5]; + cmd2.entry12 = in->gridForMultiWindows[6]; + cmd2.entry13 = in->gridForMultiWindows[7]; + cmd2.entry20 = in->gridForMultiWindows[8]; + cmd2.entry21 = in->gridForMultiWindows[9]; + cmd2.entry22 = in->gridForMultiWindows[10]; + cmd2.entry23 = in->gridForMultiWindows[11]; + cmd2.entry30 = in->gridForMultiWindows[12]; + cmd2.entry31 = in->gridForMultiWindows[13]; + cmd2.entry32 = in->gridForMultiWindows[14]; + cmd2.entry33 = in->gridForMultiWindows[15]; + + vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_GRID_0, + (uint32_t *)&cmd2, sizeof(cmd2)); +} + +void vfe_stats_setting(struct vfe_cmd_stats_setting *in) +{ + struct vfe_statsframe cmd1; + struct vfe_busstats_wrprio cmd2; + + memset(&cmd1, 0, sizeof(cmd1)); + memset(&cmd2, 0, sizeof(cmd2)); + + ctrl->afStatsControl.addressBuffer[0] = in->afBuffer[0]; + ctrl->afStatsControl.addressBuffer[1] = in->afBuffer[1]; + ctrl->afStatsControl.nextFrameAddrBuf = in->afBuffer[2]; + + ctrl->awbStatsControl.addressBuffer[0] = in->awbBuffer[0]; + ctrl->awbStatsControl.addressBuffer[1] = in->awbBuffer[1]; + ctrl->awbStatsControl.nextFrameAddrBuf = in->awbBuffer[2]; + + cmd1.lastPixel = in->frameHDimension; + cmd1.lastLine = in->frameVDimension; + vfe_prog_hw(ctrl->vfebase + VFE_STATS_FRAME_SIZE, + (uint32_t *)&cmd1, sizeof(cmd1)); + + cmd2.afBusPriority = in->afBusPriority; + cmd2.awbBusPriority = in->awbBusPriority; + cmd2.histBusPriority = in->histBusPriority; + cmd2.afBusPriorityEn = in->afBusPrioritySelection; + cmd2.awbBusPriorityEn = in->awbBusPrioritySelection; + cmd2.histBusPriorityEn = in->histBusPrioritySelection; + + vfe_prog_hw(ctrl->vfebase + VFE_BUS_STATS_WR_PRIORITY, + (uint32_t *)&cmd2, sizeof(cmd2)); + + /* Program the bus ping pong address for statistics modules. */ + writel(in->afBuffer[0], ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR); + writel(in->afBuffer[1], ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR); + writel(in->awbBuffer[0], + ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR); + writel(in->awbBuffer[1], + ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR); + writel(in->histBuffer[0], + ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PING_ADDR); + writel(in->histBuffer[1], + ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PONG_ADDR); +} + +void vfe_axi_input_config(struct vfe_cmd_axi_input_config *in) +{ + struct VFE_AxiInputCmdType cmd; + uint32_t xSizeWord, axiRdUnpackPattern; + uint8_t axiInputPpw; + uint32_t busPingpongRdIrqEnable; + + ctrl->vfeImaskLocal.rdPingpongIrq = TRUE; + + switch (in->pixelSize) { + case VFE_RAW_PIXEL_DATA_SIZE_10BIT: + ctrl->axiInputDataSize = VFE_RAW_PIXEL_DATA_SIZE_10BIT; + break; + + case VFE_RAW_PIXEL_DATA_SIZE_12BIT: + ctrl->axiInputDataSize = VFE_RAW_PIXEL_DATA_SIZE_12BIT; + break; + + case VFE_RAW_PIXEL_DATA_SIZE_8BIT: + default: + ctrl->axiInputDataSize = VFE_RAW_PIXEL_DATA_SIZE_8BIT; + break; + } + + memset(&cmd, 0, sizeof(cmd)); + + switch (in->pixelSize) { + case VFE_RAW_PIXEL_DATA_SIZE_10BIT: + axiInputPpw = 6; + axiRdUnpackPattern = 0xD43210; + break; + + case VFE_RAW_PIXEL_DATA_SIZE_12BIT: + axiInputPpw = 5; + axiRdUnpackPattern = 0xC3210; + break; + + case VFE_RAW_PIXEL_DATA_SIZE_8BIT: + default: + axiInputPpw = 8; + axiRdUnpackPattern = 0xF6543210; + break; + } + + xSizeWord = + ((((in->xOffset % axiInputPpw) + in->xSize) + + (axiInputPpw-1)) / axiInputPpw) - 1; + + cmd.stripeStartAddr0 = in->fragAddr[0]; + cmd.stripeStartAddr1 = in->fragAddr[1]; + cmd.stripeStartAddr2 = in->fragAddr[2]; + cmd.stripeStartAddr3 = in->fragAddr[3]; + cmd.ySize = in->ySize; + cmd.yOffsetDelta = 0; + cmd.xSizeWord = xSizeWord; + cmd.burstLength = 1; + cmd.NumOfRows = in->numOfRows; + cmd.RowIncrement = + (in->rowIncrement + (axiInputPpw-1))/axiInputPpw; + cmd.mainUnpackHeight = in->ySize; + cmd.mainUnpackWidth = in->xSize - 1; + cmd.mainUnpackHbiSel = (uint32_t)in->unpackHbi; + cmd.mainUnpackPhase = in->unpackPhase; + cmd.unpackPattern = axiRdUnpackPattern; + cmd.padLeft = in->padRepeatCountLeft; + cmd.padRight = in->padRepeatCountRight; + cmd.padTop = in->padRepeatCountTop; + cmd.padBottom = in->padRepeatCountBottom; + cmd.leftUnpackPattern0 = in->padLeftComponentSelectCycle0; + cmd.leftUnpackPattern1 = in->padLeftComponentSelectCycle1; + cmd.leftUnpackPattern2 = in->padLeftComponentSelectCycle2; + cmd.leftUnpackPattern3 = in->padLeftComponentSelectCycle3; + cmd.leftUnpackStop0 = in->padLeftStopCycle0; + cmd.leftUnpackStop1 = in->padLeftStopCycle1; + cmd.leftUnpackStop2 = in->padLeftStopCycle2; + cmd.leftUnpackStop3 = in->padLeftStopCycle3; + cmd.rightUnpackPattern0 = in->padRightComponentSelectCycle0; + cmd.rightUnpackPattern1 = in->padRightComponentSelectCycle1; + cmd.rightUnpackPattern2 = in->padRightComponentSelectCycle2; + cmd.rightUnpackPattern3 = in->padRightComponentSelectCycle3; + cmd.rightUnpackStop0 = in->padRightStopCycle0; + cmd.rightUnpackStop1 = in->padRightStopCycle1; + cmd.rightUnpackStop2 = in->padRightStopCycle2; + cmd.rightUnpackStop3 = in->padRightStopCycle3; + cmd.topUnapckPattern = in->padTopLineCount; + cmd.bottomUnapckPattern = in->padBottomLineCount; + + /* program vfe_bus_cfg */ + vfe_prog_hw(ctrl->vfebase + VFE_BUS_STRIPE_RD_ADDR_0, + (uint32_t *)&cmd, sizeof(cmd)); + + /* hacking code, put it to default value */ + busPingpongRdIrqEnable = 0xf; + + writel(busPingpongRdIrqEnable, + ctrl->vfebase + VFE_BUS_PINGPONG_IRQ_EN); +} + +void vfe_stats_config(struct vfe_cmd_stats_setting *in) +{ + ctrl->afStatsControl.addressBuffer[0] = in->afBuffer[0]; + ctrl->afStatsControl.addressBuffer[1] = in->afBuffer[1]; + ctrl->afStatsControl.nextFrameAddrBuf = in->afBuffer[2]; + + ctrl->awbStatsControl.addressBuffer[0] = in->awbBuffer[0]; + ctrl->awbStatsControl.addressBuffer[1] = in->awbBuffer[1]; + ctrl->awbStatsControl.nextFrameAddrBuf = in->awbBuffer[2]; + + vfe_stats_setting(in); +} + +void vfe_axi_output_config( + struct vfe_cmd_axi_output_config *in) +{ + /* local variable */ + uint32_t *pcircle; + uint32_t *pdest; + uint32_t *psrc; + uint8_t i; + uint8_t fcnt; + uint16_t axioutpw = 8; + + /* parameters check, condition and usage mode check */ + ctrl->encPath.fragCount = in->output2.fragmentCount; + if (ctrl->encPath.fragCount > 1) + ctrl->encPath.multiFrag = TRUE; + + ctrl->viewPath.fragCount = in->output1.fragmentCount; + if (ctrl->viewPath.fragCount > 1) + ctrl->viewPath.multiFrag = TRUE; + + /* VFE_BUS_CFG. raw data size */ + ctrl->vfeBusConfigLocal.rawPixelDataSize = in->outputDataSize; + + switch (in->outputDataSize) { + case VFE_RAW_PIXEL_DATA_SIZE_8BIT: + axioutpw = 8; + break; + + case VFE_RAW_PIXEL_DATA_SIZE_10BIT: + axioutpw = 6; + break; + + case VFE_RAW_PIXEL_DATA_SIZE_12BIT: + axioutpw = 5; + break; + } + + ctrl->axiOutputMode = in->outputMode; + + CDBG("axiOutputMode = %d\n", ctrl->axiOutputMode); + + switch (ctrl->axiOutputMode) { + case VFE_AXI_OUTPUT_MODE_Output1: { + ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_DISABLED; + + ctrl->encPath.pathEnabled = FALSE; + ctrl->vfeImaskLocal.encIrq = FALSE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = FALSE; + ctrl->viewPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.viewIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE; + + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_Output1 */ + break; + + case VFE_AXI_OUTPUT_MODE_Output2: { + ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_DISABLED; + + ctrl->encPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.encIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; + + ctrl->viewPath.pathEnabled = FALSE; + ctrl->vfeImaskLocal.viewIrq = FALSE; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = FALSE; + + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_Output2 */ + break; + + case VFE_AXI_OUTPUT_MODE_Output1AndOutput2: { + ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_DISABLED; + + ctrl->encPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.encIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; + ctrl->viewPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.viewIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE; + + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_Output1AndOutput2 */ + break; + + case VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2: { + /* For raw snapshot, we need both ping and pong buffer + * initialized to the same address. Otherwise, if we + * leave the pong buffer to NULL, there will be axi_error. + * Note that ideally we should deal with this at upper layer, + * which is in msm_vfe8x.c */ + if (!in->output2.outputCbcr.outFragments[1][0]) { + in->output2.outputCbcr.outFragments[1][0] = + in->output2.outputCbcr.outFragments[0][0]; + } + + ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = FALSE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_ENC_CBCR_PATH; + + ctrl->encPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.encIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_CBCR_ONLY; + + ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; + + ctrl->viewPath.pathEnabled = FALSE; + ctrl->vfeImaskLocal.viewIrq = FALSE; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = FALSE; + + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2 */ + break; + + case VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1: { + ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_VIEW_CBCR_PATH; + + ctrl->encPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.encIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; + + ctrl->viewPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.viewIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_CBCR_ONLY; + + ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE; + + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1 */ + break; + + case VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2: { + ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_ENC_CBCR_PATH; + + ctrl->encPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.encIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_CBCR_ONLY; + + ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; + + ctrl->viewPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.viewIrq = TRUE; + + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE; + + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2 */ + break; + + case VFE_AXI_LAST_OUTPUT_MODE_ENUM: + break; + } /* switch */ + + /* Save the addresses for each path. */ + /* output2 path */ + fcnt = ctrl->encPath.fragCount; + + pcircle = ctrl->encPath.yPath.addressBuffer; + pdest = ctrl->encPath.nextFrameAddrBuf; + + psrc = &(in->output2.outputY.outFragments[0][0]); + for (i = 0; i < fcnt; i++) + *pcircle++ = *psrc++; + + psrc = &(in->output2.outputY.outFragments[1][0]); + for (i = 0; i < fcnt; i++) + *pcircle++ = *psrc++; + + psrc = &(in->output2.outputY.outFragments[2][0]); + for (i = 0; i < fcnt; i++) + *pdest++ = *psrc++; + + pcircle = ctrl->encPath.cbcrPath.addressBuffer; + + psrc = &(in->output2.outputCbcr.outFragments[0][0]); + for (i = 0; i < fcnt; i++) + *pcircle++ = *psrc++; + + psrc = &(in->output2.outputCbcr.outFragments[1][0]); + for (i = 0; i < fcnt; i++) + *pcircle++ = *psrc++; + + psrc = &(in->output2.outputCbcr.outFragments[2][0]); + for (i = 0; i < fcnt; i++) + *pdest++ = *psrc++; + + vfe_set_bus_pipo_addr(&ctrl->viewPath, &ctrl->encPath); + + ctrl->encPath.ackPending = FALSE; + ctrl->encPath.currentFrame = ping; + ctrl->encPath.whichOutputPath = 1; + ctrl->encPath.yPath.fragIndex = 2; + ctrl->encPath.cbcrPath.fragIndex = 2; + ctrl->encPath.yPath.hwCurrentFlag = ping; + ctrl->encPath.cbcrPath.hwCurrentFlag = ping; + + /* output1 path */ + pcircle = ctrl->viewPath.yPath.addressBuffer; + pdest = ctrl->viewPath.nextFrameAddrBuf; + fcnt = ctrl->viewPath.fragCount; + + psrc = &(in->output1.outputY.outFragments[0][0]); + for (i = 0; i < fcnt; i++) + *pcircle++ = *psrc++; + + psrc = &(in->output1.outputY.outFragments[1][0]); + for (i = 0; i < fcnt; i++) + *pcircle++ = *psrc++; + + psrc = &(in->output1.outputY.outFragments[2][0]); + for (i = 0; i < fcnt; i++) + *pdest++ = *psrc++; + + pcircle = ctrl->viewPath.cbcrPath.addressBuffer; + + psrc = &(in->output1.outputCbcr.outFragments[0][0]); + for (i = 0; i < fcnt; i++) + *pcircle++ = *psrc++; + + psrc = &(in->output1.outputCbcr.outFragments[1][0]); + for (i = 0; i < fcnt; i++) + *pcircle++ = *psrc++; + + psrc = &(in->output1.outputCbcr.outFragments[2][0]); + for (i = 0; i < fcnt; i++) + *pdest++ = *psrc++; + + ctrl->viewPath.ackPending = FALSE; + ctrl->viewPath.currentFrame = ping; + ctrl->viewPath.whichOutputPath = 0; + ctrl->viewPath.yPath.fragIndex = 2; + ctrl->viewPath.cbcrPath.fragIndex = 2; + ctrl->viewPath.yPath.hwCurrentFlag = ping; + ctrl->viewPath.cbcrPath.hwCurrentFlag = ping; + + /* call to program the registers. */ + vfe_axi_output(in, &ctrl->viewPath, &ctrl->encPath, axioutpw); +} + +void vfe_camif_config(struct vfe_cmd_camif_config *in) +{ + struct vfe_camifcfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + CDBG("camif.frame pixelsPerLine = %d\n", in->frame.pixelsPerLine); + CDBG("camif.frame linesPerFrame = %d\n", in->frame.linesPerFrame); + CDBG("camif.window firstpixel = %d\n", in->window.firstpixel); + CDBG("camif.window lastpixel = %d\n", in->window.lastpixel); + CDBG("camif.window firstline = %d\n", in->window.firstline); + CDBG("camif.window lastline = %d\n", in->window.lastline); + + /* determine if epoch interrupt needs to be enabled. */ + if ((in->epoch1.enable == TRUE) && + (in->epoch1.lineindex <= + in->frame.linesPerFrame)) + ctrl->vfeImaskLocal.camifEpoch1Irq = 1; + + if ((in->epoch2.enable == TRUE) && + (in->epoch2.lineindex <= + in->frame.linesPerFrame)) { + ctrl->vfeImaskLocal.camifEpoch2Irq = 1; + } + + /* save the content to program CAMIF_CONFIG seperately. */ + ctrl->vfeCamifConfigLocal.camifCfgFromCmd = in->camifConfig; + + /* EFS_Config */ + cmd.efsEndOfLine = in->EFS.efsendofline; + cmd.efsStartOfLine = in->EFS.efsstartofline; + cmd.efsEndOfFrame = in->EFS.efsendofframe; + cmd.efsStartOfFrame = in->EFS.efsstartofframe; + + /* Frame Config */ + cmd.frameConfigPixelsPerLine = in->frame.pixelsPerLine; + cmd.frameConfigLinesPerFrame = in->frame.linesPerFrame; + + /* Window Width Config */ + cmd.windowWidthCfgLastPixel = in->window.lastpixel; + cmd.windowWidthCfgFirstPixel = in->window.firstpixel; + + /* Window Height Config */ + cmd.windowHeightCfglastLine = in->window.lastline; + cmd.windowHeightCfgfirstLine = in->window.firstline; + + /* Subsample 1 Config */ + cmd.subsample1CfgPixelSkip = in->subsample.pixelskipmask; + cmd.subsample1CfgLineSkip = in->subsample.lineskipmask; + + /* Subsample 2 Config */ + cmd.subsample2CfgFrameSkip = in->subsample.frameskip; + cmd.subsample2CfgFrameSkipMode = in->subsample.frameskipmode; + cmd.subsample2CfgPixelSkipWrap = in->subsample.pixelskipwrap; + + /* Epoch Interrupt */ + cmd.epoch1Line = in->epoch1.lineindex; + cmd.epoch2Line = in->epoch2.lineindex; + + vfe_prog_hw(ctrl->vfebase + CAMIF_EFS_CONFIG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_fov_crop_config(struct vfe_cmd_fov_crop_config *in) +{ + struct vfe_fov_crop_cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + + ctrl->vfeModuleEnableLocal.cropEnable = in->enable; + + /* FOV Corp, Part 1 */ + cmd.lastPixel = in->lastPixel; + cmd.firstPixel = in->firstPixel; + + /* FOV Corp, Part 2 */ + cmd.lastLine = in->lastLine; + cmd.firstLine = in->firstLine; + + vfe_prog_hw(ctrl->vfebase + VFE_CROP_WIDTH_CFG, + (uint32_t *)&cmd, sizeof(cmd)); +} + +void vfe_get_hw_version(struct vfe_cmd_hw_version *out) +{ + uint32_t vfeHwVersionPacked; + struct vfe_hw_ver ver; + + vfeHwVersionPacked = readl(ctrl->vfebase + VFE_HW_VERSION); + + ver = *((struct vfe_hw_ver *)&vfeHwVersionPacked); + + out->coreVersion = ver.coreVersion; + out->minorVersion = ver.minorVersion; + out->majorVersion = ver.majorVersion; +} + +static void vfe_reset_internal_variables(void) +{ + unsigned long flags; + + /* local variables to program the hardware. */ + ctrl->vfeImaskPacked = 0; + ctrl->vfeImaskCompositePacked = 0; + + /* FALSE = disable, 1 = enable. */ + memset(&ctrl->vfeModuleEnableLocal, 0, + sizeof(ctrl->vfeModuleEnableLocal)); + + /* 0 = disable, 1 = enable */ + memset(&ctrl->vfeCamifConfigLocal, 0, + sizeof(ctrl->vfeCamifConfigLocal)); + /* 0 = disable, 1 = enable */ + memset(&ctrl->vfeImaskLocal, 0, sizeof(ctrl->vfeImaskLocal)); + memset(&ctrl->vfeStatsCmdLocal, 0, sizeof(ctrl->vfeStatsCmdLocal)); + memset(&ctrl->vfeBusConfigLocal, 0, sizeof(ctrl->vfeBusConfigLocal)); + memset(&ctrl->vfeBusPmConfigLocal, 0, + sizeof(ctrl->vfeBusPmConfigLocal)); + memset(&ctrl->vfeBusCmdLocal, 0, sizeof(ctrl->vfeBusCmdLocal)); + memset(&ctrl->vfeInterruptNameLocal, 0, + sizeof(ctrl->vfeInterruptNameLocal)); + memset(&ctrl->vfeDroppedFrameCounts, 0, + sizeof(ctrl->vfeDroppedFrameCounts)); + memset(&ctrl->vfeIrqThreadMsgLocal, 0, + sizeof(ctrl->vfeIrqThreadMsgLocal)); + + /* state control variables */ + ctrl->vfeStartAckPendingFlag = FALSE; + ctrl->vfeStopAckPending = FALSE; + ctrl->vfeIrqCompositeMaskLocal.ceDoneSel = 0; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + spin_lock_irqsave(&ctrl->state_lock, flags); + ctrl->vstate = VFE_STATE_IDLE; + spin_unlock_irqrestore(&ctrl->state_lock, flags); + + ctrl->axiOutputMode = VFE_AXI_LAST_OUTPUT_MODE_ENUM; + /* 0 for continuous mode, 1 for snapshot mode */ + ctrl->vfeOperationMode = VFE_START_OPERATION_MODE_CONTINUOUS; + ctrl->vfeSnapShotCount = 0; + ctrl->vfeStatsPingPongReloadFlag = FALSE; + /* this is unsigned 32 bit integer. */ + ctrl->vfeFrameId = 0; + ctrl->vfeFrameSkip.output1Pattern = 0xffffffff; + ctrl->vfeFrameSkip.output1Period = 31; + ctrl->vfeFrameSkip.output2Pattern = 0xffffffff; + ctrl->vfeFrameSkip.output2Period = 31; + ctrl->vfeFrameSkipPattern = 0xffffffff; + ctrl->vfeFrameSkipCount = 0; + ctrl->vfeFrameSkipPeriod = 31; + + memset((void *)&ctrl->encPath, 0, sizeof(ctrl->encPath)); + memset((void *)&ctrl->viewPath, 0, sizeof(ctrl->viewPath)); + + ctrl->encPath.whichOutputPath = 1; + ctrl->encPath.cbcrStatusBit = 5; + ctrl->viewPath.whichOutputPath = 0; + ctrl->viewPath.cbcrStatusBit = 7; + + ctrl->vfeTestGenStartFlag = FALSE; + + /* default to bank 0. */ + ctrl->vfeLaBankSel = 0; + + /* default to bank 0 for all channels. */ + memset(&ctrl->vfeGammaLutSel, 0, sizeof(ctrl->vfeGammaLutSel)); + + /* Stats control variables. */ + memset(&ctrl->afStatsControl, 0, sizeof(ctrl->afStatsControl)); + memset(&ctrl->awbStatsControl, 0, sizeof(ctrl->awbStatsControl)); + vfe_set_stats_pingpong_address(&ctrl->afStatsControl, + &ctrl->awbStatsControl); +} + +void vfe_reset(void) +{ + vfe_reset_internal_variables(); + + ctrl->vfeImaskLocal.resetAckIrq = TRUE; + ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal); + + /* disable all interrupts. */ + writel(VFE_DISABLE_ALL_IRQS, + ctrl->vfebase + VFE_IRQ_COMPOSITE_MASK); + + /* clear all pending interrupts*/ + writel(VFE_CLEAR_ALL_IRQS, + ctrl->vfebase + VFE_IRQ_CLEAR); + + /* enable reset_ack interrupt. */ + writel(ctrl->vfeImaskPacked, + ctrl->vfebase + VFE_IRQ_MASK); + + writel(VFE_RESET_UPON_RESET_CMD, + ctrl->vfebase + VFE_GLOBAL_RESET_CMD); +} diff --git a/drivers/media/video/msm/msm_vfe8x_proc.h b/drivers/media/video/msm/msm_vfe8x_proc.h new file mode 100644 index 0000000000000..91828569a4d7b --- /dev/null +++ b/drivers/media/video/msm/msm_vfe8x_proc.h @@ -0,0 +1,1549 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#ifndef __MSM_VFE8X_REG_H__ +#define __MSM_VFE8X_REG_H__ + +#include +#include +#include "msm_vfe8x.h" + +/* at start of camif, bit 1:0 = 0x01:enable + * image data capture at frame boundary. */ +#define CAMIF_COMMAND_START 0x00000005 + +/* bit 2= 0x1:clear the CAMIF_STATUS register + * value. */ +#define CAMIF_COMMAND_CLEAR 0x00000004 + +/* at stop of vfe pipeline, for now it is assumed + * that camif will stop at any time. Bit 1:0 = 0x10: + * disable image data capture immediately. */ +#define CAMIF_COMMAND_STOP_IMMEDIATELY 0x00000002 + +/* at stop of vfe pipeline, for now it is assumed + * that camif will stop at any time. Bit 1:0 = 0x00: + * disable image data capture at frame boundary */ +#define CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY 0x00000000 + +/* to halt axi bridge */ +#define AXI_HALT 0x00000001 + +/* clear the halt bit. */ +#define AXI_HALT_CLEAR 0x00000000 + +/* reset the pipeline when stop command is issued. + * (without reset the register.) bit 26-31 = 0, + * domain reset, bit 0-9 = 1 for module reset, except + * register module. */ +#define VFE_RESET_UPON_STOP_CMD 0x000003ef + +/* reset the pipeline when reset command. + * bit 26-31 = 0, domain reset, bit 0-9 = 1 for module reset. */ +#define VFE_RESET_UPON_RESET_CMD 0x000003ff + +/* bit 5 is for axi status idle or busy. + * 1 = halted, 0 = busy */ +#define AXI_STATUS_BUSY_MASK 0x00000020 + +/* bit 0 & bit 1 = 1, both y and cbcr irqs need to be present + * for frame done interrupt */ +#define VFE_COMP_IRQ_BOTH_Y_CBCR 3 + +/* bit 1 = 1, only cbcr irq triggers frame done interrupt */ +#define VFE_COMP_IRQ_CBCR_ONLY 2 + +/* bit 0 = 1, only y irq triggers frame done interrupt */ +#define VFE_COMP_IRQ_Y_ONLY 1 + +/* bit 0 = 1, PM go; bit1 = 1, PM stop */ +#define VFE_PERFORMANCE_MONITOR_GO 0x00000001 +#define VFE_PERFORMANCE_MONITOR_STOP 0x00000002 + +/* bit 0 = 1, test gen go; bit1 = 1, test gen stop */ +#define VFE_TEST_GEN_GO 0x00000001 +#define VFE_TEST_GEN_STOP 0x00000002 + +/* the chroma is assumed to be interpolated between + * the luma samples. JPEG 4:2:2 */ +#define VFE_CHROMA_UPSAMPLE_INTERPOLATED 0 + +/* constants for irq registers */ +#define VFE_DISABLE_ALL_IRQS 0 +/* bit =1 is to clear the corresponding bit in VFE_IRQ_STATUS. */ +#define VFE_CLEAR_ALL_IRQS 0xffffffff +/* imask for while waiting for stop ack, driver has already + * requested stop, waiting for reset irq, + * bit 29,28,27,26 for async timer, bit 9 for reset */ +#define VFE_IMASK_WHILE_STOPPING 0x3c000200 + +/* when normal case, don't want to block error status. + * bit 0,6,20,21,22,30,31 */ +#define VFE_IMASK_ERROR_ONLY 0xC0700041 +#define VFE_REG_UPDATE_TRIGGER 1 +#define VFE_PM_BUF_MAX_CNT_MASK 0xFF +#define VFE_DMI_CFG_DEFAULT 0x00000100 +#define LENS_ROLL_OFF_DELTA_TABLE_OFFSET 32 +#define VFE_AF_PINGPONG_STATUS_BIT 0x100 +#define VFE_AWB_PINGPONG_STATUS_BIT 0x200 + +/* VFE I/O registers */ +enum { + VFE_HW_VERSION = 0x00000000, + VFE_GLOBAL_RESET_CMD = 0x00000004, + VFE_MODULE_RESET = 0x00000008, + VFE_CGC_OVERRIDE = 0x0000000C, + VFE_MODULE_CFG = 0x00000010, + VFE_CFG = 0x00000014, + VFE_IRQ_MASK = 0x00000018, + VFE_IRQ_CLEAR = 0x0000001C, +VFE_IRQ_STATUS = 0x00000020, +VFE_IRQ_COMPOSITE_MASK = 0x00000024, +VFE_BUS_CMD = 0x00000028, +VFE_BUS_CFG = 0x0000002C, +VFE_BUS_ENC_Y_WR_PING_ADDR = 0x00000030, +VFE_BUS_ENC_Y_WR_PONG_ADDR = 0x00000034, +VFE_BUS_ENC_Y_WR_IMAGE_SIZE = 0x00000038, +VFE_BUS_ENC_Y_WR_BUFFER_CFG = 0x0000003C, +VFE_BUS_ENC_CBCR_WR_PING_ADDR = 0x00000040, +VFE_BUS_ENC_CBCR_WR_PONG_ADDR = 0x00000044, +VFE_BUS_ENC_CBCR_WR_IMAGE_SIZE = 0x00000048, +VFE_BUS_ENC_CBCR_WR_BUFFER_CFG = 0x0000004C, +VFE_BUS_VIEW_Y_WR_PING_ADDR = 0x00000050, +VFE_BUS_VIEW_Y_WR_PONG_ADDR = 0x00000054, +VFE_BUS_VIEW_Y_WR_IMAGE_SIZE = 0x00000058, +VFE_BUS_VIEW_Y_WR_BUFFER_CFG = 0x0000005C, +VFE_BUS_VIEW_CBCR_WR_PING_ADDR = 0x00000060, +VFE_BUS_VIEW_CBCR_WR_PONG_ADDR = 0x00000064, +VFE_BUS_VIEW_CBCR_WR_IMAGE_SIZE = 0x00000068, +VFE_BUS_VIEW_CBCR_WR_BUFFER_CFG = 0x0000006C, +VFE_BUS_STATS_AF_WR_PING_ADDR = 0x00000070, +VFE_BUS_STATS_AF_WR_PONG_ADDR = 0x00000074, +VFE_BUS_STATS_AWB_WR_PING_ADDR = 0x00000078, +VFE_BUS_STATS_AWB_WR_PONG_ADDR = 0x0000007C, +VFE_BUS_STATS_HIST_WR_PING_ADDR = 0x00000080, +VFE_BUS_STATS_HIST_WR_PONG_ADDR = 0x00000084, +VFE_BUS_STATS_WR_PRIORITY = 0x00000088, +VFE_BUS_STRIPE_RD_ADDR_0 = 0x0000008C, +VFE_BUS_STRIPE_RD_ADDR_1 = 0x00000090, +VFE_BUS_STRIPE_RD_ADDR_2 = 0x00000094, +VFE_BUS_STRIPE_RD_ADDR_3 = 0x00000098, +VFE_BUS_STRIPE_RD_VSIZE = 0x0000009C, +VFE_BUS_STRIPE_RD_HSIZE = 0x000000A0, +VFE_BUS_STRIPE_RD_BUFFER_CFG = 0x000000A4, +VFE_BUS_STRIPE_RD_UNPACK_CFG = 0x000000A8, +VFE_BUS_STRIPE_RD_UNPACK = 0x000000AC, +VFE_BUS_STRIPE_RD_PAD_SIZE = 0x000000B0, +VFE_BUS_STRIPE_RD_PAD_L_UNPACK = 0x000000B4, +VFE_BUS_STRIPE_RD_PAD_R_UNPACK = 0x000000B8, +VFE_BUS_STRIPE_RD_PAD_TB_UNPACK = 0x000000BC, +VFE_BUS_PINGPONG_IRQ_EN = 0x000000C0, +VFE_BUS_PINGPONG_STATUS = 0x000000C4, +VFE_BUS_PM_CMD = 0x000000C8, +VFE_BUS_PM_CFG = 0x000000CC, +VFE_BUS_ENC_Y_WR_PM_STATS_0 = 0x000000D0, +VFE_BUS_ENC_Y_WR_PM_STATS_1 = 0x000000D4, +VFE_BUS_ENC_CBCR_WR_PM_STATS_0 = 0x000000D8, +VFE_BUS_ENC_CBCR_WR_PM_STATS_1 = 0x000000DC, +VFE_BUS_VIEW_Y_WR_PM_STATS_0 = 0x000000E0, +VFE_BUS_VIEW_Y_WR_PM_STATS_1 = 0x000000E4, +VFE_BUS_VIEW_CBCR_WR_PM_STATS_0 = 0x000000E8, +VFE_BUS_VIEW_CBCR_WR_PM_STATS_1 = 0x000000EC, +VFE_BUS_MISR_CFG = 0x000000F4, +VFE_BUS_MISR_MAST_CFG_0 = 0x000000F8, +VFE_BUS_MISR_MAST_CFG_1 = 0x000000FC, +VFE_BUS_MISR_RD_VAL = 0x00000100, +VFE_AXI_CMD = 0x00000104, +VFE_AXI_CFG = 0x00000108, +VFE_AXI_STATUS = 0x0000010C, +CAMIF_COMMAND = 0x00000110, +CAMIF_CONFIG = 0x00000114, +CAMIF_EFS_CONFIG = 0x00000118, +CAMIF_FRAME_CONFIG = 0x0000011C, +CAMIF_WINDOW_WIDTH_CONFIG = 0x00000120, +CAMIF_WINDOW_HEIGHT_CONFIG = 0x00000124, +CAMIF_SUBSAMPLE1_CONFIG = 0x00000128, +CAMIF_SUBSAMPLE2_CONFIG = 0x0000012C, +CAMIF_EPOCH_IRQ = 0x00000130, +CAMIF_STATUS = 0x00000134, +CAMIF_MISR = 0x00000138, +VFE_SYNC_TIMER_CMD = 0x0000013C, +VFE_SYNC_TIMER0_LINE_START = 0x00000140, +VFE_SYNC_TIMER0_PIXEL_START = 0x00000144, +VFE_SYNC_TIMER0_PIXEL_DURATION = 0x00000148, +VFE_SYNC_TIMER1_LINE_START = 0x0000014C, +VFE_SYNC_TIMER1_PIXEL_START = 0x00000150, +VFE_SYNC_TIMER1_PIXEL_DURATION = 0x00000154, +VFE_SYNC_TIMER2_LINE_START = 0x00000158, +VFE_SYNC_TIMER2_PIXEL_START = 0x0000015C, +VFE_SYNC_TIMER2_PIXEL_DURATION = 0x00000160, +VFE_SYNC_TIMER_POLARITY = 0x00000164, +VFE_ASYNC_TIMER_CMD = 0x00000168, +VFE_ASYNC_TIMER0_CFG_0 = 0x0000016C, +VFE_ASYNC_TIMER0_CFG_1 = 0x00000170, +VFE_ASYNC_TIMER1_CFG_0 = 0x00000174, +VFE_ASYNC_TIMER1_CFG_1 = 0x00000178, +VFE_ASYNC_TIMER2_CFG_0 = 0x0000017C, +VFE_ASYNC_TIMER2_CFG_1 = 0x00000180, +VFE_ASYNC_TIMER3_CFG_0 = 0x00000184, +VFE_ASYNC_TIMER3_CFG_1 = 0x00000188, +VFE_TIMER_SEL = 0x0000018C, +VFE_REG_UPDATE_CMD = 0x00000190, +VFE_BLACK_EVEN_EVEN_VALUE = 0x00000194, +VFE_BLACK_EVEN_ODD_VALUE = 0x00000198, +VFE_BLACK_ODD_EVEN_VALUE = 0x0000019C, +VFE_BLACK_ODD_ODD_VALUE = 0x000001A0, +VFE_ROLLOFF_CFG_0 = 0x000001A4, +VFE_ROLLOFF_CFG_1 = 0x000001A8, +VFE_ROLLOFF_CFG_2 = 0x000001AC, +VFE_DEMUX_CFG = 0x000001B0, +VFE_DEMUX_GAIN_0 = 0x000001B4, +VFE_DEMUX_GAIN_1 = 0x000001B8, +VFE_DEMUX_EVEN_CFG = 0x000001BC, +VFE_DEMUX_ODD_CFG = 0x000001C0, +VFE_DEMOSAIC_CFG = 0x000001C4, +VFE_DEMOSAIC_ABF_CFG_0 = 0x000001C8, +VFE_DEMOSAIC_ABF_CFG_1 = 0x000001CC, +VFE_DEMOSAIC_BPC_CFG_0 = 0x000001D0, +VFE_DEMOSAIC_BPC_CFG_1 = 0x000001D4, +VFE_DEMOSAIC_STATUS = 0x000001D8, +VFE_CHROMA_UPSAMPLE_CFG = 0x000001DC, +VFE_CROP_WIDTH_CFG = 0x000001E0, +VFE_CROP_HEIGHT_CFG = 0x000001E4, +VFE_COLOR_CORRECT_COEFF_0 = 0x000001E8, +VFE_COLOR_CORRECT_COEFF_1 = 0x000001EC, +VFE_COLOR_CORRECT_COEFF_2 = 0x000001F0, +VFE_COLOR_CORRECT_COEFF_3 = 0x000001F4, +VFE_COLOR_CORRECT_COEFF_4 = 0x000001F8, +VFE_COLOR_CORRECT_COEFF_5 = 0x000001FC, +VFE_COLOR_CORRECT_COEFF_6 = 0x00000200, +VFE_COLOR_CORRECT_COEFF_7 = 0x00000204, +VFE_COLOR_CORRECT_COEFF_8 = 0x00000208, +VFE_COLOR_CORRECT_OFFSET_0 = 0x0000020C, +VFE_COLOR_CORRECT_OFFSET_1 = 0x00000210, +VFE_COLOR_CORRECT_OFFSET_2 = 0x00000214, +VFE_COLOR_CORRECT_COEFF_Q = 0x00000218, +VFE_LA_CFG = 0x0000021C, +VFE_LUT_BANK_SEL = 0x00000220, +VFE_CHROMA_ENHAN_A = 0x00000224, +VFE_CHROMA_ENHAN_B = 0x00000228, +VFE_CHROMA_ENHAN_C = 0x0000022C, +VFE_CHROMA_ENHAN_D = 0x00000230, +VFE_CHROMA_ENHAN_K = 0x00000234, +VFE_COLOR_CONVERT_COEFF_0 = 0x00000238, +VFE_COLOR_CONVERT_COEFF_1 = 0x0000023C, +VFE_COLOR_CONVERT_COEFF_2 = 0x00000240, +VFE_COLOR_CONVERT_OFFSET = 0x00000244, +VFE_ASF_CFG = 0x00000248, +VFE_ASF_SHARP_CFG_0 = 0x0000024C, +VFE_ASF_SHARP_CFG_1 = 0x00000250, +VFE_ASF_SHARP_COEFF_0 = 0x00000254, +VFE_ASF_SHARP_COEFF_1 = 0x00000258, +VFE_ASF_SHARP_COEFF_2 = 0x0000025C, +VFE_ASF_SHARP_COEFF_3 = 0x00000260, +VFE_ASF_MAX_EDGE = 0x00000264, +VFE_ASF_CROP_WIDTH_CFG = 0x00000268, +VFE_ASF_CROP_HEIGHT_CFG = 0x0000026C, +VFE_SCALE_CFG = 0x00000270, +VFE_SCALE_H_IMAGE_SIZE_CFG = 0x00000274, +VFE_SCALE_H_PHASE_CFG = 0x00000278, +VFE_SCALE_H_STRIPE_CFG = 0x0000027C, +VFE_SCALE_V_IMAGE_SIZE_CFG = 0x00000280, +VFE_SCALE_V_PHASE_CFG = 0x00000284, +VFE_SCALE_V_STRIPE_CFG = 0x00000288, +VFE_SCALE_Y_CFG = 0x0000028C, +VFE_SCALE_Y_H_IMAGE_SIZE_CFG = 0x00000290, +VFE_SCALE_Y_H_PHASE_CFG = 0x00000294, +VFE_SCALE_Y_V_IMAGE_SIZE_CFG = 0x00000298, +VFE_SCALE_Y_V_PHASE_CFG = 0x0000029C, +VFE_SCALE_CBCR_CFG = 0x000002A0, +VFE_SCALE_CBCR_H_IMAGE_SIZE_CFG = 0x000002A4, +VFE_SCALE_CBCR_H_PHASE_CFG = 0x000002A8, +VFE_SCALE_CBCR_V_IMAGE_SIZE_CFG = 0x000002AC, +VFE_SCALE_CBCR_V_PHASE_CFG = 0x000002B0, +VFE_WB_CFG = 0x000002B4, +VFE_CHROMA_SUPPRESS_CFG_0 = 0x000002B8, +VFE_CHROMA_SUPPRESS_CFG_1 = 0x000002BC, +VFE_CHROMA_SUBSAMPLE_CFG = 0x000002C0, +VFE_CHROMA_SUB_CROP_WIDTH_CFG = 0x000002C4, +VFE_CHROMA_SUB_CROP_HEIGHT_CFG = 0x000002C8, +VFE_FRAMEDROP_ENC_Y_CFG = 0x000002CC, +VFE_FRAMEDROP_ENC_CBCR_CFG = 0x000002D0, +VFE_FRAMEDROP_ENC_Y_PATTERN = 0x000002D4, +VFE_FRAMEDROP_ENC_CBCR_PATTERN = 0x000002D8, +VFE_FRAMEDROP_VIEW_Y_CFG = 0x000002DC, +VFE_FRAMEDROP_VIEW_CBCR_CFG = 0x000002E0, +VFE_FRAMEDROP_VIEW_Y_PATTERN = 0x000002E4, +VFE_FRAMEDROP_VIEW_CBCR_PATTERN = 0x000002E8, +VFE_CLAMP_MAX_CFG = 0x000002EC, +VFE_CLAMP_MIN_CFG = 0x000002F0, +VFE_STATS_CMD = 0x000002F4, +VFE_STATS_AF_CFG = 0x000002F8, +VFE_STATS_AF_DIM = 0x000002FC, +VFE_STATS_AF_GRID_0 = 0x00000300, +VFE_STATS_AF_GRID_1 = 0x00000304, +VFE_STATS_AF_GRID_2 = 0x00000308, +VFE_STATS_AF_GRID_3 = 0x0000030C, +VFE_STATS_AF_HEADER = 0x00000310, +VFE_STATS_AF_COEF0 = 0x00000314, +VFE_STATS_AF_COEF1 = 0x00000318, +VFE_STATS_AWBAE_CFG = 0x0000031C, +VFE_STATS_AXW_HEADER = 0x00000320, +VFE_STATS_AWB_MCFG = 0x00000324, +VFE_STATS_AWB_CCFG1 = 0x00000328, +VFE_STATS_AWB_CCFG2 = 0x0000032C, +VFE_STATS_HIST_HEADER = 0x00000330, +VFE_STATS_HIST_INNER_OFFSET = 0x00000334, +VFE_STATS_HIST_INNER_DIM = 0x00000338, +VFE_STATS_FRAME_SIZE = 0x0000033C, +VFE_DMI_CFG = 0x00000340, +VFE_DMI_ADDR = 0x00000344, +VFE_DMI_DATA_HI = 0x00000348, +VFE_DMI_DATA_LO = 0x0000034C, +VFE_DMI_RAM_AUTO_LOAD_CMD = 0x00000350, +VFE_DMI_RAM_AUTO_LOAD_STATUS = 0x00000354, +VFE_DMI_RAM_AUTO_LOAD_CFG = 0x00000358, +VFE_DMI_RAM_AUTO_LOAD_SEED = 0x0000035C, +VFE_TESTBUS_SEL = 0x00000360, +VFE_TESTGEN_CFG = 0x00000364, +VFE_SW_TESTGEN_CMD = 0x00000368, +VFE_HW_TESTGEN_CMD = 0x0000036C, +VFE_HW_TESTGEN_CFG = 0x00000370, +VFE_HW_TESTGEN_IMAGE_CFG = 0x00000374, +VFE_HW_TESTGEN_SOF_OFFSET_CFG = 0x00000378, +VFE_HW_TESTGEN_EOF_NOFFSET_CFG = 0x0000037C, +VFE_HW_TESTGEN_SOL_OFFSET_CFG = 0x00000380, +VFE_HW_TESTGEN_EOL_NOFFSET_CFG = 0x00000384, +VFE_HW_TESTGEN_HBI_CFG = 0x00000388, +VFE_HW_TESTGEN_VBL_CFG = 0x0000038C, +VFE_HW_TESTGEN_SOF_DUMMY_LINE_CFG2 = 0x00000390, +VFE_HW_TESTGEN_EOF_DUMMY_LINE_CFG2 = 0x00000394, +VFE_HW_TESTGEN_COLOR_BARS_CFG = 0x00000398, +VFE_HW_TESTGEN_RANDOM_CFG = 0x0000039C, +VFE_SPARE = 0x000003A0, +}; + +#define ping 0x0 +#define pong 0x1 + +struct vfe_bus_cfg_data { + boolean stripeRdPathEn; + boolean encYWrPathEn; + boolean encCbcrWrPathEn; + boolean viewYWrPathEn; + boolean viewCbcrWrPathEn; + enum VFE_RAW_PIXEL_DATA_SIZE rawPixelDataSize; + enum VFE_RAW_WR_PATH_SEL rawWritePathSelect; +}; + +struct vfe_camif_cfg_data { + boolean camif2OutputEnable; + boolean camif2BusEnable; + struct vfe_cmds_camif_cfg camifCfgFromCmd; +}; + +struct vfe_irq_composite_mask_config { + uint8_t encIrqComMask; + uint8_t viewIrqComMask; + uint8_t ceDoneSel; +}; + +/* define a structure for each output path.*/ +struct vfe_output_path { + uint32_t addressBuffer[8]; + uint16_t fragIndex; + boolean hwCurrentFlag; + uint8_t *hwRegPingAddress; + uint8_t *hwRegPongAddress; +}; + +struct vfe_output_path_combo { + boolean whichOutputPath; + boolean pathEnabled; + boolean multiFrag; + uint8_t fragCount; + boolean ackPending; + uint8_t currentFrame; + uint32_t nextFrameAddrBuf[8]; + struct vfe_output_path yPath; + struct vfe_output_path cbcrPath; + uint8_t snapshotPendingCount; + boolean pmEnabled; + uint8_t cbcrStatusBit; +}; + +struct vfe_stats_control { + boolean ackPending; + uint32_t addressBuffer[2]; + uint32_t nextFrameAddrBuf; + boolean pingPongStatus; + uint8_t *hwRegPingAddress; + uint8_t *hwRegPongAddress; + uint32_t droppedStatsFrameCount; + uint32_t bufToRender; +}; + +struct vfe_gamma_lut_sel { + boolean ch0BankSelect; + boolean ch1BankSelect; + boolean ch2BankSelect; +}; + +struct vfe_interrupt_mask { + boolean camifErrorIrq; + boolean camifSofIrq; + boolean camifEolIrq; + boolean camifEofIrq; + boolean camifEpoch1Irq; + boolean camifEpoch2Irq; + boolean camifOverflowIrq; + boolean ceIrq; + boolean regUpdateIrq; + boolean resetAckIrq; + boolean encYPingpongIrq; + boolean encCbcrPingpongIrq; + boolean viewYPingpongIrq; + boolean viewCbcrPingpongIrq; + boolean rdPingpongIrq; + boolean afPingpongIrq; + boolean awbPingpongIrq; + boolean histPingpongIrq; + boolean encIrq; + boolean viewIrq; + boolean busOverflowIrq; + boolean afOverflowIrq; + boolean awbOverflowIrq; + boolean syncTimer0Irq; + boolean syncTimer1Irq; + boolean syncTimer2Irq; + boolean asyncTimer0Irq; + boolean asyncTimer1Irq; + boolean asyncTimer2Irq; + boolean asyncTimer3Irq; + boolean axiErrorIrq; + boolean violationIrq; +}; + +enum vfe_interrupt_name { + CAMIF_ERROR_IRQ, + CAMIF_SOF_IRQ, + CAMIF_EOL_IRQ, + CAMIF_EOF_IRQ, + CAMIF_EPOCH1_IRQ, + CAMIF_EPOCH2_IRQ, + CAMIF_OVERFLOW_IRQ, + CE_IRQ, + REG_UPDATE_IRQ, + RESET_ACK_IRQ, + ENC_Y_PINGPONG_IRQ, + ENC_CBCR_PINGPONG_IRQ, + VIEW_Y_PINGPONG_IRQ, + VIEW_CBCR_PINGPONG_IRQ, + RD_PINGPONG_IRQ, + AF_PINGPONG_IRQ, + AWB_PINGPONG_IRQ, + HIST_PINGPONG_IRQ, + ENC_IRQ, + VIEW_IRQ, + BUS_OVERFLOW_IRQ, + AF_OVERFLOW_IRQ, + AWB_OVERFLOW_IRQ, + SYNC_TIMER0_IRQ, + SYNC_TIMER1_IRQ, + SYNC_TIMER2_IRQ, + ASYNC_TIMER0_IRQ, + ASYNC_TIMER1_IRQ, + ASYNC_TIMER2_IRQ, + ASYNC_TIMER3_IRQ, + AXI_ERROR_IRQ, + VIOLATION_IRQ +}; + +enum VFE_DMI_RAM_SEL { + NO_MEM_SELECTED = 0, + ROLLOFF_RAM = 0x1, + RGBLUT_RAM_CH0_BANK0 = 0x2, + RGBLUT_RAM_CH0_BANK1 = 0x3, + RGBLUT_RAM_CH1_BANK0 = 0x4, + RGBLUT_RAM_CH1_BANK1 = 0x5, + RGBLUT_RAM_CH2_BANK0 = 0x6, + RGBLUT_RAM_CH2_BANK1 = 0x7, + STATS_HIST_CB_EVEN_RAM = 0x8, + STATS_HIST_CB_ODD_RAM = 0x9, + STATS_HIST_CR_EVEN_RAM = 0xa, + STATS_HIST_CR_ODD_RAM = 0xb, + RGBLUT_CHX_BANK0 = 0xc, + RGBLUT_CHX_BANK1 = 0xd, + LUMA_ADAPT_LUT_RAM_BANK0 = 0xe, + LUMA_ADAPT_LUT_RAM_BANK1 = 0xf +}; + +struct vfe_module_enable { + boolean blackLevelCorrectionEnable; + boolean lensRollOffEnable; + boolean demuxEnable; + boolean chromaUpsampleEnable; + boolean demosaicEnable; + boolean statsEnable; + boolean cropEnable; + boolean mainScalerEnable; + boolean whiteBalanceEnable; + boolean colorCorrectionEnable; + boolean yHistEnable; + boolean skinToneEnable; + boolean lumaAdaptationEnable; + boolean rgbLUTEnable; + boolean chromaEnhanEnable; + boolean asfEnable; + boolean chromaSuppressionEnable; + boolean chromaSubsampleEnable; + boolean scaler2YEnable; + boolean scaler2CbcrEnable; +}; + +struct vfe_bus_cmd_data { + boolean stripeReload; + boolean busPingpongReload; + boolean statsPingpongReload; +}; + +struct vfe_stats_cmd_data { + boolean autoFocusEnable; + boolean axwEnable; + boolean histEnable; + boolean clearHistEnable; + boolean histAutoClearEnable; + boolean colorConversionEnable; +}; + +struct vfe_hw_ver { + uint32_t minorVersion:8; + uint32_t majorVersion:8; + uint32_t coreVersion:4; + uint32_t /* reserved */ : 12; +} __attribute__((packed, aligned(4))); + +struct vfe_cfg { + uint32_t pixelPattern:3; + uint32_t /* reserved */ : 13; + uint32_t inputSource:2; + uint32_t /* reserved */ : 14; +} __attribute__((packed, aligned(4))); + +struct vfe_buscmd { + uint32_t stripeReload:1; + uint32_t /* reserved */ : 3; + uint32_t busPingpongReload:1; + uint32_t statsPingpongReload:1; + uint32_t /* reserved */ : 26; +} __attribute__((packed, aligned(4))); + +struct VFE_Irq_Composite_MaskType { + uint32_t encIrqComMaskBits:2; + uint32_t viewIrqComMaskBits:2; + uint32_t ceDoneSelBits:5; + uint32_t /* reserved */ : 23; +} __attribute__((packed, aligned(4))); + +struct vfe_mod_enable { + uint32_t blackLevelCorrectionEnable:1; + uint32_t lensRollOffEnable:1; + uint32_t demuxEnable:1; + uint32_t chromaUpsampleEnable:1; + uint32_t demosaicEnable:1; + uint32_t statsEnable:1; + uint32_t cropEnable:1; + uint32_t mainScalerEnable:1; + uint32_t whiteBalanceEnable:1; + uint32_t colorCorrectionEnable:1; + uint32_t yHistEnable:1; + uint32_t skinToneEnable:1; + uint32_t lumaAdaptationEnable:1; + uint32_t rgbLUTEnable:1; + uint32_t chromaEnhanEnable:1; + uint32_t asfEnable:1; + uint32_t chromaSuppressionEnable:1; + uint32_t chromaSubsampleEnable:1; + uint32_t scaler2YEnable:1; + uint32_t scaler2CbcrEnable:1; + uint32_t /* reserved */ : 14; +} __attribute__((packed, aligned(4))); + +struct vfe_irqenable { + uint32_t camifErrorIrq:1; + uint32_t camifSofIrq:1; + uint32_t camifEolIrq:1; + uint32_t camifEofIrq:1; + uint32_t camifEpoch1Irq:1; + uint32_t camifEpoch2Irq:1; + uint32_t camifOverflowIrq:1; + uint32_t ceIrq:1; + uint32_t regUpdateIrq:1; + uint32_t resetAckIrq:1; + uint32_t encYPingpongIrq:1; + uint32_t encCbcrPingpongIrq:1; + uint32_t viewYPingpongIrq:1; + uint32_t viewCbcrPingpongIrq:1; + uint32_t rdPingpongIrq:1; + uint32_t afPingpongIrq:1; + uint32_t awbPingpongIrq:1; + uint32_t histPingpongIrq:1; + uint32_t encIrq:1; + uint32_t viewIrq:1; + uint32_t busOverflowIrq:1; + uint32_t afOverflowIrq:1; + uint32_t awbOverflowIrq:1; + uint32_t syncTimer0Irq:1; + uint32_t syncTimer1Irq:1; + uint32_t syncTimer2Irq:1; + uint32_t asyncTimer0Irq:1; + uint32_t asyncTimer1Irq:1; + uint32_t asyncTimer2Irq:1; + uint32_t asyncTimer3Irq:1; + uint32_t axiErrorIrq:1; + uint32_t violationIrq:1; +} __attribute__((packed, aligned(4))); + +struct vfe_upsample_cfg { + uint32_t chromaCositingForYCbCrInputs:1; + uint32_t /* reserved */ : 31; +} __attribute__((packed, aligned(4))); + +struct VFE_CAMIFConfigType { + /* CAMIF Config */ + uint32_t /* reserved */ : 1; + uint32_t VSyncEdge:1; + uint32_t HSyncEdge:1; + uint32_t syncMode:2; + uint32_t vfeSubsampleEnable:1; + uint32_t /* reserved */ : 1; + uint32_t busSubsampleEnable:1; + uint32_t camif2vfeEnable:1; + uint32_t /* reserved */ : 1; + uint32_t camif2busEnable:1; + uint32_t irqSubsampleEnable:1; + uint32_t binningEnable:1; + uint32_t /* reserved */ : 18; + uint32_t misrEnable:1; +} __attribute__((packed, aligned(4))); + +struct vfe_camifcfg { + /* EFS_Config */ + uint32_t efsEndOfLine:8; + uint32_t efsStartOfLine:8; + uint32_t efsEndOfFrame:8; + uint32_t efsStartOfFrame:8; + /* Frame Config */ + uint32_t frameConfigPixelsPerLine:14; + uint32_t /* reserved */ : 2; + uint32_t frameConfigLinesPerFrame:14; + uint32_t /* reserved */ : 2; + /* Window Width Config */ + uint32_t windowWidthCfgLastPixel:14; + uint32_t /* reserved */ : 2; + uint32_t windowWidthCfgFirstPixel:14; + uint32_t /* reserved */ : 2; + /* Window Height Config */ + uint32_t windowHeightCfglastLine:14; + uint32_t /* reserved */ : 2; + uint32_t windowHeightCfgfirstLine:14; + uint32_t /* reserved */ : 2; + /* Subsample 1 Config */ + uint32_t subsample1CfgPixelSkip:16; + uint32_t subsample1CfgLineSkip:16; + /* Subsample 2 Config */ + uint32_t subsample2CfgFrameSkip:4; + uint32_t subsample2CfgFrameSkipMode:1; + uint32_t subsample2CfgPixelSkipWrap:1; + uint32_t /* reserved */ : 26; + /* Epoch Interrupt */ + uint32_t epoch1Line:14; + uint32_t /* reserved */ : 2; + uint32_t epoch2Line:14; + uint32_t /* reserved */ : 2; +} __attribute__((packed, aligned(4))); + +struct vfe_camifframe_update { + uint32_t pixelsPerLine:14; + uint32_t /* reserved */ : 2; + uint32_t linesPerFrame:14; + uint32_t /* reserved */ : 2; +} __attribute__((packed, aligned(4))); + +struct vfe_axi_bus_cfg { + uint32_t stripeRdPathEn:1; + uint32_t /* reserved */ : 3; + uint32_t encYWrPathEn:1; + uint32_t encCbcrWrPathEn:1; + uint32_t viewYWrPathEn:1; + uint32_t viewCbcrWrPathEn:1; + uint32_t rawPixelDataSize:2; + uint32_t rawWritePathSelect:2; + uint32_t /* reserved */ : 20; +} __attribute__((packed, aligned(4))); + +struct vfe_axi_out_cfg { + uint32_t out2YPingAddr:32; + uint32_t out2YPongAddr:32; + uint32_t out2YImageHeight:12; + uint32_t /* reserved */ : 4; + uint32_t out2YImageWidthin64bit:10; + uint32_t /* reserved */ : 6; + uint32_t out2YBurstLength:2; + uint32_t /* reserved */ : 2; + uint32_t out2YNumRows:12; + uint32_t out2YRowIncrementIn64bit:12; + uint32_t /* reserved */ : 4; + uint32_t out2CbcrPingAddr:32; + uint32_t out2CbcrPongAddr:32; + uint32_t out2CbcrImageHeight:12; + uint32_t /* reserved */ : 4; + uint32_t out2CbcrImageWidthIn64bit:10; + uint32_t /* reserved */ : 6; + uint32_t out2CbcrBurstLength:2; + uint32_t /* reserved */ : 2; + uint32_t out2CbcrNumRows:12; + uint32_t out2CbcrRowIncrementIn64bit:12; + uint32_t /* reserved */ : 4; + uint32_t out1YPingAddr:32; + uint32_t out1YPongAddr:32; + uint32_t out1YImageHeight:12; + uint32_t /* reserved */ : 4; + uint32_t out1YImageWidthin64bit:10; + uint32_t /* reserved */ : 6; + uint32_t out1YBurstLength:2; + uint32_t /* reserved */ : 2; + uint32_t out1YNumRows:12; + uint32_t out1YRowIncrementIn64bit:12; + uint32_t /* reserved */ : 4; + uint32_t out1CbcrPingAddr:32; + uint32_t out1CbcrPongAddr:32; + uint32_t out1CbcrImageHeight:12; + uint32_t /* reserved */ : 4; + uint32_t out1CbcrImageWidthIn64bit:10; + uint32_t /* reserved */ : 6; + uint32_t out1CbcrBurstLength:2; + uint32_t /* reserved */ : 2; + uint32_t out1CbcrNumRows:12; + uint32_t out1CbcrRowIncrementIn64bit:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct vfe_output_clamp_cfg { + /* Output Clamp Maximums */ + uint32_t yChanMax:8; + uint32_t cbChanMax:8; + uint32_t crChanMax:8; + uint32_t /* reserved */ : 8; + /* Output Clamp Minimums */ + uint32_t yChanMin:8; + uint32_t cbChanMin:8; + uint32_t crChanMin:8; + uint32_t /* reserved */ : 8; +} __attribute__((packed, aligned(4))); + +struct vfe_fov_crop_cfg { + uint32_t lastPixel:12; + uint32_t /* reserved */ : 4; + uint32_t firstPixel:12; + uint32_t /* reserved */ : 4; + + /* FOV Corp, Part 2 */ + uint32_t lastLine:12; + uint32_t /* reserved */ : 4; + uint32_t firstLine:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct VFE_FRAME_SKIP_UpdateCmdType { + uint32_t yPattern:32; + uint32_t cbcrPattern:32; +} __attribute__((packed, aligned(4))); + +struct vfe_frame_skip_cfg { + /* Frame Drop Enc (output2) */ + uint32_t output2YPeriod:5; + uint32_t /* reserved */ : 27; + uint32_t output2CbCrPeriod:5; + uint32_t /* reserved */ : 27; + uint32_t output2YPattern:32; + uint32_t output2CbCrPattern:32; + /* Frame Drop View (output1) */ + uint32_t output1YPeriod:5; + uint32_t /* reserved */ : 27; + uint32_t output1CbCrPeriod:5; + uint32_t /* reserved */ : 27; + uint32_t output1YPattern:32; + uint32_t output1CbCrPattern:32; +} __attribute__((packed, aligned(4))); + +struct vfe_main_scaler_cfg { + /* Scaler Enable Config */ + uint32_t hEnable:1; + uint32_t vEnable:1; + uint32_t /* reserved */ : 30; + /* Scale H Image Size Config */ + uint32_t inWidth:12; + uint32_t /* reserved */ : 4; + uint32_t outWidth:12; + uint32_t /* reserved */ : 4; + /* Scale H Phase Config */ + uint32_t horizPhaseMult:18; + uint32_t /* reserved */ : 2; + uint32_t horizInterResolution:2; + uint32_t /* reserved */ : 10; + /* Scale H Stripe Config */ + uint32_t horizMNInit:12; + uint32_t /* reserved */ : 4; + uint32_t horizPhaseInit:15; + uint32_t /* reserved */ : 1; + /* Scale V Image Size Config */ + uint32_t inHeight:12; + uint32_t /* reserved */ : 4; + uint32_t outHeight:12; + uint32_t /* reserved */ : 4; + /* Scale V Phase Config */ + uint32_t vertPhaseMult:18; + uint32_t /* reserved */ : 2; + uint32_t vertInterResolution:2; + uint32_t /* reserved */ : 10; + /* Scale V Stripe Config */ + uint32_t vertMNInit:12; + uint32_t /* reserved */ : 4; + uint32_t vertPhaseInit:15; + uint32_t /* reserved */ : 1; +} __attribute__((packed, aligned(4))); + +struct vfe_scaler2_cfg { + /* Scaler Enable Config */ + uint32_t hEnable:1; + uint32_t vEnable:1; + uint32_t /* reserved */ : 30; + /* Scaler H Image Size Config */ + uint32_t inWidth:12; + uint32_t /* reserved */ : 4; + uint32_t outWidth:12; + uint32_t /* reserved */ : 4; + /* Scaler H Phase Config */ + uint32_t horizPhaseMult:18; + uint32_t /* reserved */ : 2; + uint32_t horizInterResolution:2; + uint32_t /* reserved */ : 10; + /* Scaler V Image Size Config */ + uint32_t inHeight:12; + uint32_t /* reserved */ : 4; + uint32_t outHeight:12; + uint32_t /* reserved */ : 4; + /* Scaler V Phase Config */ + uint32_t vertPhaseMult:18; + uint32_t /* reserved */ : 2; + uint32_t vertInterResolution:2; + uint32_t /* reserved */ : 10; +} __attribute__((packed, aligned(4))); + +struct vfe_rolloff_cfg { + /* Rolloff 0 Config */ + uint32_t gridWidth:9; + uint32_t gridHeight:9; + uint32_t yDelta:9; + uint32_t /* reserved */ : 5; + /* Rolloff 1 Config*/ + uint32_t gridX:4; + uint32_t gridY:4; + uint32_t pixelX:9; + uint32_t /* reserved */ : 3; + uint32_t pixelY:9; + uint32_t /* reserved */ : 3; + /* Rolloff 2 Config */ + uint32_t yDeltaAccum:12; + uint32_t /* reserved */ : 20; +} __attribute__((packed, aligned(4))); + +struct vfe_asf_update { + /* ASF Config Command */ + uint32_t smoothEnable:1; + uint32_t sharpMode:2; + uint32_t /* reserved */ : 1; + uint32_t smoothCoeff1:4; + uint32_t smoothCoeff0:8; + uint32_t pipeFlushCount:12; + uint32_t pipeFlushOvd:1; + uint32_t flushHaltOvd:1; + uint32_t cropEnable:1; + uint32_t /* reserved */ : 1; + /* Sharpening Config 0 */ + uint32_t sharpThresholdE1:7; + uint32_t /* reserved */ : 1; + uint32_t sharpDegreeK1:5; + uint32_t /* reserved */ : 3; + uint32_t sharpDegreeK2:5; + uint32_t /* reserved */ : 3; + uint32_t normalizeFactor:7; + uint32_t /* reserved */ : 1; + /* Sharpening Config 1 */ + uint32_t sharpThresholdE2:8; + uint32_t sharpThresholdE3:8; + uint32_t sharpThresholdE4:8; + uint32_t sharpThresholdE5:8; + /* Sharpening Coefficients 0 */ + uint32_t F1Coeff0:6; + uint32_t F1Coeff1:6; + uint32_t F1Coeff2:6; + uint32_t F1Coeff3:6; + uint32_t F1Coeff4:6; + uint32_t /* reserved */ : 2; + /* Sharpening Coefficients 1 */ + uint32_t F1Coeff5:6; + uint32_t F1Coeff6:6; + uint32_t F1Coeff7:6; + uint32_t F1Coeff8:7; + uint32_t /* reserved */ : 7; + /* Sharpening Coefficients 2 */ + uint32_t F2Coeff0:6; + uint32_t F2Coeff1:6; + uint32_t F2Coeff2:6; + uint32_t F2Coeff3:6; + uint32_t F2Coeff4:6; + uint32_t /* reserved */ : 2; + /* Sharpening Coefficients 3 */ + uint32_t F2Coeff5:6; + uint32_t F2Coeff6:6; + uint32_t F2Coeff7:6; + uint32_t F2Coeff8:7; + uint32_t /* reserved */ : 7; +} __attribute__((packed, aligned(4))); + +struct vfe_asfcrop_cfg { + /* ASF Crop Width Config */ + uint32_t lastPixel:12; + uint32_t /* reserved */ : 4; + uint32_t firstPixel:12; + uint32_t /* reserved */ : 4; + /* ASP Crop Height Config */ + uint32_t lastLine:12; + uint32_t /* reserved */ : 4; + uint32_t firstLine:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct vfe_chroma_suppress_cfg { + /* Chroma Suppress 0 Config */ + uint32_t m1:8; + uint32_t m3:8; + uint32_t n1:3; + uint32_t /* reserved */ : 1; + uint32_t n3:3; + uint32_t /* reserved */ : 9; + /* Chroma Suppress 1 Config */ + uint32_t mm1:8; + uint32_t nn1:3; + uint32_t /* reserved */ : 21; +} __attribute__((packed, aligned(4))); + +struct vfe_chromasubsample_cfg { + /* Chroma Subsample Selection */ + uint32_t hCositedPhase:1; + uint32_t vCositedPhase:1; + uint32_t hCosited:1; + uint32_t vCosited:1; + uint32_t hsubSampleEnable:1; + uint32_t vsubSampleEnable:1; + uint32_t cropEnable:1; + uint32_t /* reserved */ : 25; + uint32_t cropWidthLastPixel:12; + uint32_t /* reserved */ : 4; + uint32_t cropWidthFirstPixel:12; + uint32_t /* reserved */ : 4; + uint32_t cropHeightLastLine:12; + uint32_t /* reserved */ : 4; + uint32_t cropHeightFirstLine:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct vfe_blacklevel_cfg { + /* Black Even-Even Value Config */ + uint32_t evenEvenAdjustment:9; + uint32_t /* reserved */ : 23; + /* Black Even-Odd Value Config */ + uint32_t evenOddAdjustment:9; + uint32_t /* reserved */ : 23; + /* Black Odd-Even Value Config */ + uint32_t oddEvenAdjustment:9; + uint32_t /* reserved */ : 23; + /* Black Odd-Odd Value Config */ + uint32_t oddOddAdjustment:9; + uint32_t /* reserved */ : 23; +} __attribute__((packed, aligned(4))); + +struct vfe_demux_cfg { + /* Demux Gain 0 Config */ + uint32_t ch0EvenGain:10; + uint32_t /* reserved */ : 6; + uint32_t ch0OddGain:10; + uint32_t /* reserved */ : 6; + /* Demux Gain 1 Config */ + uint32_t ch1Gain:10; + uint32_t /* reserved */ : 6; + uint32_t ch2Gain:10; + uint32_t /* reserved */ : 6; +} __attribute__((packed, aligned(4))); + +struct vfe_bps_info { + uint32_t greenBadPixelCount:8; + uint32_t /* reserved */ : 8; + uint32_t RedBlueBadPixelCount:8; + uint32_t /* reserved */ : 8; +} __attribute__((packed, aligned(4))); + +struct vfe_demosaic_cfg { + /* Demosaic Config */ + uint32_t abfEnable:1; + uint32_t badPixelCorrEnable:1; + uint32_t forceAbfOn:1; + uint32_t /* reserved */ : 1; + uint32_t abfShift:4; + uint32_t fminThreshold:7; + uint32_t /* reserved */ : 1; + uint32_t fmaxThreshold:7; + uint32_t /* reserved */ : 5; + uint32_t slopeShift:3; + uint32_t /* reserved */ : 1; +} __attribute__((packed, aligned(4))); + +struct vfe_demosaic_bpc_cfg { + /* Demosaic BPC Config 0 */ + uint32_t blueDiffThreshold:12; + uint32_t redDiffThreshold:12; + uint32_t /* reserved */ : 8; + /* Demosaic BPC Config 1 */ + uint32_t greenDiffThreshold:12; + uint32_t /* reserved */ : 20; +} __attribute__((packed, aligned(4))); + +struct vfe_demosaic_abf_cfg { + /* Demosaic ABF Config 0 */ + uint32_t lpThreshold:10; + uint32_t /* reserved */ : 22; + /* Demosaic ABF Config 1 */ + uint32_t ratio:4; + uint32_t minValue:10; + uint32_t /* reserved */ : 2; + uint32_t maxValue:10; + uint32_t /* reserved */ : 6; +} __attribute__((packed, aligned(4))); + +struct vfe_color_correction_cfg { + /* Color Corr. Coefficient 0 Config */ + uint32_t c0:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Coefficient 1 Config */ + uint32_t c1:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Coefficient 2 Config */ + uint32_t c2:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Coefficient 3 Config */ + uint32_t c3:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Coefficient 4 Config */ + uint32_t c4:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Coefficient 5 Config */ + uint32_t c5:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Coefficient 6 Config */ + uint32_t c6:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Coefficient 7 Config */ + uint32_t c7:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Coefficient 8 Config */ + uint32_t c8:12; + uint32_t /* reserved */ : 20; + /* Color Corr. Offset 0 Config */ + uint32_t k0:11; + uint32_t /* reserved */ : 21; + /* Color Corr. Offset 1 Config */ + uint32_t k1:11; + uint32_t /* reserved */ : 21; + /* Color Corr. Offset 2 Config */ + uint32_t k2:11; + uint32_t /* reserved */ : 21; + /* Color Corr. Coefficient Q Config */ + uint32_t coefQFactor:2; + uint32_t /* reserved */ : 30; +} __attribute__((packed, aligned(4))); + +struct VFE_LumaAdaptation_ConfigCmdType { + /* LA Config */ + uint32_t lutBankSelect:1; + uint32_t /* reserved */ : 31; +} __attribute__((packed, aligned(4))); + +struct vfe_wb_cfg { + /* WB Config */ + uint32_t ch0Gain:9; + uint32_t ch1Gain:9; + uint32_t ch2Gain:9; + uint32_t /* reserved */ : 5; +} __attribute__((packed, aligned(4))); + +struct VFE_GammaLutSelect_ConfigCmdType { + /* LUT Bank Select Config */ + uint32_t ch0BankSelect:1; + uint32_t ch1BankSelect:1; + uint32_t ch2BankSelect:1; + uint32_t /* reserved */ : 29; +} __attribute__((packed, aligned(4))); + +struct vfe_chroma_enhance_cfg { + /* Chroma Enhance A Config */ + uint32_t ap:11; + uint32_t /* reserved */ : 5; + uint32_t am:11; + uint32_t /* reserved */ : 5; + /* Chroma Enhance B Config */ + uint32_t bp:11; + uint32_t /* reserved */ : 5; + uint32_t bm:11; + uint32_t /* reserved */ : 5; + /* Chroma Enhance C Config */ + uint32_t cp:11; + uint32_t /* reserved */ : 5; + uint32_t cm:11; + uint32_t /* reserved */ : 5; + /* Chroma Enhance D Config */ + uint32_t dp:11; + uint32_t /* reserved */ : 5; + uint32_t dm:11; + uint32_t /* reserved */ : 5; + /* Chroma Enhance K Config */ + uint32_t kcb:11; + uint32_t /* reserved */ : 5; + uint32_t kcr:11; + uint32_t /* reserved */ : 5; +} __attribute__((packed, aligned(4))); + +struct vfe_color_convert_cfg { + /* Conversion Coefficient 0 */ + uint32_t v0:12; + uint32_t /* reserved */ : 20; + /* Conversion Coefficient 1 */ + uint32_t v1:12; + uint32_t /* reserved */ : 20; + /* Conversion Coefficient 2 */ + uint32_t v2:12; + uint32_t /* reserved */ : 20; + /* Conversion Offset */ + uint32_t ConvertOffset:8; + uint32_t /* reserved */ : 24; +} __attribute__((packed, aligned(4))); + +struct VFE_SyncTimer_ConfigCmdType { + /* Timer Line Start Config */ + uint32_t timerLineStart:12; + uint32_t /* reserved */ : 20; + /* Timer Pixel Start Config */ + uint32_t timerPixelStart:18; + uint32_t /* reserved */ : 14; + /* Timer Pixel Duration Config */ + uint32_t timerPixelDuration:28; + uint32_t /* reserved */ : 4; + /* Sync Timer Polarity Config */ + uint32_t timer0Polarity:1; + uint32_t timer1Polarity:1; + uint32_t timer2Polarity:1; + uint32_t /* reserved */ : 29; +} __attribute__((packed, aligned(4))); + +struct VFE_AsyncTimer_ConfigCmdType { + /* Async Timer Config 0 */ + uint32_t inactiveLength:20; + uint32_t numRepetition:10; + uint32_t /* reserved */ : 1; + uint32_t polarity:1; + /* Async Timer Config 1 */ + uint32_t activeLength:20; + uint32_t /* reserved */ : 12; +} __attribute__((packed, aligned(4))); + +struct VFE_AWBAEStatistics_ConfigCmdType { + /* AWB autoexposure Config */ + uint32_t aeRegionConfig:1; + uint32_t aeSubregionConfig:1; + uint32_t /* reserved */ : 14; + uint32_t awbYMin:8; + uint32_t awbYMax:8; + /* AXW Header */ + uint32_t axwHeader:8; + uint32_t /* reserved */ : 24; + /* AWB Mconfig */ + uint32_t m4:8; + uint32_t m3:8; + uint32_t m2:8; + uint32_t m1:8; + /* AWB Cconfig */ + uint32_t c2:12; + uint32_t /* reserved */ : 4; + uint32_t c1:12; + uint32_t /* reserved */ : 4; + /* AWB Cconfig 2 */ + uint32_t c4:12; + uint32_t /* reserved */ : 4; + uint32_t c3:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct VFE_TestGen_ConfigCmdType { + /* HW Test Gen Config */ + uint32_t numFrame:10; + uint32_t /* reserved */ : 2; + uint32_t pixelDataSelect:1; + uint32_t systematicDataSelect:1; + uint32_t /* reserved */ : 2; + uint32_t pixelDataSize:2; + uint32_t hsyncEdge:1; + uint32_t vsyncEdge:1; + uint32_t /* reserved */ : 12; + /* HW Test Gen Image Config */ + uint32_t imageWidth:14; + uint32_t /* reserved */ : 2; + uint32_t imageHeight:14; + uint32_t /* reserved */ : 2; + /* SOF Offset Config */ + uint32_t sofOffset:24; + uint32_t /* reserved */ : 8; + /* EOF NOffset Config */ + uint32_t eofNOffset:24; + uint32_t /* reserved */ : 8; + /* SOL Offset Config */ + uint32_t solOffset:9; + uint32_t /* reserved */ : 23; + /* EOL NOffset Config */ + uint32_t eolNOffset:9; + uint32_t /* reserved */ : 23; + /* HBI Config */ + uint32_t hBlankInterval:14; + uint32_t /* reserved */ : 18; + /* VBL Config */ + uint32_t vBlankInterval:14; + uint32_t /* reserved */ : 2; + uint32_t vBlankIntervalEnable:1; + uint32_t /* reserved */ : 15; + /* SOF Dummy Line Config */ + uint32_t sofDummy:8; + uint32_t /* reserved */ : 24; + /* EOF Dummy Line Config */ + uint32_t eofDummy:8; + uint32_t /* reserved */ : 24; + /* Color Bars Config */ + uint32_t unicolorBarSelect:3; + uint32_t /* reserved */ : 1; + uint32_t unicolorBarEnable:1; + uint32_t splitEnable:1; + uint32_t pixelPattern:2; + uint32_t rotatePeriod:6; + uint32_t /* reserved */ : 18; + /* Random Config */ + uint32_t randomSeed:16; + uint32_t /* reserved */ : 16; +} __attribute__((packed, aligned(4))); + +struct VFE_Bus_Pm_ConfigCmdType { + /* VFE Bus Performance Monitor Config */ + uint32_t output2YWrPmEnable:1; + uint32_t output2CbcrWrPmEnable:1; + uint32_t output1YWrPmEnable:1; + uint32_t output1CbcrWrPmEnable:1; + uint32_t /* reserved */ : 28; +} __attribute__((packed, aligned(4))); + +struct vfe_asf_info { + /* asf max edge */ + uint32_t maxEdge:13; + uint32_t /* reserved */ : 3; + /* HBi count */ + uint32_t HBICount:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct vfe_camif_stats { + uint32_t pixelCount:14; + uint32_t /* reserved */ : 2; + uint32_t lineCount:14; + uint32_t /* reserved */ : 1; + uint32_t camifHalt:1; +} __attribute__((packed, aligned(4))); + +struct VFE_StatsCmdType { + uint32_t autoFocusEnable:1; + uint32_t axwEnable:1; + uint32_t histEnable:1; + uint32_t clearHistEnable:1; + uint32_t histAutoClearEnable:1; + uint32_t colorConversionEnable:1; + uint32_t /* reserved */ : 26; +} __attribute__((packed, aligned(4))); + + +struct vfe_statsframe { + uint32_t lastPixel:12; + uint32_t /* reserved */ : 4; + uint32_t lastLine:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct vfe_busstats_wrprio { + uint32_t afBusPriority:4; + uint32_t awbBusPriority:4; + uint32_t histBusPriority:4; + uint32_t afBusPriorityEn:1; + uint32_t awbBusPriorityEn:1; + uint32_t histBusPriorityEn:1; + uint32_t /* reserved */ : 17; +} __attribute__((packed, aligned(4))); + +struct vfe_statsaf_update { + /* VFE_STATS_AF_CFG */ + uint32_t windowVOffset:12; + uint32_t /* reserved */ : 4; + uint32_t windowHOffset:12; + uint32_t /* reserved */ : 3; + uint32_t windowMode:1; + + /* VFE_STATS_AF_DIM */ + uint32_t windowHeight:12; + uint32_t /* reserved */ : 4; + uint32_t windowWidth:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct vfe_statsaf_cfg { + /* VFE_STATS_AF_GRID_0 */ + uint32_t entry00:8; + uint32_t entry01:8; + uint32_t entry02:8; + uint32_t entry03:8; + + /* VFE_STATS_AF_GRID_1 */ + uint32_t entry10:8; + uint32_t entry11:8; + uint32_t entry12:8; + uint32_t entry13:8; + + /* VFE_STATS_AF_GRID_2 */ + uint32_t entry20:8; + uint32_t entry21:8; + uint32_t entry22:8; + uint32_t entry23:8; + + /* VFE_STATS_AF_GRID_3 */ + uint32_t entry30:8; + uint32_t entry31:8; + uint32_t entry32:8; + uint32_t entry33:8; + + /* VFE_STATS_AF_HEADER */ + uint32_t afHeader:8; + uint32_t /* reserved */ : 24; + /* VFE_STATS_AF_COEF0 */ + uint32_t a00:5; + uint32_t a04:5; + uint32_t fvMax:11; + uint32_t fvMetric:1; + uint32_t /* reserved */ : 10; + + /* VFE_STATS_AF_COEF1 */ + uint32_t a20:5; + uint32_t a21:5; + uint32_t a22:5; + uint32_t a23:5; + uint32_t a24:5; + uint32_t /* reserved */ : 7; +} __attribute__((packed, aligned(4))); + +struct vfe_statsawbae_update { + uint32_t aeRegionCfg:1; + uint32_t aeSubregionCfg:1; + uint32_t /* reserved */ : 14; + uint32_t awbYMin:8; + uint32_t awbYMax:8; +} __attribute__((packed, aligned(4))); + +struct vfe_statsaxw_hdr_cfg { + /* Stats AXW Header Config */ + uint32_t axwHeader:8; + uint32_t /* reserved */ : 24; +} __attribute__((packed, aligned(4))); + +struct vfe_statsawb_update { + /* AWB MConfig */ + uint32_t m4:8; + uint32_t m3:8; + uint32_t m2:8; + uint32_t m1:8; + + /* AWB CConfig1 */ + uint32_t c2:12; + uint32_t /* reserved */ : 4; + uint32_t c1:12; + uint32_t /* reserved */ : 4; + + /* AWB CConfig2 */ + uint32_t c4:12; + uint32_t /* reserved */ : 4; + uint32_t c3:12; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct VFE_SyncTimerCmdType { + uint32_t hsyncCount:12; + uint32_t /* reserved */ : 20; + uint32_t pclkCount:18; + uint32_t /* reserved */ : 14; + uint32_t outputDuration:28; + uint32_t /* reserved */ : 4; +} __attribute__((packed, aligned(4))); + +struct VFE_AsyncTimerCmdType { + /* config 0 */ + uint32_t inactiveCount:20; + uint32_t repeatCount:10; + uint32_t /* reserved */ : 1; + uint32_t polarity:1; + /* config 1 */ + uint32_t activeCount:20; + uint32_t /* reserved */ : 12; +} __attribute__((packed, aligned(4))); + +struct VFE_AxiInputCmdType { + uint32_t stripeStartAddr0:32; + uint32_t stripeStartAddr1:32; + uint32_t stripeStartAddr2:32; + uint32_t stripeStartAddr3:32; + + uint32_t ySize:12; + uint32_t yOffsetDelta:12; + uint32_t /* reserved */ : 8; + + /* bus_stripe_rd_hSize */ + uint32_t /* reserved */ : 16; + uint32_t xSizeWord:10; + uint32_t /* reserved */ : 6; + + /* bus_stripe_rd_buffer_cfg */ + uint32_t burstLength:2; + uint32_t /* reserved */ : 2; + uint32_t NumOfRows:12; + uint32_t RowIncrement:12; + uint32_t /* reserved */ : 4; + + /* bus_stripe_rd_unpack_cfg */ + uint32_t mainUnpackHeight:12; + uint32_t mainUnpackWidth:13; + uint32_t mainUnpackHbiSel:3; + uint32_t mainUnpackPhase:3; + uint32_t /* reserved */ : 1; + + /* bus_stripe_rd_unpack */ + uint32_t unpackPattern:32; + + /* bus_stripe_rd_pad_size */ + uint32_t padLeft:7; + uint32_t /* reserved */ : 1; + uint32_t padRight:7; + uint32_t /* reserved */ : 1; + uint32_t padTop:7; + uint32_t /* reserved */ : 1; + uint32_t padBottom:7; + uint32_t /* reserved */ : 1; + + /* bus_stripe_rd_pad_L_unpack */ + uint32_t leftUnpackPattern0:4; + uint32_t leftUnpackPattern1:4; + uint32_t leftUnpackPattern2:4; + uint32_t leftUnpackPattern3:4; + uint32_t leftUnpackStop0:1; + uint32_t leftUnpackStop1:1; + uint32_t leftUnpackStop2:1; + uint32_t leftUnpackStop3:1; + uint32_t /* reserved */ : 12; + + /* bus_stripe_rd_pad_R_unpack */ + uint32_t rightUnpackPattern0:4; + uint32_t rightUnpackPattern1:4; + uint32_t rightUnpackPattern2:4; + uint32_t rightUnpackPattern3:4; + uint32_t rightUnpackStop0:1; + uint32_t rightUnpackStop1:1; + uint32_t rightUnpackStop2:1; + uint32_t rightUnpackStop3:1; + uint32_t /* reserved */ : 12; + + /* bus_stripe_rd_pad_tb_unpack */ + uint32_t topUnapckPattern:4; + uint32_t /* reserved */ : 12; + uint32_t bottomUnapckPattern:4; + uint32_t /* reserved */ : 12; +} __attribute__((packed, aligned(4))); + +struct VFE_AxiRdFragIrqEnable { + uint32_t stripeRdFragirq0Enable:1; + uint32_t stripeRdFragirq1Enable:1; + uint32_t stripeRdFragirq2Enable:1; + uint32_t stripeRdFragirq3Enable:1; + uint32_t /* reserved */ : 28; +} __attribute__((packed, aligned(4))); + +int vfe_cmd_init(struct msm_vfe_callback *, struct platform_device *, void *); +void vfe_stats_af_stop(void); +void vfe_stop(void); +void vfe_update(void); +int vfe_rgb_gamma_update(struct vfe_cmd_rgb_gamma_config *); +int vfe_rgb_gamma_config(struct vfe_cmd_rgb_gamma_config *); +void vfe_stats_wb_exp_ack(struct vfe_cmd_stats_wb_exp_ack *); +void vfe_stats_af_ack(struct vfe_cmd_stats_af_ack *); +void vfe_start(struct vfe_cmd_start *); +void vfe_la_update(struct vfe_cmd_la_config *); +void vfe_la_config(struct vfe_cmd_la_config *); +void vfe_test_gen_start(struct vfe_cmd_test_gen_start *); +void vfe_frame_skip_update(struct vfe_cmd_frame_skip_update *); +void vfe_frame_skip_config(struct vfe_cmd_frame_skip_config *); +void vfe_output_clamp_config(struct vfe_cmd_output_clamp_config *); +void vfe_camif_frame_update(struct vfe_cmds_camif_frame *); +void vfe_color_correction_config(struct vfe_cmd_color_correction_config *); +void vfe_demosaic_abf_update(struct vfe_cmd_demosaic_abf_update *); +void vfe_demosaic_bpc_update(struct vfe_cmd_demosaic_bpc_update *); +void vfe_demosaic_config(struct vfe_cmd_demosaic_config *); +void vfe_demux_channel_gain_update(struct vfe_cmd_demux_channel_gain_config *); +void vfe_demux_channel_gain_config(struct vfe_cmd_demux_channel_gain_config *); +void vfe_black_level_update(struct vfe_cmd_black_level_config *); +void vfe_black_level_config(struct vfe_cmd_black_level_config *); +void vfe_asf_update(struct vfe_cmd_asf_update *); +void vfe_asf_config(struct vfe_cmd_asf_config *); +void vfe_white_balance_config(struct vfe_cmd_white_balance_config *); +void vfe_chroma_sup_config(struct vfe_cmd_chroma_suppression_config *); +void vfe_roll_off_config(struct vfe_cmd_roll_off_config *); +void vfe_chroma_subsample_config(struct vfe_cmd_chroma_subsample_config *); +void vfe_chroma_enhan_config(struct vfe_cmd_chroma_enhan_config *); +void vfe_scaler2cbcr_config(struct vfe_cmd_scaler2_config *); +void vfe_scaler2y_config(struct vfe_cmd_scaler2_config *); +void vfe_main_scaler_config(struct vfe_cmd_main_scaler_config *); +void vfe_stats_wb_exp_stop(void); +void vfe_stats_update_wb_exp(struct vfe_cmd_stats_wb_exp_update *); +void vfe_stats_update_af(struct vfe_cmd_stats_af_update *); +void vfe_stats_start_wb_exp(struct vfe_cmd_stats_wb_exp_start *); +void vfe_stats_start_af(struct vfe_cmd_stats_af_start *); +void vfe_stats_setting(struct vfe_cmd_stats_setting *); +void vfe_axi_input_config(struct vfe_cmd_axi_input_config *); +void vfe_stats_config(struct vfe_cmd_stats_setting *); +void vfe_axi_output_config(struct vfe_cmd_axi_output_config *); +void vfe_camif_config(struct vfe_cmd_camif_config *); +void vfe_fov_crop_config(struct vfe_cmd_fov_crop_config *); +void vfe_get_hw_version(struct vfe_cmd_hw_version *); +void vfe_reset(void); +void vfe_cmd_release(struct platform_device *); +void vfe_output1_ack(struct vfe_cmd_output_ack *); +void vfe_output2_ack(struct vfe_cmd_output_ack *); +#endif /* __MSM_VFE8X_REG_H__ */ diff --git a/drivers/media/video/msm/mt9d112.c b/drivers/media/video/msm/mt9d112.c new file mode 100644 index 0000000000000..4f938f9dfc470 --- /dev/null +++ b/drivers/media/video/msm/mt9d112.c @@ -0,0 +1,761 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "mt9d112.h" + +/* Micron MT9D112 Registers and their values */ +/* Sensor Core Registers */ +#define REG_MT9D112_MODEL_ID 0x3000 +#define MT9D112_MODEL_ID 0x1580 + +/* SOC Registers Page 1 */ +#define REG_MT9D112_SENSOR_RESET 0x301A +#define REG_MT9D112_STANDBY_CONTROL 0x3202 +#define REG_MT9D112_MCU_BOOT 0x3386 + +struct mt9d112_work { + struct work_struct work; +}; + +static struct mt9d112_work *mt9d112_sensorw; +static struct i2c_client *mt9d112_client; + +struct mt9d112_ctrl { + const struct msm_camera_sensor_info *sensordata; +}; + + +static struct mt9d112_ctrl *mt9d112_ctrl; + +static DECLARE_WAIT_QUEUE_HEAD(mt9d112_wait_queue); +DECLARE_MUTEX(mt9d112_sem); + + +/*============================================================= + EXTERNAL DECLARATIONS +==============================================================*/ +extern struct mt9d112_reg mt9d112_regs; + + +/*=============================================================*/ + +static int mt9d112_reset(const struct msm_camera_sensor_info *dev) +{ + int rc = 0; + + rc = gpio_request(dev->sensor_reset, "mt9d112"); + + if (!rc) { + rc = gpio_direction_output(dev->sensor_reset, 0); + mdelay(20); + rc = gpio_direction_output(dev->sensor_reset, 1); + } + + gpio_free(dev->sensor_reset); + return rc; +} + +static int32_t mt9d112_i2c_txdata(unsigned short saddr, + unsigned char *txdata, int length) +{ + struct i2c_msg msg[] = { + { + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, + }, + }; + + if (i2c_transfer(mt9d112_client->adapter, msg, 1) < 0) { + CDBG("mt9d112_i2c_txdata failed\n"); + return -EIO; + } + + return 0; +} + +static int32_t mt9d112_i2c_write(unsigned short saddr, + unsigned short waddr, unsigned short wdata, enum mt9d112_width width) +{ + int32_t rc = -EIO; + unsigned char buf[4]; + + memset(buf, 0, sizeof(buf)); + switch (width) { + case WORD_LEN: { + buf[0] = (waddr & 0xFF00)>>8; + buf[1] = (waddr & 0x00FF); + buf[2] = (wdata & 0xFF00)>>8; + buf[3] = (wdata & 0x00FF); + + rc = mt9d112_i2c_txdata(saddr, buf, 4); + } + break; + + case BYTE_LEN: { + buf[0] = waddr; + buf[1] = wdata; + rc = mt9d112_i2c_txdata(saddr, buf, 2); + } + break; + + default: + break; + } + + if (rc < 0) + CDBG( + "i2c_write failed, addr = 0x%x, val = 0x%x!\n", + waddr, wdata); + + return rc; +} + +static int32_t mt9d112_i2c_write_table( + struct mt9d112_i2c_reg_conf const *reg_conf_tbl, + int num_of_items_in_table) +{ + int i; + int32_t rc = -EIO; + + for (i = 0; i < num_of_items_in_table; i++) { + rc = mt9d112_i2c_write(mt9d112_client->addr, + reg_conf_tbl->waddr, reg_conf_tbl->wdata, + reg_conf_tbl->width); + if (rc < 0) + break; + if (reg_conf_tbl->mdelay_time != 0) + mdelay(reg_conf_tbl->mdelay_time); + reg_conf_tbl++; + } + + return rc; +} + +static int mt9d112_i2c_rxdata(unsigned short saddr, + unsigned char *rxdata, int length) +{ + struct i2c_msg msgs[] = { + { + .addr = saddr, + .flags = 0, + .len = 2, + .buf = rxdata, + }, + { + .addr = saddr, + .flags = I2C_M_RD, + .len = length, + .buf = rxdata, + }, + }; + + if (i2c_transfer(mt9d112_client->adapter, msgs, 2) < 0) { + CDBG("mt9d112_i2c_rxdata failed!\n"); + return -EIO; + } + + return 0; +} + +static int32_t mt9d112_i2c_read(unsigned short saddr, + unsigned short raddr, unsigned short *rdata, enum mt9d112_width width) +{ + int32_t rc = 0; + unsigned char buf[4]; + + if (!rdata) + return -EIO; + + memset(buf, 0, sizeof(buf)); + + switch (width) { + case WORD_LEN: { + buf[0] = (raddr & 0xFF00)>>8; + buf[1] = (raddr & 0x00FF); + + rc = mt9d112_i2c_rxdata(saddr, buf, 2); + if (rc < 0) + return rc; + + *rdata = buf[0] << 8 | buf[1]; + } + break; + + default: + break; + } + + if (rc < 0) + CDBG("mt9d112_i2c_read failed!\n"); + + return rc; +} + +static int32_t mt9d112_set_lens_roll_off(void) +{ + int32_t rc = 0; + rc = mt9d112_i2c_write_table(&mt9d112_regs.rftbl[0], + mt9d112_regs.rftbl_size); + return rc; +} + +static long mt9d112_reg_init(void) +{ + int32_t array_length; + int32_t i; + long rc; + + /* PLL Setup Start */ + rc = mt9d112_i2c_write_table(&mt9d112_regs.plltbl[0], + mt9d112_regs.plltbl_size); + + if (rc < 0) + return rc; + /* PLL Setup End */ + + array_length = mt9d112_regs.prev_snap_reg_settings_size; + + /* Configure sensor for Preview mode and Snapshot mode */ + for (i = 0; i < array_length; i++) { + rc = mt9d112_i2c_write(mt9d112_client->addr, + mt9d112_regs.prev_snap_reg_settings[i].register_address, + mt9d112_regs.prev_snap_reg_settings[i].register_value, + WORD_LEN); + + if (rc < 0) + return rc; + } + + /* Configure for Noise Reduction, Saturation and Aperture Correction */ + array_length = mt9d112_regs.noise_reduction_reg_settings_size; + + for (i = 0; i < array_length; i++) { + rc = mt9d112_i2c_write(mt9d112_client->addr, + mt9d112_regs.noise_reduction_reg_settings[i].register_address, + mt9d112_regs.noise_reduction_reg_settings[i].register_value, + WORD_LEN); + + if (rc < 0) + return rc; + } + + /* Set Color Kill Saturation point to optimum value */ + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x35A4, + 0x0593, + WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write_table(&mt9d112_regs.stbl[0], + mt9d112_regs.stbl_size); + if (rc < 0) + return rc; + + rc = mt9d112_set_lens_roll_off(); + if (rc < 0) + return rc; + + return 0; +} + +static long mt9d112_set_sensor_mode(int mode) +{ + uint16_t clock; + long rc = 0; + + switch (mode) { + case SENSOR_PREVIEW_MODE: + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA20C, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0004, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA215, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0004, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA20B, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0000, WORD_LEN); + if (rc < 0) + return rc; + + clock = 0x0250; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x341C, clock, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA103, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0001, WORD_LEN); + if (rc < 0) + return rc; + + mdelay(5); + break; + + case SENSOR_SNAPSHOT_MODE: + /* Switch to lower fps for Snapshot */ + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x341C, 0x0120, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA120, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0002, WORD_LEN); + if (rc < 0) + return rc; + + mdelay(5); + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA103, WORD_LEN); + if (rc < 0) + return rc; + + rc = + mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0002, WORD_LEN); + if (rc < 0) + return rc; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static long mt9d112_set_effect(int mode, int effect) +{ + uint16_t reg_addr; + uint16_t reg_val; + long rc = 0; + + switch (mode) { + case SENSOR_PREVIEW_MODE: + /* Context A Special Effects */ + reg_addr = 0x2799; + break; + + case SENSOR_SNAPSHOT_MODE: + /* Context B Special Effects */ + reg_addr = 0x279B; + break; + + default: + reg_addr = 0x2799; + break; + } + + switch (effect) { + case CAMERA_EFFECT_OFF: { + reg_val = 0x6440; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; + } + break; + + case CAMERA_EFFECT_MONO: { + reg_val = 0x6441; + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; + } + break; + + case CAMERA_EFFECT_NEGATIVE: { + reg_val = 0x6443; + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; + } + break; + + case CAMERA_EFFECT_SOLARIZE: { + reg_val = 0x6445; + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; + } + break; + + case CAMERA_EFFECT_SEPIA: { + reg_val = 0x6442; + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; + } + break; + + case CAMERA_EFFECT_PASTEL: + case CAMERA_EFFECT_MOSAIC: + case CAMERA_EFFECT_RESIZE: + return -EINVAL; + + default: { + reg_val = 0x6440; + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; + + return -EINVAL; + } + } + + /* Refresh Sequencer */ + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA103, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0005, WORD_LEN); + + return rc; +} + +static int mt9d112_sensor_init_probe(const struct msm_camera_sensor_info *data) +{ + uint16_t model_id = 0; + int rc = 0; + + CDBG("init entry \n"); + rc = mt9d112_reset(data); + if (rc < 0) { + CDBG("reset failed!\n"); + goto init_probe_fail; + } + + mdelay(5); + + /* Micron suggested Power up block Start: + * Put MCU into Reset - Stop MCU */ + rc = mt9d112_i2c_write(mt9d112_client->addr, + REG_MT9D112_MCU_BOOT, 0x0501, WORD_LEN); + if (rc < 0) + goto init_probe_fail; + + /* Pull MCU from Reset - Start MCU */ + rc = mt9d112_i2c_write(mt9d112_client->addr, + REG_MT9D112_MCU_BOOT, 0x0500, WORD_LEN); + if (rc < 0) + goto init_probe_fail; + + mdelay(5); + + /* Micron Suggested - Power up block */ + rc = mt9d112_i2c_write(mt9d112_client->addr, + REG_MT9D112_SENSOR_RESET, 0x0ACC, WORD_LEN); + if (rc < 0) + goto init_probe_fail; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + REG_MT9D112_STANDBY_CONTROL, 0x0008, WORD_LEN); + if (rc < 0) + goto init_probe_fail; + + /* FUSED_DEFECT_CORRECTION */ + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x33F4, 0x031D, WORD_LEN); + if (rc < 0) + goto init_probe_fail; + + mdelay(5); + + /* Micron suggested Power up block End */ + /* Read the Model ID of the sensor */ + rc = mt9d112_i2c_read(mt9d112_client->addr, + REG_MT9D112_MODEL_ID, &model_id, WORD_LEN); + if (rc < 0) + goto init_probe_fail; + + CDBG("mt9d112 model_id = 0x%x\n", model_id); + + /* Check if it matches it with the value in Datasheet */ + if (model_id != MT9D112_MODEL_ID) { + rc = -EINVAL; + goto init_probe_fail; + } + + rc = mt9d112_reg_init(); + if (rc < 0) + goto init_probe_fail; + + return rc; + +init_probe_fail: + return rc; +} + +int mt9d112_sensor_init(const struct msm_camera_sensor_info *data) +{ + int rc = 0; + + mt9d112_ctrl = kzalloc(sizeof(struct mt9d112_ctrl), GFP_KERNEL); + if (!mt9d112_ctrl) { + CDBG("mt9d112_init failed!\n"); + rc = -ENOMEM; + goto init_done; + } + + if (data) + mt9d112_ctrl->sensordata = data; + + /* Input MCLK = 24MHz */ + msm_camio_clk_rate_set(24000000); + mdelay(5); + + msm_camio_camif_pad_reg_reset(); + + rc = mt9d112_sensor_init_probe(data); + if (rc < 0) { + CDBG("mt9d112_sensor_init failed!\n"); + goto init_fail; + } + +init_done: + return rc; + +init_fail: + kfree(mt9d112_ctrl); + return rc; +} + +static int mt9d112_init_client(struct i2c_client *client) +{ + /* Initialize the MSM_CAMI2C Chip */ + init_waitqueue_head(&mt9d112_wait_queue); + return 0; +} + +int mt9d112_sensor_config(void __user *argp) +{ + struct sensor_cfg_data cfg_data; + long rc = 0; + + if (copy_from_user(&cfg_data, + (void *)argp, + sizeof(struct sensor_cfg_data))) + return -EFAULT; + + /* down(&mt9d112_sem); */ + + CDBG("mt9d112_ioctl, cfgtype = %d, mode = %d\n", + cfg_data.cfgtype, cfg_data.mode); + + switch (cfg_data.cfgtype) { + case CFG_SET_MODE: + rc = mt9d112_set_sensor_mode( + cfg_data.mode); + break; + + case CFG_SET_EFFECT: + rc = mt9d112_set_effect(cfg_data.mode, + cfg_data.cfg.effect); + break; + + case CFG_GET_AF_MAX_STEPS: + default: + rc = -EINVAL; + break; + } + + /* up(&mt9d112_sem); */ + + return rc; +} + +int mt9d112_sensor_release(void) +{ + int rc = 0; + + /* down(&mt9d112_sem); */ + + kfree(mt9d112_ctrl); + /* up(&mt9d112_sem); */ + + return rc; +} + +static int mt9d112_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + rc = -ENOTSUPP; + goto probe_failure; + } + + mt9d112_sensorw = + kzalloc(sizeof(struct mt9d112_work), GFP_KERNEL); + + if (!mt9d112_sensorw) { + rc = -ENOMEM; + goto probe_failure; + } + + i2c_set_clientdata(client, mt9d112_sensorw); + mt9d112_init_client(client); + mt9d112_client = client; + + CDBG("mt9d112_probe succeeded!\n"); + + return 0; + +probe_failure: + kfree(mt9d112_sensorw); + mt9d112_sensorw = NULL; + CDBG("mt9d112_probe failed!\n"); + return rc; +} + +static const struct i2c_device_id mt9d112_i2c_id[] = { + { "mt9d112", 0}, + { }, +}; + +static struct i2c_driver mt9d112_i2c_driver = { + .id_table = mt9d112_i2c_id, + .probe = mt9d112_i2c_probe, + .remove = __exit_p(mt9d112_i2c_remove), + .driver = { + .name = "mt9d112", + }, +}; + +static int mt9d112_sensor_probe(const struct msm_camera_sensor_info *info, + struct msm_sensor_ctrl *s) +{ + int rc = i2c_add_driver(&mt9d112_i2c_driver); + if (rc < 0 || mt9d112_client == NULL) { + rc = -ENOTSUPP; + goto probe_done; + } + + /* Input MCLK = 24MHz */ + msm_camio_clk_rate_set(24000000); + mdelay(5); + + rc = mt9d112_sensor_init_probe(info); + if (rc < 0) + goto probe_done; + + s->s_init = mt9d112_sensor_init; + s->s_release = mt9d112_sensor_release; + s->s_config = mt9d112_sensor_config; + +probe_done: + CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__); + return rc; +} + +static int __mt9d112_probe(struct platform_device *pdev) +{ + return msm_camera_drv_start(pdev, mt9d112_sensor_probe); +} + +static struct platform_driver msm_camera_driver = { + .probe = __mt9d112_probe, + .driver = { + .name = "msm_camera_mt9d112", + .owner = THIS_MODULE, + }, +}; + +static int __init mt9d112_init(void) +{ + return platform_driver_register(&msm_camera_driver); +} + +module_init(mt9d112_init); diff --git a/drivers/media/video/msm/mt9d112.h b/drivers/media/video/msm/mt9d112.h new file mode 100644 index 0000000000000..c678996f9e2ba --- /dev/null +++ b/drivers/media/video/msm/mt9d112.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#ifndef MT9D112_H +#define MT9D112_H + +#include +#include + +enum mt9d112_width { + WORD_LEN, + BYTE_LEN +}; + +struct mt9d112_i2c_reg_conf { + unsigned short waddr; + unsigned short wdata; + enum mt9d112_width width; + unsigned short mdelay_time; +}; + +struct mt9d112_reg { + const struct register_address_value_pair *prev_snap_reg_settings; + uint16_t prev_snap_reg_settings_size; + const struct register_address_value_pair *noise_reduction_reg_settings; + uint16_t noise_reduction_reg_settings_size; + const struct mt9d112_i2c_reg_conf *plltbl; + uint16_t plltbl_size; + const struct mt9d112_i2c_reg_conf *stbl; + uint16_t stbl_size; + const struct mt9d112_i2c_reg_conf *rftbl; + uint16_t rftbl_size; +}; + +#endif /* MT9D112_H */ diff --git a/drivers/media/video/msm/mt9d112_reg.c b/drivers/media/video/msm/mt9d112_reg.c new file mode 100644 index 0000000000000..c52e96f47141f --- /dev/null +++ b/drivers/media/video/msm/mt9d112_reg.c @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#include "mt9d112.h" + +struct register_address_value_pair +preview_snapshot_mode_reg_settings_array[] = { + {0x338C, 0x2703}, + {0x3390, 800}, /* Output Width (P) = 640 */ + {0x338C, 0x2705}, + {0x3390, 600}, /* Output Height (P) = 480 */ + {0x338C, 0x2707}, + {0x3390, 0x0640}, /* Output Width (S) = 1600 */ + {0x338C, 0x2709}, + {0x3390, 0x04B0}, /* Output Height (S) = 1200 */ + {0x338C, 0x270D}, + {0x3390, 0x0000}, /* Row Start (P) = 0 */ + {0x338C, 0x270F}, + {0x3390, 0x0000}, /* Column Start (P) = 0 */ + {0x338C, 0x2711}, + {0x3390, 0x04BD}, /* Row End (P) = 1213 */ + {0x338C, 0x2713}, + {0x3390, 0x064D}, /* Column End (P) = 1613 */ + {0x338C, 0x2715}, + {0x3390, 0x0000}, /* Extra Delay (P) = 0 */ + {0x338C, 0x2717}, + {0x3390, 0x2111}, /* Row Speed (P) = 8465 */ + {0x338C, 0x2719}, + {0x3390, 0x046C}, /* Read Mode (P) = 1132 */ + {0x338C, 0x271B}, + {0x3390, 0x024F}, /* Sensor_Sample_Time_pck(P) = 591 */ + {0x338C, 0x271D}, + {0x3390, 0x0102}, /* Sensor_Fine_Correction(P) = 258 */ + {0x338C, 0x271F}, + {0x3390, 0x0279}, /* Sensor_Fine_IT_min(P) = 633 */ + {0x338C, 0x2721}, + {0x3390, 0x0155}, /* Sensor_Fine_IT_max_margin(P) = 341 */ + {0x338C, 0x2723}, + {0x3390, 659}, /* Frame Lines (P) = 679 */ + {0x338C, 0x2725}, + {0x3390, 0x0824}, /* Line Length (P) = 2084 */ + {0x338C, 0x2727}, + {0x3390, 0x2020}, + {0x338C, 0x2729}, + {0x3390, 0x2020}, + {0x338C, 0x272B}, + {0x3390, 0x1020}, + {0x338C, 0x272D}, + {0x3390, 0x2007}, + {0x338C, 0x272F}, + {0x3390, 0x0004}, /* Row Start(S) = 4 */ + {0x338C, 0x2731}, + {0x3390, 0x0004}, /* Column Start(S) = 4 */ + {0x338C, 0x2733}, + {0x3390, 0x04BB}, /* Row End(S) = 1211 */ + {0x338C, 0x2735}, + {0x3390, 0x064B}, /* Column End(S) = 1611 */ + {0x338C, 0x2737}, + {0x3390, 0x04CE}, /* Extra Delay(S) = 1230 */ + {0x338C, 0x2739}, + {0x3390, 0x2111}, /* Row Speed(S) = 8465 */ + {0x338C, 0x273B}, + {0x3390, 0x0024}, /* Read Mode(S) = 36 */ + {0x338C, 0x273D}, + {0x3390, 0x0120}, /* Sensor sample time pck(S) = 288 */ + {0x338C, 0x2741}, + {0x3390, 0x0169}, /* Sensor_Fine_IT_min(P) = 361 */ + {0x338C, 0x2745}, + {0x3390, 0x04FF}, /* Frame Lines(S) = 1279 */ + {0x338C, 0x2747}, + {0x3390, 0x0824}, /* Line Length(S) = 2084 */ + {0x338C, 0x2751}, + {0x3390, 0x0000}, /* Crop_X0(P) = 0 */ + {0x338C, 0x2753}, + {0x3390, 0x0320}, /* Crop_X1(P) = 800 */ + {0x338C, 0x2755}, + {0x3390, 0x0000}, /* Crop_Y0(P) = 0 */ + {0x338C, 0x2757}, + {0x3390, 0x0258}, /* Crop_Y1(P) = 600 */ + {0x338C, 0x275F}, + {0x3390, 0x0000}, /* Crop_X0(S) = 0 */ + {0x338C, 0x2761}, + {0x3390, 0x0640}, /* Crop_X1(S) = 1600 */ + {0x338C, 0x2763}, + {0x3390, 0x0000}, /* Crop_Y0(S) = 0 */ + {0x338C, 0x2765}, + {0x3390, 0x04B0}, /* Crop_Y1(S) = 1200 */ + {0x338C, 0x222E}, + {0x3390, 0x00A0}, /* R9 Step = 160 */ + {0x338C, 0xA408}, + {0x3390, 0x001F}, + {0x338C, 0xA409}, + {0x3390, 0x0021}, + {0x338C, 0xA40A}, + {0x3390, 0x0025}, + {0x338C, 0xA40B}, + {0x3390, 0x0027}, + {0x338C, 0x2411}, + {0x3390, 0x00A0}, + {0x338C, 0x2413}, + {0x3390, 0x00C0}, + {0x338C, 0x2415}, + {0x3390, 0x00A0}, + {0x338C, 0x2417}, + {0x3390, 0x00C0}, + {0x338C, 0x2799}, + {0x3390, 0x6408}, /* MODE_SPEC_EFFECTS(P) */ + {0x338C, 0x279B}, + {0x3390, 0x6408}, /* MODE_SPEC_EFFECTS(S) */ +}; + +static struct register_address_value_pair +noise_reduction_reg_settings_array[] = { + {0x338C, 0xA76D}, + {0x3390, 0x0003}, + {0x338C, 0xA76E}, + {0x3390, 0x0003}, + {0x338C, 0xA76F}, + {0x3390, 0}, + {0x338C, 0xA770}, + {0x3390, 21}, + {0x338C, 0xA771}, + {0x3390, 37}, + {0x338C, 0xA772}, + {0x3390, 63}, + {0x338C, 0xA773}, + {0x3390, 100}, + {0x338C, 0xA774}, + {0x3390, 128}, + {0x338C, 0xA775}, + {0x3390, 151}, + {0x338C, 0xA776}, + {0x3390, 169}, + {0x338C, 0xA777}, + {0x3390, 186}, + {0x338C, 0xA778}, + {0x3390, 199}, + {0x338C, 0xA779}, + {0x3390, 210}, + {0x338C, 0xA77A}, + {0x3390, 220}, + {0x338C, 0xA77B}, + {0x3390, 228}, + {0x338C, 0xA77C}, + {0x3390, 234}, + {0x338C, 0xA77D}, + {0x3390, 240}, + {0x338C, 0xA77E}, + {0x3390, 244}, + {0x338C, 0xA77F}, + {0x3390, 248}, + {0x338C, 0xA780}, + {0x3390, 252}, + {0x338C, 0xA781}, + {0x3390, 255}, + {0x338C, 0xA782}, + {0x3390, 0}, + {0x338C, 0xA783}, + {0x3390, 21}, + {0x338C, 0xA784}, + {0x3390, 37}, + {0x338C, 0xA785}, + {0x3390, 63}, + {0x338C, 0xA786}, + {0x3390, 100}, + {0x338C, 0xA787}, + {0x3390, 128}, + {0x338C, 0xA788}, + {0x3390, 151}, + {0x338C, 0xA789}, + {0x3390, 169}, + {0x338C, 0xA78A}, + {0x3390, 186}, + {0x338C, 0xA78B}, + {0x3390, 199}, + {0x338C, 0xA78C}, + {0x3390, 210}, + {0x338C, 0xA78D}, + {0x3390, 220}, + {0x338C, 0xA78E}, + {0x3390, 228}, + {0x338C, 0xA78F}, + {0x3390, 234}, + {0x338C, 0xA790}, + {0x3390, 240}, + {0x338C, 0xA791}, + {0x3390, 244}, + {0x338C, 0xA793}, + {0x3390, 252}, + {0x338C, 0xA794}, + {0x3390, 255}, + {0x338C, 0xA103}, + {0x3390, 6}, +}; + +static const struct mt9d112_i2c_reg_conf const lens_roll_off_tbl[] = { + { 0x34CE, 0x81A0, WORD_LEN, 0 }, + { 0x34D0, 0x6331, WORD_LEN, 0 }, + { 0x34D2, 0x3394, WORD_LEN, 0 }, + { 0x34D4, 0x9966, WORD_LEN, 0 }, + { 0x34D6, 0x4B25, WORD_LEN, 0 }, + { 0x34D8, 0x2670, WORD_LEN, 0 }, + { 0x34DA, 0x724C, WORD_LEN, 0 }, + { 0x34DC, 0xFFFD, WORD_LEN, 0 }, + { 0x34DE, 0x00CA, WORD_LEN, 0 }, + { 0x34E6, 0x00AC, WORD_LEN, 0 }, + { 0x34EE, 0x0EE1, WORD_LEN, 0 }, + { 0x34F6, 0x0D87, WORD_LEN, 0 }, + { 0x3500, 0xE1F7, WORD_LEN, 0 }, + { 0x3508, 0x1CF4, WORD_LEN, 0 }, + { 0x3510, 0x1D28, WORD_LEN, 0 }, + { 0x3518, 0x1F26, WORD_LEN, 0 }, + { 0x3520, 0x2220, WORD_LEN, 0 }, + { 0x3528, 0x333D, WORD_LEN, 0 }, + { 0x3530, 0x15D9, WORD_LEN, 0 }, + { 0x3538, 0xCFB8, WORD_LEN, 0 }, + { 0x354C, 0x05FE, WORD_LEN, 0 }, + { 0x3544, 0x05F8, WORD_LEN, 0 }, + { 0x355C, 0x0596, WORD_LEN, 0 }, + { 0x3554, 0x0611, WORD_LEN, 0 }, + { 0x34E0, 0x00F2, WORD_LEN, 0 }, + { 0x34E8, 0x00A8, WORD_LEN, 0 }, + { 0x34F0, 0x0F7B, WORD_LEN, 0 }, + { 0x34F8, 0x0CD7, WORD_LEN, 0 }, + { 0x3502, 0xFEDB, WORD_LEN, 0 }, + { 0x350A, 0x13E4, WORD_LEN, 0 }, + { 0x3512, 0x1F2C, WORD_LEN, 0 }, + { 0x351A, 0x1D20, WORD_LEN, 0 }, + { 0x3522, 0x2422, WORD_LEN, 0 }, + { 0x352A, 0x2925, WORD_LEN, 0 }, + { 0x3532, 0x1D04, WORD_LEN, 0 }, + { 0x353A, 0xFBF2, WORD_LEN, 0 }, + { 0x354E, 0x0616, WORD_LEN, 0 }, + { 0x3546, 0x0597, WORD_LEN, 0 }, + { 0x355E, 0x05CD, WORD_LEN, 0 }, + { 0x3556, 0x0529, WORD_LEN, 0 }, + { 0x34E4, 0x00B2, WORD_LEN, 0 }, + { 0x34EC, 0x005E, WORD_LEN, 0 }, + { 0x34F4, 0x0F43, WORD_LEN, 0 }, + { 0x34FC, 0x0E2F, WORD_LEN, 0 }, + { 0x3506, 0xF9FC, WORD_LEN, 0 }, + { 0x350E, 0x0CE4, WORD_LEN, 0 }, + { 0x3516, 0x1E1E, WORD_LEN, 0 }, + { 0x351E, 0x1B19, WORD_LEN, 0 }, + { 0x3526, 0x151B, WORD_LEN, 0 }, + { 0x352E, 0x1416, WORD_LEN, 0 }, + { 0x3536, 0x10FC, WORD_LEN, 0 }, + { 0x353E, 0xC018, WORD_LEN, 0 }, + { 0x3552, 0x06B4, WORD_LEN, 0 }, + { 0x354A, 0x0506, WORD_LEN, 0 }, + { 0x3562, 0x06AB, WORD_LEN, 0 }, + { 0x355A, 0x063A, WORD_LEN, 0 }, + { 0x34E2, 0x00E5, WORD_LEN, 0 }, + { 0x34EA, 0x008B, WORD_LEN, 0 }, + { 0x34F2, 0x0E4C, WORD_LEN, 0 }, + { 0x34FA, 0x0CA3, WORD_LEN, 0 }, + { 0x3504, 0x0907, WORD_LEN, 0 }, + { 0x350C, 0x1DFD, WORD_LEN, 0 }, + { 0x3514, 0x1E24, WORD_LEN, 0 }, + { 0x351C, 0x2529, WORD_LEN, 0 }, + { 0x3524, 0x1D20, WORD_LEN, 0 }, + { 0x352C, 0x2332, WORD_LEN, 0 }, + { 0x3534, 0x10E9, WORD_LEN, 0 }, + { 0x353C, 0x0BCB, WORD_LEN, 0 }, + { 0x3550, 0x04EF, WORD_LEN, 0 }, + { 0x3548, 0x0609, WORD_LEN, 0 }, + { 0x3560, 0x0580, WORD_LEN, 0 }, + { 0x3558, 0x05DD, WORD_LEN, 0 }, + { 0x3540, 0x0000, WORD_LEN, 0 }, + { 0x3542, 0x0000, WORD_LEN, 0 } +}; + +static const struct mt9d112_i2c_reg_conf const pll_setup_tbl[] = { + { 0x341E, 0x8F09, WORD_LEN, 0 }, + { 0x341C, 0x0250, WORD_LEN, 0 }, + { 0x341E, 0x8F09, WORD_LEN, 5 }, + { 0x341E, 0x8F08, WORD_LEN, 0 } +}; + +/* Refresh Sequencer */ +static const struct mt9d112_i2c_reg_conf const sequencer_tbl[] = { + { 0x338C, 0x2799, WORD_LEN, 0}, + { 0x3390, 0x6440, WORD_LEN, 5}, + { 0x338C, 0x279B, WORD_LEN, 0}, + { 0x3390, 0x6440, WORD_LEN, 5}, + { 0x338C, 0xA103, WORD_LEN, 0}, + { 0x3390, 0x0005, WORD_LEN, 5}, + { 0x338C, 0xA103, WORD_LEN, 0}, + { 0x3390, 0x0006, WORD_LEN, 5} +}; + +struct mt9d112_reg mt9d112_regs = { + .prev_snap_reg_settings = &preview_snapshot_mode_reg_settings_array[0], + .prev_snap_reg_settings_size = ARRAY_SIZE(preview_snapshot_mode_reg_settings_array), + .noise_reduction_reg_settings = &noise_reduction_reg_settings_array[0], + .noise_reduction_reg_settings_size = ARRAY_SIZE(noise_reduction_reg_settings_array), + .plltbl = pll_setup_tbl, + .plltbl_size = ARRAY_SIZE(pll_setup_tbl), + .stbl = sequencer_tbl, + .stbl_size = ARRAY_SIZE(sequencer_tbl), + .rftbl = lens_roll_off_tbl, + .rftbl_size = ARRAY_SIZE(lens_roll_off_tbl) +}; + + + diff --git a/drivers/media/video/msm/mt9p012.h b/drivers/media/video/msm/mt9p012.h new file mode 100644 index 0000000000000..678a0027d42ee --- /dev/null +++ b/drivers/media/video/msm/mt9p012.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + + +#ifndef MT9T012_H +#define MT9T012_H + +#include + +struct reg_struct { + uint16_t vt_pix_clk_div; /* 0x0300 */ + uint16_t vt_sys_clk_div; /* 0x0302 */ + uint16_t pre_pll_clk_div; /* 0x0304 */ + uint16_t pll_multiplier; /* 0x0306 */ + uint16_t op_pix_clk_div; /* 0x0308 */ + uint16_t op_sys_clk_div; /* 0x030A */ + uint16_t scale_m; /* 0x0404 */ + uint16_t row_speed; /* 0x3016 */ + uint16_t x_addr_start; /* 0x3004 */ + uint16_t x_addr_end; /* 0x3008 */ + uint16_t y_addr_start; /* 0x3002 */ + uint16_t y_addr_end; /* 0x3006 */ + uint16_t read_mode; /* 0x3040 */ + uint16_t x_output_size ; /* 0x034C */ + uint16_t y_output_size; /* 0x034E */ + uint16_t line_length_pck; /* 0x300C */ + uint16_t frame_length_lines; /* 0x300A */ + uint16_t coarse_int_time; /* 0x3012 */ + uint16_t fine_int_time; /* 0x3014 */ +}; + + +struct mt9p012_i2c_reg_conf { + unsigned short waddr; + unsigned short wdata; +}; + + +struct mt9p012_reg { + struct reg_struct *reg_pat; + uint16_t reg_pat_size; + struct mt9p012_i2c_reg_conf *ttbl; + uint16_t ttbl_size; + struct mt9p012_i2c_reg_conf *lctbl; + uint16_t lctbl_size; + struct mt9p012_i2c_reg_conf *rftbl; + uint16_t rftbl_size; +}; + +#endif /* MT9T012_H */ diff --git a/drivers/media/video/msm/mt9p012_fox.c b/drivers/media/video/msm/mt9p012_fox.c new file mode 100644 index 0000000000000..70119d5e0ab32 --- /dev/null +++ b/drivers/media/video/msm/mt9p012_fox.c @@ -0,0 +1,1305 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mt9p012.h" + +/*============================================================= + SENSOR REGISTER DEFINES +==============================================================*/ +#define MT9P012_REG_MODEL_ID 0x0000 +#define MT9P012_MODEL_ID 0x2801 +#define REG_GROUPED_PARAMETER_HOLD 0x0104 +#define GROUPED_PARAMETER_HOLD 0x0100 +#define GROUPED_PARAMETER_UPDATE 0x0000 +#define REG_COARSE_INT_TIME 0x3012 +#define REG_VT_PIX_CLK_DIV 0x0300 +#define REG_VT_SYS_CLK_DIV 0x0302 +#define REG_PRE_PLL_CLK_DIV 0x0304 +#define REG_PLL_MULTIPLIER 0x0306 +#define REG_OP_PIX_CLK_DIV 0x0308 +#define REG_OP_SYS_CLK_DIV 0x030A +#define REG_SCALE_M 0x0404 +#define REG_FRAME_LENGTH_LINES 0x300A +#define REG_LINE_LENGTH_PCK 0x300C +#define REG_X_ADDR_START 0x3004 +#define REG_Y_ADDR_START 0x3002 +#define REG_X_ADDR_END 0x3008 +#define REG_Y_ADDR_END 0x3006 +#define REG_X_OUTPUT_SIZE 0x034C +#define REG_Y_OUTPUT_SIZE 0x034E +#define REG_FINE_INTEGRATION_TIME 0x3014 +#define REG_ROW_SPEED 0x3016 +#define MT9P012_REG_RESET_REGISTER 0x301A +#define MT9P012_RESET_REGISTER_PWON 0x10CC +#define MT9P012_RESET_REGISTER_PWOFF 0x10C8 +#define REG_READ_MODE 0x3040 +#define REG_GLOBAL_GAIN 0x305E +#define REG_TEST_PATTERN_MODE 0x3070 + +#define MT9P012_REV_7 + + +enum mt9p012_test_mode { + TEST_OFF, + TEST_1, + TEST_2, + TEST_3 +}; + +enum mt9p012_resolution { + QTR_SIZE, + FULL_SIZE, + INVALID_SIZE +}; + +enum mt9p012_reg_update { + /* Sensor egisters that need to be updated during initialization */ + REG_INIT, + /* Sensor egisters that needs periodic I2C writes */ + UPDATE_PERIODIC, + /* All the sensor Registers will be updated */ + UPDATE_ALL, + /* Not valid update */ + UPDATE_INVALID +}; + +enum mt9p012_setting { + RES_PREVIEW, + RES_CAPTURE +}; + +/* actuator's Slave Address */ +#define MT9P012_AF_I2C_ADDR 0x18 + +/* AF Total steps parameters */ +#define MT9P012_STEPS_NEAR_TO_CLOSEST_INF 32 +#define MT9P012_TOTAL_STEPS_NEAR_TO_FAR 32 + +#define MT9P012_MU5M0_PREVIEW_DUMMY_PIXELS 0 +#define MT9P012_MU5M0_PREVIEW_DUMMY_LINES 0 + +/* Time in milisecs for waiting for the sensor to reset.*/ +#define MT9P012_RESET_DELAY_MSECS 66 + +/* for 20 fps preview */ +#define MT9P012_DEFAULT_CLOCK_RATE 24000000 +#define MT9P012_DEFAULT_MAX_FPS 26 /* ???? */ + +struct mt9p012_work { + struct work_struct work; +}; +static struct mt9p012_work *mt9p012_sensorw; +static struct i2c_client *mt9p012_client; + +struct mt9p012_ctrl { + const struct msm_camera_sensor_info *sensordata; + + int sensormode; + uint32_t fps_divider; /* init to 1 * 0x00000400 */ + uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */ + + uint16_t curr_lens_pos; + uint16_t init_curr_lens_pos; + uint16_t my_reg_gain; + uint32_t my_reg_line_count; + + enum mt9p012_resolution prev_res; + enum mt9p012_resolution pict_res; + enum mt9p012_resolution curr_res; + enum mt9p012_test_mode set_test; +}; + + +static struct mt9p012_ctrl *mt9p012_ctrl; +static DECLARE_WAIT_QUEUE_HEAD(mt9p012_wait_queue); +DECLARE_MUTEX(mt9p012_sem); + +/*============================================================= + EXTERNAL DECLARATIONS +==============================================================*/ +extern struct mt9p012_reg mt9p012_regs; /* from mt9p012_reg.c */ + + + +/*=============================================================*/ + +static int mt9p012_i2c_rxdata(unsigned short saddr, unsigned char *rxdata, + int length) +{ + struct i2c_msg msgs[] = { + { + .addr = saddr, + .flags = 0, + .len = 2, + .buf = rxdata, + }, + { + .addr = saddr, + .flags = I2C_M_RD, + .len = length, + .buf = rxdata, + }, + }; + + if (i2c_transfer(mt9p012_client->adapter, msgs, 2) < 0) { + CDBG("mt9p012_i2c_rxdata failed!\n"); + return -EIO; + } + + return 0; +} + +static int32_t mt9p012_i2c_read_w(unsigned short saddr, unsigned short raddr, + unsigned short *rdata) +{ + int32_t rc = 0; + unsigned char buf[4]; + + if (!rdata) + return -EIO; + + memset(buf, 0, sizeof(buf)); + + buf[0] = (raddr & 0xFF00)>>8; + buf[1] = (raddr & 0x00FF); + + rc = mt9p012_i2c_rxdata(saddr, buf, 2); + if (rc < 0) + return rc; + + *rdata = buf[0] << 8 | buf[1]; + + if (rc < 0) + CDBG("mt9p012_i2c_read failed!\n"); + + return rc; +} + +static int32_t mt9p012_i2c_txdata(unsigned short saddr, unsigned char *txdata, + int length) +{ + struct i2c_msg msg[] = { + { + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, + }, + }; + + if (i2c_transfer(mt9p012_client->adapter, msg, 1) < 0) { + CDBG("mt9p012_i2c_txdata failed\n"); + return -EIO; + } + + return 0; +} + +static int32_t mt9p012_i2c_write_b(unsigned short saddr, unsigned short baddr, + unsigned short bdata) +{ + int32_t rc = -EIO; + unsigned char buf[2]; + + memset(buf, 0, sizeof(buf)); + buf[0] = baddr; + buf[1] = bdata; + rc = mt9p012_i2c_txdata(saddr, buf, 2); + + if (rc < 0) + CDBG("i2c_write failed, saddr = 0x%x addr = 0x%x, val =0x%x!\n", + saddr, baddr, bdata); + + return rc; +} + +static int32_t mt9p012_i2c_write_w(unsigned short saddr, unsigned short waddr, + unsigned short wdata) +{ + int32_t rc = -EIO; + unsigned char buf[4]; + + memset(buf, 0, sizeof(buf)); + buf[0] = (waddr & 0xFF00)>>8; + buf[1] = (waddr & 0x00FF); + buf[2] = (wdata & 0xFF00)>>8; + buf[3] = (wdata & 0x00FF); + + rc = mt9p012_i2c_txdata(saddr, buf, 4); + + if (rc < 0) + CDBG("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n", + waddr, wdata); + + return rc; +} + +static int32_t mt9p012_i2c_write_w_table( + struct mt9p012_i2c_reg_conf *reg_conf_tbl, int num) +{ + int i; + int32_t rc = -EIO; + + for (i = 0; i < num; i++) { + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + reg_conf_tbl->waddr, reg_conf_tbl->wdata); + if (rc < 0) + break; + reg_conf_tbl++; + } + + return rc; +} + +static int32_t mt9p012_test(enum mt9p012_test_mode mo) +{ + int32_t rc = 0; + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + if (mo == TEST_OFF) + return 0; + else { + rc = mt9p012_i2c_write_w_table(mt9p012_regs.ttbl, mt9p012_regs.ttbl_size); + if (rc < 0) + return rc; + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_TEST_PATTERN_MODE, (uint16_t)mo); + if (rc < 0) + return rc; + } + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + return rc; + + return rc; +} + +static int32_t mt9p012_lens_shading_enable(uint8_t is_enable) +{ + int32_t rc = 0; + + CDBG("%s: entered. enable = %d\n", __func__, is_enable); + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x3780, + ((uint16_t) is_enable) << 15); + if (rc < 0) + return rc; + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE); + + CDBG("%s: exiting. rc = %d\n", __func__, rc); + return rc; +} + +static int32_t mt9p012_set_lc(void) +{ + int32_t rc; + + rc = mt9p012_i2c_write_w_table(mt9p012_regs.lctbl, mt9p012_regs.lctbl_size); + if (rc < 0) + return rc; + + rc = mt9p012_i2c_write_w_table(mt9p012_regs.rftbl, mt9p012_regs.rftbl_size); + + return rc; +} + +static void mt9p012_get_pict_fps(uint16_t fps, uint16_t *pfps) +{ + /* input fps is preview fps in Q8 format */ + uint32_t divider; /*Q10 */ + uint32_t pclk_mult; /*Q10 */ + + if (mt9p012_ctrl->prev_res == QTR_SIZE) { + divider = (uint32_t) + (((mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines * + mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck) * 0x00000400) / + (mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines * + mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck)); + + pclk_mult = + (uint32_t) ((mt9p012_regs.reg_pat[RES_CAPTURE].pll_multiplier * + 0x00000400) / (mt9p012_regs.reg_pat[RES_PREVIEW].pll_multiplier)); + } else { + /* full size resolution used for preview. */ + divider = 0x00000400; /*1.0 */ + pclk_mult = 0x00000400; /*1.0 */ + } + + /* Verify PCLK settings and frame sizes. */ + *pfps = (uint16_t) (fps * divider * pclk_mult / 0x00000400 / + 0x00000400); +} + +static uint16_t mt9p012_get_prev_lines_pf(void) +{ + if (mt9p012_ctrl->prev_res == QTR_SIZE) + return mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines; + else + return mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines; +} + +static uint16_t mt9p012_get_prev_pixels_pl(void) +{ + if (mt9p012_ctrl->prev_res == QTR_SIZE) + return mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck; + else + return mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck; +} + +static uint16_t mt9p012_get_pict_lines_pf(void) +{ + return mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines; +} + +static uint16_t mt9p012_get_pict_pixels_pl(void) +{ + return mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck; +} + +static uint32_t mt9p012_get_pict_max_exp_lc(void) +{ + uint16_t snapshot_lines_per_frame; + + if (mt9p012_ctrl->pict_res == QTR_SIZE) + snapshot_lines_per_frame = + mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1; + else + snapshot_lines_per_frame = + mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1; + + return snapshot_lines_per_frame * 24; +} + +static int32_t mt9p012_set_fps(struct fps_cfg *fps) +{ + /* input is new fps in Q10 format */ + int32_t rc = 0; + + mt9p012_ctrl->fps_divider = fps->fps_div; + mt9p012_ctrl->pict_fps_divider = fps->pict_fps_div; + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return -EBUSY; + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + REG_LINE_LENGTH_PCK, + (mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck * + fps->f_mult / 0x00000400)); + if (rc < 0) + return rc; + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + + return rc; +} + +static int32_t mt9p012_write_exp_gain(uint16_t gain, uint32_t line) +{ + uint16_t max_legal_gain = 0x01FF; + uint32_t line_length_ratio = 0x00000400; + enum mt9p012_setting setting; + int32_t rc = 0; + + CDBG("Line:%d mt9p012_write_exp_gain \n", __LINE__); + + if (mt9p012_ctrl->sensormode == SENSOR_PREVIEW_MODE) { + mt9p012_ctrl->my_reg_gain = gain; + mt9p012_ctrl->my_reg_line_count = (uint16_t)line; + } + + if (gain > max_legal_gain) { + CDBG("Max legal gain Line:%d \n", __LINE__); + gain = max_legal_gain; + } + + /* Verify no overflow */ + if (mt9p012_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) { + line = (uint32_t)(line * mt9p012_ctrl->fps_divider / + 0x00000400); + setting = RES_PREVIEW; + } else { + line = (uint32_t)(line * mt9p012_ctrl->pict_fps_divider / + 0x00000400); + setting = RES_CAPTURE; + } + + /* Set digital gain to 1 */ +#ifdef MT9P012_REV_7 + gain |= 0x1000; +#else + gain |= 0x0200; +#endif + + if ((mt9p012_regs.reg_pat[setting].frame_length_lines - 1) < line) { + line_length_ratio = (uint32_t) (line * 0x00000400) / + (mt9p012_regs.reg_pat[setting].frame_length_lines - 1); + } else + line_length_ratio = 0x00000400; + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) { + CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__); + return rc; + } + + rc = + mt9p012_i2c_write_w( + mt9p012_client->addr, + REG_GLOBAL_GAIN, gain); + if (rc < 0) { + CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__); + return rc; + } + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + REG_COARSE_INT_TIME, + line); + if (rc < 0) { + CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__); + return rc; + } + + CDBG("mt9p012_write_exp_gain: gain = %d, line = %d\n", gain, line); + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__); + + return rc; +} + +static int32_t mt9p012_set_pict_exp_gain(uint16_t gain, uint32_t line) +{ + int32_t rc = 0; + + CDBG("Line:%d mt9p012_set_pict_exp_gain \n", __LINE__); + + rc = + mt9p012_write_exp_gain(gain, line); + if (rc < 0) { + CDBG("Line:%d mt9p012_set_pict_exp_gain failed... \n", + __LINE__); + return rc; + } + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + MT9P012_REG_RESET_REGISTER, + 0x10CC | 0x0002); + if (rc < 0) { + CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__); + return rc; + } + + mdelay(5); + + /* camera_timed_wait(snapshot_wait*exposure_ratio); */ + return rc; +} + +static int32_t mt9p012_setting(enum mt9p012_reg_update rupdate, + enum mt9p012_setting rt) +{ + int32_t rc = 0; + + switch (rupdate) { + case UPDATE_PERIODIC: + if (rt == RES_PREVIEW || rt == RES_CAPTURE) { + + struct mt9p012_i2c_reg_conf ppc_tbl[] = { + {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD}, + {REG_ROW_SPEED, mt9p012_regs.reg_pat[rt].row_speed}, + {REG_X_ADDR_START, mt9p012_regs.reg_pat[rt].x_addr_start}, + {REG_X_ADDR_END, mt9p012_regs.reg_pat[rt].x_addr_end}, + {REG_Y_ADDR_START, mt9p012_regs.reg_pat[rt].y_addr_start}, + {REG_Y_ADDR_END, mt9p012_regs.reg_pat[rt].y_addr_end}, + {REG_READ_MODE, mt9p012_regs.reg_pat[rt].read_mode}, + {REG_SCALE_M, mt9p012_regs.reg_pat[rt].scale_m}, + {REG_X_OUTPUT_SIZE, mt9p012_regs.reg_pat[rt].x_output_size}, + {REG_Y_OUTPUT_SIZE, mt9p012_regs.reg_pat[rt].y_output_size}, + + {REG_LINE_LENGTH_PCK, mt9p012_regs.reg_pat[rt].line_length_pck}, + {REG_FRAME_LENGTH_LINES, + (mt9p012_regs.reg_pat[rt].frame_length_lines * + mt9p012_ctrl->fps_divider / 0x00000400)}, + {REG_COARSE_INT_TIME, mt9p012_regs.reg_pat[rt].coarse_int_time}, + {REG_FINE_INTEGRATION_TIME, mt9p012_regs.reg_pat[rt].fine_int_time}, + {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE}, + }; + + rc = mt9p012_i2c_write_w_table(&ppc_tbl[0], + ARRAY_SIZE(ppc_tbl)); + if (rc < 0) + return rc; + + rc = mt9p012_test(mt9p012_ctrl->set_test); + if (rc < 0) + return rc; + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + MT9P012_REG_RESET_REGISTER, + MT9P012_RESET_REGISTER_PWON | 0x0002); + if (rc < 0) + return rc; + + mdelay(5); /* 15? wait for sensor to transition*/ + + return rc; + } + break; /* UPDATE_PERIODIC */ + + case REG_INIT: + if (rt == RES_PREVIEW || rt == RES_CAPTURE) { + struct mt9p012_i2c_reg_conf ipc_tbl1[] = { + {MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWOFF}, + {REG_VT_PIX_CLK_DIV, mt9p012_regs.reg_pat[rt].vt_pix_clk_div}, + {REG_VT_SYS_CLK_DIV, mt9p012_regs.reg_pat[rt].vt_sys_clk_div}, + {REG_PRE_PLL_CLK_DIV, mt9p012_regs.reg_pat[rt].pre_pll_clk_div}, + {REG_PLL_MULTIPLIER, mt9p012_regs.reg_pat[rt].pll_multiplier}, + {REG_OP_PIX_CLK_DIV, mt9p012_regs.reg_pat[rt].op_pix_clk_div}, + {REG_OP_SYS_CLK_DIV, mt9p012_regs.reg_pat[rt].op_sys_clk_div}, +#ifdef MT9P012_REV_7 + {0x30B0, 0x0001}, + {0x308E, 0xE060}, + {0x3092, 0x0A52}, + {0x3094, 0x4656}, + {0x3096, 0x5652}, + {0x30CA, 0x8006}, + {0x312A, 0xDD02}, + {0x312C, 0x00E4}, + {0x3170, 0x299A}, +#endif + /* optimized settings for noise */ + {0x3088, 0x6FF6}, + {0x3154, 0x0282}, + {0x3156, 0x0381}, + {0x3162, 0x04CE}, + {0x0204, 0x0010}, + {0x0206, 0x0010}, + {0x0208, 0x0010}, + {0x020A, 0x0010}, + {0x020C, 0x0010}, + {MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWON}, + }; + + struct mt9p012_i2c_reg_conf ipc_tbl2[] = { + {MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWOFF}, + {REG_VT_PIX_CLK_DIV, mt9p012_regs.reg_pat[rt].vt_pix_clk_div}, + {REG_VT_SYS_CLK_DIV, mt9p012_regs.reg_pat[rt].vt_sys_clk_div}, + {REG_PRE_PLL_CLK_DIV, mt9p012_regs.reg_pat[rt].pre_pll_clk_div}, + {REG_PLL_MULTIPLIER, mt9p012_regs.reg_pat[rt].pll_multiplier}, + {REG_OP_PIX_CLK_DIV, mt9p012_regs.reg_pat[rt].op_pix_clk_div}, + {REG_OP_SYS_CLK_DIV, mt9p012_regs.reg_pat[rt].op_sys_clk_div}, +#ifdef MT9P012_REV_7 + {0x30B0, 0x0001}, + {0x308E, 0xE060}, + {0x3092, 0x0A52}, + {0x3094, 0x4656}, + {0x3096, 0x5652}, + {0x30CA, 0x8006}, + {0x312A, 0xDD02}, + {0x312C, 0x00E4}, + {0x3170, 0x299A}, +#endif + /* optimized settings for noise */ + {0x3088, 0x6FF6}, + {0x3154, 0x0282}, + {0x3156, 0x0381}, + {0x3162, 0x04CE}, + {0x0204, 0x0010}, + {0x0206, 0x0010}, + {0x0208, 0x0010}, + {0x020A, 0x0010}, + {0x020C, 0x0010}, + {MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWON}, + }; + + struct mt9p012_i2c_reg_conf ipc_tbl3[] = { + {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD}, + /* Set preview or snapshot mode */ + {REG_ROW_SPEED, mt9p012_regs.reg_pat[rt].row_speed}, + {REG_X_ADDR_START, mt9p012_regs.reg_pat[rt].x_addr_start}, + {REG_X_ADDR_END, mt9p012_regs.reg_pat[rt].x_addr_end}, + {REG_Y_ADDR_START, mt9p012_regs.reg_pat[rt].y_addr_start}, + {REG_Y_ADDR_END, mt9p012_regs.reg_pat[rt].y_addr_end}, + {REG_READ_MODE, mt9p012_regs.reg_pat[rt].read_mode}, + {REG_SCALE_M, mt9p012_regs.reg_pat[rt].scale_m}, + {REG_X_OUTPUT_SIZE, mt9p012_regs.reg_pat[rt].x_output_size}, + {REG_Y_OUTPUT_SIZE, mt9p012_regs.reg_pat[rt].y_output_size}, + {REG_LINE_LENGTH_PCK, mt9p012_regs.reg_pat[rt].line_length_pck}, + {REG_FRAME_LENGTH_LINES, + mt9p012_regs.reg_pat[rt].frame_length_lines}, + {REG_COARSE_INT_TIME, mt9p012_regs.reg_pat[rt].coarse_int_time}, + {REG_FINE_INTEGRATION_TIME, mt9p012_regs.reg_pat[rt].fine_int_time}, + {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE}, + }; + + /* reset fps_divider */ + mt9p012_ctrl->fps_divider = 1 * 0x0400; + + rc = mt9p012_i2c_write_w_table(&ipc_tbl1[0], + ARRAY_SIZE(ipc_tbl1)); + if (rc < 0) + return rc; + + rc = mt9p012_i2c_write_w_table(&ipc_tbl2[0], + ARRAY_SIZE(ipc_tbl2)); + if (rc < 0) + return rc; + + mdelay(5); + + rc = mt9p012_i2c_write_w_table(&ipc_tbl3[0], + ARRAY_SIZE(ipc_tbl3)); + if (rc < 0) + return rc; + + /* load lens shading */ + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + rc = mt9p012_set_lc(); + if (rc < 0) + return rc; + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE); + + if (rc < 0) + return rc; + } + break; /* case REG_INIT: */ + + default: + rc = -EINVAL; + break; + } /* switch (rupdate) */ + + return rc; +} + +static int32_t mt9p012_video_config(int mode, int res) +{ + int32_t rc; + + switch (res) { + case QTR_SIZE: + rc = mt9p012_setting(UPDATE_PERIODIC, RES_PREVIEW); + if (rc < 0) + return rc; + + CDBG("mt9p012 sensor configuration done!\n"); + break; + + case FULL_SIZE: + rc = + mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE); + if (rc < 0) + return rc; + + break; + + default: + return 0; + } /* switch */ + + mt9p012_ctrl->prev_res = res; + mt9p012_ctrl->curr_res = res; + mt9p012_ctrl->sensormode = mode; + + rc = + mt9p012_write_exp_gain(mt9p012_ctrl->my_reg_gain, + mt9p012_ctrl->my_reg_line_count); + + rc = + mt9p012_i2c_write_w(mt9p012_client->addr, + MT9P012_REG_RESET_REGISTER, + 0x10cc|0x0002); + + return rc; +} + +static int32_t mt9p012_snapshot_config(int mode) +{ + int32_t rc = 0; + + rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE); + if (rc < 0) + return rc; + + mt9p012_ctrl->curr_res = mt9p012_ctrl->pict_res; + + mt9p012_ctrl->sensormode = mode; + + return rc; +} + +static int32_t mt9p012_raw_snapshot_config(int mode) +{ + int32_t rc = 0; + + rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE); + if (rc < 0) + return rc; + + mt9p012_ctrl->curr_res = mt9p012_ctrl->pict_res; + + mt9p012_ctrl->sensormode = mode; + + return rc; +} + +static int32_t mt9p012_power_down(void) +{ + int32_t rc = 0; + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + MT9P012_REG_RESET_REGISTER, + MT9P012_RESET_REGISTER_PWOFF); + + mdelay(5); + return rc; +} + +static int32_t mt9p012_move_focus(int direction, int32_t num_steps) +{ + int16_t step_direction; + int16_t actual_step; + int16_t next_position; + uint8_t code_val_msb, code_val_lsb; + + if (num_steps > MT9P012_TOTAL_STEPS_NEAR_TO_FAR) + num_steps = MT9P012_TOTAL_STEPS_NEAR_TO_FAR; + else if (num_steps == 0) { + CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__); + return -EINVAL; + } + + if (direction == MOVE_NEAR) + step_direction = 16; /* 10bit */ + else if (direction == MOVE_FAR) + step_direction = -16; /* 10 bit */ + else { + CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__); + return -EINVAL; + } + + if (mt9p012_ctrl->curr_lens_pos < mt9p012_ctrl->init_curr_lens_pos) + mt9p012_ctrl->curr_lens_pos = + mt9p012_ctrl->init_curr_lens_pos; + + actual_step = (int16_t)(step_direction * (int16_t)num_steps); + next_position = (int16_t)(mt9p012_ctrl->curr_lens_pos + actual_step); + + if (next_position > 1023) + next_position = 1023; + else if (next_position < 0) + next_position = 0; + + code_val_msb = next_position >> 4; + code_val_lsb = (next_position & 0x000F) << 4; + /* code_val_lsb |= mode_mask; */ + + /* Writing the digital code for current to the actuator */ + if (mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, + code_val_msb, code_val_lsb) < 0) { + CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__); + return -EBUSY; + } + + /* Storing the current lens Position */ + mt9p012_ctrl->curr_lens_pos = next_position; + + return 0; +} + +static int32_t mt9p012_set_default_focus(void) +{ + int32_t rc = 0; + uint8_t code_val_msb, code_val_lsb; + + code_val_msb = 0x00; + code_val_lsb = 0x00; + + /* Write the digital code for current to the actuator */ + rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, + code_val_msb, code_val_lsb); + + mt9p012_ctrl->curr_lens_pos = 0; + mt9p012_ctrl->init_curr_lens_pos = 0; + + return rc; +} + +static int mt9p012_probe_init_done(const struct msm_camera_sensor_info *data) +{ + gpio_direction_output(data->sensor_reset, 0); + gpio_free(data->sensor_reset); + return 0; +} + +static int mt9p012_probe_init_sensor(const struct msm_camera_sensor_info *data) +{ + int32_t rc; + uint16_t chipid; + + rc = gpio_request(data->sensor_reset, "mt9p012"); + if (!rc) + gpio_direction_output(data->sensor_reset, 1); + else + goto init_probe_done; + + mdelay(20); + + /* RESET the sensor image part via I2C command */ + CDBG("mt9p012_sensor_init(): reseting sensor.\n"); + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + MT9P012_REG_RESET_REGISTER, 0x10CC|0x0001); + if (rc < 0) { + CDBG("sensor reset failed. rc = %d\n", rc); + goto init_probe_fail; + } + + mdelay(MT9P012_RESET_DELAY_MSECS); + + /* 3. Read sensor Model ID: */ + rc = mt9p012_i2c_read_w(mt9p012_client->addr, + MT9P012_REG_MODEL_ID, &chipid); + if (rc < 0) + goto init_probe_fail; + + /* 4. Compare sensor ID to MT9T012VC ID: */ + if (chipid != MT9P012_MODEL_ID) { + CDBG("mt9p012 wrong model_id = 0x%x\n", chipid); + rc = -ENODEV; + goto init_probe_fail; + } + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x306E, 0x9000); + if (rc < 0) { + CDBG("REV_7 write failed. rc = %d\n", rc); + goto init_probe_fail; + } + + /* RESET_REGISTER, enable parallel interface and disable serialiser */ + CDBG("mt9p012_sensor_init(): enabling parallel interface.\n"); + rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x301A, 0x10CC); + if (rc < 0) { + CDBG("enable parallel interface failed. rc = %d\n", rc); + goto init_probe_fail; + } + + /* To disable the 2 extra lines */ + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + 0x3064, 0x0805); + + if (rc < 0) { + CDBG("disable the 2 extra lines failed. rc = %d\n", rc); + goto init_probe_fail; + } + + mdelay(MT9P012_RESET_DELAY_MSECS); + goto init_probe_done; + +init_probe_fail: + mt9p012_probe_init_done(data); +init_probe_done: + return rc; +} + +static int mt9p012_sensor_open_init(const struct msm_camera_sensor_info *data) +{ + int32_t rc; + + mt9p012_ctrl = kzalloc(sizeof(struct mt9p012_ctrl), GFP_KERNEL); + if (!mt9p012_ctrl) { + CDBG("mt9p012_init failed!\n"); + rc = -ENOMEM; + goto init_done; + } + + mt9p012_ctrl->fps_divider = 1 * 0x00000400; + mt9p012_ctrl->pict_fps_divider = 1 * 0x00000400; + mt9p012_ctrl->set_test = TEST_OFF; + mt9p012_ctrl->prev_res = QTR_SIZE; + mt9p012_ctrl->pict_res = FULL_SIZE; + + if (data) + mt9p012_ctrl->sensordata = data; + + /* enable mclk first */ + msm_camio_clk_rate_set(MT9P012_DEFAULT_CLOCK_RATE); + mdelay(20); + + msm_camio_camif_pad_reg_reset(); + mdelay(20); + + rc = mt9p012_probe_init_sensor(data); + if (rc < 0) + goto init_fail1; + + if (mt9p012_ctrl->prev_res == QTR_SIZE) + rc = mt9p012_setting(REG_INIT, RES_PREVIEW); + else + rc = mt9p012_setting(REG_INIT, RES_CAPTURE); + + if (rc < 0) { + CDBG("mt9p012_setting failed. rc = %d\n", rc); + goto init_fail1; + } + + /* sensor : output enable */ + CDBG("mt9p012_sensor_open_init(): enabling output.\n"); + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWON); + if (rc < 0) { + CDBG("sensor output enable failed. rc = %d\n", rc); + goto init_fail1; + } + + /* TODO: enable AF actuator */ +#if 0 + CDBG("enable AF actuator, gpio = %d\n", + mt9p012_ctrl->sensordata->vcm_pwd); + rc = gpio_request(mt9p012_ctrl->sensordata->vcm_pwd, "mt9p012"); + if (!rc) + gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd, 1); + else { + CDBG("mt9p012_ctrl gpio request failed!\n"); + goto init_fail1; + } + mdelay(20); + + rc = mt9p012_set_default_focus(); +#endif + if (rc >= 0) + goto init_done; + + /* TODO: + * gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd, 0); + * gpio_free(mt9p012_ctrl->sensordata->vcm_pwd); */ +init_fail1: + mt9p012_probe_init_done(data); + kfree(mt9p012_ctrl); +init_done: + return rc; +} + +static int mt9p012_init_client(struct i2c_client *client) +{ + /* Initialize the MSM_CAMI2C Chip */ + init_waitqueue_head(&mt9p012_wait_queue); + return 0; +} + +static int32_t mt9p012_set_sensor_mode(int mode, int res) +{ + int32_t rc = 0; + + switch (mode) { + case SENSOR_PREVIEW_MODE: + rc = mt9p012_video_config(mode, res); + break; + + case SENSOR_SNAPSHOT_MODE: + rc = mt9p012_snapshot_config(mode); + break; + + case SENSOR_RAW_SNAPSHOT_MODE: + rc = mt9p012_raw_snapshot_config(mode); + break; + + default: + rc = -EINVAL; + break; + } + + return rc; +} + +int mt9p012_sensor_config(void __user *argp) +{ + struct sensor_cfg_data cdata; + int rc = 0; + + if (copy_from_user(&cdata, + (void *)argp, + sizeof(struct sensor_cfg_data))) + return -EFAULT; + + down(&mt9p012_sem); + + CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype); + switch (cdata.cfgtype) { + case CFG_GET_PICT_FPS: + mt9p012_get_pict_fps(cdata.cfg.gfps.prevfps, + &(cdata.cfg.gfps.pictfps)); + + if (copy_to_user((void *)argp, &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_L_PF: + cdata.cfg.prevl_pf = mt9p012_get_prev_lines_pf(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_P_PL: + cdata.cfg.prevp_pl = mt9p012_get_prev_pixels_pl(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_L_PF: + cdata.cfg.pictl_pf = mt9p012_get_pict_lines_pf(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_P_PL: + cdata.cfg.pictp_pl = mt9p012_get_pict_pixels_pl(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_MAX_EXP_LC: + cdata.cfg.pict_max_exp_lc = + mt9p012_get_pict_max_exp_lc(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_SET_FPS: + case CFG_SET_PICT_FPS: + rc = mt9p012_set_fps(&(cdata.cfg.fps)); + break; + + case CFG_SET_EXP_GAIN: + rc = mt9p012_write_exp_gain(cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_PICT_EXP_GAIN: + CDBG("Line:%d CFG_SET_PICT_EXP_GAIN \n", __LINE__); + rc = mt9p012_set_pict_exp_gain(cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_MODE: + rc = mt9p012_set_sensor_mode(cdata.mode, cdata.rs); + break; + + case CFG_PWR_DOWN: + rc = mt9p012_power_down(); + break; + + case CFG_MOVE_FOCUS: + CDBG("mt9p012_ioctl: CFG_MOVE_FOCUS: cdata.cfg.focus.dir=%d cdata.cfg.focus.steps=%d\n", + cdata.cfg.focus.dir, cdata.cfg.focus.steps); + rc = mt9p012_move_focus(cdata.cfg.focus.dir, + cdata.cfg.focus.steps); + break; + + case CFG_SET_DEFAULT_FOCUS: + rc = mt9p012_set_default_focus(); + break; + + case CFG_SET_LENS_SHADING: + CDBG("%s: CFG_SET_LENS_SHADING\n", __func__); + rc = mt9p012_lens_shading_enable(cdata.cfg.lens_shading); + break; + + case CFG_GET_AF_MAX_STEPS: + cdata.max_steps = MT9P012_STEPS_NEAR_TO_CLOSEST_INF; + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_SET_EFFECT: + default: + rc = -EINVAL; + break; + } + + up(&mt9p012_sem); + return rc; +} + +int mt9p012_sensor_release(void) +{ + int rc = -EBADF; + + down(&mt9p012_sem); + + mt9p012_power_down(); + + gpio_direction_output(mt9p012_ctrl->sensordata->sensor_reset, + 0); + gpio_free(mt9p012_ctrl->sensordata->sensor_reset); + + gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd, 0); + gpio_free(mt9p012_ctrl->sensordata->vcm_pwd); + + kfree(mt9p012_ctrl); + mt9p012_ctrl = NULL; + + CDBG("mt9p012_release completed\n"); + + up(&mt9p012_sem); + return rc; +} + +static int mt9p012_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + CDBG("mt9p012_probe called!\n"); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + CDBG("i2c_check_functionality failed\n"); + goto probe_failure; + } + + mt9p012_sensorw = kzalloc(sizeof(struct mt9p012_work), GFP_KERNEL); + if (!mt9p012_sensorw) { + CDBG("kzalloc failed.\n"); + rc = -ENOMEM; + goto probe_failure; + } + + i2c_set_clientdata(client, mt9p012_sensorw); + mt9p012_init_client(client); + mt9p012_client = client; + + mdelay(50); + + CDBG("mt9p012_probe successed! rc = %d\n", rc); + return 0; + +probe_failure: + CDBG("mt9p012_probe failed! rc = %d\n", rc); + return rc; +} + +static const struct i2c_device_id mt9p012_i2c_id[] = { + { "mt9p012", 0}, + { } +}; + +static struct i2c_driver mt9p012_i2c_driver = { + .id_table = mt9p012_i2c_id, + .probe = mt9p012_i2c_probe, + .remove = __exit_p(mt9p012_i2c_remove), + .driver = { + .name = "mt9p012", + }, +}; + +static int mt9p012_sensor_probe(const struct msm_camera_sensor_info *info, + struct msm_sensor_ctrl *s) +{ + int rc = i2c_add_driver(&mt9p012_i2c_driver); + if (rc < 0 || mt9p012_client == NULL) { + rc = -ENOTSUPP; + goto probe_done; + } + + msm_camio_clk_rate_set(MT9P012_DEFAULT_CLOCK_RATE); + mdelay(20); + + rc = mt9p012_probe_init_sensor(info); + if (rc < 0) + goto probe_done; + + s->s_init = mt9p012_sensor_open_init; + s->s_release = mt9p012_sensor_release; + s->s_config = mt9p012_sensor_config; + mt9p012_probe_init_done(info); + +probe_done: + CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__); + return rc; +} + +static int __mt9p012_probe(struct platform_device *pdev) +{ + return msm_camera_drv_start(pdev, mt9p012_sensor_probe); +} + +static struct platform_driver msm_camera_driver = { + .probe = __mt9p012_probe, + .driver = { + .name = "msm_camera_mt9p012", + .owner = THIS_MODULE, + }, +}; + +static int __init mt9p012_init(void) +{ + return platform_driver_register(&msm_camera_driver); +} + +module_init(mt9p012_init); diff --git a/drivers/media/video/msm/mt9p012_reg.c b/drivers/media/video/msm/mt9p012_reg.c new file mode 100644 index 0000000000000..e5223d693b2c2 --- /dev/null +++ b/drivers/media/video/msm/mt9p012_reg.c @@ -0,0 +1,573 @@ +/* + * Copyright (C) 2009 QUALCOMM Incorporated. + */ + +#include "mt9p012.h" +#include + +/*Micron settings from Applications for lower power consumption.*/ +struct reg_struct mt9p012_reg_pat[2] = { + { /* Preview */ + /* vt_pix_clk_div REG=0x0300 */ + 6, /* 5 */ + + /* vt_sys_clk_div REG=0x0302 */ + 1, + + /* pre_pll_clk_div REG=0x0304 */ + 2, + + /* pll_multiplier REG=0x0306 */ + 60, + + /* op_pix_clk_div REG=0x0308 */ + 8, /* 10 */ + + /* op_sys_clk_div REG=0x030A */ + 1, + + /* scale_m REG=0x0404 */ + 16, + + /* row_speed REG=0x3016 */ + 0x0111, + + /* x_addr_start REG=0x3004 */ + 8, + + /* x_addr_end REG=0x3008 */ + 2597, + + /* y_addr_start REG=0x3002 */ + 8, + + /* y_addr_end REG=0x3006 */ + 1949, + + /* read_mode REG=0x3040 + * Preview 2x2 skipping */ + 0x00C3, + + /* x_output_size REG=0x034C */ + 1296, + + /* y_output_size REG=0x034E */ + 972, + + /* line_length_pck REG=0x300C */ + 3784, + + /* frame_length_lines REG=0x300A */ + 1057, + + /* coarse_integration_time REG=0x3012 */ + 16, + + /* fine_integration_time REG=0x3014 */ + 1764 + }, + { /*Snapshot*/ + /* vt_pix_clk_div REG=0x0300 */ + 6, + + /* vt_sys_clk_div REG=0x0302 */ + 1, + + /* pre_pll_clk_div REG=0x0304 */ + 2, + + /* pll_multiplier REG=0x0306 + * 60 for 10fps snapshot */ + 60, + + /* op_pix_clk_div REG=0x0308 */ + 8, + + /* op_sys_clk_div REG=0x030A */ + 1, + + /* scale_m REG=0x0404 */ + 16, + + /* row_speed REG=0x3016 */ + 0x0111, + + /* x_addr_start REG=0x3004 */ + 8, + + /* x_addr_end REG=0x3008 */ + 2615, + + /* y_addr_start REG=0x3002 */ + 8, + + /* y_addr_end REG=0x3006 */ + 1967, + + /* read_mode REG=0x3040 */ + 0x0041, + + /* x_output_size REG=0x034C */ + 2608, + + /* y_output_size REG=0x034E */ + 1960, + + /* line_length_pck REG=0x300C */ + 3911, + + /* frame_length_lines REG=0x300A //10 fps snapshot */ + 2045, + + /* coarse_integration_time REG=0x3012 */ + 16, + + /* fine_integration_time REG=0x3014 */ + 882 + } +}; + + +struct mt9p012_i2c_reg_conf mt9p012_test_tbl[] = { + {0x3044, 0x0544 & 0xFBFF}, + {0x30CA, 0x0004 | 0x0001}, + {0x30D4, 0x9020 & 0x7FFF}, + {0x31E0, 0x0003 & 0xFFFE}, + {0x3180, 0x91FF & 0x7FFF}, + {0x301A, (0x10CC | 0x8000) & 0xFFF7}, + {0x301E, 0x0000}, + {0x3780, 0x0000}, +}; + + +struct mt9p012_i2c_reg_conf mt9p012_lc_tbl[] = { + /* [Lens shading 85 Percent TL84] */ + /* P_RD_P0Q0 */ + {0x360A, 0x7FEF}, + /* P_RD_P0Q1 */ + {0x360C, 0x232C}, + /* P_RD_P0Q2 */ + {0x360E, 0x7050}, + /* P_RD_P0Q3 */ + {0x3610, 0xF3CC}, + /* P_RD_P0Q4 */ + {0x3612, 0x89D1}, + /* P_RD_P1Q0 */ + {0x364A, 0xBE0D}, + /* P_RD_P1Q1 */ + {0x364C, 0x9ACB}, + /* P_RD_P1Q2 */ + {0x364E, 0x2150}, + /* P_RD_P1Q3 */ + {0x3650, 0xB26B}, + /* P_RD_P1Q4 */ + {0x3652, 0x9511}, + /* P_RD_P2Q0 */ + {0x368A, 0x2151}, + /* P_RD_P2Q1 */ + {0x368C, 0x00AD}, + /* P_RD_P2Q2 */ + {0x368E, 0x8334}, + /* P_RD_P2Q3 */ + {0x3690, 0x478E}, + /* P_RD_P2Q4 */ + {0x3692, 0x0515}, + /* P_RD_P3Q0 */ + {0x36CA, 0x0710}, + /* P_RD_P3Q1 */ + {0x36CC, 0x452D}, + /* P_RD_P3Q2 */ + {0x36CE, 0xF352}, + /* P_RD_P3Q3 */ + {0x36D0, 0x190F}, + /* P_RD_P3Q4 */ + {0x36D2, 0x4413}, + /* P_RD_P4Q0 */ + {0x370A, 0xD112}, + /* P_RD_P4Q1 */ + {0x370C, 0xF50F}, + /* P_RD_P4Q2 */ + {0x370C, 0xF50F}, + /* P_RD_P4Q3 */ + {0x3710, 0xDC11}, + /* P_RD_P4Q4 */ + {0x3712, 0xD776}, + /* P_GR_P0Q0 */ + {0x3600, 0x1750}, + /* P_GR_P0Q1 */ + {0x3602, 0xF0AC}, + /* P_GR_P0Q2 */ + {0x3604, 0x4711}, + /* P_GR_P0Q3 */ + {0x3606, 0x07CE}, + /* P_GR_P0Q4 */ + {0x3608, 0x96B2}, + /* P_GR_P1Q0 */ + {0x3640, 0xA9AE}, + /* P_GR_P1Q1 */ + {0x3642, 0xF9AC}, + /* P_GR_P1Q2 */ + {0x3644, 0x39F1}, + /* P_GR_P1Q3 */ + {0x3646, 0x016F}, + /* P_GR_P1Q4 */ + {0x3648, 0x8AB2}, + /* P_GR_P2Q0 */ + {0x3680, 0x1752}, + /* P_GR_P2Q1 */ + {0x3682, 0x70F0}, + /* P_GR_P2Q2 */ + {0x3684, 0x83F5}, + /* P_GR_P2Q3 */ + {0x3686, 0x8392}, + /* P_GR_P2Q4 */ + {0x3688, 0x1FD6}, + /* P_GR_P3Q0 */ + {0x36C0, 0x1131}, + /* P_GR_P3Q1 */ + {0x36C2, 0x3DAF}, + /* P_GR_P3Q2 */ + {0x36C4, 0x89B4}, + /* P_GR_P3Q3 */ + {0x36C6, 0xA391}, + /* P_GR_P3Q4 */ + {0x36C8, 0x1334}, + /* P_GR_P4Q0 */ + {0x3700, 0xDC13}, + /* P_GR_P4Q1 */ + {0x3702, 0xD052}, + /* P_GR_P4Q2 */ + {0x3704, 0x5156}, + /* P_GR_P4Q3 */ + {0x3706, 0x1F13}, + /* P_GR_P4Q4 */ + {0x3708, 0x8C38}, + /* P_BL_P0Q0 */ + {0x3614, 0x0050}, + /* P_BL_P0Q1 */ + {0x3616, 0xBD4C}, + /* P_BL_P0Q2 */ + {0x3618, 0x41B0}, + /* P_BL_P0Q3 */ + {0x361A, 0x660D}, + /* P_BL_P0Q4 */ + {0x361C, 0xC590}, + /* P_BL_P1Q0 */ + {0x3654, 0x87EC}, + /* P_BL_P1Q1 */ + {0x3656, 0xE44C}, + /* P_BL_P1Q2 */ + {0x3658, 0x302E}, + /* P_BL_P1Q3 */ + {0x365A, 0x106E}, + /* P_BL_P1Q4 */ + {0x365C, 0xB58E}, + /* P_BL_P2Q0 */ + {0x3694, 0x0DD1}, + /* P_BL_P2Q1 */ + {0x3696, 0x2A50}, + /* P_BL_P2Q2 */ + {0x3698, 0xC793}, + /* P_BL_P2Q3 */ + {0x369A, 0xE8F1}, + /* P_BL_P2Q4 */ + {0x369C, 0x4174}, + /* P_BL_P3Q0 */ + {0x36D4, 0x01EF}, + /* P_BL_P3Q1 */ + {0x36D6, 0x06CF}, + /* P_BL_P3Q2 */ + {0x36D8, 0x8D91}, + /* P_BL_P3Q3 */ + {0x36DA, 0x91F0}, + /* P_BL_P3Q4 */ + {0x36DC, 0x52EF}, + /* P_BL_P4Q0 */ + {0x3714, 0xA6D2}, + /* P_BL_P4Q1 */ + {0x3716, 0xA312}, + /* P_BL_P4Q2 */ + {0x3718, 0x2695}, + /* P_BL_P4Q3 */ + {0x371A, 0x3953}, + /* P_BL_P4Q4 */ + {0x371C, 0x9356}, + /* P_GB_P0Q0 */ + {0x361E, 0x7EAF}, + /* P_GB_P0Q1 */ + {0x3620, 0x2A4C}, + /* P_GB_P0Q2 */ + {0x3622, 0x49F0}, + {0x3624, 0xF1EC}, + /* P_GB_P0Q4 */ + {0x3626, 0xC670}, + /* P_GB_P1Q0 */ + {0x365E, 0x8E0C}, + /* P_GB_P1Q1 */ + {0x3660, 0xC2A9}, + /* P_GB_P1Q2 */ + {0x3662, 0x274F}, + /* P_GB_P1Q3 */ + {0x3664, 0xADAB}, + /* P_GB_P1Q4 */ + {0x3666, 0x8EF0}, + /* P_GB_P2Q0 */ + {0x369E, 0x09B1}, + /* P_GB_P2Q1 */ + {0x36A0, 0xAA2E}, + /* P_GB_P2Q2 */ + {0x36A2, 0xC3D3}, + /* P_GB_P2Q3 */ + {0x36A4, 0x7FAF}, + /* P_GB_P2Q4 */ + {0x36A6, 0x3F34}, + /* P_GB_P3Q0 */ + {0x36DE, 0x4C8F}, + /* P_GB_P3Q1 */ + {0x36E0, 0x886E}, + /* P_GB_P3Q2 */ + {0x36E2, 0xE831}, + /* P_GB_P3Q3 */ + {0x36E4, 0x1FD0}, + /* P_GB_P3Q4 */ + {0x36E6, 0x1192}, + /* P_GB_P4Q0 */ + {0x371E, 0xB952}, + /* P_GB_P4Q1 */ + {0x3720, 0x6DCF}, + /* P_GB_P4Q2 */ + {0x3722, 0x1B55}, + /* P_GB_P4Q3 */ + {0x3724, 0xA112}, + /* P_GB_P4Q4 */ + {0x3726, 0x82F6}, + /* POLY_ORIGIN_C */ + {0x3782, 0x0510}, + /* POLY_ORIGIN_R */ + {0x3784, 0x0390}, + /* POLY_SC_ENABLE */ + {0x3780, 0x8000}, +}; + +/* rolloff table for illuminant A */ +struct mt9p012_i2c_reg_conf mt9p012_rolloff_tbl[] = { + /* P_RD_P0Q0 */ + {0x360A, 0x7FEF}, + /* P_RD_P0Q1 */ + {0x360C, 0x232C}, + /* P_RD_P0Q2 */ + {0x360E, 0x7050}, + /* P_RD_P0Q3 */ + {0x3610, 0xF3CC}, + /* P_RD_P0Q4 */ + {0x3612, 0x89D1}, + /* P_RD_P1Q0 */ + {0x364A, 0xBE0D}, + /* P_RD_P1Q1 */ + {0x364C, 0x9ACB}, + /* P_RD_P1Q2 */ + {0x364E, 0x2150}, + /* P_RD_P1Q3 */ + {0x3650, 0xB26B}, + /* P_RD_P1Q4 */ + {0x3652, 0x9511}, + /* P_RD_P2Q0 */ + {0x368A, 0x2151}, + /* P_RD_P2Q1 */ + {0x368C, 0x00AD}, + /* P_RD_P2Q2 */ + {0x368E, 0x8334}, + /* P_RD_P2Q3 */ + {0x3690, 0x478E}, + /* P_RD_P2Q4 */ + {0x3692, 0x0515}, + /* P_RD_P3Q0 */ + {0x36CA, 0x0710}, + /* P_RD_P3Q1 */ + {0x36CC, 0x452D}, + /* P_RD_P3Q2 */ + {0x36CE, 0xF352}, + /* P_RD_P3Q3 */ + {0x36D0, 0x190F}, + /* P_RD_P3Q4 */ + {0x36D2, 0x4413}, + /* P_RD_P4Q0 */ + {0x370A, 0xD112}, + /* P_RD_P4Q1 */ + {0x370C, 0xF50F}, + /* P_RD_P4Q2 */ + {0x370E, 0x6375}, + /* P_RD_P4Q3 */ + {0x3710, 0xDC11}, + /* P_RD_P4Q4 */ + {0x3712, 0xD776}, + /* P_GR_P0Q0 */ + {0x3600, 0x1750}, + /* P_GR_P0Q1 */ + {0x3602, 0xF0AC}, + /* P_GR_P0Q2 */ + {0x3604, 0x4711}, + /* P_GR_P0Q3 */ + {0x3606, 0x07CE}, + /* P_GR_P0Q4 */ + {0x3608, 0x96B2}, + /* P_GR_P1Q0 */ + {0x3640, 0xA9AE}, + /* P_GR_P1Q1 */ + {0x3642, 0xF9AC}, + /* P_GR_P1Q2 */ + {0x3644, 0x39F1}, + /* P_GR_P1Q3 */ + {0x3646, 0x016F}, + /* P_GR_P1Q4 */ + {0x3648, 0x8AB2}, + /* P_GR_P2Q0 */ + {0x3680, 0x1752}, + /* P_GR_P2Q1 */ + {0x3682, 0x70F0}, + /* P_GR_P2Q2 */ + {0x3684, 0x83F5}, + /* P_GR_P2Q3 */ + {0x3686, 0x8392}, + /* P_GR_P2Q4 */ + {0x3688, 0x1FD6}, + /* P_GR_P3Q0 */ + {0x36C0, 0x1131}, + /* P_GR_P3Q1 */ + {0x36C2, 0x3DAF}, + /* P_GR_P3Q2 */ + {0x36C4, 0x89B4}, + /* P_GR_P3Q3 */ + {0x36C6, 0xA391}, + /* P_GR_P3Q4 */ + {0x36C8, 0x1334}, + /* P_GR_P4Q0 */ + {0x3700, 0xDC13}, + /* P_GR_P4Q1 */ + {0x3702, 0xD052}, + /* P_GR_P4Q2 */ + {0x3704, 0x5156}, + /* P_GR_P4Q3 */ + {0x3706, 0x1F13}, + /* P_GR_P4Q4 */ + {0x3708, 0x8C38}, + /* P_BL_P0Q0 */ + {0x3614, 0x0050}, + /* P_BL_P0Q1 */ + {0x3616, 0xBD4C}, + /* P_BL_P0Q2 */ + {0x3618, 0x41B0}, + /* P_BL_P0Q3 */ + {0x361A, 0x660D}, + /* P_BL_P0Q4 */ + {0x361C, 0xC590}, + /* P_BL_P1Q0 */ + {0x3654, 0x87EC}, + /* P_BL_P1Q1 */ + {0x3656, 0xE44C}, + /* P_BL_P1Q2 */ + {0x3658, 0x302E}, + /* P_BL_P1Q3 */ + {0x365A, 0x106E}, + /* P_BL_P1Q4 */ + {0x365C, 0xB58E}, + /* P_BL_P2Q0 */ + {0x3694, 0x0DD1}, + /* P_BL_P2Q1 */ + {0x3696, 0x2A50}, + /* P_BL_P2Q2 */ + {0x3698, 0xC793}, + /* P_BL_P2Q3 */ + {0x369A, 0xE8F1}, + /* P_BL_P2Q4 */ + {0x369C, 0x4174}, + /* P_BL_P3Q0 */ + {0x36D4, 0x01EF}, + /* P_BL_P3Q1 */ + {0x36D6, 0x06CF}, + /* P_BL_P3Q2 */ + {0x36D8, 0x8D91}, + /* P_BL_P3Q3 */ + {0x36DA, 0x91F0}, + /* P_BL_P3Q4 */ + {0x36DC, 0x52EF}, + /* P_BL_P4Q0 */ + {0x3714, 0xA6D2}, + /* P_BL_P4Q1 */ + {0x3716, 0xA312}, + /* P_BL_P4Q2 */ + {0x3718, 0x2695}, + /* P_BL_P4Q3 */ + {0x371A, 0x3953}, + /* P_BL_P4Q4 */ + {0x371C, 0x9356}, + /* P_GB_P0Q0 */ + {0x361E, 0x7EAF}, + /* P_GB_P0Q1 */ + {0x3620, 0x2A4C}, + /* P_GB_P0Q2 */ + {0x3622, 0x49F0}, + {0x3624, 0xF1EC}, + /* P_GB_P0Q4 */ + {0x3626, 0xC670}, + /* P_GB_P1Q0 */ + {0x365E, 0x8E0C}, + /* P_GB_P1Q1 */ + {0x3660, 0xC2A9}, + /* P_GB_P1Q2 */ + {0x3662, 0x274F}, + /* P_GB_P1Q3 */ + {0x3664, 0xADAB}, + /* P_GB_P1Q4 */ + {0x3666, 0x8EF0}, + /* P_GB_P2Q0 */ + {0x369E, 0x09B1}, + /* P_GB_P2Q1 */ + {0x36A0, 0xAA2E}, + /* P_GB_P2Q2 */ + {0x36A2, 0xC3D3}, + /* P_GB_P2Q3 */ + {0x36A4, 0x7FAF}, + /* P_GB_P2Q4 */ + {0x36A6, 0x3F34}, + /* P_GB_P3Q0 */ + {0x36DE, 0x4C8F}, + /* P_GB_P3Q1 */ + {0x36E0, 0x886E}, + /* P_GB_P3Q2 */ + {0x36E2, 0xE831}, + /* P_GB_P3Q3 */ + {0x36E4, 0x1FD0}, + /* P_GB_P3Q4 */ + {0x36E6, 0x1192}, + /* P_GB_P4Q0 */ + {0x371E, 0xB952}, + /* P_GB_P4Q1 */ + {0x3720, 0x6DCF}, + /* P_GB_P4Q2 */ + {0x3722, 0x1B55}, + /* P_GB_P4Q3 */ + {0x3724, 0xA112}, + /* P_GB_P4Q4 */ + {0x3726, 0x82F6}, + /* POLY_ORIGIN_C */ + {0x3782, 0x0510}, + /* POLY_ORIGIN_R */ + {0x3784, 0x0390}, + /* POLY_SC_ENABLE */ + {0x3780, 0x8000}, +}; + + +struct mt9p012_reg mt9p012_regs = { + .reg_pat = &mt9p012_reg_pat[0], + .reg_pat_size = ARRAY_SIZE(mt9p012_reg_pat), + .ttbl = &mt9p012_test_tbl[0], + .ttbl_size = ARRAY_SIZE(mt9p012_test_tbl), + .lctbl = &mt9p012_lc_tbl[0], + .lctbl_size = ARRAY_SIZE(mt9p012_lc_tbl), + .rftbl = &mt9p012_rolloff_tbl[0], + .rftbl_size = ARRAY_SIZE(mt9p012_rolloff_tbl) +}; + + diff --git a/drivers/media/video/msm/mt9t013.c b/drivers/media/video/msm/mt9t013.c new file mode 100644 index 0000000000000..fa18f2f1324fa --- /dev/null +++ b/drivers/media/video/msm/mt9t013.c @@ -0,0 +1,1496 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mt9t013.h" + +/*============================================================= + SENSOR REGISTER DEFINES +==============================================================*/ +#define MT9T013_REG_MODEL_ID 0x0000 +#define MT9T013_MODEL_ID 0x2600 +#define REG_GROUPED_PARAMETER_HOLD 0x0104 +#define GROUPED_PARAMETER_HOLD 0x0100 +#define GROUPED_PARAMETER_UPDATE 0x0000 +#define REG_COARSE_INT_TIME 0x3012 +#define REG_VT_PIX_CLK_DIV 0x0300 +#define REG_VT_SYS_CLK_DIV 0x0302 +#define REG_PRE_PLL_CLK_DIV 0x0304 +#define REG_PLL_MULTIPLIER 0x0306 +#define REG_OP_PIX_CLK_DIV 0x0308 +#define REG_OP_SYS_CLK_DIV 0x030A +#define REG_SCALE_M 0x0404 +#define REG_FRAME_LENGTH_LINES 0x300A +#define REG_LINE_LENGTH_PCK 0x300C +#define REG_X_ADDR_START 0x3004 +#define REG_Y_ADDR_START 0x3002 +#define REG_X_ADDR_END 0x3008 +#define REG_Y_ADDR_END 0x3006 +#define REG_X_OUTPUT_SIZE 0x034C +#define REG_Y_OUTPUT_SIZE 0x034E +#define REG_FINE_INT_TIME 0x3014 +#define REG_ROW_SPEED 0x3016 +#define MT9T013_REG_RESET_REGISTER 0x301A +#define MT9T013_RESET_REGISTER_PWON 0x10CC +#define MT9T013_RESET_REGISTER_PWOFF 0x1008 /* 0x10C8 stop streaming*/ +#define REG_READ_MODE 0x3040 +#define REG_GLOBAL_GAIN 0x305E +#define REG_TEST_PATTERN_MODE 0x3070 + + +enum mt9t013_test_mode { + TEST_OFF, + TEST_1, + TEST_2, + TEST_3 +}; + +enum mt9t013_resolution { + QTR_SIZE, + FULL_SIZE, + INVALID_SIZE +}; + +enum mt9t013_reg_update { + REG_INIT, /* registers that need to be updated during initialization */ + UPDATE_PERIODIC, /* registers that needs periodic I2C writes */ + UPDATE_ALL, /* all registers will be updated */ + UPDATE_INVALID +}; + +enum mt9t013_setting { + RES_PREVIEW, + RES_CAPTURE +}; + +/* actuator's Slave Address */ +#define MT9T013_AF_I2C_ADDR 0x18 + +/* +* AF Total steps parameters +*/ +#define MT9T013_TOTAL_STEPS_NEAR_TO_FAR 30 + +/* + * Time in milisecs for waiting for the sensor to reset. + */ +#define MT9T013_RESET_DELAY_MSECS 66 + +/* for 30 fps preview */ +#define MT9T013_DEFAULT_CLOCK_RATE 24000000 +#define MT9T013_DEFAULT_MAX_FPS 26 + + +/* FIXME: Changes from here */ +struct mt9t013_work { + struct work_struct work; +}; + +static struct mt9t013_work *mt9t013_sensorw; +static struct i2c_client *mt9t013_client; + +struct mt9t013_ctrl { + const struct msm_camera_sensor_info *sensordata; + + int sensormode; + uint32_t fps_divider; /* init to 1 * 0x00000400 */ + uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */ + + uint16_t curr_lens_pos; + uint16_t init_curr_lens_pos; + uint16_t my_reg_gain; + uint32_t my_reg_line_count; + + enum mt9t013_resolution prev_res; + enum mt9t013_resolution pict_res; + enum mt9t013_resolution curr_res; + enum mt9t013_test_mode set_test; + + unsigned short imgaddr; +}; + + +static struct mt9t013_ctrl *mt9t013_ctrl; +static DECLARE_WAIT_QUEUE_HEAD(mt9t013_wait_queue); +DECLARE_MUTEX(mt9t013_sem); + +extern struct mt9t013_reg mt9t013_regs; /* from mt9t013_reg.c */ + +static int mt9t013_i2c_rxdata(unsigned short saddr, + unsigned char *rxdata, int length) +{ + struct i2c_msg msgs[] = { + { + .addr = saddr, + .flags = 0, + .len = 2, + .buf = rxdata, + }, + { + .addr = saddr, + .flags = I2C_M_RD, + .len = length, + .buf = rxdata, + }, + }; + + if (i2c_transfer(mt9t013_client->adapter, msgs, 2) < 0) { + pr_err("mt9t013_i2c_rxdata failed!\n"); + return -EIO; + } + + return 0; +} + +static int32_t mt9t013_i2c_read_w(unsigned short saddr, + unsigned short raddr, unsigned short *rdata) +{ + int32_t rc = 0; + unsigned char buf[4]; + + if (!rdata) + return -EIO; + + memset(buf, 0, sizeof(buf)); + + buf[0] = (raddr & 0xFF00)>>8; + buf[1] = (raddr & 0x00FF); + + rc = mt9t013_i2c_rxdata(saddr, buf, 2); + if (rc < 0) + return rc; + + *rdata = buf[0] << 8 | buf[1]; + + if (rc < 0) + pr_err("mt9t013_i2c_read failed!\n"); + + return rc; +} + +static int32_t mt9t013_i2c_txdata(unsigned short saddr, + unsigned char *txdata, int length) +{ + struct i2c_msg msg[] = { + { + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, + }, + }; + + if (i2c_transfer(mt9t013_client->adapter, msg, 1) < 0) { + pr_err("mt9t013_i2c_txdata failed\n"); + return -EIO; + } + + return 0; +} + +static int32_t mt9t013_i2c_write_b(unsigned short saddr, + unsigned short waddr, unsigned short wdata) +{ + int32_t rc = -EIO; + unsigned char buf[2]; + + memset(buf, 0, sizeof(buf)); + buf[0] = waddr; + buf[1] = wdata; + rc = mt9t013_i2c_txdata(saddr, buf, 2); + + if (rc < 0) + pr_err("i2c_write failed, addr = 0x%x, val = 0x%x!\n", + waddr, wdata); + + return rc; +} + +static int32_t mt9t013_i2c_write_w(unsigned short saddr, + unsigned short waddr, unsigned short wdata) +{ + int32_t rc = -EIO; + unsigned char buf[4]; + + memset(buf, 0, sizeof(buf)); + buf[0] = (waddr & 0xFF00)>>8; + buf[1] = (waddr & 0x00FF); + buf[2] = (wdata & 0xFF00)>>8; + buf[3] = (wdata & 0x00FF); + + rc = mt9t013_i2c_txdata(saddr, buf, 4); + + if (rc < 0) + pr_err("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n", + waddr, wdata); + + return rc; +} + +static int32_t mt9t013_i2c_write_w_table( + struct mt9t013_i2c_reg_conf *reg_conf_tbl, int num_of_items_in_table) +{ + int i; + int32_t rc = -EIO; + + for (i = 0; i < num_of_items_in_table; i++) { + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + reg_conf_tbl->waddr, reg_conf_tbl->wdata); + if (rc < 0) + break; + reg_conf_tbl++; + } + + return rc; +} + +static int32_t mt9t013_test(enum mt9t013_test_mode mo) +{ + int32_t rc = 0; + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + if (mo == TEST_OFF) + return 0; + else { + rc = mt9t013_i2c_write_w_table(mt9t013_regs.ttbl, + mt9t013_regs.ttbl_size); + if (rc < 0) + return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_TEST_PATTERN_MODE, (uint16_t)mo); + if (rc < 0) + return rc; + } + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + return rc; + + return rc; +} + +static int32_t mt9t013_set_lc(void) +{ + int32_t rc; + + rc = mt9t013_i2c_write_w_table(mt9t013_regs.lctbl, mt9t013_regs.lctbl_size); + if (rc < 0) + return rc; + + return rc; +} + +static int32_t mt9t013_set_default_focus(uint8_t af_step) +{ + int32_t rc = 0; + uint8_t code_val_msb, code_val_lsb; + code_val_msb = 0x01; + code_val_lsb = af_step; + + /* Write the digital code for current to the actuator */ + rc = mt9t013_i2c_write_b(MT9T013_AF_I2C_ADDR>>1, + code_val_msb, code_val_lsb); + + mt9t013_ctrl->curr_lens_pos = 0; + mt9t013_ctrl->init_curr_lens_pos = 0; + return rc; +} + +static void mt9t013_get_pict_fps(uint16_t fps, uint16_t *pfps) +{ + /* input fps is preview fps in Q8 format */ + uint32_t divider; /*Q10 */ + uint32_t pclk_mult; /*Q10 */ + + if (mt9t013_ctrl->prev_res == QTR_SIZE) { + divider = + (uint32_t)( + ((mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines * + mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck) * + 0x00000400) / + (mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines * + mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck)); + + pclk_mult = + (uint32_t) ((mt9t013_regs.reg_pat[RES_CAPTURE].pll_multiplier * + 0x00000400) / + (mt9t013_regs.reg_pat[RES_PREVIEW].pll_multiplier)); + + } else { + /* full size resolution used for preview. */ + divider = 0x00000400; /*1.0 */ + pclk_mult = 0x00000400; /*1.0 */ + } + + /* Verify PCLK settings and frame sizes. */ + *pfps = + (uint16_t) (fps * divider * pclk_mult / + 0x00000400 / 0x00000400); +} + +static uint16_t mt9t013_get_prev_lines_pf(void) +{ + if (mt9t013_ctrl->prev_res == QTR_SIZE) + return mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines; + else + return mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines; +} + +static uint16_t mt9t013_get_prev_pixels_pl(void) +{ + if (mt9t013_ctrl->prev_res == QTR_SIZE) + return mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck; + else + return mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck; +} + +static uint16_t mt9t013_get_pict_lines_pf(void) +{ + return mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines; +} + +static uint16_t mt9t013_get_pict_pixels_pl(void) +{ + return mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck; +} + +static uint32_t mt9t013_get_pict_max_exp_lc(void) +{ + uint16_t snapshot_lines_per_frame; + + if (mt9t013_ctrl->pict_res == QTR_SIZE) { + snapshot_lines_per_frame = + mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1; + } else { + snapshot_lines_per_frame = + mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1; + } + + return snapshot_lines_per_frame * 24; +} + +static int32_t mt9t013_set_fps(struct fps_cfg *fps) +{ + /* input is new fps in Q8 format */ + int32_t rc = 0; + + mt9t013_ctrl->fps_divider = fps->fps_div; + mt9t013_ctrl->pict_fps_divider = fps->pict_fps_div; + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return -EBUSY; + + CDBG("mt9t013_set_fps: fps_div is %d, frame_rate is %d\n", + fps->fps_div, + (uint16_t) (mt9t013_regs.reg_pat[RES_PREVIEW]. + frame_length_lines * + fps->fps_div/0x00000400)); + + CDBG("mt9t013_set_fps: fps_mult is %d, frame_rate is %d\n", + fps->f_mult, + (uint16_t)(mt9t013_regs.reg_pat[RES_PREVIEW]. + line_length_pck * + fps->f_mult / 0x00000400)); + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_LINE_LENGTH_PCK, + (uint16_t) ( + mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck * + fps->f_mult / 0x00000400)); + if (rc < 0) + return rc; + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + return rc; + + return rc; +} + +static int32_t mt9t013_write_exp_gain(uint16_t gain, uint32_t line) +{ + const uint16_t max_legal_gain = 0x01FF; + uint32_t line_length_ratio = 0x00000400; + enum mt9t013_setting setting; + int32_t rc = 0; + + if (mt9t013_ctrl->sensormode == SENSOR_PREVIEW_MODE) { + mt9t013_ctrl->my_reg_gain = gain; + mt9t013_ctrl->my_reg_line_count = (uint16_t) line; + } + + if (gain > max_legal_gain) + gain = max_legal_gain; + + /* Verify no overflow */ + if (mt9t013_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) { + line = (uint32_t) (line * mt9t013_ctrl->fps_divider / + 0x00000400); + + setting = RES_PREVIEW; + + } else { + line = (uint32_t) (line * mt9t013_ctrl->pict_fps_divider / + 0x00000400); + + setting = RES_CAPTURE; + } + + /*Set digital gain to 1 */ + gain |= 0x0200; + + if ((mt9t013_regs.reg_pat[setting].frame_length_lines - 1) < line) { + + line_length_ratio = + (uint32_t) (line * 0x00000400) / + (mt9t013_regs.reg_pat[setting].frame_length_lines - 1); + } else + line_length_ratio = 0x00000400; + + /* There used to be PARAMETER_HOLD register write before and + * after REG_GLOBAL_GAIN & REG_COARSE_INIT_TIME. This causes + * aec oscillation. Hence removed. */ + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, REG_GLOBAL_GAIN, gain); + if (rc < 0) + return rc; + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_COARSE_INT_TIME, + (uint16_t)((uint32_t) line )); + if (rc < 0) + return rc; + + return rc; +} + +static int32_t mt9t013_set_pict_exp_gain(uint16_t gain, uint32_t line) +{ + int32_t rc = 0; + + rc = mt9t013_write_exp_gain(gain, line); + if (rc < 0) + return rc; + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + 0x10CC | 0x0002); + + mdelay(5); + + /* camera_timed_wait(snapshot_wait*exposure_ratio); */ + return rc; +} + +static int32_t mt9t013_setting(enum mt9t013_reg_update rupdate, + enum mt9t013_setting rt) +{ + int32_t rc = 0; + + switch (rupdate) { + case UPDATE_PERIODIC: { + + if (rt == RES_PREVIEW || rt == RES_CAPTURE) { +#if 0 + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWOFF); + if (rc < 0) + return rc; +#endif + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_VT_PIX_CLK_DIV, + mt9t013_regs.reg_pat[rt].vt_pix_clk_div); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_VT_SYS_CLK_DIV, + mt9t013_regs.reg_pat[rt].vt_sys_clk_div); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_PRE_PLL_CLK_DIV, + mt9t013_regs.reg_pat[rt].pre_pll_clk_div); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_PLL_MULTIPLIER, + mt9t013_regs.reg_pat[rt].pll_multiplier); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_OP_PIX_CLK_DIV, + mt9t013_regs.reg_pat[rt].op_pix_clk_div); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_OP_SYS_CLK_DIV, + mt9t013_regs.reg_pat[rt].op_sys_clk_div); + if (rc < 0) + return rc; + + mdelay(5); + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_ROW_SPEED, + mt9t013_regs.reg_pat[rt].row_speed); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_X_ADDR_START, + mt9t013_regs.reg_pat[rt].x_addr_start); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_X_ADDR_END, + mt9t013_regs.reg_pat[rt].x_addr_end); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_Y_ADDR_START, + mt9t013_regs.reg_pat[rt].y_addr_start); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_Y_ADDR_END, + mt9t013_regs.reg_pat[rt].y_addr_end); + if (rc < 0) + return rc; + + if (machine_is_sapphire()) { + if (rt == 0) + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_READ_MODE, + 0x046F); + else + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_READ_MODE, + 0x0027); + } else + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_READ_MODE, + mt9t013_regs.reg_pat[rt].read_mode); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_SCALE_M, + mt9t013_regs.reg_pat[rt].scale_m); + if (rc < 0) + return rc; + + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_X_OUTPUT_SIZE, + mt9t013_regs.reg_pat[rt].x_output_size); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_Y_OUTPUT_SIZE, + mt9t013_regs.reg_pat[rt].y_output_size); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_LINE_LENGTH_PCK, + mt9t013_regs.reg_pat[rt].line_length_pck); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_FRAME_LENGTH_LINES, + (mt9t013_regs.reg_pat[rt].frame_length_lines * + mt9t013_ctrl->fps_divider / 0x00000400)); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_COARSE_INT_TIME, + mt9t013_regs.reg_pat[rt].coarse_int_time); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_FINE_INT_TIME, + mt9t013_regs.reg_pat[rt].fine_int_time); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + return rc; + + rc = mt9t013_test(mt9t013_ctrl->set_test); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWON); + if (rc < 0) + return rc; + + mdelay(5); + + return rc; + } + } + break; + + /*CAMSENSOR_REG_UPDATE_PERIODIC */ + case REG_INIT: { + if (rt == RES_PREVIEW || rt == RES_CAPTURE) { + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWOFF); + if (rc < 0) + /* MODE_SELECT, stop streaming */ + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_VT_PIX_CLK_DIV, + mt9t013_regs.reg_pat[rt].vt_pix_clk_div); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_VT_SYS_CLK_DIV, + mt9t013_regs.reg_pat[rt].vt_sys_clk_div); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_PRE_PLL_CLK_DIV, + mt9t013_regs.reg_pat[rt].pre_pll_clk_div); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_PLL_MULTIPLIER, + mt9t013_regs.reg_pat[rt].pll_multiplier); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_OP_PIX_CLK_DIV, + mt9t013_regs.reg_pat[rt].op_pix_clk_div); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_OP_SYS_CLK_DIV, + mt9t013_regs.reg_pat[rt].op_sys_clk_div); + if (rc < 0) + return rc; + + mdelay(5); + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + /* additional power saving mode ok around 38.2MHz */ + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + 0x3084, 0x2409); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + 0x3092, 0x0A49); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + 0x3094, 0x4949); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + 0x3096, 0x4949); + if (rc < 0) + return rc; + + /* Set preview or snapshot mode */ + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_ROW_SPEED, + mt9t013_regs.reg_pat[rt].row_speed); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_X_ADDR_START, + mt9t013_regs.reg_pat[rt].x_addr_start); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_X_ADDR_END, + mt9t013_regs.reg_pat[rt].x_addr_end); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_Y_ADDR_START, + mt9t013_regs.reg_pat[rt].y_addr_start); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_Y_ADDR_END, + mt9t013_regs.reg_pat[rt].y_addr_end); + if (rc < 0) + return rc; + + if (machine_is_sapphire()) { + if (rt == 0) + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_READ_MODE, + 0x046F); + else + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_READ_MODE, + 0x0027); + } else + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_READ_MODE, + mt9t013_regs.reg_pat[rt].read_mode); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_SCALE_M, + mt9t013_regs.reg_pat[rt].scale_m); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_X_OUTPUT_SIZE, + mt9t013_regs.reg_pat[rt].x_output_size); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_Y_OUTPUT_SIZE, + mt9t013_regs.reg_pat[rt].y_output_size); + if (rc < 0) + return 0; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_LINE_LENGTH_PCK, + mt9t013_regs.reg_pat[rt].line_length_pck); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_FRAME_LENGTH_LINES, + mt9t013_regs.reg_pat[rt].frame_length_lines); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_COARSE_INT_TIME, + mt9t013_regs.reg_pat[rt].coarse_int_time); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_FINE_INT_TIME, + mt9t013_regs.reg_pat[rt].fine_int_time); + if (rc < 0) + return rc; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + return rc; + + /* load lens shading */ + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + /* most likely needs to be written only once. */ + rc = mt9t013_set_lc(); + if (rc < 0) + return -EBUSY; + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + return rc; + + rc = mt9t013_test(mt9t013_ctrl->set_test); + if (rc < 0) + return rc; + + mdelay(5); + + rc = + mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWON); + if (rc < 0) + /* MODE_SELECT, stop streaming */ + return rc; + + CDBG("!!! mt9t013 !!! PowerOn is done!\n"); + mdelay(5); + return rc; + } + } /* case CAMSENSOR_REG_INIT: */ + break; + + /*CAMSENSOR_REG_INIT */ + default: + rc = -EINVAL; + break; + } /* switch (rupdate) */ + + return rc; +} + +static int32_t mt9t013_video_config(int mode, int res) +{ + int32_t rc; + + switch (res) { + case QTR_SIZE: + rc = mt9t013_setting(UPDATE_PERIODIC, RES_PREVIEW); + if (rc < 0) + return rc; + CDBG("sensor configuration done!\n"); + break; + + case FULL_SIZE: + rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE); + if (rc < 0) + return rc; + break; + + default: + return -EINVAL; + } /* switch */ + + mt9t013_ctrl->prev_res = res; + mt9t013_ctrl->curr_res = res; + mt9t013_ctrl->sensormode = mode; + + return mt9t013_write_exp_gain(mt9t013_ctrl->my_reg_gain, + mt9t013_ctrl->my_reg_line_count); +} + +static int32_t mt9t013_snapshot_config(int mode) +{ + int32_t rc = 0; + + rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE); + if (rc < 0) + return rc; + + mt9t013_ctrl->curr_res = mt9t013_ctrl->pict_res; + mt9t013_ctrl->sensormode = mode; + return rc; +} + +static int32_t mt9t013_raw_snapshot_config(int mode) +{ + int32_t rc = 0; + + rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE); + if (rc < 0) + return rc; + + mt9t013_ctrl->curr_res = mt9t013_ctrl->pict_res; + mt9t013_ctrl->sensormode = mode; + return rc; +} + +static int32_t mt9t013_power_down(void) +{ + int32_t rc = 0; + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWOFF); + if (rc >= 0) + mdelay(5); + return rc; +} + +static int32_t mt9t013_move_focus(int direction, int32_t num_steps) +{ + int16_t step_direction; + int16_t actual_step; + int16_t next_position; + int16_t break_steps[4]; + uint8_t code_val_msb, code_val_lsb; + int16_t i; + + if (num_steps > MT9T013_TOTAL_STEPS_NEAR_TO_FAR) + num_steps = MT9T013_TOTAL_STEPS_NEAR_TO_FAR; + else if (num_steps == 0) + return -EINVAL; + + if (direction == MOVE_NEAR) + step_direction = 4; + else if (direction == MOVE_FAR) + step_direction = -4; + else + return -EINVAL; + + if (mt9t013_ctrl->curr_lens_pos < mt9t013_ctrl->init_curr_lens_pos) + mt9t013_ctrl->curr_lens_pos = mt9t013_ctrl->init_curr_lens_pos; + + actual_step = + (int16_t) (step_direction * + (int16_t) num_steps); + + for (i = 0; i < 4; i++) + break_steps[i] = + actual_step / 4 * (i + 1) - actual_step / 4 * i; + + for (i = 0; i < 4; i++) { + next_position = + (int16_t) + (mt9t013_ctrl->curr_lens_pos + break_steps[i]); + + if (next_position > 255) + next_position = 255; + else if (next_position < 0) + next_position = 0; + + code_val_msb = + ((next_position >> 4) << 2) | + ((next_position << 4) >> 6); + + code_val_lsb = + ((next_position & 0x03) << 6); + + /* Writing the digital code for current to the actuator */ + if (mt9t013_i2c_write_b(MT9T013_AF_I2C_ADDR>>1, + code_val_msb, code_val_lsb) < 0) + return -EBUSY; + + /* Storing the current lens Position */ + mt9t013_ctrl->curr_lens_pos = next_position; + + if (i < 3) + mdelay(1); + } /* for */ + + return 0; +} + +static int mt9t013_sensor_init_done(const struct msm_camera_sensor_info *data) +{ + gpio_direction_output(data->sensor_reset, 0); + gpio_free(data->sensor_reset); + return 0; +} + +static int mt9t013_probe_init_sensor(const struct msm_camera_sensor_info *data) +{ + int rc; + uint16_t chipid; + + rc = gpio_request(data->sensor_reset, "mt9t013"); + if (!rc) + gpio_direction_output(data->sensor_reset, 1); + else + goto init_probe_done; + + mdelay(20); + + /* RESET the sensor image part via I2C command */ + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, 0x1009); + if (rc < 0) + goto init_probe_fail; + + /* 3. Read sensor Model ID: */ + rc = mt9t013_i2c_read_w(mt9t013_client->addr, + MT9T013_REG_MODEL_ID, &chipid); + + if (rc < 0) + goto init_probe_fail; + + CDBG("mt9t013 model_id = 0x%x\n", chipid); + + /* 4. Compare sensor ID to MT9T012VC ID: */ + if (chipid != MT9T013_MODEL_ID) { + rc = -ENODEV; + goto init_probe_fail; + } + + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + 0x3064, 0x0805); + if (rc < 0) + goto init_probe_fail; + + mdelay(MT9T013_RESET_DELAY_MSECS); + + goto init_probe_done; + + /* sensor: output enable */ +#if 0 + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWON); + + /* if this fails, the sensor is not the MT9T013 */ + rc = mt9t013_set_default_focus(0); +#endif + +init_probe_fail: + gpio_direction_output(data->sensor_reset, 0); + gpio_free(data->sensor_reset); +init_probe_done: + return rc; +} + +static int32_t mt9t013_poweron_af(void) +{ + int32_t rc = 0; + + /* enable AF actuator */ + CDBG("enable AF actuator, gpio = %d\n", + mt9t013_ctrl->sensordata->vcm_pwd); + rc = gpio_request(mt9t013_ctrl->sensordata->vcm_pwd, "mt9t013"); + if (!rc) { + gpio_direction_output(mt9t013_ctrl->sensordata->vcm_pwd, 0); + mdelay(20); + rc = mt9t013_set_default_focus(0); + } else + pr_err("%s, gpio_request failed (%d)!\n", __func__, rc); + return rc; +} + +static void mt9t013_poweroff_af(void) +{ + gpio_direction_output(mt9t013_ctrl->sensordata->vcm_pwd, 1); + gpio_free(mt9t013_ctrl->sensordata->vcm_pwd); +} + +int mt9t013_sensor_open_init(const struct msm_camera_sensor_info *data) +{ + int32_t rc; + + mt9t013_ctrl = kzalloc(sizeof(struct mt9t013_ctrl), GFP_KERNEL); + if (!mt9t013_ctrl) { + pr_err("mt9t013_init failed!\n"); + rc = -ENOMEM; + goto init_done; + } + + mt9t013_ctrl->fps_divider = 1 * 0x00000400; + mt9t013_ctrl->pict_fps_divider = 1 * 0x00000400; + mt9t013_ctrl->set_test = TEST_OFF; + mt9t013_ctrl->prev_res = QTR_SIZE; + mt9t013_ctrl->pict_res = FULL_SIZE; + + if (data) + mt9t013_ctrl->sensordata = data; + + /* enable mclk first */ + msm_camio_clk_rate_set(MT9T013_DEFAULT_CLOCK_RATE); + mdelay(20); + + msm_camio_camif_pad_reg_reset(); + mdelay(20); + + rc = mt9t013_probe_init_sensor(data); + if (rc < 0) + goto init_fail; + + if (mt9t013_ctrl->prev_res == QTR_SIZE) + rc = mt9t013_setting(REG_INIT, RES_PREVIEW); + else + rc = mt9t013_setting(REG_INIT, RES_CAPTURE); + + if (rc >= 0) + rc = mt9t013_poweron_af(); + + if (rc < 0) + goto init_fail; + else + goto init_done; + +init_fail: + kfree(mt9t013_ctrl); +init_done: + return rc; +} + +static int mt9t013_init_client(struct i2c_client *client) +{ + /* Initialize the MSM_CAMI2C Chip */ + init_waitqueue_head(&mt9t013_wait_queue); + return 0; +} + + +static int32_t mt9t013_set_sensor_mode(int mode, int res) +{ + int32_t rc = 0; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + switch (mode) { + case SENSOR_PREVIEW_MODE: + rc = mt9t013_video_config(mode, res); + break; + + case SENSOR_SNAPSHOT_MODE: + rc = mt9t013_snapshot_config(mode); + break; + + case SENSOR_RAW_SNAPSHOT_MODE: + rc = mt9t013_raw_snapshot_config(mode); + break; + + default: + return -EINVAL; + } + + /* FIXME: what should we do if rc < 0? */ + if (rc >= 0) + return mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + return rc; +} + +int mt9t013_sensor_config(void __user *argp) +{ + struct sensor_cfg_data cdata; + long rc = 0; + + if (copy_from_user(&cdata, (void *)argp, + sizeof(struct sensor_cfg_data))) + return -EFAULT; + + down(&mt9t013_sem); + + CDBG("mt9t013_sensor_config: cfgtype = %d\n", cdata.cfgtype); + switch (cdata.cfgtype) { + case CFG_GET_PICT_FPS: + mt9t013_get_pict_fps(cdata.cfg.gfps.prevfps, + &(cdata.cfg.gfps.pictfps)); + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_L_PF: + cdata.cfg.prevl_pf = mt9t013_get_prev_lines_pf(); + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_P_PL: + cdata.cfg.prevp_pl = mt9t013_get_prev_pixels_pl(); + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_L_PF: + cdata.cfg.pictl_pf = mt9t013_get_pict_lines_pf(); + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_P_PL: + cdata.cfg.pictp_pl = + mt9t013_get_pict_pixels_pl(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_MAX_EXP_LC: + cdata.cfg.pict_max_exp_lc = + mt9t013_get_pict_max_exp_lc(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_SET_FPS: + case CFG_SET_PICT_FPS: + rc = mt9t013_set_fps(&(cdata.cfg.fps)); + break; + + case CFG_SET_EXP_GAIN: + rc = mt9t013_write_exp_gain(cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_PICT_EXP_GAIN: + rc = mt9t013_set_pict_exp_gain(cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_MODE: + rc = mt9t013_set_sensor_mode(cdata.mode, cdata.rs); + break; + + case CFG_PWR_DOWN: + rc = mt9t013_power_down(); + break; + + case CFG_MOVE_FOCUS: + rc = mt9t013_move_focus(cdata.cfg.focus.dir, + cdata.cfg.focus.steps); + break; + + case CFG_SET_DEFAULT_FOCUS: + rc = mt9t013_set_default_focus(cdata.cfg.focus.steps); + break; + + case CFG_GET_AF_MAX_STEPS: + cdata.max_steps = MT9T013_TOTAL_STEPS_NEAR_TO_FAR; + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_SET_EFFECT: + default: + rc = -EINVAL; + break; + } + + up(&mt9t013_sem); + return rc; +} + +int mt9t013_sensor_release(void) +{ + int rc = -EBADF; + + down(&mt9t013_sem); + + mt9t013_poweroff_af(); + mt9t013_power_down(); + + gpio_direction_output(mt9t013_ctrl->sensordata->sensor_reset, + 0); + gpio_free(mt9t013_ctrl->sensordata->sensor_reset); + + kfree(mt9t013_ctrl); + + up(&mt9t013_sem); + CDBG("mt9t013_release completed!\n"); + return rc; +} + +static int mt9t013_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + rc = -ENOTSUPP; + goto probe_failure; + } + + mt9t013_sensorw = + kzalloc(sizeof(struct mt9t013_work), GFP_KERNEL); + + if (!mt9t013_sensorw) { + rc = -ENOMEM; + goto probe_failure; + } + + i2c_set_clientdata(client, mt9t013_sensorw); + mt9t013_init_client(client); + mt9t013_client = client; + mt9t013_client->addr = mt9t013_client->addr >> 1; + mdelay(50); + + CDBG("i2c probe ok\n"); + return 0; + +probe_failure: + kfree(mt9t013_sensorw); + mt9t013_sensorw = NULL; + pr_err("i2c probe failure %d\n", rc); + return rc; +} + +static const struct i2c_device_id mt9t013_i2c_id[] = { + { "mt9t013", 0}, + { } +}; + +static struct i2c_driver mt9t013_i2c_driver = { + .id_table = mt9t013_i2c_id, + .probe = mt9t013_i2c_probe, + .remove = __exit_p(mt9t013_i2c_remove), + .driver = { + .name = "mt9t013", + }, +}; + +static int mt9t013_sensor_probe( + const struct msm_camera_sensor_info *info, + struct msm_sensor_ctrl *s) +{ + /* We expect this driver to match with the i2c device registered + * in the board file immediately. */ + int rc = i2c_add_driver(&mt9t013_i2c_driver); + if (rc < 0 || mt9t013_client == NULL) { + rc = -ENOTSUPP; + goto probe_done; + } + + /* enable mclk first */ + msm_camio_clk_rate_set(MT9T013_DEFAULT_CLOCK_RATE); + mdelay(20); + + rc = mt9t013_probe_init_sensor(info); + if (rc < 0) { + i2c_del_driver(&mt9t013_i2c_driver); + goto probe_done; + } + + s->s_init = mt9t013_sensor_open_init; + s->s_release = mt9t013_sensor_release; + s->s_config = mt9t013_sensor_config; + mt9t013_sensor_init_done(info); + +probe_done: + return rc; +} + +static int __mt9t013_probe(struct platform_device *pdev) +{ + return msm_camera_drv_start(pdev, mt9t013_sensor_probe); +} + +static struct platform_driver msm_camera_driver = { + .probe = __mt9t013_probe, + .driver = { + .name = "msm_camera_mt9t013", + .owner = THIS_MODULE, + }, +}; + +static int __init mt9t013_init(void) +{ + return platform_driver_register(&msm_camera_driver); +} + +module_init(mt9t013_init); diff --git a/drivers/media/video/msm/mt9t013.h b/drivers/media/video/msm/mt9t013.h new file mode 100644 index 0000000000000..9bce2036e3b63 --- /dev/null +++ b/drivers/media/video/msm/mt9t013.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#ifndef MT9T013_H +#define MT9T013_H + +#include + +struct reg_struct { + uint16_t vt_pix_clk_div; /* 0x0300 */ + uint16_t vt_sys_clk_div; /* 0x0302 */ + uint16_t pre_pll_clk_div; /* 0x0304 */ + uint16_t pll_multiplier; /* 0x0306 */ + uint16_t op_pix_clk_div; /* 0x0308 */ + uint16_t op_sys_clk_div; /* 0x030A */ + uint16_t scale_m; /* 0x0404 */ + uint16_t row_speed; /* 0x3016 */ + uint16_t x_addr_start; /* 0x3004 */ + uint16_t x_addr_end; /* 0x3008 */ + uint16_t y_addr_start; /* 0x3002 */ + uint16_t y_addr_end; /* 0x3006 */ + uint16_t read_mode; /* 0x3040 */ + uint16_t x_output_size; /* 0x034C */ + uint16_t y_output_size; /* 0x034E */ + uint16_t line_length_pck; /* 0x300C */ + uint16_t frame_length_lines; /* 0x300A */ + uint16_t coarse_int_time; /* 0x3012 */ + uint16_t fine_int_time; /* 0x3014 */ +}; + +struct mt9t013_i2c_reg_conf { + unsigned short waddr; + unsigned short wdata; +}; + +struct mt9t013_reg { + struct reg_struct *reg_pat; + uint16_t reg_pat_size; + struct mt9t013_i2c_reg_conf *ttbl; + uint16_t ttbl_size; + struct mt9t013_i2c_reg_conf *lctbl; + uint16_t lctbl_size; + struct mt9t013_i2c_reg_conf *rftbl; + uint16_t rftbl_size; +}; + +#endif /* #define MT9T013_H */ diff --git a/drivers/media/video/msm/mt9t013_reg.c b/drivers/media/video/msm/mt9t013_reg.c new file mode 100644 index 0000000000000..f296d353e6eea --- /dev/null +++ b/drivers/media/video/msm/mt9t013_reg.c @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2009 QUALCOMM Incorporated. + */ + +#include "mt9t013.h" +#include + +struct reg_struct const mt9t013_reg_pat[2] = { + { /* Preview 2x2 binning 20fps, pclk MHz, MCLK 24MHz */ + /* vt_pix_clk_div:REG=0x0300 update get_snapshot_fps + * if this change */ + 10, + + /* vt_sys_clk_div: REG=0x0302 update get_snapshot_fps + * if this change */ + 1, + + /* pre_pll_clk_div REG=0x0304 update get_snapshot_fps + * if this change */ + 3, + + /* pll_multiplier REG=0x0306 60 for 30fps preview, 40 + * for 20fps preview + * 46 for 30fps preview, try 47/48 to increase further */ + 80, + + /* op_pix_clk_div REG=0x0308 */ + 10, + + /* op_sys_clk_div REG=0x030A */ + 1, + + /* scale_m REG=0x0404 */ + 16, + + /* row_speed REG=0x3016 */ + 0x0111, + + /* x_addr_start REG=0x3004 */ + 8, + + /* x_addr_end REG=0x3008 */ + 2053, + + /* y_addr_start REG=0x3002 */ + 8, + + /* y_addr_end REG=0x3006 */ + 1541, + + /* read_mode REG=0x3040 */ + 0x046C, + + /* x_output_size REG=0x034C */ + 1024, + + /* y_output_size REG=0x034E */ + 768, + + /* line_length_pck REG=0x300C */ + 3540, + + /* frame_length_lines REG=0x300A */ + 861, + + /* coarse_int_time REG=0x3012 */ + 16, + + /* fine_int_time REG=0x3014 */ + 1461 + }, + + { /*Snapshot */ + /* vt_pix_clk_div REG=0x0300 update get_snapshot_fps + * if this change */ + 10, + + /* vt_sys_clk_div REG=0x0302 update get_snapshot_fps + * if this change */ + 1, + + /* pre_pll_clk_div REG=0x0304 update get_snapshot_fps + * if this change */ + 3, + + /* pll_multiplier REG=0x0306 50 for 15fps snapshot, + * 40 for 10fps snapshot + * 46 for 30fps snapshot, try 47/48 to increase further */ + 80, + + /* op_pix_clk_div REG=0x0308 */ + 10, + + /* op_sys_clk_div REG=0x030A */ + 1, + + /* scale_m REG=0x0404 */ + 16, + + /* row_speed REG=0x3016 */ + 0x0111, + + /* x_addr_start REG=0x3004 */ + 8, + + /* x_addr_end REG=0x3008 */ + 2063, + + /* y_addr_start REG=0x3002 */ + 8, + + /* y_addr_end REG=0x3006 */ + 1551, + + /* read_mode REG=0x3040 */ + 0x0024, + + /* x_output_size REG=0x034C */ + 2064, + + /* y_output_size REG=0x034E */ + 1544, + + /* line_length_pck REG=0x300C */ + 4800, + + /* frame_length_lines REG=0x300A */ + 1629, + + /* coarse_int_time REG=0x3012 */ + 16, + + /* fine_int_time REG=0x3014 */ + 733 + } +}; + +struct mt9t013_i2c_reg_conf mt9t013_test_tbl[] = { + { 0x3044, 0x0544 & 0xFBFF }, + { 0x30CA, 0x0004 | 0x0001 }, + { 0x30D4, 0x9020 & 0x7FFF }, + { 0x31E0, 0x0003 & 0xFFFE }, + { 0x3180, 0x91FF & 0x7FFF }, + { 0x301A, (0x10CC | 0x8000) & 0xFFF7 }, + { 0x301E, 0x0000 }, + { 0x3780, 0x0000 }, +}; + +/* [Lens shading 85 Percent TL84] */ +struct mt9t013_i2c_reg_conf mt9t013_lc_tbl[] = { + { 0x360A, 0x0290 }, /* P_RD_P0Q0 */ + { 0x360C, 0xC92D }, /* P_RD_P0Q1 */ + { 0x360E, 0x0771 }, /* P_RD_P0Q2 */ + { 0x3610, 0xE38C }, /* P_RD_P0Q3 */ + { 0x3612, 0xD74F }, /* P_RD_P0Q4 */ + { 0x364A, 0x168C }, /* P_RD_P1Q0 */ + { 0x364C, 0xCACB }, /* P_RD_P1Q1 */ + { 0x364E, 0x8C4C }, /* P_RD_P1Q2 */ + { 0x3650, 0x0BEA }, /* P_RD_P1Q3 */ + { 0x3652, 0xDC0F }, /* P_RD_P1Q4 */ + { 0x368A, 0x70B0 }, /* P_RD_P2Q0 */ + { 0x368C, 0x200B }, /* P_RD_P2Q1 */ + { 0x368E, 0x30B2 }, /* P_RD_P2Q2 */ + { 0x3690, 0xD04F }, /* P_RD_P2Q3 */ + { 0x3692, 0xACF5 }, /* P_RD_P2Q4 */ + { 0x36CA, 0xF7C9 }, /* P_RD_P3Q0 */ + { 0x36CC, 0x2AED }, /* P_RD_P3Q1 */ + { 0x36CE, 0xA652 }, /* P_RD_P3Q2 */ + { 0x36D0, 0x8192 }, /* P_RD_P3Q3 */ + { 0x36D2, 0x3A15 }, /* P_RD_P3Q4 */ + { 0x370A, 0xDA30 }, /* P_RD_P4Q0 */ + { 0x370C, 0x2E2F }, /* P_RD_P4Q1 */ + { 0x370E, 0xBB56 }, /* P_RD_P4Q2 */ + { 0x3710, 0x8195 }, /* P_RD_P4Q3 */ + { 0x3712, 0x02F9 }, /* P_RD_P4Q4 */ + { 0x3600, 0x0230 }, /* P_GR_P0Q0 */ + { 0x3602, 0x58AD }, /* P_GR_P0Q1 */ + { 0x3604, 0x18D1 }, /* P_GR_P0Q2 */ + { 0x3606, 0x260D }, /* P_GR_P0Q3 */ + { 0x3608, 0xF530 }, /* P_GR_P0Q4 */ + { 0x3640, 0x17EB }, /* P_GR_P1Q0 */ + { 0x3642, 0x3CAB }, /* P_GR_P1Q1 */ + { 0x3644, 0x87CE }, /* P_GR_P1Q2 */ + { 0x3646, 0xC02E }, /* P_GR_P1Q3 */ + { 0x3648, 0xF48F }, /* P_GR_P1Q4 */ + { 0x3680, 0x5350 }, /* P_GR_P2Q0 */ + { 0x3682, 0x7EAF }, /* P_GR_P2Q1 */ + { 0x3684, 0x4312 }, /* P_GR_P2Q2 */ + { 0x3686, 0xC652 }, /* P_GR_P2Q3 */ + { 0x3688, 0xBC15 }, /* P_GR_P2Q4 */ + { 0x36C0, 0xB8AD }, /* P_GR_P3Q0 */ + { 0x36C2, 0xBDCD }, /* P_GR_P3Q1 */ + { 0x36C4, 0xE4B2 }, /* P_GR_P3Q2 */ + { 0x36C6, 0xB50F }, /* P_GR_P3Q3 */ + { 0x36C8, 0x5B95 }, /* P_GR_P3Q4 */ + { 0x3700, 0xFC90 }, /* P_GR_P4Q0 */ + { 0x3702, 0x8C51 }, /* P_GR_P4Q1 */ + { 0x3704, 0xCED6 }, /* P_GR_P4Q2 */ + { 0x3706, 0xB594 }, /* P_GR_P4Q3 */ + { 0x3708, 0x0A39 }, /* P_GR_P4Q4 */ + { 0x3614, 0x0230 }, /* P_BL_P0Q0 */ + { 0x3616, 0x160D }, /* P_BL_P0Q1 */ + { 0x3618, 0x08D1 }, /* P_BL_P0Q2 */ + { 0x361A, 0x98AB }, /* P_BL_P0Q3 */ + { 0x361C, 0xEA50 }, /* P_BL_P0Q4 */ + { 0x3654, 0xB4EA }, /* P_BL_P1Q0 */ + { 0x3656, 0xEA6C }, /* P_BL_P1Q1 */ + { 0x3658, 0xFE08 }, /* P_BL_P1Q2 */ + { 0x365A, 0x2C6E }, /* P_BL_P1Q3 */ + { 0x365C, 0xEB0E }, /* P_BL_P1Q4 */ + { 0x3694, 0x6DF0 }, /* P_BL_P2Q0 */ + { 0x3696, 0x3ACF }, /* P_BL_P2Q1 */ + { 0x3698, 0x3E0F }, /* P_BL_P2Q2 */ + { 0x369A, 0xB2B1 }, /* P_BL_P2Q3 */ + { 0x369C, 0xC374 }, /* P_BL_P2Q4 */ + { 0x36D4, 0xF2AA }, /* P_BL_P3Q0 */ + { 0x36D6, 0x8CCC }, /* P_BL_P3Q1 */ + { 0x36D8, 0xDEF2 }, /* P_BL_P3Q2 */ + { 0x36DA, 0xFA11 }, /* P_BL_P3Q3 */ + { 0x36DC, 0x42F5 }, /* P_BL_P3Q4 */ + { 0x3714, 0xF4F1 }, /* P_BL_P4Q0 */ + { 0x3716, 0xF6F0 }, /* P_BL_P4Q1 */ + { 0x3718, 0x8FD6 }, /* P_BL_P4Q2 */ + { 0x371A, 0xEA14 }, /* P_BL_P4Q3 */ + { 0x371C, 0x6338 }, /* P_BL_P4Q4 */ + { 0x361E, 0x0350 }, /* P_GB_P0Q0 */ + { 0x3620, 0x91AE }, /* P_GB_P0Q1 */ + { 0x3622, 0x0571 }, /* P_GB_P0Q2 */ + { 0x3624, 0x100D }, /* P_GB_P0Q3 */ + { 0x3626, 0xCA70 }, /* P_GB_P0Q4 */ + { 0x365E, 0xE6CB }, /* P_GB_P1Q0 */ + { 0x3660, 0x50ED }, /* P_GB_P1Q1 */ + { 0x3662, 0x3DAE }, /* P_GB_P1Q2 */ + { 0x3664, 0xAA4F }, /* P_GB_P1Q3 */ + { 0x3666, 0xDC50 }, /* P_GB_P1Q4 */ + { 0x369E, 0x5470 }, /* P_GB_P2Q0 */ + { 0x36A0, 0x1F6E }, /* P_GB_P2Q1 */ + { 0x36A2, 0x6671 }, /* P_GB_P2Q2 */ + { 0x36A4, 0xC010 }, /* P_GB_P2Q3 */ + { 0x36A6, 0x8DF5 }, /* P_GB_P2Q4 */ + { 0x36DE, 0x0B0C }, /* P_GB_P3Q0 */ + { 0x36E0, 0x84CE }, /* P_GB_P3Q1 */ + { 0x36E2, 0x8493 }, /* P_GB_P3Q2 */ + { 0x36E4, 0xA610 }, /* P_GB_P3Q3 */ + { 0x36E6, 0x50B5 }, /* P_GB_P3Q4 */ + { 0x371E, 0x9651 }, /* P_GB_P4Q0 */ + { 0x3720, 0x1EAB }, /* P_GB_P4Q1 */ + { 0x3722, 0xAF76 }, /* P_GB_P4Q2 */ + { 0x3724, 0xE4F4 }, /* P_GB_P4Q3 */ + { 0x3726, 0x79F8 }, /* P_GB_P4Q4 */ + { 0x3782, 0x0410 }, /* POLY_ORIGIN_C */ + { 0x3784, 0x0320 }, /* POLY_ORIGIN_R */ + { 0x3780, 0x8000 } /* POLY_SC_ENABLE */ +}; + +struct mt9t013_reg mt9t013_regs = { + .reg_pat = &mt9t013_reg_pat[0], + .reg_pat_size = ARRAY_SIZE(mt9t013_reg_pat), + .ttbl = &mt9t013_test_tbl[0], + .ttbl_size = ARRAY_SIZE(mt9t013_test_tbl), + .lctbl = &mt9t013_lc_tbl[0], + .lctbl_size = ARRAY_SIZE(mt9t013_lc_tbl), + .rftbl = &mt9t013_lc_tbl[0], /* &mt9t013_rolloff_tbl[0], */ + .rftbl_size = ARRAY_SIZE(mt9t013_lc_tbl) +}; + + diff --git a/drivers/media/video/msm/s5k3e2fx.c b/drivers/media/video/msm/s5k3e2fx.c new file mode 100644 index 0000000000000..b0cf2d2a6dc9e --- /dev/null +++ b/drivers/media/video/msm/s5k3e2fx.c @@ -0,0 +1,1311 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "s5k3e2fx.h" + +#define S5K3E2FX_REG_MODEL_ID 0x0000 +#define S5K3E2FX_MODEL_ID 0x3E2F + +/* PLL Registers */ +#define REG_PRE_PLL_CLK_DIV 0x0305 +#define REG_PLL_MULTIPLIER_MSB 0x0306 +#define REG_PLL_MULTIPLIER_LSB 0x0307 +#define REG_VT_PIX_CLK_DIV 0x0301 +#define REG_VT_SYS_CLK_DIV 0x0303 +#define REG_OP_PIX_CLK_DIV 0x0309 +#define REG_OP_SYS_CLK_DIV 0x030B + +/* Data Format Registers */ +#define REG_CCP_DATA_FORMAT_MSB 0x0112 +#define REG_CCP_DATA_FORMAT_LSB 0x0113 + +/* Output Size */ +#define REG_X_OUTPUT_SIZE_MSB 0x034C +#define REG_X_OUTPUT_SIZE_LSB 0x034D +#define REG_Y_OUTPUT_SIZE_MSB 0x034E +#define REG_Y_OUTPUT_SIZE_LSB 0x034F + +/* Binning */ +#define REG_X_EVEN_INC 0x0381 +#define REG_X_ODD_INC 0x0383 +#define REG_Y_EVEN_INC 0x0385 +#define REG_Y_ODD_INC 0x0387 +/*Reserved register */ +#define REG_BINNING_ENABLE 0x3014 + +/* Frame Fotmat */ +#define REG_FRAME_LENGTH_LINES_MSB 0x0340 +#define REG_FRAME_LENGTH_LINES_LSB 0x0341 +#define REG_LINE_LENGTH_PCK_MSB 0x0342 +#define REG_LINE_LENGTH_PCK_LSB 0x0343 + +/* MSR setting */ +/* Reserved registers */ +#define REG_SHADE_CLK_ENABLE 0x30AC +#define REG_SEL_CCP 0x30C4 +#define REG_VPIX 0x3024 +#define REG_CLAMP_ON 0x3015 +#define REG_OFFSET 0x307E + +/* CDS timing settings */ +/* Reserved registers */ +#define REG_LD_START 0x3000 +#define REG_LD_END 0x3001 +#define REG_SL_START 0x3002 +#define REG_SL_END 0x3003 +#define REG_RX_START 0x3004 +#define REG_S1_START 0x3005 +#define REG_S1_END 0x3006 +#define REG_S1S_START 0x3007 +#define REG_S1S_END 0x3008 +#define REG_S3_START 0x3009 +#define REG_S3_END 0x300A +#define REG_CMP_EN_START 0x300B +#define REG_CLP_SL_START 0x300C +#define REG_CLP_SL_END 0x300D +#define REG_OFF_START 0x300E +#define REG_RMP_EN_START 0x300F +#define REG_TX_START 0x3010 +#define REG_TX_END 0x3011 +#define REG_STX_WIDTH 0x3012 +#define REG_TYPE1_AF_ENABLE 0x3130 +#define DRIVER_ENABLED 0x0001 +#define AUTO_START_ENABLED 0x0010 +#define REG_NEW_POSITION 0x3131 +#define REG_3152_RESERVED 0x3152 +#define REG_315A_RESERVED 0x315A +#define REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB 0x0204 +#define REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB 0x0205 +#define REG_FINE_INTEGRATION_TIME 0x0200 +#define REG_COARSE_INTEGRATION_TIME 0x0202 +#define REG_COARSE_INTEGRATION_TIME_LSB 0x0203 + +/* Mode select register */ +#define S5K3E2FX_REG_MODE_SELECT 0x0100 +#define S5K3E2FX_MODE_SELECT_STREAM 0x01 /* start streaming */ +#define S5K3E2FX_MODE_SELECT_SW_STANDBY 0x00 /* software standby */ +#define S5K3E2FX_REG_SOFTWARE_RESET 0x0103 +#define S5K3E2FX_SOFTWARE_RESET 0x01 +#define REG_TEST_PATTERN_MODE 0x0601 + +struct reg_struct { + uint8_t pre_pll_clk_div; /* 0x0305 */ + uint8_t pll_multiplier_msb; /* 0x0306 */ + uint8_t pll_multiplier_lsb; /* 0x0307 */ + uint8_t vt_pix_clk_div; /* 0x0301 */ + uint8_t vt_sys_clk_div; /* 0x0303 */ + uint8_t op_pix_clk_div; /* 0x0309 */ + uint8_t op_sys_clk_div; /* 0x030B */ + uint8_t ccp_data_format_msb; /* 0x0112 */ + uint8_t ccp_data_format_lsb; /* 0x0113 */ + uint8_t x_output_size_msb; /* 0x034C */ + uint8_t x_output_size_lsb; /* 0x034D */ + uint8_t y_output_size_msb; /* 0x034E */ + uint8_t y_output_size_lsb; /* 0x034F */ + uint8_t x_even_inc; /* 0x0381 */ + uint8_t x_odd_inc; /* 0x0383 */ + uint8_t y_even_inc; /* 0x0385 */ + uint8_t y_odd_inc; /* 0x0387 */ + uint8_t binning_enable; /* 0x3014 */ + uint8_t frame_length_lines_msb; /* 0x0340 */ + uint8_t frame_length_lines_lsb; /* 0x0341 */ + uint8_t line_length_pck_msb; /* 0x0342 */ + uint8_t line_length_pck_lsb; /* 0x0343 */ + uint8_t shade_clk_enable ; /* 0x30AC */ + uint8_t sel_ccp; /* 0x30C4 */ + uint8_t vpix; /* 0x3024 */ + uint8_t clamp_on; /* 0x3015 */ + uint8_t offset; /* 0x307E */ + uint8_t ld_start; /* 0x3000 */ + uint8_t ld_end; /* 0x3001 */ + uint8_t sl_start; /* 0x3002 */ + uint8_t sl_end; /* 0x3003 */ + uint8_t rx_start; /* 0x3004 */ + uint8_t s1_start; /* 0x3005 */ + uint8_t s1_end; /* 0x3006 */ + uint8_t s1s_start; /* 0x3007 */ + uint8_t s1s_end; /* 0x3008 */ + uint8_t s3_start; /* 0x3009 */ + uint8_t s3_end; /* 0x300A */ + uint8_t cmp_en_start; /* 0x300B */ + uint8_t clp_sl_start; /* 0x300C */ + uint8_t clp_sl_end; /* 0x300D */ + uint8_t off_start; /* 0x300E */ + uint8_t rmp_en_start; /* 0x300F */ + uint8_t tx_start; /* 0x3010 */ + uint8_t tx_end; /* 0x3011 */ + uint8_t stx_width; /* 0x3012 */ + uint8_t reg_3152_reserved; /* 0x3152 */ + uint8_t reg_315A_reserved; /* 0x315A */ + uint8_t analogue_gain_code_global_msb; /* 0x0204 */ + uint8_t analogue_gain_code_global_lsb; /* 0x0205 */ + uint8_t fine_integration_time; /* 0x0200 */ + uint8_t coarse_integration_time; /* 0x0202 */ + uint32_t size_h; + uint32_t blk_l; + uint32_t size_w; + uint32_t blk_p; +}; + +struct reg_struct s5k3e2fx_reg_pat[2] = { + { /* Preview */ + 0x06, /* pre_pll_clk_div REG=0x0305 */ + 0x00, /* pll_multiplier_msb REG=0x0306 */ + 0x88, /* pll_multiplier_lsb REG=0x0307 */ + 0x0a, /* vt_pix_clk_div REG=0x0301 */ + 0x01, /* vt_sys_clk_div REG=0x0303 */ + 0x0a, /* op_pix_clk_div REG=0x0309 */ + 0x01, /* op_sys_clk_div REG=0x030B */ + 0x0a, /* ccp_data_format_msb REG=0x0112 */ + 0x0a, /* ccp_data_format_lsb REG=0x0113 */ + 0x05, /* x_output_size_msb REG=0x034C */ + 0x10, /* x_output_size_lsb REG=0x034D */ + 0x03, /* y_output_size_msb REG=0x034E */ + 0xcc, /* y_output_size_lsb REG=0x034F */ + + /* enable binning for preview */ + 0x01, /* x_even_inc REG=0x0381 */ + 0x01, /* x_odd_inc REG=0x0383 */ + 0x01, /* y_even_inc REG=0x0385 */ + 0x03, /* y_odd_inc REG=0x0387 */ + 0x06, /* binning_enable REG=0x3014 */ + + 0x03, /* frame_length_lines_msb REG=0x0340 */ + 0xde, /* frame_length_lines_lsb REG=0x0341 */ + 0x0a, /* line_length_pck_msb REG=0x0342 */ + 0xac, /* line_length_pck_lsb REG=0x0343 */ + 0x81, /* shade_clk_enable REG=0x30AC */ + 0x01, /* sel_ccp REG=0x30C4 */ + 0x04, /* vpix REG=0x3024 */ + 0x00, /* clamp_on REG=0x3015 */ + 0x02, /* offset REG=0x307E */ + 0x03, /* ld_start REG=0x3000 */ + 0x9c, /* ld_end REG=0x3001 */ + 0x02, /* sl_start REG=0x3002 */ + 0x9e, /* sl_end REG=0x3003 */ + 0x05, /* rx_start REG=0x3004 */ + 0x0f, /* s1_start REG=0x3005 */ + 0x24, /* s1_end REG=0x3006 */ + 0x7c, /* s1s_start REG=0x3007 */ + 0x9a, /* s1s_end REG=0x3008 */ + 0x10, /* s3_start REG=0x3009 */ + 0x14, /* s3_end REG=0x300A */ + 0x10, /* cmp_en_start REG=0x300B */ + 0x04, /* clp_sl_start REG=0x300C */ + 0x26, /* clp_sl_end REG=0x300D */ + 0x02, /* off_start REG=0x300E */ + 0x0e, /* rmp_en_start REG=0x300F */ + 0x30, /* tx_start REG=0x3010 */ + 0x4e, /* tx_end REG=0x3011 */ + 0x1E, /* stx_width REG=0x3012 */ + 0x08, /* reg_3152_reserved REG=0x3152 */ + 0x10, /* reg_315A_reserved REG=0x315A */ + 0x00, /* analogue_gain_code_global_msb REG=0x0204 */ + 0x80, /* analogue_gain_code_global_lsb REG=0x0205 */ + 0x02, /* fine_integration_time REG=0x0200 */ + 0x03, /* coarse_integration_time REG=0x0202 */ + 972, + 18, + 1296, + 1436 + }, + { /* Snapshot */ + 0x06, /* pre_pll_clk_div REG=0x0305 */ + 0x00, /* pll_multiplier_msb REG=0x0306 */ + 0x88, /* pll_multiplier_lsb REG=0x0307 */ + 0x0a, /* vt_pix_clk_div REG=0x0301 */ + 0x01, /* vt_sys_clk_div REG=0x0303 */ + 0x0a, /* op_pix_clk_div REG=0x0309 */ + 0x01, /* op_sys_clk_div REG=0x030B */ + 0x0a, /* ccp_data_format_msb REG=0x0112 */ + 0x0a, /* ccp_data_format_lsb REG=0x0113 */ + 0x0a, /* x_output_size_msb REG=0x034C */ + 0x30, /* x_output_size_lsb REG=0x034D */ + 0x07, /* y_output_size_msb REG=0x034E */ + 0xa8, /* y_output_size_lsb REG=0x034F */ + + /* disable binning for snapshot */ + 0x01, /* x_even_inc REG=0x0381 */ + 0x01, /* x_odd_inc REG=0x0383 */ + 0x01, /* y_even_inc REG=0x0385 */ + 0x01, /* y_odd_inc REG=0x0387 */ + 0x00, /* binning_enable REG=0x3014 */ + + 0x07, /* frame_length_lines_msb REG=0x0340 */ + 0xb6, /* frame_length_lines_lsb REG=0x0341 */ + 0x0a, /* line_length_pck_msb REG=0x0342 */ + 0xac, /* line_length_pck_lsb REG=0x0343 */ + 0x81, /* shade_clk_enable REG=0x30AC */ + 0x01, /* sel_ccp REG=0x30C4 */ + 0x04, /* vpix REG=0x3024 */ + 0x00, /* clamp_on REG=0x3015 */ + 0x02, /* offset REG=0x307E */ + 0x03, /* ld_start REG=0x3000 */ + 0x9c, /* ld_end REG=0x3001 */ + 0x02, /* sl_start REG=0x3002 */ + 0x9e, /* sl_end REG=0x3003 */ + 0x05, /* rx_start REG=0x3004 */ + 0x0f, /* s1_start REG=0x3005 */ + 0x24, /* s1_end REG=0x3006 */ + 0x7c, /* s1s_start REG=0x3007 */ + 0x9a, /* s1s_end REG=0x3008 */ + 0x10, /* s3_start REG=0x3009 */ + 0x14, /* s3_end REG=0x300A */ + 0x10, /* cmp_en_start REG=0x300B */ + 0x04, /* clp_sl_start REG=0x300C */ + 0x26, /* clp_sl_end REG=0x300D */ + 0x02, /* off_start REG=0x300E */ + 0x0e, /* rmp_en_start REG=0x300F */ + 0x30, /* tx_start REG=0x3010 */ + 0x4e, /* tx_end REG=0x3011 */ + 0x1E, /* stx_width REG=0x3012 */ + 0x08, /* reg_3152_reserved REG=0x3152 */ + 0x10, /* reg_315A_reserved REG=0x315A */ + 0x00, /* analogue_gain_code_global_msb REG=0x0204 */ + 0x80, /* analogue_gain_code_global_lsb REG=0x0205 */ + 0x02, /* fine_integration_time REG=0x0200 */ + 0x03, /* coarse_integration_time REG=0x0202 */ + 1960, + 14, + 2608, + 124 + } +}; + +struct s5k3e2fx_work { + struct work_struct work; +}; +static struct s5k3e2fx_work *s5k3e2fx_sensorw; +static struct i2c_client *s5k3e2fx_client; + +struct s5k3e2fx_ctrl { + const struct msm_camera_sensor_info *sensordata; + + int sensormode; + uint32_t fps_divider; /* init to 1 * 0x00000400 */ + uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */ + + uint16_t curr_lens_pos; + uint16_t init_curr_lens_pos; + uint16_t my_reg_gain; + uint32_t my_reg_line_count; + + enum msm_s_resolution prev_res; + enum msm_s_resolution pict_res; + enum msm_s_resolution curr_res; + enum msm_s_test_mode set_test; +}; + +struct s5k3e2fx_i2c_reg_conf { + unsigned short waddr; + unsigned char bdata; +}; + +static struct s5k3e2fx_ctrl *s5k3e2fx_ctrl; +static DECLARE_WAIT_QUEUE_HEAD(s5k3e2fx_wait_queue); +DECLARE_MUTEX(s5k3e2fx_sem); + +static int s5k3e2fx_i2c_rxdata(unsigned short saddr, unsigned char *rxdata, + int length) +{ + struct i2c_msg msgs[] = { + { + .addr = saddr, + .flags = 0, + .len = 2, + .buf = rxdata, + }, + { + .addr = saddr, + .flags = I2C_M_RD, + .len = length, + .buf = rxdata, + }, + }; + + if (i2c_transfer(s5k3e2fx_client->adapter, msgs, 2) < 0) { + CDBG("s5k3e2fx_i2c_rxdata failed!\n"); + return -EIO; + } + + return 0; +} + +static int32_t s5k3e2fx_i2c_txdata(unsigned short saddr, + unsigned char *txdata, int length) +{ + struct i2c_msg msg[] = { + { + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, + }, + }; + + if (i2c_transfer(s5k3e2fx_client->adapter, msg, 1) < 0) { + CDBG("s5k3e2fx_i2c_txdata failed\n"); + return -EIO; + } + + return 0; +} + +static int32_t s5k3e2fx_i2c_write_b(unsigned short saddr, unsigned short waddr, + unsigned char bdata) +{ + int32_t rc = -EIO; + unsigned char buf[4]; + + memset(buf, 0, sizeof(buf)); + buf[0] = (waddr & 0xFF00)>>8; + buf[1] = (waddr & 0x00FF); + buf[2] = bdata; + + rc = s5k3e2fx_i2c_txdata(saddr, buf, 3); + + if (rc < 0) + CDBG("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n", + waddr, bdata); + + return rc; +} + +static int32_t s5k3e2fx_i2c_write_table( + struct s5k3e2fx_i2c_reg_conf *reg_cfg_tbl, int num) +{ + int i; + int32_t rc = -EIO; + for (i = 0; i < num; i++) { + if (rc < 0) + break; + reg_cfg_tbl++; + } + + return rc; +} + +static int32_t s5k3e2fx_i2c_read_w(unsigned short saddr, unsigned short raddr, + unsigned short *rdata) +{ + int32_t rc = 0; + unsigned char buf[4]; + + if (!rdata) + return -EIO; + + memset(buf, 0, sizeof(buf)); + + buf[0] = (raddr & 0xFF00)>>8; + buf[1] = (raddr & 0x00FF); + + rc = s5k3e2fx_i2c_rxdata(saddr, buf, 2); + if (rc < 0) + return rc; + + *rdata = buf[0] << 8 | buf[1]; + + if (rc < 0) + CDBG("s5k3e2fx_i2c_read failed!\n"); + + return rc; +} + +static int s5k3e2fx_probe_init_done(const struct msm_camera_sensor_info *data) +{ + gpio_direction_output(data->sensor_reset, 0); + gpio_free(data->sensor_reset); + return 0; +} + +static int s5k3e2fx_probe_init_sensor(const struct msm_camera_sensor_info *data) +{ + int32_t rc; + uint16_t chipid = 0; + + rc = gpio_request(data->sensor_reset, "s5k3e2fx"); + if (!rc) + gpio_direction_output(data->sensor_reset, 1); + else + goto init_probe_done; + + mdelay(20); + + CDBG("s5k3e2fx_sensor_init(): reseting sensor.\n"); + + rc = s5k3e2fx_i2c_read_w(s5k3e2fx_client->addr, + S5K3E2FX_REG_MODEL_ID, &chipid); + if (rc < 0) + goto init_probe_fail; + + if (chipid != S5K3E2FX_MODEL_ID) { + CDBG("S5K3E2FX wrong model_id = 0x%x\n", chipid); + rc = -ENODEV; + goto init_probe_fail; + } + + goto init_probe_done; + +init_probe_fail: + s5k3e2fx_probe_init_done(data); +init_probe_done: + return rc; +} + +static int s5k3e2fx_init_client(struct i2c_client *client) +{ + /* Initialize the MSM_CAMI2C Chip */ + init_waitqueue_head(&s5k3e2fx_wait_queue); + return 0; +} + +static const struct i2c_device_id s5k3e2fx_i2c_id[] = { + { "s5k3e2fx", 0}, + { } +}; + +static int s5k3e2fx_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + CDBG("s5k3e2fx_probe called!\n"); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + CDBG("i2c_check_functionality failed\n"); + goto probe_failure; + } + + s5k3e2fx_sensorw = kzalloc(sizeof(struct s5k3e2fx_work), GFP_KERNEL); + if (!s5k3e2fx_sensorw) { + CDBG("kzalloc failed.\n"); + rc = -ENOMEM; + goto probe_failure; + } + + i2c_set_clientdata(client, s5k3e2fx_sensorw); + s5k3e2fx_init_client(client); + s5k3e2fx_client = client; + + mdelay(50); + + CDBG("s5k3e2fx_probe successed! rc = %d\n", rc); + return 0; + +probe_failure: + CDBG("s5k3e2fx_probe failed! rc = %d\n", rc); + return rc; +} + +static struct i2c_driver s5k3e2fx_i2c_driver = { + .id_table = s5k3e2fx_i2c_id, + .probe = s5k3e2fx_i2c_probe, + .remove = __exit_p(s5k3e2fx_i2c_remove), + .driver = { + .name = "s5k3e2fx", + }, +}; + +static int32_t s5k3e2fx_test(enum msm_s_test_mode mo) +{ + int32_t rc = 0; + + if (mo == S_TEST_OFF) + rc = 0; + else + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_TEST_PATTERN_MODE, (uint16_t)mo); + + return rc; +} + +static int32_t s5k3e2fx_setting(enum msm_s_reg_update rupdate, + enum msm_s_setting rt) +{ + int32_t rc = 0; + uint16_t num_lperf; + + switch (rupdate) { + case S_UPDATE_PERIODIC: + if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) { + + struct s5k3e2fx_i2c_reg_conf tbl_1[] = { + {REG_CCP_DATA_FORMAT_MSB, s5k3e2fx_reg_pat[rt].ccp_data_format_msb}, + {REG_CCP_DATA_FORMAT_LSB, s5k3e2fx_reg_pat[rt].ccp_data_format_lsb}, + {REG_X_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].x_output_size_msb}, + {REG_X_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].x_output_size_lsb}, + {REG_Y_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].y_output_size_msb}, + {REG_Y_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].y_output_size_lsb}, + {REG_X_EVEN_INC, s5k3e2fx_reg_pat[rt].x_even_inc}, + {REG_X_ODD_INC, s5k3e2fx_reg_pat[rt].x_odd_inc}, + {REG_Y_EVEN_INC, s5k3e2fx_reg_pat[rt].y_even_inc}, + {REG_Y_ODD_INC, s5k3e2fx_reg_pat[rt].y_odd_inc}, + {REG_BINNING_ENABLE, s5k3e2fx_reg_pat[rt].binning_enable}, + }; + + struct s5k3e2fx_i2c_reg_conf tbl_2[] = { + {REG_FRAME_LENGTH_LINES_MSB, 0}, + {REG_FRAME_LENGTH_LINES_LSB, 0}, + {REG_LINE_LENGTH_PCK_MSB, s5k3e2fx_reg_pat[rt].line_length_pck_msb}, + {REG_LINE_LENGTH_PCK_LSB, s5k3e2fx_reg_pat[rt].line_length_pck_lsb}, + {REG_SHADE_CLK_ENABLE, s5k3e2fx_reg_pat[rt].shade_clk_enable}, + {REG_SEL_CCP, s5k3e2fx_reg_pat[rt].sel_ccp}, + {REG_VPIX, s5k3e2fx_reg_pat[rt].vpix}, + {REG_CLAMP_ON, s5k3e2fx_reg_pat[rt].clamp_on}, + {REG_OFFSET, s5k3e2fx_reg_pat[rt].offset}, + {REG_LD_START, s5k3e2fx_reg_pat[rt].ld_start}, + {REG_LD_END, s5k3e2fx_reg_pat[rt].ld_end}, + {REG_SL_START, s5k3e2fx_reg_pat[rt].sl_start}, + {REG_SL_END, s5k3e2fx_reg_pat[rt].sl_end}, + {REG_RX_START, s5k3e2fx_reg_pat[rt].rx_start}, + {REG_S1_START, s5k3e2fx_reg_pat[rt].s1_start}, + {REG_S1_END, s5k3e2fx_reg_pat[rt].s1_end}, + {REG_S1S_START, s5k3e2fx_reg_pat[rt].s1s_start}, + {REG_S1S_END, s5k3e2fx_reg_pat[rt].s1s_end}, + {REG_S3_START, s5k3e2fx_reg_pat[rt].s3_start}, + {REG_S3_END, s5k3e2fx_reg_pat[rt].s3_end}, + {REG_CMP_EN_START, s5k3e2fx_reg_pat[rt].cmp_en_start}, + {REG_CLP_SL_START, s5k3e2fx_reg_pat[rt].clp_sl_start}, + {REG_CLP_SL_END, s5k3e2fx_reg_pat[rt].clp_sl_end}, + {REG_OFF_START, s5k3e2fx_reg_pat[rt].off_start}, + {REG_RMP_EN_START, s5k3e2fx_reg_pat[rt].rmp_en_start}, + {REG_TX_START, s5k3e2fx_reg_pat[rt].tx_start}, + {REG_TX_END, s5k3e2fx_reg_pat[rt].tx_end}, + {REG_STX_WIDTH, s5k3e2fx_reg_pat[rt].stx_width}, + {REG_3152_RESERVED, s5k3e2fx_reg_pat[rt].reg_3152_reserved}, + {REG_315A_RESERVED, s5k3e2fx_reg_pat[rt].reg_315A_reserved}, + {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_msb}, + {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_lsb}, + {REG_FINE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].fine_integration_time}, + {REG_COARSE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].coarse_integration_time}, + {S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_STREAM}, + }; + + rc = s5k3e2fx_i2c_write_table(&tbl_1[0], + ARRAY_SIZE(tbl_1)); + if (rc < 0) + return rc; + + num_lperf = + (uint16_t)((s5k3e2fx_reg_pat[rt].frame_length_lines_msb << 8) & 0xFF00) + + s5k3e2fx_reg_pat[rt].frame_length_lines_lsb; + + num_lperf = num_lperf * s5k3e2fx_ctrl->fps_divider / 0x0400; + + tbl_2[0] = (struct s5k3e2fx_i2c_reg_conf) {REG_FRAME_LENGTH_LINES_MSB, (num_lperf & 0xFF00) >> 8}; + tbl_2[1] = (struct s5k3e2fx_i2c_reg_conf) {REG_FRAME_LENGTH_LINES_LSB, (num_lperf & 0x00FF)}; + + rc = s5k3e2fx_i2c_write_table(&tbl_2[0], + ARRAY_SIZE(tbl_2)); + if (rc < 0) + return rc; + + mdelay(5); + + rc = s5k3e2fx_test(s5k3e2fx_ctrl->set_test); + if (rc < 0) + return rc; + } + break; /* UPDATE_PERIODIC */ + + case S_REG_INIT: + if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) { + + struct s5k3e2fx_i2c_reg_conf tbl_3[] = { + {S5K3E2FX_REG_SOFTWARE_RESET, S5K3E2FX_SOFTWARE_RESET}, + {S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_SW_STANDBY}, + /* PLL setting */ + {REG_PRE_PLL_CLK_DIV, s5k3e2fx_reg_pat[rt].pre_pll_clk_div}, + {REG_PLL_MULTIPLIER_MSB, s5k3e2fx_reg_pat[rt].pll_multiplier_msb}, + {REG_PLL_MULTIPLIER_LSB, s5k3e2fx_reg_pat[rt].pll_multiplier_lsb}, + {REG_VT_PIX_CLK_DIV, s5k3e2fx_reg_pat[rt].vt_pix_clk_div}, + {REG_VT_SYS_CLK_DIV, s5k3e2fx_reg_pat[rt].vt_sys_clk_div}, + {REG_OP_PIX_CLK_DIV, s5k3e2fx_reg_pat[rt].op_pix_clk_div}, + {REG_OP_SYS_CLK_DIV, s5k3e2fx_reg_pat[rt].op_sys_clk_div}, + /*Data Format */ + {REG_CCP_DATA_FORMAT_MSB, s5k3e2fx_reg_pat[rt].ccp_data_format_msb}, + {REG_CCP_DATA_FORMAT_LSB, s5k3e2fx_reg_pat[rt].ccp_data_format_lsb}, + /*Output Size */ + {REG_X_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].x_output_size_msb}, + {REG_X_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].x_output_size_lsb}, + {REG_Y_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].y_output_size_msb}, + {REG_Y_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].y_output_size_lsb}, + /* Binning */ + {REG_X_EVEN_INC, s5k3e2fx_reg_pat[rt].x_even_inc}, + {REG_X_ODD_INC, s5k3e2fx_reg_pat[rt].x_odd_inc }, + {REG_Y_EVEN_INC, s5k3e2fx_reg_pat[rt].y_even_inc}, + {REG_Y_ODD_INC, s5k3e2fx_reg_pat[rt].y_odd_inc}, + {REG_BINNING_ENABLE, s5k3e2fx_reg_pat[rt].binning_enable}, + /* Frame format */ + {REG_FRAME_LENGTH_LINES_MSB, s5k3e2fx_reg_pat[rt].frame_length_lines_msb}, + {REG_FRAME_LENGTH_LINES_LSB, s5k3e2fx_reg_pat[rt].frame_length_lines_lsb}, + {REG_LINE_LENGTH_PCK_MSB, s5k3e2fx_reg_pat[rt].line_length_pck_msb}, + {REG_LINE_LENGTH_PCK_LSB, s5k3e2fx_reg_pat[rt].line_length_pck_lsb}, + /* MSR setting */ + {REG_SHADE_CLK_ENABLE, s5k3e2fx_reg_pat[rt].shade_clk_enable}, + {REG_SEL_CCP, s5k3e2fx_reg_pat[rt].sel_ccp}, + {REG_VPIX, s5k3e2fx_reg_pat[rt].vpix}, + {REG_CLAMP_ON, s5k3e2fx_reg_pat[rt].clamp_on}, + {REG_OFFSET, s5k3e2fx_reg_pat[rt].offset}, + /* CDS timing setting */ + {REG_LD_START, s5k3e2fx_reg_pat[rt].ld_start}, + {REG_LD_END, s5k3e2fx_reg_pat[rt].ld_end}, + {REG_SL_START, s5k3e2fx_reg_pat[rt].sl_start}, + {REG_SL_END, s5k3e2fx_reg_pat[rt].sl_end}, + {REG_RX_START, s5k3e2fx_reg_pat[rt].rx_start}, + {REG_S1_START, s5k3e2fx_reg_pat[rt].s1_start}, + {REG_S1_END, s5k3e2fx_reg_pat[rt].s1_end}, + {REG_S1S_START, s5k3e2fx_reg_pat[rt].s1s_start}, + {REG_S1S_END, s5k3e2fx_reg_pat[rt].s1s_end}, + {REG_S3_START, s5k3e2fx_reg_pat[rt].s3_start}, + {REG_S3_END, s5k3e2fx_reg_pat[rt].s3_end}, + {REG_CMP_EN_START, s5k3e2fx_reg_pat[rt].cmp_en_start}, + {REG_CLP_SL_START, s5k3e2fx_reg_pat[rt].clp_sl_start}, + {REG_CLP_SL_END, s5k3e2fx_reg_pat[rt].clp_sl_end}, + {REG_OFF_START, s5k3e2fx_reg_pat[rt].off_start}, + {REG_RMP_EN_START, s5k3e2fx_reg_pat[rt].rmp_en_start}, + {REG_TX_START, s5k3e2fx_reg_pat[rt].tx_start}, + {REG_TX_END, s5k3e2fx_reg_pat[rt].tx_end}, + {REG_STX_WIDTH, s5k3e2fx_reg_pat[rt].stx_width}, + {REG_3152_RESERVED, s5k3e2fx_reg_pat[rt].reg_3152_reserved}, + {REG_315A_RESERVED, s5k3e2fx_reg_pat[rt].reg_315A_reserved}, + {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_msb}, + {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_lsb}, + {REG_FINE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].fine_integration_time}, + {REG_COARSE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].coarse_integration_time}, + {S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_STREAM}, + }; + + /* reset fps_divider */ + s5k3e2fx_ctrl->fps_divider = 1 * 0x0400; + rc = s5k3e2fx_i2c_write_table(&tbl_3[0], + ARRAY_SIZE(tbl_3)); + if (rc < 0) + return rc; + } + break; /* case REG_INIT: */ + + default: + rc = -EINVAL; + break; + } /* switch (rupdate) */ + + return rc; +} + +static int s5k3e2fx_sensor_open_init(const struct msm_camera_sensor_info *data) +{ + int32_t rc; + + s5k3e2fx_ctrl = kzalloc(sizeof(struct s5k3e2fx_ctrl), GFP_KERNEL); + if (!s5k3e2fx_ctrl) { + CDBG("s5k3e2fx_init failed!\n"); + rc = -ENOMEM; + goto init_done; + } + + s5k3e2fx_ctrl->fps_divider = 1 * 0x00000400; + s5k3e2fx_ctrl->pict_fps_divider = 1 * 0x00000400; + s5k3e2fx_ctrl->set_test = S_TEST_OFF; + s5k3e2fx_ctrl->prev_res = S_QTR_SIZE; + s5k3e2fx_ctrl->pict_res = S_FULL_SIZE; + + if (data) + s5k3e2fx_ctrl->sensordata = data; + + /* enable mclk first */ + msm_camio_clk_rate_set(24000000); + mdelay(20); + + msm_camio_camif_pad_reg_reset(); + mdelay(20); + + rc = s5k3e2fx_probe_init_sensor(data); + if (rc < 0) + goto init_fail1; + + if (s5k3e2fx_ctrl->prev_res == S_QTR_SIZE) + rc = s5k3e2fx_setting(S_REG_INIT, S_RES_PREVIEW); + else + rc = s5k3e2fx_setting(S_REG_INIT, S_RES_CAPTURE); + + if (rc < 0) { + CDBG("s5k3e2fx_setting failed. rc = %d\n", rc); + goto init_fail1; + } + + /* initialize AF */ + if ((rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + 0x3146, 0x3A)) < 0) + goto init_fail1; + + if ((rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + 0x3130, 0x03)) < 0) + goto init_fail1; + + goto init_done; + +init_fail1: + s5k3e2fx_probe_init_done(data); + kfree(s5k3e2fx_ctrl); +init_done: + return rc; +} + +static int32_t s5k3e2fx_power_down(void) +{ + int32_t rc = 0; + return rc; +} + +static int s5k3e2fx_sensor_release(void) +{ + int rc = -EBADF; + + down(&s5k3e2fx_sem); + + s5k3e2fx_power_down(); + + gpio_direction_output(s5k3e2fx_ctrl->sensordata->sensor_reset, + 0); + gpio_free(s5k3e2fx_ctrl->sensordata->sensor_reset); + + kfree(s5k3e2fx_ctrl); + s5k3e2fx_ctrl = NULL; + + CDBG("s5k3e2fx_release completed\n"); + + up(&s5k3e2fx_sem); + return rc; +} + +static void s5k3e2fx_get_pict_fps(uint16_t fps, uint16_t *pfps) +{ + /* input fps is preview fps in Q8 format */ + uint32_t divider; /* Q10 */ + + divider = (uint32_t) + ((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) * + (s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p)) * 0x00000400 / + ((s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l) * + (s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p)); + + /* Verify PCLK settings and frame sizes. */ + *pfps = (uint16_t)(fps * divider / 0x00000400); +} + +static uint16_t s5k3e2fx_get_prev_lines_pf(void) +{ + return (s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l); +} + +static uint16_t s5k3e2fx_get_prev_pixels_pl(void) +{ + return (s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p); +} + +static uint16_t s5k3e2fx_get_pict_lines_pf(void) +{ + return (s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l); +} + +static uint16_t s5k3e2fx_get_pict_pixels_pl(void) +{ + return (s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p); +} + +static uint32_t s5k3e2fx_get_pict_max_exp_lc(void) +{ + uint32_t snapshot_lines_per_frame; + + if (s5k3e2fx_ctrl->pict_res == S_QTR_SIZE) + snapshot_lines_per_frame = + s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l; + else + snapshot_lines_per_frame = 3961 * 3; + + return snapshot_lines_per_frame; +} + +static int32_t s5k3e2fx_set_fps(struct fps_cfg *fps) +{ + /* input is new fps in Q10 format */ + int32_t rc = 0; + + s5k3e2fx_ctrl->fps_divider = fps->fps_div; + + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_FRAME_LENGTH_LINES_MSB, + (((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) * + s5k3e2fx_ctrl->fps_divider / 0x400) & 0xFF00) >> 8); + if (rc < 0) + goto set_fps_done; + + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_FRAME_LENGTH_LINES_LSB, + (((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) * + s5k3e2fx_ctrl->fps_divider / 0x400) & 0xFF00)); + +set_fps_done: + return rc; +} + +static int32_t s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) +{ + int32_t rc = 0; + + uint16_t max_legal_gain = 0x0200; + uint32_t ll_ratio; /* Q10 */ + uint16_t ll_pck, fl_lines; + uint16_t offset = 4; + uint8_t gain_msb, gain_lsb; + uint8_t intg_t_msb, intg_t_lsb; + uint8_t ll_pck_msb, ll_pck_lsb, tmp; + + struct s5k3e2fx_i2c_reg_conf tbl[2]; + + CDBG("Line:%d s5k3e2fx_write_exp_gain \n", __LINE__); + + if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) { + + s5k3e2fx_ctrl->my_reg_gain = gain; + s5k3e2fx_ctrl->my_reg_line_count = (uint16_t)line; + + fl_lines = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l; + + ll_pck = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p; + + } else { + + fl_lines = s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l; + + ll_pck = s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p; + } + + if (gain > max_legal_gain) + gain = max_legal_gain; + + /* in Q10 */ + line = (line * s5k3e2fx_ctrl->fps_divider); + + if (fl_lines < (line / 0x400)) + ll_ratio = (line / (fl_lines - offset)); + else + ll_ratio = 0x400; + + /* update gain registers */ + gain_msb = (gain & 0xFF00) >> 8; + gain_lsb = gain & 0x00FF; + tbl[0].waddr = REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB; + tbl[0].bdata = gain_msb; + tbl[1].waddr = REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB; + tbl[1].bdata = gain_lsb; + rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); + if (rc < 0) + goto write_gain_done; + + ll_pck = ll_pck * ll_ratio; + ll_pck_msb = ((ll_pck / 0x400) & 0xFF00) >> 8; + ll_pck_lsb = (ll_pck / 0x400) & 0x00FF; + tbl[0].waddr = REG_LINE_LENGTH_PCK_MSB; + tbl[0].bdata = s5k3e2fx_reg_pat[S_RES_PREVIEW].line_length_pck_msb; + tbl[1].waddr = REG_LINE_LENGTH_PCK_LSB; + tbl[1].bdata = s5k3e2fx_reg_pat[S_RES_PREVIEW].line_length_pck_lsb; + rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); + if (rc < 0) + goto write_gain_done; + + tmp = (ll_pck * 0x400) / ll_ratio; + intg_t_msb = (tmp & 0xFF00) >> 8; + intg_t_lsb = (tmp & 0x00FF); + tbl[0].waddr = REG_COARSE_INTEGRATION_TIME; + tbl[0].bdata = intg_t_msb; + tbl[1].waddr = REG_COARSE_INTEGRATION_TIME_LSB; + tbl[1].bdata = intg_t_lsb; + rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); + +write_gain_done: + return rc; +} + +static int32_t s5k3e2fx_set_pict_exp_gain(uint16_t gain, uint32_t line) +{ + int32_t rc = 0; + + CDBG("Line:%d s5k3e2fx_set_pict_exp_gain \n", __LINE__); + + rc = + s5k3e2fx_write_exp_gain(gain, line); + + return rc; +} + +static int32_t s5k3e2fx_video_config(int mode, int res) +{ + int32_t rc; + + switch (res) { + case S_QTR_SIZE: + rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_PREVIEW); + if (rc < 0) + return rc; + + CDBG("s5k3e2fx sensor configuration done!\n"); + break; + + case S_FULL_SIZE: + rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE); + if (rc < 0) + return rc; + + break; + + default: + return 0; + } /* switch */ + + s5k3e2fx_ctrl->prev_res = res; + s5k3e2fx_ctrl->curr_res = res; + s5k3e2fx_ctrl->sensormode = mode; + + rc = + s5k3e2fx_write_exp_gain(s5k3e2fx_ctrl->my_reg_gain, + s5k3e2fx_ctrl->my_reg_line_count); + + return rc; +} + +static int32_t s5k3e2fx_snapshot_config(int mode) +{ + int32_t rc = 0; + + rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE); + if (rc < 0) + return rc; + + s5k3e2fx_ctrl->curr_res = s5k3e2fx_ctrl->pict_res; + s5k3e2fx_ctrl->sensormode = mode; + + return rc; +} + +static int32_t s5k3e2fx_raw_snapshot_config(int mode) +{ + int32_t rc = 0; + + rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE); + if (rc < 0) + return rc; + + s5k3e2fx_ctrl->curr_res = s5k3e2fx_ctrl->pict_res; + s5k3e2fx_ctrl->sensormode = mode; + + return rc; +} + +static int32_t s5k3e2fx_set_sensor_mode(int mode, int res) +{ + int32_t rc = 0; + + switch (mode) { + case SENSOR_PREVIEW_MODE: + rc = s5k3e2fx_video_config(mode, res); + break; + + case SENSOR_SNAPSHOT_MODE: + rc = s5k3e2fx_snapshot_config(mode); + break; + + case SENSOR_RAW_SNAPSHOT_MODE: + rc = s5k3e2fx_raw_snapshot_config(mode); + break; + + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static int32_t s5k3e2fx_set_default_focus(void) +{ + int32_t rc = 0; + + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + 0x3131, 0); + if (rc < 0) + return rc; + + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + 0x3132, 0); + if (rc < 0) + return rc; + + s5k3e2fx_ctrl->curr_lens_pos = 0; + + return rc; +} + +static int32_t s5k3e2fx_move_focus(int direction, int32_t num_steps) +{ + int32_t rc = 0; + int32_t i; + int16_t step_direction; + int16_t actual_step; + int16_t next_pos, pos_offset; + int16_t init_code = 50; + uint8_t next_pos_msb, next_pos_lsb; + int16_t s_move[5]; + uint32_t gain; /* Q10 format */ + + if (direction == MOVE_NEAR) + step_direction = 20; + else if (direction == MOVE_FAR) + step_direction = -20; + else { + CDBG("s5k3e2fx_move_focus failed at line %d ...\n", __LINE__); + return -EINVAL; + } + + actual_step = step_direction * (int16_t)num_steps; + pos_offset = init_code + s5k3e2fx_ctrl->curr_lens_pos; + gain = actual_step * 0x400 / 5; + + for (i = 0; i <= 4; i++) { + if (actual_step >= 0) + s_move[i] = ((((i+1)*gain+0x200) - (i*gain+0x200))/0x400); + else + s_move[i] = ((((i+1)*gain-0x200) - (i*gain-0x200))/0x400); + } + + /* Ring Damping Code */ + for (i = 0; i <= 4; i++) { + next_pos = (int16_t)(pos_offset + s_move[i]); + + if (next_pos > (738 + init_code)) + next_pos = 738 + init_code; + else if (next_pos < 0) + next_pos = 0; + + CDBG("next_position in damping mode = %d\n", next_pos); + /* Writing the Values to the actuator */ + if (next_pos == init_code) + next_pos = 0x00; + + next_pos_msb = next_pos >> 8; + next_pos_lsb = next_pos & 0x00FF; + + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3131, next_pos_msb); + if (rc < 0) + break; + + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3132, next_pos_lsb); + if (rc < 0) + break; + + pos_offset = next_pos; + s5k3e2fx_ctrl->curr_lens_pos = pos_offset - init_code; + if (i < 4) + mdelay(3); + } + + return rc; +} + +static int s5k3e2fx_sensor_config(void __user *argp) +{ + struct sensor_cfg_data cdata; + long rc = 0; + + if (copy_from_user(&cdata, + (void *)argp, + sizeof(struct sensor_cfg_data))) + return -EFAULT; + + down(&s5k3e2fx_sem); + + CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype); + switch (cdata.cfgtype) { + case CFG_GET_PICT_FPS: + s5k3e2fx_get_pict_fps(cdata.cfg.gfps.prevfps, + &(cdata.cfg.gfps.pictfps)); + + if (copy_to_user((void *)argp, &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_L_PF: + cdata.cfg.prevl_pf = s5k3e2fx_get_prev_lines_pf(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_P_PL: + cdata.cfg.prevp_pl = s5k3e2fx_get_prev_pixels_pl(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_L_PF: + cdata.cfg.pictl_pf = s5k3e2fx_get_pict_lines_pf(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_P_PL: + cdata.cfg.pictp_pl = s5k3e2fx_get_pict_pixels_pl(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_MAX_EXP_LC: + cdata.cfg.pict_max_exp_lc = + s5k3e2fx_get_pict_max_exp_lc(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_SET_FPS: + case CFG_SET_PICT_FPS: + rc = s5k3e2fx_set_fps(&(cdata.cfg.fps)); + break; + + case CFG_SET_EXP_GAIN: + rc = + s5k3e2fx_write_exp_gain(cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_PICT_EXP_GAIN: + CDBG("Line:%d CFG_SET_PICT_EXP_GAIN \n", __LINE__); + rc = + s5k3e2fx_set_pict_exp_gain( + cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_MODE: + rc = + s5k3e2fx_set_sensor_mode( + cdata.mode, cdata.rs); + break; + + case CFG_PWR_DOWN: + rc = s5k3e2fx_power_down(); + break; + + case CFG_MOVE_FOCUS: + rc = + s5k3e2fx_move_focus( + cdata.cfg.focus.dir, + cdata.cfg.focus.steps); + break; + + case CFG_SET_DEFAULT_FOCUS: + rc = + s5k3e2fx_set_default_focus(); + break; + + case CFG_GET_AF_MAX_STEPS: + case CFG_SET_EFFECT: + case CFG_SET_LENS_SHADING: + default: + rc = -EINVAL; + break; + } + + up(&s5k3e2fx_sem); + return rc; +} + +static int s5k3e2fx_sensor_probe(const struct msm_camera_sensor_info *info, + struct msm_sensor_ctrl *s) +{ + int rc = 0; + + rc = i2c_add_driver(&s5k3e2fx_i2c_driver); + if (rc < 0 || s5k3e2fx_client == NULL) { + rc = -ENOTSUPP; + goto probe_fail; + } + + msm_camio_clk_rate_set(24000000); + mdelay(20); + + rc = s5k3e2fx_probe_init_sensor(info); + if (rc < 0) + goto probe_fail; + + s->s_init = s5k3e2fx_sensor_open_init; + s->s_release = s5k3e2fx_sensor_release; + s->s_config = s5k3e2fx_sensor_config; + s5k3e2fx_probe_init_done(info); + + return rc; + +probe_fail: + CDBG("SENSOR PROBE FAILS!\n"); + return rc; +} + +static int __s5k3e2fx_probe(struct platform_device *pdev) +{ + return msm_camera_drv_start(pdev, s5k3e2fx_sensor_probe); +} + +static struct platform_driver msm_camera_driver = { + .probe = __s5k3e2fx_probe, + .driver = { + .name = "msm_camera_s5k3e2fx", + .owner = THIS_MODULE, + }, +}; + +static int __init s5k3e2fx_init(void) +{ + return platform_driver_register(&msm_camera_driver); +} + +module_init(s5k3e2fx_init); + diff --git a/drivers/media/video/msm/s5k3e2fx.h b/drivers/media/video/msm/s5k3e2fx.h new file mode 100644 index 0000000000000..69bc750844573 --- /dev/null +++ b/drivers/media/video/msm/s5k3e2fx.h @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ + +#ifndef CAMSENSOR_S5K3E2FX +#define CAMSENSOR_S5K3E2FX + +#include +#endif /* CAMSENSOR_S5K3E2FX */ diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h new file mode 100644 index 0000000000000..1f22d4d26fec5 --- /dev/null +++ b/include/media/msm_camera.h @@ -0,0 +1,390 @@ +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. + */ +#ifndef __LINUX_MSM_CAMERA_H +#define __LINUX_MSM_CAMERA_H + +#include +#include +#include + +#define MSM_CAM_IOCTL_MAGIC 'm' + +#define MSM_CAM_IOCTL_GET_SENSOR_INFO \ + _IOR(MSM_CAM_IOCTL_MAGIC, 1, struct msm_camsensor_info *) + +#define MSM_CAM_IOCTL_REGISTER_PMEM \ + _IOW(MSM_CAM_IOCTL_MAGIC, 2, struct msm_pmem_info *) + +#define MSM_CAM_IOCTL_UNREGISTER_PMEM \ + _IOW(MSM_CAM_IOCTL_MAGIC, 3, unsigned) + +#define MSM_CAM_IOCTL_CTRL_COMMAND \ + _IOW(MSM_CAM_IOCTL_MAGIC, 4, struct msm_ctrl_cmd *) + +#define MSM_CAM_IOCTL_CONFIG_VFE \ + _IOW(MSM_CAM_IOCTL_MAGIC, 5, struct msm_camera_vfe_cfg_cmd *) + +#define MSM_CAM_IOCTL_GET_STATS \ + _IOR(MSM_CAM_IOCTL_MAGIC, 6, struct msm_camera_stats_event_ctrl *) + +#define MSM_CAM_IOCTL_GETFRAME \ + _IOR(MSM_CAM_IOCTL_MAGIC, 7, struct msm_camera_get_frame *) + +#define MSM_CAM_IOCTL_ENABLE_VFE \ + _IOW(MSM_CAM_IOCTL_MAGIC, 8, struct camera_enable_cmd *) + +#define MSM_CAM_IOCTL_CTRL_CMD_DONE \ + _IOW(MSM_CAM_IOCTL_MAGIC, 9, struct camera_cmd *) + +#define MSM_CAM_IOCTL_CONFIG_CMD \ + _IOW(MSM_CAM_IOCTL_MAGIC, 10, struct camera_cmd *) + +#define MSM_CAM_IOCTL_DISABLE_VFE \ + _IOW(MSM_CAM_IOCTL_MAGIC, 11, struct camera_enable_cmd *) + +#define MSM_CAM_IOCTL_PAD_REG_RESET2 \ + _IOW(MSM_CAM_IOCTL_MAGIC, 12, struct camera_enable_cmd *) + +#define MSM_CAM_IOCTL_VFE_APPS_RESET \ + _IOW(MSM_CAM_IOCTL_MAGIC, 13, struct camera_enable_cmd *) + +#define MSM_CAM_IOCTL_RELEASE_FRAME_BUFFER \ + _IOW(MSM_CAM_IOCTL_MAGIC, 14, struct camera_enable_cmd *) + +#define MSM_CAM_IOCTL_RELEASE_STATS_BUFFER \ + _IOW(MSM_CAM_IOCTL_MAGIC, 15, struct msm_stats_buf *) + +#define MSM_CAM_IOCTL_AXI_CONFIG \ + _IOW(MSM_CAM_IOCTL_MAGIC, 16, struct msm_camera_vfe_cfg_cmd *) + +#define MSM_CAM_IOCTL_GET_PICTURE \ + _IOW(MSM_CAM_IOCTL_MAGIC, 17, struct msm_camera_ctrl_cmd *) + +#define MSM_CAM_IOCTL_SET_CROP \ + _IOW(MSM_CAM_IOCTL_MAGIC, 18, struct crop_info *) + +#define MSM_CAM_IOCTL_PICT_PP \ + _IOW(MSM_CAM_IOCTL_MAGIC, 19, uint8_t *) + +#define MSM_CAM_IOCTL_PICT_PP_DONE \ + _IOW(MSM_CAM_IOCTL_MAGIC, 20, struct msm_snapshot_pp_status *) + +#define MSM_CAM_IOCTL_SENSOR_IO_CFG \ + _IOW(MSM_CAM_IOCTL_MAGIC, 21, struct sensor_cfg_data *) + +#define MSM_CAMERA_LED_OFF 0 +#define MSM_CAMERA_LED_LOW 1 +#define MSM_CAMERA_LED_HIGH 2 + +#define MSM_CAM_IOCTL_FLASH_LED_CFG \ + _IOW(MSM_CAM_IOCTL_MAGIC, 22, unsigned *) + +#define MSM_CAM_IOCTL_UNBLOCK_POLL_FRAME \ + _IO(MSM_CAM_IOCTL_MAGIC, 23) + +#define MSM_CAM_IOCTL_CTRL_COMMAND_2 \ + _IOW(MSM_CAM_IOCTL_MAGIC, 24, struct msm_ctrl_cmd *) + +#define MAX_SENSOR_NUM 3 +#define MAX_SENSOR_NAME 32 + +#define MSM_CAM_CTRL_CMD_DONE 0 +#define MSM_CAM_SENSOR_VFE_CMD 1 + +/***************************************************** + * structure + *****************************************************/ + +/* define five type of structures for userspace <==> kernel + * space communication: + * command 1 - 2 are from userspace ==> kernel + * command 3 - 4 are from kernel ==> userspace + * + * 1. control command: control command(from control thread), + * control status (from config thread); + */ +struct msm_ctrl_cmd { + uint16_t type; + uint16_t length; + void *value; + uint16_t status; + uint32_t timeout_ms; + int resp_fd; /* FIXME: to be used by the kernel, pass-through for now */ +}; + +struct msm_vfe_evt_msg { + unsigned short type; /* 1 == event (RPC), 0 == message (adsp) */ + unsigned short msg_id; + unsigned int len; /* size in, number of bytes out */ + void *data; +}; + +#define MSM_CAM_RESP_CTRL 0 +#define MSM_CAM_RESP_STAT_EVT_MSG 1 +#define MSM_CAM_RESP_V4L2 2 +#define MSM_CAM_RESP_MAX 3 + +/* this one is used to send ctrl/status up to config thread */ +struct msm_stats_event_ctrl { + /* 0 - ctrl_cmd from control thread, + * 1 - stats/event kernel, + * 2 - V4L control or read request */ + int resptype; + int timeout_ms; + struct msm_ctrl_cmd ctrl_cmd; + /* struct vfe_event_t stats_event; */ + struct msm_vfe_evt_msg stats_event; +}; + +/* 2. config command: config command(from config thread); */ +struct msm_camera_cfg_cmd { + /* what to config: + * 1 - sensor config, 2 - vfe config */ + uint16_t cfg_type; + + /* sensor config type */ + uint16_t cmd_type; + uint16_t queue; + uint16_t length; + void *value; +}; + +#define CMD_GENERAL 0 +#define CMD_AXI_CFG_OUT1 1 +#define CMD_AXI_CFG_SNAP_O1_AND_O2 2 +#define CMD_AXI_CFG_OUT2 3 +#define CMD_PICT_T_AXI_CFG 4 +#define CMD_PICT_M_AXI_CFG 5 +#define CMD_RAW_PICT_AXI_CFG 6 +#define CMD_STATS_AXI_CFG 7 +#define CMD_STATS_AF_AXI_CFG 8 +#define CMD_FRAME_BUF_RELEASE 9 +#define CMD_PREV_BUF_CFG 10 +#define CMD_SNAP_BUF_RELEASE 11 +#define CMD_SNAP_BUF_CFG 12 +#define CMD_STATS_DISABLE 13 +#define CMD_STATS_ENABLE 14 +#define CMD_STATS_AF_ENABLE 15 +#define CMD_STATS_BUF_RELEASE 16 +#define CMD_STATS_AF_BUF_RELEASE 17 +#define UPDATE_STATS_INVALID 18 + +/* vfe config command: config command(from config thread)*/ +struct msm_vfe_cfg_cmd { + int cmd_type; + uint16_t length; + void *value; +}; + +#define MAX_CAMERA_ENABLE_NAME_LEN 32 +struct camera_enable_cmd { + char name[MAX_CAMERA_ENABLE_NAME_LEN]; +}; + +#define MSM_PMEM_OUTPUT1 0 +#define MSM_PMEM_OUTPUT2 1 +#define MSM_PMEM_OUTPUT1_OUTPUT2 2 +#define MSM_PMEM_THUMBNAIL 3 +#define MSM_PMEM_MAINIMG 4 +#define MSM_PMEM_RAW_MAINIMG 5 +#define MSM_PMEM_AEC_AWB 6 +#define MSM_PMEM_AF 7 +#define MSM_PMEM_MAX 8 + +#define FRAME_PREVIEW_OUTPUT1 0 +#define FRAME_PREVIEW_OUTPUT2 1 +#define FRAME_SNAPSHOT 2 +#define FRAME_THUMBAIL 3 +#define FRAME_RAW_SNAPSHOT 4 +#define FRAME_MAX 5 + +struct msm_pmem_info { + int type; + int fd; + void *vaddr; + uint32_t offset; + uint32_t len; + uint32_t y_off; /* relative to offset */ + uint32_t cbcr_off; /* relative to offset */ + uint8_t active; +}; + +struct outputCfg { + uint32_t height; + uint32_t width; + + uint32_t window_height_firstline; + uint32_t window_height_lastline; +}; + +#define OUTPUT_1 0 +#define OUTPUT_2 1 +#define OUTPUT_1_AND_2 2 +#define CAMIF_TO_AXI_VIA_OUTPUT_2 3 +#define OUTPUT_1_AND_CAMIF_TO_AXI_VIA_OUTPUT_2 4 +#define OUTPUT_2_AND_CAMIF_TO_AXI_VIA_OUTPUT_1 5 +#define LAST_AXI_OUTPUT_MODE_ENUM = OUTPUT_2_AND_CAMIF_TO_AXI_VIA_OUTPUT_1 6 + +#define MSM_FRAME_PREV_1 0 +#define MSM_FRAME_PREV_2 1 +#define MSM_FRAME_ENC 2 + +struct msm_frame { + int path; + unsigned long buffer; + uint32_t y_off; + uint32_t cbcr_off; + int fd; + + void *cropinfo; + int croplen; +}; + +#define STAT_AEAW 0 +#define STAT_AF 1 +#define STAT_MAX 2 + +struct msm_stats_buf { + int type; + unsigned long buffer; + int fd; +}; + +#define MSM_V4L2_VID_CAP_TYPE 0 +#define MSM_V4L2_STREAM_ON 1 +#define MSM_V4L2_STREAM_OFF 2 +#define MSM_V4L2_SNAPSHOT 3 +#define MSM_V4L2_QUERY_CTRL 4 +#define MSM_V4L2_GET_CTRL 5 +#define MSM_V4L2_SET_CTRL 6 +#define MSM_V4L2_QUERY 7 +#define MSM_V4L2_MAX 8 + +struct crop_info { + void *info; + int len; +}; + +struct msm_postproc { + int ftnum; + struct msm_frame fthumnail; + int fmnum; + struct msm_frame fmain; +}; + +struct msm_snapshot_pp_status { + void *status; +}; + +#define CFG_SET_MODE 0 +#define CFG_SET_EFFECT 1 +#define CFG_START 2 +#define CFG_PWR_UP 3 +#define CFG_PWR_DOWN 4 +#define CFG_WRITE_EXPOSURE_GAIN 5 +#define CFG_SET_DEFAULT_FOCUS 6 +#define CFG_MOVE_FOCUS 7 +#define CFG_REGISTER_TO_REAL_GAIN 8 +#define CFG_REAL_TO_REGISTER_GAIN 9 +#define CFG_SET_FPS 10 +#define CFG_SET_PICT_FPS 11 +#define CFG_SET_BRIGHTNESS 12 +#define CFG_SET_CONTRAST 13 +#define CFG_SET_ZOOM 14 +#define CFG_SET_EXPOSURE_MODE 15 +#define CFG_SET_WB 16 +#define CFG_SET_ANTIBANDING 17 +#define CFG_SET_EXP_GAIN 18 +#define CFG_SET_PICT_EXP_GAIN 19 +#define CFG_SET_LENS_SHADING 20 +#define CFG_GET_PICT_FPS 21 +#define CFG_GET_PREV_L_PF 22 +#define CFG_GET_PREV_P_PL 23 +#define CFG_GET_PICT_L_PF 24 +#define CFG_GET_PICT_P_PL 25 +#define CFG_GET_AF_MAX_STEPS 26 +#define CFG_GET_PICT_MAX_EXP_LC 27 +#define CFG_MAX 28 + +#define MOVE_NEAR 0 +#define MOVE_FAR 1 + +#define SENSOR_PREVIEW_MODE 0 +#define SENSOR_SNAPSHOT_MODE 1 +#define SENSOR_RAW_SNAPSHOT_MODE 2 + +#define SENSOR_QTR_SIZE 0 +#define SENSOR_FULL_SIZE 1 +#define SENSOR_INVALID_SIZE 2 + +#define CAMERA_EFFECT_OFF 0 +#define CAMERA_EFFECT_MONO 1 +#define CAMERA_EFFECT_NEGATIVE 2 +#define CAMERA_EFFECT_SOLARIZE 3 +#define CAMERA_EFFECT_PASTEL 4 +#define CAMERA_EFFECT_MOSAIC 5 +#define CAMERA_EFFECT_RESIZE 6 +#define CAMERA_EFFECT_SEPIA 7 +#define CAMERA_EFFECT_POSTERIZE 8 +#define CAMERA_EFFECT_WHITEBOARD 9 +#define CAMERA_EFFECT_BLACKBOARD 10 +#define CAMERA_EFFECT_AQUA 11 +#define CAMERA_EFFECT_MAX 12 + +struct sensor_pict_fps { + uint16_t prevfps; + uint16_t pictfps; +}; + +struct exp_gain_cfg { + uint16_t gain; + uint32_t line; +}; + +struct focus_cfg { + int32_t steps; + int dir; +}; + +struct fps_cfg { + uint16_t f_mult; + uint16_t fps_div; + uint32_t pict_fps_div; +}; + +struct sensor_cfg_data { + int cfgtype; + int mode; + int rs; + uint8_t max_steps; + + union { + int8_t effect; + uint8_t lens_shading; + uint16_t prevl_pf; + uint16_t prevp_pl; + uint16_t pictl_pf; + uint16_t pictp_pl; + uint32_t pict_max_exp_lc; + uint16_t p_fps; + struct sensor_pict_fps gfps; + struct exp_gain_cfg exp_gain; + struct focus_cfg focus; + struct fps_cfg fps; + } cfg; +}; + +#define GET_NAME 0 +#define GET_PREVIEW_LINE_PER_FRAME 1 +#define GET_PREVIEW_PIXELS_PER_LINE 2 +#define GET_SNAPSHOT_LINE_PER_FRAME 3 +#define GET_SNAPSHOT_PIXELS_PER_LINE 4 +#define GET_SNAPSHOT_FPS 5 +#define GET_SNAPSHOT_MAX_EP_LINE_CNT 6 + +struct msm_camsensor_info { + char name[MAX_SENSOR_NAME]; + uint8_t flash_enabled; +}; +#endif /* __LINUX_MSM_CAMERA_H */ From 75c3ebf32d59702adf26776c150d06a83f8682b2 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Tue, 9 Feb 2010 19:10:04 -0800 Subject: [PATCH 0320/2556] [ARM] msm: board file changes for camera support Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-halibut.c | 12 ++++++++---- arch/arm/mach-msm/board-sapphire.c | 6 ++++-- arch/arm/mach-msm/board-trout.c | 3 ++- arch/arm/mach-msm/include/mach/board.h | 1 + 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-msm/board-halibut.c b/arch/arm/mach-msm/board-halibut.c index 6161e0db8ccdd..00747d64ecb98 100644 --- a/arch/arm/mach-msm/board-halibut.c +++ b/arch/arm/mach-msm/board-halibut.c @@ -174,7 +174,8 @@ static struct msm_camera_sensor_info msm_camera_sensor_mt9d112_data = { .sensor_reset = 89, .sensor_pwd = 85, .vcm_pwd = 0, - .pdata = &msm_camera_device_data + .pdata = &msm_camera_device_data, + .flash_type = MSM_CAMERA_FLASH_NONE }; static struct platform_device msm_camera_sensor_mt9d112 = { @@ -191,7 +192,8 @@ static struct msm_camera_sensor_info msm_camera_sensor_s5k3e2fx_data = { .sensor_reset = 89, .sensor_pwd = 85, .vcm_pwd = 0, - .pdata = &msm_camera_device_data + .pdata = &msm_camera_device_data, + .flash_type = MSM_CAMERA_FLASH_NONE }; static struct platform_device msm_camera_sensor_s5k3e2fx = { @@ -208,7 +210,8 @@ static struct msm_camera_sensor_info msm_camera_sensor_mt9p012_data = { .sensor_reset = 89, .sensor_pwd = 85, .vcm_pwd = 88, - .pdata = &msm_camera_device_data + .pdata = &msm_camera_device_data, + .flash_type = MSM_CAMERA_FLASH_NONE }; static struct platform_device msm_camera_sensor_mt9p012 = { @@ -225,7 +228,8 @@ static struct msm_camera_sensor_info msm_camera_sensor_mt9t013_data = { .sensor_reset = 89, .sensor_pwd = 85, .vcm_pwd = 0, - .pdata = &msm_camera_device_data + .pdata = &msm_camera_device_data, + .flash_type = MSM_CAMERA_FLASH_NONE }; static struct platform_device msm_camera_sensor_mt9t013 = { diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index 4617712b60a45..82f71ce728ec6 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -857,7 +857,8 @@ static struct msm_camera_sensor_info msm_camera_sensor_mt9t013_data = { .sensor_reset = 108, .sensor_pwd = 85, .vcm_pwd = SAPPHIRE_GPIO_VCM_PWDN, - .pdata = &msm_camera_device_data + .pdata = &msm_camera_device_data, + .flash_type = MSM_CAMERA_FLASH_NONE }; static struct platform_device msm_camera_sensor_mt9t013 = { @@ -874,7 +875,8 @@ static struct msm_camera_sensor_info msm_camera_sensor_mt9p012_data = { .sensor_reset = 108, .sensor_pwd = 85, .vcm_pwd = SAPPHIRE_GPIO_VCM_PWDN, - .pdata = &msm_camera_device_data + .pdata = &msm_camera_device_data, + .flash_type = MSM_CAMERA_FLASH_NONE }; static struct platform_device msm_camera_sensor_mt9p012 = { diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 8b4e8e489244c..e820d2059587f 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -434,7 +434,8 @@ static struct msm_camera_sensor_info msm_camera_sensor_mt9t013_data = { .sensor_reset = 108, .sensor_pwd = 85, .vcm_pwd = TROUT_GPIO_VCM_PWDN, - .pdata = &msm_camera_device_data + .pdata = &msm_camera_device_data, + .flash_type = MSM_CAMERA_FLASH_NONE }; static struct platform_device msm_camera_sensor_mt9t013 = { diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index 6d81780b34216..5574480f24d06 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -63,6 +63,7 @@ struct msm_camera_sensor_info { int sensor_pwd; int vcm_pwd; int mclk; + int flash_type; struct msm_camera_device_platform_data *pdata; }; From 001151323401b198e01157c5e6e84382f4d82920 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Tue, 9 Feb 2010 19:10:04 -0800 Subject: [PATCH 0321/2556] [ARM] mt9t013: camera support changes Signed-off-by: Iliyan Malchev --- drivers/i2c/chips/mt9t013.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/chips/mt9t013.c b/drivers/i2c/chips/mt9t013.c index 744a02f88e77d..200a9f8b457ae 100755 --- a/drivers/i2c/chips/mt9t013.c +++ b/drivers/i2c/chips/mt9t013.c @@ -173,7 +173,7 @@ static inline void allow_suspend(void) DECLARE_MUTEX(sem); -static struct msm_camera_device_platform_data *cam; +static struct msm_camera_legacy_device_platform_data *cam; #define out_dword(addr, val) \ (*((volatile unsigned long *)(addr)) = ((unsigned long)(val))) @@ -436,7 +436,7 @@ static int msm_camio_clk_rate_set(int rate) static int clk_select(int internal) { - int rc = -EIO; + int rc = -EIO; printk(KERN_INFO "mt9t013: clk select %d\n", internal); CLK_GET(vfe_clk); if (vfe_clk != NULL) { @@ -451,9 +451,9 @@ static void mt9t013_sensor_init(void) { int ret; printk(KERN_INFO "mt9t013: init\n"); - if (!pclient) + if (!pclient) return; - + /*pull hi reset*/ printk(KERN_INFO "mt9t013: mt9t013_register_init\n"); ret = gpio_request(cam->sensor_reset, "mt9t013"); @@ -485,7 +485,7 @@ static void mt9t013_sensor_init(void) if(ret < 0) printk(KERN_ERR "camio clk rate select error\n"); mdelay(2); - + /* enable gpio */ cam->config_gpio_on(); /* delay 2 ms */ From 6815253eb548418d79745b994aa2285cb0371cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 4 Jun 2009 14:57:45 -0700 Subject: [PATCH 0322/2556] [ARM] msm: adsp: Fix msm_adsp_write to not call mdelay(1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/qdsp5/adsp.c | 8 ++++---- arch/arm/mach-msm/qdsp5/adsp_new.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-msm/qdsp5/adsp.c b/arch/arm/mach-msm/qdsp5/adsp.c index 0eb19a890bfe0..b598342bb48ca 100644 --- a/arch/arm/mach-msm/qdsp5/adsp.c +++ b/arch/arm/mach-msm/qdsp5/adsp.c @@ -312,13 +312,13 @@ int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, while (((ctrl_word = readl(info->write_ctrl)) & ADSP_RTOS_WRITE_CTRL_WORD_READY_M) != ADSP_RTOS_WRITE_CTRL_WORD_READY_V) { - if (cnt > 10) { + if (cnt > 100) { pr_err("adsp: timeout waiting for DSP write ready\n"); ret_status = -EIO; goto fail; } pr_warning("adsp: waiting for DSP write ready\n"); - udelay(10); + udelay(1); cnt++; } @@ -349,12 +349,12 @@ int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, while ((readl(info->write_ctrl) & ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M) == ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V) { - if (cnt > 5) { + if (cnt > 5000) { pr_err("adsp: timeout waiting for adsp ack\n"); ret_status = -EIO; goto fail; } - mdelay(1); + udelay(1); cnt++; } diff --git a/arch/arm/mach-msm/qdsp5/adsp_new.c b/arch/arm/mach-msm/qdsp5/adsp_new.c index 7f9f48f3d9260..a39f45639ba8d 100644 --- a/arch/arm/mach-msm/qdsp5/adsp_new.c +++ b/arch/arm/mach-msm/qdsp5/adsp_new.c @@ -414,13 +414,13 @@ int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, while (((ctrl_word = readl(info->write_ctrl)) & ADSP_RTOS_WRITE_CTRL_WORD_READY_M) != ADSP_RTOS_WRITE_CTRL_WORD_READY_V) { - if (cnt > 10) { + if (cnt > 100) { pr_err("adsp: timeout waiting for DSP write ready\n"); ret_status = -EIO; goto fail; } pr_warning("adsp: waiting for DSP write ready\n"); - udelay(10); + udelay(1); cnt++; } @@ -451,12 +451,12 @@ int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, while ((readl(info->write_ctrl) & ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M) == ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V) { - if (cnt > 5) { + if (cnt > 5000) { pr_err("adsp: timeout waiting for adsp ack\n"); ret_status = -EIO; goto fail; } - mdelay(1); + udelay(1); cnt++; } From b9df9bfc79521767c9c35cd514c1855f8bd34101 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Fri, 5 Jun 2009 15:29:54 -0700 Subject: [PATCH 0323/2556] [ARM] msm: adsp: retry msm_adsp_write() when a queue is full. Commit fee2df57284bcca209ad9cf9ab5f7cf895592b3f replaced an mdelay(1) with a udelay(1) in two places, which sped up msm_adsp_write() considerably in those cases where we did not have to wait for the DSP to get ready for a command. This had the side effect that back-to-back DSP commands can be rejected by the DSP for a given DSP module and queue number when the DSP is still busy processing the last command for that queue. This patch retries sending the command 100 times (for up to ~1ms) in such situations. Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/qdsp5/adsp.c | 20 +++++++++++++++++--- arch/arm/mach-msm/qdsp5/adsp_new.c | 20 +++++++++++++++++--- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-msm/qdsp5/adsp.c b/arch/arm/mach-msm/qdsp5/adsp.c index b598342bb48ca..a72d856c99595 100644 --- a/arch/arm/mach-msm/qdsp5/adsp.c +++ b/arch/arm/mach-msm/qdsp5/adsp.c @@ -281,7 +281,7 @@ static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client, return rc; } -int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, +int __msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, void *cmd_buf, size_t cmd_size) { uint32_t ctrl_word; @@ -363,8 +363,7 @@ int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, if ((ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M) != ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V) { - ret_status = -EIO; - pr_err("adsp: failed to write queue %x, retry\n", dsp_q_addr); + ret_status = -EAGAIN; goto fail; } @@ -427,6 +426,21 @@ int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, return ret_status; } +int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, + void *cmd_buf, size_t cmd_size) +{ + int rc, retries = 0; + do { + rc = __msm_adsp_write(module, dsp_queue_addr, cmd_buf, cmd_size); + if (rc == -EAGAIN) + udelay(10); + } while(rc == -EAGAIN && retries++ < 100); + if (retries > 50) + pr_warning("adsp: %s command took %d attempts: rc %d\n", + module->name, retries, rc); + return rc; +} + #ifdef CONFIG_MSM_ADSP_REPORT_EVENTS static void *modem_event_addr; static void read_modem_event(void *buf, size_t len) diff --git a/arch/arm/mach-msm/qdsp5/adsp_new.c b/arch/arm/mach-msm/qdsp5/adsp_new.c index a39f45639ba8d..026cb6818bb02 100644 --- a/arch/arm/mach-msm/qdsp5/adsp_new.c +++ b/arch/arm/mach-msm/qdsp5/adsp_new.c @@ -377,7 +377,7 @@ static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client, return rc; } -int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, +int __msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, void *cmd_buf, size_t cmd_size) { uint32_t ctrl_word; @@ -465,8 +465,7 @@ int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, if ((ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M) != ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V) { - ret_status = -EIO; - pr_err("adsp: failed to write queue %x, retry\n", dsp_q_addr); + ret_status = -EAGAIN; goto fail; } @@ -530,6 +529,21 @@ int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, } EXPORT_SYMBOL(msm_adsp_write); +int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, + void *cmd_buf, size_t cmd_size) +{ + int rc, retries = 0; + do { + rc = __msm_adsp_write(module, dsp_queue_addr, cmd_buf, cmd_size); + if (rc == -EAGAIN) + udelay(10); + } while(rc == -EAGAIN && retries++ < 100); + if (retries > 50) + pr_warning("adsp: %s command took %d attempts: rc %d\n", + module->name, retries, rc); + return rc; +} + #ifdef CONFIG_MSM_ADSP_REPORT_EVENTS static void *event_addr; static void read_event(void *buf, size_t len) From 0f4a5a9367d0a69ddd5dfc7ff14a8a7ad29e1a62 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Fri, 5 Jun 2009 18:10:08 -0700 Subject: [PATCH 0324/2556] [ARM] msm: adsp: combine old and new adsp implementation adsp.[ch] and adsp_new.[ch] are close enough, and it makes sense to combine them. The differences are featurized under CONFIG_MSM_AMSS_VERSION >= 6350 for the new adsp implementation. Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/qdsp5/Makefile | 11 +- arch/arm/mach-msm/qdsp5/adsp.c | 281 ++++++- arch/arm/mach-msm/qdsp5/adsp.h | 115 ++- arch/arm/mach-msm/qdsp5/adsp_new.c | 1123 ---------------------------- arch/arm/mach-msm/qdsp5/adsp_new.h | 329 -------- 5 files changed, 356 insertions(+), 1503 deletions(-) delete mode 100644 arch/arm/mach-msm/qdsp5/adsp_new.c delete mode 100644 arch/arm/mach-msm/qdsp5/adsp_new.h diff --git a/arch/arm/mach-msm/qdsp5/Makefile b/arch/arm/mach-msm/qdsp5/Makefile index cd24c7aa90dd3..991d4a7e157f7 100644 --- a/arch/arm/mach-msm/qdsp5/Makefile +++ b/arch/arm/mach-msm/qdsp5/Makefile @@ -1,16 +1,17 @@ +obj-y += adsp.o ifeq ($(CONFIG_MSM_AMSS_VERSION_6350),y) -obj-y += adsp_new.o adsp_info.o +obj-y += adsp_info.o obj-y += audio_evrc.o audio_qcelp.o audio_amrnb.o audio_aac.o else -obj-y += adsp.o adsp_6225.o +obj-y += adsp_6225.o endif obj-y += adsp_driver.o obj-y += adsp_video_verify_cmd.o obj-y += adsp_videoenc_verify_cmd.o -obj-y += adsp_jpeg_verify_cmd.o adsp_jpeg_patch_event.o -obj-y += adsp_vfe_verify_cmd.o adsp_vfe_patch_event.o -obj-y += adsp_lpm_verify_cmd.o +obj-y += adsp_jpeg_verify_cmd.o adsp_jpeg_patch_event.o +obj-y += adsp_vfe_verify_cmd.o adsp_vfe_patch_event.o +obj-y += adsp_lpm_verify_cmd.o obj-y += audio_out.o audio_in.o audio_mp3.o audmgr.o audpp.o obj-y += snd.o diff --git a/arch/arm/mach-msm/qdsp5/adsp.c b/arch/arm/mach-msm/qdsp5/adsp.c index a72d856c99595..371443758f910 100644 --- a/arch/arm/mach-msm/qdsp5/adsp.c +++ b/arch/arm/mach-msm/qdsp5/adsp.c @@ -67,8 +67,34 @@ void adsp_set_image(struct adsp_info *info, uint32_t image) current_image = image; } +/* + * Checks whether the module_id is available in the + * module_entries table.If module_id is available returns `0`. + * If module_id is not available returns `-ENXIO`. + */ +#if CONFIG_MSM_AMSS_VERSION >= 6350 +static int32_t adsp_validate_module(uint32_t module_id) +{ + uint32_t *ptr; + uint32_t module_index; + uint32_t num_mod_entries; + + ptr = adsp_info.init_info_ptr->module_entries; + num_mod_entries = adsp_info.init_info_ptr->module_table_size; + + for (module_index = 0; module_index < num_mod_entries; module_index++) + if (module_id == ptr[module_index]) + return 0; + + return -ENXIO; +} +#else +static inline int32_t adsp_validate_module(uint32_t module_id) { return 0; } +#endif + uint32_t adsp_get_module(struct adsp_info *info, uint32_t task) { + BUG_ON(current_image == -1UL); return info->task_to_module[current_image][task]; } @@ -87,7 +113,7 @@ static int rpc_adsp_rtos_app_to_modem(uint32_t cmd, uint32_t module, msm_rpc_setup_req(&rpc_req.hdr, RPC_ADSP_RTOS_ATOM_PROG, - RPC_ADSP_RTOS_ATOM_VERS, + msm_rpc_get_vers(adsp_module->rpc_client), RPC_ADSP_RTOS_APP_TO_MODEM_PROC); rpc_req.gotit = cpu_to_be32(1); @@ -126,13 +152,31 @@ static int rpc_adsp_rtos_app_to_modem(uint32_t cmd, uint32_t module, return 0; } -struct msm_adsp_module *find_adsp_module_by_id( +#if CONFIG_MSM_AMSS_VERSION >= 6350 +static int get_module_index(uint32_t id) +{ + int mod_idx; + for (mod_idx = 0; mod_idx < adsp_info.module_count; mod_idx++) + if (adsp_info.module[mod_idx].id == id) + return mod_idx; + + return -ENXIO; +} +#endif + +static struct msm_adsp_module *find_adsp_module_by_id( struct adsp_info *info, uint32_t id) { - if (id > info->max_module_id) + if (id > info->max_module_id) { return NULL; - else + } else { +#if CONFIG_MSM_AMSS_VERSION >= 6350 + id = get_module_index(id); + if (id < 0) + return NULL; +#endif return info->id_to_module[id]; + } } static struct msm_adsp_module *find_adsp_module_by_name( @@ -147,10 +191,11 @@ static struct msm_adsp_module *find_adsp_module_by_name( static int adsp_rpc_init(struct msm_adsp_module *adsp_module) { + /* remove the original connect once compatible support is complete */ adsp_module->rpc_client = msm_rpc_connect( - RPC_ADSP_RTOS_ATOM_PROG, - RPC_ADSP_RTOS_ATOM_VERS, - MSM_RPC_UNINTERRUPTIBLE); + RPC_ADSP_RTOS_ATOM_PROG, + RPC_ADSP_RTOS_ATOM_VERS, + MSM_RPC_UNINTERRUPTIBLE); if (IS_ERR(adsp_module->rpc_client)) { int rc = PTR_ERR(adsp_module->rpc_client); @@ -162,12 +207,66 @@ static int adsp_rpc_init(struct msm_adsp_module *adsp_module) return 0; } +#if CONFIG_MSM_AMSS_VERSION >= 6350 +/* + * Send RPC_ADSP_RTOS_CMD_GET_INIT_INFO cmd to ARM9 and get + * queue offsets and module entries (init info) as part of the event. + */ +static void msm_get_init_info(void) +{ + int rc; + struct rpc_adsp_rtos_app_to_modem_args_t rpc_req; + + adsp_info.init_info_rpc_client = msm_rpc_connect( + RPC_ADSP_RTOS_ATOM_PROG, + RPC_ADSP_RTOS_ATOM_VERS, + MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(adsp_info.init_info_rpc_client)) { + rc = PTR_ERR(adsp_info.init_info_rpc_client); + adsp_info.init_info_rpc_client = 0; + pr_err("adsp: could not open rpc client: %d\n", rc); + return; + } + + msm_rpc_setup_req(&rpc_req.hdr, + RPC_ADSP_RTOS_ATOM_PROG, + msm_rpc_get_vers(adsp_info.init_info_rpc_client), + RPC_ADSP_RTOS_APP_TO_MODEM_PROC); + + rpc_req.gotit = cpu_to_be32(1); + rpc_req.cmd = cpu_to_be32(RPC_ADSP_RTOS_CMD_GET_INIT_INFO); + rpc_req.proc_id = cpu_to_be32(RPC_ADSP_RTOS_PROC_APPS); + rpc_req.module = 0; + + rc = msm_rpc_write(adsp_info.init_info_rpc_client, + &rpc_req, sizeof(rpc_req)); + if (rc < 0) + pr_err("adsp: could not send RPC request: %d\n", rc); +} +#endif + int msm_adsp_get(const char *name, struct msm_adsp_module **out, struct msm_adsp_ops *ops, void *driver_data) { struct msm_adsp_module *module; int rc = 0; +#if CONFIG_MSM_AMSS_VERSION >= 6350 + static uint32_t init_info_cmd_sent; + if (!init_info_cmd_sent) { + msm_get_init_info(); + init_waitqueue_head(&adsp_info.init_info_wait); + rc = wait_event_timeout(adsp_info.init_info_wait, + adsp_info.init_info_state == ADSP_STATE_INIT_INFO, + 5 * HZ); + if (!rc) { + pr_info("adsp: INIT_INFO failed\n"); + return -ETIMEDOUT; + } + init_info_cmd_sent++; + } +#endif + module = find_adsp_module_by_name(&adsp_info, name); if (!module) return -ENODEV; @@ -220,6 +319,7 @@ int msm_adsp_get(const char *name, struct msm_adsp_module **out, mutex_unlock(&module->lock); return rc; } +EXPORT_SYMBOL(msm_adsp_get); static int msm_adsp_disable_locked(struct msm_adsp_module *module); @@ -258,6 +358,7 @@ void msm_adsp_put(struct msm_adsp_module *module) } mutex_unlock(&module->lock); } +EXPORT_SYMBOL(msm_adsp_put); /* this should be common code with rpc_servers.c */ static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client, @@ -301,6 +402,12 @@ int __msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, module->name); return -ENODEV; } + if (adsp_validate_module(module->id)) { + spin_unlock_irqrestore(&adsp_cmd_lock, flags); + pr_info("adsp: module id validation failed %s %d\n", + module->name, module->id); + return -ENXIO; + } dsp_q_addr = adsp_get_queue_offset(info, dsp_queue_addr); dsp_q_addr &= ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M; @@ -362,12 +469,12 @@ int __msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, ctrl_word = readl(info->write_ctrl); if ((ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M) != - ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V) { + ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V) { ret_status = -EAGAIN; goto fail; } - /* Ctrl word status bits are 00, no error in the ctrl word */ + /* Ctrl word status bits were 00, no error in the ctrl word */ /* Get the DSP buffer address */ dsp_addr = (ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M) + @@ -425,6 +532,7 @@ int __msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, spin_unlock_irqrestore(&adsp_cmd_lock, flags); return ret_status; } +EXPORT_SYMBOL(msm_adsp_write); int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, void *cmd_buf, size_t cmd_size) @@ -443,6 +551,21 @@ int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, #ifdef CONFIG_MSM_ADSP_REPORT_EVENTS static void *modem_event_addr; +#if CONFIG_MSM_AMSS_VERSION >= 6350 +static void read_modem_event(void *buf, size_t len) +{ + uint32_t *dptr = buf; + struct rpc_adsp_rtos_modem_to_app_args_t *sptr; + struct adsp_rtos_mp_mtoa_type *pkt_ptr; + + sptr = modem_event_addr; + pkt_ptr = &sptr->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_packet; + + dptr[0] = be32_to_cpu(sptr->mtoa_pkt.mp_mtoa_header.event); + dptr[1] = be32_to_cpu(pkt_ptr->module); + dptr[2] = be32_to_cpu(pkt_ptr->image); +} +#else static void read_modem_event(void *buf, size_t len) { uint32_t *dptr = buf; @@ -452,17 +575,89 @@ static void read_modem_event(void *buf, size_t len) dptr[1] = be32_to_cpu(sptr->module); dptr[2] = be32_to_cpu(sptr->image); } -#endif +#endif /* CONFIG_MSM_AMSS_VERSION >= 6350 */ +#endif /* CONFIG_MSM_ADSP_REPORT_EVENTS */ static void handle_adsp_rtos_mtoa_app(struct rpc_request_hdr *req) { struct rpc_adsp_rtos_modem_to_app_args_t *args = (struct rpc_adsp_rtos_modem_to_app_args_t *)req; - uint32_t event = be32_to_cpu(args->event); - uint32_t proc_id = be32_to_cpu(args->proc_id); - uint32_t module_id = be32_to_cpu(args->module); - uint32_t image = be32_to_cpu(args->image); + uint32_t event; + uint32_t proc_id; + uint32_t module_id; + uint32_t image; struct msm_adsp_module *module; +#if CONFIG_MSM_AMSS_VERSION >= 6350 + struct adsp_rtos_mp_mtoa_type *pkt_ptr = + &args->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_packet; + + event = be32_to_cpu(args->mtoa_pkt.mp_mtoa_header.event); + proc_id = be32_to_cpu(args->mtoa_pkt.mp_mtoa_header.proc_id); + module_id = be32_to_cpu(pkt_ptr->module); + image = be32_to_cpu(pkt_ptr->image); + + if (be32_to_cpu(args->mtoa_pkt.desc_field) == RPC_ADSP_RTOS_INIT_INFO) { + struct queue_to_offset_type *qptr; + struct queue_to_offset_type *qtbl; + uint32_t *mptr; + uint32_t *mtbl; + uint32_t q_idx; + uint32_t num_entries; + uint32_t entries_per_image; + struct adsp_rtos_mp_mtoa_init_info_type *iptr; + struct adsp_rtos_mp_mtoa_init_info_type *sptr; + int32_t i_no, e_idx; + + pr_info("adsp:INIT_INFO Event\n"); + sptr = &args->mtoa_pkt.adsp_rtos_mp_mtoa_data. + mp_mtoa_init_packet; + + iptr = adsp_info.init_info_ptr; + iptr->image_count = be32_to_cpu(sptr->image_count); + iptr->num_queue_offsets = be32_to_cpu(sptr->num_queue_offsets); + num_entries = iptr->num_queue_offsets; + qptr = &sptr->queue_offsets_tbl[0][0]; + for (i_no = 0; i_no < iptr->image_count; i_no++) { + qtbl = &iptr->queue_offsets_tbl[i_no][0]; + for (e_idx = 0; e_idx < num_entries; e_idx++) { + qtbl[e_idx].offset = be32_to_cpu(qptr->offset); + qtbl[e_idx].queue = be32_to_cpu(qptr->queue); + q_idx = be32_to_cpu(qptr->queue); + iptr->queue_offsets[i_no][q_idx] = + qtbl[e_idx].offset; + qptr++; + } + } + + num_entries = be32_to_cpu(sptr->num_task_module_entries); + iptr->num_task_module_entries = num_entries; + entries_per_image = num_entries / iptr->image_count; + mptr = &sptr->task_to_module_tbl[0][0]; + for (i_no = 0; i_no < iptr->image_count; i_no++) { + mtbl = &iptr->task_to_module_tbl[i_no][0]; + for (e_idx = 0; e_idx < entries_per_image; e_idx++) { + mtbl[e_idx] = be32_to_cpu(*mptr); + mptr++; + } + } + + iptr->module_table_size = be32_to_cpu(sptr->module_table_size); + mptr = &sptr->module_entries[0]; + for (i_no = 0; i_no < iptr->module_table_size; i_no++) + iptr->module_entries[i_no] = be32_to_cpu(mptr[i_no]); + adsp_info.init_info_state = ADSP_STATE_INIT_INFO; + rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, + RPC_ACCEPTSTAT_SUCCESS); + wake_up(&adsp_info.init_info_wait); + + return; + } +#else + event = be32_to_cpu(args->event); + proc_id = be32_to_cpu(args->proc_id); + module_id = be32_to_cpu(args->module); + image = be32_to_cpu(args->image); +#endif pr_info("adsp: rpc event=%d, proc_id=%d, module=%d, image=%d\n", event, proc_id, module_id, image); @@ -499,22 +694,25 @@ static void handle_adsp_rtos_mtoa_app(struct rpc_request_hdr *req) case RPC_ADSP_RTOS_CMD_FAIL: pr_info("adsp: module %s: CMD_FAIL\n", module->name); break; +#if CONFIG_MSM_AMSS_VERSION >= 6350 + case RPC_ADSP_RTOS_DISABLE_FAIL: + pr_info("adsp: module %s: DISABLE_FAIL\n", module->name); + break; +#endif default: pr_info("adsp: unknown event %d\n", event); rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, RPC_ACCEPTSTAT_GARBAGE_ARGS); - goto done; + mutex_unlock(&module->lock); + return; } rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, RPC_ACCEPTSTAT_SUCCESS); -done: mutex_unlock(&module->lock); #ifdef CONFIG_MSM_ADSP_REPORT_EVENTS modem_event_addr = (uint32_t *)req; - module->ops->event(module->driver_data, - EVENT_MSG_ID, - EVENT_LEN, - read_modem_event); + module->ops->event(module->driver_data, EVENT_MSG_ID, + EVENT_LEN, read_modem_event); #endif } @@ -578,6 +776,7 @@ static int adsp_rpc_thread(void *data) pr_err("adsp: bogus rpc from modem\n"); kfree(buffer); } while (1); + do_exit(0); } @@ -756,11 +955,20 @@ static irqreturn_t adsp_irq_handler(int irq, void *data) return IRQ_HANDLED; } +int adsp_set_clkrate(struct msm_adsp_module *module, unsigned long clk_rate) +{ + if (module->clk && clk_rate) + return clk_set_rate(module->clk, clk_rate); + + return -EINVAL; +} + int msm_adsp_enable(struct msm_adsp_module *module) { int rc = 0; - pr_info("msm_adsp_enable() '%s'\n", module->name); + pr_info("msm_adsp_enable() '%s'state[%d] id[%d]\n", + module->name, module->state, module->id); mutex_lock(&module->lock); switch (module->state) { @@ -800,6 +1008,7 @@ int msm_adsp_enable(struct msm_adsp_module *module) mutex_unlock(&module->lock); return rc; } +EXPORT_SYMBOL(msm_adsp_enable); static int msm_adsp_disable_locked(struct msm_adsp_module *module) { @@ -828,20 +1037,29 @@ int msm_adsp_disable(struct msm_adsp_module *module) mutex_unlock(&module->lock); return rc; } +EXPORT_SYMBOL(msm_adsp_disable); static int msm_adsp_probe(struct platform_device *pdev) { unsigned count; int rc, i; + pr_info("adsp: probe\n"); + wake_lock_init(&adsp_wake_lock, WAKE_LOCK_SUSPEND, "adsp"); +#if CONFIG_MSM_AMSS_VERSION >= 6350 + adsp_info.init_info_ptr = kzalloc( + (sizeof(struct adsp_rtos_mp_mtoa_init_info_type)), GFP_KERNEL); + if (!adsp_info.init_info_ptr) + return -ENOMEM; +#endif rc = adsp_init_info(&adsp_info); if (rc) return rc; - adsp_info.send_irq += MSM_AD5_BASE; - adsp_info.read_ctrl += MSM_AD5_BASE; - adsp_info.write_ctrl += MSM_AD5_BASE; + adsp_info.send_irq += (uint32_t) MSM_AD5_BASE; + adsp_info.read_ctrl += (uint32_t) MSM_AD5_BASE; + adsp_info.write_ctrl += (uint32_t) MSM_AD5_BASE; count = adsp_info.module_count; adsp_modules = kzalloc( @@ -897,7 +1115,11 @@ static int msm_adsp_probe(struct platform_device *pdev) INIT_HLIST_HEAD(&mod->pmem_regions); mod->pdev.name = adsp_info.module[i].pdev_name; mod->pdev.id = -1; +#if CONFIG_MSM_AMSS_VERSION >= 6350 + adsp_info.id_to_module[i] = mod; +#else adsp_info.id_to_module[mod->id] = mod; +#endif platform_device_register(&mod->pdev); } @@ -913,19 +1135,16 @@ static int msm_adsp_probe(struct platform_device *pdev) free_irq(INT_ADSP, 0); fail_request_irq: kfree(adsp_modules); +#if CONFIG_MSM_AMSS_VERSION >= 6350 + kfree(adsp_info.init_info_ptr); +#endif return rc; } static struct platform_driver msm_adsp_driver = { .probe = msm_adsp_probe, .driver = { -#if CONFIG_MSM_AMSS_VERSION == 6210 - .name = "rs3000000a:20f17fd3", -#elif (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225) - .name = "rs3000000a:71d1094b", -#else -#error "Unknown AMSS version" -#endif + .name = MSM_ADSP_DRIVER_NAME, .owner = THIS_MODULE, }, }; diff --git a/arch/arm/mach-msm/qdsp5/adsp.h b/arch/arm/mach-msm/qdsp5/adsp.h index 1b53d9edd86f9..0e5c9abd3da58 100644 --- a/arch/arm/mach-msm/qdsp5/adsp.h +++ b/arch/arm/mach-msm/qdsp5/adsp.h @@ -18,10 +18,6 @@ #ifndef _ARCH_ARM_MACH_MSM_ADSP_H #define _ARCH_ARM_MACH_MSM_ADSP_H -#if CONFIG_MSM_AMSS_VERSION>=6350 -#include "adsp_new.h" -#else - #include #include #include @@ -71,8 +67,8 @@ struct adsp_module_info { }; #define ADSP_EVENT_MAX_SIZE 496 -#define EVENT_LEN 12 -#define EVENT_MSG_ID ((uint16_t)~0) +#define EVENT_LEN 12 +#define EVENT_MSG_ID ((uint16_t)~0) struct adsp_event { struct list_head list; @@ -114,6 +110,14 @@ struct adsp_info { /* stats */ uint32_t events_received; uint32_t event_backlog_max; + +#if CONFIG_MSM_AMSS_VERSION >= 6350 + /* rpc_client for init_info */ + struct msm_rpc_endpoint *init_info_rpc_client; + struct adsp_rtos_mp_mtoa_init_info_type *init_info_ptr; + wait_queue_head_t init_info_wait; + unsigned init_info_state; +#endif }; #define RPC_ADSP_RTOS_ATOM_PROG 0x3000000a @@ -123,12 +127,18 @@ struct adsp_info { #define RPC_ADSP_RTOS_APP_TO_MODEM_PROC 2 #define RPC_ADSP_RTOS_MODEM_TO_APP_PROC 2 -#if CONFIG_MSM_AMSS_VERSION == 6210 -#define RPC_ADSP_RTOS_ATOM_VERS 0x20f17fd3 -#define RPC_ADSP_RTOS_MTOA_VERS 0x75babbd6 +#if CONFIG_MSM_AMSS_VERSION >= 6350 +#define RPC_ADSP_RTOS_ATOM_VERS MSM_RPC_VERS(1,0) +#define RPC_ADSP_RTOS_MTOA_VERS MSM_RPC_VERS(2,1) /* must be actual vers */ +#define MSM_ADSP_DRIVER_NAME "rs3000000a:00010000" #elif (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225) -#define RPC_ADSP_RTOS_ATOM_VERS 0x71d1094b -#define RPC_ADSP_RTOS_MTOA_VERS 0xee3a9966 +#define RPC_ADSP_RTOS_ATOM_VERS MSM_RPC_VERS(0x71d1094b, 0) +#define RPC_ADSP_RTOS_MTOA_VERS MSM_RPC_VERS(0xee3a9966, 0) +#define MSM_ADSP_DRIVER_NAME "rs3000000a:71d1094b" +#elif CONFIG_MSM_AMSS_VERSION == 6210 +#define RPC_ADSP_RTOS_ATOM_VERS MSM_RPC_VERS(0x20f17fd3, 0) +#define RPC_ADSP_RTOS_MTOA_VERS MSM_RPC_VERS(0x75babbd6, 0) +#define MSM_ADSP_DRIVER_NAME "rs3000000a:20f17fd3" #else #error "Unknown AMSS version" #endif @@ -149,6 +159,10 @@ enum { RPC_ADSP_RTOS_CMD_DISABLE_EVENT_RSP, RPC_ADSP_RTOS_CMD_REMOTE_EVENT, RPC_ADSP_RTOS_CMD_SET_STATE, +#if CONFIG_MSM_AMSS_VERSION >= 6350 + RPC_ADSP_RTOS_CMD_REMOTE_INIT_INFO_EVENT, + RPC_ADSP_RTOS_CMD_GET_INIT_INFO, +#endif }; enum rpc_adsp_rtos_mod_status_type { @@ -157,6 +171,10 @@ enum rpc_adsp_rtos_mod_status_type { RPC_ADSP_RTOS_SERVICE_RESET, RPC_ADSP_RTOS_CMD_FAIL, RPC_ADSP_RTOS_CMD_SUCCESS, +#if CONFIG_MSM_AMSS_VERSION >= 6350 + RPC_ADSP_RTOS_INIT_INFO, + RPC_ADSP_RTOS_DISABLE_FAIL, +#endif }; struct rpc_adsp_rtos_app_to_modem_args_t { @@ -167,6 +185,68 @@ struct rpc_adsp_rtos_app_to_modem_args_t { uint32_t module; /* e.g., QDSP_MODULE_AUDPPTASK */ }; +#if CONFIG_MSM_AMSS_VERSION >= 6350 +enum qdsp_image_type { + QDSP_IMAGE_COMBO, + QDSP_IMAGE_GAUDIO, + QDSP_IMAGE_QTV_LP, + QDSP_IMAGE_MAX, + /* DO NOT USE: Force this enum to be a 32bit type to improve speed */ + QDSP_IMAGE_32BIT_DUMMY = 0x10000 +}; + +struct adsp_rtos_mp_mtoa_header_type { + enum rpc_adsp_rtos_mod_status_type event; + enum rpc_adsp_rtos_proc_type proc_id; +}; + +/* ADSP RTOS MP Communications - Modem to APP's Event Info*/ +struct adsp_rtos_mp_mtoa_type { + uint32_t module; + uint32_t image; + uint32_t apps_okts; +}; + +/* ADSP RTOS MP Communications - Modem to APP's Init Info */ +#define IMG_MAX 8 +#define ENTRIES_MAX 64 + +struct queue_to_offset_type { + uint32_t queue; + uint32_t offset; +}; + +struct adsp_rtos_mp_mtoa_init_info_type { + uint32_t image_count; + uint32_t num_queue_offsets; + struct queue_to_offset_type queue_offsets_tbl[IMG_MAX][ENTRIES_MAX]; + uint32_t num_task_module_entries; + uint32_t task_to_module_tbl[IMG_MAX][ENTRIES_MAX]; + + uint32_t module_table_size; + uint32_t module_entries[ENTRIES_MAX]; + /* + * queue_offsets[] is to store only queue_offsets + */ + uint32_t queue_offsets[IMG_MAX][ENTRIES_MAX]; +}; + +struct adsp_rtos_mp_mtoa_s_type { + struct adsp_rtos_mp_mtoa_header_type mp_mtoa_header; + + uint32_t desc_field; + union { + struct adsp_rtos_mp_mtoa_init_info_type mp_mtoa_init_packet; + struct adsp_rtos_mp_mtoa_type mp_mtoa_packet; + } adsp_rtos_mp_mtoa_data; +}; + +struct rpc_adsp_rtos_modem_to_app_args_t { + struct rpc_request_hdr hdr; + uint32_t gotit; /* if 1, the next elements are present */ + struct adsp_rtos_mp_mtoa_s_type mtoa_pkt; +}; +#else struct rpc_adsp_rtos_modem_to_app_args_t { struct rpc_request_hdr hdr; uint32_t gotit; /* if 1, the next elements are present */ @@ -175,11 +255,15 @@ struct rpc_adsp_rtos_modem_to_app_args_t { uint32_t module; /* e.g., QDSP_MODULE_AUDPPTASK */ uint32_t image; /* RPC_QDSP_IMAGE_GAUDIO */ }; +#endif /* CONFIG_MSM_AMSS_VERSION >= 6350 */ #define ADSP_STATE_DISABLED 0 #define ADSP_STATE_ENABLING 1 #define ADSP_STATE_ENABLED 2 #define ADSP_STATE_DISABLING 3 +#if CONFIG_MSM_AMSS_VERSION >= 6350 +#define ADSP_STATE_INIT_INFO 4 +#endif struct msm_adsp_module { struct mutex lock; @@ -211,11 +295,13 @@ struct msm_adsp_module { extern void msm_adsp_publish_cdevs(struct msm_adsp_module *, unsigned); extern int adsp_init_info(struct adsp_info *info); -extern struct msm_adsp_module *find_adsp_module_by_id(struct adsp_info *info, - uint32_t id); /* Value to indicate that a queue is not defined for a particular image */ +#if CONFIG_MSM_AMSS_VERSION >= 6350 +#define QDSP_RTOS_NO_QUEUE 0xfffffffe +#else #define QDSP_RTOS_NO_QUEUE 0xffffffff +#endif /* * Constants used to communicate with the ADSP RTOS @@ -280,5 +366,4 @@ extern struct msm_adsp_module *find_adsp_module_by_id(struct adsp_info *info, /* Base address of DSP and DSP hardware registers */ #define QDSP_RAMC_OFFSET 0x400000 -#endif /* CONFIG_MSM_AMSS_VERSION>=6350 */ -#endif +#endif /* _ARCH_ARM_MACH_MSM_ADSP_H */ diff --git a/arch/arm/mach-msm/qdsp5/adsp_new.c b/arch/arm/mach-msm/qdsp5/adsp_new.c deleted file mode 100644 index 026cb6818bb02..0000000000000 --- a/arch/arm/mach-msm/qdsp5/adsp_new.c +++ /dev/null @@ -1,1123 +0,0 @@ -/* arch/arm/mach-msm/qdsp5/adsp.c - * - * Register/Interrupt access for userspace aDSP library. - * - * Copyright (c) 2008 QUALCOMM Incorporated - * Copyright (C) 2008 Google, Inc. - * Author: Iliyan Malchev - * - * 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. - * - */ - -/* TODO: - * - move shareable rpc code outside of adsp.c - * - general solution for virt->phys patchup - * - queue IDs should be relative to modules - * - disallow access to non-associated queues - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static struct wake_lock adsp_wake_lock; -static inline void prevent_suspend(void) -{ - wake_lock(&adsp_wake_lock); -} -static inline void allow_suspend(void) -{ - wake_unlock(&adsp_wake_lock); -} - -#include -#include -#include "adsp.h" - -#define INT_ADSP INT_ADSP_A9_A11 - -static struct adsp_info adsp_info; -static struct msm_rpc_endpoint *rpc_cb_server_client; -static struct msm_adsp_module *adsp_modules; -static int adsp_open_count; - -#define RPC_ADSP_RTOS_ATOM_PROG 0x3000000a -#define RPC_ADSP_RTOS_ATOM_VERS MSM_RPC_VERS(1,0) -#define RPC_ADSP_RTOS_MTOA_PROG 0x3000000b -/* WARNING: this must be the actual version: the modem can't figure out that - * this is a backwards-compatible server. */ -#define RPC_ADSP_RTOS_MTOA_VERS MSM_RPC_VERS(2,1) -static DEFINE_MUTEX(adsp_open_lock); - -/* protect interactions with the ADSP command/message queue */ -static spinlock_t adsp_cmd_lock; - -static uint32_t current_image = -1; - -void adsp_set_image(struct adsp_info *info, uint32_t image) -{ - current_image = image; -} - -/* - * Checks whether the module_id is available in the - * module_entries table.If module_id is available returns `0`. - * If module_id is not available returns `-ENXIO`. - */ -static int32_t adsp_validate_module(uint32_t module_id) -{ - uint32_t *ptr; - uint32_t module_index; - uint32_t num_mod_entries; - - ptr = adsp_info.init_info_ptr->module_entries; - num_mod_entries = adsp_info.init_info_ptr->module_table_size; - - for (module_index = 0; module_index < num_mod_entries; module_index++) - if (module_id == ptr[module_index]) - return 0; - - return -ENXIO; -} - -uint32_t adsp_get_module(struct adsp_info *info, uint32_t task) -{ - BUG_ON(current_image == -1UL); - return info->task_to_module[current_image][task]; -} - -uint32_t adsp_get_queue_offset(struct adsp_info *info, uint32_t queue_id) -{ - return info->queue_offset[current_image][queue_id]; -} - -static int rpc_adsp_rtos_app_to_modem(uint32_t cmd, uint32_t module, - struct msm_adsp_module *adsp_module) -{ - int rc; - struct rpc_adsp_rtos_app_to_modem_args_t rpc_req; - struct rpc_reply_hdr *rpc_rsp; - - msm_rpc_setup_req(&rpc_req.hdr, - RPC_ADSP_RTOS_ATOM_PROG, - msm_rpc_get_vers(adsp_module->rpc_client), - RPC_ADSP_RTOS_APP_TO_MODEM_PROC); - - rpc_req.gotit = cpu_to_be32(1); - rpc_req.cmd = cpu_to_be32(cmd); - rpc_req.proc_id = cpu_to_be32(RPC_ADSP_RTOS_PROC_APPS); - rpc_req.module = cpu_to_be32(module); - rc = msm_rpc_write(adsp_module->rpc_client, &rpc_req, sizeof(rpc_req)); - if (rc < 0) { - pr_err("adsp: could not send RPC request: %d\n", rc); - return rc; - } - - rc = msm_rpc_read(adsp_module->rpc_client, - (void **)&rpc_rsp, -1, (5*HZ)); - if (rc < 0) { - pr_err("adsp: error receiving RPC reply: %d (%d)\n", - rc, -ERESTARTSYS); - return rc; - } - - if (be32_to_cpu(rpc_rsp->reply_stat) != RPCMSG_REPLYSTAT_ACCEPTED) { - pr_err("adsp: RPC call was denied!\n"); - kfree(rpc_rsp); - return -EPERM; - } - - if (be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat) != - RPC_ACCEPTSTAT_SUCCESS) { - pr_err("adsp error: RPC call was not successful (%d)\n", - be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat)); - kfree(rpc_rsp); - return -EINVAL; - } - - kfree(rpc_rsp); - return 0; -} - -static int get_module_index(uint32_t id) -{ - int mod_idx; - for (mod_idx = 0; mod_idx < adsp_info.module_count; mod_idx++) - if (adsp_info.module[mod_idx].id == id) - return mod_idx; - - return -ENXIO; -} - -static struct msm_adsp_module *find_adsp_module_by_id( - struct adsp_info *info, uint32_t id) -{ - int mod_idx; - - if (id > info->max_module_id) { - return NULL; - } else { - mod_idx = get_module_index(id); - if (mod_idx < 0) - return NULL; - return info->id_to_module[mod_idx]; - } -} - -static struct msm_adsp_module *find_adsp_module_by_name( - struct adsp_info *info, const char *name) -{ - unsigned n; - for (n = 0; n < info->module_count; n++) - if (!strcmp(name, adsp_modules[n].name)) - return adsp_modules + n; - return NULL; -} - -static int adsp_rpc_init(struct msm_adsp_module *adsp_module) -{ - /* remove the original connect once compatible support is complete */ - adsp_module->rpc_client = msm_rpc_connect( - RPC_ADSP_RTOS_ATOM_PROG, - RPC_ADSP_RTOS_ATOM_VERS, - MSM_RPC_UNINTERRUPTIBLE); - - if (IS_ERR(adsp_module->rpc_client)) { - int rc = PTR_ERR(adsp_module->rpc_client); - adsp_module->rpc_client = 0; - pr_err("adsp: could not open rpc client: %d\n", rc); - return rc; - } - - return 0; -} - -/* - * Send RPC_ADSP_RTOS_CMD_GET_INIT_INFO cmd to ARM9 and get - * queue offsets and module entries (init info) as part of the event. - */ -static void msm_get_init_info(void) -{ - int rc; - struct rpc_adsp_rtos_app_to_modem_args_t rpc_req; - - adsp_info.init_info_rpc_client = msm_rpc_connect( - RPC_ADSP_RTOS_ATOM_PROG, - RPC_ADSP_RTOS_ATOM_VERS, - MSM_RPC_UNINTERRUPTIBLE); - if (IS_ERR(adsp_info.init_info_rpc_client)) { - rc = PTR_ERR(adsp_info.init_info_rpc_client); - adsp_info.init_info_rpc_client = 0; - pr_err("adsp: could not open rpc client: %d\n", rc); - return; - } - - msm_rpc_setup_req(&rpc_req.hdr, - RPC_ADSP_RTOS_ATOM_PROG, - msm_rpc_get_vers(adsp_info.init_info_rpc_client), - RPC_ADSP_RTOS_APP_TO_MODEM_PROC); - - rpc_req.gotit = cpu_to_be32(1); - rpc_req.cmd = cpu_to_be32(RPC_ADSP_RTOS_CMD_GET_INIT_INFO); - rpc_req.proc_id = cpu_to_be32(RPC_ADSP_RTOS_PROC_APPS); - rpc_req.module = 0; - - rc = msm_rpc_write(adsp_info.init_info_rpc_client, - &rpc_req, sizeof(rpc_req)); - if (rc < 0) - pr_err("adsp: could not send RPC request: %d\n", rc); -} - -int msm_adsp_get(const char *name, struct msm_adsp_module **out, - struct msm_adsp_ops *ops, void *driver_data) -{ - struct msm_adsp_module *module; - int rc = 0; - static uint32_t init_info_cmd_sent; - - if (!init_info_cmd_sent) { - msm_get_init_info(); - init_waitqueue_head(&adsp_info.init_info_wait); - rc = wait_event_timeout(adsp_info.init_info_wait, - adsp_info.init_info_state == ADSP_STATE_INIT_INFO, - 5 * HZ); - if (!rc) { - pr_info("adsp: INIT_INFO failed\n"); - return -ETIMEDOUT; - } - init_info_cmd_sent++; - } - - module = find_adsp_module_by_name(&adsp_info, name); - if (!module) - return -ENODEV; - - mutex_lock(&module->lock); - pr_info("adsp: opening module %s\n", module->name); - if (module->open_count++ == 0 && module->clk) - clk_enable(module->clk); - - mutex_lock(&adsp_open_lock); - if (adsp_open_count++ == 0) { - enable_irq(INT_ADSP); - prevent_suspend(); - } - mutex_unlock(&adsp_open_lock); - - if (module->ops) { - rc = -EBUSY; - goto done; - } - - rc = adsp_rpc_init(module); - if (rc) - goto done; - - module->ops = ops; - module->driver_data = driver_data; - *out = module; - rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_REGISTER_APP, - module->id, module); - if (rc) { - module->ops = NULL; - module->driver_data = NULL; - *out = NULL; - pr_err("adsp: REGISTER_APP failed\n"); - goto done; - } - - pr_info("adsp: module %s has been registered\n", module->name); - -done: - mutex_lock(&adsp_open_lock); - if (rc && --adsp_open_count == 0) { - disable_irq(INT_ADSP); - allow_suspend(); - } - if (rc && --module->open_count == 0 && module->clk) - clk_disable(module->clk); - mutex_unlock(&adsp_open_lock); - mutex_unlock(&module->lock); - return rc; -} -EXPORT_SYMBOL(msm_adsp_get); - -static int msm_adsp_disable_locked(struct msm_adsp_module *module); - -void msm_adsp_put(struct msm_adsp_module *module) -{ - unsigned long flags; - - mutex_lock(&module->lock); - if (--module->open_count == 0 && module->clk) - clk_disable(module->clk); - if (module->ops) { - pr_info("adsp: closing module %s\n", module->name); - - /* lock to ensure a dsp event cannot be delivered - * during or after removal of the ops and driver_data - */ - spin_lock_irqsave(&adsp_cmd_lock, flags); - module->ops = NULL; - module->driver_data = NULL; - spin_unlock_irqrestore(&adsp_cmd_lock, flags); - - if (module->state != ADSP_STATE_DISABLED) { - pr_info("adsp: disabling module %s\n", module->name); - msm_adsp_disable_locked(module); - } - - msm_rpc_close(module->rpc_client); - module->rpc_client = 0; - if (--adsp_open_count == 0) { - disable_irq(INT_ADSP); - allow_suspend(); - pr_info("adsp: disable interrupt\n"); - } - } else { - pr_info("adsp: module %s is already closed\n", module->name); - } - mutex_unlock(&module->lock); -} -EXPORT_SYMBOL(msm_adsp_put); - -/* this should be common code with rpc_servers.c */ -static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client, - uint32_t xid, uint32_t accept_status) -{ - int rc = 0; - uint8_t reply_buf[sizeof(struct rpc_reply_hdr)]; - struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf; - - reply->xid = cpu_to_be32(xid); - reply->type = cpu_to_be32(1); /* reply */ - reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); - - reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status); - reply->data.acc_hdr.verf_flavor = 0; - reply->data.acc_hdr.verf_length = 0; - - rc = msm_rpc_write(rpc_cb_server_client, reply_buf, sizeof(reply_buf)); - if (rc < 0) - pr_err("adsp: could not write RPC response: %d\n", rc); - return rc; -} - -int __msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, - void *cmd_buf, size_t cmd_size) -{ - uint32_t ctrl_word; - uint32_t dsp_q_addr; - uint32_t dsp_addr; - uint32_t cmd_id = 0; - int cnt = 0; - int ret_status = 0; - unsigned long flags; - struct adsp_info *info = module->info; - - spin_lock_irqsave(&adsp_cmd_lock, flags); - - if (module->state != ADSP_STATE_ENABLED) { - spin_unlock_irqrestore(&adsp_cmd_lock, flags); - pr_err("adsp: module %s not enabled before write\n", - module->name); - return -ENODEV; - } - if (adsp_validate_module(module->id)) { - spin_unlock_irqrestore(&adsp_cmd_lock, flags); - pr_info("adsp: module id validation failed %s %d\n", - module->name, module->id); - return -ENXIO; - } - dsp_q_addr = adsp_get_queue_offset(info, dsp_queue_addr); - dsp_q_addr &= ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M; - - /* Poll until the ADSP is ready to accept a command. - * Wait for 100us, return error if it's not responding. - * If this returns an error, we need to disable ALL modules and - * then retry. - */ - while (((ctrl_word = readl(info->write_ctrl)) & - ADSP_RTOS_WRITE_CTRL_WORD_READY_M) != - ADSP_RTOS_WRITE_CTRL_WORD_READY_V) { - if (cnt > 100) { - pr_err("adsp: timeout waiting for DSP write ready\n"); - ret_status = -EIO; - goto fail; - } - pr_warning("adsp: waiting for DSP write ready\n"); - udelay(1); - cnt++; - } - - /* Set the mutex bits */ - ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M); - ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V; - - /* Clear the command bits */ - ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M); - - /* Set the queue address bits */ - ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M); - ctrl_word |= dsp_q_addr; - - writel(ctrl_word, info->write_ctrl); - - /* Generate an interrupt to the DSP. This notifies the DSP that - * we are about to send a command on this particular queue. The - * DSP will in response change its state. - */ - writel(1, info->send_irq); - - /* Poll until the adsp responds to the interrupt; this does not - * generate an interrupt from the adsp. This should happen within - * 5ms. - */ - cnt = 0; - while ((readl(info->write_ctrl) & - ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M) == - ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V) { - if (cnt > 5000) { - pr_err("adsp: timeout waiting for adsp ack\n"); - ret_status = -EIO; - goto fail; - } - udelay(1); - cnt++; - } - - /* Read the ctrl word */ - ctrl_word = readl(info->write_ctrl); - - if ((ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M) != - ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V) { - ret_status = -EAGAIN; - goto fail; - } - - /* Ctrl word status bits were 00, no error in the ctrl word */ - - /* Get the DSP buffer address */ - dsp_addr = (ctrl_word & ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M) + - (uint32_t)MSM_AD5_BASE; - - if (dsp_addr < (uint32_t)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) { - uint16_t *buf_ptr = (uint16_t *) cmd_buf; - uint16_t *dsp_addr16 = (uint16_t *)dsp_addr; - cmd_size /= sizeof(uint16_t); - - /* Save the command ID */ - cmd_id = (uint32_t) buf_ptr[0]; - - /* Copy the command to DSP memory */ - cmd_size++; - while (--cmd_size) - *dsp_addr16++ = *buf_ptr++; - } else { - uint32_t *buf_ptr = (uint32_t *) cmd_buf; - uint32_t *dsp_addr32 = (uint32_t *)dsp_addr; - cmd_size /= sizeof(uint32_t); - - /* Save the command ID */ - cmd_id = buf_ptr[0]; - - cmd_size++; - while (--cmd_size) - *dsp_addr32++ = *buf_ptr++; - } - - /* Set the mutex bits */ - ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M); - ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V; - - /* Set the command bits to write done */ - ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_CMD_M); - ctrl_word |= ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V; - - /* Set the queue address bits */ - ctrl_word &= ~(ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M); - ctrl_word |= dsp_q_addr; - - writel(ctrl_word, info->write_ctrl); - - /* Generate an interrupt to the DSP. It does not respond with - * an interrupt, and we do not need to wait for it to - * acknowledge, because it will hold the mutex lock until it's - * ready to receive more commands again. - */ - writel(1, info->send_irq); - - module->num_commands++; - -fail: - spin_unlock_irqrestore(&adsp_cmd_lock, flags); - return ret_status; -} -EXPORT_SYMBOL(msm_adsp_write); - -int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, - void *cmd_buf, size_t cmd_size) -{ - int rc, retries = 0; - do { - rc = __msm_adsp_write(module, dsp_queue_addr, cmd_buf, cmd_size); - if (rc == -EAGAIN) - udelay(10); - } while(rc == -EAGAIN && retries++ < 100); - if (retries > 50) - pr_warning("adsp: %s command took %d attempts: rc %d\n", - module->name, retries, rc); - return rc; -} - -#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS -static void *event_addr; -static void read_event(void *buf, size_t len) -{ - uint32_t *dptr = buf; - struct rpc_adsp_rtos_modem_to_app_args_t *sptr; - struct adsp_rtos_mp_mtoa_type *pkt_ptr; - - sptr = event_addr; - pkt_ptr = &sptr->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_packet; - - dptr[0] = be32_to_cpu(sptr->mtoa_pkt.mp_mtoa_header.event); - dptr[1] = be32_to_cpu(pkt_ptr->module); - dptr[2] = be32_to_cpu(pkt_ptr->image); -} -#endif - -static void handle_adsp_rtos_mtoa_app(struct rpc_request_hdr *req) -{ - struct rpc_adsp_rtos_modem_to_app_args_t *args = - (struct rpc_adsp_rtos_modem_to_app_args_t *)req; - uint32_t event; - uint32_t proc_id; - uint32_t desc_field; - uint32_t module_id; - uint32_t image; - struct msm_adsp_module *module; - struct adsp_rtos_mp_mtoa_type *pkt_ptr; - struct queue_to_offset_type *qptr; - struct queue_to_offset_type *qtbl; - uint32_t *mptr; - uint32_t *mtbl; - uint32_t q_idx; - uint32_t num_entries; - uint32_t entries_per_image; - struct adsp_rtos_mp_mtoa_init_info_type *iptr; - struct adsp_rtos_mp_mtoa_init_info_type *sptr; - int32_t i_no, e_idx; - - event = be32_to_cpu(args->mtoa_pkt.mp_mtoa_header.event); - proc_id = be32_to_cpu(args->mtoa_pkt.mp_mtoa_header.proc_id); - desc_field = be32_to_cpu(args->mtoa_pkt.desc_field); - - if (desc_field == RPC_ADSP_RTOS_INIT_INFO) { - pr_info("adsp:INIT_INFO Event\n"); - sptr = &args->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_init_packet; - - iptr = adsp_info.init_info_ptr; - iptr->image_count = be32_to_cpu(sptr->image_count); - iptr->num_queue_offsets = be32_to_cpu(sptr->num_queue_offsets); - num_entries = iptr->num_queue_offsets; - qptr = &sptr->queue_offsets_tbl[0][0]; - for (i_no = 0; i_no < iptr->image_count; i_no++) { - qtbl = &iptr->queue_offsets_tbl[i_no][0]; - for (e_idx = 0; e_idx < num_entries; e_idx++) { - qtbl[e_idx].offset = be32_to_cpu(qptr->offset); - qtbl[e_idx].queue = be32_to_cpu(qptr->queue); - q_idx = be32_to_cpu(qptr->queue); - iptr->queue_offsets[i_no][q_idx] = qtbl[e_idx].offset; - qptr++; - } - } - - num_entries = be32_to_cpu(sptr->num_task_module_entries); - iptr->num_task_module_entries = num_entries; - entries_per_image = num_entries / iptr->image_count; - mptr = &sptr->task_to_module_tbl[0][0]; - for (i_no = 0; i_no < iptr->image_count; i_no++) { - mtbl = &iptr->task_to_module_tbl[i_no][0]; - for (e_idx = 0; e_idx < entries_per_image; e_idx++) { - mtbl[e_idx] = be32_to_cpu(*mptr); - mptr++; - } - } - - iptr->module_table_size = be32_to_cpu(sptr->module_table_size); - mptr = &sptr->module_entries[0]; - for (i_no = 0; i_no < iptr->module_table_size; i_no++) - iptr->module_entries[i_no] = be32_to_cpu(mptr[i_no]); - adsp_info.init_info_state = ADSP_STATE_INIT_INFO; - rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, - RPC_ACCEPTSTAT_SUCCESS); - wake_up(&adsp_info.init_info_wait); - - return; - } - - pkt_ptr = &args->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_packet; - module_id = be32_to_cpu(pkt_ptr->module); - image = be32_to_cpu(pkt_ptr->image); - - pr_info("adsp: rpc event=%d, proc_id=%d, module=%d, image=%d\n", - event, proc_id, module_id, image); - - module = find_adsp_module_by_id(&adsp_info, module_id); - if (!module) { - pr_err("adsp: module %d is not supported!\n", module_id); - rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, - RPC_ACCEPTSTAT_GARBAGE_ARGS); - return; - } - - mutex_lock(&module->lock); - switch (event) { - case RPC_ADSP_RTOS_MOD_READY: - pr_info("adsp: module %s: READY\n", module->name); - module->state = ADSP_STATE_ENABLED; - wake_up(&module->state_wait); - adsp_set_image(module->info, image); - break; - case RPC_ADSP_RTOS_MOD_DISABLE: - pr_info("adsp: module %s: DISABLED\n", module->name); - module->state = ADSP_STATE_DISABLED; - wake_up(&module->state_wait); - break; - case RPC_ADSP_RTOS_SERVICE_RESET: - pr_info("adsp: module %s: SERVICE_RESET\n", module->name); - module->state = ADSP_STATE_DISABLED; - wake_up(&module->state_wait); - break; - case RPC_ADSP_RTOS_CMD_SUCCESS: - pr_info("adsp: module %s: CMD_SUCCESS\n", module->name); - break; - case RPC_ADSP_RTOS_CMD_FAIL: - pr_info("adsp: module %s: CMD_FAIL\n", module->name); - break; - case RPC_ADSP_RTOS_DISABLE_FAIL: - pr_info("adsp: module %s: DISABLE_FAIL\n", module->name); - break; - default: - pr_info("adsp: unknown event %d\n", event); - rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, - RPC_ACCEPTSTAT_GARBAGE_ARGS); - mutex_unlock(&module->lock); - return; - } - rpc_send_accepted_void_reply(rpc_cb_server_client, req->xid, - RPC_ACCEPTSTAT_SUCCESS); - mutex_unlock(&module->lock); -#ifdef CONFIG_MSM_ADSP_REPORT_EVENTS - event_addr = (uint32_t *)req; - module->ops->event(module->driver_data, EVENT_MSG_ID, - EVENT_LEN, read_event); -#endif -} - -static int handle_adsp_rtos_mtoa(struct rpc_request_hdr *req) -{ - switch (req->procedure) { - case RPC_ADSP_RTOS_MTOA_NULL_PROC: - rpc_send_accepted_void_reply(rpc_cb_server_client, - req->xid, - RPC_ACCEPTSTAT_SUCCESS); - break; - case RPC_ADSP_RTOS_MODEM_TO_APP_PROC: - handle_adsp_rtos_mtoa_app(req); - break; - default: - pr_err("adsp: unknowned proc %d\n", req->procedure); - rpc_send_accepted_void_reply( - rpc_cb_server_client, req->xid, - RPC_ACCEPTSTAT_PROC_UNAVAIL); - break; - } - return 0; -} - -/* this should be common code with rpc_servers.c */ -static int adsp_rpc_thread(void *data) -{ - void *buffer; - struct rpc_request_hdr *req; - int rc; - - do { - rc = msm_rpc_read(rpc_cb_server_client, &buffer, -1, -1); - if (rc < 0) { - pr_err("adsp: could not read rpc: %d\n", rc); - break; - } - req = (struct rpc_request_hdr *)buffer; - - req->type = be32_to_cpu(req->type); - req->xid = be32_to_cpu(req->xid); - req->rpc_vers = be32_to_cpu(req->rpc_vers); - req->prog = be32_to_cpu(req->prog); - req->vers = be32_to_cpu(req->vers); - req->procedure = be32_to_cpu(req->procedure); - - if (req->type != 0) - goto bad_rpc; - if (req->rpc_vers != 2) - goto bad_rpc; - if (req->prog != RPC_ADSP_RTOS_MTOA_PROG) - goto bad_rpc; - if (req->vers != RPC_ADSP_RTOS_MTOA_VERS) - goto bad_rpc; - - handle_adsp_rtos_mtoa(req); - kfree(buffer); - continue; - -bad_rpc: - pr_err("adsp: bogus rpc from modem\n"); - kfree(buffer); - } while (1); - - do_exit(0); -} - -static size_t read_event_size; -static void *read_event_addr; - -static void read_event_16(void *buf, size_t len) -{ - uint16_t *dst = buf; - uint16_t *src = read_event_addr; - len /= 2; - if (len > read_event_size) - len = read_event_size; - while (len--) - *dst++ = *src++; -} - -static void read_event_32(void *buf, size_t len) -{ - uint32_t *dst = buf; - uint32_t *src = read_event_addr; - len /= 2; - if (len > read_event_size) - len = read_event_size; - while (len--) - *dst++ = *src++; -} - -static int adsp_rtos_read_ctrl_word_cmd_tast_to_h_v( - struct adsp_info *info, void *dsp_addr) -{ - struct msm_adsp_module *module; - unsigned rtos_task_id; - unsigned msg_id; - unsigned msg_length; - void (*func)(void *, size_t); - - if (dsp_addr >= (void *)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) { - uint32_t *dsp_addr32 = dsp_addr; - uint32_t tmp = *dsp_addr32++; - rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8; - msg_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M); - read_event_size = tmp >> 16; - read_event_addr = dsp_addr32; - msg_length = read_event_size * sizeof(uint32_t); - func = read_event_32; - } else { - uint16_t *dsp_addr16 = dsp_addr; - uint16_t tmp = *dsp_addr16++; - rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8; - msg_id = tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M; - read_event_size = *dsp_addr16++; - read_event_addr = dsp_addr16; - msg_length = read_event_size * sizeof(uint16_t); - func = read_event_16; - } - - if (rtos_task_id > info->max_task_id) { - pr_err("adsp: bogus task id %d\n", rtos_task_id); - return 0; - } - module = find_adsp_module_by_id(info, - adsp_get_module(info, rtos_task_id)); - - if (!module) { - pr_err("adsp: no module for task id %d\n", rtos_task_id); - return 0; - } - - module->num_events++; - - if (!module->ops) { - pr_err("adsp: module %s is not open\n", module->name); - return 0; - } - - module->ops->event(module->driver_data, msg_id, msg_length, func); - return 0; -} - -static int adsp_get_event(struct adsp_info *info) -{ - uint32_t ctrl_word; - uint32_t ready; - void *dsp_addr; - uint32_t cmd_type; - int cnt; - unsigned long flags; - int rc = 0; - - spin_lock_irqsave(&adsp_cmd_lock, flags); - - /* Whenever the DSP has a message, it updates this control word - * and generates an interrupt. When we receive the interrupt, we - * read this register to find out what ADSP task the command is - * comming from. - * - * The ADSP should *always* be ready on the first call, but the - * irq handler calls us in a loop (to handle back-to-back command - * processing), so we give the DSP some time to return to the - * ready state. The DSP will not issue another IRQ for events - * pending between the first IRQ and the event queue being drained, - * unfortunately. - */ - - for (cnt = 0; cnt < 10; cnt++) { - ctrl_word = readl(info->read_ctrl); - - if ((ctrl_word & ADSP_RTOS_READ_CTRL_WORD_FLAG_M) == - ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V) - goto ready; - - udelay(10); - } - pr_warning("adsp: not ready after 100uS\n"); - rc = -EBUSY; - goto done; - -ready: - /* Here we check to see if there are pending messages. If there are - * none, we siply return -EAGAIN to indicate that there are no more - * messages pending. - */ - ready = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_READY_M; - if ((ready != ADSP_RTOS_READ_CTRL_WORD_READY_V) && - (ready != ADSP_RTOS_READ_CTRL_WORD_CONT_V)) { - rc = -EAGAIN; - goto done; - } - - /* DSP says that there are messages waiting for the host to read */ - - /* Get the Command Type */ - cmd_type = ctrl_word & ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M; - - /* Get the DSP buffer address */ - dsp_addr = (void *)((ctrl_word & - ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M) + - (uint32_t)MSM_AD5_BASE); - - /* We can only handle Task-to-Host messages */ - if (cmd_type != ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V) { - pr_err("adsp: unknown dsp cmd_type %d\n", cmd_type); - rc = -EIO; - goto done; - } - - adsp_rtos_read_ctrl_word_cmd_tast_to_h_v(info, dsp_addr); - - ctrl_word = readl(info->read_ctrl); - ctrl_word &= ~ADSP_RTOS_READ_CTRL_WORD_READY_M; - - /* Write ctrl word to the DSP */ - writel(ctrl_word, info->read_ctrl); - - /* Generate an interrupt to the DSP */ - writel(1, info->send_irq); - -done: - spin_unlock_irqrestore(&adsp_cmd_lock, flags); - return rc; -} - -static irqreturn_t adsp_irq_handler(int irq, void *data) -{ - struct adsp_info *info = &adsp_info; - int cnt = 0; - for (cnt = 0; cnt < 10; cnt++) - if (adsp_get_event(info) < 0) - break; - if (cnt > info->event_backlog_max) - info->event_backlog_max = cnt; - info->events_received += cnt; - if (cnt == 10) - pr_err("adsp: too many (%d) events for single irq!\n", cnt); - return IRQ_HANDLED; -} - -int adsp_set_clkrate(struct msm_adsp_module *module, unsigned long clk_rate) -{ - if (module->clk && clk_rate) - return clk_set_rate(module->clk, clk_rate); - - return -EINVAL; -} - -int msm_adsp_enable(struct msm_adsp_module *module) -{ - int rc = 0; - - pr_info("msm_adsp_enable() '%s'state[%d] id[%d]\n", - module->name, module->state, module->id); - - mutex_lock(&module->lock); - switch (module->state) { - case ADSP_STATE_DISABLED: - rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_ENABLE, - module->id, module); - if (rc) - break; - module->state = ADSP_STATE_ENABLING; - mutex_unlock(&module->lock); - rc = wait_event_timeout(module->state_wait, - module->state != ADSP_STATE_ENABLING, - 1 * HZ); - mutex_lock(&module->lock); - if (module->state == ADSP_STATE_ENABLED) { - rc = 0; - } else { - pr_err("adsp: module '%s' enable timed out\n", - module->name); - rc = -ETIMEDOUT; - } - break; - case ADSP_STATE_ENABLING: - pr_warning("adsp: module '%s' enable in progress\n", - module->name); - break; - case ADSP_STATE_ENABLED: - pr_warning("adsp: module '%s' already enabled\n", - module->name); - break; - case ADSP_STATE_DISABLING: - pr_err("adsp: module '%s' disable in progress\n", - module->name); - rc = -EBUSY; - break; - } - mutex_unlock(&module->lock); - return rc; -} -EXPORT_SYMBOL(msm_adsp_enable); - -static int msm_adsp_disable_locked(struct msm_adsp_module *module) -{ - int rc = 0; - - switch (module->state) { - case ADSP_STATE_DISABLED: - pr_warning("adsp: module '%s' already disabled\n", - module->name); - break; - case ADSP_STATE_ENABLING: - case ADSP_STATE_ENABLED: - rc = rpc_adsp_rtos_app_to_modem(RPC_ADSP_RTOS_CMD_DISABLE, - module->id, module); - module->state = ADSP_STATE_DISABLED; - } - return rc; -} - -int msm_adsp_disable(struct msm_adsp_module *module) -{ - int rc; - pr_info("msm_adsp_disable() '%s'\n", module->name); - mutex_lock(&module->lock); - rc = msm_adsp_disable_locked(module); - mutex_unlock(&module->lock); - return rc; -} -EXPORT_SYMBOL(msm_adsp_disable); - -static int msm_adsp_probe(struct platform_device *pdev) -{ - unsigned count; - int rc, i; - - pr_info("adsp: probe\n"); - - wake_lock_init(&adsp_wake_lock, WAKE_LOCK_SUSPEND, "adsp"); - adsp_info.init_info_ptr = kzalloc( - (sizeof(struct adsp_rtos_mp_mtoa_init_info_type)), GFP_KERNEL); - if (!adsp_info.init_info_ptr) - return -ENOMEM; - - rc = adsp_init_info(&adsp_info); - if (rc) - return rc; - adsp_info.send_irq += (uint32_t) MSM_AD5_BASE; - adsp_info.read_ctrl += (uint32_t) MSM_AD5_BASE; - adsp_info.write_ctrl += (uint32_t) MSM_AD5_BASE; - count = adsp_info.module_count; - - adsp_modules = kzalloc( - (sizeof(struct msm_adsp_module) + sizeof(void *)) * - count, GFP_KERNEL); - if (!adsp_modules) - return -ENOMEM; - - adsp_info.id_to_module = (void *) (adsp_modules + count); - - spin_lock_init(&adsp_cmd_lock); - - rc = request_irq(INT_ADSP, adsp_irq_handler, IRQF_TRIGGER_RISING, - "adsp", 0); - if (rc < 0) - goto fail_request_irq; - disable_irq(INT_ADSP); - - rpc_cb_server_client = msm_rpc_open(); - if (IS_ERR(rpc_cb_server_client)) { - rpc_cb_server_client = NULL; - rc = PTR_ERR(rpc_cb_server_client); - pr_err("adsp: could not create rpc server (%d)\n", rc); - goto fail_rpc_open; - } - - rc = msm_rpc_register_server(rpc_cb_server_client, - RPC_ADSP_RTOS_MTOA_PROG, - RPC_ADSP_RTOS_MTOA_VERS); - if (rc) { - pr_err("adsp: could not register callback server (%d)\n", rc); - goto fail_rpc_register; - } - - /* start the kernel thread to process the callbacks */ - kthread_run(adsp_rpc_thread, NULL, "kadspd"); - - for (i = 0; i < count; i++) { - struct msm_adsp_module *mod = adsp_modules + i; - mutex_init(&mod->lock); - init_waitqueue_head(&mod->state_wait); - mod->info = &adsp_info; - mod->name = adsp_info.module[i].name; - mod->id = adsp_info.module[i].id; - if (adsp_info.module[i].clk_name) - mod->clk = clk_get(NULL, adsp_info.module[i].clk_name); - else - mod->clk = NULL; - if (mod->clk && adsp_info.module[i].clk_rate) - clk_set_rate(mod->clk, adsp_info.module[i].clk_rate); - mod->verify_cmd = adsp_info.module[i].verify_cmd; - mod->patch_event = adsp_info.module[i].patch_event; - INIT_HLIST_HEAD(&mod->pmem_regions); - mod->pdev.name = adsp_info.module[i].pdev_name; - mod->pdev.id = -1; - adsp_info.id_to_module[i] = mod; - platform_device_register(&mod->pdev); - } - - msm_adsp_publish_cdevs(adsp_modules, count); - - return 0; - -fail_rpc_register: - msm_rpc_close(rpc_cb_server_client); - rpc_cb_server_client = NULL; -fail_rpc_open: - enable_irq(INT_ADSP); - free_irq(INT_ADSP, 0); -fail_request_irq: - kfree(adsp_modules); - kfree(adsp_info.init_info_ptr); - return rc; -} - -static struct platform_driver msm_adsp_driver = { - .probe = msm_adsp_probe, - .driver = { - .name = "rs3000000a:00010000", - .owner = THIS_MODULE, - }, -}; - -static int __init adsp_init(void) -{ - return platform_driver_register(&msm_adsp_driver); -} - -device_initcall(adsp_init); diff --git a/arch/arm/mach-msm/qdsp5/adsp_new.h b/arch/arm/mach-msm/qdsp5/adsp_new.h deleted file mode 100644 index 4940235b0c133..0000000000000 --- a/arch/arm/mach-msm/qdsp5/adsp_new.h +++ /dev/null @@ -1,329 +0,0 @@ -/* arch/arm/mach-msm/qdsp5/adsp.h - * - * Copyright (c) 2008 QUALCOMM Incorporated - * Copyright (C) 2008 Google, Inc. - * Author: Iliyan Malchev - * - * 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 _ARCH_ARM_MACH_MSM_ADSP_NEW_H -#define _ARCH_ARM_MACH_MSM_ADSP_NEW_H - -#include -#include -#include -#include - -int adsp_pmem_fixup(struct msm_adsp_module *module, void **addr, - unsigned long len); -int adsp_pmem_fixup_kvaddr(struct msm_adsp_module *module, void **addr, - unsigned long *kvaddr, unsigned long len); -int adsp_pmem_paddr_fixup(struct msm_adsp_module *module, void **addr); - -int adsp_vfe_verify_cmd(struct msm_adsp_module *module, - unsigned int queue_id, void *cmd_data, - size_t cmd_size); -int adsp_jpeg_verify_cmd(struct msm_adsp_module *module, - unsigned int queue_id, void *cmd_data, - size_t cmd_size); -int adsp_lpm_verify_cmd(struct msm_adsp_module *module, - unsigned int queue_id, void *cmd_data, - size_t cmd_size); -int adsp_video_verify_cmd(struct msm_adsp_module *module, - unsigned int queue_id, void *cmd_data, - size_t cmd_size); -int adsp_videoenc_verify_cmd(struct msm_adsp_module *module, - unsigned int queue_id, void *cmd_data, - size_t cmd_size); - - -struct adsp_event; - -int adsp_vfe_patch_event(struct msm_adsp_module *module, - struct adsp_event *event); - -int adsp_jpeg_patch_event(struct msm_adsp_module *module, - struct adsp_event *event); - - -struct adsp_module_info { - const char *name; - const char *pdev_name; - uint32_t id; - const char *clk_name; - unsigned long clk_rate; - int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *, - size_t); - int (*patch_event) (struct msm_adsp_module*, struct adsp_event *); -}; - -#define ADSP_EVENT_MAX_SIZE 496 - -struct adsp_event { - struct list_head list; - uint32_t size; /* always in bytes */ - uint16_t msg_id; - uint16_t type; /* 0 for msgs (from aDSP), -1 for events (from ARM9) */ - int is16; /* always 0 (msg is 32-bit) when the event type is 1(ARM9) */ - union { - uint16_t msg16[ADSP_EVENT_MAX_SIZE / 2]; - uint32_t msg32[ADSP_EVENT_MAX_SIZE / 4]; - } data; -}; - -struct adsp_info { - uint32_t send_irq; - uint32_t read_ctrl; - uint32_t write_ctrl; - - uint32_t max_msg16_size; - uint32_t max_msg32_size; - - uint32_t max_task_id; - uint32_t max_module_id; - uint32_t max_queue_id; - uint32_t max_image_id; - - /* for each image id, a map of queue id to offset */ - uint32_t **queue_offset; - - /* for each image id, a map of task id to module id */ - uint32_t **task_to_module; - - /* for each module id, map of module id to module */ - struct msm_adsp_module **id_to_module; - - uint32_t module_count; - struct adsp_module_info *module; - - /* stats */ - uint32_t events_received; - uint32_t event_backlog_max; - - /* rpc_client for init_info */ - struct msm_rpc_endpoint *init_info_rpc_client; - struct adsp_rtos_mp_mtoa_init_info_type *init_info_ptr; - wait_queue_head_t init_info_wait; - unsigned init_info_state; -}; - -#define RPC_ADSP_RTOS_ATOM_NULL_PROC 0 -#define RPC_ADSP_RTOS_MTOA_NULL_PROC 0 -#define RPC_ADSP_RTOS_APP_TO_MODEM_PROC 2 -#define RPC_ADSP_RTOS_MODEM_TO_APP_PROC 2 - -enum rpc_adsp_rtos_proc_type { - RPC_ADSP_RTOS_PROC_NONE = 0, - RPC_ADSP_RTOS_PROC_MODEM = 1, - RPC_ADSP_RTOS_PROC_APPS = 2, -}; - -enum { - RPC_ADSP_RTOS_CMD_REGISTER_APP, - RPC_ADSP_RTOS_CMD_ENABLE, - RPC_ADSP_RTOS_CMD_DISABLE, - RPC_ADSP_RTOS_CMD_KERNEL_COMMAND, - RPC_ADSP_RTOS_CMD_16_COMMAND, - RPC_ADSP_RTOS_CMD_32_COMMAND, - RPC_ADSP_RTOS_CMD_DISABLE_EVENT_RSP, - RPC_ADSP_RTOS_CMD_REMOTE_EVENT, - RPC_ADSP_RTOS_CMD_SET_STATE, - RPC_ADSP_RTOS_CMD_REMOTE_INIT_INFO_EVENT, - RPC_ADSP_RTOS_CMD_GET_INIT_INFO, -}; - -enum rpc_adsp_rtos_mod_status_type { - RPC_ADSP_RTOS_MOD_READY, - RPC_ADSP_RTOS_MOD_DISABLE, - RPC_ADSP_RTOS_SERVICE_RESET, - RPC_ADSP_RTOS_CMD_FAIL, - RPC_ADSP_RTOS_CMD_SUCCESS, - RPC_ADSP_RTOS_INIT_INFO, - RPC_ADSP_RTOS_DISABLE_FAIL, -}; - -struct rpc_adsp_rtos_app_to_modem_args_t { - struct rpc_request_hdr hdr; - uint32_t gotit; /* if 1, the next elements are present */ - uint32_t cmd; /* e.g., RPC_ADSP_RTOS_CMD_REGISTER_APP */ - uint32_t proc_id; /* e.g., RPC_ADSP_RTOS_PROC_APPS */ - uint32_t module; /* e.g., QDSP_MODULE_AUDPPTASK */ -}; - -enum qdsp_image_type { - QDSP_IMAGE_COMBO, - QDSP_IMAGE_GAUDIO, - QDSP_IMAGE_QTV_LP, - QDSP_IMAGE_MAX, - /* DO NOT USE: Force this enum to be a 32bit type to improve speed */ - QDSP_IMAGE_32BIT_DUMMY = 0x10000 -}; - -#define EVENT_LEN 12 -#define EVENT_MSG_ID ((uint16_t)~0) - -struct adsp_rtos_mp_mtoa_header_type { - enum rpc_adsp_rtos_mod_status_type event; - enum rpc_adsp_rtos_proc_type proc_id; -}; - -/* ADSP RTOS MP Communications - Modem to APP's Event Info*/ -struct adsp_rtos_mp_mtoa_type { - uint32_t module; - uint32_t image; - uint32_t apps_okts; -}; - -/* ADSP RTOS MP Communications - Modem to APP's Init Info */ -#define IMG_MAX 8 -#define ENTRIES_MAX 64 - -struct queue_to_offset_type { - uint32_t queue; - uint32_t offset; -}; - -struct adsp_rtos_mp_mtoa_init_info_type { - uint32_t image_count; - uint32_t num_queue_offsets; - struct queue_to_offset_type queue_offsets_tbl[IMG_MAX][ENTRIES_MAX]; - uint32_t num_task_module_entries; - uint32_t task_to_module_tbl[IMG_MAX][ENTRIES_MAX]; - - uint32_t module_table_size; - uint32_t module_entries[ENTRIES_MAX]; - /* - * queue_offsets[] is to store only queue_offsets - */ - uint32_t queue_offsets[IMG_MAX][ENTRIES_MAX]; -}; - -struct adsp_rtos_mp_mtoa_s_type { - struct adsp_rtos_mp_mtoa_header_type mp_mtoa_header; - - uint32_t desc_field; - union { - struct adsp_rtos_mp_mtoa_init_info_type mp_mtoa_init_packet; - struct adsp_rtos_mp_mtoa_type mp_mtoa_packet; - } adsp_rtos_mp_mtoa_data; -}; - -struct rpc_adsp_rtos_modem_to_app_args_t { - struct rpc_request_hdr hdr; - uint32_t gotit; /* if 1, the next elements are present */ - struct adsp_rtos_mp_mtoa_s_type mtoa_pkt; -}; - -#define ADSP_STATE_DISABLED 0 -#define ADSP_STATE_ENABLING 1 -#define ADSP_STATE_ENABLED 2 -#define ADSP_STATE_DISABLING 3 -#define ADSP_STATE_INIT_INFO 4 - -struct msm_adsp_module { - struct mutex lock; - const char *name; - unsigned id; - struct adsp_info *info; - - struct msm_rpc_endpoint *rpc_client; - struct msm_adsp_ops *ops; - void *driver_data; - - /* statistics */ - unsigned num_commands; - unsigned num_events; - - wait_queue_head_t state_wait; - unsigned state; - - struct platform_device pdev; - struct clk *clk; - int open_count; - - struct mutex pmem_regions_lock; - struct hlist_head pmem_regions; - int (*verify_cmd) (struct msm_adsp_module*, unsigned int, void *, - size_t); - int (*patch_event) (struct msm_adsp_module*, struct adsp_event *); -}; - -extern void msm_adsp_publish_cdevs(struct msm_adsp_module *, unsigned); -extern int adsp_init_info(struct adsp_info *info); - -/* Value to indicate that a queue is not defined for a particular image */ -#define QDSP_RTOS_NO_QUEUE 0xfffffffe - -/* - * Constants used to communicate with the ADSP RTOS - */ -#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_M 0x80000000U -#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_NAVAIL_V 0x80000000U -#define ADSP_RTOS_WRITE_CTRL_WORD_MUTEX_AVAIL_V 0x00000000U - -#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_M 0x70000000U -#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_REQ_V 0x00000000U -#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_WRITE_DONE_V 0x10000000U -#define ADSP_RTOS_WRITE_CTRL_WORD_CMD_NO_CMD_V 0x70000000U - -#define ADSP_RTOS_WRITE_CTRL_WORD_STATUS_M 0x0E000000U -#define ADSP_RTOS_WRITE_CTRL_WORD_NO_ERR_V 0x00000000U -#define ADSP_RTOS_WRITE_CTRL_WORD_NO_FREE_BUF_V 0x02000000U - -#define ADSP_RTOS_WRITE_CTRL_WORD_KERNEL_FLG_M 0x01000000U -#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_MSG_WRITE_V 0x00000000U -#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_V 0x01000000U - -#define ADSP_RTOS_WRITE_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU -#define ADSP_RTOS_WRITE_CTRL_WORD_HTOD_CMD_ID_M 0x00FFFFFFU - -/* Combination of MUTEX and CMD bits to check if the DSP is busy */ -#define ADSP_RTOS_WRITE_CTRL_WORD_READY_M 0xF0000000U -#define ADSP_RTOS_WRITE_CTRL_WORD_READY_V 0x70000000U - -/* RTOS to Host processor command mask values */ -#define ADSP_RTOS_READ_CTRL_WORD_FLAG_M 0x80000000U -#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_WAIT_V 0x00000000U -#define ADSP_RTOS_READ_CTRL_WORD_FLAG_UP_CONT_V 0x80000000U - -#define ADSP_RTOS_READ_CTRL_WORD_CMD_M 0x60000000U -#define ADSP_RTOS_READ_CTRL_WORD_READ_DONE_V 0x00000000U -#define ADSP_RTOS_READ_CTRL_WORD_READ_REQ_V 0x20000000U -#define ADSP_RTOS_READ_CTRL_WORD_NO_CMD_V 0x60000000U - -/* Combination of FLAG and COMMAND bits to check if MSG ready */ -#define ADSP_RTOS_READ_CTRL_WORD_READY_M 0xE0000000U -#define ADSP_RTOS_READ_CTRL_WORD_READY_V 0xA0000000U -#define ADSP_RTOS_READ_CTRL_WORD_CONT_V 0xC0000000U -#define ADSP_RTOS_READ_CTRL_WORD_DONE_V 0xE0000000U - -#define ADSP_RTOS_READ_CTRL_WORD_STATUS_M 0x18000000U -#define ADSP_RTOS_READ_CTRL_WORD_NO_ERR_V 0x00000000U - -#define ADSP_RTOS_READ_CTRL_WORD_IN_PROG_M 0x04000000U -#define ADSP_RTOS_READ_CTRL_WORD_NO_READ_IN_PROG_V 0x00000000U -#define ADSP_RTOS_READ_CTRL_WORD_READ_IN_PROG_V 0x04000000U - -#define ADSP_RTOS_READ_CTRL_WORD_CMD_TYPE_M 0x03000000U -#define ADSP_RTOS_READ_CTRL_WORD_CMD_TASK_TO_H_V 0x00000000U -#define ADSP_RTOS_READ_CTRL_WORD_CMD_KRNL_TO_H_V 0x01000000U -#define ADSP_RTOS_READ_CTRL_WORD_CMD_H_TO_KRNL_CFM_V 0x02000000U - -#define ADSP_RTOS_READ_CTRL_WORD_DSP_ADDR_M 0x00FFFFFFU - -#define ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M 0x000000FFU -#define ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M 0x0000FF00U - -/* Base address of DSP and DSP hardware registers */ -#define QDSP_RAMC_OFFSET 0x400000 - -#endif From 43cfb1ba4d8e63586f0ed6445b697b43ac8c7b60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 10 Jun 2009 15:02:03 -0700 Subject: [PATCH 0325/2556] [ARM] msm: Enable CACHE_FLUSH_RANGE_LIMIT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the default value v6_dma_flush_range will flush the entire cache if the requested range is more than 256k. Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/Kconfig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index feb3cb323706c..9a2009ce00e99 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -211,6 +211,14 @@ config HTC_PWRSINK default y bool "HTC Power Sink Driver" +config CACHE_FLUSH_RANGE_LIMIT + hex "Cache flush range limit" + default 0x40000 + help + When flushing a cache range larger then this (hex) limit, flush the + entire cache instead. Flushing a large range can be slower than + flushing, then refilling, the entire cache. + choice prompt "Default Timer" default MSM7X00A_USE_GP_TIMER From 19c706192bec454f32af964506aacb2abec51d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 11 Jun 2009 18:32:43 -0700 Subject: [PATCH 0326/2556] [ARM] msm: adsp: Fix heap corruption. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For amss versions before 6350 the adsp driver created a lookup table from module id to module index, but used the max index instead of the max id when allocating the table. Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/qdsp5/adsp.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/qdsp5/adsp.c b/arch/arm/mach-msm/qdsp5/adsp.c index 371443758f910..729bb476aac0b 100644 --- a/arch/arm/mach-msm/qdsp5/adsp.c +++ b/arch/arm/mach-msm/qdsp5/adsp.c @@ -1043,6 +1043,7 @@ static int msm_adsp_probe(struct platform_device *pdev) { unsigned count; int rc, i; + int max_module_id; pr_info("adsp: probe\n"); @@ -1062,9 +1063,15 @@ static int msm_adsp_probe(struct platform_device *pdev) adsp_info.write_ctrl += (uint32_t) MSM_AD5_BASE; count = adsp_info.module_count; +#if CONFIG_MSM_AMSS_VERSION >= 6350 + max_module_id = count; +#else + max_module_id = adsp_info.max_module_id + 1; +#endif + adsp_modules = kzalloc( - (sizeof(struct msm_adsp_module) + sizeof(void *)) * - count, GFP_KERNEL); + sizeof(struct msm_adsp_module) * count + + sizeof(void *) * max_module_id, GFP_KERNEL); if (!adsp_modules) return -ENOMEM; From 72832b28f626c227f881de5bfac5ec0e1252bb1b Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Fri, 12 Jun 2009 17:05:04 +0200 Subject: [PATCH 0327/2556] msm: update docs for HTC Dream aka. t-mobile g1 support Improve documentation, add machine description to Kconfig, remove misleading comment. Signed-off-by: Pavel Machek --- Documentation/android.txt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Documentation/android.txt b/Documentation/android.txt index 72a62afdf2025..15922d6fc7f6e 100644 --- a/Documentation/android.txt +++ b/Documentation/android.txt @@ -14,6 +14,12 @@ CONTENTS: 1.3 Recommended enabled config options 2. Contact +0. Getting sources: +----------------- + +git clone --reference /path/to/linux-git/for/speedup/ git://android.git.kernel.org/kernel/msm.git +git checkout -b android-msm-2.6.29 origin/android-msm-2.6.29 + 1. Android ========== @@ -26,6 +32,7 @@ To see a working defconfig look at msm_defconfig or goldfish_defconfig which can be found at http://android.git.kernel.org in kernel/common.git and kernel/msm.git +msm_defconfig should work on qualcomm reference design, HTC Magic and G1/ADP1. 1.1 Required enabled config options ----------------------------------- @@ -114,6 +121,22 @@ SERIAL_CORE SERIAL_CORE_CONSOLE +Board code names +---------------- + +board-halibut is a qualcomm reference design. board-sapphire is HTC +Magic. board-trout is HTC Dream/G1/ADP1. + +Booting your kernel +------------------- + +hold down camera and red button to boot into rainbow screen. Then + +./fastboot boot linux-msm/arch/arm/boot/zImage ramdisk.img + +Machine will freeze at rainbow screen for a while, be +patient. ramdisk.img is required. + 2. Contact ========== website: http://android.git.kernel.org From 344bdda00396edc78faef6391c249059af4ec6e9 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Fri, 12 Jun 2009 13:44:24 -0700 Subject: [PATCH 0328/2556] msm: docs: adjust board descriptions in android.txt Signed-off-by: Brian Swetland --- Documentation/android.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/android.txt b/Documentation/android.txt index 15922d6fc7f6e..e1e41884895d5 100644 --- a/Documentation/android.txt +++ b/Documentation/android.txt @@ -124,8 +124,9 @@ SERIAL_CORE_CONSOLE Board code names ---------------- -board-halibut is a qualcomm reference design. board-sapphire is HTC -Magic. board-trout is HTC Dream/G1/ADP1. +board-halibut - Qualcomm SURF 7201A +board-sapphire - HTC Magic +board-trout - HTC Dream / T-Mobile G1 / Android ADP1 Booting your kernel ------------------- From 910a02a4bcdb56c824f34c39f99bf1dc2b0f1a7f Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Tue, 16 Jun 2009 11:03:51 -0400 Subject: [PATCH 0329/2556] [ARM] msm: htc: Remove HTC specific connect notification from msm_hsusb driver Use a callback function in the msm_hsusb platform data instead. Add USB connect notification to msm72k_udc gadget driver. Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/devices_htc.c | 1 + arch/arm/mach-msm/htc_battery.c | 10 ++-------- arch/arm/mach-msm/include/mach/board_htc.h | 14 ++------------ arch/arm/mach-msm/include/mach/msm_hsusb.h | 3 +++ drivers/usb/gadget/msm72k_udc.c | 10 ++++++++++ 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c index b24df91ef0709..ab2de7c71091b 100644 --- a/arch/arm/mach-msm/devices_htc.c +++ b/arch/arm/mach-msm/devices_htc.c @@ -88,6 +88,7 @@ static int hsusb_phy_init_seq[] = { 0x40, 0x31, 0x1D, 0x0D, 0x1D, 0x10, -1 }; struct msm_hsusb_platform_data msm_hsusb_pdata = { .phy_reset = internal_phy_reset, .phy_init_seq = hsusb_phy_init_seq, + .usb_connected = notify_usb_connected, }; #ifdef CONFIG_USB_ANDROID diff --git a/arch/arm/mach-msm/htc_battery.c b/arch/arm/mach-msm/htc_battery.c index 1a4face74195a..e1dbbf55675e3 100644 --- a/arch/arm/mach-msm/htc_battery.c +++ b/arch/arm/mach-msm/htc_battery.c @@ -182,12 +182,7 @@ static struct power_supply htc_power_supplies[] = { }, }; -static void usb_status_notifier_func(int online); static int g_usb_online; -static struct t_usb_status_notifier usb_status_notifier = { - .name = "htc_battery", - .func = usb_status_notifier_func, -}; /* -------------------------------------------------------------------------- */ @@ -364,8 +359,8 @@ int htc_cable_status_update(int status) /* A9 reports USB charging when helf AC cable in and China AC charger. */ /* Work arround: notify userspace AC charging first, -and notify USB charging again when receiving usb connected notificaiton from usb driver. */ -static void usb_status_notifier_func(int online) +and notify USB charging again when receiving usb connected notification from usb driver. */ +void notify_usb_connected(int online) { mutex_lock(&htc_batt_info.lock); @@ -790,7 +785,6 @@ static int __init htc_battery_init(void) wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present"); mutex_init(&htc_batt_info.lock); mutex_init(&htc_batt_info.rpc_lock); - usb_register_notifier(&usb_status_notifier); msm_rpc_create_server(&battery_server); platform_driver_register(&htc_battery_driver); return 0; diff --git a/arch/arm/mach-msm/include/mach/board_htc.h b/arch/arm/mach-msm/include/mach/board_htc.h index b537c91b957a0..994accd56272f 100644 --- a/arch/arm/mach-msm/include/mach/board_htc.h +++ b/arch/arm/mach-msm/include/mach/board_htc.h @@ -55,24 +55,14 @@ struct mmc_platform_data; int __init msm_add_sdcc_devices(unsigned int controller, struct mmc_platform_data *plat); int __init msm_add_serial_devices(unsigned uart); -#if defined(CONFIG_USB_FUNCTION_MSM_HSUSB) -/* START: add USB connected notify function */ -struct t_usb_status_notifier{ - struct list_head notifier_link; - const char *name; - void (*func)(int online); -}; - int usb_register_notifier(struct t_usb_status_notifier *); - static LIST_HEAD(g_lh_usb_notifier_list); -/* END: add USB connected notify function */ -#endif - int __init board_mfg_mode(void); int __init parse_tag_smi(const struct tag *tags); int __init parse_tag_hwid(const struct tag * tags); int __init parse_tag_skuid(const struct tag * tags); int parse_tag_engineerid(const struct tag * tags); +void notify_usb_connected(int online); + char *board_serialno(void); #endif diff --git a/arch/arm/mach-msm/include/mach/msm_hsusb.h b/arch/arm/mach-msm/include/mach/msm_hsusb.h index 534db4e440cb8..93fb9a4727ad9 100644 --- a/arch/arm/mach-msm/include/mach/msm_hsusb.h +++ b/arch/arm/mach-msm/include/mach/msm_hsusb.h @@ -28,6 +28,9 @@ struct msm_hsusb_platform_data { /* (de)assert the reset to the usb core */ void (*hw_reset)(bool enable); + /* for notification when USB is connected or disconnected */ + void (*usb_connected)(int); + /* val, reg pairs terminated by -1 */ int *phy_init_seq; }; diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 9ae42d5a77ad9..f99391edfa03b 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -162,6 +162,9 @@ struct usb_info { int *phy_init_seq; void (*phy_reset)(void); + /* for notification when USB is connected or disconnected */ + void (*usb_connected)(int); + struct work_struct work; unsigned phy_status; unsigned phy_fail_count; @@ -1090,6 +1093,9 @@ static void usb_do_work(struct work_struct *w) msm72k_pullup(&ui->gadget, 0); spin_unlock_irqrestore(&ui->lock, iflags); + if (ui->usb_connected) + ui->usb_connected(0); + /* terminate any transactions, etc */ flush_all_endpoints(ui); @@ -1126,6 +1132,9 @@ static void usb_do_work(struct work_struct *w) clk_enable(ui->pclk); usb_reset(ui); + if (ui->usb_connected) + ui->usb_connected(1); + ui->state = USB_STATE_ONLINE; usb_do_work_check_vbus(ui); } @@ -1553,6 +1562,7 @@ static int msm72k_probe(struct platform_device *pdev) struct msm_hsusb_platform_data *pdata = pdev->dev.platform_data; ui->phy_reset = pdata->phy_reset; ui->phy_init_seq = pdata->phy_init_seq; + ui->usb_connected = pdata->usb_connected; } irq = platform_get_irq(pdev, 0); From f1c87c3cb65de70e1fcd88171c9cd979e274bcb8 Mon Sep 17 00:00:00 2001 From: stevel Date: Tue, 16 Jun 2009 15:27:45 -0400 Subject: [PATCH 0330/2556] [ARM] msm: usb: gadget: Configure endpoints properly in msm72k_enable Fixes a problem with full-speed USB support. Signed-off-by: Mike Lockwood --- drivers/usb/gadget/msm72k_udc.c | 35 +++++++++++++++++---------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index f99391edfa03b..c9ba072d2147c 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -281,29 +281,29 @@ static void init_endpoints(struct usb_info *ui) } } -static void configure_endpoints(struct usb_info *ui) +static void config_ept(struct msm_endpoint *ept) { - unsigned n; - unsigned cfg; + unsigned cfg = CONFIG_MAX_PKT(ept->ep.maxpacket) | CONFIG_ZLT; - for (n = 0; n < 32; n++) { - struct msm_endpoint *ept = ui->ept + n; + if (ept->bit == 0) + /* ep0 out needs interrupt-on-setup */ + cfg |= CONFIG_IOS; - cfg = CONFIG_MAX_PKT(ept->ep.maxpacket) | CONFIG_ZLT; + ept->head->config = cfg; + ept->head->next = TERMINATE; - if (ept->bit == 0) - /* ep0 out needs interrupt-on-setup */ - cfg |= CONFIG_IOS; + if (ept->ep.maxpacket) + INFO("ept #%d %s max:%d head:%p bit:%d\n", + ept->num, (ept->flags & EPT_FLAG_IN) ? "in" : "out", + ept->ep.maxpacket, ept->head, ept->bit); +} - ept->head->config = cfg; - ept->head->next = TERMINATE; +static void configure_endpoints(struct usb_info *ui) +{ + unsigned n; - if (ept->ep.maxpacket) - INFO("ept #%d %s max:%d head:%p bit:%d\n", - ept->num, - (ept->flags & EPT_FLAG_IN) ? "in" : "out", - ept->ep.maxpacket, ept->head, ept->bit); - } + for (n = 0; n < 32; n++) + config_ept(ui->ept + n); } struct usb_request *usb_ept_alloc_req(struct msm_endpoint *ept, @@ -1307,6 +1307,7 @@ msm72k_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; _ep->maxpacket = le16_to_cpu(desc->wMaxPacketSize); + config_ept(ept); usb_ept_enable(ept, 1, ep_type); return 0; } From 9a66e0ed31443395d804697ce48945ada4848771 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Wed, 15 Jul 2009 19:53:32 -0700 Subject: [PATCH 0331/2556] [ARM] msm: adsp: audio drivers: fix out-of-bounds array references Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/qdsp5/audio_aac.c | 1 - arch/arm/mach-msm/qdsp5/audio_amrnb.c | 1 - arch/arm/mach-msm/qdsp5/audio_evrc.c | 1 - arch/arm/mach-msm/qdsp5/audio_mp3.c | 1 - arch/arm/mach-msm/qdsp5/audio_out.c | 1 - arch/arm/mach-msm/qdsp5/audio_qcelp.c | 1 - 6 files changed, 6 deletions(-) diff --git a/arch/arm/mach-msm/qdsp5/audio_aac.c b/arch/arm/mach-msm/qdsp5/audio_aac.c index 1c20f28e7bf04..4232b9f248e9e 100644 --- a/arch/arm/mach-msm/qdsp5/audio_aac.c +++ b/arch/arm/mach-msm/qdsp5/audio_aac.c @@ -666,7 +666,6 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) config.unused[0] = 0; config.unused[1] = 0; config.unused[2] = 0; - config.unused[3] = 0; if (copy_to_user((void *)arg, &config, sizeof(config))) rc = -EFAULT; diff --git a/arch/arm/mach-msm/qdsp5/audio_amrnb.c b/arch/arm/mach-msm/qdsp5/audio_amrnb.c index cd818a526f836..63fe2d0f0d8d7 100644 --- a/arch/arm/mach-msm/qdsp5/audio_amrnb.c +++ b/arch/arm/mach-msm/qdsp5/audio_amrnb.c @@ -551,7 +551,6 @@ static long audamrnb_ioctl(struct file *file, unsigned int cmd, config.unused[0] = 0; config.unused[1] = 0; config.unused[2] = 0; - config.unused[3] = 0; if (copy_to_user((void *)arg, &config, sizeof(config))) rc = -EFAULT; diff --git a/arch/arm/mach-msm/qdsp5/audio_evrc.c b/arch/arm/mach-msm/qdsp5/audio_evrc.c index 4b43e183f9e86..8ee8d53ce5580 100644 --- a/arch/arm/mach-msm/qdsp5/audio_evrc.c +++ b/arch/arm/mach-msm/qdsp5/audio_evrc.c @@ -517,7 +517,6 @@ static long audevrc_ioctl(struct file *file, unsigned int cmd, config.unused[0] = 0; config.unused[1] = 0; config.unused[2] = 0; - config.unused[3] = 0; if (copy_to_user((void *)arg, &config, sizeof(config))) rc = -EFAULT; else diff --git a/arch/arm/mach-msm/qdsp5/audio_mp3.c b/arch/arm/mach-msm/qdsp5/audio_mp3.c index 657390403a397..f09bdcbc50495 100644 --- a/arch/arm/mach-msm/qdsp5/audio_mp3.c +++ b/arch/arm/mach-msm/qdsp5/audio_mp3.c @@ -604,7 +604,6 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) config.unused[0] = 0; config.unused[1] = 0; config.unused[2] = 0; - config.unused[3] = 0; if (copy_to_user((void *) arg, &config, sizeof(config))) { rc = -EFAULT; } else { diff --git a/arch/arm/mach-msm/qdsp5/audio_out.c b/arch/arm/mach-msm/qdsp5/audio_out.c index e6fcacbf4223a..fcb1f139e62c4 100644 --- a/arch/arm/mach-msm/qdsp5/audio_out.c +++ b/arch/arm/mach-msm/qdsp5/audio_out.c @@ -573,7 +573,6 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) config.unused[0] = 0; config.unused[1] = 0; config.unused[2] = 0; - config.unused[3] = 0; if (copy_to_user((void*) arg, &config, sizeof(config))) { rc = -EFAULT; } else { diff --git a/arch/arm/mach-msm/qdsp5/audio_qcelp.c b/arch/arm/mach-msm/qdsp5/audio_qcelp.c index f0f50e36805a3..9571469e4c1b9 100644 --- a/arch/arm/mach-msm/qdsp5/audio_qcelp.c +++ b/arch/arm/mach-msm/qdsp5/audio_qcelp.c @@ -531,7 +531,6 @@ static long audqcelp_ioctl(struct file *file, unsigned int cmd, config.unused[0] = 0; config.unused[1] = 0; config.unused[2] = 0; - config.unused[3] = 0; if (copy_to_user((void *)arg, &config, sizeof(config))) rc = -EFAULT; From 96f2a9ddaa7a152db100e6b82e6667b2dee10294 Mon Sep 17 00:00:00 2001 From: Nita Huang Date: Mon, 4 May 2009 21:58:09 +0800 Subject: [PATCH 0332/2556] msm: sapphire: add sharp panel Noveteck NT35399 driver ic setting Signed-off-by: San Mehat Signed-off-by: Eric Olsen --- arch/arm/mach-msm/board-sapphire-panel.c | 533 ++++++++++++++++++++++- 1 file changed, 531 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire-panel.c b/arch/arm/mach-msm/board-sapphire-panel.c index 6f594ef922cde..1120579535b7f 100644 --- a/arch/arm/mach-msm/board-sapphire-panel.c +++ b/arch/arm/mach-msm/board-sapphire-panel.c @@ -34,6 +34,7 @@ #include "devices.h" #define DEBUG_SAPPHIRE_PANEL 0 +#define userid 0xD10 enum sapphire_panel_type { SAPPHIRE_PANEL_SHARP = 0, @@ -432,8 +433,7 @@ static void sapphire_mddi_power_client(struct msm_mddi_client_data *client_data, msleep(10); } else { gpio_set_value(SAPPHIRE_GPIO_MDDI_32K_EN, 0); - gpio_set_value(MDDI_RST_N, 0); - msleep(10); + vreg_disable(vreg_lcm_2v85); on_off = 1; id = PM_VREG_PDOWN_AUX_ID; @@ -464,7 +464,9 @@ static int sapphire_mddi_toshiba_client_init( g_panel_id = panel_id = (client_data->remote_read(client_data, GPIODATA) >> 4) & 3; if (panel_id > 1) { +#if DEBUG_SAPPHIRE_PANEL printk(KERN_ERR "unknown panel id at mddi_enable\n"); +#endif return -1; } return 0; @@ -474,6 +476,9 @@ static int sapphire_mddi_toshiba_client_uninit( struct msm_mddi_bridge_platform_data *bridge_data, struct msm_mddi_client_data *client_data) { + gpio_set_value(MDDI_RST_N, 0); + msleep(10); + return 0; } @@ -490,18 +495,23 @@ static int sapphire_mddi_panel_unblank( panel_id = (client_data->remote_read(client_data, GPIODATA) >> 4) & 3; switch (panel_id) { case 0: +#if DEBUG_SAPPHIRE_PANEL printk(KERN_DEBUG "init sharp panel\n"); +#endif sapphire_process_mddi_table(client_data, mddi_sharp_init_table, ARRAY_SIZE(mddi_sharp_init_table)); break; case 1: +#if DEBUG_SAPPHIRE_PANEL printk(KERN_DEBUG "init tpo panel\n"); +#endif sapphire_process_mddi_table(client_data, mddi_tpo_init_table, ARRAY_SIZE(mddi_tpo_init_table)); break; default: + printk(KERN_DEBUG "unknown panel_id: %d\n", panel_id); ret = -1; }; @@ -555,6 +565,525 @@ static int sapphire_mddi_panel_blank( return ret; } + +/* Initial sequence of sharp panel with Novatek NT35399 MDDI client */ +static const struct mddi_table sharp2_init_table[] = { + { 0x0110, 0x00 }, + { 0x1, 0x5 }, + + { 0x0F20, 0x55 }, + { 0x0F21, 0xAA }, + { 0x0F22, 0x66 }, + { 0x0F32, 0x01 }, + { 0x0F36, 0x10 }, + + { 0x02A0, 0x00 }, + { 0x02A1, 0x00 }, + { 0x02A2, 0x3F }, + { 0x02A3, 0x01 }, + { 0x02B0, 0x00 }, + { 0x02B1, 0x00 }, + { 0x02B2, 0xDF }, + { 0x02B3, 0x01 }, + { 0x02D0, 0x00 }, + { 0x02D1, 0x00 }, + { 0x02D2, 0x00 }, + { 0x02D3, 0x00 }, + { 0x0350, 0x70 }, + { 0x0351, 0x00 }, + { 0x0360, 0x30 }, + { 0x0361, 0xC0 }, + { 0x0362, 0x00 }, + { 0x0370, 0x00 }, + { 0x0371, 0xEF }, + { 0x0372, 0x01 }, + + { 0x0B00, 0x10 }, + + { 0x0B10, 0x00 }, + { 0x0B20, 0x22 }, + { 0x0B30, 0x46 }, + { 0x0B40, 0x07 }, + { 0x0B41, 0x1C }, + { 0x0B50, 0x0F }, + { 0x0B51, 0x7A }, + { 0x0B60, 0x16 }, + { 0x0B70, 0x0D }, + { 0x0B80, 0x04 }, + { 0x0B90, 0x07 }, + { 0x0BA0, 0x04 }, + { 0x0BA1, 0x86 }, + { 0x0BB0, 0xFF }, + { 0x0BB1, 0x01 }, + { 0x0BB2, 0xF7 }, + { 0x0BB3, 0x01 }, + { 0x0BC0, 0x00 }, + { 0x0BC1, 0x00 }, + { 0x0BC2, 0x00 }, + { 0x0BC3, 0x00 }, + { 0x0BE0, 0x01 }, + { 0x0BE1, 0x3F }, + + { 0x0BF0, 0x03 }, + + { 0x0C10, 0x02 }, + + { 0x0C30, 0x22 }, + { 0x0C31, 0x20 }, + { 0x0C40, 0x48 }, + { 0x0C41, 0x06 }, + + { 0xE00, 0x0028}, + { 0xE01, 0x002F}, + { 0xE02, 0x0032}, + { 0xE03, 0x000A}, + { 0xE04, 0x0023}, + { 0xE05, 0x0024}, + { 0xE06, 0x0022}, + { 0xE07, 0x0012}, + { 0xE08, 0x000D}, + { 0xE09, 0x0035}, + { 0xE0A, 0x000E}, + { 0xE0B, 0x001A}, + { 0xE0C, 0x003C}, + { 0xE0D, 0x003A}, + { 0xE0E, 0x0050}, + { 0xE0F, 0x0069}, + { 0xE10, 0x0006}, + { 0xE11, 0x001F}, + { 0xE12, 0x0035}, + { 0xE13, 0x0020}, + { 0xE14, 0x0043}, + { 0xE15, 0x0030}, + { 0xE16, 0x003C}, + { 0xE17, 0x0010}, + { 0xE18, 0x0009}, + { 0xE19, 0x0051}, + { 0xE1A, 0x001D}, + { 0xE1B, 0x003C}, + { 0xE1C, 0x0053}, + { 0xE1D, 0x0041}, + { 0xE1E, 0x0045}, + { 0xE1F, 0x004B}, + { 0xE20, 0x000A}, + { 0xE21, 0x0014}, + { 0xE22, 0x001C}, + { 0xE23, 0x0013}, + { 0xE24, 0x002E}, + { 0xE25, 0x0029}, + { 0xE26, 0x001B}, + { 0xE27, 0x0014}, + { 0xE28, 0x000E}, + { 0xE29, 0x0032}, + { 0xE2A, 0x000D}, + { 0xE2B, 0x001B}, + { 0xE2C, 0x0033}, + { 0xE2D, 0x0033}, + { 0xE2E, 0x005B}, + { 0xE2F, 0x0069}, + { 0xE30, 0x0006}, + { 0xE31, 0x0014}, + { 0xE32, 0x003D}, + { 0xE33, 0x0029}, + { 0xE34, 0x0042}, + { 0xE35, 0x0032}, + { 0xE36, 0x003F}, + { 0xE37, 0x000E}, + { 0xE38, 0x0008}, + { 0xE39, 0x0059}, + { 0xE3A, 0x0015}, + { 0xE3B, 0x002E}, + { 0xE3C, 0x0049}, + { 0xE3D, 0x0058}, + { 0xE3E, 0x0061}, + { 0xE3F, 0x006B}, + { 0xE40, 0x000A}, + { 0xE41, 0x001A}, + { 0xE42, 0x0022}, + { 0xE43, 0x0014}, + { 0xE44, 0x002F}, + { 0xE45, 0x002A}, + { 0xE46, 0x001A}, + { 0xE47, 0x0014}, + { 0xE48, 0x000E}, + { 0xE49, 0x002F}, + { 0xE4A, 0x000F}, + { 0xE4B, 0x001B}, + { 0xE4C, 0x0030}, + { 0xE4D, 0x002C}, + { 0xE4E, 0x0051}, + { 0xE4F, 0x0069}, + { 0xE50, 0x0006}, + { 0xE51, 0x001E}, + { 0xE52, 0x0043}, + { 0xE53, 0x002F}, + { 0xE54, 0x0043}, + { 0xE55, 0x0032}, + { 0xE56, 0x0043}, + { 0xE57, 0x000D}, + { 0xE58, 0x0008}, + { 0xE59, 0x0059}, + { 0xE5A, 0x0016}, + { 0xE5B, 0x0030}, + { 0xE5C, 0x004B}, + { 0xE5D, 0x0051}, + { 0xE5E, 0x005A}, + { 0xE5F, 0x006B}, + + + + { 0x0290, 0x01 }, +}; + +#undef TPO2_ONE_GAMMA +/* Initial sequence of TPO panel with Novatek NT35399 MDDI client */ + +static const struct mddi_table tpo2_init_table[] = { + /* Panel interface control */ + { 0xB30, 0x44 }, + { 0xB40, 0x00 }, + { 0xB41, 0x87 }, + { 0xB50, 0x06 }, + { 0xB51, 0x7B }, + { 0xB60, 0x0E }, + { 0xB70, 0x0F }, + { 0xB80, 0x03 }, + { 0xB90, 0x00 }, + { 0x350, 0x70 }, /* FTE is at line 0x70 */ + + /* Entry Mode */ + { 0x360, 0x30 }, + { 0x361, 0xC1 }, + { 0x362, 0x04 }, + +/* 0x2 for gray scale gamma correction, 0x12 for RGB gamma correction */ +#ifdef TPO2_ONE_GAMMA + { 0xB00, 0x02 }, +#else + { 0xB00, 0x12 }, +#endif + /* Driver output control */ + { 0x371, 0xEF }, + { 0x372, 0x03 }, + + /* DCDC on glass control */ + { 0xC31, 0x10 }, + { 0xBA0, 0x00 }, + { 0xBA1, 0x86 }, + + /* VCOMH voltage control */ + { 0xC50, 0x3b }, + + /* Special function control */ + { 0xC10, 0x82 }, + + /* Power control */ + { 0xC40, 0x44 }, + { 0xC41, 0x02 }, + + /* Source output control */ + { 0xBE0, 0x01 }, + { 0xBE1, 0x00 }, + + /* Windows address setting */ + { 0x2A0, 0x00 }, + { 0x2A1, 0x00 }, + { 0x2A2, 0x3F }, + { 0x2A3, 0x01 }, + { 0x2B0, 0x00 }, + { 0x2B1, 0x00 }, + { 0x2B2, 0xDF }, + { 0x2B3, 0x01 }, + + /* RAM address setting */ + { 0x2D0, 0x00 }, + { 0x2D1, 0x00 }, + { 0x2D2, 0x00 }, + { 0x2D3, 0x00 }, + + { 0xF20, 0x55 }, + { 0xF21, 0xAA }, + { 0xF22, 0x66 }, + { 0xF57, 0x45 }, + +/* + * The NT35399 provides gray or RGB gamma correction table, + * which determinated by register-0xb00, and following table + */ +#ifdef TPO2_ONE_GAMMA + /* Positive Gamma setting */ + { 0xE00, 0x04 }, + { 0xE01, 0x12 }, + { 0xE02, 0x18 }, + { 0xE03, 0x10 }, + { 0xE04, 0x29 }, + { 0xE05, 0x26 }, + { 0xE06, 0x1f }, + { 0xE07, 0x11 }, + { 0xE08, 0x0c }, + { 0xE09, 0x3a }, + { 0xE0A, 0x0d }, + { 0xE0B, 0x28 }, + { 0xE0C, 0x40 }, + { 0xE0D, 0x4e }, + { 0xE0E, 0x6f }, + { 0xE0F, 0x5E }, + + /* Negative Gamma setting */ + { 0xE10, 0x0B }, + { 0xE11, 0x00 }, + { 0xE12, 0x00 }, + { 0xE13, 0x1F }, + { 0xE14, 0x4b }, + { 0xE15, 0x33 }, + { 0xE16, 0x13 }, + { 0xE17, 0x12 }, + { 0xE18, 0x0d }, + { 0xE19, 0x2f }, + { 0xE1A, 0x16 }, + { 0xE1B, 0x2e }, + { 0xE1C, 0x49 }, + { 0xE1D, 0x41 }, + { 0xE1E, 0x46 }, + { 0xE1F, 0x55 }, +#else + /* Red Positive Gamma */ + { 0xE00, 0x0f }, + { 0xE01, 0x19 }, + { 0xE02, 0x22 }, + { 0xE03, 0x0b }, + { 0xE04, 0x23 }, + { 0xE05, 0x23 }, + { 0xE06, 0x14 }, + { 0xE07, 0x13 }, + { 0xE08, 0x0f }, + { 0xE09, 0x2a }, + { 0xE0A, 0x0d }, + { 0xE0B, 0x26 }, + { 0xE0C, 0x43 }, + { 0xE0D, 0x20 }, + { 0xE0E, 0x2a }, + { 0xE0F, 0x5c }, + + /* Red Negative Gamma */ + { 0xE10, 0x0d }, + { 0xE11, 0x45 }, + { 0xE12, 0x4c }, + { 0xE13, 0x1c }, + { 0xE14, 0x4d }, + { 0xE15, 0x33 }, + { 0xE16, 0x23 }, + { 0xE17, 0x0f }, + { 0xE18, 0x0b }, + { 0xE19, 0x3a }, + { 0xE1A, 0x19 }, + { 0xE1B, 0x32 }, + { 0xE1C, 0x4e }, + { 0xE1D, 0x37 }, + { 0xE1E, 0x38 }, + { 0xE1F, 0x3b }, + + /* Green Positive Gamma */ + { 0xE20, 0x00 }, + { 0xE21, 0x09 }, + { 0xE22, 0x10 }, + { 0xE23, 0x0f }, + { 0xE24, 0x29 }, + { 0xE25, 0x23 }, + { 0xE26, 0x0b }, + { 0xE27, 0x14 }, + { 0xE28, 0x12 }, + { 0xE29, 0x25 }, + { 0xE2A, 0x12 }, + { 0xE2B, 0x2f }, + { 0xE2C, 0x43 }, + { 0xE2D, 0x2d }, + { 0xE2E, 0x52 }, + { 0xE2F, 0x61 }, + + /* Green Negative Gamma */ + { 0xE30, 0x08 }, + { 0xE31, 0x1d }, + { 0xE32, 0x3f }, + { 0xE33, 0x1c }, + { 0xE34, 0x44 }, + { 0xE35, 0x2e }, + { 0xE36, 0x28 }, + { 0xE37, 0x0c }, + { 0xE38, 0x0a }, + { 0xE39, 0x42 }, + { 0xE3A, 0x17 }, + { 0xE3B, 0x30 }, + { 0xE3C, 0x4b }, + { 0xE3D, 0x3f }, + { 0xE3E, 0x43 }, + { 0xE3F, 0x45 }, + + /* Blue Positive Gamma */ + { 0xE40, 0x32 }, + { 0xE41, 0x32 }, + { 0xE42, 0x31 }, + { 0xE43, 0x06 }, + { 0xE44, 0x08 }, + { 0xE45, 0x0d }, + { 0xE46, 0x04 }, + { 0xE47, 0x14 }, + { 0xE48, 0x0f }, + { 0xE49, 0x1d }, + { 0xE4A, 0x1a }, + { 0xE4B, 0x39 }, + { 0xE4C, 0x4c }, + { 0xE4D, 0x1e }, + { 0xE4E, 0x43 }, + { 0xE4F, 0x61 }, + + /* Blue Negative Gamma */ + { 0xE50, 0x08 }, + { 0xE51, 0x2c }, + { 0xE52, 0x4e }, + { 0xE53, 0x13 }, + { 0xE54, 0x3a }, + { 0xE55, 0x26 }, + { 0xE56, 0x30 }, + { 0xE57, 0x0f }, + { 0xE58, 0x0a }, + { 0xE59, 0x49 }, + { 0xE5A, 0x34 }, + { 0xE5B, 0x4a }, + { 0xE5C, 0x53 }, + { 0xE5D, 0x28 }, + { 0xE5E, 0x26 }, + { 0xE5F, 0x27 }, + +#endif + /* Sleep in mode */ + { 0x110, 0x00 }, + { 0x1, 0x23 }, + /* Display on mode */ + { 0x290, 0x00 }, + { 0x1, 0x27 }, + /* Driver output control */ + { 0x372, 0x01 }, + { 0x1, 0x40 }, + /* Display on mode */ + { 0x290, 0x01 }, +}; + +static const struct mddi_table tpo2_display_on[] = { + { 0x290, 0x01 }, +}; + +static const struct mddi_table tpo2_display_off[] = { + { 0x110, 0x01 }, + { 0x290, 0x00 }, + { 0x1, 100 }, +}; + +static const struct mddi_table tpo2_power_off[] = { + { 0x0110, 0x01 }, +}; + +static int nt35399_detect_panel(struct msm_mddi_client_data *client_data) +{ + int id = -1 ; + + id = client_data->remote_read(client_data, userid) ; + switch(id) { + case 0: + return SAPPHIRE_PANEL_TOPPOLY; + case 1: + return SAPPHIRE_PANEL_SHARP; + default : + printk(KERN_ERR, "%s(): Invalid panel ID!\n", + __FUNCTION__); + return -1 ; + } +} + +static int nt35399_client_init( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + int panel_id; + + if (g_panel_inited == 0) { + g_panel_id = panel_id = nt35399_detect_panel(client_data); + g_panel_inited = 1 ; + } else { + gpio_set_value(MDDI_RST_N, 1); + msleep(10); + gpio_set_value(MDDI_RST_N, 0); + udelay(100); + gpio_set_value(MDDI_RST_N, 1); + mdelay(10); + + g_panel_id = panel_id = nt35399_detect_panel(client_data); + if (panel_id == -1) { + printk("Invalid panel id\n"); + return -1; + } + + client_data->auto_hibernate(client_data, 0); + + if (panel_id == SAPPHIRE_PANEL_TOPPOLY) { + sapphire_process_mddi_table(client_data, tpo2_init_table, + ARRAY_SIZE(tpo2_init_table)); + } else if(panel_id == SAPPHIRE_PANEL_SHARP) { + sapphire_process_mddi_table(client_data, sharp2_init_table, + ARRAY_SIZE(sharp2_init_table)); + } + + client_data->auto_hibernate(client_data, 1); + } + + return 0; +} + +static int nt35399_client_uninit( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *cdata) +{ + return 0; +} + +static int nt35399_panel_unblank( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + int ret = 0; + + sapphire_set_backlight_level(0); + client_data->auto_hibernate(client_data, 0); + + mutex_lock(&sapphire_backlight_lock); + sapphire_set_backlight_level(sapphire_backlight_brightness); + sapphire_backlight_off = 0; + mutex_unlock(&sapphire_backlight_lock); + + client_data->auto_hibernate(client_data, 1); + + return ret; +} + +static int nt35399_panel_blank( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + int ret = 0; + + client_data->auto_hibernate(client_data, 0); + sapphire_process_mddi_table(client_data, tpo2_display_off, + ARRAY_SIZE(tpo2_display_off)); + client_data->auto_hibernate(client_data, 1); + + mutex_lock(&sapphire_backlight_lock); + sapphire_set_backlight_level(0); + sapphire_backlight_off = 1; + mutex_unlock(&sapphire_backlight_lock); + + return ret; +} + static void sapphire_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) { mutex_lock(&sapphire_backlight_lock); From 38c882aa40fecd0640bc0615b821f560ebfce700 Mon Sep 17 00:00:00 2001 From: Thomas Tsai Date: Fri, 10 Jul 2009 00:17:55 +0800 Subject: [PATCH 0333/2556] [ARM] msm: sapphire: Add pull down to sharp panel mddi power sequence Without this patch, leakage current occurs when the mddi panel is turned off Signed-off-by: Thomas Tsai Signed-off-by: Rebecca Schultz Zavin Signed-off-by: Eric Olsen --- arch/arm/mach-msm/board-sapphire-panel.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire-panel.c b/arch/arm/mach-msm/board-sapphire-panel.c index 1120579535b7f..0c687d1d5b697 100644 --- a/arch/arm/mach-msm/board-sapphire-panel.c +++ b/arch/arm/mach-msm/board-sapphire-panel.c @@ -433,7 +433,8 @@ static void sapphire_mddi_power_client(struct msm_mddi_client_data *client_data, msleep(10); } else { gpio_set_value(SAPPHIRE_GPIO_MDDI_32K_EN, 0); - + gpio_set_value(MDDI_RST_N, 0); + msleep(10); vreg_disable(vreg_lcm_2v85); on_off = 1; id = PM_VREG_PDOWN_AUX_ID; @@ -592,7 +593,7 @@ static const struct mddi_table sharp2_init_table[] = { { 0x0350, 0x70 }, { 0x0351, 0x00 }, { 0x0360, 0x30 }, - { 0x0361, 0xC0 }, + { 0x0361, 0xC1 }, { 0x0362, 0x00 }, { 0x0370, 0x00 }, { 0x0371, 0xEF }, From d58e5ade82a2c033356f85b0ae0e3dc84cdafd77 Mon Sep 17 00:00:00 2001 From: solomon Date: Wed, 15 Jul 2009 04:06:24 +0800 Subject: [PATCH 0334/2556] [ARM] msm: sapphire: Update Novatek client detect and Sharp panel init. This patch does the following: 1. Add retrial method for panel ID detection with nt35399 MDDI client and treat as a Sharp is still fails. 2. Remove the test mode of sharp panel with Novatek MDDI client 3. To avoid blink of sharp panel after resume. 4. Change FTE postion of sharp panel to 0x80. (Tearing issue) Signed-off-by: Eric Olsen --- arch/arm/mach-msm/board-sapphire-panel.c | 59 ++++++++++++++++-------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire-panel.c b/arch/arm/mach-msm/board-sapphire-panel.c index 0c687d1d5b697..2aec7638d6e12 100644 --- a/arch/arm/mach-msm/board-sapphire-panel.c +++ b/arch/arm/mach-msm/board-sapphire-panel.c @@ -569,15 +569,6 @@ static int sapphire_mddi_panel_blank( /* Initial sequence of sharp panel with Novatek NT35399 MDDI client */ static const struct mddi_table sharp2_init_table[] = { - { 0x0110, 0x00 }, - { 0x1, 0x5 }, - - { 0x0F20, 0x55 }, - { 0x0F21, 0xAA }, - { 0x0F22, 0x66 }, - { 0x0F32, 0x01 }, - { 0x0F36, 0x10 }, - { 0x02A0, 0x00 }, { 0x02A1, 0x00 }, { 0x02A2, 0x3F }, @@ -590,7 +581,7 @@ static const struct mddi_table sharp2_init_table[] = { { 0x02D1, 0x00 }, { 0x02D2, 0x00 }, { 0x02D3, 0x00 }, - { 0x0350, 0x70 }, + { 0x0350, 0x80 }, /* Set frame tearing effect(FTE) position */ { 0x0351, 0x00 }, { 0x0360, 0x30 }, { 0x0361, 0xC1 }, @@ -731,8 +722,6 @@ static const struct mddi_table sharp2_init_table[] = { { 0xE5E, 0x005A}, { 0xE5F, 0x006B}, - - { 0x0290, 0x01 }, }; @@ -986,18 +975,52 @@ static const struct mddi_table tpo2_power_off[] = { static int nt35399_detect_panel(struct msm_mddi_client_data *client_data) { - int id = -1 ; + int id = -1, i ; + + /* If the MDDI client is failed to report the panel ID, + * perform retrial 5 times. + */ + for( i=0; i < 5; i++ ) { + client_data->remote_write(client_data, 0, 0x110); + msleep(5); + id = client_data->remote_read(client_data, userid) ; + if( id == 0 || id == 1 ) { + if(i==0) { + printk(KERN_ERR "%s: got valid panel ID=%d, " + "without retry\n", + __FUNCTION__, id); + } + else { + printk(KERN_ERR "%s: got valid panel ID=%d, " + "after %d retry\n", + __FUNCTION__, id, i+1); + } + break ; + } + printk(KERN_ERR "%s: got invalid panel ID:%d, trial #%d\n", + __FUNCTION__, id, i+1); + + gpio_set_value(MDDI_RST_N, 0); + msleep(5); + + gpio_set_value(MDDI_RST_N, 1); + msleep(10); + gpio_set_value(MDDI_RST_N, 0); + udelay(100); + gpio_set_value(MDDI_RST_N, 1); + mdelay(10); + } + printk(KERN_INFO "%s: final panel id=%d\n", __FUNCTION__, id); - id = client_data->remote_read(client_data, userid) ; switch(id) { case 0: return SAPPHIRE_PANEL_TOPPOLY; case 1: return SAPPHIRE_PANEL_SHARP; default : - printk(KERN_ERR, "%s(): Invalid panel ID!\n", - __FUNCTION__); - return -1 ; + printk(KERN_ERR "%s(): Invalid panel ID: %d, " + "treat as sharp panel.", __FUNCTION__, id); + return SAPPHIRE_PANEL_SHARP; } } @@ -1025,7 +1048,6 @@ static int nt35399_client_init( } client_data->auto_hibernate(client_data, 0); - if (panel_id == SAPPHIRE_PANEL_TOPPOLY) { sapphire_process_mddi_table(client_data, tpo2_init_table, ARRAY_SIZE(tpo2_init_table)); @@ -1053,6 +1075,7 @@ static int nt35399_panel_unblank( { int ret = 0; + mdelay(20); sapphire_set_backlight_level(0); client_data->auto_hibernate(client_data, 0); From 279fa79ab75bbebfca34672e9201a178fabf7a2c Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 7 Aug 2009 10:53:18 -0700 Subject: [PATCH 0335/2556] [ARM] msm: Remove hw3d config option. Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Kconfig | 7 ------- 1 file changed, 7 deletions(-) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 9a2009ce00e99..1f7e9df20aa62 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -457,13 +457,6 @@ config MSM_CPU_FREQ_ONDEMAND_MIN endif # MSM_CPU_FREQ_ONDEMAND -config MSM_HW3D - tristate "MSM Hardware 3D Register Driver" - default y - help - Provides access to registers needed by the userspace OpenGL|ES - library. - config MSM_ADSP tristate "MSM ADSP driver" default y From d12d0cf6e50dcb95d8371bdc6b147b110887395d Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 6 Jul 2009 16:29:18 -0700 Subject: [PATCH 0336/2556] [ARM] msm: change the hw3d driver to let appications directly map the gpu If another user has control of the device, open() will attempt to revoke the gpu by releasing a blocked "wait for revoke" ioctl, if any. If a grace period expires and the gpu is still not relased, the application is SIGKILLed. On suspend, the same approach is applied to make the application release the gpu so that hw can be put into low power mode. Change-Id: Iceae571d3c5f13c04778c104ad54ccd48cb55d7d Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Kconfig | 8 + arch/arm/mach-msm/devices_htc.c | 79 +-- arch/arm/mach-msm/hw3d.c | 831 +++++++++++++++++++++++++++----- include/linux/msm_hw3d.h | 54 +++ 4 files changed, 811 insertions(+), 161 deletions(-) create mode 100644 include/linux/msm_hw3d.h diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 1f7e9df20aa62..d1edb8498d947 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -457,6 +457,14 @@ config MSM_CPU_FREQ_ONDEMAND_MIN endif # MSM_CPU_FREQ_ONDEMAND +config MSM_HW3D + tristate "MSM Hardware 3D Register Driver" + depends on EARLYSUSPEND + default y + help + Provides access to registers needed by the userspace OpenGL|ES + library. + config MSM_ADSP tristate "MSM ADSP driver" default y diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c index ab2de7c71091b..7db0ac7289fc6 100644 --- a/arch/arm/mach-msm/devices_htc.c +++ b/arch/arm/mach-msm/devices_htc.c @@ -141,20 +141,6 @@ static struct android_pmem_platform_data pmem_camera_pdata = { .cached = 0, }; -static struct android_pmem_platform_data pmem_gpu0_pdata = { - .name = "pmem_gpu0", - .no_allocator = 1, - .cached = 0, - .buffered = 1, -}; - -static struct android_pmem_platform_data pmem_gpu1_pdata = { - .name = "pmem_gpu1", - .no_allocator = 1, - .cached = 0, - .buffered = 1, -}; - static struct platform_device pmem_device = { .name = "android_pmem", .id = 0, @@ -167,21 +153,9 @@ static struct platform_device pmem_adsp_device = { .dev = { .platform_data = &pmem_adsp_pdata }, }; -static struct platform_device pmem_gpu0_device = { - .name = "android_pmem", - .id = 2, - .dev = { .platform_data = &pmem_gpu0_pdata }, -}; - -static struct platform_device pmem_gpu1_device = { - .name = "android_pmem", - .id = 3, - .dev = { .platform_data = &pmem_gpu1_pdata }, -}; - static struct platform_device pmem_camera_device = { .name = "android_pmem", - .id = 4, + .id = 2, .dev = { .platform_data = &pmem_camera_pdata }, }; @@ -198,6 +172,36 @@ static struct platform_device ram_console_device = { .resource = ram_console_resource, }; +static struct resource resources_hw3d[] = { + { + .start = 0xA0000000, + .end = 0xA00fffff, + .flags = IORESOURCE_MEM, + .name = "regs", + }, + { + .flags = IORESOURCE_MEM, + .name = "smi", + }, + { + .flags = IORESOURCE_MEM, + .name = "ebi", + }, + { + .start = INT_GRAPHICS, + .end = INT_GRAPHICS, + .flags = IORESOURCE_IRQ, + .name = "gfx", + }, +}; + +static struct platform_device hw3d_device = { + .name = "msm_hw3d", + .id = 0, + .num_resources = ARRAY_SIZE(resources_hw3d), + .resource = resources_hw3d, +}; + void __init msm_add_mem_devices(struct msm_pmem_setting *setting) { if (setting->pmem_size) { @@ -212,16 +216,19 @@ void __init msm_add_mem_devices(struct msm_pmem_setting *setting) platform_device_register(&pmem_adsp_device); } - if (setting->pmem_gpu0_size) { - pmem_gpu0_pdata.start = setting->pmem_gpu0_start; - pmem_gpu0_pdata.size = setting->pmem_gpu0_size; - platform_device_register(&pmem_gpu0_device); - } + if (setting->pmem_gpu0_size && setting->pmem_gpu1_size) { + struct resource *res; + + res = platform_get_resource_byname(&hw3d_device, IORESOURCE_MEM, + "smi"); + res->start = setting->pmem_gpu0_start; + res->end = res->start + setting->pmem_gpu0_size - 1; - if (setting->pmem_gpu1_size) { - pmem_gpu1_pdata.start = setting->pmem_gpu1_start; - pmem_gpu1_pdata.size = setting->pmem_gpu1_size; - platform_device_register(&pmem_gpu1_device); + res = platform_get_resource_byname(&hw3d_device, IORESOURCE_MEM, + "ebi"); + res->start = setting->pmem_gpu1_start; + res->end = res->start + setting->pmem_gpu1_size - 1; + platform_device_register(&hw3d_device); } if (setting->pmem_camera_size) { diff --git a/arch/arm/mach-msm/hw3d.c b/arch/arm/mach-msm/hw3d.c index b013b57077af9..72845b24cc32a 100644 --- a/arch/arm/mach-msm/hw3d.c +++ b/arch/arm/mach-msm/hw3d.c @@ -4,6 +4,7 @@ * * Copyright (C) 2007 Google, Inc. * Author: Brian Swetland + * Heavily modified: Dima Zavin * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -16,80 +17,190 @@ * */ -#include +/* #define DEBUG */ +/* #define VERBOSE */ + +#include +#include +#include #include +#include +#include #include -#include +#include +#include +#include +#include +#include #include +#include +#include #include -#include -#include +#include #include -#include -#include -#include +#include +#include + #include -static DEFINE_SPINLOCK(hw3d_lock); -static DECLARE_WAIT_QUEUE_HEAD(hw3d_queue); -static int hw3d_pending; -static int hw3d_disabled; +#if defined(VERBOSE) +#define VDBG(x...) pr_debug(x) +#else +#define VDBG(x...) do {} while(0) +#endif -static struct clk *grp_clk; -static struct clk *imem_clk; -DECLARE_MUTEX(hw3d_sem); -static unsigned int hw3d_granted; -static struct file *hw3d_granted_file; +struct mem_region { + unsigned long pbase; + unsigned long size; + void __iomem *vbase; +}; -static irqreturn_t hw3d_irq_handler(int irq, void *data) +struct hw3d_info { + struct miscdevice master_dev; + struct miscdevice client_dev; + + struct clk *grp_clk; + struct clk *imem_clk; + int irq; + + struct mem_region regions[HW3D_NUM_REGIONS]; + + wait_queue_head_t irq_wq; + bool irq_pending; + bool irq_en; + bool suspending; + bool revoking; + bool enabled; + + struct timer_list revoke_timer; + wait_queue_head_t revoke_wq; + wait_queue_head_t revoke_done_wq; + + spinlock_t lock; + + struct file *client_file; + struct task_struct *client_task; + + struct early_suspend early_suspend; + struct wake_lock wake_lock; +}; +static struct hw3d_info *hw3d_info; + +struct hw3d_data { + struct vm_area_struct *vmas[HW3D_NUM_REGIONS]; + struct mutex mutex; + bool closing; +}; + +#define REGION_PAGE_ID(addr) \ + ((((uint32_t)(addr)) >> (28 - PAGE_SHIFT)) & 0xf) +#define REGION_PAGE_OFFS(addr) \ + ((((uint32_t)(addr)) & ~(0xf << (28 - PAGE_SHIFT)))) + +static int hw3d_open(struct inode *, struct file *); +static int hw3d_release(struct inode *, struct file *); +static int hw3d_mmap(struct file *, struct vm_area_struct *); +static int hw3d_flush(struct file *, fl_owner_t); +static long hw3d_ioctl(struct file *, unsigned int, unsigned long); + +static void hw3d_vma_open(struct vm_area_struct *); +static void hw3d_vma_close(struct vm_area_struct *); + +static struct file_operations hw3d_fops = { + .open = hw3d_open, + .release = hw3d_release, + .mmap = hw3d_mmap, + .flush = hw3d_flush, + .unlocked_ioctl = hw3d_ioctl, +}; + +static struct vm_operations_struct hw3d_vm_ops = { + .open = hw3d_vma_open, + .close = hw3d_vma_close, +}; + +static bool is_master(struct hw3d_info *info, struct file *file) { - unsigned long flags; + int fmin = MINOR(file->f_dentry->d_inode->i_rdev); + return fmin == info->master_dev.minor; +} + +static bool is_client(struct hw3d_info *info, struct file *file) +{ + int fmin = MINOR(file->f_dentry->d_inode->i_rdev); + return fmin == info->client_dev.minor; +} - spin_lock_irqsave(&hw3d_lock, flags); - if (!hw3d_disabled) { - disable_irq(INT_GRAPHICS); - hw3d_disabled = 1; +inline static void locked_hw3d_irq_disable(struct hw3d_info *info) +{ + if (info->irq_en) { + disable_irq_nosync(info->irq); + info->irq_en = 0; } - hw3d_pending = 1; - spin_unlock_irqrestore(&hw3d_lock, flags); +} - wake_up(&hw3d_queue); +inline static void locked_hw3d_irq_enable(struct hw3d_info *info) +{ + if (!info->irq_en) { + enable_irq(info->irq); + info->irq_en = 1; + } +} - return IRQ_HANDLED; +static void hw3d_disable_interrupt(struct hw3d_info *info) +{ + unsigned long flags; + + spin_lock_irqsave(&info->lock, flags); + locked_hw3d_irq_disable(info); + spin_unlock_irqrestore(&info->lock, flags); } -static void hw3d_disable_interrupt(void) +static irqreturn_t hw3d_irq_handler(int irq, void *data) { + struct hw3d_info *info = data; unsigned long flags; - spin_lock_irqsave(&hw3d_lock, flags); - if (!hw3d_disabled) { - disable_irq(INT_GRAPHICS); - hw3d_disabled = 1; - } - spin_unlock_irqrestore(&hw3d_lock, flags); + + spin_lock_irqsave(&info->lock, flags); + locked_hw3d_irq_disable(info); + info->irq_pending = 1; + spin_unlock_irqrestore(&info->lock, flags); + + wake_up(&info->irq_wq); + + return IRQ_HANDLED; } -static long hw3d_wait_for_interrupt(void) +static long hw3d_wait_for_interrupt(struct hw3d_info *info, struct file *filp) { + struct hw3d_data *data = filp->private_data; unsigned long flags; int ret; + if (is_master(info, filp)) { + pr_err("%s: cannot wait for irqs on master node\n", __func__); + return -EPERM; + } + for (;;) { - spin_lock_irqsave(&hw3d_lock, flags); - if (hw3d_pending) { - hw3d_pending = 0; - spin_unlock_irqrestore(&hw3d_lock, flags); + spin_lock_irqsave(&info->lock, flags); + if (info->irq_pending) { + info->irq_pending = 0; + spin_unlock_irqrestore(&info->lock, flags); return 0; } - if (hw3d_disabled) { - hw3d_disabled = 0; - enable_irq(INT_GRAPHICS); - } - spin_unlock_irqrestore(&hw3d_lock, flags); + locked_hw3d_irq_enable(info); + spin_unlock_irqrestore(&info->lock, flags); - ret = wait_event_interruptible(hw3d_queue, hw3d_pending); + ret = wait_event_interruptible(info->irq_wq, + info->irq_pending || + info->revoking || + data->closing); + /* always make sure the irq gets disabled */ + if (ret == 0 && !info->irq_pending) + ret = -EPIPE; if (ret < 0) { - hw3d_disable_interrupt(); + hw3d_disable_interrupt(info); return ret; } } @@ -97,117 +208,587 @@ static long hw3d_wait_for_interrupt(void) return 0; } -#define HW3D_REGS_LEN 0x100000 +static long hw3d_wait_for_revoke(struct hw3d_info *info, struct file *filp) +{ + struct hw3d_data *data = filp->private_data; + int ret; + + if (is_master(info, filp)) { + pr_err("%s: cannot revoke on master node\n", __func__); + return -EPERM; + } + + ret = wait_event_interruptible(info->revoke_wq, + info->revoking || + data->closing); + if (ret == 0 && data->closing) + ret = -EPIPE; + if (ret < 0) + return ret; + return 0; +} + +static void locked_hw3d_client_done(struct hw3d_info *info, int had_timer) +{ + if (info->enabled) { + pr_debug("hw3d: was enabled\n"); + info->enabled = 0; + clk_disable(info->grp_clk); + clk_disable(info->imem_clk); + } + info->revoking = 0; + info->client_file = NULL; + + /* double check that the irqs are disabled */ + locked_hw3d_irq_disable(info); + + if (had_timer) + wake_unlock(&info->wake_lock); + wake_up(&info->revoke_done_wq); +} + +static void do_force_revoke(struct hw3d_info *info) +{ + unsigned long flags; + + /* at this point, the task had a chance to relinquish the gpu, but + * it hasn't. So, we kill it */ + spin_lock_irqsave(&info->lock, flags); + pr_debug("hw3d: forcing revoke\n"); + locked_hw3d_irq_disable(info); + if (info->client_task) { + pr_info("hw3d: force revoke from pid=%d\n", + info->client_task->pid); + force_sig(SIGKILL, info->client_task); + put_task_struct(info->client_task); + info->client_task = NULL; + } + locked_hw3d_client_done(info, 1); + pr_debug("hw3d: done forcing revoke\n"); + spin_unlock_irqrestore(&info->lock, flags); +} + +#define REVOKE_TIMEOUT (HZ / 2) +static void locked_hw3d_revoke(struct hw3d_info *info) +{ + /* force us to wait to suspend until the revoke is done. If the + * user doesn't release the gpu, the timer will turn off the gpu, + * and force kill the process. */ + wake_lock(&info->wake_lock); + info->revoking = 1; + wake_up(&info->revoke_wq); + mod_timer(&info->revoke_timer, jiffies + REVOKE_TIMEOUT); +} -static long hw3d_revoke_gpu(struct file *file) +bool is_msm_hw3d_file(struct file *file) { + struct hw3d_info *info = hw3d_info; + if (MAJOR(file->f_dentry->d_inode->i_rdev) == MISC_MAJOR && + (is_master(info, file) || is_client(info, file))) + return 1; + return 0; +} + +void put_msm_hw3d_file(struct file *file) +{ + if (!is_msm_hw3d_file(file)) + return; + fput(file); +} + +int get_msm_hw3d_file(int fd, int region, uint32_t offs, unsigned long *pbase, + unsigned long *len, struct file **filp) +{ + struct hw3d_info *info = hw3d_info; + struct file *file; + struct hw3d_data *data; int ret = 0; - unsigned long user_start, user_len; - struct pmem_region region = {.offset = 0x0, .len = HW3D_REGS_LEN}; - - down(&hw3d_sem); - if (!hw3d_granted) { - goto end; - } - /* revoke the pmem region completely */ - if ((ret = pmem_remap(®ion, file, PMEM_UNMAP))) - goto end; - get_pmem_user_addr(file, &user_start, &user_len); - /* reset the gpu */ - clk_disable(grp_clk); - clk_disable(imem_clk); - hw3d_granted = 0; -end: - up(&hw3d_sem); + + if (unlikely(region >= HW3D_NUM_REGIONS)) { + VDBG("hw3d: invalid region %d requested\n", region); + return -EINVAL; + } else if (unlikely(offs >= info->regions[region].size)) { + VDBG("hw3d: offset %08x outside of the requested region %d\n", + offs, region); + return -EINVAL; + } + + file = fget(fd); + if (unlikely(file == NULL)) { + pr_info("%s: requested data from file descriptor that doesn't " + "exist.", __func__); + return -EINVAL; + } else if (!is_msm_hw3d_file(file)) { + ret = -1; + goto err; + } + + data = file->private_data; + if (unlikely(!data)) { + VDBG("hw3d: invalid file\n"); + ret = -EINVAL; + goto err; + } + + mutex_lock(&data->mutex); + if (unlikely(!data->vmas[region])) { + mutex_unlock(&data->mutex); + VDBG("hw3d: requested hw3d region is not mapped\n"); + ret = -ENOENT; + goto err; + } + + *pbase = info->regions[region].pbase; + *filp = file; + *len = data->vmas[region]->vm_end - data->vmas[region]->vm_start; + mutex_unlock(&data->mutex); + return 0; + +err: + fput(file); return ret; } -static long hw3d_grant_gpu(struct file *file) +static int hw3d_flush(struct file *filp, fl_owner_t id) { + struct hw3d_info *info = hw3d_info; + struct hw3d_data *data = filp->private_data; + + if (!data) { + pr_err("%s: no private data\n", __func__); + return -EINVAL; + } + + if (is_master(info, filp)) + return 0; + pr_debug("hw3d: closing\n"); + /* releases any blocked ioctls */ + data->closing = 1; + wake_up(&info->revoke_wq); + wake_up(&info->irq_wq); + return 0; +} + + +static int hw3d_open(struct inode *inode, struct file *file) +{ + struct hw3d_info *info = hw3d_info; + struct hw3d_data *data; + unsigned long flags; int ret = 0; - struct pmem_region region = {.offset = 0x0, .len = HW3D_REGS_LEN}; - down(&hw3d_sem); - if (hw3d_granted) { - ret = -1; - goto end; - } - /* map the registers */ - if ((ret = pmem_remap(®ion, file, PMEM_MAP))) - goto end; - clk_enable(grp_clk); - clk_enable(imem_clk); - hw3d_granted = 1; - hw3d_granted_file = file; -end: - up(&hw3d_sem); + pr_info("%s: pid %d tid %d opening %s node\n", __func__, + current->group_leader->pid, current->pid, + is_master(info, file) ? "master" : "client"); + + if (file->private_data != NULL) + return -EINVAL; + + data = kzalloc(sizeof(struct hw3d_data), GFP_KERNEL); + if (!data) { + pr_err("%s: unable to allocate memory for hw3d_data.\n", + __func__); + return -ENOMEM; + } + + mutex_init(&data->mutex); + file->private_data = data; + + /* master always succeeds, so we are done */ + if (is_master(info, file)) + return 0; + + spin_lock_irqsave(&info->lock, flags); + if (info->suspending) { + pr_warning("%s: can't open hw3d while suspending\n", __func__); + ret = -EPERM; + spin_unlock_irqrestore(&info->lock, flags); + goto err; + } + + if (info->client_file) { + pr_debug("hw3d: have client_file, need revoke\n"); + locked_hw3d_revoke(info); + spin_unlock_irqrestore(&info->lock, flags); + ret = wait_event_interruptible(info->revoke_done_wq, + !info->client_file); + if (ret < 0) + goto err; + spin_lock_irqsave(&info->lock, flags); + if (info->client_file) { + /* between is waking up and grabbing the lock, + * someone else tried to open the gpu, and got here + * first, let them have it. */ + spin_unlock_irqrestore(&info->lock, flags); + ret = -EBUSY; + goto err; + } + } + + info->client_file = file; + get_task_struct(current->group_leader); + info->client_task = current->group_leader; + + /* XXX: we enable these clocks if the client connects.. + * probably not right? Should only turn the clocks on when the user + * tries to map the registers? */ + clk_enable(info->imem_clk); + clk_enable(info->grp_clk); + info->enabled = 1; + + spin_unlock_irqrestore(&info->lock, flags); + return 0; + +err: + file->private_data = NULL; + kfree(data); return ret; + } static int hw3d_release(struct inode *inode, struct file *file) { - down(&hw3d_sem); - /* if the gpu is in use, and its inuse by the file that was released */ - if (hw3d_granted && (file == hw3d_granted_file)) { - clk_disable(grp_clk); - clk_disable(imem_clk); - hw3d_granted = 0; - hw3d_granted_file = NULL; + struct hw3d_info *info = hw3d_info; + struct hw3d_data *data = file->private_data; + unsigned long flags; + + BUG_ON(!data); + + file->private_data = NULL; + + if (is_master(info, file)) + goto done; + + pr_info("%s: in release for pid=%d tid=%d\n", __func__, + current->group_leader->pid, current->pid); + spin_lock_irqsave(&info->lock, flags); + + if (info->client_task && info->client_task == current->group_leader) { + pr_debug("hw3d: releasing %d\n", info->client_task->pid); + put_task_struct(info->client_task); + info->client_task = NULL; } - up(&hw3d_sem); + + if (info->client_file && info->client_file == file) { + int pending; + /* this will be true if we are still the "owner" of the gpu */ + pr_debug("hw3d: had file\n"); + pending = del_timer(&info->revoke_timer); + locked_hw3d_client_done(info, pending); + } + spin_unlock_irqrestore(&info->lock, flags); + +done: + kfree(data); return 0; } +static void hw3d_vma_open(struct vm_area_struct *vma) +{ + /* XXX: should the master be allowed to fork and keep the mappings? */ + + /* TODO: remap garbage page into here. + * + * For now, just pull the mapping. The user shouldn't be forking + * and using it anyway. */ + zap_page_range(vma, vma->vm_start, vma->vm_end - vma->vm_start, NULL); +} + +static void hw3d_vma_close(struct vm_area_struct *vma) +{ + struct file *file = vma->vm_file; + struct hw3d_data *data = file->private_data; + int i; + + pr_debug("hw3d: current %u ppid %u file %p count %ld\n", + current->pid, current->parent->pid, file, file_count(file)); + + BUG_ON(!data); + + mutex_lock(&data->mutex); + for (i = 0; i < HW3D_NUM_REGIONS; ++i) { + if (data->vmas[i] == vma) { + data->vmas[i] = NULL; + goto done; + } + } + pr_warning("%s: vma %p not of ours during vma_close\n", __func__, vma); +done: + mutex_unlock(&data->mutex); +} + +static int hw3d_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct hw3d_info *info = hw3d_info; + struct hw3d_data *data = file->private_data; + unsigned long vma_size = vma->vm_end - vma->vm_start; + int ret = 0; + int region = REGION_PAGE_ID(vma->vm_pgoff); + + if (region >= HW3D_NUM_REGIONS) { + pr_err("%s: Trying to mmap unknown region %d\n", __func__, + region); + return -EINVAL; + } else if (REGION_PAGE_OFFS(vma->vm_pgoff) != 0 || + (vma_size & ~PAGE_MASK)) { + pr_err("%s: Can't remap part of the region %d\n", __func__, + region); + return -EINVAL; + } else if (!is_master(info, file) && + current->group_leader != info->client_task) { + pr_err("%s: current(%d) != client_task(%d)\n", __func__, + current->group_leader->pid, info->client_task->pid); + return -EPERM; + } else if (!is_master(info, file) && + (info->revoking || info->suspending)) { + pr_err("%s: cannot mmap while revoking(%d) or suspending(%d)\n", + __func__, info->revoking, info->suspending); + return -EPERM; + } + + mutex_lock(&data->mutex); + if (data->vmas[region] != NULL) { + pr_err("%s: Region %d already mapped (pid=%d tid=%d)\n", + __func__, region, current->group_leader->pid, + current->pid); + ret = -EBUSY; + goto done; + } + + /* our mappings are always noncached */ +#ifdef pgprot_noncached + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); +#endif + + ret = io_remap_pfn_range(vma, vma->vm_start, + info->regions[region].pbase >> PAGE_SHIFT, + vma_size, vma->vm_page_prot); + if (ret) { + pr_err("%s: Cannot remap page range for region %d!\n", __func__, + region); + ret = -EAGAIN; + goto done; + } + + vma->vm_ops = &hw3d_vm_ops; + + /* mark this region as mapped */ + data->vmas[region] = vma; + +done: + mutex_unlock(&data->mutex); + return ret; +} + static long hw3d_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + struct hw3d_info *info = hw3d_info; + struct hw3d_region regions[HW3D_NUM_REGIONS]; + int i; + + if (!file->private_data) + return -EINVAL; + switch (cmd) { - case HW3D_REVOKE_GPU: - return hw3d_revoke_gpu(file); - break; - case HW3D_GRANT_GPU: - return hw3d_grant_gpu(file); - break; - case HW3D_WAIT_FOR_INTERRUPT: - return hw3d_wait_for_interrupt(); - break; - default: - return -EINVAL; + case HW3D_WAIT_FOR_REVOKE: + return hw3d_wait_for_revoke(info, file); + + case HW3D_WAIT_FOR_INTERRUPT: + return hw3d_wait_for_interrupt(info, file); + + case HW3D_GET_REGIONS: + for (i = 0; i < HW3D_NUM_REGIONS; ++i) { + regions[i].phys = info->regions[i].pbase; + regions[i].map_offset = HW3D_REGION_OFFSET(i); + regions[i].len = info->regions[i].size; + } + if (copy_to_user((void __user *)arg, regions, sizeof(regions))) + return -EFAULT; + break; + + default: + return -EINVAL; } + return 0; } -static struct android_pmem_platform_data pmem_data = { - .name = "hw3d", - .start = 0xA0000000, - .size = 0x100000, - .no_allocator = 1, - .cached = 0, -}; +static void hw3d_early_suspend(struct early_suspend *h) +{ + unsigned long flags; + struct hw3d_info *info; + info = container_of(h, struct hw3d_info, early_suspend); -static int __init hw3d_init(void) + spin_lock_irqsave(&info->lock, flags); + info->suspending = 1; + if (info->client_file) { + pr_debug("hw3d: Requesting revoke for suspend\n"); + locked_hw3d_revoke(info); + } + spin_unlock_irqrestore(&info->lock, flags); +} + +static void hw3d_late_resume(struct early_suspend *h) { - int ret; + unsigned long flags; + struct hw3d_info *info; + info = container_of(h, struct hw3d_info, early_suspend); + + spin_lock_irqsave(&info->lock, flags); + pr_info("%s: resuming\n", __func__); + info->suspending = 0; + spin_unlock_irqrestore(&info->lock, flags); +} + +static int hw3d_resume(struct platform_device *pdev) +{ + struct hw3d_info *info = platform_get_drvdata(pdev); + unsigned long flags; + + spin_lock_irqsave(&info->lock, flags); + pr_info("%s: resuming\n", __func__); + info->suspending = 0; + spin_unlock_irqrestore(&info->lock, flags); + return 0; +} + +static int __init hw3d_probe(struct platform_device *pdev) +{ + struct hw3d_info *info; + struct resource *res[HW3D_NUM_REGIONS]; + int i; + int irq; + int ret = 0; + + res[HW3D_REGS] = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "regs"); + res[HW3D_SMI] = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "smi"); + res[HW3D_EBI] = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "ebi"); + irq = platform_get_irq(pdev, 0); + if (!res[HW3D_REGS] || !res[HW3D_SMI] || !res[HW3D_EBI] || irq < 0) { + pr_err("%s: incomplete resources\n", __func__); + return -EINVAL; + } + + info = kzalloc(sizeof(struct hw3d_info), GFP_KERNEL); + if (info == NULL) { + pr_err("%s: Cannot allocate memory for hw3d_info\n", __func__); + ret = -ENOMEM; + goto err_alloc; + } + + info->irq = irq; + wake_lock_init(&info->wake_lock, WAKE_LOCK_SUSPEND, "hw3d_revoke_lock"); + spin_lock_init(&info->lock); + init_waitqueue_head(&info->irq_wq); + init_waitqueue_head(&info->revoke_wq); + init_waitqueue_head(&info->revoke_done_wq); + setup_timer(&info->revoke_timer, + (void (*)(unsigned long))do_force_revoke, + (unsigned long)info); + + platform_set_drvdata(pdev, info); + + info->grp_clk = clk_get(NULL, "grp_clk"); + if (IS_ERR(info->grp_clk)) { + pr_err("%s: Cannot get grp_clk\n", __func__); + ret = PTR_ERR(info->grp_clk); + goto err_get_grp_clk; + } + + info->imem_clk = clk_get(NULL, "imem_clk"); + if (IS_ERR(info->imem_clk)) { + pr_err("%s: Cannot get imem_clk\n", __func__); + ret = PTR_ERR(info->imem_clk); + goto err_get_imem_clk; + } - grp_clk = clk_get(NULL, "grp_clk"); - if (IS_ERR(grp_clk)) - return PTR_ERR(grp_clk); - - imem_clk = clk_get(NULL, "imem_clk"); - if (IS_ERR(imem_clk)) { - clk_put(grp_clk); - return PTR_ERR(imem_clk); - } - ret = request_irq(INT_GRAPHICS, hw3d_irq_handler, - IRQF_TRIGGER_HIGH, "hw3d", 0); + for (i = 0; i < HW3D_NUM_REGIONS; ++i) { + info->regions[i].pbase = res[i]->start; + info->regions[i].size = res[i]->end - res[i]->start + 1; + info->regions[i].vbase = ioremap(info->regions[i].pbase, + info->regions[i].size); + if (info->regions[i].vbase == 0) { + pr_err("%s: Cannot remap region %d\n", __func__, i); + goto err_remap_region; + } + } + + /* register the master/client devices */ + info->master_dev.name = "msm_hw3dm"; + info->master_dev.minor = MISC_DYNAMIC_MINOR; + info->master_dev.fops = &hw3d_fops; + info->master_dev.parent = &pdev->dev; + ret = misc_register(&info->master_dev); if (ret) { - clk_put(grp_clk); - clk_put(imem_clk); - return ret; + pr_err("%s: Cannot register master device node\n", __func__); + goto err_misc_reg_master; + } + + info->client_dev.name = "msm_hw3dc"; + info->client_dev.minor = MISC_DYNAMIC_MINOR; + info->client_dev.fops = &hw3d_fops; + info->client_dev.parent = &pdev->dev; + ret = misc_register(&info->client_dev); + if (ret) { + pr_err("%s: Cannot register client device node\n", __func__); + goto err_misc_reg_client; } - hw3d_disable_interrupt(); - hw3d_granted = 0; - return pmem_setup(&pmem_data, hw3d_ioctl, hw3d_release); + info->early_suspend.suspend = hw3d_early_suspend; + info->early_suspend.resume = hw3d_late_resume; + info->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + register_early_suspend(&info->early_suspend); + + info->irq_en = 1; + ret = request_irq(info->irq, hw3d_irq_handler, IRQF_TRIGGER_HIGH, + "hw3d", info); + if (ret != 0) { + pr_err("%s: Cannot request irq\n", __func__); + goto err_req_irq; + } + hw3d_disable_interrupt(info); + + hw3d_info = info; + + return 0; + +err_req_irq: + unregister_early_suspend(&info->early_suspend); + misc_deregister(&info->client_dev); +err_misc_reg_client: + misc_deregister(&info->master_dev); +err_misc_reg_master: +err_remap_region: + for (i = 0; i < HW3D_NUM_REGIONS; ++i) + if (info->regions[i].vbase != 0) + iounmap(info->regions[i].vbase); + clk_put(info->imem_clk); +err_get_imem_clk: + clk_put(info->grp_clk); +err_get_grp_clk: + wake_lock_destroy(&info->wake_lock); + kfree(info); + platform_set_drvdata(pdev, NULL); +err_alloc: + hw3d_info = NULL; + return ret; +} + +static struct platform_driver msm_hw3d_driver = { + .probe = hw3d_probe, + .resume = hw3d_resume, + .driver = { + .name = "msm_hw3d", + .owner = THIS_MODULE, + }, +}; + +static int __init hw3d_init(void) +{ + return platform_driver_register(&msm_hw3d_driver); } device_initcall(hw3d_init); diff --git a/include/linux/msm_hw3d.h b/include/linux/msm_hw3d.h new file mode 100644 index 0000000000000..c79bb39f5755a --- /dev/null +++ b/include/linux/msm_hw3d.h @@ -0,0 +1,54 @@ +/* include/linux/msm_hw3d.h + * + * Copyright (C) 2007 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 _MSM_HW3D_H_ +#define _MSM_HW3D_H_ + +#include +#include + +struct hw3d_region; + +#define HW3D_IOCTL_MAGIC 'h' +#define HW3D_WAIT_FOR_REVOKE _IO(HW3D_IOCTL_MAGIC, 0x80) +#define HW3D_WAIT_FOR_INTERRUPT _IO(HW3D_IOCTL_MAGIC, 0x81) +#define HW3D_GET_REGIONS \ + _IOR(HW3D_IOCTL_MAGIC, 0x82, struct hw3d_region *) + +#define HW3D_REGION_OFFSET(id) ((((uint32_t)(id)) & 0xf) << 28) +#define HW3D_REGION_ID(addr) (((uint32_t)(addr) >> 28) & 0xf) +#define HW3D_OFFSET_IN_REGION(addr) ((uint32_t)(addr) & ~(0xfUL << 28)) + +enum { + HW3D_EBI = 0, + HW3D_SMI = 1, + HW3D_REGS = 2, + + HW3D_NUM_REGIONS = HW3D_REGS + 1, +}; + +struct hw3d_region { + unsigned long phys; + unsigned long map_offset; + unsigned long len; +}; + +int get_msm_hw3d_file(int fd, int region, uint32_t offs, unsigned long *pbase, + unsigned long *len, struct file **filp); +void put_msm_hw3d_file(struct file *file); +bool is_msm_hw3d_file(struct file *file); + +#endif /* _MSM_HW3D_H_ */ From 58aa91db7b7e6b4699dfc7ff711a84d9629e8f1a Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 9 Jul 2009 16:32:01 -0700 Subject: [PATCH 0337/2556] video: msmfb: Put the partial update magic value into the fix_screen struct. This can then be tested by userspace to see if the capability is supported. Userspace cannot rely on that value being left in var_screen, since userspace itself can change it. Signed-off-by: Dima Zavin --- drivers/video/msm/msm_fb.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index debe5933fd2e6..ecdb8ff403572 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -470,6 +470,14 @@ static void setup_fb_info(struct msmfb_info *msmfb) fb_info->var.yoffset = 0; if (msmfb->panel->caps & MSMFB_CAP_PARTIAL_UPDATES) { + /* set the param in the fixed screen, so userspace can't + * change it. This will be used to check for the + * capability. */ + fb_info->fix.reserved[0] = 0x5444; + fb_info->fix.reserved[1] = 0x5055; + + /* This preloads the value so that if userspace doesn't + * change it, it will be a full update */ fb_info->var.reserved[0] = 0x54445055; fb_info->var.reserved[1] = 0; fb_info->var.reserved[2] = (uint16_t)msmfb->xres | From 5d186efe3f5cdb0f8f1990eb1a162dd658117c99 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 4 Aug 2009 23:48:12 -0700 Subject: [PATCH 0338/2556] [ARM] video: msmfb: Add support for using msm_hw3d fds for blits Signed-off-by: Dima Zavin --- drivers/video/msm/mdp.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index c3636d55a3c5a..f5077041b8e57 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -22,7 +22,9 @@ #include #include #include +#include #include +#include #include #include @@ -259,6 +261,18 @@ int get_img(struct mdp_img *img, struct fb_info *info, int put_needed, ret = 0; struct file *file; + if (!get_pmem_file(img->memory_id, start, &vstart, len, filep)) + return 0; + + ret = get_msm_hw3d_file(img->memory_id, HW3D_REGION_ID(img->offset), + HW3D_OFFSET_IN_REGION(img->offset), start, len, + filep); + if (!ret) { + /* need to chop off the region id from the user offset */ + img->offset = HW3D_OFFSET_IN_REGION(img->offset); + return 0; + } + file = fget_light(img->memory_id, &put_needed); if (file == NULL) return -1; @@ -266,6 +280,7 @@ int get_img(struct mdp_img *img, struct fb_info *info, if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) { *start = info->fix.smem_start; *len = info->fix.smem_len; + ret = 0; } else ret = -1; fput_light(file, put_needed); @@ -275,6 +290,18 @@ int get_img(struct mdp_img *img, struct fb_info *info, void put_img(struct file *src_file, struct file *dst_file) { + if (src_file) { + if (is_pmem_file(src_file)) + put_pmem_file(src_file); + else if (is_msm_hw3d_file(src_file)) + put_msm_hw3d_file(src_file); + } + if (dst_file) { + if (is_pmem_file(dst_file)) + put_pmem_file(dst_file); + else if (is_msm_hw3d_file(dst_file)) + put_msm_hw3d_file(dst_file); + } } int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, From adaad69efc1e1790dacd1d24ef0a7112bdb510c9 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 12 Aug 2009 16:41:43 -0700 Subject: [PATCH 0339/2556] [ARM] video: msm: mdp: Make put_img operate on one file at a time Signed-off-by: Dima Zavin --- drivers/video/msm/mdp.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index f5077041b8e57..87d0dbbfda557 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -288,19 +288,13 @@ int get_img(struct mdp_img *img, struct fb_info *info, return ret; } -void put_img(struct file *src_file, struct file *dst_file) +static void put_img(struct file *file) { - if (src_file) { - if (is_pmem_file(src_file)) - put_pmem_file(src_file); - else if (is_msm_hw3d_file(src_file)) - put_msm_hw3d_file(src_file); - } - if (dst_file) { - if (is_pmem_file(dst_file)) - put_pmem_file(dst_file); - else if (is_msm_hw3d_file(dst_file)) - put_msm_hw3d_file(dst_file); + if (file) { + if (is_pmem_file(file)) + put_pmem_file(file); + else if (is_msm_hw3d_file(file)) + put_msm_hw3d_file(file); } } @@ -333,6 +327,7 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, if (unlikely(get_img(&req->dst, fb, &dst_start, &dst_len, &dst_file))) { printk(KERN_ERR "mpd_ppp: could not retrieve dst image from " "memory\n"); + put_img(src_file); return -EINVAL; } mutex_lock(&mdp_mutex); @@ -377,13 +372,15 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, if (ret) goto err_wait_failed; end: - put_img(src_file, dst_file); + put_img(src_file); + put_img(dst_file); mutex_unlock(&mdp_mutex); return 0; err_bad_blit: disable_mdp_irq(mdp, DL0_ROI_DONE); err_wait_failed: - put_img(src_file, dst_file); + put_img(src_file); + put_img(dst_file); mutex_unlock(&mdp_mutex); return ret; } From 75d00e1a33e82b267075d421d1050f5738b98c1e Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 12 Aug 2009 17:17:45 -0700 Subject: [PATCH 0340/2556] [ARM] msm: hw3d: Clean up the interface to get file from fd Signed-off-by: Dima Zavin --- arch/arm/mach-msm/hw3d.c | 9 ++++++--- drivers/video/msm/mdp.c | 16 +++++----------- include/linux/msm_hw3d.h | 2 +- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-msm/hw3d.c b/arch/arm/mach-msm/hw3d.c index 72845b24cc32a..2f4a7c00d6004 100644 --- a/arch/arm/mach-msm/hw3d.c +++ b/arch/arm/mach-msm/hw3d.c @@ -296,20 +296,22 @@ void put_msm_hw3d_file(struct file *file) fput(file); } -int get_msm_hw3d_file(int fd, int region, uint32_t offs, unsigned long *pbase, +int get_msm_hw3d_file(int fd, uint32_t *offs, unsigned long *pbase, unsigned long *len, struct file **filp) { struct hw3d_info *info = hw3d_info; struct file *file; struct hw3d_data *data; + uint32_t offset = HW3D_OFFSET_IN_REGION(*offs); + int region = HW3D_REGION_ID(*offs); int ret = 0; if (unlikely(region >= HW3D_NUM_REGIONS)) { VDBG("hw3d: invalid region %d requested\n", region); return -EINVAL; - } else if (unlikely(offs >= info->regions[region].size)) { + } else if (unlikely(offset >= info->regions[region].size)) { VDBG("hw3d: offset %08x outside of the requested region %d\n", - offs, region); + offset, region); return -EINVAL; } @@ -338,6 +340,7 @@ int get_msm_hw3d_file(int fd, int region, uint32_t offs, unsigned long *pbase, goto err; } + *offs = offset; *pbase = info->regions[region].pbase; *filp = file; *len = data->vmas[region]->vm_end - data->vmas[region]->vm_start; diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 87d0dbbfda557..693b75d48d3e7 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -254,24 +254,18 @@ void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride, } } -int get_img(struct mdp_img *img, struct fb_info *info, - unsigned long *start, unsigned long *len, - struct file **filep) +static int get_img(struct mdp_img *img, struct fb_info *info, + unsigned long *start, unsigned long *len, + struct file** filep) { int put_needed, ret = 0; struct file *file; if (!get_pmem_file(img->memory_id, start, &vstart, len, filep)) return 0; - - ret = get_msm_hw3d_file(img->memory_id, HW3D_REGION_ID(img->offset), - HW3D_OFFSET_IN_REGION(img->offset), start, len, - filep); - if (!ret) { - /* need to chop off the region id from the user offset */ - img->offset = HW3D_OFFSET_IN_REGION(img->offset); + else if (!get_msm_hw3d_file(img->memory_id, &img->offset, start, len, + filep)) return 0; - } file = fget_light(img->memory_id, &put_needed); if (file == NULL) diff --git a/include/linux/msm_hw3d.h b/include/linux/msm_hw3d.h index c79bb39f5755a..a4afd8790de7d 100644 --- a/include/linux/msm_hw3d.h +++ b/include/linux/msm_hw3d.h @@ -46,7 +46,7 @@ struct hw3d_region { unsigned long len; }; -int get_msm_hw3d_file(int fd, int region, uint32_t offs, unsigned long *pbase, +int get_msm_hw3d_file(int fd, uint32_t *offs, unsigned long *pbase, unsigned long *len, struct file **filp); void put_msm_hw3d_file(struct file *file); bool is_msm_hw3d_file(struct file *file); From 3c43aef2ac2691177f27f8181acea6786bb098cf Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 12 Aug 2009 23:15:05 -0700 Subject: [PATCH 0341/2556] [ARM] msm: hw3d: Don't reset client_file until release time This addresses a potential problem where the current client started an MDP blit operation, and then got revoked before the blit has finished. The new client should have to wait until the MDP is done, which will fput the current client file. Signed-off-by: Dima Zavin --- arch/arm/mach-msm/hw3d.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-msm/hw3d.c b/arch/arm/mach-msm/hw3d.c index 2f4a7c00d6004..4ac083251aca4 100644 --- a/arch/arm/mach-msm/hw3d.c +++ b/arch/arm/mach-msm/hw3d.c @@ -237,7 +237,6 @@ static void locked_hw3d_client_done(struct hw3d_info *info, int had_timer) clk_disable(info->imem_clk); } info->revoking = 0; - info->client_file = NULL; /* double check that the irqs are disabled */ locked_hw3d_irq_disable(info); @@ -478,7 +477,9 @@ static int hw3d_release(struct inode *inode, struct file *file) pr_debug("hw3d: had file\n"); pending = del_timer(&info->revoke_timer); locked_hw3d_client_done(info, pending); - } + info->client_file = NULL; + } else + pr_warning("hw3d: release without client_file.\n"); spin_unlock_irqrestore(&info->lock, flags); done: @@ -625,7 +626,7 @@ static void hw3d_early_suspend(struct early_suspend *h) spin_lock_irqsave(&info->lock, flags); info->suspending = 1; if (info->client_file) { - pr_debug("hw3d: Requesting revoke for suspend\n"); + pr_info("hw3d: Requesting revoke for suspend\n"); locked_hw3d_revoke(info); } spin_unlock_irqrestore(&info->lock, flags); @@ -638,7 +639,8 @@ static void hw3d_late_resume(struct early_suspend *h) info = container_of(h, struct hw3d_info, early_suspend); spin_lock_irqsave(&info->lock, flags); - pr_info("%s: resuming\n", __func__); + if (info->suspending) + pr_info("%s: resuming\n", __func__); info->suspending = 0; spin_unlock_irqrestore(&info->lock, flags); } @@ -649,7 +651,8 @@ static int hw3d_resume(struct platform_device *pdev) unsigned long flags; spin_lock_irqsave(&info->lock, flags); - pr_info("%s: resuming\n", __func__); + if (info->suspending) + pr_info("%s: resuming\n", __func__); info->suspending = 0; spin_unlock_irqrestore(&info->lock, flags); return 0; From af60201d9669d69182d29310051392714c6df801 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 14 Aug 2009 16:51:59 -0700 Subject: [PATCH 0342/2556] [ARM] msm: mdp: Add support for RGBX 8888 image format. Signed-off-by: Dima Zavin --- drivers/video/msm/mdp_hw.h | 9 ++++++++- drivers/video/msm/mdp_ppp.c | 1 + include/linux/msm_mdp.h | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h index 4e3deb4e592b3..9e1e92ef3edbe 100644 --- a/drivers/video/msm/mdp_hw.h +++ b/drivers/video/msm/mdp_hw.h @@ -449,6 +449,7 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, #define PPP_CFG_MDP_XRGB_8888(dir) PPP_CFG_MDP_ARGB_8888(dir) #define PPP_CFG_MDP_RGBA_8888(dir) PPP_CFG_MDP_ARGB_8888(dir) #define PPP_CFG_MDP_BGRA_8888(dir) PPP_CFG_MDP_ARGB_8888(dir) +#define PPP_CFG_MDP_RGBX_8888(dir) PPP_CFG_MDP_ARGB_8888(dir) #define PPP_CFG_MDP_Y_CBCR_H2V2(dir) (PPP_##dir##_C2R_8BIT | \ PPP_##dir##_C0G_8BIT | \ @@ -494,6 +495,8 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R, 8) #define PPP_PACK_PATTERN_MDP_BGRA_8888 \ MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, 8) +#define PPP_PACK_PATTERN_MDP_RGBX_8888 \ + MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R, 8) #define PPP_PACK_PATTERN_MDP_Y_CBCR_H2V1 \ MDP_GET_PACK_PATTERN(0, 0, CLR_CB, CLR_CR, 8) #define PPP_PACK_PATTERN_MDP_Y_CBCR_H2V2 PPP_PACK_PATTERN_MDP_Y_CBCR_H2V1 @@ -509,6 +512,7 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, #define PPP_CHROMA_SAMP_MDP_ARGB_8888(dir) PPP_OP_##dir##_CHROMA_RGB #define PPP_CHROMA_SAMP_MDP_RGBA_8888(dir) PPP_OP_##dir##_CHROMA_RGB #define PPP_CHROMA_SAMP_MDP_BGRA_8888(dir) PPP_OP_##dir##_CHROMA_RGB +#define PPP_CHROMA_SAMP_MDP_RGBX_8888(dir) PPP_OP_##dir##_CHROMA_RGB #define PPP_CHROMA_SAMP_MDP_Y_CBCR_H2V1(dir) PPP_OP_##dir##_CHROMA_H2V1 #define PPP_CHROMA_SAMP_MDP_Y_CBCR_H2V2(dir) PPP_OP_##dir##_CHROMA_420 #define PPP_CHROMA_SAMP_MDP_Y_CRCB_H2V1(dir) PPP_OP_##dir##_CHROMA_H2V1 @@ -523,6 +527,7 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, [MDP_ARGB_8888] = PPP_##name##_MDP_ARGB_8888,\ [MDP_RGBA_8888] = PPP_##name##_MDP_RGBA_8888,\ [MDP_BGRA_8888] = PPP_##name##_MDP_BGRA_8888,\ + [MDP_RGBX_8888] = PPP_##name##_MDP_RGBX_8888,\ [MDP_Y_CBCR_H2V1] = PPP_##name##_MDP_Y_CBCR_H2V1,\ [MDP_Y_CBCR_H2V2] = PPP_##name##_MDP_Y_CBCR_H2V2,\ [MDP_Y_CRCB_H2V1] = PPP_##name##_MDP_Y_CRCB_H2V1,\ @@ -536,6 +541,7 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, [MDP_ARGB_8888] = PPP_##name##_MDP_ARGB_8888(dir),\ [MDP_RGBA_8888] = PPP_##name##_MDP_RGBA_8888(dir),\ [MDP_BGRA_8888] = PPP_##name##_MDP_BGRA_8888(dir),\ + [MDP_RGBX_8888] = PPP_##name##_MDP_RGBX_8888(dir),\ [MDP_Y_CBCR_H2V1] = PPP_##name##_MDP_Y_CBCR_H2V1(dir),\ [MDP_Y_CBCR_H2V2] = PPP_##name##_MDP_Y_CBCR_H2V2(dir),\ [MDP_Y_CRCB_H2V1] = PPP_##name##_MDP_Y_CRCB_H2V1(dir),\ @@ -547,7 +553,8 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, (img == MDP_YCRYCB_H2V1)) #define IS_RGB(img) ((img == MDP_RGB_565) | (img == MDP_RGB_888) | \ (img == MDP_ARGB_8888) | (img == MDP_RGBA_8888) | \ - (img == MDP_XRGB_8888) | (img == MDP_BGRA_8888)) + (img == MDP_XRGB_8888) | (img == MDP_BGRA_8888) | \ + (img == MDP_RGBX_8888)) #define HAS_ALPHA(img) ((img == MDP_ARGB_8888) | (img == MDP_RGBA_8888) | \ (img == MDP_BGRA_8888)) diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c index 4ff001f4cbbdd..2b6564e8bfeaa 100644 --- a/drivers/video/msm/mdp_ppp.c +++ b/drivers/video/msm/mdp_ppp.c @@ -69,6 +69,7 @@ static uint32_t bytes_per_pixel[] = { [MDP_ARGB_8888] = 4, [MDP_RGBA_8888] = 4, [MDP_BGRA_8888] = 4, + [MDP_RGBX_8888] = 4, [MDP_Y_CBCR_H2V1] = 1, [MDP_Y_CBCR_H2V2] = 1, [MDP_Y_CRCB_H2V1] = 1, diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h index d11fe0f2f956f..fe722c1fb61d5 100644 --- a/include/linux/msm_mdp.h +++ b/include/linux/msm_mdp.h @@ -32,6 +32,7 @@ enum { MDP_Y_CBCR_H2V1, /* Y and CrCb, pseduo planar w/ Cr is in MSB */ MDP_RGBA_8888, /* ARGB 888 */ MDP_BGRA_8888, /* ABGR 888 */ + MDP_RGBX_8888, /* RGBX 888 */ MDP_IMGTYPE_LIMIT /* Non valid image type after this enum */ }; From 6de38c5e6f56b53258ce75960106503108f775af Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 14 Aug 2009 18:10:06 -0700 Subject: [PATCH 0343/2556] [ARM] msm: mdp: Set the correct pack pattern for XRGB/ARGB Signed-off-by: Dima Zavin --- drivers/video/msm/mdp_hw.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h index 9e1e92ef3edbe..d80477415caaa 100644 --- a/drivers/video/msm/mdp_hw.h +++ b/drivers/video/msm/mdp_hw.h @@ -489,7 +489,7 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 8) #define PPP_PACK_PATTERN_MDP_RGB_888 PPP_PACK_PATTERN_MDP_RGB_565 #define PPP_PACK_PATTERN_MDP_XRGB_8888 \ - MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_R, CLR_G, CLR_B, 8) + MDP_GET_PACK_PATTERN(CLR_B, CLR_G, CLR_R, CLR_ALPHA, 8) #define PPP_PACK_PATTERN_MDP_ARGB_8888 PPP_PACK_PATTERN_MDP_XRGB_8888 #define PPP_PACK_PATTERN_MDP_RGBA_8888 \ MDP_GET_PACK_PATTERN(CLR_ALPHA, CLR_B, CLR_G, CLR_R, 8) From 7ffb63959e348c251a0077c7175e3a2098d0667b Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sat, 19 Sep 2009 02:39:33 -0700 Subject: [PATCH 0344/2556] [ARM] video: mdp: add flag for fg-premultiplied alpha blend Signed-off-by: Dima Zavin --- include/linux/msm_mdp.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h index fe722c1fb61d5..32ada8376c663 100644 --- a/include/linux/msm_mdp.h +++ b/include/linux/msm_mdp.h @@ -50,6 +50,7 @@ enum { #define MDP_ROT_270 (MDP_ROT_90|MDP_FLIP_UD|MDP_FLIP_LR) #define MDP_DITHER 0x8 #define MDP_BLUR 0x10 +#define MDP_BLEND_FG_PREMULT 0x20000 #define MDP_TRANSP_NOP 0xffffffff #define MDP_ALPHA_NOP 0xff From 1680941bc1aeb9d3b3d231d1e39b383af496570e Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Fri, 21 Aug 2009 16:28:17 -0700 Subject: [PATCH 0345/2556] [ARM] msm: smd_rpc: Fix wakelock leak for interrupted rpc call/reply. Discard incoming packets when rpc_call_reply is not expecting a reply. Always process for server. Signed-off-by: Mike Chan --- .../arm/mach-msm/include/mach/msm_rpcrouter.h | 1 + arch/arm/mach-msm/qdsp5/adsp.c | 4 +-- arch/arm/mach-msm/qdsp5/audmgr.c | 2 +- arch/arm/mach-msm/smd_rpcrouter.c | 28 +++++++++++++++---- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/msm_rpcrouter.h b/arch/arm/mach-msm/include/mach/msm_rpcrouter.h index 9724ece1c97c9..5e517055fe57f 100644 --- a/arch/arm/mach-msm/include/mach/msm_rpcrouter.h +++ b/arch/arm/mach-msm/include/mach/msm_rpcrouter.h @@ -125,6 +125,7 @@ struct rpc_reply_hdr /* flags for msm_rpc_connect() */ #define MSM_RPC_UNINTERRUPTIBLE 0x0001 +#define MSM_RPC_ENABLE_RECEIVE (0x10000) /* use IS_ERR() to check for failure */ struct msm_rpc_endpoint *msm_rpc_open(void); diff --git a/arch/arm/mach-msm/qdsp5/adsp.c b/arch/arm/mach-msm/qdsp5/adsp.c index 729bb476aac0b..a92fc0519eab7 100644 --- a/arch/arm/mach-msm/qdsp5/adsp.c +++ b/arch/arm/mach-msm/qdsp5/adsp.c @@ -195,7 +195,7 @@ static int adsp_rpc_init(struct msm_adsp_module *adsp_module) adsp_module->rpc_client = msm_rpc_connect( RPC_ADSP_RTOS_ATOM_PROG, RPC_ADSP_RTOS_ATOM_VERS, - MSM_RPC_UNINTERRUPTIBLE); + MSM_RPC_UNINTERRUPTIBLE | MSM_RPC_ENABLE_RECEIVE); if (IS_ERR(adsp_module->rpc_client)) { int rc = PTR_ERR(adsp_module->rpc_client); @@ -220,7 +220,7 @@ static void msm_get_init_info(void) adsp_info.init_info_rpc_client = msm_rpc_connect( RPC_ADSP_RTOS_ATOM_PROG, RPC_ADSP_RTOS_ATOM_VERS, - MSM_RPC_UNINTERRUPTIBLE); + MSM_RPC_UNINTERRUPTIBLE | MSM_RPC_ENABLE_RECEIVE); if (IS_ERR(adsp_info.init_info_rpc_client)) { rc = PTR_ERR(adsp_info.init_info_rpc_client); adsp_info.init_info_rpc_client = 0; diff --git a/arch/arm/mach-msm/qdsp5/audmgr.c b/arch/arm/mach-msm/qdsp5/audmgr.c index c1d60b30bb355..b7d8158dab24e 100644 --- a/arch/arm/mach-msm/qdsp5/audmgr.c +++ b/arch/arm/mach-msm/qdsp5/audmgr.c @@ -209,7 +209,7 @@ int audmgr_open(struct audmgr *am) am->ept = msm_rpc_connect(AUDMGR_PROG, AUDMGR_VERS, - MSM_RPC_UNINTERRUPTIBLE); + MSM_RPC_UNINTERRUPTIBLE | MSM_RPC_ENABLE_RECEIVE); init_waitqueue_head(&am->wait); diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c index 7cd3458204de4..5ac7053a3d1c1 100644 --- a/arch/arm/mach-msm/smd_rpcrouter.c +++ b/arch/arm/mach-msm/smd_rpcrouter.c @@ -280,6 +280,7 @@ struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev) ept->dst_cid = srv->cid; ept->dst_prog = cpu_to_be32(srv->prog); ept->dst_vers = cpu_to_be32(srv->vers); + ept->flags |= MSM_RPC_ENABLE_RECEIVE; D("Creating local ept %p @ %08x:%08x\n", ept, srv->prog, srv->vers); } else { @@ -677,9 +678,15 @@ static void do_read_data(struct work_struct *work) packet_complete: spin_lock_irqsave(&ept->read_q_lock, flags); - wake_lock(&ept->read_q_wake_lock); - list_add_tail(&pkt->list, &ept->read_q); - wake_up(&ept->wait_q); + if (ept->flags & MSM_RPC_ENABLE_RECEIVE) { + wake_lock(&ept->read_q_wake_lock); + list_add_tail(&pkt->list, &ept->read_q); + wake_up(&ept->wait_q); + } else { + pr_warning("smd_rpcrouter: Unexpected incoming data on %08x:%08x\n", + be32_to_cpu(ept->dst_prog), + be32_to_cpu(ept->dst_vers)); + } spin_unlock_irqrestore(&ept->read_q_lock, flags); done: @@ -954,14 +961,17 @@ int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc, req->vers = ept->dst_vers; req->procedure = cpu_to_be32(proc); + /* Allow replys to be added to the queue */ + ept->flags |= MSM_RPC_ENABLE_RECEIVE; + rc = msm_rpc_write(ept, req, request_size); if (rc < 0) - return rc; + goto error; for (;;) { rc = msm_rpc_read(ept, (void*) &reply, -1, timeout); if (rc < 0) - return rc; + goto error; if (rc < (3 * sizeof(uint32_t))) { rc = -EIO; break; @@ -999,6 +1009,10 @@ int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc, break; } kfree(reply); +error: + ept->flags &= ~MSM_RPC_ENABLE_RECEIVE; + wake_unlock(&ept->read_q_wake_lock); + return rc; } EXPORT_SYMBOL(msm_rpc_call_reply); @@ -1204,6 +1218,7 @@ int msm_rpc_register_server(struct msm_rpc_endpoint *ept, if (rc < 0) return rc; + ept->flags |= MSM_RPC_ENABLE_RECEIVE; return 0; } @@ -1216,6 +1231,9 @@ int msm_rpc_unregister_server(struct msm_rpc_endpoint *ept, if (!server) return -ENOENT; + + ept->flags &= ~MSM_RPC_ENABLE_RECEIVE; + wake_unlock(&ept->read_q_wake_lock); rpcrouter_destroy_server(server); return 0; } From f1d10941b140e788a9f27a8010838607992563d8 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Tue, 1 Sep 2009 14:01:11 -0700 Subject: [PATCH 0346/2556] [ARM] mach-msm: nand: Add Virtual kpanic mtd partition support. Adds an option to snag a portion of a specified partition into a virtual partition named 'kpanic' Useful for platforms which do not have a kpanic partition but want to take advantage of CONFIG_APANIC logging. As this is not a real nand partition, both the snagged and new partitions should be erased when switching to/from a kernel with this option enabled/disabled. **** WARNING **** DO NOT enable if you are using a Recovery/OTA scheme in which the recovery kernel needs to access the partition you are snagging from. If you do not heed this warning, you may end up with a corrupted OTA image and there be dragons. Just don't do it. **** WARNING **** Signed-off-by: San Mehat --- arch/arm/mach-msm/Kconfig | 23 ++++++++++++++++ arch/arm/mach-msm/nand_partitions.c | 41 +++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index d1edb8498d947..226e18bf66c7c 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -489,4 +489,27 @@ config WIFI_MEM_PREALLOC bool "Preallocate memory for WiFi buffers" help Preallocates memory buffers for WiFi driver + +config VIRTUAL_KPANIC_PARTITION + bool "Create virtual kpanic partition" + default n + help + Creates a virtual mtd partition named 'kpanic', stealing space from + the specified mtd partition label. + *** DO NOT USE IF YOU ARE USING OTA/RECOVERY *** + +config VIRTUAL_KPANIC_PSIZE + depends on VIRTUAL_KPANIC_PARTITION + int "Default kpanic partition size" + default 1048576 + help + Sets the size of the virtual kpanic paritition to create. + +config VIRTUAL_KPANIC_SRC + depends on VIRTUAL_KPANIC_PARTITION + string "Partition to steal from" + default "cache" + help + Sets the partition to steal from to make the virtual one. + endif diff --git a/arch/arm/mach-msm/nand_partitions.c b/arch/arm/mach-msm/nand_partitions.c index 482e6958504b4..14c579264ec33 100644 --- a/arch/arm/mach-msm/nand_partitions.c +++ b/arch/arm/mach-msm/nand_partitions.c @@ -33,6 +33,7 @@ #include + /* configuration tags specific to msm */ #define ATAG_MSM_PARTITION 0x4d534D70 /* MSMp */ @@ -58,6 +59,7 @@ static int __init parse_tag_msm_partition(const struct tag *tag) char *name = msm_nand_names; struct msm_ptbl_entry *entry = (void *) &tag->u; unsigned count, n; + unsigned have_kpanic = 0; count = (tag->hdr.size - 2) / (sizeof(struct msm_ptbl_entry) / sizeof(__u32)); @@ -69,6 +71,9 @@ static int __init parse_tag_msm_partition(const struct tag *tag) memcpy(name, entry->name, 15); name[15] = 0; + if (!strcmp(name, "kpanic")) + have_kpanic = 1; + ptn->name = name; ptn->offset = entry->offset * 64 * 2048; ptn->size = entry->size * 64 * 2048; @@ -78,6 +83,42 @@ static int __init parse_tag_msm_partition(const struct tag *tag) ptn++; } +#if CONFIG_VIRTUAL_KPANIC_PARTITION + if (!have_kpanic) { + int i; + uint64_t kpanic_off = 0; + + if (count == MSM_MAX_PARTITIONS) { + printk("Cannot create virtual 'kpanic' partition\n"); + goto out; + } + + for (i = 0; i < count; i++) { + ptn = &msm_nand_partitions[i]; + if (!strcmp(ptn->name, CONFIG_VIRTUAL_KPANIC_SRC)) { + ptn->size -= CONFIG_VIRTUAL_KPANIC_PSIZE; + kpanic_off = ptn->offset + ptn->size; + break; + } + } + if (i == count) { + printk(KERN_ERR "Partition %s not found\n", + CONFIG_VIRTUAL_KPANIC_SRC); + goto out; + } + + ptn = &msm_nand_partitions[count]; + ptn->name ="kpanic"; + ptn->offset = kpanic_off; + ptn->size = CONFIG_VIRTUAL_KPANIC_PSIZE; + + printk("Virtual mtd partition '%s' created @%llx (%llu)\n", + ptn->name, ptn->offset, ptn->size); + + count++; + } +#endif /* CONFIG_VIRTUAL_KPANIC_SRC */ +out: msm_nand_data.nr_parts = count; msm_nand_data.parts = msm_nand_partitions; From a97c1848a490807a2935281c52d6fbf7275d1726 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Fri, 11 Sep 2009 11:46:08 -0400 Subject: [PATCH 0347/2556] [ARM] msm: htc: Avoid usb_mass_storage_platform_data symbol collision. Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/devices_htc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c index 7db0ac7289fc6..c47a5acce4d8c 100644 --- a/arch/arm/mach-msm/devices_htc.c +++ b/arch/arm/mach-msm/devices_htc.c @@ -25,8 +25,13 @@ #include #include #include + +#ifdef CONFIG_USB_FUNCTION #include +#endif +#ifdef CONFIG_USB_ANDROID #include +#endif #include #include From f1d0fe171bab58c25422a3505df00c4a81a8f97e Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Mon, 14 Sep 2009 15:18:51 -0700 Subject: [PATCH 0348/2556] [ARM] msm: hw3d: stub out functions when CONFIG_MSM_HW3D not defined Signed-off-by: Iliyan Malchev --- include/linux/msm_hw3d.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/msm_hw3d.h b/include/linux/msm_hw3d.h index a4afd8790de7d..9178526952d60 100644 --- a/include/linux/msm_hw3d.h +++ b/include/linux/msm_hw3d.h @@ -46,9 +46,16 @@ struct hw3d_region { unsigned long len; }; +#ifdef CONFIG_MSM_HW3D int get_msm_hw3d_file(int fd, uint32_t *offs, unsigned long *pbase, unsigned long *len, struct file **filp); void put_msm_hw3d_file(struct file *file); bool is_msm_hw3d_file(struct file *file); +#else +int get_msm_hw3d_file(int fd, uint32_t *offs, unsigned long *pbase, + unsigned long *len, struct file **filp) { return -1; } +void put_msm_hw3d_file(struct file *file) {} +bool is_msm_hw3d_file(struct file *file) { return false; } +#endif #endif /* _MSM_HW3D_H_ */ From c4f29f326fd1c193345f2fd833ad8a9fce52d865 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Tue, 9 Feb 2010 20:59:48 -0800 Subject: [PATCH 0349/2556] [ARM] msm: camera: 8k support and bugfixes [ARM] msm: camera: fix memory corruptions and add checks on copy_from_user Signed-off-by: Iliyan Malchev [ARM] msm: camera: do an AXI reset together with the VFE reset Signed-off-by: Iliyan Malchev [ARM] msm: camera: s5k3e2fx-sensor updates Signed-off-by: Iliyan Malchev [ARM] msm: camera: add lookup for AF buffers Signed-off-by: Iliyan Malchev [ARM] msm: camera: s5k3e2fx: add suspend/resume Signed-off-by: Iliyan Malchev [ARM] msm: camera: add Code-Aurora license and GPL boilerplate Signed-off-by: Iliyan Malchev [ARM] msm: camera: add more support for postprocessing Signed-off-by: Iliyan Malchev [ARM] msm: camera: replace flash_type with a flash function Replace the old flash_type with a pointer to a function that takes in a flash level from [0, num_flash_levels). Signed-off-by: Iliyan Malchev [ARM] msm: make msm_camera_legacy_device_platform_data always available Signed-off-by: Iliyan Malchev [ARM] msm: add a need_suspend flag to the camera-sensor info struct When this flag is set in the board file, then the sensor's suspend/resume code is allowed to run. Otherwise, suspend/resume is a no-op. Signed-off-by: Iliyan Malchev [ARM] msm: camera: s5k3e2fx: check suspend flag Signed-off-by: Iliyan Malchev [ARM] msm: camera: msm8k updates Signed-off-by: Iliyan Malchev [ARM] msm: camera: use the camera_flash function from the board file Also, remove CONFIG_MSM_CAMERA_FLASH. This is now configured automarically via the board file. Signed-off-by: Iliyan Malchev [ARM] msm: camera: fix a memory leak in msm_vfe8x.c Signed-off-by: Iliyan Malchev [ARM] msm: camera: s5k3e2fx: color tuning update, AE flashing fix Signed-off-by: Iliyan Malchev [ARM] msm8k: camera: do not crash when frame skipping is disabled Signed-off-by: Iliyan Malchev [ARM] msm: camera: add GET_SENSOR_INFO to the control ioctl Signed-off-by: Iliyan Malchev [ARM] msm: camera: run through Lindent, make checkpatch happy Signed-off-by: Iliyan Malchev [ARM] qsq8k: camera: fix the return value of vfe_get_af_pingpong_status Signed-off-by: Iliyan Malchev [ARM] msm_camera: clean up msm_config_vfe Signed-off-by: Iliyan Malchev [ARM] qsd8k: camera: clean up of msm_vfe8x.c -- in vfe_config(), structures that can be local in scope were being allocated on the heap. -- added a pr_err() in vfe_release -- misc cleanup Signed-off-by: Iliyan Malchev [ARM] qsd8k: camera: cleanup of msm_vfe8x_proc.c -- change error-related CDBG to pr_err -- simplify vfe_get_awb_pingpong_status -- remove dead code -- rewrite the following functions to not return structs: vfe_parse_interrupt_status vfe_get_asf_frame_info vfe_get_demosaic_frame_info vfe_get_camif_status vfe_get_performance_monitor_data -- in vfe_send_output1_msg, replace memmove with memcpy -- removed unnecessary spinlocks Signed-off-by: Iliyan Malchev [ARM] msm: camera: clean up printks Signed-off-by: Iliyan Malchev [ARM] msm: camera: add vfe_free to complement vfe_alloc Signed-off-by: Iliyan Malchev [ARM] msm: s5k3e2fx: add retries for i2c transfers Signed-off-by: Iliyan Malchev [ARM] qsd8k: stub out msm_camio_vfe_blk_beset Signed-off-by: Iliyan Malchev [ARM] msm: camera: fix crashes on interrupted syscalls Signed-off-by: Iliyan Malchev [ARM] qsd8k: camera: cleanup of msm_vfe8x and msm_vfe8x_proc -- remove unnecessary mutexes and spinlocks -- eliminate unnecessary allocations -- eliminated allocations made by the vfe interrupt in favor of a statically-allocated ring buffer. -- use vfe_alloc for all allocations made by the VFE tasklet -- combined extdata and ctrl in one kzalloc -- rewrite vfe_proc_ops to use a table of callbacks and event mappings Signed-off-by: Iliyan Malchev [ARM] msm: camera: add a gfp mask to vfe_alloc and vfe_resp Signed-off-by: Iliyan Malchev [ARM] msm: camera: add on_heap field to msm_queue_cmd Signed-off-by: Iliyan Malchev [ARM] msm: camera: more cleanup -- renamed msm_sync::{frame, stats} to pmem_frames & pmem_stats -- keep track of the max length of prev_frame_q and msg_event_q -- make pict_pp a uint32_t to reflect usage from userspace -- in vfe_sync, preview and snapshot frames were being duplicated on the heap and then sent to both the config thread and the frame/control thread; the config thread would in turn take its copy of the frame or snapshot frames and discard them -- simplified and cleaned up postprocessing -- no need for pp_sync_flag -- send frames and snapshots to the config thread ONLY when PP_PREV, PP_SNAP, or PP_RAW_SNAP are enabled. -- allow only one type of PP at a time: you can't have PP_PREV and PP_SNAP together for example; userspace does not use the more than one PP at a time -- added msm_divert_frame and msm_divert_snapshot to reduce indentation in msm_get_stats -- renamed MSM_CAM_IOCTL_PICT_PP and MSM_CAM_IOCTL_PICT_PP_DONE to just MSM_CAM_IOCTL_PP and MSM_CAM_IOCTL_PP_DONE, since they apply to post- processing for preview as well as snapshot -- added msm_deliver_frame and msm_deliver_snapshot, which are used from vfe_sync and msm_pp_release to deliver preview and snapshot frames respectively -- in msm_pp_release, added a check for length of user-space allocation request limiting it to PAGE_SIZE Signed-off-by: Iliyan Malchev [ARM] msm: camera: eliminate kmalloc for MSM_CAM_IOCTL_CTRL_COMMAND packets Signed-off-by: Iliyan Malchev [ARM] qsd8k: camera: enable vfe_process_error_irq Signed-off-by: Iliyan Malchev [ARM] msm: s5k3e2fx: fix for low-light VFE CAMIF errors, cleanup Replaced all mdelay()s over 10ms with msleep()s. Signed-off-by: Iliyan Malchev [ARM] msm8k: fix camera-driver Makefile qsd8k: camera: request min AXI bus frequency of 128MHz Signed-off-by: Iliyan Malchev [ARM] msm: camera: cleanup; send preview and snapshot notifications to config CLEANUP: -- renamed pict_pp to pp_mask in msm_sync -- simplified MSM_CAM_IOCTL_PP_DONE to just pass the PP mask of what it wants done: now we use pp_prev and pp_snap to remember the preview and snapshot frames being diverted for posprocessing. The old API required userspace to remember the frame and pass it back to the kernel. -- added functions and macros to handle msm_device_queue: msm_queue_init msm_queue_drain /* replaces MSM_DRAIN_QUEUE{,_NOSYNC} */ msm_enqueue msm_dequeue -- new structure msm_device_queue that combines: list of msm_queue_cmd spinlock max and len counters wait queue -- use the new structure to replace: msg_event_q, msg_event_q_{lock,max,len}, msg_event_wait prev_frame_q, prev_frame_q_{lock,max,len,wait} pict_frame_q, pict_frame_q_lock, pict_frame_wait msm_control_device_queue CLONING FRAME AND SNAPSHOT TO CONFIG: The changes below re-enable sending frame and snapshot qcmd instances to config in addition to the frame/snapshot threads. This was implemented previously but the implementation was wasteful of memory. -- since msm_queue_cmd can be on more than one queue at a time (e.g., frame and config or snapshot and config), msm_queue_cmd has different list_head entries for each queue. Each call to msm_queue(), msm_dequeue(), and msm_queue_drain() uses the right list_head. -- msm_queue_cmd::on_heap, when non-zero, is now a reference counter; we use it to make sure we do not prematurely kfree() a qcmd in those cases where it is on two or more msm_device_queues. -- Added config ioctl MSM_CAM_IOCTL_ENABLE_OUTPUT_IND, which enables routing of preview frames to the config thread; by default, the flag is disabled POSTPROCESSING: -- add parameter clear_active to msm_pmem_frame_ptov_lookup() to control whether it clears the active flag of a preview or snapshot frame. This helps with PP_PREV postprocessing by making sure we do not mark a region as inactive too early. FIXES: -- in msm_control, remove the qcmd packet from the event_q if wait_event_interruptible_timeout fails. Signed-off-by: Iliyan Malchev [ARM] msm: camera: remove _t from variable names Signed-off-by: Iliyan Malchev [ARM] msm: camera: add cache invalidation for snapshot -- change msm_pmem_frame_ptov_lookup() to take a msm_pmem_region** instead of a msm_pmem_info*; -- add a kvaddr member to msm_pmem_region so that we can keep track of the kernel addresses of pmem pools; -- make sure that pmem registrations start on a page boundary; -- in msm_get_pic(), invalidate the cache for the (cacheline-aligned) pmem subregion corresponding to the snapshot being passed to userspace. Signed-off-by: Iliyan Malchev [ARM] msm: rename msm_pmem_info::active to vfe_can_write The meaning of "active" is very confusing. vfe_can_write means that the VFE block is allowed to DMA into that region of pmem. Signed-off-by: Iliyan Malchev [ARM] qsd8k: apparent fix for spurious VFE resets Signed-off-by: Iliyan Malchev [ARM] msm: camera: s5k3e2fx sensor adjustments -- adjust pclk to 72Mhz to reduce noise -- AF fine tune: -- reduce fps to reduce lens damping -- increase delay for lens to become stable -- #define REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB_VALUE to keep track more easily of registers necessary for initial-gain tuning -- #define REG_PLL_MULTIPLIER_LSB_VALUE to make PCLK configuration easier -- make video_config general set mode function call -- remove unnecessary flags -- remove redundant init AF -- remove unnecessary mutex -- make more readable Signed-off-by: Iliyan Malchev [ARM] qsd8k: camera: fix for spurious VFE resets The problem is that if we call clk_set_rate() on vfe_clk after we enable that clock, it becomes unstable. This overrides commit 372e0000e6e61be65e5809801fd396803502347f. Signed-off-by: Iliyan Malchev [ARM] qsd8k: camera: add pr_err() on failed copy_from_user Signed-off-by: Iliyan Malchev [ARM] qsd8k: camera: fix spinlocks Signed-off-by: Iliyan Malchev [ARM] msm: camera: fix a memory leak on msm_open_control error Signed-off-by: Iliyan Malchev [ARM] msm: camera: replace the idle lock with a suspend lock. Since the camera driver does not implement suspend and resume, we need to prevent suspend while the camera is in use. Signed-off-by: Iliyan Malchev [ARM] qsd8k: camera: initialize irqs_lock Signed-off-by: Iliyan Malchev [ARM] msm: camera: fix to build on 2.6.32 Change-Id: Ia4e5cd9d9f1e9d8faed943656cb8522bf9ad4408 [ARM] msm: camera: fix build on 2.6.32 Signed-off-by: Iliyan Malchev [ARM] msm: camera: fix return value of ioctl MSM_CAM_IOCTL_ENABLE_OUTPUT_IND Signed-off-by: Iliyan Malchev [ARM] qsd8k: camera: use epoch1 interrupt to control the flash more precisely We use the epoch1 interrupt to control flash timing in the kernel. The purpose is to reduce flash duration as much as possible. Userspace has no way to control the exact timing, but the VFE can do it. Flash should cover the exposure period of each line. Currently we skip a frame during snapshot, so snapshot uses two frames. We want to fire the flash in the middle of the first frame when the exposure time is short. We are using rolling shutter. It means flash duration = readout time + exposure time. We have to fire the flash earlier to cover the exposure time of the first line of the second frame. The epoch1 interrupt allows us to set a line index and we will get an interrupt when VFE reaches the line. VFE will fire the flash in high mode when it gets the epoch1 interrupt. VFE will turn off the flash after snapshot is done. Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/include/mach/camera.h | 88 +- drivers/media/video/Kconfig | 7 - drivers/media/video/msm/Makefile | 4 +- drivers/media/video/msm/msm_camera.c | 1269 +++++---- drivers/media/video/msm/msm_io7x.c | 48 +- drivers/media/video/msm/msm_io8x.c | 76 +- drivers/media/video/msm/msm_v4l2.c | 229 +- drivers/media/video/msm/msm_vfe7x.c | 595 +++-- drivers/media/video/msm/msm_vfe7x.h | 334 +-- drivers/media/video/msm/msm_vfe8x.c | 832 +++--- drivers/media/video/msm/msm_vfe8x.h | 352 +-- drivers/media/video/msm/msm_vfe8x_proc.c | 3053 ++++++++++----------- drivers/media/video/msm/msm_vfe8x_proc.h | 1743 ++++++------ drivers/media/video/msm/mt9d112.c | 495 ++-- drivers/media/video/msm/mt9d112.h | 20 +- drivers/media/video/msm/mt9d112_reg.c | 281 +- drivers/media/video/msm/mt9p012.h | 61 +- drivers/media/video/msm/mt9p012_fox.c | 779 +++--- drivers/media/video/msm/mt9p012_reg.c | 185 +- drivers/media/video/msm/mt9t013.c | 1160 ++++---- drivers/media/video/msm/mt9t013.h | 64 +- drivers/media/video/msm/mt9t013_reg.c | 422 +-- drivers/media/video/msm/s5k3e2fx.c | 3118 +++++++++++++++++----- drivers/media/video/msm/s5k3e2fx.h | 9 - include/media/msm_camera.h | 19 +- 25 files changed, 8625 insertions(+), 6618 deletions(-) delete mode 100644 drivers/media/video/msm/s5k3e2fx.h diff --git a/arch/arm/mach-msm/include/mach/camera.h b/arch/arm/mach-msm/include/mach/camera.h index 4897b7d80e169..3518b5c35a149 100644 --- a/arch/arm/mach-msm/include/mach/camera.h +++ b/arch/arm/mach-msm/include/mach/camera.h @@ -61,8 +61,11 @@ struct msm_vfe_resp { struct msm_vfe_callback { void (*vfe_resp)(struct msm_vfe_resp *, - enum msm_queue, void *syncdata); - void* (*vfe_alloc)(int, void *syncdata); + enum msm_queue, void *syncdata, + gfp_t gfp); + void* (*vfe_alloc)(int, void *syncdata, gfp_t gfp); + void (*vfe_free)(void *ptr); + int (*flash_ctrl)(void *syncdata, int level); }; struct msm_camvfe_fn { @@ -80,34 +83,51 @@ struct msm_sensor_ctrl { int (*s_config)(void __user *); }; +/* this structure is used in kernel */ +struct msm_queue_cmd { + struct list_head list_config; + struct list_head list_control; + struct list_head list_frame; + struct list_head list_pict; + enum msm_queue type; + void *command; + int on_heap; +}; + +struct msm_device_queue { + struct list_head list; + spinlock_t lock; + wait_queue_head_t wait; + int max; + int len; + const char *name; +}; + struct msm_sync { - /* These two queues are accessed from a process context only. */ - struct hlist_head frame; /* most-frequently accessed */ - struct hlist_head stats; + /* These two queues are accessed from a process context only. They contain + * pmem descriptors for the preview frames and the stats coming from the + * camera sensor. + */ + struct hlist_head pmem_frames; + struct hlist_head pmem_stats; /* The message queue is used by the control thread to send commands * to the config thread, and also by the DSP to send messages to the * config thread. Thus it is the only queue that is accessed from * both interrupt and process context. */ - spinlock_t msg_event_q_lock; - struct list_head msg_event_q; - wait_queue_head_t msg_event_wait; + struct msm_device_queue event_q; /* This queue contains preview frames. It is accessed by the DSP (in * in interrupt context, and by the frame thread. */ - spinlock_t prev_frame_q_lock; - struct list_head prev_frame_q; - wait_queue_head_t prev_frame_wait; + struct msm_device_queue frame_q; int unblock_poll_frame; /* This queue contains snapshot frames. It is accessed by the DSP (in * interrupt context, and by the control thread. */ - spinlock_t pict_frame_q_lock; - struct list_head pict_frame_q; - wait_queue_head_t pict_frame_wait; + struct msm_device_queue pict_q; struct msm_camera_sensor_info *sdata; struct msm_camvfe_fn vfefn; @@ -117,7 +137,15 @@ struct msm_sync { uint8_t opencnt; void *cropinfo; int croplen; - unsigned pict_pp; + + uint32_t pp_mask; + struct msm_queue_cmd *pp_prev; + struct msm_queue_cmd *pp_snap; + + /* When this flag is set, we send preview-frame notifications to config + * as well as to the frame queue. By default, the flag is cleared. + */ + uint32_t report_preview_to_config; const char *apps_id; @@ -138,26 +166,18 @@ struct msm_device { atomic_t opened; }; -struct msm_control_device_queue { - spinlock_t ctrl_status_q_lock; - struct list_head ctrl_status_q; - wait_queue_head_t ctrl_status_wait; -}; - struct msm_control_device { struct msm_device *pmsm; + /* Used for MSM_CAM_IOCTL_CTRL_CMD_DONE responses */ + uint8_t ctrl_data[50]; + struct msm_ctrl_cmd ctrl; + struct msm_queue_cmd qcmd; + /* This queue used by the config thread to send responses back to the * control thread. It is accessed only from a process context. */ - struct msm_control_device_queue ctrl_q; -}; - -/* this structure is used in kernel */ -struct msm_queue_cmd { - struct list_head list; - enum msm_queue type; - void *command; + struct msm_device_queue ctrl_q; }; struct register_address_value_pair { @@ -168,6 +188,7 @@ struct register_address_value_pair { struct msm_pmem_region { struct hlist_node list; unsigned long paddr; + unsigned long kvaddr; unsigned long len; struct file *file; struct msm_pmem_info info; @@ -179,15 +200,6 @@ struct axidata { struct msm_pmem_region *region; }; -#ifdef CONFIG_MSM_CAMERA_FLASH -int msm_camera_flash_set_led_state(unsigned led_state); -#else -static inline int msm_camera_flash_set_led_state(unsigned led_state) -{ - return -ENOTSUPP; -} -#endif - /* Below functions are added for V4L2 kernel APIs */ struct msm_v4l2_driver { struct msm_sync *sync; diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 3c383c57d988f..2e2c0416b2cb0 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -966,13 +966,6 @@ config MSM_CAMERA_DEBUG help Enable printk() debug for msm camera -config MSM_CAMERA_FLASH - bool "Qualcomm MSM camera flash support" - depends on MSM_CAMERA - ---help--- - Enable support for LED flash for msm camera - - source "drivers/media/video/msm/Kconfig" endif # VIDEO_CAPTURE_DRIVERS diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile index 4429ae5fcafda..37d6037e0470c 100755 --- a/drivers/media/video/msm/Makefile +++ b/drivers/media/video/msm/Makefile @@ -3,5 +3,5 @@ obj-$(CONFIG_MT9D112) += mt9d112.o mt9d112_reg.o obj-$(CONFIG_MT9P012) += mt9p012_fox.o mt9p012_reg.o obj-$(CONFIG_MSM_CAMERA) += msm_camera.o msm_v4l2.o obj-$(CONFIG_S5K3E2FX) += s5k3e2fx.o -obj-$(CONFIG_ARCH_MSM) += msm_vfe7x.o msm_io7x.o -obj-$(CONFIG_ARCH_QSD) += msm_vfe8x.o msm_vfe8x_proc.o msm_io8x.o +obj-$(CONFIG_ARCH_MSM7X00A) += msm_vfe7x.o msm_io7x.o +obj-$(CONFIG_ARCH_QSD8X50) += msm_vfe8x.o msm_vfe8x_proc.o msm_io8x.o diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c index 868ac5bc7dbe5..cdb24763ca52f 100644 --- a/drivers/media/video/msm/msm_camera.c +++ b/drivers/media/video/msm/msm_camera.c @@ -1,14 +1,22 @@ -/* - * Copyright (C) 2008-2009 QUALCOMM Incorporated. +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ -//FIXME: most allocations need not be GFP_ATOMIC /* FIXME: management of mutexes */ -/* FIXME: msm_pmem_region_lookup return values */ -/* FIXME: way too many copy to/from user */ -/* FIXME: does region->active mean free */ -/* FIXME: check limits on command lenghts passed from userspace */ -/* FIXME: __msm_release: which queues should we flush when opencnt != 0 */ #include #include @@ -25,6 +33,8 @@ #include #include +#include + #define MSM_MAX_CAMERA_SENSORS 5 #define ERR_USER_COPY(to) pr_err("%s(%d): copy %s user\n", \ @@ -68,22 +78,70 @@ static LIST_HEAD(msm_sensors); res; \ }) -#define MSM_DRAIN_QUEUE_NOSYNC(sync, name) do { \ - struct msm_queue_cmd *qcmd = NULL; \ - CDBG("%s: draining queue "#name"\n", __func__); \ - while (!list_empty(&(sync)->name)) { \ - qcmd = list_first_entry(&(sync)->name, \ - struct msm_queue_cmd, list); \ - list_del_init(&qcmd->list); \ - kfree(qcmd); \ - }; \ -} while(0) +static inline void free_qcmd(struct msm_queue_cmd *qcmd) +{ + if (!qcmd || !qcmd->on_heap) + return; + if (!--qcmd->on_heap) + kfree(qcmd); +} -#define MSM_DRAIN_QUEUE(sync, name) do { \ +static void msm_queue_init(struct msm_device_queue *queue, const char *name) +{ + spin_lock_init(&queue->lock); + queue->len = 0; + queue->max = 0; + queue->name = name; + INIT_LIST_HEAD(&queue->list); + init_waitqueue_head(&queue->wait); +} + +static void msm_enqueue(struct msm_device_queue *queue, + struct list_head *entry) +{ + unsigned long flags; + spin_lock_irqsave(&queue->lock, flags); + queue->len++; + if (queue->len > queue->max) { + queue->max = queue->len; + pr_info("%s: queue %s new max is %d\n", __func__, + queue->name, queue->max); + } + list_add_tail(entry, &queue->list); + wake_up(&queue->wait); + CDBG("%s: woke up %s\n", __func__, queue->name); + spin_unlock_irqrestore(&queue->lock, flags); +} + +#define msm_dequeue(queue, member) ({ \ unsigned long flags; \ - spin_lock_irqsave(&(sync)->name##_lock, flags); \ - MSM_DRAIN_QUEUE_NOSYNC(sync, name); \ - spin_unlock_irqrestore(&(sync)->name##_lock, flags); \ + struct msm_device_queue *__q = (queue); \ + struct msm_queue_cmd *qcmd = 0; \ + spin_lock_irqsave(&__q->lock, flags); \ + if (!list_empty(&__q->list)) { \ + __q->len--; \ + qcmd = list_first_entry(&__q->list, \ + struct msm_queue_cmd, member); \ + list_del_init(&qcmd->member); \ + } \ + spin_unlock_irqrestore(&__q->lock, flags); \ + qcmd; \ +}) + +#define msm_queue_drain(queue, member) do { \ + unsigned long flags; \ + struct msm_device_queue *__q = (queue); \ + struct msm_queue_cmd *qcmd; \ + spin_lock_irqsave(&__q->lock, flags); \ + CDBG("%s: draining queue %s\n", __func__, __q->name); \ + while (!list_empty(&__q->list)) { \ + qcmd = list_first_entry(&__q->list, \ + struct msm_queue_cmd, member); \ + list_del_init(&qcmd->member); \ + free_qcmd(qcmd); \ + }; \ + __q->len = 0; \ + spin_unlock_irqrestore(&__q->lock, flags); \ } while(0) static int check_overlap(struct hlist_head *ptype, @@ -113,12 +171,18 @@ static int check_overlap(struct hlist_head *ptype, static int check_pmem_info(struct msm_pmem_info *info, int len) { + if (info->offset & (PAGE_SIZE - 1)) { + pr_err("%s: pmem offset is not page-aligned\n", __func__); + goto error; + } + if (info->offset < len && info->offset + info->len <= len && info->y_off < len && info->cbcr_off < len) return 0; +error: pr_err("%s: check failed: off %d len %d y %d cbcr %d (total len %d)\n", __func__, info->offset, @@ -155,12 +219,13 @@ static int msm_pmem_table_add(struct hlist_head *ptype, return rc; paddr += info->offset; + kvstart += info->offset; len = info->len; if (check_overlap(ptype, paddr, len) < 0) return -EINVAL; - CDBG("%s: type = %d, paddr = 0x%lx, vaddr = 0x%lx\n", + CDBG("%s: type %d, paddr 0x%lx, vaddr 0x%lx\n", __func__, info->type, paddr, (unsigned long)info->vaddr); @@ -171,6 +236,7 @@ static int msm_pmem_table_add(struct hlist_head *ptype, INIT_HLIST_NODE(®ion->list); region->paddr = paddr; + region->kvaddr = kvstart; region->len = len; region->file = file; memcpy(®ion->info, info, sizeof(region->info)); @@ -193,12 +259,13 @@ static uint8_t msm_pmem_region_lookup(struct hlist_head *ptype, regptr = reg; hlist_for_each_entry_safe(region, node, n, ptype, list) { - if (region->info.type == pmem_type && region->info.active) { - *regptr = *region; - rc += 1; - if (rc >= maxcount) - break; - regptr++; + if (region->info.type == pmem_type && + region->info.vfe_can_write) { + *regptr = *region; + rc += 1; + if (rc >= maxcount) + break; + regptr++; } } @@ -206,23 +273,20 @@ static uint8_t msm_pmem_region_lookup(struct hlist_head *ptype, } static int msm_pmem_frame_ptov_lookup(struct msm_sync *sync, - unsigned long pyaddr, - unsigned long pcbcraddr, - struct msm_pmem_info *pmem_info) + unsigned long pyaddr, unsigned long pcbcraddr, + struct msm_pmem_region **pmem_region, + int take_from_vfe) { - struct msm_pmem_region *region; struct hlist_node *node, *n; + struct msm_pmem_region *region; - hlist_for_each_entry_safe(region, node, n, &sync->frame, list) { + hlist_for_each_entry_safe(region, node, n, &sync->pmem_frames, list) { if (pyaddr == (region->paddr + region->info.y_off) && pcbcraddr == (region->paddr + region->info.cbcr_off) && - region->info.active) { - /* offset since we could pass vaddr inside - * a registerd pmem buffer - */ - memcpy(pmem_info, ®ion->info, sizeof(*pmem_info)); - region->info.active = 0; + region->info.vfe_can_write) { + *pmem_region = region; + region->info.vfe_can_write = !take_from_vfe; return 0; } } @@ -236,12 +300,12 @@ static unsigned long msm_pmem_stats_ptov_lookup(struct msm_sync *sync, struct msm_pmem_region *region; struct hlist_node *node, *n; - hlist_for_each_entry_safe(region, node, n, &sync->stats, list) { - if (addr == region->paddr && region->info.active) { + hlist_for_each_entry_safe(region, node, n, &sync->pmem_stats, list) { + if (addr == region->paddr && region->info.vfe_can_write) { /* offset since we could pass vaddr inside a * registered pmem buffer */ *fd = region->info.fd; - region->info.active = 0; + region->info.vfe_can_write = 0; return (unsigned long)(region->info.vaddr); } } @@ -257,13 +321,13 @@ static unsigned long msm_pmem_frame_vtop_lookup(struct msm_sync *sync, struct hlist_node *node, *n; hlist_for_each_entry_safe(region, - node, n, &sync->frame, list) { + node, n, &sync->pmem_frames, list) { if (((unsigned long)(region->info.vaddr) == buffer) && (region->info.y_off == yoff) && (region->info.cbcr_off == cbcroff) && (region->info.fd == fd) && - (region->info.active == 0)) { - region->info.active = 1; + (region->info.vfe_can_write == 0)) { + region->info.vfe_can_write = 1; return region->paddr; } } @@ -279,11 +343,11 @@ static unsigned long msm_pmem_stats_vtop_lookup( struct msm_pmem_region *region; struct hlist_node *node, *n; - hlist_for_each_entry_safe(region, node, n, &sync->stats, list) { + hlist_for_each_entry_safe(region, node, n, &sync->pmem_stats, list) { if (((unsigned long)(region->info.vaddr) == buffer) && (region->info.fd == fd) && - region->info.active == 0) { - region->info.active = 1; + region->info.vfe_can_write == 0) { + region->info.vfe_can_write = 1; return region->paddr; } } @@ -305,7 +369,7 @@ static int __msm_pmem_table_del(struct msm_sync *sync, case MSM_PMEM_MAINIMG: case MSM_PMEM_RAW_MAINIMG: hlist_for_each_entry_safe(region, node, n, - &sync->frame, list) { + &sync->pmem_frames, list) { if (pinfo->type == region->info.type && pinfo->vaddr == region->info.vaddr && @@ -320,7 +384,7 @@ static int __msm_pmem_table_del(struct msm_sync *sync, case MSM_PMEM_AEC_AWB: case MSM_PMEM_AF: hlist_for_each_entry_safe(region, node, n, - &sync->stats, list) { + &sync->pmem_stats, list) { if (pinfo->type == region->info.type && pinfo->vaddr == region->info.vaddr && @@ -355,52 +419,49 @@ static int msm_pmem_table_del(struct msm_sync *sync, void __user *arg) static int __msm_get_frame(struct msm_sync *sync, struct msm_frame *frame) { - unsigned long flags; int rc = 0; - struct msm_pmem_info pmem_info; + struct msm_pmem_region *region; struct msm_queue_cmd *qcmd = NULL; + struct msm_vfe_resp *vdata; struct msm_vfe_phy_info *pphy; - spin_lock_irqsave(&sync->prev_frame_q_lock, flags); - if (!list_empty(&sync->prev_frame_q)) { - qcmd = list_first_entry(&sync->prev_frame_q, - struct msm_queue_cmd, list); - list_del_init(&qcmd->list); - } - spin_unlock_irqrestore(&sync->prev_frame_q_lock, flags); + qcmd = msm_dequeue(&sync->frame_q, list_frame); if (!qcmd) { pr_err("%s: no preview frame.\n", __func__); return -EAGAIN; } - pphy = (struct msm_vfe_phy_info *)(qcmd->command); + vdata = (struct msm_vfe_resp *)(qcmd->command); + pphy = &vdata->phy; rc = msm_pmem_frame_ptov_lookup(sync, pphy->y_phy, pphy->cbcr_phy, - &pmem_info); + ®ion, + 1); /* give frame to user space */ if (rc < 0) { pr_err("%s: cannot get frame, invalid lookup address " - "y=%x cbcr=%x\n", - __FUNCTION__, + "y %x cbcr %x\n", + __func__, pphy->y_phy, pphy->cbcr_phy); goto err; } - frame->buffer = (unsigned long)pmem_info.vaddr; - frame->y_off = pmem_info.y_off; - frame->cbcr_off = pmem_info.cbcr_off; - frame->fd = pmem_info.fd; + frame->buffer = (unsigned long)region->info.vaddr; + frame->y_off = region->info.y_off; + frame->cbcr_off = region->info.cbcr_off; + frame->fd = region->info.fd; - CDBG("__msm_get_frame: y=0x%x, cbcr=0x%x, qcmd=0x%x, virt_addr=0x%x\n", + CDBG("%s: y %x, cbcr %x, qcmd %x, virt_addr %x\n", + __func__, pphy->y_phy, pphy->cbcr_phy, (int) qcmd, (int) frame->buffer); err: - kfree(qcmd); + free_qcmd(qcmd); return rc; } @@ -422,8 +483,9 @@ static int msm_get_frame(struct msm_sync *sync, void __user *arg) if (sync->croplen) { if (frame.croplen != sync->croplen) { - pr_err("msm_get_frame: invalid frame croplen %d," + pr_err("%s: invalid frame croplen %d," "expecting %d\n", + __func__, frame.croplen, sync->croplen); return -EINVAL; @@ -443,7 +505,7 @@ static int msm_get_frame(struct msm_sync *sync, void __user *arg) rc = -EFAULT; } - CDBG("Got frame!!!\n"); + CDBG("%s: got frame\n", __func__); return rc; } @@ -463,7 +525,7 @@ static int msm_enable_vfe(struct msm_sync *sync, void __user *arg) if (sync->vfefn.vfe_enable) rc = sync->vfefn.vfe_enable(&cfg); - CDBG("msm_enable_vfe: returned rc = %d\n", rc); + CDBG("%s: rc %d\n", __func__, rc); return rc; } @@ -482,64 +544,82 @@ static int msm_disable_vfe(struct msm_sync *sync, void __user *arg) if (sync->vfefn.vfe_disable) rc = sync->vfefn.vfe_disable(&cfg, NULL); - CDBG("msm_disable_vfe: returned rc = %d\n", rc); + CDBG("%s: rc %d\n", __func__, rc); return rc; } -static struct msm_queue_cmd* __msm_control(struct msm_sync *sync, - struct msm_control_device_queue *queue, +static struct msm_queue_cmd *__msm_control(struct msm_sync *sync, + struct msm_device_queue *queue, struct msm_queue_cmd *qcmd, int timeout) { - unsigned long flags; int rc; - spin_lock_irqsave(&sync->msg_event_q_lock, flags); - list_add_tail(&qcmd->list, &sync->msg_event_q); - /* wake up config thread */ - wake_up(&sync->msg_event_wait); - spin_unlock_irqrestore(&sync->msg_event_q_lock, flags); + msm_enqueue(&sync->event_q, &qcmd->list_config); if (!queue) return NULL; /* wait for config status */ rc = wait_event_interruptible_timeout( - queue->ctrl_status_wait, - !list_empty_careful(&queue->ctrl_status_q), + queue->wait, + !list_empty_careful(&queue->list), timeout); - if (list_empty_careful(&queue->ctrl_status_q)) { + if (list_empty_careful(&queue->list)) { if (!rc) rc = -ETIMEDOUT; if (rc < 0) { - pr_err("msm_control: wait_event error %d\n", rc); -#if 0 - /* This is a bit scary. If we time out too early, we - * will free qcmd at the end of this function, and the - * dsp may do the same when it does respond, so we - * remove the message from the source queue. + pr_err("%s: wait_event error %d\n", __func__, rc); + /* qcmd may be still on the event_q, in which case we + * need to remove it. Alternatively, qcmd may have + * been dequeued and processed already, in which case + * the list removal will be a no-op. */ - pr_err("%s: error waiting for ctrl_status_q: %d\n", - __func__, rc); - spin_lock_irqsave(&sync->msg_event_q_lock, flags); - list_del_init(&qcmd->list); - spin_unlock_irqrestore(&sync->msg_event_q_lock, flags); -#endif + list_del_init(&qcmd->list_config); return ERR_PTR(rc); } } - /* control command status is ready */ - spin_lock_irqsave(&queue->ctrl_status_q_lock, flags); - BUG_ON(list_empty(&queue->ctrl_status_q)); - qcmd = list_first_entry(&queue->ctrl_status_q, - struct msm_queue_cmd, list); - list_del_init(&qcmd->list); - spin_unlock_irqrestore(&queue->ctrl_status_q_lock, flags); + qcmd = msm_dequeue(queue, list_control); + BUG_ON(!qcmd); return qcmd; } +static struct msm_queue_cmd *__msm_control_nb(struct msm_sync *sync, + struct msm_queue_cmd *qcmd_to_copy) +{ + /* Since this is a non-blocking command, we cannot use qcmd_to_copy and + * its data, since they are on the stack. We replicate them on the heap + * and mark them on_heap so that they get freed when the config thread + * dequeues them. + */ + + struct msm_ctrl_cmd *udata; + struct msm_ctrl_cmd *udata_to_copy = qcmd_to_copy->command; + + struct msm_queue_cmd *qcmd = + kmalloc(sizeof(*qcmd_to_copy) + + sizeof(*udata_to_copy) + + udata_to_copy->length, + GFP_KERNEL); + if (!qcmd) { + pr_err("%s: out of memory\n", __func__); + return ERR_PTR(-ENOMEM); + } + + *qcmd = *qcmd_to_copy; + udata = qcmd->command = qcmd + 1; + memcpy(udata, udata_to_copy, sizeof(*udata)); + udata->value = udata + 1; + memcpy(udata->value, udata_to_copy->value, udata_to_copy->length); + + qcmd->on_heap = 1; + + /* qcmd_resp will be set to NULL */ + return __msm_control(sync, NULL, qcmd, 0); +} + static int msm_control(struct msm_control_device *ctrl_pmsm, int block, void __user *arg) @@ -547,8 +627,11 @@ static int msm_control(struct msm_control_device *ctrl_pmsm, int rc = 0; struct msm_sync *sync = ctrl_pmsm->pmsm->sync; - struct msm_ctrl_cmd udata, *ctrlcmd; - struct msm_queue_cmd *qcmd = NULL, *qcmd_temp; + void __user *uptr; + struct msm_ctrl_cmd udata; + struct msm_queue_cmd qcmd; + struct msm_queue_cmd *qcmd_resp = NULL; + uint8_t data[50]; if (copy_from_user(&udata, arg, sizeof(struct msm_ctrl_cmd))) { ERR_COPY_FROM_USER(); @@ -556,50 +639,56 @@ static int msm_control(struct msm_control_device *ctrl_pmsm, goto end; } - qcmd = kmalloc(sizeof(struct msm_queue_cmd) + - sizeof(struct msm_ctrl_cmd) + udata.length, - GFP_KERNEL); - if (!qcmd) { - pr_err("msm_control: cannot allocate buffer\n"); - rc = -ENOMEM; - goto end; - } + uptr = udata.value; + udata.value = data; - qcmd->type = MSM_CAM_Q_CTRL; - qcmd->command = ctrlcmd = (struct msm_ctrl_cmd *)(qcmd + 1); - *ctrlcmd = udata; - ctrlcmd->value = ctrlcmd + 1; + qcmd.on_heap = 0; + qcmd.type = MSM_CAM_Q_CTRL; + qcmd.command = &udata; if (udata.length) { - if (copy_from_user(ctrlcmd->value, - udata.value, udata.length)) { + if (udata.length > sizeof(data)) { + pr_err("%s: user data too large (%d, max is %d)\n", + __func__, + udata.length, + sizeof(data)); + rc = -EIO; + goto end; + } + if (copy_from_user(udata.value, uptr, udata.length)) { ERR_COPY_FROM_USER(); rc = -EFAULT; goto end; } } - if (!block) { - /* qcmd will be set to NULL */ - qcmd = __msm_control(sync, NULL, qcmd, 0); + if (unlikely(!block)) { + qcmd_resp = __msm_control_nb(sync, &qcmd); goto end; } - qcmd_temp = __msm_control(sync, + qcmd_resp = __msm_control(sync, &ctrl_pmsm->ctrl_q, - qcmd, MAX_SCHEDULE_TIMEOUT); - - if (IS_ERR(qcmd_temp)) { - rc = PTR_ERR(qcmd_temp); + &qcmd, MAX_SCHEDULE_TIMEOUT); + + if (!qcmd_resp || IS_ERR(qcmd_resp)) { + /* Do not free qcmd_resp here. If the config thread read it, + * then it has already been freed, and we timed out because + * we did not receive a MSM_CAM_IOCTL_CTRL_CMD_DONE. If the + * config thread itself is blocked and not dequeueing commands, + * then it will either eventually unblock and process them, + * or when it is killed, qcmd will be freed in + * msm_release_config. + */ + rc = PTR_ERR(qcmd_resp); + qcmd_resp = NULL; goto end; } - qcmd = qcmd_temp; - if (qcmd->command) { - void __user *to = udata.value; - udata = *(struct msm_ctrl_cmd *)qcmd->command; + if (qcmd_resp->command) { + udata = *(struct msm_ctrl_cmd *)qcmd_resp->command; if (udata.length > 0) { - if (copy_to_user(to, + if (copy_to_user(uptr, udata.value, udata.length)) { ERR_COPY_TO_USER(); @@ -607,7 +696,7 @@ static int msm_control(struct msm_control_device *ctrl_pmsm, goto end; } } - udata.value = to; + udata.value = uptr; if (copy_to_user((void *)arg, &udata, sizeof(struct msm_ctrl_cmd))) { @@ -618,21 +707,111 @@ static int msm_control(struct msm_control_device *ctrl_pmsm, } end: - /* Note: if we get here as a result of an error, we will free the - * qcmd that we kmalloc() in this function. When we come here as - * a result of a successful completion, we are freeing the qcmd that - * we dequeued from queue->ctrl_status_q. - */ - if (qcmd) - kfree(qcmd); - - CDBG("msm_control: end rc = %d\n", rc); + free_qcmd(qcmd_resp); + CDBG("%s: rc %d\n", __func__, rc); return rc; } +/* Divert frames for post-processing by delivering them to the config thread; + * when post-processing is done, it will return the frame to the frame thread. + */ +static int msm_divert_frame(struct msm_sync *sync, + struct msm_vfe_resp *data, + struct msm_stats_event_ctrl *se) +{ + struct msm_pmem_region *region; + struct msm_postproc buf; + int rc; + + pr_info("%s: preview PP sync->pp_mask %d\n", __func__, sync->pp_mask); + + if (!(sync->pp_mask & PP_PREV)) { + pr_err("%s: diverting preview frame but not in PP_PREV!\n", + __func__); + return -EINVAL; + } + + rc = msm_pmem_frame_ptov_lookup(sync, + data->phy.y_phy, + data->phy.cbcr_phy, + ®ion, + 0); /* vfe can still write to frame */ + if (rc < 0) { + CDBG("%s: msm_pmem_frame_ptov_lookup failed\n", __func__); + return rc; + } + + buf.fmain.buffer = (unsigned long)region->info.vaddr; + buf.fmain.y_off = region->info.y_off; + buf.fmain.cbcr_off = region->info.cbcr_off; + buf.fmain.fd = region->info.fd; + + CDBG("%s: buf %ld fd %d\n", + __func__, buf.fmain.buffer, + buf.fmain.fd); + if (copy_to_user((void *)(se->stats_event.data), + &(buf.fmain), + sizeof(struct msm_frame))) { + ERR_COPY_TO_USER(); + return -EFAULT; + } + + return 0; +} + +static int msm_divert_snapshot(struct msm_sync *sync, + struct msm_vfe_resp *data, + struct msm_stats_event_ctrl *se) +{ + struct msm_postproc buf; + struct msm_pmem_region region; + + CDBG("%s: preview PP sync->pp_mask %d\n", __func__, sync->pp_mask); + + if (!(sync->pp_mask & (PP_SNAP|PP_RAW_SNAP))) { + pr_err("%s: diverting snapshot but not in PP_SNAP!\n", + __func__); + return -EINVAL; + } + + memset(®ion, 0, sizeof(region)); + buf.fmnum = msm_pmem_region_lookup(&sync->pmem_frames, + MSM_PMEM_MAINIMG, + ®ion, 1); + if (buf.fmnum == 1) { + buf.fmain.buffer = (uint32_t)region.info.vaddr; + buf.fmain.y_off = region.info.y_off; + buf.fmain.cbcr_off = region.info.cbcr_off; + buf.fmain.fd = region.info.fd; + } else { + if (buf.fmnum > 1) + pr_err("%s: MSM_PMEM_MAINIMG lookup found %d\n", + __func__, buf.fmnum); + buf.fmnum = msm_pmem_region_lookup(&sync->pmem_frames, + MSM_PMEM_RAW_MAINIMG, + ®ion, 1); + if (buf.fmnum == 1) { + buf.fmain.path = MSM_FRAME_PREV_2; + buf.fmain.buffer = (uint32_t)region.info.vaddr; + buf.fmain.fd = region.info.fd; + } else { + pr_err("%s: pmem lookup fail (found %d)\n", + __func__, buf.fmnum); + return -EIO; + } + } + + CDBG("%s: snapshot copy_to_user!\n", __func__); + if (copy_to_user((void *)(se->stats_event.data), &buf, sizeof(buf))) { + ERR_COPY_TO_USER(); + return -EFAULT; + } + + return 0; +} + static int msm_get_stats(struct msm_sync *sync, void __user *arg) { - unsigned long flags; int timeout; int rc = 0; @@ -651,29 +830,27 @@ static int msm_get_stats(struct msm_sync *sync, void __user *arg) timeout = (int)se.timeout_ms; - CDBG("msm_get_stats timeout %d\n", timeout); + CDBG("%s: timeout %d\n", __func__, timeout); rc = wait_event_interruptible_timeout( - sync->msg_event_wait, - !list_empty_careful(&sync->msg_event_q), + sync->event_q.wait, + !list_empty_careful(&sync->event_q.list), msecs_to_jiffies(timeout)); - if (list_empty_careful(&sync->msg_event_q)) { + if (list_empty_careful(&sync->event_q.list)) { if (rc == 0) rc = -ETIMEDOUT; if (rc < 0) { - pr_err("msm_get_stats error %d\n", rc); + pr_err("%s: error %d\n", __func__, rc); return rc; } } - CDBG("msm_get_stats returned from wait: %d\n", rc); + CDBG("%s: returned from wait: %d\n", __func__, rc); - spin_lock_irqsave(&sync->msg_event_q_lock, flags); - BUG_ON(list_empty(&sync->msg_event_q)); - qcmd = list_first_entry(&sync->msg_event_q, - struct msm_queue_cmd, list); - list_del_init(&qcmd->list); - spin_unlock_irqrestore(&sync->msg_event_q_lock, flags); + rc = 0; - CDBG("=== received from DSP === %d\n", qcmd->type); + qcmd = msm_dequeue(&sync->event_q, list_config); + BUG_ON(!qcmd); + + CDBG("%s: received from DSP %d\n", __func__, qcmd->type); switch (qcmd->type) { case MSM_CAM_Q_VFE_EVT: @@ -688,9 +865,10 @@ static int msm_get_stats(struct msm_sync *sync, void __user *arg) se.stats_event.msg_id = data->evt_msg.msg_id; se.stats_event.len = data->evt_msg.len; - CDBG("msm_get_stats, qcmd->type = %d\n", qcmd->type); - CDBG("length = %d\n", se.stats_event.len); - CDBG("msg_id = %d\n", se.stats_event.msg_id); + CDBG("%s: qcmd->type %d length %d msd_id %d\n", __func__, + qcmd->type, + se.stats_event.len, + se.stats_event.msg_id); if ((data->type == VFE_MSG_STATS_AF) || (data->type == VFE_MSG_STATS_WE)) { @@ -701,7 +879,7 @@ static int msm_get_stats(struct msm_sync *sync, void __user *arg) &(stats.fd)); if (!stats.buffer) { pr_err("%s: msm_pmem_stats_ptov_lookup error\n", - __FUNCTION__); + __func__); rc = -EINVAL; goto failure; } @@ -722,53 +900,15 @@ static int msm_get_stats(struct msm_sync *sync, void __user *arg) rc = -EFAULT; goto failure; } - } else if (data->type == VFE_MSG_OUTPUT1 || - data->type == VFE_MSG_OUTPUT2) { - if (copy_to_user((void *)(se.stats_event.data), - data->extdata, - data->extlen)) { - ERR_COPY_TO_USER(); - rc = -EFAULT; - goto failure; - } - } else if (data->type == VFE_MSG_SNAPSHOT && sync->pict_pp) { - struct msm_postproc buf; - struct msm_pmem_region region; - buf.fmnum = msm_pmem_region_lookup(&sync->frame, - MSM_PMEM_MAINIMG, - ®ion, 1); - if (buf.fmnum == 1) { - buf.fmain.buffer = - (unsigned long)region.info.vaddr; - buf.fmain.y_off = region.info.y_off; - buf.fmain.cbcr_off = region.info.cbcr_off; - buf.fmain.fd = region.info.fd; - } else { - buf.fmnum = msm_pmem_region_lookup(&sync->frame, - MSM_PMEM_RAW_MAINIMG, - ®ion, 1); - if (buf.fmnum == 1) { - buf.fmain.path = MSM_FRAME_PREV_2; - buf.fmain.buffer = - (unsigned long)region. - info.vaddr; - buf.fmain.fd = region.info.fd; - } - else { - pr_err("%s: pmem lookup failed\n", - __func__); - rc = -EINVAL; - goto failure; - } - } - - if (copy_to_user((void *)(se.stats_event.data), &buf, - sizeof(buf))) { - ERR_COPY_TO_USER(); - rc = -EFAULT; - goto failure; - } - CDBG("snapshot copy_to_user!\n"); + } else { + if ((sync->pp_mask & PP_PREV) && + (data->type == VFE_MSG_OUTPUT1 || + data->type == VFE_MSG_OUTPUT2)) + rc = msm_divert_frame(sync, data, &se); + else if ((sync->pp_mask & (PP_SNAP|PP_RAW_SNAP)) && + data->type == VFE_MSG_SNAPSHOT) + rc = msm_divert_snapshot(sync, + data, &se); } break; @@ -776,8 +916,8 @@ static int msm_get_stats(struct msm_sync *sync, void __user *arg) /* control command from control thread */ ctrl = (struct msm_ctrl_cmd *)(qcmd->command); - CDBG("msm_get_stats, qcmd->type = %d\n", qcmd->type); - CDBG("length = %d\n", ctrl->length); + CDBG("%s: qcmd->type %d length %d\n", __func__, + qcmd->type, ctrl->length); if (ctrl->length > 0) { if (copy_to_user((void *)(se.ctrl_cmd.value), @@ -801,8 +941,7 @@ static int msm_get_stats(struct msm_sync *sync, void __user *arg) /* control command from v4l2 client */ ctrl = (struct msm_ctrl_cmd *)(qcmd->command); - CDBG("msm_get_stats, qcmd->type = %d\n", qcmd->type); - CDBG("length = %d\n", ctrl->length); + CDBG("%s: qcmd->type %d len %d\n", __func__, qcmd->type, ctrl->length); if (ctrl->length > 0) { if (copy_to_user((void *)(se.ctrl_cmd.value), @@ -829,66 +968,55 @@ static int msm_get_stats(struct msm_sync *sync, void __user *arg) if (copy_to_user((void *)arg, &se, sizeof(se))) { ERR_COPY_TO_USER(); rc = -EFAULT; + goto failure; } failure: - if (qcmd) - kfree(qcmd); + free_qcmd(qcmd); - CDBG("msm_get_stats: %d\n", rc); + CDBG("%s: %d\n", __func__, rc); return rc; } static int msm_ctrl_cmd_done(struct msm_control_device *ctrl_pmsm, void __user *arg) { - unsigned long flags; - int rc = 0; - - struct msm_ctrl_cmd udata, *ctrlcmd; - struct msm_queue_cmd *qcmd = NULL; + void __user *uptr; + struct msm_queue_cmd *qcmd = &ctrl_pmsm->qcmd; + struct msm_ctrl_cmd *command = &ctrl_pmsm->ctrl; - if (copy_from_user(&udata, arg, sizeof(struct msm_ctrl_cmd))) { + if (copy_from_user(command, arg, sizeof(*command))) { ERR_COPY_FROM_USER(); - rc = -EFAULT; - goto end; + return -EFAULT; } - qcmd = kmalloc(sizeof(struct msm_queue_cmd) + - sizeof(struct msm_ctrl_cmd) + udata.length, - GFP_KERNEL); - if (!qcmd) { - rc = -ENOMEM; - goto end; - } + qcmd->on_heap = 0; + qcmd->command = command; + uptr = command->value; - qcmd->command = ctrlcmd = (struct msm_ctrl_cmd *)(qcmd + 1); - *ctrlcmd = udata; - if (udata.length > 0) { - ctrlcmd->value = ctrlcmd + 1; - if (copy_from_user(ctrlcmd->value, - (void *)udata.value, - udata.length)) { + if (command->length > 0) { + command->value = ctrl_pmsm->ctrl_data; + if (command->length > sizeof(ctrl_pmsm->ctrl_data)) { + pr_err("%s: user data %d is too big (max %d)\n", + __func__, command->length, + sizeof(ctrl_pmsm->ctrl_data)); + return -EINVAL; + } + if (copy_from_user(command->value, + uptr, + command->length)) { ERR_COPY_FROM_USER(); - rc = -EFAULT; - kfree(qcmd); - goto end; + return -EFAULT; } - } - else ctrlcmd->value = NULL; + } else + command->value = NULL; -end: - CDBG("msm_ctrl_cmd_done: end rc = %d\n", rc); - if (rc == 0) { - /* wake up control thread */ - spin_lock_irqsave(&ctrl_pmsm->ctrl_q.ctrl_status_q_lock, flags); - list_add_tail(&qcmd->list, &ctrl_pmsm->ctrl_q.ctrl_status_q); - wake_up(&ctrl_pmsm->ctrl_q.ctrl_status_wait); - spin_unlock_irqrestore(&ctrl_pmsm->ctrl_q.ctrl_status_q_lock, - flags); - } + CDBG("%s: end\n", __func__); - return rc; + /* wake up control thread */ + msm_enqueue(&ctrl_pmsm->ctrl_q, &qcmd->list_control); + + return 0; } static int msm_config_vfe(struct msm_sync *sync, void __user *arg) @@ -896,57 +1024,70 @@ static int msm_config_vfe(struct msm_sync *sync, void __user *arg) struct msm_vfe_cfg_cmd cfgcmd; struct msm_pmem_region region[8]; struct axidata axi_data; - void *data = NULL; - int rc = -EIO; - memset(&axi_data, 0, sizeof(axi_data)); + if (!sync->vfefn.vfe_config) { + pr_err("%s: no vfe_config!\n", __func__); + return -EIO; + } if (copy_from_user(&cfgcmd, arg, sizeof(cfgcmd))) { ERR_COPY_FROM_USER(); return -EFAULT; } - switch(cfgcmd.cmd_type) { + memset(&axi_data, 0, sizeof(axi_data)); + + CDBG("%s: cmd_type %d\n", __func__, cfgcmd.cmd_type); + + switch (cfgcmd.cmd_type) { case CMD_STATS_ENABLE: axi_data.bufnum1 = - msm_pmem_region_lookup(&sync->stats, + msm_pmem_region_lookup(&sync->pmem_stats, MSM_PMEM_AEC_AWB, ®ion[0], NUM_WB_EXP_STAT_OUTPUT_BUFFERS); - if (!axi_data.bufnum1) { - pr_err("%s %d: pmem region lookup error\n", - __FUNCTION__, __LINE__); + axi_data.bufnum2 = + msm_pmem_region_lookup(&sync->pmem_stats, + MSM_PMEM_AF, ®ion[axi_data.bufnum1], + NUM_AF_STAT_OUTPUT_BUFFERS); + if (!axi_data.bufnum1 || !axi_data.bufnum2) { + pr_err("%s: pmem region lookup error\n", __func__); return -EINVAL; } axi_data.region = ®ion[0]; - data = &axi_data; - break; + return sync->vfefn.vfe_config(&cfgcmd, &axi_data); case CMD_STATS_AF_ENABLE: axi_data.bufnum1 = - msm_pmem_region_lookup(&sync->stats, + msm_pmem_region_lookup(&sync->pmem_stats, MSM_PMEM_AF, ®ion[0], NUM_AF_STAT_OUTPUT_BUFFERS); if (!axi_data.bufnum1) { pr_err("%s %d: pmem region lookup error\n", - __FUNCTION__, __LINE__); + __func__, __LINE__); return -EINVAL; } axi_data.region = ®ion[0]; - data = &axi_data; - break; + return sync->vfefn.vfe_config(&cfgcmd, &axi_data); + case CMD_STATS_AEC_AWB_ENABLE: + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->pmem_stats, + MSM_PMEM_AEC_AWB, ®ion[0], + NUM_WB_EXP_STAT_OUTPUT_BUFFERS); + if (!axi_data.bufnum1) { + pr_err("%s %d: pmem region lookup error\n", + __func__, __LINE__); + return -EINVAL; + } + axi_data.region = ®ion[0]; + return sync->vfefn.vfe_config(&cfgcmd, &axi_data); case CMD_GENERAL: case CMD_STATS_DISABLE: - break; + return sync->vfefn.vfe_config(&cfgcmd, NULL); default: pr_err("%s: unknown command type %d\n", - __FUNCTION__, cfgcmd.cmd_type); - return -EINVAL; + __func__, cfgcmd.cmd_type); } - - if (sync->vfefn.vfe_config) - rc = sync->vfefn.vfe_config(&cfgcmd, data); - - return rc; + return -EINVAL; } static int msm_frame_axi_cfg(struct msm_sync *sync, @@ -964,11 +1105,11 @@ static int msm_frame_axi_cfg(struct msm_sync *sync, case CMD_AXI_CFG_OUT1: pmem_type = MSM_PMEM_OUTPUT1; axi_data.bufnum1 = - msm_pmem_region_lookup(&sync->frame, pmem_type, + msm_pmem_region_lookup(&sync->pmem_frames, pmem_type, ®ion[0], 8); if (!axi_data.bufnum1) { pr_err("%s %d: pmem region lookup error\n", - __FUNCTION__, __LINE__); + __func__, __LINE__); return -EINVAL; } break; @@ -976,12 +1117,12 @@ static int msm_frame_axi_cfg(struct msm_sync *sync, case CMD_AXI_CFG_OUT2: pmem_type = MSM_PMEM_OUTPUT2; axi_data.bufnum2 = - msm_pmem_region_lookup(&sync->frame, pmem_type, + msm_pmem_region_lookup(&sync->pmem_frames, pmem_type, ®ion[0], 8); if (!axi_data.bufnum2) { pr_err("%s %d: pmem region lookup error (empty %d)\n", - __FUNCTION__, __LINE__, - hlist_empty(&sync->frame)); + __func__, __LINE__, + hlist_empty(&sync->pmem_frames)); return -EINVAL; } break; @@ -989,21 +1130,21 @@ static int msm_frame_axi_cfg(struct msm_sync *sync, case CMD_AXI_CFG_SNAP_O1_AND_O2: pmem_type = MSM_PMEM_THUMBNAIL; axi_data.bufnum1 = - msm_pmem_region_lookup(&sync->frame, pmem_type, + msm_pmem_region_lookup(&sync->pmem_frames, pmem_type, ®ion[0], 8); if (!axi_data.bufnum1) { pr_err("%s %d: pmem region lookup error\n", - __FUNCTION__, __LINE__); + __func__, __LINE__); return -EINVAL; } pmem_type = MSM_PMEM_MAINIMG; axi_data.bufnum2 = - msm_pmem_region_lookup(&sync->frame, pmem_type, + msm_pmem_region_lookup(&sync->pmem_frames, pmem_type, ®ion[axi_data.bufnum1], 8); if (!axi_data.bufnum2) { pr_err("%s %d: pmem region lookup error\n", - __FUNCTION__, __LINE__); + __func__, __LINE__); return -EINVAL; } break; @@ -1011,11 +1152,11 @@ static int msm_frame_axi_cfg(struct msm_sync *sync, case CMD_RAW_PICT_AXI_CFG: pmem_type = MSM_PMEM_RAW_MAINIMG; axi_data.bufnum2 = - msm_pmem_region_lookup(&sync->frame, pmem_type, + msm_pmem_region_lookup(&sync->pmem_frames, pmem_type, ®ion[0], 8); if (!axi_data.bufnum2) { pr_err("%s %d: pmem region lookup error\n", - __FUNCTION__, __LINE__); + __func__, __LINE__); return -EINVAL; } break; @@ -1026,7 +1167,7 @@ static int msm_frame_axi_cfg(struct msm_sync *sync, default: pr_err("%s: unknown command type %d\n", - __FUNCTION__, cfgcmd->cmd_type); + __func__, cfgcmd->cmd_type); return -EINVAL; } @@ -1053,12 +1194,12 @@ static int msm_get_sensor_info(struct msm_sync *sync, void __user *arg) } sdata = sync->pdev->dev.platform_data; - CDBG("sensor_name %s\n", sdata->sensor_name); + CDBG("%s: sensor_name %s\n", __func__, sdata->sensor_name); memcpy(&info.name[0], sdata->sensor_name, MAX_SENSOR_NAME); - info.flash_enabled = sdata->flash_type != MSM_CAMERA_FLASH_NONE; + info.flash_enabled = !!sdata->camera_flash; /* copy back to user space */ if (copy_to_user((void *)arg, @@ -1084,7 +1225,8 @@ static int __msm_put_frame_buf(struct msm_sync *sync, pb->y_off, pb->cbcr_off, pb->fd); if (pphy != 0) { - CDBG("rel: vaddr = 0x%lx, paddr = 0x%lx\n", + CDBG("%s: rel: vaddr %lx, paddr %lx\n", + __func__, pb->buffer, pphy); cfgcmd.cmd_type = CMD_FRAME_BUF_RELEASE; cfgcmd.value = (void *)pb; @@ -1092,7 +1234,7 @@ static int __msm_put_frame_buf(struct msm_sync *sync, rc = sync->vfefn.vfe_config(&cfgcmd, &pphy); } else { pr_err("%s: msm_pmem_frame_vtop_lookup failed\n", - __FUNCTION__); + __func__); rc = -EINVAL; } @@ -1101,16 +1243,16 @@ static int __msm_put_frame_buf(struct msm_sync *sync, static int msm_put_frame_buffer(struct msm_sync *sync, void __user *arg) { - struct msm_frame buf_t; + struct msm_frame buf; - if (copy_from_user(&buf_t, + if (copy_from_user(&buf, arg, sizeof(struct msm_frame))) { ERR_COPY_FROM_USER(); return -EFAULT; } - return __msm_put_frame_buf(sync, &buf_t); + return __msm_put_frame_buf(sync, &buf); } static int __msm_register_pmem(struct msm_sync *sync, @@ -1124,12 +1266,12 @@ static int __msm_register_pmem(struct msm_sync *sync, case MSM_PMEM_THUMBNAIL: case MSM_PMEM_MAINIMG: case MSM_PMEM_RAW_MAINIMG: - rc = msm_pmem_table_add(&sync->frame, pinfo); + rc = msm_pmem_table_add(&sync->pmem_frames, pinfo); break; case MSM_PMEM_AEC_AWB: case MSM_PMEM_AF: - rc = msm_pmem_table_add(&sync->stats, pinfo); + rc = msm_pmem_table_add(&sync->pmem_stats, pinfo); break; default: @@ -1176,17 +1318,17 @@ static int msm_stats_axi_cfg(struct msm_sync *sync, break; default: pr_err("%s: unknown command type %d\n", - __FUNCTION__, cfgcmd->cmd_type); + __func__, cfgcmd->cmd_type); return -EINVAL; } if (cfgcmd->cmd_type != CMD_GENERAL) { axi_data.bufnum1 = - msm_pmem_region_lookup(&sync->stats, pmem_type, + msm_pmem_region_lookup(&sync->pmem_stats, pmem_type, ®ion[0], NUM_WB_EXP_STAT_OUTPUT_BUFFERS); if (!axi_data.bufnum1) { pr_err("%s %d: pmem region lookup error\n", - __FUNCTION__, __LINE__); + __func__, __LINE__); return -EINVAL; } axi_data.region = ®ion[0]; @@ -1213,7 +1355,7 @@ static int msm_put_stats_buffer(struct msm_sync *sync, void __user *arg) return -EFAULT; } - CDBG("msm_put_stats_buffer\n"); + CDBG("%s\n", __func__); pphy = msm_pmem_stats_vtop_lookup(sync, buf.buffer, buf.fd); if (pphy != 0) { @@ -1223,7 +1365,7 @@ static int msm_put_stats_buffer(struct msm_sync *sync, void __user *arg) cfgcmd.cmd_type = CMD_STATS_AF_BUF_RELEASE; else { pr_err("%s: invalid buf type %d\n", - __FUNCTION__, + __func__, buf.type); rc = -EINVAL; goto put_done; @@ -1234,12 +1376,12 @@ static int msm_put_stats_buffer(struct msm_sync *sync, void __user *arg) if (sync->vfefn.vfe_config) { rc = sync->vfefn.vfe_config(&cfgcmd, &pphy); if (rc < 0) - pr_err("msm_put_stats_buffer: "\ - "vfe_config err %d\n", rc); + pr_err("%s: vfe_config error %d\n", + __func__, rc); } else - pr_err("msm_put_stats_buffer: vfe_config is NULL\n"); + pr_err("%s: vfe_config is NULL\n", __func__); } else { - pr_err("msm_put_stats_buffer: NULL physical address\n"); + pr_err("%s: NULL physical address\n", __func__); rc = -EINVAL; } @@ -1269,7 +1411,7 @@ static int msm_axi_config(struct msm_sync *sync, void __user *arg) default: pr_err("%s: unknown command type %d\n", - __FUNCTION__, + __func__, cfgcmd.cmd_type); return -EINVAL; } @@ -1279,7 +1421,6 @@ static int msm_axi_config(struct msm_sync *sync, void __user *arg) static int __msm_get_pic(struct msm_sync *sync, struct msm_ctrl_cmd *ctrl) { - unsigned long flags; int rc = 0; int tm; @@ -1288,24 +1429,22 @@ static int __msm_get_pic(struct msm_sync *sync, struct msm_ctrl_cmd *ctrl) tm = (int)ctrl->timeout_ms; rc = wait_event_interruptible_timeout( - sync->pict_frame_wait, - !list_empty_careful(&sync->pict_frame_q), + sync->pict_q.wait, + !list_empty_careful(&sync->pict_q.list), msecs_to_jiffies(tm)); - if (list_empty_careful(&sync->pict_frame_q)) { + if (list_empty_careful(&sync->pict_q.list)) { if (rc == 0) return -ETIMEDOUT; if (rc < 0) { - pr_err("msm_camera_get_picture, rc = %d\n", rc); + pr_err("%s: rc %d\n", __func__, rc); return rc; } } - spin_lock_irqsave(&sync->pict_frame_q_lock, flags); - BUG_ON(list_empty(&sync->pict_frame_q)); - qcmd = list_first_entry(&sync->pict_frame_q, - struct msm_queue_cmd, list); - list_del_init(&qcmd->list); - spin_unlock_irqrestore(&sync->pict_frame_q_lock, flags); + rc = 0; + + qcmd = msm_dequeue(&sync->pict_q, list_pict); + BUG_ON(!qcmd); if (qcmd->command != NULL) { struct msm_ctrl_cmd *q = @@ -1317,34 +1456,39 @@ static int __msm_get_pic(struct msm_sync *sync, struct msm_ctrl_cmd *ctrl) ctrl->status = -1; } - kfree(qcmd); + free_qcmd(qcmd); + return rc; } static int msm_get_pic(struct msm_sync *sync, void __user *arg) { - struct msm_ctrl_cmd ctrlcmd_t; + struct msm_ctrl_cmd ctrlcmd; + struct msm_pmem_region pic_pmem_region; int rc; + unsigned long end; + int cline_mask; - if (copy_from_user(&ctrlcmd_t, + if (copy_from_user(&ctrlcmd, arg, sizeof(struct msm_ctrl_cmd))) { ERR_COPY_FROM_USER(); return -EFAULT; } - rc = __msm_get_pic(sync, &ctrlcmd_t); + rc = __msm_get_pic(sync, &ctrlcmd); if (rc < 0) return rc; if (sync->croplen) { - if (ctrlcmd_t.length != sync->croplen) { - pr_err("msm_get_pic: invalid len %d < %d\n", - ctrlcmd_t.length, + if (ctrlcmd.length != sync->croplen) { + pr_err("%s: invalid len %d < %d\n", + __func__, + ctrlcmd.length, sync->croplen); return -EINVAL; } - if (copy_to_user(ctrlcmd_t.value, + if (copy_to_user(ctrlcmd.value, sync->cropinfo, sync->croplen)) { ERR_COPY_TO_USER(); @@ -1352,8 +1496,27 @@ static int msm_get_pic(struct msm_sync *sync, void __user *arg) } } + if (msm_pmem_region_lookup(&sync->pmem_frames, + MSM_PMEM_MAINIMG, + &pic_pmem_region, 1) == 0) { + pr_err("%s pmem region lookup error\n", __func__); + return -EIO; + } + + cline_mask = cache_line_size() - 1; + end = pic_pmem_region.kvaddr + pic_pmem_region.len; + end = (end + cline_mask) & ~cline_mask; + + pr_info("%s: flushing cache for [%08lx, %08lx)\n", + __func__, + pic_pmem_region.kvaddr, end); + + dmac_inv_range((const void *)pic_pmem_region.kvaddr, + (const void *)end); + + CDBG("%s: copy snapshot frame to user\n", __func__); if (copy_to_user((void *)arg, - &ctrlcmd_t, + &ctrlcmd, sizeof(struct msm_ctrl_cmd))) { ERR_COPY_TO_USER(); return -EFAULT; @@ -1392,51 +1555,82 @@ static int msm_set_crop(struct msm_sync *sync, void __user *arg) return 0; } -static int msm_pict_pp_done(struct msm_sync *sync, void __user *arg) +static int msm_pp_grab(struct msm_sync *sync, void __user *arg) { - struct msm_ctrl_cmd udata; - struct msm_ctrl_cmd *ctrlcmd = NULL; - struct msm_queue_cmd *qcmd = NULL; - unsigned long flags; - int rc = 0; + uint32_t enable; + if (copy_from_user(&enable, arg, sizeof(enable))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } else { + enable &= PP_MASK; + if (enable & (enable - 1)) { + pr_err("%s: error: more than one PP request!\n", + __func__); + return -EINVAL; + } + if (sync->pp_mask) { + pr_err("%s: postproc %x is already enabled\n", + __func__, sync->pp_mask & enable); + return -EINVAL; + } - if (!sync->pict_pp) - return -EINVAL; + CDBG("%s: sync->pp_mask %d enable %d\n", __func__, + sync->pp_mask, enable); + sync->pp_mask |= enable; + } - if (copy_from_user(&udata, arg, sizeof(struct msm_ctrl_cmd))) { + return 0; +} + +static int msm_pp_release(struct msm_sync *sync, void __user *arg) +{ + uint32_t mask; + if (copy_from_user(&mask, arg, sizeof(uint32_t))) { ERR_COPY_FROM_USER(); - rc = -EFAULT; - goto pp_fail; + return -EFAULT; } - qcmd = kmalloc(sizeof(struct msm_queue_cmd) + - sizeof(struct msm_ctrl_cmd), - GFP_KERNEL); - if (!qcmd) { - rc = -ENOMEM; - goto pp_fail; + mask &= PP_MASK; + if (!(sync->pp_mask & mask)) { + pr_warning("%s: pp not in progress for %x\n", __func__, + mask); + return -EINVAL; } - qcmd->type = MSM_CAM_Q_VFE_MSG; - qcmd->command = ctrlcmd = (struct msm_ctrl_cmd *)(qcmd + 1); - memset(ctrlcmd, 0, sizeof(struct msm_ctrl_cmd)); - ctrlcmd->type = udata.type; - ctrlcmd->status = udata.status; + if ((mask & PP_PREV) && (sync->pp_mask & PP_PREV)) { + if (!sync->pp_prev) { + pr_err("%s: no preview frame to deliver!\n", __func__); + return -EINVAL; + } + pr_info("%s: delivering pp_prev\n", __func__); - spin_lock_irqsave(&sync->pict_frame_q_lock, flags); - list_add_tail(&qcmd->list, &sync->pict_frame_q); - spin_unlock_irqrestore(&sync->pict_frame_q_lock, flags); - wake_up(&sync->pict_frame_wait); + msm_enqueue(&sync->frame_q, &sync->pp_prev->list_frame); + sync->pp_prev = NULL; + goto done; + } -pp_fail: - return rc; + if (((mask & PP_SNAP) && (sync->pp_mask & PP_SNAP)) || + ((mask & PP_RAW_SNAP) && + (sync->pp_mask & PP_RAW_SNAP))) { + if (!sync->pp_snap) { + pr_err("%s: no snapshot to deliver!\n", __func__); + return -EINVAL; + } + pr_info("%s: delivering pp_snap\n", __func__); + msm_enqueue(&sync->pict_q, &sync->pp_snap->list_pict); + sync->pp_snap = NULL; + } + +done: + sync->pp_mask = 0; + return 0; } static long msm_ioctl_common(struct msm_device *pmsm, unsigned int cmd, void __user *argp) { - CDBG("msm_ioctl_common\n"); + CDBG("%s\n", __func__); switch (cmd) { case MSM_CAM_IOCTL_REGISTER_PMEM: return msm_register_pmem(pmsm->sync, argp); @@ -1447,6 +1641,38 @@ static long msm_ioctl_common(struct msm_device *pmsm, } } +int msm_camera_flash(struct msm_sync *sync, int level) +{ + int flash_level; + + if (!sync->sdata->camera_flash) { + pr_err("%s: camera flash is not supported.\n", __func__); + return -EINVAL; + } + + if (!sync->sdata->num_flash_levels) { + pr_err("%s: no flash levels.\n", __func__); + return -EINVAL; + } + + switch (level) { + case MSM_CAMERA_LED_HIGH: + flash_level = sync->sdata->num_flash_levels - 1; + break; + case MSM_CAMERA_LED_LOW: + flash_level = sync->sdata->num_flash_levels / 2; + break; + case MSM_CAMERA_LED_OFF: + flash_level = 0; + break; + default: + pr_err("%s: invalid flash level %d.\n", __func__, level); + return -EINVAL; + } + + return sync->sdata->camera_flash(level); +} + static long msm_ioctl_config(struct file *filep, unsigned int cmd, unsigned long arg) { @@ -1454,7 +1680,7 @@ static long msm_ioctl_config(struct file *filep, unsigned int cmd, void __user *argp = (void __user *)arg; struct msm_device *pmsm = filep->private_data; - CDBG("msm_ioctl_config cmd = %d\n", _IOC_NR(cmd)); + CDBG("%s: cmd %d\n", __func__, _IOC_NR(cmd)); switch (cmd) { case MSM_CAM_IOCTL_GET_SENSOR_INFO: @@ -1501,20 +1727,18 @@ static long msm_ioctl_config(struct file *filep, unsigned int cmd, rc = msm_set_crop(pmsm->sync, argp); break; - case MSM_CAM_IOCTL_PICT_PP: { - uint8_t enable; - if (copy_from_user(&enable, argp, sizeof(enable))) { - ERR_COPY_FROM_USER(); - rc = -EFAULT; - } else { - pmsm->sync->pict_pp = enable; - rc = 0; - } + case MSM_CAM_IOCTL_PP: + /* Grab one preview frame or one snapshot + * frame. + */ + rc = msm_pp_grab(pmsm->sync, argp); break; - } - case MSM_CAM_IOCTL_PICT_PP_DONE: - rc = msm_pict_pp_done(pmsm->sync, argp); + case MSM_CAM_IOCTL_PP_DONE: + /* Release the preview of snapshot frame + * that was grabbed. + */ + rc = msm_pp_release(pmsm->sync, argp); break; case MSM_CAM_IOCTL_SENSOR_IO_CFG: @@ -1527,7 +1751,21 @@ static long msm_ioctl_config(struct file *filep, unsigned int cmd, ERR_COPY_FROM_USER(); rc = -EFAULT; } else - rc = msm_camera_flash_set_led_state(led_state); + rc = msm_camera_flash(pmsm->sync, led_state); + break; + } + + case MSM_CAM_IOCTL_ENABLE_OUTPUT_IND: { + uint32_t enable; + if (copy_from_user(&enable, argp, sizeof(enable))) { + ERR_COPY_FROM_USER(); + rc = -EFAULT; + break; + } + pr_info("%s: copying all preview frames to config: %d\n", + __func__, enable); + pmsm->sync->report_preview_to_config = enable; + rc = 0; break; } @@ -1536,7 +1774,7 @@ static long msm_ioctl_config(struct file *filep, unsigned int cmd, break; } - CDBG("msm_ioctl_config cmd = %d DONE\n", _IOC_NR(cmd)); + CDBG("%s: cmd %d DONE\n", __func__, _IOC_NR(cmd)); return rc; } @@ -1597,6 +1835,9 @@ static long msm_ioctl_control(struct file *filep, unsigned int cmd, case MSM_CAM_IOCTL_GET_PICTURE: rc = msm_get_pic(pmsm->sync, argp); break; + case MSM_CAM_IOCTL_GET_SENSOR_INFO: + rc = msm_get_sensor_info(pmsm->sync, argp); + break; default: rc = msm_ioctl_common(pmsm, cmd, argp); break; @@ -1616,39 +1857,38 @@ static int __msm_release(struct msm_sync *sync) sync->opencnt--; if (!sync->opencnt) { + /*sensor release*/ + sync->sctrl.s_release(); /* need to clean up system resource */ if (sync->vfefn.vfe_release) sync->vfefn.vfe_release(sync->pdev); - if (sync->cropinfo) { - kfree(sync->cropinfo); - sync->cropinfo = NULL; - sync->croplen = 0; - } + kfree(sync->cropinfo); + sync->cropinfo = NULL; + sync->croplen = 0; hlist_for_each_entry_safe(region, hnode, n, - &sync->frame, list) { + &sync->pmem_frames, list) { hlist_del(hnode); put_pmem_file(region->file); kfree(region); } hlist_for_each_entry_safe(region, hnode, n, - &sync->stats, list) { + &sync->pmem_stats, list) { hlist_del(hnode); put_pmem_file(region->file); kfree(region); } - MSM_DRAIN_QUEUE(sync, msg_event_q); - MSM_DRAIN_QUEUE(sync, prev_frame_q); - MSM_DRAIN_QUEUE(sync, pict_frame_q); + msm_queue_drain(&sync->event_q, list_config); + msm_queue_drain(&sync->frame_q, list_frame); + msm_queue_drain(&sync->pict_q, list_pict); - sync->sctrl.s_release(); wake_unlock(&sync->wake_lock); sync->apps_id = NULL; - CDBG("msm_release completed!\n"); + CDBG("%s: completed\n", __func__); } mutex_unlock(&sync->lock); @@ -1659,9 +1899,10 @@ static int msm_release_config(struct inode *node, struct file *filep) { int rc; struct msm_device *pmsm = filep->private_data; - printk("msm_camera: RELEASE %s\n", filep->f_path.dentry->d_name.name); + CDBG("%s: %s\n", __func__, filep->f_path.dentry->d_name.name); rc = __msm_release(pmsm->sync); - atomic_set(&pmsm->opened, 0); + if (!rc) + atomic_set(&pmsm->opened, 0); return rc; } @@ -1670,13 +1911,12 @@ static int msm_release_control(struct inode *node, struct file *filep) int rc; struct msm_control_device *ctrl_pmsm = filep->private_data; struct msm_device *pmsm = ctrl_pmsm->pmsm; - printk("msm_camera: RELEASE %s\n", filep->f_path.dentry->d_name.name); + CDBG("%s: %s\n", __func__, filep->f_path.dentry->d_name.name); rc = __msm_release(pmsm->sync); if (!rc) { - MSM_DRAIN_QUEUE(&ctrl_pmsm->ctrl_q, ctrl_status_q); - MSM_DRAIN_QUEUE(pmsm->sync, pict_frame_q); + msm_queue_drain(&ctrl_pmsm->ctrl_q, list_control); + kfree(ctrl_pmsm); } - kfree(ctrl_pmsm); return rc; } @@ -1684,23 +1924,21 @@ static int msm_release_frame(struct inode *node, struct file *filep) { int rc; struct msm_device *pmsm = filep->private_data; - printk("msm_camera: RELEASE %s\n", filep->f_path.dentry->d_name.name); + CDBG("%s: %s\n", __func__, filep->f_path.dentry->d_name.name); rc = __msm_release(pmsm->sync); - if (!rc) { - MSM_DRAIN_QUEUE(pmsm->sync, prev_frame_q); + if (!rc) atomic_set(&pmsm->opened, 0); - } return rc; } static int msm_unblock_poll_frame(struct msm_sync *sync) { unsigned long flags; - CDBG("msm_unblock_poll_frame\n"); - spin_lock_irqsave(&sync->prev_frame_q_lock, flags); + CDBG("%s\n", __func__); + spin_lock_irqsave(&sync->frame_q.lock, flags); sync->unblock_poll_frame = 1; - wake_up(&sync->prev_frame_wait); - spin_unlock_irqrestore(&sync->prev_frame_q_lock, flags); + wake_up(&sync->frame_q.wait); + spin_unlock_irqrestore(&sync->frame_q.lock, flags); return 0; } @@ -1711,10 +1949,10 @@ static unsigned int __msm_poll_frame(struct msm_sync *sync, int rc = 0; unsigned long flags; - poll_wait(filep, &sync->prev_frame_wait, pll_table); + poll_wait(filep, &sync->frame_q.wait, pll_table); - spin_lock_irqsave(&sync->prev_frame_q_lock, flags); - if (!list_empty_careful(&sync->prev_frame_q)) + spin_lock_irqsave(&sync->frame_q.lock, flags); + if (!list_empty_careful(&sync->frame_q.list)) /* frame ready */ rc = POLLIN | POLLRDNORM; if (sync->unblock_poll_frame) { @@ -1722,7 +1960,7 @@ static unsigned int __msm_poll_frame(struct msm_sync *sync, rc |= POLLPRI; sync->unblock_poll_frame = 0; } - spin_unlock_irqrestore(&sync->prev_frame_q_lock, flags); + spin_unlock_irqrestore(&sync->frame_q.lock, flags); return rc; } @@ -1739,102 +1977,114 @@ static unsigned int msm_poll_frame(struct file *filep, */ static void *msm_vfe_sync_alloc(int size, - void *syncdata __attribute__((unused))) + void *syncdata __attribute__((unused)), + gfp_t gfp) { struct msm_queue_cmd *qcmd = - kmalloc(sizeof(struct msm_queue_cmd) + size, GFP_ATOMIC); - return qcmd ? qcmd + 1 : NULL; + kmalloc(sizeof(struct msm_queue_cmd) + size, gfp); + if (qcmd) { + qcmd->on_heap = 1; + return qcmd + 1; + } + return NULL; +} + +static void msm_vfe_sync_free(void *ptr) +{ + if (ptr) { + struct msm_queue_cmd *qcmd = + (struct msm_queue_cmd *)ptr; + qcmd--; + if (qcmd->on_heap) + kfree(qcmd); + } } /* - * This function executes in interrupt context. + * This function may execute in interrupt context. */ static void msm_vfe_sync(struct msm_vfe_resp *vdata, - enum msm_queue qtype, void *syncdata) + enum msm_queue qtype, void *syncdata, + gfp_t gfp) { struct msm_queue_cmd *qcmd = NULL; - struct msm_queue_cmd *qcmd_frame = NULL; - struct msm_vfe_phy_info *fphy; - - unsigned long flags; struct msm_sync *sync = (struct msm_sync *)syncdata; + if (!sync) { - pr_err("msm_camera: no context in dsp callback.\n"); + pr_err("%s: no context in dsp callback.\n", __func__); + return; + } + + if (!sync->opencnt) { + pr_err("%s: SPURIOUS INTERRUPT\n", __func__); return; } qcmd = ((struct msm_queue_cmd *)vdata) - 1; qcmd->type = qtype; - - if (qtype == MSM_CAM_Q_VFE_MSG) { - switch(vdata->type) { - case VFE_MSG_OUTPUT1: - case VFE_MSG_OUTPUT2: - qcmd_frame = - kmalloc(sizeof(struct msm_queue_cmd) + - sizeof(struct msm_vfe_phy_info), - GFP_ATOMIC); - if (!qcmd_frame) - goto mem_fail; - fphy = (struct msm_vfe_phy_info *)(qcmd_frame + 1); - *fphy = vdata->phy; - - qcmd_frame->type = MSM_CAM_Q_VFE_MSG; - qcmd_frame->command = fphy; - - CDBG("qcmd_frame= 0x%x phy_y= 0x%x, phy_cbcr= 0x%x\n", - (int) qcmd_frame, fphy->y_phy, fphy->cbcr_phy); - - spin_lock_irqsave(&sync->prev_frame_q_lock, flags); - list_add_tail(&qcmd_frame->list, &sync->prev_frame_q); - wake_up(&sync->prev_frame_wait); - spin_unlock_irqrestore(&sync->prev_frame_q_lock, flags); - CDBG("woke up frame thread\n"); + qcmd->command = vdata; + + if (qtype != MSM_CAM_Q_VFE_MSG) + goto for_config; + + CDBG("%s: vdata->type %d\n", __func__, vdata->type); + switch (vdata->type) { + case VFE_MSG_OUTPUT1: + case VFE_MSG_OUTPUT2: + if (sync->pp_mask & PP_PREV) { + CDBG("%s: PP_PREV in progress: phy_y %x phy_cbcr %x\n", + __func__, + vdata->phy.y_phy, + vdata->phy.cbcr_phy); + if (sync->pp_prev) + pr_warning("%s: overwriting pp_prev!\n", + __func__); + pr_info("%s: sending preview to config\n", __func__); + sync->pp_prev = qcmd; break; - case VFE_MSG_SNAPSHOT: - if (sync->pict_pp) - break; - - CDBG("snapshot pp = %d\n", sync->pict_pp); - qcmd_frame = - kmalloc(sizeof(struct msm_queue_cmd), - GFP_ATOMIC); - if (!qcmd_frame) - goto mem_fail; - qcmd_frame->type = MSM_CAM_Q_VFE_MSG; - qcmd_frame->command = NULL; - spin_lock_irqsave(&sync->pict_frame_q_lock, - flags); - list_add_tail(&qcmd_frame->list, &sync->pict_frame_q); - wake_up(&sync->pict_frame_wait); - spin_unlock_irqrestore(&sync->pict_frame_q_lock, flags); - CDBG("woke up picture thread\n"); + } + + if (sync->report_preview_to_config) { + if (qcmd->on_heap) + qcmd->on_heap++; + msm_enqueue(&sync->frame_q, &qcmd->list_frame); break; - default: - CDBG("%s: qtype = %d not handled\n", - __func__, vdata->type); + } + msm_enqueue(&sync->frame_q, &qcmd->list_frame); + return; + + case VFE_MSG_SNAPSHOT: + if (sync->pp_mask & (PP_SNAP | PP_RAW_SNAP)) { + CDBG("%s: PP_SNAP in progress: pp_mask %x\n", + __func__, sync->pp_mask); + if (sync->pp_snap) + pr_warning("%s: overwriting pp_snap!\n", + __func__); + pr_info("%s: sending snapshot to config\n", __func__); + sync->pp_snap = qcmd; break; } - } - qcmd->command = (void *)vdata; - CDBG("vdata->type = %d\n", vdata->type); + if (qcmd->on_heap) + qcmd->on_heap++; + msm_enqueue(&sync->pict_q, &qcmd->list_pict); + break; - spin_lock_irqsave(&sync->msg_event_q_lock, flags); - list_add_tail(&qcmd->list, &sync->msg_event_q); - wake_up(&sync->msg_event_wait); - spin_unlock_irqrestore(&sync->msg_event_q_lock, flags); - CDBG("woke up config thread\n"); - return; + default: + CDBG("%s: qtype %d not handled\n", __func__, vdata->type); + /* fall through, send to config. */ + } -mem_fail: - kfree(qcmd); +for_config: + msm_enqueue(&sync->event_q, &qcmd->list_config); } static struct msm_vfe_callback msm_vfe_s = { .vfe_resp = msm_vfe_sync, .vfe_alloc = msm_vfe_sync_alloc, + .vfe_free = msm_vfe_sync_free, + .flash_ctrl = msm_camera_flash, }; static int __msm_open(struct msm_sync *sync, const char *const apps_id) @@ -1843,7 +2093,8 @@ static int __msm_open(struct msm_sync *sync, const char *const apps_id) mutex_lock(&sync->lock); if (sync->apps_id && strcmp(sync->apps_id, apps_id)) { - pr_err("msm_camera(%s): sensor %s is already opened for %s\n", + pr_err("%s(%s): sensor %s is already opened for %s\n", + __func__, apps_id, sync->sdata->sensor_name, sync->apps_id); @@ -1861,23 +2112,25 @@ static int __msm_open(struct msm_sync *sync, const char *const apps_id) rc = sync->vfefn.vfe_init(&msm_vfe_s, sync->pdev); if (rc < 0) { - pr_err("vfe_init failed at %d\n", rc); + pr_err("%s: vfe_init failed at %d\n", + __func__, rc); goto msm_open_done; } rc = sync->sctrl.s_init(sync->sdata); if (rc < 0) { - pr_err("sensor init failed: %d\n", rc); + pr_err("%s: sensor init failed: %d\n", + __func__, rc); goto msm_open_done; } } else { - pr_err("no sensor init func\n"); + pr_err("%s: no sensor init func\n", __func__); rc = -ENODEV; goto msm_open_done; } if (rc >= 0) { - INIT_HLIST_HEAD(&sync->frame); - INIT_HLIST_HEAD(&sync->stats); + INIT_HLIST_HEAD(&sync->pmem_frames); + INIT_HLIST_HEAD(&sync->pmem_stats); sync->unblock_poll_frame = 0; } } @@ -1895,17 +2148,18 @@ static int msm_open_common(struct inode *inode, struct file *filep, struct msm_device *pmsm = container_of(inode->i_cdev, struct msm_device, cdev); - CDBG("msm_camera: open %s\n", filep->f_path.dentry->d_name.name); + CDBG("%s: open %s\n", __func__, filep->f_path.dentry->d_name.name); if (atomic_cmpxchg(&pmsm->opened, 0, 1) && once) { - pr_err("msm_camera: %s is already opened.\n", + pr_err("%s: %s is already opened.\n", + __func__, filep->f_path.dentry->d_name.name); return -EBUSY; } rc = nonseekable_open(inode, filep); if (rc < 0) { - pr_err("msm_open: nonseekable_open error %d\n", rc); + pr_err("%s: nonseekable_open error %d\n", __func__, rc); return rc; } @@ -1915,7 +2169,7 @@ static int msm_open_common(struct inode *inode, struct file *filep, filep->private_data = pmsm; - CDBG("msm_open() open: rc = %d\n", rc); + CDBG("%s: rc %d\n", __func__, rc); return rc; } @@ -1934,16 +2188,17 @@ static int msm_open_control(struct inode *inode, struct file *filep) return -ENOMEM; rc = msm_open_common(inode, filep, 0); - if (rc < 0) + if (rc < 0) { + kfree(ctrl_pmsm); return rc; + } ctrl_pmsm->pmsm = filep->private_data; filep->private_data = ctrl_pmsm; - spin_lock_init(&ctrl_pmsm->ctrl_q.ctrl_status_q_lock); - INIT_LIST_HEAD(&ctrl_pmsm->ctrl_q.ctrl_status_q); - init_waitqueue_head(&ctrl_pmsm->ctrl_q.ctrl_status_wait); - CDBG("msm_open() open: rc = %d\n", rc); + msm_queue_init(&ctrl_pmsm->ctrl_q, "control"); + + CDBG("%s: rc %d\n", __func__, rc); return rc; } @@ -1954,17 +2209,18 @@ static int __msm_v4l2_control(struct msm_sync *sync, struct msm_queue_cmd *qcmd = NULL, *rcmd = NULL; struct msm_ctrl_cmd *ctrl; - struct msm_control_device_queue FIXME; + struct msm_device_queue FIXME; /* wake up config thread, 4 is for V4L2 application */ qcmd = kmalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL); if (!qcmd) { - pr_err("msm_control: cannot allocate buffer\n"); + pr_err("%s: cannot allocate buffer\n", __func__); rc = -ENOMEM; goto end; } qcmd->type = MSM_CAM_Q_V4L2_REQ; qcmd->command = out; + qcmd->on_heap = 1; rcmd = __msm_control(sync, &FIXME, qcmd, out->timeout_ms); if (IS_ERR(rcmd)) { @@ -1978,8 +2234,8 @@ static int __msm_v4l2_control(struct msm_sync *sync, memcpy(out->value, ctrl->value, ctrl->length); end: - if (rcmd) kfree(rcmd); - CDBG("__msm_v4l2_control: end rc = %d\n", rc); + free_qcmd(rcmd); + CDBG("%s: rc %d\n", __func__, rc); return rc; } @@ -2020,7 +2276,7 @@ static int msm_setup_cdev(struct msm_device *msm, if (IS_ERR(device)) { rc = PTR_ERR(device); - pr_err("msm_camera: error creating device: %d\n", rc); + pr_err("%s: error creating device: %d\n", __func__, rc); return rc; } @@ -2029,7 +2285,7 @@ static int msm_setup_cdev(struct msm_device *msm, rc = cdev_add(&msm->cdev, devno, 1); if (rc < 0) { - pr_err("msm_camera: error adding cdev: %d\n", rc); + pr_err("%s: error adding cdev: %d\n", __func__, rc); device_destroy(msm_class, devno); return rc; } @@ -2080,19 +2336,11 @@ static int msm_sync_init(struct msm_sync *sync, struct msm_sensor_ctrl sctrl; sync->sdata = pdev->dev.platform_data; - spin_lock_init(&sync->msg_event_q_lock); - INIT_LIST_HEAD(&sync->msg_event_q); - init_waitqueue_head(&sync->msg_event_wait); - - spin_lock_init(&sync->prev_frame_q_lock); - INIT_LIST_HEAD(&sync->prev_frame_q); - init_waitqueue_head(&sync->prev_frame_wait); + msm_queue_init(&sync->event_q, "event"); + msm_queue_init(&sync->frame_q, "frame"); + msm_queue_init(&sync->pict_q, "pict"); - spin_lock_init(&sync->pict_frame_q_lock); - INIT_LIST_HEAD(&sync->pict_frame_q); - init_waitqueue_head(&sync->pict_frame_wait); - - wake_lock_init(&sync->wake_lock, WAKE_LOCK_IDLE, "msm_camera"); + wake_lock_init(&sync->wake_lock, WAKE_LOCK_SUSPEND, "msm_camera"); rc = msm_camio_probe_on(pdev); if (rc < 0) @@ -2104,7 +2352,8 @@ static int msm_sync_init(struct msm_sync *sync, } msm_camio_probe_off(pdev); if (rc < 0) { - pr_err("msm_camera: failed to initialize %s\n", + pr_err("%s: failed to initialize %s\n", + __func__, sync->sdata->sensor_name); wake_lock_destroy(&sync->wake_lock); return rc; @@ -2112,7 +2361,7 @@ static int msm_sync_init(struct msm_sync *sync, sync->opencnt = 0; mutex_init(&sync->lock); - CDBG("initialized %s\n", sync->sdata->sensor_name); + CDBG("%s: initialized %s\n", __func__, sync->sdata->sensor_name); return rc; } @@ -2131,7 +2380,7 @@ static int msm_device_init(struct msm_device *pmsm, MKDEV(MAJOR(msm_devno), dev_num), "control", &msm_fops_control); if (rc < 0) { - pr_err("error creating control node: %d\n", rc); + pr_err("%s: error creating control node: %d\n", __func__, rc); return rc; } @@ -2139,7 +2388,7 @@ static int msm_device_init(struct msm_device *pmsm, MKDEV(MAJOR(msm_devno), dev_num + 1), "config", &msm_fops_config); if (rc < 0) { - pr_err("error creating config node: %d\n", rc); + pr_err("%s: error creating config node: %d\n", __func__, rc); msm_tear_down_cdev(pmsm, MKDEV(MAJOR(msm_devno), dev_num)); return rc; @@ -2149,7 +2398,7 @@ static int msm_device_init(struct msm_device *pmsm, MKDEV(MAJOR(msm_devno), dev_num + 2), "frame", &msm_fops_frame); if (rc < 0) { - pr_err("error creating frame node: %d\n", rc); + pr_err("%s: error creating frame node: %d\n", __func__, rc); msm_tear_down_cdev(pmsm, MKDEV(MAJOR(msm_devno), dev_num)); msm_tear_down_cdev(pmsm + 1, @@ -2178,7 +2427,7 @@ int msm_camera_drv_start(struct platform_device *dev, static int camera_node; if (camera_node >= MSM_MAX_CAMERA_SENSORS) { - pr_err("msm_camera: too many camera sensors\n"); + pr_err("%s: too many camera sensors\n", __func__); return rc; } @@ -2188,7 +2437,7 @@ int msm_camera_drv_start(struct platform_device *dev, 3 * MSM_MAX_CAMERA_SENSORS, "msm_camera"); if (rc < 0) { - pr_err("msm_camera: failed to allocate chrdev: %d\n", + pr_err("%s: failed to allocate chrdev: %d\n", __func__, rc); return rc; } @@ -2196,8 +2445,8 @@ int msm_camera_drv_start(struct platform_device *dev, msm_class = class_create(THIS_MODULE, "msm_camera"); if (IS_ERR(msm_class)) { rc = PTR_ERR(msm_class); - pr_err("msm_camera: create device class failed: %d\n", - rc); + pr_err("%s: create device class failed: %d\n", + __func__, rc); return rc; } } @@ -2214,7 +2463,7 @@ int msm_camera_drv_start(struct platform_device *dev, return rc; } - CDBG("setting camera node %d\n", camera_node); + CDBG("%s: setting camera node %d\n", __func__, camera_node); rc = msm_device_init(pmsm, sync, camera_node); if (rc < 0) { msm_sync_destroy(sync); diff --git a/drivers/media/video/msm/msm_io7x.c b/drivers/media/video/msm/msm_io7x.c index 55c020bb7afa3..63c9b64fa865c 100644 --- a/drivers/media/video/msm/msm_io7x.c +++ b/drivers/media/video/msm/msm_io7x.c @@ -1,5 +1,19 @@ -/* - * Copyright (c) 2008-2009 QUALCOMM Incorporated +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ #include @@ -41,7 +55,7 @@ static struct msm_camera_io_ext camio_ext; static struct resource *appio, *mdcio; void __iomem *appbase, *mdcbase; -extern int clk_set_flags(struct clk *clk, unsigned long flags); +int clk_set_flags(struct clk *clk, unsigned long flags); int msm_camio_clk_enable(enum msm_camio_clk_type clktype) { @@ -121,28 +135,26 @@ int msm_camio_enable(struct platform_device *pdev) camio_ext = camdev->ioext; appio = request_mem_region(camio_ext.appphy, - camio_ext.appsz, pdev->name); + camio_ext.appsz, pdev->name); if (!appio) { rc = -EBUSY; goto enable_fail; } - appbase = ioremap(camio_ext.appphy, - camio_ext.appsz); + appbase = ioremap(camio_ext.appphy, camio_ext.appsz); if (!appbase) { rc = -ENOMEM; goto apps_no_mem; } mdcio = request_mem_region(camio_ext.mdcphy, - camio_ext.mdcsz, pdev->name); + camio_ext.mdcsz, pdev->name); if (!mdcio) { rc = -EBUSY; goto mdc_busy; } - mdcbase = ioremap(camio_ext.mdcphy, - camio_ext.mdcsz); + mdcbase = ioremap(camio_ext.mdcphy, camio_ext.mdcsz); if (!mdcbase) { rc = -ENOMEM; goto mdc_no_mem; @@ -192,13 +204,10 @@ void msm_camio_camif_pad_reg_reset(void) reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; - mask = CAM_SEL_BMSK | - CAM_PCLK_SRC_SEL_BMSK | - CAM_PCLK_INVERT_BMSK; + mask = CAM_SEL_BMSK | CAM_PCLK_SRC_SEL_BMSK | CAM_PCLK_INVERT_BMSK; value = 1 << CAM_SEL_SHFT | - 3 << CAM_PCLK_SRC_SEL_SHFT | - 0 << CAM_PCLK_INVERT_SHFT; + 3 << CAM_PCLK_SRC_SEL_SHFT | 0 << CAM_PCLK_INVERT_SHFT; writel((reg & (~mask)) | (value & mask), mdcbase); mdelay(10); @@ -232,6 +241,17 @@ void msm_camio_vfe_blk_reset(void) val &= ~0x1; writel(val, appbase + 0x00000210); mdelay(10); + + /* do axi reset */ + val = readl(appbase + 0x00000208); + val |= 0x1; + writel(val, appbase + 0x00000208); + mdelay(10); + + val = readl(appbase + 0x00000208); + val &= ~0x1; + writel(val, appbase + 0x00000208); + mdelay(10); } void msm_camio_camif_pad_reg_reset_2(void) diff --git a/drivers/media/video/msm/msm_io8x.c b/drivers/media/video/msm/msm_io8x.c index 895161ae2e148..ae4fe44c6a41a 100644 --- a/drivers/media/video/msm/msm_io8x.c +++ b/drivers/media/video/msm/msm_io8x.c @@ -1,5 +1,19 @@ -/* - * Copyright (c) 2008-2009 QUALCOMM Incorporated +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ #include @@ -37,7 +51,7 @@ static struct msm_camera_io_ext camio_ext; static struct resource *appio, *mdcio; void __iomem *appbase, *mdcbase; -extern int clk_set_flags(struct clk *clk, unsigned long flags); +int clk_set_flags(struct clk *clk, unsigned long flags); int msm_camio_clk_enable(enum msm_camio_clk_type clktype) { @@ -46,31 +60,34 @@ int msm_camio_clk_enable(enum msm_camio_clk_type clktype) switch (clktype) { case CAMIO_VFE_MDC_CLK: - camio_vfe_mdc_clk = - clk = clk_get(NULL, "vfe_mdc_clk"); + camio_vfe_mdc_clk = clk = clk_get(NULL, "vfe_mdc_clk"); break; case CAMIO_MDC_CLK: - camio_mdc_clk = - clk = clk_get(NULL, "mdc_clk"); + camio_mdc_clk = clk = clk_get(NULL, "mdc_clk"); break; case CAMIO_VFE_CLK: - camio_vfe_clk = - clk = clk_get(NULL, "vfe_clk"); + camio_vfe_clk = clk = clk_get(NULL, "vfe_clk"); break; case CAMIO_VFE_AXI_CLK: - camio_vfe_axi_clk = - clk = clk_get(NULL, "vfe_axi_clk"); + camio_vfe_axi_clk = clk = clk_get(NULL, "vfe_axi_clk"); break; default: break; } - if (!IS_ERR(clk)) + if (!IS_ERR(clk)) { + /* Set rate here *before* enabling the block to prevent + * unstable clock from source. + */ + if (clktype == CAMIO_VFE_CLK && camio_vfe_clk) { + clk_set_rate(camio_vfe_clk, 96000000); + } clk_enable(clk); + } else rc = -1; @@ -129,28 +146,26 @@ int msm_camio_enable(struct platform_device *pdev) camio_ext = camdev->ioext; appio = request_mem_region(camio_ext.appphy, - camio_ext.appsz, pdev->name); + camio_ext.appsz, pdev->name); if (!appio) { rc = -EBUSY; goto enable_fail; } - appbase = ioremap(camio_ext.appphy, - camio_ext.appsz); + appbase = ioremap(camio_ext.appphy, camio_ext.appsz); if (!appbase) { rc = -ENOMEM; goto apps_no_mem; } mdcio = request_mem_region(camio_ext.mdcphy, - camio_ext.mdcsz, pdev->name); + camio_ext.mdcsz, pdev->name); if (!mdcio) { rc = -EBUSY; goto mdc_busy; } - mdcbase = ioremap(camio_ext.mdcphy, - camio_ext.mdcsz); + mdcbase = ioremap(camio_ext.mdcphy, camio_ext.mdcsz); if (!mdcbase) { rc = -ENOMEM; goto mdc_no_mem; @@ -162,6 +177,7 @@ int msm_camio_enable(struct platform_device *pdev) msm_camio_clk_enable(CAMIO_MDC_CLK); msm_camio_clk_enable(CAMIO_VFE_MDC_CLK); msm_camio_clk_enable(CAMIO_VFE_AXI_CLK); + return 0; mdc_no_mem: @@ -203,18 +219,16 @@ void msm_camio_camif_pad_reg_reset(void) reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; mask = CAM_SEL_BMSK | - CAM_PCLK_SRC_SEL_BMSK | - CAM_PCLK_INVERT_BMSK | - EXT_CAM_HSYNC_POL_SEL_BMSK | - EXT_CAM_VSYNC_POL_SEL_BMSK | - MDDI_CLK_CHICKEN_BIT_BMSK; + CAM_PCLK_SRC_SEL_BMSK | + CAM_PCLK_INVERT_BMSK | + EXT_CAM_HSYNC_POL_SEL_BMSK | + EXT_CAM_VSYNC_POL_SEL_BMSK | MDDI_CLK_CHICKEN_BIT_BMSK; value = 1 << CAM_SEL_SHFT | - 3 << CAM_PCLK_SRC_SEL_SHFT | - 0 << CAM_PCLK_INVERT_SHFT | - 0 << EXT_CAM_HSYNC_POL_SEL_SHFT | - 0 << EXT_CAM_VSYNC_POL_SEL_SHFT | - 0 << MDDI_CLK_CHICKEN_BIT_SHFT; + 3 << CAM_PCLK_SRC_SEL_SHFT | + 0 << CAM_PCLK_INVERT_SHFT | + 0 << EXT_CAM_HSYNC_POL_SEL_SHFT | + 0 << EXT_CAM_VSYNC_POL_SEL_SHFT | 0 << MDDI_CLK_CHICKEN_BIT_SHFT; writel((reg & (~mask)) | (value & mask), mdcbase); mdelay(10); @@ -233,14 +247,11 @@ void msm_camio_camif_pad_reg_reset(void) msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_EXTERNAL); mdelay(10); - - /* todo: check return */ - if (camio_vfe_clk) - clk_set_rate(camio_vfe_clk, 96000000); } void msm_camio_vfe_blk_reset(void) { +#if 0 uint32_t val; val = readl(appbase + 0x00000210); @@ -252,6 +263,7 @@ void msm_camio_vfe_blk_reset(void) val &= ~0x1; writel(val, appbase + 0x00000210); mdelay(10); +#endif } void msm_camio_camif_pad_reg_reset_2(void) diff --git a/drivers/media/video/msm/msm_v4l2.c b/drivers/media/video/msm/msm_v4l2.c index 744c92b360b7b..6abbdb957cb5a 100644 --- a/drivers/media/video/msm/msm_v4l2.c +++ b/drivers/media/video/msm/msm_v4l2.c @@ -1,6 +1,18 @@ -/* +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * Copyright (C) 2008-2009 QUALCOMM Incorporated. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. * */ @@ -48,7 +60,7 @@ struct msm_v4l2_device { struct v4l2_format current_cap_format; struct v4l2_format current_pix_format; struct video_device *pvdev; - struct msm_v4l2_driver *drv; + struct msm_v4l2_driver *drv; uint8_t opencnt; spinlock_t read_queue_lock; @@ -56,7 +68,6 @@ struct msm_v4l2_device { static struct msm_v4l2_device *g_pmsm_v4l2_dev; - static DEFINE_MUTEX(msm_v4l2_opencnt_lock); static int msm_v4l2_open(struct file *f) @@ -65,9 +76,8 @@ static int msm_v4l2_open(struct file *f) D("%s\n", __func__); mutex_lock(&msm_v4l2_opencnt_lock); if (!g_pmsm_v4l2_dev->opencnt) { - rc = g_pmsm_v4l2_dev->drv->open( - g_pmsm_v4l2_dev->drv->sync, - MSM_APPS_ID_V4L2); + rc = g_pmsm_v4l2_dev->drv->open(g_pmsm_v4l2_dev->drv->sync, + MSM_APPS_ID_V4L2); } g_pmsm_v4l2_dev->opencnt++; mutex_unlock(&msm_v4l2_opencnt_lock); @@ -82,8 +92,8 @@ static int msm_v4l2_release(struct file *f) if (!g_pmsm_v4l2_dev->opencnt) { g_pmsm_v4l2_dev->opencnt--; if (!g_pmsm_v4l2_dev->opencnt) { - rc = g_pmsm_v4l2_dev->drv->release( - g_pmsm_v4l2_dev->drv->sync); + rc = g_pmsm_v4l2_dev->drv->release(g_pmsm_v4l2_dev-> + drv->sync); } } mutex_unlock(&msm_v4l2_opencnt_lock); @@ -111,21 +121,21 @@ static long msm_v4l2_ioctl(struct file *filep, return -ENOMEM; } - ctrlcmd->length = 0; - ctrlcmd->value = NULL; + ctrlcmd->length = 0; + ctrlcmd->value = NULL; ctrlcmd->timeout_ms = 10000; D("msm_v4l2_ioctl, MSM_V4L2_START_SNAPSHOT v4l2 ioctl %d\n", - cmd); + cmd); ctrlcmd->type = MSM_V4L2_SNAPSHOT; return g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, - ctrlcmd); + ctrlcmd); case MSM_V4L2_GET_PICTURE: D("msm_v4l2_ioctl, MSM_V4L2_GET_PICTURE v4l2 ioctl %d\n", cmd); ctrlcmd = (struct msm_ctrl_cmd *)arg; - return g_pmsm_v4l2_dev->drv->get_pict( - g_pmsm_v4l2_dev->drv->sync, ctrlcmd); + return g_pmsm_v4l2_dev->drv->get_pict(g_pmsm_v4l2_dev->drv-> + sync, ctrlcmd); default: D("msm_v4l2_ioctl, standard v4l2 ioctl %d\n", cmd); @@ -143,23 +153,22 @@ static int msm_v4l2_querycap(struct file *f, { D("%s\n", __func__); strncpy(pcaps->driver, MSM_APPS_ID_V4L2, sizeof(pcaps->driver)); - strncpy(pcaps->card, - MSM_V4L2_DEVICE_NAME, sizeof(pcaps->card)); + strncpy(pcaps->card, MSM_V4L2_DEVICE_NAME, sizeof(pcaps->card)); pcaps->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; return 0; } -static int msm_v4l2_s_std(struct file *f, void *pctx, v4l2_std_id *pnorm) +static int msm_v4l2_s_std(struct file *f, void *pctx, v4l2_std_id * pnorm) { D("%s\n", __func__); return 0; } static int msm_v4l2_queryctrl(struct file *f, - void *pctx, struct v4l2_queryctrl *pqctrl) + void *pctx, struct v4l2_queryctrl *pqctrl) { - int rc = 0; - struct msm_ctrl_cmd *ctrlcmd; + int rc = 0; + struct msm_ctrl_cmd *ctrlcmd; D("%s\n", __func__); @@ -169,9 +178,9 @@ static int msm_v4l2_queryctrl(struct file *f, return -ENOMEM; } - ctrlcmd->type = MSM_V4L2_QUERY_CTRL; - ctrlcmd->length = sizeof(struct v4l2_queryctrl); - ctrlcmd->value = pqctrl; + ctrlcmd->type = MSM_V4L2_QUERY_CTRL; + ctrlcmd->length = sizeof(struct v4l2_queryctrl); + ctrlcmd->value = pqctrl; ctrlcmd->timeout_ms = 10000; rc = g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd); @@ -194,9 +203,9 @@ static int msm_v4l2_g_ctrl(struct file *f, void *pctx, struct v4l2_control *c) return -ENOMEM; } - ctrlcmd->type = MSM_V4L2_GET_CTRL; - ctrlcmd->length = sizeof(struct v4l2_control); - ctrlcmd->value = c; + ctrlcmd->type = MSM_V4L2_GET_CTRL; + ctrlcmd->length = sizeof(struct v4l2_control); + ctrlcmd->value = c; ctrlcmd->timeout_ms = 10000; rc = g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd); @@ -217,9 +226,9 @@ static int msm_v4l2_s_ctrl(struct file *f, void *pctx, struct v4l2_control *c) return -ENOMEM; } - ctrlcmd->type = MSM_V4L2_SET_CTRL; - ctrlcmd->length = sizeof(struct v4l2_control); - ctrlcmd->value = c; + ctrlcmd->type = MSM_V4L2_SET_CTRL; + ctrlcmd->length = sizeof(struct v4l2_control); + ctrlcmd->value = c; ctrlcmd->timeout_ms = 10000; D("%s\n", __func__); @@ -258,7 +267,7 @@ static int msm_v4l2_querybuf(struct file *f, void *pctx, struct v4l2_buffer *pb) y_pad = y_size % 4; #endif - __u32 y_pad = pb->bytesused % 4; + __u32 y_pad = pb->bytesused % 4; /* V4L2 videodev will do the copy_from_user. */ @@ -268,7 +277,7 @@ static int msm_v4l2_querybuf(struct file *f, void *pctx, struct v4l2_buffer *pb) pmem_buf.y_off = 0; pmem_buf.fd = (int)pb->reserved; /* pmem_buf.cbcr_off = (y_size + y_pad); */ - pmem_buf.cbcr_off = (pb->bytesused + y_pad); + pmem_buf.cbcr_off = (pb->bytesused + y_pad); g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync, &pmem_buf); @@ -277,12 +286,12 @@ static int msm_v4l2_querybuf(struct file *f, void *pctx, struct v4l2_buffer *pb) static int msm_v4l2_qbuf(struct file *f, void *pctx, struct v4l2_buffer *pb) { - /* - __u32 y_size = 0; - __u32 y_pad = 0; - __u32 width = 0; - __u32 height = 0; - */ + /* + __u32 y_size = 0; + __u32 y_pad = 0; + __u32 width = 0; + __u32 height = 0; + */ __u32 y_pad = 0; @@ -307,65 +316,62 @@ static int msm_v4l2_qbuf(struct file *f, void *pctx, struct v4l2_buffer *pb) if (pb->type == V4L2_BUF_TYPE_PRIVATE) { /* this qbuf is actually for releasing */ - frame.buffer = pb->m.userptr; - frame.y_off = 0; + frame.buffer = pb->m.userptr; + frame.y_off = 0; /* frame.cbcr_off = (y_size + y_pad); */ - frame.cbcr_off = (pb->bytesused + y_pad); - frame.fd = pb->reserved; + frame.cbcr_off = (pb->bytesused + y_pad); + frame.fd = pb->reserved; D("V4L2_BUF_TYPE_PRIVATE: pb->bytesused = %d \n", - pb->bytesused); + pb->bytesused); - g_pmsm_v4l2_dev->drv->put_frame( - g_pmsm_v4l2_dev->drv->sync, - &frame); + g_pmsm_v4l2_dev->drv->put_frame(g_pmsm_v4l2_dev->drv-> + sync, &frame); return 0; } D("V4L2_BUF_TYPE_VIDEO_CAPTURE: pb->bytesused = %d \n", - pb->bytesused); + pb->bytesused); - meminfo.type = MSM_PMEM_OUTPUT2; - meminfo.fd = (int)pb->reserved; - meminfo.vaddr = (void *)pb->m.userptr; - meminfo.y_off = 0; + meminfo.type = MSM_PMEM_OUTPUT2; + meminfo.fd = (int)pb->reserved; + meminfo.vaddr = (void *)pb->m.userptr; + meminfo.y_off = 0; /* meminfo.cbcr_off = (y_size + y_pad); */ - meminfo.cbcr_off = (pb->bytesused + y_pad); - if (cnt == PREVIEW_FRAMES_NUM - 1) - meminfo.active = 0; - else - meminfo.active = 1; + meminfo.cbcr_off = (pb->bytesused + y_pad); + meminfo.vfe_can_write = + cnt != PREVIEW_FRAMES_NUM - 1; cnt++; g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync, - &meminfo); + &meminfo); } else if ((pb->flags) & 0x0001) { /* this is for snapshot */ - __u32 y_size = 0; + __u32 y_size = 0; - if ((pb->flags >> 8) & 0x01) { + if ((pb->flags >> 8) & 0x01) { - y_size = pb->bytesused; + y_size = pb->bytesused; - meminfo.type = MSM_PMEM_THUMBNAIL; - } else if ((pb->flags >> 9) & 0x01) { + meminfo.type = MSM_PMEM_THUMBNAIL; + } else if ((pb->flags >> 9) & 0x01) { - y_size = pb->bytesused; + y_size = pb->bytesused; - meminfo.type = MSM_PMEM_MAINIMG; - } + meminfo.type = MSM_PMEM_MAINIMG; + } - y_pad = y_size % 4; + y_pad = y_size % 4; - meminfo.fd = (int)pb->reserved; - meminfo.vaddr = (void *)pb->m.userptr; - meminfo.y_off = 0; - /* meminfo.cbcr_off = (y_size + y_pad); */ - meminfo.cbcr_off = (y_size + y_pad); - meminfo.active = 1; - g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync, - &meminfo); + meminfo.fd = (int)pb->reserved; + meminfo.vaddr = (void *)pb->m.userptr; + meminfo.y_off = 0; + /* meminfo.cbcr_off = (y_size + y_pad); */ + meminfo.cbcr_off = (y_size + y_pad); + meminfo.vfe_can_write = 1; + g_pmsm_v4l2_dev->drv->reg_pmem(g_pmsm_v4l2_dev->drv->sync, + &meminfo); } return 0; @@ -381,29 +387,27 @@ static int msm_v4l2_dqbuf(struct file *f, void *pctx, struct v4l2_buffer *pb) D("%s, %d\n", __func__, __LINE__); - g_pmsm_v4l2_dev->drv->get_frame( - g_pmsm_v4l2_dev->drv->sync, - &frame); + g_pmsm_v4l2_dev->drv->get_frame(g_pmsm_v4l2_dev->drv->sync, + &frame); - pb->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - pb->m.userptr = (unsigned long)frame.buffer; /* FIXME */ - pb->reserved = (int)frame.fd; + pb->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + pb->m.userptr = (unsigned long)frame.buffer; /* FIXME */ + pb->reserved = (int)frame.fd; /* pb->length = (int)frame.cbcr_off; */ - pb->bytesused = frame.cbcr_off; + pb->bytesused = frame.cbcr_off; } else if (pb->type == V4L2_BUF_TYPE_PRIVATE) { - __u32 y_pad = pb->bytesused % 4; + __u32 y_pad = pb->bytesused % 4; - frame.buffer = pb->m.userptr; - frame.y_off = 0; + frame.buffer = pb->m.userptr; + frame.y_off = 0; /* frame.cbcr_off = (y_size + y_pad); */ frame.cbcr_off = (pb->bytesused + y_pad); - frame.fd = pb->reserved; + frame.fd = pb->reserved; - g_pmsm_v4l2_dev->drv->put_frame( - g_pmsm_v4l2_dev->drv->sync, - &frame); + g_pmsm_v4l2_dev->drv->put_frame(g_pmsm_v4l2_dev->drv->sync, + &frame); } return 0; @@ -411,7 +415,7 @@ static int msm_v4l2_dqbuf(struct file *f, void *pctx, struct v4l2_buffer *pb) static int msm_v4l2_streamon(struct file *f, void *pctx, enum v4l2_buf_type i) { - struct msm_ctrl_cmd *ctrlcmd; + struct msm_ctrl_cmd *ctrlcmd; ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC); if (!ctrlcmd) { @@ -419,16 +423,14 @@ static int msm_v4l2_streamon(struct file *f, void *pctx, enum v4l2_buf_type i) return -ENOMEM; } - ctrlcmd->type = MSM_V4L2_STREAM_ON; + ctrlcmd->type = MSM_V4L2_STREAM_ON; ctrlcmd->timeout_ms = 10000; - ctrlcmd->length = 0; - ctrlcmd->value = NULL; + ctrlcmd->length = 0; + ctrlcmd->value = NULL; D("%s\n", __func__); - g_pmsm_v4l2_dev->drv->ctrl( - g_pmsm_v4l2_dev->drv->sync, - ctrlcmd); + g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd); D("%s after drv->ctrl \n", __func__); @@ -437,7 +439,7 @@ static int msm_v4l2_streamon(struct file *f, void *pctx, enum v4l2_buf_type i) static int msm_v4l2_streamoff(struct file *f, void *pctx, enum v4l2_buf_type i) { - struct msm_ctrl_cmd *ctrlcmd; + struct msm_ctrl_cmd *ctrlcmd; ctrlcmd = kmalloc(sizeof(struct msm_ctrl_cmd), GFP_ATOMIC); if (!ctrlcmd) { @@ -445,17 +447,14 @@ static int msm_v4l2_streamoff(struct file *f, void *pctx, enum v4l2_buf_type i) return -ENOMEM; } - ctrlcmd->type = MSM_V4L2_STREAM_OFF; + ctrlcmd->type = MSM_V4L2_STREAM_OFF; ctrlcmd->timeout_ms = 10000; - ctrlcmd->length = 0; - ctrlcmd->value = NULL; - + ctrlcmd->length = 0; + ctrlcmd->value = NULL; D("%s\n", __func__); - g_pmsm_v4l2_dev->drv->ctrl( - g_pmsm_v4l2_dev->drv->sync, - ctrlcmd); + g_pmsm_v4l2_dev->drv->ctrl(g_pmsm_v4l2_dev->drv->sync, ctrlcmd); return 0; } @@ -506,7 +505,7 @@ static int msm_v4l2_g_fmt_cap(struct file *f, static int msm_v4l2_s_fmt_cap(struct file *f, void *pctx, struct v4l2_format *pfmt) { - struct msm_ctrl_cmd *ctrlcmd; + struct msm_ctrl_cmd *ctrlcmd; D("%s\n", __func__); @@ -516,10 +515,10 @@ static int msm_v4l2_s_fmt_cap(struct file *f, return -ENOMEM; } - ctrlcmd->type = MSM_V4L2_VID_CAP_TYPE; - ctrlcmd->length = sizeof(struct v4l2_format); - ctrlcmd->value = pfmt; - ctrlcmd->timeout_ms = 10000; + ctrlcmd->type = MSM_V4L2_VID_CAP_TYPE; + ctrlcmd->length = sizeof(struct v4l2_format); + ctrlcmd->value = pfmt; + ctrlcmd->timeout_ms = 10000; if (pfmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -1; @@ -650,14 +649,14 @@ static void msm_v4l2_dev_init(struct msm_v4l2_device *pmsm_v4l2_dev) } static int msm_v4l2_try_fmt_cap(struct file *file, - void *fh, struct v4l2_format *f) + void *fh, struct v4l2_format *f) { /* FIXME */ return 0; } static int mm_v4l2_try_fmt_type_private(struct file *file, - void *fh, struct v4l2_format *f) + void *fh, struct v4l2_format *f) { /* FIXME */ return 0; @@ -704,7 +703,6 @@ static const struct v4l2_ioctl_ops msm_ioctl_ops = { static int msm_v4l2_video_dev_init(struct video_device *pvd) { strncpy(pvd->name, MSM_APPS_ID_V4L2, sizeof(pvd->name)); - pvd->vfl_type = VID_TYPE_CAPTURE; pvd->fops = &msm_v4l2_fops; pvd->release = msm_v4l2_release_dev; pvd->minor = -1; @@ -723,8 +721,7 @@ static int __init msm_v4l2_init(void) if (pvdev == NULL) return rc; - pmsm_v4l2_dev = - kzalloc(sizeof(struct msm_v4l2_device), GFP_KERNEL); + pmsm_v4l2_dev = kzalloc(sizeof(struct msm_v4l2_device), GFP_KERNEL); if (pmsm_v4l2_dev == NULL) { video_device_release(pvdev); return rc; @@ -736,7 +733,7 @@ static int __init msm_v4l2_init(void) g_pmsm_v4l2_dev->pvdev = pvdev; g_pmsm_v4l2_dev->drv = - kzalloc(sizeof(struct msm_v4l2_driver), GFP_KERNEL); + kzalloc(sizeof(struct msm_v4l2_driver), GFP_KERNEL); if (!g_pmsm_v4l2_dev->drv) { video_device_release(pvdev); kfree(pmsm_v4l2_dev); @@ -751,8 +748,8 @@ static int __init msm_v4l2_init(void) return rc; } - if (video_register_device(pvdev, VFL_TYPE_GRABBER, - MSM_V4L2_DEVNUM_YUV)) { + if (video_register_device(pvdev, + VFL_TYPE_GRABBER, MSM_V4L2_DEVNUM_YUV)) { D("failed to register device\n"); video_device_release(pvdev); kfree(g_pmsm_v4l2_dev); diff --git a/drivers/media/video/msm/msm_vfe7x.c b/drivers/media/video/msm/msm_vfe7x.c index ee282339b33cd..ab3123b720d0f 100644 --- a/drivers/media/video/msm/msm_vfe7x.c +++ b/drivers/media/video/msm/msm_vfe7x.c @@ -1,5 +1,19 @@ -/* - * Copyright (C) 2008-2009 QUALCOMM Incorporated. +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ #include @@ -8,6 +22,7 @@ #include #include #include +#include #include #include #include "msm_vfe7x.h" @@ -31,65 +46,68 @@ static struct msm_adsp_module *qcam_mod; static struct msm_adsp_module *vfe_mod; static struct msm_vfe_callback *resp; -static void *extdata; -static uint32_t extlen; +static struct vfe_frame_extra *extdata; struct mutex vfe_lock; -static void *vfe_syncdata; +static void *vfe_syncdata; static uint8_t vfestopped; static struct stop_event stopevent; static void vfe_7x_convert(struct msm_vfe_phy_info *pinfo, - enum vfe_resp_msg type, - void *data, void **ext, int32_t *elen) + enum vfe_resp_msg type, + void *data, void **ext, int *elen) { switch (type) { case VFE_MSG_OUTPUT1: - case VFE_MSG_OUTPUT2: { - pinfo->y_phy = ((struct vfe_endframe *)data)->y_address; - pinfo->cbcr_phy = - ((struct vfe_endframe *)data)->cbcr_address; + case VFE_MSG_OUTPUT2:{ + pinfo->y_phy = ((struct vfe_endframe *)data)->y_address; + pinfo->cbcr_phy = + ((struct vfe_endframe *)data)->cbcr_address; - CDBG("vfe_7x_convert, y_phy = 0x%x, cbcr_phy = 0x%x\n", - pinfo->y_phy, pinfo->cbcr_phy); + CDBG("vfe_7x_convert, y_phy = 0x%x, cbcr_phy = 0x%x\n", + pinfo->y_phy, pinfo->cbcr_phy); - ((struct vfe_frame_extra *)extdata)->bl_evencol = - ((struct vfe_endframe *)data)->blacklevelevencolumn; + ((struct vfe_frame_extra *)extdata)->bl_evencol = + ((struct vfe_endframe *)data)->blacklevelevencolumn; - ((struct vfe_frame_extra *)extdata)->bl_oddcol = - ((struct vfe_endframe *)data)->blackleveloddcolumn; + ((struct vfe_frame_extra *)extdata)->bl_oddcol = + ((struct vfe_endframe *)data)->blackleveloddcolumn; - ((struct vfe_frame_extra *)extdata)->g_def_p_cnt = - ((struct vfe_endframe *)data)->greendefectpixelcount; + ((struct vfe_frame_extra *)extdata)->g_def_p_cnt = + ((struct vfe_endframe *)data)-> + greendefectpixelcount; - ((struct vfe_frame_extra *)extdata)->r_b_def_p_cnt = - ((struct vfe_endframe *)data)->redbluedefectpixelcount; + ((struct vfe_frame_extra *)extdata)->r_b_def_p_cnt = + ((struct vfe_endframe *)data)-> + redbluedefectpixelcount; - *ext = extdata; - *elen = extlen; - } + *ext = extdata; + *elen = sizeof(*extdata); + } break; case VFE_MSG_STATS_AF: case VFE_MSG_STATS_WE: - pinfo->sbuf_phy = *(uint32_t *)data; + pinfo->sbuf_phy = *(uint32_t *) data; break; default: break; - } /* switch */ + } } static void vfe_7x_ops(void *driver_data, unsigned id, size_t len, - void (*getevent)(void *ptr, size_t len)) + void (*getevent) (void *ptr, size_t len)) { uint32_t evt_buf[3]; struct msm_vfe_resp *rp; void *data; len = (id == (uint16_t)-1) ? 0 : len; - data = resp->vfe_alloc(sizeof(struct msm_vfe_resp) + len, vfe_syncdata); + data = resp->vfe_alloc(sizeof(struct msm_vfe_resp) + len, + vfe_syncdata, + GFP_ATOMIC); if (!data) { pr_err("rp: cannot allocate buffer\n"); @@ -100,14 +118,16 @@ static void vfe_7x_ops(void *driver_data, unsigned id, size_t len, if (id == ((uint16_t)-1)) { /* event */ - rp->type = VFE_EVENT; - rp->evt_msg.type = MSM_CAMERA_EVT; + rp->type = VFE_EVENT; + rp->evt_msg.type = MSM_CAMERA_EVT; getevent(evt_buf, sizeof(evt_buf)); rp->evt_msg.msg_id = evt_buf[0]; - resp->vfe_resp(rp, MSM_CAM_Q_VFE_EVT, vfe_syncdata); + resp->vfe_resp(rp, MSM_CAM_Q_VFE_EVT, + vfe_syncdata, + GFP_ATOMIC); } else { /* messages */ - rp->evt_msg.type = MSM_CAMERA_MSG; + rp->evt_msg.type = MSM_CAMERA_MSG; rp->evt_msg.msg_id = id; rp->evt_msg.data = rp + 1; getevent(rp->evt_msg.data, len); @@ -120,27 +140,27 @@ static void vfe_7x_ops(void *driver_data, unsigned id, size_t len, case MSG_OUTPUT1: rp->type = VFE_MSG_OUTPUT1; vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT1, - rp->evt_msg.data, &(rp->extdata), - &(rp->extlen)); + rp->evt_msg.data, &(rp->extdata), + &(rp->extlen)); break; case MSG_OUTPUT2: rp->type = VFE_MSG_OUTPUT2; vfe_7x_convert(&(rp->phy), VFE_MSG_OUTPUT2, - rp->evt_msg.data, &(rp->extdata), - &(rp->extlen)); + rp->evt_msg.data, &(rp->extdata), + &(rp->extlen)); break; case MSG_STATS_AF: rp->type = VFE_MSG_STATS_AF; vfe_7x_convert(&(rp->phy), VFE_MSG_STATS_AF, - rp->evt_msg.data, NULL, NULL); + rp->evt_msg.data, NULL, NULL); break; case MSG_STATS_WE: rp->type = VFE_MSG_STATS_WE; vfe_7x_convert(&(rp->phy), VFE_MSG_STATS_WE, - rp->evt_msg.data, NULL, NULL); + rp->evt_msg.data, NULL, NULL); CDBG("MSG_STATS_WE: phy = 0x%x\n", rp->phy.sbuf_phy); break; @@ -151,12 +171,11 @@ static void vfe_7x_ops(void *driver_data, unsigned id, size_t len, wake_up(&stopevent.wait); break; - default: rp->type = VFE_MSG_GENERAL; break; } - resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, vfe_syncdata); + resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, vfe_syncdata, GFP_ATOMIC); } } @@ -177,7 +196,7 @@ static int vfe_7x_enable(struct camera_enable_cmd *enable) } static int vfe_7x_disable(struct camera_enable_cmd *enable, - struct platform_device *dev __attribute__((unused))) + struct platform_device *dev __attribute__ ((unused))) { int rc = -EFAULT; @@ -193,8 +212,7 @@ static int vfe_7x_stop(void) { int rc = 0; uint32_t stopcmd = VFE_STOP_CMD; - rc = msm_adsp_write(vfe_mod, QDSP_CMDQUEUE, - &stopcmd, sizeof(uint32_t)); + rc = msm_adsp_write(vfe_mod, QDSP_CMDQUEUE, &stopcmd, sizeof(uint32_t)); if (rc < 0) { CDBG("%s:%d: failed rc = %d \n", __func__, __LINE__, rc); return rc; @@ -202,8 +220,8 @@ static int vfe_7x_stop(void) stopevent.state = 0; rc = wait_event_timeout(stopevent.wait, - stopevent.state != 0, - msecs_to_jiffies(stopevent.timeout)); + stopevent.state != 0, + msecs_to_jiffies(stopevent.timeout)); return rc; } @@ -229,11 +247,11 @@ static void vfe_7x_release(struct platform_device *pdev) msm_camio_disable(pdev); kfree(extdata); - extlen = 0; + extdata = 0; } static int vfe_7x_init(struct msm_vfe_callback *presp, - struct platform_device *dev) + struct platform_device *dev) { int rc = 0; @@ -253,10 +271,7 @@ static int vfe_7x_init(struct msm_vfe_callback *presp, msm_camio_camif_pad_reg_reset(); - extlen = sizeof(struct vfe_frame_extra); - - extdata = - kmalloc(sizeof(extlen), GFP_ATOMIC); + extdata = kmalloc(sizeof(struct vfe_frame_extra), GFP_ATOMIC); if (!extdata) { rc = -ENOMEM; goto init_fail; @@ -281,16 +296,14 @@ static int vfe_7x_init(struct msm_vfe_callback *presp, get_qcam_fail: kfree(extdata); init_fail: - extlen = 0; return rc; } -static int vfe_7x_config_axi(int mode, - struct axidata *ad, struct axiout *ao) +static int vfe_7x_config_axi(int mode, struct axidata *ad, struct axiout *ao) { struct msm_pmem_region *regptr; unsigned long *bptr; - int cnt; + int cnt; int rc = 0; @@ -299,7 +312,7 @@ static int vfe_7x_config_axi(int mode, CDBG("bufnum1 = %d\n", ad->bufnum1); CDBG("config_axi1: O1, phy = 0x%lx, y_off = %d, cbcr_off =%d\n", - regptr->paddr, regptr->info.y_off, regptr->info.cbcr_off); + regptr->paddr, regptr->info.y_off, regptr->info.cbcr_off); bptr = &ao->output1buffer1_y_phy; for (cnt = 0; cnt < ad->bufnum1; cnt++) { @@ -318,14 +331,14 @@ static int vfe_7x_config_axi(int mode, *bptr = regptr->paddr + regptr->info.cbcr_off; bptr++; } - } /* if OUTPUT1 or Both */ - + } + /* if OUTPUT1 or Both */ if (mode == OUTPUT_2 || mode == OUTPUT_1_AND_2) { regptr = &(ad->region[ad->bufnum1]); CDBG("bufnum2 = %d\n", ad->bufnum2); CDBG("config_axi2: O2, phy = 0x%lx, y_off = %d, cbcr_off =%d\n", - regptr->paddr, regptr->info.y_off, regptr->info.cbcr_off); + regptr->paddr, regptr->info.y_off, regptr->info.cbcr_off); bptr = &ao->output2buffer1_y_phy; for (cnt = 0; cnt < ad->bufnum2; cnt++) { @@ -362,14 +375,12 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) struct vfe_stats_af_cfg *sfcfg = NULL; struct axiout *axio = NULL; - void *cmd_data = NULL; - void *cmd_data_alloc = NULL; + void *cmd_data = NULL; + void *cmd_data_alloc = NULL; long rc = 0; struct msm_vfe_command_7k *vfecmd; - vfecmd = - kmalloc(sizeof(struct msm_vfe_command_7k), - GFP_ATOMIC); + vfecmd = kmalloc(sizeof(struct msm_vfe_command_7k), GFP_ATOMIC); if (!vfecmd) { pr_err("vfecmd alloc failed!\n"); return -ENOMEM; @@ -379,305 +390,325 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) cmd->cmd_type != CMD_STATS_BUF_RELEASE && cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) { if (copy_from_user(vfecmd, - (void __user *)(cmd->value), - sizeof(struct msm_vfe_command_7k))) { + (void __user *)(cmd->value), + sizeof(struct msm_vfe_command_7k))) { rc = -EFAULT; goto config_failure; } } switch (cmd->cmd_type) { - case CMD_STATS_ENABLE: - case CMD_STATS_AXI_CFG: { - axid = data; - if (!axid) { - rc = -EFAULT; - goto config_failure; - } + case CMD_STATS_AEC_AWB_ENABLE: + case CMD_STATS_AXI_CFG:{ + axid = data; + if (!axid) { + rc = -EFAULT; + goto config_failure; + } - scfg = - kmalloc(sizeof(struct vfe_stats_we_cfg), - GFP_ATOMIC); - if (!scfg) { - rc = -ENOMEM; - goto config_failure; - } + scfg = + kmalloc(sizeof(struct vfe_stats_we_cfg), + GFP_ATOMIC); + if (!scfg) { + rc = -ENOMEM; + goto config_failure; + } - if (copy_from_user(scfg, - (void __user *)(vfecmd->value), - vfecmd->length)) { + if (vfecmd->length != sizeof(typeof(*scfg))) { + rc = -EIO; + pr_err + ("msm_camera: %s: cmd %d: "\ + "user-space data size %d "\ + "!= kernel data size %d\n", __func__, + cmd->cmd_type, vfecmd->length, + sizeof(typeof(*scfg))); + goto config_failure; + } - rc = -EFAULT; - goto config_done; - } + if (copy_from_user(scfg, + (void __user *)(vfecmd->value), + vfecmd->length)) { - CDBG("STATS_ENABLE: bufnum = %d, enabling = %d\n", - axid->bufnum1, scfg->wb_expstatsenable); + rc = -EFAULT; + goto config_done; + } - if (axid->bufnum1 > 0) { - regptr = axid->region; + CDBG("STATS_ENABLE: bufnum = %d, enabling = %d\n", + axid->bufnum1, scfg->wb_expstatsenable); - for (i = 0; i < axid->bufnum1; i++) { + if (axid->bufnum1 > 0) { + regptr = axid->region; - CDBG("STATS_ENABLE, phy = 0x%lx\n", - regptr->paddr); + for (i = 0; i < axid->bufnum1; i++) { - scfg->wb_expstatoutputbuffer[i] = - (void *)regptr->paddr; - regptr++; - } + CDBG("STATS_ENABLE, phy = 0x%lx\n", + regptr->paddr); - cmd_data = scfg; + scfg->wb_expstatoutputbuffer[i] = + (void *)regptr->paddr; + regptr++; + } - } else { - rc = -EINVAL; - goto config_done; + cmd_data = scfg; + + } else { + rc = -EINVAL; + goto config_done; + } } - } break; case CMD_STATS_AF_ENABLE: - case CMD_STATS_AF_AXI_CFG: { - axid = data; - if (!axid) { - rc = -EFAULT; - goto config_failure; - } + case CMD_STATS_AF_AXI_CFG:{ + axid = data; + if (!axid) { + rc = -EFAULT; + goto config_failure; + } - sfcfg = - kmalloc(sizeof(struct vfe_stats_af_cfg), - GFP_ATOMIC); + sfcfg = + kmalloc(sizeof(struct vfe_stats_af_cfg), + GFP_ATOMIC); - if (!sfcfg) { - rc = -ENOMEM; - goto config_failure; - } + if (!sfcfg) { + rc = -ENOMEM; + goto config_failure; + } - if (copy_from_user(sfcfg, - (void __user *)(vfecmd->value), - vfecmd->length)) { + if (vfecmd->length > sizeof(typeof(*sfcfg))) { + pr_err + ("msm_camera: %s: cmd %d: user-space "\ + "data %d exceeds kernel buffer %d\n", + __func__, cmd->cmd_type, vfecmd->length, + sizeof(typeof(*sfcfg))); + rc = -EIO; + goto config_failure; + } - rc = -EFAULT; - goto config_done; - } + if (copy_from_user(sfcfg, + (void __user *)(vfecmd->value), + vfecmd->length)) { - CDBG("AF_ENABLE: bufnum = %d, enabling = %d\n", - axid->bufnum1, sfcfg->af_enable); + rc = -EFAULT; + goto config_done; + } - if (axid->bufnum1 > 0) { - regptr = axid->region; + CDBG("AF_ENABLE: bufnum = %d, enabling = %d\n", + axid->bufnum1, sfcfg->af_enable); - for (i = 0; i < axid->bufnum1; i++) { + if (axid->bufnum1 > 0) { + regptr = axid->region; - CDBG("STATS_ENABLE, phy = 0x%lx\n", - regptr->paddr); + for (i = 0; i < axid->bufnum1; i++) { - sfcfg->af_outbuf[i] = - (void *)regptr->paddr; + CDBG("STATS_ENABLE, phy = 0x%lx\n", + regptr->paddr); - regptr++; - } + sfcfg->af_outbuf[i] = + (void *)regptr->paddr; - cmd_data = sfcfg; + regptr++; + } - } else { - rc = -EINVAL; - goto config_done; + cmd_data = sfcfg; + + } else { + rc = -EINVAL; + goto config_done; + } } - } break; - case CMD_FRAME_BUF_RELEASE: { - struct msm_frame *b; - unsigned long p; - struct vfe_outputack fack; - if (!data) { - rc = -EFAULT; - goto config_failure; - } + case CMD_FRAME_BUF_RELEASE:{ + struct msm_frame *b; + unsigned long p; + struct vfe_outputack fack; + if (!data) { + rc = -EFAULT; + goto config_failure; + } - b = (struct msm_frame *)(cmd->value); - p = *(unsigned long *)data; + b = (struct msm_frame *)(cmd->value); + p = *(unsigned long *)data; - fack.header = VFE_FRAME_ACK; + fack.header = VFE_FRAME_ACK; - fack.output2newybufferaddress = - (void *)(p + b->y_off); + fack.output2newybufferaddress = (void *)(p + b->y_off); - fack.output2newcbcrbufferaddress = - (void *)(p + b->cbcr_off); + fack.output2newcbcrbufferaddress = + (void *)(p + b->cbcr_off); - vfecmd->queue = QDSP_CMDQUEUE; - vfecmd->length = sizeof(struct vfe_outputack); - cmd_data = &fack; - } + vfecmd->queue = QDSP_CMDQUEUE; + vfecmd->length = sizeof(struct vfe_outputack); + cmd_data = &fack; + } break; case CMD_SNAP_BUF_RELEASE: break; - case CMD_STATS_BUF_RELEASE: { - CDBG("vfe_7x_config: CMD_STATS_BUF_RELEASE\n"); - if (!data) { - rc = -EFAULT; - goto config_failure; - } - - sack.header = STATS_WE_ACK; - sack.bufaddr = (void *)*(uint32_t *)data; + case CMD_STATS_BUF_RELEASE:{ + CDBG("vfe_7x_config: CMD_STATS_BUF_RELEASE\n"); + if (!data) { + rc = -EFAULT; + goto config_failure; + } - vfecmd->queue = QDSP_CMDQUEUE; - vfecmd->length = sizeof(struct vfe_stats_ack); - cmd_data = &sack; - } - break; + sack.header = STATS_WE_ACK; + sack.bufaddr = (void *)*(uint32_t *) data; - case CMD_STATS_AF_BUF_RELEASE: { - CDBG("vfe_7x_config: CMD_STATS_AF_BUF_RELEASE\n"); - if (!data) { - rc = -EFAULT; - goto config_failure; + vfecmd->queue = QDSP_CMDQUEUE; + vfecmd->length = sizeof(struct vfe_stats_ack); + cmd_data = &sack; } - - sack.header = STATS_AF_ACK; - sack.bufaddr = (void *)*(uint32_t *)data; - - vfecmd->queue = QDSP_CMDQUEUE; - vfecmd->length = sizeof(struct vfe_stats_ack); - cmd_data = &sack; - } break; - case CMD_GENERAL: - case CMD_STATS_DISABLE: { - if (vfecmd->length > 256) { - cmd_data_alloc = - cmd_data = kmalloc(vfecmd->length, GFP_ATOMIC); - if (!cmd_data) { - rc = -ENOMEM; + case CMD_STATS_AF_BUF_RELEASE:{ + CDBG("vfe_7x_config: CMD_STATS_AF_BUF_RELEASE\n"); + if (!data) { + rc = -EFAULT; goto config_failure; } - } else - cmd_data = buf; - if (copy_from_user(cmd_data, - (void __user *)(vfecmd->value), - vfecmd->length)) { + sack.header = STATS_AF_ACK; + sack.bufaddr = (void *)*(uint32_t *) data; - rc = -EFAULT; - goto config_done; + vfecmd->queue = QDSP_CMDQUEUE; + vfecmd->length = sizeof(struct vfe_stats_ack); + cmd_data = &sack; } + break; - if (vfecmd->queue == QDSP_CMDQUEUE) { - switch (*(uint32_t *)cmd_data) { - case VFE_RESET_CMD: - msm_camio_vfe_blk_reset(); - msm_camio_camif_pad_reg_reset_2(); - vfestopped = 0; - break; - - case VFE_START_CMD: - msm_camio_camif_pad_reg_reset_2(); - vfestopped = 0; - break; - - case VFE_STOP_CMD: - vfestopped = 1; - goto config_send; - - default: - break; + case CMD_GENERAL: + case CMD_STATS_DISABLE:{ + if (vfecmd->length > sizeof(buf)) { + cmd_data_alloc = + cmd_data = + kmalloc(vfecmd->length, GFP_ATOMIC); + if (!cmd_data) { + rc = -ENOMEM; + goto config_failure; + } + } else + cmd_data = buf; + + if (copy_from_user(cmd_data, + (void __user *)(vfecmd->value), + vfecmd->length)) { + + rc = -EFAULT; + goto config_done; } - } /* QDSP_CMDQUEUE */ - } - break; - case CMD_AXI_CFG_OUT1: { - axid = data; - if (!axid) { - rc = -EFAULT; - goto config_failure; + if (vfecmd->queue == QDSP_CMDQUEUE) { + switch (*(uint32_t *) cmd_data) { + case VFE_RESET_CMD: + msm_camio_vfe_blk_reset(); + msm_camio_camif_pad_reg_reset_2(); + vfestopped = 0; + break; + + case VFE_START_CMD: + msm_camio_camif_pad_reg_reset_2(); + vfestopped = 0; + break; + + case VFE_STOP_CMD: + vfestopped = 1; + goto config_send; + + default: + break; + } + } /* QDSP_CMDQUEUE */ } + break; - axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); - if (!axio) { - rc = -ENOMEM; - goto config_failure; - } + case CMD_AXI_CFG_OUT1:{ + axid = data; + if (!axid) { + rc = -EFAULT; + goto config_failure; + } - if (copy_from_user(axio, (void *)(vfecmd->value), - sizeof(struct axiout))) { - rc = -EFAULT; - goto config_done; - } + axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); + if (!axio) { + rc = -ENOMEM; + goto config_failure; + } - vfe_7x_config_axi(OUTPUT_1, axid, axio); + if (copy_from_user(axio, (void *)(vfecmd->value), + sizeof(struct axiout))) { + rc = -EFAULT; + goto config_done; + } - cmd_data = axio; - } + vfe_7x_config_axi(OUTPUT_1, axid, axio); + + cmd_data = axio; + } break; case CMD_AXI_CFG_OUT2: - case CMD_RAW_PICT_AXI_CFG: { - axid = data; - if (!axid) { - rc = -EFAULT; - goto config_failure; - } + case CMD_RAW_PICT_AXI_CFG:{ + axid = data; + if (!axid) { + rc = -EFAULT; + goto config_failure; + } - axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); - if (!axio) { - rc = -ENOMEM; - goto config_failure; - } + axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); + if (!axio) { + rc = -ENOMEM; + goto config_failure; + } - if (copy_from_user(axio, (void __user *)(vfecmd->value), - sizeof(struct axiout))) { - rc = -EFAULT; - goto config_done; - } + if (copy_from_user(axio, (void __user *)(vfecmd->value), + sizeof(struct axiout))) { + rc = -EFAULT; + goto config_done; + } - vfe_7x_config_axi(OUTPUT_2, axid, axio); - cmd_data = axio; - } + vfe_7x_config_axi(OUTPUT_2, axid, axio); + cmd_data = axio; + } break; - case CMD_AXI_CFG_SNAP_O1_AND_O2: { - axid = data; - if (!axid) { - rc = -EFAULT; - goto config_failure; - } + case CMD_AXI_CFG_SNAP_O1_AND_O2:{ + axid = data; + if (!axid) { + rc = -EFAULT; + goto config_failure; + } - axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); - if (!axio) { - rc = -ENOMEM; - goto config_failure; - } + axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); + if (!axio) { + rc = -ENOMEM; + goto config_failure; + } - if (copy_from_user(axio, (void __user *)(vfecmd->value), - sizeof(struct axiout))) { - rc = -EFAULT; - goto config_done; - } + if (copy_from_user(axio, (void __user *)(vfecmd->value), + sizeof(struct axiout))) { + rc = -EFAULT; + goto config_done; + } - vfe_7x_config_axi(OUTPUT_1_AND_2, axid, axio); + vfe_7x_config_axi(OUTPUT_1_AND_2, axid, axio); - cmd_data = axio; - } + cmd_data = axio; + } break; default: break; - } /* switch */ + } /* switch */ if (vfestopped) goto config_done; config_send: - CDBG("send adsp command = %d\n", *(uint32_t *)cmd_data); - rc = msm_adsp_write(vfe_mod, vfecmd->queue, - cmd_data, vfecmd->length); + CDBG("send adsp command = %d\n", *(uint32_t *) cmd_data); + rc = msm_adsp_write(vfe_mod, vfecmd->queue, cmd_data, vfecmd->length); config_done: if (cmd_data_alloc != NULL) @@ -693,9 +724,9 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data) { mutex_init(&vfe_lock); - fptr->vfe_init = vfe_7x_init; - fptr->vfe_enable = vfe_7x_enable; - fptr->vfe_config = vfe_7x_config; + fptr->vfe_init = vfe_7x_init; + fptr->vfe_enable = vfe_7x_enable; + fptr->vfe_config = vfe_7x_config; fptr->vfe_disable = vfe_7x_disable; fptr->vfe_release = vfe_7x_release; vfe_syncdata = data; diff --git a/drivers/media/video/msm/msm_vfe7x.h b/drivers/media/video/msm/msm_vfe7x.h index be3e9ad8f5248..5e86ce0ba9df4 100644 --- a/drivers/media/video/msm/msm_vfe7x.h +++ b/drivers/media/video/msm/msm_vfe7x.h @@ -1,255 +1,269 @@ -/* - * Copyright (C) 2008-2009 QUALCOMM Incorporated. +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ + #ifndef __MSM_VFE7X_H__ #define __MSM_VFE7X_H__ #include #include struct vfe_frame_extra { - uint32_t bl_evencol; - uint32_t bl_oddcol; - uint16_t g_def_p_cnt; - uint16_t r_b_def_p_cnt; + uint32_t bl_evencol; + uint32_t bl_oddcol; + uint16_t g_def_p_cnt; + uint16_t r_b_def_p_cnt; }; struct vfe_endframe { - uint32_t y_address; - uint32_t cbcr_address; + uint32_t y_address; + uint32_t cbcr_address; - unsigned int blacklevelevencolumn:23; - uint16_t reserved1:9; - unsigned int blackleveloddcolumn:23; - uint16_t reserved2:9; + unsigned int blacklevelevencolumn:23; + uint16_t reserved1:9; + unsigned int blackleveloddcolumn:23; + uint16_t reserved2:9; - uint16_t greendefectpixelcount:8; - uint16_t reserved3:8; - uint16_t redbluedefectpixelcount:8; - uint16_t reserved4:8; -} __attribute__((packed, aligned(4))); + uint16_t greendefectpixelcount:8; + uint16_t reserved3:8; + uint16_t redbluedefectpixelcount:8; + uint16_t reserved4:8; +} __attribute__ ((packed, aligned(4))); struct vfe_outputack { - uint32_t header; - void *output2newybufferaddress; - void *output2newcbcrbufferaddress; -} __attribute__((packed, aligned(4))); + uint32_t header; + void *output2newybufferaddress; + void *output2newcbcrbufferaddress; +} __attribute__ ((packed, aligned(4))); struct vfe_stats_ack { uint32_t header; /* MUST BE 64 bit ALIGNED */ - void *bufaddr; -} __attribute__((packed, aligned(4))); + void *bufaddr; +} __attribute__ ((packed, aligned(4))); /* AXI Output Config Command sent to DSP */ struct axiout { - uint32_t cmdheader:32; - int outputmode:3; - uint8_t format:2; - uint32_t /* reserved */ : 27; + uint32_t cmdheader:32; + int outputmode:3; + uint8_t format:2; + uint32_t /* reserved */ : 27; /* AXI Output 1 Y Configuration, Part 1 */ - uint32_t out1yimageheight:12; - uint32_t /* reserved */ : 4; - uint32_t out1yimagewidthin64bitwords:10; - uint32_t /* reserved */ : 6; + uint32_t out1yimageheight:12; + uint32_t /* reserved */ : 4; + uint32_t out1yimagewidthin64bitwords:10; + uint32_t /* reserved */ : 6; /* AXI Output 1 Y Configuration, Part 2 */ - uint8_t out1yburstlen:2; - uint32_t out1ynumrows:12; - uint32_t out1yrowincin64bitincs:12; - uint32_t /* reserved */ : 6; + uint8_t out1yburstlen:2; + uint32_t out1ynumrows:12; + uint32_t out1yrowincin64bitincs:12; + uint32_t /* reserved */ : 6; /* AXI Output 1 CbCr Configuration, Part 1 */ - uint32_t out1cbcrimageheight:12; - uint32_t /* reserved */ : 4; - uint32_t out1cbcrimagewidthin64bitwords:10; - uint32_t /* reserved */ : 6; + uint32_t out1cbcrimageheight:12; + uint32_t /* reserved */ : 4; + uint32_t out1cbcrimagewidthin64bitwords:10; + uint32_t /* reserved */ : 6; /* AXI Output 1 CbCr Configuration, Part 2 */ - uint8_t out1cbcrburstlen:2; - uint32_t out1cbcrnumrows:12; - uint32_t out1cbcrrowincin64bitincs:12; - uint32_t /* reserved */ : 6; + uint8_t out1cbcrburstlen:2; + uint32_t out1cbcrnumrows:12; + uint32_t out1cbcrrowincin64bitincs:12; + uint32_t /* reserved */ : 6; /* AXI Output 2 Y Configuration, Part 1 */ - uint32_t out2yimageheight:12; - uint32_t /* reserved */ : 4; - uint32_t out2yimagewidthin64bitwords:10; - uint32_t /* reserved */ : 6; + uint32_t out2yimageheight:12; + uint32_t /* reserved */ : 4; + uint32_t out2yimagewidthin64bitwords:10; + uint32_t /* reserved */ : 6; /* AXI Output 2 Y Configuration, Part 2 */ - uint8_t out2yburstlen:2; - uint32_t out2ynumrows:12; - uint32_t out2yrowincin64bitincs:12; - uint32_t /* reserved */ : 6; + uint8_t out2yburstlen:2; + uint32_t out2ynumrows:12; + uint32_t out2yrowincin64bitincs:12; + uint32_t /* reserved */ : 6; /* AXI Output 2 CbCr Configuration, Part 1 */ - uint32_t out2cbcrimageheight:12; - uint32_t /* reserved */ : 4; - uint32_t out2cbcrimagewidtein64bitwords:10; - uint32_t /* reserved */ : 6; + uint32_t out2cbcrimageheight:12; + uint32_t /* reserved */ : 4; + uint32_t out2cbcrimagewidtein64bitwords:10; + uint32_t /* reserved */ : 6; /* AXI Output 2 CbCr Configuration, Part 2 */ - uint8_t out2cbcrburstlen:2; - uint32_t out2cbcrnumrows:12; - uint32_t out2cbcrrowincin64bitincs:12; - uint32_t /* reserved */ : 6; + uint8_t out2cbcrburstlen:2; + uint32_t out2cbcrnumrows:12; + uint32_t out2cbcrrowincin64bitincs:12; + uint32_t /* reserved */ : 6; /* Address configuration: * output1 phisycal address */ - unsigned long output1buffer1_y_phy; - unsigned long output1buffer1_cbcr_phy; - unsigned long output1buffer2_y_phy; - unsigned long output1buffer2_cbcr_phy; - unsigned long output1buffer3_y_phy; - unsigned long output1buffer3_cbcr_phy; - unsigned long output1buffer4_y_phy; - unsigned long output1buffer4_cbcr_phy; - unsigned long output1buffer5_y_phy; - unsigned long output1buffer5_cbcr_phy; - unsigned long output1buffer6_y_phy; - unsigned long output1buffer6_cbcr_phy; - unsigned long output1buffer7_y_phy; - unsigned long output1buffer7_cbcr_phy; - unsigned long output1buffer8_y_phy; - unsigned long output1buffer8_cbcr_phy; + unsigned long output1buffer1_y_phy; + unsigned long output1buffer1_cbcr_phy; + unsigned long output1buffer2_y_phy; + unsigned long output1buffer2_cbcr_phy; + unsigned long output1buffer3_y_phy; + unsigned long output1buffer3_cbcr_phy; + unsigned long output1buffer4_y_phy; + unsigned long output1buffer4_cbcr_phy; + unsigned long output1buffer5_y_phy; + unsigned long output1buffer5_cbcr_phy; + unsigned long output1buffer6_y_phy; + unsigned long output1buffer6_cbcr_phy; + unsigned long output1buffer7_y_phy; + unsigned long output1buffer7_cbcr_phy; + unsigned long output1buffer8_y_phy; + unsigned long output1buffer8_cbcr_phy; /* output2 phisycal address */ - unsigned long output2buffer1_y_phy; - unsigned long output2buffer1_cbcr_phy; - unsigned long output2buffer2_y_phy; - unsigned long output2buffer2_cbcr_phy; - unsigned long output2buffer3_y_phy; - unsigned long output2buffer3_cbcr_phy; - unsigned long output2buffer4_y_phy; - unsigned long output2buffer4_cbcr_phy; - unsigned long output2buffer5_y_phy; - unsigned long output2buffer5_cbcr_phy; - unsigned long output2buffer6_y_phy; - unsigned long output2buffer6_cbcr_phy; - unsigned long output2buffer7_y_phy; - unsigned long output2buffer7_cbcr_phy; - unsigned long output2buffer8_y_phy; - unsigned long output2buffer8_cbcr_phy; -} __attribute__((packed, aligned(4))); + unsigned long output2buffer1_y_phy; + unsigned long output2buffer1_cbcr_phy; + unsigned long output2buffer2_y_phy; + unsigned long output2buffer2_cbcr_phy; + unsigned long output2buffer3_y_phy; + unsigned long output2buffer3_cbcr_phy; + unsigned long output2buffer4_y_phy; + unsigned long output2buffer4_cbcr_phy; + unsigned long output2buffer5_y_phy; + unsigned long output2buffer5_cbcr_phy; + unsigned long output2buffer6_y_phy; + unsigned long output2buffer6_cbcr_phy; + unsigned long output2buffer7_y_phy; + unsigned long output2buffer7_cbcr_phy; + unsigned long output2buffer8_y_phy; + unsigned long output2buffer8_cbcr_phy; +} __attribute__ ((packed, aligned(4))); struct vfe_stats_we_cfg { - uint32_t header; + uint32_t header; /* White Balance/Exposure Statistic Selection */ - uint8_t wb_expstatsenable:1; - uint8_t wb_expstatbuspriorityselection:1; - unsigned int wb_expstatbuspriorityvalue:4; - unsigned int /* reserved */ : 26; + uint8_t wb_expstatsenable:1; + uint8_t wb_expstatbuspriorityselection:1; + unsigned int wb_expstatbuspriorityvalue:4; + unsigned int /* reserved */ : 26; /* White Balance/Exposure Statistic Configuration, Part 1 */ - uint8_t exposurestatregions:1; - uint8_t exposurestatsubregions:1; - unsigned int /* reserved */ : 14; + uint8_t exposurestatregions:1; + uint8_t exposurestatsubregions:1; + unsigned int /* reserved */ : 14; - unsigned int whitebalanceminimumy:8; - unsigned int whitebalancemaximumy:8; + unsigned int whitebalanceminimumy:8; + unsigned int whitebalancemaximumy:8; /* White Balance/Exposure Statistic Configuration, Part 2 */ - uint8_t wb_expstatslopeofneutralregionline[ - NUM_WB_EXP_NEUTRAL_REGION_LINES]; + uint8_t + wb_expstatslopeofneutralregionline[NUM_WB_EXP_NEUTRAL_REGION_LINES]; /* White Balance/Exposure Statistic Configuration, Part 3 */ - unsigned int wb_expstatcrinterceptofneutralregionline2:12; - unsigned int /* reserved */ : 4; - unsigned int wb_expstatcbinterceptofneutralreginnline1:12; - unsigned int /* reserved */ : 4; + unsigned int wb_expstatcrinterceptofneutralregionline2:12; + unsigned int /* reserved */ : 4; + unsigned int wb_expstatcbinterceptofneutralreginnline1:12; + unsigned int /* reserved */ : 4; /* White Balance/Exposure Statistic Configuration, Part 4 */ - unsigned int wb_expstatcrinterceptofneutralregionline4:12; - unsigned int /* reserved */ : 4; - unsigned int wb_expstatcbinterceptofneutralregionline3:12; - unsigned int /* reserved */ : 4; + unsigned int wb_expstatcrinterceptofneutralregionline4:12; + unsigned int /* reserved */ : 4; + unsigned int wb_expstatcbinterceptofneutralregionline3:12; + unsigned int /* reserved */ : 4; /* White Balance/Exposure Statistic Output Buffer Header */ - unsigned int wb_expmetricheaderpattern:8; - unsigned int /* reserved */ : 24; + unsigned int wb_expmetricheaderpattern:8; + unsigned int /* reserved */ : 24; /* White Balance/Exposure Statistic Output Buffers-MUST - * BE 64 bit ALIGNED */ - void *wb_expstatoutputbuffer[NUM_WB_EXP_STAT_OUTPUT_BUFFERS]; -} __attribute__((packed, aligned(4))); + * BE 64 bit ALIGNED */ + void *wb_expstatoutputbuffer[NUM_WB_EXP_STAT_OUTPUT_BUFFERS]; +} __attribute__ ((packed, aligned(4))); struct vfe_stats_af_cfg { uint32_t header; /* Autofocus Statistic Selection */ - uint8_t af_enable:1; - uint8_t af_busprioritysel:1; - unsigned int af_buspriorityval:4; - unsigned int /* reserved */ : 26; + uint8_t af_enable:1; + uint8_t af_busprioritysel:1; + unsigned int af_buspriorityval:4; + unsigned int /* reserved */ : 26; /* Autofocus Statistic Configuration, Part 1 */ - unsigned int af_singlewinvoffset:12; - unsigned int /* reserved */ : 4; - unsigned int af_singlewinhoffset:12; - unsigned int /* reserved */ : 3; - uint8_t af_winmode:1; + unsigned int af_singlewinvoffset:12; + unsigned int /* reserved */ : 4; + unsigned int af_singlewinhoffset:12; + unsigned int /* reserved */ : 3; + uint8_t af_winmode:1; /* Autofocus Statistic Configuration, Part 2 */ - unsigned int af_singglewinvh:11; - unsigned int /* reserved */ : 5; - unsigned int af_singlewinhw:11; - unsigned int /* reserved */ : 5; + unsigned int af_singglewinvh:11; + unsigned int /* reserved */ : 5; + unsigned int af_singlewinhw:11; + unsigned int /* reserved */ : 5; /* Autofocus Statistic Configuration, Parts 3-6 */ - uint8_t af_multiwingrid[NUM_AUTOFOCUS_MULTI_WINDOW_GRIDS]; + uint8_t af_multiwingrid[NUM_AUTOFOCUS_MULTI_WINDOW_GRIDS]; /* Autofocus Statistic Configuration, Part 7 */ - signed int af_metrichpfcoefa00:5; - signed int af_metrichpfcoefa04:5; - unsigned int af_metricmaxval:11; - uint8_t af_metricsel:1; - unsigned int /* reserved */ : 10; + signed int af_metrichpfcoefa00:5; + signed int af_metrichpfcoefa04:5; + unsigned int af_metricmaxval:11; + uint8_t af_metricsel:1; + unsigned int /* reserved */ : 10; /* Autofocus Statistic Configuration, Part 8 */ - signed int af_metrichpfcoefa20:5; - signed int af_metrichpfcoefa21:5; - signed int af_metrichpfcoefa22:5; - signed int af_metrichpfcoefa23:5; - signed int af_metrichpfcoefa24:5; - unsigned int /* reserved */ : 7; + signed int af_metrichpfcoefa20:5; + signed int af_metrichpfcoefa21:5; + signed int af_metrichpfcoefa22:5; + signed int af_metrichpfcoefa23:5; + signed int af_metrichpfcoefa24:5; + unsigned int /* reserved */ : 7; /* Autofocus Statistic Output Buffer Header */ - unsigned int af_metrichp:8; - unsigned int /* reserved */ : 24; + unsigned int af_metrichp:8; + unsigned int /* reserved */ : 24; /* Autofocus Statistic Output Buffers - MUST BE 64 bit ALIGNED!!! */ void *af_outbuf[NUM_AF_STAT_OUTPUT_BUFFERS]; -} __attribute__((packed, aligned(4))); /* VFE_StatsAutofocusConfigCmdType */ +} __attribute__ ((packed, aligned(4))); /* VFE_StatsAutofocusConfigCmdType */ struct msm_camera_frame_msg { - unsigned long output_y_address; - unsigned long output_cbcr_address; + unsigned long output_y_address; + unsigned long output_cbcr_address; - unsigned int blacklevelevenColumn:23; - uint16_t reserved1:9; - unsigned int blackleveloddColumn:23; - uint16_t reserved2:9; + unsigned int blacklevelevenColumn:23; + uint16_t reserved1:9; + unsigned int blackleveloddColumn:23; + uint16_t reserved2:9; - uint16_t greendefectpixelcount:8; - uint16_t reserved3:8; - uint16_t redbluedefectpixelcount:8; - uint16_t reserved4:8; -} __attribute__((packed, aligned(4))); + uint16_t greendefectpixelcount:8; + uint16_t reserved3:8; + uint16_t redbluedefectpixelcount:8; + uint16_t reserved4:8; +} __attribute__ ((packed, aligned(4))); /* New one for 7k */ struct msm_vfe_command_7k { uint16_t queue; uint16_t length; - void *value; + void *value; }; struct stop_event { - wait_queue_head_t wait; + wait_queue_head_t wait; int state; - int timeout; + int timeout; }; - #endif /* __MSM_VFE7X_H__ */ diff --git a/drivers/media/video/msm/msm_vfe8x.c b/drivers/media/video/msm/msm_vfe8x.c index e34aaeb7e2643..267d99d0fb92c 100644 --- a/drivers/media/video/msm/msm_vfe8x.c +++ b/drivers/media/video/msm/msm_vfe8x.c @@ -1,47 +1,88 @@ -/* - * Copyright (C) 2008-2009 QUALCOMM Incorporated. +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ + #include #include #include #include +#include #include "msm_vfe8x_proc.h" #define ON 1 #define OFF 0 -struct mutex vfe_lock; -static void *vfe_syncdata; +static void *vfe_syncdata; +static struct clk *ebi1_clk; +static const char *const clk_name = "ebi1_clk"; static int vfe_enable(struct camera_enable_cmd *enable) { - int rc = 0; - return rc; + return 0; } static int vfe_disable(struct camera_enable_cmd *enable, - struct platform_device *dev) + struct platform_device *dev) +{ + vfe_stop(); + msm_camio_disable(dev); + return 0; +} + +static int vfe_init(struct msm_vfe_callback *presp, struct platform_device *dev) { int rc = 0; - vfe_stop(); + ebi1_clk = clk_get(NULL, clk_name); + if (!ebi1_clk) { + pr_err("%s: could not get %s\n", __func__, clk_name); + return -EIO; + } - msm_camio_disable(dev); - return rc; + rc = clk_set_rate(ebi1_clk, 128000000); + if (rc < 0) { + pr_err("%s: clk_set_rate(%s) failed: %d\n", __func__, + clk_name, rc); + return rc; + } + + rc = vfe_cmd_init(presp, dev, vfe_syncdata); + if (rc < 0) + return rc; + + /* Bring up all the required GPIOs and Clocks */ + return msm_camio_enable(dev); } static void vfe_release(struct platform_device *dev) { + if (ebi1_clk) { + clk_set_rate(ebi1_clk, 0); + clk_put(ebi1_clk); + ebi1_clk = 0; + } msm_camio_disable(dev); vfe_cmd_release(dev); - - mutex_lock(&vfe_lock); vfe_syncdata = NULL; - mutex_unlock(&vfe_lock); } static void vfe_config_axi(int mode, - struct axidata *ad, struct vfe_cmd_axi_output_config *ao) + struct axidata *ad, + struct vfe_cmd_axi_output_config *ao) { struct msm_pmem_region *regptr; int i, j; @@ -49,14 +90,12 @@ static void vfe_config_axi(int mode, if (mode == OUTPUT_1 || mode == OUTPUT_1_AND_2) { regptr = ad->region; - for (i = 0; - i < ad->bufnum1; i++) { + for (i = 0; i < ad->bufnum1; i++) { p1 = &(ao->output1.outputY.outFragments[i][0]); p2 = &(ao->output1.outputCbcr.outFragments[i][0]); - for (j = 0; - j < ao->output1.fragmentCount; j++) { + for (j = 0; j < ao->output1.fragmentCount; j++) { *p1 = regptr->paddr + regptr->info.y_off; p1++; @@ -66,24 +105,23 @@ static void vfe_config_axi(int mode, } regptr++; } - } /* if OUTPUT1 or Both */ - + } + /* if OUTPUT1 or Both */ if (mode == OUTPUT_2 || mode == OUTPUT_1_AND_2) { regptr = &(ad->region[ad->bufnum1]); CDBG("bufnum2 = %d\n", ad->bufnum2); - for (i = 0; - i < ad->bufnum2; i++) { + for (i = 0; i < ad->bufnum2; i++) { p1 = &(ao->output2.outputY.outFragments[i][0]); p2 = &(ao->output2.outputCbcr.outFragments[i][0]); - CDBG("config_axi: O2, phy = 0x%lx, y_off = %d, cbcr_off = %d\n", - regptr->paddr, regptr->info.y_off, regptr->info.cbcr_off); + CDBG("config_axi: O2, phy = 0x%lx, y_off = %d, "\ + "cbcr_off = %d\n", regptr->paddr, + regptr->info.y_off, regptr->info.cbcr_off); - for (j = 0; - j < ao->output2.fragmentCount; j++) { + for (j = 0; j < ao->output2.fragmentCount; j++) { *p1 = regptr->paddr + regptr->info.y_off; CDBG("vfe_config_axi: p1 = 0x%x\n", *p1); @@ -98,11 +136,31 @@ static void vfe_config_axi(int mode, } } +#define ERR_COPY_FROM_USER() \ + pr_err("%s(%d): copy from user\n", __func__, __LINE__) + +#define CHECKED_COPY_FROM_USER(in) { \ + if (cmd->length != sizeof(*(in))) { \ + pr_err("msm_camera: %s:%d cmd %d: user data size %d " \ + "!= kernel data size %d\n", \ + __func__, __LINE__, \ + cmd->id, cmd->length, sizeof(*(in))); \ + rc = -EIO; \ + break; \ + } \ + if (copy_from_user((in), (void __user *)cmd->value, \ + sizeof(*(in)))) { \ + ERR_COPY_FROM_USER(); \ + rc = -EFAULT; \ + break; \ + } \ +} + static int vfe_proc_general(struct msm_vfe_command_8k *cmd) { int rc = 0; - CDBG("vfe_proc_general: cmdID = %d\n", cmd->id); + CDBG("%s: cmdID = %d\n", __func__, cmd->id); switch (cmd->id) { case VFE_CMD_ID_RESET: @@ -111,330 +169,282 @@ static int vfe_proc_general(struct msm_vfe_command_8k *cmd) vfe_reset(); break; - case VFE_CMD_ID_START: { - struct vfe_cmd_start start; - if (copy_from_user(&start, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_START:{ + struct vfe_cmd_start start; + CHECKED_COPY_FROM_USER(&start); - /* msm_camio_camif_pad_reg_reset_2(); */ - msm_camio_camif_pad_reg_reset(); - vfe_start(&start); - } + /* msm_camio_camif_pad_reg_reset_2(); */ + msm_camio_camif_pad_reg_reset(); + vfe_start(&start); + } break; - case VFE_CMD_ID_CAMIF_CONFIG: { - struct vfe_cmd_camif_config camif; - if (copy_from_user(&camif, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_CAMIF_CONFIG:{ + struct vfe_cmd_camif_config camif; + CHECKED_COPY_FROM_USER(&camif); - vfe_camif_config(&camif); - } + vfe_camif_config(&camif); + } break; - case VFE_CMD_ID_BLACK_LEVEL_CONFIG: { - struct vfe_cmd_black_level_config bl; - if (copy_from_user(&bl, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_BLACK_LEVEL_CONFIG:{ + struct vfe_cmd_black_level_config bl; + CHECKED_COPY_FROM_USER(&bl); - vfe_black_level_config(&bl); - } + vfe_black_level_config(&bl); + } break; - case VFE_CMD_ID_ROLL_OFF_CONFIG: { - struct vfe_cmd_roll_off_config rolloff; - if (copy_from_user(&rolloff, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; - - vfe_roll_off_config(&rolloff); - } + case VFE_CMD_ID_ROLL_OFF_CONFIG:{ + /* rolloff is too big to be on the stack */ + struct vfe_cmd_roll_off_config *rolloff = + kmalloc(sizeof(struct vfe_cmd_roll_off_config), + GFP_KERNEL); + if (!rolloff) { + pr_err("%s: out of memory\n", __func__); + rc = -ENOMEM; + break; + } + /* Wrap CHECKED_COPY_FROM_USER() in a do-while(0) loop + * to make sure we free rolloff when copy_from_user() + * fails. + */ + do { + CHECKED_COPY_FROM_USER(rolloff); + vfe_roll_off_config(rolloff); + } while (0); + kfree(rolloff); + } break; - case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_CONFIG: { - struct vfe_cmd_demux_channel_gain_config demuxc; - if (copy_from_user(&demuxc, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_CONFIG:{ + struct vfe_cmd_demux_channel_gain_config demuxc; + CHECKED_COPY_FROM_USER(&demuxc); - /* demux is always enabled. */ - vfe_demux_channel_gain_config(&demuxc); - } + /* demux is always enabled. */ + vfe_demux_channel_gain_config(&demuxc); + } break; - case VFE_CMD_ID_DEMOSAIC_CONFIG: { - struct vfe_cmd_demosaic_config demosaic; - if (copy_from_user(&demosaic, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_DEMOSAIC_CONFIG:{ + struct vfe_cmd_demosaic_config demosaic; + CHECKED_COPY_FROM_USER(&demosaic); - vfe_demosaic_config(&demosaic); - } + vfe_demosaic_config(&demosaic); + } break; case VFE_CMD_ID_FOV_CROP_CONFIG: - case VFE_CMD_ID_FOV_CROP_UPDATE: { - struct vfe_cmd_fov_crop_config fov; - if (copy_from_user(&fov, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_FOV_CROP_UPDATE:{ + struct vfe_cmd_fov_crop_config fov; + CHECKED_COPY_FROM_USER(&fov); - vfe_fov_crop_config(&fov); - } + vfe_fov_crop_config(&fov); + } break; case VFE_CMD_ID_MAIN_SCALER_CONFIG: - case VFE_CMD_ID_MAIN_SCALER_UPDATE: { - struct vfe_cmd_main_scaler_config mainds; - if (copy_from_user(&mainds, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_MAIN_SCALER_UPDATE:{ + struct vfe_cmd_main_scaler_config mainds; + CHECKED_COPY_FROM_USER(&mainds); - vfe_main_scaler_config(&mainds); - } + vfe_main_scaler_config(&mainds); + } break; case VFE_CMD_ID_WHITE_BALANCE_CONFIG: - case VFE_CMD_ID_WHITE_BALANCE_UPDATE: { - struct vfe_cmd_white_balance_config wb; - if (copy_from_user(&wb, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_WHITE_BALANCE_UPDATE:{ + struct vfe_cmd_white_balance_config wb; + CHECKED_COPY_FROM_USER(&wb); - vfe_white_balance_config(&wb); - } + vfe_white_balance_config(&wb); + } break; case VFE_CMD_ID_COLOR_CORRECTION_CONFIG: - case VFE_CMD_ID_COLOR_CORRECTION_UPDATE: { - struct vfe_cmd_color_correction_config cc; - if (copy_from_user(&cc, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_COLOR_CORRECTION_UPDATE:{ + struct vfe_cmd_color_correction_config cc; + CHECKED_COPY_FROM_USER(&cc); - vfe_color_correction_config(&cc); - } + vfe_color_correction_config(&cc); + } break; - case VFE_CMD_ID_LA_CONFIG: { - struct vfe_cmd_la_config la; - if (copy_from_user(&la, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_LA_CONFIG:{ + struct vfe_cmd_la_config la; + CHECKED_COPY_FROM_USER(&la); - vfe_la_config(&la); - } + vfe_la_config(&la); + } break; - case VFE_CMD_ID_RGB_GAMMA_CONFIG: { - struct vfe_cmd_rgb_gamma_config rgb; - if (copy_from_user(&rgb, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_RGB_GAMMA_CONFIG:{ + struct vfe_cmd_rgb_gamma_config rgb; + CHECKED_COPY_FROM_USER(&rgb); - rc = vfe_rgb_gamma_config(&rgb); - } + rc = vfe_rgb_gamma_config(&rgb); + } break; case VFE_CMD_ID_CHROMA_ENHAN_CONFIG: - case VFE_CMD_ID_CHROMA_ENHAN_UPDATE: { - struct vfe_cmd_chroma_enhan_config chrom; - if (copy_from_user(&chrom, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_CHROMA_ENHAN_UPDATE:{ + struct vfe_cmd_chroma_enhan_config chrom; + CHECKED_COPY_FROM_USER(&chrom); - vfe_chroma_enhan_config(&chrom); - } + vfe_chroma_enhan_config(&chrom); + } break; case VFE_CMD_ID_CHROMA_SUPPRESSION_CONFIG: - case VFE_CMD_ID_CHROMA_SUPPRESSION_UPDATE: { - struct vfe_cmd_chroma_suppression_config chromsup; - if (copy_from_user(&chromsup, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_CHROMA_SUPPRESSION_UPDATE:{ + struct vfe_cmd_chroma_suppression_config chromsup; + CHECKED_COPY_FROM_USER(&chromsup); - vfe_chroma_sup_config(&chromsup); - } + vfe_chroma_sup_config(&chromsup); + } break; - case VFE_CMD_ID_ASF_CONFIG: { - struct vfe_cmd_asf_config asf; - if (copy_from_user(&asf, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_ASF_CONFIG:{ + struct vfe_cmd_asf_config asf; + CHECKED_COPY_FROM_USER(&asf); - vfe_asf_config(&asf); - } + vfe_asf_config(&asf); + } break; case VFE_CMD_ID_SCALER2Y_CONFIG: - case VFE_CMD_ID_SCALER2Y_UPDATE: { - struct vfe_cmd_scaler2_config ds2y; - if (copy_from_user(&ds2y, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_SCALER2Y_UPDATE:{ + struct vfe_cmd_scaler2_config ds2y; + CHECKED_COPY_FROM_USER(&ds2y); - vfe_scaler2y_config(&ds2y); - } + vfe_scaler2y_config(&ds2y); + } break; case VFE_CMD_ID_SCALER2CbCr_CONFIG: - case VFE_CMD_ID_SCALER2CbCr_UPDATE: { - struct vfe_cmd_scaler2_config ds2cbcr; - if (copy_from_user(&ds2cbcr, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_SCALER2CbCr_UPDATE:{ + struct vfe_cmd_scaler2_config ds2cbcr; + CHECKED_COPY_FROM_USER(&ds2cbcr); - vfe_scaler2cbcr_config(&ds2cbcr); - } + vfe_scaler2cbcr_config(&ds2cbcr); + } break; - case VFE_CMD_ID_CHROMA_SUBSAMPLE_CONFIG: { - struct vfe_cmd_chroma_subsample_config sub; - if (copy_from_user(&sub, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_CHROMA_SUBSAMPLE_CONFIG:{ + struct vfe_cmd_chroma_subsample_config sub; + CHECKED_COPY_FROM_USER(&sub); - vfe_chroma_subsample_config(&sub); - } + vfe_chroma_subsample_config(&sub); + } break; - case VFE_CMD_ID_FRAME_SKIP_CONFIG: { - struct vfe_cmd_frame_skip_config fskip; - if (copy_from_user(&fskip, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_FRAME_SKIP_CONFIG:{ + struct vfe_cmd_frame_skip_config fskip; + CHECKED_COPY_FROM_USER(&fskip); - vfe_frame_skip_config(&fskip); - } + vfe_frame_skip_config(&fskip); + } break; - case VFE_CMD_ID_OUTPUT_CLAMP_CONFIG: { - struct vfe_cmd_output_clamp_config clamp; - if (copy_from_user(&clamp, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_OUTPUT_CLAMP_CONFIG:{ + struct vfe_cmd_output_clamp_config clamp; + CHECKED_COPY_FROM_USER(&clamp); - vfe_output_clamp_config(&clamp); - } + vfe_output_clamp_config(&clamp); + } break; - /* module update commands */ - case VFE_CMD_ID_BLACK_LEVEL_UPDATE: { - struct vfe_cmd_black_level_config blk; - if (copy_from_user(&blk, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + /* module update commands */ + case VFE_CMD_ID_BLACK_LEVEL_UPDATE:{ + struct vfe_cmd_black_level_config blk; + CHECKED_COPY_FROM_USER(&blk); - vfe_black_level_update(&blk); - } + vfe_black_level_update(&blk); + } break; - case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_UPDATE: { - struct vfe_cmd_demux_channel_gain_config dmu; - if (copy_from_user(&dmu, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_DEMUX_CHANNEL_GAIN_UPDATE:{ + struct vfe_cmd_demux_channel_gain_config dmu; + CHECKED_COPY_FROM_USER(&dmu); - vfe_demux_channel_gain_update(&dmu); - } + vfe_demux_channel_gain_update(&dmu); + } break; - case VFE_CMD_ID_DEMOSAIC_BPC_UPDATE: { - struct vfe_cmd_demosaic_bpc_update demo_bpc; - if (copy_from_user(&demo_bpc, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_DEMOSAIC_BPC_UPDATE:{ + struct vfe_cmd_demosaic_bpc_update demo_bpc; + CHECKED_COPY_FROM_USER(&demo_bpc); - vfe_demosaic_bpc_update(&demo_bpc); - } + vfe_demosaic_bpc_update(&demo_bpc); + } break; - case VFE_CMD_ID_DEMOSAIC_ABF_UPDATE: { - struct vfe_cmd_demosaic_abf_update demo_abf; - if (copy_from_user(&demo_abf, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_DEMOSAIC_ABF_UPDATE:{ + struct vfe_cmd_demosaic_abf_update demo_abf; + CHECKED_COPY_FROM_USER(&demo_abf); - vfe_demosaic_abf_update(&demo_abf); - } + vfe_demosaic_abf_update(&demo_abf); + } break; - case VFE_CMD_ID_LA_UPDATE: { - struct vfe_cmd_la_config la; - if (copy_from_user(&la, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_LA_UPDATE:{ + struct vfe_cmd_la_config la; + CHECKED_COPY_FROM_USER(&la); - vfe_la_update(&la); - } + vfe_la_update(&la); + } break; - case VFE_CMD_ID_RGB_GAMMA_UPDATE: { - struct vfe_cmd_rgb_gamma_config rgb; - if (copy_from_user(&rgb, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_RGB_GAMMA_UPDATE:{ + struct vfe_cmd_rgb_gamma_config rgb; + CHECKED_COPY_FROM_USER(&rgb); - rc = vfe_rgb_gamma_update(&rgb); - } + rc = vfe_rgb_gamma_update(&rgb); + } break; - case VFE_CMD_ID_ASF_UPDATE: { - struct vfe_cmd_asf_update asf; - if (copy_from_user(&asf, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_ASF_UPDATE:{ + struct vfe_cmd_asf_update asf; + CHECKED_COPY_FROM_USER(&asf); - vfe_asf_update(&asf); - } + vfe_asf_update(&asf); + } break; - case VFE_CMD_ID_FRAME_SKIP_UPDATE: { - struct vfe_cmd_frame_skip_update fskip; - if (copy_from_user(&fskip, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_FRAME_SKIP_UPDATE:{ + struct vfe_cmd_frame_skip_update fskip; + CHECKED_COPY_FROM_USER(&fskip); - vfe_frame_skip_update(&fskip); - } + vfe_frame_skip_update(&fskip); + } break; - case VFE_CMD_ID_CAMIF_FRAME_UPDATE: { - struct vfe_cmds_camif_frame fup; - if (copy_from_user(&fup, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_CAMIF_FRAME_UPDATE:{ + struct vfe_cmds_camif_frame fup; + CHECKED_COPY_FROM_USER(&fup); - vfe_camif_frame_update(&fup); - } + vfe_camif_frame_update(&fup); + } break; - /* stats update commands */ - case VFE_CMD_ID_STATS_AUTOFOCUS_UPDATE: { - struct vfe_cmd_stats_af_update afup; - if (copy_from_user(&afup, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + /* stats update commands */ + case VFE_CMD_ID_STATS_AUTOFOCUS_UPDATE:{ + struct vfe_cmd_stats_af_update afup; + CHECKED_COPY_FROM_USER(&afup); - vfe_stats_update_af(&afup); - } + vfe_stats_update_af(&afup); + } break; - case VFE_CMD_ID_STATS_WB_EXP_UPDATE: { - struct vfe_cmd_stats_wb_exp_update wbexp; - if (copy_from_user(&wbexp, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_STATS_WB_EXP_UPDATE:{ + struct vfe_cmd_stats_wb_exp_update wbexp; + CHECKED_COPY_FROM_USER(&wbexp); - vfe_stats_update_wb_exp(&wbexp); - } + vfe_stats_update_wb_exp(&wbexp); + } break; - /* control of start, stop, update, etc... */ + /* control of start, stop, update, etc... */ case VFE_CMD_ID_STOP: vfe_stop(); break; @@ -442,39 +452,33 @@ static int vfe_proc_general(struct msm_vfe_command_8k *cmd) case VFE_CMD_ID_GET_HW_VERSION: break; - /* stats */ - case VFE_CMD_ID_STATS_SETTING: { - struct vfe_cmd_stats_setting stats; - if (copy_from_user(&stats, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + /* stats */ + case VFE_CMD_ID_STATS_SETTING:{ + struct vfe_cmd_stats_setting stats; + CHECKED_COPY_FROM_USER(&stats); - vfe_stats_setting(&stats); - } + vfe_stats_setting(&stats); + } break; - case VFE_CMD_ID_STATS_AUTOFOCUS_START: { - struct vfe_cmd_stats_af_start af; - if (copy_from_user(&af, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_STATS_AUTOFOCUS_START:{ + struct vfe_cmd_stats_af_start af; + CHECKED_COPY_FROM_USER(&af); - vfe_stats_start_af(&af); - } + vfe_stats_start_af(&af); + } break; case VFE_CMD_ID_STATS_AUTOFOCUS_STOP: vfe_stats_af_stop(); break; - case VFE_CMD_ID_STATS_WB_EXP_START: { - struct vfe_cmd_stats_wb_exp_start awexp; - if (copy_from_user(&awexp, - (void __user *) cmd->value, cmd->length)) - rc = -EFAULT; + case VFE_CMD_ID_STATS_WB_EXP_START:{ + struct vfe_cmd_stats_wb_exp_start awexp; + CHECKED_COPY_FROM_USER(&awexp); - vfe_stats_start_wb_exp(&awexp); - } + vfe_stats_start_wb_exp(&awexp); + } break; case VFE_CMD_ID_STATS_WB_EXP_STOP: @@ -488,10 +492,17 @@ static int vfe_proc_general(struct msm_vfe_command_8k *cmd) vfe_update(); break; - /* test gen */ + /* test gen */ case VFE_CMD_ID_TEST_GEN_START: break; + case VFE_CMD_ID_EPOCH1_CONFIG:{ + struct vfe_cmds_camif_epoch epoch1; + CHECKED_COPY_FROM_USER(&epoch1); + vfe_epoch1_config(&epoch1); + } + break; + /* acknowledge from upper layer these are not in general command. @@ -511,8 +522,10 @@ static int vfe_proc_general(struct msm_vfe_command_8k *cmd) */ default: + pr_err("%s: invalid cmd id %d\n", __func__, cmd->id); + rc = -EINVAL; break; - } /* switch */ + } /* switch */ return rc; } @@ -521,25 +534,22 @@ static int vfe_config(struct msm_vfe_cfg_cmd *cmd, void *data) { struct msm_pmem_region *regptr; struct msm_vfe_command_8k vfecmd; + struct vfe_cmd_axi_output_config axio; + struct axidata *axid = data; - uint32_t i; - - void *cmd_data = NULL; - long rc = 0; - - struct vfe_cmd_axi_output_config *axio = NULL; - struct vfe_cmd_stats_setting *scfg = NULL; + int rc = 0; if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE && - cmd->cmd_type != CMD_STATS_BUF_RELEASE) { - + cmd->cmd_type != CMD_STATS_BUF_RELEASE && + cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) { if (copy_from_user(&vfecmd, - (void __user *)(cmd->value), - sizeof(struct msm_vfe_command_8k))) + (void __user *)(cmd->value), sizeof(vfecmd))) { + ERR_COPY_FROM_USER(); return -EFAULT; + } } - CDBG("vfe_config: cmdType = %d\n", cmd->cmd_type); + CDBG("%s: cmdType = %d\n", __func__, cmd->cmd_type); switch (cmd->cmd_type) { case CMD_GENERAL: @@ -548,209 +558,161 @@ static int vfe_config(struct msm_vfe_cfg_cmd *cmd, void *data) case CMD_STATS_ENABLE: case CMD_STATS_AXI_CFG: { - struct axidata *axid; - - axid = data; - if (!axid) - return -EFAULT; - - scfg = - kmalloc(sizeof(struct vfe_cmd_stats_setting), - GFP_ATOMIC); - if (!scfg) - return -ENOMEM; - - if (copy_from_user(scfg, - (void __user *)(vfecmd.value), - vfecmd.length)) { + int i; + struct vfe_cmd_stats_setting scfg; + + BUG_ON(!axid); + + if (vfecmd.length != sizeof(scfg)) { + pr_err + ("msm_camera: %s: cmd %d: user-space "\ + "data size %d != kernel data size %d\n", + __func__, + cmd->cmd_type, vfecmd.length, + sizeof(scfg)); + return -EIO; + } - kfree(scfg); - return -EFAULT; - } + if (copy_from_user(&scfg, + (void __user *)(vfecmd.value), + sizeof(scfg))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } - regptr = axid->region; - if (axid->bufnum1 > 0) { - for (i = 0; i < axid->bufnum1; i++) { - scfg->awbBuffer[i] = - (uint32_t)(regptr->paddr); - regptr++; + regptr = axid->region; + if (axid->bufnum1 > 0) { + for (i = 0; i < axid->bufnum1; i++) { + scfg.awbBuffer[i] = + (uint32_t) (regptr->paddr); + regptr++; + } } - } - if (axid->bufnum2 > 0) { - for (i = 0; i < axid->bufnum2; i++) { - scfg->afBuffer[i] = - (uint32_t)(regptr->paddr); - regptr++; + if (axid->bufnum2 > 0) { + for (i = 0; i < axid->bufnum2; i++) { + scfg.afBuffer[i] = + (uint32_t) (regptr->paddr); + regptr++; + } } - } - vfe_stats_config(scfg); - } + vfe_stats_setting(&scfg); + } break; - case CMD_STATS_AF_AXI_CFG: { - } + case CMD_STATS_AF_AXI_CFG: break; case CMD_FRAME_BUF_RELEASE: { - /* preview buffer release */ - struct msm_frame *b; - unsigned long p; - struct vfe_cmd_output_ack fack; + /* preview buffer release */ + struct msm_frame *b; + unsigned long p; + struct vfe_cmd_output_ack fack; - if (!data) - return -EFAULT; + BUG_ON(!data); - b = (struct msm_frame *)(cmd->value); - p = *(unsigned long *)data; + b = (struct msm_frame *)(cmd->value); + p = *(unsigned long *)data; - b->path = MSM_FRAME_ENC; + b->path = MSM_FRAME_ENC; - fack.ybufaddr[0] = - (uint32_t)(p + b->y_off); + fack.ybufaddr[0] = (uint32_t) (p + b->y_off); - fack.chromabufaddr[0] = - (uint32_t)(p + b->cbcr_off); + fack.chromabufaddr[0] = (uint32_t) (p + b->cbcr_off); - if (b->path == MSM_FRAME_PREV_1) - vfe_output1_ack(&fack); + if (b->path == MSM_FRAME_PREV_1) + vfe_output1_ack(&fack); - if (b->path == MSM_FRAME_ENC || - b->path == MSM_FRAME_PREV_2) - vfe_output2_ack(&fack); - } + if (b->path == MSM_FRAME_ENC || + b->path == MSM_FRAME_PREV_2) + vfe_output2_ack(&fack); + } break; - case CMD_SNAP_BUF_RELEASE: { - } + case CMD_SNAP_BUF_RELEASE: break; case CMD_STATS_BUF_RELEASE: { - struct vfe_cmd_stats_wb_exp_ack sack; + struct vfe_cmd_stats_wb_exp_ack sack; - if (!data) - return -EFAULT; + BUG_ON(!data); - sack.nextWbExpOutputBufferAddr = *(uint32_t *)data; - vfe_stats_wb_exp_ack(&sack); - } + sack.nextWbExpOutputBufferAddr = *(uint32_t *) data; + vfe_stats_wb_exp_ack(&sack); + } + break; + + case CMD_STATS_AF_BUF_RELEASE: { + struct vfe_cmd_stats_af_ack ack; + + BUG_ON(!data); + + ack.nextAFOutputBufferAddr = *(uint32_t *) data; + vfe_stats_af_ack(&ack); + } break; case CMD_AXI_CFG_OUT1: { - struct axidata *axid; - axid = data; - if (!axid) - return -EFAULT; + BUG_ON(!axid); - axio = - kmalloc(sizeof(struct vfe_cmd_axi_output_config), - GFP_ATOMIC); - if (!axio) - return -ENOMEM; + if (copy_from_user(&axio, (void __user *)(vfecmd.value), + sizeof(axio))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } - if (copy_from_user(axio, (void __user *)(vfecmd.value), - sizeof(struct vfe_cmd_axi_output_config))) { - kfree(axio); - return -EFAULT; + vfe_config_axi(OUTPUT_1, axid, &axio); + vfe_axi_output_config(&axio); } - - vfe_config_axi(OUTPUT_1, axid, axio); - vfe_axi_output_config(axio); - } break; case CMD_AXI_CFG_OUT2: case CMD_RAW_PICT_AXI_CFG: { - struct axidata *axid; - - axid = data; - if (!axid) - return -EFAULT; - axio = - kmalloc(sizeof(struct vfe_cmd_axi_output_config), - GFP_ATOMIC); - if (!axio) - return -ENOMEM; + BUG_ON(!axid); - if (copy_from_user(axio, (void __user *)(vfecmd.value), - sizeof(struct vfe_cmd_axi_output_config))) { - kfree(axio); - return -EFAULT; - } + if (copy_from_user(&axio, (void __user *)(vfecmd.value), + sizeof(axio))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } - vfe_config_axi(OUTPUT_2, axid, axio); + vfe_config_axi(OUTPUT_2, axid, &axio); - axio->outputDataSize = 0; - vfe_axi_output_config(axio); - } + axio.outputDataSize = 0; + vfe_axi_output_config(&axio); + } break; - case CMD_AXI_CFG_SNAP_O1_AND_O2: { - struct axidata *axid; - axid = data; - if (!axid) - return -EFAULT; + case CMD_AXI_CFG_SNAP_O1_AND_O2:{ - axio = - kmalloc(sizeof(struct vfe_cmd_axi_output_config), - GFP_ATOMIC); - if (!axio) - return -ENOMEM; + BUG_ON(!axid); - if (copy_from_user(axio, (void __user *)(vfecmd.value), - sizeof(struct vfe_cmd_axi_output_config))) { - kfree(axio); - return -EFAULT; - } + if (copy_from_user(&axio, (void __user *)(vfecmd.value), + sizeof(axio))) { + ERR_COPY_FROM_USER(); + return -EFAULT; + } - vfe_config_axi(OUTPUT_1_AND_2, - axid, axio); - vfe_axi_output_config(axio); - cmd_data = axio; - } + vfe_config_axi(OUTPUT_1_AND_2, axid, &axio); + vfe_axi_output_config(&axio); + } break; default: break; - } /* switch */ - - kfree(scfg); + } /* switch */ - kfree(axio); - -/* - if (cmd->length > 256 && - cmd_data && - (cmd->cmd_type == CMD_GENERAL || - cmd->cmd_type == CMD_STATS_DISABLE)) { - kfree(cmd_data); - } -*/ return rc; } -static int vfe_init(struct msm_vfe_callback *presp, - struct platform_device *dev) -{ - int rc = 0; - - rc = vfe_cmd_init(presp, dev, vfe_syncdata); - if (rc < 0) - return rc; - - /* Bring up all the required GPIOs and Clocks */ - return msm_camio_enable(dev); -} - void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data) { - mutex_init(&vfe_lock); - fptr->vfe_init = vfe_init; - fptr->vfe_enable = vfe_enable; - fptr->vfe_config = vfe_config; + fptr->vfe_init = vfe_init; + fptr->vfe_enable = vfe_enable; + fptr->vfe_config = vfe_config; fptr->vfe_disable = vfe_disable; fptr->vfe_release = vfe_release; vfe_syncdata = data; diff --git a/drivers/media/video/msm/msm_vfe8x.h b/drivers/media/video/msm/msm_vfe8x.h index 28a70a9e5ed7a..09d5875d4d1f5 100644 --- a/drivers/media/video/msm/msm_vfe8x.h +++ b/drivers/media/video/msm/msm_vfe8x.h @@ -1,6 +1,21 @@ -/* - * Copyright (C) 2008-2009 QUALCOMM Incorporated. +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ + #ifndef __MSM_VFE8X_H__ #define __MSM_VFE8X_H__ @@ -8,15 +23,15 @@ #define FALSE 0 #define boolean uint8_t -enum VFE_STATE { +enum VFE_STATE { VFE_STATE_IDLE, VFE_STATE_ACTIVE }; enum vfe_cmd_id { /* - *Important! Command_ID are arranged in order. - *Don't change!*/ + *Important! Command_ID are arranged in order. + *Don't change!*/ VFE_CMD_ID_START, VFE_CMD_ID_RESET, @@ -82,7 +97,7 @@ enum vfe_cmd_id { VFE_CMD_ID_STATS_WB_EXP_UPDATE, /* control of start, stop, update, etc... */ - VFE_CMD_ID_STOP, + VFE_CMD_ID_STOP, VFE_CMD_ID_GET_HW_VERSION, /* stats */ @@ -94,6 +109,9 @@ enum vfe_cmd_id { VFE_CMD_ID_ASYNC_TIMER_SETTING, + /* epoch1 */ + VFE_CMD_ID_EPOCH1_CONFIG, + /* max id */ VFE_CMD_ID_MAX }; @@ -163,14 +181,14 @@ struct vfe_cmds_camif_subsample { }; struct vfe_cmds_camif_epoch { - uint8_t enable; + uint8_t enable; uint16_t lineindex; }; struct vfe_cmds_camif_cfg { - enum VFE_CAMIF_SYNC_EDGE vSyncEdge; - enum VFE_CAMIF_SYNC_EDGE hSyncEdge; - enum VFE_CAMIF_SYNC_MODE syncMode; + enum VFE_CAMIF_SYNC_EDGE vSyncEdge; + enum VFE_CAMIF_SYNC_EDGE hSyncEdge; + enum VFE_CAMIF_SYNC_MODE syncMode; uint8_t vfeSubSampleEnable; uint8_t busSubSampleEnable; uint8_t irqSubSampleEnable; @@ -181,11 +199,11 @@ struct vfe_cmds_camif_cfg { struct vfe_cmd_camif_config { struct vfe_cmds_camif_cfg camifConfig; struct vfe_cmds_camif_efs EFS; - struct vfe_cmds_camif_frame frame; - struct vfe_cmds_camif_window window; + struct vfe_cmds_camif_frame frame; + struct vfe_cmds_camif_window window; struct vfe_cmds_camif_subsample subsample; - struct vfe_cmds_camif_epoch epoch1; - struct vfe_cmds_camif_epoch epoch2; + struct vfe_cmds_camif_epoch epoch1; + struct vfe_cmds_camif_epoch epoch2; }; enum VFE_AXI_OUTPUT_MODE { @@ -221,7 +239,7 @@ struct vfe_cmds_axi_out_per_component { uint16_t outRowCount; uint16_t outRowIncrement; uint32_t outFragments[VFE_AXI_OUTPUT_CFG_FRAME_COUNT] - [VFE_MAX_NUM_FRAGMENTS_PER_FRAME]; + [VFE_MAX_NUM_FRAGMENTS_PER_FRAME]; }; struct vfe_cmds_axi_per_output_path { @@ -231,9 +249,9 @@ struct vfe_cmds_axi_per_output_path { }; enum VFE_AXI_BURST_LENGTH { - VFE_AXI_BURST_LENGTH_IS_2 = 2, - VFE_AXI_BURST_LENGTH_IS_4 = 4, - VFE_AXI_BURST_LENGTH_IS_8 = 8, + VFE_AXI_BURST_LENGTH_IS_2 = 2, + VFE_AXI_BURST_LENGTH_IS_4 = 4, + VFE_AXI_BURST_LENGTH_IS_8 = 8, VFE_AXI_BURST_LENGTH_IS_16 = 16 }; @@ -259,17 +277,17 @@ struct vfe_cmds_main_scaler_stripe_init { }; struct vfe_cmds_scaler_one_dimension { - uint8_t enable; + uint8_t enable; uint16_t inputSize; uint16_t outputSize; uint32_t phaseMultiplicationFactor; - uint8_t interpolationResolution; + uint8_t interpolationResolution; }; struct vfe_cmd_main_scaler_config { uint8_t enable; - struct vfe_cmds_scaler_one_dimension hconfig; - struct vfe_cmds_scaler_one_dimension vconfig; + struct vfe_cmds_scaler_one_dimension hconfig; + struct vfe_cmds_scaler_one_dimension vconfig; struct vfe_cmds_main_scaler_stripe_init MNInitH; struct vfe_cmds_main_scaler_stripe_init MNInitV; }; @@ -354,7 +372,7 @@ enum VFE_YUV_INPUT_COSITING_MODE { struct vfe_cmd_start { enum VFE_START_INPUT_SOURCE inputSource; enum VFE_START_OPERATION_MODE operationMode; - uint8_t snapshotCount; + uint8_t snapshotCount; enum VFE_START_PIXEL_PATTERN pixel; enum VFE_YUV_INPUT_COSITING_MODE yuvInputCositingMode; }; @@ -369,33 +387,33 @@ struct vfe_cmd_output_ack { struct vfe_cmd_stats_setting { uint16_t frameHDimension; uint16_t frameVDimension; - uint8_t afBusPrioritySelection; - uint8_t afBusPriority; - uint8_t awbBusPrioritySelection; - uint8_t awbBusPriority; - uint8_t histBusPrioritySelection; - uint8_t histBusPriority; + uint8_t afBusPrioritySelection; + uint8_t afBusPriority; + uint8_t awbBusPrioritySelection; + uint8_t awbBusPriority; + uint8_t histBusPrioritySelection; + uint8_t histBusPriority; uint32_t afBuffer[VFE_STATS_BUFFER_COUNT]; uint32_t awbBuffer[VFE_STATS_BUFFER_COUNT]; uint32_t histBuffer[VFE_STATS_BUFFER_COUNT]; }; struct vfe_cmd_stats_af_start { - uint8_t enable; - uint8_t windowMode; + uint8_t enable; + uint8_t windowMode; uint16_t windowHOffset; uint16_t windowVOffset; uint16_t windowWidth; uint16_t windowHeight; - uint8_t gridForMultiWindows[16]; - uint8_t metricSelection; - int16_t metricMax; - int8_t highPassCoef[7]; - int8_t bufferHeader; + uint8_t gridForMultiWindows[16]; + uint8_t metricSelection; + int16_t metricMax; + int8_t highPassCoef[7]; + int8_t bufferHeader; }; struct vfe_cmd_stats_af_update { - uint8_t windowMode; + uint8_t windowMode; uint16_t windowHOffset; uint16_t windowVOffset; uint16_t windowWidth; @@ -403,22 +421,22 @@ struct vfe_cmd_stats_af_update { }; struct vfe_cmd_stats_wb_exp_start { - uint8_t enable; - uint8_t wbExpRegions; - uint8_t wbExpSubRegion; - uint8_t awbYMin; - uint8_t awbYMax; - int8_t awbMCFG[4]; - int16_t awbCCFG[4]; - int8_t axwHeader; + uint8_t enable; + uint8_t wbExpRegions; + uint8_t wbExpSubRegion; + uint8_t awbYMin; + uint8_t awbYMax; + int8_t awbMCFG[4]; + int16_t awbCCFG[4]; + int8_t axwHeader; }; struct vfe_cmd_stats_wb_exp_update { uint8_t wbExpRegions; uint8_t wbExpSubRegion; - int8_t awbYMin; - int8_t awbYMax; - int8_t awbMCFG[4]; + int8_t awbYMin; + int8_t awbYMax; + int8_t awbMCFG[4]; int16_t awbCCFG[4]; }; @@ -427,11 +445,11 @@ struct vfe_cmd_stats_af_ack { }; struct vfe_cmd_stats_wb_exp_ack { - uint32_t nextWbExpOutputBufferAddr; + uint32_t nextWbExpOutputBufferAddr; }; struct vfe_cmd_black_level_config { - uint8_t enable; + uint8_t enable; uint16_t evenEvenAdjustment; uint16_t evenOddAdjustment; uint16_t oddEvenAdjustment; @@ -444,12 +462,12 @@ struct vfe_cmd_black_level_config { #define VFE_ROLL_OFF_DELTA_TABLE_SIZE 208 struct vfe_cmd_roll_off_config { - uint8_t enable; + uint8_t enable; uint16_t gridWidth; uint16_t gridHeight; - uint16_t yDelta; - uint8_t gridXIndex; - uint8_t gridYIndex; + uint16_t yDelta; + uint8_t gridXIndex; + uint8_t gridYIndex; uint16_t gridPixelXIndex; uint16_t gridPixelYIndex; uint16_t yDeltaAccum; @@ -457,10 +475,10 @@ struct vfe_cmd_roll_off_config { uint16_t initTableGr[VFE_ROLL_OFF_INIT_TABLE_SIZE]; uint16_t initTableB[VFE_ROLL_OFF_INIT_TABLE_SIZE]; uint16_t initTableGb[VFE_ROLL_OFF_INIT_TABLE_SIZE]; - int16_t deltaTableR[VFE_ROLL_OFF_DELTA_TABLE_SIZE]; - int16_t deltaTableGr[VFE_ROLL_OFF_DELTA_TABLE_SIZE]; - int16_t deltaTableB[VFE_ROLL_OFF_DELTA_TABLE_SIZE]; - int16_t deltaTableGb[VFE_ROLL_OFF_DELTA_TABLE_SIZE]; + int16_t deltaTableR[VFE_ROLL_OFF_DELTA_TABLE_SIZE]; + int16_t deltaTableGr[VFE_ROLL_OFF_DELTA_TABLE_SIZE]; + int16_t deltaTableB[VFE_ROLL_OFF_DELTA_TABLE_SIZE]; + int16_t deltaTableGb[VFE_ROLL_OFF_DELTA_TABLE_SIZE]; }; struct vfe_cmd_demux_channel_gain_config { @@ -471,27 +489,27 @@ struct vfe_cmd_demux_channel_gain_config { }; struct vfe_cmds_demosaic_abf { - uint8_t enable; - uint8_t forceOn; - uint8_t shift; - uint16_t lpThreshold; - uint16_t max; - uint16_t min; - uint8_t ratio; + uint8_t enable; + uint8_t forceOn; + uint8_t shift; + uint16_t lpThreshold; + uint16_t max; + uint16_t min; + uint8_t ratio; }; struct vfe_cmds_demosaic_bpc { - uint8_t enable; - uint16_t fmaxThreshold; - uint16_t fminThreshold; - uint16_t redDiffThreshold; - uint16_t blueDiffThreshold; - uint16_t greenDiffThreshold; + uint8_t enable; + uint16_t fmaxThreshold; + uint16_t fminThreshold; + uint16_t redDiffThreshold; + uint16_t blueDiffThreshold; + uint16_t greenDiffThreshold; }; struct vfe_cmd_demosaic_config { - uint8_t enable; - uint8_t slopeShift; + uint8_t enable; + uint8_t slopeShift; struct vfe_cmds_demosaic_abf abfConfig; struct vfe_cmds_demosaic_bpc bpcConfig; }; @@ -505,7 +523,7 @@ struct vfe_cmd_demosaic_abf_update { }; struct vfe_cmd_white_balance_config { - uint8_t enable; + uint8_t enable; uint16_t ch2Gain; uint16_t ch1Gain; uint16_t ch0Gain; @@ -519,20 +537,20 @@ enum VFE_COLOR_CORRECTION_COEF_QFACTOR { }; struct vfe_cmd_color_correction_config { - uint8_t enable; + uint8_t enable; enum VFE_COLOR_CORRECTION_COEF_QFACTOR coefQFactor; - int16_t C0; - int16_t C1; - int16_t C2; - int16_t C3; - int16_t C4; - int16_t C5; - int16_t C6; - int16_t C7; - int16_t C8; - int16_t K0; - int16_t K1; - int16_t K2; + int16_t C0; + int16_t C1; + int16_t C2; + int16_t C3; + int16_t C4; + int16_t C5; + int16_t C6; + int16_t C7; + int16_t C8; + int16_t K0; + int16_t K1; + int16_t K2; }; #define VFE_LA_TABLE_LENGTH 256 @@ -559,7 +577,7 @@ struct vfe_cmd_rgb_gamma_config { }; struct vfe_cmd_chroma_enhan_config { - uint8_t enable; + uint8_t enable; int16_t am; int16_t ap; int16_t bm; @@ -602,7 +620,7 @@ struct vfe_cmd_asf_config { int8_t sharpThreshE5; int8_t filter1Coefficients[9]; int8_t filter2Coefficients[9]; - uint8_t cropEnable; + uint8_t cropEnable; uint16_t cropFirstPixel; uint16_t cropLastPixel; uint16_t cropFirstLine; @@ -619,12 +637,12 @@ struct vfe_cmd_asf_update { uint8_t sharpK1; uint8_t sharpK2; uint8_t sharpThreshE1; - int8_t sharpThreshE2; - int8_t sharpThreshE3; - int8_t sharpThreshE4; - int8_t sharpThreshE5; - int8_t filter1Coefficients[9]; - int8_t filter2Coefficients[9]; + int8_t sharpThreshE2; + int8_t sharpThreshE3; + int8_t sharpThreshE4; + int8_t sharpThreshE5; + int8_t filter1Coefficients[9]; + int8_t filter2Coefficients[9]; uint8_t cropEnable; }; @@ -636,8 +654,8 @@ enum VFE_TEST_GEN_SYNC_EDGE { struct vfe_cmd_test_gen_start { uint8_t pixelDataSelect; uint8_t systematicDataSelect; - enum VFE_TEST_GEN_SYNC_EDGE hsyncEdge; - enum VFE_TEST_GEN_SYNC_EDGE vsyncEdge; + enum VFE_TEST_GEN_SYNC_EDGE hsyncEdge; + enum VFE_TEST_GEN_SYNC_EDGE vsyncEdge; uint16_t numFrame; enum VFE_RAW_PIXEL_DATA_SIZE pixelDataSize; uint16_t imageWidth; @@ -647,15 +665,15 @@ struct vfe_cmd_test_gen_start { uint16_t startOfLineOffset; uint16_t endOfLineNOffset; uint16_t hbi; - uint8_t vblEnable; + uint8_t vblEnable; uint16_t vbl; - uint8_t startOfFrameDummyLine; - uint8_t endOfFrameDummyLine; - uint8_t unicolorBarEnable; - uint8_t colorBarsSplitEnable; - uint8_t unicolorBarSelect; - enum VFE_START_PIXEL_PATTERN colorBarsPixelPattern; - uint8_t colorBarsRotatePeriod; + uint8_t startOfFrameDummyLine; + uint8_t endOfFrameDummyLine; + uint8_t unicolorBarEnable; + uint8_t colorBarsSplitEnable; + uint8_t unicolorBarSelect; + enum VFE_START_PIXEL_PATTERN colorBarsPixelPattern; + uint8_t colorBarsRotatePeriod; uint16_t testGenRandomSeed; }; @@ -671,9 +689,9 @@ struct vfe_cmd_camif_frame_update { }; struct vfe_cmd_sync_timer_setting { - uint8_t whichSyncTimer; - uint8_t operation; - uint8_t polarity; + uint8_t whichSyncTimer; + uint8_t operation; + uint8_t polarity; uint16_t repeatCount; uint16_t hsyncCount; uint32_t pclkCount; @@ -681,18 +699,18 @@ struct vfe_cmd_sync_timer_setting { }; struct vfe_cmd_async_timer_setting { - uint8_t whichAsyncTimer; - uint8_t operation; - uint8_t polarity; + uint8_t whichAsyncTimer; + uint8_t operation; + uint8_t polarity; uint16_t repeatCount; uint16_t inactiveCount; uint32_t activeCount; }; -struct vfe_frame_skip_counts { - uint32_t totalFrameCount; - uint32_t output1Count; - uint32_t output2Count; +struct vfe_frame_skip_counts { + uint32_t totalFrameCount; + uint32_t output1Count; + uint32_t output2Count; }; enum VFE_AXI_RD_UNPACK_HBI_SEL { @@ -707,39 +725,39 @@ enum VFE_AXI_RD_UNPACK_HBI_SEL { }; struct vfe_cmd_axi_input_config { - uint32_t fragAddr[4]; - uint8_t totalFragmentCount; - uint16_t ySize; - uint16_t xOffset; - uint16_t xSize; - uint16_t rowIncrement; - uint16_t numOfRows; + uint32_t fragAddr[4]; + uint8_t totalFragmentCount; + uint16_t ySize; + uint16_t xOffset; + uint16_t xSize; + uint16_t rowIncrement; + uint16_t numOfRows; enum VFE_AXI_BURST_LENGTH burstLength; - uint8_t unpackPhase; + uint8_t unpackPhase; enum VFE_AXI_RD_UNPACK_HBI_SEL unpackHbi; - enum VFE_RAW_PIXEL_DATA_SIZE pixelSize; - uint8_t padRepeatCountLeft; - uint8_t padRepeatCountRight; - uint8_t padRepeatCountTop; - uint8_t padRepeatCountBottom; - uint8_t padLeftComponentSelectCycle0; - uint8_t padLeftComponentSelectCycle1; - uint8_t padLeftComponentSelectCycle2; - uint8_t padLeftComponentSelectCycle3; - uint8_t padLeftStopCycle0; - uint8_t padLeftStopCycle1; - uint8_t padLeftStopCycle2; - uint8_t padLeftStopCycle3; - uint8_t padRightComponentSelectCycle0; - uint8_t padRightComponentSelectCycle1; - uint8_t padRightComponentSelectCycle2; - uint8_t padRightComponentSelectCycle3; - uint8_t padRightStopCycle0; - uint8_t padRightStopCycle1; - uint8_t padRightStopCycle2; - uint8_t padRightStopCycle3; - uint8_t padTopLineCount; - uint8_t padBottomLineCount; + enum VFE_RAW_PIXEL_DATA_SIZE pixelSize; + uint8_t padRepeatCountLeft; + uint8_t padRepeatCountRight; + uint8_t padRepeatCountTop; + uint8_t padRepeatCountBottom; + uint8_t padLeftComponentSelectCycle0; + uint8_t padLeftComponentSelectCycle1; + uint8_t padLeftComponentSelectCycle2; + uint8_t padLeftComponentSelectCycle3; + uint8_t padLeftStopCycle0; + uint8_t padLeftStopCycle1; + uint8_t padLeftStopCycle2; + uint8_t padLeftStopCycle3; + uint8_t padRightComponentSelectCycle0; + uint8_t padRightComponentSelectCycle1; + uint8_t padRightComponentSelectCycle2; + uint8_t padRightComponentSelectCycle3; + uint8_t padRightStopCycle0; + uint8_t padRightStopCycle1; + uint8_t padRightStopCycle2; + uint8_t padRightStopCycle3; + uint8_t padTopLineCount; + uint8_t padBottomLineCount; }; struct vfe_interrupt_status { @@ -813,8 +831,8 @@ enum VFE_MESSAGE_ID { }; struct vfe_msg_stats_autofocus { - uint32_t afBuffer; - uint32_t frameCounter; + uint32_t afBuffer; + uint32_t frameCounter; }; struct vfe_msg_stats_wb_exp { @@ -828,12 +846,12 @@ struct vfe_frame_bpc_info { }; struct vfe_frame_asf_info { - uint32_t asfMaxEdge; - uint32_t asfHbiCount; + uint32_t asfMaxEdge; + uint32_t asfHbiCount; }; struct vfe_msg_camif_status { - uint8_t camifState; + uint8_t camifState; uint32_t pixelCount; uint32_t lineCount; }; @@ -851,45 +869,45 @@ struct vfe_bus_performance_monitor { }; struct vfe_irq_thread_msg { - uint32_t vfeIrqStatus; - uint32_t camifStatus; - uint32_t demosaicStatus; - uint32_t asfMaxEdge; + uint32_t vfeIrqStatus; + uint32_t camifStatus; + uint32_t demosaicStatus; + uint32_t asfMaxEdge; struct vfe_bus_performance_monitor pmInfo; }; struct vfe_msg_output { - uint32_t yBuffer; - uint32_t cbcrBuffer; + uint32_t yBuffer; + uint32_t cbcrBuffer; struct vfe_frame_bpc_info bpcInfo; struct vfe_frame_asf_info asfInfo; - uint32_t frameCounter; + uint32_t frameCounter; struct vfe_bus_pm_per_path pmData; }; struct vfe_message { enum VFE_MESSAGE_ID _d; union { - struct vfe_msg_output msgOutput1; - struct vfe_msg_output msgOutput2; - struct vfe_msg_stats_autofocus msgStatsAf; - struct vfe_msg_stats_wb_exp msgStatsWbExp; - struct vfe_msg_camif_status msgCamifError; + struct vfe_msg_output msgOutput1; + struct vfe_msg_output msgOutput2; + struct vfe_msg_stats_autofocus msgStatsAf; + struct vfe_msg_stats_wb_exp msgStatsWbExp; + struct vfe_msg_camif_status msgCamifError; struct vfe_bus_performance_monitor msgBusOverflow; - } _u; + } _u; }; /* New one for 8k */ struct msm_vfe_command_8k { - int32_t id; + int id; uint16_t length; - void *value; + void *value; }; struct vfe_frame_extra { struct vfe_frame_bpc_info bpcInfo; struct vfe_frame_asf_info asfInfo; - uint32_t frameCounter; + uint32_t frameCounter; struct vfe_bus_pm_per_path pmData; }; #endif /* __MSM_VFE8X_H__ */ diff --git a/drivers/media/video/msm/msm_vfe8x_proc.c b/drivers/media/video/msm/msm_vfe8x_proc.c index bb65013402113..1edbacba0ae65 100644 --- a/drivers/media/video/msm/msm_vfe8x_proc.c +++ b/drivers/media/video/msm/msm_vfe8x_proc.c @@ -1,6 +1,21 @@ -/* -* Copyright (C) 2008-2009 QUALCOMM Incorporated. -*/ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + #include #include #include @@ -10,6 +25,16 @@ #include #include "msm_vfe8x_proc.h" #include +#include + +struct isr_queue_cmd { + struct list_head list; + struct vfe_interrupt_status vfeInterruptStatus; + struct vfe_frame_asf_info vfeAsfFrameInfo; + struct vfe_frame_bpc_info vfeBpcFrameInfo; + struct vfe_msg_camif_status vfeCamifStatusLocal; + struct vfe_bus_performance_monitor vfePmData; +}; struct msm_vfe8x_ctrl { /* bit 1:0 ENC_IRQ_MASK = 0x11: @@ -19,165 +44,114 @@ struct msm_vfe8x_ctrl { * generate IRQ when both y and cbcr frame is ready. */ struct vfe_irq_composite_mask_config vfeIrqCompositeMaskLocal; struct vfe_module_enable vfeModuleEnableLocal; - struct vfe_camif_cfg_data vfeCamifConfigLocal; - struct vfe_interrupt_mask vfeImaskLocal; - struct vfe_stats_cmd_data vfeStatsCmdLocal; - struct vfe_bus_cfg_data vfeBusConfigLocal; + struct vfe_camif_cfg_data vfeCamifConfigLocal; + struct vfe_cmds_camif_epoch vfeCamifEpoch1Local; + struct vfe_interrupt_mask vfeImaskLocal; + struct vfe_stats_cmd_data vfeStatsCmdLocal; + struct vfe_bus_cfg_data vfeBusConfigLocal; struct vfe_cmd_bus_pm_start vfeBusPmConfigLocal; - struct vfe_bus_cmd_data vfeBusCmdLocal; - enum vfe_interrupt_name vfeInterruptNameLocal; + struct vfe_bus_cmd_data vfeBusCmdLocal; + enum vfe_interrupt_name vfeInterruptNameLocal; uint32_t vfeLaBankSel; - struct vfe_gamma_lut_sel vfeGammaLutSel; + struct vfe_gamma_lut_sel vfeGammaLutSel; boolean vfeStartAckPendingFlag; boolean vfeStopAckPending; boolean vfeResetAckPending; boolean vfeUpdateAckPending; - enum VFE_AXI_OUTPUT_MODE axiOutputMode; - enum VFE_START_OPERATION_MODE vfeOperationMode; + enum VFE_AXI_OUTPUT_MODE axiOutputMode; + enum VFE_START_OPERATION_MODE vfeOperationMode; - uint32_t vfeSnapShotCount; - uint32_t vfeRequestedSnapShotCount; - boolean vfeStatsPingPongReloadFlag; - uint32_t vfeFrameId; + uint32_t vfeSnapShotCount; + uint32_t vfeRequestedSnapShotCount; + boolean vfeStatsPingPongReloadFlag; + uint32_t vfeFrameId; struct vfe_cmd_frame_skip_config vfeFrameSkip; uint32_t vfeFrameSkipPattern; - uint8_t vfeFrameSkipCount; - uint8_t vfeFrameSkipPeriod; + uint8_t vfeFrameSkipCount; + uint8_t vfeFrameSkipPeriod; - boolean vfeTestGenStartFlag; + boolean vfeTestGenStartFlag; uint32_t vfeImaskPacked; uint32_t vfeImaskCompositePacked; - enum VFE_RAW_PIXEL_DATA_SIZE axiInputDataSize; - struct vfe_irq_thread_msg vfeIrqThreadMsgLocal; + enum VFE_RAW_PIXEL_DATA_SIZE axiInputDataSize; + struct vfe_irq_thread_msg vfeIrqThreadMsgLocal; - struct vfe_output_path_combo viewPath; - struct vfe_output_path_combo encPath; + struct vfe_output_path_combo viewPath; + struct vfe_output_path_combo encPath; struct vfe_frame_skip_counts vfeDroppedFrameCounts; struct vfe_stats_control afStatsControl; struct vfe_stats_control awbStatsControl; - enum VFE_STATE vstate; - - spinlock_t ack_lock; - spinlock_t state_lock; - spinlock_t io_lock; + enum VFE_STATE vstate; struct msm_vfe_callback *resp; - uint32_t extlen; - void *extdata; + struct vfe_frame_extra extdata; - spinlock_t tasklet_lock; - struct list_head tasklet_q; + struct isr_queue_cmd irqs[5]; + spinlock_t irqs_lock; + int irq_get; + int irq_put; int vfeirq; void __iomem *vfebase; void *syncdata; }; -static struct msm_vfe8x_ctrl *ctrl; -static irqreturn_t vfe_parse_irq(int irq_num, void *data); -struct isr_queue_cmd { - struct list_head list; - struct vfe_interrupt_status vfeInterruptStatus; - struct vfe_frame_asf_info vfeAsfFrameInfo; - struct vfe_frame_bpc_info vfeBpcFrameInfo; - struct vfe_msg_camif_status vfeCamifStatusLocal; - struct vfe_bus_performance_monitor vfePmData; -}; +static struct msm_vfe8x_ctrl *ctrl; -static void vfe_prog_hw(uint8_t *hwreg, - uint32_t *inptr, uint32_t regcnt) +static void vfe_prog_hw(uint8_t *hwreg, uint32_t *inptr, uint32_t regcnt) { /* unsigned long flags; */ uint32_t i; uint32_t *p; - /* @todo This is causing issues, need further investigate */ - /* spin_lock_irqsave(&ctrl->io_lock, flags); */ - - p = (uint32_t *)(hwreg); + p = (uint32_t *) (hwreg); for (i = 0; i < (regcnt >> 2); i++) writel(*inptr++, p++); - /* *p++ = *inptr++; */ - - /* spin_unlock_irqrestore(&ctrl->io_lock, flags); */ -} - -static void vfe_read_reg_values(uint8_t *hwreg, - uint32_t *dest, uint32_t count) -{ - /* unsigned long flags; */ - uint32_t *temp; - uint32_t i; - - /* @todo This is causing issues, need further investigate */ - /* spin_lock_irqsave(&ctrl->io_lock, flags); */ - - temp = (uint32_t *)(hwreg); - for (i = 0; i < count; i++) - *dest++ = *temp++; - - /* spin_unlock_irqrestore(&ctrl->io_lock, flags); */ -} - -static struct vfe_irqenable vfe_read_irq_mask(void) -{ - /* unsigned long flags; */ - uint32_t *temp; - struct vfe_irqenable rc; - - memset(&rc, 0, sizeof(rc)); - - /* @todo This is causing issues, need further investigate */ - /* spin_lock_irqsave(&ctrl->io_lock, flags); */ - temp = (uint32_t *)(ctrl->vfebase + VFE_IRQ_MASK); - - rc = *((struct vfe_irqenable *)temp); - /* spin_unlock_irqrestore(&ctrl->io_lock, flags); */ - - return rc; + /* *p++ = *inptr++; */ } static void vfe_set_bus_pipo_addr(struct vfe_output_path_combo *vpath, - struct vfe_output_path_combo *epath) + struct vfe_output_path_combo *epath) { vpath->yPath.hwRegPingAddress = (uint8_t *) - (ctrl->vfebase + VFE_BUS_VIEW_Y_WR_PING_ADDR); + (ctrl->vfebase + VFE_BUS_VIEW_Y_WR_PING_ADDR); vpath->yPath.hwRegPongAddress = (uint8_t *) - (ctrl->vfebase + VFE_BUS_VIEW_Y_WR_PONG_ADDR); + (ctrl->vfebase + VFE_BUS_VIEW_Y_WR_PONG_ADDR); vpath->cbcrPath.hwRegPingAddress = (uint8_t *) - (ctrl->vfebase + VFE_BUS_VIEW_CBCR_WR_PING_ADDR); + (ctrl->vfebase + VFE_BUS_VIEW_CBCR_WR_PING_ADDR); vpath->cbcrPath.hwRegPongAddress = (uint8_t *) - (ctrl->vfebase + VFE_BUS_VIEW_CBCR_WR_PONG_ADDR); + (ctrl->vfebase + VFE_BUS_VIEW_CBCR_WR_PONG_ADDR); epath->yPath.hwRegPingAddress = (uint8_t *) - (ctrl->vfebase + VFE_BUS_ENC_Y_WR_PING_ADDR); + (ctrl->vfebase + VFE_BUS_ENC_Y_WR_PING_ADDR); epath->yPath.hwRegPongAddress = (uint8_t *) - (ctrl->vfebase + VFE_BUS_ENC_Y_WR_PONG_ADDR); + (ctrl->vfebase + VFE_BUS_ENC_Y_WR_PONG_ADDR); epath->cbcrPath.hwRegPingAddress = (uint8_t *) - (ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PING_ADDR); + (ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PING_ADDR); epath->cbcrPath.hwRegPongAddress = (uint8_t *) - (ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PONG_ADDR); + (ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PONG_ADDR); } static void vfe_axi_output(struct vfe_cmd_axi_output_config *in, - struct vfe_output_path_combo *out1, - struct vfe_output_path_combo *out2, uint16_t out) + struct vfe_output_path_combo *out1, + struct vfe_output_path_combo *out2, uint16_t out) { struct vfe_axi_out_cfg cmd; uint16_t temp; uint32_t burstLength; + memset(&cmd, 0, sizeof(cmd)); /* force it to burst length 4, hardware does not support it. */ burstLength = 1; - /* AXI Output 2 Y Configuration*/ + /* AXI Output 2 Y Configuration */ /* VFE_BUS_ENC_Y_WR_PING_ADDR */ cmd.out2YPingAddr = out2->yPath.addressBuffer[0]; @@ -188,18 +162,16 @@ static void vfe_axi_output(struct vfe_cmd_axi_output_config *in, cmd.out2YImageHeight = in->output2.outputY.imageHeight; /* convert the image width and row increment to be in * unit of 64bit (8 bytes) */ - temp = (in->output2.outputY.imageWidth + (out - 1)) / - out; + temp = (in->output2.outputY.imageWidth + (out - 1)) / out; cmd.out2YImageWidthin64bit = temp; /* VFE_BUS_ENC_Y_WR_BUFFER_CFG */ cmd.out2YBurstLength = burstLength; cmd.out2YNumRows = in->output2.outputY.outRowCount; - temp = (in->output2.outputY.outRowIncrement + (out - 1)) / - out; + temp = (in->output2.outputY.outRowIncrement + (out - 1)) / out; cmd.out2YRowIncrementIn64bit = temp; - /* AXI Output 2 Cbcr Configuration*/ + /* AXI Output 2 Cbcr Configuration */ /* VFE_BUS_ENC_Cbcr_WR_PING_ADDR */ cmd.out2CbcrPingAddr = out2->cbcrPath.addressBuffer[0]; @@ -208,15 +180,13 @@ static void vfe_axi_output(struct vfe_cmd_axi_output_config *in, /* VFE_BUS_ENC_Cbcr_WR_IMAGE_SIZE */ cmd.out2CbcrImageHeight = in->output2.outputCbcr.imageHeight; - temp = (in->output2.outputCbcr.imageWidth + (out - 1)) / - out; + temp = (in->output2.outputCbcr.imageWidth + (out - 1)) / out; cmd.out2CbcrImageWidthIn64bit = temp; /* VFE_BUS_ENC_Cbcr_WR_BUFFER_CFG */ cmd.out2CbcrBurstLength = burstLength; cmd.out2CbcrNumRows = in->output2.outputCbcr.outRowCount; - temp = (in->output2.outputCbcr.outRowIncrement + (out - 1)) / - out; + temp = (in->output2.outputCbcr.outRowIncrement + (out - 1)) / out; cmd.out2CbcrRowIncrementIn64bit = temp; /* AXI Output 1 Y Configuration */ @@ -228,55 +198,49 @@ static void vfe_axi_output(struct vfe_cmd_axi_output_config *in, /* VFE_BUS_VIEW_Y_WR_IMAGE_SIZE */ cmd.out1YImageHeight = in->output1.outputY.imageHeight; - temp = (in->output1.outputY.imageWidth + (out - 1)) / - out; + temp = (in->output1.outputY.imageWidth + (out - 1)) / out; cmd.out1YImageWidthin64bit = temp; /* VFE_BUS_VIEW_Y_WR_BUFFER_CFG */ cmd.out1YBurstLength = burstLength; cmd.out1YNumRows = in->output1.outputY.outRowCount; - temp = - (in->output1.outputY.outRowIncrement + - (out - 1)) / out; + temp = (in->output1.outputY.outRowIncrement + (out - 1)) / out; cmd.out1YRowIncrementIn64bit = temp; - /* AXI Output 1 Cbcr Configuration*/ + /* AXI Output 1 Cbcr Configuration */ cmd.out1CbcrPingAddr = out1->cbcrPath.addressBuffer[0]; /* VFE_BUS_VIEW_Cbcr_WR_PONG_ADDR */ - cmd.out1CbcrPongAddr = - out1->cbcrPath.addressBuffer[1]; + cmd.out1CbcrPongAddr = out1->cbcrPath.addressBuffer[1]; /* VFE_BUS_VIEW_Cbcr_WR_IMAGE_SIZE */ cmd.out1CbcrImageHeight = in->output1.outputCbcr.imageHeight; - temp = (in->output1.outputCbcr.imageWidth + - (out - 1)) / out; + temp = (in->output1.outputCbcr.imageWidth + (out - 1)) / out; cmd.out1CbcrImageWidthIn64bit = temp; cmd.out1CbcrBurstLength = burstLength; cmd.out1CbcrNumRows = in->output1.outputCbcr.outRowCount; - temp = - (in->output1.outputCbcr.outRowIncrement + - (out - 1)) / out; + temp = (in->output1.outputCbcr.outRowIncrement + (out - 1)) / out; cmd.out1CbcrRowIncrementIn64bit = temp; vfe_prog_hw(ctrl->vfebase + VFE_BUS_ENC_Y_WR_PING_ADDR, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } static void vfe_reg_bus_cfg(struct vfe_bus_cfg_data *in) { struct vfe_axi_bus_cfg cmd; - cmd.stripeRdPathEn = in->stripeRdPathEn; - cmd.encYWrPathEn = in->encYWrPathEn; - cmd.encCbcrWrPathEn = in->encCbcrWrPathEn; - cmd.viewYWrPathEn = in->viewYWrPathEn; - cmd.viewCbcrWrPathEn = in->viewCbcrWrPathEn; - cmd.rawPixelDataSize = (uint32_t)in->rawPixelDataSize; - cmd.rawWritePathSelect = (uint32_t)in->rawWritePathSelect; + memset(&cmd, 0, sizeof(cmd)); + cmd.stripeRdPathEn = in->stripeRdPathEn; + cmd.encYWrPathEn = in->encYWrPathEn; + cmd.encCbcrWrPathEn = in->encCbcrWrPathEn; + cmd.viewYWrPathEn = in->viewYWrPathEn; + cmd.viewCbcrWrPathEn = in->viewCbcrWrPathEn; + cmd.rawPixelDataSize = (uint32_t) in->rawPixelDataSize; + cmd.rawWritePathSelect = (uint32_t) in->rawWritePathSelect; /* program vfe_bus_cfg */ writel(*((uint32_t *)&cmd), ctrl->vfebase + VFE_BUS_CFG); @@ -288,35 +252,25 @@ static void vfe_reg_camif_config(struct vfe_camif_cfg_data *in) memset(&cfg, 0, sizeof(cfg)); - cfg.VSyncEdge = - in->camifCfgFromCmd.vSyncEdge; + cfg.VSyncEdge = in->camifCfgFromCmd.vSyncEdge; - cfg.HSyncEdge = - in->camifCfgFromCmd.hSyncEdge; + cfg.HSyncEdge = in->camifCfgFromCmd.hSyncEdge; - cfg.syncMode = - in->camifCfgFromCmd.syncMode; + cfg.syncMode = in->camifCfgFromCmd.syncMode; - cfg.vfeSubsampleEnable = - in->camifCfgFromCmd.vfeSubSampleEnable; + cfg.vfeSubsampleEnable = in->camifCfgFromCmd.vfeSubSampleEnable; - cfg.busSubsampleEnable = - in->camifCfgFromCmd.busSubSampleEnable; + cfg.busSubsampleEnable = in->camifCfgFromCmd.busSubSampleEnable; - cfg.camif2vfeEnable = - in->camif2OutputEnable; + cfg.camif2vfeEnable = in->camif2OutputEnable; - cfg.camif2busEnable = - in->camif2BusEnable; + cfg.camif2busEnable = in->camif2BusEnable; - cfg.irqSubsampleEnable = - in->camifCfgFromCmd.irqSubSampleEnable; + cfg.irqSubsampleEnable = in->camifCfgFromCmd.irqSubSampleEnable; - cfg.binningEnable = - in->camifCfgFromCmd.binningEnable; + cfg.binningEnable = in->camifCfgFromCmd.binningEnable; - cfg.misrEnable = - in->camifCfgFromCmd.misrEnable; + cfg.misrEnable = in->camifCfgFromCmd.misrEnable; /* program camif_config */ writel(*((uint32_t *)&cfg), ctrl->vfebase + CAMIF_CONFIG); @@ -327,8 +281,8 @@ static void vfe_reg_bus_cmd(struct vfe_bus_cmd_data *in) struct vfe_buscmd cmd; memset(&cmd, 0, sizeof(cmd)); - cmd.stripeReload = in->stripeReload; - cmd.busPingpongReload = in->busPingpongReload; + cmd.stripeReload = in->stripeReload; + cmd.busPingpongReload = in->busPingpongReload; cmd.statsPingpongReload = in->statsPingpongReload; writel(*((uint32_t *)&cmd), ctrl->vfebase + VFE_BUS_CMD); @@ -349,25 +303,25 @@ static void vfe_reg_module_cfg(struct vfe_module_enable *in) memset(&ena, 0, sizeof(ena)); ena.blackLevelCorrectionEnable = in->blackLevelCorrectionEnable; - ena.lensRollOffEnable = in->lensRollOffEnable; - ena.demuxEnable = in->demuxEnable; - ena.chromaUpsampleEnable = in->chromaUpsampleEnable; - ena.demosaicEnable = in->demosaicEnable; - ena.statsEnable = in->statsEnable; - ena.cropEnable = in->cropEnable; - ena.mainScalerEnable = in->mainScalerEnable; - ena.whiteBalanceEnable = in->whiteBalanceEnable; - ena.colorCorrectionEnable = in->colorCorrectionEnable; - ena.yHistEnable = in->yHistEnable; - ena.skinToneEnable = in->skinToneEnable; - ena.lumaAdaptationEnable = in->lumaAdaptationEnable; - ena.rgbLUTEnable = in->rgbLUTEnable; - ena.chromaEnhanEnable = in->chromaEnhanEnable; - ena.asfEnable = in->asfEnable; - ena.chromaSuppressionEnable = in->chromaSuppressionEnable; - ena.chromaSubsampleEnable = in->chromaSubsampleEnable; - ena.scaler2YEnable = in->scaler2YEnable; - ena.scaler2CbcrEnable = in->scaler2CbcrEnable; + ena.lensRollOffEnable = in->lensRollOffEnable; + ena.demuxEnable = in->demuxEnable; + ena.chromaUpsampleEnable = in->chromaUpsampleEnable; + ena.demosaicEnable = in->demosaicEnable; + ena.statsEnable = in->statsEnable; + ena.cropEnable = in->cropEnable; + ena.mainScalerEnable = in->mainScalerEnable; + ena.whiteBalanceEnable = in->whiteBalanceEnable; + ena.colorCorrectionEnable = in->colorCorrectionEnable; + ena.yHistEnable = in->yHistEnable; + ena.skinToneEnable = in->skinToneEnable; + ena.lumaAdaptationEnable = in->lumaAdaptationEnable; + ena.rgbLUTEnable = in->rgbLUTEnable; + ena.chromaEnhanEnable = in->chromaEnhanEnable; + ena.asfEnable = in->asfEnable; + ena.chromaSuppressionEnable = in->chromaSuppressionEnable; + ena.chromaSubsampleEnable = in->chromaSubsampleEnable; + ena.scaler2YEnable = in->scaler2YEnable; + ena.scaler2CbcrEnable = in->scaler2CbcrEnable; writel(*((uint32_t *)&ena), ctrl->vfebase + VFE_MODULE_CFG); } @@ -377,42 +331,41 @@ static void vfe_program_dmi_cfg(enum VFE_DMI_RAM_SEL bankSel) /* set bit 8 for auto increment. */ uint32_t value = (uint32_t) ctrl->vfebase + VFE_DMI_CFG_DEFAULT; - value += (uint32_t)bankSel; + value += (uint32_t) bankSel; /* CDBG("dmi cfg input bank is 0x%x\n", bankSel); */ writel(value, ctrl->vfebase + VFE_DMI_CFG); writel(0, ctrl->vfebase + VFE_DMI_ADDR); } -static void vfe_write_lens_roll_off_table( - struct vfe_cmd_roll_off_config *in) +static void vfe_write_lens_roll_off_table(struct vfe_cmd_roll_off_config *in) { uint16_t i; uint32_t data; uint16_t *initGr = in->initTableGr; uint16_t *initGb = in->initTableGb; - uint16_t *initB = in->initTableB; - uint16_t *initR = in->initTableR; + uint16_t *initB = in->initTableB; + uint16_t *initR = in->initTableR; int16_t *pDeltaGr = in->deltaTableGr; int16_t *pDeltaGb = in->deltaTableGb; - int16_t *pDeltaB = in->deltaTableB; - int16_t *pDeltaR = in->deltaTableR; + int16_t *pDeltaB = in->deltaTableB; + int16_t *pDeltaR = in->deltaTableR; vfe_program_dmi_cfg(ROLLOFF_RAM); /* first pack and write init table */ for (i = 0; i < VFE_ROLL_OFF_INIT_TABLE_SIZE; i++) { - data = (((uint32_t)(*initR)) & 0x0000FFFF) | - (((uint32_t)(*initGr)) << 16); + data = (((uint32_t) (*initR)) & 0x0000FFFF) | + (((uint32_t) (*initGr)) << 16); initR++; initGr++; writel(data, ctrl->vfebase + VFE_DMI_DATA_LO); - data = (((uint32_t)(*initB)) & 0x0000FFFF) | - (((uint32_t)(*initGr))<<16); + data = (((uint32_t) (*initB)) & 0x0000FFFF) | + (((uint32_t) (*initGr)) << 16); initB++; initGb++; @@ -421,20 +374,17 @@ static void vfe_write_lens_roll_off_table( /* there are gaps between the init table and delta table, * set the offset for delta table. */ - writel(LENS_ROLL_OFF_DELTA_TABLE_OFFSET, - ctrl->vfebase + VFE_DMI_ADDR); + writel(LENS_ROLL_OFF_DELTA_TABLE_OFFSET, ctrl->vfebase + VFE_DMI_ADDR); /* pack and write delta table */ for (i = 0; i < VFE_ROLL_OFF_DELTA_TABLE_SIZE; i++) { - data = (((int32_t)(*pDeltaR)) & 0x0000FFFF) | - (((int32_t)(*pDeltaGr))<<16); + data = *pDeltaR | (*pDeltaGr << 16); pDeltaR++; pDeltaGr++; writel(data, ctrl->vfebase + VFE_DMI_DATA_LO); - data = (((int32_t)(*pDeltaB)) & 0x0000FFFF) | - (((int32_t)(*pDeltaGb))<<16); + data = *pDeltaB | (*pDeltaGb << 16); pDeltaB++; pDeltaGb++; @@ -479,16 +429,6 @@ static void vfe_pm_stop(void) writel(VFE_PERFORMANCE_MONITOR_STOP, ctrl->vfebase + VFE_BUS_PM_CMD); } -static void vfe_program_bus_rd_irq_en(uint32_t value) -{ - writel(value, ctrl->vfebase + VFE_BUS_PINGPONG_IRQ_EN); -} - -static void vfe_camif_go(void) -{ - writel(CAMIF_COMMAND_START, ctrl->vfebase + CAMIF_COMMAND); -} - static void vfe_camif_stop_immediately(void) { writel(CAMIF_COMMAND_STOP_IMMEDIATELY, ctrl->vfebase + CAMIF_COMMAND); @@ -500,11 +440,6 @@ static void vfe_program_reg_update_cmd(uint32_t value) writel(value, ctrl->vfebase + VFE_REG_UPDATE_CMD); } -static void vfe_program_bus_cmd(uint32_t value) -{ - writel(value, ctrl->vfebase + VFE_BUS_CMD); -} - static void vfe_program_global_reset_cmd(uint32_t value) { writel(value, ctrl->vfebase + VFE_GLOBAL_RESET_CMD); @@ -525,39 +460,24 @@ static inline void vfe_program_irq_mask(uint32_t value) writel(value, ctrl->vfebase + VFE_IRQ_MASK); } -static void vfe_program_chroma_upsample_cfg(uint32_t value) -{ - writel(value, ctrl->vfebase + VFE_CHROMA_UPSAMPLE_CFG); -} - static uint32_t vfe_read_axi_status(void) { return readl(ctrl->vfebase + VFE_AXI_STATUS); } -static uint32_t vfe_read_pm_status_in_raw_capture(void) -{ - return readl(ctrl->vfebase + VFE_BUS_ENC_CBCR_WR_PM_STATS_1); -} - static void vfe_set_stats_pingpong_address(struct vfe_stats_control *afControl, - struct vfe_stats_control *awbControl) + struct vfe_stats_control *awbControl) { afControl->hwRegPingAddress = (uint8_t *) - (ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR); + (ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR); afControl->hwRegPongAddress = (uint8_t *) - (ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR); + (ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR); awbControl->hwRegPingAddress = (uint8_t *) - (ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR); + (ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR); awbControl->hwRegPongAddress = (uint8_t *) - (ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR); -} - -static uint32_t vfe_read_camif_status(void) -{ - return readl(ctrl->vfebase + CAMIF_STATUS); + (ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR); } static void vfe_program_lut_bank_sel(struct vfe_gamma_lut_sel *in) @@ -571,7 +491,7 @@ static void vfe_program_lut_bank_sel(struct vfe_gamma_lut_sel *in) cmd.ch2BankSelect = in->ch2BankSelect; CDBG("VFE gamma lut bank selection is 0x%x\n", *((uint32_t *)&cmd)); vfe_prog_hw(ctrl->vfebase + VFE_LUT_BANK_SEL, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } static void vfe_program_stats_cmd(struct vfe_stats_cmd_data *in) @@ -579,12 +499,12 @@ static void vfe_program_stats_cmd(struct vfe_stats_cmd_data *in) struct VFE_StatsCmdType stats; memset(&stats, 0, sizeof(stats)); - stats.autoFocusEnable = in->autoFocusEnable; - stats.axwEnable = in->axwEnable; - stats.histEnable = in->histEnable; - stats.clearHistEnable = in->clearHistEnable; - stats.histAutoClearEnable = in->histAutoClearEnable; - stats.colorConversionEnable = in->colorConversionEnable; + stats.autoFocusEnable = in->autoFocusEnable; + stats.axwEnable = in->axwEnable; + stats.histEnable = in->histEnable; + stats.clearHistEnable = in->clearHistEnable; + stats.histAutoClearEnable = in->histAutoClearEnable; + stats.colorConversionEnable = in->colorConversionEnable; writel(*((uint32_t *)&stats), ctrl->vfebase + VFE_STATS_CMD); } @@ -594,21 +514,21 @@ static void vfe_pm_start(struct vfe_cmd_bus_pm_start *in) struct VFE_Bus_Pm_ConfigCmdType cmd; memset(&cmd, 0, sizeof(struct VFE_Bus_Pm_ConfigCmdType)); - cmd.output2YWrPmEnable = in->output2YWrPmEnable; - cmd.output2CbcrWrPmEnable = in->output2CbcrWrPmEnable; - cmd.output1YWrPmEnable = in->output1YWrPmEnable; - cmd.output1CbcrWrPmEnable = in->output1CbcrWrPmEnable; + cmd.output2YWrPmEnable = in->output2YWrPmEnable; + cmd.output2CbcrWrPmEnable = in->output2CbcrWrPmEnable; + cmd.output1YWrPmEnable = in->output1YWrPmEnable; + cmd.output1CbcrWrPmEnable = in->output1CbcrWrPmEnable; vfe_prog_hw(ctrl->vfebase + VFE_BUS_PM_CFG, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } static void vfe_8k_pm_start(struct vfe_cmd_bus_pm_start *in) { in->output1CbcrWrPmEnable = ctrl->vfeBusConfigLocal.viewCbcrWrPathEn; - in->output1YWrPmEnable = ctrl->vfeBusConfigLocal.viewYWrPathEn; + in->output1YWrPmEnable = ctrl->vfeBusConfigLocal.viewYWrPathEn; in->output2CbcrWrPmEnable = ctrl->vfeBusConfigLocal.encCbcrWrPathEn; - in->output2YWrPmEnable = ctrl->vfeBusConfigLocal.encYWrPathEn; + in->output2YWrPmEnable = ctrl->vfeBusConfigLocal.encYWrPathEn; if (in->output1CbcrWrPmEnable || in->output1YWrPmEnable) ctrl->viewPath.pmEnabled = TRUE; @@ -627,38 +547,38 @@ static uint32_t vfe_irq_pack(struct vfe_interrupt_mask data) memset(&packedData, 0, sizeof(packedData)); - packedData.camifErrorIrq = data.camifErrorIrq; - packedData.camifSofIrq = data.camifSofIrq; - packedData.camifEolIrq = data.camifEolIrq; - packedData.camifEofIrq = data.camifEofIrq; - packedData.camifEpoch1Irq = data.camifEpoch1Irq; - packedData.camifEpoch2Irq = data.camifEpoch2Irq; - packedData.camifOverflowIrq = data.camifOverflowIrq; - packedData.ceIrq = data.ceIrq; - packedData.regUpdateIrq = data.regUpdateIrq; - packedData.resetAckIrq = data.resetAckIrq; - packedData.encYPingpongIrq = data.encYPingpongIrq; - packedData.encCbcrPingpongIrq = data.encCbcrPingpongIrq; - packedData.viewYPingpongIrq = data.viewYPingpongIrq; - packedData.viewCbcrPingpongIrq = data.viewCbcrPingpongIrq; - packedData.rdPingpongIrq = data.rdPingpongIrq; - packedData.afPingpongIrq = data.afPingpongIrq; - packedData.awbPingpongIrq = data.awbPingpongIrq; - packedData.histPingpongIrq = data.histPingpongIrq; - packedData.encIrq = data.encIrq; - packedData.viewIrq = data.viewIrq; - packedData.busOverflowIrq = data.busOverflowIrq; - packedData.afOverflowIrq = data.afOverflowIrq; - packedData.awbOverflowIrq = data.awbOverflowIrq; - packedData.syncTimer0Irq = data.syncTimer0Irq; - packedData.syncTimer1Irq = data.syncTimer1Irq; - packedData.syncTimer2Irq = data.syncTimer2Irq; - packedData.asyncTimer0Irq = data.asyncTimer0Irq; - packedData.asyncTimer1Irq = data.asyncTimer1Irq; - packedData.asyncTimer2Irq = data.asyncTimer2Irq; - packedData.asyncTimer3Irq = data.asyncTimer3Irq; - packedData.axiErrorIrq = data.axiErrorIrq; - packedData.violationIrq = data.violationIrq; + packedData.camifErrorIrq = data.camifErrorIrq; + packedData.camifSofIrq = data.camifSofIrq; + packedData.camifEolIrq = data.camifEolIrq; + packedData.camifEofIrq = data.camifEofIrq; + packedData.camifEpoch1Irq = data.camifEpoch1Irq; + packedData.camifEpoch2Irq = data.camifEpoch2Irq; + packedData.camifOverflowIrq = data.camifOverflowIrq; + packedData.ceIrq = data.ceIrq; + packedData.regUpdateIrq = data.regUpdateIrq; + packedData.resetAckIrq = data.resetAckIrq; + packedData.encYPingpongIrq = data.encYPingpongIrq; + packedData.encCbcrPingpongIrq = data.encCbcrPingpongIrq; + packedData.viewYPingpongIrq = data.viewYPingpongIrq; + packedData.viewCbcrPingpongIrq = data.viewCbcrPingpongIrq; + packedData.rdPingpongIrq = data.rdPingpongIrq; + packedData.afPingpongIrq = data.afPingpongIrq; + packedData.awbPingpongIrq = data.awbPingpongIrq; + packedData.histPingpongIrq = data.histPingpongIrq; + packedData.encIrq = data.encIrq; + packedData.viewIrq = data.viewIrq; + packedData.busOverflowIrq = data.busOverflowIrq; + packedData.afOverflowIrq = data.afOverflowIrq; + packedData.awbOverflowIrq = data.awbOverflowIrq; + packedData.syncTimer0Irq = data.syncTimer0Irq; + packedData.syncTimer1Irq = data.syncTimer1Irq; + packedData.syncTimer2Irq = data.syncTimer2Irq; + packedData.asyncTimer0Irq = data.asyncTimer0Irq; + packedData.asyncTimer1Irq = data.asyncTimer1Irq; + packedData.asyncTimer2Irq = data.asyncTimer2Irq; + packedData.asyncTimer3Irq = data.asyncTimer3Irq; + packedData.axiErrorIrq = data.axiErrorIrq; + packedData.violationIrq = data.violationIrq; return *((uint32_t *)&packedData); } @@ -670,223 +590,276 @@ vfe_irq_composite_pack(struct vfe_irq_composite_mask_config data) memset(&packedData, 0, sizeof(packedData)); - packedData.encIrqComMaskBits = data.encIrqComMask; - packedData.viewIrqComMaskBits = data.viewIrqComMask; - packedData.ceDoneSelBits = data.ceDoneSel; + packedData.encIrqComMaskBits = data.encIrqComMask; + packedData.viewIrqComMaskBits = data.viewIrqComMask; + packedData.ceDoneSelBits = data.ceDoneSel; return *((uint32_t *)&packedData); } static void vfe_addr_convert(struct msm_vfe_phy_info *pinfo, - enum vfe_resp_msg type, void *data, void **ext, int32_t *elen) + enum vfe_resp_msg type, void *data, void **ext, + int *elen) { switch (type) { - case VFE_MSG_OUTPUT1: { - pinfo->y_phy = - ((struct vfe_message *)data)->_u.msgOutput1.yBuffer; - pinfo->cbcr_phy = - ((struct vfe_message *)data)->_u.msgOutput1.cbcrBuffer; + case VFE_MSG_OUTPUT1:{ + pinfo->y_phy = + ((struct vfe_message *)data)->_u.msgOutput1.yBuffer; + pinfo->cbcr_phy = + ((struct vfe_message *)data)->_u.msgOutput1. + cbcrBuffer; - ((struct vfe_frame_extra *)ctrl->extdata)->bpcInfo = - ((struct vfe_message *)data)->_u.msgOutput1.bpcInfo; + ctrl->extdata.bpcInfo = + ((struct vfe_message *)data)->_u.msgOutput1.bpcInfo; - ((struct vfe_frame_extra *)ctrl->extdata)->asfInfo = - ((struct vfe_message *)data)->_u.msgOutput1.asfInfo; + ctrl->extdata.asfInfo = + ((struct vfe_message *)data)->_u.msgOutput1.asfInfo; - ((struct vfe_frame_extra *)ctrl->extdata)->frameCounter = - ((struct vfe_message *)data)->_u.msgOutput1.frameCounter; + ctrl->extdata.frameCounter = + ((struct vfe_message *)data)->_u.msgOutput1. + frameCounter; - ((struct vfe_frame_extra *)ctrl->extdata)->pmData = - ((struct vfe_message *)data)->_u.msgOutput1.pmData; + ctrl->extdata.pmData = + ((struct vfe_message *)data)->_u.msgOutput1.pmData; - *ext = ctrl->extdata; - *elen = ctrl->extlen; - } + *ext = &ctrl->extdata; + *elen = sizeof(ctrl->extdata); + } break; - case VFE_MSG_OUTPUT2: { - pinfo->y_phy = - ((struct vfe_message *)data)->_u.msgOutput2.yBuffer; - pinfo->cbcr_phy = - ((struct vfe_message *)data)->_u.msgOutput2.cbcrBuffer; + case VFE_MSG_OUTPUT2:{ + pinfo->y_phy = + ((struct vfe_message *)data)->_u.msgOutput2.yBuffer; + pinfo->cbcr_phy = + ((struct vfe_message *)data)->_u.msgOutput2. + cbcrBuffer; - CDBG("vfe_addr_convert, pinfo->y_phy = 0x%x\n", pinfo->y_phy); - CDBG("vfe_addr_convert, pinfo->cbcr_phy = 0x%x\n", - pinfo->cbcr_phy); + CDBG("vfe_addr_convert, pinfo->y_phy = 0x%x\n", + pinfo->y_phy); + CDBG("vfe_addr_convert, pinfo->cbcr_phy = 0x%x\n", + pinfo->cbcr_phy); - ((struct vfe_frame_extra *)ctrl->extdata)->bpcInfo = - ((struct vfe_message *)data)->_u.msgOutput2.bpcInfo; + ctrl->extdata.bpcInfo = + ((struct vfe_message *)data)->_u.msgOutput2.bpcInfo; - ((struct vfe_frame_extra *)ctrl->extdata)->asfInfo = - ((struct vfe_message *)data)->_u.msgOutput2.asfInfo; + ctrl->extdata.asfInfo = + ((struct vfe_message *)data)->_u.msgOutput2.asfInfo; - ((struct vfe_frame_extra *)ctrl->extdata)->frameCounter = - ((struct vfe_message *)data)->_u.msgOutput2.frameCounter; + ctrl->extdata.frameCounter = + ((struct vfe_message *)data)->_u.msgOutput2. + frameCounter; - ((struct vfe_frame_extra *)ctrl->extdata)->pmData = - ((struct vfe_message *)data)->_u.msgOutput2.pmData; + ctrl->extdata.pmData = + ((struct vfe_message *)data)->_u.msgOutput2.pmData; - *ext = ctrl->extdata; - *elen = ctrl->extlen; - } + *ext = &ctrl->extdata; + *elen = sizeof(ctrl->extdata); + } break; case VFE_MSG_STATS_AF: pinfo->sbuf_phy = - ((struct vfe_message *)data)->_u.msgStatsAf.afBuffer; + ((struct vfe_message *)data)->_u.msgStatsAf.afBuffer; break; case VFE_MSG_STATS_WE: pinfo->sbuf_phy = - ((struct vfe_message *)data)->_u.msgStatsWbExp.awbBuffer; + ((struct vfe_message *)data)->_u.msgStatsWbExp.awbBuffer; break; default: break; - } /* switch */ -} + } /* switch */ +} + +static boolean vfe_send_output1_msg(struct msm_vfe_resp *rp, + struct vfe_message *msg, void *data); +static boolean vfe_send_output2_msg(struct msm_vfe_resp *rp, + struct vfe_message *msg, void *data); +static boolean vfe_send_af_stats_msg(struct msm_vfe_resp *rp, + struct vfe_message *msg, void *data); +static boolean vfe_send_awb_stats_msg(struct msm_vfe_resp *rp, + struct vfe_message *msg, void *data); +static boolean vfe_send_camif_error_msg(struct msm_vfe_resp *rp, + struct vfe_message *msg, void *data); +static boolean vfe_send_bus_overflow_msg(struct msm_vfe_resp *rp, + struct vfe_message *msg, void *data); + +static boolean invalid(struct msm_vfe_resp *rp, + struct vfe_message *_m, void *_d) +{ + BUG_ON(1); /* this function should not be called. */ + return FALSE; +} + +static struct { + boolean (*fn)(struct msm_vfe_resp *rp, struct vfe_message *msg, void *data); + enum vfe_resp_msg rt; /* reponse type */ +} vfe_funcs[] = { + [VFE_MSG_ID_RESET_ACK] = { NULL, VFE_MSG_GENERAL }, + [VFE_MSG_ID_START_ACK] = { NULL, VFE_MSG_GENERAL }, + [VFE_MSG_ID_STOP_ACK] = { NULL, VFE_MSG_GENERAL }, + [VFE_MSG_ID_UPDATE_ACK] = { NULL, VFE_MSG_GENERAL }, + [VFE_MSG_ID_OUTPUT1] = { vfe_send_output1_msg, VFE_MSG_OUTPUT1 }, + [VFE_MSG_ID_OUTPUT2] = { vfe_send_output2_msg, VFE_MSG_OUTPUT2 }, + [VFE_MSG_ID_SNAPSHOT_DONE] = { NULL, VFE_MSG_SNAPSHOT }, + [VFE_MSG_ID_STATS_AUTOFOCUS] = { vfe_send_af_stats_msg, VFE_MSG_STATS_AF }, + [VFE_MSG_ID_STATS_WB_EXP] = { vfe_send_awb_stats_msg, VFE_MSG_STATS_WE }, + [VFE_MSG_ID_EPOCH1] = { NULL, VFE_MSG_GENERAL }, + [VFE_MSG_ID_EPOCH2] = { NULL, VFE_MSG_GENERAL }, + [VFE_MSG_ID_SYNC_TIMER0_DONE] = { invalid }, + [VFE_MSG_ID_SYNC_TIMER1_DONE] = { invalid }, + [VFE_MSG_ID_SYNC_TIMER2_DONE] = { invalid }, + [VFE_MSG_ID_ASYNC_TIMER0_DONE] = { invalid }, + [VFE_MSG_ID_ASYNC_TIMER1_DONE] = { invalid }, + [VFE_MSG_ID_ASYNC_TIMER2_DONE] = { invalid }, + [VFE_MSG_ID_ASYNC_TIMER3_DONE] = { invalid }, + [VFE_MSG_ID_AF_OVERFLOW] = { NULL, VFE_MSG_GENERAL }, + [VFE_MSG_ID_AWB_OVERFLOW] = { NULL, VFE_MSG_GENERAL }, + [VFE_MSG_ID_AXI_ERROR] = { NULL, VFE_MSG_GENERAL }, + [VFE_MSG_ID_CAMIF_OVERFLOW] = { NULL, VFE_MSG_GENERAL }, + [VFE_MSG_ID_VIOLATION] = { invalid }, + [VFE_MSG_ID_CAMIF_ERROR] = { vfe_send_camif_error_msg, VFE_MSG_GENERAL }, + [VFE_MSG_ID_BUS_OVERFLOW] = { vfe_send_bus_overflow_msg, VFE_MSG_GENERAL }, +}; -static void -vfe_proc_ops(enum VFE_MESSAGE_ID id, void *msg, size_t len) +static void vfe_proc_ops(enum VFE_MESSAGE_ID id, void *data) { struct msm_vfe_resp *rp; - - /* In 8k, OUTPUT1 & OUTPUT2 messages arrive before - * SNAPSHOT_DONE. We don't send such messages to user */ + struct vfe_message *msg; + struct msm_sync *sync = (struct msm_sync *)ctrl->syncdata; CDBG("ctrl->vfeOperationMode = %d, msgId = %d\n", - ctrl->vfeOperationMode, id); + ctrl->vfeOperationMode, id); - if ((ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT) && - (id == VFE_MSG_ID_OUTPUT1 || id == VFE_MSG_ID_OUTPUT2)) { + if (id >= ARRAY_SIZE(vfe_funcs) || vfe_funcs[id].fn == invalid) { + pr_err("%s: invalid VFE message id %d\n", __func__, id); return; } - rp = ctrl->resp->vfe_alloc(sizeof(struct msm_vfe_resp), ctrl->syncdata); + /* In 8k, OUTPUT1 & OUTPUT2 messages arrive before SNAPSHOT_DONE. + * We don't send such messages to the user. Note that we can do + * this in the vfe_func[] callback, but that would cause us to + * allocate and then immediately free the msm_vfe_resp structure, + * which is wasteful. + */ + if ((ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT) && + (id == VFE_MSG_ID_OUTPUT1 || + id == VFE_MSG_ID_OUTPUT2)) + return; + + rp = ctrl->resp->vfe_alloc(sizeof(*rp) + + (vfe_funcs[id].fn ? sizeof(*msg) : 0), + ctrl->syncdata, + GFP_ATOMIC); if (!rp) { - CDBG("rp: cannot allocate buffer\n"); + pr_err("%s: out of memory\n", __func__); return; } - CDBG("vfe_proc_ops, msgId = %d\n", id); - - rp->evt_msg.type = MSM_CAMERA_MSG; + rp->type = vfe_funcs[id].rt; + rp->evt_msg.type = MSM_CAMERA_MSG; rp->evt_msg.msg_id = id; - rp->evt_msg.len = len; - rp->evt_msg.data = msg; - - switch (rp->evt_msg.msg_id) { - case VFE_MSG_ID_SNAPSHOT_DONE: - rp->type = VFE_MSG_SNAPSHOT; - break; - - case VFE_MSG_ID_OUTPUT1: - rp->type = VFE_MSG_OUTPUT1; - vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT1, - rp->evt_msg.data, &(rp->extdata), - &(rp->extlen)); - break; - - case VFE_MSG_ID_OUTPUT2: - rp->type = VFE_MSG_OUTPUT2; - vfe_addr_convert(&(rp->phy), VFE_MSG_OUTPUT2, - rp->evt_msg.data, &(rp->extdata), - &(rp->extlen)); - break; - case VFE_MSG_ID_STATS_AUTOFOCUS: - rp->type = VFE_MSG_STATS_AF; - vfe_addr_convert(&(rp->phy), VFE_MSG_STATS_AF, - rp->evt_msg.data, NULL, NULL); - break; - - case VFE_MSG_ID_STATS_WB_EXP: - rp->type = VFE_MSG_STATS_WE; - vfe_addr_convert(&(rp->phy), VFE_MSG_STATS_WE, - rp->evt_msg.data, NULL, NULL); - break; - - default: - rp->type = VFE_MSG_GENERAL; - break; + /* Turn off the flash if epoch1 is enabled and snapshot is done. */ + if (ctrl->vfeCamifEpoch1Local.enable && + ctrl->vfeOperationMode == + VFE_START_OPERATION_MODE_SNAPSHOT && + id == VFE_MSG_ID_SNAPSHOT_DONE) { + ctrl->resp->flash_ctrl(sync, MSM_CAMERA_LED_OFF); + ctrl->vfeCamifEpoch1Local.enable = 0; } - ctrl->resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, ctrl->syncdata); -} - -static void vfe_send_msg_no_payload(enum VFE_MESSAGE_ID id) -{ - struct vfe_message *msg; - - msg = kzalloc(sizeof(msg), GFP_ATOMIC); - if (!msg) - return; + if (!vfe_funcs[id].fn) { + rp->evt_msg.len = 0; + rp->evt_msg.data = 0; + } else { + /* populate the message accordingly */ + if (vfe_funcs[id].fn) + rp->evt_msg.data = msg = + (struct vfe_message *)(rp + 1); + else + rp->evt_msg.data = msg = 0; + rp->evt_msg.len = sizeof(*msg); + msg->_d = id; + if (vfe_funcs[id].fn(rp, msg, data) == FALSE) { + pr_info("%s: freeing memory: handler for %d " + "returned false\n", __func__, id); + ctrl->resp->vfe_free(rp); + return; + } + } - msg->_d = id; - vfe_proc_ops(id, msg, 0); + ctrl->resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, ctrl->syncdata, GFP_ATOMIC); } -static void vfe_send_bus_overflow_msg(void) +static boolean vfe_send_bus_overflow_msg(struct msm_vfe_resp *rp, + struct vfe_message *msg, + void *data) { - struct vfe_message *msg; - msg = - kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); - if (!msg) - return; - - msg->_d = VFE_MSG_ID_BUS_OVERFLOW; -#if 0 + struct isr_queue_cmd *qcmd = data; memcpy(&(msg->_u.msgBusOverflow), - &ctrl->vfePmData, sizeof(ctrl->vfePmData)); -#endif - - vfe_proc_ops(VFE_MSG_ID_BUS_OVERFLOW, - msg, sizeof(struct vfe_message)); + &qcmd->vfePmData, sizeof(qcmd->vfePmData)); + return TRUE; } -static void vfe_send_camif_error_msg(void) +static boolean vfe_send_camif_error_msg(struct msm_vfe_resp *rp, + struct vfe_message *msg, + void *data) { -#if 0 - struct vfe_message *msg; - msg = - kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); - if (!msg) - return; - - msg->_d = VFE_MSG_ID_CAMIF_ERROR; + struct isr_queue_cmd *qcmd = data; memcpy(&(msg->_u.msgCamifError), - &ctrl->vfeCamifStatusLocal, sizeof(ctrl->vfeCamifStatusLocal)); - - vfe_proc_ops(VFE_MSG_ID_CAMIF_ERROR, - msg, sizeof(struct vfe_message)); -#endif + &qcmd->vfeCamifStatusLocal, sizeof(qcmd->vfeCamifStatusLocal)); + return TRUE; } -static void vfe_process_error_irq( - struct vfe_interrupt_status *irqstatus) +static void vfe_process_error_irq(struct isr_queue_cmd *qcmd) { + struct vfe_interrupt_status *irqstatus = &qcmd->vfeInterruptStatus; + /* all possible error irq. Note error irqs are not enabled, it is * checked only when other interrupts are present. */ if (irqstatus->afOverflowIrq) - vfe_send_msg_no_payload(VFE_MSG_ID_AF_OVERFLOW); + vfe_proc_ops(VFE_MSG_ID_AF_OVERFLOW, qcmd); if (irqstatus->awbOverflowIrq) - vfe_send_msg_no_payload(VFE_MSG_ID_AWB_OVERFLOW); + vfe_proc_ops(VFE_MSG_ID_AWB_OVERFLOW, qcmd); if (irqstatus->axiErrorIrq) - vfe_send_msg_no_payload(VFE_MSG_ID_AXI_ERROR); + vfe_proc_ops(VFE_MSG_ID_AXI_ERROR, qcmd); if (irqstatus->busOverflowIrq) - vfe_send_bus_overflow_msg(); + vfe_proc_ops(VFE_MSG_ID_BUS_OVERFLOW, qcmd); if (irqstatus->camifErrorIrq) - vfe_send_camif_error_msg(); + vfe_proc_ops(VFE_MSG_ID_CAMIF_ERROR, qcmd); if (irqstatus->camifOverflowIrq) - vfe_send_msg_no_payload(VFE_MSG_ID_CAMIF_OVERFLOW); + vfe_proc_ops(VFE_MSG_ID_CAMIF_OVERFLOW, qcmd); if (irqstatus->violationIrq) - ; + pr_err("%s: violation irq\n", __func__); +} + +/* We use epoch1 interrupt to control flash timing. The purpose is to reduce the + * flash duration as much as possible. Userspace driver has no way to control + * the exactly timing like VFE. Currently we skip a frame during snapshot. + * We want to fire the flash in the middle of the first frame. Epoch1 interrupt + * allows us to set a line index and we will get an interrupt when VFE reaches + * the line. Userspace driver sets the line index in camif configuration. VFE + * will fire the flash in high mode when it gets the epoch1 interrupt. Flash + * will be turned off after snapshot is done. + */ +static void vfe_process_camif_epoch1_irq(void) +{ + /* Turn on the flash. */ + struct msm_sync *sync = (struct msm_sync *)ctrl->syncdata; + ctrl->resp->flash_ctrl(sync, MSM_CAMERA_LED_HIGH); + + /* Disable the epoch1 interrupt. */ + ctrl->vfeImaskLocal.camifEpoch1Irq = FALSE; + ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal); + vfe_program_irq_mask(ctrl->vfeImaskPacked); } static void vfe_process_camif_sof_irq(void) @@ -894,47 +867,37 @@ static void vfe_process_camif_sof_irq(void) /* increment the frame id number. */ ctrl->vfeFrameId++; - CDBG("camif_sof_irq, frameId = %d\n", - ctrl->vfeFrameId); + CDBG("camif_sof_irq, frameId = %d\n", ctrl->vfeFrameId); /* In snapshot mode, if frame skip is programmed, - * need to check it accordingly to stop camif at - * correct frame boundary. For the dropped frames, - * there won't be any output path irqs, but there is - * still SOF irq, which can help us determine when - * to stop the camif. - */ + * need to check it accordingly to stop camif at + * correct frame boundary. For the dropped frames, + * there won't be any output path irqs, but there is + * still SOF irq, which can help us determine when + * to stop the camif. + */ if (ctrl->vfeOperationMode) { - if ((1 << ctrl->vfeFrameSkipCount) & - ctrl->vfeFrameSkipPattern) { + if ((1 << ctrl->vfeFrameSkipCount)&ctrl->vfeFrameSkipPattern) { ctrl->vfeSnapShotCount--; if (ctrl->vfeSnapShotCount == 0) /* terminate vfe pipeline at frame boundary. */ writel(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY, - ctrl->vfebase + CAMIF_COMMAND); + ctrl->vfebase + CAMIF_COMMAND); } /* update frame skip counter for bit checking. */ ctrl->vfeFrameSkipCount++; - if (ctrl->vfeFrameSkipCount == - (ctrl->vfeFrameSkipPeriod + 1)) + if (ctrl->vfeFrameSkipCount == (ctrl->vfeFrameSkipPeriod + 1)) ctrl->vfeFrameSkipCount = 0; } } -static int vfe_get_af_pingpong_status(void) +static boolean vfe_get_af_pingpong_status(void) { - uint32_t busPingPongStatus; - int rc = 0; - - busPingPongStatus = + uint32_t busPingPongStatus = readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS); - - if ((busPingPongStatus & VFE_AF_PINGPONG_STATUS_BIT) == 0) - return -EFAULT; - - return rc; + return !!(busPingPongStatus & VFE_AF_PINGPONG_STATUS_BIT); } static uint32_t vfe_read_af_buf_addr(boolean pipo) @@ -945,8 +908,7 @@ static uint32_t vfe_read_af_buf_addr(boolean pipo) return readl(ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR); } -static void -vfe_update_af_buf_addr(boolean pipo, uint32_t addr) +static void vfe_update_af_buf_addr(boolean pipo, uint32_t addr) { if (pipo == FALSE) writel(addr, ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR); @@ -954,34 +916,22 @@ vfe_update_af_buf_addr(boolean pipo, uint32_t addr) writel(addr, ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR); } -static void -vfe_send_af_stats_msg(uint32_t afBufAddress) +static boolean vfe_send_af_stats_msg(struct msm_vfe_resp *rp, + struct vfe_message *msg, void *data) { - /* unsigned long flags; */ - struct vfe_message *msg; - msg = - kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); - if (!msg) - return; + uint32_t afBufAddress = (uint32_t)data; - /* fill message with right content. */ - /* @todo This is causing issues, need further investigate */ - /* spin_lock_irqsave(&ctrl->state_lock, flags); */ if (ctrl->vstate != VFE_STATE_ACTIVE) - goto af_stats_done; + return FALSE; - msg->_d = VFE_MSG_ID_STATS_AUTOFOCUS; msg->_u.msgStatsAf.afBuffer = afBufAddress; msg->_u.msgStatsAf.frameCounter = ctrl->vfeFrameId; - vfe_proc_ops(VFE_MSG_ID_STATS_AUTOFOCUS, - msg, sizeof(struct vfe_message)); - ctrl->afStatsControl.ackPending = TRUE; -af_stats_done: - /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ - return; + vfe_addr_convert(&(rp->phy), rp->type, msg, NULL, NULL); + + return TRUE; } static void vfe_process_stats_af_irq(void) @@ -992,38 +942,31 @@ static void vfe_process_stats_af_irq(void) /* read hardware status. */ ctrl->afStatsControl.pingPongStatus = - vfe_get_af_pingpong_status(); + vfe_get_af_pingpong_status(); - bufferAvailable = - (ctrl->afStatsControl.pingPongStatus) ^ 1; + bufferAvailable = (ctrl->afStatsControl.pingPongStatus) ^ 1; ctrl->afStatsControl.bufToRender = - vfe_read_af_buf_addr(bufferAvailable); + vfe_read_af_buf_addr(bufferAvailable); /* update the same buffer address (ping or pong) */ vfe_update_af_buf_addr(bufferAvailable, - ctrl->afStatsControl.nextFrameAddrBuf); + ctrl->afStatsControl.nextFrameAddrBuf); - vfe_send_af_stats_msg(ctrl->afStatsControl.bufToRender); + vfe_proc_ops(VFE_MSG_ID_STATS_AUTOFOCUS, + (void *)ctrl->afStatsControl.bufToRender); } else ctrl->afStatsControl.droppedStatsFrameCount++; } static boolean vfe_get_awb_pingpong_status(void) { - uint32_t busPingPongStatus; - - busPingPongStatus = + uint32_t busPingPongStatus = readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS); - - if ((busPingPongStatus & VFE_AWB_PINGPONG_STATUS_BIT) == 0) - return FALSE; - - return TRUE; + return !!(busPingPongStatus & VFE_AWB_PINGPONG_STATUS_BIT); } -static uint32_t -vfe_read_awb_buf_addr(boolean pingpong) +static uint32_t vfe_read_awb_buf_addr(boolean pingpong) { if (pingpong == FALSE) return readl(ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR); @@ -1031,8 +974,7 @@ vfe_read_awb_buf_addr(boolean pingpong) return readl(ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR); } -static void vfe_update_awb_buf_addr( - boolean pingpong, uint32_t addr) +static void vfe_update_awb_buf_addr(boolean pingpong, uint32_t addr) { if (pingpong == FALSE) writel(addr, ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR); @@ -1040,34 +982,24 @@ static void vfe_update_awb_buf_addr( writel(addr, ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR); } -static void vfe_send_awb_stats_msg(uint32_t awbBufAddress) +static boolean vfe_send_awb_stats_msg(struct msm_vfe_resp *rp, + struct vfe_message *msg, void *data) { - /* unsigned long flags; */ - struct vfe_message *msg; - - msg = - kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); - if (!msg) - return; + uint32_t awbBufAddress = (uint32_t)data; - /* fill message with right content. */ - /* @todo This is causing issues, need further investigate */ - /* spin_lock_irqsave(&ctrl->state_lock, flags); */ if (ctrl->vstate != VFE_STATE_ACTIVE) - goto awb_stats_done; + return FALSE; - msg->_d = VFE_MSG_ID_STATS_WB_EXP; msg->_u.msgStatsWbExp.awbBuffer = awbBufAddress; msg->_u.msgStatsWbExp.frameCounter = ctrl->vfeFrameId; - vfe_proc_ops(VFE_MSG_ID_STATS_WB_EXP, - msg, sizeof(struct vfe_message)); - ctrl->awbStatsControl.ackPending = TRUE; -awb_stats_done: - /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ - return; + vfe_addr_convert(&(rp->phy), + rp->type, msg, + NULL, NULL); + + return TRUE; } static void vfe_process_stats_awb_irq(void) @@ -1077,64 +1009,25 @@ static void vfe_process_stats_awb_irq(void) if (!(ctrl->awbStatsControl.ackPending)) { ctrl->awbStatsControl.pingPongStatus = - vfe_get_awb_pingpong_status(); + vfe_get_awb_pingpong_status(); bufferAvailable = (ctrl->awbStatsControl.pingPongStatus) ^ 1; ctrl->awbStatsControl.bufToRender = - vfe_read_awb_buf_addr(bufferAvailable); + vfe_read_awb_buf_addr(bufferAvailable); vfe_update_awb_buf_addr(bufferAvailable, ctrl->awbStatsControl.nextFrameAddrBuf); - vfe_send_awb_stats_msg(ctrl->awbStatsControl.bufToRender); + vfe_proc_ops(VFE_MSG_ID_STATS_WB_EXP, + (void *)ctrl->awbStatsControl.bufToRender); } else ctrl->awbStatsControl.droppedStatsFrameCount++; } -static void vfe_process_sync_timer_irq( - struct vfe_interrupt_status *irqstatus) -{ - if (irqstatus->syncTimer0Irq) - vfe_send_msg_no_payload(VFE_MSG_ID_SYNC_TIMER0_DONE); - - if (irqstatus->syncTimer1Irq) - vfe_send_msg_no_payload(VFE_MSG_ID_SYNC_TIMER1_DONE); - - if (irqstatus->syncTimer2Irq) - vfe_send_msg_no_payload(VFE_MSG_ID_SYNC_TIMER2_DONE); -} - -static void vfe_process_async_timer_irq( - struct vfe_interrupt_status *irqstatus) -{ - - if (irqstatus->asyncTimer0Irq) - vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER0_DONE); - - if (irqstatus->asyncTimer1Irq) - vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER1_DONE); - - if (irqstatus->asyncTimer2Irq) - vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER2_DONE); - - if (irqstatus->asyncTimer3Irq) - vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER3_DONE); -} - -static void vfe_send_violation_msg(void) -{ - vfe_send_msg_no_payload(VFE_MSG_ID_VIOLATION); -} - -static void vfe_send_async_timer_msg(void) -{ - vfe_send_msg_no_payload(VFE_MSG_ID_ASYNC_TIMER0_DONE); -} - static void vfe_write_gamma_table(uint8_t channel, - boolean bank, int16_t *pTable) + boolean bank, int16_t *pTable) { uint16_t i; @@ -1169,12 +1062,12 @@ static void vfe_write_gamma_table(uint8_t channel, vfe_program_dmi_cfg(dmiRamSel); for (i = 0; i < VFE_GAMMA_TABLE_LENGTH; i++) { - writel((uint32_t)(*pTable), ctrl->vfebase + VFE_DMI_DATA_LO); + writel((uint32_t) (*pTable), ctrl->vfebase + VFE_DMI_DATA_LO); pTable++; } /* After DMI transfer, need to set the DMI_CFG to unselect any SRAM - unselect the SRAM Bank. */ + unselect the SRAM Bank. */ writel(VFE_DMI_CFG_DEFAULT, ctrl->vfebase + VFE_DMI_CFG); } @@ -1189,15 +1082,16 @@ static inline void vfe_read_irq_status(struct vfe_irq_thread_msg *out) memset(out, 0, sizeof(struct vfe_irq_thread_msg)); - temp = (uint32_t *)(ctrl->vfebase + VFE_IRQ_STATUS); + temp = (uint32_t *) (ctrl->vfebase + VFE_IRQ_STATUS); out->vfeIrqStatus = readl(temp); - temp = (uint32_t *)(ctrl->vfebase + CAMIF_STATUS); + temp = (uint32_t *) (ctrl->vfebase + CAMIF_STATUS); out->camifStatus = readl(temp); +#if 0 /*this for YUV performance tuning */ writel(0x7, ctrl->vfebase + CAMIF_COMMAND); writel(0x3, ctrl->vfebase + CAMIF_COMMAND); CDBG("camifStatus = 0x%x\n", out->camifStatus); - +#endif /* temp = (uint32_t *)(ctrl->vfebase + VFE_DEMOSAIC_STATUS); out->demosaicStatus = readl(temp); @@ -1209,252 +1103,213 @@ static inline void vfe_read_irq_status(struct vfe_irq_thread_msg *out) */ #if 0 - out->pmInfo.encPathPmInfo.yWrPmStats0 = readl(temp++); - out->pmInfo.encPathPmInfo.yWrPmStats1 = readl(temp++); - out->pmInfo.encPathPmInfo.cbcrWrPmStats0 = readl(temp++); - out->pmInfo.encPathPmInfo.cbcrWrPmStats1 = readl(temp++); - out->pmInfo.viewPathPmInfo.yWrPmStats0 = readl(temp++); - out->pmInfo.viewPathPmInfo.yWrPmStats1 = readl(temp++); - out->pmInfo.viewPathPmInfo.cbcrWrPmStats0 = readl(temp++); - out->pmInfo.viewPathPmInfo.cbcrWrPmStats1 = readl(temp); + out->pmInfo.encPathPmInfo.yWrPmStats0 = readl(temp++); + out->pmInfo.encPathPmInfo.yWrPmStats1 = readl(temp++); + out->pmInfo.encPathPmInfo.cbcrWrPmStats0 = readl(temp++); + out->pmInfo.encPathPmInfo.cbcrWrPmStats1 = readl(temp++); + out->pmInfo.viewPathPmInfo.yWrPmStats0 = readl(temp++); + out->pmInfo.viewPathPmInfo.yWrPmStats1 = readl(temp++); + out->pmInfo.viewPathPmInfo.cbcrWrPmStats0 = readl(temp++); + out->pmInfo.viewPathPmInfo.cbcrWrPmStats1 = readl(temp); #endif /* if 0 Jeff */ } -static struct vfe_interrupt_status -vfe_parse_interrupt_status(uint32_t irqStatusIn) +static void +vfe_parse_interrupt_status(struct vfe_interrupt_status *ret, uint32_t irqStatusIn) { struct vfe_irqenable hwstat; - struct vfe_interrupt_status ret; boolean temp; memset(&hwstat, 0, sizeof(hwstat)); - memset(&ret, 0, sizeof(ret)); + memset(ret, 0, sizeof(*ret)); hwstat = *((struct vfe_irqenable *)(&irqStatusIn)); - ret.camifErrorIrq = hwstat.camifErrorIrq; - ret.camifSofIrq = hwstat.camifSofIrq; - ret.camifEolIrq = hwstat.camifEolIrq; - ret.camifEofIrq = hwstat.camifEofIrq; - ret.camifEpoch1Irq = hwstat.camifEpoch1Irq; - ret.camifEpoch2Irq = hwstat.camifEpoch2Irq; - ret.camifOverflowIrq = hwstat.camifOverflowIrq; - ret.ceIrq = hwstat.ceIrq; - ret.regUpdateIrq = hwstat.regUpdateIrq; - ret.resetAckIrq = hwstat.resetAckIrq; - ret.encYPingpongIrq = hwstat.encYPingpongIrq; - ret.encCbcrPingpongIrq = hwstat.encCbcrPingpongIrq; - ret.viewYPingpongIrq = hwstat.viewYPingpongIrq; - ret.viewCbcrPingpongIrq = hwstat.viewCbcrPingpongIrq; - ret.rdPingpongIrq = hwstat.rdPingpongIrq; - ret.afPingpongIrq = hwstat.afPingpongIrq; - ret.awbPingpongIrq = hwstat.awbPingpongIrq; - ret.histPingpongIrq = hwstat.histPingpongIrq; - ret.encIrq = hwstat.encIrq; - ret.viewIrq = hwstat.viewIrq; - ret.busOverflowIrq = hwstat.busOverflowIrq; - ret.afOverflowIrq = hwstat.afOverflowIrq; - ret.awbOverflowIrq = hwstat.awbOverflowIrq; - ret.syncTimer0Irq = hwstat.syncTimer0Irq; - ret.syncTimer1Irq = hwstat.syncTimer1Irq; - ret.syncTimer2Irq = hwstat.syncTimer2Irq; - ret.asyncTimer0Irq = hwstat.asyncTimer0Irq; - ret.asyncTimer1Irq = hwstat.asyncTimer1Irq; - ret.asyncTimer2Irq = hwstat.asyncTimer2Irq; - ret.asyncTimer3Irq = hwstat.asyncTimer3Irq; - ret.axiErrorIrq = hwstat.axiErrorIrq; - ret.violationIrq = hwstat.violationIrq; + ret->camifErrorIrq = hwstat.camifErrorIrq; + ret->camifSofIrq = hwstat.camifSofIrq; + ret->camifEolIrq = hwstat.camifEolIrq; + ret->camifEofIrq = hwstat.camifEofIrq; + ret->camifEpoch1Irq = hwstat.camifEpoch1Irq; + ret->camifEpoch2Irq = hwstat.camifEpoch2Irq; + ret->camifOverflowIrq = hwstat.camifOverflowIrq; + ret->ceIrq = hwstat.ceIrq; + ret->regUpdateIrq = hwstat.regUpdateIrq; + ret->resetAckIrq = hwstat.resetAckIrq; + ret->encYPingpongIrq = hwstat.encYPingpongIrq; + ret->encCbcrPingpongIrq = hwstat.encCbcrPingpongIrq; + ret->viewYPingpongIrq = hwstat.viewYPingpongIrq; + ret->viewCbcrPingpongIrq = hwstat.viewCbcrPingpongIrq; + ret->rdPingpongIrq = hwstat.rdPingpongIrq; + ret->afPingpongIrq = hwstat.afPingpongIrq; + ret->awbPingpongIrq = hwstat.awbPingpongIrq; + ret->histPingpongIrq = hwstat.histPingpongIrq; + ret->encIrq = hwstat.encIrq; + ret->viewIrq = hwstat.viewIrq; + ret->busOverflowIrq = hwstat.busOverflowIrq; + ret->afOverflowIrq = hwstat.afOverflowIrq; + ret->awbOverflowIrq = hwstat.awbOverflowIrq; + ret->syncTimer0Irq = hwstat.syncTimer0Irq; + ret->syncTimer1Irq = hwstat.syncTimer1Irq; + ret->syncTimer2Irq = hwstat.syncTimer2Irq; + ret->asyncTimer0Irq = hwstat.asyncTimer0Irq; + ret->asyncTimer1Irq = hwstat.asyncTimer1Irq; + ret->asyncTimer2Irq = hwstat.asyncTimer2Irq; + ret->asyncTimer3Irq = hwstat.asyncTimer3Irq; + ret->axiErrorIrq = hwstat.axiErrorIrq; + ret->violationIrq = hwstat.violationIrq; /* logic OR of any error bits * although each irq corresponds to a bit, the data type here is a * boolean already. hence use logic operation. */ temp = - ret.camifErrorIrq || - ret.camifOverflowIrq || - ret.afOverflowIrq || - ret.awbPingpongIrq || - ret.busOverflowIrq || - ret.axiErrorIrq || - ret.violationIrq; + ret->camifErrorIrq || + ret->camifOverflowIrq || + ret->afOverflowIrq || + ret->awbOverflowIrq || + ret->awbPingpongIrq || + ret->afPingpongIrq || + ret->busOverflowIrq || ret->axiErrorIrq || ret->violationIrq; - ret.anyErrorIrqs = temp; + ret->anyErrorIrqs = temp; - /* logic OR of any output path bits*/ - temp = - ret.encYPingpongIrq || - ret.encCbcrPingpongIrq || - ret.encIrq; + /* logic OR of any output path bits */ + temp = ret->encYPingpongIrq || ret->encCbcrPingpongIrq || ret->encIrq; - ret.anyOutput2PathIrqs = temp; + ret->anyOutput2PathIrqs = temp; - temp = - ret.viewYPingpongIrq || - ret.viewCbcrPingpongIrq || - ret.viewIrq; + temp = ret->viewYPingpongIrq || ret->viewCbcrPingpongIrq || ret->viewIrq; - ret.anyOutput1PathIrqs = temp; + ret->anyOutput1PathIrqs = temp; - ret.anyOutputPathIrqs = - ret.anyOutput1PathIrqs || - ret.anyOutput2PathIrqs; + ret->anyOutputPathIrqs = + ret->anyOutput1PathIrqs || ret->anyOutput2PathIrqs; - /* logic OR of any sync timer bits*/ - temp = - ret.syncTimer0Irq || - ret.syncTimer1Irq || - ret.syncTimer2Irq; + /* logic OR of any sync timer bits */ + temp = ret->syncTimer0Irq || ret->syncTimer1Irq || ret->syncTimer2Irq; - ret.anySyncTimerIrqs = temp; + ret->anySyncTimerIrqs = temp; - /* logic OR of any async timer bits*/ + /* logic OR of any async timer bits */ temp = - ret.asyncTimer0Irq || - ret.asyncTimer1Irq || - ret.asyncTimer2Irq || - ret.asyncTimer3Irq; + ret->asyncTimer0Irq || + ret->asyncTimer1Irq || ret->asyncTimer2Irq || ret->asyncTimer3Irq; - ret.anyAsyncTimerIrqs = temp; + ret->anyAsyncTimerIrqs = temp; /* bool for all interrupts that are not allowed in idle state */ temp = - ret.anyErrorIrqs || - ret.anyOutputPathIrqs || - ret.anySyncTimerIrqs || - ret.regUpdateIrq || - ret.awbPingpongIrq || - ret.afPingpongIrq || - ret.camifSofIrq || - ret.camifEpoch2Irq || - ret.camifEpoch1Irq; - - ret.anyIrqForActiveStatesOnly = - temp; + ret->anyErrorIrqs || + ret->anyOutputPathIrqs || + ret->anySyncTimerIrqs || + ret->regUpdateIrq || + ret->awbPingpongIrq || + ret->afPingpongIrq || + ret->camifSofIrq || ret->camifEpoch2Irq || ret->camifEpoch1Irq; - return ret; + ret->anyIrqForActiveStatesOnly = temp; } -static struct vfe_frame_asf_info -vfe_get_asf_frame_info(struct vfe_irq_thread_msg *in) +static void +vfe_get_asf_frame_info(struct vfe_frame_asf_info *rc, struct vfe_irq_thread_msg *in) { - struct vfe_asf_info asfInfoTemp; - struct vfe_frame_asf_info rc; + struct vfe_asf_info asfInfoTemp; - memset(&rc, 0, sizeof(rc)); + memset(rc, 0, sizeof(*rc)); memset(&asfInfoTemp, 0, sizeof(asfInfoTemp)); - asfInfoTemp = - *((struct vfe_asf_info *)(&(in->asfMaxEdge))); - - rc.asfHbiCount = asfInfoTemp.HBICount; - rc.asfMaxEdge = asfInfoTemp.maxEdge; + asfInfoTemp = *((struct vfe_asf_info *)(&(in->asfMaxEdge))); - return rc; + rc->asfHbiCount = asfInfoTemp.HBICount; + rc->asfMaxEdge = asfInfoTemp.maxEdge; } -static struct vfe_frame_bpc_info -vfe_get_demosaic_frame_info(struct vfe_irq_thread_msg *in) +static void +vfe_get_demosaic_frame_info(struct vfe_frame_bpc_info *rc, struct vfe_irq_thread_msg *in) { - struct vfe_bps_info bpcInfoTemp; - struct vfe_frame_bpc_info rc; + struct vfe_bps_info bpcInfoTemp; - memset(&rc, 0, sizeof(rc)); + memset(rc, 0, sizeof(*rc)); memset(&bpcInfoTemp, 0, sizeof(bpcInfoTemp)); - bpcInfoTemp = - *((struct vfe_bps_info *)(&(in->demosaicStatus))); - - rc.greenDefectPixelCount = - bpcInfoTemp.greenBadPixelCount; + bpcInfoTemp = *((struct vfe_bps_info *)(&(in->demosaicStatus))); - rc.redBlueDefectPixelCount = - bpcInfoTemp.RedBlueBadPixelCount; + rc->greenDefectPixelCount = bpcInfoTemp.greenBadPixelCount; - return rc; + rc->redBlueDefectPixelCount = bpcInfoTemp.RedBlueBadPixelCount; } -static struct vfe_msg_camif_status -vfe_get_camif_status(struct vfe_irq_thread_msg *in) +static void +vfe_get_camif_status(struct vfe_msg_camif_status *rc, struct vfe_irq_thread_msg *in) { struct vfe_camif_stats camifStatusTemp; - struct vfe_msg_camif_status rc; - memset(&rc, 0, sizeof(rc)); + memset(rc, 0, sizeof(*rc)); memset(&camifStatusTemp, 0, sizeof(camifStatusTemp)); - camifStatusTemp = - *((struct vfe_camif_stats *)(&(in->camifStatus))); - - rc.camifState = (boolean)camifStatusTemp.camifHalt; - rc.lineCount = camifStatusTemp.lineCount; - rc.pixelCount = camifStatusTemp.pixelCount; + camifStatusTemp = *((struct vfe_camif_stats *)(&(in->camifStatus))); - return rc; + rc->camifState = (boolean) camifStatusTemp.camifHalt; + rc->lineCount = camifStatusTemp.lineCount; + rc->pixelCount = camifStatusTemp.pixelCount; } -static struct vfe_bus_performance_monitor -vfe_get_performance_monitor_data(struct vfe_irq_thread_msg *in) -{ - struct vfe_bus_performance_monitor rc; - memset(&rc, 0, sizeof(rc)); - - rc.encPathPmInfo.yWrPmStats0 = - in->pmInfo.encPathPmInfo.yWrPmStats0; - rc.encPathPmInfo.yWrPmStats1 = - in->pmInfo.encPathPmInfo.yWrPmStats1; - rc.encPathPmInfo.cbcrWrPmStats0 = - in->pmInfo.encPathPmInfo.cbcrWrPmStats0; - rc.encPathPmInfo.cbcrWrPmStats1 = - in->pmInfo.encPathPmInfo.cbcrWrPmStats1; - rc.viewPathPmInfo.yWrPmStats0 = - in->pmInfo.viewPathPmInfo.yWrPmStats0; - rc.viewPathPmInfo.yWrPmStats1 = - in->pmInfo.viewPathPmInfo.yWrPmStats1; - rc.viewPathPmInfo.cbcrWrPmStats0 = - in->pmInfo.viewPathPmInfo.cbcrWrPmStats0; - rc.viewPathPmInfo.cbcrWrPmStats1 = - in->pmInfo.viewPathPmInfo.cbcrWrPmStats1; +static void +vfe_get_performance_monitor_data(struct vfe_bus_performance_monitor *rc, + struct vfe_irq_thread_msg *in) +{ + memset(rc, 0, sizeof(*rc)); - return rc; + rc->encPathPmInfo.yWrPmStats0 = in->pmInfo.encPathPmInfo.yWrPmStats0; + rc->encPathPmInfo.yWrPmStats1 = in->pmInfo.encPathPmInfo.yWrPmStats1; + rc->encPathPmInfo.cbcrWrPmStats0 = + in->pmInfo.encPathPmInfo.cbcrWrPmStats0; + rc->encPathPmInfo.cbcrWrPmStats1 = + in->pmInfo.encPathPmInfo.cbcrWrPmStats1; + rc->viewPathPmInfo.yWrPmStats0 = in->pmInfo.viewPathPmInfo.yWrPmStats0; + rc->viewPathPmInfo.yWrPmStats1 = in->pmInfo.viewPathPmInfo.yWrPmStats1; + rc->viewPathPmInfo.cbcrWrPmStats0 = + in->pmInfo.viewPathPmInfo.cbcrWrPmStats0; + rc->viewPathPmInfo.cbcrWrPmStats1 = + in->pmInfo.viewPathPmInfo.cbcrWrPmStats1; } static void vfe_process_reg_update_irq(void) { CDBG("vfe_process_reg_update_irq: ackPendingFlag is %d\n", - ctrl->vfeStartAckPendingFlag); + ctrl->vfeStartAckPendingFlag); if (ctrl->vfeStartAckPendingFlag == TRUE) { - vfe_send_msg_no_payload(VFE_MSG_ID_START_ACK); + vfe_proc_ops(VFE_MSG_ID_START_ACK, NULL); ctrl->vfeStartAckPendingFlag = FALSE; } else - vfe_send_msg_no_payload(VFE_MSG_ID_UPDATE_ACK); + vfe_proc_ops(VFE_MSG_ID_UPDATE_ACK, NULL); } static void vfe_process_reset_irq(void) { /* unsigned long flags; */ - /* @todo This is causing issues, need further investigate */ - /* spin_lock_irqsave(&ctrl->state_lock, flags); */ ctrl->vstate = VFE_STATE_IDLE; - /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ if (ctrl->vfeStopAckPending == TRUE) { ctrl->vfeStopAckPending = FALSE; - vfe_send_msg_no_payload(VFE_MSG_ID_STOP_ACK); + /* disable all irqs when got stop ack from VFE */ + vfe_program_irq_mask(VFE_DISABLE_ALL_IRQS); + vfe_proc_ops(VFE_MSG_ID_STOP_ACK, NULL); } else { vfe_set_default_reg_values(); - vfe_send_msg_no_payload(VFE_MSG_ID_RESET_ACK); + vfe_proc_ops(VFE_MSG_ID_RESET_ACK, NULL); } } static void vfe_process_pingpong_irq(struct vfe_output_path *in, - uint8_t fragmentCount) + uint8_t fragmentCount) { uint16_t circularIndex; uint32_t nextFragmentAddr; /* get next fragment address from circular buffer */ - circularIndex = (in->fragIndex) % (2 * fragmentCount); + circularIndex = (in->fragIndex) % (2 * fragmentCount); nextFragmentAddr = in->addressBuffer[circularIndex]; in->fragIndex = circularIndex + 1; @@ -1470,78 +1325,54 @@ static void vfe_process_pingpong_irq(struct vfe_output_path *in, } } -static void vfe_send_output2_msg( - struct vfe_msg_output *pPayload) +static boolean vfe_send_output2_msg(struct msm_vfe_resp *rp, + struct vfe_message *msg, void *data) { - /* unsigned long flags; */ - struct vfe_message *msg; - - msg = kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); - if (!msg) - return; + struct vfe_msg_output *pPayload = data; - /* fill message with right content. */ - /* @todo This is causing issues, need further investigate */ - /* spin_lock_irqsave(&ctrl->state_lock, flags); */ if (ctrl->vstate != VFE_STATE_ACTIVE) - goto output2_msg_done; - - msg->_d = VFE_MSG_ID_OUTPUT2; + return FALSE; memcpy(&(msg->_u.msgOutput2), - (void *)pPayload, sizeof(struct vfe_msg_output)); - - vfe_proc_ops(VFE_MSG_ID_OUTPUT2, - msg, sizeof(struct vfe_message)); + (void *)pPayload, sizeof(struct vfe_msg_output)); ctrl->encPath.ackPending = TRUE; if (!(ctrl->vfeRequestedSnapShotCount <= 3) && - (ctrl->vfeOperationMode == - VFE_START_OPERATION_MODE_SNAPSHOT)) + (ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT)) ctrl->encPath.ackPending = TRUE; -output2_msg_done: - /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ - return; + vfe_addr_convert(&(rp->phy), + rp->type, msg, + &(rp->extdata), &(rp->extlen)); + return TRUE; } -static void vfe_send_output1_msg( - struct vfe_msg_output *pPayload) +static boolean vfe_send_output1_msg(struct msm_vfe_resp *rp, + struct vfe_message *msg, void *data) { - /* unsigned long flags; */ - struct vfe_message *msg; + struct vfe_msg_output *pPayload = data; - msg = kzalloc(sizeof(struct vfe_message), GFP_ATOMIC); - if (!msg) - return; - - /* @todo This is causing issues, need further investigate */ - /* spin_lock_irqsave(&ctrl->state_lock, flags); */ if (ctrl->vstate != VFE_STATE_ACTIVE) - goto output1_msg_done; - - msg->_d = VFE_MSG_ID_OUTPUT1; - memmove(&(msg->_u), - (void *)pPayload, sizeof(struct vfe_msg_output)); + return FALSE; - vfe_proc_ops(VFE_MSG_ID_OUTPUT1, - msg, sizeof(struct vfe_message)); + memcpy(&(msg->_u), (void *)pPayload, sizeof(struct vfe_msg_output)); ctrl->viewPath.ackPending = TRUE; if (!(ctrl->vfeRequestedSnapShotCount <= 3) && - (ctrl->vfeOperationMode == - VFE_START_OPERATION_MODE_SNAPSHOT)) + (ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT)) ctrl->viewPath.ackPending = TRUE; -output1_msg_done: - /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ - return; + vfe_addr_convert(&(rp->phy), + rp->type, msg, + &(rp->extdata), &(rp->extlen)); + + return TRUE; } static void vfe_send_output_msg(boolean whichOutputPath, - uint32_t yPathAddr, uint32_t cbcrPathAddr) + uint32_t yPathAddr, uint32_t cbcrPathAddr) { struct vfe_msg_output msgPayload; @@ -1555,9 +1386,9 @@ static void vfe_send_output_msg(boolean whichOutputPath, /* demosaic info is common for both output1 and output2 */ msgPayload.bpcInfo.greenDefectPixelCount = - ctrl->vfeBpcFrameInfo.greenDefectPixelCount; + ctrl->vfeBpcFrameInfo.greenDefectPixelCount; msgPayload.bpcInfo.redBlueDefectPixelCount = - ctrl->vfeBpcFrameInfo.redBlueDefectPixelCount; + ctrl->vfeBpcFrameInfo.redBlueDefectPixelCount; #endif /* if 0 */ /* frame ID is common for both paths. */ @@ -1565,15 +1396,15 @@ static void vfe_send_output_msg(boolean whichOutputPath, if (whichOutputPath) { /* msgPayload.pmData = ctrl->vfePmData.encPathPmInfo; */ - vfe_send_output2_msg(&msgPayload); + vfe_proc_ops(VFE_MSG_ID_OUTPUT2, &msgPayload); } else { /* msgPayload.pmData = ctrl->vfePmData.viewPathPmInfo; */ - vfe_send_output1_msg(&msgPayload); + vfe_proc_ops(VFE_MSG_ID_OUTPUT1, &msgPayload); } } -static void vfe_process_frame_done_irq_multi_frag( - struct vfe_output_path_combo *in) +static void vfe_process_frame_done_irq_multi_frag(struct vfe_output_path_combo + *in) { uint32_t yAddress, cbcrAddress; uint16_t idx; @@ -1591,9 +1422,9 @@ static void vfe_process_frame_done_irq_multi_frag( cbcrAddress = in->cbcrPath.addressBuffer[idx]; /* copy next frame to current frame. */ - ptrSrc = in->nextFrameAddrBuf; - ptrY = (uint32_t *)&(in->yPath.addressBuffer[idx]); - ptrCbcr = (uint32_t *)&(in->cbcrPath.addressBuffer[idx]); + ptrSrc = in->nextFrameAddrBuf; + ptrY = (uint32_t *)&in->yPath.addressBuffer[idx]; + ptrCbcr = (uint32_t *)&in->cbcrPath.addressBuffer[idx]; /* Copy Y address */ for (i = 0; i < in->fragCount; i++) @@ -1614,28 +1445,28 @@ static void vfe_process_frame_done_irq_multi_frag( } /* toggle current frame. */ - in->currentFrame = in->currentFrame^1; + in->currentFrame = in->currentFrame ^ 1; if (ctrl->vfeOperationMode) in->snapshotPendingCount--; } static void vfe_process_frame_done_irq_no_frag_io( - struct vfe_output_path_combo *in, uint32_t *pNextAddr, - uint32_t *pdestRenderAddr) + struct vfe_output_path_combo *in, + uint32_t *pNextAddr, + uint32_t *pdestRenderAddr) { uint32_t busPingPongStatus; uint32_t tempAddress; /* 1. read hw status register. */ - busPingPongStatus = - readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS); + busPingPongStatus = readl(ctrl->vfebase + VFE_BUS_PINGPONG_STATUS); CDBG("hardware status is 0x%x\n", busPingPongStatus); /* 2. determine ping or pong */ /* use cbcr status */ - busPingPongStatus = busPingPongStatus & (1<<(in->cbcrStatusBit)); + busPingPongStatus = busPingPongStatus & (1 << (in->cbcrStatusBit)); /* 3. read out address and update address */ if (busPingPongStatus == 0) { @@ -1674,22 +1505,18 @@ static void vfe_process_frame_done_irq_no_frag_io( } } -static void vfe_process_frame_done_irq_no_frag( - struct vfe_output_path_combo *in) +static void vfe_process_frame_done_irq_no_frag(struct vfe_output_path_combo *in) { uint32_t addressToRender[2]; - static uint32_t fcnt; - - if (fcnt++ < 3) - return; if (!in->ackPending) { vfe_process_frame_done_irq_no_frag_io(in, - in->nextFrameAddrBuf, addressToRender); + in->nextFrameAddrBuf, + addressToRender); /* use addressToRender to send out message. */ vfe_send_output_msg(in->whichOutputPath, - addressToRender[0], addressToRender[1]); + addressToRender[0], addressToRender[1]); } else { /* ackPending is still there, accumulate dropped frame count. @@ -1713,8 +1540,7 @@ static void vfe_process_frame_done_irq_no_frag( in->snapshotPendingCount--; } -static void vfe_process_output_path_irq( - struct vfe_interrupt_status *irqstatus) +static void vfe_process_output_path_irq(struct vfe_interrupt_status *irqstatus) { /* unsigned long flags; */ @@ -1723,25 +1549,28 @@ static void vfe_process_output_path_irq( if (ctrl->viewPath.multiFrag) { if (irqstatus->viewCbcrPingpongIrq) - vfe_process_pingpong_irq( - &(ctrl->viewPath.cbcrPath), - ctrl->viewPath.fragCount); + vfe_process_pingpong_irq(& + (ctrl->viewPath. + cbcrPath), + ctrl->viewPath. + fragCount); if (irqstatus->viewYPingpongIrq) - vfe_process_pingpong_irq( - &(ctrl->viewPath.yPath), - ctrl->viewPath.fragCount); + vfe_process_pingpong_irq(& + (ctrl->viewPath.yPath), + ctrl->viewPath. + fragCount); if (irqstatus->viewIrq) - vfe_process_frame_done_irq_multi_frag( - &ctrl->viewPath); + vfe_process_frame_done_irq_multi_frag(&ctrl-> + viewPath); } else { /* typical case for no fragment, - only frame done irq is enabled. */ + only frame done irq is enabled. */ if (irqstatus->viewIrq) - vfe_process_frame_done_irq_no_frag( - &ctrl->viewPath); + vfe_process_frame_done_irq_no_frag(&ctrl-> + viewPath); } } @@ -1749,89 +1578,68 @@ static void vfe_process_output_path_irq( if (irqstatus->anyOutput2PathIrqs) { if (ctrl->encPath.multiFrag) { if (irqstatus->encCbcrPingpongIrq) - vfe_process_pingpong_irq( - &(ctrl->encPath.cbcrPath), - ctrl->encPath.fragCount); + vfe_process_pingpong_irq(& + (ctrl->encPath. + cbcrPath), + ctrl->encPath. + fragCount); if (irqstatus->encYPingpongIrq) vfe_process_pingpong_irq(&(ctrl->encPath.yPath), - ctrl->encPath.fragCount); + ctrl->encPath. + fragCount); if (irqstatus->encIrq) - vfe_process_frame_done_irq_multi_frag( - &ctrl->encPath); + vfe_process_frame_done_irq_multi_frag(&ctrl-> + encPath); } else { if (irqstatus->encIrq) - vfe_process_frame_done_irq_no_frag( - &ctrl->encPath); + vfe_process_frame_done_irq_no_frag(&ctrl-> + encPath); } } if (ctrl->vfeOperationMode) { if ((ctrl->encPath.snapshotPendingCount == 0) && - (ctrl->viewPath.snapshotPendingCount == 0)) { + (ctrl->viewPath.snapshotPendingCount == 0)) { - /* @todo This is causing issues, further investigate */ - /* spin_lock_irqsave(&ctrl->state_lock, flags); */ ctrl->vstate = VFE_STATE_IDLE; - /* spin_unlock_irqrestore(&ctrl->state_lock, flags); */ - vfe_send_msg_no_payload(VFE_MSG_ID_SNAPSHOT_DONE); + vfe_proc_ops(VFE_MSG_ID_SNAPSHOT_DONE, NULL); vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_STOP); vfe_pm_stop(); } } } -static void vfe_do_tasklet(unsigned long data) +static void __vfe_do_tasklet(struct isr_queue_cmd *qcmd) { - unsigned long flags; - - struct isr_queue_cmd *qcmd = NULL; - - CDBG("=== vfe_do_tasklet start === \n"); - - spin_lock_irqsave(&ctrl->tasklet_lock, flags); - qcmd = list_first_entry(&ctrl->tasklet_q, - struct isr_queue_cmd, list); - - if (!qcmd) { - spin_unlock_irqrestore(&ctrl->tasklet_lock, flags); - return; - } - - list_del(&qcmd->list); - spin_unlock_irqrestore(&ctrl->tasklet_lock, flags); - if (qcmd->vfeInterruptStatus.regUpdateIrq) { - CDBG("irq regUpdateIrq\n"); + CDBG("irq regUpdateIrq\n"); vfe_process_reg_update_irq(); } if (qcmd->vfeInterruptStatus.resetAckIrq) { - CDBG("irq resetAckIrq\n"); + CDBG("%s: process resetAckIrq\n", __func__); vfe_process_reset_irq(); } - spin_lock_irqsave(&ctrl->state_lock, flags); - if (ctrl->vstate != VFE_STATE_ACTIVE) { - spin_unlock_irqrestore(&ctrl->state_lock, flags); + if (ctrl->vstate != VFE_STATE_ACTIVE) return; + + if (qcmd->vfeInterruptStatus.camifEpoch1Irq) { + vfe_process_camif_epoch1_irq(); } - spin_unlock_irqrestore(&ctrl->state_lock, flags); #if 0 - if (qcmd->vfeInterruptStatus.camifEpoch1Irq) - vfe_send_msg_no_payload(VFE_MSG_ID_EPOCH1); - if (qcmd->vfeInterruptStatus.camifEpoch2Irq) - vfe_send_msg_no_payload(VFE_MSG_ID_EPOCH2); -#endif /* Jeff */ + vfe_proc_ops(VFE_MSG_ID_EPOCH2); +#endif /* next, check output path related interrupts. */ if (qcmd->vfeInterruptStatus.anyOutputPathIrqs) { - CDBG("irq anyOutputPathIrqs\n"); + CDBG("irq: anyOutputPathIrqs\n"); vfe_process_output_path_irq(&qcmd->vfeInterruptStatus); } @@ -1841,9 +1649,9 @@ static void vfe_do_tasklet(unsigned long data) if (qcmd->vfeInterruptStatus.awbPingpongIrq) vfe_process_stats_awb_irq(); - /* any error irqs*/ + /* any error irqs */ if (qcmd->vfeInterruptStatus.anyErrorIrqs) - vfe_process_error_irq(&qcmd->vfeInterruptStatus); + vfe_process_error_irq(qcmd); #if 0 if (qcmd->vfeInterruptStatus.anySyncTimerIrqs) @@ -1851,15 +1659,69 @@ static void vfe_do_tasklet(unsigned long data) if (qcmd->vfeInterruptStatus.anyAsyncTimerIrqs) vfe_process_async_timer_irq(); -#endif /* Jeff */ +#endif if (qcmd->vfeInterruptStatus.camifSofIrq) { - CDBG("irq camifSofIrq\n"); + CDBG("irq: camifSofIrq\n"); vfe_process_camif_sof_irq(); } +} + +static struct isr_queue_cmd *get_irq_cmd_nosync(void) +{ + int old_get = ctrl->irq_get++; + ctrl->irq_get = ctrl->irq_get % ARRAY_SIZE(ctrl->irqs); + if (ctrl->irq_get == ctrl->irq_put) { + pr_err("%s: out of irq command packets\n", __func__); + ctrl->irq_get = old_get; + return NULL; + } + + return ctrl->irqs + old_get; +} + +static struct isr_queue_cmd *next_irq_cmd(void) +{ + unsigned long flags; + struct isr_queue_cmd *cmd; + spin_lock_irqsave(&ctrl->irqs_lock, flags); + if (ctrl->irq_get == ctrl->irq_put) { + spin_unlock_irqrestore(&ctrl->irqs_lock, flags); + return NULL; /* already empty */ + } + cmd = ctrl->irqs + ctrl->irq_put; + spin_unlock_irqrestore(&ctrl->irqs_lock, flags); + return cmd; +} + +static void put_irq_cmd(void) +{ + unsigned long flags; + spin_lock_irqsave(&ctrl->irqs_lock, flags); + if (ctrl->irq_get == ctrl->irq_put) { + spin_unlock_irqrestore(&ctrl->irqs_lock, flags); + return; /* already empty */ + } + ctrl->irq_put++; + ctrl->irq_put %= ARRAY_SIZE(ctrl->irqs); + spin_unlock_irqrestore(&ctrl->irqs_lock, flags); +} + +static void vfe_do_tasklet(unsigned long data) +{ + int cnt = 0; + struct isr_queue_cmd *qcmd = NULL; + + CDBG("%s\n", __func__); + + while ((qcmd = next_irq_cmd())) { + __vfe_do_tasklet(qcmd); + put_irq_cmd(); + cnt++; + } - kfree(qcmd); - CDBG("=== vfe_do_tasklet end === \n"); + if (cnt > 1) + pr_info("%s: serviced %d vfe interrupts\n", __func__, cnt); } DECLARE_TASKLET(vfe_tasklet, vfe_do_tasklet, 0); @@ -1880,112 +1742,98 @@ static irqreturn_t vfe_parse_irq(int irq_num, void *data) return IRQ_HANDLED; } - qcmd = kzalloc(sizeof(struct isr_queue_cmd), - GFP_ATOMIC); - if (!qcmd) { - CDBG("vfe_parse_irq: qcmd malloc failed!\n"); - return IRQ_HANDLED; - } - - spin_lock_irqsave(&ctrl->ack_lock, flags); - if (ctrl->vfeStopAckPending) - irqStatusLocal = - (VFE_IMASK_WHILE_STOPPING & irq.vfeIrqStatus); + irqStatusLocal = (VFE_IMASK_WHILE_STOPPING & irq.vfeIrqStatus); else irqStatusLocal = - ((ctrl->vfeImaskPacked | VFE_IMASK_ERROR_ONLY) & - irq.vfeIrqStatus); - - spin_unlock_irqrestore(&ctrl->ack_lock, flags); + ((ctrl->vfeImaskPacked | VFE_IMASK_ERROR_ONLY) & + irq.vfeIrqStatus); - /* first parse the interrupt status to local data structures. */ - qcmd->vfeInterruptStatus = vfe_parse_interrupt_status(irqStatusLocal); - qcmd->vfeAsfFrameInfo = vfe_get_asf_frame_info(&irq); - qcmd->vfeBpcFrameInfo = vfe_get_demosaic_frame_info(&irq); - qcmd->vfeCamifStatusLocal = vfe_get_camif_status(&irq); - qcmd->vfePmData = vfe_get_performance_monitor_data(&irq); - - spin_lock_irqsave(&ctrl->tasklet_lock, flags); - list_add_tail(&qcmd->list, &ctrl->tasklet_q); - spin_unlock_irqrestore(&ctrl->tasklet_lock, flags); + spin_lock_irqsave(&ctrl->irqs_lock, flags); + qcmd = get_irq_cmd_nosync(); + if (!qcmd) { + spin_unlock_irqrestore(&ctrl->irqs_lock, flags); + goto done; + } + /* parse the interrupt status to local data structures. */ + vfe_parse_interrupt_status(&qcmd->vfeInterruptStatus, irqStatusLocal); + vfe_get_asf_frame_info(&qcmd->vfeAsfFrameInfo, &irq); + vfe_get_demosaic_frame_info(&qcmd->vfeBpcFrameInfo, &irq); + vfe_get_camif_status(&qcmd->vfeCamifStatusLocal, &irq); + vfe_get_performance_monitor_data(&qcmd->vfePmData, &irq); + spin_unlock_irqrestore(&ctrl->irqs_lock, flags); tasklet_schedule(&vfe_tasklet); - /* clear the pending interrupt of the same kind.*/ +done: + /* clear the pending interrupt of the same kind. */ writel(irq.vfeIrqStatus, ctrl->vfebase + VFE_IRQ_CLEAR); - return IRQ_HANDLED; } int vfe_cmd_init(struct msm_vfe_callback *presp, - struct platform_device *pdev, void *sdata) + struct platform_device *pdev, void *sdata) { - struct resource *vfemem, *vfeirq, *vfeio; + struct resource *vfemem, *vfeirq, *vfeio; int rc; + struct msm_camera_sensor_info *s_info; + s_info = pdev->dev.platform_data; + + pdev->resource = s_info->resource; + pdev->num_resources = s_info->num_resources; vfemem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!vfemem) { - CDBG("no mem resource?\n"); + pr_err("%s: no mem resource\n", __func__); return -ENODEV; } vfeirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!vfeirq) { - CDBG("no irq resource?\n"); + pr_err("%s: no irq resource\n", __func__); return -ENODEV; } vfeio = request_mem_region(vfemem->start, - resource_size(vfemem), pdev->name); + resource_size(vfemem), pdev->name); if (!vfeio) { - CDBG("VFE region already claimed\n"); + pr_err("%s: VFE region already claimed\n", __func__); return -EBUSY; } - ctrl = - kzalloc(sizeof(struct msm_vfe8x_ctrl), GFP_KERNEL); + ctrl = kzalloc(sizeof(struct msm_vfe8x_ctrl), GFP_KERNEL); if (!ctrl) { + pr_err("%s: out of memory\n", __func__); rc = -ENOMEM; goto cmd_init_failed1; } - ctrl->vfeirq = vfeirq->start; + spin_lock_init(&ctrl->irqs_lock); + + ctrl->vfeirq = vfeirq->start; ctrl->vfebase = - ioremap(vfemem->start, (vfemem->end - vfemem->start) + 1); + ioremap(vfemem->start, (vfemem->end - vfemem->start) + 1); if (!ctrl->vfebase) { + pr_err("%s: ioremap failed\n", __func__); rc = -ENOMEM; goto cmd_init_failed2; } rc = request_irq(ctrl->vfeirq, vfe_parse_irq, - IRQF_TRIGGER_RISING, "vfe", 0); - if (rc < 0) + IRQF_TRIGGER_RISING, "vfe", 0); + if (rc < 0) { + pr_err("%s: request_irq(%d) failed\n", __func__, ctrl->vfeirq); goto cmd_init_failed2; + } if (presp && presp->vfe_resp) ctrl->resp = presp; else { - rc = -EINVAL; + pr_err("%s: no vfe_resp function\n", __func__); + rc = -EIO; goto cmd_init_failed3; } - ctrl->extdata = - kmalloc(sizeof(struct vfe_frame_extra), GFP_KERNEL); - if (!ctrl->extdata) { - rc = -ENOMEM; - goto cmd_init_failed3; - } - - spin_lock_init(&ctrl->ack_lock); - spin_lock_init(&ctrl->state_lock); - spin_lock_init(&ctrl->io_lock); - - ctrl->extlen = sizeof(struct vfe_frame_extra); - - spin_lock_init(&ctrl->tasklet_lock); - INIT_LIST_HEAD(&ctrl->tasklet_q); - ctrl->syncdata = sdata; return 0; @@ -2002,7 +1850,7 @@ int vfe_cmd_init(struct msm_vfe_callback *presp, void vfe_cmd_release(struct platform_device *dev) { - struct resource *mem; + struct resource *mem; disable_irq(ctrl->vfeirq); free_irq(ctrl->vfeirq, 0); @@ -2011,10 +1859,8 @@ void vfe_cmd_release(struct platform_device *dev) mem = platform_get_resource(dev, IORESOURCE_MEM, 0); release_mem_region(mem->start, (mem->end - mem->start) + 1); - ctrl->extlen = 0; - - kfree(ctrl->extdata); kfree(ctrl); + ctrl = 0; } void vfe_stats_af_stop(void) @@ -2025,10 +1871,10 @@ void vfe_stats_af_stop(void) void vfe_stop(void) { - boolean vfeAxiBusy; + int spin_cnt = 0; uint32_t vfeAxiStauts; - /* for reset hw modules, and send msg when reset_irq comes.*/ + /* for reset hw modules, and send msg when reset_irq comes. */ ctrl->vfeStopAckPending = TRUE; ctrl->vfeStatsPingPongReloadFlag = FALSE; @@ -2044,13 +1890,12 @@ void vfe_stop(void) vfe_program_axi_cmd(AXI_HALT); vfe_prog_hw_testgen_cmd(VFE_TEST_GEN_STOP); - vfeAxiBusy = TRUE; - - while (vfeAxiBusy) { + do { vfeAxiStauts = vfe_read_axi_status(); - if ((vfeAxiStauts & AXI_STATUS_BUSY_MASK) != 0) - vfeAxiBusy = FALSE; - } + spin_cnt++; + } while (!(vfeAxiStauts & AXI_STATUS_BUSY_MASK)); + if (spin_cnt > 1) + pr_warning("%s: spin_cnt %d\n", __func__, spin_cnt); vfe_program_axi_cmd(AXI_HALT_CLEAR); @@ -2068,8 +1913,8 @@ void vfe_stop(void) void vfe_update(void) { ctrl->vfeModuleEnableLocal.statsEnable = - ctrl->vfeStatsCmdLocal.autoFocusEnable | - ctrl->vfeStatsCmdLocal.axwEnable; + ctrl->vfeStatsCmdLocal.autoFocusEnable | + ctrl->vfeStatsCmdLocal.axwEnable; vfe_reg_module_cfg(&ctrl->vfeModuleEnableLocal); @@ -2079,7 +1924,7 @@ void vfe_update(void) vfe_program_irq_mask(ctrl->vfeImaskPacked); if ((ctrl->vfeModuleEnableLocal.statsEnable == TRUE) && - (ctrl->vfeStatsPingPongReloadFlag == FALSE)) { + (ctrl->vfeStatsPingPongReloadFlag == FALSE)) { ctrl->vfeStatsPingPongReloadFlag = TRUE; ctrl->vfeBusCmdLocal.statsPingpongReload = TRUE; @@ -2099,46 +1944,49 @@ int vfe_rgb_gamma_update(struct vfe_cmd_rgb_gamma_config *in) case RGB_GAMMA_CH0_SELECTED: ctrl->vfeGammaLutSel.ch0BankSelect ^= 1; vfe_write_gamma_table(0, - ctrl->vfeGammaLutSel.ch0BankSelect, in->table); + ctrl->vfeGammaLutSel.ch0BankSelect, + in->table); break; case RGB_GAMMA_CH1_SELECTED: ctrl->vfeGammaLutSel.ch1BankSelect ^= 1; vfe_write_gamma_table(1, - ctrl->vfeGammaLutSel.ch1BankSelect, in->table); + ctrl->vfeGammaLutSel.ch1BankSelect, + in->table); break; case RGB_GAMMA_CH2_SELECTED: ctrl->vfeGammaLutSel.ch2BankSelect ^= 1; vfe_write_gamma_table(2, - ctrl->vfeGammaLutSel.ch2BankSelect, in->table); + ctrl->vfeGammaLutSel.ch2BankSelect, + in->table); break; case RGB_GAMMA_CH0_CH1_SELECTED: ctrl->vfeGammaLutSel.ch0BankSelect ^= 1; ctrl->vfeGammaLutSel.ch1BankSelect ^= 1; vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect, - in->table); + in->table); vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect, - in->table); + in->table); break; case RGB_GAMMA_CH0_CH2_SELECTED: ctrl->vfeGammaLutSel.ch0BankSelect ^= 1; ctrl->vfeGammaLutSel.ch2BankSelect ^= 1; vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect, - in->table); + in->table); vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect, - in->table); + in->table); break; case RGB_GAMMA_CH1_CH2_SELECTED: ctrl->vfeGammaLutSel.ch1BankSelect ^= 1; ctrl->vfeGammaLutSel.ch2BankSelect ^= 1; vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect, - in->table); + in->table); vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect, - in->table); + in->table); break; case RGB_GAMMA_CH0_CH1_CH2_SELECTED: @@ -2146,16 +1994,17 @@ int vfe_rgb_gamma_update(struct vfe_cmd_rgb_gamma_config *in) ctrl->vfeGammaLutSel.ch1BankSelect ^= 1; ctrl->vfeGammaLutSel.ch2BankSelect ^= 1; vfe_write_gamma_table(0, ctrl->vfeGammaLutSel.ch0BankSelect, - in->table); + in->table); vfe_write_gamma_table(1, ctrl->vfeGammaLutSel.ch1BankSelect, - in->table); + in->table); vfe_write_gamma_table(2, ctrl->vfeGammaLutSel.ch2BankSelect, - in->table); + in->table); break; default: + pr_err("%s: invalid gamma channel %d\n", __func__, in->channelSelect); return -EINVAL; - } /* switch */ + } /* switch */ /* update the gammaLutSel register. */ vfe_program_lut_bank_sel(&ctrl->vfeGammaLutSel); @@ -2171,8 +2020,8 @@ int vfe_rgb_gamma_config(struct vfe_cmd_rgb_gamma_config *in) switch (in->channelSelect) { case RGB_GAMMA_CH0_SELECTED: -vfe_write_gamma_table(0, 0, in->table); -break; + vfe_write_gamma_table(0, 0, in->table); + break; case RGB_GAMMA_CH1_SELECTED: vfe_write_gamma_table(1, 0, in->table); @@ -2204,9 +2053,10 @@ break; break; default: + pr_err("%s: invalid gamma channel %d\n", __func__, in->channelSelect); rc = -EINVAL; break; - } /* switch */ + } /* switch */ return rc; } @@ -2265,16 +2115,15 @@ void vfe_output1_ack(struct vfe_cmd_output_ack *in) void vfe_start(struct vfe_cmd_start *in) { - unsigned long flags; - uint32_t pmstatus = 0; + uint32_t pmstatus = 0; boolean rawmode; - uint32_t demperiod = 0; - uint32_t demeven = 0; - uint32_t demodd = 0; + uint32_t demperiod = 0; + uint32_t demeven = 0; + uint32_t demodd = 0; /* derived from other commands. (camif config, axi output config, * etc) - */ + */ struct vfe_cfg hwcfg; struct vfe_upsample_cfg chromupcfg; @@ -2342,42 +2191,39 @@ void vfe_start(struct vfe_cmd_start *in) /* save variables to local. */ ctrl->vfeOperationMode = in->operationMode; - if (ctrl->vfeOperationMode == - VFE_START_OPERATION_MODE_SNAPSHOT) { - /* in snapshot mode, initialize snapshot count*/ + if (ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT) { + /* in snapshot mode, initialize snapshot count */ ctrl->vfeSnapShotCount = in->snapshotCount; /* save the requested count, this is temporarily done, to - help with HJR / multishot. */ + help with HJR / multishot. */ ctrl->vfeRequestedSnapShotCount = ctrl->vfeSnapShotCount; CDBG("requested snapshot count = %d\n", ctrl->vfeSnapShotCount); /* Assumption is to have the same pattern and period for both - paths, if both paths are used. */ + paths, if both paths are used. */ if (ctrl->viewPath.pathEnabled) { - ctrl->viewPath.snapshotPendingCount = - in->snapshotCount; + ctrl->viewPath.snapshotPendingCount = in->snapshotCount; ctrl->vfeFrameSkipPattern = - ctrl->vfeFrameSkip.output1Pattern; + ctrl->vfeFrameSkip.output1Pattern; ctrl->vfeFrameSkipPeriod = - ctrl->vfeFrameSkip.output1Period; + ctrl->vfeFrameSkip.output1Period; } if (ctrl->encPath.pathEnabled) { - ctrl->encPath.snapshotPendingCount = - in->snapshotCount; + ctrl->encPath.snapshotPendingCount = in->snapshotCount; ctrl->vfeFrameSkipPattern = - ctrl->vfeFrameSkip.output2Pattern; + ctrl->vfeFrameSkip.output2Pattern; ctrl->vfeFrameSkipPeriod = - ctrl->vfeFrameSkip.output2Period; + ctrl->vfeFrameSkip.output2Period; } } /* enable color conversion for bayer sensor - if stats enabled, need to do color conversion. */ + if stats enabled, need to do color conversion. */ if (in->pixel <= VFE_BAYER_GBGBGB) ctrl->vfeStatsCmdLocal.colorConversionEnable = TRUE; @@ -2390,8 +2236,8 @@ void vfe_start(struct vfe_cmd_start *in) /* if any stats module is enabled, the main bit is enabled. */ ctrl->vfeModuleEnableLocal.statsEnable = - ctrl->vfeStatsCmdLocal.autoFocusEnable | - ctrl->vfeStatsCmdLocal.axwEnable; + ctrl->vfeStatsCmdLocal.autoFocusEnable | + ctrl->vfeStatsCmdLocal.axwEnable; vfe_reg_module_cfg(&ctrl->vfeModuleEnableLocal); @@ -2406,7 +2252,7 @@ void vfe_start(struct vfe_cmd_start *in) ctrl->vfeBusConfigLocal.stripeRdPathEn = TRUE; ctrl->vfeBusCmdLocal.stripeReload = TRUE; ctrl->vfeBusConfigLocal.rawPixelDataSize = - ctrl->axiInputDataSize; + ctrl->axiInputDataSize; } vfe_reg_bus_cfg(&ctrl->vfeBusConfigLocal); @@ -2418,11 +2264,10 @@ void vfe_start(struct vfe_cmd_start *in) /* regardless module enabled or not, it does not hurt * to program the cositing mode. */ - chromupcfg.chromaCositingForYCbCrInputs = - in->yuvInputCositingMode; + chromupcfg.chromaCositingForYCbCrInputs = in->yuvInputCositingMode; - writel(*(uint32_t *)&(chromupcfg), - ctrl->vfebase + VFE_CHROMA_UPSAMPLE_CFG); + writel(*(uint32_t *)&chromupcfg, + ctrl->vfebase + VFE_CHROMA_UPSAMPLE_CFG); /* MISR to monitor the axi read. */ writel(0xd8, ctrl->vfebase + VFE_BUS_MISR_MAST_CFG_0); @@ -2432,14 +2277,14 @@ void vfe_start(struct vfe_cmd_start *in) /* define how composite interrupt work. */ ctrl->vfeImaskCompositePacked = - vfe_irq_composite_pack(ctrl->vfeIrqCompositeMaskLocal); + vfe_irq_composite_pack(ctrl->vfeIrqCompositeMaskLocal); vfe_program_irq_composite_mask(ctrl->vfeImaskCompositePacked); /* enable all necessary interrupts. */ - ctrl->vfeImaskLocal.camifSofIrq = TRUE; + ctrl->vfeImaskLocal.camifSofIrq = TRUE; ctrl->vfeImaskLocal.regUpdateIrq = TRUE; - ctrl->vfeImaskLocal.resetAckIrq = TRUE; + ctrl->vfeImaskLocal.resetAckIrq = TRUE; ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal); vfe_program_irq_mask(ctrl->vfeImaskPacked); @@ -2458,16 +2303,14 @@ void vfe_start(struct vfe_cmd_start *in) ctrl->vfeStatsPingPongReloadFlag = TRUE; } - writel(VFE_REG_UPDATE_TRIGGER, - ctrl->vfebase + VFE_REG_UPDATE_CMD); + writel(VFE_REG_UPDATE_TRIGGER, ctrl->vfebase + VFE_REG_UPDATE_CMD); /* program later than the reg update. */ vfe_reg_bus_cmd(&ctrl->vfeBusCmdLocal); if ((in->inputSource == - VFE_START_INPUT_SOURCE_CAMIF) || - (in->inputSource == - VFE_START_INPUT_SOURCE_TESTGEN)) + VFE_START_INPUT_SOURCE_CAMIF) || + (in->inputSource == VFE_START_INPUT_SOURCE_TESTGEN)) writel(CAMIF_COMMAND_START, ctrl->vfebase + CAMIF_COMMAND); /* start test gen if it is enabled */ @@ -2483,20 +2326,18 @@ void vfe_start(struct vfe_cmd_start *in) while (rawmode) { pmstatus = - readl(ctrl->vfebase + - VFE_BUS_ENC_CBCR_WR_PM_STATS_1); + readl(ctrl->vfebase + + VFE_BUS_ENC_CBCR_WR_PM_STATS_1); if ((pmstatus & VFE_PM_BUF_MAX_CNT_MASK) != 0) rawmode = FALSE; } - vfe_send_msg_no_payload(VFE_MSG_ID_START_ACK); + vfe_proc_ops(VFE_MSG_ID_START_ACK, NULL); ctrl->vfeStartAckPendingFlag = FALSE; } - spin_lock_irqsave(&ctrl->state_lock, flags); ctrl->vstate = VFE_STATE_ACTIVE; - spin_unlock_irqrestore(&ctrl->state_lock, flags); } void vfe_la_update(struct vfe_cmd_la_config *in) @@ -2520,7 +2361,7 @@ void vfe_la_update(struct vfe_cmd_la_config *in) vfe_program_dmi_cfg(dmiRamSel); for (i = 0; i < VFE_LA_TABLE_LENGTH; i++) { - writel((uint32_t)(*pTable), ctrl->vfebase + VFE_DMI_DATA_LO); + writel((uint32_t) (*pTable), ctrl->vfebase + VFE_DMI_DATA_LO); pTable++; } @@ -2533,7 +2374,7 @@ void vfe_la_update(struct vfe_cmd_la_config *in) void vfe_la_config(struct vfe_cmd_la_config *in) { uint16_t i; - int16_t *pTable; + int16_t *pTable; enum VFE_DMI_RAM_SEL dmiRamSel; pTable = in->table; @@ -2548,7 +2389,7 @@ void vfe_la_config(struct vfe_cmd_la_config *in) vfe_program_dmi_cfg(dmiRamSel); for (i = 0; i < VFE_LA_TABLE_LENGTH; i++) { - writel((uint32_t)(*pTable), ctrl->vfebase + VFE_DMI_DATA_LO); + writel((uint32_t) (*pTable), ctrl->vfebase + VFE_DMI_DATA_LO); pTable++; } @@ -2559,7 +2400,7 @@ void vfe_la_config(struct vfe_cmd_la_config *in) /* can only be bank 0 or bank 1 for now. */ writel(ctrl->vfeLaBankSel, ctrl->vfebase + VFE_LA_CFG); CDBG("VFE Luma adaptation bank selection is 0x%x\n", - *(uint32_t *)&ctrl->vfeLaBankSel); + *(uint32_t *)&ctrl->vfeLaBankSel); } void vfe_test_gen_start(struct vfe_cmd_test_gen_start *in) @@ -2568,47 +2409,47 @@ void vfe_test_gen_start(struct vfe_cmd_test_gen_start *in) memset(&cmd, 0, sizeof(cmd)); - cmd.numFrame = in->numFrame; - cmd.pixelDataSelect = in->pixelDataSelect; - cmd.systematicDataSelect = in->systematicDataSelect; - cmd.pixelDataSize = (uint32_t)in->pixelDataSize; - cmd.hsyncEdge = (uint32_t)in->hsyncEdge; - cmd.vsyncEdge = (uint32_t)in->vsyncEdge; - cmd.imageWidth = in->imageWidth; - cmd.imageHeight = in->imageHeight; - cmd.sofOffset = in->startOfFrameOffset; - cmd.eofNOffset = in->endOfFrameNOffset; - cmd.solOffset = in->startOfLineOffset; - cmd.eolNOffset = in->endOfLineNOffset; - cmd.hBlankInterval = in->hbi; - cmd.vBlankInterval = in->vbl; - cmd.vBlankIntervalEnable = in->vblEnable; - cmd.sofDummy = in->startOfFrameDummyLine; - cmd.eofDummy = in->endOfFrameDummyLine; - cmd.unicolorBarSelect = in->unicolorBarSelect; - cmd.unicolorBarEnable = in->unicolorBarEnable; - cmd.splitEnable = in->colorBarsSplitEnable; - cmd.pixelPattern = (uint32_t)in->colorBarsPixelPattern; - cmd.rotatePeriod = in->colorBarsRotatePeriod; - cmd.randomSeed = in->testGenRandomSeed; + cmd.numFrame = in->numFrame; + cmd.pixelDataSelect = in->pixelDataSelect; + cmd.systematicDataSelect = in->systematicDataSelect; + cmd.pixelDataSize = (uint32_t) in->pixelDataSize; + cmd.hsyncEdge = (uint32_t) in->hsyncEdge; + cmd.vsyncEdge = (uint32_t) in->vsyncEdge; + cmd.imageWidth = in->imageWidth; + cmd.imageHeight = in->imageHeight; + cmd.sofOffset = in->startOfFrameOffset; + cmd.eofNOffset = in->endOfFrameNOffset; + cmd.solOffset = in->startOfLineOffset; + cmd.eolNOffset = in->endOfLineNOffset; + cmd.hBlankInterval = in->hbi; + cmd.vBlankInterval = in->vbl; + cmd.vBlankIntervalEnable = in->vblEnable; + cmd.sofDummy = in->startOfFrameDummyLine; + cmd.eofDummy = in->endOfFrameDummyLine; + cmd.unicolorBarSelect = in->unicolorBarSelect; + cmd.unicolorBarEnable = in->unicolorBarEnable; + cmd.splitEnable = in->colorBarsSplitEnable; + cmd.pixelPattern = (uint32_t) in->colorBarsPixelPattern; + cmd.rotatePeriod = in->colorBarsRotatePeriod; + cmd.randomSeed = in->testGenRandomSeed; vfe_prog_hw(ctrl->vfebase + VFE_HW_TESTGEN_CFG, - (uint32_t *) &cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } void vfe_frame_skip_update(struct vfe_cmd_frame_skip_update *in) { struct VFE_FRAME_SKIP_UpdateCmdType cmd; - cmd.yPattern = in->output1Pattern; + cmd.yPattern = in->output1Pattern; cmd.cbcrPattern = in->output1Pattern; vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_VIEW_Y_PATTERN, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); - cmd.yPattern = in->output2Pattern; + cmd.yPattern = in->output2Pattern; cmd.cbcrPattern = in->output2Pattern; vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_PATTERN, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } void vfe_frame_skip_config(struct vfe_cmd_frame_skip_config *in) @@ -2618,17 +2459,17 @@ void vfe_frame_skip_config(struct vfe_cmd_frame_skip_config *in) ctrl->vfeFrameSkip = *in; - cmd.output2YPeriod = in->output2Period; - cmd.output2CbCrPeriod = in->output2Period; - cmd.output2YPattern = in->output2Pattern; + cmd.output2YPeriod = in->output2Period; + cmd.output2CbCrPeriod = in->output2Period; + cmd.output2YPattern = in->output2Pattern; cmd.output2CbCrPattern = in->output2Pattern; - cmd.output1YPeriod = in->output1Period; - cmd.output1CbCrPeriod = in->output1Period; - cmd.output1YPattern = in->output1Pattern; + cmd.output1YPeriod = in->output1Period; + cmd.output1CbCrPeriod = in->output1Period; + cmd.output1YPattern = in->output1Pattern; cmd.output1CbCrPattern = in->output1Pattern; vfe_prog_hw(ctrl->vfebase + VFE_FRAMEDROP_ENC_Y_CFG, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } void vfe_output_clamp_config(struct vfe_cmd_output_clamp_config *in) @@ -2636,16 +2477,16 @@ void vfe_output_clamp_config(struct vfe_cmd_output_clamp_config *in) struct vfe_output_clamp_cfg cmd; memset(&cmd, 0, sizeof(cmd)); - cmd.yChanMax = in->maxCh0; + cmd.yChanMax = in->maxCh0; cmd.cbChanMax = in->maxCh1; cmd.crChanMax = in->maxCh2; - cmd.yChanMin = in->minCh0; + cmd.yChanMin = in->minCh0; cmd.cbChanMin = in->minCh1; cmd.crChanMin = in->minCh2; vfe_prog_hw(ctrl->vfebase + VFE_CLAMP_MAX_CFG, (uint32_t *)&cmd, - sizeof(cmd)); + sizeof(cmd)); } void vfe_camif_frame_update(struct vfe_cmds_camif_frame *in) @@ -2658,11 +2499,10 @@ void vfe_camif_frame_update(struct vfe_cmds_camif_frame *in) cmd.linesPerFrame = in->linesPerFrame; vfe_prog_hw(ctrl->vfebase + CAMIF_FRAME_CONFIG, (uint32_t *)&cmd, - sizeof(cmd)); + sizeof(cmd)); } -void vfe_color_correction_config( - struct vfe_cmd_color_correction_config *in) +void vfe_color_correction_config(struct vfe_cmd_color_correction_config *in) { struct vfe_color_correction_cfg cmd; @@ -2686,12 +2526,12 @@ void vfe_color_correction_config( cmd.coefQFactor = in->coefQFactor; vfe_prog_hw(ctrl->vfebase + VFE_COLOR_CORRECT_COEFF_0, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } void vfe_demosaic_abf_update(struct vfe_cmd_demosaic_abf_update *in) { -struct vfe_demosaic_cfg cmd; + struct vfe_demosaic_cfg cmd; struct vfe_demosaic_abf_cfg cmdabf; uint32_t temp; @@ -2699,18 +2539,18 @@ struct vfe_demosaic_cfg cmd; temp = readl(ctrl->vfebase + VFE_DEMOSAIC_CFG); cmd = *((struct vfe_demosaic_cfg *)(&temp)); - cmd.abfEnable = in->abfUpdate.enable; - cmd.forceAbfOn = in->abfUpdate.forceOn; - cmd.abfShift = in->abfUpdate.shift; + cmd.abfEnable = in->abfUpdate.enable; + cmd.forceAbfOn = in->abfUpdate.forceOn; + cmd.abfShift = in->abfUpdate.shift; vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); - cmdabf.lpThreshold = in->abfUpdate.lpThreshold; - cmdabf.ratio = in->abfUpdate.ratio; - cmdabf.minValue = in->abfUpdate.min; - cmdabf.maxValue = in->abfUpdate.max; + cmdabf.lpThreshold = in->abfUpdate.lpThreshold; + cmdabf.ratio = in->abfUpdate.ratio; + cmdabf.minValue = in->abfUpdate.min; + cmdabf.maxValue = in->abfUpdate.max; vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_ABF_CFG_0, - (uint32_t *)&cmdabf, sizeof(cmdabf)); + (uint32_t *)&cmdabf, sizeof(cmdabf)); } void vfe_demosaic_bpc_update(struct vfe_cmd_demosaic_bpc_update *in) @@ -2725,18 +2565,18 @@ void vfe_demosaic_bpc_update(struct vfe_cmd_demosaic_bpc_update *in) cmd = *((struct vfe_demosaic_cfg *)(&temp)); cmd.badPixelCorrEnable = in->bpcUpdate.enable; - cmd.fminThreshold = in->bpcUpdate.fminThreshold; - cmd.fmaxThreshold = in->bpcUpdate.fmaxThreshold; + cmd.fminThreshold = in->bpcUpdate.fminThreshold; + cmd.fmaxThreshold = in->bpcUpdate.fmaxThreshold; vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); - cmdbpc.blueDiffThreshold = in->bpcUpdate.blueDiffThreshold; - cmdbpc.redDiffThreshold = in->bpcUpdate.redDiffThreshold; + cmdbpc.blueDiffThreshold = in->bpcUpdate.blueDiffThreshold; + cmdbpc.redDiffThreshold = in->bpcUpdate.redDiffThreshold; cmdbpc.greenDiffThreshold = in->bpcUpdate.greenDiffThreshold; vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_BPC_CFG_0, - (uint32_t *)&cmdbpc, sizeof(cmdbpc)); + (uint32_t *)&cmdbpc, sizeof(cmdbpc)); } void vfe_demosaic_config(struct vfe_cmd_demosaic_config *in) @@ -2751,63 +2591,61 @@ void vfe_demosaic_config(struct vfe_cmd_demosaic_config *in) ctrl->vfeModuleEnableLocal.demosaicEnable = in->enable; - cmd.abfEnable = in->abfConfig.enable; + cmd.abfEnable = in->abfConfig.enable; cmd.badPixelCorrEnable = in->bpcConfig.enable; - cmd.forceAbfOn = in->abfConfig.forceOn; - cmd.abfShift = in->abfConfig.shift; - cmd.fminThreshold = in->bpcConfig.fminThreshold; - cmd.fmaxThreshold = in->bpcConfig.fmaxThreshold; - cmd.slopeShift = in->slopeShift; + cmd.forceAbfOn = in->abfConfig.forceOn; + cmd.abfShift = in->abfConfig.shift; + cmd.fminThreshold = in->bpcConfig.fminThreshold; + cmd.fmaxThreshold = in->bpcConfig.fmaxThreshold; + cmd.slopeShift = in->slopeShift; vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_CFG, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); cmd_abf.lpThreshold = in->abfConfig.lpThreshold; - cmd_abf.ratio = in->abfConfig.ratio; - cmd_abf.minValue = in->abfConfig.min; - cmd_abf.maxValue = in->abfConfig.max; + cmd_abf.ratio = in->abfConfig.ratio; + cmd_abf.minValue = in->abfConfig.min; + cmd_abf.maxValue = in->abfConfig.max; vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_ABF_CFG_0, - (uint32_t *)&cmd_abf, sizeof(cmd_abf)); + (uint32_t *)&cmd_abf, sizeof(cmd_abf)); - cmd_bpc.blueDiffThreshold = in->bpcConfig.blueDiffThreshold; - cmd_bpc.redDiffThreshold = in->bpcConfig.redDiffThreshold; - cmd_bpc.greenDiffThreshold = in->bpcConfig.greenDiffThreshold; + cmd_bpc.blueDiffThreshold = in->bpcConfig.blueDiffThreshold; + cmd_bpc.redDiffThreshold = in->bpcConfig.redDiffThreshold; + cmd_bpc.greenDiffThreshold = in->bpcConfig.greenDiffThreshold; vfe_prog_hw(ctrl->vfebase + VFE_DEMOSAIC_BPC_CFG_0, - (uint32_t *)&cmd_bpc, sizeof(cmd_bpc)); + (uint32_t *)&cmd_bpc, sizeof(cmd_bpc)); } -void vfe_demux_channel_gain_update( - struct vfe_cmd_demux_channel_gain_config *in) +void vfe_demux_channel_gain_update(struct vfe_cmd_demux_channel_gain_config *in) { struct vfe_demux_cfg cmd; memset(&cmd, 0, sizeof(cmd)); - cmd.ch0EvenGain = in->ch0EvenGain; - cmd.ch0OddGain = in->ch0OddGain; - cmd.ch1Gain = in->ch1Gain; - cmd.ch2Gain = in->ch2Gain; + cmd.ch0EvenGain = in->ch0EvenGain; + cmd.ch0OddGain = in->ch0OddGain; + cmd.ch1Gain = in->ch1Gain; + cmd.ch2Gain = in->ch2Gain; vfe_prog_hw(ctrl->vfebase + VFE_DEMUX_GAIN_0, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } -void vfe_demux_channel_gain_config( - struct vfe_cmd_demux_channel_gain_config *in) +void vfe_demux_channel_gain_config(struct vfe_cmd_demux_channel_gain_config *in) { struct vfe_demux_cfg cmd; memset(&cmd, 0, sizeof(cmd)); cmd.ch0EvenGain = in->ch0EvenGain; - cmd.ch0OddGain = in->ch0OddGain; - cmd.ch1Gain = in->ch1Gain; - cmd.ch2Gain = in->ch2Gain; + cmd.ch0OddGain = in->ch0OddGain; + cmd.ch1Gain = in->ch1Gain; + cmd.ch2Gain = in->ch2Gain; vfe_prog_hw(ctrl->vfebase + VFE_DEMUX_GAIN_0, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } void vfe_black_level_update(struct vfe_cmd_black_level_config *in) @@ -2818,12 +2656,12 @@ void vfe_black_level_update(struct vfe_cmd_black_level_config *in) ctrl->vfeModuleEnableLocal.blackLevelCorrectionEnable = in->enable; cmd.evenEvenAdjustment = in->evenEvenAdjustment; - cmd.evenOddAdjustment = in->evenOddAdjustment; - cmd.oddEvenAdjustment = in->oddEvenAdjustment; - cmd.oddOddAdjustment = in->oddOddAdjustment; + cmd.evenOddAdjustment = in->evenOddAdjustment; + cmd.oddEvenAdjustment = in->oddEvenAdjustment; + cmd.oddOddAdjustment = in->oddOddAdjustment; vfe_prog_hw(ctrl->vfebase + VFE_BLACK_EVEN_EVEN_VALUE, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } void vfe_black_level_config(struct vfe_cmd_black_level_config *in) @@ -2834,12 +2672,12 @@ void vfe_black_level_config(struct vfe_cmd_black_level_config *in) ctrl->vfeModuleEnableLocal.blackLevelCorrectionEnable = in->enable; cmd.evenEvenAdjustment = in->evenEvenAdjustment; - cmd.evenOddAdjustment = in->evenOddAdjustment; - cmd.oddEvenAdjustment = in->oddEvenAdjustment; - cmd.oddOddAdjustment = in->oddOddAdjustment; + cmd.evenOddAdjustment = in->evenOddAdjustment; + cmd.oddEvenAdjustment = in->oddEvenAdjustment; + cmd.oddOddAdjustment = in->oddOddAdjustment; vfe_prog_hw(ctrl->vfebase + VFE_BLACK_EVEN_EVEN_VALUE, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } void vfe_asf_update(struct vfe_cmd_asf_update *in) @@ -2849,45 +2687,45 @@ void vfe_asf_update(struct vfe_cmd_asf_update *in) ctrl->vfeModuleEnableLocal.asfEnable = in->enable; - cmd.smoothEnable = in->smoothFilterEnabled; - cmd.sharpMode = in->sharpMode; - cmd.smoothCoeff1 = in->smoothCoefCenter; - cmd.smoothCoeff0 = in->smoothCoefSurr; - cmd.cropEnable = in->cropEnable; + cmd.smoothEnable = in->smoothFilterEnabled; + cmd.sharpMode = in->sharpMode; + cmd.smoothCoeff0 = in->smoothCoefCenter; + cmd.smoothCoeff1 = in->smoothCoefSurr; + cmd.cropEnable = in->cropEnable; cmd.sharpThresholdE1 = in->sharpThreshE1; - cmd.sharpDegreeK1 = in->sharpK1; - cmd.sharpDegreeK2 = in->sharpK2; - cmd.normalizeFactor = in->normalizeFactor; + cmd.sharpDegreeK1 = in->sharpK1; + cmd.sharpDegreeK2 = in->sharpK2; + cmd.normalizeFactor = in->normalizeFactor; cmd.sharpThresholdE2 = in->sharpThreshE2; cmd.sharpThresholdE3 = in->sharpThreshE3; cmd.sharpThresholdE4 = in->sharpThreshE4; cmd.sharpThresholdE5 = in->sharpThreshE5; - cmd.F1Coeff0 = in->filter1Coefficients[0]; - cmd.F1Coeff1 = in->filter1Coefficients[1]; - cmd.F1Coeff2 = in->filter1Coefficients[2]; - cmd.F1Coeff3 = in->filter1Coefficients[3]; - cmd.F1Coeff4 = in->filter1Coefficients[4]; - cmd.F1Coeff5 = in->filter1Coefficients[5]; - cmd.F1Coeff6 = in->filter1Coefficients[6]; - cmd.F1Coeff7 = in->filter1Coefficients[7]; - cmd.F1Coeff8 = in->filter1Coefficients[8]; - cmd.F2Coeff0 = in->filter2Coefficients[0]; - cmd.F2Coeff1 = in->filter2Coefficients[1]; - cmd.F2Coeff2 = in->filter2Coefficients[2]; - cmd.F2Coeff3 = in->filter2Coefficients[3]; - cmd.F2Coeff4 = in->filter2Coefficients[4]; - cmd.F2Coeff5 = in->filter2Coefficients[5]; - cmd.F2Coeff6 = in->filter2Coefficients[6]; - cmd.F2Coeff7 = in->filter2Coefficients[7]; - cmd.F2Coeff8 = in->filter2Coefficients[8]; + cmd.F1Coeff0 = in->filter1Coefficients[0]; + cmd.F1Coeff1 = in->filter1Coefficients[1]; + cmd.F1Coeff2 = in->filter1Coefficients[2]; + cmd.F1Coeff3 = in->filter1Coefficients[3]; + cmd.F1Coeff4 = in->filter1Coefficients[4]; + cmd.F1Coeff5 = in->filter1Coefficients[5]; + cmd.F1Coeff6 = in->filter1Coefficients[6]; + cmd.F1Coeff7 = in->filter1Coefficients[7]; + cmd.F1Coeff8 = in->filter1Coefficients[8]; + cmd.F2Coeff0 = in->filter2Coefficients[0]; + cmd.F2Coeff1 = in->filter2Coefficients[1]; + cmd.F2Coeff2 = in->filter2Coefficients[2]; + cmd.F2Coeff3 = in->filter2Coefficients[3]; + cmd.F2Coeff4 = in->filter2Coefficients[4]; + cmd.F2Coeff5 = in->filter2Coefficients[5]; + cmd.F2Coeff6 = in->filter2Coefficients[6]; + cmd.F2Coeff7 = in->filter2Coefficients[7]; + cmd.F2Coeff8 = in->filter2Coefficients[8]; vfe_prog_hw(ctrl->vfebase + VFE_ASF_CFG, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } void vfe_asf_config(struct vfe_cmd_asf_config *in) { - struct vfe_asf_update cmd; + struct vfe_asf_update cmd; struct vfe_asfcrop_cfg cmd2; memset(&cmd, 0, sizeof(cmd)); @@ -2895,48 +2733,48 @@ void vfe_asf_config(struct vfe_cmd_asf_config *in) ctrl->vfeModuleEnableLocal.asfEnable = in->enable; - cmd.smoothEnable = in->smoothFilterEnabled; - cmd.sharpMode = in->sharpMode; - cmd.smoothCoeff0 = in->smoothCoefCenter; - cmd.smoothCoeff1 = in->smoothCoefSurr; - cmd.cropEnable = in->cropEnable; - cmd.sharpThresholdE1 = in->sharpThreshE1; - cmd.sharpDegreeK1 = in->sharpK1; - cmd.sharpDegreeK2 = in->sharpK2; - cmd.normalizeFactor = in->normalizeFactor; - cmd.sharpThresholdE2 = in->sharpThreshE2; - cmd.sharpThresholdE3 = in->sharpThreshE3; - cmd.sharpThresholdE4 = in->sharpThreshE4; - cmd.sharpThresholdE5 = in->sharpThreshE5; - cmd.F1Coeff0 = in->filter1Coefficients[0]; - cmd.F1Coeff1 = in->filter1Coefficients[1]; - cmd.F1Coeff2 = in->filter1Coefficients[2]; - cmd.F1Coeff3 = in->filter1Coefficients[3]; - cmd.F1Coeff4 = in->filter1Coefficients[4]; - cmd.F1Coeff5 = in->filter1Coefficients[5]; - cmd.F1Coeff6 = in->filter1Coefficients[6]; - cmd.F1Coeff7 = in->filter1Coefficients[7]; - cmd.F1Coeff8 = in->filter1Coefficients[8]; - cmd.F2Coeff0 = in->filter2Coefficients[0]; - cmd.F2Coeff1 = in->filter2Coefficients[1]; - cmd.F2Coeff2 = in->filter2Coefficients[2]; - cmd.F2Coeff3 = in->filter2Coefficients[3]; - cmd.F2Coeff4 = in->filter2Coefficients[4]; - cmd.F2Coeff5 = in->filter2Coefficients[5]; - cmd.F2Coeff6 = in->filter2Coefficients[6]; - cmd.F2Coeff7 = in->filter2Coefficients[7]; - cmd.F2Coeff8 = in->filter2Coefficients[8]; + cmd.smoothEnable = in->smoothFilterEnabled; + cmd.sharpMode = in->sharpMode; + cmd.smoothCoeff0 = in->smoothCoefCenter; + cmd.smoothCoeff1 = in->smoothCoefSurr; + cmd.cropEnable = in->cropEnable; + cmd.sharpThresholdE1 = in->sharpThreshE1; + cmd.sharpDegreeK1 = in->sharpK1; + cmd.sharpDegreeK2 = in->sharpK2; + cmd.normalizeFactor = in->normalizeFactor; + cmd.sharpThresholdE2 = in->sharpThreshE2; + cmd.sharpThresholdE3 = in->sharpThreshE3; + cmd.sharpThresholdE4 = in->sharpThreshE4; + cmd.sharpThresholdE5 = in->sharpThreshE5; + cmd.F1Coeff0 = in->filter1Coefficients[0]; + cmd.F1Coeff1 = in->filter1Coefficients[1]; + cmd.F1Coeff2 = in->filter1Coefficients[2]; + cmd.F1Coeff3 = in->filter1Coefficients[3]; + cmd.F1Coeff4 = in->filter1Coefficients[4]; + cmd.F1Coeff5 = in->filter1Coefficients[5]; + cmd.F1Coeff6 = in->filter1Coefficients[6]; + cmd.F1Coeff7 = in->filter1Coefficients[7]; + cmd.F1Coeff8 = in->filter1Coefficients[8]; + cmd.F2Coeff0 = in->filter2Coefficients[0]; + cmd.F2Coeff1 = in->filter2Coefficients[1]; + cmd.F2Coeff2 = in->filter2Coefficients[2]; + cmd.F2Coeff3 = in->filter2Coefficients[3]; + cmd.F2Coeff4 = in->filter2Coefficients[4]; + cmd.F2Coeff5 = in->filter2Coefficients[5]; + cmd.F2Coeff6 = in->filter2Coefficients[6]; + cmd.F2Coeff7 = in->filter2Coefficients[7]; + cmd.F2Coeff8 = in->filter2Coefficients[8]; vfe_prog_hw(ctrl->vfebase + VFE_ASF_CFG, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); - cmd2.firstLine = in->cropFirstLine; - cmd2.lastLine = in->cropLastLine; + cmd2.firstLine = in->cropFirstLine; + cmd2.lastLine = in->cropLastLine; cmd2.firstPixel = in->cropFirstPixel; - cmd2.lastPixel = in->cropLastPixel; + cmd2.lastPixel = in->cropLastPixel; vfe_prog_hw(ctrl->vfebase + VFE_ASF_CROP_WIDTH_CFG, - (uint32_t *)&cmd2, sizeof(cmd2)); + (uint32_t *)&cmd2, sizeof(cmd2)); } void vfe_white_balance_config(struct vfe_cmd_white_balance_config *in) @@ -2944,15 +2782,14 @@ void vfe_white_balance_config(struct vfe_cmd_white_balance_config *in) struct vfe_wb_cfg cmd; memset(&cmd, 0, sizeof(cmd)); - ctrl->vfeModuleEnableLocal.whiteBalanceEnable = - in->enable; + ctrl->vfeModuleEnableLocal.whiteBalanceEnable = in->enable; cmd.ch0Gain = in->ch0Gain; cmd.ch1Gain = in->ch1Gain; cmd.ch2Gain = in->ch2Gain; vfe_prog_hw(ctrl->vfebase + VFE_WB_CFG, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } void vfe_chroma_sup_config(struct vfe_cmd_chroma_suppression_config *in) @@ -2962,15 +2799,15 @@ void vfe_chroma_sup_config(struct vfe_cmd_chroma_suppression_config *in) ctrl->vfeModuleEnableLocal.chromaSuppressionEnable = in->enable; - cmd.m1 = in->m1; - cmd.m3 = in->m3; - cmd.n1 = in->n1; - cmd.n3 = in->n3; + cmd.m1 = in->m1; + cmd.m3 = in->m3; + cmd.n1 = in->n1; + cmd.n3 = in->n3; cmd.mm1 = in->mm1; cmd.nn1 = in->nn1; vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_SUPPRESS_CFG_0, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } void vfe_roll_off_config(struct vfe_cmd_roll_off_config *in) @@ -2980,43 +2817,42 @@ void vfe_roll_off_config(struct vfe_cmd_roll_off_config *in) ctrl->vfeModuleEnableLocal.lensRollOffEnable = in->enable; - cmd.gridWidth = in->gridWidth; - cmd.gridHeight = in->gridHeight; - cmd.yDelta = in->yDelta; - cmd.gridX = in->gridXIndex; - cmd.gridY = in->gridYIndex; - cmd.pixelX = in->gridPixelXIndex; - cmd.pixelY = in->gridPixelYIndex; + cmd.gridWidth = in->gridWidth; + cmd.gridHeight = in->gridHeight; + cmd.yDelta = in->yDelta; + cmd.gridX = in->gridXIndex; + cmd.gridY = in->gridYIndex; + cmd.pixelX = in->gridPixelXIndex; + cmd.pixelY = in->gridPixelYIndex; cmd.yDeltaAccum = in->yDeltaAccum; vfe_prog_hw(ctrl->vfebase + VFE_ROLLOFF_CFG_0, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); vfe_write_lens_roll_off_table(in); } -void vfe_chroma_subsample_config( - struct vfe_cmd_chroma_subsample_config *in) +void vfe_chroma_subsample_config(struct vfe_cmd_chroma_subsample_config *in) { struct vfe_chromasubsample_cfg cmd; memset(&cmd, 0, sizeof(cmd)); ctrl->vfeModuleEnableLocal.chromaSubsampleEnable = in->enable; - cmd.hCositedPhase = in->hCositedPhase; - cmd.vCositedPhase = in->vCositedPhase; - cmd.hCosited = in->hCosited; - cmd.vCosited = in->vCosited; - cmd.hsubSampleEnable = in->hsubSampleEnable; - cmd.vsubSampleEnable = in->vsubSampleEnable; - cmd.cropEnable = in->cropEnable; - cmd.cropWidthLastPixel = in->cropWidthLastPixel; + cmd.hCositedPhase = in->hCositedPhase; + cmd.vCositedPhase = in->vCositedPhase; + cmd.hCosited = in->hCosited; + cmd.vCosited = in->vCosited; + cmd.hsubSampleEnable = in->hsubSampleEnable; + cmd.vsubSampleEnable = in->vsubSampleEnable; + cmd.cropEnable = in->cropEnable; + cmd.cropWidthLastPixel = in->cropWidthLastPixel; cmd.cropWidthFirstPixel = in->cropWidthFirstPixel; - cmd.cropHeightLastLine = in->cropHeightLastLine; + cmd.cropHeightLastLine = in->cropHeightLastLine; cmd.cropHeightFirstLine = in->cropHeightFirstLine; vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_SUBSAMPLE_CFG, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } void vfe_chroma_enhan_config(struct vfe_cmd_chroma_enhan_config *in) @@ -3029,27 +2865,27 @@ void vfe_chroma_enhan_config(struct vfe_cmd_chroma_enhan_config *in) ctrl->vfeModuleEnableLocal.chromaEnhanEnable = in->enable; - cmd.ap = in->ap; - cmd.am = in->am; - cmd.bp = in->bp; - cmd.bm = in->bm; - cmd.cp = in->cp; - cmd.cm = in->cm; - cmd.dp = in->dp; - cmd.dm = in->dm; - cmd.kcb = in->kcb; - cmd.kcr = in->kcr; - - cmd2.v0 = in->RGBtoYConversionV0; - cmd2.v1 = in->RGBtoYConversionV1; - cmd2.v2 = in->RGBtoYConversionV2; + cmd.ap = in->ap; + cmd.am = in->am; + cmd.bp = in->bp; + cmd.bm = in->bm; + cmd.cp = in->cp; + cmd.cm = in->cm; + cmd.dp = in->dp; + cmd.dm = in->dm; + cmd.kcb = in->kcb; + cmd.kcr = in->kcr; + + cmd2.v0 = in->RGBtoYConversionV0; + cmd2.v1 = in->RGBtoYConversionV1; + cmd2.v2 = in->RGBtoYConversionV2; cmd2.ConvertOffset = in->RGBtoYConversionOffset; vfe_prog_hw(ctrl->vfebase + VFE_CHROMA_ENHAN_A, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); vfe_prog_hw(ctrl->vfebase + VFE_COLOR_CONVERT_COEFF_0, - (uint32_t *)&cmd2, sizeof(cmd2)); + (uint32_t *)&cmd2, sizeof(cmd2)); } void vfe_scaler2cbcr_config(struct vfe_cmd_scaler2_config *in) @@ -3060,19 +2896,19 @@ void vfe_scaler2cbcr_config(struct vfe_cmd_scaler2_config *in) ctrl->vfeModuleEnableLocal.scaler2CbcrEnable = in->enable; - cmd.hEnable = in->hconfig.enable; - cmd.vEnable = in->vconfig.enable; - cmd.inWidth = in->hconfig.inputSize; - cmd.outWidth = in->hconfig.outputSize; - cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor; + cmd.hEnable = in->hconfig.enable; + cmd.vEnable = in->vconfig.enable; + cmd.inWidth = in->hconfig.inputSize; + cmd.outWidth = in->hconfig.outputSize; + cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor; cmd.horizInterResolution = in->hconfig.interpolationResolution; - cmd.inHeight = in->vconfig.inputSize; - cmd.outHeight = in->vconfig.outputSize; - cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor; - cmd.vertInterResolution = in->vconfig.interpolationResolution; + cmd.inHeight = in->vconfig.inputSize; + cmd.outHeight = in->vconfig.outputSize; + cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor; + cmd.vertInterResolution = in->vconfig.interpolationResolution; vfe_prog_hw(ctrl->vfebase + VFE_SCALE_CBCR_CFG, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } void vfe_scaler2y_config(struct vfe_cmd_scaler2_config *in) @@ -3083,19 +2919,19 @@ void vfe_scaler2y_config(struct vfe_cmd_scaler2_config *in) ctrl->vfeModuleEnableLocal.scaler2YEnable = in->enable; - cmd.hEnable = in->hconfig.enable; - cmd.vEnable = in->vconfig.enable; - cmd.inWidth = in->hconfig.inputSize; - cmd.outWidth = in->hconfig.outputSize; - cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor; - cmd.horizInterResolution = in->hconfig.interpolationResolution; - cmd.inHeight = in->vconfig.inputSize; - cmd.outHeight = in->vconfig.outputSize; - cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor; - cmd.vertInterResolution = in->vconfig.interpolationResolution; + cmd.hEnable = in->hconfig.enable; + cmd.vEnable = in->vconfig.enable; + cmd.inWidth = in->hconfig.inputSize; + cmd.outWidth = in->hconfig.outputSize; + cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor; + cmd.horizInterResolution = in->hconfig.interpolationResolution; + cmd.inHeight = in->vconfig.inputSize; + cmd.outHeight = in->vconfig.outputSize; + cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor; + cmd.vertInterResolution = in->vconfig.interpolationResolution; vfe_prog_hw(ctrl->vfebase + VFE_SCALE_Y_CFG, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } void vfe_main_scaler_config(struct vfe_cmd_main_scaler_config *in) @@ -3106,23 +2942,23 @@ void vfe_main_scaler_config(struct vfe_cmd_main_scaler_config *in) ctrl->vfeModuleEnableLocal.mainScalerEnable = in->enable; - cmd.hEnable = in->hconfig.enable; - cmd.vEnable = in->vconfig.enable; - cmd.inWidth = in->hconfig.inputSize; - cmd.outWidth = in->hconfig.outputSize; - cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor; + cmd.hEnable = in->hconfig.enable; + cmd.vEnable = in->vconfig.enable; + cmd.inWidth = in->hconfig.inputSize; + cmd.outWidth = in->hconfig.outputSize; + cmd.horizPhaseMult = in->hconfig.phaseMultiplicationFactor; cmd.horizInterResolution = in->hconfig.interpolationResolution; - cmd.horizMNInit = in->MNInitH.MNCounterInit; - cmd.horizPhaseInit = in->MNInitH.phaseInit; - cmd.inHeight = in->vconfig.inputSize; - cmd.outHeight = in->vconfig.outputSize; - cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor; - cmd.vertInterResolution = in->vconfig.interpolationResolution; - cmd.vertMNInit = in->MNInitV.MNCounterInit; - cmd.vertPhaseInit = in->MNInitV.phaseInit; + cmd.horizMNInit = in->MNInitH.MNCounterInit; + cmd.horizPhaseInit = in->MNInitH.phaseInit; + cmd.inHeight = in->vconfig.inputSize; + cmd.outHeight = in->vconfig.outputSize; + cmd.vertPhaseMult = in->vconfig.phaseMultiplicationFactor; + cmd.vertInterResolution = in->vconfig.interpolationResolution; + cmd.vertMNInit = in->MNInitV.MNCounterInit; + cmd.vertPhaseInit = in->MNInitV.phaseInit; vfe_prog_hw(ctrl->vfebase + VFE_SCALE_CFG, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } void vfe_stats_wb_exp_stop(void) @@ -3133,29 +2969,29 @@ void vfe_stats_wb_exp_stop(void) void vfe_stats_update_wb_exp(struct vfe_cmd_stats_wb_exp_update *in) { - struct vfe_statsawb_update cmd; + struct vfe_statsawb_update cmd; struct vfe_statsawbae_update cmd2; memset(&cmd, 0, sizeof(cmd)); memset(&cmd2, 0, sizeof(cmd2)); - cmd.m1 = in->awbMCFG[0]; - cmd.m2 = in->awbMCFG[1]; - cmd.m3 = in->awbMCFG[2]; - cmd.m4 = in->awbMCFG[3]; - cmd.c1 = in->awbCCFG[0]; - cmd.c2 = in->awbCCFG[1]; - cmd.c3 = in->awbCCFG[2]; - cmd.c4 = in->awbCCFG[3]; + cmd.m1 = in->awbMCFG[0]; + cmd.m2 = in->awbMCFG[1]; + cmd.m3 = in->awbMCFG[2]; + cmd.m4 = in->awbMCFG[3]; + cmd.c1 = in->awbCCFG[0]; + cmd.c2 = in->awbCCFG[1]; + cmd.c3 = in->awbCCFG[2]; + cmd.c4 = in->awbCCFG[3]; vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWB_MCFG, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); - cmd2.aeRegionCfg = in->wbExpRegions; + cmd2.aeRegionCfg = in->wbExpRegions; cmd2.aeSubregionCfg = in->wbExpSubRegion; - cmd2.awbYMin = in->awbYMin; - cmd2.awbYMax = in->awbYMax; + cmd2.awbYMin = in->awbYMin; + cmd2.awbYMax = in->awbYMax; vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWBAE_CFG, - (uint32_t *)&cmd2, sizeof(cmd2)); + (uint32_t *)&cmd2, sizeof(cmd2)); } void vfe_stats_update_af(struct vfe_cmd_stats_af_update *in) @@ -3165,99 +3001,99 @@ void vfe_stats_update_af(struct vfe_cmd_stats_af_update *in) cmd.windowVOffset = in->windowVOffset; cmd.windowHOffset = in->windowHOffset; - cmd.windowMode = in->windowMode; - cmd.windowHeight = in->windowHeight; - cmd.windowWidth = in->windowWidth; + cmd.windowMode = in->windowMode; + cmd.windowHeight = in->windowHeight; + cmd.windowWidth = in->windowWidth; vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_CFG, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } void vfe_stats_start_wb_exp(struct vfe_cmd_stats_wb_exp_start *in) { - struct vfe_statsawb_update cmd; + struct vfe_statsawb_update cmd; struct vfe_statsawbae_update cmd2; - struct vfe_statsaxw_hdr_cfg cmd3; + struct vfe_statsaxw_hdr_cfg cmd3; - ctrl->vfeStatsCmdLocal.axwEnable = in->enable; + ctrl->vfeStatsCmdLocal.axwEnable = in->enable; ctrl->vfeImaskLocal.awbPingpongIrq = TRUE; memset(&cmd, 0, sizeof(cmd)); memset(&cmd2, 0, sizeof(cmd2)); memset(&cmd3, 0, sizeof(cmd3)); - cmd.m1 = in->awbMCFG[0]; - cmd.m2 = in->awbMCFG[1]; - cmd.m3 = in->awbMCFG[2]; - cmd.m4 = in->awbMCFG[3]; - cmd.c1 = in->awbCCFG[0]; - cmd.c2 = in->awbCCFG[1]; - cmd.c3 = in->awbCCFG[2]; - cmd.c4 = in->awbCCFG[3]; + cmd.m1 = in->awbMCFG[0]; + cmd.m2 = in->awbMCFG[1]; + cmd.m3 = in->awbMCFG[2]; + cmd.m4 = in->awbMCFG[3]; + cmd.c1 = in->awbCCFG[0]; + cmd.c2 = in->awbCCFG[1]; + cmd.c3 = in->awbCCFG[2]; + cmd.c4 = in->awbCCFG[3]; vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWB_MCFG, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); - cmd2.aeRegionCfg = in->wbExpRegions; - cmd2.aeSubregionCfg = in->wbExpSubRegion; - cmd2.awbYMin = in->awbYMin; - cmd2.awbYMax = in->awbYMax; + cmd2.aeRegionCfg = in->wbExpRegions; + cmd2.aeSubregionCfg = in->wbExpSubRegion; + cmd2.awbYMin = in->awbYMin; + cmd2.awbYMax = in->awbYMax; vfe_prog_hw(ctrl->vfebase + VFE_STATS_AWBAE_CFG, - (uint32_t *)&cmd2, sizeof(cmd2)); + (uint32_t *)&cmd2, sizeof(cmd2)); - cmd3.axwHeader = in->axwHeader; + cmd3.axwHeader = in->axwHeader; vfe_prog_hw(ctrl->vfebase + VFE_STATS_AXW_HEADER, - (uint32_t *)&cmd3, sizeof(cmd3)); + (uint32_t *)&cmd3, sizeof(cmd3)); } void vfe_stats_start_af(struct vfe_cmd_stats_af_start *in) { struct vfe_statsaf_update cmd; - struct vfe_statsaf_cfg cmd2; + struct vfe_statsaf_cfg cmd2; memset(&cmd, 0, sizeof(cmd)); memset(&cmd2, 0, sizeof(cmd2)); -ctrl->vfeStatsCmdLocal.autoFocusEnable = in->enable; -ctrl->vfeImaskLocal.afPingpongIrq = TRUE; + ctrl->vfeStatsCmdLocal.autoFocusEnable = in->enable; + ctrl->vfeImaskLocal.afPingpongIrq = TRUE; cmd.windowVOffset = in->windowVOffset; cmd.windowHOffset = in->windowHOffset; - cmd.windowMode = in->windowMode; - cmd.windowHeight = in->windowHeight; - cmd.windowWidth = in->windowWidth; + cmd.windowMode = in->windowMode; + cmd.windowHeight = in->windowHeight; + cmd.windowWidth = in->windowWidth; vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_CFG, - (uint32_t *)&cmd, sizeof(cmd)); - - cmd2.a00 = in->highPassCoef[0]; - cmd2.a04 = in->highPassCoef[1]; - cmd2.a20 = in->highPassCoef[2]; - cmd2.a21 = in->highPassCoef[3]; - cmd2.a22 = in->highPassCoef[4]; - cmd2.a23 = in->highPassCoef[5]; - cmd2.a24 = in->highPassCoef[6]; - cmd2.fvMax = in->metricMax; - cmd2.fvMetric = in->metricSelection; - cmd2.afHeader = in->bufferHeader; - cmd2.entry00 = in->gridForMultiWindows[0]; - cmd2.entry01 = in->gridForMultiWindows[1]; - cmd2.entry02 = in->gridForMultiWindows[2]; - cmd2.entry03 = in->gridForMultiWindows[3]; - cmd2.entry10 = in->gridForMultiWindows[4]; - cmd2.entry11 = in->gridForMultiWindows[5]; - cmd2.entry12 = in->gridForMultiWindows[6]; - cmd2.entry13 = in->gridForMultiWindows[7]; - cmd2.entry20 = in->gridForMultiWindows[8]; - cmd2.entry21 = in->gridForMultiWindows[9]; - cmd2.entry22 = in->gridForMultiWindows[10]; - cmd2.entry23 = in->gridForMultiWindows[11]; - cmd2.entry30 = in->gridForMultiWindows[12]; - cmd2.entry31 = in->gridForMultiWindows[13]; - cmd2.entry32 = in->gridForMultiWindows[14]; - cmd2.entry33 = in->gridForMultiWindows[15]; + (uint32_t *)&cmd, sizeof(cmd)); + + cmd2.a00 = in->highPassCoef[0]; + cmd2.a04 = in->highPassCoef[1]; + cmd2.a20 = in->highPassCoef[2]; + cmd2.a21 = in->highPassCoef[3]; + cmd2.a22 = in->highPassCoef[4]; + cmd2.a23 = in->highPassCoef[5]; + cmd2.a24 = in->highPassCoef[6]; + cmd2.fvMax = in->metricMax; + cmd2.fvMetric = in->metricSelection; + cmd2.afHeader = in->bufferHeader; + cmd2.entry00 = in->gridForMultiWindows[0]; + cmd2.entry01 = in->gridForMultiWindows[1]; + cmd2.entry02 = in->gridForMultiWindows[2]; + cmd2.entry03 = in->gridForMultiWindows[3]; + cmd2.entry10 = in->gridForMultiWindows[4]; + cmd2.entry11 = in->gridForMultiWindows[5]; + cmd2.entry12 = in->gridForMultiWindows[6]; + cmd2.entry13 = in->gridForMultiWindows[7]; + cmd2.entry20 = in->gridForMultiWindows[8]; + cmd2.entry21 = in->gridForMultiWindows[9]; + cmd2.entry22 = in->gridForMultiWindows[10]; + cmd2.entry23 = in->gridForMultiWindows[11]; + cmd2.entry30 = in->gridForMultiWindows[12]; + cmd2.entry31 = in->gridForMultiWindows[13]; + cmd2.entry32 = in->gridForMultiWindows[14]; + cmd2.entry33 = in->gridForMultiWindows[15]; vfe_prog_hw(ctrl->vfebase + VFE_STATS_AF_GRID_0, - (uint32_t *)&cmd2, sizeof(cmd2)); + (uint32_t *)&cmd2, sizeof(cmd2)); } void vfe_stats_setting(struct vfe_cmd_stats_setting *in) @@ -3277,38 +3113,38 @@ void vfe_stats_setting(struct vfe_cmd_stats_setting *in) ctrl->awbStatsControl.nextFrameAddrBuf = in->awbBuffer[2]; cmd1.lastPixel = in->frameHDimension; - cmd1.lastLine = in->frameVDimension; + cmd1.lastLine = in->frameVDimension; vfe_prog_hw(ctrl->vfebase + VFE_STATS_FRAME_SIZE, - (uint32_t *)&cmd1, sizeof(cmd1)); + (uint32_t *)&cmd1, sizeof(cmd1)); - cmd2.afBusPriority = in->afBusPriority; - cmd2.awbBusPriority = in->awbBusPriority; - cmd2.histBusPriority = in->histBusPriority; - cmd2.afBusPriorityEn = in->afBusPrioritySelection; + cmd2.afBusPriority = in->afBusPriority; + cmd2.awbBusPriority = in->awbBusPriority; + cmd2.histBusPriority = in->histBusPriority; + cmd2.afBusPriorityEn = in->afBusPrioritySelection; cmd2.awbBusPriorityEn = in->awbBusPrioritySelection; cmd2.histBusPriorityEn = in->histBusPrioritySelection; vfe_prog_hw(ctrl->vfebase + VFE_BUS_STATS_WR_PRIORITY, - (uint32_t *)&cmd2, sizeof(cmd2)); + (uint32_t *)&cmd2, sizeof(cmd2)); /* Program the bus ping pong address for statistics modules. */ writel(in->afBuffer[0], ctrl->vfebase + VFE_BUS_STATS_AF_WR_PING_ADDR); writel(in->afBuffer[1], ctrl->vfebase + VFE_BUS_STATS_AF_WR_PONG_ADDR); writel(in->awbBuffer[0], - ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR); + ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PING_ADDR); writel(in->awbBuffer[1], - ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR); + ctrl->vfebase + VFE_BUS_STATS_AWB_WR_PONG_ADDR); writel(in->histBuffer[0], - ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PING_ADDR); + ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PING_ADDR); writel(in->histBuffer[1], - ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PONG_ADDR); + ctrl->vfebase + VFE_BUS_STATS_HIST_WR_PONG_ADDR); } void vfe_axi_input_config(struct vfe_cmd_axi_input_config *in) { struct VFE_AxiInputCmdType cmd; uint32_t xSizeWord, axiRdUnpackPattern; - uint8_t axiInputPpw; + uint8_t axiInputPpw; uint32_t busPingpongRdIrqEnable; ctrl->vfeImaskLocal.rdPingpongIrq = TRUE; @@ -3349,81 +3185,65 @@ void vfe_axi_input_config(struct vfe_cmd_axi_input_config *in) } xSizeWord = - ((((in->xOffset % axiInputPpw) + in->xSize) + - (axiInputPpw-1)) / axiInputPpw) - 1; - - cmd.stripeStartAddr0 = in->fragAddr[0]; - cmd.stripeStartAddr1 = in->fragAddr[1]; - cmd.stripeStartAddr2 = in->fragAddr[2]; - cmd.stripeStartAddr3 = in->fragAddr[3]; - cmd.ySize = in->ySize; - cmd.yOffsetDelta = 0; - cmd.xSizeWord = xSizeWord; - cmd.burstLength = 1; - cmd.NumOfRows = in->numOfRows; - cmd.RowIncrement = - (in->rowIncrement + (axiInputPpw-1))/axiInputPpw; - cmd.mainUnpackHeight = in->ySize; - cmd.mainUnpackWidth = in->xSize - 1; - cmd.mainUnpackHbiSel = (uint32_t)in->unpackHbi; - cmd.mainUnpackPhase = in->unpackPhase; - cmd.unpackPattern = axiRdUnpackPattern; - cmd.padLeft = in->padRepeatCountLeft; - cmd.padRight = in->padRepeatCountRight; - cmd.padTop = in->padRepeatCountTop; - cmd.padBottom = in->padRepeatCountBottom; - cmd.leftUnpackPattern0 = in->padLeftComponentSelectCycle0; - cmd.leftUnpackPattern1 = in->padLeftComponentSelectCycle1; - cmd.leftUnpackPattern2 = in->padLeftComponentSelectCycle2; - cmd.leftUnpackPattern3 = in->padLeftComponentSelectCycle3; - cmd.leftUnpackStop0 = in->padLeftStopCycle0; - cmd.leftUnpackStop1 = in->padLeftStopCycle1; - cmd.leftUnpackStop2 = in->padLeftStopCycle2; - cmd.leftUnpackStop3 = in->padLeftStopCycle3; - cmd.rightUnpackPattern0 = in->padRightComponentSelectCycle0; - cmd.rightUnpackPattern1 = in->padRightComponentSelectCycle1; - cmd.rightUnpackPattern2 = in->padRightComponentSelectCycle2; - cmd.rightUnpackPattern3 = in->padRightComponentSelectCycle3; - cmd.rightUnpackStop0 = in->padRightStopCycle0; - cmd.rightUnpackStop1 = in->padRightStopCycle1; - cmd.rightUnpackStop2 = in->padRightStopCycle2; - cmd.rightUnpackStop3 = in->padRightStopCycle3; - cmd.topUnapckPattern = in->padTopLineCount; - cmd.bottomUnapckPattern = in->padBottomLineCount; + ((((in->xOffset % axiInputPpw) + in->xSize) + + (axiInputPpw - 1)) / axiInputPpw) - 1; + + cmd.stripeStartAddr0 = in->fragAddr[0]; + cmd.stripeStartAddr1 = in->fragAddr[1]; + cmd.stripeStartAddr2 = in->fragAddr[2]; + cmd.stripeStartAddr3 = in->fragAddr[3]; + cmd.ySize = in->ySize; + cmd.yOffsetDelta = 0; + cmd.xSizeWord = xSizeWord; + cmd.burstLength = 1; + cmd.NumOfRows = in->numOfRows; + cmd.RowIncrement = (in->rowIncrement + (axiInputPpw - 1)) / axiInputPpw; + cmd.mainUnpackHeight = in->ySize; + cmd.mainUnpackWidth = in->xSize - 1; + cmd.mainUnpackHbiSel = (uint32_t) in->unpackHbi; + cmd.mainUnpackPhase = in->unpackPhase; + cmd.unpackPattern = axiRdUnpackPattern; + cmd.padLeft = in->padRepeatCountLeft; + cmd.padRight = in->padRepeatCountRight; + cmd.padTop = in->padRepeatCountTop; + cmd.padBottom = in->padRepeatCountBottom; + cmd.leftUnpackPattern0 = in->padLeftComponentSelectCycle0; + cmd.leftUnpackPattern1 = in->padLeftComponentSelectCycle1; + cmd.leftUnpackPattern2 = in->padLeftComponentSelectCycle2; + cmd.leftUnpackPattern3 = in->padLeftComponentSelectCycle3; + cmd.leftUnpackStop0 = in->padLeftStopCycle0; + cmd.leftUnpackStop1 = in->padLeftStopCycle1; + cmd.leftUnpackStop2 = in->padLeftStopCycle2; + cmd.leftUnpackStop3 = in->padLeftStopCycle3; + cmd.rightUnpackPattern0 = in->padRightComponentSelectCycle0; + cmd.rightUnpackPattern1 = in->padRightComponentSelectCycle1; + cmd.rightUnpackPattern2 = in->padRightComponentSelectCycle2; + cmd.rightUnpackPattern3 = in->padRightComponentSelectCycle3; + cmd.rightUnpackStop0 = in->padRightStopCycle0; + cmd.rightUnpackStop1 = in->padRightStopCycle1; + cmd.rightUnpackStop2 = in->padRightStopCycle2; + cmd.rightUnpackStop3 = in->padRightStopCycle3; + cmd.topUnapckPattern = in->padTopLineCount; + cmd.bottomUnapckPattern = in->padBottomLineCount; /* program vfe_bus_cfg */ vfe_prog_hw(ctrl->vfebase + VFE_BUS_STRIPE_RD_ADDR_0, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); /* hacking code, put it to default value */ busPingpongRdIrqEnable = 0xf; - writel(busPingpongRdIrqEnable, - ctrl->vfebase + VFE_BUS_PINGPONG_IRQ_EN); -} - -void vfe_stats_config(struct vfe_cmd_stats_setting *in) -{ - ctrl->afStatsControl.addressBuffer[0] = in->afBuffer[0]; - ctrl->afStatsControl.addressBuffer[1] = in->afBuffer[1]; - ctrl->afStatsControl.nextFrameAddrBuf = in->afBuffer[2]; - - ctrl->awbStatsControl.addressBuffer[0] = in->awbBuffer[0]; - ctrl->awbStatsControl.addressBuffer[1] = in->awbBuffer[1]; - ctrl->awbStatsControl.nextFrameAddrBuf = in->awbBuffer[2]; - - vfe_stats_setting(in); + writel(busPingpongRdIrqEnable, ctrl->vfebase + VFE_BUS_PINGPONG_IRQ_EN); } -void vfe_axi_output_config( - struct vfe_cmd_axi_output_config *in) +void vfe_axi_output_config(struct vfe_cmd_axi_output_config *in) { /* local variable */ uint32_t *pcircle; uint32_t *pdest; uint32_t *psrc; - uint8_t i; - uint8_t fcnt; + uint8_t i; + uint8_t fcnt; uint16_t axioutpw = 8; /* parameters check, condition and usage mode check */ @@ -3457,258 +3277,259 @@ void vfe_axi_output_config( CDBG("axiOutputMode = %d\n", ctrl->axiOutputMode); switch (ctrl->axiOutputMode) { - case VFE_AXI_OUTPUT_MODE_Output1: { - ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE; - ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; - ctrl->vfeBusConfigLocal.rawWritePathSelect = - VFE_RAW_OUTPUT_DISABLED; - - ctrl->encPath.pathEnabled = FALSE; - ctrl->vfeImaskLocal.encIrq = FALSE; - ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = - VFE_COMP_IRQ_BOTH_Y_CBCR; - - ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE; - ctrl->vfeBusConfigLocal.encCbcrWrPathEn = FALSE; - ctrl->viewPath.pathEnabled = TRUE; - ctrl->vfeImaskLocal.viewIrq = TRUE; - ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = - VFE_COMP_IRQ_BOTH_Y_CBCR; - - ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE; - ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE; - - if (ctrl->vfeBusConfigLocal.encYWrPathEn && - ctrl->encPath.multiFrag) - ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; - - if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && - ctrl->encPath.multiFrag) - ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; - - if (ctrl->vfeBusConfigLocal.viewYWrPathEn && - ctrl->viewPath.multiFrag) - ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; - - if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && - ctrl->viewPath.multiFrag) - ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; - } /* VFE_AXI_OUTPUT_MODE_Output1 */ + case VFE_AXI_OUTPUT_MODE_Output1:{ + ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_DISABLED; + + ctrl->encPath.pathEnabled = FALSE; + ctrl->vfeImaskLocal.encIrq = FALSE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = FALSE; + ctrl->viewPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.viewIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE; + + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_Output1 */ break; - case VFE_AXI_OUTPUT_MODE_Output2: { - ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE; - ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; - ctrl->vfeBusConfigLocal.rawWritePathSelect = - VFE_RAW_OUTPUT_DISABLED; - - ctrl->encPath.pathEnabled = TRUE; - ctrl->vfeImaskLocal.encIrq = TRUE; - ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = - VFE_COMP_IRQ_BOTH_Y_CBCR; - - ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE; - ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; - - ctrl->viewPath.pathEnabled = FALSE; - ctrl->vfeImaskLocal.viewIrq = FALSE; - ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = - VFE_COMP_IRQ_BOTH_Y_CBCR; - - ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE; - ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = FALSE; - - if (ctrl->vfeBusConfigLocal.encYWrPathEn && - ctrl->encPath.multiFrag) - ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; - - if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && - ctrl->encPath.multiFrag) - ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; - - if (ctrl->vfeBusConfigLocal.viewYWrPathEn && - ctrl->viewPath.multiFrag) - ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; - - if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && - ctrl->viewPath.multiFrag) - ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; - } /* VFE_AXI_OUTPUT_MODE_Output2 */ - break; - - case VFE_AXI_OUTPUT_MODE_Output1AndOutput2: { - ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE; - ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; - ctrl->vfeBusConfigLocal.rawWritePathSelect = - VFE_RAW_OUTPUT_DISABLED; - - ctrl->encPath.pathEnabled = TRUE; - ctrl->vfeImaskLocal.encIrq = TRUE; - ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = - VFE_COMP_IRQ_BOTH_Y_CBCR; - - ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE; - ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; - ctrl->viewPath.pathEnabled = TRUE; - ctrl->vfeImaskLocal.viewIrq = TRUE; - ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = - VFE_COMP_IRQ_BOTH_Y_CBCR; - - ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE; - ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE; - - if (ctrl->vfeBusConfigLocal.encYWrPathEn && - ctrl->encPath.multiFrag) - ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; - - if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && - ctrl->encPath.multiFrag) - ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; - - if (ctrl->vfeBusConfigLocal.viewYWrPathEn && - ctrl->viewPath.multiFrag) - ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; - - if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && - ctrl->viewPath.multiFrag) - ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; - } /* VFE_AXI_OUTPUT_MODE_Output1AndOutput2 */ - break; + case VFE_AXI_OUTPUT_MODE_Output2:{ + ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_DISABLED; - case VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2: { - /* For raw snapshot, we need both ping and pong buffer - * initialized to the same address. Otherwise, if we - * leave the pong buffer to NULL, there will be axi_error. - * Note that ideally we should deal with this at upper layer, - * which is in msm_vfe8x.c */ - if (!in->output2.outputCbcr.outFragments[1][0]) { - in->output2.outputCbcr.outFragments[1][0] = - in->output2.outputCbcr.outFragments[0][0]; - } + ctrl->encPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.encIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; - ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE; - ctrl->vfeCamifConfigLocal.camif2OutputEnable = FALSE; - ctrl->vfeBusConfigLocal.rawWritePathSelect = - VFE_RAW_OUTPUT_ENC_CBCR_PATH; + ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; - ctrl->encPath.pathEnabled = TRUE; - ctrl->vfeImaskLocal.encIrq = TRUE; - ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = - VFE_COMP_IRQ_CBCR_ONLY; + ctrl->viewPath.pathEnabled = FALSE; + ctrl->vfeImaskLocal.viewIrq = FALSE; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; - ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE; - ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = FALSE; - ctrl->viewPath.pathEnabled = FALSE; - ctrl->vfeImaskLocal.viewIrq = FALSE; - ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = - VFE_COMP_IRQ_BOTH_Y_CBCR; + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; - ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE; - ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = FALSE; + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; - if (ctrl->vfeBusConfigLocal.encYWrPathEn && - ctrl->encPath.multiFrag) - ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; - if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && - ctrl->encPath.multiFrag) - ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_Output2 */ + break; - if (ctrl->vfeBusConfigLocal.viewYWrPathEn && - ctrl->viewPath.multiFrag) - ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + case VFE_AXI_OUTPUT_MODE_Output1AndOutput2:{ + ctrl->vfeCamifConfigLocal.camif2BusEnable = FALSE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_DISABLED; + + ctrl->encPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.encIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; + ctrl->viewPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.viewIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE; + + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_Output1AndOutput2 */ + break; - if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && - ctrl->viewPath.multiFrag) - ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; - } /* VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2 */ + case VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2:{ + /* For raw snapshot, we need both ping and pong buffer + * initialized to the same address. Otherwise, if we + * leave the pong buffer to NULL, there will be + * axi_error. + * Note that ideally we should deal with this at upper + * layer, which is in msm_vfe8x.c */ + if (!in->output2.outputCbcr.outFragments[1][0]) { + in->output2.outputCbcr.outFragments[1][0] = + in->output2.outputCbcr.outFragments[0][0]; + } + + ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = FALSE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_ENC_CBCR_PATH; + + ctrl->encPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.encIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_CBCR_ONLY; + + ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; + + ctrl->viewPath.pathEnabled = FALSE; + ctrl->vfeImaskLocal.viewIrq = FALSE; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; + + ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = FALSE; + + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2 */ break; - case VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1: { - ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE; - ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; - ctrl->vfeBusConfigLocal.rawWritePathSelect = - VFE_RAW_OUTPUT_VIEW_CBCR_PATH; + case VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1:{ + ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_VIEW_CBCR_PATH; - ctrl->encPath.pathEnabled = TRUE; - ctrl->vfeImaskLocal.encIrq = TRUE; - ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = - VFE_COMP_IRQ_BOTH_Y_CBCR; + ctrl->encPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.encIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; - ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE; - ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.encYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; - ctrl->viewPath.pathEnabled = TRUE; - ctrl->vfeImaskLocal.viewIrq = TRUE; - ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = - VFE_COMP_IRQ_CBCR_ONLY; + ctrl->viewPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.viewIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_CBCR_ONLY; - ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE; - ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.viewYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE; - if (ctrl->vfeBusConfigLocal.encYWrPathEn && - ctrl->encPath.multiFrag) - ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; - if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && - ctrl->encPath.multiFrag) - ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; - if (ctrl->vfeBusConfigLocal.viewYWrPathEn && - ctrl->viewPath.multiFrag) - ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; - if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && - ctrl->viewPath.multiFrag) - ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; - } /* VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1 */ + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1 */ break; - case VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2: { - ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE; - ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; - ctrl->vfeBusConfigLocal.rawWritePathSelect = - VFE_RAW_OUTPUT_ENC_CBCR_PATH; + case VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2:{ + ctrl->vfeCamifConfigLocal.camif2BusEnable = TRUE; + ctrl->vfeCamifConfigLocal.camif2OutputEnable = TRUE; + ctrl->vfeBusConfigLocal.rawWritePathSelect = + VFE_RAW_OUTPUT_ENC_CBCR_PATH; - ctrl->encPath.pathEnabled = TRUE; - ctrl->vfeImaskLocal.encIrq = TRUE; - ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = - VFE_COMP_IRQ_CBCR_ONLY; + ctrl->encPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.encIrq = TRUE; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = + VFE_COMP_IRQ_CBCR_ONLY; - ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE; - ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.encYWrPathEn = FALSE; + ctrl->vfeBusConfigLocal.encCbcrWrPathEn = TRUE; - ctrl->viewPath.pathEnabled = TRUE; - ctrl->vfeImaskLocal.viewIrq = TRUE; + ctrl->viewPath.pathEnabled = TRUE; + ctrl->vfeImaskLocal.viewIrq = TRUE; - ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = - VFE_COMP_IRQ_BOTH_Y_CBCR; + ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = + VFE_COMP_IRQ_BOTH_Y_CBCR; - ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE; - ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.viewYWrPathEn = TRUE; + ctrl->vfeBusConfigLocal.viewCbcrWrPathEn = TRUE; - if (ctrl->vfeBusConfigLocal.encYWrPathEn && - ctrl->encPath.multiFrag) - ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; + if (ctrl->vfeBusConfigLocal.encYWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encYPingpongIrq = TRUE; - if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && - ctrl->encPath.multiFrag) - ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; + if (ctrl->vfeBusConfigLocal.encCbcrWrPathEn && + ctrl->encPath.multiFrag) + ctrl->vfeImaskLocal.encCbcrPingpongIrq = TRUE; - if (ctrl->vfeBusConfigLocal.viewYWrPathEn && - ctrl->viewPath.multiFrag) - ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; + if (ctrl->vfeBusConfigLocal.viewYWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewYPingpongIrq = TRUE; - if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && - ctrl->viewPath.multiFrag) - ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; - } /* VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2 */ + if (ctrl->vfeBusConfigLocal.viewCbcrWrPathEn && + ctrl->viewPath.multiFrag) + ctrl->vfeImaskLocal.viewCbcrPingpongIrq = TRUE; + } /* VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2 */ break; case VFE_AXI_LAST_OUTPUT_MODE_ENUM: break; - } /* switch */ + } /* switch */ /* Save the addresses for each path. */ /* output2 path */ @@ -3796,6 +3617,27 @@ void vfe_axi_output_config( vfe_axi_output(in, &ctrl->viewPath, &ctrl->encPath, axioutpw); } +void vfe_epoch1_config(struct vfe_cmds_camif_epoch *in) +{ + struct vfe_epoch1cfg cmd; + memset(&cmd, 0, sizeof(cmd)); + /* determine if epoch interrupt needs to be enabled. */ + if (in->enable == TRUE) { + cmd.epoch1Line = in->lineindex; + vfe_prog_hw(ctrl->vfebase + CAMIF_EPOCH_IRQ, (uint32_t *)&cmd, + sizeof(cmd)); + } + + /* Set the epoch1 interrupt mask. */ + ctrl->vfeImaskLocal.camifEpoch1Irq = in->enable; + ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal); + vfe_program_irq_mask(ctrl->vfeImaskPacked); + + /* Store the epoch1 data. */ + ctrl->vfeCamifEpoch1Local.enable = in->enable; + ctrl->vfeCamifEpoch1Local.lineindex = in->lineindex; +} + void vfe_camif_config(struct vfe_cmd_camif_config *in) { struct vfe_camifcfg cmd; @@ -3804,19 +3646,17 @@ void vfe_camif_config(struct vfe_cmd_camif_config *in) CDBG("camif.frame pixelsPerLine = %d\n", in->frame.pixelsPerLine); CDBG("camif.frame linesPerFrame = %d\n", in->frame.linesPerFrame); CDBG("camif.window firstpixel = %d\n", in->window.firstpixel); - CDBG("camif.window lastpixel = %d\n", in->window.lastpixel); - CDBG("camif.window firstline = %d\n", in->window.firstline); - CDBG("camif.window lastline = %d\n", in->window.lastline); + CDBG("camif.window lastpixel = %d\n", in->window.lastpixel); + CDBG("camif.window firstline = %d\n", in->window.firstline); + CDBG("camif.window lastline = %d\n", in->window.lastline); /* determine if epoch interrupt needs to be enabled. */ if ((in->epoch1.enable == TRUE) && - (in->epoch1.lineindex <= - in->frame.linesPerFrame)) + (in->epoch1.lineindex <= in->frame.linesPerFrame)) ctrl->vfeImaskLocal.camifEpoch1Irq = 1; if ((in->epoch2.enable == TRUE) && - (in->epoch2.lineindex <= - in->frame.linesPerFrame)) { + (in->epoch2.lineindex <= in->frame.linesPerFrame)) { ctrl->vfeImaskLocal.camifEpoch2Irq = 1; } @@ -3824,38 +3664,38 @@ void vfe_camif_config(struct vfe_cmd_camif_config *in) ctrl->vfeCamifConfigLocal.camifCfgFromCmd = in->camifConfig; /* EFS_Config */ - cmd.efsEndOfLine = in->EFS.efsendofline; - cmd.efsStartOfLine = in->EFS.efsstartofline; - cmd.efsEndOfFrame = in->EFS.efsendofframe; - cmd.efsStartOfFrame = in->EFS.efsstartofframe; + cmd.efsEndOfLine = in->EFS.efsendofline; + cmd.efsStartOfLine = in->EFS.efsstartofline; + cmd.efsEndOfFrame = in->EFS.efsendofframe; + cmd.efsStartOfFrame = in->EFS.efsstartofframe; /* Frame Config */ cmd.frameConfigPixelsPerLine = in->frame.pixelsPerLine; cmd.frameConfigLinesPerFrame = in->frame.linesPerFrame; /* Window Width Config */ - cmd.windowWidthCfgLastPixel = in->window.lastpixel; + cmd.windowWidthCfgLastPixel = in->window.lastpixel; cmd.windowWidthCfgFirstPixel = in->window.firstpixel; /* Window Height Config */ - cmd.windowHeightCfglastLine = in->window.lastline; - cmd.windowHeightCfgfirstLine = in->window.firstline; + cmd.windowHeightCfglastLine = in->window.lastline; + cmd.windowHeightCfgfirstLine = in->window.firstline; /* Subsample 1 Config */ cmd.subsample1CfgPixelSkip = in->subsample.pixelskipmask; - cmd.subsample1CfgLineSkip = in->subsample.lineskipmask; + cmd.subsample1CfgLineSkip = in->subsample.lineskipmask; /* Subsample 2 Config */ - cmd.subsample2CfgFrameSkip = in->subsample.frameskip; - cmd.subsample2CfgFrameSkipMode = in->subsample.frameskipmode; - cmd.subsample2CfgPixelSkipWrap = in->subsample.pixelskipwrap; + cmd.subsample2CfgFrameSkip = in->subsample.frameskip; + cmd.subsample2CfgFrameSkipMode = in->subsample.frameskipmode; + cmd.subsample2CfgPixelSkipWrap = in->subsample.pixelskipwrap; /* Epoch Interrupt */ cmd.epoch1Line = in->epoch1.lineindex; cmd.epoch2Line = in->epoch2.lineindex; vfe_prog_hw(ctrl->vfebase + CAMIF_EFS_CONFIG, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } void vfe_fov_crop_config(struct vfe_cmd_fov_crop_config *in) @@ -3866,15 +3706,15 @@ void vfe_fov_crop_config(struct vfe_cmd_fov_crop_config *in) ctrl->vfeModuleEnableLocal.cropEnable = in->enable; /* FOV Corp, Part 1 */ - cmd.lastPixel = in->lastPixel; + cmd.lastPixel = in->lastPixel; cmd.firstPixel = in->firstPixel; /* FOV Corp, Part 2 */ - cmd.lastLine = in->lastLine; - cmd.firstLine = in->firstLine; + cmd.lastLine = in->lastLine; + cmd.firstLine = in->firstLine; vfe_prog_hw(ctrl->vfebase + VFE_CROP_WIDTH_CFG, - (uint32_t *)&cmd, sizeof(cmd)); + (uint32_t *)&cmd, sizeof(cmd)); } void vfe_get_hw_version(struct vfe_cmd_hw_version *out) @@ -3886,52 +3726,47 @@ void vfe_get_hw_version(struct vfe_cmd_hw_version *out) ver = *((struct vfe_hw_ver *)&vfeHwVersionPacked); - out->coreVersion = ver.coreVersion; + out->coreVersion = ver.coreVersion; out->minorVersion = ver.minorVersion; out->majorVersion = ver.majorVersion; } static void vfe_reset_internal_variables(void) { - unsigned long flags; - /* local variables to program the hardware. */ ctrl->vfeImaskPacked = 0; ctrl->vfeImaskCompositePacked = 0; /* FALSE = disable, 1 = enable. */ memset(&ctrl->vfeModuleEnableLocal, 0, - sizeof(ctrl->vfeModuleEnableLocal)); + sizeof(ctrl->vfeModuleEnableLocal)); /* 0 = disable, 1 = enable */ memset(&ctrl->vfeCamifConfigLocal, 0, - sizeof(ctrl->vfeCamifConfigLocal)); + sizeof(ctrl->vfeCamifConfigLocal)); /* 0 = disable, 1 = enable */ memset(&ctrl->vfeImaskLocal, 0, sizeof(ctrl->vfeImaskLocal)); memset(&ctrl->vfeStatsCmdLocal, 0, sizeof(ctrl->vfeStatsCmdLocal)); memset(&ctrl->vfeBusConfigLocal, 0, sizeof(ctrl->vfeBusConfigLocal)); memset(&ctrl->vfeBusPmConfigLocal, 0, - sizeof(ctrl->vfeBusPmConfigLocal)); + sizeof(ctrl->vfeBusPmConfigLocal)); memset(&ctrl->vfeBusCmdLocal, 0, sizeof(ctrl->vfeBusCmdLocal)); memset(&ctrl->vfeInterruptNameLocal, 0, - sizeof(ctrl->vfeInterruptNameLocal)); + sizeof(ctrl->vfeInterruptNameLocal)); memset(&ctrl->vfeDroppedFrameCounts, 0, - sizeof(ctrl->vfeDroppedFrameCounts)); + sizeof(ctrl->vfeDroppedFrameCounts)); memset(&ctrl->vfeIrqThreadMsgLocal, 0, - sizeof(ctrl->vfeIrqThreadMsgLocal)); + sizeof(ctrl->vfeIrqThreadMsgLocal)); /* state control variables */ ctrl->vfeStartAckPendingFlag = FALSE; ctrl->vfeStopAckPending = FALSE; ctrl->vfeIrqCompositeMaskLocal.ceDoneSel = 0; - ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = - VFE_COMP_IRQ_BOTH_Y_CBCR; + ctrl->vfeIrqCompositeMaskLocal.encIrqComMask = VFE_COMP_IRQ_BOTH_Y_CBCR; ctrl->vfeIrqCompositeMaskLocal.viewIrqComMask = - VFE_COMP_IRQ_BOTH_Y_CBCR; + VFE_COMP_IRQ_BOTH_Y_CBCR; - spin_lock_irqsave(&ctrl->state_lock, flags); ctrl->vstate = VFE_STATE_IDLE; - spin_unlock_irqrestore(&ctrl->state_lock, flags); ctrl->axiOutputMode = VFE_AXI_LAST_OUTPUT_MODE_ENUM; /* 0 for continuous mode, 1 for snapshot mode */ @@ -3941,20 +3776,20 @@ static void vfe_reset_internal_variables(void) /* this is unsigned 32 bit integer. */ ctrl->vfeFrameId = 0; ctrl->vfeFrameSkip.output1Pattern = 0xffffffff; - ctrl->vfeFrameSkip.output1Period = 31; + ctrl->vfeFrameSkip.output1Period = 31; ctrl->vfeFrameSkip.output2Pattern = 0xffffffff; - ctrl->vfeFrameSkip.output2Period = 31; + ctrl->vfeFrameSkip.output2Period = 31; ctrl->vfeFrameSkipPattern = 0xffffffff; - ctrl->vfeFrameSkipCount = 0; - ctrl->vfeFrameSkipPeriod = 31; + ctrl->vfeFrameSkipCount = 0; + ctrl->vfeFrameSkipPeriod = 31; memset((void *)&ctrl->encPath, 0, sizeof(ctrl->encPath)); memset((void *)&ctrl->viewPath, 0, sizeof(ctrl->viewPath)); - ctrl->encPath.whichOutputPath = 1; - ctrl->encPath.cbcrStatusBit = 5; + ctrl->encPath.whichOutputPath = 1; + ctrl->encPath.cbcrStatusBit = 5; ctrl->viewPath.whichOutputPath = 0; - ctrl->viewPath.cbcrStatusBit = 7; + ctrl->viewPath.cbcrStatusBit = 7; ctrl->vfeTestGenStartFlag = FALSE; @@ -3968,7 +3803,7 @@ static void vfe_reset_internal_variables(void) memset(&ctrl->afStatsControl, 0, sizeof(ctrl->afStatsControl)); memset(&ctrl->awbStatsControl, 0, sizeof(ctrl->awbStatsControl)); vfe_set_stats_pingpong_address(&ctrl->afStatsControl, - &ctrl->awbStatsControl); + &ctrl->awbStatsControl); } void vfe_reset(void) @@ -3979,17 +3814,13 @@ void vfe_reset(void) ctrl->vfeImaskPacked = vfe_irq_pack(ctrl->vfeImaskLocal); /* disable all interrupts. */ - writel(VFE_DISABLE_ALL_IRQS, - ctrl->vfebase + VFE_IRQ_COMPOSITE_MASK); + writel(VFE_DISABLE_ALL_IRQS, ctrl->vfebase + VFE_IRQ_COMPOSITE_MASK); - /* clear all pending interrupts*/ - writel(VFE_CLEAR_ALL_IRQS, - ctrl->vfebase + VFE_IRQ_CLEAR); + /* clear all pending interrupts */ + writel(VFE_CLEAR_ALL_IRQS, ctrl->vfebase + VFE_IRQ_CLEAR); /* enable reset_ack interrupt. */ - writel(ctrl->vfeImaskPacked, - ctrl->vfebase + VFE_IRQ_MASK); + writel(ctrl->vfeImaskPacked, ctrl->vfebase + VFE_IRQ_MASK); - writel(VFE_RESET_UPON_RESET_CMD, - ctrl->vfebase + VFE_GLOBAL_RESET_CMD); + writel(VFE_RESET_UPON_RESET_CMD, ctrl->vfebase + VFE_GLOBAL_RESET_CMD); } diff --git a/drivers/media/video/msm/msm_vfe8x_proc.h b/drivers/media/video/msm/msm_vfe8x_proc.h index 91828569a4d7b..aef7ecac22221 100644 --- a/drivers/media/video/msm/msm_vfe8x_proc.h +++ b/drivers/media/video/msm/msm_vfe8x_proc.h @@ -1,5 +1,19 @@ -/* - * Copyright (C) 2008-2009 QUALCOMM Incorporated. +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ #ifndef __MSM_VFE8X_REG_H__ @@ -90,251 +104,251 @@ /* VFE I/O registers */ enum { - VFE_HW_VERSION = 0x00000000, - VFE_GLOBAL_RESET_CMD = 0x00000004, - VFE_MODULE_RESET = 0x00000008, - VFE_CGC_OVERRIDE = 0x0000000C, - VFE_MODULE_CFG = 0x00000010, - VFE_CFG = 0x00000014, - VFE_IRQ_MASK = 0x00000018, - VFE_IRQ_CLEAR = 0x0000001C, -VFE_IRQ_STATUS = 0x00000020, -VFE_IRQ_COMPOSITE_MASK = 0x00000024, -VFE_BUS_CMD = 0x00000028, -VFE_BUS_CFG = 0x0000002C, -VFE_BUS_ENC_Y_WR_PING_ADDR = 0x00000030, -VFE_BUS_ENC_Y_WR_PONG_ADDR = 0x00000034, -VFE_BUS_ENC_Y_WR_IMAGE_SIZE = 0x00000038, -VFE_BUS_ENC_Y_WR_BUFFER_CFG = 0x0000003C, -VFE_BUS_ENC_CBCR_WR_PING_ADDR = 0x00000040, -VFE_BUS_ENC_CBCR_WR_PONG_ADDR = 0x00000044, -VFE_BUS_ENC_CBCR_WR_IMAGE_SIZE = 0x00000048, -VFE_BUS_ENC_CBCR_WR_BUFFER_CFG = 0x0000004C, -VFE_BUS_VIEW_Y_WR_PING_ADDR = 0x00000050, -VFE_BUS_VIEW_Y_WR_PONG_ADDR = 0x00000054, -VFE_BUS_VIEW_Y_WR_IMAGE_SIZE = 0x00000058, -VFE_BUS_VIEW_Y_WR_BUFFER_CFG = 0x0000005C, -VFE_BUS_VIEW_CBCR_WR_PING_ADDR = 0x00000060, -VFE_BUS_VIEW_CBCR_WR_PONG_ADDR = 0x00000064, -VFE_BUS_VIEW_CBCR_WR_IMAGE_SIZE = 0x00000068, -VFE_BUS_VIEW_CBCR_WR_BUFFER_CFG = 0x0000006C, -VFE_BUS_STATS_AF_WR_PING_ADDR = 0x00000070, -VFE_BUS_STATS_AF_WR_PONG_ADDR = 0x00000074, -VFE_BUS_STATS_AWB_WR_PING_ADDR = 0x00000078, -VFE_BUS_STATS_AWB_WR_PONG_ADDR = 0x0000007C, -VFE_BUS_STATS_HIST_WR_PING_ADDR = 0x00000080, -VFE_BUS_STATS_HIST_WR_PONG_ADDR = 0x00000084, -VFE_BUS_STATS_WR_PRIORITY = 0x00000088, -VFE_BUS_STRIPE_RD_ADDR_0 = 0x0000008C, -VFE_BUS_STRIPE_RD_ADDR_1 = 0x00000090, -VFE_BUS_STRIPE_RD_ADDR_2 = 0x00000094, -VFE_BUS_STRIPE_RD_ADDR_3 = 0x00000098, -VFE_BUS_STRIPE_RD_VSIZE = 0x0000009C, -VFE_BUS_STRIPE_RD_HSIZE = 0x000000A0, -VFE_BUS_STRIPE_RD_BUFFER_CFG = 0x000000A4, -VFE_BUS_STRIPE_RD_UNPACK_CFG = 0x000000A8, -VFE_BUS_STRIPE_RD_UNPACK = 0x000000AC, -VFE_BUS_STRIPE_RD_PAD_SIZE = 0x000000B0, -VFE_BUS_STRIPE_RD_PAD_L_UNPACK = 0x000000B4, -VFE_BUS_STRIPE_RD_PAD_R_UNPACK = 0x000000B8, -VFE_BUS_STRIPE_RD_PAD_TB_UNPACK = 0x000000BC, -VFE_BUS_PINGPONG_IRQ_EN = 0x000000C0, -VFE_BUS_PINGPONG_STATUS = 0x000000C4, -VFE_BUS_PM_CMD = 0x000000C8, -VFE_BUS_PM_CFG = 0x000000CC, -VFE_BUS_ENC_Y_WR_PM_STATS_0 = 0x000000D0, -VFE_BUS_ENC_Y_WR_PM_STATS_1 = 0x000000D4, -VFE_BUS_ENC_CBCR_WR_PM_STATS_0 = 0x000000D8, -VFE_BUS_ENC_CBCR_WR_PM_STATS_1 = 0x000000DC, -VFE_BUS_VIEW_Y_WR_PM_STATS_0 = 0x000000E0, -VFE_BUS_VIEW_Y_WR_PM_STATS_1 = 0x000000E4, -VFE_BUS_VIEW_CBCR_WR_PM_STATS_0 = 0x000000E8, -VFE_BUS_VIEW_CBCR_WR_PM_STATS_1 = 0x000000EC, -VFE_BUS_MISR_CFG = 0x000000F4, -VFE_BUS_MISR_MAST_CFG_0 = 0x000000F8, -VFE_BUS_MISR_MAST_CFG_1 = 0x000000FC, -VFE_BUS_MISR_RD_VAL = 0x00000100, -VFE_AXI_CMD = 0x00000104, -VFE_AXI_CFG = 0x00000108, -VFE_AXI_STATUS = 0x0000010C, -CAMIF_COMMAND = 0x00000110, -CAMIF_CONFIG = 0x00000114, -CAMIF_EFS_CONFIG = 0x00000118, -CAMIF_FRAME_CONFIG = 0x0000011C, -CAMIF_WINDOW_WIDTH_CONFIG = 0x00000120, -CAMIF_WINDOW_HEIGHT_CONFIG = 0x00000124, -CAMIF_SUBSAMPLE1_CONFIG = 0x00000128, -CAMIF_SUBSAMPLE2_CONFIG = 0x0000012C, -CAMIF_EPOCH_IRQ = 0x00000130, -CAMIF_STATUS = 0x00000134, -CAMIF_MISR = 0x00000138, -VFE_SYNC_TIMER_CMD = 0x0000013C, -VFE_SYNC_TIMER0_LINE_START = 0x00000140, -VFE_SYNC_TIMER0_PIXEL_START = 0x00000144, -VFE_SYNC_TIMER0_PIXEL_DURATION = 0x00000148, -VFE_SYNC_TIMER1_LINE_START = 0x0000014C, -VFE_SYNC_TIMER1_PIXEL_START = 0x00000150, -VFE_SYNC_TIMER1_PIXEL_DURATION = 0x00000154, -VFE_SYNC_TIMER2_LINE_START = 0x00000158, -VFE_SYNC_TIMER2_PIXEL_START = 0x0000015C, -VFE_SYNC_TIMER2_PIXEL_DURATION = 0x00000160, -VFE_SYNC_TIMER_POLARITY = 0x00000164, -VFE_ASYNC_TIMER_CMD = 0x00000168, -VFE_ASYNC_TIMER0_CFG_0 = 0x0000016C, -VFE_ASYNC_TIMER0_CFG_1 = 0x00000170, -VFE_ASYNC_TIMER1_CFG_0 = 0x00000174, -VFE_ASYNC_TIMER1_CFG_1 = 0x00000178, -VFE_ASYNC_TIMER2_CFG_0 = 0x0000017C, -VFE_ASYNC_TIMER2_CFG_1 = 0x00000180, -VFE_ASYNC_TIMER3_CFG_0 = 0x00000184, -VFE_ASYNC_TIMER3_CFG_1 = 0x00000188, -VFE_TIMER_SEL = 0x0000018C, -VFE_REG_UPDATE_CMD = 0x00000190, -VFE_BLACK_EVEN_EVEN_VALUE = 0x00000194, -VFE_BLACK_EVEN_ODD_VALUE = 0x00000198, -VFE_BLACK_ODD_EVEN_VALUE = 0x0000019C, -VFE_BLACK_ODD_ODD_VALUE = 0x000001A0, -VFE_ROLLOFF_CFG_0 = 0x000001A4, -VFE_ROLLOFF_CFG_1 = 0x000001A8, -VFE_ROLLOFF_CFG_2 = 0x000001AC, -VFE_DEMUX_CFG = 0x000001B0, -VFE_DEMUX_GAIN_0 = 0x000001B4, -VFE_DEMUX_GAIN_1 = 0x000001B8, -VFE_DEMUX_EVEN_CFG = 0x000001BC, -VFE_DEMUX_ODD_CFG = 0x000001C0, -VFE_DEMOSAIC_CFG = 0x000001C4, -VFE_DEMOSAIC_ABF_CFG_0 = 0x000001C8, -VFE_DEMOSAIC_ABF_CFG_1 = 0x000001CC, -VFE_DEMOSAIC_BPC_CFG_0 = 0x000001D0, -VFE_DEMOSAIC_BPC_CFG_1 = 0x000001D4, -VFE_DEMOSAIC_STATUS = 0x000001D8, -VFE_CHROMA_UPSAMPLE_CFG = 0x000001DC, -VFE_CROP_WIDTH_CFG = 0x000001E0, -VFE_CROP_HEIGHT_CFG = 0x000001E4, -VFE_COLOR_CORRECT_COEFF_0 = 0x000001E8, -VFE_COLOR_CORRECT_COEFF_1 = 0x000001EC, -VFE_COLOR_CORRECT_COEFF_2 = 0x000001F0, -VFE_COLOR_CORRECT_COEFF_3 = 0x000001F4, -VFE_COLOR_CORRECT_COEFF_4 = 0x000001F8, -VFE_COLOR_CORRECT_COEFF_5 = 0x000001FC, -VFE_COLOR_CORRECT_COEFF_6 = 0x00000200, -VFE_COLOR_CORRECT_COEFF_7 = 0x00000204, -VFE_COLOR_CORRECT_COEFF_8 = 0x00000208, -VFE_COLOR_CORRECT_OFFSET_0 = 0x0000020C, -VFE_COLOR_CORRECT_OFFSET_1 = 0x00000210, -VFE_COLOR_CORRECT_OFFSET_2 = 0x00000214, -VFE_COLOR_CORRECT_COEFF_Q = 0x00000218, -VFE_LA_CFG = 0x0000021C, -VFE_LUT_BANK_SEL = 0x00000220, -VFE_CHROMA_ENHAN_A = 0x00000224, -VFE_CHROMA_ENHAN_B = 0x00000228, -VFE_CHROMA_ENHAN_C = 0x0000022C, -VFE_CHROMA_ENHAN_D = 0x00000230, -VFE_CHROMA_ENHAN_K = 0x00000234, -VFE_COLOR_CONVERT_COEFF_0 = 0x00000238, -VFE_COLOR_CONVERT_COEFF_1 = 0x0000023C, -VFE_COLOR_CONVERT_COEFF_2 = 0x00000240, -VFE_COLOR_CONVERT_OFFSET = 0x00000244, -VFE_ASF_CFG = 0x00000248, -VFE_ASF_SHARP_CFG_0 = 0x0000024C, -VFE_ASF_SHARP_CFG_1 = 0x00000250, -VFE_ASF_SHARP_COEFF_0 = 0x00000254, -VFE_ASF_SHARP_COEFF_1 = 0x00000258, -VFE_ASF_SHARP_COEFF_2 = 0x0000025C, -VFE_ASF_SHARP_COEFF_3 = 0x00000260, -VFE_ASF_MAX_EDGE = 0x00000264, -VFE_ASF_CROP_WIDTH_CFG = 0x00000268, -VFE_ASF_CROP_HEIGHT_CFG = 0x0000026C, -VFE_SCALE_CFG = 0x00000270, -VFE_SCALE_H_IMAGE_SIZE_CFG = 0x00000274, -VFE_SCALE_H_PHASE_CFG = 0x00000278, -VFE_SCALE_H_STRIPE_CFG = 0x0000027C, -VFE_SCALE_V_IMAGE_SIZE_CFG = 0x00000280, -VFE_SCALE_V_PHASE_CFG = 0x00000284, -VFE_SCALE_V_STRIPE_CFG = 0x00000288, -VFE_SCALE_Y_CFG = 0x0000028C, -VFE_SCALE_Y_H_IMAGE_SIZE_CFG = 0x00000290, -VFE_SCALE_Y_H_PHASE_CFG = 0x00000294, -VFE_SCALE_Y_V_IMAGE_SIZE_CFG = 0x00000298, -VFE_SCALE_Y_V_PHASE_CFG = 0x0000029C, -VFE_SCALE_CBCR_CFG = 0x000002A0, -VFE_SCALE_CBCR_H_IMAGE_SIZE_CFG = 0x000002A4, -VFE_SCALE_CBCR_H_PHASE_CFG = 0x000002A8, -VFE_SCALE_CBCR_V_IMAGE_SIZE_CFG = 0x000002AC, -VFE_SCALE_CBCR_V_PHASE_CFG = 0x000002B0, -VFE_WB_CFG = 0x000002B4, -VFE_CHROMA_SUPPRESS_CFG_0 = 0x000002B8, -VFE_CHROMA_SUPPRESS_CFG_1 = 0x000002BC, -VFE_CHROMA_SUBSAMPLE_CFG = 0x000002C0, -VFE_CHROMA_SUB_CROP_WIDTH_CFG = 0x000002C4, -VFE_CHROMA_SUB_CROP_HEIGHT_CFG = 0x000002C8, -VFE_FRAMEDROP_ENC_Y_CFG = 0x000002CC, -VFE_FRAMEDROP_ENC_CBCR_CFG = 0x000002D0, -VFE_FRAMEDROP_ENC_Y_PATTERN = 0x000002D4, -VFE_FRAMEDROP_ENC_CBCR_PATTERN = 0x000002D8, -VFE_FRAMEDROP_VIEW_Y_CFG = 0x000002DC, -VFE_FRAMEDROP_VIEW_CBCR_CFG = 0x000002E0, -VFE_FRAMEDROP_VIEW_Y_PATTERN = 0x000002E4, -VFE_FRAMEDROP_VIEW_CBCR_PATTERN = 0x000002E8, -VFE_CLAMP_MAX_CFG = 0x000002EC, -VFE_CLAMP_MIN_CFG = 0x000002F0, -VFE_STATS_CMD = 0x000002F4, -VFE_STATS_AF_CFG = 0x000002F8, -VFE_STATS_AF_DIM = 0x000002FC, -VFE_STATS_AF_GRID_0 = 0x00000300, -VFE_STATS_AF_GRID_1 = 0x00000304, -VFE_STATS_AF_GRID_2 = 0x00000308, -VFE_STATS_AF_GRID_3 = 0x0000030C, -VFE_STATS_AF_HEADER = 0x00000310, -VFE_STATS_AF_COEF0 = 0x00000314, -VFE_STATS_AF_COEF1 = 0x00000318, -VFE_STATS_AWBAE_CFG = 0x0000031C, -VFE_STATS_AXW_HEADER = 0x00000320, -VFE_STATS_AWB_MCFG = 0x00000324, -VFE_STATS_AWB_CCFG1 = 0x00000328, -VFE_STATS_AWB_CCFG2 = 0x0000032C, -VFE_STATS_HIST_HEADER = 0x00000330, -VFE_STATS_HIST_INNER_OFFSET = 0x00000334, -VFE_STATS_HIST_INNER_DIM = 0x00000338, -VFE_STATS_FRAME_SIZE = 0x0000033C, -VFE_DMI_CFG = 0x00000340, -VFE_DMI_ADDR = 0x00000344, -VFE_DMI_DATA_HI = 0x00000348, -VFE_DMI_DATA_LO = 0x0000034C, -VFE_DMI_RAM_AUTO_LOAD_CMD = 0x00000350, -VFE_DMI_RAM_AUTO_LOAD_STATUS = 0x00000354, -VFE_DMI_RAM_AUTO_LOAD_CFG = 0x00000358, -VFE_DMI_RAM_AUTO_LOAD_SEED = 0x0000035C, -VFE_TESTBUS_SEL = 0x00000360, -VFE_TESTGEN_CFG = 0x00000364, -VFE_SW_TESTGEN_CMD = 0x00000368, -VFE_HW_TESTGEN_CMD = 0x0000036C, -VFE_HW_TESTGEN_CFG = 0x00000370, -VFE_HW_TESTGEN_IMAGE_CFG = 0x00000374, -VFE_HW_TESTGEN_SOF_OFFSET_CFG = 0x00000378, -VFE_HW_TESTGEN_EOF_NOFFSET_CFG = 0x0000037C, -VFE_HW_TESTGEN_SOL_OFFSET_CFG = 0x00000380, -VFE_HW_TESTGEN_EOL_NOFFSET_CFG = 0x00000384, -VFE_HW_TESTGEN_HBI_CFG = 0x00000388, -VFE_HW_TESTGEN_VBL_CFG = 0x0000038C, -VFE_HW_TESTGEN_SOF_DUMMY_LINE_CFG2 = 0x00000390, -VFE_HW_TESTGEN_EOF_DUMMY_LINE_CFG2 = 0x00000394, -VFE_HW_TESTGEN_COLOR_BARS_CFG = 0x00000398, -VFE_HW_TESTGEN_RANDOM_CFG = 0x0000039C, -VFE_SPARE = 0x000003A0, + VFE_HW_VERSION = 0x00000000, + VFE_GLOBAL_RESET_CMD = 0x00000004, + VFE_MODULE_RESET = 0x00000008, + VFE_CGC_OVERRIDE = 0x0000000C, + VFE_MODULE_CFG = 0x00000010, + VFE_CFG = 0x00000014, + VFE_IRQ_MASK = 0x00000018, + VFE_IRQ_CLEAR = 0x0000001C, + VFE_IRQ_STATUS = 0x00000020, + VFE_IRQ_COMPOSITE_MASK = 0x00000024, + VFE_BUS_CMD = 0x00000028, + VFE_BUS_CFG = 0x0000002C, + VFE_BUS_ENC_Y_WR_PING_ADDR = 0x00000030, + VFE_BUS_ENC_Y_WR_PONG_ADDR = 0x00000034, + VFE_BUS_ENC_Y_WR_IMAGE_SIZE = 0x00000038, + VFE_BUS_ENC_Y_WR_BUFFER_CFG = 0x0000003C, + VFE_BUS_ENC_CBCR_WR_PING_ADDR = 0x00000040, + VFE_BUS_ENC_CBCR_WR_PONG_ADDR = 0x00000044, + VFE_BUS_ENC_CBCR_WR_IMAGE_SIZE = 0x00000048, + VFE_BUS_ENC_CBCR_WR_BUFFER_CFG = 0x0000004C, + VFE_BUS_VIEW_Y_WR_PING_ADDR = 0x00000050, + VFE_BUS_VIEW_Y_WR_PONG_ADDR = 0x00000054, + VFE_BUS_VIEW_Y_WR_IMAGE_SIZE = 0x00000058, + VFE_BUS_VIEW_Y_WR_BUFFER_CFG = 0x0000005C, + VFE_BUS_VIEW_CBCR_WR_PING_ADDR = 0x00000060, + VFE_BUS_VIEW_CBCR_WR_PONG_ADDR = 0x00000064, + VFE_BUS_VIEW_CBCR_WR_IMAGE_SIZE = 0x00000068, + VFE_BUS_VIEW_CBCR_WR_BUFFER_CFG = 0x0000006C, + VFE_BUS_STATS_AF_WR_PING_ADDR = 0x00000070, + VFE_BUS_STATS_AF_WR_PONG_ADDR = 0x00000074, + VFE_BUS_STATS_AWB_WR_PING_ADDR = 0x00000078, + VFE_BUS_STATS_AWB_WR_PONG_ADDR = 0x0000007C, + VFE_BUS_STATS_HIST_WR_PING_ADDR = 0x00000080, + VFE_BUS_STATS_HIST_WR_PONG_ADDR = 0x00000084, + VFE_BUS_STATS_WR_PRIORITY = 0x00000088, + VFE_BUS_STRIPE_RD_ADDR_0 = 0x0000008C, + VFE_BUS_STRIPE_RD_ADDR_1 = 0x00000090, + VFE_BUS_STRIPE_RD_ADDR_2 = 0x00000094, + VFE_BUS_STRIPE_RD_ADDR_3 = 0x00000098, + VFE_BUS_STRIPE_RD_VSIZE = 0x0000009C, + VFE_BUS_STRIPE_RD_HSIZE = 0x000000A0, + VFE_BUS_STRIPE_RD_BUFFER_CFG = 0x000000A4, + VFE_BUS_STRIPE_RD_UNPACK_CFG = 0x000000A8, + VFE_BUS_STRIPE_RD_UNPACK = 0x000000AC, + VFE_BUS_STRIPE_RD_PAD_SIZE = 0x000000B0, + VFE_BUS_STRIPE_RD_PAD_L_UNPACK = 0x000000B4, + VFE_BUS_STRIPE_RD_PAD_R_UNPACK = 0x000000B8, + VFE_BUS_STRIPE_RD_PAD_TB_UNPACK = 0x000000BC, + VFE_BUS_PINGPONG_IRQ_EN = 0x000000C0, + VFE_BUS_PINGPONG_STATUS = 0x000000C4, + VFE_BUS_PM_CMD = 0x000000C8, + VFE_BUS_PM_CFG = 0x000000CC, + VFE_BUS_ENC_Y_WR_PM_STATS_0 = 0x000000D0, + VFE_BUS_ENC_Y_WR_PM_STATS_1 = 0x000000D4, + VFE_BUS_ENC_CBCR_WR_PM_STATS_0 = 0x000000D8, + VFE_BUS_ENC_CBCR_WR_PM_STATS_1 = 0x000000DC, + VFE_BUS_VIEW_Y_WR_PM_STATS_0 = 0x000000E0, + VFE_BUS_VIEW_Y_WR_PM_STATS_1 = 0x000000E4, + VFE_BUS_VIEW_CBCR_WR_PM_STATS_0 = 0x000000E8, + VFE_BUS_VIEW_CBCR_WR_PM_STATS_1 = 0x000000EC, + VFE_BUS_MISR_CFG = 0x000000F4, + VFE_BUS_MISR_MAST_CFG_0 = 0x000000F8, + VFE_BUS_MISR_MAST_CFG_1 = 0x000000FC, + VFE_BUS_MISR_RD_VAL = 0x00000100, + VFE_AXI_CMD = 0x00000104, + VFE_AXI_CFG = 0x00000108, + VFE_AXI_STATUS = 0x0000010C, + CAMIF_COMMAND = 0x00000110, + CAMIF_CONFIG = 0x00000114, + CAMIF_EFS_CONFIG = 0x00000118, + CAMIF_FRAME_CONFIG = 0x0000011C, + CAMIF_WINDOW_WIDTH_CONFIG = 0x00000120, + CAMIF_WINDOW_HEIGHT_CONFIG = 0x00000124, + CAMIF_SUBSAMPLE1_CONFIG = 0x00000128, + CAMIF_SUBSAMPLE2_CONFIG = 0x0000012C, + CAMIF_EPOCH_IRQ = 0x00000130, + CAMIF_STATUS = 0x00000134, + CAMIF_MISR = 0x00000138, + VFE_SYNC_TIMER_CMD = 0x0000013C, + VFE_SYNC_TIMER0_LINE_START = 0x00000140, + VFE_SYNC_TIMER0_PIXEL_START = 0x00000144, + VFE_SYNC_TIMER0_PIXEL_DURATION = 0x00000148, + VFE_SYNC_TIMER1_LINE_START = 0x0000014C, + VFE_SYNC_TIMER1_PIXEL_START = 0x00000150, + VFE_SYNC_TIMER1_PIXEL_DURATION = 0x00000154, + VFE_SYNC_TIMER2_LINE_START = 0x00000158, + VFE_SYNC_TIMER2_PIXEL_START = 0x0000015C, + VFE_SYNC_TIMER2_PIXEL_DURATION = 0x00000160, + VFE_SYNC_TIMER_POLARITY = 0x00000164, + VFE_ASYNC_TIMER_CMD = 0x00000168, + VFE_ASYNC_TIMER0_CFG_0 = 0x0000016C, + VFE_ASYNC_TIMER0_CFG_1 = 0x00000170, + VFE_ASYNC_TIMER1_CFG_0 = 0x00000174, + VFE_ASYNC_TIMER1_CFG_1 = 0x00000178, + VFE_ASYNC_TIMER2_CFG_0 = 0x0000017C, + VFE_ASYNC_TIMER2_CFG_1 = 0x00000180, + VFE_ASYNC_TIMER3_CFG_0 = 0x00000184, + VFE_ASYNC_TIMER3_CFG_1 = 0x00000188, + VFE_TIMER_SEL = 0x0000018C, + VFE_REG_UPDATE_CMD = 0x00000190, + VFE_BLACK_EVEN_EVEN_VALUE = 0x00000194, + VFE_BLACK_EVEN_ODD_VALUE = 0x00000198, + VFE_BLACK_ODD_EVEN_VALUE = 0x0000019C, + VFE_BLACK_ODD_ODD_VALUE = 0x000001A0, + VFE_ROLLOFF_CFG_0 = 0x000001A4, + VFE_ROLLOFF_CFG_1 = 0x000001A8, + VFE_ROLLOFF_CFG_2 = 0x000001AC, + VFE_DEMUX_CFG = 0x000001B0, + VFE_DEMUX_GAIN_0 = 0x000001B4, + VFE_DEMUX_GAIN_1 = 0x000001B8, + VFE_DEMUX_EVEN_CFG = 0x000001BC, + VFE_DEMUX_ODD_CFG = 0x000001C0, + VFE_DEMOSAIC_CFG = 0x000001C4, + VFE_DEMOSAIC_ABF_CFG_0 = 0x000001C8, + VFE_DEMOSAIC_ABF_CFG_1 = 0x000001CC, + VFE_DEMOSAIC_BPC_CFG_0 = 0x000001D0, + VFE_DEMOSAIC_BPC_CFG_1 = 0x000001D4, + VFE_DEMOSAIC_STATUS = 0x000001D8, + VFE_CHROMA_UPSAMPLE_CFG = 0x000001DC, + VFE_CROP_WIDTH_CFG = 0x000001E0, + VFE_CROP_HEIGHT_CFG = 0x000001E4, + VFE_COLOR_CORRECT_COEFF_0 = 0x000001E8, + VFE_COLOR_CORRECT_COEFF_1 = 0x000001EC, + VFE_COLOR_CORRECT_COEFF_2 = 0x000001F0, + VFE_COLOR_CORRECT_COEFF_3 = 0x000001F4, + VFE_COLOR_CORRECT_COEFF_4 = 0x000001F8, + VFE_COLOR_CORRECT_COEFF_5 = 0x000001FC, + VFE_COLOR_CORRECT_COEFF_6 = 0x00000200, + VFE_COLOR_CORRECT_COEFF_7 = 0x00000204, + VFE_COLOR_CORRECT_COEFF_8 = 0x00000208, + VFE_COLOR_CORRECT_OFFSET_0 = 0x0000020C, + VFE_COLOR_CORRECT_OFFSET_1 = 0x00000210, + VFE_COLOR_CORRECT_OFFSET_2 = 0x00000214, + VFE_COLOR_CORRECT_COEFF_Q = 0x00000218, + VFE_LA_CFG = 0x0000021C, + VFE_LUT_BANK_SEL = 0x00000220, + VFE_CHROMA_ENHAN_A = 0x00000224, + VFE_CHROMA_ENHAN_B = 0x00000228, + VFE_CHROMA_ENHAN_C = 0x0000022C, + VFE_CHROMA_ENHAN_D = 0x00000230, + VFE_CHROMA_ENHAN_K = 0x00000234, + VFE_COLOR_CONVERT_COEFF_0 = 0x00000238, + VFE_COLOR_CONVERT_COEFF_1 = 0x0000023C, + VFE_COLOR_CONVERT_COEFF_2 = 0x00000240, + VFE_COLOR_CONVERT_OFFSET = 0x00000244, + VFE_ASF_CFG = 0x00000248, + VFE_ASF_SHARP_CFG_0 = 0x0000024C, + VFE_ASF_SHARP_CFG_1 = 0x00000250, + VFE_ASF_SHARP_COEFF_0 = 0x00000254, + VFE_ASF_SHARP_COEFF_1 = 0x00000258, + VFE_ASF_SHARP_COEFF_2 = 0x0000025C, + VFE_ASF_SHARP_COEFF_3 = 0x00000260, + VFE_ASF_MAX_EDGE = 0x00000264, + VFE_ASF_CROP_WIDTH_CFG = 0x00000268, + VFE_ASF_CROP_HEIGHT_CFG = 0x0000026C, + VFE_SCALE_CFG = 0x00000270, + VFE_SCALE_H_IMAGE_SIZE_CFG = 0x00000274, + VFE_SCALE_H_PHASE_CFG = 0x00000278, + VFE_SCALE_H_STRIPE_CFG = 0x0000027C, + VFE_SCALE_V_IMAGE_SIZE_CFG = 0x00000280, + VFE_SCALE_V_PHASE_CFG = 0x00000284, + VFE_SCALE_V_STRIPE_CFG = 0x00000288, + VFE_SCALE_Y_CFG = 0x0000028C, + VFE_SCALE_Y_H_IMAGE_SIZE_CFG = 0x00000290, + VFE_SCALE_Y_H_PHASE_CFG = 0x00000294, + VFE_SCALE_Y_V_IMAGE_SIZE_CFG = 0x00000298, + VFE_SCALE_Y_V_PHASE_CFG = 0x0000029C, + VFE_SCALE_CBCR_CFG = 0x000002A0, + VFE_SCALE_CBCR_H_IMAGE_SIZE_CFG = 0x000002A4, + VFE_SCALE_CBCR_H_PHASE_CFG = 0x000002A8, + VFE_SCALE_CBCR_V_IMAGE_SIZE_CFG = 0x000002AC, + VFE_SCALE_CBCR_V_PHASE_CFG = 0x000002B0, + VFE_WB_CFG = 0x000002B4, + VFE_CHROMA_SUPPRESS_CFG_0 = 0x000002B8, + VFE_CHROMA_SUPPRESS_CFG_1 = 0x000002BC, + VFE_CHROMA_SUBSAMPLE_CFG = 0x000002C0, + VFE_CHROMA_SUB_CROP_WIDTH_CFG = 0x000002C4, + VFE_CHROMA_SUB_CROP_HEIGHT_CFG = 0x000002C8, + VFE_FRAMEDROP_ENC_Y_CFG = 0x000002CC, + VFE_FRAMEDROP_ENC_CBCR_CFG = 0x000002D0, + VFE_FRAMEDROP_ENC_Y_PATTERN = 0x000002D4, + VFE_FRAMEDROP_ENC_CBCR_PATTERN = 0x000002D8, + VFE_FRAMEDROP_VIEW_Y_CFG = 0x000002DC, + VFE_FRAMEDROP_VIEW_CBCR_CFG = 0x000002E0, + VFE_FRAMEDROP_VIEW_Y_PATTERN = 0x000002E4, + VFE_FRAMEDROP_VIEW_CBCR_PATTERN = 0x000002E8, + VFE_CLAMP_MAX_CFG = 0x000002EC, + VFE_CLAMP_MIN_CFG = 0x000002F0, + VFE_STATS_CMD = 0x000002F4, + VFE_STATS_AF_CFG = 0x000002F8, + VFE_STATS_AF_DIM = 0x000002FC, + VFE_STATS_AF_GRID_0 = 0x00000300, + VFE_STATS_AF_GRID_1 = 0x00000304, + VFE_STATS_AF_GRID_2 = 0x00000308, + VFE_STATS_AF_GRID_3 = 0x0000030C, + VFE_STATS_AF_HEADER = 0x00000310, + VFE_STATS_AF_COEF0 = 0x00000314, + VFE_STATS_AF_COEF1 = 0x00000318, + VFE_STATS_AWBAE_CFG = 0x0000031C, + VFE_STATS_AXW_HEADER = 0x00000320, + VFE_STATS_AWB_MCFG = 0x00000324, + VFE_STATS_AWB_CCFG1 = 0x00000328, + VFE_STATS_AWB_CCFG2 = 0x0000032C, + VFE_STATS_HIST_HEADER = 0x00000330, + VFE_STATS_HIST_INNER_OFFSET = 0x00000334, + VFE_STATS_HIST_INNER_DIM = 0x00000338, + VFE_STATS_FRAME_SIZE = 0x0000033C, + VFE_DMI_CFG = 0x00000340, + VFE_DMI_ADDR = 0x00000344, + VFE_DMI_DATA_HI = 0x00000348, + VFE_DMI_DATA_LO = 0x0000034C, + VFE_DMI_RAM_AUTO_LOAD_CMD = 0x00000350, + VFE_DMI_RAM_AUTO_LOAD_STATUS = 0x00000354, + VFE_DMI_RAM_AUTO_LOAD_CFG = 0x00000358, + VFE_DMI_RAM_AUTO_LOAD_SEED = 0x0000035C, + VFE_TESTBUS_SEL = 0x00000360, + VFE_TESTGEN_CFG = 0x00000364, + VFE_SW_TESTGEN_CMD = 0x00000368, + VFE_HW_TESTGEN_CMD = 0x0000036C, + VFE_HW_TESTGEN_CFG = 0x00000370, + VFE_HW_TESTGEN_IMAGE_CFG = 0x00000374, + VFE_HW_TESTGEN_SOF_OFFSET_CFG = 0x00000378, + VFE_HW_TESTGEN_EOF_NOFFSET_CFG = 0x0000037C, + VFE_HW_TESTGEN_SOL_OFFSET_CFG = 0x00000380, + VFE_HW_TESTGEN_EOL_NOFFSET_CFG = 0x00000384, + VFE_HW_TESTGEN_HBI_CFG = 0x00000388, + VFE_HW_TESTGEN_VBL_CFG = 0x0000038C, + VFE_HW_TESTGEN_SOF_DUMMY_LINE_CFG2 = 0x00000390, + VFE_HW_TESTGEN_EOF_DUMMY_LINE_CFG2 = 0x00000394, + VFE_HW_TESTGEN_COLOR_BARS_CFG = 0x00000398, + VFE_HW_TESTGEN_RANDOM_CFG = 0x0000039C, + VFE_SPARE = 0x000003A0, }; #define ping 0x0 #define pong 0x1 struct vfe_bus_cfg_data { - boolean stripeRdPathEn; - boolean encYWrPathEn; - boolean encCbcrWrPathEn; - boolean viewYWrPathEn; - boolean viewCbcrWrPathEn; + boolean stripeRdPathEn; + boolean encYWrPathEn; + boolean encCbcrWrPathEn; + boolean viewYWrPathEn; + boolean viewCbcrWrPathEn; enum VFE_RAW_PIXEL_DATA_SIZE rawPixelDataSize; - enum VFE_RAW_WR_PATH_SEL rawWritePathSelect; + enum VFE_RAW_WR_PATH_SEL rawWritePathSelect; }; struct vfe_camif_cfg_data { @@ -353,76 +367,76 @@ struct vfe_irq_composite_mask_config { struct vfe_output_path { uint32_t addressBuffer[8]; uint16_t fragIndex; - boolean hwCurrentFlag; - uint8_t *hwRegPingAddress; - uint8_t *hwRegPongAddress; + boolean hwCurrentFlag; + uint8_t *hwRegPingAddress; + uint8_t *hwRegPongAddress; }; struct vfe_output_path_combo { - boolean whichOutputPath; - boolean pathEnabled; - boolean multiFrag; - uint8_t fragCount; - boolean ackPending; - uint8_t currentFrame; - uint32_t nextFrameAddrBuf[8]; - struct vfe_output_path yPath; - struct vfe_output_path cbcrPath; - uint8_t snapshotPendingCount; - boolean pmEnabled; - uint8_t cbcrStatusBit; + boolean whichOutputPath; + boolean pathEnabled; + boolean multiFrag; + uint8_t fragCount; + boolean ackPending; + uint8_t currentFrame; + uint32_t nextFrameAddrBuf[8]; + struct vfe_output_path yPath; + struct vfe_output_path cbcrPath; + uint8_t snapshotPendingCount; + boolean pmEnabled; + uint8_t cbcrStatusBit; }; struct vfe_stats_control { - boolean ackPending; + boolean ackPending; uint32_t addressBuffer[2]; uint32_t nextFrameAddrBuf; - boolean pingPongStatus; - uint8_t *hwRegPingAddress; - uint8_t *hwRegPongAddress; + boolean pingPongStatus; + uint8_t *hwRegPingAddress; + uint8_t *hwRegPongAddress; uint32_t droppedStatsFrameCount; uint32_t bufToRender; }; struct vfe_gamma_lut_sel { - boolean ch0BankSelect; - boolean ch1BankSelect; - boolean ch2BankSelect; + boolean ch0BankSelect; + boolean ch1BankSelect; + boolean ch2BankSelect; }; struct vfe_interrupt_mask { - boolean camifErrorIrq; - boolean camifSofIrq; - boolean camifEolIrq; - boolean camifEofIrq; - boolean camifEpoch1Irq; - boolean camifEpoch2Irq; - boolean camifOverflowIrq; - boolean ceIrq; - boolean regUpdateIrq; - boolean resetAckIrq; - boolean encYPingpongIrq; - boolean encCbcrPingpongIrq; - boolean viewYPingpongIrq; - boolean viewCbcrPingpongIrq; - boolean rdPingpongIrq; - boolean afPingpongIrq; - boolean awbPingpongIrq; - boolean histPingpongIrq; - boolean encIrq; - boolean viewIrq; - boolean busOverflowIrq; - boolean afOverflowIrq; - boolean awbOverflowIrq; - boolean syncTimer0Irq; - boolean syncTimer1Irq; - boolean syncTimer2Irq; - boolean asyncTimer0Irq; - boolean asyncTimer1Irq; - boolean asyncTimer2Irq; - boolean asyncTimer3Irq; - boolean axiErrorIrq; - boolean violationIrq; + boolean camifErrorIrq; + boolean camifSofIrq; + boolean camifEolIrq; + boolean camifEofIrq; + boolean camifEpoch1Irq; + boolean camifEpoch2Irq; + boolean camifOverflowIrq; + boolean ceIrq; + boolean regUpdateIrq; + boolean resetAckIrq; + boolean encYPingpongIrq; + boolean encCbcrPingpongIrq; + boolean viewYPingpongIrq; + boolean viewCbcrPingpongIrq; + boolean rdPingpongIrq; + boolean afPingpongIrq; + boolean awbPingpongIrq; + boolean histPingpongIrq; + boolean encIrq; + boolean viewIrq; + boolean busOverflowIrq; + boolean afOverflowIrq; + boolean awbOverflowIrq; + boolean syncTimer0Irq; + boolean syncTimer1Irq; + boolean syncTimer2Irq; + boolean asyncTimer0Irq; + boolean asyncTimer1Irq; + boolean asyncTimer2Irq; + boolean asyncTimer3Irq; + boolean axiErrorIrq; + boolean violationIrq; }; enum vfe_interrupt_name { @@ -461,90 +475,90 @@ enum vfe_interrupt_name { }; enum VFE_DMI_RAM_SEL { - NO_MEM_SELECTED = 0, - ROLLOFF_RAM = 0x1, - RGBLUT_RAM_CH0_BANK0 = 0x2, - RGBLUT_RAM_CH0_BANK1 = 0x3, - RGBLUT_RAM_CH1_BANK0 = 0x4, - RGBLUT_RAM_CH1_BANK1 = 0x5, - RGBLUT_RAM_CH2_BANK0 = 0x6, - RGBLUT_RAM_CH2_BANK1 = 0x7, - STATS_HIST_CB_EVEN_RAM = 0x8, - STATS_HIST_CB_ODD_RAM = 0x9, - STATS_HIST_CR_EVEN_RAM = 0xa, - STATS_HIST_CR_ODD_RAM = 0xb, - RGBLUT_CHX_BANK0 = 0xc, - RGBLUT_CHX_BANK1 = 0xd, + NO_MEM_SELECTED = 0, + ROLLOFF_RAM = 0x1, + RGBLUT_RAM_CH0_BANK0 = 0x2, + RGBLUT_RAM_CH0_BANK1 = 0x3, + RGBLUT_RAM_CH1_BANK0 = 0x4, + RGBLUT_RAM_CH1_BANK1 = 0x5, + RGBLUT_RAM_CH2_BANK0 = 0x6, + RGBLUT_RAM_CH2_BANK1 = 0x7, + STATS_HIST_CB_EVEN_RAM = 0x8, + STATS_HIST_CB_ODD_RAM = 0x9, + STATS_HIST_CR_EVEN_RAM = 0xa, + STATS_HIST_CR_ODD_RAM = 0xb, + RGBLUT_CHX_BANK0 = 0xc, + RGBLUT_CHX_BANK1 = 0xd, LUMA_ADAPT_LUT_RAM_BANK0 = 0xe, LUMA_ADAPT_LUT_RAM_BANK1 = 0xf }; struct vfe_module_enable { - boolean blackLevelCorrectionEnable; - boolean lensRollOffEnable; - boolean demuxEnable; - boolean chromaUpsampleEnable; - boolean demosaicEnable; - boolean statsEnable; - boolean cropEnable; - boolean mainScalerEnable; - boolean whiteBalanceEnable; - boolean colorCorrectionEnable; - boolean yHistEnable; - boolean skinToneEnable; - boolean lumaAdaptationEnable; - boolean rgbLUTEnable; - boolean chromaEnhanEnable; - boolean asfEnable; - boolean chromaSuppressionEnable; - boolean chromaSubsampleEnable; - boolean scaler2YEnable; - boolean scaler2CbcrEnable; + boolean blackLevelCorrectionEnable; + boolean lensRollOffEnable; + boolean demuxEnable; + boolean chromaUpsampleEnable; + boolean demosaicEnable; + boolean statsEnable; + boolean cropEnable; + boolean mainScalerEnable; + boolean whiteBalanceEnable; + boolean colorCorrectionEnable; + boolean yHistEnable; + boolean skinToneEnable; + boolean lumaAdaptationEnable; + boolean rgbLUTEnable; + boolean chromaEnhanEnable; + boolean asfEnable; + boolean chromaSuppressionEnable; + boolean chromaSubsampleEnable; + boolean scaler2YEnable; + boolean scaler2CbcrEnable; }; struct vfe_bus_cmd_data { - boolean stripeReload; - boolean busPingpongReload; - boolean statsPingpongReload; + boolean stripeReload; + boolean busPingpongReload; + boolean statsPingpongReload; }; struct vfe_stats_cmd_data { - boolean autoFocusEnable; - boolean axwEnable; - boolean histEnable; - boolean clearHistEnable; - boolean histAutoClearEnable; - boolean colorConversionEnable; + boolean autoFocusEnable; + boolean axwEnable; + boolean histEnable; + boolean clearHistEnable; + boolean histAutoClearEnable; + boolean colorConversionEnable; }; struct vfe_hw_ver { uint32_t minorVersion:8; uint32_t majorVersion:8; uint32_t coreVersion:4; - uint32_t /* reserved */ : 12; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 12; +} __attribute__ ((packed, aligned(4))); struct vfe_cfg { uint32_t pixelPattern:3; - uint32_t /* reserved */ : 13; + uint32_t /* reserved */ : 13; uint32_t inputSource:2; - uint32_t /* reserved */ : 14; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 14; +} __attribute__ ((packed, aligned(4))); struct vfe_buscmd { - uint32_t stripeReload:1; - uint32_t /* reserved */ : 3; - uint32_t busPingpongReload:1; - uint32_t statsPingpongReload:1; - uint32_t /* reserved */ : 26; -} __attribute__((packed, aligned(4))); + uint32_t stripeReload:1; + uint32_t /* reserved */ : 3; + uint32_t busPingpongReload:1; + uint32_t statsPingpongReload:1; + uint32_t /* reserved */ : 26; +} __attribute__ ((packed, aligned(4))); struct VFE_Irq_Composite_MaskType { - uint32_t encIrqComMaskBits:2; - uint32_t viewIrqComMaskBits:2; - uint32_t ceDoneSelBits:5; - uint32_t /* reserved */ : 23; -} __attribute__((packed, aligned(4))); + uint32_t encIrqComMaskBits:2; + uint32_t viewIrqComMaskBits:2; + uint32_t ceDoneSelBits:5; + uint32_t /* reserved */ : 23; +} __attribute__ ((packed, aligned(4))); struct vfe_mod_enable { uint32_t blackLevelCorrectionEnable:1; @@ -567,8 +581,8 @@ struct vfe_mod_enable { uint32_t chromaSubsampleEnable:1; uint32_t scaler2YEnable:1; uint32_t scaler2CbcrEnable:1; - uint32_t /* reserved */ : 14; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 14; +} __attribute__ ((packed, aligned(4))); struct vfe_irqenable { uint32_t camifErrorIrq:1; @@ -603,30 +617,30 @@ struct vfe_irqenable { uint32_t asyncTimer3Irq:1; uint32_t axiErrorIrq:1; uint32_t violationIrq:1; -} __attribute__((packed, aligned(4))); +} __attribute__ ((packed, aligned(4))); struct vfe_upsample_cfg { uint32_t chromaCositingForYCbCrInputs:1; - uint32_t /* reserved */ : 31; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 31; +} __attribute__ ((packed, aligned(4))); struct VFE_CAMIFConfigType { /* CAMIF Config */ - uint32_t /* reserved */ : 1; - uint32_t VSyncEdge:1; - uint32_t HSyncEdge:1; - uint32_t syncMode:2; - uint32_t vfeSubsampleEnable:1; - uint32_t /* reserved */ : 1; - uint32_t busSubsampleEnable:1; - uint32_t camif2vfeEnable:1; - uint32_t /* reserved */ : 1; - uint32_t camif2busEnable:1; - uint32_t irqSubsampleEnable:1; - uint32_t binningEnable:1; - uint32_t /* reserved */ : 18; - uint32_t misrEnable:1; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 1; + uint32_t VSyncEdge:1; + uint32_t HSyncEdge:1; + uint32_t syncMode:2; + uint32_t vfeSubsampleEnable:1; + uint32_t /* reserved */ : 1; + uint32_t busSubsampleEnable:1; + uint32_t camif2vfeEnable:1; + uint32_t /* reserved */ : 1; + uint32_t camif2busEnable:1; + uint32_t irqSubsampleEnable:1; + uint32_t binningEnable:1; + uint32_t /* reserved */ : 18; + uint32_t misrEnable:1; +} __attribute__ ((packed, aligned(4))); struct vfe_camifcfg { /* EFS_Config */ @@ -636,19 +650,19 @@ struct vfe_camifcfg { uint32_t efsStartOfFrame:8; /* Frame Config */ uint32_t frameConfigPixelsPerLine:14; - uint32_t /* reserved */ : 2; + uint32_t /* reserved */ : 2; uint32_t frameConfigLinesPerFrame:14; - uint32_t /* reserved */ : 2; + uint32_t /* reserved */ : 2; /* Window Width Config */ uint32_t windowWidthCfgLastPixel:14; - uint32_t /* reserved */ : 2; + uint32_t /* reserved */ : 2; uint32_t windowWidthCfgFirstPixel:14; - uint32_t /* reserved */ : 2; + uint32_t /* reserved */ : 2; /* Window Height Config */ uint32_t windowHeightCfglastLine:14; - uint32_t /* reserved */ : 2; + uint32_t /* reserved */ : 2; uint32_t windowHeightCfgfirstLine:14; - uint32_t /* reserved */ : 2; + uint32_t /* reserved */ : 2; /* Subsample 1 Config */ uint32_t subsample1CfgPixelSkip:16; uint32_t subsample1CfgLineSkip:16; @@ -656,231 +670,238 @@ struct vfe_camifcfg { uint32_t subsample2CfgFrameSkip:4; uint32_t subsample2CfgFrameSkipMode:1; uint32_t subsample2CfgPixelSkipWrap:1; - uint32_t /* reserved */ : 26; + uint32_t /* reserved */ : 26; /* Epoch Interrupt */ uint32_t epoch1Line:14; - uint32_t /* reserved */ : 2; + uint32_t /* reserved */ : 2; uint32_t epoch2Line:14; + uint32_t /* reserved */ : 2; +} __attribute__ ((packed, aligned(4))); + +struct vfe_epoch1cfg { + /* Epoch Interrupt */ + uint32_t epoch1Line:14; uint32_t /* reserved */ : 2; -} __attribute__((packed, aligned(4))); +} __attribute__ ((packed, aligned(4))); + struct vfe_camifframe_update { uint32_t pixelsPerLine:14; - uint32_t /* reserved */ : 2; + uint32_t /* reserved */ : 2; uint32_t linesPerFrame:14; - uint32_t /* reserved */ : 2; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 2; +} __attribute__ ((packed, aligned(4))); struct vfe_axi_bus_cfg { - uint32_t stripeRdPathEn:1; - uint32_t /* reserved */ : 3; - uint32_t encYWrPathEn:1; - uint32_t encCbcrWrPathEn:1; - uint32_t viewYWrPathEn:1; - uint32_t viewCbcrWrPathEn:1; - uint32_t rawPixelDataSize:2; - uint32_t rawWritePathSelect:2; - uint32_t /* reserved */ : 20; -} __attribute__((packed, aligned(4))); + uint32_t stripeRdPathEn:1; + uint32_t /* reserved */ : 3; + uint32_t encYWrPathEn:1; + uint32_t encCbcrWrPathEn:1; + uint32_t viewYWrPathEn:1; + uint32_t viewCbcrWrPathEn:1; + uint32_t rawPixelDataSize:2; + uint32_t rawWritePathSelect:2; + uint32_t /* reserved */ : 20; +} __attribute__ ((packed, aligned(4))); struct vfe_axi_out_cfg { - uint32_t out2YPingAddr:32; - uint32_t out2YPongAddr:32; - uint32_t out2YImageHeight:12; - uint32_t /* reserved */ : 4; - uint32_t out2YImageWidthin64bit:10; - uint32_t /* reserved */ : 6; - uint32_t out2YBurstLength:2; - uint32_t /* reserved */ : 2; - uint32_t out2YNumRows:12; - uint32_t out2YRowIncrementIn64bit:12; - uint32_t /* reserved */ : 4; - uint32_t out2CbcrPingAddr:32; - uint32_t out2CbcrPongAddr:32; - uint32_t out2CbcrImageHeight:12; - uint32_t /* reserved */ : 4; - uint32_t out2CbcrImageWidthIn64bit:10; - uint32_t /* reserved */ : 6; - uint32_t out2CbcrBurstLength:2; - uint32_t /* reserved */ : 2; - uint32_t out2CbcrNumRows:12; - uint32_t out2CbcrRowIncrementIn64bit:12; - uint32_t /* reserved */ : 4; - uint32_t out1YPingAddr:32; - uint32_t out1YPongAddr:32; - uint32_t out1YImageHeight:12; - uint32_t /* reserved */ : 4; - uint32_t out1YImageWidthin64bit:10; - uint32_t /* reserved */ : 6; - uint32_t out1YBurstLength:2; - uint32_t /* reserved */ : 2; - uint32_t out1YNumRows:12; - uint32_t out1YRowIncrementIn64bit:12; - uint32_t /* reserved */ : 4; - uint32_t out1CbcrPingAddr:32; - uint32_t out1CbcrPongAddr:32; - uint32_t out1CbcrImageHeight:12; - uint32_t /* reserved */ : 4; - uint32_t out1CbcrImageWidthIn64bit:10; - uint32_t /* reserved */ : 6; - uint32_t out1CbcrBurstLength:2; - uint32_t /* reserved */ : 2; - uint32_t out1CbcrNumRows:12; - uint32_t out1CbcrRowIncrementIn64bit:12; - uint32_t /* reserved */ : 4; -} __attribute__((packed, aligned(4))); + uint32_t out2YPingAddr:32; + uint32_t out2YPongAddr:32; + uint32_t out2YImageHeight:12; + uint32_t /* reserved */ : 4; + uint32_t out2YImageWidthin64bit:10; + uint32_t /* reserved */ : 6; + uint32_t out2YBurstLength:2; + uint32_t /* reserved */ : 2; + uint32_t out2YNumRows:12; + uint32_t out2YRowIncrementIn64bit:12; + uint32_t /* reserved */ : 4; + uint32_t out2CbcrPingAddr:32; + uint32_t out2CbcrPongAddr:32; + uint32_t out2CbcrImageHeight:12; + uint32_t /* reserved */ : 4; + uint32_t out2CbcrImageWidthIn64bit:10; + uint32_t /* reserved */ : 6; + uint32_t out2CbcrBurstLength:2; + uint32_t /* reserved */ : 2; + uint32_t out2CbcrNumRows:12; + uint32_t out2CbcrRowIncrementIn64bit:12; + uint32_t /* reserved */ : 4; + uint32_t out1YPingAddr:32; + uint32_t out1YPongAddr:32; + uint32_t out1YImageHeight:12; + uint32_t /* reserved */ : 4; + uint32_t out1YImageWidthin64bit:10; + uint32_t /* reserved */ : 6; + uint32_t out1YBurstLength:2; + uint32_t /* reserved */ : 2; + uint32_t out1YNumRows:12; + uint32_t out1YRowIncrementIn64bit:12; + uint32_t /* reserved */ : 4; + uint32_t out1CbcrPingAddr:32; + uint32_t out1CbcrPongAddr:32; + uint32_t out1CbcrImageHeight:12; + uint32_t /* reserved */ : 4; + uint32_t out1CbcrImageWidthIn64bit:10; + uint32_t /* reserved */ : 6; + uint32_t out1CbcrBurstLength:2; + uint32_t /* reserved */ : 2; + uint32_t out1CbcrNumRows:12; + uint32_t out1CbcrRowIncrementIn64bit:12; + uint32_t /* reserved */ : 4; +} __attribute__ ((packed, aligned(4))); struct vfe_output_clamp_cfg { /* Output Clamp Maximums */ uint32_t yChanMax:8; uint32_t cbChanMax:8; uint32_t crChanMax:8; - uint32_t /* reserved */ : 8; + uint32_t /* reserved */ : 8; /* Output Clamp Minimums */ uint32_t yChanMin:8; uint32_t cbChanMin:8; uint32_t crChanMin:8; - uint32_t /* reserved */ : 8; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 8; +} __attribute__ ((packed, aligned(4))); struct vfe_fov_crop_cfg { uint32_t lastPixel:12; - uint32_t /* reserved */ : 4; + uint32_t /* reserved */ : 4; uint32_t firstPixel:12; - uint32_t /* reserved */ : 4; + uint32_t /* reserved */ : 4; /* FOV Corp, Part 2 */ uint32_t lastLine:12; - uint32_t /* reserved */ : 4; + uint32_t /* reserved */ : 4; uint32_t firstLine:12; - uint32_t /* reserved */ : 4; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 4; +} __attribute__ ((packed, aligned(4))); struct VFE_FRAME_SKIP_UpdateCmdType { - uint32_t yPattern:32; - uint32_t cbcrPattern:32; -} __attribute__((packed, aligned(4))); + uint32_t yPattern:32; + uint32_t cbcrPattern:32; +} __attribute__ ((packed, aligned(4))); struct vfe_frame_skip_cfg { /* Frame Drop Enc (output2) */ uint32_t output2YPeriod:5; - uint32_t /* reserved */ : 27; + uint32_t /* reserved */ : 27; uint32_t output2CbCrPeriod:5; - uint32_t /* reserved */ : 27; + uint32_t /* reserved */ : 27; uint32_t output2YPattern:32; uint32_t output2CbCrPattern:32; /* Frame Drop View (output1) */ uint32_t output1YPeriod:5; - uint32_t /* reserved */ : 27; + uint32_t /* reserved */ : 27; uint32_t output1CbCrPeriod:5; - uint32_t /* reserved */ : 27; + uint32_t /* reserved */ : 27; uint32_t output1YPattern:32; uint32_t output1CbCrPattern:32; -} __attribute__((packed, aligned(4))); +} __attribute__ ((packed, aligned(4))); struct vfe_main_scaler_cfg { /* Scaler Enable Config */ uint32_t hEnable:1; uint32_t vEnable:1; - uint32_t /* reserved */ : 30; + uint32_t /* reserved */ : 30; /* Scale H Image Size Config */ uint32_t inWidth:12; - uint32_t /* reserved */ : 4; + uint32_t /* reserved */ : 4; uint32_t outWidth:12; - uint32_t /* reserved */ : 4; + uint32_t /* reserved */ : 4; /* Scale H Phase Config */ uint32_t horizPhaseMult:18; - uint32_t /* reserved */ : 2; + uint32_t /* reserved */ : 2; uint32_t horizInterResolution:2; - uint32_t /* reserved */ : 10; + uint32_t /* reserved */ : 10; /* Scale H Stripe Config */ uint32_t horizMNInit:12; - uint32_t /* reserved */ : 4; + uint32_t /* reserved */ : 4; uint32_t horizPhaseInit:15; - uint32_t /* reserved */ : 1; + uint32_t /* reserved */ : 1; /* Scale V Image Size Config */ uint32_t inHeight:12; - uint32_t /* reserved */ : 4; + uint32_t /* reserved */ : 4; uint32_t outHeight:12; - uint32_t /* reserved */ : 4; + uint32_t /* reserved */ : 4; /* Scale V Phase Config */ uint32_t vertPhaseMult:18; - uint32_t /* reserved */ : 2; + uint32_t /* reserved */ : 2; uint32_t vertInterResolution:2; - uint32_t /* reserved */ : 10; + uint32_t /* reserved */ : 10; /* Scale V Stripe Config */ uint32_t vertMNInit:12; - uint32_t /* reserved */ : 4; + uint32_t /* reserved */ : 4; uint32_t vertPhaseInit:15; - uint32_t /* reserved */ : 1; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 1; +} __attribute__ ((packed, aligned(4))); struct vfe_scaler2_cfg { /* Scaler Enable Config */ - uint32_t hEnable:1; - uint32_t vEnable:1; - uint32_t /* reserved */ : 30; + uint32_t hEnable:1; + uint32_t vEnable:1; + uint32_t /* reserved */ : 30; /* Scaler H Image Size Config */ - uint32_t inWidth:12; - uint32_t /* reserved */ : 4; - uint32_t outWidth:12; - uint32_t /* reserved */ : 4; + uint32_t inWidth:12; + uint32_t /* reserved */ : 4; + uint32_t outWidth:12; + uint32_t /* reserved */ : 4; /* Scaler H Phase Config */ - uint32_t horizPhaseMult:18; - uint32_t /* reserved */ : 2; - uint32_t horizInterResolution:2; - uint32_t /* reserved */ : 10; + uint32_t horizPhaseMult:18; + uint32_t /* reserved */ : 2; + uint32_t horizInterResolution:2; + uint32_t /* reserved */ : 10; /* Scaler V Image Size Config */ - uint32_t inHeight:12; - uint32_t /* reserved */ : 4; - uint32_t outHeight:12; - uint32_t /* reserved */ : 4; + uint32_t inHeight:12; + uint32_t /* reserved */ : 4; + uint32_t outHeight:12; + uint32_t /* reserved */ : 4; /* Scaler V Phase Config */ - uint32_t vertPhaseMult:18; - uint32_t /* reserved */ : 2; - uint32_t vertInterResolution:2; - uint32_t /* reserved */ : 10; -} __attribute__((packed, aligned(4))); + uint32_t vertPhaseMult:18; + uint32_t /* reserved */ : 2; + uint32_t vertInterResolution:2; + uint32_t /* reserved */ : 10; +} __attribute__ ((packed, aligned(4))); struct vfe_rolloff_cfg { /* Rolloff 0 Config */ - uint32_t gridWidth:9; - uint32_t gridHeight:9; - uint32_t yDelta:9; - uint32_t /* reserved */ : 5; - /* Rolloff 1 Config*/ - uint32_t gridX:4; - uint32_t gridY:4; - uint32_t pixelX:9; - uint32_t /* reserved */ : 3; - uint32_t pixelY:9; - uint32_t /* reserved */ : 3; + uint32_t gridWidth:9; + uint32_t gridHeight:9; + uint32_t yDelta:9; + uint32_t /* reserved */ : 5; + /* Rolloff 1 Config */ + uint32_t gridX:4; + uint32_t gridY:4; + uint32_t pixelX:9; + uint32_t /* reserved */ : 3; + uint32_t pixelY:9; + uint32_t /* reserved */ : 3; /* Rolloff 2 Config */ - uint32_t yDeltaAccum:12; - uint32_t /* reserved */ : 20; -} __attribute__((packed, aligned(4))); + uint32_t yDeltaAccum:12; + uint32_t /* reserved */ : 20; +} __attribute__ ((packed, aligned(4))); struct vfe_asf_update { /* ASF Config Command */ uint32_t smoothEnable:1; uint32_t sharpMode:2; - uint32_t /* reserved */ : 1; + uint32_t /* reserved */ : 1; uint32_t smoothCoeff1:4; uint32_t smoothCoeff0:8; uint32_t pipeFlushCount:12; uint32_t pipeFlushOvd:1; uint32_t flushHaltOvd:1; uint32_t cropEnable:1; - uint32_t /* reserved */ : 1; + uint32_t /* reserved */ : 1; /* Sharpening Config 0 */ uint32_t sharpThresholdE1:7; - uint32_t /* reserved */ : 1; + uint32_t /* reserved */ : 1; uint32_t sharpDegreeK1:5; - uint32_t /* reserved */ : 3; + uint32_t /* reserved */ : 3; uint32_t sharpDegreeK2:5; - uint32_t /* reserved */ : 3; + uint32_t /* reserved */ : 3; uint32_t normalizeFactor:7; - uint32_t /* reserved */ : 1; + uint32_t /* reserved */ : 1; /* Sharpening Config 1 */ uint32_t sharpThresholdE2:8; uint32_t sharpThresholdE3:8; @@ -892,407 +913,406 @@ struct vfe_asf_update { uint32_t F1Coeff2:6; uint32_t F1Coeff3:6; uint32_t F1Coeff4:6; - uint32_t /* reserved */ : 2; + uint32_t /* reserved */ : 2; /* Sharpening Coefficients 1 */ uint32_t F1Coeff5:6; uint32_t F1Coeff6:6; uint32_t F1Coeff7:6; uint32_t F1Coeff8:7; - uint32_t /* reserved */ : 7; + uint32_t /* reserved */ : 7; /* Sharpening Coefficients 2 */ uint32_t F2Coeff0:6; uint32_t F2Coeff1:6; uint32_t F2Coeff2:6; uint32_t F2Coeff3:6; uint32_t F2Coeff4:6; - uint32_t /* reserved */ : 2; + uint32_t /* reserved */ : 2; /* Sharpening Coefficients 3 */ uint32_t F2Coeff5:6; uint32_t F2Coeff6:6; uint32_t F2Coeff7:6; uint32_t F2Coeff8:7; - uint32_t /* reserved */ : 7; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 7; +} __attribute__ ((packed, aligned(4))); struct vfe_asfcrop_cfg { /* ASF Crop Width Config */ uint32_t lastPixel:12; - uint32_t /* reserved */ : 4; + uint32_t /* reserved */ : 4; uint32_t firstPixel:12; - uint32_t /* reserved */ : 4; + uint32_t /* reserved */ : 4; /* ASP Crop Height Config */ uint32_t lastLine:12; - uint32_t /* reserved */ : 4; + uint32_t /* reserved */ : 4; uint32_t firstLine:12; - uint32_t /* reserved */ : 4; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 4; +} __attribute__ ((packed, aligned(4))); struct vfe_chroma_suppress_cfg { /* Chroma Suppress 0 Config */ uint32_t m1:8; uint32_t m3:8; uint32_t n1:3; - uint32_t /* reserved */ : 1; + uint32_t /* reserved */ : 1; uint32_t n3:3; - uint32_t /* reserved */ : 9; + uint32_t /* reserved */ : 9; /* Chroma Suppress 1 Config */ uint32_t mm1:8; uint32_t nn1:3; - uint32_t /* reserved */ : 21; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 21; +} __attribute__ ((packed, aligned(4))); struct vfe_chromasubsample_cfg { /* Chroma Subsample Selection */ - uint32_t hCositedPhase:1; - uint32_t vCositedPhase:1; - uint32_t hCosited:1; - uint32_t vCosited:1; - uint32_t hsubSampleEnable:1; - uint32_t vsubSampleEnable:1; - uint32_t cropEnable:1; - uint32_t /* reserved */ : 25; - uint32_t cropWidthLastPixel:12; - uint32_t /* reserved */ : 4; - uint32_t cropWidthFirstPixel:12; - uint32_t /* reserved */ : 4; - uint32_t cropHeightLastLine:12; - uint32_t /* reserved */ : 4; - uint32_t cropHeightFirstLine:12; - uint32_t /* reserved */ : 4; -} __attribute__((packed, aligned(4))); + uint32_t hCositedPhase:1; + uint32_t vCositedPhase:1; + uint32_t hCosited:1; + uint32_t vCosited:1; + uint32_t hsubSampleEnable:1; + uint32_t vsubSampleEnable:1; + uint32_t cropEnable:1; + uint32_t /* reserved */ : 25; + uint32_t cropWidthLastPixel:12; + uint32_t /* reserved */ : 4; + uint32_t cropWidthFirstPixel:12; + uint32_t /* reserved */ : 4; + uint32_t cropHeightLastLine:12; + uint32_t /* reserved */ : 4; + uint32_t cropHeightFirstLine:12; + uint32_t /* reserved */ : 4; +} __attribute__ ((packed, aligned(4))); struct vfe_blacklevel_cfg { /* Black Even-Even Value Config */ - uint32_t evenEvenAdjustment:9; - uint32_t /* reserved */ : 23; + uint32_t evenEvenAdjustment:9; + uint32_t /* reserved */ : 23; /* Black Even-Odd Value Config */ - uint32_t evenOddAdjustment:9; - uint32_t /* reserved */ : 23; + uint32_t evenOddAdjustment:9; + uint32_t /* reserved */ : 23; /* Black Odd-Even Value Config */ - uint32_t oddEvenAdjustment:9; - uint32_t /* reserved */ : 23; + uint32_t oddEvenAdjustment:9; + uint32_t /* reserved */ : 23; /* Black Odd-Odd Value Config */ - uint32_t oddOddAdjustment:9; - uint32_t /* reserved */ : 23; -} __attribute__((packed, aligned(4))); + uint32_t oddOddAdjustment:9; + uint32_t /* reserved */ : 23; +} __attribute__ ((packed, aligned(4))); struct vfe_demux_cfg { /* Demux Gain 0 Config */ - uint32_t ch0EvenGain:10; - uint32_t /* reserved */ : 6; - uint32_t ch0OddGain:10; - uint32_t /* reserved */ : 6; + uint32_t ch0EvenGain:10; + uint32_t /* reserved */ : 6; + uint32_t ch0OddGain:10; + uint32_t /* reserved */ : 6; /* Demux Gain 1 Config */ - uint32_t ch1Gain:10; - uint32_t /* reserved */ : 6; - uint32_t ch2Gain:10; - uint32_t /* reserved */ : 6; -} __attribute__((packed, aligned(4))); + uint32_t ch1Gain:10; + uint32_t /* reserved */ : 6; + uint32_t ch2Gain:10; + uint32_t /* reserved */ : 6; +} __attribute__ ((packed, aligned(4))); struct vfe_bps_info { - uint32_t greenBadPixelCount:8; - uint32_t /* reserved */ : 8; - uint32_t RedBlueBadPixelCount:8; - uint32_t /* reserved */ : 8; -} __attribute__((packed, aligned(4))); + uint32_t greenBadPixelCount:8; + uint32_t /* reserved */ : 8; + uint32_t RedBlueBadPixelCount:8; + uint32_t /* reserved */ : 8; +} __attribute__ ((packed, aligned(4))); struct vfe_demosaic_cfg { /* Demosaic Config */ uint32_t abfEnable:1; uint32_t badPixelCorrEnable:1; uint32_t forceAbfOn:1; - uint32_t /* reserved */ : 1; + uint32_t /* reserved */ : 1; uint32_t abfShift:4; uint32_t fminThreshold:7; - uint32_t /* reserved */ : 1; + uint32_t /* reserved */ : 1; uint32_t fmaxThreshold:7; - uint32_t /* reserved */ : 5; + uint32_t /* reserved */ : 5; uint32_t slopeShift:3; - uint32_t /* reserved */ : 1; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 1; +} __attribute__ ((packed, aligned(4))); struct vfe_demosaic_bpc_cfg { /* Demosaic BPC Config 0 */ uint32_t blueDiffThreshold:12; uint32_t redDiffThreshold:12; - uint32_t /* reserved */ : 8; + uint32_t /* reserved */ : 8; /* Demosaic BPC Config 1 */ uint32_t greenDiffThreshold:12; - uint32_t /* reserved */ : 20; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 20; +} __attribute__ ((packed, aligned(4))); struct vfe_demosaic_abf_cfg { /* Demosaic ABF Config 0 */ uint32_t lpThreshold:10; - uint32_t /* reserved */ : 22; + uint32_t /* reserved */ : 22; /* Demosaic ABF Config 1 */ uint32_t ratio:4; uint32_t minValue:10; - uint32_t /* reserved */ : 2; + uint32_t /* reserved */ : 2; uint32_t maxValue:10; - uint32_t /* reserved */ : 6; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 6; +} __attribute__ ((packed, aligned(4))); struct vfe_color_correction_cfg { /* Color Corr. Coefficient 0 Config */ - uint32_t c0:12; - uint32_t /* reserved */ : 20; + uint32_t c0:12; + uint32_t /* reserved */ : 20; /* Color Corr. Coefficient 1 Config */ - uint32_t c1:12; - uint32_t /* reserved */ : 20; + uint32_t c1:12; + uint32_t /* reserved */ : 20; /* Color Corr. Coefficient 2 Config */ - uint32_t c2:12; - uint32_t /* reserved */ : 20; + uint32_t c2:12; + uint32_t /* reserved */ : 20; /* Color Corr. Coefficient 3 Config */ - uint32_t c3:12; - uint32_t /* reserved */ : 20; + uint32_t c3:12; + uint32_t /* reserved */ : 20; /* Color Corr. Coefficient 4 Config */ - uint32_t c4:12; - uint32_t /* reserved */ : 20; + uint32_t c4:12; + uint32_t /* reserved */ : 20; /* Color Corr. Coefficient 5 Config */ - uint32_t c5:12; - uint32_t /* reserved */ : 20; + uint32_t c5:12; + uint32_t /* reserved */ : 20; /* Color Corr. Coefficient 6 Config */ - uint32_t c6:12; - uint32_t /* reserved */ : 20; + uint32_t c6:12; + uint32_t /* reserved */ : 20; /* Color Corr. Coefficient 7 Config */ - uint32_t c7:12; - uint32_t /* reserved */ : 20; + uint32_t c7:12; + uint32_t /* reserved */ : 20; /* Color Corr. Coefficient 8 Config */ - uint32_t c8:12; - uint32_t /* reserved */ : 20; + uint32_t c8:12; + uint32_t /* reserved */ : 20; /* Color Corr. Offset 0 Config */ - uint32_t k0:11; - uint32_t /* reserved */ : 21; + uint32_t k0:11; + uint32_t /* reserved */ : 21; /* Color Corr. Offset 1 Config */ - uint32_t k1:11; - uint32_t /* reserved */ : 21; + uint32_t k1:11; + uint32_t /* reserved */ : 21; /* Color Corr. Offset 2 Config */ - uint32_t k2:11; - uint32_t /* reserved */ : 21; + uint32_t k2:11; + uint32_t /* reserved */ : 21; /* Color Corr. Coefficient Q Config */ - uint32_t coefQFactor:2; - uint32_t /* reserved */ : 30; -} __attribute__((packed, aligned(4))); + uint32_t coefQFactor:2; + uint32_t /* reserved */ : 30; +} __attribute__ ((packed, aligned(4))); struct VFE_LumaAdaptation_ConfigCmdType { /* LA Config */ - uint32_t lutBankSelect:1; - uint32_t /* reserved */ : 31; -} __attribute__((packed, aligned(4))); + uint32_t lutBankSelect:1; + uint32_t /* reserved */ : 31; +} __attribute__ ((packed, aligned(4))); struct vfe_wb_cfg { /* WB Config */ uint32_t ch0Gain:9; uint32_t ch1Gain:9; uint32_t ch2Gain:9; - uint32_t /* reserved */ : 5; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 5; +} __attribute__ ((packed, aligned(4))); struct VFE_GammaLutSelect_ConfigCmdType { /* LUT Bank Select Config */ - uint32_t ch0BankSelect:1; - uint32_t ch1BankSelect:1; - uint32_t ch2BankSelect:1; - uint32_t /* reserved */ : 29; -} __attribute__((packed, aligned(4))); + uint32_t ch0BankSelect:1; + uint32_t ch1BankSelect:1; + uint32_t ch2BankSelect:1; + uint32_t /* reserved */ : 29; +} __attribute__ ((packed, aligned(4))); struct vfe_chroma_enhance_cfg { /* Chroma Enhance A Config */ uint32_t ap:11; - uint32_t /* reserved */ : 5; + uint32_t /* reserved */ : 5; uint32_t am:11; - uint32_t /* reserved */ : 5; + uint32_t /* reserved */ : 5; /* Chroma Enhance B Config */ uint32_t bp:11; - uint32_t /* reserved */ : 5; + uint32_t /* reserved */ : 5; uint32_t bm:11; - uint32_t /* reserved */ : 5; + uint32_t /* reserved */ : 5; /* Chroma Enhance C Config */ uint32_t cp:11; - uint32_t /* reserved */ : 5; + uint32_t /* reserved */ : 5; uint32_t cm:11; - uint32_t /* reserved */ : 5; + uint32_t /* reserved */ : 5; /* Chroma Enhance D Config */ uint32_t dp:11; - uint32_t /* reserved */ : 5; + uint32_t /* reserved */ : 5; uint32_t dm:11; - uint32_t /* reserved */ : 5; + uint32_t /* reserved */ : 5; /* Chroma Enhance K Config */ uint32_t kcb:11; - uint32_t /* reserved */ : 5; + uint32_t /* reserved */ : 5; uint32_t kcr:11; - uint32_t /* reserved */ : 5; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 5; +} __attribute__ ((packed, aligned(4))); struct vfe_color_convert_cfg { /* Conversion Coefficient 0 */ uint32_t v0:12; - uint32_t /* reserved */ : 20; + uint32_t /* reserved */ : 20; /* Conversion Coefficient 1 */ uint32_t v1:12; - uint32_t /* reserved */ : 20; + uint32_t /* reserved */ : 20; /* Conversion Coefficient 2 */ uint32_t v2:12; - uint32_t /* reserved */ : 20; + uint32_t /* reserved */ : 20; /* Conversion Offset */ uint32_t ConvertOffset:8; - uint32_t /* reserved */ : 24; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 24; +} __attribute__ ((packed, aligned(4))); struct VFE_SyncTimer_ConfigCmdType { /* Timer Line Start Config */ - uint32_t timerLineStart:12; - uint32_t /* reserved */ : 20; + uint32_t timerLineStart:12; + uint32_t /* reserved */ : 20; /* Timer Pixel Start Config */ - uint32_t timerPixelStart:18; - uint32_t /* reserved */ : 14; + uint32_t timerPixelStart:18; + uint32_t /* reserved */ : 14; /* Timer Pixel Duration Config */ - uint32_t timerPixelDuration:28; - uint32_t /* reserved */ : 4; + uint32_t timerPixelDuration:28; + uint32_t /* reserved */ : 4; /* Sync Timer Polarity Config */ - uint32_t timer0Polarity:1; - uint32_t timer1Polarity:1; - uint32_t timer2Polarity:1; - uint32_t /* reserved */ : 29; -} __attribute__((packed, aligned(4))); + uint32_t timer0Polarity:1; + uint32_t timer1Polarity:1; + uint32_t timer2Polarity:1; + uint32_t /* reserved */ : 29; +} __attribute__ ((packed, aligned(4))); struct VFE_AsyncTimer_ConfigCmdType { /* Async Timer Config 0 */ - uint32_t inactiveLength:20; - uint32_t numRepetition:10; - uint32_t /* reserved */ : 1; - uint32_t polarity:1; + uint32_t inactiveLength:20; + uint32_t numRepetition:10; + uint32_t /* reserved */ : 1; + uint32_t polarity:1; /* Async Timer Config 1 */ - uint32_t activeLength:20; - uint32_t /* reserved */ : 12; -} __attribute__((packed, aligned(4))); + uint32_t activeLength:20; + uint32_t /* reserved */ : 12; +} __attribute__ ((packed, aligned(4))); struct VFE_AWBAEStatistics_ConfigCmdType { /* AWB autoexposure Config */ - uint32_t aeRegionConfig:1; - uint32_t aeSubregionConfig:1; - uint32_t /* reserved */ : 14; - uint32_t awbYMin:8; - uint32_t awbYMax:8; + uint32_t aeRegionConfig:1; + uint32_t aeSubregionConfig:1; + uint32_t /* reserved */ : 14; + uint32_t awbYMin:8; + uint32_t awbYMax:8; /* AXW Header */ - uint32_t axwHeader:8; - uint32_t /* reserved */ : 24; + uint32_t axwHeader:8; + uint32_t /* reserved */ : 24; /* AWB Mconfig */ - uint32_t m4:8; - uint32_t m3:8; - uint32_t m2:8; - uint32_t m1:8; + uint32_t m4:8; + uint32_t m3:8; + uint32_t m2:8; + uint32_t m1:8; /* AWB Cconfig */ - uint32_t c2:12; - uint32_t /* reserved */ : 4; - uint32_t c1:12; - uint32_t /* reserved */ : 4; + uint32_t c2:12; + uint32_t /* reserved */ : 4; + uint32_t c1:12; + uint32_t /* reserved */ : 4; /* AWB Cconfig 2 */ - uint32_t c4:12; - uint32_t /* reserved */ : 4; - uint32_t c3:12; - uint32_t /* reserved */ : 4; -} __attribute__((packed, aligned(4))); + uint32_t c4:12; + uint32_t /* reserved */ : 4; + uint32_t c3:12; + uint32_t /* reserved */ : 4; +} __attribute__ ((packed, aligned(4))); struct VFE_TestGen_ConfigCmdType { /* HW Test Gen Config */ - uint32_t numFrame:10; - uint32_t /* reserved */ : 2; - uint32_t pixelDataSelect:1; - uint32_t systematicDataSelect:1; - uint32_t /* reserved */ : 2; - uint32_t pixelDataSize:2; - uint32_t hsyncEdge:1; - uint32_t vsyncEdge:1; - uint32_t /* reserved */ : 12; + uint32_t numFrame:10; + uint32_t /* reserved */ : 2; + uint32_t pixelDataSelect:1; + uint32_t systematicDataSelect:1; + uint32_t /* reserved */ : 2; + uint32_t pixelDataSize:2; + uint32_t hsyncEdge:1; + uint32_t vsyncEdge:1; + uint32_t /* reserved */ : 12; /* HW Test Gen Image Config */ - uint32_t imageWidth:14; - uint32_t /* reserved */ : 2; - uint32_t imageHeight:14; - uint32_t /* reserved */ : 2; + uint32_t imageWidth:14; + uint32_t /* reserved */ : 2; + uint32_t imageHeight:14; + uint32_t /* reserved */ : 2; /* SOF Offset Config */ - uint32_t sofOffset:24; - uint32_t /* reserved */ : 8; + uint32_t sofOffset:24; + uint32_t /* reserved */ : 8; /* EOF NOffset Config */ - uint32_t eofNOffset:24; - uint32_t /* reserved */ : 8; + uint32_t eofNOffset:24; + uint32_t /* reserved */ : 8; /* SOL Offset Config */ - uint32_t solOffset:9; - uint32_t /* reserved */ : 23; + uint32_t solOffset:9; + uint32_t /* reserved */ : 23; /* EOL NOffset Config */ - uint32_t eolNOffset:9; - uint32_t /* reserved */ : 23; + uint32_t eolNOffset:9; + uint32_t /* reserved */ : 23; /* HBI Config */ - uint32_t hBlankInterval:14; - uint32_t /* reserved */ : 18; + uint32_t hBlankInterval:14; + uint32_t /* reserved */ : 18; /* VBL Config */ - uint32_t vBlankInterval:14; - uint32_t /* reserved */ : 2; - uint32_t vBlankIntervalEnable:1; - uint32_t /* reserved */ : 15; + uint32_t vBlankInterval:14; + uint32_t /* reserved */ : 2; + uint32_t vBlankIntervalEnable:1; + uint32_t /* reserved */ : 15; /* SOF Dummy Line Config */ - uint32_t sofDummy:8; - uint32_t /* reserved */ : 24; + uint32_t sofDummy:8; + uint32_t /* reserved */ : 24; /* EOF Dummy Line Config */ - uint32_t eofDummy:8; - uint32_t /* reserved */ : 24; + uint32_t eofDummy:8; + uint32_t /* reserved */ : 24; /* Color Bars Config */ - uint32_t unicolorBarSelect:3; - uint32_t /* reserved */ : 1; - uint32_t unicolorBarEnable:1; - uint32_t splitEnable:1; - uint32_t pixelPattern:2; - uint32_t rotatePeriod:6; - uint32_t /* reserved */ : 18; + uint32_t unicolorBarSelect:3; + uint32_t /* reserved */ : 1; + uint32_t unicolorBarEnable:1; + uint32_t splitEnable:1; + uint32_t pixelPattern:2; + uint32_t rotatePeriod:6; + uint32_t /* reserved */ : 18; /* Random Config */ - uint32_t randomSeed:16; - uint32_t /* reserved */ : 16; -} __attribute__((packed, aligned(4))); + uint32_t randomSeed:16; + uint32_t /* reserved */ : 16; +} __attribute__ ((packed, aligned(4))); struct VFE_Bus_Pm_ConfigCmdType { /* VFE Bus Performance Monitor Config */ - uint32_t output2YWrPmEnable:1; - uint32_t output2CbcrWrPmEnable:1; - uint32_t output1YWrPmEnable:1; - uint32_t output1CbcrWrPmEnable:1; - uint32_t /* reserved */ : 28; -} __attribute__((packed, aligned(4))); + uint32_t output2YWrPmEnable:1; + uint32_t output2CbcrWrPmEnable:1; + uint32_t output1YWrPmEnable:1; + uint32_t output1CbcrWrPmEnable:1; + uint32_t /* reserved */ : 28; +} __attribute__ ((packed, aligned(4))); struct vfe_asf_info { /* asf max edge */ uint32_t maxEdge:13; - uint32_t /* reserved */ : 3; + uint32_t /* reserved */ : 3; /* HBi count */ uint32_t HBICount:12; - uint32_t /* reserved */ : 4; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 4; +} __attribute__ ((packed, aligned(4))); struct vfe_camif_stats { - uint32_t pixelCount:14; - uint32_t /* reserved */ : 2; - uint32_t lineCount:14; - uint32_t /* reserved */ : 1; - uint32_t camifHalt:1; -} __attribute__((packed, aligned(4))); + uint32_t pixelCount:14; + uint32_t /* reserved */ : 2; + uint32_t lineCount:14; + uint32_t /* reserved */ : 1; + uint32_t camifHalt:1; +} __attribute__ ((packed, aligned(4))); struct VFE_StatsCmdType { - uint32_t autoFocusEnable:1; - uint32_t axwEnable:1; - uint32_t histEnable:1; - uint32_t clearHistEnable:1; - uint32_t histAutoClearEnable:1; - uint32_t colorConversionEnable:1; - uint32_t /* reserved */ : 26; -} __attribute__((packed, aligned(4))); - + uint32_t autoFocusEnable:1; + uint32_t axwEnable:1; + uint32_t histEnable:1; + uint32_t clearHistEnable:1; + uint32_t histAutoClearEnable:1; + uint32_t colorConversionEnable:1; + uint32_t /* reserved */ : 26; +} __attribute__ ((packed, aligned(4))); struct vfe_statsframe { uint32_t lastPixel:12; - uint32_t /* reserved */ : 4; + uint32_t /* reserved */ : 4; uint32_t lastLine:12; - uint32_t /* reserved */ : 4; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 4; +} __attribute__ ((packed, aligned(4))); struct vfe_busstats_wrprio { uint32_t afBusPriority:4; @@ -1301,200 +1321,200 @@ struct vfe_busstats_wrprio { uint32_t afBusPriorityEn:1; uint32_t awbBusPriorityEn:1; uint32_t histBusPriorityEn:1; - uint32_t /* reserved */ : 17; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 17; +} __attribute__ ((packed, aligned(4))); struct vfe_statsaf_update { /* VFE_STATS_AF_CFG */ uint32_t windowVOffset:12; - uint32_t /* reserved */ : 4; + uint32_t /* reserved */ : 4; uint32_t windowHOffset:12; - uint32_t /* reserved */ : 3; + uint32_t /* reserved */ : 3; uint32_t windowMode:1; /* VFE_STATS_AF_DIM */ uint32_t windowHeight:12; - uint32_t /* reserved */ : 4; + uint32_t /* reserved */ : 4; uint32_t windowWidth:12; - uint32_t /* reserved */ : 4; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 4; +} __attribute__ ((packed, aligned(4))); struct vfe_statsaf_cfg { /* VFE_STATS_AF_GRID_0 */ - uint32_t entry00:8; - uint32_t entry01:8; - uint32_t entry02:8; - uint32_t entry03:8; + uint32_t entry00:8; + uint32_t entry01:8; + uint32_t entry02:8; + uint32_t entry03:8; /* VFE_STATS_AF_GRID_1 */ - uint32_t entry10:8; - uint32_t entry11:8; - uint32_t entry12:8; - uint32_t entry13:8; + uint32_t entry10:8; + uint32_t entry11:8; + uint32_t entry12:8; + uint32_t entry13:8; /* VFE_STATS_AF_GRID_2 */ - uint32_t entry20:8; - uint32_t entry21:8; - uint32_t entry22:8; - uint32_t entry23:8; + uint32_t entry20:8; + uint32_t entry21:8; + uint32_t entry22:8; + uint32_t entry23:8; /* VFE_STATS_AF_GRID_3 */ - uint32_t entry30:8; - uint32_t entry31:8; - uint32_t entry32:8; - uint32_t entry33:8; + uint32_t entry30:8; + uint32_t entry31:8; + uint32_t entry32:8; + uint32_t entry33:8; /* VFE_STATS_AF_HEADER */ - uint32_t afHeader:8; - uint32_t /* reserved */ : 24; + uint32_t afHeader:8; + uint32_t /* reserved */ : 24; /* VFE_STATS_AF_COEF0 */ - uint32_t a00:5; - uint32_t a04:5; - uint32_t fvMax:11; - uint32_t fvMetric:1; - uint32_t /* reserved */ : 10; + uint32_t a00:5; + uint32_t a04:5; + uint32_t fvMax:11; + uint32_t fvMetric:1; + uint32_t /* reserved */ : 10; /* VFE_STATS_AF_COEF1 */ - uint32_t a20:5; - uint32_t a21:5; - uint32_t a22:5; - uint32_t a23:5; - uint32_t a24:5; - uint32_t /* reserved */ : 7; -} __attribute__((packed, aligned(4))); + uint32_t a20:5; + uint32_t a21:5; + uint32_t a22:5; + uint32_t a23:5; + uint32_t a24:5; + uint32_t /* reserved */ : 7; +} __attribute__ ((packed, aligned(4))); struct vfe_statsawbae_update { - uint32_t aeRegionCfg:1; - uint32_t aeSubregionCfg:1; - uint32_t /* reserved */ : 14; - uint32_t awbYMin:8; - uint32_t awbYMax:8; -} __attribute__((packed, aligned(4))); + uint32_t aeRegionCfg:1; + uint32_t aeSubregionCfg:1; + uint32_t /* reserved */ : 14; + uint32_t awbYMin:8; + uint32_t awbYMax:8; +} __attribute__ ((packed, aligned(4))); struct vfe_statsaxw_hdr_cfg { /* Stats AXW Header Config */ uint32_t axwHeader:8; - uint32_t /* reserved */ : 24; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 24; +} __attribute__ ((packed, aligned(4))); struct vfe_statsawb_update { /* AWB MConfig */ - uint32_t m4:8; - uint32_t m3:8; - uint32_t m2:8; - uint32_t m1:8; + uint32_t m4:8; + uint32_t m3:8; + uint32_t m2:8; + uint32_t m1:8; /* AWB CConfig1 */ - uint32_t c2:12; - uint32_t /* reserved */ : 4; - uint32_t c1:12; - uint32_t /* reserved */ : 4; + uint32_t c2:12; + uint32_t /* reserved */ : 4; + uint32_t c1:12; + uint32_t /* reserved */ : 4; /* AWB CConfig2 */ - uint32_t c4:12; - uint32_t /* reserved */ : 4; - uint32_t c3:12; - uint32_t /* reserved */ : 4; -} __attribute__((packed, aligned(4))); + uint32_t c4:12; + uint32_t /* reserved */ : 4; + uint32_t c3:12; + uint32_t /* reserved */ : 4; +} __attribute__ ((packed, aligned(4))); struct VFE_SyncTimerCmdType { - uint32_t hsyncCount:12; - uint32_t /* reserved */ : 20; - uint32_t pclkCount:18; - uint32_t /* reserved */ : 14; - uint32_t outputDuration:28; - uint32_t /* reserved */ : 4; -} __attribute__((packed, aligned(4))); + uint32_t hsyncCount:12; + uint32_t /* reserved */ : 20; + uint32_t pclkCount:18; + uint32_t /* reserved */ : 14; + uint32_t outputDuration:28; + uint32_t /* reserved */ : 4; +} __attribute__ ((packed, aligned(4))); struct VFE_AsyncTimerCmdType { /* config 0 */ - uint32_t inactiveCount:20; - uint32_t repeatCount:10; - uint32_t /* reserved */ : 1; - uint32_t polarity:1; + uint32_t inactiveCount:20; + uint32_t repeatCount:10; + uint32_t /* reserved */ : 1; + uint32_t polarity:1; /* config 1 */ - uint32_t activeCount:20; - uint32_t /* reserved */ : 12; -} __attribute__((packed, aligned(4))); + uint32_t activeCount:20; + uint32_t /* reserved */ : 12; +} __attribute__ ((packed, aligned(4))); struct VFE_AxiInputCmdType { - uint32_t stripeStartAddr0:32; - uint32_t stripeStartAddr1:32; - uint32_t stripeStartAddr2:32; - uint32_t stripeStartAddr3:32; + uint32_t stripeStartAddr0:32; + uint32_t stripeStartAddr1:32; + uint32_t stripeStartAddr2:32; + uint32_t stripeStartAddr3:32; - uint32_t ySize:12; - uint32_t yOffsetDelta:12; - uint32_t /* reserved */ : 8; + uint32_t ySize:12; + uint32_t yOffsetDelta:12; + uint32_t /* reserved */ : 8; /* bus_stripe_rd_hSize */ - uint32_t /* reserved */ : 16; - uint32_t xSizeWord:10; - uint32_t /* reserved */ : 6; + uint32_t /* reserved */ : 16; + uint32_t xSizeWord:10; + uint32_t /* reserved */ : 6; /* bus_stripe_rd_buffer_cfg */ - uint32_t burstLength:2; - uint32_t /* reserved */ : 2; - uint32_t NumOfRows:12; - uint32_t RowIncrement:12; - uint32_t /* reserved */ : 4; + uint32_t burstLength:2; + uint32_t /* reserved */ : 2; + uint32_t NumOfRows:12; + uint32_t RowIncrement:12; + uint32_t /* reserved */ : 4; /* bus_stripe_rd_unpack_cfg */ - uint32_t mainUnpackHeight:12; - uint32_t mainUnpackWidth:13; - uint32_t mainUnpackHbiSel:3; - uint32_t mainUnpackPhase:3; - uint32_t /* reserved */ : 1; + uint32_t mainUnpackHeight:12; + uint32_t mainUnpackWidth:13; + uint32_t mainUnpackHbiSel:3; + uint32_t mainUnpackPhase:3; + uint32_t /* reserved */ : 1; /* bus_stripe_rd_unpack */ - uint32_t unpackPattern:32; + uint32_t unpackPattern:32; /* bus_stripe_rd_pad_size */ - uint32_t padLeft:7; - uint32_t /* reserved */ : 1; - uint32_t padRight:7; - uint32_t /* reserved */ : 1; - uint32_t padTop:7; - uint32_t /* reserved */ : 1; - uint32_t padBottom:7; - uint32_t /* reserved */ : 1; + uint32_t padLeft:7; + uint32_t /* reserved */ : 1; + uint32_t padRight:7; + uint32_t /* reserved */ : 1; + uint32_t padTop:7; + uint32_t /* reserved */ : 1; + uint32_t padBottom:7; + uint32_t /* reserved */ : 1; /* bus_stripe_rd_pad_L_unpack */ - uint32_t leftUnpackPattern0:4; - uint32_t leftUnpackPattern1:4; - uint32_t leftUnpackPattern2:4; - uint32_t leftUnpackPattern3:4; - uint32_t leftUnpackStop0:1; - uint32_t leftUnpackStop1:1; - uint32_t leftUnpackStop2:1; - uint32_t leftUnpackStop3:1; - uint32_t /* reserved */ : 12; + uint32_t leftUnpackPattern0:4; + uint32_t leftUnpackPattern1:4; + uint32_t leftUnpackPattern2:4; + uint32_t leftUnpackPattern3:4; + uint32_t leftUnpackStop0:1; + uint32_t leftUnpackStop1:1; + uint32_t leftUnpackStop2:1; + uint32_t leftUnpackStop3:1; + uint32_t /* reserved */ : 12; /* bus_stripe_rd_pad_R_unpack */ - uint32_t rightUnpackPattern0:4; - uint32_t rightUnpackPattern1:4; - uint32_t rightUnpackPattern2:4; - uint32_t rightUnpackPattern3:4; - uint32_t rightUnpackStop0:1; - uint32_t rightUnpackStop1:1; - uint32_t rightUnpackStop2:1; - uint32_t rightUnpackStop3:1; - uint32_t /* reserved */ : 12; + uint32_t rightUnpackPattern0:4; + uint32_t rightUnpackPattern1:4; + uint32_t rightUnpackPattern2:4; + uint32_t rightUnpackPattern3:4; + uint32_t rightUnpackStop0:1; + uint32_t rightUnpackStop1:1; + uint32_t rightUnpackStop2:1; + uint32_t rightUnpackStop3:1; + uint32_t /* reserved */ : 12; /* bus_stripe_rd_pad_tb_unpack */ - uint32_t topUnapckPattern:4; - uint32_t /* reserved */ : 12; - uint32_t bottomUnapckPattern:4; - uint32_t /* reserved */ : 12; -} __attribute__((packed, aligned(4))); + uint32_t topUnapckPattern:4; + uint32_t /* reserved */ : 12; + uint32_t bottomUnapckPattern:4; + uint32_t /* reserved */ : 12; +} __attribute__ ((packed, aligned(4))); struct VFE_AxiRdFragIrqEnable { uint32_t stripeRdFragirq0Enable:1; uint32_t stripeRdFragirq1Enable:1; uint32_t stripeRdFragirq2Enable:1; uint32_t stripeRdFragirq3Enable:1; - uint32_t /* reserved */ : 28; -} __attribute__((packed, aligned(4))); + uint32_t /* reserved */ : 28; +} __attribute__ ((packed, aligned(4))); int vfe_cmd_init(struct msm_vfe_callback *, struct platform_device *, void *); void vfe_stats_af_stop(void); @@ -1546,4 +1566,5 @@ void vfe_reset(void); void vfe_cmd_release(struct platform_device *); void vfe_output1_ack(struct vfe_cmd_output_ack *); void vfe_output2_ack(struct vfe_cmd_output_ack *); +void vfe_epoch1_config(struct vfe_cmds_camif_epoch *); #endif /* __MSM_VFE8X_REG_H__ */ diff --git a/drivers/media/video/msm/mt9d112.c b/drivers/media/video/msm/mt9d112.c index 4f938f9dfc470..5a6a8dbdd96c7 100644 --- a/drivers/media/video/msm/mt9d112.c +++ b/drivers/media/video/msm/mt9d112.c @@ -1,5 +1,19 @@ -/* - * Copyright (C) 2008-2009 QUALCOMM Incorporated. +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ #include @@ -25,27 +39,16 @@ struct mt9d112_work { struct work_struct work; }; -static struct mt9d112_work *mt9d112_sensorw; -static struct i2c_client *mt9d112_client; +static struct mt9d112_work *mt9d112_sensorw; +static struct i2c_client *mt9d112_client; struct mt9d112_ctrl { const struct msm_camera_sensor_info *sensordata; }; - static struct mt9d112_ctrl *mt9d112_ctrl; static DECLARE_WAIT_QUEUE_HEAD(mt9d112_wait_queue); -DECLARE_MUTEX(mt9d112_sem); - - -/*============================================================= - EXTERNAL DECLARATIONS -==============================================================*/ -extern struct mt9d112_reg mt9d112_regs; - - -/*=============================================================*/ static int mt9d112_reset(const struct msm_camera_sensor_info *dev) { @@ -63,16 +66,16 @@ static int mt9d112_reset(const struct msm_camera_sensor_info *dev) return rc; } -static int32_t mt9d112_i2c_txdata(unsigned short saddr, - unsigned char *txdata, int length) +static int mt9d112_i2c_txdata(unsigned short saddr, + unsigned char *txdata, int length) { struct i2c_msg msg[] = { { - .addr = saddr, - .flags = 0, - .len = length, - .buf = txdata, - }, + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, + }, }; if (i2c_transfer(mt9d112_client->adapter, msg, 1) < 0) { @@ -83,29 +86,30 @@ static int32_t mt9d112_i2c_txdata(unsigned short saddr, return 0; } -static int32_t mt9d112_i2c_write(unsigned short saddr, - unsigned short waddr, unsigned short wdata, enum mt9d112_width width) +static int mt9d112_i2c_write(unsigned short saddr, + unsigned short waddr, unsigned short wdata, + enum mt9d112_width width) { - int32_t rc = -EIO; + int rc = -EIO; unsigned char buf[4]; memset(buf, 0, sizeof(buf)); switch (width) { - case WORD_LEN: { - buf[0] = (waddr & 0xFF00)>>8; - buf[1] = (waddr & 0x00FF); - buf[2] = (wdata & 0xFF00)>>8; - buf[3] = (wdata & 0x00FF); + case WORD_LEN:{ + buf[0] = (waddr & 0xFF00) >> 8; + buf[1] = (waddr & 0x00FF); + buf[2] = (wdata & 0xFF00) >> 8; + buf[3] = (wdata & 0x00FF); - rc = mt9d112_i2c_txdata(saddr, buf, 4); - } + rc = mt9d112_i2c_txdata(saddr, buf, 4); + } break; - case BYTE_LEN: { - buf[0] = waddr; - buf[1] = wdata; - rc = mt9d112_i2c_txdata(saddr, buf, 2); - } + case BYTE_LEN:{ + buf[0] = waddr; + buf[1] = wdata; + rc = mt9d112_i2c_txdata(saddr, buf, 2); + } break; default: @@ -113,24 +117,22 @@ static int32_t mt9d112_i2c_write(unsigned short saddr, } if (rc < 0) - CDBG( - "i2c_write failed, addr = 0x%x, val = 0x%x!\n", - waddr, wdata); + CDBG("i2c_write failed, addr = 0x%x, val = 0x%x!\n", + waddr, wdata); return rc; } -static int32_t mt9d112_i2c_write_table( - struct mt9d112_i2c_reg_conf const *reg_conf_tbl, - int num_of_items_in_table) +static int mt9d112_i2c_write_table(struct mt9d112_i2c_reg_conf const + *reg_conf_tbl, int num_of_items_in_table) { int i; - int32_t rc = -EIO; + int rc = -EIO; for (i = 0; i < num_of_items_in_table; i++) { rc = mt9d112_i2c_write(mt9d112_client->addr, - reg_conf_tbl->waddr, reg_conf_tbl->wdata, - reg_conf_tbl->width); + reg_conf_tbl->waddr, reg_conf_tbl->wdata, + reg_conf_tbl->width); if (rc < 0) break; if (reg_conf_tbl->mdelay_time != 0) @@ -142,21 +144,21 @@ static int32_t mt9d112_i2c_write_table( } static int mt9d112_i2c_rxdata(unsigned short saddr, - unsigned char *rxdata, int length) + unsigned char *rxdata, int length) { struct i2c_msg msgs[] = { - { - .addr = saddr, - .flags = 0, - .len = 2, - .buf = rxdata, - }, - { - .addr = saddr, - .flags = I2C_M_RD, - .len = length, - .buf = rxdata, - }, + { + .addr = saddr, + .flags = 0, + .len = 2, + .buf = rxdata, + }, + { + .addr = saddr, + .flags = I2C_M_RD, + .len = length, + .buf = rxdata, + }, }; if (i2c_transfer(mt9d112_client->adapter, msgs, 2) < 0) { @@ -167,10 +169,11 @@ static int mt9d112_i2c_rxdata(unsigned short saddr, return 0; } -static int32_t mt9d112_i2c_read(unsigned short saddr, - unsigned short raddr, unsigned short *rdata, enum mt9d112_width width) +static int mt9d112_i2c_read(unsigned short saddr, + unsigned short raddr, unsigned short *rdata, + enum mt9d112_width width) { - int32_t rc = 0; + int rc = 0; unsigned char buf[4]; if (!rdata) @@ -179,16 +182,16 @@ static int32_t mt9d112_i2c_read(unsigned short saddr, memset(buf, 0, sizeof(buf)); switch (width) { - case WORD_LEN: { - buf[0] = (raddr & 0xFF00)>>8; - buf[1] = (raddr & 0x00FF); + case WORD_LEN:{ + buf[0] = (raddr & 0xFF00) >> 8; + buf[1] = (raddr & 0x00FF); - rc = mt9d112_i2c_rxdata(saddr, buf, 2); - if (rc < 0) - return rc; + rc = mt9d112_i2c_rxdata(saddr, buf, 2); + if (rc < 0) + return rc; - *rdata = buf[0] << 8 | buf[1]; - } + *rdata = buf[0] << 8 | buf[1]; + } break; default: @@ -201,36 +204,37 @@ static int32_t mt9d112_i2c_read(unsigned short saddr, return rc; } -static int32_t mt9d112_set_lens_roll_off(void) +static int mt9d112_set_lens_roll_off(void) { - int32_t rc = 0; + int rc = 0; rc = mt9d112_i2c_write_table(&mt9d112_regs.rftbl[0], - mt9d112_regs.rftbl_size); + mt9d112_regs.rftbl_size); return rc; } -static long mt9d112_reg_init(void) +static int mt9d112_reg_init(void) { - int32_t array_length; - int32_t i; - long rc; + int array_length; + int i; + int rc; /* PLL Setup Start */ rc = mt9d112_i2c_write_table(&mt9d112_regs.plltbl[0], - mt9d112_regs.plltbl_size); + mt9d112_regs.plltbl_size); if (rc < 0) return rc; - /* PLL Setup End */ + /* PLL Setup End */ array_length = mt9d112_regs.prev_snap_reg_settings_size; /* Configure sensor for Preview mode and Snapshot mode */ for (i = 0; i < array_length; i++) { rc = mt9d112_i2c_write(mt9d112_client->addr, - mt9d112_regs.prev_snap_reg_settings[i].register_address, - mt9d112_regs.prev_snap_reg_settings[i].register_value, - WORD_LEN); + mt9d112_regs.prev_snap_reg_settings[i]. + register_address, + mt9d112_regs.prev_snap_reg_settings[i]. + register_value, WORD_LEN); if (rc < 0) return rc; @@ -241,25 +245,24 @@ static long mt9d112_reg_init(void) for (i = 0; i < array_length; i++) { rc = mt9d112_i2c_write(mt9d112_client->addr, - mt9d112_regs.noise_reduction_reg_settings[i].register_address, - mt9d112_regs.noise_reduction_reg_settings[i].register_value, - WORD_LEN); + mt9d112_regs. + noise_reduction_reg_settings[i]. + register_address, + mt9d112_regs. + noise_reduction_reg_settings[i]. + register_value, WORD_LEN); if (rc < 0) return rc; } /* Set Color Kill Saturation point to optimum value */ - rc = - mt9d112_i2c_write(mt9d112_client->addr, - 0x35A4, - 0x0593, - WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, 0x35A4, 0x0593, WORD_LEN); if (rc < 0) return rc; rc = mt9d112_i2c_write_table(&mt9d112_regs.stbl[0], - mt9d112_regs.stbl_size); + mt9d112_regs.stbl_size); if (rc < 0) return rc; @@ -270,66 +273,57 @@ static long mt9d112_reg_init(void) return 0; } -static long mt9d112_set_sensor_mode(int mode) +static int mt9d112_set_sensor_mode(int mode) { uint16_t clock; - long rc = 0; + int rc = 0; switch (mode) { case SENSOR_PREVIEW_MODE: - rc = - mt9d112_i2c_write(mt9d112_client->addr, - 0x338C, 0xA20C, WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA20C, WORD_LEN); if (rc < 0) return rc; - rc = - mt9d112_i2c_write(mt9d112_client->addr, - 0x3390, 0x0004, WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0004, WORD_LEN); if (rc < 0) return rc; - rc = - mt9d112_i2c_write(mt9d112_client->addr, - 0x338C, 0xA215, WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA215, WORD_LEN); if (rc < 0) return rc; - rc = - mt9d112_i2c_write(mt9d112_client->addr, - 0x3390, 0x0004, WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0004, WORD_LEN); if (rc < 0) return rc; - rc = - mt9d112_i2c_write(mt9d112_client->addr, - 0x338C, 0xA20B, WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA20B, WORD_LEN); if (rc < 0) return rc; - rc = - mt9d112_i2c_write(mt9d112_client->addr, - 0x3390, 0x0000, WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0000, WORD_LEN); if (rc < 0) return rc; clock = 0x0250; - rc = - mt9d112_i2c_write(mt9d112_client->addr, - 0x341C, clock, WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x341C, clock, WORD_LEN); if (rc < 0) return rc; - rc = - mt9d112_i2c_write(mt9d112_client->addr, - 0x338C, 0xA103, WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA103, WORD_LEN); if (rc < 0) return rc; - rc = - mt9d112_i2c_write(mt9d112_client->addr, - 0x3390, 0x0001, WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0001, WORD_LEN); if (rc < 0) return rc; @@ -338,35 +332,30 @@ static long mt9d112_set_sensor_mode(int mode) case SENSOR_SNAPSHOT_MODE: /* Switch to lower fps for Snapshot */ - rc = - mt9d112_i2c_write(mt9d112_client->addr, - 0x341C, 0x0120, WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x341C, 0x0120, WORD_LEN); if (rc < 0) return rc; - rc = - mt9d112_i2c_write(mt9d112_client->addr, - 0x338C, 0xA120, WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA120, WORD_LEN); if (rc < 0) return rc; - rc = - mt9d112_i2c_write(mt9d112_client->addr, - 0x3390, 0x0002, WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0002, WORD_LEN); if (rc < 0) return rc; mdelay(5); - rc = - mt9d112_i2c_write(mt9d112_client->addr, - 0x338C, 0xA103, WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, 0xA103, WORD_LEN); if (rc < 0) return rc; - rc = - mt9d112_i2c_write(mt9d112_client->addr, - 0x3390, 0x0002, WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, 0x0002, WORD_LEN); if (rc < 0) return rc; break; @@ -378,11 +367,11 @@ static long mt9d112_set_sensor_mode(int mode) return 0; } -static long mt9d112_set_effect(int mode, int effect) +static int mt9d112_set_effect(int mode, int effect) { uint16_t reg_addr; uint16_t reg_val; - long rc = 0; + int rc = 0; switch (mode) { case SENSOR_PREVIEW_MODE: @@ -401,75 +390,75 @@ static long mt9d112_set_effect(int mode, int effect) } switch (effect) { - case CAMERA_EFFECT_OFF: { - reg_val = 0x6440; - - rc = mt9d112_i2c_write(mt9d112_client->addr, - 0x338C, reg_addr, WORD_LEN); - if (rc < 0) - return rc; - - rc = mt9d112_i2c_write(mt9d112_client->addr, - 0x3390, reg_val, WORD_LEN); - if (rc < 0) - return rc; - } - break; - - case CAMERA_EFFECT_MONO: { - reg_val = 0x6441; - rc = mt9d112_i2c_write(mt9d112_client->addr, - 0x338C, reg_addr, WORD_LEN); - if (rc < 0) - return rc; - - rc = mt9d112_i2c_write(mt9d112_client->addr, - 0x3390, reg_val, WORD_LEN); - if (rc < 0) - return rc; - } + case CAMERA_EFFECT_OFF:{ + reg_val = 0x6440; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; + } break; - case CAMERA_EFFECT_NEGATIVE: { - reg_val = 0x6443; - rc = mt9d112_i2c_write(mt9d112_client->addr, - 0x338C, reg_addr, WORD_LEN); - if (rc < 0) - return rc; - - rc = mt9d112_i2c_write(mt9d112_client->addr, - 0x3390, reg_val, WORD_LEN); - if (rc < 0) - return rc; - } + case CAMERA_EFFECT_MONO:{ + reg_val = 0x6441; + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; + } break; - case CAMERA_EFFECT_SOLARIZE: { - reg_val = 0x6445; - rc = mt9d112_i2c_write(mt9d112_client->addr, - 0x338C, reg_addr, WORD_LEN); - if (rc < 0) - return rc; - - rc = mt9d112_i2c_write(mt9d112_client->addr, - 0x3390, reg_val, WORD_LEN); - if (rc < 0) - return rc; - } + case CAMERA_EFFECT_NEGATIVE:{ + reg_val = 0x6443; + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; + } break; - case CAMERA_EFFECT_SEPIA: { - reg_val = 0x6442; - rc = mt9d112_i2c_write(mt9d112_client->addr, - 0x338C, reg_addr, WORD_LEN); - if (rc < 0) - return rc; + case CAMERA_EFFECT_SOLARIZE:{ + reg_val = 0x6445; + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; + } + break; - rc = mt9d112_i2c_write(mt9d112_client->addr, - 0x3390, reg_val, WORD_LEN); - if (rc < 0) - return rc; - } + case CAMERA_EFFECT_SEPIA:{ + reg_val = 0x6442; + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; + + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; + } break; case CAMERA_EFFECT_PASTEL: @@ -477,30 +466,28 @@ static long mt9d112_set_effect(int mode, int effect) case CAMERA_EFFECT_RESIZE: return -EINVAL; - default: { - reg_val = 0x6440; - rc = mt9d112_i2c_write(mt9d112_client->addr, - 0x338C, reg_addr, WORD_LEN); - if (rc < 0) - return rc; + default:{ + reg_val = 0x6440; + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x338C, reg_addr, WORD_LEN); + if (rc < 0) + return rc; - rc = mt9d112_i2c_write(mt9d112_client->addr, - 0x3390, reg_val, WORD_LEN); - if (rc < 0) - return rc; + rc = mt9d112_i2c_write(mt9d112_client->addr, + 0x3390, reg_val, WORD_LEN); + if (rc < 0) + return rc; - return -EINVAL; - } + return -EINVAL; + } } /* Refresh Sequencer */ - rc = mt9d112_i2c_write(mt9d112_client->addr, - 0x338C, 0xA103, WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, 0x338C, 0xA103, WORD_LEN); if (rc < 0) return rc; - rc = mt9d112_i2c_write(mt9d112_client->addr, - 0x3390, 0x0005, WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, 0x3390, 0x0005, WORD_LEN); return rc; } @@ -520,15 +507,15 @@ static int mt9d112_sensor_init_probe(const struct msm_camera_sensor_info *data) mdelay(5); /* Micron suggested Power up block Start: - * Put MCU into Reset - Stop MCU */ + * Put MCU into Reset - Stop MCU */ rc = mt9d112_i2c_write(mt9d112_client->addr, - REG_MT9D112_MCU_BOOT, 0x0501, WORD_LEN); + REG_MT9D112_MCU_BOOT, 0x0501, WORD_LEN); if (rc < 0) goto init_probe_fail; /* Pull MCU from Reset - Start MCU */ rc = mt9d112_i2c_write(mt9d112_client->addr, - REG_MT9D112_MCU_BOOT, 0x0500, WORD_LEN); + REG_MT9D112_MCU_BOOT, 0x0500, WORD_LEN); if (rc < 0) goto init_probe_fail; @@ -536,18 +523,17 @@ static int mt9d112_sensor_init_probe(const struct msm_camera_sensor_info *data) /* Micron Suggested - Power up block */ rc = mt9d112_i2c_write(mt9d112_client->addr, - REG_MT9D112_SENSOR_RESET, 0x0ACC, WORD_LEN); + REG_MT9D112_SENSOR_RESET, 0x0ACC, WORD_LEN); if (rc < 0) goto init_probe_fail; rc = mt9d112_i2c_write(mt9d112_client->addr, - REG_MT9D112_STANDBY_CONTROL, 0x0008, WORD_LEN); + REG_MT9D112_STANDBY_CONTROL, 0x0008, WORD_LEN); if (rc < 0) goto init_probe_fail; /* FUSED_DEFECT_CORRECTION */ - rc = mt9d112_i2c_write(mt9d112_client->addr, - 0x33F4, 0x031D, WORD_LEN); + rc = mt9d112_i2c_write(mt9d112_client->addr, 0x33F4, 0x031D, WORD_LEN); if (rc < 0) goto init_probe_fail; @@ -556,7 +542,7 @@ static int mt9d112_sensor_init_probe(const struct msm_camera_sensor_info *data) /* Micron suggested Power up block End */ /* Read the Model ID of the sensor */ rc = mt9d112_i2c_read(mt9d112_client->addr, - REG_MT9D112_MODEL_ID, &model_id, WORD_LEN); + REG_MT9D112_MODEL_ID, &model_id, WORD_LEN); if (rc < 0) goto init_probe_fail; @@ -622,36 +608,29 @@ static int mt9d112_init_client(struct i2c_client *client) int mt9d112_sensor_config(void __user *argp) { struct sensor_cfg_data cfg_data; - long rc = 0; + long rc = 0; if (copy_from_user(&cfg_data, - (void *)argp, - sizeof(struct sensor_cfg_data))) + (void *)argp, sizeof(struct sensor_cfg_data))) return -EFAULT; - /* down(&mt9d112_sem); */ - CDBG("mt9d112_ioctl, cfgtype = %d, mode = %d\n", - cfg_data.cfgtype, cfg_data.mode); - - switch (cfg_data.cfgtype) { - case CFG_SET_MODE: - rc = mt9d112_set_sensor_mode( - cfg_data.mode); - break; + cfg_data.cfgtype, cfg_data.mode); - case CFG_SET_EFFECT: - rc = mt9d112_set_effect(cfg_data.mode, - cfg_data.cfg.effect); - break; + switch (cfg_data.cfgtype) { + case CFG_SET_MODE: + rc = mt9d112_set_sensor_mode(cfg_data.mode); + break; - case CFG_GET_AF_MAX_STEPS: - default: - rc = -EINVAL; - break; - } + case CFG_SET_EFFECT: + rc = mt9d112_set_effect(cfg_data.mode, cfg_data.cfg.effect); + break; - /* up(&mt9d112_sem); */ + case CFG_GET_AF_MAX_STEPS: + default: + rc = -EINVAL; + break; + } return rc; } @@ -660,16 +639,13 @@ int mt9d112_sensor_release(void) { int rc = 0; - /* down(&mt9d112_sem); */ - kfree(mt9d112_ctrl); - /* up(&mt9d112_sem); */ return rc; } static int mt9d112_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { int rc = 0; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { @@ -677,8 +653,7 @@ static int mt9d112_i2c_probe(struct i2c_client *client, goto probe_failure; } - mt9d112_sensorw = - kzalloc(sizeof(struct mt9d112_work), GFP_KERNEL); + mt9d112_sensorw = kzalloc(sizeof(struct mt9d112_work), GFP_KERNEL); if (!mt9d112_sensorw) { rc = -ENOMEM; @@ -701,17 +676,17 @@ static int mt9d112_i2c_probe(struct i2c_client *client, } static const struct i2c_device_id mt9d112_i2c_id[] = { - { "mt9d112", 0}, - { }, + {"mt9d112", 0}, + {}, }; static struct i2c_driver mt9d112_i2c_driver = { .id_table = mt9d112_i2c_id, - .probe = mt9d112_i2c_probe, + .probe = mt9d112_i2c_probe, .remove = __exit_p(mt9d112_i2c_remove), .driver = { - .name = "mt9d112", - }, + .name = "mt9d112", + }, }; static int mt9d112_sensor_probe(const struct msm_camera_sensor_info *info, @@ -733,7 +708,7 @@ static int mt9d112_sensor_probe(const struct msm_camera_sensor_info *info, s->s_init = mt9d112_sensor_init; s->s_release = mt9d112_sensor_release; - s->s_config = mt9d112_sensor_config; + s->s_config = mt9d112_sensor_config; probe_done: CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__); @@ -748,9 +723,9 @@ static int __mt9d112_probe(struct platform_device *pdev) static struct platform_driver msm_camera_driver = { .probe = __mt9d112_probe, .driver = { - .name = "msm_camera_mt9d112", - .owner = THIS_MODULE, - }, + .name = "msm_camera_mt9d112", + .owner = THIS_MODULE, + }, }; static int __init mt9d112_init(void) diff --git a/drivers/media/video/msm/mt9d112.h b/drivers/media/video/msm/mt9d112.h index c678996f9e2ba..39777c7343a53 100644 --- a/drivers/media/video/msm/mt9d112.h +++ b/drivers/media/video/msm/mt9d112.h @@ -1,5 +1,19 @@ -/* - * Copyright (C) 2008-2009 QUALCOMM Incorporated. +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ #ifndef MT9D112_H @@ -33,4 +47,6 @@ struct mt9d112_reg { uint16_t rftbl_size; }; +extern struct mt9d112_reg mt9d112_regs; + #endif /* MT9D112_H */ diff --git a/drivers/media/video/msm/mt9d112_reg.c b/drivers/media/video/msm/mt9d112_reg.c index c52e96f47141f..f2ae0c7d1301f 100644 --- a/drivers/media/video/msm/mt9d112_reg.c +++ b/drivers/media/video/msm/mt9d112_reg.c @@ -1,45 +1,59 @@ -/* - * Copyright (C) 2008-2009 QUALCOMM Incorporated. +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ #include "mt9d112.h" struct register_address_value_pair -preview_snapshot_mode_reg_settings_array[] = { + preview_snapshot_mode_reg_settings_array[] = { {0x338C, 0x2703}, - {0x3390, 800}, /* Output Width (P) = 640 */ + {0x3390, 800}, /* Output Width (P) = 640 */ {0x338C, 0x2705}, - {0x3390, 600}, /* Output Height (P) = 480 */ + {0x3390, 600}, /* Output Height (P) = 480 */ {0x338C, 0x2707}, - {0x3390, 0x0640}, /* Output Width (S) = 1600 */ + {0x3390, 0x0640}, /* Output Width (S) = 1600 */ {0x338C, 0x2709}, - {0x3390, 0x04B0}, /* Output Height (S) = 1200 */ + {0x3390, 0x04B0}, /* Output Height (S) = 1200 */ {0x338C, 0x270D}, - {0x3390, 0x0000}, /* Row Start (P) = 0 */ + {0x3390, 0x0000}, /* Row Start (P) = 0 */ {0x338C, 0x270F}, - {0x3390, 0x0000}, /* Column Start (P) = 0 */ + {0x3390, 0x0000}, /* Column Start (P) = 0 */ {0x338C, 0x2711}, - {0x3390, 0x04BD}, /* Row End (P) = 1213 */ + {0x3390, 0x04BD}, /* Row End (P) = 1213 */ {0x338C, 0x2713}, - {0x3390, 0x064D}, /* Column End (P) = 1613 */ + {0x3390, 0x064D}, /* Column End (P) = 1613 */ {0x338C, 0x2715}, - {0x3390, 0x0000}, /* Extra Delay (P) = 0 */ + {0x3390, 0x0000}, /* Extra Delay (P) = 0 */ {0x338C, 0x2717}, - {0x3390, 0x2111}, /* Row Speed (P) = 8465 */ + {0x3390, 0x2111}, /* Row Speed (P) = 8465 */ {0x338C, 0x2719}, - {0x3390, 0x046C}, /* Read Mode (P) = 1132 */ + {0x3390, 0x046C}, /* Read Mode (P) = 1132 */ {0x338C, 0x271B}, - {0x3390, 0x024F}, /* Sensor_Sample_Time_pck(P) = 591 */ + {0x3390, 0x024F}, /* Sensor_Sample_Time_pck(P) = 591 */ {0x338C, 0x271D}, - {0x3390, 0x0102}, /* Sensor_Fine_Correction(P) = 258 */ + {0x3390, 0x0102}, /* Sensor_Fine_Correction(P) = 258 */ {0x338C, 0x271F}, - {0x3390, 0x0279}, /* Sensor_Fine_IT_min(P) = 633 */ + {0x3390, 0x0279}, /* Sensor_Fine_IT_min(P) = 633 */ {0x338C, 0x2721}, - {0x3390, 0x0155}, /* Sensor_Fine_IT_max_margin(P) = 341 */ + {0x3390, 0x0155}, /* Sensor_Fine_IT_max_margin(P) = 341 */ {0x338C, 0x2723}, - {0x3390, 659}, /* Frame Lines (P) = 679 */ + {0x3390, 659}, /* Frame Lines (P) = 679 */ {0x338C, 0x2725}, - {0x3390, 0x0824}, /* Line Length (P) = 2084 */ + {0x3390, 0x0824}, /* Line Length (P) = 2084 */ {0x338C, 0x2727}, {0x3390, 0x2020}, {0x338C, 0x2729}, @@ -49,45 +63,45 @@ preview_snapshot_mode_reg_settings_array[] = { {0x338C, 0x272D}, {0x3390, 0x2007}, {0x338C, 0x272F}, - {0x3390, 0x0004}, /* Row Start(S) = 4 */ + {0x3390, 0x0004}, /* Row Start(S) = 4 */ {0x338C, 0x2731}, - {0x3390, 0x0004}, /* Column Start(S) = 4 */ + {0x3390, 0x0004}, /* Column Start(S) = 4 */ {0x338C, 0x2733}, - {0x3390, 0x04BB}, /* Row End(S) = 1211 */ + {0x3390, 0x04BB}, /* Row End(S) = 1211 */ {0x338C, 0x2735}, - {0x3390, 0x064B}, /* Column End(S) = 1611 */ + {0x3390, 0x064B}, /* Column End(S) = 1611 */ {0x338C, 0x2737}, - {0x3390, 0x04CE}, /* Extra Delay(S) = 1230 */ + {0x3390, 0x04CE}, /* Extra Delay(S) = 1230 */ {0x338C, 0x2739}, - {0x3390, 0x2111}, /* Row Speed(S) = 8465 */ + {0x3390, 0x2111}, /* Row Speed(S) = 8465 */ {0x338C, 0x273B}, - {0x3390, 0x0024}, /* Read Mode(S) = 36 */ + {0x3390, 0x0024}, /* Read Mode(S) = 36 */ {0x338C, 0x273D}, - {0x3390, 0x0120}, /* Sensor sample time pck(S) = 288 */ + {0x3390, 0x0120}, /* Sensor sample time pck(S) = 288 */ {0x338C, 0x2741}, - {0x3390, 0x0169}, /* Sensor_Fine_IT_min(P) = 361 */ + {0x3390, 0x0169}, /* Sensor_Fine_IT_min(P) = 361 */ {0x338C, 0x2745}, - {0x3390, 0x04FF}, /* Frame Lines(S) = 1279 */ + {0x3390, 0x04FF}, /* Frame Lines(S) = 1279 */ {0x338C, 0x2747}, - {0x3390, 0x0824}, /* Line Length(S) = 2084 */ + {0x3390, 0x0824}, /* Line Length(S) = 2084 */ {0x338C, 0x2751}, - {0x3390, 0x0000}, /* Crop_X0(P) = 0 */ + {0x3390, 0x0000}, /* Crop_X0(P) = 0 */ {0x338C, 0x2753}, - {0x3390, 0x0320}, /* Crop_X1(P) = 800 */ + {0x3390, 0x0320}, /* Crop_X1(P) = 800 */ {0x338C, 0x2755}, - {0x3390, 0x0000}, /* Crop_Y0(P) = 0 */ + {0x3390, 0x0000}, /* Crop_Y0(P) = 0 */ {0x338C, 0x2757}, - {0x3390, 0x0258}, /* Crop_Y1(P) = 600 */ + {0x3390, 0x0258}, /* Crop_Y1(P) = 600 */ {0x338C, 0x275F}, - {0x3390, 0x0000}, /* Crop_X0(S) = 0 */ + {0x3390, 0x0000}, /* Crop_X0(S) = 0 */ {0x338C, 0x2761}, - {0x3390, 0x0640}, /* Crop_X1(S) = 1600 */ + {0x3390, 0x0640}, /* Crop_X1(S) = 1600 */ {0x338C, 0x2763}, - {0x3390, 0x0000}, /* Crop_Y0(S) = 0 */ + {0x3390, 0x0000}, /* Crop_Y0(S) = 0 */ {0x338C, 0x2765}, - {0x3390, 0x04B0}, /* Crop_Y1(S) = 1200 */ + {0x3390, 0x04B0}, /* Crop_Y1(S) = 1200 */ {0x338C, 0x222E}, - {0x3390, 0x00A0}, /* R9 Step = 160 */ + {0x3390, 0x00A0}, /* R9 Step = 160 */ {0x338C, 0xA408}, {0x3390, 0x001F}, {0x338C, 0xA409}, @@ -105,13 +119,13 @@ preview_snapshot_mode_reg_settings_array[] = { {0x338C, 0x2417}, {0x3390, 0x00C0}, {0x338C, 0x2799}, - {0x3390, 0x6408}, /* MODE_SPEC_EFFECTS(P) */ + {0x3390, 0x6408}, /* MODE_SPEC_EFFECTS(P) */ {0x338C, 0x279B}, - {0x3390, 0x6408}, /* MODE_SPEC_EFFECTS(S) */ + {0x3390, 0x6408}, /* MODE_SPEC_EFFECTS(S) */ }; static struct register_address_value_pair -noise_reduction_reg_settings_array[] = { + noise_reduction_reg_settings_array[] = { {0x338C, 0xA76D}, {0x3390, 0x0003}, {0x338C, 0xA76E}, @@ -195,106 +209,108 @@ noise_reduction_reg_settings_array[] = { }; static const struct mt9d112_i2c_reg_conf const lens_roll_off_tbl[] = { - { 0x34CE, 0x81A0, WORD_LEN, 0 }, - { 0x34D0, 0x6331, WORD_LEN, 0 }, - { 0x34D2, 0x3394, WORD_LEN, 0 }, - { 0x34D4, 0x9966, WORD_LEN, 0 }, - { 0x34D6, 0x4B25, WORD_LEN, 0 }, - { 0x34D8, 0x2670, WORD_LEN, 0 }, - { 0x34DA, 0x724C, WORD_LEN, 0 }, - { 0x34DC, 0xFFFD, WORD_LEN, 0 }, - { 0x34DE, 0x00CA, WORD_LEN, 0 }, - { 0x34E6, 0x00AC, WORD_LEN, 0 }, - { 0x34EE, 0x0EE1, WORD_LEN, 0 }, - { 0x34F6, 0x0D87, WORD_LEN, 0 }, - { 0x3500, 0xE1F7, WORD_LEN, 0 }, - { 0x3508, 0x1CF4, WORD_LEN, 0 }, - { 0x3510, 0x1D28, WORD_LEN, 0 }, - { 0x3518, 0x1F26, WORD_LEN, 0 }, - { 0x3520, 0x2220, WORD_LEN, 0 }, - { 0x3528, 0x333D, WORD_LEN, 0 }, - { 0x3530, 0x15D9, WORD_LEN, 0 }, - { 0x3538, 0xCFB8, WORD_LEN, 0 }, - { 0x354C, 0x05FE, WORD_LEN, 0 }, - { 0x3544, 0x05F8, WORD_LEN, 0 }, - { 0x355C, 0x0596, WORD_LEN, 0 }, - { 0x3554, 0x0611, WORD_LEN, 0 }, - { 0x34E0, 0x00F2, WORD_LEN, 0 }, - { 0x34E8, 0x00A8, WORD_LEN, 0 }, - { 0x34F0, 0x0F7B, WORD_LEN, 0 }, - { 0x34F8, 0x0CD7, WORD_LEN, 0 }, - { 0x3502, 0xFEDB, WORD_LEN, 0 }, - { 0x350A, 0x13E4, WORD_LEN, 0 }, - { 0x3512, 0x1F2C, WORD_LEN, 0 }, - { 0x351A, 0x1D20, WORD_LEN, 0 }, - { 0x3522, 0x2422, WORD_LEN, 0 }, - { 0x352A, 0x2925, WORD_LEN, 0 }, - { 0x3532, 0x1D04, WORD_LEN, 0 }, - { 0x353A, 0xFBF2, WORD_LEN, 0 }, - { 0x354E, 0x0616, WORD_LEN, 0 }, - { 0x3546, 0x0597, WORD_LEN, 0 }, - { 0x355E, 0x05CD, WORD_LEN, 0 }, - { 0x3556, 0x0529, WORD_LEN, 0 }, - { 0x34E4, 0x00B2, WORD_LEN, 0 }, - { 0x34EC, 0x005E, WORD_LEN, 0 }, - { 0x34F4, 0x0F43, WORD_LEN, 0 }, - { 0x34FC, 0x0E2F, WORD_LEN, 0 }, - { 0x3506, 0xF9FC, WORD_LEN, 0 }, - { 0x350E, 0x0CE4, WORD_LEN, 0 }, - { 0x3516, 0x1E1E, WORD_LEN, 0 }, - { 0x351E, 0x1B19, WORD_LEN, 0 }, - { 0x3526, 0x151B, WORD_LEN, 0 }, - { 0x352E, 0x1416, WORD_LEN, 0 }, - { 0x3536, 0x10FC, WORD_LEN, 0 }, - { 0x353E, 0xC018, WORD_LEN, 0 }, - { 0x3552, 0x06B4, WORD_LEN, 0 }, - { 0x354A, 0x0506, WORD_LEN, 0 }, - { 0x3562, 0x06AB, WORD_LEN, 0 }, - { 0x355A, 0x063A, WORD_LEN, 0 }, - { 0x34E2, 0x00E5, WORD_LEN, 0 }, - { 0x34EA, 0x008B, WORD_LEN, 0 }, - { 0x34F2, 0x0E4C, WORD_LEN, 0 }, - { 0x34FA, 0x0CA3, WORD_LEN, 0 }, - { 0x3504, 0x0907, WORD_LEN, 0 }, - { 0x350C, 0x1DFD, WORD_LEN, 0 }, - { 0x3514, 0x1E24, WORD_LEN, 0 }, - { 0x351C, 0x2529, WORD_LEN, 0 }, - { 0x3524, 0x1D20, WORD_LEN, 0 }, - { 0x352C, 0x2332, WORD_LEN, 0 }, - { 0x3534, 0x10E9, WORD_LEN, 0 }, - { 0x353C, 0x0BCB, WORD_LEN, 0 }, - { 0x3550, 0x04EF, WORD_LEN, 0 }, - { 0x3548, 0x0609, WORD_LEN, 0 }, - { 0x3560, 0x0580, WORD_LEN, 0 }, - { 0x3558, 0x05DD, WORD_LEN, 0 }, - { 0x3540, 0x0000, WORD_LEN, 0 }, - { 0x3542, 0x0000, WORD_LEN, 0 } + {0x34CE, 0x81A0, WORD_LEN, 0}, + {0x34D0, 0x6331, WORD_LEN, 0}, + {0x34D2, 0x3394, WORD_LEN, 0}, + {0x34D4, 0x9966, WORD_LEN, 0}, + {0x34D6, 0x4B25, WORD_LEN, 0}, + {0x34D8, 0x2670, WORD_LEN, 0}, + {0x34DA, 0x724C, WORD_LEN, 0}, + {0x34DC, 0xFFFD, WORD_LEN, 0}, + {0x34DE, 0x00CA, WORD_LEN, 0}, + {0x34E6, 0x00AC, WORD_LEN, 0}, + {0x34EE, 0x0EE1, WORD_LEN, 0}, + {0x34F6, 0x0D87, WORD_LEN, 0}, + {0x3500, 0xE1F7, WORD_LEN, 0}, + {0x3508, 0x1CF4, WORD_LEN, 0}, + {0x3510, 0x1D28, WORD_LEN, 0}, + {0x3518, 0x1F26, WORD_LEN, 0}, + {0x3520, 0x2220, WORD_LEN, 0}, + {0x3528, 0x333D, WORD_LEN, 0}, + {0x3530, 0x15D9, WORD_LEN, 0}, + {0x3538, 0xCFB8, WORD_LEN, 0}, + {0x354C, 0x05FE, WORD_LEN, 0}, + {0x3544, 0x05F8, WORD_LEN, 0}, + {0x355C, 0x0596, WORD_LEN, 0}, + {0x3554, 0x0611, WORD_LEN, 0}, + {0x34E0, 0x00F2, WORD_LEN, 0}, + {0x34E8, 0x00A8, WORD_LEN, 0}, + {0x34F0, 0x0F7B, WORD_LEN, 0}, + {0x34F8, 0x0CD7, WORD_LEN, 0}, + {0x3502, 0xFEDB, WORD_LEN, 0}, + {0x350A, 0x13E4, WORD_LEN, 0}, + {0x3512, 0x1F2C, WORD_LEN, 0}, + {0x351A, 0x1D20, WORD_LEN, 0}, + {0x3522, 0x2422, WORD_LEN, 0}, + {0x352A, 0x2925, WORD_LEN, 0}, + {0x3532, 0x1D04, WORD_LEN, 0}, + {0x353A, 0xFBF2, WORD_LEN, 0}, + {0x354E, 0x0616, WORD_LEN, 0}, + {0x3546, 0x0597, WORD_LEN, 0}, + {0x355E, 0x05CD, WORD_LEN, 0}, + {0x3556, 0x0529, WORD_LEN, 0}, + {0x34E4, 0x00B2, WORD_LEN, 0}, + {0x34EC, 0x005E, WORD_LEN, 0}, + {0x34F4, 0x0F43, WORD_LEN, 0}, + {0x34FC, 0x0E2F, WORD_LEN, 0}, + {0x3506, 0xF9FC, WORD_LEN, 0}, + {0x350E, 0x0CE4, WORD_LEN, 0}, + {0x3516, 0x1E1E, WORD_LEN, 0}, + {0x351E, 0x1B19, WORD_LEN, 0}, + {0x3526, 0x151B, WORD_LEN, 0}, + {0x352E, 0x1416, WORD_LEN, 0}, + {0x3536, 0x10FC, WORD_LEN, 0}, + {0x353E, 0xC018, WORD_LEN, 0}, + {0x3552, 0x06B4, WORD_LEN, 0}, + {0x354A, 0x0506, WORD_LEN, 0}, + {0x3562, 0x06AB, WORD_LEN, 0}, + {0x355A, 0x063A, WORD_LEN, 0}, + {0x34E2, 0x00E5, WORD_LEN, 0}, + {0x34EA, 0x008B, WORD_LEN, 0}, + {0x34F2, 0x0E4C, WORD_LEN, 0}, + {0x34FA, 0x0CA3, WORD_LEN, 0}, + {0x3504, 0x0907, WORD_LEN, 0}, + {0x350C, 0x1DFD, WORD_LEN, 0}, + {0x3514, 0x1E24, WORD_LEN, 0}, + {0x351C, 0x2529, WORD_LEN, 0}, + {0x3524, 0x1D20, WORD_LEN, 0}, + {0x352C, 0x2332, WORD_LEN, 0}, + {0x3534, 0x10E9, WORD_LEN, 0}, + {0x353C, 0x0BCB, WORD_LEN, 0}, + {0x3550, 0x04EF, WORD_LEN, 0}, + {0x3548, 0x0609, WORD_LEN, 0}, + {0x3560, 0x0580, WORD_LEN, 0}, + {0x3558, 0x05DD, WORD_LEN, 0}, + {0x3540, 0x0000, WORD_LEN, 0}, + {0x3542, 0x0000, WORD_LEN, 0} }; static const struct mt9d112_i2c_reg_conf const pll_setup_tbl[] = { - { 0x341E, 0x8F09, WORD_LEN, 0 }, - { 0x341C, 0x0250, WORD_LEN, 0 }, - { 0x341E, 0x8F09, WORD_LEN, 5 }, - { 0x341E, 0x8F08, WORD_LEN, 0 } + {0x341E, 0x8F09, WORD_LEN, 0}, + {0x341C, 0x0250, WORD_LEN, 0}, + {0x341E, 0x8F09, WORD_LEN, 5}, + {0x341E, 0x8F08, WORD_LEN, 0} }; /* Refresh Sequencer */ static const struct mt9d112_i2c_reg_conf const sequencer_tbl[] = { - { 0x338C, 0x2799, WORD_LEN, 0}, - { 0x3390, 0x6440, WORD_LEN, 5}, - { 0x338C, 0x279B, WORD_LEN, 0}, - { 0x3390, 0x6440, WORD_LEN, 5}, - { 0x338C, 0xA103, WORD_LEN, 0}, - { 0x3390, 0x0005, WORD_LEN, 5}, - { 0x338C, 0xA103, WORD_LEN, 0}, - { 0x3390, 0x0006, WORD_LEN, 5} + {0x338C, 0x2799, WORD_LEN, 0}, + {0x3390, 0x6440, WORD_LEN, 5}, + {0x338C, 0x279B, WORD_LEN, 0}, + {0x3390, 0x6440, WORD_LEN, 5}, + {0x338C, 0xA103, WORD_LEN, 0}, + {0x3390, 0x0005, WORD_LEN, 5}, + {0x338C, 0xA103, WORD_LEN, 0}, + {0x3390, 0x0006, WORD_LEN, 5} }; struct mt9d112_reg mt9d112_regs = { .prev_snap_reg_settings = &preview_snapshot_mode_reg_settings_array[0], - .prev_snap_reg_settings_size = ARRAY_SIZE(preview_snapshot_mode_reg_settings_array), + .prev_snap_reg_settings_size = + ARRAY_SIZE(preview_snapshot_mode_reg_settings_array), .noise_reduction_reg_settings = &noise_reduction_reg_settings_array[0], - .noise_reduction_reg_settings_size = ARRAY_SIZE(noise_reduction_reg_settings_array), + .noise_reduction_reg_settings_size = + ARRAY_SIZE(noise_reduction_reg_settings_array), .plltbl = pll_setup_tbl, .plltbl_size = ARRAY_SIZE(pll_setup_tbl), .stbl = sequencer_tbl, @@ -302,6 +318,3 @@ struct mt9d112_reg mt9d112_regs = { .rftbl = lens_roll_off_tbl, .rftbl_size = ARRAY_SIZE(lens_roll_off_tbl) }; - - - diff --git a/drivers/media/video/msm/mt9p012.h b/drivers/media/video/msm/mt9p012.h index 678a0027d42ee..4c05f79e14d2e 100644 --- a/drivers/media/video/msm/mt9p012.h +++ b/drivers/media/video/msm/mt9p012.h @@ -1,42 +1,53 @@ -/* - * Copyright (C) 2008-2009 QUALCOMM Incorporated. +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ - #ifndef MT9T012_H #define MT9T012_H #include struct reg_struct { - uint16_t vt_pix_clk_div; /* 0x0300 */ - uint16_t vt_sys_clk_div; /* 0x0302 */ - uint16_t pre_pll_clk_div; /* 0x0304 */ - uint16_t pll_multiplier; /* 0x0306 */ - uint16_t op_pix_clk_div; /* 0x0308 */ - uint16_t op_sys_clk_div; /* 0x030A */ - uint16_t scale_m; /* 0x0404 */ - uint16_t row_speed; /* 0x3016 */ - uint16_t x_addr_start; /* 0x3004 */ - uint16_t x_addr_end; /* 0x3008 */ - uint16_t y_addr_start; /* 0x3002 */ - uint16_t y_addr_end; /* 0x3006 */ - uint16_t read_mode; /* 0x3040 */ - uint16_t x_output_size ; /* 0x034C */ - uint16_t y_output_size; /* 0x034E */ - uint16_t line_length_pck; /* 0x300C */ - uint16_t frame_length_lines; /* 0x300A */ - uint16_t coarse_int_time; /* 0x3012 */ - uint16_t fine_int_time; /* 0x3014 */ + uint16_t vt_pix_clk_div; /* 0x0300 */ + uint16_t vt_sys_clk_div; /* 0x0302 */ + uint16_t pre_pll_clk_div; /* 0x0304 */ + uint16_t pll_multiplier; /* 0x0306 */ + uint16_t op_pix_clk_div; /* 0x0308 */ + uint16_t op_sys_clk_div; /* 0x030A */ + uint16_t scale_m; /* 0x0404 */ + uint16_t row_speed; /* 0x3016 */ + uint16_t x_addr_start; /* 0x3004 */ + uint16_t x_addr_end; /* 0x3008 */ + uint16_t y_addr_start; /* 0x3002 */ + uint16_t y_addr_end; /* 0x3006 */ + uint16_t read_mode; /* 0x3040 */ + uint16_t x_output_size; /* 0x034C */ + uint16_t y_output_size; /* 0x034E */ + uint16_t line_length_pck; /* 0x300C */ + uint16_t frame_length_lines; /* 0x300A */ + uint16_t coarse_int_time; /* 0x3012 */ + uint16_t fine_int_time; /* 0x3014 */ }; - struct mt9p012_i2c_reg_conf { unsigned short waddr; unsigned short wdata; }; - struct mt9p012_reg { struct reg_struct *reg_pat; uint16_t reg_pat_size; @@ -48,4 +59,6 @@ struct mt9p012_reg { uint16_t rftbl_size; }; +extern struct mt9p012_reg mt9p012_regs; + #endif /* MT9T012_H */ diff --git a/drivers/media/video/msm/mt9p012_fox.c b/drivers/media/video/msm/mt9p012_fox.c index 70119d5e0ab32..f2ff40a6614f5 100644 --- a/drivers/media/video/msm/mt9p012_fox.c +++ b/drivers/media/video/msm/mt9p012_fox.c @@ -1,5 +1,19 @@ -/* - * Copyright (C) 2008-2009 QUALCOMM Incorporated. +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ #include @@ -48,7 +62,6 @@ #define MT9P012_REV_7 - enum mt9p012_test_mode { TEST_OFF, TEST_1, @@ -93,7 +106,7 @@ enum mt9p012_setting { /* for 20 fps preview */ #define MT9P012_DEFAULT_CLOCK_RATE 24000000 -#define MT9P012_DEFAULT_MAX_FPS 26 /* ???? */ +#define MT9P012_DEFAULT_MAX_FPS 26 /* ???? */ struct mt9p012_work { struct work_struct work; @@ -105,8 +118,8 @@ struct mt9p012_ctrl { const struct msm_camera_sensor_info *sensordata; int sensormode; - uint32_t fps_divider; /* init to 1 * 0x00000400 */ - uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */ + uint32_t fps_divider; /* init to 1 * 0x00000400 */ + uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */ uint16_t curr_lens_pos; uint16_t init_curr_lens_pos; @@ -116,39 +129,28 @@ struct mt9p012_ctrl { enum mt9p012_resolution prev_res; enum mt9p012_resolution pict_res; enum mt9p012_resolution curr_res; - enum mt9p012_test_mode set_test; + enum mt9p012_test_mode set_test; }; - static struct mt9p012_ctrl *mt9p012_ctrl; static DECLARE_WAIT_QUEUE_HEAD(mt9p012_wait_queue); -DECLARE_MUTEX(mt9p012_sem); - -/*============================================================= - EXTERNAL DECLARATIONS -==============================================================*/ -extern struct mt9p012_reg mt9p012_regs; /* from mt9p012_reg.c */ - - - -/*=============================================================*/ static int mt9p012_i2c_rxdata(unsigned short saddr, unsigned char *rxdata, - int length) + int length) { struct i2c_msg msgs[] = { { - .addr = saddr, - .flags = 0, - .len = 2, - .buf = rxdata, - }, + .addr = saddr, + .flags = 0, + .len = 2, + .buf = rxdata, + }, { - .addr = saddr, - .flags = I2C_M_RD, - .len = length, - .buf = rxdata, - }, + .addr = saddr, + .flags = I2C_M_RD, + .len = length, + .buf = rxdata, + }, }; if (i2c_transfer(mt9p012_client->adapter, msgs, 2) < 0) { @@ -159,10 +161,10 @@ static int mt9p012_i2c_rxdata(unsigned short saddr, unsigned char *rxdata, return 0; } -static int32_t mt9p012_i2c_read_w(unsigned short saddr, unsigned short raddr, - unsigned short *rdata) +static int mt9p012_i2c_read_w(unsigned short saddr, unsigned short raddr, + unsigned short *rdata) { - int32_t rc = 0; + int rc = 0; unsigned char buf[4]; if (!rdata) @@ -170,7 +172,7 @@ static int32_t mt9p012_i2c_read_w(unsigned short saddr, unsigned short raddr, memset(buf, 0, sizeof(buf)); - buf[0] = (raddr & 0xFF00)>>8; + buf[0] = (raddr & 0xFF00) >> 8; buf[1] = (raddr & 0x00FF); rc = mt9p012_i2c_rxdata(saddr, buf, 2); @@ -185,16 +187,16 @@ static int32_t mt9p012_i2c_read_w(unsigned short saddr, unsigned short raddr, return rc; } -static int32_t mt9p012_i2c_txdata(unsigned short saddr, unsigned char *txdata, - int length) +static int mt9p012_i2c_txdata(unsigned short saddr, unsigned char *txdata, + int length) { struct i2c_msg msg[] = { { - .addr = saddr, - .flags = 0, - .len = length, - .buf = txdata, - }, + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, + }, }; if (i2c_transfer(mt9p012_client->adapter, msg, 1) < 0) { @@ -205,10 +207,10 @@ static int32_t mt9p012_i2c_txdata(unsigned short saddr, unsigned char *txdata, return 0; } -static int32_t mt9p012_i2c_write_b(unsigned short saddr, unsigned short baddr, - unsigned short bdata) +static int mt9p012_i2c_write_b(unsigned short saddr, unsigned short baddr, + unsigned short bdata) { - int32_t rc = -EIO; + int rc = -EIO; unsigned char buf[2]; memset(buf, 0, sizeof(buf)); @@ -218,41 +220,42 @@ static int32_t mt9p012_i2c_write_b(unsigned short saddr, unsigned short baddr, if (rc < 0) CDBG("i2c_write failed, saddr = 0x%x addr = 0x%x, val =0x%x!\n", - saddr, baddr, bdata); + saddr, baddr, bdata); return rc; } -static int32_t mt9p012_i2c_write_w(unsigned short saddr, unsigned short waddr, - unsigned short wdata) +static int mt9p012_i2c_write_w(unsigned short saddr, unsigned short waddr, + unsigned short wdata) { - int32_t rc = -EIO; + int rc = -EIO; unsigned char buf[4]; memset(buf, 0, sizeof(buf)); - buf[0] = (waddr & 0xFF00)>>8; + buf[0] = (waddr & 0xFF00) >> 8; buf[1] = (waddr & 0x00FF); - buf[2] = (wdata & 0xFF00)>>8; + buf[2] = (wdata & 0xFF00) >> 8; buf[3] = (wdata & 0x00FF); rc = mt9p012_i2c_txdata(saddr, buf, 4); if (rc < 0) CDBG("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n", - waddr, wdata); + waddr, wdata); return rc; } -static int32_t mt9p012_i2c_write_w_table( - struct mt9p012_i2c_reg_conf *reg_conf_tbl, int num) +static int mt9p012_i2c_write_w_table(struct mt9p012_i2c_reg_conf + *reg_conf_tbl, int num) { int i; - int32_t rc = -EIO; + int rc = -EIO; for (i = 0; i < num; i++) { rc = mt9p012_i2c_write_w(mt9p012_client->addr, - reg_conf_tbl->waddr, reg_conf_tbl->wdata); + reg_conf_tbl->waddr, + reg_conf_tbl->wdata); if (rc < 0) break; reg_conf_tbl++; @@ -261,70 +264,75 @@ static int32_t mt9p012_i2c_write_w_table( return rc; } -static int32_t mt9p012_test(enum mt9p012_test_mode mo) +static int mt9p012_test(enum mt9p012_test_mode mo) { - int32_t rc = 0; + int rc = 0; rc = mt9p012_i2c_write_w(mt9p012_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_HOLD); + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); if (rc < 0) return rc; if (mo == TEST_OFF) return 0; else { - rc = mt9p012_i2c_write_w_table(mt9p012_regs.ttbl, mt9p012_regs.ttbl_size); + rc = mt9p012_i2c_write_w_table(mt9p012_regs.ttbl, + mt9p012_regs.ttbl_size); if (rc < 0) return rc; rc = mt9p012_i2c_write_w(mt9p012_client->addr, - REG_TEST_PATTERN_MODE, (uint16_t)mo); + REG_TEST_PATTERN_MODE, (uint16_t) mo); if (rc < 0) return rc; } rc = mt9p012_i2c_write_w(mt9p012_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_UPDATE); + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); if (rc < 0) return rc; return rc; } -static int32_t mt9p012_lens_shading_enable(uint8_t is_enable) +static int mt9p012_lens_shading_enable(uint8_t is_enable) { - int32_t rc = 0; + int rc = 0; CDBG("%s: entered. enable = %d\n", __func__, is_enable); rc = mt9p012_i2c_write_w(mt9p012_client->addr, - REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD); + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); if (rc < 0) return rc; rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x3780, - ((uint16_t) is_enable) << 15); + ((uint16_t) is_enable) << 15); if (rc < 0) return rc; rc = mt9p012_i2c_write_w(mt9p012_client->addr, - REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE); + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); CDBG("%s: exiting. rc = %d\n", __func__, rc); return rc; } -static int32_t mt9p012_set_lc(void) +static int mt9p012_set_lc(void) { - int32_t rc; + int rc; - rc = mt9p012_i2c_write_w_table(mt9p012_regs.lctbl, mt9p012_regs.lctbl_size); + rc = mt9p012_i2c_write_w_table(mt9p012_regs.lctbl, + mt9p012_regs.lctbl_size); if (rc < 0) return rc; - rc = mt9p012_i2c_write_w_table(mt9p012_regs.rftbl, mt9p012_regs.rftbl_size); + rc = mt9p012_i2c_write_w_table(mt9p012_regs.rftbl, + mt9p012_regs.rftbl_size); return rc; } @@ -332,28 +340,31 @@ static int32_t mt9p012_set_lc(void) static void mt9p012_get_pict_fps(uint16_t fps, uint16_t *pfps) { /* input fps is preview fps in Q8 format */ - uint32_t divider; /*Q10 */ - uint32_t pclk_mult; /*Q10 */ + uint32_t divider; /*Q10 */ + uint32_t pclk_mult; /*Q10 */ if (mt9p012_ctrl->prev_res == QTR_SIZE) { divider = (uint32_t) - (((mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines * - mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck) * 0x00000400) / - (mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines * - mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck)); + (((mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines * + mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck) * + 0x00000400) / + (mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines * + mt9p012_regs.reg_pat[RES_CAPTURE].line_length_pck)); pclk_mult = - (uint32_t) ((mt9p012_regs.reg_pat[RES_CAPTURE].pll_multiplier * - 0x00000400) / (mt9p012_regs.reg_pat[RES_PREVIEW].pll_multiplier)); + (uint32_t) ((mt9p012_regs.reg_pat[RES_CAPTURE]. + pll_multiplier * 0x00000400) / + (mt9p012_regs.reg_pat[RES_PREVIEW]. + pll_multiplier)); } else { /* full size resolution used for preview. */ - divider = 0x00000400; /*1.0 */ - pclk_mult = 0x00000400; /*1.0 */ + divider = 0x00000400; /*1.0 */ + pclk_mult = 0x00000400; /*1.0 */ } /* Verify PCLK settings and frame sizes. */ *pfps = (uint16_t) (fps * divider * pclk_mult / 0x00000400 / - 0x00000400); + 0x00000400); } static uint16_t mt9p012_get_prev_lines_pf(void) @@ -388,57 +399,54 @@ static uint32_t mt9p012_get_pict_max_exp_lc(void) if (mt9p012_ctrl->pict_res == QTR_SIZE) snapshot_lines_per_frame = - mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1; + mt9p012_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1; else snapshot_lines_per_frame = - mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1; + mt9p012_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1; return snapshot_lines_per_frame * 24; } -static int32_t mt9p012_set_fps(struct fps_cfg *fps) +static int mt9p012_set_fps(struct fps_cfg *fps) { /* input is new fps in Q10 format */ - int32_t rc = 0; + int rc = 0; mt9p012_ctrl->fps_divider = fps->fps_div; mt9p012_ctrl->pict_fps_divider = fps->pict_fps_div; - rc = - mt9p012_i2c_write_w(mt9p012_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_HOLD); + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); if (rc < 0) return -EBUSY; - rc = - mt9p012_i2c_write_w(mt9p012_client->addr, - REG_LINE_LENGTH_PCK, - (mt9p012_regs.reg_pat[RES_PREVIEW].line_length_pck * - fps->f_mult / 0x00000400)); + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_LINE_LENGTH_PCK, + (mt9p012_regs.reg_pat[RES_PREVIEW]. + line_length_pck * fps->f_mult / 0x00000400)); if (rc < 0) return rc; - rc = - mt9p012_i2c_write_w(mt9p012_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_UPDATE); + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); return rc; } -static int32_t mt9p012_write_exp_gain(uint16_t gain, uint32_t line) +static int mt9p012_write_exp_gain(uint16_t gain, uint32_t line) { uint16_t max_legal_gain = 0x01FF; uint32_t line_length_ratio = 0x00000400; enum mt9p012_setting setting; - int32_t rc = 0; + int rc = 0; CDBG("Line:%d mt9p012_write_exp_gain \n", __LINE__); if (mt9p012_ctrl->sensormode == SENSOR_PREVIEW_MODE) { mt9p012_ctrl->my_reg_gain = gain; - mt9p012_ctrl->my_reg_line_count = (uint16_t)line; + mt9p012_ctrl->my_reg_line_count = (uint16_t) line; } if (gain > max_legal_gain) { @@ -448,12 +456,12 @@ static int32_t mt9p012_write_exp_gain(uint16_t gain, uint32_t line) /* Verify no overflow */ if (mt9p012_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) { - line = (uint32_t)(line * mt9p012_ctrl->fps_divider / - 0x00000400); + line = (uint32_t) (line * mt9p012_ctrl->fps_divider / + 0x00000400); setting = RES_PREVIEW; } else { - line = (uint32_t)(line * mt9p012_ctrl->pict_fps_divider / - 0x00000400); + line = (uint32_t) (line * mt9p012_ctrl->pict_fps_divider / + 0x00000400); setting = RES_CAPTURE; } @@ -466,32 +474,26 @@ static int32_t mt9p012_write_exp_gain(uint16_t gain, uint32_t line) if ((mt9p012_regs.reg_pat[setting].frame_length_lines - 1) < line) { line_length_ratio = (uint32_t) (line * 0x00000400) / - (mt9p012_regs.reg_pat[setting].frame_length_lines - 1); + (mt9p012_regs.reg_pat[setting].frame_length_lines - 1); } else line_length_ratio = 0x00000400; - rc = - mt9p012_i2c_write_w(mt9p012_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_HOLD); + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); if (rc < 0) { CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__); return rc; } - rc = - mt9p012_i2c_write_w( - mt9p012_client->addr, - REG_GLOBAL_GAIN, gain); + rc = mt9p012_i2c_write_w(mt9p012_client->addr, REG_GLOBAL_GAIN, gain); if (rc < 0) { CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__); return rc; } - rc = - mt9p012_i2c_write_w(mt9p012_client->addr, - REG_COARSE_INT_TIME, - line); + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_COARSE_INT_TIME, line); if (rc < 0) { CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__); return rc; @@ -499,34 +501,30 @@ static int32_t mt9p012_write_exp_gain(uint16_t gain, uint32_t line) CDBG("mt9p012_write_exp_gain: gain = %d, line = %d\n", gain, line); - rc = - mt9p012_i2c_write_w(mt9p012_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_UPDATE); + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); if (rc < 0) CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__); return rc; } -static int32_t mt9p012_set_pict_exp_gain(uint16_t gain, uint32_t line) +static int mt9p012_set_pict_exp_gain(uint16_t gain, uint32_t line) { - int32_t rc = 0; + int rc = 0; CDBG("Line:%d mt9p012_set_pict_exp_gain \n", __LINE__); - rc = - mt9p012_write_exp_gain(gain, line); + rc = mt9p012_write_exp_gain(gain, line); if (rc < 0) { CDBG("Line:%d mt9p012_set_pict_exp_gain failed... \n", - __LINE__); + __LINE__); return rc; } - rc = - mt9p012_i2c_write_w(mt9p012_client->addr, - MT9P012_REG_RESET_REGISTER, - 0x10CC | 0x0002); + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + MT9P012_REG_RESET_REGISTER, 0x10CC | 0x0002); if (rc < 0) { CDBG("mt9p012_i2c_write_w failed... Line:%d \n", __LINE__); return rc; @@ -538,193 +536,237 @@ static int32_t mt9p012_set_pict_exp_gain(uint16_t gain, uint32_t line) return rc; } -static int32_t mt9p012_setting(enum mt9p012_reg_update rupdate, - enum mt9p012_setting rt) +static int mt9p012_setting(enum mt9p012_reg_update rupdate, + enum mt9p012_setting rt) { - int32_t rc = 0; + int rc = 0; switch (rupdate) { case UPDATE_PERIODIC: - if (rt == RES_PREVIEW || rt == RES_CAPTURE) { - - struct mt9p012_i2c_reg_conf ppc_tbl[] = { - {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD}, - {REG_ROW_SPEED, mt9p012_regs.reg_pat[rt].row_speed}, - {REG_X_ADDR_START, mt9p012_regs.reg_pat[rt].x_addr_start}, - {REG_X_ADDR_END, mt9p012_regs.reg_pat[rt].x_addr_end}, - {REG_Y_ADDR_START, mt9p012_regs.reg_pat[rt].y_addr_start}, - {REG_Y_ADDR_END, mt9p012_regs.reg_pat[rt].y_addr_end}, - {REG_READ_MODE, mt9p012_regs.reg_pat[rt].read_mode}, - {REG_SCALE_M, mt9p012_regs.reg_pat[rt].scale_m}, - {REG_X_OUTPUT_SIZE, mt9p012_regs.reg_pat[rt].x_output_size}, - {REG_Y_OUTPUT_SIZE, mt9p012_regs.reg_pat[rt].y_output_size}, - - {REG_LINE_LENGTH_PCK, mt9p012_regs.reg_pat[rt].line_length_pck}, - {REG_FRAME_LENGTH_LINES, - (mt9p012_regs.reg_pat[rt].frame_length_lines * - mt9p012_ctrl->fps_divider / 0x00000400)}, - {REG_COARSE_INT_TIME, mt9p012_regs.reg_pat[rt].coarse_int_time}, - {REG_FINE_INTEGRATION_TIME, mt9p012_regs.reg_pat[rt].fine_int_time}, - {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE}, - }; - - rc = mt9p012_i2c_write_w_table(&ppc_tbl[0], - ARRAY_SIZE(ppc_tbl)); - if (rc < 0) - return rc; + if (rt == RES_PREVIEW || rt == RES_CAPTURE) { + + struct mt9p012_i2c_reg_conf ppc_tbl[] = { + {REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD}, + {REG_ROW_SPEED, + mt9p012_regs.reg_pat[rt].row_speed}, + {REG_X_ADDR_START, + mt9p012_regs.reg_pat[rt].x_addr_start}, + {REG_X_ADDR_END, + mt9p012_regs.reg_pat[rt].x_addr_end}, + {REG_Y_ADDR_START, + mt9p012_regs.reg_pat[rt].y_addr_start}, + {REG_Y_ADDR_END, + mt9p012_regs.reg_pat[rt].y_addr_end}, + {REG_READ_MODE, + mt9p012_regs.reg_pat[rt].read_mode}, + {REG_SCALE_M, mt9p012_regs.reg_pat[rt].scale_m}, + {REG_X_OUTPUT_SIZE, + mt9p012_regs.reg_pat[rt].x_output_size}, + {REG_Y_OUTPUT_SIZE, + mt9p012_regs.reg_pat[rt].y_output_size}, + + {REG_LINE_LENGTH_PCK, + mt9p012_regs.reg_pat[rt].line_length_pck}, + {REG_FRAME_LENGTH_LINES, + (mt9p012_regs.reg_pat[rt].frame_length_lines * + mt9p012_ctrl->fps_divider / 0x00000400)}, + {REG_COARSE_INT_TIME, + mt9p012_regs.reg_pat[rt].coarse_int_time}, + {REG_FINE_INTEGRATION_TIME, + mt9p012_regs.reg_pat[rt].fine_int_time}, + {REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE}, + }; + + rc = mt9p012_i2c_write_w_table(&ppc_tbl[0], + ARRAY_SIZE(ppc_tbl)); + if (rc < 0) + return rc; + + rc = mt9p012_test(mt9p012_ctrl->set_test); + if (rc < 0) + return rc; + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + MT9P012_REG_RESET_REGISTER, + MT9P012_RESET_REGISTER_PWON | + 0x0002); + if (rc < 0) + return rc; + + mdelay(5); /* 15? wait for sensor to transition */ - rc = mt9p012_test(mt9p012_ctrl->set_test); - if (rc < 0) - return rc; - - rc = - mt9p012_i2c_write_w(mt9p012_client->addr, - MT9P012_REG_RESET_REGISTER, - MT9P012_RESET_REGISTER_PWON | 0x0002); - if (rc < 0) return rc; - - mdelay(5); /* 15? wait for sensor to transition*/ - - return rc; - } - break; /* UPDATE_PERIODIC */ + } + break; /* UPDATE_PERIODIC */ case REG_INIT: - if (rt == RES_PREVIEW || rt == RES_CAPTURE) { - struct mt9p012_i2c_reg_conf ipc_tbl1[] = { - {MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWOFF}, - {REG_VT_PIX_CLK_DIV, mt9p012_regs.reg_pat[rt].vt_pix_clk_div}, - {REG_VT_SYS_CLK_DIV, mt9p012_regs.reg_pat[rt].vt_sys_clk_div}, - {REG_PRE_PLL_CLK_DIV, mt9p012_regs.reg_pat[rt].pre_pll_clk_div}, - {REG_PLL_MULTIPLIER, mt9p012_regs.reg_pat[rt].pll_multiplier}, - {REG_OP_PIX_CLK_DIV, mt9p012_regs.reg_pat[rt].op_pix_clk_div}, - {REG_OP_SYS_CLK_DIV, mt9p012_regs.reg_pat[rt].op_sys_clk_div}, + if (rt == RES_PREVIEW || rt == RES_CAPTURE) { + struct mt9p012_i2c_reg_conf ipc_tbl1[] = { + {MT9P012_REG_RESET_REGISTER, + MT9P012_RESET_REGISTER_PWOFF}, + {REG_VT_PIX_CLK_DIV, + mt9p012_regs.reg_pat[rt].vt_pix_clk_div}, + {REG_VT_SYS_CLK_DIV, + mt9p012_regs.reg_pat[rt].vt_sys_clk_div}, + {REG_PRE_PLL_CLK_DIV, + mt9p012_regs.reg_pat[rt].pre_pll_clk_div}, + {REG_PLL_MULTIPLIER, + mt9p012_regs.reg_pat[rt].pll_multiplier}, + {REG_OP_PIX_CLK_DIV, + mt9p012_regs.reg_pat[rt].op_pix_clk_div}, + {REG_OP_SYS_CLK_DIV, + mt9p012_regs.reg_pat[rt].op_sys_clk_div}, #ifdef MT9P012_REV_7 - {0x30B0, 0x0001}, - {0x308E, 0xE060}, - {0x3092, 0x0A52}, - {0x3094, 0x4656}, - {0x3096, 0x5652}, - {0x30CA, 0x8006}, - {0x312A, 0xDD02}, - {0x312C, 0x00E4}, - {0x3170, 0x299A}, + {0x30B0, 0x0001}, + {0x308E, 0xE060}, + {0x3092, 0x0A52}, + {0x3094, 0x4656}, + {0x3096, 0x5652}, + {0x30CA, 0x8006}, + {0x312A, 0xDD02}, + {0x312C, 0x00E4}, + {0x3170, 0x299A}, #endif - /* optimized settings for noise */ - {0x3088, 0x6FF6}, - {0x3154, 0x0282}, - {0x3156, 0x0381}, - {0x3162, 0x04CE}, - {0x0204, 0x0010}, - {0x0206, 0x0010}, - {0x0208, 0x0010}, - {0x020A, 0x0010}, - {0x020C, 0x0010}, - {MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWON}, - }; - - struct mt9p012_i2c_reg_conf ipc_tbl2[] = { - {MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWOFF}, - {REG_VT_PIX_CLK_DIV, mt9p012_regs.reg_pat[rt].vt_pix_clk_div}, - {REG_VT_SYS_CLK_DIV, mt9p012_regs.reg_pat[rt].vt_sys_clk_div}, - {REG_PRE_PLL_CLK_DIV, mt9p012_regs.reg_pat[rt].pre_pll_clk_div}, - {REG_PLL_MULTIPLIER, mt9p012_regs.reg_pat[rt].pll_multiplier}, - {REG_OP_PIX_CLK_DIV, mt9p012_regs.reg_pat[rt].op_pix_clk_div}, - {REG_OP_SYS_CLK_DIV, mt9p012_regs.reg_pat[rt].op_sys_clk_div}, + /* optimized settings for noise */ + {0x3088, 0x6FF6}, + {0x3154, 0x0282}, + {0x3156, 0x0381}, + {0x3162, 0x04CE}, + {0x0204, 0x0010}, + {0x0206, 0x0010}, + {0x0208, 0x0010}, + {0x020A, 0x0010}, + {0x020C, 0x0010}, + {MT9P012_REG_RESET_REGISTER, + MT9P012_RESET_REGISTER_PWON}, + }; + + struct mt9p012_i2c_reg_conf ipc_tbl2[] = { + {MT9P012_REG_RESET_REGISTER, + MT9P012_RESET_REGISTER_PWOFF}, + {REG_VT_PIX_CLK_DIV, + mt9p012_regs.reg_pat[rt].vt_pix_clk_div}, + {REG_VT_SYS_CLK_DIV, + mt9p012_regs.reg_pat[rt].vt_sys_clk_div}, + {REG_PRE_PLL_CLK_DIV, + mt9p012_regs.reg_pat[rt].pre_pll_clk_div}, + {REG_PLL_MULTIPLIER, + mt9p012_regs.reg_pat[rt].pll_multiplier}, + {REG_OP_PIX_CLK_DIV, + mt9p012_regs.reg_pat[rt].op_pix_clk_div}, + {REG_OP_SYS_CLK_DIV, + mt9p012_regs.reg_pat[rt].op_sys_clk_div}, #ifdef MT9P012_REV_7 - {0x30B0, 0x0001}, - {0x308E, 0xE060}, - {0x3092, 0x0A52}, - {0x3094, 0x4656}, - {0x3096, 0x5652}, - {0x30CA, 0x8006}, - {0x312A, 0xDD02}, - {0x312C, 0x00E4}, - {0x3170, 0x299A}, + {0x30B0, 0x0001}, + {0x308E, 0xE060}, + {0x3092, 0x0A52}, + {0x3094, 0x4656}, + {0x3096, 0x5652}, + {0x30CA, 0x8006}, + {0x312A, 0xDD02}, + {0x312C, 0x00E4}, + {0x3170, 0x299A}, #endif - /* optimized settings for noise */ - {0x3088, 0x6FF6}, - {0x3154, 0x0282}, - {0x3156, 0x0381}, - {0x3162, 0x04CE}, - {0x0204, 0x0010}, - {0x0206, 0x0010}, - {0x0208, 0x0010}, - {0x020A, 0x0010}, - {0x020C, 0x0010}, - {MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWON}, - }; - - struct mt9p012_i2c_reg_conf ipc_tbl3[] = { - {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD}, - /* Set preview or snapshot mode */ - {REG_ROW_SPEED, mt9p012_regs.reg_pat[rt].row_speed}, - {REG_X_ADDR_START, mt9p012_regs.reg_pat[rt].x_addr_start}, - {REG_X_ADDR_END, mt9p012_regs.reg_pat[rt].x_addr_end}, - {REG_Y_ADDR_START, mt9p012_regs.reg_pat[rt].y_addr_start}, - {REG_Y_ADDR_END, mt9p012_regs.reg_pat[rt].y_addr_end}, - {REG_READ_MODE, mt9p012_regs.reg_pat[rt].read_mode}, - {REG_SCALE_M, mt9p012_regs.reg_pat[rt].scale_m}, - {REG_X_OUTPUT_SIZE, mt9p012_regs.reg_pat[rt].x_output_size}, - {REG_Y_OUTPUT_SIZE, mt9p012_regs.reg_pat[rt].y_output_size}, - {REG_LINE_LENGTH_PCK, mt9p012_regs.reg_pat[rt].line_length_pck}, - {REG_FRAME_LENGTH_LINES, - mt9p012_regs.reg_pat[rt].frame_length_lines}, - {REG_COARSE_INT_TIME, mt9p012_regs.reg_pat[rt].coarse_int_time}, - {REG_FINE_INTEGRATION_TIME, mt9p012_regs.reg_pat[rt].fine_int_time}, - {REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE}, - }; - - /* reset fps_divider */ - mt9p012_ctrl->fps_divider = 1 * 0x0400; - - rc = mt9p012_i2c_write_w_table(&ipc_tbl1[0], - ARRAY_SIZE(ipc_tbl1)); - if (rc < 0) - return rc; - - rc = mt9p012_i2c_write_w_table(&ipc_tbl2[0], - ARRAY_SIZE(ipc_tbl2)); - if (rc < 0) - return rc; - - mdelay(5); - - rc = mt9p012_i2c_write_w_table(&ipc_tbl3[0], - ARRAY_SIZE(ipc_tbl3)); - if (rc < 0) - return rc; - - /* load lens shading */ - rc = mt9p012_i2c_write_w(mt9p012_client->addr, - REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_HOLD); - if (rc < 0) - return rc; - - rc = mt9p012_set_lc(); - if (rc < 0) - return rc; - - rc = mt9p012_i2c_write_w(mt9p012_client->addr, - REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE); - - if (rc < 0) - return rc; - } - break; /* case REG_INIT: */ + /* optimized settings for noise */ + {0x3088, 0x6FF6}, + {0x3154, 0x0282}, + {0x3156, 0x0381}, + {0x3162, 0x04CE}, + {0x0204, 0x0010}, + {0x0206, 0x0010}, + {0x0208, 0x0010}, + {0x020A, 0x0010}, + {0x020C, 0x0010}, + {MT9P012_REG_RESET_REGISTER, + MT9P012_RESET_REGISTER_PWON}, + }; + + struct mt9p012_i2c_reg_conf ipc_tbl3[] = { + {REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD}, + /* Set preview or snapshot mode */ + {REG_ROW_SPEED, + mt9p012_regs.reg_pat[rt].row_speed}, + {REG_X_ADDR_START, + mt9p012_regs.reg_pat[rt].x_addr_start}, + {REG_X_ADDR_END, + mt9p012_regs.reg_pat[rt].x_addr_end}, + {REG_Y_ADDR_START, + mt9p012_regs.reg_pat[rt].y_addr_start}, + {REG_Y_ADDR_END, + mt9p012_regs.reg_pat[rt].y_addr_end}, + {REG_READ_MODE, + mt9p012_regs.reg_pat[rt].read_mode}, + {REG_SCALE_M, mt9p012_regs.reg_pat[rt].scale_m}, + {REG_X_OUTPUT_SIZE, + mt9p012_regs.reg_pat[rt].x_output_size}, + {REG_Y_OUTPUT_SIZE, + mt9p012_regs.reg_pat[rt].y_output_size}, + {REG_LINE_LENGTH_PCK, + mt9p012_regs.reg_pat[rt].line_length_pck}, + {REG_FRAME_LENGTH_LINES, + mt9p012_regs.reg_pat[rt].frame_length_lines}, + {REG_COARSE_INT_TIME, + mt9p012_regs.reg_pat[rt].coarse_int_time}, + {REG_FINE_INTEGRATION_TIME, + mt9p012_regs.reg_pat[rt].fine_int_time}, + {REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE}, + }; + + /* reset fps_divider */ + mt9p012_ctrl->fps_divider = 1 * 0x0400; + + rc = mt9p012_i2c_write_w_table(&ipc_tbl1[0], + ARRAY_SIZE(ipc_tbl1)); + if (rc < 0) + return rc; + + rc = mt9p012_i2c_write_w_table(&ipc_tbl2[0], + ARRAY_SIZE(ipc_tbl2)); + if (rc < 0) + return rc; + + mdelay(5); + + rc = mt9p012_i2c_write_w_table(&ipc_tbl3[0], + ARRAY_SIZE(ipc_tbl3)); + if (rc < 0) + return rc; + + /* load lens shading */ + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + rc = mt9p012_set_lc(); + if (rc < 0) + return rc; + + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + + if (rc < 0) + return rc; + } + break; /* case REG_INIT: */ default: rc = -EINVAL; break; - } /* switch (rupdate) */ + } /* switch (rupdate) */ return rc; } -static int32_t mt9p012_video_config(int mode, int res) +static int mt9p012_video_config(int mode, int res) { - int32_t rc; + int rc; switch (res) { case QTR_SIZE: @@ -736,8 +778,7 @@ static int32_t mt9p012_video_config(int mode, int res) break; case FULL_SIZE: - rc = - mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE); + rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE); if (rc < 0) return rc; @@ -745,27 +786,24 @@ static int32_t mt9p012_video_config(int mode, int res) default: return 0; - } /* switch */ + } /* switch */ mt9p012_ctrl->prev_res = res; mt9p012_ctrl->curr_res = res; mt9p012_ctrl->sensormode = mode; - rc = - mt9p012_write_exp_gain(mt9p012_ctrl->my_reg_gain, - mt9p012_ctrl->my_reg_line_count); + rc = mt9p012_write_exp_gain(mt9p012_ctrl->my_reg_gain, + mt9p012_ctrl->my_reg_line_count); - rc = - mt9p012_i2c_write_w(mt9p012_client->addr, - MT9P012_REG_RESET_REGISTER, - 0x10cc|0x0002); + rc = mt9p012_i2c_write_w(mt9p012_client->addr, + MT9P012_REG_RESET_REGISTER, 0x10cc | 0x0002); return rc; } -static int32_t mt9p012_snapshot_config(int mode) +static int mt9p012_snapshot_config(int mode) { - int32_t rc = 0; + int rc = 0; rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE); if (rc < 0) @@ -778,9 +816,9 @@ static int32_t mt9p012_snapshot_config(int mode) return rc; } -static int32_t mt9p012_raw_snapshot_config(int mode) +static int mt9p012_raw_snapshot_config(int mode) { - int32_t rc = 0; + int rc = 0; rc = mt9p012_setting(UPDATE_PERIODIC, RES_CAPTURE); if (rc < 0) @@ -793,19 +831,19 @@ static int32_t mt9p012_raw_snapshot_config(int mode) return rc; } -static int32_t mt9p012_power_down(void) +static int mt9p012_power_down(void) { - int32_t rc = 0; + int rc = 0; rc = mt9p012_i2c_write_w(mt9p012_client->addr, - MT9P012_REG_RESET_REGISTER, - MT9P012_RESET_REGISTER_PWOFF); + MT9P012_REG_RESET_REGISTER, + MT9P012_RESET_REGISTER_PWOFF); mdelay(5); return rc; } -static int32_t mt9p012_move_focus(int direction, int32_t num_steps) +static int mt9p012_move_focus(int direction, int num_steps) { int16_t step_direction; int16_t actual_step; @@ -820,20 +858,19 @@ static int32_t mt9p012_move_focus(int direction, int32_t num_steps) } if (direction == MOVE_NEAR) - step_direction = 16; /* 10bit */ + step_direction = 16; /* 10bit */ else if (direction == MOVE_FAR) - step_direction = -16; /* 10 bit */ + step_direction = -16; /* 10 bit */ else { CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__); return -EINVAL; } if (mt9p012_ctrl->curr_lens_pos < mt9p012_ctrl->init_curr_lens_pos) - mt9p012_ctrl->curr_lens_pos = - mt9p012_ctrl->init_curr_lens_pos; + mt9p012_ctrl->curr_lens_pos = mt9p012_ctrl->init_curr_lens_pos; - actual_step = (int16_t)(step_direction * (int16_t)num_steps); - next_position = (int16_t)(mt9p012_ctrl->curr_lens_pos + actual_step); + actual_step = (int16_t) (step_direction * (int16_t) num_steps); + next_position = (int16_t) (mt9p012_ctrl->curr_lens_pos + actual_step); if (next_position > 1023) next_position = 1023; @@ -846,7 +883,7 @@ static int32_t mt9p012_move_focus(int direction, int32_t num_steps) /* Writing the digital code for current to the actuator */ if (mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, - code_val_msb, code_val_lsb) < 0) { + code_val_msb, code_val_lsb) < 0) { CDBG("mt9p012_move_focus failed at line %d ...\n", __LINE__); return -EBUSY; } @@ -857,9 +894,9 @@ static int32_t mt9p012_move_focus(int direction, int32_t num_steps) return 0; } -static int32_t mt9p012_set_default_focus(void) +static int mt9p012_set_default_focus(void) { - int32_t rc = 0; + int rc = 0; uint8_t code_val_msb, code_val_lsb; code_val_msb = 0x00; @@ -867,7 +904,7 @@ static int32_t mt9p012_set_default_focus(void) /* Write the digital code for current to the actuator */ rc = mt9p012_i2c_write_b(MT9P012_AF_I2C_ADDR >> 1, - code_val_msb, code_val_lsb); + code_val_msb, code_val_lsb); mt9p012_ctrl->curr_lens_pos = 0; mt9p012_ctrl->init_curr_lens_pos = 0; @@ -884,7 +921,7 @@ static int mt9p012_probe_init_done(const struct msm_camera_sensor_info *data) static int mt9p012_probe_init_sensor(const struct msm_camera_sensor_info *data) { - int32_t rc; + int rc; uint16_t chipid; rc = gpio_request(data->sensor_reset, "mt9p012"); @@ -898,7 +935,7 @@ static int mt9p012_probe_init_sensor(const struct msm_camera_sensor_info *data) /* RESET the sensor image part via I2C command */ CDBG("mt9p012_sensor_init(): reseting sensor.\n"); rc = mt9p012_i2c_write_w(mt9p012_client->addr, - MT9P012_REG_RESET_REGISTER, 0x10CC|0x0001); + MT9P012_REG_RESET_REGISTER, 0x10CC | 0x0001); if (rc < 0) { CDBG("sensor reset failed. rc = %d\n", rc); goto init_probe_fail; @@ -908,7 +945,7 @@ static int mt9p012_probe_init_sensor(const struct msm_camera_sensor_info *data) /* 3. Read sensor Model ID: */ rc = mt9p012_i2c_read_w(mt9p012_client->addr, - MT9P012_REG_MODEL_ID, &chipid); + MT9P012_REG_MODEL_ID, &chipid); if (rc < 0) goto init_probe_fail; @@ -934,8 +971,7 @@ static int mt9p012_probe_init_sensor(const struct msm_camera_sensor_info *data) } /* To disable the 2 extra lines */ - rc = mt9p012_i2c_write_w(mt9p012_client->addr, - 0x3064, 0x0805); + rc = mt9p012_i2c_write_w(mt9p012_client->addr, 0x3064, 0x0805); if (rc < 0) { CDBG("disable the 2 extra lines failed. rc = %d\n", rc); @@ -953,7 +989,7 @@ static int mt9p012_probe_init_sensor(const struct msm_camera_sensor_info *data) static int mt9p012_sensor_open_init(const struct msm_camera_sensor_info *data) { - int32_t rc; + int rc; mt9p012_ctrl = kzalloc(sizeof(struct mt9p012_ctrl), GFP_KERNEL); if (!mt9p012_ctrl) { @@ -995,7 +1031,8 @@ static int mt9p012_sensor_open_init(const struct msm_camera_sensor_info *data) /* sensor : output enable */ CDBG("mt9p012_sensor_open_init(): enabling output.\n"); rc = mt9p012_i2c_write_w(mt9p012_client->addr, - MT9P012_REG_RESET_REGISTER, MT9P012_RESET_REGISTER_PWON); + MT9P012_REG_RESET_REGISTER, + MT9P012_RESET_REGISTER_PWON); if (rc < 0) { CDBG("sensor output enable failed. rc = %d\n", rc); goto init_fail1; @@ -1004,7 +1041,7 @@ static int mt9p012_sensor_open_init(const struct msm_camera_sensor_info *data) /* TODO: enable AF actuator */ #if 0 CDBG("enable AF actuator, gpio = %d\n", - mt9p012_ctrl->sensordata->vcm_pwd); + mt9p012_ctrl->sensordata->vcm_pwd); rc = gpio_request(mt9p012_ctrl->sensordata->vcm_pwd, "mt9p012"); if (!rc) gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd, 1); @@ -1036,9 +1073,9 @@ static int mt9p012_init_client(struct i2c_client *client) return 0; } -static int32_t mt9p012_set_sensor_mode(int mode, int res) +static int mt9p012_set_sensor_mode(int mode, int res) { - int32_t rc = 0; + int rc = 0; switch (mode) { case SENSOR_PREVIEW_MODE: @@ -1067,20 +1104,17 @@ int mt9p012_sensor_config(void __user *argp) int rc = 0; if (copy_from_user(&cdata, - (void *)argp, - sizeof(struct sensor_cfg_data))) + (void *)argp, sizeof(struct sensor_cfg_data))) return -EFAULT; - down(&mt9p012_sem); - - CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype); + CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype); switch (cdata.cfgtype) { case CFG_GET_PICT_FPS: mt9p012_get_pict_fps(cdata.cfg.gfps.prevfps, - &(cdata.cfg.gfps.pictfps)); + &(cdata.cfg.gfps.pictfps)); if (copy_to_user((void *)argp, &cdata, - sizeof(struct sensor_cfg_data))) + sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; @@ -1088,8 +1122,7 @@ int mt9p012_sensor_config(void __user *argp) cdata.cfg.prevl_pf = mt9p012_get_prev_lines_pf(); if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; @@ -1097,8 +1130,7 @@ int mt9p012_sensor_config(void __user *argp) cdata.cfg.prevp_pl = mt9p012_get_prev_pixels_pl(); if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; @@ -1106,8 +1138,7 @@ int mt9p012_sensor_config(void __user *argp) cdata.cfg.pictl_pf = mt9p012_get_pict_lines_pf(); if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; @@ -1115,18 +1146,15 @@ int mt9p012_sensor_config(void __user *argp) cdata.cfg.pictp_pl = mt9p012_get_pict_pixels_pl(); if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; case CFG_GET_PICT_MAX_EXP_LC: - cdata.cfg.pict_max_exp_lc = - mt9p012_get_pict_max_exp_lc(); + cdata.cfg.pict_max_exp_lc = mt9p012_get_pict_max_exp_lc(); if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; @@ -1137,13 +1165,13 @@ int mt9p012_sensor_config(void __user *argp) case CFG_SET_EXP_GAIN: rc = mt9p012_write_exp_gain(cdata.cfg.exp_gain.gain, - cdata.cfg.exp_gain.line); + cdata.cfg.exp_gain.line); break; case CFG_SET_PICT_EXP_GAIN: CDBG("Line:%d CFG_SET_PICT_EXP_GAIN \n", __LINE__); rc = mt9p012_set_pict_exp_gain(cdata.cfg.exp_gain.gain, - cdata.cfg.exp_gain.line); + cdata.cfg.exp_gain.line); break; case CFG_SET_MODE: @@ -1155,8 +1183,9 @@ int mt9p012_sensor_config(void __user *argp) break; case CFG_MOVE_FOCUS: - CDBG("mt9p012_ioctl: CFG_MOVE_FOCUS: cdata.cfg.focus.dir=%d cdata.cfg.focus.steps=%d\n", - cdata.cfg.focus.dir, cdata.cfg.focus.steps); + CDBG("mt9p012_ioctl: CFG_MOVE_FOCUS: cdata.cfg.focus.dir=%d " + "cdata.cfg.focus.steps=%d\n", + cdata.cfg.focus.dir, cdata.cfg.focus.steps); rc = mt9p012_move_focus(cdata.cfg.focus.dir, cdata.cfg.focus.steps); break; @@ -1173,8 +1202,7 @@ int mt9p012_sensor_config(void __user *argp) case CFG_GET_AF_MAX_STEPS: cdata.max_steps = MT9P012_STEPS_NEAR_TO_CLOSEST_INF; if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; @@ -1184,7 +1212,6 @@ int mt9p012_sensor_config(void __user *argp) break; } - up(&mt9p012_sem); return rc; } @@ -1192,12 +1219,9 @@ int mt9p012_sensor_release(void) { int rc = -EBADF; - down(&mt9p012_sem); - mt9p012_power_down(); - gpio_direction_output(mt9p012_ctrl->sensordata->sensor_reset, - 0); + gpio_direction_output(mt9p012_ctrl->sensordata->sensor_reset, 0); gpio_free(mt9p012_ctrl->sensordata->sensor_reset); gpio_direction_output(mt9p012_ctrl->sensordata->vcm_pwd, 0); @@ -1208,12 +1232,11 @@ int mt9p012_sensor_release(void) CDBG("mt9p012_release completed\n"); - up(&mt9p012_sem); return rc; } static int mt9p012_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { int rc = 0; CDBG("mt9p012_probe called!\n"); @@ -1245,17 +1268,17 @@ static int mt9p012_i2c_probe(struct i2c_client *client, } static const struct i2c_device_id mt9p012_i2c_id[] = { - { "mt9p012", 0}, - { } + {"mt9p012", 0}, + {} }; static struct i2c_driver mt9p012_i2c_driver = { .id_table = mt9p012_i2c_id, - .probe = mt9p012_i2c_probe, + .probe = mt9p012_i2c_probe, .remove = __exit_p(mt9p012_i2c_remove), .driver = { - .name = "mt9p012", - }, + .name = "mt9p012", + }, }; static int mt9p012_sensor_probe(const struct msm_camera_sensor_info *info, @@ -1276,7 +1299,7 @@ static int mt9p012_sensor_probe(const struct msm_camera_sensor_info *info, s->s_init = mt9p012_sensor_open_init; s->s_release = mt9p012_sensor_release; - s->s_config = mt9p012_sensor_config; + s->s_config = mt9p012_sensor_config; mt9p012_probe_init_done(info); probe_done: @@ -1292,9 +1315,9 @@ static int __mt9p012_probe(struct platform_device *pdev) static struct platform_driver msm_camera_driver = { .probe = __mt9p012_probe, .driver = { - .name = "msm_camera_mt9p012", - .owner = THIS_MODULE, - }, + .name = "msm_camera_mt9p012", + .owner = THIS_MODULE, + }, }; static int __init mt9p012_init(void) diff --git a/drivers/media/video/msm/mt9p012_reg.c b/drivers/media/video/msm/mt9p012_reg.c index e5223d693b2c2..713a0619fc81d 100644 --- a/drivers/media/video/msm/mt9p012_reg.c +++ b/drivers/media/video/msm/mt9p012_reg.c @@ -1,5 +1,19 @@ -/* - * Copyright (C) 2009 QUALCOMM Incorporated. +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ #include "mt9p012.h" @@ -7,127 +21,124 @@ /*Micron settings from Applications for lower power consumption.*/ struct reg_struct mt9p012_reg_pat[2] = { - { /* Preview */ - /* vt_pix_clk_div REG=0x0300 */ - 6, /* 5 */ + { /* Preview */ + /* vt_pix_clk_div REG=0x0300 */ + 6, /* 5 */ - /* vt_sys_clk_div REG=0x0302 */ - 1, + /* vt_sys_clk_div REG=0x0302 */ + 1, - /* pre_pll_clk_div REG=0x0304 */ - 2, + /* pre_pll_clk_div REG=0x0304 */ + 2, - /* pll_multiplier REG=0x0306 */ - 60, + /* pll_multiplier REG=0x0306 */ + 60, - /* op_pix_clk_div REG=0x0308 */ - 8, /* 10 */ + /* op_pix_clk_div REG=0x0308 */ + 8, /* 10 */ - /* op_sys_clk_div REG=0x030A */ - 1, + /* op_sys_clk_div REG=0x030A */ + 1, - /* scale_m REG=0x0404 */ - 16, + /* scale_m REG=0x0404 */ + 16, - /* row_speed REG=0x3016 */ - 0x0111, + /* row_speed REG=0x3016 */ + 0x0111, - /* x_addr_start REG=0x3004 */ - 8, + /* x_addr_start REG=0x3004 */ + 8, - /* x_addr_end REG=0x3008 */ - 2597, + /* x_addr_end REG=0x3008 */ + 2597, - /* y_addr_start REG=0x3002 */ - 8, + /* y_addr_start REG=0x3002 */ + 8, - /* y_addr_end REG=0x3006 */ - 1949, + /* y_addr_end REG=0x3006 */ + 1949, - /* read_mode REG=0x3040 - * Preview 2x2 skipping */ - 0x00C3, + /* read_mode REG=0x3040 + * Preview 2x2 skipping */ + 0x00C3, - /* x_output_size REG=0x034C */ - 1296, + /* x_output_size REG=0x034C */ + 1296, - /* y_output_size REG=0x034E */ - 972, + /* y_output_size REG=0x034E */ + 972, - /* line_length_pck REG=0x300C */ - 3784, + /* line_length_pck REG=0x300C */ + 3784, - /* frame_length_lines REG=0x300A */ - 1057, + /* frame_length_lines REG=0x300A */ + 1057, - /* coarse_integration_time REG=0x3012 */ - 16, + /* coarse_integration_time REG=0x3012 */ + 16, - /* fine_integration_time REG=0x3014 */ - 1764 - }, - { /*Snapshot*/ - /* vt_pix_clk_div REG=0x0300 */ - 6, + /* fine_integration_time REG=0x3014 */ + 1764}, + { /*Snapshot */ + /* vt_pix_clk_div REG=0x0300 */ + 6, - /* vt_sys_clk_div REG=0x0302 */ - 1, + /* vt_sys_clk_div REG=0x0302 */ + 1, - /* pre_pll_clk_div REG=0x0304 */ - 2, + /* pre_pll_clk_div REG=0x0304 */ + 2, - /* pll_multiplier REG=0x0306 - * 60 for 10fps snapshot */ - 60, + /* pll_multiplier REG=0x0306 + * 60 for 10fps snapshot */ + 60, - /* op_pix_clk_div REG=0x0308 */ - 8, + /* op_pix_clk_div REG=0x0308 */ + 8, - /* op_sys_clk_div REG=0x030A */ - 1, + /* op_sys_clk_div REG=0x030A */ + 1, - /* scale_m REG=0x0404 */ - 16, + /* scale_m REG=0x0404 */ + 16, - /* row_speed REG=0x3016 */ - 0x0111, + /* row_speed REG=0x3016 */ + 0x0111, - /* x_addr_start REG=0x3004 */ - 8, + /* x_addr_start REG=0x3004 */ + 8, - /* x_addr_end REG=0x3008 */ - 2615, + /* x_addr_end REG=0x3008 */ + 2615, - /* y_addr_start REG=0x3002 */ - 8, + /* y_addr_start REG=0x3002 */ + 8, - /* y_addr_end REG=0x3006 */ - 1967, + /* y_addr_end REG=0x3006 */ + 1967, - /* read_mode REG=0x3040 */ - 0x0041, + /* read_mode REG=0x3040 */ + 0x0041, - /* x_output_size REG=0x034C */ - 2608, + /* x_output_size REG=0x034C */ + 2608, - /* y_output_size REG=0x034E */ - 1960, + /* y_output_size REG=0x034E */ + 1960, - /* line_length_pck REG=0x300C */ - 3911, + /* line_length_pck REG=0x300C */ + 3911, - /* frame_length_lines REG=0x300A //10 fps snapshot */ - 2045, + /* frame_length_lines REG=0x300A //10 fps snapshot */ + 2045, - /* coarse_integration_time REG=0x3012 */ - 16, + /* coarse_integration_time REG=0x3012 */ + 16, - /* fine_integration_time REG=0x3014 */ - 882 - } + /* fine_integration_time REG=0x3014 */ + 882} }; - struct mt9p012_i2c_reg_conf mt9p012_test_tbl[] = { {0x3044, 0x0544 & 0xFBFF}, {0x30CA, 0x0004 | 0x0001}, @@ -139,7 +150,6 @@ struct mt9p012_i2c_reg_conf mt9p012_test_tbl[] = { {0x3780, 0x0000}, }; - struct mt9p012_i2c_reg_conf mt9p012_lc_tbl[] = { /* [Lens shading 85 Percent TL84] */ /* P_RD_P0Q0 */ @@ -558,7 +568,6 @@ struct mt9p012_i2c_reg_conf mt9p012_rolloff_tbl[] = { {0x3780, 0x8000}, }; - struct mt9p012_reg mt9p012_regs = { .reg_pat = &mt9p012_reg_pat[0], .reg_pat_size = ARRAY_SIZE(mt9p012_reg_pat), @@ -569,5 +578,3 @@ struct mt9p012_reg mt9p012_regs = { .rftbl = &mt9p012_rolloff_tbl[0], .rftbl_size = ARRAY_SIZE(mt9p012_rolloff_tbl) }; - - diff --git a/drivers/media/video/msm/mt9t013.c b/drivers/media/video/msm/mt9t013.c index fa18f2f1324fa..e77904e5d551f 100644 --- a/drivers/media/video/msm/mt9t013.c +++ b/drivers/media/video/msm/mt9t013.c @@ -1,5 +1,19 @@ -/* - * Copyright (C) 2008-2009 QUALCOMM Incorporated. +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ #include @@ -43,12 +57,11 @@ #define REG_ROW_SPEED 0x3016 #define MT9T013_REG_RESET_REGISTER 0x301A #define MT9T013_RESET_REGISTER_PWON 0x10CC -#define MT9T013_RESET_REGISTER_PWOFF 0x1008 /* 0x10C8 stop streaming*/ +#define MT9T013_RESET_REGISTER_PWOFF 0x1008 /* 0x10C8 stop streaming */ #define REG_READ_MODE 0x3040 #define REG_GLOBAL_GAIN 0x305E #define REG_TEST_PATTERN_MODE 0x3070 - enum mt9t013_test_mode { TEST_OFF, TEST_1, @@ -91,21 +104,20 @@ enum mt9t013_setting { #define MT9T013_DEFAULT_CLOCK_RATE 24000000 #define MT9T013_DEFAULT_MAX_FPS 26 - /* FIXME: Changes from here */ struct mt9t013_work { struct work_struct work; }; -static struct mt9t013_work *mt9t013_sensorw; -static struct i2c_client *mt9t013_client; +static struct mt9t013_work *mt9t013_sensorw; +static struct i2c_client *mt9t013_client; struct mt9t013_ctrl { const struct msm_camera_sensor_info *sensordata; int sensormode; - uint32_t fps_divider; /* init to 1 * 0x00000400 */ - uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */ + uint32_t fps_divider; /* init to 1 * 0x00000400 */ + uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */ uint16_t curr_lens_pos; uint16_t init_curr_lens_pos; @@ -115,34 +127,30 @@ struct mt9t013_ctrl { enum mt9t013_resolution prev_res; enum mt9t013_resolution pict_res; enum mt9t013_resolution curr_res; - enum mt9t013_test_mode set_test; + enum mt9t013_test_mode set_test; unsigned short imgaddr; }; - static struct mt9t013_ctrl *mt9t013_ctrl; static DECLARE_WAIT_QUEUE_HEAD(mt9t013_wait_queue); -DECLARE_MUTEX(mt9t013_sem); - -extern struct mt9t013_reg mt9t013_regs; /* from mt9t013_reg.c */ static int mt9t013_i2c_rxdata(unsigned short saddr, - unsigned char *rxdata, int length) + unsigned char *rxdata, int length) { struct i2c_msg msgs[] = { - { - .addr = saddr, - .flags = 0, - .len = 2, - .buf = rxdata, - }, - { - .addr = saddr, - .flags = I2C_M_RD, - .len = length, - .buf = rxdata, - }, + { + .addr = saddr, + .flags = 0, + .len = 2, + .buf = rxdata, + }, + { + .addr = saddr, + .flags = I2C_M_RD, + .len = length, + .buf = rxdata, + }, }; if (i2c_transfer(mt9t013_client->adapter, msgs, 2) < 0) { @@ -153,10 +161,10 @@ static int mt9t013_i2c_rxdata(unsigned short saddr, return 0; } -static int32_t mt9t013_i2c_read_w(unsigned short saddr, - unsigned short raddr, unsigned short *rdata) +static int mt9t013_i2c_read_w(unsigned short saddr, + unsigned short raddr, unsigned short *rdata) { - int32_t rc = 0; + int rc = 0; unsigned char buf[4]; if (!rdata) @@ -164,7 +172,7 @@ static int32_t mt9t013_i2c_read_w(unsigned short saddr, memset(buf, 0, sizeof(buf)); - buf[0] = (raddr & 0xFF00)>>8; + buf[0] = (raddr & 0xFF00) >> 8; buf[1] = (raddr & 0x00FF); rc = mt9t013_i2c_rxdata(saddr, buf, 2); @@ -179,16 +187,16 @@ static int32_t mt9t013_i2c_read_w(unsigned short saddr, return rc; } -static int32_t mt9t013_i2c_txdata(unsigned short saddr, - unsigned char *txdata, int length) +static int mt9t013_i2c_txdata(unsigned short saddr, + unsigned char *txdata, int length) { struct i2c_msg msg[] = { - { - .addr = saddr, - .flags = 0, - .len = length, - .buf = txdata, - }, + { + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, + }, }; if (i2c_transfer(mt9t013_client->adapter, msg, 1) < 0) { @@ -199,10 +207,10 @@ static int32_t mt9t013_i2c_txdata(unsigned short saddr, return 0; } -static int32_t mt9t013_i2c_write_b(unsigned short saddr, - unsigned short waddr, unsigned short wdata) +static int mt9t013_i2c_write_b(unsigned short saddr, + unsigned short waddr, unsigned short wdata) { - int32_t rc = -EIO; + int rc = -EIO; unsigned char buf[2]; memset(buf, 0, sizeof(buf)); @@ -212,41 +220,43 @@ static int32_t mt9t013_i2c_write_b(unsigned short saddr, if (rc < 0) pr_err("i2c_write failed, addr = 0x%x, val = 0x%x!\n", - waddr, wdata); + waddr, wdata); return rc; } -static int32_t mt9t013_i2c_write_w(unsigned short saddr, - unsigned short waddr, unsigned short wdata) +static int mt9t013_i2c_write_w(unsigned short saddr, + unsigned short waddr, unsigned short wdata) { - int32_t rc = -EIO; + int rc = -EIO; unsigned char buf[4]; memset(buf, 0, sizeof(buf)); - buf[0] = (waddr & 0xFF00)>>8; + buf[0] = (waddr & 0xFF00) >> 8; buf[1] = (waddr & 0x00FF); - buf[2] = (wdata & 0xFF00)>>8; + buf[2] = (wdata & 0xFF00) >> 8; buf[3] = (wdata & 0x00FF); rc = mt9t013_i2c_txdata(saddr, buf, 4); if (rc < 0) pr_err("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n", - waddr, wdata); + waddr, wdata); return rc; } -static int32_t mt9t013_i2c_write_w_table( - struct mt9t013_i2c_reg_conf *reg_conf_tbl, int num_of_items_in_table) +static int mt9t013_i2c_write_w_table(struct mt9t013_i2c_reg_conf + *reg_conf_tbl, + int num_of_items_in_table) { int i; - int32_t rc = -EIO; + int rc = -EIO; for (i = 0; i < num_of_items_in_table; i++) { rc = mt9t013_i2c_write_w(mt9t013_client->addr, - reg_conf_tbl->waddr, reg_conf_tbl->wdata); + reg_conf_tbl->waddr, + reg_conf_tbl->wdata); if (rc < 0) break; reg_conf_tbl++; @@ -255,13 +265,13 @@ static int32_t mt9t013_i2c_write_w_table( return rc; } -static int32_t mt9t013_test(enum mt9t013_test_mode mo) +static int mt9t013_test(enum mt9t013_test_mode mo) { - int32_t rc = 0; + int rc = 0; rc = mt9t013_i2c_write_w(mt9t013_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_HOLD); + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); if (rc < 0) return rc; @@ -269,45 +279,46 @@ static int32_t mt9t013_test(enum mt9t013_test_mode mo) return 0; else { rc = mt9t013_i2c_write_w_table(mt9t013_regs.ttbl, - mt9t013_regs.ttbl_size); + mt9t013_regs.ttbl_size); if (rc < 0) return rc; rc = mt9t013_i2c_write_w(mt9t013_client->addr, - REG_TEST_PATTERN_MODE, (uint16_t)mo); + REG_TEST_PATTERN_MODE, (uint16_t) mo); if (rc < 0) return rc; } rc = mt9t013_i2c_write_w(mt9t013_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_UPDATE); + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); if (rc < 0) return rc; return rc; } -static int32_t mt9t013_set_lc(void) +static int mt9t013_set_lc(void) { - int32_t rc; + int rc; - rc = mt9t013_i2c_write_w_table(mt9t013_regs.lctbl, mt9t013_regs.lctbl_size); + rc = mt9t013_i2c_write_w_table(mt9t013_regs.lctbl, + mt9t013_regs.lctbl_size); if (rc < 0) return rc; return rc; } -static int32_t mt9t013_set_default_focus(uint8_t af_step) +static int mt9t013_set_default_focus(uint8_t af_step) { - int32_t rc = 0; + int rc = 0; uint8_t code_val_msb, code_val_lsb; code_val_msb = 0x01; code_val_lsb = af_step; /* Write the digital code for current to the actuator */ - rc = mt9t013_i2c_write_b(MT9T013_AF_I2C_ADDR>>1, - code_val_msb, code_val_lsb); + rc = mt9t013_i2c_write_b(MT9T013_AF_I2C_ADDR >> 1, + code_val_msb, code_val_lsb); mt9t013_ctrl->curr_lens_pos = 0; mt9t013_ctrl->init_curr_lens_pos = 0; @@ -317,33 +328,35 @@ static int32_t mt9t013_set_default_focus(uint8_t af_step) static void mt9t013_get_pict_fps(uint16_t fps, uint16_t *pfps) { /* input fps is preview fps in Q8 format */ - uint32_t divider; /*Q10 */ - uint32_t pclk_mult; /*Q10 */ + uint32_t divider; /*Q10 */ + uint32_t pclk_mult; /*Q10 */ if (mt9t013_ctrl->prev_res == QTR_SIZE) { divider = - (uint32_t)( - ((mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines * - mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck) * - 0x00000400) / - (mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines * - mt9t013_regs.reg_pat[RES_CAPTURE].line_length_pck)); + (uint32_t) (((mt9t013_regs.reg_pat[RES_PREVIEW]. + frame_length_lines * + mt9t013_regs.reg_pat[RES_PREVIEW]. + line_length_pck) * 0x00000400) / + (mt9t013_regs.reg_pat[RES_CAPTURE]. + frame_length_lines * + mt9t013_regs.reg_pat[RES_CAPTURE]. + line_length_pck)); pclk_mult = - (uint32_t) ((mt9t013_regs.reg_pat[RES_CAPTURE].pll_multiplier * - 0x00000400) / - (mt9t013_regs.reg_pat[RES_PREVIEW].pll_multiplier)); + (uint32_t) ((mt9t013_regs.reg_pat[RES_CAPTURE]. + pll_multiplier * 0x00000400) / + (mt9t013_regs.reg_pat[RES_PREVIEW]. + pll_multiplier)); } else { /* full size resolution used for preview. */ - divider = 0x00000400; /*1.0 */ - pclk_mult = 0x00000400; /*1.0 */ + divider = 0x00000400; /*1.0 */ + pclk_mult = 0x00000400; /*1.0 */ } /* Verify PCLK settings and frame sizes. */ *pfps = - (uint16_t) (fps * divider * pclk_mult / - 0x00000400 / 0x00000400); + (uint16_t) (fps * divider * pclk_mult / 0x00000400 / 0x00000400); } static uint16_t mt9t013_get_prev_lines_pf(void) @@ -378,64 +391,62 @@ static uint32_t mt9t013_get_pict_max_exp_lc(void) if (mt9t013_ctrl->pict_res == QTR_SIZE) { snapshot_lines_per_frame = - mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1; - } else { + mt9t013_regs.reg_pat[RES_PREVIEW].frame_length_lines - 1; + } else { snapshot_lines_per_frame = - mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1; + mt9t013_regs.reg_pat[RES_CAPTURE].frame_length_lines - 1; } return snapshot_lines_per_frame * 24; } -static int32_t mt9t013_set_fps(struct fps_cfg *fps) +static int mt9t013_set_fps(struct fps_cfg *fps) { /* input is new fps in Q8 format */ - int32_t rc = 0; + int rc = 0; mt9t013_ctrl->fps_divider = fps->fps_div; mt9t013_ctrl->pict_fps_divider = fps->pict_fps_div; rc = mt9t013_i2c_write_w(mt9t013_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_HOLD); + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); if (rc < 0) return -EBUSY; CDBG("mt9t013_set_fps: fps_div is %d, frame_rate is %d\n", - fps->fps_div, - (uint16_t) (mt9t013_regs.reg_pat[RES_PREVIEW]. - frame_length_lines * - fps->fps_div/0x00000400)); + fps->fps_div, + (uint16_t) (mt9t013_regs.reg_pat[RES_PREVIEW]. + frame_length_lines * fps->fps_div / 0x00000400)); CDBG("mt9t013_set_fps: fps_mult is %d, frame_rate is %d\n", - fps->f_mult, - (uint16_t)(mt9t013_regs.reg_pat[RES_PREVIEW]. - line_length_pck * - fps->f_mult / 0x00000400)); + fps->f_mult, + (uint16_t) (mt9t013_regs.reg_pat[RES_PREVIEW]. + line_length_pck * fps->f_mult / 0x00000400)); rc = mt9t013_i2c_write_w(mt9t013_client->addr, - REG_LINE_LENGTH_PCK, - (uint16_t) ( - mt9t013_regs.reg_pat[RES_PREVIEW].line_length_pck * - fps->f_mult / 0x00000400)); + REG_LINE_LENGTH_PCK, + (uint16_t) (mt9t013_regs.reg_pat[RES_PREVIEW]. + line_length_pck * fps->f_mult / + 0x00000400)); if (rc < 0) return rc; rc = mt9t013_i2c_write_w(mt9t013_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_UPDATE); + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); if (rc < 0) return rc; return rc; } -static int32_t mt9t013_write_exp_gain(uint16_t gain, uint32_t line) +static int mt9t013_write_exp_gain(uint16_t gain, uint32_t line) { const uint16_t max_legal_gain = 0x01FF; uint32_t line_length_ratio = 0x00000400; enum mt9t013_setting setting; - int32_t rc = 0; + int rc = 0; if (mt9t013_ctrl->sensormode == SENSOR_PREVIEW_MODE) { mt9t013_ctrl->my_reg_gain = gain; @@ -448,13 +459,13 @@ static int32_t mt9t013_write_exp_gain(uint16_t gain, uint32_t line) /* Verify no overflow */ if (mt9t013_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) { line = (uint32_t) (line * mt9t013_ctrl->fps_divider / - 0x00000400); + 0x00000400); setting = RES_PREVIEW; } else { line = (uint32_t) (line * mt9t013_ctrl->pict_fps_divider / - 0x00000400); + 0x00000400); setting = RES_CAPTURE; } @@ -465,8 +476,8 @@ static int32_t mt9t013_write_exp_gain(uint16_t gain, uint32_t line) if ((mt9t013_regs.reg_pat[setting].frame_length_lines - 1) < line) { line_length_ratio = - (uint32_t) (line * 0x00000400) / - (mt9t013_regs.reg_pat[setting].frame_length_lines - 1); + (uint32_t) (line * 0x00000400) / + (mt9t013_regs.reg_pat[setting].frame_length_lines - 1); } else line_length_ratio = 0x00000400; @@ -479,25 +490,24 @@ static int32_t mt9t013_write_exp_gain(uint16_t gain, uint32_t line) return rc; rc = mt9t013_i2c_write_w(mt9t013_client->addr, - REG_COARSE_INT_TIME, - (uint16_t)((uint32_t) line )); + REG_COARSE_INT_TIME, + (uint16_t) ((uint32_t) line)); if (rc < 0) return rc; return rc; } -static int32_t mt9t013_set_pict_exp_gain(uint16_t gain, uint32_t line) +static int mt9t013_set_pict_exp_gain(uint16_t gain, uint32_t line) { - int32_t rc = 0; + int rc = 0; rc = mt9t013_write_exp_gain(gain, line); if (rc < 0) return rc; rc = mt9t013_i2c_write_w(mt9t013_client->addr, - MT9T013_REG_RESET_REGISTER, - 0x10CC | 0x0002); + MT9T013_REG_RESET_REGISTER, 0x10CC | 0x0002); mdelay(5); @@ -505,451 +515,476 @@ static int32_t mt9t013_set_pict_exp_gain(uint16_t gain, uint32_t line) return rc; } -static int32_t mt9t013_setting(enum mt9t013_reg_update rupdate, - enum mt9t013_setting rt) +static int mt9t013_setting(enum mt9t013_reg_update rupdate, + enum mt9t013_setting rt) { - int32_t rc = 0; + int rc = 0; switch (rupdate) { - case UPDATE_PERIODIC: { + case UPDATE_PERIODIC:{ - if (rt == RES_PREVIEW || rt == RES_CAPTURE) { + if (rt == RES_PREVIEW || rt == RES_CAPTURE) { #if 0 - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - MT9T013_REG_RESET_REGISTER, - MT9T013_RESET_REGISTER_PWOFF); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWOFF); + if (rc < 0) + return rc; #endif - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_VT_PIX_CLK_DIV, - mt9t013_regs.reg_pat[rt].vt_pix_clk_div); - if (rc < 0) - return rc; - - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_VT_SYS_CLK_DIV, - mt9t013_regs.reg_pat[rt].vt_sys_clk_div); - if (rc < 0) - return rc; - - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_PRE_PLL_CLK_DIV, - mt9t013_regs.reg_pat[rt].pre_pll_clk_div); - if (rc < 0) - return rc; - - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_PLL_MULTIPLIER, - mt9t013_regs.reg_pat[rt].pll_multiplier); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_VT_PIX_CLK_DIV, + mt9t013_regs. + reg_pat[rt]. + vt_pix_clk_div); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_OP_PIX_CLK_DIV, - mt9t013_regs.reg_pat[rt].op_pix_clk_div); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_VT_SYS_CLK_DIV, + mt9t013_regs. + reg_pat[rt]. + vt_sys_clk_div); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_OP_SYS_CLK_DIV, - mt9t013_regs.reg_pat[rt].op_sys_clk_div); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_PRE_PLL_CLK_DIV, + mt9t013_regs. + reg_pat[rt]. + pre_pll_clk_div); + if (rc < 0) + return rc; - mdelay(5); + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_PLL_MULTIPLIER, + mt9t013_regs. + reg_pat[rt]. + pll_multiplier); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_HOLD); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_OP_PIX_CLK_DIV, + mt9t013_regs. + reg_pat[rt]. + op_pix_clk_div); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_ROW_SPEED, - mt9t013_regs.reg_pat[rt].row_speed); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_OP_SYS_CLK_DIV, + mt9t013_regs. + reg_pat[rt]. + op_sys_clk_div); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_X_ADDR_START, - mt9t013_regs.reg_pat[rt].x_addr_start); - if (rc < 0) - return rc; + mdelay(5); - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_X_ADDR_END, - mt9t013_regs.reg_pat[rt].x_addr_end); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_Y_ADDR_START, - mt9t013_regs.reg_pat[rt].y_addr_start); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_ROW_SPEED, + mt9t013_regs. + reg_pat[rt].row_speed); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_Y_ADDR_END, - mt9t013_regs.reg_pat[rt].y_addr_end); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_X_ADDR_START, + mt9t013_regs. + reg_pat[rt]. + x_addr_start); + if (rc < 0) + return rc; - if (machine_is_sapphire()) { - if (rt == 0) rc = mt9t013_i2c_write_w(mt9t013_client->addr, - REG_READ_MODE, - 0x046F); - else + REG_X_ADDR_END, + mt9t013_regs. + reg_pat[rt]. + x_addr_end); + if (rc < 0) + return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, - REG_READ_MODE, - 0x0027); - } else - rc = mt9t013_i2c_write_w(mt9t013_client->addr, - REG_READ_MODE, - mt9t013_regs.reg_pat[rt].read_mode); - if (rc < 0) - return rc; + REG_Y_ADDR_START, + mt9t013_regs. + reg_pat[rt]. + y_addr_start); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_SCALE_M, - mt9t013_regs.reg_pat[rt].scale_m); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_Y_ADDR_END, + mt9t013_regs. + reg_pat[rt]. + y_addr_end); + if (rc < 0) + return rc; + + if (machine_is_sapphire()) { + if (rt == 0) + rc = mt9t013_i2c_write_w + (mt9t013_client->addr, + REG_READ_MODE, 0x046F); + else + rc = mt9t013_i2c_write_w + (mt9t013_client->addr, + REG_READ_MODE, 0x0027); + } else + rc = mt9t013_i2c_write_w + (mt9t013_client->addr, + REG_READ_MODE, + mt9t013_regs.reg_pat[rt]. + read_mode); + if (rc < 0) + return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_SCALE_M, + mt9t013_regs. + reg_pat[rt].scale_m); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_X_OUTPUT_SIZE, - mt9t013_regs.reg_pat[rt].x_output_size); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_X_OUTPUT_SIZE, + mt9t013_regs. + reg_pat[rt]. + x_output_size); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_Y_OUTPUT_SIZE, - mt9t013_regs.reg_pat[rt].y_output_size); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_Y_OUTPUT_SIZE, + mt9t013_regs. + reg_pat[rt]. + y_output_size); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_LINE_LENGTH_PCK, - mt9t013_regs.reg_pat[rt].line_length_pck); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_LINE_LENGTH_PCK, + mt9t013_regs. + reg_pat[rt]. + line_length_pck); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_FRAME_LENGTH_LINES, - (mt9t013_regs.reg_pat[rt].frame_length_lines * - mt9t013_ctrl->fps_divider / 0x00000400)); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_FRAME_LENGTH_LINES, + (mt9t013_regs. + reg_pat[rt]. + frame_length_lines * + mt9t013_ctrl-> + fps_divider / + 0x00000400)); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_COARSE_INT_TIME, - mt9t013_regs.reg_pat[rt].coarse_int_time); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_COARSE_INT_TIME, + mt9t013_regs. + reg_pat[rt]. + coarse_int_time); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_FINE_INT_TIME, - mt9t013_regs.reg_pat[rt].fine_int_time); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_FINE_INT_TIME, + mt9t013_regs. + reg_pat[rt]. + fine_int_time); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_UPDATE); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + return rc; - rc = mt9t013_test(mt9t013_ctrl->set_test); - if (rc < 0) - return rc; + rc = mt9t013_test(mt9t013_ctrl->set_test); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - MT9T013_REG_RESET_REGISTER, - MT9T013_RESET_REGISTER_PWON); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWON); + if (rc < 0) + return rc; - mdelay(5); + mdelay(5); - return rc; - } - } + return rc; + } + } break; - /*CAMSENSOR_REG_UPDATE_PERIODIC */ - case REG_INIT: { - if (rt == RES_PREVIEW || rt == RES_CAPTURE) { - - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - MT9T013_REG_RESET_REGISTER, - MT9T013_RESET_REGISTER_PWOFF); - if (rc < 0) - /* MODE_SELECT, stop streaming */ - return rc; - - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_VT_PIX_CLK_DIV, - mt9t013_regs.reg_pat[rt].vt_pix_clk_div); - if (rc < 0) - return rc; - - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_VT_SYS_CLK_DIV, - mt9t013_regs.reg_pat[rt].vt_sys_clk_div); - if (rc < 0) - return rc; + /*CAMSENSOR_REG_UPDATE_PERIODIC */ + case REG_INIT:{ + if (rt == RES_PREVIEW || rt == RES_CAPTURE) { - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_PRE_PLL_CLK_DIV, - mt9t013_regs.reg_pat[rt].pre_pll_clk_div); - if (rc < 0) - return rc; - - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_PLL_MULTIPLIER, - mt9t013_regs.reg_pat[rt].pll_multiplier); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWOFF); + if (rc < 0) + /* MODE_SELECT, stop streaming */ + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_OP_PIX_CLK_DIV, - mt9t013_regs.reg_pat[rt].op_pix_clk_div); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_VT_PIX_CLK_DIV, + mt9t013_regs. + reg_pat[rt]. + vt_pix_clk_div); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_OP_SYS_CLK_DIV, - mt9t013_regs.reg_pat[rt].op_sys_clk_div); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_VT_SYS_CLK_DIV, + mt9t013_regs. + reg_pat[rt]. + vt_sys_clk_div); + if (rc < 0) + return rc; - mdelay(5); + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_PRE_PLL_CLK_DIV, + mt9t013_regs. + reg_pat[rt]. + pre_pll_clk_div); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_HOLD); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_PLL_MULTIPLIER, + mt9t013_regs. + reg_pat[rt]. + pll_multiplier); + if (rc < 0) + return rc; - /* additional power saving mode ok around 38.2MHz */ - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - 0x3084, 0x2409); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_OP_PIX_CLK_DIV, + mt9t013_regs. + reg_pat[rt]. + op_pix_clk_div); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - 0x3092, 0x0A49); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_OP_SYS_CLK_DIV, + mt9t013_regs. + reg_pat[rt]. + op_sys_clk_div); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - 0x3094, 0x4949); - if (rc < 0) - return rc; + mdelay(5); - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - 0x3096, 0x4949); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; + + /* additional power saving mode ok around + * 38.2MHz + */ + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + 0x3084, 0x2409); + if (rc < 0) + return rc; - /* Set preview or snapshot mode */ - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_ROW_SPEED, - mt9t013_regs.reg_pat[rt].row_speed); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + 0x3092, 0x0A49); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_X_ADDR_START, - mt9t013_regs.reg_pat[rt].x_addr_start); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + 0x3094, 0x4949); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_X_ADDR_END, - mt9t013_regs.reg_pat[rt].x_addr_end); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + 0x3096, 0x4949); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_Y_ADDR_START, - mt9t013_regs.reg_pat[rt].y_addr_start); - if (rc < 0) - return rc; + /* Set preview or snapshot mode */ + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_ROW_SPEED, + mt9t013_regs. + reg_pat[rt].row_speed); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_Y_ADDR_END, - mt9t013_regs.reg_pat[rt].y_addr_end); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_X_ADDR_START, + mt9t013_regs. + reg_pat[rt]. + x_addr_start); + if (rc < 0) + return rc; - if (machine_is_sapphire()) { - if (rt == 0) rc = mt9t013_i2c_write_w(mt9t013_client->addr, - REG_READ_MODE, - 0x046F); - else + REG_X_ADDR_END, + mt9t013_regs. + reg_pat[rt]. + x_addr_end); + if (rc < 0) + return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, - REG_READ_MODE, - 0x0027); - } else - rc = mt9t013_i2c_write_w(mt9t013_client->addr, - REG_READ_MODE, - mt9t013_regs.reg_pat[rt].read_mode); - if (rc < 0) - return rc; + REG_Y_ADDR_START, + mt9t013_regs. + reg_pat[rt]. + y_addr_start); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_SCALE_M, - mt9t013_regs.reg_pat[rt].scale_m); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_Y_ADDR_END, + mt9t013_regs. + reg_pat[rt]. + y_addr_end); + if (rc < 0) + return rc; + + if (machine_is_sapphire()) { + if (rt == 0) + rc = mt9t013_i2c_write_w + (mt9t013_client->addr, + REG_READ_MODE, 0x046F); + else + rc = mt9t013_i2c_write_w + (mt9t013_client->addr, + REG_READ_MODE, 0x0027); + } else + rc = mt9t013_i2c_write_w + (mt9t013_client->addr, + REG_READ_MODE, + mt9t013_regs.reg_pat[rt]. + read_mode); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_X_OUTPUT_SIZE, - mt9t013_regs.reg_pat[rt].x_output_size); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_SCALE_M, + mt9t013_regs. + reg_pat[rt].scale_m); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_Y_OUTPUT_SIZE, - mt9t013_regs.reg_pat[rt].y_output_size); - if (rc < 0) - return 0; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_X_OUTPUT_SIZE, + mt9t013_regs. + reg_pat[rt]. + x_output_size); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_LINE_LENGTH_PCK, - mt9t013_regs.reg_pat[rt].line_length_pck); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_Y_OUTPUT_SIZE, + mt9t013_regs. + reg_pat[rt]. + y_output_size); + if (rc < 0) + return 0; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_FRAME_LENGTH_LINES, - mt9t013_regs.reg_pat[rt].frame_length_lines); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_LINE_LENGTH_PCK, + mt9t013_regs. + reg_pat[rt]. + line_length_pck); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_COARSE_INT_TIME, - mt9t013_regs.reg_pat[rt].coarse_int_time); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_FRAME_LENGTH_LINES, + mt9t013_regs. + reg_pat[rt]. + frame_length_lines); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_FINE_INT_TIME, - mt9t013_regs.reg_pat[rt].fine_int_time); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_COARSE_INT_TIME, + mt9t013_regs. + reg_pat[rt]. + coarse_int_time); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_UPDATE); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_FINE_INT_TIME, + mt9t013_regs. + reg_pat[rt]. + fine_int_time); + if (rc < 0) + return rc; - /* load lens shading */ - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_HOLD); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + return rc; - /* most likely needs to be written only once. */ - rc = mt9t013_set_lc(); - if (rc < 0) - return -EBUSY; + /* load lens shading */ + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_UPDATE); - if (rc < 0) - return rc; + /* most likely needs to be written only once. */ + rc = mt9t013_set_lc(); + if (rc < 0) + return -EBUSY; - rc = mt9t013_test(mt9t013_ctrl->set_test); - if (rc < 0) - return rc; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + return rc; - mdelay(5); + rc = mt9t013_test(mt9t013_ctrl->set_test); + if (rc < 0) + return rc; - rc = - mt9t013_i2c_write_w(mt9t013_client->addr, - MT9T013_REG_RESET_REGISTER, - MT9T013_RESET_REGISTER_PWON); - if (rc < 0) - /* MODE_SELECT, stop streaming */ - return rc; + mdelay(5); - CDBG("!!! mt9t013 !!! PowerOn is done!\n"); - mdelay(5); - return rc; - } - } /* case CAMSENSOR_REG_INIT: */ - break; + rc = mt9t013_i2c_write_w(mt9t013_client->addr, + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWON); + if (rc < 0) + /* MODE_SELECT, stop streaming */ + return rc; + + CDBG("!!! mt9t013 !!! PowerOn is done!\n"); + mdelay(5); + return rc; + } + } /* case CAMSENSOR_REG_INIT: */ + break; - /*CAMSENSOR_REG_INIT */ + /*CAMSENSOR_REG_INIT */ default: rc = -EINVAL; break; - } /* switch (rupdate) */ + } return rc; } -static int32_t mt9t013_video_config(int mode, int res) +static int mt9t013_video_config(int mode, int res) { - int32_t rc; + int rc; switch (res) { case QTR_SIZE: @@ -967,19 +1002,19 @@ static int32_t mt9t013_video_config(int mode, int res) default: return -EINVAL; - } /* switch */ + } mt9t013_ctrl->prev_res = res; mt9t013_ctrl->curr_res = res; mt9t013_ctrl->sensormode = mode; return mt9t013_write_exp_gain(mt9t013_ctrl->my_reg_gain, - mt9t013_ctrl->my_reg_line_count); + mt9t013_ctrl->my_reg_line_count); } -static int32_t mt9t013_snapshot_config(int mode) +static int mt9t013_snapshot_config(int mode) { - int32_t rc = 0; + int rc = 0; rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE); if (rc < 0) @@ -990,9 +1025,9 @@ static int32_t mt9t013_snapshot_config(int mode) return rc; } -static int32_t mt9t013_raw_snapshot_config(int mode) +static int mt9t013_raw_snapshot_config(int mode) { - int32_t rc = 0; + int rc = 0; rc = mt9t013_setting(UPDATE_PERIODIC, RES_CAPTURE); if (rc < 0) @@ -1003,19 +1038,19 @@ static int32_t mt9t013_raw_snapshot_config(int mode) return rc; } -static int32_t mt9t013_power_down(void) +static int mt9t013_power_down(void) { - int32_t rc = 0; + int rc = 0; rc = mt9t013_i2c_write_w(mt9t013_client->addr, - MT9T013_REG_RESET_REGISTER, - MT9T013_RESET_REGISTER_PWOFF); + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWOFF); if (rc >= 0) mdelay(5); return rc; } -static int32_t mt9t013_move_focus(int direction, int32_t num_steps) +static int mt9t013_move_focus(int direction, int num_steps) { int16_t step_direction; int16_t actual_step; @@ -1039,18 +1074,15 @@ static int32_t mt9t013_move_focus(int direction, int32_t num_steps) if (mt9t013_ctrl->curr_lens_pos < mt9t013_ctrl->init_curr_lens_pos) mt9t013_ctrl->curr_lens_pos = mt9t013_ctrl->init_curr_lens_pos; - actual_step = - (int16_t) (step_direction * - (int16_t) num_steps); + actual_step = (int16_t) (step_direction * (int16_t) num_steps); for (i = 0; i < 4; i++) break_steps[i] = - actual_step / 4 * (i + 1) - actual_step / 4 * i; + actual_step / 4 * (i + 1) - actual_step / 4 * i; for (i = 0; i < 4; i++) { - next_position = - (int16_t) - (mt9t013_ctrl->curr_lens_pos + break_steps[i]); + next_position = (int16_t) + (mt9t013_ctrl->curr_lens_pos + break_steps[i]); if (next_position > 255) next_position = 255; @@ -1058,15 +1090,13 @@ static int32_t mt9t013_move_focus(int direction, int32_t num_steps) next_position = 0; code_val_msb = - ((next_position >> 4) << 2) | - ((next_position << 4) >> 6); + ((next_position >> 4) << 2) | ((next_position << 4) >> 6); - code_val_lsb = - ((next_position & 0x03) << 6); + code_val_lsb = ((next_position & 0x03) << 6); /* Writing the digital code for current to the actuator */ - if (mt9t013_i2c_write_b(MT9T013_AF_I2C_ADDR>>1, - code_val_msb, code_val_lsb) < 0) + if (mt9t013_i2c_write_b(MT9T013_AF_I2C_ADDR >> 1, + code_val_msb, code_val_lsb) < 0) return -EBUSY; /* Storing the current lens Position */ @@ -1074,7 +1104,7 @@ static int32_t mt9t013_move_focus(int direction, int32_t num_steps) if (i < 3) mdelay(1); - } /* for */ + } return 0; } @@ -1101,13 +1131,13 @@ static int mt9t013_probe_init_sensor(const struct msm_camera_sensor_info *data) /* RESET the sensor image part via I2C command */ rc = mt9t013_i2c_write_w(mt9t013_client->addr, - MT9T013_REG_RESET_REGISTER, 0x1009); + MT9T013_REG_RESET_REGISTER, 0x1009); if (rc < 0) goto init_probe_fail; /* 3. Read sensor Model ID: */ rc = mt9t013_i2c_read_w(mt9t013_client->addr, - MT9T013_REG_MODEL_ID, &chipid); + MT9T013_REG_MODEL_ID, &chipid); if (rc < 0) goto init_probe_fail; @@ -1120,8 +1150,7 @@ static int mt9t013_probe_init_sensor(const struct msm_camera_sensor_info *data) goto init_probe_fail; } - rc = mt9t013_i2c_write_w(mt9t013_client->addr, - 0x3064, 0x0805); + rc = mt9t013_i2c_write_w(mt9t013_client->addr, 0x3064, 0x0805); if (rc < 0) goto init_probe_fail; @@ -1132,8 +1161,8 @@ static int mt9t013_probe_init_sensor(const struct msm_camera_sensor_info *data) /* sensor: output enable */ #if 0 rc = mt9t013_i2c_write_w(mt9t013_client->addr, - MT9T013_REG_RESET_REGISTER, - MT9T013_RESET_REGISTER_PWON); + MT9T013_REG_RESET_REGISTER, + MT9T013_RESET_REGISTER_PWON); /* if this fails, the sensor is not the MT9T013 */ rc = mt9t013_set_default_focus(0); @@ -1146,13 +1175,13 @@ static int mt9t013_probe_init_sensor(const struct msm_camera_sensor_info *data) return rc; } -static int32_t mt9t013_poweron_af(void) +static int mt9t013_poweron_af(void) { - int32_t rc = 0; + int rc = 0; /* enable AF actuator */ CDBG("enable AF actuator, gpio = %d\n", - mt9t013_ctrl->sensordata->vcm_pwd); + mt9t013_ctrl->sensordata->vcm_pwd); rc = gpio_request(mt9t013_ctrl->sensordata->vcm_pwd, "mt9t013"); if (!rc) { gpio_direction_output(mt9t013_ctrl->sensordata->vcm_pwd, 0); @@ -1171,7 +1200,7 @@ static void mt9t013_poweroff_af(void) int mt9t013_sensor_open_init(const struct msm_camera_sensor_info *data) { - int32_t rc; + int rc; mt9t013_ctrl = kzalloc(sizeof(struct mt9t013_ctrl), GFP_KERNEL); if (!mt9t013_ctrl) { @@ -1226,13 +1255,12 @@ static int mt9t013_init_client(struct i2c_client *client) return 0; } - -static int32_t mt9t013_set_sensor_mode(int mode, int res) +static int mt9t013_set_sensor_mode(int mode, int res) { - int32_t rc = 0; + int rc = 0; rc = mt9t013_i2c_write_w(mt9t013_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_HOLD); + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); if (rc < 0) return rc; @@ -1256,74 +1284,64 @@ static int32_t mt9t013_set_sensor_mode(int mode, int res) /* FIXME: what should we do if rc < 0? */ if (rc >= 0) return mt9t013_i2c_write_w(mt9t013_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_UPDATE); + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); return rc; } int mt9t013_sensor_config(void __user *argp) { struct sensor_cfg_data cdata; - long rc = 0; + long rc = 0; if (copy_from_user(&cdata, (void *)argp, - sizeof(struct sensor_cfg_data))) + sizeof(struct sensor_cfg_data))) return -EFAULT; - down(&mt9t013_sem); - CDBG("mt9t013_sensor_config: cfgtype = %d\n", cdata.cfgtype); switch (cdata.cfgtype) { case CFG_GET_PICT_FPS: mt9t013_get_pict_fps(cdata.cfg.gfps.prevfps, - &(cdata.cfg.gfps.pictfps)); + &(cdata.cfg.gfps.pictfps)); if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; case CFG_GET_PREV_L_PF: cdata.cfg.prevl_pf = mt9t013_get_prev_lines_pf(); if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; case CFG_GET_PREV_P_PL: cdata.cfg.prevp_pl = mt9t013_get_prev_pixels_pl(); if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; case CFG_GET_PICT_L_PF: cdata.cfg.pictl_pf = mt9t013_get_pict_lines_pf(); if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; case CFG_GET_PICT_P_PL: - cdata.cfg.pictp_pl = - mt9t013_get_pict_pixels_pl(); + cdata.cfg.pictp_pl = mt9t013_get_pict_pixels_pl(); if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; case CFG_GET_PICT_MAX_EXP_LC: - cdata.cfg.pict_max_exp_lc = - mt9t013_get_pict_max_exp_lc(); + cdata.cfg.pict_max_exp_lc = mt9t013_get_pict_max_exp_lc(); if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; @@ -1334,12 +1352,12 @@ int mt9t013_sensor_config(void __user *argp) case CFG_SET_EXP_GAIN: rc = mt9t013_write_exp_gain(cdata.cfg.exp_gain.gain, - cdata.cfg.exp_gain.line); + cdata.cfg.exp_gain.line); break; case CFG_SET_PICT_EXP_GAIN: rc = mt9t013_set_pict_exp_gain(cdata.cfg.exp_gain.gain, - cdata.cfg.exp_gain.line); + cdata.cfg.exp_gain.line); break; case CFG_SET_MODE: @@ -1352,7 +1370,7 @@ int mt9t013_sensor_config(void __user *argp) case CFG_MOVE_FOCUS: rc = mt9t013_move_focus(cdata.cfg.focus.dir, - cdata.cfg.focus.steps); + cdata.cfg.focus.steps); break; case CFG_SET_DEFAULT_FOCUS: @@ -1362,8 +1380,7 @@ int mt9t013_sensor_config(void __user *argp) case CFG_GET_AF_MAX_STEPS: cdata.max_steps = MT9T013_TOTAL_STEPS_NEAR_TO_FAR; if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; @@ -1373,7 +1390,6 @@ int mt9t013_sensor_config(void __user *argp) break; } - up(&mt9t013_sem); return rc; } @@ -1381,24 +1397,20 @@ int mt9t013_sensor_release(void) { int rc = -EBADF; - down(&mt9t013_sem); - mt9t013_poweroff_af(); mt9t013_power_down(); - gpio_direction_output(mt9t013_ctrl->sensordata->sensor_reset, - 0); + gpio_direction_output(mt9t013_ctrl->sensordata->sensor_reset, 0); gpio_free(mt9t013_ctrl->sensordata->sensor_reset); kfree(mt9t013_ctrl); - up(&mt9t013_sem); CDBG("mt9t013_release completed!\n"); return rc; } static int mt9t013_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { int rc = 0; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { @@ -1406,8 +1418,7 @@ static int mt9t013_i2c_probe(struct i2c_client *client, goto probe_failure; } - mt9t013_sensorw = - kzalloc(sizeof(struct mt9t013_work), GFP_KERNEL); + mt9t013_sensorw = kzalloc(sizeof(struct mt9t013_work), GFP_KERNEL); if (!mt9t013_sensorw) { rc = -ENOMEM; @@ -1431,22 +1442,21 @@ static int mt9t013_i2c_probe(struct i2c_client *client, } static const struct i2c_device_id mt9t013_i2c_id[] = { - { "mt9t013", 0}, - { } + {"mt9t013", 0}, + {} }; static struct i2c_driver mt9t013_i2c_driver = { .id_table = mt9t013_i2c_id, - .probe = mt9t013_i2c_probe, + .probe = mt9t013_i2c_probe, .remove = __exit_p(mt9t013_i2c_remove), .driver = { - .name = "mt9t013", - }, + .name = "mt9t013", + }, }; -static int mt9t013_sensor_probe( - const struct msm_camera_sensor_info *info, - struct msm_sensor_ctrl *s) +static int mt9t013_sensor_probe(const struct msm_camera_sensor_info *info, + struct msm_sensor_ctrl *s) { /* We expect this driver to match with the i2c device registered * in the board file immediately. */ @@ -1468,7 +1478,7 @@ static int mt9t013_sensor_probe( s->s_init = mt9t013_sensor_open_init; s->s_release = mt9t013_sensor_release; - s->s_config = mt9t013_sensor_config; + s->s_config = mt9t013_sensor_config; mt9t013_sensor_init_done(info); probe_done: @@ -1483,9 +1493,9 @@ static int __mt9t013_probe(struct platform_device *pdev) static struct platform_driver msm_camera_driver = { .probe = __mt9t013_probe, .driver = { - .name = "msm_camera_mt9t013", - .owner = THIS_MODULE, - }, + .name = "msm_camera_mt9t013", + .owner = THIS_MODULE, + }, }; static int __init mt9t013_init(void) diff --git a/drivers/media/video/msm/mt9t013.h b/drivers/media/video/msm/mt9t013.h index 9bce2036e3b63..67c4d9c5b65c5 100644 --- a/drivers/media/video/msm/mt9t013.h +++ b/drivers/media/video/msm/mt9t013.h @@ -1,5 +1,19 @@ -/* - * Copyright (C) 2008-2009 QUALCOMM Incorporated. +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ #ifndef MT9T013_H @@ -8,25 +22,25 @@ #include struct reg_struct { - uint16_t vt_pix_clk_div; /* 0x0300 */ - uint16_t vt_sys_clk_div; /* 0x0302 */ - uint16_t pre_pll_clk_div; /* 0x0304 */ - uint16_t pll_multiplier; /* 0x0306 */ - uint16_t op_pix_clk_div; /* 0x0308 */ - uint16_t op_sys_clk_div; /* 0x030A */ - uint16_t scale_m; /* 0x0404 */ - uint16_t row_speed; /* 0x3016 */ - uint16_t x_addr_start; /* 0x3004 */ - uint16_t x_addr_end; /* 0x3008 */ - uint16_t y_addr_start; /* 0x3002 */ - uint16_t y_addr_end; /* 0x3006 */ - uint16_t read_mode; /* 0x3040 */ - uint16_t x_output_size; /* 0x034C */ - uint16_t y_output_size; /* 0x034E */ - uint16_t line_length_pck; /* 0x300C */ + uint16_t vt_pix_clk_div; /* 0x0300 */ + uint16_t vt_sys_clk_div; /* 0x0302 */ + uint16_t pre_pll_clk_div; /* 0x0304 */ + uint16_t pll_multiplier; /* 0x0306 */ + uint16_t op_pix_clk_div; /* 0x0308 */ + uint16_t op_sys_clk_div; /* 0x030A */ + uint16_t scale_m; /* 0x0404 */ + uint16_t row_speed; /* 0x3016 */ + uint16_t x_addr_start; /* 0x3004 */ + uint16_t x_addr_end; /* 0x3008 */ + uint16_t y_addr_start; /* 0x3002 */ + uint16_t y_addr_end; /* 0x3006 */ + uint16_t read_mode; /* 0x3040 */ + uint16_t x_output_size; /* 0x034C */ + uint16_t y_output_size; /* 0x034E */ + uint16_t line_length_pck; /* 0x300C */ uint16_t frame_length_lines; /* 0x300A */ - uint16_t coarse_int_time; /* 0x3012 */ - uint16_t fine_int_time; /* 0x3014 */ + uint16_t coarse_int_time; /* 0x3012 */ + uint16_t fine_int_time; /* 0x3014 */ }; struct mt9t013_i2c_reg_conf { @@ -36,13 +50,15 @@ struct mt9t013_i2c_reg_conf { struct mt9t013_reg { struct reg_struct *reg_pat; - uint16_t reg_pat_size; + int reg_pat_size; struct mt9t013_i2c_reg_conf *ttbl; - uint16_t ttbl_size; + int ttbl_size; struct mt9t013_i2c_reg_conf *lctbl; - uint16_t lctbl_size; + int lctbl_size; struct mt9t013_i2c_reg_conf *rftbl; - uint16_t rftbl_size; + int rftbl_size; }; +extern struct mt9t013_reg mt9t013_regs; + #endif /* #define MT9T013_H */ diff --git a/drivers/media/video/msm/mt9t013_reg.c b/drivers/media/video/msm/mt9t013_reg.c index f296d353e6eea..aac81c796883a 100644 --- a/drivers/media/video/msm/mt9t013_reg.c +++ b/drivers/media/video/msm/mt9t013_reg.c @@ -1,256 +1,268 @@ -/* - * Copyright (C) 2009 QUALCOMM Incorporated. +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ #include "mt9t013.h" #include -struct reg_struct const mt9t013_reg_pat[2] = { - { /* Preview 2x2 binning 20fps, pclk MHz, MCLK 24MHz */ - /* vt_pix_clk_div:REG=0x0300 update get_snapshot_fps - * if this change */ - 10, +struct reg_struct mt9t013_reg_pat[2] = { + {/* Preview 2x2 binning 20fps, pclk MHz, MCLK 24MHz */ + /* vt_pix_clk_div:REG=0x0300 update get_snapshot_fps + * if this change */ + 10, - /* vt_sys_clk_div: REG=0x0302 update get_snapshot_fps - * if this change */ - 1, + /* vt_sys_clk_div: REG=0x0302 update get_snapshot_fps + * if this change */ + 1, - /* pre_pll_clk_div REG=0x0304 update get_snapshot_fps - * if this change */ - 3, + /* pre_pll_clk_div REG=0x0304 update get_snapshot_fps + * if this change */ + 3, - /* pll_multiplier REG=0x0306 60 for 30fps preview, 40 - * for 20fps preview - * 46 for 30fps preview, try 47/48 to increase further */ - 80, + /* pll_multiplier REG=0x0306 60 for 30fps preview, 40 + * for 20fps preview + * 46 for 30fps preview, try 47/48 to increase further */ + 80, - /* op_pix_clk_div REG=0x0308 */ - 10, + /* op_pix_clk_div REG=0x0308 */ + 10, - /* op_sys_clk_div REG=0x030A */ - 1, + /* op_sys_clk_div REG=0x030A */ + 1, - /* scale_m REG=0x0404 */ - 16, + /* scale_m REG=0x0404 */ + 16, - /* row_speed REG=0x3016 */ - 0x0111, + /* row_speed REG=0x3016 */ + 0x0111, - /* x_addr_start REG=0x3004 */ - 8, + /* x_addr_start REG=0x3004 */ + 8, - /* x_addr_end REG=0x3008 */ - 2053, + /* x_addr_end REG=0x3008 */ + 2053, - /* y_addr_start REG=0x3002 */ - 8, + /* y_addr_start REG=0x3002 */ + 8, - /* y_addr_end REG=0x3006 */ - 1541, + /* y_addr_end REG=0x3006 */ + 1541, - /* read_mode REG=0x3040 */ - 0x046C, + /* read_mode REG=0x3040 */ + 0x046C, - /* x_output_size REG=0x034C */ - 1024, + /* x_output_size REG=0x034C */ + 1024, - /* y_output_size REG=0x034E */ - 768, + /* y_output_size REG=0x034E */ + 768, - /* line_length_pck REG=0x300C */ - 3540, + /* line_length_pck REG=0x300C */ + 3540, - /* frame_length_lines REG=0x300A */ - 861, + /* frame_length_lines REG=0x300A */ + 861, - /* coarse_int_time REG=0x3012 */ - 16, + /* coarse_int_time REG=0x3012 */ + 16, - /* fine_int_time REG=0x3014 */ - 1461 - }, + /* fine_int_time REG=0x3014 */ + 1461}, { /*Snapshot */ - /* vt_pix_clk_div REG=0x0300 update get_snapshot_fps - * if this change */ - 10, + /* vt_pix_clk_div REG=0x0300 update get_snapshot_fps + * if this change */ + 10, - /* vt_sys_clk_div REG=0x0302 update get_snapshot_fps - * if this change */ - 1, + /* vt_sys_clk_div REG=0x0302 update get_snapshot_fps + * if this change */ + 1, - /* pre_pll_clk_div REG=0x0304 update get_snapshot_fps - * if this change */ - 3, + /* pre_pll_clk_div REG=0x0304 update get_snapshot_fps + * if this change */ + 3, - /* pll_multiplier REG=0x0306 50 for 15fps snapshot, - * 40 for 10fps snapshot - * 46 for 30fps snapshot, try 47/48 to increase further */ - 80, + /* pll_multiplier REG=0x0306 50 for 15fps snapshot, + * 40 for 10fps snapshot + * 46 for 30fps snapshot, try 47/48 to increase further */ + 80, - /* op_pix_clk_div REG=0x0308 */ - 10, + /* op_pix_clk_div REG=0x0308 */ + 10, - /* op_sys_clk_div REG=0x030A */ - 1, + /* op_sys_clk_div REG=0x030A */ + 1, - /* scale_m REG=0x0404 */ - 16, + /* scale_m REG=0x0404 */ + 16, - /* row_speed REG=0x3016 */ - 0x0111, + /* row_speed REG=0x3016 */ + 0x0111, - /* x_addr_start REG=0x3004 */ - 8, + /* x_addr_start REG=0x3004 */ + 8, - /* x_addr_end REG=0x3008 */ - 2063, + /* x_addr_end REG=0x3008 */ + 2063, - /* y_addr_start REG=0x3002 */ - 8, + /* y_addr_start REG=0x3002 */ + 8, - /* y_addr_end REG=0x3006 */ - 1551, + /* y_addr_end REG=0x3006 */ + 1551, - /* read_mode REG=0x3040 */ - 0x0024, + /* read_mode REG=0x3040 */ + 0x0024, - /* x_output_size REG=0x034C */ - 2064, + /* x_output_size REG=0x034C */ + 2064, - /* y_output_size REG=0x034E */ - 1544, + /* y_output_size REG=0x034E */ + 1544, - /* line_length_pck REG=0x300C */ - 4800, + /* line_length_pck REG=0x300C */ + 4800, - /* frame_length_lines REG=0x300A */ - 1629, + /* frame_length_lines REG=0x300A */ + 1629, - /* coarse_int_time REG=0x3012 */ - 16, + /* coarse_int_time REG=0x3012 */ + 16, - /* fine_int_time REG=0x3014 */ - 733 - } + /* fine_int_time REG=0x3014 */ + 733} }; struct mt9t013_i2c_reg_conf mt9t013_test_tbl[] = { - { 0x3044, 0x0544 & 0xFBFF }, - { 0x30CA, 0x0004 | 0x0001 }, - { 0x30D4, 0x9020 & 0x7FFF }, - { 0x31E0, 0x0003 & 0xFFFE }, - { 0x3180, 0x91FF & 0x7FFF }, - { 0x301A, (0x10CC | 0x8000) & 0xFFF7 }, - { 0x301E, 0x0000 }, - { 0x3780, 0x0000 }, + {0x3044, 0x0544 & 0xFBFF}, + {0x30CA, 0x0004 | 0x0001}, + {0x30D4, 0x9020 & 0x7FFF}, + {0x31E0, 0x0003 & 0xFFFE}, + {0x3180, 0x91FF & 0x7FFF}, + {0x301A, (0x10CC | 0x8000) & 0xFFF7}, + {0x301E, 0x0000}, + {0x3780, 0x0000}, }; /* [Lens shading 85 Percent TL84] */ struct mt9t013_i2c_reg_conf mt9t013_lc_tbl[] = { - { 0x360A, 0x0290 }, /* P_RD_P0Q0 */ - { 0x360C, 0xC92D }, /* P_RD_P0Q1 */ - { 0x360E, 0x0771 }, /* P_RD_P0Q2 */ - { 0x3610, 0xE38C }, /* P_RD_P0Q3 */ - { 0x3612, 0xD74F }, /* P_RD_P0Q4 */ - { 0x364A, 0x168C }, /* P_RD_P1Q0 */ - { 0x364C, 0xCACB }, /* P_RD_P1Q1 */ - { 0x364E, 0x8C4C }, /* P_RD_P1Q2 */ - { 0x3650, 0x0BEA }, /* P_RD_P1Q3 */ - { 0x3652, 0xDC0F }, /* P_RD_P1Q4 */ - { 0x368A, 0x70B0 }, /* P_RD_P2Q0 */ - { 0x368C, 0x200B }, /* P_RD_P2Q1 */ - { 0x368E, 0x30B2 }, /* P_RD_P2Q2 */ - { 0x3690, 0xD04F }, /* P_RD_P2Q3 */ - { 0x3692, 0xACF5 }, /* P_RD_P2Q4 */ - { 0x36CA, 0xF7C9 }, /* P_RD_P3Q0 */ - { 0x36CC, 0x2AED }, /* P_RD_P3Q1 */ - { 0x36CE, 0xA652 }, /* P_RD_P3Q2 */ - { 0x36D0, 0x8192 }, /* P_RD_P3Q3 */ - { 0x36D2, 0x3A15 }, /* P_RD_P3Q4 */ - { 0x370A, 0xDA30 }, /* P_RD_P4Q0 */ - { 0x370C, 0x2E2F }, /* P_RD_P4Q1 */ - { 0x370E, 0xBB56 }, /* P_RD_P4Q2 */ - { 0x3710, 0x8195 }, /* P_RD_P4Q3 */ - { 0x3712, 0x02F9 }, /* P_RD_P4Q4 */ - { 0x3600, 0x0230 }, /* P_GR_P0Q0 */ - { 0x3602, 0x58AD }, /* P_GR_P0Q1 */ - { 0x3604, 0x18D1 }, /* P_GR_P0Q2 */ - { 0x3606, 0x260D }, /* P_GR_P0Q3 */ - { 0x3608, 0xF530 }, /* P_GR_P0Q4 */ - { 0x3640, 0x17EB }, /* P_GR_P1Q0 */ - { 0x3642, 0x3CAB }, /* P_GR_P1Q1 */ - { 0x3644, 0x87CE }, /* P_GR_P1Q2 */ - { 0x3646, 0xC02E }, /* P_GR_P1Q3 */ - { 0x3648, 0xF48F }, /* P_GR_P1Q4 */ - { 0x3680, 0x5350 }, /* P_GR_P2Q0 */ - { 0x3682, 0x7EAF }, /* P_GR_P2Q1 */ - { 0x3684, 0x4312 }, /* P_GR_P2Q2 */ - { 0x3686, 0xC652 }, /* P_GR_P2Q3 */ - { 0x3688, 0xBC15 }, /* P_GR_P2Q4 */ - { 0x36C0, 0xB8AD }, /* P_GR_P3Q0 */ - { 0x36C2, 0xBDCD }, /* P_GR_P3Q1 */ - { 0x36C4, 0xE4B2 }, /* P_GR_P3Q2 */ - { 0x36C6, 0xB50F }, /* P_GR_P3Q3 */ - { 0x36C8, 0x5B95 }, /* P_GR_P3Q4 */ - { 0x3700, 0xFC90 }, /* P_GR_P4Q0 */ - { 0x3702, 0x8C51 }, /* P_GR_P4Q1 */ - { 0x3704, 0xCED6 }, /* P_GR_P4Q2 */ - { 0x3706, 0xB594 }, /* P_GR_P4Q3 */ - { 0x3708, 0x0A39 }, /* P_GR_P4Q4 */ - { 0x3614, 0x0230 }, /* P_BL_P0Q0 */ - { 0x3616, 0x160D }, /* P_BL_P0Q1 */ - { 0x3618, 0x08D1 }, /* P_BL_P0Q2 */ - { 0x361A, 0x98AB }, /* P_BL_P0Q3 */ - { 0x361C, 0xEA50 }, /* P_BL_P0Q4 */ - { 0x3654, 0xB4EA }, /* P_BL_P1Q0 */ - { 0x3656, 0xEA6C }, /* P_BL_P1Q1 */ - { 0x3658, 0xFE08 }, /* P_BL_P1Q2 */ - { 0x365A, 0x2C6E }, /* P_BL_P1Q3 */ - { 0x365C, 0xEB0E }, /* P_BL_P1Q4 */ - { 0x3694, 0x6DF0 }, /* P_BL_P2Q0 */ - { 0x3696, 0x3ACF }, /* P_BL_P2Q1 */ - { 0x3698, 0x3E0F }, /* P_BL_P2Q2 */ - { 0x369A, 0xB2B1 }, /* P_BL_P2Q3 */ - { 0x369C, 0xC374 }, /* P_BL_P2Q4 */ - { 0x36D4, 0xF2AA }, /* P_BL_P3Q0 */ - { 0x36D6, 0x8CCC }, /* P_BL_P3Q1 */ - { 0x36D8, 0xDEF2 }, /* P_BL_P3Q2 */ - { 0x36DA, 0xFA11 }, /* P_BL_P3Q3 */ - { 0x36DC, 0x42F5 }, /* P_BL_P3Q4 */ - { 0x3714, 0xF4F1 }, /* P_BL_P4Q0 */ - { 0x3716, 0xF6F0 }, /* P_BL_P4Q1 */ - { 0x3718, 0x8FD6 }, /* P_BL_P4Q2 */ - { 0x371A, 0xEA14 }, /* P_BL_P4Q3 */ - { 0x371C, 0x6338 }, /* P_BL_P4Q4 */ - { 0x361E, 0x0350 }, /* P_GB_P0Q0 */ - { 0x3620, 0x91AE }, /* P_GB_P0Q1 */ - { 0x3622, 0x0571 }, /* P_GB_P0Q2 */ - { 0x3624, 0x100D }, /* P_GB_P0Q3 */ - { 0x3626, 0xCA70 }, /* P_GB_P0Q4 */ - { 0x365E, 0xE6CB }, /* P_GB_P1Q0 */ - { 0x3660, 0x50ED }, /* P_GB_P1Q1 */ - { 0x3662, 0x3DAE }, /* P_GB_P1Q2 */ - { 0x3664, 0xAA4F }, /* P_GB_P1Q3 */ - { 0x3666, 0xDC50 }, /* P_GB_P1Q4 */ - { 0x369E, 0x5470 }, /* P_GB_P2Q0 */ - { 0x36A0, 0x1F6E }, /* P_GB_P2Q1 */ - { 0x36A2, 0x6671 }, /* P_GB_P2Q2 */ - { 0x36A4, 0xC010 }, /* P_GB_P2Q3 */ - { 0x36A6, 0x8DF5 }, /* P_GB_P2Q4 */ - { 0x36DE, 0x0B0C }, /* P_GB_P3Q0 */ - { 0x36E0, 0x84CE }, /* P_GB_P3Q1 */ - { 0x36E2, 0x8493 }, /* P_GB_P3Q2 */ - { 0x36E4, 0xA610 }, /* P_GB_P3Q3 */ - { 0x36E6, 0x50B5 }, /* P_GB_P3Q4 */ - { 0x371E, 0x9651 }, /* P_GB_P4Q0 */ - { 0x3720, 0x1EAB }, /* P_GB_P4Q1 */ - { 0x3722, 0xAF76 }, /* P_GB_P4Q2 */ - { 0x3724, 0xE4F4 }, /* P_GB_P4Q3 */ - { 0x3726, 0x79F8 }, /* P_GB_P4Q4 */ - { 0x3782, 0x0410 }, /* POLY_ORIGIN_C */ - { 0x3784, 0x0320 }, /* POLY_ORIGIN_R */ - { 0x3780, 0x8000 } /* POLY_SC_ENABLE */ + {0x360A, 0x0290}, /* P_RD_P0Q0 */ + {0x360C, 0xC92D}, /* P_RD_P0Q1 */ + {0x360E, 0x0771}, /* P_RD_P0Q2 */ + {0x3610, 0xE38C}, /* P_RD_P0Q3 */ + {0x3612, 0xD74F}, /* P_RD_P0Q4 */ + {0x364A, 0x168C}, /* P_RD_P1Q0 */ + {0x364C, 0xCACB}, /* P_RD_P1Q1 */ + {0x364E, 0x8C4C}, /* P_RD_P1Q2 */ + {0x3650, 0x0BEA}, /* P_RD_P1Q3 */ + {0x3652, 0xDC0F}, /* P_RD_P1Q4 */ + {0x368A, 0x70B0}, /* P_RD_P2Q0 */ + {0x368C, 0x200B}, /* P_RD_P2Q1 */ + {0x368E, 0x30B2}, /* P_RD_P2Q2 */ + {0x3690, 0xD04F}, /* P_RD_P2Q3 */ + {0x3692, 0xACF5}, /* P_RD_P2Q4 */ + {0x36CA, 0xF7C9}, /* P_RD_P3Q0 */ + {0x36CC, 0x2AED}, /* P_RD_P3Q1 */ + {0x36CE, 0xA652}, /* P_RD_P3Q2 */ + {0x36D0, 0x8192}, /* P_RD_P3Q3 */ + {0x36D2, 0x3A15}, /* P_RD_P3Q4 */ + {0x370A, 0xDA30}, /* P_RD_P4Q0 */ + {0x370C, 0x2E2F}, /* P_RD_P4Q1 */ + {0x370E, 0xBB56}, /* P_RD_P4Q2 */ + {0x3710, 0x8195}, /* P_RD_P4Q3 */ + {0x3712, 0x02F9}, /* P_RD_P4Q4 */ + {0x3600, 0x0230}, /* P_GR_P0Q0 */ + {0x3602, 0x58AD}, /* P_GR_P0Q1 */ + {0x3604, 0x18D1}, /* P_GR_P0Q2 */ + {0x3606, 0x260D}, /* P_GR_P0Q3 */ + {0x3608, 0xF530}, /* P_GR_P0Q4 */ + {0x3640, 0x17EB}, /* P_GR_P1Q0 */ + {0x3642, 0x3CAB}, /* P_GR_P1Q1 */ + {0x3644, 0x87CE}, /* P_GR_P1Q2 */ + {0x3646, 0xC02E}, /* P_GR_P1Q3 */ + {0x3648, 0xF48F}, /* P_GR_P1Q4 */ + {0x3680, 0x5350}, /* P_GR_P2Q0 */ + {0x3682, 0x7EAF}, /* P_GR_P2Q1 */ + {0x3684, 0x4312}, /* P_GR_P2Q2 */ + {0x3686, 0xC652}, /* P_GR_P2Q3 */ + {0x3688, 0xBC15}, /* P_GR_P2Q4 */ + {0x36C0, 0xB8AD}, /* P_GR_P3Q0 */ + {0x36C2, 0xBDCD}, /* P_GR_P3Q1 */ + {0x36C4, 0xE4B2}, /* P_GR_P3Q2 */ + {0x36C6, 0xB50F}, /* P_GR_P3Q3 */ + {0x36C8, 0x5B95}, /* P_GR_P3Q4 */ + {0x3700, 0xFC90}, /* P_GR_P4Q0 */ + {0x3702, 0x8C51}, /* P_GR_P4Q1 */ + {0x3704, 0xCED6}, /* P_GR_P4Q2 */ + {0x3706, 0xB594}, /* P_GR_P4Q3 */ + {0x3708, 0x0A39}, /* P_GR_P4Q4 */ + {0x3614, 0x0230}, /* P_BL_P0Q0 */ + {0x3616, 0x160D}, /* P_BL_P0Q1 */ + {0x3618, 0x08D1}, /* P_BL_P0Q2 */ + {0x361A, 0x98AB}, /* P_BL_P0Q3 */ + {0x361C, 0xEA50}, /* P_BL_P0Q4 */ + {0x3654, 0xB4EA}, /* P_BL_P1Q0 */ + {0x3656, 0xEA6C}, /* P_BL_P1Q1 */ + {0x3658, 0xFE08}, /* P_BL_P1Q2 */ + {0x365A, 0x2C6E}, /* P_BL_P1Q3 */ + {0x365C, 0xEB0E}, /* P_BL_P1Q4 */ + {0x3694, 0x6DF0}, /* P_BL_P2Q0 */ + {0x3696, 0x3ACF}, /* P_BL_P2Q1 */ + {0x3698, 0x3E0F}, /* P_BL_P2Q2 */ + {0x369A, 0xB2B1}, /* P_BL_P2Q3 */ + {0x369C, 0xC374}, /* P_BL_P2Q4 */ + {0x36D4, 0xF2AA}, /* P_BL_P3Q0 */ + {0x36D6, 0x8CCC}, /* P_BL_P3Q1 */ + {0x36D8, 0xDEF2}, /* P_BL_P3Q2 */ + {0x36DA, 0xFA11}, /* P_BL_P3Q3 */ + {0x36DC, 0x42F5}, /* P_BL_P3Q4 */ + {0x3714, 0xF4F1}, /* P_BL_P4Q0 */ + {0x3716, 0xF6F0}, /* P_BL_P4Q1 */ + {0x3718, 0x8FD6}, /* P_BL_P4Q2 */ + {0x371A, 0xEA14}, /* P_BL_P4Q3 */ + {0x371C, 0x6338}, /* P_BL_P4Q4 */ + {0x361E, 0x0350}, /* P_GB_P0Q0 */ + {0x3620, 0x91AE}, /* P_GB_P0Q1 */ + {0x3622, 0x0571}, /* P_GB_P0Q2 */ + {0x3624, 0x100D}, /* P_GB_P0Q3 */ + {0x3626, 0xCA70}, /* P_GB_P0Q4 */ + {0x365E, 0xE6CB}, /* P_GB_P1Q0 */ + {0x3660, 0x50ED}, /* P_GB_P1Q1 */ + {0x3662, 0x3DAE}, /* P_GB_P1Q2 */ + {0x3664, 0xAA4F}, /* P_GB_P1Q3 */ + {0x3666, 0xDC50}, /* P_GB_P1Q4 */ + {0x369E, 0x5470}, /* P_GB_P2Q0 */ + {0x36A0, 0x1F6E}, /* P_GB_P2Q1 */ + {0x36A2, 0x6671}, /* P_GB_P2Q2 */ + {0x36A4, 0xC010}, /* P_GB_P2Q3 */ + {0x36A6, 0x8DF5}, /* P_GB_P2Q4 */ + {0x36DE, 0x0B0C}, /* P_GB_P3Q0 */ + {0x36E0, 0x84CE}, /* P_GB_P3Q1 */ + {0x36E2, 0x8493}, /* P_GB_P3Q2 */ + {0x36E4, 0xA610}, /* P_GB_P3Q3 */ + {0x36E6, 0x50B5}, /* P_GB_P3Q4 */ + {0x371E, 0x9651}, /* P_GB_P4Q0 */ + {0x3720, 0x1EAB}, /* P_GB_P4Q1 */ + {0x3722, 0xAF76}, /* P_GB_P4Q2 */ + {0x3724, 0xE4F4}, /* P_GB_P4Q3 */ + {0x3726, 0x79F8}, /* P_GB_P4Q4 */ + {0x3782, 0x0410}, /* POLY_ORIGIN_C */ + {0x3784, 0x0320}, /* POLY_ORIGIN_R */ + {0x3780, 0x8000} /* POLY_SC_ENABLE */ }; struct mt9t013_reg mt9t013_regs = { @@ -260,8 +272,6 @@ struct mt9t013_reg mt9t013_regs = { .ttbl_size = ARRAY_SIZE(mt9t013_test_tbl), .lctbl = &mt9t013_lc_tbl[0], .lctbl_size = ARRAY_SIZE(mt9t013_lc_tbl), - .rftbl = &mt9t013_lc_tbl[0], /* &mt9t013_rolloff_tbl[0], */ + .rftbl = &mt9t013_lc_tbl[0], .rftbl_size = ARRAY_SIZE(mt9t013_lc_tbl) }; - - diff --git a/drivers/media/video/msm/s5k3e2fx.c b/drivers/media/video/msm/s5k3e2fx.c index b0cf2d2a6dc9e..7f4546ff61237 100644 --- a/drivers/media/video/msm/s5k3e2fx.c +++ b/drivers/media/video/msm/s5k3e2fx.c @@ -9,148 +9,1410 @@ #include #include #include +#include #include #include -#include "s5k3e2fx.h" +#include +#include + +static uint16_t g_usModuleVersion; /*0: rev.4, 1: rev.5 */ + +/* prepare for modify PCLK*/ +#define REG_PLL_MULTIPLIER_LSB_VALUE 0x90 +/* 0xA0 for PCLK=80MHz */ +/* 0x90 for PCLK=72MHz */ + +/* prepare for modify initial gain*/ +#define REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB_VALUE 0x80 #define S5K3E2FX_REG_MODEL_ID 0x0000 -#define S5K3E2FX_MODEL_ID 0x3E2F +#define S5K3E2FX_MODEL_ID 0x3E2F + +#define S5K3E2FX_REG_MODULE_VER 0x0002 + +#define S5K3E2FX_DEF_MCLK 24000000 + +#define S5K3E2FX_QTR_SIZE_WIDTH 1296 +#define S5K3E2FX_QTR_SIZE_HEIGHT 972 + +#define S5K3E2FX_FULL_SIZE_WIDTH 2608 +#define S5K3E2FX_FULL_SIZE_HEIGHT 1960 + +/* AEC_FLASHING */ +#define REG_GROUPED_PARAMETER_HOLD 0x0104 +#define GROUPED_PARAMETER_HOLD 0x01 +#define GROUPED_PARAMETER_UPDATE 0x00 /* PLL Registers */ -#define REG_PRE_PLL_CLK_DIV 0x0305 -#define REG_PLL_MULTIPLIER_MSB 0x0306 -#define REG_PLL_MULTIPLIER_LSB 0x0307 -#define REG_VT_PIX_CLK_DIV 0x0301 -#define REG_VT_SYS_CLK_DIV 0x0303 -#define REG_OP_PIX_CLK_DIV 0x0309 -#define REG_OP_SYS_CLK_DIV 0x030B +#define REG_PRE_PLL_CLK_DIV 0x0305 +#define REG_PLL_MULTIPLIER_MSB 0x0306 +#define REG_PLL_MULTIPLIER_LSB 0x0307 +#define REG_VT_PIX_CLK_DIV 0x0301 +#define REG_VT_SYS_CLK_DIV 0x0303 +#define REG_OP_PIX_CLK_DIV 0x0309 +#define REG_OP_SYS_CLK_DIV 0x030B /* Data Format Registers */ -#define REG_CCP_DATA_FORMAT_MSB 0x0112 -#define REG_CCP_DATA_FORMAT_LSB 0x0113 +#define REG_CCP_DATA_FORMAT_MSB 0x0112 +#define REG_CCP_DATA_FORMAT_LSB 0x0113 /* Output Size */ -#define REG_X_OUTPUT_SIZE_MSB 0x034C -#define REG_X_OUTPUT_SIZE_LSB 0x034D -#define REG_Y_OUTPUT_SIZE_MSB 0x034E -#define REG_Y_OUTPUT_SIZE_LSB 0x034F +#define REG_X_OUTPUT_SIZE_MSB 0x034C +#define REG_X_OUTPUT_SIZE_LSB 0x034D +#define REG_Y_OUTPUT_SIZE_MSB 0x034E +#define REG_Y_OUTPUT_SIZE_LSB 0x034F /* Binning */ -#define REG_X_EVEN_INC 0x0381 -#define REG_X_ODD_INC 0x0383 -#define REG_Y_EVEN_INC 0x0385 -#define REG_Y_ODD_INC 0x0387 +#define REG_X_EVEN_INC 0x0381 +#define REG_X_ODD_INC 0x0383 +#define REG_Y_EVEN_INC 0x0385 +#define REG_Y_ODD_INC 0x0387 /*Reserved register */ -#define REG_BINNING_ENABLE 0x3014 +#define REG_BINNING_ENABLE 0x3014 /* Frame Fotmat */ -#define REG_FRAME_LENGTH_LINES_MSB 0x0340 -#define REG_FRAME_LENGTH_LINES_LSB 0x0341 -#define REG_LINE_LENGTH_PCK_MSB 0x0342 -#define REG_LINE_LENGTH_PCK_LSB 0x0343 +#define REG_FRAME_LENGTH_LINES_MSB 0x0340 +#define REG_FRAME_LENGTH_LINES_LSB 0x0341 +#define REG_LINE_LENGTH_PCK_MSB 0x0342 +#define REG_LINE_LENGTH_PCK_LSB 0x0343 /* MSR setting */ /* Reserved registers */ -#define REG_SHADE_CLK_ENABLE 0x30AC -#define REG_SEL_CCP 0x30C4 -#define REG_VPIX 0x3024 -#define REG_CLAMP_ON 0x3015 -#define REG_OFFSET 0x307E +#define REG_SHADE_CLK_ENABLE 0x30AC +#define REG_SEL_CCP 0x30C4 +#define REG_VPIX 0x3024 +#define REG_CLAMP_ON 0x3015 +#define REG_OFFSET 0x307E /* CDS timing settings */ /* Reserved registers */ -#define REG_LD_START 0x3000 -#define REG_LD_END 0x3001 -#define REG_SL_START 0x3002 -#define REG_SL_END 0x3003 -#define REG_RX_START 0x3004 -#define REG_S1_START 0x3005 -#define REG_S1_END 0x3006 -#define REG_S1S_START 0x3007 -#define REG_S1S_END 0x3008 -#define REG_S3_START 0x3009 -#define REG_S3_END 0x300A -#define REG_CMP_EN_START 0x300B -#define REG_CLP_SL_START 0x300C -#define REG_CLP_SL_END 0x300D -#define REG_OFF_START 0x300E -#define REG_RMP_EN_START 0x300F -#define REG_TX_START 0x3010 -#define REG_TX_END 0x3011 -#define REG_STX_WIDTH 0x3012 -#define REG_TYPE1_AF_ENABLE 0x3130 -#define DRIVER_ENABLED 0x0001 -#define AUTO_START_ENABLED 0x0010 -#define REG_NEW_POSITION 0x3131 -#define REG_3152_RESERVED 0x3152 -#define REG_315A_RESERVED 0x315A +#define REG_LD_START 0x3000 +#define REG_LD_END 0x3001 +#define REG_SL_START 0x3002 +#define REG_SL_END 0x3003 +#define REG_RX_START 0x3004 +#define REG_S1_START 0x3005 +#define REG_S1_END 0x3006 +#define REG_S1S_START 0x3007 +#define REG_S1S_END 0x3008 +#define REG_S3_START 0x3009 +#define REG_S3_END 0x300A +#define REG_CMP_EN_START 0x300B +#define REG_CLP_SL_START 0x300C +#define REG_CLP_SL_END 0x300D +#define REG_OFF_START 0x300E +#define REG_RMP_EN_START 0x300F +#define REG_TX_START 0x3010 +#define REG_TX_END 0x3011 +#define REG_STX_WIDTH 0x3012 +#define REG_TYPE1_AF_ENABLE 0x3130 +#define DRIVER_ENABLED 0x0001 +#define AUTO_START_ENABLED 0x0010 +#define REG_NEW_POSITION 0x3131 +#define REG_3152_RESERVED 0x3152 +#define REG_315A_RESERVED 0x315A #define REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB 0x0204 #define REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB 0x0205 -#define REG_FINE_INTEGRATION_TIME 0x0200 -#define REG_COARSE_INTEGRATION_TIME 0x0202 +#define REG_FINE_INTEGRATION_TIME 0x0200 +#define REG_COARSE_INTEGRATION_TIME 0x0202 #define REG_COARSE_INTEGRATION_TIME_LSB 0x0203 /* Mode select register */ -#define S5K3E2FX_REG_MODE_SELECT 0x0100 -#define S5K3E2FX_MODE_SELECT_STREAM 0x01 /* start streaming */ -#define S5K3E2FX_MODE_SELECT_SW_STANDBY 0x00 /* software standby */ -#define S5K3E2FX_REG_SOFTWARE_RESET 0x0103 -#define S5K3E2FX_SOFTWARE_RESET 0x01 -#define REG_TEST_PATTERN_MODE 0x0601 +#define S5K3E2FX_REG_MODE_SELECT 0x0100 +#define S5K3E2FX_MODE_SELECT_STREAM 0x01 /* start streaming */ +#define S5K3E2FX_MODE_SELECT_SW_STANDBY 0x00 /* software standby */ +#define S5K3E2FX_REG_SOFTWARE_RESET 0x0103 +#define S5K3E2FX_SOFTWARE_RESET 0x01 +#define REG_TEST_PATTERN_MODE 0x0601 + +/* Samsung other MSR setting*/ +#define REG_301D_RESERVED 0x301D +#define REG_3028_RESERVED 0x3028 +#define REG_3070_RESERVED 0x3070 +#define REG_3072_RESERVED 0x3072 +#define REG_301B_RESERVED 0x301B +#define REG_30BD_RESERVED 0x30BD +#define REG_30C2_RESERVED 0x30C2 +#define REG_3051_RESERVED 0x3051 +#define REG_3029_RESERVED 0x3029 +#define REG_30BF_RESERVED 0x30BF +#define REG_3022_RESERVED 0x3022 +#define REG_3019_RESERVED 0x3019 +#define REG_3150_RESERVED 0x3150 +#define REG_3157_RESERVED 0x3157 +#define REG_3159_RESERVED 0x3159 +/* LC Preview/Snapshot difference register */ +#define REG_SH4CH_BLK_WIDTH_R 0x309E +#define REG_SH4CH_BLK_HEIGHT_R 0x309F +#define REG_SH4CH_STEP_X_R_MSB 0x30A0 +#define REG_SH4CH_STEP_X_R_LSB 0x30A1 +#define REG_SH4CH_STEP_Y_R_MSB 0x30A2 +#define REG_SH4CH_STEP_Y_R_LSB 0x30A3 +#define REG_SH4CH_START_BLK_CNT_X_R 0x30A4 +#define REG_SH4CH_START_BLK_INT_X_R 0x30A5 +#define REG_SH4CH_START_FRAC_X_R_MSB 0x30A6 +#define REG_SH4CH_START_FRAC_X_R_LSB 0x30A7 +#define REG_SH4CH_START_BLK_CNT_Y_R 0x30A8 +#define REG_SH4CH_START_BLK_INT_Y_R 0x30A9 +#define REG_SH4CH_START_FRAC_Y_R_MSB 0x30AA +#define REG_SH4CH_START_FRAC_Y_R_LSB 0x30AB +#define REG_X_ADDR_START_MSB 0x0344 +#define REG_X_ADDR_START_LSB 0x0345 +#define REG_Y_ADDR_START_MSB 0x0346 +#define REG_Y_ADDR_START_LSB 0x0347 +#define REG_X_ADDR_END_MSB 0x0348 +#define REG_X_ADDR_END_LSB 0x0349 +#define REG_Y_ADDR_END_MSB 0x034A +#define REG_Y_ADDR_END_LSB 0x034B + +#define NUM_INIT_REG 94 +#define NUM_LC_REG 434 + +struct s5k3e2fx_i2c_reg_conf { + unsigned short waddr; + unsigned char bdata; +}; + +/* Separate the EVT4/EVT5 sensor init and LC setting start */ +struct s5k3e2fx_i2c_reg_conf Init_setting[2][NUM_INIT_REG] = { +/*EVT4 */ + { + {REG_PRE_PLL_CLK_DIV, 0x06}, /* PLL setting */ + {REG_PLL_MULTIPLIER_MSB, 0x00}, + {REG_PLL_MULTIPLIER_LSB, REG_PLL_MULTIPLIER_LSB_VALUE}, + {REG_VT_PIX_CLK_DIV, 0x08}, + {REG_VT_SYS_CLK_DIV, 0x01}, + {REG_OP_PIX_CLK_DIV, 0x08}, + {REG_OP_SYS_CLK_DIV, 0x01}, +/* Data Format */ + {REG_CCP_DATA_FORMAT_MSB, 0x0a}, + {REG_CCP_DATA_FORMAT_LSB, 0x0a}, +/* Preview Output Size */ + {REG_X_OUTPUT_SIZE_MSB, 0x05}, + {REG_X_OUTPUT_SIZE_LSB, 0x10}, + {REG_Y_OUTPUT_SIZE_MSB, 0x03}, + {REG_Y_OUTPUT_SIZE_LSB, 0xcc}, + {REG_X_ADDR_START_MSB, 0x00}, + {REG_X_ADDR_START_LSB, 0x08}, + {REG_Y_ADDR_START_MSB, 0x00}, + {REG_Y_ADDR_START_LSB, 0x08}, + {REG_X_ADDR_END_MSB, 0x0a}, + {REG_X_ADDR_END_LSB, 0x27}, + {REG_Y_ADDR_END_MSB, 0x07}, + {REG_Y_ADDR_END_LSB, 0x9f}, +/* Frame format */ + {REG_FRAME_LENGTH_LINES_MSB, 0x03}, + {REG_FRAME_LENGTH_LINES_LSB, 0xe2}, + {REG_LINE_LENGTH_PCK_MSB, 0x0a}, + {REG_LINE_LENGTH_PCK_LSB, 0xac}, +/* Preview Binning */ + {REG_X_EVEN_INC, 0x01}, + {REG_X_ODD_INC, 0x01}, + {REG_Y_EVEN_INC, 0x01}, + {REG_Y_ODD_INC, 0x03}, + {REG_BINNING_ENABLE, 0x06}, +/* Samsung MSR Setting */ + {REG_SEL_CCP, 0x01}, + {REG_LD_START, 0x03}, +/* Add EVT5 sensor Samsung MSR setting, Start */ + {REG_LD_END, 0x94}, + {REG_SL_START, 0x02}, + {REG_SL_END, 0x95}, + {REG_RX_START, 0x0f}, + {REG_S1_START, 0x05}, + {REG_S1_END, 0x3c}, + {REG_S1S_START, 0x8c}, + {REG_S1S_END, 0x93}, + {REG_S3_START, 0x05}, + {REG_S3_END, 0x3a}, + {REG_CMP_EN_START, 0x10}, + {REG_CLP_SL_START, 0x02}, + {REG_CLP_SL_END, 0x3e}, + {REG_OFF_START, 0x02}, + {REG_RMP_EN_START, 0x0e}, + {REG_TX_START, 0x46}, + {REG_TX_END, 0x64}, + {REG_STX_WIDTH, 0x1e}, + {REG_CLAMP_ON, 0x00}, + {REG_301D_RESERVED, 0x3f}, + {REG_VPIX, 0x04}, + {REG_3028_RESERVED, 0x40}, + {REG_3070_RESERVED, 0xdf}, + {REG_3072_RESERVED, 0x20}, + {REG_301B_RESERVED, 0x73}, + {REG_OFFSET, 0x02}, + {REG_30BD_RESERVED, 0x06}, + {REG_30C2_RESERVED, 0x0b}, + {REG_SHADE_CLK_ENABLE, 0x81}, + {REG_3051_RESERVED, 0xe6}, + {REG_3029_RESERVED, 0x02}, + {REG_30BF_RESERVED, 0x00}, + {REG_3022_RESERVED, 0x87}, + {REG_3019_RESERVED, 0x60}, + {REG_3019_RESERVED, 0x60}, + {REG_3019_RESERVED, 0x60}, + {REG_3019_RESERVED, 0x60}, + {REG_3019_RESERVED, 0x60}, + {REG_3019_RESERVED, 0x60}, + {REG_3152_RESERVED, 0x08}, + {REG_3150_RESERVED, 0x50}, /* from 0x40 to 0x50 for PCLK=80MHz */ +/* Inverse PCLK = 0x50 */ + {REG_3157_RESERVED, 0x04}, /* from 0x00 to 0x04 for PCLK=80MHz */ +/* PCLK Delay offset; 0x0a will delay around 4ns at 80MHz */ + {REG_3159_RESERVED, 0x0f}, /* from 0x00 to 0x0f for PCLK=80MHz */ +/* HS, VS driving strength [3:2]=>VS, [1:0]=>HS 00:2mA, 01:4mA, 10:6mA, + * 11:8mA + */ + {REG_315A_RESERVED, 0xf0}, /* from 0x10 to 0xf0 for PCLK=80MHz */ +/* PCLK, DATA driving strength [7:6]=>data, [5:4]=>PCLK 00:2mA, 01:4mA + * 10:6mA, 11:8mA + */ +/* AEC Setting */ + {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, 0x00}, + {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB_VALUE}, + {REG_FINE_INTEGRATION_TIME, 0x02}, + {REG_COARSE_INTEGRATION_TIME, 0x03}, +/* Preview LC config Setting */ + {REG_SH4CH_BLK_WIDTH_R, 0x52}, + {REG_SH4CH_BLK_HEIGHT_R, 0x3e}, + {REG_SH4CH_STEP_X_R_MSB, 0x03}, + {REG_SH4CH_STEP_X_R_LSB, 0x1f}, + {REG_SH4CH_STEP_Y_R_MSB, 0x04}, + {REG_SH4CH_STEP_Y_R_LSB, 0x21}, + {REG_SH4CH_START_BLK_CNT_X_R, 0x04}, + {REG_SH4CH_START_BLK_INT_X_R, 0x00}, + {REG_SH4CH_START_FRAC_X_R_MSB, 0x0c}, + {REG_SH4CH_START_FRAC_X_R_LSB, 0x7c}, + {REG_SH4CH_START_BLK_CNT_Y_R, 0x04}, + {REG_SH4CH_START_BLK_INT_Y_R, 0x00}, + {REG_SH4CH_START_FRAC_Y_R_MSB, 0x10}, + {REG_SH4CH_START_FRAC_Y_R_LSB, 0x84}, + }, + +/* EVT5 */ + { + {REG_PRE_PLL_CLK_DIV, 0x06}, /* PLL setting */ + {REG_PLL_MULTIPLIER_MSB, 0x00}, + {REG_PLL_MULTIPLIER_LSB, REG_PLL_MULTIPLIER_LSB_VALUE}, + {REG_VT_PIX_CLK_DIV, 0x08}, + {REG_VT_SYS_CLK_DIV, 0x01}, + {REG_OP_PIX_CLK_DIV, 0x08}, + {REG_OP_SYS_CLK_DIV, 0x01}, +/* Data Format */ + {REG_CCP_DATA_FORMAT_MSB, 0x0a}, + {REG_CCP_DATA_FORMAT_LSB, 0x0a}, +/* Preview Output Size */ + {REG_X_OUTPUT_SIZE_MSB, 0x05}, + {REG_X_OUTPUT_SIZE_LSB, 0x10}, + {REG_Y_OUTPUT_SIZE_MSB, 0x03}, + {REG_Y_OUTPUT_SIZE_LSB, 0xcc}, + {REG_X_ADDR_START_MSB, 0x00}, + {REG_X_ADDR_START_LSB, 0x08}, + {REG_Y_ADDR_START_MSB, 0x00}, + {REG_Y_ADDR_START_LSB, 0x08}, + {REG_X_ADDR_END_MSB, 0x0a}, + {REG_X_ADDR_END_LSB, 0x27}, + {REG_Y_ADDR_END_MSB, 0x07}, + {REG_Y_ADDR_END_LSB, 0x9f}, +/* Frame format */ + {REG_FRAME_LENGTH_LINES_MSB, 0x03}, + {REG_FRAME_LENGTH_LINES_LSB, 0xe2}, + {REG_LINE_LENGTH_PCK_MSB, 0x0a}, + {REG_LINE_LENGTH_PCK_LSB, 0xac}, +/* Preview Binning */ + {REG_X_EVEN_INC, 0x01}, + {REG_X_ODD_INC, 0x01}, + {REG_Y_EVEN_INC, 0x01}, + {REG_Y_ODD_INC, 0x03}, + {REG_BINNING_ENABLE, 0x06}, +/* Samsung MSR Setting */ + {REG_SEL_CCP, 0x01}, + {REG_LD_START, 0x03}, +/* EVT5 sensor Samsung MSR setting */ + {REG_LD_END, 0x99}, + {REG_SL_START, 0x02}, + {REG_SL_END, 0x9A}, + {REG_RX_START, 0x0f}, + {REG_S1_START, 0x05}, + {REG_S1_END, 0x3c}, + {REG_S1S_START, 0x8c}, + {REG_S1S_END, 0x26}, + {REG_S3_START, 0x05}, + {REG_S3_END, 0x3a}, + {REG_CMP_EN_START, 0x10}, + {REG_CLP_SL_START, 0x02}, + {REG_CLP_SL_END, 0x3e}, + {REG_OFF_START, 0x02}, + {REG_RMP_EN_START, 0x0e}, + {REG_TX_START, 0x46}, + {REG_TX_END, 0x64}, + {REG_STX_WIDTH, 0x1e}, + {REG_CLAMP_ON, 0x00}, + {REG_301D_RESERVED, 0x3f}, + {REG_VPIX, 0x04}, + {REG_3028_RESERVED, 0x40}, + {REG_3070_RESERVED, 0xdf}, + {REG_3072_RESERVED, 0x20}, + {REG_301B_RESERVED, 0x73}, + {REG_OFFSET, 0x02}, + {REG_30BD_RESERVED, 0x06}, + {REG_30C2_RESERVED, 0x0b}, + {REG_SHADE_CLK_ENABLE, 0x81}, + {REG_3051_RESERVED, 0xe6}, + {REG_3029_RESERVED, 0x02}, + {REG_30BF_RESERVED, 0x00}, + {REG_3022_RESERVED, 0x87}, + {REG_3019_RESERVED, 0x60}, + {0x3060, 0x03}, + {0x3061, 0x6C}, + {0x3062, 0x00}, + {0x3063, 0xD6}, + {0x3023, 0x0C}, + {REG_3152_RESERVED, 0x08}, + {REG_3150_RESERVED, 0x50}, /* from 0x40 to 0x50 for PCLK=80MHz */ +/* Inverse PCLK = 0x50 */ + {REG_3157_RESERVED, 0x04}, /* from 0x00 to 0x04 for PCLK=80MHz */ +/* PCLK Delay offset; 0x0a will delay around 4ns at 80MHz */ + {REG_3159_RESERVED, 0x0f}, /* from 0x00 to 0x0f for PCLK=80MHz */ +/* HS, VS driving strength [3:2]=>VS, [1:0]=>HS 00:2mA, 01:4mA, 10:6mA, + * 11:8mA + */ + {REG_315A_RESERVED, 0xf0}, /* from 0x10 to 0xf0 for PCLK=80MHz */ +/* PCLK, DATA driving strength [7:6]=>data, [5:4]=>PCLK 00:2mA, 01:4mA, + * 10:6mA, 11:8mA + */ +/* AEC Setting */ + {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, 0x00}, + {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB_VALUE}, + {REG_FINE_INTEGRATION_TIME, 0x02}, + {REG_COARSE_INTEGRATION_TIME, 0x03}, +/* Preview LC config Setting */ + {REG_SH4CH_BLK_WIDTH_R, 0x52}, + {REG_SH4CH_BLK_HEIGHT_R, 0x3e}, + {REG_SH4CH_STEP_X_R_MSB, 0x03}, + {REG_SH4CH_STEP_X_R_LSB, 0x1f}, + {REG_SH4CH_STEP_Y_R_MSB, 0x04}, + {REG_SH4CH_STEP_Y_R_LSB, 0x21}, + {REG_SH4CH_START_BLK_CNT_X_R, 0x04}, + {REG_SH4CH_START_BLK_INT_X_R, 0x00}, + {REG_SH4CH_START_FRAC_X_R_MSB, 0x0c}, + {REG_SH4CH_START_FRAC_X_R_LSB, 0x7c}, + {REG_SH4CH_START_BLK_CNT_Y_R, 0x04}, + {REG_SH4CH_START_BLK_INT_Y_R, 0x00}, + {REG_SH4CH_START_FRAC_Y_R_MSB, 0x10}, + {REG_SH4CH_START_FRAC_Y_R_LSB, 0x84}, + } +}; + +struct s5k3e2fx_i2c_reg_conf lc_setting[2][NUM_LC_REG] = { +/*EVT4 */ + { +/*LC setting Start */ + {0x3200, 0x00}, /* 09011 modify LC setting (t75-r82) setting */ + {0x3201, 0x8a}, + {0x3202, 0xa7}, + {0x3203, 0x0f}, + {0x3204, 0xe1}, + {0x3205, 0x9e}, + {0x3206, 0x00}, + {0x3207, 0x12}, + {0x3208, 0xfc}, + {0x3209, 0x0f}, + {0x320a, 0xfb}, + {0x320b, 0xd9}, + {0x320c, 0x00}, + {0x320d, 0x01}, + {0x320e, 0x07}, + {0x320f, 0x00}, + {0x3210, 0x00}, + {0x3211, 0x52}, + {0x3212, 0x0f}, + {0x3213, 0xd5}, + {0x3214, 0x04}, + {0x3215, 0x00}, + {0x3216, 0x0e}, + {0x3217, 0x7e}, + {0x3218, 0x0f}, + {0x3219, 0xf3}, + {0x321a, 0x05}, + {0x321b, 0x00}, + {0x321c, 0x06}, + {0x321d, 0xe2}, + {0x321e, 0x0f}, + {0x321f, 0xff}, + {0x3220, 0x65}, + {0x3221, 0x0f}, + {0x3222, 0xfe}, + {0x3223, 0x1c}, + {0x3224, 0x00}, + {0x3225, 0x1c}, + {0x3226, 0x3c}, + {0x3227, 0x0f}, + {0x3228, 0xe1}, + {0x3229, 0x63}, + {0x322a, 0x00}, + {0x322b, 0x10}, + {0x322c, 0xd1}, + {0x322d, 0x00}, + {0x322e, 0x02}, + {0x322f, 0x6b}, + {0x3230, 0x0f}, + {0x3231, 0xf5}, + {0x3232, 0x25}, + {0x3233, 0x00}, + {0x3234, 0x08}, + {0x3235, 0xc8}, + {0x3236, 0x0f}, + {0x3237, 0xfd}, + {0x3238, 0xd4}, + {0x3239, 0x00}, + {0x323a, 0x20}, + {0x323b, 0xd1}, + {0x323c, 0x0f}, + {0x323d, 0xf3}, + {0x323e, 0x3c}, + {0x323f, 0x0f}, + {0x3240, 0xf7}, + {0x3241, 0x31}, + {0x3242, 0x00}, + {0x3243, 0x0c}, + {0x3244, 0xc9}, + {0x3245, 0x0f}, + {0x3246, 0xf7}, + {0x3247, 0x90}, + {0x3248, 0x0f}, + {0x3249, 0xed}, + {0x324a, 0xdf}, + {0x324b, 0x0f}, + {0x324c, 0xfa}, + {0x324d, 0x9b}, + {0x324e, 0x0f}, + {0x324f, 0xfa}, + {0x3250, 0x8b}, + {0x3251, 0x00}, + {0x3252, 0x0a}, + {0x3253, 0x3d}, + {0x3254, 0x00}, + {0x3255, 0x00}, + {0x3256, 0x99}, + {0x3257, 0x0f}, + {0x3258, 0xf9}, + {0x3259, 0xa2}, + {0x325a, 0x00}, + {0x325b, 0x18}, + {0x325c, 0xdd}, + {0x325d, 0x0f}, + {0x325e, 0xeb}, + {0x325f, 0x42}, + {0x3260, 0x00}, + {0x3261, 0x17}, + {0x3262, 0x7c}, + {0x3263, 0x0f}, + {0x3264, 0xef}, + {0x3265, 0x19}, + {0x3266, 0x0f}, + {0x3267, 0xfe}, + {0x3268, 0x3d}, + {0x3269, 0x00}, + {0x326a, 0x0c}, + {0x326b, 0x89}, + {0x326c, 0x00}, + {0x326d, 0x88}, + {0x326e, 0xff}, + {0x326f, 0x0f}, + {0x3270, 0xe3}, + {0x3271, 0x36}, + {0x3272, 0x00}, + {0x3273, 0x11}, + {0x3274, 0xdf}, + {0x3275, 0x0f}, + {0x3276, 0xfc}, + {0x3277, 0x9a}, + {0x3278, 0x00}, + {0x3279, 0x01}, + {0x327a, 0x09}, + {0x327b, 0x0f}, + {0x327c, 0xff}, + {0x327d, 0x97}, + {0x327e, 0x0f}, + {0x327f, 0xd8}, + {0x3280, 0x0f}, + {0x3281, 0x00}, + {0x3282, 0x0b}, + {0x3283, 0x39}, + {0x3284, 0x0f}, + {0x3285, 0xf3}, + {0x3286, 0x13}, + {0x3287, 0x00}, + {0x3288, 0x08}, + {0x3289, 0x1d}, + {0x328a, 0x00}, + {0x328b, 0x02}, + {0x328c, 0xa4}, + {0x328d, 0x0f}, + {0x328e, 0xf9}, + {0x328f, 0x71}, + {0x3290, 0x00}, + {0x3291, 0x16}, + {0x3292, 0x77}, + {0x3293, 0x0f}, + {0x3294, 0xe6}, + {0x3295, 0x88}, + {0x3296, 0x00}, + {0x3297, 0x15}, + {0x3298, 0x06}, + {0x3299, 0x0f}, + {0x329a, 0xfb}, + {0x329b, 0xdb}, + {0x329c, 0x0f}, + {0x329d, 0xec}, + {0x329e, 0x3c}, + {0x329f, 0x00}, + {0x32a0, 0x17}, + {0x32a1, 0xcc}, + {0x32a2, 0x00}, + {0x32a3, 0x03}, + {0x32a4, 0x25}, + {0x32a5, 0x00}, + {0x32a6, 0x1a}, + {0x32a7, 0x9b}, + {0x32a8, 0x0f}, + {0x32a9, 0xed}, + {0x32aa, 0x0b}, + {0x32ab, 0x0f}, + {0x32ac, 0xfe}, + {0x32ad, 0xe2}, + {0x32ae, 0x00}, + {0x32af, 0x1a}, + {0x32b0, 0xc6}, + {0x32b1, 0x0f}, + {0x32b2, 0xe2}, + {0x32b3, 0xb1}, + {0x32b4, 0x0f}, + {0x32b5, 0xf0}, + {0x32b6, 0x7c}, + {0x32b7, 0x0f}, + {0x32b8, 0xfe}, + {0x32b9, 0xd0}, + {0x32ba, 0x0f}, + {0x32bb, 0xfd}, + {0x32bc, 0xc4}, + {0x32bd, 0x00}, + {0x32be, 0x06}, + {0x32bf, 0x4d}, + {0x32c0, 0x0f}, + {0x32c1, 0xf9}, + {0x32c2, 0xbe}, + {0x32c3, 0x00}, + {0x32c4, 0x02}, + {0x32c5, 0x58}, + {0x32c6, 0x00}, + {0x32c7, 0x11}, + {0x32c8, 0x60}, + {0x32c9, 0x0f}, + {0x32ca, 0xeb}, + {0x32cb, 0x60}, + {0x32cc, 0x00}, + {0x32cd, 0x16}, + {0x32ce, 0x25}, + {0x32cf, 0x0f}, + {0x32d0, 0xf2}, + {0x32d1, 0x40}, + {0x32d2, 0x0f}, + {0x32d3, 0xf7}, + {0x32d4, 0x9e}, + {0x32d5, 0x00}, + {0x32d6, 0x13}, + {0x32d7, 0xd5}, + {0x32d8, 0x00}, + {0x32d9, 0x70}, + {0x32da, 0xc7}, + {0x32db, 0x0f}, + {0x32dc, 0xec}, + {0x32dd, 0x9f}, + {0x32de, 0x00}, + {0x32df, 0x09}, + {0x32e0, 0xf3}, + {0x32e1, 0x00}, + {0x32e2, 0x00}, + {0x32e3, 0xf2}, + {0x32e4, 0x0f}, + {0x32e5, 0xff}, + {0x32e6, 0x30}, + {0x32e7, 0x0f}, + {0x32e8, 0xff}, + {0x32e9, 0x27}, + {0x32ea, 0x0f}, + {0x32eb, 0xe4}, + {0x32ec, 0x66}, + {0x32ed, 0x00}, + {0x32ee, 0x0a}, + {0x32ef, 0xe9}, + {0x32f0, 0x0f}, + {0x32f1, 0xf4}, + {0x32f2, 0xdd}, + {0x32f3, 0x00}, + {0x32f4, 0x05}, + {0x32f5, 0xd1}, + {0x32f6, 0x0f}, + {0x32f7, 0xff}, + {0x32f8, 0x29}, + {0x32f9, 0x00}, + {0x32fa, 0x00}, + {0x32fb, 0x58}, + {0x32fc, 0x00}, + {0x32fd, 0x11}, + {0x32fe, 0x32}, + {0x32ff, 0x0f}, + {0x3300, 0xe8}, + {0x3301, 0x8e}, + {0x3302, 0x00}, + {0x3303, 0x12}, + {0x3304, 0xc1}, + {0x3305, 0x0f}, + {0x3306, 0xff}, + {0x3307, 0x8f}, + {0x3308, 0x0f}, + {0x3309, 0xf4}, + {0x330a, 0x23}, + {0x330b, 0x00}, + {0x330c, 0x07}, + {0x330d, 0xc4}, + {0x330e, 0x0f}, + {0x330f, 0xfe}, + {0x3310, 0x11}, + {0x3311, 0x00}, + {0x3312, 0x1c}, + {0x3313, 0x3a}, + {0x3314, 0x0f}, + {0x3315, 0xe9}, + {0x3316, 0x73}, + {0x3317, 0x00}, + {0x3318, 0x00}, + {0x3319, 0x78}, + {0x331a, 0x00}, + {0x331b, 0x0a}, + {0x331c, 0x8b}, + {0x331d, 0x0f}, + {0x331e, 0xfc}, + {0x331f, 0x0f}, + {0x3320, 0x0f}, + {0x3321, 0xfc}, + {0x3322, 0xd3}, + {0x3323, 0x0f}, + {0x3324, 0xf6}, + {0x3325, 0x43}, + {0x3326, 0x00}, + {0x3327, 0x03}, + {0x3328, 0x06}, + {0x3329, 0x00}, + {0x332a, 0x06}, + {0x332b, 0x41}, + {0x332c, 0x00}, + {0x332d, 0x01}, + {0x332e, 0xae}, + {0x332f, 0x0f}, + {0x3330, 0xf3}, + {0x3331, 0x9a}, + {0x3332, 0x00}, + {0x3333, 0x06}, + {0x3334, 0x36}, + {0x3335, 0x0f}, + {0x3336, 0xf3}, + {0x3337, 0xde}, + {0x3338, 0x00}, + {0x3339, 0x13}, + {0x333a, 0x8e}, + {0x333b, 0x0f}, + {0x333c, 0xef}, + {0x333d, 0xfb}, + {0x333e, 0x0f}, + {0x333f, 0xfb}, + {0x3340, 0x87}, + {0x3341, 0x00}, + {0x3342, 0x11}, + {0x3343, 0x60}, + {0x3344, 0x00}, + {0x3345, 0x8b}, + {0x3346, 0xb4}, + {0x3347, 0x0f}, + {0x3348, 0xe4}, + {0x3349, 0x4e}, + {0x334a, 0x00}, + {0x334b, 0x12}, + {0x334c, 0x0e}, + {0x334d, 0x0f}, + {0x334e, 0xf8}, + {0x334f, 0xf4}, + {0x3350, 0x00}, + {0x3351, 0x07}, + {0x3352, 0x27}, + {0x3353, 0x0f}, + {0x3354, 0xfb}, + {0x3355, 0x9b}, + {0x3356, 0x0f}, + {0x3357, 0xd7}, + {0x3358, 0xc0}, + {0x3359, 0x00}, + {0x335a, 0x0c}, + {0x335b, 0x12}, + {0x335c, 0x0f}, + {0x335d, 0xf2}, + {0x335e, 0xda}, + {0x335f, 0x00}, + {0x3360, 0x0c}, + {0x3361, 0x1b}, + {0x3362, 0x0f}, + {0x3363, 0xf5}, + {0x3364, 0x6f}, + {0x3365, 0x00}, + {0x3366, 0x05}, + {0x3367, 0xd8}, + {0x3368, 0x00}, + {0x3369, 0x12}, + {0x336a, 0x7c}, + {0x336b, 0x0f}, + {0x336c, 0xe5}, + {0x336d, 0xe8}, + {0x336e, 0x00}, + {0x336f, 0x0c}, + {0x3370, 0xad}, + {0x3371, 0x00}, + {0x3372, 0x04}, + {0x3373, 0xc3}, + {0x3374, 0x0f}, + {0x3375, 0xf4}, + {0x3376, 0x91}, + {0x3377, 0x00}, + {0x3378, 0x09}, + {0x3379, 0xab}, + {0x337a, 0x00}, + {0x337b, 0x09}, + {0x337c, 0xbc}, + {0x337d, 0x00}, + {0x337e, 0x1c}, + {0x337f, 0x3b}, + {0x3380, 0x0f}, + {0x3381, 0xf7}, + {0x3382, 0x00}, + {0x3383, 0x0f}, + {0x3384, 0xf6}, + {0x3385, 0xfb}, + {0x3386, 0x00}, + {0x3387, 0x10}, + {0x3388, 0x59}, + {0x3389, 0x0f}, + {0x338a, 0xed}, + {0x338b, 0x65}, + {0x338c, 0x0f}, + {0x338d, 0xed}, + {0x338e, 0x18}, + {0x338f, 0x0f}, + {0x3390, 0xfd}, + {0x3391, 0xae}, + {0x3392, 0x0f}, + {0x3393, 0xf8}, + {0x3394, 0x35}, + {0x3395, 0x00}, + {0x3396, 0x01}, + {0x3397, 0xcb}, + {0x3398, 0x00}, + {0x3399, 0x01}, + {0x339a, 0xe8}, + {0x339b, 0x00}, + {0x339c, 0x0a}, + {0x339d, 0x5f}, + {0x339e, 0x00}, + {0x339f, 0x11}, + {0x33a0, 0xa0}, + {0x33a1, 0x0f}, + {0x33a2, 0xe9}, + {0x33a3, 0xeb}, + {0x33a4, 0x00}, + {0x33a5, 0x18}, + {0x33a6, 0x77}, + {0x33a7, 0x0f}, + {0x33a8, 0xf9}, + {0x33a9, 0x9b}, + {0x33aa, 0x0f}, + {0x33ab, 0xf8}, + {0x33ac, 0xd0}, + {0x33ad, 0x00}, + {0x33ae, 0x00}, + {0x33af, 0xd2}, + {0x309D, 0x62}, + {0x309d, 0x22}, /* shading enable */ +/* LC setting End */ + }, +/*EVT5 */ + { +/* LC setting Start */ + {0x3200, 0x00}, /* modify LC setting (t75-r82) setting */ + {0x3201, 0x8a}, + {0x3202, 0xa7}, + {0x3203, 0x0f}, + {0x3204, 0xe1}, + {0x3205, 0x9e}, + {0x3206, 0x00}, + {0x3207, 0x12}, + {0x3208, 0xfc}, + {0x3209, 0x0f}, + {0x320a, 0xfb}, + {0x320b, 0xd9}, + {0x320c, 0x00}, + {0x320d, 0x01}, + {0x320e, 0x07}, + {0x320f, 0x00}, + {0x3210, 0x00}, + {0x3211, 0x52}, + {0x3212, 0x0f}, + {0x3213, 0xd5}, + {0x3214, 0x04}, + {0x3215, 0x00}, + {0x3216, 0x0e}, + {0x3217, 0x7e}, + {0x3218, 0x0f}, + {0x3219, 0xf3}, + {0x321a, 0x05}, + {0x321b, 0x00}, + {0x321c, 0x06}, + {0x321d, 0xe2}, + {0x321e, 0x0f}, + {0x321f, 0xff}, + {0x3220, 0x65}, + {0x3221, 0x0f}, + {0x3222, 0xfe}, + {0x3223, 0x1c}, + {0x3224, 0x00}, + {0x3225, 0x1c}, + {0x3226, 0x3c}, + {0x3227, 0x0f}, + {0x3228, 0xe1}, + {0x3229, 0x63}, + {0x322a, 0x00}, + {0x322b, 0x10}, + {0x322c, 0xd1}, + {0x322d, 0x00}, + {0x322e, 0x02}, + {0x322f, 0x6b}, + {0x3230, 0x0f}, + {0x3231, 0xf5}, + {0x3232, 0x25}, + {0x3233, 0x00}, + {0x3234, 0x08}, + {0x3235, 0xc8}, + {0x3236, 0x0f}, + {0x3237, 0xfd}, + {0x3238, 0xd4}, + {0x3239, 0x00}, + {0x323a, 0x20}, + {0x323b, 0xd1}, + {0x323c, 0x0f}, + {0x323d, 0xf3}, + {0x323e, 0x3c}, + {0x323f, 0x0f}, + {0x3240, 0xf7}, + {0x3241, 0x31}, + {0x3242, 0x00}, + {0x3243, 0x0c}, + {0x3244, 0xc9}, + {0x3245, 0x0f}, + {0x3246, 0xf7}, + {0x3247, 0x90}, + {0x3248, 0x0f}, + {0x3249, 0xed}, + {0x324a, 0xdf}, + {0x324b, 0x0f}, + {0x324c, 0xfa}, + {0x324d, 0x9b}, + {0x324e, 0x0f}, + {0x324f, 0xfa}, + {0x3250, 0x8b}, + {0x3251, 0x00}, + {0x3252, 0x0a}, + {0x3253, 0x3d}, + {0x3254, 0x00}, + {0x3255, 0x00}, + {0x3256, 0x99}, + {0x3257, 0x0f}, + {0x3258, 0xf9}, + {0x3259, 0xa2}, + {0x325a, 0x00}, + {0x325b, 0x18}, + {0x325c, 0xdd}, + {0x325d, 0x0f}, + {0x325e, 0xeb}, + {0x325f, 0x42}, + {0x3260, 0x00}, + {0x3261, 0x17}, + {0x3262, 0x7c}, + {0x3263, 0x0f}, + {0x3264, 0xef}, + {0x3265, 0x19}, + {0x3266, 0x0f}, + {0x3267, 0xfe}, + {0x3268, 0x3d}, + {0x3269, 0x00}, + {0x326a, 0x0c}, + {0x326b, 0x89}, + {0x326c, 0x00}, + {0x326d, 0x88}, + {0x326e, 0xff}, + {0x326f, 0x0f}, + {0x3270, 0xe3}, + {0x3271, 0x36}, + {0x3272, 0x00}, + {0x3273, 0x11}, + {0x3274, 0xdf}, + {0x3275, 0x0f}, + {0x3276, 0xfc}, + {0x3277, 0x9a}, + {0x3278, 0x00}, + {0x3279, 0x01}, + {0x327a, 0x09}, + {0x327b, 0x0f}, + {0x327c, 0xff}, + {0x327d, 0x97}, + {0x327e, 0x0f}, + {0x327f, 0xd8}, + {0x3280, 0x0f}, + {0x3281, 0x00}, + {0x3282, 0x0b}, + {0x3283, 0x39}, + {0x3284, 0x0f}, + {0x3285, 0xf3}, + {0x3286, 0x13}, + {0x3287, 0x00}, + {0x3288, 0x08}, + {0x3289, 0x1d}, + {0x328a, 0x00}, + {0x328b, 0x02}, + {0x328c, 0xa4}, + {0x328d, 0x0f}, + {0x328e, 0xf9}, + {0x328f, 0x71}, + {0x3290, 0x00}, + {0x3291, 0x16}, + {0x3292, 0x77}, + {0x3293, 0x0f}, + {0x3294, 0xe6}, + {0x3295, 0x88}, + {0x3296, 0x00}, + {0x3297, 0x15}, + {0x3298, 0x06}, + {0x3299, 0x0f}, + {0x329a, 0xfb}, + {0x329b, 0xdb}, + {0x329c, 0x0f}, + {0x329d, 0xec}, + {0x329e, 0x3c}, + {0x329f, 0x00}, + {0x32a0, 0x17}, + {0x32a1, 0xcc}, + {0x32a2, 0x00}, + {0x32a3, 0x03}, + {0x32a4, 0x25}, + {0x32a5, 0x00}, + {0x32a6, 0x1a}, + {0x32a7, 0x9b}, + {0x32a8, 0x0f}, + {0x32a9, 0xed}, + {0x32aa, 0x0b}, + {0x32ab, 0x0f}, + {0x32ac, 0xfe}, + {0x32ad, 0xe2}, + {0x32ae, 0x00}, + {0x32af, 0x1a}, + {0x32b0, 0xc6}, + {0x32b1, 0x0f}, + {0x32b2, 0xe2}, + {0x32b3, 0xb1}, + {0x32b4, 0x0f}, + {0x32b5, 0xf0}, + {0x32b6, 0x7c}, + {0x32b7, 0x0f}, + {0x32b8, 0xfe}, + {0x32b9, 0xd0}, + {0x32ba, 0x0f}, + {0x32bb, 0xfd}, + {0x32bc, 0xc4}, + {0x32bd, 0x00}, + {0x32be, 0x06}, + {0x32bf, 0x4d}, + {0x32c0, 0x0f}, + {0x32c1, 0xf9}, + {0x32c2, 0xbe}, + {0x32c3, 0x00}, + {0x32c4, 0x02}, + {0x32c5, 0x58}, + {0x32c6, 0x00}, + {0x32c7, 0x11}, + {0x32c8, 0x60}, + {0x32c9, 0x0f}, + {0x32ca, 0xeb}, + {0x32cb, 0x60}, + {0x32cc, 0x00}, + {0x32cd, 0x16}, + {0x32ce, 0x25}, + {0x32cf, 0x0f}, + {0x32d0, 0xf2}, + {0x32d1, 0x40}, + {0x32d2, 0x0f}, + {0x32d3, 0xf7}, + {0x32d4, 0x9e}, + {0x32d5, 0x00}, + {0x32d6, 0x13}, + {0x32d7, 0xd5}, + {0x32d8, 0x00}, + {0x32d9, 0x70}, + {0x32da, 0xc7}, + {0x32db, 0x0f}, + {0x32dc, 0xec}, + {0x32dd, 0x9f}, + {0x32de, 0x00}, + {0x32df, 0x09}, + {0x32e0, 0xf3}, + {0x32e1, 0x00}, + {0x32e2, 0x00}, + {0x32e3, 0xf2}, + {0x32e4, 0x0f}, + {0x32e5, 0xff}, + {0x32e6, 0x30}, + {0x32e7, 0x0f}, + {0x32e8, 0xff}, + {0x32e9, 0x27}, + {0x32ea, 0x0f}, + {0x32eb, 0xe4}, + {0x32ec, 0x66}, + {0x32ed, 0x00}, + {0x32ee, 0x0a}, + {0x32ef, 0xe9}, + {0x32f0, 0x0f}, + {0x32f1, 0xf4}, + {0x32f2, 0xdd}, + {0x32f3, 0x00}, + {0x32f4, 0x05}, + {0x32f5, 0xd1}, + {0x32f6, 0x0f}, + {0x32f7, 0xff}, + {0x32f8, 0x29}, + {0x32f9, 0x00}, + {0x32fa, 0x00}, + {0x32fb, 0x58}, + {0x32fc, 0x00}, + {0x32fd, 0x11}, + {0x32fe, 0x32}, + {0x32ff, 0x0f}, + {0x3300, 0xe8}, + {0x3301, 0x8e}, + {0x3302, 0x00}, + {0x3303, 0x12}, + {0x3304, 0xc1}, + {0x3305, 0x0f}, + {0x3306, 0xff}, + {0x3307, 0x8f}, + {0x3308, 0x0f}, + {0x3309, 0xf4}, + {0x330a, 0x23}, + {0x330b, 0x00}, + {0x330c, 0x07}, + {0x330d, 0xc4}, + {0x330e, 0x0f}, + {0x330f, 0xfe}, + {0x3310, 0x11}, + {0x3311, 0x00}, + {0x3312, 0x1c}, + {0x3313, 0x3a}, + {0x3314, 0x0f}, + {0x3315, 0xe9}, + {0x3316, 0x73}, + {0x3317, 0x00}, + {0x3318, 0x00}, + {0x3319, 0x78}, + {0x331a, 0x00}, + {0x331b, 0x0a}, + {0x331c, 0x8b}, + {0x331d, 0x0f}, + {0x331e, 0xfc}, + {0x331f, 0x0f}, + {0x3320, 0x0f}, + {0x3321, 0xfc}, + {0x3322, 0xd3}, + {0x3323, 0x0f}, + {0x3324, 0xf6}, + {0x3325, 0x43}, + {0x3326, 0x00}, + {0x3327, 0x03}, + {0x3328, 0x06}, + {0x3329, 0x00}, + {0x332a, 0x06}, + {0x332b, 0x41}, + {0x332c, 0x00}, + {0x332d, 0x01}, + {0x332e, 0xae}, + {0x332f, 0x0f}, + {0x3330, 0xf3}, + {0x3331, 0x9a}, + {0x3332, 0x00}, + {0x3333, 0x06}, + {0x3334, 0x36}, + {0x3335, 0x0f}, + {0x3336, 0xf3}, + {0x3337, 0xde}, + {0x3338, 0x00}, + {0x3339, 0x13}, + {0x333a, 0x8e}, + {0x333b, 0x0f}, + {0x333c, 0xef}, + {0x333d, 0xfb}, + {0x333e, 0x0f}, + {0x333f, 0xfb}, + {0x3340, 0x87}, + {0x3341, 0x00}, + {0x3342, 0x11}, + {0x3343, 0x60}, + {0x3344, 0x00}, + {0x3345, 0x8b}, + {0x3346, 0xb4}, + {0x3347, 0x0f}, + {0x3348, 0xe4}, + {0x3349, 0x4e}, + {0x334a, 0x00}, + {0x334b, 0x12}, + {0x334c, 0x0e}, + {0x334d, 0x0f}, + {0x334e, 0xf8}, + {0x334f, 0xf4}, + {0x3350, 0x00}, + {0x3351, 0x07}, + {0x3352, 0x27}, + {0x3353, 0x0f}, + {0x3354, 0xfb}, + {0x3355, 0x9b}, + {0x3356, 0x0f}, + {0x3357, 0xd7}, + {0x3358, 0xc0}, + {0x3359, 0x00}, + {0x335a, 0x0c}, + {0x335b, 0x12}, + {0x335c, 0x0f}, + {0x335d, 0xf2}, + {0x335e, 0xda}, + {0x335f, 0x00}, + {0x3360, 0x0c}, + {0x3361, 0x1b}, + {0x3362, 0x0f}, + {0x3363, 0xf5}, + {0x3364, 0x6f}, + {0x3365, 0x00}, + {0x3366, 0x05}, + {0x3367, 0xd8}, + {0x3368, 0x00}, + {0x3369, 0x12}, + {0x336a, 0x7c}, + {0x336b, 0x0f}, + {0x336c, 0xe5}, + {0x336d, 0xe8}, + {0x336e, 0x00}, + {0x336f, 0x0c}, + {0x3370, 0xad}, + {0x3371, 0x00}, + {0x3372, 0x04}, + {0x3373, 0xc3}, + {0x3374, 0x0f}, + {0x3375, 0xf4}, + {0x3376, 0x91}, + {0x3377, 0x00}, + {0x3378, 0x09}, + {0x3379, 0xab}, + {0x337a, 0x00}, + {0x337b, 0x09}, + {0x337c, 0xbc}, + {0x337d, 0x00}, + {0x337e, 0x1c}, + {0x337f, 0x3b}, + {0x3380, 0x0f}, + {0x3381, 0xf7}, + {0x3382, 0x00}, + {0x3383, 0x0f}, + {0x3384, 0xf6}, + {0x3385, 0xfb}, + {0x3386, 0x00}, + {0x3387, 0x10}, + {0x3388, 0x59}, + {0x3389, 0x0f}, + {0x338a, 0xed}, + {0x338b, 0x65}, + {0x338c, 0x0f}, + {0x338d, 0xed}, + {0x338e, 0x18}, + {0x338f, 0x0f}, + {0x3390, 0xfd}, + {0x3391, 0xae}, + {0x3392, 0x0f}, + {0x3393, 0xf8}, + {0x3394, 0x35}, + {0x3395, 0x00}, + {0x3396, 0x01}, + {0x3397, 0xcb}, + {0x3398, 0x00}, + {0x3399, 0x01}, + {0x339a, 0xe8}, + {0x339b, 0x00}, + {0x339c, 0x0a}, + {0x339d, 0x5f}, + {0x339e, 0x00}, + {0x339f, 0x11}, + {0x33a0, 0xa0}, + {0x33a1, 0x0f}, + {0x33a2, 0xe9}, + {0x33a3, 0xeb}, + {0x33a4, 0x00}, + {0x33a5, 0x18}, + {0x33a6, 0x77}, + {0x33a7, 0x0f}, + {0x33a8, 0xf9}, + {0x33a9, 0x9b}, + {0x33aa, 0x0f}, + {0x33ab, 0xf8}, + {0x33ac, 0xd0}, + {0x33ad, 0x00}, + {0x33ae, 0x00}, + {0x33af, 0xd2}, + {0x309D, 0x62}, + {0x309d, 0x22}, /* shading enable */ + /*LC setting End */ + } +}; /* lc_setting} */ + +static struct wake_lock s5k3e2fx_wake_lock; + +static inline void init_suspend(void) +{ + wake_lock_init(&s5k3e2fx_wake_lock, WAKE_LOCK_IDLE, "s5k3e2fx"); +} + +static inline void deinit_suspend(void) +{ + wake_lock_destroy(&s5k3e2fx_wake_lock); +} + +static inline void prevent_suspend(void) +{ + wake_lock(&s5k3e2fx_wake_lock); +} + +static inline void allow_suspend(void) +{ + wake_unlock(&s5k3e2fx_wake_lock); +} struct reg_struct { - uint8_t pre_pll_clk_div; /* 0x0305 */ - uint8_t pll_multiplier_msb; /* 0x0306 */ - uint8_t pll_multiplier_lsb; /* 0x0307 */ - uint8_t vt_pix_clk_div; /* 0x0301 */ - uint8_t vt_sys_clk_div; /* 0x0303 */ - uint8_t op_pix_clk_div; /* 0x0309 */ - uint8_t op_sys_clk_div; /* 0x030B */ - uint8_t ccp_data_format_msb; /* 0x0112 */ - uint8_t ccp_data_format_lsb; /* 0x0113 */ - uint8_t x_output_size_msb; /* 0x034C */ - uint8_t x_output_size_lsb; /* 0x034D */ - uint8_t y_output_size_msb; /* 0x034E */ - uint8_t y_output_size_lsb; /* 0x034F */ - uint8_t x_even_inc; /* 0x0381 */ - uint8_t x_odd_inc; /* 0x0383 */ - uint8_t y_even_inc; /* 0x0385 */ - uint8_t y_odd_inc; /* 0x0387 */ - uint8_t binning_enable; /* 0x3014 */ - uint8_t frame_length_lines_msb; /* 0x0340 */ - uint8_t frame_length_lines_lsb; /* 0x0341 */ - uint8_t line_length_pck_msb; /* 0x0342 */ - uint8_t line_length_pck_lsb; /* 0x0343 */ - uint8_t shade_clk_enable ; /* 0x30AC */ - uint8_t sel_ccp; /* 0x30C4 */ - uint8_t vpix; /* 0x3024 */ - uint8_t clamp_on; /* 0x3015 */ - uint8_t offset; /* 0x307E */ - uint8_t ld_start; /* 0x3000 */ - uint8_t ld_end; /* 0x3001 */ - uint8_t sl_start; /* 0x3002 */ - uint8_t sl_end; /* 0x3003 */ - uint8_t rx_start; /* 0x3004 */ - uint8_t s1_start; /* 0x3005 */ - uint8_t s1_end; /* 0x3006 */ - uint8_t s1s_start; /* 0x3007 */ - uint8_t s1s_end; /* 0x3008 */ - uint8_t s3_start; /* 0x3009 */ - uint8_t s3_end; /* 0x300A */ - uint8_t cmp_en_start; /* 0x300B */ - uint8_t clp_sl_start; /* 0x300C */ - uint8_t clp_sl_end; /* 0x300D */ - uint8_t off_start; /* 0x300E */ - uint8_t rmp_en_start; /* 0x300F */ - uint8_t tx_start; /* 0x3010 */ - uint8_t tx_end; /* 0x3011 */ - uint8_t stx_width; /* 0x3012 */ - uint8_t reg_3152_reserved; /* 0x3152 */ - uint8_t reg_315A_reserved; /* 0x315A */ - uint8_t analogue_gain_code_global_msb; /* 0x0204 */ - uint8_t analogue_gain_code_global_lsb; /* 0x0205 */ - uint8_t fine_integration_time; /* 0x0200 */ - uint8_t coarse_integration_time; /* 0x0202 */ +/* PLL setting */ + uint8_t pre_pll_clk_div; /* 0x0305 */ + uint8_t pll_multiplier_msb; /* 0x0306 */ + uint8_t pll_multiplier_lsb; /* 0x0307 */ + uint8_t vt_pix_clk_div; /* 0x0301 */ + uint8_t vt_sys_clk_div; /* 0x0303 */ + uint8_t op_pix_clk_div; /* 0x0309 */ + uint8_t op_sys_clk_div; /* 0x030B */ +/* Data Format */ + uint8_t ccp_data_format_msb; /* 0x0112 */ + uint8_t ccp_data_format_lsb; /* 0x0113 */ +/* Preview Output Size */ + uint8_t x_output_size_msb; /* 0x034C */ + uint8_t x_output_size_lsb; /* 0x034D */ + uint8_t y_output_size_msb; /* 0x034E */ + uint8_t y_output_size_lsb; /* 0x034F */ +/* add the X-Y addr setting position */ + uint8_t x_addr_start_MSB; /* 0x0344 */ + uint8_t x_addr_start_LSB; /* 0x0345 */ + uint8_t y_addr_start_MSB; /* 0x0346 */ + uint8_t y_addr_start_LSB; /* 0x0347 */ + uint8_t x_addr_end_MSB; /* 0x0348 */ + uint8_t x_addr_end_LSB; /* 0x0349 */ + uint8_t y_addr_end_MSB; /* 0x034A */ + uint8_t y_addr_end_LSB; /* 0x034B */ +/* change the setting position */ +/* Frame format */ + uint8_t frame_length_lines_msb; /* 0x0340 */ + uint8_t frame_length_lines_lsb; /* 0x0341 */ + uint8_t line_length_pck_msb; /* 0x0342 */ + uint8_t line_length_pck_lsb; /* 0x0343 */ +/* binning */ + uint8_t x_even_inc; /* 0x0381 */ + uint8_t x_odd_inc; /* 0x0383 */ + uint8_t y_even_inc; /* 0x0385 */ + uint8_t y_odd_inc; /* 0x0387 */ + uint8_t binning_enable; /* 0x3014 */ +/* Samsung MSR Setting */ + uint8_t sel_ccp; /* 0x30C4 */ + uint8_t ld_start; /* 0x3000 */ + uint8_t ld_end; /* 0x3001 */ + uint8_t sl_start; /* 0x3002 */ + uint8_t sl_end; /* 0x3003 */ + uint8_t rx_start; /* 0x3004 */ + uint8_t s1_start; /* 0x3005 */ + uint8_t s1_end; /* 0x3006 */ + uint8_t s1s_start; /* 0x3007 */ + uint8_t s1s_end; /* 0x3008 */ + uint8_t s3_start; /* 0x3009 */ + uint8_t s3_end; /* 0x300A */ + uint8_t cmp_en_start; /* 0x300B */ + uint8_t clp_sl_start; /* 0x300C */ + uint8_t clp_sl_end; /* 0x300D */ + uint8_t off_start; /* 0x300E */ + uint8_t rmp_en_start; /* 0x300F */ + uint8_t tx_start; /* 0x3010 */ + uint8_t tx_end; /* 0x3011 */ + uint8_t stx_width; /* 0x3012 */ +/* Samsung other MSR setting */ + uint8_t clamp_on; /* 0x3015 */ + uint8_t reg_301d_reserved; /* 0x301D */ + uint8_t vpix; /* 0x3024 */ + uint8_t reg_3028_reserved; /* 0x3028 */ + uint8_t reg_3070_reserved; /* 0x3070 */ + uint8_t reg_3072_reserved; /* 0x3072 */ + uint8_t reg_301b_reserved; /* 0x301B */ + uint8_t offset; /* 0x307E */ + uint8_t reg_30bd_reserved; /* 0x30BD */ + uint8_t reg_30c2_reserved; /* 0x30C2 */ + uint8_t shade_clk_enable; /* 0x30AC */ + uint8_t reg_3051_reserved; /* 0x3051 */ + uint8_t reg_3029_reserved; /* 0x3029 */ + uint8_t reg_30bf_reserved; /* 0x30BF */ + uint8_t reg_3022_reserved; /* 0x3022 */ + uint8_t reg_3019_reserved; /* 0x3019 */ +/* end: Samsung other MSR setting */ + uint8_t reg_3152_reserved; /* 0x3152 */ +/* Samsung signal output setting */ + uint8_t reg_3150_reserved; /* 0x3150 */ + uint8_t reg_3157_reserved; /* 0x3157 */ + uint8_t reg_3159_reserved; /* 0x3159 */ +/* end: Samsung signal output setting */ + uint8_t reg_315A_reserved; /* 0x315A */ +/* AEC Setting */ + uint8_t analogue_gain_code_global_msb; /* 0x0204 */ + uint8_t analogue_gain_code_global_lsb; /* 0x0205 */ + uint8_t fine_integration_time; /* 0x0200 */ + uint8_t coarse_integration_time; /* 0x0202 */ +/* LC Preview/Snapshot difference register */ +/* Preview LC Setting */ + uint8_t sh4ch_blk_width_r; /* 0x309E */ + uint8_t sh4ch_blk_height_r; /* 0x309F */ + uint8_t sh4ch_step_x_r_MSB; /* 0x30A0 */ + uint8_t sh4ch_step_x_r_LSB; /* 0x30A1 */ + uint8_t sh4ch_step_y_r_MSB; /* 0x30A2 */ + uint8_t sh4ch_step_y_r_LSB; /* 0x30A3 */ + uint8_t sh4ch_start_blk_cnt_x_r; /* 0x30A4 */ + uint8_t sh4ch_start_blk_int_x_r; /* 0x30A5 */ + uint8_t sh4ch_start_frac_x_r_MSB; /* 0x30A6 */ + uint8_t sh4ch_start_frac_x_r_LSB; /* 0x30A7 */ + uint8_t sh4ch_start_blk_cnt_y_r; /* 0x30A8 */ + uint8_t sh4ch_start_blk_int_y_r; /* 0x30A9 */ + uint8_t sh4ch_start_frac_y_r_MSB; /* 0x30AA */ + uint8_t sh4ch_start_frac_y_r_LSB; /* 0x30AB */ +/* end: LC Preview/Snapshot difference register */ uint32_t size_h; uint32_t blk_l; uint32_t size_w; @@ -158,128 +1420,243 @@ struct reg_struct { }; struct reg_struct s5k3e2fx_reg_pat[2] = { - { /* Preview */ - 0x06, /* pre_pll_clk_div REG=0x0305 */ - 0x00, /* pll_multiplier_msb REG=0x0306 */ - 0x88, /* pll_multiplier_lsb REG=0x0307 */ - 0x0a, /* vt_pix_clk_div REG=0x0301 */ - 0x01, /* vt_sys_clk_div REG=0x0303 */ - 0x0a, /* op_pix_clk_div REG=0x0309 */ - 0x01, /* op_sys_clk_div REG=0x030B */ - 0x0a, /* ccp_data_format_msb REG=0x0112 */ - 0x0a, /* ccp_data_format_lsb REG=0x0113 */ - 0x05, /* x_output_size_msb REG=0x034C */ - 0x10, /* x_output_size_lsb REG=0x034D */ - 0x03, /* y_output_size_msb REG=0x034E */ - 0xcc, /* y_output_size_lsb REG=0x034F */ - - /* enable binning for preview */ - 0x01, /* x_even_inc REG=0x0381 */ - 0x01, /* x_odd_inc REG=0x0383 */ - 0x01, /* y_even_inc REG=0x0385 */ - 0x03, /* y_odd_inc REG=0x0387 */ - 0x06, /* binning_enable REG=0x3014 */ - - 0x03, /* frame_length_lines_msb REG=0x0340 */ - 0xde, /* frame_length_lines_lsb REG=0x0341 */ - 0x0a, /* line_length_pck_msb REG=0x0342 */ - 0xac, /* line_length_pck_lsb REG=0x0343 */ - 0x81, /* shade_clk_enable REG=0x30AC */ - 0x01, /* sel_ccp REG=0x30C4 */ - 0x04, /* vpix REG=0x3024 */ - 0x00, /* clamp_on REG=0x3015 */ - 0x02, /* offset REG=0x307E */ - 0x03, /* ld_start REG=0x3000 */ - 0x9c, /* ld_end REG=0x3001 */ - 0x02, /* sl_start REG=0x3002 */ - 0x9e, /* sl_end REG=0x3003 */ - 0x05, /* rx_start REG=0x3004 */ - 0x0f, /* s1_start REG=0x3005 */ - 0x24, /* s1_end REG=0x3006 */ - 0x7c, /* s1s_start REG=0x3007 */ - 0x9a, /* s1s_end REG=0x3008 */ - 0x10, /* s3_start REG=0x3009 */ - 0x14, /* s3_end REG=0x300A */ - 0x10, /* cmp_en_start REG=0x300B */ - 0x04, /* clp_sl_start REG=0x300C */ - 0x26, /* clp_sl_end REG=0x300D */ - 0x02, /* off_start REG=0x300E */ - 0x0e, /* rmp_en_start REG=0x300F */ - 0x30, /* tx_start REG=0x3010 */ - 0x4e, /* tx_end REG=0x3011 */ - 0x1E, /* stx_width REG=0x3012 */ - 0x08, /* reg_3152_reserved REG=0x3152 */ - 0x10, /* reg_315A_reserved REG=0x315A */ - 0x00, /* analogue_gain_code_global_msb REG=0x0204 */ - 0x80, /* analogue_gain_code_global_lsb REG=0x0205 */ - 0x02, /* fine_integration_time REG=0x0200 */ - 0x03, /* coarse_integration_time REG=0x0202 */ - 972, - 18, - 1296, - 1436 - }, - { /* Snapshot */ - 0x06, /* pre_pll_clk_div REG=0x0305 */ - 0x00, /* pll_multiplier_msb REG=0x0306 */ - 0x88, /* pll_multiplier_lsb REG=0x0307 */ - 0x0a, /* vt_pix_clk_div REG=0x0301 */ - 0x01, /* vt_sys_clk_div REG=0x0303 */ - 0x0a, /* op_pix_clk_div REG=0x0309 */ - 0x01, /* op_sys_clk_div REG=0x030B */ - 0x0a, /* ccp_data_format_msb REG=0x0112 */ - 0x0a, /* ccp_data_format_lsb REG=0x0113 */ - 0x0a, /* x_output_size_msb REG=0x034C */ - 0x30, /* x_output_size_lsb REG=0x034D */ - 0x07, /* y_output_size_msb REG=0x034E */ - 0xa8, /* y_output_size_lsb REG=0x034F */ - - /* disable binning for snapshot */ - 0x01, /* x_even_inc REG=0x0381 */ - 0x01, /* x_odd_inc REG=0x0383 */ - 0x01, /* y_even_inc REG=0x0385 */ - 0x01, /* y_odd_inc REG=0x0387 */ - 0x00, /* binning_enable REG=0x3014 */ - - 0x07, /* frame_length_lines_msb REG=0x0340 */ - 0xb6, /* frame_length_lines_lsb REG=0x0341 */ - 0x0a, /* line_length_pck_msb REG=0x0342 */ - 0xac, /* line_length_pck_lsb REG=0x0343 */ - 0x81, /* shade_clk_enable REG=0x30AC */ - 0x01, /* sel_ccp REG=0x30C4 */ - 0x04, /* vpix REG=0x3024 */ - 0x00, /* clamp_on REG=0x3015 */ - 0x02, /* offset REG=0x307E */ - 0x03, /* ld_start REG=0x3000 */ - 0x9c, /* ld_end REG=0x3001 */ - 0x02, /* sl_start REG=0x3002 */ - 0x9e, /* sl_end REG=0x3003 */ - 0x05, /* rx_start REG=0x3004 */ - 0x0f, /* s1_start REG=0x3005 */ - 0x24, /* s1_end REG=0x3006 */ - 0x7c, /* s1s_start REG=0x3007 */ - 0x9a, /* s1s_end REG=0x3008 */ - 0x10, /* s3_start REG=0x3009 */ - 0x14, /* s3_end REG=0x300A */ - 0x10, /* cmp_en_start REG=0x300B */ - 0x04, /* clp_sl_start REG=0x300C */ - 0x26, /* clp_sl_end REG=0x300D */ - 0x02, /* off_start REG=0x300E */ - 0x0e, /* rmp_en_start REG=0x300F */ - 0x30, /* tx_start REG=0x3010 */ - 0x4e, /* tx_end REG=0x3011 */ - 0x1E, /* stx_width REG=0x3012 */ - 0x08, /* reg_3152_reserved REG=0x3152 */ - 0x10, /* reg_315A_reserved REG=0x315A */ - 0x00, /* analogue_gain_code_global_msb REG=0x0204 */ - 0x80, /* analogue_gain_code_global_lsb REG=0x0205 */ - 0x02, /* fine_integration_time REG=0x0200 */ - 0x03, /* coarse_integration_time REG=0x0202 */ - 1960, - 14, - 2608, - 124 - } + { /* Preview */ +/* PLL setting */ + 0x06, /* pre_pll_clk_div REG=0x0305 */ + 0x00, /* pll_multiplier_msb REG=0x0306 */ + REG_PLL_MULTIPLIER_LSB_VALUE, + /* pll_multiplier_lsb REG=0x0307 */ + 0x08, /* vt_pix_clk_div REG=0x0301 */ + 0x01, /* vt_sys_clk_div REG=0x0303 */ + 0x08, /* op_pix_clk_div REG=0x0309 */ + 0x01, /* op_sys_clk_div REG=0x030B */ +/* Data Format */ + 0x0a, /* ccp_data_format_msb REG=0x0112 */ + 0x0a, /* ccp_data_format_lsb REG=0x0113 */ +/* Preview Output Size */ + 0x05, /* x_output_size_msb REG=0x034C */ + 0x10, /* x_output_size_lsb REG=0x034D */ + 0x03, /* y_output_size_msb REG=0x034E */ + 0xcc, /* y_output_size_lsb REG=0x034F */ +/* X-Y addr setting position. Start */ + 0x00, /* x_addr_start_MSB REG=0x0344 */ + 0x08, /* x_addr_start_LSB REG=0x0345 */ + 0x00, /* y_addr_start_MSB REG=0x0346 */ + 0x08, /* y_addr_start_LSB REG=0x0347 */ + 0x0a, /* x_addr_end_MSB REG=0x0348 */ + 0x27, /* x_addr_end_LSB REG=0x0349 */ + 0x07, /* y_addr_end_MSB REG=0x034A */ + 0x9f, /* y_addr_end_LSB REG=0x034B */ +/* change the setting position */ +/* Frame format */ + 0x03, /* frame_length_lines_msb REG=0x0340 */ + 0xe2, /* frame_length_lines_lsb REG=0x0341 */ + 0x0a, /* line_length_pck_msb REG=0x0342 */ + 0xac, /* line_length_pck_lsb REG=0x0343 */ +/* enable binning for preview */ + 0x01, /* x_even_inc REG=0x0381 */ + 0x01, /* x_odd_inc REG=0x0383 */ + 0x01, /* y_even_inc REG=0x0385 */ + 0x03, /* y_odd_inc REG=0x0387 */ + 0x06, /* binning_enable REG=0x3014 */ +/* Samsung MSR Setting */ + 0x01, /* sel_ccp REG=0x30C4 */ + 0x03, /* ld_start REG=0x3000 */ + 0x94, /* ld_end REG=0x3001 */ + 0x02, /* sl_start REG=0x3002 */ + 0x95, /* sl_end REG=0x3003 */ + 0x0f, /* rx_start REG=0x3004 */ + 0x05, /* s1_start REG=0x3005 */ + 0x3c, /* s1_end REG=0x3006 */ + 0x8c, /* s1s_start REG=0x3007 */ + 0x93, /* s1s_end REG=0x3008 */ + 0x05, /* s3_start REG=0x3009 */ + 0x3a, /* s3_end REG=0x300A */ + 0x10, /* cmp_en_start REG=0x300B */ + 0x02, /* clp_sl_start REG=0x300C */ + 0x3e, /* clp_sl_end REG=0x300D */ + 0x02, /* off_start REG=0x300E */ + 0x0e, /* rmp_en_start REG=0x300F */ + 0x46, /* tx_start REG=0x3010 */ + 0x64, /* tx_end REG=0x3011 */ + 0x1e, /* stx_width REG=0x3012 */ +/* Samsung other MSR setting. */ + 0x00, /* clamp_on REG=0x3015 */ + 0x3f, /* reg_301d_reserved REG=0x301D */ + 0x04, /* vpix REG=0x3024 */ + 0x40, /* reg_3028_reserved REG=0x3028 */ + 0xdf, /* reg_3070_reserved REG=0x3070 */ + 0x20, /* reg_3072_reserved REG=0x3072 */ + 0x73, /* reg_3073_reserved REG=0x301B */ + 0x02, /* offset REG=0x307E */ + 0x06, /* reg_30bd_reserved REG=0x30BD */ + 0x0b, /* reg_30c2_reserved REG=0x30C2 */ + 0x81, /* shade_clk_enable REG=0x30AC */ + 0xe6, /* reg_3051_reserved REG=0x3051 */ + 0x02, /* reg_3029_reserved REG=0x3029 */ + 0x00, /* reg_30bf_reserved REG=0x30BF */ + 0x87, /* reg_3022_reserved REG=0x3022 */ + 0x60, /* reg_3019_reserved REG=0x3019 */ +/* end: Samsung other MSR setting. */ + 0x08, /* reg_3152_reserved REG=0x3152 */ + 0x50, /* reg_3150_reserved REG=0x3150 */ +/* Inverse PCLK */ + 0x04, /* reg_3157_reserved REG=0x3157 */ +/* PCLK Delay offset; 0x0a will delay around 4ns at 80MHz */ + 0x0f, /* reg_3159_reserved REG=0x3159 */ +/* HS, VS driving strength [3:2]=>VS, [1:0]=>HS 00:2mA, 01:4mA, 10:6mA, + * 11:8mA + */ + 0xf0, /* reg_315A_reserved REG=0x315A */ +/* PCLK, DATA driving strength [7:6]=>data, [5:4]=>PCLK 00:2mA, 01:4mA, 10:6mA, + * 11:8mA + */ +/* AEC Setting */ + 0x00, /* analogue_gain_code_global_msb REG=0x0204 */ + REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB_VALUE, + /* analogue_gain_code_global_lsb REG=0x0205 */ + 0x02, /* fine_integration_time REG=0x0200 */ + 0x03, /* coarse_integration_time REG=0x0202 */ +/* LC Preview/Snapshot difference register. */ +/* Preview LC config Setting */ + 0x52, /* sh4ch_blk_width_r REG=0x309E */ + 0x3e, /* sh4ch_blk_height_r REG=0x309F */ + 0x03, /* sh4ch_step_x_r_MSB REG=0x30A0 */ + 0x1f, /* sh4ch_step_x_r_LSB REG=0x30A1 */ + 0x04, /* sh4ch_step_y_r_MSB REG=0x30A2 */ + 0x21, /* sh4ch_step_y_r_LSB REG=0x30A3 */ + 0x04, /* sh4ch_start_blk_cnt_x_r REG=0x30A4 */ + 0x00, /* sh4ch_start_blk_int_x_r REG=0x30A5 */ + 0x0c, /* sh4ch_start_frac_x_r_MSB REG=0x30A6 */ + 0x7c, /* sh4ch_start_frac_x_r_LSB REG=0x30A7 */ + 0x04, /* sh4ch_start_blk_cnt_y_r REG=0x30A8 */ + 0x00, /* sh4ch_start_blk_int_y_r REG=0x30A9 */ + 0x10, /* sh4ch_start_frac_y_r_MSB REG=0x30AA */ + 0x84, /* sh4ch_start_frac_y_r_LSB REG=0x30AB */ +/* end: LC Preview/Snapshot difference register. */ + S5K3E2FX_QTR_SIZE_HEIGHT, + 18, + S5K3E2FX_QTR_SIZE_WIDTH, + 1436}, + { /* Snapshot */ +/* PLL setting */ + 0x06, /* pre_pll_clk_div REG=0x0305 */ + 0x00, /* pll_multiplier_msb REG=0x0306 */ + REG_PLL_MULTIPLIER_LSB_VALUE, + /* pll_multiplier_lsb REG=0x0307 */ + 0x08, /* vt_pix_clk_div REG=0x0301 */ + 0x01, /* vt_sys_clk_div REG=0x0303 */ + 0x08, /* op_pix_clk_div REG=0x0309 */ + 0x01, /* op_sys_clk_div REG=0x030B */ +/* Data Format */ + 0x0a, /* ccp_data_format_msb REG=0x0112 */ + 0x0a, /* ccp_data_format_lsb REG=0x0113 */ +/* Snapshot Output Size */ + 0x0a, /* x_output_size_msb REG=0x034C */ + 0x30, /* x_output_size_lsb REG=0x034D */ + 0x07, /* y_output_size_msb REG=0x034E */ + 0xa8, /* y_output_size_lsb REG=0x034F */ +/* add the X-Y addr setting position. */ + 0x00, /* x_addr_start_MSB REG=0x0344 */ + 0x00, /* x_addr_start_LSB REG=0x0345 */ + 0x00, /* y_addr_start_MSB REG=0x0346 */ + 0x00, /* y_addr_start_LSB REG=0x0347 */ + 0x0a, /* x_addr_end_MSB REG=0x0348 */ + 0x2F, /* x_addr_end_LSB REG=0x0349 */ + 0x07, /* y_addr_end_MSB REG=0x034A */ + 0xA7, /* y_addr_end_LSB REG=0x034B */ +/* Change the setting position. */ +/* Frame format */ + 0x07, /* frame_length_lines_msb REG=0x0340 */ + 0xb6, /* frame_length_lines_lsb REG=0x0341 */ + 0x0a, /* line_length_pck_msb REG=0x0342 */ + 0xac, /* line_length_pck_lsb REG=0x0343 */ +/* disable binning for snapshot */ + 0x01, /* x_even_inc REG=0x0381 */ + 0x01, /* x_odd_inc REG=0x0383 */ + 0x01, /* y_even_inc REG=0x0385 */ + 0x01, /* y_odd_inc REG=0x0387 */ + 0x00, /* binning_enable REG=0x3014 */ +/* Samsung MSR Setting */ + 0x01, /* sel_ccp REG=0x30C4 */ + 0x03, /* ld_start REG=0x3000 */ + 0x94, /* ld_end REG=0x3001 */ + 0x02, /* sl_start REG=0x3002 */ + 0x95, /* sl_end REG=0x3003 */ + 0x0f, /* rx_start REG=0x3004 */ + 0x05, /* s1_start REG=0x3005 */ + 0x3c, /* s1_end REG=0x3006 */ + 0x8c, /* s1s_start REG=0x3007 */ + 0x93, /* s1s_end REG=0x3008 */ + 0x05, /* s3_start REG=0x3009 */ + 0x3a, /* s3_end REG=0x300A */ + 0x10, /* cmp_en_start REG=0x300B */ + 0x02, /* clp_sl_start REG=0x300C */ + 0x3e, /* clp_sl_end REG=0x300D */ + 0x02, /* off_start REG=0x300E */ + 0x0e, /* rmp_en_start REG=0x300F */ + 0x46, /* tx_start REG=0x3010 */ + 0x64, /* tx_end REG=0x3011 */ + 0x1e, /* stx_width REG=0x3012 */ +/* Add Samsung other MSR setting. */ + 0x00, /* clamp_on REG=0x3015 */ + 0x3f, /* reg_301d_reserved REG=0x301D */ + 0x04, /* vpix REG=0x3024 */ + 0x40, /* reg_3028_reserved REG=0x3028 */ + 0xdf, /* reg_3070_reserved REG=0x3070 */ + 0x20, /* reg_3072_reserved REG=0x3072 */ + 0x73, /* reg_3073_reserved REG=0x301B */ + 0x02, /* offset REG=0x307E */ + 0x06, /* reg_30bd_reserved REG=0x30BD */ + 0x0b, /* reg_30c2_reserved REG=0x30C2 */ + 0x81, /* shade_clk_enable REG=0x30AC */ + 0xe6, /* reg_3051_reserved REG=0x3051 */ + 0x02, /* reg_3029_reserved REG=0x3029 */ + 0x00, /* reg_30bf_reserved REG=0x30BF */ + 0x87, /* reg_3022_reserved REG=0x3022 */ + 0x60, /* reg_3019_reserved REG=0x3019 */ +/* end: Add Samsung other MSR setting. */ + 0x08, /* reg_3152_reserved REG=0x3152 */ +/* Add Samsung signal output setting. */ + 0x50, /* reg_3150_reserved REG=0x3150 */ +/* Inverse PCLK = 0x50 */ + 0x04, /* reg_3157_reserved REG=0x3157 */ +/* PCLK Delay offset; 0x0a will delay around 4ns at 80MHz */ + 0x0f, /* reg_3159_reserved REG=0x3159 */ +/* HS, VS driving strength [3:2]=>VS, [1:0]=>HS 00:2mA, 01:4mA, 10:6mA, + * 11:8mA + */ + 0xf0, /* reg_315A_reserved REG=0x315A */ +/* PCLK, DATA driving strength [7:6]=>data, [5:4]=>PCLK 00:2mA, 01:4mA, 10:6mA, + * 11:8mA + */ +/* AEC Setting */ + 0x00, /* analogue_gain_code_global_msb REG=0x0204 */ + REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB_VALUE, + /* analogue_gain_code_global_lsb REG=0x0205 */ + 0x02, /* fine_integration_time REG=0x0200 */ + 0x03, /* coarse_integration_time REG=0x0202 */ +/* Add LC Preview/Snapshot diff register. */ +/* Snapshot LC config Setting */ + 0x52, /* sh4ch_blk_width_r REG=0x309E */ + 0x7b, /* sh4ch_blk_height_r REG=0x309F */ + 0x03, /* sh4ch_step_x_r_MSB REG=0x30A0 */ + 0x1f, /* sh4ch_step_x_r_LSB REG=0x30A1 */ + 0x02, /* sh4ch_step_y_r_MSB REG=0x30A2 */ + 0x15, /* sh4ch_step_y_r_LSB REG=0x30A3 */ + 0x00, /* sh4ch_start_blk_cnt_x_r REG=0x30A4 */ + 0x00, /* sh4ch_start_blk_int_x_r REG=0x30A5 */ + 0x00, /* sh4ch_start_frac_x_r_MSB REG=0x30A6 */ + 0x00, /* sh4ch_start_frac_x_r_LSB REG=0x30A7 */ + 0x00, /* sh4ch_start_blk_cnt_y_r REG=0x30A8 */ + 0x00, /* sh4ch_start_blk_int_y_r REG=0x30A9 */ + 0x00, /* sh4ch_start_frac_y_r_MSB REG=0x30AA */ + 0x00, /* sh4ch_start_frac_y_r_LSB REG=0x30AB */ +/* diff: Add LC Preview/Snapshot diff register. */ + S5K3E2FX_FULL_SIZE_HEIGHT, + 14, + S5K3E2FX_FULL_SIZE_WIDTH, + 124} }; struct s5k3e2fx_work { @@ -292,8 +1669,8 @@ struct s5k3e2fx_ctrl { const struct msm_camera_sensor_info *sensordata; int sensormode; - uint32_t fps_divider; /* init to 1 * 0x00000400 */ - uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */ + uint32_t fps_divider; /* init to 1 * 0x00000400 */ + uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */ uint16_t curr_lens_pos; uint16_t init_curr_lens_pos; @@ -303,102 +1680,114 @@ struct s5k3e2fx_ctrl { enum msm_s_resolution prev_res; enum msm_s_resolution pict_res; enum msm_s_resolution curr_res; - enum msm_s_test_mode set_test; -}; - -struct s5k3e2fx_i2c_reg_conf { - unsigned short waddr; - unsigned char bdata; + enum msm_s_test_mode set_test; }; static struct s5k3e2fx_ctrl *s5k3e2fx_ctrl; static DECLARE_WAIT_QUEUE_HEAD(s5k3e2fx_wait_queue); -DECLARE_MUTEX(s5k3e2fx_sem); -static int s5k3e2fx_i2c_rxdata(unsigned short saddr, unsigned char *rxdata, - int length) +#define MAX_I2C_RETRIES 20 +static int i2c_transfer_retry(struct i2c_adapter *adap, + struct i2c_msg *msgs, + int len) +{ + int i2c_retry = 0; + int ns; /* number sent */ + + while (i2c_retry++ < MAX_I2C_RETRIES) { + ns = i2c_transfer(adap, msgs, len); + if (ns == len) + break; + pr_err("%s: try %d/%d: i2c_transfer sent: %d, len %d\n", + __func__, + i2c_retry, MAX_I2C_RETRIES, ns, len); + msleep(10); + } + + return ns == len ? 0 : -EIO; +} + +static inline int s5k3e2fx_i2c_rxdata(unsigned short saddr, unsigned char *rxdata, + int length) { struct i2c_msg msgs[] = { { - .addr = saddr, + .addr = saddr, .flags = 0, - .len = 2, - .buf = rxdata, + .len = 2, + .buf = rxdata, }, { - .addr = saddr, + .addr = saddr, .flags = I2C_M_RD, - .len = length, - .buf = rxdata, + .len = length, + .buf = rxdata, }, }; - if (i2c_transfer(s5k3e2fx_client->adapter, msgs, 2) < 0) { - CDBG("s5k3e2fx_i2c_rxdata failed!\n"); - return -EIO; - } - - return 0; + return i2c_transfer_retry(s5k3e2fx_client->adapter, msgs, 2); } -static int32_t s5k3e2fx_i2c_txdata(unsigned short saddr, - unsigned char *txdata, int length) +static inline int s5k3e2fx_i2c_txdata(unsigned short saddr, + unsigned char *txdata, int length) { struct i2c_msg msg[] = { { - .addr = saddr, - .flags = 0, - .len = length, - .buf = txdata, + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, }, }; - if (i2c_transfer(s5k3e2fx_client->adapter, msg, 1) < 0) { - CDBG("s5k3e2fx_i2c_txdata failed\n"); - return -EIO; - } - - return 0; + return i2c_transfer_retry(s5k3e2fx_client->adapter, msg, 1); } -static int32_t s5k3e2fx_i2c_write_b(unsigned short saddr, unsigned short waddr, - unsigned char bdata) +static int s5k3e2fx_i2c_write_b(unsigned short saddr, unsigned short waddr, + unsigned char bdata) { - int32_t rc = -EIO; + int rc = -EFAULT; unsigned char buf[4]; memset(buf, 0, sizeof(buf)); - buf[0] = (waddr & 0xFF00)>>8; + buf[0] = (waddr & 0xFF00) >> 8; buf[1] = (waddr & 0x00FF); buf[2] = bdata; rc = s5k3e2fx_i2c_txdata(saddr, buf, 3); if (rc < 0) - CDBG("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n", - waddr, bdata); + pr_err("i2c_write_w failed, addr = 0x%x, val = 0x%x!\n", + waddr, bdata); return rc; } -static int32_t s5k3e2fx_i2c_write_table( - struct s5k3e2fx_i2c_reg_conf *reg_cfg_tbl, int num) +static int s5k3e2fx_i2c_write_table(struct s5k3e2fx_i2c_reg_conf + *reg_cfg_tbl, int num) { int i; - int32_t rc = -EIO; + int rc = -EFAULT; + CDBG("s5k3e2fx_i2c_write_table starts\n"); for (i = 0; i < num; i++) { + CDBG("%d: waddr = 0x%x, bdata = 0x%x\n", i, + (int)reg_cfg_tbl->waddr, (int)reg_cfg_tbl->bdata); + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + reg_cfg_tbl->waddr, + reg_cfg_tbl->bdata); if (rc < 0) break; reg_cfg_tbl++; } + CDBG("s5k3e2fx_i2c_write_table ends\n"); return rc; } -static int32_t s5k3e2fx_i2c_read_w(unsigned short saddr, unsigned short raddr, - unsigned short *rdata) +static int s5k3e2fx_i2c_read_w(unsigned short saddr, unsigned short raddr, + unsigned short *rdata) { - int32_t rc = 0; + int rc = 0; unsigned char buf[4]; if (!rdata) @@ -406,7 +1795,7 @@ static int32_t s5k3e2fx_i2c_read_w(unsigned short saddr, unsigned short raddr, memset(buf, 0, sizeof(buf)); - buf[0] = (raddr & 0xFF00)>>8; + buf[0] = (raddr & 0xFF00) >> 8; buf[1] = (raddr & 0x00FF); rc = s5k3e2fx_i2c_rxdata(saddr, buf, 2); @@ -416,48 +1805,93 @@ static int32_t s5k3e2fx_i2c_read_w(unsigned short saddr, unsigned short raddr, *rdata = buf[0] << 8 | buf[1]; if (rc < 0) - CDBG("s5k3e2fx_i2c_read failed!\n"); + pr_err("s5k3e2fx_i2c_read failed!\n"); return rc; } -static int s5k3e2fx_probe_init_done(const struct msm_camera_sensor_info *data) +static int s5k3e2fx_i2c_read_b(unsigned short saddr, unsigned short raddr, + unsigned short *rdata) { - gpio_direction_output(data->sensor_reset, 0); - gpio_free(data->sensor_reset); - return 0; + int rc = 0; + unsigned char buf[4]; + + if (!rdata) + return -EIO; + + memset(buf, 0, sizeof(buf)); + + buf[0] = (raddr & 0xFF00) >> 8; + buf[1] = (raddr & 0x00FF); + + rc = s5k3e2fx_i2c_rxdata(saddr, buf, 1); + if (rc < 0) + return rc; + + *rdata = buf[0]; + + if (rc < 0) + pr_err("s5k3e2fx_i2c_read failed!\n"); + + return rc; } static int s5k3e2fx_probe_init_sensor(const struct msm_camera_sensor_info *data) { - int32_t rc; + int rc; uint16_t chipid = 0; + uint16_t modulever = 0; + CDBG("s5k3e2fx: gpio_request: %d\n", data->sensor_reset); rc = gpio_request(data->sensor_reset, "s5k3e2fx"); if (!rc) gpio_direction_output(data->sensor_reset, 1); - else - goto init_probe_done; + else { + pr_err("s5k3e2fx: request GPIO(sensor_reset): %d failed\n", + data->sensor_reset); + goto init_probe_fail; + } + CDBG("s5k3e2fx: gpio_free: %d\n", data->sensor_reset); - mdelay(20); + gpio_free(data->sensor_reset); + + msleep(20); CDBG("s5k3e2fx_sensor_init(): reseting sensor.\n"); - rc = s5k3e2fx_i2c_read_w(s5k3e2fx_client->addr, - S5K3E2FX_REG_MODEL_ID, &chipid); - if (rc < 0) + rc = s5k3e2fx_i2c_read_w(s5k3e2fx_client->addr, S5K3E2FX_REG_MODEL_ID, + &chipid); + if (rc < 0) { + pr_err("s5k3e2fx: read model_id failed: %d\n", rc); goto init_probe_fail; + } + CDBG("s5k3e2fx_sensor_init(): model_id=0x%X\n", chipid); if (chipid != S5K3E2FX_MODEL_ID) { - CDBG("S5K3E2FX wrong model_id = 0x%x\n", chipid); + pr_err("S5K3E2FX wrong model_id = 0x%x\n", chipid); rc = -ENODEV; goto init_probe_fail; } + rc = s5k3e2fx_i2c_read_b(s5k3e2fx_client->addr, + S5K3E2FX_REG_MODULE_VER, &modulever); + if (rc < 0) { + pr_err("S5K3E2FX read module version failed, line=%d\n", + __LINE__); + goto init_probe_fail; + } + /* modulever = (0xF000 & modulever) >> 8; */ + modulever = 0x00F0 & modulever; + CDBG("s5k3e2fx_sensor_init(): module version=0x%X\n", modulever); + + if (modulever == 0x40) + g_usModuleVersion = 0; + else if (modulever == 0x50) + g_usModuleVersion = 1; goto init_probe_done; init_probe_fail: - s5k3e2fx_probe_init_done(data); + pr_err("s5k3e2fx: prob init sensor failed\n"); init_probe_done: return rc; } @@ -470,24 +1904,24 @@ static int s5k3e2fx_init_client(struct i2c_client *client) } static const struct i2c_device_id s5k3e2fx_i2c_id[] = { - { "s5k3e2fx", 0}, - { } + {"s5k3e2fx", 0}, + {} }; static int s5k3e2fx_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { int rc = 0; CDBG("s5k3e2fx_probe called!\n"); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - CDBG("i2c_check_functionality failed\n"); + pr_err("i2c_check_functionality failed\n"); goto probe_failure; } s5k3e2fx_sensorw = kzalloc(sizeof(struct s5k3e2fx_work), GFP_KERNEL); if (!s5k3e2fx_sensorw) { - CDBG("kzalloc failed.\n"); + pr_err("kzalloc failed\n"); rc = -ENOMEM; goto probe_failure; } @@ -496,206 +1930,355 @@ static int s5k3e2fx_i2c_probe(struct i2c_client *client, s5k3e2fx_init_client(client); s5k3e2fx_client = client; - mdelay(50); + msleep(50); CDBG("s5k3e2fx_probe successed! rc = %d\n", rc); return 0; probe_failure: - CDBG("s5k3e2fx_probe failed! rc = %d\n", rc); + pr_err("s5k3e2fx_probe failed! rc = %d\n", rc); return rc; } static struct i2c_driver s5k3e2fx_i2c_driver = { .id_table = s5k3e2fx_i2c_id, - .probe = s5k3e2fx_i2c_probe, - .remove = __exit_p(s5k3e2fx_i2c_remove), + .probe = s5k3e2fx_i2c_probe, .driver = { .name = "s5k3e2fx", }, }; -static int32_t s5k3e2fx_test(enum msm_s_test_mode mo) +static int s5k3e2fx_test(enum msm_s_test_mode mo) { - int32_t rc = 0; + int rc = 0; if (mo == S_TEST_OFF) rc = 0; else rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, - REG_TEST_PATTERN_MODE, (uint16_t)mo); + REG_TEST_PATTERN_MODE, (uint16_t) mo); return rc; } -static int32_t s5k3e2fx_setting(enum msm_s_reg_update rupdate, - enum msm_s_setting rt) +static int s5k3e2fx_setting(enum msm_s_reg_update rupdate, + enum msm_s_setting rt) { - int32_t rc = 0; + int rc = 0; uint16_t num_lperf; switch (rupdate) { - case S_UPDATE_PERIODIC: - if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) { - - struct s5k3e2fx_i2c_reg_conf tbl_1[] = { - {REG_CCP_DATA_FORMAT_MSB, s5k3e2fx_reg_pat[rt].ccp_data_format_msb}, - {REG_CCP_DATA_FORMAT_LSB, s5k3e2fx_reg_pat[rt].ccp_data_format_lsb}, - {REG_X_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].x_output_size_msb}, - {REG_X_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].x_output_size_lsb}, - {REG_Y_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].y_output_size_msb}, - {REG_Y_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].y_output_size_lsb}, - {REG_X_EVEN_INC, s5k3e2fx_reg_pat[rt].x_even_inc}, - {REG_X_ODD_INC, s5k3e2fx_reg_pat[rt].x_odd_inc}, - {REG_Y_EVEN_INC, s5k3e2fx_reg_pat[rt].y_even_inc}, - {REG_Y_ODD_INC, s5k3e2fx_reg_pat[rt].y_odd_inc}, - {REG_BINNING_ENABLE, s5k3e2fx_reg_pat[rt].binning_enable}, - }; - - struct s5k3e2fx_i2c_reg_conf tbl_2[] = { - {REG_FRAME_LENGTH_LINES_MSB, 0}, - {REG_FRAME_LENGTH_LINES_LSB, 0}, - {REG_LINE_LENGTH_PCK_MSB, s5k3e2fx_reg_pat[rt].line_length_pck_msb}, - {REG_LINE_LENGTH_PCK_LSB, s5k3e2fx_reg_pat[rt].line_length_pck_lsb}, - {REG_SHADE_CLK_ENABLE, s5k3e2fx_reg_pat[rt].shade_clk_enable}, - {REG_SEL_CCP, s5k3e2fx_reg_pat[rt].sel_ccp}, - {REG_VPIX, s5k3e2fx_reg_pat[rt].vpix}, - {REG_CLAMP_ON, s5k3e2fx_reg_pat[rt].clamp_on}, - {REG_OFFSET, s5k3e2fx_reg_pat[rt].offset}, - {REG_LD_START, s5k3e2fx_reg_pat[rt].ld_start}, - {REG_LD_END, s5k3e2fx_reg_pat[rt].ld_end}, - {REG_SL_START, s5k3e2fx_reg_pat[rt].sl_start}, - {REG_SL_END, s5k3e2fx_reg_pat[rt].sl_end}, - {REG_RX_START, s5k3e2fx_reg_pat[rt].rx_start}, - {REG_S1_START, s5k3e2fx_reg_pat[rt].s1_start}, - {REG_S1_END, s5k3e2fx_reg_pat[rt].s1_end}, - {REG_S1S_START, s5k3e2fx_reg_pat[rt].s1s_start}, - {REG_S1S_END, s5k3e2fx_reg_pat[rt].s1s_end}, - {REG_S3_START, s5k3e2fx_reg_pat[rt].s3_start}, - {REG_S3_END, s5k3e2fx_reg_pat[rt].s3_end}, - {REG_CMP_EN_START, s5k3e2fx_reg_pat[rt].cmp_en_start}, - {REG_CLP_SL_START, s5k3e2fx_reg_pat[rt].clp_sl_start}, - {REG_CLP_SL_END, s5k3e2fx_reg_pat[rt].clp_sl_end}, - {REG_OFF_START, s5k3e2fx_reg_pat[rt].off_start}, - {REG_RMP_EN_START, s5k3e2fx_reg_pat[rt].rmp_en_start}, - {REG_TX_START, s5k3e2fx_reg_pat[rt].tx_start}, - {REG_TX_END, s5k3e2fx_reg_pat[rt].tx_end}, - {REG_STX_WIDTH, s5k3e2fx_reg_pat[rt].stx_width}, - {REG_3152_RESERVED, s5k3e2fx_reg_pat[rt].reg_3152_reserved}, - {REG_315A_RESERVED, s5k3e2fx_reg_pat[rt].reg_315A_reserved}, - {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_msb}, - {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_lsb}, - {REG_FINE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].fine_integration_time}, - {REG_COARSE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].coarse_integration_time}, - {S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_STREAM}, - }; - - rc = s5k3e2fx_i2c_write_table(&tbl_1[0], - ARRAY_SIZE(tbl_1)); - if (rc < 0) - return rc; - - num_lperf = - (uint16_t)((s5k3e2fx_reg_pat[rt].frame_length_lines_msb << 8) & 0xFF00) + - s5k3e2fx_reg_pat[rt].frame_length_lines_lsb; - - num_lperf = num_lperf * s5k3e2fx_ctrl->fps_divider / 0x0400; - - tbl_2[0] = (struct s5k3e2fx_i2c_reg_conf) {REG_FRAME_LENGTH_LINES_MSB, (num_lperf & 0xFF00) >> 8}; - tbl_2[1] = (struct s5k3e2fx_i2c_reg_conf) {REG_FRAME_LENGTH_LINES_LSB, (num_lperf & 0x00FF)}; - - rc = s5k3e2fx_i2c_write_table(&tbl_2[0], - ARRAY_SIZE(tbl_2)); - if (rc < 0) - return rc; - - mdelay(5); - - rc = s5k3e2fx_test(s5k3e2fx_ctrl->set_test); - if (rc < 0) - return rc; - } - break; /* UPDATE_PERIODIC */ - - case S_REG_INIT: - if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) { - - struct s5k3e2fx_i2c_reg_conf tbl_3[] = { - {S5K3E2FX_REG_SOFTWARE_RESET, S5K3E2FX_SOFTWARE_RESET}, - {S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_SW_STANDBY}, - /* PLL setting */ - {REG_PRE_PLL_CLK_DIV, s5k3e2fx_reg_pat[rt].pre_pll_clk_div}, - {REG_PLL_MULTIPLIER_MSB, s5k3e2fx_reg_pat[rt].pll_multiplier_msb}, - {REG_PLL_MULTIPLIER_LSB, s5k3e2fx_reg_pat[rt].pll_multiplier_lsb}, - {REG_VT_PIX_CLK_DIV, s5k3e2fx_reg_pat[rt].vt_pix_clk_div}, - {REG_VT_SYS_CLK_DIV, s5k3e2fx_reg_pat[rt].vt_sys_clk_div}, - {REG_OP_PIX_CLK_DIV, s5k3e2fx_reg_pat[rt].op_pix_clk_div}, - {REG_OP_SYS_CLK_DIV, s5k3e2fx_reg_pat[rt].op_sys_clk_div}, - /*Data Format */ - {REG_CCP_DATA_FORMAT_MSB, s5k3e2fx_reg_pat[rt].ccp_data_format_msb}, - {REG_CCP_DATA_FORMAT_LSB, s5k3e2fx_reg_pat[rt].ccp_data_format_lsb}, - /*Output Size */ - {REG_X_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].x_output_size_msb}, - {REG_X_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].x_output_size_lsb}, - {REG_Y_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt].y_output_size_msb}, - {REG_Y_OUTPUT_SIZE_LSB, s5k3e2fx_reg_pat[rt].y_output_size_lsb}, - /* Binning */ - {REG_X_EVEN_INC, s5k3e2fx_reg_pat[rt].x_even_inc}, - {REG_X_ODD_INC, s5k3e2fx_reg_pat[rt].x_odd_inc }, - {REG_Y_EVEN_INC, s5k3e2fx_reg_pat[rt].y_even_inc}, - {REG_Y_ODD_INC, s5k3e2fx_reg_pat[rt].y_odd_inc}, - {REG_BINNING_ENABLE, s5k3e2fx_reg_pat[rt].binning_enable}, - /* Frame format */ - {REG_FRAME_LENGTH_LINES_MSB, s5k3e2fx_reg_pat[rt].frame_length_lines_msb}, - {REG_FRAME_LENGTH_LINES_LSB, s5k3e2fx_reg_pat[rt].frame_length_lines_lsb}, - {REG_LINE_LENGTH_PCK_MSB, s5k3e2fx_reg_pat[rt].line_length_pck_msb}, - {REG_LINE_LENGTH_PCK_LSB, s5k3e2fx_reg_pat[rt].line_length_pck_lsb}, - /* MSR setting */ - {REG_SHADE_CLK_ENABLE, s5k3e2fx_reg_pat[rt].shade_clk_enable}, - {REG_SEL_CCP, s5k3e2fx_reg_pat[rt].sel_ccp}, - {REG_VPIX, s5k3e2fx_reg_pat[rt].vpix}, - {REG_CLAMP_ON, s5k3e2fx_reg_pat[rt].clamp_on}, - {REG_OFFSET, s5k3e2fx_reg_pat[rt].offset}, - /* CDS timing setting */ - {REG_LD_START, s5k3e2fx_reg_pat[rt].ld_start}, - {REG_LD_END, s5k3e2fx_reg_pat[rt].ld_end}, - {REG_SL_START, s5k3e2fx_reg_pat[rt].sl_start}, - {REG_SL_END, s5k3e2fx_reg_pat[rt].sl_end}, - {REG_RX_START, s5k3e2fx_reg_pat[rt].rx_start}, - {REG_S1_START, s5k3e2fx_reg_pat[rt].s1_start}, - {REG_S1_END, s5k3e2fx_reg_pat[rt].s1_end}, - {REG_S1S_START, s5k3e2fx_reg_pat[rt].s1s_start}, - {REG_S1S_END, s5k3e2fx_reg_pat[rt].s1s_end}, - {REG_S3_START, s5k3e2fx_reg_pat[rt].s3_start}, - {REG_S3_END, s5k3e2fx_reg_pat[rt].s3_end}, - {REG_CMP_EN_START, s5k3e2fx_reg_pat[rt].cmp_en_start}, - {REG_CLP_SL_START, s5k3e2fx_reg_pat[rt].clp_sl_start}, - {REG_CLP_SL_END, s5k3e2fx_reg_pat[rt].clp_sl_end}, - {REG_OFF_START, s5k3e2fx_reg_pat[rt].off_start}, - {REG_RMP_EN_START, s5k3e2fx_reg_pat[rt].rmp_en_start}, - {REG_TX_START, s5k3e2fx_reg_pat[rt].tx_start}, - {REG_TX_END, s5k3e2fx_reg_pat[rt].tx_end}, - {REG_STX_WIDTH, s5k3e2fx_reg_pat[rt].stx_width}, - {REG_3152_RESERVED, s5k3e2fx_reg_pat[rt].reg_3152_reserved}, - {REG_315A_RESERVED, s5k3e2fx_reg_pat[rt].reg_315A_reserved}, - {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_msb}, - {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, s5k3e2fx_reg_pat[rt].analogue_gain_code_global_lsb}, - {REG_FINE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].fine_integration_time}, - {REG_COARSE_INTEGRATION_TIME, s5k3e2fx_reg_pat[rt].coarse_integration_time}, - {S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_STREAM}, - }; - - /* reset fps_divider */ - s5k3e2fx_ctrl->fps_divider = 1 * 0x0400; - rc = s5k3e2fx_i2c_write_table(&tbl_3[0], - ARRAY_SIZE(tbl_3)); - if (rc < 0) - return rc; - } - break; /* case REG_INIT: */ + case S_UPDATE_PERIODIC:{ + if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) { + struct s5k3e2fx_i2c_reg_conf tbl_1[] = { + {S5K3E2FX_REG_MODE_SELECT, + S5K3E2FX_MODE_SELECT_SW_STANDBY}, + {REG_X_OUTPUT_SIZE_MSB, + s5k3e2fx_reg_pat[rt]. + x_output_size_msb}, + {REG_X_OUTPUT_SIZE_LSB, + s5k3e2fx_reg_pat[rt]. + x_output_size_lsb}, + {REG_Y_OUTPUT_SIZE_MSB, + s5k3e2fx_reg_pat[rt]. + y_output_size_msb}, + {REG_Y_OUTPUT_SIZE_LSB, + s5k3e2fx_reg_pat[rt]. + y_output_size_lsb}, + /* Start-End address */ + {REG_X_ADDR_START_MSB, + s5k3e2fx_reg_pat[rt].x_addr_start_MSB}, + {REG_X_ADDR_START_LSB, + s5k3e2fx_reg_pat[rt].x_addr_start_LSB}, + {REG_Y_ADDR_START_MSB, + s5k3e2fx_reg_pat[rt].y_addr_start_MSB}, + {REG_Y_ADDR_START_LSB, + s5k3e2fx_reg_pat[rt].y_addr_start_LSB}, + {REG_X_ADDR_END_MSB, + s5k3e2fx_reg_pat[rt].x_addr_end_MSB}, + {REG_X_ADDR_END_LSB, + s5k3e2fx_reg_pat[rt].x_addr_end_LSB}, + {REG_Y_ADDR_END_MSB, + s5k3e2fx_reg_pat[rt].y_addr_end_MSB}, + {REG_Y_ADDR_END_LSB, + s5k3e2fx_reg_pat[rt].y_addr_end_LSB}, + /* Binning */ + {REG_X_EVEN_INC, + s5k3e2fx_reg_pat[rt].x_even_inc}, + {REG_X_ODD_INC, + s5k3e2fx_reg_pat[rt].x_odd_inc}, + {REG_Y_EVEN_INC, + s5k3e2fx_reg_pat[rt].y_even_inc}, + {REG_Y_ODD_INC, + s5k3e2fx_reg_pat[rt].y_odd_inc}, + {REG_BINNING_ENABLE, + s5k3e2fx_reg_pat[rt].binning_enable}, + }; + struct s5k3e2fx_i2c_reg_conf tbl_2[] = { + {REG_FRAME_LENGTH_LINES_MSB, 0}, + {REG_FRAME_LENGTH_LINES_LSB, 0}, + {REG_LINE_LENGTH_PCK_MSB, + s5k3e2fx_reg_pat[rt]. + line_length_pck_msb}, + {REG_LINE_LENGTH_PCK_LSB, + s5k3e2fx_reg_pat[rt]. + line_length_pck_lsb}, + {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, + s5k3e2fx_reg_pat[rt]. + analogue_gain_code_global_msb}, + {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, + s5k3e2fx_reg_pat[rt]. + analogue_gain_code_global_lsb}, + {REG_FINE_INTEGRATION_TIME, + s5k3e2fx_reg_pat[rt]. + fine_integration_time}, + {REG_COARSE_INTEGRATION_TIME, + s5k3e2fx_reg_pat[rt]. + coarse_integration_time}, + /* LC Preview/Snapshot difference + * register + */ + {REG_SH4CH_BLK_WIDTH_R, + s5k3e2fx_reg_pat[rt]. + sh4ch_blk_width_r}, + {REG_SH4CH_BLK_HEIGHT_R, + s5k3e2fx_reg_pat[rt]. + sh4ch_blk_height_r}, + {REG_SH4CH_STEP_X_R_MSB, + s5k3e2fx_reg_pat[rt]. + sh4ch_step_x_r_MSB}, + {REG_SH4CH_STEP_X_R_LSB, + s5k3e2fx_reg_pat[rt]. + sh4ch_step_x_r_LSB}, + {REG_SH4CH_STEP_Y_R_MSB, + s5k3e2fx_reg_pat[rt]. + sh4ch_step_y_r_MSB}, + {REG_SH4CH_STEP_Y_R_LSB, + s5k3e2fx_reg_pat[rt]. + sh4ch_step_y_r_LSB}, + {REG_SH4CH_START_BLK_CNT_X_R, + s5k3e2fx_reg_pat[rt]. + sh4ch_start_blk_cnt_x_r}, + {REG_SH4CH_START_BLK_INT_X_R, + s5k3e2fx_reg_pat[rt]. + sh4ch_start_blk_int_x_r}, + {REG_SH4CH_START_FRAC_X_R_MSB, + s5k3e2fx_reg_pat[rt]. + sh4ch_start_frac_x_r_MSB}, + {REG_SH4CH_START_FRAC_X_R_LSB, + s5k3e2fx_reg_pat[rt]. + sh4ch_start_frac_x_r_LSB}, + {REG_SH4CH_START_BLK_CNT_Y_R, + s5k3e2fx_reg_pat[rt]. + sh4ch_start_blk_cnt_y_r}, + {REG_SH4CH_START_BLK_INT_Y_R, + s5k3e2fx_reg_pat[rt]. + sh4ch_start_blk_int_y_r}, + {REG_SH4CH_START_FRAC_Y_R_MSB, + s5k3e2fx_reg_pat[rt]. + sh4ch_start_frac_y_r_MSB}, + {REG_SH4CH_START_FRAC_Y_R_LSB, + s5k3e2fx_reg_pat[rt]. + sh4ch_start_frac_y_r_LSB}, + }; + +/* add EVT5 sensor Samsung difference MSR setting between Preview and Capture */ + + struct s5k3e2fx_i2c_reg_conf + tbl_only_for_EVT5[2][2] = { + { /* S_RES_PREVIEW */ + {0x3062, 0x00}, + {0x3063, 0xD6}, + }, + { /* S_RES_CAPTURE */ + {0x3062, 0x01}, + {0x3063, 0x16}, + } + }; + + CDBG("Binning_enable = 0x %2x" + "[s5k3e2fx.c s5k3e2fx_setting]\r\n", + s5k3e2fx_reg_pat[rt].binning_enable); + + rc = s5k3e2fx_i2c_write_table(&tbl_1[0], + ARRAY_SIZE + (tbl_1)); + if (rc < 0) { + pr_err("UPDATE_PERIODIC, tb1_1 failed"); + return rc; + } + + num_lperf = + (uint16_t) ((s5k3e2fx_reg_pat[rt]. + frame_length_lines_msb + << 8) & 0xFF00) + + s5k3e2fx_reg_pat[rt]. + frame_length_lines_lsb; + + num_lperf = + num_lperf * + s5k3e2fx_ctrl->fps_divider / 0x0400; + + tbl_2[0] = + (struct s5k3e2fx_i2c_reg_conf) { + REG_FRAME_LENGTH_LINES_MSB, + (num_lperf & 0xFF00) >> 8}; + tbl_2[1] = + (struct s5k3e2fx_i2c_reg_conf) { + REG_FRAME_LENGTH_LINES_LSB, + (num_lperf & 0x00FF)}; + + rc = s5k3e2fx_i2c_write_table(&tbl_2[0], + ARRAY_SIZE + (tbl_2)); + if (rc < 0) { + pr_err("UPDATE_PERIODIC, tb1_2 failed"); + return rc; + } + + /* only for evt5 */ + if (g_usModuleVersion == 1) { + rc = s5k3e2fx_i2c_write_table + (&tbl_only_for_EVT5[rt][0], + 2); + if (rc < 0) + return rc; + } + + /* Fixes CAMIF errors */ + if (rt == S_RES_CAPTURE) + msleep(25); + + rc = s5k3e2fx_i2c_write_b + (s5k3e2fx_client->addr, + S5K3E2FX_REG_MODE_SELECT, + S5K3E2FX_MODE_SELECT_STREAM); + if (rc < 0) + return rc; + + mdelay(5); + + rc = s5k3e2fx_test(s5k3e2fx_ctrl-> + set_test); + if (rc < 0) + return rc; + } + } + break; /* UPDATE_PERIODIC */ + + case S_REG_INIT:{ + if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) { + struct s5k3e2fx_i2c_reg_conf tbl_3[] = { +/* {S5K3E2FX_REG_SOFTWARE_RESET, S5K3E2FX_SOFTWARE_RESET},*/ + {S5K3E2FX_REG_MODE_SELECT, + S5K3E2FX_MODE_SELECT_SW_STANDBY}, + /*Output Size */ + {REG_X_OUTPUT_SIZE_MSB, + s5k3e2fx_reg_pat[rt]. + x_output_size_msb}, + {REG_X_OUTPUT_SIZE_LSB, + s5k3e2fx_reg_pat[rt]. + x_output_size_lsb}, + {REG_Y_OUTPUT_SIZE_MSB, + s5k3e2fx_reg_pat[rt]. + y_output_size_msb}, + {REG_Y_OUTPUT_SIZE_LSB, + s5k3e2fx_reg_pat[rt]. + y_output_size_lsb}, + /* Start-End address */ + {REG_X_ADDR_START_MSB, + s5k3e2fx_reg_pat[rt].x_addr_start_MSB}, + {REG_X_ADDR_START_LSB, + s5k3e2fx_reg_pat[rt].x_addr_start_LSB}, + {REG_Y_ADDR_START_MSB, + s5k3e2fx_reg_pat[rt].y_addr_start_MSB}, + {REG_Y_ADDR_START_LSB, + s5k3e2fx_reg_pat[rt].y_addr_start_LSB}, + {REG_X_ADDR_END_MSB, + s5k3e2fx_reg_pat[rt].x_addr_end_MSB}, + {REG_X_ADDR_END_LSB, + s5k3e2fx_reg_pat[rt].x_addr_end_LSB}, + {REG_Y_ADDR_END_MSB, + s5k3e2fx_reg_pat[rt].y_addr_end_MSB}, + {REG_Y_ADDR_END_LSB, + s5k3e2fx_reg_pat[rt].y_addr_end_LSB}, + /* Binning */ + {REG_X_EVEN_INC, + s5k3e2fx_reg_pat[rt].x_even_inc}, + {REG_X_ODD_INC, + s5k3e2fx_reg_pat[rt].x_odd_inc}, + {REG_Y_EVEN_INC, + s5k3e2fx_reg_pat[rt].y_even_inc}, + {REG_Y_ODD_INC, + s5k3e2fx_reg_pat[rt].y_odd_inc}, + {REG_BINNING_ENABLE, + s5k3e2fx_reg_pat[rt].binning_enable}, + /* Frame format */ + {REG_FRAME_LENGTH_LINES_MSB, + s5k3e2fx_reg_pat[rt]. + frame_length_lines_msb}, + {REG_FRAME_LENGTH_LINES_LSB, + s5k3e2fx_reg_pat[rt]. + frame_length_lines_lsb}, + {REG_LINE_LENGTH_PCK_MSB, + s5k3e2fx_reg_pat[rt]. + line_length_pck_msb}, + {REG_LINE_LENGTH_PCK_LSB, + s5k3e2fx_reg_pat[rt]. + line_length_pck_lsb}, + /* MSR setting */ + {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, + s5k3e2fx_reg_pat[rt]. + analogue_gain_code_global_msb}, + {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, + s5k3e2fx_reg_pat[rt]. + analogue_gain_code_global_lsb}, + {REG_FINE_INTEGRATION_TIME, + s5k3e2fx_reg_pat[rt]. + fine_integration_time}, + {REG_COARSE_INTEGRATION_TIME, + s5k3e2fx_reg_pat[rt]. + coarse_integration_time}, + {S5K3E2FX_REG_MODE_SELECT, + S5K3E2FX_MODE_SELECT_STREAM}, + }; + unsigned short rData = 0; + mdelay(1); + s5k3e2fx_i2c_read_b(s5k3e2fx_client-> + addr, + REG_3150_RESERVED, + &rData); + s5k3e2fx_i2c_write_b(s5k3e2fx_client-> + addr, + REG_3150_RESERVED, + (rData & 0xFFFE)); + mdelay(1); + s5k3e2fx_i2c_read_b(s5k3e2fx_client-> + addr, + REG_TYPE1_AF_ENABLE, + &rData); + s5k3e2fx_i2c_write_b(s5k3e2fx_client-> + addr, + REG_TYPE1_AF_ENABLE, + (rData | 0x0001)); + mdelay(1); + + /* reset fps_divider */ + s5k3e2fx_ctrl->fps_divider = 1 * 0x0400; + /* write REG_INIT registers */ + rc = s5k3e2fx_i2c_write_table(&tbl_3[0], + ARRAY_SIZE + (tbl_3)); + if (rc < 0) { + pr_err("REG_INIT failed, rc=%d\n", rc); + return rc; + } + } + } + break; /* REG_INIT */ default: - rc = -EINVAL; + rc = -EFAULT; break; } /* switch (rupdate) */ @@ -704,15 +2287,15 @@ static int32_t s5k3e2fx_setting(enum msm_s_reg_update rupdate, static int s5k3e2fx_sensor_open_init(const struct msm_camera_sensor_info *data) { - int32_t rc; + int rc; + CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__); s5k3e2fx_ctrl = kzalloc(sizeof(struct s5k3e2fx_ctrl), GFP_KERNEL); if (!s5k3e2fx_ctrl) { - CDBG("s5k3e2fx_init failed!\n"); + pr_err("s5k3e2fx_init failed!\n"); rc = -ENOMEM; goto init_done; } - s5k3e2fx_ctrl->fps_divider = 1 * 0x00000400; s5k3e2fx_ctrl->pict_fps_divider = 1 * 0x00000400; s5k3e2fx_ctrl->set_test = S_TEST_OFF; @@ -723,11 +2306,12 @@ static int s5k3e2fx_sensor_open_init(const struct msm_camera_sensor_info *data) s5k3e2fx_ctrl->sensordata = data; /* enable mclk first */ - msm_camio_clk_rate_set(24000000); - mdelay(20); + msm_camio_clk_rate_set(S5K3E2FX_DEF_MCLK); + + msleep(20); msm_camio_camif_pad_reg_reset(); - mdelay(20); + msleep(20); rc = s5k3e2fx_probe_init_sensor(data); if (rc < 0) @@ -739,31 +2323,48 @@ static int s5k3e2fx_sensor_open_init(const struct msm_camera_sensor_info *data) rc = s5k3e2fx_setting(S_REG_INIT, S_RES_CAPTURE); if (rc < 0) { - CDBG("s5k3e2fx_setting failed. rc = %d\n", rc); + pr_err("s5k3e2fx_setting failed. rc = %d\n", rc); goto init_fail1; } - /* initialize AF */ - if ((rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, - 0x3146, 0x3A)) < 0) - goto init_fail1; - - if ((rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, - 0x3130, 0x03)) < 0) + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + 0x3130, 0x03); + if (rc < 0) goto init_fail1; goto init_done; init_fail1: - s5k3e2fx_probe_init_done(data); kfree(s5k3e2fx_ctrl); init_done: return rc; } -static int32_t s5k3e2fx_power_down(void) +static void s5k3e2fx_suspend_sensor(void) { - int32_t rc = 0; + unsigned short rData = 0; + /*AF*/ + s5k3e2fx_i2c_read_b(s5k3e2fx_client->addr, + REG_TYPE1_AF_ENABLE, &rData); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_TYPE1_AF_ENABLE, (rData & 0xFFFE)); + mdelay(1); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + S5K3E2FX_REG_MODE_SELECT, + S5K3E2FX_MODE_SELECT_SW_STANDBY); + msleep(210); /*for 5FPS */ + /* hi z */ + s5k3e2fx_i2c_read_b(s5k3e2fx_client->addr, REG_3150_RESERVED, &rData); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_3150_RESERVED, (rData | 0x0001)); + mdelay(1); + +} + +static int s5k3e2fx_power_down(void) +{ + int rc = -EBADF; + s5k3e2fx_suspend_sensor(); return rc; } @@ -771,64 +2372,96 @@ static int s5k3e2fx_sensor_release(void) { int rc = -EBADF; - down(&s5k3e2fx_sem); - - s5k3e2fx_power_down(); - - gpio_direction_output(s5k3e2fx_ctrl->sensordata->sensor_reset, - 0); - gpio_free(s5k3e2fx_ctrl->sensordata->sensor_reset); + s5k3e2fx_suspend_sensor(); kfree(s5k3e2fx_ctrl); s5k3e2fx_ctrl = NULL; + allow_suspend(); + CDBG("s5k3e2fx_release completed\n"); - up(&s5k3e2fx_sem); + return rc; +} + +static int s5k3e2fx_probe_init_lens_correction( + const struct msm_camera_sensor_info *data) +{ + int rc = 0; + + /* LC setting */ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + S5K3E2FX_REG_SOFTWARE_RESET, + S5K3E2FX_SOFTWARE_RESET); + mdelay(2); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + S5K3E2FX_REG_MODE_SELECT, + S5K3E2FX_MODE_SELECT_SW_STANDBY); + /*20090811 separates the EVT4/EVT5 sensor init and LC setting start */ + s5k3e2fx_i2c_write_table(&Init_setting[g_usModuleVersion][0], + NUM_INIT_REG); + + /* 090911 Add for Samsung VCM calibration current Start */ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3112, 0x0A); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3112, 0x09); + mdelay(5); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3145, 0x04); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3146, 0x80); + /* 090911 Add for Samsung VCM calibration current End */ + + s5k3e2fx_i2c_write_table(&lc_setting[g_usModuleVersion][0], NUM_LC_REG); + + /*20090811 separates the EVT4/EVT5 sensor init and LC setting end */ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + S5K3E2FX_REG_MODE_SELECT, + S5K3E2FX_MODE_SELECT_STREAM); + msleep(10); + s5k3e2fx_suspend_sensor(); + return rc; } static void s5k3e2fx_get_pict_fps(uint16_t fps, uint16_t *pfps) { /* input fps is preview fps in Q8 format */ - uint32_t divider; /* Q10 */ + uint32_t divider; /* Q10 */ divider = (uint32_t) - ((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + - s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) * - (s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w + - s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p)) * 0x00000400 / - ((s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h + - s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l) * - (s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w + - s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p)); + ((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) * + (s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p)) * 0x00000400 / + ((s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l) * + (s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p)); /* Verify PCLK settings and frame sizes. */ - *pfps = (uint16_t)(fps * divider / 0x00000400); + *pfps = (uint16_t) (fps * divider / 0x00000400); } static uint16_t s5k3e2fx_get_prev_lines_pf(void) { - return (s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + - s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l); + return s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l; } static uint16_t s5k3e2fx_get_prev_pixels_pl(void) { - return (s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w + - s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p); + return s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p; } static uint16_t s5k3e2fx_get_pict_lines_pf(void) { - return (s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h + - s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l); + return s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l; } static uint16_t s5k3e2fx_get_pict_pixels_pl(void) { - return (s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w + - s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p); + return s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w + + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p; } static uint32_t s5k3e2fx_get_pict_max_exp_lc(void) @@ -837,50 +2470,55 @@ static uint32_t s5k3e2fx_get_pict_max_exp_lc(void) if (s5k3e2fx_ctrl->pict_res == S_QTR_SIZE) snapshot_lines_per_frame = - s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + - s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l; + s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l; else snapshot_lines_per_frame = 3961 * 3; return snapshot_lines_per_frame; } -static int32_t s5k3e2fx_set_fps(struct fps_cfg *fps) +static int s5k3e2fx_set_fps(struct fps_cfg *fps) { /* input is new fps in Q10 format */ - int32_t rc = 0; + int rc = 0; s5k3e2fx_ctrl->fps_divider = fps->fps_div; + CDBG("s5k3e2fx_ctrl->fps_divider = %d\n", + s5k3e2fx_ctrl->fps_divider); + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, - REG_FRAME_LENGTH_LINES_MSB, - (((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + - s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) * - s5k3e2fx_ctrl->fps_divider / 0x400) & 0xFF00) >> 8); + REG_FRAME_LENGTH_LINES_MSB, + (((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) * + s5k3e2fx_ctrl->fps_divider / + 0x400) & 0xFF00) >> 8); if (rc < 0) goto set_fps_done; rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, - REG_FRAME_LENGTH_LINES_LSB, - (((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + - s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) * - s5k3e2fx_ctrl->fps_divider / 0x400) & 0xFF00)); + REG_FRAME_LENGTH_LINES_LSB, + (((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) * + s5k3e2fx_ctrl->fps_divider / + 0x400) & 0xFF00)); set_fps_done: return rc; } -static int32_t s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) +static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) { - int32_t rc = 0; + int rc = 0; uint16_t max_legal_gain = 0x0200; - uint32_t ll_ratio; /* Q10 */ - uint16_t ll_pck, fl_lines; + uint32_t ll_ratio; /* Q10 */ + uint32_t ll_pck, fl_lines; uint16_t offset = 4; - uint8_t gain_msb, gain_lsb; - uint8_t intg_t_msb, intg_t_lsb; - uint8_t ll_pck_msb, ll_pck_lsb, tmp; + uint32_t gain_msb, gain_lsb; + uint32_t intg_t_msb, intg_t_lsb; + uint32_t ll_pck_msb, ll_pck_lsb; struct s5k3e2fx_i2c_reg_conf tbl[2]; @@ -889,21 +2527,21 @@ static int32_t s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) { s5k3e2fx_ctrl->my_reg_gain = gain; - s5k3e2fx_ctrl->my_reg_line_count = (uint16_t)line; + s5k3e2fx_ctrl->my_reg_line_count = (uint16_t) line; fl_lines = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + - s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l; + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l; ll_pck = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w + - s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p; + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p; } else { fl_lines = s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h + - s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l; + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l; ll_pck = s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w + - s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p; + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p; } if (gain > max_legal_gain) @@ -917,6 +2555,15 @@ static int32_t s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) else ll_ratio = 0x400; +/* AEC_FLASHING */ + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) { + pr_err("s5k3e2fx_i2c_write_b failed on line %d\n", __LINE__); + return rc; + } + /* update gain registers */ gain_msb = (gain & 0xFF00) >> 8; gain_lsb = gain & 0x00FF; @@ -927,142 +2574,115 @@ static int32_t s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); if (rc < 0) goto write_gain_done; - +#if 0 /* AEC_FLASHING */ ll_pck = ll_pck * ll_ratio; ll_pck_msb = ((ll_pck / 0x400) & 0xFF00) >> 8; ll_pck_lsb = (ll_pck / 0x400) & 0x00FF; tbl[0].waddr = REG_LINE_LENGTH_PCK_MSB; - tbl[0].bdata = s5k3e2fx_reg_pat[S_RES_PREVIEW].line_length_pck_msb; + tbl[0].bdata = ll_pck_msb; tbl[1].waddr = REG_LINE_LENGTH_PCK_LSB; - tbl[1].bdata = s5k3e2fx_reg_pat[S_RES_PREVIEW].line_length_pck_lsb; + tbl[1].bdata = ll_pck_lsb; + rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); if (rc < 0) goto write_gain_done; - tmp = (ll_pck * 0x400) / ll_ratio; - intg_t_msb = (tmp & 0xFF00) >> 8; - intg_t_lsb = (tmp & 0x00FF); + line = line / ll_ratio; + intg_t_msb = (line & 0xFF00) >> 8; + intg_t_lsb = (line & 0x00FF); tbl[0].waddr = REG_COARSE_INTEGRATION_TIME; tbl[0].bdata = intg_t_msb; tbl[1].waddr = REG_COARSE_INTEGRATION_TIME_LSB; tbl[1].bdata = intg_t_lsb; rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); +#endif /* AEC_FLASHING */ + if (line / 0x400 + offset > fl_lines) + ll_pck = line / 0x400 + offset; + else + ll_pck = fl_lines; + + ll_pck_msb = ((ll_pck) & 0xFF00) >> 8; + ll_pck_lsb = (ll_pck) & 0x00FF; + tbl[0].waddr = REG_FRAME_LENGTH_LINES_MSB; + tbl[0].bdata = ll_pck_msb; + tbl[1].waddr = REG_FRAME_LENGTH_LINES_LSB; + tbl[1].bdata = ll_pck_lsb; + rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); + if (rc < 0) + goto write_gain_done; + + line = line / 0x400; + intg_t_msb = (line & 0xFF00) >> 8; + intg_t_lsb = (line & 0x00FF); + tbl[0].waddr = REG_COARSE_INTEGRATION_TIME; + tbl[0].bdata = intg_t_msb; + tbl[1].waddr = REG_COARSE_INTEGRATION_TIME_LSB; + tbl[1].bdata = intg_t_lsb; + rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); + + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) { + pr_err("s5k3e2fx_i2c_write_b failed on line %d\n", __LINE__); + return rc; + } write_gain_done: return rc; } -static int32_t s5k3e2fx_set_pict_exp_gain(uint16_t gain, uint32_t line) +static int s5k3e2fx_set_pict_exp_gain(uint16_t gain, uint32_t line) { - int32_t rc = 0; + int rc = 0; CDBG("Line:%d s5k3e2fx_set_pict_exp_gain \n", __LINE__); - rc = - s5k3e2fx_write_exp_gain(gain, line); + rc = s5k3e2fx_write_exp_gain(gain, line); return rc; } -static int32_t s5k3e2fx_video_config(int mode, int res) +static int s5k3e2fx_video_config(int mode, int res) { - int32_t rc; + int rc; switch (res) { case S_QTR_SIZE: rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_PREVIEW); if (rc < 0) return rc; - - CDBG("s5k3e2fx sensor configuration done!\n"); break; case S_FULL_SIZE: rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE); if (rc < 0) return rc; - break; default: return 0; - } /* switch */ + } s5k3e2fx_ctrl->prev_res = res; s5k3e2fx_ctrl->curr_res = res; s5k3e2fx_ctrl->sensormode = mode; - rc = - s5k3e2fx_write_exp_gain(s5k3e2fx_ctrl->my_reg_gain, - s5k3e2fx_ctrl->my_reg_line_count); - - return rc; -} - -static int32_t s5k3e2fx_snapshot_config(int mode) -{ - int32_t rc = 0; - - rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE); - if (rc < 0) - return rc; - - s5k3e2fx_ctrl->curr_res = s5k3e2fx_ctrl->pict_res; - s5k3e2fx_ctrl->sensormode = mode; + rc = s5k3e2fx_write_exp_gain(s5k3e2fx_ctrl->my_reg_gain, + s5k3e2fx_ctrl->my_reg_line_count); return rc; } -static int32_t s5k3e2fx_raw_snapshot_config(int mode) +static int s5k3e2fx_set_default_focus(void) { - int32_t rc = 0; - - rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE); - if (rc < 0) - return rc; - - s5k3e2fx_ctrl->curr_res = s5k3e2fx_ctrl->pict_res; - s5k3e2fx_ctrl->sensormode = mode; - - return rc; -} - -static int32_t s5k3e2fx_set_sensor_mode(int mode, int res) -{ - int32_t rc = 0; - - switch (mode) { - case SENSOR_PREVIEW_MODE: - rc = s5k3e2fx_video_config(mode, res); - break; - - case SENSOR_SNAPSHOT_MODE: - rc = s5k3e2fx_snapshot_config(mode); - break; - - case SENSOR_RAW_SNAPSHOT_MODE: - rc = s5k3e2fx_raw_snapshot_config(mode); - break; - - default: - rc = -EINVAL; - break; - } - - return rc; -} - -static int32_t s5k3e2fx_set_default_focus(void) -{ - int32_t rc = 0; + int rc = 0; - rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, - 0x3131, 0); + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3131, 0); if (rc < 0) return rc; - rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, - 0x3132, 0); + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3132, 0); if (rc < 0) return rc; @@ -1071,41 +2691,45 @@ static int32_t s5k3e2fx_set_default_focus(void) return rc; } -static int32_t s5k3e2fx_move_focus(int direction, int32_t num_steps) +static int s5k3e2fx_move_focus(int direction, int num_steps) { - int32_t rc = 0; - int32_t i; + int rc = 0; + int i; int16_t step_direction; int16_t actual_step; int16_t next_pos, pos_offset; - int16_t init_code = 50; + int16_t init_code = 0; uint8_t next_pos_msb, next_pos_lsb; int16_t s_move[5]; - uint32_t gain; /* Q10 format */ + uint32_t gain; /* Q10 format */ if (direction == MOVE_NEAR) step_direction = 20; else if (direction == MOVE_FAR) step_direction = -20; else { - CDBG("s5k3e2fx_move_focus failed at line %d ...\n", __LINE__); + pr_err("s5k3e2fx_move_focus failed at line %d ...\n", __LINE__); return -EINVAL; } - actual_step = step_direction * (int16_t)num_steps; + actual_step = step_direction * (int16_t) num_steps; pos_offset = init_code + s5k3e2fx_ctrl->curr_lens_pos; gain = actual_step * 0x400 / 5; for (i = 0; i <= 4; i++) { if (actual_step >= 0) - s_move[i] = ((((i+1)*gain+0x200) - (i*gain+0x200))/0x400); + s_move[i] = + ((((i + 1) * gain + 0x200) - + (i * gain + 0x200)) / 0x400); else - s_move[i] = ((((i+1)*gain-0x200) - (i*gain-0x200))/0x400); + s_move[i] = + ((((i + 1) * gain - 0x200) - + (i * gain - 0x200)) / 0x400); } /* Ring Damping Code */ for (i = 0; i <= 4; i++) { - next_pos = (int16_t)(pos_offset + s_move[i]); + next_pos = (int16_t) (pos_offset + s_move[i]); if (next_pos > (738 + init_code)) next_pos = 738 + init_code; @@ -1120,18 +2744,22 @@ static int32_t s5k3e2fx_move_focus(int direction, int32_t num_steps) next_pos_msb = next_pos >> 8; next_pos_lsb = next_pos & 0x00FF; - rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3131, next_pos_msb); + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3131, + next_pos_msb); if (rc < 0) break; - rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3132, next_pos_lsb); + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3132, + next_pos_lsb); if (rc < 0) break; pos_offset = next_pos; s5k3e2fx_ctrl->curr_lens_pos = pos_offset - init_code; - if (i < 4) - mdelay(3); + if (num_steps > 1) + mdelay(6); + else + mdelay(4); } return rc; @@ -1140,23 +2768,20 @@ static int32_t s5k3e2fx_move_focus(int direction, int32_t num_steps) static int s5k3e2fx_sensor_config(void __user *argp) { struct sensor_cfg_data cdata; - long rc = 0; + long rc = 0; if (copy_from_user(&cdata, - (void *)argp, - sizeof(struct sensor_cfg_data))) + (void *)argp, sizeof(struct sensor_cfg_data))) return -EFAULT; - down(&s5k3e2fx_sem); - CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype); switch (cdata.cfgtype) { case CFG_GET_PICT_FPS: s5k3e2fx_get_pict_fps(cdata.cfg.gfps.prevfps, - &(cdata.cfg.gfps.pictfps)); + &(cdata.cfg.gfps.pictfps)); if (copy_to_user((void *)argp, &cdata, - sizeof(struct sensor_cfg_data))) + sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; @@ -1164,8 +2789,7 @@ static int s5k3e2fx_sensor_config(void __user *argp) cdata.cfg.prevl_pf = s5k3e2fx_get_prev_lines_pf(); if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; @@ -1173,8 +2797,7 @@ static int s5k3e2fx_sensor_config(void __user *argp) cdata.cfg.prevp_pl = s5k3e2fx_get_prev_pixels_pl(); if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; @@ -1182,8 +2805,7 @@ static int s5k3e2fx_sensor_config(void __user *argp) cdata.cfg.pictl_pf = s5k3e2fx_get_pict_lines_pf(); if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; @@ -1191,18 +2813,15 @@ static int s5k3e2fx_sensor_config(void __user *argp) cdata.cfg.pictp_pl = s5k3e2fx_get_pict_pixels_pl(); if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; case CFG_GET_PICT_MAX_EXP_LC: - cdata.cfg.pict_max_exp_lc = - s5k3e2fx_get_pict_max_exp_lc(); + cdata.cfg.pict_max_exp_lc = s5k3e2fx_get_pict_max_exp_lc(); if (copy_to_user((void *)argp, - &cdata, - sizeof(struct sensor_cfg_data))) + &cdata, sizeof(struct sensor_cfg_data))) rc = -EFAULT; break; @@ -1212,23 +2831,17 @@ static int s5k3e2fx_sensor_config(void __user *argp) break; case CFG_SET_EXP_GAIN: - rc = - s5k3e2fx_write_exp_gain(cdata.cfg.exp_gain.gain, - cdata.cfg.exp_gain.line); + rc = s5k3e2fx_write_exp_gain(cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); break; case CFG_SET_PICT_EXP_GAIN: - CDBG("Line:%d CFG_SET_PICT_EXP_GAIN \n", __LINE__); - rc = - s5k3e2fx_set_pict_exp_gain( - cdata.cfg.exp_gain.gain, - cdata.cfg.exp_gain.line); + rc = s5k3e2fx_set_pict_exp_gain(cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); break; case CFG_SET_MODE: - rc = - s5k3e2fx_set_sensor_mode( - cdata.mode, cdata.rs); + rc = s5k3e2fx_video_config(cdata.mode, cdata.rs); break; case CFG_PWR_DOWN: @@ -1236,33 +2849,34 @@ static int s5k3e2fx_sensor_config(void __user *argp) break; case CFG_MOVE_FOCUS: - rc = - s5k3e2fx_move_focus( - cdata.cfg.focus.dir, - cdata.cfg.focus.steps); + rc = s5k3e2fx_move_focus(cdata.cfg.focus.dir, + cdata.cfg.focus.steps); break; case CFG_SET_DEFAULT_FOCUS: - rc = - s5k3e2fx_set_default_focus(); + rc = s5k3e2fx_set_default_focus(); break; - case CFG_GET_AF_MAX_STEPS: +/* case CFG_GET_AF_MAX_STEPS: */ case CFG_SET_EFFECT: + rc = s5k3e2fx_set_default_focus(); + break; + case CFG_SET_LENS_SHADING: default: - rc = -EINVAL; + rc = -EFAULT; break; } - up(&s5k3e2fx_sem); + prevent_suspend(); return rc; } static int s5k3e2fx_sensor_probe(const struct msm_camera_sensor_info *info, - struct msm_sensor_ctrl *s) + struct msm_sensor_ctrl *s) { int rc = 0; + pr_info("%s\n", __func__); rc = i2c_add_driver(&s5k3e2fx_i2c_driver); if (rc < 0 || s5k3e2fx_client == NULL) { @@ -1270,22 +2884,171 @@ static int s5k3e2fx_sensor_probe(const struct msm_camera_sensor_info *info, goto probe_fail; } - msm_camio_clk_rate_set(24000000); - mdelay(20); + msm_camio_clk_rate_set(S5K3E2FX_DEF_MCLK); + msleep(20); rc = s5k3e2fx_probe_init_sensor(info); if (rc < 0) goto probe_fail; + /* lens correction */ + s5k3e2fx_probe_init_lens_correction(info); + init_suspend(); + s->s_init = s5k3e2fx_sensor_open_init; s->s_release = s5k3e2fx_sensor_release; - s->s_config = s5k3e2fx_sensor_config; - s5k3e2fx_probe_init_done(info); + s->s_config = s5k3e2fx_sensor_config; return rc; probe_fail: - CDBG("SENSOR PROBE FAILS!\n"); + pr_err("SENSOR PROBE FAILS!\n"); + return rc; +} + +static int s5k3e2fx_suspend(struct platform_device *pdev, pm_message_t state) +{ + int rc; + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + + if (!sinfo->need_suspend) + return 0; + + CDBG("s5k3e2fx: camera suspend\n"); + rc = gpio_request(sinfo->sensor_reset, "s5k3e2fx"); + if (!rc) + gpio_direction_output(sinfo->sensor_reset, 0); + else { + pr_err("s5k3e2fx: request GPIO(sensor_reset) :%d faile\n", + sinfo->sensor_reset); + goto suspend_fail; + } + CDBG("s5k3e2fx: gpio_free:%d line:%d\n", sinfo->sensor_reset, + __LINE__); + gpio_free(sinfo->sensor_reset); + +suspend_fail: + return rc; +} +static void s5k3e2fx_sensor_resume_setting(void) +{ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + S5K3E2FX_REG_SOFTWARE_RESET, + S5K3E2FX_SOFTWARE_RESET); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0100, 0x00); + /*--------------PLL setting for 80Mhz*/ + /* PLL setting */ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0305, 0x06); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0306, 0x00); + /*88 54.4Mhz */ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0307, 0x83); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0301, 0x08); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0303, 0x01); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0309, 0x08); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x030b, 0x01); + /*--------------output size*/ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x034c, 0x05); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x034d, 0x10); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x034e, 0x03); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x034f, 0xcc); + /*--------------frame format (min blanking)*/ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0340, 0x03); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0341, 0xe2); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0342, 0x0a); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0343, 0xac); + /*--------------Binning */ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0381, 0x01); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0383, 0x01); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0385, 0x01); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0387, 0x03); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3014, 0x06); + /*--------------MSR setting*/ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x30c4, 0x01); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3000, 0x03); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3001, 0x94); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3002, 0x02); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3003, 0x95); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3004, 0x0f); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3005, 0x05); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3006, 0x3c); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3007, 0x8c); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3008, 0x93); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3009, 0x05); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x300a, 0x3a); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x300c, 0x02); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x300d, 0x3e); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x300f, 0x0e); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3010, 0x46); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3011, 0x64); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3012, 0x1e); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x301d, 0x3f); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3024, 0x04); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3028, 0x40); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3070, 0xdf); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x301b, 0x73); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x307e, 0x02); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x30bd, 0x06); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x30c2, 0x0b); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x30ac, 0x81); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3151, 0xe6); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3029, 0x02); + /*--------------EVT4 setting*/ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x30bf, 0x00); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3022, 0x87); + /*tune ADC to got batter yield rate in EDS */ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3019, 0x60); + /*AF driving strength */ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3146, 0x3c); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3152, 0x08); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x315a, 0xaa); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3159, 0x0a); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0205, 0x80); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0202, 0x03); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0200, 0x02); +} +static int s5k3e2fx_resume(struct platform_device *pdev) +{ + int rc = 0; + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + + if (!sinfo->need_suspend) + return 0; + + CDBG("s5k3e2fx_resume\n"); + /*init msm,clk ,GPIO,enable */ + msm_camio_probe_on(pdev); + msm_camio_clk_enable(CAMIO_MDC_CLK); + + CDBG("msm_camio_probe_on\n"); + /*read sensor ID and pull down reset */ + msm_camio_clk_rate_set(S5K3E2FX_DEF_MCLK); + CDBG("msm_camio_clk_rate_set\n"); + msleep(20); + s5k3e2fx_probe_init_sensor(sinfo); + CDBG("s5k3e2fx_probe_init_sensor\n"); + /*init sensor,streaming on, SW init streaming off */ + s5k3e2fx_sensor_resume_setting(); + /*lens sharding */ + s5k3e2fx_probe_init_lens_correction(sinfo); + /*stream on */ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + S5K3E2FX_REG_MODE_SELECT, + S5K3E2FX_MODE_SELECT_STREAM); + /*software standby */ + msleep(25); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3130, 0x00); + mdelay(1); + /*stream off */ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + S5K3E2FX_REG_MODE_SELECT, + S5K3E2FX_MODE_SELECT_SW_STANDBY); + mdelay(1); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3150, 0x51); + msleep(240); + /*set RST to low */ + msm_camio_probe_off(pdev); + msm_camio_clk_disable(CAMIO_MDC_CLK); + CDBG("s5k3e2fx:resume done\n"); return rc; } @@ -1300,6 +3063,8 @@ static struct platform_driver msm_camera_driver = { .name = "msm_camera_s5k3e2fx", .owner = THIS_MODULE, }, + .suspend = s5k3e2fx_suspend, + .resume = s5k3e2fx_resume, }; static int __init s5k3e2fx_init(void) @@ -1308,4 +3073,3 @@ static int __init s5k3e2fx_init(void) } module_init(s5k3e2fx_init); - diff --git a/drivers/media/video/msm/s5k3e2fx.h b/drivers/media/video/msm/s5k3e2fx.h deleted file mode 100644 index 69bc750844573..0000000000000 --- a/drivers/media/video/msm/s5k3e2fx.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (C) 2008-2009 QUALCOMM Incorporated. - */ - -#ifndef CAMSENSOR_S5K3E2FX -#define CAMSENSOR_S5K3E2FX - -#include -#endif /* CAMSENSOR_S5K3E2FX */ diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h index 1f22d4d26fec5..60a54355491ce 100644 --- a/include/media/msm_camera.h +++ b/include/media/msm_camera.h @@ -64,10 +64,10 @@ #define MSM_CAM_IOCTL_SET_CROP \ _IOW(MSM_CAM_IOCTL_MAGIC, 18, struct crop_info *) -#define MSM_CAM_IOCTL_PICT_PP \ +#define MSM_CAM_IOCTL_PP \ _IOW(MSM_CAM_IOCTL_MAGIC, 19, uint8_t *) -#define MSM_CAM_IOCTL_PICT_PP_DONE \ +#define MSM_CAM_IOCTL_PP_DONE \ _IOW(MSM_CAM_IOCTL_MAGIC, 20, struct msm_snapshot_pp_status *) #define MSM_CAM_IOCTL_SENSOR_IO_CFG \ @@ -86,9 +86,17 @@ #define MSM_CAM_IOCTL_CTRL_COMMAND_2 \ _IOW(MSM_CAM_IOCTL_MAGIC, 24, struct msm_ctrl_cmd *) +#define MSM_CAM_IOCTL_ENABLE_OUTPUT_IND \ + _IOW(MSM_CAM_IOCTL_MAGIC, 25, uint32_t *) + #define MAX_SENSOR_NUM 3 #define MAX_SENSOR_NAME 32 +#define PP_SNAP 1 +#define PP_RAW_SNAP (1<<1) +#define PP_PREV (1<<2) +#define PP_MASK (PP_SNAP|PP_RAW_SNAP|PP_PREV) + #define MSM_CAM_CTRL_CMD_DONE 0 #define MSM_CAM_SENSOR_VFE_CMD 1 @@ -164,11 +172,12 @@ struct msm_camera_cfg_cmd { #define CMD_SNAP_BUF_RELEASE 11 #define CMD_SNAP_BUF_CFG 12 #define CMD_STATS_DISABLE 13 -#define CMD_STATS_ENABLE 14 +#define CMD_STATS_AEC_AWB_ENABLE 14 #define CMD_STATS_AF_ENABLE 15 #define CMD_STATS_BUF_RELEASE 16 #define CMD_STATS_AF_BUF_RELEASE 17 -#define UPDATE_STATS_INVALID 18 +#define CMD_STATS_ENABLE 18 +#define UPDATE_STATS_INVALID 19 /* vfe config command: config command(from config thread)*/ struct msm_vfe_cfg_cmd { @@ -207,7 +216,7 @@ struct msm_pmem_info { uint32_t len; uint32_t y_off; /* relative to offset */ uint32_t cbcr_off; /* relative to offset */ - uint8_t active; + uint8_t vfe_can_write; }; struct outputCfg { From d0226a538aed2a1facb5808b952b06969c28db06 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 9 Feb 2010 21:13:43 -0800 Subject: [PATCH 0350/2556] [ARM] msm: board file updates for camera --- arch/arm/mach-msm/board-halibut.c | 4 ---- arch/arm/mach-msm/board-sapphire.c | 2 -- arch/arm/mach-msm/board-trout.c | 1 - arch/arm/mach-msm/include/mach/board.h | 11 +++++------ 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/arch/arm/mach-msm/board-halibut.c b/arch/arm/mach-msm/board-halibut.c index 00747d64ecb98..2968db9bce3c6 100644 --- a/arch/arm/mach-msm/board-halibut.c +++ b/arch/arm/mach-msm/board-halibut.c @@ -175,7 +175,6 @@ static struct msm_camera_sensor_info msm_camera_sensor_mt9d112_data = { .sensor_pwd = 85, .vcm_pwd = 0, .pdata = &msm_camera_device_data, - .flash_type = MSM_CAMERA_FLASH_NONE }; static struct platform_device msm_camera_sensor_mt9d112 = { @@ -193,7 +192,6 @@ static struct msm_camera_sensor_info msm_camera_sensor_s5k3e2fx_data = { .sensor_pwd = 85, .vcm_pwd = 0, .pdata = &msm_camera_device_data, - .flash_type = MSM_CAMERA_FLASH_NONE }; static struct platform_device msm_camera_sensor_s5k3e2fx = { @@ -211,7 +209,6 @@ static struct msm_camera_sensor_info msm_camera_sensor_mt9p012_data = { .sensor_pwd = 85, .vcm_pwd = 88, .pdata = &msm_camera_device_data, - .flash_type = MSM_CAMERA_FLASH_NONE }; static struct platform_device msm_camera_sensor_mt9p012 = { @@ -229,7 +226,6 @@ static struct msm_camera_sensor_info msm_camera_sensor_mt9t013_data = { .sensor_pwd = 85, .vcm_pwd = 0, .pdata = &msm_camera_device_data, - .flash_type = MSM_CAMERA_FLASH_NONE }; static struct platform_device msm_camera_sensor_mt9t013 = { diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index 82f71ce728ec6..40822d714e5d1 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -858,7 +858,6 @@ static struct msm_camera_sensor_info msm_camera_sensor_mt9t013_data = { .sensor_pwd = 85, .vcm_pwd = SAPPHIRE_GPIO_VCM_PWDN, .pdata = &msm_camera_device_data, - .flash_type = MSM_CAMERA_FLASH_NONE }; static struct platform_device msm_camera_sensor_mt9t013 = { @@ -876,7 +875,6 @@ static struct msm_camera_sensor_info msm_camera_sensor_mt9p012_data = { .sensor_pwd = 85, .vcm_pwd = SAPPHIRE_GPIO_VCM_PWDN, .pdata = &msm_camera_device_data, - .flash_type = MSM_CAMERA_FLASH_NONE }; static struct platform_device msm_camera_sensor_mt9p012 = { diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index e820d2059587f..16cff0b6bccf7 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -435,7 +435,6 @@ static struct msm_camera_sensor_info msm_camera_sensor_mt9t013_data = { .sensor_pwd = 85, .vcm_pwd = TROUT_GPIO_VCM_PWDN, .pdata = &msm_camera_device_data, - .flash_type = MSM_CAMERA_FLASH_NONE }; static struct platform_device msm_camera_sensor_mt9t013 = { diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index 5574480f24d06..68291b6a8fcaa 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -44,7 +44,6 @@ struct msm_camera_device_platform_data { struct msm_camera_io_ext ioext; }; -#ifdef CONFIG_SENSORS_MT9T013 struct msm_camera_legacy_device_platform_data { int sensor_reset; int sensor_pwd; @@ -52,10 +51,6 @@ struct msm_camera_legacy_device_platform_data { void (*config_gpio_on) (void); void (*config_gpio_off)(void); }; -#endif - -#define MSM_CAMERA_FLASH_NONE 0 -#define MSM_CAMERA_FLASH_LED 1 struct msm_camera_sensor_info { const char *sensor_name; @@ -63,8 +58,12 @@ struct msm_camera_sensor_info { int sensor_pwd; int vcm_pwd; int mclk; - int flash_type; + int num_flash_levels; + int (*camera_flash)(int level); + int need_suspend; struct msm_camera_device_platform_data *pdata; + struct resource *resource; + uint8_t num_resources; }; struct snd_endpoint { From e3a65ea03500c8cfc6ab9079a0a525cb08cd95e5 Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Fri, 16 Oct 2009 14:30:13 -0700 Subject: [PATCH 0351/2556] msm: cpufreq: Export scaling_available_frequencies attr in /sys Signed-off-by: Mike Chan --- arch/arm/mach-msm/cpufreq.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c index 729391c64424d..d362c1c39cdfe 100644 --- a/arch/arm/mach-msm/cpufreq.c +++ b/arch/arm/mach-msm/cpufreq.c @@ -101,6 +101,11 @@ static int __init msm_cpufreq_init(struct cpufreq_policy *policy) return 0; } +static struct freq_attr *msm_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + static struct cpufreq_driver msm_cpufreq_driver = { /* lps calculations are handled here. */ .flags = CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS, @@ -108,6 +113,7 @@ static struct cpufreq_driver msm_cpufreq_driver = { .verify = msm_cpufreq_verify, .target = msm_cpufreq_target, .name = "msm", + .attr = msm_cpufreq_attr, }; static int __init msm_cpufreq_register(void) From 3adeb5e33916a244451f20ff41d5504fd8b04020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Sun, 20 Sep 2009 16:58:47 -0700 Subject: [PATCH 0352/2556] [ARM] msm: smd_rpcrouter: Fix a problem where the rtc read on suspend could trigger a wakeup. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a suspend handler that delays suspend until the rpcrouter worker thread blocks. Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/smd_rpcrouter.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c index 5ac7053a3d1c1..67bde4b60db72 100644 --- a/arch/arm/mach-msm/smd_rpcrouter.c +++ b/arch/arm/mach-msm/smd_rpcrouter.c @@ -90,6 +90,7 @@ static smd_channel_t *smd_channel; static int initialized; static wait_queue_head_t newserver_wait; static wait_queue_head_t smd_wait; +static int smd_wait_count; /* odd while waiting */ static DEFINE_SPINLOCK(local_endpoints_lock); static DEFINE_SPINLOCK(remote_endpoints_lock); @@ -582,7 +583,10 @@ static int rr_read(void *data, int len) spin_unlock_irqrestore(&smd_lock, flags); // printk("rr_read: waiting (%d)\n", len); + smd_wait_count++; + wake_up(&smd_wait); wait_event(smd_wait, smd_read_avail(smd_channel) >= len); + smd_wait_count++; } return 0; } @@ -1274,12 +1278,25 @@ static int msm_rpcrouter_probe(struct platform_device *pdev) return rc; } +static int msm_rpcrouter_suspend(struct platform_device *pdev, + pm_message_t state) +{ + /* Wait until the worker thread has waited at least once so that it + * gets a chance to release its wakelock. + */ + int wait_count = smd_wait_count; + if (!(smd_wait_count & 1)) + wait_event(smd_wait, smd_wait_count != wait_count); + return 0; +} + static struct platform_driver msm_smd_channel2_driver = { .probe = msm_rpcrouter_probe, .driver = { .name = "SMD_RPCCALL", .owner = THIS_MODULE, }, + .suspend = msm_rpcrouter_suspend, }; static int __init rpcrouter_init(void) From 4c72e037b7610ff7f0e751a7f379b4bfe0b23bcc Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Wed, 28 Oct 2009 21:14:12 -0700 Subject: [PATCH 0353/2556] msm: cpufreq: Do not call a cpu transition if selecting the same speed. Signed-off-by: Mike Chan --- arch/arm/mach-msm/cpufreq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c index d362c1c39cdfe..a49486b49cf96 100644 --- a/arch/arm/mach-msm/cpufreq.c +++ b/arch/arm/mach-msm/cpufreq.c @@ -63,6 +63,9 @@ static int msm_cpufreq_target(struct cpufreq_policy *policy, return -EINVAL; } + if (policy->cur == table[index].frequency) + return 0; + #ifdef CONFIG_CPU_FREQ_DEBUG printk("msm_cpufreq_target %d r %d (%d-%d) selected %d\n", target_freq, relation, policy->min, policy->max, table[index].frequency); From 2bc7a65404c7d57ce1835eff3d6b04f97105ebf4 Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Thu, 29 Oct 2009 12:25:39 -0700 Subject: [PATCH 0354/2556] [ARM] msm: cpufreq: Kconfig cleanup, less msm specific options Change-Id: Id588c1814dc438a8fad1ed113938207665d1d862 Signed-off-by: Mike Chan --- arch/arm/mach-msm/Kconfig | 37 +++++-------------------------------- arch/arm/mach-msm/Makefile | 2 +- arch/arm/mach-msm/cpufreq.c | 15 +++------------ 3 files changed, 9 insertions(+), 45 deletions(-) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 226e18bf66c7c..d4f7b04a9c57b 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -411,26 +411,12 @@ config MSM_RPCSERVERS help none -config MSM_CPU_FREQ +config MSM_CPU_FREQ_SCREEN bool - default y if MSM_CPU_FREQ_ONDEMAND || MSM_CPU_FREQ_SCREEN - -choice - prompt "Cpufreq mode" - default MSM_CPU_FREQ_ONDEMAND - default MSM_CPU_FREQ_SCREEN - - config MSM_CPU_FREQ_ONDEMAND - depends on CPU_FREQ_DEFAULT_GOV_ONDEMAND - bool "Enable ONDEMAND cpufreq govoner for" - - config MSM_CPU_FREQ_SCREEN - depends on HAS_EARLYSUSPEND - bool "Enable simple cpu frequency scaling" - help - Simple cpufreq scaling based on screen ON/OFF. - -endchoice + default n + depends on HAS_EARLYSUSPEND + help + Simple cpufreq scaling based on screen ON/OFF. if MSM_CPU_FREQ_SCREEN @@ -444,19 +430,6 @@ config MSM_CPU_FREQ_SCREEN_ON endif # MSM_CPU_FREQ_SCREEN - -if MSM_CPU_FREQ_ONDEMAND - -config MSM_CPU_FREQ_ONDEMAND_MAX - int "Max" - default 384000 - -config MSM_CPU_FREQ_ONDEMAND_MIN - int "Min" - default 245760 - -endif # MSM_CPU_FREQ_ONDEMAND - config MSM_HW3D tristate "MSM Hardware 3D Register Driver" depends on EARLYSUSPEND diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 440f601b7d909..fd4b25eb735e2 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -36,7 +36,7 @@ obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_time_remote.o obj-$(CONFIG_MSM_ADSP) += qdsp5/ obj-$(CONFIG_MSM_HW3D) += hw3d.o obj-$(CONFIG_PM) += pm.o -obj-$(CONFIG_MSM_CPU_FREQ) += cpufreq.o +obj-$(CONFIG_CPU_FREQ) += cpufreq.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c index a49486b49cf96..5317991240227 100644 --- a/arch/arm/mach-msm/cpufreq.c +++ b/arch/arm/mach-msm/cpufreq.c @@ -17,14 +17,11 @@ * */ +#include #include #include #include "acpuclock.h" -#ifdef CONFIG_MSM_CPU_FREQ_ONDEMAND -#include -#endif - #ifdef CONFIG_MSM_CPU_FREQ_SCREEN static void msm_early_suspend(struct early_suspend *handler) { acpuclk_set_rate(CONFIG_MSM_CPU_FREQ_SCREEN_OFF * 1000, 0); @@ -46,7 +43,7 @@ static int __init clock_late_init(void) } late_initcall(clock_late_init); -#elif defined(CONFIG_MSM_CPU_FREQ_ONDEMAND) +#else static int msm_cpufreq_target(struct cpufreq_policy *policy, unsigned int target_freq, @@ -91,14 +88,8 @@ static int __init msm_cpufreq_init(struct cpufreq_policy *policy) struct cpufreq_frequency_table *table = cpufreq_frequency_get_table(smp_processor_id()); + BUG_ON(cpufreq_frequency_table_cpuinfo(policy, table)); policy->cur = acpuclk_get_rate(); - if (cpufreq_frequency_table_cpuinfo(policy, table)) { - policy->cpuinfo.min_freq = CONFIG_MSM_CPU_FREQ_ONDEMAND_MIN; - policy->cpuinfo.max_freq = CONFIG_MSM_CPU_FREQ_ONDEMAND_MAX; - } - policy->min = CONFIG_MSM_CPU_FREQ_ONDEMAND_MIN; - policy->max = CONFIG_MSM_CPU_FREQ_ONDEMAND_MAX; - policy->cpuinfo.transition_latency = acpuclk_get_switch_time() * NSEC_PER_USEC; return 0; From a7bdb63ada308ff7f4ea5630cf079979d285f767 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Tue, 27 Oct 2009 16:19:49 -0700 Subject: [PATCH 0355/2556] [ARM] msm: msm_serial_hs: Rename wakeup to rx_wakeup. Preparation for a new low power wakeup mode. Signed-off-by: Nick Pelly --- .../arm/mach-msm/include/mach/msm_serial_hs.h | 4 +- drivers/tty/serial/msm_serial_hs.c | 74 +++++++++++-------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/msm_serial_hs.h b/arch/arm/mach-msm/include/mach/msm_serial_hs.h index 0337deb8358d3..e47fce339c7ba 100644 --- a/arch/arm/mach-msm/include/mach/msm_serial_hs.h +++ b/arch/arm/mach-msm/include/mach/msm_serial_hs.h @@ -16,9 +16,9 @@ #define __ASM_ARCH_MSM_SERIAL_HS_H /* Optional platform device data for msm_serial_hs driver. - * Used to configure low power wakeup */ + * Used to configure low power rx wakeup */ struct msm_serial_hs_platform_data { - int wakeup_irq; /* wakeup irq */ + int rx_wakeup_irq; /* wakeup irq */ /* bool: inject char into rx tty on wakeup */ unsigned char inject_rx_on_wakeup; char rx_to_inject; diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 35eebbea3b589..1a98b2feeb0dd 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -23,8 +23,19 @@ * along with this program; if not, you can find it at http://www.fsf.org */ -/* This driver has optional support for low power wakeup on a rx gpio. This is - * useful for peripherals that send unsolicited RX such as Bluetooth. +/* + * MSM 7k/8k High speed uart driver + * + * Has optional support for uart power management independent of linux + * suspend/resume: + * + * RX wakeup. + * UART wakeup can be triggered by RX activity (using a wakeup GPIO on the + * UART RX pin). This should only be used if there is not a wakeup + * GPIO on the UART CTS, and the first RX byte is known (for example, with the + * Bluetooth Texas Instruments HCILL protocol), since the first RX byte will + * always be lost. RTS will be asserted even while the UART is off in this mode + * of operation. See msm_serial_hs_platform_data.rx_wakeup_irq. */ #include @@ -98,8 +109,8 @@ struct msm_hs_rx { struct wake_lock wake_lock; }; -/* optional low power wakeup, typically on a GPIO RX irq */ -struct msm_hs_wakeup { +/* optional RX GPIO IRQ low power wakeup */ +struct msm_hs_rx_wakeup { int irq; /* < 0 indicates low power wakeup disabled */ unsigned char ignore; /* bool */ @@ -124,7 +135,7 @@ struct msm_hs_port { ktime_t clk_off_delay; enum msm_hs_clk_states_e clk_state; - struct msm_hs_wakeup wakeup; + struct msm_hs_rx_wakeup rx_wakeup; }; #define MSM_UARTDM_BURST_SIZE 16 /* DM burst size (in bytes) */ @@ -141,9 +152,9 @@ static struct uart_ops msm_hs_ops; #define UARTDM_TO_MSM(uart_port) \ container_of((uart_port), struct msm_hs_port, uport) -static inline unsigned int use_low_power_wakeup(struct msm_hs_port *msm_uport) +static inline unsigned int use_low_power_rx_wakeup(struct msm_hs_port *msm_uport) { - return (msm_uport->wakeup.irq >= 0); + return (msm_uport->rx_wakeup.irq >= 0); } static inline unsigned int msm_hs_read(struct uart_port *uport, @@ -239,7 +250,7 @@ static void msm_hs_pm(struct uart_port *uport, unsigned int state, unsigned int oldstate) { struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - if (use_low_power_wakeup(msm_uport)) + if (use_low_power_rx_wakeup(msm_uport)) return; switch (state) { @@ -874,9 +885,9 @@ static int msm_hs_check_clock_off_locked(struct uart_port *uport) /* we really want to clock off */ clk_disable(msm_uport->clk); msm_uport->clk_state = MSM_HS_CLK_OFF; - if (use_low_power_wakeup(msm_uport)) { - msm_uport->wakeup.ignore = 1; - enable_irq(msm_uport->wakeup.irq); + if (use_low_power_rx_wakeup(msm_uport)) { + msm_uport->rx_wakeup.ignore = 1; + enable_irq(msm_uport->rx_wakeup.irq); } return 1; } @@ -992,7 +1003,7 @@ static void msm_hs_request_clock_on_locked(struct uart_port *uport) { switch (msm_uport->clk_state) { case MSM_HS_CLK_OFF: clk_enable(msm_uport->clk); - disable_irq(msm_uport->wakeup.irq); + disable_irq(msm_uport->rx_wakeup.irq); /* fall-through */ case MSM_HS_CLK_REQUEST_OFF: hrtimer_try_to_cancel(&msm_uport->clk_off_timer); @@ -1014,7 +1025,7 @@ void msm_hs_request_clock_on(struct uart_port *uport) { spin_unlock_irqrestore(&uport->lock, flags); } -static irqreturn_t msm_hs_wakeup_isr(int irq, void *dev) +static irqreturn_t msm_hs_rx_wakeup_isr(int irq, void *dev) { unsigned int wakeup = 0; unsigned long flags; @@ -1026,8 +1037,8 @@ static irqreturn_t msm_hs_wakeup_isr(int irq, void *dev) if (msm_uport->clk_state == MSM_HS_CLK_OFF) { /* ignore the first irq - it is a pending irq that occured * before enable_irq() */ - if (msm_uport->wakeup.ignore) - msm_uport->wakeup.ignore = 0; + if (msm_uport->rx_wakeup.ignore) + msm_uport->rx_wakeup.ignore = 0; else wakeup = 1; } @@ -1036,17 +1047,17 @@ static irqreturn_t msm_hs_wakeup_isr(int irq, void *dev) /* the uart was clocked off during an rx, wake up and * optionally inject char into tty rx */ msm_hs_request_clock_on_locked(uport); - if (msm_uport->wakeup.inject_rx) { + if (msm_uport->rx_wakeup.inject_rx) { tty = uport->info->port.tty; tty_insert_flip_char(tty, - msm_uport->wakeup.rx_to_inject, + msm_uport->rx_wakeup.rx_to_inject, TTY_NORMAL); } } spin_unlock_irqrestore(&uport->lock, flags); - if (wakeup && msm_uport->wakeup.inject_rx) + if (wakeup && msm_uport->rx_wakeup.inject_rx) tty_flip_buffer_push(tty); return IRQ_HANDLED; } @@ -1154,13 +1165,14 @@ static int msm_hs_startup(struct uart_port *uport) "msm_hs_uart", msm_uport); if (unlikely(ret)) return ret; - if (use_low_power_wakeup(msm_uport)) { - ret = request_irq(msm_uport->wakeup.irq, msm_hs_wakeup_isr, + if (use_low_power_rx_wakeup(msm_uport)) { + ret = request_irq(msm_uport->rx_wakeup.irq, + msm_hs_rx_wakeup_isr, IRQF_TRIGGER_FALLING, - "msm_hs_wakeup", msm_uport); + "msm_hs_rx_wakeup", msm_uport); if (unlikely(ret)) return ret; - disable_irq(msm_uport->wakeup.irq); + disable_irq(msm_uport->rx_wakeup.irq); } spin_lock_irqsave(&uport->lock, flags); @@ -1263,16 +1275,16 @@ static int __init msm_hs_probe(struct platform_device *pdev) return -ENXIO; if (pdata == NULL) - msm_uport->wakeup.irq = -1; + msm_uport->rx_wakeup.irq = -1; else { - msm_uport->wakeup.irq = pdata->wakeup_irq; - msm_uport->wakeup.ignore = 1; - msm_uport->wakeup.inject_rx = pdata->inject_rx_on_wakeup; - msm_uport->wakeup.rx_to_inject = pdata->rx_to_inject; + msm_uport->rx_wakeup.irq = pdata->rx_wakeup_irq; + msm_uport->rx_wakeup.ignore = 1; + msm_uport->rx_wakeup.inject_rx = pdata->inject_rx_on_wakeup; + msm_uport->rx_wakeup.rx_to_inject = pdata->rx_to_inject; - if (unlikely(msm_uport->wakeup.irq < 0)) + if (unlikely(msm_uport->rx_wakeup.irq < 0)) return -ENXIO; - if (unlikely(set_irq_wake(msm_uport->wakeup.irq, 1))) + if (unlikely(set_irq_wake(msm_uport->rx_wakeup.irq, 1))) return -ENXIO; } @@ -1364,8 +1376,8 @@ static void msm_hs_shutdown(struct uart_port *uport) /* Free the interrupt */ free_irq(uport->irq, msm_uport); - if (use_low_power_wakeup(msm_uport)) - free_irq(msm_uport->wakeup.irq, msm_uport); + if (use_low_power_rx_wakeup(msm_uport)) + free_irq(msm_uport->rx_wakeup.irq, msm_uport); msm_uport->imr_reg = 0; msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); From 570ed657fd05c9af0b55c7051ffaa2cd26983b1a Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 28 Oct 2009 18:55:15 -0700 Subject: [PATCH 0356/2556] [ARM] msm: sapphire: Update msm_serial_hs platform data for new wakeup irq API. Signed-off-by: Nick Pelly --- arch/arm/mach-msm/board-sapphire.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index 40822d714e5d1..2bf385cd72529 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -1114,7 +1114,7 @@ static struct msm_acpu_clock_platform_data sapphire_clock_data = { #ifdef CONFIG_SERIAL_MSM_HS static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { - .wakeup_irq = MSM_GPIO_TO_INT(45), + .rx_wakeup_irq = MSM_GPIO_TO_INT(45), .inject_rx_on_wakeup = 1, .rx_to_inject = 0x32, }; From 34f1f6330e26b82c15299aa7b104082154ddfc6b Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 28 Oct 2009 18:55:53 -0700 Subject: [PATCH 0357/2556] [ARM] msm: trout: Update msm_serial_hs platform data for new wakeup irq API. Signed-off-by: Nick Pelly --- arch/arm/mach-msm/board-trout.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 16cff0b6bccf7..ca6eace5a1584 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -765,7 +765,7 @@ static struct msm_acpu_clock_platform_data trout_clock_data = { #ifdef CONFIG_SERIAL_MSM_HS static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { - .wakeup_irq = MSM_GPIO_TO_INT(45), + .rx_wakeup_irq = MSM_GPIO_TO_INT(45), .inject_rx_on_wakeup = 1, .rx_to_inject = 0x32, }; From ae9fe273da498a18b2a2d61c4925dc37a9e93375 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 28 Oct 2009 18:47:05 -0700 Subject: [PATCH 0358/2556] [ARM] msm: msm_serial_hs: Improve low power mode support. Move the request_clock_off()/on() into the header. Add exit_lpm_cb callback to help exit low power mode. Clean up RTS / RFR handling. Deactivate RFR when clocking off the uart unless RX wakeup lpm is being used. Signed-off-by: Nick Pelly --- .../arm/mach-msm/include/mach/msm_serial_hs.h | 14 +++ drivers/tty/serial/msm_serial_hs.c | 90 ++++++++++++------- 2 files changed, 73 insertions(+), 31 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/msm_serial_hs.h b/arch/arm/mach-msm/include/mach/msm_serial_hs.h index e47fce339c7ba..fe54b02f622d2 100644 --- a/arch/arm/mach-msm/include/mach/msm_serial_hs.h +++ b/arch/arm/mach-msm/include/mach/msm_serial_hs.h @@ -15,6 +15,18 @@ #ifndef __ASM_ARCH_MSM_SERIAL_HS_H #define __ASM_ARCH_MSM_SERIAL_HS_H +#include + +/* API to request the uart clock off or on for low power management + * Clients should call request_clock_off() when no uart data is expected, + * and must call request_clock_on() before any further uart data can be + * received. */ +extern void msm_hs_request_clock_off(struct uart_port *uport); +extern void msm_hs_request_clock_on(struct uart_port *uport); +/* uport->lock must be held when calling _locked() */ +extern void msm_hs_request_clock_off_locked(struct uart_port *uport); +extern void msm_hs_request_clock_on_locked(struct uart_port *uport); + /* Optional platform device data for msm_serial_hs driver. * Used to configure low power rx wakeup */ struct msm_serial_hs_platform_data { @@ -22,6 +34,8 @@ struct msm_serial_hs_platform_data { /* bool: inject char into rx tty on wakeup */ unsigned char inject_rx_on_wakeup; char rx_to_inject; + + void (*exit_lpm_cb)(struct uart_port *); }; #endif diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 1a98b2feeb0dd..455aafb9d5074 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -136,6 +136,8 @@ struct msm_hs_port { enum msm_hs_clk_states_e clk_state; struct msm_hs_rx_wakeup rx_wakeup; + /* optional callback to exit low power mode */ + void (*exit_lpm_cb)(struct uart_port *); }; #define MSM_UARTDM_BURST_SIZE 16 /* DM burst size (in bytes) */ @@ -250,8 +252,9 @@ static void msm_hs_pm(struct uart_port *uport, unsigned int state, unsigned int oldstate) { struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - if (use_low_power_rx_wakeup(msm_uport)) - return; + + if (use_low_power_rx_wakeup(msm_uport) || msm_uport->exit_lpm_cb) + return; /* ignore linux PM states, use msm_hs_request_clock API */ switch (state) { case 0: @@ -616,6 +619,9 @@ static void msm_hs_start_tx_locked(struct uart_port *uport ) clk_enable(msm_uport->clk); + if (msm_uport->exit_lpm_cb) + msm_uport->exit_lpm_cb(uport); + if (msm_uport->tx.tx_ready_int_en == 0) { msm_uport->tx.tx_ready_int_en = 1; msm_hs_submit_tx_locked(uport); @@ -755,37 +761,41 @@ static unsigned int msm_hs_get_mctrl_locked(struct uart_port *uport) } /* - * Standard API, Set or clear RFR_signal - * - * Set RFR high, (Indicate we are not ready for data), we disable auto - * ready for receiving and then set RFR_N high. To set RFR to low we just turn - * back auto ready for receiving and it should lower RFR signal - * when hardware is ready + * True enables UART auto RFR, which indicates we are ready for data if the RX + * buffer is not full. False disables auto RFR, and deasserts RFR to indicate + * we are not ready for data. Must be called with UART clock on. */ -static void msm_hs_set_mctrl_locked(struct uart_port *uport, - unsigned int mctrl) -{ - unsigned int set_rts; +static void set_rfr_locked(struct uart_port *uport, int auto_rfr) { unsigned int data; - struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - - clk_enable(msm_uport->clk); - - /* RTS is active low */ - set_rts = TIOCM_RTS & mctrl ? 0 : 1; data = msm_hs_read(uport, UARTDM_MR1_ADDR); - if (set_rts) { - /*disable auto ready-for-receiving */ - data &= ~UARTDM_MR1_RX_RDY_CTL_BMSK; + + if (auto_rfr) { + /* enable auto ready-for-receiving */ + data |= UARTDM_MR1_RX_RDY_CTL_BMSK; msm_hs_write(uport, UARTDM_MR1_ADDR, data); - /* set RFR_N to high */ - msm_hs_write(uport, UARTDM_CR_ADDR, RFR_HIGH); } else { - /* Enable auto ready-for-receiving */ - data |= UARTDM_MR1_RX_RDY_CTL_BMSK; + /* disable auto ready-for-receiving */ + data &= ~UARTDM_MR1_RX_RDY_CTL_BMSK; msm_hs_write(uport, UARTDM_MR1_ADDR, data); + /* RFR is active low, set high */ + msm_hs_write(uport, UARTDM_CR_ADDR, RFR_HIGH); } +} + +/* + * Standard API, used to set or clear RFR + */ +static void msm_hs_set_mctrl_locked(struct uart_port *uport, + unsigned int mctrl) +{ + unsigned int auto_rfr; + struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + + clk_enable(msm_uport->clk); + + auto_rfr = TIOCM_RTS & mctrl ? 1 : 0; + set_rfr_locked(uport, auto_rfr); clk_disable(msm_uport->clk); } @@ -883,6 +893,8 @@ static int msm_hs_check_clock_off_locked(struct uart_port *uport) } /* we really want to clock off */ + if (!use_low_power_rx_wakeup(msm_uport)) + set_rfr_locked(uport, 0); clk_disable(msm_uport->clk); msm_uport->clk_state = MSM_HS_CLK_OFF; if (use_low_power_rx_wakeup(msm_uport)) { @@ -983,27 +995,36 @@ static irqreturn_t msm_hs_isr(int irq, void *dev) return IRQ_HANDLED; } -/* request to turn off uart clock once pending TX is flushed */ -void msm_hs_request_clock_off(struct uart_port *uport) { - unsigned long flags; +void msm_hs_request_clock_off_locked(struct uart_port *uport) { struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - spin_lock_irqsave(&uport->lock, flags); if (msm_uport->clk_state == MSM_HS_CLK_ON) { msm_uport->clk_state = MSM_HS_CLK_REQUEST_OFF; msm_uport->imr_reg |= UARTDM_ISR_TXLEV_BMSK; msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); } +} +EXPORT_SYMBOL(msm_hs_request_clock_off_locked); + +/* request to turn off uart clock once pending TX is flushed */ +void msm_hs_request_clock_off(struct uart_port *uport) { + unsigned long flags; + + spin_lock_irqsave(&uport->lock, flags); + msm_hs_request_clock_off_locked(uport); spin_unlock_irqrestore(&uport->lock, flags); } +EXPORT_SYMBOL(msm_hs_request_clock_off); -static void msm_hs_request_clock_on_locked(struct uart_port *uport) { +void msm_hs_request_clock_on_locked(struct uart_port *uport) { struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); switch (msm_uport->clk_state) { case MSM_HS_CLK_OFF: clk_enable(msm_uport->clk); disable_irq(msm_uport->rx_wakeup.irq); + if (!use_low_power_rx_wakeup(msm_uport)) + set_rfr_locked(uport, 1); /* fall-through */ case MSM_HS_CLK_REQUEST_OFF: hrtimer_try_to_cancel(&msm_uport->clk_off_timer); @@ -1017,6 +1038,7 @@ static void msm_hs_request_clock_on_locked(struct uart_port *uport) { case MSM_HS_CLK_PORT_OFF: break; } } +EXPORT_SYMBOL(msm_hs_request_clock_on_locked); void msm_hs_request_clock_on(struct uart_port *uport) { unsigned long flags; @@ -1024,6 +1046,7 @@ void msm_hs_request_clock_on(struct uart_port *uport) { msm_hs_request_clock_on_locked(uport); spin_unlock_irqrestore(&uport->lock, flags); } +EXPORT_SYMBOL(msm_hs_request_clock_on); static irqreturn_t msm_hs_rx_wakeup_isr(int irq, void *dev) { @@ -1274,7 +1297,7 @@ static int __init msm_hs_probe(struct platform_device *pdev) if (unlikely(set_irq_wake(uport->irq, 1))) return -ENXIO; - if (pdata == NULL) + if (pdata == NULL || pdata->rx_wakeup_irq < 0) msm_uport->rx_wakeup.irq = -1; else { msm_uport->rx_wakeup.irq = pdata->rx_wakeup_irq; @@ -1288,6 +1311,11 @@ static int __init msm_hs_probe(struct platform_device *pdev) return -ENXIO; } + if (pdata == NULL) + msm_uport->exit_lpm_cb = NULL; + else + msm_uport->exit_lpm_cb = pdata->exit_lpm_cb; + resource = platform_get_resource_byname(pdev, IORESOURCE_DMA, "uartdm_channels"); if (unlikely(!resource)) From c52391f0c0bcc8e36798363734664354529128ff Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 29 Oct 2009 19:23:36 -0700 Subject: [PATCH 0359/2556] [ARM] msm: msm_serial_hs: Wait for RX fifo to empty before clocking off. This is needed if we clock off quickly after RX. Signed-off-by: Nick Pelly --- drivers/tty/serial/msm_serial_hs.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 455aafb9d5074..26f10c7e3fcf6 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -610,6 +610,10 @@ static void msm_hs_start_rx_locked(struct uart_port *uport) msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_ENABLE); msm_uport->imr_reg |= UARTDM_ISR_RXLEV_BMSK; msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + + /* might have finished RX and be ready to clock off */ + hrtimer_start(&msm_uport->clk_off_timer, msm_uport->clk_off_delay, + HRTIMER_MODE_REL); } /* Enable the transmitter Interrupt */ @@ -874,10 +878,11 @@ static int msm_hs_check_clock_off_locked(struct uart_port *uport) struct circ_buf *tx_buf = &uport->info->xmit; /* Cancel if tx tty buffer is not empty, dma is in flight, - * or tx fifo is not empty */ + * or tx fifo is not empty, or rx fifo is not empty */ if (msm_uport->clk_state != MSM_HS_CLK_REQUEST_OFF || !uart_circ_empty(tx_buf) || msm_uport->tx.dma_in_flight || - msm_uport->imr_reg & UARTDM_ISR_TXLEV_BMSK) { + (msm_uport->imr_reg & UARTDM_ISR_TXLEV_BMSK) || + !(msm_uport->imr_reg & UARTDM_ISR_RXLEV_BMSK)) { return -1; } From 1cea44bd0474cb7200257fa039a126cac4cfc285 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 4 Nov 2009 20:30:42 -0800 Subject: [PATCH 0360/2556] [ARM] msm: msm_serial_hs: Do not allow suspend while BT DMA could be active. Signed-off-by: Nick Pelly --- drivers/tty/serial/msm_serial_hs.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 26f10c7e3fcf6..8526510fdce24 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -138,6 +138,8 @@ struct msm_hs_port { struct msm_hs_rx_wakeup rx_wakeup; /* optional callback to exit low power mode */ void (*exit_lpm_cb)(struct uart_port *); + + struct wake_lock dma_wake_lock; /* held while any DMA active */ }; #define MSM_UARTDM_BURST_SIZE 16 /* DM burst size (in bytes) */ @@ -208,6 +210,7 @@ static int __devexit msm_hs_remove(struct platform_device *pdev) DMA_TO_DEVICE); wake_lock_destroy(&msm_uport->rx.wake_lock); + wake_lock_destroy(&msm_uport->dma_wake_lock); uart_remove_one_port(&msm_hs_driver, &msm_uport->uport); clk_put(msm_uport->clk); @@ -230,6 +233,7 @@ static int msm_hs_init_clk_locked(struct uart_port *uport) int ret; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + wake_lock(&msm_uport->dma_wake_lock); ret = clk_enable(msm_uport->clk); if (ret) { printk(KERN_ERR "Error could not turn on UART clk\n"); @@ -902,6 +906,7 @@ static int msm_hs_check_clock_off_locked(struct uart_port *uport) set_rfr_locked(uport, 0); clk_disable(msm_uport->clk); msm_uport->clk_state = MSM_HS_CLK_OFF; + wake_unlock(&msm_uport->dma_wake_lock); if (use_low_power_rx_wakeup(msm_uport)) { msm_uport->rx_wakeup.ignore = 1; enable_irq(msm_uport->rx_wakeup.irq); @@ -1026,6 +1031,7 @@ void msm_hs_request_clock_on_locked(struct uart_port *uport) { switch (msm_uport->clk_state) { case MSM_HS_CLK_OFF: + wake_lock(&msm_uport->dma_wake_lock); clk_enable(msm_uport->clk); disable_irq(msm_uport->rx_wakeup.irq); if (!use_low_power_rx_wakeup(msm_uport)) @@ -1237,6 +1243,8 @@ static int uartdm_init_port(struct uart_port *uport) init_waitqueue_head(&rx->wait); wake_lock_init(&rx->wake_lock, WAKE_LOCK_SUSPEND, "msm_serial_hs_rx"); + wake_lock_init(&msm_uport->dma_wake_lock, WAKE_LOCK_SUSPEND, + "msm_serial_hs_dma"); rx->pool = dma_pool_create("rx_buffer_pool", uport->dev, UARTDM_RX_BUF_SIZE, 16, 0); @@ -1418,8 +1426,10 @@ static void msm_hs_shutdown(struct uart_port *uport) wait_event(msm_uport->rx.wait, msm_uport->rx.flush == FLUSH_SHUTDOWN); clk_disable(msm_uport->clk); /* to balance local clk_enable() */ - if (msm_uport->clk_state != MSM_HS_CLK_OFF) + if (msm_uport->clk_state != MSM_HS_CLK_OFF) { + wake_unlock(&msm_uport->dma_wake_lock); clk_disable(msm_uport->clk); /* to balance clk_state */ + } msm_uport->clk_state = MSM_HS_CLK_PORT_OFF; dma_unmap_single(uport->dev, msm_uport->tx.dma_base, From 0edf01bf1691984490867ff14c6497e362e74f3c Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Fri, 20 Nov 2009 13:31:36 -0800 Subject: [PATCH 0361/2556] [ARM] msm: msm_serial_hs: Improve RX flush procedure during clock off sequence. Clock off is requested by msm_hs_request_clock_off. The new procedure is: 1) Immediately deassert RTS to prevent new RX data. 2) Wait for the TX pipeline to flush (TXLEV, TXEMT). 3) Issue a FORCE_STALE_EVENT command, and flush on RXSTALE. 5) After this flush completes, disable DLINK and flush one more time (discarding data). 6) Finally clock off the uart. The key changes are to disable DLINK - which fixes the datamover lockup, and the issuing of the FORCE_STALE_EVENT command - which prevents RX data loss during clock off sequence. Signed-off-by: Nick Pelly --- drivers/tty/serial/msm_serial_hs.c | 62 +++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 8526510fdce24..8b04ac547e8c2 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -79,8 +79,17 @@ enum flush_reason { enum msm_hs_clk_states_e { MSM_HS_CLK_PORT_OFF, /* port not in use */ MSM_HS_CLK_OFF, /* clock disabled */ - MSM_HS_CLK_REQUEST_OFF, /* disable after TX flushed */ - MSM_HS_CLK_ON, /* clock disabled */ + MSM_HS_CLK_REQUEST_OFF, /* disable after TX and RX flushed */ + MSM_HS_CLK_ON, /* clock enabled */ +}; + +/* Track the forced RXSTALE flush during clock off sequence. + * These states are only valid during MSM_HS_CLK_REQUEST_OFF */ +enum msm_hs_clk_req_off_state_e { + CLK_REQ_OFF_START, + CLK_REQ_OFF_RXSTALE_ISSUED, + CLK_REQ_OFF_FLUSH_ISSUED, + CLK_REQ_OFF_RXSTALE_FLUSHED, }; struct msm_hs_tx { @@ -134,6 +143,7 @@ struct msm_hs_port { struct hrtimer clk_off_timer; /* to poll TXEMT before clock off */ ktime_t clk_off_delay; enum msm_hs_clk_states_e clk_state; + enum msm_hs_clk_req_off_state_e clk_req_off_state; struct msm_hs_rx_wakeup rx_wakeup; /* optional callback to exit low power mode */ @@ -534,9 +544,15 @@ static void msm_hs_stop_tx_locked(struct uart_port *uport) static void msm_hs_stop_rx_locked(struct uart_port *uport) { struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + unsigned int data; clk_enable(msm_uport->clk); + /* disable dlink */ + data = msm_hs_read(uport, UARTDM_DMEN_ADDR); + data &= ~UARTDM_RX_DM_EN_BMSK; + msm_hs_write(uport, UARTDM_DMEN_ADDR, data); + /* Disable the receiver */ if (msm_uport->rx.flush == FLUSH_NONE) { wake_lock(&msm_uport->rx.wake_lock); @@ -606,15 +622,15 @@ static void msm_hs_start_rx_locked(struct uart_port *uport) { struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - msm_uport->rx.flush = FLUSH_NONE; - msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel, &msm_uport->rx.xfer); - msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT); msm_hs_write(uport, UARTDM_DMRX_ADDR, UARTDM_RX_BUF_SIZE); msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_ENABLE); msm_uport->imr_reg |= UARTDM_ISR_RXLEV_BMSK; msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); + msm_uport->rx.flush = FLUSH_NONE; + msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel, &msm_uport->rx.xfer); + /* might have finished RX and be ready to clock off */ hrtimer_start(&msm_uport->clk_off_timer, msm_uport->clk_off_delay, HRTIMER_MODE_REL); @@ -720,6 +736,9 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, if (error_f) msm_hs_write(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS); + if (msm_uport->clk_req_off_state == CLK_REQ_OFF_FLUSH_ISSUED) + msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_FLUSHED; + flush = msm_uport->rx.flush; if (flush == FLUSH_IGNORE) msm_hs_start_rx_locked(uport); @@ -895,6 +914,19 @@ static int msm_hs_check_clock_off_locked(struct uart_port *uport) if (!(sr_status & UARTDM_SR_TXEMT_BMSK)) return 0; /* retry */ + /* Make sure forced RXSTALE flush complete */ + switch (msm_uport->clk_req_off_state) { + case CLK_REQ_OFF_START: + msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_ISSUED; + msm_hs_write(uport, UARTDM_CR_ADDR, FORCE_STALE_EVENT); + return 0; /* RXSTALE flush not complete - retry */ + case CLK_REQ_OFF_RXSTALE_ISSUED: + case CLK_REQ_OFF_FLUSH_ISSUED: + return 0; /* RXSTALE flush not complete - retry */ + case CLK_REQ_OFF_RXSTALE_FLUSHED: + break; /* continue */ + } + if (msm_uport->rx.flush != FLUSH_SHUTDOWN) { if (msm_uport->rx.flush == FLUSH_NONE) msm_hs_stop_rx_locked(uport); @@ -902,8 +934,6 @@ static int msm_hs_check_clock_off_locked(struct uart_port *uport) } /* we really want to clock off */ - if (!use_low_power_rx_wakeup(msm_uport)) - set_rfr_locked(uport, 0); clk_disable(msm_uport->clk); msm_uport->clk_state = MSM_HS_CLK_OFF; wake_unlock(&msm_uport->dma_wake_lock); @@ -958,6 +988,9 @@ static irqreturn_t msm_hs_isr(int irq, void *dev) msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT); + if (msm_uport->clk_req_off_state == CLK_REQ_OFF_RXSTALE_ISSUED) + msm_uport->clk_req_off_state = + CLK_REQ_OFF_FLUSH_ISSUED; if (rx->flush == FLUSH_NONE) { rx->flush = FLUSH_DATA_READY; msm_dmov_flush(msm_uport->dma_rx_channel); @@ -1010,6 +1043,9 @@ void msm_hs_request_clock_off_locked(struct uart_port *uport) { if (msm_uport->clk_state == MSM_HS_CLK_ON) { msm_uport->clk_state = MSM_HS_CLK_REQUEST_OFF; + msm_uport->clk_req_off_state = CLK_REQ_OFF_START; + if (!use_low_power_rx_wakeup(msm_uport)) + set_rfr_locked(uport, 0); msm_uport->imr_reg |= UARTDM_ISR_TXLEV_BMSK; msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); } @@ -1028,19 +1064,27 @@ EXPORT_SYMBOL(msm_hs_request_clock_off); void msm_hs_request_clock_on_locked(struct uart_port *uport) { struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); + unsigned int data; switch (msm_uport->clk_state) { case MSM_HS_CLK_OFF: wake_lock(&msm_uport->dma_wake_lock); clk_enable(msm_uport->clk); disable_irq(msm_uport->rx_wakeup.irq); - if (!use_low_power_rx_wakeup(msm_uport)) - set_rfr_locked(uport, 1); /* fall-through */ case MSM_HS_CLK_REQUEST_OFF: + if (msm_uport->rx.flush == FLUSH_STOP || + msm_uport->rx.flush == FLUSH_SHUTDOWN) { + msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX); + data = msm_hs_read(uport, UARTDM_DMEN_ADDR); + data |= UARTDM_RX_DM_EN_BMSK; + msm_hs_write(uport, UARTDM_DMEN_ADDR, data); + } hrtimer_try_to_cancel(&msm_uport->clk_off_timer); if (msm_uport->rx.flush == FLUSH_SHUTDOWN) msm_hs_start_rx_locked(uport); + if (!use_low_power_rx_wakeup(msm_uport)) + set_rfr_locked(uport, 1); if (msm_uport->rx.flush == FLUSH_STOP) msm_uport->rx.flush = FLUSH_IGNORE; msm_uport->clk_state = MSM_HS_CLK_ON; From ae87514dc7f7c4f317ebe9a3b15a8d3cdb9543e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 10 Dec 2009 17:13:01 -0800 Subject: [PATCH 0362/2556] msm_serial_hs: fix to build on 2.6.32 msm_serial_hs: fix lockup on 2.6.32 --- drivers/tty/serial/msm_serial_hs.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 8b04ac547e8c2..b2043028934f5 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -572,9 +572,9 @@ static void msm_hs_submit_tx_locked(struct uart_port *uport) dma_addr_t src_addr; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); struct msm_hs_tx *tx = &msm_uport->tx; - struct circ_buf *tx_buf = &msm_uport->uport.info->xmit; + struct circ_buf *tx_buf = &msm_uport->uport.state->xmit; - if (uart_circ_empty(tx_buf) || uport->info->port.tty->stopped) { + if (uart_circ_empty(tx_buf) || uport->state->port.tty->stopped) { msm_hs_stop_tx_locked(uport); return; } @@ -708,7 +708,7 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, spin_lock_irqsave(&uport->lock, flags); clk_enable(msm_uport->clk); - tty = uport->info->port.tty; + tty = uport->state->port.tty; msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE); @@ -886,7 +886,7 @@ static void msm_hs_handle_delta_cts(struct uart_port *uport) spin_unlock_irqrestore(&uport->lock, flags); /* clear the IOCTL TIOCMIWAIT if called */ - wake_up_interruptible(&uport->info->delta_msr_wait); + wake_up_interruptible(&uport->state->port.delta_msr_wait); } /* check if the TX path is flushed, and if so clock off @@ -898,7 +898,7 @@ static int msm_hs_check_clock_off_locked(struct uart_port *uport) { unsigned long sr_status; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - struct circ_buf *tx_buf = &uport->info->xmit; + struct circ_buf *tx_buf = &uport->state->xmit; /* Cancel if tx tty buffer is not empty, dma is in flight, * or tx fifo is not empty, or rx fifo is not empty */ @@ -969,7 +969,7 @@ static irqreturn_t msm_hs_isr(int irq, void *dev) unsigned long isr_status; struct msm_hs_port *msm_uport = (struct msm_hs_port *)dev; struct uart_port *uport = &msm_uport->uport; - struct circ_buf *tx_buf = &uport->info->xmit; + struct circ_buf *tx_buf = &uport->state->xmit; struct msm_hs_tx *tx = &msm_uport->tx; struct msm_hs_rx *rx = &msm_uport->rx; @@ -1070,7 +1070,7 @@ void msm_hs_request_clock_on_locked(struct uart_port *uport) { case MSM_HS_CLK_OFF: wake_lock(&msm_uport->dma_wake_lock); clk_enable(msm_uport->clk); - disable_irq(msm_uport->rx_wakeup.irq); + disable_irq_nosync(msm_uport->rx_wakeup.irq); /* fall-through */ case MSM_HS_CLK_REQUEST_OFF: if (msm_uport->rx.flush == FLUSH_STOP || @@ -1126,7 +1126,7 @@ static irqreturn_t msm_hs_rx_wakeup_isr(int irq, void *dev) * optionally inject char into tty rx */ msm_hs_request_clock_on_locked(uport); if (msm_uport->rx_wakeup.inject_rx) { - tty = uport->info->port.tty; + tty = uport->state->port.tty; tty_insert_flip_char(tty, msm_uport->rx_wakeup.rx_to_inject, TTY_NORMAL); @@ -1153,7 +1153,7 @@ static int msm_hs_startup(struct uart_port *uport) unsigned long flags; unsigned int data; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); - struct circ_buf *tx_buf = &uport->info->xmit; + struct circ_buf *tx_buf = &uport->state->xmit; struct msm_hs_tx *tx = &msm_uport->tx; struct msm_hs_rx *rx = &msm_uport->rx; From 7b33a83284defe857ad4a831de8c73d9a62ce0e0 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 2 Nov 2009 10:32:46 -0800 Subject: [PATCH 0363/2556] [ARM] mach: htc_pwrsink: Remove stub printk Signed-off-by: San Mehat --- arch/arm/mach-msm/include/mach/htc_pwrsink.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-msm/include/mach/htc_pwrsink.h b/arch/arm/mach-msm/include/mach/htc_pwrsink.h index d82c21273a45b..c7a91f1d906cf 100644 --- a/arch/arm/mach-msm/include/mach/htc_pwrsink.h +++ b/arch/arm/mach-msm/include/mach/htc_pwrsink.h @@ -68,7 +68,6 @@ struct pwr_sink_platform_data { #ifndef CONFIG_HTC_PWRSINK static inline int htc_pwrsink_set(pwrsink_id_type id, unsigned percent) { - printk(KERN_DEBUG "%s:STUB!\n", __func__); return 0; } static inline int htc_pwrsink_audio_set(pwrsink_audio_id_type id, From 76b172a5b30e1e053677e434a5c4ed9f61a392b8 Mon Sep 17 00:00:00 2001 From: Kuma Chang Date: Thu, 12 Nov 2009 16:41:01 -0800 Subject: [PATCH 0364/2556] [ARM] msm: qdsp5: add a NULL-pointer check on module->ops->event Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/qdsp5/adsp.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/qdsp5/adsp.c b/arch/arm/mach-msm/qdsp5/adsp.c index a92fc0519eab7..b45f52c9840e1 100644 --- a/arch/arm/mach-msm/qdsp5/adsp.c +++ b/arch/arm/mach-msm/qdsp5/adsp.c @@ -710,9 +710,11 @@ static void handle_adsp_rtos_mtoa_app(struct rpc_request_hdr *req) RPC_ACCEPTSTAT_SUCCESS); mutex_unlock(&module->lock); #ifdef CONFIG_MSM_ADSP_REPORT_EVENTS - modem_event_addr = (uint32_t *)req; - module->ops->event(module->driver_data, EVENT_MSG_ID, - EVENT_LEN, read_modem_event); + if (module->ops != NULL && module->ops->event != NULL) { + modem_event_addr = (uint32_t *)req; + module->ops->event(module->driver_data, EVENT_MSG_ID, + EVENT_LEN, read_modem_event); + } #endif } From 5f24916c112544b7439cfe7a89389c437561088c Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Mon, 16 Nov 2009 13:23:40 -0800 Subject: [PATCH 0365/2556] [ARM] msm: pm: msm_sleep() hooks for clock.c Signed-off-by: Mike Chan --- arch/arm/mach-msm/clock.c | 8 ++++++++ arch/arm/mach-msm/clock.h | 3 +++ arch/arm/mach-msm/pm.c | 3 +++ 3 files changed, 14 insertions(+) diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c index 2069bfaa3a261..be5dfc83182e5 100644 --- a/arch/arm/mach-msm/clock.c +++ b/arch/arm/mach-msm/clock.c @@ -177,6 +177,14 @@ int clk_set_flags(struct clk *clk, unsigned long flags) } EXPORT_SYMBOL(clk_set_flags); +void clk_enter_sleep(int from_idle) +{ +} + +void clk_exit_sleep(void) +{ +} + /* EBI1 is the only shared clock that several clients want to vote on as of * this commit. If this changes in the future, then it might be better to * make clk_min_rate handle the voting or make ebi1_clk_set_min_rate more diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h index 5db5c3e9c928f..cb7959ca14a94 100644 --- a/arch/arm/mach-msm/clock.h +++ b/arch/arm/mach-msm/clock.h @@ -106,5 +106,8 @@ int msm_clock_get_name(uint32_t id, char *name, uint32_t size); int ebi1_clk_set_min_rate(enum clkvote_client client, unsigned long rate); unsigned long clk_get_max_axi_khz(void); +void clk_enter_sleep(int from_idle); +void clk_exit_sleep(void); + #endif diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index fc1e11873b579..06f401711aa03 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -31,6 +31,7 @@ #include "smd_private.h" #include "acpuclock.h" #include "proc_comm.h" +#include "clock.h" #ifdef CONFIG_HAS_WAKELOCK #include #endif @@ -201,6 +202,7 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) exit_state = 0; } + clk_enter_sleep(from_idle); msm_irq_enter_sleep1(!!enter_state, from_idle); msm_gpio_enter_sleep(from_idle); @@ -317,6 +319,7 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) msm_irq_exit_sleep3(); msm_gpio_exit_sleep(); smd_sleep_exit(); + clk_exit_sleep(); return rv; } From b707c9910eaf463a274bd6192734f6493a0bf76c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 20 Oct 2009 17:29:24 -0700 Subject: [PATCH 0366/2556] [ARM] msm: clock: Use hlist instead of list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Id0bb9f2a6fe73ef05a818e7e5bd627c0052493b0 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/clock.c | 12 +++++++----- arch/arm/mach-msm/clock.h | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c index be5dfc83182e5..898a17f8600ff 100644 --- a/arch/arm/mach-msm/clock.c +++ b/arch/arm/mach-msm/clock.c @@ -32,7 +32,7 @@ static DEFINE_MUTEX(clocks_mutex); static DEFINE_SPINLOCK(clocks_lock); -static LIST_HEAD(clocks); +static HLIST_HEAD(clocks); struct clk *msm_clocks; unsigned msm_num_clocks; @@ -49,14 +49,15 @@ static DEFINE_SPINLOCK(clock_map_lock); struct clk *clk_get(struct device *dev, const char *id) { struct clk *clk; + struct hlist_node *pos; mutex_lock(&clocks_mutex); - list_for_each_entry(clk, &clocks, list) + hlist_for_each_entry(clk, pos, &clocks, list) if (!strcmp(id, clk->name) && clk->dev == dev) goto found_it; - list_for_each_entry(clk, &clocks, list) + hlist_for_each_entry(clk, pos, &clocks, list) if (!strcmp(id, clk->name) && clk->dev == NULL) goto found_it; @@ -210,7 +211,7 @@ void __init msm_clock_init(struct clk *clock_tbl, unsigned num_clocks) msm_num_clocks = num_clocks; for (n = 0; n < msm_num_clocks; n++) { set_clock_ops(&msm_clocks[n]); - list_add_tail(&msm_clocks[n].list, &clocks); + hlist_add_head(&msm_clocks[n].list, &clocks); } mutex_unlock(&clocks_mutex); @@ -336,10 +337,11 @@ static int __init clock_late_init(void) { unsigned long flags; struct clk *clk; + struct hlist_node *pos; unsigned count = 0; mutex_lock(&clocks_mutex); - list_for_each_entry(clk, &clocks, list) { + hlist_for_each_entry(clk, pos, &clocks, list) { if (clk->flags & CLKFLAG_AUTO_OFF) { spin_lock_irqsave(&clocks_lock, flags); if (!clk->count) { diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h index cb7959ca14a94..52577f1b212b5 100644 --- a/arch/arm/mach-msm/clock.h +++ b/arch/arm/mach-msm/clock.h @@ -56,7 +56,7 @@ struct clk { const char *name; struct clk_ops *ops; const char *dbg_name; - struct list_head list; + struct hlist_node list; struct device *dev; }; From 039944a8e2630c6cc3444f779aa7ab4e9a1c159b Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Date: Wed, 19 Nov 2008 16:24:43 -0800 Subject: [PATCH 0367/2556] [ARM] msm: Adding display drivers. Adds mddi, mdp and msm_fb drivers. Change-Id: I6da4c81f2cbc2777f1c99808d07fdf4256e42c84 Signed-off-by: Rebecca Schultz --- arch/arm/mach-msm/devices.h | 3 + drivers/video/msm/Makefile | 3 + drivers/video/msm/logo.c | 98 ++++++++++++++++++ drivers/video/msm/mddi.c | 121 +++++++++++++++++++++- drivers/video/msm/mdp_ppp.c | 21 +++- drivers/video/msm/msm_fb.c | 196 ++++++++++++++++++++++++++++++++++++ 6 files changed, 440 insertions(+), 2 deletions(-) create mode 100644 drivers/video/msm/logo.c diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h index b7206c80ab051..8ee78f08b1224 100644 --- a/arch/arm/mach-msm/devices.h +++ b/arch/arm/mach-msm/devices.h @@ -39,6 +39,9 @@ extern struct platform_device msm_device_i2c; extern struct platform_device msm_device_smd; extern struct platform_device msm_device_nand; +extern struct platform_device msm_device_mddi0; +extern struct platform_device msm_device_mddi1; +extern struct platform_device msm_device_mdp; extern struct platform_device msm_device_mddi0; extern struct platform_device msm_device_mddi1; diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile index 802d6ae523fb3..992a74173b887 100644 --- a/drivers/video/msm/Makefile +++ b/drivers/video/msm/Makefile @@ -2,6 +2,9 @@ # core framebuffer # obj-y := msm_fb.o +ifeq ($(CONFIG_FB_MSM_LOGO),y) +obj-y += logo.o +endif # MDP DMA/PPP engine # diff --git a/drivers/video/msm/logo.c b/drivers/video/msm/logo.c new file mode 100644 index 0000000000000..7272765f48cd0 --- /dev/null +++ b/drivers/video/msm/logo.c @@ -0,0 +1,98 @@ +/* drivers/video/msm/logo.c + * + * Show Logo in RLE 565 format + * + * Copyright (C) 2008 Google Incorporated + * + * 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 + +#define fb_width(fb) ((fb)->var.xres) +#define fb_height(fb) ((fb)->var.yres) +#define fb_size(fb) ((fb)->var.xres * (fb)->var.yres * 2) + +static void memset16(void *_ptr, unsigned short val, unsigned count) +{ + unsigned short *ptr = _ptr; + count >>= 1; + while (count--) + *ptr++ = val; +} + +/* 565RLE image format: [count(2 bytes), rle(2 bytes)] */ +int load_565rle_image(char *filename) +{ + struct fb_info *info; + int fd, err = 0; + unsigned count, max; + unsigned short *data, *bits, *ptr; + + info = registered_fb[0]; + if (!info) { + printk(KERN_WARNING "%s: Can not access framebuffer\n", + __func__); + return -ENODEV; + } + + fd = sys_open(filename, O_RDONLY, 0); + if (fd < 0) { + printk(KERN_WARNING "%s: Can not open %s\n", + __func__, filename); + return -ENOENT; + } + count = (unsigned)sys_lseek(fd, (off_t)0, 2); + if (count == 0) { + sys_close(fd); + err = -EIO; + goto err_logo_close_file; + } + sys_lseek(fd, (off_t)0, 0); + data = kmalloc(count, GFP_KERNEL); + if (!data) { + printk(KERN_WARNING "%s: Can not alloc data\n", __func__); + err = -ENOMEM; + goto err_logo_close_file; + } + if ((unsigned)sys_read(fd, (char *)data, count) != count) { + err = -EIO; + goto err_logo_free_data; + } + + max = fb_width(info) * fb_height(info); + ptr = data; + bits = (unsigned short *)(info->screen_base); + while (count > 3) { + unsigned n = ptr[0]; + if (n > max) + break; + memset16(bits, ptr[1], n << 1); + bits += n; + max -= n; + ptr += 2; + count -= 4; + } + +err_logo_free_data: + kfree(data); +err_logo_close_file: + sys_close(fd); + return err; +} +EXPORT_SYMBOL(load_565rle_image); diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c index b66d86ac7cea6..5ad08a4b49358 100644 --- a/drivers/video/msm/mddi.c +++ b/drivers/video/msm/mddi.c @@ -29,6 +29,10 @@ #include #include #include +#include +#include +#include + #include #include "mddi_hw.h" @@ -40,6 +44,8 @@ #define CMD_GET_CLIENT_CAP 0x0601 #define CMD_GET_CLIENT_STATUS 0x0602 +static uint32_t mddi_debug_flags; + union mddi_rev { unsigned char raw[MDDI_REV_BUFFER_SIZE]; struct mddi_rev_packet hdr; @@ -84,6 +90,9 @@ struct mddi_info { struct mddi_client_caps caps; struct mddi_client_status status; + struct wake_lock idle_lock; + struct wake_lock link_active_idle_lock; + void (*power_client)(struct msm_mddi_client_data *, int); /* client device published to bind us to the @@ -103,7 +112,7 @@ void mddi_activate_link(struct msm_mddi_client_data *cdata) { struct mddi_info *mddi = container_of(cdata, struct mddi_info, client_data); - + wake_lock(&mddi->link_active_idle_lock); mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD); } @@ -127,6 +136,8 @@ static void mddi_handle_rev_data(struct mddi_info *mddi, union mddi_rev *rev) if ((rev->hdr.length <= MDDI_REV_BUFFER_SIZE - 2) && (rev->hdr.length >= sizeof(struct mddi_rev_packet) - 2)) { + /* printk(KERN_INFO "rev: len=%04x type=%04x\n", + * rev->hdr.length, rev->hdr.type); */ switch (rev->hdr.type) { case TYPE_CLIENT_CAPS: @@ -142,6 +153,9 @@ static void mddi_handle_rev_data(struct mddi_info *mddi, union mddi_rev *rev) wake_up(&mddi->int_wait); break; case TYPE_REGISTER_ACCESS: + /* printk(KERN_INFO "rev: reg %x = %x\n", + * rev->reg.register_address, + * rev->reg.register_data_list); */ ri = mddi->reg_read; if (ri == 0) { printk(KERN_INFO "rev: got reg %x = %x without " @@ -201,6 +215,8 @@ static void mddi_handle_rev_data_avail(struct mddi_info *mddi) rev_crc_err_count = mddi_readl(REV_CRC_ERR); if (rev_data_count > 1) printk(KERN_INFO "rev_data_count %d\n", rev_data_count); + /* printk(KERN_INFO "rev_data_count %d, INT %x\n", rev_data_count, + * mddi_readl(INT)); */ if (rev_crc_err_count) { printk(KERN_INFO "rev_crc_err_count %d, INT %x\n", @@ -220,6 +236,20 @@ static void mddi_handle_rev_data_avail(struct mddi_info *mddi) if (rev_data_count == 0) return; + if (mddi_debug_flags & 1) { + printk(KERN_INFO "INT %x, STAT %x, CURR_REV_PTR %x\n", + mddi_readl(INT), mddi_readl(STAT), + mddi_readl(CURR_REV_PTR)); + for (i = 0; i < MDDI_REV_BUFFER_SIZE; i++) { + if ((i % 16) == 0) + printk(KERN_INFO "\n"); + printk(KERN_INFO " %02x", rev->raw[i]); + } + printk(KERN_INFO "\n"); + } + + /* printk(KERN_INFO "rev_data_curr %d + %d\n", mddi->rev_data_curr, + * crev->hdr.length); */ prev_offset = mddi->rev_data_curr; length = *((uint8_t *)mddi->rev_data + mddi->rev_data_curr); @@ -245,12 +275,23 @@ static void mddi_handle_rev_data_avail(struct mddi_info *mddi) memcpy(&tmprev.raw[0], mddi->rev_data + prev_offset, rem); memcpy(&tmprev.raw[rem], mddi->rev_data, 2 + length - rem); mddi_handle_rev_data(mddi, &tmprev); + if (mddi_debug_flags & 2) { + memset(mddi->rev_data + prev_offset, 0xee, rem); + memset(mddi->rev_data, 0xee, mddi->rev_data_curr); + } } else { mddi_handle_rev_data(mddi, crev); + if (mddi_debug_flags & 2) + memset(mddi->rev_data + prev_offset, 0xee, + mddi->rev_data_curr - prev_offset); } + /* if(mddi->rev_data_curr + MDDI_MAX_REV_PKT_SIZE >= + * MDDI_REV_BUFFER_SIZE) { */ if (prev_offset < MDDI_REV_BUFFER_SIZE / 2 && mddi->rev_data_curr >= MDDI_REV_BUFFER_SIZE / 2) { + /* printk(KERN_INFO "passed buffer half full: rev_data_curr + * %d\n", mddi->rev_data_curr); */ mddi_writel(mddi->rev_addr, REV_PTR); } } @@ -269,6 +310,9 @@ static irqreturn_t mddi_isr(int irq, void *data) mddi_writel(active, INT); + /* printk(KERN_INFO "%s: isr a=%08x e=%08x s=%08x\n", + mddi->name, active, mddi->int_enable, status); */ + /* ignore any interrupts we have disabled */ active &= mddi->int_enable; @@ -288,11 +332,13 @@ static irqreturn_t mddi_isr(int irq, void *data) if (active & MDDI_INT_LINK_ACTIVE) { mddi->int_enable &= (~MDDI_INT_LINK_ACTIVE); mddi->int_enable |= MDDI_INT_IN_HIBERNATION; + wake_lock(&mddi->link_active_idle_lock); } if (active & MDDI_INT_IN_HIBERNATION) { mddi->int_enable &= (~MDDI_INT_IN_HIBERNATION); mddi->int_enable |= MDDI_INT_LINK_ACTIVE; + wake_unlock(&mddi->link_active_idle_lock); } mddi_writel(mddi->int_enable, INTEN); @@ -379,7 +425,11 @@ static uint16_t mddi_init_registers(struct mddi_info *mddi) mddi_set_auto_hibernate(&mddi->client_data, 0); +#if 1 /* ignore listen */ mddi_writel(MDDI_CMD_DISP_IGNORE, CMD); +#else + mddi_writel(MDDI_CMD_DISP_LISTEN, CMD); +#endif mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); mddi_init_rev_encap(mddi); @@ -390,6 +440,7 @@ static void mddi_suspend(struct msm_mddi_client_data *cdata) { struct mddi_info *mddi = container_of(cdata, struct mddi_info, client_data); + wake_lock(&mddi->idle_lock); /* turn off the client */ if (mddi->power_client) mddi->power_client(&mddi->client_data, 0); @@ -398,12 +449,14 @@ static void mddi_suspend(struct msm_mddi_client_data *cdata) mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); /* turn off the clock */ clk_disable(mddi->clk); + wake_unlock(&mddi->idle_lock); } static void mddi_resume(struct msm_mddi_client_data *cdata) { struct mddi_info *mddi = container_of(cdata, struct mddi_info, client_data); + wake_lock(&mddi->idle_lock); mddi_set_auto_hibernate(&mddi->client_data, 0); /* turn on the client */ if (mddi->power_client) @@ -418,6 +471,7 @@ static void mddi_resume(struct msm_mddi_client_data *cdata) mddi_writel(MDDI_CMD_SEND_RTD, CMD); mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); mddi_set_auto_hibernate(&mddi->client_data, 1); + wake_unlock(&mddi->idle_lock); } static int __init mddi_get_client_caps(struct mddi_info *mddi) @@ -515,6 +569,7 @@ void mddi_remote_write(struct msm_mddi_client_data *cdata, uint32_t val, client_data); struct mddi_llentry *ll; struct mddi_register_access *ra; + /* unsigned s; */ mutex_lock(&mddi->reg_write_lock); @@ -538,9 +593,19 @@ void mddi_remote_write(struct msm_mddi_client_data *cdata, uint32_t val, ll->next = 0; ll->reserved = 0; + /* s = mddi_readl(STAT); */ + /* printk(KERN_INFO "mddi_remote_write(%x, %x), stat = %x\n", val, + * reg, s); */ + mddi_writel(mddi->reg_write_addr, PRI_PTR); + /* s = mddi_readl(STAT); */ + /* printk(KERN_INFO "mddi_remote_write(%x, %x) sent, stat = %x\n", + * val, reg, s); */ + mddi_wait_interrupt(mddi, MDDI_INT_PRI_LINK_LIST_DONE); + /* printk(KERN_INFO "mddi_remote_write(%x, %x) done, stat = %x\n", + * val, reg, s); */ mutex_unlock(&mddi->reg_write_lock); } @@ -576,6 +641,7 @@ uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg) ll->reserved = 0; s = mddi_readl(STAT); + /* printk(KERN_INFO "mddi_remote_read(%x), stat = %x\n", reg, s); */ ri.reg = reg; ri.status = -1; @@ -586,6 +652,14 @@ uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg) mddi_writel(mddi->reg_read_addr, PRI_PTR); mddi_wait_interrupt(mddi, MDDI_INT_PRI_LINK_LIST_DONE); + /* s = mddi_readl(STAT); */ + /* printk(KERN_INFO "mddi_remote_read(%x) sent, stat = %x\n", + * reg, s); */ + + /* s = mddi_readl(STAT); */ + /* while((s & MDDI_STAT_PRI_LINK_LIST_DONE) == 0){ */ + /* s = mddi_readl(STAT); */ + /* } */ /* Enable Periodic Reverse Encapsulation. */ mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 1, CMD); @@ -604,8 +678,14 @@ uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg) if (ri.status == 0) break; + /* printk(KERN_INFO "mddi_remote_read: failed, sent + * MDDI_CMD_SEND_RTD: int %x, stat %x, rtd val %x\n", + * mddi_readl(INT), mddi_readl(STAT), mddi_readl(RTD_VAL)); */ mddi_writel(MDDI_CMD_SEND_RTD, CMD); mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD); + /* printk(KERN_INFO "mddi_remote_read: failed, sent + * MDDI_CMD_SEND_RTD: int %x, stat %x, rtd val %x\n", + * mddi_readl(INT), mddi_readl(STAT), mddi_readl(RTD_VAL)); */ mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); printk(KERN_INFO "mddi_remote_read: failed, sent " "MDDI_CMD_SEND_RTD: int %x, stat %x, rtd val %x " @@ -615,6 +695,8 @@ uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg) /* Disable Periodic Reverse Encapsulation. */ mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 0, CMD); mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + /* printk(KERN_INFO "mddi_remote_read(%x) done, stat = %x, + * return %x\n", reg, s, ri.result); */ mddi->reg_read = NULL; mutex_unlock(&mddi->reg_read_lock); return ri.result; @@ -701,6 +783,10 @@ static int __devinit mddi_probe(struct platform_device *pdev) spin_lock_init(&mddi->int_lock); init_waitqueue_head(&mddi->int_wait); + wake_lock_init(&mddi->idle_lock, WAKE_LOCK_IDLE, "mddi_idle_lock"); + wake_lock_init(&mddi->link_active_idle_lock, WAKE_LOCK_IDLE, + "mddi_link_active_idle_lock"); + ret = mddi_clk_setup(pdev, mddi, pdata->clk_rate); if (ret) { printk(KERN_ERR "mddi: failed to setup clock!\n"); @@ -803,6 +889,8 @@ static int __devinit mddi_probe(struct platform_device *pdev) dma_free_coherent(NULL, 0x1000, mddi->rev_data, mddi->rev_addr); error_rev_data: error_clk_setup: + wake_lock_destroy(&mddi->idle_lock); + wake_lock_destroy(&mddi->link_active_idle_lock); error_get_irq_resource: iounmap(mddi->base); error_ioremap: @@ -811,6 +899,37 @@ static int __devinit mddi_probe(struct platform_device *pdev) return ret; } +#if 0 /* read/write mddi registers from userspace */ +module_param_named(debug, mddi_debug_flags, uint, 0644); + +static uint32_t selected_register; +module_param_named(reg, selected_register, uint, 0644); + +static int set_reg(const char *val, struct kernel_param *kp) +{ + char *endp; + uint32_t l; + + if (!val) + return -EINVAL; + l = simple_strtoul(val, &endp, 0); + if (endp == val || ((uint32_t)l != l)) + return -EINVAL; + mddi_remote_write(kp->arg, l, selected_register); + return 0; +} + +static int get_reg(char *buffer, struct kernel_param *kp) +{ + int val; + val = mddi_remote_read(kp->arg, selected_register); + return sprintf(buffer, "%x", val); +} + +module_param_call(pmdh_val, set_reg, get_reg, &mddi_info[0], 0644); +module_param_call(emdh_val, set_reg, get_reg, &mddi_info[1], 0644); + +#endif static struct platform_driver mddi_driver = { .probe = mddi_probe, diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c index 2b6564e8bfeaa..579a7a6bf1c73 100644 --- a/drivers/video/msm/mdp_ppp.c +++ b/drivers/video/msm/mdp_ppp.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "mdp_hw.h" @@ -575,10 +576,28 @@ static int valid_src_dst(unsigned long src_start, unsigned long src_len, return 1; } - static void flush_imgs(struct mdp_blit_req *req, struct mdp_regs *regs, struct file *src_file, struct file *dst_file) { +#ifdef CONFIG_ANDROID_PMEM + uint32_t src0_len, src1_len, dst0_len, dst1_len; + + /* flush src images to memory before dma to mdp */ + get_len(&req->src, &req->src_rect, regs->src_bpp, &src0_len, + &src1_len); + flush_pmem_file(src_file, req->src.offset, src0_len); + if (IS_PSEUDOPLNR(req->src.format)) + flush_pmem_file(src_file, req->src.offset + src0_len, + src1_len); + + /* flush dst images */ + get_len(&req->dst, &req->dst_rect, regs->dst_bpp, &dst0_len, + &dst1_len); + flush_pmem_file(dst_file, req->dst.offset, dst0_len); + if (IS_PSEUDOPLNR(req->dst.format)) + flush_pmem_file(dst_file, req->dst.offset + dst0_len, + dst1_len); +#endif } static void get_chroma_addr(struct mdp_img *img, struct mdp_rect *rect, diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index ecdb8ff403572..67100fb054529 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -22,6 +22,8 @@ #include #include +#include +#include #include #include #include @@ -32,6 +34,12 @@ #include #include +#define MSMFB_DEBUG 1 +#ifdef CONFIG_FB_MSM_LOGO +#define INIT_IMAGE_FILE "/logo.rle" +extern int load_565rle_image(char *filename); +#endif + #define PRINT_FPS 0 #define PRINT_BLIT_TIME 0 @@ -78,6 +86,9 @@ struct msmfb_info { } update_info; char *black; + struct early_suspend earlier_suspend; + struct early_suspend early_suspend; + struct wake_lock idle_lock; spinlock_t update_lock; struct mutex panel_init_lock; wait_queue_head_t frame_wq; @@ -105,6 +116,12 @@ static void msmfb_handle_dma_interrupt(struct msmfb_callback *callback) unsigned long irq_flags; struct msmfb_info *msmfb = container_of(callback, struct msmfb_info, dma_callback); +#if PRINT_FPS + int64_t dt; + ktime_t now; + static int64_t frame_count; + static ktime_t last_sec; +#endif spin_lock_irqsave(&msmfb->update_lock, irq_flags); msmfb->frame_done = msmfb->frame_requested; @@ -113,6 +130,18 @@ static void msmfb_handle_dma_interrupt(struct msmfb_callback *callback) DLOG(SUSPEND_RESUME, "full update completed\n"); queue_work(msmfb->resume_workqueue, &msmfb->resume_work); } +#if PRINT_FPS + now = ktime_get(); + dt = ktime_to_ns(ktime_sub(now, last_sec)); + frame_count++; + if (dt > NSEC_PER_SEC) { + int64_t fps = frame_count * NSEC_PER_SEC * 100; + frame_count = 0; + last_sec = ktime_get(); + do_div(fps, dt); + DLOG(FPS, "fps * 100: %llu\n", fps); + } +#endif spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); wake_up(&msmfb->frame_wq); } @@ -181,6 +210,7 @@ static void msmfb_handle_vsync_interrupt(struct msmfb_callback *callback) { struct msmfb_info *msmfb = container_of(callback, struct msmfb_info, vsync_callback); + wake_unlock(&msmfb->idle_lock); msmfb_start_dma(msmfb); } @@ -201,6 +231,12 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, unsigned long irq_flags; int sleeping; int retry = 1; +#if PRINT_FPS + ktime_t t1, t2; + static uint64_t pans; + static uint64_t dt; + t1 = ktime_get(); +#endif DLOG(SHOW_UPDATES, "update %d %d %d %d %d %d\n", left, top, eright, ebottom, yoffset, pan_display); @@ -231,6 +267,7 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, msmfb->sleeping == UPDATING)) { if (retry && panel->request_vsync && (sleeping == AWAKE)) { + wake_lock_timeout(&msmfb->idle_lock, HZ/4); panel->request_vsync(panel, &msmfb->vsync_callback); retry = 0; @@ -247,6 +284,21 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, goto restart; } +#if PRINT_FPS + t2 = ktime_get(); + if (pan_display) { + uint64_t temp = ktime_to_ns(ktime_sub(t2, t1)); + do_div(temp, 1000); + dt += temp; + pans++; + if (pans > 1000) { + do_div(dt, pans); + DLOG(FPS, "ave_wait_time: %lld\n", dt); + dt = 0; + pans = 0; + } + } +#endif msmfb->frame_requested++; /* if necessary, update the y offset, if this is the @@ -282,6 +334,7 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, * for 16 ms (long enough for the dma to panel) and then begin dma */ msmfb->vsync_request_time = ktime_get(); if (panel->request_vsync && (sleeping == AWAKE)) { + wake_lock_timeout(&msmfb->idle_lock, HZ/4); panel->request_vsync(panel, &msmfb->vsync_callback); } else { if (!hrtimer_active(&msmfb->fake_vsync)) { @@ -308,11 +361,13 @@ static void power_on_panel(struct work_struct *work) mutex_lock(&msmfb->panel_init_lock); DLOG(SUSPEND_RESUME, "turning on panel\n"); if (msmfb->sleeping == UPDATING) { + wake_lock_timeout(&msmfb->idle_lock, HZ); if (panel->unblank(panel)) { printk(KERN_INFO "msmfb: panel unblank failed," "not starting drawing\n"); goto error; } + wake_unlock(&msmfb->idle_lock); spin_lock_irqsave(&msmfb->update_lock, irq_flags); msmfb->sleeping = AWAKE; wake_up(&msmfb->frame_wq); @@ -322,6 +377,61 @@ static void power_on_panel(struct work_struct *work) mutex_unlock(&msmfb->panel_init_lock); } +#ifdef CONFIG_HAS_EARLYSUSPEND +/* turn off the panel */ +static void msmfb_earlier_suspend(struct early_suspend *h) +{ + struct msmfb_info *msmfb = container_of(h, struct msmfb_info, + earlier_suspend); + struct msm_panel_data *panel = msmfb->panel; + unsigned long irq_flags; + + mutex_lock(&msmfb->panel_init_lock); + msmfb->sleeping = SLEEPING; + wake_up(&msmfb->frame_wq); + spin_lock_irqsave(&msmfb->update_lock, irq_flags); + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + wait_event_timeout(msmfb->frame_wq, + msmfb->frame_requested == msmfb->frame_done, HZ/10); + + mdp->dma(mdp, virt_to_phys(msmfb->black), 0, + msmfb->fb->var.xres, msmfb->fb->var.yres, 0, 0, + NULL, panel->interface_type); + mdp->dma_wait(mdp); + + /* turn off the panel */ + panel->blank(panel); +} + +static void msmfb_suspend(struct early_suspend *h) +{ + struct msmfb_info *msmfb = container_of(h, struct msmfb_info, + early_suspend); + struct msm_panel_data *panel = msmfb->panel; + /* suspend the panel */ + panel->suspend(panel); + mutex_unlock(&msmfb->panel_init_lock); +} + +static void msmfb_resume(struct early_suspend *h) +{ + struct msmfb_info *msmfb = container_of(h, struct msmfb_info, + early_suspend); + struct msm_panel_data *panel = msmfb->panel; + unsigned long irq_flags; + + if (panel->resume(panel)) { + printk(KERN_INFO "msmfb: panel resume failed, not resuming " + "fb\n"); + return; + } + spin_lock_irqsave(&msmfb->update_lock, irq_flags); + msmfb->frame_requested = msmfb->frame_done = msmfb->update_frame = 0; + msmfb->sleeping = WAKING; + DLOG(SUSPEND_RESUME, "ready, waiting for full update\n"); + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); +} +#endif static int msmfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { @@ -344,6 +454,13 @@ int msmfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) /* "UPDT" */ if ((panel->caps & MSMFB_CAP_PARTIAL_UPDATES) && (var->reserved[0] == 0x54445055)) { +#if 0 + printk(KERN_INFO "pan frame %d-%d, rect %d %d %d %d\n", + msmfb->frame_requested, msmfb->frame_done, + var->reserved[1] & 0xffff, + var->reserved[1] >> 16, var->reserved[2] & 0xffff, + var->reserved[2] >> 16); +#endif msmfb_pan_update(info, var->reserved[1] & 0xffff, var->reserved[1] >> 16, var->reserved[2] & 0xffff, @@ -407,15 +524,26 @@ static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; int ret; +#if PRINT_BLIT_TIME + ktime_t t1, t2; +#endif switch (cmd) { case MSMFB_GRP_DISP: mdp->set_grp_disp(mdp, arg); break; case MSMFB_BLIT: +#if PRINT_BLIT_TIME + t1 = ktime_get(); +#endif ret = msmfb_blit(p, argp); if (ret) return ret; +#if PRINT_BLIT_TIME + t2 = ktime_get(); + DLOG(BLIT_TIME, "total %lld\n", + ktime_to_ns(t2) - ktime_to_ns(t1)); +#endif break; default: printk(KERN_INFO "msmfb unknown ioctl: %d\n", cmd); @@ -439,6 +567,44 @@ static struct fb_ops msmfb_ops = { static unsigned PP[16]; +#if MSMFB_DEBUG +static ssize_t debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + + +static ssize_t debug_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0; + struct msmfb_info *msmfb = (struct msmfb_info *)file->private_data; + unsigned long irq_flags; + + spin_lock_irqsave(&msmfb->update_lock, irq_flags); + n = scnprintf(buffer, debug_bufmax, "yoffset %d\n", msmfb->yoffset); + n += scnprintf(buffer + n, debug_bufmax, "frame_requested %d\n", + msmfb->frame_requested); + n += scnprintf(buffer + n, debug_bufmax, "frame_done %d\n", + msmfb->frame_done); + n += scnprintf(buffer + n, debug_bufmax, "sleeping %d\n", + msmfb->sleeping); + n += scnprintf(buffer + n, debug_bufmax, "update_frame %d\n", + msmfb->update_frame); + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static struct file_operations debug_fops = { + .read = debug_read, + .open = debug_open, +}; +#endif #define BITS_PER_PIXEL 16 @@ -577,6 +743,24 @@ static int msmfb_probe(struct platform_device *pdev) msmfb->black = kzalloc(msmfb->fb->var.bits_per_pixel*msmfb->xres, GFP_KERNEL); + wake_lock_init(&msmfb->idle_lock, WAKE_LOCK_IDLE, "msmfb_idle_lock"); + +#ifdef CONFIG_HAS_EARLYSUSPEND + msmfb->early_suspend.suspend = msmfb_suspend; + msmfb->early_suspend.resume = msmfb_resume; + msmfb->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + register_early_suspend(&msmfb->early_suspend); + + msmfb->earlier_suspend.suspend = msmfb_earlier_suspend; + msmfb->earlier_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN; + register_early_suspend(&msmfb->earlier_suspend); +#endif + +#if MSMFB_DEBUG + debugfs_create_file("msm_fb", S_IFREG | S_IRUGO, NULL, + (void *)fb->par, &debug_fops); +#endif + printk(KERN_INFO "msmfb_probe() installing %d x %d panel\n", msmfb->xres, msmfb->yres); @@ -594,9 +778,21 @@ static int msmfb_probe(struct platform_device *pdev) msmfb->sleeping = WAKING; +#ifdef CONFIG_FB_MSM_LOGO + if (!load_565rle_image(INIT_IMAGE_FILE)) { + /* Flip buffer */ + msmfb->update_info.left = 0; + msmfb->update_info.top = 0; + msmfb->update_info.eright = info->var.xres; + msmfb->update_info.ebottom = info->var.yres; + msmfb_pan_update(info, 0, 0, fb->var.xres, + fb->var.yres, 0, 1); + } +#endif return 0; error_register_framebuffer: + wake_lock_destroy(&msmfb->idle_lock); destroy_workqueue(msmfb->resume_workqueue); error_create_workqueue: iounmap(fb->screen_base); From f4225980383980ed32eb662c925850534749a0bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 14 Dec 2009 22:20:03 -0800 Subject: [PATCH 0368/2556] [ARM] msm_fb: Fix framebuffer console Don't allow non panning updates to bypass the wait for the panel to turn on. Change-Id: I32eedf37dd1b0d34cdcad782027b635e5c65f691 --- drivers/video/msm/msm_fb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index 67100fb054529..64dc0de8464ac 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -256,8 +256,8 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, sleeping = msmfb->sleeping; /* on a full update, if the last frame has not completed, wait for it */ - if (pan_display && (msmfb->frame_requested != msmfb->frame_done || - sleeping == UPDATING)) { + if ((pan_display && msmfb->frame_requested != msmfb->frame_done) || + sleeping == UPDATING) { int ret; spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); ret = wait_event_interruptible_timeout(msmfb->frame_wq, From 67b5d5d2a4ae4559257d41d051ca0bb34b69d632 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 9 Dec 2009 22:40:24 -0800 Subject: [PATCH 0369/2556] board-trout-panel from android-2.6.29 Change-Id: I16c9290dfa3dd0bf7825d14445e7e7767c57756d --- arch/arm/mach-msm/board-trout-panel.c | 74 +++++++++++++-------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/arch/arm/mach-msm/board-trout-panel.c b/arch/arm/mach-msm/board-trout-panel.c index dfd32d03cb414..f4bc83a85a229 100644 --- a/arch/arm/mach-msm/board-trout-panel.c +++ b/arch/arm/mach-msm/board-trout-panel.c @@ -356,7 +356,7 @@ static struct mddi_table mddi_tpo_deinit_table[] = { #define GPIOSEL_VWAKEINT (1U << 0) #define INTMASK_VWAKEOUT (1U << 0) -static void trout_process_mddi_table(struct msm_mddi_client_data *cdata, +static void trout_process_mddi_table(struct msm_mddi_client_data *client_data, struct mddi_table *table, size_t count) { int i; @@ -369,14 +369,14 @@ static void trout_process_mddi_table(struct msm_mddi_client_data *cdata, else if (reg == 1) msleep(value); else - cdata->remote_write(cdata, value, reg); + client_data->remote_write(client_data, value, reg); } } static struct vreg *vreg_mddi_1v5; static struct vreg *vreg_lcm_2v85; -static void trout_mddi_power_client(struct msm_mddi_client_data *cdata, +static void trout_mddi_power_client(struct msm_mddi_client_data *client_data, int on) { unsigned id, on_off; @@ -412,15 +412,17 @@ static void trout_mddi_power_client(struct msm_mddi_client_data *cdata, } } -static int trout_mddi_toshiba_client_init(struct msm_mddi_client_data *cdata) +static int trout_mddi_toshiba_client_init( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) { int panel_id; - cdata->auto_hibernate(cdata, 0); - trout_process_mddi_table(cdata, mddi_toshiba_init_table, + client_data->auto_hibernate(client_data, 0); + trout_process_mddi_table(client_data, mddi_toshiba_init_table, ARRAY_SIZE(mddi_toshiba_init_table)); - cdata->auto_hibernate(cdata, 1); - panel_id = (cdata->remote_read(cdata, GPIODATA) >> 4) & 3; + client_data->auto_hibernate(client_data, 1); + panel_id = (client_data->remote_read(client_data, GPIODATA) >> 4) & 3; if (panel_id > 1) { printk("unknown panel id at mddi_enable\n"); return -1; @@ -428,34 +430,35 @@ static int trout_mddi_toshiba_client_init(struct msm_mddi_client_data *cdata) return 0; } -static int trout_mddi_toshiba_client_uninit(struct msm_mddi_client_data *cdata) +static int trout_mddi_toshiba_client_uninit( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) { return 0; } -static int trout_mddi_panel_unblank(struct msm_panel_data *panel_data) +static int trout_mddi_panel_unblank( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) { - struct msm_mddi_panel_info *panel = container_of(panel_data, - struct msm_mddi_panel_info, panel_data); - struct msm_mddi_client_data *mddi_client = panel->client_data; int panel_id, ret = 0; trout_set_backlight_level(0); - mddi_client->auto_hibernate(mddi_client, 0); - trout_process_mddi_table(mddi_client, mddi_toshiba_panel_init_table, + client_data->auto_hibernate(client_data, 0); + trout_process_mddi_table(client_data, mddi_toshiba_panel_init_table, ARRAY_SIZE(mddi_toshiba_panel_init_table)); - panel_id = (mddi_client->remote_read(mddi_client, GPIODATA) >> 4) & 3; + panel_id = (client_data->remote_read(client_data, GPIODATA) >> 4) & 3; switch(panel_id) { case 0: printk("init sharp panel\n"); - trout_process_mddi_table(mddi_client, + trout_process_mddi_table(client_data, mddi_sharp_init_table, ARRAY_SIZE(mddi_sharp_init_table)); break; case 1: printk("init tpo panel\n"); - trout_process_mddi_table(mddi_client, + trout_process_mddi_table(client_data, mddi_tpo_init_table, ARRAY_SIZE(mddi_tpo_init_table)); break; @@ -467,35 +470,31 @@ static int trout_mddi_panel_unblank(struct msm_panel_data *panel_data) trout_set_backlight_level(trout_backlight_brightness); trout_backlight_off = 0; mutex_unlock(&trout_backlight_lock); - mddi_client->auto_hibernate(mddi_client, 1); - // reenable vsync - mddi_client->remote_write(mddi_client, GPIOSEL_VWAKEINT, - GPIOSEL); - mddi_client->remote_write(mddi_client, INTMASK_VWAKEOUT, - INTMASK); + client_data->auto_hibernate(client_data, 1); + client_data->remote_write(client_data, GPIOSEL_VWAKEINT, GPIOSEL); + client_data->remote_write(client_data, INTMASK_VWAKEOUT, INTMASK); return ret; } -static int trout_mddi_panel_blank(struct msm_panel_data *panel_data) +static int trout_mddi_panel_blank( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) { - struct msm_mddi_panel_info *panel = container_of(panel_data, - struct msm_mddi_panel_info, panel_data); - struct msm_mddi_client_data *mddi_client = panel->client_data; int panel_id, ret = 0; - panel_id = (mddi_client->remote_read(mddi_client, GPIODATA) >> 4) & 3; - mddi_client->auto_hibernate(mddi_client, 0); + panel_id = (client_data->remote_read(client_data, GPIODATA) >> 4) & 3; + client_data->auto_hibernate(client_data, 0); switch(panel_id) { case 0: printk("deinit sharp panel\n"); - trout_process_mddi_table(mddi_client, + trout_process_mddi_table(client_data, mddi_sharp_deinit_table, ARRAY_SIZE(mddi_sharp_deinit_table)); break; case 1: printk("deinit tpo panel\n"); - trout_process_mddi_table(mddi_client, + trout_process_mddi_table(client_data, mddi_tpo_deinit_table, ARRAY_SIZE(mddi_tpo_deinit_table)); break; @@ -503,14 +502,13 @@ static int trout_mddi_panel_blank(struct msm_panel_data *panel_data) printk("unknown panel_id: %d\n", panel_id); ret = -1; }; - mddi_client->auto_hibernate(mddi_client,1); + client_data->auto_hibernate(client_data, 1); mutex_lock(&trout_backlight_lock); trout_set_backlight_level(0); trout_backlight_off = 1; mutex_unlock(&trout_backlight_lock); - mddi_client->remote_write(mddi_client, 0, SYSCLKENA); - mddi_client->remote_write(mddi_client, 1, DPSUS); - + client_data->remote_write(client_data, 0, SYSCLKENA); + client_data->remote_write(client_data, 1, DPSUS); return ret; } @@ -558,7 +556,7 @@ static struct resource resources_msm_fb[] = { }, }; -struct msm_mddi_toshiba_client_data toshiba_client_data = { +struct msm_mddi_bridge_platform_data toshiba_client_data = { .init = trout_mddi_toshiba_client_init, .uninit = trout_mddi_toshiba_client_uninit, .blank = trout_mddi_panel_blank, @@ -581,8 +579,10 @@ static struct msm_mddi_platform_data mddi_pdata = { { .product_id = (0xd263 << 16 | 0), .name = "mddi_c_d263_0000", + //.name = "mddi_c_dummy", .id = 0, .client_data = &toshiba_client_data, + //.client_data = &toshiba_client_data.fb_data, .clk_rate = 0, }, }, From 5b01c880d53481c8031aee794ff1af872a35ee4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 9 Dec 2009 22:45:03 -0800 Subject: [PATCH 0370/2556] hack: mmc files from 2.6.29 Change-Id: I5f1791108fb0577b6d520e2d357b32aef9498510 --- arch/arm/include/asm/mach/mmc.h | 26 ++++++++++++++++++++++++++ arch/arm/mach-msm/board-trout-mmc.c | 9 +++++---- 2 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 arch/arm/include/asm/mach/mmc.h diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h new file mode 100644 index 0000000000000..8948dec26067a --- /dev/null +++ b/arch/arm/include/asm/mach/mmc.h @@ -0,0 +1,26 @@ +/* + * arch/arm/include/asm/mach/mmc.h + */ +#ifndef ASMARM_MACH_MMC_H +#define ASMARM_MACH_MMC_H + +#include +#include +#include + +struct embedded_sdio_data { + struct sdio_cis cis; + struct sdio_cccr cccr; + struct sdio_embedded_func *funcs; + int num_funcs; +}; + +struct mmc_platform_data { + unsigned int ocr_mask; /* available voltages */ + u32 (*translate_vdd)(struct device *, unsigned int); + unsigned int (*status)(struct device *); + struct embedded_sdio_data *embedded_sdio; + int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); +}; + +#endif diff --git a/arch/arm/mach-msm/board-trout-mmc.c b/arch/arm/mach-msm/board-trout-mmc.c index 4054c5ddcdbcf..0b3da5a25fb81 100644 --- a/arch/arm/mach-msm/board-trout-mmc.c +++ b/arch/arm/mach-msm/board-trout-mmc.c @@ -27,7 +27,8 @@ #define DEBUG_SDSLOT_VDD 1 -extern int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat); +extern int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, + unsigned int stat_irq, unsigned long stat_irq_flags); /* ---- COMMON ---- */ static void config_gpio_table(uint32_t *table, int len) @@ -165,7 +166,6 @@ static unsigned int trout_sdslot_status(struct device *dev) static struct mmc_platform_data trout_sdslot_data = { .ocr_mask = TROUT_MMC_VDD, - .status_irq = TROUT_GPIO_TO_INT(TROUT_GPIO_SDMC_CD_N), .status = trout_sdslot_status, .translate_vdd = trout_sdslot_switchvdd, }; @@ -320,10 +320,11 @@ int __init trout_init_mmc(unsigned int sys_rev) set_irq_wake(TROUT_GPIO_TO_INT(TROUT_GPIO_SDMC_CD_N), 1); - msm_add_sdcc(1, &trout_wifi_data); + msm_add_sdcc(1, &trout_wifi_data, 0, 0); if (!opt_disable_sdcard) - msm_add_sdcc(2, &trout_sdslot_data); + msm_add_sdcc(2, &trout_sdslot_data, + TROUT_GPIO_TO_INT(TROUT_GPIO_SDMC_CD_N), 0); else printk(KERN_INFO "trout: SD-Card interface disabled\n"); return 0; From 22bb3d48da36c762e23a084df0a758f7328f6523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 9 Dec 2009 22:46:11 -0800 Subject: [PATCH 0371/2556] board-sapphire-panel.c from 2.6.29 Change-Id: I28f707096722c744a2d6818b08eeb885e70a2452 --- arch/arm/mach-msm/board-sapphire-panel.c | 46 +++++++++++++++++++++--- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire-panel.c b/arch/arm/mach-msm/board-sapphire-panel.c index 2aec7638d6e12..b2030c0913376 100644 --- a/arch/arm/mach-msm/board-sapphire-panel.c +++ b/arch/arm/mach-msm/board-sapphire-panel.c @@ -42,6 +42,7 @@ enum sapphire_panel_type { NUM_OF_SAPPHIRE_PANELS, }; static int g_panel_id = -1 ; +static int g_panel_inited = 0 ; #define SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS 132 #define GOOGLE_DEFAULT_BACKLIGHT_BRIGHTNESS 102 @@ -385,7 +386,8 @@ static struct mddi_table mddi_tpo_deinit_table[] = { static void sapphire_process_mddi_table( struct msm_mddi_client_data *client_data, - struct mddi_table *table, size_t count) + const struct mddi_table *table, + size_t count) { int i; for (i = 0; i < count; i++) { @@ -429,8 +431,6 @@ static void sapphire_mddi_power_client(struct msm_mddi_client_data *client_data, msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); vreg_enable(vreg_lcm_2v85); msleep(3); - gpio_set_value(MDDI_RST_N, 1); - msleep(10); } else { gpio_set_value(SAPPHIRE_GPIO_MDDI_32K_EN, 0); gpio_set_value(MDDI_RST_N, 0); @@ -458,6 +458,12 @@ static int sapphire_mddi_toshiba_client_init( { int panel_id; + /* Set the MDDI_RST_N accroding to MDDI client repectively( + * been set in sapphire_mddi_power_client() originally) + */ + gpio_set_value(MDDI_RST_N, 1); + msleep(10); + client_data->auto_hibernate(client_data, 0); sapphire_process_mddi_table(client_data, mddi_toshiba_init_table, ARRAY_SIZE(mddi_toshiba_init_table)); @@ -1166,11 +1172,35 @@ static struct msm_mddi_bridge_platform_data toshiba_client_data = { }, }; +#define NT35399_MFR_NAME 0x0bda +#define NT35399_PRODUCT_CODE 0x8a47 + +static void nt35399_fixup(uint16_t * mfr_name, uint16_t * product_code) +{ + printk(KERN_DEBUG "%s: enter.\n", __func__); + *mfr_name = NT35399_MFR_NAME ; + *product_code= NT35399_PRODUCT_CODE ; +} + +static struct msm_mddi_bridge_platform_data nt35399_client_data = { + + .init = nt35399_client_init, + .uninit = nt35399_client_uninit, + .blank = nt35399_panel_blank, + .unblank = nt35399_panel_unblank, + .fb_data = { + .xres = 320, + .yres = 480, + .output_format = 0, + }, +}; + static struct msm_mddi_platform_data mddi_pdata = { .clk_rate = 122880000, .power_client = sapphire_mddi_power_client, + .fixup = nt35399_fixup, .fb_resource = resources_msm_fb, - .num_clients = 1, + .num_clients = 2, .client_platform_data = { { .product_id = (0xd263 << 16 | 0), @@ -1179,6 +1209,14 @@ static struct msm_mddi_platform_data mddi_pdata = { .client_data = &toshiba_client_data, .clk_rate = 0, }, + { + .product_id = + (NT35399_MFR_NAME << 16 | NT35399_PRODUCT_CODE), + .name = "mddi_c_0bda_8a47" , + .id = 0, + .client_data = &nt35399_client_data, + .clk_rate = 0, + }, }, }; From 9073d621622dcc057d87b3a12d43f77515f9daeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 9 Dec 2009 23:26:59 -0800 Subject: [PATCH 0372/2556] board-sapphire-mmc from 2.6.29 Change-Id: I74c9d09c243536856f88188d71bc5cd9b7917851 --- arch/arm/mach-msm/board-sapphire-mmc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire-mmc.c b/arch/arm/mach-msm/board-sapphire-mmc.c index fbc0fb1ffee29..a8a7963abe4b6 100755 --- a/arch/arm/mach-msm/board-sapphire-mmc.c +++ b/arch/arm/mach-msm/board-sapphire-mmc.c @@ -37,8 +37,8 @@ #define DEBUG_SDSLOT_VDD 0 -extern int msm_add_sdcc(unsigned int controller, - struct mmc_platform_data *plat); +extern int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, + unsigned int stat_irq, unsigned long stat_irq_flags); /* ---- COMMON ---- */ static void config_gpio_table(uint32_t *table, int len) @@ -176,7 +176,6 @@ static unsigned int sapphire_sdslot_status(struct device *dev) static struct mmc_platform_data sapphire_sdslot_data = { .ocr_mask = SAPPHIRE_MMC_VDD, - .status_irq = SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_SDMC_CD_N), .status = sapphire_sdslot_status, .translate_vdd = sapphire_sdslot_switchvdd, }; @@ -365,10 +364,11 @@ int __init sapphire_init_mmc(unsigned int sys_rev) set_irq_wake(SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_SDMC_CD_N), 1); - msm_add_sdcc(1, &sapphire_wifi_data); + msm_add_sdcc(1, &sapphire_wifi_data, 0, 0); if (!opt_disable_sdcard) - msm_add_sdcc(2, &sapphire_sdslot_data); + msm_add_sdcc(2, &sapphire_sdslot_data, + SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_SDMC_CD_N), 0); else printk(KERN_INFO "sapphire: SD-Card interface disabled\n"); return 0; From aa47b12364d3a96cf1c8ca89c1e4455647853937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 10 Dec 2009 22:04:27 -0800 Subject: [PATCH 0373/2556] serial debug fixes Change-Id: If15c0d0aedbf8ad443bb9c8d2dd47b109d593eb2 --- arch/arm/mach-msm/board-sapphire.c | 7 +++---- arch/arm/mach-msm/board-trout.c | 4 +--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index 2bf385cd72529..f011504babad3 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -58,6 +58,7 @@ #include #include +#include #include #include @@ -1101,9 +1102,6 @@ static void __init config_gpios(void) config_sapphire_camera_off_gpios(); } -void msm_serial_debug_init(unsigned int base, int irq, - struct device *clk_device, int signal_irq); - static struct msm_acpu_clock_platform_data sapphire_clock_data = { .acpu_switch_time_us = 20, .max_speed_delta_khz = 256000, @@ -1141,7 +1139,8 @@ static void __init sapphire_init(void) #if defined(CONFIG_MSM_SERIAL_DEBUGGER) if (!opt_disable_uart3) msm_serial_debug_init(MSM_UART3_PHYS, INT_UART3, - &msm_device_uart3.dev, 1); + &msm_device_uart3.dev, 1, + MSM_GPIO_TO_INT(86)); #endif /* gpio_configure(108, IRQF_TRIGGER_LOW); */ diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index ca6eace5a1584..cc31607a2896f 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -59,6 +59,7 @@ #include #include +#include #include #include #ifdef CONFIG_HTC_HEADSET @@ -752,9 +753,6 @@ static void __init config_gpios(void) config_camera_off_gpios(); } -void msm_serial_debug_init(unsigned int base, int irq, - struct device *clk_device, int signal_irq); - static struct msm_acpu_clock_platform_data trout_clock_data = { .acpu_switch_time_us = 20, .max_speed_delta_khz = 256000, From 34f3a69bc5ffd9662a2f413aea537ff1c043749b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 18 Dec 2009 16:30:07 -0800 Subject: [PATCH 0374/2556] board-sapphire: Set mux to debug uart Change-Id: I934b9672958dab6805b54fba901fd59b34f12f72 --- arch/arm/mach-msm/board-sapphire.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index f011504babad3..71873003b7c81 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -541,7 +541,7 @@ static void h2w_config_cpld(int route) static void h2w_init_cpld(void) { - h2w_config_cpld(H2W_GPIO); + h2w_config_cpld(H2W_UART3); } static void set_h2w_dat(int n) From 2cbdcb50493d1514157275afa8b0a02f919e23d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 6 Jan 2010 17:51:38 -0800 Subject: [PATCH 0375/2556] [ARM] msm: sapphire: Don't use gpio_configure. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I35958f865e5fad6b56e5a00648d5ea78ffc89f68 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/board-sapphire.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index 71873003b7c81..d25fe9ef50f2f 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -544,13 +544,17 @@ static void h2w_init_cpld(void) h2w_config_cpld(H2W_UART3); } +static int h2w_dat_value; static void set_h2w_dat(int n) { + h2w_dat_value = n; gpio_set_value(SAPPHIRE_GPIO_H2W_DATA, n); } +static int h2w_clk_value; static void set_h2w_clk(int n) { + h2w_clk_value = n; gpio_set_value(SAPPHIRE_GPIO_H2W_CLK, n); } @@ -559,7 +563,7 @@ static void set_h2w_dat_dir(int n) if (n == 0) /* input */ gpio_direction_input(SAPPHIRE_GPIO_H2W_DATA); else - gpio_configure(SAPPHIRE_GPIO_H2W_DATA, GPIOF_DRIVE_OUTPUT); + gpio_direction_output(SAPPHIRE_GPIO_H2W_DATA, h2w_dat_value); gpio_set_value(SAPPHIRE_GPIO_H2W_DAT_DIR, n); @@ -570,7 +574,7 @@ static void set_h2w_clk_dir(int n) if (n == 0) /* input */ gpio_direction_input(SAPPHIRE_GPIO_H2W_CLK); else - gpio_configure(SAPPHIRE_GPIO_H2W_CLK, GPIOF_DRIVE_OUTPUT); + gpio_direction_output(SAPPHIRE_GPIO_H2W_CLK, h2w_clk_value); gpio_set_value(SAPPHIRE_GPIO_H2W_CLK_DIR, n); } From b2f13d78c7fd33c458fc95d1cf064484343881dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 10 Dec 2009 00:03:36 -0800 Subject: [PATCH 0376/2556] [ARM] msm: trout: Pass wakeup irq to serial debugger Change-Id: I695226bbd595d35ed6f9e29f09d13741c345de76 --- arch/arm/mach-msm/board-trout.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index cc31607a2896f..1ce43e1c25ca2 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -790,7 +790,8 @@ static void __init trout_init(void) #if defined(CONFIG_MSM_SERIAL_DEBUGGER) if (!opt_disable_uart3) msm_serial_debug_init(MSM_UART3_PHYS, INT_UART3, - &msm_device_uart3.dev, 1); + &msm_device_uart3.dev, 1, + MSM_GPIO_TO_INT(86)); #endif /* gpio_configure(108, IRQF_TRIGGER_LOW); */ From d082386fa1ab6820d56d84aee6021ebc6d6f5501 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Mon, 26 Jan 2009 16:56:46 -0800 Subject: [PATCH 0377/2556] [ARM] msm: start providing per-SoC config This adds ARCH_MSM7X00A and ARCH_QSD8X50 under the overall ARCH_MSM umbrella. These allow for per-SoC configuration (currently selecting either ARCH_MSM_ARM11 or ARCH_MSM_SCORPION) to indicate which core and core peripherals are present in the SoC. Signed-off-by: Brian Swetland --- arch/arm/mach-msm/Kconfig | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index d4f7b04a9c57b..b58f74539ff61 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -113,6 +113,13 @@ config MACH_MSM7X30_SURF help Support for the Qualcomm MSM7x30 SURF eval board. +config MACH_SWORDFISH + depends on ARCH_QSD8X50 + default y + bool "Swordfish Board (QCT SURF8250)" + help + Support for the Qualcomm SURF8250 eval board. + config MACH_QSD8X50_SURF depends on ARCH_QSD8X50 bool "QSD8x50 SURF" @@ -189,7 +196,7 @@ config MSM_PROC_COMM bool config MACH_SAPPHIRE - depends on ARCH_MSM + depends on ARCH_MSM7X00A default y bool "Sapphire" From 85fb25ea9eaf769ee7738198f523cc137cbb90fb Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Wed, 18 Mar 2009 12:45:38 -0700 Subject: [PATCH 0378/2556] [ARM] msm: qsd8k memory base is at 0x20000000 The memory layout is effective as of AMSS 3170+. Signed-off-by: Haley Teng Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Makefile.boot | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot index 24dfbf8c07c47..326672707ce7c 100644 --- a/arch/arm/mach-msm/Makefile.boot +++ b/arch/arm/mach-msm/Makefile.boot @@ -1,3 +1,8 @@ zreladdr-y := 0x10008000 params_phys-y := 0x10000100 initrd_phys-y := 0x10800000 + +# for now, override for QSD8x50 + zreladdr-$(CONFIG_ARCH_QSD8X50) := 0x20008000 +params_phys-$(CONFIG_ARCH_QSD8X50) := 0x20000100 +initrd_phys-$(CONFIG_ARCH_QSD8X50) := 0x21000000 From e92eebba3571b458a38a313c5868471998a3616e Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Mon, 26 Jan 2009 20:02:47 -0800 Subject: [PATCH 0379/2556] [ARM] msm: support the scorpion version of the VIC (QSD8x50) Signed-off-by: Brian Swetland --- arch/arm/mach-msm/irq.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c index 4ed6b20cf7948..d63a5e25b4c5d 100644 --- a/arch/arm/mach-msm/irq.c +++ b/arch/arm/mach-msm/irq.c @@ -58,9 +58,16 @@ module_param_named(debug_mask, msm_irq_debug_mask, int, S_IRUGO | S_IWUSR | S_IW #define VIC_INT_POLARITY0 VIC_REG(0x0050) /* 1: NEG, 0: POS */ #define VIC_INT_POLARITY1 VIC_REG(0x0054) /* 1: NEG, 0: POS */ #define VIC_NO_PEND_VAL VIC_REG(0x0060) + +#if defined(CONFIG_ARCH_MSM_SCORPION) +#define VIC_NO_PEND_VAL_FIQ VIC_REG(0x0064) +#define VIC_INT_MASTEREN VIC_REG(0x0068) /* 1: IRQ, 2: FIQ */ +#define VIC_CONFIG VIC_REG(0x006C) /* 1: USE SC VIC */ +#else #define VIC_INT_MASTEREN VIC_REG(0x0064) /* 1: IRQ, 2: FIQ */ -#define VIC_PROTECTION VIC_REG(0x006C) /* 1: ENABLE */ #define VIC_CONFIG VIC_REG(0x0068) /* 1: USE ARM1136 VIC */ +#define VIC_PROTECTION VIC_REG(0x006C) /* 1: ENABLE */ +#endif #define VIC_IRQ_STATUS0 VIC_REG(0x0080) #define VIC_IRQ_STATUS1 VIC_REG(0x0084) #define VIC_FIQ_STATUS0 VIC_REG(0x0090) @@ -74,9 +81,22 @@ module_param_named(debug_mask, msm_irq_debug_mask, int, S_IRUGO | S_IWUSR | S_IW #define VIC_IRQ_VEC_RD VIC_REG(0x00D0) /* pending int # */ #define VIC_IRQ_VEC_PEND_RD VIC_REG(0x00D4) /* pending vector addr */ #define VIC_IRQ_VEC_WR VIC_REG(0x00D8) + +#if defined(CONFIG_ARCH_MSM_SCORPION) +#define VIC_FIQ_VEC_RD VIC_REG(0x00DC) +#define VIC_FIQ_VEC_PEND_RD VIC_REG(0x00E0) +#define VIC_FIQ_VEC_WR VIC_REG(0x00E4) +#define VIC_IRQ_IN_SERVICE VIC_REG(0x00E8) +#define VIC_IRQ_IN_STACK VIC_REG(0x00EC) +#define VIC_FIQ_IN_SERVICE VIC_REG(0x00F0) +#define VIC_FIQ_IN_STACK VIC_REG(0x00F4) +#define VIC_TEST_BUS_SEL VIC_REG(0x00F8) +#define VIC_IRQ_CTRL_CONFIG VIC_REG(0x00FC) +#else #define VIC_IRQ_IN_SERVICE VIC_REG(0x00E0) #define VIC_IRQ_IN_STACK VIC_REG(0x00E4) #define VIC_TEST_BUS_SEL VIC_REG(0x00E8) +#endif #define VIC_VECTPRIORITY(n) VIC_REG(0x0200+((n) * 4)) #define VIC_VECTADDR(n) VIC_REG(0x0400+((n) * 4)) @@ -91,7 +111,7 @@ static struct { static uint32_t msm_irq_idle_disable[2]; #define SMSM_FAKE_IRQ (0xff) -static uint8_t msm_irq_to_smsm[NR_MSM_IRQS] = { +static uint8_t msm_irq_to_smsm[NR_MSM_IRQS + NR_SIRC_IRQS] = { [INT_MDDI_EXT] = 1, [INT_MDDI_PRI] = 2, [INT_MDDI_CLIENT] = 3, From 00691818dfcd569308feed31b9f6abf1776c4f65 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Mon, 8 Feb 2010 20:31:33 -0800 Subject: [PATCH 0380/2556] [ARM] msm: acpuclock control for scorpion core (with dedicated pll) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Brian Swetland [ARM] msm: Add cpufreq support for scorpion. Signed-off-by: Dima Zavin msm: acpuclock-scorpion: clean up scorpion acpu clock management - New init sequence is simpler and more tolerant of different clock configurations coming out of the bootloader - New clock switching logic is simplified - Leftovers from the msm7k/arm11 implementation removed - LPJ adjustment now applied on clock switch - make checkpatch happy - mutex -> spinlock Signed-off-by: Brian Swetland [ARM] msm: scorpion: wait for 100uS after changing pll freq According to low/high temperarute tests, the old value of 20 was not enough. Signed-off-by: Dima Zavin [ARM] msm: Take 256MHz out of the scorpion cpufreq table. Signed-off-by: Dima Zavin [ARM] msm: acpuclock-scorpion: various [ARM] msm: acpuclock-scorpion: add cpu voltage scaling when frequency changes Change-Id: I52822195fc086f527ed91295aa9b8f6b0771699b Signed-off-by: Haley Teng Signed-off-by: Arve Hjønnevåg [ARM] msm: acpuclock-scorpion: Use regulator api to set voltage Looks for a supply named "acpu_vcore". Signed-off-by: Arve Hjønnevåg [ARM] msm: acpuclock: Initalize cpufreq_table for all cpu speeds on scorpion Signed-off-by: Mike Chan [ARM] msm: acpuclock-scorpion: cleanup compiler warning Change-Id: I740e754fc92fef2351185eb0182708634fcaf90e Signed-off-by: Dima Zavin qsd8k: acpuclk: correctly set loops_per_jiffy at boot lpj was being initialized to 0 always... not at all good. Signed-off-by: Brian Swetland qsd8k: acpuclk: make sure we set lpj at the right time When switching to SCPLL, we must set the new LPJ before we select the new clock speed, otherwise our udelay() to let the scpll stabilize after selecting the new frequency may be too short when moving to a higher frequency. Signed-off-by: Brian Swetland [ARM] msm: scorpion: Do not force reconfiguring of the scpll at boot If we are already running at the target boot frequency (768Mhz), do not reconfigure the SCPLL. Should prevent us from loading the AXI bus during boot. Change-Id: I5939f65c89b4a28e36a673ebb225c469905be9b9 Signed-off-by: Dima Zavin [ARM] msm: scorpion: Use the TCXO instead of AXI as a standby clock When switching SCPLL speeds, we should not use the AXI clock for the intermediate step. Use the TCXO instead. Change-Id: Ic5809396a1cb8e371b4544e2c5aa138af2d51ab1 Signed-off-by: Dima Zavin [ARM] msm: scorpion: Ensure the MPLL is up before doing acpu clock init We want to make sure that the MPLL is up before we continue so that we can use it for the temporary step while frequency scaling. Change-Id: I2f5da0138b744e1f8f0b2bd5ac4f26c62eb80751 Signed-off-by: Dima Zavin [ARM] msm: scorpion: Use MPLL for intermediate steps while freq scaling Change-Id: I19e0a411472225330e2dc68c5ef6ebbe4d8d4561 Signed-off-by: Dima Zavin [ARM] msm: scorpion: Wait for 200uS more after enabling the SCPLL We should probably trim this down once we get confirmation from Qualcomm about the total necessary delay. Change-Id: Iba33027dc7685c848f2ab8720dc19d81df83cb8c Signed-off-by: Dima Zavin [ARM] msm: acpuclock: Do not ramp up when going to power collapse/wfi Apply these same changes to scorpion. Power collapse and wfi speeds specified in the board files are the fastest speeds one can go before entering the mode. If the acpu is running at a slower rate do not clock up, this can cause PLL depencency issues. Signed-off-by: Mike Chan [ARM] msm: acpuclock-scorpion: add get_wfi_rate() If the board file specifies 0 as the WFI speed do not busy spin or ramp the cpu down. WFI speed ramp and busy spin are only required for 8k and not 7k. Signed-off-by: Mike Chan [ARM] msm: acpuclock-scorpion: various [ARM] msm: scorpion: use correct units when setting acpu rate (khz -> hz) Change-Id: I71a47abf0fe7ac357372491b5d67b65304cccc49 Signed-off-by: Dima Zavin [ARM] msm: acpuclock: Adjust vol to 1.275 for 998mhz per spec Signed-off-by: Mike Chan [ARM] msm: scorpion: adjust the voltages for acpu frequency table Provides better margin for process variance. Change-Id: I0203797d4314618c20400e6f56c4731c6ce7e952 Signed-off-by: Haley Teng Acked-by: Dima Zavin msm: acpuclk: Limit cpu speeds available for cpufreq. Signed-off-by: Mike Chan qsd8k: scorpion: rework acpuclk code for more correctness - observe delay between standby and normal modes - always do a shot switch after exiting powerdown - always do a hop switch after the shot switch - remove obsolete/incorrect init code Signed-off-by: Brian Swetland qsd8k: acpuclk scorpion: reduce delay after scpll enable Stress testing has shown this code stable to delays as low as 10uS. Let's move from 100uS to 25uS and avoid excessive delays while still keeping plenty of margin. Signed-off-by: Brian Swetland [ARM] msm: scorpion: raise the voltage across the frequency range, 998 @ 1.3V This increases the margin across all the frequencies to be 50mV above QCT nominal up to 1.275V. Note that some values already account for some of this due to changes from HTC, so not al values in the table have been changed. Make 998Mhz run at 1.3V. These changes may be necessary to eliminate L2 cache parity errors. Change-Id: I517239bbb8ba39ab7f08a52e57f21f64e200d558 Signed-off-by: Dima Zavin [ARM] msm: scorpion: make 921Mhz and 960Mhz also run at 1.3V Change-Id: Ibdb29ad65b55a4e913537fbe6e8552e7da323dd3 Signed-off-by: Dima Zavin [ARM] msm: scorpion: properly check for requested rate == 0 The for loop check would have incorrectly chosen 0 as a valid frequency. Change-Id: I47ea2868ddcdf6d7a70db31248f32240307abcca Signed-off-by: Dima Zavin [ARM] msm: scorpion: do not use 1.3V for 900Mhz+ (back to 1.275) Qualcomm is not currently able to certify that 1.3V is safe for production due to timing concerns. Until that happens, we cannot use it for ship. Change-Id: Ibf03a5f6f6170f01ae82dd02ff442af806650f1e Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Makefile | 4 +- arch/arm/mach-msm/acpuclock-scorpion.c | 489 +++++++++++++++++++++++++ 2 files changed, 492 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-msm/acpuclock-scorpion.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index fd4b25eb735e2..644aa4443d384 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -1,12 +1,14 @@ obj-y += io.o idle.o timer.o obj-y += memory.o ifndef CONFIG_ARCH_MSM8X60 -obj-y += acpuclock-arm11.o obj-y += dma.o endif obj-y += nand_partitions.o +obj-$(CONFIG_ARCH_MSM_ARM11) += acpuclock-arm11.o +obj-$(CONFIG_ARCH_MSM_SCORPION) += acpuclock-scorpion.o + ifdef CONFIG_MSM_VIC obj-y += irq-vic.o else diff --git a/arch/arm/mach-msm/acpuclock-scorpion.c b/arch/arm/mach-msm/acpuclock-scorpion.c new file mode 100644 index 0000000000000..7177547c2a6f4 --- /dev/null +++ b/arch/arm/mach-msm/acpuclock-scorpion.c @@ -0,0 +1,489 @@ +/* + * Copyright (c) 2009 Google, Inc. + * Copyright (c) 2008 QUALCOMM Incorporated. + * + * 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 "acpuclock.h" +#include "proc_comm.h" + +#if 0 +#define DEBUG(x...) pr_info(x) +#else +#define DEBUG(x...) do {} while (0) +#endif + +#define SHOT_SWITCH 4 +#define HOP_SWITCH 5 +#define SIMPLE_SLEW 6 +#define COMPLEX_SLEW 7 + +#define SPSS_CLK_CNTL_ADDR (MSM_CSR_BASE + 0x100) +#define SPSS_CLK_SEL_ADDR (MSM_CSR_BASE + 0x104) + +/* Scorpion PLL registers */ +#define SCPLL_CTL_ADDR (MSM_SCPLL_BASE + 0x4) +#define SCPLL_STATUS_ADDR (MSM_SCPLL_BASE + 0x18) +#define SCPLL_FSM_CTL_EXT_ADDR (MSM_SCPLL_BASE + 0x10) + +struct clkctl_acpu_speed { + unsigned acpu_khz; + unsigned clk_cfg; + unsigned clk_sel; + unsigned sc_l_value; + unsigned lpj; + int vdd; +}; + +/* clock sources */ +#define CLK_TCXO 0 /* 19.2 MHz */ +#define CLK_GLOBAL_PLL 1 /* 768 MHz */ +#define CLK_MODEM_PLL 4 /* 245 MHz (UMTS) or 235.93 MHz (CDMA) */ + +#define CCTL(src, div) (((src) << 4) | (div - 1)) + +/* core sources */ +#define SRC_RAW 0 /* clock from SPSS_CLK_CNTL */ +#define SRC_SCPLL 1 /* output of scpll 128-998 MHZ */ +#define SRC_AXI 2 /* 128 MHz */ +#define SRC_PLL1 3 /* 768 MHz */ + +struct clkctl_acpu_speed acpu_freq_tbl[] = { + { 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 1050 }, + { 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 1050 }, + { 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 1050 }, + { 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 1050 }, + { 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 1050 }, + { 422400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0B, 0, 1050 }, + { 460800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0C, 0, 1050 }, + { 499200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0D, 0, 1075 }, + { 537600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0E, 0, 1100 }, + { 576000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0F, 0, 1100 }, + { 614400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x10, 0, 1125 }, + { 652800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x11, 0, 1150 }, + { 691200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x12, 0, 1175 }, + { 729600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x13, 0, 1200 }, + { 768000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x14, 0, 1200 }, + { 806400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x15, 0, 1225 }, + { 844800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x16, 0, 1250 }, + { 883200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x17, 0, 1275 }, + { 921600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x18, 0, 1275 }, + { 960000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x19, 0, 1275 }, + { 998400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1A, 0, 1275 }, + { 0 }, +}; + +/* select the standby clock that is used when switching scpll + * frequencies + * + * Currently: MPLL + */ +struct clkctl_acpu_speed *acpu_stby = &acpu_freq_tbl[2]; +#define IS_ACPU_STANDBY(x) (((x)->clk_cfg == acpu_stby->clk_cfg) && \ + ((x)->clk_sel == acpu_stby->clk_sel)) + +#ifdef CONFIG_CPU_FREQ_TABLE +static struct cpufreq_frequency_table freq_table[ARRAY_SIZE(acpu_freq_tbl)]; + +static void __init acpuclk_init_cpufreq_table(void) +{ + int i; + int vdd; + for (i = 0; acpu_freq_tbl[i].acpu_khz; i++) { + freq_table[i].index = i; + freq_table[i].frequency = CPUFREQ_ENTRY_INVALID; + + /* Skip speeds using the global pll */ + if (acpu_freq_tbl[i].acpu_khz == 256000 || + acpu_freq_tbl[i].acpu_khz == 19200) + continue; + + vdd = acpu_freq_tbl[i].vdd; + /* Allow mpll and the first scpll speeds */ + if (acpu_freq_tbl[i].acpu_khz == 245000 || + acpu_freq_tbl[i].acpu_khz == 384000) { + freq_table[i].frequency = acpu_freq_tbl[i].acpu_khz; + continue; + } + + /* Take the fastest speed available at the specified VDD level */ + if (vdd != acpu_freq_tbl[i + 1].vdd) + freq_table[i].frequency = acpu_freq_tbl[i].acpu_khz; + } + + freq_table[i].index = i; + freq_table[i].frequency = CPUFREQ_TABLE_END; + + cpufreq_frequency_table_get_attr(freq_table, smp_processor_id()); +} +#else +#define acpuclk_init_cpufreq_table() do {} while (0); +#endif + +struct clock_state { + struct clkctl_acpu_speed *current_speed; + struct mutex lock; + uint32_t acpu_switch_time_us; + uint32_t max_speed_delta_khz; + uint32_t vdd_switch_time_us; + unsigned long power_collapse_khz; + unsigned long wait_for_irq_khz; + struct regulator *regulator; +}; + +static struct clock_state drv_state = { 0 }; + +static DEFINE_SPINLOCK(acpu_lock); + +#define PLLMODE_POWERDOWN 0 +#define PLLMODE_BYPASS 1 +#define PLLMODE_STANDBY 2 +#define PLLMODE_FULL_CAL 4 +#define PLLMODE_HALF_CAL 5 +#define PLLMODE_STEP_CAL 6 +#define PLLMODE_NORMAL 7 +#define PLLMODE_MASK 7 + +static void scpll_power_down(void) +{ + uint32_t val; + + /* Wait for any frequency switches to finish. */ + while (readl(SCPLL_STATUS_ADDR) & 0x1) + ; + + /* put the pll in standby mode */ + val = readl(SCPLL_CTL_ADDR); + val = (val & (~PLLMODE_MASK)) | PLLMODE_STANDBY; + writel(val, SCPLL_CTL_ADDR); + dmb(); + + /* wait to stabilize in standby mode */ + udelay(10); + + val = (val & (~PLLMODE_MASK)) | PLLMODE_POWERDOWN; + writel(val, SCPLL_CTL_ADDR); + dmb(); +} + +static void scpll_set_freq(uint32_t lval) +{ + uint32_t val, ctl; + + if (lval > 33) + lval = 33; + if (lval < 10) + lval = 10; + + /* wait for any calibrations or frequency switches to finish */ + while (readl(SCPLL_STATUS_ADDR) & 0x3) + ; + + ctl = readl(SCPLL_CTL_ADDR); + + if ((ctl & PLLMODE_MASK) != PLLMODE_NORMAL) { + /* put the pll in standby mode */ + writel((ctl & (~PLLMODE_MASK)) | PLLMODE_STANDBY, SCPLL_CTL_ADDR); + dmb(); + + /* wait to stabilize in standby mode */ + udelay(10); + + /* switch to 384 MHz */ + val = readl(SCPLL_FSM_CTL_EXT_ADDR); + val = (val & (~0x1FF)) | (0x0A << 3) | SHOT_SWITCH; + writel(val, SCPLL_FSM_CTL_EXT_ADDR); + dmb(); + + ctl = readl(SCPLL_CTL_ADDR); + writel(ctl | PLLMODE_NORMAL, SCPLL_CTL_ADDR); + dmb(); + + /* wait for frequency switch to finish */ + while (readl(SCPLL_STATUS_ADDR) & 0x1) + ; + + /* completion bit is not reliable for SHOT switch */ + udelay(25); + } + + /* write the new L val and switch mode */ + val = readl(SCPLL_FSM_CTL_EXT_ADDR); + val = (val & (~0x1FF)) | (lval << 3) | HOP_SWITCH; + writel(val, SCPLL_FSM_CTL_EXT_ADDR); + dmb(); + + ctl = readl(SCPLL_CTL_ADDR); + writel(ctl | PLLMODE_NORMAL, SCPLL_CTL_ADDR); + dmb(); + + /* wait for frequency switch to finish */ + while (readl(SCPLL_STATUS_ADDR) & 0x1) + ; +} + +/* this is still a bit weird... */ +static void select_clock(unsigned src, unsigned config) +{ + uint32_t val; + + if (src == SRC_RAW) { + uint32_t sel = readl(SPSS_CLK_SEL_ADDR); + unsigned shift = (sel & 1) ? 8 : 0; + + /* set other clock source to the new configuration */ + val = readl(SPSS_CLK_CNTL_ADDR); + val = (val & (~(0x7F << shift))) | (config << shift); + writel(val, SPSS_CLK_CNTL_ADDR); + + /* switch to other clock source */ + writel(sel ^ 1, SPSS_CLK_SEL_ADDR); + + dmb(); /* necessary? */ + } + + /* switch to new source */ + val = readl(SPSS_CLK_SEL_ADDR) & (~6); + writel(val | ((src & 3) << 1), SPSS_CLK_SEL_ADDR); +} + +static int acpuclk_set_vdd_level(int vdd) +{ + if (!drv_state.regulator || IS_ERR(drv_state.regulator)) { + drv_state.regulator = regulator_get(NULL, "acpu_vcore"); + if (IS_ERR(drv_state.regulator)) { + pr_info("acpuclk_set_vdd_level %d no regulator\n", vdd); + /* Assume that the PMIC supports scaling the processor + * to its maximum frequency at its default voltage. + */ + return 0; + } + pr_info("acpuclk_set_vdd_level got regulator\n"); + } + vdd *= 1000; /* mV -> uV */ + return regulator_set_voltage(drv_state.regulator, vdd, vdd); +} + +int acpuclk_set_rate(unsigned long rate, int for_power_collapse) +{ + struct clkctl_acpu_speed *cur, *next; + unsigned long flags; + + cur = drv_state.current_speed; + + /* convert to KHz */ + rate /= 1000; + + DEBUG("acpuclk_set_rate(%d,%d)\n", (int) rate, for_power_collapse); + + if (rate == 0 || rate == cur->acpu_khz) + return 0; + + next = acpu_freq_tbl; + for (;;) { + if (next->acpu_khz == rate) + break; + if (next->acpu_khz == 0) + return -EINVAL; + next++; + } + + if (!for_power_collapse) { + mutex_lock(&drv_state.lock); + /* Increase VDD if needed. */ + if (next->vdd > cur->vdd) { + if (acpuclk_set_vdd_level(next->vdd)) { + pr_err("acpuclock: Unable to increase ACPU VDD.\n"); + mutex_unlock(&drv_state.lock); + return -EINVAL; + } + } + } + + spin_lock_irqsave(&acpu_lock, flags); + + DEBUG("sel=%d cfg=%02x lv=%02x -> sel=%d, cfg=%02x lv=%02x\n", + cur->clk_sel, cur->clk_cfg, cur->sc_l_value, + next->clk_sel, next->clk_cfg, next->sc_l_value); + + if (next->clk_sel == SRC_SCPLL) { + if (!IS_ACPU_STANDBY(cur)) + select_clock(acpu_stby->clk_sel, acpu_stby->clk_cfg); + loops_per_jiffy = next->lpj; + scpll_set_freq(next->sc_l_value); + select_clock(SRC_SCPLL, 0); + } else { + loops_per_jiffy = next->lpj; + if (cur->clk_sel == SRC_SCPLL) { + select_clock(acpu_stby->clk_sel, acpu_stby->clk_cfg); + select_clock(next->clk_sel, next->clk_cfg); + scpll_power_down(); + } else { + select_clock(next->clk_sel, next->clk_cfg); + } + } + + drv_state.current_speed = next; + + spin_unlock_irqrestore(&acpu_lock, flags); + if (!for_power_collapse) { + /* Drop VDD level if we can. */ + if (next->vdd < cur->vdd) { + if (acpuclk_set_vdd_level(next->vdd)) + pr_err("acpuclock: Unable to drop ACPU VDD.\n"); + } + mutex_unlock(&drv_state.lock); + } + + return 0; +} + +static unsigned __init acpuclk_find_speed(void) +{ + uint32_t sel, val; + + sel = readl(SPSS_CLK_SEL_ADDR); + switch ((sel & 6) >> 1) { + case 1: + val = readl(SCPLL_FSM_CTL_EXT_ADDR); + val = (val >> 3) & 0x3f; + return val * 38400; + case 2: + return 128000; + default: + pr_err("acpu_find_speed: failed\n"); + BUG(); + return 0; + } +} + +#define PCOM_MODEM_PLL 0 +static int pll_request(unsigned id, unsigned on) +{ + on = !!on; + return msm_proc_comm(PCOM_CLKCTL_RPC_PLL_REQUEST, &id, &on); +} + +static void __init acpuclk_init(void) +{ + struct clkctl_acpu_speed *speed; + unsigned init_khz; + + init_khz = acpuclk_find_speed(); + + /* request the modem pll, and then drop it. We don't want to keep a + * ref to it, but we do want to make sure that it is initialized at + * this point. The ARM9 will ensure that the MPLL is always on + * once it is fully booted, but it may not be up by the time we get + * to here. So, our pll_request for it will block until the mpll is + * actually up. We want it up because we will want to use it as a + * temporary step during frequency scaling. */ + pll_request(PCOM_MODEM_PLL, 1); + pll_request(PCOM_MODEM_PLL, 0); + + if (!(readl(MSM_CLK_CTL_BASE + 0x300) & 1)) { + pr_err("%s: MPLL IS NOT ON!!! RUN AWAY!!\n", __func__); + BUG(); + } + + /* Move to 768MHz for boot, which is a safe frequency + * for all versions of Scorpion at the moment. + */ + speed = acpu_freq_tbl; + for (;;) { + if (speed->acpu_khz == 768000) + break; + if (speed->acpu_khz == 0) { + pr_err("acpuclk_init: cannot find 768MHz\n"); + BUG(); + } + speed++; + } + + if (init_khz != speed->acpu_khz) { + /* Bootloader needs to have SCPLL operating, but we're + * going to step over to the standby clock and make sure + * we select the right frequency on SCPLL and then + * step back to it, to make sure we're sane here. + */ + select_clock(acpu_stby->clk_sel, acpu_stby->clk_cfg); + scpll_power_down(); + scpll_set_freq(speed->sc_l_value); + select_clock(SRC_SCPLL, 0); + } + drv_state.current_speed = speed; + + for (speed = acpu_freq_tbl; speed->acpu_khz; speed++) + speed->lpj = cpufreq_scale(loops_per_jiffy, + init_khz, speed->acpu_khz); + + loops_per_jiffy = drv_state.current_speed->lpj; +} + +unsigned long acpuclk_get_rate(void) +{ + return drv_state.current_speed->acpu_khz; +} + +uint32_t acpuclk_get_switch_time(void) +{ + return drv_state.acpu_switch_time_us; +} + +unsigned long acpuclk_power_collapse(void) +{ + int ret = acpuclk_get_rate(); + if (ret > drv_state.power_collapse_khz) + acpuclk_set_rate(drv_state.power_collapse_khz * 1000, 1); + return ret * 1000; +} + +unsigned long acpuclk_get_wfi_rate(void) +{ + return drv_state.wait_for_irq_khz * 1000; +} + +unsigned long acpuclk_wait_for_irq(void) +{ + int ret = acpuclk_get_rate(); + if (ret > drv_state.wait_for_irq_khz) + acpuclk_set_rate(drv_state.wait_for_irq_khz * 1000, 1); + return ret * 1000; +} + +void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *clkdata) +{ + spin_lock_init(&acpu_lock); + mutex_init(&drv_state.lock); + + drv_state.acpu_switch_time_us = clkdata->acpu_switch_time_us; + drv_state.max_speed_delta_khz = clkdata->max_speed_delta_khz; + drv_state.vdd_switch_time_us = clkdata->vdd_switch_time_us; + drv_state.power_collapse_khz = clkdata->power_collapse_khz; + drv_state.wait_for_irq_khz = clkdata->wait_for_irq_khz; + + acpuclk_init(); + acpuclk_init_cpufreq_table(); +} From 7e825e40fe5a1aff0f314d0ca081b8b3f4af1857 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Thu, 29 Jan 2009 00:35:32 -0800 Subject: [PATCH 0381/2556] [ARM] msm: secondary interrupt controller for scorpion Change-Id: I5f543c834fdf79b8b8364022e9f81624ecd40d3e Signed-off-by: Brian Swetland --- arch/arm/mach-msm/irq.c | 7 +++++++ arch/arm/mach-msm/sirc.c | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c index d63a5e25b4c5d..0f3c33f52a180 100644 --- a/arch/arm/mach-msm/irq.c +++ b/arch/arm/mach-msm/irq.c @@ -468,6 +468,13 @@ void __init msm_init_irq(void) set_irq_handler(n, handle_level_irq); set_irq_flags(n, IRQF_VALID); } + +#if defined(CONFIG_ARCH_QSD8X50) + { + void msm_init_sirc(void); + msm_init_sirc(); + } +#endif } #if defined(CONFIG_MSM_FIQ_SUPPORT) diff --git a/arch/arm/mach-msm/sirc.c b/arch/arm/mach-msm/sirc.c index 11b54c7aeb09a..126a8f045adb7 100644 --- a/arch/arm/mach-msm/sirc.c +++ b/arch/arm/mach-msm/sirc.c @@ -1,25 +1,24 @@ -/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. +/* linux/arch/arm/mach-msm/irq.c * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. + * Copyright (c) 2009 QUALCOMM Incorporated. + * Copyright (C) 2009 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. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * */ #include #include #include #include +#include static unsigned int int_enable; static unsigned int wake_enable; @@ -40,6 +39,9 @@ static struct sirc_cascade_regs sirc_reg_table[] = { } }; +static unsigned int save_type; +static unsigned int save_polarity; + /* Mask off the given interrupt. Keep the int_enable mask in sync with the enable reg, so it can be restored after power collapse. */ static void sirc_irq_mask(struct irq_data *d) @@ -141,6 +143,22 @@ static void sirc_irq_handler(unsigned int irq, struct irq_desc *desc) desc->irq_data.chip->irq_ack(&desc->irq_data); } +void msm_sirc_enter_sleep(void) +{ + save_type = readl(sirc_regs.int_type); + save_polarity = readl(sirc_regs.int_polarity); + writel(wake_enable, sirc_regs.int_enable); + return; +} + +void msm_sirc_exit_sleep(void) +{ + writel(save_type, sirc_regs.int_type); + writel(save_polarity, sirc_regs.int_polarity); + writel(int_enable, sirc_regs.int_enable); + return; +} + static struct irq_chip sirc_irq_chip = { .name = "sirc", .irq_ack = sirc_irq_ack, @@ -157,7 +175,7 @@ void __init msm_init_sirc(void) int_enable = 0; wake_enable = 0; - for (i = FIRST_SIRC_IRQ; i < LAST_SIRC_IRQ; i++) { + for (i = FIRST_SIRC_IRQ; i < FIRST_SIRC_IRQ + NR_SIRC_IRQS; i++) { set_irq_chip(i, &sirc_irq_chip); set_irq_handler(i, handle_edge_irq); set_irq_flags(i, IRQF_VALID); From 5bd0c306a5786e481a1080dfd19e686e8126302e Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Tue, 27 Jan 2009 19:48:19 -0800 Subject: [PATCH 0382/2556] [ARM] msm: io.c setup support for scorpion Change-Id: I976621e42582dff38fa118ccdc31b195f1561836 Signed-off-by: Brian Swetland --- arch/arm/mach-msm/io.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c index 4ef6faa8565d3..3e0bff394f609 100644 --- a/arch/arm/mach-msm/io.c +++ b/arch/arm/mach-msm/io.c @@ -155,6 +155,7 @@ void __init msm_map_msm7x30_io(void) void __iomem * __msm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype) { +#ifdef CONFIG_ARCH_MSM_ARM11 if (mtype == MT_DEVICE) { /* The peripherals in the 88000000 - D0000000 range * are only accessible by type MT_DEVICE_NONSHARED. @@ -163,7 +164,7 @@ __msm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype) if ((phys_addr >= 0x88000000) && (phys_addr < 0xD0000000)) mtype = MT_DEVICE_NONSHARED; } - +#endif return __arm_ioremap_caller(phys_addr, size, mtype, __builtin_return_address(0)); } From 4878caa6f082eb7f1e55ac845cb1ac00f115202a Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 18 Feb 2009 09:13:15 -0800 Subject: [PATCH 0383/2556] [ARM] msm: add qsd8k-only clocks for mdp lcdc Signed-off-by: Dima Zavin --- arch/arm/mach-msm/clock-pcom.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-msm/clock-pcom.h b/arch/arm/mach-msm/clock-pcom.h index c126fb96135b3..b2a04d048a491 100644 --- a/arch/arm/mach-msm/clock-pcom.h +++ b/arch/arm/mach-msm/clock-pcom.h @@ -72,12 +72,12 @@ #define P_USB_HS_P_CLK 37 /* High speed USB pbus clock */ #define P_USB_OTG_CLK 38 /* Full speed USB clock */ #define P_VDC_CLK 39 /* Video controller clock */ -#if CONFIG_MSM_AMSS_VERSION >= 6350 -#define VFE_MDC_CLK 40 /* VFE MDDI client clock */ -#define VFE_CLK 41 /* Camera / Video Front End clock */ +#if CONFIG_MSM_AMSS_VERSION >= 6350 || defined(CONFIG_ARCH_QSD8X50) +#define P_VFE_MDC_CLK 40 /* VFE MDDI client clock */ +#define P_VFE_CLK 41 /* Camera / Video Front End clock */ #else/* For radio code base others */ -#define VFE_MDC_CLK 41 /* VFE MDDI client clock */ -#define VFE_CLK 40 /* Camera / Video Front End clock */ +#define P_VFE_MDC_CLK 41 /* VFE MDDI client clock */ +#define P_VFE_CLK 40 /* Camera / Video Front End clock */ #endif #define P_MDP_LCDC_PCLK_CLK 42 #define P_MDP_LCDC_PAD_PCLK_CLK 43 From fe78db4ccdaa57379811db9552940327329c3525 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Mon, 8 Feb 2010 20:52:09 -0800 Subject: [PATCH 0384/2556] [ARM] video: msm: Add support for MDP 3.1 (qsd8k) Includes support for the PPP which does scale, rotate, blend, color-convert, etc. Also adds support for the LCDC output. Signed-off-by: Dima Zavin [ARM] video: mdp: add support for pre-multiplied alpha for mdp 3.1 Signed-off-by: Dima Zavin HACK: [ARM] video: msm: lcdc: Set ebi1 clock rate to 128Mhz on resume MDP clock rate is tied to the AXI, and setting the MDP clock doesn't work. So, we request the ebi1 clock at the correct frequency whenever the screen is on, which will force the MDP clock higher. Signed-off-by: Dima Zavin [ARM] msm: mdp: cleanup in probe corectly on failure Change-Id: Ife63744371480048ae1523ec1185f9acd0e637bf Signed-off-by: Dima Zavin [ARM] video: msm: mdp: Set the ebi1 clock to 128mhz when performing blits Thsi makes sure that the MDP (locked to AXI) and the AXI/ABI1 run at the maximum frequency during blit operations. Change-Id: Ifec2f4258c00146f478fc9941a261b89254bac0f Signed-off-by: Dima Zavin [ARM] video: msm: mdp_lcdc: Do not force EBI1 to max whenever the screen is on Previously, when the frambuffer was located in the EBI, there would be visual artifacts if the EBI1/AXI frequency was not forced to 128mhz. This should no longer be required, and we should let the ARM9 scale EBI/AXI appropriately during idle or static screen refreshes. The MDP blits will force it higher when necessary. Change-Id: Iace4a056abbe50dc7d1ddb70fe3636873e5cfaf3 Signed-off-by: Dima Zavin [ARM] video: msm: Allow framebuffer to be configured in 32 bit mode For now only accept XRGB_8888, RBG_888 and RGB_565 Signed-off-by: Rebecca Schultz Zavin [ARM] video: msm: Allow users to request a larger x and yvirtual fb As long as the total size fits inside the memory allocated to the framebuffer users can request a larger size. This change allows users to request a triple buffered fb. Signed-off-by: Rebecca Schultz Zavin [ARM] video: msm: Correct use of resource in fb The resource end is the last byte, add one to get the actual size. Signed-off-by: Rebecca Schultz Zavin [ARM] video: msm: mdp_lcdc: Don't update the dma config at init Updating the dma config at init time leads to a race condition with the framebuffer which hasn't configured the format yet. Also if the dma config of the bootloader and the kernel don't match this will cause artifacts. Signed-off-by: Rebecca Schultz Zavin [ARM] video: msm: mdp_lcdc: Correct lcdc timings Signed-off-by: Rebecca Schultz Zavin video: msm: Add support for 32 bit framebuffer packed BGR instead of RGB This is the 32 bit format produced by the gpu. Signed-off-by: Rebecca Schultz Zavin ARM: msm: mdp: Catch blits that the mdp can't handle and return an error Adds checks for the following two conditions: 1. ROI extends outside the area of the source or destination image 2. Source width or height is greater than 2048 (MDP bug) Signed-off-by: Colin Cross Change-Id: I28d4dc46f5b157ebf7cacbbf5d00962fa2851c0e ARM: msm: mdp: Disable pixel-repeat mode for scaling Pixel-repeat mode was used for RGB->RGB blits, but it causes scaled text to look bad. Using the same FIR tables that we use for YUV blits looks better. Change-Id: I3041c9c42458da09b29489c5b2e8ac5b01f2c105 Signed-off-by: Colin Cross [ARM] msm: mdp: Disable SVI (sharpening) for RGB blits Leaves SVI enabled for YUV frames (generally video). Change-Id: If708e452896634c50ffa9026eb4f08c10819bc52 Signed-off-by: Colin Cross video: msm: Change the output to 565 instead of 888 Signed-off-by: Rebecca Schultz Zavin [ARM] msm: mdp: Mask out old DMA destination bits when setting new ones Change-Id: I3b9c15071bf30b93788a43ee931e5064cd337f78 Signed-off-by: Colin Cross [ARM] msm: mdp: Prevent framebuffer glitch during initialization Holds a reference to the mdp_clk until lateinit, and moves the framebuffer initialization to device_init. The framebuffer lcdc driver will grab a reference to mdp_clk, which prevents the clock from being disabled by clock_late_init Signed-off-by: Colin Cross Change-Id: I70d49b4c78b3cca8a52cf846b370d8ea7ffaa418 [ARM] msm: mdp: Prevent DMA lockups when switching bit depths Forces the lcdc off and waits a frame vsync period to prevent the MDP DMA engine crashing if the DMA bit depth is changed. A sleep cannot be used because lcdc_dma_start is called in irq context. Change-Id: Ibdceede494a6edd8f49878419f5410cabafe0253 Signed-off-by: Colin Cross [ARM] msm: mdp: Print useful messages and BUG when the MDP locks up Change-Id: Id930bc638ca86ae406c2714f5789d8f26363683a Signed-off-by: Colin Cross [ARM] msm: mdp: Add more debugging output on mdp crashes Signed-off-by: Colin Cross Change-Id: I4c188a702987a7a3baff6aab0eee77348e84a6ad [ARM] msm: mdp: Add additional debugging info when blits fail Change-Id: I5ddf43aef8b53d19c3a1cc160676ce2d0785914f --- arch/arm/mach-msm/Kconfig | 10 + arch/arm/mach-msm/include/mach/msm_fb.h | 43 +- drivers/video/Kconfig | 5 + drivers/video/msm/Makefile | 8 +- drivers/video/msm/mdp.c | 569 +++++++++++---- drivers/video/msm/mdp_csc_table.h | 163 +++-- drivers/video/msm/mdp_hw.h | 249 +++++-- drivers/video/msm/mdp_lcdc.c | 428 +++++++++++ drivers/video/msm/mdp_ppp.c | 689 ++++++++---------- drivers/video/msm/mdp_ppp.h | 82 +++ .../msm/{mdp_scale_tables.c => mdp_ppp22.c} | 331 ++++++++- drivers/video/msm/mdp_ppp31.c | 332 +++++++++ drivers/video/msm/mdp_scale_tables.h | 38 - drivers/video/msm/msm_fb.c | 71 +- 14 files changed, 2318 insertions(+), 700 deletions(-) create mode 100644 drivers/video/msm/mdp_lcdc.c create mode 100644 drivers/video/msm/mdp_ppp.h rename drivers/video/msm/{mdp_scale_tables.c => mdp_ppp22.c} (69%) create mode 100644 drivers/video/msm/mdp_ppp31.c delete mode 100644 drivers/video/msm/mdp_scale_tables.h diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index b58f74539ff61..dab91bc3eee6f 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -66,6 +66,16 @@ config HAS_MSM_DEBUG_UART_PHYS config MSM_VIC bool +config MSM_MDP22 + bool + depends on ARCH_MSM7X00A + default y + +config MSM_MDP31 + bool + depends on ARCH_QSD8X50 + default y + config MSM_AMSS_VERSION int default 6210 if MSM_AMSS_VERSION_6210 diff --git a/arch/arm/mach-msm/include/mach/msm_fb.h b/arch/arm/mach-msm/include/mach/msm_fb.h index 1f4fc81b3d8fc..42c5e2888e755 100644 --- a/arch/arm/mach-msm/include/mach/msm_fb.h +++ b/arch/arm/mach-msm/include/mach/msm_fb.h @@ -34,9 +34,12 @@ struct msmfb_callback { }; enum { - MSM_MDDI_PMDH_INTERFACE, + MSM_MDDI_PMDH_INTERFACE = 0, MSM_MDDI_EMDH_INTERFACE, MSM_EBI2_INTERFACE, + MSM_LCDC_INTERFACE, + + MSM_MDP_NUM_INTERFACES = MSM_LCDC_INTERFACE + 1, }; #define MSMFB_CAP_PARTIAL_UPDATES (1 << 0) @@ -110,17 +113,51 @@ struct msm_mddi_platform_data { } client_platform_data[]; }; +struct msm_lcdc_timing { + unsigned int clk_rate; /* dclk freq */ + unsigned int hsync_pulse_width; /* in dclks */ + unsigned int hsync_back_porch; /* in dclks */ + unsigned int hsync_front_porch; /* in dclks */ + unsigned int hsync_skew; /* in dclks */ + unsigned int vsync_pulse_width; /* in lines */ + unsigned int vsync_back_porch; /* in lines */ + unsigned int vsync_front_porch; /* in lines */ + + /* control signal polarity */ + unsigned int vsync_act_low:1; + unsigned int hsync_act_low:1; + unsigned int den_act_low:1; +}; + +struct msm_lcdc_panel_ops { + int (*init)(struct msm_lcdc_panel_ops *); + int (*uninit)(struct msm_lcdc_panel_ops *); + int (*blank)(struct msm_lcdc_panel_ops *); + int (*unblank)(struct msm_lcdc_panel_ops *); +}; + +struct msm_lcdc_platform_data { + struct msm_lcdc_panel_ops *panel_ops; + struct msm_lcdc_timing *timing; + int fb_id; + struct msm_fb_data *fb_data; + struct resource *fb_resource; +}; + struct mdp_blit_req; struct fb_info; struct mdp_device { struct device dev; - void (*dma)(struct mdp_device *mpd, uint32_t addr, + void (*dma)(struct mdp_device *mdp, uint32_t addr, uint32_t stride, uint32_t w, uint32_t h, uint32_t x, uint32_t y, struct msmfb_callback *callback, int interface); - void (*dma_wait)(struct mdp_device *mdp); + void (*dma_wait)(struct mdp_device *mdp, int interface); int (*blit)(struct mdp_device *mdp, struct fb_info *fb, struct mdp_blit_req *req); void (*set_grp_disp)(struct mdp_device *mdp, uint32_t disp_id); + void (*configure_dma)(struct mdp_device *mdp); + int (*check_output_format)(struct mdp_device *mdp, int bpp); + int (*set_output_format)(struct mdp_device *mdp, int bpp); }; struct class_interface; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 6bafb51bb4373..f92b19c1baa6d 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2330,6 +2330,11 @@ config FB_MSM select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT +config FB_MSM_LCDC + bool "Support for integrated LCD controller in qsd8x50" + depends on FB_MSM && MSM_MDP31 + default y + config FB_MX3 tristate "MX3 Framebuffer support" depends on FB && MX3_IPU diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile index 992a74173b887..15d4d552c3d34 100644 --- a/drivers/video/msm/Makefile +++ b/drivers/video/msm/Makefile @@ -8,7 +8,11 @@ endif # MDP DMA/PPP engine # -obj-y += mdp.o mdp_scale_tables.o mdp_ppp.o +obj-y += mdp.o mdp_ppp.o + + +obj-$(CONFIG_MSM_MDP22) += mdp_ppp22.o +obj-$(CONFIG_MSM_MDP31) += mdp_ppp31.o # MDDI interface # @@ -20,3 +24,5 @@ obj-y += mddi_client_dummy.o obj-y += mddi_client_toshiba.o obj-y += mddi_client_nt35399.o +# MDP LCD controller driver +obj-$(CONFIG_FB_MSM_LCDC) += mdp_lcdc.o diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 693b75d48d3e7..8e81f87738031 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -32,50 +32,52 @@ #include #include "mdp_hw.h" +#include "mdp_ppp.h" struct class *mdp_class; #define MDP_CMD_DEBUG_ACCESS_BASE (0x10000) -static uint16_t mdp_default_ccs[] = { - 0x254, 0x000, 0x331, 0x254, 0xF38, 0xE61, 0x254, 0x409, 0x000, - 0x010, 0x080, 0x080 -}; - -static DECLARE_WAIT_QUEUE_HEAD(mdp_dma2_waitqueue); static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue); -static struct msmfb_callback *dma_callback; -static struct clk *clk; static unsigned int mdp_irq_mask; -static DEFINE_SPINLOCK(mdp_lock); +struct clk *mdp_clk_to_disable_later = 0; DEFINE_MUTEX(mdp_mutex); -static int enable_mdp_irq(struct mdp_info *mdp, uint32_t mask) +static int locked_enable_mdp_irq(struct mdp_info *mdp, uint32_t mask) { - unsigned long irq_flags; - int ret = 0; - BUG_ON(!mask); - spin_lock_irqsave(&mdp_lock, irq_flags); /* if the mask bits are already set return an error, this interrupt * is already enabled */ if (mdp_irq_mask & mask) { - printk(KERN_ERR "mdp irq already on already on %x %x\n", - mdp_irq_mask, mask); - ret = -1; + pr_err("mdp irq already on %x %x\n", mdp_irq_mask, mask); + return -1; } /* if the mdp irq is not already enabled enable it */ if (!mdp_irq_mask) { - if (clk) - clk_enable(clk); + clk_set_rate(mdp->ebi1_clk, 128000000); + clk_enable(mdp->clk); enable_irq(mdp->irq); } + /* clear out any previous irqs for the requested mask*/ + mdp_writel(mdp, mask, MDP_INTR_CLEAR); + /* update the irq mask to reflect the fact that the interrupt is * enabled */ mdp_irq_mask |= mask; - spin_unlock_irqrestore(&mdp_lock, irq_flags); + mdp_writel(mdp, mdp_irq_mask, MDP_INTR_ENABLE); + return 0; +} + +static int enable_mdp_irq(struct mdp_info *mdp, uint32_t mask) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&mdp->lock, flags); + ret = locked_enable_mdp_irq(mdp, mask); + spin_unlock_irqrestore(&mdp->lock, flags); return ret; } @@ -90,23 +92,26 @@ static int locked_disable_mdp_irq(struct mdp_info *mdp, uint32_t mask) /* update the irq mask to reflect the fact that the interrupt is * disabled */ mdp_irq_mask &= ~(mask); + mdp_writel(mdp, mdp_irq_mask, MDP_INTR_ENABLE); + /* if no one is waiting on the interrupt, disable it */ if (!mdp_irq_mask) { disable_irq_nosync(mdp->irq); - if (clk) - clk_disable(clk); + if (mdp->clk) + clk_disable(mdp->clk); + clk_set_rate(mdp->ebi1_clk, 0); } return 0; } -static int disable_mdp_irq(struct mdp_info *mdp, uint32_t mask) +int disable_mdp_irq(struct mdp_info *mdp, uint32_t mask) { unsigned long irq_flags; int ret; - spin_lock_irqsave(&mdp_lock, irq_flags); + spin_lock_irqsave(&mdp->lock, irq_flags); ret = locked_disable_mdp_irq(mdp, mask); - spin_unlock_irqrestore(&mdp_lock, irq_flags); + spin_unlock_irqrestore(&mdp->lock, irq_flags); return ret; } @@ -115,19 +120,30 @@ static irqreturn_t mdp_isr(int irq, void *data) uint32_t status; unsigned long irq_flags; struct mdp_info *mdp = data; + int i; - spin_lock_irqsave(&mdp_lock, irq_flags); + spin_lock_irqsave(&mdp->lock, irq_flags); status = mdp_readl(mdp, MDP_INTR_STATUS); mdp_writel(mdp, status, MDP_INTR_CLEAR); +// pr_info("%s: status=%08x (irq_mask=%08x)\n", __func__, status, +// mdp_irq_mask); status &= mdp_irq_mask; - if (status & DL0_DMA2_TERM_DONE) { - if (dma_callback) { - dma_callback->func(dma_callback); - dma_callback = NULL; + + for (i = 0; i < MSM_MDP_NUM_INTERFACES; ++i) { + struct mdp_out_interface *out_if = &mdp->out_if[i]; + if (status & out_if->dma_mask) { + if (out_if->dma_cb) { + out_if->dma_cb->func(out_if->dma_cb); + out_if->dma_cb = NULL; + } + wake_up(&out_if->dma_waitqueue); + } + if (status & out_if->irq_mask) { + out_if->irq_cb->func(out_if->irq_cb); + out_if->irq_cb = NULL; } - wake_up(&mdp_dma2_waitqueue); } if (status & DL0_ROI_DONE) @@ -136,18 +152,18 @@ static irqreturn_t mdp_isr(int irq, void *data) if (status) locked_disable_mdp_irq(mdp, status); - spin_unlock_irqrestore(&mdp_lock, irq_flags); + spin_unlock_irqrestore(&mdp->lock, irq_flags); return IRQ_HANDLED; } -static uint32_t mdp_check_mask(uint32_t mask) +static uint32_t mdp_check_mask(struct mdp_info *mdp, uint32_t mask) { uint32_t ret; unsigned long irq_flags; - spin_lock_irqsave(&mdp_lock, irq_flags); + spin_lock_irqsave(&mdp->lock, irq_flags); ret = mdp_irq_mask & mask; - spin_unlock_irqrestore(&mdp_lock, irq_flags); + spin_unlock_irqrestore(&mdp->lock, irq_flags); return ret; } @@ -156,27 +172,52 @@ static int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq) int ret = 0; unsigned long irq_flags; - wait_event_timeout(*wq, !mdp_check_mask(mask), HZ); +// pr_info("%s: WAITING for 0x%x\n", __func__, mask); + wait_event_timeout(*wq, !mdp_check_mask(mdp, mask), HZ); - spin_lock_irqsave(&mdp_lock, irq_flags); + spin_lock_irqsave(&mdp->lock, irq_flags); if (mdp_irq_mask & mask) { + pr_warning("%s: timeout waiting for mdp to complete 0x%x\n", + __func__, mask); + printk("GLBL_CLK_ENA: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0000)); + printk("GLBL_CLK_STATE: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0004)); + printk("GLBL_SLEEP_EN: %08X\n", readl(MSM_CLK_CTL_BASE + 0x001C)); + printk("GLBL_CLK_ENA_2: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0220)); + printk("GLBL_CLK_STATE_2: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0224)); + printk("GLBL_CLK_SLEEP_EN_2: %08X\n", readl(MSM_CLK_CTL_BASE + 0x023C)); + mdp_ppp_dump_debug(mdp); locked_disable_mdp_irq(mdp, mask); - printk(KERN_WARNING "timeout waiting for mdp to complete %x\n", - mask); ret = -ETIMEDOUT; + } else { +// pr_info("%s: SUCCESS waiting for 0x%x\n", __func__, mask); } - spin_unlock_irqrestore(&mdp_lock, irq_flags); + spin_unlock_irqrestore(&mdp->lock, irq_flags); return ret; } -void mdp_dma_wait(struct mdp_device *mdp_dev) +void mdp_dma_wait(struct mdp_device *mdp_dev, int interface) { #define MDP_MAX_TIMEOUTS 20 static int timeout_count; struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + unsigned int mask = 0; + wait_queue_head_t *wq; + + switch (interface) { + case MSM_MDDI_PMDH_INTERFACE: + case MSM_MDDI_EMDH_INTERFACE: + case MSM_LCDC_INTERFACE: + BUG_ON(!mdp->out_if[interface].registered); + mask = mdp->out_if[interface].dma_mask; + wq = &mdp->out_if[interface].dma_waitqueue; + break; + default: + pr_err("%s: Unknown interface %d\n", __func__, interface); + BUG(); + } - if (mdp_wait(mdp, DL0_DMA2_TERM_DONE, &mdp_dma2_waitqueue) == -ETIMEDOUT) + if (mdp_wait(mdp, mask, wq) == -ETIMEDOUT) timeout_count++; else timeout_count = 0; @@ -193,27 +234,21 @@ static int mdp_ppp_wait(struct mdp_info *mdp) return mdp_wait(mdp, DL0_ROI_DONE, &mdp_ppp_waitqueue); } -void mdp_dma_to_mddi(struct mdp_info *mdp, uint32_t addr, uint32_t stride, - uint32_t width, uint32_t height, uint32_t x, uint32_t y, - struct msmfb_callback *callback) +static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, + uint32_t width, uint32_t height, uint32_t x, + uint32_t y) { + struct mdp_info *mdp = priv; uint32_t dma2_cfg; uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */ - if (enable_mdp_irq(mdp, DL0_DMA2_TERM_DONE)) { - printk(KERN_ERR "mdp_dma_to_mddi: busy\n"); - return; - } - - dma_callback = callback; - dma2_cfg = DMA_PACK_TIGHT | DMA_PACK_ALIGN_LSB | DMA_PACK_PATTERN_RGB | DMA_OUT_SEL_AHB | DMA_IBUF_NONCONTIGUOUS; - dma2_cfg |= DMA_IBUF_FORMAT_RGB565; + dma2_cfg |= mdp->format; dma2_cfg |= DMA_OUT_SEL_MDDI; @@ -221,15 +256,16 @@ void mdp_dma_to_mddi(struct mdp_info *mdp, uint32_t addr, uint32_t stride, dma2_cfg |= DMA_DITHER_EN; + /* 666 18BPP */ + dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + +#ifdef CONFIG_MSM_MDP22 /* setup size, address, and stride */ mdp_writel(mdp, (height << 16) | (width), MDP_CMD_DEBUG_ACCESS_BASE + 0x0184); mdp_writel(mdp, addr, MDP_CMD_DEBUG_ACCESS_BASE + 0x0188); mdp_writel(mdp, stride, MDP_CMD_DEBUG_ACCESS_BASE + 0x018C); - /* 666 18BPP */ - dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; - /* set y & x offset and MDDI transaction parameters */ mdp_writel(mdp, (y << 16) | (x), MDP_CMD_DEBUG_ACCESS_BASE + 0x0194); mdp_writel(mdp, ld_param, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0); @@ -240,6 +276,21 @@ void mdp_dma_to_mddi(struct mdp_info *mdp, uint32_t addr, uint32_t stride, /* start DMA2 */ mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0044); +#else + /* setup size, address, and stride */ + mdp_writel(mdp, (height << 16) | (width), MDP_DMA_P_SIZE); + mdp_writel(mdp, addr, MDP_DMA_P_IBUF_ADDR); + mdp_writel(mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE); + + /* set y & x offset and MDDI transaction parameters */ + mdp_writel(mdp, (y << 16) | (x), MDP_DMA_P_OUT_XY); + mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL); + mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM, + MDP_MDDI_PARAM); + + mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG); + mdp_writel(mdp, 0, MDP_DMA_P_START); +#endif } void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride, @@ -247,11 +298,26 @@ void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride, struct msmfb_callback *callback, int interface) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + struct mdp_out_interface *out_if; + unsigned long flags; + + if (interface < 0 || interface > MSM_MDP_NUM_INTERFACES || + !mdp->out_if[interface].registered) { + pr_err("%s: Unknown interface: %d\n", __func__, interface); + BUG(); + } + out_if = &mdp->out_if[interface]; - if (interface == MSM_MDDI_PMDH_INTERFACE) { - mdp_dma_to_mddi(mdp, addr, stride, width, height, x, y, - callback); + spin_lock_irqsave(&mdp->lock, flags); + if (locked_enable_mdp_irq(mdp, out_if->dma_mask)) { + pr_err("%s: busy\n", __func__); + goto done; } + + out_if->dma_cb = callback; + out_if->dma_start(out_if->priv, addr, stride, width, height, x, y); +done: + spin_unlock_irqrestore(&mdp->lock, flags); } static int get_img(struct mdp_img *img, struct fb_info *info, @@ -292,6 +358,119 @@ static void put_img(struct file *file) } } +void mdp_configure_dma(struct mdp_device *mdp_dev) +{ + struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + uint32_t dma_cfg; + + if (!mdp->dma_config_dirty) + return; + dma_cfg = mdp_readl(mdp, MDP_DMA_P_CONFIG); + dma_cfg &= ~DMA_IBUF_FORMAT_MASK; + dma_cfg &= ~DMA_PACK_PATTERN_MASK; + dma_cfg |= (mdp->format | mdp->pack_pattern); + mdp_writel(mdp, dma_cfg, MDP_DMA_P_CONFIG); + mdp->dma_config_dirty = false; + + return; +} + +int mdp_check_output_format(struct mdp_device *mdp_dev, int bpp) +{ + switch (bpp) { + case 16: + case 24: + case 32: + break; + default: + return -EINVAL; + } + return 0; +} + +int mdp_set_output_format(struct mdp_device *mdp_dev, int bpp) +{ + struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + uint32_t format, pack_pattern; + + switch (bpp) { + case 16: + format = DMA_IBUF_FORMAT_RGB565; + pack_pattern = DMA_PACK_PATTERN_RGB; + break; +#ifdef CONFIG_MSM_MDP22 + case 24: + case 32: + format = DMA_IBUF_FORMAT_RGB888_OR_ARGB8888; + break; +#else + case 24: + format = DMA_IBUF_FORMAT_RGB888; + pack_pattern = DMA_PACK_PATTERN_BGR; + break; + case 32: + format = DMA_IBUF_FORMAT_XRGB8888; + pack_pattern = DMA_PACK_PATTERN_BGR; + break; +#endif + default: + return -EINVAL; + } + if (format != mdp->format || pack_pattern != mdp->pack_pattern) { + mdp->format = format; + mdp->pack_pattern = pack_pattern; + mdp->dma_config_dirty = true; + } + + return 0; +} + +int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, unsigned long src_len, + struct file *dst_file, unsigned long dst_start, unsigned long dst_len) +{ + int ret; + enable_mdp_irq(mdp, DL0_ROI_DONE); + ret = mdp_ppp_blit(mdp, req, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); + if (unlikely(ret)) { + disable_mdp_irq(mdp, DL0_ROI_DONE); + return ret; + } + ret = mdp_ppp_wait(mdp); + if (unlikely(ret)) { + printk(KERN_ERR "%s: failed!\n", __func__); + + printk(KERN_ERR "flags: 0x%x\n", req->flags); + printk(KERN_ERR "src_start: 0x%08lx\n", src_start); + printk(KERN_ERR "src_len: 0x%08lx\n", src_len); + printk(KERN_ERR "src.offset: 0x%x\n", req->src.offset); + printk(KERN_ERR "src.format: 0x%x\n", req->src.format); + printk(KERN_ERR "src.width: %d\n", req->src.width); + printk(KERN_ERR "src.height: %d\n", req->src.height); + printk(KERN_ERR "src_rect.x: %d\n", req->src_rect.x); + printk(KERN_ERR "src_rect.y: %d\n", req->src_rect.y); + printk(KERN_ERR "src_rect.w: %d\n", req->src_rect.w); + printk(KERN_ERR "src_rect.h: %d\n", req->src_rect.h); + + printk(KERN_ERR "dst_start: 0x%08lx\n", dst_start); + printk(KERN_ERR "dst_len: 0x%08lx\n", dst_len); + printk(KERN_ERR "dst.offset: 0x%x\n", req->dst.offset); + printk(KERN_ERR "dst.format: 0x%x\n", req->dst.format); + printk(KERN_ERR "dst.width: %d\n", req->dst.width); + printk(KERN_ERR "dst.height: %d\n", req->dst.height); + printk(KERN_ERR "dst_rect.x: %d\n", req->dst_rect.x); + printk(KERN_ERR "dst_rect.y: %d\n", req->dst_rect.y); + printk(KERN_ERR "dst_rect.w: %d\n", req->dst_rect.w); + printk(KERN_ERR "dst_rect.h: %d\n", req->dst_rect.h); + + BUG(); + return ret; + } + return 0; +} + int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, struct mdp_blit_req *req) { @@ -303,7 +482,7 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, /* WORKAROUND FOR HARDWARE BUG IN BG TILE FETCH */ if (unlikely(req->src_rect.h == 0 || req->src_rect.w == 0)) { - printk(KERN_ERR "mpd_ppp: src img of zero size!\n"); + printk(KERN_ERR "mdp_ppp: src img of zero size!\n"); return -EINVAL; } if (unlikely(req->dst_rect.h == 0 || @@ -313,13 +492,13 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, /* do this first so that if this fails, the caller can always * safely call put_img */ if (unlikely(get_img(&req->src, fb, &src_start, &src_len, &src_file))) { - printk(KERN_ERR "mpd_ppp: could not retrieve src image from " + printk(KERN_ERR "mdp_ppp: could not retrieve src image from " "memory\n"); return -EINVAL; } if (unlikely(get_img(&req->dst, fb, &dst_start, &dst_len, &dst_file))) { - printk(KERN_ERR "mpd_ppp: could not retrieve dst image from " + printk(KERN_ERR "mdp_ppp: could not retrieve dst image from " "memory\n"); put_img(src_file); return -EINVAL; @@ -328,6 +507,7 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, /* transp_masking unimplemented */ req->transp_mask = MDP_TRANSP_NOP; +#ifndef CONFIG_MSM_MDP31 if (unlikely((req->transp_mask != MDP_TRANSP_NOP || req->alpha != MDP_ALPHA_NOP || HAS_ALPHA(req->src.format)) && @@ -339,15 +519,11 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, req->src_rect.w = 16*req->src_rect.w / req->dst_rect.h; req->dst_rect.h = 16; for (i = 0; i < tiles; i++) { - enable_mdp_irq(mdp, DL0_ROI_DONE); - ret = mdp_ppp_blit(mdp, req, src_file, src_start, - src_len, dst_file, dst_start, - dst_len); + ret = mdp_blit_and_wait(mdp, req, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); if (ret) - goto err_bad_blit; - ret = mdp_ppp_wait(mdp); - if (ret) - goto err_wait_failed; + goto end; req->dst_rect.y += 16; req->src_rect.x += req->src_rect.w; } @@ -356,23 +532,11 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, req->src_rect.w = remainder*req->src_rect.w / req->dst_rect.h; req->dst_rect.h = remainder; } - enable_mdp_irq(mdp, DL0_ROI_DONE); - ret = mdp_ppp_blit(mdp, req, src_file, src_start, src_len, dst_file, - dst_start, - dst_len); - if (ret) - goto err_bad_blit; - ret = mdp_ppp_wait(mdp); - if (ret) - goto err_wait_failed; +#endif + ret = mdp_blit_and_wait(mdp, req, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); end: - put_img(src_file); - put_img(dst_file); - mutex_unlock(&mdp_mutex); - return 0; -err_bad_blit: - disable_mdp_irq(mdp, DL0_ROI_DONE); -err_wait_failed: put_img(src_file); put_img(dst_file); mutex_unlock(&mdp_mutex); @@ -387,6 +551,78 @@ void mdp_set_grp_disp(struct mdp_device *mdp_dev, unsigned disp_id) mdp_writel(mdp, disp_id, MDP_FULL_BYPASS_WORD43); } +/* used by output interface drivers like mddi and lcdc */ +int mdp_out_if_register(struct mdp_device *mdp_dev, int interface, + void *private_data, uint32_t dma_mask, + mdp_dma_start_func_t dma_start) +{ + struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + unsigned long flags; + int ret = 0; + + if (interface < 0 || interface >= MSM_MDP_NUM_INTERFACES) { + pr_err("%s: invalid interface (%d)\n", __func__, interface); + return -EINVAL; + } + + spin_lock_irqsave(&mdp->lock, flags); + + if (mdp->out_if[interface].registered) { + pr_err("%s: interface (%d) already registered\n", __func__, + interface); + ret = -EINVAL; + goto done; + } + + init_waitqueue_head(&mdp->out_if[interface].dma_waitqueue); + mdp->out_if[interface].registered = 1; + mdp->out_if[interface].priv = private_data; + mdp->out_if[interface].dma_mask = dma_mask; + mdp->out_if[interface].dma_start = dma_start; + mdp->out_if[interface].dma_cb = NULL; + +done: + spin_unlock_irqrestore(&mdp->lock, flags); + return ret; +} + +int mdp_out_if_req_irq(struct mdp_device *mdp_dev, int interface, + uint32_t mask, struct msmfb_callback *cb) +{ + struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + unsigned long flags; + int ret = 0; + + if (interface < 0 || interface >= MSM_MDP_NUM_INTERFACES) { + pr_err("%s: invalid interface (%d)\n", __func__, interface); + BUG(); + } else if (!mdp->out_if[interface].registered) { + pr_err("%s: interface (%d) not registered\n", __func__, + interface); + BUG(); + } + + spin_lock_irqsave(&mdp->lock, flags); + + if (mask) { + ret = locked_enable_mdp_irq(mdp, mask); + if (ret) { + pr_err("%s: busy\n", __func__); + goto done; + } + mdp->out_if[interface].irq_mask = mask; + mdp->out_if[interface].irq_cb = cb; + } else { + locked_disable_mdp_irq(mdp, mask); + mdp->out_if[interface].irq_mask = 0; + mdp->out_if[interface].irq_cb = NULL; + } + +done: + spin_unlock_irqrestore(&mdp->lock, flags); + return ret; +} + int register_mdp_client(struct class_interface *cint) { if (!mdp_class) { @@ -398,13 +634,76 @@ int register_mdp_client(struct class_interface *cint) } #include "mdp_csc_table.h" -#include "mdp_scale_tables.h" + +void mdp_hw_init(struct mdp_info *mdp) +{ + int n; + + mdp_irq_mask = 0; + + mdp_writel(mdp, 0, MDP_INTR_ENABLE); + + /* debug interface write access */ + mdp_writel(mdp, 1, 0x60); + mdp_writel(mdp, 1, MDP_EBI2_PORTMAP_MODE); + +#ifndef CONFIG_MSM_MDP22 + /* disable lcdc */ + mdp_writel(mdp, 0, MDP_LCDC_EN); + /* enable auto clock gating for all blocks by default */ + mdp_writel(mdp, 0xffffffff, MDP_CGC_EN); + /* reset color/gamma correct parms */ + mdp_writel(mdp, 0, MDP_DMA_P_COLOR_CORRECT_CONFIG); +#endif + + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01f8); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01fc); + mdp_writel(mdp, 1, 0x60); + + for (n = 0; n < ARRAY_SIZE(csc_color_lut); n++) + mdp_writel(mdp, csc_color_lut[n].val, csc_color_lut[n].reg); + + /* clear up unused fg/main registers */ + /* comp.plane 2&3 ystride */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0120); + + /* unpacked pattern */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x012c); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0130); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0134); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0158); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x015c); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0160); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0170); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0174); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x017c); + + /* comp.plane 2 & 3 */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0114); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0118); + + /* clear unused bg registers */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01c8); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01d0); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01dc); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e0); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e4); + + for (n = 0; n < ARRAY_SIZE(csc_matrix_config_table); n++) + mdp_writel(mdp, csc_matrix_config_table[n].val, + csc_matrix_config_table[n].reg); + + mdp_ppp_init_scale(mdp); + +#ifndef CONFIG_MSM_MDP31 + mdp_writel(mdp, 0x04000400, MDP_COMMAND_CONFIG); +#endif +} int mdp_probe(struct platform_device *pdev) { struct resource *resource; int ret; - int n; struct mdp_info *mdp; resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -417,6 +716,8 @@ int mdp_probe(struct platform_device *pdev) if (!mdp) return -ENOMEM; + spin_lock_init(&mdp->lock); + mdp->irq = platform_get_irq(pdev, 0); if (mdp->irq < 0) { pr_err("mdp: can not get mdp irq\n"); @@ -436,66 +737,38 @@ int mdp_probe(struct platform_device *pdev) mdp->mdp_dev.dma_wait = mdp_dma_wait; mdp->mdp_dev.blit = mdp_blit; mdp->mdp_dev.set_grp_disp = mdp_set_grp_disp; + mdp->mdp_dev.set_output_format = mdp_set_output_format; + mdp->mdp_dev.check_output_format = mdp_check_output_format; + mdp->mdp_dev.configure_dma = mdp_configure_dma; + + ret = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp, + MDP_DMA_P_DONE, mdp_dma_to_mddi); + if (ret) + goto error_mddi_pmdh_register; - clk = clk_get(&pdev->dev, "mdp_clk"); - if (IS_ERR(clk)) { + mdp->clk = clk_get(&pdev->dev, "mdp_clk"); + if (IS_ERR(mdp->clk)) { printk(KERN_INFO "mdp: failed to get mdp clk"); - return PTR_ERR(clk); + ret = PTR_ERR(mdp->clk); + goto error_get_mdp_clk; } + mdp->ebi1_clk = clk_get(NULL, "ebi1_clk"); + if (IS_ERR(mdp->ebi1_clk)) { + pr_err("mdp: failed to get ebi1 clk\n"); + ret = PTR_ERR(mdp->ebi1_clk); + goto error_get_ebi1_clk; + } + + ret = request_irq(mdp->irq, mdp_isr, IRQF_DISABLED, "msm_mdp", mdp); if (ret) goto error_request_irq; disable_irq(mdp->irq); - mdp_irq_mask = 0; - - /* debug interface write access */ - mdp_writel(mdp, 1, 0x60); - - mdp_writel(mdp, MDP_ANY_INTR_MASK, MDP_INTR_ENABLE); - mdp_writel(mdp, 1, MDP_EBI2_PORTMAP_MODE); - - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01f8); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01fc); - - for (n = 0; n < ARRAY_SIZE(csc_table); n++) - mdp_writel(mdp, csc_table[n].val, csc_table[n].reg); - - /* clear up unused fg/main registers */ - /* comp.plane 2&3 ystride */ - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0120); - - /* unpacked pattern */ - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x012c); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0130); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0134); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0158); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x015c); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0160); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0170); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0174); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x017c); - - /* comp.plane 2 & 3 */ - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0114); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0118); - - /* clear unused bg registers */ - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01c8); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01d0); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01dc); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e0); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e4); - for (n = 0; n < ARRAY_SIZE(mdp_upscale_table); n++) - mdp_writel(mdp, mdp_upscale_table[n].val, - mdp_upscale_table[n].reg); - - for (n = 0; n < 9; n++) - mdp_writel(mdp, mdp_default_ccs[n], 0x40440 + 4 * n); - mdp_writel(mdp, mdp_default_ccs[9], 0x40500 + 4 * 0); - mdp_writel(mdp, mdp_default_ccs[10], 0x40500 + 4 * 0); - mdp_writel(mdp, mdp_default_ccs[11], 0x40500 + 4 * 0); + clk_enable(mdp->clk); + mdp_clk_to_disable_later = mdp->clk; + mdp_hw_init(mdp); /* register mdp device */ mdp->mdp_dev.dev.parent = &pdev->dev; @@ -509,14 +782,22 @@ int mdp_probe(struct platform_device *pdev) ret = device_register(&mdp->mdp_dev.dev); if (ret) goto error_device_register; + + pr_info("%s: initialized\n", __func__); + return 0; error_device_register: free_irq(mdp->irq, mdp); error_request_irq: + clk_put(mdp->ebi1_clk); +error_get_ebi1_clk: + clk_put(mdp->clk); +error_get_mdp_clk: +error_mddi_pmdh_register: iounmap(mdp->base); -error_get_irq: error_ioremap: +error_get_irq: kfree(mdp); return ret; } @@ -526,6 +807,13 @@ static struct platform_driver msm_mdp_driver = { .driver = {.name = "msm_mdp"}, }; +static int __init mdp_lateinit(void) +{ + if (mdp_clk_to_disable_later) + clk_disable(mdp_clk_to_disable_later); + return 0; +} + static int __init mdp_init(void) { mdp_class = class_create(THIS_MODULE, "msm_mdp"); @@ -537,3 +825,4 @@ static int __init mdp_init(void) } subsys_initcall(mdp_init); +late_initcall(mdp_lateinit); diff --git a/drivers/video/msm/mdp_csc_table.h b/drivers/video/msm/mdp_csc_table.h index d1cde30ead52f..a0f72c0ebd4f7 100644 --- a/drivers/video/msm/mdp_csc_table.h +++ b/drivers/video/msm/mdp_csc_table.h @@ -1,4 +1,4 @@ -/* drivers/video/msm_fb/mdp_csc_table.h +/* drivers/video/msm/mdp_csc_table.h * * Copyright (C) 2007 QUALCOMM Incorporated * Copyright (C) 2007 Google Incorporated @@ -16,57 +16,116 @@ static struct { uint32_t reg; uint32_t val; -} csc_table[] = { - { 0x40400, 0x83 }, - { 0x40404, 0x102 }, - { 0x40408, 0x32 }, - { 0x4040c, 0xffffffb5 }, - { 0x40410, 0xffffff6c }, - { 0x40414, 0xe1 }, - { 0x40418, 0xe1 }, - { 0x4041c, 0xffffff45 }, - { 0x40420, 0xffffffdc }, - { 0x40440, 0x254 }, - { 0x40444, 0x0 }, - { 0x40448, 0x331 }, - { 0x4044c, 0x254 }, - { 0x40450, 0xffffff38 }, - { 0x40454, 0xfffffe61 }, - { 0x40458, 0x254 }, - { 0x4045c, 0x409 }, - { 0x40460, 0x0 }, - { 0x40480, 0x5d }, - { 0x40484, 0x13a }, - { 0x40488, 0x20 }, - { 0x4048c, 0xffffffcd }, - { 0x40490, 0xffffff54 }, - { 0x40494, 0xe1 }, - { 0x40498, 0xe1 }, - { 0x4049c, 0xffffff35 }, - { 0x404a0, 0xffffffec }, - { 0x404c0, 0x254 }, - { 0x404c4, 0x0 }, - { 0x404c8, 0x396 }, - { 0x404cc, 0x254 }, - { 0x404d0, 0xffffff94 }, - { 0x404d4, 0xfffffef0 }, - { 0x404d8, 0x254 }, - { 0x404dc, 0x43a }, - { 0x404e0, 0x0 }, - { 0x40500, 0x10 }, - { 0x40504, 0x80 }, - { 0x40508, 0x80 }, - { 0x40540, 0x10 }, - { 0x40544, 0x80 }, - { 0x40548, 0x80 }, - { 0x40580, 0x10 }, - { 0x40584, 0xeb }, - { 0x40588, 0x10 }, - { 0x4058c, 0xf0 }, - { 0x405c0, 0x10 }, - { 0x405c4, 0xeb }, - { 0x405c8, 0x10 }, - { 0x405cc, 0xf0 }, +} csc_matrix_config_table[] = { + /* RGB -> YUV primary forward matrix (set1). */ + { MDP_CSC_PFMVn(0), 0x83 }, + { MDP_CSC_PFMVn(1), 0x102 }, + { MDP_CSC_PFMVn(2), 0x32 }, + { MDP_CSC_PFMVn(3), 0xffffffb5 }, + { MDP_CSC_PFMVn(4), 0xffffff6c }, + { MDP_CSC_PFMVn(5), 0xe1 }, + { MDP_CSC_PFMVn(6), 0xe1 }, + { MDP_CSC_PFMVn(7), 0xffffff45 }, + { MDP_CSC_PFMVn(8), 0xffffffdc }, + + /* YUV -> RGB primary reverse matrix (set2) */ + { MDP_CSC_PRMVn(0), 0x254 }, + { MDP_CSC_PRMVn(1), 0x0 }, + { MDP_CSC_PRMVn(2), 0x331 }, + { MDP_CSC_PRMVn(3), 0x254 }, + { MDP_CSC_PRMVn(4), 0xffffff38 }, + { MDP_CSC_PRMVn(5), 0xfffffe61 }, + { MDP_CSC_PRMVn(6), 0x254 }, + { MDP_CSC_PRMVn(7), 0x409 }, + { MDP_CSC_PRMVn(8), 0x0 }, + +#ifndef CONFIG_MSM_MDP31 + /* For MDP 2.2/3.0 */ + + /* primary limit vector */ + { MDP_CSC_PLVn(0), 0x10 }, + { MDP_CSC_PLVn(1), 0xeb }, + { MDP_CSC_PLVn(2), 0x10 }, + { MDP_CSC_PLVn(3), 0xf0 }, + + /* primary bias vector */ + { MDP_CSC_PBVn(0), 0x10 }, + { MDP_CSC_PBVn(1), 0x80 }, + { MDP_CSC_PBVn(2), 0x80 }, + +#else /* CONFIG_MSM_MDP31 */ + + /* limit vectors configuration */ + /* rgb -> yuv (set1) pre-limit vector */ + { MDP_PPP_CSC_PRE_LV1n(0), 0x10 }, + { MDP_PPP_CSC_PRE_LV1n(1), 0xeb }, + { MDP_PPP_CSC_PRE_LV1n(2), 0x10 }, + { MDP_PPP_CSC_PRE_LV1n(3), 0xf0 }, + { MDP_PPP_CSC_PRE_LV1n(4), 0x10 }, + { MDP_PPP_CSC_PRE_LV1n(5), 0xf0 }, + + /* rgb -> yuv (set1) post-limit vector */ + { MDP_PPP_CSC_POST_LV1n(0), 0x0 }, + { MDP_PPP_CSC_POST_LV1n(1), 0xff }, + { MDP_PPP_CSC_POST_LV1n(2), 0x0 }, + { MDP_PPP_CSC_POST_LV1n(3), 0xff }, + { MDP_PPP_CSC_POST_LV1n(4), 0x0 }, + { MDP_PPP_CSC_POST_LV1n(5), 0xff }, + + /* yuv -> rgb (set2) pre-limit vector */ + { MDP_PPP_CSC_PRE_LV2n(0), 0x0 }, + { MDP_PPP_CSC_PRE_LV2n(1), 0xff }, + { MDP_PPP_CSC_PRE_LV2n(2), 0x0 }, + { MDP_PPP_CSC_PRE_LV2n(3), 0xff }, + { MDP_PPP_CSC_PRE_LV2n(4), 0x0 }, + { MDP_PPP_CSC_PRE_LV2n(5), 0xff }, + + /* yuv -> rgb (set2) post-limit vector */ + { MDP_PPP_CSC_POST_LV2n(0), 0x10 }, + { MDP_PPP_CSC_POST_LV2n(1), 0xeb }, + { MDP_PPP_CSC_POST_LV2n(2), 0x10 }, + { MDP_PPP_CSC_POST_LV2n(3), 0xf0 }, + { MDP_PPP_CSC_POST_LV2n(4), 0x10 }, + { MDP_PPP_CSC_POST_LV2n(5), 0xf0 }, + + /* bias vectors configuration */ + + /* XXX: why is set2 used for rgb->yuv, but set1 */ + /* used for yuv -> rgb??!? Seems to be the reverse of the + * other vectors. */ + + /* RGB -> YUV pre-bias vector... */ + { MDP_PPP_CSC_PRE_BV2n(0), 0 }, + { MDP_PPP_CSC_PRE_BV2n(1), 0 }, + { MDP_PPP_CSC_PRE_BV2n(2), 0 }, + + /* RGB -> YUV post-bias vector */ + { MDP_PPP_CSC_POST_BV2n(0), 0x10 }, + { MDP_PPP_CSC_POST_BV2n(1), 0x80 }, + { MDP_PPP_CSC_POST_BV2n(2), 0x80 }, + + /* YUV -> RGB pre-bias vector... */ + { MDP_PPP_CSC_PRE_BV1n(0), 0x1f0 }, + { MDP_PPP_CSC_PRE_BV1n(1), 0x180 }, + { MDP_PPP_CSC_PRE_BV1n(2), 0x180 }, + + /* YUV -> RGB post-bias vector */ + { MDP_PPP_CSC_POST_BV1n(0), 0 }, + { MDP_PPP_CSC_POST_BV1n(1), 0 }, + { MDP_PPP_CSC_POST_BV1n(2), 0 }, + + /* luma filter coefficients */ + { MDP_PPP_DEINT_COEFFn(0), 0x3e0 }, + { MDP_PPP_DEINT_COEFFn(1), 0x360 }, + { MDP_PPP_DEINT_COEFFn(2), 0x120 }, + { MDP_PPP_DEINT_COEFFn(3), 0x140 }, +#endif +}; + +static struct { + uint32_t reg; + uint32_t val; +} csc_color_lut[] = { { 0x40800, 0x0 }, { 0x40804, 0x151515 }, { 0x40808, 0x1d1d1d }, diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h index d80477415caaa..f35a757d4731e 100644 --- a/drivers/video/msm/mdp_hw.h +++ b/drivers/video/msm/mdp_hw.h @@ -15,20 +15,61 @@ #ifndef _MDP_HW_H_ #define _MDP_HW_H_ +#include +#include #include #include +typedef void (*mdp_dma_start_func_t)(void *private_data, uint32_t addr, + uint32_t stride, uint32_t width, + uint32_t height, uint32_t x, uint32_t y); + +struct mdp_out_interface { + uint32_t registered:1; + void *priv; + + /* If the interface client wants to get DMA_DONE events */ + uint32_t dma_mask; + mdp_dma_start_func_t dma_start; + + struct msmfb_callback *dma_cb; + wait_queue_head_t dma_waitqueue; + + /* If the interface client wants to be notified of non-DMA irqs, + * e.g. LCDC/TV-out frame start */ + uint32_t irq_mask; + struct msmfb_callback *irq_cb; +}; + struct mdp_info { + spinlock_t lock; struct mdp_device mdp_dev; char * __iomem base; int irq; + struct clk *clk; + struct clk *ebi1_clk; + struct mdp_out_interface out_if[MSM_MDP_NUM_INTERFACES]; + int format; + int pack_pattern; + bool dma_config_dirty; }; + +extern int mdp_out_if_register(struct mdp_device *mdp_dev, int interface, + void *private_data, uint32_t dma_mask, + mdp_dma_start_func_t dma_start); + +extern int mdp_out_if_req_irq(struct mdp_device *mdp_dev, int interface, + uint32_t mask, struct msmfb_callback *cb); + struct mdp_blit_req; struct mdp_device; int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, struct file *src_file, unsigned long src_start, unsigned long src_len, struct file *dst_file, unsigned long dst_start, unsigned long dst_len); + +void mdp_ppp_dump_debug(const struct mdp_info *mdp); + #define mdp_writel(mdp, value, offset) writel(value, mdp->base + offset) #define mdp_readl(mdp, offset) readl(mdp->base + offset) @@ -48,10 +89,18 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, #define MDP_DISPLAY_STATUS (0x00038) #define MDP_EBI2_LCD0 (0x0003c) #define MDP_EBI2_LCD1 (0x00040) +#define MDP_EBI2_PORTMAP_MODE (0x0005c) + +#ifndef CONFIG_MSM_MDP31 #define MDP_DISPLAY0_ADDR (0x00054) #define MDP_DISPLAY1_ADDR (0x00058) -#define MDP_EBI2_PORTMAP_MODE (0x0005c) -#define MDP_MODE (0x00060) +#define MDP_PPP_CMD_MODE (0x00060) +#else +#define MDP_DISPLAY0_ADDR (0x10000) +#define MDP_DISPLAY1_ADDR (0x10004) +#define MDP_PPP_CMD_MODE (0x10060) +#endif + #define MDP_TV_OUT_STATUS (0x00064) #define MDP_HW_VERSION (0x00070) #define MDP_SW_RESET (0x00074) @@ -61,6 +110,8 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, #define MDP_SECONDARY_VSYNC_OUT_CTRL (0x00084) #define MDP_EXTERNAL_VSYNC_OUT_CTRL (0x00088) #define MDP_VSYNC_CTRL (0x0008c) +#define MDP_MDDI_PARAM_WR_SEL (0x00090) +#define MDP_MDDI_PARAM (0x00094) #define MDP_CGC_EN (0x00100) #define MDP_CMD_STATUS (0x10008) #define MDP_PROFILE_EN (0x10010) @@ -107,6 +158,7 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, #define MDP_FULL_BYPASS_WORD35 (0x1018c) #define MDP_FULL_BYPASS_WORD37 (0x10194) #define MDP_FULL_BYPASS_WORD39 (0x1019c) +#define MDP_PPP_OUT_XY (0x1019c) #define MDP_FULL_BYPASS_WORD40 (0x101a0) #define MDP_FULL_BYPASS_WORD41 (0x101a4) #define MDP_FULL_BYPASS_WORD43 (0x101ac) @@ -129,11 +181,27 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, #define MDP_FULL_BYPASS_WORD61 (0x101f4) #define MDP_FULL_BYPASS_WORD62 (0x101f8) #define MDP_FULL_BYPASS_WORD63 (0x101fc) + +#ifdef CONFIG_MSM_MDP31 +#define MDP_PPP_SRC_XY (0x10200) +#define MDP_PPP_BG_XY (0x10204) +#define MDP_PPP_SRC_IMAGE_SIZE (0x10208) +#define MDP_PPP_BG_IMAGE_SIZE (0x1020c) +#define MDP_PPP_SCALE_CONFIG (0x10230) +#define MDP_PPP_CSC_CONFIG (0x10240) +#define MDP_PPP_BLEND_BG_ALPHA_SEL (0x70010) +#endif + #define MDP_TFETCH_TEST_MODE (0x20004) #define MDP_TFETCH_STATUS (0x20008) #define MDP_TFETCH_TILE_COUNT (0x20010) #define MDP_TFETCH_FETCH_COUNT (0x20014) #define MDP_TFETCH_CONSTANT_COLOR (0x20040) +#define MDP_BGTFETCH_TEST_MODE (0x28004) +#define MDP_BGTFETCH_STATUS (0x28008) +#define MDP_BGTFETCH_TILE_COUNT (0x28010) +#define MDP_BGTFETCH_FETCH_COUNT (0x28014) +#define MDP_BGTFETCH_CONSTANT_COLOR (0x28040) #define MDP_CSC_BYPASS (0x40004) #define MDP_SCALE_COEFF_LSB (0x5fffc) #define MDP_TV_OUT_CTL (0xc0000) @@ -158,55 +226,49 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, #define MDP_TEST_MISR_CURR_VAL_DCLK (0xd020c) #define MDP_TEST_CAPTURED_DCLK (0xd0210) #define MDP_TEST_MISR_CAPT_VAL_DCLK (0xd0214) -#define MDP_LCDC_CTL (0xe0000) + +#define MDP_DMA_P_START (0x00044) +#define MDP_DMA_P_CONFIG (0x90000) +#define MDP_DMA_P_SIZE (0x90004) +#define MDP_DMA_P_IBUF_ADDR (0x90008) +#define MDP_DMA_P_IBUF_Y_STRIDE (0x9000c) +#define MDP_DMA_P_OUT_XY (0x90010) +#define MDP_DMA_P_COLOR_CORRECT_CONFIG (0x90070) + +#define MDP_LCDC_EN (0xe0000) #define MDP_LCDC_HSYNC_CTL (0xe0004) -#define MDP_LCDC_VSYNC_CTL (0xe0008) -#define MDP_LCDC_ACTIVE_HCTL (0xe000c) -#define MDP_LCDC_ACTIVE_VCTL (0xe0010) -#define MDP_LCDC_BORDER_CLR (0xe0014) -#define MDP_LCDC_H_BLANK (0xe0018) -#define MDP_LCDC_V_BLANK (0xe001c) -#define MDP_LCDC_UNDERFLOW_CLR (0xe0020) -#define MDP_LCDC_HSYNC_SKEW (0xe0024) -#define MDP_LCDC_TEST_CTL (0xe0028) -#define MDP_LCDC_LINE_IRQ (0xe002c) -#define MDP_LCDC_CTL_POLARITY (0xe0030) -#define MDP_LCDC_DMA_CONFIG (0xe1000) -#define MDP_LCDC_DMA_SIZE (0xe1004) -#define MDP_LCDC_DMA_IBUF_ADDR (0xe1008) -#define MDP_LCDC_DMA_IBUF_Y_STRIDE (0xe100c) - - -#define MDP_DMA2_TERM 0x1 -#define MDP_DMA3_TERM 0x2 -#define MDP_PPP_TERM 0x3 +#define MDP_LCDC_VSYNC_PERIOD (0xe0008) +#define MDP_LCDC_VSYNC_PULSE_WIDTH (0xe000c) +#define MDP_LCDC_DISPLAY_HCTL (0xe0010) +#define MDP_LCDC_DISPLAY_V_START (0xe0014) +#define MDP_LCDC_DISPLAY_V_END (0xe0018) +#define MDP_LCDC_ACTIVE_HCTL (0xe001c) +#define MDP_LCDC_ACTIVE_V_START (0xe0020) +#define MDP_LCDC_ACTIVE_V_END (0xe0024) +#define MDP_LCDC_BORDER_CLR (0xe0028) +#define MDP_LCDC_UNDERFLOW_CTL (0xe002c) +#define MDP_LCDC_HSYNC_SKEW (0xe0030) +#define MDP_LCDC_TEST_CTL (0xe0034) +#define MDP_LCDC_CTL_POLARITY (0xe0038) + +#define MDP_PPP_SCALE_STATUS (0x50000) +#define MDP_PPP_BLEND_STATUS (0x70000) + +/* MDP_SW_RESET */ +#define MDP_PPP_SW_RESET (1<<4) /* MDP_INTR_ENABLE */ -#define DL0_ROI_DONE (1<<0) -#define DL1_ROI_DONE (1<<1) -#define DL0_DMA2_TERM_DONE (1<<2) -#define DL1_DMA2_TERM_DONE (1<<3) -#define DL0_PPP_TERM_DONE (1<<4) -#define DL1_PPP_TERM_DONE (1<<5) -#define TV_OUT_DMA3_DONE (1<<6) -#define TV_ENC_UNDERRUN (1<<7) -#define DL0_FETCH_DONE (1<<11) -#define DL1_FETCH_DONE (1<<12) - -#define MDP_PPP_BUSY_STATUS (DL0_ROI_DONE| \ - DL1_ROI_DONE| \ - DL0_PPP_TERM_DONE| \ - DL1_PPP_TERM_DONE) - -#define MDP_ANY_INTR_MASK (DL0_ROI_DONE| \ - DL1_ROI_DONE| \ - DL0_DMA2_TERM_DONE| \ - DL1_DMA2_TERM_DONE| \ - DL0_PPP_TERM_DONE| \ - DL1_PPP_TERM_DONE| \ - DL0_FETCH_DONE| \ - DL1_FETCH_DONE| \ - TV_ENC_UNDERRUN) +#define DL0_ROI_DONE (1<<0) +#define TV_OUT_DMA3_DONE (1<<6) +#define TV_ENC_UNDERRUN (1<<7) + +#ifdef CONFIG_MSM_MDP22 +#define MDP_DMA_P_DONE (1 << 2) +#else /* CONFIG_MSM_MDP31 */ +#define MDP_DMA_P_DONE (1 << 14) +#define MDP_LCDC_UNDERFLOW (1 << 16) +#define MDP_LCDC_FRAME_START (1 << 15) +#endif #define MDP_TOP_LUMA 16 #define MDP_TOP_CHROMA 0 @@ -316,7 +378,12 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, #define PPP_OP_SCALE_X_ON (1<<0) #define PPP_OP_SCALE_Y_ON (1<<1) +#ifndef CONFIG_MSM_MDP31 #define PPP_OP_CONVERT_RGB2YCBCR 0 +#else +#define PPP_OP_CONVERT_RGB2YCBCR (1<<30) +#endif + #define PPP_OP_CONVERT_YCBCR2RGB (1<<2) #define PPP_OP_CONVERT_ON (1<<3) @@ -372,6 +439,13 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, #define PPP_OP_BG_CHROMA_SITE_COSITE 0 #define PPP_OP_BG_CHROMA_SITE_OFFSITE (1<<27) +#define PPP_BLEND_BG_USE_ALPHA_SEL (1 << 0) +#define PPP_BLEND_BG_ALPHA_REVERSE (1 << 3) +#define PPP_BLEND_BG_SRCPIXEL_ALPHA (0 << 1) +#define PPP_BLEND_BG_DSTPIXEL_ALPHA (1 << 1) +#define PPP_BLEND_BG_CONSTANT_ALPHA (2 << 1) +#define PPP_BLEND_BG_CONST_ALPHA_VAL(x) ((x) << 24) + /* MDP_PPP_DESTINATION_CONFIG / MDP_FULL_BYPASS_WORD20 */ #define PPP_DST_C0G_8BIT ((1<<0)|(1<<1)) #define PPP_DST_C1B_8BIT ((1<<3)|(1<<2)) @@ -589,20 +663,71 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, #define PPP_ADDR_BG_CFG MDP_FULL_BYPASS_WORD53 #define PPP_ADDR_BG_PACK_PATTERN MDP_FULL_BYPASS_WORD54 +/* color conversion matrix configuration registers */ +/* pfmv is mv1, prmv is mv2 */ +#define MDP_CSC_PFMVn(n) (0x40400 + (4 * (n))) +#define MDP_CSC_PRMVn(n) (0x40440 + (4 * (n))) + +#ifdef CONFIG_MSM_MDP31 +#define MDP_PPP_CSC_PRE_BV1n(n) (0x40500 + (4 * (n))) +#define MDP_PPP_CSC_PRE_BV2n(n) (0x40540 + (4 * (n))) +#define MDP_PPP_CSC_POST_BV1n(n) (0x40580 + (4 * (n))) +#define MDP_PPP_CSC_POST_BV2n(n) (0x405c0 + (4 * (n))) + +#define MDP_PPP_CSC_PRE_LV1n(n) (0x40600 + (4 * (n))) +#define MDP_PPP_CSC_PRE_LV2n(n) (0x40640 + (4 * (n))) +#define MDP_PPP_CSC_POST_LV1n(n) (0x40680 + (4 * (n))) +#define MDP_PPP_CSC_POST_LV2n(n) (0x406c0 + (4 * (n))) + +#define MDP_PPP_SCALE_COEFF_D0_SET (0) +#define MDP_PPP_SCALE_COEFF_D1_SET (1) +#define MDP_PPP_SCALE_COEFF_D2_SET (2) +#define MDP_PPP_SCALE_COEFF_U1_SET (3) +#define MDP_PPP_SCALE_COEFF_LSBn(n) (0x50400 + (8 * (n))) +#define MDP_PPP_SCALE_COEFF_MSBn(n) (0x50404 + (8 * (n))) + +#define MDP_PPP_DEINT_COEFFn(n) (0x30010 + (4 * (n))) + +#define MDP_PPP_SCALER_FIR (0) +#define MDP_PPP_SCALER_MN (1) + +#else /* !defined(CONFIG_MSM_MDP31) */ + +#define MDP_CSC_PBVn(n) (0x40500 + (4 * (n))) +#define MDP_CSC_SBVn(n) (0x40540 + (4 * (n))) +#define MDP_CSC_PLVn(n) (0x40580 + (4 * (n))) +#define MDP_CSC_SLVn(n) (0x405c0 + (4 * (n))) + +#endif + + /* MDP_DMA_CONFIG / MDP_FULL_BYPASS_WORD32 */ -#define DMA_DSTC0G_6BITS (1<<1) -#define DMA_DSTC1B_6BITS (1<<3) -#define DMA_DSTC2R_6BITS (1<<5) #define DMA_DSTC0G_5BITS (1<<0) #define DMA_DSTC1B_5BITS (1<<2) #define DMA_DSTC2R_5BITS (1<<4) +#define DMA_DSTC0G_6BITS (2<<0) +#define DMA_DSTC1B_6BITS (2<<2) +#define DMA_DSTC2R_6BITS (2<<4) + +#define DMA_DSTC0G_8BITS (3<<0) +#define DMA_DSTC1B_8BITS (3<<2) +#define DMA_DSTC2R_8BITS (3<<4) + +#define DMA_DST_BITS_MASK 0x3F + #define DMA_PACK_TIGHT (1<<6) #define DMA_PACK_LOOSE 0 #define DMA_PACK_ALIGN_LSB 0 #define DMA_PACK_ALIGN_MSB (1<<7) +#define DMA_PACK_PATTERN_MASK (0x3f<<8) #define DMA_PACK_PATTERN_RGB \ (MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 2)<<8) +#define DMA_PACK_PATTERN_BGR \ + (MDP_GET_PACK_PATTERN(0, CLR_B, CLR_G, CLR_R, 2)<<8) + + +#ifdef CONFIG_MSM_MDP22 #define DMA_OUT_SEL_AHB 0 #define DMA_OUT_SEL_MDDI (1<<14) @@ -610,16 +735,32 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, #define DMA_AHBM_LCD_SEL_SECONDARY (1<<15) #define DMA_IBUF_C3ALPHA_EN (1<<16) #define DMA_DITHER_EN (1<<17) - #define DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY 0 #define DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY (1<<18) #define DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL (1<<19) - #define DMA_IBUF_FORMAT_RGB565 (1<<20) #define DMA_IBUF_FORMAT_RGB888_OR_ARGB8888 0 - +#define DMA_IBUF_FORMAT_MASK (1 << 20) #define DMA_IBUF_NONCONTIGUOUS (1<<21) +#else /* CONFIG_MSM_MDP31 */ + +#define DMA_OUT_SEL_AHB (0 << 19) +#define DMA_OUT_SEL_MDDI (1 << 19) +#define DMA_OUT_SEL_LCDC (2 << 19) +#define DMA_OUT_SEL_LCDC_MDDI (3 << 19) +#define DMA_DITHER_EN (1 << 24) +#define DMA_IBUF_FORMAT_RGB888 (0 << 25) +#define DMA_IBUF_FORMAT_RGB565 (1 << 25) +#define DMA_IBUF_FORMAT_XRGB8888 (2 << 25) +#define DMA_IBUF_FORMAT_MASK (3 << 25) +#define DMA_IBUF_NONCONTIGUOUS (0) + +#define DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY (0) +#define DMA_MDDI_DMAOUT_LCD_SEL_SECONDARY (0) +#define DMA_MDDI_DMAOUT_LCD_SEL_EXTERNAL (0) +#endif + /* MDDI REGISTER ? */ #define MDDI_VDO_PACKET_DESC 0x5666 #define MDDI_VDO_PACKET_PRIM 0xC3 diff --git a/drivers/video/msm/mdp_lcdc.c b/drivers/video/msm/mdp_lcdc.c new file mode 100644 index 0000000000000..a1e526d2f9438 --- /dev/null +++ b/drivers/video/msm/mdp_lcdc.c @@ -0,0 +1,428 @@ +/* drivers/video/msm/mdp_lcdc.c + * + * Copyright (c) 2009 Google Inc. + * Copyright (c) 2009 QUALCOMM Incorporated + * + * 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. + * + * Author: Dima Zavin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "mdp_hw.h" + +struct mdp_lcdc_info { + struct mdp_info *mdp; + struct clk *mdp_clk; + struct clk *pclk; + struct clk *pad_pclk; + struct msm_panel_data fb_panel_data; + struct platform_device fb_pdev; + struct msm_lcdc_platform_data *pdata; + uint32_t fb_start; + + struct msmfb_callback frame_start_cb; + wait_queue_head_t vsync_waitq; + int got_vsync; + + struct { + uint32_t clk_rate; + uint32_t hsync_ctl; + uint32_t vsync_period; + uint32_t vsync_pulse_width; + uint32_t display_hctl; + uint32_t display_vstart; + uint32_t display_vend; + uint32_t hsync_skew; + uint32_t polarity; + } parms; +}; + +static struct mdp_device *mdp_dev; + +#define panel_to_lcdc(p) container_of((p), struct mdp_lcdc_info, fb_panel_data) + +static int lcdc_unblank(struct msm_panel_data *fb_panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; + + pr_info("%s: ()\n", __func__); + panel_ops->unblank(panel_ops); + + return 0; +} + +static int lcdc_blank(struct msm_panel_data *fb_panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; + + pr_info("%s: ()\n", __func__); + panel_ops->blank(panel_ops); + + return 0; +} + +static int lcdc_suspend(struct msm_panel_data *fb_panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + + pr_info("%s: suspending\n", __func__); + + mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); + clk_disable(lcdc->pad_pclk); + clk_disable(lcdc->pclk); + clk_disable(lcdc->mdp_clk); + + return 0; +} + +static int lcdc_resume(struct msm_panel_data *fb_panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + + pr_info("%s: resuming\n", __func__); + + clk_enable(lcdc->mdp_clk); + clk_enable(lcdc->pclk); + clk_enable(lcdc->pad_pclk); + mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); + + return 0; +} + +static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) +{ + struct msm_panel_data *fb_panel = &lcdc->fb_panel_data; + uint32_t dma_cfg; + + clk_enable(lcdc->mdp_clk); + clk_enable(lcdc->pclk); + clk_enable(lcdc->pad_pclk); + + clk_set_rate(lcdc->pclk, lcdc->parms.clk_rate); + clk_set_rate(lcdc->pad_pclk, lcdc->parms.clk_rate); + + /* write the lcdc params */ + mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL); + mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD); + mdp_writel(lcdc->mdp, lcdc->parms.vsync_pulse_width, + MDP_LCDC_VSYNC_PULSE_WIDTH); + mdp_writel(lcdc->mdp, lcdc->parms.display_hctl, MDP_LCDC_DISPLAY_HCTL); + mdp_writel(lcdc->mdp, lcdc->parms.display_vstart, + MDP_LCDC_DISPLAY_V_START); + mdp_writel(lcdc->mdp, lcdc->parms.display_vend, MDP_LCDC_DISPLAY_V_END); + mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW); + + mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR); + mdp_writel(lcdc->mdp, 0xff, MDP_LCDC_UNDERFLOW_CTL); + mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL); + mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START); + mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END); + mdp_writel(lcdc->mdp, lcdc->parms.polarity, MDP_LCDC_CTL_POLARITY); + + /* config the dma_p block that drives the lcdc data */ + mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_DMA_P_IBUF_ADDR); + mdp_writel(lcdc->mdp, (((fb_panel->fb_data->yres & 0x7ff) << 16) | + (fb_panel->fb_data->xres & 0x7ff)), + MDP_DMA_P_SIZE); + + mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY); + + dma_cfg = mdp_readl(lcdc->mdp, MDP_DMA_P_CONFIG); + dma_cfg |= (DMA_PACK_ALIGN_LSB | + DMA_PACK_PATTERN_RGB | + DMA_DITHER_EN); + dma_cfg |= DMA_OUT_SEL_LCDC; + dma_cfg &= ~DMA_DST_BITS_MASK; + dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG); + + /* enable the lcdc timing generation */ + mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); + + return 0; +} + +static void lcdc_wait_vsync(struct msm_panel_data *panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(panel); + int ret; + + ret = wait_event_timeout(lcdc->vsync_waitq, lcdc->got_vsync, HZ / 2); + if (!ret && !lcdc->got_vsync) + pr_err("%s: timeout waiting for VSYNC\n", __func__); + lcdc->got_vsync = 0; +} + +static void lcdc_request_vsync(struct msm_panel_data *fb_panel, + struct msmfb_callback *vsync_cb) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + + /* the vsync callback will start the dma */ + vsync_cb->func(vsync_cb); + lcdc->got_vsync = 0; + mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, MDP_LCDC_FRAME_START, + &lcdc->frame_start_cb); + lcdc_wait_vsync(fb_panel); +} + +static void lcdc_clear_vsync(struct msm_panel_data *fb_panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + lcdc->got_vsync = 0; + mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, 0, NULL); +} + +/* called in irq context with mdp lock held, when mdp gets the + * MDP_LCDC_FRAME_START interrupt */ +static void lcdc_frame_start(struct msmfb_callback *cb) +{ + struct mdp_lcdc_info *lcdc; + + lcdc = container_of(cb, struct mdp_lcdc_info, frame_start_cb); + + lcdc->got_vsync = 1; + wake_up(&lcdc->vsync_waitq); +} + +static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride, + uint32_t width, uint32_t height, uint32_t x, + uint32_t y) +{ + struct mdp_lcdc_info *lcdc = priv; + + struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + if (mdp->dma_config_dirty) + { + mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); + mdelay(20); + mdp_dev->configure_dma(mdp_dev); + mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); + } + mdp_writel(lcdc->mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE); + mdp_writel(lcdc->mdp, addr, MDP_DMA_P_IBUF_ADDR); +} + +static void precompute_timing_parms(struct mdp_lcdc_info *lcdc) +{ + struct msm_lcdc_timing *timing = lcdc->pdata->timing; + struct msm_fb_data *fb_data = lcdc->pdata->fb_data; + unsigned int hsync_period; + unsigned int hsync_start_x; + unsigned int hsync_end_x; + unsigned int vsync_period; + unsigned int display_vstart; + unsigned int display_vend; + + hsync_period = (timing->hsync_back_porch + + fb_data->xres + timing->hsync_front_porch); + hsync_start_x = timing->hsync_back_porch; + hsync_end_x = hsync_start_x + fb_data->xres - 1; + + vsync_period = (timing->vsync_back_porch + + fb_data->yres + timing->vsync_front_porch); + vsync_period *= hsync_period; + + display_vstart = timing->vsync_back_porch; + display_vstart *= hsync_period; + display_vstart += timing->hsync_skew; + + display_vend = (timing->vsync_back_porch + fb_data->yres) * + hsync_period; + display_vend += timing->hsync_skew - 1; + + /* register values we pre-compute at init time from the timing + * information in the panel info */ + lcdc->parms.hsync_ctl = (((hsync_period & 0xfff) << 16) | + (timing->hsync_pulse_width & 0xfff)); + lcdc->parms.vsync_period = vsync_period & 0xffffff; + lcdc->parms.vsync_pulse_width = (timing->vsync_pulse_width * + hsync_period) & 0xffffff; + + lcdc->parms.display_hctl = (((hsync_end_x & 0xfff) << 16) | + (hsync_start_x & 0xfff)); + lcdc->parms.display_vstart = display_vstart & 0xffffff; + lcdc->parms.display_vend = display_vend & 0xffffff; + lcdc->parms.hsync_skew = timing->hsync_skew & 0xfff; + lcdc->parms.polarity = ((timing->hsync_act_low << 0) | + (timing->vsync_act_low << 1) | + (timing->den_act_low << 2)); + lcdc->parms.clk_rate = timing->clk_rate; +} + +static int mdp_lcdc_probe(struct platform_device *pdev) +{ + struct msm_lcdc_platform_data *pdata = pdev->dev.platform_data; + struct mdp_lcdc_info *lcdc; + int ret = 0; + + if (!pdata) { + pr_err("%s: no LCDC platform data found\n", __func__); + return -EINVAL; + } + + lcdc = kzalloc(sizeof(struct mdp_lcdc_info), GFP_KERNEL); + if (!lcdc) + return -ENOMEM; + + /* We don't actually own the clocks, the mdp does. */ + lcdc->mdp_clk = clk_get(mdp_dev->dev.parent, "mdp_clk"); + if (IS_ERR(lcdc->mdp_clk)) { + pr_err("%s: failed to get mdp_clk\n", __func__); + ret = PTR_ERR(lcdc->mdp_clk); + goto err_get_mdp_clk; + } + + lcdc->pclk = clk_get(mdp_dev->dev.parent, "lcdc_pclk_clk"); + if (IS_ERR(lcdc->pclk)) { + pr_err("%s: failed to get lcdc_pclk\n", __func__); + ret = PTR_ERR(lcdc->pclk); + goto err_get_pclk; + } + + lcdc->pad_pclk = clk_get(mdp_dev->dev.parent, "lcdc_pad_pclk_clk"); + if (IS_ERR(lcdc->pad_pclk)) { + pr_err("%s: failed to get lcdc_pad_pclk\n", __func__); + ret = PTR_ERR(lcdc->pad_pclk); + goto err_get_pad_pclk; + } + + init_waitqueue_head(&lcdc->vsync_waitq); + lcdc->pdata = pdata; + lcdc->frame_start_cb.func = lcdc_frame_start; + + platform_set_drvdata(pdev, lcdc); + + mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, MDP_DMA_P_DONE, + lcdc_dma_start); + + precompute_timing_parms(lcdc); + + lcdc->fb_start = pdata->fb_resource->start; + lcdc->mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + + lcdc->fb_panel_data.suspend = lcdc_suspend; + lcdc->fb_panel_data.resume = lcdc_resume; + lcdc->fb_panel_data.wait_vsync = lcdc_wait_vsync; + lcdc->fb_panel_data.request_vsync = lcdc_request_vsync; + lcdc->fb_panel_data.clear_vsync = lcdc_clear_vsync; + lcdc->fb_panel_data.blank = lcdc_blank; + lcdc->fb_panel_data.unblank = lcdc_unblank; + lcdc->fb_panel_data.fb_data = pdata->fb_data; + lcdc->fb_panel_data.interface_type = MSM_LCDC_INTERFACE; + + ret = lcdc_hw_init(lcdc); + if (ret) { + pr_err("%s: Cannot initialize the mdp_lcdc\n", __func__); + goto err_hw_init; + } + + lcdc->fb_pdev.name = "msm_panel"; + lcdc->fb_pdev.id = pdata->fb_id; + lcdc->fb_pdev.resource = pdata->fb_resource; + lcdc->fb_pdev.num_resources = 1; + lcdc->fb_pdev.dev.platform_data = &lcdc->fb_panel_data; + + if (pdata->panel_ops->init) + pdata->panel_ops->init(pdata->panel_ops); + + ret = platform_device_register(&lcdc->fb_pdev); + if (ret) { + pr_err("%s: Cannot register msm_panel pdev\n", __func__); + goto err_plat_dev_reg; + } + + pr_info("%s: initialized\n", __func__); + + return 0; + +err_plat_dev_reg: +err_hw_init: + platform_set_drvdata(pdev, NULL); + clk_put(lcdc->pad_pclk); +err_get_pad_pclk: + clk_put(lcdc->pclk); +err_get_pclk: + clk_put(lcdc->mdp_clk); +err_get_mdp_clk: + kfree(lcdc); + return ret; +} + +static int mdp_lcdc_remove(struct platform_device *pdev) +{ + struct mdp_lcdc_info *lcdc = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + clk_put(lcdc->pclk); + clk_put(lcdc->pad_pclk); + kfree(lcdc); + + return 0; +} + +static struct platform_driver mdp_lcdc_driver = { + .probe = mdp_lcdc_probe, + .remove = mdp_lcdc_remove, + .driver = { + .name = "msm_mdp_lcdc", + .owner = THIS_MODULE, + }, +}; + +static int mdp_lcdc_add_mdp_device(struct device *dev, + struct class_interface *class_intf) +{ + /* might need locking if mulitple mdp devices */ + if (mdp_dev) + return 0; + mdp_dev = container_of(dev, struct mdp_device, dev); + return platform_driver_register(&mdp_lcdc_driver); +} + +static void mdp_lcdc_remove_mdp_device(struct device *dev, + struct class_interface *class_intf) +{ + /* might need locking if mulitple mdp devices */ + if (dev != &mdp_dev->dev) + return; + platform_driver_unregister(&mdp_lcdc_driver); + mdp_dev = NULL; +} + +static struct class_interface mdp_lcdc_interface = { + .add_dev = &mdp_lcdc_add_mdp_device, + .remove_dev = &mdp_lcdc_remove_mdp_device, +}; + +static int __init mdp_lcdc_init(void) +{ + return register_mdp_client(&mdp_lcdc_interface); +} + +module_init(mdp_lcdc_init); diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c index 579a7a6bf1c73..ee53e4952c0bf 100644 --- a/drivers/video/msm/mdp_ppp.c +++ b/drivers/video/msm/mdp_ppp.c @@ -20,36 +20,25 @@ #include #include "mdp_hw.h" -#include "mdp_scale_tables.h" +#include "mdp_ppp.h" +#define PPP_DUMP_BLITS 0 + +#define PPP_DEBUG_MSGS 1 +#if PPP_DEBUG_MSGS +#define DLOG(fmt,args...) \ + do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, \ + __LINE__, ##args); } \ + while (0) +#else #define DLOG(x...) do {} while (0) +#endif -#define MDP_DOWNSCALE_BLUR (MDP_DOWNSCALE_MAX + 1) -static int downscale_y_table = MDP_DOWNSCALE_MAX; -static int downscale_x_table = MDP_DOWNSCALE_MAX; - -struct mdp_regs { - uint32_t src0; - uint32_t src1; - uint32_t dst0; - uint32_t dst1; - uint32_t src_cfg; - uint32_t dst_cfg; - uint32_t src_pack; - uint32_t dst_pack; - uint32_t src_rect; - uint32_t dst_rect; - uint32_t src_ystride; - uint32_t dst_ystride; - uint32_t op; - uint32_t src_bpp; - uint32_t dst_bpp; - uint32_t edge; - uint32_t phasex_init; - uint32_t phasey_init; - uint32_t phasex_step; - uint32_t phasey_step; -}; +#define IMG_LEN(rect_h, w, rect_w, bpp) (((rect_h) * w) * bpp) + +#define Y_TO_CRCB_RATIO(format) \ + ((format == MDP_Y_CBCR_H2V2 || format == MDP_Y_CRCB_H2V2) ? 2 :\ + (format == MDP_Y_CBCR_H2V1 || format == MDP_Y_CRCB_H2V1) ? 1 : 1) static uint32_t pack_pattern[] = { PPP_ARRAY0(PACK_PATTERN) @@ -90,26 +79,105 @@ static uint32_t bg_op_chroma[] = { PPP_ARRAY1(CHROMA_SAMP, BG) }; -static void rotate_dst_addr_x(struct mdp_blit_req *req, struct mdp_regs *regs) +static uint32_t get_luma_offset(struct mdp_img *img, + struct mdp_rect *rect, uint32_t bpp) { +#ifndef CONFIG_MSM_MDP31 + return (rect->x + (rect->y * img->width)) * bpp; +#else + return 0; +#endif +} + +static uint32_t get_chroma_offset(struct mdp_img *img, + struct mdp_rect *rect, uint32_t bpp) +{ +#ifndef CONFIG_MSM_MDP31 + uint32_t compress_v = Y_TO_CRCB_RATIO(img->format); + uint32_t compress_h = 2; + uint32_t offset = 0; + + if (IS_PSEUDOPLNR(img->format)) { + offset = (rect->x / compress_h) * compress_h; + offset += rect->y == 0 ? 0 : + ((rect->y + 1) / compress_v) * img->width; + offset *= bpp; + } + return offset; +#else + return 0; +#endif +} + +static void set_src_region(struct mdp_img *img, struct mdp_rect *rect, + struct ppp_regs *regs) +{ + regs->src_rect = (rect->h << 16) | (rect->w & 0x1fff); + +#ifdef CONFIG_MSM_MDP31 + regs->src_xy = (rect->y << 16) | (rect->x & 0x1fff); + regs->src_img_sz = (img->height << 16) | (img->width & 0x1fff); +#endif +} + +static inline void set_dst_region(struct mdp_rect *rect, struct ppp_regs *regs) +{ + regs->dst_rect = (rect->h << 16) | (rect->w & 0xfff); + +#ifdef CONFIG_MSM_MDP31 + regs->dst_xy = (rect->y << 16) | (rect->x & 0x1fff); +#endif +} + +static void set_blend_region(struct mdp_img *img, struct mdp_rect *rect, + struct ppp_regs *regs) +{ +#ifdef CONFIG_MSM_MDP31 + uint32_t rect_x = rect->x; + uint32_t rect_y = rect->y; + uint32_t img_w = img->width; + uint32_t img_h = img->height; + + /* HW bug workaround */ + if (img->format == MDP_YCRYCB_H2V1) { + regs->bg0 += (rect_x + (rect_y * img_w)) * regs->bg_bpp; + rect_x = 0; + rect_y = 0; + img_w = rect->w; + img_h = rect->h; + } + + regs->bg_xy = (rect_y << 16) | (rect_x & 0x1fff); + regs->bg_img_sz = (img_h << 16) | (img_w & 0x1fff); +#endif +} + +static void rotate_dst_addr_x(struct mdp_blit_req *req, + struct ppp_regs *regs) +{ +#ifndef CONFIG_MSM_MDP31 regs->dst0 += (req->dst_rect.w - min((uint32_t)16, req->dst_rect.w)) * regs->dst_bpp; regs->dst1 += (req->dst_rect.w - min((uint32_t)16, req->dst_rect.w)) * regs->dst_bpp; +#endif } -static void rotate_dst_addr_y(struct mdp_blit_req *req, struct mdp_regs *regs) +static void rotate_dst_addr_y(struct mdp_blit_req *req, + struct ppp_regs *regs) { +#ifndef CONFIG_MSM_MDP31 regs->dst0 += (req->dst_rect.h - min((uint32_t)16, req->dst_rect.h)) * regs->dst_ystride; regs->dst1 += (req->dst_rect.h - min((uint32_t)16, req->dst_rect.h)) * regs->dst_ystride; +#endif } static void blit_rotate(struct mdp_blit_req *req, - struct mdp_regs *regs) + struct ppp_regs *regs) { if (req->flags == MDP_ROT_NOP) return; @@ -128,16 +196,24 @@ static void blit_rotate(struct mdp_blit_req *req, regs->op |= PPP_OP_FLIP_LR; } -static void blit_convert(struct mdp_blit_req *req, struct mdp_regs *regs) +static void blit_convert(struct mdp_blit_req *req, struct ppp_regs *regs) { if (req->src.format == req->dst.format) return; if (IS_RGB(req->src.format) && IS_YCRCB(req->dst.format)) { regs->op |= PPP_OP_CONVERT_RGB2YCBCR | PPP_OP_CONVERT_ON; +#ifdef CONFIG_MSM_MDP31 + /* primary really means set1 */ + regs->op |= PPP_OP_CONVERT_MATRIX_PRIMARY; + regs->csc_cfg = 0x1e; +#endif } else if (IS_YCRCB(req->src.format) && IS_RGB(req->dst.format)) { regs->op |= PPP_OP_CONVERT_YCBCR2RGB | PPP_OP_CONVERT_ON; - if (req->dst.format == MDP_RGB_565) - regs->op |= PPP_OP_CONVERT_MATRIX_SECONDARY; +#ifdef CONFIG_MSM_MDP31 + /* secondary really means set2 */ + regs->op |= PPP_OP_CONVERT_MATRIX_SECONDARY; + regs->csc_cfg = 0; +#endif } } @@ -167,7 +243,7 @@ static uint32_t transp_convert(struct mdp_blit_req *req) } #undef GET_BIT_RANGE -static void blit_blend(struct mdp_blit_req *req, struct mdp_regs *regs) +static void blit_blend(struct mdp_blit_req *req, struct ppp_regs *regs) { /* TRANSP BLEND */ if (req->transp_mask != MDP_TRANSP_NOP) { @@ -192,8 +268,22 @@ static void blit_blend(struct mdp_blit_req *req, struct mdp_regs *regs) req->alpha &= 0xff; /* ALPHA BLEND */ if (HAS_ALPHA(req->src.format)) { - regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | - PPP_OP_BLEND_SRCPIXEL_ALPHA; + regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON; + if (req->flags & MDP_BLEND_FG_PREMULT) { +#ifdef CONFIG_MSM_MDP31 + /* premultiplied alpha: + * bg_alpha = (1 - fg_alpha) + * fg_alpha = 0xff + */ + regs->bg_alpha_sel = PPP_BLEND_BG_USE_ALPHA_SEL | + PPP_BLEND_BG_ALPHA_REVERSE | + PPP_BLEND_BG_SRCPIXEL_ALPHA; + regs->op |= PPP_OP_BLEND_CONSTANT_ALPHA; + req->alpha = 0xff; +#endif + } else { + regs->op |= PPP_OP_BLEND_SRCPIXEL_ALPHA; + } } else if (req->alpha < MDP_ALPHA_NOP) { /* just blend by alpha */ regs->op |= PPP_OP_ROT_ON | PPP_OP_BLEND_ON | @@ -202,254 +292,31 @@ static void blit_blend(struct mdp_blit_req *req, struct mdp_regs *regs) } regs->op |= bg_op_chroma[req->dst.format]; -} - -#define ONE_HALF (1LL << 32) -#define ONE (1LL << 33) -#define TWO (2LL << 33) -#define THREE (3LL << 33) -#define FRAC_MASK (ONE - 1) -#define INT_MASK (~FRAC_MASK) - -static int scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t origin, - uint32_t *phase_init, uint32_t *phase_step) -{ - /* to improve precicsion calculations are done in U31.33 and converted - * to U3.29 at the end */ - int64_t k1, k2, k3, k4, tmp; - uint64_t n, d, os, os_p, od, od_p, oreq; - unsigned rpa = 0; - int64_t ip64, delta; - - if (dim_out % 3 == 0) - rpa = !(dim_in % (dim_out / 3)); - - n = ((uint64_t)dim_out) << 34; - d = dim_in; - if (!d) - return -1; - do_div(n, d); - k3 = (n + 1) >> 1; - if ((k3 >> 4) < (1LL << 27) || (k3 >> 4) > (1LL << 31)) { - DLOG("crap bad scale\n"); - return -1; - } - n = ((uint64_t)dim_in) << 34; - d = (uint64_t)dim_out; - if (!d) - return -1; - do_div(n, d); - k1 = (n + 1) >> 1; - k2 = (k1 - ONE) >> 1; - - *phase_init = (int)(k2 >> 4); - k4 = (k3 - ONE) >> 1; - - if (rpa) { - os = ((uint64_t)origin << 33) - ONE_HALF; - tmp = (dim_out * os) + ONE_HALF; - if (!dim_in) - return -1; - do_div(tmp, dim_in); - od = tmp - ONE_HALF; - } else { - os = ((uint64_t)origin << 1) - 1; - od = (((k3 * os) >> 1) + k4); - } - - od_p = od & INT_MASK; - if (od_p != od) - od_p += ONE; - - if (rpa) { - tmp = (dim_in * od_p) + ONE_HALF; - if (!dim_in) - return -1; - do_div(tmp, dim_in); - os_p = tmp - ONE_HALF; - } else { - os_p = ((k1 * (od_p >> 33)) + k2); - } - - oreq = (os_p & INT_MASK) - ONE; - - ip64 = os_p - oreq; - delta = ((int64_t)(origin) << 33) - oreq; - ip64 -= delta; - /* limit to valid range before the left shift */ - delta = (ip64 & (1LL << 63)) ? 4 : -4; - delta <<= 33; - while (abs((int)(ip64 >> 33)) > 4) - ip64 += delta; - *phase_init = (int)(ip64 >> 4); - *phase_step = (uint32_t)(k1 >> 4); - return 0; -} - -static void load_scale_table(const struct mdp_info *mdp, - struct mdp_table_entry *table, int len) -{ - int i; - for (i = 0; i < len; i++) - mdp_writel(mdp, table[i].val, table[i].reg); -} - -enum { -IMG_LEFT, -IMG_RIGHT, -IMG_TOP, -IMG_BOTTOM, -}; - -static void get_edge_info(uint32_t src, uint32_t src_coord, uint32_t dst, - uint32_t *interp1, uint32_t *interp2, - uint32_t *repeat1, uint32_t *repeat2) { - if (src > 3 * dst) { - *interp1 = 0; - *interp2 = src - 1; - *repeat1 = 0; - *repeat2 = 0; - } else if (src == 3 * dst) { - *interp1 = 0; - *interp2 = src; - *repeat1 = 0; - *repeat2 = 1; - } else if (src > dst && src < 3 * dst) { - *interp1 = -1; - *interp2 = src; - *repeat1 = 1; - *repeat2 = 1; - } else if (src == dst) { - *interp1 = -1; - *interp2 = src + 1; - *repeat1 = 1; - *repeat2 = 2; - } else { - *interp1 = -2; - *interp2 = src + 1; - *repeat1 = 2; - *repeat2 = 2; - } - *interp1 += src_coord; - *interp2 += src_coord; -} - -static int get_edge_cond(struct mdp_blit_req *req, struct mdp_regs *regs) -{ - int32_t luma_interp[4]; - int32_t luma_repeat[4]; - int32_t chroma_interp[4]; - int32_t chroma_bound[4]; - int32_t chroma_repeat[4]; - uint32_t dst_w, dst_h; - - memset(&luma_interp, 0, sizeof(int32_t) * 4); - memset(&luma_repeat, 0, sizeof(int32_t) * 4); - memset(&chroma_interp, 0, sizeof(int32_t) * 4); - memset(&chroma_bound, 0, sizeof(int32_t) * 4); - memset(&chroma_repeat, 0, sizeof(int32_t) * 4); - regs->edge = 0; - - if (req->flags & MDP_ROT_90) { - dst_w = req->dst_rect.h; - dst_h = req->dst_rect.w; - } else { - dst_w = req->dst_rect.w; - dst_h = req->dst_rect.h; - } - - if (regs->op & (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON)) { - get_edge_info(req->src_rect.h, req->src_rect.y, dst_h, - &luma_interp[IMG_TOP], &luma_interp[IMG_BOTTOM], - &luma_repeat[IMG_TOP], &luma_repeat[IMG_BOTTOM]); - get_edge_info(req->src_rect.w, req->src_rect.x, dst_w, - &luma_interp[IMG_LEFT], &luma_interp[IMG_RIGHT], - &luma_repeat[IMG_LEFT], &luma_repeat[IMG_RIGHT]); - } else { - luma_interp[IMG_LEFT] = req->src_rect.x; - luma_interp[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1; - luma_interp[IMG_TOP] = req->src_rect.y; - luma_interp[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1; - luma_repeat[IMG_LEFT] = 0; - luma_repeat[IMG_TOP] = 0; - luma_repeat[IMG_RIGHT] = 0; - luma_repeat[IMG_BOTTOM] = 0; - } - - chroma_interp[IMG_LEFT] = luma_interp[IMG_LEFT]; - chroma_interp[IMG_RIGHT] = luma_interp[IMG_RIGHT]; - chroma_interp[IMG_TOP] = luma_interp[IMG_TOP]; - chroma_interp[IMG_BOTTOM] = luma_interp[IMG_BOTTOM]; - - chroma_bound[IMG_LEFT] = req->src_rect.x; - chroma_bound[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1; - chroma_bound[IMG_TOP] = req->src_rect.y; - chroma_bound[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1; - - if (IS_YCRCB(req->src.format)) { - chroma_interp[IMG_LEFT] = chroma_interp[IMG_LEFT] >> 1; - chroma_interp[IMG_RIGHT] = (chroma_interp[IMG_RIGHT] + 1) >> 1; - - chroma_bound[IMG_LEFT] = chroma_bound[IMG_LEFT] >> 1; - chroma_bound[IMG_RIGHT] = chroma_bound[IMG_RIGHT] >> 1; - } - - if (req->src.format == MDP_Y_CBCR_H2V2 || - req->src.format == MDP_Y_CRCB_H2V2) { - chroma_interp[IMG_TOP] = (chroma_interp[IMG_TOP] - 1) >> 1; - chroma_interp[IMG_BOTTOM] = (chroma_interp[IMG_BOTTOM] + 1) - >> 1; - chroma_bound[IMG_TOP] = (chroma_bound[IMG_TOP] + 1) >> 1; - chroma_bound[IMG_BOTTOM] = chroma_bound[IMG_BOTTOM] >> 1; - } - chroma_repeat[IMG_LEFT] = chroma_bound[IMG_LEFT] - - chroma_interp[IMG_LEFT]; - chroma_repeat[IMG_RIGHT] = chroma_interp[IMG_RIGHT] - - chroma_bound[IMG_RIGHT]; - chroma_repeat[IMG_TOP] = chroma_bound[IMG_TOP] - - chroma_interp[IMG_TOP]; - chroma_repeat[IMG_BOTTOM] = chroma_interp[IMG_BOTTOM] - - chroma_bound[IMG_BOTTOM]; - - if (chroma_repeat[IMG_LEFT] < 0 || chroma_repeat[IMG_LEFT] > 3 || - chroma_repeat[IMG_RIGHT] < 0 || chroma_repeat[IMG_RIGHT] > 3 || - chroma_repeat[IMG_TOP] < 0 || chroma_repeat[IMG_TOP] > 3 || - chroma_repeat[IMG_BOTTOM] < 0 || chroma_repeat[IMG_BOTTOM] > 3 || - luma_repeat[IMG_LEFT] < 0 || luma_repeat[IMG_LEFT] > 3 || - luma_repeat[IMG_RIGHT] < 0 || luma_repeat[IMG_RIGHT] > 3 || - luma_repeat[IMG_TOP] < 0 || luma_repeat[IMG_TOP] > 3 || - luma_repeat[IMG_BOTTOM] < 0 || luma_repeat[IMG_BOTTOM] > 3) - return -1; - - regs->edge |= (chroma_repeat[IMG_LEFT] & 3) << MDP_LEFT_CHROMA; - regs->edge |= (chroma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_CHROMA; - regs->edge |= (chroma_repeat[IMG_TOP] & 3) << MDP_TOP_CHROMA; - regs->edge |= (chroma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_CHROMA; - regs->edge |= (luma_repeat[IMG_LEFT] & 3) << MDP_LEFT_LUMA; - regs->edge |= (luma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_LUMA; - regs->edge |= (luma_repeat[IMG_TOP] & 3) << MDP_TOP_LUMA; - regs->edge |= (luma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_LUMA; - return 0; + /* since we always blend src + dst -> dst, copy most of the + * configuration from dest to bg */ + regs->bg0 = regs->dst0; + regs->bg1 = regs->dst1; + regs->bg_cfg = src_img_cfg[req->dst.format]; + regs->bg_bpp = regs->dst_bpp; + regs->bg_pack = pack_pattern[req->dst.format]; + regs->bg_ystride = regs->dst_ystride; + set_blend_region(&req->dst, &req->dst_rect, regs); } static int blit_scale(const struct mdp_info *mdp, struct mdp_blit_req *req, - struct mdp_regs *regs) + struct ppp_regs *regs) { - uint32_t phase_init_x, phase_init_y, phase_step_x, phase_step_y; - uint32_t scale_factor_x, scale_factor_y; - uint32_t downscale; - uint32_t dst_w, dst_h; + struct mdp_rect dst_rect; + memcpy(&dst_rect, &req->dst_rect, sizeof(dst_rect)); if (req->flags & MDP_ROT_90) { - dst_w = req->dst_rect.h; - dst_h = req->dst_rect.w; - } else { - dst_w = req->dst_rect.w; - dst_h = req->dst_rect.h; + dst_rect.w = req->dst_rect.h; + dst_rect.h = req->dst_rect.w; } - if ((req->src_rect.w == dst_w) && (req->src_rect.h == dst_h) && - !(req->flags & MDP_BLUR)) { + + if ((req->src_rect.w == dst_rect.w) && (req->src_rect.h == dst_rect.h) + && !(req->flags & MDP_BLUR)) { regs->phasex_init = 0; regs->phasey_init = 0; regs->phasex_step = 0; @@ -457,73 +324,30 @@ static int blit_scale(const struct mdp_info *mdp, struct mdp_blit_req *req, return 0; } - if (scale_params(req->src_rect.w, dst_w, 1, &phase_init_x, - &phase_step_x) || - scale_params(req->src_rect.h, dst_h, 1, &phase_init_y, - &phase_step_y)) + if (mdp_ppp_cfg_scale(mdp, regs, &req->src_rect, &dst_rect, + req->src.format, req->dst.format)) { + DLOG("crap, bad scale\n"); return -1; - - scale_factor_x = (dst_w * 10) / req->src_rect.w; - scale_factor_y = (dst_h * 10) / req->src_rect.h; - - if (scale_factor_x > 8) - downscale = MDP_DOWNSCALE_PT8TO1; - else if (scale_factor_x > 6) - downscale = MDP_DOWNSCALE_PT6TOPT8; - else if (scale_factor_x > 4) - downscale = MDP_DOWNSCALE_PT4TOPT6; - else - downscale = MDP_DOWNSCALE_PT2TOPT4; - if (downscale != downscale_x_table) { - load_scale_table(mdp, mdp_downscale_x_table[downscale], 64); - downscale_x_table = downscale; - } - - if (scale_factor_y > 8) - downscale = MDP_DOWNSCALE_PT8TO1; - else if (scale_factor_y > 6) - downscale = MDP_DOWNSCALE_PT6TOPT8; - else if (scale_factor_y > 4) - downscale = MDP_DOWNSCALE_PT4TOPT6; - else - downscale = MDP_DOWNSCALE_PT2TOPT4; - if (downscale != downscale_y_table) { - load_scale_table(mdp, mdp_downscale_y_table[downscale], 64); - downscale_y_table = downscale; } - regs->phasex_init = phase_init_x; - regs->phasey_init = phase_init_y; - regs->phasex_step = phase_step_x; - regs->phasey_step = phase_step_y; regs->op |= (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON); return 0; - } static void blit_blur(const struct mdp_info *mdp, struct mdp_blit_req *req, - struct mdp_regs *regs) + struct ppp_regs *regs) { + int ret; if (!(req->flags & MDP_BLUR)) return; - if (!(downscale_x_table == MDP_DOWNSCALE_BLUR && - downscale_y_table == MDP_DOWNSCALE_BLUR)) { - load_scale_table(mdp, mdp_gaussian_blur_table, 128); - downscale_x_table = MDP_DOWNSCALE_BLUR; - downscale_y_table = MDP_DOWNSCALE_BLUR; - } + ret = mdp_ppp_load_blur(mdp); + if (ret) + return; regs->op |= (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON); } - -#define IMG_LEN(rect_h, w, rect_w, bpp) (((rect_h) * w) * bpp) - -#define Y_TO_CRCB_RATIO(format) \ - ((format == MDP_Y_CBCR_H2V2 || format == MDP_Y_CRCB_H2V2) ? 2 :\ - (format == MDP_Y_CBCR_H2V1 || format == MDP_Y_CRCB_H2V1) ? 1 : 1) - static void get_len(struct mdp_img *img, struct mdp_rect *rect, uint32_t bpp, uint32_t *len0, uint32_t *len1) { @@ -536,7 +360,7 @@ static void get_len(struct mdp_img *img, struct mdp_rect *rect, uint32_t bpp, static int valid_src_dst(unsigned long src_start, unsigned long src_len, unsigned long dst_start, unsigned long dst_len, - struct mdp_blit_req *req, struct mdp_regs *regs) + struct mdp_blit_req *req, struct ppp_regs *regs) { unsigned long src_min_ok = src_start; unsigned long src_max_ok = src_start + src_len; @@ -576,7 +400,7 @@ static int valid_src_dst(unsigned long src_start, unsigned long src_len, return 1; } -static void flush_imgs(struct mdp_blit_req *req, struct mdp_regs *regs, +static void flush_imgs(struct mdp_blit_req *req, struct ppp_regs *regs, struct file *src_file, struct file *dst_file) { #ifdef CONFIG_ANDROID_PMEM @@ -600,77 +424,120 @@ static void flush_imgs(struct mdp_blit_req *req, struct mdp_regs *regs, #endif } -static void get_chroma_addr(struct mdp_img *img, struct mdp_rect *rect, - uint32_t base, uint32_t bpp, uint32_t cfg, - uint32_t *addr, uint32_t *ystride) +static uint32_t get_chroma_base(struct mdp_img *img, uint32_t base, + uint32_t bpp) { - uint32_t compress_v = Y_TO_CRCB_RATIO(img->format); - uint32_t compress_h = 2; - uint32_t offset; + uint32_t addr = 0; - if (IS_PSEUDOPLNR(img->format)) { - offset = (rect->x / compress_h) * compress_h; - offset += rect->y == 0 ? 0 : - ((rect->y + 1) / compress_v) * img->width; - *addr = base + (img->width * img->height * bpp); - *addr += offset * bpp; - *ystride |= *ystride << 16; - } else { - *addr = 0; - } + if (IS_PSEUDOPLNR(img->format)) + addr = base + (img->width * img->height * bpp); + return addr; } +#if PPP_DUMP_BLITS +#define mdp_writel_dbg(mdp, val, reg) do { \ + pr_info("%s: writing 0x%08x=0x%08x\n", __func__, (reg), (val));\ + mdp_writel((mdp), (val), (reg)); \ + } while (0) +#else +#define mdp_writel_dbg(mdp, val, reg) mdp_writel((mdp), (val), (reg)) +#endif + + static int send_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, - struct mdp_regs *regs, struct file *src_file, + struct ppp_regs *regs, struct file *src_file, struct file *dst_file) { - mdp_writel(mdp, 1, 0x060); - mdp_writel(mdp, regs->src_rect, PPP_ADDR_SRC_ROI); - mdp_writel(mdp, regs->src0, PPP_ADDR_SRC0); - mdp_writel(mdp, regs->src1, PPP_ADDR_SRC1); - mdp_writel(mdp, regs->src_ystride, PPP_ADDR_SRC_YSTRIDE); - mdp_writel(mdp, regs->src_cfg, PPP_ADDR_SRC_CFG); - mdp_writel(mdp, regs->src_pack, PPP_ADDR_SRC_PACK_PATTERN); - - mdp_writel(mdp, regs->op, PPP_ADDR_OPERATION); - mdp_writel(mdp, regs->phasex_init, PPP_ADDR_PHASEX_INIT); - mdp_writel(mdp, regs->phasey_init, PPP_ADDR_PHASEY_INIT); - mdp_writel(mdp, regs->phasex_step, PPP_ADDR_PHASEX_STEP); - mdp_writel(mdp, regs->phasey_step, PPP_ADDR_PHASEY_STEP); - - mdp_writel(mdp, (req->alpha << 24) | (req->transp_mask & 0xffffff), +#if 0 + mdp_writel_dbg(mdp, 1, MDP_PPP_CMD_MODE); +#endif + mdp_writel_dbg(mdp, regs->src_rect, PPP_ADDR_SRC_ROI); + mdp_writel_dbg(mdp, regs->src0, PPP_ADDR_SRC0); + mdp_writel_dbg(mdp, regs->src1, PPP_ADDR_SRC1); + mdp_writel_dbg(mdp, regs->src_ystride, PPP_ADDR_SRC_YSTRIDE); + mdp_writel_dbg(mdp, regs->src_cfg, PPP_ADDR_SRC_CFG); + mdp_writel_dbg(mdp, regs->src_pack, PPP_ADDR_SRC_PACK_PATTERN); + + mdp_writel_dbg(mdp, regs->op, PPP_ADDR_OPERATION); + mdp_writel_dbg(mdp, regs->phasex_init, PPP_ADDR_PHASEX_INIT); + mdp_writel_dbg(mdp, regs->phasey_init, PPP_ADDR_PHASEY_INIT); + mdp_writel_dbg(mdp, regs->phasex_step, PPP_ADDR_PHASEX_STEP); + mdp_writel_dbg(mdp, regs->phasey_step, PPP_ADDR_PHASEY_STEP); + +#ifdef CONFIG_MSM_MDP31 + mdp_writel_dbg(mdp, regs->scale_cfg, MDP_PPP_SCALE_CONFIG); + mdp_writel_dbg(mdp, regs->csc_cfg, MDP_PPP_CSC_CONFIG); + mdp_writel_dbg(mdp, regs->src_xy, MDP_PPP_SRC_XY); + mdp_writel_dbg(mdp, regs->src_img_sz, MDP_PPP_SRC_IMAGE_SIZE); + mdp_writel_dbg(mdp, regs->dst_xy, MDP_PPP_OUT_XY); +#else + /* no edge conditions to set for MDP 3.1 */ + mdp_writel_dbg(mdp, regs->edge, PPP_ADDR_EDGE); +#endif + + mdp_writel_dbg(mdp, (req->alpha << 24) | (req->transp_mask & 0xffffff), PPP_ADDR_ALPHA_TRANSP); - mdp_writel(mdp, regs->dst_cfg, PPP_ADDR_DST_CFG); - mdp_writel(mdp, regs->dst_pack, PPP_ADDR_DST_PACK_PATTERN); - mdp_writel(mdp, regs->dst_rect, PPP_ADDR_DST_ROI); - mdp_writel(mdp, regs->dst0, PPP_ADDR_DST0); - mdp_writel(mdp, regs->dst1, PPP_ADDR_DST1); - mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_DST_YSTRIDE); + mdp_writel_dbg(mdp, regs->dst_cfg, PPP_ADDR_DST_CFG); + mdp_writel_dbg(mdp, regs->dst_pack, PPP_ADDR_DST_PACK_PATTERN); + mdp_writel_dbg(mdp, regs->dst_rect, PPP_ADDR_DST_ROI); + mdp_writel_dbg(mdp, regs->dst0, PPP_ADDR_DST0); + mdp_writel_dbg(mdp, regs->dst1, PPP_ADDR_DST1); + mdp_writel_dbg(mdp, regs->dst_ystride, PPP_ADDR_DST_YSTRIDE); - mdp_writel(mdp, regs->edge, PPP_ADDR_EDGE); if (regs->op & PPP_OP_BLEND_ON) { - mdp_writel(mdp, regs->dst0, PPP_ADDR_BG0); - mdp_writel(mdp, regs->dst1, PPP_ADDR_BG1); - mdp_writel(mdp, regs->dst_ystride, PPP_ADDR_BG_YSTRIDE); - mdp_writel(mdp, src_img_cfg[req->dst.format], PPP_ADDR_BG_CFG); - mdp_writel(mdp, pack_pattern[req->dst.format], - PPP_ADDR_BG_PACK_PATTERN); + mdp_writel_dbg(mdp, regs->bg0, PPP_ADDR_BG0); + mdp_writel_dbg(mdp, regs->bg1, PPP_ADDR_BG1); + mdp_writel_dbg(mdp, regs->bg_ystride, PPP_ADDR_BG_YSTRIDE); + mdp_writel_dbg(mdp, regs->bg_cfg, PPP_ADDR_BG_CFG); + mdp_writel_dbg(mdp, regs->bg_pack, PPP_ADDR_BG_PACK_PATTERN); +#ifdef CONFIG_MSM_MDP31 + mdp_writel_dbg(mdp, regs->bg_xy, MDP_PPP_BG_XY); + mdp_writel_dbg(mdp, regs->bg_img_sz, MDP_PPP_BG_IMAGE_SIZE); + mdp_writel_dbg(mdp, regs->bg_alpha_sel, + MDP_PPP_BLEND_BG_ALPHA_SEL); +#endif } flush_imgs(req, regs, src_file, dst_file); - mdp_writel(mdp, 0x1000, MDP_DISPLAY0_START); + mdp_writel_dbg(mdp, 0x1000, MDP_DISPLAY0_START); return 0; } +#if PPP_DUMP_BLITS +static void mdp_dump_blit(struct mdp_blit_req *req) +{ + pr_info("%s: src: w=%d h=%d f=0x%x offs=0x%x mem_id=%d\n", __func__, + req->src.width, req->src.height, req->src.format, + req->src.offset, req->src.memory_id); + pr_info("%s: dst: w=%d h=%d f=0x%x offs=0x%x mem_id=%d\n", __func__, + req->dst.width, req->dst.height, req->dst.format, + req->dst.offset, req->dst.memory_id); + pr_info("%s: src_rect: x=%d y=%d w=%d h=%d\n", __func__, + req->src_rect.x, req->src_rect.y, req->src_rect.w, + req->src_rect.h); + pr_info("%s: dst_rect: x=%d y=%d w=%d h=%d\n", __func__, + req->dst_rect.x, req->dst_rect.y, req->dst_rect.w, + req->dst_rect.h); + pr_info("%s: alpha=0x%08x\n", __func__, req->alpha); + pr_info("%s: transp_max=0x%08x\n", __func__, req->transp_mask); + pr_info("%s: flags=%08x\n", __func__, req->flags); +} +#endif + int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, struct file *src_file, unsigned long src_start, unsigned long src_len, struct file *dst_file, unsigned long dst_start, unsigned long dst_len) { - struct mdp_regs regs = {0}; + struct ppp_regs regs = {0}; + uint32_t luma_base; + +#if PPP_DUMP_BLITS + mdp_dump_blit(req); +#endif if (unlikely(req->src.format >= MDP_IMGTYPE_LIMIT || req->dst.format >= MDP_IMGTYPE_LIMIT)) { - printk(KERN_ERR "mpd_ppp: img is of wrong format\n"); + printk(KERN_ERR "mdp_ppp: img is of wrong format\n"); return -EINVAL; } @@ -678,7 +545,15 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, req->src_rect.y > req->src.height || req->dst_rect.x > req->dst.width || req->dst_rect.y > req->dst.height)) { - printk(KERN_ERR "mpd_ppp: img rect is outside of img!\n"); + printk(KERN_ERR "mdp_ppp: img rect is outside of img!\n"); + return -EINVAL; + } + + if (unlikely(req->src_rect.x + req->src_rect.w > req->src.width || + req->src_rect.y + req->src_rect.h > req->src.height || + req->dst_rect.x + req->dst_rect.w > req->dst.width || + req->dst_rect.y + req->dst_rect.h > req->dst.height)) { + printk(KERN_ERR "mdp_ppp: img rect extends outside of img!\n"); return -EINVAL; } @@ -686,35 +561,41 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, regs.src_cfg = src_img_cfg[req->src.format]; regs.src_cfg |= (req->src_rect.x & 0x1) ? PPP_SRC_BPP_ROI_ODD_X : 0; regs.src_cfg |= (req->src_rect.y & 0x1) ? PPP_SRC_BPP_ROI_ODD_Y : 0; - regs.src_rect = (req->src_rect.h << 16) | req->src_rect.w; regs.src_pack = pack_pattern[req->src.format]; /* set the dest image configuration */ regs.dst_cfg = dst_img_cfg[req->dst.format] | PPP_DST_OUT_SEL_AXI; - regs.dst_rect = (req->dst_rect.h << 16) | req->dst_rect.w; regs.dst_pack = pack_pattern[req->dst.format]; /* set src, bpp, start pixel and ystride */ regs.src_bpp = bytes_per_pixel[req->src.format]; - regs.src0 = src_start + req->src.offset; + luma_base = src_start + req->src.offset; + regs.src0 = luma_base + + get_luma_offset(&req->src, &req->src_rect, regs.src_bpp); + regs.src1 = get_chroma_base(&req->src, luma_base, regs.src_bpp); + regs.src1 += get_chroma_offset(&req->src, &req->src_rect, regs.src_bpp); regs.src_ystride = req->src.width * regs.src_bpp; - get_chroma_addr(&req->src, &req->src_rect, regs.src0, regs.src_bpp, - regs.src_cfg, ®s.src1, ®s.src_ystride); - regs.src0 += (req->src_rect.x + (req->src_rect.y * req->src.width)) * - regs.src_bpp; + set_src_region(&req->src, &req->src_rect, ®s); /* set dst, bpp, start pixel and ystride */ regs.dst_bpp = bytes_per_pixel[req->dst.format]; - regs.dst0 = dst_start + req->dst.offset; + luma_base = dst_start + req->dst.offset; + regs.dst0 = luma_base + + get_luma_offset(&req->dst, &req->dst_rect, regs.dst_bpp); + regs.dst1 = get_chroma_base(&req->dst, luma_base, regs.dst_bpp); + regs.dst1 += get_chroma_offset(&req->dst, &req->dst_rect, regs.dst_bpp); regs.dst_ystride = req->dst.width * regs.dst_bpp; - get_chroma_addr(&req->dst, &req->dst_rect, regs.dst0, regs.dst_bpp, - regs.dst_cfg, ®s.dst1, ®s.dst_ystride); - regs.dst0 += (req->dst_rect.x + (req->dst_rect.y * req->dst.width)) * - regs.dst_bpp; + set_dst_region(&req->dst_rect, ®s); + + /* for simplicity, always write the chroma stride */ + regs.src_ystride &= 0x3fff; + regs.src_ystride |= regs.src_ystride << 16; + regs.dst_ystride &= 0x3fff; + regs.dst_ystride |= regs.dst_ystride << 16; if (!valid_src_dst(src_start, src_len, dst_start, dst_len, req, ®s)) { - printk(KERN_ERR "mpd_ppp: final src or dst location is " + printk(KERN_ERR "mdp_ppp: final src or dst location is " "invalid, are you trying to make an image too large " "or to place it outside the screen?\n"); return -EINVAL; @@ -728,7 +609,7 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, regs.op |= PPP_OP_DITHER_EN; blit_blend(req, ®s); if (blit_scale(mdp, req, ®s)) { - printk(KERN_ERR "mpd_ppp: error computing scale for img.\n"); + printk(KERN_ERR "mdp_ppp: error computing scale for img.\n"); return -EINVAL; } blit_blur(mdp, req, ®s); @@ -742,9 +623,31 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, req->dst_rect.x = req->dst_rect.x & (~0x1); req->dst_rect.w = req->dst_rect.w & (~0x1); } - if (get_edge_cond(req, ®s)) + + if (mdp_ppp_cfg_edge_cond(req, ®s)) return -EINVAL; +#if PPP_DUMP_BLITS + pr_info("%s: sending blit\n", __func__); +#endif send_blit(mdp, req, ®s, src_file, dst_file); return 0; } + + +#define mdp_dump_register(mdp, reg) \ + printk(# reg ": %08x\n", mdp_readl((mdp), (reg))) + +void mdp_ppp_dump_debug(const struct mdp_info *mdp) +{ + mdp_dump_register(mdp, MDP_TFETCH_STATUS); + mdp_dump_register(mdp, MDP_TFETCH_TILE_COUNT); + mdp_dump_register(mdp, MDP_TFETCH_FETCH_COUNT); + mdp_dump_register(mdp, MDP_BGTFETCH_STATUS); + mdp_dump_register(mdp, MDP_BGTFETCH_TILE_COUNT); + mdp_dump_register(mdp, MDP_BGTFETCH_FETCH_COUNT); + mdp_dump_register(mdp, MDP_PPP_SCALE_STATUS); + mdp_dump_register(mdp, MDP_PPP_BLEND_STATUS); + mdp_dump_register(mdp, MDP_INTR_STATUS); + mdp_dump_register(mdp, MDP_INTR_ENABLE); +} diff --git a/drivers/video/msm/mdp_ppp.h b/drivers/video/msm/mdp_ppp.h new file mode 100644 index 0000000000000..e04564347bdfe --- /dev/null +++ b/drivers/video/msm/mdp_ppp.h @@ -0,0 +1,82 @@ +/* drivers/video/msm/mdp_ppp.h + * + * Copyright (C) 2009 Google Incorporated + * + * 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 _VIDEO_MSM_MDP_PPP_H_ +#define _VIDEO_MSM_MDP_PPP_H_ + +#include + +struct ppp_regs { + uint32_t src0; + uint32_t src1; + uint32_t dst0; + uint32_t dst1; + uint32_t src_cfg; + uint32_t dst_cfg; + uint32_t src_pack; + uint32_t dst_pack; + uint32_t src_rect; + uint32_t dst_rect; + uint32_t src_ystride; + uint32_t dst_ystride; + uint32_t op; + uint32_t src_bpp; + uint32_t dst_bpp; + uint32_t edge; + uint32_t phasex_init; + uint32_t phasey_init; + uint32_t phasex_step; + uint32_t phasey_step; + + uint32_t bg0; + uint32_t bg1; + uint32_t bg_cfg; + uint32_t bg_bpp; + uint32_t bg_pack; + uint32_t bg_ystride; + +#ifdef CONFIG_MSM_MDP31 + uint32_t src_xy; + uint32_t src_img_sz; + uint32_t dst_xy; + uint32_t bg_xy; + uint32_t bg_img_sz; + uint32_t bg_alpha_sel; + + uint32_t scale_cfg; + uint32_t csc_cfg; +#endif +}; + +struct mdp_info; +struct mdp_rect; +struct mdp_blit_req; + +void mdp_ppp_init_scale(const struct mdp_info *mdp); +int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs, + struct mdp_rect *src_rect, struct mdp_rect *dst_rect, + uint32_t src_format, uint32_t dst_format); +int mdp_ppp_load_blur(const struct mdp_info *mdp); + +#ifndef CONFIG_MSM_MDP31 +int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs); +#else +static inline int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, + struct ppp_regs *regs) +{ + return 0; +} +#endif + +#endif /* _VIDEO_MSM_MDP_PPP_H_ */ diff --git a/drivers/video/msm/mdp_scale_tables.c b/drivers/video/msm/mdp_ppp22.c similarity index 69% rename from drivers/video/msm/mdp_scale_tables.c rename to drivers/video/msm/mdp_ppp22.c index 604783b2e17c9..02fca9a3d11a8 100644 --- a/drivers/video/msm/mdp_scale_tables.c +++ b/drivers/video/msm/mdp_ppp22.c @@ -1,4 +1,4 @@ -/* drivers/video/msm_fb/mdp_scale_tables.c +/* drivers/video/msm/mdp_ppp22.c * * Copyright (C) 2007 QUALCOMM Incorporated * Copyright (C) 2007 Google Incorporated @@ -13,10 +13,33 @@ * GNU General Public License for more details. */ -#include "mdp_scale_tables.h" +#include +#include +#include + #include "mdp_hw.h" +#include "mdp_ppp.h" + +struct mdp_table_entry { + uint32_t reg; + uint32_t val; +}; + +enum { + MDP_DOWNSCALE_PT2TOPT4, + MDP_DOWNSCALE_PT4TOPT6, + MDP_DOWNSCALE_PT6TOPT8, + MDP_DOWNSCALE_PT8TO1, + MDP_DOWNSCALE_MAX, + + /* not technically in the downscale table list */ + MDP_DOWNSCALE_BLUR, +}; -struct mdp_table_entry mdp_upscale_table[] = { +static int downscale_x_table; +static int downscale_y_table; + +static struct mdp_table_entry mdp_upscale_table[] = { { 0x5fffc, 0x0 }, { 0x50200, 0x7fc00000 }, { 0x5fffc, 0xff80000d }, @@ -764,3 +787,305 @@ struct mdp_table_entry mdp_gaussian_blur_table[] = { { 0x5fffc, 0x20000080 }, { 0x5037c, 0x20000080 }, }; + +static void load_table(const struct mdp_info *mdp, + struct mdp_table_entry *table, int len) +{ + int i; + for (i = 0; i < len; i++) + mdp_writel(mdp, table[i].val, table[i].reg); +} + +enum { + IMG_LEFT, + IMG_RIGHT, + IMG_TOP, + IMG_BOTTOM, +}; + +static void get_edge_info(uint32_t src, uint32_t src_coord, uint32_t dst, + uint32_t *interp1, uint32_t *interp2, + uint32_t *repeat1, uint32_t *repeat2) { + if (src > 3 * dst) { + *interp1 = 0; + *interp2 = src - 1; + *repeat1 = 0; + *repeat2 = 0; + } else if (src == 3 * dst) { + *interp1 = 0; + *interp2 = src; + *repeat1 = 0; + *repeat2 = 1; + } else if (src > dst && src < 3 * dst) { + *interp1 = -1; + *interp2 = src; + *repeat1 = 1; + *repeat2 = 1; + } else if (src == dst) { + *interp1 = -1; + *interp2 = src + 1; + *repeat1 = 1; + *repeat2 = 2; + } else { + *interp1 = -2; + *interp2 = src + 1; + *repeat1 = 2; + *repeat2 = 2; + } + *interp1 += src_coord; + *interp2 += src_coord; +} + +int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs) +{ + int32_t luma_interp[4]; + int32_t luma_repeat[4]; + int32_t chroma_interp[4]; + int32_t chroma_bound[4]; + int32_t chroma_repeat[4]; + uint32_t dst_w, dst_h; + + memset(&luma_interp, 0, sizeof(int32_t) * 4); + memset(&luma_repeat, 0, sizeof(int32_t) * 4); + memset(&chroma_interp, 0, sizeof(int32_t) * 4); + memset(&chroma_bound, 0, sizeof(int32_t) * 4); + memset(&chroma_repeat, 0, sizeof(int32_t) * 4); + regs->edge = 0; + + if (req->flags & MDP_ROT_90) { + dst_w = req->dst_rect.h; + dst_h = req->dst_rect.w; + } else { + dst_w = req->dst_rect.w; + dst_h = req->dst_rect.h; + } + + if (regs->op & (PPP_OP_SCALE_Y_ON | PPP_OP_SCALE_X_ON)) { + get_edge_info(req->src_rect.h, req->src_rect.y, dst_h, + &luma_interp[IMG_TOP], &luma_interp[IMG_BOTTOM], + &luma_repeat[IMG_TOP], &luma_repeat[IMG_BOTTOM]); + get_edge_info(req->src_rect.w, req->src_rect.x, dst_w, + &luma_interp[IMG_LEFT], &luma_interp[IMG_RIGHT], + &luma_repeat[IMG_LEFT], &luma_repeat[IMG_RIGHT]); + } else { + luma_interp[IMG_LEFT] = req->src_rect.x; + luma_interp[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1; + luma_interp[IMG_TOP] = req->src_rect.y; + luma_interp[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1; + luma_repeat[IMG_LEFT] = 0; + luma_repeat[IMG_TOP] = 0; + luma_repeat[IMG_RIGHT] = 0; + luma_repeat[IMG_BOTTOM] = 0; + } + + chroma_interp[IMG_LEFT] = luma_interp[IMG_LEFT]; + chroma_interp[IMG_RIGHT] = luma_interp[IMG_RIGHT]; + chroma_interp[IMG_TOP] = luma_interp[IMG_TOP]; + chroma_interp[IMG_BOTTOM] = luma_interp[IMG_BOTTOM]; + + chroma_bound[IMG_LEFT] = req->src_rect.x; + chroma_bound[IMG_RIGHT] = req->src_rect.x + req->src_rect.w - 1; + chroma_bound[IMG_TOP] = req->src_rect.y; + chroma_bound[IMG_BOTTOM] = req->src_rect.y + req->src_rect.h - 1; + + if (IS_YCRCB(req->src.format)) { + chroma_interp[IMG_LEFT] = chroma_interp[IMG_LEFT] >> 1; + chroma_interp[IMG_RIGHT] = (chroma_interp[IMG_RIGHT] + 1) >> 1; + + chroma_bound[IMG_LEFT] = chroma_bound[IMG_LEFT] >> 1; + chroma_bound[IMG_RIGHT] = chroma_bound[IMG_RIGHT] >> 1; + } + + if (req->src.format == MDP_Y_CBCR_H2V2 || + req->src.format == MDP_Y_CRCB_H2V2) { + chroma_interp[IMG_TOP] = (chroma_interp[IMG_TOP] - 1) >> 1; + chroma_interp[IMG_BOTTOM] = (chroma_interp[IMG_BOTTOM] + 1) + >> 1; + chroma_bound[IMG_TOP] = (chroma_bound[IMG_TOP] + 1) >> 1; + chroma_bound[IMG_BOTTOM] = chroma_bound[IMG_BOTTOM] >> 1; + } + + chroma_repeat[IMG_LEFT] = chroma_bound[IMG_LEFT] - + chroma_interp[IMG_LEFT]; + chroma_repeat[IMG_RIGHT] = chroma_interp[IMG_RIGHT] - + chroma_bound[IMG_RIGHT]; + chroma_repeat[IMG_TOP] = chroma_bound[IMG_TOP] - + chroma_interp[IMG_TOP]; + chroma_repeat[IMG_BOTTOM] = chroma_interp[IMG_BOTTOM] - + chroma_bound[IMG_BOTTOM]; + + if (chroma_repeat[IMG_LEFT] < 0 || chroma_repeat[IMG_LEFT] > 3 || + chroma_repeat[IMG_RIGHT] < 0 || chroma_repeat[IMG_RIGHT] > 3 || + chroma_repeat[IMG_TOP] < 0 || chroma_repeat[IMG_TOP] > 3 || + chroma_repeat[IMG_BOTTOM] < 0 || chroma_repeat[IMG_BOTTOM] > 3 || + luma_repeat[IMG_LEFT] < 0 || luma_repeat[IMG_LEFT] > 3 || + luma_repeat[IMG_RIGHT] < 0 || luma_repeat[IMG_RIGHT] > 3 || + luma_repeat[IMG_TOP] < 0 || luma_repeat[IMG_TOP] > 3 || + luma_repeat[IMG_BOTTOM] < 0 || luma_repeat[IMG_BOTTOM] > 3) + return -1; + + regs->edge |= (chroma_repeat[IMG_LEFT] & 3) << MDP_LEFT_CHROMA; + regs->edge |= (chroma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_CHROMA; + regs->edge |= (chroma_repeat[IMG_TOP] & 3) << MDP_TOP_CHROMA; + regs->edge |= (chroma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_CHROMA; + regs->edge |= (luma_repeat[IMG_LEFT] & 3) << MDP_LEFT_LUMA; + regs->edge |= (luma_repeat[IMG_RIGHT] & 3) << MDP_RIGHT_LUMA; + regs->edge |= (luma_repeat[IMG_TOP] & 3) << MDP_TOP_LUMA; + regs->edge |= (luma_repeat[IMG_BOTTOM] & 3) << MDP_BOTTOM_LUMA; + return 0; +} + +#define ONE_HALF (1LL << 32) +#define ONE (1LL << 33) +#define TWO (2LL << 33) +#define THREE (3LL << 33) +#define FRAC_MASK (ONE - 1) +#define INT_MASK (~FRAC_MASK) + +static int scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t origin, + uint32_t *phase_init, uint32_t *phase_step) +{ + /* to improve precicsion calculations are done in U31.33 and converted + * to U3.29 at the end */ + int64_t k1, k2, k3, k4, tmp; + uint64_t n, d, os, os_p, od, od_p, oreq; + unsigned rpa = 0; + int64_t ip64, delta; + + if (dim_out % 3 == 0) + rpa = !(dim_in % (dim_out / 3)); + + n = ((uint64_t)dim_out) << 34; + d = dim_in; + if (!d) + return -1; + do_div(n, d); + k3 = (n + 1) >> 1; + if ((k3 >> 4) < (1LL << 27) || (k3 >> 4) > (1LL << 31)) + return -1; + + n = ((uint64_t)dim_in) << 34; + d = (uint64_t)dim_out; + if (!d) + return -1; + do_div(n, d); + k1 = (n + 1) >> 1; + k2 = (k1 - ONE) >> 1; + + *phase_init = (int)(k2 >> 4); + k4 = (k3 - ONE) >> 1; + + if (rpa) { + os = ((uint64_t)origin << 33) - ONE_HALF; + tmp = (dim_out * os) + ONE_HALF; + if (!dim_in) + return -1; + do_div(tmp, dim_in); + od = tmp - ONE_HALF; + } else { + os = ((uint64_t)origin << 1) - 1; + od = (((k3 * os) >> 1) + k4); + } + + od_p = od & INT_MASK; + if (od_p != od) + od_p += ONE; + + if (rpa) { + tmp = (dim_in * od_p) + ONE_HALF; + if (!dim_in) + return -1; + do_div(tmp, dim_in); + os_p = tmp - ONE_HALF; + } else { + os_p = ((k1 * (od_p >> 33)) + k2); + } + + oreq = (os_p & INT_MASK) - ONE; + + ip64 = os_p - oreq; + delta = ((int64_t)(origin) << 33) - oreq; + ip64 -= delta; + /* limit to valid range before the left shift */ + delta = (ip64 & (1LL << 63)) ? 4 : -4; + delta <<= 33; + while (abs((int)(ip64 >> 33)) > 4) + ip64 += delta; + *phase_init = (int)(ip64 >> 4); + *phase_step = (uint32_t)(k1 >> 4); + return 0; +} + +int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs, + struct mdp_rect *src_rect, struct mdp_rect *dst_rect, + uint32_t src_format, uint32_t dst_format) +{ + int downscale; + uint32_t phase_init_x, phase_init_y, phase_step_x, phase_step_y; + uint32_t scale_factor_x, scale_factor_y; + + if (scale_params(src_rect->w, dst_rect->w, 1, &phase_init_x, + &phase_step_x) || + scale_params(src_rect->h, dst_rect->h, 1, &phase_init_y, + &phase_step_y)) + return -1; + + regs->phasex_init = phase_init_x; + regs->phasey_init = phase_init_y; + regs->phasex_step = phase_step_x; + regs->phasey_step = phase_step_y; + + scale_factor_x = (dst_rect->w * 10) / src_rect->w; + scale_factor_y = (dst_rect->h * 10) / src_rect->h; + + if (scale_factor_x > 8) + downscale = MDP_DOWNSCALE_PT8TO1; + else if (scale_factor_x > 6) + downscale = MDP_DOWNSCALE_PT6TOPT8; + else if (scale_factor_x > 4) + downscale = MDP_DOWNSCALE_PT4TOPT6; + else + downscale = MDP_DOWNSCALE_PT2TOPT4; + + if (downscale != downscale_x_table) { + load_table(mdp, mdp_downscale_x_table[downscale], 64); + downscale_x_table = downscale; + } + + if (scale_factor_y > 8) + downscale = MDP_DOWNSCALE_PT8TO1; + else if (scale_factor_y > 6) + downscale = MDP_DOWNSCALE_PT6TOPT8; + else if (scale_factor_y > 4) + downscale = MDP_DOWNSCALE_PT4TOPT6; + else + downscale = MDP_DOWNSCALE_PT2TOPT4; + + if (downscale != downscale_y_table) { + load_table(mdp, mdp_downscale_y_table[downscale], 64); + downscale_y_table = downscale; + } + + return 0; +} + + +int mdp_ppp_load_blur(const struct mdp_info *mdp) +{ + if (!(downscale_x_table == MDP_DOWNSCALE_BLUR && + downscale_y_table == MDP_DOWNSCALE_BLUR)) { + load_table(mdp, mdp_gaussian_blur_table, 128); + downscale_x_table = MDP_DOWNSCALE_BLUR; + downscale_y_table = MDP_DOWNSCALE_BLUR; + } + + return 0; +} + +void mdp_ppp_init_scale(const struct mdp_info *mdp) +{ + downscale_x_table = MDP_DOWNSCALE_MAX; + downscale_y_table = MDP_DOWNSCALE_MAX; + + load_table(mdp, mdp_upscale_table, ARRAY_SIZE(mdp_upscale_table)); +} diff --git a/drivers/video/msm/mdp_ppp31.c b/drivers/video/msm/mdp_ppp31.c new file mode 100644 index 0000000000000..0a2833cb1cdd6 --- /dev/null +++ b/drivers/video/msm/mdp_ppp31.c @@ -0,0 +1,332 @@ +/* drivers/video/msm/mdp_ppp31.c + * + * Copyright (C) 2009 QUALCOMM Incorporated + * Copyright (C) 2009 Google Incorporated + * + * 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 "mdp_hw.h" +#include "mdp_ppp.h" + +#define NUM_COEFFS 32 + +struct mdp_scale_coeffs { + uint16_t c[4][NUM_COEFFS]; +}; + +struct mdp_scale_tbl_info { + uint16_t offset; + uint32_t set:2; + int use_pr; + struct mdp_scale_coeffs coeffs; +}; + +enum { + MDP_SCALE_PT2TOPT4, + MDP_SCALE_PT4TOPT6, + MDP_SCALE_PT6TOPT8, + MDP_SCALE_PT8TO8, + MDP_SCALE_MAX, +}; + +static struct mdp_scale_coeffs mdp_scale_pr_coeffs = { + .c = { + [0] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + [1] = { + 511, 511, 511, 511, 511, 511, 511, 511, + 511, 511, 511, 511, 511, 511, 511, 511, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + [2] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 511, 511, 511, 511, 511, 511, 511, 511, + 511, 511, 511, 511, 511, 511, 511, 511, + }, + [3] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, + }, +}; + +static struct mdp_scale_tbl_info mdp_scale_tbl[MDP_SCALE_MAX] = { + [ MDP_SCALE_PT2TOPT4 ] = { + .offset = 0, + .set = MDP_PPP_SCALE_COEFF_D0_SET, + .use_pr = -1, + .coeffs.c = { + [0] = { + 131, 131, 130, 129, 128, 127, 127, 126, + 125, 125, 124, 123, 123, 121, 120, 119, + 119, 118, 117, 117, 116, 115, 115, 114, + 113, 112, 111, 110, 109, 109, 108, 107, + }, + [1] = { + 141, 140, 140, 140, 140, 139, 138, 138, + 138, 137, 137, 137, 136, 137, 137, 137, + 136, 136, 136, 135, 135, 135, 134, 134, + 134, 134, 134, 133, 133, 132, 132, 132, + }, + [2] = { + 132, 132, 132, 133, 133, 134, 134, 134, + 134, 134, 135, 135, 135, 136, 136, 136, + 137, 137, 137, 136, 137, 137, 137, 138, + 138, 138, 139, 140, 140, 140, 140, 141, + }, + [3] = { + 107, 108, 109, 109, 110, 111, 112, 113, + 114, 115, 115, 116, 117, 117, 118, 119, + 119, 120, 121, 123, 123, 124, 125, 125, + 126, 127, 127, 128, 129, 130, 131, 131, + } + }, + }, + [ MDP_SCALE_PT4TOPT6 ] = { + .offset = 32, + .set = MDP_PPP_SCALE_COEFF_D1_SET, + .use_pr = -1, + .coeffs.c = { + [0] = { + 136, 132, 128, 123, 119, 115, 111, 107, + 103, 98, 95, 91, 87, 84, 80, 76, + 73, 69, 66, 62, 59, 57, 54, 50, + 47, 44, 41, 39, 36, 33, 32, 29, + }, + [1] = { + 206, 205, 204, 204, 201, 200, 199, 197, + 196, 194, 191, 191, 189, 185, 184, 182, + 180, 178, 176, 173, 170, 168, 165, 162, + 160, 157, 155, 152, 148, 146, 142, 140, + }, + [2] = { + 140, 142, 146, 148, 152, 155, 157, 160, + 162, 165, 168, 170, 173, 176, 178, 180, + 182, 184, 185, 189, 191, 191, 194, 196, + 197, 199, 200, 201, 204, 204, 205, 206, + }, + [3] = { + 29, 32, 33, 36, 39, 41, 44, 47, + 50, 54, 57, 59, 62, 66, 69, 73, + 76, 80, 84, 87, 91, 95, 98, 103, + 107, 111, 115, 119, 123, 128, 132, 136, + }, + }, + }, + [ MDP_SCALE_PT6TOPT8 ] = { + .offset = 64, + .set = MDP_PPP_SCALE_COEFF_D2_SET, + .use_pr = -1, + .coeffs.c = { + [0] = { + 104, 96, 89, 82, 75, 68, 61, 55, + 49, 43, 38, 33, 28, 24, 20, 16, + 12, 9, 6, 4, 2, 0, -2, -4, + -5, -6, -7, -7, -8, -8, -8, -8, + }, + [1] = { + 303, 303, 302, 300, 298, 296, 293, 289, + 286, 281, 276, 270, 265, 258, 252, 245, + 238, 230, 223, 214, 206, 197, 189, 180, + 172, 163, 154, 145, 137, 128, 120, 112, + }, + [2] = { + 112, 120, 128, 137, 145, 154, 163, 172, + 180, 189, 197, 206, 214, 223, 230, 238, + 245, 252, 258, 265, 270, 276, 281, 286, + 289, 293, 296, 298, 300, 302, 303, 303, + }, + [3] = { + -8, -8, -8, -8, -7, -7, -6, -5, + -4, -2, 0, 2, 4, 6, 9, 12, + 16, 20, 24, 28, 33, 38, 43, 49, + 55, 61, 68, 75, 82, 89, 96, 104, + }, + }, + }, + [ MDP_SCALE_PT8TO8 ] = { + .offset = 96, + .set = MDP_PPP_SCALE_COEFF_U1_SET, + .use_pr = -1, + .coeffs.c = { + [0] = { + 0, -7, -13, -19, -24, -28, -32, -34, + -37, -39, -40, -41, -41, -41, -40, -40, + -38, -37, -35, -33, -31, -29, -26, -24, + -21, -18, -15, -13, -10, -7, -5, -2, + }, + [1] = { + 511, 507, 501, 494, 485, 475, 463, 450, + 436, 422, 405, 388, 370, 352, 333, 314, + 293, 274, 253, 233, 213, 193, 172, 152, + 133, 113, 95, 77, 60, 43, 28, 13, + }, + [2] = { + 0, 13, 28, 43, 60, 77, 95, 113, + 133, 152, 172, 193, 213, 233, 253, 274, + 294, 314, 333, 352, 370, 388, 405, 422, + 436, 450, 463, 475, 485, 494, 501, 507, + }, + [3] = { + 0, -2, -5, -7, -10, -13, -15, -18, + -21, -24, -26, -29, -31, -33, -35, -37, + -38, -40, -40, -41, -41, -41, -40, -39, + -37, -34, -32, -28, -24, -19, -13, -7, + }, + }, + }, +}; + +static void load_table(const struct mdp_info *mdp, int scale, int use_pr) +{ + int i; + uint32_t val; + struct mdp_scale_coeffs *coeffs; + struct mdp_scale_tbl_info *tbl = &mdp_scale_tbl[scale]; + + if (use_pr == tbl->use_pr) + return; + + tbl->use_pr = use_pr; + if (!use_pr) + coeffs = &tbl->coeffs; + else + coeffs = &mdp_scale_pr_coeffs; + + for (i = 0; i < NUM_COEFFS; ++i) { + val = ((coeffs->c[1][i] & 0x3ff) << 16) | + (coeffs->c[0][i] & 0x3ff); + mdp_writel(mdp, val, MDP_PPP_SCALE_COEFF_LSBn(tbl->offset + i)); + + val = ((coeffs->c[3][i] & 0x3ff) << 16) | + (coeffs->c[2][i] & 0x3ff); + mdp_writel(mdp, val, MDP_PPP_SCALE_COEFF_MSBn(tbl->offset + i)); + } +} + +#define SCALER_PHASE_BITS 29 +static void scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t scaler, + uint32_t *phase_init, uint32_t *phase_step) +{ + uint64_t src = dim_in; + uint64_t dst = dim_out; + uint64_t numer; + uint64_t denom; + + *phase_init = 0; + + if (dst == 1) { + /* if destination is 1 pixel wide, the value of phase_step + * is unimportant. */ + *phase_step = (uint32_t) (src << SCALER_PHASE_BITS); + if (scaler == MDP_PPP_SCALER_FIR) + *phase_init = + (uint32_t) ((src - 1) << SCALER_PHASE_BITS); + return; + } + + if (scaler == MDP_PPP_SCALER_FIR) { + numer = (src - 1) << SCALER_PHASE_BITS; + denom = dst - 1; + /* we want to round up the result*/ + numer += denom - 1; + } else { + numer = src << SCALER_PHASE_BITS; + denom = dst; + } + + do_div(numer, denom); + *phase_step = (uint32_t) numer; +} + +static int scale_idx(int factor) +{ + int idx; + + if (factor > 80) + idx = MDP_SCALE_PT8TO8; + else if (factor > 60) + idx = MDP_SCALE_PT6TOPT8; + else if (factor > 40) + idx = MDP_SCALE_PT4TOPT6; + else + idx = MDP_SCALE_PT2TOPT4; + + return idx; +} + +int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs, + struct mdp_rect *src_rect, struct mdp_rect *dst_rect, + uint32_t src_format, uint32_t dst_format) +{ + uint32_t x_fac; + uint32_t y_fac; + uint32_t scaler_x = MDP_PPP_SCALER_FIR; + uint32_t scaler_y = MDP_PPP_SCALER_FIR; + // Don't use pixel repeat mode, it looks bad + int use_pr = 0; + int x_idx; + int y_idx; + + if (unlikely(src_rect->w > 2048 || src_rect->h > 2048)) + return -ENOTSUPP; + + x_fac = (dst_rect->w * 100) / src_rect->w; + y_fac = (dst_rect->h * 100) / src_rect->h; + + /* if down-scaling by a factor smaller than 1/4, use M/N */ + scaler_x = x_fac <= 25 ? MDP_PPP_SCALER_MN : MDP_PPP_SCALER_FIR; + scaler_y = y_fac <= 25 ? MDP_PPP_SCALER_MN : MDP_PPP_SCALER_FIR; + scale_params(src_rect->w, dst_rect->w, scaler_x, ®s->phasex_init, + ®s->phasex_step); + scale_params(src_rect->h, dst_rect->h, scaler_y, ®s->phasey_init, + ®s->phasey_step); + + x_idx = scale_idx(x_fac); + y_idx = scale_idx(y_fac); + load_table(mdp, x_idx, use_pr); + load_table(mdp, y_idx, use_pr); + + regs->scale_cfg = 0; + // Enable SVI when source or destination is YUV + if (!IS_RGB(src_format) && !IS_RGB(dst_format)) + regs->scale_cfg |= (1 << 6); + regs->scale_cfg |= (mdp_scale_tbl[x_idx].set << 2) | + (mdp_scale_tbl[x_idx].set << 4); + regs->scale_cfg |= (scaler_x << 0) | (scaler_y << 1); + + return 0; +} + +int mdp_ppp_load_blur(const struct mdp_info *mdp) +{ + return -ENOTSUPP; +} + +void mdp_ppp_init_scale(const struct mdp_info *mdp) +{ + int scale; + for (scale = 0; scale < MDP_SCALE_MAX; ++scale) + load_table(mdp, scale, 0); +} diff --git a/drivers/video/msm/mdp_scale_tables.h b/drivers/video/msm/mdp_scale_tables.h deleted file mode 100644 index 34077b1af6039..0000000000000 --- a/drivers/video/msm/mdp_scale_tables.h +++ /dev/null @@ -1,38 +0,0 @@ -/* drivers/video/msm_fb/mdp_scale_tables.h - * - * Copyright (C) 2007 QUALCOMM Incorporated - * Copyright (C) 2007 Google Incorporated - * - * 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 _MDP_SCALE_TABLES_H_ -#define _MDP_SCALE_TABLES_H_ - -#include -struct mdp_table_entry { - uint32_t reg; - uint32_t val; -}; - -extern struct mdp_table_entry mdp_upscale_table[64]; - -enum { - MDP_DOWNSCALE_PT2TOPT4, - MDP_DOWNSCALE_PT4TOPT6, - MDP_DOWNSCALE_PT6TOPT8, - MDP_DOWNSCALE_PT8TO1, - MDP_DOWNSCALE_MAX, -}; - -extern struct mdp_table_entry *mdp_downscale_x_table[MDP_DOWNSCALE_MAX]; -extern struct mdp_table_entry *mdp_downscale_y_table[MDP_DOWNSCALE_MAX]; -extern struct mdp_table_entry mdp_gaussian_blur_table[]; - -#endif diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index 64dc0de8464ac..76f2c9eba6148 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -61,6 +61,9 @@ do { \ printk(KERN_INFO "msmfb: "fmt, ##args); \ } while (0) +#define BITS_PER_PIXEL(info) (info->fb->var.bits_per_pixel) +#define BYTES_PER_PIXEL(info) (info->fb->var.bits_per_pixel >> 3) + static int msmfb_debug_mask; module_param_named(msmfb_debug_mask, msmfb_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); @@ -191,9 +194,10 @@ static int msmfb_start_dma(struct msmfb_info *msmfb) } spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); - addr = ((msmfb->xres * (yoffset + y) + x) * 2); + addr = ((msmfb->xres * (yoffset + y) + x) * BYTES_PER_PIXEL(msmfb)); mdp->dma(mdp, addr + msmfb->fb->fix.smem_start, - msmfb->xres * 2, w, h, x, y, &msmfb->dma_callback, + msmfb->xres * BYTES_PER_PIXEL(msmfb), w, h, x, y, + &msmfb->dma_callback, panel->interface_type); return 0; error: @@ -397,7 +401,7 @@ static void msmfb_earlier_suspend(struct early_suspend *h) mdp->dma(mdp, virt_to_phys(msmfb->black), 0, msmfb->fb->var.xres, msmfb->fb->var.yres, 0, 0, NULL, panel->interface_type); - mdp->dma_wait(mdp); + mdp->dma_wait(mdp, panel->interface_type); /* turn off the panel */ panel->blank(panel); @@ -435,14 +439,47 @@ static void msmfb_resume(struct early_suspend *h) static int msmfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { + u32 size; + if ((var->xres != info->var.xres) || (var->yres != info->var.yres) || - (var->xres_virtual != info->var.xres_virtual) || - (var->yres_virtual != info->var.yres_virtual) || (var->xoffset != info->var.xoffset) || - (var->bits_per_pixel != info->var.bits_per_pixel) || + (mdp->check_output_format(mdp, var->bits_per_pixel)) || (var->grayscale != info->var.grayscale)) return -EINVAL; + + size = var->xres_virtual * var->yres_virtual * + (var->bits_per_pixel >> 3); + if (size > info->fix.smem_len) + return -EINVAL; + return 0; +} + +static int msmfb_set_par(struct fb_info *info) +{ + struct fb_var_screeninfo *var = &info->var; + struct fb_fix_screeninfo *fix = &info->fix; + + /* we only support RGB ordering for now */ + if (var->bits_per_pixel == 32 || var->bits_per_pixel == 24) { + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 16; + var->blue.length = 8; + } else if (var->bits_per_pixel == 16) { + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + } else + return -1; + mdp->set_output_format(mdp, var->bits_per_pixel); + fix->line_length = var->xres * var->bits_per_pixel / 8; + return 0; } @@ -557,6 +594,7 @@ static struct fb_ops msmfb_ops = { .fb_open = msmfb_open, .fb_release = msmfb_release, .fb_check_var = msmfb_check_var, + .fb_set_par = msmfb_set_par, .fb_pan_display = msmfb_pan_display, .fb_fillrect = msmfb_fillrect, .fb_copyarea = msmfb_copyarea, @@ -606,8 +644,6 @@ static struct file_operations debug_fops = { }; #endif -#define BITS_PER_PIXEL 16 - static void setup_fb_info(struct msmfb_info *msmfb) { struct fb_info *fb_info = msmfb->fb; @@ -623,14 +659,13 @@ static void setup_fb_info(struct msmfb_info *msmfb) fb_info->fix.type = FB_TYPE_PACKED_PIXELS; fb_info->fix.visual = FB_VISUAL_TRUECOLOR; fb_info->fix.line_length = msmfb->xres * 2; - fb_info->var.xres = msmfb->xres; fb_info->var.yres = msmfb->yres; fb_info->var.width = msmfb->panel->fb_data->width; fb_info->var.height = msmfb->panel->fb_data->height; fb_info->var.xres_virtual = msmfb->xres; fb_info->var.yres_virtual = msmfb->yres * 2; - fb_info->var.bits_per_pixel = BITS_PER_PIXEL; + fb_info->var.bits_per_pixel = 16; fb_info->var.accel_flags = 0; fb_info->var.yoffset = 0; @@ -660,6 +695,8 @@ static void setup_fb_info(struct msmfb_info *msmfb) fb_info->var.blue.length = 5; fb_info->var.blue.msb_right = 0; + mdp->set_output_format(mdp, fb_info->var.bits_per_pixel); + r = fb_alloc_cmap(&fb_info->cmap, 16, 0); fb_info->pseudo_palette = PP; @@ -673,28 +710,30 @@ static int setup_fbmem(struct msmfb_info *msmfb, struct platform_device *pdev) struct fb_info *fb = msmfb->fb; struct resource *resource; unsigned long size = msmfb->xres * msmfb->yres * - (BITS_PER_PIXEL >> 3) * 2; + BYTES_PER_PIXEL(msmfb) * 2; + unsigned long resource_size; unsigned char *fbram; /* board file might have attached a resource describing an fb */ resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!resource) return -EINVAL; + resource_size = resource->end - resource->start + 1; /* check the resource is large enough to fit the fb */ - if (resource->end - resource->start < size) { - printk(KERN_ERR "allocated resource is too small for " + if (resource_size < size) { + printk(KERN_ERR "msmfb: allocated resource is too small for " "fb\n"); return -ENOMEM; } fb->fix.smem_start = resource->start; - fb->fix.smem_len = resource->end - resource->start; - fbram = ioremap(resource->start, - resource->end - resource->start); + fb->fix.smem_len = resource_size; + fbram = ioremap(resource->start, resource_size); if (fbram == 0) { printk(KERN_ERR "msmfb: cannot allocate fbram!\n"); return -ENOMEM; } + fb->screen_base = fbram; return 0; } From 4213efc64414681f2191872ad24bad42300ad9c2 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 29 May 2009 20:38:24 -0700 Subject: [PATCH 0385/2556] [ARM] msm: msm_fb: Move config options into a separate Kconfig under video/msm Signed-off-by: Dima Zavin --- drivers/video/Kconfig | 14 ++------------ drivers/video/msm/Kconfig | 12 ++++++++++++ 2 files changed, 14 insertions(+), 12 deletions(-) create mode 100644 drivers/video/msm/Kconfig diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index f92b19c1baa6d..d110ba496e5ed 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2323,18 +2323,6 @@ config FB_PRE_INIT_FB Select this option if display contents should be inherited as set by the bootloader. -config FB_MSM - tristate "MSM Framebuffer support" - depends on FB && ARCH_MSM - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - -config FB_MSM_LCDC - bool "Support for integrated LCD controller in qsd8x50" - depends on FB_MSM && MSM_MDP31 - default y - config FB_MX3 tristate "MX3 Framebuffer support" depends on FB && MX3_IPU @@ -2370,6 +2358,8 @@ config FB_JZ4740 help Framebuffer support for the JZ4740 SoC. +source "drivers/video/msm/Kconfig" + source "drivers/video/omap/Kconfig" source "drivers/video/omap2/Kconfig" diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig new file mode 100644 index 0000000000000..ebe5b52a8c6a9 --- /dev/null +++ b/drivers/video/msm/Kconfig @@ -0,0 +1,12 @@ +config FB_MSM + tristate + depends on FB && ARCH_MSM + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + default y + +config FB_MSM_LCDC + bool "Support for integrated LCD controller in qsd8x50" + depends on FB_MSM && MSM_MDP31 + default y From de99f09ee8637e6a5a26b3141176f07767ab670f Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 8 Feb 2010 21:01:27 -0800 Subject: [PATCH 0386/2556] [ARM] msm: Add the yamato GPU driver found on qsd8x50 This driver authored and provided by Qualcomm Inc. Signed-off-by: Dima Zavin msm_kgsl: report rev 2.1.1 instead of 2.1 due to hardware bug Hardware rev 2.1.1 is incorrectly reported as 2.1 when reading the chip ID register. This causes triangle strips to be made larger by the usermode driver and thus reduces triangle rate in applications such as Powerlift. The chip ID is changed back to 2.1.1 to avoid this. CRs-fixed: 187187 Signed-off-by: Simon Wilson msm_kgsl: Clean up memory when timestamps are freed Call yamato_runpending when a timestamp is freed to ensure that the memory actually gets cleaned. Signed-off-by: Jordan Crouse [ARM] video: msm: kgsl: Fix typo when tearing down contexts on file release Signed-off-by: Dima Zavin [ARM] video: msm: kgsl: Disable/enable clocks and irq at suspend/resume. Signed-off-by: Dima Zavin [ARM] msm: kgsl: Integrate latest driver from Qualcomm. Fixes several context and command stream management issues. Fix MMU support, and other fixups. Change-Id: I384694bd611213e7a14a729f55a35d4e7f921d15 Signed-off-by: Dima Zavin [ARM] msm: kgsl: check the right timestamp on drain Change-Id: I5932bf639ff33c31bdb5686dd63929df3a6b2868 Signed-off-by: Dima Zavin kgsl: request 128MHz EBI1 clock when active Signed-off-by: Brian Swetland [ARM] video: msm: kgsl: Add an idle status check function Add a function that checks if the ringbuffer is empty and all the blocks in the GPU are idle. Change-Id: I9d58a2f607f6f7f28d0fbef6d7104e2444f3ebab Signed-off-by: Dima Zavin [ARM] video: msm: kgsl: disable the GPU during periods of inactivity The GPU is only enabled when there are pending commands in the ringbuffer. Whenever the last user of the driver leaves the kernel, a timer is started which will check the emptyness of the ringbuffer and whether or not the GPU itself is idle. If the conditions are not met, the timer is restarted. We try aggressively turn off the GPU after slightly more than a frame of inactivity. Change-Id: I9a9be81d52431ef2defdc22a3df3af2b2b1ce817 Signed-off-by: Dima Zavin [ARM] video: msm: kgsl: Dump register state on timeout for waittimestamp. Also, if the user asks for no timeout, force it to be 10 seconds so we don't end up blocked forever if GPU hangs. Change-Id: Ieb3ed7c87904b0897997b0b55ba758030d388aed Signed-off-by: Dima Zavin [ARM] video: msm: kgsl: Write the error register on axi errors Change-Id: I0536874ddc5f9844b0fe5b6f32457a4470075b1d Signed-off-by: Dima Zavin [ARM] video: msm: kgsl: GPU is truly idle when rbbm_status is 0x110 Change-Id: I1066d8bab2b9fb8f06a89f120a7467241d64b78b Signed-off-by: Dima Zavin --- drivers/video/msm/Kconfig | 19 + drivers/video/msm/Makefile | 3 + drivers/video/msm/gpu/kgsl/Makefile | 11 + drivers/video/msm/gpu/kgsl/kgsl.c | 1174 ++++++++++++ drivers/video/msm/gpu/kgsl/kgsl.h | 83 + drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c | 109 ++ drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h | 44 + drivers/video/msm/gpu/kgsl/kgsl_device.h | 140 ++ drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c | 1766 ++++++++++++++++++ drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h | 116 ++ drivers/video/msm/gpu/kgsl/kgsl_log.c | 292 +++ drivers/video/msm/gpu/kgsl/kgsl_log.h | 105 ++ drivers/video/msm/gpu/kgsl/kgsl_mmu.c | 607 ++++++ drivers/video/msm/gpu/kgsl/kgsl_mmu.h | 137 ++ drivers/video/msm/gpu/kgsl/kgsl_pm4types.h | 181 ++ drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c | 809 ++++++++ drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.h | 254 +++ drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c | 282 +++ drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h | 106 ++ drivers/video/msm/gpu/kgsl/kgsl_yamato.c | 861 +++++++++ drivers/video/msm/gpu/kgsl/yamato_reg.h | 399 ++++ include/linux/msm_kgsl.h | 271 +++ 22 files changed, 7769 insertions(+) create mode 100644 drivers/video/msm/gpu/kgsl/Makefile create mode 100644 drivers/video/msm/gpu/kgsl/kgsl.c create mode 100644 drivers/video/msm/gpu/kgsl/kgsl.h create mode 100644 drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c create mode 100644 drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h create mode 100644 drivers/video/msm/gpu/kgsl/kgsl_device.h create mode 100644 drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c create mode 100644 drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h create mode 100644 drivers/video/msm/gpu/kgsl/kgsl_log.c create mode 100644 drivers/video/msm/gpu/kgsl/kgsl_log.h create mode 100644 drivers/video/msm/gpu/kgsl/kgsl_mmu.c create mode 100644 drivers/video/msm/gpu/kgsl/kgsl_mmu.h create mode 100644 drivers/video/msm/gpu/kgsl/kgsl_pm4types.h create mode 100644 drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c create mode 100644 drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.h create mode 100644 drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c create mode 100644 drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h create mode 100644 drivers/video/msm/gpu/kgsl/kgsl_yamato.c create mode 100644 drivers/video/msm/gpu/kgsl/yamato_reg.h create mode 100644 include/linux/msm_kgsl.h diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig index ebe5b52a8c6a9..aef8db52181c1 100644 --- a/drivers/video/msm/Kconfig +++ b/drivers/video/msm/Kconfig @@ -10,3 +10,22 @@ config FB_MSM_LCDC bool "Support for integrated LCD controller in qsd8x50" depends on FB_MSM && MSM_MDP31 default y + +config GPU_MSM_KGSL + tristate "MSM 3D Graphics driver for QSD8x50 and MSM7x27" + default n + depends on FB_MSM && ARCH_QSD8X50 + select GENERIC_ALLOCATOR + select CONFIG_FW_LOADER + help + 3D graphics driver for QSD8x50 and MSM7x27. Required to + use hardware accelerated OpenGL ES 2.0 and 1.1 on these + chips. + +config MSM_KGSL_MMU + bool "Turn on MMU for graphics driver " + depends on GPU_MSM_KGSL && MMU + default n + help + If enabled, the GPU driver will allocate memory from vmalloc + and enable the use of GPU MMU, instead of using pmem. diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile index 15d4d552c3d34..40bc6c9bcfaa6 100644 --- a/drivers/video/msm/Makefile +++ b/drivers/video/msm/Makefile @@ -26,3 +26,6 @@ obj-y += mddi_client_nt35399.o # MDP LCD controller driver obj-$(CONFIG_FB_MSM_LCDC) += mdp_lcdc.o + +# Yamato GL driver +obj-$(CONFIG_GPU_MSM_KGSL) += gpu/kgsl/ diff --git a/drivers/video/msm/gpu/kgsl/Makefile b/drivers/video/msm/gpu/kgsl/Makefile new file mode 100644 index 0000000000000..0290b50c37e3c --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/Makefile @@ -0,0 +1,11 @@ +msm_kgsl-objs = \ + kgsl_drawctxt.o \ + kgsl_cmdstream.o \ + kgsl.o \ + kgsl_log.o \ + kgsl_mmu.o \ + kgsl_ringbuffer.o \ + kgsl_sharedmem.o \ + kgsl_yamato.o + +obj-$(CONFIG_GPU_MSM_KGSL) += msm_kgsl.o diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c new file mode 100644 index 0000000000000..3bc71fcd58e93 --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/kgsl.c @@ -0,0 +1,1174 @@ +/* +* Copyright (c) 2008-2009 QUALCOMM USA, INC. +* +* All source code in this file is licensed under the following license +* +* 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, you can find it at http://www.fsf.org +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "kgsl.h" +#include "kgsl_drawctxt.h" +#include "kgsl_ringbuffer.h" +#include "kgsl_cmdstream.h" +#include "kgsl_log.h" + +struct kgsl_file_private { + struct list_head list; + struct list_head mem_list; + uint32_t ctxt_id_mask; + struct kgsl_pagetable *pagetable; + unsigned long vmalloc_size; +}; + +#ifdef CONFIG_MSM_KGSL_MMU +static long flush_l1_cache_range(unsigned long addr, int size) +{ + struct page *page; + pte_t *pte_ptr; + unsigned long end; + + for (end = addr; end < (addr + size); end += KGSL_PAGESIZE) { + pte_ptr = kgsl_get_pte_from_vaddr(end); + if (!pte_ptr) + return -EINVAL; + + page = pte_page(pte_val(*pte_ptr)); + if (!page) { + KGSL_DRV_ERR("could not find page for pte\n"); + pte_unmap(pte_ptr); + return -EINVAL; + } + + pte_unmap(pte_ptr); + flush_dcache_page(page); + } + + return 0; +} + +static long flush_l1_cache_all(struct kgsl_file_private *private) +{ + int result = 0; + struct kgsl_mem_entry *entry = NULL; + + kgsl_yamato_runpending(&kgsl_driver.yamato_device); + list_for_each_entry(entry, &private->mem_list, list) { + if (KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH & entry->memdesc.priv) { + result = + flush_l1_cache_range((unsigned long)entry-> + memdesc.hostptr, + entry->memdesc.size); + if (result) + goto done; + } + } +done: + return result; +} +#else +static inline long flush_l1_cache_range(unsigned long addr, int size) +{ return 0; } + +static inline long flush_l1_cache_all(struct kgsl_file_private *private) +{ return 0; } +#endif + +/*this is used for logging, so that we can call the dev_printk + functions without export struct kgsl_driver everywhere*/ +struct device *kgsl_driver_getdevnode(void) +{ + BUG_ON(kgsl_driver.pdev == NULL); + return &kgsl_driver.pdev->dev; +} + +/* the hw and clk enable/disable funcs must be either called from softirq or + * with mutex held */ +static void kgsl_clk_enable(void) +{ + clk_set_rate(kgsl_driver.ebi1_clk, 128000000); + clk_enable(kgsl_driver.imem_clk); + clk_enable(kgsl_driver.grp_clk); +} + +static void kgsl_clk_disable(void) +{ + clk_disable(kgsl_driver.grp_clk); + clk_disable(kgsl_driver.imem_clk); + clk_set_rate(kgsl_driver.ebi1_clk, 0); +} + +static void kgsl_hw_disable(void) +{ + kgsl_driver.active = false; + disable_irq(kgsl_driver.interrupt_num); + kgsl_clk_disable(); + pr_debug("kgsl: hw disabled\n"); + wake_unlock(&kgsl_driver.wake_lock); +} + +static void kgsl_hw_enable(void) +{ + wake_lock(&kgsl_driver.wake_lock); + kgsl_clk_enable(); + enable_irq(kgsl_driver.interrupt_num); + kgsl_driver.active = true; + pr_debug("kgsl: hw enabled\n"); +} + +static void kgsl_hw_get_locked(void) +{ + /* active_cnt is protected by driver mutex */ + if (kgsl_driver.active_cnt++ == 0) { + if (kgsl_driver.active) { + del_timer_sync(&kgsl_driver.standby_timer); + barrier(); + } + if (!kgsl_driver.active) + kgsl_hw_enable(); + } +} + +static void kgsl_hw_put_locked(bool start_timer) +{ + if ((--kgsl_driver.active_cnt == 0) && start_timer) { + mod_timer(&kgsl_driver.standby_timer, + jiffies + msecs_to_jiffies(20)); + } +} + +static void kgsl_do_standby_timer(unsigned long data) +{ + if (kgsl_yamato_is_idle(&kgsl_driver.yamato_device)) { + kgsl_hw_disable(); + } else { + pr_warning("%s: not idle, rescheduling\n", __func__); + mod_timer(&kgsl_driver.standby_timer, + jiffies + msecs_to_jiffies(10)); + } +} + +/* file operations */ +static int kgsl_first_open_locked(void) +{ + int result = 0; + + BUG_ON(kgsl_driver.active); + BUG_ON(kgsl_driver.active_cnt); + + kgsl_clk_enable(); + + /* init memory apertures */ + result = kgsl_sharedmem_init(&kgsl_driver.shmem); + if (result != 0) + goto done; + + /* init devices */ + result = kgsl_yamato_init(&kgsl_driver.yamato_device, + &kgsl_driver.yamato_config); + if (result != 0) + goto done; + + result = kgsl_yamato_start(&kgsl_driver.yamato_device, 0); + if (result != 0) + goto done; + +done: + kgsl_clk_disable(); + return result; +} + +static int kgsl_last_release_locked(void) +{ + BUG_ON(kgsl_driver.active_cnt); + + disable_irq(kgsl_driver.interrupt_num); + + kgsl_yamato_stop(&kgsl_driver.yamato_device); + + /* close devices */ + kgsl_yamato_close(&kgsl_driver.yamato_device); + + /* shutdown memory apertures */ + kgsl_sharedmem_close(&kgsl_driver.shmem); + + kgsl_clk_disable(); + kgsl_driver.active = false; + wake_unlock(&kgsl_driver.wake_lock); + + return 0; +} + +static int kgsl_release(struct inode *inodep, struct file *filep) +{ + int result = 0; + unsigned int i; + struct kgsl_mem_entry *entry, *entry_tmp; + struct kgsl_file_private *private = NULL; + + mutex_lock(&kgsl_driver.mutex); + + private = filep->private_data; + BUG_ON(private == NULL); + filep->private_data = NULL; + list_del(&private->list); + + kgsl_hw_get_locked(); + + for (i = 0; i < KGSL_CONTEXT_MAX; i++) + if (private->ctxt_id_mask & (1 << i)) + kgsl_drawctxt_destroy(&kgsl_driver.yamato_device, i); + + list_for_each_entry_safe(entry, entry_tmp, &private->mem_list, list) + kgsl_remove_mem_entry(entry); + + if (private->pagetable != NULL) { +#ifdef PER_PROCESS_PAGE_TABLE + kgsl_mmu_destroypagetableobject(private->pagetable); +#endif + private->pagetable = NULL; + } + + kfree(private); + + if (atomic_dec_return(&kgsl_driver.open_count) == 0) { + KGSL_DRV_VDBG("last_release\n"); + kgsl_hw_put_locked(false); + result = kgsl_last_release_locked(); + } else + kgsl_hw_put_locked(true); + + mutex_unlock(&kgsl_driver.mutex); + + return result; +} + +static int kgsl_open(struct inode *inodep, struct file *filep) +{ + int result = 0; + struct kgsl_file_private *private = NULL; + + KGSL_DRV_DBG("file %p pid %d\n", filep, task_pid_nr(current)); + + + if (filep->f_flags & O_EXCL) { + KGSL_DRV_ERR("O_EXCL not allowed\n"); + return -EBUSY; + } + + private = kzalloc(sizeof(*private), GFP_KERNEL); + if (private == NULL) { + KGSL_DRV_ERR("cannot allocate file private data\n"); + return -ENOMEM; + } + + mutex_lock(&kgsl_driver.mutex); + + private->ctxt_id_mask = 0; + INIT_LIST_HEAD(&private->mem_list); + + filep->private_data = private; + + list_add(&private->list, &kgsl_driver.client_list); + + if (atomic_inc_return(&kgsl_driver.open_count) == 1) { + result = kgsl_first_open_locked(); + if (result != 0) + goto done; + } + + kgsl_hw_get_locked(); + + /*NOTE: this must happen after first_open */ +#ifdef PER_PROCESS_PAGE_TABLE + private->pagetable = + kgsl_mmu_createpagetableobject(&kgsl_driver.yamato_device.mmu); + if (private->pagetable == NULL) { + result = -ENOMEM; + goto done; + } +#else + private->pagetable = kgsl_driver.yamato_device.mmu.hwpagetable; +#endif + private->vmalloc_size = 0; +done: + kgsl_hw_put_locked(true); + mutex_unlock(&kgsl_driver.mutex); + if (result != 0) + kgsl_release(inodep, filep); + return result; +} + + +/*call with driver locked */ +static struct kgsl_mem_entry * +kgsl_sharedmem_find(struct kgsl_file_private *private, unsigned int gpuaddr) +{ + struct kgsl_mem_entry *entry = NULL, *result = NULL; + + BUG_ON(private == NULL); + + list_for_each_entry(entry, &private->mem_list, list) { + if (entry->memdesc.gpuaddr == gpuaddr) { + result = entry; + break; + } + } + return result; +} + +/*call with driver locked */ +struct kgsl_mem_entry * +kgsl_sharedmem_find_region(struct kgsl_file_private *private, + unsigned int gpuaddr, + size_t size) +{ + struct kgsl_mem_entry *entry = NULL, *result = NULL; + + BUG_ON(private == NULL); + + list_for_each_entry(entry, &private->mem_list, list) { + if (gpuaddr >= entry->memdesc.gpuaddr && + ((gpuaddr + size) <= + (entry->memdesc.gpuaddr + entry->memdesc.size))) { + result = entry; + break; + } + } + + return result; +} + +/*call all ioctl sub functions with driver locked*/ + +static long kgsl_ioctl_device_getproperty(struct kgsl_file_private *private, + void __user *arg) +{ + int result = 0; + struct kgsl_device_getproperty param; + + if (copy_from_user(¶m, arg, sizeof(param))) { + result = -EFAULT; + goto done; + } + result = kgsl_yamato_getproperty(&kgsl_driver.yamato_device, + param.type, + param.value, param.sizebytes); +done: + return result; +} + +static long kgsl_ioctl_device_regread(struct kgsl_file_private *private, + void __user *arg) +{ + int result = 0; + struct kgsl_device_regread param; + + if (copy_from_user(¶m, arg, sizeof(param))) { + result = -EFAULT; + goto done; + } + result = kgsl_yamato_regread(&kgsl_driver.yamato_device, + param.offsetwords, ¶m.value); + if (result != 0) + goto done; + + if (copy_to_user(arg, ¶m, sizeof(param))) { + result = -EFAULT; + goto done; + } +done: + return result; +} + + +static long kgsl_ioctl_device_waittimestamp(struct kgsl_file_private *private, + void __user *arg) +{ + int result = 0; + struct kgsl_device_waittimestamp param; + + if (copy_from_user(¶m, arg, sizeof(param))) { + result = -EFAULT; + goto done; + } + + mutex_unlock(&kgsl_driver.mutex); + /* Don't wait forever, set a max value for now */ + if (param.timeout == -1) + param.timeout = 10 * MSEC_PER_SEC; + result = kgsl_yamato_waittimestamp(&kgsl_driver.yamato_device, + param.timestamp, + param.timeout); + mutex_lock(&kgsl_driver.mutex); + + kgsl_yamato_runpending(&kgsl_driver.yamato_device); +done: + return result; +} + +static long kgsl_ioctl_rb_issueibcmds(struct kgsl_file_private *private, + void __user *arg) +{ + int result = 0; + struct kgsl_ringbuffer_issueibcmds param; + + if (copy_from_user(¶m, arg, sizeof(param))) { + result = -EFAULT; + goto done; + } + + if (param.drawctxt_id >= KGSL_CONTEXT_MAX + || (private->ctxt_id_mask & 1 << param.drawctxt_id) == 0) { + result = -EINVAL; + KGSL_DRV_ERR("invalid drawctxt drawctxt_id %d\n", + param.drawctxt_id); + result = -EINVAL; + goto done; + } + + if (kgsl_sharedmem_find_region(private, param.ibaddr, + param.sizedwords*sizeof(uint32_t)) == NULL) { + KGSL_DRV_ERR("invalid cmd buffer ibaddr %08x sizedwords %d\n", + param.ibaddr, param.sizedwords); + result = -EINVAL; + goto done; + + } + + result = kgsl_ringbuffer_issueibcmds(&kgsl_driver.yamato_device, + param.drawctxt_id, + param.ibaddr, + param.sizedwords, + ¶m.timestamp, + param.flags); + if (result != 0) + goto done; + + if (copy_to_user(arg, ¶m, sizeof(param))) { + result = -EFAULT; + goto done; + } +done: + return result; +} + +static long kgsl_ioctl_cmdstream_readtimestamp(struct kgsl_file_private + *private, void __user *arg) +{ + int result = 0; + struct kgsl_cmdstream_readtimestamp param; + + if (copy_from_user(¶m, arg, sizeof(param))) { + result = -EFAULT; + goto done; + } + + param.timestamp = + kgsl_cmdstream_readtimestamp(&kgsl_driver.yamato_device, + param.type); + if (result != 0) + goto done; + + if (copy_to_user(arg, ¶m, sizeof(param))) { + result = -EFAULT; + goto done; + } +done: + return result; +} + +static long kgsl_ioctl_cmdstream_freememontimestamp(struct kgsl_file_private + *private, void __user *arg) +{ + int result = 0; + struct kgsl_cmdstream_freememontimestamp param; + struct kgsl_mem_entry *entry = NULL; + + if (copy_from_user(¶m, arg, sizeof(param))) { + result = -EFAULT; + goto done; + } + + entry = kgsl_sharedmem_find(private, param.gpuaddr); + if (entry == NULL) { + KGSL_DRV_ERR("invalid gpuaddr %08x\n", param.gpuaddr); + result = -EINVAL; + goto done; + } + + if (entry->memdesc.priv & KGSL_MEMFLAGS_VMALLOC_MEM) + entry->memdesc.priv &= ~KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH; + + result = kgsl_cmdstream_freememontimestamp(&kgsl_driver.yamato_device, + entry, + param.timestamp, + param.type); + + kgsl_yamato_runpending(&kgsl_driver.yamato_device); + +done: + return result; +} + +static long kgsl_ioctl_drawctxt_create(struct kgsl_file_private *private, + void __user *arg) +{ + int result = 0; + struct kgsl_drawctxt_create param; + + if (copy_from_user(¶m, arg, sizeof(param))) { + result = -EFAULT; + goto done; + } + + result = kgsl_drawctxt_create(&kgsl_driver.yamato_device, + private->pagetable, + param.flags, + ¶m.drawctxt_id); + if (result != 0) + goto done; + + if (copy_to_user(arg, ¶m, sizeof(param))) { + result = -EFAULT; + goto done; + } + + private->ctxt_id_mask |= 1 << param.drawctxt_id; + +done: + return result; +} + +static long kgsl_ioctl_drawctxt_destroy(struct kgsl_file_private *private, + void __user *arg) +{ + int result = 0; + struct kgsl_drawctxt_destroy param; + + if (copy_from_user(¶m, arg, sizeof(param))) { + result = -EFAULT; + goto done; + } + + if (param.drawctxt_id >= KGSL_CONTEXT_MAX + || (private->ctxt_id_mask & 1 << param.drawctxt_id) == 0) { + result = -EINVAL; + goto done; + } + + result = kgsl_drawctxt_destroy(&kgsl_driver.yamato_device, + param.drawctxt_id); + if (result == 0) + private->ctxt_id_mask &= ~(1 << param.drawctxt_id); + +done: + return result; +} + +void kgsl_remove_mem_entry(struct kgsl_mem_entry *entry) +{ + if (KGSL_MEMFLAGS_VMALLOC_MEM & entry->memdesc.priv) { + vfree((void *)entry->memdesc.physaddr); + entry->priv->vmalloc_size -= entry->memdesc.size; + kgsl_mmu_unmap(entry->memdesc.pagetable, + entry->memdesc.gpuaddr, entry->memdesc.size); + } else { + KGSL_DRV_DBG("unlocked pmem fd %p\n", entry->pmem_file); + put_pmem_file(entry->pmem_file); + } + list_del(&entry->list); + + if (entry->free_list.prev) + list_del(&entry->free_list); + + kfree(entry); + +} + +static long kgsl_ioctl_sharedmem_free(struct kgsl_file_private *private, + void __user *arg) +{ + int result = 0; + struct kgsl_sharedmem_free param; + struct kgsl_mem_entry *entry = NULL; + + if (copy_from_user(¶m, arg, sizeof(param))) { + result = -EFAULT; + goto done; + } + + entry = kgsl_sharedmem_find(private, param.gpuaddr); + if (entry == NULL) { + KGSL_DRV_ERR("invalid gpuaddr %08x\n", param.gpuaddr); + result = -EINVAL; + goto done; + } + + kgsl_remove_mem_entry(entry); +done: + return result; +} + +#ifdef CONFIG_MSM_KGSL_MMU +static int kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_file_private *private, + void __user *arg) +{ + int result = 0, len; + struct kgsl_sharedmem_from_vmalloc param; + struct kgsl_mem_entry *entry = NULL; + void *vmalloc_area; + struct vm_area_struct *vma; + + if (copy_from_user(¶m, arg, sizeof(param))) { + result = -EFAULT; + goto error; + } + + if (!param.hostptr) { + KGSL_DRV_ERR + ("Invalid host pointer of malloc passed: param.hostptr " + "%08x\n", param.hostptr); + result = -EINVAL; + goto error; + } + + vma = find_vma(current->mm, param.hostptr); + if (!vma) { + KGSL_MEM_ERR("Could not find vma for address %x\n", + param.hostptr); + result = -EINVAL; + goto error; + } + len = vma->vm_end - vma->vm_start; + if (vma->vm_pgoff || !IS_ALIGNED(len, PAGE_SIZE) + || !IS_ALIGNED(vma->vm_start, PAGE_SIZE)) { + KGSL_MEM_ERR + ("kgsl vmalloc mapping must be at offset 0 and page aligned\n"); + result = -EINVAL; + goto error; + } + if (vma->vm_start != param.hostptr) { + KGSL_MEM_ERR + ("vma start address is not equal to mmap address\n"); + result = -EINVAL; + goto error; + } + + if ((private->vmalloc_size + len) > KGSL_GRAPHICS_MEMORY_LOW_WATERMARK + && !param.force_no_low_watermark) { + result = -ENOMEM; + goto error; + } + + entry = kzalloc(sizeof(struct kgsl_mem_entry), GFP_KERNEL); + if (entry == NULL) { + result = -ENOMEM; + goto error; + } + + /* allocate memory and map it to user space */ + vmalloc_area = vmalloc_user(len); + if (!vmalloc_area) { + KGSL_MEM_ERR("vmalloc failed\n"); + result = -ENOMEM; + goto error_free_entry; + } + if (!kgsl_cache_enable) { + KGSL_MEM_INFO("Caching for memory allocation turned off\n"); + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + } else { + KGSL_MEM_INFO("Caching for memory allocation turned on\n"); + } + + result = remap_vmalloc_range(vma, vmalloc_area, 0); + if (result) { + KGSL_MEM_ERR("remap_vmalloc_range returned %d\n", result); + goto error_free_vmalloc; + } + + result = + kgsl_mmu_map(private->pagetable, (unsigned long)vmalloc_area, len, + GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, + &entry->memdesc.gpuaddr); + + if (result != 0) + goto error_free_vmalloc; + + entry->memdesc.pagetable = private->pagetable; + entry->memdesc.size = len; + entry->memdesc.hostptr = (void *)param.hostptr; + entry->memdesc.priv = KGSL_MEMFLAGS_VMALLOC_MEM | + KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH; + entry->memdesc.physaddr = (unsigned long)vmalloc_area; + entry->priv = private; + + param.gpuaddr = entry->memdesc.gpuaddr; + + if (copy_to_user(arg, ¶m, sizeof(param))) { + result = -EFAULT; + goto error_unmap_entry; + } + private->vmalloc_size += len; + list_add(&entry->list, &private->mem_list); + + return 0; + +error_unmap_entry: + kgsl_mmu_unmap(private->pagetable, entry->memdesc.gpuaddr, + entry->memdesc.size); + +error_free_vmalloc: + vfree(vmalloc_area); + +error_free_entry: + kfree(entry); + +error: + return result; +} +#else +static inline int kgsl_ioctl_sharedmem_from_vmalloc( + struct kgsl_file_private *private, void __user *arg) +{ + return -ENOSYS; +} +#endif + +static int kgsl_ioctl_sharedmem_from_pmem(struct kgsl_file_private *private, + void __user *arg) +{ + int result = 0; + struct kgsl_sharedmem_from_pmem param; + struct kgsl_mem_entry *entry = NULL; + unsigned long start = 0, vstart = 0, len = 0; + struct file *pmem_file = NULL; + + if (copy_from_user(¶m, arg, sizeof(param))) { + result = -EFAULT; + goto error; + } + + if (get_pmem_file(param.pmem_fd, &start, &vstart, &len, &pmem_file)) { + result = -EINVAL; + goto error; + } + KGSL_MEM_INFO("pmem file %p start 0x%lx vstart 0x%lx len 0x%lx\n", + pmem_file, start, vstart, len); + KGSL_DRV_DBG("locked pmem file %p\n", pmem_file); + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (entry == NULL) { + result = -ENOMEM; + goto error_put_pmem; + } + + entry->pmem_file = pmem_file; + + entry->memdesc.pagetable = private->pagetable; + entry->memdesc.size = len; + /*we shouldn't need to write here from kernel mode */ + entry->memdesc.hostptr = NULL; + + entry->memdesc.physaddr = start; + entry->memdesc.gpuaddr = start; + + param.gpuaddr = entry->memdesc.gpuaddr; + + if (copy_to_user(arg, ¶m, sizeof(param))) { + result = -EFAULT; + goto error_free_entry; + } + list_add(&entry->list, &private->mem_list); + return 0; +error_free_entry: + kfree(entry); + +error_put_pmem: + KGSL_DRV_DBG("unlocked pmem file %p\n", pmem_file); + put_pmem_file(pmem_file); + +error: + return result; +} + +#ifdef CONFIG_MSM_KGSL_MMU +/*This function flushes a graphics memory allocation from CPU cache + *when caching is enabled with MMU*/ +static int kgsl_ioctl_sharedmem_flush_cache(struct kgsl_file_private *private, + void __user *arg) +{ + int result = 0; + struct kgsl_mem_entry *entry; + struct kgsl_sharedmem_free param; + + if (copy_from_user(¶m, arg, sizeof(param))) { + result = -EFAULT; + goto done; + } + + entry = kgsl_sharedmem_find(private, param.gpuaddr); + if (!entry) { + KGSL_DRV_ERR("invalid gpuaddr %08x\n", param.gpuaddr); + result = -EINVAL; + goto done; + } + result = flush_l1_cache_range((unsigned long)entry->memdesc.hostptr, + entry->memdesc.size); + /* Mark memory as being flushed so we don't flush it again */ + entry->memdesc.priv &= ~KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH; +done: + return result; +} +#else +static int kgsl_ioctl_sharedmem_flush_cache(struct kgsl_file_private *private, + void __user *arg) +{ + return -ENOSYS; +} +#endif + + +static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + int result = 0; + struct kgsl_file_private *private = filep->private_data; + + BUG_ON(private == NULL); + + KGSL_DRV_VDBG("filep %p cmd 0x%08x arg 0x%08lx\n", filep, cmd, arg); + + mutex_lock(&kgsl_driver.mutex); + + kgsl_hw_get_locked(); + + switch (cmd) { + + case IOCTL_KGSL_DEVICE_GETPROPERTY: + result = + kgsl_ioctl_device_getproperty(private, (void __user *)arg); + break; + + case IOCTL_KGSL_DEVICE_REGREAD: + result = kgsl_ioctl_device_regread(private, (void __user *)arg); + break; + + case IOCTL_KGSL_DEVICE_WAITTIMESTAMP: + result = kgsl_ioctl_device_waittimestamp(private, + (void __user *)arg); + break; + + case IOCTL_KGSL_RINGBUFFER_ISSUEIBCMDS: + if (kgsl_cache_enable) + flush_l1_cache_all(private); + result = kgsl_ioctl_rb_issueibcmds(private, (void __user *)arg); + break; + + case IOCTL_KGSL_CMDSTREAM_READTIMESTAMP: + result = + kgsl_ioctl_cmdstream_readtimestamp(private, + (void __user *)arg); + break; + + case IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP: + result = + kgsl_ioctl_cmdstream_freememontimestamp(private, + (void __user *)arg); + break; + + case IOCTL_KGSL_DRAWCTXT_CREATE: + result = kgsl_ioctl_drawctxt_create(private, + (void __user *)arg); + break; + + case IOCTL_KGSL_DRAWCTXT_DESTROY: + result = + kgsl_ioctl_drawctxt_destroy(private, (void __user *)arg); + break; + + case IOCTL_KGSL_SHAREDMEM_FREE: + result = kgsl_ioctl_sharedmem_free(private, (void __user *)arg); + break; + + case IOCTL_KGSL_SHAREDMEM_FROM_VMALLOC: + result = kgsl_ioctl_sharedmem_from_vmalloc(private, + (void __user *)arg); + break; + + case IOCTL_KGSL_SHAREDMEM_FLUSH_CACHE: + if (kgsl_cache_enable) + result = kgsl_ioctl_sharedmem_flush_cache(private, + (void __user *)arg); + break; + case IOCTL_KGSL_SHAREDMEM_FROM_PMEM: + result = kgsl_ioctl_sharedmem_from_pmem(private, + (void __user *)arg); + break; + + default: + KGSL_DRV_ERR("invalid ioctl code %08x\n", cmd); + result = -EINVAL; + break; + } + + kgsl_hw_put_locked(true); + mutex_unlock(&kgsl_driver.mutex); + KGSL_DRV_VDBG("result %d\n", result); + return result; +} + +static int kgsl_mmap(struct file *file, struct vm_area_struct *vma) +{ + int result; + struct kgsl_memdesc *memdesc = NULL; + unsigned long vma_size = vma->vm_end - vma->vm_start; + unsigned long vma_offset = vma->vm_pgoff << PAGE_SHIFT; + struct kgsl_device *device = NULL; + + mutex_lock(&kgsl_driver.mutex); + + device = &kgsl_driver.yamato_device; + + /*allow yamato memstore to be mapped read only */ + if (vma_offset == device->memstore.physaddr) { + if (vma->vm_flags & VM_WRITE) { + result = -EPERM; + goto done; + } + memdesc = &device->memstore; + } + + if (memdesc->size != vma_size) { + KGSL_MEM_ERR("file %p bad size %ld, should be %d\n", + file, vma_size, memdesc->size); + result = -EINVAL; + goto done; + } + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + result = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma_size, vma->vm_page_prot); + if (result != 0) { + KGSL_MEM_ERR("remap_pfn_range returned %d\n", + result); + goto done; + } +done: + mutex_unlock(&kgsl_driver.mutex); + return result; +} + +static struct file_operations kgsl_fops = { + .owner = THIS_MODULE, + .release = kgsl_release, + .open = kgsl_open, + .mmap = kgsl_mmap, + .unlocked_ioctl = kgsl_ioctl, +}; + + +struct kgsl_driver kgsl_driver = { + .misc = { + .name = DRIVER_NAME, + .minor = MISC_DYNAMIC_MINOR, + .fops = &kgsl_fops, + }, + .open_count = ATOMIC_INIT(0), + .mutex = __MUTEX_INITIALIZER(kgsl_driver.mutex), +}; + +static void kgsl_driver_cleanup(void) +{ + + wake_lock_destroy(&kgsl_driver.wake_lock); + + if (kgsl_driver.interrupt_num > 0) { + if (kgsl_driver.have_irq) { + free_irq(kgsl_driver.interrupt_num, NULL); + kgsl_driver.have_irq = 0; + } + kgsl_driver.interrupt_num = 0; + } + + if (kgsl_driver.grp_clk) { + clk_put(kgsl_driver.grp_clk); + kgsl_driver.grp_clk = NULL; + } + + if (kgsl_driver.imem_clk != NULL) { + clk_put(kgsl_driver.imem_clk); + kgsl_driver.imem_clk = NULL; + } + + if (kgsl_driver.ebi1_clk != NULL) { + clk_put(kgsl_driver.ebi1_clk); + kgsl_driver.ebi1_clk = NULL; + } + + kgsl_driver.pdev = NULL; + +} + + +static int __devinit kgsl_platform_probe(struct platform_device *pdev) +{ + int result = 0; + struct clk *clk; + struct resource *res = NULL; + + kgsl_debug_init(); + + INIT_LIST_HEAD(&kgsl_driver.client_list); + + /*acquire clocks */ + BUG_ON(kgsl_driver.grp_clk != NULL); + BUG_ON(kgsl_driver.imem_clk != NULL); + BUG_ON(kgsl_driver.ebi1_clk != NULL); + + kgsl_driver.pdev = pdev; + + setup_timer(&kgsl_driver.standby_timer, kgsl_do_standby_timer, 0); + wake_lock_init(&kgsl_driver.wake_lock, WAKE_LOCK_SUSPEND, "kgsl"); + + clk = clk_get(&pdev->dev, "grp_clk"); + if (IS_ERR(clk)) { + result = PTR_ERR(clk); + KGSL_DRV_ERR("clk_get(grp_clk) returned %d\n", result); + goto done; + } + kgsl_driver.grp_clk = clk; + + clk = clk_get(&pdev->dev, "imem_clk"); + if (IS_ERR(clk)) { + result = PTR_ERR(clk); + KGSL_DRV_ERR("clk_get(imem_clk) returned %d\n", result); + goto done; + } + kgsl_driver.imem_clk = clk; + + clk = clk_get(&pdev->dev, "ebi1_clk"); + if (IS_ERR(clk)) { + result = PTR_ERR(clk); + KGSL_DRV_ERR("clk_get(ebi1_clk) returned %d\n", result); + goto done; + } + kgsl_driver.ebi1_clk = clk; + + /*acquire interrupt */ + kgsl_driver.interrupt_num = platform_get_irq(pdev, 0); + if (kgsl_driver.interrupt_num <= 0) { + KGSL_DRV_ERR("platform_get_irq() returned %d\n", + kgsl_driver.interrupt_num); + result = -EINVAL; + goto done; + } + + result = request_irq(kgsl_driver.interrupt_num, kgsl_yamato_isr, + IRQF_TRIGGER_HIGH, DRIVER_NAME, NULL); + if (result) { + KGSL_DRV_ERR("request_irq(%d) returned %d\n", + kgsl_driver.interrupt_num, result); + goto done; + } + kgsl_driver.have_irq = 1; + disable_irq(kgsl_driver.interrupt_num); + + result = kgsl_yamato_config(&kgsl_driver.yamato_config, pdev); + if (result != 0) + goto done; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "kgsl_phys_memory"); + if (res == NULL) { + result = -EINVAL; + goto done; + } + + kgsl_driver.shmem.physbase = res->start; + kgsl_driver.shmem.size = resource_size(res); + +done: + if (result) + kgsl_driver_cleanup(); + else + result = misc_register(&kgsl_driver.misc); + + return result; +} + +static int kgsl_platform_remove(struct platform_device *pdev) +{ + + kgsl_driver_cleanup(); + misc_deregister(&kgsl_driver.misc); + + return 0; +} + +static int kgsl_platform_suspend(struct platform_device *pdev, + pm_message_t state) +{ + mutex_lock(&kgsl_driver.mutex); + if (atomic_read(&kgsl_driver.open_count) > 0) { + if (kgsl_driver.active) + pr_err("%s: Suspending while active???\n", __func__); + } + mutex_unlock(&kgsl_driver.mutex); + return 0; +} + +static struct platform_driver kgsl_platform_driver = { + .probe = kgsl_platform_probe, + .remove = __devexit_p(kgsl_platform_remove), + .suspend = kgsl_platform_suspend, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME + } +}; + +static int __init kgsl_mod_init(void) +{ + return platform_driver_register(&kgsl_platform_driver); +} + +static void __exit kgsl_mod_exit(void) +{ + platform_driver_unregister(&kgsl_platform_driver); +} + +module_init(kgsl_mod_init); +module_exit(kgsl_mod_exit); + +MODULE_AUTHOR("QUALCOMM"); +MODULE_DESCRIPTION("3D graphics driver for QSD8x50 and MSM7x27"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS("platform:kgsl"); diff --git a/drivers/video/msm/gpu/kgsl/kgsl.h b/drivers/video/msm/gpu/kgsl/kgsl.h new file mode 100644 index 0000000000000..c445f75d33b82 --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/kgsl.h @@ -0,0 +1,83 @@ +/* +* Copyright (c) 2008-2009 QUALCOMM USA, INC. +* +* All source code in this file is licensed under the following license +* +* 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, you can find it at http://www.fsf.org +*/ +#ifndef _GSL_DRIVER_H +#define _GSL_DRIVER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "kgsl_device.h" +#include "kgsl_sharedmem.h" + +#define DRIVER_NAME "kgsl" + +struct kgsl_driver { + struct miscdevice misc; + struct platform_device *pdev; + atomic_t open_count; + struct mutex mutex; + + int interrupt_num; + int have_irq; + + struct clk *grp_clk; + struct clk *imem_clk; + struct clk *ebi1_clk; + + struct kgsl_devconfig yamato_config; + + uint32_t flags_debug; + + struct kgsl_sharedmem shmem; + struct kgsl_device yamato_device; + + struct list_head client_list; + + bool active; + int active_cnt; + struct timer_list standby_timer; + + struct wake_lock wake_lock; +}; + +extern struct kgsl_driver kgsl_driver; + +struct kgsl_mem_entry { + struct kgsl_memdesc memdesc; + struct file *pmem_file; + struct list_head list; + struct list_head free_list; + uint32_t free_timestamp; + + /* back pointer to private structure under whose context this + * allocation is made */ + struct kgsl_file_private *priv; +}; + +void kgsl_remove_mem_entry(struct kgsl_mem_entry *entry); + +#endif /* _GSL_DRIVER_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c new file mode 100644 index 0000000000000..7a2e861bf497b --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c @@ -0,0 +1,109 @@ +/* +* Copyright (c) 2008-2009 QUALCOMM USA, INC. +* +* All source code in this file is licensed under the following license +* +* 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, you can find it at http://www.fsf.org +*/ + +#include "kgsl.h" +#include "kgsl_device.h" +#include "kgsl_cmdstream.h" +#include "kgsl_sharedmem.h" + +int kgsl_cmdstream_init(struct kgsl_device *device) +{ + return 0; +} + +int kgsl_cmdstream_close(struct kgsl_device *device) +{ + return 0; +} + +uint32_t +kgsl_cmdstream_readtimestamp(struct kgsl_device *device, + enum kgsl_timestamp_type type) +{ + uint32_t timestamp = 0; + + KGSL_CMD_VDBG("enter (device_id=%d, type=%d)\n", device->id, type); + + if (type == KGSL_TIMESTAMP_CONSUMED) + KGSL_CMDSTREAM_GET_SOP_TIMESTAMP(device, + (unsigned int *)×tamp); + else if (type == KGSL_TIMESTAMP_RETIRED) + KGSL_CMDSTREAM_GET_EOP_TIMESTAMP(device, + (unsigned int *)×tamp); + + KGSL_CMD_VDBG("return %d\n", timestamp); + + return timestamp; +} + +static bool timestamp_cmp(unsigned int new, unsigned int old) +{ + int ts_diff = new - old; + return (ts_diff >= 0) || (ts_diff < -20000); +} + +int kgsl_cmdstream_check_timestamp(struct kgsl_device *device, + unsigned int timestamp) +{ + unsigned int ts_processed; + + ts_processed = kgsl_cmdstream_readtimestamp(device, + KGSL_TIMESTAMP_RETIRED); + return timestamp_cmp(ts_processed, timestamp); +} + +void kgsl_cmdstream_memqueue_drain(struct kgsl_device *device) +{ + struct kgsl_mem_entry *entry, *entry_tmp; + uint32_t ts_processed; + struct kgsl_ringbuffer *rb = &device->ringbuffer; + + /* get current EOP timestamp */ + ts_processed = + kgsl_cmdstream_readtimestamp(device, KGSL_TIMESTAMP_RETIRED); + + list_for_each_entry_safe(entry, entry_tmp, &rb->memqueue, free_list) { + /*NOTE: this assumes that the free list is sorted by + * timestamp, but I'm not yet sure that it is a valid + * assumption + */ + if (!timestamp_cmp(ts_processed, entry->free_timestamp)) + break; + KGSL_MEM_DBG("ts_processed %d ts_free %d gpuaddr %x)\n", + ts_processed, entry->free_timestamp, + entry->memdesc.gpuaddr); + kgsl_remove_mem_entry(entry); + } +} + +int +kgsl_cmdstream_freememontimestamp(struct kgsl_device *device, + struct kgsl_mem_entry *entry, + uint32_t timestamp, + enum kgsl_timestamp_type type) +{ + struct kgsl_ringbuffer *rb = &device->ringbuffer; + KGSL_MEM_DBG("enter (dev %p gpuaddr %x ts %d)\n", + device, entry->memdesc.gpuaddr, timestamp); + (void)type; /* unref. For now just use EOP timestamp */ + + list_add_tail(&entry->free_list, &rb->memqueue); + entry->free_timestamp = timestamp; + + return 0; +} diff --git a/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h new file mode 100644 index 0000000000000..6664174fed916 --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h @@ -0,0 +1,44 @@ +#ifndef __KGSL_CMDSTREAM_H +#define __KGSL_CMDSTREAM_H + +#include +#include "kgsl_device.h" +#include "kgsl_log.h" + +#ifdef KGSL_DEVICE_SHADOW_MEMSTORE_TO_USER +#define KGSL_CMDSTREAM_USE_MEM_TIMESTAMP +#endif /* KGSL_DEVICE_SHADOW_MEMSTORE_TO_USER */ + +#ifdef KGSL_CMDSTREAM_USE_MEM_TIMESTAMP +#define KGSL_CMDSTREAM_GET_SOP_TIMESTAMP(device, data) \ + kgsl_sharedmem_read(&device->memstore, (data), \ + KGSL_DEVICE_MEMSTORE_OFFSET(soptimestamp), 4) +#else +#define KGSL_CMDSTREAM_GET_SOP_TIMESTAMP(device, data) \ + kgsl_yamato_regread(device, REG_CP_TIMESTAMP, (data)) +#endif /* KGSL_CMDSTREAM_USE_MEM_TIMESTAMP */ + +#define KGSL_CMDSTREAM_GET_EOP_TIMESTAMP(device, data) \ + kgsl_sharedmem_read(&device->memstore, (data), \ + KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp), 4) + +int kgsl_cmdstream_init(struct kgsl_device *device); + +int kgsl_cmdstream_close(struct kgsl_device *device); + +void kgsl_cmdstream_memqueue_drain(struct kgsl_device *device); + +uint32_t +kgsl_cmdstream_readtimestamp(struct kgsl_device *device, + enum kgsl_timestamp_type type); + +int kgsl_cmdstream_check_timestamp(struct kgsl_device *device, + unsigned int timestamp); + +int +kgsl_cmdstream_freememontimestamp(struct kgsl_device *device, + struct kgsl_mem_entry *entry, + uint32_t timestamp, + enum kgsl_timestamp_type type); + +#endif /* __KGSL_CMDSTREAM_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_device.h b/drivers/video/msm/gpu/kgsl/kgsl_device.h new file mode 100644 index 0000000000000..2b6288b4bc439 --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/kgsl_device.h @@ -0,0 +1,140 @@ +/* + * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 + * Copyright (c) 2008-2009 QUALCOMM USA, INC. + * + * All source code in this file is licensed under the following license + * + * 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, you can find it at http://www.fsf.org + */ +#ifndef _KGSL_DEVICE_H +#define _KGSL_DEVICE_H + +#include + +#include +#include +#include +#include + +#include "kgsl_drawctxt.h" +#include "kgsl_mmu.h" +#include "kgsl_ringbuffer.h" + +#define KGSL_CONTEXT_MAX 8 + +#define KGSL_TIMEOUT_NONE 0 +#define KGSL_TIMEOUT_DEFAULT 0xFFFFFFFF + +#define KGSL_DEV_FLAGS_INITIALIZED0 0x00000001 +#define KGSL_DEV_FLAGS_INITIALIZED 0x00000002 +#define KGSL_DEV_FLAGS_STARTED 0x00000004 +#define KGSL_DEV_FLAGS_ACTIVE 0x00000008 + +#define KGSL_CHIPID_YAMATODX_REV21 0x20100 +#define KGSL_CHIPID_YAMATODX_REV211 0x20101 + +/* Private memory flags for use with memdesc->priv feild */ +#define KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH 0x00000001 +#define KGSL_MEMFLAGS_VMALLOC_MEM 0x00000002 + +#define KGSL_GRAPHICS_MEMORY_LOW_WATERMARK 0x1000000 +#define KGSL_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK))) + +struct kgsl_device; +struct platform_device; + + +struct kgsl_memregion { + unsigned char *mmio_virt_base; + unsigned int mmio_phys_base; + uint32_t gpu_base; + unsigned int sizebytes; +}; + +struct kgsl_device { + + unsigned int refcnt; + uint32_t flags; + enum kgsl_deviceid id; + unsigned int chip_id; + struct kgsl_memregion regspace; + struct kgsl_memdesc memstore; + + struct kgsl_mmu mmu; + struct kgsl_memregion gmemspace; + struct kgsl_ringbuffer ringbuffer; + unsigned int drawctxt_count; + struct kgsl_drawctxt *drawctxt_active; + struct kgsl_drawctxt drawctxt[KGSL_CONTEXT_MAX]; + + wait_queue_head_t ib1_wq; +}; + +struct kgsl_devconfig { + struct kgsl_memregion regspace; + + unsigned int mmu_config; + uint32_t mpu_base; + int mpu_range; + uint32_t va_base; + unsigned int va_range; + + struct kgsl_memregion gmemspace; +}; + +int kgsl_yamato_start(struct kgsl_device *device, uint32_t flags); + +int kgsl_yamato_stop(struct kgsl_device *device); + +bool kgsl_yamato_is_idle(struct kgsl_device *device); + +int kgsl_yamato_idle(struct kgsl_device *device, unsigned int timeout); + +int kgsl_yamato_getproperty(struct kgsl_device *device, + enum kgsl_property_type type, void *value, + unsigned int sizebytes); + +int kgsl_yamato_regread(struct kgsl_device *device, unsigned int offsetwords, + unsigned int *value); + +int kgsl_yamato_regwrite(struct kgsl_device *device, unsigned int offsetwords, + unsigned int value); + +int kgsl_yamato_waittimestamp(struct kgsl_device *device, + unsigned int timestamp, unsigned int timeout); + + +int kgsl_yamato_init(struct kgsl_device *, struct kgsl_devconfig *); + +int kgsl_yamato_close(struct kgsl_device *device); + +int kgsl_yamato_runpending(struct kgsl_device *device); + +int __init kgsl_yamato_config(struct kgsl_devconfig *, + struct platform_device *pdev); + +void kgsl_register_dump(struct kgsl_device *device); + +#ifdef CONFIG_MSM_KGSL_MMU +int kgsl_yamato_setpagetable(struct kgsl_device *device); +int kgsl_yamato_tlbinvalidate(struct kgsl_device *device); +#else +static inline int kgsl_yamato_tlbinvalidate(struct kgsl_device *device) +{ return 0; } +static inline int kgsl_yamato_setpagetable(struct kgsl_device *device) +{ return 0; } +#endif + +irqreturn_t kgsl_yamato_isr(int irq, void *data); + +#endif /* _KGSL_DEVICE_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c new file mode 100644 index 0000000000000..305e4bb61cfb0 --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c @@ -0,0 +1,1766 @@ +/* + * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 + * Copyright (c) 2008-2009 QUALCOMM USA, INC. + * + * All source code in this file is licensed under the following license + * + * 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, you can find it at http://www.fsf.org + */ +#include +#include +#include + +#include "kgsl_drawctxt.h" + +#include "yamato_reg.h" +#include "kgsl.h" +#include "kgsl_log.h" +#include "kgsl_pm4types.h" + +/* #define DISABLE_SHADOW_WRITES */ +/* +* +* Memory Map for Register, Constant & Instruction Shadow, and Command Buffers +* (34.5KB) +* +* +---------------------+------------+-------------+---+---------------------+ +* | ALU Constant Shadow | Reg Shadow | C&V Buffers |Tex| Shader Instr Shadow | +* +---------------------+------------+-------------+---+---------------------+ +* ________________________________/ \____________________ +* / | +* +--------------+-----------+------+-----------+------------------------+ +* | Restore Regs | Save Regs | Quad | Gmem Save | Gmem Restore | unused | +* +--------------+-----------+------+-----------+------------------------+ +* +* 8K - ALU Constant Shadow (8K aligned) +* 4K - H/W Register Shadow (8K aligned) +* 4K - Command and Vertex Buffers +* - Indirect command buffer : Const/Reg restore +* - includes Loop & Bool const shadows +* - Indirect command buffer : Const/Reg save +* - Quad vertices & texture coordinates +* - Indirect command buffer : Gmem save +* - Indirect command buffer : Gmem restore +* - Unused (padding to 8KB boundary) +* <1K - Texture Constant Shadow (768 bytes) (8K aligned) +* 18K - Shader Instruction Shadow +* - 6K vertex (32 byte aligned) +* - 6K pixel (32 byte aligned) +* - 6K shared (32 byte aligned) +* +* Note: Reading constants into a shadow, one at a time using REG_TO_MEM, takes +* 3 DWORDS per DWORD transfered, plus 1 DWORD for the shadow, for a total of +* 16 bytes per constant. If the texture constants were transfered this way, +* the Command & Vertex Buffers section would extend past the 16K boundary. +* By moving the texture constant shadow area to start at 16KB boundary, we +* only require approximately 40 bytes more memory, but are able to use the +* LOAD_CONSTANT_CONTEXT shadowing feature for the textures, speeding up +* context switching. +* +* [Using LOAD_CONSTANT_CONTEXT shadowing feature for the Loop and/or Bool +* constants would require an additional 8KB each, for alignment.] +* +*/ + +/* Constants */ + +#define ALU_CONSTANTS 2048 /* DWORDS */ +#define NUM_REGISTERS 1024 /* DWORDS */ +#ifdef DISABLE_SHADOW_WRITES +#define CMD_BUFFER_LEN 9216 /* DWORDS */ +#else +#define CMD_BUFFER_LEN 3072 /* DWORDS */ +#endif +#define TEX_CONSTANTS (32*6) /* DWORDS */ +#define BOOL_CONSTANTS 8 /* DWORDS */ +#define LOOP_CONSTANTS 56 /* DWORDS */ +#define SHADER_INSTRUCT_LOG2 9U /* 2^n == SHADER_INSTRUCTIONS */ + +#if defined(PM4_IM_STORE) +/* 96-bit instructions */ +#define SHADER_INSTRUCT (1<= 0x10000) { + exp += 16; + u >>= 16; + } + if (u >= 0x100) { + exp += 8; + u >>= 8; + } + if (u >= 0x10) { + exp += 4; + u >>= 4; + } + if (u >= 0x4) { + exp += 2; + u >>= 2; + } + if (u >= 0x2) { + exp += 1; + u >>= 1; + } + + /* Calculate fraction */ + frac = (uintval & (~(1 << exp))) << (23 - exp); + + /* Exp is biased by 127 and shifted 23 bits */ + exp = (exp + 127) << 23; + + return exp | frac; +} + +/* context save (gmem -> sys) */ + +/* pre-compiled vertex shader program +* +* attribute vec4 P; +* void main(void) +* { +* gl_Position = P; +* } +*/ +#define GMEM2SYS_VTX_PGM_LEN 0x12 + +static unsigned int gmem2sys_vtx_pgm[GMEM2SYS_VTX_PGM_LEN] = { + 0x00011003, 0x00001000, 0xc2000000, + 0x00001004, 0x00001000, 0xc4000000, + 0x00001005, 0x00002000, 0x00000000, + 0x1cb81000, 0x00398a88, 0x00000003, + 0x140f803e, 0x00000000, 0xe2010100, + 0x14000000, 0x00000000, 0xe2000000 +}; + +/* pre-compiled fragment shader program +* +* precision highp float; +* uniform vec4 clear_color; +* void main(void) +* { +* gl_FragColor = clear_color; +* } +*/ + +#define GMEM2SYS_FRAG_PGM_LEN 0x0c + +static unsigned int gmem2sys_frag_pgm[GMEM2SYS_FRAG_PGM_LEN] = { + 0x00000000, 0x1002c400, 0x10000000, + 0x00001003, 0x00002000, 0x00000000, + 0x140f8000, 0x00000000, 0x22000000, + 0x14000000, 0x00000000, 0xe2000000 +}; + +/* context restore (sys -> gmem) */ +/* pre-compiled vertex shader program +* +* attribute vec4 position; +* attribute vec4 texcoord; +* varying vec4 texcoord0; +* void main() +* { +* gl_Position = position; +* texcoord0 = texcoord; +* } +*/ + +#define SYS2GMEM_VTX_PGM_LEN 0x18 + +static unsigned int sys2gmem_vtx_pgm[SYS2GMEM_VTX_PGM_LEN] = { + 0x00052003, 0x00001000, 0xc2000000, 0x00001005, + 0x00001000, 0xc4000000, 0x00001006, 0x10071000, + 0x20000000, 0x18981000, 0x0039ba88, 0x00000003, + 0x12982000, 0x40257b08, 0x00000002, 0x140f803e, + 0x00000000, 0xe2010100, 0x140f8000, 0x00000000, + 0xe2020200, 0x14000000, 0x00000000, 0xe2000000 +}; + +/* pre-compiled fragment shader program +* +* precision mediump float; +* uniform sampler2D tex0; +* varying vec4 texcoord0; +* void main() +* { +* gl_FragColor = texture2D(tex0, texcoord0.xy); +* } +*/ + +#define SYS2GMEM_FRAG_PGM_LEN 0x0f + +static unsigned int sys2gmem_frag_pgm[SYS2GMEM_FRAG_PGM_LEN] = { + 0x00011002, 0x00001000, 0xc4000000, 0x00001003, + 0x10041000, 0x20000000, 0x10000001, 0x1ffff688, + 0x00000002, 0x140f8000, 0x00000000, 0xe2000000, + 0x14000000, 0x00000000, 0xe2000000 +}; + +/* shader texture constants (sysmem -> gmem) */ +#define SYS2GMEM_TEX_CONST_LEN 6 + +static unsigned int sys2gmem_tex_const[SYS2GMEM_TEX_CONST_LEN] = { + /* Texture, FormatXYZW=Unsigned, ClampXYZ=Wrap/Repeat, + * RFMode=ZeroClamp-1, Dim=1:2d + */ + 0x00000002, /* Pitch = TBD */ + + /* Format=6:8888_WZYX, EndianSwap=0:None, ReqSize=0:256bit, DimHi=0, + * NearestClamp=1:OGL Mode + */ + 0x00000800, /* Address[31:12] = TBD */ + + /* Width, Height, EndianSwap=0:None */ + 0, /* Width & Height = TBD */ + + /* NumFormat=0:RF, DstSelXYZW=XYZW, ExpAdj=0, MagFilt=MinFilt=0:Point, + * Mip=2:BaseMap + */ + 0 << 1 | 1 << 4 | 2 << 7 | 3 << 10 | 2 << 23, + + /* VolMag=VolMin=0:Point, MinMipLvl=0, MaxMipLvl=1, LodBiasH=V=0, + * Dim3d=0 + */ + 0, + + /* BorderColor=0:ABGRBlack, ForceBC=0:diable, TriJuice=0, Aniso=0, + * Dim=1:2d, MipPacking=0 + */ + 1 << 9 /* Mip Address[31:12] = TBD */ +}; + +/* quad for copying GMEM to context shadow */ +#define QUAD_LEN 12 + +static unsigned int gmem_copy_quad[QUAD_LEN] = { + 0x00000000, 0x00000000, 0x3f800000, + 0x00000000, 0x00000000, 0x3f800000, + 0x00000000, 0x00000000, 0x3f800000, + 0x00000000, 0x00000000, 0x3f800000 +}; + +#define TEXCOORD_LEN 8 + +static unsigned int gmem_copy_texcoord[TEXCOORD_LEN] = { + 0x00000000, 0x3f800000, + 0x3f800000, 0x3f800000, + 0x00000000, 0x00000000, + 0x3f800000, 0x00000000 +}; + +#define NUM_COLOR_FORMATS 13 + +static enum SURFACEFORMAT surface_format_table[NUM_COLOR_FORMATS] = { + FMT_4_4_4_4, /* COLORX_4_4_4_4 */ + FMT_1_5_5_5, /* COLORX_1_5_5_5 */ + FMT_5_6_5, /* COLORX_5_6_5 */ + FMT_8, /* COLORX_8 */ + FMT_8_8, /* COLORX_8_8 */ + FMT_8_8_8_8, /* COLORX_8_8_8_8 */ + FMT_8_8_8_8, /* COLORX_S8_8_8_8 */ + FMT_16_FLOAT, /* COLORX_16_FLOAT */ + FMT_16_16_FLOAT, /* COLORX_16_16_FLOAT */ + FMT_16_16_16_16_FLOAT, /* COLORX_16_16_16_16_FLOAT */ + FMT_32_FLOAT, /* COLORX_32_FLOAT */ + FMT_32_32_FLOAT, /* COLORX_32_32_FLOAT */ + FMT_32_32_32_32_FLOAT, /* COLORX_32_32_32_32_FLOAT */ +}; + +static unsigned int format2bytesperpixel[NUM_COLOR_FORMATS] = { + 2, /* COLORX_4_4_4_4 */ + 2, /* COLORX_1_5_5_5 */ + 2, /* COLORX_5_6_5 */ + 1, /* COLORX_8 */ + 2, /* COLORX_8_8 8*/ + 4, /* COLORX_8_8_8_8 */ + 4, /* COLORX_S8_8_8_8 */ + 2, /* COLORX_16_FLOAT */ + 4, /* COLORX_16_16_FLOAT */ + 8, /* COLORX_16_16_16_16_FLOAT */ + 4, /* COLORX_32_FLOAT */ + 8, /* COLORX_32_32_FLOAT */ + 16, /* COLORX_32_32_32_32_FLOAT */ +}; + +/* shader linkage info */ +#define SHADER_CONST_ADDR (11 * 6 + 3) + +/* gmem command buffer length */ +#define PM4_REG(reg) ((0x4 << 16) | (GSL_HAL_SUBBLOCK_OFFSET(reg))) + +/* functions */ +static void config_gmemsize(struct gmem_shadow_t *shadow, int gmem_size) +{ + int w = 64, h = 64; /* 16KB surface, minimum */ + + shadow->format = COLORX_8_8_8_8; + /* convert from bytes to 32-bit words */ + gmem_size = (gmem_size + 3) / 4; + + /* find the right surface size, close to a square. */ + while (w * h < gmem_size) + if (w < h) + w *= 2; + else + h *= 2; + + shadow->width = w; + shadow->pitch = w; + shadow->height = h; + shadow->gmem_pitch = shadow->pitch; + + shadow->size = shadow->pitch * shadow->height * 4; +} + +static unsigned int gpuaddr(unsigned int *cmd, struct kgsl_memdesc *memdesc) +{ + return memdesc->gpuaddr + ((char *)cmd - (char *)memdesc->hostptr); +} + +static void +create_ib1(struct kgsl_drawctxt *drawctxt, unsigned int *cmd, + unsigned int *start, unsigned int *end) +{ + cmd[0] = PM4_HDR_INDIRECT_BUFFER_PFD; + cmd[1] = gpuaddr(start, &drawctxt->gpustate); + cmd[2] = end - start; +} + +static unsigned int *program_shader(unsigned int *cmds, int vtxfrag, + unsigned int *shader_pgm, int dwords) +{ + /* load the patched vertex shader stream */ + *cmds++ = pm4_type3_packet(PM4_IM_LOAD_IMMEDIATE, 2 + dwords); + /* 0=vertex shader, 1=fragment shader */ + *cmds++ = vtxfrag; + /* instruction start & size (in 32-bit words) */ + *cmds++ = ((0 << 16) | dwords); + + memcpy(cmds, shader_pgm, dwords << 2); + cmds += dwords; + + return cmds; +} + +static unsigned int *reg_to_mem(unsigned int *cmds, uint32_t dst, + uint32_t src, int dwords) +{ + while (dwords-- > 0) { + *cmds++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmds++ = src++; + *cmds++ = dst; + dst += 4; + } + + return cmds; +} + +#ifdef DISABLE_SHADOW_WRITES + +static void build_reg_to_mem_range(unsigned int start, unsigned int end, + unsigned int **cmd, + struct kgsl_drawctxt *drawctxt) +{ + unsigned int i = start; + + for (i = start; i <= end; i++) { + *(*cmd)++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *(*cmd)++ = i | (1 << 30); + *(*cmd)++ = + ((drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000) + + (i - 0x2000) * 4; + } +} + +#endif + +/* chicken restore */ +static unsigned int *build_chicken_restore_cmds(struct kgsl_drawctxt *drawctxt, + struct tmp_ctx *ctx) +{ + unsigned int *start = ctx->cmd; + unsigned int *cmds = start; + + *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmds++ = 0; + + *cmds++ = pm4_type0_packet(REG_TP0_CHICKEN, 1); + ctx->chicken_restore = gpuaddr(cmds, &drawctxt->gpustate); + *cmds++ = 0x00000000; + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->chicken_restore, start, cmds); + + return cmds; +} + +/* save h/w regs, alu constants, texture contants, etc. ... +* requires: bool_shadow_gpuaddr, loop_shadow_gpuaddr +*/ +static void build_regsave_cmds(struct kgsl_device *device, + struct kgsl_drawctxt *drawctxt, + struct tmp_ctx *ctx) +{ + unsigned int *start = ctx->cmd; + unsigned int *cmd = start; + unsigned int pm_override1; + + kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE1, &pm_override1); + + *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + +#ifdef DISABLE_SHADOW_WRITES + /* Make sure the HW context has the correct register values + * before reading them. */ + *cmd++ = pm4_type3_packet(PM4_CONTEXT_UPDATE, 1); + *cmd++ = 0; +#endif + + /* Enable clock override for REG_FIFOS_SCLK */ + *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); + *cmd++ = pm_override1 | (1 << 6); + +#ifdef DISABLE_SHADOW_WRITES + /* Write HW registers into shadow */ + build_reg_to_mem_range(REG_RB_SURFACE_INFO, REG_RB_DEPTH_INFO, &cmd, + drawctxt); + build_reg_to_mem_range(REG_COHER_DEST_BASE_0, + REG_PA_SC_SCREEN_SCISSOR_BR, &cmd, drawctxt); + build_reg_to_mem_range(REG_PA_SC_WINDOW_OFFSET, + REG_PA_SC_WINDOW_SCISSOR_BR, &cmd, drawctxt); + build_reg_to_mem_range(REG_VGT_MAX_VTX_INDX, REG_RB_FOG_COLOR, &cmd, + drawctxt); + build_reg_to_mem_range(REG_RB_STENCILREFMASK_BF, + REG_PA_CL_VPORT_ZOFFSET, &cmd, drawctxt); + build_reg_to_mem_range(REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1, &cmd, + drawctxt); + build_reg_to_mem_range(REG_RB_DEPTHCONTROL, REG_RB_MODECONTROL, &cmd, + drawctxt); + build_reg_to_mem_range(REG_PA_SU_POINT_SIZE, REG_PA_SC_LINE_STIPPLE, + &cmd, drawctxt); + build_reg_to_mem_range(REG_PA_SC_VIZ_QUERY, REG_PA_SC_VIZ_QUERY, &cmd, + drawctxt); + build_reg_to_mem_range(REG_PA_SC_LINE_CNTL, REG_SQ_PS_CONST, &cmd, + drawctxt); + build_reg_to_mem_range(REG_PA_SC_AA_MASK, REG_PA_SC_AA_MASK, &cmd, + drawctxt); + build_reg_to_mem_range(REG_VGT_VERTEX_REUSE_BLOCK_CNTL, + REG_RB_DEPTH_CLEAR, &cmd, drawctxt); + build_reg_to_mem_range(REG_RB_SAMPLE_COUNT_CTL, REG_RB_COLOR_DEST_MASK, + &cmd, drawctxt); + build_reg_to_mem_range(REG_PA_SU_POLY_OFFSET_FRONT_SCALE, + REG_PA_SU_POLY_OFFSET_BACK_OFFSET, &cmd, + drawctxt); + + /* Copy ALU constants */ + cmd = + reg_to_mem(cmd, (drawctxt->gpustate.gpuaddr) & 0xFFFFE000, + REG_SQ_CONSTANT_0, ALU_CONSTANTS); + + /* Copy Tex constants */ + cmd = + reg_to_mem(cmd, + (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000, + REG_SQ_FETCH_0, TEX_CONSTANTS); +#else + + /* Insert a wait for idle packet before reading the registers. + * This is to fix a hang/reset seen during stress testing. In this + * hang, CP encountered a timeout reading SQ's boolean constant + * register. There is logic in the HW that blocks reading of this + * register when the SQ block is not idle, which we believe is + * contributing to the hang.*/ + *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + /* H/w registers are already shadowed; just need to disable shadowing + * to prevent corruption. + */ + *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); + *cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000; + *cmd++ = 4 << 16; /* regs, start=0 */ + *cmd++ = 0x0; /* count = 0 */ + + /* ALU constants are already shadowed; just need to disable shadowing + * to prevent corruption. + */ + *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); + *cmd++ = drawctxt->gpustate.gpuaddr & 0xFFFFE000; + *cmd++ = 0 << 16; /* ALU, start=0 */ + *cmd++ = 0x0; /* count = 0 */ + + /* Tex constants are already shadowed; just need to disable shadowing + * to prevent corruption. + */ + *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); + *cmd++ = (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000; + *cmd++ = 1 << 16; /* Tex, start=0 */ + *cmd++ = 0x0; /* count = 0 */ +#endif + + /* Need to handle some of the registers separately */ + *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmd++ = REG_SQ_GPR_MANAGEMENT; + *cmd++ = ctx->reg_values[0]; + + *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmd++ = REG_TP0_CHICKEN; + *cmd++ = ctx->reg_values[1]; + + *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmd++ = REG_RBBM_PM_OVERRIDE1; + *cmd++ = ctx->reg_values[2]; + + *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmd++ = REG_RBBM_PM_OVERRIDE2; + *cmd++ = ctx->reg_values[3]; + + /* Copy Boolean constants */ + cmd = reg_to_mem(cmd, ctx->bool_shadow, REG_SQ_CF_BOOLEANS, + BOOL_CONSTANTS); + + /* Copy Loop constants */ + cmd = reg_to_mem(cmd, ctx->loop_shadow, REG_SQ_CF_LOOP, LOOP_CONSTANTS); + + /* Restore RBBM_PM_OVERRIDE1 */ + *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); + *cmd++ = pm_override1; + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->reg_save, start, cmd); + + ctx->cmd = cmd; +} + +/*copy colour, depth, & stencil buffers from graphics memory to system memory*/ +static unsigned int *build_gmem2sys_cmds(struct kgsl_device *device, + struct kgsl_drawctxt *drawctxt, + struct tmp_ctx *ctx, + struct gmem_shadow_t *shadow) +{ + unsigned int *cmds = shadow->gmem_save_commands; + unsigned int *start = cmds; + unsigned int pm_override1; + /* Calculate the new offset based on the adjusted base */ + unsigned int bytesperpixel = format2bytesperpixel[shadow->format]; + unsigned int addr = + (shadow->gmemshadow.gpuaddr + shadow->offset * bytesperpixel); + unsigned int offset = (addr - (addr & 0xfffff000)) / bytesperpixel; + + kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE1, &pm_override1); + + /* Store TP0_CHICKEN register */ + *cmds++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmds++ = REG_TP0_CHICKEN; + if (ctx) + *cmds++ = ctx->chicken_restore; + else + cmds++; + + *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmds++ = 0; + + /* Enable clock override for REG_FIFOS_SCLK */ + *cmds++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); + *cmds++ = pm_override1 | (1 << 6); + + /* Set TP0_CHICKEN to zero */ + *cmds++ = pm4_type0_packet(REG_TP0_CHICKEN, 1); + *cmds++ = 0x00000000; + + /* Set PA_SC_AA_CONFIG to 0 */ + *cmds++ = pm4_type0_packet(REG_PA_SC_AA_CONFIG, 1); + *cmds++ = 0x00000000; + + /* program shader */ + + /* load shader vtx constants ... 5 dwords */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 4); + *cmds++ = (0x1 << 16) | SHADER_CONST_ADDR; + *cmds++ = 0; + /* valid(?) vtx constant flag & addr */ + *cmds++ = shadow->quad_vertices.gpuaddr | 0x3; + /* limit = 12 dwords */ + *cmds++ = 0x00000030; + + /* Invalidate L2 cache to make sure vertices are updated */ + *cmds++ = pm4_type0_packet(REG_TC_CNTL_STATUS, 1); + *cmds++ = 0x1; + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 4); + *cmds++ = PM4_REG(REG_VGT_MAX_VTX_INDX); + *cmds++ = 0x00ffffff; /* REG_VGT_MAX_VTX_INDX */ + *cmds++ = 0x0; /* REG_VGT_MIN_VTX_INDX */ + *cmds++ = 0x00000000; /* REG_VGT_INDX_OFFSET */ + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_PA_SC_AA_MASK); + *cmds++ = 0x0000ffff; /* REG_PA_SC_AA_MASK */ + + /* load the patched vertex shader stream */ + cmds = program_shader(cmds, 0, gmem2sys_vtx_pgm, GMEM2SYS_VTX_PGM_LEN); + + /* Load the patched fragment shader stream */ + cmds = + program_shader(cmds, 1, gmem2sys_frag_pgm, GMEM2SYS_FRAG_PGM_LEN); + + /* SQ_PROGRAM_CNTL / SQ_CONTEXT_MISC */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_SQ_PROGRAM_CNTL); + *cmds++ = 0x10010001; + *cmds++ = 0x00000008; + + /* resolve */ + + /* PA_CL_VTE_CNTL */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_PA_CL_VTE_CNTL); + /* disable X/Y/Z transforms, X/Y/Z are premultiplied by W */ + *cmds++ = 0x00000b00; + + /* program surface info */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_RB_SURFACE_INFO); + *cmds++ = shadow->gmem_pitch; /* pitch, MSAA = 1 */ + + /* RB_COLOR_INFO Endian=none, Linear, Format=RGBA8888, Swap=0, + * Base=gmem_base + */ + /* gmem base assumed 4K aligned. */ + if (ctx) { + BUG_ON(ctx->gmem_base & 0xFFF); + *cmds++ = + (shadow-> + format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT) | ctx-> + gmem_base; + } else { + unsigned int temp = *cmds; + *cmds++ = (temp & ~RB_COLOR_INFO__COLOR_FORMAT_MASK) | + (shadow->format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT); + } + + /* disable Z */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_RB_DEPTHCONTROL); + *cmds++ = 0; + + /* set REG_PA_SU_SC_MODE_CNTL + * Front_ptype = draw triangles + * Back_ptype = draw triangles + * Provoking vertex = last + */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_PA_SU_SC_MODE_CNTL); + *cmds++ = 0x00080240; + + /* Use maximum scissor values -- quad vertices already have the + * correct bounds */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_PA_SC_SCREEN_SCISSOR_TL); + *cmds++ = (0 << 16) | 0; + *cmds++ = (0x1fff << 16) | (0x1fff); + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_PA_SC_WINDOW_SCISSOR_TL); + *cmds++ = (unsigned int)((1U << 31) | (0 << 16) | 0); + *cmds++ = (0x1fff << 16) | (0x1fff); + + /* load the viewport so that z scale = clear depth and + * z offset = 0.0f + */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_PA_CL_VPORT_ZSCALE); + *cmds++ = 0xbf800000; /* -1.0f */ + *cmds++ = 0x0; + + /* load the stencil ref value + * $AAM - do this later + */ + + /* load the COPY state */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 6); + *cmds++ = PM4_REG(REG_RB_COPY_CONTROL); + *cmds++ = 0; /* RB_COPY_CONTROL */ + *cmds++ = addr & 0xfffff000; /* RB_COPY_DEST_BASE */ + *cmds++ = shadow->pitch >> 5; /* RB_COPY_DEST_PITCH */ + + /* Endian=none, Linear, Format=RGBA8888,Swap=0,!Dither, + * MaskWrite:R=G=B=A=1 + */ + *cmds++ = 0x0003c008 | + (shadow->format << RB_COPY_DEST_INFO__COPY_DEST_FORMAT__SHIFT); + /* Make sure we stay in offsetx field. */ + BUG_ON(offset & 0xfffff000); + *cmds++ = offset; + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_RB_MODECONTROL); + *cmds++ = 0x6; /* EDRAM copy */ + + /* queue the draw packet */ + *cmds++ = pm4_type3_packet(PM4_DRAW_INDX, 2); + *cmds++ = 0; /* viz query info. */ + /* PrimType=RectList, NumIndices=3, SrcSel=AutoIndex */ + *cmds++ = 0x00030088; + + /* Restore RBBM_PM_OVERRIDE1 */ + *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmds++ = 0; + *cmds++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); + *cmds++ = pm_override1; + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, shadow->gmem_save, start, cmds); + + return cmds; +} + +/* context restore */ + +/*copy colour, depth, & stencil buffers from system memory to graphics memory*/ +static unsigned int *build_sys2gmem_cmds(struct kgsl_device *device, + struct kgsl_drawctxt *drawctxt, + struct tmp_ctx *ctx, + struct gmem_shadow_t *shadow) +{ + unsigned int *cmds = shadow->gmem_restore_commands; + unsigned int *start = cmds; + unsigned int pm_override1; + + kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE1, &pm_override1); + + /* Store TP0_CHICKEN register */ + *cmds++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmds++ = REG_TP0_CHICKEN; + if (ctx) + *cmds++ = ctx->chicken_restore; + else + cmds++; + + *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmds++ = 0; + + /* Enable clock override for REG_FIFOS_SCLK */ + *cmds++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); + *cmds++ = pm_override1 | (1 << 6); + + /* Set TP0_CHICKEN to zero */ + *cmds++ = pm4_type0_packet(REG_TP0_CHICKEN, 1); + *cmds++ = 0x00000000; + + /* Set PA_SC_AA_CONFIG to 0 */ + *cmds++ = pm4_type0_packet(REG_PA_SC_AA_CONFIG, 1); + *cmds++ = 0x00000000; + /* shader constants */ + + /* vertex buffer constants */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 7); + + *cmds++ = (0x1 << 16) | (9 * 6); + /* valid(?) vtx constant flag & addr */ + *cmds++ = shadow->quad_vertices.gpuaddr | 0x3; + /* limit = 12 dwords */ + *cmds++ = 0x00000030; + /* valid(?) vtx constant flag & addr */ + *cmds++ = shadow->quad_texcoords.gpuaddr | 0x3; + /* limit = 8 dwords */ + *cmds++ = 0x00000020; + *cmds++ = 0; + *cmds++ = 0; + + /* Invalidate L2 cache to make sure vertices are updated */ + *cmds++ = pm4_type0_packet(REG_TC_CNTL_STATUS, 1); + *cmds++ = 0x1; + + cmds = program_shader(cmds, 0, sys2gmem_vtx_pgm, SYS2GMEM_VTX_PGM_LEN); + + /* Load the patched fragment shader stream */ + cmds = + program_shader(cmds, 1, sys2gmem_frag_pgm, SYS2GMEM_FRAG_PGM_LEN); + + /* SQ_PROGRAM_CNTL / SQ_CONTEXT_MISC */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_SQ_PROGRAM_CNTL); + *cmds++ = 0x10030002; + *cmds++ = 0x00000008; + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_PA_SC_AA_MASK); + *cmds++ = 0x0000ffff; /* REG_PA_SC_AA_MASK */ + + /* PA_SC_VIZ_QUERY */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_PA_SC_VIZ_QUERY); + *cmds++ = 0x0; /*REG_PA_SC_VIZ_QUERY */ + + /* RB_COLORCONTROL */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_RB_COLORCONTROL); + *cmds++ = 0x00000c20; + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 4); + *cmds++ = PM4_REG(REG_VGT_MAX_VTX_INDX); + *cmds++ = 0x00ffffff; /* mmVGT_MAX_VTX_INDX */ + *cmds++ = 0x0; /* mmVGT_MIN_VTX_INDX */ + *cmds++ = 0x00000000; /* mmVGT_INDX_OFFSET */ + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_VGT_VERTEX_REUSE_BLOCK_CNTL); + *cmds++ = 0x00000002; /* mmVGT_VERTEX_REUSE_BLOCK_CNTL */ + *cmds++ = 0x00000002; /* mmVGT_OUT_DEALLOC_CNTL */ + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_SQ_INTERPOLATOR_CNTL); + //*cmds++ = 0x0000ffff; //mmSQ_INTERPOLATOR_CNTL + *cmds++ = 0xffffffff; //mmSQ_INTERPOLATOR_CNTL + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_PA_SC_AA_CONFIG); + *cmds++ = 0x00000000; /* REG_PA_SC_AA_CONFIG */ + + /* set REG_PA_SU_SC_MODE_CNTL + * Front_ptype = draw triangles + * Back_ptype = draw triangles + * Provoking vertex = last + */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_PA_SU_SC_MODE_CNTL); + *cmds++ = 0x00080240; + + /* texture constants */ + *cmds++ = + pm4_type3_packet(PM4_SET_CONSTANT, (SYS2GMEM_TEX_CONST_LEN + 1)); + *cmds++ = (0x1 << 16) | (0 * 6); + memcpy(cmds, sys2gmem_tex_const, SYS2GMEM_TEX_CONST_LEN << 2); + cmds[0] |= (shadow->pitch >> 5) << 22; + cmds[1] |= + shadow->gmemshadow.gpuaddr | surface_format_table[shadow->format]; + cmds[2] |= + (shadow->width + shadow->offset_x - 1) | (shadow->height + + shadow->offset_y - + 1) << 13; + cmds += SYS2GMEM_TEX_CONST_LEN; + + /* program surface info */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_RB_SURFACE_INFO); + *cmds++ = shadow->gmem_pitch; /* pitch, MSAA = 1 */ + + /* RB_COLOR_INFO Endian=none, Linear, Format=RGBA8888, Swap=0, + * Base=gmem_base + */ + if (ctx) + *cmds++ = + (shadow-> + format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT) | ctx-> + gmem_base; + else { + unsigned int temp = *cmds; + *cmds++ = (temp & ~RB_COLOR_INFO__COLOR_FORMAT_MASK) | + (shadow->format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT); + } + + /* RB_DEPTHCONTROL */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_RB_DEPTHCONTROL); + *cmds++ = 0; /* disable Z */ + + /* Use maximum scissor values -- quad vertices already + * have the correct bounds */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_PA_SC_SCREEN_SCISSOR_TL); + *cmds++ = (0 << 16) | 0; + *cmds++ = ((0x1fff) << 16) | 0x1fff; + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_PA_SC_WINDOW_SCISSOR_TL); + *cmds++ = (unsigned int)((1U << 31) | (0 << 16) | 0); + *cmds++ = ((0x1fff) << 16) | 0x1fff; + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_PA_CL_VTE_CNTL); + /* disable X/Y/Z transforms, X/Y/Z are premultiplied by W */ + *cmds++ = 0x00000b00; + + /*load the viewport so that z scale = clear depth and z offset = 0.0f */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_PA_CL_VPORT_ZSCALE); + *cmds++ = 0xbf800000; + *cmds++ = 0x0; + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_RB_COLOR_MASK); + *cmds++ = 0x0000000f; /* R = G = B = 1:enabled */ + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_RB_COLOR_DEST_MASK); + *cmds++ = 0xffffffff; + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_SQ_WRAPPING_0); + *cmds++ = 0x00000000; + *cmds++ = 0x00000000; + + /* load the stencil ref value + * $AAM - do this later + */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_RB_MODECONTROL); + /* draw pixels with color and depth/stencil component */ + *cmds++ = 0x4; + + /* queue the draw packet */ + *cmds++ = pm4_type3_packet(PM4_DRAW_INDX, 2); + *cmds++ = 0; /* viz query info. */ + /* PrimType=RectList, NumIndices=3, SrcSel=AutoIndex */ + *cmds++ = 0x00030088; + + /* Restore RBBM_PM_OVERRIDE1 */ + *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmds++ = 0; + *cmds++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); + *cmds++ = pm_override1; + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, shadow->gmem_restore, start, cmds); + + return cmds; +} + +/* restore h/w regs, alu constants, texture constants, etc. ... */ +static unsigned *reg_range(unsigned int *cmd, unsigned int start, + unsigned int end) +{ + *cmd++ = PM4_REG(start); /* h/w regs, start addr */ + *cmd++ = end - start + 1; /* count */ + return cmd; +} + +static void build_regrestore_cmds(struct kgsl_device *device, + struct kgsl_drawctxt *drawctxt, + struct tmp_ctx *ctx) +{ + unsigned int *start = ctx->cmd; + unsigned int *cmd = start; + unsigned int pm_override1; + + kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE1, &pm_override1); + + *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + + /* Enable clock override for REG_FIFOS_SCLK */ + *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); + *cmd++ = pm_override1 | (1 << 6); + + /* H/W Registers */ + /* deferred pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, ???); */ + cmd++; +#ifdef DISABLE_SHADOW_WRITES + /* Force mismatch */ + *cmd++ = ((drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000) | 1; +#else + *cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000; +#endif + + cmd = reg_range(cmd, REG_RB_SURFACE_INFO, REG_PA_SC_SCREEN_SCISSOR_BR); + cmd = reg_range(cmd, REG_PA_SC_WINDOW_OFFSET, + REG_PA_SC_WINDOW_SCISSOR_BR); + cmd = reg_range(cmd, REG_VGT_MAX_VTX_INDX, REG_PA_CL_VPORT_ZOFFSET); + cmd = reg_range(cmd, REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1); + cmd = reg_range(cmd, REG_RB_DEPTHCONTROL, REG_RB_MODECONTROL); + cmd = reg_range(cmd, REG_PA_SU_POINT_SIZE, + REG_PA_SC_VIZ_QUERY); /*REG_VGT_ENHANCE */ + cmd = reg_range(cmd, REG_PA_SC_LINE_CNTL, REG_RB_COLOR_DEST_MASK); + cmd = reg_range(cmd, REG_PA_SU_POLY_OFFSET_FRONT_SCALE, + REG_PA_SU_POLY_OFFSET_BACK_OFFSET); + + /* Now we know how many register blocks we have, we can compute command + * length + */ + start[4] = + pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, (cmd - start) - 5); + /* Enable shadowing for the entire register block. */ +#ifdef DISABLE_SHADOW_WRITES + start[6] |= (0 << 24) | (4 << 16); /* Disable shadowing. */ +#else + start[6] |= (1 << 24) | (4 << 16); +#endif + + /* Need to handle some of the registers separately */ + *cmd++ = pm4_type0_packet(REG_SQ_GPR_MANAGEMENT, 1); + ctx->reg_values[0] = gpuaddr(cmd, &drawctxt->gpustate); + *cmd++ = 0x00040400; + + *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + *cmd++ = pm4_type0_packet(REG_TP0_CHICKEN, 1); + ctx->reg_values[1] = gpuaddr(cmd, &drawctxt->gpustate); + *cmd++ = 0x00000000; + + *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); + ctx->reg_values[2] = gpuaddr(cmd, &drawctxt->gpustate); + *cmd++ = 0x00000000; + + *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE2, 1); + ctx->reg_values[3] = gpuaddr(cmd, &drawctxt->gpustate); + *cmd++ = 0x00000000; + + /* ALU Constants */ + *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); + *cmd++ = drawctxt->gpustate.gpuaddr & 0xFFFFE000; +#ifdef DISABLE_SHADOW_WRITES + *cmd++ = (0 << 24) | (0 << 16) | 0; /* Disable shadowing */ +#else + *cmd++ = (1 << 24) | (0 << 16) | 0; +#endif + *cmd++ = ALU_CONSTANTS; + + /* Texture Constants */ + *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); + *cmd++ = (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000; +#ifdef DISABLE_SHADOW_WRITES + /* Disable shadowing */ + *cmd++ = (0 << 24) | (1 << 16) | 0; +#else + *cmd++ = (1 << 24) | (1 << 16) | 0; +#endif + *cmd++ = TEX_CONSTANTS; + + /* Boolean Constants */ + *cmd++ = pm4_type3_packet(PM4_SET_CONSTANT, 1 + BOOL_CONSTANTS); + *cmd++ = (2 << 16) | 0; + + /* the next BOOL_CONSTANT dwords is the shadow area for + * boolean constants. + */ + ctx->bool_shadow = gpuaddr(cmd, &drawctxt->gpustate); + cmd += BOOL_CONSTANTS; + + /* Loop Constants */ + *cmd++ = pm4_type3_packet(PM4_SET_CONSTANT, 1 + LOOP_CONSTANTS); + *cmd++ = (3 << 16) | 0; + + /* the next LOOP_CONSTANTS dwords is the shadow area for + * loop constants. + */ + ctx->loop_shadow = gpuaddr(cmd, &drawctxt->gpustate); + cmd += LOOP_CONSTANTS; + + /* Restore RBBM_PM_OVERRIDE1 */ + *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); + *cmd++ = pm_override1; + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->reg_restore, start, cmd); + + ctx->cmd = cmd; +} + +/* quad for saving/restoring gmem */ +static void set_gmem_copy_quad(struct gmem_shadow_t *shadow) +{ + /* set vertex buffer values */ + gmem_copy_quad[1] = uint2float(shadow->height + shadow->gmem_offset_y); + gmem_copy_quad[3] = uint2float(shadow->width + shadow->gmem_offset_x); + gmem_copy_quad[4] = uint2float(shadow->height + shadow->gmem_offset_y); + gmem_copy_quad[9] = uint2float(shadow->width + shadow->gmem_offset_x); + + gmem_copy_quad[0] = uint2float(shadow->gmem_offset_x); + gmem_copy_quad[6] = uint2float(shadow->gmem_offset_x); + gmem_copy_quad[7] = uint2float(shadow->gmem_offset_y); + gmem_copy_quad[10] = uint2float(shadow->gmem_offset_y); + + BUG_ON(shadow->offset_x); + BUG_ON(shadow->offset_y); + + memcpy(shadow->quad_vertices.hostptr, gmem_copy_quad, QUAD_LEN << 2); + + memcpy(shadow->quad_texcoords.hostptr, gmem_copy_texcoord, + TEXCOORD_LEN << 2); +} + +/* quad for saving/restoring gmem */ +static void build_quad_vtxbuff(struct kgsl_drawctxt *drawctxt, + struct tmp_ctx *ctx, struct gmem_shadow_t *shadow) +{ + unsigned int *cmd = ctx->cmd; + + /* quad vertex buffer location (in GPU space) */ + shadow->quad_vertices.hostptr = cmd; + shadow->quad_vertices.gpuaddr = gpuaddr(cmd, &drawctxt->gpustate); + + cmd += QUAD_LEN; + + /* tex coord buffer location (in GPU space) */ + shadow->quad_texcoords.hostptr = cmd; + shadow->quad_texcoords.gpuaddr = gpuaddr(cmd, &drawctxt->gpustate); + + cmd += TEXCOORD_LEN; + + set_gmem_copy_quad(shadow); + + ctx->cmd = cmd; +} + +static void +build_shader_save_restore_cmds(struct kgsl_drawctxt *drawctxt, + struct tmp_ctx *ctx) +{ + unsigned int *cmd = ctx->cmd; + unsigned int *save, *restore, *fixup; +#if defined(PM4_IM_STORE) + unsigned int *startSizeVtx, *startSizePix, *startSizeShared; +#endif + unsigned int *partition1; + unsigned int *shaderBases, *partition2; + +#if defined(PM4_IM_STORE) + /* compute vertex, pixel and shared instruction shadow GPU addresses */ + ctx->shader_vertex = drawctxt->gpustate.gpuaddr + SHADER_OFFSET; + ctx->shader_pixel = ctx->shader_vertex + SHADER_SHADOW_SIZE; + ctx->shader_shared = ctx->shader_pixel + SHADER_SHADOW_SIZE; +#endif + + /* restore shader partitioning and instructions */ + + restore = cmd; /* start address */ + + /* Invalidate Vertex & Pixel instruction code address and sizes */ + *cmd++ = pm4_type3_packet(PM4_INVALIDATE_STATE, 1); + *cmd++ = 0x00000300; /* 0x100 = Vertex, 0x200 = Pixel */ + + /* Restore previous shader vertex & pixel instruction bases. */ + *cmd++ = pm4_type3_packet(PM4_SET_SHADER_BASES, 1); + shaderBases = cmd++; /* TBD #5: shader bases (from fixup) */ + + /* write the shader partition information to a scratch register */ + *cmd++ = pm4_type0_packet(REG_SQ_INST_STORE_MANAGMENT, 1); + partition1 = cmd++; /* TBD #4a: partition info (from save) */ + +#if defined(PM4_IM_STORE) + /* load vertex shader instructions from the shadow. */ + *cmd++ = pm4_type3_packet(PM4_IM_LOAD, 2); + *cmd++ = ctx->shader_vertex + 0x0; /* 0x0 = Vertex */ + startSizeVtx = cmd++; /* TBD #1: start/size (from save) */ + + /* load pixel shader instructions from the shadow. */ + *cmd++ = pm4_type3_packet(PM4_IM_LOAD, 2); + *cmd++ = ctx->shader_pixel + 0x1; /* 0x1 = Pixel */ + startSizePix = cmd++; /* TBD #2: start/size (from save) */ + + /* load shared shader instructions from the shadow. */ + *cmd++ = pm4_type3_packet(PM4_IM_LOAD, 2); + *cmd++ = ctx->shader_shared + 0x2; /* 0x2 = Shared */ + startSizeShared = cmd++; /* TBD #3: start/size (from save) */ +#endif + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->shader_restore, restore, cmd); + + /* + * fixup SET_SHADER_BASES data + * + * since self-modifying PM4 code is being used here, a seperate + * command buffer is used for this fixup operation, to ensure the + * commands are not read by the PM4 engine before the data fields + * have been written. + */ + + fixup = cmd; /* start address */ + + /* write the shader partition information to a scratch register */ + *cmd++ = pm4_type0_packet(REG_SCRATCH_REG2, 1); + partition2 = cmd++; /* TBD #4b: partition info (from save) */ + + /* mask off unused bits, then OR with shader instruction memory size */ + *cmd++ = pm4_type3_packet(PM4_REG_RMW, 3); + *cmd++ = REG_SCRATCH_REG2; + /* AND off invalid bits. */ + *cmd++ = 0x0FFF0FFF; + /* OR in instruction memory size */ + *cmd++ = (unsigned int)((SHADER_INSTRUCT_LOG2 - 5U) << 29); + + /* write the computed value to the SET_SHADER_BASES data field */ + *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmd++ = REG_SCRATCH_REG2; + /* TBD #5: shader bases (to restore) */ + *cmd++ = gpuaddr(shaderBases, &drawctxt->gpustate); + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->shader_fixup, fixup, cmd); + + /* save shader partitioning and instructions */ + + save = cmd; /* start address */ + + *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + + /* fetch the SQ_INST_STORE_MANAGMENT register value, + * store the value in the data fields of the SET_CONSTANT commands + * above. + */ + *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmd++ = REG_SQ_INST_STORE_MANAGMENT; + /* TBD #4a: partition info (to restore) */ + *cmd++ = gpuaddr(partition1, &drawctxt->gpustate); + *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmd++ = REG_SQ_INST_STORE_MANAGMENT; + /* TBD #4b: partition info (to fixup) */ + *cmd++ = gpuaddr(partition2, &drawctxt->gpustate); + +#if defined(PM4_IM_STORE) + + /* store the vertex shader instructions */ + *cmd++ = pm4_type3_packet(PM4_IM_STORE, 2); + *cmd++ = ctx->shader_vertex + 0x0; /* 0x0 = Vertex */ + /* TBD #1: start/size (to restore) */ + *cmd++ = gpuaddr(startSizeVtx, &drawctxt->gpustate); + + /* store the pixel shader instructions */ + *cmd++ = pm4_type3_packet(PM4_IM_STORE, 2); + *cmd++ = ctx->shader_pixel + 0x1; /* 0x1 = Pixel */ + /* TBD #2: start/size (to restore) */ + *cmd++ = gpuaddr(startSizePix, &drawctxt->gpustate); + + /* store the shared shader instructions if vertex base is nonzero */ + + *cmd++ = pm4_type3_packet(PM4_IM_STORE, 2); + *cmd++ = ctx->shader_shared + 0x2; /* 0x2 = Shared */ + /* TBD #3: start/size (to restore) */ + *cmd++ = gpuaddr(startSizeShared, &drawctxt->gpustate); + +#endif + + *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->shader_save, save, cmd); + + ctx->cmd = cmd; +} + +/* create buffers for saving/restoring registers, constants, & GMEM */ +static int +create_gpustate_shadow(struct kgsl_device *device, + struct kgsl_drawctxt *drawctxt, struct tmp_ctx *ctx) +{ + uint32_t flags; + + flags = (KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN8K); + + /* allocate memory to allow HW to save sub-blocks for efficient context + * save/restore + */ + if (kgsl_sharedmem_alloc(flags, CONTEXT_SIZE, &drawctxt->gpustate) != 0) + return -ENOMEM; + + drawctxt->flags |= CTXT_FLAGS_STATE_SHADOW; + + /* Blank out h/w register, constant, and command buffer shadows. */ + kgsl_sharedmem_set(&drawctxt->gpustate, 0, 0, CONTEXT_SIZE); + + /* set-up command and vertex buffer pointers */ + ctx->cmd = ctx->start + = (unsigned int *)((char *)drawctxt->gpustate.hostptr + CMD_OFFSET); + + /* build indirect command buffers to save & restore regs/constants */ + kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT); + build_regrestore_cmds(device, drawctxt, ctx); + build_regsave_cmds(device, drawctxt, ctx); + + build_shader_save_restore_cmds(drawctxt, ctx); + + return 0; +} + +/* create buffers for saving/restoring registers, constants, & GMEM */ +static int +create_gmem_shadow(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt, + struct tmp_ctx *ctx) +{ + unsigned int flags = KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN8K, i; + + config_gmemsize(&drawctxt->context_gmem_shadow, + device->gmemspace.sizebytes); + ctx->gmem_base = device->gmemspace.gpu_base; + + /* allocate memory for GMEM shadow */ + if (kgsl_sharedmem_alloc(flags, drawctxt->context_gmem_shadow.size, + &drawctxt->context_gmem_shadow.gmemshadow) != + 0) + return -ENOMEM; + + /* we've allocated the shadow, when swapped out, GMEM must be saved. */ + drawctxt->flags |= CTXT_FLAGS_GMEM_SHADOW | CTXT_FLAGS_GMEM_SAVE; + + /* blank out gmem shadow. */ + kgsl_sharedmem_set(&drawctxt->context_gmem_shadow.gmemshadow, 0, 0, + drawctxt->context_gmem_shadow.size); + + /* build quad vertex buffer */ + build_quad_vtxbuff(drawctxt, ctx, &drawctxt->context_gmem_shadow); + + /* build TP0_CHICKEN register restore command buffer */ + ctx->cmd = build_chicken_restore_cmds(drawctxt, ctx); + + /* build indirect command buffers to save & restore gmem */ + /* Idle because we are reading PM override registers */ + kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT); + drawctxt->context_gmem_shadow.gmem_save_commands = ctx->cmd; + ctx->cmd = + build_gmem2sys_cmds(device, drawctxt, ctx, + &drawctxt->context_gmem_shadow); + drawctxt->context_gmem_shadow.gmem_restore_commands = ctx->cmd; + ctx->cmd = + build_sys2gmem_cmds(device, drawctxt, ctx, + &drawctxt->context_gmem_shadow); + + for (i = 0; i < KGSL_MAX_GMEM_SHADOW_BUFFERS; i++) { + build_quad_vtxbuff(drawctxt, ctx, + &drawctxt->user_gmem_shadow[i]); + + drawctxt->user_gmem_shadow[i].gmem_save_commands = ctx->cmd; + ctx->cmd = + build_gmem2sys_cmds(device, drawctxt, ctx, + &drawctxt->user_gmem_shadow[i]); + + drawctxt->user_gmem_shadow[i].gmem_restore_commands = ctx->cmd; + ctx->cmd = + build_sys2gmem_cmds(device, drawctxt, ctx, + &drawctxt->user_gmem_shadow[i]); + } + + return 0; +} + +/* init draw context */ + +int kgsl_drawctxt_init(struct kgsl_device *device) +{ + return 0; +} + +/* close draw context */ +int kgsl_drawctxt_close(struct kgsl_device *device) +{ + return 0; +} + +/* create a new drawing context */ + +int +kgsl_drawctxt_create(struct kgsl_device *device, + struct kgsl_pagetable *pagetable, + unsigned int flags, unsigned int *drawctxt_id) +{ + struct kgsl_drawctxt *drawctxt; + int index; + struct tmp_ctx ctx; + + KGSL_CTXT_INFO("pt %p flags %08x\n", pagetable, flags); + if (device->drawctxt_count >= KGSL_CONTEXT_MAX) + return -EINVAL; + + /* find a free context slot */ + index = 0; + while (index < KGSL_CONTEXT_MAX) { + if (device->drawctxt[index].flags == CTXT_FLAGS_NOT_IN_USE) + break; + + index++; + } + + if (index >= KGSL_CONTEXT_MAX) + return -EINVAL; + + drawctxt = &device->drawctxt[index]; + drawctxt->pagetable = pagetable; + drawctxt->flags = CTXT_FLAGS_IN_USE; + + device->drawctxt_count++; + + if (create_gpustate_shadow(device, drawctxt, &ctx) + != 0) { + kgsl_drawctxt_destroy(device, index); + return -EINVAL; + } + + /* Save the shader instruction memory on context switching */ + drawctxt->flags |= CTXT_FLAGS_SHADER_SAVE; + + if (!(flags & KGSL_CONTEXT_NO_GMEM_ALLOC)) { + /* create gmem shadow */ + memset(drawctxt->user_gmem_shadow, 0, + sizeof(struct gmem_shadow_t) * + KGSL_MAX_GMEM_SHADOW_BUFFERS); + + if (create_gmem_shadow(device, drawctxt, &ctx) + != 0) { + kgsl_drawctxt_destroy(device, index); + return -EINVAL; + } + } + + BUG_ON(ctx.cmd - ctx.start > CMD_BUFFER_LEN); + + *drawctxt_id = index; + + KGSL_CTXT_INFO("return drawctxt_id %d\n", *drawctxt_id); + return 0; +} + +/* destroy a drawing context */ + +int kgsl_drawctxt_destroy(struct kgsl_device *device, unsigned int drawctxt_id) +{ + struct kgsl_drawctxt *drawctxt; + + drawctxt = &device->drawctxt[drawctxt_id]; + + KGSL_CTXT_INFO("drawctxt_id %d ptr %p\n", drawctxt_id, drawctxt); + if (drawctxt->flags != CTXT_FLAGS_NOT_IN_USE) { + /* deactivate context */ + if (device->drawctxt_active == drawctxt) { + /* no need to save GMEM or shader, the context is + * being destroyed. + */ + drawctxt->flags &= ~(CTXT_FLAGS_GMEM_SAVE | + CTXT_FLAGS_SHADER_SAVE | + CTXT_FLAGS_GMEM_SHADOW | + CTXT_FLAGS_STATE_SHADOW); + + kgsl_drawctxt_switch(device, NULL, 0); + } + + kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT); + + /* destroy state shadow, if allocated */ + if (drawctxt->gpustate.gpuaddr != 0) { + drawctxt->gpustate.gpuaddr = 0; + } + if (drawctxt->gpustate.physaddr != 0) + kgsl_sharedmem_free(&drawctxt->gpustate); + + /* destroy gmem shadow, if allocated */ + if (drawctxt->context_gmem_shadow.gmemshadow.gpuaddr != 0) + drawctxt->context_gmem_shadow.gmemshadow.gpuaddr = 0; + + if (drawctxt->context_gmem_shadow.gmemshadow.physaddr != 0) + kgsl_sharedmem_free(&drawctxt->context_gmem_shadow. + gmemshadow); + + drawctxt->flags = CTXT_FLAGS_NOT_IN_USE; + + BUG_ON(device->drawctxt_count == 0); + device->drawctxt_count--; + } + KGSL_CTXT_INFO("return\n"); + return 0; +} + +/* Binds a user specified buffer as GMEM shadow area */ +int kgsl_drawctxt_bind_gmem_shadow(struct kgsl_device *device, + unsigned int drawctxt_id, + const struct kgsl_gmem_desc *gmem_desc, + unsigned int shadow_x, + unsigned int shadow_y, + const struct kgsl_buffer_desc + *shadow_buffer, unsigned int buffer_id) +{ + struct kgsl_drawctxt *drawctxt; + + /* Shadow struct being modified */ + struct gmem_shadow_t *shadow; + unsigned int i; + + if (device->flags & KGSL_FLAGS_SAFEMODE) + /* No need to bind any buffers since safe mode + * skips context switch */ + return 0; + + drawctxt = &device->drawctxt[drawctxt_id]; + + shadow = &drawctxt->user_gmem_shadow[buffer_id]; + + if (!shadow_buffer->enabled) { + /* Disable shadow */ + KGSL_MEM_ERR("shadow is disabled in bind_gmem\n"); + shadow->gmemshadow.size = 0; + } else { + /* Binding to a buffer */ + unsigned int width, height; + + BUG_ON(gmem_desc->x % 2); /* Needs to be a multiple of 2 */ + BUG_ON(gmem_desc->y % 2); /* Needs to be a multiple of 2 */ + BUG_ON(gmem_desc->width % 2); /* Needs to be a multiple of 2 */ + /* Needs to be a multiple of 2 */ + BUG_ON(gmem_desc->height % 2); + /* Needs to be a multiple of 32 */ + BUG_ON(gmem_desc->pitch % 32); + + BUG_ON(shadow_x % 2); /* Needs to be a multiple of 2 */ + BUG_ON(shadow_y % 2); /* Needs to be a multiple of 2 */ + + BUG_ON(shadow_buffer->format < COLORX_4_4_4_4); + BUG_ON(shadow_buffer->format > COLORX_32_32_32_32_FLOAT); + /* Needs to be a multiple of 32 */ + BUG_ON(shadow_buffer->pitch % 32); + + BUG_ON(buffer_id < 0); + BUG_ON(buffer_id > KGSL_MAX_GMEM_SHADOW_BUFFERS); + + width = gmem_desc->width; + height = gmem_desc->height; + + shadow->width = width; + shadow->format = shadow_buffer->format; + + shadow->height = height; + shadow->pitch = shadow_buffer->pitch; + + memset(&shadow->gmemshadow, 0, sizeof(struct kgsl_memdesc)); + shadow->gmemshadow.hostptr = shadow_buffer->hostptr; + shadow->gmemshadow.gpuaddr = shadow_buffer->gpuaddr; + shadow->gmemshadow.physaddr = shadow->gmemshadow.gpuaddr; + shadow->gmemshadow.size = shadow_buffer->size; + + /* Calculate offset */ + shadow->offset = + (int)(shadow_buffer->pitch) * ((int)shadow_y - + (int)gmem_desc->y) + + (int)shadow_x - (int)gmem_desc->x; + + shadow->offset_x = shadow_x; + shadow->offset_y = shadow_y; + shadow->gmem_offset_x = gmem_desc->x; + shadow->gmem_offset_y = gmem_desc->y; + + shadow->size = shadow->gmemshadow.size; + + shadow->gmem_pitch = gmem_desc->pitch; + + /* Modify quad vertices */ + set_gmem_copy_quad(shadow); + + /* Idle because we are reading PM override registers */ + kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT); + + /* Modify commands */ + build_gmem2sys_cmds(device, drawctxt, NULL, shadow); + build_sys2gmem_cmds(device, drawctxt, NULL, shadow); + + /* Release context GMEM shadow if found */ + if (drawctxt->context_gmem_shadow.gmemshadow.physaddr != 0) { + kgsl_sharedmem_free(&drawctxt->context_gmem_shadow. + gmemshadow); + drawctxt->context_gmem_shadow.gmemshadow.physaddr = 0; + } + } + + /* Enable GMEM shadowing if we have any of the user buffers enabled */ + drawctxt->flags &= ~CTXT_FLAGS_GMEM_SHADOW; + for (i = 0; i < KGSL_MAX_GMEM_SHADOW_BUFFERS; i++) { + if (drawctxt->user_gmem_shadow[i].gmemshadow.size > 0) + drawctxt->flags |= CTXT_FLAGS_GMEM_SHADOW; + } + + return 0; +} + +/* switch drawing contexts */ +void +kgsl_drawctxt_switch(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt, + unsigned int flags) +{ + struct kgsl_drawctxt *active_ctxt = device->drawctxt_active; + + /* already current? */ + if (active_ctxt == drawctxt) + return; + + KGSL_CTXT_INFO("from %p to %p flags %d\n", + device->drawctxt_active, drawctxt, flags); + /* save old context*/ + if (active_ctxt != NULL) { + KGSL_CTXT_INFO("active_ctxt flags %08x\n", active_ctxt->flags); + /* save registers and constants. */ + KGSL_CTXT_DBG("save regs"); + kgsl_ringbuffer_issuecmds(device, 0, active_ctxt->reg_save, 3); + + if (active_ctxt->flags & CTXT_FLAGS_SHADER_SAVE) { + /* save shader partitioning and instructions. */ + KGSL_CTXT_DBG("save shader"); + kgsl_ringbuffer_issuecmds(device, 1, + active_ctxt->shader_save, 3); + + /* fixup shader partitioning parameter for + * SET_SHADER_BASES. + */ + KGSL_CTXT_DBG("save shader fixup"); + kgsl_ringbuffer_issuecmds(device, 0, + active_ctxt->shader_fixup, 3); + + active_ctxt->flags |= CTXT_FLAGS_SHADER_RESTORE; + } + + if (active_ctxt->flags & CTXT_FLAGS_GMEM_SAVE + && flags & KGSL_CONTEXT_SAVE_GMEM) { + /* save gmem. + * (note: changes shader. shader must already be saved.) + */ + unsigned int i, numbuffers = 0; + KGSL_CTXT_DBG("save gmem"); + for (i = 0; i < KGSL_MAX_GMEM_SHADOW_BUFFERS; i++) { + if (active_ctxt->user_gmem_shadow[i].gmemshadow. + size > 0) { + kgsl_ringbuffer_issuecmds(device, 1, + active_ctxt->user_gmem_shadow[i]. + gmem_save, 3); + + /* Restore TP0_CHICKEN */ + kgsl_ringbuffer_issuecmds(device, 0, + active_ctxt->chicken_restore, 3); + + numbuffers++; + } + } + if (numbuffers == 0) { + kgsl_ringbuffer_issuecmds(device, 1, + active_ctxt->context_gmem_shadow. + gmem_save, 3); + + /* Restore TP0_CHICKEN */ + kgsl_ringbuffer_issuecmds(device, 0, + active_ctxt->chicken_restore, 3); + } + + active_ctxt->flags |= CTXT_FLAGS_GMEM_RESTORE; + } + } + + device->drawctxt_active = drawctxt; + + /* restore new context */ + if (drawctxt != NULL) { + + KGSL_CTXT_INFO("drawctxt flags %08x\n", drawctxt->flags); + KGSL_CTXT_DBG("restore pagetable"); + kgsl_mmu_setpagetable(device, drawctxt->pagetable); + + /* restore gmem. + * (note: changes shader. shader must not already be restored.) + */ + if (drawctxt->flags & CTXT_FLAGS_GMEM_RESTORE) { + unsigned int i, numbuffers = 0; + KGSL_CTXT_DBG("restore gmem"); + + for (i = 0; i < KGSL_MAX_GMEM_SHADOW_BUFFERS; i++) { + if (drawctxt->user_gmem_shadow[i].gmemshadow. + size > 0) { + kgsl_ringbuffer_issuecmds(device, 1, + drawctxt->user_gmem_shadow[i]. + gmem_restore, 3); + + /* Restore TP0_CHICKEN */ + kgsl_ringbuffer_issuecmds(device, 0, + drawctxt->chicken_restore, 3); + numbuffers++; + } + } + if (numbuffers == 0) { + kgsl_ringbuffer_issuecmds(device, 1, + drawctxt->context_gmem_shadow.gmem_restore, + 3); + + /* Restore TP0_CHICKEN */ + kgsl_ringbuffer_issuecmds(device, 0, + drawctxt->chicken_restore, 3); + } + drawctxt->flags &= ~CTXT_FLAGS_GMEM_RESTORE; + } + + /* restore registers and constants. */ + KGSL_CTXT_DBG("restore regs"); + kgsl_ringbuffer_issuecmds(device, 0, + drawctxt->reg_restore, 3); + + /* restore shader instructions & partitioning. */ + if (drawctxt->flags & CTXT_FLAGS_SHADER_RESTORE) + KGSL_CTXT_DBG("restore shader"); + kgsl_ringbuffer_issuecmds(device, 0, + drawctxt->shader_restore, 3); + } + KGSL_CTXT_INFO("return\n"); +} diff --git a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h new file mode 100644 index 0000000000000..d5bd805b83420 --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h @@ -0,0 +1,116 @@ +/* +* (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 +* Copyright (c) 2008-2009 QUALCOMM USA, INC. +* +* All source code in this file is licensed under the following license +* +* 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, you can find it at http://www.fsf.org +*/ +#ifndef __GSL_DRAWCTXT_H +#define __GSL_DRAWCTXT_H + +/* Flags */ + +#define CTXT_FLAGS_NOT_IN_USE 0x00000000 +#define CTXT_FLAGS_IN_USE 0x00000001 + +/* state shadow memory allocated */ +#define CTXT_FLAGS_STATE_SHADOW 0x00000010 + +/* gmem shadow memory allocated */ +#define CTXT_FLAGS_GMEM_SHADOW 0x00000100 +/* gmem must be copied to shadow */ +#define CTXT_FLAGS_GMEM_SAVE 0x00000200 +/* gmem can be restored from shadow */ +#define CTXT_FLAGS_GMEM_RESTORE 0x00000400 +/* shader must be copied to shadow */ +#define CTXT_FLAGS_SHADER_SAVE 0x00002000 +/* shader can be restored from shadow */ +#define CTXT_FLAGS_SHADER_RESTORE 0x00004000 + +#include "kgsl_sharedmem.h" +#include "yamato_reg.h" + +#define KGSL_MAX_GMEM_SHADOW_BUFFERS 2 + +struct kgsl_device; + +/* types */ + +/* draw context */ +struct gmem_shadow_t { + struct kgsl_memdesc gmemshadow; /* Shadow buffer address */ + + /* 256 KB GMEM surface = 4 bytes-per-pixel x 256 pixels/row x + * 256 rows. */ + /* width & height must be a multiples of 32, in case tiled textures + * are used. */ + enum COLORFORMATX format; + unsigned int size; /* Size of surface used to store GMEM */ + unsigned int width; /* Width of surface used to store GMEM */ + unsigned int height; /* Height of surface used to store GMEM */ + unsigned int pitch; /* Pitch of surface used to store GMEM */ + int offset; + unsigned int offset_x; + unsigned int offset_y; + unsigned int gmem_offset_x; + unsigned int gmem_offset_y; + unsigned int gmem_pitch; /* Pitch value used for GMEM */ + unsigned int *gmem_save_commands; + unsigned int *gmem_restore_commands; + unsigned int gmem_save[3]; + unsigned int gmem_restore[3]; + struct kgsl_memdesc quad_vertices; + struct kgsl_memdesc quad_texcoords; +}; + +struct kgsl_drawctxt { + uint32_t flags; + struct kgsl_pagetable *pagetable; + struct kgsl_memdesc gpustate; + unsigned int reg_save[3]; + unsigned int reg_restore[3]; + unsigned int shader_save[3]; + unsigned int shader_fixup[3]; + unsigned int shader_restore[3]; + unsigned int chicken_restore[3]; + + /* Information of the GMEM shadow that is created in context create */ + struct gmem_shadow_t context_gmem_shadow; + /* User defined GMEM shadow buffers */ + struct gmem_shadow_t user_gmem_shadow[KGSL_MAX_GMEM_SHADOW_BUFFERS]; +}; + + +int kgsl_drawctxt_create(struct kgsl_device *, struct kgsl_pagetable *, + unsigned int flags, + unsigned int *drawctxt_id); + +int kgsl_drawctxt_destroy(struct kgsl_device *device, unsigned int drawctxt_id); + +int kgsl_drawctxt_init(struct kgsl_device *device); + +int kgsl_drawctxt_close(struct kgsl_device *device); + +void kgsl_drawctxt_switch(struct kgsl_device *device, + struct kgsl_drawctxt *drawctxt, + unsigned int flags); +int kgsl_drawctxt_bind_gmem_shadow(struct kgsl_device *device, + unsigned int drawctxt_id, + const struct kgsl_gmem_desc *gmem_desc, + unsigned int shadow_x, + unsigned int shadow_y, + const struct kgsl_buffer_desc + *shadow_buffer, unsigned int buffer_id); + +#endif /* __GSL_DRAWCTXT_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_log.c b/drivers/video/msm/gpu/kgsl/kgsl_log.c new file mode 100644 index 0000000000000..6308087d87110 --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/kgsl_log.c @@ -0,0 +1,292 @@ +/* + * (C) Copyright Advanced Micro Devices, Inc. 2002, 2008 + * Copyright (c) 2008-2009 QUALCOMM USA, INC. + * + * All source code in this file is licensed under the following license + * + * 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, you can find it at http://www.fsf.org + */ +#include +#include "kgsl_log.h" +#include "kgsl_ringbuffer.h" +#include "kgsl_device.h" +#include "kgsl.h" + +/*default log levels is error for everything*/ +#define KGSL_LOG_LEVEL_DEFAULT 3 +#define KGSL_LOG_LEVEL_MAX 7 +unsigned int kgsl_drv_log = KGSL_LOG_LEVEL_DEFAULT; +unsigned int kgsl_cmd_log = KGSL_LOG_LEVEL_DEFAULT; +unsigned int kgsl_ctxt_log = KGSL_LOG_LEVEL_DEFAULT; +unsigned int kgsl_mem_log = KGSL_LOG_LEVEL_DEFAULT; + +unsigned int kgsl_cache_enable; + +#ifdef CONFIG_DEBUG_FS +static int kgsl_log_set(unsigned int *log_val, void *data, u64 val) +{ + *log_val = min((unsigned int)val, (unsigned int)KGSL_LOG_LEVEL_MAX); + return 0; +} + +static int kgsl_drv_log_set(void *data, u64 val) +{ + return kgsl_log_set(&kgsl_drv_log, data, val); +} + +static int kgsl_drv_log_get(void *data, u64 *val) +{ + *val = kgsl_drv_log; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(kgsl_drv_log_fops, kgsl_drv_log_get, + kgsl_drv_log_set, "%llu\n"); + +static int kgsl_cmd_log_set(void *data, u64 val) +{ + return kgsl_log_set(&kgsl_cmd_log, data, val); +} + +static int kgsl_cmd_log_get(void *data, u64 *val) +{ + *val = kgsl_cmd_log; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(kgsl_cmd_log_fops, kgsl_cmd_log_get, + kgsl_cmd_log_set, "%llu\n"); + +static int kgsl_ctxt_log_set(void *data, u64 val) +{ + return kgsl_log_set(&kgsl_ctxt_log, data, val); +} + +static int kgsl_ctxt_log_get(void *data, u64 *val) +{ + *val = kgsl_ctxt_log; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(kgsl_ctxt_log_fops, kgsl_ctxt_log_get, + kgsl_ctxt_log_set, "%llu\n"); + +static int kgsl_mem_log_set(void *data, u64 val) +{ + return kgsl_log_set(&kgsl_mem_log, data, val); +} + +static int kgsl_mem_log_get(void *data, u64 *val) +{ + *val = kgsl_mem_log; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(kgsl_mem_log_fops, kgsl_mem_log_get, + kgsl_mem_log_set, "%llu\n"); + +#ifdef DEBUG +static ssize_t rb_regs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t rb_regs_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0; + struct kgsl_device *device = NULL; + struct kgsl_ringbuffer *rb = NULL; + struct kgsl_rb_debug rb_debug; + + device = &kgsl_driver.yamato_device; + + rb = &device->ringbuffer; + + kgsl_ringbuffer_debug(rb, &rb_debug); + + n += scnprintf(buffer + n, debug_bufmax - n, + "rbbm_status %08x mem_rptr %08x mem_wptr_poll %08x\n", + rb_debug.rbbm_status, + rb_debug.mem_rptr, + rb_debug.mem_wptr_poll); + + n += scnprintf(buffer + n, debug_bufmax - n, + "rb_base %08x rb_cntl %08x rb_rptr_addr %08x" + " rb_rptr %08x rb_rptr_wr %08x\n", + rb_debug.cp_rb_base, + rb_debug.cp_rb_cntl, + rb_debug.cp_rb_rptr_addr, + rb_debug.cp_rb_rptr, + rb_debug.cp_rb_rptr_wr); + + n += scnprintf(buffer + n, debug_bufmax - n, + "rb_wptr %08x rb_wptr_delay %08x rb_wptr_base %08x" + " ib1_base %08x ib1_bufsz %08x\n", + rb_debug.cp_rb_wptr, + rb_debug.cp_rb_wptr_delay, + rb_debug.cp_rb_wptr_base, + rb_debug.cp_ib1_base, + rb_debug.cp_ib1_bufsz); + + n += scnprintf(buffer + n, debug_bufmax - n, + "ib2_base %08x ib2_bufsz %08x st_base %08x" + " st_bufsz %08x cp_me_cntl %08x cp_me_status %08x\n", + rb_debug.cp_ib2_base, + rb_debug.cp_ib2_bufsz, + rb_debug.cp_st_base, + rb_debug.cp_st_bufsz, + rb_debug.cp_me_cntl, + rb_debug.cp_me_status); + + n += scnprintf(buffer + n, debug_bufmax - n, + "csq_cp_rb %08x csq_cp_ib1 %08x csq_cp_ib2 %08x\n", + rb_debug.cp_csq_rb_stat, + rb_debug.cp_csq_ib1_stat, + rb_debug.cp_csq_ib2_stat); + + n += scnprintf(buffer + n, debug_bufmax - n, + "cp_debug %08x cp_stat %08x cp_int_status %08x" + " cp_int_cntl %08x\n", + rb_debug.cp_debug, + rb_debug.cp_stat, + rb_debug.cp_int_status, + rb_debug.cp_int_cntl); + + n += scnprintf(buffer + n, debug_bufmax - n, + "sop_timestamp: %0d eop_timestamp: %d\n", + rb_debug.sop_timestamp, + rb_debug.eop_timestamp); + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static struct file_operations kgsl_rb_regs_fops = { + .read = rb_regs_read, + .open = rb_regs_open, +}; +#endif /*DEBUG*/ + +#ifdef DEBUG +static ssize_t mmu_regs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t mmu_regs_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0; + struct kgsl_device *device = NULL; + struct kgsl_mmu *mmu = NULL; + struct kgsl_mmu_debug mmu_debug; + + device = &kgsl_driver.yamato_device; + + mmu = &device->mmu; + + kgsl_mmu_debug(mmu, &mmu_debug); + + n += scnprintf(buffer + n, debug_bufmax - n, + "config %08x mpu_base %08x mpu_end %08x\n", + mmu_debug.config, + mmu_debug.mpu_base, + mmu_debug.mpu_end); + + n += scnprintf(buffer + n, debug_bufmax - n, + "va_range %08x pt_base %08x\n", + mmu_debug.va_range, + mmu_debug.pt_base); + + n += scnprintf(buffer + n, debug_bufmax - n, + "page_fault %08x trans_error %08x axi_error %08x\n", + mmu_debug.page_fault, + mmu_debug.trans_error, + mmu_debug.axi_error); + + n += scnprintf(buffer + n, debug_bufmax - n, + "interrupt_mask %08x interrupt_status %08x\n", + mmu_debug.interrupt_mask, + mmu_debug.interrupt_status); + + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static struct file_operations kgsl_mmu_regs_fops = { + .read = mmu_regs_read, + .open = mmu_regs_open, +}; +#endif /*DEBUG*/ + +#ifdef CONFIG_MSM_KGSL_MMU +static int kgsl_cache_enable_set(void *data, u64 val) +{ + kgsl_cache_enable = (val != 0); + return 0; +} + +static int kgsl_cache_enable_get(void *data, u64 *val) +{ + *val = kgsl_cache_enable; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(kgsl_cache_enable_fops, kgsl_cache_enable_get, + kgsl_cache_enable_set, "%llu\n"); +#endif + +#endif /* CONFIG_DEBUG_FS */ + +int kgsl_debug_init(void) +{ +#ifdef CONFIG_DEBUG_FS + struct dentry *dent; + dent = debugfs_create_dir("kgsl", 0); + if (IS_ERR(dent)) + return 0; + + debugfs_create_file("log_level_cmd", 0644, dent, 0, + &kgsl_cmd_log_fops); + debugfs_create_file("log_level_ctxt", 0644, dent, 0, + &kgsl_ctxt_log_fops); + debugfs_create_file("log_level_drv", 0644, dent, 0, + &kgsl_drv_log_fops); + debugfs_create_file("log_level_mem", 0644, dent, 0, + &kgsl_mem_log_fops); +#ifdef DEBUG + debugfs_create_file("rb_regs", 0444, dent, 0, + &kgsl_rb_regs_fops); +#endif + +#ifdef DEBUG + debugfs_create_file("mmu_regs", 0444, dent, 0, + &kgsl_mmu_regs_fops); +#endif + +#ifdef CONFIG_MSM_KGSL_MMU + debugfs_create_file("cache_enable", 0644, dent, 0, + &kgsl_cache_enable_fops); +#endif + +#endif /* CONFIG_DEBUG_FS */ + return 0; +} diff --git a/drivers/video/msm/gpu/kgsl/kgsl_log.h b/drivers/video/msm/gpu/kgsl/kgsl_log.h new file mode 100644 index 0000000000000..3869ff74a732c --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/kgsl_log.h @@ -0,0 +1,105 @@ +/* + * (C) Copyright Advanced Micro Devices, Inc. 2002, 2008 + * Copyright (c) 2008-2009 QUALCOMM USA, INC. + * + * All source code in this file is licensed under the following license + * + * 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, you can find it at http://www.fsf.org + */ +#ifndef _GSL_LOG_H +#define _GSL_LOG_H + +#include +#include +#include +#include + +extern unsigned int kgsl_drv_log; +extern unsigned int kgsl_cmd_log; +extern unsigned int kgsl_ctxt_log; +extern unsigned int kgsl_mem_log; + +struct device *kgsl_driver_getdevnode(void); +int kgsl_debug_init(void); + +#define KGSL_LOG_VDBG(lvl, fmt, args...) \ + do { \ + if ((lvl) >= 7) \ + dev_vdbg(kgsl_driver_getdevnode(), "|%s| " fmt, \ + __func__, ##args);\ + } while (0) + +#define KGSL_LOG_DBG(lvl, fmt, args...) \ + do { \ + if ((lvl) >= 7) \ + dev_dbg(kgsl_driver_getdevnode(), "|%s| " fmt, \ + __func__, ##args);\ + } while (0) + +#define KGSL_LOG_INFO(lvl, fmt, args...) \ + do { \ + if ((lvl) >= 6) \ + dev_info(kgsl_driver_getdevnode(), "|%s| " fmt, \ + __func__, ##args);\ + } while (0) + +#define KGSL_LOG_WARN(lvl, fmt, args...) \ + do { \ + if ((lvl) >= 4) \ + dev_warn(kgsl_driver_getdevnode(), "|%s| " fmt, \ + __func__, ##args);\ + } while (0) + +#define KGSL_LOG_ERR(lvl, fmt, args...) \ + do { \ + if ((lvl) >= 3) \ + dev_err(kgsl_driver_getdevnode(), "|%s| " fmt, \ + __func__, ##args);\ + } while (0) + +#define KGSL_LOG_FATAL(lvl, fmt, args...) \ + do { \ + if ((lvl) >= 2) \ + dev_crit(kgsl_driver_getdevnode(), "|%s| " fmt, \ + __func__, ##args);\ + } while (0) + +#define KGSL_DRV_VDBG(fmt, args...) KGSL_LOG_VDBG(kgsl_drv_log, fmt, ##args) +#define KGSL_DRV_DBG(fmt, args...) KGSL_LOG_DBG(kgsl_drv_log, fmt, ##args) +#define KGSL_DRV_INFO(fmt, args...) KGSL_LOG_INFO(kgsl_drv_log, fmt, ##args) +#define KGSL_DRV_WARN(fmt, args...) KGSL_LOG_WARN(kgsl_drv_log, fmt, ##args) +#define KGSL_DRV_ERR(fmt, args...) KGSL_LOG_ERR(kgsl_drv_log, fmt, ##args) +#define KGSL_DRV_FATAL(fmt, args...) KGSL_LOG_FATAL(kgsl_drv_log, fmt, ##args) + +#define KGSL_CMD_VDBG(fmt, args...) KGSL_LOG_VDBG(kgsl_cmd_log, fmt, ##args) +#define KGSL_CMD_DBG(fmt, args...) KGSL_LOG_DBG(kgsl_cmd_log, fmt, ##args) +#define KGSL_CMD_INFO(fmt, args...) KGSL_LOG_INFO(kgsl_cmd_log, fmt, ##args) +#define KGSL_CMD_WARN(fmt, args...) KGSL_LOG_WARN(kgsl_cmd_log, fmt, ##args) +#define KGSL_CMD_ERR(fmt, args...) KGSL_LOG_ERR(kgsl_cmd_log, fmt, ##args) +#define KGSL_CMD_FATAL(fmt, args...) KGSL_LOG_FATAL(kgsl_cmd_log, fmt, ##args) + +#define KGSL_CTXT_VDBG(fmt, args...) KGSL_LOG_VDBG(kgsl_ctxt_log, fmt, ##args) +#define KGSL_CTXT_DBG(fmt, args...) KGSL_LOG_DBG(kgsl_ctxt_log, fmt, ##args) +#define KGSL_CTXT_INFO(fmt, args...) KGSL_LOG_INFO(kgsl_ctxt_log, fmt, ##args) +#define KGSL_CTXT_WARN(fmt, args...) KGSL_LOG_WARN(kgsl_ctxt_log, fmt, ##args) +#define KGSL_CTXT_ERR(fmt, args...) KGSL_LOG_ERR(kgsl_ctxt_log, fmt, ##args) +#define KGSL_CTXT_FATAL(fmt, args...) KGSL_LOG_FATAL(kgsl_ctxt_log, fmt, ##args) + +#define KGSL_MEM_VDBG(fmt, args...) KGSL_LOG_VDBG(kgsl_mem_log, fmt, ##args) +#define KGSL_MEM_DBG(fmt, args...) KGSL_LOG_DBG(kgsl_mem_log, fmt, ##args) +#define KGSL_MEM_INFO(fmt, args...) KGSL_LOG_INFO(kgsl_mem_log, fmt, ##args) +#define KGSL_MEM_WARN(fmt, args...) KGSL_LOG_WARN(kgsl_mem_log, fmt, ##args) +#define KGSL_MEM_ERR(fmt, args...) KGSL_LOG_ERR(kgsl_mem_log, fmt, ##args) +#define KGSL_MEM_FATAL(fmt, args...) KGSL_LOG_FATAL(kgsl_mem_log, fmt, ##args) + +#endif /* _GSL_LOG_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c new file mode 100644 index 0000000000000..7e2016eb84ed3 --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c @@ -0,0 +1,607 @@ +/* + * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 + * Copyright (c) 2008-2009 QUALCOMM USA, INC. + * + * All source code in this file is licensed under the following license + * + * 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, you can find it at http://www.fsf.org + */ +#include +#include +#include +#include + +#include +#include + +#include "kgsl_mmu.h" +#include "kgsl.h" +#include "kgsl_log.h" +#include "yamato_reg.h" + +struct kgsl_pte_debug { + unsigned int read:1; + unsigned int write:1; + unsigned int dirty:1; + unsigned int reserved:9; + unsigned int phyaddr:20; +}; + +#define GSL_PTE_SIZE 4 +#define GSL_PT_EXTRA_ENTRIES 16 + + +#define GSL_PT_PAGE_BITS_MASK 0x00000007 +#define GSL_PT_PAGE_ADDR_MASK (~(KGSL_PAGESIZE - 1)) + +#define GSL_MMU_INT_MASK \ + (MH_INTERRUPT_MASK__AXI_READ_ERROR | \ + MH_INTERRUPT_MASK__AXI_WRITE_ERROR) + +uint32_t kgsl_pt_entry_get(struct kgsl_pagetable *pt, uint32_t va) +{ + return (va - pt->va_base) >> KGSL_PAGESIZE_SHIFT; +} + +uint32_t kgsl_pt_map_get(struct kgsl_pagetable *pt, uint32_t pte) +{ + uint32_t *baseptr = (uint32_t *)pt->base.hostptr; + return baseptr[pte]; +} + +void kgsl_pt_map_set(struct kgsl_pagetable *pt, uint32_t pte, uint32_t val) +{ + uint32_t *baseptr = (uint32_t *)pt->base.hostptr; + baseptr[pte] = val; +} +#define GSL_PT_MAP_DEBUG(pte) ((struct kgsl_pte_debug *) \ + &gsl_pt_map_get(pagetable, pte)) + +void kgsl_pt_map_setbits(struct kgsl_pagetable *pt, uint32_t pte, uint32_t bits) +{ + uint32_t *baseptr = (uint32_t *)pt->base.hostptr; + baseptr[pte] |= bits; +} + +void kgsl_pt_map_setaddr(struct kgsl_pagetable *pt, uint32_t pte, + uint32_t pageaddr) +{ + uint32_t *baseptr = (uint32_t *)pt->base.hostptr; + uint32_t val = baseptr[pte]; + val &= ~GSL_PT_PAGE_ADDR_MASK; + val |= (pageaddr & GSL_PT_PAGE_ADDR_MASK); + baseptr[pte] = val; +} + +void kgsl_pt_map_resetall(struct kgsl_pagetable *pt, uint32_t pte) +{ + uint32_t *baseptr = (uint32_t *)pt->base.hostptr; + baseptr[pte] &= GSL_PT_PAGE_DIRTY; +} + +void kgsl_pt_map_resetbits(struct kgsl_pagetable *pt, uint32_t pte, + uint32_t bits) +{ + uint32_t *baseptr = (uint32_t *)pt->base.hostptr; + baseptr[pte] &= ~(bits & GSL_PT_PAGE_BITS_MASK); +} + +int kgsl_pt_map_isdirty(struct kgsl_pagetable *pt, uint32_t pte) +{ + uint32_t *baseptr = (uint32_t *)pt->base.hostptr; + return baseptr[pte] & GSL_PT_PAGE_DIRTY; +} + +uint32_t kgsl_pt_map_getaddr(struct kgsl_pagetable *pt, uint32_t pte) +{ + uint32_t *baseptr = (uint32_t *)pt->base.hostptr; + return baseptr[pte] & GSL_PT_PAGE_ADDR_MASK; +} + +void kgsl_mh_intrcallback(struct kgsl_device *device) +{ + unsigned int status = 0; + unsigned int reg; + unsigned int axi_error; + struct kgsl_mmu_debug dbg; + + KGSL_MEM_VDBG("enter (device=%p)\n", device); + + kgsl_yamato_regread(device, REG_MH_INTERRUPT_STATUS, &status); + + if (status & MH_INTERRUPT_MASK__AXI_READ_ERROR) { + kgsl_yamato_regread(device, REG_MH_AXI_ERROR, &axi_error); + KGSL_MEM_FATAL("axi read error interrupt (%08x)\n", axi_error); + kgsl_mmu_debug(&device->mmu, &dbg); + } else if (status & MH_INTERRUPT_MASK__AXI_WRITE_ERROR) { + kgsl_yamato_regread(device, REG_MH_AXI_ERROR, &axi_error); + KGSL_MEM_FATAL("axi write error interrupt (%08x)\n", axi_error); + kgsl_mmu_debug(&device->mmu, &dbg); + } else if (status & MH_INTERRUPT_MASK__MMU_PAGE_FAULT) { + kgsl_yamato_regread(device, REG_MH_MMU_PAGE_FAULT, ®); + KGSL_MEM_FATAL("mmu page fault interrupt: %08x\n", reg); + kgsl_mmu_debug(&device->mmu, &dbg); + } else { + KGSL_MEM_DBG("bad bits in REG_MH_INTERRUPT_STATUS %08x\n", + status); + } + + kgsl_yamato_regwrite(device, REG_MH_INTERRUPT_CLEAR, status); + + /*TODO: figure out how to handle errror interupts. + * specifically, page faults should probably nuke the client that + * caused them, but we don't have enough info to figure that out yet. + */ + + KGSL_MEM_VDBG("return\n"); +} + +#ifdef DEBUG +void kgsl_mmu_debug(struct kgsl_mmu *mmu, struct kgsl_mmu_debug *regs) +{ + memset(regs, 0, sizeof(struct kgsl_mmu_debug)); + + kgsl_yamato_regread(mmu->device, REG_MH_MMU_CONFIG, + ®s->config); + kgsl_yamato_regread(mmu->device, REG_MH_MMU_MPU_BASE, + ®s->mpu_base); + kgsl_yamato_regread(mmu->device, REG_MH_MMU_MPU_END, + ®s->mpu_end); + kgsl_yamato_regread(mmu->device, REG_MH_MMU_VA_RANGE, + ®s->va_range); + kgsl_yamato_regread(mmu->device, REG_MH_MMU_PT_BASE, + ®s->pt_base); + kgsl_yamato_regread(mmu->device, REG_MH_MMU_PAGE_FAULT, + ®s->page_fault); + kgsl_yamato_regread(mmu->device, REG_MH_MMU_TRAN_ERROR, + ®s->trans_error); + kgsl_yamato_regread(mmu->device, REG_MH_AXI_ERROR, + ®s->axi_error); + kgsl_yamato_regread(mmu->device, REG_MH_INTERRUPT_MASK, + ®s->interrupt_mask); + kgsl_yamato_regread(mmu->device, REG_MH_INTERRUPT_STATUS, + ®s->interrupt_status); + + KGSL_MEM_DBG("mmu config %08x mpu_base %08x mpu_end %08x\n", + regs->config, regs->mpu_base, regs->mpu_end); + KGSL_MEM_DBG("mmu va_range %08x pt_base %08x \n", + regs->va_range, regs->pt_base); + KGSL_MEM_DBG("mmu page_fault %08x tran_err %08x\n", + regs->page_fault, regs->trans_error); + KGSL_MEM_DBG("mmu int mask %08x int status %08x\n", + regs->interrupt_mask, regs->interrupt_status); +} +#endif + +struct kgsl_pagetable *kgsl_mmu_createpagetableobject(struct kgsl_mmu *mmu) +{ + int status = 0; + struct kgsl_pagetable *pagetable = NULL; + uint32_t flags; + + KGSL_MEM_VDBG("enter (mmu=%p)\n", mmu); + + pagetable = kzalloc(sizeof(struct kgsl_pagetable), GFP_KERNEL); + if (pagetable == NULL) { + KGSL_MEM_ERR("Unable to allocate pagetable object.\n"); + return NULL; + } + + pagetable->mmu = mmu; + pagetable->va_base = mmu->va_base; + pagetable->va_range = mmu->va_range; + pagetable->last_superpte = 0; + pagetable->max_entries = (mmu->va_range >> KGSL_PAGESIZE_SHIFT) + + GSL_PT_EXTRA_ENTRIES; + + pagetable->pool = gen_pool_create(KGSL_PAGESIZE_SHIFT, -1); + if (pagetable->pool == NULL) { + KGSL_MEM_ERR("Unable to allocate virtualaddr pool.\n"); + return NULL; + } + + if (gen_pool_add(pagetable->pool, pagetable->va_base, + pagetable->va_range, -1)) { + KGSL_MEM_ERR("gen_pool_create failed for pagetable %p\n", + pagetable); + goto done; + } + + /* allocate page table memory */ + flags = (KGSL_MEMFLAGS_ALIGN4K | KGSL_MEMFLAGS_CONPHYS + | KGSL_MEMFLAGS_STRICTREQUEST); + status = kgsl_sharedmem_alloc(flags, + pagetable->max_entries * GSL_PTE_SIZE, + &pagetable->base); + + if (status == 0) { + /* reset page table entries + * -- all pte's are marked as not dirty initially + */ + kgsl_sharedmem_set(&pagetable->base, 0, 0, + pagetable->base.size); + } + + KGSL_MEM_VDBG("return %p\n", pagetable); + + return pagetable; +done: + return NULL; +} + +int kgsl_mmu_destroypagetableobject(struct kgsl_pagetable *pagetable) +{ + KGSL_MEM_VDBG("enter (pagetable=%p)\n", pagetable); + + if (pagetable) { + if (pagetable->base.gpuaddr) + kgsl_sharedmem_free(&pagetable->base); + + if (pagetable->pool) { + gen_pool_destroy(pagetable->pool); + pagetable->pool = NULL; + } + + kfree(pagetable); + + } + KGSL_MEM_VDBG("return 0x%08x\n", 0); + + return 0; +} + +int kgsl_mmu_setpagetable(struct kgsl_device *device, + struct kgsl_pagetable *pagetable) +{ + int status = 0; + struct kgsl_mmu *mmu = &device->mmu; + + KGSL_MEM_VDBG("enter (device=%p, pagetable=%p)\n", device, pagetable); + + if (mmu->flags & KGSL_FLAGS_STARTED) { + /* page table not current, then setup mmu to use new + * specified page table + */ + KGSL_MEM_INFO("from %p to %p\n", mmu->hwpagetable, pagetable); + if (mmu->hwpagetable != pagetable) { + mmu->hwpagetable = pagetable; + + /* call device specific set page table */ + status = kgsl_yamato_setpagetable(mmu->device); + } + } + + KGSL_MEM_VDBG("return %d\n", status); + + return status; +} + +int kgsl_mmu_init(struct kgsl_device *device) +{ + /* + * intialize device mmu + * + * call this with the global lock held + */ + int status; + uint32_t flags; + struct kgsl_mmu *mmu = &device->mmu; +#ifdef _DEBUG + struct kgsl_mmu_debug regs; +#endif /* _DEBUG */ + + KGSL_MEM_VDBG("enter (device=%p)\n", device); + + if (mmu->flags & KGSL_FLAGS_INITIALIZED0) { + KGSL_MEM_INFO("MMU already initialized.\n"); + return 0; + } + + mmu->device = device; + +#ifndef CONFIG_MSM_KGSL_MMU + mmu->config = 0x00000000; +#endif + + /* setup MMU and sub-client behavior */ + kgsl_yamato_regwrite(device, REG_MH_MMU_CONFIG, mmu->config); + + /* enable axi interrupts */ + KGSL_MEM_DBG("enabling mmu interrupts mask=0x%08lx\n", + GSL_MMU_INT_MASK); + kgsl_yamato_regwrite(device, REG_MH_INTERRUPT_MASK, GSL_MMU_INT_MASK); + + mmu->flags |= KGSL_FLAGS_INITIALIZED0; + + /* MMU not enabled */ + if ((mmu->config & 0x1) == 0) { + KGSL_MEM_VDBG("return %d\n", 0); + return 0; + } + + /* idle device */ + kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT); + + /* make sure aligned to pagesize */ + BUG_ON(mmu->mpu_base & (KGSL_PAGESIZE - 1)); + BUG_ON((mmu->mpu_base + mmu->mpu_range) & (KGSL_PAGESIZE - 1)); + + /* define physical memory range accessible by the core */ + kgsl_yamato_regwrite(device, REG_MH_MMU_MPU_BASE, + mmu->mpu_base); + kgsl_yamato_regwrite(device, REG_MH_MMU_MPU_END, + mmu->mpu_base + mmu->mpu_range); + + /* enable axi interrupts */ + KGSL_MEM_DBG("enabling mmu interrupts mask=0x%08lx\n", + GSL_MMU_INT_MASK | MH_INTERRUPT_MASK__MMU_PAGE_FAULT); + kgsl_yamato_regwrite(device, REG_MH_INTERRUPT_MASK, + GSL_MMU_INT_MASK | MH_INTERRUPT_MASK__MMU_PAGE_FAULT); + + mmu->flags |= KGSL_FLAGS_INITIALIZED; + + /* sub-client MMU lookups require address translation */ + if ((mmu->config & ~0x1) > 0) { + /*make sure virtual address range is a multiple of 64Kb */ + BUG_ON(mmu->va_range & ((1 << 16) - 1)); + + /* allocate memory used for completing r/w operations that + * cannot be mapped by the MMU + */ + flags = (KGSL_MEMFLAGS_ALIGN4K | KGSL_MEMFLAGS_CONPHYS + | KGSL_MEMFLAGS_STRICTREQUEST); + status = kgsl_sharedmem_alloc(flags, 32, &mmu->dummyspace); + if (status != 0) { + KGSL_MEM_ERR + ("Unable to allocate dummy space memory.\n"); + kgsl_mmu_close(device); + return status; + } + mmu->dummyspace.gpuaddr = mmu->dummyspace.physaddr; + + kgsl_yamato_regwrite(device, + REG_MH_MMU_TRAN_ERROR, + mmu->dummyspace.gpuaddr); + +#ifndef PER_PROCESS_PAGE_TABLE + mmu->hwpagetable = kgsl_mmu_createpagetableobject(mmu); + if (!mmu->hwpagetable) { + KGSL_MEM_ERR("Failed to create global page table\n"); + kgsl_mmu_close(device); + return -ENOMEM; + } + status = kgsl_yamato_setpagetable(device); + if (status) { + kgsl_mmu_close(device); + return status; + } +#endif + kgsl_yamato_tlbinvalidate(device); + + mmu->flags |= KGSL_FLAGS_STARTED; + } + + KGSL_MEM_VDBG("return %d\n", 0); + + return 0; +} + +#ifdef CONFIG_MSM_KGSL_MMU +pte_t *kgsl_get_pte_from_vaddr(unsigned int vaddr) +{ + pgd_t *pgd_ptr = NULL; + pmd_t *pmd_ptr = NULL; + pte_t *pte_ptr = NULL; + + pgd_ptr = pgd_offset(current->mm, vaddr); + if (pgd_none(*pgd) || pgd_bad(*pgd)) { + KGSL_MEM_ERR + ("Invalid pgd entry found while trying to convert virtual " + "address to physical\n"); + return 0; + } + + pmd_ptr = pmd_offset(pgd_ptr, vaddr); + if (pmd_none(*pmd_ptr) || pmd_bad(*pmd_ptr)) { + KGSL_MEM_ERR + ("Invalid pmd entry found while trying to convert virtual " + "address to physical\n"); + return 0; + } + + pte_ptr = pte_offset_map(pmd_ptr, vaddr); + if (!pte_ptr) { + KGSL_MEM_ERR + ("Unable to map pte entry while trying to convert virtual " + "address to physical\n"); + return 0; + } + return pte_ptr; +} + +int kgsl_mmu_map(struct kgsl_pagetable *pagetable, + unsigned int address, + int range, + unsigned int protflags, + unsigned int *gpuaddr) +{ + int numpages; + unsigned int pte, superpte, ptefirst, ptelast, physaddr; + int flushtlb; + struct kgsl_mmu *mmu = NULL; + + KGSL_MEM_VDBG("enter (pt=%p, physaddr=%08x, range=%08d, gpuaddr=%p)\n", + pagetable, address, range, gpuaddr); + + mmu = pagetable->mmu; + + BUG_ON(mmu == NULL); + BUG_ON(protflags & ~(GSL_PT_PAGE_RV | GSL_PT_PAGE_WV)); + BUG_ON(protflags == 0); + BUG_ON(range <= 0); + + *gpuaddr = gen_pool_alloc(pagetable->pool, range); + if (*gpuaddr == 0) { + KGSL_MEM_ERR("gen_pool_alloc failed\n"); + return -ENOMEM; + } + + + + numpages = (range >> KGSL_PAGESIZE_SHIFT); + if (range & (KGSL_PAGESIZE - 1)) + numpages++; + + ptefirst = kgsl_pt_entry_get(pagetable, *gpuaddr); + ptelast = ptefirst + numpages; + + pte = ptefirst; + flushtlb = 0; + + + superpte = ptefirst & (GSL_PT_SUPER_PTE - 1); + for (pte = superpte; pte < ptefirst; pte++) { + /* tlb needs to be flushed only when a dirty superPTE + gets backed */ + if (kgsl_pt_map_isdirty(pagetable, pte)) { + flushtlb = 1; + break; + } + } + + for (pte = ptefirst; pte < ptelast; pte++) { +#ifdef VERBOSE_DEBUG + /* check if PTE exists */ + uint32_t val = kgsl_pt_map_getaddr(pagetable, pte); + BUG_ON(val != 0 && val != GSL_PT_PAGE_DIRTY); +#endif + if (kgsl_pt_map_isdirty(pagetable, pte)) + flushtlb = 1; + /* mark pte as in use */ + physaddr = vmalloc_to_pfn((void *)address); + physaddr <<= PAGE_SHIFT; + + if (physaddr) + kgsl_pt_map_set(pagetable, pte, physaddr | protflags); + else { + KGSL_MEM_ERR + ("Unable to find physaddr for vmallloc address: %x\n", + address); + kgsl_mmu_unmap(pagetable, *gpuaddr, range); + return -EFAULT; + } + address += KGSL_PAGESIZE; + } + + /* set superpte to end of next superpte */ + superpte = (ptelast + (GSL_PT_SUPER_PTE - 1)) + & (GSL_PT_SUPER_PTE - 1); + for (pte = ptelast; pte < superpte; pte++) { + /* tlb needs to be flushed only when a dirty superPTE + gets backed */ + if (kgsl_pt_map_isdirty(pagetable, pte)) { + flushtlb = 1; + break; + } + } + KGSL_MEM_INFO("pt %p p %08x g %08x pte f %d l %d n %d f %d\n", + pagetable, address, *gpuaddr, ptefirst, ptelast, + numpages, flushtlb); + + /* Invalidate tlb only if current page table used by GPU is the + * pagetable that we used to allocate */ + if (pagetable == mmu->hwpagetable) + kgsl_yamato_tlbinvalidate(mmu->device); + + + KGSL_MEM_VDBG("return %d\n", 0); + + return 0; +} + +int +kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, unsigned int gpuaddr, + int range) +{ + unsigned int numpages; + unsigned int pte, ptefirst, ptelast; + + KGSL_MEM_VDBG("enter (pt=%p, gpuaddr=0x%08x, range=%d)\n", + pagetable, gpuaddr, range); + + BUG_ON(range <= 0); + + gen_pool_free(pagetable->pool, gpuaddr, range); + + numpages = (range >> KGSL_PAGESIZE_SHIFT); + if (range & (KGSL_PAGESIZE - 1)) + numpages++; + + ptefirst = kgsl_pt_entry_get(pagetable, gpuaddr); + ptelast = ptefirst + numpages; + + KGSL_MEM_INFO("pt %p gpu %08x pte first %d last %d numpages %d\n", + pagetable, gpuaddr, ptefirst, ptelast, numpages); + + for (pte = ptefirst; pte < ptelast; pte++) { +#ifdef VERBOSE_DEBUG + /* check if PTE exists */ + BUG_ON(!kgsl_pt_map_getaddr(pagetable, pte)); +#endif + kgsl_pt_map_set(pagetable, pte, GSL_PT_PAGE_DIRTY); + } + + KGSL_MEM_VDBG("return %d\n", 0); + + return 0; +} +#endif + +int kgsl_mmu_close(struct kgsl_device *device) +{ + /* + * close device mmu + * + * call this with the global lock held + */ + struct kgsl_mmu *mmu = &device->mmu; +#ifdef _DEBUG + int i; +#endif /* _DEBUG */ + + KGSL_MEM_VDBG("enter (device=%p)\n", device); + + if (mmu->flags & KGSL_FLAGS_INITIALIZED0) { + /* disable mh interrupts */ + KGSL_MEM_DBG("disabling mmu interrupts\n"); + kgsl_yamato_regwrite(device, REG_MH_INTERRUPT_MASK, 0); + + /* disable MMU */ + kgsl_yamato_regwrite(device, REG_MH_MMU_CONFIG, 0x00000000); + + if (mmu->dummyspace.gpuaddr) + kgsl_sharedmem_free(&mmu->dummyspace); + + mmu->flags &= ~KGSL_FLAGS_STARTED; + mmu->flags &= ~KGSL_FLAGS_INITIALIZED; + mmu->flags &= ~KGSL_FLAGS_INITIALIZED0; +#ifndef PER_PROCESS_PAGE_TABLE + kgsl_mmu_destroypagetableobject(mmu->hwpagetable); +#endif + mmu->hwpagetable = NULL; + } + + KGSL_MEM_VDBG("return %d\n", 0); + + return 0; +} diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.h b/drivers/video/msm/gpu/kgsl/kgsl_mmu.h new file mode 100644 index 0000000000000..40d390df5458b --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.h @@ -0,0 +1,137 @@ +/* + * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 + * Copyright (c) 2008-2009 QUALCOMM USA, INC. + * + * All source code in this file is licensed under the following license + * + * 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, you can find it at http://www.fsf.org + */ +#ifndef __GSL_MMU_H +#define __GSL_MMU_H +#include +#include +#include "kgsl_log.h" +#include "kgsl_sharedmem.h" + +#define GSL_PT_SUPER_PTE 8 +#define GSL_PT_PAGE_WV 0x00000001 +#define GSL_PT_PAGE_RV 0x00000002 +#define GSL_PT_PAGE_DIRTY 0x00000004 + +extern unsigned int kgsl_cache_enable; + +struct kgsl_device; + +struct kgsl_mmu_debug { + unsigned int config; + unsigned int mpu_base; + unsigned int mpu_end; + unsigned int va_range; + unsigned int pt_base; + unsigned int page_fault; + unsigned int trans_error; + unsigned int axi_error; + unsigned int interrupt_mask; + unsigned int interrupt_status; +}; + +struct kgsl_ptstats { + int64_t maps; + int64_t unmaps; + int64_t superpteallocs; + int64_t superptefrees; + int64_t ptswitches; + int64_t tlbflushes[KGSL_DEVICE_MAX]; +}; + +struct kgsl_pagetable { + unsigned int refcnt; + struct kgsl_mmu *mmu; + struct kgsl_memdesc base; + uint32_t va_base; + unsigned int va_range; + unsigned int last_superpte; + unsigned int max_entries; + struct gen_pool *pool; +}; + +struct kgsl_mmu { + unsigned int refcnt; + uint32_t flags; + struct kgsl_device *device; + unsigned int config; + uint32_t mpu_base; + int mpu_range; + uint32_t va_base; + unsigned int va_range; + struct kgsl_memdesc dummyspace; + /* current page table object being used by device mmu */ + struct kgsl_pagetable *hwpagetable; +}; + + +static inline int +kgsl_mmu_isenabled(struct kgsl_mmu *mmu) +{ + return ((mmu)->flags & KGSL_FLAGS_STARTED) ? 1 : 0; +} + + +int kgsl_mmu_init(struct kgsl_device *device); + +int kgsl_mmu_close(struct kgsl_device *device); + +struct kgsl_pagetable *kgsl_mmu_createpagetableobject(struct kgsl_mmu *mmu); + +int kgsl_mmu_destroypagetableobject(struct kgsl_pagetable *pagetable); + +int kgsl_mmu_setpagetable(struct kgsl_device *device, + struct kgsl_pagetable *pagetable); + +#ifdef CONFIG_MSM_KGSL_MMU +int kgsl_mmu_map(struct kgsl_pagetable *pagetable, + unsigned int address, + int range, + unsigned int protflags, + unsigned int *gpuaddr); + +int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, + unsigned int gpuaddr, int range); + +pte_t *kgsl_get_pte_from_vaddr(unsigned int vaddr); +#else +static inline int kgsl_mmu_map(struct kgsl_pagetable *pagetable, + unsigned int address, + int range, + unsigned int protflags, + unsigned int *gpuaddr) { return -1; } + +static inline int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, + unsigned int gpuaddr, int range) { return -1; } + +static inline pte_t *kgsl_get_pte_from_vaddr(unsigned int vaddr) {return NULL;} +#endif + +int kgsl_mmu_querystats(struct kgsl_pagetable *pagetable, + struct kgsl_ptstats *stats); + +void kgsl_mh_intrcallback(struct kgsl_device *device); + +#ifdef DEBUG +void kgsl_mmu_debug(struct kgsl_mmu *, struct kgsl_mmu_debug*); +#else +static inline void kgsl_mmu_debug(struct kgsl_mmu *mmu, + struct kgsl_mmu_debug *mmu_debug) { } +#endif /* DEBUG */ + +#endif /* __GSL_MMU_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_pm4types.h b/drivers/video/msm/gpu/kgsl/kgsl_pm4types.h new file mode 100644 index 0000000000000..018f03f9e5167 --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/kgsl_pm4types.h @@ -0,0 +1,181 @@ +/* + * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 + * Copyright (c) 2008-2009 QUALCOMM USA, INC. + * + * All source code in this file is licensed under the following license + * + * 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, you can find it at http://www.fsf.org + */ +#ifndef __GSL_PM4TYPES_H +#define __GSL_PM4TYPES_H + + +#define PM4_PKT_MASK 0xc0000000 + +#define PM4_TYPE0_PKT ((unsigned int)0 << 30) +#define PM4_TYPE1_PKT ((unsigned int)1 << 30) +#define PM4_TYPE2_PKT ((unsigned int)2 << 30) +#define PM4_TYPE3_PKT ((unsigned int)3 << 30) + + +/* type3 packets */ +/* initialize CP's micro-engine */ +#define PM4_ME_INIT 0x48 + +/* skip N 32-bit words to get to the next packet */ +#define PM4_NOP 0x10 + +/* indirect buffer dispatch. prefetch parser uses this packet type to determine +* whether to pre-fetch the IB +*/ +#define PM4_INDIRECT_BUFFER 0x3f + +/* indirect buffer dispatch. same as IB, but init is pipelined */ +#define PM4_INDIRECT_BUFFER_PFD 0x37 + +/* wait for the IDLE state of the engine */ +#define PM4_WAIT_FOR_IDLE 0x26 + +/* wait until a register or memory location is a specific value */ +#define PM4_WAIT_REG_MEM 0x3c + +/* wait until a register location is equal to a specific value */ +#define PM4_WAIT_REG_EQ 0x52 + +/* wait until a register location is >= a specific value */ +#define PM4_WAT_REG_GTE 0x53 + +/* wait until a read completes */ +#define PM4_WAIT_UNTIL_READ 0x5c + +/* wait until all base/size writes from an IB_PFD packet have completed */ +#define PM4_WAIT_IB_PFD_COMPLETE 0x5d + +/* register read/modify/write */ +#define PM4_REG_RMW 0x21 + +/* reads register in chip and writes to memory */ +#define PM4_REG_TO_MEM 0x3e + +/* write N 32-bit words to memory */ +#define PM4_MEM_WRITE 0x3d + +/* write CP_PROG_COUNTER value to memory */ +#define PM4_MEM_WRITE_CNTR 0x4f + +/* conditional execution of a sequence of packets */ +#define PM4_COND_EXEC 0x44 + +/* conditional write to memory or register */ +#define PM4_COND_WRITE 0x45 + +/* generate an event that creates a write to memory when completed */ +#define PM4_EVENT_WRITE 0x46 + +/* generate a VS|PS_done event */ +#define PM4_EVENT_WRITE_SHD 0x58 + +/* generate a cache flush done event */ +#define PM4_EVENT_WRITE_CFL 0x59 + +/* generate a z_pass done event */ +#define PM4_EVENT_WRITE_ZPD 0x5b + + +/* initiate fetch of index buffer and draw */ +#define PM4_DRAW_INDX 0x22 + +/* draw using supplied indices in packet */ +#define PM4_DRAW_INDX_2 0x36 + +/* initiate fetch of index buffer and binIDs and draw */ +#define PM4_DRAW_INDX_BIN 0x37 + +/* initiate fetch of bin IDs and draw using supplied indices */ +#define PM4_DRAW_INDX_2_BIN 0x38 + + +/* begin/end initiator for viz query extent processing */ +#define PM4_VIZ_QUERY 0x23 + +/* fetch state sub-blocks and initiate shader code DMAs */ +#define PM4_SET_STATE 0x25 + +/* load constant into chip and to memory */ +#define PM4_SET_CONSTANT 0x2d + +/* load sequencer instruction memory (pointer-based) */ +#define PM4_IM_LOAD 0x27 + +/* load sequencer instruction memory (code embedded in packet) */ +#define PM4_IM_LOAD_IMMEDIATE 0x2b + +/* load constants from a location in memory */ +#define PM4_LOAD_CONSTANT_CONTEXT 0x2e + +/* selective invalidation of state pointers */ +#define PM4_INVALIDATE_STATE 0x3b + + +/* dynamically changes shader instruction memory partition */ +#define PM4_SET_SHADER_BASES 0x4A + +/* sets the 64-bit BIN_MASK register in the PFP */ +#define PM4_SET_BIN_MASK 0x50 + +/* sets the 64-bit BIN_SELECT register in the PFP */ +#define PM4_SET_BIN_SELECT 0x51 + + +/* updates the current context, if needed */ +#define PM4_CONTEXT_UPDATE 0x5e + +/* generate interrupt from the command stream */ +#define PM4_INTERRUPT 0x40 + + +/* copy sequencer instruction memory to system memory */ +#define PM4_IM_STORE 0x2c + +/* program an offset that will added to the BIN_BASE value of + * the 3D_DRAW_INDX_BIN packet */ +#define PM4_SET_BIN_BASE_OFFSET 0x4B + + +/* packet header building macros */ +#define pm4_type0_packet(regindx, cnt) \ + (PM4_TYPE0_PKT | (((cnt)-1) << 16) | ((regindx) & 0x7FFF)) + +#define pm4_type0_packet_for_sameregister(regindx, cnt) \ + ((PM4_TYPE0_PKT | (((cnt)-1) << 16) | ((1 << 15) | \ + ((regindx) & 0x7FFF))) + +#define pm4_type1_packet(reg0, reg1) \ + (PM4_TYPE1_PKT | ((reg1) << 12) | (reg0)) + +#define pm4_type3_packet(opcode, cnt) \ + (PM4_TYPE3_PKT | (((cnt)-1) << 16) | (((opcode) & 0xFF) << 8)) + +#define pm4_predicated_type3_packet(opcode, cnt) \ + (PM4_TYPE3_PKT | (((cnt)-1) << 16) | (((opcode) & 0xFF) << 8) | 0x1) + +#define pm4_nop_packet(cnt) \ + (PM4_TYPE3_PKT | (((cnt)-1) << 16) | (PM4_NOP << 8)) + + +/* packet headers */ +#define PM4_HDR_ME_INIT pm4_type3_packet(PM4_ME_INIT, 18) +#define PM4_HDR_INDIRECT_BUFFER_PFD pm4_type3_packet(PM4_INDIRECT_BUFFER_PFD, 2) +#define PM4_HDR_INDIRECT_BUFFER pm4_type3_packet(PM4_INDIRECT_BUFFER, 2) + +#endif /* __GSL_PM4TYPES_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c new file mode 100644 index 0000000000000..99a0eb293adce --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c @@ -0,0 +1,809 @@ +/* + * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 + * Copyright (c) 2008-2009 QUALCOMM USA, INC. + * + * All source code in this file is licensed under the following license + * + * 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, you can find it at http://www.fsf.org + */ +#include +#include +#include +#include + +#include "kgsl.h" +#include "kgsl_device.h" +#include "kgsl_log.h" +#include "kgsl_pm4types.h" +#include "kgsl_ringbuffer.h" +#include "kgsl_cmdstream.h" + +#include "yamato_reg.h" + +#define GSL_RB_NOP_SIZEDWORDS 2 +/* protected mode error checking below register address 0x800 +* note: if CP_INTERRUPT packet is used then checking needs +* to change to below register address 0x7C8 +*/ +#define GSL_RB_PROTECTED_MODE_CONTROL 0x200001F2 + +#define GSL_CP_INT_MASK \ + (CP_INT_CNTL__SW_INT_MASK | \ + CP_INT_CNTL__T0_PACKET_IN_IB_MASK | \ + CP_INT_CNTL__OPCODE_ERROR_MASK | \ + CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK | \ + CP_INT_CNTL__RESERVED_BIT_ERROR_MASK | \ + CP_INT_CNTL__IB_ERROR_MASK | \ + CP_INT_CNTL__IB2_INT_MASK | \ + CP_INT_CNTL__IB1_INT_MASK | \ + CP_INT_CNTL__RB_INT_MASK) + +#define YAMATO_PFP_FW "yamato_pfp.fw" +#define YAMATO_PM4_FW "yamato_pm4.fw" + +/* ringbuffer size log2 quadwords equivalent */ +inline unsigned int kgsl_ringbuffer_sizelog2quadwords(unsigned int sizedwords) +{ + unsigned int sizelog2quadwords = 0; + int i = sizedwords >> 1; + + while (i >>= 1) + sizelog2quadwords++; + + return sizelog2quadwords; +} + + +/* functions */ +void kgsl_cp_intrcallback(struct kgsl_device *device) +{ + unsigned int status = 0; + struct kgsl_ringbuffer *rb = &device->ringbuffer; + + KGSL_CMD_VDBG("enter (device=%p)\n", device); + + kgsl_yamato_regread(device, REG_CP_INT_STATUS, &status); + + if (status & CP_INT_CNTL__IB1_INT_MASK) { + /*this is the only used soft interrupt */ + KGSL_CMD_WARN("ringbuffer ib1 interrupt\n"); + wake_up_interruptible_all(&device->ib1_wq); + } + if (status & CP_INT_CNTL__T0_PACKET_IN_IB_MASK) { + KGSL_CMD_FATAL("ringbuffer TO packet in IB interrupt\n"); + kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0); + kgsl_ringbuffer_dump(rb); + } + if (status & CP_INT_CNTL__OPCODE_ERROR_MASK) { + KGSL_CMD_FATAL("ringbuffer opcode error interrupt\n"); + kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0); + kgsl_ringbuffer_dump(rb); + } + if (status & CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK) { + KGSL_CMD_FATAL("ringbuffer protected mode error interrupt\n"); + kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0); + kgsl_ringbuffer_dump(rb); + } + if (status & CP_INT_CNTL__RESERVED_BIT_ERROR_MASK) { + KGSL_CMD_FATAL("ringbuffer reserved bit error interrupt\n"); + kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0); + kgsl_ringbuffer_dump(rb); + } + if (status & CP_INT_CNTL__IB_ERROR_MASK) { + KGSL_CMD_FATAL("ringbuffer IB error interrupt\n"); + kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0); + kgsl_ringbuffer_dump(rb); + } + if (status & CP_INT_CNTL__SW_INT_MASK) + KGSL_CMD_DBG("ringbuffer software interrupt\n"); + + if (status & CP_INT_CNTL__RB_INT_MASK) + KGSL_CMD_DBG("ringbuffer rb interrupt\n"); + + if (status & CP_INT_CNTL__IB2_INT_MASK) + KGSL_CMD_DBG("ringbuffer ib2 interrupt\n"); + + if (status & (~GSL_CP_INT_MASK)) + KGSL_CMD_DBG("bad bits in REG_CP_INT_STATUS %08x\n", status); + + /* only ack bits we understand */ + status &= GSL_CP_INT_MASK; + kgsl_yamato_regwrite(device, REG_CP_INT_ACK, status); + + KGSL_CMD_VDBG("return\n"); +} + + +void kgsl_ringbuffer_watchdog() +{ + struct kgsl_device *device = NULL; + struct kgsl_ringbuffer *rb = NULL; + + device = &kgsl_driver.yamato_device; + + BUG_ON(device == NULL); + + rb = &device->ringbuffer; + + KGSL_CMD_VDBG("enter\n"); + + if ((rb->flags & KGSL_FLAGS_STARTED) == 0) { + KGSL_CMD_VDBG("not started\n"); + return; + } + + GSL_RB_GET_READPTR(rb, &rb->rptr); + + if (rb->rptr == rb->wptr) { + /* clear rptr sample for interval n */ + rb->watchdog.flags &= ~KGSL_FLAGS_ACTIVE; + goto done; + } + /* ringbuffer is currently not empty */ + /* and a rptr sample was taken during interval n-1 */ + if (rb->watchdog.flags & KGSL_FLAGS_ACTIVE) { + /* and the rptr did not advance between + * interval n-1 and n */ + if (rb->rptr == rb->watchdog.rptr_sample) { + /* then the core has hung */ + KGSL_CMD_FATAL("Watchdog detected core hung.\n"); + goto done; + } + /* save rptr sample for interval n */ + rb->watchdog.flags |= KGSL_FLAGS_ACTIVE; + rb->watchdog.rptr_sample = rb->rptr; + } +done: + KGSL_CMD_VDBG("return\n"); +} + +static void kgsl_ringbuffer_submit(struct kgsl_ringbuffer *rb) +{ + unsigned int value; + + BUG_ON(rb->wptr == 0); + + GSL_RB_UPDATE_WPTR_POLLING(rb); + /* Drain write buffer and data memory barrier */ + dsb(); + dmb(); + + /* Memory fence to ensure all data has posted. On some systems, + * like 7x27, the register block is not allocated as strongly ordered + * memory. Adding a memory fence ensures ordering during ringbuffer + * submits.*/ + mb(); + + kgsl_yamato_regwrite(rb->device, REG_CP_RB_WPTR, rb->wptr); + + /* force wptr register to be updated */ + do { + kgsl_yamato_regread(rb->device, REG_CP_RB_WPTR, &value); + } while (value != rb->wptr); + + rb->flags |= KGSL_FLAGS_ACTIVE; +} + +static int +kgsl_ringbuffer_waitspace(struct kgsl_ringbuffer *rb, unsigned int numcmds, + int wptr_ahead) +{ + int nopcount; + unsigned int freecmds; + unsigned int *cmds; + + KGSL_CMD_VDBG("enter (rb=%p, numcmds=%d, wptr_ahead=%d)\n", + rb, numcmds, wptr_ahead); + + /* if wptr ahead, fill the remaining with NOPs */ + if (wptr_ahead) { + /* -1 for header */ + nopcount = rb->sizedwords - rb->wptr - 1; + + cmds = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr; + GSL_RB_WRITE(cmds, pm4_nop_packet(nopcount)); + rb->wptr++; + + kgsl_ringbuffer_submit(rb); + + rb->wptr = 0; + } + + /* wait for space in ringbuffer */ + do { + GSL_RB_GET_READPTR(rb, &rb->rptr); + + freecmds = rb->rptr - rb->wptr; + + } while ((freecmds != 0) && (freecmds < numcmds)); + + KGSL_CMD_VDBG("return %d\n", 0); + + return 0; +} + + +static unsigned int *kgsl_ringbuffer_allocspace(struct kgsl_ringbuffer *rb, + unsigned int numcmds) +{ + unsigned int *ptr = NULL; + int status = 0; + + BUG_ON(numcmds >= rb->sizedwords); + + /* check for available space */ + if (rb->wptr >= rb->rptr) { + /* wptr ahead or equal to rptr */ + /* reserve dwords for nop packet */ + if ((rb->wptr + numcmds) > (rb->sizedwords - + GSL_RB_NOP_SIZEDWORDS)) + status = kgsl_ringbuffer_waitspace(rb, numcmds, 1); + } else { + /* wptr behind rptr */ + if ((rb->wptr + numcmds) >= rb->rptr) + status = kgsl_ringbuffer_waitspace(rb, numcmds, 0); + /* check for remaining space */ + /* reserve dwords for nop packet */ + if ((rb->wptr + numcmds) > (rb->sizedwords - + GSL_RB_NOP_SIZEDWORDS)) + status = kgsl_ringbuffer_waitspace(rb, numcmds, 1); + } + + if (status == 0) { + ptr = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr; + rb->wptr += numcmds; + } + + return ptr; +} + +static int kgsl_ringbuffer_load_pm4_ucode(struct kgsl_device *device) +{ + int status = 0; + int i; + const struct firmware *fw = NULL; + unsigned int *fw_ptr = NULL; + size_t fw_word_size = 0; + + status = request_firmware(&fw, YAMATO_PM4_FW, + kgsl_driver.misc.this_device); + if (status != 0) { + KGSL_DRV_ERR("request_firmware failed for %s with error %d\n", + YAMATO_PM4_FW, status); + goto done; + } + /*this firmware must come in 3 word chunks. plus 1 word of version*/ + if ((fw->size % (sizeof(uint32_t)*3)) != 4) { + KGSL_DRV_ERR("bad firmware size %d.\n", fw->size); + status = -EINVAL; + goto done; + } + fw_ptr = (unsigned int *)fw->data; + fw_word_size = fw->size/sizeof(uint32_t); + KGSL_DRV_INFO("loading pm4 ucode version: %d\n", fw_ptr[0]); + + kgsl_yamato_regwrite(device, REG_CP_DEBUG, 0x02000000); + kgsl_yamato_regwrite(device, REG_CP_ME_RAM_WADDR, 0); + for (i = 1; i < fw_word_size; i++) + kgsl_yamato_regwrite(device, REG_CP_ME_RAM_DATA, fw_ptr[i]); + +done: + release_firmware(fw); + return status; +} + +static int kgsl_ringbuffer_load_pfp_ucode(struct kgsl_device *device) +{ + int status = 0; + int i; + const struct firmware *fw = NULL; + unsigned int *fw_ptr = NULL; + size_t fw_word_size = 0; + + status = request_firmware(&fw, YAMATO_PFP_FW, + kgsl_driver.misc.this_device); + if (status != 0) { + KGSL_DRV_ERR("request_firmware for %s failed with error %d\n", + YAMATO_PFP_FW, status); + return status; + } + /*this firmware must come in 1 word chunks. */ + if ((fw->size % sizeof(uint32_t)) != 0) { + KGSL_DRV_ERR("bad firmware size %d.\n", fw->size); + release_firmware(fw); + return -EINVAL; + } + fw_ptr = (unsigned int *)fw->data; + fw_word_size = fw->size/sizeof(uint32_t); + + KGSL_DRV_INFO("loading pfp ucode version: %d\n", fw_ptr[0]); + + kgsl_yamato_regwrite(device, REG_CP_PFP_UCODE_ADDR, 0); + for (i = 1; i < fw_word_size; i++) + kgsl_yamato_regwrite(device, REG_CP_PFP_UCODE_DATA, fw_ptr[i]); + + release_firmware(fw); + return status; +} + +static int kgsl_ringbuffer_start(struct kgsl_ringbuffer *rb) +{ + int status; + /*cp_rb_cntl_u cp_rb_cntl; */ + union reg_cp_rb_cntl cp_rb_cntl; + unsigned int *cmds, rb_cntl; + struct kgsl_device *device = rb->device; + + KGSL_CMD_VDBG("enter (rb=%p)\n", rb); + + if (rb->flags & KGSL_FLAGS_STARTED) { + KGSL_CMD_VDBG("return %d\n", 0); + return 0; + } + kgsl_sharedmem_set(&rb->memptrs_desc, 0, 0, + sizeof(struct kgsl_rbmemptrs)); + + kgsl_sharedmem_set(&rb->buffer_desc, 0, 0x12341234, + (rb->sizedwords << 2)); + + kgsl_yamato_regwrite(device, REG_CP_RB_WPTR_BASE, + (rb->memptrs_desc.gpuaddr + + GSL_RB_MEMPTRS_WPTRPOLL_OFFSET)); + + /* setup WPTR delay */ + kgsl_yamato_regwrite(device, REG_CP_RB_WPTR_DELAY, 0 /*0x70000010 */); + + /*setup REG_CP_RB_CNTL */ + kgsl_yamato_regread(device, REG_CP_RB_CNTL, &rb_cntl); + cp_rb_cntl.val = rb_cntl; + /* size of ringbuffer */ + cp_rb_cntl.f.rb_bufsz = + kgsl_ringbuffer_sizelog2quadwords(rb->sizedwords); + /* quadwords to read before updating mem RPTR */ + cp_rb_cntl.f.rb_blksz = rb->blksizequadwords; + cp_rb_cntl.f.rb_poll_en = GSL_RB_CNTL_POLL_EN; /* WPTR polling */ + /* mem RPTR writebacks */ + cp_rb_cntl.f.rb_no_update = GSL_RB_CNTL_NO_UPDATE; + + kgsl_yamato_regwrite(device, REG_CP_RB_CNTL, cp_rb_cntl.val); + + kgsl_yamato_regwrite(device, REG_CP_RB_BASE, rb->buffer_desc.gpuaddr); + + kgsl_yamato_regwrite(device, REG_CP_RB_RPTR_ADDR, + rb->memptrs_desc.gpuaddr + + GSL_RB_MEMPTRS_RPTR_OFFSET); + + /* explicitly clear all cp interrupts */ + kgsl_yamato_regwrite(device, REG_CP_INT_ACK, 0xFFFFFFFF); + + /* setup scratch/timestamp */ + kgsl_yamato_regwrite(device, REG_SCRATCH_ADDR, + device->memstore.gpuaddr + + KGSL_DEVICE_MEMSTORE_OFFSET(soptimestamp)); + + kgsl_yamato_regwrite(device, REG_SCRATCH_UMSK, + GSL_RB_MEMPTRS_SCRATCH_MASK); + + /* load the CP ucode */ + + status = kgsl_ringbuffer_load_pm4_ucode(device); + if (status != 0) { + KGSL_DRV_ERR("kgsl_ringbuffer_load_pm4_ucode failed %d\n", + status); + return status; + } + + + /* load the prefetch parser ucode */ + status = kgsl_ringbuffer_load_pfp_ucode(device); + if (status != 0) { + KGSL_DRV_ERR("kgsl_ringbuffer_load_pm4_ucode failed %d\n", + status); + return status; + } + + kgsl_yamato_regwrite(device, REG_CP_QUEUE_THRESHOLDS, 0x000C0804); + + rb->rptr = 0; + rb->wptr = 0; + + rb->timestamp = 0; + GSL_RB_INIT_TIMESTAMP(rb); + + INIT_LIST_HEAD(&rb->memqueue); + + /* clear ME_HALT to start micro engine */ + kgsl_yamato_regwrite(device, REG_CP_ME_CNTL, 0); + + /* ME_INIT */ + cmds = kgsl_ringbuffer_allocspace(rb, 19); + + GSL_RB_WRITE(cmds, PM4_HDR_ME_INIT); + /* All fields present (bits 9:0) */ + GSL_RB_WRITE(cmds, 0x000003ff); + /* Disable/Enable Real-Time Stream processing (present but ignored) */ + GSL_RB_WRITE(cmds, 0x00000000); + /* Enable (2D <-> 3D) implicit synchronization (present but ignored) */ + GSL_RB_WRITE(cmds, 0x00000000); + + GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_RB_SURFACE_INFO)); + GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SC_WINDOW_OFFSET)); + GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_VGT_MAX_VTX_INDX)); + GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_SQ_PROGRAM_CNTL)); + GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_RB_DEPTHCONTROL)); + GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SU_POINT_SIZE)); + GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SC_LINE_CNTL)); + GSL_RB_WRITE(cmds, + GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SU_POLY_OFFSET_FRONT_SCALE)); + + /* Vertex and Pixel Shader Start Addresses in instructions + * (3 DWORDS per instruction) */ + GSL_RB_WRITE(cmds, 0x80000180); + /* Maximum Contexts */ + GSL_RB_WRITE(cmds, 0x00000001); + /* Write Confirm Interval and The CP will wait the + * wait_interval * 16 clocks between polling */ + GSL_RB_WRITE(cmds, 0x00000000); + + /* NQ and External Memory Swap */ + GSL_RB_WRITE(cmds, 0x00000000); + /* Protected mode error checking */ + GSL_RB_WRITE(cmds, GSL_RB_PROTECTED_MODE_CONTROL); + /* Disable header dumping and Header dump address */ + GSL_RB_WRITE(cmds, 0x00000000); + /* Header dump size */ + GSL_RB_WRITE(cmds, 0x00000000); + + kgsl_ringbuffer_submit(rb); + + /* idle device to validate ME INIT */ + status = kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT); + + KGSL_CMD_DBG("enabling CP interrupts: mask %08lx\n", GSL_CP_INT_MASK); + kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, GSL_CP_INT_MASK); + if (status == 0) + rb->flags |= KGSL_FLAGS_STARTED; + + KGSL_CMD_VDBG("return %d\n", status); + + return status; +} + +static int kgsl_ringbuffer_stop(struct kgsl_ringbuffer *rb) +{ + KGSL_CMD_VDBG("enter (rb=%p)\n", rb); + + if (rb->flags & KGSL_FLAGS_STARTED) { + KGSL_CMD_DBG("disabling CP interrupts: mask %08x\n", 0); + kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0); + + /* ME_HALT */ + kgsl_yamato_regwrite(rb->device, REG_CP_ME_CNTL, 0x10000000); + + rb->flags &= ~KGSL_FLAGS_STARTED; + kgsl_ringbuffer_dump(rb); + } + + KGSL_CMD_VDBG("return %d\n", 0); + + return 0; +} + +int kgsl_ringbuffer_init(struct kgsl_device *device) +{ + int status; + uint32_t flags; + struct kgsl_ringbuffer *rb = &device->ringbuffer; + + KGSL_CMD_VDBG("enter (device=%p)\n", device); + + rb->device = device; + rb->sizedwords = (2 << kgsl_cfg_rb_sizelog2quadwords); + rb->blksizequadwords = kgsl_cfg_rb_blksizequadwords; + + /* allocate memory for ringbuffer, needs to be double octword aligned + * align on page from contiguous physical memory + */ + flags = + (KGSL_MEMFLAGS_ALIGNPAGE | KGSL_MEMFLAGS_CONPHYS | + KGSL_MEMFLAGS_STRICTREQUEST); + + status = kgsl_sharedmem_alloc(flags, (rb->sizedwords << 2), + &rb->buffer_desc); + + if (status != 0) { + kgsl_ringbuffer_close(rb); + KGSL_CMD_VDBG("return %d\n", status); + return status; + } + + /* allocate memory for polling and timestamps */ + flags = (KGSL_MEMFLAGS_ALIGN32 | KGSL_MEMFLAGS_CONPHYS); + + status = kgsl_sharedmem_alloc(flags, sizeof(struct kgsl_rbmemptrs), + &rb->memptrs_desc); + + if (status != 0) { + kgsl_ringbuffer_close(rb); + KGSL_CMD_VDBG("return %d\n", status); + return status; + } + + /* overlay structure on memptrs memory */ + rb->memptrs = (struct kgsl_rbmemptrs *) rb->memptrs_desc.hostptr; + + rb->flags |= KGSL_FLAGS_INITIALIZED; + + status = kgsl_ringbuffer_start(rb); + if (status != 0) { + kgsl_ringbuffer_close(rb); + KGSL_CMD_VDBG("return %d\n", status); + return status; + } + + KGSL_CMD_VDBG("return %d\n", 0); + return 0; +} + +int kgsl_ringbuffer_close(struct kgsl_ringbuffer *rb) +{ + KGSL_CMD_VDBG("enter (rb=%p)\n", rb); + + kgsl_cmdstream_memqueue_drain(rb->device); + + kgsl_ringbuffer_stop(rb); + + if (rb->buffer_desc.hostptr) + kgsl_sharedmem_free(&rb->buffer_desc); + + if (rb->memptrs_desc.hostptr) + kgsl_sharedmem_free(&rb->memptrs_desc); + + rb->flags &= ~KGSL_FLAGS_INITIALIZED; + + memset(rb, 0, sizeof(struct kgsl_ringbuffer)); + + KGSL_CMD_VDBG("return %d\n", 0); + return 0; +} + +static uint32_t +kgsl_ringbuffer_addcmds(struct kgsl_ringbuffer *rb, + int pmodeoff, unsigned int *cmds, + int sizedwords) +{ + unsigned int pmodesizedwords; + unsigned int *ringcmds; + unsigned int timestamp; + + /* reserve space to temporarily turn off protected mode + * error checking if needed + */ + pmodesizedwords = pmodeoff ? 8 : 0; + + ringcmds = kgsl_ringbuffer_allocspace(rb, + pmodesizedwords + sizedwords + 6); + + if (pmodeoff) { + /* disable protected mode error checking */ + *ringcmds++ = pm4_type3_packet(PM4_ME_INIT, 2); + *ringcmds++ = 0x00000080; + *ringcmds++ = 0x00000000; + } + + memcpy(ringcmds, cmds, (sizedwords << 2)); + + ringcmds += sizedwords; + + if (pmodeoff) { + *ringcmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *ringcmds++ = 0; + + /* re-enable protected mode error checking */ + *ringcmds++ = pm4_type3_packet(PM4_ME_INIT, 2); + *ringcmds++ = 0x00000080; + *ringcmds++ = GSL_RB_PROTECTED_MODE_CONTROL; + } + + rb->timestamp++; + timestamp = rb->timestamp; + + /* start-of-pipeline and end-of-pipeline timestamps */ + *ringcmds++ = pm4_type0_packet(REG_CP_TIMESTAMP, 1); + *ringcmds++ = rb->timestamp; + *ringcmds++ = pm4_type3_packet(PM4_EVENT_WRITE, 3); + *ringcmds++ = CACHE_FLUSH_TS; + *ringcmds++ = + (rb->device->memstore.gpuaddr + + KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp)); + *ringcmds++ = rb->timestamp; + + kgsl_ringbuffer_submit(rb); + + GSL_RB_STATS(rb->stats.words_total += sizedwords); + GSL_RB_STATS(rb->stats.issues++); + + KGSL_CMD_VDBG("return %d\n", timestamp); + + /* return timestamp of issued coREG_ands */ + return timestamp; +} + +uint32_t +kgsl_ringbuffer_issuecmds(struct kgsl_device *device, + int pmodeoff, + unsigned int *cmds, + int sizedwords) +{ + unsigned int timestamp; + struct kgsl_ringbuffer *rb = &device->ringbuffer; + + KGSL_CMD_VDBG("enter (device->id=%d, pmodeoff=%d, cmds=%p, " + "sizedwords=%d)\n", device->id, pmodeoff, cmds, sizedwords); + + timestamp = kgsl_ringbuffer_addcmds(rb, pmodeoff, cmds, sizedwords); + + KGSL_CMD_VDBG("return %d\n)", timestamp); + return timestamp; +} + +int +kgsl_ringbuffer_issueibcmds(struct kgsl_device *device, + int drawctxt_index, + uint32_t ibaddr, + int sizedwords, + uint32_t *timestamp, + unsigned int flags) +{ + unsigned int link[3]; + + KGSL_CMD_VDBG("enter (device_id=%d, drawctxt_index=%d, ibaddr=0x%08x," + " sizedwords=%d, timestamp=%p)\n", + device->id, drawctxt_index, ibaddr, + sizedwords, timestamp); + + if (!(device->ringbuffer.flags & KGSL_FLAGS_STARTED)) { + KGSL_CMD_VDBG("return %d\n", -EINVAL); + return -EINVAL; + } + + BUG_ON(ibaddr == 0); + BUG_ON(sizedwords == 0); + + link[0] = PM4_HDR_INDIRECT_BUFFER_PFD; + link[1] = ibaddr; + link[2] = sizedwords; + + kgsl_drawctxt_switch(device, &device->drawctxt[drawctxt_index], flags); + + *timestamp = kgsl_ringbuffer_addcmds(&device->ringbuffer, + 0, &link[0], 3); + + + KGSL_CMD_INFO("ctxt %d g %08x sd %d ts %d\n", + drawctxt_index, ibaddr, sizedwords, *timestamp); + + KGSL_CMD_VDBG("return %d\n", 0); + + return 0; +} + + +#ifdef DEBUG +void kgsl_ringbuffer_debug(struct kgsl_ringbuffer *rb, + struct kgsl_rb_debug *rb_debug) +{ + memset(rb_debug, 0, sizeof(struct kgsl_rb_debug)); + + rb_debug->mem_rptr = rb->memptrs->rptr; + rb_debug->mem_wptr_poll = rb->memptrs->wptr_poll; + kgsl_yamato_regread(rb->device, REG_CP_RB_BASE, + (unsigned int *)&rb_debug->cp_rb_base); + kgsl_yamato_regread(rb->device, REG_CP_RB_CNTL, + (unsigned int *)&rb_debug->cp_rb_cntl); + kgsl_yamato_regread(rb->device, REG_CP_RB_RPTR_ADDR, + (unsigned int *)&rb_debug->cp_rb_rptr_addr); + kgsl_yamato_regread(rb->device, REG_CP_RB_RPTR, + (unsigned int *)&rb_debug->cp_rb_rptr); + kgsl_yamato_regread(rb->device, REG_CP_RB_RPTR_WR, + (unsigned int *)&rb_debug->cp_rb_rptr_wr); + kgsl_yamato_regread(rb->device, REG_CP_RB_WPTR, + (unsigned int *)&rb_debug->cp_rb_wptr); + kgsl_yamato_regread(rb->device, REG_CP_RB_WPTR_DELAY, + (unsigned int *)&rb_debug->cp_rb_wptr_delay); + kgsl_yamato_regread(rb->device, REG_CP_RB_WPTR_BASE, + (unsigned int *)&rb_debug->cp_rb_wptr_base); + kgsl_yamato_regread(rb->device, REG_CP_IB1_BASE, + (unsigned int *)&rb_debug->cp_ib1_base); + kgsl_yamato_regread(rb->device, REG_CP_IB1_BUFSZ, + (unsigned int *)&rb_debug->cp_ib1_bufsz); + kgsl_yamato_regread(rb->device, REG_CP_IB2_BASE, + (unsigned int *)&rb_debug->cp_ib2_base); + kgsl_yamato_regread(rb->device, REG_CP_IB2_BUFSZ, + (unsigned int *)&rb_debug->cp_ib2_bufsz); + kgsl_yamato_regread(rb->device, REG_CP_ST_BASE, + (unsigned int *)&rb_debug->cp_st_base); + kgsl_yamato_regread(rb->device, REG_CP_ST_BUFSZ, + (unsigned int *)&rb_debug->cp_st_bufsz); + kgsl_yamato_regread(rb->device, REG_CP_CSQ_RB_STAT, + (unsigned int *)&rb_debug->cp_csq_rb_stat); + kgsl_yamato_regread(rb->device, REG_CP_CSQ_IB1_STAT, + (unsigned int *)&rb_debug->cp_csq_ib1_stat); + kgsl_yamato_regread(rb->device, REG_CP_CSQ_IB2_STAT, + (unsigned int *)&rb_debug->cp_csq_ib2_stat); + kgsl_yamato_regread(rb->device, REG_SCRATCH_UMSK, + (unsigned int *)&rb_debug->scratch_umsk); + kgsl_yamato_regread(rb->device, REG_SCRATCH_ADDR, + (unsigned int *)&rb_debug->scratch_addr); + kgsl_yamato_regread(rb->device, REG_CP_ME_CNTL, + (unsigned int *)&rb_debug->cp_me_cntl); + kgsl_yamato_regread(rb->device, REG_CP_ME_STATUS, + (unsigned int *)&rb_debug->cp_me_status); + kgsl_yamato_regread(rb->device, REG_CP_DEBUG, + (unsigned int *)&rb_debug->cp_debug); + kgsl_yamato_regread(rb->device, REG_CP_STAT, + (unsigned int *)&rb_debug->cp_stat); + kgsl_yamato_regread(rb->device, REG_CP_INT_STATUS, + (unsigned int *)&rb_debug->cp_int_status); + kgsl_yamato_regread(rb->device, REG_CP_INT_CNTL, + (unsigned int *)&rb_debug->cp_int_cntl); + kgsl_yamato_regread(rb->device, REG_RBBM_STATUS, + (unsigned int *)&rb_debug->rbbm_status); + kgsl_yamato_regread(rb->device, REG_RBBM_INT_STATUS, + (unsigned int *)&rb_debug->rbbm_int_status); + GSL_RB_GET_SOP_TIMESTAMP(rb, (unsigned int *)&rb_debug->sop_timestamp); + GSL_RB_GET_EOP_TIMESTAMP(rb, (unsigned int *)&rb_debug->eop_timestamp); + +} +#endif /*DEBUG*/ + +#ifdef DEBUG +void kgsl_ringbuffer_dump(struct kgsl_ringbuffer *rb) +{ + struct kgsl_rb_debug rb_debug; + kgsl_ringbuffer_debug(rb, &rb_debug); + + KGSL_CMD_DBG("rbbm_status %08x rbbm_int_status %08x" + " mem_rptr %08x mem_wptr_poll %08x\n", + rb_debug.rbbm_status, + rb_debug.rbbm_int_status, + rb_debug.mem_rptr, rb_debug.mem_wptr_poll); + + KGSL_CMD_DBG("rb_base %08x rb_cntl %08x rb_rptr_addr %08x rb_rptr %08x" + " rb_rptr_wr %08x\n", + rb_debug.cp_rb_base, rb_debug.cp_rb_cntl, + rb_debug.cp_rb_rptr_addr, rb_debug.cp_rb_rptr, + rb_debug.cp_rb_rptr_wr); + + KGSL_CMD_DBG("rb_wptr %08x rb_wptr_delay %08x rb_wptr_base %08x" + " ib1_base %08x ib1_bufsz %08x\n", + rb_debug.cp_rb_wptr, rb_debug.cp_rb_wptr_delay, + rb_debug.cp_rb_wptr_base, rb_debug.cp_ib1_base, + rb_debug.cp_ib1_bufsz); + + KGSL_CMD_DBG("ib2_base %08x ib2_bufsz %08x st_base %08x st_bufsz %08x" + " cp_me_cntl %08x cp_me_status %08x\n", + rb_debug.cp_ib2_base, rb_debug.cp_ib2_bufsz, + rb_debug.cp_st_base, rb_debug.cp_st_bufsz, + rb_debug.cp_me_cntl, rb_debug.cp_me_status); + + KGSL_CMD_DBG("cp_debug %08x cp_stat %08x cp_int_status %08x" + " cp_int_cntl %08x\n", + rb_debug.cp_debug, rb_debug.cp_stat, + rb_debug.cp_int_status, rb_debug.cp_int_cntl); + + KGSL_CMD_DBG("sop_timestamp: %d eop_timestamp: %d\n", + rb_debug.sop_timestamp, rb_debug.eop_timestamp); + +} +#endif /* DEBUG */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.h b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.h new file mode 100644 index 0000000000000..0d060209de4d8 --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.h @@ -0,0 +1,254 @@ +/* + * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 + * Copyright (c) 2008-2009 QUALCOMM USA, INC. + * + * All source code in this file is licensed under the following license + * + * 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, you can find it at http://www.fsf.org + */ +#ifndef __GSL_RINGBUFFER_H +#define __GSL_RINGBUFFER_H + +#include +#include +#include +#include "kgsl_log.h" +#include "kgsl_sharedmem.h" +#include "yamato_reg.h" + +#define GSL_STATS_RINGBUFFER + +#define GSL_RB_USE_MEM_RPTR +#define GSL_RB_USE_MEM_TIMESTAMP +#define GSL_DEVICE_SHADOW_MEMSTORE_TO_USER + +/* ringbuffer sizes log2quadword */ +#define GSL_RB_SIZE_8 0 +#define GSL_RB_SIZE_16 1 +#define GSL_RB_SIZE_32 2 +#define GSL_RB_SIZE_64 3 +#define GSL_RB_SIZE_128 4 +#define GSL_RB_SIZE_256 5 +#define GSL_RB_SIZE_512 6 +#define GSL_RB_SIZE_1K 7 +#define GSL_RB_SIZE_2K 8 +#define GSL_RB_SIZE_4K 9 +#define GSL_RB_SIZE_8K 10 +#define GSL_RB_SIZE_16K 11 +#define GSL_RB_SIZE_32K 12 +#define GSL_RB_SIZE_64K 13 +#define GSL_RB_SIZE_128K 14 +#define GSL_RB_SIZE_256K 15 +#define GSL_RB_SIZE_512K 16 +#define GSL_RB_SIZE_1M 17 +#define GSL_RB_SIZE_2M 18 +#define GSL_RB_SIZE_4M 19 + +/* Yamato ringbuffer config*/ +static const unsigned int kgsl_cfg_rb_sizelog2quadwords = GSL_RB_SIZE_32K; +static const unsigned int kgsl_cfg_rb_blksizequadwords = GSL_RB_SIZE_16; + +/* CP timestamp register */ +#define REG_CP_TIMESTAMP REG_SCRATCH_REG0 + + +struct kgsl_device; +struct kgsl_drawctxt; +struct kgsl_ringbuffer; + +struct kgsl_rb_debug { + unsigned int pm4_ucode_rel; + unsigned int pfp_ucode_rel; + unsigned int mem_wptr_poll; + unsigned int mem_rptr; + unsigned int cp_rb_base; + unsigned int cp_rb_cntl; + unsigned int cp_rb_rptr_addr; + unsigned int cp_rb_rptr; + unsigned int cp_rb_rptr_wr; + unsigned int cp_rb_wptr; + unsigned int cp_rb_wptr_delay; + unsigned int cp_rb_wptr_base; + unsigned int cp_ib1_base; + unsigned int cp_ib1_bufsz; + unsigned int cp_ib2_base; + unsigned int cp_ib2_bufsz; + unsigned int cp_st_base; + unsigned int cp_st_bufsz; + unsigned int cp_csq_rb_stat; + unsigned int cp_csq_ib1_stat; + unsigned int cp_csq_ib2_stat; + unsigned int scratch_umsk; + unsigned int scratch_addr; + unsigned int cp_me_cntl; + unsigned int cp_me_status; + unsigned int cp_debug; + unsigned int cp_stat; + unsigned int cp_int_status; + unsigned int cp_int_cntl; + unsigned int rbbm_status; + unsigned int rbbm_int_status; + unsigned int sop_timestamp; + unsigned int eop_timestamp; +}; +#ifdef DEBUG +void kgsl_ringbuffer_debug(struct kgsl_ringbuffer *rb, + struct kgsl_rb_debug *rb_debug); + +void kgsl_ringbuffer_dump(struct kgsl_ringbuffer *rb); +#else +static inline void kgsl_ringbuffer_debug(struct kgsl_ringbuffer *rb, + struct kgsl_rb_debug *rb_debug) +{ +} + +static inline void kgsl_ringbuffer_dump(struct kgsl_ringbuffer *rb) +{ +} +#endif + +struct kgsl_rbwatchdog { + uint32_t flags; + unsigned int rptr_sample; +}; + +#define GSL_RB_MEMPTRS_SCRATCH_COUNT 8 +struct kgsl_rbmemptrs { + volatile int rptr; + volatile int wptr_poll; +} __attribute__ ((packed)); + +#define GSL_RB_MEMPTRS_RPTR_OFFSET \ + (offsetof(struct kgsl_rbmemptrs, rptr)) + +#define GSL_RB_MEMPTRS_WPTRPOLL_OFFSET \ + (offsetof(struct kgsl_rbmemptrs, wptr_poll)) + +struct kgsl_rbstats { + int64_t issues; + int64_t words_total; +}; + + +struct kgsl_ringbuffer { + struct kgsl_device *device; + uint32_t flags; + + struct kgsl_memdesc buffer_desc; + + struct kgsl_memdesc memptrs_desc; + struct kgsl_rbmemptrs *memptrs; + + /*ringbuffer size */ + unsigned int sizedwords; + unsigned int blksizequadwords; + + unsigned int wptr; /* write pointer offset in dwords from baseaddr */ + unsigned int rptr; /* read pointer offset in dwords from baseaddr */ + uint32_t timestamp; + + /* queue of memfrees pending timestamp elapse */ + struct list_head memqueue; + + struct kgsl_rbwatchdog watchdog; + +#ifdef GSL_STATS_RINGBUFFER + struct kgsl_rbstats stats; +#endif /* GSL_STATS_RINGBUFFER */ + +}; + +/* dword base address of the GFX decode space */ +#define GSL_HAL_SUBBLOCK_OFFSET(reg) ((unsigned int)((reg) - (0x2000))) + +#define GSL_RB_WRITE(ring, data) \ + do { \ + mb(); \ + writel(data, ring); \ + ring++; \ + } while (0) + +/* timestamp */ +#ifdef GSL_DEVICE_SHADOW_MEMSTORE_TO_USER +#define GSL_RB_USE_MEM_TIMESTAMP +#endif /* GSL_DEVICE_SHADOW_MEMSTORE_TO_USER */ + +#ifdef GSL_RB_USE_MEM_TIMESTAMP +/* enable timestamp (...scratch0) memory shadowing */ +#define GSL_RB_MEMPTRS_SCRATCH_MASK 0x1 +#define GSL_RB_INIT_TIMESTAMP(rb) + +#else +#define GSL_RB_MEMPTRS_SCRATCH_MASK 0x0 +#define GSL_RB_INIT_TIMESTAMP(rb) \ + kgsl_yamato_regwrite((rb)->device->id, REG_CP_TIMESTAMP, 0) + +#endif /* GSL_RB_USE_MEMTIMESTAMP */ + +/* mem rptr */ +#ifdef GSL_RB_USE_MEM_RPTR +#define GSL_RB_CNTL_NO_UPDATE 0x0 /* enable */ +#define GSL_RB_GET_READPTR(rb, data) \ + do { \ + *(data) = (rb)->memptrs->rptr; \ + } while (0) +#else +#define GSL_RB_CNTL_NO_UPDATE 0x1 /* disable */ +#define GSL_RB_GET_READPTR(rb, data) \ + do { \ + kgsl_yamato_regread((rb)->device->id, REG_CP_RB_RPTR, (data)); \ + } while (0) +#endif /* GSL_RB_USE_MEMRPTR */ + +/* wptr polling */ +#ifdef GSL_RB_USE_WPTR_POLLING +#define GSL_RB_CNTL_POLL_EN 0x1 /* enable */ +#define GSL_RB_UPDATE_WPTR_POLLING(rb) \ + do { (rb)->memptrs->wptr_poll = (rb)->wptr; } while (0) +#else +#define GSL_RB_CNTL_POLL_EN 0x0 /* disable */ +#define GSL_RB_UPDATE_WPTR_POLLING(rb) +#endif /* GSL_RB_USE_WPTR_POLLING */ + +/* stats */ +#ifdef GSL_STATS_RINGBUFFER +#define GSL_RB_STATS(x) x +#else +#define GSL_RB_STATS(x) +#endif /* GSL_STATS_RINGBUFFER */ + +struct kgsl_pmem_entry; + +int kgsl_ringbuffer_issueibcmds(struct kgsl_device *, int drawctxt_index, + uint32_t ibaddr, int sizedwords, + uint32_t *timestamp, + unsigned int flags); + +int kgsl_ringbuffer_init(struct kgsl_device *device); + +int kgsl_ringbuffer_close(struct kgsl_ringbuffer *rb); + +uint32_t kgsl_ringbuffer_issuecmds(struct kgsl_device *device, + int pmodeoff, + unsigned int *cmdaddr, + int sizedwords); + +int kgsl_ringbuffer_gettimestampshadow(struct kgsl_device *device, + unsigned int *sopaddr, + unsigned int *eopaddr); + +void kgsl_ringbuffer_watchdog(void); + +void kgsl_cp_intrcallback(struct kgsl_device *device); + +#endif /* __GSL_RINGBUFFER_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c new file mode 100644 index 0000000000000..7b6cf251919b2 --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c @@ -0,0 +1,282 @@ +/* + * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 + * Copyright (c) 2008-2009 QUALCOMM USA, INC. + * + * All source code in this file is licensed under the following license + * + * 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, you can find it at http://www.fsf.org + */ +#include +#include +#include + +#include "kgsl_sharedmem.h" +#include "kgsl_device.h" +#include "kgsl.h" +#include "kgsl_log.h" + +/* block alignment shift count */ +static inline unsigned int +kgsl_memarena_get_order(uint32_t flags) +{ + unsigned int alignshift; + alignshift = ((flags & KGSL_MEMFLAGS_ALIGN_MASK) + >> KGSL_MEMFLAGS_ALIGN_SHIFT); + return alignshift; +} + +/* block alignment shift count */ +static inline unsigned int +kgsl_memarena_align(unsigned int address, unsigned int shift) +{ + unsigned int alignedbaseaddr = ((address) >> shift) << shift; + if (alignedbaseaddr < address) + alignedbaseaddr += (1 << shift); + + return alignedbaseaddr; +} + +int +kgsl_sharedmem_init(struct kgsl_sharedmem *shmem) +{ + int result = -EINVAL; + + if (!request_mem_region(shmem->physbase, shmem->size, DRIVER_NAME)) { + KGSL_MEM_ERR("request_mem_region failed\n"); + goto error; + } + + shmem->baseptr = ioremap(shmem->physbase, shmem->size); + KGSL_MEM_INFO("ioremap(shm) = %p\n", shmem->baseptr); + + if (shmem->baseptr == NULL) { + KGSL_MEM_ERR("ioremap failed for address %08x size %d\n", + shmem->physbase, shmem->size); + result = -ENODEV; + goto error_release_mem; + } + + shmem->pool = gen_pool_create(KGSL_PAGESIZE_SHIFT, -1); + if (shmem->pool == NULL) { + KGSL_MEM_ERR("gen_pool_create failed\n"); + result = -ENOMEM; + goto error_iounmap; + } + + if (gen_pool_add(shmem->pool, shmem->physbase, shmem->size, -1)) { + KGSL_MEM_ERR("gen_pool_create failed\n"); + result = -ENOMEM; + goto error_pool_destroy; + } + result = 0; + KGSL_MEM_INFO("physbase 0x%08x size 0x%08x baseptr 0x%p\n", + shmem->physbase, shmem->size, shmem->baseptr); + return 0; + +error_pool_destroy: + gen_pool_destroy(shmem->pool); +error_iounmap: + iounmap(shmem->baseptr); + shmem->baseptr = NULL; +error_release_mem: + release_mem_region(shmem->physbase, shmem->size); +error: + return result; +} + +int +kgsl_sharedmem_close(struct kgsl_sharedmem *shmem) +{ + if (shmem->pool) { + gen_pool_destroy(shmem->pool); + shmem->pool = NULL; + } + + if (shmem->baseptr != NULL) { + KGSL_MEM_INFO("iounmap(shm) = %p\n", shmem->baseptr); + iounmap(shmem->baseptr); + shmem->baseptr = NULL; + release_mem_region(shmem->physbase, shmem->size); + } + + return 0; +} +/* +* get the host mapped address for a hardware device address +*/ +static void *kgsl_memarena_gethostptr(struct kgsl_sharedmem *shmem, + uint32_t physaddr) +{ + void *result; + + KGSL_MEM_VDBG("enter (memarena=%p, physaddr=0x%08x)\n", + shmem, physaddr); + + BUG_ON(shmem == NULL); + + /* check address range */ + if (physaddr < shmem->physbase) + return NULL; + + if (physaddr >= shmem->physbase + shmem->size) + return NULL; + + if (shmem->baseptr == NULL) { + KGSL_MEM_VDBG("return: %p\n", NULL); + return NULL; + } + + result = ((physaddr - shmem->physbase) + shmem->baseptr); + + KGSL_MEM_VDBG("return: %p\n", result); + + return result; +} + + +int +kgsl_sharedmem_alloc(uint32_t flags, int size, + struct kgsl_memdesc *memdesc) +{ + struct kgsl_sharedmem *shmem; + int result = -ENOMEM; + unsigned int blksize; + unsigned int baseaddr; + unsigned int alignshift; + unsigned int alignedbaseaddr; + + KGSL_MEM_VDBG("enter (flags=0x%08x, size=%d, memdesc=%p)\n", + flags, size, memdesc); + + shmem = &kgsl_driver.shmem; + BUG_ON(memdesc == NULL); + BUG_ON(size <= 0); + + alignshift = kgsl_memarena_get_order(flags); + + size = ALIGN(size, KGSL_PAGESIZE); + blksize = size; + if (alignshift > KGSL_PAGESIZE_SHIFT) + blksize += (1 << alignshift) - KGSL_PAGESIZE; + + baseaddr = gen_pool_alloc(shmem->pool, blksize); + if (baseaddr == 0) { + KGSL_MEM_ERR("gen_pool_alloc failed\n"); + result = -ENOMEM; + goto done; + } + result = 0; + + if (alignshift > KGSL_PAGESIZE_SHIFT) { + alignedbaseaddr = ALIGN(baseaddr, (1 << alignshift)); + + KGSL_MEM_VDBG("ba %x al %x as %d m->as %d bs %x s %x\n", + baseaddr, alignedbaseaddr, alignshift, + KGSL_PAGESIZE_SHIFT, blksize, size); + if (alignedbaseaddr > baseaddr) { + KGSL_MEM_VDBG("physaddr %x free before %x size %x\n", + alignedbaseaddr, + baseaddr, alignedbaseaddr - baseaddr); + gen_pool_free(shmem->pool, baseaddr, + alignedbaseaddr - baseaddr); + blksize -= alignedbaseaddr - baseaddr; + } + if (blksize > size) { + KGSL_MEM_VDBG("physaddr %x free after %x size %x\n", + alignedbaseaddr, + alignedbaseaddr + size, + blksize - size); + gen_pool_free(shmem->pool, + alignedbaseaddr + size, + blksize - size); + } + } else { + alignedbaseaddr = baseaddr; + } + + memdesc->physaddr = alignedbaseaddr; + memdesc->hostptr = kgsl_memarena_gethostptr(shmem, memdesc->physaddr); + memdesc->size = size; + memdesc->gpuaddr = memdesc->physaddr; + + KGSL_MEM_VDBG("ashift %d m->ashift %d blksize %d base %x abase %x\n", + alignshift, KGSL_PAGESIZE_SHIFT, blksize, baseaddr, + alignedbaseaddr); + +done: + if (result) + memset(memdesc, 0, sizeof(*memdesc)); + + + KGSL_MEM_VDBG("return: %d\n", result); + return result; +} + +void +kgsl_sharedmem_free(struct kgsl_memdesc *memdesc) +{ + struct kgsl_sharedmem *shmem = &kgsl_driver.shmem; + + KGSL_MEM_VDBG("enter (shmem=%p, memdesc=%p, physaddr=%08x, size=%d)\n", + shmem, memdesc, memdesc->physaddr, memdesc->size); + + BUG_ON(memdesc == NULL); + BUG_ON(memdesc->size <= 0); + BUG_ON(shmem->physbase > memdesc->physaddr); + BUG_ON((shmem->physbase + shmem->size) + < (memdesc->physaddr + memdesc->size)); + + gen_pool_free(shmem->pool, memdesc->physaddr, memdesc->size); + + memset(memdesc, 0, sizeof(struct kgsl_memdesc)); + KGSL_MEM_VDBG("return\n"); +} + +int +kgsl_sharedmem_read(const struct kgsl_memdesc *memdesc, void *dst, + unsigned int offsetbytes, unsigned int sizebytes) +{ + if (memdesc == NULL || memdesc->hostptr == NULL || dst == NULL) { + KGSL_MEM_ERR("bad ptr memdesc %p hostptr %p dst %p\n", + memdesc, + (memdesc ? memdesc->hostptr : NULL), + dst); + return -EINVAL; + } + if (offsetbytes + sizebytes > memdesc->size) { + KGSL_MEM_ERR("bad range: offset %d size %d memdesc %d\n", + offsetbytes, sizebytes, memdesc->size); + return -ERANGE; + } + memcpy(dst, memdesc->hostptr + offsetbytes, sizebytes); + return 0; +} + +int +kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc, unsigned int offsetbytes, + unsigned int value, unsigned int sizebytes) +{ + if (memdesc == NULL || memdesc->hostptr == NULL) { + KGSL_MEM_ERR("bad ptr memdesc %p hostptr %p\n", memdesc, + (memdesc ? memdesc->hostptr : NULL)); + return -EINVAL; + } + if (offsetbytes + sizebytes > memdesc->size) { + KGSL_MEM_ERR("bad range: offset %d size %d memdesc %d\n", + offsetbytes, sizebytes, memdesc->size); + return -ERANGE; + } + memset(memdesc->hostptr + offsetbytes, value, sizebytes); + return 0; +} + diff --git a/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h new file mode 100644 index 0000000000000..2f7e3406e9f5a --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h @@ -0,0 +1,106 @@ +/* + * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 + * Copyright (c) 2008-2009 QUALCOMM USA, INC. + * + * All source code in this file is licensed under the following license + * + * 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, you can find it at http://www.fsf.org + */ +#ifndef __GSL_SHAREDMEM_H +#define __GSL_SHAREDMEM_H + +#include +#include + +#define KGSL_PAGESIZE 0x1000 +#define KGSL_PAGESIZE_SHIFT 12 + +struct kgsl_pagetable; + +struct platform_device; +struct gen_pool; + +/* memory allocation flags */ +#define KGSL_MEMFLAGS_ANY 0x00000000 /*dont care*/ + +#define KGSL_MEMFLAGS_APERTUREANY 0x00000000 +#define KGSL_MEMFLAGS_EMEM 0x00000000 +#define KGSL_MEMFLAGS_CONPHYS 0x00001000 + +#define KGSL_MEMFLAGS_ALIGNANY 0x00000000 +#define KGSL_MEMFLAGS_ALIGN32 0x00000000 +#define KGSL_MEMFLAGS_ALIGN64 0x00060000 +#define KGSL_MEMFLAGS_ALIGN128 0x00070000 +#define KGSL_MEMFLAGS_ALIGN256 0x00080000 +#define KGSL_MEMFLAGS_ALIGN512 0x00090000 +#define KGSL_MEMFLAGS_ALIGN1K 0x000A0000 +#define KGSL_MEMFLAGS_ALIGN2K 0x000B0000 +#define KGSL_MEMFLAGS_ALIGN4K 0x000C0000 +#define KGSL_MEMFLAGS_ALIGN8K 0x000D0000 +#define KGSL_MEMFLAGS_ALIGN16K 0x000E0000 +#define KGSL_MEMFLAGS_ALIGN32K 0x000F0000 +#define KGSL_MEMFLAGS_ALIGN64K 0x00100000 +#define KGSL_MEMFLAGS_ALIGNPAGE KGSL_MEMFLAGS_ALIGN4K + +/* fail the alloc if the flags cannot be honored */ +#define KGSL_MEMFLAGS_STRICTREQUEST 0x80000000 + +#define KGSL_MEMFLAGS_APERTURE_MASK 0x0000F000 +#define KGSL_MEMFLAGS_ALIGN_MASK 0x00FF0000 + +#define KGSL_MEMFLAGS_APERTURE_SHIFT 12 +#define KGSL_MEMFLAGS_ALIGN_SHIFT 16 + + +/* shared memory allocation */ +struct kgsl_memdesc { + struct kgsl_pagetable *pagetable; + void *hostptr; + unsigned int gpuaddr; + unsigned int physaddr; + unsigned int size; + unsigned int priv; +}; + +struct kgsl_sharedmem { + void *baseptr; + unsigned int physbase; + unsigned int size; + struct gen_pool *pool; +}; + +int kgsl_sharedmem_alloc(uint32_t flags, int size, + struct kgsl_memdesc *memdesc); + +/*TODO: add protection flags */ +int kgsl_sharedmem_import(struct kgsl_pagetable *, + uint32_t phys_addr, + uint32_t size, + struct kgsl_memdesc *memdesc); + + +void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc); + + +int kgsl_sharedmem_read(const struct kgsl_memdesc *memdesc, void *dst, + unsigned int offsetbytes, unsigned int sizebytes); + +int kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc, + unsigned int offsetbytes, unsigned int value, + unsigned int sizebytes); + +int kgsl_sharedmem_init(struct kgsl_sharedmem *shmem); + +int kgsl_sharedmem_close(struct kgsl_sharedmem *shmem); + +#endif /* __GSL_SHAREDMEM_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c new file mode 100644 index 0000000000000..1e59b92206ad4 --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c @@ -0,0 +1,861 @@ +/* + * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 + * Copyright (c) 2008-2009 QUALCOMM USA, INC. + * + * All source code in this file is licensed under the following license + * + * 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, you can find it at http://www.fsf.org + */ +#include +#include +#include +#include +#include +#include + +#include "kgsl.h" +#include "kgsl_log.h" +#include "kgsl_pm4types.h" +#include "kgsl_cmdstream.h" + +#include "yamato_reg.h" + +#define GSL_RBBM_INT_MASK \ + (RBBM_INT_CNTL__RDERR_INT_MASK | \ + RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK) + +#define GSL_SQ_INT_MASK \ + (SQ_INT_CNTL__PS_WATCHDOG_MASK | \ + SQ_INT_CNTL__VS_WATCHDOG_MASK) + +/* Yamato MH arbiter config*/ +#define KGSL_CFG_YAMATO_MHARB \ + (0x10 \ + | (0 << MH_ARBITER_CONFIG__SAME_PAGE_GRANULARITY__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__L1_ARB_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__L1_ARB_HOLD_ENABLE__SHIFT) \ + | (0 << MH_ARBITER_CONFIG__L2_ARB_CONTROL__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__PAGE_SIZE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__TC_REORDER_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__TC_ARB_HOLD_ENABLE__SHIFT) \ + | (0 << MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT_ENABLE__SHIFT) \ + | (0x8 << MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__CP_CLNT_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__VGT_CLNT_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__TC_CLNT_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT)) + +void kgsl_register_dump(struct kgsl_device *device) +{ + unsigned int regValue; + + kgsl_yamato_regread(device, REG_RBBM_STATUS, ®Value); + KGSL_CMD_ERR("RBBM_STATUS = %8.8X\n", regValue); + kgsl_yamato_regread(device, REG_CP_RB_BASE, ®Value); + KGSL_CMD_ERR("CP_RB_BASE = %08x\n", regValue); + kgsl_yamato_regread(device, REG_CP_RB_CNTL, ®Value); + KGSL_CMD_ERR("CP_RB_CNTL = %08x\n", regValue); + kgsl_yamato_regread(device, REG_CP_RB_RPTR_ADDR, ®Value); + KGSL_CMD_ERR("CP_RB_RPTR_ADDR = %08x\n", regValue); + kgsl_yamato_regread(device, REG_CP_RB_RPTR, ®Value); + KGSL_CMD_ERR("CP_RB_RPTR = %08x\n", regValue); + kgsl_yamato_regread(device, REG_CP_RB_WPTR, ®Value); + KGSL_CMD_ERR("CP_RB_WPTR = %08x\n", regValue); + kgsl_yamato_regread(device, REG_CP_RB_RPTR_WR, ®Value); + KGSL_CMD_ERR("CP_RB_RPTR_WR = %08x\n", regValue); + kgsl_yamato_regread(device, REG_CP_INT_CNTL, ®Value); + KGSL_CMD_ERR("CP_INT_CNTL = %08x\n", regValue); + kgsl_yamato_regread(device, REG_CP_INT_STATUS, ®Value); + KGSL_CMD_ERR("CP_INT_STATUS = %08x\n", regValue); + kgsl_yamato_regread(device, REG_CP_ME_CNTL, ®Value); + KGSL_CMD_ERR("CP_ME_CNTL = %08x\n", regValue); + kgsl_yamato_regread(device, REG_CP_ME_STATUS, ®Value); + KGSL_CMD_ERR("CP_ME_STATUS = %08x\n", regValue); + kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE1, ®Value); + KGSL_CMD_ERR("RBBM_PM_OVERRIDE1 = %08x\n", regValue); + kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE2, ®Value); + KGSL_CMD_ERR("RBBM_PM_OVERRIDE2 = %08x\n", regValue); + kgsl_yamato_regread(device, REG_RBBM_INT_CNTL, ®Value); + KGSL_CMD_ERR("RBBM_INT_CNTL = %08x\n", regValue); + kgsl_yamato_regread(device, REG_RBBM_INT_STATUS, ®Value); + KGSL_CMD_ERR("RBBM_INT_STATUS = %08x\n", regValue); + kgsl_yamato_regread(device, REG_MASTER_INT_SIGNAL, ®Value); + KGSL_CMD_ERR("MASTER_INT_SIGNAL = %08x\n", regValue); + kgsl_yamato_regread(device, REG_CP_IB1_BASE, ®Value); + KGSL_CMD_ERR("CP_IB1_BASE = %08x\n", regValue); + kgsl_yamato_regread(device, REG_CP_IB1_BUFSZ, ®Value); + KGSL_CMD_ERR("CP_IB1_BUFSZ = %08x\n", regValue); + kgsl_yamato_regread(device, REG_CP_IB2_BASE, ®Value); + KGSL_CMD_ERR("CP_IB2_BASE = %08x\n", regValue); + kgsl_yamato_regread(device, REG_CP_IB2_BUFSZ, ®Value); + KGSL_CMD_ERR("CP_IB2_BUFSZ = %08x\n", regValue); + kgsl_yamato_regread(device, REG_CP_STAT, ®Value); + KGSL_CMD_ERR("CP_STAT = %08x\n", regValue); + kgsl_yamato_regread(device, REG_SCRATCH_REG0, ®Value); + KGSL_CMD_ERR("SCRATCH_REG0 = %08x\n", regValue); + kgsl_yamato_regread(device, REG_COHER_SIZE_PM4, ®Value); + KGSL_CMD_ERR("COHER_SIZE_PM4 = %08x\n", regValue); + kgsl_yamato_regread(device, REG_COHER_BASE_PM4, ®Value); + KGSL_CMD_ERR("COHER_BASE_PM4 = %08x\n", regValue); + kgsl_yamato_regread(device, REG_COHER_STATUS_PM4, ®Value); + KGSL_CMD_ERR("COHER_STATUS_PM4 = %08x\n", regValue); + kgsl_yamato_regread(device, REG_RBBM_READ_ERROR, ®Value); + KGSL_CMD_ERR("RBBM_READ_ERROR = %08x\n", regValue); + kgsl_yamato_regread(device, REG_MH_AXI_ERROR, ®Value); + KGSL_CMD_ERR("MH_AXI_ERROR = %08x\n", regValue); +} + +static int kgsl_yamato_gmeminit(struct kgsl_device *device) +{ + union reg_rb_edram_info rb_edram_info; + unsigned int gmem_size; + unsigned int edram_value = 0; + + /* make sure edram range is aligned to size */ + BUG_ON(device->gmemspace.gpu_base & (device->gmemspace.sizebytes - 1)); + + /* get edram_size value equivalent */ + gmem_size = (device->gmemspace.sizebytes >> 14); + while (gmem_size >>= 1) + edram_value++; + + rb_edram_info.val = 0; + + rb_edram_info.f.edram_size = edram_value; + rb_edram_info.f.edram_mapping_mode = 0; /* EDRAM_MAP_UPPER */ + /* must be aligned to size */ + rb_edram_info.f.edram_range = (device->gmemspace.gpu_base >> 14); + + kgsl_yamato_regwrite(device, REG_RB_EDRAM_INFO, rb_edram_info.val); + + return 0; +} + +static int kgsl_yamato_gmemclose(struct kgsl_device *device) +{ + kgsl_yamato_regwrite(device, REG_RB_EDRAM_INFO, 0x00000000); + + return 0; +} + +void kgsl_yamato_rbbm_intrcallback(struct kgsl_device *device) +{ + unsigned int status = 0; + unsigned int rderr = 0; + + KGSL_DRV_VDBG("enter (device=%p)\n", device); + + kgsl_yamato_regread(device, REG_RBBM_INT_STATUS, &status); + + if (status & RBBM_INT_CNTL__RDERR_INT_MASK) { + kgsl_yamato_regread(device, REG_RBBM_READ_ERROR, &rderr); + KGSL_DRV_FATAL("rbbm read error interrupt: %08x\n", rderr); + } else if (status & RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK) { + KGSL_DRV_DBG("rbbm display update interrupt\n"); + } else if (status & RBBM_INT_CNTL__GUI_IDLE_INT_MASK) { + KGSL_DRV_DBG("rbbm gui idle interrupt\n"); + } else { + KGSL_CMD_DBG("bad bits in REG_CP_INT_STATUS %08x\n", status); + } + + status &= GSL_RBBM_INT_MASK; + kgsl_yamato_regwrite(device, REG_RBBM_INT_ACK, status); + + KGSL_DRV_VDBG("return\n"); +} + +void kgsl_yamato_sq_intrcallback(struct kgsl_device *device) +{ + unsigned int status = 0; + + KGSL_DRV_VDBG("enter (device=%p)\n", device); + + kgsl_yamato_regread(device, REG_SQ_INT_STATUS, &status); + + if (status & SQ_INT_CNTL__PS_WATCHDOG_MASK) + KGSL_DRV_DBG("sq ps watchdog interrupt\n"); + else if (status & SQ_INT_CNTL__VS_WATCHDOG_MASK) + KGSL_DRV_DBG("sq vs watchdog interrupt\n"); + else + KGSL_DRV_DBG("bad bits in REG_SQ_INT_STATUS %08x\n", status); + + + status &= GSL_SQ_INT_MASK; + kgsl_yamato_regwrite(device, REG_SQ_INT_ACK, status); + + KGSL_DRV_VDBG("return\n"); +} + +irqreturn_t kgsl_yamato_isr(int irq, void *data) +{ + irqreturn_t result = IRQ_NONE; + + struct kgsl_device *device = &kgsl_driver.yamato_device; + unsigned int status; + + kgsl_yamato_regread(device, REG_MASTER_INT_SIGNAL, &status); + + if (status & MASTER_INT_SIGNAL__MH_INT_STAT) { + kgsl_mh_intrcallback(device); + result = IRQ_HANDLED; + } + + if (status & MASTER_INT_SIGNAL__CP_INT_STAT) { + kgsl_cp_intrcallback(device); + result = IRQ_HANDLED; + } + + if (status & MASTER_INT_SIGNAL__RBBM_INT_STAT) { + kgsl_yamato_rbbm_intrcallback(device); + result = IRQ_HANDLED; + } + + if (status & MASTER_INT_SIGNAL__SQ_INT_STAT) { + kgsl_yamato_sq_intrcallback(device); + result = IRQ_HANDLED; + } + + + return result; +} + +#ifdef CONFIG_MSM_KGSL_MMU +int kgsl_yamato_tlbinvalidate(struct kgsl_device *device) +{ +#ifdef PER_PROCESS_PAGE_TABLE + unsigned int link[2]; +#endif + unsigned int mh_mmu_invalidate = 0x00000003; /*invalidate all and tc */ + + KGSL_MEM_DBG("device %p ctxt %p pt %p\n", + device, + device->drawctxt_active, + device->mmu.hwpagetable); +#ifdef PER_PROCESS_PAGE_TABLE + /* if there is an active draw context, invalidate via command stream, + * otherwise invalidate via direct register writes + */ + if (device->drawctxt_active) { + link[0] = pm4_type0_packet(REG_MH_MMU_INVALIDATE, 1); + link[1] = mh_mmu_invalidate; + + KGSL_MEM_DBG("cmds\n"); + kgsl_ringbuffer_issuecmds(device, 1, + &link[0], 2); + } else { +#endif + KGSL_MEM_DBG("regs\n"); + + kgsl_yamato_regwrite(device, REG_MH_MMU_INVALIDATE, + mh_mmu_invalidate); +#ifdef PER_PROCESS_PAGE_TABLE + } +#endif + return 0; +} +#endif + +#ifdef CONFIG_MSM_KGSL_MMU +int kgsl_yamato_setpagetable(struct kgsl_device *device) +{ + unsigned int link[27]; + unsigned int mh_mmu_invalidate = 0x00000003; /*invalidate all and tc */ + + KGSL_MEM_DBG("device %p ctxt %p pt %p\n", + device, + device->drawctxt_active, + device->mmu.hwpagetable); + /* if there is an active draw context, set via command stream, + * otherwise set via direct register writes + */ + if (device->drawctxt_active) { + KGSL_MEM_DBG("cmds\n"); + /* wait for graphics pipe to be idle */ + link[0] = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + link[1] = 0x00000000; + + /* set page table base */ + link[2] = pm4_type0_packet(REG_MH_MMU_PT_BASE, 1); + link[3] = device->mmu.hwpagetable->base.gpuaddr; + + link[4] = pm4_type3_packet(PM4_SET_CONSTANT, 2); + link[5] = (0x4 << 16) | (REG_PA_SU_SC_MODE_CNTL - 0x2000); + link[6] = 0; /* disable faceness generation */ + link[7] = pm4_type3_packet(PM4_SET_BIN_BASE_OFFSET, 1); + link[8] = device->mmu.dummyspace.gpuaddr; + link[9] = pm4_type3_packet(PM4_DRAW_INDX_BIN, 6); + link[10] = 0; /* viz query info */ + link[11] = 0x0003C004; /* draw indicator */ + link[12] = 0; /* bin base */ + link[13] = 3; /* bin size */ + link[14] = device->mmu.dummyspace.gpuaddr; /* dma base */ + link[15] = 6; /* dma size */ + link[16] = pm4_type3_packet(PM4_DRAW_INDX_BIN, 6); + link[17] = 0; /* viz query info */ + link[18] = 0x0003C004; /* draw indicator */ + link[19] = 0; /* bin base */ + link[20] = 3; /* bin size */ + link[21] = device->mmu.dummyspace.gpuaddr; /* dma base */ + link[22] = 6; /* dma size */ + link[23] = pm4_type0_packet(REG_MH_MMU_INVALIDATE, 1); + link[24] = mh_mmu_invalidate; + link[25] = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + link[26] = 0x00000000; + + kgsl_ringbuffer_issuecmds(device, 1, + &link[0], 27); + } else { + KGSL_MEM_DBG("regs\n"); + + kgsl_yamato_regwrite(device, REG_MH_MMU_PT_BASE, + device->mmu.hwpagetable->base.gpuaddr); + kgsl_yamato_regwrite(device, REG_MH_MMU_VA_RANGE, + (device->mmu.hwpagetable-> + va_base | (device->mmu.hwpagetable-> + va_range >> 16))); + kgsl_yamato_regwrite(device, REG_MH_MMU_INVALIDATE, + mh_mmu_invalidate); + } + + return 0; +} +#endif + +static unsigned int +kgsl_yamato_getchipid(struct kgsl_device *device) +{ + unsigned int chipid; + unsigned int coreid, majorid, minorid, patchid, revid; + + /* YDX */ + kgsl_yamato_regread(device, REG_RBBM_PERIPHID1, &coreid); + coreid &= 0xF; + + kgsl_yamato_regread(device, REG_RBBM_PERIPHID2, &majorid); + majorid = (majorid >> 4) & 0xF; + + kgsl_yamato_regread(device, REG_RBBM_PATCH_RELEASE, &revid); + /* this is a 16bit field, but extremely unlikely it would ever get + * this high + */ + minorid = ((revid >> 0) & 0xFF); + + + patchid = ((revid >> 16) & 0xFF); + + chipid = ((coreid << 24) | (majorid << 16) | + (minorid << 8) | (patchid << 0)); + + /* Hardware revision 211 (8650) returns the wrong chip ID */ + if (chipid == KGSL_CHIPID_YAMATODX_REV21) + chipid = KGSL_CHIPID_YAMATODX_REV211; + + return chipid; +} + +int kgsl_yamato_init(struct kgsl_device *device, struct kgsl_devconfig *config) +{ + int status = -EINVAL; + struct kgsl_memregion *regspace = &device->regspace; + unsigned int memflags = KGSL_MEMFLAGS_ALIGNPAGE | KGSL_MEMFLAGS_CONPHYS; + + KGSL_DRV_VDBG("enter (device=%p, config=%p)\n", device, config); + + if (device->flags & KGSL_FLAGS_INITIALIZED) { + KGSL_DRV_VDBG("return %d\n", 0); + return 0; + } + memset(device, 0, sizeof(*device)); + + init_waitqueue_head(&device->ib1_wq); + + memcpy(regspace, &config->regspace, sizeof(device->regspace)); + if (regspace->mmio_phys_base == 0 || regspace->sizebytes == 0) { + KGSL_DRV_ERR("dev %d invalid regspace\n", device->id); + goto error; + } + if (!request_mem_region(regspace->mmio_phys_base, + regspace->sizebytes, DRIVER_NAME)) { + KGSL_DRV_ERR("request_mem_region failed for register memory\n"); + status = -ENODEV; + goto error; + } + + regspace->mmio_virt_base = ioremap(regspace->mmio_phys_base, + regspace->sizebytes); + KGSL_MEM_INFO("ioremap(regs) = %p\n", regspace->mmio_virt_base); + if (regspace->mmio_virt_base == NULL) { + KGSL_DRV_ERR("ioremap failed for register memory\n"); + status = -ENODEV; + goto error_release_mem; + } + + KGSL_DRV_INFO("dev %d regs phys 0x%08x size 0x%08x virt %p\n", + device->id, regspace->mmio_phys_base, + regspace->sizebytes, regspace->mmio_virt_base); + + memcpy(&device->gmemspace, &config->gmemspace, + sizeof(device->gmemspace)); + + device->id = KGSL_DEVICE_YAMATO; + + if (config->mmu_config) { + device->mmu.config = config->mmu_config; + device->mmu.mpu_base = config->mpu_base; + device->mmu.mpu_range = config->mpu_range; + device->mmu.va_base = config->va_base; + device->mmu.va_range = config->va_range; + } + + device->chip_id = kgsl_yamato_getchipid(device); + + /*We need to make sure all blocks are powered up and clocked before + *issuing a soft reset. The overrides will be turned off (set to 0) + *later in kgsl_yamato_start. + */ + kgsl_yamato_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0xfffffffe); + kgsl_yamato_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xffffffff); + + kgsl_yamato_regwrite(device, REG_RBBM_SOFT_RESET, 0xFFFFFFFF); + msleep(50); + kgsl_yamato_regwrite(device, REG_RBBM_SOFT_RESET, 0x00000000); + + kgsl_yamato_regwrite(device, REG_RBBM_CNTL, 0x00004442); + + kgsl_yamato_regwrite(device, REG_MH_ARBITER_CONFIG, + KGSL_CFG_YAMATO_MHARB); + + kgsl_yamato_regwrite(device, REG_SQ_VS_PROGRAM, 0x00000000); + kgsl_yamato_regwrite(device, REG_SQ_PS_PROGRAM, 0x00000000); + + + status = kgsl_mmu_init(device); + if (status != 0) { + status = -ENODEV; + goto error_iounmap; + } + + status = kgsl_cmdstream_init(device); + if (status != 0) { + status = -ENODEV; + goto error_close_mmu; + } + + status = kgsl_sharedmem_alloc(memflags, sizeof(device->memstore), + &device->memstore); + if (status != 0) { + status = -ENODEV; + goto error_close_cmdstream; + } + kgsl_sharedmem_set(&device->memstore, 0, 0, device->memstore.size); + + pr_info("msm_kgsl: initilized dev=%d mmu=%s\n", device->id, + kgsl_mmu_isenabled(&device->mmu) ? "on" : "off"); + + device->flags |= KGSL_FLAGS_INITIALIZED; + return 0; + +error_close_cmdstream: + kgsl_cmdstream_close(device); +error_close_mmu: + kgsl_mmu_close(device); +error_iounmap: + iounmap(regspace->mmio_virt_base); + regspace->mmio_virt_base = NULL; +error_release_mem: + release_mem_region(regspace->mmio_phys_base, regspace->sizebytes); +error: + return status; +} + +int kgsl_yamato_close(struct kgsl_device *device) +{ + struct kgsl_memregion *regspace = &device->regspace; + + if (device->memstore.hostptr) + kgsl_sharedmem_free(&device->memstore); + + kgsl_mmu_close(device); + + kgsl_cmdstream_close(device); + + if (regspace->mmio_virt_base != NULL) { + KGSL_MEM_INFO("iounmap(regs) = %p\n", regspace->mmio_virt_base); + iounmap(regspace->mmio_virt_base); + regspace->mmio_virt_base = NULL; + release_mem_region(regspace->mmio_phys_base, + regspace->sizebytes); + } + + KGSL_DRV_VDBG("return %d\n", 0); + device->flags &= ~KGSL_FLAGS_INITIALIZED; + return 0; +} + +int kgsl_yamato_start(struct kgsl_device *device, uint32_t flags) +{ + int status = -EINVAL; + + KGSL_DRV_VDBG("enter (device=%p)\n", device); + + if (!(device->flags & KGSL_FLAGS_INITIALIZED)) { + KGSL_DRV_ERR("Trying to start uninitialized device.\n"); + return -EINVAL; + } + + device->refcnt++; + + if (device->flags & KGSL_FLAGS_STARTED) { + KGSL_DRV_VDBG("already started"); + return 0; + } + + kgsl_yamato_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0); + kgsl_yamato_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0); + + KGSL_DRV_DBG("enabling RBBM interrupts mask 0x%08lx\n", + GSL_RBBM_INT_MASK); + kgsl_yamato_regwrite(device, REG_RBBM_INT_CNTL, GSL_RBBM_INT_MASK); + + /* make sure SQ interrupts are disabled */ + kgsl_yamato_regwrite(device, REG_SQ_INT_CNTL, 0); + + kgsl_yamato_gmeminit(device); + + status = kgsl_ringbuffer_init(device); + if (status != 0) { + kgsl_yamato_stop(device); + return status; + } + + status = kgsl_drawctxt_init(device); + if (status != 0) { + kgsl_yamato_stop(device); + return status; + } + + device->flags |= KGSL_FLAGS_STARTED; + + KGSL_DRV_VDBG("return %d\n", status); + return status; +} + +int kgsl_yamato_stop(struct kgsl_device *device) +{ + if (device->flags & KGSL_FLAGS_STARTED) { + + kgsl_yamato_regwrite(device, REG_RBBM_INT_CNTL, 0); + + kgsl_yamato_regwrite(device, REG_SQ_INT_CNTL, 0); + + kgsl_drawctxt_close(device); + + kgsl_ringbuffer_close(&device->ringbuffer); + + kgsl_yamato_gmemclose(device); + + device->flags &= ~KGSL_FLAGS_STARTED; + } + + return 0; +} + +int kgsl_yamato_getproperty(struct kgsl_device *device, + enum kgsl_property_type type, + void *value, + unsigned int sizebytes) +{ + int status = -EINVAL; + + switch (type) { + case KGSL_PROP_DEVICE_INFO: + { + struct kgsl_devinfo devinfo; + + if (sizebytes != sizeof(devinfo)) { + status = -EINVAL; + break; + } + + memset(&devinfo, 0, sizeof(devinfo)); + devinfo.device_id = device->id; + devinfo.chip_id = device->chip_id; + devinfo.mmu_enabled = kgsl_mmu_isenabled(&device->mmu); + devinfo.gmem_hostbaseaddr = + (unsigned int)device->gmemspace.mmio_virt_base; + devinfo.gmem_gpubaseaddr = device->gmemspace.gpu_base; + devinfo.gmem_sizebytes = device->gmemspace.sizebytes; + + if (copy_to_user(value, &devinfo, sizeof(devinfo)) != + 0) { + status = -EFAULT; + break; + } + status = 0; + } + break; + case KGSL_PROP_DEVICE_SHADOW: + { + struct kgsl_shadowprop shadowprop; + + if (sizebytes != sizeof(shadowprop)) { + status = -EINVAL; + break; + } + memset(&shadowprop, 0, sizeof(shadowprop)); + if (device->memstore.hostptr) { + /*NOTE: with mmu enabled, gpuaddr doesn't mean + * anything to mmap(). + */ + shadowprop.gpuaddr = device->memstore.physaddr; + shadowprop.size = device->memstore.size; + shadowprop.flags = KGSL_FLAGS_INITIALIZED; + } + if (copy_to_user(value, &shadowprop, + sizeof(shadowprop))) { + status = -EFAULT; + break; + } + status = 0; + } + break; + case KGSL_PROP_MMU_ENABLE: + { +#ifdef CONFIG_MSM_KGSL_MMU + int mmuProp = 1; +#else + int mmuProp = 0; +#endif + if (sizebytes != sizeof(int)) { + status = -EINVAL; + break; + } + if (copy_to_user(value, &mmuProp, sizeof(mmuProp))) { + status = -EFAULT; + break; + } + status = 0; + } + break; + default: + status = -EINVAL; + } + + return status; +} + +/* Note: This is either called from the standby timer, or while holding the + * driver mutex. + * + * The reader may obseve that this function may be called without holding the + * driver mutex (in the timer), which can cause the ringbuffer write pointer + * to change, when a user submits a command. However, the user must be holding + * the driver mutex when doing so, and then must + * have canceled the timer. If the timer was executing at the time of + * cancellation, the active flag would have been cleared, which the user + * ioctl checks for after cancelling the timer. + */ +bool kgsl_yamato_is_idle(struct kgsl_device *device) +{ + struct kgsl_ringbuffer *rb = &device->ringbuffer; + unsigned int rbbm_status; + + BUG_ON(!(rb->flags & KGSL_FLAGS_STARTED)); + + GSL_RB_GET_READPTR(rb, &rb->rptr); + if (rb->rptr == rb->wptr) { + kgsl_yamato_regread(device, REG_RBBM_STATUS, &rbbm_status); + if (rbbm_status == 0x110) + return true; + } + return false; +} + +int kgsl_yamato_idle(struct kgsl_device *device, unsigned int timeout) +{ + int status = -EINVAL; + struct kgsl_ringbuffer *rb = &device->ringbuffer; + struct kgsl_mmu_debug mmu_dbg; + unsigned int rbbm_status; + int idle_count = 0; +#define IDLE_COUNT_MAX 1000000 + + KGSL_DRV_VDBG("enter (device=%p, timeout=%d)\n", device, timeout); + + (void)timeout; + + /* first, wait until the CP has consumed all the commands in + * the ring buffer + */ + if (rb->flags & KGSL_FLAGS_STARTED) { + do { + idle_count++; + GSL_RB_GET_READPTR(rb, &rb->rptr); + + } while (rb->rptr != rb->wptr && idle_count < IDLE_COUNT_MAX); + if (idle_count == IDLE_COUNT_MAX) { + KGSL_DRV_ERR("spun too long waiting for RB to idle\n"); + status = -EINVAL; + kgsl_ringbuffer_dump(rb); + kgsl_mmu_debug(&device->mmu, &mmu_dbg); + goto done; + } + } + /* now, wait for the GPU to finish its operations */ + for (idle_count = 0; idle_count < IDLE_COUNT_MAX; idle_count++) { + kgsl_yamato_regread(device, REG_RBBM_STATUS, &rbbm_status); + + if (rbbm_status == 0x110) { + status = 0; + break; + } + } + + if (idle_count == IDLE_COUNT_MAX) { + KGSL_DRV_ERR("spun too long waiting for RBBM status to idle\n"); + status = -EINVAL; + kgsl_ringbuffer_dump(rb); + kgsl_mmu_debug(&device->mmu, &mmu_dbg); + goto done; + } +done: + KGSL_DRV_VDBG("return %d\n", status); + + return status; +} + +int kgsl_yamato_regread(struct kgsl_device *device, unsigned int offsetwords, + unsigned int *value) +{ + unsigned int *reg; + + if (offsetwords*sizeof(uint32_t) >= device->regspace.sizebytes) { + KGSL_DRV_ERR("invalid offset %d\n", offsetwords); + return -ERANGE; + } + + reg = (unsigned int *)(device->regspace.mmio_virt_base + + (offsetwords << 2)); + *value = readl(reg); + + return 0; +} + +int kgsl_yamato_regwrite(struct kgsl_device *device, unsigned int offsetwords, + unsigned int value) +{ + unsigned int *reg; + + if (offsetwords*sizeof(uint32_t) >= device->regspace.sizebytes) { + KGSL_DRV_ERR("invalid offset %d\n", offsetwords); + return -ERANGE; + } + + reg = (unsigned int *)(device->regspace.mmio_virt_base + + (offsetwords << 2)); + writel(value, reg); + + return 0; +} + +int kgsl_yamato_waittimestamp(struct kgsl_device *device, + unsigned int timestamp, + unsigned int msecs) +{ + long status; + + KGSL_DRV_INFO("enter (device=%p,timestamp=%d,timeout=0x%08x)\n", + device, timestamp, msecs); + + status = wait_event_interruptible_timeout(device->ib1_wq, + kgsl_cmdstream_check_timestamp(device, timestamp), + msecs_to_jiffies(msecs)); + + if (status > 0) + status = 0; + else if (status == 0) { + if (!kgsl_cmdstream_check_timestamp(device, timestamp)) { + status = -ETIMEDOUT; + kgsl_register_dump(device); + } + } + + KGSL_DRV_INFO("return %ld\n", status); + return (int)status; +} + +int kgsl_yamato_runpending(struct kgsl_device *device) +{ + if (device->flags & KGSL_FLAGS_INITIALIZED) + kgsl_cmdstream_memqueue_drain(device); + return 0; +} + +int __init kgsl_yamato_config(struct kgsl_devconfig *devconfig, + struct platform_device *pdev) +{ + int result = 0; + struct resource *res = NULL; + + memset(devconfig, 0, sizeof(*devconfig)); + + /*find memory regions */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "kgsl_reg_memory"); + if (res == NULL) { + KGSL_DRV_ERR("platform_get_resource_byname failed\n"); + result = -EINVAL; + goto done; + } + KGSL_DRV_DBG("registers at %08x to %08x\n", res->start, res->end); + devconfig->regspace.mmio_phys_base = res->start; + devconfig->regspace.sizebytes = resource_size(res); + + devconfig->gmemspace.gpu_base = 0; + devconfig->gmemspace.sizebytes = SZ_256K; + + /*note: for all of these behavior masks: + * 0 = do not translate + * 1 = translate within va_range, otherwise use phyisical + * 2 = translate within va_range, otherwise fault + */ + devconfig->mmu_config = 1 /* mmu enable */ + | (1 << MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT) + | (1 << MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT) + | (1 << MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT) + | (1 << MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT) + | (1 << MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT) + | (1 << MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT) + | (1 << MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT) + | (1 << MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT) + | (1 << MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT) + | (1 << MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT) + | (1 << MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT); + + /*TODO: these should probably be configurable from platform device + * stuff */ + devconfig->va_base = 0x66000000; + devconfig->va_range = SZ_32M; + + /* turn off memory protection unit by setting acceptable physical + * address range to include all pages. Apparrently MPU causing + * problems. + */ + devconfig->mpu_base = 0x00000000; + devconfig->mpu_range = 0xFFFFF000; + + result = 0; +done: + return result; +} diff --git a/drivers/video/msm/gpu/kgsl/yamato_reg.h b/drivers/video/msm/gpu/kgsl/yamato_reg.h new file mode 100644 index 0000000000000..e3382f7892437 --- /dev/null +++ b/drivers/video/msm/gpu/kgsl/yamato_reg.h @@ -0,0 +1,399 @@ +/* + * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 + * Copyright (c) 2008-2009 QUALCOMM USA, INC. + * + * All source code in this file is licensed under the following license + * + * 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, you can find it at http://www.fsf.org + */ +#ifndef _YAMATO_REG_H +#define _YAMATO_REG_H + +enum VGT_EVENT_TYPE { + VS_DEALLOC = 0, + PS_DEALLOC = 1, + VS_DONE_TS = 2, + PS_DONE_TS = 3, + CACHE_FLUSH_TS = 4, + CONTEXT_DONE = 5, + CACHE_FLUSH = 6, + VIZQUERY_START = 7, + VIZQUERY_END = 8, + SC_WAIT_WC = 9, + RST_PIX_CNT = 13, + RST_VTX_CNT = 14, + TILE_FLUSH = 15, + CACHE_FLUSH_AND_INV_TS_EVENT = 20, + ZPASS_DONE = 21, + CACHE_FLUSH_AND_INV_EVENT = 22, + PERFCOUNTER_START = 23, + PERFCOUNTER_STOP = 24, + VS_FETCH_DONE = 27, + FACENESS_FLUSH = 28, +}; + +enum COLORFORMATX { + COLORX_4_4_4_4 = 0, + COLORX_1_5_5_5 = 1, + COLORX_5_6_5 = 2, + COLORX_8 = 3, + COLORX_8_8 = 4, + COLORX_8_8_8_8 = 5, + COLORX_S8_8_8_8 = 6, + COLORX_16_FLOAT = 7, + COLORX_16_16_FLOAT = 8, + COLORX_16_16_16_16_FLOAT = 9, + COLORX_32_FLOAT = 10, + COLORX_32_32_FLOAT = 11, + COLORX_32_32_32_32_FLOAT = 12, + COLORX_2_3_3 = 13, + COLORX_8_8_8 = 14, +}; + +enum SURFACEFORMAT { + FMT_1_REVERSE = 0, + FMT_1 = 1, + FMT_8 = 2, + FMT_1_5_5_5 = 3, + FMT_5_6_5 = 4, + FMT_6_5_5 = 5, + FMT_8_8_8_8 = 6, + FMT_2_10_10_10 = 7, + FMT_8_A = 8, + FMT_8_B = 9, + FMT_8_8 = 10, + FMT_Cr_Y1_Cb_Y0 = 11, + FMT_Y1_Cr_Y0_Cb = 12, + FMT_5_5_5_1 = 13, + FMT_8_8_8_8_A = 14, + FMT_4_4_4_4 = 15, + FMT_10_11_11 = 16, + FMT_11_11_10 = 17, + FMT_DXT1 = 18, + FMT_DXT2_3 = 19, + FMT_DXT4_5 = 20, + FMT_24_8 = 22, + FMT_24_8_FLOAT = 23, + FMT_16 = 24, + FMT_16_16 = 25, + FMT_16_16_16_16 = 26, + FMT_16_EXPAND = 27, + FMT_16_16_EXPAND = 28, + FMT_16_16_16_16_EXPAND = 29, + FMT_16_FLOAT = 30, + FMT_16_16_FLOAT = 31, + FMT_16_16_16_16_FLOAT = 32, + FMT_32 = 33, + FMT_32_32 = 34, + FMT_32_32_32_32 = 35, + FMT_32_FLOAT = 36, + FMT_32_32_FLOAT = 37, + FMT_32_32_32_32_FLOAT = 38, + FMT_32_AS_8 = 39, + FMT_32_AS_8_8 = 40, + FMT_16_MPEG = 41, + FMT_16_16_MPEG = 42, + FMT_8_INTERLACED = 43, + FMT_32_AS_8_INTERLACED = 44, + FMT_32_AS_8_8_INTERLACED = 45, + FMT_16_INTERLACED = 46, + FMT_16_MPEG_INTERLACED = 47, + FMT_16_16_MPEG_INTERLACED = 48, + FMT_DXN = 49, + FMT_8_8_8_8_AS_16_16_16_16 = 50, + FMT_DXT1_AS_16_16_16_16 = 51, + FMT_DXT2_3_AS_16_16_16_16 = 52, + FMT_DXT4_5_AS_16_16_16_16 = 53, + FMT_2_10_10_10_AS_16_16_16_16 = 54, + FMT_10_11_11_AS_16_16_16_16 = 55, + FMT_11_11_10_AS_16_16_16_16 = 56, + FMT_32_32_32_FLOAT = 57, + FMT_DXT3A = 58, + FMT_DXT5A = 59, + FMT_CTX1 = 60, + FMT_DXT3A_AS_1_1_1_1 = 61 +}; + +#define RB_EDRAM_INFO_EDRAM_SIZE_SIZE 4 +#define RB_EDRAM_INFO_EDRAM_MAPPING_MODE_SIZE 2 +#define RB_EDRAM_INFO_UNUSED0_SIZE 8 +#define RB_EDRAM_INFO_EDRAM_RANGE_SIZE 18 + +struct rb_edram_info_t { + unsigned int edram_size:RB_EDRAM_INFO_EDRAM_SIZE_SIZE; + unsigned int edram_mapping_mode:RB_EDRAM_INFO_EDRAM_MAPPING_MODE_SIZE; + unsigned int unused0:RB_EDRAM_INFO_UNUSED0_SIZE; + unsigned int edram_range:RB_EDRAM_INFO_EDRAM_RANGE_SIZE; +}; + +union reg_rb_edram_info { + unsigned int val:32; + struct rb_edram_info_t f; +}; + +#define CP_RB_CNTL_RB_BUFSZ_SIZE 6 +#define CP_RB_CNTL_UNUSED0_SIZE 2 +#define CP_RB_CNTL_RB_BLKSZ_SIZE 6 +#define CP_RB_CNTL_UNUSED1_SIZE 2 +#define CP_RB_CNTL_BUF_SWAP_SIZE 2 +#define CP_RB_CNTL_UNUSED2_SIZE 2 +#define CP_RB_CNTL_RB_POLL_EN_SIZE 1 +#define CP_RB_CNTL_UNUSED3_SIZE 6 +#define CP_RB_CNTL_RB_NO_UPDATE_SIZE 1 +#define CP_RB_CNTL_UNUSED4_SIZE 3 +#define CP_RB_CNTL_RB_RPTR_WR_ENA_SIZE 1 + +struct cp_rb_cntl_t { + unsigned int rb_bufsz:CP_RB_CNTL_RB_BUFSZ_SIZE; + unsigned int unused0:CP_RB_CNTL_UNUSED0_SIZE; + unsigned int rb_blksz:CP_RB_CNTL_RB_BLKSZ_SIZE; + unsigned int unused1:CP_RB_CNTL_UNUSED1_SIZE; + unsigned int buf_swap:CP_RB_CNTL_BUF_SWAP_SIZE; + unsigned int unused2:CP_RB_CNTL_UNUSED2_SIZE; + unsigned int rb_poll_en:CP_RB_CNTL_RB_POLL_EN_SIZE; + unsigned int unused3:CP_RB_CNTL_UNUSED3_SIZE; + unsigned int rb_no_update:CP_RB_CNTL_RB_NO_UPDATE_SIZE; + unsigned int unused4:CP_RB_CNTL_UNUSED4_SIZE; + unsigned int rb_rptr_wr_ena:CP_RB_CNTL_RB_RPTR_WR_ENA_SIZE; +}; + +union reg_cp_rb_cntl { + unsigned int val:32; + struct cp_rb_cntl_t f; +}; + +#define RB_COLOR_INFO__COLOR_FORMAT_MASK 0x0000000fL +#define RB_COPY_DEST_INFO__COPY_DEST_FORMAT__SHIFT 0x00000004 + + +#define SQ_INT_CNTL__PS_WATCHDOG_MASK 0x00000001L +#define SQ_INT_CNTL__VS_WATCHDOG_MASK 0x00000002L + +#define MH_INTERRUPT_MASK__AXI_READ_ERROR 0x00000001L +#define MH_INTERRUPT_MASK__AXI_WRITE_ERROR 0x00000002L +#define MH_INTERRUPT_MASK__MMU_PAGE_FAULT 0x00000004L + +#define RBBM_INT_CNTL__RDERR_INT_MASK 0x00000001L +#define RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK 0x00000002L +#define RBBM_INT_CNTL__GUI_IDLE_INT_MASK 0x00080000L + +#define RBBM_STATUS__CMDFIFO_AVAIL_MASK 0x0000001fL +#define RBBM_STATUS__TC_BUSY_MASK 0x00000020L +#define RBBM_STATUS__HIRQ_PENDING_MASK 0x00000100L +#define RBBM_STATUS__CPRQ_PENDING_MASK 0x00000200L +#define RBBM_STATUS__CFRQ_PENDING_MASK 0x00000400L +#define RBBM_STATUS__PFRQ_PENDING_MASK 0x00000800L +#define RBBM_STATUS__VGT_BUSY_NO_DMA_MASK 0x00001000L +#define RBBM_STATUS__RBBM_WU_BUSY_MASK 0x00004000L +#define RBBM_STATUS__CP_NRT_BUSY_MASK 0x00010000L +#define RBBM_STATUS__MH_BUSY_MASK 0x00040000L +#define RBBM_STATUS__MH_COHERENCY_BUSY_MASK 0x00080000L +#define RBBM_STATUS__SX_BUSY_MASK 0x00200000L +#define RBBM_STATUS__TPC_BUSY_MASK 0x00400000L +#define RBBM_STATUS__SC_CNTX_BUSY_MASK 0x01000000L +#define RBBM_STATUS__PA_BUSY_MASK 0x02000000L +#define RBBM_STATUS__VGT_BUSY_MASK 0x04000000L +#define RBBM_STATUS__SQ_CNTX17_BUSY_MASK 0x08000000L +#define RBBM_STATUS__SQ_CNTX0_BUSY_MASK 0x10000000L +#define RBBM_STATUS__RB_CNTX_BUSY_MASK 0x40000000L +#define RBBM_STATUS__GUI_ACTIVE_MASK 0x80000000L + +#define CP_INT_CNTL__SW_INT_MASK 0x00080000L +#define CP_INT_CNTL__T0_PACKET_IN_IB_MASK 0x00800000L +#define CP_INT_CNTL__OPCODE_ERROR_MASK 0x01000000L +#define CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK 0x02000000L +#define CP_INT_CNTL__RESERVED_BIT_ERROR_MASK 0x04000000L +#define CP_INT_CNTL__IB_ERROR_MASK 0x08000000L +#define CP_INT_CNTL__IB2_INT_MASK 0x20000000L +#define CP_INT_CNTL__IB1_INT_MASK 0x40000000L +#define CP_INT_CNTL__RB_INT_MASK 0x80000000L + +#define MASTER_INT_SIGNAL__MH_INT_STAT 0x00000020L +#define MASTER_INT_SIGNAL__SQ_INT_STAT 0x04000000L +#define MASTER_INT_SIGNAL__CP_INT_STAT 0x40000000L +#define MASTER_INT_SIGNAL__RBBM_INT_STAT 0x80000000L + +#define RB_EDRAM_INFO__EDRAM_SIZE_MASK 0x0000000fL +#define RB_EDRAM_INFO__EDRAM_RANGE_MASK 0xffffc000L + +#define MH_ARBITER_CONFIG__SAME_PAGE_GRANULARITY__SHIFT 0x00000006 +#define MH_ARBITER_CONFIG__L1_ARB_ENABLE__SHIFT 0x00000007 +#define MH_ARBITER_CONFIG__L1_ARB_HOLD_ENABLE__SHIFT 0x00000008 +#define MH_ARBITER_CONFIG__L2_ARB_CONTROL__SHIFT 0x00000009 +#define MH_ARBITER_CONFIG__PAGE_SIZE__SHIFT 0x0000000a +#define MH_ARBITER_CONFIG__TC_REORDER_ENABLE__SHIFT 0x0000000d +#define MH_ARBITER_CONFIG__TC_ARB_HOLD_ENABLE__SHIFT 0x0000000e +#define MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT_ENABLE__SHIFT 0x0000000f +#define MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT__SHIFT 0x00000010 +#define MH_ARBITER_CONFIG__CP_CLNT_ENABLE__SHIFT 0x00000016 +#define MH_ARBITER_CONFIG__VGT_CLNT_ENABLE__SHIFT 0x00000017 +#define MH_ARBITER_CONFIG__TC_CLNT_ENABLE__SHIFT 0x00000018 +#define MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT 0x00000019 +#define MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT 0x0000001a + +#define MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT 0x00000004 +#define MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT 0x00000006 +#define MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT 0x00000008 +#define MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT 0x0000000a +#define MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT 0x0000000c +#define MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT 0x0000000e +#define MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT 0x00000010 +#define MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT 0x00000012 +#define MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT 0x00000014 +#define MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT 0x00000016 +#define MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT 0x00000018 + +#define CP_RB_CNTL__RB_BUFSZ__SHIFT 0x00000000 +#define CP_RB_CNTL__RB_BLKSZ__SHIFT 0x00000008 +#define CP_RB_CNTL__RB_POLL_EN__SHIFT 0x00000014 +#define CP_RB_CNTL__RB_NO_UPDATE__SHIFT 0x0000001b + +#define RB_COLOR_INFO__COLOR_FORMAT__SHIFT 0x00000000 +#define RB_EDRAM_INFO__EDRAM_MAPPING_MODE__SHIFT 0x00000004 +#define RB_EDRAM_INFO__EDRAM_RANGE__SHIFT 0x0000000e + +#define REG_CP_CSQ_IB1_STAT 0x01FE +#define REG_CP_CSQ_IB2_STAT 0x01FF +#define REG_CP_CSQ_RB_STAT 0x01FD +#define REG_CP_DEBUG 0x01FC +#define REG_CP_IB1_BASE 0x0458 +#define REG_CP_IB1_BUFSZ 0x0459 +#define REG_CP_IB2_BASE 0x045A +#define REG_CP_IB2_BUFSZ 0x045B +#define REG_CP_INT_ACK 0x01F4 +#define REG_CP_INT_CNTL 0x01F2 +#define REG_CP_INT_STATUS 0x01F3 +#define REG_CP_ME_CNTL 0x01F6 +#define REG_CP_ME_RAM_DATA 0x01FA +#define REG_CP_ME_RAM_WADDR 0x01F8 +#define REG_CP_ME_STATUS 0x01F7 +#define REG_CP_PFP_UCODE_ADDR 0x00C0 +#define REG_CP_PFP_UCODE_DATA 0x00C1 +#define REG_CP_QUEUE_THRESHOLDS 0x01D5 +#define REG_CP_RB_BASE 0x01C0 +#define REG_CP_RB_CNTL 0x01C1 +#define REG_CP_RB_RPTR 0x01C4 +#define REG_CP_RB_RPTR_ADDR 0x01C3 +#define REG_CP_RB_RPTR_WR 0x01C7 +#define REG_CP_RB_WPTR 0x01C5 +#define REG_CP_RB_WPTR_BASE 0x01C8 +#define REG_CP_RB_WPTR_DELAY 0x01C6 +#define REG_CP_STAT 0x047F +#define REG_CP_STATE_DEBUG_DATA 0x01ED +#define REG_CP_STATE_DEBUG_INDEX 0x01EC +#define REG_CP_ST_BASE 0x044D +#define REG_CP_ST_BUFSZ 0x044E + +#define REG_MASTER_INT_SIGNAL 0x03B7 + +#define REG_MH_ARBITER_CONFIG 0x0A40 +#define REG_MH_INTERRUPT_CLEAR 0x0A44 +#define REG_MH_INTERRUPT_MASK 0x0A42 +#define REG_MH_INTERRUPT_STATUS 0x0A43 +#define REG_MH_MMU_CONFIG 0x0040 +#define REG_MH_MMU_INVALIDATE 0x0045 +#define REG_MH_MMU_MPU_BASE 0x0046 +#define REG_MH_MMU_MPU_END 0x0047 +#define REG_MH_MMU_PAGE_FAULT 0x0043 +#define REG_MH_MMU_PT_BASE 0x0042 +#define REG_MH_MMU_TRAN_ERROR 0x0044 +#define REG_MH_MMU_VA_RANGE 0x0041 + +#define REG_PA_CL_VPORT_XSCALE 0x210F +#define REG_PA_CL_VPORT_ZOFFSET 0x2114 +#define REG_PA_CL_VPORT_ZSCALE 0x2113 +#define REG_PA_CL_VTE_CNTL 0x2206 +#define REG_PA_SC_AA_MASK 0x2312 +#define REG_PA_SC_LINE_CNTL 0x2300 +#define REG_PA_SC_SCREEN_SCISSOR_BR 0x200F +#define REG_PA_SC_SCREEN_SCISSOR_TL 0x200E +#define REG_PA_SC_VIZ_QUERY 0x2293 +#define REG_PA_SC_VIZ_QUERY_STATUS 0x0C44 +#define REG_PA_SC_WINDOW_OFFSET 0x2080 +#define REG_PA_SC_WINDOW_SCISSOR_BR 0x2082 +#define REG_PA_SC_WINDOW_SCISSOR_TL 0x2081 +#define REG_PA_SU_FACE_DATA 0x0C86 +#define REG_PA_SU_POINT_SIZE 0x2280 +#define REG_PA_SU_POLY_OFFSET_BACK_OFFSET 0x2383 +#define REG_PA_SU_POLY_OFFSET_FRONT_SCALE 0x2380 +#define REG_PA_SU_SC_MODE_CNTL 0x2205 + +#define REG_RBBM_CNTL 0x003B +#define REG_RBBM_INT_ACK 0x03B6 +#define REG_RBBM_INT_CNTL 0x03B4 +#define REG_RBBM_INT_STATUS 0x03B5 +#define REG_RBBM_PATCH_RELEASE 0x0001 +#define REG_RBBM_PERIPHID1 0x03F9 +#define REG_RBBM_PERIPHID2 0x03FA +#define REG_RBBM_PM_OVERRIDE1 0x039C +#define REG_RBBM_PM_OVERRIDE2 0x039D +#define REG_RBBM_READ_ERROR 0x03B3 +#define REG_RBBM_SOFT_RESET 0x003C +#define REG_RBBM_STATUS 0x05D0 + +#define REG_RB_COLORCONTROL 0x2202 +#define REG_RB_COLOR_DEST_MASK 0x2326 +#define REG_RB_COLOR_MASK 0x2104 +#define REG_RB_COPY_CONTROL 0x2318 +#define REG_RB_DEPTHCONTROL 0x2200 +#define REG_RB_EDRAM_INFO 0x0F02 +#define REG_RB_MODECONTROL 0x2208 +#define REG_RB_SURFACE_INFO 0x2000 + +#define REG_SCRATCH_ADDR 0x01DD +#define REG_SCRATCH_REG0 0x0578 +#define REG_SCRATCH_REG2 0x057A +#define REG_SCRATCH_UMSK 0x01DC + +#define REG_SQ_CF_BOOLEANS 0x4900 +#define REG_SQ_CF_LOOP 0x4908 +#define REG_SQ_GPR_MANAGEMENT 0x0D00 +#define REG_SQ_INST_STORE_MANAGMENT 0x0D02 +#define REG_SQ_INT_ACK 0x0D36 +#define REG_SQ_INT_CNTL 0x0D34 +#define REG_SQ_INT_STATUS 0x0D35 +#define REG_SQ_PROGRAM_CNTL 0x2180 +#define REG_SQ_PS_PROGRAM 0x21F6 +#define REG_SQ_VS_PROGRAM 0x21F7 +#define REG_SQ_WRAPPING_0 0x2183 +#define REG_SQ_WRAPPING_1 0x2184 + +#define REG_VGT_ENHANCE 0x2294 +#define REG_VGT_INDX_OFFSET 0x2102 +#define REG_VGT_MAX_VTX_INDX 0x2100 +#define REG_VGT_MIN_VTX_INDX 0x2101 + +#define REG_TP0_CHICKEN 0x0E1E +#define REG_TC_CNTL_STATUS 0x0E00 +#define REG_PA_SC_AA_CONFIG 0x2301 +#define REG_VGT_VERTEX_REUSE_BLOCK_CNTL 0x2316 +#define REG_SQ_INTERPOLATOR_CNTL 0x2182 +#define REG_RB_DEPTH_INFO 0x2002 +#define REG_COHER_DEST_BASE_0 0x2006 +#define REG_PA_SC_SCREEN_SCISSOR_BR 0x200F +#define REG_RB_FOG_COLOR 0x2109 +#define REG_RB_STENCILREFMASK_BF 0x210C +#define REG_PA_SC_LINE_STIPPLE 0x2283 +#define REG_SQ_PS_CONST 0x2308 +#define REG_VGT_VERTEX_REUSE_BLOCK_CNTL 0x2316 +#define REG_RB_DEPTH_CLEAR 0x231D +#define REG_RB_SAMPLE_COUNT_CTL 0x2324 +#define REG_SQ_CONSTANT_0 0x4000 +#define REG_SQ_FETCH_0 0x4800 + +#define REG_MH_AXI_ERROR 0xA45 +#define REG_COHER_BASE_PM4 0xA2A +#define REG_COHER_STATUS_PM4 0xA2B +#define REG_COHER_SIZE_PM4 0xA29 + +#endif /* _YAMATO_REG_H */ diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h new file mode 100644 index 0000000000000..af5cca6057f07 --- /dev/null +++ b/include/linux/msm_kgsl.h @@ -0,0 +1,271 @@ +/* + * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 + * Copyright (c) 2008-2009 QUALCOMM USA, INC. + * + * All source code in this file is licensed under the following license + * + * 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, you can find it at http://www.fsf.org + */ +#ifndef _MSM_KGSL_H +#define _MSM_KGSL_H + +/*context flags */ +#define KGSL_CONTEXT_SAVE_GMEM 1 +#define KGSL_CONTEXT_NO_GMEM_ALLOC 2 + +/* generic flag values */ +#define KGSL_FLAGS_NORMALMODE 0x00000000 +#define KGSL_FLAGS_SAFEMODE 0x00000001 +#define KGSL_FLAGS_INITIALIZED0 0x00000002 +#define KGSL_FLAGS_INITIALIZED 0x00000004 +#define KGSL_FLAGS_STARTED 0x00000008 +#define KGSL_FLAGS_ACTIVE 0x00000010 +#define KGSL_FLAGS_RESERVED0 0x00000020 +#define KGSL_FLAGS_RESERVED1 0x00000040 +#define KGSL_FLAGS_RESERVED2 0x00000080 + +/* device id */ +enum kgsl_deviceid { + KGSL_DEVICE_ANY = 0x00000000, + KGSL_DEVICE_YAMATO = 0x00000001, + KGSL_DEVICE_G12 = 0x00000002, + KGSL_DEVICE_MAX = 0x00000002 +}; + +struct kgsl_devinfo { + + unsigned int device_id; + /* chip revision id + * coreid:8 majorrev:8 minorrev:8 patch:8 + */ + unsigned int chip_id; + unsigned int mmu_enabled; + unsigned int gmem_gpubaseaddr; + /* if gmem_hostbaseaddr is NULL, we would know its not mapped into + * mmio space */ + unsigned int gmem_hostbaseaddr; + unsigned int gmem_sizebytes; +}; + +/* this structure defines the region of memory that can be mmap()ed from this + driver. The timestamp fields are volatile because they are written by the + GPU +*/ +struct kgsl_devmemstore { + volatile unsigned int soptimestamp; + unsigned int sbz; + volatile unsigned int eoptimestamp; + unsigned int sbz2; +}; + +#define KGSL_DEVICE_MEMSTORE_OFFSET(field) \ + offsetof(struct kgsl_devmemstore, field) + + +/* timestamp id*/ +enum kgsl_timestamp_type { + KGSL_TIMESTAMP_CONSUMED = 0x00000001, /* start-of-pipeline timestamp */ + KGSL_TIMESTAMP_RETIRED = 0x00000002, /* end-of-pipeline timestamp*/ + KGSL_TIMESTAMP_MAX = 0x00000002, +}; + +/* property types - used with kgsl_device_getproperty */ +enum kgsl_property_type { + KGSL_PROP_DEVICE_INFO = 0x00000001, + KGSL_PROP_DEVICE_SHADOW = 0x00000002, + KGSL_PROP_DEVICE_POWER = 0x00000003, + KGSL_PROP_SHMEM = 0x00000004, + KGSL_PROP_SHMEM_APERTURES = 0x00000005, + KGSL_PROP_MMU_ENABLE = 0x00000006 +}; + +struct kgsl_shadowprop { + unsigned int gpuaddr; + unsigned int size; + unsigned int flags; /* contains KGSL_FLAGS_ values */ +}; + +/* ioctls */ +#define KGSL_IOC_TYPE 0x09 + +/* get misc info about the GPU + type should be a value from enum kgsl_property_type + value points to a structure that varies based on type + sizebytes is sizeof() that structure + for KGSL_PROP_DEVICE_INFO, use struct kgsl_devinfo + this structure contaings hardware versioning info. + for KGSL_PROP_DEVICE_SHADOW, use struct kgsl_shadowprop + this is used to find mmap() offset and sizes for mapping + struct kgsl_memstore into userspace. +*/ +struct kgsl_device_getproperty { + unsigned int type; + void *value; + unsigned int sizebytes; +}; + +#define IOCTL_KGSL_DEVICE_GETPROPERTY \ + _IOWR(KGSL_IOC_TYPE, 0x2, struct kgsl_device_getproperty) + + +/* read a GPU register. + offsetwords it the 32 bit word offset from the beginning of the + GPU register space. + */ +struct kgsl_device_regread { + unsigned int offsetwords; + unsigned int value; /* output param */ +}; + +#define IOCTL_KGSL_DEVICE_REGREAD \ + _IOWR(KGSL_IOC_TYPE, 0x3, struct kgsl_device_regread) + + +/* block until the GPU has executed past a given timestamp + * timeout is in milliseconds. + */ +struct kgsl_device_waittimestamp { + unsigned int timestamp; + unsigned int timeout; +}; + +#define IOCTL_KGSL_DEVICE_WAITTIMESTAMP \ + _IOW(KGSL_IOC_TYPE, 0x6, struct kgsl_device_waittimestamp) + + +/* issue indirect commands to the GPU. + * drawctxt_id must have been created with IOCTL_KGSL_DRAWCTXT_CREATE + * ibaddr and sizedwords must specify a subset of a buffer created + * with IOCTL_KGSL_SHAREDMEM_FROM_PMEM + * flags may be a mask of KGSL_CONTEXT_ values + * timestamp is a returned counter value which can be passed to + * other ioctls to determine when the commands have been executed by + * the GPU. + */ +struct kgsl_ringbuffer_issueibcmds { + unsigned int drawctxt_id; + unsigned int ibaddr; + unsigned int sizedwords; + unsigned int timestamp; /*output param */ + unsigned int flags; +}; + +#define IOCTL_KGSL_RINGBUFFER_ISSUEIBCMDS \ + _IOWR(KGSL_IOC_TYPE, 0x10, struct kgsl_ringbuffer_issueibcmds) + +/* read the most recently executed timestamp value + * type should be a value from enum kgsl_timestamp_type + */ +struct kgsl_cmdstream_readtimestamp { + unsigned int type; + unsigned int timestamp; /*output param */ +}; + +#define IOCTL_KGSL_CMDSTREAM_READTIMESTAMP \ + _IOR(KGSL_IOC_TYPE, 0x11, struct kgsl_cmdstream_readtimestamp) + +/* free memory when the GPU reaches a given timestamp. + * gpuaddr specify a memory region created by a + * IOCTL_KGSL_SHAREDMEM_FROM_PMEM call + * type should be a value from enum kgsl_timestamp_type + */ +struct kgsl_cmdstream_freememontimestamp { + unsigned int gpuaddr; + unsigned int type; + unsigned int timestamp; +}; + +#define IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP \ + _IOR(KGSL_IOC_TYPE, 0x12, struct kgsl_cmdstream_freememontimestamp) + +/* create a draw context, which is used to preserve GPU state. + * The flags field may contain a mask KGSL_CONTEXT_* values + */ +struct kgsl_drawctxt_create { + unsigned int flags; + unsigned int drawctxt_id; /*output param */ +}; + +#define IOCTL_KGSL_DRAWCTXT_CREATE \ + _IOWR(KGSL_IOC_TYPE, 0x13, struct kgsl_drawctxt_create) + +/* destroy a draw context */ +struct kgsl_drawctxt_destroy { + unsigned int drawctxt_id; +}; + +#define IOCTL_KGSL_DRAWCTXT_DESTROY \ + _IOW(KGSL_IOC_TYPE, 0x14, struct kgsl_drawctxt_destroy) + +/* add a block of pmem into the GPU address space */ +struct kgsl_sharedmem_from_pmem { + int pmem_fd; + unsigned int gpuaddr; /*output param */ +}; + +#define IOCTL_KGSL_SHAREDMEM_FROM_PMEM \ + _IOWR(KGSL_IOC_TYPE, 0x20, struct kgsl_sharedmem_from_pmem) + +/* remove memory from the GPU's address space */ +struct kgsl_sharedmem_free { + unsigned int gpuaddr; +}; + +#define IOCTL_KGSL_SHAREDMEM_FREE \ + _IOW(KGSL_IOC_TYPE, 0x21, struct kgsl_sharedmem_free) + +struct kgsl_gmem_desc { + unsigned int x; + unsigned int y; + unsigned int width; + unsigned int height; + unsigned int pitch; +}; + +struct kgsl_buffer_desc { + void *hostptr; + unsigned int gpuaddr; + int size; + unsigned int format; + unsigned int pitch; + unsigned int enabled; +}; + +struct kgsl_bind_gmem_shadow { + unsigned int drawctxt_id; + struct kgsl_gmem_desc gmem_desc; + unsigned int shadow_x; + unsigned int shadow_y; + struct kgsl_buffer_desc shadow_buffer; + unsigned int buffer_id; +}; + +#define IOCTL_KGSL_DRAWCTXT_BIND_GMEM_SHADOW \ + _IOW(KGSL_IOC_TYPE, 0x22, struct kgsl_bind_gmem_shadow) + +/* add a block of memory into the GPU address space */ +struct kgsl_sharedmem_from_vmalloc { + unsigned int gpuaddr; /*output param */ + unsigned int hostptr; + /* If set from user space then will attempt to + * allocate even if low watermark is crossed */ + int force_no_low_watermark; +}; + +#define IOCTL_KGSL_SHAREDMEM_FROM_VMALLOC \ + _IOWR(KGSL_IOC_TYPE, 0x23, struct kgsl_sharedmem_from_vmalloc) + +#define IOCTL_KGSL_SHAREDMEM_FLUSH_CACHE \ + _IOW(KGSL_IOC_TYPE, 0x24, struct kgsl_sharedmem_free) + +#endif /* _MSM_KGSL_H */ From 2647b0af9d6f87508361a0fc62277077d05c0552 Mon Sep 17 00:00:00 2001 From: Omprakash Dhyade Date: Wed, 4 Nov 2009 21:43:00 -0800 Subject: [PATCH 0387/2556] [ARM] video: msm: kgsl: Bypass SQ RTR for host accesses. Host access are blocked based on status of SQ block this may cause 3ms delay on the bus to access a register. This delay can lock out GSM and cause fatal error in MDSP. So, bypass SQ RTR for host register access. Change-Id: Ia096d7c3a6a8f04c8ffdcbf0a9547bb281e6bc39 Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl_yamato.c | 2 ++ drivers/video/msm/gpu/kgsl/yamato_reg.h | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c index 1e59b92206ad4..34911948991ed 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c @@ -460,6 +460,8 @@ int kgsl_yamato_init(struct kgsl_device *device, struct kgsl_devconfig *config) } kgsl_sharedmem_set(&device->memstore, 0, 0, device->memstore.size); + kgsl_yamato_regwrite(device, REG_RBBM_DEBUG, 0x00080000); + pr_info("msm_kgsl: initilized dev=%d mmu=%s\n", device->id, kgsl_mmu_isenabled(&device->mmu) ? "on" : "off"); diff --git a/drivers/video/msm/gpu/kgsl/yamato_reg.h b/drivers/video/msm/gpu/kgsl/yamato_reg.h index e3382f7892437..828b95aa61d6f 100644 --- a/drivers/video/msm/gpu/kgsl/yamato_reg.h +++ b/drivers/video/msm/gpu/kgsl/yamato_reg.h @@ -335,6 +335,7 @@ union reg_cp_rb_cntl { #define REG_RBBM_PATCH_RELEASE 0x0001 #define REG_RBBM_PERIPHID1 0x03F9 #define REG_RBBM_PERIPHID2 0x03FA +#define REG_RBBM_DEBUG 0x039B #define REG_RBBM_PM_OVERRIDE1 0x039C #define REG_RBBM_PM_OVERRIDE2 0x039D #define REG_RBBM_READ_ERROR 0x03B3 From 5bcd37651a3192f1f375fba719b7204dd08a5c4f Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sun, 8 Nov 2009 13:13:00 -0800 Subject: [PATCH 0388/2556] [ARM] video: msm: kgsl: Set the GPU mappable virtual address space to 128MB. Change-Id: Ic0cf8b12e6e0b34a2dd60a313dd807ba3e25c874 Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl_yamato.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c index 34911948991ed..f5de394b8ebfd 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c @@ -848,7 +848,7 @@ int __init kgsl_yamato_config(struct kgsl_devconfig *devconfig, /*TODO: these should probably be configurable from platform device * stuff */ devconfig->va_base = 0x66000000; - devconfig->va_range = SZ_32M; + devconfig->va_range = SZ_128M; /* turn off memory protection unit by setting acceptable physical * address range to include all pages. Apparrently MPU causing From 98edb44e45392c2b9ef2691386715703a440bab7 Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Wed, 11 Nov 2009 17:54:18 -0700 Subject: [PATCH 0389/2556] [ARM] video: msm: kgsl: Properly restore the bin base offset on context switches The correct bin base offset is set for the context whenever it changes after the binning pass so that it can be restored on context switch. Change-Id: I688eb3daf94fa68fa4e1defd7c5ef0ac797b6c10 Signed-off-by: Shubhraprakash Das Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl.c | 20 ++++++++++++++++++++ drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c | 21 +++++++++++++++++++++ drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h | 6 +++++- include/linux/msm_kgsl.h | 8 ++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c index 3bc71fcd58e93..450aa8caecefa 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.c +++ b/drivers/video/msm/gpu/kgsl/kgsl.c @@ -860,6 +860,7 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { int result = 0; struct kgsl_file_private *private = filep->private_data; + struct kgsl_drawctxt_set_bin_base_offset binbase; BUG_ON(private == NULL); @@ -932,6 +933,25 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) (void __user *)arg); break; + case IOCTL_KGSL_DRAWCTXT_SET_BIN_BASE_OFFSET: + if (copy_from_user(&binbase, (void __user *)arg, + sizeof(binbase))) { + result = -EFAULT; + break; + } + + if (private->ctxt_id_mask & (1 << binbase.drawctxt_id)) { + result = kgsl_drawctxt_set_bin_base_offset( + &kgsl_driver.yamato_device, + binbase.drawctxt_id, + binbase.offset); + } else { + result = -EINVAL; + KGSL_DRV_ERR("invalid drawctxt drawctxt_id %d\n", + binbase.drawctxt_id); + } + break; + default: KGSL_DRV_ERR("invalid ioctl code %08x\n", cmd); result = -EINVAL; diff --git a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c index 305e4bb61cfb0..dcf481315c4dd 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c @@ -1448,6 +1448,7 @@ kgsl_drawctxt_create(struct kgsl_device *device, drawctxt = &device->drawctxt[index]; drawctxt->pagetable = pagetable; drawctxt->flags = CTXT_FLAGS_IN_USE; + drawctxt->bin_base_offset = 0; device->drawctxt_count++; @@ -1639,12 +1640,27 @@ int kgsl_drawctxt_bind_gmem_shadow(struct kgsl_device *device, return 0; } +/* set bin base offset */ +int kgsl_drawctxt_set_bin_base_offset(struct kgsl_device *device, + unsigned int drawctxt_id, + unsigned int offset) +{ + struct kgsl_drawctxt *drawctxt; + + drawctxt = &device->drawctxt[drawctxt_id]; + + drawctxt->bin_base_offset = offset; + + return 0; +} + /* switch drawing contexts */ void kgsl_drawctxt_switch(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt, unsigned int flags) { struct kgsl_drawctxt *active_ctxt = device->drawctxt_active; + unsigned int cmds[2]; /* already current? */ if (active_ctxt == drawctxt) @@ -1761,6 +1777,11 @@ kgsl_drawctxt_switch(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt, KGSL_CTXT_DBG("restore shader"); kgsl_ringbuffer_issuecmds(device, 0, drawctxt->shader_restore, 3); + + cmds[0] = pm4_type3_packet(PM4_SET_BIN_BASE_OFFSET, 1); + cmds[1] = drawctxt->bin_base_offset; + kgsl_ringbuffer_issuecmds(device, 0, cmds, 2); + } KGSL_CTXT_INFO("return\n"); } diff --git a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h index d5bd805b83420..3090373e30fe7 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h +++ b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h @@ -84,7 +84,7 @@ struct kgsl_drawctxt { unsigned int shader_fixup[3]; unsigned int shader_restore[3]; unsigned int chicken_restore[3]; - + unsigned int bin_base_offset; /* Information of the GMEM shadow that is created in context create */ struct gmem_shadow_t context_gmem_shadow; /* User defined GMEM shadow buffers */ @@ -113,4 +113,8 @@ int kgsl_drawctxt_bind_gmem_shadow(struct kgsl_device *device, const struct kgsl_buffer_desc *shadow_buffer, unsigned int buffer_id); +int kgsl_drawctxt_set_bin_base_offset(struct kgsl_device *device, + unsigned int drawctxt_id, + unsigned int offset); + #endif /* __GSL_DRAWCTXT_H */ diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index af5cca6057f07..5266329130972 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -268,4 +268,12 @@ struct kgsl_sharedmem_from_vmalloc { #define IOCTL_KGSL_SHAREDMEM_FLUSH_CACHE \ _IOW(KGSL_IOC_TYPE, 0x24, struct kgsl_sharedmem_free) +struct kgsl_drawctxt_set_bin_base_offset { + unsigned int drawctxt_id; + unsigned int offset; +}; + +#define IOCTL_KGSL_DRAWCTXT_SET_BIN_BASE_OFFSET \ + _IOW(KGSL_IOC_TYPE, 0x25, struct kgsl_drawctxt_set_bin_base_offset) + #endif /* _MSM_KGSL_H */ From e004d39b60144c5cba49ec0a260b98e7560eec9e Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Wed, 11 Nov 2009 17:55:19 -0700 Subject: [PATCH 0390/2556] [ARM] video: msm: kgsl: Set the right values for PM4_DRAW_INDX_BIN and PM4_DRAW_INDX_2_BIN Change-Id: Ic57a4bfd512c13c09329ddde20064c78fca71a7f Signed-off-by: Shubhraprakash Das Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl_pm4types.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_pm4types.h b/drivers/video/msm/gpu/kgsl/kgsl_pm4types.h index 018f03f9e5167..7c735bd76f610 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_pm4types.h +++ b/drivers/video/msm/gpu/kgsl/kgsl_pm4types.h @@ -99,10 +99,10 @@ #define PM4_DRAW_INDX_2 0x36 /* initiate fetch of index buffer and binIDs and draw */ -#define PM4_DRAW_INDX_BIN 0x37 +#define PM4_DRAW_INDX_BIN 0x34 /* initiate fetch of bin IDs and draw using supplied indices */ -#define PM4_DRAW_INDX_2_BIN 0x38 +#define PM4_DRAW_INDX_2_BIN 0x35 /* begin/end initiator for viz query extent processing */ From a33d5d249b45abecbb10fc30e2857edb5bee16ab Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Wed, 11 Nov 2009 18:01:10 -0700 Subject: [PATCH 0391/2556] [ARM] video: msm: kgsl: Do not wait for the REG_CP_RB_WPTR to be updated kgsl_yamato_regread can cause rbbm read errors and it is not nessasary to wait for REG_CP_RB_WPTR to be updated here anyway. Change-Id: I65b641074a05ee4217dfad81c255e06c49c45aa0 Signed-off-by: Shubhraprakash Das Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c index 99a0eb293adce..fef527da4c66f 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c @@ -169,8 +169,6 @@ void kgsl_ringbuffer_watchdog() static void kgsl_ringbuffer_submit(struct kgsl_ringbuffer *rb) { - unsigned int value; - BUG_ON(rb->wptr == 0); GSL_RB_UPDATE_WPTR_POLLING(rb); @@ -186,11 +184,6 @@ static void kgsl_ringbuffer_submit(struct kgsl_ringbuffer *rb) kgsl_yamato_regwrite(rb->device, REG_CP_RB_WPTR, rb->wptr); - /* force wptr register to be updated */ - do { - kgsl_yamato_regread(rb->device, REG_CP_RB_WPTR, &value); - } while (value != rb->wptr); - rb->flags |= KGSL_FLAGS_ACTIVE; } From 6141833e645b9893893f29da281382ff4049ab57 Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Wed, 11 Nov 2009 19:11:49 -0700 Subject: [PATCH 0392/2556] [ARM] video: msm: kgsl: Call kgsl_yamato_runpending before allocating memory. Calling kgsl_yamato_runpending before we allocate memory will cause the driver to free all pending buffers which will improve the chance of memory allocation on low memory conditions. Change-Id: I847dce494dcfa0e75ee43b6b0c544065d2301acf Signed-off-by: Shubhraprakash Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c index 450aa8caecefa..85a13a5bee24f 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.c +++ b/drivers/video/msm/gpu/kgsl/kgsl.c @@ -919,6 +919,7 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) break; case IOCTL_KGSL_SHAREDMEM_FROM_VMALLOC: + kgsl_yamato_runpending(&kgsl_driver.yamato_device); result = kgsl_ioctl_sharedmem_from_vmalloc(private, (void __user *)arg); break; @@ -929,6 +930,7 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) (void __user *)arg); break; case IOCTL_KGSL_SHAREDMEM_FROM_PMEM: + kgsl_yamato_runpending(&kgsl_driver.yamato_device); result = kgsl_ioctl_sharedmem_from_pmem(private, (void __user *)arg); break; From dc770f066888319fe6aef4846d92db3f920dfd51 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 17 Nov 2009 02:09:14 -0800 Subject: [PATCH 0393/2556] [ARM] msm: kgsl: don't print the reschedule warning from standby timer Change-Id: I04e55a193bfbda178716a348b7b1374adaf36558 Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c index 85a13a5bee24f..a515fc7e7ac19 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.c +++ b/drivers/video/msm/gpu/kgsl/kgsl.c @@ -166,13 +166,11 @@ static void kgsl_hw_put_locked(bool start_timer) static void kgsl_do_standby_timer(unsigned long data) { - if (kgsl_yamato_is_idle(&kgsl_driver.yamato_device)) { + if (kgsl_yamato_is_idle(&kgsl_driver.yamato_device)) kgsl_hw_disable(); - } else { - pr_warning("%s: not idle, rescheduling\n", __func__); + else mod_timer(&kgsl_driver.standby_timer, jiffies + msecs_to_jiffies(10)); - } } /* file operations */ From 7c095c7b0228b621a626cfc7a13a0cd8ba09affb Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Mon, 16 Nov 2009 20:22:27 -0700 Subject: [PATCH 0394/2556] [ARM] msm: kgsl: Fixes multicontext screen corruption issue This change fixes an issue in multicontext environment where some part of texture of one apllication gets displayed on the screen surface of another application Change-Id: Ic5ff3655d2cf7ffad28f2483982f557236335369 Signed-off-by: Shubhraprakash Das Acked-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c index dcf481315c4dd..dc72c4db39003 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c @@ -149,6 +149,9 @@ unsigned int uint2float(unsigned int uintval) unsigned int frac = 0; unsigned int u = uintval; + /* Handle zero separately */ + if (uintval == 0) + return 0; /* Find log2 of u */ if (u >= 0x10000) { exp += 16; From 421c8ccef684093168f367ab5bd7ad6e1b3c3174 Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Mon, 23 Nov 2009 18:03:55 -0700 Subject: [PATCH 0395/2556] [ARM] msm: kgsl: save gmem based on correct use of flags Corrects the way we save gmem. Renderboy informs kgsl via a flag when the gmem should be saved by kgsl on a context switch and when it does not need to be saved. Change-Id: I36c55049188f335a3353c65fd0e4f7b2c672cc24 Signed-off-by: Shubhraprakash Das Acked-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c index dc72c4db39003..6176fa11aff14 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c @@ -1665,6 +1665,14 @@ kgsl_drawctxt_switch(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt, struct kgsl_drawctxt *active_ctxt = device->drawctxt_active; unsigned int cmds[2]; + if (drawctxt) { + /* Set the flag in context so that the save is done + * when this context is switched out. */ + if (flags & KGSL_CONTEXT_SAVE_GMEM) + drawctxt->flags |= CTXT_FLAGS_GMEM_SAVE; + else + drawctxt->flags &= ~CTXT_FLAGS_GMEM_SAVE; + } /* already current? */ if (active_ctxt == drawctxt) return; @@ -1695,7 +1703,7 @@ kgsl_drawctxt_switch(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt, } if (active_ctxt->flags & CTXT_FLAGS_GMEM_SAVE - && flags & KGSL_CONTEXT_SAVE_GMEM) { + && active_ctxt->flags & CTXT_FLAGS_GMEM_SHADOW) { /* save gmem. * (note: changes shader. shader must already be saved.) */ From 3a038b8b44c823e3351d9a1e5f62e011712d5b89 Mon Sep 17 00:00:00 2001 From: Shubh Das Date: Mon, 30 Nov 2009 17:26:17 -0700 Subject: [PATCH 0396/2556] [ARM] msm: kgsl: Map all memory allocations through the MMU Map all memory allocations through the MMU and set it up to fault if an address does not lie within its virtual address range. The only allocations that are not mapped are the allocations for MMU page table and a dummyspace allocation whose address is written to the REG_MH_MMU_TRAN_ERROR register. Change-Id: I92f93d87a9d4d2551d58520d46d820f164b150dc Signed-off-by: Simon Wilson Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl.c | 46 +++++++-- drivers/video/msm/gpu/kgsl/kgsl_device.h | 4 + drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c | 24 ++++- drivers/video/msm/gpu/kgsl/kgsl_mmu.c | 61 ++++++++---- drivers/video/msm/gpu/kgsl/kgsl_mmu.h | 13 ++- drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c | 16 +++- drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c | 1 - drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h | 1 + drivers/video/msm/gpu/kgsl/kgsl_yamato.c | 98 +++++++++++++++++--- include/linux/msm_kgsl.h | 4 +- 10 files changed, 223 insertions(+), 45 deletions(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c index a515fc7e7ac19..cb7b8d74edfd1 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.c +++ b/drivers/video/msm/gpu/kgsl/kgsl.c @@ -249,6 +249,8 @@ static int kgsl_release(struct inode *inodep, struct file *filep) if (private->pagetable != NULL) { #ifdef PER_PROCESS_PAGE_TABLE + kgsl_yamato_cleanup_pt(&kgsl_driver.yamato_device, + private->pagetable); kgsl_mmu_destroypagetableobject(private->pagetable); #endif private->pagetable = NULL; @@ -312,6 +314,12 @@ static int kgsl_open(struct inode *inodep, struct file *filep) result = -ENOMEM; goto done; } + result = kgsl_yamato_setup_pt(device, private->pagetable); + if (result) { + kgsl_mmu_destroypagetableobject(private->pagetable); + private->pagetable == NULL; + goto done; + } #else private->pagetable = kgsl_driver.yamato_device.mmu.hwpagetable; #endif @@ -593,14 +601,16 @@ static long kgsl_ioctl_drawctxt_destroy(struct kgsl_file_private *private, void kgsl_remove_mem_entry(struct kgsl_mem_entry *entry) { + kgsl_mmu_unmap(entry->memdesc.pagetable, + entry->memdesc.gpuaddr & KGSL_PAGEMASK, + entry->memdesc.size); if (KGSL_MEMFLAGS_VMALLOC_MEM & entry->memdesc.priv) { vfree((void *)entry->memdesc.physaddr); entry->priv->vmalloc_size -= entry->memdesc.size; - kgsl_mmu_unmap(entry->memdesc.pagetable, - entry->memdesc.gpuaddr, entry->memdesc.size); } else { KGSL_DRV_DBG("unlocked pmem fd %p\n", entry->pmem_file); - put_pmem_file(entry->pmem_file); + if (entry->pmem_file) + put_pmem_file(entry->pmem_file); } list_del(&entry->list); @@ -715,7 +725,7 @@ static int kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_file_private *private, result = kgsl_mmu_map(private->pagetable, (unsigned long)vmalloc_area, len, GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, - &entry->memdesc.gpuaddr); + &entry->memdesc.gpuaddr, KGSL_MEMFLAGS_ALIGN4K); if (result != 0) goto error_free_vmalloc; @@ -778,6 +788,7 @@ static int kgsl_ioctl_sharedmem_from_pmem(struct kgsl_file_private *private, result = -EINVAL; goto error; } + KGSL_MEM_INFO("pmem file %p start 0x%lx vstart 0x%lx len 0x%lx\n", pmem_file, start, vstart, len); KGSL_DRV_DBG("locked pmem file %p\n", pmem_file); @@ -791,21 +802,38 @@ static int kgsl_ioctl_sharedmem_from_pmem(struct kgsl_file_private *private, entry->pmem_file = pmem_file; entry->memdesc.pagetable = private->pagetable; - entry->memdesc.size = len; + + /* Any MMU mapped memory must have a length in multiple of PAGESIZE */ + entry->memdesc.size = ALIGN(param.len, PAGE_SIZE); + /*we shouldn't need to write here from kernel mode */ entry->memdesc.hostptr = NULL; - entry->memdesc.physaddr = start; - entry->memdesc.gpuaddr = start; + /* ensure that MMU mappings are at page boundary */ + entry->memdesc.physaddr = start + (param.offset & KGSL_PAGEMASK); + result = kgsl_mmu_map(private->pagetable, entry->memdesc.physaddr, + entry->memdesc.size, GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, + &entry->memdesc.gpuaddr, + KGSL_MEMFLAGS_ALIGN4K | KGSL_MEMFLAGS_CONPHYS); + if (result) + goto error_free_entry; + /* If the offset is not at 4K boundary then add the correct offset + * value to gpuaddr */ + entry->memdesc.gpuaddr += (param.offset & ~KGSL_PAGEMASK); param.gpuaddr = entry->memdesc.gpuaddr; if (copy_to_user(arg, ¶m, sizeof(param))) { result = -EFAULT; - goto error_free_entry; + goto error_unmap_entry; } list_add(&entry->list, &private->mem_list); - return 0; + return result; + +error_unmap_entry: + kgsl_mmu_unmap(entry->memdesc.pagetable, + entry->memdesc.gpuaddr & KGSL_PAGEMASK, + entry->memdesc.size); error_free_entry: kfree(entry); diff --git a/drivers/video/msm/gpu/kgsl/kgsl_device.h b/drivers/video/msm/gpu/kgsl/kgsl_device.h index 2b6288b4bc439..d761bdf032bd5 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_device.h +++ b/drivers/video/msm/gpu/kgsl/kgsl_device.h @@ -125,6 +125,10 @@ int __init kgsl_yamato_config(struct kgsl_devconfig *, void kgsl_register_dump(struct kgsl_device *device); +int kgsl_yamato_setup_pt(struct kgsl_device *device, + struct kgsl_pagetable *pagetable); +int kgsl_yamato_cleanup_pt(struct kgsl_device *device, + struct kgsl_pagetable *pagetable); #ifdef CONFIG_MSM_KGSL_MMU int kgsl_yamato_setpagetable(struct kgsl_device *device); int kgsl_yamato_tlbinvalidate(struct kgsl_device *device); diff --git a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c index 6176fa11aff14..b0689592daca0 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c @@ -1328,6 +1328,12 @@ create_gpustate_shadow(struct kgsl_device *device, */ if (kgsl_sharedmem_alloc(flags, CONTEXT_SIZE, &drawctxt->gpustate) != 0) return -ENOMEM; + if (kgsl_mmu_map(drawctxt->pagetable, drawctxt->gpustate.physaddr, + drawctxt->gpustate.size, + GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, + &drawctxt->gpustate.gpuaddr, + KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN8K)) + return -EINVAL; drawctxt->flags |= CTXT_FLAGS_STATE_SHADOW; @@ -1364,6 +1370,13 @@ create_gmem_shadow(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt, &drawctxt->context_gmem_shadow.gmemshadow) != 0) return -ENOMEM; + if (kgsl_mmu_map(drawctxt->pagetable, + drawctxt->context_gmem_shadow.gmemshadow.physaddr, + drawctxt->context_gmem_shadow.gmemshadow.size, + GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, + &drawctxt->context_gmem_shadow.gmemshadow.gpuaddr, + KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN8K)) + return -EINVAL; /* we've allocated the shadow, when swapped out, GMEM must be saved. */ drawctxt->flags |= CTXT_FLAGS_GMEM_SHADOW | CTXT_FLAGS_GMEM_SAVE; @@ -1512,14 +1525,21 @@ int kgsl_drawctxt_destroy(struct kgsl_device *device, unsigned int drawctxt_id) /* destroy state shadow, if allocated */ if (drawctxt->gpustate.gpuaddr != 0) { + kgsl_mmu_unmap(drawctxt->pagetable, + drawctxt->gpustate.gpuaddr, + drawctxt->gpustate.size); drawctxt->gpustate.gpuaddr = 0; } if (drawctxt->gpustate.physaddr != 0) kgsl_sharedmem_free(&drawctxt->gpustate); /* destroy gmem shadow, if allocated */ - if (drawctxt->context_gmem_shadow.gmemshadow.gpuaddr != 0) - drawctxt->context_gmem_shadow.gmemshadow.gpuaddr = 0; + if (drawctxt->context_gmem_shadow.gmemshadow.gpuaddr != 0) { + kgsl_mmu_unmap(drawctxt->pagetable, + drawctxt->context_gmem_shadow.gmemshadow.gpuaddr, + drawctxt->context_gmem_shadow.gmemshadow.size); + drawctxt->context_gmem_shadow.gmemshadow.gpuaddr = 0; + } if (drawctxt->context_gmem_shadow.gmemshadow.physaddr != 0) kgsl_sharedmem_free(&drawctxt->context_gmem_shadow. diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c index 7e2016eb84ed3..f8b17ac77c765 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c @@ -231,6 +231,7 @@ struct kgsl_pagetable *kgsl_mmu_createpagetableobject(struct kgsl_mmu *mmu) kgsl_sharedmem_set(&pagetable->base, 0, 0, pagetable->base.size); } + pagetable->base.gpuaddr = pagetable->base.physaddr; KGSL_MEM_VDBG("return %p\n", pagetable); @@ -373,20 +374,18 @@ int kgsl_mmu_init(struct kgsl_device *device) REG_MH_MMU_TRAN_ERROR, mmu->dummyspace.gpuaddr); -#ifndef PER_PROCESS_PAGE_TABLE - mmu->hwpagetable = kgsl_mmu_createpagetableobject(mmu); - if (!mmu->hwpagetable) { + mmu->defaultpagetable = kgsl_mmu_createpagetableobject(mmu); + if (!mmu->defaultpagetable) { KGSL_MEM_ERR("Failed to create global page table\n"); kgsl_mmu_close(device); return -ENOMEM; } + mmu->hwpagetable = mmu->defaultpagetable; status = kgsl_yamato_setpagetable(device); if (status) { kgsl_mmu_close(device); return status; } -#endif - kgsl_yamato_tlbinvalidate(device); mmu->flags |= KGSL_FLAGS_STARTED; } @@ -433,12 +432,15 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, unsigned int address, int range, unsigned int protflags, - unsigned int *gpuaddr) + unsigned int *gpuaddr, + unsigned int flags) { int numpages; unsigned int pte, superpte, ptefirst, ptelast, physaddr; - int flushtlb; + int flushtlb, alloc_size; struct kgsl_mmu *mmu = NULL; + int phys_contiguous = flags & KGSL_MEMFLAGS_CONPHYS; + unsigned int align = flags & KGSL_MEMFLAGS_ALIGN_MASK; KGSL_MEM_VDBG("enter (pt=%p, physaddr=%08x, range=%08d, gpuaddr=%p)\n", pagetable, address, range, gpuaddr); @@ -450,17 +452,41 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, BUG_ON(protflags == 0); BUG_ON(range <= 0); - *gpuaddr = gen_pool_alloc(pagetable->pool, range); + /* Only support 4K and 8K alignment for now */ + if (align != KGSL_MEMFLAGS_ALIGN8K && align != KGSL_MEMFLAGS_ALIGN4K) { + KGSL_MEM_ERR("Cannot map memory according to " + "requested flags: %08x\n", flags); + return -EINVAL; + } + + /* Make sure address being mapped is at 4K boundary */ + if (!IS_ALIGNED(address, KGSL_PAGESIZE) || range & ~KGSL_PAGEMASK) { + KGSL_MEM_ERR("Cannot map address not aligned " + "at page boundary: address: %08x, range: %08x\n", + address, range); + return -EINVAL; + } + alloc_size = range; + if (align == KGSL_MEMFLAGS_ALIGN8K) + alloc_size += KGSL_PAGESIZE; + + *gpuaddr = gen_pool_alloc(pagetable->pool, alloc_size); if (*gpuaddr == 0) { KGSL_MEM_ERR("gen_pool_alloc failed\n"); return -ENOMEM; } - + if (align == KGSL_MEMFLAGS_ALIGN8K) { + if (*gpuaddr & ((1 << 13) - 1)) { + /* Not 8k aligned, align it */ + gen_pool_free(pagetable->pool, *gpuaddr, KGSL_PAGESIZE); + *gpuaddr = *gpuaddr + KGSL_PAGESIZE; + } else + gen_pool_free(pagetable->pool, *gpuaddr + range, + KGSL_PAGESIZE); + } numpages = (range >> KGSL_PAGESIZE_SHIFT); - if (range & (KGSL_PAGESIZE - 1)) - numpages++; ptefirst = kgsl_pt_entry_get(pagetable, *gpuaddr); ptelast = ptefirst + numpages; @@ -468,7 +494,6 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, pte = ptefirst; flushtlb = 0; - superpte = ptefirst & (GSL_PT_SUPER_PTE - 1); for (pte = superpte; pte < ptefirst; pte++) { /* tlb needs to be flushed only when a dirty superPTE @@ -488,8 +513,12 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, if (kgsl_pt_map_isdirty(pagetable, pte)) flushtlb = 1; /* mark pte as in use */ - physaddr = vmalloc_to_pfn((void *)address); - physaddr <<= PAGE_SHIFT; + if (phys_contiguous) + physaddr = address; + else { + physaddr = vmalloc_to_pfn((void *)address); + physaddr <<= PAGE_SHIFT; + } if (physaddr) kgsl_pt_map_set(pagetable, pte, physaddr | protflags); @@ -596,9 +625,9 @@ int kgsl_mmu_close(struct kgsl_device *device) mmu->flags &= ~KGSL_FLAGS_INITIALIZED; mmu->flags &= ~KGSL_FLAGS_INITIALIZED0; #ifndef PER_PROCESS_PAGE_TABLE - kgsl_mmu_destroypagetableobject(mmu->hwpagetable); + kgsl_mmu_destroypagetableobject(mmu->defaultpagetable); #endif - mmu->hwpagetable = NULL; + mmu->defaultpagetable = NULL; } KGSL_MEM_VDBG("return %d\n", 0); diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.h b/drivers/video/msm/gpu/kgsl/kgsl_mmu.h index 40d390df5458b..e271fee8f3c33 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_mmu.h +++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.h @@ -76,6 +76,7 @@ struct kgsl_mmu { unsigned int va_range; struct kgsl_memdesc dummyspace; /* current page table object being used by device mmu */ + struct kgsl_pagetable *defaultpagetable; struct kgsl_pagetable *hwpagetable; }; @@ -103,7 +104,8 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, unsigned int address, int range, unsigned int protflags, - unsigned int *gpuaddr); + unsigned int *gpuaddr, + unsigned int flags); int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, unsigned int gpuaddr, int range); @@ -114,10 +116,15 @@ static inline int kgsl_mmu_map(struct kgsl_pagetable *pagetable, unsigned int address, int range, unsigned int protflags, - unsigned int *gpuaddr) { return -1; } + unsigned int *gpuaddr, + unsigned int flags) +{ + *gpuaddr = address; + return 0; +} static inline int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, - unsigned int gpuaddr, int range) { return -1; } + unsigned int gpuaddr, int range) { return 0; } static inline pte_t *kgsl_get_pte_from_vaddr(unsigned int vaddr) {return NULL;} #endif diff --git a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c index fef527da4c66f..79d90cbbc4ef6 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c @@ -521,7 +521,9 @@ int kgsl_ringbuffer_init(struct kgsl_device *device) } /* allocate memory for polling and timestamps */ - flags = (KGSL_MEMFLAGS_ALIGN32 | KGSL_MEMFLAGS_CONPHYS); + /* This really can be at 4 byte alignment boundry but for using MMU + * we need to make it at page boundary */ + flags = (KGSL_MEMFLAGS_ALIGNPAGE | KGSL_MEMFLAGS_CONPHYS); status = kgsl_sharedmem_alloc(flags, sizeof(struct kgsl_rbmemptrs), &rb->memptrs_desc); @@ -532,6 +534,15 @@ int kgsl_ringbuffer_init(struct kgsl_device *device) return status; } + /* last allocation of init process is made here so map all + * allocations to MMU */ + status = kgsl_yamato_setup_pt(device, device->mmu.defaultpagetable); + if (status != 0) { + kgsl_ringbuffer_close(rb); + KGSL_CMD_VDBG("return %d\n", status); + return status; + } + /* overlay structure on memptrs memory */ rb->memptrs = (struct kgsl_rbmemptrs *) rb->memptrs_desc.hostptr; @@ -556,6 +567,9 @@ int kgsl_ringbuffer_close(struct kgsl_ringbuffer *rb) kgsl_ringbuffer_stop(rb); + /* this must happen before first sharedmem_free */ + kgsl_yamato_cleanup_pt(rb->device, rb->device->mmu.defaultpagetable); + if (rb->buffer_desc.hostptr) kgsl_sharedmem_free(&rb->buffer_desc); diff --git a/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c index 7b6cf251919b2..c5a9a8a5ec8f9 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c @@ -207,7 +207,6 @@ kgsl_sharedmem_alloc(uint32_t flags, int size, memdesc->physaddr = alignedbaseaddr; memdesc->hostptr = kgsl_memarena_gethostptr(shmem, memdesc->physaddr); memdesc->size = size; - memdesc->gpuaddr = memdesc->physaddr; KGSL_MEM_VDBG("ashift %d m->ashift %d blksize %d base %x abase %x\n", alignshift, KGSL_PAGESIZE_SHIFT, blksize, baseaddr, diff --git a/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h index 2f7e3406e9f5a..eaf215613ac89 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h +++ b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h @@ -24,6 +24,7 @@ #define KGSL_PAGESIZE 0x1000 #define KGSL_PAGESIZE_SHIFT 12 +#define KGSL_PAGEMASK (~(KGSL_PAGESIZE - 1)) struct kgsl_pagetable; diff --git a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c index f5de394b8ebfd..a8c560b12f122 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c @@ -230,6 +230,80 @@ irqreturn_t kgsl_yamato_isr(int irq, void *data) return result; } +int kgsl_yamato_cleanup_pt(struct kgsl_device *device, + struct kgsl_pagetable *pagetable) +{ + kgsl_mmu_unmap(pagetable, device->ringbuffer.buffer_desc.gpuaddr, + device->ringbuffer.buffer_desc.size); + + kgsl_mmu_unmap(pagetable, device->ringbuffer.memptrs_desc.gpuaddr, + device->ringbuffer.memptrs_desc.size); + + kgsl_mmu_unmap(pagetable, device->memstore.gpuaddr, + device->memstore.size); + + return 0; +} + +int kgsl_yamato_setup_pt(struct kgsl_device *device, + struct kgsl_pagetable *pagetable) +{ + int result = 0; + unsigned int gpuaddr; + + BUG_ON(device->ringbuffer.buffer_desc.physaddr == 0); + BUG_ON(device->ringbuffer.memptrs_desc.physaddr == 0); + BUG_ON(device->memstore.physaddr == 0); + + result = kgsl_mmu_map(pagetable, + device->ringbuffer.buffer_desc.physaddr, + device->ringbuffer.buffer_desc.size, + GSL_PT_PAGE_RV, &gpuaddr, + KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN4K); + + if (result) + goto error; + + if (device->ringbuffer.buffer_desc.gpuaddr == 0) + device->ringbuffer.buffer_desc.gpuaddr = gpuaddr; + BUG_ON(device->ringbuffer.buffer_desc.gpuaddr != gpuaddr); + + result = kgsl_mmu_map(pagetable, + device->ringbuffer.memptrs_desc.physaddr, + device->ringbuffer.memptrs_desc.size, + GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, &gpuaddr, + KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN4K); + if (result) + goto unmap_buffer_desc; + + if (device->ringbuffer.memptrs_desc.gpuaddr == 0) + device->ringbuffer.memptrs_desc.gpuaddr = gpuaddr; + BUG_ON(device->ringbuffer.memptrs_desc.gpuaddr != gpuaddr); + + result = kgsl_mmu_map(pagetable, device->memstore.physaddr, + device->memstore.size, + GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, &gpuaddr, + KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN4K); + if (result) + goto unmap_memptrs_desc; + + if (device->memstore.gpuaddr == 0) + device->memstore.gpuaddr = gpuaddr; + BUG_ON(device->memstore.gpuaddr != gpuaddr); + + return result; + +unmap_memptrs_desc: + kgsl_mmu_unmap(pagetable, device->ringbuffer.memptrs_desc.gpuaddr, + device->ringbuffer.memptrs_desc.size); +unmap_buffer_desc: + kgsl_mmu_unmap(pagetable, device->ringbuffer.buffer_desc.gpuaddr, + device->ringbuffer.buffer_desc.size); +error: + return result; + +} + #ifdef CONFIG_MSM_KGSL_MMU int kgsl_yamato_tlbinvalidate(struct kgsl_device *device) { @@ -829,21 +903,21 @@ int __init kgsl_yamato_config(struct kgsl_devconfig *devconfig, /*note: for all of these behavior masks: * 0 = do not translate - * 1 = translate within va_range, otherwise use phyisical + * 1 = translate within va_range, otherwise use physical * 2 = translate within va_range, otherwise fault */ devconfig->mmu_config = 1 /* mmu enable */ - | (1 << MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT) - | (1 << MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT) - | (1 << MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT) - | (1 << MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT) - | (1 << MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT) - | (1 << MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT) - | (1 << MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT) - | (1 << MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT) - | (1 << MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT) - | (1 << MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT) - | (1 << MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT); + | (2 << MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT) + | (2 << MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT) + | (2 << MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT) + | (2 << MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT) + | (2 << MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT) + | (2 << MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT) + | (2 << MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT) + | (2 << MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT) + | (2 << MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT) + | (2 << MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT) + | (2 << MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT); /*TODO: these should probably be configurable from platform device * stuff */ diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index 5266329130972..a06507be81ffb 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -207,10 +207,12 @@ struct kgsl_drawctxt_destroy { #define IOCTL_KGSL_DRAWCTXT_DESTROY \ _IOW(KGSL_IOC_TYPE, 0x14, struct kgsl_drawctxt_destroy) -/* add a block of pmem into the GPU address space */ +/* add a block of pmem or fb into the GPU address space */ struct kgsl_sharedmem_from_pmem { int pmem_fd; unsigned int gpuaddr; /*output param */ + unsigned int len; + unsigned int offset; }; #define IOCTL_KGSL_SHAREDMEM_FROM_PMEM \ From 6bcaa2de2a8c344d7461634a2cd3ece1f12ca521 Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Wed, 2 Dec 2009 22:09:58 -0800 Subject: [PATCH 0397/2556] [ARM] msm: kgsl: Add barriers and flush the tlb when changing MMU mappings Change-Id: Ia681b27c3a4bc074a535b20ac07404d10fda094e Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl_mmu.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c index f8b17ac77c765..04325f2ee5f00 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c @@ -547,8 +547,10 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, pagetable, address, *gpuaddr, ptefirst, ptelast, numpages, flushtlb); + dmb(); + /* Invalidate tlb only if current page table used by GPU is the - * pagetable that we used to allocate */ + * pagetable that we used to allocate */ if (pagetable == mmu->hwpagetable) kgsl_yamato_tlbinvalidate(mmu->device); @@ -590,6 +592,13 @@ kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, unsigned int gpuaddr, kgsl_pt_map_set(pagetable, pte, GSL_PT_PAGE_DIRTY); } + dmb(); + + /* Invalidate tlb only if current page table used by GPU is the + * pagetable that we used to allocate */ + if (pagetable == pagetable->mmu->hwpagetable) + kgsl_yamato_tlbinvalidate(pagetable->mmu->device); + KGSL_MEM_VDBG("return %d\n", 0); return 0; From 471609ab777851024ed801425e25c872cf54d4a9 Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Wed, 2 Dec 2009 22:08:20 -0800 Subject: [PATCH 0398/2556] [ARM] msm: kgsl: flush the vmalloc range if we are mapping non-cached to user Change-Id: I38777fe2d6ffcf3173961558b77a9dc5b7f6ac5e Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c index cb7b8d74edfd1..71070bfe6372c 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.c +++ b/drivers/video/msm/gpu/kgsl/kgsl.c @@ -710,6 +710,10 @@ static int kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_file_private *private, goto error_free_entry; } if (!kgsl_cache_enable) { + /* If we are going to map non-cached, make sure to flush the + * cache to ensure that previously cached data does not + * overwrite this memory */ + dmac_flush_range(vmalloc_area, vmalloc_area + len); KGSL_MEM_INFO("Caching for memory allocation turned off\n"); vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); } else { From 34096c57624e1b377ff3338f25a375648c53ab12 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 3 Dec 2009 01:39:11 -0800 Subject: [PATCH 0399/2556] [ARM] msm: kgsl: Disable shadow writes The use of this freature currently has some known instabilities and does not actually provide a measurable performance improvement. Change-Id: I2ef785e4894334c000fb97365eb676f25bee65e6 Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c index b0689592daca0..c20a3e67376a9 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c @@ -27,7 +27,7 @@ #include "kgsl_log.h" #include "kgsl_pm4types.h" -/* #define DISABLE_SHADOW_WRITES */ +#define DISABLE_SHADOW_WRITES /* * * Memory Map for Register, Constant & Instruction Shadow, and Command Buffers From f818a43e701b5c0f8d03101b12d3208734ba685f Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 4 Dec 2009 20:26:50 -0800 Subject: [PATCH 0400/2556] [ARM] msm: kgsl: allow the framebuffer to be mapped into the mmu The from_pmem ioctl can now accept an fd for the framebuffer, and create an mmu mapping so that the GPU can render straight into the fb. Change-Id: I8845eb9ee3ce793349e71785311655c8b096e6eb Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl.c | 66 +++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c index 71070bfe6372c..40e5bef058ef8 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.c +++ b/drivers/video/msm/gpu/kgsl/kgsl.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,8 @@ struct kgsl_file_private { unsigned long vmalloc_size; }; +static void kgsl_put_phys_file(struct file *file); + #ifdef CONFIG_MSM_KGSL_MMU static long flush_l1_cache_range(unsigned long addr, int size) { @@ -607,11 +610,8 @@ void kgsl_remove_mem_entry(struct kgsl_mem_entry *entry) if (KGSL_MEMFLAGS_VMALLOC_MEM & entry->memdesc.priv) { vfree((void *)entry->memdesc.physaddr); entry->priv->vmalloc_size -= entry->memdesc.size; - } else { - KGSL_DRV_DBG("unlocked pmem fd %p\n", entry->pmem_file); - if (entry->pmem_file) - put_pmem_file(entry->pmem_file); - } + } else + kgsl_put_phys_file(entry->pmem_file); list_del(&entry->list); if (entry->free_list.prev) @@ -774,13 +774,51 @@ static inline int kgsl_ioctl_sharedmem_from_vmalloc( } #endif +static int kgsl_get_phys_file(int fd, unsigned long *start, unsigned long *len, + struct file **filep) +{ + struct file *fbfile; + int put_needed; + unsigned long vstart = 0; + int ret = 0; + dev_t rdev; + struct fb_info *info; + + *filep = NULL; + if (!get_pmem_file(fd, start, &vstart, len, filep)) + return 0; + + fbfile = fget_light(fd, &put_needed); + if (fbfile == NULL) + return -1; + + rdev = fbfile->f_dentry->d_inode->i_rdev; + info = MAJOR(rdev) == FB_MAJOR ? registered_fb[MINOR(rdev)] : NULL; + if (info) { + *start = info->fix.smem_start; + *len = info->fix.smem_len; + ret = 0; + } else + ret = -1; + fput_light(fbfile, put_needed); + + return ret; +} + +static void kgsl_put_phys_file(struct file *file) +{ + KGSL_DRV_DBG("put phys file %p\n", file); + if (file) + put_pmem_file(file); +} + static int kgsl_ioctl_sharedmem_from_pmem(struct kgsl_file_private *private, void __user *arg) { int result = 0; struct kgsl_sharedmem_from_pmem param; struct kgsl_mem_entry *entry = NULL; - unsigned long start = 0, vstart = 0, len = 0; + unsigned long start = 0, len = 0; struct file *pmem_file = NULL; if (copy_from_user(¶m, arg, sizeof(param))) { @@ -788,14 +826,19 @@ static int kgsl_ioctl_sharedmem_from_pmem(struct kgsl_file_private *private, goto error; } - if (get_pmem_file(param.pmem_fd, &start, &vstart, &len, &pmem_file)) { + if (kgsl_get_phys_file(param.pmem_fd, &start, &len, &pmem_file)) { result = -EINVAL; goto error; + } else if (param.offset + param.len >= len) { + KGSL_DRV_ERR("%s: region too large 0x%x + 0x%x >= 0x%lx\n", + __func__, param.offset, param.len, len); + result = -EINVAL; + goto error_put_pmem; } - KGSL_MEM_INFO("pmem file %p start 0x%lx vstart 0x%lx len 0x%lx\n", - pmem_file, start, vstart, len); - KGSL_DRV_DBG("locked pmem file %p\n", pmem_file); + KGSL_MEM_INFO("get phys file %p start 0x%lx len 0x%lx\n", + pmem_file, start, len); + KGSL_DRV_DBG("locked phys file %p\n", pmem_file); entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (entry == NULL) { @@ -842,8 +885,7 @@ static int kgsl_ioctl_sharedmem_from_pmem(struct kgsl_file_private *private, kfree(entry); error_put_pmem: - KGSL_DRV_DBG("unlocked pmem file %p\n", pmem_file); - put_pmem_file(pmem_file); + kgsl_put_phys_file(pmem_file); error: return result; From 702ac47bbfb6732e8075cf1cf657458d94a62101 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 14 Dec 2009 01:13:05 -0800 Subject: [PATCH 0401/2556] [ARM] msm: kgsl: Fix a typo when checking pmem region size Change-Id: Icab8179e15569c77762e24764380f8f97d0d43e2 Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c index 40e5bef058ef8..01931c4748d6b 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.c +++ b/drivers/video/msm/gpu/kgsl/kgsl.c @@ -829,7 +829,7 @@ static int kgsl_ioctl_sharedmem_from_pmem(struct kgsl_file_private *private, if (kgsl_get_phys_file(param.pmem_fd, &start, &len, &pmem_file)) { result = -EINVAL; goto error; - } else if (param.offset + param.len >= len) { + } else if (param.offset + param.len > len) { KGSL_DRV_ERR("%s: region too large 0x%x + 0x%x >= 0x%lx\n", __func__, param.offset, param.len, len); result = -EINVAL; From 606c3b23ddf7b3636debbb3199c707bec3228651 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 30 Mar 2009 18:35:36 -0700 Subject: [PATCH 0402/2556] input: Add driver for Qualcomm MSM touchscreen controller Change-Id: Iac8c49b109cff600f7aac54559fed4fd069aebd8 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/include/mach/msm_ts.h | 51 ++++ drivers/input/touchscreen/Kconfig | 9 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/msm_ts.c | 344 ++++++++++++++++++++++++ 4 files changed, 405 insertions(+) create mode 100644 arch/arm/mach-msm/include/mach/msm_ts.h create mode 100644 drivers/input/touchscreen/msm_ts.c diff --git a/arch/arm/mach-msm/include/mach/msm_ts.h b/arch/arm/mach-msm/include/mach/msm_ts.h new file mode 100644 index 0000000000000..cb41aed7f0705 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_ts.h @@ -0,0 +1,51 @@ +/* arch/arm/mach-msm/include/mach/msm_ts.h + * + * Internal platform definitions for msm/qsd touchscreen devices + * + * Copyright (C) 2008 Google Incorporated + * + * 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 __ASM_ARCH_MSM_TS_H +#define __ASM_ARCH_MSM_TS_H + +#include + +/* The dimensions for the virtual key are for the other axis, i.e. if + * virtual keys are in the Y dimension then min/max is the range in the X + * dimension where that key would be activated */ +struct ts_virt_key { + int key; + int min; + int max; +}; + +struct msm_ts_virtual_keys { + struct ts_virt_key *keys; + int num_keys; +}; + +struct msm_ts_platform_data { + uint32_t min_x; + uint32_t max_x; + uint32_t min_y; + uint32_t max_y; + uint32_t min_press; + uint32_t max_press; + struct msm_ts_virtual_keys *vkeys_x; + uint32_t virt_x_start; + struct msm_ts_virtual_keys *vkeys_y; + uint32_t virt_y_start; + uint32_t inv_x; + uint32_t inv_y; +}; + +#endif /* __ASM_ARCH_MSM_TS_H */ diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 90fa1813af955..3c9da231da1ed 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -355,6 +355,15 @@ config TOUCHSCREEN_QT602240 To compile this driver as a module, choose M here: the module will be called qt602240_ts. +config TOUCHSCREEN_MSM + bool "Qualcomm MSM touchscreen controller" + depends on ARCH_MSM + default n + help + Say Y here if you have a 4-wire resistive touchscreen panel + connected to the TSSC touchscreen controller on a + Qualcomm MSM/QSD based SoC. + config TOUCHSCREEN_MIGOR tristate "Renesas MIGO-R touchscreen" depends on SH_MIGOR && I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index d0da12eb237c6..be5078e463524 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o +obj-$(CONFIG_TOUCHSCREEN_MSM) += msm_ts.o obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o diff --git a/drivers/input/touchscreen/msm_ts.c b/drivers/input/touchscreen/msm_ts.c new file mode 100644 index 0000000000000..dff730ad86c33 --- /dev/null +++ b/drivers/input/touchscreen/msm_ts.c @@ -0,0 +1,344 @@ +/* drivers/input/touchscreen/msm_ts.c + * + * Copyright (C) 2008 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. + * + * TODO: + * - Add a timer to simulate a pen_up in case there's a timeout. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define TSSC_CTL 0x100 +#define TSSC_CTL_PENUP_IRQ (1 << 12) +#define TSSC_CTL_DATA_FLAG (1 << 11) +#define TSSC_CTL_DEBOUNCE_EN (1 << 6) +#define TSSC_CTL_EN_AVERAGE (1 << 5) +#define TSSC_CTL_MODE_MASTER (3 << 3) +#define TSSC_CTL_ENABLE (1 << 0) +#define TSSC_OPN 0x104 +#define TSSC_OPN_NOOP 0x00 +#define TSSC_OPN_4WIRE_X 0x01 +#define TSSC_OPN_4WIRE_Y 0x02 +#define TSSC_OPN_4WIRE_Z1 0x03 +#define TSSC_OPN_4WIRE_Z2 0x04 +#define TSSC_SAMPLING_INT 0x108 +#define TSSC_STATUS 0x10c +#define TSSC_AVG_12 0x110 +#define TSSC_AVG_34 0x114 +#define TSSC_SAMPLE(op,samp) ((0x118 + ((op & 0x3) * 0x20)) + \ + ((samp & 0x7) * 0x4)) +#define TSSC_TEST_1 0x198 +#define TSSC_TEST_2 0x19c + +struct msm_ts { + struct msm_ts_platform_data *pdata; + struct input_dev *input_dev; + void __iomem *tssc_base; + uint32_t ts_down:1; + struct ts_virt_key *vkey_down; +}; + +static uint32_t msm_tsdebug; +module_param_named(tsdebug, msm_tsdebug, uint, 0664); + +#define tssc_readl(t, a) (readl(((t)->tssc_base) + (a))) +#define tssc_writel(t, v, a) do {writel(v, ((t)->tssc_base) + (a));} while(0) + +static void setup_next_sample(struct msm_ts *ts) +{ + uint32_t tmp; + + /* 1.2ms debounce time */ + tmp = ((2 << 7) | TSSC_CTL_DEBOUNCE_EN | TSSC_CTL_EN_AVERAGE | + TSSC_CTL_MODE_MASTER | TSSC_CTL_ENABLE); + tssc_writel(ts, tmp, TSSC_CTL); +} + +static struct ts_virt_key *find_virt_key(struct msm_ts *ts, + struct msm_ts_virtual_keys *vkeys, + uint32_t val) +{ + int i; + + if (!vkeys) + return NULL; + + for (i = 0; i < vkeys->num_keys; ++i) + if ((val >= vkeys->keys[i].min) && (val <= vkeys->keys[i].max)) + return &vkeys->keys[i]; + return NULL; +} + + +static irqreturn_t msm_ts_irq(int irq, void *dev_id) +{ + struct msm_ts *ts = dev_id; + struct msm_ts_platform_data *pdata = ts->pdata; + + uint32_t tssc_avg12, tssc_avg34, tssc_status, tssc_ctl; + int x, y, z1, z2; + int was_down; + int down; + + tssc_ctl = tssc_readl(ts, TSSC_CTL); + tssc_status = tssc_readl(ts, TSSC_STATUS); + tssc_avg12 = tssc_readl(ts, TSSC_AVG_12); + tssc_avg34 = tssc_readl(ts, TSSC_AVG_34); + + setup_next_sample(ts); + + x = tssc_avg12 & 0xffff; + y = tssc_avg12 >> 16; + z1 = tssc_avg34 & 0xffff; + z2 = tssc_avg34 >> 16; + + /* invert the inputs if necessary */ + if (pdata->inv_x) x = pdata->inv_x - x; + if (pdata->inv_y) y = pdata->inv_y - y; + if (x < 0) x = 0; + if (y < 0) y = 0; + + down = !(tssc_ctl & TSSC_CTL_PENUP_IRQ); + was_down = ts->ts_down; + ts->ts_down = down; + + /* no valid data */ + if (down && !(tssc_ctl & TSSC_CTL_DATA_FLAG)) + return IRQ_HANDLED; + + if (msm_tsdebug & 2) + printk("%s: down=%d, x=%d, y=%d, z1=%d, z2=%d, status %x\n", + __func__, down, x, y, z1, z2, tssc_status); + + if (!was_down && down) { + struct ts_virt_key *vkey = NULL; + + if (pdata->vkeys_y && (y > pdata->virt_y_start)) + vkey = find_virt_key(ts, pdata->vkeys_y, x); + if (!vkey && ts->pdata->vkeys_x && (x > pdata->virt_x_start)) + vkey = find_virt_key(ts, pdata->vkeys_x, y); + + if (vkey) { + WARN_ON(ts->vkey_down != NULL); + if(msm_tsdebug) + printk("%s: virtual key down %d\n", __func__, + vkey->key); + ts->vkey_down = vkey; + input_report_key(ts->input_dev, vkey->key, 1); + input_sync(ts->input_dev); + return IRQ_HANDLED; + } + } else if (ts->vkey_down != NULL) { + if (!down) { + if(msm_tsdebug) + printk("%s: virtual key up %d\n", __func__, + ts->vkey_down->key); + input_report_key(ts->input_dev, ts->vkey_down->key, 0); + input_sync(ts->input_dev); + ts->vkey_down = NULL; + } + return IRQ_HANDLED; + } + + if (down) { + input_report_abs(ts->input_dev, ABS_X, x); + input_report_abs(ts->input_dev, ABS_Y, y); + input_report_abs(ts->input_dev, ABS_PRESSURE, z1); + } + input_report_key(ts->input_dev, BTN_TOUCH, down); + input_sync(ts->input_dev); + + return IRQ_HANDLED; +} + +static void dump_tssc_regs(struct msm_ts *ts) +{ +#define __dump_tssc_reg(r) \ + do { printk(#r " %x\n", tssc_readl(ts, (r))); } while(0) + + __dump_tssc_reg(TSSC_CTL); + __dump_tssc_reg(TSSC_OPN); + __dump_tssc_reg(TSSC_SAMPLING_INT); + __dump_tssc_reg(TSSC_STATUS); + __dump_tssc_reg(TSSC_AVG_12); + __dump_tssc_reg(TSSC_AVG_34); +#undef __dump_tssc_reg +} + +static int __devinit msm_ts_hw_init(struct msm_ts *ts) +{ + uint32_t tmp; + + /* Enable the register clock to tssc so we can configure it. */ + tssc_writel(ts, TSSC_CTL_ENABLE, TSSC_CTL); + + /* op1 - measure X, 1 sample, 12bit resolution */ + tmp = (TSSC_OPN_4WIRE_X << 16) | (2 << 8) | (2 << 0); + /* op2 - measure Y, 1 sample, 12bit resolution */ + tmp |= (TSSC_OPN_4WIRE_Y << 20) | (2 << 10) | (2 << 2); + /* op3 - measure Z1, 1 sample, 8bit resolution */ + tmp |= (TSSC_OPN_4WIRE_Z1 << 24) | (2 << 12) | (0 << 4); + + /* XXX: we don't actually need to measure Z2 (thus 0 samples) when + * doing voltage-driven measurement */ + /* op4 - measure Z2, 0 samples, 8bit resolution */ + tmp |= (TSSC_OPN_4WIRE_Z2 << 28) | (0 << 14) | (0 << 6); + tssc_writel(ts, tmp, TSSC_OPN); + + /* 16ms sampling interval */ + tssc_writel(ts, 16, TSSC_SAMPLING_INT); + + setup_next_sample(ts); + + return 0; +} + +static int __devinit msm_ts_probe(struct platform_device *pdev) +{ + struct msm_ts_platform_data *pdata = pdev->dev.platform_data; + struct msm_ts *ts; + struct resource *tssc_res; + struct resource *irq1_res; + struct resource *irq2_res; + int err = 0; + int i; + + tssc_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tssc"); + irq1_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "tssc1"); + irq2_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "tssc2"); + + if (!tssc_res || !irq1_res || !irq2_res) { + pr_err("%s: required resources not defined\n", __func__); + return -ENODEV; + } + + if (pdata == NULL) { + pr_err("%s: missing platform_data\n", __func__); + return -ENODEV; + } + + ts = kzalloc(sizeof(struct msm_ts), GFP_KERNEL); + if (ts == NULL) { + pr_err("%s: No memory for struct msm_ts\n", __func__); + return -ENOMEM; + } + ts->pdata = pdata; + + ts->tssc_base = ioremap(tssc_res->start, resource_size(tssc_res)); + if (ts->tssc_base == NULL) { + pr_err("%s: Can't ioremap region (0x%08x - 0x%08x)\n", __func__, + (uint32_t)tssc_res->start, (uint32_t)tssc_res->end); + err = -ENOMEM; + goto err_ioremap_tssc; + } + + ts->input_dev = input_allocate_device(); + if (ts->input_dev == NULL) { + pr_err("failed to allocate touchscreen input device\n"); + err = -ENOMEM; + goto err_alloc_input_dev; + } + ts->input_dev->name = "msm-touchscreen"; + input_set_drvdata(ts->input_dev, ts); + + input_set_capability(ts->input_dev, EV_KEY, BTN_TOUCH); + set_bit(EV_ABS, ts->input_dev->evbit); + + input_set_abs_params(ts->input_dev, ABS_X, pdata->min_x, pdata->max_x, + 0, 0); + input_set_abs_params(ts->input_dev, ABS_Y, pdata->min_y, pdata->max_y, + 0, 0); + input_set_abs_params(ts->input_dev, ABS_PRESSURE, pdata->min_press, + pdata->max_press, 0, 0); + + for (i = 0; pdata->vkeys_x && (i < pdata->vkeys_x->num_keys); ++i) + input_set_capability(ts->input_dev, EV_KEY, + pdata->vkeys_x->keys[i].key); + for (i = 0; pdata->vkeys_y && (i < pdata->vkeys_y->num_keys); ++i) + input_set_capability(ts->input_dev, EV_KEY, + pdata->vkeys_y->keys[i].key); + + err = input_register_device(ts->input_dev); + if (err != 0) { + pr_err("%s: failed to register input device\n", __func__); + goto err_input_dev_reg; + } + + msm_ts_hw_init(ts); + + err = request_irq(irq1_res->start, msm_ts_irq, + (irq1_res->flags & ~IORESOURCE_IRQ) | IRQF_DISABLED, + "msm_touchscreen", ts); + if (err != 0) { + pr_err("%s: Cannot register irq1 (%d)\n", __func__, err); + goto err_request_irq1; + } + + err = request_irq(irq2_res->start, msm_ts_irq, + (irq2_res->flags & ~IORESOURCE_IRQ) | IRQF_DISABLED, + "msm_touchscreen", ts); + if (err != 0) { + pr_err("%s: Cannot register irq2 (%d)\n", __func__, err); + goto err_request_irq2; + } + + platform_set_drvdata(pdev, ts); + + pr_info("%s: tssc_base=%p irq1=%d irq2=%d\n", __func__, + ts->tssc_base, (int)irq1_res->start, (int)irq2_res->start); + return 0; + +err_request_irq2: + free_irq(irq1_res->start, ts); + +err_request_irq1: + /* disable the tssc */ + tssc_writel(ts, TSSC_CTL_ENABLE, TSSC_CTL); + +err_input_dev_reg: + input_set_drvdata(ts->input_dev, NULL); + input_free_device(ts->input_dev); + +err_alloc_input_dev: + iounmap(ts->tssc_base); + +err_ioremap_tssc: + kfree(ts); + return err; +} + +static struct platform_driver msm_touchscreen_driver = { + .driver = { + .name = "msm_touchscreen", + .owner = THIS_MODULE, + }, + .probe = msm_ts_probe, +}; + +static int __init msm_ts_init(void) +{ + return platform_driver_register(&msm_touchscreen_driver); +} +device_initcall(msm_ts_init); + +MODULE_DESCRIPTION("Qualcomm MSM/QSD Touchscreen controller driver"); +MODULE_LICENSE("GPL"); From 2df2f09657f840cb3ffe76e5eed28953daa8adfa Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 30 Mar 2009 18:36:59 -0700 Subject: [PATCH 0403/2556] [ARM] msm: Add touchscreen device definition Signed-off-by: Dima Zavin --- arch/arm/mach-msm/devices.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h index 8ee78f08b1224..275a926d3ba25 100644 --- a/arch/arm/mach-msm/devices.h +++ b/arch/arm/mach-msm/devices.h @@ -46,6 +46,7 @@ extern struct platform_device msm_device_mdp; extern struct platform_device msm_device_mddi0; extern struct platform_device msm_device_mddi1; extern struct platform_device msm_device_mdp; +extern struct platform_device msm_device_touchscreen; extern struct clk msm_clocks_7x01a[]; extern unsigned msm_num_clocks_7x01a; From 56c77adda1dda059bd2532a44689548d90b8f079 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Wed, 18 Mar 2009 12:41:05 -0700 Subject: [PATCH 0404/2556] smc91x: ARCH_MSM configuration --- drivers/net/smc91x.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h index ee747919a766e..0de1589ffb5b1 100644 --- a/drivers/net/smc91x.h +++ b/drivers/net/smc91x.h @@ -285,6 +285,20 @@ static inline void LPD7_SMC_outsw (unsigned char* a, int r, #define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l) #define SMC_IRQ_FLAGS (-1) /* from resource */ +#elif defined(CONFIG_ARCH_MSM) + +#define SMC_CAN_USE_8BIT 0 +#define SMC_CAN_USE_16BIT 1 +#define SMC_CAN_USE_32BIT 0 +#define SMC_NOWAIT 1 + +#define SMC_inw(a, r) readw((a) + (r)) +#define SMC_outw(v, a, r) writew(v, (a) + (r)) +#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l) +#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l) + +#define SMC_IRQ_FLAGS IRQF_TRIGGER_HIGH + #elif defined(CONFIG_MN10300) /* From 95074b6164565cde3eb65209e6b080278e735e97 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Thu, 29 Jan 2009 00:28:29 -0800 Subject: [PATCH 0405/2556] [ARM] msm: swordfish: Add support for qsd8x50 surf board Also provides a swordfish_defconfig Change-Id: I478a15656d2ce3966ae980edd6a87c13464265ac Signed-off-by: Brian Swetland Signed-off-by: Dima Zavin --- arch/arm/configs/swordfish_defconfig | 167 ++++++++++ arch/arm/mach-msm/Makefile | 4 + arch/arm/mach-msm/board-swordfish-keypad.c | 177 +++++++++++ arch/arm/mach-msm/board-swordfish-mmc.c | 263 ++++++++++++++++ arch/arm/mach-msm/board-swordfish-panel.c | 116 +++++++ arch/arm/mach-msm/board-swordfish.c | 337 +++++++++++++++++++++ arch/arm/mach-msm/board-swordfish.h | 48 +++ 7 files changed, 1112 insertions(+) create mode 100644 arch/arm/configs/swordfish_defconfig create mode 100644 arch/arm/mach-msm/board-swordfish-keypad.c create mode 100644 arch/arm/mach-msm/board-swordfish-mmc.c create mode 100644 arch/arm/mach-msm/board-swordfish-panel.c create mode 100644 arch/arm/mach-msm/board-swordfish.c create mode 100644 arch/arm/mach-msm/board-swordfish.h diff --git a/arch/arm/configs/swordfish_defconfig b/arch/arm/configs/swordfish_defconfig new file mode 100644 index 0000000000000..38fc1f1d351dd --- /dev/null +++ b/arch/arm/configs/swordfish_defconfig @@ -0,0 +1,167 @@ +CONFIG_EXPERIMENTAL=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EMBEDDED=y +# CONFIG_SYSCTL_SYSCALL is not set +# CONFIG_ELF_CORE is not set +CONFIG_ASHMEM=y +CONFIG_SLAB=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_ARCH_MSM=y +CONFIG_ARCH_QSD8X50=y +CONFIG_MSM_DEBUG_UART3=y +# CONFIG_HTC_PWRSINK is not set +CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT=y +CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT=y +# CONFIG_MSM_IDLE_STATS is not set +# CONFIG_MSM_FIQ_SUPPORT is not set +# CONFIG_MSM_HW3D is not set +# CONFIG_MSM_ADSP is not set +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +CONFIG_PM=y +CONFIG_WAKELOCK=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +# CONFIG_IPV6 is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_LL=y +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_MTD=y +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_KERNEL_DEBUGGER_CORE=y +CONFIG_UID_STAT=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +CONFIG_NET_ETHERNET=y +CONFIG_SMC91X=y +CONFIG_PPP=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +# CONFIG_INPUT_MOUSEDEV is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_MSM=y +CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_SERIO is not set +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_HW_RANDOM is not set +CONFIG_I2C=y +CONFIG_POWER_SUPPLY=y +# CONFIG_HWMON is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +CONFIG_GPU_MSM_KGSL=y +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_MSM7X00A=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_SLEEP=y +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +# CONFIG_RTC_DRV_MSM7X00A is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +# CONFIG_DNOTIFY is not set +CONFIG_INOTIFY=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_YAFFS_FS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_KERNEL=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_SPINLOCK_SLEEP=y +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_VM=y +CONFIG_DEBUG_SG=y +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +CONFIG_DEBUG_LL=y +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_TWOFISH=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 644aa4443d384..faaa7c4234c16 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -56,6 +56,10 @@ obj-$(CONFIG_MACH_SAPPHIRE) += devices-msm7x00.o devices_htc.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-panel.o fish_battery.o +obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish.o +obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-keypad.o fish_battery.o +obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-panel.o +obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-mmc.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o obj-$(CONFIG_ARCH_QSD8X50) += board-qsd8x50.o devices-qsd8x50.o obj-$(CONFIG_ARCH_MSM8X60) += board-msm8x60.o diff --git a/arch/arm/mach-msm/board-swordfish-keypad.c b/arch/arm/mach-msm/board-swordfish-keypad.c new file mode 100644 index 0000000000000..f2c2f3962f651 --- /dev/null +++ b/arch/arm/mach-msm/board-swordfish-keypad.c @@ -0,0 +1,177 @@ +/* linux/arch/arm/mach-msm/board-swordfish-keypad.c + * + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland + * + * 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 + +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "board_swordfish." +static int swordfish_ffa; +module_param_named(ffa, swordfish_ffa, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define SCAN_FUNCTION_KEYS 0 /* don't turn this on without updating the ffa support */ + +static unsigned int swordfish_row_gpios[] = { + 31, 32, 33, 34, 35, 41 +#if SCAN_FUNCTION_KEYS + , 42 +#endif +}; + +static unsigned int swordfish_col_gpios[] = { 36, 37, 38, 39, 40 }; + +/* FFA: + 36: KEYSENSE_N(0) + 37: KEYSENSE_N(1) + 38: KEYSENSE_N(2) + 39: KEYSENSE_N(3) + 40: KEYSENSE_N(4) + + 31: KYPD_17 + 32: KYPD_15 + 33: KYPD_13 + 34: KYPD_11 + 35: KYPD_9 + 41: KYPD_MEMO +*/ + +#define KEYMAP_INDEX(row, col) ((row)*ARRAY_SIZE(swordfish_col_gpios) + (col)) + +static const unsigned short swordfish_keymap[ARRAY_SIZE(swordfish_col_gpios) * ARRAY_SIZE(swordfish_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_5, + [KEYMAP_INDEX(0, 1)] = KEY_9, + [KEYMAP_INDEX(0, 2)] = 229, /* SOFT1 */ + [KEYMAP_INDEX(0, 3)] = KEY_6, + [KEYMAP_INDEX(0, 4)] = KEY_LEFT, + + [KEYMAP_INDEX(1, 0)] = KEY_0, + [KEYMAP_INDEX(1, 1)] = KEY_RIGHT, + [KEYMAP_INDEX(1, 2)] = KEY_1, + [KEYMAP_INDEX(1, 3)] = 228, /* KEY_SHARP */ + [KEYMAP_INDEX(1, 4)] = KEY_SEND, + + [KEYMAP_INDEX(2, 0)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(2, 1)] = KEY_HOME, /* FA */ + [KEYMAP_INDEX(2, 2)] = KEY_F8, /* QCHT */ + [KEYMAP_INDEX(2, 3)] = KEY_F6, /* R+ */ + [KEYMAP_INDEX(2, 4)] = KEY_F7, /* R- */ + + [KEYMAP_INDEX(3, 0)] = KEY_UP, + [KEYMAP_INDEX(3, 1)] = KEY_CLEAR, + [KEYMAP_INDEX(3, 2)] = KEY_4, + [KEYMAP_INDEX(3, 3)] = KEY_MUTE, /* SPKR */ + [KEYMAP_INDEX(3, 4)] = KEY_2, + + [KEYMAP_INDEX(4, 0)] = 230, /* SOFT2 */ + [KEYMAP_INDEX(4, 1)] = 232, /* KEY_CENTER */ + [KEYMAP_INDEX(4, 2)] = KEY_DOWN, + [KEYMAP_INDEX(4, 3)] = KEY_BACK, /* FB */ + [KEYMAP_INDEX(4, 4)] = KEY_8, + + [KEYMAP_INDEX(5, 0)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */ + [KEYMAP_INDEX(5, 2)] = KEY_MAIL, /* MESG */ + [KEYMAP_INDEX(5, 3)] = KEY_3, + [KEYMAP_INDEX(5, 4)] = KEY_7, + +#if SCAN_FUNCTION_KEYS + [KEYMAP_INDEX(6, 0)] = KEY_F5, + [KEYMAP_INDEX(6, 1)] = KEY_F4, + [KEYMAP_INDEX(6, 2)] = KEY_F3, + [KEYMAP_INDEX(6, 3)] = KEY_F2, + [KEYMAP_INDEX(6, 4)] = KEY_F1 +#endif +}; + +static const unsigned short swordfish_keymap_ffa[ARRAY_SIZE(swordfish_col_gpios) * ARRAY_SIZE(swordfish_row_gpios)] = { + /*[KEYMAP_INDEX(0, 0)] = ,*/ + /*[KEYMAP_INDEX(0, 1)] = ,*/ + [KEYMAP_INDEX(0, 2)] = KEY_1, + [KEYMAP_INDEX(0, 3)] = KEY_SEND, + [KEYMAP_INDEX(0, 4)] = KEY_LEFT, + + [KEYMAP_INDEX(1, 0)] = KEY_3, + [KEYMAP_INDEX(1, 1)] = KEY_RIGHT, + [KEYMAP_INDEX(1, 2)] = KEY_VOLUMEUP, + /*[KEYMAP_INDEX(1, 3)] = ,*/ + [KEYMAP_INDEX(1, 4)] = KEY_6, + + [KEYMAP_INDEX(2, 0)] = KEY_HOME, /* A */ + [KEYMAP_INDEX(2, 1)] = KEY_BACK, /* B */ + [KEYMAP_INDEX(2, 2)] = KEY_0, + [KEYMAP_INDEX(2, 3)] = 228, /* KEY_SHARP */ + [KEYMAP_INDEX(2, 4)] = KEY_9, + + [KEYMAP_INDEX(3, 0)] = KEY_UP, + [KEYMAP_INDEX(3, 1)] = 232, /* KEY_CENTER */ /* i */ + [KEYMAP_INDEX(3, 2)] = KEY_4, + /*[KEYMAP_INDEX(3, 3)] = ,*/ + [KEYMAP_INDEX(3, 4)] = KEY_2, + + [KEYMAP_INDEX(4, 0)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(4, 1)] = KEY_SOUND, + [KEYMAP_INDEX(4, 2)] = KEY_DOWN, + [KEYMAP_INDEX(4, 3)] = KEY_8, + [KEYMAP_INDEX(4, 4)] = KEY_5, + + /*[KEYMAP_INDEX(5, 0)] = ,*/ + [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */ + [KEYMAP_INDEX(5, 2)] = 230, /*SOFT2*/ /* 2 */ + [KEYMAP_INDEX(5, 3)] = KEY_MENU, /* 1 */ + [KEYMAP_INDEX(5, 4)] = KEY_7, +}; + +static struct gpio_event_matrix_info swordfish_matrix_info = { + .info.func = gpio_event_matrix_func, + .keymap = swordfish_keymap, + .output_gpios = swordfish_row_gpios, + .input_gpios = swordfish_col_gpios, + .noutputs = ARRAY_SIZE(swordfish_row_gpios), + .ninputs = ARRAY_SIZE(swordfish_col_gpios), + .settle_time.tv.nsec = 0, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE | GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/ +}; + +struct gpio_event_info *swordfish_keypad_info[] = { + &swordfish_matrix_info.info +}; + +static struct gpio_event_platform_data swordfish_keypad_data = { + .name = "swordfish_keypad", + .info = swordfish_keypad_info, + .info_count = ARRAY_SIZE(swordfish_keypad_info) +}; + +static struct platform_device swordfish_keypad_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &swordfish_keypad_data, + }, +}; + +static int __init swordfish_init_keypad(void) +{ + if (!machine_is_swordfish()) + return 0; + if (swordfish_ffa) + swordfish_matrix_info.keymap = swordfish_keymap_ffa; + return platform_device_register(&swordfish_keypad_device); +} + +device_initcall(swordfish_init_keypad); diff --git a/arch/arm/mach-msm/board-swordfish-mmc.c b/arch/arm/mach-msm/board-swordfish-mmc.c new file mode 100644 index 0000000000000..bb0b1734b6072 --- /dev/null +++ b/arch/arm/mach-msm/board-swordfish-mmc.c @@ -0,0 +1,263 @@ +/* linux/arch/arm/mach-msm/board-swordfish-mmc.c + * + * Copyright (C) 2008 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 +#include +#include +#include + +#include +#include +#include + +#include + +#include "proc_comm.h" +#include "devices.h" + +#define FPGA_BASE 0x70000000 +#define FPGA_SDIO_STATUS 0x280 + +static void __iomem *fpga_base; + +#define DEBUG_SWORDFISH_MMC 1 + +extern int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, + unsigned int stat_irq, unsigned long stat_irq_flags); + +static int config_gpio_table(unsigned *table, int len, int enable) +{ + int n; + int rc = 0; + + for (n = 0; n < len; n++) { + unsigned dis = !enable; + unsigned id = table[n]; + + if (msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, &dis)) { + pr_err("%s: id=0x%08x dis=%d\n", __func__, table[n], + dis); + rc = -1; + } + } + + return rc; +} + +static unsigned sdc1_gpio_table[] = { + PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), +}; + +static unsigned sdc2_gpio_table[] = { + PCOM_GPIO_CFG(62, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), + PCOM_GPIO_CFG(63, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(64, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(65, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(66, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(67, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), +}; + +static unsigned sdc3_gpio_table[] = { + PCOM_GPIO_CFG(88, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), + PCOM_GPIO_CFG(89, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(90, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(91, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(92, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(93, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), +}; + +static unsigned sdc4_gpio_table[] = { + PCOM_GPIO_CFG(142, 3, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), + PCOM_GPIO_CFG(143, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(144, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(145, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(146, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(147, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), +}; + +struct sdc_info { + unsigned *table; + unsigned len; +}; + +static struct sdc_info sdcc_gpio_tables[] = { + [0] = { + .table = sdc1_gpio_table, + .len = ARRAY_SIZE(sdc1_gpio_table), + }, + [1] = { + .table = sdc2_gpio_table, + .len = ARRAY_SIZE(sdc2_gpio_table), + }, + [2] = { + .table = sdc3_gpio_table, + .len = ARRAY_SIZE(sdc3_gpio_table), + }, + [3] = { + .table = sdc4_gpio_table, + .len = ARRAY_SIZE(sdc4_gpio_table), + }, +}; + +static int swordfish_sdcc_setup_gpio(int dev_id, unsigned enable) +{ + struct sdc_info *info; + + if (dev_id < 1 || dev_id > 4) + return -1; + + info = &sdcc_gpio_tables[dev_id - 1]; + return config_gpio_table(info->table, info->len, enable); +} + +struct mmc_vdd_xlat { + int mask; + int level; +}; + +static struct mmc_vdd_xlat mmc_vdd_table[] = { + { MMC_VDD_165_195, 1800 }, + { MMC_VDD_20_21, 2050 }, + { MMC_VDD_21_22, 2150 }, + { MMC_VDD_22_23, 2250 }, + { MMC_VDD_23_24, 2350 }, + { MMC_VDD_24_25, 2450 }, + { MMC_VDD_25_26, 2550 }, + { MMC_VDD_26_27, 2650 }, + { MMC_VDD_27_28, 2750 }, + { MMC_VDD_28_29, 2850 }, + { MMC_VDD_29_30, 2950 }, +}; + +static struct vreg *vreg_sdcc; +static unsigned int vreg_sdcc_enabled; +static unsigned int sdcc_vdd = 0xffffffff; + +static uint32_t sdcc_translate_vdd(struct device *dev, unsigned int vdd) +{ + int i; + int rc = 0; + struct platform_device *pdev; + + pdev = container_of(dev, struct platform_device, dev); + BUG_ON(!vreg_sdcc); + + if (vdd == sdcc_vdd) + return 0; + + sdcc_vdd = vdd; + + /* enable/disable the signals to the slot */ + swordfish_sdcc_setup_gpio(pdev->id, !!vdd); + + /* power down */ + if (vdd == 0) { +#if DEBUG_SWORDFISH_MMC + pr_info("%s: disable sdcc power\n", __func__); +#endif + vreg_disable(vreg_sdcc); + vreg_sdcc_enabled = 0; + return 0; + } + + if (!vreg_sdcc_enabled) { + rc = vreg_enable(vreg_sdcc); + if (rc) + pr_err("%s: Error enabling vreg (%d)\n", __func__, rc); + vreg_sdcc_enabled = 1; + } + + for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) { + if (mmc_vdd_table[i].mask != (1 << vdd)) + continue; +#if DEBUG_SWORDFISH_MMC + pr_info("%s: Setting level to %u\n", __func__, + mmc_vdd_table[i].level); +#endif + rc = vreg_set_level(vreg_sdcc, mmc_vdd_table[i].level); + if (rc) + pr_err("%s: Error setting vreg level (%d)\n", __func__, rc); + return 0; + } + + pr_err("%s: Invalid VDD %d specified\n", __func__, vdd); + return 0; +} + +static unsigned int swordfish_sdcc_slot_status (struct device *dev) +{ + struct platform_device *pdev; + uint32_t sdcc_stat; + + pdev = container_of(dev, struct platform_device, dev); + + sdcc_stat = readl(fpga_base + FPGA_SDIO_STATUS); + + /* bit 0 - sdcc1 crd_det + * bit 1 - sdcc1 wr_prt + * bit 2 - sdcc2 crd_det + * bit 3 - sdcc2 wr_prt + * etc... + */ + + /* crd_det is active low */ + return !(sdcc_stat & (1 << ((pdev->id - 1) << 1))); +} + +#define SWORDFISH_MMC_VDD (MMC_VDD_165_195 | MMC_VDD_20_21 | MMC_VDD_21_22 \ + | MMC_VDD_22_23 | MMC_VDD_23_24 | MMC_VDD_24_25 \ + | MMC_VDD_25_26 | MMC_VDD_26_27 | MMC_VDD_27_28 \ + | MMC_VDD_28_29 | MMC_VDD_29_30) + +static struct mmc_platform_data swordfish_sdcc_data = { + .ocr_mask = SWORDFISH_MMC_VDD/*MMC_VDD_27_28 | MMC_VDD_28_29*/, + .status = swordfish_sdcc_slot_status, + .translate_vdd = sdcc_translate_vdd, +}; + +int __init swordfish_init_mmc(void) +{ + vreg_sdcc_enabled = 0; + vreg_sdcc = vreg_get(NULL, "gp5"); + if (IS_ERR(vreg_sdcc)) { + pr_err("%s: vreg get failed (%ld)\n", + __func__, PTR_ERR(vreg_sdcc)); + return PTR_ERR(vreg_sdcc); + } + + fpga_base = ioremap(FPGA_BASE, SZ_4K); + if (!fpga_base) { + pr_err("%s: Can't ioremap FPGA base address (0x%08x)\n", + __func__, FPGA_BASE); + vreg_put(vreg_sdcc); + return -EIO; + } + + msm_add_sdcc(1, &swordfish_sdcc_data, 0, 0); + msm_add_sdcc(4, &swordfish_sdcc_data, 0, 0); + + return 0; +} + diff --git a/arch/arm/mach-msm/board-swordfish-panel.c b/arch/arm/mach-msm/board-swordfish-panel.c new file mode 100644 index 0000000000000..cf5f3f62b767d --- /dev/null +++ b/arch/arm/mach-msm/board-swordfish-panel.c @@ -0,0 +1,116 @@ +/* linux/arch/arm/mach-msm/board-swordfish-panel.c + * + * Copyright (c) 2009 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. + * + * Author: Dima Zavin + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#include "board-swordfish.h" +#include "devices.h" + +#define CLK_NS_TO_RATE(ns) (1000000000UL / (ns)) + +int swordfish_panel_blank(struct msm_lcdc_panel_ops *ops) +{ + /* TODO: Turn backlight off? */ + return 0; +} + +int swordfish_panel_unblank(struct msm_lcdc_panel_ops *ops) +{ + /* TODO: Turn backlight on? */ + return 0; +} + +int swordfish_panel_init(struct msm_lcdc_panel_ops *ops) +{ + return 0; +} + +static struct resource resources_msm_fb[] = { + { + .start = MSM_FB_BASE, + .end = MSM_FB_BASE + MSM_FB_SIZE, + .flags = IORESOURCE_MEM, + }, +}; + +static struct msm_lcdc_timing swordfish_lcdc_timing = { + .clk_rate = CLK_NS_TO_RATE(26), + .hsync_pulse_width = 60, + .hsync_back_porch = 81, + .hsync_front_porch = 81, + .hsync_skew = 0, + .vsync_pulse_width = 2, + .vsync_back_porch = 20, + .vsync_front_porch = 27, + .vsync_act_low = 0, + .hsync_act_low = 0, + .den_act_low = 0, +}; + +static struct msm_fb_data swordfish_lcdc_fb_data = { + .xres = 800, + .yres = 480, + .width = 94, + .height = 57, + .output_format = 0, +}; + +static struct msm_lcdc_panel_ops swordfish_lcdc_panel_ops = { + .init = swordfish_panel_init, + .blank = swordfish_panel_blank, + .unblank = swordfish_panel_unblank, +}; + +static struct msm_lcdc_platform_data swordfish_lcdc_platform_data = { + .panel_ops = &swordfish_lcdc_panel_ops, + .timing = &swordfish_lcdc_timing, + .fb_id = 0, + .fb_data = &swordfish_lcdc_fb_data, + .fb_resource = &resources_msm_fb[0], +}; + +static struct platform_device swordfish_lcdc_device = { + .name = "msm_mdp_lcdc", + .id = -1, + .dev = { + .platform_data = &swordfish_lcdc_platform_data, + }, +}; + +int __init swordfish_init_panel(void) +{ + int rc; + if (!machine_is_swordfish()) + return 0; + + if ((rc = platform_device_register(&msm_device_mdp)) != 0) + return rc; + + if ((rc = platform_device_register(&swordfish_lcdc_device)) != 0) + return rc; + + return 0; +} + +device_initcall(swordfish_init_panel); diff --git a/arch/arm/mach-msm/board-swordfish.c b/arch/arm/mach-msm/board-swordfish.c new file mode 100644 index 0000000000000..e181b10f74d54 --- /dev/null +++ b/arch/arm/mach-msm/board-swordfish.c @@ -0,0 +1,337 @@ +/* linux/arch/arm/mach-msm/board-swordfish.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * 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 + +#ifdef CONFIG_USB_FUNCTION_MASS_STORAGE +#include +#endif + +#ifdef CONFIG_USB_ANDROID +#include +#endif + +#include "board-swordfish.h" +#include "devices.h" + +extern int swordfish_init_mmc(void); + +static struct resource smc91x_resources[] = { + [0] = { + .start = 0x70000300, + .end = 0x70000400, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MSM_GPIO_TO_INT(156), + .end = MSM_GPIO_TO_INT(156), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +#ifdef CONFIG_USB_FUNCTION +static char *swordfish_usb_functions[] = { +#if defined(CONFIG_USB_FUNCTION_MASS_STORAGE) + "usb_mass_storage", +#endif +#ifdef CONFIG_USB_FUNCTION_ADB + "adb", +#endif +}; + +static struct msm_hsusb_product swordfish_usb_products[] = { + { + .product_id = 0x0d01, + .functions = 0x00000001, /* "usb_mass_storage" only */ + }, + { + .product_id = 0x0d02, + .functions = 0x00000003, /* "usb_mass_storage" and "adb" */ + }, +}; +#endif + +static int swordfish_phy_init_seq[] = { 0x1D, 0x0D, 0x1D, 0x10, -1 }; + +static struct msm_hsusb_platform_data msm_hsusb_pdata = { + .phy_init_seq = swordfish_phy_init_seq, +#ifdef CONFIG_USB_FUNCTION + .vendor_id = 0x18d1, + .product_id = 0x0d02, + .version = 0x0100, + .product_name = "Swordfish", + .serial_number = "42", + .manufacturer_name = "Qualcomm", + + .functions = swordfish_usb_functions, + .num_functions = ARRAY_SIZE(swordfish_usb_functions), + .products = swordfish_usb_products, + .num_products = ARRAY_SIZE(swordfish_usb_products), +#endif +}; + +#ifdef CONFIG_USB_FUNCTION_MASS_STORAGE +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .buf_size = 16384, + .vendor = "Qualcomm", + .product = "Swordfish", + .release = 0x0100, +}; + +static struct platform_device usb_mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &mass_storage_pdata, + }, +}; +#endif + +static struct resource msm_kgsl_resources[] = { + { + .name = "kgsl_reg_memory", + .start = MSM_GPU_REG_PHYS, + .end = MSM_GPU_REG_PHYS + MSM_GPU_REG_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "kgsl_phys_memory", + .start = MSM_GPU_MEM_BASE, + .end = MSM_GPU_MEM_BASE + MSM_GPU_MEM_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_GRAPHICS, + .end = INT_GRAPHICS, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_kgsl_device = { + .name = "kgsl", + .id = -1, + .resource = msm_kgsl_resources, + .num_resources = ARRAY_SIZE(msm_kgsl_resources), +}; + +static struct android_pmem_platform_data mdp_pmem_pdata = { + .name = "pmem", + .start = MSM_PMEM_MDP_BASE, + .size = MSM_PMEM_MDP_SIZE, + .no_allocator = 0, + .cached = 1, +}; + +static struct android_pmem_platform_data android_pmem_gpu0_pdata = { + .name = "pmem_gpu0", + .start = MSM_PMEM_GPU0_BASE, + .size = MSM_PMEM_GPU0_SIZE, + .no_allocator = 0, + .cached = 0, +}; + +static struct android_pmem_platform_data android_pmem_gpu1_pdata = { + .name = "pmem_gpu1", + .start = MSM_PMEM_GPU1_BASE, + .size = MSM_PMEM_GPU1_SIZE, + .no_allocator = 0, + .cached = 0, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .start = MSM_PMEM_ADSP_BASE, + .size = MSM_PMEM_ADSP_SIZE, + .no_allocator = 0, + .cached = 0, +}; + +static struct platform_device android_pmem_mdp_device = { + .name = "android_pmem", + .id = 0, + .dev = { + .platform_data = &mdp_pmem_pdata + }, +}; + +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 1, + .dev = { + .platform_data = &android_pmem_adsp_pdata, + }, +}; + +static struct platform_device android_pmem_gpu0_device = { + .name = "android_pmem", + .id = 2, + .dev = { + .platform_data = &android_pmem_gpu0_pdata, + }, +}; + +static struct platform_device android_pmem_gpu1_device = { + .name = "android_pmem", + .id = 3, + .dev = { + .platform_data = &android_pmem_gpu1_pdata, + }, +}; + +#ifdef CONFIG_USB_ANDROID +static struct android_usb_platform_data android_usb_pdata = { + .vendor_id = 0x18d1, + .product_id = 0x0d01, + .adb_product_id = 0x0d02, + .version = 0x0100, + .serial_number = "42", + .product_name = "Swordfishdroid", + .manufacturer_name = "Qualcomm", + .nluns = 1, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; +#endif + +static struct platform_device fish_battery_device = { + .name = "fish_battery", +}; + +static struct msm_ts_platform_data swordfish_ts_pdata = { + .min_x = 296, + .max_x = 3800, + .min_y = 296, + .max_y = 3800, + .min_press = 0, + .max_press = 256, + .inv_x = 4096, + .inv_y = 4096, +}; + +static struct platform_device *devices[] __initdata = { +#if !defined(CONFIG_MSM_SERIAL_DEBUGGER) + &msm_device_uart3, +#endif + &msm_device_smd, + &msm_device_nand, + &msm_device_hsusb, +#ifdef CONFIG_USB_FUNCTION_MASS_STORAGE + &usb_mass_storage_device, +#endif +#ifdef CONFIG_USB_ANDROID + &android_usb_device, +#endif + &fish_battery_device, + &smc91x_device, + &msm_device_touchscreen, + &android_pmem_mdp_device, + &android_pmem_adsp_device, + &android_pmem_gpu0_device, + &android_pmem_gpu1_device, + &msm_kgsl_device, +}; + +extern struct sys_timer msm_timer; + +static struct msm_acpu_clock_platform_data swordfish_clock_data = { + .acpu_switch_time_us = 20, + .max_speed_delta_khz = 256000, + .vdd_switch_time_us = 62, + .power_collapse_khz = 128000000, + .wait_for_irq_khz = 128000000, +}; + +void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq); + +static void __init swordfish_init(void) +{ + int rc; + + msm_acpu_clock_init(&swordfish_clock_data); +#if defined(CONFIG_MSM_SERIAL_DEBUGGER) + msm_serial_debug_init(MSM_UART3_PHYS, INT_UART3, + &msm_device_uart3.dev, 1); +#endif + msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; + msm_device_touchscreen.dev.platform_data = &swordfish_ts_pdata; + platform_add_devices(devices, ARRAY_SIZE(devices)); + msm_hsusb_set_vbus_state(1); + rc = swordfish_init_mmc(); + if (rc) + pr_crit("%s: MMC init failure (%d)\n", __func__, rc); +} + +static void __init swordfish_fixup(struct machine_desc *desc, struct tag *tags, + char **cmdline, struct meminfo *mi) +{ + mi->nr_banks = 1; + mi->bank[0].start = PHYS_OFFSET; + mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); + mi->bank[0].size = (101*1024*1024); +} + +static void __init swordfish_map_io(void) +{ + msm_map_qsd8x50_io(); + msm_clock_init(msm_clocks_8x50, msm_num_clocks_8x50); +} + +MACHINE_START(SWORDFISH, "Swordfish Board (QCT SURF8250)") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x20000100, + .fixup = swordfish_fixup, + .map_io = swordfish_map_io, + .init_irq = msm_init_irq, + .init_machine = swordfish_init, + .timer = &msm_timer, +MACHINE_END diff --git a/arch/arm/mach-msm/board-swordfish.h b/arch/arm/mach-msm/board-swordfish.h new file mode 100644 index 0000000000000..b9ea54f680dbc --- /dev/null +++ b/arch/arm/mach-msm/board-swordfish.h @@ -0,0 +1,48 @@ +/* arch/arm/mach-msm/board-swordfish.h + * + * Copyright (C) 2009 Google Inc. + * Author: Dima Zavin + * + * 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 __ARCH_ARM_MACH_MSM_BOARD_SWORDFISH_H +#define __ARCH_ARM_MACH_MSM_BOARD_SWORDFISH_H + +#include + +#define MSM_SMI_BASE 0x02B00000 +#define MSM_SMI_SIZE 0x01500000 + +#define MSM_PMEM_MDP_BASE 0x03000000 +#define MSM_PMEM_MDP_SIZE 0x01000000 + +#define MSM_EBI1_BASE 0x20000000 +#define MSM_EBI1_SIZE 0x0E000000 + +#define MSM_PMEM_ADSP_BASE 0x2A300000 +#define MSM_PMEM_ADSP_SIZE 0x02000000 + +#define MSM_PMEM_GPU1_BASE 0x2C300000 +#define MSM_PMEM_GPU1_SIZE 0x01400000 + +#define MSM_PMEM_GPU0_BASE 0x2D700000 +#define MSM_PMEM_GPU0_SIZE 0x00400000 + +#define MSM_GPU_MEM_BASE 0x2DB00000 +#define MSM_GPU_MEM_SIZE 0x00200000 + +#define MSM_RAM_CONSOLE_BASE 0x2DD00000 +#define MSM_RAM_CONSOLE_SIZE 0x00040000 + +#define MSM_FB_BASE 0x2DE00000 +#define MSM_FB_SIZE 0x00200000 + +#endif /* __ARCH_ARM_MACH_MSM_BOARD_SWORDFISH_H */ From 8ed8a52924a25413fa539392762900bf52fd680e Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 20 May 2009 22:55:58 -0700 Subject: [PATCH 0406/2556] [ARM] msm: Hack the RTC driver to match either msm7k AMSS version or arch_qsd8k Signed-off-by: Dima Zavin --- drivers/rtc/rtc-msm7x00a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-msm7x00a.c b/drivers/rtc/rtc-msm7x00a.c index 03f57cebbeee8..690bc398cd6c0 100644 --- a/drivers/rtc/rtc-msm7x00a.c +++ b/drivers/rtc/rtc-msm7x00a.c @@ -30,7 +30,7 @@ extern void msm_pm_set_max_sleep_time(int64_t sleep_time_ns); -#if CONFIG_MSM_AMSS_VERSION >= 6350 +#if CONFIG_MSM_AMSS_VERSION >= 6350 || defined(CONFIG_ARCH_QSD8X50) #define APP_TIMEREMOTE_PDEV_NAME "rs30000048:00010000" #else #define APP_TIMEREMOTE_PDEV_NAME "rs30000048:0da5b528" From b49c0f50acc3f5232b561c7bd17a5ffa4596493a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 24 Jun 2009 19:43:05 -0700 Subject: [PATCH 0407/2556] [ARM] msm: Add sirc fiq support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/include/mach/sirc.h | 1 + arch/arm/mach-msm/irq.c | 39 ++++++++++++++++++++------- arch/arm/mach-msm/sirc.c | 26 ++++++++++++++++++ arch/arm/mach-msm/sirc.h | 27 +++++++++++++++++++ 4 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 arch/arm/mach-msm/sirc.h diff --git a/arch/arm/mach-msm/include/mach/sirc.h b/arch/arm/mach-msm/include/mach/sirc.h index 452c38daeee55..c23d429422d78 100644 --- a/arch/arm/mach-msm/include/mach/sirc.h +++ b/arch/arm/mach-msm/include/mach/sirc.h @@ -42,6 +42,7 @@ struct sirc_regs_t { struct sirc_cascade_regs { void *int_status; unsigned int cascade_irq; + unsigned int cascade_fiq; }; void msm_init_sirc(void); diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c index 0f3c33f52a180..ea7d86fb29dd0 100644 --- a/arch/arm/mach-msm/irq.c +++ b/arch/arm/mach-msm/irq.c @@ -31,6 +31,7 @@ #include #include +#include "sirc.h" #include "smd_private.h" enum { @@ -469,12 +470,7 @@ void __init msm_init_irq(void) set_irq_flags(n, IRQF_VALID); } -#if defined(CONFIG_ARCH_QSD8X50) - { - void msm_init_sirc(void); - msm_init_sirc(); - } -#endif + msm_init_sirc(); } #if defined(CONFIG_MSM_FIQ_SUPPORT) @@ -487,21 +483,23 @@ void msm_trigger_irq(int irq) void msm_fiq_enable(int irq) { + struct irq_data *d = irq_get_irq_data(irq); unsigned long flags; local_irq_save(flags); - msm_irq_unmask(irq); + d->chip->irq_unmask(d); local_irq_restore(flags); } void msm_fiq_disable(int irq) { + struct irq_data *d = irq_get_irq_data(irq); unsigned long flags; local_irq_save(flags); - msm_irq_mask(irq); + d->chip->irq_mask(d); local_irq_restore(flags); } -void msm_fiq_select(int irq) +static void _msm_fiq_select(int irq) { void __iomem *reg = VIC_INT_SELECT0 + ((irq & 32) ? 4 : 0); unsigned index = (irq >> 5) & 1; @@ -514,7 +512,7 @@ void msm_fiq_select(int irq) local_irq_restore(flags); } -void msm_fiq_unselect(int irq) +static void _msm_fiq_unselect(int irq) { void __iomem *reg = VIC_INT_SELECT0 + ((irq & 32) ? 4 : 0); unsigned index = (irq >> 5) & 1; @@ -526,6 +524,27 @@ void msm_fiq_unselect(int irq) writel(msm_irq_shadow_reg[index].int_select, reg); local_irq_restore(flags); } + +void msm_fiq_select(int irq) +{ + if (irq < FIRST_SIRC_IRQ) + _msm_fiq_select(irq); + else if (irq < FIRST_GPIO_IRQ) + sirc_fiq_select(irq, true); + else + pr_err("unsupported fiq %d", irq); +} + +void msm_fiq_unselect(int irq) +{ + if (irq < FIRST_SIRC_IRQ) + _msm_fiq_unselect(irq); + else if (irq < FIRST_GPIO_IRQ) + sirc_fiq_select(irq, false); + else + pr_err("unsupported fiq %d", irq); +} + /* set_fiq_handler originally from arch/arm/kernel/fiq.c */ static void set_fiq_handler(void *start, unsigned int length) { diff --git a/arch/arm/mach-msm/sirc.c b/arch/arm/mach-msm/sirc.c index 126a8f045adb7..06688a3ffdd74 100644 --- a/arch/arm/mach-msm/sirc.c +++ b/arch/arm/mach-msm/sirc.c @@ -18,8 +18,11 @@ #include #include #include +#include #include +#include "sirc.h" + static unsigned int int_enable; static unsigned int wake_enable; @@ -36,6 +39,7 @@ static struct sirc_cascade_regs sirc_reg_table[] = { { .int_status = SPSS_SIRC_IRQ_STATUS, .cascade_irq = INT_SIRC_0, + .cascade_fiq = INT_SIRC_1, } }; @@ -118,6 +122,24 @@ static int sirc_irq_set_type(struct irq_data *d, unsigned int flow_type) return 0; } +#if defined(CONFIG_MSM_FIQ_SUPPORT) +void sirc_fiq_select(int irq, bool enable) +{ + uint32_t mask = 1 << (irq - FIRST_SIRC_IRQ); + uint32_t val; + unsigned long flags; + + local_irq_save(flags); + val = readl(SPSS_SIRC_INT_SELECT); + if (enable) + val |= mask; + else + val &= ~mask; + writel(val, SPSS_SIRC_INT_SELECT); + local_irq_restore(flags); +} +#endif + /* Finds the pending interrupt on the passed cascade irq and redrives it */ static void sirc_irq_handler(unsigned int irq, struct irq_desc *desc) { @@ -185,6 +207,10 @@ void __init msm_init_sirc(void) set_irq_chained_handler(sirc_reg_table[i].cascade_irq, sirc_irq_handler); set_irq_wake(sirc_reg_table[i].cascade_irq, 1); +#if defined(CONFIG_MSM_FIQ_SUPPORT) + msm_fiq_select(sirc_reg_table[i].cascade_fiq); + msm_fiq_enable(sirc_reg_table[i].cascade_fiq); +#endif } return; } diff --git a/arch/arm/mach-msm/sirc.h b/arch/arm/mach-msm/sirc.h new file mode 100644 index 0000000000000..8e1399f028b2d --- /dev/null +++ b/arch/arm/mach-msm/sirc.h @@ -0,0 +1,27 @@ +/* arch/arm/mach-msm/pm.h + * + * Copyright (C) 2009 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 _ARCH_ARM_MACH_MSM_SIRC_H +#define _ARCH_ARM_MACH_MSM_SIRC_H + +#ifdef CONFIG_ARCH_QSD8X50 +void sirc_fiq_select(int irq, bool enable); +void __init msm_init_sirc(void); +#else +static inline void sirc_fiq_select(int irq, bool enable) {} +static inline void __init msm_init_sirc(void) {} +#endif + +#endif From d5ed60460d4427ea5e548d68d4d953dd2f74c848 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 22 Feb 2010 17:28:34 -0800 Subject: [PATCH 0408/2556] [ARM] msm: smd_debug: parse the hw version/id info from smem build id Change-Id: Ibbf172c08f5cddb373492b4c6e9257414537b0f6 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/smd_debug.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-msm/smd_debug.c b/arch/arm/mach-msm/smd_debug.c index 8736afff82f3e..69a9cd9b6e3ba 100644 --- a/arch/arm/mach-msm/smd_debug.c +++ b/arch/arm/mach-msm/smd_debug.c @@ -154,20 +154,31 @@ static int debug_read_version(char *buf, int max) return sprintf(buf, "%d.%d\n", version >> 16, version & 0xffff); } +struct smem_msm_id { + uint32_t format; + uint32_t msm_id; + uint32_t msm_ver; + char build_id[32]; +}; + static int debug_read_build_id(char *buf, int max) { unsigned size; void *data; + struct smem_msm_id *msm_id; - data = smem_item(SMEM_HW_SW_BUILD_ID, &size); - if (!data) + msm_id = smem_item(SMEM_HW_SW_BUILD_ID, &size); + if (!msm_id || (size < sizeof(struct smem_msm_id))) return 0; if (size >= max) size = max; - memcpy(buf, data, size); - return size; + return scnprintf(buf, size, "fmt=%d id=%d vers=%d.%d build_id='%s'\n", + msm_id->format,msm_id->msm_id, + (msm_id->msm_ver >> 16) & 0xffff, + msm_id->msm_ver & 0xffff, + msm_id->build_id); } static int debug_read_alloc_tbl(char *buf, int max) From 2be0b3ee1007fcc99b6fe0814b09ee6ebe7f90d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 20 May 2009 16:52:36 -0700 Subject: [PATCH 0409/2556] [ARM] msm: Fix pm/irq/timer for: Add item argument to smsm_change_state and smsm_get_state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new protocol require writing to two state fields, and reading several fields. Change-Id: Idb759ad98a6dc03e8bbe585135a61f21df0582e1 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/irq.c | 3 ++- arch/arm/mach-msm/pm.c | 18 +++++++++--------- arch/arm/mach-msm/timer.c | 10 +++++----- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c index ea7d86fb29dd0..81191cb98f66f 100644 --- a/arch/arm/mach-msm/irq.c +++ b/arch/arm/mach-msm/irq.c @@ -425,7 +425,8 @@ void msm_irq_exit_sleep3(void) "state %x\n", int_info->aArm_en_mask, int_info->aArm_interrupts_pending, int_info->aArm_wakeup_reason, readl(VIC_IRQ_STATUS0), - readl(VIC_IRQ_STATUS1), smsm_get_state()); + readl(VIC_IRQ_STATUS1), + smsm_get_state(SMSM_STATE_MODEM)); } static struct irq_chip msm_irq_chip = { diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index 06f401711aa03..7b452ce5fac25 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -139,7 +139,7 @@ msm_pm_wait_state(uint32_t wait_state_all_set, uint32_t wait_state_all_clear, uint32_t state; for (i = 0; i < 100000; i++) { - state = smsm_get_state(); + state = smsm_get_state(SMSM_STATE_MODEM); if (((state & wait_state_all_set) == wait_state_all_set) && ((~state & wait_state_all_clear) == wait_state_all_clear) && (wait_state_any_set == 0 || (state & wait_state_any_set) || @@ -210,7 +210,7 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) if (sleep_delay == 0 && sleep_mode >= MSM_PM_SLEEP_MODE_APPS_SLEEP) sleep_delay = 192000*5; /* APPS_SLEEP does not allow infinite timeout */ smsm_set_sleep_duration(sleep_delay); - ret = smsm_change_state(SMSM_RUN, enter_state); + ret = smsm_change_state(SMSM_STATE_APPS, SMSM_RUN, enter_state); if (ret) { printk(KERN_ERR "msm_sleep(): smsm_change_state %x failed\n", enter_state); enter_state = 0; @@ -218,7 +218,7 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) } ret = msm_pm_wait_state(enter_wait_set, enter_wait_clear, 0, 0); if (ret) { - printk(KERN_INFO "msm_sleep(): msm_pm_wait_state failed, %x\n", smsm_get_state()); + printk(KERN_INFO "msm_sleep(): msm_pm_wait_state failed, %x\n", smsm_get_state(SMSM_STATE_MODEM)); goto enter_failed; } } @@ -236,7 +236,7 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) printk(KERN_INFO "msm_sleep(): enter " "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, " "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN), - readl(A11S_PWRDOWN), smsm_get_state()); + readl(A11S_PWRDOWN), smsm_get_state(SMSM_STATE_MODEM)); } if (sleep_mode <= MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT) { @@ -289,32 +289,32 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) printk(KERN_INFO "msm_sleep(): exit A11S_CLK_SLEEP_EN %x, " "A11S_PWRDOWN %x, smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN), readl(A11S_PWRDOWN), - smsm_get_state()); + smsm_get_state(SMSM_STATE_MODEM)); ramp_down_failed: msm_irq_exit_sleep1(); enter_failed: if (enter_state) { writel(0x00, A11S_CLK_SLEEP_EN); writel(0, A11S_PWRDOWN); - smsm_change_state(enter_state, exit_state); + smsm_change_state(SMSM_STATE_APPS, enter_state, exit_state); msm_pm_wait_state(exit_wait_set, exit_wait_clear, 0, 0); if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) printk(KERN_INFO "msm_sleep(): sleep exit " "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, " "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN), - readl(A11S_PWRDOWN), smsm_get_state()); + readl(A11S_PWRDOWN), smsm_get_state(SMSM_STATE_MODEM)); if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) smsm_print_sleep_info(); } msm_irq_exit_sleep2(); if (enter_state) { - smsm_change_state(exit_state, SMSM_RUN); + smsm_change_state(SMSM_STATE_APPS, exit_state, SMSM_RUN); msm_pm_wait_state(SMSM_RUN, 0, 0, 0); if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) printk(KERN_INFO "msm_sleep(): sleep exit " "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, " "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN), - readl(A11S_PWRDOWN), smsm_get_state()); + readl(A11S_PWRDOWN), smsm_get_state(SMSM_STATE_MODEM)); } msm_irq_exit_sleep3(); msm_gpio_exit_sleep(); diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index 8101ca4a84434..a6975b19a0584 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -245,11 +245,11 @@ static uint32_t msm_timer_sync_smem_clock(int exit_sleep) timeout_delta = (clock->freq >> (7 - clock->shift)); /* 7.8ms */ - last_state = state = smsm_get_state(); + last_state = state = smsm_get_state(SMSM_STATE_MODEM); if (*smem_clock) { printk(KERN_INFO "get_smem_clock: invalid start state %x " "clock %u\n", state, *smem_clock); - smsm_change_state(SMSM_TIMEWAIT, SMSM_TIMEINIT); + smsm_change_state(SMSM_STATE_APPS, SMSM_TIMEWAIT, SMSM_TIMEINIT); entry_time = msm_read_timer_count(clock); timeout = entry_time + timeout_delta; while (*smem_clock != 0 && check_timeout(clock, timeout)) @@ -264,10 +264,10 @@ static uint32_t msm_timer_sync_smem_clock(int exit_sleep) } entry_time = msm_read_timer_count(clock); timeout = entry_time + timeout_delta; - smsm_change_state(SMSM_TIMEINIT, SMSM_TIMEWAIT); + smsm_change_state(SMSM_STATE_APPS, SMSM_TIMEINIT, SMSM_TIMEWAIT); do { smem_clock_val = *smem_clock; - state = smsm_get_state(); + state = smsm_get_state(SMSM_STATE_MODEM); if (state != last_state) { last_state = state; printk(KERN_INFO "get_smem_clock: state %x clock %u\n", @@ -293,7 +293,7 @@ static uint32_t msm_timer_sync_smem_clock(int exit_sleep) "in %d ticks\n", state, *smem_clock, msm_read_timer_count(clock) - entry_time); } - smsm_change_state(SMSM_TIMEWAIT, SMSM_TIMEINIT); + smsm_change_state(SMSM_STATE_APPS, SMSM_TIMEWAIT, SMSM_TIMEINIT); entry_time = msm_read_timer_count(clock); timeout = entry_time + timeout_delta; while (*smem_clock != 0 && check_timeout(clock, timeout)) From 87756b41949fb7671e157257b27ce7a6c30c58fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 20 May 2009 17:08:52 -0700 Subject: [PATCH 0410/2556] [ARM] msm: Add time synchronization for 8k. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/timer.c | 142 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 138 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index a6975b19a0584..5c531c7f73e4a 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -28,7 +28,9 @@ #include "smd_private.h" enum { - MSM_TIMER_DEBUG_SYNC = 1U << 0, + MSM_TIMER_DEBUG_SYNC_STATE = 1U << 0, + MSM_TIMER_DEBUG_SYNC_UPDATE = 1U << 1, + MSM_TIMER_DEBUG_SYNC = 1U << 2, }; static int msm_timer_debug_mask; module_param_named(debug_mask, msm_timer_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); @@ -220,6 +222,8 @@ static inline int check_timeout(struct msm_clock *clock, uint32_t timeout) return (int32_t)(msm_read_timer_count(clock) - timeout) <= 0; } +#ifndef CONFIG_ARCH_MSM_SCORPION + static uint32_t msm_timer_sync_smem_clock(int exit_sleep) { struct msm_clock *clock = &msm_clocks[MSM_CLOCK_GPT]; @@ -270,8 +274,9 @@ static uint32_t msm_timer_sync_smem_clock(int exit_sleep) state = smsm_get_state(SMSM_STATE_MODEM); if (state != last_state) { last_state = state; - printk(KERN_INFO "get_smem_clock: state %x clock %u\n", - state, smem_clock_val); + if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC_STATE) + pr_info("get_smem_clock: state %x clock %u\n", + state, smem_clock_val); } } while (smem_clock_val == 0 && check_timeout(clock, timeout)); if (smem_clock_val) { @@ -282,11 +287,16 @@ static uint32_t msm_timer_sync_smem_clock(int exit_sleep) else clock->smem_offset = new_offset - clock->offset; clock->smem_in_sync = 1; - if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC) + if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC_UPDATE) printk(KERN_INFO "get_smem_clock: state %x " "clock %u new offset %u+%u\n", state, smem_clock_val, clock->offset, clock->smem_offset); + } else if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC) { + printk(KERN_INFO "get_smem_clock: state %x " + "clock %u offset %u+%u\n", + state, smem_clock_val, + clock->offset, clock->smem_offset); } } else { printk(KERN_INFO "get_smem_clock: timeout state %x clock %u " @@ -305,6 +315,130 @@ static uint32_t msm_timer_sync_smem_clock(int exit_sleep) return smem_clock_val; } +#else + +/* Time Master State Bits */ +#define DEM_TIME_MASTER_TIME_PENDING_APPS BIT(0) + +/* Time Slave State Bits */ +#define DEM_TIME_SLAVE_TIME_REQUEST 0x0400 +#define DEM_TIME_SLAVE_TIME_POLL 0x0800 +#define DEM_TIME_SLAVE_TIME_INIT 0x1000 + +static uint32_t msm_timer_sync_smem_clock(int exit_sleep) +{ + struct msm_clock *clock = &msm_clocks[MSM_CLOCK_GPT]; + uint32_t *smem_clock; + uint32_t smem_clock_val; + uint32_t bad_clock = 0; + uint32_t timeout; + uint32_t entry_time; + uint32_t timeout_delta; + uint32_t last_state; + uint32_t state; + uint32_t new_offset; + + smem_clock = smem_alloc(SMEM_SMEM_SLOW_CLOCK_VALUE, + sizeof(uint32_t)); + + if (smem_clock == NULL) { + printk(KERN_ERR "no smem clock\n"); + return 0; + } + + if (!exit_sleep && clock->smem_in_sync) + return 0; + + timeout_delta = (clock->freq >> (7 - clock->shift)); /* 7.8ms */ + + entry_time = msm_read_timer_count(clock); + last_state = state = smsm_get_state(SMSM_STATE_TIME_MASTER_DEM); + timeout = entry_time + timeout_delta; + while ((smsm_get_state(SMSM_STATE_TIME_MASTER_DEM) + & DEM_TIME_MASTER_TIME_PENDING_APPS) + && check_timeout(clock, timeout)) + ; + if ((smsm_get_state(SMSM_STATE_TIME_MASTER_DEM) & + DEM_TIME_MASTER_TIME_PENDING_APPS)) { + printk(KERN_INFO "get_smem_clock: invalid start state %x " + "clock %u in %d ticks\n", + state, *smem_clock, + msm_read_timer_count(clock) - entry_time); + bad_clock = *smem_clock; + } + entry_time = msm_read_timer_count(clock); + timeout = entry_time + timeout_delta; + smsm_change_state(SMSM_STATE_APPS_DEM, + DEM_TIME_SLAVE_TIME_INIT, DEM_TIME_SLAVE_TIME_REQUEST); + while (!(smsm_get_state(SMSM_STATE_TIME_MASTER_DEM) + & DEM_TIME_MASTER_TIME_PENDING_APPS) + && check_timeout(clock, timeout)) + ; + if (!(smsm_get_state(SMSM_STATE_TIME_MASTER_DEM) & + DEM_TIME_MASTER_TIME_PENDING_APPS)) { + printk(KERN_INFO "get_smem_clock: invalid start state %x " + "clock %u in %d ticks\n", + state, *smem_clock, + msm_read_timer_count(clock) - entry_time); + bad_clock = *smem_clock; + } + smsm_change_state(SMSM_STATE_APPS_DEM, + DEM_TIME_SLAVE_TIME_REQUEST, DEM_TIME_SLAVE_TIME_POLL); + do { + smem_clock_val = *smem_clock; + state = smsm_get_state(SMSM_STATE_TIME_MASTER_DEM); + if (state != last_state) { + last_state = state; + if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC_STATE) + pr_info("get_smem_clock: state %x clock %u\n", + state, smem_clock_val); + } + } while ((!smem_clock_val || smem_clock_val == bad_clock) + && check_timeout(clock, timeout)); + if (smem_clock_val && smem_clock_val != bad_clock) { + new_offset = smem_clock_val - msm_read_timer_count(clock); + if (clock->offset + clock->smem_offset != new_offset) { + if (exit_sleep) + clock->offset = new_offset - clock->smem_offset; + else + clock->smem_offset = new_offset - clock->offset; + clock->smem_in_sync = 1; + if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC_UPDATE) + printk(KERN_INFO "get_smem_clock: state %x " + "clock %u new offset %u+%u\n", + state, smem_clock_val, + clock->offset, clock->smem_offset); + } else if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC) { + printk(KERN_INFO "get_smem_clock: state %x " + "clock %u offset %u+%u\n", + state, smem_clock_val, + clock->offset, clock->smem_offset); + } + } else { + printk(KERN_INFO "get_smem_clock: timeout state %x clock %u " + "in %d ticks\n", state, *smem_clock, + msm_read_timer_count(clock) - entry_time); + } + smsm_change_state(SMSM_STATE_APPS_DEM, + DEM_TIME_SLAVE_TIME_POLL, DEM_TIME_SLAVE_TIME_INIT); +#if 1 /* debug */ + entry_time = msm_read_timer_count(clock); + timeout = entry_time + timeout_delta; + while ((smsm_get_state(SMSM_STATE_TIME_MASTER_DEM) + & DEM_TIME_MASTER_TIME_PENDING_APPS) + && check_timeout(clock, timeout)) + ; + if (smsm_get_state(SMSM_STATE_TIME_MASTER_DEM) & + DEM_TIME_MASTER_TIME_PENDING_APPS) + printk(KERN_INFO "get_smem_clock: exit timeout state %x " + "clock %u in %d ticks\n", state, *smem_clock, + msm_read_timer_count(clock) - entry_time); +#endif + return smem_clock_val; +} + +#endif + static void msm_timer_reactivate_alarm(struct msm_clock *clock) { long alarm_delta = clock->alarm_vtime - clock->offset - From 6b8e16463a61b7033a90802af64ed818e32eb73d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Sun, 15 Nov 2009 20:00:22 -0800 Subject: [PATCH 0411/2556] [ARM] msm: timer: Don't crash if we get an interrupt from the inactive timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I7ab048e0da9c15b3b89def5c6b0d342aa70f0b64 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/timer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index 5c531c7f73e4a..e0f401c5b10e0 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -101,7 +101,8 @@ static struct msm_clock *msm_active_clock; static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) { struct clock_event_device *evt = dev_id; - evt->event_handler(evt); + if (evt->event_handler) + evt->event_handler(evt); return IRQ_HANDLED; } From e36bc1d4176d1915472baf63e98a2158eb49efc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 13 Nov 2009 20:24:54 -0800 Subject: [PATCH 0412/2556] [ARM] msm: timer: Add fast timer api to read debug timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Activating this timer disables low power idle modes, but the debug timer, as configured, has ~0.2us resolution instead of the gp timer's fixed ~30.5us resolution. Change-Id: I6ada25442454cfb380f8fe618fc507502b1a279e Signed-off-by: Arve Hjønnevåg --- .../mach-msm/include/mach/msm_fast_timer.h | 19 ++++++ arch/arm/mach-msm/timer.c | 63 ++++++++++++++++++- 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-msm/include/mach/msm_fast_timer.h diff --git a/arch/arm/mach-msm/include/mach/msm_fast_timer.h b/arch/arm/mach-msm/include/mach/msm_fast_timer.h new file mode 100644 index 0000000000000..e1660c192a3ca --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_fast_timer.h @@ -0,0 +1,19 @@ +/* arch/arm/mach-msm/include/mach/msm_fast_timer.h + * + * Copyright (C) 2009 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. + * + */ + +void msm_enable_fast_timer(void); +void msm_disable_fast_timer(void); +u32 msm_read_fast_timer(void); + diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index e0f401c5b10e0..4492ec81b082c 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -97,6 +97,8 @@ enum { }; static struct msm_clock msm_clocks[]; static struct msm_clock *msm_active_clock; +static DEFINE_SPINLOCK(msm_fast_timer_lock); +static int msm_fast_timer_enabled; static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) { @@ -457,7 +459,7 @@ int64_t msm_timer_enter_idle(void) uint32_t count; int32_t delta; - if (clock != &msm_clocks[MSM_CLOCK_GPT]) + if (clock != &msm_clocks[MSM_CLOCK_GPT] || msm_fast_timer_enabled) return 0; msm_timer_sync_smem_clock(0); @@ -609,6 +611,65 @@ static struct msm_clock msm_clocks[] = { } }; +/** + * msm_enable_fast_timer - Enable fast timer + * + * Prevents low power idle, but the caller must call msm_disable_fast_timer + * before suspend completes. + * Reference counted. + */ +void msm_enable_fast_timer(void) +{ + u32 max; + unsigned long irq_flags; + struct msm_clock *clock = &msm_clocks[MSM_CLOCK_DGT]; + + spin_lock_irqsave(&msm_fast_timer_lock, irq_flags); + if (msm_fast_timer_enabled++) + goto done; + if (msm_active_clock == &msm_clocks[MSM_CLOCK_DGT]) { + pr_warning("msm_enable_fast_timer: timer already in use, " + "returned time will jump when hardware timer wraps\n"); + goto done; + } + max = (clock->clockevent.mult >> (clock->clockevent.shift - 32)) - 1; + writel(max, clock->regbase + TIMER_MATCH_VAL); + writel(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN, + clock->regbase + TIMER_ENABLE); +done: + spin_unlock_irqrestore(&msm_fast_timer_lock, irq_flags); +} + +/** + * msm_enable_fast_timer - Disable fast timer + */ +void msm_disable_fast_timer(void) +{ + unsigned long irq_flags; + struct msm_clock *clock = &msm_clocks[MSM_CLOCK_DGT]; + + spin_lock_irqsave(&msm_fast_timer_lock, irq_flags); + if (!WARN(!msm_fast_timer_enabled, "msm_disable_fast_timer undeflow") + && !--msm_fast_timer_enabled + && msm_active_clock != &msm_clocks[MSM_CLOCK_DGT]) + writel(0, clock->regbase + TIMER_ENABLE); + spin_unlock_irqrestore(&msm_fast_timer_lock, irq_flags); +} + +/** + * msm_enable_fast_timer - Read fast timer + * + * Returns 32bit nanosecond time value. + */ +u32 msm_read_fast_timer(void) +{ + cycle_t ticks; + struct msm_clock *clock = &msm_clocks[MSM_CLOCK_DGT]; + ticks = msm_read_timer_count(clock) >> MSM_DGT_SHIFT; + return clocksource_cyc2ns(ticks, clock->clocksource.mult, + clock->clocksource.shift); +} + static void __init msm_timer_init(void) { int i; From 68b275a5d4451aa341cf7cf479df27efdddcef10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 8 Jan 2010 16:35:17 -0800 Subject: [PATCH 0413/2556] [ARM] msm: Fix time synchronization on 7k. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The modem on 7k writes to the apps processor's state word during clock synchronization. This could cause the signal that we finished synchronizing to be lost. Change-Id: I0b46ff3198a7876d847af9963d71c6bd9cf786a1 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/timer.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index 4492ec81b082c..7868db17862c2 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -309,8 +309,16 @@ static uint32_t msm_timer_sync_smem_clock(int exit_sleep) smsm_change_state(SMSM_STATE_APPS, SMSM_TIMEWAIT, SMSM_TIMEINIT); entry_time = msm_read_timer_count(clock); timeout = entry_time + timeout_delta; - while (*smem_clock != 0 && check_timeout(clock, timeout)) - ; + while (*smem_clock != 0 && check_timeout(clock, timeout)) { + uint32_t astate = smsm_get_state(SMSM_STATE_APPS); + if ((astate & SMSM_TIMEWAIT) || !(astate & SMSM_TIMEINIT)) { + if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC_STATE) + pr_info("get_smem_clock: modem overwrote " + "apps state %x\n", astate); + smsm_change_state(SMSM_STATE_APPS, + SMSM_TIMEWAIT, SMSM_TIMEINIT); + } + } if (*smem_clock) printk(KERN_INFO "get_smem_clock: exit timeout state %x " "clock %u in %d ticks\n", state, *smem_clock, From e838f6665c8e240eb099fc216f2a7caad3077451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 16 Jun 2009 14:48:21 -0700 Subject: [PATCH 0414/2556] [ARM] msm: Add 8k power collapse support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds idle-v7.S from Qualcomm's tree, but with cpu init code from the bootloader added. Change-Id: I6059f5ab2ba7b9fb49b014b109e8b108ee5917f3 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/Makefile | 6 +- arch/arm/mach-msm/idle-v7.S | 230 ++++++++++++++++++++++++++++++++++++ arch/arm/mach-msm/irq.c | 68 +++++++---- arch/arm/mach-msm/pm.c | 96 +++++++++++++-- 4 files changed, 360 insertions(+), 40 deletions(-) create mode 100644 arch/arm/mach-msm/idle-v7.S diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index faaa7c4234c16..751019f217f9d 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -1,4 +1,4 @@ -obj-y += io.o idle.o timer.o +obj-y += io.o timer.o obj-y += memory.o ifndef CONFIG_ARCH_MSM8X60 obj-y += dma.o @@ -6,8 +6,8 @@ endif obj-y += nand_partitions.o -obj-$(CONFIG_ARCH_MSM_ARM11) += acpuclock-arm11.o -obj-$(CONFIG_ARCH_MSM_SCORPION) += acpuclock-scorpion.o +obj-$(CONFIG_ARCH_MSM_ARM11) += acpuclock-arm11.o idle.o +obj-$(CONFIG_ARCH_MSM_SCORPION) += acpuclock-scorpion.o idle-v7.o ifdef CONFIG_MSM_VIC obj-y += irq-vic.o diff --git a/arch/arm/mach-msm/idle-v7.S b/arch/arm/mach-msm/idle-v7.S new file mode 100644 index 0000000000000..919e546c70975 --- /dev/null +++ b/arch/arm/mach-msm/idle-v7.S @@ -0,0 +1,230 @@ +/* + * Idle processing for ARMv7-based Qualcomm SoCs. + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. + * + * 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 + +ENTRY(msm_arch_idle) + wfi + bx lr + +ENTRY(msm_pm_collapse) +#if defined(CONFIG_MSM_FIQ_SUPPORT) + cpsid f +#endif + + ldr r0, =saved_state + stmia r0!, {r4-r14} + mrc p15, 0, r1, c1, c0, 0 /* MMU control */ + mrc p15, 0, r2, c2, c0, 0 /* TTBR0 */ + mrc p15, 0, r3, c3, c0, 0 /* dacr */ + mrc p15, 3, r4, c15, c0, 3 /* L2CR1 is the L2 cache control reg 1 */ + mrc p15, 0, r5, c10, c2, 0 /* PRRR */ + mrc p15, 0, r6, c10, c2, 1 /* NMRR */ + mrc p15, 0, r7, c1, c0, 1 /* ACTLR */ + mrc p15, 0, r8, c2, c0, 1 /* TTBR1 */ + mrc p15, 0, r9, c13, c0, 3 /* TPIDRURO */ + mrc p15, 0, ip, c13, c0, 1 /* context ID */ + stmia r0!, {r1-r9, ip} + +#ifdef CONFIG_VFP + VFPFSTMIA r0, r1 /* Save VFP working registers */ + fmrx r1, fpexc + fmrx r2, fpscr + stmia r0!, {r1, r2} /* Save VFP state registers */ +#endif + bl v7_flush_dcache_all + + mrc p15, 0, r1, c1, c0, 0 /* read current CR */ + bic r0, r1, #(1 << 2) /* clear dcache bit */ + bic r0, r0, #(1 << 12) /* clear icache bit */ + mcr p15, 0, r0, c1, c0, 0 /* disable d/i cache */ + + dsb + wfi + + mcr p15, 0, r1, c1, c0, 0 /* restore d/i cache */ + isb + +#if defined(CONFIG_MSM_FIQ_SUPPORT) + cpsie f +#endif + + ldr r0, =saved_state /* restore registers */ + ldmfd r0, {r4-r14} + mov r0, #0 /* return power collapse failed */ + bx lr + +ENTRY(msm_pm_collapse_exit) +#if 0 /* serial debug */ + mov r0, #0x80000016 + mcr p15, 0, r0, c15, c2, 4 + mov r0, #0xA9000000 + add r0, r0, #0x00A00000 /* UART1 */ + /*add r0, r0, #0x00C00000*/ /* UART3 */ + mov r1, #'A' + str r1, [r0, #0x00C] +#endif + + //; REMOVE FOLLOWING THREE INSTRUCTIONS WHEN POWER COLLAPSE IS ENA + //;Make sure the DBGOSLSR[LOCK] bit is cleared to allow access to the debug registers + //; Writing anything but the "secret code" to the DBGOSLAR clears the DBGOSLSR[LOCK] bit + MCR p14, 0, r0, c1, c0, 4 //; WCP14_DBGOSLAR r0 + + + //; Read the DBGPRSR to clear the DBGPRSR[STICKYPD] + //; Any read to DBGPRSR clear the STICKYPD bit + //; ISB guarantees the read completes before attempting to + //; execute a CP14 instruction. + MRC p14, 0, r3, c1, c5, 4 //; RCP14_DBGPRSR r3 + ISB + +#if 0 /* allow jtag reconnect */ +1: + b 1b +#endif + + //; Write L2VR3F1 to make L2 array work properly at 998 on Raptor2 + LDR r1, =0x00212102 //; the l2 timing watch out on P2 + MCR p15, 3, r1, c15, c15, 1 //; WCP15_L2VR3F1 + DSB + ISB + + //; write the value for Icache to run at 998 on Raptor2 + LDR r1, =0x3800E000 + MCR p15, 0, r1, c15, c15, 0 //; WCP15_PVR0F0 + ISB + + //; DCIALL to invalidate L2 cache bank (needs to be run 4 times, once per bank) + //; This must be done early in code (prior to enabling the caches) + MOV r1, #0x2 + MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank D ([15:14] == 2'b00) + ORR r1, r1, #0x00004000 + MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank C ([15:14] == 2'b01) + ADD r1, r1, #0x00004000 + MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank B ([15:14] == 2'b10) + ADD r1, r1, #0x00004000 + MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank A ([15:14] == 2'b11) + + //; DCIALL to invalidate entire D-Cache + MCR p15, 0, r0, c9, c0, 6 //; DCIALL r0 + + //; Initialize the BPCR - setup Global History Mask (GHRM) to all 1's + //; and have all address bits (AM) participate. + //; Different settings can be used to improve performance + MOVW r1, #0x01FF + MOVT r1, #0x01FF + MCR p15, 7, r1, c15, c0, 2 //; WCP15_BPCR + + dsb + isb + + ldr r1, =saved_state_end + ldr r2, =msm_pm_collapse_exit + adr r3, msm_pm_collapse_exit + add r1, r1, r3 + sub r1, r1, r2 +#ifdef CONFIG_VFP + mrc p15, 0, r2, c1, c0, 2 /* Read CP Access Control Register */ + orr r2, r2, #0x00F00000 /* Enable full access for p10,11 */ + mcr p15, 0, r2, c1, c0, 2 /* Write CPACR */ + isb + mov r2, #0x40000000 /* Enable VFP */ + fmxr fpexc, r2 + isb + ldmdb r1!, {r2, r3} /* Read saved VFP state registers */ + sub r1, r1, #32*8 /* Jump to start of vfp regs area */ + VFPFLDMIA r1, r4 /* Restore VFP working registers, + * r1 incremented to end of vfp + * regs area */ + sub r1, r1, #32*8 /* Jump back to start of vfp regs area */ + fmxr fpscr, r3 /* Restore FPSCR */ + fmxr fpexc, r2 /* Restore FPEXC last */ +#endif + ldmdb r1!, {r2-r11} + mcr p15, 0, r4, c3, c0, 0 /* dacr */ + mcr p15, 0, r3, c2, c0, 0 /* TTBR0 */ + mcr p15, 3, r5, c15, c0, 3 /* L2CR1 */ + mcr p15, 0, r6, c10, c2, 0 /* PRRR */ + mcr p15, 0, r7, c10, c2, 1 /* NMRR */ + mcr p15, 0, r8, c1, c0, 1 /* ACTLR */ + mcr p15, 0, r9, c2, c0, 1 /* TTBR1 */ + mcr p15, 0, r10, c13, c0, 3 /* TPIDRURO */ + mcr p15, 0, r11, c13, c0, 1 /* context ID */ + isb + ldmdb r1!, {r4-r14} + /* Add 1:1 map in the PMD to allow smooth switch when turning on MMU */ + and r3, r3, #~0x7F /* mask off lower 7 bits of TTB */ + adr r0, msm_pm_mapped_pa /* get address of the mapped instr */ + lsr r1, r0, #20 /* get the addr range of addr in MB */ + lsl r1, r1, #2 /* multiply by 4 to get to the pg index */ + add r3, r3, r1 /* pgd + pgd_index(addr) */ + ldr r1, [r3] /* save current entry to r1 */ + lsr r0, #20 /* align current addr to 1MB boundary */ + lsl r0, #20 + /* Create new entry for this 1MB page */ + orr r0, r0, #0x4 /* PMD_SECT_BUFFERED */ + orr r0, r0, #0x400 /* PMD_SECT_AP_WRITE */ + orr r0, r0, #0x2 /* PMD_TYPE_SECT|PMD_DOMAIN(DOMAIN_KERNEL) */ + str r0, [r3] /* put new entry into the MMU table */ + mcr p15, 0, r3, c7, c10, 1 /* flush_pmd */ + dsb + isb + mcr p15, 0, r2, c1, c0, 0 /* MMU control */ + isb +msm_pm_mapped_pa: + /* Switch to virtual */ + adr r2, msm_pm_pa_to_va + ldr r0, =msm_pm_pa_to_va + mov pc, r0 +msm_pm_pa_to_va: + sub r0, r0, r2 + /* Restore r1 in MMU table */ + add r3, r3, r0 + str r1, [r3] + mcr p15, 0, r3, c7, c10, 1 /* flush_pmd */ + dsb + isb + mcr p15, 0, r3, c8, c7, 0 /* UTLBIALL */ + mcr p15, 0, r3, c7, c5, 6 /* BPIALL */ + dsb + isb + stmfd sp!, {lr} + bl v7_flush_kern_cache_all + ldmfd sp!, {lr} + mov r0, #1 + bx lr + nop + nop + nop + nop + nop +1: b 1b + + + .data + +saved_state: + .space 4 * 11 /* r4-14 */ + .space 4 * 10 /* cp15 */ +#ifdef CONFIG_VFP + .space 8 * 32 /* VFP working registers */ + .space 4 * 2 /* VFP state registers */ +#endif +saved_state_end: + diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c index 81191cb98f66f..34bb7d6c6108f 100644 --- a/arch/arm/mach-msm/irq.c +++ b/arch/arm/mach-msm/irq.c @@ -111,6 +111,15 @@ static struct { } msm_irq_shadow_reg[2]; static uint32_t msm_irq_idle_disable[2]; +#ifndef CONFIG_ARCH_MSM_SCORPION +#define INT_INFO_SMSM_ID SMEM_SMSM_INT_INFO +struct smsm_interrupt_info *smsm_int_info; +#else +#define INT_INFO_SMSM_ID SMEM_APPS_DEM_SLAVE_DATA +struct msm_dem_slave_data *smsm_int_info; +#endif + + #define SMSM_FAKE_IRQ (0xff) static uint8_t msm_irq_to_smsm[NR_MSM_IRQS + NR_SIRC_IRQS] = { [INT_MDDI_EXT] = 1, @@ -161,6 +170,10 @@ static uint8_t msm_irq_to_smsm[NR_MSM_IRQS + NR_SIRC_IRQS] = { [INT_A9_M2A_5] = SMSM_FAKE_IRQ, [INT_GP_TIMER_EXP] = SMSM_FAKE_IRQ, [INT_DEBUG_TIMER_EXP] = SMSM_FAKE_IRQ, +#ifdef CONFIG_ARCH_MSM_SCORPION + [INT_SIRC_0] = SMSM_FAKE_IRQ, + [INT_SIRC_1] = SMSM_FAKE_IRQ, +#endif }; static void msm_irq_ack(struct irq_data *d) @@ -272,7 +285,8 @@ int msm_irq_idle_sleep_allowed(void) if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_REQUEST) printk(KERN_INFO "msm_irq_idle_sleep_allowed: disable %x %x\n", msm_irq_idle_disable[0], msm_irq_idle_disable[1]); - return !(msm_irq_idle_disable[0] || msm_irq_idle_disable[1]); + return !(msm_irq_idle_disable[0] || msm_irq_idle_disable[1] || + !smsm_int_info); } /* If arm9_wake is set: pass control to the other core. @@ -280,12 +294,10 @@ int msm_irq_idle_sleep_allowed(void) */ void msm_irq_enter_sleep1(bool arm9_wake, int from_idle) { - struct smsm_interrupt_info int_info; - if (arm9_wake) { - int_info.aArm_en_mask = msm_irq_smsm_wake_enable[!from_idle]; - int_info.aArm_interrupts_pending = 0; - smsm_set_interrupt_info(&int_info); - } + if (!arm9_wake || !smsm_int_info) + return; + smsm_int_info->interrupt_mask = msm_irq_smsm_wake_enable[!from_idle]; + smsm_int_info->pending_interrupts = 0; } int msm_irq_enter_sleep2(bool arm9_wake, int from_idle) @@ -342,7 +354,6 @@ int msm_irq_enter_sleep2(bool arm9_wake, int from_idle) void msm_irq_exit_sleep1(void) { - struct smsm_interrupt_info *int_info; int i; msm_irq_ack(irq_get_irq_data(INT_A9_M2A_6)); @@ -353,16 +364,15 @@ void msm_irq_exit_sleep1(void) writel(msm_irq_shadow_reg[i].int_select, VIC_INT_SELECT0 + i * 4); } writel(3, VIC_INT_MASTEREN); - int_info = smem_alloc(SMEM_SMSM_INT_INFO, sizeof(*int_info)); - if (int_info == NULL) { + if (!smsm_int_info) { printk(KERN_ERR "msm_irq_exit_sleep \n"); return; } if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) printk(KERN_INFO "msm_irq_exit_sleep1 %x %x %x now %x %x\n", - int_info->aArm_en_mask, - int_info->aArm_interrupts_pending, - int_info->aArm_wakeup_reason, + smsm_int_info->interrupt_mask, + smsm_int_info->pending_interrupts, + smsm_int_info->wakeup_reason, readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1)); } @@ -370,19 +380,18 @@ void msm_irq_exit_sleep2(void) { int i; uint32_t pending; - struct smsm_interrupt_info *int_info; - int_info = smem_alloc(SMEM_SMSM_INT_INFO, sizeof(*int_info)); - if (int_info == NULL) { + + if (!smsm_int_info) { printk(KERN_ERR "msm_irq_exit_sleep \n"); return; } if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) printk(KERN_INFO "msm_irq_exit_sleep2 %x %x %x now %x %x\n", - int_info->aArm_en_mask, - int_info->aArm_interrupts_pending, - int_info->aArm_wakeup_reason, + smsm_int_info->interrupt_mask, + smsm_int_info->pending_interrupts, + smsm_int_info->wakeup_reason, readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1)); - pending = int_info->aArm_interrupts_pending; + pending = smsm_int_info->pending_interrupts; for (i = 0; pending && i < ARRAY_SIZE(msm_irq_to_smsm); i++) { unsigned reg_offset = (i & 32) ? 4 : 0; uint32_t reg_mask = 1UL << (i & 31); @@ -414,17 +423,15 @@ void msm_irq_exit_sleep2(void) void msm_irq_exit_sleep3(void) { - struct smsm_interrupt_info *int_info; - int_info = smem_alloc(SMEM_SMSM_INT_INFO, sizeof(*int_info)); - if (int_info == NULL) { + if (!smsm_int_info) { printk(KERN_ERR "msm_irq_exit_sleep \n"); return; } if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) printk(KERN_INFO "msm_irq_exit_sleep3 %x %x %x now %x %x " - "state %x\n", int_info->aArm_en_mask, - int_info->aArm_interrupts_pending, - int_info->aArm_wakeup_reason, readl(VIC_IRQ_STATUS0), + "state %x\n", smsm_int_info->interrupt_mask, + smsm_int_info->pending_interrupts, + smsm_int_info->wakeup_reason, readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1), smsm_get_state(SMSM_STATE_MODEM)); } @@ -474,6 +481,15 @@ void __init msm_init_irq(void) msm_init_sirc(); } +static int __init msm_init_irq_late(void) +{ + smsm_int_info = smem_alloc(INT_INFO_SMSM_ID, sizeof(*smsm_int_info)); + if (!smsm_int_info) + pr_err("set_wakeup_mask NO INT_INFO (%d)\n", INT_INFO_SMSM_ID); + return 0; +} +late_initcall(msm_init_irq_late); + #if defined(CONFIG_MSM_FIQ_SUPPORT) void msm_trigger_irq(int irq) { diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index 7b452ce5fac25..eac2e879fc299 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -69,6 +69,47 @@ module_param_named(idle_spin_time, msm_pm_idle_spin_time, int, S_IRUGO | S_IWUSR #define A11S_STANDBY_CTL (MSM_CSR_BASE + 0x108) #define A11RAMBACKBIAS (MSM_CSR_BASE + 0x508) + +#define DEM_MASTER_BITS_PER_CPU 5 + +/* Power Master State Bits - Per CPU */ +#define DEM_MASTER_SMSM_RUN \ + (0x01UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS)) +#define DEM_MASTER_SMSM_RSA \ + (0x02UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS)) +#define DEM_MASTER_SMSM_PWRC_EARLY_EXIT \ + (0x04UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS)) +#define DEM_MASTER_SMSM_SLEEP_EXIT \ + (0x08UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS)) +#define DEM_MASTER_SMSM_READY \ + (0x10UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS)) + +/* Power Slave State Bits */ +#define DEM_SLAVE_SMSM_RUN (0x0001) +#define DEM_SLAVE_SMSM_PWRC (0x0002) +#define DEM_SLAVE_SMSM_PWRC_DELAY (0x0004) +#define DEM_SLAVE_SMSM_PWRC_EARLY_EXIT (0x0008) +#define DEM_SLAVE_SMSM_WFPI (0x0010) +#define DEM_SLAVE_SMSM_SLEEP (0x0020) +#define DEM_SLAVE_SMSM_SLEEP_EXIT (0x0040) +#define DEM_SLAVE_SMSM_MSGS_REDUCED (0x0080) +#define DEM_SLAVE_SMSM_RESET (0x0100) +#define DEM_SLAVE_SMSM_PWRC_SUSPEND (0x0200) + +#ifndef CONFIG_ARCH_MSM_SCORPION +#define PM_SMSM_WRITE_STATE SMSM_STATE_APPS +#define PM_SMSM_READ_STATE SMSM_STATE_MODEM + +#define PM_SMSM_WRITE_RUN SMSM_RUN +#define PM_SMSM_READ_RUN SMSM_RUN +#else +#define PM_SMSM_WRITE_STATE SMSM_STATE_APPS_DEM +#define PM_SMSM_READ_STATE SMSM_STATE_POWER_MASTER_DEM + +#define PM_SMSM_WRITE_RUN DEM_SLAVE_SMSM_RUN +#define PM_SMSM_READ_RUN DEM_MASTER_SMSM_RUN +#endif + int msm_pm_collapse(void); int msm_arch_idle(void); void msm_pm_collapse_exit(void); @@ -139,7 +180,7 @@ msm_pm_wait_state(uint32_t wait_state_all_set, uint32_t wait_state_all_clear, uint32_t state; for (i = 0; i < 100000; i++) { - state = smsm_get_state(SMSM_STATE_MODEM); + state = smsm_get_state(PM_SMSM_READ_STATE); if (((state & wait_state_all_set) == wait_state_all_set) && ((~state & wait_state_all_clear) == wait_state_all_clear) && (wait_state_any_set == 0 || (state & wait_state_any_set) || @@ -179,6 +220,7 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) printk(KERN_INFO "msm_sleep(): mode %d delay %u idle %d\n", sleep_mode, sleep_delay, from_idle); +#ifndef CONFIG_ARCH_MSM_SCORPION switch (sleep_mode) { case MSM_PM_SLEEP_MODE_POWER_COLLAPSE: enter_state = SMSM_PWRC; @@ -201,6 +243,30 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) enter_state = 0; exit_state = 0; } +#else + switch (sleep_mode) { + case MSM_PM_SLEEP_MODE_POWER_COLLAPSE: + enter_state = DEM_SLAVE_SMSM_PWRC; + enter_wait_set = DEM_MASTER_SMSM_RSA; + exit_state = DEM_SLAVE_SMSM_WFPI; + exit_wait_set = DEM_MASTER_SMSM_RUN; + break; + case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND: + enter_state = DEM_SLAVE_SMSM_PWRC_SUSPEND; + enter_wait_set = DEM_MASTER_SMSM_RSA; + exit_state = DEM_SLAVE_SMSM_WFPI; + exit_wait_set = DEM_MASTER_SMSM_RUN; + break; + case MSM_PM_SLEEP_MODE_APPS_SLEEP: + enter_state = DEM_SLAVE_SMSM_SLEEP; + exit_state = 0; + exit_wait_set = DEM_MASTER_SMSM_SLEEP_EXIT; + break; + default: + enter_state = 0; + exit_state = 0; + } +#endif clk_enter_sleep(from_idle); msm_irq_enter_sleep1(!!enter_state, from_idle); @@ -209,8 +275,13 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) if (enter_state) { if (sleep_delay == 0 && sleep_mode >= MSM_PM_SLEEP_MODE_APPS_SLEEP) sleep_delay = 192000*5; /* APPS_SLEEP does not allow infinite timeout */ - smsm_set_sleep_duration(sleep_delay); - ret = smsm_change_state(SMSM_STATE_APPS, SMSM_RUN, enter_state); + ret = smsm_set_sleep_duration(sleep_delay); + if (ret) { + printk(KERN_ERR "msm_sleep(): smsm_set_sleep_duration %x failed\n", enter_state); + enter_state = 0; + exit_state = 0; + } + ret = smsm_change_state(PM_SMSM_WRITE_STATE, PM_SMSM_WRITE_RUN, enter_state); if (ret) { printk(KERN_ERR "msm_sleep(): smsm_change_state %x failed\n", enter_state); enter_state = 0; @@ -218,7 +289,7 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) } ret = msm_pm_wait_state(enter_wait_set, enter_wait_clear, 0, 0); if (ret) { - printk(KERN_INFO "msm_sleep(): msm_pm_wait_state failed, %x\n", smsm_get_state(SMSM_STATE_MODEM)); + printk(KERN_INFO "msm_sleep(): msm_pm_wait_state failed, %x\n", smsm_get_state(PM_SMSM_READ_STATE)); goto enter_failed; } } @@ -230,13 +301,15 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) writel(1, A11S_PWRDOWN); writel(0, A11S_STANDBY_CTL); +#ifndef CONFIG_ARCH_MSM_SCORPION writel(0, A11RAMBACKBIAS); +#endif if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) printk(KERN_INFO "msm_sleep(): enter " "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, " "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN), - readl(A11S_PWRDOWN), smsm_get_state(SMSM_STATE_MODEM)); + readl(A11S_PWRDOWN), smsm_get_state(PM_SMSM_READ_STATE)); } if (sleep_mode <= MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT) { @@ -263,6 +336,7 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) msm_pm_reset_vector[1] = saved_vector[1]; if (collapsed) { cpu_init(); + __asm__("cpsie a"); msm_fiq_exit_sleep(); local_fiq_enable(); rv = 0; @@ -289,32 +363,32 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) printk(KERN_INFO "msm_sleep(): exit A11S_CLK_SLEEP_EN %x, " "A11S_PWRDOWN %x, smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN), readl(A11S_PWRDOWN), - smsm_get_state(SMSM_STATE_MODEM)); + smsm_get_state(PM_SMSM_READ_STATE)); ramp_down_failed: msm_irq_exit_sleep1(); enter_failed: if (enter_state) { writel(0x00, A11S_CLK_SLEEP_EN); writel(0, A11S_PWRDOWN); - smsm_change_state(SMSM_STATE_APPS, enter_state, exit_state); + smsm_change_state(PM_SMSM_WRITE_STATE, enter_state, exit_state); msm_pm_wait_state(exit_wait_set, exit_wait_clear, 0, 0); if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) printk(KERN_INFO "msm_sleep(): sleep exit " "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, " "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN), - readl(A11S_PWRDOWN), smsm_get_state(SMSM_STATE_MODEM)); + readl(A11S_PWRDOWN), smsm_get_state(PM_SMSM_READ_STATE)); if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) smsm_print_sleep_info(); } msm_irq_exit_sleep2(); if (enter_state) { - smsm_change_state(SMSM_STATE_APPS, exit_state, SMSM_RUN); - msm_pm_wait_state(SMSM_RUN, 0, 0, 0); + smsm_change_state(PM_SMSM_WRITE_STATE, exit_state, PM_SMSM_WRITE_RUN); + msm_pm_wait_state(PM_SMSM_READ_RUN, 0, 0, 0); if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) printk(KERN_INFO "msm_sleep(): sleep exit " "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, " "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN), - readl(A11S_PWRDOWN), smsm_get_state(SMSM_STATE_MODEM)); + readl(A11S_PWRDOWN), smsm_get_state(PM_SMSM_READ_STATE)); } msm_irq_exit_sleep3(); msm_gpio_exit_sleep(); From e380af808472a94df5e5e08df487df3808b584b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Sun, 20 Sep 2009 17:42:34 -0700 Subject: [PATCH 0415/2556] [ARM] msm: Remove i2c interrupt from wakeup list. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The other core uses the i2c controller while we are in power-collapse which makes this interrupt unusable as a wakeup source. Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c index 34bb7d6c6108f..b9af20290e41d 100644 --- a/arch/arm/mach-msm/irq.c +++ b/arch/arm/mach-msm/irq.c @@ -127,7 +127,7 @@ static uint8_t msm_irq_to_smsm[NR_MSM_IRQS + NR_SIRC_IRQS] = { [INT_MDDI_CLIENT] = 3, [INT_USB_OTG] = 4, - [INT_PWB_I2C] = 5, + /* [INT_PWB_I2C] = 5 -- not usable */ [INT_SDC1_0] = 6, [INT_SDC1_1] = 7, [INT_SDC2_0] = 8, From 62bc0316dd339f1f287c13735065ea1e489f4eb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 19 Aug 2009 21:14:13 -0700 Subject: [PATCH 0416/2556] [ARM] msm: irq: Add INT_ADSP_A11 to the list of fake wakeup interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/irq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c index b9af20290e41d..9f13140f88ab2 100644 --- a/arch/arm/mach-msm/irq.c +++ b/arch/arm/mach-msm/irq.c @@ -170,6 +170,7 @@ static uint8_t msm_irq_to_smsm[NR_MSM_IRQS + NR_SIRC_IRQS] = { [INT_A9_M2A_5] = SMSM_FAKE_IRQ, [INT_GP_TIMER_EXP] = SMSM_FAKE_IRQ, [INT_DEBUG_TIMER_EXP] = SMSM_FAKE_IRQ, + [INT_ADSP_A11] = SMSM_FAKE_IRQ, #ifdef CONFIG_ARCH_MSM_SCORPION [INT_SIRC_0] = SMSM_FAKE_IRQ, [INT_SIRC_1] = SMSM_FAKE_IRQ, From fc395b1a3ad5c5ae8a9db4de4d32ab3e215c10c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Sun, 27 Sep 2009 19:10:31 -0700 Subject: [PATCH 0417/2556] [ARM] msm: irq: Clear i2c interrupt after sleep. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/irq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c index 9f13140f88ab2..9b24e4a3658a2 100644 --- a/arch/arm/mach-msm/irq.c +++ b/arch/arm/mach-msm/irq.c @@ -358,6 +358,7 @@ void msm_irq_exit_sleep1(void) int i; msm_irq_ack(irq_get_irq_data(INT_A9_M2A_6)); + msm_irq_ack(irq_get_irq_data(INT_PWB_I2C)); for (i = 0; i < 2; i++) { writel(msm_irq_shadow_reg[i].int_type, VIC_INT_TYPE0 + i * 4); writel(msm_irq_shadow_reg[i].int_polarity, VIC_INT_POLARITY0 + i * 4); From 67554d474109a4902be7cab5a77ace7f5022af1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 19 Oct 2009 16:37:17 -0700 Subject: [PATCH 0418/2556] [ARM] msm: irq: Clear wakeup interrupt before sleep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This works around a problem where the device fails to power wakeup at all if the modem tries to initiate an early exit when the wakeup interrupt is already pending. Another bug in the modem code causes triggers a spurious wakeup interrupt if the apps processor initiates an early exit by setting DEM_SLAVE_SMSM_WFPI. Change-Id: Id6db67f86a532a4c55cb19719d68329651dfca1e Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/irq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c index 9b24e4a3658a2..69914557194b6 100644 --- a/arch/arm/mach-msm/irq.c +++ b/arch/arm/mach-msm/irq.c @@ -345,6 +345,7 @@ int msm_irq_enter_sleep2(bool arm9_wake, int from_idle) if (arm9_wake) { msm_irq_set_type(irq_get_irq_data(INT_A9_M2A_6), IRQF_TRIGGER_RISING); + msm_irq_ack(irq_get_irq_data(INT_A9_M2A_6)); writel(1U << INT_A9_M2A_6, VIC_INT_ENSET0); } else { writel(msm_irq_shadow_reg[0].int_en[1], VIC_INT_ENSET0); From 09333883afd07141ceec7534970914b6daff1197 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 14 Jul 2009 13:31:19 -0700 Subject: [PATCH 0419/2556] [ARM] msm: Add SPI device and resources definitions Change-Id: Ie611371512e1f721d8fb431f133266dac8bc49c5 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/devices.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h index 275a926d3ba25..0559a1f7e53f2 100644 --- a/arch/arm/mach-msm/devices.h +++ b/arch/arm/mach-msm/devices.h @@ -47,6 +47,7 @@ extern struct platform_device msm_device_mddi0; extern struct platform_device msm_device_mddi1; extern struct platform_device msm_device_mdp; extern struct platform_device msm_device_touchscreen; +extern struct platform_device msm_device_spi; extern struct clk msm_clocks_7x01a[]; extern unsigned msm_num_clocks_7x01a; From bbbe3e242e007f41fb7508f0d8f013a944c93330 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 21 Jul 2009 15:00:49 -0700 Subject: [PATCH 0420/2556] [ARM] msm: rpc: Match qsd8x50 rpc behavior to that of msm7k's 6350. This will be absoleted by putting AMSS features into the Kconfig. Signed-off-by: Dima Zavin --- arch/arm/mach-msm/include/mach/msm_rpcrouter.h | 2 +- arch/arm/mach-msm/smd_rpcrouter.c | 6 +++--- arch/arm/mach-msm/smd_rpcrouter_device.c | 4 ++-- arch/arm/mach-msm/smd_rpcrouter_servers.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/msm_rpcrouter.h b/arch/arm/mach-msm/include/mach/msm_rpcrouter.h index 5e517055fe57f..d46deba8a00c0 100644 --- a/arch/arm/mach-msm/include/mach/msm_rpcrouter.h +++ b/arch/arm/mach-msm/include/mach/msm_rpcrouter.h @@ -22,7 +22,7 @@ #include #include -#if CONFIG_MSM_AMSS_VERSION >= 6350 +#if (CONFIG_MSM_AMSS_VERSION >= 6350) || defined(CONFIG_ARCH_QSD8X50) /* RPC API version structure * Version bit 31 : 1->hashkey versioning, * 0->major-minor (backward compatible) versioning diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c index 67bde4b60db72..fba8075dc3702 100644 --- a/arch/arm/mach-msm/smd_rpcrouter.c +++ b/arch/arm/mach-msm/smd_rpcrouter.c @@ -776,7 +776,7 @@ int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count) return -ENOTCONN; } -#if CONFIG_MSM_AMSS_VERSION >= 6350 +#if CONFIG_MSM_AMSS_VERSION >= 6350 || defined(CONFIG_ARCH_QSD8X50) if ((ept->dst_prog != rq->prog) || !msm_rpc_is_compatible_version( be32_to_cpu(ept->dst_vers), @@ -1113,7 +1113,7 @@ int __msm_rpc_read(struct msm_rpc_endpoint *ept, return rc; } -#if CONFIG_MSM_AMSS_VERSION >= 6350 +#if CONFIG_MSM_AMSS_VERSION >= 6350 || defined(CONFIG_ARCH_QSD8X50) int msm_rpc_is_compatible_version(uint32_t server_version, uint32_t client_version) { @@ -1159,7 +1159,7 @@ struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned struct msm_rpc_endpoint *ept; struct rr_server *server; -#if CONFIG_MSM_AMSS_VERSION >= 6350 +#if CONFIG_MSM_AMSS_VERSION >= 6350 || defined(CONFIG_ARCH_QSD8X50) if (!(vers & RPC_VERSION_MODE_MASK)) { uint32_t found_vers; if (msm_rpc_get_compatible_server(prog, vers, &found_vers) < 0) diff --git a/arch/arm/mach-msm/smd_rpcrouter_device.c b/arch/arm/mach-msm/smd_rpcrouter_device.c index 05a122c88d060..f88bf00495105 100644 --- a/arch/arm/mach-msm/smd_rpcrouter_device.c +++ b/arch/arm/mach-msm/smd_rpcrouter_device.c @@ -247,7 +247,7 @@ int msm_rpcrouter_create_server_cdev(struct rr_server *server) return -ENOBUFS; } -#if CONFIG_MSM_AMSS_VERSION >= 6350 +#if CONFIG_MSM_AMSS_VERSION >= 6350 || defined(CONFIG_ARCH_QSD8X50) /* Servers with bit 31 set are remote msm servers with hashkey version. * Servers with bit 31 not set are remote msm servers with * backwards compatible version type in which case the minor number @@ -297,7 +297,7 @@ int msm_rpcrouter_create_server_pdev(struct rr_server *server) { sprintf(server->pdev_name, "rs%.8x:%.8x", server->prog, -#if CONFIG_MSM_AMSS_VERSION >= 6350 +#if CONFIG_MSM_AMSS_VERSION >= 6350 || defined(CONFIG_ARCH_QSD8X50) (server->vers & RPC_VERSION_MODE_MASK) ? server->vers : (server->vers & RPC_VERSION_MAJOR_MASK)); #else diff --git a/arch/arm/mach-msm/smd_rpcrouter_servers.c b/arch/arm/mach-msm/smd_rpcrouter_servers.c index d340bfa12c4fd..fe6e3c91e8495 100644 --- a/arch/arm/mach-msm/smd_rpcrouter_servers.c +++ b/arch/arm/mach-msm/smd_rpcrouter_servers.c @@ -60,7 +60,7 @@ static struct msm_rpc_server *rpc_server_find(uint32_t prog, uint32_t vers) mutex_lock(&rpc_server_list_lock); list_for_each_entry(server, &rpc_server_list, list) { if ((server->prog == prog) && -#if CONFIG_MSM_AMSS_VERSION >= 6350 +#if CONFIG_MSM_AMSS_VERSION >= 6350 || defined(CONFIG_ARCH_QSD8X50) msm_rpc_is_compatible_version(server->vers, vers)) { #else server->vers == vers) { From ae2965f6f9d525e4ce8af377b8c7ed3d5fbd7494 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 24 Aug 2009 00:06:17 -0700 Subject: [PATCH 0421/2556] [ARM] msm: scorpion does speculative fetches Signed-off-by: Dima Zavin --- arch/arm/mach-msm/include/mach/memory.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h index dffe9bb01862e..9ddbdd7930335 100644 --- a/arch/arm/mach-msm/include/mach/memory.h +++ b/arch/arm/mach-msm/include/mach/memory.h @@ -33,5 +33,9 @@ #define CONSISTENT_DMA_SIZE (4*SZ_1M) +#ifdef CONFIG_ARCH_MSM_SCORPION +#define arch_has_speculative_dfetch() 1 +#endif + #endif From 3e750bf18469120bc0f6c85eb6b0b84f74d97ad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 31 Jul 2009 14:20:32 -0700 Subject: [PATCH 0422/2556] [ARM] msm: pm: Run full cpu init code from bootloader on resume. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes random crashes after returning from power collapse. Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/arch-init-scorpion.S | 540 +++++++++++++++++++++++++ arch/arm/mach-msm/idle-v7.S | 37 +- 3 files changed, 544 insertions(+), 34 deletions(-) create mode 100644 arch/arm/mach-msm/arch-init-scorpion.S diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 751019f217f9d..7dd6b7802134f 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -8,6 +8,7 @@ obj-y += nand_partitions.o obj-$(CONFIG_ARCH_MSM_ARM11) += acpuclock-arm11.o idle.o obj-$(CONFIG_ARCH_MSM_SCORPION) += acpuclock-scorpion.o idle-v7.o +obj-$(CONFIG_ARCH_MSM_SCORPION) += arch-init-scorpion.o ifdef CONFIG_MSM_VIC obj-y += irq-vic.o diff --git a/arch/arm/mach-msm/arch-init-scorpion.S b/arch/arm/mach-msm/arch-init-scorpion.S new file mode 100644 index 0000000000000..07a99ac221cb8 --- /dev/null +++ b/arch/arm/mach-msm/arch-init-scorpion.S @@ -0,0 +1,540 @@ +/* + * Copyright (c) 2008, QUALCOMM Incorporated. + * Copyright (c) 2008, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +/* TODO: + * - style cleanup + * - do we need to do *all* of this at boot? + */ + +.text +.code 32 + +#define DSB .byte 0x4f, 0xf0, 0x7f, 0xf5 +#define ISB .byte 0x6f, 0xf0, 0x7f, 0xf5 + +/* + ; LVT Ring Osc counter + ; used to determine sense amp settings + ; Clobbers registers r0, r4, r5, r6, r7, r9, r10, r11 +*/ +.equ CLK_CTL_BASE, 0xA8600000 +.equ A_GLBL_CLK_ENA, 0x0000 +.equ A_PRPH_WEB_NS_REG,0x0080 +.equ A_MSM_CLK_RINGOSC,0x00D0 +.equ A_TCXO_CNT, 0x00D4 +.equ A_TCXO_CNT_DONE, 0x00D8 +.equ A_RINGOSC_CNT, 0x00DC +.equ A_MISC_CLK_CTL, 0x0108 +.equ CLK_TEST, 0xA8600114 +.equ SPSS_CSR_BASE, 0xAC100000 +.equ A_SCRINGOSC, 0x0510 + +//;; Number of TCXO cycles to count ring oscillations +.equ TCXO_CNT_VAL, 0x100 + +SET_SA: + //; clear CLK_TEST[19] (RING_OSC_DBG_SEL) to count ring osc + LDR r5, =CLK_TEST + LDR r4, [r5] + BIC r4, r4, #0x80000 + STR r4, [r5] + + //; setup memory pointers + LDR r5, =CLK_CTL_BASE + LDR r6, =SPSS_CSR_BASE + + //; PRPH_WEB_NS_REG = 0x00000A00 - enable root and crc + MOV r7, #0x0A00 + STR r7,[r5,#A_PRPH_WEB_NS_REG] + + //; MISC_CLK_CTL = 0x0 to disable tcxo4cnt, ringosccnt + MOV r0, #0 + STR r0,[r5,#A_MISC_CLK_CTL] + DSB + + //;; MSM_CLK_RINGOSC = 0x00000A0E + //;; the E selects Scorpion LVT RO(or DS) output, + //;; the A enables RO CRC and CXC + LDR r7, =0x0A0E + STR r7,[r5,#A_MSM_CLK_RINGOSC] + + MOV r10, #0x1 //; SCRINGOSC[1:0] = 01 + MOV r11, #TCXO_CNT_VAL + + //;; initialize r9 - counts the 4 oscillators + MOV r9,#0x0 + ADR r4,RO_CNT + +ACQUIRE_FREQ: + //; SCRINGOSC[1:0] is set by r10, [13:11][10:8] = 0..7 + //;MOV r7, r10 + BIC r7, r10, #0x3F00 + //;BFI r7, r9, #8, #3 + ORR r7, r7, r9, LSL #8 + //;BFI r7, r9, #11, #3 + ORR r7, r7, r9, LSL #11 + STR r7,[r6,#A_SCRINGOSC] + + STR r11,[r5,#A_TCXO_CNT] + DSB //; Make sure the countdown value gets written before the ROs are enabled + + //; MISC_CLK_CTL = 0x3 to enable tcxo4cnt, ringosccnt + MOV r7,#0x3 + STR r7,[r5,#A_MISC_CLK_CTL] + + //;; According to Raptor HDD, to read TCXO_CNT_DONE, use address for TCXO_CNT on pass1. + //;; Then read RINGOSC_CNT and store to debug_mem table +WAIT_TCXO_CNT_DONE: + LDR r7,[r5,#A_TCXO_CNT_DONE] + CMP r7,#0x1 + BNE WAIT_TCXO_CNT_DONE + LDR r7,[r5,#A_RINGOSC_CNT] + STR r7,[r4],#0x04 //; post-indexed update r4 to next empty table entry + + //; MISC_CLK_CTL = 0x0 to disable tcxo4cnt, ringosccnt + STR r0,[r5,#A_MISC_CLK_CTL] + DSB + + //;; move to next oscillator - r9 counts oscillators 0..7 + ADD r9,r9,#1 + CMP r9,#0x4 + BLT ACQUIRE_FREQ + + //; average the 4 counts + ADR r4,RO_CNT + LDR r5, [r4], #0x4 + LDR r6, [r4], #0x4 + ADD r5, r5, r6 + LDR r6, [r4], #0x4 + ADD r5, r5, r6 + LDR r6, [r4], #0x4 + ADD r5, r5, r6 + MOV r5, r5, LSR #2 + +.equ SLOW_THRESHOLD, 0x0E10 +.equ NOM_THRESHOLD, 0x123A + + //; compare average to slow part threshold + LDR r4, =SLOW_THRESHOLD + CMP r5, r4 + ADRLT r4, SLOW_SA + BLT WRITE_SA + + //; compare average to nominal part threshold + LDR r4, =NOM_THRESHOLD + CMP r5, r4 + ADRLT r4, NOM_SA + BLT WRITE_SA + + //; must be fast part + ADR r4, FAST_SA + B WRITE_SA + + +SLOW_SA: + .word 0x38001C00 //; PVR0F0 + .word 0x1C000000 //; PVR2F0 + .word 0x00212102 //; L2VR3F1 + +NOM_SA: + .word 0x38001C00 //; PVR0F0 + .word 0x1C000000 //; PVR2F0 + .word 0x00212102 //; L2VR3F1 + +FAST_SA: + .word 0x38003C00 //; PVR0F0 + .word 0x1C000000 //; PVR2F0 + .word 0x00212102 //; L2VR3F1 + +RO_CNT: + .word 0x0 + .word 0x0 + .word 0x0 + .word 0x0 + +WRITE_SA: + //; write the sense amp settings + + //; PVR0F0[15:10] = I$ + LDR r5, [r4], #4 + //;WCP15_PVR0F0 r5 + MCR p15,0x0,r5,c15,c15,0 + + //; PVR2F0[31:26] = D$ + LDR r5, [r4], #4 + //;WCP15_PVR2F0 r5 + MCR p15,0x2,r5,c15,c15,0 + + //; L2VR3F1[23:0] + LDR r5, [r4], #4 + //;WCP15_L2VR3F1 r5 + MCR p15,0x3,r5,c15,c15,1 + + ISB + + BX lr + +.globl __cpu_early_init +__cpu_early_init: + //; Zero out r0 for use throughout this code. All other GPRs + //; (r1-r3) are set throughout this code to help establish + //; a consistent startup state for any code that follows. + //; Users should add code at the end of this routine to establish + //; their own stack address (r13), add translation page tables, enable + //; the caches, etc. + MOV r0, #0x0 + + //; Write L2VR3F1 to make L2 array work properly at 998 on Raptor2 + LDR r1, =0x00212102 //; the l2 timing watch out on P2 + MCR p15, 3, r1, c15, c15, 1 //; WCP15_L2VR3F1 + DSB + ISB + + //; write the value for Icache to run at 998 on Raptor2 + LDR r1, =0x3800E000 + MCR p15, 0, r1, c15, c15, 0 //; WCP15_PVR0F0 + ISB + + //; DCIALL to invalidate L2 cache bank (needs to be run 4 times, once per bank) + //; This must be done early in code (prior to enabling the caches) + MOV r1, #0x2 + MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank D ([15:14] == 2'b00) + ORR r1, r1, #0x00004000 + MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank C ([15:14] == 2'b01) + ADD r1, r1, #0x00004000 + MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank B ([15:14] == 2'b10) + ADD r1, r1, #0x00004000 + MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank A ([15:14] == 2'b11) + + //; Initialize the BPCR - setup Global History Mask (GHRM) to all 1's + //; and have all address bits (AM) participate. + //; Different settings can be used to improve performance + .word 0xe30011ff // MOVW r1, #0x01FF + .word 0xe34011ff // MOVT r1, #0x01FF + MCR p15, 7, r1, c15, c0, 2 //; WCP15_BPCR + + + //; Initialize all I$ Victim Registers to 0 for startup + MCR p15, 0, r0, c9, c1, 0 //; WCP15_ICVIC0 r0 + MCR p15, 0, r0, c9, c1, 1 //; WCP15_ICVIC1 r0 + MCR p15, 0, r0, c9, c1, 2 //; WCP15_ICVIC2 r0 + MCR p15, 0, r0, c9, c1, 3 //; WCP15_ICVIC3 r0 + MCR p15, 0, r0, c9, c1, 4 //; WCP15_ICVIC4 r0 + MCR p15, 0, r0, c9, c1, 5 //; WCP15_ICVIC5 r0 + MCR p15, 0, r0, c9, c1, 6 //; WCP15_ICVIC5 r0 + MCR p15, 0, r0, c9, c1, 7 //; WCP15_ICVIC7 r0 + + //; Initialize all I$ Locked Victim Registers (Unlocked Floors) to 0 + MCR p15, 1, r0, c9, c1, 0 //; WCP15_ICFLOOR0 r0 + MCR p15, 1, r0, c9, c1, 1 //; WCP15_ICFLOOR1 r0 + MCR p15, 1, r0, c9, c1, 2 //; WCP15_ICFLOOR2 r0 + MCR p15, 1, r0, c9, c1, 3 //; WCP15_ICFLOOR3 r0 + MCR p15, 1, r0, c9, c1, 4 //; WCP15_ICFLOOR4 r0 + MCR p15, 1, r0, c9, c1, 5 //; WCP15_ICFLOOR5 r0 + MCR p15, 1, r0, c9, c1, 6 //; WCP15_ICFLOOR6 r0 + MCR p15, 1, r0, c9, c1, 7 //; WCP15_ICFLOOR7 r0 + + //; Initialize all D$ Victim Registers to 0 + MCR p15, 2, r0, c9, c1, 0 //; WP15_DCVIC0 r0 + MCR p15, 2, r0, c9, c1, 1 //; WP15_DCVIC1 r0 + MCR p15, 2, r0, c9, c1, 2 //; WP15_DCVIC2 r0 + MCR p15, 2, r0, c9, c1, 3 //; WP15_DCVIC3 r0 + MCR p15, 2, r0, c9, c1, 4 //; WP15_DCVIC4 r0 + MCR p15, 2, r0, c9, c1, 5 //; WP15_DCVIC5 r0 + MCR p15, 2, r0, c9, c1, 6 //; WP15_DCVIC6 r0 + MCR p15, 2, r0, c9, c1, 7 //; WP15_DCVIC7 r0 + + //; Initialize all D$ Locked VDCtim Registers (Unlocked Floors) to 0 + MCR p15, 3, r0, c9, c1, 0 //; WCP15_DCFLOOR0 r0 + MCR p15, 3, r0, c9, c1, 1 //; WCP15_DCFLOOR1 r0 + MCR p15, 3, r0, c9, c1, 2 //; WCP15_DCFLOOR2 r0 + MCR p15, 3, r0, c9, c1, 3 //; WCP15_DCFLOOR3 r0 + MCR p15, 3, r0, c9, c1, 4 //; WCP15_DCFLOOR4 r0 + MCR p15, 3, r0, c9, c1, 5 //; WCP15_DCFLOOR5 r0 + MCR p15, 3, r0, c9, c1, 6 //; WCP15_DCFLOOR6 r0 + MCR p15, 3, r0, c9, c1, 7 //; WCP15_DCFLOOR7 r0 + + //; Initialize ASID to zero + MCR p15, 0, r0, c13, c0, 1 //; WCP15_CONTEXTIDR r0 + + //; ICIALL to invalidate entire I-Cache + MCR p15, 0, r0, c7, c5, 0 //; ICIALLU + + //; DCIALL to invalidate entire D-Cache + MCR p15, 0, r0, c9, c0, 6 //; DCIALL r0 + + + //; The VBAR (Vector Base Address Register) should be initialized + //; early in your code. We are setting it to zero + MCR p15, 0, r0, c12, c0, 0 //; WCP15_VBAR r0 + + //; Ensure the MCR's above have completed their operation before continuing + DSB + ISB + + //;------------------------------------------------------------------- + //; There are a number of registers that must be set prior to enabling + //; the MMU. The DCAR is one of these registers. We are setting + //; it to zero (no access) to easily detect improper setup in subsequent + //; code sequences + //;------------------------------------------------------------------- + //; Setup DACR (Domain Access Control Register) to zero + MCR p15, 0, r0, c3, c0, 0 //; WCP15_DACR r0 + + //; Setup DCLKCR to allow normal D-Cache line fills + MCR p15, 1, r0, c9, c0, 7 //; WCP15_DCLKCR r0 + + //; Setup the TLBLKCR + //; Victim = 6'b000000; Floor = 6'b000000; + //; IASIDCFG = 2'b00 (State-Machine); IALLCFG = 2'b01 (Flash); BNA = 1'b0; + MOV r1, #0x02 + MCR p15, 0, r1, c10, c1, 3 //; WCP15_TLBLKCR r1 + + //;Make sure TLBLKCR is complete before continuing + ISB + + //; Invalidate the UTLB + MCR p15, 0, r0, c8, c7, 0 //; UTLBIALL + + //; Make sure UTLB request has been presented to macro before continuing + ISB + + //; setup L2CR1 to some default Instruction and data prefetching values + //; Users may want specific settings for various performance enhancements + MCR p15, 3, r0, c15, c0, 3 //; WCP15_L2CR1 r0 + + + //; Enable Z bit to enable branch prediction (default is off) + + MRC p15, 0, r2, c1, c0, 0 //; RCP15_SCTLR r2 + ORR r2, r2, #0x00000800 + MCR p15, 0, r2, c1, c0, 0 //; WCP15_SCTLR r2 + + mov r1, lr + //; Make sure Link stack is initialized with branch and links to sequential addresses + //; This aids in creating a predictable startup environment + BL SEQ1 +SEQ1: BL SEQ2 +SEQ2: BL SEQ3 +SEQ3: BL SEQ4 +SEQ4: BL SEQ5 +SEQ5: BL SEQ6 +SEQ6: BL SEQ7 +SEQ7: BL SEQ8 +SEQ8: + + mov lr, r1 + + //; REMOVE FOLLOWING THREE INSTRUCTIONS WHEN POWER COLLAPSE IS ENA + //;Make sure the DBGOSLSR[LOCK] bit is cleared to allow access to the debug registers + //; Writing anything but the "secret code" to the DBGOSLAR clears the DBGOSLSR[LOCK] bit + MCR p14, 0, r0, c1, c0, 4 //; WCP14_DBGOSLAR r0 + + + //; Read the DBGPRSR to clear the DBGPRSR[STICKYPD] + //; Any read to DBGPRSR clear the STICKYPD bit + //; ISB guarantees the read completes before attempting to + //; execute a CP14 instruction. + MRC p14, 0, r3, c1, c5, 4 //; RCP14_DBGPRSR r3 + ISB + + //; Initialize the Watchpoint Control Registers to zero (optional) +//;;; MCR p14, 0, r0, c0, c0, 7 ; WCP14_DBGWCR0 r0 +//;;; MCR p14, 0, r0, c0, c1, 7 ; WCP14_DBGWCR1 r0 + + //;---------------------------------------------------------------------- + //; Enabling Error reporting is something users may want to do at + //; some other point in time. We have chosen some default settings + //; that should be reviewed. Most of these registers come up in an + //; unpredictable state after reset. + //;---------------------------------------------------------------------- +//;Start of error and control setting + + //; setup L2CR0 with various L2/TCM control settings + //; enable out of order bus attributes and error reporting + //; this register comes up unpredictable after reset + .word 0xe3001f0f // MOVW r1, #0x0F0F + .word 0xe34c1005 // MOVT r1, #0xC005 + + MCR p15, 3, r1, c15, c0, 1 //; WCP15_L2CR0 r1 + + //; setup L2CPUCR + MOV r2, #0 + MCR p15, 3, r2, c15, c0, 2 //; WCP15_L2CPUCR r2 + + //; setup SPCR + //; enable all error reporting (reset value is unpredicatble for most bits) + MOV r3, #0x0F + MCR p15, 0, r3, c9, c7, 0 //; WCP15_SPCR r3 + + + //; setup DMACHCRs (reset value unpredictable) + //; control setting and enable all error reporting + MOV r1, #0x0F + + //; DMACHCR0 = 0000000F + MOV r2, #0x00 //; channel 0 + MCR p15, 0, r2, c11, c0, 0 //; WCP15_DMASELR r2 + MCR p15, 0, r1, c11, c0, 2 //; WCP15_DMACHCR r1 + + //; DMACHCR1 = 0000000F + MOV r2, #0x01 //; channel 1 + MCR p15, 0, r2, c11, c0, 0 //; WCP15_DMASELR r2 + MCR p15, 0, r1, c11, c0, 2 //; WCP15_DMACHCR r1 + + //; DMACHCR2 = 0000000F + MOV r2, #0x02 //; channel 2 + MCR p15, 0, r2, c11, c0, 0 //; WCP15_DMASELR r2 + MCR p15, 0, r1, c11, c0, 2 //; WCP15_DMACHCR r1 + + //; DMACHCR3 = 0000000F + MOV r2, #0x03 //; channel 3 + MCR p15, 0, r2, c11, c0, 0 //; WCP15_DMASELR r2 + MCR p15, 0, r1, c11, c0, 2 //; WCP15_DMACHCR r1 + + //; Set ACTLR (reset unpredictable) + //; Set AVIVT control, error reporting, etc. + MOV r3, #0x04 + + //; keep d-cache disabled on Raptor Rev 1.0 silicon + //; orr r3, r3, #0x4000 //;disable dcache + MCR p15, 0, r3, c1, c0, 1 //; WCP15_ACTLR r3 + +//;End of error and control setting + + //;---------------------------------------------------------------------- + //; Unlock ETM and read StickyPD to halt the ETM clocks from running. + //; This is required for power saving whether the ETM is used or not. + //;---------------------------------------------------------------------- + + //;Clear ETMOSLSR[LOCK] bit + MOV r1, #0x00000000 + MCR p14, 1, r1, c1, c0, 4 //; WCP14_ETMOSLAR r1 + + //;Clear ETMPDSR[STICKYPD] bit + MRC p14, 1, r2, c1, c5, 4 //; RCP14_ETMPDSR r2 + +/* +#ifdef APPSBL_ETM_ENABLE + ;---------------------------------------------------------------------- + ; Optionally Enable the ETM (Embedded Trace Macro) which is used for debug + ;---------------------------------------------------------------------- + + ; enable ETM clock if disabled + MRC p15, 7, r1, c15, c0, 5 ; RCP15_CPMR r1 + ORR r1, r1, #0x00000008 + MCR p15, 7, r1, c15, c0, 5 ; WCP15_CPMR r1 + ISB + + ; set trigger event to counter1 being zero + MOV r3, #0x00000040 + MCR p14, 1, r3, c0, c2, 0 ; WCP14_ETMTRIGGER r3 + + ; clear ETMSR + MOV r2, #0x00000000 + MCR p14, 1, r2, c0, c4, 0 ; WCP14_ETMSR r2 + + ; clear trace enable single address comparator usage + MCR p14, 1, r2, c0, c7, 0 ; WCP14_ETMTECR2 r2 + + ; set trace enable to always + MOV r2, #0x0000006F + MCR p14, 1, r2, c0, c8, 0 ; WCP14_ETMTEEVR r2 + + ; clear trace enable address range comparator usage and exclude nothing + MOV r2, #0x01000000 + MCR p14, 1, r2, c0, c9, 0 ; WCP14_ETMTECR1 r2 + + ; set view data to always + MOV r2, #0x0000006F + MCR p14, 1, r2, c0, c12, 0 ; WCP14_ETMVDEVR r2 + + ; clear view data single address comparator usage + MOV r2, #0x00000000 + MCR p14, 1, r2, c0, c13, 0 ; WCP14_ETMVDCR1 r2 + + ; clear view data address range comparator usage and exclude nothing + MOV r2, #0x00010000 + MCR p14, 1, r2, c0, c15, 0 ; WCP14_ETMVDCR3 r2 + + ; set counter1 to 194 + MOV r2, #0x000000C2 + MCR p14, 1, r2, c0, c0, 5 ; WCP14_ETMCNTRLDVR1 r2 + + ; set counter1 to never reload + MOV r2, #0x0000406F + MCR p14, 1, r2, c0, c8, 5 ; WCP14_ETMCNTRLDEVR1 r2 + + ; set counter1 to decrement every cycle + MOV r2, #0x0000006F + MCR p14, 1, r2, c0, c4, 5 ; WCP14_ETMCNTENR1 r2 + + ; Set trace synchronization frequency 1024 bytes + MOV r2, #0x00000400 + MCR p14, 1, r2, c0, c8, 7 ; WCP14_ETMSYNCFR r2 + + ; Program etm control register + ; - Set the CPU to ETM clock ratio to 1:1 + ; - Set the ETM to perform data address tracing + MOV r2, #0x00002008 + MCR p14, 1, r2, c0, c0, 0 ; WCP14_ETMCR r2 + ISB +#endif *//* APPSBL_ETM_ENABLE */ + +/* +#ifdef APPSBL_VFP_ENABLE + ;---------------------------------------------------------------------- + ; Perform the following operations if you intend to make use of + ; the VFP/Neon unit. Note that the FMXR instruction requires a CPU ID + ; indicating the VFP unit is present (i.e.Cortex-A8). . + ; Some tools will require full double precision floating point support + ; which will become available in Scorpion pass 2 + ;---------------------------------------------------------------------- + ; allow full access to CP 10 and 11 space for VFP/NEON use + MRC p15, 0, r1, c1, c0, 2 ; Read CP Access Control Register + ORR r1, r1, #0x00F00000 ; enable full access for p10,11 + MCR p15, 0, r1, c1, c0, 2 ; Write CPACR + + ;make sure the CPACR is complete before continuing + ISB + + ; Enable VFP itself (certain OSes may want to dynamically set/clear + ; the enable bit based on the application being executed + MOV r1, #0x40000000 + FMXR FPEXC, r1 +#endif *//* APPSBL_VFP_ENABLE */ + + /* we have no stack, so just tail-call into the SET_SA routine... */ + b SET_SA + +.ltorg diff --git a/arch/arm/mach-msm/idle-v7.S b/arch/arm/mach-msm/idle-v7.S index 919e546c70975..967db0b4ac510 100644 --- a/arch/arm/mach-msm/idle-v7.S +++ b/arch/arm/mach-msm/idle-v7.S @@ -81,6 +81,7 @@ ENTRY(msm_pm_collapse_exit) str r1, [r0, #0x00C] #endif +#if 0 //; REMOVE FOLLOWING THREE INSTRUCTIONS WHEN POWER COLLAPSE IS ENA //;Make sure the DBGOSLSR[LOCK] bit is cleared to allow access to the debug registers //; Writing anything but the "secret code" to the DBGOSLAR clears the DBGOSLSR[LOCK] bit @@ -93,46 +94,14 @@ ENTRY(msm_pm_collapse_exit) //; execute a CP14 instruction. MRC p14, 0, r3, c1, c5, 4 //; RCP14_DBGPRSR r3 ISB +#endif #if 0 /* allow jtag reconnect */ 1: b 1b #endif - //; Write L2VR3F1 to make L2 array work properly at 998 on Raptor2 - LDR r1, =0x00212102 //; the l2 timing watch out on P2 - MCR p15, 3, r1, c15, c15, 1 //; WCP15_L2VR3F1 - DSB - ISB - - //; write the value for Icache to run at 998 on Raptor2 - LDR r1, =0x3800E000 - MCR p15, 0, r1, c15, c15, 0 //; WCP15_PVR0F0 - ISB - - //; DCIALL to invalidate L2 cache bank (needs to be run 4 times, once per bank) - //; This must be done early in code (prior to enabling the caches) - MOV r1, #0x2 - MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank D ([15:14] == 2'b00) - ORR r1, r1, #0x00004000 - MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank C ([15:14] == 2'b01) - ADD r1, r1, #0x00004000 - MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank B ([15:14] == 2'b10) - ADD r1, r1, #0x00004000 - MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank A ([15:14] == 2'b11) - - //; DCIALL to invalidate entire D-Cache - MCR p15, 0, r0, c9, c0, 6 //; DCIALL r0 - - //; Initialize the BPCR - setup Global History Mask (GHRM) to all 1's - //; and have all address bits (AM) participate. - //; Different settings can be used to improve performance - MOVW r1, #0x01FF - MOVT r1, #0x01FF - MCR p15, 7, r1, c15, c0, 2 //; WCP15_BPCR - - dsb - isb + bl __cpu_early_init ldr r1, =saved_state_end ldr r2, =msm_pm_collapse_exit From 357f90d7b23b9eac87f9d0c6e73b079dc92f3a11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 21 Sep 2009 20:28:27 -0700 Subject: [PATCH 0423/2556] [ARM] msm: Import new bootinit code from Qualcomm. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New SET_SA code based on fuse registers plus some cache control register changes. Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/arch-init-scorpion.S | 405 +++++++++++-------------- 1 file changed, 169 insertions(+), 236 deletions(-) diff --git a/arch/arm/mach-msm/arch-init-scorpion.S b/arch/arm/mach-msm/arch-init-scorpion.S index 07a99ac221cb8..3bc533bcf4eb7 100644 --- a/arch/arm/mach-msm/arch-init-scorpion.S +++ b/arch/arm/mach-msm/arch-init-scorpion.S @@ -1,6 +1,7 @@ /* * Copyright (c) 2008, QUALCOMM Incorporated. - * Copyright (c) 2008, Google Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Copyright (c) 2008-2009, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,188 +43,71 @@ #define DSB .byte 0x4f, 0xf0, 0x7f, 0xf5 #define ISB .byte 0x6f, 0xf0, 0x7f, 0xf5 -/* - ; LVT Ring Osc counter - ; used to determine sense amp settings - ; Clobbers registers r0, r4, r5, r6, r7, r9, r10, r11 -*/ -.equ CLK_CTL_BASE, 0xA8600000 -.equ A_GLBL_CLK_ENA, 0x0000 -.equ A_PRPH_WEB_NS_REG,0x0080 -.equ A_MSM_CLK_RINGOSC,0x00D0 -.equ A_TCXO_CNT, 0x00D4 -.equ A_TCXO_CNT_DONE, 0x00D8 -.equ A_RINGOSC_CNT, 0x00DC -.equ A_MISC_CLK_CTL, 0x0108 -.equ CLK_TEST, 0xA8600114 -.equ SPSS_CSR_BASE, 0xAC100000 -.equ A_SCRINGOSC, 0x0510 - -//;; Number of TCXO cycles to count ring oscillations -.equ TCXO_CNT_VAL, 0x100 - -SET_SA: - //; clear CLK_TEST[19] (RING_OSC_DBG_SEL) to count ring osc - LDR r5, =CLK_TEST - LDR r4, [r5] - BIC r4, r4, #0x80000 - STR r4, [r5] - - //; setup memory pointers - LDR r5, =CLK_CTL_BASE - LDR r6, =SPSS_CSR_BASE - - //; PRPH_WEB_NS_REG = 0x00000A00 - enable root and crc - MOV r7, #0x0A00 - STR r7,[r5,#A_PRPH_WEB_NS_REG] - - //; MISC_CLK_CTL = 0x0 to disable tcxo4cnt, ringosccnt - MOV r0, #0 - STR r0,[r5,#A_MISC_CLK_CTL] - DSB - - //;; MSM_CLK_RINGOSC = 0x00000A0E - //;; the E selects Scorpion LVT RO(or DS) output, - //;; the A enables RO CRC and CXC - LDR r7, =0x0A0E - STR r7,[r5,#A_MSM_CLK_RINGOSC] - - MOV r10, #0x1 //; SCRINGOSC[1:0] = 01 - MOV r11, #TCXO_CNT_VAL - - //;; initialize r9 - counts the 4 oscillators - MOV r9,#0x0 - ADR r4,RO_CNT - -ACQUIRE_FREQ: - //; SCRINGOSC[1:0] is set by r10, [13:11][10:8] = 0..7 - //;MOV r7, r10 - BIC r7, r10, #0x3F00 - //;BFI r7, r9, #8, #3 - ORR r7, r7, r9, LSL #8 - //;BFI r7, r9, #11, #3 - ORR r7, r7, r9, LSL #11 - STR r7,[r6,#A_SCRINGOSC] - - STR r11,[r5,#A_TCXO_CNT] - DSB //; Make sure the countdown value gets written before the ROs are enabled - - //; MISC_CLK_CTL = 0x3 to enable tcxo4cnt, ringosccnt - MOV r7,#0x3 - STR r7,[r5,#A_MISC_CLK_CTL] - - //;; According to Raptor HDD, to read TCXO_CNT_DONE, use address for TCXO_CNT on pass1. - //;; Then read RINGOSC_CNT and store to debug_mem table -WAIT_TCXO_CNT_DONE: - LDR r7,[r5,#A_TCXO_CNT_DONE] - CMP r7,#0x1 - BNE WAIT_TCXO_CNT_DONE - LDR r7,[r5,#A_RINGOSC_CNT] - STR r7,[r4],#0x04 //; post-indexed update r4 to next empty table entry - - //; MISC_CLK_CTL = 0x0 to disable tcxo4cnt, ringosccnt - STR r0,[r5,#A_MISC_CLK_CTL] - DSB - - //;; move to next oscillator - r9 counts oscillators 0..7 - ADD r9,r9,#1 - CMP r9,#0x4 - BLT ACQUIRE_FREQ - - //; average the 4 counts - ADR r4,RO_CNT - LDR r5, [r4], #0x4 - LDR r6, [r4], #0x4 - ADD r5, r5, r6 - LDR r6, [r4], #0x4 - ADD r5, r5, r6 - LDR r6, [r4], #0x4 - ADD r5, r5, r6 - MOV r5, r5, LSR #2 - -.equ SLOW_THRESHOLD, 0x0E10 -.equ NOM_THRESHOLD, 0x123A - - //; compare average to slow part threshold - LDR r4, =SLOW_THRESHOLD - CMP r5, r4 - ADRLT r4, SLOW_SA - BLT WRITE_SA - - //; compare average to nominal part threshold - LDR r4, =NOM_THRESHOLD - CMP r5, r4 - ADRLT r4, NOM_SA - BLT WRITE_SA - - //; must be fast part - ADR r4, FAST_SA - B WRITE_SA - - -SLOW_SA: - .word 0x38001C00 //; PVR0F0 - .word 0x1C000000 //; PVR2F0 - .word 0x00212102 //; L2VR3F1 - -NOM_SA: - .word 0x38001C00 //; PVR0F0 - .word 0x1C000000 //; PVR2F0 - .word 0x00212102 //; L2VR3F1 - -FAST_SA: - .word 0x38003C00 //; PVR0F0 - .word 0x1C000000 //; PVR2F0 - .word 0x00212102 //; L2VR3F1 - -RO_CNT: - .word 0x0 - .word 0x0 - .word 0x0 - .word 0x0 - -WRITE_SA: - //; write the sense amp settings - - //; PVR0F0[15:10] = I$ - LDR r5, [r4], #4 - //;WCP15_PVR0F0 r5 - MCR p15,0x0,r5,c15,c15,0 - - //; PVR2F0[31:26] = D$ - LDR r5, [r4], #4 - //;WCP15_PVR2F0 r5 - MCR p15,0x2,r5,c15,c15,0 - - //; L2VR3F1[23:0] - LDR r5, [r4], #4 - //;WCP15_L2VR3F1 r5 - MCR p15,0x3,r5,c15,c15,1 - - ISB - - BX lr +.equ TCSR_SPARE2, 0xA8700060 + +SET_SA: + ldr r0, =TCSR_SPARE2 + ldr r12, [r0] + + /* pack bits 8,2,0 into 2,1,0 */ + and r0, r12, #0x001 + and r1, r12, #0x004 + and r2, r12, #0x100 + orr r0, r1, lsr #1 + orr r0, r2, lsr #6 + + adr r1, table_l1_acc + mov r0, r0, lsl #2 + ldr r3, [r1, r0] + + /* write 3800XXXX to PVR0F0 */ + orr r0, r3, #0x38000000 + mcr p15, 0, r0, c15, c15, 0 + + /* write XXXX0000 to PVR2F0 */ + mov r1, r3, lsl #16 + mcr p15, 2, r1, c15, c15, 0 + + adr r1, table_l2_acc + and r0, r12, #0x008 + and r2, r12, #0x002 + orr r0, r0, r2, lsl #1 + ldr r2, [r1, r0] + + /* write to L2VR3F1 */ + mcr p15, 3, r2, c15, c15, 1 + + bx lr + +table_l1_acc: + .word 0xFC00 + .word 0xFC00 + .word 0x7C00 + .word 0xFC00 + .word 0x3C00 + .word 0x0400 + .word 0x0C00 + .word 0x1C00 + +table_l2_acc: + .word 0x010102 + .word 0x010102 + .word 0x010101 + .word 0x212102 .globl __cpu_early_init __cpu_early_init: - //; Zero out r0 for use throughout this code. All other GPRs + //; Zero out r0 for use throughout this code. All other GPRs //; (r1-r3) are set throughout this code to help establish //; a consistent startup state for any code that follows. //; Users should add code at the end of this routine to establish //; their own stack address (r13), add translation page tables, enable //; the caches, etc. - MOV r0, #0x0 + MOV r0, #0x0 - //; Write L2VR3F1 to make L2 array work properly at 998 on Raptor2 - LDR r1, =0x00212102 //; the l2 timing watch out on P2 - MCR p15, 3, r1, c15, c15, 1 //; WCP15_L2VR3F1 - DSB - ISB - //; write the value for Icache to run at 998 on Raptor2 - LDR r1, =0x3800E000 - MCR p15, 0, r1, c15, c15, 0 //; WCP15_PVR0F0 - ISB + //; Remove hardcoded cache settings. appsbl_handler.s calls Set_SA + //; API to dynamically configure cache for slow/nominal/fast parts //; DCIALL to invalidate L2 cache bank (needs to be run 4 times, once per bank) //; This must be done early in code (prior to enabling the caches) @@ -235,16 +119,18 @@ __cpu_early_init: MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank B ([15:14] == 2'b10) ADD r1, r1, #0x00004000 MCR p15, 0, r1, c9, c0, 6 //; DCIALL bank A ([15:14] == 2'b11) - + //; Initialize the BPCR - setup Global History Mask (GHRM) to all 1's //; and have all address bits (AM) participate. //; Different settings can be used to improve performance - .word 0xe30011ff // MOVW r1, #0x01FF - .word 0xe34011ff // MOVT r1, #0x01FF + // MOVW r1, #0x01FF +.word 0xe30011ff // hardcoded MOVW instruction due to lack of compiler support + // MOVT r1, #0x01FF +.word 0xe34011ff // hardcoded MOVT instruction due to lack of compiler support MCR p15, 7, r1, c15, c0, 2 //; WCP15_BPCR - - - //; Initialize all I$ Victim Registers to 0 for startup + + + //; Initialize all I$ Victim Registers to 0 for startup MCR p15, 0, r0, c9, c1, 0 //; WCP15_ICVIC0 r0 MCR p15, 0, r0, c9, c1, 1 //; WCP15_ICVIC1 r0 MCR p15, 0, r0, c9, c1, 2 //; WCP15_ICVIC2 r0 @@ -283,21 +169,21 @@ __cpu_early_init: MCR p15, 3, r0, c9, c1, 5 //; WCP15_DCFLOOR5 r0 MCR p15, 3, r0, c9, c1, 6 //; WCP15_DCFLOOR6 r0 MCR p15, 3, r0, c9, c1, 7 //; WCP15_DCFLOOR7 r0 - + //; Initialize ASID to zero MCR p15, 0, r0, c13, c0, 1 //; WCP15_CONTEXTIDR r0 - + //; ICIALL to invalidate entire I-Cache - MCR p15, 0, r0, c7, c5, 0 //; ICIALLU - + MCR p15, 0, r0, c7, c5, 0 //; ICIALLU + //; DCIALL to invalidate entire D-Cache MCR p15, 0, r0, c9, c0, 6 //; DCIALL r0 //; The VBAR (Vector Base Address Register) should be initialized - //; early in your code. We are setting it to zero + //; early in your code. We are setting it to zero MCR p15, 0, r0, c12, c0, 0 //; WCP15_VBAR r0 - + //; Ensure the MCR's above have completed their operation before continuing DSB ISB @@ -305,16 +191,16 @@ __cpu_early_init: //;------------------------------------------------------------------- //; There are a number of registers that must be set prior to enabling //; the MMU. The DCAR is one of these registers. We are setting - //; it to zero (no access) to easily detect improper setup in subsequent + //; it to zero (no access) to easily detect improper setup in subsequent //; code sequences //;------------------------------------------------------------------- //; Setup DACR (Domain Access Control Register) to zero MCR p15, 0, r0, c3, c0, 0 //; WCP15_DACR r0 - - //; Setup DCLKCR to allow normal D-Cache line fills + + //; Setup DCLKCR to allow normal D-Cache line fills MCR p15, 1, r0, c9, c0, 7 //; WCP15_DCLKCR r0 - - //; Setup the TLBLKCR + + //; Setup the TLBLKCR //; Victim = 6'b000000; Floor = 6'b000000; //; IASIDCFG = 2'b00 (State-Machine); IALLCFG = 2'b01 (Flash); BNA = 1'b0; MOV r1, #0x02 @@ -328,19 +214,21 @@ __cpu_early_init: //; Make sure UTLB request has been presented to macro before continuing ISB - + //; setup L2CR1 to some default Instruction and data prefetching values - //; Users may want specific settings for various performance enhancements - MCR p15, 3, r0, c15, c0, 3 //; WCP15_L2CR1 r0 + //; Users may want specific settings for various performance enhancements + //; In Halcyon we do not have broadcasting barriers. So we need to turn + // ; on bit 8 of L2CR1; which DBB:( Disable barrier broadcast ) + MOV r2, #0x100 + MCR p15, 3, r2, c15, c0, 3 //; WCP15_L2CR1 r0 - //; Enable Z bit to enable branch prediction (default is off) - + //; Enable Z bit to enable branch prediction (default is off) MRC p15, 0, r2, c1, c0, 0 //; RCP15_SCTLR r2 ORR r2, r2, #0x00000800 MCR p15, 0, r2, c1, c0, 0 //; WCP15_SCTLR r2 - mov r1, lr + mov r1, lr //; Make sure Link stack is initialized with branch and links to sequential addresses //; This aids in creating a predictable startup environment BL SEQ1 @@ -351,57 +239,88 @@ SEQ4: BL SEQ5 SEQ5: BL SEQ6 SEQ6: BL SEQ7 SEQ7: BL SEQ8 -SEQ8: - - mov lr, r1 +SEQ8: + mov lr, r1 //; REMOVE FOLLOWING THREE INSTRUCTIONS WHEN POWER COLLAPSE IS ENA //;Make sure the DBGOSLSR[LOCK] bit is cleared to allow access to the debug registers //; Writing anything but the "secret code" to the DBGOSLAR clears the DBGOSLSR[LOCK] bit MCR p14, 0, r0, c1, c0, 4 //; WCP14_DBGOSLAR r0 - - + + //; Read the DBGPRSR to clear the DBGPRSR[STICKYPD] //; Any read to DBGPRSR clear the STICKYPD bit - //; ISB guarantees the read completes before attempting to + //; ISB guarantees the read completes before attempting to //; execute a CP14 instruction. MRC p14, 0, r3, c1, c5, 4 //; RCP14_DBGPRSR r3 ISB - + //; Initialize the Watchpoint Control Registers to zero (optional) -//;;; MCR p14, 0, r0, c0, c0, 7 ; WCP14_DBGWCR0 r0 -//;;; MCR p14, 0, r0, c0, c1, 7 ; WCP14_DBGWCR1 r0 + //;;; MCR p14, 0, r0, c0, c0, 7 ; WCP14_DBGWCR0 r0 + //;;; MCR p14, 0, r0, c0, c1, 7 ; WCP14_DBGWCR1 r0 + + + //;---------------------------------------------------------------------- + //; The saved Program Status Registers (SPSRs) should be setup + //; prior to any automatic mode switches. The following + //; code sets these registers up to a known state. Users will need to + //; customize these settings to meet their needs. + //;---------------------------------------------------------------------- + MOV r2, #0x1f + MOV r1, #0x17 //;ABT mode + msr cpsr_c, r1 //;ABT mode + msr spsr_cxfs, r2 //;clear the spsr + MOV r1, #0x1b //;UND mode + msr cpsr_c, r1 //;UND mode + msr spsr_cxfs, r2 //;clear the spsr + MOV r1, #0x11 //;FIQ mode + msr cpsr_c, r1 //;FIQ mode + msr spsr_cxfs, r2 //;clear the spsr + MOV r1, #0x12 //;IRQ mode + msr cpsr_c, r1 //;IRQ mode + msr spsr_cxfs, r2 //;clear the spsr + MOV r1, #0x16 //;Monitor mode + msr cpsr_c, r1 //;Monitor mode + msr spsr_cxfs, r2 //;clear the spsr + MOV r1, #0x13 //;SVC mode + msr cpsr_c, r1 //;SVC mode + msr spsr_cxfs, r2 //;clear the spsr + //;---------------------------------------------------------------------- //; Enabling Error reporting is something users may want to do at - //; some other point in time. We have chosen some default settings + //; some other point in time. We have chosen some default settings //; that should be reviewed. Most of these registers come up in an - //; unpredictable state after reset. + //; unpredictable state after reset. //;---------------------------------------------------------------------- //;Start of error and control setting //; setup L2CR0 with various L2/TCM control settings //; enable out of order bus attributes and error reporting - //; this register comes up unpredictable after reset - .word 0xe3001f0f // MOVW r1, #0x0F0F - .word 0xe34c1005 // MOVT r1, #0xC005 - - MCR p15, 3, r1, c15, c0, 1 //; WCP15_L2CR0 r1 + //; this register comes up unpredictable after reset + // MOVW r1, #0x0F0F +.word 0xe3001f0f // hardcoded MOVW instruction due to lack of compiler support + // MOVT r1, #0xC005 +.word 0xe34c1005 // hardcoded MOVW instruction due to lack of compiler support + MCR p15, 3, r1, c15, c0, 1 //; WCP15_L2CR0 r1 //; setup L2CPUCR - MOV r2, #0 - MCR p15, 3, r2, c15, c0, 2 //; WCP15_L2CPUCR r2 + //; MOV r2, #0xFF + //; Enable I and D cache parity + //;L2CPUCR[7:5] = 3~Rh7 ~V enable parity error reporting for modified, + //;tag, and data parity errors + MOV r2, #0xe0 + MCR p15, 3, r2, c15, c0, 2 //; WCP15_L2CPUCR r2 //; setup SPCR //; enable all error reporting (reset value is unpredicatble for most bits) MOV r3, #0x0F - MCR p15, 0, r3, c9, c7, 0 //; WCP15_SPCR r3 + MCR p15, 0, r3, c9, c7, 0 //; WCP15_SPCR r3 - //; setup DMACHCRs (reset value unpredictable) //; control setting and enable all error reporting MOV r1, #0x0F - + //; DMACHCR0 = 0000000F MOV r2, #0x00 //; channel 0 MCR p15, 0, r2, c11, c0, 0 //; WCP15_DMASELR r2 @@ -424,22 +343,36 @@ SEQ8: //; Set ACTLR (reset unpredictable) //; Set AVIVT control, error reporting, etc. - MOV r3, #0x04 - - //; keep d-cache disabled on Raptor Rev 1.0 silicon - //; orr r3, r3, #0x4000 //;disable dcache + //; MOV r3, #0x07 + //; Enable I and D cache parity + //;ACTLR[2:0] = 3'h7 - enable parity error reporting from L2/I$/D$) + //;ACTLR[5:4] = 2'h3 - enable parity + //;ACTLR[19:18] =2'h3 - always generate and check parity(when MMU disabled). + //;Value to be written #0xC0037 + // MOVW r3, #0x0037 +.word 0xe3003037 // hardcoded MOVW instruction due to lack of compiler support + // MOVT r3, #0x000C +.word 0xe340300c // hardcoded MOVW instruction due to lack of compiler support + //; read the version_id to determine if d-cache should be disabled + LDR r2, = 0xa8e00270 //;Read HW_REVISION_NUMBER, HWIO_HW_REVISION_NUMBER_ADDR + LDR r2,[r2] + AND r2,r2,#0xf0000000 //;hw_revision mask off bits 28-31 + //;if HW_revision is 1.0 or older, (revision==0) + CMP r2,#0 + //; Disable d-cache on older QSD8650 (Rev 1.0) silicon + orreq r3, r3, #0x4000 //;disable dcache MCR p15, 0, r3, c1, c0, 1 //; WCP15_ACTLR r3 //;End of error and control setting - + //;---------------------------------------------------------------------- //; Unlock ETM and read StickyPD to halt the ETM clocks from running. //; This is required for power saving whether the ETM is used or not. //;---------------------------------------------------------------------- - + //;Clear ETMOSLSR[LOCK] bit MOV r1, #0x00000000 - MCR p14, 1, r1, c1, c0, 4 //; WCP14_ETMOSLAR r1 + MCR p14, 1, r1, c1, c0, 4 //; WCP14_ETMOSLAR r1 //;Clear ETMPDSR[STICKYPD] bit MRC p14, 1, r2, c1, c5, 4 //; RCP14_ETMPDSR r2 @@ -448,11 +381,11 @@ SEQ8: #ifdef APPSBL_ETM_ENABLE ;---------------------------------------------------------------------- ; Optionally Enable the ETM (Embedded Trace Macro) which is used for debug - ;---------------------------------------------------------------------- - + ;---------------------------------------------------------------------- + ; enable ETM clock if disabled MRC p15, 7, r1, c15, c0, 5 ; RCP15_CPMR r1 - ORR r1, r1, #0x00000008 + ORR r1, r1, #0x00000008 MCR p15, 7, r1, c15, c0, 5 ; WCP15_CPMR r1 ISB @@ -486,7 +419,7 @@ SEQ8: ; clear view data address range comparator usage and exclude nothing MOV r2, #0x00010000 MCR p14, 1, r2, c0, c15, 0 ; WCP14_ETMVDCR3 r2 - + ; set counter1 to 194 MOV r2, #0x000000C2 MCR p14, 1, r2, c0, c0, 5 ; WCP14_ETMCNTRLDVR1 r2 @@ -508,18 +441,18 @@ SEQ8: ; - Set the ETM to perform data address tracing MOV r2, #0x00002008 MCR p14, 1, r2, c0, c0, 0 ; WCP14_ETMCR r2 - ISB + ISB #endif *//* APPSBL_ETM_ENABLE */ /* #ifdef APPSBL_VFP_ENABLE ;---------------------------------------------------------------------- - ; Perform the following operations if you intend to make use of - ; the VFP/Neon unit. Note that the FMXR instruction requires a CPU ID + ; Perform the following operations if you intend to make use of + ; the VFP/Neon unit. Note that the FMXR instruction requires a CPU ID ; indicating the VFP unit is present (i.e.Cortex-A8). . ; Some tools will require full double precision floating point support ; which will become available in Scorpion pass 2 - ;---------------------------------------------------------------------- + ;---------------------------------------------------------------------- ; allow full access to CP 10 and 11 space for VFP/NEON use MRC p15, 0, r1, c1, c0, 2 ; Read CP Access Control Register ORR r1, r1, #0x00F00000 ; enable full access for p10,11 @@ -527,14 +460,14 @@ SEQ8: ;make sure the CPACR is complete before continuing ISB - + ; Enable VFP itself (certain OSes may want to dynamically set/clear - ; the enable bit based on the application being executed + ; the enable bit based on the application being executed MOV r1, #0x40000000 FMXR FPEXC, r1 #endif *//* APPSBL_VFP_ENABLE */ - /* we have no stack, so just tail-call into the SET_SA routine... */ - b SET_SA + /* we have no stack, so just tail-call into the SET_SA routine... */ + b SET_SA .ltorg From cfa2c7afe9ac0ab99aa3e2d40a06773673104606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 20 Aug 2009 18:18:57 -0700 Subject: [PATCH 0424/2556] [ARM] msm: pm: Fix apps sleep and modem initiated early exit from power collapse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new apps sleep wait states. Wait for modem to set run or early-exit after returning from power collapse. Fix msm_pm_wait_state when waiting for any bit in a mask to get set. Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/pm.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index eac2e879fc299..e47f5795f2931 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -70,7 +70,7 @@ module_param_named(idle_spin_time, msm_pm_idle_spin_time, int, S_IRUGO | S_IWUSR #define A11RAMBACKBIAS (MSM_CSR_BASE + 0x508) -#define DEM_MASTER_BITS_PER_CPU 5 +#define DEM_MASTER_BITS_PER_CPU 6 /* Power Master State Bits - Per CPU */ #define DEM_MASTER_SMSM_RUN \ @@ -83,6 +83,8 @@ module_param_named(idle_spin_time, msm_pm_idle_spin_time, int, S_IRUGO | S_IWUSR (0x08UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS)) #define DEM_MASTER_SMSM_READY \ (0x10UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS)) +#define DEM_MASTER_SMSM_SLEEP \ + (0x20UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS)) /* Power Slave State Bits */ #define DEM_SLAVE_SMSM_RUN (0x0001) @@ -173,23 +175,21 @@ static void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t) #endif static int -msm_pm_wait_state(uint32_t wait_state_all_set, uint32_t wait_state_all_clear, - uint32_t wait_state_any_set, uint32_t wait_state_any_clear) +msm_pm_wait_state(uint32_t wait_all_set, uint32_t wait_all_clear, + uint32_t wait_any_set, uint32_t wait_any_clear) { int i; uint32_t state; for (i = 0; i < 100000; i++) { state = smsm_get_state(PM_SMSM_READ_STATE); - if (((state & wait_state_all_set) == wait_state_all_set) && - ((~state & wait_state_all_clear) == wait_state_all_clear) && - (wait_state_any_set == 0 || (state & wait_state_any_set) || - wait_state_any_clear == 0 || (state & wait_state_any_clear))) + if (((wait_all_set || wait_all_clear) && + !(~state & wait_all_set) && !(state & wait_all_clear)) || + (state & wait_any_set) || (~state & wait_any_clear)) return 0; } - printk(KERN_ERR "msm_pm_wait_state(%x, %x, %x, %x) failed %x\n", - wait_state_all_set, wait_state_all_clear, - wait_state_any_set, wait_state_any_clear, state); + pr_err("msm_pm_wait_state(%x, %x, %x, %x) failed %x\n", wait_all_set, + wait_all_clear, wait_any_set, wait_any_clear, state); return -ETIMEDOUT; } @@ -211,7 +211,7 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) uint32_t enter_wait_clear = 0; uint32_t exit_state; uint32_t exit_wait_clear = 0; - uint32_t exit_wait_set = 0; + uint32_t exit_wait_any_set = 0; unsigned long pm_saved_acpu_clk_rate = 0; int ret; int rv = -EINTR; @@ -237,7 +237,7 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) case MSM_PM_SLEEP_MODE_APPS_SLEEP: enter_state = SMSM_SLEEP; exit_state = SMSM_SLEEPEXIT; - exit_wait_set = SMSM_SLEEPEXIT; + exit_wait_any_set = SMSM_SLEEPEXIT; break; default: enter_state = 0; @@ -249,18 +249,21 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) enter_state = DEM_SLAVE_SMSM_PWRC; enter_wait_set = DEM_MASTER_SMSM_RSA; exit_state = DEM_SLAVE_SMSM_WFPI; - exit_wait_set = DEM_MASTER_SMSM_RUN; + exit_wait_any_set = + DEM_MASTER_SMSM_RUN | DEM_MASTER_SMSM_PWRC_EARLY_EXIT; break; case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND: enter_state = DEM_SLAVE_SMSM_PWRC_SUSPEND; enter_wait_set = DEM_MASTER_SMSM_RSA; exit_state = DEM_SLAVE_SMSM_WFPI; - exit_wait_set = DEM_MASTER_SMSM_RUN; + exit_wait_any_set = + DEM_MASTER_SMSM_RUN | DEM_MASTER_SMSM_PWRC_EARLY_EXIT; break; case MSM_PM_SLEEP_MODE_APPS_SLEEP: enter_state = DEM_SLAVE_SMSM_SLEEP; - exit_state = 0; - exit_wait_set = DEM_MASTER_SMSM_SLEEP_EXIT; + enter_wait_set = DEM_MASTER_SMSM_SLEEP; + exit_state = DEM_SLAVE_SMSM_SLEEP_EXIT; + exit_wait_any_set = DEM_MASTER_SMSM_SLEEP_EXIT; break; default: enter_state = 0; @@ -371,7 +374,7 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) writel(0x00, A11S_CLK_SLEEP_EN); writel(0, A11S_PWRDOWN); smsm_change_state(PM_SMSM_WRITE_STATE, enter_state, exit_state); - msm_pm_wait_state(exit_wait_set, exit_wait_clear, 0, 0); + msm_pm_wait_state(0, exit_wait_clear, exit_wait_any_set, 0); if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) printk(KERN_INFO "msm_sleep(): sleep exit " "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, " From 1cbeb9201ffe86c76068202d9679c8b08843cd84 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Fri, 17 Jul 2009 18:50:18 -0700 Subject: [PATCH 0425/2556] qsd8k: pmic control Change-Id: I81d904080c7660b07b850348ee7eee212a2daa44 Signed-off-by: Brian Swetland --- arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/pmic.c | 530 +++++++++++++++++++++++++++++++++++++ arch/arm/mach-msm/pmic.h | 310 ++++++++++++++++++++++ 3 files changed, 841 insertions(+) create mode 100644 arch/arm/mach-msm/pmic.c create mode 100644 arch/arm/mach-msm/pmic.h diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 7dd6b7802134f..3da4dc160a9a2 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -5,6 +5,7 @@ obj-y += dma.o endif obj-y += nand_partitions.o +obj-y += pmic.o obj-$(CONFIG_ARCH_MSM_ARM11) += acpuclock-arm11.o idle.o obj-$(CONFIG_ARCH_MSM_SCORPION) += acpuclock-scorpion.o idle-v7.o diff --git a/arch/arm/mach-msm/pmic.c b/arch/arm/mach-msm/pmic.c new file mode 100644 index 0000000000000..e6dc9fe50aaea --- /dev/null +++ b/arch/arm/mach-msm/pmic.c @@ -0,0 +1,530 @@ +/* arch/arm/mach-msm/qdsp6/pmic.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * 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 "pmic.h" + +#include + +#define LIB_NULL_PROC 0 +#define LIB_RPC_GLUE_CODE_INFO_REMOTE_PROC 1 +#define LP_MODE_CONTROL_PROC 2 +#define VREG_SET_LEVEL_PROC 3 +#define VREG_PULL_DOWN_SWITCH_PROC 4 +#define SECURE_MPP_CONFIG_DIGITAL_OUTPUT_PROC 5 +#define SECURE_MPP_CONFIG_I_SINK_PROC 6 +#define RTC_START_PROC 7 +#define RTC_STOP_PROC 8 +#define RTC_GET_TIME_PROC 9 +#define RTC_ENABLE_ALARM_PROC 10 +#define RTC_DISABLE_ALARM_PROC 11 +#define RTC_GET_ALARM_TIME_PROC 12 +#define RTC_GET_ALARM_STATUS_PROC 13 +#define RTC_SET_TIME_ADJUST_PROC 14 +#define RTC_GET_TIME_ADJUST_PROC 15 +#define SET_LED_INTENSITY_PROC 16 +#define FLASH_LED_SET_CURRENT_PROC 17 +#define FLASH_LED_SET_MODE_PROC 18 +#define FLASH_LED_SET_POLARITY_PROC 19 +#define SPEAKER_CMD_PROC 20 +#define SET_SPEAKER_GAIN_PROC 21 +#define VIB_MOT_SET_VOLT_PROC 22 +#define VIB_MOT_SET_MODE_PROC 23 +#define VIB_MOT_SET_POLARITY_PROC 24 +#define VID_EN_PROC 25 +#define VID_IS_EN_PROC 26 +#define VID_LOAD_DETECT_EN_PROC 27 +#define MIC_EN_PROC 28 +#define MIC_IS_EN_PROC 29 +#define MIC_SET_VOLT_PROC 30 +#define MIC_GET_VOLT_PROC 31 +#define SPKR_EN_RIGHT_CHAN_PROC 32 +#define SPKR_IS_RIGHT_CHAN_EN_PROC 33 +#define SPKR_EN_LEFT_CHAN_PROC 34 +#define SPKR_IS_LEFT_CHAN_EN_PROC 35 +#define SET_SPKR_CONFIGURATION_PROC 36 +#define GET_SPKR_CONFIGURATION_PROC 37 +#define SPKR_GET_GAIN_PROC 38 +#define SPKR_IS_EN_PROC 39 +#define SPKR_EN_MUTE_PROC 40 +#define SPKR_IS_MUTE_EN_PROC 41 +#define SPKR_SET_DELAY_PROC 42 +#define SPKR_GET_DELAY_PROC 43 +#define SECURE_MPP_CONFIG_DIGITAL_INPUT_PROC 44 +#define SET_SPEAKER_DELAY_PROC 45 +#define SPEAKER_1K6_ZIN_ENABLE_PROC 46 +#define SPKR_SET_MUX_HPF_CORNER_FREQ_PROC 47 +#define SPKR_GET_MUX_HPF_CORNER_FREQ_PROC 48 +#define SPKR_IS_RIGHT_LEFT_CHAN_ADDED_PROC 49 +#define SPKR_EN_STEREO_PROC 50 +#define SPKR_IS_STEREO_EN_PROC 51 +#define SPKR_SELECT_USB_WITH_HPF_20HZ_PROC 52 +#define SPKR_IS_USB_WITH_HPF_20HZ_PROC 53 +#define SPKR_BYPASS_MUX_PROC 54 +#define SPKR_IS_MUX_BYPASSED_PROC 55 +#define SPKR_EN_HPF_PROC 56 +#define SPKR_IS_HPF_EN_PROC 57 +#define SPKR_EN_SINK_CURR_FROM_REF_VOLT_CIR_PROC 58 +#define SPKR_IS_SINK_CURR_FROM_REF_VOLT_CIR_EN_PROC 59 +#define SPKR_ADD_RIGHT_LEFT_CHAN_PROC 60 +#define SPKR_SET_GAIN_PROC 61 +#define SPKR_EN_PROC 62 + + +/* rpc related */ +#define PMIC_RPC_TIMEOUT (5*HZ) + +#define PMIC_RPC_PROG 0x30000061 +#define PMIC_RPC_VER 0x00010001 + +/* error bit flags defined by modem side */ +#define PM_ERR_FLAG__PAR1_OUT_OF_RANGE (0x0001) +#define PM_ERR_FLAG__PAR2_OUT_OF_RANGE (0x0002) +#define PM_ERR_FLAG__PAR3_OUT_OF_RANGE (0x0004) +#define PM_ERR_FLAG__PAR4_OUT_OF_RANGE (0x0008) +#define PM_ERR_FLAG__PAR5_OUT_OF_RANGE (0x0010) + +#define PM_ERR_FLAG__ALL_PARMS_OUT_OF_RANGE (0x001F) + +#define PM_ERR_FLAG__SBI_OPT_ERR (0x0080) +#define PM_ERR_FLAG__FEATURE_NOT_SUPPORTED (0x0100) + +#define PMIC_BUFF_SIZE 256 + +static DEFINE_MUTEX(pmic_mutex); +static struct msm_rpc_endpoint *pmic_ept; + + +static int modem_to_linux_err(uint err) +{ + if (err == 0) + return 0; + + if (err & PM_ERR_FLAG__ALL_PARMS_OUT_OF_RANGE) + return -EINVAL; + + if (err & PM_ERR_FLAG__SBI_OPT_ERR) + return -EIO; + + if (err & PM_ERR_FLAG__FEATURE_NOT_SUPPORTED) + return -ENOSYS; + + return -EPERM; +} + + +/* + * 1) network byte order + * 2) RPC request header(40 bytes) and RPC reply header (24 bytes) + * 3) each transaction consists of a request and reply + * 3) PROC (comamnd) layer has its own sub-protocol defined + * 4) sub-protocol can be grouped to follwoing 7 cases: + * a) set one argument, no get + * b) set two argument, no get + * c) set three argument, no get + * d) set a struct, no get + * e) set a argument followed by a struct, no get + * f) set a argument, get a argument + * g) no set, get either a argument or a struct + */ + +/* Returns number of reply bytes (minus reply header size) or + * negative value on error. + */ +static int pmic_rpc(int proc, void *msg, int msglen, void *rep, int replen) +{ + int r; + mutex_lock(&pmic_mutex); + + if (!pmic_ept) { + pmic_ept = msm_rpc_connect(PMIC_RPC_PROG, PMIC_RPC_VER, 0); + if (!pmic_ept) { + pr_err("pmic: cannot connect to rpc server\n"); + r = -ENODEV; + goto done; + } + } + r = msm_rpc_call_reply(pmic_ept, proc, msg, msglen, + rep, replen, PMIC_RPC_TIMEOUT); + if (r >= 0) { + if (r < sizeof(struct rpc_reply_hdr)) { + r = -EIO; + goto done; + } + r -= sizeof(struct rpc_reply_hdr); + } +done: + mutex_unlock(&pmic_mutex); + return r; +} + +struct pmic_reply { + struct rpc_reply_hdr hdr; + uint32_t status; + uint32_t data; +}; + +/** + * pmic_rpc_set_only() - set arguments and no get + * @data0: first argumrnt + * @data1: second argument + * @data2: third argument + * @data3: fourth argument + * @num: number of argument + * @proc: command/request id + * + * This function covers case a, b, and c + */ +static int pmic_rpc_set_only(uint data0, uint data1, uint data2, uint data3, + int num, int proc) +{ + struct { + struct rpc_request_hdr hdr; + uint32_t data[4]; + } msg; + struct pmic_reply rep; + int r; + + if (num > 4) + return -EINVAL; + + msg.data[0] = cpu_to_be32(data0); + msg.data[1] = cpu_to_be32(data1); + msg.data[2] = cpu_to_be32(data2); + msg.data[3] = cpu_to_be32(data3); + + r = pmic_rpc(proc, &msg, + sizeof(struct rpc_request_hdr) + num * sizeof(uint32_t), + &rep, sizeof(rep)); + if (r < 0) + return r; + if (r < sizeof(uint32_t)) + return -EIO; + + return modem_to_linux_err(be32_to_cpu(rep.status)); +} + +/** + * pmic_rpc_set_struct() - set the whole struct + * @xflag: indicates an extra argument + * @xdata: the extra argument + * @*data: starting address of struct + * @size: size of struct + * @proc: command/request id + * + * This fucntion covers case d and e + */ +static int pmic_rpc_set_struct(int xflag, uint xdata, uint *data, uint size, + int proc) +{ + struct { + struct rpc_request_hdr hdr; + uint32_t data[32+2]; + } msg; + struct pmic_reply rep; + int n = 0; + + size = (size + 3) & (~3); + if (size > (32 * sizeof(uint32_t))) + return -EINVAL; + + if (xflag) + msg.data[n++] = cpu_to_be32(xdata); + + msg.data[n++] = cpu_to_be32(1); + while (size > 0) { + size -= 4; + msg.data[n++] = cpu_to_be32(*data++); + } + + n = pmic_rpc(proc, &msg, + sizeof(struct rpc_request_hdr) + n * sizeof(uint32_t), + &rep, sizeof(rep)); + if (n < 0) + return n; + if (n < sizeof(uint32_t)) + return -EIO; + + return modem_to_linux_err(be32_to_cpu(rep.status)); +} + +int pmic_lp_mode_control(enum switch_cmd cmd, enum vreg_lp_id id) +{ + return pmic_rpc_set_only(cmd, id, 0, 0, 2, LP_MODE_CONTROL_PROC); +} +EXPORT_SYMBOL(pmic_lp_mode_control); + +int pmic_secure_mpp_control_digital_output(enum mpp_which which, + enum mpp_dlogic_level level, + enum mpp_dlogic_out_ctrl out) +{ + return pmic_rpc_set_only(which, level, out, 0, 3, + SECURE_MPP_CONFIG_DIGITAL_OUTPUT_PROC); +} +EXPORT_SYMBOL(pmic_secure_mpp_control_digital_output); + +int pmic_secure_mpp_config_i_sink(enum mpp_which which, + enum mpp_i_sink_level level, + enum mpp_i_sink_switch onoff) +{ + return pmic_rpc_set_only(which, level, onoff, 0, 3, + SECURE_MPP_CONFIG_I_SINK_PROC); +} +EXPORT_SYMBOL(pmic_secure_mpp_config_i_sink); + +int pmic_secure_mpp_config_digital_input(enum mpp_which which, + enum mpp_dlogic_level level, + enum mpp_dlogic_in_dbus dbus) +{ + return pmic_rpc_set_only(which, level, dbus, 0, 3, + SECURE_MPP_CONFIG_DIGITAL_INPUT_PROC); +} +EXPORT_SYMBOL(pmic_secure_mpp_config_digital_input); + +int pmic_rtc_start(struct rtc_time *time) +{ + return pmic_rpc_set_struct(0, 0, (uint *)time, sizeof(*time), + RTC_START_PROC); +} +EXPORT_SYMBOL(pmic_rtc_start); + +int pmic_rtc_stop(void) +{ + return pmic_rpc_set_only(0, 0, 0, 0, 0, RTC_STOP_PROC); +} +EXPORT_SYMBOL(pmic_rtc_stop); + +int pmic_rtc_enable_alarm(enum rtc_alarm alarm, + struct rtc_time *time) +{ + return pmic_rpc_set_struct(1, alarm, (uint *)time, sizeof(*time), + RTC_ENABLE_ALARM_PROC); +} +EXPORT_SYMBOL(pmic_rtc_enable_alarm); + +int pmic_rtc_disable_alarm(enum rtc_alarm alarm) +{ + return pmic_rpc_set_only(alarm, 0, 0, 0, 1, RTC_DISABLE_ALARM_PROC); +} +EXPORT_SYMBOL(pmic_rtc_disable_alarm); + +int pmic_rtc_set_time_adjust(uint adjust) +{ + return pmic_rpc_set_only(adjust, 0, 0, 0, 1, + RTC_SET_TIME_ADJUST_PROC); +} +EXPORT_SYMBOL(pmic_rtc_set_time_adjust); + +/* + * generic speaker + */ +int pmic_speaker_cmd(const enum spkr_cmd cmd) +{ + return pmic_rpc_set_only(cmd, 0, 0, 0, 1, SPEAKER_CMD_PROC); +} +EXPORT_SYMBOL(pmic_speaker_cmd); + +int pmic_set_spkr_configuration(struct spkr_config_mode *cfg) +{ + return pmic_rpc_set_struct(0, 0, (uint *)cfg, sizeof(*cfg), + SET_SPKR_CONFIGURATION_PROC); +} +EXPORT_SYMBOL(pmic_set_spkr_configuration); + +int pmic_spkr_en_right_chan(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_EN_RIGHT_CHAN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_right_chan); + +int pmic_spkr_en_left_chan(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_EN_LEFT_CHAN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_left_chan); + +int pmic_set_speaker_gain(enum spkr_gain gain) +{ + return pmic_rpc_set_only(gain, 0, 0, 0, 1, SET_SPEAKER_GAIN_PROC); +} +EXPORT_SYMBOL(pmic_set_speaker_gain); + +int pmic_set_speaker_delay(enum spkr_dly delay) +{ + return pmic_rpc_set_only(delay, 0, 0, 0, 1, SET_SPEAKER_DELAY_PROC); +} +EXPORT_SYMBOL(pmic_set_speaker_delay); + +int pmic_speaker_1k6_zin_enable(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, + SPEAKER_1K6_ZIN_ENABLE_PROC); +} +EXPORT_SYMBOL(pmic_speaker_1k6_zin_enable); + +int pmic_spkr_set_mux_hpf_corner_freq(enum spkr_hpf_corner_freq freq) +{ + return pmic_rpc_set_only(freq, 0, 0, 0, 1, + SPKR_SET_MUX_HPF_CORNER_FREQ_PROC); +} +EXPORT_SYMBOL(pmic_spkr_set_mux_hpf_corner_freq); + +int pmic_spkr_select_usb_with_hpf_20hz(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, + SPKR_SELECT_USB_WITH_HPF_20HZ_PROC); +} +EXPORT_SYMBOL(pmic_spkr_select_usb_with_hpf_20hz); + +int pmic_spkr_bypass_mux(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_BYPASS_MUX_PROC); +} +EXPORT_SYMBOL(pmic_spkr_bypass_mux); + +int pmic_spkr_en_hpf(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_EN_HPF_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_hpf); + +int pmic_spkr_en_sink_curr_from_ref_volt_cir(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, + SPKR_EN_SINK_CURR_FROM_REF_VOLT_CIR_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_sink_curr_from_ref_volt_cir); + +/* + * speaker indexed by left_right + */ +int pmic_spkr_en(enum spkr_left_right left_right, uint enable) +{ + return pmic_rpc_set_only(left_right, enable, 0, 0, 2, SPKR_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en); + +int pmic_spkr_set_gain(enum spkr_left_right left_right, enum spkr_gain gain) +{ + return pmic_rpc_set_only(left_right, gain, 0, 0, 2, SPKR_SET_GAIN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_set_gain); + +int pmic_spkr_set_delay(enum spkr_left_right left_right, enum spkr_dly delay) +{ + return pmic_rpc_set_only(left_right, delay, 0, 0, 2, + SPKR_SET_DELAY_PROC); +} +EXPORT_SYMBOL(pmic_spkr_set_delay); + +int pmic_spkr_en_mute(enum spkr_left_right left_right, uint enabled) +{ + return pmic_rpc_set_only(left_right, enabled, 0, 0, 2, + SPKR_EN_MUTE_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_mute); + +/* + * mic + */ +int pmic_mic_en(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, MIC_EN_PROC); +} +EXPORT_SYMBOL(pmic_mic_en); + +int pmic_mic_set_volt(enum mic_volt vol) +{ + return pmic_rpc_set_only(vol, 0, 0, 0, 1, MIC_SET_VOLT_PROC); +} +EXPORT_SYMBOL(pmic_mic_set_volt); + +int pmic_vib_mot_set_volt(uint vol) +{ + return pmic_rpc_set_only(vol, 0, 0, 0, 1, VIB_MOT_SET_VOLT_PROC); +} +EXPORT_SYMBOL(pmic_vib_mot_set_volt); + +int pmic_vib_mot_set_mode(enum pm_vib_mot_mode mode) +{ + return pmic_rpc_set_only(mode, 0, 0, 0, 1, VIB_MOT_SET_MODE_PROC); +} +EXPORT_SYMBOL(pmic_vib_mot_set_mode); + +int pmic_vib_mot_set_polarity(enum pm_vib_mot_pol pol) +{ + return pmic_rpc_set_only(pol, 0, 0, 0, 1, VIB_MOT_SET_POLARITY_PROC); +} +EXPORT_SYMBOL(pmic_vib_mot_set_polarity); + +int pmic_vid_en(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, VID_EN_PROC); +} +EXPORT_SYMBOL(pmic_vid_en); + +int pmic_vid_load_detect_en(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, VID_LOAD_DETECT_EN_PROC); +} +EXPORT_SYMBOL(pmic_vid_load_detect_en); + +int pmic_set_led_intensity(enum ledtype type, int level) +{ + return pmic_rpc_set_only(type, level, 0, 0, 2, SET_LED_INTENSITY_PROC); +} +EXPORT_SYMBOL(pmic_set_led_intensity); + +int pmic_flash_led_set_current(const uint16_t milliamps) +{ + return pmic_rpc_set_only(milliamps, 0, 0, 0, 1, + FLASH_LED_SET_CURRENT_PROC); +} +EXPORT_SYMBOL(pmic_flash_led_set_current); + +int pmic_flash_led_set_mode(enum flash_led_mode mode) +{ + return pmic_rpc_set_only((int)mode, 0, 0, 0, 1, + FLASH_LED_SET_MODE_PROC); +} +EXPORT_SYMBOL(pmic_flash_led_set_mode); + +int pmic_flash_led_set_polarity(enum flash_led_pol pol) +{ + return pmic_rpc_set_only((int)pol, 0, 0, 0, 1, + FLASH_LED_SET_POLARITY_PROC); +} +EXPORT_SYMBOL(pmic_flash_led_set_polarity); + +int pmic_spkr_add_right_left_chan(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, + SPKR_ADD_RIGHT_LEFT_CHAN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_add_right_left_chan); + +int pmic_spkr_en_stereo(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_EN_STEREO_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_stereo); + diff --git a/arch/arm/mach-msm/pmic.h b/arch/arm/mach-msm/pmic.h new file mode 100644 index 0000000000000..14ad7897953d3 --- /dev/null +++ b/arch/arm/mach-msm/pmic.h @@ -0,0 +1,310 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __ARCH_ARM_MACH_PMIC_H +#define __ARCH_ARM_MACH_PMIC_H + +#include "proc_comm.h" + +enum spkr_left_right { + LEFT_SPKR, + RIGHT_SPKR, +}; + +enum spkr_gain { + SPKR_GAIN_MINUS16DB, /* -16 db */ + SPKR_GAIN_MINUS12DB, /* -12 db */ + SPKR_GAIN_MINUS08DB, /* -08 db */ + SPKR_GAIN_MINUS04DB, /* -04 db */ + SPKR_GAIN_00DB, /* 00 db */ + SPKR_GAIN_PLUS04DB, /* +04 db */ + SPKR_GAIN_PLUS08DB, /* +08 db */ + SPKR_GAIN_PLUS12DB, /* +12 db */ +}; + +enum spkr_dly { + SPKR_DLY_10MS, /* ~10 ms delay */ + SPKR_DLY_100MS, /* ~100 ms delay */ +}; + +enum spkr_hpf_corner_freq { + SPKR_FREQ_1_39KHZ, /* 1.39 kHz */ + SPKR_FREQ_0_64KHZ, /* 0.64 kHz */ + SPKR_FREQ_0_86KHZ, /* 0.86 kHz */ + SPKR_FREQ_0_51KHZ, /* 0.51 kHz */ + SPKR_FREQ_1_06KHZ, /* 1.06 kHz */ + SPKR_FREQ_0_57KHZ, /* 0.57 kHz */ + SPKR_FREQ_0_73KHZ, /* 0.73 kHz */ + SPKR_FREQ_0_47KHZ, /* 0.47 kHz */ + SPKR_FREQ_1_20KHZ, /* 1.20 kHz */ + SPKR_FREQ_0_60KHZ, /* 0.60 kHz */ + SPKR_FREQ_0_76KHZ, /* 0.76 kHz */ + SPKR_FREQ_0_49KHZ, /* 0.49 kHz */ + SPKR_FREQ_0_95KHZ, /* 0.95 kHz */ + SPKR_FREQ_0_54KHZ, /* 0.54 kHz */ + SPKR_FREQ_0_68KHZ, /* 0.68 kHz */ + SPKR_FREQ_0_45KHZ, /* 0.45 kHz */ +}; + +/* Turn the speaker on or off and enables or disables mute.*/ +enum spkr_cmd { + SPKR_DISABLE, /* Enable Speaker */ + SPKR_ENABLE, /* Disable Speaker */ + SPKR_MUTE_OFF, /* turn speaker mute off, SOUND ON */ + SPKR_MUTE_ON, /* turn speaker mute on, SOUND OFF */ + SPKR_OFF, /* turn speaker OFF (speaker disable and mute on) */ + SPKR_ON, /* turn speaker ON (speaker enable and mute off) */ + SPKR_SET_FREQ_CMD, /* set speaker frequency */ + SPKR_GET_FREQ_CMD, /* get speaker frequency */ + SPKR_SET_GAIN_CMD, /* set speaker gain */ + SPKR_GET_GAIN_CMD, /* get speaker gain */ + SPKR_SET_DELAY_CMD, /* set speaker delay */ + SPKR_GET_DELAY_CMD, /* get speaker delay */ + SPKR_SET_PDM_MODE, + SPKR_SET_PWM_MODE, +}; + +struct spkr_config_mode { + uint32_t is_right_chan_en; + uint32_t is_left_chan_en; + uint32_t is_right_left_chan_added; + uint32_t is_stereo_en; + uint32_t is_usb_with_hpf_20hz; + uint32_t is_mux_bypassed; + uint32_t is_hpf_en; + uint32_t is_sink_curr_from_ref_volt_cir_en; +}; + +enum mic_volt { + MIC_VOLT_2_00V, /* 2.00 V */ + MIC_VOLT_1_93V, /* 1.93 V */ + MIC_VOLT_1_80V, /* 1.80 V */ + MIC_VOLT_1_73V, /* 1.73 V */ +}; + +enum ledtype { + LED_LCD, + LED_KEYPAD, +}; + +enum flash_led_mode { + FLASH_LED_MODE__MANUAL, + FLASH_LED_MODE__DBUS1, + FLASH_LED_MODE__DBUS2, + FLASH_LED_MODE__DBUS3, +}; + +enum flash_led_pol { + FLASH_LED_POL__ACTIVE_HIGH, + FLASH_LED_POL__ACTIVE_LOW, +}; + +enum switch_cmd { + OFF_CMD, + ON_CMD +}; + +enum vreg_lp_id { + PM_VREG_LP_MSMA_ID, + PM_VREG_LP_MSMP_ID, + PM_VREG_LP_MSME1_ID, + PM_VREG_LP_GP3_ID, + PM_VREG_LP_MSMC_ID, + PM_VREG_LP_MSME2_ID, + PM_VREG_LP_GP4_ID, + PM_VREG_LP_GP1_ID, + PM_VREG_LP_RFTX_ID, + PM_VREG_LP_RFRX1_ID, + PM_VREG_LP_RFRX2_ID, + PM_VREG_LP_WLAN_ID, + PM_VREG_LP_MMC_ID, + PM_VREG_LP_RUIM_ID, + PM_VREG_LP_MSMC0_ID, + PM_VREG_LP_GP2_ID, + PM_VREG_LP_GP5_ID, + PM_VREG_LP_GP6_ID, + PM_VREG_LP_MPLL_ID, + PM_VREG_LP_RFUBM_ID, + PM_VREG_LP_RFA_ID, + PM_VREG_LP_CDC2_ID, + PM_VREG_LP_RFTX2_ID, + PM_VREG_LP_USIM_ID, + PM_VREG_LP_USB2P6_ID, + PM_VREG_LP_TCXO_ID, + PM_VREG_LP_USB3P3_ID, + + PM_VREG_LP_MSME_ID = PM_VREG_LP_MSME1_ID, + /* backward compatible enums only */ + PM_VREG_LP_CAM_ID = PM_VREG_LP_GP1_ID, + PM_VREG_LP_MDDI_ID = PM_VREG_LP_GP2_ID, + PM_VREG_LP_RUIM2_ID = PM_VREG_LP_GP3_ID, + PM_VREG_LP_AUX_ID = PM_VREG_LP_GP4_ID, + PM_VREG_LP_AUX2_ID = PM_VREG_LP_GP5_ID, + PM_VREG_LP_BT_ID = PM_VREG_LP_GP6_ID, + PM_VREG_LP_MSMC_LDO_ID = PM_VREG_LP_MSMC_ID, + PM_VREG_LP_MSME1_LDO_ID = PM_VREG_LP_MSME1_ID, + PM_VREG_LP_MSME2_LDO_ID = PM_VREG_LP_MSME2_ID, + PM_VREG_LP_RFA1_ID = PM_VREG_LP_RFRX2_ID, + PM_VREG_LP_RFA2_ID = PM_VREG_LP_RFTX2_ID, + PM_VREG_LP_XO_ID = PM_VREG_LP_TCXO_ID +}; + +enum mpp_which { + PM_MPP_1, + PM_MPP_2, + PM_MPP_3, + PM_MPP_4, + PM_MPP_5, + PM_MPP_6, + PM_MPP_7, + PM_MPP_8, + PM_MPP_9, + PM_MPP_10, + PM_MPP_11, + PM_MPP_12, + PM_MPP_13, + PM_MPP_14, + PM_MPP_15, + PM_MPP_16, + PM_MPP_17, + PM_MPP_18, + PM_MPP_19, + PM_MPP_20, + PM_MPP_21, + PM_MPP_22, + + PM_NUM_MPP_HAN = PM_MPP_4 + 1, + PM_NUM_MPP_KIP = PM_MPP_4 + 1, + PM_NUM_MPP_EPIC = PM_MPP_4 + 1, + PM_NUM_MPP_PM7500 = PM_MPP_22 + 1, + PM_NUM_MPP_PM6650 = PM_MPP_12 + 1, + PM_NUM_MPP_PM6658 = PM_MPP_12 + 1, + PM_NUM_MPP_PANORAMIX = PM_MPP_2 + 1, + PM_NUM_MPP_PM6640 = PM_NUM_MPP_PANORAMIX, + PM_NUM_MPP_PM6620 = PM_NUM_MPP_PANORAMIX +}; + +enum mpp_dlogic_level { + PM_MPP__DLOGIC__LVL_MSME, + PM_MPP__DLOGIC__LVL_MSMP, + PM_MPP__DLOGIC__LVL_RUIM, + PM_MPP__DLOGIC__LVL_MMC, + PM_MPP__DLOGIC__LVL_VDD, +}; + +enum mpp_dlogic_in_dbus { + PM_MPP__DLOGIC_IN__DBUS_NONE, + PM_MPP__DLOGIC_IN__DBUS1, + PM_MPP__DLOGIC_IN__DBUS2, + PM_MPP__DLOGIC_IN__DBUS3, +}; + +enum mpp_dlogic_out_ctrl { + PM_MPP__DLOGIC_OUT__CTRL_LOW, + PM_MPP__DLOGIC_OUT__CTRL_HIGH, + PM_MPP__DLOGIC_OUT__CTRL_MPP, + PM_MPP__DLOGIC_OUT__CTRL_NOT_MPP, +}; + +enum mpp_i_sink_level { + PM_MPP__I_SINK__LEVEL_5mA, + PM_MPP__I_SINK__LEVEL_10mA, + PM_MPP__I_SINK__LEVEL_15mA, + PM_MPP__I_SINK__LEVEL_20mA, + PM_MPP__I_SINK__LEVEL_25mA, + PM_MPP__I_SINK__LEVEL_30mA, + PM_MPP__I_SINK__LEVEL_35mA, + PM_MPP__I_SINK__LEVEL_40mA, +}; + +enum mpp_i_sink_switch { + PM_MPP__I_SINK__SWITCH_DIS, + PM_MPP__I_SINK__SWITCH_ENA, + PM_MPP__I_SINK__SWITCH_ENA_IF_MPP_HIGH, + PM_MPP__I_SINK__SWITCH_ENA_IF_MPP_LOW, +}; + +enum pm_vib_mot_mode { + PM_VIB_MOT_MODE__MANUAL, + PM_VIB_MOT_MODE__DBUS1, + PM_VIB_MOT_MODE__DBUS2, + PM_VIB_MOT_MODE__DBUS3, +}; + +enum pm_vib_mot_pol { + PM_VIB_MOT_POL__ACTIVE_HIGH, + PM_VIB_MOT_POL__ACTIVE_LOW, +}; + +struct rtc_time { + uint sec; +}; + +enum rtc_alarm { + PM_RTC_ALARM_1, +}; + + +int pmic_lp_mode_control(enum switch_cmd cmd, enum vreg_lp_id id); +int pmic_secure_mpp_control_digital_output(enum mpp_which which, + enum mpp_dlogic_level level, enum mpp_dlogic_out_ctrl out); +int pmic_secure_mpp_config_i_sink(enum mpp_which which, + enum mpp_i_sink_level level, enum mpp_i_sink_switch onoff); +int pmic_secure_mpp_config_digital_input(enum mpp_which which, + enum mpp_dlogic_level level, enum mpp_dlogic_in_dbus dbus); +int pmic_speaker_cmd(const enum spkr_cmd cmd); +int pmic_set_spkr_configuration(struct spkr_config_mode *cfg); +int pmic_spkr_en_right_chan(uint enable); +int pmic_spkr_en_left_chan(uint enable); +int pmic_spkr_en(enum spkr_left_right left_right, uint enabled); +int pmic_spkr_set_gain(enum spkr_left_right left_right, enum spkr_gain gain); +int pmic_set_speaker_gain(enum spkr_gain gain); +int pmic_set_speaker_delay(enum spkr_dly delay); +int pmic_speaker_1k6_zin_enable(uint enable); +int pmic_spkr_set_mux_hpf_corner_freq(enum spkr_hpf_corner_freq freq); +int pmic_spkr_select_usb_with_hpf_20hz(uint enable); +int pmic_spkr_bypass_mux(uint enable); +int pmic_spkr_en_hpf(uint enable); +int pmic_spkr_en_sink_curr_from_ref_volt_cir(uint enable); +int pmic_spkr_set_delay(enum spkr_left_right left_right, enum spkr_dly delay); +int pmic_spkr_en_mute(enum spkr_left_right left_right, uint enabled); +int pmic_mic_en(uint enable); +int pmic_mic_set_volt(enum mic_volt vol); +int pmic_set_led_intensity(enum ledtype type, int level); +int pmic_flash_led_set_current(uint16_t milliamps); +int pmic_flash_led_set_mode(enum flash_led_mode mode); +int pmic_flash_led_set_polarity(enum flash_led_pol pol); +int pmic_spkr_add_right_left_chan(uint enable); +int pmic_spkr_en_stereo(uint enable); +int pmic_vib_mot_set_volt(uint vol); +int pmic_vib_mot_set_mode(enum pm_vib_mot_mode mode); +int pmic_vib_mot_set_polarity(enum pm_vib_mot_pol pol); +int pmic_vid_en(uint enable); +int pmic_vid_load_detect_en(uint enable); + +#endif From e08c81f6ac806c604faa1f439a0ad35571bad194 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Mon, 8 Feb 2010 20:02:43 -0800 Subject: [PATCH 0426/2556] [ARM] msm: qsd8k: DSP / DAL / Audio / Video MEGAPATCH [ARM] msm: qsd8k: Device Access Layer (DAL) RPC Device Access Layer (DAL) RPC is yet another remote interface to software on the baseband and dsp processors of the msm7k/qsd8k family. It uses SMD as a transport. Signed-off-by: Brian Swetland [ARM] msm: Add qdsp6 config option and makefile Change-Id: I6120d03a96339139d4d88f32e165d26b75ac805e Signed-off-by: Dima Zavin {ARM] msm: qsd8k: support for qdsp6 audio subsystem Provide support for QDSP6's AUDIO service, as well as the A9's ACDB (Audio Config Database) and ADIE services, necessary for audio operation. Provide pcm in/out drivers. Major contributions, and submitted on behalf of HK Chen @ HTC. Signed-off-by: Brian Swetland Cc: HK Chen [ARM] msm: qdsp6: Add video decode support for AMSS 3180+ - sometimes dal_call_f0 rpc returns failure need to investigate Signed-off-by: Dima Zavin [ARM]: msm: q6vdec: close the dsp in vdec_release to free up the pmem and proper cleanup of qdsp6 Signed-off-by: Tom Liu Signed-off-by: Colin Cross Change-Id: I6c9203685abbc50c8ef9633dfc99024dd720e17a qsd8k: vdec: hold wake/idlelocks while decoder is active DSP interrupts are being lost if we go to sleep while playing video. This usually doesn't happen because we usually have an audio channel open, providing its own wake/idlelocks, but this fixes the other case. Signed-off-by: Brian Swetland qsd8k: vdec: don't leak wakelocks Fix a stupid cut-and-paste error. Signed-off-by: Brian Swetland [ARM] msm: qdsp6: Add support for h/w video encode. Change-Id: If3d8c1d836955f834f4988f364cce07fce22bfa6 Signed-off-by: Dima Zavin [ARM] msm: qdsp6: flush and invalidate cahces in venc Signed-off-by: Erik Gilling [ARM] msm: qdsp6: Invalidate video encoder output buffers before encode When an output buffer is passed to the DSP, we have to invalidate it first to make sure no dirty cache lines are later written to the physical memory while the DSP is using it. Also fixes problems with cleaning the input buffer cache before the DSP. Userspace is not setting the size of the input buffer on each ioctl, so we have to pre-compute the size based on the encoder paramters. Signed-off-by: Colin Cross Change-Id: I7a858f0d833e82b296ac7e32eeaff820d52acba9 [ARM] msm: qdsp6: Invalidate encoder buffers before DSP initialization The DSP may write to the rlc_bufs during initialization. Invalidate them so that a later flush doesn't cause corruption. Change-Id: Ic482334526712c14e48c790dbeeb7c0f5e1d0cbc Signed-off-by: Colin Cross [ARM] msm: q6venc: Use flush_pmem_file to flush the input buffers to the DSP Change-Id: I37ca4ee3e85144cf1f0ccacceb3e2d7d5f0b334c Signed-off-by: Colin Cross [ARM] msm: q6_venc: detach the dal client before releasing the buffers Change-Id: Ib96c0fb6227c53b97a68d51d627f6fe2d45b5aee Signed-off-by: Dima Zavin qsd8k: audio: combined changes qsd8k: audio: handle transient errors during pcm stream open Sometimes we get an EUNSUPPORTED (?!) error while trying to open a pcm stream. If we retry in 1ms it succeeds. Add some debugging/tracing code handy for diagnosing issues like this (disabled by default). Signed-off-by: Brian Swetland [ARM] msm: qdsp: add wakelocks Signed-off-by: Mike Chan qsd8k: audio: move to new dsp protocol (3200.04+) Signed-off-by: Brian Swetland qsd8k: audio: fix q6audio_update_acdb(). Correctly indicate RX or TX device. Signed-off-by: Brian Swetland qsd8k: add receiver analog control Signed-off-by: Brian Swetland qsd8k: audio: support ACDB reinitialization Signed-off-by: Brian Swetland qsd8k: audio: add support for stream volume. Signed-off-by: Brian Swetland qsd8k: audio: fix recording fail issue when sampling rate higher than 8K. Signed-off-by: Brian Swetland qsd8k: audio: change the buffer size in pcm driver from 8192 to 4800. Userspace expects 4800 default. Easier to fix it on this side. Signed-off-by: Brian Swetland qsd8k: audio: tracing, and stuck dsp panic code If the dsp doesn't respond in 5s, it will probably never respond, leaving the device in a bad state -- panic in that case. Add some debug chatter on pcm in/out open and close as well. Signed-off-by: Brian Swetland qsd8k: audio: fix volume control. Per QCT's comments, volume control only works when there are active audio/voice sessions, or DSP will return status 20 (UNSUPPORTED). To solve this issue, QCT has introduced new voice device ID which is using for in-call volume control. It will directly apply the volume setting to next voice call. Also, for in-call uplink volume, remove the redundant volume setting and use the default setting in DSP (0dB as default). Signed-off-by: Brian Swetland qsd8k: audio: fix ordering of analog/digital power up sequencing for tx Avoids noise on audio input. Change-Id: Ib1baab87e99a09f9e1284d32cf1d6a5b5fc3a6c8 Signed-off-by: Brian Swetland qsd8k: dal: add tracing capabilities to DAL channels If requested, log the last 32 calls, replies, and/or events. Signed-off-by: Brian Swetland qsd8k: trace dal io and dump it on fatal error Signed-off-by: Brian Swetland qsd8k: audio: correctly release the firmware on acdb change Signed-off-by: Brian Swetland qsd8k: audio: fix bongus ID look up in q6audio_set_rx_volume(). Signed-off-by: Brian Swetland qsd8k: audio: enhance stuck dal debugging Dump the packet we were in the act of sending, if we timed out while waiting for a reply. This covers the situation of failing on a different channel than one being traced. Signed-off-by: Brian Swetland qsd8k: audio: pcm_out: default buffer size is now 3072 Signed-off-by: Brian Swetland [ARM] qsd8k: audio: fix typo in q6audio_devices.h Signed-off-by: Iliyan Malchev qsd8k: audio combined changes qsd8k: audio: clean up code for ACDB update. Signed-off-by: Brian Swetland qsd8k: audio: remove redundant ACDB updates for audio playback and recording. q6audio_open_*() now takes an acdb_id to allow the device to be changed as part of the channel open process. Signed-off-by: Brian Swetland qsd8k: audio: remove redundant ACDB updates during audio route changes. Audio HAL will pass the ACDB ID to audio driver when audio route changes. Signed-off-by: Brian Swetland qsd8k: audio: change the timing for ADIE enable/disable. Avoid pop noises. Signed-off-by: Brian Swetland qsd8k: audio: change the timing when ACDB updates to avoid tick sound during voice call. Signed-off-by: Brian Swetland qsd8k: audio: do not require acdb_ids on channel open This allows the simple case of using the default device to work. Signed-off-by: Brian Swetland qsd8k: dal: don't use dch before setting it Harmless on a UP, non-spinlock-debug build, but quite very wrong. Signed-off-by: Brian Swetland qsd8k: audio: reorder audio path setup around pcm open Some clicks and pops on stream start are avoided if the audio path setup is deferred until after the stream is opened but before the stream is started. The whole audio path management scheme wants some serious rewriting, but for now we'll stick with a minimal-risk change. Signed-off-by: Brian Swetland [ARM] qsd8k: initialize the spinlock in dal_client Signed-off-by: Iliyan Malchev qsd8k: audio: avoid incorrect sampling rate on first input pcm open Also avoid double calling audio_tx_analog_enable() Signed-off-by: Brian Swetland Signed-off-by: Paul Eastham [ARM] msm: q6audio: remove device gain control for pcm playback. Change-Id: Id243b6771a6a324931afa86c662eef8b907b213b Signed-off-by: Dima Zavin [ARM] qsd8k: audio: combined changes [ARM] qsd8k: audio: use default settings if ACDB-table query fails Signed-off-by: Iliyan Malchev [ARM] qsd8k: audio: remove device gain control for mp3 playback Fixes the problem where in-call downlink volume gets degraded after tone is played. Signed-off-by: Iliyan Malchev [ARM] qsd8k: audio: add HAC ACDB ID support -- q6_voice_start, q6voice_open take an ACDB-ID parameter -- q6_ioctl AUDIO_START_VOICE passes ACDB ID -- q6voice_open passes the ACDB ID to audio_rx/tx_path_enable Signed-off-by: Iliyan Malchev [ARM] qsdk8k: audio: remove in-call volume control from driver Will move to board file. Signed-off-by: Iliyan Malchev [ARM] qsd8k: audio: add function to override default ACDB file. Signed-off-by: Iliyan Malchev [ARM] qsd8k: audio: enable selection of input and output buffer size -- through AUDIO_SET_CONFIG ioctl -- lowers default buffer size from 4k to 256 bytes Signed-off-by: Eric Laurent --- arch/arm/mach-msm/Kconfig | 8 + arch/arm/mach-msm/Makefile | 1 + .../mach-msm/include/mach/msm_qdsp6_audio.h | 99 ++ arch/arm/mach-msm/qdsp6/Makefile | 9 + arch/arm/mach-msm/qdsp6/analog_audio.c | 67 + arch/arm/mach-msm/qdsp6/audio_ctl.c | 160 ++ arch/arm/mach-msm/qdsp6/dal.c | 600 +++++++ arch/arm/mach-msm/qdsp6/dal.h | 65 + arch/arm/mach-msm/qdsp6/dal_acdb.h | 84 + arch/arm/mach-msm/qdsp6/dal_adie.h | 108 ++ arch/arm/mach-msm/qdsp6/dal_audio.h | 565 ++++++ arch/arm/mach-msm/qdsp6/dal_audio_format.h | 285 +++ arch/arm/mach-msm/qdsp6/mp3.c | 220 +++ arch/arm/mach-msm/qdsp6/msm_q6vdec.c | 941 ++++++++++ arch/arm/mach-msm/qdsp6/msm_q6venc.c | 630 +++++++ arch/arm/mach-msm/qdsp6/pcm_in.c | 219 +++ arch/arm/mach-msm/qdsp6/pcm_out.c | 229 +++ arch/arm/mach-msm/qdsp6/q6audio.c | 1560 +++++++++++++++++ arch/arm/mach-msm/qdsp6/q6audio_devices.h | 265 +++ arch/arm/mach-msm/qdsp6/routing.c | 71 + include/linux/msm_audio.h | 5 + include/linux/msm_q6vdec.h | 230 +++ include/linux/msm_q6venc.h | 125 ++ 23 files changed, 6546 insertions(+) create mode 100644 arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h create mode 100644 arch/arm/mach-msm/qdsp6/Makefile create mode 100644 arch/arm/mach-msm/qdsp6/analog_audio.c create mode 100644 arch/arm/mach-msm/qdsp6/audio_ctl.c create mode 100644 arch/arm/mach-msm/qdsp6/dal.c create mode 100644 arch/arm/mach-msm/qdsp6/dal.h create mode 100644 arch/arm/mach-msm/qdsp6/dal_acdb.h create mode 100644 arch/arm/mach-msm/qdsp6/dal_adie.h create mode 100644 arch/arm/mach-msm/qdsp6/dal_audio.h create mode 100644 arch/arm/mach-msm/qdsp6/dal_audio_format.h create mode 100644 arch/arm/mach-msm/qdsp6/mp3.c create mode 100644 arch/arm/mach-msm/qdsp6/msm_q6vdec.c create mode 100644 arch/arm/mach-msm/qdsp6/msm_q6venc.c create mode 100644 arch/arm/mach-msm/qdsp6/pcm_in.c create mode 100644 arch/arm/mach-msm/qdsp6/pcm_out.c create mode 100644 arch/arm/mach-msm/qdsp6/q6audio.c create mode 100644 arch/arm/mach-msm/qdsp6/q6audio_devices.h create mode 100644 arch/arm/mach-msm/qdsp6/routing.c create mode 100644 include/linux/msm_q6vdec.h create mode 100755 include/linux/msm_q6venc.h diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index dab91bc3eee6f..870718ada5fec 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -456,6 +456,7 @@ config MSM_HW3D library. config MSM_ADSP + depends on ARCH_MSM7X00A tristate "MSM ADSP driver" default y help @@ -469,6 +470,13 @@ config MSM_ADSP_REPORT_EVENTS Normally, only messages from the aDSP are reported to userspace. With this option, we report events from the aDSP as well. +config MSM_QDSP6 + tristate "QDSP6 support" + depends on ARCH_QSD8X50 + default y + help + Enable support for qdsp6. This provides audio and video functionality. + config WIFI_CONTROL_FUNC bool "Enable WiFi control function abstraction" help diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 3da4dc160a9a2..09c614e81df44 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_dog_keepalive.o obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_time_remote.o obj-$(CONFIG_MSM_ADSP) += qdsp5/ +obj-$(CONFIG_MSM_QDSP6) += qdsp6/ obj-$(CONFIG_MSM_HW3D) += hw3d.o obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o diff --git a/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h new file mode 100644 index 0000000000000..ef8b3d127a29f --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h @@ -0,0 +1,99 @@ +/* arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * 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 _MACH_MSM_QDSP6_Q6AUDIO_ +#define _MACH_MSM_QDSP6_Q6AUDIO_ + +#define AUDIO_FLAG_READ 0 +#define AUDIO_FLAG_WRITE 1 + +struct audio_buffer { + dma_addr_t phys; + void *data; + uint32_t size; + uint32_t used; /* 1 = CPU is waiting for DSP to consume this buf */ +}; + +struct audio_client { + struct audio_buffer buf[2]; + int cpu_buf; /* next buffer the CPU will touch */ + int dsp_buf; /* next buffer the DSP will touch */ + int running; + int session; + + wait_queue_head_t wait; + struct dal_client *client; + + int cb_status; + uint32_t flags; +}; + +#define Q6_HW_HANDSET 0 +#define Q6_HW_HEADSET 1 +#define Q6_HW_SPEAKER 2 +#define Q6_HW_TTY 3 +#define Q6_HW_BT_SCO 4 +#define Q6_HW_BT_A2DP 5 + +#define Q6_HW_COUNT 6 + +struct q6_hw_info { + int min_gain; + int max_gain; +}; + +/* Obtain a 16bit signed, interleaved audio channel of the specified + * rate (Hz) and channels (1 or 2), with two buffers of bufsz bytes. + */ +struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, + uint32_t channels, uint32_t flags, + uint32_t acdb_id); + +struct audio_client *q6voice_open(uint32_t flags, uint32_t acdb_id); + +struct audio_client *q6audio_open_mp3(uint32_t bufsz, uint32_t rate, + uint32_t channels, uint32_t acdb_id); + +int q6audio_close(struct audio_client *ac); +int q6voice_close(struct audio_client *ac); +int q6audio_mp3_close(struct audio_client *ac); + +int q6audio_read(struct audio_client *ac, struct audio_buffer *ab); +int q6audio_write(struct audio_client *ac, struct audio_buffer *ab); +int q6audio_async(struct audio_client *ac); + +int q6audio_do_routing(uint32_t route, uint32_t acdb_id); +int q6audio_set_tx_mute(int mute); +int q6audio_reinit_acdb(char* filename); +int q6audio_update_acdb(uint32_t id_src, uint32_t id_dst); +int q6audio_set_rx_volume(int level); +int q6audio_set_stream_volume(struct audio_client *ac, int vol); + +struct q6audio_analog_ops { + void (*init)(void); + void (*speaker_enable)(int en); + void (*headset_enable)(int en); + void (*receiver_enable)(int en); + void (*bt_sco_enable)(int en); + void (*int_mic_enable)(int en); + void (*ext_mic_enable)(int en); + int (*get_rx_vol)(uint8_t hw, int level); +}; + +void q6audio_register_analog_ops(struct q6audio_analog_ops *ops); +void q6audio_set_acdb_file(char* filename); + +#endif diff --git a/arch/arm/mach-msm/qdsp6/Makefile b/arch/arm/mach-msm/qdsp6/Makefile new file mode 100644 index 0000000000000..2f42decfdf252 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/Makefile @@ -0,0 +1,9 @@ +obj-y += dal.o +obj-y += q6audio.o +obj-y += pcm_out.o +obj-y += pcm_in.o +obj-y += mp3.o +obj-y += routing.o +obj-y += audio_ctl.o +obj-y += msm_q6vdec.o +obj-y += msm_q6venc.o diff --git a/arch/arm/mach-msm/qdsp6/analog_audio.c b/arch/arm/mach-msm/qdsp6/analog_audio.c new file mode 100644 index 0000000000000..3ec80d4e64859 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/analog_audio.c @@ -0,0 +1,67 @@ + +#include +#include "../pmic.h" +#include + +#define GPIO_HEADSET_AMP 157 + +void analog_init(void) +{ + /* stereo pmic init */ + pmic_spkr_set_gain(LEFT_SPKR, SPKR_GAIN_PLUS12DB); + pmic_spkr_set_gain(RIGHT_SPKR, SPKR_GAIN_PLUS12DB); + pmic_mic_set_volt(MIC_VOLT_1_80V); + + gpio_direction_output(GPIO_HEADSET_AMP, 1); + gpio_set_value(GPIO_HEADSET_AMP, 0); +} + +void analog_headset_enable(int en) +{ + /* enable audio amp */ + gpio_set_value(GPIO_HEADSET_AMP, !!en); +} + +void analog_speaker_enable(int en) +{ + struct spkr_config_mode scm; + memset(&scm, 0, sizeof(scm)); + + if (en) { + scm.is_right_chan_en = 1; + scm.is_left_chan_en = 1; + scm.is_stereo_en = 1; + scm.is_hpf_en = 1; + pmic_spkr_en_mute(LEFT_SPKR, 0); + pmic_spkr_en_mute(RIGHT_SPKR, 0); + pmic_set_spkr_configuration(&scm); + pmic_spkr_en(LEFT_SPKR, 1); + pmic_spkr_en(RIGHT_SPKR, 1); + + /* unmute */ + pmic_spkr_en_mute(LEFT_SPKR, 1); + pmic_spkr_en_mute(RIGHT_SPKR, 1); + } else { + pmic_spkr_en_mute(LEFT_SPKR, 0); + pmic_spkr_en_mute(RIGHT_SPKR, 0); + + pmic_spkr_en(LEFT_SPKR, 0); + pmic_spkr_en(RIGHT_SPKR, 0); + + pmic_set_spkr_configuration(&scm); + } +} + +static struct q6audio_analog_ops ops = { + .init = analog_init, + .speaker_enable = analog_speaker_enable, + .headset_enable = analog_headset_enable, +}; + +static int __init init(void) +{ + q6audio_register_analog_ops(&ops); + return 0; +} + +device_initcall(init); diff --git a/arch/arm/mach-msm/qdsp6/audio_ctl.c b/arch/arm/mach-msm/qdsp6/audio_ctl.c new file mode 100644 index 0000000000000..5f9f317babb9b --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/audio_ctl.c @@ -0,0 +1,160 @@ +/* arch/arm/mach-msm/qdsp6/audio_ctrl.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * + * 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 + +#define BUFSZ (0) + +static DEFINE_MUTEX(voice_lock); +static int voice_started; + +static struct audio_client *voc_tx_clnt; +static struct audio_client *voc_rx_clnt; + +static int q6_voice_start(uint32_t rx_acdb_id, uint32_t tx_acdb_id) +{ + int rc = 0; + + mutex_lock(&voice_lock); + + if (voice_started) { + pr_err("voice: busy\n"); + rc = -EBUSY; + goto done; + } + + voc_rx_clnt = q6voice_open(AUDIO_FLAG_WRITE, rx_acdb_id); + if (!voc_rx_clnt) { + pr_err("voice: open voice rx failed.\n"); + rc = -ENOMEM; + goto done; + } + + voc_tx_clnt = q6voice_open(AUDIO_FLAG_READ, tx_acdb_id); + if (!voc_tx_clnt) { + pr_err("voice: open voice tx failed.\n"); + q6voice_close(voc_rx_clnt); + rc = -ENOMEM; + } + + voice_started = 1; +done: + mutex_unlock(&voice_lock); + return rc; +} + +static int q6_voice_stop(void) +{ + mutex_lock(&voice_lock); + if (voice_started) { + q6voice_close(voc_tx_clnt); + q6voice_close(voc_rx_clnt); + voice_started = 0; + } + mutex_unlock(&voice_lock); + return 0; +} + +static int q6_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int q6_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int rc; + uint32_t n; + uint32_t id[2]; + char filename[64]; + + switch (cmd) { + case AUDIO_SWITCH_DEVICE: + rc = copy_from_user(&id, (void *)arg, sizeof(id)); + if (!rc) + rc = q6audio_do_routing(id[0], id[1]); + break; + case AUDIO_SET_VOLUME: + rc = copy_from_user(&n, (void *)arg, sizeof(n)); + if (!rc) + rc = q6audio_set_rx_volume(n); + break; + case AUDIO_SET_MUTE: + rc = copy_from_user(&n, (void *)arg, sizeof(n)); + if (!rc) + rc = q6audio_set_tx_mute(n); + break; + case AUDIO_UPDATE_ACDB: + rc = copy_from_user(&id, (void *)arg, sizeof(id)); + if (!rc) + rc = q6audio_update_acdb(id[0], id[1]); + break; + case AUDIO_START_VOICE: + if (arg == 0) { + id[0] = id[1] = 0; + } else if (copy_from_user(&id, (void*) arg, sizeof(id))) { + pr_info("voice: copy acdb_id from user failed\n"); + rc = -EFAULT; + break; + } + rc = q6_voice_start(id[0], id[1]); + break; + case AUDIO_STOP_VOICE: + rc = q6_voice_stop(); + break; + case AUDIO_REINIT_ACDB: + rc = copy_from_user(&filename, (void *)arg, sizeof(filename)); + if (!rc) + rc = q6audio_reinit_acdb(filename); + break; + default: + rc = -EINVAL; + } + + return rc; +} + + +static int q6_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static struct file_operations q6_dev_fops = { + .owner = THIS_MODULE, + .open = q6_open, + .ioctl = q6_ioctl, + .release = q6_release, +}; + +struct miscdevice q6_control_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_audio_ctl", + .fops = &q6_dev_fops, +}; + + +static int __init q6_audio_ctl_init(void) { + return misc_register(&q6_control_device); +} + +device_initcall(q6_audio_ctl_init); diff --git a/arch/arm/mach-msm/qdsp6/dal.c b/arch/arm/mach-msm/qdsp6/dal.c new file mode 100644 index 0000000000000..584ac32b453e2 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/dal.c @@ -0,0 +1,600 @@ +/* arch/arm/mach-msm/qdsp6/dal.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * 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 "dal.h" + +#define DAL_TRACE 0 + +struct dal_hdr { + uint32_t length:16; /* message length (header inclusive) */ + uint32_t version:8; /* DAL protocol version */ + uint32_t priority:7; + uint32_t async:1; + uint32_t ddi:16; /* DDI method number */ + uint32_t prototype:8; /* DDI serialization format */ + uint32_t msgid:8; /* message id (DDI, ATTACH, DETACH, ...) */ + void *from; + void *to; +} __attribute__((packed)); + +#define TRACE_DATA_MAX 128 +#define TRACE_LOG_MAX 32 +#define TRACE_LOG_MASK (TRACE_LOG_MAX - 1) + +struct dal_trace { + unsigned timestamp; + struct dal_hdr hdr; + uint32_t data[TRACE_DATA_MAX]; +}; + +#define DAL_HDR_SIZE (sizeof(struct dal_hdr)) +#define DAL_DATA_MAX 512 +#define DAL_MSG_MAX (DAL_HDR_SIZE + DAL_DATA_MAX) + +#define DAL_VERSION 0x11 + +#define DAL_MSGID_DDI 0x00 +#define DAL_MSGID_ATTACH 0x01 +#define DAL_MSGID_DETACH 0x02 +#define DAL_MSGID_ASYNCH 0xC0 +#define DAL_MSGID_REPLY 0x80 + +struct dal_channel { + struct list_head list; + struct list_head clients; + + /* synchronization for changing channel state, + * adding/removing clients, smd callbacks, etc + */ + spinlock_t lock; + + struct smd_channel *sch; + char *name; + + /* events are delivered at IRQ context immediately, so + * we only need one assembly buffer for the entire channel + */ + struct dal_hdr hdr; + unsigned char data[DAL_DATA_MAX]; + + unsigned count; + void *ptr; + + /* client which the current inbound message is for */ + struct dal_client *active; +}; + +struct dal_client { + struct list_head list; + struct dal_channel *dch; + void *cookie; + dal_event_func_t event; + + /* opaque handle for the far side */ + void *remote; + + /* dal rpc calls are fully synchronous -- only one call may be + * active per client at a time + */ + struct mutex write_lock; + wait_queue_head_t wait; + + unsigned char data[DAL_DATA_MAX]; + + void *reply; + int reply_max; + int status; + unsigned msgid; /* msgid of expected reply */ + + spinlock_t tr_lock; + unsigned tr_head; + unsigned tr_tail; + struct dal_trace *tr_log; +}; + +static unsigned now(void) +{ + struct timespec ts; + ktime_get_ts(&ts); + return (ts.tv_nsec / 1000000) + (ts.tv_sec * 1000); +} + +void dal_trace(struct dal_client *c) +{ + if (c->tr_log) + return; + c->tr_log = kzalloc(sizeof(struct dal_trace) * TRACE_LOG_MAX, + GFP_KERNEL); +} + +void dal_trace_print(struct dal_hdr *hdr, unsigned *data, int len, unsigned when) +{ + int i; + printk("DAL %08x -> %08x L=%03x A=%d D=%04x P=%02x M=%02x T=%d", + (unsigned) hdr->from, (unsigned) hdr->to, + hdr->length, hdr->async, + hdr->ddi, hdr->prototype, hdr->msgid, + when); + len /= 4; + for (i = 0; i < len; i++) { + if (!(i & 7)) + printk("\n%03x", i * 4); + printk(" %08x", data[i]); + } + printk("\n"); +} + +void dal_trace_dump(struct dal_client *c) +{ + struct dal_trace *dt; + unsigned n, len; + + if (!c->tr_log) + return; + + for (n = c->tr_tail; n != c->tr_head; n = (n + 1) & TRACE_LOG_MASK) { + dt = c->tr_log + n; + len = dt->hdr.length; + if (len > TRACE_DATA_MAX) + len = TRACE_DATA_MAX; + dal_trace_print(&dt->hdr, dt->data, len, dt->timestamp); + } +} + +static void dal_trace_log(struct dal_client *c, + struct dal_hdr *hdr, void *data, unsigned len) +{ + unsigned long flags; + unsigned t, n; + struct dal_trace *dt; + + t = now(); + if (len > TRACE_DATA_MAX) + len = TRACE_DATA_MAX; + + spin_lock_irqsave(&c->tr_lock, flags); + n = (c->tr_head + 1) & TRACE_LOG_MASK; + if (c->tr_tail == n) + c->tr_tail = (c->tr_tail + 1) & TRACE_LOG_MASK; + dt = c->tr_log + n; + dt->timestamp = t; + memcpy(&dt->hdr, hdr, sizeof(struct dal_hdr)); + memcpy(dt->data, data, len); + c->tr_head = n; + + spin_unlock_irqrestore(&c->tr_lock, flags); +} + + +static void dal_channel_notify(void *priv, unsigned event) +{ + struct dal_channel *dch = priv; + struct dal_hdr *hdr = &dch->hdr; + struct dal_client *client; + unsigned long flags; + int len; + int r; + + spin_lock_irqsave(&dch->lock, flags); + +again: + if (dch->count == 0) { + if (smd_read_avail(dch->sch) < DAL_HDR_SIZE) + goto done; + + smd_read(dch->sch, hdr, DAL_HDR_SIZE); + + if (hdr->length < DAL_HDR_SIZE) + goto done; + + if (hdr->length > DAL_MSG_MAX) + panic("oversize message"); + + dch->count = hdr->length - DAL_HDR_SIZE; + + /* locate the client this message is targeted to */ + list_for_each_entry(client, &dch->clients, list) { + if (dch->hdr.to == client) { + dch->active = client; + dch->ptr = client->data; + goto check_data; + } + } + pr_err("$$$ receiving unknown message len = %d $$$\n", + dch->count); + dch->active = 0; + dch->ptr = dch->data; + } + +check_data: + len = dch->count; + if (len > 0) { + if (smd_read_avail(dch->sch) < len) + goto done; + + r = smd_read(dch->sch, dch->ptr, len); + if (r != len) + panic("invalid read"); + +#if DAL_TRACE + pr_info("dal recv %p <- %p %02x:%04x:%02x %d\n", + hdr->to, hdr->from, hdr->msgid, hdr->ddi, + hdr->prototype, hdr->length - sizeof(*hdr)); + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, dch->ptr, len); +#endif + dch->count = 0; + + client = dch->active; + if (!client) { + pr_err("dal: message to %p discarded\n", dch->hdr.to); + goto again; + } + + if (client->tr_log) + dal_trace_log(client, hdr, dch->ptr, len); + + if (hdr->msgid == DAL_MSGID_ASYNCH) { + if (client->event) + client->event(dch->ptr, len, client->cookie); + else + pr_err("dal: client %p has no event handler\n", + client); + goto again; + } + + if (hdr->msgid == client->msgid) { + if (!client->remote) + client->remote = hdr->from; + if (len > client->reply_max) + len = client->reply_max; + memcpy(client->reply, client->data, len); + client->status = len; + wake_up(&client->wait); + goto again; + } + + pr_err("dal: cannot find client %p\n", dch->hdr.to); + goto again; + } + +done: + spin_unlock_irqrestore(&dch->lock, flags); +} + +static LIST_HEAD(dal_channel_list); +static DEFINE_MUTEX(dal_channel_list_lock); + +static struct dal_channel *dal_open_channel(const char *name) +{ + struct dal_channel *dch; + + /* quick sanity check to avoid trying to talk to + * some non-DAL channel... + */ + if (strncmp(name, "DSP_DAL", 7) && strncmp(name, "SMD_DAL", 7)) + return 0; + + mutex_lock(&dal_channel_list_lock); + + list_for_each_entry(dch, &dal_channel_list, list) { + if (!strcmp(dch->name, name)) + goto found_it; + } + + dch = kzalloc(sizeof(*dch) + strlen(name) + 1, GFP_KERNEL); + if (!dch) + goto fail; + + dch->name = (char *) (dch + 1); + strcpy(dch->name, name); + spin_lock_init(&dch->lock); + INIT_LIST_HEAD(&dch->clients); + + list_add(&dch->list, &dal_channel_list); + +found_it: + if (!dch->sch) { + if (smd_open(name, &dch->sch, dch, dal_channel_notify)) + dch = NULL; + /* FIXME: wait for channel to open before returning */ + msleep(100); + } + +fail: + mutex_unlock(&dal_channel_list_lock); + + return dch; +} + +int dal_call_raw(struct dal_client *client, + struct dal_hdr *hdr, + void *data, int data_len, + void *reply, int reply_max) +{ + struct dal_channel *dch = client->dch; + unsigned long flags; + + client->reply = reply; + client->reply_max = reply_max; + client->msgid = hdr->msgid | DAL_MSGID_REPLY; + client->status = -EBUSY; + +#if DAL_TRACE + pr_info("dal send %p -> %p %02x:%04x:%02x %d\n", + hdr->from, hdr->to, hdr->msgid, hdr->ddi, + hdr->prototype, hdr->length - sizeof(*hdr)); + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, data_len); +#endif + + if (client->tr_log) + dal_trace_log(client, hdr, data, data_len); + + spin_lock_irqsave(&dch->lock, flags); + /* FIXME: ensure entire message is written or none. */ + smd_write(dch->sch, hdr, sizeof(*hdr)); + smd_write(dch->sch, data, data_len); + spin_unlock_irqrestore(&dch->lock, flags); + + if (!wait_event_timeout(client->wait, (client->status != -EBUSY), 5*HZ)) { + dal_trace_dump(client); + pr_err("dal: call timed out. dsp is probably dead.\n"); + dal_trace_print(hdr, data, data_len, 0); + BUG(); + } + + return client->status; +} + +int dal_call(struct dal_client *client, + unsigned ddi, unsigned prototype, + void *data, int data_len, + void *reply, int reply_max) +{ + struct dal_hdr hdr; + int r; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.length = data_len + sizeof(hdr); + hdr.version = DAL_VERSION; + hdr.msgid = DAL_MSGID_DDI; + hdr.ddi = ddi; + hdr.prototype = prototype; + hdr.from = client; + hdr.to = client->remote; + + if (hdr.length > DAL_MSG_MAX) + return -EINVAL; + + mutex_lock(&client->write_lock); + r = dal_call_raw(client, &hdr, data, data_len, reply, reply_max); + mutex_unlock(&client->write_lock); +#if 0 + if ((r > 3) && (((uint32_t*) reply)[0] == 0)) { + pr_info("dal call OK\n"); + } else { + pr_info("dal call ERROR\n"); + } +#endif + return r; +} + +struct dal_msg_attach { + uint32_t device_id; + char attach[64]; + char service_name[32]; +} __attribute__((packed)); + +struct dal_reply_attach { + uint32_t status; + char name[64]; +}; + +struct dal_client *dal_attach(uint32_t device_id, const char *name, + dal_event_func_t func, void *cookie) +{ + struct dal_hdr hdr; + struct dal_msg_attach msg; + struct dal_reply_attach reply; + struct dal_channel *dch; + struct dal_client *client; + unsigned long flags; + int r; + + dch = dal_open_channel(name); + if (!dch) + return 0; + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return 0; + + client->dch = dch; + client->event = func; + client->cookie = cookie; + mutex_init(&client->write_lock); + spin_lock_init(&client->tr_lock); + init_waitqueue_head(&client->wait); + + spin_lock_irqsave(&dch->lock, flags); + list_add(&client->list, &dch->clients); + spin_unlock_irqrestore(&dch->lock, flags); + + memset(&hdr, 0, sizeof(hdr)); + memset(&msg, 0, sizeof(msg)); + + hdr.length = sizeof(hdr) + sizeof(msg); + hdr.version = DAL_VERSION; + hdr.msgid = DAL_MSGID_ATTACH; + hdr.from = client; + msg.device_id = device_id; + + r = dal_call_raw(client, &hdr, &msg, sizeof(msg), + &reply, sizeof(reply)); + + if ((r == sizeof(reply)) && (reply.status == 0)) { + reply.name[63] = 0; + pr_info("dal_attach: status = %d, name = '%s'\n", + reply.status, reply.name); + return client; + } + + pr_err("dal_attach: failure\n"); + + dal_detach(client); + return 0; +} + +int dal_detach(struct dal_client *client) +{ + struct dal_channel *dch; + unsigned long flags; + + mutex_lock(&client->write_lock); + if (client->remote) { + struct dal_hdr hdr; + uint32_t data; + + memset(&hdr, 0, sizeof(hdr)); + hdr.length = sizeof(hdr) + sizeof(data); + hdr.version = DAL_VERSION; + hdr.msgid = DAL_MSGID_DETACH; + hdr.from = client; + hdr.to = client->remote; + data = (uint32_t) client; + + dal_call_raw(client, &hdr, &data, sizeof(data), + &data, sizeof(data)); + } + + dch = client->dch; + spin_lock_irqsave(&dch->lock, flags); + if (dch->active == client) { + /* We have received a message header for this client + * but not the body of the message. Ensure that when + * the body arrives we don't write it into the now-closed + * client. In *theory* this should never happen. + */ + dch->active = 0; + dch->ptr = dch->data; + } + list_del(&client->list); + spin_unlock_irqrestore(&dch->lock, flags); + + mutex_unlock(&client->write_lock); + + kfree(client); + return 0; +} + +void *dal_get_remote_handle(struct dal_client *client) +{ + return client->remote; +} + +/* convenience wrappers */ + +int dal_call_f0(struct dal_client *client, uint32_t ddi, uint32_t arg1) +{ + uint32_t tmp = arg1; + int res; + res = dal_call(client, ddi, 0, &tmp, sizeof(tmp), &tmp, sizeof(tmp)); + if (res >= 4) + return (int) tmp; + return res; +} + +int dal_call_f1(struct dal_client *client, uint32_t ddi, uint32_t arg1, uint32_t arg2) +{ + uint32_t tmp[2]; + int res; + tmp[0] = arg1; + tmp[1] = arg2; + res = dal_call(client, ddi, 1, tmp, sizeof(tmp), tmp, sizeof(uint32_t)); + if (res >= 4) + return (int) tmp[0]; + return res; +} + +int dal_call_f5(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t ilen) +{ + uint32_t tmp[128]; + int res; + int param_idx = 0; + + if (ilen + 4 > DAL_DATA_MAX) + return -EINVAL; + + tmp[param_idx] = ilen; + param_idx++; + + memcpy(&tmp[param_idx], ibuf, ilen); + param_idx += DIV_ROUND_UP(ilen, 4); + + res = dal_call(client, ddi, 5, tmp, param_idx * 4, tmp, sizeof(tmp)); + + if (res >= 4) + return (int) tmp[0]; + return res; +} + +int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1, + uint32_t ilen1, void *ibuf2, uint32_t ilen2, void *obuf, + uint32_t olen) +{ + uint32_t tmp[128]; + int res; + int param_idx = 0; + + if (ilen1 + ilen2 + 8 > DAL_DATA_MAX) + return -EINVAL; + + tmp[param_idx] = ilen1; + param_idx++; + + memcpy(&tmp[param_idx], ibuf1, ilen1); + param_idx += DIV_ROUND_UP(ilen1, 4); + + tmp[param_idx++] = ilen2; + memcpy(&tmp[param_idx], ibuf2, ilen2); + param_idx += DIV_ROUND_UP(ilen2, 4); + + tmp[param_idx++] = olen; + res = dal_call(client, ddi, 13, tmp, param_idx * 4, tmp, sizeof(tmp)); + + if (res >= 4) + res = (int)tmp[0]; + + if (!res) { + if (tmp[1] > olen) + return -EIO; + memcpy(obuf, &tmp[2], tmp[1]); + } + return res; +} diff --git a/arch/arm/mach-msm/qdsp6/dal.h b/arch/arm/mach-msm/qdsp6/dal.h new file mode 100644 index 0000000000000..c02f5c7843b0e --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/dal.h @@ -0,0 +1,65 @@ +/* arch/arm/mach-msm/qdsp6/dal.h + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * 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 _MACH_MSM_DAL_ +#define _MACH_MSM_DAL_ + +struct dal_client; + +typedef void (*dal_event_func_t)(void *data, int len, void *cookie); + +struct dal_client *dal_attach(uint32_t device_id, const char *name, + dal_event_func_t func, void *cookie); + +int dal_detach(struct dal_client *client); + +int dal_call(struct dal_client *client, + unsigned ddi, unsigned prototype, + void *data, int data_len, + void *reply, int reply_max); + +void dal_trace(struct dal_client *client); +void dal_trace_dump(struct dal_client *client); + +/* function to call before panic on stalled dal calls */ +void dal_set_oops(struct dal_client *client, void (*oops)(void)); + +/* convenience wrappers */ +int dal_call_f0(struct dal_client *client, uint32_t ddi, + uint32_t arg1); +int dal_call_f1(struct dal_client *client, uint32_t ddi, + uint32_t arg1, uint32_t arg2); +int dal_call_f5(struct dal_client *client, uint32_t ddi, + void *ibuf, uint32_t ilen); +int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1, + uint32_t ilen1, void *ibuf2, uint32_t ilen2, void *obuf, + uint32_t olen); + +/* common DAL operations */ +enum { + DAL_OP_ATTACH = 0, + DAL_OP_DETACH, + DAL_OP_INIT, + DAL_OP_DEINIT, + DAL_OP_OPEN, + DAL_OP_CLOSE, + DAL_OP_INFO, + DAL_OP_POWEREVENT, + DAL_OP_SYSREQUEST, + DAL_OP_FIRST_DEVICE_API, +}; + +#endif diff --git a/arch/arm/mach-msm/qdsp6/dal_acdb.h b/arch/arm/mach-msm/qdsp6/dal_acdb.h new file mode 100644 index 0000000000000..0e95b3b8a37a9 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/dal_acdb.h @@ -0,0 +1,84 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#define ACDB_DAL_DEVICE 0x02000069 +#define ACDB_DAL_PORT "SMD_DAL_AM_AUD" + +#define ACDB_OP_IOCTL DAL_OP_FIRST_DEVICE_API + +/* ioctls */ +#define ACDB_GET_DEVICE 0x0108bb92 +#define ACDB_SET_DEVICE 0x0108bb93 +#define ACDB_GET_STREAM 0x0108bb95 +#define ACDB_SET_STREAM 0x0108bb96 +#define ACDB_GET_DEVICE_TABLE 0x0108bb97 +#define ACDB_GET_STREAM_TABLE 0x0108bb98 + +#define ACDB_RES_SUCCESS 0 +#define ACDB_RES_FAILURE -1 +#define ACDB_RES_BADPARM -2 +#define ACDB_RES_BADSTATE -3 + +struct acdb_cmd_device { + uint32_t size; + + uint32_t command_id; + uint32_t device_id; + uint32_t network_id; + uint32_t sample_rate_id; + uint32_t interface_id; + uint32_t algorithm_block_id; + + /* physical page aligned buffer */ + uint32_t total_bytes; + uint32_t unmapped_buf; +} __attribute__((packed)); + +struct acdb_cmd_device_table { + uint32_t size; + + uint32_t command_id; + uint32_t device_id; + uint32_t network_id; + uint32_t sample_rate_id; + + /* physical page aligned buffer */ + uint32_t total_bytes; + uint32_t unmapped_buf; + + uint32_t res_size; +} __attribute__((packed)); + +struct acdb_result { + uint32_t dal_status; + uint32_t size; + + uint32_t unmapped_buf; + uint32_t used_bytes; + uint32_t result; +} __attribute__((packed)); diff --git a/arch/arm/mach-msm/qdsp6/dal_adie.h b/arch/arm/mach-msm/qdsp6/dal_adie.h new file mode 100644 index 0000000000000..b7f58456e5b19 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/dal_adie.h @@ -0,0 +1,108 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _MACH_MSM_QDSP6_ADIE_ +#define _MACH_MSM_QDSP6_ADIE_ + +#include "dal.h" + +#define ADIE_DAL_DEVICE 0x02000029 +#define ADIE_DAL_PORT "SMD_DAL_AM_AUD" + +enum { + ADIE_OP_GET_NUM_PATHS = DAL_OP_FIRST_DEVICE_API, + ADIE_OP_GET_ALL_PATH_IDS, + ADIE_OP_SET_PATH, + ADIE_OP_GET_NUM_PATH_FREQUENCY_PLANS, + ADIE_OP_GET_PATH_FREQUENCY_PLANS, + ADIE_OP_SET_PATH_FREQUENCY_PLAN, + ADIE_OP_PROCEED_TO_STAGE, + ADIE_OP_MUTE_PATH +}; + +/* Path IDs for normal operation. */ +#define ADIE_PATH_HANDSET_TX 0x010740f6 +#define ADIE_PATH_HANDSET_RX 0x010740f7 +#define ADIE_PATH_HEADSET_MONO_TX 0x010740f8 +#define ADIE_PATH_HEADSET_STEREO_TX 0x010740f9 +#define ADIE_PATH_HEADSET_MONO_RX 0x010740fa +#define ADIE_PATH_HEADSET_STEREO_RX 0x010740fb +#define ADIE_PATH_SPEAKER_TX 0x010740fc +#define ADIE_PATH_SPEAKER_RX 0x010740fd +#define ADIE_PATH_SPEAKER_STEREO_RX 0x01074101 + +/* Path IDs used for TTY */ +#define ADIE_PATH_TTY_HEADSET_TX 0x010740fe +#define ADIE_PATH_TTY_HEADSET_RX 0x010740ff + +/* Path IDs used by Factory Test Mode. */ +#define ADIE_PATH_FTM_MIC1_TX 0x01074108 +#define ADIE_PATH_FTM_MIC2_TX 0x01074107 +#define ADIE_PATH_FTM_HPH_L_RX 0x01074106 +#define ADIE_PATH_FTM_HPH_R_RX 0x01074104 +#define ADIE_PATH_FTM_EAR_RX 0x01074103 +#define ADIE_PATH_FTM_SPKR_RX 0x01074102 + +/* Path IDs for Loopback */ +/* Path IDs used for Line in -> AuxPGA -> Line Out Stereo Mode*/ +#define ADIE_PATH_AUXPGA_LINEOUT_STEREO_LB 0x01074100 +/* Line in -> AuxPGA -> LineOut Mono */ +#define ADIE_PATH_AUXPGA_LINEOUT_MONO_LB 0x01073d82 +/* Line in -> AuxPGA -> Stereo Headphone */ +#define ADIE_PATH_AUXPGA_HDPH_STEREO_LB 0x01074109 +/* Line in -> AuxPGA -> Mono Headphone */ +#define ADIE_PATH_AUXPGA_HDPH_MONO_LB 0x01073d85 +/* Line in -> AuxPGA -> Earpiece */ +#define ADIE_PATH_AUXPGA_EAP_LB 0x01073d81 +/* Line in -> AuxPGA -> AuxOut */ +#define ADIE_PATH_AUXPGA_AUXOUT_LB 0x01073d86 + +/* Concurrency Profiles */ +#define ADIE_PATH_SPKR_STEREO_HDPH_MONO_RX 0x01073d83 +#define ADIE_PATH_SPKR_MONO_HDPH_MONO_RX 0x01073d84 +#define ADIE_PATH_SPKR_MONO_HDPH_STEREO_RX 0x01073d88 +#define ADIE_PATH_SPKR_STEREO_HDPH_STEREO_RX 0x01073d89 + +/* stages */ +#define ADIE_STAGE_PATH_OFF 0x0050 +#define ADIE_STAGE_DIGITAL_READY 0x0100 +#define ADIE_STAGE_DIGITAL_ANALOG_READY 0x1000 +#define ADIE_STAGE_ANALOG_OFF 0x0750 +#define ADIE_STAGE_DIGITAL_OFF 0x0600 + +/* path types */ +#define ADIE_PATH_RX 0 +#define ADIE_PATH_TX 1 +#define ADIE_PATH_LOOPBACK 2 + +/* mute states */ +#define ADIE_MUTE_OFF 0 +#define ADIE_MUTE_ON 1 + + +#endif diff --git a/arch/arm/mach-msm/qdsp6/dal_audio.h b/arch/arm/mach-msm/qdsp6/dal_audio.h new file mode 100644 index 0000000000000..b1ad07db4a38f --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/dal_audio.h @@ -0,0 +1,565 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __DAL_AUDIO_H__ +#define __DAL_AUDIO_H__ + +#include "dal_audio_format.h" + +#define AUDIO_DAL_DEVICE 0x02000028 +#define AUDIO_DAL_PORT "DSP_DAL_AQ_AUD" + +enum { + AUDIO_OP_CONTROL = DAL_OP_FIRST_DEVICE_API, + AUDIO_OP_DATA, + AUDIO_OP_INIT, +}; + +/* ---- common audio structures ---- */ + +/* This flag, if set, indicates that the beginning of the data in the*/ +/* buffer is a synchronization point or key frame, meaning no data */ +/* before it in the stream is required in order to render the stream */ +/* from this point onward. */ +#define ADSP_AUDIO_BUFFER_FLAG_SYNC_POINT 0x01 + +/* This flag, if set, indicates that the buffer object is using valid */ +/* physical address used to store the media data */ +#define ADSP_AUDIO_BUFFER_FLAG_PHYS_ADDR 0x04 + +/* This flag, if set, indicates that a media start timestamp has been */ +/* set for a buffer. */ +#define ADSP_AUDIO_BUFFER_FLAG_START_SET 0x08 + +/* This flag, if set, indicates that a media stop timestamp has been set */ +/* for a buffer. */ +#define ADSP_AUDIO_BUFFER_FLAG_STOP_SET 0x10 + +/* This flag, if set, indicates that a preroll timestamp has been set */ +/* for a buffer. */ +#define ADSP_AUDIO_BUFFER_FLAG_PREROLL_SET 0x20 + +/* This flag, if set, indicates that the data in the buffer is a fragment of */ +/* a larger block of data, and will be continued by the data in the next */ +/* buffer to be delivered. */ +#define ADSP_AUDIO_BUFFER_FLAG_CONTINUATION 0x40 + +struct adsp_audio_buffer { + u32 addr; /* Physical Address of buffer */ + u32 max_size; /* Maximum size of buffer */ + u32 actual_size; /* Actual size of valid data in the buffer */ + u32 offset; /* Offset to the first valid byte */ + u32 flags; /* ADSP_AUDIO_BUFFER_FLAGs that has been set */ + s64 start; /* Start timestamp, if any */ + s64 stop; /* Stop timestamp, if any */ + s64 preroll; /* Preroll timestamp, if any */ +} __attribute__ ((packed)); + + + +/* ---- audio commands ---- */ + +/* Command/event response types */ +#define ADSP_AUDIO_RESPONSE_COMMAND 0 +#define ADSP_AUDIO_RESPONSE_ASYNC 1 + +struct adsp_command_hdr { + u32 size; /* sizeof(cmd) - sizeof(u32) */ + + u32 dst; + u32 src; + + u32 opcode; + u32 response_type; + u32 seq_number; + + u32 context; /* opaque to DSP */ + u32 data; + + u32 padding; +} __attribute__ ((packed)); + + +#define AUDIO_DOMAIN_APP 0 +#define AUDIO_DOMAIN_MODEM 1 +#define AUDIO_DOMAIN_DSP 2 + +#define AUDIO_SERVICE_AUDIO 0 +#define AUDIO_SERVICE_VIDEO 1 /* really? */ + +/* adsp audio addresses are (byte order) domain, service, major, minor */ +//#define AUDIO_ADDR(maj,min) ( (((maj) & 0xff) << 16) | (((min) & 0xff) << 24) | (1) ) + +#define AUDIO_ADDR(maj,min,dom) ( (((min) & 0xff) << 24) | (((maj) & 0xff) << 16) | ((AUDIO_SERVICE_AUDIO) << 8) | (dom) ) + + +/* AAC Encoder modes */ +#define ADSP_AUDIO_ENC_AAC_LC_ONLY_MODE 0 +#define ADSP_AUDIO_ENC_AAC_PLUS_MODE 1 +#define ADSP_AUDIO_ENC_ENHANCED_AAC_PLUS_MODE 2 + +struct adsp_audio_aac_enc_cfg { + u32 bit_rate; /* bits per second */ + u32 encoder_mode; /* ADSP_AUDIO_ENC_* */ +} __attribute__ ((packed)); + +#define ADSP_AUDIO_ENC_SBC_ALLOCATION_METHOD_LOUNDNESS 0 +#define ADSP_AUDIO_ENC_SBC_ALLOCATION_METHOD_SNR 1 + +#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_MONO 1 +#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_STEREO 2 +#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_DUAL 8 +#define ADSP_AUDIO_ENC_SBC_CHANNEL_MODE_JOINT_STEREO 9 + +struct adsp_audio_sbc_encoder_cfg { + u32 num_subbands; + u32 block_len; + u32 channel_mode; + u32 allocation_method; + u32 bit_rate; +} __attribute__ ((packed)); + +/* AMR NB encoder modes */ +#define ADSP_AUDIO_AMR_MR475 0 +#define ADSP_AUDIO_AMR_MR515 1 +#define ADSP_AUDIO_AMR_MMR59 2 +#define ADSP_AUDIO_AMR_MMR67 3 +#define ADSP_AUDIO_AMR_MMR74 4 +#define ADSP_AUDIO_AMR_MMR795 5 +#define ADSP_AUDIO_AMR_MMR102 6 +#define ADSP_AUDIO_AMR_MMR122 7 + +/* The following are valid AMR NB DTX modes */ +#define ADSP_AUDIO_AMR_DTX_MODE_OFF 0 +#define ADSP_AUDIO_AMR_DTX_MODE_ON_VAD1 1 +#define ADSP_AUDIO_AMR_DTX_MODE_ON_VAD2 2 +#define ADSP_AUDIO_AMR_DTX_MODE_ON_AUTO 3 + +/* AMR Encoder configuration */ +struct adsp_audio_amr_enc_cfg { + u32 mode; /* ADSP_AUDIO_AMR_MR* */ + u32 dtx_mode; /* ADSP_AUDIO_AMR_DTX_MODE* */ + u32 enable; /* 1 = enable, 0 = disable */ +} __attribute__ ((packed)); + +struct adsp_audio_qcelp13k_enc_cfg { + u16 min_rate; + u16 max_rate; +} __attribute__ ((packed)); + +struct adsp_audio_evrc_enc_cfg { + u16 min_rate; + u16 max_rate; +} __attribute__ ((packed)); + +union adsp_audio_codec_config { + struct adsp_audio_amr_enc_cfg amr; + struct adsp_audio_aac_enc_cfg aac; + struct adsp_audio_qcelp13k_enc_cfg qcelp13k; + struct adsp_audio_evrc_enc_cfg evrc; + struct adsp_audio_sbc_encoder_cfg sbc; +} __attribute__ ((packed)); + + +/* This is the default value. */ +#define ADSP_AUDIO_OPEN_STREAM_MODE_NONE 0x0000 + +/* This bit, if set, indicates that the AVSync mode is activated. */ +#define ADSP_AUDIO_OPEN_STREAM_MODE_AVSYNC 0x0001 + +/* This bit, if set, indicates that the Sample Rate/Channel Mode */ +/* Change Notification mode is activated. */ +#define ADSP_AUDIO_OPEN_STREAM_MODE_SR_CM_NOTIFY 0x0002 + +/* This bit, if set, indicates that the sync clock is enabled */ +#define ADSP_AUDIO_OPEN_STREAM_MODE_ENABLE_SYNC_CLOCK 0x0004 + +struct adsp_open_command { + struct adsp_command_hdr hdr; + + u32 device; + u32 endpoint; /* address */ + + u32 stream_context; + u32 mode; + + u32 buf_max_size; + + union adsp_audio_format format; + union adsp_audio_codec_config config; +} __attribute__ ((packed)); + + +/* --- audio control and stream session ioctls ---- */ + +/* Opcode to open a device stream session to capture audio */ +#define ADSP_AUDIO_IOCTL_CMD_OPEN_READ 0x0108dd79 + +/* Opcode to open a device stream session to render audio */ +#define ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE 0x0108dd7a + +/* Opcode to open a device session, must open a device */ +#define ADSP_AUDIO_IOCTL_CMD_OPEN_DEVICE 0x0108dd7b + +/* Close an existing stream or device */ +#define ADSP_AUDIO_IOCTL_CMD_CLOSE 0x0108d8bc + + + +/* A device switch requires three IOCTL */ +/* commands in the following sequence: PREPARE, STANDBY, COMMIT */ + +/* adsp_audio_device_switch_command structure is needed for */ +/* DEVICE_SWITCH_PREPARE */ + +/* Device switch protocol step #1. Pause old device and */ +/* generate silence for the old device. */ +#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_PREPARE 0x010815c4 + +/* Device switch protocol step #2. Release old device, */ +/* create new device and generate silence for the new device. */ + +/* When client receives ack for this IOCTL, the client can */ +/* start sending IOCTL commands to configure, calibrate and */ +/* change filter settings on the new device. */ +#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_STANDBY 0x010815c5 + +/* Device switch protocol step #3. Start normal operations on new device */ +#define ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_COMMIT 0x01075ee7 + +struct adsp_device_switch_command { + struct adsp_command_hdr hdr; + u32 old_device; + u32 new_device; + u8 device_class; /* 0 = i.rx, 1 = i.tx, 2 = e.rx, 3 = e.tx */ + u8 device_type; /* 0 = rx, 1 = tx, 2 = both */ +} __attribute__ ((packed)); + + + +/* --- audio control session ioctls ---- */ + +#define ADSP_PATH_RX 0 +#define ADSP_PATH_TX 1 +#define ADSP_PATH_BOTH 2 + +/* These commands will affect a logical device and all its associated */ +/* streams. */ + + +/* Set device volume. */ +#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL 0x0107605c + +struct adsp_set_dev_volume_command { + struct adsp_command_hdr hdr; + u32 device_id; + u32 path; /* 0 = rx, 1 = tx, 2 = both */ + s32 volume; +} __attribute__ ((packed)); + +/* Set Device stereo volume. This command has data payload, */ +/* struct adsp_audio_set_dev_stereo_volume_command. */ +#define ADSP_AUDIO_IOCTL_SET_DEVICE_STEREO_VOL 0x0108df3e + +/* Set L, R cross channel gain for a Device. This command has */ +/* data payload, struct adsp_audio_set_dev_x_chan_gain_command. */ +#define ADSP_AUDIO_IOCTL_SET_DEVICE_XCHAN_GAIN 0x0108df40 + +/* Set device mute state. */ +#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE 0x0107605f + +struct adsp_set_dev_mute_command { + struct adsp_command_hdr hdr; + u32 device_id; + u32 path; /* 0 = rx, 1 = tx, 2 = both */ + u32 mute; /* 1 = mute */ +} __attribute__ ((packed)); + +/* Configure Equalizer for a device. */ +/* This command has payload struct adsp_audio_set_dev_equalizer_command. */ +#define ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_EQ_CONFIG 0x0108b10e + +/* Set configuration data for an algorithm aspect of a device. */ +/* This command has payload struct adsp_audio_set_dev_cfg_command. */ +#define ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG 0x0108b6cb + +struct adsp_set_dev_cfg_command { + struct adsp_command_hdr hdr; + u32 device_id; + u32 block_id; + u32 interface_id; + u32 phys_addr; + u32 phys_size; + u32 phys_used; +} __attribute__ ((packed)); + +/* Set configuration data for all interfaces of a device. */ +#define ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG_TABLE 0x0108b6bf + +struct adsp_set_dev_cfg_table_command { + struct adsp_command_hdr hdr; + u32 device_id; + u32 phys_addr; + u32 phys_size; + u32 phys_used; +} __attribute__ ((packed)); + +/* ---- audio stream data commands ---- */ + +#define ADSP_AUDIO_IOCTL_CMD_DATA_TX 0x0108dd7f +#define ADSP_AUDIO_IOCTL_CMD_DATA_RX 0x0108dd80 + +struct adsp_buffer_command { + struct adsp_command_hdr hdr; + struct adsp_audio_buffer buffer; +} __attribute__ ((packed)); + + + +/* ---- audio stream ioctls (only affect a single stream in a session) ---- */ + +/* Stop stream for audio device. */ +#define ADSP_AUDIO_IOCTL_CMD_STREAM_STOP 0x01075c54 + +/* End of stream reached. Client will not send any more data. */ +#define ADSP_AUDIO_IOCTL_CMD_STREAM_EOS 0x0108b150 + +/* Do sample slipping/stuffing on AAC outputs. The payload of */ +/* this command is struct adsp_audio_slip_sample_command. */ +#define ADSP_AUDIO_IOCTL_CMD_STREAM_SLIPSAMPLE 0x0108d40e + +/* Set stream volume. */ +/* This command has data payload, struct adsp_audio_set_volume_command. */ +#define ADSP_AUDIO_IOCTL_CMD_SET_STREAM_VOL 0x0108c0de + +/* Set stream stereo volume. This command has data payload, */ +/* struct adsp_audio_set_stereo_volume_command. */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_STEREO_VOL 0x0108dd7c + +/* Set L, R cross channel gain for a Stream. This command has */ +/* data payload, struct adsp_audio_set_x_chan_gain_command. */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_XCHAN_GAIN 0x0108dd7d + +/* Set stream mute state. */ +/* This command has data payload, struct adsp_audio_set_stream_mute. */ +#define ADSP_AUDIO_IOCTL_CMD_SET_STREAM_MUTE 0x0108c0df + +/* Reconfigure bit rate information. This command has data */ +/* payload, struct adsp_audio_set_bit_rate_command */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_BITRATE 0x0108ccf1 + +/* Set Channel Mapping. This command has data payload, struct */ +/* This command has data payload struct adsp_audio_set_channel_map_command. */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_CHANNELMAP 0x0108d32a + +/* Enable/disable AACPlus SBR. */ +/* This command has data payload struct adsp_audio_set_sbr_command */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_SBR 0x0108d416 + +/* Enable/disable WMA Pro Chex and Fex. This command has data payload */ +/* struct adsp_audio_stream_set_wma_command. */ +#define ADSP_AUDIO_IOCTL_SET_STREAM_WMAPRO 0x0108d417 + + +/* ---- audio session ioctls (affect all streams in a session) --- */ + +/* Start stream for audio device. */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_START 0x010815c6 + +/* Stop all stream(s) for audio session as indicated by major id. */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_STOP 0x0108dd7e + +/* Pause the data flow for a session as indicated by major id. */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_PAUSE 0x01075ee8 + +/* Resume the data flow for a session as indicated by major id. */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_RESUME 0x01075ee9 + +/* Drop any unprocessed data buffers for a session as indicated by major id. */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_FLUSH 0x01075eea + +/* Start Stream DTMF tone */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_DTMF_START 0x0108c0dd + +/* Stop Stream DTMF tone */ +#define ADSP_AUDIO_IOCTL_CMD_SESSION_DTMF_STOP 0x01087554 + +/* Set Session volume. */ +/* This command has data payload, struct adsp_audio_set_volume_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_VOL 0x0108d8bd + +/* Set session stereo volume. This command has data payload, */ +/* struct adsp_audio_set_stereo_volume_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_STEREO_VOL 0x0108df3d + +/* Set L, R cross channel gain for a session. This command has */ +/* data payload, struct adsp_audio_set_x_chan_gain_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_XCHAN_GAIN 0x0108df3f + +/* Set Session mute state. */ +/* This command has data payload, struct adsp_audio_set_mute_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_MUTE 0x0108d8be + +/* Configure Equalizer for a stream. */ +/* This command has payload struct adsp_audio_set_equalizer_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_EQ_CONFIG 0x0108c0e0 + +/* Set Audio Video sync information. */ +/* This command has data payload, struct adsp_audio_set_av_sync_command. */ +#define ADSP_AUDIO_IOCTL_SET_SESSION_AVSYNC 0x0108d1e2 + +/* Get Audio Media Session time. */ +/* This command returns the audioTime in adsp_audio_unsigned64_event */ +#define ADSP_AUDIO_IOCTL_CMD_GET_AUDIO_TIME 0x0108c26c + + +/* these command structures are used for both STREAM and SESSION ioctls */ + +struct adsp_set_volume_command { + struct adsp_command_hdr hdr; + s32 volume; +} __attribute__ ((packed)); + +struct adsp_set_mute_command { + struct adsp_command_hdr hdr; + u32 mute; /* 1 == mute */ +} __attribute__ ((packed)); + + + +/* ---- audio events ---- */ + +/* All IOCTL commands generate an event with the IOCTL opcode as the */ +/* event id after the IOCTL command has been executed. */ + +/* This event is generated after a media stream session is opened. */ +#define ADSP_AUDIO_EVT_STATUS_OPEN 0x0108c0d6 + +/* This event is generated after a media stream session is closed. */ +#define ADSP_AUDIO_EVT_STATUS_CLOSE 0x0108c0d7 + +/* Asyncronous buffer consumption. This event is generated after a */ +/* recived buffer is consumed during rendering or filled during */ +/* capture opeartion. */ +#define ADSP_AUDIO_EVT_STATUS_BUF_DONE 0x0108c0d8 + +/* This event is generated when rendering operation is starving for */ +/* data. In order to avoid audio loss at the end of a plauback, the */ +/* client should wait for this event before issuing the close command. */ +#define ADSP_AUDIO_EVT_STATUS_BUF_UNDERRUN 0x0108c0d9 + +/* This event is generated during capture operation when there are no */ +/* buffers available to copy the captured audio data */ +#define ADSP_AUDIO_EVT_STATUS_BUF_OVERFLOW 0x0108c0da + +/* This asynchronous event is generated as a result of an input */ +/* sample rate change and/or channel mode change detected by the */ +/* decoder. The event payload data is an array of 2 uint32 */ +/* values containing the sample rate in Hz and channel mode. */ +#define ADSP_AUDIO_EVT_SR_CM_CHANGE 0x0108d329 + +struct adsp_event_hdr { + u32 evt_handle; /* DAL common header */ + u32 evt_cookie; + u32 evt_length; + + u32 src; /* "source" audio address */ + u32 dst; /* "destination" audio address */ + + u32 event_id; + u32 response_type; + u32 seq_number; + + u32 context; /* opaque to DSP */ + u32 data; + + u32 status; +} __attribute__ ((packed)); + +struct adsp_buffer_event { + struct adsp_event_hdr hdr; + struct adsp_audio_buffer buffer; +} __attribute__ ((packed)); + + +/* ---- audio device IDs ---- */ + +/* Device direction Rx/Tx flag */ +#define ADSP_AUDIO_RX_DEVICE 0x00 +#define ADSP_AUDIO_TX_DEVICE 0x01 + +/* Default RX or TX device */ +#define ADSP_AUDIO_DEVICE_ID_DEFAULT 0x1081679 + +/* Source (TX) devices */ +#define ADSP_AUDIO_DEVICE_ID_HANDSET_MIC 0x107ac8d +#define ADSP_AUDIO_DEVICE_ID_HEADSET_MIC 0x1081510 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC 0x1081512 +#define ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC 0x1081518 +#define ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC 0x108151b +#define ADSP_AUDIO_DEVICE_ID_I2S_MIC 0x1089bf3 + +/* Special loopback pseudo device to be paired with an RX device */ +/* with usage ADSP_AUDIO_DEVICE_USAGE_MIXED_PCM_LOOPBACK */ +#define ADSP_AUDIO_DEVICE_ID_MIXED_PCM_LOOPBACK_TX 0x1089bf2 + +/* Sink (RX) devices */ +#define ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR 0x107ac88 +#define ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO 0x1081511 +#define ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO 0x107ac8a +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO 0x1081513 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET 0x108c508 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET 0x108c894 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO 0x1081514 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET 0x108c895 +#define ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET 0x108c509 +#define ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR 0x1081519 +#define ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR 0x108151c +#define ADSP_AUDIO_DEVICE_ID_I2S_SPKR 0x1089bf4 +#define ADSP_AUDIO_DEVICE_ID_NULL_SINK 0x108e512 + +/* BT A2DP playback device. */ +/* This device must be paired with */ +/* ADSP_AUDIO_DEVICE_ID_MIXED_PCM_LOOPBACK_TX using */ +/* ADSP_AUDIO_DEVICE_USAGE_MIXED_PCM_LOOPBACK mode */ +#define ADSP_AUDIO_DEVICE_ID_BT_A2DP_SPKR 0x108151a + +/* Voice Destination identifier - specifically used for */ +/* controlling Voice module from the Device Control Session */ +#define ADSP_AUDIO_DEVICE_ID_VOICE 0x0108df3c + +/* Audio device usage types. */ +/* This is a bit mask to determine which topology to use in the */ +/* device session */ +#define ADSP_AUDIO_DEVICE_CONTEXT_VOICE 0x01 +#define ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK 0x02 +#define ADSP_AUDIO_DEVICE_CONTEXT_MIXED_RECORD 0x10 +#define ADSP_AUDIO_DEVICE_CONTEXT_RECORD 0x20 +#define ADSP_AUDIO_DEVICE_CONTEXT_PCM_LOOPBACK 0x40 + +#endif diff --git a/arch/arm/mach-msm/qdsp6/dal_audio_format.h b/arch/arm/mach-msm/qdsp6/dal_audio_format.h new file mode 100644 index 0000000000000..cdb2e1ab98812 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/dal_audio_format.h @@ -0,0 +1,285 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __ADSP_AUDIO_MEDIA_FORMAT_H +#define __ADSP_AUDIO_MEDIA_FORMAT_H + + + +/* Supported audio media formats */ + +/* format block in shmem */ +#define ADSP_AUDIO_FORMAT_SHAREDMEMORY 0x01091a78 +/* adsp_audio_format_raw_pcm type */ +#define ADSP_AUDIO_FORMAT_PCM 0x0103d2fd +/* adsp_audio_format_raw_pcm type */ +#define ADSP_AUDIO_FORMAT_DTMF 0x01087725 +/* adsp_audio_format_adpcm type */ +#define ADSP_AUDIO_FORMAT_ADPCM 0x0103d2ff +/* Yamaha PCM format */ +#define ADSP_AUDIO_FORMAT_YADPCM 0x0108dc07 +/* ISO/IEC 11172 */ +#define ADSP_AUDIO_FORMAT_MP3 0x0103d308 +/* ISO/IEC 14496 */ +#define ADSP_AUDIO_FORMAT_MPEG4_AAC 0x010422f1 +/* AMR-NB audio in FS format */ +#define ADSP_AUDIO_FORMAT_AMRNB_FS 0x0105c16c +/* AMR-WB audio in FS format */ +#define ADSP_AUDIO_FORMAT_AMRWB_FS 0x0105c16e +/* QCELP 13k, IS733 */ +#define ADSP_AUDIO_FORMAT_V13K_FS 0x01080b8a +/* EVRC 8k, IS127 */ +#define ADSP_AUDIO_FORMAT_EVRC_FS 0x01080b89 +/* EVRC-B 8k, 4GV */ +#define ADSP_AUDIO_FORMAT_EVRCB_FS 0x0108f2a3 +/* MIDI command stream */ +#define ADSP_AUDIO_FORMAT_MIDI 0x0103d300 +/* A2DP SBC stream */ +#define ADSP_AUDIO_FORMAT_SBC 0x0108c4d8 +/* Version 10 Professional */ +#define ADSP_AUDIO_FORMAT_WMA_V10PRO 0x0108aa92 +/* Version 9 Starndard */ +#define ADSP_AUDIO_FORMAT_WMA_V9 0x0108d430 +/* AMR WideBand Plus */ +#define ADSP_AUDIO_FORMAT_AMR_WB_PLUS 0x0108f3da +/* AC3 Decoder */ +#define ADSP_AUDIO_FORMAT_AC3_DECODER 0x0108d5f9 + + +/* Not yet supported audio media formats */ + + + +/* ISO/IEC 13818 */ +#define ADSP_AUDIO_FORMAT_MPEG2_AAC 0x0103d309 +/* 3GPP TS 26.101 Sec 4.0 */ +#define ADSP_AUDIO_FORMAT_AMRNB_IF1 0x0103d305 +/* 3GPP TS 26.101 Annex A */ +#define ADSP_AUDIO_FORMAT_AMRNB_IF2 0x01057b31 +/* 3GPP TS 26.201 */ +#define ADSP_AUDIO_FORMAT_AMRWB_IF1 0x0103d306 +/* 3GPP TS 26.201 */ +#define ADSP_AUDIO_FORMAT_AMRWB_IF2 0x0105c16d +/* G.711 */ +#define ADSP_AUDIO_FORMAT_G711 0x0106201d +/* QCELP 8k, IS96A */ +#define ADSP_AUDIO_FORMAT_V8K_FS 0x01081d29 +/* Version 1 codec */ +#define ADSP_AUDIO_FORMAT_WMA_V1 0x01055b2b +/* Version 2, 7 & 8 codec */ +#define ADSP_AUDIO_FORMAT_WMA_V8 0x01055b2c +/* Version 9 Professional codec */ +#define ADSP_AUDIO_FORMAT_WMA_V9PRO 0x01055b2d +/* Version 9 Voice codec */ +#define ADSP_AUDIO_FORMAT_WMA_SP1 0x01055b2e +/* Version 9 Lossless codec */ +#define ADSP_AUDIO_FORMAT_WMA_LOSSLESS 0x01055b2f +/* Real Media content, low-bitrate */ +#define ADSP_AUDIO_FORMAT_RA_SIPR 0x01042a0f +/* Real Media content */ +#define ADSP_AUDIO_FORMAT_RA_COOK 0x01042a0e + + +/* For all of the audio formats, unless specified otherwise, */ +/* the following apply: */ +/* Format block bits are arranged in bytes and words in little-endian */ +/* order, i.e., least-significant bit first and least-significant */ +/* byte first. */ + + + +/* AAC Format Block. */ + +/* AAC format block consist of a format identifier followed by */ +/* AudioSpecificConfig formatted according to ISO/IEC 14496-3 */ + +/* The following AAC format identifiers are supported */ +#define ADSP_AUDIO_AAC_ADTS 0x010619cf +#define ADSP_AUDIO_AAC_MPEG4_ADTS 0x010619d0 +#define ADSP_AUDIO_AAC_LOAS 0x010619d1 +#define ADSP_AUDIO_AAC_ADIF 0x010619d2 +#define ADSP_AUDIO_AAC_RAW 0x010619d3 +#define ADSP_AUDIO_AAC_FRAMED_RAW 0x0108c1fb + + +#define ADSP_AUDIO_COMPANDING_ALAW 0x10619cd +#define ADSP_AUDIO_COMPANDING_MLAW 0x10619ce + +/* Maxmum number of bytes allowed in a format block */ +#define ADSP_AUDIO_FORMAT_DATA_MAX 16 + + +struct adsp_audio_no_payload_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* no payload for this format type */ +} __attribute__ ((packed)); + + +/* For convenience, to be used as a standard format block */ +/* for various media types that don't need a unique format block */ +/* ie. PCM, DTMF, etc. */ +struct adsp_audio_standard_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u16 channels; + u16 bits_per_sample; + u32 sampling_rate; + u8 is_signed; + u8 is_interleaved; +} __attribute__ ((packed)); + + + +/* ADPCM format block */ +struct adsp_audio_adpcm_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u16 channels; + u16 bits_per_sample; + u32 sampling_rate; + u8 is_signed; + u8 is_interleaved; + u32 block_size; +} __attribute__ ((packed)); + + +/* MIDI format block */ +struct adsp_audio_midi_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u32 sampling_rate; + u16 channels; + u16 mode; +} __attribute__ ((packed)); + + +/* G711 format block */ +struct adsp_audio_g711_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u32 companding; +} __attribute__ ((packed)); + + +struct adsp_audio_wma_pro_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u16 format_tag; + u16 channels; + u32 samples_per_sec; + u32 avg_bytes_per_sec; + u16 block_align; + u16 valid_bits_per_sample; + u32 channel_mask; + u16 encode_opt; + u16 advanced_encode_opt; + u32 advanced_encode_opt2; + u32 drc_peak_reference; + u32 drc_peak_target; + u32 drc_average_reference; + u32 drc_average_target; +} __attribute__ ((packed)); + + +struct adsp_audio_amrwb_plus_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + u32 size; + u32 version; + u32 channels; + u32 amr_band_mode; + u32 amr_dtx_mode; + u32 amr_frame_format; + u32 amr_isf_index; +} __attribute__ ((packed)); + + +/* Binary Byte Stream Format */ +/* Binary format type that defines a byte stream, */ +/* can be used to specify any format (ie. AAC) */ +struct adsp_audio_binary_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* payload */ + /* number of bytes set in byte stream */ + u32 num_bytes; + /* Byte stream binary data */ + u8 data[ADSP_AUDIO_FORMAT_DATA_MAX]; +} __attribute__ ((packed)); + + +struct adsp_audio_shared_memory_format { + /* Media Format Code (must always be first element) */ + u32 format; + + /* Number of bytes in shared memory */ + u32 len; + /* Phyisical address to data in shared memory */ + u32 address; +} __attribute__ ((packed)); + + +/* Union of all format types */ +union adsp_audio_format { + /* Basic format block with no payload */ + struct adsp_audio_no_payload_format no_payload; + /* Generic format block PCM, DTMF */ + struct adsp_audio_standard_format standard; + /* ADPCM format block */ + struct adsp_audio_adpcm_format adpcm; + /* MIDI format block */ + struct adsp_audio_midi_format midi; + /* G711 format block */ + struct adsp_audio_g711_format g711; + /* WmaPro format block */ + struct adsp_audio_wma_pro_format wma_pro; + /* WmaPro format block */ + struct adsp_audio_amrwb_plus_format amrwb_plus; + /* binary (byte stream) format block, used for AAC */ + struct adsp_audio_binary_format binary; + /* format block in shared memory */ + struct adsp_audio_shared_memory_format shared_mem; +}; + +#endif + diff --git a/arch/arm/mach-msm/qdsp6/mp3.c b/arch/arm/mach-msm/qdsp6/mp3.c new file mode 100644 index 0000000000000..759afb16182ba --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/mp3.c @@ -0,0 +1,220 @@ +/* arch/arm/mach-msm/qdsp6/mp3.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * + * 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 + +#define BUFSZ (8192) +#define DMASZ (BUFSZ * 2) + +struct mp3 { + struct mutex lock; + struct audio_client *ac; + uint32_t sample_rate; + uint32_t channel_count; +}; + +static long mp3_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct mp3 *mp3 = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void*) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + mutex_lock(&mp3->lock); + switch (cmd) { + case AUDIO_SET_VOLUME: { + int vol; + if (copy_from_user(&vol, (void*) arg, sizeof(vol))) { + rc = -EFAULT; + break; + } + rc = q6audio_set_stream_volume(mp3->ac, vol); + break; + } + case AUDIO_START: { + uint32_t acdb_id; + if (arg == 0) { + acdb_id = 0; + } else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) { + pr_info("pcm_out: copy acdb_id from user failed\n"); + rc = -EFAULT; + break; + } + if (mp3->ac) { + rc = -EBUSY; + } else { + mp3->ac = q6audio_open_mp3(BUFSZ, + mp3->sample_rate, mp3->channel_count, acdb_id); + if (!mp3->ac) + rc = -ENOMEM; + } + break; + } + case AUDIO_STOP: + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (mp3->ac) { + rc = -EBUSY; + break; + } + if (copy_from_user(&config, (void*) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count < 1 || config.channel_count > 2) { + rc = -EINVAL; + break; + } + mp3->sample_rate = config.sample_rate; + mp3->channel_count = config.channel_count; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = BUFSZ; + config.buffer_count = 2; + config.sample_rate = mp3->sample_rate; + config.channel_count = mp3->channel_count; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void*) arg, &config, sizeof(config))) { + rc = -EFAULT; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&mp3->lock); + return rc; +} + +static int mp3_open(struct inode *inode, struct file *file) +{ + int rc = 0; + + struct mp3 *mp3; + mp3 = kzalloc(sizeof(struct mp3), GFP_KERNEL); + + if (!mp3) + return -ENOMEM; + + mutex_init(&mp3->lock); + mp3->channel_count = 2; + mp3->sample_rate = 44100; + + file->private_data = mp3; + return rc; +} + +static ssize_t mp3_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct mp3 *mp3 = file->private_data; + struct audio_client *ac; + struct audio_buffer *ab; + const char __user *start = buf; + int xfer; + + if (!mp3->ac) + mp3_ioctl(file, AUDIO_START, 0); + + ac = mp3->ac; + if (!ac) + return -ENODEV; + + while (count > 0) { + ab = ac->buf + ac->cpu_buf; + + if (ab->used) + wait_event(ac->wait, (ab->used == 0)); + + xfer = count; + if (xfer > ab->size) + xfer = ab->size; + + if (copy_from_user(ab->data, buf, xfer)) + return -EFAULT; + + buf += xfer; + count -= xfer; + + ab->used = xfer; + q6audio_write(ac, ab); + ac->cpu_buf ^= 1; + } + + return buf - start; +} + +static int mp3_fsync(struct file *f, struct dentry *dentry, int datasync) +{ + struct mp3 *mp3 = f->private_data; + if (mp3->ac) + return q6audio_async(mp3->ac); + return -ENODEV; +} + +static int mp3_release(struct inode *inode, struct file *file) +{ + struct mp3 *mp3 = file->private_data; + if (mp3->ac) + q6audio_mp3_close(mp3->ac); + kfree(mp3); + return 0; +} + +static struct file_operations mp3_fops = { + .owner = THIS_MODULE, + .open = mp3_open, + .write = mp3_write, + .fsync = mp3_fsync, + .release = mp3_release, + .unlocked_ioctl = mp3_ioctl, +}; + +struct miscdevice mp3_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_mp3", + .fops = &mp3_fops, +}; + +static int __init mp3_init(void) { + return misc_register(&mp3_misc); +} + +device_initcall(mp3_init); diff --git a/arch/arm/mach-msm/qdsp6/msm_q6vdec.c b/arch/arm/mach-msm/qdsp6/msm_q6vdec.c new file mode 100644 index 0000000000000..ec95ecfbe5a0e --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/msm_q6vdec.c @@ -0,0 +1,941 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* +#define DEBUG_TRACE_VDEC +#define DEBUG +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dal.h" + +#define DALDEVICEID_VDEC_DEVICE 0x02000026 +#define DALDEVICEID_VDEC_PORTNAME "DSP_DAL_AQ_VID" + +#define VDEC_INTERFACE_VERSION 0x00020000 + +#define MAJOR_MASK 0xFFFF0000 +#define MINOR_MASK 0x0000FFFF + +#define VDEC_GET_MAJOR_VERSION(version) (((version)&MAJOR_MASK)>>16) + +#define VDEC_GET_MINOR_VERSION(version) ((version)&MINOR_MASK) + +#ifdef DEBUG_TRACE_VDEC +#define TRACE(fmt,x...) \ + do { pr_debug("%s:%d " fmt, __func__, __LINE__, ##x); } while (0) +#else +#define TRACE(fmt,x...) do { } while (0) +#endif + + +static DEFINE_MUTEX(idlecount_lock); +static int idlecount; +static struct wake_lock wakelock; +static struct wake_lock idlelock; + +static void prevent_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (++idlecount == 1) { + wake_lock(&idlelock); + wake_lock(&wakelock); + } + mutex_unlock(&idlecount_lock); +} + +static void allow_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (--idlecount == 0) { + wake_unlock(&idlelock); + wake_unlock(&wakelock); + } + mutex_unlock(&idlecount_lock); +} + + +enum { + VDEC_DALRPC_INITIALIZE = DAL_OP_FIRST_DEVICE_API, + VDEC_DALRPC_SETBUFFERS, + VDEC_DALRPC_FREEBUFFERS, + VDEC_DALRPC_QUEUE, + VDEC_DALRPC_SIGEOFSTREAM, + VDEC_DALRPC_FLUSH, + VDEC_DALRPC_REUSEFRAMEBUFFER, + VDEC_DALRPC_GETDECATTRIBUTES, +}; + +enum { + VDEC_ASYNCMSG_DECODE_DONE = 0xdec0de00, + VDEC_ASYNCMSG_REUSE_FRAME, +}; + +struct vdec_init_cfg { + u32 decode_done_evt; + u32 reuse_frame_evt; + struct vdec_config cfg; +}; + +struct vdec_buffer_status { + u32 data; + u32 status; +}; + +#define VDEC_MSG_MAX 128 + +struct vdec_msg_list { + struct list_head list; + struct vdec_msg vdec_msg; +}; + +struct vdec_mem_info { + u32 buf_type; + u32 id; + unsigned long phys_addr; + unsigned long len; + struct file *file; +}; + +struct vdec_mem_list { + struct list_head list; + struct vdec_mem_info mem; +}; + +struct vdec_data { + struct dal_client *vdec_handle; + struct list_head vdec_msg_list_head; + struct list_head vdec_msg_list_free; + wait_queue_head_t vdec_msg_evt; + spinlock_t vdec_list_lock; + struct list_head vdec_mem_list_head; + spinlock_t vdec_mem_list_lock; + int mem_initialized; + int running; + int close_decode; +}; + +static struct class *driver_class; +static dev_t vdec_device_no; +static struct cdev vdec_cdev; +static int ref_cnt; +static DEFINE_MUTEX(vdec_ref_lock); + +static inline int vdec_check_version(u32 client, u32 server) +{ + int ret = -EINVAL; + if ((VDEC_GET_MAJOR_VERSION(client) == VDEC_GET_MAJOR_VERSION(server)) + && (VDEC_GET_MINOR_VERSION(client) <= + VDEC_GET_MINOR_VERSION(server))) + ret = 0; + return ret; +} + +static int vdec_get_msg(struct vdec_data *vd, void *msg) +{ + struct vdec_msg_list *l; + unsigned long flags; + int ret = 0; + + if (!vd->running) + return -EPERM; + + spin_lock_irqsave(&vd->vdec_list_lock, flags); + list_for_each_entry_reverse(l, &vd->vdec_msg_list_head, list) { + if (copy_to_user(msg, &l->vdec_msg, sizeof(struct vdec_msg))) + pr_err("vdec_get_msg failed to copy_to_user!\n"); + if (l->vdec_msg.id == VDEC_MSG_REUSEINPUTBUFFER) + TRACE("reuse_input_buffer %d\n", l->vdec_msg.buf_id); + else if (l->vdec_msg.id == VDEC_MSG_FRAMEDONE) + TRACE("frame_done (stat=%d)\n", + l->vdec_msg.vfr_info.status); + else + TRACE("unknown msg (msgid=%d)\n", l->vdec_msg.id); + list_del(&l->list); + list_add(&l->list, &vd->vdec_msg_list_free); + ret = 1; + break; + } + spin_unlock_irqrestore(&vd->vdec_list_lock, flags); + + if (vd->close_decode) + ret = 1; + + return ret; +} + +static void vdec_put_msg(struct vdec_data *vd, struct vdec_msg *msg) +{ + struct vdec_msg_list *l; + unsigned long flags; + int found = 0; + + spin_lock_irqsave(&vd->vdec_list_lock, flags); + list_for_each_entry(l, &vd->vdec_msg_list_free, list) { + memcpy(&l->vdec_msg, msg, sizeof(struct vdec_msg)); + list_del(&l->list); + list_add(&l->list, &vd->vdec_msg_list_head); + found = 1; + break; + } + spin_unlock_irqrestore(&vd->vdec_list_lock, flags); + + if (found) + wake_up(&vd->vdec_msg_evt); + else + pr_err("vdec_put_msg can't find free list!\n"); +} + +static struct vdec_mem_list *vdec_get_mem_from_list(struct vdec_data *vd, + u32 pmem_id, u32 buf_type) +{ + struct vdec_mem_list *l; + unsigned long flags; + int found = 0; + + spin_lock_irqsave(&vd->vdec_mem_list_lock, flags); + list_for_each_entry(l, &vd->vdec_mem_list_head, list) { + if (l->mem.buf_type == buf_type && l->mem.id == pmem_id) { + found = 1; + break; + } + } + spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags); + + if (found) + return l; + else + return NULL; + +} + +static int vdec_initialize(struct vdec_data *vd, void *argp) +{ + struct vdec_config_sps vdec_cfg_sps; + struct vdec_init_cfg vi_cfg; + struct vdec_buf_req vdec_buf_req; + struct u8 *header; + int ret = 0; + + ret = copy_from_user(&vdec_cfg_sps, + &((struct vdec_init *)argp)->sps_cfg, + sizeof(vdec_cfg_sps)); + + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + + vi_cfg.decode_done_evt = VDEC_ASYNCMSG_DECODE_DONE; + vi_cfg.reuse_frame_evt = VDEC_ASYNCMSG_REUSE_FRAME; + memcpy(&vi_cfg.cfg, &vdec_cfg_sps.cfg, sizeof(struct vdec_config)); + + header = kmalloc(vdec_cfg_sps.seq.len, GFP_KERNEL); + if (!header) { + pr_err("%s: kmalloc failed\n", __func__); + return -ENOMEM; + } + + ret = copy_from_user(header, + ((struct vdec_init *)argp)->sps_cfg.seq.header, + vdec_cfg_sps.seq.len); + + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + kfree(header); + return ret; + } + + TRACE("vi_cfg: handle=%p fourcc=0x%x w=%d h=%d order=%d notify_en=%d " + "vc1_rb=%d h264_sd=%d h264_nls=%d pp_flag=%d fruc_en=%d\n", + vd->vdec_handle, vi_cfg.cfg.fourcc, vi_cfg.cfg.width, + vi_cfg.cfg.height, vi_cfg.cfg.order, vi_cfg.cfg.notify_enable, + vi_cfg.cfg.vc1_rowbase, vi_cfg.cfg.h264_startcode_detect, + vi_cfg.cfg.h264_nal_len_size, vi_cfg.cfg.postproc_flag, + vi_cfg.cfg.fruc_enable); + ret = dal_call_f13(vd->vdec_handle, VDEC_DALRPC_INITIALIZE, + &vi_cfg, sizeof(vi_cfg), + header, vdec_cfg_sps.seq.len, + &vdec_buf_req, sizeof(vdec_buf_req)); + + kfree(header); + + if (ret) + pr_err("%s: remote function failed (%d)\n", __func__, ret); + else + ret = copy_to_user(((struct vdec_init *)argp)->buf_req, + &vdec_buf_req, sizeof(vdec_buf_req)); + + vd->close_decode = 0; + return ret; +} + +static int vdec_setbuffers(struct vdec_data *vd, void *argp) +{ + struct vdec_buffer vmem; + struct vdec_mem_list *l; + unsigned long vstart; + unsigned long flags; + struct { + uint32_t size; + struct vdec_buf_info buf; + } rpc; + uint32_t res; + + int ret = 0; + + vd->mem_initialized = 0; + + ret = copy_from_user(&vmem, argp, sizeof(vmem)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + + l = kzalloc(sizeof(struct vdec_mem_list), GFP_KERNEL); + if (!l) { + pr_err("%s: kzalloc failed!\n", __func__); + return -ENOMEM; + } + + l->mem.id = vmem.pmem_id; + l->mem.buf_type = vmem.buf.buf_type; + + ret = get_pmem_file(l->mem.id, &l->mem.phys_addr, &vstart, + &l->mem.len, &l->mem.file); + if (ret) { + pr_err("%s: get_pmem_fd failed\n", __func__); + goto err_get_pmem_file; + } + + TRACE("pmem_id=%d (phys=0x%08lx len=0x%lx) buftype=%d num_buf=%d " + "islast=%d src_id=%d offset=0x%08x size=0x%x\n", + vmem.pmem_id, l->mem.phys_addr, l->mem.len, + vmem.buf.buf_type, vmem.buf.num_buf, vmem.buf.islast, + vmem.buf.region.src_id, vmem.buf.region.offset, + vmem.buf.region.size); + + /* input buffers */ + if ((vmem.buf.region.offset + vmem.buf.region.size) > l->mem.len) { + pr_err("%s: invalid input buffer offset!\n", __func__); + ret = -EINVAL; + goto err_bad_offset; + + } + vmem.buf.region.offset += l->mem.phys_addr; + + rpc.size = sizeof(vmem.buf); + memcpy(&rpc.buf, &vmem.buf, sizeof(struct vdec_buf_info)); + + + ret = dal_call(vd->vdec_handle, VDEC_DALRPC_SETBUFFERS, 5, + &rpc, sizeof(rpc), &res, sizeof(res)); + + if (ret < 4) { + pr_err("%s: remote function failed (%d)\n", __func__, ret); + ret = -EIO; + goto err_dal_call; + } + + spin_lock_irqsave(&vd->vdec_mem_list_lock, flags); + list_add(&l->list, &vd->vdec_mem_list_head); + spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags); + + vd->mem_initialized = 1; + return ret; + +err_dal_call: +err_bad_offset: + put_pmem_file(l->mem.file); +err_get_pmem_file: + kfree(l); + return ret; +} + +static int vdec_queue(struct vdec_data *vd, void *argp) +{ + struct { + uint32_t size; + struct vdec_input_buf_info buf_info; + uint32_t osize; + } rpc; + struct vdec_mem_list *l; + struct { + uint32_t result; + uint32_t size; + struct vdec_queue_status status; + } rpc_res; + + u32 pmem_id; + int ret = 0; + + if (!vd->mem_initialized) { + pr_err("%s: memory is not being initialized!\n", __func__); + return -EPERM; + } + + ret = copy_from_user(&rpc.buf_info, + &((struct vdec_input_buf *)argp)->buffer, + sizeof(rpc.buf_info)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + + ret = copy_from_user(&pmem_id, + &((struct vdec_input_buf *)argp)->pmem_id, + sizeof(u32)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + + l = vdec_get_mem_from_list(vd, pmem_id, VDEC_BUFFER_TYPE_INPUT); + + if (NULL == l) { + pr_err("%s: not able to find the buffer from list\n", __func__); + return -EPERM; + } + + if ((rpc.buf_info.size + rpc.buf_info.offset) >= l->mem.len) { + pr_err("%s: invalid queue buffer offset!\n", __func__); + return -EINVAL; + } + + rpc.buf_info.offset += l->mem.phys_addr; + rpc.size = sizeof(struct vdec_input_buf_info); + rpc.osize = sizeof(struct vdec_queue_status); + + ret = dal_call(vd->vdec_handle, VDEC_DALRPC_QUEUE, 8, + &rpc, sizeof(rpc), &rpc_res, sizeof(rpc_res)); + if (ret < 4) { + pr_err("%s: remote function failed (%d)\n", __func__, ret); + ret = -EIO; + } + return ret; +} + +static int vdec_reuse_framebuffer(struct vdec_data *vd, void *argp) +{ + u32 buf_id; + int ret = 0; + + ret = copy_from_user(&buf_id, argp, sizeof(buf_id)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + + ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_REUSEFRAMEBUFFER, + buf_id); + if (ret) + pr_err("%s: remote function failed (%d)\n", __func__, ret); + + return ret; +} + +static int vdec_flush(struct vdec_data *vd, void *argp) +{ + u32 flush_type; + int ret = 0; + + ret = copy_from_user(&flush_type, argp, sizeof(flush_type)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + + TRACE("flush_type=%d\n", flush_type); + ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_FLUSH, flush_type); + if (ret) { + pr_err("%s: remote function failed (%d)\n", __func__, ret); + return ret; + } + + return ret; +} + +static int vdec_close(struct vdec_data *vd, void *argp) +{ + struct vdec_mem_list *l; + int ret = 0; + + pr_info("q6vdec_close()\n"); + vd->close_decode = 1; + wake_up(&vd->vdec_msg_evt); + ret = dal_call_f0(vd->vdec_handle, DAL_OP_CLOSE, 0); + if (ret) + pr_err("%s: failed to close daldevice (%d)\n", __func__, ret); + + if (vd->mem_initialized) { + list_for_each_entry(l, &vd->vdec_mem_list_head, list) + put_pmem_file(l->mem.file); + } + + return ret; +} +static int vdec_getdecattributes(struct vdec_data *vd, void *argp) +{ + struct { + uint32_t status; + uint32_t size; + struct vdec_dec_attributes dec_attr; + } rpc; + uint32_t inp; + int ret = 0; + inp = sizeof(struct vdec_dec_attributes); + + ret = dal_call(vd->vdec_handle, VDEC_DALRPC_GETDECATTRIBUTES, 9, + &inp, sizeof(inp), &rpc, sizeof(rpc)); + if (ret < 4 || rpc.size != sizeof(struct vdec_dec_attributes)) { + pr_err("%s: remote function failed (%d)\n", __func__, ret); + ret = -EIO; + } else + ret = + copy_to_user(((struct vdec_dec_attributes *)argp), + &rpc.dec_attr, sizeof(rpc.dec_attr)); + return ret; +} + +static int vdec_freebuffers(struct vdec_data *vd, void *argp) +{ + struct vdec_buffer vmem; + struct vdec_mem_list *l; + struct { + uint32_t size; + struct vdec_buf_info buf; + } rpc; + uint32_t res; + + int ret = 0; + + if (!vd->mem_initialized) { + pr_err("%s: memory is not being initialized!\n", __func__); + return -EPERM; + } + + ret = copy_from_user(&vmem, argp, sizeof(vmem)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + + l = vdec_get_mem_from_list(vd, vmem.pmem_id, vmem.buf.buf_type); + + if (NULL == l) { + pr_err("%s: not able to find the buffer from list\n", __func__); + return -EPERM; + } + + /* input buffers */ + if ((vmem.buf.region.offset + vmem.buf.region.size) > l->mem.len) { + pr_err("%s: invalid input buffer offset!\n", __func__); + return -EINVAL; + + } + vmem.buf.region.offset += l->mem.phys_addr; + + rpc.size = sizeof(vmem.buf); + memcpy(&rpc.buf, &vmem.buf, sizeof(struct vdec_buf_info)); + + ret = dal_call(vd->vdec_handle, VDEC_DALRPC_FREEBUFFERS, 5, + &rpc, sizeof(rpc), &res, sizeof(res)); + if (ret < 4) { + pr_err("%s: remote function failed (%d)\n", __func__, ret); + } + + return ret; +} +static long vdec_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct vdec_data *vd = file->private_data; + void __user *argp = (void __user *)arg; + int ret = 0; + + if (!vd->running) + return -EPERM; + + switch (cmd) { + case VDEC_IOCTL_INITIALIZE: + ret = vdec_initialize(vd, argp); + break; + + case VDEC_IOCTL_SETBUFFERS: + ret = vdec_setbuffers(vd, argp); + break; + + case VDEC_IOCTL_QUEUE: + TRACE("VDEC_IOCTL_QUEUE (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + ret = vdec_queue(vd, argp); + break; + + case VDEC_IOCTL_REUSEFRAMEBUFFER: + TRACE("VDEC_IOCTL_REUSEFRAMEBUFFER (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + ret = vdec_reuse_framebuffer(vd, argp); + break; + + case VDEC_IOCTL_FLUSH: + ret = vdec_flush(vd, argp); + break; + + case VDEC_IOCTL_EOS: + TRACE("VDEC_IOCTL_EOS (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + ret = dal_call_f0(vd->vdec_handle, VDEC_DALRPC_SIGEOFSTREAM, 0); + if (ret) + pr_err("%s: remote function failed (%d)\n", + __func__, ret); + break; + + case VDEC_IOCTL_GETMSG: + TRACE("VDEC_IOCTL_GETMSG (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + wait_event_interruptible(vd->vdec_msg_evt, + vdec_get_msg(vd, argp)); + + if (vd->close_decode) + ret = -EINTR; + break; + + case VDEC_IOCTL_CLOSE: + ret = vdec_close(vd, argp); + break; + + case VDEC_IOCTL_GETDECATTRIBUTES: + TRACE("VDEC_IOCTL_GETDECATTRIBUTES (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + ret = vdec_getdecattributes(vd, argp); + + if (ret) + pr_err("%s: remote function failed (%d)\n", + __func__, ret); + break; + + case VDEC_IOCTL_FREEBUFFERS: + TRACE("VDEC_IOCTL_FREEBUFFERS (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + ret = vdec_freebuffers(vd, argp); + + if (ret) + pr_err("%s: remote function failed (%d)\n", + __func__, ret); + break; + + default: + pr_err("%s: invalid ioctl!\n", __func__); + ret = -EINVAL; + break; + } + + TRACE("ioctl done (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + + return ret; +} + +static void vdec_dcdone_handler(struct vdec_data *vd, void *frame, + uint32_t frame_size) +{ + struct vdec_msg msg; + struct vdec_mem_list *l; + unsigned long flags; + int found = 0; + + if (frame_size != sizeof(struct vdec_frame_info)) { + pr_warning("%s: msg size mismatch %d != %d\n", __func__, + frame_size, sizeof(struct vdec_frame_info)); + return; + } + + memcpy(&msg.vfr_info, (struct vdec_frame_info *)frame, + sizeof(struct vdec_frame_info)); + + if (msg.vfr_info.status == VDEC_FRAME_DECODE_OK) { + spin_lock_irqsave(&vd->vdec_mem_list_lock, flags); + list_for_each_entry(l, &vd->vdec_mem_list_head, list) { + if ((l->mem.buf_type == VDEC_BUFFER_TYPE_OUTPUT) && + (msg.vfr_info.offset >= l->mem.phys_addr) && + (msg.vfr_info.offset < + (l->mem.phys_addr + l->mem.len))) { + found = 1; + msg.vfr_info.offset -= l->mem.phys_addr; + msg.vfr_info.data2 = l->mem.id; + break; + } + } + spin_unlock_irqrestore(&vd->vdec_mem_list_lock, flags); + } + + if (found || (msg.vfr_info.status != VDEC_FRAME_DECODE_OK)) { + msg.id = VDEC_MSG_FRAMEDONE; + vdec_put_msg(vd, &msg); + } else { + pr_err("%s: invalid phys addr = 0x%x\n", + __func__, msg.vfr_info.offset); + } + +} + +static void vdec_reuseibuf_handler(struct vdec_data *vd, void *bufstat, + uint32_t bufstat_size) +{ + struct vdec_buffer_status *vdec_bufstat; + struct vdec_msg msg; + + /* TODO: how do we signal the client? If they are waiting on a + * message in an ioctl, they may block forever */ + if (bufstat_size != sizeof(struct vdec_buffer_status)) { + pr_warning("%s: msg size mismatch %d != %d\n", __func__, + bufstat_size, sizeof(struct vdec_buffer_status)); + return; + } + vdec_bufstat = (struct vdec_buffer_status *)bufstat; + msg.id = VDEC_MSG_REUSEINPUTBUFFER; + msg.buf_id = vdec_bufstat->data; + vdec_put_msg(vd, &msg); +} + +static void callback(void *data, int len, void *cookie) +{ + struct vdec_data *vd = (struct vdec_data *)cookie; + uint32_t *tmp = (uint32_t *) data; + + if (!vd->mem_initialized) { + pr_err("%s:memory not initialize but callback called!\n", + __func__); + return; + } + + TRACE("vdec_async: tmp=0x%08x 0x%08x 0x%08x\n", tmp[0], tmp[1], tmp[2]); + switch (tmp[0]) { + case VDEC_ASYNCMSG_DECODE_DONE: + vdec_dcdone_handler(vd, &tmp[3], tmp[2]); + break; + case VDEC_ASYNCMSG_REUSE_FRAME: + vdec_reuseibuf_handler(vd, &tmp[3], tmp[2]); + break; + default: + pr_err("%s: Unknown async message from DSP id=0x%08x sz=%u\n", + __func__, tmp[0], tmp[2]); + } +} +static int vdec_open(struct inode *inode, struct file *file) +{ + int ret; + int i; + struct vdec_msg_list *l; + struct vdec_data *vd; + + pr_info("q6vdec_open()\n"); + mutex_lock(&vdec_ref_lock); + if (ref_cnt > 0) { + pr_err("%s: Instance alredy running\n", __func__); + mutex_unlock(&vdec_ref_lock); + return -ENOMEM; + } + ref_cnt++; + mutex_unlock(&vdec_ref_lock); + vd = kmalloc(sizeof(struct vdec_data), GFP_KERNEL); + if (!vd) { + pr_err("%s: kmalloc failed\n", __func__); + ret = -ENOMEM; + goto vdec_open_err_handle_vd; + } + file->private_data = vd; + + vd->mem_initialized = 0; + INIT_LIST_HEAD(&vd->vdec_msg_list_head); + INIT_LIST_HEAD(&vd->vdec_msg_list_free); + INIT_LIST_HEAD(&vd->vdec_mem_list_head); + init_waitqueue_head(&vd->vdec_msg_evt); + + spin_lock_init(&vd->vdec_list_lock); + spin_lock_init(&vd->vdec_mem_list_lock); + for (i = 0; i < VDEC_MSG_MAX; i++) { + l = kzalloc(sizeof(struct vdec_msg_list), GFP_KERNEL); + if (!l) { + pr_err("%s: kzalloc failed!\n", __func__); + ret = -ENOMEM; + goto vdec_open_err_handle_list; + } + list_add(&l->list, &vd->vdec_msg_list_free); + } + + vd->vdec_handle = dal_attach(DALDEVICEID_VDEC_DEVICE, + DALDEVICEID_VDEC_PORTNAME, + callback, vd); + + if (!vd->vdec_handle) { + pr_err("%s: failed to attach \n", __func__); + ret = -EIO; + goto vdec_open_err_handle_list; + } + + vd->running = 1; + prevent_sleep(); + return 0; + +vdec_open_err_handle_list: + { + struct vdec_msg_list *l, *n; + list_for_each_entry_safe(l, n, &vd->vdec_msg_list_free, list) { + list_del(&l->list); + kfree(l); + } + } +vdec_open_err_handle_vd: + kfree(vd); + return ret; +} + +static int vdec_release(struct inode *inode, struct file *file) +{ + int ret; + struct vdec_msg_list *l, *n; + struct vdec_mem_list *m, *k; + struct vdec_data *vd = file->private_data; + + vd->running = 0; + wake_up_all(&vd->vdec_msg_evt); + + if (!vd->close_decode) + vdec_close(vd, NULL); + + ret = dal_detach(vd->vdec_handle); + if (ret) + printk(KERN_INFO "%s: failed to detach (%d)\n", __func__, ret); + + list_for_each_entry_safe(l, n, &vd->vdec_msg_list_free, list) { + list_del(&l->list); + kfree(l); + } + + list_for_each_entry_safe(l, n, &vd->vdec_msg_list_head, list) { + list_del(&l->list); + kfree(l); + } + + list_for_each_entry_safe(m, k, &vd->vdec_mem_list_head, list) { + list_del(&m->list); + kfree(m); + } + mutex_lock(&vdec_ref_lock); + BUG_ON(ref_cnt <= 0); + ref_cnt--; + mutex_unlock(&vdec_ref_lock); + kfree(vd); + allow_sleep(); + return 0; +} + +static const struct file_operations vdec_fops = { + .owner = THIS_MODULE, + .open = vdec_open, + .release = vdec_release, + .unlocked_ioctl = vdec_ioctl, +}; + +static int __init vdec_init(void) +{ + struct device *class_dev; + int rc = 0; + + wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "vdec_idle"); + wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "vdec_suspend"); + + rc = alloc_chrdev_region(&vdec_device_no, 0, 1, "vdec"); + if (rc < 0) { + pr_err("%s: alloc_chrdev_region failed %d\n", __func__, rc); + return rc; + } + + driver_class = class_create(THIS_MODULE, "vdec"); + if (IS_ERR(driver_class)) { + rc = -ENOMEM; + pr_err("%s: class_create failed %d\n", __func__, rc); + goto vdec_init_err_unregister_chrdev_region; + } + class_dev = device_create(driver_class, NULL, + vdec_device_no, NULL, "vdec"); + if (!class_dev) { + pr_err("%s: class_device_create failed %d\n", __func__, rc); + rc = -ENOMEM; + goto vdec_init_err_class_destroy; + } + + cdev_init(&vdec_cdev, &vdec_fops); + vdec_cdev.owner = THIS_MODULE; + rc = cdev_add(&vdec_cdev, MKDEV(MAJOR(vdec_device_no), 0), 1); + + if (rc < 0) { + pr_err("%s: cdev_add failed %d\n", __func__, rc); + goto vdec_init_err_class_device_destroy; + } + + return 0; + +vdec_init_err_class_device_destroy: + device_destroy(driver_class, vdec_device_no); +vdec_init_err_class_destroy: + class_destroy(driver_class); +vdec_init_err_unregister_chrdev_region: + unregister_chrdev_region(vdec_device_no, 1); + return rc; +} + +static void __exit vdec_exit(void) +{ + device_destroy(driver_class, vdec_device_no); + class_destroy(driver_class); + unregister_chrdev_region(vdec_device_no, 1); +} + +MODULE_DESCRIPTION("video decoder driver for QSD platform"); +MODULE_VERSION("2.00"); + +module_init(vdec_init); +module_exit(vdec_exit); diff --git a/arch/arm/mach-msm/qdsp6/msm_q6venc.c b/arch/arm/mach-msm/qdsp6/msm_q6venc.c new file mode 100644 index 0000000000000..9010b3cb322dd --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/msm_q6venc.c @@ -0,0 +1,630 @@ +/* + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * Copyright (c) 2009, Google Inc. + * + * Original authors: Code Aurora Forum + * Major cleanup: Dima Zavin + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +//#define DEBUG 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dal.h" + +#define DALDEVICEID_VENC_DEVICE 0x0200002D +#define DALDEVICEID_VENC_PORTNAME "DSP_DAL_AQ_VID" + +enum { + VENC_DALRPC_INITIALIZE = DAL_OP_FIRST_DEVICE_API, + VENC_DALRPC_SET_CB_CHANNEL, + VENC_DALRPC_ENCODE, + VENC_DALRPC_INTRA_REFRESH, + VENC_DALRPC_RC_CONFIG, + VENC_DALRPC_ENCODE_CONFIG, + VENC_DALRPC_STOP, +}; + +struct callback_event_data { + u32 data_notify_event; + u32 enc_cb_handle; + u32 empty_input_buffer_event; +}; + +struct buf_info { + unsigned long paddr; + unsigned long vaddr; + struct file *file; + struct venc_buf venc_buf; +}; + +#define VENC_MAX_BUF_NUM 15 +#define RLC_MAX_BUF_NUM 2 +#define BITS_PER_PIXEL 12 +#define PIXELS_PER_MACROBLOCK 16 + +#define VENC_CB_EVENT_ID 0xd0e4c0de + +struct q6venc_dev { + struct dal_client *venc; + struct callback_event_data cb_ev_data; + bool stop_encode; + struct buf_info rlc_bufs[RLC_MAX_BUF_NUM]; + unsigned int rlc_buf_index; + unsigned int rlc_buf_len; + unsigned int enc_buf_size; + struct buf_info enc_bufs[VENC_MAX_BUF_NUM]; + unsigned int num_enc_bufs; + wait_queue_head_t encode_wq; + + /* protects all state in q6venc_dev except for cb stuff below */ + struct mutex lock; + + /* protects encode_done and done_frame inside the callback */ + spinlock_t done_lock; + struct frame_type done_frame; + bool encode_done; +}; + +static int get_buf_info(struct buf_info *buf_info, struct venc_buf *venc_buf) +{ + unsigned long len; + unsigned long vaddr; + unsigned long paddr; + struct file *file; + int ret; + + ret = get_pmem_file(venc_buf->fd, &paddr, &vaddr, &len, &file); + if (ret) { + pr_err("%s: get_pmem_file failed for fd=%d offset=%ld\n", + __func__, venc_buf->fd, venc_buf->offset); + return ret; + } else if (venc_buf->offset >= len) { + /* XXX: we really should check venc_buf->size too, but userspace + * sometimes leaves this uninitialized (in encode ioctl) */ + pr_err("%s: invalid offset/size (%ld + %ld > %ld) for fd=%d\n", + __func__, venc_buf->offset, venc_buf->size, len, + venc_buf->fd); + put_pmem_file(file); + return -EINVAL; + } + + buf_info->file = file; + buf_info->paddr = paddr + venc_buf->offset; + buf_info->vaddr = vaddr; + memcpy(&buf_info->venc_buf, venc_buf, sizeof(struct venc_buf)); + return 0; +} + +static void put_buf_info(struct buf_info *buf_info) +{ + if (!buf_info || !buf_info->file) + return; + put_pmem_file(buf_info->file); + buf_info->file = NULL; +} + +static void q6venc_callback(void *context, void *data, uint32_t len) +{ + struct q6venc_dev *q6venc = context; + struct q6_frame_type *q6frame = data; + struct buf_info *rlc_buf; + unsigned long flags; + int i; + + pr_debug("%s \n", __func__); + + spin_lock_irqsave(&q6venc->done_lock, flags); + q6venc->encode_done = true; + for (i = 0; i < RLC_MAX_BUF_NUM; ++i) { + rlc_buf = &q6venc->rlc_bufs[i]; + if (rlc_buf->paddr == q6frame->frame_addr) + goto frame_found; + } + + pr_err("%s: got incorrect phy address 0x%08x from q6 \n", __func__, + q6frame->frame_addr); + q6venc->done_frame.q6_frame_type.frame_len = 0; + wake_up_interruptible(&q6venc->encode_wq); + goto done; + +frame_found: + memcpy(&q6venc->done_frame.frame_addr, &rlc_buf->venc_buf, + sizeof(struct venc_buf)); + memcpy(&q6venc->done_frame.q6_frame_type, q6frame, + sizeof(struct q6_frame_type)); + + dmac_inv_range((const void *)q6venc->rlc_bufs[i].vaddr, + (const void *)(q6venc->rlc_bufs[i].vaddr + + q6venc->rlc_buf_len)); + + wake_up_interruptible(&q6venc->encode_wq); + +done: + spin_unlock_irqrestore(&q6venc->done_lock, flags); +} + +static void callback(void *data, int len, void *cookie) +{ + struct q6venc_dev *ve = (struct q6venc_dev *)cookie; + uint32_t *tmp = (uint32_t *) data; + + if (tmp[0] == VENC_CB_EVENT_ID) + q6venc_callback(ve, &tmp[3], tmp[2]); + else + pr_err("%s: Unknown callback received for %p\n", __func__, ve); +} + +static int q6venc_open(struct inode *inode, struct file *file) +{ + struct q6venc_dev *q6venc; + int err; + + q6venc = kzalloc(sizeof(struct q6venc_dev), GFP_KERNEL); + if (!q6venc) { + pr_err("%s: Unable to allocate memory for q6venc_dev\n", + __func__); + return -ENOMEM; + } + + file->private_data = q6venc; + + init_waitqueue_head(&q6venc->encode_wq); + mutex_init(&q6venc->lock); + spin_lock_init(&q6venc->done_lock); + + q6venc->venc = dal_attach(DALDEVICEID_VENC_DEVICE, + DALDEVICEID_VENC_PORTNAME, + callback, q6venc); + if (!q6venc->venc) { + pr_err("%s: dal_attach failed\n", __func__); + err = -EIO; + goto err_dal_attach; + } + + q6venc->cb_ev_data.enc_cb_handle = VENC_CB_EVENT_ID; + err = dal_call_f5(q6venc->venc, VENC_DALRPC_SET_CB_CHANNEL, + &q6venc->cb_ev_data, sizeof(q6venc->cb_ev_data)); + if (err) { + pr_err("%s: set_cb_channgel failed\n",__func__); + goto err_dal_call_set_cb; + } + + pr_info("%s() handle=%p enc_cb=%08x\n", __func__, q6venc->venc, + q6venc->cb_ev_data.enc_cb_handle); + + return 0; + +err_dal_call_set_cb: + dal_detach(q6venc->venc); +err_dal_attach: + file->private_data = NULL; + mutex_destroy(&q6venc->lock); + kfree(q6venc); + return err; +} + +static int q6venc_release(struct inode *inode, struct file *file) +{ + struct q6venc_dev *q6venc; + int id; + + q6venc = file->private_data; + file->private_data = NULL; + + pr_info("q6venc_close() handle=%p\n", q6venc->venc); + + dal_detach(q6venc->venc); + + for (id = 0; id < q6venc->num_enc_bufs; id++) + put_buf_info(&q6venc->enc_bufs[id]); + put_buf_info(&q6venc->rlc_bufs[0]); + put_buf_info(&q6venc->rlc_bufs[1]); + + mutex_destroy(&q6venc->lock); + kfree(q6venc); + return 0; +} + +static int q6_config_encode(struct q6venc_dev *q6venc, uint32_t type, + struct init_config *init_config) +{ + struct q6_init_config *q6_init_config = &init_config->q6_init_config; + int ret; + int i; + + mutex_lock(&q6venc->lock); + + if (q6venc->num_enc_bufs != 0) { + pr_err("%s: multiple sessions not supported\n", __func__); + ret = -EBUSY; + goto err_busy; + } + + ret = get_buf_info(&q6venc->enc_bufs[0], &init_config->ref_frame_buf1); + if (ret) { + pr_err("%s: can't get ref_frame_buf1\n", __func__); + goto err_get_ref_frame_buf1; + } + + ret = get_buf_info(&q6venc->enc_bufs[1], &init_config->ref_frame_buf2); + if (ret) { + pr_err("%s: can't get ref_frame_buf2\n", __func__); + goto err_get_ref_frame_buf2; + } + + ret = get_buf_info(&q6venc->rlc_bufs[0], &init_config->rlc_buf1); + if (ret) { + pr_err("%s: can't get rlc_buf1\n", __func__); + goto err_get_rlc_buf1; + } + + ret = get_buf_info(&q6venc->rlc_bufs[1], &init_config->rlc_buf2); + if (ret) { + pr_err("%s: can't get rlc_buf2\n", __func__); + goto err_get_rlc_buf2; + } + q6venc->rlc_buf_len = 2 * q6_init_config->rlc_buf_length; + q6venc->num_enc_bufs = 2; + + q6venc->enc_buf_size = + (q6_init_config->enc_frame_width_inmb * PIXELS_PER_MACROBLOCK) * + (q6_init_config->enc_frame_height_inmb * PIXELS_PER_MACROBLOCK) * + BITS_PER_PIXEL / 8; + + q6_init_config->ref_frame_buf1_phy = q6venc->enc_bufs[0].paddr; + q6_init_config->ref_frame_buf2_phy = q6venc->enc_bufs[1].paddr; + q6_init_config->rlc_buf1_phy = q6venc->rlc_bufs[0].paddr; + q6_init_config->rlc_buf2_phy = q6venc->rlc_bufs[1].paddr; + + // The DSP may use the rlc_bufs during initialization, + for (i=0; irlc_bufs[i].vaddr, + (const void *)(q6venc->rlc_bufs[i].vaddr + + q6venc->rlc_buf_len)); + } + + ret = dal_call_f5(q6venc->venc, type, q6_init_config, + sizeof(struct q6_init_config)); + if (ret) { + pr_err("%s: rpc failed \n", __func__); + goto err_dal_rpc_init; + } + mutex_unlock(&q6venc->lock); + return 0; + +err_dal_rpc_init: + q6venc->num_enc_bufs = 0; + put_pmem_file(q6venc->rlc_bufs[1].file); +err_get_rlc_buf2: + put_pmem_file(q6venc->rlc_bufs[0].file); +err_get_rlc_buf1: + put_pmem_file(q6venc->enc_bufs[1].file); +err_get_ref_frame_buf2: + put_pmem_file(q6venc->enc_bufs[0].file); +err_get_ref_frame_buf1: +err_busy: + mutex_unlock(&q6venc->lock); + return ret; +} + +static int q6_encode(struct q6venc_dev *q6venc, struct encode_param *enc_param) +{ + struct q6_encode_param *q6_param = &enc_param->q6_encode_param; + struct file *file; + struct buf_info *buf; + int i; + int ret; + int rlc_buf_index; + + pr_debug("y_addr fd=%d offset=0x%08lx uv_offset=0x%08lx\n", + enc_param->y_addr.fd, enc_param->y_addr.offset, + enc_param->uv_offset); + + file = fget(enc_param->y_addr.fd); + if (!file) { + pr_err("%s: invalid encode buffer fd %d\n", __func__, + enc_param->y_addr.fd); + return -EBADF; + } + + mutex_lock(&q6venc->lock); + + for (i = 0; i < q6venc->num_enc_bufs; i++) { + buf = &q6venc->enc_bufs[i]; + if (buf->file == file + && buf->venc_buf.offset == enc_param->y_addr.offset) + break; + } + + if (i == q6venc->num_enc_bufs) { + if (q6venc->num_enc_bufs == VENC_MAX_BUF_NUM) { + pr_err("%s: too many input buffers\n", __func__); + ret = -ENOMEM; + goto done; + } + + buf = &q6venc->enc_bufs[q6venc->num_enc_bufs]; + ret = get_buf_info(buf, &enc_param->y_addr); + if (ret) { + pr_err("%s: can't get encode buffer\n", __func__); + ret = -EINVAL; + goto done; + } + + if (!IS_ALIGNED(buf->paddr, PAGE_SIZE)) { + pr_err("%s: input buffer not 4k aligned\n", __func__); + put_buf_info(buf); + ret = -EINVAL; + goto done; + } + q6venc->num_enc_bufs++; + } + + // We must invalidate the buffer that the DSP will write to + // to ensure that a dirty cache line doesn't get flushed on + // top of the data that the DSP is writing. + // Unfortunately, we have to predict which rlc_buf index the + // DSP is going to write to. We assume it will write to buf + // 0 the first time we call q6_encode, and alternate afterwards + rlc_buf_index = q6venc->rlc_buf_index; + dmac_inv_range((const void *)q6venc->rlc_bufs[rlc_buf_index].vaddr, + (const void *)(q6venc->rlc_bufs[rlc_buf_index].vaddr + + q6venc->rlc_buf_len)); + q6venc->rlc_buf_index = (q6venc->rlc_buf_index + 1) % RLC_MAX_BUF_NUM; + + q6_param->luma_addr = buf->paddr; + q6_param->chroma_addr = q6_param->luma_addr + enc_param->uv_offset; + pr_debug("luma_addr=0x%08x chroma_addr=0x%08x\n", q6_param->luma_addr, + q6_param->chroma_addr); + + // Ideally, each ioctl that passed in a data buffer would include the size + // of the input buffer, so we can properly flush the cache on it. Since + // userspace does not fill in the size fields, we have to assume the size + // based on the encoder configuration for now. + flush_pmem_file(buf->file, enc_param->y_addr.offset, + q6venc->enc_buf_size); + + ret = dal_call_f5(q6venc->venc, VENC_DALRPC_ENCODE, q6_param, + sizeof(struct q6_encode_param)); + if (ret) { + pr_err("%s: encode rpc failed\n", __func__); + goto done; + } + + ret = 0; + +done: + mutex_unlock(&q6venc->lock); + fput(file); + return ret; +} + +static int q6venc_ioctl(struct inode *inode, struct file *file, + unsigned cmd, unsigned long arg) +{ + struct q6venc_dev *q6venc = file->private_data; + struct init_config config; + struct encode_param encode_param; + struct intra_refresh intra_refresh; + struct rc_config rc_config; + struct frame_type frame_done; + unsigned int id; + unsigned long flags; + int err = 0; + + if (!q6venc) { + pr_err("%s: file has no private data\n", __func__); + return -ENODEV; + } + + pr_debug("%s\n", __func__); + + switch (cmd) { + case VENC_IOCTL_INITIALIZE: + pr_debug("%s: VENC_IOCTL_INITIALIZE\n", __func__); + if (copy_from_user(&config, (void __user *)arg, sizeof(config))) + return -EFAULT; + err = q6_config_encode(q6venc, VENC_DALRPC_INITIALIZE, &config); + break; + + case VENC_IOCTL_ENCODE_CONFIG: + pr_debug("%s: VENC_IOCTL_ENCODE_CONFIG\n", __func__); + if (copy_from_user(&config, (void __user *)arg, sizeof(config))) + return -EFAULT; + + err = q6_config_encode(q6venc, VENC_DALRPC_ENCODE_CONFIG, + &config); + break; + + case VENC_IOCTL_ENCODE: + pr_debug("%s: VENC_IOCTL_ENCODE\n", __func__); + if (copy_from_user(&encode_param, (void __user *)arg, + sizeof(encode_param))) + return -EFAULT; + err = q6_encode(q6venc, &encode_param); + break; + + case VENC_IOCTL_INTRA_REFRESH: + pr_debug("%s: VENC_IOCTL_INTRA_REFRESH\n", __func__); + if (copy_from_user(&intra_refresh, (void __user *)arg, + sizeof(intra_refresh))) + return -EFAULT; + + mutex_lock(&q6venc->lock); + err = dal_call_f5(q6venc->venc, VENC_DALRPC_INTRA_REFRESH, + &intra_refresh, sizeof(struct intra_refresh)); + mutex_unlock(&q6venc->lock); + if (err) + pr_err("%s: intra_refresh rpc failed\n", __func__); + break; + + case VENC_IOCTL_RC_CONFIG: + pr_debug("%s: VENC_IOCTL_RC_CONFIG\n", __func__); + if (copy_from_user(&rc_config, (void __user *)arg, + sizeof(rc_config))) + return -EFAULT; + + mutex_lock(&q6venc->lock); + err = dal_call_f5(q6venc->venc, VENC_DALRPC_RC_CONFIG, + &rc_config, sizeof(rc_config)); + mutex_unlock(&q6venc->lock); + if (err) + pr_err("%s: dal_call_f5 failed\n", __func__); + break; + + case VENC_IOCTL_STOP: + pr_debug("%s: VENC_IOCTL_STOP\n", __func__); + + mutex_lock(&q6venc->lock); + err = dal_call_f0(q6venc->venc, VENC_DALRPC_STOP, 1); + if (err) + pr_err("%s: dal_rpc STOP call failed\n", __func__); + + /* XXX: if the dal call fails we still want to continue to free + * the buffers. Is this correct? */ + for (id = 0; id < q6venc->num_enc_bufs; id++) + put_buf_info(&q6venc->enc_bufs[id]); + put_buf_info(&q6venc->rlc_bufs[0]); + put_buf_info(&q6venc->rlc_bufs[1]); + q6venc->num_enc_bufs = 0; + q6venc->stop_encode = true; + mutex_unlock(&q6venc->lock); + break; + + case VENC_IOCTL_WAIT_FOR_ENCODE: + pr_debug("%s: waiting for encode done event \n", __func__); + err = wait_event_interruptible(q6venc->encode_wq, + (q6venc->encode_done || q6venc->stop_encode)); + if (err < 0) { + err = -ERESTARTSYS; + break; + } + + mutex_lock(&q6venc->lock); + if (q6venc->stop_encode) { + q6venc->stop_encode = false; + mutex_unlock(&q6venc->lock); + pr_debug("%s: Received Stop encode event \n", __func__); + err = -EINTR; + break; + } + + spin_lock_irqsave(&q6venc->done_lock, flags); + if (!q6venc->encode_done) { + spin_unlock_irqrestore(&q6venc->done_lock, flags); + pr_err("%s: encoding not stopped, and is not done.\n", + __func__); + err = -EIO; + break; + } + + memcpy(&frame_done, &q6venc->done_frame, + sizeof(struct frame_type)); + q6venc->encode_done = false; + spin_unlock_irqrestore(&q6venc->done_lock, flags); + mutex_unlock(&q6venc->lock); + + if (frame_done.q6_frame_type.frame_len == 0) { + pr_debug("%s: got incorrect address from q6\n", + __func__); + err = -EIO; + break; + } + + pr_debug("%s: done encoding \n", __func__); + if (copy_to_user((void __user *)arg, &frame_done, + sizeof(struct frame_type))) + err = -EFAULT; + break; + + case VENC_IOCTL_STOP_ENCODE: + pr_debug("%s: Stop encode event \n", __func__); + mutex_lock(&q6venc->lock); + q6venc->stop_encode = true; + wake_up_interruptible(&q6venc->encode_wq); + mutex_unlock(&q6venc->lock); + break; + + default: + err = -ENOTTY; + break; + } + + return err; +} + +static const struct file_operations q6venc_dev_fops = { + .owner = THIS_MODULE, + .open = q6venc_open, + .release = q6venc_release, + .ioctl = q6venc_ioctl, +}; + +static struct miscdevice q6venc_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "q6venc", + .fops = &q6venc_dev_fops, +}; + +static int __init q6venc_init(void) +{ + int rc = 0; + + rc = misc_register(&q6venc_misc); + if (rc) + pr_err("%s: Unable to register q6venc misc device\n", __func__); + return rc; +} + +static void __exit q6venc_exit(void) +{ + misc_deregister(&q6venc_misc); +} + +MODULE_DESCRIPTION("video encoder driver for QSD platform"); +MODULE_VERSION("2.0"); + +module_init(q6venc_init); +module_exit(q6venc_exit); diff --git a/arch/arm/mach-msm/qdsp6/pcm_in.c b/arch/arm/mach-msm/qdsp6/pcm_in.c new file mode 100644 index 0000000000000..3b3d23bfd7a9a --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/pcm_in.c @@ -0,0 +1,219 @@ +/* arch/arm/mach-msm/qdsp6/pcm_in.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * + * 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 + +#define BUFSZ (256) + +static DEFINE_MUTEX(pcm_in_lock); +static uint32_t sample_rate = 8000; +static uint32_t channel_count = 1; +static uint32_t buffer_size = BUFSZ; +static int pcm_in_opened = 0; + +void audio_client_dump(struct audio_client *ac); + +static long q6_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int rc = 0; + + switch (cmd) { + case AUDIO_SET_VOLUME: + break; + case AUDIO_GET_STATS: { + struct msm_audio_stats stats; + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void*) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + case AUDIO_START: { + uint32_t acdb_id; + rc = 0; + + if (arg == 0) { + acdb_id = 0; + } else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) { + rc = -EFAULT; + break; + } + + mutex_lock(&pcm_in_lock); + if (file->private_data) { + rc = -EBUSY; + } else { + file->private_data = q6audio_open_pcm( + buffer_size, sample_rate, channel_count, + AUDIO_FLAG_READ, acdb_id); + if (!file->private_data) + rc = -ENOMEM; + } + mutex_unlock(&pcm_in_lock); + break; + } + case AUDIO_STOP: + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (copy_from_user(&config, (void*) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (!config.channel_count || config.channel_count > 2) { + rc = -EINVAL; + break; + } + if (config.sample_rate < 8000 || config.sample_rate > 48000) { + rc = -EINVAL; + break; + } + if (config.buffer_size < 128 || config.buffer_size > 8192) { + rc = -EINVAL; + break; + } + sample_rate = config.sample_rate; + channel_count = config.channel_count; + buffer_size = config.buffer_size; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = buffer_size; + config.buffer_count = 2; + config.sample_rate = sample_rate; + config.channel_count = channel_count; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void*) arg, &config, sizeof(config))) { + rc = -EFAULT; + } + break; + } + default: + rc = -EINVAL; + } + return rc; +} + +static int q6_in_open(struct inode *inode, struct file *file) +{ + int rc; + + pr_info("pcm_in: open\n"); + mutex_lock(&pcm_in_lock); + if (pcm_in_opened) { + pr_err("pcm_in: busy\n"); + rc = -EBUSY; + } else { + pcm_in_opened = 1; + rc = 0; + } + mutex_unlock(&pcm_in_lock); + return rc; +} + +static ssize_t q6_in_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct audio_client *ac; + struct audio_buffer *ab; + const char __user *start = buf; + int xfer; + int res; + + mutex_lock(&pcm_in_lock); + ac = file->private_data; + if (!ac) { + res = -ENODEV; + goto fail; + } + while (count > 0) { + ab = ac->buf + ac->cpu_buf; + + if (ab->used) + if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) { + audio_client_dump(ac); + pr_err("pcm_read: timeout. dsp dead?\n"); + BUG(); + } + + xfer = count; + if (xfer > ab->size) + xfer = ab->size; + + if (copy_to_user(buf, ab->data, xfer)) { + res = -EFAULT; + goto fail; + } + + buf += xfer; + count -= xfer; + + ab->used = 1; + q6audio_read(ac, ab); + ac->cpu_buf ^= 1; + } +fail: + res = buf - start; + mutex_unlock(&pcm_in_lock); + + return res; +} + +static int q6_in_release(struct inode *inode, struct file *file) +{ + int rc = 0; + mutex_lock(&pcm_in_lock); + if (file->private_data) + rc = q6audio_close(file->private_data); + pcm_in_opened = 0; + mutex_unlock(&pcm_in_lock); + pr_info("pcm_in: release\n"); + return rc; +} + +static struct file_operations q6_in_fops = { + .owner = THIS_MODULE, + .open = q6_in_open, + .read = q6_in_read, + .release = q6_in_release, + .unlocked_ioctl = q6_in_ioctl, +}; + +struct miscdevice q6_in_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_in", + .fops = &q6_in_fops, +}; + +static int __init q6_in_init(void) { + return misc_register(&q6_in_misc); +} + +device_initcall(q6_in_init); diff --git a/arch/arm/mach-msm/qdsp6/pcm_out.c b/arch/arm/mach-msm/qdsp6/pcm_out.c new file mode 100644 index 0000000000000..edd64b705e32e --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/pcm_out.c @@ -0,0 +1,229 @@ +/* arch/arm/mach-msm/qdsp6/pcm_out.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * 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 + +void audio_client_dump(struct audio_client *ac); + +#define BUFSZ (3072) + +struct pcm { + struct mutex lock; + struct audio_client *ac; + uint32_t sample_rate; + uint32_t channel_count; + size_t buffer_size; +}; + +static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct pcm *pcm = file->private_data; + int rc = 0; + + if (cmd == AUDIO_GET_STATS) { + struct msm_audio_stats stats; + memset(&stats, 0, sizeof(stats)); + if (copy_to_user((void*) arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; + } + + mutex_lock(&pcm->lock); + switch (cmd) { + case AUDIO_SET_VOLUME: { + int vol; + if (copy_from_user(&vol, (void*) arg, sizeof(vol))) { + rc = -EFAULT; + break; + } + rc = q6audio_set_stream_volume(pcm->ac, vol); + break; + } + case AUDIO_START: { + uint32_t acdb_id; + if (arg == 0) { + acdb_id = 0; + } else if (copy_from_user(&acdb_id, (void*) arg, sizeof(acdb_id))) { + pr_info("pcm_out: copy acdb_id from user failed\n"); + rc = -EFAULT; + break; + } + if (pcm->ac) { + rc = -EBUSY; + } else { + pcm->ac = q6audio_open_pcm(pcm->buffer_size, pcm->sample_rate, + pcm->channel_count, + AUDIO_FLAG_WRITE, acdb_id); + if (!pcm->ac) + rc = -ENOMEM; + } + break; + } + case AUDIO_STOP: + break; + case AUDIO_FLUSH: + break; + case AUDIO_SET_CONFIG: { + struct msm_audio_config config; + if (pcm->ac) { + rc = -EBUSY; + break; + } + if (copy_from_user(&config, (void*) arg, sizeof(config))) { + rc = -EFAULT; + break; + } + if (config.channel_count < 1 || config.channel_count > 2) { + rc = -EINVAL; + break; + } + if (config.sample_rate < 8000 || config.sample_rate > 48000) { + rc = -EINVAL; + break; + } + if (config.buffer_size < 128 || config.buffer_size > 8192) { + rc = -EINVAL; + break; + } + pcm->sample_rate = config.sample_rate; + pcm->channel_count = config.channel_count; + pcm->buffer_size = config.buffer_size; + break; + } + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = pcm->buffer_size; + config.buffer_count = 2; + config.sample_rate = pcm->sample_rate; + config.channel_count = pcm->channel_count; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void*) arg, &config, sizeof(config))) { + rc = -EFAULT; + } + break; + } + default: + rc = -EINVAL; + } + mutex_unlock(&pcm->lock); + return rc; +} + +static int pcm_open(struct inode *inode, struct file *file) +{ + struct pcm *pcm; + + pr_info("pcm_out: open\n"); + pcm = kzalloc(sizeof(struct pcm), GFP_KERNEL); + + if (!pcm) + return -ENOMEM; + + mutex_init(&pcm->lock); + pcm->channel_count = 2; + pcm->sample_rate = 44100; + pcm->buffer_size = BUFSZ; + + file->private_data = pcm; + return 0; +} + +static ssize_t pcm_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct pcm *pcm = file->private_data; + struct audio_client *ac; + struct audio_buffer *ab; + const char __user *start = buf; + int xfer; + + if (!pcm->ac) + pcm_ioctl(file, AUDIO_START, 0); + + ac = pcm->ac; + if (!ac) + return -ENODEV; + + while (count > 0) { + ab = ac->buf + ac->cpu_buf; + + if (ab->used) + if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) { + audio_client_dump(ac); + pr_err("pcm_write: timeout. dsp dead?\n"); + BUG(); + } + + xfer = count; + if (xfer > ab->size) + xfer = ab->size; + + if (copy_from_user(ab->data, buf, xfer)) + return -EFAULT; + + buf += xfer; + count -= xfer; + + ab->used = xfer; + q6audio_write(ac, ab); + ac->cpu_buf ^= 1; + } + + return buf - start; +} + +static int pcm_release(struct inode *inode, struct file *file) +{ + struct pcm *pcm = file->private_data; + if (pcm->ac) + q6audio_close(pcm->ac); + kfree(pcm); + pr_info("pcm_out: release\n"); + return 0; +} + +static struct file_operations pcm_fops = { + .owner = THIS_MODULE, + .open = pcm_open, + .write = pcm_write, + .release = pcm_release, + .unlocked_ioctl = pcm_ioctl, +}; + +struct miscdevice pcm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_out", + .fops = &pcm_fops, +}; + +static int __init pcm_init(void) { + return misc_register(&pcm_misc); +} + +device_initcall(pcm_init); diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c new file mode 100644 index 0000000000000..83e5fb863aeac --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/q6audio.c @@ -0,0 +1,1560 @@ +/* arch/arm/mach-msm/qdsp6/q6audio.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * 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 "dal.h" +#include "dal_audio.h" +#include "dal_audio_format.h" +#include "dal_acdb.h" +#include "dal_adie.h" +#include + +#include + +#include "q6audio_devices.h" + +#if 0 +#define TRACE(x...) pr_info("Q6: "x) +#else +#define TRACE(x...) do{}while(0) +#endif + +static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { + [Q6_HW_HANDSET] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_HEADSET] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_SPEAKER] = { + .min_gain = -1500, + .max_gain = 0, + }, + [Q6_HW_TTY] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_BT_SCO] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_BT_A2DP] = { + .min_gain = -2000, + .max_gain = 0, + }, +}; + +static struct wake_lock wakelock; +static struct wake_lock idlelock; +static int idlecount; +static DEFINE_MUTEX(idlecount_lock); + +void audio_prevent_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (++idlecount == 1) { + wake_lock(&wakelock); + wake_lock(&idlelock); + } + mutex_unlock(&idlecount_lock); +} + +void audio_allow_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (--idlecount == 0) { + wake_unlock(&idlelock); + wake_unlock(&wakelock); + } + mutex_unlock(&idlecount_lock); +} + +static struct clk *icodec_rx_clk; +static struct clk *icodec_tx_clk; +static struct clk *ecodec_clk; +static struct clk *sdac_clk; + +static struct q6audio_analog_ops default_analog_ops; +static struct q6audio_analog_ops *analog_ops = &default_analog_ops; +static uint32_t tx_clk_freq = 8000; +static int tx_mute_status = 0; +static int rx_vol_level = 100; +static char acdb_file[64] = "default.acdb"; +static uint32_t tx_acdb = 0; +static uint32_t rx_acdb = 0; + +void q6audio_register_analog_ops(struct q6audio_analog_ops *ops) +{ + analog_ops = ops; +} + +void q6audio_set_acdb_file(char* filename) +{ + if (filename) + strncpy(acdb_file, filename, sizeof(acdb_file)-1); +} + +static struct q6_device_info *q6_lookup_device(uint32_t device_id) +{ + struct q6_device_info *di = q6_audio_devices; + for (;;) { + if (di->id == device_id) + return di; + if (di->id == 0) { + pr_err("q6_lookup_device: bogus id 0x%08x\n", + device_id); + return di; + } + di++; + } +} + +static uint32_t q6_device_to_codec(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + return di->codec; +} + +static uint32_t q6_device_to_dir(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + return di->dir; +} + +static uint32_t q6_device_to_cad_id(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + return di->cad_id; +} + +static uint32_t q6_device_to_path(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + return di->path; +} + +static uint32_t q6_device_to_rate(uint32_t device_id) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + return di->rate; +} + +int q6_device_volume(uint32_t device_id, int level) +{ + struct q6_device_info *di = q6_lookup_device(device_id); + if (analog_ops->get_rx_vol) + return analog_ops->get_rx_vol(di->hw, level); + else { + struct q6_hw_info *hw; + hw = &q6_audio_hw[di->hw]; + return hw->min_gain + ((hw->max_gain - hw->min_gain) * level) / 100; + } +} + +static inline int adie_open(struct dal_client *client) +{ + return dal_call_f0(client, DAL_OP_OPEN, 0); +} + +static inline int adie_close(struct dal_client *client) +{ + return dal_call_f0(client, DAL_OP_CLOSE, 0); +} + +static inline int adie_set_path(struct dal_client *client, + uint32_t id, uint32_t path_type) +{ + return dal_call_f1(client, ADIE_OP_SET_PATH, id, path_type); +} + +static inline int adie_set_path_freq_plan(struct dal_client *client, + uint32_t path_type, uint32_t plan) +{ + return dal_call_f1(client, ADIE_OP_SET_PATH_FREQUENCY_PLAN, + path_type, plan); +} + +static inline int adie_proceed_to_stage(struct dal_client *client, + uint32_t path_type, uint32_t stage) +{ + return dal_call_f1(client, ADIE_OP_PROCEED_TO_STAGE, + path_type, stage); +} + +static inline int adie_mute_path(struct dal_client *client, + uint32_t path_type, uint32_t mute_state) +{ + return dal_call_f1(client, ADIE_OP_MUTE_PATH, path_type, mute_state); +} + +static int adie_refcount; + +static struct dal_client *adie; +static struct dal_client *adsp; +static struct dal_client *acdb; + +static int adie_enable(void) +{ + adie_refcount++; + if (adie_refcount == 1) + adie_open(adie); + return 0; +} + +static int adie_disable(void) +{ + adie_refcount--; + if (adie_refcount == 0) + adie_close(adie); + return 0; +} + +/* 4k DMA scratch page used for exchanging acdb device config tables + * and stream format descriptions with the DSP. + */ +static void *audio_data; +static dma_addr_t audio_phys; + +#define SESSION_MIN 0 +#define SESSION_MAX 64 + +static DEFINE_MUTEX(session_lock); +static DEFINE_MUTEX(audio_lock); + +static struct audio_client *session[SESSION_MAX]; + +static int session_alloc(struct audio_client *ac) +{ + int n; + + mutex_lock(&session_lock); + for (n = SESSION_MIN; n < SESSION_MAX; n++) { + if (!session[n]) { + session[n] = ac; + mutex_unlock(&session_lock); + return n; + } + } + mutex_unlock(&session_lock); + return -ENOMEM; +} + +static void session_free(int n, struct audio_client *ac) +{ + mutex_lock(&session_lock); + if (session[n] == ac) + session[n] = 0; + mutex_unlock(&session_lock); +} + +static void audio_client_free(struct audio_client *ac) +{ + session_free(ac->session, ac); + + if (ac->buf[0].data) + dma_free_coherent(NULL, ac->buf[0].size, + ac->buf[0].data, ac->buf[0].phys); + if (ac->buf[1].data) + dma_free_coherent(NULL, ac->buf[1].size, + ac->buf[1].data, ac->buf[1].phys); + kfree(ac); +} + +static struct audio_client *audio_client_alloc(unsigned bufsz) +{ + struct audio_client *ac; + int n; + + ac = kzalloc(sizeof(*ac), GFP_KERNEL); + if (!ac) + return 0; + + n = session_alloc(ac); + if (n < 0) + goto fail_session; + ac->session = n; + + if (bufsz > 0) { + ac->buf[0].data = dma_alloc_coherent(NULL, bufsz, + &ac->buf[0].phys, GFP_KERNEL); + if (!ac->buf[0].data) + goto fail; + ac->buf[1].data = dma_alloc_coherent(NULL, bufsz, + &ac->buf[1].phys, GFP_KERNEL); + if (!ac->buf[1].data) + goto fail; + + ac->buf[0].size = bufsz; + ac->buf[1].size = bufsz; + } + + init_waitqueue_head(&ac->wait); + ac->client = adsp; + + return ac; + +fail: + session_free(n, ac); +fail_session: + audio_client_free(ac); + return 0; +} + +void audio_client_dump(struct audio_client *ac) +{ + dal_trace_dump(ac->client); +} + +static int audio_ioctl(struct audio_client *ac, void *ptr, uint32_t len) +{ + struct adsp_command_hdr *hdr = ptr; + uint32_t tmp; + int r; + + hdr->size = len - sizeof(u32); + hdr->dst = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_DSP); + hdr->src = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_MODEM); + hdr->context = ac->session; + ac->cb_status = -EBUSY; + r = dal_call(ac->client, AUDIO_OP_CONTROL, 5, ptr, len, &tmp, sizeof(tmp)); + if (r != 4) + return -EIO; + if (!wait_event_timeout(ac->wait, (ac->cb_status != -EBUSY), 5*HZ)) { + dal_trace_dump(ac->client); + pr_err("audio_ioctl: timeout. dsp dead?\n"); + BUG(); + } + return ac->cb_status; +} + +static int audio_command(struct audio_client *ac, uint32_t cmd) +{ + struct adsp_command_hdr rpc; + memset(&rpc, 0, sizeof(rpc)); + rpc.opcode = cmd; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_open_control(struct audio_client *ac) +{ + struct adsp_open_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_DEVICE; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_out_open(struct audio_client *ac, uint32_t bufsz, + uint32_t rate, uint32_t channels) +{ + struct adsp_open_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.format.standard.format = ADSP_AUDIO_FORMAT_PCM; + rpc.format.standard.channels = channels; + rpc.format.standard.bits_per_sample = 16; + rpc.format.standard.sampling_rate = rate; + rpc.format.standard.is_signed = 1; + rpc.format.standard.is_interleaved = 1; + + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK; + rpc.buf_max_size = bufsz; + + TRACE("open out %p\n", ac); + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_in_open(struct audio_client *ac, uint32_t bufsz, + uint32_t rate, uint32_t channels) +{ + struct adsp_open_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.format.standard.format = ADSP_AUDIO_FORMAT_PCM; + rpc.format.standard.channels = channels; + rpc.format.standard.bits_per_sample = 16; + rpc.format.standard.sampling_rate = rate; + rpc.format.standard.is_signed = 1; + rpc.format.standard.is_interleaved = 1; + + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; + rpc.buf_max_size = bufsz; + + TRACE("%p: open in\n", ac); + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_mp3_open(struct audio_client *ac, uint32_t bufsz, + uint32_t rate, uint32_t channels) +{ + struct adsp_open_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.format.standard.format = ADSP_AUDIO_FORMAT_MP3; + rpc.format.standard.channels = channels; + rpc.format.standard.bits_per_sample = 16; + rpc.format.standard.sampling_rate = rate; + rpc.format.standard.is_signed = 1; + rpc.format.standard.is_interleaved = 0; + + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK; + rpc.buf_max_size = bufsz; + + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_close(struct audio_client *ac) +{ + TRACE("%p: close\n", ac); + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_STREAM_STOP); + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_CLOSE); + return 0; +} + +static int audio_set_table(struct audio_client *ac, + uint32_t device_id, int size) +{ + struct adsp_set_dev_cfg_table_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG_TABLE; + if (q6_device_to_dir(device_id) == Q6_TX) + rpc.hdr.data = tx_clk_freq; + rpc.device_id = device_id; + rpc.phys_addr = audio_phys; + rpc.phys_size = size; + rpc.phys_used = size; + + TRACE("control: set table %x\n", device_id); + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +int q6audio_read(struct audio_client *ac, struct audio_buffer *ab) +{ + struct adsp_buffer_command rpc; + uint32_t res; + int r; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.size = sizeof(rpc) - sizeof(u32); + rpc.hdr.dst = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_DSP); + rpc.hdr.src = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_MODEM); + rpc.hdr.context = ac->session; + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DATA_TX; + rpc.buffer.addr = ab->phys; + rpc.buffer.max_size = ab->size; + rpc.buffer.actual_size = ab->used; + + TRACE("%p: read\n", ac); + r = dal_call(ac->client, AUDIO_OP_DATA, 5, &rpc, sizeof(rpc), + &res, sizeof(res)); + return 0; +} + +int q6audio_write(struct audio_client *ac, struct audio_buffer *ab) +{ + struct adsp_buffer_command rpc; + uint32_t res; + int r; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.size = sizeof(rpc) - sizeof(u32); + rpc.hdr.dst = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_DSP); + rpc.hdr.src = AUDIO_ADDR(ac->session, 0, AUDIO_DOMAIN_MODEM); + rpc.hdr.context = ac->session; + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DATA_RX; + rpc.buffer.addr = ab->phys; + rpc.buffer.max_size = ab->size; + rpc.buffer.actual_size = ab->used; + + TRACE("%p: write\n", ac); + r = dal_call(ac->client, AUDIO_OP_DATA, 5, &rpc, sizeof(rpc), + &res, sizeof(res)); + return 0; +} + +static int audio_rx_volume(struct audio_client *ac, uint32_t dev_id, int32_t volume) +{ + struct adsp_set_dev_volume_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL; + rpc.device_id = dev_id; + rpc.path = ADSP_PATH_RX; + rpc.volume = volume; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_rx_mute(struct audio_client *ac, uint32_t dev_id, int mute) +{ + struct adsp_set_dev_mute_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE; + rpc.device_id = dev_id; + rpc.path = ADSP_PATH_RX; + rpc.mute = !!mute; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_tx_volume(struct audio_client *ac, uint32_t dev_id, int32_t volume) +{ + struct adsp_set_dev_volume_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL; + rpc.device_id = dev_id; + rpc.path = ADSP_PATH_TX; + rpc.volume = volume; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_tx_mute(struct audio_client *ac, uint32_t dev_id, int mute) +{ + struct adsp_set_dev_mute_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE; + rpc.device_id = dev_id; + rpc.path = ADSP_PATH_TX; + rpc.mute = !!mute; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_stream_volume(struct audio_client *ac, int volume) +{ + struct adsp_set_volume_command rpc; + int rc; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_STREAM_VOL; + rpc.volume = volume; + rc = audio_ioctl(ac, &rpc, sizeof(rpc)); + return rc; +} + +static int audio_stream_mute(struct audio_client *ac, int mute) +{ + struct adsp_set_mute_command rpc; + int rc; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_SET_STREAM_MUTE; + rpc.mute = mute; + rc = audio_ioctl(ac, &rpc, sizeof(rpc)); + return rc; +} + +static void callback(void *data, int len, void *cookie) +{ + struct adsp_event_hdr *e = data; + struct audio_client *ac; + + + if (e->context >= SESSION_MAX) { + pr_err("audio callback: bogus session %d\n", + e->context); + return; + } + ac = session[e->context]; + if (!ac) { + pr_err("audio callback: unknown session %d\n", + e->context); + return; + } + + if (e->event_id == ADSP_AUDIO_IOCTL_CMD_STREAM_EOS) { + TRACE("%p: CB stream eos\n", ac); + if (e->status) + pr_err("playback status %d\n", e->status); + if (ac->cb_status == -EBUSY) { + ac->cb_status = e->status; + wake_up(&ac->wait); + } + return; + } + + if (e->event_id == ADSP_AUDIO_EVT_STATUS_BUF_DONE) { + TRACE("%p: CB done (%d)\n", ac, e->status); + if (e->status) + pr_err("buffer status %d\n", e->status); + ac->buf[ac->dsp_buf].used = 0; + ac->dsp_buf ^= 1; + wake_up(&ac->wait); + return; + } + + TRACE("%p: CB %08x status %d\n", ac, e->event_id, e->status); + if (e->status) + pr_warning("audio_cb: s=%d e=%08x status=%d\n", + e->context, e->event_id, e->status); + if (ac->cb_status == -EBUSY) { + ac->cb_status = e->status; + wake_up(&ac->wait); + } +} + +static void audio_init(struct dal_client *client) +{ + u32 tmp[3]; + + tmp[0] = 2 * sizeof(u32); + tmp[1] = 1; + tmp[2] = 0; + dal_call(client, AUDIO_OP_INIT, 5, tmp, sizeof(tmp), + tmp, sizeof(u32)); +} + +static struct audio_client *ac_control; + +static int q6audio_init(void) +{ + struct audio_client *ac = 0; + int res; + + mutex_lock(&audio_lock); + if (ac_control) { + res = 0; + goto done; + } + + pr_info("audio: init: codecs\n"); + icodec_rx_clk = clk_get(0, "icodec_rx_clk"); + icodec_tx_clk = clk_get(0, "icodec_tx_clk"); + ecodec_clk = clk_get(0, "ecodec_clk"); + sdac_clk = clk_get(0, "sdac_clk"); + audio_data = dma_alloc_coherent(NULL, 4096, &audio_phys, GFP_KERNEL); + + adsp = dal_attach(AUDIO_DAL_DEVICE, AUDIO_DAL_PORT, + callback, 0); + if (!adsp) { + pr_err("audio_init: cannot attach to adsp\n"); + res = -ENODEV; + goto done; + } + pr_info("audio: init: INIT\n"); + audio_init(adsp); + dal_trace(adsp); + + ac = audio_client_alloc(0); + if (!ac) { + pr_err("audio_init: cannot allocate client\n"); + res = -ENOMEM; + goto done; + } + + pr_info("audio: init: OPEN control\n"); + if (audio_open_control(ac)) { + pr_err("audio_init: cannot open control channel\n"); + res = -ENODEV; + goto done; + } + + pr_info("audio: init: attach ACDB\n"); + acdb = dal_attach(ACDB_DAL_DEVICE, ACDB_DAL_PORT, 0, 0); + if (!acdb) { + pr_err("audio_init: cannot attach to acdb channel\n"); + res = -ENODEV; + goto done; + } + + pr_info("audio: init: attach ADIE\n"); + adie = dal_attach(ADIE_DAL_DEVICE, ADIE_DAL_PORT, 0, 0); + if (!adie) { + pr_err("audio_init: cannot attach to adie\n"); + res = -ENODEV; + goto done; + } + if (analog_ops->init) + analog_ops->init(); + + res = 0; + ac_control = ac; + + wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "audio_pcm_idle"); + wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "audio_pcm_suspend"); +done: + if ((res < 0) && ac) + audio_client_free(ac); + mutex_unlock(&audio_lock); + + return res; +} + +struct audio_config_data { + uint32_t device_id; + uint32_t sample_rate; + uint32_t offset; + uint32_t length; +}; + +struct audio_config_database { + uint8_t magic[8]; + uint32_t entry_count; + uint32_t unused; + struct audio_config_data entry[0]; +}; + +void *acdb_data; +const struct firmware *acdb_fw; +extern struct miscdevice q6_control_device; + +static int acdb_init(char *filename) +{ + const struct audio_config_database *db; + const struct firmware *fw; + int n; + + pr_info("acdb: load '%s'\n", filename); + if (request_firmware(&fw, filename, q6_control_device.this_device) < 0) { + pr_err("acdb: load 'default.acdb' failed...\n"); + return -ENODEV; + } + db = (void*) fw->data; + + if (fw->size < sizeof(struct audio_config_database)) { + pr_err("acdb: undersized database\n"); + goto fail; + } + if (strcmp(db->magic, "ACDB1.0")) { + pr_err("acdb: invalid magic\n"); + goto fail; + } + if (db->entry_count > 1024) { + pr_err("acdb: too many entries\n"); + goto fail; + } + if (fw->size < (sizeof(struct audio_config_database) + + db->entry_count * sizeof(struct audio_config_data))) { + pr_err("acdb: undersized TOC\n"); + goto fail; + } + for (n = 0; n < db->entry_count; n++) { + if (db->entry[n].length > 4096) { + pr_err("acdb: entry %d too large (%d)\n", + n, db->entry[n].length); + goto fail; + } + if ((db->entry[n].offset + db->entry[n].length) > fw->size) { + pr_err("acdb: entry %d outside of data\n", n); + goto fail; + } + } + if (acdb_data) + release_firmware(acdb_fw); + acdb_data = (void*) fw->data; + acdb_fw = fw; + return 0; +fail: + release_firmware(fw); + return -ENODEV; +} + +static int acdb_get_config_table(uint32_t device_id, uint32_t sample_rate) +{ + struct audio_config_database *db; + int n, res; + + if (q6audio_init()) + return 0; + + if (!acdb_data) { + res = acdb_init(acdb_file); + if (res) + return res; + } + + db = acdb_data; + for (n = 0; n < db->entry_count; n++) { + if (db->entry[n].device_id != device_id) + continue; + if (db->entry[n].sample_rate != sample_rate) + continue; + break; + } + + if (n == db->entry_count) { + pr_err("acdb: no entry for device %d, rate %d.\n", + device_id, sample_rate); + return 0; + } + + pr_info("acdb: %d bytes for device %d, rate %d.\n", + db->entry[n].length, device_id, sample_rate); + + memcpy(audio_data, acdb_data + db->entry[n].offset, db->entry[n].length); + return db->entry[n].length; +} + +static uint32_t audio_rx_path_id = ADIE_PATH_HANDSET_RX; +static uint32_t audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR; +static uint32_t audio_rx_device_group = -1; +static uint32_t audio_tx_path_id = ADIE_PATH_HANDSET_TX; +static uint32_t audio_tx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_MIC; +static uint32_t audio_tx_device_group = -1; + +static int qdsp6_devchg_notify(struct audio_client *ac, + uint32_t dev_type, uint32_t dev_id) +{ + struct adsp_device_switch_command rpc; + + if (dev_type != ADSP_AUDIO_RX_DEVICE && + dev_type != ADSP_AUDIO_TX_DEVICE) + return -EINVAL; + + memset(&rpc, 0, sizeof(rpc)); + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_PREPARE; + if (dev_type == ADSP_AUDIO_RX_DEVICE) { + rpc.old_device = audio_rx_device_id; + rpc.new_device = dev_id; + } else { + rpc.old_device = audio_tx_device_id; + rpc.new_device = dev_id; + } + rpc.device_class = 0; + rpc.device_type = dev_type; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int qdsp6_standby(struct audio_client *ac) +{ + return audio_command(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_STANDBY); +} + +static int qdsp6_start(struct audio_client *ac) +{ + return audio_command(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_COMMIT); +} + +static void audio_rx_analog_enable(int en) +{ + switch (audio_rx_device_id) { + case ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO: + case ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO: + case ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR: + if (analog_ops->headset_enable) + analog_ops->headset_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET: + if (analog_ops->headset_enable) + analog_ops->headset_enable(en); + if (analog_ops->speaker_enable) + analog_ops->speaker_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO: + if (analog_ops->speaker_enable) + analog_ops->speaker_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR: + if (analog_ops->bt_sco_enable) + analog_ops->bt_sco_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR: + if (analog_ops->receiver_enable) + analog_ops->receiver_enable(en); + break; + } +} + +static void audio_tx_analog_enable(int en) +{ + switch (audio_tx_device_id) { + case ADSP_AUDIO_DEVICE_ID_HANDSET_MIC: + case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC: + if (analog_ops->int_mic_enable) + analog_ops->int_mic_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_HEADSET_MIC: + case ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC: + if (analog_ops->ext_mic_enable) + analog_ops->ext_mic_enable(en); + break; + case ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC: + if (analog_ops->bt_sco_enable) + analog_ops->bt_sco_enable(en); + break; + } +} + +static int audio_update_acdb(uint32_t adev, uint32_t acdb_id) +{ + uint32_t sample_rate; + int sz = -1; + + sample_rate = q6_device_to_rate(adev); + + if (q6_device_to_dir(adev) == Q6_RX) + rx_acdb = acdb_id; + else + tx_acdb = acdb_id; + + if (acdb_id != 0) + sz = acdb_get_config_table(acdb_id, sample_rate); + + if (sz <= 0) { + acdb_id = q6_device_to_cad_id(adev); + sz = acdb_get_config_table(acdb_id, sample_rate); + if (sz <= 0) + return -EINVAL; + } + + audio_set_table(ac_control, adev, sz); + return 0; +} + +static void _audio_rx_path_enable(int reconf, uint32_t acdb_id) +{ + adie_enable(); + adie_set_path(adie, audio_rx_path_id, ADIE_PATH_RX); + adie_set_path_freq_plan(adie, ADIE_PATH_RX, 48000); + + adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_READY); + adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_ANALOG_READY); + + audio_update_acdb(audio_rx_device_id, acdb_id); + if (!reconf) + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, audio_rx_device_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); + + audio_rx_analog_enable(1); +} + +static void _audio_tx_path_enable(int reconf, uint32_t acdb_id) +{ + audio_tx_analog_enable(1); + + adie_enable(); + adie_set_path(adie, audio_tx_path_id, ADIE_PATH_TX); + + if (tx_clk_freq > 8000) + adie_set_path_freq_plan(adie, ADIE_PATH_TX, 48000); + else + adie_set_path_freq_plan(adie, ADIE_PATH_TX, 8000); + + adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_DIGITAL_READY); + adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_DIGITAL_ANALOG_READY); + + audio_update_acdb(audio_tx_device_id, acdb_id); + + if (!reconf) + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, audio_tx_device_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); + + audio_tx_mute(ac_control, audio_tx_device_id, tx_mute_status); +} + +static void _audio_rx_path_disable(void) +{ + audio_rx_analog_enable(0); + + adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_ANALOG_OFF); + adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_OFF); + adie_disable(); +} + +static void _audio_tx_path_disable(void) +{ + audio_tx_analog_enable(0); + + adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_ANALOG_OFF); + adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_DIGITAL_OFF); + adie_disable(); +} + +static int icodec_rx_clk_refcount; +static int icodec_tx_clk_refcount; +static int ecodec_clk_refcount; +static int sdac_clk_refcount; + +static void _audio_rx_clk_enable(void) +{ + uint32_t device_group = q6_device_to_codec(audio_rx_device_id); + + switch(device_group) { + case Q6_ICODEC_RX: + icodec_rx_clk_refcount++; + if (icodec_rx_clk_refcount == 1) { + clk_set_rate(icodec_rx_clk, 12288000); + clk_enable(icodec_rx_clk); + } + break; + case Q6_ECODEC_RX: + ecodec_clk_refcount++; + if (ecodec_clk_refcount == 1) { + clk_set_rate(ecodec_clk, 2048000); + clk_enable(ecodec_clk); + } + break; + case Q6_SDAC_RX: + sdac_clk_refcount++; + if (sdac_clk_refcount == 1) { + clk_set_rate(sdac_clk, 12288000); + clk_enable(sdac_clk); + } + break; + default: + return; + } + audio_rx_device_group = device_group; +} + +static void _audio_tx_clk_enable(void) +{ + uint32_t device_group = q6_device_to_codec(audio_tx_device_id); + + switch (device_group) { + case Q6_ICODEC_TX: + icodec_tx_clk_refcount++; + if (icodec_tx_clk_refcount == 1) { + clk_set_rate(icodec_tx_clk, tx_clk_freq * 256); + clk_enable(icodec_tx_clk); + } + break; + case Q6_ECODEC_TX: + ecodec_clk_refcount++; + if (ecodec_clk_refcount == 1) { + clk_set_rate(ecodec_clk, 2048000); + clk_enable(ecodec_clk); + } + break; + case Q6_SDAC_TX: + /* TODO: In QCT BSP, clk rate was set to 20480000 */ + sdac_clk_refcount++; + if (sdac_clk_refcount == 1) { + clk_set_rate(sdac_clk, 12288000); + clk_enable(sdac_clk); + } + break; + default: + return; + } + audio_tx_device_group = device_group; +} + +static void _audio_rx_clk_disable(void) +{ + switch (audio_rx_device_group) { + case Q6_ICODEC_RX: + icodec_rx_clk_refcount--; + if (icodec_rx_clk_refcount == 0) { + clk_disable(icodec_rx_clk); + audio_rx_device_group = -1; + } + break; + case Q6_ECODEC_RX: + ecodec_clk_refcount--; + if (ecodec_clk_refcount == 0) { + clk_disable(ecodec_clk); + audio_rx_device_group = -1; + } + break; + case Q6_SDAC_RX: + sdac_clk_refcount--; + if (sdac_clk_refcount == 0) { + clk_disable(sdac_clk); + audio_rx_device_group = -1; + } + break; + default: + pr_err("audiolib: invalid rx device group %d\n", + audio_rx_device_group); + break; + } +} + +static void _audio_tx_clk_disable(void) +{ + switch (audio_tx_device_group) { + case Q6_ICODEC_TX: + icodec_tx_clk_refcount--; + if (icodec_tx_clk_refcount == 0) { + clk_disable(icodec_tx_clk); + audio_tx_device_group = -1; + } + break; + case Q6_ECODEC_TX: + ecodec_clk_refcount--; + if (ecodec_clk_refcount == 0) { + clk_disable(ecodec_clk); + audio_tx_device_group = -1; + } + break; + case Q6_SDAC_TX: + sdac_clk_refcount--; + if (sdac_clk_refcount == 0) { + clk_disable(sdac_clk); + audio_tx_device_group = -1; + } + break; + default: + pr_err("audiolib: invalid tx device group %d\n", + audio_tx_device_group); + break; + } +} + +static void _audio_rx_clk_reinit(uint32_t rx_device) +{ + uint32_t device_group = q6_device_to_codec(rx_device); + + if (device_group != audio_rx_device_group) + _audio_rx_clk_disable(); + + audio_rx_device_id = rx_device; + audio_rx_path_id = q6_device_to_path(rx_device); + + if (device_group != audio_rx_device_group) + _audio_rx_clk_enable(); + +} + +static void _audio_tx_clk_reinit(uint32_t tx_device) +{ + uint32_t device_group = q6_device_to_codec(tx_device); + + if (device_group != audio_tx_device_group) + _audio_tx_clk_disable(); + + audio_tx_device_id = tx_device; + audio_tx_path_id = q6_device_to_path(tx_device); + + if (device_group != audio_tx_device_group) + _audio_tx_clk_enable(); +} + +static DEFINE_MUTEX(audio_path_lock); +static int audio_rx_path_refcount; +static int audio_tx_path_refcount; + +static int audio_rx_path_enable(int en, uint32_t acdb_id) +{ + mutex_lock(&audio_path_lock); + if (en) { + audio_rx_path_refcount++; + if (audio_rx_path_refcount == 1) { + _audio_rx_clk_enable(); + _audio_rx_path_enable(0, acdb_id); + } + } else { + audio_rx_path_refcount--; + if (audio_rx_path_refcount == 0) { + _audio_rx_path_disable(); + _audio_rx_clk_disable(); + } + } + mutex_unlock(&audio_path_lock); + return 0; +} + +static int audio_tx_path_enable(int en, uint32_t acdb_id) +{ + mutex_lock(&audio_path_lock); + if (en) { + audio_tx_path_refcount++; + if (audio_tx_path_refcount == 1) { + _audio_tx_clk_enable(); + _audio_tx_path_enable(0, acdb_id); + } + } else { + audio_tx_path_refcount--; + if (audio_tx_path_refcount == 0) { + _audio_tx_path_disable(); + _audio_tx_clk_disable(); + } + } + mutex_unlock(&audio_path_lock); + return 0; +} + +int q6audio_reinit_acdb(char* filename) { + int res; + + if (q6audio_init()) + return 0; + + mutex_lock(&audio_path_lock); + if (strlen(filename) < 0 || !strcmp(filename, acdb_file)) { + res = -EINVAL; + goto done; + } + res = acdb_init(filename); + if (!res) + strcpy(acdb_file, filename); +done: + mutex_unlock(&audio_path_lock); + return res; + +} + +int q6audio_update_acdb(uint32_t id_src, uint32_t id_dst) +{ + int res; + + if (q6audio_init()) + return 0; + + mutex_lock(&audio_path_lock); + res = audio_update_acdb(id_dst, id_src); + if (res) + goto done; + + if (q6_device_to_dir(id_dst) == Q6_RX) + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, id_dst); + else + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, id_dst); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); +done: + mutex_unlock(&audio_path_lock); + return res; +} + +int q6audio_set_tx_mute(int mute) +{ + uint32_t adev; + int rc; + + if (q6audio_init()) + return 0; + + mutex_lock(&audio_path_lock); + + if (mute == tx_mute_status) { + mutex_unlock(&audio_path_lock); + return 0; + } + + adev = audio_tx_device_id; + rc = audio_tx_mute(ac_control, adev, mute); + if (!rc) tx_mute_status = mute; + mutex_unlock(&audio_path_lock); + return 0; +} + +int q6audio_set_stream_volume(struct audio_client *ac, int vol) +{ + if (vol > 1200 || vol < -4000) { + pr_err("unsupported volume level %d\n", vol); + return -EINVAL; + } + mutex_lock(&audio_path_lock); + audio_stream_mute(ac, 0); + audio_stream_volume(ac, vol); + mutex_unlock(&audio_path_lock); + return 0; +} + +int q6audio_set_rx_volume(int level) +{ + uint32_t adev; + int vol; + + if (q6audio_init()) + return 0; + + if (level < 0 || level > 100) + return -EINVAL; + + mutex_lock(&audio_path_lock); + adev = ADSP_AUDIO_DEVICE_ID_VOICE; + vol = q6_device_volume(audio_rx_device_id, level); + audio_rx_mute(ac_control, adev, 0); + audio_rx_volume(ac_control, adev, vol); + rx_vol_level = level; + mutex_unlock(&audio_path_lock); + return 0; +} + +static void do_rx_routing(uint32_t device_id, uint32_t acdb_id) +{ + if (device_id == audio_rx_device_id) { + if (acdb_id != rx_acdb) { + audio_update_acdb(device_id, acdb_id); + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, device_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); + } + return; + } + + if (audio_rx_path_refcount > 0) { + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, device_id); + _audio_rx_path_disable(); + _audio_rx_clk_reinit(device_id); + _audio_rx_path_enable(1, acdb_id); + } else { + audio_rx_device_id = device_id; + audio_rx_path_id = q6_device_to_path(device_id); + } +} + +static void do_tx_routing(uint32_t device_id, uint32_t acdb_id) +{ + if (device_id == audio_tx_device_id) { + if (acdb_id != tx_acdb) { + audio_update_acdb(device_id, acdb_id); + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, device_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); + } + return; + } + + if (audio_tx_path_refcount > 0) { + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, device_id); + _audio_tx_path_disable(); + _audio_tx_clk_reinit(device_id); + _audio_tx_path_enable(1, acdb_id); + } else { + audio_tx_device_id = device_id; + audio_tx_path_id = q6_device_to_path(device_id); + } +} + +int q6audio_do_routing(uint32_t device_id, uint32_t acdb_id) +{ + if (q6audio_init()) + return 0; + + mutex_lock(&audio_path_lock); + + switch(q6_device_to_dir(device_id)) { + case Q6_RX: + do_rx_routing(device_id, acdb_id); + break; + case Q6_TX: + do_tx_routing(device_id, acdb_id); + break; + } + + mutex_unlock(&audio_path_lock); + return 0; +} + +int q6audio_set_route(const char *name) +{ + uint32_t route; + if (!strcmp(name, "speaker")) { + route = ADIE_PATH_SPEAKER_STEREO_RX; + } else if (!strcmp(name, "headphones")) { + route = ADIE_PATH_HEADSET_STEREO_RX; + } else if (!strcmp(name, "handset")) { + route = ADIE_PATH_HANDSET_RX; + } else { + return -EINVAL; + } + + mutex_lock(&audio_path_lock); + if (route == audio_rx_path_id) + goto done; + + audio_rx_path_id = route; + + if (audio_rx_path_refcount > 0) { + _audio_rx_path_disable(); + _audio_rx_path_enable(1, 0); + } + if (audio_tx_path_refcount > 0) { + _audio_tx_path_disable(); + _audio_tx_path_enable(1, 0); + } +done: + mutex_unlock(&audio_path_lock); + return 0; +} + +struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, + uint32_t channels, uint32_t flags, uint32_t acdb_id) +{ + int rc, retry = 5; + struct audio_client *ac; + + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(bufsz); + if (!ac) + return 0; + + ac->flags = flags; + + mutex_lock(&audio_path_lock); + + if (ac->flags & AUDIO_FLAG_WRITE) { + audio_rx_path_refcount++; + if (audio_rx_path_refcount == 1) + _audio_rx_clk_enable(); + } else { + /* TODO: consider concurrency with voice call */ + tx_clk_freq = rate; + audio_tx_path_refcount++; + if (audio_tx_path_refcount == 1) { + _audio_tx_clk_enable(); + _audio_tx_path_enable(0, acdb_id); + } + } + + for (retry = 5;;retry--) { + if (ac->flags & AUDIO_FLAG_WRITE) + rc = audio_out_open(ac, bufsz, rate, channels); + else + rc = audio_in_open(ac, bufsz, rate, channels); + if (rc == 0) + break; + if (retry == 0) + BUG(); + pr_err("q6audio: open pcm error %d, retrying\n", rc); + msleep(1); + } + + if (ac->flags & AUDIO_FLAG_WRITE) + if (audio_rx_path_refcount == 1) + _audio_rx_path_enable(0, acdb_id); + mutex_unlock(&audio_path_lock); + + for (retry = 5;;retry--) { + rc = audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); + if (rc == 0) + break; + if (retry == 0) + BUG(); + pr_err("q6audio: stream start error %d, retrying\n", rc); + } + + if (!(ac->flags & AUDIO_FLAG_WRITE)) { + ac->buf[0].used = 1; + ac->buf[1].used = 1; + q6audio_read(ac, &ac->buf[0]); + q6audio_read(ac, &ac->buf[1]); + } + + audio_prevent_sleep(); + return ac; +} + +int q6audio_close(struct audio_client *ac) +{ + audio_close(ac); + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(0, 0); + else + audio_tx_path_enable(0, 0); + + audio_client_free(ac); + audio_allow_sleep(); + return 0; +} + +struct audio_client *q6voice_open(uint32_t flags, uint32_t acdb_id) +{ + struct audio_client *ac; + + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(0); + if (!ac) + return 0; + + ac->flags = flags; + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(1, acdb_id); + else { + tx_clk_freq = 8000; + audio_tx_path_enable(1, acdb_id); + } + + return ac; +} + +int q6voice_close(struct audio_client *ac) +{ + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(0, 0); + else + audio_tx_path_enable(0, 0); + + audio_client_free(ac); + return 0; +} + +struct audio_client *q6audio_open_mp3(uint32_t bufsz, uint32_t rate, + uint32_t channels, uint32_t acdb_id) +{ + struct audio_client *ac; + + printk("q6audio_open_mp3()\n"); + + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(bufsz); + if (!ac) + return 0; + + ac->flags = AUDIO_FLAG_WRITE; + audio_rx_path_enable(1, acdb_id); + + audio_mp3_open(ac, bufsz, rate, channels); + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); + + return ac; +} + +int q6audio_mp3_close(struct audio_client *ac) +{ + audio_close(ac); + audio_rx_path_enable(0, 0); + audio_client_free(ac); + return 0; +} + +int q6audio_async(struct audio_client *ac) +{ + struct adsp_command_hdr rpc; + memset(&rpc, 0, sizeof(rpc)); + rpc.opcode = ADSP_AUDIO_IOCTL_CMD_STREAM_EOS; + rpc.response_type = ADSP_AUDIO_RESPONSE_ASYNC; + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} diff --git a/arch/arm/mach-msm/qdsp6/q6audio_devices.h b/arch/arm/mach-msm/qdsp6/q6audio_devices.h new file mode 100644 index 0000000000000..d4d30b57c3554 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/q6audio_devices.h @@ -0,0 +1,265 @@ +/* arch/arm/mach-msm/qdsp6/q6audio_devices.h + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * 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. + * + */ + +struct q6_device_info { + uint32_t id; + uint32_t cad_id; + uint32_t path; + uint32_t rate; + uint8_t dir; + uint8_t codec; + uint8_t hw; +}; + +#define Q6_ICODEC_RX 0 +#define Q6_ICODEC_TX 1 +#define Q6_ECODEC_RX 2 +#define Q6_ECODEC_TX 3 +#define Q6_SDAC_RX 6 +#define Q6_SDAC_TX 7 +#define Q6_CODEC_NONE 255 + +#define Q6_TX 1 +#define Q6_RX 2 +#define Q6_TX_RX 3 + +#define CAD_HW_DEVICE_ID_HANDSET_MIC 0x01 +#define CAD_HW_DEVICE_ID_HANDSET_SPKR 0x02 +#define CAD_HW_DEVICE_ID_HEADSET_MIC 0x03 +#define CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO 0x04 +#define CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO 0x05 +#define CAD_HW_DEVICE_ID_SPKR_PHONE_MIC 0x06 +#define CAD_HW_DEVICE_ID_SPKR_PHONE_MONO 0x07 +#define CAD_HW_DEVICE_ID_SPKR_PHONE_STEREO 0x08 +#define CAD_HW_DEVICE_ID_BT_SCO_MIC 0x09 +#define CAD_HW_DEVICE_ID_BT_SCO_SPKR 0x0A +#define CAD_HW_DEVICE_ID_BT_A2DP_SPKR 0x0B +#define CAD_HW_DEVICE_ID_TTY_HEADSET_MIC 0x0C +#define CAD_HW_DEVICE_ID_TTY_HEADSET_SPKR 0x0D + +#define CAD_HW_DEVICE_ID_DEFAULT_TX 0x0E +#define CAD_HW_DEVICE_ID_DEFAULT_RX 0x0F + +/* Logical Device to indicate A2DP routing */ +#define CAD_HW_DEVICE_ID_BT_A2DP_TX 0x10 +#define CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_MONO_RX 0x11 +#define CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_STEREO_RX 0x12 +#define CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_MONO_RX 0x13 +#define CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX 0x14 + +#define CAD_HW_DEVICE_ID_VOICE 0x15 + +#define CAD_HW_DEVICE_ID_I2S_RX 0x20 +#define CAD_HW_DEVICE_ID_I2S_TX 0x21 + +/* AUXPGA */ +#define CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO_LB 0x22 +#define CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO_LB 0x23 +#define CAD_HW_DEVICE_ID_SPEAKER_SPKR_STEREO_LB 0x24 +#define CAD_HW_DEVICE_ID_SPEAKER_SPKR_MONO_LB 0x25 + +#define CAD_HW_DEVICE_ID_NULL_RX 0x2A + +#define CAD_HW_DEVICE_ID_MAX_NUM 0x2F + +#define CAD_HW_DEVICE_ID_INVALID 0xFF + +#define CAD_RX_DEVICE 0x00 +#define CAD_TX_DEVICE 0x01 + +static struct q6_device_info q6_audio_devices[] = { + { + .id = ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR, + .cad_id = CAD_HW_DEVICE_ID_HANDSET_SPKR, + .path = ADIE_PATH_HANDSET_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_HANDSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_SPKR_MONO, + .path = ADIE_PATH_HEADSET_MONO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_HEADSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_SPKR_STEREO, + .path = ADIE_PATH_HEADSET_STEREO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_HEADSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO, + .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_MONO, + .path = ADIE_PATH_SPEAKER_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO, + .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_STEREO, + .path = ADIE_PATH_SPEAKER_STEREO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_MONO_RX, + .path = ADIE_PATH_SPKR_MONO_HDPH_MONO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_MONO_RX, + .path = ADIE_PATH_SPKR_MONO_HDPH_STEREO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_MONO_PLUS_SPKR_STEREO_RX, + .path = ADIE_PATH_SPKR_STEREO_HDPH_MONO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_STEREO_PLUS_SPKR_STEREO_RX, + .path = ADIE_PATH_SPKR_STEREO_HDPH_STEREO_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR, + .cad_id = CAD_HW_DEVICE_ID_TTY_HEADSET_SPKR, + .path = ADIE_PATH_TTY_HEADSET_RX, + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ICODEC_RX, + .hw = Q6_HW_TTY, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_HANDSET_MIC, + .cad_id = CAD_HW_DEVICE_ID_HANDSET_MIC, + .path = ADIE_PATH_HANDSET_TX, + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ICODEC_TX, + .hw = Q6_HW_HANDSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_HEADSET_MIC, + .cad_id = CAD_HW_DEVICE_ID_HEADSET_MIC, + .path = ADIE_PATH_HEADSET_MONO_TX, + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ICODEC_TX, + .hw = Q6_HW_HEADSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC, + .cad_id = CAD_HW_DEVICE_ID_SPKR_PHONE_MIC, + .path = ADIE_PATH_SPEAKER_TX, + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ICODEC_TX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC, + .cad_id = CAD_HW_DEVICE_ID_TTY_HEADSET_MIC, + .path = ADIE_PATH_TTY_HEADSET_TX, + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ICODEC_TX, + .hw = Q6_HW_HEADSET, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR, + .cad_id = CAD_HW_DEVICE_ID_BT_SCO_SPKR, + .path = 0, /* XXX */ + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ECODEC_RX, + .hw = Q6_HW_BT_SCO, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_BT_A2DP_SPKR, + .cad_id = CAD_HW_DEVICE_ID_BT_A2DP_SPKR, + .path = 0, /* XXX */ + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_ECODEC_RX, + .hw = Q6_HW_BT_A2DP, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC, + .cad_id = CAD_HW_DEVICE_ID_BT_SCO_MIC, + .path = 0, /* XXX */ + .rate = 8000, + .dir = Q6_TX, + .codec = Q6_ECODEC_TX, + .hw = Q6_HW_BT_SCO, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_I2S_SPKR, + .cad_id = CAD_HW_DEVICE_ID_I2S_RX, + .path = 0, /* XXX */ + .rate = 48000, + .dir = Q6_RX, + .codec = Q6_SDAC_RX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = ADSP_AUDIO_DEVICE_ID_I2S_MIC, + .cad_id = CAD_HW_DEVICE_ID_I2S_TX, + .path = 0, /* XXX */ + .rate = 16000, + .dir = Q6_TX, + .codec = Q6_SDAC_TX, + .hw = Q6_HW_SPEAKER, + }, + { + .id = 0, + .cad_id = 0, + .path = 0, + .rate = 8000, + .dir = 0, + .codec = Q6_CODEC_NONE, + .hw = 0, + }, +}; + diff --git a/arch/arm/mach-msm/qdsp6/routing.c b/arch/arm/mach-msm/qdsp6/routing.c new file mode 100644 index 0000000000000..a851896401290 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/routing.c @@ -0,0 +1,71 @@ +/* arch/arm/mach-msm/qdsp6/routing.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * 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 + +extern int q6audio_set_route(const char *name); + +static int q6_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t q6_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + char cmd[32]; + + if (count >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buf, count)) + return -EFAULT; + cmd[count] = 0; + + if ((count > 1) && (cmd[count-1] == '\n')) + cmd[count-1] = 0; + + q6audio_set_route(cmd); + + return count; +} + +static int q6_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static struct file_operations q6_fops = { + .owner = THIS_MODULE, + .open = q6_open, + .write = q6_write, + .release = q6_release, +}; + +static struct miscdevice q6_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_audio_route", + .fops = &q6_fops, +}; + + +static int __init q6_init(void) { + return misc_register(&q6_misc); +} + +device_initcall(q6_init); diff --git a/include/linux/msm_audio.h b/include/linux/msm_audio.h index 8390eb1e1345d..5875c9950fea9 100644 --- a/include/linux/msm_audio.h +++ b/include/linux/msm_audio.h @@ -43,6 +43,11 @@ #define AUDIO_GET_PCM_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 30, unsigned) #define AUDIO_SET_PCM_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 31, unsigned) #define AUDIO_SWITCH_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 32, unsigned) +#define AUDIO_SET_MUTE _IOW(AUDIO_IOCTL_MAGIC, 33, unsigned) +#define AUDIO_UPDATE_ACDB _IOW(AUDIO_IOCTL_MAGIC, 34, unsigned) +#define AUDIO_START_VOICE _IOW(AUDIO_IOCTL_MAGIC, 35, unsigned) +#define AUDIO_STOP_VOICE _IOW(AUDIO_IOCTL_MAGIC, 36, unsigned) +#define AUDIO_REINIT_ACDB _IOW(AUDIO_IOCTL_MAGIC, 39, unsigned) #define AUDIO_MAX_COMMON_IOCTL_NUM 100 diff --git a/include/linux/msm_q6vdec.h b/include/linux/msm_q6vdec.h new file mode 100644 index 0000000000000..1dca80307033e --- /dev/null +++ b/include/linux/msm_q6vdec.h @@ -0,0 +1,230 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _MSM_VDEC_H_ +#define _MSM_VDEC_H_ + +#include + +#define VDEC_IOCTL_MAGIC 'v' + +#define VDEC_IOCTL_INITIALIZE _IOWR(VDEC_IOCTL_MAGIC, 1, struct vdec_init) +#define VDEC_IOCTL_SETBUFFERS _IOW(VDEC_IOCTL_MAGIC, 2, struct vdec_buffer) +#define VDEC_IOCTL_QUEUE _IOWR(VDEC_IOCTL_MAGIC, 3, \ + struct vdec_input_buf) +#define VDEC_IOCTL_REUSEFRAMEBUFFER _IOW(VDEC_IOCTL_MAGIC, 4, unsigned int) +#define VDEC_IOCTL_FLUSH _IOW(VDEC_IOCTL_MAGIC, 5, unsigned int) +#define VDEC_IOCTL_EOS _IO(VDEC_IOCTL_MAGIC, 6) +#define VDEC_IOCTL_GETMSG _IOR(VDEC_IOCTL_MAGIC, 7, struct vdec_msg) +#define VDEC_IOCTL_CLOSE _IO(VDEC_IOCTL_MAGIC, 8) +#define VDEC_IOCTL_FREEBUFFERS _IOW(VDEC_IOCTL_MAGIC, 9, struct vdec_buf_info) +#define VDEC_IOCTL_GETDECATTRIBUTES _IOR(VDEC_IOCTL_MAGIC, 10, \ + struct vdec_dec_attributes) + +enum { + VDEC_FRAME_DECODE_OK, + VDEC_FRAME_DECODE_ERR, + VDEC_FATAL_ERR, + VDEC_FLUSH_FINISH, + VDEC_EOS, + VDEC_FRAME_FLUSH, + VDEC_STREAM_SWITCH, + VDEC_SUSPEND_FINISH, + VDEC_BUFFER_CONSUMED +}; + +enum { + VDEC_FLUSH_INPUT, + VDEC_FLUSH_OUTPUT, + VDEC_FLUSH_ALL +}; + +enum { + VDEC_BUFFER_TYPE_INPUT, + VDEC_BUFFER_TYPE_OUTPUT, + VDEC_BUFFER_TYPE_INTERNAL1, + VDEC_BUFFER_TYPE_INTERNAL2, +}; + +enum { + VDEC_QUEUE_SUCCESS, + VDEC_QUEUE_FAILED, + VDEC_QUEUE_BADSTATE, +}; + +struct vdec_input_buf_info { + u32 offset; + u32 data; + u32 size; + int timestamp_lo; + int timestamp_hi; + int avsync_state; + u32 flags; +}; + +struct vdec_buf_desc { + u32 bufsize; + u32 num_min_buffers; + u32 num_max_buffers; +}; + +struct vdec_buf_req { + u32 max_input_queue_size; + struct vdec_buf_desc input; + struct vdec_buf_desc output; + struct vdec_buf_desc dec_req1; + struct vdec_buf_desc dec_req2; +}; + +struct vdec_region_info { + u32 src_id; + u32 offset; + u32 size; +}; + +struct vdec_config { + u32 fourcc; /* video format */ + u32 width; /* source width */ + u32 height; /* source height */ + u32 order; /* render decoder order */ + u32 notify_enable; /* enable notify input buffer done event */ + u32 vc1_rowbase; + u32 h264_startcode_detect; + u32 h264_nal_len_size; + u32 postproc_flag; + u32 fruc_enable; + u32 reserved; +}; + +struct vdec_vc1_panscan_regions { + int num; + int width[4]; + int height[4]; + int xoffset[4]; + int yoffset[4]; +}; + +struct vdec_cropping_window { + u32 x1; + u32 y1; + u32 x2; + u32 y2; +}; + +struct vdec_frame_info { + u32 status; /* video decode status */ + u32 offset; /* buffer offset */ + u32 data1; /* user data field 1 */ + u32 data2; /* user data field 2 */ + int timestamp_lo; /* lower 32 bits timestamp, in msec */ + int timestamp_hi; /* higher 32 bits timestamp, in msec */ + int cal_timestamp_lo; /* lower 32 bits cal timestamp, in msec */ + int cal_timestamp_hi; /* higher 32 bits cal timestamp, in msec */ + u32 dec_width; /* frame roi width */ + u32 dec_height; /* frame roi height */ + struct vdec_cropping_window cwin; /* The frame cropping window */ + u32 picture_type[2]; /* picture coding type */ + u32 picture_format; /* picture coding format */ + u32 vc1_rangeY; /* luma range mapping */ + u32 vc1_rangeUV; /* chroma range mapping */ + u32 picture_resolution; /* scaling factor */ + u32 frame_disp_repeat; /* how often repeated by disp */ + u32 repeat_first_field; /* repeat 1st field after 2nd */ + u32 top_field_first; /* top field displayed first */ + u32 interframe_interp; /* not for inter-frame interp */ + struct vdec_vc1_panscan_regions panscan; /* pan region */ + u32 concealed_macblk_num; /* number of concealed macro blk */ + u32 flags; /* input flags */ + u32 performance_stats; /* performance statistics returned by decoder */ + u32 data3; /* user data field 3 */ +}; + +struct vdec_buf_info { + u32 buf_type; + struct vdec_region_info region; + u32 num_buf; + u32 islast; +}; + +struct vdec_buffer { + u32 pmem_id; + struct vdec_buf_info buf; +}; + +struct vdec_sequence { + u8 *header; + u32 len; +}; + +struct vdec_config_sps { + struct vdec_config cfg; + struct vdec_sequence seq; +}; + +#define VDEC_MSG_REUSEINPUTBUFFER 1 +#define VDEC_MSG_FRAMEDONE 2 + +struct vdec_msg { + u32 id; + + union { + /* id = VDEC_MSG_REUSEINPUTBUFFER */ + u32 buf_id; + /* id = VDEC_MSG_FRAMEDONE */ + struct vdec_frame_info vfr_info; + }; +}; + +struct vdec_init { + struct vdec_config_sps sps_cfg; + struct vdec_buf_req *buf_req; +}; + +struct vdec_input_buf { + u32 pmem_id; + struct vdec_input_buf_info buffer; + struct vdec_queue_status *queue_status; +}; + +struct vdec_queue_status { + u32 status; +}; + +struct vdec_dec_attributes { + u32 fourcc; + u32 profile; + u32 level; + u32 dec_pic_width; + u32 dec_pic_height; + struct vdec_buf_desc input; + struct vdec_buf_desc output; + struct vdec_buf_desc dec_req1; + struct vdec_buf_desc dec_req2; +}; + +#endif /* _MSM_VDEC_H_ */ diff --git a/include/linux/msm_q6venc.h b/include/linux/msm_q6venc.h new file mode 100755 index 0000000000000..2ca4832d9a5e0 --- /dev/null +++ b/include/linux/msm_q6venc.h @@ -0,0 +1,125 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _MSM_VENC_H_ +#define _MSM_VENC_H_ + +#include + +struct venc_buf { + unsigned int src_id; + int fd; + unsigned long offset; + unsigned long size; +}; + +struct q6_init_config { + unsigned short venc_standard; + unsigned short partial_run_length_flag; + unsigned short h263_annex_ispt; + unsigned short h263_annex_jspt; + unsigned short h263_annex_tspt; + unsigned short rc_flag; + unsigned short one_mv_flag; + unsigned short acdc_pred_enable; + unsigned short rounding_bit_ctrl; + unsigned short rotation_flag; + unsigned short max_mvx; + unsigned short max_mvy; + unsigned short enc_frame_height_inmb; + unsigned short enc_frame_width_inmb; + unsigned short dvs_frame_height; + unsigned short dvs_frame_width; + + /* unused by userspace, filled in by kernel */ + unsigned int ref_frame_buf1_phy; + unsigned int ref_frame_buf2_phy; + unsigned int rlc_buf1_phy; + unsigned int rlc_buf2_phy; + unsigned int rlc_buf_length; +}; + +struct init_config { + struct venc_buf ref_frame_buf1; + struct venc_buf ref_frame_buf2; + struct venc_buf rlc_buf1; + struct venc_buf rlc_buf2; + struct q6_init_config q6_init_config; +}; + +struct q6_encode_param { + unsigned int luma_addr; + unsigned int chroma_addr; + unsigned int x_offset; + unsigned int y_offset; + unsigned int frame_rho_budget; + unsigned int frame_type; + unsigned int qp; +}; + +struct encode_param { + struct venc_buf y_addr; + unsigned long uv_offset; + struct q6_encode_param q6_encode_param; +}; + +struct intra_refresh { + unsigned int intra_refresh_enable; + unsigned int intra_mb_num; +}; + +struct rc_config { + unsigned short max_frame_qp_up_delta; + unsigned short max_frame_qp_down_delta; + unsigned short min_frame_qp; + unsigned short max_frame_qp; +}; + +struct q6_frame_type { + unsigned int frame_type; + unsigned int frame_len; + unsigned int frame_addr; + unsigned int map_table; +}; + +struct frame_type { + struct venc_buf frame_addr; + struct q6_frame_type q6_frame_type; +}; + +#define VENC_IOCTL_MAGIC 'V' + +#define VENC_IOCTL_INITIALIZE _IOW(VENC_IOCTL_MAGIC, 1, struct init_config) +#define VENC_IOCTL_ENCODE _IOW(VENC_IOCTL_MAGIC, 2, struct encode_param) +#define VENC_IOCTL_INTRA_REFRESH _IOW(VENC_IOCTL_MAGIC, 3, struct intra_refresh) +#define VENC_IOCTL_RC_CONFIG _IOW(VENC_IOCTL_MAGIC, 4, struct rc_config) +#define VENC_IOCTL_ENCODE_CONFIG _IOW(VENC_IOCTL_MAGIC, 5, struct init_config) +#define VENC_IOCTL_STOP _IO(VENC_IOCTL_MAGIC, 6) +#define VENC_IOCTL_WAIT_FOR_ENCODE _IOR(VENC_IOCTL_MAGIC, 7, struct frame_type) +#define VENC_IOCTL_STOP_ENCODE _IO(VENC_IOCTL_MAGIC, 8) + +#endif /* _MSM_VENC_H_ */ From b9d1ad4d75aabb892725f8ab5f93abb58991f6e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 24 Sep 2009 16:28:04 -0700 Subject: [PATCH 0427/2556] Regulator: tps65023: Hack to work around msm i2c controller limitation The i2c controller on the msm platform cannot reliably read a single byte without so it reads an extra byte for single byte reads. This causes errors with this chip, but 3 byte reads do not seem to cause any problems. --- drivers/regulator/tps65023-regulator.c | 28 +++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index 60a7ca5409e97..b269089b455a0 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -128,9 +128,35 @@ struct tps_pmic { struct mutex io_lock; }; +static int tps_65023_read_3bytes(struct tps_pmic *tps, u8 reg) +{ + int rv; + u8 txbuf[1]; + u8 rxbuf[3]; + struct i2c_msg msgs[] = { + { + .addr = tps->client->addr, + .flags = 0, + .len = sizeof(txbuf), + .buf = txbuf, + }, + { + .addr = tps->client->addr, + .flags = I2C_M_RD, + .len = sizeof(rxbuf), + .buf = rxbuf, + }, + }; + txbuf[0] = reg; + rv = i2c_transfer(tps->client->adapter, msgs, 2); + if (rv < 0) + return rv; + return rxbuf[0]; +} + static inline int tps_65023_read(struct tps_pmic *tps, u8 reg) { - return i2c_smbus_read_byte_data(tps->client, reg); + return tps_65023_read_3bytes(tps, reg); } static inline int tps_65023_write(struct tps_pmic *tps, u8 reg, u8 val) From e46b3a265ce58b5773ef3f42458b380b0353078c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 28 Sep 2009 20:06:48 -0700 Subject: [PATCH 0428/2556] Regulator: tps65023: Set GO bit after updating the voltage. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The voltage does not start to change until this bit set. Signed-off-by: Arve Hjønnevåg --- drivers/regulator/tps65023-regulator.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index b269089b455a0..cbbab6eefb249 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -62,6 +62,9 @@ #define TPS65023_REG_CTRL_LDO2_EN BIT(2) #define TPS65023_REG_CTRL_LDO1_EN BIT(1) +/* CON_CTRL2 bitfields */ +#define TPS65023_CON_CTRL2_GO BIT(7) + /* LDO_CTRL bitfields */ #define TPS65023_LDO_CTRL_LDOx_SHIFT(ldo_id) ((ldo_id)*4) #define TPS65023_LDO_CTRL_LDOx_MASK(ldo_id) (0xF0 >> ((ldo_id)*4)) @@ -353,6 +356,7 @@ static int tps65023_dcdc_set_voltage(struct regulator_dev *dev, struct tps_pmic *tps = rdev_get_drvdata(dev); int dcdc = rdev_get_id(dev); int vsel; + int rv; if (dcdc != TPS65023_DCDC_1) return -EINVAL; @@ -378,8 +382,12 @@ static int tps65023_dcdc_set_voltage(struct regulator_dev *dev, /* write to the register in case we found a match */ if (vsel == tps->info[dcdc]->table_len) return -EINVAL; - else - return tps_65023_reg_write(tps, TPS65023_REG_DEF_CORE, vsel); + + rv = tps_65023_reg_write(tps, TPS65023_REG_DEF_CORE, vsel); + if (!rv) + rv = tps_65023_reg_write(tps, TPS65023_REG_CON_CTRL2, + TPS65023_CON_CTRL2_GO); + return rv; } static int tps65023_ldo_get_voltage(struct regulator_dev *dev) From 3110b84628d9553ce2f4c80a6eef782dc780fb5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 7 Dec 2009 16:33:57 -0800 Subject: [PATCH 0429/2556] Regulator: tps65023: Wait for output voltage to change when setting dcdc1. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ic7b5421640f42387801337ad6fb55f93b77a1728 Signed-off-by: Arve Hjønnevåg --- drivers/regulator/tps65023-regulator.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index cbbab6eefb249..7698b603bba48 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -129,6 +129,7 @@ struct tps_pmic { struct regulator_dev *rdev[TPS65023_NUM_REGULATOR]; const struct tps_info *info[TPS65023_NUM_REGULATOR]; struct mutex io_lock; + unsigned dcdc1_last_uV; }; static int tps_65023_read_3bytes(struct tps_pmic *tps, u8 reg) @@ -357,6 +358,8 @@ static int tps65023_dcdc_set_voltage(struct regulator_dev *dev, int dcdc = rdev_get_id(dev); int vsel; int rv; + int uV = 0; + int delay; if (dcdc != TPS65023_DCDC_1) return -EINVAL; @@ -370,7 +373,7 @@ static int tps65023_dcdc_set_voltage(struct regulator_dev *dev, for (vsel = 0; vsel < tps->info[dcdc]->table_len; vsel++) { int mV = tps->info[dcdc]->table[vsel]; - int uV = mV * 1000; + uV = mV * 1000; /* Break at the first in-range value */ if (min_uV <= uV && uV <= max_uV) @@ -387,6 +390,16 @@ static int tps65023_dcdc_set_voltage(struct regulator_dev *dev, if (!rv) rv = tps_65023_reg_write(tps, TPS65023_REG_CON_CTRL2, TPS65023_CON_CTRL2_GO); + + /* Add delay to reach relected voltage (14.4 mV/us default slew rate) */ + if (tps->dcdc1_last_uV) + delay = abs(tps->dcdc1_last_uV - uV); + else + delay = max(uV - 800000, 1600000 - uV); + delay = DIV_ROUND_UP(delay, 14400); + udelay(delay); + tps->dcdc1_last_uV = rv ? 0 /* Unknown voltage */ : uV; + return rv; } From 5fcafccd4ea612ddbd060587fbad482c01ba4dd3 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 5 Oct 2009 16:20:55 -0700 Subject: [PATCH 0430/2556] [ARM] msm: pm: make use of msm_fiq_xxx be conditional of MSM_FIQ_SUPPORT Change-Id: I6d4a9487949a679f31d35d6f2a5942dcc9d9f2f3 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/pm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index e47f5795f2931..0d6e13838d8d3 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -193,6 +193,12 @@ msm_pm_wait_state(uint32_t wait_all_set, uint32_t wait_all_clear, return -ETIMEDOUT; } +#ifdef CONFIG_MSM_FIQ_SUPPORT +void msm_fiq_exit_sleep(void); +#else +static inline void msm_fiq_exit_sleep(void) { } +#endif + static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) { uint32_t saved_vector[2]; @@ -202,7 +208,6 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) void msm_irq_exit_sleep1(void); void msm_irq_exit_sleep2(void); void msm_irq_exit_sleep3(void); - void msm_fiq_exit_sleep(void); void msm_gpio_enter_sleep(int from_idle); void msm_gpio_exit_sleep(void); void smd_sleep_exit(void); From 0da24aa4c14e470c203b227cfba0ccb24b8f4f21 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Fri, 16 Oct 2009 11:56:34 -0700 Subject: [PATCH 0431/2556] [ARM] mm: fault: Add imprecise abort handler which prints the fault status Signed-off-by: San Mehat [ARM] mm: fault: Fix a typo in the imprecise abort handler The code was calling 'mcr' instead of 'mrc', so we were writing to the coprocessor register instead of reading. Change-Id: Ie9d91ced01c6ac6d881e2f5624a61a5910f56bc2 Signed-off-by: Dima Zavin --- arch/arm/mm/fault.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index f10f9bac22069..1a954b7cb5ab8 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -461,6 +461,20 @@ do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs) return 1; } +static int +do_imprecise_ext(unsigned long addr, unsigned int fsr, struct pt_regs *regs) +{ +#ifdef CONFIG_ARCH_MSM_SCORPION + unsigned int adfsr; + + asm("mrc p15, 0, %0, c5, c1, 0\n" /* read adfsr for fault status */ + : "=r" (adfsr)); + + printk("%s: ADFSR = 0x%.8x\n", __func__, adfsr); +#endif + return 1; +} + static struct fsr_info { int (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs); int sig; @@ -498,7 +512,7 @@ static struct fsr_info { { do_bad, SIGBUS, 0, "unknown 19" }, { do_bad, SIGBUS, 0, "lock abort" }, /* xscale */ { do_bad, SIGBUS, 0, "unknown 21" }, - { do_bad, SIGBUS, BUS_OBJERR, "imprecise external abort" }, /* xscale */ + { do_imprecise_ext, SIGBUS, BUS_OBJERR, "imprecise external abort" }, /* xscale */ { do_bad, SIGBUS, 0, "unknown 23" }, { do_bad, SIGBUS, 0, "dcache parity error" }, /* xscale */ { do_bad, SIGBUS, 0, "unknown 25" }, From a547c36eefd0e6d16f6d2bdf88b1fc4141f2db88 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 20 Oct 2009 11:38:21 -0700 Subject: [PATCH 0432/2556] [ARM] msm: swordfish: Add the USB phy/core reset functions. Change-Id: Ia5434e313d44cb55a5482d46f98fc04ff5d1b847 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-swordfish.c | 50 ++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-swordfish.c b/arch/arm/mach-msm/board-swordfish.c index e181b10f74d54..5d8f70bdba9b3 100644 --- a/arch/arm/mach-msm/board-swordfish.c +++ b/arch/arm/mach-msm/board-swordfish.c @@ -45,6 +45,7 @@ #include "board-swordfish.h" #include "devices.h" +#include "proc_comm.h" extern int swordfish_init_mmc(void); @@ -90,10 +91,57 @@ static struct msm_hsusb_product swordfish_usb_products[] = { }; #endif -static int swordfish_phy_init_seq[] = { 0x1D, 0x0D, 0x1D, 0x10, -1 }; +static int swordfish_phy_init_seq[] = { + 0x0C, 0x31, + 0x1D, 0x0D, + 0x1D, 0x10, + -1 +}; + +static void swordfish_usb_phy_reset(void) +{ + u32 id; + int ret; + + id = PCOM_CLKRGM_APPS_RESET_USB_PHY; + ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_ASSERT, &id, NULL); + if (ret) { + pr_err("%s: Cannot assert (%d)\n", __func__, ret); + return; + } + + msleep(1); + + id = PCOM_CLKRGM_APPS_RESET_USB_PHY; + ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_DEASSERT, &id, NULL); + if (ret) { + pr_err("%s: Cannot assert (%d)\n", __func__, ret); + return; + } +} + +static void swordfish_usb_hw_reset(bool enable) +{ + u32 id; + int ret; + u32 func; + + id = PCOM_CLKRGM_APPS_RESET_USBH; + if (enable) + func = PCOM_CLK_REGIME_SEC_RESET_ASSERT; + else + func = PCOM_CLK_REGIME_SEC_RESET_DEASSERT; + ret = msm_proc_comm(func, &id, NULL); + if (ret) + pr_err("%s: Cannot set reset to %d (%d)\n", __func__, enable, + ret); +} + static struct msm_hsusb_platform_data msm_hsusb_pdata = { .phy_init_seq = swordfish_phy_init_seq, + .phy_reset = swordfish_usb_phy_reset, + .hw_reset = swordfish_usb_hw_reset, #ifdef CONFIG_USB_FUNCTION .vendor_id = 0x18d1, .product_id = 0x0d02, From 2bd089e9c09e193e25ee512936b4a5a4cf7f90b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 20 Oct 2009 17:29:24 -0700 Subject: [PATCH 0433/2556] [ARM] msm: clock: Use hlist instead of list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Id0bb9f2a6fe73ef05a818e7e5bd627c0052493b0 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/clock.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c index 898a17f8600ff..bd606060ce545 100644 --- a/arch/arm/mach-msm/clock.c +++ b/arch/arm/mach-msm/clock.c @@ -297,7 +297,7 @@ static int __init clock_debug_init(void) { struct dentry *dent_rate, *dent_enable, *dent_local; struct clk *clock; - unsigned n = 0; + struct hlist_node *pos; char temp[50], *ptr; dent_rate = debugfs_create_dir("clk_rate", 0); @@ -312,7 +312,8 @@ static int __init clock_debug_init(void) if (IS_ERR(dent_local)) return PTR_ERR(dent_local); - while ((clock = msm_clock_get_nth(n++)) != 0) { + mutex_lock(&clocks_mutex); + hlist_for_each_entry(clock, pos, &clocks, list) { strncpy(temp, clock->dbg_name, ARRAY_SIZE(temp)-1); for (ptr = temp; *ptr; ptr++) *ptr = tolower(*ptr); @@ -323,10 +324,11 @@ static int __init clock_debug_init(void) debugfs_create_file(temp, S_IRUGO, dent_local, clock, &clock_local_fops); } + mutex_unlock(&clocks_mutex); return 0; } -device_initcall(clock_debug_init); +late_initcall(clock_debug_init); #endif /* The bootloader and/or AMSS may have left various clocks enabled. From 71caf48848be71e89e24753fcf7d54b5467ef97f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 20 Oct 2009 17:35:13 -0700 Subject: [PATCH 0434/2556] [ARM] msm: clock: Add debugfs file with detailed information about all clocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ie6c38093ee326d408b03a8b33dc734606b87c3c0 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/clock.c | 65 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c index bd606060ce545..83f76804ec389 100644 --- a/arch/arm/mach-msm/clock.c +++ b/arch/arm/mach-msm/clock.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include "clock.h" @@ -286,6 +288,67 @@ static int clock_debug_local_get(void *data, u64 *val) return 0; } +static void *clk_info_seq_start(struct seq_file *seq, loff_t *ppos) +{ + struct hlist_node *pos; + int i = *ppos; + mutex_lock(&clocks_mutex); + hlist_for_each(pos, &clocks) + if (i-- == 0) + return hlist_entry(pos, struct clk, list); + return NULL; +} + +static void *clk_info_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct clk *clk = v; + ++*pos; + return hlist_entry(clk->list.next, struct clk, list); +} + +static void clk_info_seq_stop(struct seq_file *seq, void *v) +{ + mutex_unlock(&clocks_mutex); +} + +static int clk_info_seq_show(struct seq_file *seq, void *v) +{ + struct clk *clk = v; + + seq_printf(seq, "Clock %s\n", clk->name); + seq_printf(seq, " Id %d\n", clk->id); + seq_printf(seq, " Count %d\n", clk->count); + seq_printf(seq, " Flags %x\n", clk->flags); + seq_printf(seq, " Dev %p %s\n", + clk->dev, clk->dev ? dev_name(clk->dev) : ""); + + seq_printf(seq, " Enabled %d\n", clk->ops->is_enabled(clk->id)); + seq_printf(seq, " Rate %ld\n", clk_get_rate(clk)); + + seq_printf(seq, "\n"); + return 0; +} + +static struct seq_operations clk_info_seqops = { + .start = clk_info_seq_start, + .next = clk_info_seq_next, + .stop = clk_info_seq_stop, + .show = clk_info_seq_show, +}; + +static int clk_info_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &clk_info_seqops); +} + +static const struct file_operations clk_info_fops = { + .owner = THIS_MODULE, + .open = clk_info_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + DEFINE_SIMPLE_ATTRIBUTE(clock_rate_fops, clock_debug_rate_get, clock_debug_rate_set, "%llu\n"); DEFINE_SIMPLE_ATTRIBUTE(clock_enable_fops, clock_debug_enable_get, @@ -312,6 +375,8 @@ static int __init clock_debug_init(void) if (IS_ERR(dent_local)) return PTR_ERR(dent_local); + debugfs_create_file("clk_info", 0x444, 0, NULL, &clk_info_fops); + mutex_lock(&clocks_mutex); hlist_for_each_entry(clock, pos, &clocks, list) { strncpy(temp, clock->dbg_name, ARRAY_SIZE(temp)-1); From 53827b7e6fc2843e617956b39cea25815b2e3f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 20 Oct 2009 17:36:25 -0700 Subject: [PATCH 0435/2556] [ARM] msm: clock: Add shared clock type. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If CLKFLAG_SHARED is set, then clk_get will allocate a handle. When clk_set_rate is called on this handle the requested rate is saved in the handle and the rate is set to the maximum request. Change-Id: I339c040c7f7957157bde50c7be828c24ebc99625 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/clock.c | 80 +++++++++++++++++++++++++++++++++++++++ arch/arm/mach-msm/clock.h | 8 ++++ 2 files changed, 88 insertions(+) diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c index 83f76804ec389..932e1e615f90d 100644 --- a/arch/arm/mach-msm/clock.c +++ b/arch/arm/mach-msm/clock.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,32 @@ unsigned msm_num_clocks; static DECLARE_BITMAP(clock_map_enabled, NR_CLKS); static DEFINE_SPINLOCK(clock_map_lock); +static struct clk *clk_allocate_handle(struct clk *sclk) +{ + unsigned long flags; + struct clk_handle *clkh = kzalloc(sizeof(*clkh), GFP_KERNEL); + if (!clkh) + return ERR_PTR(ENOMEM); + clkh->clk.flags = CLKFLAG_HANDLE; + clkh->source = sclk; + + spin_lock_irqsave(&clocks_lock, flags); + hlist_add_head(&clkh->clk.list, &sclk->handles); + spin_unlock_irqrestore(&clocks_lock, flags); + return &clkh->clk; +} + +static struct clk *source_clk(struct clk *clk) +{ + struct clk_handle *clkh; + + if (clk->flags & CLKFLAG_HANDLE) { + clkh = container_of(clk, struct clk_handle, clk); + clk = clkh->source; + } + return clk; +} + /* * Standard clock functions defined in include/linux/clk.h */ @@ -65,6 +92,8 @@ struct clk *clk_get(struct device *dev, const char *id) clk = ERR_PTR(-ENOENT); found_it: + if (!IS_ERR(clk) && (clk->flags & CLKFLAG_SHARED)) + clk = clk_allocate_handle(clk); mutex_unlock(&clocks_mutex); return clk; } @@ -72,6 +101,22 @@ EXPORT_SYMBOL(clk_get); void clk_put(struct clk *clk) { + struct clk_handle *clkh; + unsigned long flags; + + if (WARN_ON(IS_ERR(clk))) + return; + + if (!(clk->flags & CLKFLAG_HANDLE)) + return; + + clk_set_rate(clk, 0); + + spin_lock_irqsave(&clocks_lock, flags); + clkh = container_of(clk, struct clk_handle, clk); + hlist_del(&clk->list); + kfree(clkh); + spin_unlock_irqrestore(&clocks_lock, flags); } EXPORT_SYMBOL(clk_put); @@ -79,6 +124,7 @@ int clk_enable(struct clk *clk) { unsigned long flags; spin_lock_irqsave(&clocks_lock, flags); + clk = source_clk(clk); clk->count++; if (clk->count == 1) { clk->ops->enable(clk->id); @@ -95,6 +141,7 @@ void clk_disable(struct clk *clk) { unsigned long flags; spin_lock_irqsave(&clocks_lock, flags); + clk = source_clk(clk); BUG_ON(clk->count == 0); clk->count--; if (clk->count == 0) { @@ -117,13 +164,37 @@ EXPORT_SYMBOL(clk_reset); unsigned long clk_get_rate(struct clk *clk) { + clk = source_clk(clk); return clk->ops->get_rate(clk->id); } EXPORT_SYMBOL(clk_get_rate); +static unsigned long clk_find_min_rate(struct clk *clk) +{ + unsigned long rate = 0; + unsigned long flags; + struct clk_handle *clkh; + struct hlist_node *pos; + + spin_lock_irqsave(&clocks_lock, flags); + hlist_for_each_entry(clkh, pos, &clk->handles, clk.list) + if (clkh->rate > rate) + rate = clkh->rate; + spin_unlock_irqrestore(&clocks_lock, flags); + return rate; +} + int clk_set_rate(struct clk *clk, unsigned long rate) { int ret; + if (clk->flags & CLKFLAG_HANDLE) { + struct clk_handle *clkh; + clkh = container_of(clk, struct clk_handle, clk); + clkh->rate = rate; + clk = clkh->source; + rate = clk_find_min_rate(clk); + } + if (clk->flags & CLKFLAG_MAX) { ret = clk->ops->set_max_rate(clk->id, rate); if (ret) @@ -176,6 +247,7 @@ int clk_set_flags(struct clk *clk, unsigned long flags) { if (clk == NULL || IS_ERR(clk)) return -EINVAL; + clk = source_clk(clk); return clk->ops->set_flags(clk->id, flags); } EXPORT_SYMBOL(clk_set_flags); @@ -314,6 +386,9 @@ static void clk_info_seq_stop(struct seq_file *seq, void *v) static int clk_info_seq_show(struct seq_file *seq, void *v) { struct clk *clk = v; + unsigned long flags; + struct clk_handle *clkh; + struct hlist_node *pos; seq_printf(seq, "Clock %s\n", clk->name); seq_printf(seq, " Id %d\n", clk->id); @@ -321,6 +396,11 @@ static int clk_info_seq_show(struct seq_file *seq, void *v) seq_printf(seq, " Flags %x\n", clk->flags); seq_printf(seq, " Dev %p %s\n", clk->dev, clk->dev ? dev_name(clk->dev) : ""); + seq_printf(seq, " Handles %p\n", clk->handles.first); + spin_lock_irqsave(&clocks_lock, flags); + hlist_for_each_entry(clkh, pos, &clk->handles, clk.list) + seq_printf(seq, " Requested rate %ld\n", clkh->rate); + spin_unlock_irqrestore(&clocks_lock, flags); seq_printf(seq, " Enabled %d\n", clk->ops->is_enabled(clk->id)); seq_printf(seq, " Rate %ld\n", clk_get_rate(clk)); diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h index 52577f1b212b5..10d9458aa8ceb 100644 --- a/arch/arm/mach-msm/clock.h +++ b/arch/arm/mach-msm/clock.h @@ -27,6 +27,7 @@ #define CLKFLAG_NOINVERT 0x00000002 #define CLKFLAG_NONEST 0x00000004 #define CLKFLAG_NORESET 0x00000008 +#define CLKFLAG_HANDLE 0x00000010 #define CLK_FIRST_AVAILABLE_FLAG 0x00000100 #define CLKFLAG_AUTO_OFF 0x00000200 @@ -58,6 +59,13 @@ struct clk { const char *dbg_name; struct hlist_node list; struct device *dev; + struct hlist_head handles; +}; + +struct clk_handle { + struct clk clk; + struct clk *source; + unsigned long rate; }; #define A11S_CLK_CNTL_ADDR (MSM_CSR_BASE + 0x100) From ffdce54061d5c22ad6442c4d66061b92889c7600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 21 Oct 2009 16:36:51 -0700 Subject: [PATCH 0436/2556] [ARM] msm: clock: Hold spinlock while setting the clock rate. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If two drivers change their rate request for a shared clock at the same time the driver that completed clk_find_min_rate first could make the proc comm call last. Move the spinlock from clk_find_min_rate to clk_set_rate to fix this. Change-Id: Ic73be15e868c36c8351fb954427721ec486e6129 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/clock.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c index 932e1e615f90d..6bdba90cd524c 100644 --- a/arch/arm/mach-msm/clock.c +++ b/arch/arm/mach-msm/clock.c @@ -169,47 +169,48 @@ unsigned long clk_get_rate(struct clk *clk) } EXPORT_SYMBOL(clk_get_rate); -static unsigned long clk_find_min_rate(struct clk *clk) +static unsigned long clk_find_min_rate_locked(struct clk *clk) { unsigned long rate = 0; - unsigned long flags; struct clk_handle *clkh; struct hlist_node *pos; - spin_lock_irqsave(&clocks_lock, flags); hlist_for_each_entry(clkh, pos, &clk->handles, clk.list) if (clkh->rate > rate) rate = clkh->rate; - spin_unlock_irqrestore(&clocks_lock, flags); return rate; } int clk_set_rate(struct clk *clk, unsigned long rate) { int ret; + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); if (clk->flags & CLKFLAG_HANDLE) { struct clk_handle *clkh; clkh = container_of(clk, struct clk_handle, clk); clkh->rate = rate; clk = clkh->source; - rate = clk_find_min_rate(clk); + rate = clk_find_min_rate_locked(clk); } if (clk->flags & CLKFLAG_MAX) { ret = clk->ops->set_max_rate(clk->id, rate); if (ret) - return ret; + goto err; } if (clk->flags & CLKFLAG_MIN) { ret = clk->ops->set_min_rate(clk->id, rate); if (ret) - return ret; + goto err; } - if (clk->flags & CLKFLAG_MAX || clk->flags & CLKFLAG_MIN) - return ret; - - return clk->ops->set_rate(clk->id, rate); + if (!(clk->flags & (CLKFLAG_MAX | CLKFLAG_MIN))) + ret = clk->ops->set_rate(clk->id, rate); +err: + spin_unlock_irqrestore(&clocks_lock, flags); + return ret; } EXPORT_SYMBOL(clk_set_rate); From 7ba8ef6790f395dc73d729e4ed80df109521ae8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 3 Nov 2009 20:47:16 -0800 Subject: [PATCH 0437/2556] [ARM] msm: pm: Use scorpion specific A11S_CLK_SLEEP_EN value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ib0f9d70578066b8169aeded40759feec73179111 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/pm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index 0d6e13838d8d3..11357608f5a3a 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -305,7 +305,11 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) goto enter_failed; if (enter_state) { +#ifdef CONFIG_ARCH_MSM_SCORPION + writel(0x1b, A11S_CLK_SLEEP_EN); +#else writel(0x1f, A11S_CLK_SLEEP_EN); +#endif writel(1, A11S_PWRDOWN); writel(0, A11S_STANDBY_CTL); From 38463f7e6c78a87385d7775ec611655f9c9d2752 Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Sun, 15 Nov 2009 18:56:13 -0800 Subject: [PATCH 0438/2556] [ARM] msm: pm: Disable WFI rampdown and busy spin if there is no WFI speed If the board file specifies 0 as the WFI speed do not busy spin or ramp the cpu down. WFI speed ramp and busy spin are only required for 8k and not 7k. Signed-off-by: Mike Chan --- arch/arm/mach-msm/pm.c | 51 ++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index 11357608f5a3a..55cc2380eeb8c 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -409,10 +409,22 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) return rv; } +static int msm_pm_idle_spin(void) +{ + int spin; + spin = msm_pm_idle_spin_time >> 10; + while (spin-- > 0) { + if (msm_irq_pending()) { + return -1; + } + udelay(1); + } + return 0; +} + void arch_idle(void) { int ret; - int spin; int64_t sleep_time; int low_power = 0; #ifdef CONFIG_MSM_IDLE_STATS @@ -438,37 +450,48 @@ void arch_idle(void) if (msm_pm_debug_mask & MSM_PM_DEBUG_IDLE) printk(KERN_INFO "arch_idle: sleep time %llu, allow_sleep %d\n", sleep_time, allow_sleep); - spin = msm_pm_idle_spin_time >> 10; - while (spin-- > 0) { - if (msm_irq_pending()) { + if (sleep_time < msm_pm_idle_sleep_min_time || !allow_sleep) { + unsigned long saved_rate; + if (acpuclk_get_wfi_rate() && msm_pm_idle_spin() < 0) { #ifdef CONFIG_MSM_IDLE_STATS exit_stat = MSM_PM_STAT_IDLE_SPIN; #endif goto abort_idle; } - udelay(1); - } - if (sleep_time < msm_pm_idle_sleep_min_time || !allow_sleep) { - unsigned long saved_rate; saved_rate = acpuclk_wait_for_irq(); - if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK) + + + if (saved_rate && msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK) printk(KERN_DEBUG "arch_idle: clk %ld -> swfi\n", saved_rate); - if (saved_rate) - msm_arch_idle(); - else + + /* + * If there is a wfi speed specified and we failed to ramp, do not + * go into wfi. + */ + if (acpuclk_get_wfi_rate() && !saved_rate) while (!msm_irq_pending()) udelay(1); + else + msm_arch_idle(); + if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK) printk(KERN_DEBUG "msm_sleep: clk swfi -> %ld\n", saved_rate); - if (saved_rate && acpuclk_set_rate(saved_rate, 1) < 0) + if (acpuclk_set_rate(saved_rate, 1) < 0) printk(KERN_ERR "msm_sleep(): clk_set_rate %ld " "failed\n", saved_rate); #ifdef CONFIG_MSM_IDLE_STATS exit_stat = MSM_PM_STAT_IDLE_WFI; #endif - } else { + } else { + if (msm_pm_idle_spin() < 0) { +#ifdef CONFIG_MSM_IDLE_STATS + exit_stat = MSM_PM_STAT_IDLE_SPIN; +#endif + goto abort_idle; + } + low_power = 1; do_div(sleep_time, NSEC_PER_SEC / 32768); if (sleep_time > 0x6DDD000) { From 8bf7e1fb908fc92d34641d0a6320172ad882062a Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sun, 15 Nov 2009 14:43:04 -0800 Subject: [PATCH 0439/2556] [ARM] msm: scorpion: Disable predecode repair cache for qsd8x50 The predecode repair cache is used to accelerate the decode of 32-bit Thumb-2 instructions which cross an instruction cache line boundary. There's a bug in 8x50 and pre-production 7x30 that causes these instruction to be executed incorrectly. Qualcomm Scorpion errata CR145669. Change-Id: If3a7cd4f476dd4238904a513dfc5f27191944034 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/arch-init-scorpion.S | 7 +++++++ arch/arm/mach-msm/io.c | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/arch/arm/mach-msm/arch-init-scorpion.S b/arch/arm/mach-msm/arch-init-scorpion.S index 3bc533bcf4eb7..5cb549f41bfea 100644 --- a/arch/arm/mach-msm/arch-init-scorpion.S +++ b/arch/arm/mach-msm/arch-init-scorpion.S @@ -228,6 +228,13 @@ __cpu_early_init: ORR r2, r2, #0x00000800 MCR p15, 0, r2, c1, c0, 0 //; WCP15_SCTLR r2 +#ifdef CONFIG_ARCH_QSD8X50 + /* disable predecode repair cache for thumb2 (DPRC, set bit 4 in PVR0F2) */ + mrc p15, 0, r2, c15, c15, 2 + orr r2, r2, #0x10 + mcr p15, 0, r2, c15, c15, 2 +#endif + mov r1, lr //; Make sure Link stack is initialized with branch and links to sequential addresses //; This aids in creating a predictable startup environment diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c index 3e0bff394f609..07ab4c4ddbcec 100644 --- a/arch/arm/mach-msm/io.c +++ b/arch/arm/mach-msm/io.c @@ -99,6 +99,15 @@ static struct map_desc qsd8x50_io_desc[] __initdata = { void __init msm_map_qsd8x50_io(void) { + unsigned int unused; + + /* The bootloader may not have done it, so disable predecode repair + * cache for thumb2 (DPRC, set bit 4 in PVR0F2) due to a bug. + */ + asm volatile ("mrc p15, 0, %0, c15, c15, 2\n\t" + "orr %0, %0, #0x10\n\t" + "mcr p15, 0, %0, c15, c15, 2" + : "=&r" (unused)); iotable_init(qsd8x50_io_desc, ARRAY_SIZE(qsd8x50_io_desc)); } #endif /* CONFIG_ARCH_QSD8X50 */ From 7a607928cc56e1273a2800f69f4518cc545f6ac4 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 8 Jul 2009 17:06:27 -0700 Subject: [PATCH 0440/2556] [ARM] msm: expose htc nvs data through /proc/calibration Change-Id: I10ad33dad67aa8a4c9ad8fc72117bf6948311ce2 Signed-off-by: Dmitry Shmidt --- arch/arm/mach-msm/htc_wifi_nvs.c | 59 +++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-msm/htc_wifi_nvs.c b/arch/arm/mach-msm/htc_wifi_nvs.c index 95b8c3bbae9d8..062779464d323 100644 --- a/arch/arm/mach-msm/htc_wifi_nvs.c +++ b/arch/arm/mach-msm/htc_wifi_nvs.c @@ -20,18 +20,23 @@ #include #include #include +#include #include /* configuration tags specific to msm */ #define ATAG_MSM_WIFI 0x57494649 /* MSM WiFi */ -#define MAX_NVS_SIZE 0x800U -static unsigned char wifi_nvs_ram[MAX_NVS_SIZE]; +#define NVS_MAX_SIZE 0x800U +#define NVS_LEN_OFFSET 0x0C +#define NVS_DATA_OFFSET 0x40 + +static unsigned char wifi_nvs_ram[NVS_MAX_SIZE]; +static struct proc_dir_entry *wifi_calibration; unsigned char *get_wifi_nvs_ram( void ) { - return( wifi_nvs_ram ); + return wifi_nvs_ram; } EXPORT_SYMBOL(get_wifi_nvs_ram); @@ -39,18 +44,56 @@ static int __init parse_tag_msm_wifi(const struct tag *tag) { unsigned char *dptr = (unsigned char *)(&tag->u); unsigned size; - - size = min((tag->hdr.size - 2) * sizeof(__u32), MAX_NVS_SIZE); -#ifdef ATAG_MSM_WIFI_DEBUG +#ifdef ATAG_MSM_WIFI_DEBUG unsigned i; - +#endif + + size = min((tag->hdr.size - 2) * sizeof(__u32), NVS_MAX_SIZE); +#ifdef ATAG_MSM_WIFI_DEBUG printk("WiFi Data size = %d , 0x%x\n", tag->hdr.size, tag->hdr.tag); for(i=0;( i < size );i++) { printk("%02x ", *dptr++); } #endif - memcpy( (void *)wifi_nvs_ram, (void *)dptr, size ); + memcpy(wifi_nvs_ram, dptr, size); return 0; } __tagtable(ATAG_MSM_WIFI, parse_tag_msm_wifi); + +static unsigned wifi_get_nvs_size( void ) +{ + unsigned char *ptr; + unsigned len; + + ptr = get_wifi_nvs_ram(); + /* Size in format LE assumed */ + memcpy(&len, ptr + NVS_LEN_OFFSET, sizeof(len)); + len = min(len, (NVS_MAX_SIZE - NVS_DATA_OFFSET)); + return len; +} + +static int wifi_calibration_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned char *ptr; + unsigned len; + + ptr = get_wifi_nvs_ram(); + len = min(wifi_get_nvs_size(), (unsigned)count); + memcpy(page, ptr + NVS_DATA_OFFSET, len); + return len; +} + +static int __init wifi_nvs_init(void) +{ + wifi_calibration = create_proc_entry("calibration", 0444, NULL); + if (wifi_calibration != NULL) { + wifi_calibration->size = wifi_get_nvs_size(); + wifi_calibration->read_proc = wifi_calibration_read_proc; + wifi_calibration->write_proc = NULL; + } + return 0; +} + +device_initcall(wifi_nvs_init); From 6f8adca00aed53b9274404e48c18b32920d8c747 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Sun, 15 Nov 2009 10:47:11 -0800 Subject: [PATCH 0441/2556] [ARM] msm: Add ability to update calibration data size Signed-off-by: Dmitry Shmidt --- arch/arm/mach-msm/htc_wifi_nvs.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/mach-msm/htc_wifi_nvs.c b/arch/arm/mach-msm/htc_wifi_nvs.c index 062779464d323..17535959de672 100644 --- a/arch/arm/mach-msm/htc_wifi_nvs.c +++ b/arch/arm/mach-msm/htc_wifi_nvs.c @@ -73,6 +73,13 @@ static unsigned wifi_get_nvs_size( void ) return len; } +int wifi_calibration_size_set(void) +{ + if (wifi_calibration != NULL) + wifi_calibration->size = wifi_get_nvs_size(); + return 0; +} + static int wifi_calibration_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { From bfe2eecbcc7e84d097d2227ee7824beeb1252ad1 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Wed, 1 Jul 2009 20:31:36 -0700 Subject: [PATCH 0442/2556] capella cm3602 driver: initial implementation Signed-off-by: Iliyan Malchev --- drivers/input/misc/Kconfig | 6 + drivers/input/misc/Makefile | 1 + drivers/input/misc/capella_cm3602.c | 283 ++++++++++++++++++++++++++++ include/linux/capella_cm3602.h | 38 ++++ 4 files changed, 328 insertions(+) create mode 100644 drivers/input/misc/capella_cm3602.c create mode 100644 include/linux/capella_cm3602.h diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index d50c4c89df3ae..299089e14d202 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -470,4 +470,10 @@ config INPUT_CMA3000_I2C To compile this driver as a module, choose M here: the module will be called cma3000_d0x_i2c. +config INPUT_CAPELLA_CM3602 + tristate "Capella CM3602 proximity and light sensor" + help + Say Y here to enable the Capella CM3602 Short Distance Proximity + Sensor with Ambient Light Sensor. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 0968ae815c319..34ac3a2b19b1e 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o +obj-$(CONFIG_INPUT_CAPELLA_CM3602) += capella_cm3602.o obj-$(CONFIG_INPUT_CM109) += cm109.o obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o diff --git a/drivers/input/misc/capella_cm3602.c b/drivers/input/misc/capella_cm3602.c new file mode 100644 index 0000000000000..f7582d585c61e --- /dev/null +++ b/drivers/input/misc/capella_cm3602.c @@ -0,0 +1,283 @@ +/* drivers/input/misc/capella_cm3602.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Iliyan Malchev + * + * 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 + +#define D(x...) pr_info(x) + +static struct capella_cm3602_data { + struct input_dev *input_dev; + struct capella_cm3602_platform_data *pdata; + int enabled; +} the_data; + +static int misc_opened; + +static int capella_cm3602_report(struct capella_cm3602_data *data) +{ + int val = gpio_get_value(data->pdata->p_out); + if (val < 0) { + pr_err("%s: gpio_get_value error %d\n", __func__, val); + return val; + } + + D("proximity %d\n", val); + + /* 0 is close, 1 is far */ + input_report_abs(data->input_dev, ABS_DISTANCE, val); + input_sync(data->input_dev); + return val; +} + +static irqreturn_t capella_cm3602_irq_handler(int irq, void *data) +{ + struct capella_cm3602_data *ip = data; + int val = capella_cm3602_report(ip); + return IRQ_HANDLED; +} + +static int capella_cm3602_enable(struct capella_cm3602_data *data) +{ + int rc; + D("%s\n", __func__); + if (data->enabled) { + D("%s: already enabled\n", __func__); + return 0; + } + data->pdata->power(1); + rc = gpio_direction_output(data->pdata->p_en, 0); + data->enabled = !rc; + if (!rc) + capella_cm3602_report(data); + return rc; +} + +static int capella_cm3602_disable(struct capella_cm3602_data *data) +{ + int rc = -EIO; + D("%s\n", __func__); + if (!data->enabled) { + D("%s: already disabled\n", __func__); + return 0; + } + rc = gpio_direction_output(data->pdata->p_en, 1); + if (rc < 0) + return rc; + data->pdata->power(0); + data->enabled = 0; + return rc; +} + +static int capella_cm3602_setup(struct capella_cm3602_data *ip) +{ + int rc = -EIO; + struct capella_cm3602_platform_data *pdata = ip->pdata; + int irq = gpio_to_irq(pdata->p_out); + + D("%s\n", __func__); + + rc = gpio_request(pdata->p_out, "gpio_proximity_out"); + if (rc < 0) { + pr_err("%s: gpio %d request failed (%d)\n", + __func__, pdata->p_out, rc); + goto done; + } + + rc = gpio_request(pdata->p_en, "gpio_proximity_en"); + if (rc < 0) { + pr_err("%s: gpio %d request failed (%d)\n", + __func__, pdata->p_en, rc); + goto fail_free_p_out; + } + + rc = gpio_direction_input(pdata->p_out); + if (rc < 0) { + pr_err("%s: failed to set gpio %d as input (%d)\n", + __func__, pdata->p_out, rc); + goto fail_free_p_en; + } + + rc = request_irq(irq, + capella_cm3602_irq_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "capella_cm3602", + ip); + if (rc < 0) { + pr_err("%s: request_irq(%d) failed for gpio %d (%d)\n", + __func__, irq, + pdata->p_out, rc); + goto fail_free_p_en; + } + + rc = set_irq_wake(irq, 1); + if (rc < 0) { + pr_err("%s: failed to set irq %d as a wake interrupt\n", + __func__, irq); + goto fail_free_irq; + + } + + goto done; + +fail_free_irq: + free_irq(irq, 0); +fail_free_p_en: + gpio_free(pdata->p_en); +fail_free_p_out: + gpio_free(pdata->p_out); +done: + return rc; +} + +static int capella_cm3602_open(struct inode *inode, struct file *file) +{ + D("%s\n", __func__); + if (misc_opened) + return -EBUSY; + misc_opened = 1; + return 0; +} + +static int capella_cm3602_release(struct inode *inode, struct file *file) +{ + D("%s\n", __func__); + misc_opened = 0; + return capella_cm3602_disable(&the_data); +} + +static long capella_cm3602_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int val; + D("%s cmd %d\n", __func__, _IOC_NR(cmd)); + switch (cmd) { + case CAPELLA_CM3602_IOCTL_ENABLE: + if (get_user(val, (unsigned long __user *)arg)) + return -EFAULT; + if (val) + return capella_cm3602_enable(&the_data); + else + return capella_cm3602_disable(&the_data); + break; + case CAPELLA_CM3602_IOCTL_GET_ENABLED: + return put_user(the_data.enabled, (unsigned long __user *)arg); + break; + default: + pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd)); + return -EINVAL; + } +} + +static struct file_operations capella_cm3602_fops = { + .owner = THIS_MODULE, + .open = capella_cm3602_open, + .release = capella_cm3602_release, + .unlocked_ioctl = capella_cm3602_ioctl +}; + +struct miscdevice capella_cm3602_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "cm3602", + .fops = &capella_cm3602_fops +}; + +static int capella_cm3602_probe(struct platform_device *pdev) +{ + int rc = -EIO; + struct input_dev *input_dev; + struct capella_cm3602_data *ip; + struct capella_cm3602_platform_data *pdata; + + D("%s: probe\n", __func__); + + pdata = pdev->dev.platform_data; + if (!pdata) { + pr_err("%s: missing pdata!\n", __func__); + goto done; + } + if (!pdata->power) { + pr_err("%s: incomplete pdata!\n", __func__); + goto done; + } + + ip = &the_data; + platform_set_drvdata(pdev, ip); + + D("%s: allocating input device\n", __func__); + input_dev = input_allocate_device(); + if (!input_dev) { + pr_err("%s: could not allocate input device\n", __func__); + rc = -ENOMEM; + goto done; + } + ip->input_dev = input_dev; + ip->pdata = pdata; + input_set_drvdata(input_dev, ip); + + input_dev->name = "proximity"; + + set_bit(EV_ABS, input_dev->evbit); + input_set_abs_params(input_dev, ABS_DISTANCE, 0, 1, 0, 0); + + D("%s: registering input device\n", __func__); + rc = input_register_device(input_dev); + if (rc < 0) { + pr_err("%s: could not register input device\n", __func__); + goto err_free_input_device; + } + + D("%s: registering misc device\n", __func__); + rc = misc_register(&capella_cm3602_misc); + if (rc < 0) { + pr_err("%s: could not register misc device\n", __func__); + goto err_unregister_input_device; + } + + rc = capella_cm3602_setup(ip); + if (!rc) + goto done; + + misc_deregister(&capella_cm3602_misc); +err_unregister_input_device: + input_unregister_device(input_dev); +err_free_input_device: + input_free_device(input_dev); +done: + return rc; +} + +static struct platform_driver capella_cm3602_driver = { + .probe = capella_cm3602_probe, + .driver = { + .name = CAPELLA_CM3602, + .owner = THIS_MODULE + }, +}; + +static int __init capella_cm3602_init(void) +{ + return platform_driver_register(&capella_cm3602_driver); +} + +device_initcall(capella_cm3602_init); diff --git a/include/linux/capella_cm3602.h b/include/linux/capella_cm3602.h new file mode 100644 index 0000000000000..3dcffddb44730 --- /dev/null +++ b/include/linux/capella_cm3602.h @@ -0,0 +1,38 @@ +/* include/linux/capella_cm3602.h + * + * Copyright (C) 2009 Google, Inc. + * Author: Iliyan Malchev + * + * 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_CAPELLA_CM3602_H +#define __LINUX_CAPELLA_CM3602_H + +#include +#include + +#define CAPELLA_CM3602_IOCTL_MAGIC 'c' +#define CAPELLA_CM3602_IOCTL_GET_ENABLED \ + _IOR(CAPELLA_CM3602_IOCTL_MAGIC, 1, int *) +#define CAPELLA_CM3602_IOCTL_ENABLE \ + _IOW(CAPELLA_CM3602_IOCTL_MAGIC, 2, int *) + +#ifdef __KERNEL__ +#define CAPELLA_CM3602 "capella_cm3602" +struct capella_cm3602_platform_data { + int (*power)(int); /* power to the chip */ + int p_en; /* proximity-sensor enable */ + int p_out; /* proximity-sensor outpuCAPELLA_CM3602_IOCTL_ENABLE,t */ +}; +#endif /* __KERNEL__ */ + +#endif From c10552f8f1a999272befd1da417e20a2a381424c Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sun, 3 May 2009 03:11:52 -0700 Subject: [PATCH 0443/2556] input: support for Cypress TMG i2c capacitive touchscreen Based on a driver from HTC. - lots of cleanups. - don't do averaging/snapping for x/y coordinates add a fuzz value instead, and report the raw events to userspace. Change-Id: I6c8329ac630b77bcc954014eb8d7b3f14c4098e2 Signed-off-by: Dima Zavin --- drivers/input/touchscreen/Kconfig | 11 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/cy8c_tmg_ts.c | 467 ++++++++++++++++++++++++ include/linux/cy8c_tmg_ts.h | 37 ++ 4 files changed, 516 insertions(+) create mode 100644 drivers/input/touchscreen/cy8c_tmg_ts.c create mode 100644 include/linux/cy8c_tmg_ts.h diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 3c9da231da1ed..5dc30b21927b8 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -124,6 +124,17 @@ config TOUCHSCREEN_CY8CTMG110 To compile this driver as a module, choose M here: the module will be called cy8ctmg110_ts. +config TOUCHSCREEN_CYPRESS_TMG + tristate "Support for Cypress TMC i2c touchscreen" + depends on I2C + help + Say Y here to enable support for cy8ctmg touchcreens. + + If unsure, say N + + To compile this driver as a module, choose M here: the + module will be called h3600_ts_input. + config TOUCHSCREEN_DA9034 tristate "Touchscreen support for Dialog Semiconductor DA9034" depends on PMIC_DA903X diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index be5078e463524..4f2897627c660 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o +obj-$(CONFIG_TOUCHSCREEN_CYPRESS_TMG) += cy8c_tmg_ts.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o diff --git a/drivers/input/touchscreen/cy8c_tmg_ts.c b/drivers/input/touchscreen/cy8c_tmg_ts.c new file mode 100644 index 0000000000000..f48374eb30e17 --- /dev/null +++ b/drivers/input/touchscreen/cy8c_tmg_ts.c @@ -0,0 +1,467 @@ +/* drivers/input/touchscreen/cy8c_tmg_ts.c + * + * Copyright (C) 2007-2008 HTC Corporation. + * + * 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 + +#define CY8C_REG_START_NEW_SCAN 0x0F +#define CY8C_REG_INTR_STATUS 0x3C +#define CY8C_REG_VERSION 0x3E + +struct cy8c_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + int use_irq; + struct hrtimer timer; + struct work_struct work; + uint16_t version; + int (*power) (int on); + struct early_suspend early_suspend; +}; + +struct workqueue_struct *cypress_touch_wq; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void cy8c_ts_early_suspend(struct early_suspend *h); +static void cy8c_ts_late_resume(struct early_suspend *h); +#endif + +uint16_t sample_count, X_mean, Y_mean, first_touch; + +static s32 cy8c_read_word_data(struct i2c_client *client, + u8 command, uint16_t * data) +{ + s32 ret = i2c_smbus_read_word_data(client, command); + if (ret != -1) { + *data = (u16) ((ret << 8) | (ret >> 8)); + } + return ret; +} + +static int cy8c_init_panel(struct cy8c_ts_data *ts) +{ + int ret; + sample_count = X_mean = Y_mean = first_touch = 0; + + /* clean intr busy */ + ret = i2c_smbus_write_byte_data(ts->client, CY8C_REG_INTR_STATUS, + 0x00); + if (ret < 0) { + dev_err(&ts->client->dev, + "cy8c_init_panel failed for clean intr busy\n"); + goto exit; + } + + /* start new scan */ + ret = i2c_smbus_write_byte_data(ts->client, CY8C_REG_START_NEW_SCAN, + 0x01); + if (ret < 0) { + dev_err(&ts->client->dev, + "cy8c_init_panel failed for start new scan\n"); + goto exit; + } + +exit: + return ret; +} + +static void cy8c_ts_reset(struct i2c_client *client) +{ + struct cy8c_ts_data *ts = i2c_get_clientdata(client); + + if (ts->power) { + ts->power(0); + msleep(10); + ts->power(1); + msleep(10); + } + + cy8c_init_panel(ts); +} + +static void cy8c_ts_work_func(struct work_struct *work) +{ + struct cy8c_ts_data *ts = container_of(work, struct cy8c_ts_data, work); + uint16_t x1, y1, x2, y2; + uint8_t is_touch, start_reg, force, area, finger2_pressed; + uint8_t buf[11]; + struct i2c_msg msg[2]; + int ret = 0; + + x2 = y2 = 0; + + /*printk("%s: enter\n",__func__);*/ + is_touch = i2c_smbus_read_byte_data(ts->client, 0x20); + dev_dbg(&ts->client->dev, "fIsTouch %d,\n", is_touch); + if (is_touch < 0 || is_touch > 3) { + pr_err("%s: invalid is_touch = %d\n", __func__, is_touch); + cy8c_ts_reset(ts->client); + msleep(10); + goto done; + } + + msg[0].addr = ts->client->addr; + msg[0].flags = 0; + msg[0].len = 1; + start_reg = 0x16; + msg[0].buf = &start_reg; + + msg[1].addr = ts->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = sizeof(buf); + msg[1].buf = buf; + + ret = i2c_transfer(ts->client->adapter, msg, 2); + if (ret < 0) + goto done; + + /* parse data */ + force = buf[0]; + area = buf[1]; + x1 = (buf[2] << 8) | buf[3]; + y1 = (buf[6] << 8) | buf[7]; + is_touch = buf[10]; + + if (is_touch == 2) { + x2 = (buf[4] << 8) | buf[5]; + y2 = (buf[8] << 8) | buf[9]; + finger2_pressed = 1; + } + + dev_dbg(&ts->client->dev, + "bFingerForce %d, bFingerArea %d \n", force, area); + dev_dbg(&ts->client->dev, "x1: %d, y1: %d \n", x1, y1); + if (finger2_pressed) + dev_dbg(&ts->client->dev, "x2: %d, y2: %d \n", x2, y2); + + /* drop the first one? */ + if ((is_touch == 1) && (first_touch == 0)) { + first_touch = 1; + goto done; + } + + if (!first_touch) + goto done; + + if (is_touch == 2) + finger2_pressed = 1; + + input_report_abs(ts->input_dev, ABS_X, x1); + input_report_abs(ts->input_dev, ABS_Y, y1); + input_report_abs(ts->input_dev, ABS_PRESSURE, force); + input_report_abs(ts->input_dev, ABS_TOOL_WIDTH, area); + input_report_key(ts->input_dev, BTN_TOUCH, is_touch); + input_report_key(ts->input_dev, BTN_2, finger2_pressed); + + if (finger2_pressed) { + input_report_abs(ts->input_dev, ABS_HAT0X, x2); + input_report_abs(ts->input_dev, ABS_HAT0Y, y2); + } + input_sync(ts->input_dev); + +done: + if (is_touch == 0) + first_touch = sample_count = 0; + + /* prepare for next intr */ + i2c_smbus_write_byte_data(ts->client, CY8C_REG_INTR_STATUS, 0x00); + if (!ts->use_irq) + hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL); + else + enable_irq(ts->client->irq); +} + +static enum hrtimer_restart cy8c_ts_timer_func(struct hrtimer *timer) +{ + struct cy8c_ts_data *ts; + + ts = container_of(timer, struct cy8c_ts_data, timer); + queue_work(cypress_touch_wq, &ts->work); + return HRTIMER_NORESTART; +} + +static irqreturn_t cy8c_ts_irq_handler(int irq, void *dev_id) +{ + struct cy8c_ts_data *ts = dev_id; + + disable_irq_nosync(ts->client->irq); + queue_work(cypress_touch_wq, &ts->work); + return IRQ_HANDLED; +} + +static int cy8c_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cy8c_ts_data *ts; + struct cy8c_i2c_platform_data *pdata; + uint16_t panel_version; + int ret = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "need I2C_FUNC_I2C\n"); + ret = -ENODEV; + goto err_check_functionality_failed; + } + + ts = kzalloc(sizeof(struct cy8c_ts_data), GFP_KERNEL); + if (ts == NULL) { + dev_err(&client->dev, "allocate cy8c_ts_data failed\n"); + ret = -ENOMEM; + goto err_alloc_data_failed; + } + + INIT_WORK(&ts->work, cy8c_ts_work_func); + ts->client = client; + i2c_set_clientdata(client, ts); + + pdata = client->dev.platform_data; + if (pdata) { + ts->version = pdata->version; + ts->power = pdata->power; + } + + if (ts->power) { + ret = ts->power(1); + msleep(10); + if (ret < 0) { + dev_err(&client->dev, "power on failed\n"); + goto err_power_failed; + } + } + + ret = cy8c_read_word_data(ts->client, CY8C_REG_VERSION, &panel_version); + if (ret < 0) { + dev_err(&client->dev, "init panel failed\n"); + goto err_detect_failed; + } + dev_info(&client->dev, "Panel Version %04X\n", panel_version); + if (pdata) { + while (pdata->version > panel_version) { + dev_info(&client->dev, "old tp detected, " + "panel version = %x\n", panel_version); + pdata++; + } + } + + ret = cy8c_init_panel(ts); + if (ret < 0) { + dev_err(&client->dev, "init panel failed\n"); + goto err_detect_failed; + } + + ts->input_dev = input_allocate_device(); + if (ts->input_dev == NULL) { + ret = -ENOMEM; + dev_err(&client->dev, "Failed to allocate input device\n"); + goto err_input_dev_alloc_failed; + } + ts->input_dev->name = "cy8c-touchscreen"; + + set_bit(EV_SYN, ts->input_dev->evbit); + set_bit(EV_ABS, ts->input_dev->evbit); + set_bit(EV_KEY, ts->input_dev->evbit); + input_set_capability(ts->input_dev, EV_KEY, BTN_TOUCH); + input_set_capability(ts->input_dev, EV_KEY, BTN_2); + + input_set_abs_params(ts->input_dev, ABS_X, + pdata->abs_x_min, pdata->abs_x_max, 5, 0); + input_set_abs_params(ts->input_dev, ABS_Y, + pdata->abs_y_min, pdata->abs_y_max, 5, 0); + input_set_abs_params(ts->input_dev, ABS_HAT0X, + pdata->abs_x_min, pdata->abs_x_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_HAT0Y, + pdata->abs_y_min, pdata->abs_y_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_PRESSURE, + pdata->abs_pressure_min, pdata->abs_pressure_max, + 0, 0); + input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH, + pdata->abs_width_min, pdata->abs_width_max, 0, 0); + + ret = input_register_device(ts->input_dev); + if (ret) { + dev_err(&client->dev, + "cy8c_ts_probe: Unable to register %s input device\n", + ts->input_dev->name); + goto err_input_register_device_failed; + } + + if (client->irq) { + ret = request_irq(client->irq, cy8c_ts_irq_handler, + IRQF_TRIGGER_LOW, CYPRESS_TMG_NAME, ts); + if (ret == 0) + ts->use_irq = 1; + else + dev_err(&client->dev, "request_irq failed\n"); + } + + if (!ts->use_irq) { + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = cy8c_ts_timer_func; + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend = cy8c_ts_early_suspend; + ts->early_suspend.resume = cy8c_ts_late_resume; + register_early_suspend(&ts->early_suspend); +#endif + + dev_info(&client->dev, "Start touchscreen %s in %s mode\n", + ts->input_dev->name, (ts->use_irq ? "interrupt" : "polling")); + + return 0; + +err_input_register_device_failed: + input_free_device(ts->input_dev); + +err_input_dev_alloc_failed: + if (ts->power) + ts->power(0); + +err_detect_failed: +err_power_failed: + kfree(ts); + +err_alloc_data_failed: +err_check_functionality_failed: + return ret; +} + +static int cy8c_ts_remove(struct i2c_client *client) +{ + struct cy8c_ts_data *ts = i2c_get_clientdata(client); + + unregister_early_suspend(&ts->early_suspend); + + if (ts->use_irq) + free_irq(client->irq, ts); + else + hrtimer_cancel(&ts->timer); + + input_unregister_device(ts->input_dev); + kfree(ts); + + return 0; +} + +static int cy8c_ts_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct cy8c_ts_data *ts = i2c_get_clientdata(client); + int ret; + + if (ts->use_irq) + disable_irq_nosync(client->irq); + else + hrtimer_cancel(&ts->timer); + + ret = cancel_work_sync(&ts->work); + if (ret && ts->use_irq) + enable_irq(client->irq); + + if (ts->power) + ts->power(0); + + return 0; +} + +static int cy8c_ts_resume(struct i2c_client *client) +{ + int ret; + struct cy8c_ts_data *ts = i2c_get_clientdata(client); + + if (ts->power) { + ret = ts->power(1); + if (ret < 0) + dev_err(&client->dev, + "cy8c_ts_resume power on failed\n"); + msleep(10); + + cy8c_init_panel(ts); + } + + if (ts->use_irq) + enable_irq(client->irq); + else + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void cy8c_ts_early_suspend(struct early_suspend *h) +{ + struct cy8c_ts_data *ts; + ts = container_of(h, struct cy8c_ts_data, early_suspend); + cy8c_ts_suspend(ts->client, PMSG_SUSPEND); +} + +static void cy8c_ts_late_resume(struct early_suspend *h) +{ + struct cy8c_ts_data *ts; + ts = container_of(h, struct cy8c_ts_data, early_suspend); + cy8c_ts_resume(ts->client); +} +#endif + +static const struct i2c_device_id cy8c_ts_i2c_id[] = { + {CYPRESS_TMG_NAME, 0}, + {} +}; + +static struct i2c_driver cy8c_ts_driver = { + .id_table = cy8c_ts_i2c_id, + .probe = cy8c_ts_probe, + .remove = cy8c_ts_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = cy8c_ts_suspend, + .resume = cy8c_ts_resume, +#endif + .driver = { + .name = CYPRESS_TMG_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __devinit cy8c_ts_init(void) +{ + cypress_touch_wq = create_singlethread_workqueue("cypress_touch_wq"); + if (!cypress_touch_wq) + return -ENOMEM; + + return i2c_add_driver(&cy8c_ts_driver); +} + +static void __exit cy8c_ts_exit(void) +{ + if (cypress_touch_wq) + destroy_workqueue(cypress_touch_wq); + + i2c_del_driver(&cy8c_ts_driver); +} + +module_init(cy8c_ts_init); +module_exit(cy8c_ts_exit); + +MODULE_DESCRIPTION("Cypress TMG Touchscreen Driver"); +MODULE_LICENSE("GPL"); + diff --git a/include/linux/cy8c_tmg_ts.h b/include/linux/cy8c_tmg_ts.h new file mode 100644 index 0000000000000..f3cf17c4faee3 --- /dev/null +++ b/include/linux/cy8c_tmg_ts.h @@ -0,0 +1,37 @@ +/* include/linux/cy8c_tmg_ts.c + * + * Copyright (C) 2007-2008 HTC Corporation. + * + * 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 CY8C_I2C_H +#define CY8C_I2C_H + +#include + +#define CYPRESS_TMG_NAME "cy8c-tmg-ts" + +struct cy8c_i2c_platform_data { + uint16_t version; + int abs_x_min; + int abs_x_max; + int abs_y_min; + int abs_y_max; + int abs_pressure_min; + int abs_pressure_max; + int abs_width_min; + int abs_width_max; + int (*power)(int on); +}; + +#endif + From 8a6fca2147e6c33b9654d3e44551e92efb07e9a9 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Wed, 21 Oct 2009 18:33:32 -0700 Subject: [PATCH 0444/2556] ds2482: pass routine to write to SLP# pin via platform_data This allows for power savings when suspended. Signed-off-by: Brian Swetland --- drivers/w1/masters/ds2482.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index e5f74416d4b76..0077b483dca56 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -84,7 +84,9 @@ static const u8 ds2482_chan_rd[8] = static int ds2482_probe(struct i2c_client *client, const struct i2c_device_id *id); static int ds2482_remove(struct i2c_client *client); - +static int ds2482_suspend(struct i2c_client *client, + pm_message_t mesg); +static int ds2482_resume(struct i2c_client *client); /** * Driver data (common to all clients) @@ -101,6 +103,8 @@ static struct i2c_driver ds2482_driver = { }, .probe = ds2482_probe, .remove = ds2482_remove, + .suspend = ds2482_suspend, + .resume = ds2482_resume, .id_table = ds2482_id, }; @@ -408,6 +412,31 @@ static u8 ds2482_w1_reset_bus(void *data) } +static int ds2482_suspend(struct i2c_client *client, + pm_message_t mesg) +{ + void (*set_slp_n)(int n) = + client->dev.platform_data; + + if (set_slp_n) + set_slp_n(0); + + return 0; +} + +static int ds2482_resume(struct i2c_client *client) +{ + void (*set_slp_n)(int n) = + client->dev.platform_data; + + if (set_slp_n) { + set_slp_n(1); + udelay(100); + } + + return 0; +} + static int ds2482_probe(struct i2c_client *client, const struct i2c_device_id *id) { From d313ddaec609a48a2ac8aa93abf4be8baa5edeb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 7 Dec 2009 20:50:07 -0800 Subject: [PATCH 0445/2556] w1/ds2482: Retry i2c transfers on failure. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The w1 layer does not an error to be returned, and the ds2482 chip can nak i2c transfers. Change-Id: Id2802d782b1103a793c4d11297da515f94579ba1 Signed-off-by: Arve Hjønnevåg --- drivers/w1/masters/ds2482.c | 61 ++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index 0077b483dca56..62a3702c10a71 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -134,6 +134,52 @@ struct ds2482_data { u8 reg_config; }; +static int ds2482_write_byte(struct ds2482_data *pdev, u8 cmd) +{ + int ret; + int retry = 5; + + do { + ret = i2c_smbus_write_byte(pdev->client, cmd); + if (ret >= 0) + break; + dev_warn(&pdev->client->dev, + "i2c write %x failed, %d, retries left %d\n", + cmd, ret, retry); + } while(retry--); + return ret; +} + +static int ds2482_write_byte_data(struct ds2482_data *pdev, u8 cmd, u8 byte) +{ + int ret; + int retry = 5; + + do { + ret = i2c_smbus_write_byte_data(pdev->client, cmd, byte); + if (ret >= 0) + break; + dev_warn(&pdev->client->dev, + "i2c write %x %x failed, %d, retries left %d\n", + cmd, byte, ret, retry); + } while(retry--); + return ret; +} + +static int ds2482_read_byte(struct ds2482_data *pdev) +{ + int ret; + int retry = 5; + + do { + ret = i2c_smbus_read_byte(pdev->client); + if (ret >= 0) + break; + dev_warn(&pdev->client->dev, + "i2c read failed, %d, retries left %d\n", ret, retry); + } while(retry--); + return ret; +} /** * Sets the read pointer. @@ -144,8 +190,7 @@ struct ds2482_data { static inline int ds2482_select_register(struct ds2482_data *pdev, u8 read_ptr) { if (pdev->read_prt != read_ptr) { - if (i2c_smbus_write_byte_data(pdev->client, - DS2482_CMD_SET_READ_PTR, + if (ds2482_write_byte_data(pdev, DS2482_CMD_SET_READ_PTR, read_ptr) < 0) return -1; @@ -164,7 +209,7 @@ static inline int ds2482_select_register(struct ds2482_data *pdev, u8 read_ptr) */ static inline int ds2482_send_cmd(struct ds2482_data *pdev, u8 cmd) { - if (i2c_smbus_write_byte(pdev->client, cmd) < 0) + if (ds2482_write_byte(pdev, cmd) < 0) return -1; pdev->read_prt = DS2482_PTR_CODE_STATUS; @@ -184,7 +229,7 @@ static inline int ds2482_send_cmd(struct ds2482_data *pdev, u8 cmd) static inline int ds2482_send_cmd_data(struct ds2482_data *pdev, u8 cmd, u8 byte) { - if (i2c_smbus_write_byte_data(pdev->client, cmd, byte) < 0) + if (ds2482_write_byte_data(pdev, cmd, byte) < 0) return -1; /* all cmds leave in STATUS, except CONFIG */ @@ -235,13 +280,13 @@ static int ds2482_wait_1wire_idle(struct ds2482_data *pdev) */ static int ds2482_set_channel(struct ds2482_data *pdev, u8 channel) { - if (i2c_smbus_write_byte_data(pdev->client, DS2482_CMD_CHANNEL_SELECT, + if (ds2482_write_byte_data(pdev, DS2482_CMD_CHANNEL_SELECT, ds2482_chan_wr[channel]) < 0) return -1; pdev->read_prt = DS2482_PTR_CODE_CHANNEL; pdev->channel = -1; - if (i2c_smbus_read_byte(pdev->client) == ds2482_chan_rd[channel]) { + if (ds2482_read_byte(pdev) == ds2482_chan_rd[channel]) { pdev->channel = channel; return 0; } @@ -365,7 +410,7 @@ static u8 ds2482_w1_read_byte(void *data) ds2482_select_register(pdev, DS2482_PTR_CODE_DATA); /* Read the data byte */ - result = i2c_smbus_read_byte(pdev->client); + result = ds2482_read_byte(pdev); mutex_unlock(&pdev->access_lock); @@ -468,7 +513,7 @@ static int ds2482_probe(struct i2c_client *client, ndelay(525); /* Read the status byte - only reset bit and line should be set */ - temp1 = i2c_smbus_read_byte(client); + temp1 = ds2482_read_byte(data); if (temp1 != (DS2482_REG_STS_LL | DS2482_REG_STS_RST)) { dev_warn(&client->dev, "DS2482 reset status " "0x%02X - not a DS2482\n", temp1); From 9c64ef09fb60bb63100638e5551851c7362245df Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Mon, 8 Feb 2010 20:17:03 -0800 Subject: [PATCH 0446/2556] ds2784_battery: 1wire ds2784 battery gauge device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Brian Swetland ds2784_battery: 1wire battery gauge / battery driver - is_ac_power_supplied() to determine charge mode is a hack until the power supply framework provides a cleaner solution. Signed-off-by: Brian Swetland ds2784: recognize dummy batteries and handle them sanely If a dummy battery ("DUMMY!" in the EEPROM data at 0x20) is detected, set a flag to always lie to userspace about percentage (75%) and reset the ACC register to ~500mAh. Signed-off-by: Brian Swetland ds2784: clean up charge state management, add temp check - tidy up the charge state calculation a bit - remove some ugly globals - handle warm/hot/critical temperature states - report overtemp to userspace for hot+ states - log charge source and charge mode separately Signed-off-by: Brian Swetland ds2784battery: report 100% while maintaining charge Once we fully charge, we stop charging until we drop to 99% then we top off again. When in this state we will pretend we're fully charged (reporting 100% to userspace) until we drop below 99, to avoid confusion. Signed-off-by: Brian Swetland ds2784_battery: adjust poll interval when asleep If we're suspended and we are not on wall power, poll the battery gauge less often (every 10 minutes). Whenever we resume, program the timer for lastpoll+60s, to ensure that if we hadn't polled in >60s we do so upon wake. Signed-off-by: Brian Swetland ds2784_battery: remove some printk chatter, reduce log size Signed-off-by: Brian Swetland ds2784_battery: Request gpios. Change-Id: I89db04e543ee91cdbd67256f136adf4e4c6abca1 Signed-off-by: Arve Hjønnevåg ds2784_battery: Use full status from ds2784. The battery could stop charging and drain completely while plugged into the charges. If the battery level never reached 100% the old code would not turn off the charge request. The hardware changer would then eventually turn off by itself and the battery would start to drain. Change-Id: Iac83b7acae68c23ca3c5648e3616076ba5de1b95 Signed-off-by: Arve Hjønnevåg ds2784_battery: Briefly disable charger if it has discharged for an hour. If the battery fails to charge fully, the changer will eventually turn off. This will turn the charger back on. Change-Id: I051f87a2cddc8898fa3545eabd6401dd03afadb0 Signed-off-by: Arve Hjønnevåg ds2784_battery: Disable charging at battery gauge instead of charger. Leaving the charger on allows the phone to draw most of its power from the charger while the battery provides power when more current than the charger can provide is needed. Change-Id: I04ae676b6e8cf0075fc73d1c5cb55a04446da689 Signed-off-by: Arve Hjønnevåg [ARM] msm: merge w1_ds2784.c into ds2784_battery.c Signed-off-by: Iliyan Malchev [ARM] msm: ds2784_battery: remove board-specific code, add header Signed-off-by: Iliyan Malchev drivers: power: ds2784: Fix conversion of temperature so sign bit is maintained. Also report temperature changes when temp is above 45C. Signed-off-by: Eric Olsen --- drivers/power/Kconfig | 6 + drivers/power/Makefile | 1 + drivers/power/ds2784_battery.c | 757 +++++++++++++++++++++++++++++++++ drivers/power/w1_ds2784.h | 132 ++++++ drivers/w1/w1_family.h | 1 + include/linux/ds2784_battery.h | 29 ++ 6 files changed, 926 insertions(+) create mode 100755 drivers/power/ds2784_battery.c create mode 100644 drivers/power/w1_ds2784.h create mode 100644 include/linux/ds2784_battery.h diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 61bf5d724139c..f017a6e2a09ed 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -75,6 +75,12 @@ config BATTERY_DS2782 Say Y here to enable support for the DS2782/DS2786 standalone battery gas-gauge. +config BATTERY_DS2784 + tristate "DS2784 battery driver " + select W1 + help + Say Y here to enable support for batteries with ds2784 chip. + config BATTERY_PMU tristate "Apple PMU battery" depends on PPC32 && ADB_PMU diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 8385bfae87283..5cbbddf1d4541 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_TEST_POWER) += test_power.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o +obj-$(CONFIG_BATTERY_DS2784) += ds2784_battery.o obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o diff --git a/drivers/power/ds2784_battery.c b/drivers/power/ds2784_battery.c new file mode 100755 index 0000000000000..0f8a34b7c671c --- /dev/null +++ b/drivers/power/ds2784_battery.c @@ -0,0 +1,757 @@ +/* drivers/power/ds2784_battery.c + * + * Copyright (C) 2009 HTC Corporation + * Copyright (C) 2009 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../w1/w1.h" +#include "w1_ds2784.h" + +extern int is_ac_power_supplied(void); + +struct battery_status { + int timestamp; + + int voltage_uV; /* units of uV */ + int current_uA; /* units of uA */ + int current_avg_uA; + int charge_uAh; + + u16 temp_C; /* units of 0.1 C */ + + u8 percentage; /* battery percentage */ + u8 charge_source; + u8 status_reg; + u8 battery_full; /* battery full (don't charge) */ + + u8 cooldown; /* was overtemp */ + u8 charge_mode; +} __attribute__((packed)); + + +#define SOURCE_NONE 0 +#define SOURCE_USB 1 +#define SOURCE_AC 2 + +#define CHARGE_OFF 0 +#define CHARGE_SLOW 1 +#define CHARGE_FAST 2 +#define CHARGE_BATT_DISABLE 3 /* disable charging at battery */ + +#define TEMP_CRITICAL 600 /* no charging at all */ +#define TEMP_HOT 500 /* no fast charge, no charge > 4.1v */ +#define TEMP_WARM 450 /* no fast charge above this */ + +#define TEMP_HOT_MAX_MV 4100 /* stop charging here when hot */ +#define TEMP_HOT_MIN_MV 3800 /* resume charging here when hot */ +#define CE_DISABLE_MIN_MV 4100 + +#define BATTERY_LOG_MAX 1024 +#define BATTERY_LOG_MASK (BATTERY_LOG_MAX - 1) + +/* When we're awake or running on wall power, sample the battery + * gauge every FAST_POLL seconds. If we're asleep and on battery + * power, sample every SLOW_POLL seconds + */ +#define FAST_POLL (1 * 60) +#define SLOW_POLL (10 * 60) + +static DEFINE_MUTEX(battery_log_lock); +static struct battery_status battery_log[BATTERY_LOG_MAX]; +static unsigned battery_log_head; +static unsigned battery_log_tail; + +void battery_log_status(struct battery_status *s) +{ + unsigned n; + mutex_lock(&battery_log_lock); + n = battery_log_head; + memcpy(battery_log + n, s, sizeof(struct battery_status)); + n = (n + 1) & BATTERY_LOG_MASK; + if (n == battery_log_tail) + battery_log_tail = (battery_log_tail + 1) & BATTERY_LOG_MASK; + battery_log_head = n; + mutex_unlock(&battery_log_lock); +} + +static const char *battery_source[3] = { "none", " usb", " ac" }; +static const char *battery_mode[4] = { " off", "slow", "fast", "full" }; + +static int battery_log_print(struct seq_file *sf, void *private) +{ + unsigned n; + mutex_lock(&battery_log_lock); + seq_printf(sf, "timestamp mV mA avg mA uAh dC %% src mode reg full\n"); + for (n = battery_log_tail; n != battery_log_head; n = (n + 1) & BATTERY_LOG_MASK) { + struct battery_status *s = battery_log + n; + seq_printf(sf, "%9d %5d %6d %6d %8d %4d %3d %s %s 0x%02x %d\n", + s->timestamp, s->voltage_uV / 1000, + s->current_uA / 1000, s->current_avg_uA / 1000, + s->charge_uAh, s->temp_C, + s->percentage, + battery_source[s->charge_source], + battery_mode[s->charge_mode], + s->status_reg, s->battery_full); + } + mutex_unlock(&battery_log_lock); + return 0; +} + + +struct ds2784_device_info { + struct device *dev; + + /* DS2784 data, valid after calling ds2784_battery_read_status() */ + char raw[DS2784_DATA_SIZE]; /* raw DS2784 data */ + + struct battery_status status; + + struct power_supply bat; + struct workqueue_struct *monitor_wqueue; + struct work_struct monitor_work; + struct alarm alarm; + struct wake_lock work_wake_lock; + + int (*charge)(int on, int fast); + struct w1_slave *w1_slave; + + u8 dummy; /* dummy battery flag */ + u8 last_charge_mode; /* previous charger state */ + u8 slow_poll; + + ktime_t last_poll; + ktime_t last_charge_seen; +}; + +#define psy_to_dev_info(x) container_of((x), struct ds2784_device_info, bat) + +static struct wake_lock vbus_wake_lock; + +#define BATT_RSNSP (67) /*Passion battery source 1*/ + +static enum power_supply_property battery_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CHARGE_COUNTER, +}; + +static int battery_initial; + +static int battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val); + +static void battery_ext_power_changed(struct power_supply *psy); + +#define to_ds2784_device_info(x) container_of((x), struct ds2784_device_info, \ + bat); + +static void ds2784_parse_data(u8 *raw, struct battery_status *s) +{ + short n; + + /* Get status reg */ + s->status_reg = raw[DS2784_REG_STS]; + + /* Get Level */ + s->percentage = raw[DS2784_REG_RARC]; + + /* Get Voltage: Unit=4.886mV, range is 0V to 4.99V */ + n = (((raw[DS2784_REG_VOLT_MSB] << 8) | + (raw[DS2784_REG_VOLT_LSB])) >> 5); + + s->voltage_uV = n * 4886; + + /* Get Current: Unit= 1.5625uV x Rsnsp(67)=104.68 */ + n = ((raw[DS2784_REG_CURR_MSB]) << 8) | + raw[DS2784_REG_CURR_LSB]; + s->current_uA = ((n * 15625) / 10000) * 67; + + n = ((raw[DS2784_REG_AVG_CURR_MSB]) << 8) | + raw[DS2784_REG_AVG_CURR_LSB]; + s->current_avg_uA = ((n * 15625) / 10000) * 67; + + /* Get Temperature: + * 11 bit signed result in Unit=0.125 degree C. + * Convert to integer tenths of degree C. + */ + n = ((raw[DS2784_REG_TEMP_MSB] << 8) | + (raw[DS2784_REG_TEMP_LSB])) >> 5; + + s->temp_C = (n * 10) / 8; + + /* RAAC is in units of 1.6mAh */ + s->charge_uAh = ((raw[DS2784_REG_RAAC_MSB] << 8) | + raw[DS2784_REG_RAAC_LSB]) * 1600; +} + +static int w1_ds2784_io(struct w1_slave *sl, char *buf, int addr, size_t count, int io) +{ + if (!sl) + return 0; + + mutex_lock(&sl->master->mutex); + + if (addr > DS2784_DATA_SIZE || addr < 0) { + count = 0; + goto out; + } + if (addr + count > DS2784_DATA_SIZE) + count = DS2784_DATA_SIZE - addr; + + if (!w1_reset_select_slave(sl)) { + if (!io) { + w1_write_8(sl->master, W1_DS2784_READ_DATA); + w1_write_8(sl->master, addr); + count = w1_read_block(sl->master, buf, count); + } else { + w1_write_8(sl->master, W1_DS2784_WRITE_DATA); + w1_write_8(sl->master, addr); + w1_write_block(sl->master, buf, count); + /* XXX w1_write_block returns void, not n_written */ + } + } + +out: + mutex_unlock(&sl->master->mutex); + + return count; +} + +static int w1_ds2784_read(struct w1_slave *sl, char *buf, int addr, size_t count) +{ + return w1_ds2784_io(sl, buf, addr, count, 0); +} + +static int w1_ds2784_write(struct w1_slave *sl, char *buf, int addr, size_t count) +{ + return w1_ds2784_io(sl, buf, addr, count, 1); +} + +static int ds2784_set_cc(struct ds2784_device_info *di, bool enable) +{ + int ret; + + if (enable) + di->raw[DS2784_REG_PORT] |= 0x02; + else + di->raw[DS2784_REG_PORT] &= ~0x02; + ret = w1_ds2784_write(di->w1_slave, di->raw + DS2784_REG_PORT, + DS2784_REG_PORT, 1); + if (ret != 1) { + dev_warn(di->dev, "call to w1_ds2784_write failed (0x%p)\n", + di->w1_slave); + return 1; + } + return 0; +} + +static int ds2784_battery_read_status(struct ds2784_device_info *di) +{ + int ret, start, count; + + /* The first time we read the entire contents of SRAM/EEPROM, + * but after that we just read the interesting bits that change. */ + if (di->raw[DS2784_REG_RSNSP] == 0x00) { + start = 0; + count = DS2784_DATA_SIZE; + } else { + start = DS2784_REG_PORT; + count = DS2784_REG_CURR_LSB - start + 1; + } + + ret = w1_ds2784_read(di->w1_slave, di->raw + start, start, count); + if (ret != count) { + dev_warn(di->dev, "call to w1_ds2784_read failed (0x%p)\n", + di->w1_slave); + return 1; + } + + if (battery_initial == 0) { + if (!memcmp(di->raw + 0x20, "DUMMY!", 6)) { + unsigned char acr[2]; + + di->dummy = 1; + pr_info("batt: dummy battery detected\n"); + + /* reset ACC register to ~500mAh, since it may have zeroed out */ + acr[0] = 0x05; + acr[1] = 0x06; + w1_ds2784_write(di->w1_slave, acr, DS2784_REG_ACCUMULATE_CURR_MSB, 2); + } + battery_initial = 1; + } + + ds2784_parse_data(di->raw, &di->status); + + pr_info("batt: %3d%%, %d mV, %d mA (%d avg), %d.%d C, %d mAh\n", + di->status.percentage, + di->status.voltage_uV / 1000, di->status.current_uA / 1000, + di->status.current_avg_uA / 1000, + di->status.temp_C / 10, di->status.temp_C % 10, + di->status.charge_uAh / 1000); + + return 0; +} + +static int battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct ds2784_device_info *di = psy_to_dev_info(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + switch (di->status.charge_source) { + case CHARGE_OFF: + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case CHARGE_FAST: + case CHARGE_SLOW: + if (di->status.battery_full) + val->intval = POWER_SUPPLY_STATUS_FULL; + else if (di->status.charge_mode == CHARGE_OFF || + di->status.charge_mode == CHARGE_BATT_DISABLE) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + default: + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + break; + case POWER_SUPPLY_PROP_HEALTH: + if (di->status.temp_C >= TEMP_HOT) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + else + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_PRESENT: + /* XXX todo */ + val->intval = 1; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + if (di->dummy) + val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + else + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_CAPACITY: + if (di->dummy) + val->intval = 75; + else + val->intval = di->status.percentage; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = di->status.voltage_uV; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = di->status.temp_C; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = di->status.current_uA; + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + val->intval = di->status.current_avg_uA; + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + val->intval = di->status.charge_uAh; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void ds2784_battery_update_status(struct ds2784_device_info *di) +{ + u8 last_level; + last_level = di->status.percentage; + + ds2784_battery_read_status(di); + + if ((last_level != di->status.percentage) || (di->status.temp_C > 450)) + power_supply_changed(&di->bat); +} + +static DEFINE_MUTEX(charge_state_lock); + +static bool check_timeout(ktime_t now, ktime_t last, int seconds) +{ + ktime_t timeout = ktime_add(last, ktime_set(seconds, 0)); + return ktime_sub(timeout, now).tv64 < 0; +} + +static int battery_adjust_charge_state(struct ds2784_device_info *di) +{ + unsigned source; + int rc = 0; + int temp, volt; + u8 charge_mode; + bool charge_timeout = false; + + mutex_lock(&charge_state_lock); + + temp = di->status.temp_C; + volt = di->status.voltage_uV / 1000; + + source = di->status.charge_source; + + /* initially our charge mode matches our source: + * NONE:OFF, USB:SLOW, AC:FAST + */ + charge_mode = source; + + /* shut off charger when full: + * - CHGTF flag is set + */ + if (di->status.status_reg & 0x80) { + di->status.battery_full = 1; + charge_mode = CHARGE_BATT_DISABLE; + } else + di->status.battery_full = 0; + + if (temp >= TEMP_HOT) { + if (temp >= TEMP_CRITICAL) + charge_mode = CHARGE_BATT_DISABLE; + + /* once we charge to max voltage when hot, disable + * charging until the temp drops or the voltage drops + */ + if (volt >= TEMP_HOT_MAX_MV) + di->status.cooldown = 1; + } + + /* when the battery is warm, only charge in slow charge mode */ + if ((temp >= TEMP_WARM) && (charge_mode == CHARGE_FAST)) + charge_mode = CHARGE_SLOW; + + if (di->status.cooldown) { + if ((temp < TEMP_WARM) || (volt <= TEMP_HOT_MIN_MV)) + di->status.cooldown = 0; + else + charge_mode = CHARGE_BATT_DISABLE; + } + + if (di->status.current_uA > 1024) + di->last_charge_seen = di->last_poll; + else if (di->last_charge_mode != CHARGE_OFF && + check_timeout(di->last_poll, di->last_charge_seen, 60 * 60)) { + if (di->last_charge_mode == CHARGE_BATT_DISABLE) { + /* The charger is only powering the phone. Toggle the + * enable line periodically to prevent auto shutdown. + */ + di->last_charge_seen = di->last_poll; + pr_info("batt: charging POKE CHARGER\n"); + di->charge(0, 0); + udelay(10); + di->charge(1, source == CHARGE_FAST); + } else { + /* The charger has probably stopped charging. Turn it + * off until the next sample period. + */ + charge_timeout = true; + charge_mode = CHARGE_OFF; + } + } + + if (source == CHARGE_OFF) + charge_mode = CHARGE_OFF; + + /* Don't use CHARGE_BATT_DISABLE unless the voltage is high since the + * voltage drop over the discharge-path diode can cause a shutdown. + */ + if (charge_mode == CHARGE_BATT_DISABLE && volt < CE_DISABLE_MIN_MV) + charge_mode = CHARGE_OFF; + + if (di->last_charge_mode == charge_mode) + goto done; + + di->last_charge_mode = charge_mode; + di->status.charge_mode = charge_mode; + + switch (charge_mode) { + case CHARGE_OFF: + di->charge(0, 0); + ds2784_set_cc(di, true); + if (temp >= TEMP_CRITICAL) + pr_info("batt: charging OFF [OVERTEMP]\n"); + else if (di->status.cooldown) + pr_info("batt: charging OFF [COOLDOWN]\n"); + else if (di->status.battery_full) + pr_info("batt: charging OFF [FULL]\n"); + else if (charge_timeout) + pr_info("batt: charging OFF [TIMEOUT]\n"); + else + pr_info("batt: charging OFF\n"); + break; + case CHARGE_BATT_DISABLE: + di->last_charge_seen = di->last_poll; + ds2784_set_cc(di, false); + di->charge(1, source == CHARGE_FAST); + if (temp >= TEMP_CRITICAL) + pr_info("batt: charging BATTOFF [OVERTEMP]\n"); + else if (di->status.cooldown) + pr_info("batt: charging BATTOFF [COOLDOWN]\n"); + else if (di->status.battery_full) + pr_info("batt: charging BATTOFF [FULL]\n"); + else + pr_info("batt: charging BATTOFF [UNKNOWN]\n"); + break; + case CHARGE_SLOW: + di->last_charge_seen = di->last_poll; + ds2784_set_cc(di, true); + di->charge(1, 0); + pr_info("batt: charging SLOW\n"); + break; + case CHARGE_FAST: + di->last_charge_seen = di->last_poll; + ds2784_set_cc(di, true); + di->charge(1, 1); + pr_info("batt: charging FAST\n"); + break; + } + rc = 1; +done: + mutex_unlock(&charge_state_lock); + return rc; +} + +static void ds2784_program_alarm(struct ds2784_device_info *di, int seconds) +{ + ktime_t low_interval = ktime_set(seconds - 10, 0); + ktime_t slack = ktime_set(20, 0); + ktime_t next; + + next = ktime_add(di->last_poll, low_interval); + + alarm_start_range(&di->alarm, next, ktime_add(next, slack)); +} + +static void ds2784_battery_work(struct work_struct *work) +{ + struct ds2784_device_info *di = + container_of(work, struct ds2784_device_info, monitor_work); + struct timespec ts; + unsigned long flags; + + ds2784_battery_update_status(di); + + di->last_poll = alarm_get_elapsed_realtime(); + + if (battery_adjust_charge_state(di)) + power_supply_changed(&di->bat); + + ts = ktime_to_timespec(di->last_poll); + di->status.timestamp = ts.tv_sec; + battery_log_status(&di->status); + + /* prevent suspend before starting the alarm */ + local_irq_save(flags); + wake_unlock(&di->work_wake_lock); + ds2784_program_alarm(di, FAST_POLL); + local_irq_restore(flags); +} + +static void ds2784_battery_alarm(struct alarm *alarm) +{ + struct ds2784_device_info *di = + container_of(alarm, struct ds2784_device_info, alarm); + wake_lock(&di->work_wake_lock); + queue_work(di->monitor_wqueue, &di->monitor_work); +} + +static void battery_ext_power_changed(struct power_supply *psy) +{ + struct ds2784_device_info *di; + int got_power; + + di = psy_to_dev_info(psy); + got_power = power_supply_am_i_supplied(psy); + + if (got_power) { + if (is_ac_power_supplied()) + di->status.charge_source = SOURCE_AC; + else + di->status.charge_source = SOURCE_USB; + wake_lock(&vbus_wake_lock); + } else { + di->status.charge_source = SOURCE_NONE; + /* give userspace some time to see the uevent and update + * LED state or whatnot... + */ + wake_lock_timeout(&vbus_wake_lock, HZ / 2); + } + battery_adjust_charge_state(di); + power_supply_changed(psy); +} + +static int ds2784_battery_probe(struct platform_device *pdev) +{ + int rc; + struct ds2784_device_info *di; + struct ds2784_platform_data *pdata; + + di = kzalloc(sizeof(*di), GFP_KERNEL); + if (!di) + return -ENOMEM; + + platform_set_drvdata(pdev, di); + + pdata = pdev->dev.platform_data; + if (!pdata || !pdata->charge || !pdata->w1_slave) { + pr_err("%s: pdata missing or invalid\n", __func__); + rc = -EINVAL; + goto fail_register; + } + + di->charge = pdata->charge; + di->w1_slave = pdata->w1_slave; + + di->dev = &pdev->dev; + + di->bat.name = "battery"; + di->bat.type = POWER_SUPPLY_TYPE_BATTERY; + di->bat.properties = battery_properties; + di->bat.num_properties = ARRAY_SIZE(battery_properties); + di->bat.external_power_changed = battery_ext_power_changed; + di->bat.get_property = battery_get_property; + di->last_charge_mode = 0xff; + + rc = power_supply_register(&pdev->dev, &di->bat); + if (rc) + goto fail_register; + + INIT_WORK(&di->monitor_work, ds2784_battery_work); + di->monitor_wqueue = create_singlethread_workqueue(dev_name(&pdev->dev)); + + /* init to something sane */ + di->last_poll = alarm_get_elapsed_realtime(); + + if (!di->monitor_wqueue) { + rc = -ESRCH; + goto fail_workqueue; + } + wake_lock_init(&di->work_wake_lock, WAKE_LOCK_SUSPEND, + "ds2784-battery"); + alarm_init(&di->alarm, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP, + ds2784_battery_alarm); + wake_lock(&di->work_wake_lock); + queue_work(di->monitor_wqueue, &di->monitor_work); + return 0; + +fail_workqueue: + power_supply_unregister(&di->bat); +fail_register: + kfree(di); + return rc; +} + +static int ds2784_suspend(struct device *dev) +{ + struct ds2784_device_info *di = dev_get_drvdata(dev); + + /* If we are on battery, reduce our update rate until + * we next resume. + */ + if (di->status.charge_source == SOURCE_NONE) { + ds2784_program_alarm(di, SLOW_POLL); + di->slow_poll = 1; + } + return 0; +} + +static int ds2784_resume(struct device *dev) +{ + struct ds2784_device_info *di = dev_get_drvdata(dev); + + /* We might be on a slow sample cycle. If we're + * resuming we should resample the battery state + * if it's been over a minute since we last did + * so, and move back to sampling every minute until + * we suspend again. + */ + if (di->slow_poll) { + ds2784_program_alarm(di, FAST_POLL); + di->slow_poll = 0; + } + return 0; +} + +static struct dev_pm_ops ds2784_pm_ops = { + .suspend = ds2784_suspend, + .resume = ds2784_resume, +}; + +static struct platform_driver ds2784_battery_driver = { + .driver = { + .name = "ds2784-battery", + .pm = &ds2784_pm_ops, + }, + .probe = ds2784_battery_probe, +}; + +static int battery_log_open(struct inode *inode, struct file *file) +{ + return single_open(file, battery_log_print, NULL); +} + +static struct file_operations battery_log_fops = { + .open = battery_log_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init ds2784_battery_init(void) +{ + debugfs_create_file("battery_log", 0444, NULL, NULL, &battery_log_fops); + wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present"); + return platform_driver_register(&ds2784_battery_driver); +} + +module_init(ds2784_battery_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Justin Lin "); +MODULE_DESCRIPTION("ds2784 battery driver"); diff --git a/drivers/power/w1_ds2784.h b/drivers/power/w1_ds2784.h new file mode 100644 index 0000000000000..6822fc02fb852 --- /dev/null +++ b/drivers/power/w1_ds2784.h @@ -0,0 +1,132 @@ +/* drivers/w1/slaves/w1_ds2784.h + * + * Copyright (C) 2009 HTC Corporation + * Author: Justin Lin + * + * 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 __w1_ds2784_h__ +#define __w1_ds2784_h__ + + +/* Known commands to the DS2784 chip */ +#define W1_DS2784_SWAP 0xAA +#define W1_DS2784_READ_DATA 0x69 +#define W1_DS2784_WRITE_DATA 0x6C +#define W1_DS2784_COPY_DATA 0x48 +#define W1_DS2784_RECALL_DATA 0xB8 +#define W1_DS2784_LOCK 0x6A + +/* Number of valid register addresses */ +#define DS2784_DATA_SIZE 0x80 + +#define DS2784_EEPROM_BLOCK0 0x20 +#define DS2784_ACTIVE_FULL 0x20 +#define DS2784_EEPROM_BLOCK1 0x30 +#define DS2784_RATED_CAPACITY 0x32 +#define DS2784_CURRENT_OFFSET_BIAS 0x33 +#define DS2784_ACTIVE_EMPTY 0x3b + +/** + * The DS2482 registers - there are 3 registers that are addressed by a read + * pointer. The read pointer is set by the last command executed. + * + * To read the data, issue a register read for any address + */ +#define DS2482_CMD_RESET 0xF0 /* No param */ +#define DS2482_CMD_SET_READ_PTR 0xE1 /* Param: DS2482_PTR_CODE_xxx */ +#define DS2482_CMD_CHANNEL_SELECT 0xC3 +#define DS2482_CMD_WRITE_CONFIG 0xD2 /* Param: Config byte */ +#define DS2482_CMD_1WIRE_RESET 0xB4 /* Param: None */ +#define DS2482_CMD_1WIRE_SINGLE_BIT 0x87 /* Param: Bit byte (bit7) */ +#define DS2482_CMD_1WIRE_WRITE_BYTE 0xA5 /* Param: Data byte */ +#define DS2482_CMD_1WIRE_READ_BYTE 0x96 /* Param: None */ +/* Note to read the byte, Set the ReadPtr to Data then read (any addr) */ +#define DS2482_CMD_1WIRE_TRIPLET 0x78 /* Param: Dir byte (bit7) */ + +/* Values for DS2482_CMD_SET_READ_PTR */ +#define DS2482_PTR_CODE_STATUS 0xF0 +#define DS2482_PTR_CODE_DATA 0xE1 +#define DS2482_PTR_CODE_CHANNEL 0xD2 /* DS2482-800 only */ +#define DS2482_PTR_CODE_CONFIG 0xC3 + +/* +DS2784 1-wire slave memory map definitions +*/ +#define DS2784_REG_PORT 0x00 +#define DS2784_REG_STS 0x01 +#define DS2784_REG_RAAC_MSB 0x02 +#define DS2784_REG_RAAC_LSB 0x03 +#define DS2784_REG_RSAC_MSB 0x04 +#define DS2784_REG_RSAC_LSB 0x05 +#define DS2784_REG_RARC 0x06 +#define DS2784_REG_RSRC 0x07 +#define DS2784_REG_AVG_CURR_MSB 0x08 +#define DS2784_REG_AVG_CURR_LSB 0x09 +#define DS2784_REG_TEMP_MSB 0x0A +#define DS2784_REG_TEMP_LSB 0x0B +#define DS2784_REG_VOLT_MSB 0x0C +#define DS2784_REG_VOLT_LSB 0x0D +#define DS2784_REG_CURR_MSB 0x0E +#define DS2784_REG_CURR_LSB 0x0F +#define DS2784_REG_ACCUMULATE_CURR_MSB 0x10 +#define DS2784_REG_ACCUMULATE_CURR_LSB 0x11 +#define DS2784_REG_ACCUMULATE_CURR_LSB1 0x12 +#define DS2784_REG_ACCUMULATE_CURR_LSB2 0x13 +#define DS2784_REG_AGE_SCALAR 0x14 +#define DS2784_REG_SPECIALL_FEATURE 0x15 +#define DS2784_REG_FULL_MSB 0x16 +#define DS2784_REG_FULL_LSB 0x17 +#define DS2784_REG_ACTIVE_EMPTY_MSB 0x18 +#define DS2784_REG_ACTIVE_EMPTY_LSB 0x19 +#define DS2784_REG_STBY_EMPTY_MSB 0x1A +#define DS2784_REG_STBY_EMPTY_LSB 0x1B +#define DS2784_REG_EEPROM 0x1F +#define DS2784_REG_MFG_GAIN_RSGAIN_MSB 0xB0 +#define DS2784_REG_MFG_GAIN_RSGAIN_LSB 0xB1 + +#define DS2784_REG_CTRL 0x60 +#define DS2784_REG_ACCUMULATE_BIAS 0x61 +#define DS2784_REG_AGE_CAPA_MSB 0x62 +#define DS2784_REG_AGE_CAPA_LSB 0x63 +#define DS2784_REG_CHARGE_VOLT 0x64 +#define DS2784_REG_MIN_CHARGE_CURR 0x65 +#define DS2784_REG_ACTIVE_EMPTY_VOLT 0x66 +#define DS2784_REG_ACTIVE_EMPTY_CURR 0x67 +#define DS2784_REG_ACTIVE_EMPTY_40 0x68 +#define DS2784_REG_RSNSP 0x69 +#define DS2784_REG_FULL_40_MSB 0x6A +#define DS2784_REG_FULL_40_LSB 0x6B +#define DS2784_REG_FULL_SEG_4_SLOPE 0x6C +#define DS2784_REG_FULL_SEG_3_SLOPE 0x6D +#define DS2784_REG_FULL_SEG_2_SLOPE 0x6E +#define DS2784_REG_FULL_SEG_1_SLOPE 0x6F +#define DS2784_REG_AE_SEG_4_SLOPE 0x70 +#define DS2784_REG_AE_SEG_3_SLOPE 0x71 +#define DS2784_REG_AE_SEG_2_SLOPE 0x72 +#define DS2784_REG_AE_SEG_1_SLOPE 0x73 +#define DS2784_REG_SE_SEG_4_SLOPE 0x74 +#define DS2784_REG_SE_SEG_3_SLOPE 0x75 +#define DS2784_REG_SE_SEG_2_SLOPE 0x76 +#define DS2784_REG_SE_SEG_1_SLOPE 0x77 +#define DS2784_REG_RSGAIN_MSB 0x78 +#define DS2784_REG_RSGAIN_LSB 0x79 +#define DS2784_REG_RSTC 0x7A +#define DS2784_REG_CURR_OFFSET_BIAS 0x7B +#define DS2784_REG_TBP34 0x7C +#define DS2784_REG_TBP23 0x7D +#define DS2784_REG_TBP12 0x7E +#define DS2784_REG_PROTECTOR_THRESHOLD 0x7F + +#define DS2784_REG_USER_EEPROM_20 0x20 + +#endif /* !__w1_ds2784__ */ diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h index f3b636d7cafe7..4c425c07db7f8 100644 --- a/drivers/w1/w1_family.h +++ b/drivers/w1/w1_family.h @@ -36,6 +36,7 @@ #define W1_THERM_DS18B20 0x28 #define W1_EEPROM_DS2431 0x2D #define W1_FAMILY_DS2760 0x30 +#define W1_FAMILY_DS2784 0x32 #define MAXNAMELEN 32 diff --git a/include/linux/ds2784_battery.h b/include/linux/ds2784_battery.h new file mode 100644 index 0000000000000..7ca10f6fc193e --- /dev/null +++ b/include/linux/ds2784_battery.h @@ -0,0 +1,29 @@ +/* include/linux/ds2784_battery.h + * + * Copyright (C) 2009 HTC Corporation + * Copyright (C) 2009 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_DS2784_BATTERY_H +#define __LINUX_DS2784_BATTERY_H + +#ifdef __KERNEL__ + +struct ds2784_platform_data { + int (*charge)(int on, int fast); + void *w1_slave; +}; + +#endif /* __KERNEL__ */ + +#endif /* __LINUX_DS2784_BATTERY_H */ From 78889523386d057bd7b5eda27958280022fffa9b Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Mon, 8 Feb 2010 20:09:03 -0800 Subject: [PATCH 0447/2556] audience1026: add audience a1026 noise cancelation chip i2c driver Signed-off-by: Brian Swetland audience 1026: driver update 1) Support control of A1026_CLK_EN pin if hardware platform provided. 2) With new A1026 firmware(M11.4.0.9_B1747_HTC_Passion), modify i2c commands set to Audience A1026 when mode changing. 3) No need to get Ack when issuing a suspend i2c command to A1026. 4) Add A1026 software reset function. Signed-off-by: Brian Swetland change A1026 driver latency as Audience's suggestion. Signed-off-by: Brian Swetland audience1026: add control of noise suppression algorithm Added an IOCTL to force the type of noise suppression algorithm. The requested algorithm will override the choice made otherwise according to current audio path configuration. Added camcorder record configuration using back microphone. Signed-off-by: Eric Laurent Implement 8 VR modes for different settings on A1026 chip. i2c: chips: a1026: Modify I2C commands of in-call receiver mode. Remove NS from internal mic recording mode. Change-Id: I19294b6988052c98bd10eefcd3cbae2411c67f4d Signed-off-by: Dima Zavin Audience A1026 VR modes i2c commands updated. (V1-1) Audience A1026: driver clean-up -- made checkpatch happy; -- renamed some functions for bettter naming consistency; -- cleaned up pr_xxx statements, included __func__ everywhere; -- combined all DIAG ioctls under ENABLE_DIAG_IOCTLS (disabled by default); -- replaced all mdelay()s over 10ms with msleep(); -- cleaned up a1026_bootup_init(): execute_cmdmsg() was copied-and-pased there instead of being called directly; -- include/linux/a1026.h: combined kernel specifics under __KERNEL__, left; only the ioctl definitions exposed to userspace. Signed-off-by: Iliyan Malchev a1026: change digital gain of VR receiver modes to -3dB. Signed-off-by: Brian Swetland a1026: Update i2c commands for back Mic recording function. Signed-off-by: Brian Swetland a1026: latest I2C commands for all modes from Audience. Signed-off-by: Brian Swetland a1026: various improvements 1. Support A1026 i2c block write/read mechanism to shorten latency of a mode change. 2. Update some timing fine tune suggested by Audience. 3. When read back a command ACK, checking its 1st and 2nd byte is enough. Because in the case of get information cmd, the 3rd and 4th bytes can be different. Signed-off-by: Brian Swetland a1026: update to latest commands. - for internal mic and external mic reocrding, change DigitalOutputGain to 0dB. - for back mic recording, enable AGC and disable NS, also adds 6dB on DigitalOutputGain. - remove 2dB on DigitalOutputGain for phonecall receiver mode. Signed-off-by: Brian Swetland a1026: compensate the gains used by VR speaker and receiver modes for ADIE gain change. Signed-off-by: Brian Swetland a1026: Digital output gain of the A1026 from +15dB to 0dB for all VR modes. Signed-off-by: Paul Eastham a1026: add ioctl to write a 4-byte command w/o waiting for an ACK To be used for testing. Signed-off-by: Iliyan Malchev a1026: implement in-call TTY mode Signed-off-by: Iliyan Malchev --- drivers/i2c/chips/Kconfig | 6 + drivers/i2c/chips/Makefile | 1 + drivers/i2c/chips/a1026.c | 1144 ++++++++++++++++++++++++++++++++++++ include/linux/a1026.h | 234 ++++++++ 4 files changed, 1385 insertions(+) create mode 100644 drivers/i2c/chips/a1026.c create mode 100644 include/linux/a1026.h diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index d9624838e3925..f63300040528c 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -19,4 +19,10 @@ config SENSORS_MT9T013 help MT9T013 Camera Driver implemented by HTC. +config VP_A1026 + tristate "A1026 Voice Processor Driver" + depends on I2C + help + A1026 Voice Processor Driver implemented by HTC. + endmenu diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index 8a5334778389f..30edf8a07439c 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SENSORS_AKM8976) += akm8976.o obj-$(CONFIG_SENSORS_MT9T013) += mt9t013.o +obj-$(CONFIG_VP_A1026) += a1026.o ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) EXTRA_CFLAGS += -DDEBUG diff --git a/drivers/i2c/chips/a1026.c b/drivers/i2c/chips/a1026.c new file mode 100644 index 0000000000000..1a747760e7a84 --- /dev/null +++ b/drivers/i2c/chips/a1026.c @@ -0,0 +1,1144 @@ +/* drivers/i2c/chips/a1026.c - a1026 voice processor driver + * + * Copyright (C) 2009 HTC Corporation. + * + * 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 + +#define DEBUG (0) +#define ENABLE_DIAG_IOCTLS (0) + +static struct i2c_client *this_client; +static struct a1026_platform_data *pdata; + +static int execute_cmdmsg(unsigned int); + +static struct mutex a1026_lock; +static int a1026_opened; +static int a1026_suspended; +static int control_a1026_clk; +static unsigned int a1026_NS_state = A1026_NS_STATE_AUTO; +static int a1026_current_config = A1026_PATH_SUSPEND; +static int a1026_param_ID; + +struct vp_ctxt { + unsigned char *data; + unsigned int img_size; +}; + +struct vp_ctxt the_vp; + +static int a1026_i2c_read(char *rxData, int length) +{ + int rc; + struct i2c_msg msgs[] = { + { + .addr = this_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = rxData, + }, + }; + + rc = i2c_transfer(this_client->adapter, msgs, 1); + if (rc < 0) { + pr_err("%s: transfer error %d\n", __func__, rc); + return rc; + } + +#if DEBUG + { + int i = 0; + for (i = 0; i < length; i++) + pr_info("%s: rx[%d] = %2x\n", __func__, i, rxData[i]); + } +#endif + + return 0; +} + +static int a1026_i2c_write(char *txData, int length) +{ + int rc; + struct i2c_msg msg[] = { + { + .addr = this_client->addr, + .flags = 0, + .len = length, + .buf = txData, + }, + }; + + rc = i2c_transfer(this_client->adapter, msg, 1); + if (rc < 0) { + pr_err("%s: transfer error %d\n", __func__, rc); + return rc; + } + +#if DEBUG + { + int i = 0; + for (i = 0; i < length; i++) + pr_info("%s: tx[%d] = %2x\n", __func__, i, txData[i]); + } +#endif + + return 0; +} + +static int a1026_open(struct inode *inode, struct file *file) +{ + int rc = 0; + struct vp_ctxt *vp = &the_vp; + + mutex_lock(&a1026_lock); + + if (a1026_opened) { + pr_err("%s: busy\n", __func__); + rc = -EBUSY; + goto done; + } + + file->private_data = vp; + vp->img_size = 0; + a1026_opened = 1; +done: + mutex_unlock(&a1026_lock); + return rc; +} + +static int a1026_release(struct inode *inode, struct file *file) +{ + mutex_lock(&a1026_lock); + a1026_opened = 0; + mutex_unlock(&a1026_lock); + + return 0; +} + +static void a1026_i2c_sw_reset(unsigned int reset_cmd) +{ + int rc = 0; + unsigned char msgbuf[4]; + + msgbuf[0] = (reset_cmd >> 24) & 0xFF; + msgbuf[1] = (reset_cmd >> 16) & 0xFF; + msgbuf[2] = (reset_cmd >> 8) & 0xFF; + msgbuf[3] = reset_cmd & 0xFF; + + pr_info("%s: %08x\n", __func__, reset_cmd); + + rc = a1026_i2c_write(msgbuf, 4); + if (!rc) + msleep(20); +} + +static ssize_t a1026_bootup_init(struct file *file, struct a1026img *img) +{ + struct vp_ctxt *vp = file->private_data; + int rc, pass = 0; + int remaining; + int retry = RETRY_CNT; + unsigned char *index; + char buf[2]; + + if (img->img_size > A1026_MAX_FW_SIZE) { + pr_err("%s: invalid a1026 image size %d\n", __func__, + img->img_size); + return -EINVAL; + } + + vp->data = kmalloc(img->img_size, GFP_KERNEL); + if (!vp->data) { + pr_err("%s: out of memory\n", __func__); + return -ENOMEM; + } + vp->img_size = img->img_size; + if (copy_from_user(vp->data, img->buf, img->img_size)) { + pr_err("%s: copy from user failed\n", __func__); + kfree(vp->data); + return -EFAULT; + } + + while (retry--) { + /* Reset A1026 chip */ + gpio_set_value(pdata->gpio_a1026_reset, 0); + + /* Enable A1026 clock */ + if (control_a1026_clk) + gpio_set_value(pdata->gpio_a1026_clk, 1); + mdelay(1); + + /* Take out of reset */ + gpio_set_value(pdata->gpio_a1026_reset, 1); + + msleep(50); /* Delay before send I2C command */ + + /* Boot Cmd to A1026 */ + buf[0] = A1026_msg_BOOT >> 8; + buf[1] = A1026_msg_BOOT & 0xff; + + rc = a1026_i2c_write(buf, 2); + if (rc < 0) { + pr_err("%s: set boot mode error (%d retries left)\n", + __func__, retry); + continue; + } + + mdelay(1); /* use polling */ + rc = a1026_i2c_read(buf, 1); + if (rc < 0) { + pr_err("%s: boot mode ack error (%d retries left)\n", + __func__, retry); + continue; + } + + if (buf[0] != A1026_msg_BOOT_ACK) { + pr_err("%s: not a boot-mode ack (%d retries left)\n", + __func__, retry); + continue; + } + + remaining = vp->img_size / 32; + index = vp->data; + + pr_info("%s: starting to load image (%d passes)...\n", + __func__, + remaining + !!(vp->img_size % 32)); + + for (; remaining; remaining--, index += 32) { + rc = a1026_i2c_write(index, 32); + if (rc < 0) + break; + } + + if (rc >= 0 && vp->img_size % 32) + rc = a1026_i2c_write(index, vp->img_size % 32); + + if (rc < 0) { + pr_err("%s: fw load error %d (%d retries left)\n", + __func__, rc, retry); + continue; + } + + msleep(20); /* Delay time before issue a Sync Cmd */ + + pr_info("%s: firmware loaded successfully\n", __func__); + + rc = execute_cmdmsg(A100_msg_Sync); + if (rc < 0) { + pr_err("%s: sync command error %d (%d retries left)\n", + __func__, rc, retry); + continue; + } + + pass = 1; + break; + } + + /* Put A1026 into sleep mode */ + rc = execute_cmdmsg(A100_msg_Sleep); + if (rc < 0) { + pr_err("%s: suspend error\n", __func__); + goto set_suspend_err; + } + + a1026_suspended = 1; + a1026_current_config = A1026_PATH_SUSPEND; + + msleep(120); + /* Disable A1026 clock */ + if (control_a1026_clk) + gpio_set_value(pdata->gpio_a1026_clk, 0); + +set_suspend_err: + if (pass && !rc) + pr_info("%s: initialized!\n", __func__); + else + pr_err("%s: initialization failed\n", __func__); + + kfree(vp->data); + return rc; +} + +unsigned char phonecall_receiver[] = { + 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */ + 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:2-mic Close Talk (CT) */ + 0x80,0x1C,0x00,0x01, /* VoiceProcessingOn, 0x0001:Yes */ + 0x80,0x17,0x00,0x1A, /* SetAlgorithmParmID, 0x001A:Use ComfortNoise */ + 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */ + 0x80,0x17,0x00,0x04, /* SetAlgorithmParmID, 0x0004:Use AGC */ + 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */ + 0x80,0x17,0x00,0x00, /* SetAlgorithmParmID, 0x0000:Suppression Strength */ + 0x80,0x18,0x00,0x05, /* SetAlgorithmParm, 0x0005:25dB Max Suppression */ + 0x80,0x1B,0x00,0x0C, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x0C:(12 dB) */ + 0x80,0x1B,0x01,0x0C, /* SetDigitalInputGain, 0x01:Secondary Mic (Tx), 0x0C:(12 dB) */ + 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */ +}; + +unsigned char phonecall_headset[] = { + 0x80,0x26,0x00,0x15, /* SelectRouting, 0x0015:Snk,Pri,Snk,Snk - Csp,Zro,Zro (none) */ + 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */ + 0x80,0x1B,0x00,0x12, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x12:(18 dB) */ + 0x80,0x15,0x00,0xF8, /* SetDigitalOutputGain, 0x00:Tx, 0xF8:(-8 dB) */ +}; + +unsigned char phonecall_speaker[] = { + 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */ + 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002:1-mic Desktop/Vehicle (DV) */ + 0x80,0x1C,0x00,0x01, /* VoiceProcessingOn, 0x0001:Yes */ + 0x80,0x17,0x00,0x00, /* SetAlgorithmParmID, 0x0000:Suppression Strength */ + 0x80,0x18,0x00,0x04, /* SetAlgorithmParm, 0x0004:20dB Max Suppression */ + 0x80,0x17,0x00,0x04, /* SetAlgorithmParmID, 0x0004:Use AGC */ + 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */ + 0x80,0x17,0x00,0x1A, /* SetAlgorithmParmID, 0x001A:Use ComfortNoise */ + 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */ + 0x80,0x1B,0x00,0x12, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x12:(18 dB) */ + 0x80,0x15,0x00,0xFD, /* SetDigitalOutputGain, 0x00:Tx, 0xFD:(-3 dB) */ +}; + +unsigned char phonecall_bt[] = { + 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */ + 0x80,0x18,0x00,0x03, /* SetAlgorithmParm, 0x0003:1-mic External (MD) */ + 0x80,0x26,0x00,0x06, /* SelectRouting, 0x0006:Snk,Snk,Fei,Pri - Zro,Csp,Feo (PCM0->PCM1+ADCs) */ + 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */ + 0x80,0x1B,0x00,0x00, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x00:(0 dB) */ + 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */ +}; + +unsigned char phonecall_tty[] = { + 0x80,0x26,0x00,0x15, /* SelectRouting, 0x0015:Snk,Pri,Snk,Snk - Csp,Zro,Zro (none) */ + 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */ + 0x80,0x1B,0x00,0x00, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x00:(0 dB) */ + 0x80,0x15,0x00,0xFB, /* SetDigitalOutputGain, 0x00:Tx, 0xFB:(-5 dB) */ +}; + +unsigned char INT_MIC_recording_receiver[] = { + 0x80,0x26,0x00,0x07, /* SelectRouting, 0x0007:Pri,Snk,Snk,Snk - Csp,Zro,Zro (none) */ + 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */ + 0x80,0x1B,0x00,0x12, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x12:(18 dB) */ + 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */ +}; + +unsigned char EXT_MIC_recording[] = { + 0x80,0x26,0x00,0x15, /* SelectRouting, 0x0015:Snk,Pri,Snk,Snk - Csp,Zro,Zro (none) */ + 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */ + 0x80,0x1B,0x00,0x12, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x12:(18 dB) */ + 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */ +}; + +unsigned char INT_MIC_recording_speaker[] = { + 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */ + 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002:1-mic Desktop/Vehicle (DV) */ + 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */ + 0x80,0x1B,0x00,0x12, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x12:(18 dB) */ + 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */ +}; + +unsigned char BACK_MIC_recording[] = { + 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */ + 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002:1-mic Desktop/Vehicle (DV) */ + 0x80,0x26,0x00,0x15, /* SelectRouting, 0x0015:Snk,Pri,Snk,Snk - Csp,Zro,Zro (none) */ + 0x80,0x1C,0x00,0x01, /* VoiceProcessingOn, 0x0001:Yes */ + 0x80,0x17,0x00,0x04, /* SetAlgorithmParmID, 0x0004:Use AGC */ + 0x80,0x18,0x00,0x01, /* SetAlgorithmParm, 0x0001:Yes */ + 0x80,0x17,0x00,0x1A, /* SetAlgorithmParmID, 0x001A:Use ComfortNoise */ + 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */ + 0x80,0x17,0x00,0x00, /* SetAlgorithmParmID, 0x0000:Suppression Strength */ + 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No Suppression */ + 0x80,0x1B,0x00,0x12, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x12:(18 dB) */ + 0x80,0x15,0x00,0x06, /* SetDigitalOutputGain, 0x00:Tx, 0x06:(6 dB) */ +}; + +unsigned char vr_no_ns_receiver[] = { + 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */ + 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:2-mic Close Talk (CT) */ + 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */ + 0x80,0x1B,0x00,0x0C, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x0C:(12 dB) */ + 0x80,0x1B,0x01,0x0C, /* SetDigitalInputGain, 0x01:Secondary Mic (Tx), 0x09:(12 dB) */ + 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */ +}; + +unsigned char vr_no_ns_headset[] = { + 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */ + 0x80,0x18,0x00,0x03, /* SetAlgorithmParm, 0x0003:1M-DG (1-mic digital input) */ + 0x80,0x26,0x00,0x15, /* SelectRouting, 0x0015:Snk,Pri,Snk,Snk - Csp,Zro,Zro (none) */ + 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */ + 0x80,0x1B,0x00,0x12, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x12:(18 dB) */ + 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */ +}; + +unsigned char vr_no_ns_speaker[] = { + 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */ + 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002:1-mic Desktop/Vehicle (DV) */ + 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */ + 0x80,0x1B,0x00,0x0C, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x0C:(12 dB) */ + 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */ +}; + +unsigned char vr_no_ns_bt[] = { + 0x80,0x26,0x00,0x06, /* SelectRouting, 0x0006:Snk,Snk,Fei,Pri - Zro,Csp,Feo (PCM0->PCM1+ADCs) */ + 0x80,0x1C,0x00,0x00, /* VoiceProcessingOn, 0x0000:No */ + 0x80,0x1B,0x00,0x00, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x00:(0 dB) */ + 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */ +}; + +unsigned char vr_ns_receiver[] = { + 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */ + 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:2-mic Close Talk (CT) */ + 0x80,0x1C,0x00,0x01, /* VoiceProcessingOn, 0x0001:Yes */ + 0x80,0x17,0x00,0x1A, /* SetAlgorithmParmID, 0x001A:Use ComfortNoise */ + 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */ + 0x80,0x17,0x00,0x04, /* SetAlgorithmParmID, 0x0004:Use AGC */ + 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */ + 0x80,0x17,0x00,0x00, /* SetAlgorithmParmID, 0x0000:Suppression Strength */ + 0x80,0x18,0x00,0x04, /* SetAlgorithmParm, 0x0004:20dB Max Suppression */ + 0x80,0x1B,0x00,0x0C, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x0C:(12 dB) */ + 0x80,0x1B,0x01,0x0C, /* SetDigitalInputGain, 0x01:Secondary Mic (Tx), 0x0C:(12 dB) */ + 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */ +}; + +unsigned char vr_ns_headset[] = { + 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */ + 0x80,0x18,0x00,0x03, /* SetAlgorithmParm, 0x0003:1-mic External (MD) */ + 0x80,0x26,0x00,0x15, /* SelectRouting, 0x0015:Snk,Pri,Snk,Snk - Csp,Zro,Zro (none) */ + 0x80,0x1C,0x00,0x01, /* VoiceProcessingOn, 0x0001:Yes */ + 0x80,0x17,0x00,0x00, /* SetAlgorithmParmID, 0x0000:Suppression Strength */ + 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002:20dB Max Suppression */ + 0x80,0x17,0x00,0x1A, /* SetAlgorithmParmID, 0x001A:Use ComfortNoise */ + 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */ + 0x80,0x17,0x00,0x04, /* SetAlgorithmParmID, 0x0004:Use AGC */ + 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */ + 0x80,0x1B,0x00,0x12, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x12:(18 dB) */ + 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */ +}; + +unsigned char vr_ns_speaker[] = { + 0x80,0x17,0x00,0x02, /* SetAlgorithmParmID, 0x0002:Microphone Configuration */ + 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002:1-mic Desktop/Vehicle (DV) */ + 0x80,0x1C,0x00,0x01, /* VoiceProcessingOn, 0x0001:Yes */ + 0x80,0x17,0x00,0x00, /* SetAlgorithmParmID, 0x0000:Suppression Strength */ + 0x80,0x18,0x00,0x04, /* SetAlgorithmParm, 0x0004:20dB Max Suppression */ + 0x80,0x17,0x00,0x04, /* SetAlgorithmParmID, 0x0004:Use AGC */ + 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */ + 0x80,0x17,0x00,0x1A, /* SetAlgorithmParmID, 0x001A:Use ComfortNoise */ + 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */ + 0x80,0x1B,0x00,0x0C, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x0C:(12 dB) */ + 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */ +}; + +unsigned char vr_ns_bt[] = { + 0x80,0x26,0x00,0x06, /* SelectRouting, 0x0006:Snk,Snk,Fei,Pri - Zro,Csp,Feo (PCM0->PCM1+ADCs) */ + 0x80,0x1C,0x00,0x01, /* VoiceProcessingOn, 0x0001:Yes */ + 0x80,0x17,0x00,0x00, /* SetAlgorithmParmID, 0x0000:Suppression Strength */ + 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002:20dB Max Suppression */ + 0x80,0x17,0x00,0x04, /* SetAlgorithmParmID, 0x0004:Use AGC */ + 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */ + 0x80,0x17,0x00,0x1A, /* SetAlgorithmParmID, 0x001A:Use ComfortNoise */ + 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */ + 0x80,0x1B,0x00,0x00, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x00:(0 dB) */ + 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */ +}; + +unsigned char suspend_mode[] = { + 0x80,0x10,0x00,0x01 +}; + +static ssize_t chk_wakeup_a1026(void) +{ + int rc = 0, retry = 3; + + if (a1026_suspended == 1) { + /* Enable A1026 clock */ + if (control_a1026_clk) { + gpio_set_value(pdata->gpio_a1026_clk, 1); + mdelay(1); + } + + gpio_set_value(pdata->gpio_a1026_wakeup, 0); + msleep(120); + + do { + rc = execute_cmdmsg(A100_msg_Sync); + } while ((rc < 0) && --retry); + + gpio_set_value(pdata->gpio_a1026_wakeup, 1); + if (rc < 0) { + pr_err("%s: failed (%d)\n", __func__, rc); + goto wakeup_sync_err; + } + + a1026_suspended = 0; + } +wakeup_sync_err: + return rc; +} + +/* Filter commands according to noise suppression state forced by + * A1026_SET_NS_STATE ioctl. + * + * For this function to operate properly, all configurations must include + * both A100_msg_Bypass and Mic_Config commands even if default values + * are selected or if Mic_Config is useless because VP is off + */ +int a1026_filter_vp_cmd(int cmd, int mode) +{ + int msg = (cmd >> 16) & 0xFFFF; + int filtered_cmd = cmd; + + if (a1026_NS_state == A1026_NS_STATE_AUTO) + return cmd; + + switch (msg) { + case A100_msg_Bypass: + if (a1026_NS_state == A1026_NS_STATE_OFF) + filtered_cmd = A1026_msg_VP_OFF; + else + filtered_cmd = A1026_msg_VP_ON; + break; + case A100_msg_SetAlgorithmParmID: + a1026_param_ID = cmd & 0xFFFF; + break; + case A100_msg_SetAlgorithmParm: + if (a1026_param_ID == Mic_Config) { + if (a1026_NS_state == A1026_NS_STATE_CT) + filtered_cmd = (msg << 16); + else if (a1026_NS_state == A1026_NS_STATE_FT) + filtered_cmd = (msg << 16) | 0x0002; + } + break; + default: + if (mode == A1026_CONFIG_VP) + filtered_cmd = -1; + break; + } + + pr_info("%s: %x filtered = %x, a1026_NS_state %d, mode %d\n", __func__, + cmd, filtered_cmd, a1026_NS_state, mode); + + return filtered_cmd; +} + +int a1026_set_config(char newid, int mode) +{ + int i = 0, rc = 0, size = 0; + int number_of_cmd_sets, rd_retry_cnt; + unsigned int sw_reset = 0; + unsigned char *i2c_cmds; + unsigned char *index = 0; + unsigned char ack_buf[A1026_CMD_FIFO_DEPTH * 4]; + unsigned char rdbuf[4]; + + if ((a1026_suspended) && (newid == A1026_PATH_SUSPEND)) + return rc; + + rc = chk_wakeup_a1026(); + if (rc < 0) + return rc; + + sw_reset = ((A100_msg_Reset << 16) | RESET_IMMEDIATE); + + switch (newid) { + case A1026_PATH_INCALL_RECEIVER: + gpio_set_value(pdata->gpio_a1026_micsel, 0); + i2c_cmds = phonecall_receiver; + size = sizeof(phonecall_receiver); + break; + case A1026_PATH_INCALL_HEADSET: + gpio_set_value(pdata->gpio_a1026_micsel, 1); + i2c_cmds = phonecall_headset; + size = sizeof(phonecall_headset); + break; + case A1026_PATH_INCALL_SPEAKER: + gpio_set_value(pdata->gpio_a1026_micsel, 0); + i2c_cmds = phonecall_speaker; + size = sizeof(phonecall_speaker); + break; + case A1026_PATH_INCALL_BT: + gpio_set_value(pdata->gpio_a1026_micsel, 0); + i2c_cmds = phonecall_bt; + size = sizeof(phonecall_bt); + break; + case A1026_PATH_INCALL_TTY: + gpio_set_value(pdata->gpio_a1026_micsel, 1); + i2c_cmds = phonecall_tty; + size = sizeof(phonecall_tty); + break; + case A1026_PATH_VR_NO_NS_RECEIVER: + gpio_set_value(pdata->gpio_a1026_micsel, 0); + i2c_cmds = vr_no_ns_receiver; + size = sizeof(vr_no_ns_receiver); + break; + case A1026_PATH_VR_NO_NS_HEADSET: + gpio_set_value(pdata->gpio_a1026_micsel, 1); + i2c_cmds = vr_no_ns_headset; + size = sizeof(vr_no_ns_headset); + break; + case A1026_PATH_VR_NO_NS_SPEAKER: + gpio_set_value(pdata->gpio_a1026_micsel, 0); + i2c_cmds = vr_no_ns_speaker; + size = sizeof(vr_no_ns_speaker); + break; + case A1026_PATH_VR_NO_NS_BT: + gpio_set_value(pdata->gpio_a1026_micsel, 0); + i2c_cmds = vr_no_ns_bt; + size = sizeof(vr_no_ns_bt); + break; + case A1026_PATH_VR_NS_RECEIVER: + gpio_set_value(pdata->gpio_a1026_micsel, 0); + i2c_cmds = vr_ns_receiver; + size = sizeof(vr_ns_receiver); + break; + case A1026_PATH_VR_NS_HEADSET: + gpio_set_value(pdata->gpio_a1026_micsel, 1); + i2c_cmds = vr_ns_headset; + size = sizeof(vr_ns_headset); + break; + case A1026_PATH_VR_NS_SPEAKER: + gpio_set_value(pdata->gpio_a1026_micsel, 0); + i2c_cmds = vr_ns_speaker; + size = sizeof(vr_ns_speaker); + break; + case A1026_PATH_VR_NS_BT: + gpio_set_value(pdata->gpio_a1026_micsel, 0); + i2c_cmds = vr_ns_bt; + size = sizeof(vr_ns_bt); + break; + case A1026_PATH_RECORD_RECEIVER: + gpio_set_value(pdata->gpio_a1026_micsel, 0); + i2c_cmds = INT_MIC_recording_receiver; + size = sizeof(INT_MIC_recording_receiver); + break; + case A1026_PATH_RECORD_HEADSET: + gpio_set_value(pdata->gpio_a1026_micsel, 1); + i2c_cmds = EXT_MIC_recording; + size = sizeof(EXT_MIC_recording); + break; + case A1026_PATH_RECORD_SPEAKER: + gpio_set_value(pdata->gpio_a1026_micsel, 0); + i2c_cmds = INT_MIC_recording_speaker; + size = sizeof(INT_MIC_recording_speaker); + break; + case A1026_PATH_RECORD_BT: + gpio_set_value(pdata->gpio_a1026_micsel, 0); + i2c_cmds = phonecall_bt; + size = sizeof(phonecall_bt); + break; + case A1026_PATH_SUSPEND: + gpio_set_value(pdata->gpio_a1026_micsel, 0); + i2c_cmds = (unsigned char *)suspend_mode; + size = sizeof(suspend_mode); + break; + case A1026_PATH_CAMCORDER: + gpio_set_value(pdata->gpio_a1026_micsel, 0); + i2c_cmds = BACK_MIC_recording; + size = sizeof(BACK_MIC_recording); + break; + default: + pr_err("%s: invalid cmd %d\n", __func__, newid); + rc = -1; + goto input_err; + break; + } + + a1026_current_config = newid; + pr_info("%s: change to mode %d\n", __func__, newid); + + pr_info("%s: block write start (size = %d)\n", __func__, size); +#if DEBUG + for (i = 1; i <= size; i++) { + pr_info("%x ", *(i2c_cmds + i - 1)); + if ( !(i % 4)) + pr_info("\n"); + } +#endif + + rc = a1026_i2c_write(i2c_cmds, size); + if (rc < 0) { + pr_err("A1026 CMD block write error!\n"); + a1026_i2c_sw_reset(sw_reset); + return rc; + } + pr_info("%s: block write end\n", __func__); + + /* Don't need to get Ack after sending out a suspend command */ + if (*i2c_cmds == 0x80 && *(i2c_cmds + 1) == 0x10 + && *(i2c_cmds + 2) == 0x00 && *(i2c_cmds + 3) == 0x01) { + a1026_suspended = 1; + /* Disable A1026 clock */ + msleep(120); + if (control_a1026_clk) + gpio_set_value(pdata->gpio_a1026_clk, 0); + return rc; + } + + memset(ack_buf, 0, sizeof(ack_buf)); + msleep(20); + pr_info("%s: CMD ACK block read start\n", __func__); + rc = a1026_i2c_read(ack_buf, size); + if (rc < 0) { + pr_err("%s: CMD ACK block read error\n", __func__); + a1026_i2c_sw_reset(sw_reset); + return rc; + } else { + pr_info("%s: CMD ACK block read end\n", __func__); +#if DEBUG + for (i = 1; i <= size; i++) { + pr_info("%x ", ack_buf[i-1]); + if ( !(i % 4)) + pr_info("\n"); + } +#endif + index = ack_buf; + number_of_cmd_sets = size / 4; + do { + if (*index == 0x00) { + rd_retry_cnt = POLLING_RETRY_CNT; +rd_retry: + if (rd_retry_cnt--) { + memset(rdbuf, 0, sizeof(rdbuf)); + rc = a1026_i2c_read(rdbuf, 4); + if (rc < 0) + return rc; +#if DEBUG + for (i = 0; i < sizeof(rdbuf); i++) { + pr_info("0x%x\n", rdbuf[i]); + } + pr_info("-----------------\n"); +#endif + if (rdbuf[0] == 0x00) { + msleep(20); + goto rd_retry; + } + } else { + pr_err("%s: CMD ACK Not Ready\n", + __func__); + return -EBUSY; + } + } else if (*index == 0xff) { /* illegal cmd */ + return -ENOEXEC; + } else if (*index == 0x80) { + index += 4; + } + } while (--number_of_cmd_sets); + } +input_err: + return rc; +} + +int execute_cmdmsg(unsigned int msg) +{ + int rc = 0; + int retries, pass = 0; + unsigned char msgbuf[4]; + unsigned char chkbuf[4]; + unsigned int sw_reset = 0; + + sw_reset = ((A100_msg_Reset << 16) | RESET_IMMEDIATE); + + msgbuf[0] = (msg >> 24) & 0xFF; + msgbuf[1] = (msg >> 16) & 0xFF; + msgbuf[2] = (msg >> 8) & 0xFF; + msgbuf[3] = msg & 0xFF; + + memcpy(chkbuf, msgbuf, 4); + + rc = a1026_i2c_write(msgbuf, 4); + if (rc < 0) { + pr_err("%s: error %d\n", __func__, rc); + a1026_i2c_sw_reset(sw_reset); + return rc; + } + + /* We don't need to get Ack after sending out a suspend command */ + if (msg == A100_msg_Sleep) + return rc; + + retries = POLLING_RETRY_CNT; + while (retries--) { + rc = 0; + + msleep(20); /* use polling */ + memset(msgbuf, 0, sizeof(msgbuf)); + rc = a1026_i2c_read(msgbuf, 4); + if (rc < 0) { + pr_err("%s: ack-read error %d (%d retries)\n", __func__, + rc, retries); + continue; + } + + if (msgbuf[0] == 0x80 && msgbuf[1] == chkbuf[1]) { + pass = 1; + break; + } else if (msgbuf[0] == 0xff && msgbuf[1] == 0xff) { + pr_err("%s: illegal cmd %08x\n", __func__, msg); + rc = -EINVAL; + break; + } else if ( msgbuf[0] == 0x00 && msgbuf[1] == 0x00 ) { + pr_info("%s: not ready (%d retries)\n", __func__, + retries); + rc = -EBUSY; + } else { + pr_info("%s: cmd/ack mismatch: (%d retries left)\n", + __func__, + retries); +#if DEBUG + pr_info("%s: msgbuf[0] = %x\n", __func__, msgbuf[0]); + pr_info("%s: msgbuf[1] = %x\n", __func__, msgbuf[1]); + pr_info("%s: msgbuf[2] = %x\n", __func__, msgbuf[2]); + pr_info("%s: msgbuf[3] = %x\n", __func__, msgbuf[3]); +#endif + rc = -EBUSY; + } + } + + if (!pass) { + pr_err("%s: failed execute cmd %08x (%d)\n", __func__, + msg, rc); + a1026_i2c_sw_reset(sw_reset); + } + return rc; +} + +#if ENABLE_DIAG_IOCTLS +static int a1026_set_mic_state(char miccase) +{ + int rc = 0; + unsigned int cmd_msg = 0; + + switch (miccase) { + case 1: /* Mic-1 ON / Mic-2 OFF */ + cmd_msg = 0x80260007; + break; + case 2: /* Mic-1 OFF / Mic-2 ON */ + cmd_msg = 0x80260015; + break; + case 3: /* both ON */ + cmd_msg = 0x80260001; + break; + case 4: /* both OFF */ + cmd_msg = 0x80260006; + break; + default: + pr_info("%s: invalid input %d\n", __func__, miccase); + rc = -EINVAL; + break; + } + rc = execute_cmdmsg(cmd_msg); + return rc; +} + +static int exe_cmd_in_file(unsigned char *incmd) +{ + int rc = 0; + int i = 0; + unsigned int cmd_msg = 0; + unsigned char tmp = 0; + + for (i = 0; i < 4; i++) { + tmp = *(incmd + i); + cmd_msg |= (unsigned int)tmp; + if (i != 3) + cmd_msg = cmd_msg << 8; + } + rc = execute_cmdmsg(cmd_msg); + if (rc < 0) + pr_err("%s: cmd %08x error %d\n", __func__, cmd_msg, rc); + return rc; +} +#endif /* ENABLE_DIAG_IOCTLS */ + +static int +a1026_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct a1026img img; + int rc = 0; +#if ENABLE_DIAG_IOCTLS + char msg[4]; + int mic_cases = 0; + int mic_sel = 0; +#endif + int pathid = 0; + unsigned int ns_state; + + switch (cmd) { + case A1026_BOOTUP_INIT: + img.buf = 0; + img.img_size = 0; + if (copy_from_user(&img, argp, sizeof(img))) + return -EFAULT; + rc = a1026_bootup_init(file, &img); + break; + case A1026_SET_CONFIG: + if (copy_from_user(&pathid, argp, sizeof(pathid))) + return -EFAULT; + rc = a1026_set_config(pathid, A1026_CONFIG_FULL); + if (rc < 0) + pr_err("%s: A1026_SET_CONFIG (%d) error %d!\n", + __func__, pathid, rc); + break; + case A1026_SET_NS_STATE: + if (copy_from_user(&ns_state, argp, sizeof(ns_state))) + return -EFAULT; + pr_info("%s: set noise suppression %d\n", __func__, ns_state); + if (ns_state < 0 || ns_state >= A1026_NS_NUM_STATES) + return -EINVAL; + a1026_NS_state = ns_state; + if (!a1026_suspended) + a1026_set_config(a1026_current_config, + A1026_CONFIG_VP); + break; +#if ENABLE_DIAG_IOCTLS + case A1026_SET_MIC_ONOFF: + rc = chk_wakeup_a1026(); + if (rc < 0) + return rc; + if (copy_from_user(&mic_cases, argp, sizeof(mic_cases))) + return -EFAULT; + rc = a1026_set_mic_state(mic_cases); + if (rc < 0) + pr_err("%s: A1026_SET_MIC_ONOFF %d error %d!\n", + __func__, mic_cases, rc); + break; + case A1026_SET_MICSEL_ONOFF: + rc = chk_wakeup_a1026(); + if (rc < 0) + return rc; + if (copy_from_user(&mic_sel, argp, sizeof(mic_sel))) + return -EFAULT; + gpio_set_value(pdata->gpio_a1026_micsel, !!mic_sel); + rc = 0; + break; + case A1026_READ_DATA: + rc = chk_wakeup_a1026(); + if (rc < 0) + return rc; + rc = a1026_i2c_read(msg, 4); + if (copy_to_user(argp, &msg, 4)) + return -EFAULT; + break; + case A1026_WRITE_MSG: + rc = chk_wakeup_a1026(); + if (rc < 0) + return rc; + if (copy_from_user(msg, argp, sizeof(msg))) + return -EFAULT; + rc = a1026_i2c_write(msg, 4); + break; + case A1026_SYNC_CMD: + rc = chk_wakeup_a1026(); + if (rc < 0) + return rc; + msg[0] = 0x80; + msg[1] = 0x00; + msg[2] = 0x00; + msg[3] = 0x00; + rc = a1026_i2c_write(msg, 4); + break; + case A1026_SET_CMD_FILE: + rc = chk_wakeup_a1026(); + if (rc < 0) + return rc; + if (copy_from_user(msg, argp, sizeof(msg))) + return -EFAULT; + rc = exe_cmd_in_file(msg); + break; +#endif /* ENABLE_DIAG_IOCTLS */ + default: + pr_err("%s: invalid command %d\n", __func__, _IOC_NR(cmd)); + rc = -EINVAL; + break; + } + + return rc; +} + +static const struct file_operations a1026_fops = { + .owner = THIS_MODULE, + .open = a1026_open, + .release = a1026_release, + .ioctl = a1026_ioctl, +}; + +static struct miscdevice a1026_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "audience_a1026", + .fops = &a1026_fops, +}; + +static int a1026_probe( + struct i2c_client *client, const struct i2c_device_id *id) +{ + int rc = 0; + + pdata = client->dev.platform_data; + + if (pdata == NULL) { + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (pdata == NULL) { + rc = -ENOMEM; + pr_err("%s: platform data is NULL\n", __func__); + goto err_alloc_data_failed; + } + } + + this_client = client; + + rc = gpio_request(pdata->gpio_a1026_clk, "a1026"); + if (rc < 0) { + control_a1026_clk = 0; + goto chk_gpio_micsel; + } + control_a1026_clk = 1; + + rc = gpio_direction_output(pdata->gpio_a1026_clk, 1); + if (rc < 0) { + pr_err("%s: request clk gpio direction failed\n", __func__); + goto err_free_gpio_clk; + } + +chk_gpio_micsel: + rc = gpio_request(pdata->gpio_a1026_micsel, "a1026"); + if (rc < 0) { + pr_err("%s: gpio request mic_sel pin failed\n", __func__); + goto err_free_gpio_micsel; + } + + rc = gpio_direction_output(pdata->gpio_a1026_micsel, 1); + if (rc < 0) { + pr_err("%s: request mic_sel gpio direction failed\n", __func__); + goto err_free_gpio_micsel; + } + + rc = gpio_request(pdata->gpio_a1026_wakeup, "a1026"); + if (rc < 0) { + pr_err("%s: gpio request wakeup pin failed\n", __func__); + goto err_free_gpio; + } + + rc = gpio_direction_output(pdata->gpio_a1026_wakeup, 1); + if (rc < 0) { + pr_err("%s: request wakeup gpio direction failed\n", __func__); + goto err_free_gpio; + } + + rc = gpio_request(pdata->gpio_a1026_reset, "a1026"); + if (rc < 0) { + pr_err("%s: gpio request reset pin failed\n", __func__); + goto err_free_gpio; + } + + rc = gpio_direction_output(pdata->gpio_a1026_reset, 1); + if (rc < 0) { + pr_err("%s: request reset gpio direction failed\n", __func__); + goto err_free_gpio_all; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s: i2c check functionality error\n", __func__); + rc = -ENODEV; + goto err_free_gpio_all; + } + + if (control_a1026_clk) + gpio_set_value(pdata->gpio_a1026_clk, 1); + gpio_set_value(pdata->gpio_a1026_micsel, 0); + gpio_set_value(pdata->gpio_a1026_wakeup, 1); + gpio_set_value(pdata->gpio_a1026_reset, 1); + + rc = misc_register(&a1026_device); + if (rc) { + pr_err("%s: a1026_device register failed\n", __func__); + goto err_free_gpio_all; + } + + return 0; + +err_free_gpio_all: + gpio_free(pdata->gpio_a1026_reset); +err_free_gpio: + gpio_free(pdata->gpio_a1026_wakeup); +err_free_gpio_micsel: + gpio_free(pdata->gpio_a1026_micsel); +err_free_gpio_clk: + if (control_a1026_clk) + gpio_free(pdata->gpio_a1026_clk); +err_alloc_data_failed: + return rc; +} + +static int a1026_remove(struct i2c_client *client) +{ + struct a1026_platform_data *p1026data = i2c_get_clientdata(client); + kfree(p1026data); + + return 0; +} + +static int a1026_suspend(struct i2c_client *client, pm_message_t mesg) +{ + return 0; +} + +static int a1026_resume(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id a1026_id[] = { + { "audience_a1026", 0 }, + { } +}; + +static struct i2c_driver a1026_driver = { + .probe = a1026_probe, + .remove = a1026_remove, + .suspend = a1026_suspend, + .resume = a1026_resume, + .id_table = a1026_id, + .driver = { + .name = "audience_a1026", + }, +}; + +static int __init a1026_init(void) +{ + pr_info("%s\n", __func__); + mutex_init(&a1026_lock); + + return i2c_add_driver(&a1026_driver); +} + +static void __exit a1026_exit(void) +{ + i2c_del_driver(&a1026_driver); +} + +module_init(a1026_init); +module_exit(a1026_exit); + +MODULE_DESCRIPTION("A1026 voice processor driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/a1026.h b/include/linux/a1026.h new file mode 100644 index 0000000000000..44cdf4d623964 --- /dev/null +++ b/include/linux/a1026.h @@ -0,0 +1,234 @@ +/* include/linux/a1026.h - a1026 voice processor driver + * + * Copyright (C) 2009 HTC Corporation. + * + * 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_A1026_H +#define __LINUX_A1026_H + +#include + +#define A1026_MAX_FW_SIZE (32*1024) +struct a1026img { + unsigned char *buf; + unsigned img_size; +}; + +enum A1026_PathID { + A1026_PATH_SUSPEND, + A1026_PATH_INCALL_RECEIVER, + A1026_PATH_INCALL_HEADSET, + A1026_PATH_INCALL_SPEAKER, + A1026_PATH_INCALL_BT, + A1026_PATH_VR_NO_NS_RECEIVER, + A1026_PATH_VR_NO_NS_HEADSET, + A1026_PATH_VR_NO_NS_SPEAKER, + A1026_PATH_VR_NO_NS_BT, + A1026_PATH_VR_NS_RECEIVER, + A1026_PATH_VR_NS_HEADSET, + A1026_PATH_VR_NS_SPEAKER, + A1026_PATH_VR_NS_BT, + A1026_PATH_RECORD_RECEIVER, + A1026_PATH_RECORD_HEADSET, + A1026_PATH_RECORD_SPEAKER, + A1026_PATH_RECORD_BT, + A1026_PATH_CAMCORDER, + A1026_PATH_INCALL_TTY +}; + +/* noise suppression states */ +enum A1026_NS_states { + A1026_NS_STATE_AUTO, /* leave mode as selected by driver */ + A1026_NS_STATE_OFF, /* disable noise suppression */ + A1026_NS_STATE_CT, /* force close talk mode */ + A1026_NS_STATE_FT, /* force far talk mode */ + A1026_NS_NUM_STATES +}; + +/* indicates if a1026_set_config() performs a full configuration or only + * a voice processing algorithm configuration */ +/* IOCTLs for Audience A1026 */ +#define A1026_IOCTL_MAGIC 'u' + +#define A1026_BOOTUP_INIT _IOW(A1026_IOCTL_MAGIC, 0x01, struct a1026img *) +#define A1026_SET_CONFIG _IOW(A1026_IOCTL_MAGIC, 0x02, enum A1026_PathID) +#define A1026_SET_NS_STATE _IOW(A1026_IOCTL_MAGIC, 0x03, enum A1026_NS_states) + +/* For Diag */ +#define A1026_SET_MIC_ONOFF _IOW(A1026_IOCTL_MAGIC, 0x50, unsigned) +#define A1026_SET_MICSEL_ONOFF _IOW(A1026_IOCTL_MAGIC, 0x51, unsigned) +#define A1026_READ_DATA _IOR(A1026_IOCTL_MAGIC, 0x52, unsigned) +#define A1026_WRITE_MSG _IOW(A1026_IOCTL_MAGIC, 0x53, unsigned) +#define A1026_SYNC_CMD _IO(A1026_IOCTL_MAGIC, 0x54) +#define A1026_SET_CMD_FILE _IOW(A1026_IOCTL_MAGIC, 0x55, unsigned) + +#ifdef __KERNEL__ + +/* A1026 Command codes */ +#define CtrlMode_LAL 0x0001 /* Level Active Low */ +#define CtrlMode_LAH 0x0002 /* Level Active High */ +#define CtrlMode_FE 0x0003 /* Falling Edge */ +#define CtrlMode_RE 0x0004 /* Rising Edge */ +#define A100_msg_Sync 0x80000000 +#define A100_msg_Sync_Ack 0x80000000 + +#define A100_msg_Reset 0x8002 +#define RESET_IMMEDIATE 0x0000 +#define RESET_DELAYED 0x0001 + +#define A100_msg_BootloadInitiate 0x8003 +#define A100_msg_GetDeviceParm 0x800B +#define A100_msg_SetDeviceParmID 0x800C +#define A100_msg_SetDeviceParm 0x800D + +/* Get/Set PCM Device Parameter ID List */ +/* PCM-0 */ +#define PCM0WordLength 0x0100 +#define PCM0DelFromFsTx 0x0101 +#define PCM0DelFromFsRx 0x0102 +#define PCM0LatchEdge 0x0103 +#define PCM0Endianness 0x0105 +#define PCM0TristateEnable 0x0107 + +/* PCM-1 */ +#define PCM1WordLength 0x0200 +#define PCM1DelFromFsTx 0x0201 +#define PCM1DelFromFsRx 0x0202 +#define PCM1LatchEdge 0x0203 +#define PCM1Endianness 0x0205 +#define PCM1TristateEnable 0x0207 + +/* Possible setting values for PCM I/F */ +#define PCMWordLength_16bit 0x10 /* Default */ +#define PCMWordLength_24bit 0x18 +#define PCMWordLength_32bit 0x20 +#define PCMLatchEdge_Tx_F_Rx_R 0x00 /* Tx/Rx on falling/rising edge */ +#define PCMLatchEdge_Tx_R_Rx_F 0x03 /* Tx/Rx on falling/rising edge */ +#define PCMEndianness_Little 0x00 +#define PCMEndianness_Big 0x01 /* Default */ +#define PCMTristate_Disable 0x00 /* Default */ +#define PCMTristate_Enable 0x01 + +/* Get/Set ADC Device Parameter ID List */ +/* ADC-0 */ +#define ADC0Gain 0x0300 +#define ADC0Rate 0x0301 +#define ADC0CutoffFreq 0x0302 + +/* ADC-1 */ +#define ADC1Gain 0x0400 +#define ADC1Rate 0x0401 +#define ADC1CutoffFreq 0x0402 + +/* Possible setting values for ADC I/F */ +#define ADC_Gain_0db 0x00 +#define ADC_Gain_6db 0x01 +#define ADC_Gain_12db 0x02 +#define ADC_Gain_18db 0x03 +#define ADC_Gain_24db 0x04 /* Default */ +#define ADC_Gain_30db 0x05 +#define ADC_Rate_8kHz 0x00 /* Default */ +#define ADC_Rate_16kHz 0x01 +#define ADC_CutoffFreq_NO_DC_Filter 0x00 +#define ADC_CutoffFreq_59p68Hz 0x01 /* Default */ +#define ADC_CutoffFreq_7p46Hz 0x02 +#define ADC_CutoffFreq_3p73Hz 0x03 + +/* Set Power State */ +#define A100_msg_Sleep 0x80100001 + +/* Get/Set Algorithm Parameter command codes list */ +#define A100_msg_GetAlgorithmParm 0x8016 +#define A100_msg_SetAlgorithmParmID 0x8017 +#define A100_msg_SetAlgorithmParm 0x8018 + +/* Get/Set Algorithm Parameter ID List (Transmit Feature) */ +#define AIS_Global_Supression_Level 0x0000 +#define Mic_Config 0x0002 +#define AEC_Mode 0x0003 +#define AEC_CNG 0x0023 +#define Output_AGC 0x0004 +#define Output_AGC_Target_Level 0x0005 +#define Output_AGC_Noise_Floor 0x0006 +#define Output_AGC_SNR_Improvement 0x0007 +#define Comfort_Noise 0x001A +#define Comfort_Noise_Level 0x001B + +/* Get/Set Algorithm Parameter ID List (Receive Feature) */ +#define Speaker_Volume 0x0012 +#define VEQ_Mode 0x0009 +#define VEQ_Max_FarEnd_Limiter_Level 0x000D +#define VEQ_Noise_Estimation_Adj 0x0025 +#define Receive_NS 0x000E +#define Receive_NS_Level 0x000F +#define SideTone 0x0015 +#define SideTone_Gain 0x0016 + +/* Audio Path Commands */ +/* Get/Set Transmit Digital Input Gain */ +#define A100_msg_GetTxDigitalInputGain 0x801A +#define A100_msg_SetTxDigitalInputGain 0x801B + +/* Get/Set Receive Digital Input Gain */ +#define A100_msg_GetRcvDigitalInputGain 0x8022 +#define A100_msg_SetRcvDigitalInputGain 0x8023 + +/* Get/Set Transmit Digital Output Gain */ +#define A100_msg_GetTxDigitalOutputGain 0x801D +#define A100_msg_SetTxDigitalOutputGain 0x8015 + +/* Bypass */ +#define A100_msg_Bypass 0x801C /* 0ff = 0x0000; on = 0x0001 (Default) */ +#define A1026_msg_VP_ON 0x801C0001 +#define A1026_msg_VP_OFF 0x801C0000 + +/* Diagnostic API Commands */ +#define A100_msg_GetMicRMS 0x8013 +#define A100_msg_GetMicPeak 0x8014 +#define DiagPath_Pri_Input_Mic 0x0000 +#define DiagPath_Sec_Input_Mic 0x0001 +#define DiagPath_Output_Mic 0x0002 +#define DiagPath_Far_End_Input 0x0003 +#define DiagPath_Far_End_Output 0x0004 +#define A100_msg_SwapInputCh 0x8019 +#define A100_msg_OutputKnownSig 0x801E + +#define A1026_msg_BOOT 0x0001 +#define A1026_msg_BOOT_ACK 0x01 + +/* general definitions */ +#define TIMEOUT 20 /* ms */ +#define RETRY_CNT 5 +#define POLLING_RETRY_CNT 3 +#define A1026_ERROR_CODE 0xffff +#define A1026_SLEEP 0 +#define A1026_ACTIVE 1 +#define A1026_CMD_FIFO_DEPTH 64 +#define ERROR 0xffffffff + +enum A1026_config_mode { + A1026_CONFIG_FULL, + A1026_CONFIG_VP +}; + +struct a1026_platform_data { + uint32_t gpio_a1026_micsel; + uint32_t gpio_a1026_wakeup; + uint32_t gpio_a1026_reset; + uint32_t gpio_a1026_int; + uint32_t gpio_a1026_clk; +}; + + +#endif /* __KERNEL__ */ +#endif /* __LINUX_A1026_H */ From d34ba2389465206df3e42755737d7441133d20c8 Mon Sep 17 00:00:00 2001 From: Eric Olsen Date: Wed, 2 Sep 2009 16:49:08 -0700 Subject: [PATCH 0448/2556] include: Driver interface for BMA150 G-sensor chip Signed-off-by: Eric Olsen --- include/linux/bma150.h | 78 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 include/linux/bma150.h diff --git a/include/linux/bma150.h b/include/linux/bma150.h new file mode 100644 index 0000000000000..fe8ef225b03f3 --- /dev/null +++ b/include/linux/bma150.h @@ -0,0 +1,78 @@ +/* + * Definitions for BMA150 G-sensor chip. + */ +#ifndef BMA150_H +#define BMA150_H + +#include + +#define BMA150_I2C_NAME "bma150" +#define BMA150_G_SENSOR_NAME "bma150" + +#define BMAIO 0xA1 + +/* BMA150 register address */ +#define CHIP_ID_REG 0x00 +#define VERSION_REG 0x01 +#define X_AXIS_LSB_REG 0x02 +#define X_AXIS_MSB_REG 0x03 +#define Y_AXIS_LSB_REG 0x04 +#define Y_AXIS_MSB_REG 0x05 +#define Z_AXIS_LSB_REG 0x06 +#define Z_AXIS_MSB_REG 0x07 +#define TEMP_RD_REG 0x08 +#define SMB150_STATUS_REG 0x09 +#define SMB150_CTRL_REG 0x0a +#define SMB150_CONF1_REG 0x0b +#define LG_THRESHOLD_REG 0x0c +#define LG_DURATION_REG 0x0d +#define HG_THRESHOLD_REG 0x0e +#define HG_DURATION_REG 0x0f +#define MOTION_THRS_REG 0x10 +#define HYSTERESIS_REG 0x11 +#define CUSTOMER1_REG 0x12 +#define CUSTOMER2_REG 0x13 +#define RANGE_BWIDTH_REG 0x14 +#define SMB150_CONF2_REG 0x15 + +#define OFFS_GAIN_X_REG 0x16 +#define OFFS_GAIN_Y_REG 0x17 +#define OFFS_GAIN_Z_REG 0x18 +#define OFFS_GAIN_T_REG 0x19 +#define OFFSET_X_REG 0x1a +#define OFFSET_Y_REG 0x1b +#define OFFSET_Z_REG 0x1c +#define OFFSET_T_REG 0x1d + + +/* IOCTLs*/ +#define BMA_IOCTL_INIT _IO(BMAIO, 0x31) +#define BMA_IOCTL_WRITE _IOW(BMAIO, 0x32, char[5]) +#define BMA_IOCTL_READ _IOWR(BMAIO, 0x33, char[5]) +#define BMA_IOCTL_READ_ACCELERATION _IOWR(BMAIO, 0x34, short[7]) +#define BMA_IOCTL_SET_MODE _IOW(BMAIO, 0x35, short) +#define BMA_IOCTL_GET_INT _IOR(BMAIO, 0x36, short) + + +/* range and bandwidth */ +#define BMA_RANGE_2G 0 +#define BMA_RANGE_4G 1 +#define BMA_RANGE_8G 2 + +#define BMA_BW_25HZ 0 +#define BMA_BW_50HZ 1 +#define BMA_BW_100HZ 2 +#define BMA_BW_190HZ 3 +#define BMA_BW_375HZ 4 +#define BMA_BW_750HZ 5 +#define BMA_BW_1500HZ 6 + +/* mode settings */ +#define BMA_MODE_NORMAL 0 +#define BMA_MODE_SLEEP 1 + +struct bma150_platform_data { + int intr; +}; + +#endif From 4484cfbb8c683f7af888cddbaad9a50517ab9122 Mon Sep 17 00:00:00 2001 From: Eric Olsen Date: Wed, 2 Sep 2009 16:58:24 -0700 Subject: [PATCH 0449/2556] include: Driver interface for lightsensor Signed-off-by: Eric Olsen --- include/linux/lightsensor.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 include/linux/lightsensor.h diff --git a/include/linux/lightsensor.h b/include/linux/lightsensor.h new file mode 100644 index 0000000000000..501074f406bba --- /dev/null +++ b/include/linux/lightsensor.h @@ -0,0 +1,28 @@ +/* include/linux/lightsensor.h + * + * Copyright (C) 2009 Google, Inc. + * Author: Iliyan Malchev + * + * 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_LIGHTSENSOR_H +#define __LINUX_LIGHTSENSOR_H + +#include +#include + +#define LIGHTSENSOR_IOCTL_MAGIC 'l' + +#define LIGHTSENSOR_IOCTL_GET_ENABLED _IOR(LIGHTSENSOR_IOCTL_MAGIC, 1, int *) +#define LIGHTSENSOR_IOCTL_ENABLE _IOW(LIGHTSENSOR_IOCTL_MAGIC, 2, int *) + +#endif From 40c74e8f5e1a5defb7349bafebbb36fb3f439e66 Mon Sep 17 00:00:00 2001 From: Eric Olsen Date: Wed, 26 Aug 2009 11:59:58 -0700 Subject: [PATCH 0450/2556] i2c: chips: Add driver for AKM8973 Compass device Signed-off-by: Eric Olsen Signed-off-by: Iliyan Malchev Clean up driver debug message from the logs Change-Id: Id8524eee501c9a1644537454a59f9c7598e61dfe Signed-off-by: Eric Olsen [DRIVERS] i2c: akm8973: Fix for orientation issue Signed-off-by: Andy Chuang Signed-off-by: Eric Olsen --- drivers/i2c/chips/Kconfig | 6 + drivers/i2c/chips/Makefile | 1 + drivers/i2c/chips/akm8973.c | 774 ++++++++++++++++++++++++++++++++++++ include/linux/akm8973.h | 61 +++ 4 files changed, 842 insertions(+) create mode 100644 drivers/i2c/chips/akm8973.c create mode 100644 include/linux/akm8973.h diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index f63300040528c..d255a49bb5f89 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -6,6 +6,12 @@ menu "Miscellaneous I2C Chip support" +config SENSORS_AKM8973 + tristate "AKM8973 Compass Driver" + depends on I2C + help + AKM8973 Compass Driver implemented by HTC. + config SENSORS_AKM8976 tristate "AKM8976 Compass Driver" depends on I2C diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index 30edf8a07439c..38362d6c04503 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -10,6 +10,7 @@ # * I/O expander drivers go to drivers/gpio # +obj-$(CONFIG_SENSORS_AKM8973) += akm8973.o obj-$(CONFIG_SENSORS_AKM8976) += akm8976.o obj-$(CONFIG_SENSORS_MT9T013) += mt9t013.o obj-$(CONFIG_VP_A1026) += a1026.o diff --git a/drivers/i2c/chips/akm8973.c b/drivers/i2c/chips/akm8973.c new file mode 100644 index 0000000000000..5f65ffcb1d3eb --- /dev/null +++ b/drivers/i2c/chips/akm8973.c @@ -0,0 +1,774 @@ +/* + * drivers/i2c/chips/akm8973.c - akm8973 compass driver + * + * Copyright (C) 2008-2009 HTC Corporation. + * Author: viral wang + * + * 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 + +#define DEBUG 0 +#define MAX_FAILURE_COUNT 3 + +static struct i2c_client *this_client; + +struct akm8973_data { + struct input_dev *input_dev; + struct work_struct work; + struct early_suspend early_suspend_akm; +}; + +/* Addresses to scan -- protected by sense_data_mutex */ +static char sense_data[RBUFF_SIZE + 1]; +static struct mutex sense_data_mutex; +#define AKM8973_RETRY_COUNT 10 +static DECLARE_WAIT_QUEUE_HEAD(data_ready_wq); +static DECLARE_WAIT_QUEUE_HEAD(open_wq); + +static atomic_t data_ready; +static atomic_t open_count; +static atomic_t open_flag; +static atomic_t reserve_open_flag; + +static atomic_t m_flag; +static atomic_t a_flag; +static atomic_t t_flag; +static atomic_t mv_flag; + +static int failure_count = 0; + +static short akmd_delay = 0; + +static atomic_t suspend_flag = ATOMIC_INIT(0); + +static struct akm8973_platform_data *pdata; + +static int AKI2C_RxData(char *rxData, int length) +{ + uint8_t loop_i; + struct i2c_msg msgs[] = { + { + .addr = this_client->addr, + .flags = 0, + .len = 1, + .buf = rxData, + }, + { + .addr = this_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = rxData, + }, + }; + + for (loop_i = 0; loop_i < AKM8973_RETRY_COUNT; loop_i++) { + if (i2c_transfer(this_client->adapter, msgs, 2) > 0) { + break; + } + mdelay(10); + } + + if (loop_i >= AKM8973_RETRY_COUNT) { + printk(KERN_ERR "%s retry over %d\n", __func__, AKM8973_RETRY_COUNT); + return -EIO; + } + return 0; +} + +static int AKI2C_TxData(char *txData, int length) +{ + uint8_t loop_i; + struct i2c_msg msg[] = { + { + .addr = this_client->addr, + .flags = 0, + .len = length, + .buf = txData, + }, + }; + + for (loop_i = 0; loop_i < AKM8973_RETRY_COUNT; loop_i++) { + if (i2c_transfer(this_client->adapter, msg, 1) > 0) { + break; + } + mdelay(10); + } + + if (loop_i >= AKM8973_RETRY_COUNT) { + printk(KERN_ERR "%s retry over %d\n", __func__, AKM8973_RETRY_COUNT); + return -EIO; + } + return 0; +} + +static void AKECS_Reset(void) +{ + gpio_set_value(pdata->reset, 0); + udelay(120); + gpio_set_value(pdata->reset, 1); +} + +static int AKECS_StartMeasure(void) +{ + char buffer[2]; + + atomic_set(&data_ready, 0); + + /* Set measure mode */ + buffer[0] = AKECS_REG_MS1; + buffer[1] = AKECS_MODE_MEASURE; + + /* Set data */ + return AKI2C_TxData(buffer, 2); +} + +static int AKECS_PowerDown(void) +{ + char buffer[2]; + int ret; + + /* Set powerdown mode */ + buffer[0] = AKECS_REG_MS1; + buffer[1] = AKECS_MODE_POWERDOWN; + /* Set data */ + ret = AKI2C_TxData(buffer, 2); + if (ret < 0) + return ret; + + /* Dummy read for clearing INT pin */ + buffer[0] = AKECS_REG_TMPS; + /* Read data */ + ret = AKI2C_RxData(buffer, 1); + if (ret < 0) + return ret; + return ret; +} + +static int AKECS_StartE2PRead(void) +{ + char buffer[2]; + + /* Set measure mode */ + buffer[0] = AKECS_REG_MS1; + buffer[1] = AKECS_MODE_E2P_READ; + /* Set data */ + return AKI2C_TxData(buffer, 2); +} + +static int AKECS_GetData(void) +{ + char buffer[RBUFF_SIZE + 1]; + int ret; + + memset(buffer, 0, RBUFF_SIZE + 1); + buffer[0] = AKECS_REG_ST; + ret = AKI2C_RxData(buffer, RBUFF_SIZE+1); + if (ret < 0) + return ret; + + mutex_lock(&sense_data_mutex); + memcpy(sense_data, buffer, sizeof(buffer)); + atomic_set(&data_ready, 1); + wake_up(&data_ready_wq); + mutex_unlock(&sense_data_mutex); + + return 0; +} + +static int AKECS_SetMode(char mode) +{ + int ret; + + switch (mode) { + case AKECS_MODE_MEASURE: + ret = AKECS_StartMeasure(); + break; + case AKECS_MODE_E2P_READ: + ret = AKECS_StartE2PRead(); + break; + case AKECS_MODE_POWERDOWN: + ret = AKECS_PowerDown(); + break; + default: + return -EINVAL; + } + + /* wait at least 300us after changing mode */ + mdelay(1); + return ret; +} + +static int AKECS_TransRBuff(char *rbuf, int size) +{ + wait_event_interruptible_timeout(data_ready_wq, + atomic_read(&data_ready), 1000); + if (!atomic_read(&data_ready)) { + if (!atomic_read(&suspend_flag)) { + printk(KERN_ERR + "AKM8973 AKECS_TransRBUFF: Data not ready\n"); + failure_count++; + if (failure_count >= MAX_FAILURE_COUNT) { + printk(KERN_ERR + "AKM8973 AKECS_TransRBUFF: successive %d failure.\n", + failure_count); + atomic_set(&open_flag, -1); + wake_up(&open_wq); + failure_count = 0; + } + } + return -1; + } + + mutex_lock(&sense_data_mutex); + memcpy(&rbuf[1], &sense_data[1], size); + atomic_set(&data_ready, 0); + mutex_unlock(&sense_data_mutex); + + failure_count = 0; + return 0; +} + + +static void AKECS_Report_Value(short *rbuf) +{ + struct akm8973_data *data = i2c_get_clientdata(this_client); +#if DEBUG + printk(KERN_INFO"AKECS_Report_Value: yaw = %d, pitch = %d, roll = %d\n", rbuf[0], + rbuf[1], rbuf[2]); + printk(KERN_INFO" tmp = %d, m_stat= %d, g_stat=%d\n", rbuf[3], + rbuf[4], rbuf[5]); + printk(KERN_INFO" G_Sensor: x = %d LSB, y = %d LSB, z = %d LSB\n", + rbuf[6], rbuf[7], rbuf[8]); +#endif + /* Report magnetic sensor information */ + if (atomic_read(&m_flag)) { + input_report_abs(data->input_dev, ABS_RX, rbuf[0]); + input_report_abs(data->input_dev, ABS_RY, rbuf[1]); + input_report_abs(data->input_dev, ABS_RZ, rbuf[2]); + input_report_abs(data->input_dev, ABS_RUDDER, rbuf[4]); + } + + /* Report acceleration sensor information */ + if (atomic_read(&a_flag)) { + input_report_abs(data->input_dev, ABS_X, rbuf[6]); + input_report_abs(data->input_dev, ABS_Y, rbuf[7]); + input_report_abs(data->input_dev, ABS_Z, rbuf[8]); + input_report_abs(data->input_dev, ABS_WHEEL, rbuf[5]); + } + + /* Report temperature information */ + if (atomic_read(&t_flag)) + input_report_abs(data->input_dev, ABS_THROTTLE, rbuf[3]); + + if (atomic_read(&mv_flag)) { + input_report_abs(data->input_dev, ABS_HAT0X, rbuf[9]); + input_report_abs(data->input_dev, ABS_HAT0Y, rbuf[10]); + input_report_abs(data->input_dev, ABS_BRAKE, rbuf[11]); + } + + input_sync(data->input_dev); +} + +static int AKECS_GetOpenStatus(void) +{ + wait_event_interruptible(open_wq, (atomic_read(&open_flag) != 0)); + return atomic_read(&open_flag); +} + +static int AKECS_GetCloseStatus(void) +{ + wait_event_interruptible(open_wq, (atomic_read(&open_flag) <= 0)); + return atomic_read(&open_flag); +} + +static void AKECS_CloseDone(void) +{ + atomic_set(&m_flag, 1); + atomic_set(&a_flag, 1); + atomic_set(&t_flag, 1); + atomic_set(&mv_flag, 1); +} + +static int akm_aot_open(struct inode *inode, struct file *file) +{ + int ret = -1; + if (atomic_cmpxchg(&open_count, 0, 1) == 0) { + if (atomic_cmpxchg(&open_flag, 0, 1) == 0) { + atomic_set(&reserve_open_flag, 1); + wake_up(&open_wq); + ret = 0; + } + } + return ret; +} + +static int akm_aot_release(struct inode *inode, struct file *file) +{ + atomic_set(&reserve_open_flag, 0); + atomic_set(&open_flag, 0); + atomic_set(&open_count, 0); + wake_up(&open_wq); + return 0; +} + +static int +akm_aot_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + short flag; + + switch (cmd) { + case ECS_IOCTL_APP_SET_MFLAG: + case ECS_IOCTL_APP_SET_AFLAG: + case ECS_IOCTL_APP_SET_TFLAG: + case ECS_IOCTL_APP_SET_MVFLAG: + if (copy_from_user(&flag, argp, sizeof(flag))) + return -EFAULT; + if (flag < 0 || flag > 1) + return -EINVAL; + break; + case ECS_IOCTL_APP_SET_DELAY: + if (copy_from_user(&flag, argp, sizeof(flag))) + return -EFAULT; + break; + default: + break; + } + + switch (cmd) { + case ECS_IOCTL_APP_SET_MFLAG: + atomic_set(&m_flag, flag); + break; + case ECS_IOCTL_APP_GET_MFLAG: + flag = atomic_read(&m_flag); + break; + case ECS_IOCTL_APP_SET_AFLAG: + atomic_set(&a_flag, flag); + break; + case ECS_IOCTL_APP_GET_AFLAG: + flag = atomic_read(&a_flag); + break; + case ECS_IOCTL_APP_SET_TFLAG: + atomic_set(&t_flag, flag); + break; + case ECS_IOCTL_APP_GET_TFLAG: + flag = atomic_read(&t_flag); + break; + case ECS_IOCTL_APP_SET_MVFLAG: + atomic_set(&mv_flag, flag); + break; + case ECS_IOCTL_APP_GET_MVFLAG: + flag = atomic_read(&mv_flag); + break; + case ECS_IOCTL_APP_SET_DELAY: + akmd_delay = flag; + break; + case ECS_IOCTL_APP_GET_DELAY: + flag = akmd_delay; + break; + default: + return -ENOTTY; + } + + switch (cmd) { + case ECS_IOCTL_APP_GET_MFLAG: + case ECS_IOCTL_APP_GET_AFLAG: + case ECS_IOCTL_APP_GET_TFLAG: + case ECS_IOCTL_APP_GET_MVFLAG: + case ECS_IOCTL_APP_GET_DELAY: + if (copy_to_user(argp, &flag, sizeof(flag))) + return -EFAULT; + break; + default: + break; + } + + return 0; +} + +static int akmd_open(struct inode *inode, struct file *file) +{ + return nonseekable_open(inode, file); +} + +static int akmd_release(struct inode *inode, struct file *file) +{ + AKECS_CloseDone(); + return 0; +} + +static int +akmd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + + void __user *argp = (void __user *)arg; + + char msg[RBUFF_SIZE + 1], rwbuf[5]; + int ret = -1, status; + short mode, value[12], delay; + char project_name[64]; + short layouts[4][3][3]; + int i, j, k; + + + switch (cmd) { + case ECS_IOCTL_WRITE: + case ECS_IOCTL_READ: + if (copy_from_user(&rwbuf, argp, sizeof(rwbuf))) + return -EFAULT; + break; + case ECS_IOCTL_SET_MODE: + if (copy_from_user(&mode, argp, sizeof(mode))) + return -EFAULT; + break; + case ECS_IOCTL_SET_YPR: + if (copy_from_user(&value, argp, sizeof(value))) + return -EFAULT; + break; + default: + break; + } + + switch (cmd) { + case ECS_IOCTL_WRITE: + if (rwbuf[0] < 2) + return -EINVAL; + ret = AKI2C_TxData(&rwbuf[1], rwbuf[0]); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_READ: + if (rwbuf[0] < 1) + return -EINVAL; + ret = AKI2C_RxData(&rwbuf[1], rwbuf[0]); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_RESET: + AKECS_Reset(); + break; + case ECS_IOCTL_SET_MODE: + ret = AKECS_SetMode((char)mode); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_GETDATA: + ret = AKECS_TransRBuff(msg, RBUFF_SIZE); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_SET_YPR: + AKECS_Report_Value(value); + break; + case ECS_IOCTL_GET_OPEN_STATUS: + status = AKECS_GetOpenStatus(); + break; + case ECS_IOCTL_GET_CLOSE_STATUS: + status = AKECS_GetCloseStatus(); + break; + case ECS_IOCTL_GET_DELAY: + delay = akmd_delay; + break; + case ECS_IOCTL_GET_PROJECT_NAME: + strncpy(project_name, pdata->project_name, 64); + break; + case ECS_IOCTL_GET_MATRIX: + for (i = 0; i < 4; i++) + for (j = 0; j < 3; j++) + for (k = 0; k < 3; k++) { + layouts[i][j][k] = pdata->layouts[i][j][k]; + } + break; + default: + return -ENOTTY; + } + + switch (cmd) { + case ECS_IOCTL_READ: + if (copy_to_user(argp, &rwbuf, sizeof(rwbuf))) + return -EFAULT; + break; + case ECS_IOCTL_GETDATA: + if (copy_to_user(argp, &msg, sizeof(msg))) + return -EFAULT; + break; + case ECS_IOCTL_GET_OPEN_STATUS: + case ECS_IOCTL_GET_CLOSE_STATUS: + if (copy_to_user(argp, &status, sizeof(status))) + return -EFAULT; + break; + case ECS_IOCTL_GET_DELAY: + if (copy_to_user(argp, &delay, sizeof(delay))) + return -EFAULT; + break; + case ECS_IOCTL_GET_PROJECT_NAME: + if (copy_to_user(argp, project_name, sizeof(project_name))) + return -EFAULT; + break; + case ECS_IOCTL_GET_MATRIX: + if (copy_to_user(argp, layouts, sizeof(layouts))) + return -EFAULT; + break; + default: + break; + } + + return 0; +} + +static void akm_work_func(struct work_struct *work) +{ + if (AKECS_GetData() < 0) + printk(KERN_ERR "AKM8973 akm_work_func: Get data failed\n"); + enable_irq(this_client->irq); +} + +static irqreturn_t akm8973_interrupt(int irq, void *dev_id) +{ + struct akm8973_data *data = dev_id; + disable_irq_nosync(this_client->irq); + schedule_work(&data->work); + return IRQ_HANDLED; +} + +static void akm8973_early_suspend(struct early_suspend *handler) +{ + atomic_set(&suspend_flag, 1); + atomic_set(&reserve_open_flag, atomic_read(&open_flag)); + atomic_set(&open_flag, 0); + wake_up(&open_wq); + disable_irq(this_client->irq); +} + +static void akm8973_early_resume(struct early_suspend *handler) +{ + enable_irq(this_client->irq); + atomic_set(&suspend_flag, 0); + atomic_set(&open_flag, atomic_read(&reserve_open_flag)); + wake_up(&open_wq); +} + +static struct file_operations akmd_fops = { + .owner = THIS_MODULE, + .open = akmd_open, + .release = akmd_release, + .ioctl = akmd_ioctl, +}; + +static struct file_operations akm_aot_fops = { + .owner = THIS_MODULE, + .open = akm_aot_open, + .release = akm_aot_release, + .ioctl = akm_aot_ioctl, +}; + + +static struct miscdevice akm_aot_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "akm8973_aot", + .fops = &akm_aot_fops, +}; + + +static struct miscdevice akmd_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "akm8973_daemon", + .fops = &akmd_fops, +}; + +int akm8973_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct akm8973_data *akm; + int err = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + err = -ENODEV; + goto exit_check_functionality_failed; + } + + akm = kzalloc(sizeof(struct akm8973_data), GFP_KERNEL); + if (!akm) { + err = -ENOMEM; + goto exit_alloc_data_failed; + } + + INIT_WORK(&akm->work, akm_work_func); + i2c_set_clientdata(client, akm); + + pdata = client->dev.platform_data; + if (pdata == NULL) { + printk(KERN_ERR"AKM8973 akm8973_probe: platform data is NULL\n"); + goto exit_platform_data_null; + } + this_client = client; + + err = AKECS_PowerDown(); + if (err < 0) { + printk(KERN_ERR"AKM8973 akm8973_probe: set power down mode error\n"); + goto exit_set_mode_failed; + } + + err = request_irq(client->irq, akm8973_interrupt, IRQF_TRIGGER_HIGH, + "akm8973", akm); + + if (err < 0) { + printk(KERN_ERR"AKM8973 akm8973_probe: request irq failed\n"); + goto exit_irq_request_failed; + } + + akm->input_dev = input_allocate_device(); + + if (!akm->input_dev) { + err = -ENOMEM; + printk(KERN_ERR + "AKM8973 akm8973_probe: Failed to allocate input device\n"); + goto exit_input_dev_alloc_failed; + } + + set_bit(EV_ABS, akm->input_dev->evbit); + /* yaw */ + input_set_abs_params(akm->input_dev, ABS_RX, 0, 360, 0, 0); + /* pitch */ + input_set_abs_params(akm->input_dev, ABS_RY, -180, 180, 0, 0); + /* roll */ + input_set_abs_params(akm->input_dev, ABS_RZ, -90, 90, 0, 0); + /* x-axis acceleration */ + input_set_abs_params(akm->input_dev, ABS_X, -1872, 1872, 0, 0); + /* y-axis acceleration */ + input_set_abs_params(akm->input_dev, ABS_Y, -1872, 1872, 0, 0); + /* z-axis acceleration */ + input_set_abs_params(akm->input_dev, ABS_Z, -1872, 1872, 0, 0); + /* temparature */ + input_set_abs_params(akm->input_dev, ABS_THROTTLE, -30, 85, 0, 0); + /* status of magnetic sensor */ + input_set_abs_params(akm->input_dev, ABS_RUDDER, -32768, 3, 0, 0); + /* status of acceleration sensor */ + input_set_abs_params(akm->input_dev, ABS_WHEEL, -32768, 3, 0, 0); + /* step count */ + input_set_abs_params(akm->input_dev, ABS_GAS, 0, 65535, 0, 0); + /* x-axis of raw magnetic vector */ + input_set_abs_params(akm->input_dev, ABS_HAT0X, -2048, 2032, 0, 0); + /* y-axis of raw magnetic vector */ + input_set_abs_params(akm->input_dev, ABS_HAT0Y, -2048, 2032, 0, 0); + /* z-axis of raw magnetic vector */ + input_set_abs_params(akm->input_dev, ABS_BRAKE, -2048, 2032, 0, 0); + + akm->input_dev->name = "compass"; + + err = input_register_device(akm->input_dev); + + if (err) { + printk(KERN_ERR + "AKM8973 akm8973_probe: Unable to register input device: %s\n", + akm->input_dev->name); + goto exit_input_register_device_failed; + } + + err = misc_register(&akmd_device); + if (err) { + printk(KERN_ERR "AKM8973 akm8973_probe: akmd_device register failed\n"); + goto exit_misc_device_register_failed; + } + + err = misc_register(&akm_aot_device); + if (err) { + printk(KERN_ERR + "AKM8973 akm8973_probe: akm_aot_device register failed\n"); + goto exit_misc_device_register_failed; + } + + mutex_init(&sense_data_mutex); + + init_waitqueue_head(&data_ready_wq); + init_waitqueue_head(&open_wq); + + /* As default, report all information */ + atomic_set(&m_flag, 1); + atomic_set(&a_flag, 1); + atomic_set(&t_flag, 1); + atomic_set(&mv_flag, 1); + + akm->early_suspend_akm.suspend = akm8973_early_suspend; + akm->early_suspend_akm.resume = akm8973_early_resume; + register_early_suspend(&akm->early_suspend_akm); + + return 0; + +exit_misc_device_register_failed: +exit_input_register_device_failed: + input_free_device(akm->input_dev); +exit_input_dev_alloc_failed: + free_irq(client->irq, akm); +exit_irq_request_failed: +exit_set_mode_failed: +exit_platform_data_null: + kfree(akm); +exit_alloc_data_failed: +exit_check_functionality_failed: + return err; + +} + +static int akm8973_remove(struct i2c_client *client) +{ + struct akm8973_data *akm = i2c_get_clientdata(client); + free_irq(client->irq, akm); + input_unregister_device(akm->input_dev); + kfree(akm); + return 0; +} +static const struct i2c_device_id akm8973_id[] = { + { AKM8973_I2C_NAME, 0 }, + { } +}; + +static struct i2c_driver akm8973_driver = { + .probe = akm8973_probe, + .remove = akm8973_remove, + .id_table = akm8973_id, + .driver = { + .name = AKM8973_I2C_NAME, + }, +}; + +static int __init akm8973_init(void) +{ + printk(KERN_INFO "AKM8973 compass driver: init\n"); + return i2c_add_driver(&akm8973_driver); +} + +static void __exit akm8973_exit(void) +{ + i2c_del_driver(&akm8973_driver); +} + +module_init(akm8973_init); +module_exit(akm8973_exit); + +MODULE_AUTHOR("viral wang "); +MODULE_DESCRIPTION("AKM8973 compass driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/akm8973.h b/include/linux/akm8973.h new file mode 100644 index 0000000000000..2afa3bacddacf --- /dev/null +++ b/include/linux/akm8973.h @@ -0,0 +1,61 @@ +/* + * Definitions for akm8973 compass chip. + */ +#ifndef AKM8973_H +#define AKM8973_H + +#include + +#define AKM8973_I2C_NAME "akm8973" + +/* Compass device dependent definition */ +#define AKECS_MODE_MEASURE 0x00 /* Starts measurement. Please use AKECS_MODE_MEASURE_SNG */ + /* or AKECS_MODE_MEASURE_SEQ instead of this. */ +#define AKECS_MODE_E2P_READ 0x02 /* E2P access mode (read). */ +#define AKECS_MODE_POWERDOWN 0x03 /* Power down mode */ + +#define RBUFF_SIZE 4 /* Rx buffer size */ + +/* AK8973 register address */ +#define AKECS_REG_ST 0xC0 +#define AKECS_REG_TMPS 0xC1 +#define AKECS_REG_MS1 0xE0 + +#define AKMIO 0xA1 + +/* IOCTLs for AKM library */ +#define ECS_IOCTL_WRITE _IOW(AKMIO, 0x01, char[5]) +#define ECS_IOCTL_READ _IOWR(AKMIO, 0x02, char[5]) +#define ECS_IOCTL_RESET _IO(AKMIO, 0x03) +#define ECS_IOCTL_SET_MODE _IOW(AKMIO, 0x04, short) +#define ECS_IOCTL_GETDATA _IOR(AKMIO, 0x05, char[RBUFF_SIZE+1]) +#define ECS_IOCTL_SET_YPR _IOW(AKMIO, 0x06, short[12]) +#define ECS_IOCTL_GET_OPEN_STATUS _IOR(AKMIO, 0x07, int) +#define ECS_IOCTL_GET_CLOSE_STATUS _IOR(AKMIO, 0x08, int) +#define ECS_IOCTL_GET_DELAY _IOR(AKMIO, 0x30, short) +#define ECS_IOCTL_GET_PROJECT_NAME _IOR(AKMIO, 0x0D, char[64]) +#define ECS_IOCTL_GET_MATRIX _IOR(AKMIO, 0x0E, short [4][3][3]) + +/* IOCTLs for APPs */ +#define ECS_IOCTL_APP_SET_MODE _IOW(AKMIO, 0x10, short) +#define ECS_IOCTL_APP_SET_MFLAG _IOW(AKMIO, 0x11, short) +#define ECS_IOCTL_APP_GET_MFLAG _IOW(AKMIO, 0x12, short) +#define ECS_IOCTL_APP_SET_AFLAG _IOW(AKMIO, 0x13, short) +#define ECS_IOCTL_APP_GET_AFLAG _IOR(AKMIO, 0x14, short) +#define ECS_IOCTL_APP_SET_TFLAG _IOR(AKMIO, 0x15, short) +#define ECS_IOCTL_APP_GET_TFLAG _IOR(AKMIO, 0x16, short) +#define ECS_IOCTL_APP_RESET_PEDOMETER _IO(AKMIO, 0x17) +#define ECS_IOCTL_APP_SET_DELAY _IOW(AKMIO, 0x18, short) +#define ECS_IOCTL_APP_GET_DELAY ECS_IOCTL_GET_DELAY +#define ECS_IOCTL_APP_SET_MVFLAG _IOW(AKMIO, 0x19, short) /* Set raw magnetic vector flag */ +#define ECS_IOCTL_APP_GET_MVFLAG _IOR(AKMIO, 0x1A, short) /* Get raw magnetic vector flag */ + +struct akm8973_platform_data { + short layouts[4][3][3]; + char project_name[64]; + int reset; + int intr; +}; + +#endif + From 1cdad85f568dbd496157655d476f038da1b120a7 Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Wed, 3 Mar 2010 16:48:19 -0800 Subject: [PATCH 0451/2556] i2c: akm8973: Use normal suspend instead of early suspend Can now generate events when screen is off + wakelock. Change-Id: I07ca77ea7465e05d01e5978cae639926fadeab8f Signed-off-by: Mike Chan Signed-off-by: Iliyan Malchev --- drivers/i2c/chips/akm8973.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/i2c/chips/akm8973.c b/drivers/i2c/chips/akm8973.c index 5f65ffcb1d3eb..f19c5de31444a 100644 --- a/drivers/i2c/chips/akm8973.c +++ b/drivers/i2c/chips/akm8973.c @@ -27,7 +27,6 @@ #include #include #include -#include #define DEBUG 0 #define MAX_FAILURE_COUNT 3 @@ -37,7 +36,6 @@ static struct i2c_client *this_client; struct akm8973_data { struct input_dev *input_dev; struct work_struct work; - struct early_suspend early_suspend_akm; }; /* Addresses to scan -- protected by sense_data_mutex */ @@ -555,7 +553,7 @@ static irqreturn_t akm8973_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static void akm8973_early_suspend(struct early_suspend *handler) +static void akm8973_suspend(struct device *device) { atomic_set(&suspend_flag, 1); atomic_set(&reserve_open_flag, atomic_read(&open_flag)); @@ -564,7 +562,7 @@ static void akm8973_early_suspend(struct early_suspend *handler) disable_irq(this_client->irq); } -static void akm8973_early_resume(struct early_suspend *handler) +static void akm8973_resume(struct device *device) { enable_irq(this_client->irq); atomic_set(&suspend_flag, 0); @@ -712,10 +710,6 @@ int akm8973_probe(struct i2c_client *client, const struct i2c_device_id *id) atomic_set(&t_flag, 1); atomic_set(&mv_flag, 1); - akm->early_suspend_akm.suspend = akm8973_early_suspend; - akm->early_suspend_akm.resume = akm8973_early_resume; - register_early_suspend(&akm->early_suspend_akm); - return 0; exit_misc_device_register_failed: @@ -746,12 +740,18 @@ static const struct i2c_device_id akm8973_id[] = { { } }; +static struct dev_pm_ops akm8973_pm_ops = { + .suspend_noirq = akm8973_suspend, + .resume_noirq = akm8973_resume, +}; + static struct i2c_driver akm8973_driver = { .probe = akm8973_probe, .remove = akm8973_remove, .id_table = akm8973_id, .driver = { .name = AKM8973_I2C_NAME, + .pm = &akm8973_pm_ops, }, }; From 0deaf749d30c2f42f3ec305ac70fe2d2d232312e Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Thu, 1 Apr 2010 15:35:03 -0700 Subject: [PATCH 0452/2556] i2c: akm8973: Disable on akm_aot_release() instead of pm hooks. When close is called on the fd and akm_aot_release() is called disable interrupts and have akm8973 auto power down.. Change-Id: Ice8168321a3427443069bb39d796d4c2667bfc7f Signed-off-by: Mike Chan --- drivers/i2c/chips/akm8973.c | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/drivers/i2c/chips/akm8973.c b/drivers/i2c/chips/akm8973.c index f19c5de31444a..723eac70d98cb 100644 --- a/drivers/i2c/chips/akm8973.c +++ b/drivers/i2c/chips/akm8973.c @@ -59,8 +59,6 @@ static int failure_count = 0; static short akmd_delay = 0; -static atomic_t suspend_flag = ATOMIC_INIT(0); - static struct akm8973_platform_data *pdata; static int AKI2C_RxData(char *rxData, int length) @@ -223,7 +221,8 @@ static int AKECS_TransRBuff(char *rbuf, int size) wait_event_interruptible_timeout(data_ready_wq, atomic_read(&data_ready), 1000); if (!atomic_read(&data_ready)) { - if (!atomic_read(&suspend_flag)) { + /* Ignore data errors if there are no open handles */ + if (atomic_read(&open_count) > 0) { printk(KERN_ERR "AKM8973 AKECS_TransRBUFF: Data not ready\n"); failure_count++; @@ -315,6 +314,7 @@ static int akm_aot_open(struct inode *inode, struct file *file) if (atomic_cmpxchg(&open_count, 0, 1) == 0) { if (atomic_cmpxchg(&open_flag, 0, 1) == 0) { atomic_set(&reserve_open_flag, 1); + enable_irq(this_client->irq); wake_up(&open_wq); ret = 0; } @@ -328,6 +328,7 @@ static int akm_aot_release(struct inode *inode, struct file *file) atomic_set(&open_flag, 0); atomic_set(&open_count, 0); wake_up(&open_wq); + disable_irq(this_client->irq); return 0; } @@ -553,23 +554,6 @@ static irqreturn_t akm8973_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static void akm8973_suspend(struct device *device) -{ - atomic_set(&suspend_flag, 1); - atomic_set(&reserve_open_flag, atomic_read(&open_flag)); - atomic_set(&open_flag, 0); - wake_up(&open_wq); - disable_irq(this_client->irq); -} - -static void akm8973_resume(struct device *device) -{ - enable_irq(this_client->irq); - atomic_set(&suspend_flag, 0); - atomic_set(&open_flag, atomic_read(&reserve_open_flag)); - wake_up(&open_wq); -} - static struct file_operations akmd_fops = { .owner = THIS_MODULE, .open = akmd_open, @@ -632,6 +616,7 @@ int akm8973_probe(struct i2c_client *client, const struct i2c_device_id *id) err = request_irq(client->irq, akm8973_interrupt, IRQF_TRIGGER_HIGH, "akm8973", akm); + disable_irq(this_client->irq); if (err < 0) { printk(KERN_ERR"AKM8973 akm8973_probe: request irq failed\n"); @@ -740,18 +725,12 @@ static const struct i2c_device_id akm8973_id[] = { { } }; -static struct dev_pm_ops akm8973_pm_ops = { - .suspend_noirq = akm8973_suspend, - .resume_noirq = akm8973_resume, -}; - static struct i2c_driver akm8973_driver = { .probe = akm8973_probe, .remove = akm8973_remove, .id_table = akm8973_id, .driver = { .name = AKM8973_I2C_NAME, - .pm = &akm8973_pm_ops, }, }; From c061067f772fbd459bbcac95e239b6bde9a86a8f Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 28 Jul 2010 12:28:32 -0700 Subject: [PATCH 0453/2556] misc: move akm8973 driver out of deprecated i2c/chips into misc Change-Id: Ie7816e6f04ec404c1f2972fd89b3f29e7b771f02 Signed-off-by: Dima Zavin --- drivers/i2c/chips/Kconfig | 6 ------ drivers/i2c/chips/Makefile | 1 - drivers/misc/Kconfig | 6 ++++++ drivers/misc/Makefile | 1 + drivers/{i2c/chips => misc}/akm8973.c | 0 5 files changed, 7 insertions(+), 7 deletions(-) rename drivers/{i2c/chips => misc}/akm8973.c (100%) diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index d255a49bb5f89..f63300040528c 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -6,12 +6,6 @@ menu "Miscellaneous I2C Chip support" -config SENSORS_AKM8973 - tristate "AKM8973 Compass Driver" - depends on I2C - help - AKM8973 Compass Driver implemented by HTC. - config SENSORS_AKM8976 tristate "AKM8976 Compass Driver" depends on I2C diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index 38362d6c04503..30edf8a07439c 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -10,7 +10,6 @@ # * I/O expander drivers go to drivers/gpio # -obj-$(CONFIG_SENSORS_AKM8973) += akm8973.o obj-$(CONFIG_SENSORS_AKM8976) += akm8976.o obj-$(CONFIG_SENSORS_MT9T013) += mt9t013.o obj-$(CONFIG_VP_A1026) += a1026.o diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 722fc12d5a4ac..d1593da4037a8 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -390,6 +390,12 @@ config SENSORS_AK8975 If you say yes here you get support for Asahi Kasei's orientation sensor AK8975. +config SENSORS_AKM8973 + tristate "AKM8973 Compass Driver" + depends on I2C + help + AKM8973 Compass Driver implemented by HTC. + config EP93XX_PWM tristate "EP93xx PWM support" depends on ARCH_EP93XX diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 21f4bc8a5e3de..d8e6f0c430c38 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -48,3 +48,4 @@ obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o obj-$(CONFIG_WL127X_RFKILL) += wl127x-rfkill.o obj-$(CONFIG_APANIC) += apanic.o obj-$(CONFIG_SENSORS_AK8975) += akm8975.o +obj-$(CONFIG_SENSORS_AKM8973) += akm8973.o diff --git a/drivers/i2c/chips/akm8973.c b/drivers/misc/akm8973.c similarity index 100% rename from drivers/i2c/chips/akm8973.c rename to drivers/misc/akm8973.c From c0af23b206905318485286728ad27aa1a41ecc98 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 5 Jan 2011 21:22:43 -0800 Subject: [PATCH 0454/2556] misc: akm8973: convert ioctl to unlocked_ioctl, add mutex Change-Id: Id99d79a5fd2451acf7bacbf65f2ab8d4325a279a Signed-off-by: Dima Zavin --- drivers/misc/akm8973.c | 60 +++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/drivers/misc/akm8973.c b/drivers/misc/akm8973.c index 723eac70d98cb..3e756fcee0d80 100644 --- a/drivers/misc/akm8973.c +++ b/drivers/misc/akm8973.c @@ -27,6 +27,7 @@ #include #include #include +#include #define DEBUG 0 #define MAX_FAILURE_COUNT 3 @@ -288,29 +289,38 @@ static void AKECS_Report_Value(short *rbuf) input_sync(data->input_dev); } +static DEFINE_MUTEX(akmd_lock); + static int AKECS_GetOpenStatus(void) { + mutex_unlock(&akmd_lock); wait_event_interruptible(open_wq, (atomic_read(&open_flag) != 0)); + mutex_lock(&akmd_lock); return atomic_read(&open_flag); } static int AKECS_GetCloseStatus(void) { + mutex_unlock(&akmd_lock); wait_event_interruptible(open_wq, (atomic_read(&open_flag) <= 0)); + mutex_lock(&akmd_lock); return atomic_read(&open_flag); } static void AKECS_CloseDone(void) { + mutex_lock(&akmd_lock); atomic_set(&m_flag, 1); atomic_set(&a_flag, 1); atomic_set(&t_flag, 1); atomic_set(&mv_flag, 1); + mutex_unlock(&akmd_lock); } static int akm_aot_open(struct inode *inode, struct file *file) { int ret = -1; + mutex_lock(&akmd_lock); if (atomic_cmpxchg(&open_count, 0, 1) == 0) { if (atomic_cmpxchg(&open_flag, 0, 1) == 0) { atomic_set(&reserve_open_flag, 1); @@ -319,22 +329,24 @@ static int akm_aot_open(struct inode *inode, struct file *file) ret = 0; } } + mutex_unlock(&akmd_lock); return ret; } static int akm_aot_release(struct inode *inode, struct file *file) { + mutex_lock(&akmd_lock); atomic_set(&reserve_open_flag, 0); atomic_set(&open_flag, 0); atomic_set(&open_count, 0); wake_up(&open_wq); disable_irq(this_client->irq); + mutex_unlock(&akmd_lock); return 0; } -static int -akm_aot_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long +akm_aot_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; short flag; @@ -357,6 +369,7 @@ akm_aot_ioctl(struct inode *inode, struct file *file, break; } + mutex_lock(&akmd_lock); switch (cmd) { case ECS_IOCTL_APP_SET_MFLAG: atomic_set(&m_flag, flag); @@ -389,8 +402,10 @@ akm_aot_ioctl(struct inode *inode, struct file *file, flag = akmd_delay; break; default: + mutex_unlock(&akmd_lock); return -ENOTTY; } + mutex_unlock(&akmd_lock); switch (cmd) { case ECS_IOCTL_APP_GET_MFLAG: @@ -419,9 +434,8 @@ static int akmd_release(struct inode *inode, struct file *file) return 0; } -static int -akmd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) +static long +akmd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; @@ -433,7 +447,6 @@ akmd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, short layouts[4][3][3]; int i, j, k; - switch (cmd) { case ECS_IOCTL_WRITE: case ECS_IOCTL_READ: @@ -452,20 +465,25 @@ akmd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, break; } + mutex_lock(&akmd_lock); switch (cmd) { case ECS_IOCTL_WRITE: - if (rwbuf[0] < 2) - return -EINVAL; + if (rwbuf[0] < 2) { + ret = -EINVAL; + goto err; + } ret = AKI2C_TxData(&rwbuf[1], rwbuf[0]); if (ret < 0) - return ret; + goto err; break; case ECS_IOCTL_READ: - if (rwbuf[0] < 1) - return -EINVAL; + if (rwbuf[0] < 1) { + ret = -EINVAL; + goto err; + } ret = AKI2C_RxData(&rwbuf[1], rwbuf[0]); if (ret < 0) - return ret; + goto err; break; case ECS_IOCTL_RESET: AKECS_Reset(); @@ -473,12 +491,12 @@ akmd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, case ECS_IOCTL_SET_MODE: ret = AKECS_SetMode((char)mode); if (ret < 0) - return ret; + goto err; break; case ECS_IOCTL_GETDATA: ret = AKECS_TransRBuff(msg, RBUFF_SIZE); if (ret < 0) - return ret; + goto err; break; case ECS_IOCTL_SET_YPR: AKECS_Report_Value(value); @@ -503,8 +521,10 @@ akmd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, } break; default: - return -ENOTTY; + ret = -ENOTTY; + goto err; } + mutex_unlock(&akmd_lock); switch (cmd) { case ECS_IOCTL_READ: @@ -537,6 +557,10 @@ akmd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, } return 0; + +err: + mutex_unlock(&akmd_lock); + return ret; } static void akm_work_func(struct work_struct *work) @@ -558,14 +582,14 @@ static struct file_operations akmd_fops = { .owner = THIS_MODULE, .open = akmd_open, .release = akmd_release, - .ioctl = akmd_ioctl, + .unlocked_ioctl = akmd_ioctl, }; static struct file_operations akm_aot_fops = { .owner = THIS_MODULE, .open = akm_aot_open, .release = akm_aot_release, - .ioctl = akm_aot_ioctl, + .unlocked_ioctl = akm_aot_ioctl, }; From 023a5c91d66678f6e7d824fc633316f2c76c13f5 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 29 Oct 2009 13:51:17 -0700 Subject: [PATCH 0455/2556] [ARM] msm: bcm_bt_lpm: Add driver for Broadcom Bluetooth low power mode. Signed-off-by: Nick Pelly [ARM] msm: bcm_bt_lpm: fix spinlocks Change-Id: Iee7615b96355543205ee28a0ca8d6d8ade88fa14 Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/include/mach/bcm_bt_lpm.h | 36 ++++ drivers/tty/serial/Kconfig | 8 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/bcm_bt_lpm.c | 176 ++++++++++++++++++++ 4 files changed, 221 insertions(+) create mode 100644 arch/arm/mach-msm/include/mach/bcm_bt_lpm.h create mode 100644 drivers/tty/serial/bcm_bt_lpm.c diff --git a/arch/arm/mach-msm/include/mach/bcm_bt_lpm.h b/arch/arm/mach-msm/include/mach/bcm_bt_lpm.h new file mode 100644 index 0000000000000..c22429718809a --- /dev/null +++ b/arch/arm/mach-msm/include/mach/bcm_bt_lpm.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2009 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 __ASM_ARCH_BCM_BT_LPM_H +#define __ASM_ARCH_BCM_BT_LPM_H + +#include + +/* Uart driver must call this every time it beings TX, to ensure + * this driver keeps WAKE asserted during TX. Called with uart + * spinlock held. */ +extern void bcm_bt_lpm_exit_lpm_locked(struct uart_port *uport); + +struct bcm_bt_lpm_platform_data { + unsigned int gpio_wake; /* CPU -> BCM wakeup gpio */ + unsigned int gpio_host_wake; /* BCM -> CPU wakeup gpio */ + + /* Callback to request the uart driver to clock off. + * Called with uart spinlock held. */ + void (*request_clock_off_locked)(struct uart_port *uport); + /* Callback to request the uart driver to clock on. + * Called with uart spinlock held. */ + void (*request_clock_on_locked)(struct uart_port *uport); +}; + +#endif diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index a5628cedcf226..4b1067f99d2f6 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1353,6 +1353,14 @@ config SERIAL_MSM_HS help Select this module to enable MSM high speed UART driver. +config SERIAL_BCM_BT_LPM + tristate "Broadcom Bluetooth Low Power Mode" + depends on ARM && ARCH_MSM + select SERIAL_CORE + default n + help + Select this module for Broadcom Bluetooth low power management. + config SERIAL_NETX tristate "NetX serial port support" depends on ARM && ARCH_NETX diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 176699d75c0eb..41757700c465c 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_SERIAL_PXA) += pxa.o obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o obj-$(CONFIG_SERIAL_SA1100) += sa1100.o obj-$(CONFIG_SERIAL_BCM63XX) += bcm63xx_uart.o +obj-$(CONFIG_SERIAL_BCM_BT_LPM) += bcm_bt_lpm.o obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o diff --git a/drivers/tty/serial/bcm_bt_lpm.c b/drivers/tty/serial/bcm_bt_lpm.c new file mode 100644 index 0000000000000..bf5ab3320594c --- /dev/null +++ b/drivers/tty/serial/bcm_bt_lpm.c @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2009 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 +#include +#include + +/* + * Manage WAKE and HOST_WAKE low power mode signals for Broadcom + * Bluetooth chipsets. + * + * This driver needs to be tightly coupled with a uart driver that supports + * request_clock_off_locked() and request_clock_on_locked(), to clock off and + * on the uart indepdently of Linux suspend/resume. + * + * The uart driver needs to call bcm_bt_lpm_exit_lpm_locked() every time it + * begins TX, to ensure this driver keeps WAKE asserted during TX. + * + * The callbacks and hijacking of the uart_port struct are not a clean API, + * but the Linux tty and serial core layers do not have a better alternative + * right now: there is no good way to plumb uart clock control through these + * layers. See http://lkml.org/lkml/2008/12/19/213 for more background. + */ + +struct bcm_bt_lpm { + unsigned int gpio_wake; + unsigned int gpio_host_wake; + + int wake; + int host_wake; + + struct hrtimer enter_lpm_timer; + ktime_t enter_lpm_delay; + + struct uart_port *uport; + + void (*request_clock_off_locked)(struct uart_port *uport); + void (*request_clock_on_locked)(struct uart_port *uport); +} bt_lpm; + +static void set_wake_locked(int wake) +{ + if (wake == bt_lpm.wake) + return; + bt_lpm.wake = wake; + + if (wake || bt_lpm.host_wake) + bt_lpm.request_clock_on_locked(bt_lpm.uport); + else + bt_lpm.request_clock_off_locked(bt_lpm.uport); + + gpio_set_value(bt_lpm.gpio_wake, wake); +} + +static enum hrtimer_restart enter_lpm(struct hrtimer *timer) { + unsigned long flags; + + spin_lock_irqsave(&bt_lpm.uport->lock, flags); + set_wake_locked(0); + spin_unlock_irqrestore(&bt_lpm.uport->lock, flags); + + return HRTIMER_NORESTART; +} + +void bcm_bt_lpm_exit_lpm_locked(struct uart_port *uport) { + bt_lpm.uport = uport; + + hrtimer_try_to_cancel(&bt_lpm.enter_lpm_timer); + + set_wake_locked(1); + + hrtimer_start(&bt_lpm.enter_lpm_timer, bt_lpm.enter_lpm_delay, + HRTIMER_MODE_REL); +} +EXPORT_SYMBOL(bcm_bt_lpm_exit_lpm_locked); + +static void update_host_wake_locked(int host_wake) +{ + if (host_wake == bt_lpm.host_wake) + return; + bt_lpm.host_wake = host_wake; + + if (bt_lpm.wake || host_wake) + bt_lpm.request_clock_on_locked(bt_lpm.uport); + else + bt_lpm.request_clock_off_locked(bt_lpm.uport); +} + +static irqreturn_t host_wake_isr(int irq, void *dev) +{ + int host_wake; + unsigned long flags; + + host_wake = gpio_get_value(bt_lpm.gpio_host_wake); + set_irq_type(irq, host_wake ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + + if (!bt_lpm.uport) { + bt_lpm.host_wake = host_wake; + return IRQ_HANDLED; + } + + spin_lock_irqsave(&bt_lpm.uport->lock, flags); + + update_host_wake_locked(host_wake); + + spin_unlock_irqrestore(&bt_lpm.uport->lock, flags); + + return IRQ_HANDLED; +} + +static int bcm_bt_lpm_probe(struct platform_device *pdev) +{ + int irq; + int ret; + struct bcm_bt_lpm_platform_data *pdata = pdev->dev.platform_data; + + if (bt_lpm.request_clock_off_locked != NULL) { + printk(KERN_ERR "Cannot register two bcm_bt_lpm drivers\n"); + return -EINVAL; + } + + bt_lpm.gpio_wake = pdata->gpio_wake; + bt_lpm.gpio_host_wake = pdata->gpio_host_wake; + bt_lpm.request_clock_off_locked = pdata->request_clock_off_locked; + bt_lpm.request_clock_on_locked = pdata->request_clock_on_locked; + + hrtimer_init(&bt_lpm.enter_lpm_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + bt_lpm.enter_lpm_delay = ktime_set(1, 0); /* 1 sec */ + bt_lpm.enter_lpm_timer.function = enter_lpm; + + gpio_set_value(bt_lpm.gpio_wake, 0); + bt_lpm.host_wake = 0; + + irq = gpio_to_irq(bt_lpm.gpio_host_wake); + ret = request_irq(irq, host_wake_isr, IRQF_TRIGGER_HIGH, + "bt host_wake", NULL); + if (ret) + return ret; + ret = set_irq_wake(irq, 1); + if (ret) + return ret; + + return 0; +} + +static struct platform_driver bcm_bt_lpm_driver = { + .probe = bcm_bt_lpm_probe, + .driver = { + .name = "bcm_bt_lpm", + .owner = THIS_MODULE, + }, +}; + +static int __init bcm_bt_lpm_init(void) +{ + return platform_driver_register(&bcm_bt_lpm_driver); +} + +module_init(bcm_bt_lpm_init); +MODULE_DESCRIPTION("Broadcom Bluetooth low power mode driver"); +MODULE_AUTHOR("Nick Pelly "); +MODULE_LICENSE("GPL"); From 82e6f85d4e00dde604f41053dcd1fbf080ed98ad Mon Sep 17 00:00:00 2001 From: HK Chen Date: Wed, 5 Aug 2009 21:13:54 +0800 Subject: [PATCH 0456/2556] [ARM] msm: add qsd8k htc acoustic driver. Change-Id: Ifb0028791129987f6801683e2bcf177bf4ce0aa9 Signed-off-by: Brian Swetland --- arch/arm/mach-msm/htc_acoustic_qsd.c | 381 ++++++++++++++++++ .../mach-msm/include/mach/htc_acoustic_qsd.h | 29 ++ 2 files changed, 410 insertions(+) create mode 100644 arch/arm/mach-msm/htc_acoustic_qsd.c create mode 100644 arch/arm/mach-msm/include/mach/htc_acoustic_qsd.h diff --git a/arch/arm/mach-msm/htc_acoustic_qsd.c b/arch/arm/mach-msm/htc_acoustic_qsd.c new file mode 100644 index 0000000000000..8bea0d40f66b4 --- /dev/null +++ b/arch/arm/mach-msm/htc_acoustic_qsd.c @@ -0,0 +1,381 @@ +/* arch/arm/mach-msm/htc_acoustic_qsd.c + * + * Copyright (C) 2009 HTC Corporation + * + * 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 "smd_private.h" + +#define ACOUSTIC_IOCTL_MAGIC 'p' +#define ACOUSTIC_UPDATE_ADIE \ + _IOW(ACOUSTIC_IOCTL_MAGIC, 24, unsigned int) + +#define ACOUSTIC_REINIT_ACDB \ + _IOW(ACOUSTIC_IOCTL_MAGIC, 25, unsigned int) + +#define HTCACOUSTICPROG 0x30100003 +#define HTCACOUSTICVERS 0 +#define ONCRPC_ALLOC_ACOUSTIC_MEM_PROC (1) +#define ONCRPC_UPDATE_ADIE_PROC (2) +#define ONCRPC_ENABLE_AUX_PGA_LOOPBACK_PROC (3) +#define ONCRPC_FORCE_HEADSET_SPEAKER_PROC (4) +#define ONCRPC_ENABLE_LINE_IN_TX_PATH_PROC (5) +#define ONCRPC_ENABLE_AUDIO_LOOPBACK_TRIGGER_PROC (6) +#define ONCRPC_DUMP_ADIE_REG_PROC (7) +#define ONCRPC_REINIT_ACDB (8) + +#define HTCACOUSTICCBPROG 0x30100004 +#define HTCACOUSTICCBVERS 0 +#define RPC_HTC_ACOUSTICCB_NULL (0) +#define RPC_HTC_ACOUSTICCB_SET_UPLINK_MUTE_PROC (1) + +#define HTC_ACOUSTIC_TABLE_SIZE (0x20000) + +#define D(fmt, args...) printk(KERN_INFO "htc-acoustic: "fmt, ##args) +#define E(fmt, args...) printk(KERN_ERR "htc-acoustic: "fmt, ##args) + +static uint32_t htc_acoustic_vir_addr; +static struct msm_rpc_endpoint *endpoint; +static struct mutex api_lock; +static struct mutex rpc_connect_lock; +static struct qsd_acoustic_ops *the_ops; + +void acoustic_register_ops(struct qsd_acoustic_ops *ops) +{ + the_ops = ops; +} + +static int is_rpc_connect(void) +{ + mutex_lock(&rpc_connect_lock); + if (endpoint == NULL) { + endpoint = msm_rpc_connect(HTCACOUSTICPROG, + HTCACOUSTICVERS, 0); + if (IS_ERR(endpoint)) { + pr_err("%s: init rpc failed! rc = %ld\n", + __func__, PTR_ERR(endpoint)); + mutex_unlock(&rpc_connect_lock); + return -1; + } + } + mutex_unlock(&rpc_connect_lock); + return 0; +} + +int turn_mic_bias_on(int on) +{ + D("%s called %d\n", __func__, on); + if (the_ops->enable_mic_bias) + the_ops->enable_mic_bias(on); + + return 0; +} +EXPORT_SYMBOL(turn_mic_bias_on); + +int force_headset_speaker_on(int enable) +{ + struct speaker_headset_req { + struct rpc_request_hdr hdr; + uint32_t enable; + } spkr_req; + + D("%s called %d\n", __func__, enable); + + if (is_rpc_connect() == -1) + return -1; + + spkr_req.enable = cpu_to_be32(enable); + return msm_rpc_call(endpoint, + ONCRPC_FORCE_HEADSET_SPEAKER_PROC, + &spkr_req, sizeof(spkr_req), 5 * HZ); +} +EXPORT_SYMBOL(force_headset_speaker_on); + +int enable_aux_loopback(uint32_t enable) +{ + struct aux_loopback_req { + struct rpc_request_hdr hdr; + uint32_t enable; + } aux_req; + + D("%s called %d\n", __func__, enable); + + if (is_rpc_connect() == -1) + return -1; + + aux_req.enable = cpu_to_be32(enable); + return msm_rpc_call(endpoint, + ONCRPC_ENABLE_AUX_PGA_LOOPBACK_PROC, + &aux_req, sizeof(aux_req), 5 * HZ); +} +EXPORT_SYMBOL(enable_aux_loopback); + +static int acoustic_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long pgoff; + int rc = -EINVAL; + size_t size; + + D("mmap\n"); + + mutex_lock(&api_lock); + + size = vma->vm_end - vma->vm_start; + + if (vma->vm_pgoff != 0) { + E("mmap failed: page offset %lx\n", vma->vm_pgoff); + goto done; + } + + if (!htc_acoustic_vir_addr) { + E("mmap failed: smem region not allocated\n"); + rc = -EIO; + goto done; + } + + pgoff = MSM_SHARED_RAM_PHYS + + (htc_acoustic_vir_addr - (uint32_t)MSM_SHARED_RAM_BASE); + pgoff = ((pgoff + 4095) & ~4095); + htc_acoustic_vir_addr = ((htc_acoustic_vir_addr + 4095) & ~4095); + + if (pgoff <= 0) { + E("pgoff wrong. %ld\n", pgoff); + goto done; + } + + if (size <= HTC_ACOUSTIC_TABLE_SIZE) { + pgoff = pgoff >> PAGE_SHIFT; + } else { + E("size > HTC_ACOUSTIC_TABLE_SIZE %d\n", size); + goto done; + } + + vma->vm_flags |= VM_IO | VM_RESERVED; + rc = io_remap_pfn_range(vma, vma->vm_start, pgoff, + size, vma->vm_page_prot); + + if (rc < 0) + E("mmap failed: remap error %d\n", rc); + +done: mutex_unlock(&api_lock); + return rc; +} + +static int acoustic_open(struct inode *inode, struct file *file) +{ + int reply_value; + int rc = -EIO; + struct set_smem_req { + struct rpc_request_hdr hdr; + uint32_t size; + } req_smem; + + struct set_smem_rep { + struct rpc_reply_hdr hdr; + int n; + } rep_smem; + + D("open\n"); + + mutex_lock(&api_lock); + + if (!htc_acoustic_vir_addr) { + if (is_rpc_connect() == -1) + goto done; + + req_smem.size = cpu_to_be32(HTC_ACOUSTIC_TABLE_SIZE); + rc = msm_rpc_call_reply(endpoint, + ONCRPC_ALLOC_ACOUSTIC_MEM_PROC, + &req_smem, sizeof(req_smem), + &rep_smem, sizeof(rep_smem), + 5 * HZ); + + reply_value = be32_to_cpu(rep_smem.n); + if (reply_value != 0 || rc < 0) { + E("open failed: ALLOC_ACOUSTIC_MEM_PROC error %d.\n", + rc); + goto done; + } + htc_acoustic_vir_addr = + (uint32_t)smem_alloc(SMEM_ID_VENDOR1, + HTC_ACOUSTIC_TABLE_SIZE); + if (!htc_acoustic_vir_addr) { + E("open failed: smem_alloc error\n"); + goto done; + } + } + + rc = 0; +done: + mutex_unlock(&api_lock); + return rc; +} + +static int acoustic_release(struct inode *inode, struct file *file) +{ + D("release\n"); + return 0; +} + +static long acoustic_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int rc, reply_value; + + D("ioctl\n"); + + mutex_lock(&api_lock); + + switch (cmd) { + case ACOUSTIC_UPDATE_ADIE: { + struct update_adie_req { + struct rpc_request_hdr hdr; + int id; + } adie_req; + + struct update_adie_rep { + struct rpc_reply_hdr hdr; + int ret; + } adie_rep; + + D("ioctl: ACOUSTIC_UPDATE_ADIE called %d.\n", current->pid); + + adie_req.id = cpu_to_be32(-1); /* update all codecs */ + rc = msm_rpc_call_reply(endpoint, + ONCRPC_UPDATE_ADIE_PROC, &adie_req, + sizeof(adie_req), &adie_rep, + sizeof(adie_rep), 5 * HZ); + + reply_value = be32_to_cpu(adie_rep.ret); + if (reply_value != 0 || rc < 0) { + E("ioctl failed: ONCRPC_UPDATE_ADIE_PROC "\ + "error %d.\n", rc); + if (rc >= 0) + rc = -EIO; + break; + } + D("ioctl: ONCRPC_UPDATE_ADIE_PROC success.\n"); + break; + } + case ACOUSTIC_REINIT_ACDB: { + struct rpc_request_hdr acdb_req; + struct update_acdb_rep { + struct rpc_reply_hdr hdr; + int ret; + } acdb_rep; + + D("ioctl: ACOUSTIC_REINIT_ACDB called %d.\n", current->pid); + + rc = msm_rpc_call_reply(endpoint, + ONCRPC_REINIT_ACDB, &acdb_req, + sizeof(acdb_req), &acdb_rep, + sizeof(acdb_rep), 5 * HZ); + + reply_value = be32_to_cpu(acdb_rep.ret); + if (reply_value != 0 || rc < 0) { + E("ioctl failed: ONCRPC_REINIT_ACDB "\ + "error %d.\n", rc); + if (rc >= 0) + rc = -EIO; + break; + } + D("ioctl: ONCRPC_REINIT_ACDB success.\n"); + break; + } + default: + E("ioctl: invalid command\n"); + rc = -EINVAL; + } + + mutex_unlock(&api_lock); + return rc; +} + +struct rpc_set_uplink_mute_args { + int mute; +}; + +static int handle_htc_acoustic_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + switch (req->procedure) { + case RPC_HTC_ACOUSTICCB_NULL: + return 0; + + case RPC_HTC_ACOUSTICCB_SET_UPLINK_MUTE_PROC: { + struct rpc_set_uplink_mute_args *args; + args = (struct rpc_set_uplink_mute_args *)(req + 1); + args->mute = be32_to_cpu(args->mute); + D("[M2A_RPC] set uplink mute: %d\n", args->mute); + q6audio_set_tx_mute(args->mute); + return 0; + } + default: + E("%s: program 0x%08x:%d: unknown procedure %d\n", + __func__, req->prog, req->vers, req->procedure); + return -ENODEV; + } +} + +static struct msm_rpc_server htc_acoustic_server = { + .prog = HTCACOUSTICCBPROG, + .vers = HTCACOUSTICCBVERS, + .rpc_call = handle_htc_acoustic_call, +}; + +static struct file_operations acoustic_fops = { + .owner = THIS_MODULE, + .open = acoustic_open, + .release = acoustic_release, + .mmap = acoustic_mmap, + .unlocked_ioctl = acoustic_ioctl, +}; + +static struct miscdevice acoustic_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "htc-acoustic", + .fops = &acoustic_fops, +}; + +static int __init acoustic_init(void) +{ + mutex_init(&api_lock); + mutex_init(&rpc_connect_lock); + msm_rpc_create_server(&htc_acoustic_server); + return misc_register(&acoustic_misc); +} + +static void __exit acoustic_exit(void) +{ + misc_deregister(&acoustic_misc); +} + +module_init(acoustic_init); +module_exit(acoustic_exit); + diff --git a/arch/arm/mach-msm/include/mach/htc_acoustic_qsd.h b/arch/arm/mach-msm/include/mach/htc_acoustic_qsd.h new file mode 100644 index 0000000000000..2139bf9f903a5 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/htc_acoustic_qsd.h @@ -0,0 +1,29 @@ +/* include/asm/mach-msm/htc_acoustic_qsd.h + * + * Copyright (C) 2009 HTC Corporation. + * + * 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 _ARCH_ARM_MACH_MSM_HTC_ACOUSTIC_QSD_H_ +#define _ARCH_ARM_MACH_MSM_HTC_ACOUSTIC_QSD_H_ + +struct qsd_acoustic_ops { + void (*enable_mic_bias)(int en); +}; + +void acoustic_register_ops(struct qsd_acoustic_ops *ops); + +int turn_mic_bias_on(int on); +int force_headset_speaker_on(int enable); +int enable_aux_loopback(uint32_t enable); + +#endif + From 1721ef94f0d028fbc58ea1b543b7e9765b48c63a Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Fri, 7 Aug 2009 23:12:21 -0700 Subject: [PATCH 0457/2556] [ARM] msm: add a HTC power supply driver Based on the htc_battery driver, this driver only publishes the AC and USB power supplies, since battery monitoring and charging is handled by the battery gauge driver. Change-Id: I345c20b393c26d4f94c8fc74f0cfeddd3f34fd46 Signed-off-by: Brian Swetland --- arch/arm/mach-msm/Kconfig | 7 + arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/htc_power_supply.c | 198 +++++++++++++++++++++++++++ 3 files changed, 206 insertions(+) create mode 100644 arch/arm/mach-msm/htc_power_supply.c diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 870718ada5fec..2390db7b215c8 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -223,6 +223,13 @@ config TROUT_BATTCHG default y bool "Trout battery / charger driver" +config HTC_PWRSPLY + depends on MSM_ONCRPCROUTER && POWER_SUPPLY && !TROUT_BATTCHG + default y + bool "HTC Power supply driver" + help + Used by HTC devices with a dedicated battery gauge" + config HTC_PWRSINK depends on MSM_SMD default y diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 09c614e81df44..29d16e5b0683d 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -77,4 +77,5 @@ obj-y += gpio.o endif obj-$(CONFIG_HTC_PWRSINK) += htc_pwrsink.o +obj-$(CONFIG_HTC_PWRSPLY) += htc_power_supply.o obj-$(CONFIG_HTC_HEADSET) += htc_headset.o diff --git a/arch/arm/mach-msm/htc_power_supply.c b/arch/arm/mach-msm/htc_power_supply.c new file mode 100644 index 0000000000000..29cafd464d9cd --- /dev/null +++ b/arch/arm/mach-msm/htc_power_supply.c @@ -0,0 +1,198 @@ +/* arch/arm/mach-msm/htc_battery.c + * + * Copyright (C) 2008 HTC Corporation. + * Copyright (C) 2008 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 +#include +#include +#include + +static char *supply_list[] = { + "battery", +}; + +static int vbus_present; +static int usb_status; + +static int power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + if (psp != POWER_SUPPLY_PROP_ONLINE) + return -EINVAL; + + if (psy->type == POWER_SUPPLY_TYPE_MAINS) { + val->intval = (vbus_present && (usb_status == 2)); + } else { + val->intval = (vbus_present && (usb_status == 1)); + } + return 0; +} + +static enum power_supply_property power_properties[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static struct power_supply ac_supply = { + .name = "ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .supplied_to = supply_list, + .num_supplicants = ARRAY_SIZE(supply_list), + .properties = power_properties, + .num_properties = ARRAY_SIZE(power_properties), + .get_property = power_get_property, +}; + +static struct power_supply usb_supply = { + .name = "usb", + .type = POWER_SUPPLY_TYPE_USB, + .supplied_to = supply_list, + .num_supplicants = ARRAY_SIZE(supply_list), + .properties = power_properties, + .num_properties = ARRAY_SIZE(power_properties), + .get_property = power_get_property, +}; + +/* rpc related */ +#define APP_BATT_PDEV_NAME "rs30100001:00000000" +#define APP_BATT_PROG 0x30100001 +#define APP_BATT_VER MSM_RPC_VERS(0,0) +#define HTC_PROCEDURE_BATTERY_NULL 0 +#define HTC_PROCEDURE_GET_BATT_LEVEL 1 +#define HTC_PROCEDURE_GET_BATT_INFO 2 +#define HTC_PROCEDURE_GET_CABLE_STATUS 3 +#define HTC_PROCEDURE_SET_BATT_DELTA 4 + +static struct msm_rpc_endpoint *endpoint; + +struct battery_info_reply { + u32 batt_id; /* Battery ID from ADC */ + u32 batt_vol; /* Battery voltage from ADC */ + u32 batt_temp; /* Battery Temperature (C) from formula and ADC */ + u32 batt_current; /* Battery current from ADC */ + u32 level; /* formula */ + u32 charging_source; /* 0: no cable, 1:usb, 2:AC */ + u32 charging_enabled; /* 0: Disable, 1: Enable */ + u32 full_bat; /* Full capacity of battery (mAh) */ +}; + +static int htc_battery_probe(struct platform_device *pdev) +{ + struct rpc_request_hdr req; + struct htc_get_batt_info_rep { + struct rpc_reply_hdr hdr; + struct battery_info_reply info; + } rep; + + int rc; + + endpoint = msm_rpc_connect(APP_BATT_PROG, APP_BATT_VER, 0); + if (IS_ERR(endpoint)) { + printk(KERN_ERR "%s: init rpc failed! rc = %ld\n", + __FUNCTION__, PTR_ERR(endpoint)); + return PTR_ERR(endpoint); + } + + /* must do this or we won't get cable status updates */ + rc = msm_rpc_call_reply(endpoint, HTC_PROCEDURE_GET_BATT_INFO, + &req, sizeof(req), + &rep, sizeof(rep), + 5 * HZ); + if (rc < 0) + printk(KERN_ERR "%s: get info failed\n", __FUNCTION__); + + power_supply_register(&pdev->dev, &ac_supply); + power_supply_register(&pdev->dev, &usb_supply); + + return 0; +} + +static struct platform_driver htc_battery_driver = { + .probe = htc_battery_probe, + .driver = { + .name = APP_BATT_PDEV_NAME, + .owner = THIS_MODULE, + }, +}; + +/* batt_mtoa server definitions */ +#define BATT_MTOA_PROG 0x30100000 +#define BATT_MTOA_VERS 0 +#define RPC_BATT_MTOA_NULL 0 +#define RPC_BATT_MTOA_SET_CHARGING_PROC 1 +#define RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC 2 +#define RPC_BATT_MTOA_LEVEL_UPDATE_PROC 3 + +struct rpc_batt_mtoa_cable_status_update_args { + int status; +}; + +static int handle_battery_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + struct rpc_batt_mtoa_cable_status_update_args *args; + + if (req->procedure != RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC) + return 0; + + args = (struct rpc_batt_mtoa_cable_status_update_args *)(req + 1); + args->status = be32_to_cpu(args->status); + pr_info("cable_status_update: status=%d\n",args->status); + + args->status = !!args->status; + + if (vbus_present != args->status) { + vbus_present = args->status; + msm_hsusb_set_vbus_state(vbus_present); + power_supply_changed(&ac_supply); + power_supply_changed(&usb_supply); + } + return 0; +} + +void notify_usb_connected(int status) +{ + printk("### notify_usb_connected(%d) ###\n", status); + usb_status = status; + power_supply_changed(&ac_supply); + power_supply_changed(&usb_supply); +} + +int is_ac_power_supplied(void) +{ + return vbus_present && (usb_status == 2); +} + +static struct msm_rpc_server battery_server = { + .prog = BATT_MTOA_PROG, + .vers = BATT_MTOA_VERS, + .rpc_call = handle_battery_call, +}; + +static int __init htc_battery_init(void) +{ + platform_driver_register(&htc_battery_driver); + msm_rpc_create_server(&battery_server); + return 0; +} + +module_init(htc_battery_init); +MODULE_DESCRIPTION("HTC Battery Driver"); +MODULE_LICENSE("GPL"); + From 0df00c02388ee036901a26b62a44c9fa70d69626 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Thu, 12 Nov 2009 18:25:08 -0800 Subject: [PATCH 0458/2556] mahimahi: enable usb power detection on vbus present This is the behaviour we had in 1.0, cupcake, and donut, and ensures that we will always charge if power is present (but only do fast charge if we recognize the supply). Signed-off-by: Brian Swetland --- arch/arm/mach-msm/htc_power_supply.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/htc_power_supply.c b/arch/arm/mach-msm/htc_power_supply.c index 29cafd464d9cd..566b070ec2712 100644 --- a/arch/arm/mach-msm/htc_power_supply.c +++ b/arch/arm/mach-msm/htc_power_supply.c @@ -40,7 +40,7 @@ static int power_get_property(struct power_supply *psy, if (psy->type == POWER_SUPPLY_TYPE_MAINS) { val->intval = (vbus_present && (usb_status == 2)); } else { - val->intval = (vbus_present && (usb_status == 1)); + val->intval = vbus_present; } return 0; } From a88dbaa5d037ebc155bf298b125e3ad2e2972e13 Mon Sep 17 00:00:00 2001 From: Eric Olsen Date: Thu, 1 Oct 2009 15:04:38 -0700 Subject: [PATCH 0459/2556] [ARM] msm: Add support for HTC 3.5mm headsets Change-Id: Ieac522e86d5589e1d930b3026b6610618657ad16 Signed-off-by: Eric Olsen Cc: Arec_Kao@htc.com --- arch/arm/mach-msm/Kconfig | 6 + arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/htc_35mm_jack.c | 387 ++++++++++++++++++ .../arm/mach-msm/include/mach/htc_35mm_jack.h | 31 ++ arch/arm/mach-msm/include/mach/htc_headset.h | 3 + 5 files changed, 428 insertions(+) create mode 100644 arch/arm/mach-msm/htc_35mm_jack.c create mode 100644 arch/arm/mach-msm/include/mach/htc_35mm_jack.h diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 2390db7b215c8..b7e59c714e61a 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -218,6 +218,12 @@ config HTC_HEADSET headset, on the trout platform. Can be used with the msm serial debugger, but not with serial console. +config HTC_35MM_JACK + bool "HTC 3.5mm headset jack" + default n + help + Provides support for 3.5mm headset jack devices, like wired headsets. + config TROUT_BATTCHG depends on MACH_TROUT && POWER_SUPPLY default y diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 29d16e5b0683d..795e3fedb3fcc 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -79,3 +79,4 @@ endif obj-$(CONFIG_HTC_PWRSINK) += htc_pwrsink.o obj-$(CONFIG_HTC_PWRSPLY) += htc_power_supply.o obj-$(CONFIG_HTC_HEADSET) += htc_headset.o +obj-$(CONFIG_HTC_35MM_JACK) += htc_35mm_jack.o diff --git a/arch/arm/mach-msm/htc_35mm_jack.c b/arch/arm/mach-msm/htc_35mm_jack.c new file mode 100644 index 0000000000000..17ebcc5194bbf --- /dev/null +++ b/arch/arm/mach-msm/htc_35mm_jack.c @@ -0,0 +1,387 @@ +/* arch/arm/mach-msm/htc_35mm_jack.c + * + * Copyright (C) 2009 HTC, Inc. + * Author: Arec Kao + * Copyright (C) 2009 Google, Inc. + * Author: Eric Olsen + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_HTC_AUDIOJACK +#include +#endif + +/* #define CONFIG_DEBUG_H2W */ + +#define H2WI(fmt, arg...) \ + printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg) +#define H2WE(fmt, arg...) \ + printk(KERN_ERR "[H2W] %s " fmt "\r\n", __func__, ## arg) + +#ifdef CONFIG_DEBUG_H2W +#define H2W_DBG(fmt, arg...) \ + printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg) +#else +#define H2W_DBG(fmt, arg...) do {} while (0) +#endif + +void detect_h2w_do_work(struct work_struct *w); + +static struct workqueue_struct *detect_wq; +static struct workqueue_struct *button_wq; + +static DECLARE_DELAYED_WORK(detect_h2w_work, detect_h2w_do_work); + +static void insert_35mm_do_work(struct work_struct *work); +static DECLARE_WORK(insert_35mm_work, insert_35mm_do_work); +static void remove_35mm_do_work(struct work_struct *work); +static DECLARE_WORK(remove_35mm_work, remove_35mm_do_work); +static void button_35mm_do_work(struct work_struct *work); +static DECLARE_WORK(button_35mm_work, button_35mm_do_work); + +struct h35_info { + struct mutex mutex_lock; + struct switch_dev hs_change; + unsigned long insert_jiffies; + int ext_35mm_status; + int is_ext_insert; + int key_code; + int mic_bias_state; + struct input_dev *input; + + struct wake_lock headset_wake_lock; +}; + +static struct h35mm_platform_data *pd; +static struct h35_info *hi; + +static ssize_t h35mm_print_name(struct switch_dev *sdev, char *buf) +{ + return sprintf(buf, "Headset\n"); +} + +static void button_35mm_do_work(struct work_struct *work) +{ + int key = 0; + int pressed = 0; + + if (!hi->is_ext_insert) { + /* no headset ignor key event */ + H2WI("3.5mm headset is plugged out, skip report key event"); + return; + } + + switch (hi->key_code) { + case 0x1: /* Play/Pause */ + H2WI("3.5mm RC: Play Pressed"); + key = KEY_MEDIA; + pressed = 1; + break; + case 0x2: + H2WI("3.5mm RC: BACKWARD Pressed"); + key = KEY_PREVIOUSSONG; + pressed = 1; + break; + case 0x3: + H2WI("3.5mm RC: FORWARD Pressed"); + key = KEY_NEXTSONG; + pressed = 1; + break; + case 0x81: /* Play/Pause */ + H2WI("3.5mm RC: Play Released"); + key = KEY_MEDIA; + pressed = 0; + break; + case 0x82: + H2WI("3.5mm RC: BACKWARD Released"); + key = KEY_PREVIOUSSONG; + pressed = 0; + break; + case 0x83: + H2WI("3.5mm RC: FORWARD Released"); + key = KEY_NEXTSONG; + pressed = 0; + break; + default: + H2WI("3.5mm RC: Unknown Button (0x%x) Pressed", hi->key_code); + return; + } + input_report_key(hi->input, key, pressed); + input_sync(hi->input); + + wake_lock_timeout(&hi->headset_wake_lock, 1.5*HZ); +} + +static void remove_35mm_do_work(struct work_struct *work) +{ + wake_lock_timeout(&hi->headset_wake_lock, 2.5*HZ); + + H2W_DBG(""); + /*To solve the insert, remove, insert headset problem*/ + if (time_before_eq(jiffies, hi->insert_jiffies)) + msleep(800); + + if (hi->is_ext_insert) { + H2WI("Skip 3.5mm headset plug out!!!"); + return; + } + + pr_info("3.5mm_headset plug out\n"); + + if (pd->key_event_disable != NULL) + pd->key_event_disable(); + + if (hi->mic_bias_state) { + turn_mic_bias_on(0); + hi->mic_bias_state = 0; + } + hi->ext_35mm_status = 0; + + /* Notify framework via switch class */ + mutex_lock(&hi->mutex_lock); + switch_set_state(&hi->hs_change, hi->ext_35mm_status); + mutex_unlock(&hi->mutex_lock); +} + +static void insert_35mm_do_work(struct work_struct *work) +{ + H2W_DBG(""); + hi->insert_jiffies = jiffies + 1*HZ; + + wake_lock_timeout(&hi->headset_wake_lock, 1.5*HZ); + + if (hi->is_ext_insert) { + pr_info("3.5mm_headset plug in\n"); + + if (pd->key_event_enable != NULL) + pd->key_event_enable(); + + /* Turn On Mic Bias */ + if (!hi->mic_bias_state) { + turn_mic_bias_on(1); + hi->mic_bias_state = 1; + /* Wait for pin stable */ + msleep(300); + } + + /* Detect headset with or without microphone */ + if(pd->headset_has_mic) { + if (pd->headset_has_mic() == 0) { + /* without microphone */ + pr_info("3.5mm without microphone\n"); + hi->ext_35mm_status = BIT_HEADSET_NO_MIC; + } else { /* with microphone */ + pr_info("3.5mm with microphone\n"); + hi->ext_35mm_status = BIT_HEADSET; + } + } else { + /* Assume no mic */ + pr_info("3.5mm without microphone\n"); + hi->ext_35mm_status = BIT_HEADSET_NO_MIC; + } + hi->ext_35mm_status |= BIT_35MM_HEADSET; + + /* Notify framework via switch class */ + mutex_lock(&hi->mutex_lock); + switch_set_state(&hi->hs_change, hi->ext_35mm_status); + mutex_unlock(&hi->mutex_lock); + } +} + +int htc_35mm_key_event(int keycode) +{ + hi->key_code = keycode; + + if ((hi->ext_35mm_status & BIT_HEADSET) == 0) { + + pr_info("Key press with no mic. Retrying detection\n"); + queue_work(detect_wq, &insert_35mm_work); + } else + queue_work(button_wq, &button_35mm_work); + + return 0; +} + +int htc_35mm_jack_plug_event(int insert) +{ + if (!hi) { + pr_err("Plug event before driver init\n"); + return -1; + } + + mutex_lock(&hi->mutex_lock); + hi->is_ext_insert = insert; + mutex_unlock(&hi->mutex_lock); + + H2WI(" %d", hi->is_ext_insert); + if (!hi->is_ext_insert) + queue_work(detect_wq, &remove_35mm_work); + else + queue_work(detect_wq, &insert_35mm_work); + return 1; +} + +static int htc_35mm_probe(struct platform_device *pdev) +{ + int ret; + + pd = pdev->dev.platform_data; + + pr_info("H2W: htc_35mm_jack driver register\n"); + + hi = kzalloc(sizeof(struct h35_info), GFP_KERNEL); + if (!hi) + return -ENOMEM; + + hi->ext_35mm_status = 0; + hi->is_ext_insert = 0; + hi->mic_bias_state = 0; + + mutex_init(&hi->mutex_lock); + + wake_lock_init(&hi->headset_wake_lock, WAKE_LOCK_SUSPEND, "headset"); + + hi->hs_change.name = "h2w"; + hi->hs_change.print_name = h35mm_print_name; + ret = switch_dev_register(&hi->hs_change); + if (ret < 0) + goto err_switch_dev_register; + + detect_wq = create_workqueue("detection"); + if (detect_wq == NULL) { + ret = -ENOMEM; + goto err_create_detect_work_queue; + } + + button_wq = create_workqueue("button"); + if (button_wq == NULL) { + ret = -ENOMEM; + goto err_create_button_work_queue; + } + + hi->input = input_allocate_device(); + if (!hi->input) { + ret = -ENOMEM; + goto err_request_input_dev; + } + + hi->input->name = "h2w headset"; + set_bit(EV_SYN, hi->input->evbit); + set_bit(EV_KEY, hi->input->evbit); + set_bit(KEY_MEDIA, hi->input->keybit); + set_bit(KEY_NEXTSONG, hi->input->keybit); + set_bit(KEY_PLAYPAUSE, hi->input->keybit); + set_bit(KEY_PREVIOUSSONG, hi->input->keybit); + set_bit(KEY_MUTE, hi->input->keybit); + set_bit(KEY_VOLUMEUP, hi->input->keybit); + set_bit(KEY_VOLUMEDOWN, hi->input->keybit); + set_bit(KEY_END, hi->input->keybit); + set_bit(KEY_SEND, hi->input->keybit); + + ret = input_register_device(hi->input); + if (ret < 0) + goto err_register_input_dev; + + /* Enable plug events*/ + if (pd->plug_event_enable == NULL) { + ret = -ENOMEM; + goto err_enable_plug_event; + } + if (pd->plug_event_enable() != 1) { + ret = -ENOMEM; + goto err_enable_plug_event; + } + + return 0; + +err_enable_plug_event: +err_register_input_dev: + input_free_device(hi->input); +err_request_input_dev: + destroy_workqueue(button_wq); +err_create_button_work_queue: + destroy_workqueue(detect_wq); +err_create_detect_work_queue: + switch_dev_unregister(&hi->hs_change); +err_switch_dev_register: + kzfree(hi); + pr_err("H2W: Failed to register driver\n"); + + return ret; +} + +static int htc_35mm_remove(struct platform_device *pdev) +{ + H2W_DBG(""); + switch_dev_unregister(&hi->hs_change); + kzfree(hi); + +#if 0 /* Add keys later */ + input_unregister_device(hi->input); +#endif + return 0; +} + +static struct platform_driver htc_35mm_driver = { + .probe = htc_35mm_probe, + .remove = htc_35mm_remove, + .driver = { + .name = "htc_headset", + .owner = THIS_MODULE, + }, +}; + +static int __init htc_35mm_init(void) +{ + H2W_DBG(""); + return platform_driver_register(&htc_35mm_driver); +} + +static void __exit htc_35mm_exit(void) +{ + platform_driver_unregister(&htc_35mm_driver); +} + +module_init(htc_35mm_init); +module_exit(htc_35mm_exit); + +MODULE_AUTHOR("Eric Olsen "); +MODULE_DESCRIPTION("HTC 3.5MM Driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/include/mach/htc_35mm_jack.h b/arch/arm/mach-msm/include/mach/htc_35mm_jack.h new file mode 100644 index 0000000000000..9473b587f01f4 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/htc_35mm_jack.h @@ -0,0 +1,31 @@ +/* arch/arm/mach-msm/include/mach/htc_35mm_jack.h + * + * Copyright (C) 2009 HTC, Inc. + * Author: Arec Kao + * + * 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 HTC_35MM_REMOTE_H +#define HTC_35MM_REMOTE_H + +/* Driver interfaces */ +int htc_35mm_jack_plug_event(int insert); +int htc_35mm_key_event(int key); + +/* Platform Specific Callbacks */ +struct h35mm_platform_data { + int (*plug_event_enable)(void); + int (*headset_has_mic)(void); + int (*key_event_enable)(void); + int (*key_event_disable)(void); +}; +#endif diff --git a/arch/arm/mach-msm/include/mach/htc_headset.h b/arch/arm/mach-msm/include/mach/htc_headset.h index 7fdc85994173f..5bea7a2bed3d7 100644 --- a/arch/arm/mach-msm/include/mach/htc_headset.h +++ b/arch/arm/mach-msm/include/mach/htc_headset.h @@ -39,6 +39,9 @@ struct h2w_platform_data { #define BIT_TTY (1 << 2) #define BIT_FM_HEADSET (1 << 3) #define BIT_FM_SPEAKER (1 << 4) +#define BIT_TTY_VCO (1 << 5) +#define BIT_TTY_HCO (1 << 6) +#define BIT_35MM_HEADSET (1 << 7) enum { H2W_NO_DEVICE = 0, From 504922ff040dab34d024224aaf5ea79cc3cc441b Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 19 Oct 2009 04:02:14 -0700 Subject: [PATCH 0460/2556] [ARM] msm: htc: vibrator for qsd8x50 is on a diff gpio Change-Id: I4f0ba7b856a27d60205a674ab99092fcd2493331 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/msm_vibrator.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/mach-msm/msm_vibrator.c b/arch/arm/mach-msm/msm_vibrator.c index 21b024b544c5a..1adca194fbb70 100644 --- a/arch/arm/mach-msm/msm_vibrator.c +++ b/arch/arm/mach-msm/msm_vibrator.c @@ -29,7 +29,11 @@ #define PM_LIBVERS MSM_RPC_VERS(1,1) #endif +#ifdef CONFIG_ARCH_QSD8X50 +#define HTC_PROCEDURE_SET_VIB_ON_OFF 22 +#else #define HTC_PROCEDURE_SET_VIB_ON_OFF 21 +#endif #define PMIC_VIBRATOR_LEVEL (3000) static struct work_struct vibrator_work; From 4c645ff3570e855102468e008daefee772b22465 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 8 Feb 2010 21:07:46 -0800 Subject: [PATCH 0461/2556] [ARM] msm: mahimahi: Add support for the mahimahi device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To use the reworked devices with debug uart over sdard one will need to "fastboot oem writeconfig 0 c" and make sure that config 8 does not have 0x400 set. Change-Id: I8f065b941143017f60d17a4a52c3c7beeb626620 Signed-off-by: Dima Zavin Cc: Brian Swetland Cc: Arve Hjønnevåg Cc: Solomon Chiu Cc: Haley Teng Cc: Eric Olsen Cc: Iliyan Malchev Cc: Dmitry Shmidt Cc: Mike Lockwood Cc: San Mehat [ARM] msm: mahimahi: Update the memory map. - move framebuffer to SMI and mdp pmem to bank 2 of EBI - remove the gpu pmem regions since we now use the MMU - move the adsp region to bank 2, and make it bigger (41MB vs 32MB) - move ram console to SMI Change-Id: I88b4033e98374fc038609fbbb1c7e5cbed4f87c4 Signed-off-by: Dima Zavin [ARM] msm: mahimahi: Expand memory available to kernel to 219MB Change-Id: I59e69ce4209d16ce9804d3fa81814c9d0bda9a03 Signed-off-by: Dima Zavin [ARM] msm: mahimahi: Read bluetooth address from ATAG and export in sysfs. Signed-off-by: Nick Pelly mahimahi: pass set_slp_n() routine to ds2482 Saves about 0.25mA Signed-off-by: Brian Swetland [ARM] msm: mahimahi: make adsp pmem cacheable Signed-off-by: Erik Gilling [ARM] msm: mahimahi: Swap locations of back and home virtual keys on PVT Change-Id: Ia730e21230684ae5b8c6f359cf96e1c536ba40c2 Signed-off-by: Mike Lockwood [ARM] msm: mahimahi: Add an 8mb pmem region for camera snapshot Change-Id: I12e6ca74af7998a85874b3c05b10b174d608c7be Signed-off-by: Dima Zavin [ARM] msm: mahimahi: adjust usb signal strength in ulpi phy register Change-Id: I2b169de0016bd063a228cd05c9376f60497602c8 Signed-off-by: Haley Teng Acked-by: Dima Zavin mahimahi: update board file for new audience driver Signed-off-by: Brian Swetland Change the area of virtual key in PVT devices. Signed-off-by: viral wang [ARM] msm: mahimahi: Increase max voltage to 1.275 from 1.25 Signed-off-by: Mike Chan [ARM] mahimahi: wifi: Add sd_oobonly=1 to calibration data Signed-off-by: Dmitry Shmidt [ARM] mahimahi: Add bcm4329 wlan memory preallocation Signed-off-by: Dmitry Shmidt [ARM] mahimahi: Fix wlan calibration update Signed-off-by: Daniel Chen Signed-off-by: Dmitry Shmidt [ARM] mahimahi: Update wlan calibration data size Signed-off-by: Dmitry Shmidt [ARM] mahimahi: Fix wlan IRQ type Signed-off-by: Dmitry Shmidt [ARM] mahimahi: fix a race condition on setting the flash mode Signed-off-by: Iliyan Malchev [ARM] msm: mahimahi: Send current light sensor value when sensor is enabled. Change-Id: Icb42cfaf4cabe76864da14a16b85fc1f729c5d5d Signed-off-by: Mike Lockwood [ARM] msm: mahimahi: Fix problem with zero light sensor level not being reported After enabling the light sensor, we now send a -1 light sensor level to invalidate the cached value in the input famework and force an event to get sent to user space. Otherwise, if the light sensor value is zero when the sensor is enabled, no value will be reported. Change-Id: I4d45fc945a6d0e8c3d32cc9bd81180607497a35f Signed-off-by: Mike Lockwood [ARM] msm: mahimahi: Add microp jogball color support. Added jogball-backlight attr "color" for jogball-backlight color adjustment. Signed-off-by: Eric.SP Huang Signed-off-by: Eric Olsen [ARM] msm: mahimahi: Add support for light sensor factory calibration. Change-Id: I3030328f08298b9f1675cffd601ad53ef405a359 Signed-off-by: Eric.SP Huang Signed-off-by: Mike Lockwood [ARM] msm: mahimahi: Avoid flicker when writing same value to button backlight. Change-Id: I8984964afbca7d056cf99e9f5702130e44c73df6 Signed-off-by: Mike Lockwood Clean up driver debug message from the logs Change-Id: Id8524eee501c9a1644537454a59f9c7598e61dfe Signed-off-by: Eric Olsen [ARM] msm: mahimahi: Make calling brightness_set functions safe from interrupts Moves all the work that can sleep into workqueues The blank and unblank functions are already called from workqueues in the LCD driver, so no changes are needed for them. Signed-off-by: Colin Cross Change-Id: Iabb73903b6bc5a76879710759f2b13a0ee85f5b8 [ARM] msm: mahimahi: Fix build error in board-mahimahi-microp.c Signed-off-by: Colin Cross Change-Id: Icc04e853502a3cd9a04dd7f86fc29dd8ba15ace8 [ARM] msm: mahimahi: add a brightness level 20 Lineraly interpolate gamma values for brightness levels which do not have an explicit table. Also restrict levels to pre-defined set. Signed-off-by: Erik Gilling [ARM] msm: mahimahi: panel: Increase granularity of panel Panel now supports brightness granularity in increments of 2. Signed-off-by: Mike Chan [ARM] msm: mahimahi: Move lcd initialization to device_init Change-Id: Ib9b317a2cf62a09767eb0e305af99dcdf5913064 Signed-off-by: Colin Cross [ARM] msm: panel: Fix lcd panel gamma initialization Fixes a brightness jump during boot by keeping the gamma table that the bootloader wrote until the first time a new brightness level is set Change-Id: I1c1833ce4de75128c5375f6f11626a600c446d57 Signed-off-by: Colin Cross [ARM] msm: mahimahi: panel: Modify panel init, blank, and unblank sequences to match datasheet Change-Id: I9f78847f85b75b5ab0e18e081a76df79ce027111 Signed-off-by: Colin Cross [ARM] mahimahi: mmc: Update SD drive strengths as per HTC Signed-off-by: San Mehat [ARM] mach-msm: mahimahi-mmc: Add embedded sdio override table for bcm wifi Signed-off-by: San Mehat qsd8k: fix headset playback pop sound issue. Signed-off-by: Brian Swetland mahimahi: add delay on mic init to avoid noise Signed-off-by: Brian Swetland [ARM] msm: mahimahi-audio: Request gpios. Change-Id: I16687b0ad5014ed971dfb7875c10d59af56188ae Signed-off-by: Arve Hjønnevåg [ARM] msm: mahimahi: Use bcm_bt_lpm on mahimahi. Change-Id: Ied56a75e008820e1ed94b317bdac533c050a220b Signed-off-by: Nick Pelly [ARM] msm: microp: Add support for PVT headset buttons. Signed-off-by: Eric Olsen [ARM] msm: mahimahi: Change the power collapse/sleep frequency to be 19.2MHz This will make us go in and out of power collapse on the TCXO. This aligns us closer to what other Qualcomm supported OS's are doing and eliminates a possible instability. Change-Id: I2447323f8ab15132a2ac916db058694bfcd0bd81 Signed-off-by: Dima Zavin [ARM] msm: board-mahimahi: Disable wfi rampdown and busy spin Signed-off-by: Mike Chan [ARM] msm: mahimahi: Properly specify power-collapse speed in khz and not hz Change-Id: Ia962af83ae7f9114a59e39ad121edae2f00b46f4 Signed-off-by: Dima Zavin [ARM] msm: mahimahi: Use the MPLL (245MHz) for power collapse. Change-Id: I7a0d55cce9f27e2ad26fa7025c96d9b3ee265eda Signed-off-by: Dima Zavin mahimahi: update usb vid and pid Signed-off-by: Brian Swetland [ARM] msm: microp: Set ALS zone 0 to lowest/darkest threshold Signed-off-by: Mike Chan [ARM] msm: mahimahi: MicroP driver Additions for new LED scheme. Signed-off-by: Eric Olsen [ARM] msm: microp: Fix error in lightsensor for reporting level 0 Zero base error causing level 0 to be reported as level 1. Signed-off-by: Mike Chan [ARM] msm: microp: Change threshold for light sensor level 1 Change-Id: Ic5d75746567d5f023688f566b4117ee7b0cffb21 Signed-off-by: Mike Chan [ARM] msm: microp: Clean up logging. Added and error log and removed unneeded messages Signed-off-by: Eric Olsen [ARM] msm: microp: Lower light sensor threshold for level 7 Change-Id: Ia2cd7f1751472364c81bbdd13fcc3e99c3d5d8fe Signed-off-by: Mike Chan [ARM] msm: microp: Make sure we send an event when enabling the light sensor. Change-Id: Ia1f8aba961c5a919f1406e7547beb04f0dd4bcf7 Signed-off-by: Mike Lockwood [ARM] msm: mahimahi: Add logging to gsensor data for debugging orientation issue Signed-off-by: Eric Olsen [ARM] msm: mahimahi: Ensure 1ms delay between microp requests Signed-off-by: Eric Olsen [ARM] msm: microp: Disable logging for release. Signed-off-by: Eric Olsen [ARM] msm: mahimahi: Fix possible race condition in lightsensor_enable() Change-Id: I70ee11cfe262029e4075c96f3d899de4a7f2d327 Signed-off-by: Mike Lockwood [ARM] msm: mahimahi: send a light sensor reading 1/2 second after enabling. This is a temporary hack to work around a race condition in the Sensor Manager. Change-Id: Ia770ef9da465704172e00f1a2c52d3ecbd21b012 Signed-off-by: Mike Lockwood [ARM] msm: mahimahi: Add support for docks. We use a 1-wire interface to the dock to get the dock id, get its bluetooth address and to set a bluetooth pin code. Change-Id: I55a1302feabbe8861fd6b85a475407924e5e55a9 Signed-off-by: Arve Hjønnevåg [ARM] msm: mahimahi: Hold wakelock while detecting dock Change-Id: Idcb87dfc291871859d02531f9aaf2df3fb394342 Signed-off-by: Arve Hjønnevåg [ARM] msm: mahimahi: Request dock gpio. Change-Id: Ic196a72413a411c78a471906b2593801300fdf73 Signed-off-by: Arve Hjønnevåg [ARM] msm: mahimahi: reserve 3MB for gpu sharedmem for contexts/pagetables/etc Change-Id: I875de434bd9b9a99761df5b973b32c839d78edfa Signed-off-by: Dima Zavin [ARM] qsd8k: mahimahi: correct MCLK driving strength Signed-off-by: Iliyan Malchev [ARM] msm: mahimahi: Raise the max dcdc1 pmic output (MSMC2 voltage) to 1.3V Change-Id: Id70ce5c0eec23b324c1123e34da1301a06306ba2 Signed-off-by: Dima Zavin [ARM] msm: mahimahi: use the mpll when going into wfi Though Qualcomm believes that going into wfi at any speed on scpll is valid, since it has not been as thoroughly tested, we should use the mpll as that is what most of their test cycles have verified. Change-Id: Ia14cec6706ec1fda52ac9885a88089804ade5e82 Signed-off-by: Dima Zavin [ARM] msm: mahimahi: Request gpios Change-Id: Ie98a946f15ae3cf2e7c912d00b728f1e05ce4fb1 Signed-off-by: Arve Hjønnevåg [ARM] msm: mahimahi: Add platform data for android USB gadget driver Signed-off-by: Mike Lockwood [ARM] msm: mahimahi: board file changes for USB gadget diag driver. Change-Id: I36c4fe54aba402903c7afacf23a74266455ff463 Signed-off-by: Mike Lockwood [ARM] msm: mahimahi: Remove enable gpio from the cm6302 platform data. Signed-off-by: Eric Olsen [ARM] msm: mahimahi: Add support for ACM serial and RNDIS ethernet USB functions. Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/board-mahimahi-audio.c | 202 ++ arch/arm/mach-msm/board-mahimahi-flashlight.c | 279 ++ arch/arm/mach-msm/board-mahimahi-flashlight.h | 20 + arch/arm/mach-msm/board-mahimahi-keypad.c | 224 ++ arch/arm/mach-msm/board-mahimahi-microp.c | 2235 +++++++++++++++++ arch/arm/mach-msm/board-mahimahi-mmc.c | 415 +++ arch/arm/mach-msm/board-mahimahi-panel.c | 635 +++++ arch/arm/mach-msm/board-mahimahi-rfkill.c | 122 + arch/arm/mach-msm/board-mahimahi-wifi.c | 140 ++ arch/arm/mach-msm/board-mahimahi.c | 947 ++++++- arch/arm/mach-msm/board-mahimahi.h | 123 + 11 files changed, 5336 insertions(+), 6 deletions(-) create mode 100644 arch/arm/mach-msm/board-mahimahi-audio.c create mode 100644 arch/arm/mach-msm/board-mahimahi-flashlight.c create mode 100644 arch/arm/mach-msm/board-mahimahi-flashlight.h create mode 100644 arch/arm/mach-msm/board-mahimahi-keypad.c create mode 100644 arch/arm/mach-msm/board-mahimahi-microp.c create mode 100644 arch/arm/mach-msm/board-mahimahi-mmc.c create mode 100644 arch/arm/mach-msm/board-mahimahi-panel.c create mode 100644 arch/arm/mach-msm/board-mahimahi-rfkill.c create mode 100644 arch/arm/mach-msm/board-mahimahi-wifi.c create mode 100644 arch/arm/mach-msm/board-mahimahi.h diff --git a/arch/arm/mach-msm/board-mahimahi-audio.c b/arch/arm/mach-msm/board-mahimahi-audio.c new file mode 100644 index 0000000000000..fdc334beb1410 --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-audio.c @@ -0,0 +1,202 @@ +/* arch/arm/mach-msm/board-mahimahi-audio.c + * + * Copyright (C) 2009 HTC Corporation + * Copyright (C) 2009 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 "board-mahimahi.h" +#include "proc_comm.h" +#include "pmic.h" + +#if 0 +#define D(fmt, args...) printk(KERN_INFO "Audio: "fmt, ##args) +#else +#define D(fmt, args...) do {} while (0) +#endif + +static struct mutex mic_lock; +static struct mutex bt_sco_lock; + +void mahimahi_headset_enable(int en) +{ + D("%s %d\n", __func__, en); + /* enable audio amp */ + if (en) mdelay(15); + gpio_set_value(MAHIMAHI_AUD_JACKHP_EN, !!en); +} + +void mahimahi_speaker_enable(int en) +{ + struct spkr_config_mode scm; + memset(&scm, 0, sizeof(scm)); + + D("%s %d\n", __func__, en); + if (en) { + scm.is_right_chan_en = 0; + scm.is_left_chan_en = 1; + scm.is_stereo_en = 0; + scm.is_hpf_en = 1; + pmic_spkr_en_mute(LEFT_SPKR, 0); + pmic_spkr_en_mute(RIGHT_SPKR, 0); + pmic_set_spkr_configuration(&scm); + pmic_spkr_en(LEFT_SPKR, 1); + pmic_spkr_en(RIGHT_SPKR, 0); + + /* unmute */ + pmic_spkr_en_mute(LEFT_SPKR, 1); + } else { + pmic_spkr_en_mute(LEFT_SPKR, 0); + + pmic_spkr_en(LEFT_SPKR, 0); + pmic_spkr_en(RIGHT_SPKR, 0); + + pmic_set_spkr_configuration(&scm); + } +} + +void mahimahi_receiver_enable(int en) +{ + /* Do nothing for mahimahi. */ +} + +static void config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for (n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +static uint32_t bt_sco_enable[] = { + PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_OUT, 1, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_IN, 1, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_SYNC, 2, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_CLK, 2, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), +}; + +static uint32_t bt_sco_disable[] = { + PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_OUT, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_IN, 0, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_SYNC, 0, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_CLK, 0, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), +}; + +void mahimahi_bt_sco_enable(int en) +{ + static int bt_sco_refcount; + D("%s %d\n", __func__, en); + + mutex_lock(&bt_sco_lock); + if (en) { + if (++bt_sco_refcount == 1) + config_gpio_table(bt_sco_enable, + ARRAY_SIZE(bt_sco_enable)); + } else { + if (--bt_sco_refcount == 0) { + config_gpio_table(bt_sco_disable, + ARRAY_SIZE(bt_sco_disable)); + gpio_set_value(MAHIMAHI_BT_PCM_OUT, 0); + } + } + mutex_unlock(&bt_sco_lock); +} + +void mahimahi_mic_enable(int en) +{ + static int old_state = 0, new_state = 0; + + D("%s %d\n", __func__, en); + + mutex_lock(&mic_lock); + if (!!en) + new_state++; + else + new_state--; + + if (new_state == 1 && old_state == 0) { + gpio_set_value(MAHIMAHI_AUD_2V5_EN, 1); + mdelay(60); + } else if (new_state == 0 && old_state == 1) + gpio_set_value(MAHIMAHI_AUD_2V5_EN, 0); + else + D("%s: do nothing %d %d\n", __func__, old_state, new_state); + + old_state = new_state; + mutex_unlock(&mic_lock); +} + +void mahimahi_analog_init(void) +{ + D("%s\n", __func__); + /* stereo pmic init */ + pmic_spkr_set_gain(LEFT_SPKR, SPKR_GAIN_PLUS12DB); + pmic_spkr_set_gain(RIGHT_SPKR, SPKR_GAIN_PLUS12DB); + pmic_spkr_en_right_chan(OFF_CMD); + pmic_spkr_en_left_chan(OFF_CMD); + pmic_spkr_add_right_left_chan(OFF_CMD); + pmic_spkr_en_stereo(OFF_CMD); + pmic_spkr_select_usb_with_hpf_20hz(OFF_CMD); + pmic_spkr_bypass_mux(OFF_CMD); + pmic_spkr_en_hpf(ON_CMD); + pmic_spkr_en_sink_curr_from_ref_volt_cir(OFF_CMD); + pmic_spkr_set_mux_hpf_corner_freq(SPKR_FREQ_0_73KHZ); + pmic_mic_set_volt(MIC_VOLT_1_80V); + + gpio_request(MAHIMAHI_AUD_JACKHP_EN, "aud_jackhp_en"); + gpio_request(MAHIMAHI_BT_PCM_OUT, "bt_pcm_out"); + + gpio_direction_output(MAHIMAHI_AUD_JACKHP_EN, 0); + + mutex_lock(&bt_sco_lock); + config_gpio_table(bt_sco_disable, + ARRAY_SIZE(bt_sco_disable)); + gpio_direction_output(MAHIMAHI_BT_PCM_OUT, 0); + mutex_unlock(&bt_sco_lock); +} + +static struct qsd_acoustic_ops acoustic = { + .enable_mic_bias = mahimahi_mic_enable, +}; + +static struct q6audio_analog_ops ops = { + .init = mahimahi_analog_init, + .speaker_enable = mahimahi_speaker_enable, + .headset_enable = mahimahi_headset_enable, + .receiver_enable = mahimahi_receiver_enable, + .bt_sco_enable = mahimahi_bt_sco_enable, + .int_mic_enable = mahimahi_mic_enable, + .ext_mic_enable = mahimahi_mic_enable, +}; + +void __init mahimahi_audio_init(void) +{ + mutex_init(&mic_lock); + mutex_init(&bt_sco_lock); + q6audio_register_analog_ops(&ops); + acoustic_register_ops(&acoustic); +} diff --git a/arch/arm/mach-msm/board-mahimahi-flashlight.c b/arch/arm/mach-msm/board-mahimahi-flashlight.c new file mode 100644 index 0000000000000..829b1f11cf218 --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-flashlight.c @@ -0,0 +1,279 @@ +/* + * arch/arm/mach-msm/flashlight.c - flashlight driver + * + * Copyright (C) 2009 zion huang + * + * 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; version 2 of the License. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-mahimahi-flashlight.h" + +struct flashlight_struct { + struct led_classdev fl_lcdev; + struct early_suspend early_suspend_flashlight; + spinlock_t spin_lock; + struct hrtimer timer; + int brightness; + int gpio_torch; + int gpio_flash; + int flash_duration_ms; +}; + +static struct flashlight_struct the_fl; + +static inline void toggle(void) +{ + gpio_direction_output(the_fl.gpio_torch, 0); + udelay(2); + gpio_direction_output(the_fl.gpio_torch, 1); + udelay(2); +} + +static void flashlight_hw_command(uint8_t addr, uint8_t data) +{ + int i; + + for (i = 0; i < addr + 17; i++) + toggle(); + udelay(500); + + for (i = 0; i < data; i++) + toggle(); + udelay(500); +} + +static enum hrtimer_restart flashlight_timeout(struct hrtimer *timer) +{ + unsigned long flags; + + pr_debug("%s\n", __func__); + + spin_lock_irqsave(&the_fl.spin_lock, flags); + gpio_direction_output(the_fl.gpio_flash, 0); + the_fl.brightness = LED_OFF; + spin_unlock_irqrestore(&the_fl.spin_lock, flags); + + return HRTIMER_NORESTART; +} + +int flashlight_control(int mode) +{ + int ret = 0; + unsigned long flags; + + pr_debug("%s: mode %d -> %d\n", __func__, + the_fl.brightness, mode); + + spin_lock_irqsave(&the_fl.spin_lock, flags); + + the_fl.brightness = mode; + + switch (mode) { + case FLASHLIGHT_TORCH: + pr_info("%s: half\n", __func__); + /* If we are transitioning from flash to torch, make sure to + * cancel the flash timeout timer, otherwise when it expires, + * the torch will go off as well. + */ + hrtimer_cancel(&the_fl.timer); + flashlight_hw_command(2, 4); + break; + + case FLASHLIGHT_FLASH: + pr_info("%s: full\n", __func__); + hrtimer_cancel(&the_fl.timer); + gpio_direction_output(the_fl.gpio_flash, 0); + udelay(40); + gpio_direction_output(the_fl.gpio_flash, 1); + hrtimer_start(&the_fl.timer, + ktime_set(the_fl.flash_duration_ms / 1000, + (the_fl.flash_duration_ms % 1000) * + NSEC_PER_MSEC), + HRTIMER_MODE_REL); + /* Flash overrides torch mode, and after the flash period, the + * flash LED will turn off. + */ + mode = LED_OFF; + break; + + case FLASHLIGHT_OFF: + pr_info("%s: off\n", __func__); + gpio_direction_output(the_fl.gpio_flash, 0); + gpio_direction_output(the_fl.gpio_torch, 0); + break; + + default: + pr_err("%s: unknown flash_light flags: %d\n", __func__, mode); + ret = -EINVAL; + goto done; + } + +done: + spin_unlock_irqrestore(&the_fl.spin_lock, flags); + return ret; +} +EXPORT_SYMBOL(flashlight_control); + +static void fl_lcdev_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + int level; + switch (brightness) { + case LED_HALF: + level = FLASHLIGHT_TORCH; + break; + case LED_FULL: + level = FLASHLIGHT_FLASH; + break; + case LED_OFF: + default: + level = FLASHLIGHT_OFF; + }; + + flashlight_control(level); +} + +static void flashlight_early_suspend(struct early_suspend *handler) +{ + flashlight_control(FLASHLIGHT_OFF); +} + +static int flashlight_setup_gpio(struct flashlight_platform_data *fl_pdata) +{ + int ret; + + pr_debug("%s\n", __func__); + + if (fl_pdata->gpio_init) { + ret = fl_pdata->gpio_init(); + if (ret < 0) { + pr_err("%s: gpio init failed: %d\n", __func__, + ret); + return ret; + } + } + + if (fl_pdata->torch) { + ret = gpio_request(fl_pdata->torch, "flashlight_torch"); + if (ret < 0) { + pr_err("%s: gpio_request failed\n", __func__); + return ret; + } + } + + if (fl_pdata->flash) { + ret = gpio_request(fl_pdata->flash, "flashlight_flash"); + if (ret < 0) { + pr_err("%s: gpio_request failed\n", __func__); + gpio_free(fl_pdata->torch); + return ret; + } + } + + the_fl.gpio_torch = fl_pdata->torch; + the_fl.gpio_flash = fl_pdata->flash; + the_fl.flash_duration_ms = fl_pdata->flash_duration_ms; + return 0; +} + +static int flashlight_probe(struct platform_device *pdev) +{ + struct flashlight_platform_data *fl_pdata = pdev->dev.platform_data; + int err = 0; + + pr_debug("%s\n", __func__); + + err = flashlight_setup_gpio(fl_pdata); + if (err < 0) { + pr_err("%s: setup GPIO failed\n", __func__); + goto fail_free_mem; + } + + spin_lock_init(&the_fl.spin_lock); + the_fl.fl_lcdev.name = pdev->name; + the_fl.fl_lcdev.brightness_set = fl_lcdev_brightness_set; + the_fl.fl_lcdev.brightness = LED_OFF; + err = led_classdev_register(&pdev->dev, &the_fl.fl_lcdev); + if (err < 0) { + pr_err("failed on led_classdev_register\n"); + goto fail_free_gpio; + } + + hrtimer_init(&the_fl.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + the_fl.timer.function = flashlight_timeout; + +#ifdef CONFIG_HAS_EARLYSUSPEND + the_fl.early_suspend_flashlight.suspend = flashlight_early_suspend; + the_fl.early_suspend_flashlight.resume = NULL; + register_early_suspend(&the_fl.early_suspend_flashlight); +#endif + + return 0; + +fail_free_gpio: + if (fl_pdata->torch) + gpio_free(fl_pdata->torch); + if (fl_pdata->flash) + gpio_free(fl_pdata->flash); +fail_free_mem: + return err; +} + +static int flashlight_remove(struct platform_device *pdev) +{ + struct flashlight_platform_data *fl_pdata = pdev->dev.platform_data; + + pr_debug("%s\n", __func__); + + hrtimer_cancel(&the_fl.timer); + unregister_early_suspend(&the_fl.early_suspend_flashlight); + flashlight_control(FLASHLIGHT_OFF); + led_classdev_unregister(&the_fl.fl_lcdev); + if (fl_pdata->torch) + gpio_free(fl_pdata->torch); + if (fl_pdata->flash) + gpio_free(fl_pdata->flash); + return 0; +} + +static struct platform_driver flashlight_driver = { + .probe = flashlight_probe, + .remove = flashlight_remove, + .driver = { + .name = FLASHLIGHT_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init flashlight_init(void) +{ + pr_debug("%s\n", __func__); + return platform_driver_register(&flashlight_driver); +} + +static void __exit flashlight_exit(void) +{ + pr_debug("%s\n", __func__); + platform_driver_unregister(&flashlight_driver); +} + +module_init(flashlight_init); +module_exit(flashlight_exit); + +MODULE_AUTHOR("Zion Huang "); +MODULE_DESCRIPTION("flash light driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-mahimahi-flashlight.h b/arch/arm/mach-msm/board-mahimahi-flashlight.h new file mode 100644 index 0000000000000..93b4095edaab1 --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-flashlight.h @@ -0,0 +1,20 @@ +#ifndef __ASM_ARM_ARCH_FLASHLIGHT_H +#define __ASM_ARM_ARCH_FLASHLIGHT_H + +#define FLASHLIGHT_NAME "flashlight" + +#define FLASHLIGHT_OFF 0 +#define FLASHLIGHT_TORCH 1 +#define FLASHLIGHT_FLASH 2 +#define FLASHLIGHT_NUM 3 + +struct flashlight_platform_data { + int (*gpio_init) (void); + int torch; + int flash; + int flash_duration_ms; +}; + +int flashlight_control(int level); + +#endif /*__ASM_ARM_ARCH_FLASHLIGHT_H*/ diff --git a/arch/arm/mach-msm/board-mahimahi-keypad.c b/arch/arm/mach-msm/board-mahimahi-keypad.c new file mode 100644 index 0000000000000..ca8b4964e25b2 --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-keypad.c @@ -0,0 +1,224 @@ +/* arch/arm/mach-msm/board-mahimahi-keypad.c + * + * Copyright (C) 2009 Google, Inc + * Copyright (C) 2009 HTC Corporation. + * + * Author: Dima Zavin + * + * 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 "board-mahimahi.h" + +struct jog_axis_info { + struct gpio_event_axis_info info; + uint16_t in_state; + uint16_t out_state; +}; + +static struct vreg *jog_vreg; +static bool jog_just_on; +static unsigned long jog_on_jiffies; + +static unsigned int mahimahi_col_gpios[] = { 33, 32, 31 }; +static unsigned int mahimahi_row_gpios[] = { 42, 41, 40 }; + +#define KEYMAP_INDEX(col, row) ((col)*ARRAY_SIZE(mahimahi_row_gpios) + (row)) +#define KEYMAP_SIZE (ARRAY_SIZE(mahimahi_col_gpios) * \ + ARRAY_SIZE(mahimahi_row_gpios)) + +/* keypad */ +static const unsigned short mahimahi_keymap[KEYMAP_SIZE] = { + [KEYMAP_INDEX(0, 0)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(0, 1)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(1, 1)] = MATRIX_KEY(1, BTN_MOUSE), +}; + +static struct gpio_event_matrix_info mahimahi_keypad_matrix_info = { + .info.func = gpio_event_matrix_func, + .keymap = mahimahi_keymap, + .output_gpios = mahimahi_col_gpios, + .input_gpios = mahimahi_row_gpios, + .noutputs = ARRAY_SIZE(mahimahi_col_gpios), + .ninputs = ARRAY_SIZE(mahimahi_row_gpios), + .settle_time.tv.nsec = 40 * NSEC_PER_USEC, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .flags = (GPIOKPF_LEVEL_TRIGGERED_IRQ | + GPIOKPF_REMOVE_PHANTOM_KEYS | + GPIOKPF_PRINT_UNMAPPED_KEYS), +}; + +static struct gpio_event_direct_entry mahimahi_keypad_key_map[] = { + { + .gpio = MAHIMAHI_GPIO_POWER_KEY, + .code = KEY_POWER, + }, +}; + +static struct gpio_event_input_info mahimahi_keypad_key_info = { + .info.func = gpio_event_input_func, + .info.no_suspend = true, + .flags = 0, + .type = EV_KEY, + .keymap = mahimahi_keypad_key_map, + .keymap_size = ARRAY_SIZE(mahimahi_keypad_key_map) +}; + +/* jogball */ +static uint16_t jogball_axis_map(struct gpio_event_axis_info *info, uint16_t in) +{ + struct jog_axis_info *ai = + container_of(info, struct jog_axis_info, info); + uint16_t out = ai->out_state; + + if (jog_just_on) { + if (jiffies == jog_on_jiffies || jiffies == jog_on_jiffies + 1) + goto ignore; + jog_just_on = 0; + } + if((ai->in_state ^ in) & 1) + out--; + if((ai->in_state ^ in) & 2) + out++; + ai->out_state = out; +ignore: + ai->in_state = in; + return out; +} + +static int jogball_power(const struct gpio_event_platform_data *pdata, bool on) +{ + if (on) { + vreg_enable(jog_vreg); + jog_just_on = 1; + jog_on_jiffies = jiffies; + } else { + vreg_disable(jog_vreg); + } + + return 0; +} + +static uint32_t jogball_x_gpios[] = { + MAHIMAHI_GPIO_BALL_LEFT, MAHIMAHI_GPIO_BALL_RIGHT, +}; +static uint32_t jogball_y_gpios[] = { + MAHIMAHI_GPIO_BALL_UP, MAHIMAHI_GPIO_BALL_DOWN, +}; + +static struct jog_axis_info jogball_x_axis = { + .info = { + .info.func = gpio_event_axis_func, + .count = ARRAY_SIZE(jogball_x_gpios), + .dev = 1, + .type = EV_REL, + .code = REL_X, + .decoded_size = 1U << ARRAY_SIZE(jogball_x_gpios), + .map = jogball_axis_map, + .gpio = jogball_x_gpios, + .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION, + } +}; + +static struct jog_axis_info jogball_y_axis = { + .info = { + .info.func = gpio_event_axis_func, + .count = ARRAY_SIZE(jogball_y_gpios), + .dev = 1, + .type = EV_REL, + .code = REL_Y, + .decoded_size = 1U << ARRAY_SIZE(jogball_y_gpios), + .map = jogball_axis_map, + .gpio = jogball_y_gpios, + .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION, + } +}; + +static struct gpio_event_info *mahimahi_input_info[] = { + &mahimahi_keypad_matrix_info.info, + &mahimahi_keypad_key_info.info, + &jogball_x_axis.info.info, + &jogball_y_axis.info.info, +}; + +static struct gpio_event_platform_data mahimahi_input_data = { + .names = { + "mahimahi-keypad", + "mahimahi-nav", + NULL, + }, + .info = mahimahi_input_info, + .info_count = ARRAY_SIZE(mahimahi_input_info), + .power = jogball_power, +}; + +static struct platform_device mahimahi_input_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = 0, + .dev = { + .platform_data = &mahimahi_input_data, + }, +}; + +static int mahimahi_reset_keys_up[] = { + KEY_VOLUMEUP, + 0, +}; + +static struct keyreset_platform_data mahimahi_reset_keys_pdata = { + .keys_up = mahimahi_reset_keys_up, + .keys_down = { + KEY_POWER, + KEY_VOLUMEDOWN, + BTN_MOUSE, + 0 + }, +}; + +struct platform_device mahimahi_reset_keys_device = { + .name = KEYRESET_NAME, + .dev = { + .platform_data = &mahimahi_reset_keys_pdata, + }, +}; + + +static int __init mahimahi_init_keypad_jogball(void) +{ + int ret; + + if (!machine_is_mahimahi()) + return 0; + + ret = platform_device_register(&mahimahi_reset_keys_device); + if (ret != 0) + return ret; + + jog_vreg = vreg_get(&mahimahi_input_device.dev, "gp2"); + if (jog_vreg == NULL) + return -ENOENT; + + ret = platform_device_register(&mahimahi_input_device); + if (ret != 0) + return ret; + + return 0; +} + +device_initcall(mahimahi_init_keypad_jogball); diff --git a/arch/arm/mach-msm/board-mahimahi-microp.c b/arch/arm/mach-msm/board-mahimahi-microp.c new file mode 100644 index 0000000000000..7e57806f53b55 --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-microp.c @@ -0,0 +1,2235 @@ +/* board-mahimahi-microp.c + * Copyright (C) 2009 Google. + * Copyright (C) 2009 HTC Corporation. + * + * The Microp on mahimahi is an i2c device that supports + * the following functions + * - LEDs (Green, Amber, Jogball backlight) + * - Lightsensor + * - Headset remotekeys + * - G-sensor + * - Interrupts + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "board-mahimahi.h" + + +#define MICROP_I2C_NAME "mahimahi-microp" + +#define MICROP_LSENSOR_ADC_CHAN 6 +#define MICROP_REMOTE_KEY_ADC_CHAN 7 + +#define MICROP_I2C_WCMD_MISC 0x20 +#define MICROP_I2C_WCMD_SPI_EN 0x21 +#define MICROP_I2C_WCMD_AUTO_BL_CTL 0x23 +#define MICROP_I2C_RCMD_SPI_BL_STATUS 0x24 +#define MICROP_I2C_WCMD_BUTTONS_LED_CTRL 0x25 +#define MICROP_I2C_RCMD_VERSION 0x30 +#define MICROP_I2C_WCMD_ADC_TABLE 0x42 +#define MICROP_I2C_WCMD_LED_MODE 0x53 +#define MICROP_I2C_RCMD_GREEN_LED_REMAIN_TIME 0x54 +#define MICROP_I2C_RCMD_AMBER_RED_LED_REMAIN_TIME 0x55 +#define MICROP_I2C_RCMD_BLUE_LED_REMAIN_TIME 0x57 +#define MICROP_I2C_WCMD_JOGBALL_LED_MODE 0x5A +#define MICROP_I2C_RCMD_JOGBALL_LED_REMAIN_TIME 0x5B +#define MICROP_I2C_WCMD_JOGBALL_LED_PWM_SET 0x5C +#define MICROP_I2C_WCMD_JOGBALL_LED_PERIOD_SET 0x5D +#define MICROP_I2C_WCMD_READ_ADC_VALUE_REQ 0x60 +#define MICROP_I2C_RCMD_ADC_VALUE 0x62 +#define MICROP_I2C_WCMD_REMOTEKEY_TABLE 0x63 +#define MICROP_I2C_WCMD_LCM_REGISTER 0x70 +#define MICROP_I2C_WCMD_GSENSOR_REG 0x73 +#define MICROP_I2C_WCMD_GSENSOR_REG_DATA_REQ 0x74 +#define MICROP_I2C_RCMD_GSENSOR_REG_DATA 0x75 +#define MICROP_I2C_WCMD_GSENSOR_DATA_REQ 0x76 +#define MICROP_I2C_RCMD_GSENSOR_X_DATA 0x77 +#define MICROP_I2C_RCMD_GSENSOR_Y_DATA 0x78 +#define MICROP_I2C_RCMD_GSENSOR_Z_DATA 0x79 +#define MICROP_I2C_RCMD_GSENSOR_DATA 0x7A +#define MICROP_I2C_WCMD_OJ_REG 0x7B +#define MICROP_I2C_WCMD_OJ_REG_DATA_REQ 0x7C +#define MICROP_I2C_RCMD_OJ_REG_DATA 0x7D +#define MICROP_I2C_WCMD_OJ_POS_DATA_REQ 0x7E +#define MICROP_I2C_RCMD_OJ_POS_DATA 0x7F +#define MICROP_I2C_WCMD_GPI_INT_CTL_EN 0x80 +#define MICROP_I2C_WCMD_GPI_INT_CTL_DIS 0x81 +#define MICROP_I2C_RCMD_GPI_INT_STATUS 0x82 +#define MICROP_I2C_RCMD_GPI_STATUS 0x83 +#define MICROP_I2C_WCMD_GPI_INT_STATUS_CLR 0x84 +#define MICROP_I2C_RCMD_GPI_INT_SETTING 0x85 +#define MICROP_I2C_RCMD_REMOTE_KEYCODE 0x87 +#define MICROP_I2C_WCMD_REMOTE_KEY_DEBN_TIME 0x88 +#define MICROP_I2C_WCMD_REMOTE_PLUG_DEBN_TIME 0x89 +#define MICROP_I2C_WCMD_SIMCARD_DEBN_TIME 0x8A +#define MICROP_I2C_WCMD_GPO_LED_STATUS_EN 0x90 +#define MICROP_I2C_WCMD_GPO_LED_STATUS_DIS 0x91 + +#define IRQ_GSENSOR (1<<10) +#define IRQ_LSENSOR (1<<9) +#define IRQ_REMOTEKEY (1<<7) +#define IRQ_HEADSETIN (1<<2) +#define IRQ_SDCARD (1<<0) + +#define READ_GPI_STATE_HPIN (1<<2) +#define READ_GPI_STATE_SDCARD (1<<0) + +#define ALS_CALIBRATE_MODE 147 + +/* Check pattern, to check if ALS has been calibrated */ +#define ALS_CALIBRATED 0x6DA5 + +/* delay for deferred light sensor read */ +#define LS_READ_DELAY (HZ/2) + +/*#define DEBUG_BMA150 */ +#ifdef DEBUG_BMA150 +/* Debug logging of accelleration data */ +#define GSENSOR_LOG_MAX 2048 /* needs to be power of 2 */ +#define GSENSOR_LOG_MASK (GSENSOR_LOG_MAX - 1) + +struct gsensor_log { + ktime_t timestamp; + short x; + short y; + short z; +}; + +static DEFINE_MUTEX(gsensor_log_lock); +static struct gsensor_log gsensor_log[GSENSOR_LOG_MAX]; +static unsigned gsensor_log_head; +static unsigned gsensor_log_tail; + +void gsensor_log_status(ktime_t time, short x, short y, short z) +{ + unsigned n; + mutex_lock(&gsensor_log_lock); + n = gsensor_log_head; + gsensor_log[n].timestamp = time; + gsensor_log[n].x = x; + gsensor_log[n].y = y; + gsensor_log[n].z = z; + n = (n + 1) & GSENSOR_LOG_MASK; + if (n == gsensor_log_tail) + gsensor_log_tail = (gsensor_log_tail + 1) & GSENSOR_LOG_MASK; + gsensor_log_head = n; + mutex_unlock(&gsensor_log_lock); +} + +static int gsensor_log_print(struct seq_file *sf, void *private) +{ + unsigned n; + + mutex_lock(&gsensor_log_lock); + seq_printf(sf, "timestamp X Y Z\n"); + for (n = gsensor_log_tail; + n != gsensor_log_head; + n = (n + 1) & GSENSOR_LOG_MASK) { + seq_printf(sf, "%10d.%010d %6d %6d %6d\n", + gsensor_log[n].timestamp.tv.sec, + gsensor_log[n].timestamp.tv.nsec, + gsensor_log[n].x, gsensor_log[n].y, + gsensor_log[n].z); + } + mutex_unlock(&gsensor_log_lock); + return 0; +} + +static int gsensor_log_open(struct inode *inode, struct file *file) +{ + return single_open(file, gsensor_log_print, NULL); +} + +static struct file_operations gsensor_log_fops = { + .open = gsensor_log_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif /* def DEBUG_BMA150 */ + +static int microp_headset_has_mic(void); +static int microp_enable_headset_plug_event(void); +static int microp_enable_key_event(void); +static int microp_disable_key_event(void); + +static struct h35mm_platform_data mahimahi_h35mm_data = { + .plug_event_enable = microp_enable_headset_plug_event, + .headset_has_mic = microp_headset_has_mic, + .key_event_enable = microp_enable_key_event, + .key_event_disable = microp_disable_key_event, +}; + +static struct platform_device mahimahi_h35mm = { + .name = "htc_headset", + .id = -1, + .dev = { + .platform_data = &mahimahi_h35mm_data, + }, +}; + +enum led_type { + GREEN_LED, + AMBER_LED, + RED_LED, + BLUE_LED, + JOGBALL_LED, + BUTTONS_LED, + NUM_LEDS, +}; + +static uint16_t lsensor_adc_table[10] = { + 0x000, 0x001, 0x00F, 0x01E, 0x03C, 0x121, 0x190, 0x2BA, 0x26E, 0x3FF +}; + +static uint16_t remote_key_adc_table[6] = { + 0, 31, 43, 98, 129, 192 +}; + +static uint32_t golden_adc = 0xC0; +static uint32_t als_kadc; + +static struct wake_lock microp_i2c_wakelock; + +static struct i2c_client *private_microp_client; + +struct microp_int_pin { + uint16_t int_gsensor; + uint16_t int_lsensor; + uint16_t int_reset; + uint16_t int_simcard; + uint16_t int_hpin; + uint16_t int_remotekey; +}; + +struct microp_led_data { + int type; + struct led_classdev ldev; + struct mutex led_data_mutex; + struct work_struct brightness_work; + spinlock_t brightness_lock; + enum led_brightness brightness; + uint8_t mode; + uint8_t blink; +}; + +struct microp_i2c_work { + struct work_struct work; + struct i2c_client *client; + int (*intr_debounce)(uint8_t *pin_status); + void (*intr_function)(uint8_t *pin_status); +}; + +struct microp_i2c_client_data { + struct microp_led_data leds[NUM_LEDS]; + uint16_t version; + struct microp_i2c_work work; + struct delayed_work hpin_debounce_work; + struct delayed_work ls_read_work; + struct early_suspend early_suspend; + uint8_t enable_early_suspend; + uint8_t enable_reset_button; + int microp_is_suspend; + int auto_backlight_enabled; + uint8_t light_sensor_enabled; + uint8_t force_light_sensor_read; + uint8_t button_led_value; + int headset_is_in; + int is_hpin_pin_stable; + struct input_dev *ls_input_dev; + uint32_t als_kadc; + uint32_t als_gadc; + uint8_t als_calibrating; +}; + +static char *hex2string(uint8_t *data, int len) +{ + static char buf[101]; + int i; + + i = (sizeof(buf) - 1) / 4; + if (len > i) + len = i; + + for (i = 0; i < len; i++) + sprintf(buf + i * 4, "[%02X]", data[i]); + + return buf; +} + +#define I2C_READ_RETRY_TIMES 10 +#define I2C_WRITE_RETRY_TIMES 10 + +static int i2c_read_block(struct i2c_client *client, uint8_t addr, + uint8_t *data, int length) +{ + int retry; + int ret; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + } + }; + + mdelay(1); + for (retry = 0; retry <= I2C_READ_RETRY_TIMES; retry++) { + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret == 2) { + dev_dbg(&client->dev, "R [%02X] = %s\n", addr, + hex2string(data, length)); + return 0; + } + msleep(10); + } + + dev_err(&client->dev, "i2c_read_block retry over %d\n", + I2C_READ_RETRY_TIMES); + return -EIO; +} + +#define MICROP_I2C_WRITE_BLOCK_SIZE 21 +static int i2c_write_block(struct i2c_client *client, uint8_t addr, + uint8_t *data, int length) +{ + int retry; + uint8_t buf[MICROP_I2C_WRITE_BLOCK_SIZE]; + int ret; + + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length + 1, + .buf = buf, + } + }; + + dev_dbg(&client->dev, "W [%02X] = %s\n", addr, + hex2string(data, length)); + + if (length + 1 > MICROP_I2C_WRITE_BLOCK_SIZE) { + dev_err(&client->dev, "i2c_write_block length too long\n"); + return -E2BIG; + } + + buf[0] = addr; + memcpy((void *)&buf[1], (void *)data, length); + + mdelay(1); + for (retry = 0; retry <= I2C_WRITE_RETRY_TIMES; retry++) { + ret = i2c_transfer(client->adapter, msg, 1); + if (ret == 1) + return 0; + msleep(10); + } + dev_err(&client->dev, "i2c_write_block retry over %d\n", + I2C_WRITE_RETRY_TIMES); + return -EIO; +} + +static int microp_read_adc(uint8_t channel, uint16_t *value) +{ + struct i2c_client *client; + int ret; + uint8_t cmd[2], data[2]; + + client = private_microp_client; + cmd[0] = 0; + cmd[1] = channel; + ret = i2c_write_block(client, MICROP_I2C_WCMD_READ_ADC_VALUE_REQ, + cmd, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: request adc fail\n", __func__); + return -EIO; + } + + ret = i2c_read_block(client, MICROP_I2C_RCMD_ADC_VALUE, data, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: read adc fail\n", __func__); + return -EIO; + } + *value = data[0] << 8 | data[1]; + return 0; +} + +static int microp_read_gpi_status(struct i2c_client *client, uint16_t *status) +{ + uint8_t data[2]; + int ret; + + ret = i2c_read_block(client, MICROP_I2C_RCMD_GPI_STATUS, data, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: read failed\n", __func__); + return -EIO; + } + *status = (data[0] << 8) | data[1]; + return 0; +} + +static int microp_interrupt_enable(struct i2c_client *client, + uint16_t interrupt_mask) +{ + uint8_t data[2]; + int ret = -1; + + data[0] = interrupt_mask >> 8; + data[1] = interrupt_mask & 0xFF; + ret = i2c_write_block(client, MICROP_I2C_WCMD_GPI_INT_CTL_EN, data, 2); + + if (ret < 0) + dev_err(&client->dev, "%s: enable 0x%x interrupt failed\n", + __func__, interrupt_mask); + return ret; +} + +static int microp_interrupt_disable(struct i2c_client *client, + uint16_t interrupt_mask) +{ + uint8_t data[2]; + int ret = -1; + + data[0] = interrupt_mask >> 8; + data[1] = interrupt_mask & 0xFF; + ret = i2c_write_block(client, MICROP_I2C_WCMD_GPI_INT_CTL_DIS, data, 2); + + if (ret < 0) + dev_err(&client->dev, "%s: disable 0x%x interrupt failed\n", + __func__, interrupt_mask); + return ret; +} + + +/* + * SD slot card-detect support + */ +static unsigned int sdslot_cd = 0; +static void (*sdslot_status_cb)(int card_present, void *dev_id); +static void *sdslot_mmc_dev; + +int mahimahi_microp_sdslot_status_register( + void (*cb)(int card_present, void *dev_id), + void *dev_id) +{ + if (sdslot_status_cb) + return -EBUSY; + sdslot_status_cb = cb; + sdslot_mmc_dev = dev_id; + return 0; +} + +unsigned int mahimahi_microp_sdslot_status(struct device *dev) +{ + return sdslot_cd; +} + +static void mahimahi_microp_sdslot_update_status(int status) +{ + sdslot_cd = !(status & READ_GPI_STATE_SDCARD); + if (sdslot_status_cb) + sdslot_status_cb(sdslot_cd, sdslot_mmc_dev); +} + +/* + *Headset Support +*/ +static void hpin_debounce_do_work(struct work_struct *work) +{ + uint16_t gpi_status = 0; + struct microp_i2c_client_data *cdata; + int insert = 0; + struct i2c_client *client; + + client = private_microp_client; + cdata = i2c_get_clientdata(client); + + microp_read_gpi_status(client, &gpi_status); + insert = (gpi_status & READ_GPI_STATE_HPIN) ? 0 : 1; + if (insert != cdata->headset_is_in) { + cdata->headset_is_in = insert; + pr_debug("headset %s\n", insert ? "inserted" : "removed"); + htc_35mm_jack_plug_event(cdata->headset_is_in); + cdata->is_hpin_pin_stable = 1; + } +} + +static int microp_enable_headset_plug_event(void) +{ + int ret; + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + uint16_t stat; + + client = private_microp_client; + cdata = i2c_get_clientdata(client); + + /* enable microp interrupt to detect changes */ + ret = microp_interrupt_enable(client, IRQ_HEADSETIN); + if (ret < 0) { + dev_err(&client->dev, "%s: failed to enable irqs\n", + __func__); + return 0; + } + /* see if headset state has changed */ + microp_read_gpi_status(client, &stat); + stat = !(stat & READ_GPI_STATE_HPIN); + if(cdata->headset_is_in != stat) { + cdata->headset_is_in = stat; + pr_debug("Headset state changed\n"); + htc_35mm_jack_plug_event(stat); + } + + return 1; +} + +static int microp_headset_has_mic(void) +{ + uint16_t data; + int ret = 0; + + microp_read_adc(MICROP_REMOTE_KEY_ADC_CHAN, &data); + if (data >= 200) + ret = 1; + else + ret = 0; + + pr_debug("%s: microp_mic_status =0x%d\n", __func__, ret); + + return ret; +} + +static int microp_enable_key_event(void) +{ + int ret; + struct i2c_client *client; + + client = private_microp_client; + + /* turn on key interrupt */ + gpio_set_value(MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN, 1); + + /* enable microp interrupt to detect changes */ + ret = microp_interrupt_enable(client, IRQ_REMOTEKEY); + if (ret < 0) { + dev_err(&client->dev, "%s: failed to enable irqs\n", + __func__); + return ret; + } + return 0; +} + +static int microp_disable_key_event(void) +{ + int ret; + struct i2c_client *client; + + client = private_microp_client; + + /* shutdown key interrupt */ + gpio_set_value(MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN, 0); + + /* disable microp interrupt to detect changes */ + ret = microp_interrupt_disable(client, IRQ_REMOTEKEY); + if (ret < 0) { + dev_err(&client->dev, "%s: failed to disable irqs\n", + __func__); + return ret; + } + return 0; +} + +static int get_remote_keycode(int *keycode) +{ + struct i2c_client *client = private_microp_client; + int ret; + uint8_t data[2]; + + ret = i2c_read_block(client, MICROP_I2C_RCMD_REMOTE_KEYCODE, data, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: read remote keycode fail\n", + __func__); + return -EIO; + } + pr_debug("%s: key = 0x%x\n", __func__, data[1]); + if (!data[1]) { + *keycode = 0; + return 1; /* no keycode */ + } else { + *keycode = data[1]; + } + return 0; +} + +static ssize_t microp_i2c_remotekey_adc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client; + uint16_t value; + int i, button = 0; + int ret; + + client = to_i2c_client(dev); + + microp_read_adc(MICROP_REMOTE_KEY_ADC_CHAN, &value); + + for (i = 0; i < 3; i += 2) { + if ((value > remote_key_adc_table[i]) && + (value < remote_key_adc_table[i])) { + button = i + 1; + } + + } + + ret = sprintf(buf, "Remote Key[0x%03X] => button %d\n", + value, button); + + return ret; +} + +static DEVICE_ATTR(key_adc, 0644, microp_i2c_remotekey_adc_show, NULL); + +/* + * LED support +*/ +static int microp_i2c_write_led_mode(struct i2c_client *client, + struct led_classdev *led_cdev, + uint8_t mode, uint16_t off_timer) +{ + struct microp_i2c_client_data *cdata; + struct microp_led_data *ldata; + uint8_t data[7]; + int ret; + + cdata = i2c_get_clientdata(client); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + + + if (ldata->type == GREEN_LED) { + data[0] = 0x01; + data[1] = mode; + data[2] = off_timer >> 8; + data[3] = off_timer & 0xFF; + data[4] = 0x00; + data[5] = 0x00; + data[6] = 0x00; + } else if (ldata->type == AMBER_LED) { + data[0] = 0x02; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + data[4] = mode; + data[5] = off_timer >> 8; + data[6] = off_timer & 0xFF; + } else if (ldata->type == RED_LED) { + data[0] = 0x02; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + data[4] = mode? 5: 0; + data[5] = off_timer >> 8; + data[6] = off_timer & 0xFF; + } else if (ldata->type == BLUE_LED) { + data[0] = 0x04; + data[1] = mode; + data[2] = off_timer >> 8; + data[3] = off_timer & 0xFF; + data[4] = 0x00; + data[5] = 0x00; + data[6] = 0x00; + } + + ret = i2c_write_block(client, MICROP_I2C_WCMD_LED_MODE, data, 7); + if (ret == 0) { + mutex_lock(&ldata->led_data_mutex); + if (mode > 1) + ldata->blink = mode; + else + ldata->mode = mode; + mutex_unlock(&ldata->led_data_mutex); + } + return ret; +} + +static ssize_t microp_i2c_led_blink_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + int ret; + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + + mutex_lock(&ldata->led_data_mutex); + ret = sprintf(buf, "%d\n", ldata->blink ? ldata->blink - 1 : 0); + mutex_unlock(&ldata->led_data_mutex); + + return ret; +} + +static ssize_t microp_i2c_led_blink_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + struct i2c_client *client; + int val, ret; + uint8_t mode; + + val = -1; + sscanf(buf, "%u", &val); + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + client = to_i2c_client(dev->parent); + + mutex_lock(&ldata->led_data_mutex); + switch (val) { + case 0: /* stop flashing */ + mode = ldata->mode; + ldata->blink = 0; + break; + case 1: + case 2: + case 3: + mode = val + 1; + break; + + default: + mutex_unlock(&ldata->led_data_mutex); + return -EINVAL; + } + mutex_unlock(&ldata->led_data_mutex); + + ret = microp_i2c_write_led_mode(client, led_cdev, mode, 0xffff); + if (ret) + dev_err(&client->dev, "%s set blink failed\n", led_cdev->name); + + return count; +} + +static DEVICE_ATTR(blink, 0644, microp_i2c_led_blink_show, + microp_i2c_led_blink_store); + +static ssize_t microp_i2c_led_off_timer_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct microp_i2c_client_data *cdata; + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + struct i2c_client *client; + uint8_t data[2]; + int ret, offtime; + + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + client = to_i2c_client(dev->parent); + cdata = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "Getting %s remaining time\n", led_cdev->name); + + if (ldata->type == GREEN_LED) { + ret = i2c_read_block(client, + MICROP_I2C_RCMD_GREEN_LED_REMAIN_TIME, data, 2); + } else if (ldata->type == AMBER_LED) { + ret = i2c_read_block(client, + MICROP_I2C_RCMD_AMBER_RED_LED_REMAIN_TIME, + data, 2); + } else if (ldata->type == RED_LED) { + ret = i2c_read_block(client, + MICROP_I2C_RCMD_AMBER_RED_LED_REMAIN_TIME, + data, 2); + } else if (ldata->type == BLUE_LED) { + ret = i2c_read_block(client, + MICROP_I2C_RCMD_BLUE_LED_REMAIN_TIME, data, 2); + } else { + dev_err(&client->dev, "Unknown led %s\n", ldata->ldev.name); + return -EINVAL; + } + + if (ret) { + dev_err(&client->dev, + "%s get off_timer failed\n", led_cdev->name); + } + offtime = (int)((data[1] | data[0] << 8) * 2); + + ret = sprintf(buf, "Time remains %d:%d\n", offtime / 60, offtime % 60); + return ret; +} + +static ssize_t microp_i2c_led_off_timer_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + struct i2c_client *client; + int min, sec, ret; + uint16_t off_timer; + + min = -1; + sec = -1; + sscanf(buf, "%d %d", &min, &sec); + + if (min < 0 || min > 255) + return -EINVAL; + if (sec < 0 || sec > 255) + return -EINVAL; + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + client = to_i2c_client(dev->parent); + + dev_dbg(&client->dev, "Setting %s off_timer to %d min %d sec\n", + led_cdev->name, min, sec); + + if (!min && !sec) + off_timer = 0xFFFF; + else + off_timer = (min * 60 + sec) / 2; + + ret = microp_i2c_write_led_mode(client, led_cdev, + ldata->mode, off_timer); + if (ret) { + dev_err(&client->dev, + "%s set off_timer %d min %d sec failed\n", + led_cdev->name, min, sec); + } + return count; +} + +static DEVICE_ATTR(off_timer, 0644, microp_i2c_led_off_timer_show, + microp_i2c_led_off_timer_store); + +static ssize_t microp_i2c_jogball_color_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + struct i2c_client *client; + int rpwm, gpwm, bpwm, ret; + uint8_t data[4]; + + rpwm = -1; + gpwm = -1; + bpwm = -1; + sscanf(buf, "%d %d %d", &rpwm, &gpwm, &bpwm); + + if (rpwm < 0 || rpwm > 255) + return -EINVAL; + if (gpwm < 0 || gpwm > 255) + return -EINVAL; + if (bpwm < 0 || bpwm > 255) + return -EINVAL; + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + client = to_i2c_client(dev->parent); + + dev_dbg(&client->dev, "Setting %s color to R=%d, G=%d, B=%d\n", + led_cdev->name, rpwm, gpwm, bpwm); + + data[0] = rpwm; + data[1] = gpwm; + data[2] = bpwm; + data[3] = 0x00; + + ret = i2c_write_block(client, MICROP_I2C_WCMD_JOGBALL_LED_PWM_SET, + data, 4); + if (ret) { + dev_err(&client->dev, + "%s set color R=%d G=%d B=%d failed\n", + led_cdev->name, rpwm, gpwm, bpwm); + } + return count; +} + +static DEVICE_ATTR(color, 0644, NULL, microp_i2c_jogball_color_store); + +static ssize_t microp_i2c_jogball_period_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + struct i2c_client *client; + int period = -1; + int ret; + uint8_t data[4]; + + sscanf(buf, "%d", &period); + + if (period < 2 || period > 12) + return -EINVAL; + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + client = to_i2c_client(dev->parent); + + dev_info(&client->dev, "Setting Jogball flash period to %d\n", period); + + data[0] = 0x00; + data[1] = period; + + ret = i2c_write_block(client, MICROP_I2C_WCMD_JOGBALL_LED_PERIOD_SET, + data, 2); + if (ret) { + dev_err(&client->dev, "%s set period=%d failed\n", + led_cdev->name, period); + } + return count; +} + +static DEVICE_ATTR(period, 0644, NULL, microp_i2c_jogball_period_store); + +static void microp_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + unsigned long flags; + struct i2c_client *client = to_i2c_client(led_cdev->dev->parent); + struct microp_led_data *ldata = + container_of(led_cdev, struct microp_led_data, ldev); + + dev_dbg(&client->dev, "Setting %s brightness current %d new %d\n", + led_cdev->name, led_cdev->brightness, brightness); + + if (brightness > 255) + brightness = 255; + led_cdev->brightness = brightness; + + spin_lock_irqsave(&ldata->brightness_lock, flags); + ldata->brightness = brightness; + spin_unlock_irqrestore(&ldata->brightness_lock, flags); + + schedule_work(&ldata->brightness_work); +} + +static void microp_led_brightness_set_work(struct work_struct *work) +{ + unsigned long flags; + struct microp_led_data *ldata = + container_of(work, struct microp_led_data, brightness_work); + struct led_classdev *led_cdev = &ldata->ldev; + + struct i2c_client *client = to_i2c_client(led_cdev->dev->parent); + + enum led_brightness brightness; + int ret; + uint8_t mode; + + spin_lock_irqsave(&ldata->brightness_lock, flags); + brightness = ldata->brightness; + spin_unlock_irqrestore(&ldata->brightness_lock, flags); + + if (brightness) + mode = 1; + else + mode = 0; + + ret = microp_i2c_write_led_mode(client, led_cdev, mode, 0xffff); + if (ret) { + dev_err(&client->dev, + "led_brightness_set failed to set mode\n"); + } +} + +struct device_attribute *green_amber_attrs[] = { + &dev_attr_blink, + &dev_attr_off_timer, +}; + +struct device_attribute *jogball_attrs[] = { + &dev_attr_color, + &dev_attr_period, +}; + +static void microp_led_buttons_brightness_set_work(struct work_struct *work) +{ + + unsigned long flags; + struct microp_led_data *ldata = + container_of(work, struct microp_led_data, brightness_work); + struct led_classdev *led_cdev = &ldata->ldev; + + struct i2c_client *client = to_i2c_client(led_cdev->dev->parent); + struct microp_i2c_client_data *cdata = i2c_get_clientdata(client); + + + uint8_t data[4] = {0, 0, 0}; + int ret = 0; + enum led_brightness brightness; + uint8_t value; + + + spin_lock_irqsave(&ldata->brightness_lock, flags); + brightness = ldata->brightness; + spin_unlock_irqrestore(&ldata->brightness_lock, flags); + + value = brightness >= 255 ? 0x20 : 0; + + /* avoid a flicker that can occur when writing the same value */ + if (cdata->button_led_value == value) + return; + cdata->button_led_value = value; + + /* in 40ms */ + data[0] = 0x05; + /* duty cycle 0-255 */ + data[1] = value; + /* bit2 == change brightness */ + data[3] = 0x04; + + ret = i2c_write_block(client, MICROP_I2C_WCMD_BUTTONS_LED_CTRL, + data, 4); + if (ret < 0) + dev_err(&client->dev, "%s failed on set buttons\n", __func__); +} + +static void microp_led_jogball_brightness_set_work(struct work_struct *work) +{ + unsigned long flags; + struct microp_led_data *ldata = + container_of(work, struct microp_led_data, brightness_work); + struct led_classdev *led_cdev = &ldata->ldev; + + struct i2c_client *client = to_i2c_client(led_cdev->dev->parent); + uint8_t data[3] = {0, 0, 0}; + int ret = 0; + enum led_brightness brightness; + + spin_lock_irqsave(&ldata->brightness_lock, flags); + brightness = ldata->brightness; + spin_unlock_irqrestore(&ldata->brightness_lock, flags); + + switch (brightness) { + case 0: + data[0] = 0; + break; + case 3: + data[0] = 1; + data[1] = data[2] = 0xFF; + break; + case 7: + data[0] = 2; + data[1] = 0; + data[2] = 60; + break; + default: + dev_warn(&client->dev, "%s: unknown value: %d\n", + __func__, brightness); + break; + } + ret = i2c_write_block(client, MICROP_I2C_WCMD_JOGBALL_LED_MODE, + data, 3); + if (ret < 0) + dev_err(&client->dev, "%s failed on set jogball mode:0x%2.2X\n", + __func__, data[0]); +} + +/* + * Light Sensor Support + */ +static int microp_i2c_auto_backlight_mode(struct i2c_client *client, + uint8_t enabled) +{ + uint8_t data[2]; + int ret = 0; + + data[0] = 0; + if (enabled) + data[1] = 1; + else + data[1] = 0; + + ret = i2c_write_block(client, MICROP_I2C_WCMD_AUTO_BL_CTL, data, 2); + if (ret != 0) + pr_err("%s: set auto light sensor fail\n", __func__); + + return ret; +} + +static int lightsensor_enable(void) +{ + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + int ret; + + client = private_microp_client; + cdata = i2c_get_clientdata(client); + + if (cdata->microp_is_suspend) { + pr_err("%s: abort, uP is going to suspend after #\n", + __func__); + return -EIO; + } + + disable_irq(client->irq); + ret = microp_i2c_auto_backlight_mode(client, 1); + if (ret < 0) { + pr_err("%s: set auto light sensor fail\n", __func__); + enable_irq(client->irq); + return ret; + } + + cdata->auto_backlight_enabled = 1; + /* TEMPORARY HACK: schedule a deferred light sensor read + * to work around sensor manager race condition + */ + schedule_delayed_work(&cdata->ls_read_work, LS_READ_DELAY); + schedule_work(&cdata->work.work); + + return 0; +} + +static int lightsensor_disable(void) +{ + /* update trigger data when done */ + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + int ret; + + client = private_microp_client; + cdata = i2c_get_clientdata(client); + + if (cdata->microp_is_suspend) { + pr_err("%s: abort, uP is going to suspend after #\n", + __func__); + return -EIO; + } + + cancel_delayed_work(&cdata->ls_read_work); + + ret = microp_i2c_auto_backlight_mode(client, 0); + if (ret < 0) + pr_err("%s: disable auto light sensor fail\n", + __func__); + else + cdata->auto_backlight_enabled = 0; + return 0; +} + +static int microp_lightsensor_read(uint16_t *adc_value, + uint8_t *adc_level) +{ + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + uint8_t i; + int ret; + + client = private_microp_client; + cdata = i2c_get_clientdata(client); + + ret = microp_read_adc(MICROP_LSENSOR_ADC_CHAN, adc_value); + if (ret != 0) + return -1; + + if (*adc_value > 0x3FF) { + pr_warning("%s: get wrong value: 0x%X\n", + __func__, *adc_value); + return -1; + } else { + if (!cdata->als_calibrating) { + *adc_value = *adc_value + * cdata->als_gadc / cdata->als_kadc; + if (*adc_value > 0x3FF) + *adc_value = 0x3FF; + } + + *adc_level = ARRAY_SIZE(lsensor_adc_table) - 1; + for (i = 0; i < ARRAY_SIZE(lsensor_adc_table); i++) { + if (*adc_value <= lsensor_adc_table[i]) { + *adc_level = i; + break; + } + } + pr_debug("%s: ADC value: 0x%X, level: %d #\n", + __func__, *adc_value, *adc_level); + } + + return 0; +} + +static ssize_t microp_i2c_lightsensor_adc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint8_t adc_level = 0; + uint16_t adc_value = 0; + int ret; + + ret = microp_lightsensor_read(&adc_value, &adc_level); + + ret = sprintf(buf, "ADC[0x%03X] => level %d\n", adc_value, adc_level); + + return ret; +} + +static DEVICE_ATTR(ls_adc, 0644, microp_i2c_lightsensor_adc_show, NULL); + +static ssize_t microp_i2c_ls_auto_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client; + uint8_t data[2] = {0, 0}; + int ret; + + client = to_i2c_client(dev); + + i2c_read_block(client, MICROP_I2C_RCMD_SPI_BL_STATUS, data, 2); + ret = sprintf(buf, "Light sensor Auto = %d, SPI enable = %d\n", + data[0], data[1]); + + return ret; +} + +static ssize_t microp_i2c_ls_auto_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + uint8_t enable = 0; + int ls_auto; + + ls_auto = -1; + sscanf(buf, "%d", &ls_auto); + + if (ls_auto != 0 && ls_auto != 1 && ls_auto != ALS_CALIBRATE_MODE) + return -EINVAL; + + client = to_i2c_client(dev); + cdata = i2c_get_clientdata(client); + + if (ls_auto) { + enable = 1; + cdata->als_calibrating = (ls_auto == ALS_CALIBRATE_MODE) ? 1 : 0; + cdata->auto_backlight_enabled = 1; + } else { + enable = 0; + cdata->als_calibrating = 0; + cdata->auto_backlight_enabled = 0; + } + + microp_i2c_auto_backlight_mode(client, enable); + + return count; +} + +static DEVICE_ATTR(ls_auto, 0644, microp_i2c_ls_auto_show, + microp_i2c_ls_auto_store); + +DEFINE_MUTEX(api_lock); +static int lightsensor_opened; + +static int lightsensor_open(struct inode *inode, struct file *file) +{ + int rc = 0; + pr_debug("%s\n", __func__); + mutex_lock(&api_lock); + if (lightsensor_opened) { + pr_err("%s: already opened\n", __func__); + rc = -EBUSY; + } + lightsensor_opened = 1; + mutex_unlock(&api_lock); + return rc; +} + +static int lightsensor_release(struct inode *inode, struct file *file) +{ + pr_debug("%s\n", __func__); + mutex_lock(&api_lock); + lightsensor_opened = 0; + mutex_unlock(&api_lock); + return 0; +} + +static long lightsensor_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int rc, val; + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + + mutex_lock(&api_lock); + + client = private_microp_client; + cdata = i2c_get_clientdata(client); + + pr_debug("%s cmd %d\n", __func__, _IOC_NR(cmd)); + + switch (cmd) { + case LIGHTSENSOR_IOCTL_ENABLE: + if (get_user(val, (unsigned long __user *)arg)) { + rc = -EFAULT; + break; + } + rc = val ? lightsensor_enable() : lightsensor_disable(); + break; + case LIGHTSENSOR_IOCTL_GET_ENABLED: + val = cdata->auto_backlight_enabled; + pr_debug("%s enabled %d\n", __func__, val); + rc = put_user(val, (unsigned long __user *)arg); + break; + default: + pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd)); + rc = -EINVAL; + } + + mutex_unlock(&api_lock); + return rc; +} + +static struct file_operations lightsensor_fops = { + .owner = THIS_MODULE, + .open = lightsensor_open, + .release = lightsensor_release, + .unlocked_ioctl = lightsensor_ioctl +}; + +struct miscdevice lightsensor_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "lightsensor", + .fops = &lightsensor_fops +}; + +/* + * G-sensor + */ +static int microp_spi_enable(uint8_t on) +{ + struct i2c_client *client; + int ret; + + client = private_microp_client; + ret = i2c_write_block(client, MICROP_I2C_WCMD_SPI_EN, &on, 1); + if (ret < 0) { + dev_err(&client->dev,"%s: i2c_write_block fail\n", __func__); + return ret; + } + msleep(10); + return ret; +} + +static int gsensor_read_reg(uint8_t reg, uint8_t *data) +{ + struct i2c_client *client; + int ret; + uint8_t tmp[2]; + + client = private_microp_client; + ret = i2c_write_block(client, MICROP_I2C_WCMD_GSENSOR_REG_DATA_REQ, + ®, 1); + if (ret < 0) { + dev_err(&client->dev,"%s: i2c_write_block fail\n", __func__); + return ret; + } + msleep(10); + + ret = i2c_read_block(client, MICROP_I2C_RCMD_GSENSOR_REG_DATA, tmp, 2); + if (ret < 0) { + dev_err(&client->dev,"%s: i2c_read_block fail\n", __func__); + return ret; + } + *data = tmp[1]; + return ret; +} + +static int gsensor_write_reg(uint8_t reg, uint8_t data) +{ + struct i2c_client *client; + int ret; + uint8_t tmp[2]; + + client = private_microp_client; + + tmp[0] = reg; + tmp[1] = data; + ret = i2c_write_block(client, MICROP_I2C_WCMD_GSENSOR_REG, tmp, 2); + if (ret < 0) { + dev_err(&client->dev,"%s: i2c_write_block fail\n", __func__); + return ret; + } + + return ret; +} + +static int gsensor_read_acceleration(short *buf) +{ + struct i2c_client *client; + int ret; + uint8_t tmp[6]; + struct microp_i2c_client_data *cdata; + + client = private_microp_client; + + cdata = i2c_get_clientdata(client); + + tmp[0] = 1; + ret = i2c_write_block(client, MICROP_I2C_WCMD_GSENSOR_DATA_REQ, + tmp, 1); + if (ret < 0) { + dev_err(&client->dev,"%s: i2c_write_block fail\n", __func__); + return ret; + } + + msleep(10); + + if (cdata->version <= 0x615) { + /* + * Note the data is a 10bit signed value from the chip. + */ + ret = i2c_read_block(client, MICROP_I2C_RCMD_GSENSOR_X_DATA, + tmp, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: i2c_read_block fail\n", + __func__); + return ret; + } + buf[0] = (short)(tmp[0] << 8 | tmp[1]); + buf[0] >>= 6; + + ret = i2c_read_block(client, MICROP_I2C_RCMD_GSENSOR_Y_DATA, + tmp, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: i2c_read_block fail\n", + __func__); + return ret; + } + buf[1] = (short)(tmp[0] << 8 | tmp[1]); + buf[1] >>= 6; + + ret = i2c_read_block(client, MICROP_I2C_RCMD_GSENSOR_Z_DATA, + tmp, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: i2c_read_block fail\n", + __func__); + return ret; + } + buf[2] = (short)(tmp[0] << 8 | tmp[1]); + buf[2] >>= 6; + } else { + ret = i2c_read_block(client, MICROP_I2C_RCMD_GSENSOR_DATA, + tmp, 6); + if (ret < 0) { + dev_err(&client->dev, "%s: i2c_read_block fail\n", + __func__); + return ret; + } + buf[0] = (short)(tmp[0] << 8 | tmp[1]); + buf[0] >>= 6; + buf[1] = (short)(tmp[2] << 8 | tmp[3]); + buf[1] >>= 6; + buf[2] = (short)(tmp[4] << 8 | tmp[5]); + buf[2] >>= 6; + } + +#ifdef DEBUG_BMA150 + /* Log this to debugfs */ + gsensor_log_status(ktime_get(), buf[0], buf[1], buf[2]); +#endif + return 1; +} + +static int gsensor_init_hw(void) +{ + uint8_t reg; + int ret; + + pr_debug("%s\n", __func__); + + microp_spi_enable(1); + + ret = gsensor_read_reg(RANGE_BWIDTH_REG, ®); + if (ret < 0 ) + return -EIO; + reg &= 0xe0; + ret = gsensor_write_reg(RANGE_BWIDTH_REG, reg); + if (ret < 0 ) + return -EIO; + + ret = gsensor_read_reg(SMB150_CONF2_REG, ®); + if (ret < 0 ) + return -EIO; + reg |= (1 << 3); + ret = gsensor_write_reg(SMB150_CONF2_REG, reg); + + return ret; +} + +static int bma150_set_mode(char mode) +{ + uint8_t reg; + int ret; + + pr_debug("%s mode = %d\n", __func__, mode); + if (mode == BMA_MODE_NORMAL) + microp_spi_enable(1); + + + ret = gsensor_read_reg(SMB150_CTRL_REG, ®); + if (ret < 0 ) + return -EIO; + reg = (reg & 0xfe) | mode; + ret = gsensor_write_reg(SMB150_CTRL_REG, reg); + + if (mode == BMA_MODE_SLEEP) + microp_spi_enable(0); + + return ret; +} +static int gsensor_read(uint8_t *data) +{ + int ret; + uint8_t reg = data[0]; + + ret = gsensor_read_reg(reg, &data[1]); + pr_debug("%s reg = %x data = %x\n", __func__, reg, data[1]); + return ret; +} + +static int gsensor_write(uint8_t *data) +{ + int ret; + uint8_t reg = data[0]; + + pr_debug("%s reg = %x data = %x\n", __func__, reg, data[1]); + ret = gsensor_write_reg(reg, data[1]); + return ret; +} + +static int bma150_open(struct inode *inode, struct file *file) +{ + pr_debug("%s\n", __func__); + return nonseekable_open(inode, file); +} + +static int bma150_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int bma150_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + char rwbuf[8]; + int ret = -1; + short buf[8], temp; + + switch (cmd) { + case BMA_IOCTL_READ: + case BMA_IOCTL_WRITE: + case BMA_IOCTL_SET_MODE: + if (copy_from_user(&rwbuf, argp, sizeof(rwbuf))) + return -EFAULT; + break; + case BMA_IOCTL_READ_ACCELERATION: + if (copy_from_user(&buf, argp, sizeof(buf))) + return -EFAULT; + break; + default: + break; + } + + switch (cmd) { + case BMA_IOCTL_INIT: + ret = gsensor_init_hw(); + if (ret < 0) + return ret; + break; + + case BMA_IOCTL_READ: + if (rwbuf[0] < 1) + return -EINVAL; + ret = gsensor_read(rwbuf); + if (ret < 0) + return ret; + break; + case BMA_IOCTL_WRITE: + if (rwbuf[0] < 2) + return -EINVAL; + ret = gsensor_write(rwbuf); + if (ret < 0) + return ret; + break; + case BMA_IOCTL_READ_ACCELERATION: + ret = gsensor_read_acceleration(&buf[0]); + if (ret < 0) + return ret; + break; + case BMA_IOCTL_SET_MODE: + bma150_set_mode(rwbuf[0]); + break; + case BMA_IOCTL_GET_INT: + temp = 0; + break; + default: + return -ENOTTY; + } + + switch (cmd) { + case BMA_IOCTL_READ: + if (copy_to_user(argp, &rwbuf, sizeof(rwbuf))) + return -EFAULT; + break; + case BMA_IOCTL_READ_ACCELERATION: + if (copy_to_user(argp, &buf, sizeof(buf))) + return -EFAULT; + break; + case BMA_IOCTL_GET_INT: + if (copy_to_user(argp, &temp, sizeof(temp))) + return -EFAULT; + break; + default: + break; + } + + return 0; +} + +static struct file_operations bma_fops = { + .owner = THIS_MODULE, + .open = bma150_open, + .release = bma150_release, + .ioctl = bma150_ioctl, +}; + +static struct miscdevice spi_bma_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = BMA150_G_SENSOR_NAME, + .fops = &bma_fops, +}; + +/* + * Interrupt + */ +static irqreturn_t microp_i2c_intr_irq_handler(int irq, void *dev_id) +{ + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + + client = to_i2c_client(dev_id); + cdata = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "intr_irq_handler\n"); + + disable_irq_nosync(client->irq); + schedule_work(&cdata->work.work); + return IRQ_HANDLED; +} + +static void microp_i2c_intr_work_func(struct work_struct *work) +{ + struct microp_i2c_work *up_work; + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + uint8_t data[3], adc_level; + uint16_t intr_status = 0, adc_value, gpi_status = 0; + int keycode = 0, ret = 0; + + up_work = container_of(work, struct microp_i2c_work, work); + client = up_work->client; + cdata = i2c_get_clientdata(client); + + ret = i2c_read_block(client, MICROP_I2C_RCMD_GPI_INT_STATUS, data, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: read interrupt status fail\n", + __func__); + } + + intr_status = data[0]<<8 | data[1]; + ret = i2c_write_block(client, MICROP_I2C_WCMD_GPI_INT_STATUS_CLR, + data, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: clear interrupt status fail\n", + __func__); + } + pr_debug("intr_status=0x%02x\n", intr_status); + + if ((intr_status & IRQ_LSENSOR) || cdata->force_light_sensor_read) { + ret = microp_lightsensor_read(&adc_value, &adc_level); + if (cdata->force_light_sensor_read) { + /* report an invalid value first to ensure we trigger an event + * when adc_level is zero. + */ + input_report_abs(cdata->ls_input_dev, ABS_MISC, -1); + input_sync(cdata->ls_input_dev); + cdata->force_light_sensor_read = 0; + } + input_report_abs(cdata->ls_input_dev, ABS_MISC, (int)adc_level); + input_sync(cdata->ls_input_dev); + } + + if (intr_status & IRQ_SDCARD) { + microp_read_gpi_status(client, &gpi_status); + mahimahi_microp_sdslot_update_status(gpi_status); + } + + if (intr_status & IRQ_HEADSETIN) { + cdata->is_hpin_pin_stable = 0; + wake_lock_timeout(µp_i2c_wakelock, 3*HZ); + if (!cdata->headset_is_in) + schedule_delayed_work(&cdata->hpin_debounce_work, + msecs_to_jiffies(500)); + else + schedule_delayed_work(&cdata->hpin_debounce_work, + msecs_to_jiffies(300)); + } + if (intr_status & IRQ_REMOTEKEY) { + if ((get_remote_keycode(&keycode) == 0) && + (cdata->is_hpin_pin_stable)) { + htc_35mm_key_event(keycode); + } + } + + enable_irq(client->irq); +} + +static void ls_read_do_work(struct work_struct *work) +{ + struct i2c_client *client = private_microp_client; + struct microp_i2c_client_data *cdata = i2c_get_clientdata(client); + + /* force a light sensor reading */ + disable_irq(client->irq); + cdata->force_light_sensor_read = 1; + schedule_work(&cdata->work.work); +} + +static int microp_function_initialize(struct i2c_client *client) +{ + struct microp_i2c_client_data *cdata; + uint8_t data[20]; + uint16_t stat, interrupts = 0; + int i; + int ret; + + cdata = i2c_get_clientdata(client); + + /* Light Sensor */ + if (als_kadc >> 16 == ALS_CALIBRATED) + cdata->als_kadc = als_kadc & 0xFFFF; + else { + cdata->als_kadc = 0; + pr_info("%s: no ALS calibrated\n", __func__); + } + + if (cdata->als_kadc && golden_adc) { + cdata->als_kadc = + (cdata->als_kadc > 0 && cdata->als_kadc < 0x400) + ? cdata->als_kadc : golden_adc; + cdata->als_gadc = + (golden_adc > 0) + ? golden_adc : cdata->als_kadc; + } else { + cdata->als_kadc = 1; + cdata->als_gadc = 1; + } + pr_info("%s: als_kadc=0x%x, als_gadc=0x%x\n", + __func__, cdata->als_kadc, cdata->als_gadc); + + for (i = 0; i < 10; i++) { + data[i] = (uint8_t)(lsensor_adc_table[i] + * cdata->als_kadc / cdata->als_gadc >> 8); + data[i + 10] = (uint8_t)(lsensor_adc_table[i] + * cdata->als_kadc / cdata->als_gadc); + } + ret = i2c_write_block(client, MICROP_I2C_WCMD_ADC_TABLE, data, 20); + if (ret) + goto exit; + + ret = gpio_request(MAHIMAHI_GPIO_LS_EN_N, "microp_i2c"); + if (ret < 0) { + dev_err(&client->dev, "failed on request gpio ls_on\n"); + goto exit; + } + ret = gpio_direction_output(MAHIMAHI_GPIO_LS_EN_N, 0); + if (ret < 0) { + dev_err(&client->dev, "failed on gpio_direction_output" + "ls_on\n"); + goto err_gpio_ls; + } + cdata->light_sensor_enabled = 1; + + /* Headset */ + for (i = 0; i < 6; i++) { + data[i] = (uint8_t)(remote_key_adc_table[i] >> 8); + data[i + 6] = (uint8_t)(remote_key_adc_table[i]); + } + ret = i2c_write_block(client, + MICROP_I2C_WCMD_REMOTEKEY_TABLE, data, 12); + if (ret) + goto exit; + + INIT_DELAYED_WORK( + &cdata->hpin_debounce_work, hpin_debounce_do_work); + INIT_DELAYED_WORK( + &cdata->ls_read_work, ls_read_do_work); + + /* SD Card */ + interrupts |= IRQ_SDCARD; + + /* enable the interrupts */ + ret = microp_interrupt_enable(client, interrupts); + if (ret < 0) { + dev_err(&client->dev, "%s: failed to enable gpi irqs\n", + __func__); + goto err_irq_en; + } + + microp_read_gpi_status(client, &stat); + mahimahi_microp_sdslot_update_status(stat); + + return 0; + +err_irq_en: +err_gpio_ls: + gpio_free(MAHIMAHI_GPIO_LS_EN_N); +exit: + return ret; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +void microp_early_suspend(struct early_suspend *h) +{ + struct microp_i2c_client_data *cdata; + struct i2c_client *client = private_microp_client; + int ret; + + if (!client) { + pr_err("%s: dataset: client is empty\n", __func__); + return; + } + cdata = i2c_get_clientdata(client); + + cdata->microp_is_suspend = 1; + + disable_irq(client->irq); + ret = cancel_work_sync(&cdata->work.work); + if (ret != 0) { + enable_irq(client->irq); + } + + if (cdata->auto_backlight_enabled) + microp_i2c_auto_backlight_mode(client, 0); + if (cdata->light_sensor_enabled == 1) { + gpio_set_value(MAHIMAHI_GPIO_LS_EN_N, 1); + cdata->light_sensor_enabled = 0; + } +} + +void microp_early_resume(struct early_suspend *h) +{ + struct i2c_client *client = private_microp_client; + struct microp_i2c_client_data *cdata; + + if (!client) { + pr_err("%s: dataset: client is empty\n", __func__); + return; + } + cdata = i2c_get_clientdata(client); + + gpio_set_value(MAHIMAHI_GPIO_LS_EN_N, 0); + cdata->light_sensor_enabled = 1; + + if (cdata->auto_backlight_enabled) + microp_i2c_auto_backlight_mode(client, 1); + + cdata->microp_is_suspend = 0; + enable_irq(client->irq); +} +#endif + +static int microp_i2c_suspend(struct i2c_client *client, + pm_message_t mesg) +{ + return 0; +} + +static int microp_i2c_resume(struct i2c_client *client) +{ + return 0; +} + +static struct { + const char *name; + void (*led_set_work)(struct work_struct *); + struct device_attribute **attrs; + int attr_cnt; +} microp_leds[] = { + [GREEN_LED] = { + .name = "green", + .led_set_work = microp_led_brightness_set_work, + .attrs = green_amber_attrs, + .attr_cnt = ARRAY_SIZE(green_amber_attrs) + }, + [AMBER_LED] = { + .name = "amber", + .led_set_work = microp_led_brightness_set_work, + .attrs = green_amber_attrs, + .attr_cnt = ARRAY_SIZE(green_amber_attrs) + }, + [RED_LED] = { + .name = "red", + .led_set_work = microp_led_brightness_set_work, + .attrs = green_amber_attrs, + .attr_cnt = ARRAY_SIZE(green_amber_attrs) + }, + [BLUE_LED] = { + .name = "blue", + .led_set_work = microp_led_brightness_set_work, + .attrs = green_amber_attrs, + .attr_cnt = ARRAY_SIZE(green_amber_attrs) + }, + [JOGBALL_LED] = { + .name = "jogball-backlight", + .led_set_work = microp_led_jogball_brightness_set_work, + .attrs = jogball_attrs, + .attr_cnt = ARRAY_SIZE(jogball_attrs) + }, + [BUTTONS_LED] = { + .name = "button-backlight", + .led_set_work = microp_led_buttons_brightness_set_work + }, +}; + +static int microp_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct microp_i2c_client_data *cdata; + uint8_t data[6]; + int ret; + int i; + int j; + + private_microp_client = client; + ret = i2c_read_block(client, MICROP_I2C_RCMD_VERSION, data, 2); + if (ret || !(data[0] && data[1])) { + ret = -ENODEV; + dev_err(&client->dev, "failed on get microp version\n"); + goto err_exit; + } + dev_info(&client->dev, "microp version [%02X][%02X]\n", + data[0], data[1]); + + ret = gpio_request(MAHIMAHI_GPIO_UP_RESET_N, "microp_i2c_wm"); + if (ret < 0) { + dev_err(&client->dev, "failed on request gpio reset\n"); + goto err_exit; + } + ret = gpio_direction_output(MAHIMAHI_GPIO_UP_RESET_N, 1); + if (ret < 0) { + dev_err(&client->dev, + "failed on gpio_direction_output reset\n"); + goto err_gpio_reset; + } + + cdata = kzalloc(sizeof(struct microp_i2c_client_data), GFP_KERNEL); + if (!cdata) { + ret = -ENOMEM; + dev_err(&client->dev, "failed on allocat cdata\n"); + goto err_cdata; + } + + i2c_set_clientdata(client, cdata); + cdata->version = data[0] << 8 | data[1]; + cdata->microp_is_suspend = 0; + cdata->auto_backlight_enabled = 0; + cdata->light_sensor_enabled = 0; + + wake_lock_init(µp_i2c_wakelock, WAKE_LOCK_SUSPEND, + "microp_i2c_present"); + + /* Light Sensor */ + ret = device_create_file(&client->dev, &dev_attr_ls_adc); + ret = device_create_file(&client->dev, &dev_attr_ls_auto); + cdata->ls_input_dev = input_allocate_device(); + if (!cdata->ls_input_dev) { + pr_err("%s: could not allocate input device\n", __func__); + ret = -ENOMEM; + goto err_request_input_dev; + } + cdata->ls_input_dev->name = "lightsensor-level"; + set_bit(EV_ABS, cdata->ls_input_dev->evbit); + input_set_abs_params(cdata->ls_input_dev, ABS_MISC, 0, 9, 0, 0); + + ret = input_register_device(cdata->ls_input_dev); + if (ret < 0) { + dev_err(&client->dev, "%s: can not register input device\n", + __func__); + goto err_register_input_dev; + } + + ret = misc_register(&lightsensor_misc); + if (ret < 0) { + dev_err(&client->dev, "%s: can not register misc device\n", + __func__); + goto err_register_misc_register; + } + + /* LEDs */ + ret = 0; + for (i = 0; i < ARRAY_SIZE(microp_leds) && !ret; ++i) { + struct microp_led_data *ldata = &cdata->leds[i]; + + ldata->type = i; + ldata->ldev.name = microp_leds[i].name; + ldata->ldev.brightness_set = microp_brightness_set; + mutex_init(&ldata->led_data_mutex); + INIT_WORK(&ldata->brightness_work, microp_leds[i].led_set_work); + spin_lock_init(&ldata->brightness_lock); + ret = led_classdev_register(&client->dev, &ldata->ldev); + if (ret) { + ldata->ldev.name = NULL; + break; + } + + for (j = 0; j < microp_leds[i].attr_cnt && !ret; ++j) + ret = device_create_file(ldata->ldev.dev, + microp_leds[i].attrs[j]); + } + if (ret) { + dev_err(&client->dev, "failed to add leds\n"); + goto err_add_leds; + } + + /* Headset */ + cdata->headset_is_in = 0; + cdata->is_hpin_pin_stable = 1; + platform_device_register(&mahimahi_h35mm); + + ret = device_create_file(&client->dev, &dev_attr_key_adc); + + /* G-sensor */ + ret = misc_register(&spi_bma_device); + if (ret < 0) { + pr_err("%s: init bma150 misc_register fail\n", + __func__); + goto err_register_bma150; + } +#ifdef DEBUG_BMA150 + debugfs_create_file("gsensor_log", 0444, NULL, NULL, &gsensor_log_fops); +#endif + /* Setup IRQ handler */ + INIT_WORK(&cdata->work.work, microp_i2c_intr_work_func); + cdata->work.client = client; + + ret = request_irq(client->irq, + microp_i2c_intr_irq_handler, + IRQF_TRIGGER_LOW, + "microp_interrupt", + &client->dev); + if (ret) { + dev_err(&client->dev, "request_irq failed\n"); + goto err_intr; + } + ret = set_irq_wake(client->irq, 1); + if (ret) { + dev_err(&client->dev, "set_irq_wake failed\n"); + goto err_intr; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + if (cdata->enable_early_suspend) { + cdata->early_suspend.level = + EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + cdata->early_suspend.suspend = microp_early_suspend; + cdata->early_suspend.resume = microp_early_resume; + register_early_suspend(&cdata->early_suspend); + } +#endif + + ret = microp_function_initialize(client); + if (ret) { + dev_err(&client->dev, "failed on microp function initialize\n"); + goto err_fun_init; + } + + return 0; + +err_fun_init: +err_intr: + misc_deregister(&spi_bma_device); + +err_register_bma150: + platform_device_unregister(&mahimahi_h35mm); + device_remove_file(&client->dev, &dev_attr_key_adc); + +err_add_leds: + for (i = 0; i < ARRAY_SIZE(microp_leds); ++i) { + if (!cdata->leds[i].ldev.name) + continue; + led_classdev_unregister(&cdata->leds[i].ldev); + for (j = 0; j < microp_leds[i].attr_cnt; ++j) + device_remove_file(cdata->leds[i].ldev.dev, + microp_leds[i].attrs[j]); + } + + misc_deregister(&lightsensor_misc); + +err_register_misc_register: + input_unregister_device(cdata->ls_input_dev); + +err_register_input_dev: + input_free_device(cdata->ls_input_dev); + +err_request_input_dev: + wake_lock_destroy(µp_i2c_wakelock); + device_remove_file(&client->dev, &dev_attr_ls_adc); + device_remove_file(&client->dev, &dev_attr_ls_auto); + kfree(cdata); + i2c_set_clientdata(client, NULL); + +err_cdata: +err_gpio_reset: + gpio_free(MAHIMAHI_GPIO_UP_RESET_N); +err_exit: + return ret; +} + +static int __devexit microp_i2c_remove(struct i2c_client *client) +{ + struct microp_i2c_client_data *cdata; + int i; + int j; + + cdata = i2c_get_clientdata(client); + + for (i = 0; i < ARRAY_SIZE(microp_leds); ++i) { + struct microp_led_data *ldata = &cdata->leds[i]; + cancel_work_sync(&ldata->brightness_work); + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + if (cdata->enable_early_suspend) { + unregister_early_suspend(&cdata->early_suspend); + } +#endif + + for (i = 0; i < ARRAY_SIZE(microp_leds); ++i) { + if (!cdata->leds[i].ldev.name) + continue; + led_classdev_unregister(&cdata->leds[i].ldev); + for (j = 0; j < microp_leds[i].attr_cnt; ++j) + device_remove_file(cdata->leds[i].ldev.dev, + microp_leds[i].attrs[j]); + } + + free_irq(client->irq, &client->dev); + + gpio_free(MAHIMAHI_GPIO_UP_RESET_N); + + misc_deregister(&lightsensor_misc); + input_unregister_device(cdata->ls_input_dev); + input_free_device(cdata->ls_input_dev); + device_remove_file(&client->dev, &dev_attr_ls_adc); + device_remove_file(&client->dev, &dev_attr_key_adc); + device_remove_file(&client->dev, &dev_attr_ls_auto); + + platform_device_unregister(&mahimahi_h35mm); + + /* G-sensor */ + misc_deregister(&spi_bma_device); + + kfree(cdata); + + return 0; +} + +#define ATAG_ALS 0x5441001b +static int __init parse_tag_als_kadc(const struct tag *tags) +{ + int found = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_ALS) { + found = 1; + break; + } + } + + if (found) + als_kadc = t->u.revision.rev; + pr_debug("%s: als_kadc = 0x%x\n", __func__, als_kadc); + return 0; +} +__tagtable(ATAG_ALS, parse_tag_als_kadc); + +static const struct i2c_device_id microp_i2c_id[] = { + { MICROP_I2C_NAME, 0 }, + { } +}; + +static struct i2c_driver microp_i2c_driver = { + .driver = { + .name = MICROP_I2C_NAME, + }, + .id_table = microp_i2c_id, + .probe = microp_i2c_probe, + .suspend = microp_i2c_suspend, + .resume = microp_i2c_resume, + .remove = __devexit_p(microp_i2c_remove), +}; + + +static int __init microp_i2c_init(void) +{ + return i2c_add_driver(µp_i2c_driver); +} + +static void __exit microp_i2c_exit(void) +{ + i2c_del_driver(µp_i2c_driver); +} + +module_init(microp_i2c_init); +module_exit(microp_i2c_exit); + +MODULE_AUTHOR("Eric Olsen "); +MODULE_DESCRIPTION("MicroP I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-mahimahi-mmc.c b/arch/arm/mach-msm/board-mahimahi-mmc.c new file mode 100644 index 0000000000000..564a5b1c28aed --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-mmc.c @@ -0,0 +1,415 @@ +/* linux/arch/arm/mach-msm/board-mahimahi-mmc.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * + * 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 "board-mahimahi.h" +#include "devices.h" +#include "proc_comm.h" + +#undef MAHIMAHI_DEBUG_MMC + +static bool opt_disable_sdcard; +static int __init mahimahi_disablesdcard_setup(char *str) +{ + opt_disable_sdcard = (bool)simple_strtol(str, NULL, 0); + return 1; +} + +__setup("board_mahimahi.disable_sdcard=", mahimahi_disablesdcard_setup); + +static void config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for(n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +static uint32_t sdcard_on_gpio_table[] = { + PCOM_GPIO_CFG(62, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(63, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(64, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ +}; + +static uint32_t sdcard_off_gpio_table[] = { + PCOM_GPIO_CFG(62, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(63, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(64, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ +}; + +static struct vreg *sdslot_vreg; +static uint32_t sdslot_vdd = 0xffffffff; +static uint32_t sdslot_vreg_enabled; + +static struct { + int mask; + int level; +} mmc_vdd_table[] = { + { MMC_VDD_165_195, 1800 }, + { MMC_VDD_20_21, 2050 }, + { MMC_VDD_21_22, 2150 }, + { MMC_VDD_22_23, 2250 }, + { MMC_VDD_23_24, 2350 }, + { MMC_VDD_24_25, 2450 }, + { MMC_VDD_25_26, 2550 }, + { MMC_VDD_26_27, 2650 }, + { MMC_VDD_27_28, 2750 }, + { MMC_VDD_28_29, 2850 }, + { MMC_VDD_29_30, 2950 }, +}; + +static uint32_t mahimahi_sdslot_switchvdd(struct device *dev, unsigned int vdd) +{ + int i; + int ret; + + if (vdd == sdslot_vdd) + return 0; + + sdslot_vdd = vdd; + + if (vdd == 0) { + config_gpio_table(sdcard_off_gpio_table, + ARRAY_SIZE(sdcard_off_gpio_table)); + vreg_disable(sdslot_vreg); + sdslot_vreg_enabled = 0; + return 0; + } + + if (!sdslot_vreg_enabled) { + ret = vreg_enable(sdslot_vreg); + if (ret) + pr_err("%s: Error enabling vreg (%d)\n", __func__, ret); + config_gpio_table(sdcard_on_gpio_table, + ARRAY_SIZE(sdcard_on_gpio_table)); + sdslot_vreg_enabled = 1; + } + + for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) { + if (mmc_vdd_table[i].mask != (1 << vdd)) + continue; + ret = vreg_set_level(sdslot_vreg, mmc_vdd_table[i].level); + if (ret) + pr_err("%s: Error setting level (%d)\n", __func__, ret); + return 0; + } + + pr_err("%s: Invalid VDD (%d) specified\n", __func__, vdd); + return 0; +} + +static unsigned int mahimahi_sdslot_status_rev0(struct device *dev) +{ + return !gpio_get_value(MAHIMAHI_GPIO_SDMC_CD_REV0_N); +} + +#define MAHIMAHI_MMC_VDD (MMC_VDD_165_195 | MMC_VDD_20_21 | \ + MMC_VDD_21_22 | MMC_VDD_22_23 | \ + MMC_VDD_23_24 | MMC_VDD_24_25 | \ + MMC_VDD_25_26 | MMC_VDD_26_27 | \ + MMC_VDD_27_28 | MMC_VDD_28_29 | \ + MMC_VDD_29_30) + +int mahimahi_microp_sdslot_status_register(void (*cb)(int, void *), void *); +unsigned int mahimahi_microp_sdslot_status(struct device *); + +static struct mmc_platform_data mahimahi_sdslot_data = { + .ocr_mask = MAHIMAHI_MMC_VDD, + .status = mahimahi_microp_sdslot_status, + .register_status_notify = mahimahi_microp_sdslot_status_register, + .translate_vdd = mahimahi_sdslot_switchvdd, +}; + +static uint32_t wifi_on_gpio_table[] = { + PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(152, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +static uint32_t wifi_off_gpio_table[] = { + PCOM_GPIO_CFG(51, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(56, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(152, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +/* BCM4329 returns wrong sdio_vsn(1) when we read cccr, + * we use predefined value (sdio_vsn=2) here to initial sdio driver well + */ +static struct embedded_sdio_data mahimahi_wifi_emb_data = { + .cccr = { + .sdio_vsn = 2, + .multi_block = 1, + .low_speed = 0, + .wide_bus = 0, + .high_power = 1, + .high_speed = 1, + }, +}; + +static int mahimahi_wifi_cd = 0; /* WIFI virtual 'card detect' status */ +static void (*wifi_status_cb)(int card_present, void *dev_id); +static void *wifi_status_cb_devid; + +static int mahimahi_wifi_status_register( + void (*callback)(int card_present, void *dev_id), + void *dev_id) +{ + if (wifi_status_cb) + return -EAGAIN; + wifi_status_cb = callback; + wifi_status_cb_devid = dev_id; + return 0; +} + +static unsigned int mahimahi_wifi_status(struct device *dev) +{ + return mahimahi_wifi_cd; +} + +static struct mmc_platform_data mahimahi_wifi_data = { + .ocr_mask = MMC_VDD_28_29, + .status = mahimahi_wifi_status, + .register_status_notify = mahimahi_wifi_status_register, + .embedded_sdio = &mahimahi_wifi_emb_data, +}; + +int mahimahi_wifi_set_carddetect(int val) +{ + pr_info("%s: %d\n", __func__, val); + mahimahi_wifi_cd = val; + if (wifi_status_cb) { + wifi_status_cb(val, wifi_status_cb_devid); + } else + pr_warning("%s: Nobody to notify\n", __func__); + return 0; +} + +static int mahimahi_wifi_power_state; + +int mahimahi_wifi_power(int on) +{ + printk("%s: %d\n", __func__, on); + + if (on) { + config_gpio_table(wifi_on_gpio_table, + ARRAY_SIZE(wifi_on_gpio_table)); + mdelay(50); + } else { + config_gpio_table(wifi_off_gpio_table, + ARRAY_SIZE(wifi_off_gpio_table)); + } + + mdelay(100); + gpio_set_value(MAHIMAHI_GPIO_WIFI_SHUTDOWN_N, on); /* WIFI_SHUTDOWN */ + mdelay(200); + + mahimahi_wifi_power_state = on; + return 0; +} + +static int mahimahi_wifi_reset_state; + +int mahimahi_wifi_reset(int on) +{ + printk("%s: do nothing\n", __func__); + mahimahi_wifi_reset_state = on; + return 0; +} + +int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, + unsigned int stat_irq, unsigned long stat_irq_flags); + +int __init mahimahi_init_mmc(unsigned int sys_rev, unsigned debug_uart) +{ + uint32_t id; + + printk("%s()+\n", __func__); + + /* initial WIFI_SHUTDOWN# */ + id = PCOM_GPIO_CFG(MAHIMAHI_GPIO_WIFI_SHUTDOWN_N, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + + msm_add_sdcc(1, &mahimahi_wifi_data, 0, 0); + + if (debug_uart) { + pr_info("%s: sdcard disabled due to debug uart\n", __func__); + goto done; + } + if (opt_disable_sdcard) { + pr_info("%s: sdcard disabled on cmdline\n", __func__); + goto done; + } + + sdslot_vreg_enabled = 0; + + sdslot_vreg = vreg_get(0, "gp6"); + if (IS_ERR(sdslot_vreg)) + return PTR_ERR(sdslot_vreg); + + if (system_rev > 0) + msm_add_sdcc(2, &mahimahi_sdslot_data, 0, 0); + else { + mahimahi_sdslot_data.status = mahimahi_sdslot_status_rev0; + mahimahi_sdslot_data.register_status_notify = NULL; + set_irq_wake(MSM_GPIO_TO_INT(MAHIMAHI_GPIO_SDMC_CD_REV0_N), 1); + msm_add_sdcc(2, &mahimahi_sdslot_data, + MSM_GPIO_TO_INT(MAHIMAHI_GPIO_SDMC_CD_REV0_N), + IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE); + } + +done: + printk("%s()-\n", __func__); + return 0; +} + +#if defined(MAHIMAHI_DEBUG_MMC) && defined(CONFIG_DEBUG_FS) + +static int mahimahimmc_dbg_wifi_reset_set(void *data, u64 val) +{ + mahimahi_wifi_reset((int) val); + return 0; +} + +static int mahimahimmc_dbg_wifi_reset_get(void *data, u64 *val) +{ + *val = mahimahi_wifi_reset_state; + return 0; +} + +static int mahimahimmc_dbg_wifi_cd_set(void *data, u64 val) +{ + mahimahi_wifi_set_carddetect((int) val); + return 0; +} + +static int mahimahimmc_dbg_wifi_cd_get(void *data, u64 *val) +{ + *val = mahimahi_wifi_cd; + return 0; +} + +static int mahimahimmc_dbg_wifi_pwr_set(void *data, u64 val) +{ + mahimahi_wifi_power((int) val); + return 0; +} + +static int mahimahimmc_dbg_wifi_pwr_get(void *data, u64 *val) +{ + *val = mahimahi_wifi_power_state; + return 0; +} + +static int mahimahimmc_dbg_sd_pwr_set(void *data, u64 val) +{ + mahimahi_sdslot_switchvdd(NULL, (unsigned int) val); + return 0; +} + +static int mahimahimmc_dbg_sd_pwr_get(void *data, u64 *val) +{ + *val = sdslot_vdd; + return 0; +} + +static int mahimahimmc_dbg_sd_cd_set(void *data, u64 val) +{ + return -ENOSYS; +} + +static int mahimahimmc_dbg_sd_cd_get(void *data, u64 *val) +{ + *val = mahimahi_sdslot_data.status(NULL); + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_wifi_reset_fops, + mahimahimmc_dbg_wifi_reset_get, + mahimahimmc_dbg_wifi_reset_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_wifi_cd_fops, + mahimahimmc_dbg_wifi_cd_get, + mahimahimmc_dbg_wifi_cd_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_wifi_pwr_fops, + mahimahimmc_dbg_wifi_pwr_get, + mahimahimmc_dbg_wifi_pwr_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_sd_pwr_fops, + mahimahimmc_dbg_sd_pwr_get, + mahimahimmc_dbg_sd_pwr_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(mahimahimmc_dbg_sd_cd_fops, + mahimahimmc_dbg_sd_cd_get, + mahimahimmc_dbg_sd_cd_set, "%llu\n"); + +static int __init mahimahimmc_dbg_init(void) +{ + struct dentry *dent; + + if (!machine_is_mahimahi()) + return 0; + + dent = debugfs_create_dir("mahimahi_mmc_dbg", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("wifi_reset", 0644, dent, NULL, + &mahimahimmc_dbg_wifi_reset_fops); + debugfs_create_file("wifi_cd", 0644, dent, NULL, + &mahimahimmc_dbg_wifi_cd_fops); + debugfs_create_file("wifi_pwr", 0644, dent, NULL, + &mahimahimmc_dbg_wifi_pwr_fops); + debugfs_create_file("sd_pwr", 0644, dent, NULL, + &mahimahimmc_dbg_sd_pwr_fops); + debugfs_create_file("sd_cd", 0644, dent, NULL, + &mahimahimmc_dbg_sd_cd_fops); + return 0; +} + +device_initcall(mahimahimmc_dbg_init); +#endif diff --git a/arch/arm/mach-msm/board-mahimahi-panel.c b/arch/arm/mach-msm/board-mahimahi-panel.c new file mode 100644 index 0000000000000..c59c51f9b690b --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-panel.c @@ -0,0 +1,635 @@ +/* linux/arch/arm/mach-msm/board-mahimahi-panel.c + * + * Copyright (c) 2009 Google Inc. + * Author: Dima Zavin + * + * 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 "board-mahimahi.h" +#include "devices.h" + + +#define SPI_CONFIG (0x00000000) +#define SPI_IO_CONTROL (0x00000004) +#define SPI_OPERATIONAL (0x00000030) +#define SPI_ERROR_FLAGS_EN (0x00000038) +#define SPI_ERROR_FLAGS (0x00000038) +#define SPI_OUTPUT_FIFO (0x00000100) + +static void __iomem *spi_base; +static struct clk *spi_clk ; + +static int qspi_send(uint32_t id, uint8_t data) +{ + uint32_t err; + + /* bit-5: OUTPUT_FIFO_NOT_EMPTY */ + while (readl(spi_base + SPI_OPERATIONAL) & (1<<5)) { + if ((err = readl(spi_base + SPI_ERROR_FLAGS))) { + pr_err("%s: ERROR: SPI_ERROR_FLAGS=0x%08x\n", __func__, + err); + return -EIO; + } + } + writel((0x7000 | (id << 9) | data) << 16, spi_base + SPI_OUTPUT_FIFO); + udelay(100); + + return 0; +} + +static int lcm_writeb(uint8_t reg, uint8_t val) +{ + qspi_send(0x0, reg); + qspi_send(0x1, val); + return 0; +} + +static int lcm_writew(uint8_t reg, uint16_t val) +{ + qspi_send(0x0, reg); + qspi_send(0x1, val >> 8); + qspi_send(0x1, val & 0xff); + return 0; +} + +static struct resource resources_msm_fb[] = { + { + .start = MSM_FB_BASE, + .end = MSM_FB_BASE + MSM_FB_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct lcm_tbl { + uint8_t reg; + uint8_t val; +}; + +static struct lcm_tbl samsung_oled_init_table[] = { + { 0x31, 0x08 }, + { 0x32, 0x14 }, + { 0x30, 0x2 }, + { 0x27, 0x1 }, + { 0x12, 0x8 }, + { 0x13, 0x8 }, + { 0x15, 0x0 }, + { 0x16, 0x02 }, + { 0x39, 0x24 }, + { 0x17, 0x22 }, + { 0x18, 0x33 }, + { 0x19, 0x3 }, + { 0x1A, 0x1 }, + { 0x22, 0xA4 }, + { 0x23, 0x0 }, + { 0x26, 0xA0 }, +}; + +#define OLED_GAMMA_TABLE_SIZE (7 * 3) +static struct lcm_tbl samsung_oled_gamma_table[][OLED_GAMMA_TABLE_SIZE] = { + /* level 10 */ + { + /* Gamma-R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x3f }, + { 0x43, 0x35 }, + { 0x44, 0x30 }, + { 0x45, 0x2c }, + { 0x46, 0x13 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x0 }, + { 0x53, 0x0 }, + { 0x54, 0x27 }, + { 0x55, 0x2b }, + { 0x56, 0x12 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x3f }, + { 0x63, 0x34 }, + { 0x64, 0x2f }, + { 0x65, 0x2b }, + { 0x66, 0x1b }, + }, + + /* level 40 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x3e }, + { 0x43, 0x2e }, + { 0x44, 0x2d }, + { 0x45, 0x28 }, + { 0x46, 0x21 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x0 }, + { 0x53, 0x21 }, + { 0x54, 0x2a }, + { 0x55, 0x28 }, + { 0x56, 0x20 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x3e }, + { 0x63, 0x2d }, + { 0x64, 0x2b }, + { 0x65, 0x26 }, + { 0x66, 0x2d }, + }, + + /* level 70 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x35 }, + { 0x43, 0x2c }, + { 0x44, 0x2b }, + { 0x45, 0x26 }, + { 0x46, 0x29 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x0 }, + { 0x53, 0x25 }, + { 0x54, 0x29 }, + { 0x55, 0x26 }, + { 0x56, 0x28 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x34 }, + { 0x63, 0x2b }, + { 0x64, 0x2a }, + { 0x65, 0x23 }, + { 0x66, 0x37 }, + }, + + /* level 100 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x30 }, + { 0x43, 0x2a }, + { 0x44, 0x2b }, + { 0x45, 0x24 }, + { 0x46, 0x2f }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x0 }, + { 0x53, 0x25 }, + { 0x54, 0x29 }, + { 0x55, 0x24 }, + { 0x56, 0x2e }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x2f }, + { 0x63, 0x29 }, + { 0x64, 0x29 }, + { 0x65, 0x21 }, + { 0x66, 0x3f }, + }, + + /* level 130 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x2e }, + { 0x43, 0x29 }, + { 0x44, 0x2a }, + { 0x45, 0x23 }, + { 0x46, 0x34 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0xa }, + { 0x53, 0x25 }, + { 0x54, 0x28 }, + { 0x55, 0x23 }, + { 0x56, 0x33 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x2d }, + { 0x63, 0x28 }, + { 0x64, 0x27 }, + { 0x65, 0x20 }, + { 0x66, 0x46 }, + }, + + /* level 160 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x2b }, + { 0x43, 0x29 }, + { 0x44, 0x28 }, + { 0x45, 0x23 }, + { 0x46, 0x38 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0xb }, + { 0x53, 0x25 }, + { 0x54, 0x27 }, + { 0x55, 0x23 }, + { 0x56, 0x37 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x29 }, + { 0x63, 0x28 }, + { 0x64, 0x25 }, + { 0x65, 0x20 }, + { 0x66, 0x4b }, + }, + + /* level 190 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x29 }, + { 0x43, 0x29 }, + { 0x44, 0x27 }, + { 0x45, 0x22 }, + { 0x46, 0x3c }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x10 }, + { 0x53, 0x26 }, + { 0x54, 0x26 }, + { 0x55, 0x22 }, + { 0x56, 0x3b }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x28 }, + { 0x63, 0x28 }, + { 0x64, 0x24 }, + { 0x65, 0x1f }, + { 0x66, 0x50 }, + }, + + /* level 220 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x28 }, + { 0x43, 0x28 }, + { 0x44, 0x28 }, + { 0x45, 0x20 }, + { 0x46, 0x40 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x11 }, + { 0x53, 0x25 }, + { 0x54, 0x27 }, + { 0x55, 0x20 }, + { 0x56, 0x3f }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x27 }, + { 0x63, 0x26 }, + { 0x64, 0x26 }, + { 0x65, 0x1c }, + { 0x66, 0x56 }, + }, + + /* level 250 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x2a }, + { 0x43, 0x27 }, + { 0x44, 0x27 }, + { 0x45, 0x1f }, + { 0x46, 0x44 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x17 }, + { 0x53, 0x24 }, + { 0x54, 0x26 }, + { 0x55, 0x1f }, + { 0x56, 0x43 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x2a }, + { 0x63, 0x25 }, + { 0x64, 0x24 }, + { 0x65, 0x1b }, + { 0x66, 0x5c }, + }, +}; +#define SAMSUNG_OLED_NUM_LEVELS ARRAY_SIZE(samsung_oled_gamma_table) + +#define SAMSUNG_OLED_MIN_VAL 10 +#define SAMSUNG_OLED_MAX_VAL 250 +#define SAMSUNG_OLED_DEFAULT_VAL (SAMSUNG_OLED_MIN_VAL + \ + (SAMSUNG_OLED_MAX_VAL - \ + SAMSUNG_OLED_MIN_VAL) / 2) + +#define SAMSUNG_OLED_LEVEL_STEP ((SAMSUNG_OLED_MAX_VAL - \ + SAMSUNG_OLED_MIN_VAL) / \ + (SAMSUNG_OLED_NUM_LEVELS - 1)) + +static DEFINE_MUTEX(panel_lock); +static struct work_struct brightness_delayed_work; +static DEFINE_SPINLOCK(brightness_lock); +static uint8_t new_val = SAMSUNG_OLED_DEFAULT_VAL; +static uint8_t last_val = SAMSUNG_OLED_DEFAULT_VAL; +static uint8_t table_sel_vals[] = { 0x43, 0x34 }; +static int table_sel_idx = 0; +static void gamma_table_bank_select(void) +{ + lcm_writeb(0x39, table_sel_vals[table_sel_idx]); + table_sel_idx ^= 1; +} + +static void samsung_oled_set_gamma_val(int val) +{ + int i; + int level; + int frac; + + val = clamp(val, SAMSUNG_OLED_MIN_VAL, SAMSUNG_OLED_MAX_VAL); + val = (val / 2) * 2; + + level = (val - SAMSUNG_OLED_MIN_VAL) / SAMSUNG_OLED_LEVEL_STEP; + frac = (val - SAMSUNG_OLED_MIN_VAL) % SAMSUNG_OLED_LEVEL_STEP; + + clk_enable(spi_clk); + + for (i = 0; i < OLED_GAMMA_TABLE_SIZE; ++i) { + unsigned int v1; + unsigned int v2 = 0; + u8 v; + if (frac == 0) { + v = samsung_oled_gamma_table[level][i].val; + } else { + + v1 = samsung_oled_gamma_table[level][i].val; + v2 = samsung_oled_gamma_table[level+1][i].val; + v = (v1 * (SAMSUNG_OLED_LEVEL_STEP - frac) + + v2 * frac) / SAMSUNG_OLED_LEVEL_STEP; + } + lcm_writeb(samsung_oled_gamma_table[level][i].reg, v); + } + + gamma_table_bank_select(); + clk_disable(spi_clk); + last_val = val; +} + +static int samsung_oled_panel_init(struct msm_lcdc_panel_ops *ops) +{ + pr_info("%s: +()\n", __func__); + mutex_lock(&panel_lock); + + clk_enable(spi_clk); + /* Set the gamma write target to 4, leave the current gamma set at 2 */ + lcm_writeb(0x39, 0x24); + clk_disable(spi_clk); + + mutex_unlock(&panel_lock); + pr_info("%s: -()\n", __func__); + return 0; +} + +static int samsung_oled_panel_unblank(struct msm_lcdc_panel_ops *ops) +{ + int i; + + pr_info("%s: +()\n", __func__); + + mutex_lock(&panel_lock); + + gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1); + udelay(50); + gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0); + udelay(20); + gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1); + msleep(20); + + clk_enable(spi_clk); + + for (i = 0; i < ARRAY_SIZE(samsung_oled_init_table); i++) + lcm_writeb(samsung_oled_init_table[i].reg, + samsung_oled_init_table[i].val); + lcm_writew(0xef, 0xd0e8); + lcm_writeb(0x1d, 0xa0); + table_sel_idx = 0; + gamma_table_bank_select(); + samsung_oled_set_gamma_val(last_val); + msleep(250); + lcm_writeb(0x14, 0x03); + clk_disable(spi_clk); + + mutex_unlock(&panel_lock); + + pr_info("%s: -()\n", __func__); + return 0; +} + +static int samsung_oled_panel_blank(struct msm_lcdc_panel_ops *ops) +{ + pr_info("%s: +()\n", __func__); + mutex_lock(&panel_lock); + + clk_enable(spi_clk); + lcm_writeb(0x14, 0x0); + mdelay(1); + lcm_writeb(0x1d, 0xa1); + clk_disable(spi_clk); + msleep(200); + + gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0); + + mutex_unlock(&panel_lock); + pr_info("%s: -()\n", __func__); + return 0; +} + +static struct msm_lcdc_panel_ops mahimahi_lcdc_panel_ops = { + .init = samsung_oled_panel_init, + .blank = samsung_oled_panel_blank, + .unblank = samsung_oled_panel_unblank, +}; + +static struct msm_lcdc_timing mahimahi_lcdc_timing = { + .clk_rate = 24576000, + .hsync_pulse_width = 4, + .hsync_back_porch = 8, + .hsync_front_porch = 8, + .hsync_skew = 0, + .vsync_pulse_width = 2, + .vsync_back_porch = 8, + .vsync_front_porch = 8, + .vsync_act_low = 1, + .hsync_act_low = 1, + .den_act_low = 1, +}; + +static struct msm_fb_data mahimahi_lcdc_fb_data = { + .xres = 480, + .yres = 800, + .width = 48, + .height = 80, + .output_format = 0, +}; + +static struct msm_lcdc_platform_data mahimahi_lcdc_platform_data = { + .panel_ops = &mahimahi_lcdc_panel_ops, + .timing = &mahimahi_lcdc_timing, + .fb_id = 0, + .fb_data = &mahimahi_lcdc_fb_data, + .fb_resource = &resources_msm_fb[0], +}; + +static struct platform_device mahimahi_lcdc_device = { + .name = "msm_mdp_lcdc", + .id = -1, + .dev = { + .platform_data = &mahimahi_lcdc_platform_data, + }, +}; + +static int mahimahi_init_spi_hack(void) +{ + int ret; + + spi_base = ioremap(MSM_SPI_PHYS, MSM_SPI_SIZE); + if (!spi_base) + return -1; + + spi_clk = clk_get(&msm_device_spi.dev, "spi_clk"); + if (IS_ERR(spi_clk)) { + pr_err("%s: unable to get spi_clk\n", __func__); + ret = PTR_ERR(spi_clk); + goto err_clk_get; + } + + clk_enable(spi_clk); + + printk("spi: SPI_CONFIG=%x\n", readl(spi_base + SPI_CONFIG)); + printk("spi: SPI_IO_CONTROL=%x\n", readl(spi_base + SPI_IO_CONTROL)); + printk("spi: SPI_OPERATIONAL=%x\n", readl(spi_base + SPI_OPERATIONAL)); + printk("spi: SPI_ERROR_FLAGS_EN=%x\n", + readl(spi_base + SPI_ERROR_FLAGS_EN)); + printk("spi: SPI_ERROR_FLAGS=%x\n", readl(spi_base + SPI_ERROR_FLAGS)); + printk("-%s()\n", __FUNCTION__); + clk_disable(spi_clk); + + return 0; + +err_clk_get: + iounmap(spi_base); + return ret; +} + +static void mahimahi_brightness_set(struct led_classdev *led_cdev, + enum led_brightness val) +{ + unsigned long flags; + led_cdev->brightness = val; + + spin_lock_irqsave(&brightness_lock, flags); + new_val = val; + spin_unlock_irqrestore(&brightness_lock, flags); + + schedule_work(&brightness_delayed_work); +} + +static void mahimahi_brightness_set_work(struct work_struct *work_ptr) +{ + unsigned long flags; + uint8_t val; + + spin_lock_irqsave(&brightness_lock, flags); + val = new_val; + spin_unlock_irqrestore(&brightness_lock, flags); + + mutex_lock(&panel_lock); + samsung_oled_set_gamma_val(val); + mutex_unlock(&panel_lock); +} + +static struct led_classdev mahimahi_brightness_led = { + .name = "lcd-backlight", + .brightness = LED_FULL, + .brightness_set = mahimahi_brightness_set, +}; + +int __init mahimahi_init_panel(void) +{ + int ret; + + if (!machine_is_mahimahi()) + return 0; + + ret = platform_device_register(&msm_device_mdp); + if (ret != 0) + return ret; + + ret = mahimahi_init_spi_hack(); + if (ret != 0) + return ret; + + INIT_WORK(&brightness_delayed_work, mahimahi_brightness_set_work); + + ret = platform_device_register(&mahimahi_lcdc_device); + if (ret != 0) + return ret; + + ret = led_classdev_register(NULL, &mahimahi_brightness_led); + if (ret != 0) { + pr_err("%s: Cannot register brightness led\n", __func__); + return ret; + } + + return 0; +} + +device_initcall(mahimahi_init_panel); diff --git a/arch/arm/mach-msm/board-mahimahi-rfkill.c b/arch/arm/mach-msm/board-mahimahi-rfkill.c new file mode 100644 index 0000000000000..870c4bad15385 --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-rfkill.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation. + * + * 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 "board-mahimahi.h" + +static struct rfkill *bt_rfk; +static const char bt_name[] = "bcm4329"; + +static int bluetooth_set_power(void *data, bool blocked) +{ + if (!blocked) { + gpio_direction_output(MAHIMAHI_GPIO_BT_RESET_N, 1); + gpio_direction_output(MAHIMAHI_GPIO_BT_SHUTDOWN_N, 1); + } else { + gpio_direction_output(MAHIMAHI_GPIO_BT_SHUTDOWN_N, 0); + gpio_direction_output(MAHIMAHI_GPIO_BT_RESET_N, 0); + } + return 0; +} + +static struct rfkill_ops mahimahi_rfkill_ops = { + .set_block = bluetooth_set_power, +}; + +static int mahimahi_rfkill_probe(struct platform_device *pdev) +{ + int rc = 0; + bool default_state = true; /* off */ + + rc = gpio_request(MAHIMAHI_GPIO_BT_RESET_N, "bt_reset"); + if (rc) + goto err_gpio_reset; + rc = gpio_request(MAHIMAHI_GPIO_BT_SHUTDOWN_N, "bt_shutdown"); + if (rc) + goto err_gpio_shutdown; + + bluetooth_set_power(NULL, default_state); + + bt_rfk = rfkill_alloc(bt_name, &pdev->dev, RFKILL_TYPE_BLUETOOTH, + &mahimahi_rfkill_ops, NULL); + if (!bt_rfk) { + rc = -ENOMEM; + goto err_rfkill_alloc; + } + + rfkill_set_states(bt_rfk, default_state, default_state); + + /* userspace cannot take exclusive control */ + + rc = rfkill_register(bt_rfk); + if (rc) + goto err_rfkill_reg; + + return 0; + +err_rfkill_reg: + rfkill_destroy(bt_rfk); +err_rfkill_alloc: + gpio_free(MAHIMAHI_GPIO_BT_SHUTDOWN_N); +err_gpio_shutdown: + gpio_free(MAHIMAHI_GPIO_BT_RESET_N); +err_gpio_reset: + return rc; +} + +static int mahimahi_rfkill_remove(struct platform_device *dev) +{ + rfkill_unregister(bt_rfk); + rfkill_destroy(bt_rfk); + gpio_free(MAHIMAHI_GPIO_BT_SHUTDOWN_N); + gpio_free(MAHIMAHI_GPIO_BT_RESET_N); + + return 0; +} + +static struct platform_driver mahimahi_rfkill_driver = { + .probe = mahimahi_rfkill_probe, + .remove = mahimahi_rfkill_remove, + .driver = { + .name = "mahimahi_rfkill", + .owner = THIS_MODULE, + }, +}; + +static int __init mahimahi_rfkill_init(void) +{ + if (!machine_is_mahimahi()) + return 0; + + return platform_driver_register(&mahimahi_rfkill_driver); +} + +static void __exit mahimahi_rfkill_exit(void) +{ + platform_driver_unregister(&mahimahi_rfkill_driver); +} + +module_init(mahimahi_rfkill_init); +module_exit(mahimahi_rfkill_exit); +MODULE_DESCRIPTION("mahimahi rfkill"); +MODULE_AUTHOR("Nick Pelly "); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-mahimahi-wifi.c b/arch/arm/mach-msm/board-mahimahi-wifi.c new file mode 100644 index 0000000000000..28bdc55add81f --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-wifi.c @@ -0,0 +1,140 @@ +/* linux/arch/arm/mach-msm/board-mahimahi-wifi.c +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-mahimahi.h" + +int mahimahi_wifi_power(int on); +int mahimahi_wifi_reset(int on); +int mahimahi_wifi_set_carddetect(int on); + +#define PREALLOC_WLAN_NUMBER_OF_SECTIONS 4 +#define PREALLOC_WLAN_NUMBER_OF_BUFFERS 160 +#define PREALLOC_WLAN_SECTION_HEADER 24 + +#define WLAN_SECTION_SIZE_0 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128) +#define WLAN_SECTION_SIZE_1 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128) +#define WLAN_SECTION_SIZE_2 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 512) +#define WLAN_SECTION_SIZE_3 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 1024) + +#define WLAN_SKB_BUF_NUM 16 + +static struct sk_buff *wlan_static_skb[WLAN_SKB_BUF_NUM]; + +typedef struct wifi_mem_prealloc_struct { + void *mem_ptr; + unsigned long size; +} wifi_mem_prealloc_t; + +static wifi_mem_prealloc_t wifi_mem_array[PREALLOC_WLAN_NUMBER_OF_SECTIONS] = { + { NULL, (WLAN_SECTION_SIZE_0 + PREALLOC_WLAN_SECTION_HEADER) }, + { NULL, (WLAN_SECTION_SIZE_1 + PREALLOC_WLAN_SECTION_HEADER) }, + { NULL, (WLAN_SECTION_SIZE_2 + PREALLOC_WLAN_SECTION_HEADER) }, + { NULL, (WLAN_SECTION_SIZE_3 + PREALLOC_WLAN_SECTION_HEADER) } +}; + +static void *mahimahi_wifi_mem_prealloc(int section, unsigned long size) +{ + if (section == PREALLOC_WLAN_NUMBER_OF_SECTIONS) + return wlan_static_skb; + if ((section < 0) || (section > PREALLOC_WLAN_NUMBER_OF_SECTIONS)) + return NULL; + if (wifi_mem_array[section].size < size) + return NULL; + return wifi_mem_array[section].mem_ptr; +} + +int __init mahimahi_init_wifi_mem(void) +{ + int i; + + for(i=0;( i < WLAN_SKB_BUF_NUM );i++) { + if (i < (WLAN_SKB_BUF_NUM/2)) + wlan_static_skb[i] = dev_alloc_skb(4096); + else + wlan_static_skb[i] = dev_alloc_skb(8192); + } + for(i=0;( i < PREALLOC_WLAN_NUMBER_OF_SECTIONS );i++) { + wifi_mem_array[i].mem_ptr = kmalloc(wifi_mem_array[i].size, + GFP_KERNEL); + if (wifi_mem_array[i].mem_ptr == NULL) + return -ENOMEM; + } + return 0; +} + +static struct resource mahimahi_wifi_resources[] = { + [0] = { + .name = "bcm4329_wlan_irq", + .start = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_WIFI_IRQ), + .end = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_WIFI_IRQ), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE, + }, +}; + +static struct wifi_platform_data mahimahi_wifi_control = { + .set_power = mahimahi_wifi_power, + .set_reset = mahimahi_wifi_reset, + .set_carddetect = mahimahi_wifi_set_carddetect, + .mem_prealloc = mahimahi_wifi_mem_prealloc, +}; + +static struct platform_device mahimahi_wifi_device = { + .name = "bcm4329_wlan", + .id = 1, + .num_resources = ARRAY_SIZE(mahimahi_wifi_resources), + .resource = mahimahi_wifi_resources, + .dev = { + .platform_data = &mahimahi_wifi_control, + }, +}; + +extern unsigned char *get_wifi_nvs_ram(void); +extern int wifi_calibration_size_set(void); + +static unsigned mahimahi_wifi_update_nvs(char *str) +{ +#define NVS_LEN_OFFSET 0x0C +#define NVS_DATA_OFFSET 0x40 + unsigned char *ptr; + unsigned len; + + if (!str) + return -EINVAL; + ptr = get_wifi_nvs_ram(); + /* Size in format LE assumed */ + memcpy(&len, ptr + NVS_LEN_OFFSET, sizeof(len)); + /* if the last byte in NVRAM is 0, trim it */ + if (ptr[NVS_DATA_OFFSET + len - 1] == 0) + len -= 1; + strcpy(ptr + NVS_DATA_OFFSET + len, str); + len += strlen(str); + memcpy(ptr + NVS_LEN_OFFSET, &len, sizeof(len)); + wifi_calibration_size_set(); + return 0; +} + +static int __init mahimahi_wifi_init(void) +{ + int ret; + + if (!machine_is_mahimahi()) + return 0; + + printk("%s: start\n", __func__); + mahimahi_wifi_update_nvs("sd_oobonly=1\r\n"); + mahimahi_init_wifi_mem(); + ret = platform_device_register(&mahimahi_wifi_device); + return ret; +} + +late_initcall(mahimahi_wifi_init); diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index ef3ebf2f763be..589510677196b 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -15,14 +15,31 @@ * */ +#include #include #include +#include #include #include #include #include #include +#ifdef CONFIG_USB_FUNCTION +#include +#endif +#ifdef CONFIG_USB_ANDROID +#include +#endif + +#include +#include +#include +#include +#include +#include +#include <../../../drivers/staging/android/timed_gpio.h> + #include #include #include @@ -30,45 +47,963 @@ #include #include +#include +#include +#include #include +#include +#include #include "board-mahimahi.h" #include "devices.h" #include "proc_comm.h" +#include "board-mahimahi-flashlight.h" static uint debug_uart; module_param_named(debug_uart, debug_uart, uint, 0); +extern void notify_usb_connected(int); +extern void msm_init_pmic_vibrator(void); +extern void __init mahimahi_audio_init(void); + +extern int microp_headset_has_mic(void); + +static void config_gpio_table(uint32_t *table, int len); + +#ifdef CONFIG_USB_FUNCTION +static char *mahimahi_usb_functions[] = { + "usb_mass_storage", + "adb", +#if defined(CONFIG_USB_FUNCTION_DIAG) + "diag", +#endif +#if defined(CONFIG_USB_FUNCTION_SERIAL) + "fserial", +#endif +}; + +static struct msm_hsusb_product mahimahi_usb_products[] = { + { + .product_id = 0x4e11, + .functions = 0x00000001, /* "usb_mass_storage" only */ + }, + { + .product_id = 0x4e12, + .functions = 0x00000003, /* "usb_mass_storage" and "adb" */ + }, + { + .product_id = 0x4e17, + .functions = 0x00000007, /* ums, adb, diag */ + }, + { + .product_id = 0x4e14, + .functions = 0x0000000b, /* ums, adb, fserial */ + }, + { + .product_id = 0x4e19, + .functions = 0x0000000f, /* ums, adb, diag, fserial */ + }, +}; +#endif + +static int mahimahi_phy_init_seq[] = { + 0x0C, 0x31, + 0x31, 0x32, + 0x1D, 0x0D, + 0x1D, 0x10, + -1 }; + +static void mahimahi_usb_phy_reset(void) +{ + u32 id; + int ret; + + id = PCOM_CLKRGM_APPS_RESET_USB_PHY; + ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_ASSERT, &id, NULL); + if (ret) { + pr_err("%s: Cannot assert (%d)\n", __func__, ret); + return; + } + + msleep(1); + + id = PCOM_CLKRGM_APPS_RESET_USB_PHY; + ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_DEASSERT, &id, NULL); + if (ret) { + pr_err("%s: Cannot assert (%d)\n", __func__, ret); + return; + } +} + +static void mahimahi_usb_hw_reset(bool enable) +{ + u32 id; + int ret; + u32 func; + + id = PCOM_CLKRGM_APPS_RESET_USBH; + if (enable) + func = PCOM_CLK_REGIME_SEC_RESET_ASSERT; + else + func = PCOM_CLK_REGIME_SEC_RESET_DEASSERT; + ret = msm_proc_comm(func, &id, NULL); + if (ret) + pr_err("%s: Cannot set reset to %d (%d)\n", __func__, enable, + ret); +} + + +static struct msm_hsusb_platform_data msm_hsusb_pdata = { + .phy_init_seq = mahimahi_phy_init_seq, + .phy_reset = mahimahi_usb_phy_reset, + .hw_reset = mahimahi_usb_hw_reset, + .usb_connected = notify_usb_connected, +#ifdef CONFIG_USB_FUNCTION + .vendor_id = 0x18d1, + .product_id = 0x4e12, + .version = 0x0100, + .product_name = "Nexus One", + .manufacturer_name = "Google, Inc.", + + .functions = mahimahi_usb_functions, + .num_functions = ARRAY_SIZE(mahimahi_usb_functions), + .products = mahimahi_usb_products, + .num_products = ARRAY_SIZE(mahimahi_usb_products), +#endif +}; + +#ifdef CONFIG_USB_FUNCTION +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .buf_size = 16384, + .vendor = "Google, Inc.", + .product = "Nexus One", + .release = 0x0100, +}; +#endif + +#ifdef CONFIG_USB_ANDROID +static char *usb_functions[] = { + "usb_mass_storage", +#ifdef CONFIG_USB_ANDROID_RNDIS + "rndis", +#endif +#ifdef CONFIG_USB_ANDROID_ACM + "acm", +#endif +}; + +static char *usb_functions_adb[] = { + "usb_mass_storage", + "adb", +#ifdef CONFIG_USB_ANDROID_RNDIS + "rndis", +#endif +#ifdef CONFIG_USB_ANDROID_ACM + "acm", +#endif +}; + +#ifdef CONFIG_USB_ANDROID_DIAG +static char *usb_functions_adb_diag[] = { + "usb_mass_storage", + "adb", + "diag", +}; +#endif + +static char *usb_functions_all[] = { + "usb_mass_storage", + "adb", +#ifdef CONFIG_USB_ANDROID_ACM + "acm", +#endif +#ifdef CONFIG_USB_ANDROID_RNDIS + "rndis", +#endif +#ifdef CONFIG_USB_ANDROID_DIAG + "diag", +#endif +}; + +static struct android_usb_product usb_products[] = { + { + .product_id = 0x4e11, + .num_functions = ARRAY_SIZE(usb_functions), + .functions = usb_functions, + }, + { + .product_id = 0x4e12, + .num_functions = ARRAY_SIZE(usb_functions_adb), + .functions = usb_functions_adb, + }, +#ifdef CONFIG_USB_ANDROID_DIAG + { + .product_id = 0x4e17, + .num_functions = ARRAY_SIZE(usb_functions_adb_diag), + .functions = usb_functions_adb_diag, + }, +#endif +}; + +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .vendor = "Google, Inc.", + .product = "Nexus One", + .release = 0x0100, +}; +#endif + +static struct platform_device usb_mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &mass_storage_pdata, + }, +}; + +#ifdef CONFIG_USB_ANDROID +static struct android_usb_platform_data android_usb_pdata = { + .vendor_id = 0x18d1, + .product_id = 0x4e11, + .version = 0x0100, + .product_name = "Nexus One", + .manufacturer_name = "Google, Inc.", + .num_products = ARRAY_SIZE(usb_products), + .products = usb_products, + .num_functions = ARRAY_SIZE(usb_functions_all), + .functions = usb_functions_all, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; +#endif + +static struct platform_device mahimahi_rfkill = { + .name = "mahimahi_rfkill", + .id = -1, +}; + +static struct resource msm_kgsl_resources[] = { + { + .name = "kgsl_reg_memory", + .start = MSM_GPU_REG_PHYS, + .end = MSM_GPU_REG_PHYS + MSM_GPU_REG_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "kgsl_phys_memory", + .start = MSM_GPU_MEM_BASE, + .end = MSM_GPU_MEM_BASE + MSM_GPU_MEM_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_GRAPHICS, + .end = INT_GRAPHICS, + .flags = IORESOURCE_IRQ, + }, +}; + +#define PWR_RAIL_GRP_CLK 8 +static int mahimahi_kgsl_power_rail_mode(int follow_clk) +{ + int mode = follow_clk ? 0 : 1; + int rail_id = PWR_RAIL_GRP_CLK; + + return msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &rail_id, &mode); +} + +static int mahimahi_kgsl_power(bool on) +{ + int cmd; + int rail_id = PWR_RAIL_GRP_CLK; + + cmd = on ? PCOM_CLKCTL_RPC_RAIL_ENABLE : PCOM_CLKCTL_RPC_RAIL_DISABLE; + return msm_proc_comm(cmd, &rail_id, NULL); +} + +static struct platform_device msm_kgsl_device = { + .name = "kgsl", + .id = -1, + .resource = msm_kgsl_resources, + .num_resources = ARRAY_SIZE(msm_kgsl_resources), +}; + +static struct android_pmem_platform_data mdp_pmem_pdata = { + .name = "pmem", + .start = MSM_PMEM_MDP_BASE, + .size = MSM_PMEM_MDP_SIZE, + .no_allocator = 0, + .cached = 1, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .start = MSM_PMEM_ADSP_BASE, + .size = MSM_PMEM_ADSP_SIZE, + .no_allocator = 0, + .cached = 1, +}; + +static struct android_pmem_platform_data android_pmem_camera_pdata = { + .name = "pmem_camera", + .start = MSM_PMEM_CAMERA_BASE, + .size = MSM_PMEM_CAMERA_SIZE, + .no_allocator = 1, + .cached = 1, +}; + +static struct platform_device android_pmem_mdp_device = { + .name = "android_pmem", + .id = 0, + .dev = { + .platform_data = &mdp_pmem_pdata + }, +}; + +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 1, + .dev = { + .platform_data = &android_pmem_adsp_pdata, + }, +}; + +static struct platform_device android_pmem_camera_device = { + .name = "android_pmem", + .id = 2, + .dev = { + .platform_data = &android_pmem_camera_pdata, + }, +}; + +static struct resource ram_console_resources[] = { + { + .start = MSM_RAM_CONSOLE_BASE, + .end = MSM_RAM_CONSOLE_BASE + MSM_RAM_CONSOLE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device ram_console_device = { + .name = "ram_console", + .id = -1, + .num_resources = ARRAY_SIZE(ram_console_resources), + .resource = ram_console_resources, +}; + +static int mahimahi_ts_power(int on) +{ + pr_info("%s: power %d\n", __func__, on); + + if (on) { + /* level shifter should be off */ + gpio_set_value(MAHIMAHI_GPIO_TP_EN, 1); + msleep(120); + /* enable touch panel level shift */ + gpio_set_value(MAHIMAHI_GPIO_TP_LS_EN, 1); + msleep(3); + } else { + gpio_set_value(MAHIMAHI_GPIO_TP_LS_EN, 0); + gpio_set_value(MAHIMAHI_GPIO_TP_EN, 0); + udelay(50); + } + + return 0; +} + +struct cy8c_i2c_platform_data mahimahi_cy8c_ts_data = { + .version = 0x0001, + .abs_x_min = 0, + .abs_x_max = 479, + .abs_y_min = 0, + .abs_y_max = 799, + .abs_pressure_min = 0, + .abs_pressure_max = 255, + .abs_width_min = 0, + .abs_width_max = 10, + .power = mahimahi_ts_power, +}; + +static struct synaptics_i2c_rmi_platform_data mahimahi_synaptics_ts_data[] = { + { + .power = mahimahi_ts_power, + .flags = SYNAPTICS_FLIP_Y, + .inactive_left = -25 * 0x10000 / 480, + .inactive_right = -20 * 0x10000 / 480, + .inactive_top = -15 * 0x10000 / 800, + .inactive_bottom = -40 * 0x10000 / 800, + .sensitivity_adjust = 12, + }, +}; + +static struct a1026_platform_data a1026_data = { + .gpio_a1026_micsel = MAHIMAHI_AUD_MICPATH_SEL, + .gpio_a1026_wakeup = MAHIMAHI_AUD_A1026_WAKEUP, + .gpio_a1026_reset = MAHIMAHI_AUD_A1026_RESET, + .gpio_a1026_clk = MAHIMAHI_AUD_A1026_CLK, + /*.gpio_a1026_int = MAHIMAHI_AUD_A1026_INT,*/ +}; + +static struct akm8973_platform_data compass_platform_data = { + .layouts = MAHIMAHI_LAYOUTS, + .project_name = MAHIMAHI_PROJECT_NAME, + .reset = MAHIMAHI_GPIO_COMPASS_RST_N, + .intr = MAHIMAHI_GPIO_COMPASS_INT_N, +}; + +static struct regulator_consumer_supply tps65023_dcdc1_supplies[] = { + { + .supply = "acpu_vcore", + }, +}; + +static struct regulator_init_data tps65023_data[5] = { + { + .constraints = { + .name = "dcdc1", /* VREG_MSMC2_1V29 */ + .min_uV = 1000000, + .max_uV = 1300000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + }, + .consumer_supplies = tps65023_dcdc1_supplies, + .num_consumer_supplies = ARRAY_SIZE(tps65023_dcdc1_supplies), + }, + /* dummy values for unused regulators to not crash driver: */ + { + .constraints = { + .name = "dcdc2", /* VREG_MSMC1_1V26 */ + .min_uV = 1260000, + .max_uV = 1260000, + }, + }, + { + .constraints = { + .name = "dcdc3", /* unused */ + .min_uV = 800000, + .max_uV = 3300000, + }, + }, + { + .constraints = { + .name = "ldo1", /* unused */ + .min_uV = 1000000, + .max_uV = 3150000, + }, + }, + { + .constraints = { + .name = "ldo2", /* V_USBPHY_3V3 */ + .min_uV = 3300000, + .max_uV = 3300000, + }, + }, +}; + + +static void ds2482_set_slp_n(unsigned n) +{ + gpio_direction_output(MAHIMAHI_GPIO_DS2482_SLP_N, n); +} + +static struct i2c_board_info base_i2c_devices[] = { + { + I2C_BOARD_INFO("ds2482", 0x30 >> 1), + .platform_data = ds2482_set_slp_n, + }, + { + I2C_BOARD_INFO("cy8c-tmg-ts", 0x34), + .platform_data = &mahimahi_cy8c_ts_data, + .irq = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_TP_INT_N), + }, + { + I2C_BOARD_INFO(SYNAPTICS_I2C_RMI_NAME, 0x40), + .platform_data = mahimahi_synaptics_ts_data, + .irq = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_TP_INT_N) + }, + { + I2C_BOARD_INFO("mahimahi-microp", 0x66), + .irq = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_UP_INT_N) + }, + { + I2C_BOARD_INFO("s5k3e2fx", 0x20 >> 1), + }, + { + I2C_BOARD_INFO("tps65023", 0x48), + .platform_data = tps65023_data, + }, +}; + +static struct i2c_board_info rev0_i2c_devices[] = { + { + I2C_BOARD_INFO(AKM8973_I2C_NAME, 0x1C), + .platform_data = &compass_platform_data, + .irq = MSM_GPIO_TO_INT(MAHIMAHI_REV0_GPIO_COMPASS_INT_N), + }, +}; + +static struct i2c_board_info rev1_i2c_devices[] = { + { + I2C_BOARD_INFO("audience_a1026", 0x3E), + .platform_data = &a1026_data, + /*.irq = MSM_GPIO_TO_INT(MAHIMAHI_AUD_A1026_INT)*/ + }, + { + I2C_BOARD_INFO(AKM8973_I2C_NAME, 0x1C), + .platform_data = &compass_platform_data, + .irq = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_COMPASS_INT_N), + }, +}; + +static void config_gpio_table(uint32_t *table, int len); + +static uint32_t camera_off_gpio_table[] = { + /* CAMERA */ + PCOM_GPIO_CFG(0, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(1, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(2, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* PCLK */ + PCOM_GPIO_CFG(13, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* HSYNC */ + PCOM_GPIO_CFG(14, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* VSYNC */ + PCOM_GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* MCLK */ +}; + +static uint32_t camera_on_gpio_table[] = { + /* CAMERA */ + PCOM_GPIO_CFG(0, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT0 */ + PCOM_GPIO_CFG(1, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT1 */ + PCOM_GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_16MA), /* PCLK */ + PCOM_GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* HSYNC */ + PCOM_GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* VSYNC */ + PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* MCLK */ +}; + +void config_camera_on_gpios(void) +{ + config_gpio_table(camera_on_gpio_table, + ARRAY_SIZE(camera_on_gpio_table)); +} + +void config_camera_off_gpios(void) +{ + config_gpio_table(camera_off_gpio_table, + ARRAY_SIZE(camera_off_gpio_table)); +} + +static struct resource msm_camera_resources[] = { + { + .start = MSM_VFE_PHYS, + .end = MSM_VFE_PHYS + MSM_VFE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_VFE, + INT_VFE, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct msm_camera_device_platform_data msm_camera_device_data = { + .camera_gpio_on = config_camera_on_gpios, + .camera_gpio_off = config_camera_off_gpios, + .ioext.mdcphy = MSM_MDC_PHYS, + .ioext.mdcsz = MSM_MDC_SIZE, + .ioext.appphy = MSM_CLK_CTL_PHYS, + .ioext.appsz = MSM_CLK_CTL_SIZE, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_s5k3e2fx_data = { + .sensor_name = "s5k3e2fx", + .sensor_reset = 144, /* CAM1_RST */ + .sensor_pwd = 143, /* CAM1_PWDN, enabled in a9 */ + /*.vcm_pwd = 31, */ /* CAM1_VCM_EN, enabled in a9 */ + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .camera_flash = flashlight_control, + .num_flash_levels = FLASHLIGHT_NUM, +}; + +static struct platform_device msm_camera_sensor_s5k3e2fx = { + .name = "msm_camera_s5k3e2fx", + .dev = { + .platform_data = &msm_camera_sensor_s5k3e2fx_data, + }, +}; + +static int capella_cm3602_power(int on) +{ + /* TODO eolsen Add Voltage reg control */ + if (on) { + gpio_direction_output(MAHIMAHI_GPIO_PROXIMITY_EN, 0); + } else { + gpio_direction_output(MAHIMAHI_GPIO_PROXIMITY_EN, 1); + } + + return 0; +} + + +static struct capella_cm3602_platform_data capella_cm3602_pdata = { + .power = capella_cm3602_power, + .p_out = MAHIMAHI_GPIO_PROXIMITY_INT_N +}; + +static struct platform_device capella_cm3602 = { + .name = CAPELLA_CM3602, + .id = -1, + .dev = { + .platform_data = &capella_cm3602_pdata + } +}; + +static uint32_t flashlight_gpio_table[] = { + PCOM_GPIO_CFG(MAHIMAHI_GPIO_FLASHLIGHT_TORCH, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(MAHIMAHI_GPIO_FLASHLIGHT_FLASH, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), +}; + + +static int config_mahimahi_flashlight_gpios(void) +{ + config_gpio_table(flashlight_gpio_table, + ARRAY_SIZE(flashlight_gpio_table)); + return 0; +} + +static struct flashlight_platform_data mahimahi_flashlight_data = { + .gpio_init = config_mahimahi_flashlight_gpios, + .torch = MAHIMAHI_GPIO_FLASHLIGHT_TORCH, + .flash = MAHIMAHI_GPIO_FLASHLIGHT_FLASH, + .flash_duration_ms = 600 +}; + +static struct platform_device mahimahi_flashlight_device = { + .name = "flashlight", + .dev = { + .platform_data = &mahimahi_flashlight_data, + }, +}; +static struct timed_gpio timed_gpios[] = { + { + .name = "vibrator", + .gpio = MAHIMAHI_GPIO_VIBRATOR_ON, + .max_timeout = 15000, + }, +}; + +static struct timed_gpio_platform_data timed_gpio_data = { + .num_gpios = ARRAY_SIZE(timed_gpios), + .gpios = timed_gpios, +}; + +static struct platform_device mahimahi_timed_gpios = { + .name = "timed-gpio", + .id = -1, + .dev = { + .platform_data = &timed_gpio_data, + }, +}; + +static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { + .rx_wakeup_irq = -1, + .inject_rx_on_wakeup = 0, + .exit_lpm_cb = bcm_bt_lpm_exit_lpm_locked, +}; + +static struct bcm_bt_lpm_platform_data bcm_bt_lpm_pdata = { + .gpio_wake = MAHIMAHI_GPIO_BT_WAKE, + .gpio_host_wake = MAHIMAHI_GPIO_BT_HOST_WAKE, + .request_clock_off_locked = msm_hs_request_clock_off_locked, + .request_clock_on_locked = msm_hs_request_clock_on_locked, +}; + +struct platform_device bcm_bt_lpm_device = { + .name = "bcm_bt_lpm", + .id = 0, + .dev = { + .platform_data = &bcm_bt_lpm_pdata, + }, +}; + static struct platform_device *devices[] __initdata = { #if !defined(CONFIG_MSM_SERIAL_DEBUGGER) &msm_device_uart1, #endif + &bcm_bt_lpm_device, &msm_device_uart_dm1, + &ram_console_device, + &mahimahi_rfkill, + &msm_device_smd, &msm_device_nand, + &msm_device_hsusb, + &usb_mass_storage_device, +#ifdef CONFIG_USB_ANDROID + &android_usb_device, +#endif + &android_pmem_mdp_device, + &android_pmem_adsp_device, + &android_pmem_camera_device, + &msm_kgsl_device, + &msm_device_i2c, + &capella_cm3602, + &msm_camera_sensor_s5k3e2fx, + &mahimahi_flashlight_device, +}; + + +static uint32_t bt_gpio_table[] = { + PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_UART1_RTS, 2, GPIO_OUTPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_UART1_CTS, 2, GPIO_INPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_UART1_RX, 2, GPIO_INPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_UART1_TX, 2, GPIO_OUTPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_RESET_N, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_4MA), + PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_SHUTDOWN_N, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_4MA), + PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_WAKE, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_4MA), + PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_HOST_WAKE, 0, GPIO_INPUT, + GPIO_PULL_DOWN, GPIO_4MA), +}; + +static uint32_t misc_gpio_table[] = { + PCOM_GPIO_CFG(MAHIMAHI_GPIO_LCD_RST_N, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(MAHIMAHI_GPIO_LED_3V3_EN, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(MAHIMAHI_GPIO_DOCK, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_4MA), +}; + +static uint32_t key_int_shutdown_gpio_table[] = { + PCOM_GPIO_CFG(MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), }; +static void mahimahi_headset_init(void) +{ + config_gpio_table(key_int_shutdown_gpio_table, + ARRAY_SIZE(key_int_shutdown_gpio_table)); + gpio_set_value(MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN, 0); +} + +#define ATAG_BDADDR 0x43294329 /* mahimahi bluetooth address tag */ +#define ATAG_BDADDR_SIZE 4 +#define BDADDR_STR_SIZE 18 + +static char bdaddr[BDADDR_STR_SIZE]; + +module_param_string(bdaddr, bdaddr, sizeof(bdaddr), 0400); +MODULE_PARM_DESC(bdaddr, "bluetooth address"); + +static int __init parse_tag_bdaddr(const struct tag *tag) +{ + unsigned char *b = (unsigned char *)&tag->u; + + if (tag->hdr.size != ATAG_BDADDR_SIZE) + return -EINVAL; + + snprintf(bdaddr, BDADDR_STR_SIZE, "%02X:%02X:%02X:%02X:%02X:%02X", + b[0], b[1], b[2], b[3], b[4], b[5]); + + return 0; +} + +__tagtable(ATAG_BDADDR, parse_tag_bdaddr); + +static int __init board_serialno_setup(char *serialno) +{ +#ifdef CONFIG_USB_FUNCTION + msm_hsusb_pdata.serial_number = serialno; +#endif +#ifdef CONFIG_USB_ANDROID + android_usb_pdata.serial_number = serialno; +#endif + return 1; +} +__setup("androidboot.serialno=", board_serialno_setup); + +static void config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for(n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +static struct msm_acpu_clock_platform_data mahimahi_clock_data = { + .acpu_switch_time_us = 20, + .max_speed_delta_khz = 256000, + .vdd_switch_time_us = 62, + .power_collapse_khz = 245000, + .wait_for_irq_khz = 245000, +}; + +static ssize_t mahimahi_virtual_keys_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + if (system_rev > 2) { + /* center: x: back: 55, menu: 172, home: 298, search 412, y: 835 */ + return sprintf(buf, + __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":55:835:90:55" + ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":172:835:125:55" + ":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":298:835:115:55" + ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":412:835:95:55" + "\n"); + } else { + /* center: x: home: 55, menu: 185, back: 305, search 425, y: 835 */ + return sprintf(buf, + __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":55:835:70:55" + ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":185:835:100:55" + ":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":305:835:70:55" + ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":425:835:70:55" + "\n"); + } +} + +static struct kobj_attribute mahimahi_virtual_keys_attr = { + .attr = { + .name = "virtualkeys.synaptics-rmi-touchscreen", + .mode = S_IRUGO, + }, + .show = &mahimahi_virtual_keys_show, +}; + +static struct attribute *mahimahi_properties_attrs[] = { + &mahimahi_virtual_keys_attr.attr, + NULL +}; + +static struct attribute_group mahimahi_properties_attr_group = { + .attrs = mahimahi_properties_attrs, +}; + +static void mahimahi_reset(void) +{ + gpio_set_value(MAHIMAHI_GPIO_PS_HOLD, 0); +} + +int mahimahi_init_mmc(int sysrev, unsigned debug_uart); + static void __init mahimahi_init(void) { + int ret; + struct kobject *properties_kobj; + + printk("mahimahi_init() revision=%d\n", system_rev); + + msm_hw_reset_hook = mahimahi_reset; + + msm_acpu_clock_init(&mahimahi_clock_data); + + msm_serial_debug_init(MSM_UART1_PHYS, INT_UART1, + &msm_device_uart1.dev, 1, MSM_GPIO_TO_INT(139)); + + config_gpio_table(misc_gpio_table, ARRAY_SIZE(misc_gpio_table)); + + config_gpio_table(bt_gpio_table, ARRAY_SIZE(bt_gpio_table)); + gpio_request(MAHIMAHI_GPIO_TP_LS_EN, "tp_ls_en"); + gpio_direction_output(MAHIMAHI_GPIO_TP_LS_EN, 0); + gpio_request(MAHIMAHI_GPIO_TP_EN, "tp_en"); + gpio_direction_output(MAHIMAHI_GPIO_TP_EN, 0); + gpio_request(MAHIMAHI_GPIO_PROXIMITY_EN, "proximity_en"); + gpio_direction_output(MAHIMAHI_GPIO_PROXIMITY_EN, 1); + gpio_request(MAHIMAHI_GPIO_COMPASS_RST_N, "compass_rst"); + gpio_direction_output(MAHIMAHI_GPIO_COMPASS_RST_N, 1); + gpio_request(MAHIMAHI_GPIO_COMPASS_INT_N, "compass_int"); + gpio_direction_input(MAHIMAHI_GPIO_COMPASS_INT_N); + + /* set the gpu power rail to manual mode so clk en/dis will not + * turn off gpu power, and hang it on resume */ + mahimahi_kgsl_power_rail_mode(0); + mahimahi_kgsl_power(true); + + msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; + msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; + platform_add_devices(devices, ARRAY_SIZE(devices)); + + i2c_register_board_info(0, base_i2c_devices, + ARRAY_SIZE(base_i2c_devices)); + + if (system_rev == 0) { + /* Only board after XB with Audience A1026 */ + i2c_register_board_info(0, rev0_i2c_devices, + ARRAY_SIZE(rev0_i2c_devices)); + } + + if (system_rev > 0) { + /* Only board after XB with Audience A1026 */ + i2c_register_board_info(0, rev1_i2c_devices, + ARRAY_SIZE(rev1_i2c_devices)); + } + + ret = mahimahi_init_mmc(system_rev, debug_uart); + if (ret != 0) + pr_crit("%s: Unable to initialize MMC\n", __func__); + + properties_kobj = kobject_create_and_add("board_properties", NULL); + if (properties_kobj) + ret = sysfs_create_group(properties_kobj, + &mahimahi_properties_attr_group); + if (!properties_kobj || ret) + pr_err("failed to create board_properties\n"); + + mahimahi_audio_init(); + mahimahi_headset_init(); + + if (system_rev > 0) + platform_device_register(&mahimahi_timed_gpios); + else + msm_init_pmic_vibrator(); } static void __init mahimahi_fixup(struct machine_desc *desc, struct tag *tags, char **cmdline, struct meminfo *mi) { - mi->nr_banks = 2; + mi->nr_banks = 1; mi->bank[0].start = PHYS_OFFSET; mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); mi->bank[0].size = (219*1024*1024); - mi->bank[1].start = MSM_HIGHMEM_BASE; - mi->bank[1].node = PHYS_TO_NID(MSM_HIGHMEM_BASE); - mi->bank[1].size = MSM_HIGHMEM_SIZE; } static void __init mahimahi_map_io(void) { - msm_map_common_io(); - msm_clock_init(); + msm_map_qsd8x50_io(); + msm_clock_init(msm_clocks_8x50, msm_num_clocks_8x50); } extern struct sys_timer msm_timer; diff --git a/arch/arm/mach-msm/board-mahimahi.h b/arch/arm/mach-msm/board-mahimahi.h new file mode 100644 index 0000000000000..a858a3371c1f7 --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi.h @@ -0,0 +1,123 @@ +/* arch/arm/mach-msm/board-mahimahi.h + * + * Copyright (C) 2009 HTC Corporation. + * Author: Haley Teng + * + * 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 __ARCH_ARM_MACH_MSM_BOARD_MAHIMAHI_H +#define __ARCH_ARM_MACH_MSM_BOARD_MAHIMAHI_H + +#include + +#define MSM_SMI_BASE 0x02B00000 +#define MSM_SMI_SIZE 0x01500000 + +#define MSM_RAM_CONSOLE_BASE 0x03A00000 +#define MSM_RAM_CONSOLE_SIZE 0x00040000 + +#define MSM_FB_BASE 0x03B00000 +#define MSM_FB_SIZE 0x00465000 + +#define MSM_EBI1_BANK0_BASE 0x20000000 +#define MSM_EBI1_BANK0_SIZE 0x0E000000 + +#define MSM_GPU_MEM_BASE 0x2DB00000 +#define MSM_GPU_MEM_SIZE 0x00300000 + +#define MSM_EBI1_BANK1_BASE 0x30000000 +#define MSM_EBI1_BANK1_SIZE 0x10000000 + +#define MSM_PMEM_MDP_BASE 0x30000000 +#define MSM_PMEM_MDP_SIZE 0x02000000 + +#define MSM_PMEM_ADSP_BASE 0x32000000 +#define MSM_PMEM_ADSP_SIZE 0x02900000 + +#define MSM_PMEM_CAMERA_BASE 0x34900000 +#define MSM_PMEM_CAMERA_SIZE 0x00800000 + +#define MAHIMAHI_GPIO_PS_HOLD 25 + +#define MAHIMAHI_GPIO_UP_INT_N 35 +#define MAHIMAHI_GPIO_UP_RESET_N 82 +#define MAHIMAHI_GPIO_LS_EN_N 119 + +#define MAHIMAHI_GPIO_TP_INT_N 92 +#define MAHIMAHI_GPIO_TP_LS_EN 93 +#define MAHIMAHI_GPIO_TP_EN 160 + +#define MAHIMAHI_GPIO_POWER_KEY 94 +#define MAHIMAHI_GPIO_SDMC_CD_REV0_N 153 + +#define MAHIMAHI_GPIO_WIFI_SHUTDOWN_N 127 +#define MAHIMAHI_GPIO_WIFI_IRQ 152 + +#define MAHIMAHI_GPIO_BALL_UP 38 +#define MAHIMAHI_GPIO_BALL_DOWN 37 +#define MAHIMAHI_GPIO_BALL_LEFT 145 +#define MAHIMAHI_GPIO_BALL_RIGHT 21 + +#define MAHIMAHI_GPIO_BT_UART1_RTS 43 +#define MAHIMAHI_GPIO_BT_UART1_CTS 44 +#define MAHIMAHI_GPIO_BT_UART1_RX 45 +#define MAHIMAHI_GPIO_BT_UART1_TX 46 +#define MAHIMAHI_GPIO_BT_RESET_N 146 +#define MAHIMAHI_GPIO_BT_SHUTDOWN_N 128 + +#define MAHIMAHI_GPIO_BT_WAKE 57 +#define MAHIMAHI_GPIO_BT_HOST_WAKE 86 + +#define MAHIMAHI_GPIO_PROXIMITY_INT_N 90 +#define MAHIMAHI_GPIO_PROXIMITY_EN 120 + +#define MAHIMAHI_GPIO_DS2482_SLP_N 87 +#define MAHIMAHI_GPIO_VIBRATOR_ON 89 +/* Compass */ +#define MAHIMAHI_REV0_GPIO_COMPASS_INT_N 36 + +#define MAHIMAHI_GPIO_COMPASS_INT_N 153 +#define MAHIMAHI_GPIO_COMPASS_RST_N 107 +#define MAHIMAHI_PROJECT_NAME "mahimahi" +#define MAHIMAHI_LAYOUTS { \ + { {-1, 0, 0}, { 0, -1, 0}, {0, 0, 1} }, \ + { { 0, -1, 0}, { 1, 0, 0}, {0, 0, -1} }, \ + { { 0, -1, 0}, { 1, 0, 0}, {0, 0, 1} }, \ + { {-1, 0, 0}, { 0, 0, -1}, {0, 1, 0} } \ +} + +/* Audio */ +#define MAHIMAHI_AUD_JACKHP_EN 157 +#define MAHIMAHI_AUD_2V5_EN 158 +#define MAHIMAHI_AUD_MICPATH_SEL 111 +#define MAHIMAHI_AUD_A1026_INT 112 +#define MAHIMAHI_AUD_A1026_WAKEUP 113 +#define MAHIMAHI_AUD_A1026_RESET 129 +#define MAHIMAHI_AUD_A1026_CLK -1 + +/* Bluetooth PCM */ +#define MAHIMAHI_BT_PCM_OUT 68 +#define MAHIMAHI_BT_PCM_IN 69 +#define MAHIMAHI_BT_PCM_SYNC 70 +#define MAHIMAHI_BT_PCM_CLK 71 +/* flash light */ +#define MAHIMAHI_GPIO_FLASHLIGHT_TORCH 58 +#define MAHIMAHI_GPIO_FLASHLIGHT_FLASH 84 + +#define MAHIMAHI_GPIO_LED_3V3_EN 85 +#define MAHIMAHI_GPIO_LCD_RST_N 29 + +/* 3.5mm remote control key interrupt shutdown signal */ +#define MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN 19 + +#define MAHIMAHI_GPIO_DOCK 106 + +#endif /* __ARCH_ARM_MACH_MSM_BOARD_MAHIMAHI_H */ From 98925bc9a855e478dda9e445089fc6d639a1f33c Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 19 Oct 2009 04:00:49 -0700 Subject: [PATCH 0462/2556] [ARM] msm: Add mahimahi to makefile and Kconfig Change-Id: I920a188ec90cd55f48aa5fde32f52556e54ff44e Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Kconfig | 7 +++++++ arch/arm/mach-msm/Makefile | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index b7e59c714e61a..f786e8a5de6db 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -210,6 +210,13 @@ config MACH_SAPPHIRE default y bool "Sapphire" +config MACH_MAHIMAHI + depends on ARCH_QSD8X50 + default y + bool "Mahi-Mahi" + help + Select this to support the Mahi-Mahi device + config HTC_HEADSET tristate "HTC 2 Wire detection driver" default n diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 795e3fedb3fcc..bc4fb531244d0 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -56,6 +56,14 @@ obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-mmc.o board-sapphire-wifi.o obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-rfkill.o msm_vibrator.o obj-$(CONFIG_MACH_SAPPHIRE) += devices-msm7x00.o devices_htc.o +obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi.o board-mahimahi-panel.o +obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-keypad.o board-mahimahi-mmc.o +obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-rfkill.o htc_wifi_nvs.o +obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-wifi.o board-mahimahi-audio.o +obj-$(CONFIG_MACH_MAHIMAHI) += msm_vibrator.o +obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-microp.o +obj-$(CONFIG_MACH_MAHIMAHI) += htc_acoustic_qsd.o +obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-flashlight.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-panel.o fish_battery.o From 330a38987ec7fbdd4a682d46d26a0d9602bf72b2 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 19 Oct 2009 04:01:24 -0700 Subject: [PATCH 0463/2556] [ARM] msm: configs: add mahimahi_defconfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ie534c6a33d3451f19a004eddd9b347c15e5d3764 Signed-off-by: Dima Zavin mahimahi_defconfig: Enable REGULATOR_TPS65023 for power savings Signed-off-by: Mike Chan [ARM] mahimahi_defconfig: Tune cpufreq thresholds. Signed-off-by: Mike Chan [ARM] mahimahi: defconfig: Enable control groups + group scheduling in defconfig Signed-off-by: San Mehat [ARM] msm: mahimahi: Use bcm_bt_lpm on mahimahi. Change-Id: Ied56a75e008820e1ed94b317bdac533c050a220b Signed-off-by: Nick Pelly [ARM] mahimahi_defconfig: Update defconfig Change-Id: I332407f5a5888b456a8852e9aa7b693a991b9eaf Signed-off-by: Dima Zavin mahimahi_defconfig: cpufreq cleanup Signed-off-by: Mike Chan mahimahi_defconfig: Set userspace governor as default This prevents us from using 998mhz on boot before userspace can enforce the min/max limits, since we boot @ 1.25v and 998mhz requires 1.275v Signed-off-by: Mike Chan [ARM] msm: mahimahi: Enable power collapse on idle Change-Id: Ie56f37ff1da33d48a48805fbac9ab16827071f6f Signed-off-by: Arve Hjønnevåg [ARM] mahimahi_defconfig: Don't compile the cypress tmg touch driver Change-Id: I1071c56c876463c44ec5e37ea4166a534d2f61fe Signed-off-by: Dima Zavin [ARM] mahimahi_defconfig: enable CONFIG_DEBUG_BUGVERBOSE Signed-off-by: Iliyan Malchev [ARM] configs: mahimahi: Add IPV6 Tunneling Signed-off-by: Dmitry Shmidt mahimahi_defconfig: Change wfi sleep time to 50ms Change-Id: Iedeabb0352b7fd3cd307e87c47c334d6d3635581 Signed-off-by: Mike Chan [ARM] msm: mahimahi_defconfig: remove some debugging config options Change-Id: I612f925b926f4f5e0f8e8600e3684a70251c0039 Signed-off-by: Dima Zavin mahimahi_defconfig: Enable media support on 2.6.32 Change-Id: If46a20dab48a691487046b33db1d24d1d76250af [ARM] mahimahi: defconfig: Enable BLK_DEV_LOOP in defconfig Signed-off-by: San Mehat [ARM] msm: mahimahi: Switch from USB function to USB gadget drivers. Change-Id: Ic07b1001fd1a77d98a2f4707a98bd4e247bba551 Signed-off-by: Mike Lockwood [ARM] defconfig: mahimahi: Enable NETFILTER, NAT, and related options Signed-off-by: San Mehat [ARM] msm: mahimahi_defconfig: remove CONFIG_W1_SLAVE_DS2784 Signed-off-by: Iliyan Malchev [ARM] msm: mahimahi_defconfig: Enable highmem and reserve 768M vmalloc space. Change-Id: Ie477cc14281968f4a147706c641825cfed65ec92 Signed-off-by: Arve Hjønnevåg [ARM] msm: mahimahi: Disable ACM serial support in defconfig Signed-off-by: Mike Lockwood [ARM] msm: mahimahi: Enable CONFIG_USB_ANDROID_RNDIS in defconfig Signed-off-by: Mike Lockwood mahimahi_defconfig: fix usb on 2.6.33 Change-Id: I71cc5803f38df01fcf7467bf3b0a2cc4f7fd0d0e [ARM] mahimahi_defconfig: turn off swap Signed-off-by: Iliyan Malchev [ARM] msm: mahimahi_defconfig: Enable CONFIG_USB_ANDROID_RNDIS_WCEIS Change-Id: If241d0589ef66a56b4548346585a6f5677ec24ac Signed-off-by: Mike Lockwood [ARM] configs: mahimahi_defconfig: Switch to CFQ I/O scheduler by default Signed-off-by: San Mehat [ARM] qsd8k: mahimahi_defconfig: set CONFIG_SMD_OFFSET_TCXO_STAT Signed-off-by: Iliyan Malchev Revert "[ARM] qsd8k: mahimahi_defconfig: set CONFIG_SMD_OFFSET_TCXO_STAT" This reverts commit 40634687d201dd8d4e39be2e6b0a0cf84add4b5a. Signed-off-by: Iliyan Malchev [ARM] qsd8k: mahimahi_defconfig: remove APANIC from the kernel We use last_kmsg on mahimahi instead. Signed-off-by: Iliyan Malchev [ARM] qsd8k: mahimahi_defconfig: increase mmap_min_addr to 32k A lot of kernel structures are larger than 4k, and so NULL-pointer dereferences could still be exploitable. Signed-off-by: Iliyan Malchev [ARM] msm: mahimahi_defconfig: Update config with actual one generated Change-Id: I366144a62c9259234831682194d9b2262c7d1e3b Signed-off-by: Dima Zavin [ARM] msm: mahimahi_defconfig: Turn off tuner drivers These are unused. It saves 30K+ Change-Id: I23489f56c9d587a4205f060e37b817dacf734397 Signed-off-by: Dima Zavin [ARM] configs: mahimahi_defconfig: Enable kmemleak, slab, and list debugging Signed-off-by: San Mehat [ARM] configs: mahimahi: Regenerate defconfig Signed-off-by: San Mehat [ARM] configs: mahimahi: Enable CBQ/U32 packet matching Signed-off-by: San Mehat [ARM] configs: mahimahi: Enable net class actions, and ingress net sched Signed-off-by: San Mehat [ARM] configs: mahimahi: Disable debug options Signed-off-by: San Mehat [ARM] defconfig: mahimahi: Switch from CBQ -> HTB Signed-off-by: San Mehat [ARM] msm: defconfig: Disable yaffs tags ecc. Change-Id: I92317c21de61140a831d5d9f96a69fbd746e8441 Signed-off-by: Arve Hjønnevåg --- arch/arm/configs/mahimahi_defconfig | 309 ++++++++++++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 arch/arm/configs/mahimahi_defconfig diff --git a/arch/arm/configs/mahimahi_defconfig b/arch/arm/configs/mahimahi_defconfig new file mode 100644 index 0000000000000..7acd4e1672a2b --- /dev/null +++ b/arch/arm/configs/mahimahi_defconfig @@ -0,0 +1,309 @@ +CONFIG_EXPERIMENTAL=y +# CONFIG_SWAP is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EMBEDDED=y +# CONFIG_SYSCTL_SYSCALL is not set +# CONFIG_ELF_CORE is not set +CONFIG_ASHMEM=y +CONFIG_SLAB=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_ARCH_MSM=y +CONFIG_ARCH_QSD8X50=y +CONFIG_MSM_DEBUG_UART1=y +CONFIG_HTC_35MM_JACK=y +# CONFIG_HTC_PWRSINK is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000 +CONFIG_MSM_SERIAL_DEBUGGER=y +CONFIG_MSM_SERIAL_DEBUGGER_NO_SLEEP=y +# CONFIG_MSM_HW3D is not set +CONFIG_WIFI_CONTROL_FUNC=y +CONFIG_ARM_THUMBEE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_HIGHMEM=y +CONFIG_VMALLOC_RESERVE=0x30000000 +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_PM=y +CONFIG_WAKELOCK=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_INET_ESP=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_LED=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_OWNER=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +CONFIG_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_LL=y +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_MTD=y +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_UID_STAT=y +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_IFB=y +CONFIG_DUMMY=y +CONFIG_NET_ETHERNET=y +CONFIG_SMC91X=y +CONFIG_BCM4329=m +CONFIG_PPP=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +# CONFIG_INPUT_MOUSEDEV is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_MSM=y +CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +CONFIG_INPUT_CAPELLA_CM3602=y +# CONFIG_SERIO is not set +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_BCM_BT_LPM=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_HW_RANDOM is not set +CONFIG_I2C=y +CONFIG_W1_MASTER_DS2482=y +CONFIG_POWER_SUPPLY=y +CONFIG_BATTERY_DS2784=y +# CONFIG_HWMON is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +CONFIG_REGULATOR_TPS65023=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_VIDEO_DEV=y +# CONFIG_VIDEO_ALLOW_V4L1 is not set +CONFIG_MEDIA_TUNER_CUSTOMISE=y +# CONFIG_MEDIA_TUNER_SIMPLE is not set +# CONFIG_MEDIA_TUNER_TDA8290 is not set +# CONFIG_MEDIA_TUNER_TDA827X is not set +# CONFIG_MEDIA_TUNER_TDA18271 is not set +# CONFIG_MEDIA_TUNER_TDA9887 is not set +# CONFIG_MEDIA_TUNER_TEA5761 is not set +# CONFIG_MEDIA_TUNER_TEA5767 is not set +# CONFIG_MEDIA_TUNER_MT20XX is not set +# CONFIG_MEDIA_TUNER_MT2060 is not set +# CONFIG_MEDIA_TUNER_MT2266 is not set +# CONFIG_MEDIA_TUNER_MT2131 is not set +# CONFIG_MEDIA_TUNER_QT1010 is not set +# CONFIG_MEDIA_TUNER_XC2028 is not set +# CONFIG_MEDIA_TUNER_XC5000 is not set +# CONFIG_MEDIA_TUNER_MXL5005S is not set +# CONFIG_MEDIA_TUNER_MXL5007T is not set +# CONFIG_MEDIA_TUNER_MC44S803 is not set +CONFIG_MSM_CAMERA=y +CONFIG_S5K3E2FX=y +CONFIG_DAB=y +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +CONFIG_GPU_MSM_KGSL=y +CONFIG_MSM_KGSL_MMU=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_ANDROID=y +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_DIAG=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +CONFIG_USB_ANDROID_RNDIS=y +CONFIG_USB_ANDROID_RNDIS_WCEIS=y +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +CONFIG_MMC_MSM7X00A=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_SLEEP=y +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +# CONFIG_DNOTIFY is not set +CONFIG_INOTIFY=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_KERNEL=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_SPINLOCK_SLEEP=y +CONFIG_DEBUG_INFO=y +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_TWOFISH=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set From 7564412771cc1eb80b0d66c8591f19f3c307dd50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Sun, 8 Nov 2009 22:23:33 -0800 Subject: [PATCH 0464/2556] [ARM] msm: mahimahi: htc_power_supply: Add support for docks. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We use a 1-wire interface to the dock to get the dock id, get its bluetooth address and to set a bluetooth pin code. Change-Id: I55a1302feabbe8861fd6b85a475407924e5e55a9 Signed-off-by: Arve Hjønnevåg [ARM] msm: mahimahi: Hold wakelock while detecting dock Change-Id: Idcb87dfc291871859d02531f9aaf2df3fb394342 Signed-off-by: Arve Hjønnevåg [ARM] msm: mahimahi: Request dock gpio. Change-Id: Ic196a72413a411c78a471906b2593801300fdf73 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/htc_power_supply.c | 435 ++++++++++++++++++++++++++- 1 file changed, 427 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-msm/htc_power_supply.c b/arch/arm/mach-msm/htc_power_supply.c index 566b070ec2712..6edf9303105dc 100644 --- a/arch/arm/mach-msm/htc_power_supply.c +++ b/arch/arm/mach-msm/htc_power_supply.c @@ -20,15 +20,390 @@ #include #include #include +#include #include #include +#include +#include +#include +#include + +#include "board-mahimahi.h" + +extern void notify_usb_connected(int); + static char *supply_list[] = { "battery", }; +static struct switch_dev dock_switch = { + .name = "dock", +}; + static int vbus_present; static int usb_status; +static bool dock_mains; + +struct dock_state { + struct mutex lock; + u32 t; + u32 last_edge_t[2]; + u32 last_edge_i[2]; + bool level; + bool dock_connected_unknown; +}; + +static struct workqueue_struct *dock_wq; +static struct work_struct dock_work; +static struct wake_lock dock_work_wake_lock; +static struct dock_state ds = { + .lock = __MUTEX_INITIALIZER(ds.lock), +}; + +#define _GPIO_DOCK MAHIMAHI_GPIO_DOCK + +#define dock_out(n) gpio_direction_output(_GPIO_DOCK, n) +#define dock_out2(n) gpio_set_value(_GPIO_DOCK, n) +#define dock_in() gpio_direction_input(_GPIO_DOCK) +#define dock_read() gpio_get_value(_GPIO_DOCK) + +#define MFM_DELAY_NS 10000 + +static int dock_get_edge(struct dock_state *s, u32 timeout, u32 tmin, u32 tmax) +{ + bool lin; + bool in = s->level; + u32 t; + do { + lin = in; + in = dock_read(); + t = msm_read_fast_timer(); + if (in != lin) { + s->last_edge_t[in] = t; + s->last_edge_i[in] = 0; + s->level = in; + if ((s32)(t - tmin) < 0 || (s32)(t - tmax) > 0) + return -1; + return 1; + } + } while((s32)(t - timeout) < 0); + return 0; +} + +static bool dock_sync(struct dock_state *s, u32 timeout) +{ + u32 t; + + s->level = dock_read(); + t = msm_read_fast_timer(); + + if (!dock_get_edge(s, t + timeout, 0, 0)) + return false; + s->last_edge_i[s->level] = 2; + return !!dock_get_edge(s, + s->last_edge_t[s->level] + MFM_DELAY_NS * 4, 0, 0); +} + +static int dock_get_next_bit(struct dock_state *s) +{ + u32 i = s->last_edge_i[!s->level] + ++s->last_edge_i[s->level]; + u32 target = s->last_edge_t[!s->level] + MFM_DELAY_NS * i; + u32 timeout = target + MFM_DELAY_NS / 2; + u32 tmin = target - MFM_DELAY_NS / 4; + u32 tmax = target + MFM_DELAY_NS / 4; + return dock_get_edge(s, timeout, tmin, tmax); +} + +static u32 dock_get_bits(struct dock_state *s, int count, int *errp) +{ + u32 data = 0; + u32 m = 1; + int ret; + int err = 0; + while (count--) { + ret = dock_get_next_bit(s); + if (ret) + data |= m; + if (ret < 0) + err++; + m <<= 1; + } + if (errp) + *errp = err; + return data; +} + +static void dock_delay(u32 timeout) +{ + timeout += msm_read_fast_timer(); + while (((s32)(msm_read_fast_timer() - timeout)) < 0) + ; +} + +static int dock_send_bits(struct dock_state *s, u32 data, int count, int period) +{ + u32 t, t0, to; + + dock_out2(s->level); + t = to = 0; + t0 = msm_read_fast_timer(); + + while (count--) { + if (data & 1) + dock_out2((s->level = !s->level)); + + t = msm_read_fast_timer() - t0; + if (t - to > period / 2) { + pr_info("dock: to = %d, t = %d\n", to, t); + return -EIO; + } + + to += MFM_DELAY_NS; + do { + t = msm_read_fast_timer() - t0; + } while (t < to); + if (t - to > period / 4) { + pr_info("dock: to = %d, t = %d\n", to, t); + return -EIO; + } + data >>= 1; + } + return 0; +} + +static u32 mfm_encode(u16 data, int count, bool p) +{ + u32 mask; + u32 mfm = 0; + u32 clock = ~data & ~(data << 1 | !!p); + for (mask = 1UL << (count - 1); mask; mask >>= 1) { + mfm |= (data & mask); + mfm <<= 1; + mfm |= (clock & mask); + } + return mfm; +} + +static u32 mfm_decode(u32 mfm) +{ + u32 data = 0; + u32 clock = 0; + u32 mask = 1; + while (mfm) { + if (mfm & 1) + clock |= mask; + mfm >>= 1; + if (mfm & 1) + data |= mask; + mfm >>= 1; + mask <<= 1; + } + return data; +} + +static int dock_command(struct dock_state *s, u16 cmd, int len, int retlen) +{ + u32 mfm; + int count; + u32 data = cmd; + int ret; + int err = -1; + unsigned long flags; + + data = data << 2 | 3; /* add 0101 mfm data*/ + mfm = mfm_encode(data, len, false); + count = len * 2 + 2; + + msm_enable_fast_timer(); + local_irq_save(flags); + ret = dock_send_bits(s, mfm, count, MFM_DELAY_NS); + if (!ret) { + dock_in(); + if (dock_sync(s, MFM_DELAY_NS * 5)) + ret = dock_get_bits(s, retlen * 2, &err); + else + ret = -1; + dock_out(s->level); + } + local_irq_restore(flags); + + dock_delay((ret < 0) ? MFM_DELAY_NS * 6 : MFM_DELAY_NS * 2); + msm_disable_fast_timer(); + if (ret < 0) { + pr_warning("dock_command: %x: no response\n", cmd); + return ret; + } + data = mfm_decode(ret); + mfm = mfm_encode(data, retlen, true); + if (mfm != ret || err) { + pr_warning("dock_command: %x: bad response, " + "data %x, mfm %x %x, err %d\n", + cmd, data, mfm, ret, err); + return -EIO; + } + return data; +} + +static int dock_command_retry(struct dock_state *s, u16 cmd, size_t len, size_t retlen) +{ + int retry = 20; + int ret; + while (retry--) { + ret = dock_command(s, cmd, len, retlen); + if (ret >= 0) + return ret; + if (retry != 19) + msleep(10); + } + s->dock_connected_unknown = true; + return -EIO; +} + +static int dock_read_single(struct dock_state *s, int addr) +{ + int ret = -1, last; + int retry = 20; + while (retry--) { + last = ret; + ret = dock_command_retry(s, addr << 1, 6, 8); + if (ret < 0 || ret == last) + return ret; + } + return -EIO; +} + +static int dock_read_multi(struct dock_state *s, int addr, u8 *data, size_t len) +{ + int ret; + int i; + u8 suml, sumr = -1; + int retry = 20; + while (retry--) { + suml = 0; + for (i = 0; i <= len; i++) { + ret = dock_command_retry(s, (addr + i) << 1, 6, 8); + if (ret < 0) + return ret; + if (i < len) { + data[i] = ret; + suml += ret; + } else + sumr = ret; + } + if (sumr == suml) + return 0; + + pr_warning("dock_read_multi(%x): bad checksum, %x != %x\n", + addr, sumr, suml); + } + return -EIO; +} + +static int dock_write_byte(struct dock_state *s, int addr, u8 data) +{ + return dock_command_retry(s, 1 | addr << 1 | data << 4, 6 + 8, 1); +} + +static int dock_write_multi(struct dock_state *s, int addr, u8 *data, size_t len) +{ + int ret; + int i; + u8 sum; + int retry = 2; + while (retry--) { + sum = 0; + for (i = 0; i < len; i++) { + sum += data[i]; + ret = dock_write_byte(s, addr + i, data[i]); + if (ret < 0) + return ret; + } + ret = dock_write_byte(s, addr + len, sum); + if (ret <= 0) + return ret; + } + return -EIO; +} + +static int dock_acquire(struct dock_state *s) +{ + mutex_lock(&s->lock); + dock_in(); + if (dock_read()) { + /* Allow some time for the dock pull-down resistor to discharge + * the capasitor. + */ + msleep(20); + if (dock_read()) { + mutex_unlock(&s->lock); + return -ENOENT; + } + } + dock_out(0); + s->level = false; + return 0; +} + +static void dock_release(struct dock_state *s) +{ + dock_in(); + mutex_unlock(&s->lock); +} + +enum { + DOCK_TYPE = 0x0, + DOCK_BT_ADDR = 0x1, /* - 0x7 */ + + DOCK_PIN_CODE = 0x0, +}; + +static ssize_t bt_addr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret; + u8 bt_addr[6]; + + ret = dock_acquire(&ds); + if (ret < 0) + return ret; + ret = dock_read_multi(&ds, DOCK_BT_ADDR, bt_addr, 6); + dock_release(&ds); + if (ret < 0) + return ret; + + return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", + bt_addr[0], bt_addr[1], bt_addr[2], + bt_addr[3], bt_addr[4], bt_addr[5]); +} +static DEVICE_ATTR(bt_addr, S_IRUGO | S_IWUSR, bt_addr_show, NULL); + +static ssize_t bt_pin_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret, i; + u8 pin[4]; + + if (size < 4) + return -EINVAL; + + for (i = 0; i < sizeof(pin); i++) { + if ((pin[i] = buf[i] - '0') > 10) + return -EINVAL; + } + + ret = dock_acquire(&ds); + if (ret < 0) + return ret; + ret = dock_write_multi(&ds, DOCK_PIN_CODE, pin, 4); + dock_release(&ds); + if (ret < 0) + return ret; + + return size; +} +static DEVICE_ATTR(bt_pin, S_IRUGO | S_IWUSR, NULL, bt_pin_store); + static int power_get_property(struct power_supply *psy, enum power_supply_property psp, @@ -38,7 +413,7 @@ static int power_get_property(struct power_supply *psy, return -EINVAL; if (psy->type == POWER_SUPPLY_TYPE_MAINS) { - val->intval = (vbus_present && (usb_status == 2)); + val->intval = (vbus_present && (usb_status == 2 || dock_mains)); } else { val->intval = vbus_present; } @@ -92,6 +467,39 @@ struct battery_info_reply { u32 full_bat; /* Full capacity of battery (mAh) */ }; +static void dock_work_proc(struct work_struct *work) +{ + int dockid; + + if (!vbus_present || dock_acquire(&ds)) + goto no_dock; + + if (ds.dock_connected_unknown) { + /* force a new dock notification if a command failed */ + switch_set_state(&dock_switch, 0); + ds.dock_connected_unknown = false; + } + + dockid = dock_read_single(&ds, DOCK_TYPE); + dock_release(&ds); + + pr_info("Detected dock with ID %02x\n", dockid); + if (dockid >= 0) { + msm_hsusb_set_vbus_state(0); + dock_mains = !!(dockid & 0x80); + switch_set_state(&dock_switch, (dockid & 1) ? 2 : 1); + goto done; + } +no_dock: + dock_mains = false; + switch_set_state(&dock_switch, 0); + msm_hsusb_set_vbus_state(vbus_present); +done: + power_supply_changed(&ac_supply); + power_supply_changed(&usb_supply); + wake_unlock(&dock_work_wake_lock); +} + static int htc_battery_probe(struct platform_device *pdev) { struct rpc_request_hdr req; @@ -120,6 +528,9 @@ static int htc_battery_probe(struct platform_device *pdev) power_supply_register(&pdev->dev, &ac_supply); power_supply_register(&pdev->dev, &usb_supply); + INIT_WORK(&dock_work, dock_work_proc); + dock_wq = create_singlethread_workqueue("dock"); + return 0; } @@ -157,12 +568,9 @@ static int handle_battery_call(struct msm_rpc_server *server, args->status = !!args->status; - if (vbus_present != args->status) { - vbus_present = args->status; - msm_hsusb_set_vbus_state(vbus_present); - power_supply_changed(&ac_supply); - power_supply_changed(&usb_supply); - } + vbus_present = args->status; + wake_lock(&dock_work_wake_lock); + queue_work(dock_wq, &dock_work); return 0; } @@ -176,7 +584,7 @@ void notify_usb_connected(int status) int is_ac_power_supplied(void) { - return vbus_present && (usb_status == 2); + return vbus_present && (usb_status == 2 || dock_mains); } static struct msm_rpc_server battery_server = { @@ -187,8 +595,19 @@ static struct msm_rpc_server battery_server = { static int __init htc_battery_init(void) { + int ret; + gpio_request(_GPIO_DOCK, "dock"); + dock_in(); + wake_lock_init(&dock_work_wake_lock, WAKE_LOCK_SUSPEND, "dock"); platform_driver_register(&htc_battery_driver); msm_rpc_create_server(&battery_server); + if (switch_dev_register(&dock_switch) == 0) { + ret = device_create_file(dock_switch.dev, &dev_attr_bt_addr); + WARN_ON(ret); + ret = device_create_file(dock_switch.dev, &dev_attr_bt_pin); + WARN_ON(ret); + } + return 0; } From 7e1f1ff1437b9970939752a771da3c6256e8b5e8 Mon Sep 17 00:00:00 2001 From: HK Chen Date: Sun, 18 Oct 2009 14:59:22 +0800 Subject: [PATCH 0465/2556] qsd8k: audio: clean up htc_acoustic_qsd driver. Signed-off-by: Brian Swetland --- arch/arm/mach-msm/htc_acoustic_qsd.c | 66 ---------------------------- 1 file changed, 66 deletions(-) diff --git a/arch/arm/mach-msm/htc_acoustic_qsd.c b/arch/arm/mach-msm/htc_acoustic_qsd.c index 8bea0d40f66b4..ce3c3a0bbfe2c 100644 --- a/arch/arm/mach-msm/htc_acoustic_qsd.c +++ b/arch/arm/mach-msm/htc_acoustic_qsd.c @@ -36,24 +36,12 @@ #define ACOUSTIC_UPDATE_ADIE \ _IOW(ACOUSTIC_IOCTL_MAGIC, 24, unsigned int) -#define ACOUSTIC_REINIT_ACDB \ - _IOW(ACOUSTIC_IOCTL_MAGIC, 25, unsigned int) - #define HTCACOUSTICPROG 0x30100003 #define HTCACOUSTICVERS 0 #define ONCRPC_ALLOC_ACOUSTIC_MEM_PROC (1) #define ONCRPC_UPDATE_ADIE_PROC (2) #define ONCRPC_ENABLE_AUX_PGA_LOOPBACK_PROC (3) #define ONCRPC_FORCE_HEADSET_SPEAKER_PROC (4) -#define ONCRPC_ENABLE_LINE_IN_TX_PATH_PROC (5) -#define ONCRPC_ENABLE_AUDIO_LOOPBACK_TRIGGER_PROC (6) -#define ONCRPC_DUMP_ADIE_REG_PROC (7) -#define ONCRPC_REINIT_ACDB (8) - -#define HTCACOUSTICCBPROG 0x30100004 -#define HTCACOUSTICCBVERS 0 -#define RPC_HTC_ACOUSTICCB_NULL (0) -#define RPC_HTC_ACOUSTICCB_SET_UPLINK_MUTE_PROC (1) #define HTC_ACOUSTIC_TABLE_SIZE (0x20000) @@ -283,31 +271,6 @@ static long acoustic_ioctl(struct file *file, unsigned int cmd, D("ioctl: ONCRPC_UPDATE_ADIE_PROC success.\n"); break; } - case ACOUSTIC_REINIT_ACDB: { - struct rpc_request_hdr acdb_req; - struct update_acdb_rep { - struct rpc_reply_hdr hdr; - int ret; - } acdb_rep; - - D("ioctl: ACOUSTIC_REINIT_ACDB called %d.\n", current->pid); - - rc = msm_rpc_call_reply(endpoint, - ONCRPC_REINIT_ACDB, &acdb_req, - sizeof(acdb_req), &acdb_rep, - sizeof(acdb_rep), 5 * HZ); - - reply_value = be32_to_cpu(acdb_rep.ret); - if (reply_value != 0 || rc < 0) { - E("ioctl failed: ONCRPC_REINIT_ACDB "\ - "error %d.\n", rc); - if (rc >= 0) - rc = -EIO; - break; - } - D("ioctl: ONCRPC_REINIT_ACDB success.\n"); - break; - } default: E("ioctl: invalid command\n"); rc = -EINVAL; @@ -321,34 +284,6 @@ struct rpc_set_uplink_mute_args { int mute; }; -static int handle_htc_acoustic_call(struct msm_rpc_server *server, - struct rpc_request_hdr *req, unsigned len) -{ - switch (req->procedure) { - case RPC_HTC_ACOUSTICCB_NULL: - return 0; - - case RPC_HTC_ACOUSTICCB_SET_UPLINK_MUTE_PROC: { - struct rpc_set_uplink_mute_args *args; - args = (struct rpc_set_uplink_mute_args *)(req + 1); - args->mute = be32_to_cpu(args->mute); - D("[M2A_RPC] set uplink mute: %d\n", args->mute); - q6audio_set_tx_mute(args->mute); - return 0; - } - default: - E("%s: program 0x%08x:%d: unknown procedure %d\n", - __func__, req->prog, req->vers, req->procedure); - return -ENODEV; - } -} - -static struct msm_rpc_server htc_acoustic_server = { - .prog = HTCACOUSTICCBPROG, - .vers = HTCACOUSTICCBVERS, - .rpc_call = handle_htc_acoustic_call, -}; - static struct file_operations acoustic_fops = { .owner = THIS_MODULE, .open = acoustic_open, @@ -367,7 +302,6 @@ static int __init acoustic_init(void) { mutex_init(&api_lock); mutex_init(&rpc_connect_lock); - msm_rpc_create_server(&htc_acoustic_server); return misc_register(&acoustic_misc); } From 6206e0170b189fa40484300097cf6c5f7cfcde40 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Mon, 2 Nov 2009 18:42:40 -0800 Subject: [PATCH 0466/2556] mmc: core: Hack for allowing access to SDIO_CCCR_ABORT register Signed-off-by: San Mehat --- drivers/mmc/core/sdio_io.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index 549a341446462..d3d0e2e3fd5a5 100755 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -692,11 +692,13 @@ void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, BUG_ON(!func); +#if 0 if ((addr < 0xF0 || addr > 0xFF) && (!mmc_card_lenient_fn0(func->card))) { if (err_ret) *err_ret = -EINVAL; return; } +#endif ret = mmc_io_rw_direct(func->card, 1, 0, addr, b, NULL); if (err_ret) From 25da073cc82754771dff9775e67f5153950074b3 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Sat, 21 Nov 2009 09:22:14 -0800 Subject: [PATCH 0467/2556] [ARM] msm: Add 'execute' datamover callback Based on a patch from Brent DeGraaf: "The datamover supports channels which can be shared amongst devices. As a result, the actual data transfer may occur some time after the request is queued up. Some devices such as mmc host controllers will timeout if a command is issued too far in advance of the actual transfer, so if dma to other devices on the same channel is already in progress or queued up, the added delay can cause pending transfers to fail before they start. This change extends the api to allow a user callback to be invoked just before the actual transfer takes place, thus allowing actions directly associated with the dma transfer, such as device commands, to be invoked with precise timing. Without this mechanism, there is no way for a driver to realize this timing. Also adds a user pointer to the command structure for use by the caller to reference information that may be needed by the callback routine for proper identification and processing associated with that specific request. This change is necessary to fix problems associated with excessive command timeouts and race conditions in the mmc driver." This patch also fixes all the callers of msm_dmov_enqueue_cmd() to ensure their callback function is NULL. Signed-off-by: San Mehat Cc: Brent DeGraaf Cc: Brian Swetland --- drivers/mmc/host/msm_sdcc.c | 1 + drivers/tty/serial/msm_serial_hs.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 153ab977a0136..199d131e3eb2d 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -391,6 +391,7 @@ static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data) host->dma.hdr.cmdptr = DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(host->dma.cmdptr_busaddr); host->dma.hdr.complete_func = msmsdcc_dma_complete_func; + host->dma.hdr.execute_func = NULL; n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg, host->dma.num_ents, host->dma.dir); diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index b2043028934f5..2ae173861f9d4 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -1207,6 +1207,7 @@ static int msm_hs_startup(struct uart_port *uport) tx->dma_in_flight = 0; tx->xfer.complete_func = msm_hs_dmov_tx_callback; + tx->xfer.execute_func = NULL; tx->command_ptr->cmd = CMD_LC | CMD_DST_CRCI(msm_uport->dma_tx_crci) | CMD_MODE_BOX; @@ -1222,6 +1223,7 @@ static int msm_hs_startup(struct uart_port *uport) /* Turn on Uart Receive */ rx->xfer.complete_func = msm_hs_dmov_rx_callback; + rx->xfer.execute_func = NULL; rx->command_ptr->cmd = CMD_LC | CMD_SRC_CRCI(msm_uport->dma_rx_crci) | CMD_MODE_BOX; From ac0b06029b7145da42c3caf0d23d10e9cb42843f Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Mon, 30 Nov 2009 14:42:35 -0800 Subject: [PATCH 0468/2556] [ARM] qsd8k: print out more registers on an imprecise external abort. Signed-off-by: Iliyan Malchev --- arch/arm/mm/fault.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index 1a954b7cb5ab8..a08f35e7d4624 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -461,16 +461,34 @@ do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs) return 1; } +#ifdef CONFIG_ARCH_MSM_SCORPION +#define __str(x) #x +#define MRC(x, v1, v2, v4, v5, v6) do { \ + unsigned int __##x; \ + asm("mrc " __str(v1) ", " __str(v2) ", %0, " __str(v4) ", " \ + __str(v5) ", " __str(v6) "\n" \ + : "=r" (__##x)); \ + printk("%s: %s = 0x%.8x\n", __func__, #x, __##x); \ +} while(0) +#endif + static int do_imprecise_ext(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { #ifdef CONFIG_ARCH_MSM_SCORPION - unsigned int adfsr; - - asm("mrc p15, 0, %0, c5, c1, 0\n" /* read adfsr for fault status */ - : "=r" (adfsr)); - - printk("%s: ADFSR = 0x%.8x\n", __func__, adfsr); + MRC(ADFSR, p15, 0, c5, c1, 0); + MRC(DFSR, p15, 0, c5, c0, 0); + MRC(ACTLR, p15, 0, c1, c0, 1); + MRC(EFSR, p15, 7, c15, c0, 1); + MRC(L2SR, p15, 3, c15, c1, 0); + MRC(L2CR0, p15, 3, c15, c0, 1); + MRC(L2CPUESR, p15, 3, c15, c1, 1); + MRC(L2CPUCR, p15, 3, c15, c0, 2); + MRC(SPESR, p15, 1, c9, c7, 0); + MRC(SPCR, p15, 0, c9, c7, 0); + MRC(DMACHSR, p15, 1, c11, c0, 0); + MRC(DMACHESR, p15, 1, c11, c0, 1); + MRC(DMACHCR, p15, 0, c11, c0, 2); #endif return 1; } From 7f1aaa0bd283a7e61065bb880f62fe2152544e78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 21 Oct 2009 20:59:04 -0700 Subject: [PATCH 0469/2556] [ARM] Don't use domain manager mode when not needed. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I81a1be57e5053c08be54ebb21efb701274440d43 Signed-off-by: Arve Hjønnevåg --- arch/arm/include/asm/thread_info.h | 4 ++-- arch/arm/kernel/head.S | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h index 7b5cc8dae06e6..2aa757b8c4dc9 100644 --- a/arch/arm/include/asm/thread_info.h +++ b/arch/arm/include/asm/thread_info.h @@ -75,8 +75,8 @@ struct thread_info { .flags = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ .addr_limit = KERNEL_DS, \ - .cpu_domain = domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \ - domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \ + .cpu_domain = domain_val(DOMAIN_USER, DOMAIN_CLIENT) | \ + domain_val(DOMAIN_KERNEL, DOMAIN_CLIENT) | \ domain_val(DOMAIN_IO, DOMAIN_CLIENT), \ .restart_block = { \ .fn = do_no_restart_syscall, \ diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index f06ff9feb0dbb..4b0ed98f74f53 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S @@ -355,9 +355,9 @@ __enable_mmu: #ifdef CONFIG_CPU_ICACHE_DISABLE bic r0, r0, #CR_I #endif - mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \ - domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \ - domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \ + mov r5, #(domain_val(DOMAIN_USER, DOMAIN_CLIENT) | \ + domain_val(DOMAIN_KERNEL, DOMAIN_CLIENT) | \ + domain_val(DOMAIN_TABLE, DOMAIN_CLIENT) | \ domain_val(DOMAIN_IO, DOMAIN_CLIENT)) mcr p15, 0, r5, c3, c0, 0 @ load domain access register mcr p15, 0, r4, c2, c0, 0 @ load page table pointer From 2952ca04be44b0d03070f5a1d0f7cf7db04b8195 Mon Sep 17 00:00:00 2001 From: Dave Estes Date: Tue, 11 Aug 2009 17:58:49 -0400 Subject: [PATCH 0470/2556] [ARM] mm: qsd8x50: Fix incorrect permission faults Handle incorrectly reported permission faults for qsd8650. On permission faults, retry MVA to PA conversion. If retry detects translation fault. Report as translation fault. Change-Id: I2b122dc00771fb65e7aa4f8dc6dbd20a6bb905d0 Signed-off-by: Dave Estes --- arch/arm/mach-msm/Kconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index f786e8a5de6db..53bffe6bfe841 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -24,6 +24,7 @@ config ARCH_MSM7X30 select MSM_GPIOMUX select MSM_PROC_COMM select HAS_MSM_DEBUG_UART_PHYS + select VERIFY_PERMISSION_FAULT config ARCH_QSD8X50 bool "QSD8X50" @@ -35,6 +36,7 @@ config ARCH_QSD8X50 select MSM_GPIOMUX select MSM_PROC_COMM select HAS_MSM_DEBUG_UART_PHYS + select VERIFY_PERMISSION_FAULT config ARCH_MSM8X60 bool "MSM8X60" @@ -47,7 +49,7 @@ config ARCH_MSM8X60 select MSM_GPIOMUX select IOMMU_API select MSM_SCM if SMP - + select VERIFY_PERMISSION_FAULT endchoice config MSM_SOC_REV_A From c5e7982dc14ab85b19395c435e92d461fe8e78f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 1 Dec 2009 20:00:01 -0800 Subject: [PATCH 0471/2556] [ARM] Don't use domain 0 for the kernel domain if CONFIG_VERIFY_PERMISSION_FAULT is set. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I17ff457e1d4b4f6b7dcf308a014fc6776950fad8 Signed-off-by: Arve Hjønnevåg --- arch/arm/include/asm/domain.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/arm/include/asm/domain.h b/arch/arm/include/asm/domain.h index af18ceaacf5d2..5c35625ff1b0a 100644 --- a/arch/arm/include/asm/domain.h +++ b/arch/arm/include/asm/domain.h @@ -27,8 +27,13 @@ * * 36-bit addressing and supersections are only available on * CPUs based on ARMv6+ or the Intel XSC3 core. + * + * We cannot use domain 0 for the kernel on QSD8x50 since the kernel domain + * is set to manager mode when set_fs(KERNEL_DS) is called. Setting domain 0 + * to manager mode will disable the workaround for a cpu bug that can cause an + * invalid fault status and/or tlb corruption (CONFIG_VERIFY_PERMISSION_FAULT). */ -#ifndef CONFIG_IO_36 +#if !defined(CONFIG_IO_36) && !defined(CONFIG_VERIFY_PERMISSION_FAULT) #define DOMAIN_KERNEL 0 #define DOMAIN_TABLE 0 #define DOMAIN_USER 1 From 8a43ff9eaef5b2b55f7c03eac62e9d4b19762b67 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Thu, 10 Dec 2009 00:28:51 -0800 Subject: [PATCH 0472/2556] [ARM] qsd8k: map in TCSR_SPARE2 Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/io.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c index 07ab4c4ddbcec..9b2ccd5b168cd 100644 --- a/arch/arm/mach-msm/io.c +++ b/arch/arm/mach-msm/io.c @@ -86,6 +86,7 @@ static struct map_desc qsd8x50_io_desc[] __initdata = { MSM_DEVICE(SCPLL), MSM_DEVICE(AD5), MSM_DEVICE(MDC), + MSM_DEVICE(TCSR), #ifdef CONFIG_MSM_DEBUG_UART MSM_DEVICE(DEBUG_UART), #endif From ecda3c651682aa83ca57431878bb3b5ae4a88ad9 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Thu, 10 Dec 2009 00:30:03 -0800 Subject: [PATCH 0473/2556] [ARM] qsd8k: print TCSR_SPARE2 in do_imprecise_ext Signed-off-by: Iliyan Malchev --- arch/arm/mm/fault.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index a08f35e7d4624..c6a6f0a733449 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -23,6 +23,10 @@ #include #include #include +#ifdef CONFIG_ARCH_MSM_SCORPION +#include +#include +#endif #include "fault.h" @@ -468,8 +472,11 @@ do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs) asm("mrc " __str(v1) ", " __str(v2) ", %0, " __str(v4) ", " \ __str(v5) ", " __str(v6) "\n" \ : "=r" (__##x)); \ - printk("%s: %s = 0x%.8x\n", __func__, #x, __##x); \ + pr_info("%s: %s = 0x%.8x\n", __func__, #x, __##x); \ } while(0) + +#define MSM_TCSR_SPARE2 (MSM_TCSR_BASE + 0x60) + #endif static int @@ -489,6 +496,9 @@ do_imprecise_ext(unsigned long addr, unsigned int fsr, struct pt_regs *regs) MRC(DMACHSR, p15, 1, c11, c0, 0); MRC(DMACHESR, p15, 1, c11, c0, 1); MRC(DMACHCR, p15, 0, c11, c0, 2); +#endif +#ifdef CONFIG_ARCH_MSM_SCORPION + pr_info("%s: TCSR_SPARE2 = 0x%.8x\n", __func__, readl(MSM_TCSR_SPARE2)); #endif return 1; } From 125c465627b310beb1e14ffa0b43db324166ed73 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Fri, 11 Dec 2009 11:52:14 -0800 Subject: [PATCH 0474/2556] [ARM] msm: scorpion: Clear out efsr/adfsr on power-on and after fault Change-Id: I1c087d4f56c257357a34482d55e013ab041d15bf Signed-off-by: Dima Zavin --- arch/arm/mach-msm/arch-init-scorpion.S | 4 ++++ arch/arm/mach-msm/io.c | 8 ++++++++ arch/arm/mm/fault.c | 5 +++++ 3 files changed, 17 insertions(+) diff --git a/arch/arm/mach-msm/arch-init-scorpion.S b/arch/arm/mach-msm/arch-init-scorpion.S index 5cb549f41bfea..2eaf4eafb4480 100644 --- a/arch/arm/mach-msm/arch-init-scorpion.S +++ b/arch/arm/mach-msm/arch-init-scorpion.S @@ -200,6 +200,10 @@ __cpu_early_init: //; Setup DCLKCR to allow normal D-Cache line fills MCR p15, 1, r0, c9, c0, 7 //; WCP15_DCLKCR r0 + //; Initialize the ADFSR and EFSR registers. + MCR p15, 0, r0, c5, c1, 0 //; ADFSR + MCR p15, 7, r0, c15, c0, 1 //; EFSR + //; Setup the TLBLKCR //; Victim = 6'b000000; Floor = 6'b000000; //; IASIDCFG = 2'b00 (State-Machine); IALLCFG = 2'b01 (Flash); BNA = 1'b0; diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c index 9b2ccd5b168cd..80a81cab0a4e2 100644 --- a/arch/arm/mach-msm/io.c +++ b/arch/arm/mach-msm/io.c @@ -109,6 +109,10 @@ void __init msm_map_qsd8x50_io(void) "orr %0, %0, #0x10\n\t" "mcr p15, 0, %0, c15, c15, 2" : "=&r" (unused)); + /* clear out EFSR and ADFSR on boot */ + asm volatile ("mcr p15, 7, %0, c15, c0, 1\n\t" + "mcr p15, 0, %0, c5, c1, 0" + : : "r" (0)); iotable_init(qsd8x50_io_desc, ARRAY_SIZE(qsd8x50_io_desc)); } #endif /* CONFIG_ARCH_QSD8X50 */ @@ -158,6 +162,10 @@ static struct map_desc msm7x30_io_desc[] __initdata = { void __init msm_map_msm7x30_io(void) { + /* clear out EFSR and ADFSR on boot */ + asm volatile ("mcr p15, 7, %0, c15, c0, 1\n\t" + "mcr p15, 0, %0, c5, c1, 0" + : : "r" (0)); iotable_init(msm7x30_io_desc, ARRAY_SIZE(msm7x30_io_desc)); } #endif /* CONFIG_ARCH_MSM7X30 */ diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index c6a6f0a733449..92cce206779e0 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -496,6 +496,11 @@ do_imprecise_ext(unsigned long addr, unsigned int fsr, struct pt_regs *regs) MRC(DMACHSR, p15, 1, c11, c0, 0); MRC(DMACHESR, p15, 1, c11, c0, 1); MRC(DMACHCR, p15, 0, c11, c0, 2); + + /* clear out EFSR and ADFSR after fault */ + asm volatile ("mcr p15, 7, %0, c15, c0, 1\n\t" + "mcr p15, 0, %0, c5, c1, 0" + : : "r" (0)); #endif #ifdef CONFIG_ARCH_MSM_SCORPION pr_info("%s: TCSR_SPARE2 = 0x%.8x\n", __func__, readl(MSM_TCSR_SPARE2)); From a690b167113bf6107371154af21c21b20669f08a Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Tue, 3 Nov 2009 16:17:25 -0800 Subject: [PATCH 0475/2556] [ARM] msm: mahimahi: Set AXI to 128mhz when screen on, 61mhz when screen off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mike Chan [ARM] msm: pm: Clock AXI down during power collapse Keep AXI bus at 128 while the screen is on, however clock down when entering power collapse on idle when we ramp the cpu speed down. This maintains performance with idle power savings. Signed-off-by: Mike Chan [ARM] msm: pm: Only clock axi down to 120MHz when the screen is on. This may help with the recent lcdc underrun problems. Change-Id: I0478ddc131a18f90ee2299758899c2ea8ecdc508 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/pm.c | 50 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index 55cc2380eeb8c..64fd371ec50ee 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -121,6 +122,9 @@ void msm_timer_exit_idle(int low_power); int msm_irq_idle_sleep_allowed(void); int msm_irq_pending(void); +static int axi_rate; +static int sleep_axi_rate; +static struct clk *axi_clk; static uint32_t *msm_pm_reset_vector; static uint32_t msm_pm_max_sleep_time; @@ -331,6 +335,10 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) "\n", pm_saved_acpu_clk_rate); if (pm_saved_acpu_clk_rate == 0) goto ramp_down_failed; + + /* Drop AXI request when the screen is on */ + if (axi_rate) + clk_set_rate(axi_clk, sleep_axi_rate); } if (sleep_mode < MSM_PM_SLEEP_MODE_APPS_SLEEP) { if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) @@ -370,6 +378,10 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) if (acpuclk_set_rate(pm_saved_acpu_clk_rate, 1) < 0) printk(KERN_ERR "msm_sleep(): clk_set_rate %ld " "failed\n", pm_saved_acpu_clk_rate); + + /* Restore axi rate if needed */ + if (axi_rate) + clk_set_rate(axi_clk, axi_rate); } if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) printk(KERN_INFO "msm_sleep(): exit A11S_CLK_SLEEP_EN %x, " @@ -640,11 +652,49 @@ void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns) } EXPORT_SYMBOL(msm_pm_set_max_sleep_time); +#ifdef CONFIG_EARLYSUSPEND +/* axi 128 screen on, 61mhz screen off */ +static void axi_early_suspend(struct early_suspend *handler) { + axi_rate = 0; + clk_set_rate(axi_clk, axi_rate); +} + +static void axi_late_resume(struct early_suspend *handler) { + axi_rate = 128000000; + sleep_axi_rate = 120000000; + clk_set_rate(axi_clk, axi_rate); +} + +static struct early_suspend axi_screen_suspend = { + .suspend = axi_early_suspend, + .resume = axi_late_resume, +}; +#endif + +static void __init msm_pm_axi_init(void) +{ +#ifdef CONFIG_EARLYSUSPEND + axi_clk = clk_get(NULL, "ebi1_clk"); + if (IS_ERR(axi_clk)) { + int result = PTR_ERR(axi_clk); + pr_err("clk_get(ebi1_clk) returned %d\n", result); + return; + } + axi_rate = 128000000; + sleep_axi_rate = 120000000; + clk_set_rate(axi_clk, axi_rate); + register_early_suspend(&axi_screen_suspend); +#else + axi_rate = 0; +#endif +} + static int __init msm_pm_init(void) { pm_power_off = msm_pm_power_off; arm_pm_restart = msm_pm_restart; msm_pm_max_sleep_time = 0; + msm_pm_axi_init(); register_reboot_notifier(&msm_reboot_notifier); From 079fdd3bf59091960609fe70f40f9625b9a5b050 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 12 Nov 2009 17:38:22 -0800 Subject: [PATCH 0476/2556] [ARM] msm: pm: make default reboot use the pcom call instead of physical reset Change-Id: I2ad25d0d1c45616642744abd1ca29500c974735e Signed-off-by: Dima Zavin --- arch/arm/mach-msm/pm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index 64fd371ec50ee..08fd2ec8c8236 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -537,7 +537,11 @@ static struct platform_suspend_ops msm_pm_ops = { .valid = suspend_valid_only_mem, }; +#if defined(CONFIG_ARCH_MSM7X00A) static uint32_t restart_reason = 0x776655AA; +#else +static uint32_t restart_reason = 0; +#endif static void msm_pm_power_off(void) { @@ -572,6 +576,8 @@ static int msm_reboot_call(struct notifier_block *this, unsigned long code, void } else if (!strncmp(cmd, "oem-", 4)) { unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff; restart_reason = 0x6f656d00 | code; + } else if (!strcmp(cmd, "force-hard")) { + restart_reason = 0x776655AA; } else { restart_reason = 0x77665501; } From c0284cc98ae34bf109e1101c79e668551a9a06fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 14 Dec 2009 17:43:33 -0800 Subject: [PATCH 0477/2556] [ARM] msm: pm: Try to recover after timeout during resume. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the modem is stuck in the rsa state after a timeout a new sleep request with an early exit may get it unstuck. Also add a udelay to reduce timeout variation at different cpu speeds. Change-Id: I194b80f0fadb333c6f47ece807c7b200608c6e9f Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/pm.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index 08fd2ec8c8236..dfdb94c83b827 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -191,6 +191,7 @@ msm_pm_wait_state(uint32_t wait_all_set, uint32_t wait_all_clear, !(~state & wait_all_set) && !(state & wait_all_clear)) || (state & wait_any_set) || (~state & wait_any_clear)) return 0; + udelay(1); } pr_err("msm_pm_wait_state(%x, %x, %x, %x) failed %x\n", wait_all_set, wait_all_clear, wait_any_set, wait_any_clear, state); @@ -224,6 +225,7 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) unsigned long pm_saved_acpu_clk_rate = 0; int ret; int rv = -EINTR; + bool invalid_inital_state = false; if (msm_pm_debug_mask & MSM_PM_DEBUG_SUSPEND) printk(KERN_INFO "msm_sleep(): mode %d delay %u idle %d\n", @@ -285,6 +287,13 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) msm_gpio_enter_sleep(from_idle); if (enter_state) { + /* Make sure last sleep request did not end with a timeout */ + ret = msm_pm_wait_state(PM_SMSM_READ_RUN, 0, 0, 0); + if (ret) { + printk(KERN_ERR "msm_sleep(): invalid inital state\n"); + invalid_inital_state = true; + } + if (sleep_delay == 0 && sleep_mode >= MSM_PM_SLEEP_MODE_APPS_SLEEP) sleep_delay = 192000*5; /* APPS_SLEEP does not allow infinite timeout */ ret = smsm_set_sleep_duration(sleep_delay); @@ -300,7 +309,7 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) exit_state = 0; } ret = msm_pm_wait_state(enter_wait_set, enter_wait_clear, 0, 0); - if (ret) { + if (ret || invalid_inital_state) { printk(KERN_INFO "msm_sleep(): msm_pm_wait_state failed, %x\n", smsm_get_state(PM_SMSM_READ_STATE)); goto enter_failed; } From c4d42a149e8baf843c22f7c82975262d2bf5cfb7 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 15 Dec 2009 19:20:35 -0800 Subject: [PATCH 0478/2556] [ARM] msm: flush console before reboot If the console_lock was held while the system was rebooted, the messages in the temporary logbuffer would not have propogated to all the console drivers. This force releases the console lock if it failed to be acquired. Change-Id: I0c0a83af75e11d9378c3106bc88b327e1e7a1b16 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/pm.c | 28 ++++++++++++++++++++++++++++ arch/arm/mach-msm/smd.c | 3 +++ 2 files changed, 31 insertions(+) diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index dfdb94c83b827..77696ff487462 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -558,8 +559,35 @@ static void msm_pm_power_off(void) for (;;) ; } +static bool console_flushed; + +void msm_pm_flush_console(void) +{ + if (console_flushed) + return; + console_flushed = true; + + printk("\n"); + printk(KERN_EMERG "Restarting %s\n", linux_banner); + if (console_trylock()) { + console_unlock(); + return; + } + + mdelay(50); + + local_irq_disable(); + if (!console_trylock()) + printk(KERN_EMERG "msm_restart: Console was locked! Busting\n"); + else + printk(KERN_EMERG "msm_restart: Console was locked!\n"); + console_unlock(); +} + static void msm_pm_restart(char str) { + msm_pm_flush_console(); + /* If there's a hard reset hook and the restart_reason * is the default, prefer that to the (slower) proc_comm * reset command. diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c index 657be73297db9..18c1d5b61655f 100644 --- a/arch/arm/mach-msm/smd.c +++ b/arch/arm/mach-msm/smd.c @@ -95,11 +95,14 @@ static void smd_diag(void) } } +void msm_pm_flush_console(void); + /* call when SMSM_RESET flag is set in the A9's smsm_state */ static void handle_modem_crash(void) { pr_err("ARM9 has CRASHED\n"); smd_diag(); + msm_pm_flush_console(); /* hard reboot if possible */ if (msm_hw_reset_hook) From 09056f76f1afa9e2d3fa6dcbec1a57387499ca38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 17 Dec 2009 18:47:12 -0800 Subject: [PATCH 0479/2556] fix for 7k: [ARM] msm: pm: Clock AXI down during power collapse Change-Id: Ie0de5d7ec0984c311c733ebdc2e26fd3c3d2dc0c --- arch/arm/mach-msm/pm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index 77696ff487462..cbece79ac9a92 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -695,7 +695,7 @@ void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns) } EXPORT_SYMBOL(msm_pm_set_max_sleep_time); -#ifdef CONFIG_EARLYSUSPEND +#if defined(CONFIG_EARLYSUSPEND) && defined(CONFIG_ARCH_MSM_SCORPION) /* axi 128 screen on, 61mhz screen off */ static void axi_early_suspend(struct early_suspend *handler) { axi_rate = 0; @@ -716,7 +716,7 @@ static struct early_suspend axi_screen_suspend = { static void __init msm_pm_axi_init(void) { -#ifdef CONFIG_EARLYSUSPEND +#if defined(CONFIG_EARLYSUSPEND) && defined(CONFIG_ARCH_MSM_SCORPION) axi_clk = clk_get(NULL, "ebi1_clk"); if (IS_ERR(axi_clk)) { int result = PTR_ERR(axi_clk); From 2c31075849a2d867c2d1e0dd05dd502092f54a55 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 14 Dec 2009 00:21:16 -0500 Subject: [PATCH 0480/2556] [ARM] msm: htc: Update platform data for android USB gadget drivers. Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/devices_htc.c | 42 ++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c index c47a5acce4d8c..1fa330c6e2a31 100644 --- a/arch/arm/mach-msm/devices_htc.c +++ b/arch/arm/mach-msm/devices_htc.c @@ -30,7 +30,7 @@ #include #endif #ifdef CONFIG_USB_ANDROID -#include +#include #endif #include @@ -97,14 +97,49 @@ struct msm_hsusb_platform_data msm_hsusb_pdata = { }; #ifdef CONFIG_USB_ANDROID +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .vendor = "HTC ", + .product = "Android Phone ", + .release = 0x0100, +}; +#endif + +static struct platform_device usb_mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &mass_storage_pdata, + }, +}; + +#ifdef CONFIG_USB_ANDROID +static char *usb_functions[] = { "usb_mass_storage" }; +static char *usb_functions_adb[] = { "usb_mass_storage", "adb" }; + +static struct android_usb_product usb_products[] = { + { + .product_id = 0x0c01, + .num_functions = ARRAY_SIZE(usb_functions), + .functions = usb_functions, + }, + { + .product_id = 0x0c02, + .num_functions = ARRAY_SIZE(usb_functions_adb), + .functions = usb_functions_adb, + }, +}; + static struct android_usb_platform_data android_usb_pdata = { .vendor_id = 0x0bb4, .product_id = 0x0c01, - .adb_product_id = 0x0c02, .version = 0x0100, .product_name = "Android Phone", .manufacturer_name = "HTC", - .nluns = 1, + .num_products = ARRAY_SIZE(usb_products), + .products = usb_products, + .num_functions = ARRAY_SIZE(usb_functions_adb), + .functions = usb_functions_adb, }; static struct platform_device android_usb_device = { @@ -123,6 +158,7 @@ void __init msm_add_usb_devices(void (*phy_reset) (void)) msm_hsusb_pdata.phy_reset = phy_reset; msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; platform_device_register(&msm_device_hsusb); + platform_device_register(&usb_mass_storage_device); #ifdef CONFIG_USB_ANDROID platform_device_register(&android_usb_device); #endif From cd0b7d0ca69e5fa9e69fc470494d8e8b7f73652a Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 14 Dec 2009 00:21:35 -0500 Subject: [PATCH 0481/2556] [ARM] msm: halibut: Update platform data for android USB gadget drivers. Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/board-halibut.c | 37 ++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-msm/board-halibut.c b/arch/arm/mach-msm/board-halibut.c index 2968db9bce3c6..f9e0abdb8f208 100644 --- a/arch/arm/mach-msm/board-halibut.c +++ b/arch/arm/mach-msm/board-halibut.c @@ -46,7 +46,7 @@ #include #endif #ifdef CONFIG_USB_ANDROID -#include +#include #endif #include "devices.h" @@ -380,6 +380,16 @@ static struct usb_mass_storage_platform_data mass_storage_pdata = { .product = "Halibut", .release = 0x0100, }; +#endif + +#ifdef CONFIG_USB_ANDROID +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .vendor = "Qualcomm", + .product = "Halibut", + .release = 0x0100, +}; +#endif static struct platform_device usb_mass_storage_device = { .name = "usb_mass_storage", @@ -388,18 +398,35 @@ static struct platform_device usb_mass_storage_device = { .platform_data = &mass_storage_pdata, }, }; -#endif #ifdef CONFIG_USB_ANDROID +static char *usb_functions[] = { "usb_mass_storage" }; +static char *usb_functions_adb[] = { "usb_mass_storage", "adb" }; + +static struct android_usb_product usb_products[] = { + { + .product_id = 0x0c01, + .num_functions = ARRAY_SIZE(usb_functions), + .functions = usb_functions, + }, + { + .product_id = 0x0c02, + .num_functions = ARRAY_SIZE(usb_functions_adb), + .functions = usb_functions_adb, + }, +}; + static struct android_usb_platform_data android_usb_pdata = { .vendor_id = 0x18d1, .product_id = 0x0c01, - .adb_product_id = 0x0c02, .version = 0x0100, .serial_number = "42", .product_name = "Halibutdroid", .manufacturer_name = "Qualcomm", - .nluns = 1, + .num_products = ARRAY_SIZE(usb_products), + .products = usb_products, + .num_functions = ARRAY_SIZE(usb_functions_adb), + .functions = usb_functions_adb, }; static struct platform_device android_usb_device = { @@ -423,9 +450,7 @@ static struct platform_device *devices[] __initdata = { &msm_device_smd, &msm_device_nand, &msm_device_hsusb, -#ifdef CONFIG_USB_FUNCTION_MASS_STORAGE &usb_mass_storage_device, -#endif #ifdef CONFIG_USB_ANDROID &android_usb_device, #endif From 4f4764de4fa93df49ffbe649e6ebca161379ea14 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 11 Jan 2010 21:38:46 -0800 Subject: [PATCH 0482/2556] [ARM] msm: usb: gadget: misc [ARM] msm: usb: gadget: Reset the controller before and after resetting the PHY Change-Id: If0a415b4727608a78d7c667465cfa4aa0bf424e8 Signed-off-by: Mike Lockwood [ARM] msm: usb: gadget: use dev_set_name() Replacing strcpy() to device->bus_id which no longer exists in 2.6.32 Change-Id: I85d14a5569d8a70107e87bc275625cff11b836f6 Signed-off-by: Mike Lockwood [ARM] msm: usb: gadget: Integrate recent changes from msm_hsusb function driver Change-Id: I58d7cd8ea6622aef2e7d4fd0d2e47c08592aca49 Signed-off-by: Mike Lockwood [ARM] msm: usb: gadget: Add support for interrupt and isochronous endpoints Change-Id: Ica1c8e51883dff8d55156d79f831053807a2034e Signed-off-by: Mike Lockwood [ARM] msm: usb: gadget: Fix receiving ep0 out requests with length > 0 Change-Id: I4a476fcf326bbf0ea4bbafa8cbb703af8674925c Signed-off-by: Mike Lockwood [ARM] msm: usb: gadget: Another fix for ep0 requests with length > 0 Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/include/mach/msm_hsusb_hw.h | 41 +- drivers/usb/gadget/msm72k_udc.c | 360 +++++++++++++----- 2 files changed, 308 insertions(+), 93 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h b/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h index 8042b3135dbf2..a2035d936598b 100644 --- a/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h +++ b/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h @@ -51,7 +51,13 @@ #define USB_HWDEVICE (MSM_USB_BASE + 0x000C) #define USB_HWTXBUF (MSM_USB_BASE + 0x0010) #define USB_HWRXBUF (MSM_USB_BASE + 0x0014) + +#ifdef CONFIG_ARCH_MSM7X00A #define USB_SBUSCFG (MSM_USB_BASE + 0x0090) +#else +#define USB_AHBBURST (MSM_USB_BASE + 0x0090) +#define USB_AHBMODE (MSM_USB_BASE + 0x0098) +#endif #define USB_CAPLENGTH (MSM_USB_BASE + 0x0100) /* 8 bit */ #define USB_HCIVERSION (MSM_USB_BASE + 0x0102) /* 16 bit */ @@ -133,14 +139,14 @@ struct ept_queue_item { #define INFO_TXN_ERROR (1 << 3) -#define STS_NAKI (1 << 16) /* */ -#define STS_SLI (1 << 8) /* R/WC - suspend state entered */ -#define STS_SRI (1 << 7) /* R/WC - SOF recv'd */ -#define STS_URI (1 << 6) /* R/WC - RESET recv'd - write to clear */ -#define STS_FRI (1 << 3) /* R/WC - Frame List Rollover */ -#define STS_PCI (1 << 2) /* R/WC - Port Change Detect */ -#define STS_UEI (1 << 1) /* R/WC - USB Error */ -#define STS_UI (1 << 0) /* R/WC - USB Transaction Complete */ +#define STS_NAKI (1 << 16) /* */ +#define STS_SLI (1 << 8) /* R/WC - suspend state entered */ +#define STS_SRI (1 << 7) /* R/WC - SOF recv'd */ +#define STS_URI (1 << 6) /* R/WC - RESET recv'd - write to clear */ +#define STS_FRI (1 << 3) /* R/WC - Frame List Rollover */ +#define STS_PCI (1 << 2) /* R/WC - Port Change Detect */ +#define STS_UEI (1 << 1) /* R/WC - USB Error */ +#define STS_UI (1 << 0) /* R/WC - USB Transaction Complete */ /* bits used in all the endpoint status registers */ @@ -182,6 +188,13 @@ struct ept_queue_item { #define ULPI_DATA(n) ((n) & 255) #define ULPI_DATA_READ(n) (((n) >> 8) & 255) +#define ULPI_DEBUG_REG (0x15) +#define ULPI_SCRATCH_REG (0x16) + +#define ULPI_FUNC_CTRL_CLR (0x06) +#define ULPI_FUNC_SUSPENDM (1 << 6) + + /* USB_PORTSC bits for determining port speed */ #define PORTSC_PSPD_FS (0 << 26) #define PORTSC_PSPD_LS (1 << 26) @@ -201,4 +214,16 @@ struct ept_queue_item { #define PORTSC_PTC_K_STATE (0x02 << 16) #define PORTSC_PTC_SE0_NAK (0x03 << 16) #define PORTSC_PTC_TST_PKT (0x04 << 16) + +#define PORTSC_PTS_MASK (3 << 30) +#define PORTSC_PTS_ULPI (2 << 30) +#define PORTSC_PTS_SERIAL (3 << 30) + +#define PORTSC_CCS (1 << 0) /* current connect status */ +#define PORTSC_FPR (1 << 6) /* R/W - State normal => suspend */ +#define PORTSC_SUSP (1 << 7) /* Read - Port in suspend state */ +#define PORTSC_PORT_RESET (1 << 8) +#define PORTSC_LS (3 << 10) /* Read - Port's Line status */ +#define PORTSC_PHCD (1 << 23) /* phy suspend mode */ + #endif /* __LINUX_USB_GADGET_MSM72K_UDC_H__ */ diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index c9ba072d2147c..e344df6ac1bd6 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -161,6 +161,7 @@ struct usb_info { int *phy_init_seq; void (*phy_reset)(void); + void (*hw_reset)(bool en); /* for notification when USB is connected or disconnected */ void (*usb_connected)(int); @@ -177,6 +178,8 @@ struct usb_info { struct clk *clk; struct clk *pclk; + struct clk *otgclk; + struct clk *ebi1clk; unsigned int ep0_dir; u16 test_mode; @@ -203,7 +206,6 @@ static int usb_ep_get_stall(struct msm_endpoint *ept) return (CTRL_RXS & n) ? 1 : 0; } -#if 0 static unsigned ulpi_read(struct usb_info *ui, unsigned reg) { unsigned timeout = 100000; @@ -213,8 +215,7 @@ static unsigned ulpi_read(struct usb_info *ui, unsigned reg) USB_ULPI_VIEWPORT); /* wait for completion */ - while ((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout)) - ; + while ((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout)) ; if (timeout == 0) { ERROR("ulpi_read: timeout %08x\n", readl(USB_ULPI_VIEWPORT)); @@ -222,9 +223,8 @@ static unsigned ulpi_read(struct usb_info *ui, unsigned reg) } return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT)); } -#endif -static void ulpi_write(struct usb_info *ui, unsigned val, unsigned reg) +static int ulpi_write(struct usb_info *ui, unsigned val, unsigned reg) { unsigned timeout = 10000; @@ -234,11 +234,14 @@ static void ulpi_write(struct usb_info *ui, unsigned val, unsigned reg) USB_ULPI_VIEWPORT); /* wait for completion */ - while ((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout)) - ; + while((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout)) ; - if (timeout == 0) - ERROR("ulpi_write: timeout\n"); + if (timeout == 0) { + printk(KERN_ERR "ulpi_write: timeout\n"); + return -1; + } + + return 0; } static void ulpi_init(struct usb_info *ui) @@ -357,23 +360,57 @@ static void usb_ept_enable(struct msm_endpoint *ept, int yes, n = readl(USB_ENDPTCTRL(ept->num)); if (in) { + n = (n & (~CTRL_TXT_MASK)); if (yes) { - n = (n & (~CTRL_TXT_MASK)) | - (ep_type << CTRL_TXT_EP_TYPE_SHIFT); n |= CTRL_TXE | CTRL_TXR; - } else + } else { n &= (~CTRL_TXE); + } + if (yes) { + switch (ep_type) { + case USB_ENDPOINT_XFER_BULK: + n |= CTRL_TXT_BULK; + break; + case USB_ENDPOINT_XFER_INT: + n |= CTRL_TXT_INT; + break; + case USB_ENDPOINT_XFER_ISOC: + n |= CTRL_TXT_ISOCH; + break; + default: + pr_err("%s: unsupported ep_type %d for %s\n", + __func__, ep_type, ept->ep.name); + break; + } + } } else { + n = (n & (~CTRL_RXT_MASK)); if (yes) { - n = (n & (~CTRL_RXT_MASK)) | - (ep_type << CTRL_RXT_EP_TYPE_SHIFT); n |= CTRL_RXE | CTRL_RXR; - } else + } else { n &= ~(CTRL_RXE); + } + if (yes) { + switch (ep_type) { + case USB_ENDPOINT_XFER_BULK: + n |= CTRL_RXT_BULK; + break; + case USB_ENDPOINT_XFER_INT: + n |= CTRL_RXT_INT; + break; + case USB_ENDPOINT_XFER_ISOC: + n |= CTRL_RXT_ISOCH; + break; + default: + pr_err("%s: unsupported ep_type %d for %s\n", + __func__, ep_type, ept->ep.name); + break; + } + } } writel(n, USB_ENDPTCTRL(ept->num)); -#if 1 +#if 0 INFO("ept %d %s %s\n", ept->num, in ? "in" : "out", yes ? "enabled" : "disabled"); #endif @@ -580,9 +617,11 @@ static void handle_setup(struct usb_info *ui) flush_endpoint(&ui->ep0out); flush_endpoint(&ui->ep0in); +#if 0 INFO("setup: type=%02x req=%02x val=%04x idx=%04x len=%04x\n", ctl.bRequestType, ctl.bRequest, ctl.wValue, ctl.wIndex, ctl.wLength); +#endif if ((ctl.bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) == (USB_DIR_IN | USB_TYPE_STANDARD)) { @@ -707,11 +746,11 @@ static void handle_endpoint(struct usb_info *ui, unsigned bit) unsigned long flags; unsigned info; - /* +#if 0 INFO("handle_endpoint() %d %s req=%p(%08x)\n", ept->num, (ept->flags & EPT_FLAG_IN) ? "in" : "out", ept->req, ept->req ? ept->req->item_dma : 0); - */ +#endif /* expire all requests that are no longer active */ spin_lock_irqsave(&ui->lock, flags); @@ -766,19 +805,43 @@ static void handle_endpoint(struct usb_info *ui, unsigned bit) spin_unlock_irqrestore(&ui->lock, flags); } +#define FLUSH_WAIT_US 5 +#define FLUSH_TIMEOUT (2 * (USEC_PER_SEC / FLUSH_WAIT_US)) static void flush_endpoint_hw(struct usb_info *ui, unsigned bits) { + uint32_t unflushed = 0; + uint32_t stat = 0; + int cnt = 0; + /* flush endpoint, canceling transactions ** - this can take a "large amount of time" (per databook) ** - the flush can fail in some cases, thus we check STAT ** and repeat if we're still operating ** (does the fact that this doesn't use the tripwire matter?!) */ - do { + while (cnt < FLUSH_TIMEOUT) { writel(bits, USB_ENDPTFLUSH); - while (readl(USB_ENDPTFLUSH) & bits) - udelay(100); - } while (readl(USB_ENDPTSTAT) & bits); + while (((unflushed = readl(USB_ENDPTFLUSH)) & bits) && + cnt < FLUSH_TIMEOUT) { + cnt++; + udelay(FLUSH_WAIT_US); + } + + stat = readl(USB_ENDPTSTAT); + if (cnt >= FLUSH_TIMEOUT) + goto err; + if (!(stat & bits)) + goto done; + cnt++; + udelay(FLUSH_WAIT_US); + } + +err: + pr_warning("%s: Could not complete flush! NOT GOOD! " + "stat: %x unflushed: %x bits: %x\n", __func__, + stat, unflushed, bits); +done: + return; } static void flush_endpoint_sw(struct msm_endpoint *ept) @@ -933,42 +996,120 @@ static void usb_prepare(struct usb_info *ui) static void usb_suspend_phy(struct usb_info *ui) { +#if defined(CONFIG_ARCH_QSD8X50) + /* clear VBusValid and SessionEnd rising interrupts */ + ulpi_write(ui, (1 << 1) | (1 << 3), 0x0f); + /* clear VBusValid and SessionEnd falling interrupts */ + ulpi_write(ui, (1 << 1) | (1 << 3), 0x12); + + /* Disable 60MHz CLKOUT in serial or carkit mode */ + ulpi_write(ui, 0x08, 0x09); + + /* Enable PHY Low Power Suspend - Clock Disable (PLPSCD) */ + writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); + mdelay(1); +#else /* clear VBusValid and SessionEnd rising interrupts */ ulpi_write(ui, (1 << 1) | (1 << 3), 0x0f); /* clear VBusValid and SessionEnd falling interrupts */ ulpi_write(ui, (1 << 1) | (1 << 3), 0x12); /* disable interface protect circuit to drop current consumption */ ulpi_write(ui, (1 << 7), 0x08); + /* clear the SuspendM bit -> suspend the PHY */ + ulpi_write(ui, 1 << 6, 0x06); +#endif +} + +/* If this function returns < 0, the phy reset failed and we cannot + * continue at this point. The only solution is to wait until the next + * cable disconnect/reconnect to bring the phy back */ +static int usb_phy_reset(struct usb_info *ui) +{ + u32 val; + int ret; + int retries; + + if (!ui->phy_reset) + return 0; + + if (ui->hw_reset) + ui->hw_reset(1); + ui->phy_reset(); + if (ui->hw_reset) + ui->hw_reset(0); + +#if defined(CONFIG_ARCH_QSD8X50) + val = readl(USB_PORTSC) & ~PORTSC_PTS_MASK; + writel(val | PORTSC_PTS_ULPI, USB_PORTSC); + + /* XXX: only necessary for pre-45nm internal PHYs. */ + for (retries = 3; retries > 0; retries--) { + ret = ulpi_write(ui, ULPI_FUNC_SUSPENDM, ULPI_FUNC_CTRL_CLR); + if (!ret) + break; + ui->phy_reset(); + } + if (!retries) + return -1; + + /* this reset calibrates the phy, if the above write succeeded */ + ui->phy_reset(); + + /* XXX: pre-45nm internal phys have a known issue which can cause them + * to lockup on reset. If ULPI accesses fail, try resetting the phy + * again */ + for (retries = 3; retries > 0; retries--) { + ret = ulpi_read(ui, ULPI_DEBUG_REG); + if (ret != 0xffffffff) + break; + ui->phy_reset(); + } + if (!retries) + return -1; +#endif + pr_info("msm_hsusb_phy_reset: success\n"); + return 0; } static void usb_reset(struct usb_info *ui) { unsigned long flags; - unsigned otgsc; - - INFO("msm72k_udc: reset controller\n"); + printk(KERN_INFO "hsusb: reset controller\n"); spin_lock_irqsave(&ui->lock, flags); ui->running = 0; spin_unlock_irqrestore(&ui->lock, flags); + /* To prevent phantom packets being received by the usb core on + * some devices, put the controller into reset prior to + * resetting the phy. */ + writel(2, USB_USBCMD); + msleep(10); + #if 0 /* we should flush and shutdown cleanly if already running */ writel(0xffffffff, USB_ENDPTFLUSH); msleep(2); #endif - otgsc = readl(USB_OTGSC); + if (usb_phy_reset(ui) < 0) + pr_err("%s: Phy reset failed!\n", __func__); + + msleep(100); /* RESET */ writel(2, USB_USBCMD); msleep(10); - if (ui->phy_reset) - ui->phy_reset(); - +#ifdef CONFIG_ARCH_MSM7X00A /* INCR4 BURST mode */ writel(0x01, USB_SBUSCFG); +#else + /* bursts of unspecified length. */ + writel(0, USB_AHBBURST); + /* Use the AHB transactor */ + writel(0, USB_AHBMODE); +#endif /* select DEVICE mode */ writel(0x12, USB_USBMODE); @@ -995,7 +1136,6 @@ static void usb_reset(struct usb_info *ui) } /* enable interrupts */ - writel(otgsc, USB_OTGSC); writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR); /* go to RUN mode (D+ pullup enable) */ @@ -1034,6 +1174,10 @@ static int usb_free(struct usb_info *ui, int ret) clk_put(ui->clk); if (ui->pclk) clk_put(ui->pclk); + if (ui->otgclk) + clk_put(ui->otgclk); + if (ui->ebi1clk) + clk_put(ui->ebi1clk); kfree(ui); return ret; } @@ -1043,10 +1187,11 @@ static void usb_do_work_check_vbus(struct usb_info *ui) unsigned long iflags; spin_lock_irqsave(&ui->lock, iflags); - if (vbus) + if (vbus) { ui->flags |= USB_FLAG_VBUS_ONLINE; - else + } else { ui->flags |= USB_FLAG_VBUS_OFFLINE; + } spin_unlock_irqrestore(&ui->lock, iflags); } @@ -1071,8 +1216,12 @@ static void usb_do_work(struct work_struct *w) case USB_STATE_IDLE: if (flags & USB_FLAG_START) { pr_info("msm72k_udc: IDLE -> ONLINE\n"); + clk_set_rate(ui->ebi1clk, 128000000); + udelay(10); clk_enable(ui->clk); clk_enable(ui->pclk); + if (ui->otgclk) + clk_enable(ui->otgclk); usb_reset(ui); ui->state = USB_STATE_ONLINE; @@ -1109,6 +1258,9 @@ static void usb_do_work(struct work_struct *w) usb_suspend_phy(ui); clk_disable(ui->pclk); clk_disable(ui->clk); + if (ui->otgclk) + clk_disable(ui->otgclk); + clk_set_rate(ui->ebi1clk, 0); spin_unlock_irqrestore(&ui->lock, iflags); ui->state = USB_STATE_OFFLINE; @@ -1128,12 +1280,19 @@ static void usb_do_work(struct work_struct *w) */ if ((flags & USB_FLAG_VBUS_ONLINE) && _vbus) { pr_info("msm72k_udc: OFFLINE -> ONLINE\n"); + clk_set_rate(ui->ebi1clk, 128000000); + udelay(10); clk_enable(ui->clk); clk_enable(ui->pclk); + if (ui->otgclk) + clk_enable(ui->otgclk); usb_reset(ui); - if (ui->usb_connected) - ui->usb_connected(1); + /* detect shorted D+/D-, indicating AC power */ + msleep(10); + if ((readl(USB_PORTSC) & PORTSC_LS) == PORTSC_LS) + if (ui->usb_connected) + ui->usb_connected(2); ui->state = USB_STATE_ONLINE; usb_do_work_check_vbus(ui); @@ -1149,40 +1308,36 @@ static void usb_do_work(struct work_struct *w) */ void msm_hsusb_set_vbus_state(int online) { - unsigned long flags; + unsigned long flags = 0; struct usb_info *ui = the_usb_info; - if (ui) { + if (ui) spin_lock_irqsave(&ui->lock, flags); - if (vbus != online) { - vbus = online; - if (online) + if (vbus != online) { + vbus = online; + if (ui) { + if (online) { ui->flags |= USB_FLAG_VBUS_ONLINE; - else + } else { ui->flags |= USB_FLAG_VBUS_OFFLINE; + } schedule_work(&ui->work); } - spin_unlock_irqrestore(&ui->lock, flags); - } else { - printk(KERN_ERR "msm_hsusb_set_vbus_state called before driver initialized\n"); - vbus = online; } + if (ui) + spin_unlock_irqrestore(&ui->lock, flags); } -#if defined(CONFIG_DEBUG_FS) +#if defined(CONFIG_DEBUG_FS) && 0 void usb_function_reenumerate(void) { struct usb_info *ui = the_usb_info; /* disable and re-enable the D+ pullup */ - INFO("msm72k_udc: disable pullup\n"); - writel(0x00080000, USB_USBCMD); - + msm72k_pullup(&ui->gadget, false); msleep(10); - - INFO("msm72k_udc: enable pullup\n"); - writel(0x00080001, USB_USBCMD); + msm72k_pullup(&ui->gadget, true); } static char debug_buffer[PAGE_SIZE]; @@ -1201,17 +1356,17 @@ static ssize_t debug_read_status(struct file *file, char __user *ubuf, spin_lock_irqsave(&ui->lock, flags); i += scnprintf(buf + i, PAGE_SIZE - i, - "regs: setup=%08x prime=%08x stat=%08x done=%08x\n", - readl(USB_ENDPTSETUPSTAT), - readl(USB_ENDPTPRIME), - readl(USB_ENDPTSTAT), - readl(USB_ENDPTCOMPLETE)); + "regs: setup=%08x prime=%08x stat=%08x done=%08x\n", + readl(USB_ENDPTSETUPSTAT), + readl(USB_ENDPTPRIME), + readl(USB_ENDPTSTAT), + readl(USB_ENDPTCOMPLETE)); i += scnprintf(buf + i, PAGE_SIZE - i, - "regs: cmd=%08x sts=%08x intr=%08x port=%08x\n\n", - readl(USB_USBCMD), - readl(USB_USBSTS), - readl(USB_USBINTR), - readl(USB_PORTSC)); + "regs: cmd=%08x sts=%08x intr=%08x port=%08x\n\n", + readl(USB_USBCMD), + readl(USB_USBSTS), + readl(USB_USBINTR), + readl(USB_PORTSC)); for (n = 0; n < 32; n++) { @@ -1220,22 +1375,23 @@ static ssize_t debug_read_status(struct file *file, char __user *ubuf, continue; i += scnprintf(buf + i, PAGE_SIZE - i, - "ept%d %s cfg=%08x active=%08x next=%08x info=%08x\n", - ept->num, (ept->flags & EPT_FLAG_IN) ? "in " : "out", - ept->head->config, ept->head->active, - ept->head->next, ept->head->info); + "ept%d %s cfg=%08x active=%08x next=%08x info=%08x\n", + ept->num, (ept->flags & EPT_FLAG_IN) ? "in " : "out", + ept->head->config, ept->head->active, + ept->head->next, ept->head->info); for (req = ept->req; req; req = req->next) i += scnprintf(buf + i, PAGE_SIZE - i, - " req @%08x next=%08x info=%08x page0=%08x %c %c\n", - req->item_dma, req->item->next, - req->item->info, req->item->page0, - req->busy ? 'B' : ' ', - req->live ? 'L' : ' '); + " req @%08x next=%08x info=%08x page0=%08x %c %c\n", + req->item_dma, req->item->next, + req->item->info, req->item->page0, + req->busy ? 'B' : ' ', + req->live ? 'L' : ' ' + ); } i += scnprintf(buf + i, PAGE_SIZE - i, - "phy failure count: %d\n", ui->phy_fail_count); + "phy failure count: %d\n", ui->phy_fail_count); spin_unlock_irqrestore(&ui->lock, flags); @@ -1292,8 +1448,8 @@ static void usb_debugfs_init(struct usb_info *ui) return; debugfs_create_file("status", 0444, dent, ui, &debug_stat_ops); - debugfs_create_file("reset", 0222, dent, ui, &debug_reset_ops); - debugfs_create_file("cycle", 0222, dent, ui, &debug_cycle_ops); + debugfs_create_file("reset", 0220, dent, ui, &debug_reset_ops); + debugfs_create_file("cycle", 0220, dent, ui, &debug_cycle_ops); } #else static void usb_debugfs_init(struct usb_info *ui) {} @@ -1356,13 +1512,17 @@ msm72k_queue(struct usb_ep *_ep, struct usb_request *req, gfp_t gfp_flags) struct msm_request *r = to_msm_request(req); if (!req->length) goto ep_queue_done; - r->gadget_complete = req->complete; - /* ep0_queue_ack_complete queue a receive for ACK before - ** calling req->complete - */ - req->complete = ep0_queue_ack_complete; - if (ui->ep0_dir == USB_DIR_OUT) - ep = &ui->ep0out; + else { + if (ui->ep0_dir == USB_DIR_OUT) { + ep = &ui->ep0out; + ep->ep.driver_data = ui->ep0in.ep.driver_data; + } + /* ep0_queue_ack_complete queue a receive for ACK before + ** calling req->complete + */ + r->gadget_complete = req->complete; + req->complete = ep0_queue_ack_complete; + } } ep_queue_done: return usb_ept_queue_xfer(ep, req); @@ -1493,11 +1653,20 @@ static int msm72k_pullup(struct usb_gadget *_gadget, int is_active) { struct usb_info *ui = container_of(_gadget, struct usb_info, gadget); + u32 cmd = (8 << 16); + + /* disable/enable D+ pullup */ if (is_active) { - if (vbus && ui->driver) - writel(0x00080001, USB_USBCMD); - } else - writel(0x00080000, USB_USBCMD); + pr_info("msm_hsusb: enable pullup\n"); + writel(cmd | 1, USB_USBCMD); + } else { + pr_info("msm_hsusb: disable pullup\n"); + writel(cmd, USB_USBCMD); + +#if defined(CONFIG_ARCH_QSD8X50) + ulpi_write(ui, 0x48, 0x04); +#endif + } return 0; } @@ -1557,6 +1726,7 @@ static int msm72k_probe(struct platform_device *pdev) if (!ui) return -ENOMEM; + spin_lock_init(&ui->lock); ui->pdev = pdev; if (pdev->dev.platform_data) { @@ -1594,6 +1764,26 @@ static int msm72k_probe(struct platform_device *pdev) if (IS_ERR(ui->pclk)) return usb_free(ui, PTR_ERR(ui->pclk)); + ui->otgclk = clk_get(&pdev->dev, "usb_otg_clk"); + if (IS_ERR(ui->otgclk)) + ui->otgclk = NULL; + + ui->ebi1clk = clk_get(NULL, "ebi1_clk"); + if (IS_ERR(ui->ebi1clk)) + return usb_free(ui, PTR_ERR(ui->ebi1clk)); + + /* clear interrupts before requesting irq */ + clk_enable(ui->clk); + clk_enable(ui->pclk); + if (ui->otgclk) + clk_enable(ui->otgclk); + writel(0, USB_USBINTR); + writel(0, USB_OTGSC); + if (ui->otgclk) + clk_disable(ui->otgclk); + clk_disable(ui->pclk); + clk_disable(ui->clk); + ret = request_irq(irq, usb_interrupt, 0, pdev->name, ui); if (ret) return usb_free(ui, ret); @@ -1603,7 +1793,7 @@ static int msm72k_probe(struct platform_device *pdev) ui->gadget.ops = &msm72k_ops; ui->gadget.is_dualspeed = 1; device_initialize(&ui->gadget.dev); - strcpy(ui->gadget.dev.bus_id, "gadget"); + dev_set_name(&ui->gadget.dev, "gadget"); ui->gadget.dev.parent = &pdev->dev; ui->gadget.dev.dma_mask = pdev->dev.dma_mask; From 367613e76077214a6ef282e2b5b8ee59b8be79fc Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Fri, 8 Jan 2010 14:14:49 -0500 Subject: [PATCH 0483/2556] [ARM] msm: swordfish: Update platform data for android USB gadget driver. Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/board-swordfish.c | 37 ++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-msm/board-swordfish.c b/arch/arm/mach-msm/board-swordfish.c index 5d8f70bdba9b3..ecd675474187e 100644 --- a/arch/arm/mach-msm/board-swordfish.c +++ b/arch/arm/mach-msm/board-swordfish.c @@ -40,7 +40,7 @@ #endif #ifdef CONFIG_USB_ANDROID -#include +#include #endif #include "board-swordfish.h" @@ -165,6 +165,16 @@ static struct usb_mass_storage_platform_data mass_storage_pdata = { .product = "Swordfish", .release = 0x0100, }; +#endif + +#ifdef CONFIG_USB_ANDROID +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .vendor = "Qualcomm", + .product = "Swordfish", + .release = 0x0100, +}; +#endif static struct platform_device usb_mass_storage_device = { .name = "usb_mass_storage", @@ -173,7 +183,6 @@ static struct platform_device usb_mass_storage_device = { .platform_data = &mass_storage_pdata, }, }; -#endif static struct resource msm_kgsl_resources[] = { { @@ -267,15 +276,33 @@ static struct platform_device android_pmem_gpu1_device = { }; #ifdef CONFIG_USB_ANDROID +static char *usb_functions[] = { "usb_mass_storage" }; +static char *usb_functions_adb[] = { "usb_mass_storage", "adb" }; + +static struct android_usb_product usb_products[] = { + { + .product_id = 0x0c01, + .num_functions = ARRAY_SIZE(usb_functions), + .functions = usb_functions, + }, + { + .product_id = 0x0c02, + .num_functions = ARRAY_SIZE(usb_functions_adb), + .functions = usb_functions_adb, + }, +}; + static struct android_usb_platform_data android_usb_pdata = { .vendor_id = 0x18d1, .product_id = 0x0d01, - .adb_product_id = 0x0d02, .version = 0x0100, .serial_number = "42", .product_name = "Swordfishdroid", .manufacturer_name = "Qualcomm", - .nluns = 1, + .num_products = ARRAY_SIZE(usb_products), + .products = usb_products, + .num_functions = ARRAY_SIZE(usb_functions_adb), + .functions = usb_functions_adb, }; static struct platform_device android_usb_device = { @@ -309,9 +336,7 @@ static struct platform_device *devices[] __initdata = { &msm_device_smd, &msm_device_nand, &msm_device_hsusb, -#ifdef CONFIG_USB_FUNCTION_MASS_STORAGE &usb_mass_storage_device, -#endif #ifdef CONFIG_USB_ANDROID &android_usb_device, #endif From 00ca9993cf0cba8cf17fc7b859270c7c96f71ce8 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Wed, 13 Jan 2010 15:54:03 -0800 Subject: [PATCH 0484/2556] USB: gadget: MSM7K diag gadget function driver. Change-Id: Ib25e367bc9951631c4288d63b93e48820a76bc36 Signed-off-by: Mike Lockwood --- drivers/usb/gadget/Kconfig | 6 + drivers/usb/gadget/Makefile | 3 + drivers/usb/gadget/diag.c | 421 ++++++++++++++++++++++++++++++++++++ 3 files changed, 430 insertions(+) create mode 100644 drivers/usb/gadget/diag.c diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index ad68beef36e4d..9df64e685c912 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -941,6 +941,12 @@ config USB_ANDROID_ADB help Provides adb function for android gadget driver. +config USB_ANDROID_DIAG + boolean "USB MSM7K Diag Function" + depends on USB_ANDROID + help + Qualcomm diagnostics interface support. + config USB_ANDROID_MASS_STORAGE boolean "Android gadget mass storage function" depends on USB_ANDROID && SWITCH diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index cab00d4733d75..982820588ee35 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -73,3 +73,6 @@ obj-$(CONFIG_USB_ANDROID_ADB) += f_adb.o obj-$(CONFIG_USB_ANDROID_MASS_STORAGE) += f_mass_storage.o obj-$(CONFIG_USB_ANDROID_MTP) += f_mtp.o obj-$(CONFIG_USB_ANDROID_RNDIS) += f_rndis.o u_ether.o + +# MSM specific +obj-$(CONFIG_USB_ANDROID_DIAG) += diag.o diff --git a/drivers/usb/gadget/diag.c b/drivers/usb/gadget/diag.c new file mode 100644 index 0000000000000..b2ff21cff63df --- /dev/null +++ b/drivers/usb/gadget/diag.c @@ -0,0 +1,421 @@ +/* + * Diag Function Device - Route DIAG frames between SMD and USB + * + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland + * + * 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 + +#define NO_HDLC 1 + +#if 1 +#define TRACE(tag,data,len,decode) do {} while(0) +#else +static void TRACE(const char *tag, const void *_data, int len, int decode) +{ + const unsigned char *data = _data; + int escape = 0; + + printk(KERN_INFO "%s", tag); + if (decode) { + while (len-- > 0) { + unsigned x = *data++; + if (x == 0x7e) { + printk(" $$"); + escape = 0; + continue; + } + if (x == 0x7d) { + escape = 1; + continue; + } + if (escape) { + escape = 0; + printk(" %02x", x ^ 0x20); + } else { + printk(" %02x", x); + } + } + } else { + while (len-- > 0) { + printk(" %02x", *data++); + } + printk(" $$"); + } + printk("\n"); +} +#endif + +#define HDLC_MAX 4096 + +struct diag_context +{ + struct usb_function function; + struct usb_composite_dev *cdev; + struct usb_ep *out; + struct usb_ep *in; + struct usb_request *req_out; + struct usb_request *req_in; + smd_channel_t *ch; + int in_busy; + int online; + + /* assembly buffer for USB->A9 HDLC frames */ + unsigned char hdlc_buf[HDLC_MAX]; + unsigned hdlc_count; + unsigned hdlc_escape; +}; + +static struct usb_interface_descriptor diag_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = 0xFF, + .bInterfaceSubClass = 0xFF, + .bInterfaceProtocol = 0xFF, +}; + +static struct usb_endpoint_descriptor diag_highspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor diag_highspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor diag_fullspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor diag_fullspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_diag_descs[] = { + (struct usb_descriptor_header *) &diag_interface_desc, + (struct usb_descriptor_header *) &diag_fullspeed_in_desc, + (struct usb_descriptor_header *) &diag_fullspeed_out_desc, + NULL, +}; + +static struct usb_descriptor_header *hs_diag_descs[] = { + (struct usb_descriptor_header *) &diag_interface_desc, + (struct usb_descriptor_header *) &diag_highspeed_in_desc, + (struct usb_descriptor_header *) &diag_highspeed_out_desc, + NULL, +}; + +static struct diag_context _context; + +static inline struct diag_context *func_to_dev(struct usb_function *f) +{ + return container_of(f, struct diag_context, function); +} + +static void smd_try_to_send(struct diag_context *ctxt); + +static void diag_queue_out(struct diag_context *ctxt); + +static void diag_in_complete(struct usb_ep *ept, struct usb_request *req) +{ + struct diag_context *ctxt = req->context; + ctxt->in_busy = 0; + smd_try_to_send(ctxt); +} + +static void diag_process_hdlc(struct diag_context *ctxt, void *_data, unsigned len) +{ + unsigned char *data = _data; + unsigned count = ctxt->hdlc_count; + unsigned escape = ctxt->hdlc_escape; + unsigned char *hdlc = ctxt->hdlc_buf; + + while (len-- > 0) { + unsigned char x = *data++; + if (x == 0x7E) { + if (count > 2) { + /* we're just ignoring the crc here */ + TRACE("PC>", hdlc, count - 2, 0); + if (ctxt->ch) + smd_write(ctxt->ch, hdlc, count - 2); + } + count = 0; + escape = 0; + } else if (x == 0x7D) { + escape = 1; + } else { + if (escape) { + x = x ^ 0x20; + escape = 0; + } + hdlc[count++] = x; + + /* discard frame if we overflow */ + if (count == HDLC_MAX) + count = 0; + } + } + + ctxt->hdlc_count = count; + ctxt->hdlc_escape = escape; +} + +static void diag_out_complete(struct usb_ep *ept, struct usb_request *req) +{ + struct diag_context *ctxt = req->context; + + if (req->status == 0) { +#if NO_HDLC + TRACE("PC>", req->buf, req->actual, 0); + if (ctxt->ch) + smd_write(ctxt->ch, req->buf, req->actual); +#else + diag_process_hdlc(ctxt, req->buf, req->actual); +#endif + } + diag_queue_out(ctxt); +} + +static void diag_queue_out(struct diag_context *ctxt) +{ + struct usb_request *req = ctxt->req_out; + + req->complete = diag_out_complete; + req->context = ctxt; + req->length = 8192; + + usb_ep_queue(ctxt->out, req, GFP_ATOMIC); +} + +static void smd_try_to_send(struct diag_context *ctxt) +{ +again: + if (ctxt->ch && (!ctxt->in_busy)) { + int r = smd_read_avail(ctxt->ch); + + if (r > 8192) { + return; + } + if (r > 0) { + struct usb_request *req = ctxt->req_in; + smd_read(ctxt->ch, req->buf, r); + + if (!ctxt->online) { +// printk("$$$ discard %d\n", r); + goto again; + } + req->complete = diag_in_complete; + req->context = ctxt; + req->length = r; + + TRACE("A9>", req->buf, r, 1); + ctxt->in_busy = 1; + usb_ep_queue(ctxt->in, req, GFP_ATOMIC); + } + } +} + +static void smd_diag_notify(void *priv, unsigned event) +{ + struct diag_context *ctxt = priv; + smd_try_to_send(ctxt); +} + +static int __init create_bulk_endpoints(struct diag_context *ctxt, + struct usb_endpoint_descriptor *in_desc, + struct usb_endpoint_descriptor *out_desc) +{ + struct usb_composite_dev *cdev = ctxt->cdev; + struct usb_ep *ep; + + ep = usb_ep_autoconfig(cdev->gadget, in_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); + return -ENODEV; + } + ctxt->in = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + return -ENODEV; + } + ctxt->out = ep; + + ctxt->req_out = usb_ep_alloc_request(ctxt->out, GFP_KERNEL); + ctxt->req_in = usb_ep_alloc_request(ctxt->in, GFP_KERNEL); + ctxt->req_out->buf = kmalloc(8192, GFP_KERNEL); + ctxt->req_in->buf = kmalloc(8192, GFP_KERNEL); + ctxt->req_out->complete = diag_out_complete; + ctxt->req_in->complete = diag_in_complete; + + return 0; +} + +static int +diag_function_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct diag_context *ctxt = func_to_dev(f); + int id; + int ret; + + ctxt->cdev = cdev; + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + diag_interface_desc.bInterfaceNumber = id; + + /* allocate endpoints */ + ret = create_bulk_endpoints(ctxt, &diag_fullspeed_in_desc, + &diag_fullspeed_out_desc); + if (ret) + return ret; + + /* support high speed hardware */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + diag_highspeed_in_desc.bEndpointAddress = + diag_fullspeed_in_desc.bEndpointAddress; + diag_highspeed_out_desc.bEndpointAddress = + diag_fullspeed_out_desc.bEndpointAddress; + } + + return 0; +} + +static void +diag_function_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct diag_context *ctxt = func_to_dev(f); + + kfree(ctxt->req_out->buf); + kfree(ctxt->req_in->buf); + usb_ep_free_request(ctxt->out, ctxt->req_out); + usb_ep_free_request(ctxt->in, ctxt->req_in); +} + +static int diag_function_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct diag_context *ctxt = func_to_dev(f); + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + ret = usb_ep_enable(ctxt->in, + ep_choose(cdev->gadget, + &diag_highspeed_in_desc, + &diag_fullspeed_in_desc)); + if (ret) + return ret; + ret = usb_ep_enable(ctxt->out, + ep_choose(cdev->gadget, + &diag_highspeed_out_desc, + &diag_fullspeed_out_desc)); + if (ret) { + usb_ep_disable(ctxt->in); + return ret; + } + ctxt->online = 1; + + diag_queue_out(ctxt); + smd_try_to_send(ctxt); + + return 0; +} + +static void diag_function_disable(struct usb_function *f) +{ + struct diag_context *ctxt = func_to_dev(f); + + ctxt->online = 0; + usb_ep_disable(ctxt->in); + usb_ep_disable(ctxt->out); +} + +static int diag_set_enabled(const char *val, struct kernel_param *kp) +{ + int enabled = simple_strtol(val, NULL, 0); + if (_context.cdev) + android_enable_function(&_context.function, enabled); + return 0; +} + +static int diag_get_enabled(char *buffer, struct kernel_param *kp) +{ + buffer[0] = '0' + !_context.function.hidden; + return 1; +} +module_param_call(enabled, diag_set_enabled, diag_get_enabled, NULL, 0664); + +int diag_bind_config(struct usb_configuration *c) +{ + struct diag_context *ctxt = &_context; + int ret; + + printk(KERN_INFO "diag_bind_config\n"); + + ret = smd_open("SMD_DIAG", &ctxt->ch, ctxt, smd_diag_notify); + if (ret) + return ret; + + ctxt->cdev = c->cdev; + ctxt->function.name = "diag"; + ctxt->function.descriptors = fs_diag_descs; + ctxt->function.hs_descriptors = hs_diag_descs; + ctxt->function.bind = diag_function_bind; + ctxt->function.unbind = diag_function_unbind; + ctxt->function.set_alt = diag_function_set_alt; + ctxt->function.disable = diag_function_disable; + + /* start disabled */ + ctxt->function.hidden = 1; + + return usb_add_function(c, &ctxt->function); +} + +static struct android_usb_function diag_function = { + .name = "diag", + .bind_config = diag_bind_config, +}; + +static int __init init(void) +{ + printk(KERN_INFO "diag init\n"); + android_register_function(&diag_function); + return 0; +} +module_init(init); From 7f194613f8ba6c1703524716242a56535155716a Mon Sep 17 00:00:00 2001 From: Haley Teng Date: Thu, 4 Feb 2010 00:58:26 -0800 Subject: [PATCH 0485/2556] [ARM] msm: usb: diag: support enable/disable at boot time Signed-off-by: Haley Teng Signed-off-by: Mike Lockwood --- drivers/usb/gadget/diag.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/diag.c b/drivers/usb/gadget/diag.c index b2ff21cff63df..1a905732c3bd1 100644 --- a/drivers/usb/gadget/diag.c +++ b/drivers/usb/gadget/diag.c @@ -82,6 +82,8 @@ struct diag_context unsigned char hdlc_buf[HDLC_MAX]; unsigned hdlc_count; unsigned hdlc_escape; + + int function_enable; }; static struct usb_interface_descriptor diag_interface_desc = { @@ -371,6 +373,7 @@ static int diag_set_enabled(const char *val, struct kernel_param *kp) int enabled = simple_strtol(val, NULL, 0); if (_context.cdev) android_enable_function(&_context.function, enabled); + _context.function_enable = !!enabled; return 0; } @@ -401,8 +404,7 @@ int diag_bind_config(struct usb_configuration *c) ctxt->function.set_alt = diag_function_set_alt; ctxt->function.disable = diag_function_disable; - /* start disabled */ - ctxt->function.hidden = 1; + ctxt->function.hidden = !_context.function_enable; return usb_add_function(c, &ctxt->function); } From 842dc9eebcf692df7be3f253ece60a300c091b92 Mon Sep 17 00:00:00 2001 From: Eric Olsen Date: Wed, 13 Jan 2010 18:19:25 -0800 Subject: [PATCH 0486/2556] drivers: input: misc: cm6302: Remove the GPIO for enable. Just use the function. Signed-off-by: Eric Olsen --- drivers/input/misc/capella_cm3602.c | 18 +++--------------- include/linux/capella_cm3602.h | 1 - 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/drivers/input/misc/capella_cm3602.c b/drivers/input/misc/capella_cm3602.c index f7582d585c61e..55de6b3d51e65 100644 --- a/drivers/input/misc/capella_cm3602.c +++ b/drivers/input/misc/capella_cm3602.c @@ -67,7 +67,6 @@ static int capella_cm3602_enable(struct capella_cm3602_data *data) return 0; } data->pdata->power(1); - rc = gpio_direction_output(data->pdata->p_en, 0); data->enabled = !rc; if (!rc) capella_cm3602_report(data); @@ -82,9 +81,6 @@ static int capella_cm3602_disable(struct capella_cm3602_data *data) D("%s: already disabled\n", __func__); return 0; } - rc = gpio_direction_output(data->pdata->p_en, 1); - if (rc < 0) - return rc; data->pdata->power(0); data->enabled = 0; return rc; @@ -105,18 +101,11 @@ static int capella_cm3602_setup(struct capella_cm3602_data *ip) goto done; } - rc = gpio_request(pdata->p_en, "gpio_proximity_en"); - if (rc < 0) { - pr_err("%s: gpio %d request failed (%d)\n", - __func__, pdata->p_en, rc); - goto fail_free_p_out; - } - rc = gpio_direction_input(pdata->p_out); if (rc < 0) { pr_err("%s: failed to set gpio %d as input (%d)\n", __func__, pdata->p_out, rc); - goto fail_free_p_en; + goto fail_free_p_out; } rc = request_irq(irq, @@ -128,7 +117,7 @@ static int capella_cm3602_setup(struct capella_cm3602_data *ip) pr_err("%s: request_irq(%d) failed for gpio %d (%d)\n", __func__, irq, pdata->p_out, rc); - goto fail_free_p_en; + goto fail_free_p_out; } rc = set_irq_wake(irq, 1); @@ -143,8 +132,6 @@ static int capella_cm3602_setup(struct capella_cm3602_data *ip) fail_free_irq: free_irq(irq, 0); -fail_free_p_en: - gpio_free(pdata->p_en); fail_free_p_out: gpio_free(pdata->p_out); done: @@ -261,6 +248,7 @@ static int capella_cm3602_probe(struct platform_device *pdev) misc_deregister(&capella_cm3602_misc); err_unregister_input_device: input_unregister_device(input_dev); + goto done; err_free_input_device: input_free_device(input_dev); done: diff --git a/include/linux/capella_cm3602.h b/include/linux/capella_cm3602.h index 3dcffddb44730..7f1de9b912a20 100644 --- a/include/linux/capella_cm3602.h +++ b/include/linux/capella_cm3602.h @@ -30,7 +30,6 @@ #define CAPELLA_CM3602 "capella_cm3602" struct capella_cm3602_platform_data { int (*power)(int); /* power to the chip */ - int p_en; /* proximity-sensor enable */ int p_out; /* proximity-sensor outpuCAPELLA_CM3602_IOCTL_ENABLE,t */ }; #endif /* __KERNEL__ */ From 727897532036dc3a1290d13fbdbb01cc8fc1bc3b Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Sat, 16 Jan 2010 13:15:25 -0800 Subject: [PATCH 0487/2556] [ARM] msm: htc: Add support for ACM serial and RNDIS ethernet USB functions. Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/devices_htc.c | 41 +++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c index 1fa330c6e2a31..5130e12a5aaff 100644 --- a/arch/arm/mach-msm/devices_htc.c +++ b/arch/arm/mach-msm/devices_htc.c @@ -114,8 +114,41 @@ static struct platform_device usb_mass_storage_device = { }; #ifdef CONFIG_USB_ANDROID -static char *usb_functions[] = { "usb_mass_storage" }; -static char *usb_functions_adb[] = { "usb_mass_storage", "adb" }; + +static char *usb_functions[] = { + "usb_mass_storage", +#ifdef CONFIG_USB_ANDROID_RNDIS + "rndis", +#endif +#ifdef CONFIG_USB_ANDROID_ACM + "acm", +#endif +}; + +static char *usb_functions_adb[] = { + "usb_mass_storage", + "adb", +#ifdef CONFIG_USB_ANDROID_RNDIS + "rndis", +#endif +#ifdef CONFIG_USB_ANDROID_ACM + "acm", +#endif +}; + +static char *usb_functions_all[] = { + "usb_mass_storage", + "adb", +#ifdef CONFIG_USB_ANDROID_ACM + "acm", +#endif +#ifdef CONFIG_USB_ANDROID_RNDIS + "rndis", +#endif +#ifdef CONFIG_USB_ANDROID_DIAG + "diag", +#endif +}; static struct android_usb_product usb_products[] = { { @@ -138,8 +171,8 @@ static struct android_usb_platform_data android_usb_pdata = { .manufacturer_name = "HTC", .num_products = ARRAY_SIZE(usb_products), .products = usb_products, - .num_functions = ARRAY_SIZE(usb_functions_adb), - .functions = usb_functions_adb, + .num_functions = ARRAY_SIZE(usb_functions_all), + .functions = usb_functions_all, }; static struct platform_device android_usb_device = { From 2c0be173feafd3839901635218f172b61af3b5c6 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Sun, 24 Jan 2010 16:21:26 -0500 Subject: [PATCH 0488/2556] [ARM] msm: Remove obsolete USB function support. Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/board-halibut.c | 59 -------------------- arch/arm/mach-msm/board-mahimahi.c | 75 -------------------------- arch/arm/mach-msm/board-swordfish.c | 58 -------------------- arch/arm/mach-msm/devices_htc.c | 15 ------ arch/arm/mach-msm/include/mach/board.h | 2 +- 5 files changed, 1 insertion(+), 208 deletions(-) diff --git a/arch/arm/mach-msm/board-halibut.c b/arch/arm/mach-msm/board-halibut.c index f9e0abdb8f208..0c37a36df6cd2 100644 --- a/arch/arm/mach-msm/board-halibut.c +++ b/arch/arm/mach-msm/board-halibut.c @@ -41,13 +41,7 @@ #include #include #include - -#ifdef CONFIG_USB_FUNCTION -#include -#endif -#ifdef CONFIG_USB_ANDROID #include -#endif #include "devices.h" #include "board-halibut.h" @@ -330,66 +324,18 @@ static struct platform_device android_pmem_camera_device = { .dev = { .platform_data = &android_pmem_camera_pdata }, }; -#ifdef CONFIG_USB_FUNCTION -static char *halibut_usb_functions[] = { -#if defined(CONFIG_USB_FUNCTION_MASS_STORAGE) || \ - defined(CONFIG_USB_FUNCTION_UMS) - "usb_mass_storage", -#endif -#ifdef CONFIG_USB_FUNCTION_ADB - "adb", -#endif -}; - -static struct msm_hsusb_product halibut_usb_products[] = { -{ -.product_id = 0x0c01, -.functions = 0x00000001, /* "usb_mass_storage" only */ -}, -{ -.product_id = 0x0c02, -.functions = 0x00000003, /* "usb_mass_storage" and "adb" */ -}, -}; -#endif - static int halibut_phy_init_seq[] = { 0x1D, 0x0D, 0x1D, 0x10, -1 }; static struct msm_hsusb_platform_data msm_hsusb_pdata = { .phy_init_seq = halibut_phy_init_seq, -#ifdef CONFIG_USB_FUNCTION - .vendor_id = 0x18d1, - .product_id = 0x0c02, - .version = 0x0100, - .product_name = "Halibut", - .serial_number = "42", - .manufacturer_name = "Qualcomm", - - .functions = halibut_usb_functions, - .num_functions = ARRAY_SIZE(halibut_usb_functions), - .products = halibut_usb_products, - .num_products = ARRAY_SIZE(halibut_usb_products), -#endif }; -#ifdef CONFIG_USB_FUNCTION_MASS_STORAGE static struct usb_mass_storage_platform_data mass_storage_pdata = { .nluns = 1, - .buf_size = 16384, .vendor = "Qualcomm", .product = "Halibut", .release = 0x0100, }; -#endif - -#ifdef CONFIG_USB_ANDROID -static struct usb_mass_storage_platform_data mass_storage_pdata = { - .nluns = 1, - .vendor = "Qualcomm", - .product = "Halibut", - .release = 0x0100, -}; -#endif static struct platform_device usb_mass_storage_device = { .name = "usb_mass_storage", @@ -399,7 +345,6 @@ static struct platform_device usb_mass_storage_device = { }, }; -#ifdef CONFIG_USB_ANDROID static char *usb_functions[] = { "usb_mass_storage" }; static char *usb_functions_adb[] = { "usb_mass_storage", "adb" }; @@ -436,8 +381,6 @@ static struct platform_device android_usb_device = { .platform_data = &android_usb_pdata, }, }; -#endif - static struct platform_device fish_battery_device = { .name = "fish_battery", @@ -451,9 +394,7 @@ static struct platform_device *devices[] __initdata = { &msm_device_nand, &msm_device_hsusb, &usb_mass_storage_device, -#ifdef CONFIG_USB_ANDROID &android_usb_device, -#endif &msm_device_i2c, &smc91x_device, &halibut_snd, diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 589510677196b..84114c50449d7 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -24,13 +24,7 @@ #include #include #include - -#ifdef CONFIG_USB_FUNCTION -#include -#endif -#ifdef CONFIG_USB_ANDROID #include -#endif #include #include @@ -71,42 +65,6 @@ extern int microp_headset_has_mic(void); static void config_gpio_table(uint32_t *table, int len); -#ifdef CONFIG_USB_FUNCTION -static char *mahimahi_usb_functions[] = { - "usb_mass_storage", - "adb", -#if defined(CONFIG_USB_FUNCTION_DIAG) - "diag", -#endif -#if defined(CONFIG_USB_FUNCTION_SERIAL) - "fserial", -#endif -}; - -static struct msm_hsusb_product mahimahi_usb_products[] = { - { - .product_id = 0x4e11, - .functions = 0x00000001, /* "usb_mass_storage" only */ - }, - { - .product_id = 0x4e12, - .functions = 0x00000003, /* "usb_mass_storage" and "adb" */ - }, - { - .product_id = 0x4e17, - .functions = 0x00000007, /* ums, adb, diag */ - }, - { - .product_id = 0x4e14, - .functions = 0x0000000b, /* ums, adb, fserial */ - }, - { - .product_id = 0x4e19, - .functions = 0x0000000f, /* ums, adb, diag, fserial */ - }, -}; -#endif - static int mahimahi_phy_init_seq[] = { 0x0C, 0x31, 0x31, 0x32, @@ -159,31 +117,8 @@ static struct msm_hsusb_platform_data msm_hsusb_pdata = { .phy_reset = mahimahi_usb_phy_reset, .hw_reset = mahimahi_usb_hw_reset, .usb_connected = notify_usb_connected, -#ifdef CONFIG_USB_FUNCTION - .vendor_id = 0x18d1, - .product_id = 0x4e12, - .version = 0x0100, - .product_name = "Nexus One", - .manufacturer_name = "Google, Inc.", - - .functions = mahimahi_usb_functions, - .num_functions = ARRAY_SIZE(mahimahi_usb_functions), - .products = mahimahi_usb_products, - .num_products = ARRAY_SIZE(mahimahi_usb_products), -#endif -}; - -#ifdef CONFIG_USB_FUNCTION -static struct usb_mass_storage_platform_data mass_storage_pdata = { - .nluns = 1, - .buf_size = 16384, - .vendor = "Google, Inc.", - .product = "Nexus One", - .release = 0x0100, }; -#endif -#ifdef CONFIG_USB_ANDROID static char *usb_functions[] = { "usb_mass_storage", #ifdef CONFIG_USB_ANDROID_RNDIS @@ -253,7 +188,6 @@ static struct usb_mass_storage_platform_data mass_storage_pdata = { .product = "Nexus One", .release = 0x0100, }; -#endif static struct platform_device usb_mass_storage_device = { .name = "usb_mass_storage", @@ -263,7 +197,6 @@ static struct platform_device usb_mass_storage_device = { }, }; -#ifdef CONFIG_USB_ANDROID static struct android_usb_platform_data android_usb_pdata = { .vendor_id = 0x18d1, .product_id = 0x4e11, @@ -283,7 +216,6 @@ static struct platform_device android_usb_device = { .platform_data = &android_usb_pdata, }, }; -#endif static struct platform_device mahimahi_rfkill = { .name = "mahimahi_rfkill", @@ -764,9 +696,7 @@ static struct platform_device *devices[] __initdata = { &msm_device_nand, &msm_device_hsusb, &usb_mass_storage_device, -#ifdef CONFIG_USB_ANDROID &android_usb_device, -#endif &android_pmem_mdp_device, &android_pmem_adsp_device, &android_pmem_camera_device, @@ -844,12 +774,7 @@ __tagtable(ATAG_BDADDR, parse_tag_bdaddr); static int __init board_serialno_setup(char *serialno) { -#ifdef CONFIG_USB_FUNCTION - msm_hsusb_pdata.serial_number = serialno; -#endif -#ifdef CONFIG_USB_ANDROID android_usb_pdata.serial_number = serialno; -#endif return 1; } __setup("androidboot.serialno=", board_serialno_setup); diff --git a/arch/arm/mach-msm/board-swordfish.c b/arch/arm/mach-msm/board-swordfish.c index ecd675474187e..3cbc23a72d22c 100644 --- a/arch/arm/mach-msm/board-swordfish.c +++ b/arch/arm/mach-msm/board-swordfish.c @@ -34,14 +34,7 @@ #include #include #include - -#ifdef CONFIG_USB_FUNCTION_MASS_STORAGE -#include -#endif - -#ifdef CONFIG_USB_ANDROID #include -#endif #include "board-swordfish.h" #include "devices.h" @@ -69,28 +62,6 @@ static struct platform_device smc91x_device = { .resource = smc91x_resources, }; -#ifdef CONFIG_USB_FUNCTION -static char *swordfish_usb_functions[] = { -#if defined(CONFIG_USB_FUNCTION_MASS_STORAGE) - "usb_mass_storage", -#endif -#ifdef CONFIG_USB_FUNCTION_ADB - "adb", -#endif -}; - -static struct msm_hsusb_product swordfish_usb_products[] = { - { - .product_id = 0x0d01, - .functions = 0x00000001, /* "usb_mass_storage" only */ - }, - { - .product_id = 0x0d02, - .functions = 0x00000003, /* "usb_mass_storage" and "adb" */ - }, -}; -#endif - static int swordfish_phy_init_seq[] = { 0x0C, 0x31, 0x1D, 0x0D, @@ -142,39 +113,14 @@ static struct msm_hsusb_platform_data msm_hsusb_pdata = { .phy_init_seq = swordfish_phy_init_seq, .phy_reset = swordfish_usb_phy_reset, .hw_reset = swordfish_usb_hw_reset, -#ifdef CONFIG_USB_FUNCTION - .vendor_id = 0x18d1, - .product_id = 0x0d02, - .version = 0x0100, - .product_name = "Swordfish", - .serial_number = "42", - .manufacturer_name = "Qualcomm", - - .functions = swordfish_usb_functions, - .num_functions = ARRAY_SIZE(swordfish_usb_functions), - .products = swordfish_usb_products, - .num_products = ARRAY_SIZE(swordfish_usb_products), -#endif -}; - -#ifdef CONFIG_USB_FUNCTION_MASS_STORAGE -static struct usb_mass_storage_platform_data mass_storage_pdata = { - .nluns = 1, - .buf_size = 16384, - .vendor = "Qualcomm", - .product = "Swordfish", - .release = 0x0100, }; -#endif -#ifdef CONFIG_USB_ANDROID static struct usb_mass_storage_platform_data mass_storage_pdata = { .nluns = 1, .vendor = "Qualcomm", .product = "Swordfish", .release = 0x0100, }; -#endif static struct platform_device usb_mass_storage_device = { .name = "usb_mass_storage", @@ -275,7 +221,6 @@ static struct platform_device android_pmem_gpu1_device = { }, }; -#ifdef CONFIG_USB_ANDROID static char *usb_functions[] = { "usb_mass_storage" }; static char *usb_functions_adb[] = { "usb_mass_storage", "adb" }; @@ -312,7 +257,6 @@ static struct platform_device android_usb_device = { .platform_data = &android_usb_pdata, }, }; -#endif static struct platform_device fish_battery_device = { .name = "fish_battery", @@ -337,9 +281,7 @@ static struct platform_device *devices[] __initdata = { &msm_device_nand, &msm_device_hsusb, &usb_mass_storage_device, -#ifdef CONFIG_USB_ANDROID &android_usb_device, -#endif &fish_battery_device, &smc91x_device, &msm_device_touchscreen, diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c index 5130e12a5aaff..7801570cc5025 100644 --- a/arch/arm/mach-msm/devices_htc.c +++ b/arch/arm/mach-msm/devices_htc.c @@ -25,13 +25,7 @@ #include #include #include - -#ifdef CONFIG_USB_FUNCTION -#include -#endif -#ifdef CONFIG_USB_ANDROID #include -#endif #include #include @@ -96,14 +90,12 @@ struct msm_hsusb_platform_data msm_hsusb_pdata = { .usb_connected = notify_usb_connected, }; -#ifdef CONFIG_USB_ANDROID static struct usb_mass_storage_platform_data mass_storage_pdata = { .nluns = 1, .vendor = "HTC ", .product = "Android Phone ", .release = 0x0100, }; -#endif static struct platform_device usb_mass_storage_device = { .name = "usb_mass_storage", @@ -113,8 +105,6 @@ static struct platform_device usb_mass_storage_device = { }, }; -#ifdef CONFIG_USB_ANDROID - static char *usb_functions[] = { "usb_mass_storage", #ifdef CONFIG_USB_ANDROID_RNDIS @@ -182,7 +172,6 @@ static struct platform_device android_usb_device = { .platform_data = &android_usb_pdata, }, }; -#endif void __init msm_add_usb_devices(void (*phy_reset) (void)) { @@ -192,9 +181,7 @@ void __init msm_add_usb_devices(void (*phy_reset) (void)) msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; platform_device_register(&msm_device_hsusb); platform_device_register(&usb_mass_storage_device); -#ifdef CONFIG_USB_ANDROID platform_device_register(&android_usb_device); -#endif } static struct android_pmem_platform_data pmem_pdata = { @@ -467,9 +454,7 @@ static int __init board_serialno_setup(char *serialno) str = df_serialno; else str = serialno; -#ifdef CONFIG_USB_ANDROID android_usb_pdata.serial_number = str; -#endif return 1; } diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index 68291b6a8fcaa..6a075c70298c4 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -91,7 +91,7 @@ int __init msm_add_sdcc(unsigned int controller, struct msm_mmc_platform_data *plat, unsigned int stat_irq, unsigned long stat_irq_flags); -#if defined(CONFIG_USB_FUNCTION_MSM_HSUSB) || defined(CONFIG_USB_MSM_72K) +#ifdef CONFIG_USB_MSM_72K void msm_hsusb_set_vbus_state(int online); #else static inline void msm_hsusb_set_vbus_state(int online) {} From 3d8b214c444df517731442b1de361972ed13b890 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 25 Jan 2010 15:12:03 -0500 Subject: [PATCH 0489/2556] [ARM] msm: mahimahi: Enable ACM serial support in defconfig Use different USB PIDs when ACM is enabled to avoid confusion on Windows host. Change-Id: I27c01394fd385b355325b8322fc86d51dd59b802 Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/board-mahimahi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 84114c50449d7..47745a9b2b6a0 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -164,12 +164,20 @@ static char *usb_functions_all[] = { static struct android_usb_product usb_products[] = { { +#ifdef CONFIG_USB_ANDROID_ACM + .product_id = 0x4e21, +#else .product_id = 0x4e11, +#endif .num_functions = ARRAY_SIZE(usb_functions), .functions = usb_functions, }, { +#ifdef CONFIG_USB_ANDROID_ACM + .product_id = 0x4e22, +#else .product_id = 0x4e12, +#endif .num_functions = ARRAY_SIZE(usb_functions_adb), .functions = usb_functions_adb, }, From 39dc133c8c169bb5cc8b88aae02146b60ba05e40 Mon Sep 17 00:00:00 2001 From: Haley Teng Date: Tue, 15 Dec 2009 05:58:56 -0800 Subject: [PATCH 0490/2556] [ARM] qsd8k: mahimahi: driver for tpa2018d1 (speaker amplifier) Signed-off-by: Haley Teng Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/Makefile | 2 + arch/arm/mach-msm/board-mahimahi-tpa2018d1.c | 160 +++++++++++++++++++ arch/arm/mach-msm/board-mahimahi-tpa2018d1.h | 38 +++++ 3 files changed, 200 insertions(+) create mode 100644 arch/arm/mach-msm/board-mahimahi-tpa2018d1.c create mode 100644 arch/arm/mach-msm/board-mahimahi-tpa2018d1.h diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index bc4fb531244d0..79d7cd8ae5764 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -88,3 +88,5 @@ obj-$(CONFIG_HTC_PWRSINK) += htc_pwrsink.o obj-$(CONFIG_HTC_PWRSPLY) += htc_power_supply.o obj-$(CONFIG_HTC_HEADSET) += htc_headset.o obj-$(CONFIG_HTC_35MM_JACK) += htc_35mm_jack.o + +obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-tpa2018d1.o diff --git a/arch/arm/mach-msm/board-mahimahi-tpa2018d1.c b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.c new file mode 100644 index 0000000000000..851082fa29837 --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.c @@ -0,0 +1,160 @@ +/* drivers/i2c/chips/tpa2018d1.c + * + * TI TPA2018D1 Speaker Amplifier + * + * Copyright (C) 2009 HTC Corporation + * + * 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. + * + */ + +/* TODO: content validation in TPA2018_SET_CONFIG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-mahimahi-tpa2018d1.h" + +static struct i2c_client *this_client; +static struct tpa2018d1_platform_data *pdata; +static int is_on; +static const char spk_amp_on[] = { + 0x01, 0xc3, 0x20, 0x01, 0x00, 0x08, 0x1a, 0x21 +}; +static const char spk_amp_off[] = {0x01, 0xa2}; + +static int tpa2018_i2c_write(const char *txData, int length) +{ + struct i2c_msg msg[] = { + { + .addr = this_client->addr, + .flags = 0, + .len = length, + .buf = txData, + }, + }; + + if (i2c_transfer(this_client->adapter, msg, 1) < 0) { + pr_err("%s: I2C transfer error\n", __func__); + return -EIO; + } else + return 0; +} + +void tpa2018d1_set_speaker_amp(int on) +{ + if (!pdata) { + pr_err("%s: no platform data!\n", __func__); + return; + } + if (on && !is_on) { + gpio_set_value(pdata->gpio_tpa2018_spk_en, 1); + mdelay(5); /* According to TPA2018D1 Spec */ + + if (tpa2018_i2c_write(spk_amp_on, sizeof(spk_amp_on)) == 0) { + is_on = 1; + pr_info("%s: ON\n", __func__); + } + } else if (!on && is_on) { + if (tpa2018_i2c_write(spk_amp_off, sizeof(spk_amp_off)) == 0) { + is_on = 0; + mdelay(2); + gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); + pr_info("%s: OFF\n", __func__); + } + } +} + +static int tpa2018d1_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + + pdata = client->dev.platform_data; + + if (!pdata) { + ret = -EINVAL; + pr_err("%s: platform data is NULL\n", __func__); + goto err_no_pdata; + } + + this_client = client; + + ret = gpio_request(pdata->gpio_tpa2018_spk_en, "tpa2018"); + if (ret < 0) { + pr_err("%s: gpio request aud_spk_en pin failed\n", __func__); + goto err_free_gpio; + } + + ret = gpio_direction_output(pdata->gpio_tpa2018_spk_en, 1); + if (ret < 0) { + pr_err("%s: request aud_spk_en gpio direction failed\n", + __func__); + goto err_free_gpio; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s: i2c check functionality error\n", __func__); + ret = -ENODEV; + goto err_free_gpio; + } + + gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); /* Default Low */ + + return 0; + +err_free_gpio: + gpio_free(pdata->gpio_tpa2018_spk_en); +err_no_pdata: + return ret; +} + +static int tpa2018d1_suspend(struct i2c_client *client, pm_message_t mesg) +{ + return 0; +} + +static int tpa2018d1_resume(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id tpa2018d1_id[] = { + { TPA2018D1_I2C_NAME, 0 }, + { } +}; + +static struct i2c_driver tpa2018d1_driver = { + .probe = tpa2018d1_probe, + .suspend = tpa2018d1_suspend, + .resume = tpa2018d1_resume, + .id_table = tpa2018d1_id, + .driver = { + .name = TPA2018D1_I2C_NAME, + }, +}; + +static int __init tpa2018d1_init(void) +{ + pr_info("%s\n", __func__); + return i2c_add_driver(&tpa2018d1_driver); +} + +module_init(tpa2018d1_init); + +MODULE_DESCRIPTION("tpa2018d1 speaker amp driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-mahimahi-tpa2018d1.h b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.h new file mode 100644 index 0000000000000..2351f396c237b --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.h @@ -0,0 +1,38 @@ +/* include/linux/tpa2018d1.h - tpa2018d1 speaker amplifier driver + * + * Copyright (C) 2009 HTC Corporation. + * + * 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_TPA2018D1_H +#define _LINUX_TPA2018D1_H + +#include + +#ifdef __KERNEL__ + +#define TPA2018D1_I2C_NAME "tpa2018d1" + +struct tpa2018d1_platform_data { + uint32_t gpio_tpa2018_spk_en; +}; + +#endif /* __KERNEL__ */ + +#define TPA2018_IOCTL_MAGIC 'a' +#define TPA2018_SET_CONFIG _IOW(TPA2018_IOCTL_MAGIC, 0x01, unsigned) +#define TPA2018_READ_CONFIG _IOR(TPA2018_IOCTL_MAGIC, 0x02, unsigned) + +extern void tpa2018d1_set_speaker_amp(int on); + +#endif + From 05b3147400185708f4e5bee0d88b17a77f9664a5 Mon Sep 17 00:00:00 2001 From: HK Chen Date: Tue, 2 Feb 2010 11:09:51 -0800 Subject: [PATCH 0491/2556] [ARM] qsd8k: mahimahi: register as a misc. device for dynamic configuration -- Provide the following ioctls, which can override the defaults and retrieve current configuration: TPA2018_SET_CONFIG TPA2018_READ_CONFIG TPA2018_SET_MODE TPA2018_SET_PARAM -- Add a public header tpa2018d1.h for the ioctl()s Change-Id: I49b3bf4c88fa441d282ee6f19c2307a30a07a87f Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi-tpa2018d1.c | 216 ++++++++++++++++++- arch/arm/mach-msm/board-mahimahi-tpa2018d1.h | 21 +- include/linux/tpa2018d1.h | 36 ++++ 3 files changed, 257 insertions(+), 16 deletions(-) create mode 100644 include/linux/tpa2018d1.h diff --git a/arch/arm/mach-msm/board-mahimahi-tpa2018d1.c b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.c index 851082fa29837..78919b9b19543 100644 --- a/arch/arm/mach-msm/board-mahimahi-tpa2018d1.c +++ b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.c @@ -27,17 +27,27 @@ #include #include #include +#include +#include #include "board-mahimahi-tpa2018d1.h" static struct i2c_client *this_client; static struct tpa2018d1_platform_data *pdata; static int is_on; -static const char spk_amp_on[] = { +static char spk_amp_cfg[8]; +static const char spk_amp_on[8] = { /* same length as spk_amp_cfg */ 0x01, 0xc3, 0x20, 0x01, 0x00, 0x08, 0x1a, 0x21 }; static const char spk_amp_off[] = {0x01, 0xa2}; +static DEFINE_MUTEX(spk_amp_lock); +static int tpa2018d1_opened; +static char *config_data; +static int tpa2018d1_num_modes; + +#define DEBUG 0 + static int tpa2018_i2c_write(const char *txData, int length) { struct i2c_msg msg[] = { @@ -56,28 +66,220 @@ static int tpa2018_i2c_write(const char *txData, int length) return 0; } +static int tpa2018_i2c_read(char *rxData, int length) +{ + struct i2c_msg msgs[] = { + { + .addr = this_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = rxData, + }, + }; + + if (i2c_transfer(this_client->adapter, msgs, 1) < 0) { + pr_err("%s: I2C transfer error\n", __func__); + return -EIO; + } + +#if DEBUG + do { + int i = 0; + for (i = 0; i < length; i++) + pr_info("%s: rx[%d] = %2x\n", + __func__, i, rxData[i]); + } while(0); +#endif + + return 0; +} + +static int tpa2018d1_open(struct inode *inode, struct file *file) +{ + int rc = 0; + + mutex_lock(&spk_amp_lock); + + if (tpa2018d1_opened) { + pr_err("%s: busy\n", __func__); + rc = -EBUSY; + goto done; + } + + tpa2018d1_opened = 1; +done: + mutex_unlock(&spk_amp_lock); + return rc; +} + +static int tpa2018d1_release(struct inode *inode, struct file *file) +{ + mutex_lock(&spk_amp_lock); + tpa2018d1_opened = 0; + mutex_unlock(&spk_amp_lock); + + return 0; +} + +static int tpa2018d1_read_config(void __user *argp) +{ + int rc = 0; + unsigned char reg_idx = 0x01; + unsigned char tmp[7]; + + if (!is_on) { + gpio_set_value(pdata->gpio_tpa2018_spk_en, 1); + msleep(5); /* According to TPA2018D1 Spec */ + } + + rc = tpa2018_i2c_write(®_idx, sizeof(reg_idx)); + if (rc < 0) + goto err; + + rc = tpa2018_i2c_read(tmp, sizeof(tmp)); + if (rc < 0) + goto err; + + if (copy_to_user(argp, &tmp, sizeof(tmp))) + rc = -EFAULT; + +err: + if (!is_on) + gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); + return rc; +} + +static int tpa2018d1_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int rc = 0; + int mode = -1; + int offset = 0; + struct tpa2018d1_config_data cfg; + + mutex_lock(&spk_amp_lock); + + switch (cmd) { + case TPA2018_SET_CONFIG: + if (copy_from_user(spk_amp_cfg, argp, sizeof(spk_amp_cfg))) + rc = -EFAULT; + break; + + case TPA2018_READ_CONFIG: + rc = tpa2018d1_read_config(argp); + break; + + case TPA2018_SET_MODE: + if (copy_from_user(&mode, argp, sizeof(mode))) { + rc = -EFAULT; + break; + } + if (mode >= tpa2018d1_num_modes || mode < 0) { + pr_err("%s: unsupported tpa2018d1 mode %d\n", + __func__, mode); + rc = -EINVAL; + break; + } + if (!config_data) { + pr_err("%s: no config data!\n", __func__); + rc = -EIO; + break; + } + memcpy(spk_amp_cfg, config_data + mode * TPA2018D1_CMD_LEN, + TPA2018D1_CMD_LEN); + break; + + case TPA2018_SET_PARAM: + if (copy_from_user(&cfg, argp, sizeof(cfg))) { + pr_err("%s: copy from user failed.\n", __func__); + rc = -EFAULT; + break; + } + tpa2018d1_num_modes = cfg.mode_num; + if (tpa2018d1_num_modes > TPA2018_NUM_MODES) { + pr_err("%s: invalid number of modes %d\n", __func__, + tpa2018d1_num_modes); + rc = -EINVAL; + break; + } + if (cfg.data_len != tpa2018d1_num_modes*TPA2018D1_CMD_LEN) { + pr_err("%s: invalid data length %d, expecting %d\n", + __func__, cfg.data_len, + tpa2018d1_num_modes * TPA2018D1_CMD_LEN); + rc = -EINVAL; + break; + } + /* Free the old data */ + if (config_data) + kfree(config_data); + config_data = kmalloc(cfg.data_len, GFP_KERNEL); + if (!config_data) { + pr_err("%s: out of memory\n", __func__); + rc = -ENOMEM; + break; + } + if (copy_from_user(config_data, cfg.cmd_data, cfg.data_len)) { + pr_err("%s: copy data from user failed.\n", __func__); + kfree(config_data); + config_data = NULL; + rc = -EFAULT; + break; + } + /* replace default setting with playback setting */ + if (tpa2018d1_num_modes >= TPA2018_MODE_PLAYBACK) { + offset = TPA2018_MODE_PLAYBACK * TPA2018D1_CMD_LEN; + memcpy(spk_amp_cfg, config_data + offset, + TPA2018D1_CMD_LEN); + } + break; + + default: + pr_err("%s: invalid command %d\n", __func__, _IOC_NR(cmd)); + rc = -EINVAL; + break; + } + mutex_unlock(&spk_amp_lock); + return rc; +} + +static struct file_operations tpa2018d1_fops = { + .owner = THIS_MODULE, + .open = tpa2018d1_open, + .release = tpa2018d1_release, + .ioctl = tpa2018d1_ioctl, +}; + +static struct miscdevice tpa2018d1_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "tpa2018d1", + .fops = &tpa2018d1_fops, +}; + void tpa2018d1_set_speaker_amp(int on) { if (!pdata) { pr_err("%s: no platform data!\n", __func__); return; } + mutex_lock(&spk_amp_lock); if (on && !is_on) { gpio_set_value(pdata->gpio_tpa2018_spk_en, 1); - mdelay(5); /* According to TPA2018D1 Spec */ + msleep(5); /* According to TPA2018D1 Spec */ - if (tpa2018_i2c_write(spk_amp_on, sizeof(spk_amp_on)) == 0) { + if (tpa2018_i2c_write(spk_amp_cfg, sizeof(spk_amp_cfg)) == 0) { is_on = 1; pr_info("%s: ON\n", __func__); } } else if (!on && is_on) { if (tpa2018_i2c_write(spk_amp_off, sizeof(spk_amp_off)) == 0) { is_on = 0; - mdelay(2); + msleep(2); gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); pr_info("%s: OFF\n", __func__); } } + mutex_unlock(&spk_amp_lock); } static int tpa2018d1_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -115,6 +317,12 @@ static int tpa2018d1_probe(struct i2c_client *client, const struct i2c_device_id gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); /* Default Low */ + ret = misc_register(&tpa2018d1_device); + if (ret) { + pr_err("%s: tpa2018d1_device register failed\n", __func__); + goto err_free_gpio; + } + memcpy(spk_amp_cfg, spk_amp_on, sizeof(spk_amp_on)); return 0; err_free_gpio: diff --git a/arch/arm/mach-msm/board-mahimahi-tpa2018d1.h b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.h index 2351f396c237b..dc11012209454 100644 --- a/arch/arm/mach-msm/board-mahimahi-tpa2018d1.h +++ b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.h @@ -13,26 +13,23 @@ * */ -#ifndef _LINUX_TPA2018D1_H -#define _LINUX_TPA2018D1_H -#include - -#ifdef __KERNEL__ +#ifndef __ASM_ARM_ARCH_TPA2018D1_H +#define __ASM_ARM_ARCH_TPA2018D1_H #define TPA2018D1_I2C_NAME "tpa2018d1" +#define TPA2018D1_CMD_LEN 8 struct tpa2018d1_platform_data { uint32_t gpio_tpa2018_spk_en; }; -#endif /* __KERNEL__ */ - -#define TPA2018_IOCTL_MAGIC 'a' -#define TPA2018_SET_CONFIG _IOW(TPA2018_IOCTL_MAGIC, 0x01, unsigned) -#define TPA2018_READ_CONFIG _IOR(TPA2018_IOCTL_MAGIC, 0x02, unsigned) +struct tpa2018d1_config_data { + unsigned char *cmd_data; /* [mode][cmd_len][cmds..] */ + unsigned int mode_num; + unsigned int data_len; +}; extern void tpa2018d1_set_speaker_amp(int on); -#endif - +#endif /* __ASM_ARM_ARCH_TPA2018D1_H */ diff --git a/include/linux/tpa2018d1.h b/include/linux/tpa2018d1.h new file mode 100644 index 0000000000000..26f608bca1e6c --- /dev/null +++ b/include/linux/tpa2018d1.h @@ -0,0 +1,36 @@ +/* include/linux/tpa2018d1.h - tpa2018d1 speaker amplifier driver + * + * Copyright (C) 2009 HTC Corporation. + * + * 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_TPA2018D1_H +#define _LINUX_TPA2018D1_H + +#include + +enum tpa2018d1_mode { + TPA2018_MODE_OFF, + TPA2018_MODE_PLAYBACK, + TPA2018_MODE_RINGTONE, + TPA2018_MODE_VOICE_CALL, + TPA2018_NUM_MODES, +}; + +#define TPA2018_IOCTL_MAGIC 'a' +#define TPA2018_SET_CONFIG _IOW(TPA2018_IOCTL_MAGIC, 1, unsigned) +#define TPA2018_READ_CONFIG _IOR(TPA2018_IOCTL_MAGIC, 2, unsigned) +#define TPA2018_SET_PARAM _IOW(TPA2018_IOCTL_MAGIC, 3, unsigned) +#define TPA2018_SET_MODE _IOW(TPA2018_IOCTL_MAGIC, 4, unsigned) + +#endif + From 00ff04a09a62cbcd15f7d7da7588dc65c2016c80 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 19 Jan 2010 10:53:10 -0800 Subject: [PATCH 0492/2556] [ARM] qsd8k: mahimahi: add macro is_cdma_version() Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/mach-msm/board-mahimahi.h b/arch/arm/mach-msm/board-mahimahi.h index a858a3371c1f7..ca9caeef2a23f 100644 --- a/arch/arm/mach-msm/board-mahimahi.h +++ b/arch/arm/mach-msm/board-mahimahi.h @@ -120,4 +120,7 @@ #define MAHIMAHI_GPIO_DOCK 106 + +#define is_cdma_version(rev) (((rev) & 0xF0) == 0xC0) + #endif /* __ARCH_ARM_MACH_MSM_BOARD_MAHIMAHI_H */ From 9404712416981b45e80d99d045c527c597bf44a0 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 19 Jan 2010 11:05:31 -0800 Subject: [PATCH 0493/2556] [ARM] qsd8k: mahimahi: add board support for tpa2018d1 on cdma revs Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi-audio.c | 4 ++++ arch/arm/mach-msm/board-mahimahi.c | 18 ++++++++++++++++++ arch/arm/mach-msm/board-mahimahi.h | 2 ++ 3 files changed, 24 insertions(+) diff --git a/arch/arm/mach-msm/board-mahimahi-audio.c b/arch/arm/mach-msm/board-mahimahi-audio.c index fdc334beb1410..1ad93ad1396e8 100644 --- a/arch/arm/mach-msm/board-mahimahi-audio.c +++ b/arch/arm/mach-msm/board-mahimahi-audio.c @@ -22,6 +22,7 @@ #include "board-mahimahi.h" #include "proc_comm.h" #include "pmic.h" +#include "board-mahimahi-tpa2018d1.h" #if 0 #define D(fmt, args...) printk(KERN_INFO "Audio: "fmt, ##args) @@ -67,6 +68,9 @@ void mahimahi_speaker_enable(int en) pmic_set_spkr_configuration(&scm); } + + if (is_cdma_version(system_rev)) + tpa2018d1_set_speaker_amp(en); } void mahimahi_receiver_enable(int en) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 47745a9b2b6a0..55f0b2377215b 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -52,6 +52,7 @@ #include "devices.h" #include "proc_comm.h" #include "board-mahimahi-flashlight.h" +#include "board-mahimahi-tpa2018d1.h" static uint debug_uart; @@ -452,6 +453,10 @@ static void ds2482_set_slp_n(unsigned n) gpio_direction_output(MAHIMAHI_GPIO_DS2482_SLP_N, n); } +static struct tpa2018d1_platform_data tpa2018_data = { + .gpio_tpa2018_spk_en = MAHIMAHI_CDMA_GPIO_AUD_SPK_AMP_EN, +}; + static struct i2c_board_info base_i2c_devices[] = { { I2C_BOARD_INFO("ds2482", 0x30 >> 1), @@ -501,6 +506,13 @@ static struct i2c_board_info rev1_i2c_devices[] = { }, }; +static struct i2c_board_info rev_CX_i2c_devices[] = { + { + I2C_BOARD_INFO("tpa2018d1", 0x58), + .platform_data = &tpa2018_data, + }, +}; + static void config_gpio_table(uint32_t *table, int len); static uint32_t camera_off_gpio_table[] = { @@ -904,6 +916,12 @@ static void __init mahimahi_init(void) ARRAY_SIZE(rev1_i2c_devices)); } + if (is_cdma_version(system_rev)) { + /* Only CDMA version with TI TPA2018D1 Speaker Amp. */ + i2c_register_board_info(0, rev_CX_i2c_devices, + ARRAY_SIZE(rev_CX_i2c_devices)); + } + ret = mahimahi_init_mmc(system_rev, debug_uart); if (ret != 0) pr_crit("%s: Unable to initialize MMC\n", __func__); diff --git a/arch/arm/mach-msm/board-mahimahi.h b/arch/arm/mach-msm/board-mahimahi.h index ca9caeef2a23f..ccfa015d460ff 100644 --- a/arch/arm/mach-msm/board-mahimahi.h +++ b/arch/arm/mach-msm/board-mahimahi.h @@ -120,6 +120,8 @@ #define MAHIMAHI_GPIO_DOCK 106 +/* speaker amplifier enable pin for mahimahi CDMA version */ +#define MAHIMAHI_CDMA_GPIO_AUD_SPK_AMP_EN 104 #define is_cdma_version(rev) (((rev) & 0xF0) == 0xC0) From 50ece534928a6121d410a7628500285a8c4fde2f Mon Sep 17 00:00:00 2001 From: Haley Teng Date: Mon, 25 Jan 2010 22:20:20 -0800 Subject: [PATCH 0494/2556] [ARM] qsd8k: mahimahi: driver for SUMMIT Micro SMB329B switch charger Signed-off-by: Haley Teng Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/board-mahimahi-smb329.c | 177 ++++++++++++++++++++++ arch/arm/mach-msm/board-mahimahi-smb329.h | 32 ++++ 3 files changed, 210 insertions(+) create mode 100755 arch/arm/mach-msm/board-mahimahi-smb329.c create mode 100644 arch/arm/mach-msm/board-mahimahi-smb329.h diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 79d7cd8ae5764..415f94c33705b 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -90,3 +90,4 @@ obj-$(CONFIG_HTC_HEADSET) += htc_headset.o obj-$(CONFIG_HTC_35MM_JACK) += htc_35mm_jack.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-tpa2018d1.o +obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-smb329.o diff --git a/arch/arm/mach-msm/board-mahimahi-smb329.c b/arch/arm/mach-msm/board-mahimahi-smb329.c new file mode 100755 index 0000000000000..b80db78491e19 --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-smb329.c @@ -0,0 +1,177 @@ +/* drivers/i2c/chips/smb329.c + * + * SMB329B Switch Charger (SUMMIT Microelectronics) + * + * Copyright (C) 2009 HTC Corporation + * Author: Justin Lin + * + * 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 "board-mahimahi-smb329.h" + +static struct smb329_data { + struct i2c_client *client; + uint8_t version; + struct work_struct work; + struct mutex state_lock; + int chg_state; +} smb329; + +static int smb329_i2c_write(uint8_t *value, uint8_t reg, uint8_t num_bytes) +{ + int ret; + struct i2c_msg msg; + + /* write the first byte of buffer as the register address */ + value[0] = reg; + msg.addr = smb329.client->addr; + msg.len = num_bytes + 1; + msg.flags = 0; + msg.buf = value; + + ret = i2c_transfer(smb329.client->adapter, &msg, 1); + + return (ret >= 0) ? 0 : ret; +} + +static int smb329_i2c_read(uint8_t *value, uint8_t reg, uint8_t num_bytes) +{ + int ret; + struct i2c_msg msg[2]; + + /* setup the address to read */ + msg[0].addr = smb329.client->addr; + msg[0].len = 1; + msg[0].flags = 0; + msg[0].buf = ® + + /* setup the read buffer */ + msg[1].addr = smb329.client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = num_bytes; + msg[1].buf = value; + + ret = i2c_transfer(smb329.client->adapter, msg, 2); + + return (ret >= 0) ? 0 : ret; +} + +static int smb329_i2c_write_byte(uint8_t value, uint8_t reg) +{ + int ret; + uint8_t buf[2] = { 0 }; + + buf[1] = value; + ret = smb329_i2c_write(buf, reg, 1); + if (ret) + pr_err("smb329: write byte error (%d)\n", ret); + + return ret; +} + +static int smb329_i2c_read_byte(uint8_t *value, uint8_t reg) +{ + int ret = smb329_i2c_read(value, reg, 1); + if (ret) + pr_err("smb329: read byte error (%d)\n", ret); + + return ret; +} + +int smb329_set_charger_ctrl(uint32_t ctl) +{ + mutex_lock(&smb329.state_lock); + smb329.chg_state = ctl; + schedule_work(&smb329.work); + mutex_unlock(&smb329.state_lock); + return 0; +} + +static void smb329_work_func(struct work_struct *work) +{ + mutex_lock(&smb329.state_lock); + + switch (smb329.chg_state) { + case SMB329_ENABLE_FAST_CHG: + pr_info("smb329: charger on (fast)\n"); + smb329_i2c_write_byte(0x84, 0x31); + smb329_i2c_write_byte(0x08, 0x05); + if ((smb329.version & 0x18) == 0x0) + smb329_i2c_write_byte(0xA9, 0x00); + break; + + case SMB329_DISABLE_CHG: + case SMB329_ENABLE_SLOW_CHG: + pr_info("smb329: charger off/slow\n"); + smb329_i2c_write_byte(0x88, 0x31); + smb329_i2c_write_byte(0x08, 0x05); + break; + default: + pr_err("smb329: unknown charger state %d\n", + smb329.chg_state); + } + + mutex_unlock(&smb329.state_lock); +} + +static int smb329_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { + dev_dbg(&client->dev, "[SMB329]:I2C fail\n"); + return -EIO; + } + + smb329.client = client; + mutex_init(&smb329.state_lock); + INIT_WORK(&smb329.work, smb329_work_func); + + smb329_i2c_read_byte(&smb329.version, 0x3B); + pr_info("smb329 version: 0x%02x\n", smb329.version); + + return 0; +} + +static const struct i2c_device_id smb329_id[] = { + { "smb329", 0 }, + { }, +}; + +static struct i2c_driver smb329_driver = { + .driver.name = "smb329", + .id_table = smb329_id, + .probe = smb329_probe, +}; + +static int __init smb329_init(void) +{ + int ret = i2c_add_driver(&smb329_driver); + if (ret) + pr_err("smb329_init: failed\n"); + + return ret; +} + +module_init(smb329_init); + +MODULE_AUTHOR("Justin Lin "); +MODULE_DESCRIPTION("SUMMIT Microelectronics SMB329B switch charger"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-mahimahi-smb329.h b/arch/arm/mach-msm/board-mahimahi-smb329.h new file mode 100644 index 0000000000000..13b326fa71dfa --- /dev/null +++ b/arch/arm/mach-msm/board-mahimahi-smb329.h @@ -0,0 +1,32 @@ +/* include/linux/smb329.h - smb329 switch charger driver + * + * Copyright (C) 2009 HTC Corporation. + * + * 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_SMB329_H +#define _LINUX_SMB329_H + +#ifdef __KERNEL__ + +enum { + SMB329_DISABLE_CHG, + SMB329_ENABLE_SLOW_CHG, + SMB329_ENABLE_FAST_CHG, +}; + +extern int smb329_set_charger_ctrl(uint32_t ctl); + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_SMB329_H */ + From 016d01a0fd74de04b4be425aa5aacbd13c0173e7 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Fri, 22 Jan 2010 14:51:03 -0800 Subject: [PATCH 0495/2556] [ARM] qsd8k: mahimahi: add board support for ds2784_battery Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi.c | 68 ++++++++++++++++++++++++++++++ arch/arm/mach-msm/board-mahimahi.h | 4 ++ 2 files changed, 72 insertions(+) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 55f0b2377215b..5431f1769254d 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -32,7 +32,9 @@ #include #include #include +#include #include <../../../drivers/staging/android/timed_gpio.h> +#include <../../../drivers/w1/w1.h> #include #include @@ -704,6 +706,70 @@ struct platform_device bcm_bt_lpm_device = { }, }; +static int ds2784_charge(int on, int fast) +{ + gpio_direction_output(MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT, !!fast); + gpio_direction_output(MAHIMAHI_GPIO_BATTERY_CHARGER_EN, !on); + return 0; +} + +static int w1_ds2784_add_slave(struct w1_slave *sl) +{ + struct dd { + struct platform_device pdev; + struct ds2784_platform_data pdata; + } *p; + + int rc; + + rc = gpio_request(MAHIMAHI_GPIO_BATTERY_CHARGER_EN, "charger_en"); + if (rc < 0) { + pr_err("%s: gpio_request(%d) failed: %d\n", __func__, + MAHIMAHI_GPIO_BATTERY_CHARGER_EN, rc); + return rc; + } + + rc = gpio_request(MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT, "charger_current"); + if (rc < 0) { + pr_err("%s: gpio_request(%d) failed: %d\n", __func__, + MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT, rc); + gpio_free(MAHIMAHI_GPIO_BATTERY_CHARGER_EN); + return rc; + } + + p = kzalloc(sizeof(struct dd), GFP_KERNEL); + if (!p) { + pr_err("%s: out of memory\n", __func__); + gpio_free(MAHIMAHI_GPIO_BATTERY_CHARGER_EN); + gpio_free(MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT); + return -ENOMEM; + } + + p->pdev.name = "ds2784-battery"; + p->pdev.id = -1; + p->pdev.dev.platform_data = &p->pdata; + p->pdata.charge = ds2784_charge; + p->pdata.w1_slave = sl; + + platform_device_register(&p->pdev); + + return 0; +} + +static struct w1_family_ops w1_ds2784_fops = { + .add_slave = w1_ds2784_add_slave, +}; + +static struct w1_family w1_ds2784_family = { + .fid = W1_FAMILY_DS2784, + .fops = &w1_ds2784_fops, +}; + +static int __init ds2784_battery_init(void) +{ + return w1_register_family(&w1_ds2784_family); +} + static struct platform_device *devices[] __initdata = { #if !defined(CONFIG_MSM_SERIAL_DEBUGGER) &msm_device_uart1, @@ -940,6 +1006,8 @@ static void __init mahimahi_init(void) platform_device_register(&mahimahi_timed_gpios); else msm_init_pmic_vibrator(); + + ds2784_battery_init(); } static void __init mahimahi_fixup(struct machine_desc *desc, struct tag *tags, diff --git a/arch/arm/mach-msm/board-mahimahi.h b/arch/arm/mach-msm/board-mahimahi.h index ccfa015d460ff..50097bf66ca14 100644 --- a/arch/arm/mach-msm/board-mahimahi.h +++ b/arch/arm/mach-msm/board-mahimahi.h @@ -123,6 +123,10 @@ /* speaker amplifier enable pin for mahimahi CDMA version */ #define MAHIMAHI_CDMA_GPIO_AUD_SPK_AMP_EN 104 +#define MAHIMAHI_GPIO_BATTERY_DETECTION 39 +#define MAHIMAHI_GPIO_BATTERY_CHARGER_EN 22 +#define MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT 16 + #define is_cdma_version(rev) (((rev) & 0xF0) == 0xC0) #endif /* __ARCH_ARM_MACH_MSM_BOARD_MAHIMAHI_H */ From 111caacaddf90b7b6f6cde796708fc1aef652918 Mon Sep 17 00:00:00 2001 From: Haley Teng Date: Tue, 15 Dec 2009 12:36:18 -0800 Subject: [PATCH 0496/2556] [ARM] qsd8k: mahimahi: add CDMA-board support for smb329 switch charger Signed-off-by: Haley Teng Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi.c | 43 +++++++++++++++++++----------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 5431f1769254d..cb2dc9134114a 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -55,6 +55,7 @@ #include "proc_comm.h" #include "board-mahimahi-flashlight.h" #include "board-mahimahi-tpa2018d1.h" +#include "board-mahimahi-smb329.h" static uint debug_uart; @@ -513,6 +514,9 @@ static struct i2c_board_info rev_CX_i2c_devices[] = { I2C_BOARD_INFO("tpa2018d1", 0x58), .platform_data = &tpa2018_data, }, + { + I2C_BOARD_INFO("smb329", 0x6E >> 1), + }, }; static void config_gpio_table(uint32_t *table, int len); @@ -708,7 +712,14 @@ struct platform_device bcm_bt_lpm_device = { static int ds2784_charge(int on, int fast) { - gpio_direction_output(MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT, !!fast); + if (is_cdma_version(system_rev)) { + if (!on) + smb329_set_charger_ctrl(SMB329_DISABLE_CHG); + else + smb329_set_charger_ctrl(fast ? SMB329_ENABLE_FAST_CHG : SMB329_ENABLE_SLOW_CHG); + } + else + gpio_direction_output(MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT, !!fast); gpio_direction_output(MAHIMAHI_GPIO_BATTERY_CHARGER_EN, !on); return 0; } @@ -722,27 +733,29 @@ static int w1_ds2784_add_slave(struct w1_slave *sl) int rc; - rc = gpio_request(MAHIMAHI_GPIO_BATTERY_CHARGER_EN, "charger_en"); - if (rc < 0) { - pr_err("%s: gpio_request(%d) failed: %d\n", __func__, - MAHIMAHI_GPIO_BATTERY_CHARGER_EN, rc); - return rc; + p = kzalloc(sizeof(struct dd), GFP_KERNEL); + if (!p) { + pr_err("%s: out of memory\n", __func__); + return -ENOMEM; } - rc = gpio_request(MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT, "charger_current"); + rc = gpio_request(MAHIMAHI_GPIO_BATTERY_CHARGER_EN, "charger_en"); if (rc < 0) { pr_err("%s: gpio_request(%d) failed: %d\n", __func__, - MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT, rc); - gpio_free(MAHIMAHI_GPIO_BATTERY_CHARGER_EN); + MAHIMAHI_GPIO_BATTERY_CHARGER_EN, rc); + kfree(p); return rc; } - p = kzalloc(sizeof(struct dd), GFP_KERNEL); - if (!p) { - pr_err("%s: out of memory\n", __func__); - gpio_free(MAHIMAHI_GPIO_BATTERY_CHARGER_EN); - gpio_free(MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT); - return -ENOMEM; + if (!is_cdma_version(system_rev)) { + rc = gpio_request(MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT, "charger_current"); + if (rc < 0) { + pr_err("%s: gpio_request(%d) failed: %d\n", __func__, + MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT, rc); + gpio_free(MAHIMAHI_GPIO_BATTERY_CHARGER_EN); + kfree(p); + return rc; + } } p->pdev.name = "ds2784-battery"; From bb1694bf911d35d20797190585bdc782296ca959 Mon Sep 17 00:00:00 2001 From: Haley Teng Date: Tue, 15 Dec 2009 13:39:10 -0800 Subject: [PATCH 0497/2556] [ARM] qsd8k: mahimahi: add GPIO definitions for CDMA version Signed-off-by: Haley Teng Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi.c | 44 ++++++++++++++++++++++++++++-- arch/arm/mach-msm/board-mahimahi.h | 3 ++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index cb2dc9134114a..1efe5cbd09802 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -647,11 +647,23 @@ static uint32_t flashlight_gpio_table[] = { GPIO_NO_PULL, GPIO_2MA), }; +static uint32_t flashlight_gpio_table_rev_CX[] = { + PCOM_GPIO_CFG(MAHIMAHI_CDMA_GPIO_FLASHLIGHT_TORCH, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(MAHIMAHI_GPIO_FLASHLIGHT_FLASH, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), +}; + static int config_mahimahi_flashlight_gpios(void) { - config_gpio_table(flashlight_gpio_table, - ARRAY_SIZE(flashlight_gpio_table)); + if (is_cdma_version(system_rev)) { + config_gpio_table(flashlight_gpio_table_rev_CX, + ARRAY_SIZE(flashlight_gpio_table_rev_CX)); + } else { + config_gpio_table(flashlight_gpio_table, + ARRAY_SIZE(flashlight_gpio_table)); + } return 0; } @@ -826,6 +838,25 @@ static uint32_t bt_gpio_table[] = { GPIO_PULL_DOWN, GPIO_4MA), }; +static uint32_t bt_gpio_table_rev_CX[] = { + PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_UART1_RTS, 2, GPIO_OUTPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_UART1_CTS, 2, GPIO_INPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_UART1_RX, 2, GPIO_INPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_UART1_TX, 2, GPIO_OUTPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_RESET_N, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_4MA), + PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_SHUTDOWN_N, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_4MA), + PCOM_GPIO_CFG(MAHIMAHI_CDMA_GPIO_BT_WAKE, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_4MA), + PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_HOST_WAKE, 0, GPIO_INPUT, + GPIO_PULL_DOWN, GPIO_4MA), +}; + static uint32_t misc_gpio_table[] = { PCOM_GPIO_CFG(MAHIMAHI_GPIO_LCD_RST_N, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), @@ -958,7 +989,14 @@ static void __init mahimahi_init(void) config_gpio_table(misc_gpio_table, ARRAY_SIZE(misc_gpio_table)); - config_gpio_table(bt_gpio_table, ARRAY_SIZE(bt_gpio_table)); + if (is_cdma_version(system_rev)) { + bcm_bt_lpm_pdata.gpio_wake = MAHIMAHI_CDMA_GPIO_BT_WAKE; + mahimahi_flashlight_data.torch = MAHIMAHI_CDMA_GPIO_FLASHLIGHT_TORCH; + config_gpio_table(bt_gpio_table_rev_CX, ARRAY_SIZE(bt_gpio_table_rev_CX)); + } else { + config_gpio_table(bt_gpio_table, ARRAY_SIZE(bt_gpio_table)); + } + gpio_request(MAHIMAHI_GPIO_TP_LS_EN, "tp_ls_en"); gpio_direction_output(MAHIMAHI_GPIO_TP_LS_EN, 0); gpio_request(MAHIMAHI_GPIO_TP_EN, "tp_en"); diff --git a/arch/arm/mach-msm/board-mahimahi.h b/arch/arm/mach-msm/board-mahimahi.h index 50097bf66ca14..ae7dbf6ed2be7 100644 --- a/arch/arm/mach-msm/board-mahimahi.h +++ b/arch/arm/mach-msm/board-mahimahi.h @@ -127,6 +127,9 @@ #define MAHIMAHI_GPIO_BATTERY_CHARGER_EN 22 #define MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT 16 +#define MAHIMAHI_CDMA_GPIO_BT_WAKE 28 +#define MAHIMAHI_CDMA_GPIO_FLASHLIGHT_TORCH 26 + #define is_cdma_version(rev) (((rev) & 0xF0) == 0xC0) #endif /* __ARCH_ARM_MACH_MSM_BOARD_MAHIMAHI_H */ From d5653a8ebe29dd2e828e9a64cf8162634583f7af Mon Sep 17 00:00:00 2001 From: Haley Teng Date: Tue, 15 Dec 2009 13:47:36 -0800 Subject: [PATCH 0498/2556] [ARM] qsd8k: mahimahi: correct the virtual key layout for CDMA version (system_rev == 0xC0) Signed-off-by: Haley Teng Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 1efe5cbd09802..81e278c042b8d 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -930,7 +930,7 @@ static struct msm_acpu_clock_platform_data mahimahi_clock_data = { static ssize_t mahimahi_virtual_keys_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - if (system_rev > 2) { + if (system_rev > 2 && system_rev != 0xC0) { /* center: x: back: 55, menu: 172, home: 298, search 412, y: 835 */ return sprintf(buf, __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":55:835:90:55" From 003f97264b5c0ee8a63208e55146214557b3dc6c Mon Sep 17 00:00:00 2001 From: "Ryan.CL Lin" Date: Mon, 25 Jan 2010 19:14:22 -0800 Subject: [PATCH 0499/2556] [ARM] qsd8k: mahimahi: fix uplink no-sound issue on CDMA XB The no-audio issue is due to A1026 GPIO changes. Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi.c | 7 +++++++ arch/arm/mach-msm/board-mahimahi.h | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 81e278c042b8d..d641c9e649019 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -1037,6 +1037,13 @@ static void __init mahimahi_init(void) /* Only CDMA version with TI TPA2018D1 Speaker Amp. */ i2c_register_board_info(0, rev_CX_i2c_devices, ARRAY_SIZE(rev_CX_i2c_devices)); + if ((system_rev & 0x0F) == 0x00) { + a1026_data.gpio_a1026_clk = MAHIMAHI_CDMA_XA_AUD_A1026_CLK; + } else if ((system_rev & 0x0F) >= 0x01) { + a1026_data.gpio_a1026_wakeup = MAHIMAHI_CDMA_XB_AUD_A1026_WAKEUP; + a1026_data.gpio_a1026_reset = MAHIMAHI_CDMA_XB_AUD_A1026_RESET; + a1026_data.gpio_a1026_clk = MAHIMAHI_CDMA_XB_AUD_A1026_CLK; + } } ret = mahimahi_init_mmc(system_rev, debug_uart); diff --git a/arch/arm/mach-msm/board-mahimahi.h b/arch/arm/mach-msm/board-mahimahi.h index ae7dbf6ed2be7..9e1fbad6b5adf 100644 --- a/arch/arm/mach-msm/board-mahimahi.h +++ b/arch/arm/mach-msm/board-mahimahi.h @@ -102,6 +102,15 @@ #define MAHIMAHI_AUD_A1026_WAKEUP 113 #define MAHIMAHI_AUD_A1026_RESET 129 #define MAHIMAHI_AUD_A1026_CLK -1 +#define MAHIMAHI_CDMA_XA_AUD_A1026_CLK 105 +/* NOTE: MAHIMAHI_CDMA_XB_AUD_A1026_WAKEUP on CDMA is the same GPIO as + * MAHIMAHI_GPIO_BATTERY_CHARGER_CURRENT on UMTS. Also, + * MAHIMAHI_CDMA_XB_AUD_A1026_RESET is the same as + * GPIO MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN on UMTS. + */ +#define MAHIMAHI_CDMA_XB_AUD_A1026_WAKEUP 16 +#define MAHIMAHI_CDMA_XB_AUD_A1026_RESET 19 +#define MAHIMAHI_CDMA_XB_AUD_A1026_CLK -1 /* Bluetooth PCM */ #define MAHIMAHI_BT_PCM_OUT 68 From e93b49a7b6ffda39bd0df7fd6e9bf181324f6609 Mon Sep 17 00:00:00 2001 From: "Ryan.CL Lin" Date: Mon, 25 Jan 2010 19:16:09 -0800 Subject: [PATCH 0500/2556] [ARM] qsd8k: mahimahi: fix receiver no-sound issue on CDMA revisions 0xC1, 0xC2 Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi-audio.c | 26 +++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-mahimahi-audio.c b/arch/arm/mach-msm/board-mahimahi-audio.c index 1ad93ad1396e8..09b75025db7b5 100644 --- a/arch/arm/mach-msm/board-mahimahi-audio.c +++ b/arch/arm/mach-msm/board-mahimahi-audio.c @@ -75,7 +75,31 @@ void mahimahi_speaker_enable(int en) void mahimahi_receiver_enable(int en) { - /* Do nothing for mahimahi. */ + if (is_cdma_version(system_rev) && + ((system_rev == 0xC1) || (system_rev == 0xC2))) { + struct spkr_config_mode scm; + memset(&scm, 0, sizeof(scm)); + + D("%s %d\n", __func__, en); + if (en) { + scm.is_right_chan_en = 1; + scm.is_left_chan_en = 0; + scm.is_stereo_en = 0; + scm.is_hpf_en = 1; + pmic_spkr_en_mute(RIGHT_SPKR, 0); + pmic_set_spkr_configuration(&scm); + pmic_spkr_en(RIGHT_SPKR, 1); + + /* unmute */ + pmic_spkr_en_mute(RIGHT_SPKR, 1); + } else { + pmic_spkr_en_mute(RIGHT_SPKR, 0); + + pmic_spkr_en(RIGHT_SPKR, 0); + + pmic_set_spkr_configuration(&scm); + } + } } static void config_gpio_table(uint32_t *table, int len) From 01c65a54a9e340f30ee0c1cb961bfabbb89665bb Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 26 Jan 2010 00:36:46 -0800 Subject: [PATCH 0501/2556] [ARM] qsd8k: mahimahi: do not drive MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN on CDMA MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN is not defined for CDMA devices. Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi-microp.c | 8 +++++--- arch/arm/mach-msm/board-mahimahi.c | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi-microp.c b/arch/arm/mach-msm/board-mahimahi-microp.c index 7e57806f53b55..85044667954c6 100644 --- a/arch/arm/mach-msm/board-mahimahi-microp.c +++ b/arch/arm/mach-msm/board-mahimahi-microp.c @@ -546,9 +546,10 @@ static int microp_enable_key_event(void) client = private_microp_client; - /* turn on key interrupt */ - gpio_set_value(MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN, 1); + if (!is_cdma_version(system_rev)) + gpio_set_value(MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN, 1); + /* turn on key interrupt */ /* enable microp interrupt to detect changes */ ret = microp_interrupt_enable(client, IRQ_REMOTEKEY); if (ret < 0) { @@ -567,7 +568,8 @@ static int microp_disable_key_event(void) client = private_microp_client; /* shutdown key interrupt */ - gpio_set_value(MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN, 0); + if (!is_cdma_version(system_rev)) + gpio_set_value(MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN, 0); /* disable microp interrupt to detect changes */ ret = microp_interrupt_disable(client, IRQ_REMOTEKEY); diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index d641c9e649019..8eb4125ddcdbb 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -873,6 +873,8 @@ static uint32_t key_int_shutdown_gpio_table[] = { static void mahimahi_headset_init(void) { + if (is_cdma_version(system_rev)) + return; config_gpio_table(key_int_shutdown_gpio_table, ARRAY_SIZE(key_int_shutdown_gpio_table)); gpio_set_value(MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN, 0); From 34cabe07559e5966539d1a87ebc6e9d6c91abaaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 28 Jan 2010 16:45:36 -0800 Subject: [PATCH 0502/2556] [ARM] Add VMALLOC_RESERVE kconfig option. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I67949f84c38a6e3e85456d129055cbdb2c007f73 Signed-off-by: Arve Hjønnevåg --- arch/arm/Kconfig | 7 +++++++ arch/arm/mm/mmu.c | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index cb0429c9dd250..c979ec6c326ff 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1476,6 +1476,13 @@ config HW_PERF_EVENTS Enable hardware performance counter support for perf events. If disabled, perf events will use software events only. +config VMALLOC_RESERVE + hex "Reserved vmalloc space" + default 0x08000000 + depends on MMU + help + Reserved vmalloc space if not specified on the kernel commandline. + source "mm/Kconfig" config FORCE_MAX_ZONEORDER diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index 3c67e92f7d592..27c20ce568285 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -714,7 +714,8 @@ void __init iotable_init(struct map_desc *io_desc, int nr) create_mapping(io_desc + i); } -static void * __initdata vmalloc_min = (void *)(VMALLOC_END - SZ_128M); +static void * __initdata vmalloc_min = (void *)(VMALLOC_END - + CONFIG_VMALLOC_RESERVE); /* * vmalloc=size forces the vmalloc area to be exactly 'size' From 05ef8088691c444c1422afc55c2abbf45bcf0f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 26 Jan 2010 15:48:57 -0800 Subject: [PATCH 0503/2556] [ARM] msm: mahimahi: Add a memory bank for available memory in ebi1 bank1. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ib2dd68bd0cb9633f645761f1bf543d660dbe372b Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/board-mahimahi.c | 5 ++++- arch/arm/mach-msm/board-mahimahi.h | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 8eb4125ddcdbb..fe7dfe3f025cf 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -1073,10 +1073,13 @@ static void __init mahimahi_init(void) static void __init mahimahi_fixup(struct machine_desc *desc, struct tag *tags, char **cmdline, struct meminfo *mi) { - mi->nr_banks = 1; + mi->nr_banks = 2; mi->bank[0].start = PHYS_OFFSET; mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); mi->bank[0].size = (219*1024*1024); + mi->bank[1].start = MSM_HIGHMEM_BASE; + mi->bank[1].node = PHYS_TO_NID(MSM_HIGHMEM_BASE); + mi->bank[1].size = MSM_HIGHMEM_SIZE; } static void __init mahimahi_map_io(void) diff --git a/arch/arm/mach-msm/board-mahimahi.h b/arch/arm/mach-msm/board-mahimahi.h index 9e1fbad6b5adf..c27787f9edf08 100644 --- a/arch/arm/mach-msm/board-mahimahi.h +++ b/arch/arm/mach-msm/board-mahimahi.h @@ -45,6 +45,9 @@ #define MSM_PMEM_CAMERA_BASE 0x34900000 #define MSM_PMEM_CAMERA_SIZE 0x00800000 +#define MSM_HIGHMEM_BASE 0x35100000 +#define MSM_HIGHMEM_SIZE 0x0AF00000 + #define MAHIMAHI_GPIO_PS_HOLD 25 #define MAHIMAHI_GPIO_UP_INT_N 35 From f983227dd92cfbd1bbb9aabcc3cf10631aed9fa3 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 26 Jan 2010 15:41:36 -0800 Subject: [PATCH 0504/2556] [ARM] qsd8k: mahimahi: request ds2482 sleep gpio in board file Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index fe7dfe3f025cf..99bda4501fd3d 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -1010,6 +1010,8 @@ static void __init mahimahi_init(void) gpio_request(MAHIMAHI_GPIO_COMPASS_INT_N, "compass_int"); gpio_direction_input(MAHIMAHI_GPIO_COMPASS_INT_N); + gpio_request(MAHIMAHI_GPIO_DS2482_SLP_N, "ds2482_slp_n"); + /* set the gpu power rail to manual mode so clk en/dis will not * turn off gpu power, and hang it on resume */ mahimahi_kgsl_power_rail_mode(0); From c09c7efc41027039b4d4aa1a744014f787567ad6 Mon Sep 17 00:00:00 2001 From: HK Chen Date: Mon, 1 Feb 2010 14:24:31 -0800 Subject: [PATCH 0505/2556] [ARM] qsd8k: mahimahi: put in-call vol. control (GSM & CDMA) in board file CDMA revisions have non-linear volume control. Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi-audio.c | 50 ++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/arch/arm/mach-msm/board-mahimahi-audio.c b/arch/arm/mach-msm/board-mahimahi-audio.c index 09b75025db7b5..7613904b7e616 100644 --- a/arch/arm/mach-msm/board-mahimahi-audio.c +++ b/arch/arm/mach-msm/board-mahimahi-audio.c @@ -33,6 +33,33 @@ static struct mutex mic_lock; static struct mutex bt_sco_lock; +static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { + [Q6_HW_HANDSET] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_HEADSET] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_SPEAKER] = { + .min_gain = -1500, + .max_gain = 0, + }, + [Q6_HW_TTY] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_BT_SCO] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_BT_A2DP] = { + .min_gain = -2000, + .max_gain = 0, + }, +}; + void mahimahi_headset_enable(int en) { D("%s %d\n", __func__, en); @@ -207,6 +234,28 @@ void mahimahi_analog_init(void) mutex_unlock(&bt_sco_lock); } +int mahimahi_get_rx_vol(uint8_t hw, int level) +{ + int vol; + + if (level > 100) + level = 100; + else if (level < 0) + level = 0; + + if (is_cdma_version(system_rev) && hw == Q6_HW_HANDSET) { + int handset_volume[6] = { -1600, -1300, -1000, -600, -300, 0 }; + vol = handset_volume[5 * level / 100]; + } else { + struct q6_hw_info *info; + info = &q6_audio_hw[hw]; + vol = info->min_gain + ((info->max_gain - info->min_gain) * level) / 100; + } + + D("%s %d\n", __func__, vol); + return vol; +} + static struct qsd_acoustic_ops acoustic = { .enable_mic_bias = mahimahi_mic_enable, }; @@ -219,6 +268,7 @@ static struct q6audio_analog_ops ops = { .bt_sco_enable = mahimahi_bt_sco_enable, .int_mic_enable = mahimahi_mic_enable, .ext_mic_enable = mahimahi_mic_enable, + .get_rx_vol = mahimahi_get_rx_vol, }; void __init mahimahi_audio_init(void) From 9f6609dd1f2354c7acdf500f22912c10e836a59d Mon Sep 17 00:00:00 2001 From: HK Chen Date: Mon, 1 Feb 2010 14:47:31 -0800 Subject: [PATCH 0506/2556] [ARM] qsd8k: mahimahi: override default ACDB file on some CDMA revs Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi-audio.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/mach-msm/board-mahimahi-audio.c b/arch/arm/mach-msm/board-mahimahi-audio.c index 7613904b7e616..074d84e4d131d 100644 --- a/arch/arm/mach-msm/board-mahimahi-audio.c +++ b/arch/arm/mach-msm/board-mahimahi-audio.c @@ -277,4 +277,7 @@ void __init mahimahi_audio_init(void) mutex_init(&bt_sco_lock); q6audio_register_analog_ops(&ops); acoustic_register_ops(&acoustic); + if (is_cdma_version(system_rev) && + ((system_rev == 0xC1) || (system_rev == 0xC2))) + q6audio_set_acdb_file("default_PMIC.acdb"); } From df79a39df92c86d9a4d400326431bbe0afad85ad Mon Sep 17 00:00:00 2001 From: Haley Teng Date: Mon, 1 Feb 2010 15:34:50 -0800 Subject: [PATCH 0507/2556] [ARM] qsd8k: mahimahi: the vdd of sdslot is supplied by gpio on cdma revs Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi-mmc.c | 44 ++++++++++++++++++++++++-- arch/arm/mach-msm/board-mahimahi.h | 2 ++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi-mmc.c b/arch/arm/mach-msm/board-mahimahi-mmc.c index 564a5b1c28aed..5b04878cce01c 100644 --- a/arch/arm/mach-msm/board-mahimahi-mmc.c +++ b/arch/arm/mach-msm/board-mahimahi-mmc.c @@ -134,6 +134,32 @@ static uint32_t mahimahi_sdslot_switchvdd(struct device *dev, unsigned int vdd) return 0; } +static uint32_t mahimahi_cdma_sdslot_switchvdd(struct device *dev, unsigned int vdd) +{ + if (!vdd == !sdslot_vdd) + return 0; + + /* In CDMA version, the vdd of sdslot is not configurable, and it is + * fixed in 2.85V by hardware design. + */ + + sdslot_vdd = vdd ? MMC_VDD_28_29 : 0; + + if (vdd) { + gpio_set_value(MAHIMAHI_CDMA_SD_2V85_EN, 1); + config_gpio_table(sdcard_on_gpio_table, + ARRAY_SIZE(sdcard_on_gpio_table)); + } else { + config_gpio_table(sdcard_off_gpio_table, + ARRAY_SIZE(sdcard_off_gpio_table)); + gpio_set_value(MAHIMAHI_CDMA_SD_2V85_EN, 0); + } + + sdslot_vreg_enabled = !!vdd; + + return 0; +} + static unsigned int mahimahi_sdslot_status_rev0(struct device *dev) { return !gpio_get_value(MAHIMAHI_GPIO_SDMC_CD_REV0_N); @@ -286,9 +312,21 @@ int __init mahimahi_init_mmc(unsigned int sys_rev, unsigned debug_uart) sdslot_vreg_enabled = 0; - sdslot_vreg = vreg_get(0, "gp6"); - if (IS_ERR(sdslot_vreg)) - return PTR_ERR(sdslot_vreg); + if (is_cdma_version(sys_rev)) { + /* In the CDMA version, sdslot is supplied by a gpio. */ + int rc = gpio_request(MAHIMAHI_CDMA_SD_2V85_EN, "sdslot_en"); + if (rc < 0) { + pr_err("%s: gpio_request(%d) failed: %d\n", __func__, + MAHIMAHI_CDMA_SD_2V85_EN, rc); + return rc; + } + mahimahi_sdslot_data.translate_vdd = mahimahi_cdma_sdslot_switchvdd; + } else { + /* in UMTS version, sdslot is supplied by pmic */ + sdslot_vreg = vreg_get(0, "gp6"); + if (IS_ERR(sdslot_vreg)) + return PTR_ERR(sdslot_vreg); + } if (system_rev > 0) msm_add_sdcc(2, &mahimahi_sdslot_data, 0, 0); diff --git a/arch/arm/mach-msm/board-mahimahi.h b/arch/arm/mach-msm/board-mahimahi.h index c27787f9edf08..921bf458b5f7a 100644 --- a/arch/arm/mach-msm/board-mahimahi.h +++ b/arch/arm/mach-msm/board-mahimahi.h @@ -142,6 +142,8 @@ #define MAHIMAHI_CDMA_GPIO_BT_WAKE 28 #define MAHIMAHI_CDMA_GPIO_FLASHLIGHT_TORCH 26 +#define MAHIMAHI_CDMA_SD_2V85_EN 100 + #define is_cdma_version(rev) (((rev) & 0xF0) == 0xC0) #endif /* __ARCH_ARM_MACH_MSM_BOARD_MAHIMAHI_H */ From 0989b0e1a74115dfa5ead9532ce4f5793486da1d Mon Sep 17 00:00:00 2001 From: Vincent Kao Date: Thu, 4 Feb 2010 20:08:30 -0800 Subject: [PATCH 0508/2556] [ARM] msm: usb: reset the PHY before suspend Signed-off-by: Iliyan Malchev --- drivers/usb/gadget/msm72k_udc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index e344df6ac1bd6..61d35f4c030d1 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -1253,6 +1253,8 @@ static void usb_do_work(struct work_struct *w) ui->driver->disconnect(&ui->gadget); } + usb_phy_reset(ui); + /* power down phy, clock down usb */ spin_lock_irqsave(&ui->lock, iflags); usb_suspend_phy(ui); From 7d06ca37811f5b55b16c0f512fdd4769f8ef8403 Mon Sep 17 00:00:00 2001 From: Haley Teng Date: Tue, 2 Feb 2010 12:31:33 -0800 Subject: [PATCH 0509/2556] [ARM] msm: smd: initialize SMD tty nodes from a default table Also, add a way to override the default table. Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/include/mach/msm_smd.h | 7 +++++ arch/arm/mach-msm/smd_tty.c | 38 +++++++++++++++++------- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/msm_smd.h b/arch/arm/mach-msm/include/mach/msm_smd.h index 029463ec8756f..7ed29393cc7b9 100644 --- a/arch/arm/mach-msm/include/mach/msm_smd.h +++ b/arch/arm/mach-msm/include/mach/msm_smd.h @@ -106,4 +106,11 @@ typedef enum { SMD_NUM_PORTS, } smd_port_id_type; +struct smd_tty_channel_desc { + int id; + const char *name; +}; + +int smd_set_channel_list(const struct smd_tty_channel_desc *, int len); + #endif diff --git a/arch/arm/mach-msm/smd_tty.c b/arch/arm/mach-msm/smd_tty.c index 2edd9d1ec2dc9..8715544dfdc16 100644 --- a/arch/arm/mach-msm/smd_tty.c +++ b/arch/arm/mach-msm/smd_tty.c @@ -40,6 +40,21 @@ struct smd_tty_info { static struct smd_tty_info smd_tty[MAX_SMD_TTYS]; +static const struct smd_tty_channel_desc smd_default_tty_channels[] = { + { .id = 0, .name = "SMD_DS" }, + { .id = 27, .name = "SMD_GPSNMEA" }, +}; + +static const struct smd_tty_channel_desc *smd_tty_channels = + smd_default_tty_channels; +static int smd_tty_channels_len = ARRAY_SIZE(smd_default_tty_channels); + +int smd_set_channel_list(const struct smd_tty_channel_desc *channels, int len) +{ + smd_tty_channels = channels; + smd_tty_channels_len = len; + return 0; +} static void smd_tty_notify(void *priv, unsigned event) { @@ -82,15 +97,17 @@ static int smd_tty_open(struct tty_struct *tty, struct file *f) int res = 0; int n = tty->index; struct smd_tty_info *info; - const char *name; + const char *name = NULL; + int i; - if (n == 0) { - name = "SMD_DS"; - } else if (n == 27) { - name = "SMD_GPSNMEA"; - } else { - return -ENODEV; + for (i = 0; i < smd_tty_channels_len; i++) { + if (smd_tty_channels[i].id == n) { + name = smd_tty_channels[i].name; + break; + } } + if (!name) + return -ENODEV; info = smd_tty + n; @@ -178,7 +195,7 @@ static struct tty_driver *smd_tty_driver; static int __init smd_tty_init(void) { - int ret; + int ret, i; smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS); if (smd_tty_driver == 0) @@ -203,9 +220,8 @@ static int __init smd_tty_init(void) ret = tty_register_driver(smd_tty_driver); if (ret) return ret; - /* this should be dynamic */ - tty_register_device(smd_tty_driver, 0, 0); - tty_register_device(smd_tty_driver, 27, 0); + for (i = 0; i < smd_tty_channels_len; i++) + tty_register_device(smd_tty_driver, smd_tty_channels[i].id, 0); return 0; } From 5ac501e185864b0d33265c1dc35d3c43e60697ff Mon Sep 17 00:00:00 2001 From: Haley Teng Date: Tue, 2 Feb 2010 12:31:33 -0800 Subject: [PATCH 0510/2556] [ARM] qsd8k: mahimahi: add HTC OMA-DM support (for use on CDMA revs) Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 99bda4501fd3d..f3c28b5eefedf 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -49,6 +49,7 @@ #include #include #include +#include #include "board-mahimahi.h" #include "devices.h" @@ -975,6 +976,12 @@ static void mahimahi_reset(void) int mahimahi_init_mmc(int sysrev, unsigned debug_uart); +static const struct smd_tty_channel_desc smd_cdma_default_channels[] = { + { .id = 0, .name = "SMD_DS" }, + { .id = 19, .name = "SMD_DATA3" }, + { .id = 27, .name = "SMD_GPSNMEA" } +}; + static void __init mahimahi_init(void) { int ret; @@ -982,6 +989,10 @@ static void __init mahimahi_init(void) printk("mahimahi_init() revision=%d\n", system_rev); + if (is_cdma_version(system_rev)) + smd_set_channel_list(smd_cdma_default_channels, + ARRAY_SIZE(smd_cdma_default_channels)); + msm_hw_reset_hook = mahimahi_reset; msm_acpu_clock_init(&mahimahi_clock_data); From ffc0a024fc0ad694ba1e18475229e2f5fdd220bd Mon Sep 17 00:00:00 2001 From: Eric Olsen Date: Mon, 8 Feb 2010 07:32:33 -0800 Subject: [PATCH 0511/2556] [ARM] msm: mahimahi: microp: Improve false headset detection. Signed-off-by: Eric Olsen --- arch/arm/mach-msm/board-mahimahi-microp.c | 37 ++++++++++++++----- arch/arm/mach-msm/htc_35mm_jack.c | 15 +++++++- .../arm/mach-msm/include/mach/htc_35mm_jack.h | 4 +- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi-microp.c b/arch/arm/mach-msm/board-mahimahi-microp.c index 85044667954c6..1f0d23f00e04f 100644 --- a/arch/arm/mach-msm/board-mahimahi-microp.c +++ b/arch/arm/mach-msm/board-mahimahi-microp.c @@ -489,8 +489,8 @@ static void hpin_debounce_do_work(struct work_struct *work) if (insert != cdata->headset_is_in) { cdata->headset_is_in = insert; pr_debug("headset %s\n", insert ? "inserted" : "removed"); - htc_35mm_jack_plug_event(cdata->headset_is_in); - cdata->is_hpin_pin_stable = 1; + htc_35mm_jack_plug_event(cdata->headset_is_in, + &cdata->is_hpin_pin_stable); } } @@ -517,26 +517,43 @@ static int microp_enable_headset_plug_event(void) if(cdata->headset_is_in != stat) { cdata->headset_is_in = stat; pr_debug("Headset state changed\n"); - htc_35mm_jack_plug_event(stat); + htc_35mm_jack_plug_event(stat, &cdata->is_hpin_pin_stable); } return 1; } -static int microp_headset_has_mic(void) +static int microp_headset_detect_mic(void) { uint16_t data; - int ret = 0; microp_read_adc(MICROP_REMOTE_KEY_ADC_CHAN, &data); if (data >= 200) - ret = 1; + return 1; else - ret = 0; + return 0; +} - pr_debug("%s: microp_mic_status =0x%d\n", __func__, ret); +static int microp_headset_has_mic(void) +{ + int mic1 = -1; + int mic2 = -1; + int count = 0; - return ret; + mic2 = microp_headset_detect_mic(); + + /* debounce the detection wait until 2 consecutive read are equal */ + while ((mic1 != mic2) && (count < 10)) { + mic1 = mic2; + msleep(600); + mic2 = microp_headset_detect_mic(); + count++; + } + + pr_info("%s: microphone (%d) %s\n", __func__, count, + mic1 ? "present" : "not present"); + + return mic1; } static int microp_enable_key_event(void) @@ -1724,7 +1741,7 @@ static void microp_i2c_intr_work_func(struct work_struct *work) if (intr_status & IRQ_REMOTEKEY) { if ((get_remote_keycode(&keycode) == 0) && (cdata->is_hpin_pin_stable)) { - htc_35mm_key_event(keycode); + htc_35mm_key_event(keycode, &cdata->is_hpin_pin_stable); } } diff --git a/arch/arm/mach-msm/htc_35mm_jack.c b/arch/arm/mach-msm/htc_35mm_jack.c index 17ebcc5194bbf..a956472789449 100644 --- a/arch/arm/mach-msm/htc_35mm_jack.c +++ b/arch/arm/mach-msm/htc_35mm_jack.c @@ -84,6 +84,7 @@ struct h35_info { int is_ext_insert; int key_code; int mic_bias_state; + int *is_hpin_stable; struct input_dev *input; struct wake_lock headset_wake_lock; @@ -160,6 +161,8 @@ static void remove_35mm_do_work(struct work_struct *work) if (hi->is_ext_insert) { H2WI("Skip 3.5mm headset plug out!!!"); + if (hi->is_hpin_stable) + *(hi->is_hpin_stable) = 1; return; } @@ -173,6 +176,8 @@ static void remove_35mm_do_work(struct work_struct *work) hi->mic_bias_state = 0; } hi->ext_35mm_status = 0; + if (hi->is_hpin_stable) + *(hi->is_hpin_stable) = 0; /* Notify framework via switch class */ mutex_lock(&hi->mutex_lock); @@ -222,14 +227,19 @@ static void insert_35mm_do_work(struct work_struct *work) mutex_lock(&hi->mutex_lock); switch_set_state(&hi->hs_change, hi->ext_35mm_status); mutex_unlock(&hi->mutex_lock); + + if (hi->is_hpin_stable) + *(hi->is_hpin_stable) = 1; } } -int htc_35mm_key_event(int keycode) +int htc_35mm_key_event(int keycode, int *hpin_stable) { hi->key_code = keycode; + hi->is_hpin_stable = hpin_stable; if ((hi->ext_35mm_status & BIT_HEADSET) == 0) { + *(hi->is_hpin_stable) = 0; pr_info("Key press with no mic. Retrying detection\n"); queue_work(detect_wq, &insert_35mm_work); @@ -239,7 +249,7 @@ int htc_35mm_key_event(int keycode) return 0; } -int htc_35mm_jack_plug_event(int insert) +int htc_35mm_jack_plug_event(int insert, int *hpin_stable) { if (!hi) { pr_err("Plug event before driver init\n"); @@ -248,6 +258,7 @@ int htc_35mm_jack_plug_event(int insert) mutex_lock(&hi->mutex_lock); hi->is_ext_insert = insert; + hi->is_hpin_stable = hpin_stable; mutex_unlock(&hi->mutex_lock); H2WI(" %d", hi->is_ext_insert); diff --git a/arch/arm/mach-msm/include/mach/htc_35mm_jack.h b/arch/arm/mach-msm/include/mach/htc_35mm_jack.h index 9473b587f01f4..5ce1e2a1e4780 100644 --- a/arch/arm/mach-msm/include/mach/htc_35mm_jack.h +++ b/arch/arm/mach-msm/include/mach/htc_35mm_jack.h @@ -18,8 +18,8 @@ #define HTC_35MM_REMOTE_H /* Driver interfaces */ -int htc_35mm_jack_plug_event(int insert); -int htc_35mm_key_event(int key); +int htc_35mm_jack_plug_event(int insert, int *hpin_stable); +int htc_35mm_key_event(int key, int *hpin_stable); /* Platform Specific Callbacks */ struct h35mm_platform_data { From 53e4141502e22cdce895ad7d6e035ae5061fe35f Mon Sep 17 00:00:00 2001 From: Haley Teng Date: Wed, 10 Feb 2010 12:47:29 -0800 Subject: [PATCH 0512/2556] [ARM] qsd8k: mahimahi: VDD of jogball is supplied by a gpio on cdma revs Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi-keypad.c | 32 ++++++++++++++++++++--- arch/arm/mach-msm/board-mahimahi.h | 1 + 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi-keypad.c b/arch/arm/mach-msm/board-mahimahi-keypad.c index ca8b4964e25b2..59f70b8185b71 100644 --- a/arch/arm/mach-msm/board-mahimahi-keypad.c +++ b/arch/arm/mach-msm/board-mahimahi-keypad.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -115,6 +116,19 @@ static int jogball_power(const struct gpio_event_platform_data *pdata, bool on) return 0; } +static int jogball_power_cdma(const struct gpio_event_platform_data *pdata, bool on) +{ + if (on) { + gpio_set_value(MAHIMAHI_CDMA_JOG_2V6_EN, 1); + jog_just_on = 1; + jog_on_jiffies = jiffies; + } else { + gpio_set_value(MAHIMAHI_CDMA_JOG_2V6_EN, 0); + } + + return 0; +} + static uint32_t jogball_x_gpios[] = { MAHIMAHI_GPIO_BALL_LEFT, MAHIMAHI_GPIO_BALL_RIGHT, }; @@ -210,9 +224,21 @@ static int __init mahimahi_init_keypad_jogball(void) if (ret != 0) return ret; - jog_vreg = vreg_get(&mahimahi_input_device.dev, "gp2"); - if (jog_vreg == NULL) - return -ENOENT; + if (is_cdma_version(system_rev)) { + /* In the CDMA version, jogball power is supplied by a gpio. */ + ret = gpio_request(MAHIMAHI_CDMA_JOG_2V6_EN, "jog_en"); + if (ret < 0) { + pr_err("%s: gpio_request(%d) failed: %d\n", __func__, + MAHIMAHI_CDMA_JOG_2V6_EN, ret); + return ret; + } + mahimahi_input_data.power = jogball_power_cdma; + } else { + /* in UMTS version, jogball power is supplied by pmic */ + jog_vreg = vreg_get(&mahimahi_input_device.dev, "gp2"); + if (jog_vreg == NULL) + return -ENOENT; + } ret = platform_device_register(&mahimahi_input_device); if (ret != 0) diff --git a/arch/arm/mach-msm/board-mahimahi.h b/arch/arm/mach-msm/board-mahimahi.h index 921bf458b5f7a..d8d89527b471f 100644 --- a/arch/arm/mach-msm/board-mahimahi.h +++ b/arch/arm/mach-msm/board-mahimahi.h @@ -143,6 +143,7 @@ #define MAHIMAHI_CDMA_GPIO_FLASHLIGHT_TORCH 26 #define MAHIMAHI_CDMA_SD_2V85_EN 100 +#define MAHIMAHI_CDMA_JOG_2V6_EN 150 #define is_cdma_version(rev) (((rev) & 0xF0) == 0xC0) From 5236a1a5147d5a4cc879ba30a5e1b73f37bed6e3 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Wed, 10 Feb 2010 15:30:26 -0500 Subject: [PATCH 0513/2556] [ARM] msm: mahimahi: board file support for RNDIS USB networking function Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/board-mahimahi.c | 57 +++++++++++++++--------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index f3c28b5eefedf..7f195c75683d1 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -124,25 +124,24 @@ static struct msm_hsusb_platform_data msm_hsusb_pdata = { .usb_connected = notify_usb_connected, }; -static char *usb_functions[] = { +static char *usb_functions_ums[] = { "usb_mass_storage", -#ifdef CONFIG_USB_ANDROID_RNDIS - "rndis", -#endif -#ifdef CONFIG_USB_ANDROID_ACM - "acm", -#endif }; -static char *usb_functions_adb[] = { +static char *usb_functions_ums_adb[] = { "usb_mass_storage", "adb", -#ifdef CONFIG_USB_ANDROID_RNDIS +}; + +static char *usb_functions_rndis_ums[] = { "rndis", -#endif -#ifdef CONFIG_USB_ANDROID_ACM - "acm", -#endif + "usb_mass_storage", +}; + +static char *usb_functions_rndis_ums_adb[] = { + "rndis", + "usb_mass_storage", + "adb", }; #ifdef CONFIG_USB_ANDROID_DIAG @@ -154,14 +153,14 @@ static char *usb_functions_adb_diag[] = { #endif static char *usb_functions_all[] = { +#ifdef CONFIG_USB_ANDROID_RNDIS + "rndis", +#endif "usb_mass_storage", "adb", #ifdef CONFIG_USB_ANDROID_ACM "acm", #endif -#ifdef CONFIG_USB_ANDROID_RNDIS - "rndis", -#endif #ifdef CONFIG_USB_ANDROID_DIAG "diag", #endif @@ -169,22 +168,24 @@ static char *usb_functions_all[] = { static struct android_usb_product usb_products[] = { { -#ifdef CONFIG_USB_ANDROID_ACM - .product_id = 0x4e21, -#else .product_id = 0x4e11, -#endif - .num_functions = ARRAY_SIZE(usb_functions), - .functions = usb_functions, + .num_functions = ARRAY_SIZE(usb_functions_ums), + .functions = usb_functions_ums, }, { -#ifdef CONFIG_USB_ANDROID_ACM - .product_id = 0x4e22, -#else .product_id = 0x4e12, -#endif - .num_functions = ARRAY_SIZE(usb_functions_adb), - .functions = usb_functions_adb, + .num_functions = ARRAY_SIZE(usb_functions_ums_adb), + .functions = usb_functions_ums_adb, + }, + { + .product_id = 0x4e13, + .num_functions = ARRAY_SIZE(usb_functions_rndis_ums), + .functions = usb_functions_rndis_ums, + }, + { + .product_id = 0x4e14, + .num_functions = ARRAY_SIZE(usb_functions_rndis_ums_adb), + .functions = usb_functions_rndis_ums_adb, }, #ifdef CONFIG_USB_ANDROID_DIAG { From 96e0692a7c47d6c03ea37269ffa4910babe7abeb Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 8 Feb 2010 05:35:19 -0500 Subject: [PATCH 0514/2556] [ARM] msm: usb: gadget: Set max packet size to 64 for non-bulk endpoints. Signed-off-by: Mike Lockwood --- drivers/usb/gadget/msm72k_udc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 61d35f4c030d1..d625e056a381f 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -1464,7 +1464,10 @@ msm72k_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) unsigned char ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; - _ep->maxpacket = le16_to_cpu(desc->wMaxPacketSize); + if (ep_type == USB_ENDPOINT_XFER_BULK) + _ep->maxpacket = le16_to_cpu(desc->wMaxPacketSize); + else + _ep->maxpacket = le16_to_cpu(64); config_ept(ept); usb_ept_enable(ept, 1, ep_type); return 0; From d66405670622bbbd7a3893ab67a9d644fa4c6964 Mon Sep 17 00:00:00 2001 From: Haley Teng Date: Tue, 9 Feb 2010 19:41:04 -0800 Subject: [PATCH 0515/2556] [ARM] msm: mahimahi: register matrix key (2, 2) as dummy key on CDMA Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi-keypad.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/arm/mach-msm/board-mahimahi-keypad.c b/arch/arm/mach-msm/board-mahimahi-keypad.c index 59f70b8185b71..65078cb7e8f7b 100644 --- a/arch/arm/mach-msm/board-mahimahi-keypad.c +++ b/arch/arm/mach-msm/board-mahimahi-keypad.c @@ -51,6 +51,19 @@ static const unsigned short mahimahi_keymap[KEYMAP_SIZE] = { [KEYMAP_INDEX(1, 1)] = MATRIX_KEY(1, BTN_MOUSE), }; +static const unsigned short mahimahi_cdma_keymap[KEYMAP_SIZE] = { + [KEYMAP_INDEX(0, 0)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(0, 1)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(1, 1)] = MATRIX_KEY(1, BTN_MOUSE), + + /* Key (2, 2) is not a physical key on mahimahi. The purpose of + * registering the unused matrix key as a dummy key is to make + * userland able to send/receive the key event for some requested tests + * in lab. of some CDMA carriers (e.g. Verizon). + */ + [KEYMAP_INDEX(2, 2)] = KEY_END, +}; + static struct gpio_event_matrix_info mahimahi_keypad_matrix_info = { .info.func = gpio_event_matrix_func, .keymap = mahimahi_keymap, @@ -225,6 +238,7 @@ static int __init mahimahi_init_keypad_jogball(void) return ret; if (is_cdma_version(system_rev)) { + mahimahi_keypad_matrix_info.keymap = mahimahi_cdma_keymap; /* In the CDMA version, jogball power is supplied by a gpio. */ ret = gpio_request(MAHIMAHI_CDMA_JOG_2V6_EN, "jog_en"); if (ret < 0) { From 60da93744fecc33a0a0bb60153d64e4460d2816c Mon Sep 17 00:00:00 2001 From: Vincent Kao Date: Tue, 9 Feb 2010 14:32:52 -0800 Subject: [PATCH 0516/2556] [ARM] msm: usb: diag: implement tx/rx usb_request queues The queues replace original single req_in/req_out. Signed-off-by: Vincent Kao Signed-off-by: Haley Teng Signed-off-by: Iliyan Malchev --- drivers/usb/gadget/diag.c | 147 +++++++++++++++++++++++++++++++++----- 1 file changed, 128 insertions(+), 19 deletions(-) diff --git a/drivers/usb/gadget/diag.c b/drivers/usb/gadget/diag.c index 1a905732c3bd1..845c4aa64a133 100644 --- a/drivers/usb/gadget/diag.c +++ b/drivers/usb/gadget/diag.c @@ -66,14 +66,22 @@ static void TRACE(const char *tag, const void *_data, int len, int decode) #define HDLC_MAX 4096 +#define TX_REQ_BUF_SZ 8192 +#define RX_REQ_BUF_SZ 8192 + +/* number of tx/rx requests to allocate */ +#define TX_REQ_NUM 4 +#define RX_REQ_NUM 4 + struct diag_context { struct usb_function function; struct usb_composite_dev *cdev; struct usb_ep *out; struct usb_ep *in; - struct usb_request *req_out; - struct usb_request *req_in; + struct list_head tx_req_idle; + struct list_head rx_req_idle; + spinlock_t req_lock; smd_channel_t *ch; int in_busy; int online; @@ -151,10 +159,49 @@ static void smd_try_to_send(struct diag_context *ctxt); static void diag_queue_out(struct diag_context *ctxt); +/* add a request to the tail of a list */ +static void req_put(struct diag_context *ctxt, struct list_head *head, + struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&ctxt->req_lock, flags); + list_add_tail(&req->list, head); + spin_unlock_irqrestore(&ctxt->req_lock, flags); +} + +/* remove a request from the head of a list */ +static struct usb_request *req_get(struct diag_context *ctxt, + struct list_head *head) +{ + struct usb_request *req = 0; + unsigned long flags; + + spin_lock_irqsave(&ctxt->req_lock, flags); + if (!list_empty(head)) { + req = list_first_entry(head, struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&ctxt->req_lock, flags); + + return req; +} + +static void reqs_free(struct diag_context *ctxt, struct usb_ep *ep, + struct list_head *head) +{ + struct usb_request *req; + while ((req = req_get(ctxt, head))) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + static void diag_in_complete(struct usb_ep *ept, struct usb_request *req) { struct diag_context *ctxt = req->context; ctxt->in_busy = 0; + req_put(ctxt, &ctxt->tx_req_idle, req); smd_try_to_send(ctxt); } @@ -208,18 +255,31 @@ static void diag_out_complete(struct usb_ep *ept, struct usb_request *req) diag_process_hdlc(ctxt, req->buf, req->actual); #endif } + + req_put(ctxt, &ctxt->rx_req_idle, req); diag_queue_out(ctxt); } static void diag_queue_out(struct diag_context *ctxt) { - struct usb_request *req = ctxt->req_out; + struct usb_request *req; + int rc; + + req = req_get(ctxt, &ctxt->rx_req_idle); + if (!req) { + pr_err("%s: rx req queue - out of buffer\n", __func__); + return; + } req->complete = diag_out_complete; req->context = ctxt; - req->length = 8192; + req->length = RX_REQ_BUF_SZ; - usb_ep_queue(ctxt->out, req, GFP_ATOMIC); + rc = usb_ep_queue(ctxt->out, req, GFP_ATOMIC); + if (rc < 0) { + pr_err("%s: usb_ep_queue failed: %d\n", __func__, rc); + req_put(ctxt, &ctxt->rx_req_idle, req); + } } static void smd_try_to_send(struct diag_context *ctxt) @@ -228,11 +288,17 @@ static void smd_try_to_send(struct diag_context *ctxt) if (ctxt->ch && (!ctxt->in_busy)) { int r = smd_read_avail(ctxt->ch); - if (r > 8192) { + if (r > TX_REQ_BUF_SZ) { return; } if (r > 0) { - struct usb_request *req = ctxt->req_in; + struct usb_request *req; + req = req_get(ctxt, &ctxt->tx_req_idle); + if (!req) { + pr_err("%s: tx req queue is out of buffers\n", + __func__); + return; + } smd_read(ctxt->ch, req->buf, r); if (!ctxt->online) { @@ -245,7 +311,12 @@ static void smd_try_to_send(struct diag_context *ctxt) TRACE("A9>", req->buf, r, 1); ctxt->in_busy = 1; - usb_ep_queue(ctxt->in, req, GFP_ATOMIC); + r = usb_ep_queue(ctxt->in, req, GFP_ATOMIC); + if (r < 0) { + pr_err("%s: usb_ep_queue failed: %d\n", + __func__, r); + req_put(ctxt, &ctxt->tx_req_idle, req); + } } } } @@ -262,6 +333,8 @@ static int __init create_bulk_endpoints(struct diag_context *ctxt, { struct usb_composite_dev *cdev = ctxt->cdev; struct usb_ep *ep; + struct usb_request *req; + int n; ep = usb_ep_autoconfig(cdev->gadget, in_desc); if (!ep) { @@ -276,14 +349,47 @@ static int __init create_bulk_endpoints(struct diag_context *ctxt, } ctxt->out = ep; - ctxt->req_out = usb_ep_alloc_request(ctxt->out, GFP_KERNEL); - ctxt->req_in = usb_ep_alloc_request(ctxt->in, GFP_KERNEL); - ctxt->req_out->buf = kmalloc(8192, GFP_KERNEL); - ctxt->req_in->buf = kmalloc(8192, GFP_KERNEL); - ctxt->req_out->complete = diag_out_complete; - ctxt->req_in->complete = diag_in_complete; + for (n = 0; n < RX_REQ_NUM; n++) { + req = usb_ep_alloc_request(ctxt->out, GFP_KERNEL); + if (!req) { + DBG(cdev, "%s: usb_ep_alloc_request out of memory\n", + __func__); + goto rx_fail; + } + req->buf = kmalloc(RX_REQ_BUF_SZ, GFP_KERNEL); + if (!req->buf) { + DBG(cdev, "%s: kmalloc out of memory\n", __func__); + goto rx_fail; + } + req->context = ctxt; + req->complete = diag_out_complete; + req_put(ctxt, &ctxt->rx_req_idle, req); + } + + for (n = 0; n < TX_REQ_NUM; n++) { + req = usb_ep_alloc_request(ctxt->in, GFP_KERNEL); + if (!req) { + DBG(cdev, "%s: usb_ep_alloc_request out of memory\n", + __func__); + goto tx_fail; + } + req->buf = kmalloc(TX_REQ_BUF_SZ, GFP_KERNEL); + if (!req->buf) { + DBG(cdev, "%s: kmalloc out of memory\n", __func__); + goto tx_fail; + } + req->context = ctxt; + req->complete = diag_in_complete; + req_put(ctxt, &ctxt->tx_req_idle, req); + } return 0; + +tx_fail: + reqs_free(ctxt, ctxt->in, &ctxt->tx_req_idle); +rx_fail: + reqs_free(ctxt, ctxt->out, &ctxt->rx_req_idle); + return -ENOMEM; } static int @@ -323,11 +429,8 @@ static void diag_function_unbind(struct usb_configuration *c, struct usb_function *f) { struct diag_context *ctxt = func_to_dev(f); - - kfree(ctxt->req_out->buf); - kfree(ctxt->req_in->buf); - usb_ep_free_request(ctxt->out, ctxt->req_out); - usb_ep_free_request(ctxt->in, ctxt->req_in); + reqs_free(ctxt, ctxt->out, &ctxt->rx_req_idle); + reqs_free(ctxt, ctxt->in, &ctxt->tx_req_idle); } static int diag_function_set_alt(struct usb_function *f, @@ -416,7 +519,13 @@ static struct android_usb_function diag_function = { static int __init init(void) { + struct diag_context *ctxt = &_context; + printk(KERN_INFO "diag init\n"); + spin_lock_init(&ctxt->req_lock); + INIT_LIST_HEAD(&ctxt->rx_req_idle); + INIT_LIST_HEAD(&ctxt->tx_req_idle); + android_register_function(&diag_function); return 0; } From 51351bbf35cc7e231ea771d47955ca077619d561 Mon Sep 17 00:00:00 2001 From: Haley Teng Date: Tue, 9 Feb 2010 16:01:09 -0800 Subject: [PATCH 0517/2556] [ARM] msm: usb: diag: export an attribute for userland to read SMD_DIAG traffic -- /sys/module/diag/parameters/tx_rx_count -- traffic is read in bytes Signed-off-by: Iliyan Malchev --- drivers/usb/gadget/diag.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/usb/gadget/diag.c b/drivers/usb/gadget/diag.c index 845c4aa64a133..789402fb43582 100644 --- a/drivers/usb/gadget/diag.c +++ b/drivers/usb/gadget/diag.c @@ -91,6 +91,9 @@ struct diag_context unsigned hdlc_count; unsigned hdlc_escape; + u64 tx_count; /* to smd */ + u64 rx_count; /* from smd */ + int function_enable; }; @@ -254,6 +257,7 @@ static void diag_out_complete(struct usb_ep *ept, struct usb_request *req) #else diag_process_hdlc(ctxt, req->buf, req->actual); #endif + ctxt->tx_count += req->actual; } req_put(ctxt, &ctxt->rx_req_idle, req); @@ -300,6 +304,7 @@ static void smd_try_to_send(struct diag_context *ctxt) return; } smd_read(ctxt->ch, req->buf, r); + ctxt->rx_count += r; if (!ctxt->online) { // printk("$$$ discard %d\n", r); @@ -349,6 +354,8 @@ static int __init create_bulk_endpoints(struct diag_context *ctxt, } ctxt->out = ep; + ctxt->tx_count = ctxt->rx_count = 0; + for (n = 0; n < RX_REQ_NUM; n++) { req = usb_ep_alloc_request(ctxt->out, GFP_KERNEL); if (!req) { @@ -431,6 +438,8 @@ diag_function_unbind(struct usb_configuration *c, struct usb_function *f) struct diag_context *ctxt = func_to_dev(f); reqs_free(ctxt, ctxt->out, &ctxt->rx_req_idle); reqs_free(ctxt, ctxt->in, &ctxt->tx_req_idle); + + ctxt->tx_count = ctxt->rx_count = 0; } static int diag_function_set_alt(struct usb_function *f, @@ -480,6 +489,15 @@ static int diag_set_enabled(const char *val, struct kernel_param *kp) return 0; } +static int diag_get_tx_rx_count(char *buffer, struct kernel_param *kp) +{ + struct diag_context *ctxt = &_context; + + return sprintf(buffer, "tx: %llu bytes, rx: %llu bytes", + ctxt->tx_count, ctxt->rx_count); +} +module_param_call(tx_rx_count, NULL, diag_get_tx_rx_count, NULL, 0444); + static int diag_get_enabled(char *buffer, struct kernel_param *kp) { buffer[0] = '0' + !_context.function.hidden; @@ -487,6 +505,7 @@ static int diag_get_enabled(char *buffer, struct kernel_param *kp) } module_param_call(enabled, diag_set_enabled, diag_get_enabled, NULL, 0664); + int diag_bind_config(struct usb_configuration *c) { struct diag_context *ctxt = &_context; From 576fdf184b1d87cde07ed0f961ad51facea3fc7e Mon Sep 17 00:00:00 2001 From: Haley Teng Date: Tue, 9 Feb 2010 16:19:59 -0800 Subject: [PATCH 0518/2556] [ARM] msm: usb: diag: add support to route some diag data to userspace -- compiles conditionally based on ROUTE_TO_USERSPACE -- when /dev/diag is opened, command ids 0xfb..0xff are routed to userspace Signed-off-by: Iliyan Malchev --- drivers/usb/gadget/diag.c | 251 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 251 insertions(+) diff --git a/drivers/usb/gadget/diag.c b/drivers/usb/gadget/diag.c index 789402fb43582..f96c0723994a6 100644 --- a/drivers/usb/gadget/diag.c +++ b/drivers/usb/gadget/diag.c @@ -19,12 +19,18 @@ #include #include #include +#include +#include +#include +#include +#include #include #include #define NO_HDLC 1 +#define ROUTE_TO_USERSPACE 1 #if 1 #define TRACE(tag,data,len,decode) do {} while(0) @@ -82,6 +88,16 @@ struct diag_context struct list_head tx_req_idle; struct list_head rx_req_idle; spinlock_t req_lock; +#if ROUTE_TO_USERSPACE + struct mutex user_lock; + struct list_head rx_req_user; + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + char *user_read_buf; + uint32_t user_read_len; + char *user_readp; + bool opened; +#endif smd_channel_t *ch; int in_busy; int online; @@ -200,11 +216,194 @@ static void reqs_free(struct diag_context *ctxt, struct usb_ep *ep, } } +#if ROUTE_TO_USERSPACE +static ssize_t diag_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct diag_context *ctxt = &_context; + struct usb_request *req = 0; + int ret = 0; + + mutex_lock(&ctxt->user_lock); + + if (ctxt->user_read_len && ctxt->user_readp) { + if (count > ctxt->user_read_len) + count = ctxt->user_read_len; + if (copy_to_user(buf, ctxt->user_readp, count)) + ret = -EFAULT; + else { + ctxt->user_readp += count; + ctxt->user_read_len -= count; + ret = count; + } + goto end; + } + + ret = wait_event_interruptible(ctxt->read_wq, + (req = req_get(ctxt, &ctxt->rx_req_user)) || !ctxt->online); + if (ret < 0) { + pr_err("%s: wait_event_interruptible error %d\n", + __func__, ret); + goto end; + } + if (!ctxt->online) { + pr_err("%s: offline\n", __func__); + ret = -EIO; + goto end; + } + if (req) { + if (req->actual == 0) { + pr_info("%s: no data\n", __func__); + goto end; + } + if (count > req->actual) + count = req->actual; + if (copy_to_user(buf, req->buf, count)) { + ret = -EFAULT; + goto end; + } + req->actual -= count; + if (req->actual) { + memcpy(ctxt->user_read_buf, req->buf + count, req->actual); + ctxt->user_read_len = req->actual; + ctxt->user_readp = ctxt->user_read_buf; + } + ret = count; + } + +end: + if (req) + req_put(ctxt, &ctxt->rx_req_idle, req); + + mutex_unlock(&ctxt->user_lock); + return ret; +} + +static ssize_t diag_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct diag_context *ctxt = &_context; + struct usb_request *req = 0; + int ret = 0; + + mutex_lock(&ctxt->user_lock); + + ret = wait_event_interruptible(ctxt->write_wq, + ((req = req_get(ctxt, &ctxt->tx_req_idle)) || !ctxt->online)); + if (ret < 0) { + pr_err("%s: wait_event_interruptible error %d\n", + __func__, ret); + goto end; + } + + if (!ctxt->online) { + pr_err("%s: offline\n", __func__); + ret = -EIO; + goto end; + } + + if (count > TX_REQ_BUF_SZ) + count = TX_REQ_BUF_SZ; + + if (req) { + if (copy_from_user(req->buf, buf, count)) { + ret = -EFAULT; + goto end; + } + + req->length = count; + ret = usb_ep_queue(ctxt->in, req, GFP_ATOMIC); + if (ret < 0) { + pr_err("%s: usb_ep_queue error %d\n", __func__, ret); + goto end; + } + + ret = req->length; + } + +end: + if (req) + req_put(ctxt, &ctxt->tx_req_idle, req); + + mutex_unlock(&ctxt->user_lock); + return ret; +} + +static int diag_open(struct inode *ip, struct file *fp) +{ + struct diag_context *ctxt = &_context; + int rc = 0; + + mutex_lock(&ctxt->user_lock); + if (ctxt->opened) { + pr_err("%s: already opened\n", __func__); + rc = -EBUSY; + goto done; + } + ctxt->user_read_len = 0; + ctxt->user_readp = 0; + if (!ctxt->user_read_buf) { + ctxt->user_read_buf = kmalloc(RX_REQ_BUF_SZ, GFP_KERNEL); + if (!ctxt->user_read_buf) { + rc = -ENOMEM; + goto done; + } + } + ctxt->opened = true; + +done: + mutex_unlock(&ctxt->user_lock); + return rc; +} + +static int diag_release(struct inode *ip, struct file *fp) +{ + struct diag_context *ctxt = &_context; + + mutex_lock(&ctxt->user_lock); + ctxt->opened = false; + ctxt->user_read_len = 0; + ctxt->user_readp = 0; + if (ctxt->user_read_buf) { + kfree(ctxt->user_read_buf); + ctxt->user_read_buf = 0; + } + mutex_unlock(&ctxt->user_lock); + + return 0; +} + +static struct file_operations diag_fops = { + .owner = THIS_MODULE, + .read = diag_read, + .write = diag_write, + .open = diag_open, + .release = diag_release, +}; + +static struct miscdevice diag_device_fops = { + .minor = MISC_DYNAMIC_MINOR, + .name = "diag", + .fops = &diag_fops, +}; +#endif + static void diag_in_complete(struct usb_ep *ept, struct usb_request *req) { struct diag_context *ctxt = req->context; +#if ROUTE_TO_USERSPACE + char c; +#endif + ctxt->in_busy = 0; req_put(ctxt, &ctxt->tx_req_idle, req); + +#if ROUTE_TO_USERSPACE + c = *((char *)req->buf + req->actual - 1); + if (c == 0x7e) + wake_up(&ctxt->write_wq); +#endif + smd_try_to_send(ctxt); } @@ -245,11 +444,37 @@ static void diag_process_hdlc(struct diag_context *ctxt, void *_data, unsigned l ctxt->hdlc_escape = escape; } +#if ROUTE_TO_USERSPACE +static int if_route_to_userspace(struct diag_context *ctxt, unsigned int cmd_id) +{ + if (!ctxt->opened || cmd_id == 0) + return 0; + + /* command ids 0xfb..0xff are not used by msm diag; we steal these ids + * for communication between userspace tool and host test tool. + */ + if (cmd_id >= 0xfb && cmd_id <= 0xff) + return 1; + + return 0; +} +#endif + static void diag_out_complete(struct usb_ep *ept, struct usb_request *req) { struct diag_context *ctxt = req->context; if (req->status == 0) { +#if ROUTE_TO_USERSPACE + unsigned int cmd_id = *((unsigned char *)req->buf); + if (if_route_to_userspace(ctxt, cmd_id)) { + req_put(ctxt, &ctxt->rx_req_user, req); + wake_up(&ctxt->read_wq); + diag_queue_out(ctxt); + return; + } +#endif + #if NO_HDLC TRACE("PC>", req->buf, req->actual, 0); if (ctxt->ch) @@ -429,6 +654,10 @@ diag_function_bind(struct usb_configuration *c, struct usb_function *f) diag_fullspeed_out_desc.bEndpointAddress; } +#if ROUTE_TO_USERSPACE + misc_register(&diag_device_fops); +#endif + return 0; } @@ -439,6 +668,10 @@ diag_function_unbind(struct usb_configuration *c, struct usb_function *f) reqs_free(ctxt, ctxt->out, &ctxt->rx_req_idle); reqs_free(ctxt, ctxt->in, &ctxt->tx_req_idle); +#if ROUTE_TO_USERSPACE + misc_deregister(&diag_device_fops); +#endif + ctxt->tx_count = ctxt->rx_count = 0; } @@ -447,6 +680,9 @@ static int diag_function_set_alt(struct usb_function *f, { struct diag_context *ctxt = func_to_dev(f); struct usb_composite_dev *cdev = f->config->cdev; +#if ROUTE_TO_USERSPACE + struct usb_request *req; +#endif int ret; ret = usb_ep_enable(ctxt->in, @@ -465,8 +701,17 @@ static int diag_function_set_alt(struct usb_function *f, } ctxt->online = 1; +#if ROUTE_TO_USERSPACE + /* recycle unhandled rx reqs to user if any */ + while ((req = req_get(ctxt, &ctxt->rx_req_user))) + req_put(ctxt, &ctxt->rx_req_idle, req); +#endif + diag_queue_out(ctxt); smd_try_to_send(ctxt); +#if ROUTE_TO_USERSPACE + wake_up(&ctxt->read_wq); +#endif return 0; } @@ -544,6 +789,12 @@ static int __init init(void) spin_lock_init(&ctxt->req_lock); INIT_LIST_HEAD(&ctxt->rx_req_idle); INIT_LIST_HEAD(&ctxt->tx_req_idle); +#if ROUTE_TO_USERSPACE + mutex_init(&ctxt->user_lock); + INIT_LIST_HEAD(&ctxt->rx_req_user); + init_waitqueue_head(&ctxt->read_wq); + init_waitqueue_head(&ctxt->write_wq); +#endif android_register_function(&diag_function); return 0; From 862d38e79555cbab689ac9e7521aa8188bf61f4c Mon Sep 17 00:00:00 2001 From: Haley Teng Date: Tue, 9 Feb 2010 18:00:25 -0800 Subject: [PATCH 0519/2556] [ARM] msm: usb: diag: add an ioctl to support dynamic routing of command ids Signed-off-by: Iliyan Malchev --- drivers/usb/gadget/diag.c | 44 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/drivers/usb/gadget/diag.c b/drivers/usb/gadget/diag.c index f96c0723994a6..ed1c150f69cc1 100644 --- a/drivers/usb/gadget/diag.c +++ b/drivers/usb/gadget/diag.c @@ -90,6 +90,7 @@ struct diag_context spinlock_t req_lock; #if ROUTE_TO_USERSPACE struct mutex user_lock; +#define ID_TABLE_SZ 10 /* keep this small */ struct list_head rx_req_user; wait_queue_head_t read_wq; wait_queue_head_t write_wq; @@ -97,6 +98,9 @@ struct diag_context uint32_t user_read_len; char *user_readp; bool opened; + + /* list of registered command ids to be routed to userspace */ + unsigned char id_table[ID_TABLE_SZ]; #endif smd_channel_t *ch; int in_busy; @@ -217,6 +221,31 @@ static void reqs_free(struct diag_context *ctxt, struct usb_ep *ep, } #if ROUTE_TO_USERSPACE +#define USB_DIAG_IOC_MAGIC 0xFF +#define USB_DIAG_FUNC_IOC_REGISTER_SET _IOW(USB_DIAG_IOC_MAGIC, 3, char *) + +static long diag_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct diag_context *ctxt = &_context; + void __user *argp = (void __user *)arg; + unsigned long flags; + unsigned char temp_id_table[ID_TABLE_SZ]; + + if (cmd != USB_DIAG_FUNC_IOC_REGISTER_SET) { + pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd)); + return -EINVAL; + } + + if (copy_from_user(temp_id_table, (unsigned char *)argp, ID_TABLE_SZ)) + return -EFAULT; + + spin_lock_irqsave(&ctxt->req_lock, flags); + memcpy(ctxt->id_table, temp_id_table, ID_TABLE_SZ); + spin_unlock_irqrestore(&ctxt->req_lock, flags); + + return 0; +} + static ssize_t diag_read(struct file *fp, char __user *buf, size_t count, loff_t *pos) { @@ -379,6 +408,7 @@ static struct file_operations diag_fops = { .write = diag_write, .open = diag_open, .release = diag_release, + .unlocked_ioctl = diag_ioctl, }; static struct miscdevice diag_device_fops = { @@ -447,6 +477,9 @@ static void diag_process_hdlc(struct diag_context *ctxt, void *_data, unsigned l #if ROUTE_TO_USERSPACE static int if_route_to_userspace(struct diag_context *ctxt, unsigned int cmd_id) { + unsigned long flags; + int i; + if (!ctxt->opened || cmd_id == 0) return 0; @@ -456,6 +489,17 @@ static int if_route_to_userspace(struct diag_context *ctxt, unsigned int cmd_id) if (cmd_id >= 0xfb && cmd_id <= 0xff) return 1; + spin_lock_irqsave(&ctxt->req_lock, flags); + for (i = 0; i < ARRAY_SIZE(ctxt->id_table); i++) + if (ctxt->id_table[i] == cmd_id) { + /* if the command id equals to any of registered ids, + * route to userspace to handle. + */ + spin_unlock_irqrestore(&ctxt->req_lock, flags); + return 1; + } + spin_unlock_irqrestore(&ctxt->req_lock, flags); + return 0; } #endif From a2d30c47196f724a437845ae9fc6eea64a88edf9 Mon Sep 17 00:00:00 2001 From: HK Chen Date: Wed, 10 Feb 2010 23:06:42 -0800 Subject: [PATCH 0520/2556] [ARM] qsd8k: audio: fix device auto reboot for first MT call (CDMA revs) Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/qdsp6/q6audio.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c index 83e5fb863aeac..e407da37c7601 100644 --- a/arch/arm/mach-msm/qdsp6/q6audio.c +++ b/arch/arm/mach-msm/qdsp6/q6audio.c @@ -1423,8 +1423,13 @@ struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, if (ac->flags & AUDIO_FLAG_WRITE) { audio_rx_path_refcount++; - if (audio_rx_path_refcount == 1) + if (audio_rx_path_refcount == 1) { _audio_rx_clk_enable(); + audio_update_acdb(audio_rx_device_id, acdb_id); + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, audio_rx_device_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); + } } else { /* TODO: consider concurrency with voice call */ tx_clk_freq = rate; @@ -1448,9 +1453,19 @@ struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, msleep(1); } - if (ac->flags & AUDIO_FLAG_WRITE) - if (audio_rx_path_refcount == 1) - _audio_rx_path_enable(0, acdb_id); + if (ac->flags & AUDIO_FLAG_WRITE) { + if (audio_rx_path_refcount == 1) { + adie_enable(); + adie_set_path(adie, audio_rx_path_id, ADIE_PATH_RX); + adie_set_path_freq_plan(adie, ADIE_PATH_RX, 48000); + + adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_READY); + adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_ANALOG_READY); + + audio_rx_analog_enable(1); + } + } + mutex_unlock(&audio_path_lock); for (retry = 5;;retry--) { From 8de3f1b8959ac29b5292f78fa14f357034940974 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Fri, 12 Feb 2010 15:55:36 -0500 Subject: [PATCH 0521/2556] [ARM] msm: htc: Board file changes for RNDS USB ethernet support. Change-Id: Idb40029bc35a2749cf07a76fcd56aeb0445ab8fe Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/devices_htc.c | 52 ++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c index 7801570cc5025..f51c23a69980a 100644 --- a/arch/arm/mach-msm/devices_htc.c +++ b/arch/arm/mach-msm/devices_htc.c @@ -105,51 +105,57 @@ static struct platform_device usb_mass_storage_device = { }, }; -static char *usb_functions[] = { +static char *usb_functions_ums[] = { "usb_mass_storage", -#ifdef CONFIG_USB_ANDROID_RNDIS - "rndis", -#endif -#ifdef CONFIG_USB_ANDROID_ACM - "acm", -#endif }; -static char *usb_functions_adb[] = { +static char *usb_functions_ums_adb[] = { "usb_mass_storage", "adb", -#ifdef CONFIG_USB_ANDROID_RNDIS +}; + +static char *usb_functions_rndis_ums[] = { "rndis", -#endif -#ifdef CONFIG_USB_ANDROID_ACM - "acm", -#endif + "usb_mass_storage", }; -static char *usb_functions_all[] = { +static char *usb_functions_rndis_ums_adb[] = { + "rndis", "usb_mass_storage", "adb", -#ifdef CONFIG_USB_ANDROID_ACM - "acm", -#endif +}; + +static char *usb_functions_all[] = { #ifdef CONFIG_USB_ANDROID_RNDIS "rndis", #endif -#ifdef CONFIG_USB_ANDROID_DIAG - "diag", + "usb_mass_storage", + "adb", +#ifdef CONFIG_USB_ANDROID_ACM + "acm", #endif }; static struct android_usb_product usb_products[] = { { .product_id = 0x0c01, - .num_functions = ARRAY_SIZE(usb_functions), - .functions = usb_functions, + .num_functions = ARRAY_SIZE(usb_functions_ums), + .functions = usb_functions_ums, }, { .product_id = 0x0c02, - .num_functions = ARRAY_SIZE(usb_functions_adb), - .functions = usb_functions_adb, + .num_functions = ARRAY_SIZE(usb_functions_ums_adb), + .functions = usb_functions_ums_adb, + }, + { + .product_id = 0xfffe, /* FIXME need a real PID here */ + .num_functions = ARRAY_SIZE(usb_functions_rndis_ums), + .functions = usb_functions_rndis_ums, + }, + { + .product_id = 0xffff, /* FIXME need a real PID here */ + .num_functions = ARRAY_SIZE(usb_functions_rndis_ums_adb), + .functions = usb_functions_rndis_ums_adb, }, }; From 3f5c992e3bbb732030035d8599b7fc47afc366ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 9 Feb 2010 20:39:25 -0800 Subject: [PATCH 0522/2556] [ARM] Fix: Don't use domain manager mode when not needed. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some boot code passes kernel pointers to apis expecting user space pointers without calling set_fs first. Start with the kernel domain in manager mode so the system still boots. The previous patch changed the kernel domain to client mode, but since addr_limit was still set to KERNEL_DS, any code that called saved = get_fs(); set_fs(saved); would leave the kernel domain in manager mode and mask this problem. Change-Id: I9d9aecc3345402d475eb4b078880e50268d35535 Signed-off-by: Arve Hjønnevåg --- arch/arm/include/asm/thread_info.h | 2 +- arch/arm/kernel/head.S | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h index 2aa757b8c4dc9..bcb6d9d9919a1 100644 --- a/arch/arm/include/asm/thread_info.h +++ b/arch/arm/include/asm/thread_info.h @@ -76,7 +76,7 @@ struct thread_info { .preempt_count = INIT_PREEMPT_COUNT, \ .addr_limit = KERNEL_DS, \ .cpu_domain = domain_val(DOMAIN_USER, DOMAIN_CLIENT) | \ - domain_val(DOMAIN_KERNEL, DOMAIN_CLIENT) | \ + domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \ domain_val(DOMAIN_IO, DOMAIN_CLIENT), \ .restart_block = { \ .fn = do_no_restart_syscall, \ diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index 4b0ed98f74f53..610ba6cca8a0d 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S @@ -356,7 +356,7 @@ __enable_mmu: bic r0, r0, #CR_I #endif mov r5, #(domain_val(DOMAIN_USER, DOMAIN_CLIENT) | \ - domain_val(DOMAIN_KERNEL, DOMAIN_CLIENT) | \ + domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \ domain_val(DOMAIN_TABLE, DOMAIN_CLIENT) | \ domain_val(DOMAIN_IO, DOMAIN_CLIENT)) mcr p15, 0, r5, c3, c0, 0 @ load domain access register From dc645ea85e71790ce2864148bfb4459251d1f933 Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Thu, 3 Dec 2009 18:59:00 -0700 Subject: [PATCH 0523/2556] [ARM] msm: kgsl: switch to using per-fd page tables for GPU Instead of sharing a single, global page table, each GPU user (and fd) gets it's own set of page tables. This increases the per-context overhead in the driver, but greatly improves security and isolation. Change-Id: Ia3ad18204733cc9a4bae054f8f06cda697125640 Cc: Shubhraprakash Das Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl.c | 11 +- drivers/video/msm/gpu/kgsl/kgsl_device.h | 7 +- drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c | 5 +- drivers/video/msm/gpu/kgsl/kgsl_mmu.c | 25 +-- drivers/video/msm/gpu/kgsl/kgsl_mmu.h | 7 +- drivers/video/msm/gpu/kgsl/kgsl_pm4types.h | 1 + drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c | 17 +- drivers/video/msm/gpu/kgsl/kgsl_yamato.c | 169 ++++++++++--------- 8 files changed, 125 insertions(+), 117 deletions(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c index 01931c4748d6b..ec2da14d7b48d 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.c +++ b/drivers/video/msm/gpu/kgsl/kgsl.c @@ -251,11 +251,9 @@ static int kgsl_release(struct inode *inodep, struct file *filep) kgsl_remove_mem_entry(entry); if (private->pagetable != NULL) { -#ifdef PER_PROCESS_PAGE_TABLE kgsl_yamato_cleanup_pt(&kgsl_driver.yamato_device, private->pagetable); kgsl_mmu_destroypagetableobject(private->pagetable); -#endif private->pagetable = NULL; } @@ -310,22 +308,19 @@ static int kgsl_open(struct inode *inodep, struct file *filep) kgsl_hw_get_locked(); /*NOTE: this must happen after first_open */ -#ifdef PER_PROCESS_PAGE_TABLE private->pagetable = kgsl_mmu_createpagetableobject(&kgsl_driver.yamato_device.mmu); if (private->pagetable == NULL) { result = -ENOMEM; goto done; } - result = kgsl_yamato_setup_pt(device, private->pagetable); + result = kgsl_yamato_setup_pt(&kgsl_driver.yamato_device, + private->pagetable); if (result) { kgsl_mmu_destroypagetableobject(private->pagetable); - private->pagetable == NULL; + private->pagetable = NULL; goto done; } -#else - private->pagetable = kgsl_driver.yamato_device.mmu.hwpagetable; -#endif private->vmalloc_size = 0; done: kgsl_hw_put_locked(true); diff --git a/drivers/video/msm/gpu/kgsl/kgsl_device.h b/drivers/video/msm/gpu/kgsl/kgsl_device.h index d761bdf032bd5..eefb9e7a86e94 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_device.h +++ b/drivers/video/msm/gpu/kgsl/kgsl_device.h @@ -130,12 +130,9 @@ int kgsl_yamato_setup_pt(struct kgsl_device *device, int kgsl_yamato_cleanup_pt(struct kgsl_device *device, struct kgsl_pagetable *pagetable); #ifdef CONFIG_MSM_KGSL_MMU -int kgsl_yamato_setpagetable(struct kgsl_device *device); -int kgsl_yamato_tlbinvalidate(struct kgsl_device *device); +int kgsl_yamato_setstate(struct kgsl_device *device, uint32_t flags); #else -static inline int kgsl_yamato_tlbinvalidate(struct kgsl_device *device) -{ return 0; } -static inline int kgsl_yamato_setpagetable(struct kgsl_device *device) +int kgsl_yamato_setstate(struct kgsl_device *device, uint32_t flags) { return 0; } #endif diff --git a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c index c20a3e67376a9..d1412b5f7beae 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c @@ -1764,7 +1764,7 @@ kgsl_drawctxt_switch(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt, KGSL_CTXT_INFO("drawctxt flags %08x\n", drawctxt->flags); KGSL_CTXT_DBG("restore pagetable"); - kgsl_mmu_setpagetable(device, drawctxt->pagetable); + kgsl_mmu_setstate(device, drawctxt->pagetable); /* restore gmem. * (note: changes shader. shader must not already be restored.) @@ -1813,6 +1813,7 @@ kgsl_drawctxt_switch(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt, cmds[1] = drawctxt->bin_base_offset; kgsl_ringbuffer_issuecmds(device, 0, cmds, 2); - } + } else + kgsl_mmu_setstate(device, device->mmu.defaultpagetable); KGSL_CTXT_INFO("return\n"); } diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c index 04325f2ee5f00..6acc9e259134e 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c @@ -261,7 +261,7 @@ int kgsl_mmu_destroypagetableobject(struct kgsl_pagetable *pagetable) return 0; } -int kgsl_mmu_setpagetable(struct kgsl_device *device, +int kgsl_mmu_setstate(struct kgsl_device *device, struct kgsl_pagetable *pagetable) { int status = 0; @@ -278,7 +278,9 @@ int kgsl_mmu_setpagetable(struct kgsl_device *device, mmu->hwpagetable = pagetable; /* call device specific set page table */ - status = kgsl_yamato_setpagetable(mmu->device); + status = kgsl_yamato_setstate(mmu->device, + KGSL_MMUFLAGS_TLBFLUSH | + KGSL_MMUFLAGS_PTUPDATE); } } @@ -368,11 +370,10 @@ int kgsl_mmu_init(struct kgsl_device *device) kgsl_mmu_close(device); return status; } - mmu->dummyspace.gpuaddr = mmu->dummyspace.physaddr; kgsl_yamato_regwrite(device, REG_MH_MMU_TRAN_ERROR, - mmu->dummyspace.gpuaddr); + mmu->dummyspace.physaddr); mmu->defaultpagetable = kgsl_mmu_createpagetableobject(mmu); if (!mmu->defaultpagetable) { @@ -381,7 +382,12 @@ int kgsl_mmu_init(struct kgsl_device *device) return -ENOMEM; } mmu->hwpagetable = mmu->defaultpagetable; - status = kgsl_yamato_setpagetable(device); + kgsl_yamato_regwrite(device, REG_MH_MMU_PT_BASE, + mmu->hwpagetable->base.gpuaddr); + kgsl_yamato_regwrite(device, REG_MH_MMU_VA_RANGE, + (mmu->hwpagetable->va_base | + (mmu->hwpagetable->va_range >> 16))); + status = kgsl_yamato_setstate(device, KGSL_MMUFLAGS_TLBFLUSH); if (status) { kgsl_mmu_close(device); return status; @@ -472,7 +478,7 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, *gpuaddr = gen_pool_alloc(pagetable->pool, alloc_size); if (*gpuaddr == 0) { - KGSL_MEM_ERR("gen_pool_alloc failed\n"); + KGSL_MEM_ERR("gen_pool_alloc failed: %d\n", alloc_size); return -ENOMEM; } @@ -552,7 +558,7 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, /* Invalidate tlb only if current page table used by GPU is the * pagetable that we used to allocate */ if (pagetable == mmu->hwpagetable) - kgsl_yamato_tlbinvalidate(mmu->device); + kgsl_yamato_setstate(mmu->device, KGSL_MMUFLAGS_TLBFLUSH); KGSL_MEM_VDBG("return %d\n", 0); @@ -597,7 +603,8 @@ kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, unsigned int gpuaddr, /* Invalidate tlb only if current page table used by GPU is the * pagetable that we used to allocate */ if (pagetable == pagetable->mmu->hwpagetable) - kgsl_yamato_tlbinvalidate(pagetable->mmu->device); + kgsl_yamato_setstate(pagetable->mmu->device, + KGSL_MMUFLAGS_TLBFLUSH); KGSL_MEM_VDBG("return %d\n", 0); @@ -633,9 +640,7 @@ int kgsl_mmu_close(struct kgsl_device *device) mmu->flags &= ~KGSL_FLAGS_STARTED; mmu->flags &= ~KGSL_FLAGS_INITIALIZED; mmu->flags &= ~KGSL_FLAGS_INITIALIZED0; -#ifndef PER_PROCESS_PAGE_TABLE kgsl_mmu_destroypagetableobject(mmu->defaultpagetable); -#endif mmu->defaultpagetable = NULL; } diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.h b/drivers/video/msm/gpu/kgsl/kgsl_mmu.h index e271fee8f3c33..d70f24a1e514a 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_mmu.h +++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.h @@ -27,6 +27,9 @@ #define GSL_PT_PAGE_WV 0x00000001 #define GSL_PT_PAGE_RV 0x00000002 #define GSL_PT_PAGE_DIRTY 0x00000004 +/* MMU Flags */ +#define KGSL_MMUFLAGS_TLBFLUSH 0x10000000 +#define KGSL_MMUFLAGS_PTUPDATE 0x20000000 extern unsigned int kgsl_cache_enable; @@ -96,8 +99,8 @@ struct kgsl_pagetable *kgsl_mmu_createpagetableobject(struct kgsl_mmu *mmu); int kgsl_mmu_destroypagetableobject(struct kgsl_pagetable *pagetable); -int kgsl_mmu_setpagetable(struct kgsl_device *device, - struct kgsl_pagetable *pagetable); +int kgsl_mmu_setstate(struct kgsl_device *device, + struct kgsl_pagetable *pagetable); #ifdef CONFIG_MSM_KGSL_MMU int kgsl_mmu_map(struct kgsl_pagetable *pagetable, diff --git a/drivers/video/msm/gpu/kgsl/kgsl_pm4types.h b/drivers/video/msm/gpu/kgsl/kgsl_pm4types.h index 7c735bd76f610..bc224b410ff57 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_pm4types.h +++ b/drivers/video/msm/gpu/kgsl/kgsl_pm4types.h @@ -151,6 +151,7 @@ * the 3D_DRAW_INDX_BIN packet */ #define PM4_SET_BIN_BASE_OFFSET 0x4B +#define PM4_SET_PROTECTED_MODE 0x5f /* sets the register protection mode */ /* packet header building macros */ #define pm4_type0_packet(regindx, cnt) \ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c index 79d90cbbc4ef6..264561c4d7b52 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c @@ -346,7 +346,7 @@ static int kgsl_ringbuffer_start(struct kgsl_ringbuffer *rb) kgsl_sharedmem_set(&rb->memptrs_desc, 0, 0, sizeof(struct kgsl_rbmemptrs)); - kgsl_sharedmem_set(&rb->buffer_desc, 0, 0x12341234, + kgsl_sharedmem_set(&rb->buffer_desc, 0, 0xAA, (rb->sizedwords << 2)); kgsl_yamato_regwrite(device, REG_CP_RB_WPTR_BASE, @@ -596,16 +596,15 @@ kgsl_ringbuffer_addcmds(struct kgsl_ringbuffer *rb, /* reserve space to temporarily turn off protected mode * error checking if needed */ - pmodesizedwords = pmodeoff ? 8 : 0; + pmodesizedwords = pmodeoff ? 4 : 0; ringcmds = kgsl_ringbuffer_allocspace(rb, pmodesizedwords + sizedwords + 6); if (pmodeoff) { /* disable protected mode error checking */ - *ringcmds++ = pm4_type3_packet(PM4_ME_INIT, 2); - *ringcmds++ = 0x00000080; - *ringcmds++ = 0x00000000; + *ringcmds++ = pm4_type3_packet(PM4_SET_PROTECTED_MODE, 1); + *ringcmds++ = 0; } memcpy(ringcmds, cmds, (sizedwords << 2)); @@ -613,13 +612,9 @@ kgsl_ringbuffer_addcmds(struct kgsl_ringbuffer *rb, ringcmds += sizedwords; if (pmodeoff) { - *ringcmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *ringcmds++ = 0; - /* re-enable protected mode error checking */ - *ringcmds++ = pm4_type3_packet(PM4_ME_INIT, 2); - *ringcmds++ = 0x00000080; - *ringcmds++ = GSL_RB_PROTECTED_MODE_CONTROL; + *ringcmds++ = pm4_type3_packet(PM4_SET_PROTECTED_MODE, 1); + *ringcmds++ = 1; } rb->timestamp++; diff --git a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c index a8c560b12f122..d8d47d0f3c071 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c @@ -242,6 +242,9 @@ int kgsl_yamato_cleanup_pt(struct kgsl_device *device, kgsl_mmu_unmap(pagetable, device->memstore.gpuaddr, device->memstore.size); + kgsl_mmu_unmap(pagetable, device->mmu.dummyspace.gpuaddr, + device->mmu.dummyspace.size); + return 0; } @@ -254,6 +257,7 @@ int kgsl_yamato_setup_pt(struct kgsl_device *device, BUG_ON(device->ringbuffer.buffer_desc.physaddr == 0); BUG_ON(device->ringbuffer.memptrs_desc.physaddr == 0); BUG_ON(device->memstore.physaddr == 0); + BUG_ON(device->mmu.dummyspace.physaddr == 0); result = kgsl_mmu_map(pagetable, device->ringbuffer.buffer_desc.physaddr, @@ -291,8 +295,25 @@ int kgsl_yamato_setup_pt(struct kgsl_device *device, device->memstore.gpuaddr = gpuaddr; BUG_ON(device->memstore.gpuaddr != gpuaddr); + result = kgsl_mmu_map(pagetable, + device->mmu.dummyspace.physaddr, + device->mmu.dummyspace.size, + GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, &gpuaddr, + KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN4K); + + if (result) + goto unmap_memstore_desc; + + if (device->mmu.dummyspace.gpuaddr == 0) + device->mmu.dummyspace.gpuaddr = gpuaddr; + BUG_ON(device->mmu.dummyspace.gpuaddr != gpuaddr); + return result; +unmap_memstore_desc: + kgsl_mmu_unmap(pagetable, device->memstore.gpuaddr, + device->memstore.size); + unmap_memptrs_desc: kgsl_mmu_unmap(pagetable, device->ringbuffer.memptrs_desc.gpuaddr, device->ringbuffer.memptrs_desc.size); @@ -305,101 +326,92 @@ int kgsl_yamato_setup_pt(struct kgsl_device *device, } #ifdef CONFIG_MSM_KGSL_MMU -int kgsl_yamato_tlbinvalidate(struct kgsl_device *device) +int kgsl_yamato_setstate(struct kgsl_device *device, uint32_t flags) { -#ifdef PER_PROCESS_PAGE_TABLE - unsigned int link[2]; -#endif + unsigned int link[32]; + unsigned int *cmds = &link[0]; + int sizedwords = 0; unsigned int mh_mmu_invalidate = 0x00000003; /*invalidate all and tc */ KGSL_MEM_DBG("device %p ctxt %p pt %p\n", device, device->drawctxt_active, device->mmu.hwpagetable); -#ifdef PER_PROCESS_PAGE_TABLE - /* if there is an active draw context, invalidate via command stream, - * otherwise invalidate via direct register writes + /* if possible, set via command stream, + * otherwise set via direct register writes */ if (device->drawctxt_active) { - link[0] = pm4_type0_packet(REG_MH_MMU_INVALIDATE, 1); - link[1] = mh_mmu_invalidate; - KGSL_MEM_DBG("cmds\n"); - kgsl_ringbuffer_issuecmds(device, 1, - &link[0], 2); - } else { -#endif - KGSL_MEM_DBG("regs\n"); + if (flags & KGSL_MMUFLAGS_PTUPDATE) { + /* wait for graphics pipe to be idle */ + *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmds++ = 0x00000000; + + /* set page table base */ + *cmds++ = pm4_type0_packet(REG_MH_MMU_PT_BASE, 1); + *cmds++ = device->mmu.hwpagetable->base.gpuaddr; + sizedwords += 4; + } - kgsl_yamato_regwrite(device, REG_MH_MMU_INVALIDATE, - mh_mmu_invalidate); -#ifdef PER_PROCESS_PAGE_TABLE - } -#endif - return 0; -} -#endif + if (flags & KGSL_MMUFLAGS_TLBFLUSH) { + *cmds++ = pm4_type0_packet(REG_MH_MMU_INVALIDATE, 1); + *cmds++ = mh_mmu_invalidate; + sizedwords += 2; + } -#ifdef CONFIG_MSM_KGSL_MMU -int kgsl_yamato_setpagetable(struct kgsl_device *device) -{ - unsigned int link[27]; - unsigned int mh_mmu_invalidate = 0x00000003; /*invalidate all and tc */ + if (flags & KGSL_MMUFLAGS_PTUPDATE) { + /* HW workaround: to resolve MMU page fault interrupts + * caused by the VGT.It prevents the CP PFP from filling + * the VGT DMA request fifo too early,thereby ensuring + * that the VGT will not fetch vertex/bin data until + * after the page table base register has been updated. + * + * Two null DRAW_INDX_BIN packets are inserted right + * after the page table base update, followed by a + * wait for idle. The null packets will fill up the + * VGT DMA request fifo and prevent any further + * vertex/bin updates from occurring until the wait + * has finished. */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = (0x4 << 16) | + (REG_PA_SU_SC_MODE_CNTL - 0x2000); + *cmds++ = 0; /* disable faceness generation */ + *cmds++ = pm4_type3_packet(PM4_SET_BIN_BASE_OFFSET, 1); + *cmds++ = device->mmu.dummyspace.gpuaddr; + *cmds++ = pm4_type3_packet(PM4_DRAW_INDX_BIN, 6); + *cmds++ = 0; /* viz query info */ + *cmds++ = 0x0003C004; /* draw indicator */ + *cmds++ = 0; /* bin base */ + *cmds++ = 3; /* bin size */ + *cmds++ = device->mmu.dummyspace.gpuaddr; /* dma base */ + *cmds++ = 6; /* dma size */ + *cmds++ = pm4_type3_packet(PM4_DRAW_INDX_BIN, 6); + *cmds++ = 0; /* viz query info */ + *cmds++ = 0x0003C004; /* draw indicator */ + *cmds++ = 0; /* bin base */ + *cmds++ = 3; /* bin size */ + /* dma base */ + *cmds++ = device->mmu.dummyspace.gpuaddr; + *cmds++ = 6; /* dma size */ + *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmds++ = 0x00000000; + sizedwords += 21; + } - KGSL_MEM_DBG("device %p ctxt %p pt %p\n", - device, - device->drawctxt_active, - device->mmu.hwpagetable); - /* if there is an active draw context, set via command stream, - * otherwise set via direct register writes - */ - if (device->drawctxt_active) { - KGSL_MEM_DBG("cmds\n"); - /* wait for graphics pipe to be idle */ - link[0] = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - link[1] = 0x00000000; - - /* set page table base */ - link[2] = pm4_type0_packet(REG_MH_MMU_PT_BASE, 1); - link[3] = device->mmu.hwpagetable->base.gpuaddr; - - link[4] = pm4_type3_packet(PM4_SET_CONSTANT, 2); - link[5] = (0x4 << 16) | (REG_PA_SU_SC_MODE_CNTL - 0x2000); - link[6] = 0; /* disable faceness generation */ - link[7] = pm4_type3_packet(PM4_SET_BIN_BASE_OFFSET, 1); - link[8] = device->mmu.dummyspace.gpuaddr; - link[9] = pm4_type3_packet(PM4_DRAW_INDX_BIN, 6); - link[10] = 0; /* viz query info */ - link[11] = 0x0003C004; /* draw indicator */ - link[12] = 0; /* bin base */ - link[13] = 3; /* bin size */ - link[14] = device->mmu.dummyspace.gpuaddr; /* dma base */ - link[15] = 6; /* dma size */ - link[16] = pm4_type3_packet(PM4_DRAW_INDX_BIN, 6); - link[17] = 0; /* viz query info */ - link[18] = 0x0003C004; /* draw indicator */ - link[19] = 0; /* bin base */ - link[20] = 3; /* bin size */ - link[21] = device->mmu.dummyspace.gpuaddr; /* dma base */ - link[22] = 6; /* dma size */ - link[23] = pm4_type0_packet(REG_MH_MMU_INVALIDATE, 1); - link[24] = mh_mmu_invalidate; - link[25] = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - link[26] = 0x00000000; - - kgsl_ringbuffer_issuecmds(device, 1, - &link[0], 27); + kgsl_ringbuffer_issuecmds(device, 1, &link[0], sizedwords); } else { KGSL_MEM_DBG("regs\n"); - kgsl_yamato_regwrite(device, REG_MH_MMU_PT_BASE, + if (flags & KGSL_MMUFLAGS_PTUPDATE) { + kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT); + kgsl_yamato_regwrite(device, REG_MH_MMU_PT_BASE, device->mmu.hwpagetable->base.gpuaddr); - kgsl_yamato_regwrite(device, REG_MH_MMU_VA_RANGE, - (device->mmu.hwpagetable-> - va_base | (device->mmu.hwpagetable-> - va_range >> 16))); - kgsl_yamato_regwrite(device, REG_MH_MMU_INVALIDATE, - mh_mmu_invalidate); + } + + if (flags & KGSL_MMUFLAGS_TLBFLUSH) { + kgsl_yamato_regwrite(device, REG_MH_MMU_INVALIDATE, + mh_mmu_invalidate); + } } return 0; @@ -535,7 +547,6 @@ int kgsl_yamato_init(struct kgsl_device *device, struct kgsl_devconfig *config) kgsl_sharedmem_set(&device->memstore, 0, 0, device->memstore.size); kgsl_yamato_regwrite(device, REG_RBBM_DEBUG, 0x00080000); - pr_info("msm_kgsl: initilized dev=%d mmu=%s\n", device->id, kgsl_mmu_isenabled(&device->mmu) ? "on" : "off"); From b920e9af2c935ce06ba7bc56936e5802d6de9e51 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 16 Feb 2010 18:02:11 -0800 Subject: [PATCH 0524/2556] [ARM] msm: mahimahi: Bump the reserved gpu memory from 3MB to 5MB This is necessary in order to support more than a handful of contexts due to new per-context page table support. Change-Id: I4e657e16f27919753c60b55f5bb3e9d5008cfd7a Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-mahimahi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-mahimahi.h b/arch/arm/mach-msm/board-mahimahi.h index d8d89527b471f..db810129a5a17 100644 --- a/arch/arm/mach-msm/board-mahimahi.h +++ b/arch/arm/mach-msm/board-mahimahi.h @@ -31,7 +31,7 @@ #define MSM_EBI1_BANK0_SIZE 0x0E000000 #define MSM_GPU_MEM_BASE 0x2DB00000 -#define MSM_GPU_MEM_SIZE 0x00300000 +#define MSM_GPU_MEM_SIZE 0x00500000 #define MSM_EBI1_BANK1_BASE 0x30000000 #define MSM_EBI1_BANK1_SIZE 0x10000000 From 15fa9e6e56038a81646d94f3856fca9eef360851 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Wed, 17 Feb 2010 16:45:04 -0500 Subject: [PATCH 0525/2556] [ARM] msm: usb: gadget: Fix reference after free bug in handle_endpoint() Change-Id: I5ca4121f43d002719c16fa8c0bcbdd67695bb189 Signed-off-by: Mike Lockwood --- drivers/usb/gadget/msm72k_udc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index d625e056a381f..2c9d269367839 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -793,14 +793,15 @@ static void handle_endpoint(struct usb_info *ui, unsigned bit) } req->busy = 0; req->live = 0; - if (req->dead) - do_free_req(ui, req); if (req->req.complete) { spin_unlock_irqrestore(&ui->lock, flags); req->req.complete(&ept->ep, &req->req); spin_lock_irqsave(&ui->lock, flags); } + + if (req->dead) + do_free_req(ui, req); } spin_unlock_irqrestore(&ui->lock, flags); } From e89db793154557b5e7af8e2554a9669e1dcf4b85 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 17 Feb 2010 16:26:37 -0800 Subject: [PATCH 0526/2556] [ARM] msm: mdp: Fix blits with FLIP_UD set The calculation of the rotated y address was incorrect because the dst_ystride variable had already been munged into hardware format. Change-Id: I451205e915d2ef8166c3389d67c34138aeccc59d Signed-off-by: Colin Cross --- drivers/video/msm/mdp_ppp.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c index ee53e4952c0bf..f13e63c6d664a 100644 --- a/drivers/video/msm/mdp_ppp.c +++ b/drivers/video/msm/mdp_ppp.c @@ -587,12 +587,6 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, regs.dst_ystride = req->dst.width * regs.dst_bpp; set_dst_region(&req->dst_rect, ®s); - /* for simplicity, always write the chroma stride */ - regs.src_ystride &= 0x3fff; - regs.src_ystride |= regs.src_ystride << 16; - regs.dst_ystride &= 0x3fff; - regs.dst_ystride |= regs.dst_ystride << 16; - if (!valid_src_dst(src_start, src_len, dst_start, dst_len, req, ®s)) { printk(KERN_ERR "mdp_ppp: final src or dst location is " @@ -627,6 +621,14 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, if (mdp_ppp_cfg_edge_cond(req, ®s)) return -EINVAL; + /* for simplicity, always write the chroma stride */ + regs.src_ystride &= 0x3fff; + regs.src_ystride |= regs.src_ystride << 16; + regs.dst_ystride &= 0x3fff; + regs.dst_ystride |= regs.dst_ystride << 16; + regs.bg_ystride &= 0x3fff; + regs.bg_ystride |= regs.bg_ystride << 16; + #if PPP_DUMP_BLITS pr_info("%s: sending blit\n", __func__); #endif From 8c8ba0655571ceb83536c2de33b8d73e01afeb8b Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Wed, 17 Feb 2010 16:50:29 -0800 Subject: [PATCH 0527/2556] [ARM] qsd8k: add the ability to override the MPLL frequency Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/acpuclock-scorpion.c | 7 ++++++- arch/arm/mach-msm/include/mach/board.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/acpuclock-scorpion.c b/arch/arm/mach-msm/acpuclock-scorpion.c index 7177547c2a6f4..691acdeaad74c 100644 --- a/arch/arm/mach-msm/acpuclock-scorpion.c +++ b/arch/arm/mach-msm/acpuclock-scorpion.c @@ -104,6 +104,8 @@ struct clkctl_acpu_speed *acpu_stby = &acpu_freq_tbl[2]; #define IS_ACPU_STANDBY(x) (((x)->clk_cfg == acpu_stby->clk_cfg) && \ ((x)->clk_sel == acpu_stby->clk_sel)) +struct clkctl_acpu_speed *acpu_mpll = &acpu_freq_tbl[2]; + #ifdef CONFIG_CPU_FREQ_TABLE static struct cpufreq_frequency_table freq_table[ARRAY_SIZE(acpu_freq_tbl)]; @@ -122,7 +124,7 @@ static void __init acpuclk_init_cpufreq_table(void) vdd = acpu_freq_tbl[i].vdd; /* Allow mpll and the first scpll speeds */ - if (acpu_freq_tbl[i].acpu_khz == 245000 || + if (acpu_freq_tbl[i].acpu_khz == acpu_mpll->acpu_khz || acpu_freq_tbl[i].acpu_khz == 384000) { freq_table[i].frequency = acpu_freq_tbl[i].acpu_khz; continue; @@ -484,6 +486,9 @@ void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *clkdata) drv_state.power_collapse_khz = clkdata->power_collapse_khz; drv_state.wait_for_irq_khz = clkdata->wait_for_irq_khz; + if (clkdata->mpll_khz) + acpu_mpll->acpu_khz = clkdata->mpll_khz; + acpuclk_init(); acpuclk_init_cpufreq_table(); } diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index 6a075c70298c4..926a2dfddb103 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -27,6 +27,7 @@ struct msm_acpu_clock_platform_data uint32_t acpu_switch_time_us; uint32_t max_speed_delta_khz; uint32_t vdd_switch_time_us; + unsigned long mpll_khz; unsigned long power_collapse_khz; unsigned long wait_for_irq_khz; }; From 590ea7364f7807934d55f81145bffedd096ee9de Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Wed, 17 Feb 2010 16:58:17 -0800 Subject: [PATCH 0528/2556] [ARM] qsd8k: mahimahi: update the MPLL clock value on CDMA (235.93 MHz) Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 7f195c75683d1..a554c4b7f4db2 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -929,6 +929,16 @@ static struct msm_acpu_clock_platform_data mahimahi_clock_data = { .vdd_switch_time_us = 62, .power_collapse_khz = 245000, .wait_for_irq_khz = 245000, + .mpll_khz = 245000 +}; + +static struct msm_acpu_clock_platform_data mahimahi_cdma_clock_data = { + .acpu_switch_time_us = 20, + .max_speed_delta_khz = 256000, + .vdd_switch_time_us = 62, + .power_collapse_khz = 235930, + .wait_for_irq_khz = 235930, + .mpll_khz = 235930 }; static ssize_t mahimahi_virtual_keys_show(struct kobject *kobj, @@ -996,7 +1006,10 @@ static void __init mahimahi_init(void) msm_hw_reset_hook = mahimahi_reset; - msm_acpu_clock_init(&mahimahi_clock_data); + if (is_cdma_version(system_rev)) + msm_acpu_clock_init(&mahimahi_cdma_clock_data); + else + msm_acpu_clock_init(&mahimahi_clock_data); msm_serial_debug_init(MSM_UART1_PHYS, INT_UART1, &msm_device_uart1.dev, 1, MSM_GPIO_TO_INT(139)); From fd91e4c7f0ae490fdde541d4a4572a0ce086f3b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 27 Jan 2010 20:25:43 -0800 Subject: [PATCH 0529/2556] [ARM] msm: mahimahi: Add touchscreen parameters for firmware version 5. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also change the x-axis parameters slightly for the old touchscreen. Change-Id: Ifc9bf5ca98361cc4c389632fe589fe01e8c16319 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/board-mahimahi.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index a554c4b7f4db2..896d973f048a3 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -379,10 +379,19 @@ struct cy8c_i2c_platform_data mahimahi_cy8c_ts_data = { static struct synaptics_i2c_rmi_platform_data mahimahi_synaptics_ts_data[] = { { + .version = 0x105, .power = mahimahi_ts_power, .flags = SYNAPTICS_FLIP_Y, - .inactive_left = -25 * 0x10000 / 480, - .inactive_right = -20 * 0x10000 / 480, + .inactive_left = -15 * 0x10000 / 480, + .inactive_right = -15 * 0x10000 / 480, + .inactive_top = -15 * 0x10000 / 800, + .inactive_bottom = -50 * 0x10000 / 800, + .sensitivity_adjust = 9, + }, + { + .flags = SYNAPTICS_FLIP_Y, + .inactive_left = -15 * 0x10000 / 480, + .inactive_right = -15 * 0x10000 / 480, .inactive_top = -15 * 0x10000 / 800, .inactive_bottom = -40 * 0x10000 / 800, .sensitivity_adjust = 12, From 707f240376095361bb8df8aea9ddb19aa3935a84 Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Fri, 19 Feb 2010 18:14:14 -0800 Subject: [PATCH 0530/2556] power: ds2784: Change workqueue to freezable. Prevent i2c errors when making transactions while bridge is suspended. Wait until drivers have resumed before executing workqueue. Change-Id: I78c787fd5dd09fc7700f3093341532fe23f20eb8 Signed-off-by: Mike Chan --- drivers/power/ds2784_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/ds2784_battery.c b/drivers/power/ds2784_battery.c index 0f8a34b7c671c..bc1b4d7c56252 100755 --- a/drivers/power/ds2784_battery.c +++ b/drivers/power/ds2784_battery.c @@ -663,7 +663,7 @@ static int ds2784_battery_probe(struct platform_device *pdev) goto fail_register; INIT_WORK(&di->monitor_work, ds2784_battery_work); - di->monitor_wqueue = create_singlethread_workqueue(dev_name(&pdev->dev)); + di->monitor_wqueue = create_freezeable_workqueue(dev_name(&pdev->dev)); /* init to something sane */ di->last_poll = alarm_get_elapsed_realtime(); From d8fe1fa5b45432b614322bc521ebd76ffc12b516 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 23 Feb 2010 15:00:49 -0800 Subject: [PATCH 0531/2556] [ARM] msm: qdsp5: clean up read-event functions, add error checks -- read_event_16, read_event_32, and read_modem_event accept the size of the buffer in bytes, not in numbers of uint16_t or uint32_t -- read_modem_event now checks the length of the buffer -- read_event_16 and read_event_32 print out a warning if the buffer is too small Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/qdsp5/adsp.c | 47 +++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-msm/qdsp5/adsp.c b/arch/arm/mach-msm/qdsp5/adsp.c index b45f52c9840e1..9dc8945b2cba5 100644 --- a/arch/arm/mach-msm/qdsp5/adsp.c +++ b/arch/arm/mach-msm/qdsp5/adsp.c @@ -552,11 +552,17 @@ int msm_adsp_write(struct msm_adsp_module *module, unsigned dsp_queue_addr, #ifdef CONFIG_MSM_ADSP_REPORT_EVENTS static void *modem_event_addr; #if CONFIG_MSM_AMSS_VERSION >= 6350 -static void read_modem_event(void *buf, size_t len) +static void read_modem_event(void *buf, size_t size) { uint32_t *dptr = buf; struct rpc_adsp_rtos_modem_to_app_args_t *sptr; struct adsp_rtos_mp_mtoa_type *pkt_ptr; + size_t len = size / 4; + + if (len < 3) { + pr_err("%s: invalid length %d\n", __func__, len); + return; + } sptr = modem_event_addr; pkt_ptr = &sptr->mtoa_pkt.adsp_rtos_mp_mtoa_data.mp_mtoa_packet; @@ -566,11 +572,16 @@ static void read_modem_event(void *buf, size_t len) dptr[2] = be32_to_cpu(pkt_ptr->image); } #else -static void read_modem_event(void *buf, size_t len) +static void read_modem_event(void *buf, size_t size) { uint32_t *dptr = buf; struct rpc_adsp_rtos_modem_to_app_args_t *sptr = modem_event_addr; + size_t len = size / 4; + if (len < 3) { + pr_err("%s: invalid length %d\n", __func__, len); + return; + } dptr[0] = be32_to_cpu(sptr->event); dptr[1] = be32_to_cpu(sptr->module); dptr[2] = be32_to_cpu(sptr->image); @@ -782,27 +793,33 @@ static int adsp_rpc_thread(void *data) do_exit(0); } -static size_t read_event_size; +static size_t read_event_len; static void *read_event_addr; -static void read_event_16(void *buf, size_t len) +static void read_event_16(void *buf, size_t size) { uint16_t *dst = buf; uint16_t *src = read_event_addr; - len /= 2; - if (len > read_event_size) - len = read_event_size; + size_t len = size / 2; + if (len > read_event_len) + len = read_event_len; + else if (len < read_event_len) + pr_warning("%s: event bufer length too small (%d < %d)\n", + __func__, len, read_event_len); while (len--) *dst++ = *src++; } -static void read_event_32(void *buf, size_t len) +static void read_event_32(void *buf, size_t size) { uint32_t *dst = buf; uint32_t *src = read_event_addr; - len /= 2; - if (len > read_event_size) - len = read_event_size; + size_t len = size / 4; + if (len > read_event_len) + len = read_event_len; + else if (len < read_event_len) + pr_warning("%s: event bufer length too small (%d < %d)\n", + __func__, len, read_event_len); while (len--) *dst++ = *src++; } @@ -821,18 +838,18 @@ static int adsp_rtos_read_ctrl_word_cmd_tast_to_h_v( uint32_t tmp = *dsp_addr32++; rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8; msg_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M); - read_event_size = tmp >> 16; + read_event_len = tmp >> 16; read_event_addr = dsp_addr32; - msg_length = read_event_size * sizeof(uint32_t); + msg_length = read_event_len * sizeof(uint32_t); func = read_event_32; } else { uint16_t *dsp_addr16 = dsp_addr; uint16_t tmp = *dsp_addr16++; rtos_task_id = (tmp & ADSP_RTOS_READ_CTRL_WORD_TASK_ID_M) >> 8; msg_id = tmp & ADSP_RTOS_READ_CTRL_WORD_MSG_ID_M; - read_event_size = *dsp_addr16++; + read_event_len = *dsp_addr16++; read_event_addr = dsp_addr16; - msg_length = read_event_size * sizeof(uint16_t); + msg_length = read_event_len * sizeof(uint16_t); func = read_event_16; } From 139e3b370d575e2e6528320ac9fa5cd12e3ad535 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 23 Feb 2010 15:13:33 -0800 Subject: [PATCH 0532/2556] [ARM] msm: qdsp5: audio_in: pass large-enough buffers to the event callbacks Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/qdsp5/audio_in.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/qdsp5/audio_in.c b/arch/arm/mach-msm/qdsp5/audio_in.c index 35a7f7df06829..2a67209e19263 100644 --- a/arch/arm/mach-msm/qdsp5/audio_in.c +++ b/arch/arm/mach-msm/qdsp5/audio_in.c @@ -248,7 +248,7 @@ static int audio_in_disable(struct audio_in *audio) static void audpre_dsp_event(void *data, unsigned id, size_t len, void (*getevent)(void *ptr, size_t len)) { - uint16_t msg[2]; + uint16_t msg[6]; /* may be a 32-bit event, which we ignore */ getevent(msg, sizeof(msg)); switch (id) { @@ -304,7 +304,7 @@ static void audrec_dsp_event(void *data, unsigned id, size_t len, void (*getevent)(void *ptr, size_t len)) { struct audio_in *audio = data; - uint16_t msg[3]; + uint16_t msg[6]; /* may be a 32-bit event, which we ignore */ getevent(msg, sizeof(msg)); switch (id) { From 8baff4b87b2c424897d1c8b8a70a3f2ef8bd75f1 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Tue, 23 Feb 2010 15:53:34 -0800 Subject: [PATCH 0533/2556] [ARM] mahimahi: Set rfkill hardware block to false. We do not have a hardware block for the Bluetooth chip on mahimahi. This was a mistake made switching from the 2.6.29 to 2.6.32 rfkill API. Change-Id: I817fb2b7d6cdab968f74b2298907f18ac02d5bd9 Signed-off-by: Nick Pelly --- arch/arm/mach-msm/board-mahimahi-rfkill.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-mahimahi-rfkill.c b/arch/arm/mach-msm/board-mahimahi-rfkill.c index 870c4bad15385..05c9bb0b4d556 100644 --- a/arch/arm/mach-msm/board-mahimahi-rfkill.c +++ b/arch/arm/mach-msm/board-mahimahi-rfkill.c @@ -63,7 +63,7 @@ static int mahimahi_rfkill_probe(struct platform_device *pdev) goto err_rfkill_alloc; } - rfkill_set_states(bt_rfk, default_state, default_state); + rfkill_set_states(bt_rfk, default_state, false); /* userspace cannot take exclusive control */ From fb3efcfa9aaa751a6faf886b7e0506c6854e850e Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Tue, 23 Feb 2010 15:53:34 -0800 Subject: [PATCH 0534/2556] [ARM] trout: Set rfkill hardware block to false. We do not have a hardware block for the Bluetooth chip on trout. This was a mistake made switching from the 2.6.29 to 2.6.32 rfkill API. Change-Id: I86a6a2be5caacd8e7f9c95376dcac1cb0c21779e Signed-off-by: Nick Pelly --- arch/arm/mach-msm/board-trout-rfkill.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-trout-rfkill.c b/arch/arm/mach-msm/board-trout-rfkill.c index 1a0d9bef0246c..e68eb2ae4c517 100644 --- a/arch/arm/mach-msm/board-trout-rfkill.c +++ b/arch/arm/mach-msm/board-trout-rfkill.c @@ -56,7 +56,7 @@ static int trout_rfkill_probe(struct platform_device *pdev) if (!bt_rfk) return -ENOMEM; - rfkill_set_states(bt_rfk, default_state, default_state); + rfkill_set_states(bt_rfk, default_state, false); /* userspace cannot take exclusive control */ From cb589bd5ce8089b58e465a1117a0515467dcdbb9 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Tue, 23 Feb 2010 15:53:34 -0800 Subject: [PATCH 0535/2556] [ARM] sapphire: Set rfkill hardware block to false. We do not have a hardware block for the Bluetooth chip on sapphire. This was a mistake made switching from the 2.6.29 to 2.6.32 rfkill API. Change-Id: Ifacf416e37a52b550911a2fec6329a93c7a7475a Signed-off-by: Nick Pelly --- arch/arm/mach-msm/board-sapphire-rfkill.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-sapphire-rfkill.c b/arch/arm/mach-msm/board-sapphire-rfkill.c index a3e2a64d0ed1b..2fd6ea198e3df 100644 --- a/arch/arm/mach-msm/board-sapphire-rfkill.c +++ b/arch/arm/mach-msm/board-sapphire-rfkill.c @@ -62,7 +62,7 @@ static int sapphire_rfkill_probe(struct platform_device *pdev) /* userspace cannot take exclusive control */ - rfkill_set_states(bt_rfk, default_state, default_state); + rfkill_set_states(bt_rfk, default_state, false); rc = rfkill_register(bt_rfk); From 29fdf8bcb75360a2cf9d943d3c3c12e5358c95fe Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 22 Feb 2010 23:29:20 -0800 Subject: [PATCH 0536/2556] [ARM] msm: kgsl: properly cleanup when pagetable creation fails Change-Id: I017e36ec89e2f9435c60eae4ccaf0875824ed2b8 Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl_mmu.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c index 6acc9e259134e..a52e51ee26e79 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c @@ -207,14 +207,14 @@ struct kgsl_pagetable *kgsl_mmu_createpagetableobject(struct kgsl_mmu *mmu) pagetable->pool = gen_pool_create(KGSL_PAGESIZE_SHIFT, -1); if (pagetable->pool == NULL) { KGSL_MEM_ERR("Unable to allocate virtualaddr pool.\n"); - return NULL; + goto err_gen_pool_create; } if (gen_pool_add(pagetable->pool, pagetable->va_base, pagetable->va_range, -1)) { KGSL_MEM_ERR("gen_pool_create failed for pagetable %p\n", pagetable); - goto done; + goto err_gen_pool_add; } /* allocate page table memory */ @@ -224,19 +224,26 @@ struct kgsl_pagetable *kgsl_mmu_createpagetableobject(struct kgsl_mmu *mmu) pagetable->max_entries * GSL_PTE_SIZE, &pagetable->base); - if (status == 0) { - /* reset page table entries - * -- all pte's are marked as not dirty initially - */ - kgsl_sharedmem_set(&pagetable->base, 0, 0, - pagetable->base.size); + if (status) { + KGSL_MEM_ERR("cannot alloc page tables\n"); + goto err_kgsl_sharedmem_alloc; } + + /* reset page table entries + * -- all pte's are marked as not dirty initially + */ + kgsl_sharedmem_set(&pagetable->base, 0, 0, pagetable->base.size); pagetable->base.gpuaddr = pagetable->base.physaddr; KGSL_MEM_VDBG("return %p\n", pagetable); return pagetable; -done: + +err_kgsl_sharedmem_alloc: +err_gen_pool_add: + gen_pool_destroy(pagetable->pool); +err_gen_pool_create: + kfree(pagetable); return NULL; } From 20d2c14572b7a94e13c0e97b563866260bf5fe1c Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 24 Feb 2010 01:27:56 -0800 Subject: [PATCH 0537/2556] [ARM] msm: kgsl: release the memory back into the free pool AFTER unmapping it Change-Id: Id91f085ef46b7a1bdbcff96b9c41c864a6943004 Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl_mmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c index a52e51ee26e79..fd395e8e44e83 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c @@ -585,8 +585,6 @@ kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, unsigned int gpuaddr, BUG_ON(range <= 0); - gen_pool_free(pagetable->pool, gpuaddr, range); - numpages = (range >> KGSL_PAGESIZE_SHIFT); if (range & (KGSL_PAGESIZE - 1)) numpages++; @@ -613,6 +611,8 @@ kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, unsigned int gpuaddr, kgsl_yamato_setstate(pagetable->mmu->device, KGSL_MMUFLAGS_TLBFLUSH); + gen_pool_free(pagetable->pool, gpuaddr, range); + KGSL_MEM_VDBG("return %d\n", 0); return 0; From 517bb50d6f0fda477cd0378c670f71e785a83700 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 24 Feb 2010 01:29:10 -0800 Subject: [PATCH 0538/2556] [ARM] msm: kgsl: clear the dummyspace region at initialization The dummyspace area is being used to insert dummy operation for some of the blocks when reprogramming the page tables. Non-zero data in that region was causing the VGT to go off into the weeds. Change-Id: Ib124c44df2eabb303675a4a10fb36418b173259a Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl_mmu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c index fd395e8e44e83..e4cac81f5ac73 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c @@ -378,6 +378,8 @@ int kgsl_mmu_init(struct kgsl_device *device) return status; } + kgsl_sharedmem_set(&mmu->dummyspace, 0, 0, + mmu->dummyspace.size); kgsl_yamato_regwrite(device, REG_MH_MMU_TRAN_ERROR, mmu->dummyspace.physaddr); From 6d5bfa3884eef6163440a2babe77b41d8373843d Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 24 Feb 2010 12:32:35 -0800 Subject: [PATCH 0539/2556] [ARM] msm: kgsl: move translation error dummy space 32 bytes up Leave the bottom 32bytes of the dummy area zeroed out for other uses (like VGT dummy cycles). This is needed so that in case of a translation error, garbage written into this area does not have adverse effects. Change-Id: I3c7abe028bece82fec8c53bdf1d243dad1917746 Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl_mmu.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c index e4cac81f5ac73..c32558ffe5849 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c @@ -370,7 +370,7 @@ int kgsl_mmu_init(struct kgsl_device *device) */ flags = (KGSL_MEMFLAGS_ALIGN4K | KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_STRICTREQUEST); - status = kgsl_sharedmem_alloc(flags, 32, &mmu->dummyspace); + status = kgsl_sharedmem_alloc(flags, 64, &mmu->dummyspace); if (status != 0) { KGSL_MEM_ERR ("Unable to allocate dummy space memory.\n"); @@ -380,9 +380,14 @@ int kgsl_mmu_init(struct kgsl_device *device) kgsl_sharedmem_set(&mmu->dummyspace, 0, 0, mmu->dummyspace.size); + /* TRAN_ERROR needs a 32 byte (32 byte aligned) chunk of memory + * to complete transactions in case of an MMU fault. Note that + * we'll leave the bottom 32 bytes of the dummyspace for other + * purposes (e.g. use it when dummy read cycles are needed + * for other blocks */ kgsl_yamato_regwrite(device, REG_MH_MMU_TRAN_ERROR, - mmu->dummyspace.physaddr); + mmu->dummyspace.physaddr + 32); mmu->defaultpagetable = kgsl_mmu_createpagetableobject(mmu); if (!mmu->defaultpagetable) { From 01a08185fb1ba24dd7d729c22909ae3021168d5a Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Wed, 24 Feb 2010 09:48:32 -0500 Subject: [PATCH 0540/2556] [ARM] msm: mahimahi: UMS USB function is disabled when RNDIS is active. Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/board-mahimahi.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 896d973f048a3..da7956e39135f 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -133,14 +133,12 @@ static char *usb_functions_ums_adb[] = { "adb", }; -static char *usb_functions_rndis_ums[] = { +static char *usb_functions_rndis[] = { "rndis", - "usb_mass_storage", }; -static char *usb_functions_rndis_ums_adb[] = { +static char *usb_functions_rndis_adb[] = { "rndis", - "usb_mass_storage", "adb", }; @@ -179,13 +177,13 @@ static struct android_usb_product usb_products[] = { }, { .product_id = 0x4e13, - .num_functions = ARRAY_SIZE(usb_functions_rndis_ums), - .functions = usb_functions_rndis_ums, + .num_functions = ARRAY_SIZE(usb_functions_rndis), + .functions = usb_functions_rndis, }, { .product_id = 0x4e14, - .num_functions = ARRAY_SIZE(usb_functions_rndis_ums_adb), - .functions = usb_functions_rndis_ums_adb, + .num_functions = ARRAY_SIZE(usb_functions_rndis_adb), + .functions = usb_functions_rndis_adb, }, #ifdef CONFIG_USB_ANDROID_DIAG { From e66de35f77d01d37af46689848bc2e69f43f5719 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Wed, 24 Feb 2010 10:11:01 -0500 Subject: [PATCH 0541/2556] [ARM] msm: htc: Update USB configuration for RNDIS ethernet: UMS USB function is disabled when RNDIS is active. Use official USB product IDs from HTC. Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/devices_htc.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c index f51c23a69980a..f66fcbad7af3b 100644 --- a/arch/arm/mach-msm/devices_htc.c +++ b/arch/arm/mach-msm/devices_htc.c @@ -114,14 +114,12 @@ static char *usb_functions_ums_adb[] = { "adb", }; -static char *usb_functions_rndis_ums[] = { +static char *usb_functions_rndis[] = { "rndis", - "usb_mass_storage", }; -static char *usb_functions_rndis_ums_adb[] = { +static char *usb_functions_rndis_adb[] = { "rndis", - "usb_mass_storage", "adb", }; @@ -148,14 +146,14 @@ static struct android_usb_product usb_products[] = { .functions = usb_functions_ums_adb, }, { - .product_id = 0xfffe, /* FIXME need a real PID here */ - .num_functions = ARRAY_SIZE(usb_functions_rndis_ums), - .functions = usb_functions_rndis_ums, + .product_id = 0x0ffe, + .num_functions = ARRAY_SIZE(usb_functions_rndis), + .functions = usb_functions_rndis, }, { - .product_id = 0xffff, /* FIXME need a real PID here */ - .num_functions = ARRAY_SIZE(usb_functions_rndis_ums_adb), - .functions = usb_functions_rndis_ums_adb, + .product_id = 0x0ffc, + .num_functions = ARRAY_SIZE(usb_functions_rndis_adb), + .functions = usb_functions_rndis_adb, }, }; From faddcec6496c8c12c521f2c7a0f58f4c773a9327 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Sat, 27 Feb 2010 00:09:17 -0500 Subject: [PATCH 0542/2556] [ARM] msm: msm7k_udc: Call usb_connected function in platform data on configure Fixes USB charging events that broke during the move from function to gadget USB drivers Signed-off-by: Mike Lockwood --- drivers/usb/gadget/msm72k_udc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 2c9d269367839..78397b3146d6c 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -692,9 +692,11 @@ static void handle_setup(struct usb_info *ui) } } if (ctl.bRequestType == (USB_DIR_OUT | USB_TYPE_STANDARD)) { - if (ctl.bRequest == USB_REQ_SET_CONFIGURATION) + if (ctl.bRequest == USB_REQ_SET_CONFIGURATION) { ui->online = !!ctl.wValue; - else if (ctl.bRequest == USB_REQ_SET_ADDRESS) { + if (ui->online && ui->usb_connected) + ui->usb_connected(1); + } else if (ctl.bRequest == USB_REQ_SET_ADDRESS) { /* write address delayed (will take effect ** after the next IN txn) */ From d9d248b8ee6619d7f0e45cca3a70dc1c5bb95c75 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Thu, 14 Jan 2010 15:00:42 -0800 Subject: [PATCH 0543/2556] [ARM] msm: camera: s5k3e2fx lens correcions Signed-off-by: Iliyan Malchev --- drivers/media/video/msm/s5k3e2fx.c | 1487 ++++++++++++++-------------- 1 file changed, 744 insertions(+), 743 deletions(-) diff --git a/drivers/media/video/msm/s5k3e2fx.c b/drivers/media/video/msm/s5k3e2fx.c index 7f4546ff61237..679fd4edf9482 100644 --- a/drivers/media/video/msm/s5k3e2fx.c +++ b/drivers/media/video/msm/s5k3e2fx.c @@ -407,878 +407,879 @@ struct s5k3e2fx_i2c_reg_conf Init_setting[2][NUM_INIT_REG] = { struct s5k3e2fx_i2c_reg_conf lc_setting[2][NUM_LC_REG] = { /*EVT4 */ { -/*LC setting Start */ - {0x3200, 0x00}, /* 09011 modify LC setting (t75-r82) setting */ - {0x3201, 0x8a}, - {0x3202, 0xa7}, - {0x3203, 0x0f}, - {0x3204, 0xe1}, - {0x3205, 0x9e}, - {0x3206, 0x00}, - {0x3207, 0x12}, - {0x3208, 0xfc}, - {0x3209, 0x0f}, - {0x320a, 0xfb}, - {0x320b, 0xd9}, - {0x320c, 0x00}, - {0x320d, 0x01}, - {0x320e, 0x07}, - {0x320f, 0x00}, - {0x3210, 0x00}, - {0x3211, 0x52}, - {0x3212, 0x0f}, - {0x3213, 0xd5}, - {0x3214, 0x04}, - {0x3215, 0x00}, - {0x3216, 0x0e}, - {0x3217, 0x7e}, - {0x3218, 0x0f}, - {0x3219, 0xf3}, - {0x321a, 0x05}, - {0x321b, 0x00}, - {0x321c, 0x06}, - {0x321d, 0xe2}, - {0x321e, 0x0f}, - {0x321f, 0xff}, - {0x3220, 0x65}, - {0x3221, 0x0f}, - {0x3222, 0xfe}, - {0x3223, 0x1c}, - {0x3224, 0x00}, - {0x3225, 0x1c}, - {0x3226, 0x3c}, - {0x3227, 0x0f}, - {0x3228, 0xe1}, - {0x3229, 0x63}, - {0x322a, 0x00}, - {0x322b, 0x10}, - {0x322c, 0xd1}, - {0x322d, 0x00}, - {0x322e, 0x02}, - {0x322f, 0x6b}, - {0x3230, 0x0f}, - {0x3231, 0xf5}, - {0x3232, 0x25}, - {0x3233, 0x00}, - {0x3234, 0x08}, - {0x3235, 0xc8}, - {0x3236, 0x0f}, - {0x3237, 0xfd}, - {0x3238, 0xd4}, - {0x3239, 0x00}, - {0x323a, 0x20}, - {0x323b, 0xd1}, - {0x323c, 0x0f}, - {0x323d, 0xf3}, - {0x323e, 0x3c}, - {0x323f, 0x0f}, - {0x3240, 0xf7}, - {0x3241, 0x31}, - {0x3242, 0x00}, - {0x3243, 0x0c}, - {0x3244, 0xc9}, - {0x3245, 0x0f}, - {0x3246, 0xf7}, - {0x3247, 0x90}, - {0x3248, 0x0f}, - {0x3249, 0xed}, - {0x324a, 0xdf}, - {0x324b, 0x0f}, - {0x324c, 0xfa}, - {0x324d, 0x9b}, - {0x324e, 0x0f}, - {0x324f, 0xfa}, - {0x3250, 0x8b}, - {0x3251, 0x00}, - {0x3252, 0x0a}, - {0x3253, 0x3d}, - {0x3254, 0x00}, - {0x3255, 0x00}, - {0x3256, 0x99}, - {0x3257, 0x0f}, - {0x3258, 0xf9}, - {0x3259, 0xa2}, - {0x325a, 0x00}, - {0x325b, 0x18}, - {0x325c, 0xdd}, - {0x325d, 0x0f}, - {0x325e, 0xeb}, - {0x325f, 0x42}, - {0x3260, 0x00}, - {0x3261, 0x17}, - {0x3262, 0x7c}, - {0x3263, 0x0f}, - {0x3264, 0xef}, - {0x3265, 0x19}, - {0x3266, 0x0f}, - {0x3267, 0xfe}, - {0x3268, 0x3d}, - {0x3269, 0x00}, - {0x326a, 0x0c}, - {0x326b, 0x89}, - {0x326c, 0x00}, - {0x326d, 0x88}, - {0x326e, 0xff}, - {0x326f, 0x0f}, - {0x3270, 0xe3}, - {0x3271, 0x36}, - {0x3272, 0x00}, - {0x3273, 0x11}, - {0x3274, 0xdf}, - {0x3275, 0x0f}, - {0x3276, 0xfc}, - {0x3277, 0x9a}, - {0x3278, 0x00}, - {0x3279, 0x01}, - {0x327a, 0x09}, - {0x327b, 0x0f}, - {0x327c, 0xff}, - {0x327d, 0x97}, - {0x327e, 0x0f}, - {0x327f, 0xd8}, - {0x3280, 0x0f}, - {0x3281, 0x00}, - {0x3282, 0x0b}, - {0x3283, 0x39}, - {0x3284, 0x0f}, - {0x3285, 0xf3}, - {0x3286, 0x13}, - {0x3287, 0x00}, - {0x3288, 0x08}, - {0x3289, 0x1d}, - {0x328a, 0x00}, - {0x328b, 0x02}, - {0x328c, 0xa4}, - {0x328d, 0x0f}, - {0x328e, 0xf9}, - {0x328f, 0x71}, - {0x3290, 0x00}, - {0x3291, 0x16}, - {0x3292, 0x77}, - {0x3293, 0x0f}, - {0x3294, 0xe6}, - {0x3295, 0x88}, - {0x3296, 0x00}, - {0x3297, 0x15}, - {0x3298, 0x06}, - {0x3299, 0x0f}, - {0x329a, 0xfb}, - {0x329b, 0xdb}, - {0x329c, 0x0f}, - {0x329d, 0xec}, - {0x329e, 0x3c}, - {0x329f, 0x00}, - {0x32a0, 0x17}, - {0x32a1, 0xcc}, - {0x32a2, 0x00}, - {0x32a3, 0x03}, - {0x32a4, 0x25}, - {0x32a5, 0x00}, - {0x32a6, 0x1a}, - {0x32a7, 0x9b}, - {0x32a8, 0x0f}, - {0x32a9, 0xed}, - {0x32aa, 0x0b}, - {0x32ab, 0x0f}, - {0x32ac, 0xfe}, - {0x32ad, 0xe2}, - {0x32ae, 0x00}, - {0x32af, 0x1a}, - {0x32b0, 0xc6}, - {0x32b1, 0x0f}, - {0x32b2, 0xe2}, - {0x32b3, 0xb1}, - {0x32b4, 0x0f}, - {0x32b5, 0xf0}, - {0x32b6, 0x7c}, - {0x32b7, 0x0f}, - {0x32b8, 0xfe}, - {0x32b9, 0xd0}, - {0x32ba, 0x0f}, - {0x32bb, 0xfd}, - {0x32bc, 0xc4}, - {0x32bd, 0x00}, - {0x32be, 0x06}, - {0x32bf, 0x4d}, - {0x32c0, 0x0f}, - {0x32c1, 0xf9}, - {0x32c2, 0xbe}, - {0x32c3, 0x00}, - {0x32c4, 0x02}, - {0x32c5, 0x58}, - {0x32c6, 0x00}, - {0x32c7, 0x11}, - {0x32c8, 0x60}, - {0x32c9, 0x0f}, - {0x32ca, 0xeb}, - {0x32cb, 0x60}, - {0x32cc, 0x00}, - {0x32cd, 0x16}, - {0x32ce, 0x25}, - {0x32cf, 0x0f}, - {0x32d0, 0xf2}, - {0x32d1, 0x40}, - {0x32d2, 0x0f}, - {0x32d3, 0xf7}, - {0x32d4, 0x9e}, - {0x32d5, 0x00}, - {0x32d6, 0x13}, - {0x32d7, 0xd5}, - {0x32d8, 0x00}, - {0x32d9, 0x70}, - {0x32da, 0xc7}, - {0x32db, 0x0f}, - {0x32dc, 0xec}, - {0x32dd, 0x9f}, - {0x32de, 0x00}, - {0x32df, 0x09}, - {0x32e0, 0xf3}, - {0x32e1, 0x00}, - {0x32e2, 0x00}, - {0x32e3, 0xf2}, - {0x32e4, 0x0f}, - {0x32e5, 0xff}, - {0x32e6, 0x30}, - {0x32e7, 0x0f}, - {0x32e8, 0xff}, - {0x32e9, 0x27}, - {0x32ea, 0x0f}, - {0x32eb, 0xe4}, - {0x32ec, 0x66}, - {0x32ed, 0x00}, - {0x32ee, 0x0a}, - {0x32ef, 0xe9}, - {0x32f0, 0x0f}, - {0x32f1, 0xf4}, - {0x32f2, 0xdd}, - {0x32f3, 0x00}, - {0x32f4, 0x05}, - {0x32f5, 0xd1}, - {0x32f6, 0x0f}, - {0x32f7, 0xff}, - {0x32f8, 0x29}, - {0x32f9, 0x00}, - {0x32fa, 0x00}, - {0x32fb, 0x58}, - {0x32fc, 0x00}, - {0x32fd, 0x11}, - {0x32fe, 0x32}, - {0x32ff, 0x0f}, - {0x3300, 0xe8}, - {0x3301, 0x8e}, - {0x3302, 0x00}, - {0x3303, 0x12}, - {0x3304, 0xc1}, - {0x3305, 0x0f}, - {0x3306, 0xff}, - {0x3307, 0x8f}, - {0x3308, 0x0f}, - {0x3309, 0xf4}, - {0x330a, 0x23}, - {0x330b, 0x00}, - {0x330c, 0x07}, - {0x330d, 0xc4}, - {0x330e, 0x0f}, - {0x330f, 0xfe}, - {0x3310, 0x11}, - {0x3311, 0x00}, - {0x3312, 0x1c}, - {0x3313, 0x3a}, - {0x3314, 0x0f}, - {0x3315, 0xe9}, - {0x3316, 0x73}, - {0x3317, 0x00}, - {0x3318, 0x00}, - {0x3319, 0x78}, - {0x331a, 0x00}, - {0x331b, 0x0a}, - {0x331c, 0x8b}, - {0x331d, 0x0f}, - {0x331e, 0xfc}, - {0x331f, 0x0f}, - {0x3320, 0x0f}, - {0x3321, 0xfc}, - {0x3322, 0xd3}, - {0x3323, 0x0f}, - {0x3324, 0xf6}, - {0x3325, 0x43}, - {0x3326, 0x00}, - {0x3327, 0x03}, - {0x3328, 0x06}, - {0x3329, 0x00}, - {0x332a, 0x06}, - {0x332b, 0x41}, - {0x332c, 0x00}, - {0x332d, 0x01}, - {0x332e, 0xae}, - {0x332f, 0x0f}, - {0x3330, 0xf3}, - {0x3331, 0x9a}, - {0x3332, 0x00}, - {0x3333, 0x06}, - {0x3334, 0x36}, - {0x3335, 0x0f}, - {0x3336, 0xf3}, - {0x3337, 0xde}, - {0x3338, 0x00}, - {0x3339, 0x13}, - {0x333a, 0x8e}, - {0x333b, 0x0f}, - {0x333c, 0xef}, - {0x333d, 0xfb}, - {0x333e, 0x0f}, - {0x333f, 0xfb}, - {0x3340, 0x87}, - {0x3341, 0x00}, - {0x3342, 0x11}, - {0x3343, 0x60}, - {0x3344, 0x00}, - {0x3345, 0x8b}, - {0x3346, 0xb4}, - {0x3347, 0x0f}, - {0x3348, 0xe4}, - {0x3349, 0x4e}, - {0x334a, 0x00}, - {0x334b, 0x12}, - {0x334c, 0x0e}, - {0x334d, 0x0f}, - {0x334e, 0xf8}, - {0x334f, 0xf4}, - {0x3350, 0x00}, - {0x3351, 0x07}, - {0x3352, 0x27}, - {0x3353, 0x0f}, - {0x3354, 0xfb}, - {0x3355, 0x9b}, - {0x3356, 0x0f}, - {0x3357, 0xd7}, - {0x3358, 0xc0}, - {0x3359, 0x00}, - {0x335a, 0x0c}, - {0x335b, 0x12}, - {0x335c, 0x0f}, - {0x335d, 0xf2}, - {0x335e, 0xda}, - {0x335f, 0x00}, - {0x3360, 0x0c}, - {0x3361, 0x1b}, - {0x3362, 0x0f}, - {0x3363, 0xf5}, - {0x3364, 0x6f}, - {0x3365, 0x00}, - {0x3366, 0x05}, - {0x3367, 0xd8}, - {0x3368, 0x00}, - {0x3369, 0x12}, - {0x336a, 0x7c}, - {0x336b, 0x0f}, - {0x336c, 0xe5}, - {0x336d, 0xe8}, - {0x336e, 0x00}, - {0x336f, 0x0c}, - {0x3370, 0xad}, - {0x3371, 0x00}, - {0x3372, 0x04}, - {0x3373, 0xc3}, - {0x3374, 0x0f}, - {0x3375, 0xf4}, - {0x3376, 0x91}, - {0x3377, 0x00}, - {0x3378, 0x09}, - {0x3379, 0xab}, - {0x337a, 0x00}, - {0x337b, 0x09}, - {0x337c, 0xbc}, - {0x337d, 0x00}, - {0x337e, 0x1c}, - {0x337f, 0x3b}, - {0x3380, 0x0f}, - {0x3381, 0xf7}, - {0x3382, 0x00}, - {0x3383, 0x0f}, - {0x3384, 0xf6}, - {0x3385, 0xfb}, - {0x3386, 0x00}, - {0x3387, 0x10}, - {0x3388, 0x59}, - {0x3389, 0x0f}, - {0x338a, 0xed}, - {0x338b, 0x65}, - {0x338c, 0x0f}, - {0x338d, 0xed}, - {0x338e, 0x18}, - {0x338f, 0x0f}, - {0x3390, 0xfd}, - {0x3391, 0xae}, - {0x3392, 0x0f}, - {0x3393, 0xf8}, - {0x3394, 0x35}, - {0x3395, 0x00}, - {0x3396, 0x01}, - {0x3397, 0xcb}, - {0x3398, 0x00}, - {0x3399, 0x01}, - {0x339a, 0xe8}, - {0x339b, 0x00}, - {0x339c, 0x0a}, - {0x339d, 0x5f}, - {0x339e, 0x00}, - {0x339f, 0x11}, - {0x33a0, 0xa0}, - {0x33a1, 0x0f}, - {0x33a2, 0xe9}, - {0x33a3, 0xeb}, - {0x33a4, 0x00}, - {0x33a5, 0x18}, - {0x33a6, 0x77}, - {0x33a7, 0x0f}, - {0x33a8, 0xf9}, - {0x33a9, 0x9b}, - {0x33aa, 0x0f}, - {0x33ab, 0xf8}, - {0x33ac, 0xd0}, - {0x33ad, 0x00}, - {0x33ae, 0x00}, - {0x33af, 0xd2}, - {0x309D, 0x62}, - {0x309d, 0x22}, /* shading enable */ + /*EVT4 */ /* 100108 Modify LC setting DNP light source t75-r73*/ + {0x3200, 0x00}, + {0x3201, 0x99}, + {0x3202, 0xc1}, + {0x3203, 0x0f}, + {0x3204, 0xd0}, + {0x3205, 0x1b}, + {0x3206, 0x00}, + {0x3207, 0x24}, + {0x3208, 0x8d}, + {0x3209, 0x0f}, + {0x320a, 0xee}, + {0x320b, 0x0f}, + {0x320c, 0x00}, + {0x320d, 0x04}, + {0x320e, 0x5c}, + {0x320f, 0x00}, + {0x3210, 0x07}, + {0x3211, 0x68}, + {0x3212, 0x0f}, + {0x3213, 0xc2}, + {0x3214, 0x82}, + {0x3215, 0x00}, + {0x3216, 0x29}, + {0x3217, 0x3e}, + {0x3218, 0x0f}, + {0x3219, 0xd3}, + {0x321a, 0x63}, + {0x321b, 0x00}, + {0x321c, 0x22}, + {0x321d, 0x6c}, + {0x321e, 0x0f}, + {0x321f, 0xf8}, + {0x3220, 0xce}, + {0x3221, 0x0f}, + {0x3222, 0xed}, + {0x3223, 0x30}, + {0x3224, 0x00}, + {0x3225, 0x37}, + {0x3226, 0x87}, + {0x3227, 0x0f}, + {0x3228, 0xc2}, + {0x3229, 0x87}, + {0x322a, 0x00}, + {0x322b, 0x2a}, + {0x322c, 0xc6}, + {0x322d, 0x0f}, + {0x322e, 0xf3}, + {0x322f, 0xd9}, + {0x3230, 0x0f}, + {0x3231, 0xea}, + {0x3232, 0x1a}, + {0x3233, 0x00}, + {0x3234, 0x2d}, + {0x3235, 0x9f}, + {0x3236, 0x0f}, + {0x3237, 0xde}, + {0x3238, 0x7d}, + {0x3239, 0x00}, + {0x323a, 0x37}, + {0x323b, 0x1e}, + {0x323c, 0x0f}, + {0x323d, 0xed}, + {0x323e, 0x9c}, + {0x323f, 0x0f}, + {0x3240, 0xf6}, + {0x3241, 0xfd}, + {0x3242, 0x00}, + {0x3243, 0x15}, + {0x3244, 0xeb}, + {0x3245, 0x0f}, + {0x3246, 0xd3}, + {0x3247, 0xca}, + {0x3248, 0x00}, + {0x3249, 0x08}, + {0x324a, 0xe6}, + {0x324b, 0x0f}, + {0x324c, 0xf4}, + {0x324d, 0x7a}, + {0x324e, 0x0f}, + {0x324f, 0xed}, + {0x3250, 0x1e}, + {0x3251, 0x00}, + {0x3252, 0x0d}, + {0x3253, 0x46}, + {0x3254, 0x00}, + {0x3255, 0x0c}, + {0x3256, 0x3e}, + {0x3257, 0x00}, + {0x3258, 0x09}, + {0x3259, 0xcf}, + {0x325a, 0x00}, + {0x325b, 0x09}, + {0x325c, 0xb5}, + {0x325d, 0x0f}, + {0x325e, 0xec}, + {0x325f, 0x47}, + {0x3260, 0x00}, + {0x3261, 0x1d}, + {0x3262, 0xd8}, + {0x3263, 0x0f}, + {0x3264, 0xf7}, + {0x3265, 0x11}, + {0x3266, 0x0f}, + {0x3267, 0xea}, + {0x3268, 0x3d}, + {0x3269, 0x00}, + {0x326a, 0x09}, + {0x326b, 0xcc}, + {0x326c, 0x00}, + {0x326d, 0x9b}, + {0x326e, 0x73}, + {0x326f, 0x0f}, + {0x3270, 0xd4}, + {0x3271, 0x9e}, + {0x3272, 0x00}, + {0x3273, 0x1a}, + {0x3274, 0x87}, + {0x3275, 0x0f}, + {0x3276, 0xfd}, + {0x3277, 0xeb}, + {0x3278, 0x0f}, + {0x3279, 0xf5}, + {0x327a, 0xb4}, + {0x327b, 0x00}, + {0x327c, 0x0d}, + {0x327d, 0x8c}, + {0x327e, 0x0f}, + {0x327f, 0xc9}, + {0x3280, 0x4d}, + {0x3281, 0x00}, + {0x3282, 0x1d}, + {0x3283, 0x2d}, + {0x3284, 0x0f}, + {0x3285, 0xea}, + {0x3286, 0x5b}, + {0x3287, 0x00}, + {0x3288, 0x04}, + {0x3289, 0x76}, + {0x328a, 0x00}, + {0x328b, 0x10}, + {0x328c, 0x2d}, + {0x328d, 0x0f}, + {0x328e, 0xe6}, + {0x328f, 0xde}, + {0x3290, 0x00}, + {0x3291, 0x26}, + {0x3292, 0x85}, + {0x3293, 0x0f}, + {0x3294, 0xcf}, + {0x3295, 0x12}, + {0x3296, 0x00}, + {0x3297, 0x14}, + {0x3298, 0x0f}, + {0x3299, 0x00}, + {0x329a, 0x0b}, + {0x329b, 0x36}, + {0x329c, 0x0f}, + {0x329d, 0xe4}, + {0x329e, 0xa4}, + {0x329f, 0x00}, + {0x32a0, 0x21}, + {0x32a1, 0x1f}, + {0x32a2, 0x0f}, + {0x32a3, 0xf3}, + {0x32a4, 0x99}, + {0x32a5, 0x00}, + {0x32a6, 0x30}, + {0x32a7, 0x8f}, + {0x32a8, 0x0f}, + {0x32a9, 0xf9}, + {0x32aa, 0x35}, + {0x32ab, 0x0f}, + {0x32ac, 0xee}, + {0x32ad, 0x6e}, + {0x32ae, 0x00}, + {0x32af, 0x09}, + {0x32b0, 0x19}, + {0x32b1, 0x0f}, + {0x32b2, 0xf0}, + {0x32b3, 0x57}, + {0x32b4, 0x00}, + {0x32b5, 0x01}, + {0x32b6, 0xcc}, + {0x32b7, 0x0f}, + {0x32b8, 0xf1}, + {0x32b9, 0x0b}, + {0x32ba, 0x0f}, + {0x32bb, 0xee}, + {0x32bc, 0x99}, + {0x32bd, 0x00}, + {0x32be, 0x11}, + {0x32bf, 0x3d}, + {0x32c0, 0x00}, + {0x32c1, 0x10}, + {0x32c2, 0x64}, + {0x32c3, 0x0f}, + {0x32c4, 0xf6}, + {0x32c5, 0xab}, + {0x32c6, 0x00}, + {0x32c7, 0x03}, + {0x32c8, 0x19}, + {0x32c9, 0x0f}, + {0x32ca, 0xf3}, + {0x32cb, 0xc9}, + {0x32cc, 0x00}, + {0x32cd, 0x17}, + {0x32ce, 0xb3}, + {0x32cf, 0x0f}, + {0x32d0, 0xf2}, + {0x32d1, 0x3d}, + {0x32d2, 0x0f}, + {0x32d3, 0xf4}, + {0x32d4, 0x7e}, + {0x32d5, 0x00}, + {0x32d6, 0x09}, + {0x32d7, 0x46}, + {0x32d8, 0x00}, + {0x32d9, 0x7c}, + {0x32da, 0x79}, + {0x32db, 0x0f}, + {0x32dc, 0xde}, + {0x32dd, 0x19}, + {0x32de, 0x00}, + {0x32df, 0x19}, + {0x32e0, 0xe8}, + {0x32e1, 0x0f}, + {0x32e2, 0xf3}, + {0x32e3, 0x41}, + {0x32e4, 0x00}, + {0x32e5, 0x03}, + {0x32e6, 0x4c}, + {0x32e7, 0x00}, + {0x32e8, 0x05}, + {0x32e9, 0x73}, + {0x32ea, 0x0f}, + {0x32eb, 0xd6}, + {0x32ec, 0xa5}, + {0x32ed, 0x00}, + {0x32ee, 0x1f}, + {0x32ef, 0x81}, + {0x32f0, 0x0f}, + {0x32f1, 0xdc}, + {0x32f2, 0xe6}, + {0x32f3, 0x00}, + {0x32f4, 0x18}, + {0x32f5, 0x65}, + {0x32f6, 0x00}, + {0x32f7, 0x00}, + {0x32f8, 0x11}, + {0x32f9, 0x0f}, + {0x32fa, 0xed}, + {0x32fb, 0x65}, + {0x32fc, 0x00}, + {0x32fd, 0x23}, + {0x32fe, 0x12}, + {0x32ff, 0x0f}, + {0x3300, 0xcf}, + {0x3301, 0x28}, + {0x3302, 0x00}, + {0x3303, 0x2b}, + {0x3304, 0xda}, + {0x3305, 0x0f}, + {0x3306, 0xef}, + {0x3307, 0xae}, + {0x3308, 0x0f}, + {0x3309, 0xeb}, + {0x330a, 0x13}, + {0x330b, 0x00}, + {0x330c, 0x27}, + {0x330d, 0xb8}, + {0x330e, 0x0f}, + {0x330f, 0xec}, + {0x3310, 0x69}, + {0x3311, 0x00}, + {0x3312, 0x2f}, + {0x3313, 0x5f}, + {0x3314, 0x0f}, + {0x3315, 0xdf}, + {0x3316, 0x4f}, + {0x3317, 0x00}, + {0x3318, 0x05}, + {0x3319, 0x70}, + {0x331a, 0x00}, + {0x331b, 0x0f}, + {0x331c, 0xd2}, + {0x331d, 0x0f}, + {0x331e, 0xe1}, + {0x331f, 0xd8}, + {0x3320, 0x00}, + {0x3321, 0x09}, + {0x3322, 0xcf}, + {0x3323, 0x0f}, + {0x3324, 0xf2}, + {0x3325, 0x6e}, + {0x3326, 0x0f}, + {0x3327, 0xf6}, + {0x3328, 0xb4}, + {0x3329, 0x00}, + {0x332a, 0x0d}, + {0x332b, 0x87}, + {0x332c, 0x00}, + {0x332d, 0x08}, + {0x332e, 0x1e}, + {0x332f, 0x0f}, + {0x3330, 0xfa}, + {0x3331, 0x6e}, + {0x3332, 0x0f}, + {0x3333, 0xff}, + {0x3334, 0xaa}, + {0x3335, 0x0f}, + {0x3336, 0xf2}, + {0x3337, 0xc0}, + {0x3338, 0x00}, + {0x3339, 0x1d}, + {0x333a, 0x18}, + {0x333b, 0x0f}, + {0x333c, 0xef}, + {0x333d, 0xed}, + {0x333e, 0x0f}, + {0x333f, 0xec}, + {0x3340, 0xf6}, + {0x3341, 0x00}, + {0x3342, 0x16}, + {0x3343, 0x8e}, + {0x3344, 0x00}, + {0x3345, 0x9c}, + {0x3346, 0x52}, + {0x3347, 0x0f}, + {0x3348, 0xcf}, + {0x3349, 0xb9}, + {0x334a, 0x00}, + {0x334b, 0x29}, + {0x334c, 0xe9}, + {0x334d, 0x0f}, + {0x334e, 0xe2}, + {0x334f, 0x83}, + {0x3350, 0x00}, + {0x3351, 0x11}, + {0x3352, 0xcc}, + {0x3353, 0x0f}, + {0x3354, 0xff}, + {0x3355, 0xf4}, + {0x3356, 0x0f}, + {0x3357, 0xc1}, + {0x3358, 0xa4}, + {0x3359, 0x00}, + {0x335a, 0x2f}, + {0x335b, 0xce}, + {0x335c, 0x0f}, + {0x335d, 0xc5}, + {0x335e, 0xbb}, + {0x335f, 0x00}, + {0x3360, 0x35}, + {0x3361, 0x2a}, + {0x3362, 0x0f}, + {0x3363, 0xe6}, + {0x3364, 0x2a}, + {0x3365, 0x0f}, + {0x3366, 0xf7}, + {0x3367, 0x44}, + {0x3368, 0x00}, + {0x3369, 0x31}, + {0x336a, 0xfe}, + {0x336b, 0x0f}, + {0x336c, 0xb6}, + {0x336d, 0x84}, + {0x336e, 0x00}, + {0x336f, 0x3c}, + {0x3370, 0x71}, + {0x3371, 0x0f}, + {0x3372, 0xe5}, + {0x3373, 0xfe}, + {0x3374, 0x0f}, + {0x3375, 0xf2}, + {0x3376, 0x87}, + {0x3377, 0x00}, + {0x3378, 0x29}, + {0x3379, 0x2b}, + {0x337a, 0x0f}, + {0x337b, 0xe5}, + {0x337c, 0x3f}, + {0x337d, 0x00}, + {0x337e, 0x45}, + {0x337f, 0xc6}, + {0x3380, 0x0f}, + {0x3381, 0xdf}, + {0x3382, 0xe6}, + {0x3383, 0x0f}, + {0x3384, 0xfb}, + {0x3385, 0x0f}, + {0x3386, 0x00}, + {0x3387, 0x0f}, + {0x3388, 0xf4}, + {0x3389, 0x0f}, + {0x338a, 0xdf}, + {0x338b, 0x72}, + {0x338c, 0x00}, + {0x338d, 0x0e}, + {0x338e, 0xaf}, + {0x338f, 0x0f}, + {0x3390, 0xed}, + {0x3391, 0x7a}, + {0x3392, 0x0f}, + {0x3393, 0xe5}, + {0x3394, 0xab}, + {0x3395, 0x00}, + {0x3396, 0x18}, + {0x3397, 0x43}, + {0x3398, 0x00}, + {0x3399, 0x1b}, + {0x339a, 0x41}, + {0x339b, 0x0f}, + {0x339c, 0xea}, + {0x339d, 0x84}, + {0x339e, 0x0f}, + {0x339f, 0xfd}, + {0x33a0, 0xdb}, + {0x33a1, 0x0f}, + {0x33a2, 0xe9}, + {0x33a3, 0xbd}, + {0x33a4, 0x00}, + {0x33a5, 0x30}, + {0x33a6, 0x77}, + {0x33a7, 0x0f}, + {0x33a8, 0xe9}, + {0x33a9, 0x93}, + {0x33aa, 0x0f}, + {0x33ab, 0xd7}, + {0x33ac, 0xde}, + {0x33ad, 0x00}, + {0x33ae, 0x2a}, + {0x33af, 0x14}, + {0x309D, 0x62}, + {0x309d, 0x22}, + /* LC setting End */ }, /*EVT5 */ { /* LC setting Start */ - {0x3200, 0x00}, /* modify LC setting (t75-r82) setting */ - {0x3201, 0x8a}, - {0x3202, 0xa7}, + {0x3200, 0x00}, /* 100108 Modify LC setting DNP light source t75-r73*/ + {0x3201, 0x99}, + {0x3202, 0xc1}, {0x3203, 0x0f}, - {0x3204, 0xe1}, - {0x3205, 0x9e}, + {0x3204, 0xd0}, + {0x3205, 0x1b}, {0x3206, 0x00}, - {0x3207, 0x12}, - {0x3208, 0xfc}, + {0x3207, 0x24}, + {0x3208, 0x8d}, {0x3209, 0x0f}, - {0x320a, 0xfb}, - {0x320b, 0xd9}, + {0x320a, 0xee}, + {0x320b, 0x0f}, {0x320c, 0x00}, - {0x320d, 0x01}, - {0x320e, 0x07}, + {0x320d, 0x04}, + {0x320e, 0x5c}, {0x320f, 0x00}, - {0x3210, 0x00}, - {0x3211, 0x52}, + {0x3210, 0x07}, + {0x3211, 0x68}, {0x3212, 0x0f}, - {0x3213, 0xd5}, - {0x3214, 0x04}, + {0x3213, 0xc2}, + {0x3214, 0x82}, {0x3215, 0x00}, - {0x3216, 0x0e}, - {0x3217, 0x7e}, + {0x3216, 0x29}, + {0x3217, 0x3e}, {0x3218, 0x0f}, - {0x3219, 0xf3}, - {0x321a, 0x05}, + {0x3219, 0xd3}, + {0x321a, 0x63}, {0x321b, 0x00}, - {0x321c, 0x06}, - {0x321d, 0xe2}, + {0x321c, 0x22}, + {0x321d, 0x6c}, {0x321e, 0x0f}, - {0x321f, 0xff}, - {0x3220, 0x65}, + {0x321f, 0xf8}, + {0x3220, 0xce}, {0x3221, 0x0f}, - {0x3222, 0xfe}, - {0x3223, 0x1c}, + {0x3222, 0xed}, + {0x3223, 0x30}, {0x3224, 0x00}, - {0x3225, 0x1c}, - {0x3226, 0x3c}, + {0x3225, 0x37}, + {0x3226, 0x87}, {0x3227, 0x0f}, - {0x3228, 0xe1}, - {0x3229, 0x63}, + {0x3228, 0xc2}, + {0x3229, 0x87}, {0x322a, 0x00}, - {0x322b, 0x10}, - {0x322c, 0xd1}, - {0x322d, 0x00}, - {0x322e, 0x02}, - {0x322f, 0x6b}, + {0x322b, 0x2a}, + {0x322c, 0xc6}, + {0x322d, 0x0f}, + {0x322e, 0xf3}, + {0x322f, 0xd9}, {0x3230, 0x0f}, - {0x3231, 0xf5}, - {0x3232, 0x25}, + {0x3231, 0xea}, + {0x3232, 0x1a}, {0x3233, 0x00}, - {0x3234, 0x08}, - {0x3235, 0xc8}, + {0x3234, 0x2d}, + {0x3235, 0x9f}, {0x3236, 0x0f}, - {0x3237, 0xfd}, - {0x3238, 0xd4}, + {0x3237, 0xde}, + {0x3238, 0x7d}, {0x3239, 0x00}, - {0x323a, 0x20}, - {0x323b, 0xd1}, + {0x323a, 0x37}, + {0x323b, 0x1e}, {0x323c, 0x0f}, - {0x323d, 0xf3}, - {0x323e, 0x3c}, + {0x323d, 0xed}, + {0x323e, 0x9c}, {0x323f, 0x0f}, - {0x3240, 0xf7}, - {0x3241, 0x31}, + {0x3240, 0xf6}, + {0x3241, 0xfd}, {0x3242, 0x00}, - {0x3243, 0x0c}, - {0x3244, 0xc9}, + {0x3243, 0x15}, + {0x3244, 0xeb}, {0x3245, 0x0f}, - {0x3246, 0xf7}, - {0x3247, 0x90}, - {0x3248, 0x0f}, - {0x3249, 0xed}, - {0x324a, 0xdf}, + {0x3246, 0xd3}, + {0x3247, 0xca}, + {0x3248, 0x00}, + {0x3249, 0x08}, + {0x324a, 0xe6}, {0x324b, 0x0f}, - {0x324c, 0xfa}, - {0x324d, 0x9b}, + {0x324c, 0xf4}, + {0x324d, 0x7a}, {0x324e, 0x0f}, - {0x324f, 0xfa}, - {0x3250, 0x8b}, + {0x324f, 0xed}, + {0x3250, 0x1e}, {0x3251, 0x00}, - {0x3252, 0x0a}, - {0x3253, 0x3d}, + {0x3252, 0x0d}, + {0x3253, 0x46}, {0x3254, 0x00}, - {0x3255, 0x00}, - {0x3256, 0x99}, - {0x3257, 0x0f}, - {0x3258, 0xf9}, - {0x3259, 0xa2}, + {0x3255, 0x0c}, + {0x3256, 0x3e}, + {0x3257, 0x00}, + {0x3258, 0x09}, + {0x3259, 0xcf}, {0x325a, 0x00}, - {0x325b, 0x18}, - {0x325c, 0xdd}, + {0x325b, 0x09}, + {0x325c, 0xb5}, {0x325d, 0x0f}, - {0x325e, 0xeb}, - {0x325f, 0x42}, + {0x325e, 0xec}, + {0x325f, 0x47}, {0x3260, 0x00}, - {0x3261, 0x17}, - {0x3262, 0x7c}, + {0x3261, 0x1d}, + {0x3262, 0xd8}, {0x3263, 0x0f}, - {0x3264, 0xef}, - {0x3265, 0x19}, + {0x3264, 0xf7}, + {0x3265, 0x11}, {0x3266, 0x0f}, - {0x3267, 0xfe}, + {0x3267, 0xea}, {0x3268, 0x3d}, {0x3269, 0x00}, - {0x326a, 0x0c}, - {0x326b, 0x89}, + {0x326a, 0x09}, + {0x326b, 0xcc}, {0x326c, 0x00}, - {0x326d, 0x88}, - {0x326e, 0xff}, + {0x326d, 0x9b}, + {0x326e, 0x73}, {0x326f, 0x0f}, - {0x3270, 0xe3}, - {0x3271, 0x36}, + {0x3270, 0xd4}, + {0x3271, 0x9e}, {0x3272, 0x00}, - {0x3273, 0x11}, - {0x3274, 0xdf}, + {0x3273, 0x1a}, + {0x3274, 0x87}, {0x3275, 0x0f}, - {0x3276, 0xfc}, - {0x3277, 0x9a}, - {0x3278, 0x00}, - {0x3279, 0x01}, - {0x327a, 0x09}, - {0x327b, 0x0f}, - {0x327c, 0xff}, - {0x327d, 0x97}, + {0x3276, 0xfd}, + {0x3277, 0xeb}, + {0x3278, 0x0f}, + {0x3279, 0xf5}, + {0x327a, 0xb4}, + {0x327b, 0x00}, + {0x327c, 0x0d}, + {0x327d, 0x8c}, {0x327e, 0x0f}, - {0x327f, 0xd8}, - {0x3280, 0x0f}, + {0x327f, 0xc9}, + {0x3280, 0x4d}, {0x3281, 0x00}, - {0x3282, 0x0b}, - {0x3283, 0x39}, + {0x3282, 0x1d}, + {0x3283, 0x2d}, {0x3284, 0x0f}, - {0x3285, 0xf3}, - {0x3286, 0x13}, + {0x3285, 0xea}, + {0x3286, 0x5b}, {0x3287, 0x00}, - {0x3288, 0x08}, - {0x3289, 0x1d}, + {0x3288, 0x04}, + {0x3289, 0x76}, {0x328a, 0x00}, - {0x328b, 0x02}, - {0x328c, 0xa4}, + {0x328b, 0x10}, + {0x328c, 0x2d}, {0x328d, 0x0f}, - {0x328e, 0xf9}, - {0x328f, 0x71}, + {0x328e, 0xe6}, + {0x328f, 0xde}, {0x3290, 0x00}, - {0x3291, 0x16}, - {0x3292, 0x77}, + {0x3291, 0x26}, + {0x3292, 0x85}, {0x3293, 0x0f}, - {0x3294, 0xe6}, - {0x3295, 0x88}, + {0x3294, 0xcf}, + {0x3295, 0x12}, {0x3296, 0x00}, - {0x3297, 0x15}, - {0x3298, 0x06}, - {0x3299, 0x0f}, - {0x329a, 0xfb}, - {0x329b, 0xdb}, + {0x3297, 0x14}, + {0x3298, 0x0f}, + {0x3299, 0x00}, + {0x329a, 0x0b}, + {0x329b, 0x36}, {0x329c, 0x0f}, - {0x329d, 0xec}, - {0x329e, 0x3c}, + {0x329d, 0xe4}, + {0x329e, 0xa4}, {0x329f, 0x00}, - {0x32a0, 0x17}, - {0x32a1, 0xcc}, - {0x32a2, 0x00}, - {0x32a3, 0x03}, - {0x32a4, 0x25}, + {0x32a0, 0x21}, + {0x32a1, 0x1f}, + {0x32a2, 0x0f}, + {0x32a3, 0xf3}, + {0x32a4, 0x99}, {0x32a5, 0x00}, - {0x32a6, 0x1a}, - {0x32a7, 0x9b}, + {0x32a6, 0x30}, + {0x32a7, 0x8f}, {0x32a8, 0x0f}, - {0x32a9, 0xed}, - {0x32aa, 0x0b}, + {0x32a9, 0xf9}, + {0x32aa, 0x35}, {0x32ab, 0x0f}, - {0x32ac, 0xfe}, - {0x32ad, 0xe2}, + {0x32ac, 0xee}, + {0x32ad, 0x6e}, {0x32ae, 0x00}, - {0x32af, 0x1a}, - {0x32b0, 0xc6}, + {0x32af, 0x09}, + {0x32b0, 0x19}, {0x32b1, 0x0f}, - {0x32b2, 0xe2}, - {0x32b3, 0xb1}, - {0x32b4, 0x0f}, - {0x32b5, 0xf0}, - {0x32b6, 0x7c}, + {0x32b2, 0xf0}, + {0x32b3, 0x57}, + {0x32b4, 0x00}, + {0x32b5, 0x01}, + {0x32b6, 0xcc}, {0x32b7, 0x0f}, - {0x32b8, 0xfe}, - {0x32b9, 0xd0}, + {0x32b8, 0xf1}, + {0x32b9, 0x0b}, {0x32ba, 0x0f}, - {0x32bb, 0xfd}, - {0x32bc, 0xc4}, + {0x32bb, 0xee}, + {0x32bc, 0x99}, {0x32bd, 0x00}, - {0x32be, 0x06}, - {0x32bf, 0x4d}, - {0x32c0, 0x0f}, - {0x32c1, 0xf9}, - {0x32c2, 0xbe}, - {0x32c3, 0x00}, - {0x32c4, 0x02}, - {0x32c5, 0x58}, + {0x32be, 0x11}, + {0x32bf, 0x3d}, + {0x32c0, 0x00}, + {0x32c1, 0x10}, + {0x32c2, 0x64}, + {0x32c3, 0x0f}, + {0x32c4, 0xf6}, + {0x32c5, 0xab}, {0x32c6, 0x00}, - {0x32c7, 0x11}, - {0x32c8, 0x60}, + {0x32c7, 0x03}, + {0x32c8, 0x19}, {0x32c9, 0x0f}, - {0x32ca, 0xeb}, - {0x32cb, 0x60}, + {0x32ca, 0xf3}, + {0x32cb, 0xc9}, {0x32cc, 0x00}, - {0x32cd, 0x16}, - {0x32ce, 0x25}, + {0x32cd, 0x17}, + {0x32ce, 0xb3}, {0x32cf, 0x0f}, {0x32d0, 0xf2}, - {0x32d1, 0x40}, + {0x32d1, 0x3d}, {0x32d2, 0x0f}, - {0x32d3, 0xf7}, - {0x32d4, 0x9e}, + {0x32d3, 0xf4}, + {0x32d4, 0x7e}, {0x32d5, 0x00}, - {0x32d6, 0x13}, - {0x32d7, 0xd5}, + {0x32d6, 0x09}, + {0x32d7, 0x46}, {0x32d8, 0x00}, - {0x32d9, 0x70}, - {0x32da, 0xc7}, + {0x32d9, 0x7c}, + {0x32da, 0x79}, {0x32db, 0x0f}, - {0x32dc, 0xec}, - {0x32dd, 0x9f}, + {0x32dc, 0xde}, + {0x32dd, 0x19}, {0x32de, 0x00}, - {0x32df, 0x09}, - {0x32e0, 0xf3}, - {0x32e1, 0x00}, - {0x32e2, 0x00}, - {0x32e3, 0xf2}, - {0x32e4, 0x0f}, - {0x32e5, 0xff}, - {0x32e6, 0x30}, - {0x32e7, 0x0f}, - {0x32e8, 0xff}, - {0x32e9, 0x27}, + {0x32df, 0x19}, + {0x32e0, 0xe8}, + {0x32e1, 0x0f}, + {0x32e2, 0xf3}, + {0x32e3, 0x41}, + {0x32e4, 0x00}, + {0x32e5, 0x03}, + {0x32e6, 0x4c}, + {0x32e7, 0x00}, + {0x32e8, 0x05}, + {0x32e9, 0x73}, {0x32ea, 0x0f}, - {0x32eb, 0xe4}, - {0x32ec, 0x66}, + {0x32eb, 0xd6}, + {0x32ec, 0xa5}, {0x32ed, 0x00}, - {0x32ee, 0x0a}, - {0x32ef, 0xe9}, + {0x32ee, 0x1f}, + {0x32ef, 0x81}, {0x32f0, 0x0f}, - {0x32f1, 0xf4}, - {0x32f2, 0xdd}, + {0x32f1, 0xdc}, + {0x32f2, 0xe6}, {0x32f3, 0x00}, - {0x32f4, 0x05}, - {0x32f5, 0xd1}, - {0x32f6, 0x0f}, - {0x32f7, 0xff}, - {0x32f8, 0x29}, - {0x32f9, 0x00}, - {0x32fa, 0x00}, - {0x32fb, 0x58}, + {0x32f4, 0x18}, + {0x32f5, 0x65}, + {0x32f6, 0x00}, + {0x32f7, 0x00}, + {0x32f8, 0x11}, + {0x32f9, 0x0f}, + {0x32fa, 0xed}, + {0x32fb, 0x65}, {0x32fc, 0x00}, - {0x32fd, 0x11}, - {0x32fe, 0x32}, + {0x32fd, 0x23}, + {0x32fe, 0x12}, {0x32ff, 0x0f}, - {0x3300, 0xe8}, - {0x3301, 0x8e}, + {0x3300, 0xcf}, + {0x3301, 0x28}, {0x3302, 0x00}, - {0x3303, 0x12}, - {0x3304, 0xc1}, + {0x3303, 0x2b}, + {0x3304, 0xda}, {0x3305, 0x0f}, - {0x3306, 0xff}, - {0x3307, 0x8f}, + {0x3306, 0xef}, + {0x3307, 0xae}, {0x3308, 0x0f}, - {0x3309, 0xf4}, - {0x330a, 0x23}, + {0x3309, 0xeb}, + {0x330a, 0x13}, {0x330b, 0x00}, - {0x330c, 0x07}, - {0x330d, 0xc4}, + {0x330c, 0x27}, + {0x330d, 0xb8}, {0x330e, 0x0f}, - {0x330f, 0xfe}, - {0x3310, 0x11}, + {0x330f, 0xec}, + {0x3310, 0x69}, {0x3311, 0x00}, - {0x3312, 0x1c}, - {0x3313, 0x3a}, + {0x3312, 0x2f}, + {0x3313, 0x5f}, {0x3314, 0x0f}, - {0x3315, 0xe9}, - {0x3316, 0x73}, + {0x3315, 0xdf}, + {0x3316, 0x4f}, {0x3317, 0x00}, - {0x3318, 0x00}, - {0x3319, 0x78}, + {0x3318, 0x05}, + {0x3319, 0x70}, {0x331a, 0x00}, - {0x331b, 0x0a}, - {0x331c, 0x8b}, + {0x331b, 0x0f}, + {0x331c, 0xd2}, {0x331d, 0x0f}, - {0x331e, 0xfc}, - {0x331f, 0x0f}, - {0x3320, 0x0f}, - {0x3321, 0xfc}, - {0x3322, 0xd3}, + {0x331e, 0xe1}, + {0x331f, 0xd8}, + {0x3320, 0x00}, + {0x3321, 0x09}, + {0x3322, 0xcf}, {0x3323, 0x0f}, - {0x3324, 0xf6}, - {0x3325, 0x43}, - {0x3326, 0x00}, - {0x3327, 0x03}, - {0x3328, 0x06}, + {0x3324, 0xf2}, + {0x3325, 0x6e}, + {0x3326, 0x0f}, + {0x3327, 0xf6}, + {0x3328, 0xb4}, {0x3329, 0x00}, - {0x332a, 0x06}, - {0x332b, 0x41}, + {0x332a, 0x0d}, + {0x332b, 0x87}, {0x332c, 0x00}, - {0x332d, 0x01}, - {0x332e, 0xae}, + {0x332d, 0x08}, + {0x332e, 0x1e}, {0x332f, 0x0f}, - {0x3330, 0xf3}, - {0x3331, 0x9a}, - {0x3332, 0x00}, - {0x3333, 0x06}, - {0x3334, 0x36}, + {0x3330, 0xfa}, + {0x3331, 0x6e}, + {0x3332, 0x0f}, + {0x3333, 0xff}, + {0x3334, 0xaa}, {0x3335, 0x0f}, - {0x3336, 0xf3}, - {0x3337, 0xde}, + {0x3336, 0xf2}, + {0x3337, 0xc0}, {0x3338, 0x00}, - {0x3339, 0x13}, - {0x333a, 0x8e}, + {0x3339, 0x1d}, + {0x333a, 0x18}, {0x333b, 0x0f}, {0x333c, 0xef}, - {0x333d, 0xfb}, + {0x333d, 0xed}, {0x333e, 0x0f}, - {0x333f, 0xfb}, - {0x3340, 0x87}, + {0x333f, 0xec}, + {0x3340, 0xf6}, {0x3341, 0x00}, - {0x3342, 0x11}, - {0x3343, 0x60}, + {0x3342, 0x16}, + {0x3343, 0x8e}, {0x3344, 0x00}, - {0x3345, 0x8b}, - {0x3346, 0xb4}, + {0x3345, 0x9c}, + {0x3346, 0x52}, {0x3347, 0x0f}, - {0x3348, 0xe4}, - {0x3349, 0x4e}, + {0x3348, 0xcf}, + {0x3349, 0xb9}, {0x334a, 0x00}, - {0x334b, 0x12}, - {0x334c, 0x0e}, + {0x334b, 0x29}, + {0x334c, 0xe9}, {0x334d, 0x0f}, - {0x334e, 0xf8}, - {0x334f, 0xf4}, + {0x334e, 0xe2}, + {0x334f, 0x83}, {0x3350, 0x00}, - {0x3351, 0x07}, - {0x3352, 0x27}, + {0x3351, 0x11}, + {0x3352, 0xcc}, {0x3353, 0x0f}, - {0x3354, 0xfb}, - {0x3355, 0x9b}, + {0x3354, 0xff}, + {0x3355, 0xf4}, {0x3356, 0x0f}, - {0x3357, 0xd7}, - {0x3358, 0xc0}, + {0x3357, 0xc1}, + {0x3358, 0xa4}, {0x3359, 0x00}, - {0x335a, 0x0c}, - {0x335b, 0x12}, + {0x335a, 0x2f}, + {0x335b, 0xce}, {0x335c, 0x0f}, - {0x335d, 0xf2}, - {0x335e, 0xda}, + {0x335d, 0xc5}, + {0x335e, 0xbb}, {0x335f, 0x00}, - {0x3360, 0x0c}, - {0x3361, 0x1b}, + {0x3360, 0x35}, + {0x3361, 0x2a}, {0x3362, 0x0f}, - {0x3363, 0xf5}, - {0x3364, 0x6f}, - {0x3365, 0x00}, - {0x3366, 0x05}, - {0x3367, 0xd8}, + {0x3363, 0xe6}, + {0x3364, 0x2a}, + {0x3365, 0x0f}, + {0x3366, 0xf7}, + {0x3367, 0x44}, {0x3368, 0x00}, - {0x3369, 0x12}, - {0x336a, 0x7c}, + {0x3369, 0x31}, + {0x336a, 0xfe}, {0x336b, 0x0f}, - {0x336c, 0xe5}, - {0x336d, 0xe8}, + {0x336c, 0xb6}, + {0x336d, 0x84}, {0x336e, 0x00}, - {0x336f, 0x0c}, - {0x3370, 0xad}, - {0x3371, 0x00}, - {0x3372, 0x04}, - {0x3373, 0xc3}, + {0x336f, 0x3c}, + {0x3370, 0x71}, + {0x3371, 0x0f}, + {0x3372, 0xe5}, + {0x3373, 0xfe}, {0x3374, 0x0f}, - {0x3375, 0xf4}, - {0x3376, 0x91}, + {0x3375, 0xf2}, + {0x3376, 0x87}, {0x3377, 0x00}, - {0x3378, 0x09}, - {0x3379, 0xab}, - {0x337a, 0x00}, - {0x337b, 0x09}, - {0x337c, 0xbc}, + {0x3378, 0x29}, + {0x3379, 0x2b}, + {0x337a, 0x0f}, + {0x337b, 0xe5}, + {0x337c, 0x3f}, {0x337d, 0x00}, - {0x337e, 0x1c}, - {0x337f, 0x3b}, + {0x337e, 0x45}, + {0x337f, 0xc6}, {0x3380, 0x0f}, - {0x3381, 0xf7}, - {0x3382, 0x00}, + {0x3381, 0xdf}, + {0x3382, 0xe6}, {0x3383, 0x0f}, - {0x3384, 0xf6}, - {0x3385, 0xfb}, + {0x3384, 0xfb}, + {0x3385, 0x0f}, {0x3386, 0x00}, - {0x3387, 0x10}, - {0x3388, 0x59}, + {0x3387, 0x0f}, + {0x3388, 0xf4}, {0x3389, 0x0f}, - {0x338a, 0xed}, - {0x338b, 0x65}, - {0x338c, 0x0f}, - {0x338d, 0xed}, - {0x338e, 0x18}, + {0x338a, 0xdf}, + {0x338b, 0x72}, + {0x338c, 0x00}, + {0x338d, 0x0e}, + {0x338e, 0xaf}, {0x338f, 0x0f}, - {0x3390, 0xfd}, - {0x3391, 0xae}, + {0x3390, 0xed}, + {0x3391, 0x7a}, {0x3392, 0x0f}, - {0x3393, 0xf8}, - {0x3394, 0x35}, + {0x3393, 0xe5}, + {0x3394, 0xab}, {0x3395, 0x00}, - {0x3396, 0x01}, - {0x3397, 0xcb}, + {0x3396, 0x18}, + {0x3397, 0x43}, {0x3398, 0x00}, - {0x3399, 0x01}, - {0x339a, 0xe8}, - {0x339b, 0x00}, - {0x339c, 0x0a}, - {0x339d, 0x5f}, - {0x339e, 0x00}, - {0x339f, 0x11}, - {0x33a0, 0xa0}, + {0x3399, 0x1b}, + {0x339a, 0x41}, + {0x339b, 0x0f}, + {0x339c, 0xea}, + {0x339d, 0x84}, + {0x339e, 0x0f}, + {0x339f, 0xfd}, + {0x33a0, 0xdb}, {0x33a1, 0x0f}, {0x33a2, 0xe9}, - {0x33a3, 0xeb}, + {0x33a3, 0xbd}, {0x33a4, 0x00}, - {0x33a5, 0x18}, + {0x33a5, 0x30}, {0x33a6, 0x77}, {0x33a7, 0x0f}, - {0x33a8, 0xf9}, - {0x33a9, 0x9b}, + {0x33a8, 0xe9}, + {0x33a9, 0x93}, {0x33aa, 0x0f}, - {0x33ab, 0xf8}, - {0x33ac, 0xd0}, + {0x33ab, 0xd7}, + {0x33ac, 0xde}, {0x33ad, 0x00}, - {0x33ae, 0x00}, - {0x33af, 0xd2}, + {0x33ae, 0x2a}, + {0x33af, 0x14}, {0x309D, 0x62}, {0x309d, 0x22}, /* shading enable */ /*LC setting End */ From 38d5db445101d5b84d3a8a3360cf873ff518e861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 20 Apr 2010 22:27:41 -0700 Subject: [PATCH 0544/2556] [ARM] msm: Fix build when CONFIG_MSM_QDSP6 is not set. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I5ef701b80652844746c96f754e0dd3c446ef0c88 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h index ef8b3d127a29f..c4a0fdbf97cbe 100644 --- a/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h +++ b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h @@ -93,7 +93,12 @@ struct q6audio_analog_ops { int (*get_rx_vol)(uint8_t hw, int level); }; +#ifdef CONFIG_MSM_QDSP6 void q6audio_register_analog_ops(struct q6audio_analog_ops *ops); void q6audio_set_acdb_file(char* filename); +#else +static inline void q6audio_register_analog_ops(struct q6audio_analog_ops *ops) {} +static inline void q6audio_set_acdb_file(char* filename) {} +#endif #endif From 34d3308d02e295c33534ef87a0c2f78c5d8fa5be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 21 Apr 2010 14:44:16 -0700 Subject: [PATCH 0545/2556] hack: Replace calls to dmac_inv_range with a WARN to allow compiling msm_camera. Change-Id: I014d2928b884a9b936542e76c5e66b5c3840bd3f --- drivers/media/video/msm/msm_camera.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c index cdb24763ca52f..cababe070c3f4 100644 --- a/drivers/media/video/msm/msm_camera.c +++ b/drivers/media/video/msm/msm_camera.c @@ -34,6 +34,7 @@ #include #include +#define dmac_inv_range(a,b) WARN(1, "need cache invalidate\n") #define MSM_MAX_CAMERA_SENSORS 5 From 12f1a989681e522e4712e8c8b810ce650074415e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 21 Apr 2010 15:09:56 -0700 Subject: [PATCH 0546/2556] [ARM] msm: mahimahi: htc_power_supply: Include wakelock.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ib3a3dea8205eb51b6c9706c6cdad997fc4c27ddd Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/htc_power_supply.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/htc_power_supply.c b/arch/arm/mach-msm/htc_power_supply.c index 6edf9303105dc..9cfe2ccad7f6a 100644 --- a/arch/arm/mach-msm/htc_power_supply.c +++ b/arch/arm/mach-msm/htc_power_supply.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "board-mahimahi.h" From 923fffbd8e4154a3ff96df918f05a6280e2c04a2 Mon Sep 17 00:00:00 2001 From: Eiven Peng Date: Wed, 24 Feb 2010 15:50:29 -0800 Subject: [PATCH 0547/2556] [ARM] msm: clock: print still-running clocks before power-collapse suspend Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/clock.c | 31 +++++++++++++++++++++++++++++++ arch/arm/mach-msm/pm.c | 8 +++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c index 6bdba90cd524c..9f131a3d3ea55 100644 --- a/arch/arm/mach-msm/clock.c +++ b/arch/arm/mach-msm/clock.c @@ -261,6 +261,37 @@ void clk_exit_sleep(void) { } +int clks_print_running(void) +{ + struct clk *clk; + int clk_on_count = 0; + struct hlist_node *pos; + char buf[100]; + char *pbuf = buf; + int size = sizeof(buf); + int wr; + unsigned long flags; + + spin_lock_irqsave(&clocks_lock, flags); + + hlist_for_each_entry(clk, pos, &clocks, list) { + if (clk->count) { + clk_on_count++; + wr = snprintf(pbuf, size, " %s", clk->name); + if (wr >= size) + break; + pbuf += wr; + size -= wr; + } + } + if (clk_on_count) + pr_info("clocks on:%s\n", buf); + + spin_unlock_irqrestore(&clocks_lock, flags); + return !clk_on_count; +} +EXPORT_SYMBOL(clks_print_running); + /* EBI1 is the only shared clock that several clients want to vote on as of * this commit. If this changes in the future, then it might be better to * make clk_min_rate handle the voting or make ebi1_clk_set_min_rate more diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index cbece79ac9a92..2c426d46ce58c 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -46,8 +46,9 @@ enum { MSM_PM_DEBUG_RESET_VECTOR = 1U << 4, MSM_PM_DEBUG_SMSM_STATE = 1U << 5, MSM_PM_DEBUG_IDLE = 1U << 6, + MSM_PM_DEBUG_CLOCK_VOTE = 1U << 7 }; -static int msm_pm_debug_mask; +static int msm_pm_debug_mask = MSM_PM_DEBUG_CLOCK_VOTE; module_param_named(debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); enum { @@ -122,6 +123,7 @@ int64_t msm_timer_enter_idle(void); void msm_timer_exit_idle(int low_power); int msm_irq_idle_sleep_allowed(void); int msm_irq_pending(void); +int clks_print_running(void); static int axi_rate; static int sleep_axi_rate; @@ -303,6 +305,10 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) enter_state = 0; exit_state = 0; } + if ((!from_idle && (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK_VOTE)) || + (from_idle && (msm_pm_debug_mask & MSM_PM_DEBUG_IDLE))) + clks_print_running(); + ret = smsm_change_state(PM_SMSM_WRITE_STATE, PM_SMSM_WRITE_RUN, enter_state); if (ret) { printk(KERN_ERR "msm_sleep(): smsm_change_state %x failed\n", enter_state); From 6a05e4dc3843fb251854f4f3141bc25ef0c21630 Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Mon, 1 Mar 2010 16:18:45 -0800 Subject: [PATCH 0548/2556] [ARM] msm: kgsl: Remove the need to poll for timestamp completion Use conditional RB interrupt generation when we need to wait on timestamps to avoid polling in userspace. Change-Id: Ib0ae4257d2fbc2b220004c4e29137ac7ae1e0589 Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl.c | 2 - drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c | 6 -- drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h | 10 +++ drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c | 21 ++++--- drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c | 60 +++++++++++++----- drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c | 19 ++++++ drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h | 4 ++ drivers/video/msm/gpu/kgsl/kgsl_yamato.c | 65 ++++++++++++++++++-- include/linux/msm_kgsl.h | 7 ++- 9 files changed, 155 insertions(+), 39 deletions(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c index ec2da14d7b48d..a2044251cbfab 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.c +++ b/drivers/video/msm/gpu/kgsl/kgsl.c @@ -424,14 +424,12 @@ static long kgsl_ioctl_device_waittimestamp(struct kgsl_file_private *private, goto done; } - mutex_unlock(&kgsl_driver.mutex); /* Don't wait forever, set a max value for now */ if (param.timeout == -1) param.timeout = 10 * MSEC_PER_SEC; result = kgsl_yamato_waittimestamp(&kgsl_driver.yamato_device, param.timestamp, param.timeout); - mutex_lock(&kgsl_driver.mutex); kgsl_yamato_runpending(&kgsl_driver.yamato_device); done: diff --git a/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c index 7a2e861bf497b..9b7183cbd596c 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c @@ -51,12 +51,6 @@ kgsl_cmdstream_readtimestamp(struct kgsl_device *device, return timestamp; } -static bool timestamp_cmp(unsigned int new, unsigned int old) -{ - int ts_diff = new - old; - return (ts_diff >= 0) || (ts_diff < -20000); -} - int kgsl_cmdstream_check_timestamp(struct kgsl_device *device, unsigned int timestamp) { diff --git a/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h index 6664174fed916..f81ef54673911 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h +++ b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h @@ -22,6 +22,10 @@ kgsl_sharedmem_read(&device->memstore, (data), \ KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp), 4) +/* Flags to control command packet settings */ +#define KGSL_CMD_FLAGS_PMODE 0x00000001 +#define KGSL_CMD_FLAGS_NO_TS_CMP 0x00000002 + int kgsl_cmdstream_init(struct kgsl_device *device); int kgsl_cmdstream_close(struct kgsl_device *device); @@ -41,4 +45,10 @@ kgsl_cmdstream_freememontimestamp(struct kgsl_device *device, uint32_t timestamp, enum kgsl_timestamp_type type); +static inline bool timestamp_cmp(unsigned int new, unsigned int old) +{ + int ts_diff = new - old; + return (ts_diff >= 0) || (ts_diff < -20000); +} + #endif /* __KGSL_CMDSTREAM_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c index d1412b5f7beae..4489dd4eaa77c 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c @@ -26,6 +26,7 @@ #include "kgsl.h" #include "kgsl_log.h" #include "kgsl_pm4types.h" +#include "kgsl_cmdstream.h" #define DISABLE_SHADOW_WRITES /* @@ -1709,8 +1710,8 @@ kgsl_drawctxt_switch(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt, if (active_ctxt->flags & CTXT_FLAGS_SHADER_SAVE) { /* save shader partitioning and instructions. */ KGSL_CTXT_DBG("save shader"); - kgsl_ringbuffer_issuecmds(device, 1, - active_ctxt->shader_save, 3); + kgsl_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, + active_ctxt->shader_save, 3); /* fixup shader partitioning parameter for * SET_SHADER_BASES. @@ -1732,7 +1733,8 @@ kgsl_drawctxt_switch(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt, for (i = 0; i < KGSL_MAX_GMEM_SHADOW_BUFFERS; i++) { if (active_ctxt->user_gmem_shadow[i].gmemshadow. size > 0) { - kgsl_ringbuffer_issuecmds(device, 1, + kgsl_ringbuffer_issuecmds(device, + KGSL_CMD_FLAGS_PMODE, active_ctxt->user_gmem_shadow[i]. gmem_save, 3); @@ -1744,9 +1746,10 @@ kgsl_drawctxt_switch(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt, } } if (numbuffers == 0) { - kgsl_ringbuffer_issuecmds(device, 1, - active_ctxt->context_gmem_shadow. - gmem_save, 3); + kgsl_ringbuffer_issuecmds(device, + KGSL_CMD_FLAGS_PMODE, + active_ctxt->context_gmem_shadow.gmem_save, + 3); /* Restore TP0_CHICKEN */ kgsl_ringbuffer_issuecmds(device, 0, @@ -1776,7 +1779,8 @@ kgsl_drawctxt_switch(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt, for (i = 0; i < KGSL_MAX_GMEM_SHADOW_BUFFERS; i++) { if (drawctxt->user_gmem_shadow[i].gmemshadow. size > 0) { - kgsl_ringbuffer_issuecmds(device, 1, + kgsl_ringbuffer_issuecmds(device, + KGSL_CMD_FLAGS_PMODE, drawctxt->user_gmem_shadow[i]. gmem_restore, 3); @@ -1787,7 +1791,8 @@ kgsl_drawctxt_switch(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt, } } if (numbuffers == 0) { - kgsl_ringbuffer_issuecmds(device, 1, + kgsl_ringbuffer_issuecmds(device, + KGSL_CMD_FLAGS_PMODE, drawctxt->context_gmem_shadow.gmem_restore, 3); diff --git a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c index 264561c4d7b52..472d10c154f9f 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c @@ -74,9 +74,21 @@ void kgsl_cp_intrcallback(struct kgsl_device *device) kgsl_yamato_regread(device, REG_CP_INT_STATUS, &status); - if (status & CP_INT_CNTL__IB1_INT_MASK) { - /*this is the only used soft interrupt */ - KGSL_CMD_WARN("ringbuffer ib1 interrupt\n"); + if (status & CP_INT_CNTL__RB_INT_MASK) { + /* signal intr completion event */ + int init_reftimestamp = 0x7fffffff; + int enableflag = 0; + kgsl_sharedmem_write(&rb->device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable), + &enableflag, 4); + kgsl_sharedmem_write(&rb->device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), + &init_reftimestamp, 4); + KGSL_CMD_WARN("ringbuffer rb interrupt\n"); + } + + if (status & (CP_INT_CNTL__IB1_INT_MASK | CP_INT_CNTL__RB_INT_MASK)) { + KGSL_CMD_WARN("ringbuffer ib1/rb interrupt\n"); wake_up_interruptible_all(&device->ib1_wq); } if (status & CP_INT_CNTL__T0_PACKET_IN_IB_MASK) { @@ -107,9 +119,6 @@ void kgsl_cp_intrcallback(struct kgsl_device *device) if (status & CP_INT_CNTL__SW_INT_MASK) KGSL_CMD_DBG("ringbuffer software interrupt\n"); - if (status & CP_INT_CNTL__RB_INT_MASK) - KGSL_CMD_DBG("ringbuffer rb interrupt\n"); - if (status & CP_INT_CNTL__IB2_INT_MASK) KGSL_CMD_DBG("ringbuffer ib2 interrupt\n"); @@ -586,22 +595,22 @@ int kgsl_ringbuffer_close(struct kgsl_ringbuffer *rb) static uint32_t kgsl_ringbuffer_addcmds(struct kgsl_ringbuffer *rb, - int pmodeoff, unsigned int *cmds, + int flags, unsigned int *cmds, int sizedwords) { - unsigned int pmodesizedwords; unsigned int *ringcmds; unsigned int timestamp; + unsigned int total_sizedwords = sizedwords + 6; /* reserve space to temporarily turn off protected mode * error checking if needed */ - pmodesizedwords = pmodeoff ? 4 : 0; + total_sizedwords += flags & KGSL_CMD_FLAGS_PMODE ? 4 : 0; + total_sizedwords += !(flags & KGSL_CMD_FLAGS_NO_TS_CMP) ? 9 : 0; - ringcmds = kgsl_ringbuffer_allocspace(rb, - pmodesizedwords + sizedwords + 6); + ringcmds = kgsl_ringbuffer_allocspace(rb, total_sizedwords); - if (pmodeoff) { + if (flags & KGSL_CMD_FLAGS_PMODE) { /* disable protected mode error checking */ *ringcmds++ = pm4_type3_packet(PM4_SET_PROTECTED_MODE, 1); *ringcmds++ = 0; @@ -611,7 +620,7 @@ kgsl_ringbuffer_addcmds(struct kgsl_ringbuffer *rb, ringcmds += sizedwords; - if (pmodeoff) { + if (flags & KGSL_CMD_FLAGS_PMODE) { /* re-enable protected mode error checking */ *ringcmds++ = pm4_type3_packet(PM4_SET_PROTECTED_MODE, 1); *ringcmds++ = 1; @@ -630,6 +639,23 @@ kgsl_ringbuffer_addcmds(struct kgsl_ringbuffer *rb, KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp)); *ringcmds++ = rb->timestamp; + if (!(flags & KGSL_CMD_FLAGS_NO_TS_CMP)) { + /* Add idle packet so avoid RBBM errors */ + *ringcmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *ringcmds++ = 0x00000000; + /* Conditional execution based on memory values */ + *ringcmds++ = pm4_type3_packet(PM4_COND_EXEC, 4); + *ringcmds++ = (rb->device->memstore.gpuaddr + + KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable)) >> 2; + *ringcmds++ = (rb->device->memstore.gpuaddr + + KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts)) >> 2; + *ringcmds++ = rb->timestamp; + /* # of conditional command DWORDs */ + *ringcmds++ = 2; + *ringcmds++ = pm4_type3_packet(PM4_INTERRUPT, 1); + *ringcmds++ = CP_INT_CNTL__RB_INT_MASK; + } + kgsl_ringbuffer_submit(rb); GSL_RB_STATS(rb->stats.words_total += sizedwords); @@ -643,17 +669,17 @@ kgsl_ringbuffer_addcmds(struct kgsl_ringbuffer *rb, uint32_t kgsl_ringbuffer_issuecmds(struct kgsl_device *device, - int pmodeoff, + int flags, unsigned int *cmds, int sizedwords) { unsigned int timestamp; struct kgsl_ringbuffer *rb = &device->ringbuffer; - KGSL_CMD_VDBG("enter (device->id=%d, pmodeoff=%d, cmds=%p, " - "sizedwords=%d)\n", device->id, pmodeoff, cmds, sizedwords); + KGSL_CMD_VDBG("enter (device->id=%d, flags=%d, cmds=%p, " + "sizedwords=%d)\n", device->id, flags, cmds, sizedwords); - timestamp = kgsl_ringbuffer_addcmds(rb, pmodeoff, cmds, sizedwords); + timestamp = kgsl_ringbuffer_addcmds(rb, flags, cmds, sizedwords); KGSL_CMD_VDBG("return %d\n)", timestamp); return timestamp; diff --git a/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c index c5a9a8a5ec8f9..4a1b42125e6d8 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c @@ -261,6 +261,25 @@ kgsl_sharedmem_read(const struct kgsl_memdesc *memdesc, void *dst, return 0; } +int +kgsl_sharedmem_write(const struct kgsl_memdesc *memdesc, + unsigned int offsetbytes, + void *value, unsigned int sizebytes) +{ + if (memdesc == NULL || memdesc->hostptr == NULL) { + KGSL_MEM_ERR("bad ptr memdesc %p hostptr %p\n", memdesc, + (memdesc ? memdesc->hostptr : NULL)); + return -EINVAL; + } + if (offsetbytes + sizebytes > memdesc->size) { + KGSL_MEM_ERR("bad range: offset %d size %d memdesc %d\n", + offsetbytes, sizebytes, memdesc->size); + return -ERANGE; + } + memcpy(memdesc->hostptr + offsetbytes, value, sizebytes); + return 0; +} + int kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc, unsigned int offsetbytes, unsigned int value, unsigned int sizebytes) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h index eaf215613ac89..eaf840638bade 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h +++ b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h @@ -96,6 +96,10 @@ void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc); int kgsl_sharedmem_read(const struct kgsl_memdesc *memdesc, void *dst, unsigned int offsetbytes, unsigned int sizebytes); +int kgsl_sharedmem_write(const struct kgsl_memdesc *memdesc, + unsigned int offsetbytes, void *value, + unsigned int sizebytes); + int kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc, unsigned int offsetbytes, unsigned int value, unsigned int sizebytes); diff --git a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c index d8d47d0f3c071..f29a9d1e9afd0 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c @@ -398,7 +398,8 @@ int kgsl_yamato_setstate(struct kgsl_device *device, uint32_t flags) sizedwords += 21; } - kgsl_ringbuffer_issuecmds(device, 1, &link[0], sizedwords); + kgsl_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, + &link[0], sizedwords); } else { KGSL_MEM_DBG("regs\n"); @@ -453,6 +454,7 @@ kgsl_yamato_getchipid(struct kgsl_device *device) int kgsl_yamato_init(struct kgsl_device *device, struct kgsl_devconfig *config) { int status = -EINVAL; + int init_reftimestamp = 0x7fffffff; struct kgsl_memregion *regspace = &device->regspace; unsigned int memflags = KGSL_MEMFLAGS_ALIGNPAGE | KGSL_MEMFLAGS_CONPHYS; @@ -546,6 +548,10 @@ int kgsl_yamato_init(struct kgsl_device *device, struct kgsl_devconfig *config) } kgsl_sharedmem_set(&device->memstore, 0, 0, device->memstore.size); + kgsl_sharedmem_write(&device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), + &init_reftimestamp, 4); + kgsl_yamato_regwrite(device, REG_RBBM_DEBUG, 0x00080000); pr_info("msm_kgsl: initilized dev=%d mmu=%s\n", device->id, kgsl_mmu_isenabled(&device->mmu) ? "on" : "off"); @@ -735,6 +741,20 @@ int kgsl_yamato_getproperty(struct kgsl_device *device, status = 0; } break; + case KGSL_PROP_INTERRUPT_WAITS: + { + int int_waits = 1; + if (sizebytes != sizeof(int)) { + status = -EINVAL; + break; + } + if (copy_to_user(value, &int_waits, sizeof(int))) { + status = -EFAULT; + break; + } + status = 0; + } + break; default: status = -EINVAL; } @@ -856,15 +876,12 @@ int kgsl_yamato_regwrite(struct kgsl_device *device, unsigned int offsetwords, return 0; } -int kgsl_yamato_waittimestamp(struct kgsl_device *device, +static inline int _wait_timestamp(struct kgsl_device *device, unsigned int timestamp, unsigned int msecs) { long status; - KGSL_DRV_INFO("enter (device=%p,timestamp=%d,timeout=0x%08x)\n", - device, timestamp, msecs); - status = wait_event_interruptible_timeout(device->ib1_wq, kgsl_cmdstream_check_timestamp(device, timestamp), msecs_to_jiffies(msecs)); @@ -878,6 +895,44 @@ int kgsl_yamato_waittimestamp(struct kgsl_device *device, } } + return (int)status; +} + +/* MUST be called with the kgsl_driver.mutex held */ +int kgsl_yamato_waittimestamp(struct kgsl_device *device, + unsigned int timestamp, + unsigned int msecs) +{ + long status = 0; + uint32_t ref_ts; + int enableflag = 1; + unsigned int cmd[2]; + + KGSL_DRV_INFO("enter (device=%p,timestamp=%d,timeout=0x%08x)\n", + device, timestamp, msecs); + + if (!kgsl_cmdstream_check_timestamp(device, timestamp)) { + kgsl_sharedmem_read(&device->memstore, &ref_ts, + KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), 4); + if (timestamp_cmp(ref_ts, timestamp)) { + kgsl_sharedmem_write(&device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), + ×tamp, 4); + } + + cmd[0] = pm4_type3_packet(PM4_INTERRUPT, 1); + cmd[1] = CP_INT_CNTL__IB1_INT_MASK; + kgsl_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NO_TS_CMP, + cmd, 2); + kgsl_sharedmem_write(&device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable), + &enableflag, 4); + + mutex_unlock(&kgsl_driver.mutex); + status = _wait_timestamp(device, timestamp, msecs); + mutex_lock(&kgsl_driver.mutex); + } + KGSL_DRV_INFO("return %ld\n", status); return (int)status; } diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index a06507be81ffb..28a1e1e8944f0 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -66,6 +66,10 @@ struct kgsl_devmemstore { unsigned int sbz; volatile unsigned int eoptimestamp; unsigned int sbz2; + volatile unsigned int ts_cmp_enable; + unsigned int sbz3; + volatile unsigned int ref_wait_ts; + unsigned int sbz4; }; #define KGSL_DEVICE_MEMSTORE_OFFSET(field) \ @@ -86,7 +90,8 @@ enum kgsl_property_type { KGSL_PROP_DEVICE_POWER = 0x00000003, KGSL_PROP_SHMEM = 0x00000004, KGSL_PROP_SHMEM_APERTURES = 0x00000005, - KGSL_PROP_MMU_ENABLE = 0x00000006 + KGSL_PROP_MMU_ENABLE = 0x00000006, + KGSL_PROP_INTERRUPT_WAITS = 0x00000007, }; struct kgsl_shadowprop { From 222d564474a2c7f1e39c1f586fb182a544ce9e36 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Wed, 24 Feb 2010 16:13:20 -0800 Subject: [PATCH 0549/2556] qsd8k: debugging / ramdump driver for QDSP6 Provide a mechanism for userspace to get notification of DSP crashes, and to extract the DSP's RAM image before allowing the crash to "complete". Signed-off-by: Brian Swetland --- .../mach-msm/include/mach/msm_qdsp6_audio.h | 3 + arch/arm/mach-msm/qdsp6/Makefile | 1 + arch/arm/mach-msm/qdsp6/dsp_debug.c | 163 ++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 arch/arm/mach-msm/qdsp6/dsp_debug.c diff --git a/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h index c4a0fdbf97cbe..54614a9877035 100644 --- a/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h +++ b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h @@ -101,4 +101,7 @@ static inline void q6audio_register_analog_ops(struct q6audio_analog_ops *ops) { static inline void q6audio_set_acdb_file(char* filename) {} #endif +/* signal non-recoverable DSP error so we can log and/or panic */ +void q6audio_dsp_not_responding(void); + #endif diff --git a/arch/arm/mach-msm/qdsp6/Makefile b/arch/arm/mach-msm/qdsp6/Makefile index 2f42decfdf252..1be03b90d4c87 100644 --- a/arch/arm/mach-msm/qdsp6/Makefile +++ b/arch/arm/mach-msm/qdsp6/Makefile @@ -7,3 +7,4 @@ obj-y += routing.o obj-y += audio_ctl.o obj-y += msm_q6vdec.o obj-y += msm_q6venc.o +obj-y += dsp_debug.o \ No newline at end of file diff --git a/arch/arm/mach-msm/qdsp6/dsp_debug.c b/arch/arm/mach-msm/qdsp6/dsp_debug.c new file mode 100644 index 0000000000000..70a038a9822b5 --- /dev/null +++ b/arch/arm/mach-msm/qdsp6/dsp_debug.c @@ -0,0 +1,163 @@ +/* arch/arm/mach-msm/qdsp6/dsp_dump.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Brian Swetland + * + * 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 "../proc_comm.h" + +static wait_queue_head_t dsp_wait; +static int dsp_has_crashed; +static int dsp_wait_count; + +void q6audio_dsp_not_responding(void) +{ + if (dsp_wait_count) { + dsp_has_crashed = 1; + wake_up(&dsp_wait); + + while (dsp_has_crashed != 2) + wait_event(dsp_wait, dsp_has_crashed == 2); + } + BUG(); +} + +static int dsp_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t dsp_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + char cmd[32]; + + if (count >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buf, count)) + return -EFAULT; + cmd[count] = 0; + + if ((count > 1) && (cmd[count-1] == '\n')) + cmd[count-1] = 0; + + if (!strcmp(cmd, "wait-for-crash")) { + while (!dsp_has_crashed) { + int res; + dsp_wait_count++; + res = wait_event_interruptible(dsp_wait, dsp_has_crashed); + dsp_wait_count--; + if (res < 0) + return res; + } +#if defined(CONFIG_MACH_MAHIMAHI) + /* assert DSP NMI */ + msm_proc_comm(PCOM_CUSTOMER_CMD1, 0, 0); + msleep(250); +#endif + } else if (!strcmp(cmd, "boom")) { + q6audio_dsp_not_responding(); + } else if (!strcmp(cmd, "continue-crash")) { + dsp_has_crashed = 2; + wake_up(&dsp_wait); + } else { + pr_err("unknown dsp_debug command: %s\n", cmd); + } + + return count; +} + +#define DSP_RAM_BASE 0x2E800000 +#define DSP_RAM_SIZE 0x01800000 + +static unsigned copy_ok_count; + +static ssize_t dsp_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + size_t actual = 0; + size_t mapsize = PAGE_SIZE; + unsigned addr; + void __iomem *ptr; + + if (*pos >= DSP_RAM_SIZE) + return 0; + + if (*pos & (PAGE_SIZE - 1)) + return -EINVAL; + + addr = (*pos + DSP_RAM_BASE); + + /* don't blow up if we're unaligned */ + if (addr & (PAGE_SIZE - 1)) + mapsize *= 2; + + while (count >= PAGE_SIZE) { + ptr = ioremap(addr, mapsize); + if (!ptr) { + pr_err("dsp: map error @ %x\n", addr); + return -EFAULT; + } + if (copy_to_user(buf, ptr, PAGE_SIZE)) { + iounmap(ptr); + pr_err("dsp: copy error @ %p\n", buf); + return -EFAULT; + } + copy_ok_count += PAGE_SIZE; + iounmap(ptr); + addr += PAGE_SIZE; + buf += PAGE_SIZE; + actual += PAGE_SIZE; + count -= PAGE_SIZE; + } + + *pos += actual; + return actual; +} + +static int dsp_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations dsp_fops = { + .owner = THIS_MODULE, + .open = dsp_open, + .read = dsp_read, + .write = dsp_write, + .release = dsp_release, +}; + +static struct miscdevice dsp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "dsp_debug", + .fops = &dsp_fops, +}; + + +static int __init dsp_init(void) +{ + init_waitqueue_head(&dsp_wait); + return misc_register(&dsp_misc); +} + +device_initcall(dsp_init); From 106db808acf67d26f8353bcba2bf790568891714 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Sun, 21 Feb 2010 16:53:49 -0800 Subject: [PATCH 0550/2556] qsd8k: audio: call q6audio_dsp_not_responding() instead of BUG() This hooks into the DSP crash notification / dumping mechanism. Signed-off-by: Brian Swetland --- arch/arm/mach-msm/qdsp6/dal.c | 3 ++- arch/arm/mach-msm/qdsp6/pcm_in.c | 2 +- arch/arm/mach-msm/qdsp6/pcm_out.c | 2 +- arch/arm/mach-msm/qdsp6/q6audio.c | 6 +++--- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-msm/qdsp6/dal.c b/arch/arm/mach-msm/qdsp6/dal.c index 584ac32b453e2..66c2fc6a31045 100644 --- a/arch/arm/mach-msm/qdsp6/dal.c +++ b/arch/arm/mach-msm/qdsp6/dal.c @@ -26,6 +26,7 @@ #include #include +#include #include "dal.h" @@ -365,7 +366,7 @@ int dal_call_raw(struct dal_client *client, dal_trace_dump(client); pr_err("dal: call timed out. dsp is probably dead.\n"); dal_trace_print(hdr, data, data_len, 0); - BUG(); + q6audio_dsp_not_responding(); } return client->status; diff --git a/arch/arm/mach-msm/qdsp6/pcm_in.c b/arch/arm/mach-msm/qdsp6/pcm_in.c index 3b3d23bfd7a9a..d5a8f7716eced 100644 --- a/arch/arm/mach-msm/qdsp6/pcm_in.c +++ b/arch/arm/mach-msm/qdsp6/pcm_in.c @@ -160,7 +160,7 @@ static ssize_t q6_in_read(struct file *file, char __user *buf, if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) { audio_client_dump(ac); pr_err("pcm_read: timeout. dsp dead?\n"); - BUG(); + q6audio_dsp_not_responding(); } xfer = count; diff --git a/arch/arm/mach-msm/qdsp6/pcm_out.c b/arch/arm/mach-msm/qdsp6/pcm_out.c index edd64b705e32e..11221686566da 100644 --- a/arch/arm/mach-msm/qdsp6/pcm_out.c +++ b/arch/arm/mach-msm/qdsp6/pcm_out.c @@ -177,7 +177,7 @@ static ssize_t pcm_write(struct file *file, const char __user *buf, if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) { audio_client_dump(ac); pr_err("pcm_write: timeout. dsp dead?\n"); - BUG(); + q6audio_dsp_not_responding(); } xfer = count; diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c index e407da37c7601..ae78079f45fdb 100644 --- a/arch/arm/mach-msm/qdsp6/q6audio.c +++ b/arch/arm/mach-msm/qdsp6/q6audio.c @@ -348,7 +348,7 @@ static int audio_ioctl(struct audio_client *ac, void *ptr, uint32_t len) if (!wait_event_timeout(ac->wait, (ac->cb_status != -EBUSY), 5*HZ)) { dal_trace_dump(ac->client); pr_err("audio_ioctl: timeout. dsp dead?\n"); - BUG(); + q6audio_dsp_not_responding(); } return ac->cb_status; } @@ -1448,7 +1448,7 @@ struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, if (rc == 0) break; if (retry == 0) - BUG(); + q6audio_dsp_not_responding(); pr_err("q6audio: open pcm error %d, retrying\n", rc); msleep(1); } @@ -1473,7 +1473,7 @@ struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, if (rc == 0) break; if (retry == 0) - BUG(); + q6audio_dsp_not_responding(); pr_err("q6audio: stream start error %d, retrying\n", rc); } From 80178ab65b7b1ddbdfb5b205280c50603c3ab3a4 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Sat, 27 Feb 2010 16:13:23 -0800 Subject: [PATCH 0551/2556] qsd8k: video: new video encoder driver The latest DSP image for 8250 does h264/mpeg4/etc with no assist from the apps processor. This driver supports the new DSP interface. It is not backward compatible with the old venc driver. Change-Id: Ifb9549896e45cf9f5cd54dabb5b1de7dc717a9fe Signed-off-by: Brian Swetland --- arch/arm/mach-msm/qdsp6/msm_q6vdec.c | 2 +- arch/arm/mach-msm/qdsp6/msm_q6venc.c | 1452 +++++++++++++++++--------- include/linux/msm_q6venc.h | 358 +++++-- 3 files changed, 1249 insertions(+), 563 deletions(-) diff --git a/arch/arm/mach-msm/qdsp6/msm_q6vdec.c b/arch/arm/mach-msm/qdsp6/msm_q6vdec.c index ec95ecfbe5a0e..04ac8a7bf9bf7 100644 --- a/arch/arm/mach-msm/qdsp6/msm_q6vdec.c +++ b/arch/arm/mach-msm/qdsp6/msm_q6vdec.c @@ -685,7 +685,7 @@ static void vdec_dcdone_handler(struct vdec_data *vd, void *frame, unsigned long flags; int found = 0; - if (frame_size != sizeof(struct vdec_frame_info)) { + if (frame_size < sizeof(struct vdec_frame_info)) { pr_warning("%s: msg size mismatch %d != %d\n", __func__, frame_size, sizeof(struct vdec_frame_info)); return; diff --git a/arch/arm/mach-msm/qdsp6/msm_q6venc.c b/arch/arm/mach-msm/qdsp6/msm_q6venc.c index 9010b3cb322dd..462a24f029bc8 100644 --- a/arch/arm/mach-msm/qdsp6/msm_q6venc.c +++ b/arch/arm/mach-msm/qdsp6/msm_q6venc.c @@ -1,630 +1,1120 @@ -/* - * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. - * Copyright (c) 2009, Google Inc. +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * Copyright (c) 2009-2010, Google Inc. * - * Original authors: Code Aurora Forum - * Major cleanup: Dima Zavin + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Code Aurora Forum nor - * the names of its contributors may be used to endorse or promote - * products derived from this software without specific prior written - * permission. + * 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. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * Original driver and v2 protocol changes from Code Aurora. + * Heavily modified by Dima Zavin + * Further cleanup by Brian Swetland * */ -//#define DEBUG 1 - -#include +#include #include +#include #include -#include #include -#include #include -#include #include #include #include #include #include #include - -#include +#include #include "dal.h" #define DALDEVICEID_VENC_DEVICE 0x0200002D #define DALDEVICEID_VENC_PORTNAME "DSP_DAL_AQ_VID" +#define VENC_NAME "q6venc" +#define VENC_MSG_MAX 128 + +#define VENC_INTERFACE_VERSION 0x00020000 +#define MAJOR_MASK 0xFFFF0000 +#define MINOR_MASK 0x0000FFFF +#define VENC_GET_MAJOR_VERSION(version) ((version & MAJOR_MASK)>>16) +#define VENC_GET_MINOR_VERSION(version) (version & MINOR_MASK) + +#define VERSION_CHECK 0 + +static DEFINE_MUTEX(idlecount_lock); +static int idlecount; +static struct wake_lock wakelock; +static struct wake_lock idlelock; + +static void prevent_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (++idlecount == 1) { + wake_lock(&idlelock); + wake_lock(&wakelock); + } + mutex_unlock(&idlecount_lock); +} + +static void allow_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (--idlecount == 0) { + wake_unlock(&idlelock); + wake_unlock(&wakelock); + } + mutex_unlock(&idlecount_lock); +} + enum { - VENC_DALRPC_INITIALIZE = DAL_OP_FIRST_DEVICE_API, - VENC_DALRPC_SET_CB_CHANNEL, - VENC_DALRPC_ENCODE, - VENC_DALRPC_INTRA_REFRESH, - VENC_DALRPC_RC_CONFIG, - VENC_DALRPC_ENCODE_CONFIG, + VENC_BUFFER_TYPE_INPUT, + VENC_BUFFER_TYPE_OUTPUT, + VENC_BUFFER_TYPE_QDSP6, + VENC_BUFFER_TYPE_HDR +}; +enum { + VENC_DALRPC_GET_SYNTAX_HEADER = DAL_OP_FIRST_DEVICE_API, + VENC_DALRPC_UPDATE_INTRA_REFRESH, + VENC_DALRPC_UPDATE_FRAME_RATE, + VENC_DALRPC_UPDATE_BITRATE, + VENC_DALRPC_UPDATE_QP_RANGE, + VENC_DALRPC_UPDATE_INTRA_PERIOD, + VENC_DALRPC_REQUEST_IFRAME, + VENC_DALRPC_START, VENC_DALRPC_STOP, + VENC_DALRPC_SUSPEND, + VENC_DALRPC_RESUME, + VENC_DALRPC_FLUSH, + VENC_DALRPC_QUEUE_INPUT, + VENC_DALRPC_QUEUE_OUTPUT }; - -struct callback_event_data { - u32 data_notify_event; - u32 enc_cb_handle; - u32 empty_input_buffer_event; +struct venc_input_payload { + u32 data; }; - -struct buf_info { - unsigned long paddr; - unsigned long vaddr; - struct file *file; - struct venc_buf venc_buf; +struct venc_output_payload { + u32 size; + long long time_stamp; + u32 flags; + u32 data; + u32 client_data_from_input; }; - -#define VENC_MAX_BUF_NUM 15 -#define RLC_MAX_BUF_NUM 2 -#define BITS_PER_PIXEL 12 -#define PIXELS_PER_MACROBLOCK 16 - -#define VENC_CB_EVENT_ID 0xd0e4c0de - -struct q6venc_dev { - struct dal_client *venc; - struct callback_event_data cb_ev_data; - bool stop_encode; - struct buf_info rlc_bufs[RLC_MAX_BUF_NUM]; - unsigned int rlc_buf_index; - unsigned int rlc_buf_len; - unsigned int enc_buf_size; - struct buf_info enc_bufs[VENC_MAX_BUF_NUM]; - unsigned int num_enc_bufs; - wait_queue_head_t encode_wq; - - /* protects all state in q6venc_dev except for cb stuff below */ - struct mutex lock; - - /* protects encode_done and done_frame inside the callback */ - spinlock_t done_lock; - struct frame_type done_frame; - bool encode_done; +union venc_payload { + struct venc_input_payload input_payload; + struct venc_output_payload output_payload; }; - -static int get_buf_info(struct buf_info *buf_info, struct venc_buf *venc_buf) -{ - unsigned long len; - unsigned long vaddr; +struct venc_msg_type { + u32 event; + u32 status; + union venc_payload payload; +}; +struct venc_input_buf { + struct venc_buf_type yuv_buf; + u32 data_size; + long long time_stamp; + u32 flags; + u32 dvs_offsetx; + u32 dvs_offsety; + u32 client_data; + u32 op_client_data; +}; +struct venc_output_buf { + struct venc_buf_type bit_stream_buf; + u32 client_data; +}; +struct venc_buf { + int fd; + u32 offset; + u32 size; + u32 btype; unsigned long paddr; struct file *file; - int ret; +}; +struct venc_pmem_list { + struct list_head list; + struct venc_buf buf; +}; +struct venc_qmsg { + struct list_head list; + struct venc_msg msg; +}; +struct venc_dev { + bool stop_called; + enum venc_state_type state; + + struct list_head msg_pool; + struct list_head msg_queue; + spinlock_t msg_lock; + + struct list_head venc_pmem_list_head; + spinlock_t venc_pmem_list_lock; + struct dal_client *q6_handle; + wait_queue_head_t venc_msg_evt; + struct device *class_devp; +}; - ret = get_pmem_file(venc_buf->fd, &paddr, &vaddr, &len, &file); - if (ret) { - pr_err("%s: get_pmem_file failed for fd=%d offset=%ld\n", - __func__, venc_buf->fd, venc_buf->offset); - return ret; - } else if (venc_buf->offset >= len) { - /* XXX: we really should check venc_buf->size too, but userspace - * sometimes leaves this uninitialized (in encode ioctl) */ - pr_err("%s: invalid offset/size (%ld + %ld > %ld) for fd=%d\n", - __func__, venc_buf->offset, venc_buf->size, len, - venc_buf->fd); - put_pmem_file(file); - return -EINVAL; - } +#define DEBUG_VENC 0 +#if DEBUG_VENC +#define TRACE(fmt, x...) \ + do { pr_debug("%s:%d " fmt, __func__, __LINE__, ##x); } while (0) +#else +#define TRACE(fmt, x...) do { } while (0) +#endif + +static struct cdev cdev; +static dev_t venc_dev_num; +static struct class *venc_class; +static struct venc_dev *venc_device_p; +static int venc_ref; + +static inline int venc_check_version(u32 client, u32 server) +{ + int ret = -EINVAL; - buf_info->file = file; - buf_info->paddr = paddr + venc_buf->offset; - buf_info->vaddr = vaddr; - memcpy(&buf_info->venc_buf, venc_buf, sizeof(struct venc_buf)); - return 0; + if ((VENC_GET_MAJOR_VERSION(client) == VENC_GET_MAJOR_VERSION(server)) + && (VENC_GET_MINOR_VERSION(client) <= + VENC_GET_MINOR_VERSION(server))) + ret = 0; + + return ret; } -static void put_buf_info(struct buf_info *buf_info) +static struct venc_qmsg *__dequeue(spinlock_t *lock, struct list_head *queue) { - if (!buf_info || !buf_info->file) - return; - put_pmem_file(buf_info->file); - buf_info->file = NULL; + unsigned long flags; + struct venc_qmsg *msg; + spin_lock_irqsave(lock, flags); + if (list_empty(queue)) { + msg = NULL; + } else { + msg = list_first_entry(queue, struct venc_qmsg, list); + list_del(&msg->list); + } + spin_unlock_irqrestore(lock, flags); + return msg; } -static void q6venc_callback(void *context, void *data, uint32_t len) +static inline struct venc_qmsg *venc_alloc_msg(struct venc_dev *dvenc) { - struct q6venc_dev *q6venc = context; - struct q6_frame_type *q6frame = data; - struct buf_info *rlc_buf; - unsigned long flags; - int i; + return __dequeue(&dvenc->msg_lock, &dvenc->msg_pool); +} +static inline struct venc_qmsg *venc_recv_msg(struct venc_dev *dvenc) +{ + return __dequeue(&dvenc->msg_lock, &dvenc->msg_queue); +} - pr_debug("%s \n", __func__); +static void venc_free_msg(struct venc_dev *dvenc, struct venc_qmsg *msg) +{ + unsigned long flags; + spin_lock_irqsave(&dvenc->msg_lock, flags); + list_add_tail(&msg->list, &dvenc->msg_pool); + spin_unlock_irqrestore(&dvenc->msg_lock, flags); +} - spin_lock_irqsave(&q6venc->done_lock, flags); - q6venc->encode_done = true; - for (i = 0; i < RLC_MAX_BUF_NUM; ++i) { - rlc_buf = &q6venc->rlc_bufs[i]; - if (rlc_buf->paddr == q6frame->frame_addr) - goto frame_found; +static void venc_post(struct venc_dev *dvenc, + unsigned code, unsigned status, + union venc_msg_data *data) +{ + unsigned long flags; + struct venc_qmsg *msg; + msg = venc_alloc_msg(dvenc); + if (msg == NULL) { + pr_err("%s cannot alloc message\n", __func__); + return; + } + msg->msg.msg_code = code; + msg->msg.status_code = status; + if (data) { + msg->msg.msg_data_size = sizeof(union venc_msg_data); + memcpy(&msg->msg.msg_data, data, sizeof(union venc_msg_data)); + } else { + msg->msg.msg_data_size = 0; } - pr_err("%s: got incorrect phy address 0x%08x from q6 \n", __func__, - q6frame->frame_addr); - q6venc->done_frame.q6_frame_type.frame_len = 0; - wake_up_interruptible(&q6venc->encode_wq); - goto done; + spin_lock_irqsave(&dvenc->msg_lock, flags); + list_add_tail(&msg->list, &dvenc->msg_queue); + spin_unlock_irqrestore(&dvenc->msg_lock, flags); + wake_up(&dvenc->venc_msg_evt); +} -frame_found: - memcpy(&q6venc->done_frame.frame_addr, &rlc_buf->venc_buf, - sizeof(struct venc_buf)); - memcpy(&q6venc->done_frame.q6_frame_type, q6frame, - sizeof(struct q6_frame_type)); +static struct venc_pmem_list *venc_add_pmem_to_list(struct venc_dev *dvenc, + struct venc_pmem *mptr, + u32 btype) +{ + int ret = 0; + unsigned long flags; + unsigned long len; + unsigned long vaddr; + struct venc_pmem_list *plist = NULL; + + plist = kzalloc(sizeof(struct venc_pmem_list), GFP_KERNEL); + if (!plist) { + pr_err("%s: kzalloc failed\n", __func__); + return NULL; + } - dmac_inv_range((const void *)q6venc->rlc_bufs[i].vaddr, - (const void *)(q6venc->rlc_bufs[i].vaddr + - q6venc->rlc_buf_len)); + ret = get_pmem_file(mptr->fd, &(plist->buf.paddr), + &vaddr, &len, &(plist->buf.file)); - wake_up_interruptible(&q6venc->encode_wq); + /* xxx bounds checking insufficient here */ + if (ret) { + pr_err("%s: get_pmem_file failed for fd=%d offset=%d\n", + __func__, mptr->fd, mptr->offset); + goto err_venc_add_pmem; + } else if (mptr->offset >= len) { + pr_err("%s: invalid offset (%d > %ld) for fd=%d\n", + __func__, mptr->offset, len, mptr->fd); + ret = -EINVAL; + goto err_venc_get_pmem; + } -done: - spin_unlock_irqrestore(&q6venc->done_lock, flags); + plist->buf.fd = mptr->fd; + plist->buf.paddr += mptr->offset; + plist->buf.size = mptr->size; + plist->buf.btype = btype; + plist->buf.offset = mptr->offset; + + spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags); + list_add(&plist->list, &dvenc->venc_pmem_list_head); + spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags); + return plist; + +err_venc_get_pmem: + put_pmem_file(plist->buf.file); +err_venc_add_pmem: + kfree(plist); + return NULL; } -static void callback(void *data, int len, void *cookie) +static struct venc_pmem_list *venc_get_pmem_from_list( + struct venc_dev *dvenc, u32 pmem_fd, + u32 offset, u32 btype) { - struct q6venc_dev *ve = (struct q6venc_dev *)cookie; - uint32_t *tmp = (uint32_t *) data; + struct venc_pmem_list *plist; + unsigned long flags; + struct file *file; + int found = 0; + + file = fget(pmem_fd); + if (!file) { + pr_err("%s: invalid encoder buffer fd(%d)\n", __func__, + pmem_fd); + return NULL; + } + spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags); + list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) { + if (plist->buf.btype == btype && plist->buf.file == file && + plist->buf.offset == offset) { + found = 1; + break; + } + } + spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags); + fput(file); + if (found) + return plist; - if (tmp[0] == VENC_CB_EVENT_ID) - q6venc_callback(ve, &tmp[3], tmp[2]); else - pr_err("%s: Unknown callback received for %p\n", __func__, ve); + return NULL; } -static int q6venc_open(struct inode *inode, struct file *file) +static int venc_set_buffer(struct venc_dev *dvenc, void *argp, + u32 btype) { - struct q6venc_dev *q6venc; - int err; + struct venc_pmem pmem; + struct venc_pmem_list *plist; + int ret = 0; - q6venc = kzalloc(sizeof(struct q6venc_dev), GFP_KERNEL); - if (!q6venc) { - pr_err("%s: Unable to allocate memory for q6venc_dev\n", - __func__); - return -ENOMEM; + ret = copy_from_user(&pmem, argp, sizeof(pmem)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; } - - file->private_data = q6venc; - - init_waitqueue_head(&q6venc->encode_wq); - mutex_init(&q6venc->lock); - spin_lock_init(&q6venc->done_lock); - - q6venc->venc = dal_attach(DALDEVICEID_VENC_DEVICE, - DALDEVICEID_VENC_PORTNAME, - callback, q6venc); - if (!q6venc->venc) { - pr_err("%s: dal_attach failed\n", __func__); - err = -EIO; - goto err_dal_attach; + plist = venc_add_pmem_to_list(dvenc, &pmem, btype); + if (plist == NULL) { + pr_err("%s: buffer add_to_pmem_list failed\n", + __func__); + return -EPERM; } + return ret; +} - q6venc->cb_ev_data.enc_cb_handle = VENC_CB_EVENT_ID; - err = dal_call_f5(q6venc->venc, VENC_DALRPC_SET_CB_CHANNEL, - &q6venc->cb_ev_data, sizeof(q6venc->cb_ev_data)); - if (err) { - pr_err("%s: set_cb_channgel failed\n",__func__); - goto err_dal_call_set_cb; +static int venc_assign_q6_buffers(struct venc_dev *dvenc, + struct venc_buffers *pbufs, + struct venc_nonio_buf_config *pcfg) +{ + int ret = 0; + struct venc_pmem_list *plist; + + plist = venc_add_pmem_to_list(dvenc, &(pbufs->recon_buf[0]), + VENC_BUFFER_TYPE_QDSP6); + if (plist == NULL) { + pr_err("%s: recon_buf0 failed to add_to_pmem_list\n", + __func__); + return -EPERM; } + pcfg->recon_buf1.region = 0; + pcfg->recon_buf1.phys = plist->buf.paddr; + pcfg->recon_buf1.size = plist->buf.size; + pcfg->recon_buf1.offset = 0; + + plist = venc_add_pmem_to_list(dvenc, &(pbufs->recon_buf[1]), + VENC_BUFFER_TYPE_QDSP6); + if (plist == NULL) { + pr_err("%s: recons_buf1 failed to add_to_pmem_list\n", + __func__); + return -EPERM; + } + pcfg->recon_buf2.region = 0; + pcfg->recon_buf2.phys = plist->buf.paddr; + pcfg->recon_buf2.size = plist->buf.size; + pcfg->recon_buf2.offset = 0; + + plist = venc_add_pmem_to_list(dvenc, &(pbufs->wb_buf), + VENC_BUFFER_TYPE_QDSP6); + if (plist == NULL) { + pr_err("%s: wb_buf failed to add_to_pmem_list\n", + __func__); + return -EPERM; + } + pcfg->wb_buf.region = 0; + pcfg->wb_buf.phys = plist->buf.paddr; + pcfg->wb_buf.size = plist->buf.size; + pcfg->wb_buf.offset = 0; + + plist = venc_add_pmem_to_list(dvenc, &(pbufs->cmd_buf), + VENC_BUFFER_TYPE_QDSP6); + if (plist == NULL) { + pr_err("%s: cmd_buf failed to add_to_pmem_list\n", + __func__); + return -EPERM; + } + pcfg->cmd_buf.region = 0; + pcfg->cmd_buf.phys = plist->buf.paddr; + pcfg->cmd_buf.size = plist->buf.size; + pcfg->cmd_buf.offset = 0; + + plist = venc_add_pmem_to_list(dvenc, &(pbufs->vlc_buf), + VENC_BUFFER_TYPE_QDSP6); + if (plist == NULL) { + pr_err("%s: vlc_buf failed to add_to_pmem_list" + " failed\n", __func__); + return -EPERM; + } + pcfg->vlc_buf.region = 0; + pcfg->vlc_buf.phys = plist->buf.paddr; + pcfg->vlc_buf.size = plist->buf.size; + pcfg->vlc_buf.offset = 0; - pr_info("%s() handle=%p enc_cb=%08x\n", __func__, q6venc->venc, - q6venc->cb_ev_data.enc_cb_handle); - - return 0; - -err_dal_call_set_cb: - dal_detach(q6venc->venc); -err_dal_attach: - file->private_data = NULL; - mutex_destroy(&q6venc->lock); - kfree(q6venc); - return err; + return ret; } -static int q6venc_release(struct inode *inode, struct file *file) +static int venc_start(struct venc_dev *dvenc, void *argp) { - struct q6venc_dev *q6venc; - int id; + int ret = 0; + struct venc_q6_config q6_config; + struct venc_init_config vconfig; - q6venc = file->private_data; - file->private_data = NULL; + dvenc->state = VENC_STATE_START; + ret = copy_from_user(&vconfig, argp, sizeof(struct venc_init_config)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + memcpy(&q6_config, &(vconfig.q6_config), sizeof(q6_config)); + ret = venc_assign_q6_buffers(dvenc, &(vconfig.q6_bufs), + &(q6_config.buf_params)); + if (ret != 0) { + pr_err("%s: assign_q6_buffers failed\n", __func__); + return -EPERM; + } + + q6_config.callback_event = dvenc->q6_handle; + TRACE("%s: parameters: handle:%p, config:%p, callback:%p \n", __func__, + dvenc->q6_handle, &q6_config, q6_config.callback_event); + TRACE("%s: parameters:recon1:0x%x, recon2:0x%x," + " wb_buf:0x%x, cmd:0x%x, vlc:0x%x\n", __func__, + q6_config.buf_params.recon_buf1.phys, + q6_config.buf_params.recon_buf2.phys, + q6_config.buf_params.wb_buf.phys, + q6_config.buf_params.cmd_buf.phys, + q6_config.buf_params.vlc_buf.phys); + TRACE("%s: size of param:%d \n", __func__, sizeof(q6_config)); + ret = dal_call_f5(dvenc->q6_handle, VENC_DALRPC_START, &q6_config, + sizeof(q6_config)); + if (ret != 0) { + pr_err("%s: remote function failed (%d)\n", __func__, ret); + return ret; + } + return ret; +} - pr_info("q6venc_close() handle=%p\n", q6venc->venc); +static int venc_encode_frame(struct venc_dev *dvenc, void *argp) +{ + int ret = 0; + struct venc_pmem buf; + struct venc_input_buf q6_input; + struct venc_pmem_list *plist; + struct venc_buffer input; - dal_detach(q6venc->venc); + ret = copy_from_user(&input, argp, sizeof(struct venc_buffer)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + ret = copy_from_user(&buf, + ((struct venc_buffer *)argp)->ptr_buffer, + sizeof(struct venc_pmem)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } - for (id = 0; id < q6venc->num_enc_bufs; id++) - put_buf_info(&q6venc->enc_bufs[id]); - put_buf_info(&q6venc->rlc_bufs[0]); - put_buf_info(&q6venc->rlc_bufs[1]); + plist = venc_get_pmem_from_list(dvenc, buf.fd, buf.offset, + VENC_BUFFER_TYPE_INPUT); + if (NULL == plist) { + plist = venc_add_pmem_to_list(dvenc, &buf, + VENC_BUFFER_TYPE_INPUT); + if (plist == NULL) { + pr_err("%s: buffer add_to_pmem_list failed\n", + __func__); + return -EPERM; + } + } - mutex_destroy(&q6venc->lock); - kfree(q6venc); - return 0; + q6_input.flags = 0; + if (input.flags & VENC_FLAG_EOS) + q6_input.flags |= 0x00000001; + q6_input.yuv_buf.region = 0; + q6_input.yuv_buf.phys = plist->buf.paddr; + q6_input.yuv_buf.size = plist->buf.size; + q6_input.yuv_buf.offset = 0; + q6_input.data_size = plist->buf.size; + q6_input.client_data = (u32)input.client_data; + q6_input.time_stamp = input.time_stamp; + q6_input.dvs_offsetx = 0; + q6_input.dvs_offsety = 0; + + TRACE("Pushing down input phys=0x%x fd= %d, client_data: 0x%x," + " time_stamp:%lld \n", q6_input.yuv_buf.phys, plist->buf.fd, + input.client_data, input.time_stamp); + ret = dal_call_f5(dvenc->q6_handle, VENC_DALRPC_QUEUE_INPUT, + &q6_input, sizeof(q6_input)); + + if (ret != 0) + pr_err("%s: Q6 queue_input failed (%d)\n", __func__, + (int)ret); + return ret; } -static int q6_config_encode(struct q6venc_dev *q6venc, uint32_t type, - struct init_config *init_config) +static int venc_fill_output(struct venc_dev *dvenc, void *argp) { - struct q6_init_config *q6_init_config = &init_config->q6_init_config; - int ret; - int i; - - mutex_lock(&q6venc->lock); + int ret = 0; + struct venc_pmem buf; + struct venc_output_buf q6_output; + struct venc_pmem_list *plist; + struct venc_buffer output; - if (q6venc->num_enc_bufs != 0) { - pr_err("%s: multiple sessions not supported\n", __func__); - ret = -EBUSY; - goto err_busy; + ret = copy_from_user(&output, argp, sizeof(struct venc_buffer)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; } - - ret = get_buf_info(&q6venc->enc_bufs[0], &init_config->ref_frame_buf1); + ret = copy_from_user(&buf, + ((struct venc_buffer *)argp)->ptr_buffer, + sizeof(struct venc_pmem)); if (ret) { - pr_err("%s: can't get ref_frame_buf1\n", __func__); - goto err_get_ref_frame_buf1; + pr_err("%s: copy_from_user failed\n", __func__); + return ret; } + plist = venc_get_pmem_from_list(dvenc, buf.fd, buf.offset, + VENC_BUFFER_TYPE_OUTPUT); + if (NULL == plist) { + plist = venc_add_pmem_to_list(dvenc, &buf, + VENC_BUFFER_TYPE_OUTPUT); + if (NULL == plist) { + pr_err("%s: output buffer failed to add_to_pmem_list" + "\n", __func__); + return -EPERM; + } + } + q6_output.bit_stream_buf.region = 0; + q6_output.bit_stream_buf.phys = (u32)plist->buf.paddr; + q6_output.bit_stream_buf.size = plist->buf.size; + q6_output.bit_stream_buf.offset = 0; + q6_output.client_data = (u32)output.client_data; + ret = + dal_call_f5(dvenc->q6_handle, VENC_DALRPC_QUEUE_OUTPUT, &q6_output, + sizeof(q6_output)); + if (ret != 0) + pr_err("%s: remote function failed (%d)\n", __func__, ret); + return ret; +} - ret = get_buf_info(&q6venc->enc_bufs[1], &init_config->ref_frame_buf2); +static int venc_stop(struct venc_dev *dvenc) +{ + int ret = 0; + + dvenc->stop_called = 1; + ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_STOP, 1); if (ret) { - pr_err("%s: can't get ref_frame_buf2\n", __func__); - goto err_get_ref_frame_buf2; + pr_err("%s: remote runction failed (%d)\n", __func__, ret); + venc_post(dvenc, VENC_MSG_STOP, VENC_S_EFAIL, NULL); } + return ret; +} - ret = get_buf_info(&q6venc->rlc_bufs[0], &init_config->rlc_buf1); +static int venc_pause(struct venc_dev *dvenc) +{ + int ret = 0; + + ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_SUSPEND, 1); if (ret) { - pr_err("%s: can't get rlc_buf1\n", __func__); - goto err_get_rlc_buf1; + pr_err("%s: remote function failed (%d)\n", __func__, ret); + venc_post(dvenc, VENC_MSG_PAUSE, VENC_S_EFAIL, NULL); } + return ret; +} + +static int venc_resume(struct venc_dev *dvenc) +{ + int ret = 0; - ret = get_buf_info(&q6venc->rlc_bufs[1], &init_config->rlc_buf2); + ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_RESUME, 1); if (ret) { - pr_err("%s: can't get rlc_buf2\n", __func__); - goto err_get_rlc_buf2; + pr_err("%s: remote function failed (%d)\n", __func__, ret); + venc_post(dvenc, VENC_MSG_RESUME, VENC_S_EFAIL, NULL); } - q6venc->rlc_buf_len = 2 * q6_init_config->rlc_buf_length; - q6venc->num_enc_bufs = 2; + return ret; +} + +static int venc_flush(struct venc_dev *dvenc, void *argp) +{ + int ret = 0; + int status = VENC_S_SUCCESS; + union venc_msg_data data; - q6venc->enc_buf_size = - (q6_init_config->enc_frame_width_inmb * PIXELS_PER_MACROBLOCK) * - (q6_init_config->enc_frame_height_inmb * PIXELS_PER_MACROBLOCK) * - BITS_PER_PIXEL / 8; + if (copy_from_user(&data.flush_ret, argp, sizeof(struct venc_buffer_flush))) + return -EFAULT; - q6_init_config->ref_frame_buf1_phy = q6venc->enc_bufs[0].paddr; - q6_init_config->ref_frame_buf2_phy = q6venc->enc_bufs[1].paddr; - q6_init_config->rlc_buf1_phy = q6venc->rlc_bufs[0].paddr; - q6_init_config->rlc_buf2_phy = q6venc->rlc_bufs[1].paddr; + if (data.flush_ret.flush_mode == VENC_FLUSH_ALL) { + ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_FLUSH, 1); + if (ret) + status = VENC_S_EFAIL; + } else + status = VENC_S_ENOTSUPP; - // The DSP may use the rlc_bufs during initialization, - for (i=0; irlc_bufs[i].vaddr, - (const void *)(q6venc->rlc_bufs[i].vaddr + - q6venc->rlc_buf_len)); - } + if (status == VENC_S_SUCCESS) + return ret; + + venc_post(dvenc, VENC_MSG_FLUSH, status, &data); + return -EIO; +} - ret = dal_call_f5(q6venc->venc, type, q6_init_config, - sizeof(struct q6_init_config)); +static int venc_get_sequence_hdr(struct venc_dev *dvenc, void *argp) +{ + pr_err("%s not supported\n", __func__); + return -EIO; +} + +static int venc_set_qp_range(struct venc_dev *dvenc, void *argp) +{ + int ret = 0; + struct venc_qp_range qp; + + ret = copy_from_user(&qp, argp, sizeof(struct venc_qp_range)); if (ret) { - pr_err("%s: rpc failed \n", __func__); - goto err_dal_rpc_init; + pr_err("%s: copy_from_user failed\n", __func__); + return ret; } - mutex_unlock(&q6venc->lock); - return 0; -err_dal_rpc_init: - q6venc->num_enc_bufs = 0; - put_pmem_file(q6venc->rlc_bufs[1].file); -err_get_rlc_buf2: - put_pmem_file(q6venc->rlc_bufs[0].file); -err_get_rlc_buf1: - put_pmem_file(q6venc->enc_bufs[1].file); -err_get_ref_frame_buf2: - put_pmem_file(q6venc->enc_bufs[0].file); -err_get_ref_frame_buf1: -err_busy: - mutex_unlock(&q6venc->lock); + if (dvenc->state == VENC_STATE_START || + dvenc->state == VENC_STATE_PAUSE) { + ret = + dal_call_f5(dvenc->q6_handle, VENC_DALRPC_UPDATE_QP_RANGE, + &qp, sizeof(struct venc_qp_range)); + if (ret) { + pr_err("%s: remote function failed (%d) \n", __func__, + ret); + return ret; + } + } return ret; } -static int q6_encode(struct q6venc_dev *q6venc, struct encode_param *enc_param) +static int venc_set_intra_period(struct venc_dev *dvenc, void *argp) { - struct q6_encode_param *q6_param = &enc_param->q6_encode_param; - struct file *file; - struct buf_info *buf; - int i; - int ret; - int rlc_buf_index; + int ret = 0; + u32 pnum = 0; - pr_debug("y_addr fd=%d offset=0x%08lx uv_offset=0x%08lx\n", - enc_param->y_addr.fd, enc_param->y_addr.offset, - enc_param->uv_offset); - - file = fget(enc_param->y_addr.fd); - if (!file) { - pr_err("%s: invalid encode buffer fd %d\n", __func__, - enc_param->y_addr.fd); - return -EBADF; + ret = copy_from_user(&pnum, argp, sizeof(int)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + if (dvenc->state == VENC_STATE_START || + dvenc->state == VENC_STATE_PAUSE) { + ret = dal_call_f0(dvenc->q6_handle, + VENC_DALRPC_UPDATE_INTRA_PERIOD, pnum); + if (ret) + pr_err("%s: remote function failed (%d)\n", __func__, + ret); } + return ret; +} - mutex_lock(&q6venc->lock); +static int venc_set_intra_refresh(struct venc_dev *dvenc, void *argp) +{ + int ret = 0; + u32 mb_num = 0; - for (i = 0; i < q6venc->num_enc_bufs; i++) { - buf = &q6venc->enc_bufs[i]; - if (buf->file == file - && buf->venc_buf.offset == enc_param->y_addr.offset) - break; + ret = copy_from_user(&mb_num, argp, sizeof(int)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; } + if (dvenc->state == VENC_STATE_START || + dvenc->state == VENC_STATE_PAUSE) { + ret = dal_call_f0(dvenc->q6_handle, + VENC_DALRPC_UPDATE_INTRA_REFRESH, mb_num); + if (ret) + pr_err("%s: remote function failed (%d)\n", __func__, + ret); + } + return ret; +} - if (i == q6venc->num_enc_bufs) { - if (q6venc->num_enc_bufs == VENC_MAX_BUF_NUM) { - pr_err("%s: too many input buffers\n", __func__); - ret = -ENOMEM; - goto done; - } +static int venc_set_frame_rate(struct venc_dev *dvenc, void *argp) +{ + int ret = 0; + struct venc_frame_rate pdata; + ret = copy_from_user(&pdata, argp, sizeof(struct venc_frame_rate)); + if (ret) { + pr_err("%s: copy_from_user failed\n", __func__); + return ret; + } + if (dvenc->state == VENC_STATE_START || + dvenc->state == VENC_STATE_PAUSE) { + ret = dal_call_f5(dvenc->q6_handle, + VENC_DALRPC_UPDATE_FRAME_RATE, + (void *)&(pdata), + sizeof(struct venc_frame_rate)); + if (ret) + pr_err("%s: remote function failed (%d)\n", __func__, + ret); + } + return ret; +} - buf = &q6venc->enc_bufs[q6venc->num_enc_bufs]; - ret = get_buf_info(buf, &enc_param->y_addr); - if (ret) { - pr_err("%s: can't get encode buffer\n", __func__); - ret = -EINVAL; - goto done; - } +static int venc_set_target_bitrate(struct venc_dev *dvenc, void *argp) +{ + int ret = 0; + u32 pdata = 0; - if (!IS_ALIGNED(buf->paddr, PAGE_SIZE)) { - pr_err("%s: input buffer not 4k aligned\n", __func__); - put_buf_info(buf); - ret = -EINVAL; - goto done; - } - q6venc->num_enc_bufs++; - } - - // We must invalidate the buffer that the DSP will write to - // to ensure that a dirty cache line doesn't get flushed on - // top of the data that the DSP is writing. - // Unfortunately, we have to predict which rlc_buf index the - // DSP is going to write to. We assume it will write to buf - // 0 the first time we call q6_encode, and alternate afterwards - rlc_buf_index = q6venc->rlc_buf_index; - dmac_inv_range((const void *)q6venc->rlc_bufs[rlc_buf_index].vaddr, - (const void *)(q6venc->rlc_bufs[rlc_buf_index].vaddr + - q6venc->rlc_buf_len)); - q6venc->rlc_buf_index = (q6venc->rlc_buf_index + 1) % RLC_MAX_BUF_NUM; - - q6_param->luma_addr = buf->paddr; - q6_param->chroma_addr = q6_param->luma_addr + enc_param->uv_offset; - pr_debug("luma_addr=0x%08x chroma_addr=0x%08x\n", q6_param->luma_addr, - q6_param->chroma_addr); - - // Ideally, each ioctl that passed in a data buffer would include the size - // of the input buffer, so we can properly flush the cache on it. Since - // userspace does not fill in the size fields, we have to assume the size - // based on the encoder configuration for now. - flush_pmem_file(buf->file, enc_param->y_addr.offset, - q6venc->enc_buf_size); - - ret = dal_call_f5(q6venc->venc, VENC_DALRPC_ENCODE, q6_param, - sizeof(struct q6_encode_param)); + ret = copy_from_user(&pdata, argp, sizeof(int)); if (ret) { - pr_err("%s: encode rpc failed\n", __func__); - goto done; + pr_err("%s: copy_from_user failed\n", __func__); + return ret; } + if (dvenc->state == VENC_STATE_START || + dvenc->state == VENC_STATE_PAUSE) { + ret = dal_call_f0(dvenc->q6_handle, + VENC_DALRPC_UPDATE_BITRATE, pdata); + if (ret) + pr_err("%s: remote function failed (%d)\n", __func__, + ret); + } + return ret; +} - ret = 0; +static int venc_request_iframe(struct venc_dev *dvenc) +{ + int ret = 0; -done: - mutex_unlock(&q6venc->lock); - fput(file); + if (dvenc->state != VENC_STATE_START) + return -EINVAL; + + ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_REQUEST_IFRAME, 1); + if (ret) + pr_err("%s: remote function failed (%d)\n", __func__, ret); return ret; } -static int q6venc_ioctl(struct inode *inode, struct file *file, - unsigned cmd, unsigned long arg) +static int venc_stop_read_msg(struct venc_dev *dvenc) { - struct q6venc_dev *q6venc = file->private_data; - struct init_config config; - struct encode_param encode_param; - struct intra_refresh intra_refresh; - struct rc_config rc_config; - struct frame_type frame_done; - unsigned int id; - unsigned long flags; - int err = 0; + venc_post(dvenc, VENC_MSG_STOP_READING_MSG, 0, NULL); + return 0; +} - if (!q6venc) { - pr_err("%s: file has no private data\n", __func__); - return -ENODEV; +static int venc_translate_error(enum venc_status_code q6_status) +{ + switch (q6_status) { + case VENC_STATUS_SUCCESS: + return VENC_S_SUCCESS; + case VENC_STATUS_ERROR: + return VENC_S_EFAIL; + case VENC_STATUS_INVALID_STATE: + return VENC_S_EINVALSTATE; + case VENC_STATUS_FLUSHING: + return VENC_S_EFLUSHED; + case VENC_STATUS_INVALID_PARAM: + return VENC_S_EBADPARAM; + case VENC_STATUS_CMD_QUEUE_FULL: + return VENC_S_ECMDQFULL; + case VENC_STATUS_CRITICAL: + return VENC_S_EFATAL; + case VENC_STATUS_INSUFFICIENT_RESOURCES: + return VENC_S_ENOHWRES; + case VENC_STATUS_TIMEOUT: + return VENC_S_ETIMEOUT; + default: + /* xxx probably shouldn't assume success */ + return 0; } +} - pr_debug("%s\n", __func__); - - switch (cmd) { - case VENC_IOCTL_INITIALIZE: - pr_debug("%s: VENC_IOCTL_INITIALIZE\n", __func__); - if (copy_from_user(&config, (void __user *)arg, sizeof(config))) - return -EFAULT; - err = q6_config_encode(q6venc, VENC_DALRPC_INITIALIZE, &config); - break; +static void venc_q6_callback(void *_data, int len, void *cookie) +{ + int status = 0; + struct venc_dev *dvenc = (struct venc_dev *)cookie; + struct venc_msg_type *q6_msg = NULL; + struct venc_input_payload *pload1; + struct venc_output_payload *pload2; + union venc_msg_data data; + uint32_t *tmp = (uint32_t *) _data; + + if (dvenc == NULL) { + pr_err("%s: empty driver parameter\n", __func__); + return; + } + if (tmp[2] == sizeof(struct venc_msg_type)) { + q6_msg = (struct venc_msg_type *)&tmp[3]; + } else { + pr_err("%s: callback with empty message (%d, %d)\n", + __func__, tmp[2], sizeof(struct venc_msg_type)); + return; + } - case VENC_IOCTL_ENCODE_CONFIG: - pr_debug("%s: VENC_IOCTL_ENCODE_CONFIG\n", __func__); - if (copy_from_user(&config, (void __user *)arg, sizeof(config))) - return -EFAULT; + status = venc_translate_error(q6_msg->status); + if (status != VENC_STATUS_SUCCESS) + pr_err("%s: Q6 failed (%d)", __func__, (int)status); - err = q6_config_encode(q6venc, VENC_DALRPC_ENCODE_CONFIG, - &config); + switch ((enum venc_event_type_enum)q6_msg->event) { + case VENC_EVENT_START_STATUS: + dvenc->state = VENC_STATE_START; + venc_post(dvenc, VENC_MSG_START, status, NULL); break; - - case VENC_IOCTL_ENCODE: - pr_debug("%s: VENC_IOCTL_ENCODE\n", __func__); - if (copy_from_user(&encode_param, (void __user *)arg, - sizeof(encode_param))) - return -EFAULT; - err = q6_encode(q6venc, &encode_param); + case VENC_EVENT_STOP_STATUS: + dvenc->state = VENC_STATE_STOP; + venc_post(dvenc, VENC_MSG_STOP, status, NULL); break; - - case VENC_IOCTL_INTRA_REFRESH: - pr_debug("%s: VENC_IOCTL_INTRA_REFRESH\n", __func__); - if (copy_from_user(&intra_refresh, (void __user *)arg, - sizeof(intra_refresh))) - return -EFAULT; - - mutex_lock(&q6venc->lock); - err = dal_call_f5(q6venc->venc, VENC_DALRPC_INTRA_REFRESH, - &intra_refresh, sizeof(struct intra_refresh)); - mutex_unlock(&q6venc->lock); - if (err) - pr_err("%s: intra_refresh rpc failed\n", __func__); + case VENC_EVENT_SUSPEND_STATUS: + dvenc->state = VENC_STATE_PAUSE; + venc_post(dvenc, VENC_MSG_PAUSE, status, NULL); break; - - case VENC_IOCTL_RC_CONFIG: - pr_debug("%s: VENC_IOCTL_RC_CONFIG\n", __func__); - if (copy_from_user(&rc_config, (void __user *)arg, - sizeof(rc_config))) - return -EFAULT; - - mutex_lock(&q6venc->lock); - err = dal_call_f5(q6venc->venc, VENC_DALRPC_RC_CONFIG, - &rc_config, sizeof(rc_config)); - mutex_unlock(&q6venc->lock); - if (err) - pr_err("%s: dal_call_f5 failed\n", __func__); + case VENC_EVENT_RESUME_STATUS: + dvenc->state = VENC_STATE_START; + venc_post(dvenc, VENC_MSG_RESUME, status, NULL); break; - - case VENC_IOCTL_STOP: - pr_debug("%s: VENC_IOCTL_STOP\n", __func__); - - mutex_lock(&q6venc->lock); - err = dal_call_f0(q6venc->venc, VENC_DALRPC_STOP, 1); - if (err) - pr_err("%s: dal_rpc STOP call failed\n", __func__); - - /* XXX: if the dal call fails we still want to continue to free - * the buffers. Is this correct? */ - for (id = 0; id < q6venc->num_enc_bufs; id++) - put_buf_info(&q6venc->enc_bufs[id]); - put_buf_info(&q6venc->rlc_bufs[0]); - put_buf_info(&q6venc->rlc_bufs[1]); - q6venc->num_enc_bufs = 0; - q6venc->stop_encode = true; - mutex_unlock(&q6venc->lock); + case VENC_EVENT_FLUSH_STATUS: + data.flush_ret.flush_mode = VENC_FLUSH_INPUT; + venc_post(dvenc, VENC_MSG_FLUSH, status, &data); + data.flush_ret.flush_mode = VENC_FLUSH_OUTPUT; + venc_post(dvenc, VENC_MSG_FLUSH, status, &data); break; - - case VENC_IOCTL_WAIT_FOR_ENCODE: - pr_debug("%s: waiting for encode done event \n", __func__); - err = wait_event_interruptible(q6venc->encode_wq, - (q6venc->encode_done || q6venc->stop_encode)); - if (err < 0) { - err = -ERESTARTSYS; - break; + case VENC_EVENT_RELEASE_INPUT: + pload1 = &((q6_msg->payload).input_payload); + TRACE("Release_input: data: 0x%x \n", pload1->data); + if (pload1 != NULL) { + /* xxx should we zero? */ + data.buf.client_data = pload1->data; + venc_post(dvenc, VENC_MSG_INPUT_BUFFER_DONE, status, &data); + } else { + pr_err("%s no payload on buffer done?\n", __func__); } + break; + case VENC_EVENT_DELIVER_OUTPUT: + pload2 = &((q6_msg->payload).output_payload); + data.buf.flags = 0; + if (pload2->flags & VENC_FLAG_SYNC_FRAME) + data.buf.flags |= VENC_FLAG_SYNC_FRAME; + if (pload2->flags & VENC_FLAG_CODEC_CONFIG) + data.buf.flags |= VENC_FLAG_CODEC_CONFIG; + if (pload2->flags & VENC_FLAG_END_OF_FRAME) + data.buf.flags |= VENC_FLAG_END_OF_FRAME; + if (pload2->flags & VENC_FLAG_EOS) + data.buf.flags |= VENC_FLAG_EOS; + data.buf.len = pload2->size; + data.buf.offset = 0; + data.buf.time_stamp = pload2->time_stamp; + data.buf.client_data = pload2->data; + venc_post(dvenc, VENC_MSG_OUTPUT_BUFFER_DONE, status, &data); + break; + default: + pr_err("%s: invalid response from Q6 (%d)\n", __func__, + (int)q6_msg->event); + break; + } +} - mutex_lock(&q6venc->lock); - if (q6venc->stop_encode) { - q6venc->stop_encode = false; - mutex_unlock(&q6venc->lock); - pr_debug("%s: Received Stop encode event \n", __func__); - err = -EINTR; - break; - } +static int venc_read_next_msg(struct venc_dev *dvenc, void __user *argp) +{ + int res; + struct venc_qmsg *msg; + + res = wait_event_interruptible(dvenc->venc_msg_evt, + (msg = venc_recv_msg(dvenc)) != NULL); + if (res < 0) + return res; + res = copy_to_user(argp, &msg->msg, sizeof(struct venc_msg)); + venc_free_msg(dvenc, msg); + if (res) + return -EFAULT; + return 0; +} - spin_lock_irqsave(&q6venc->done_lock, flags); - if (!q6venc->encode_done) { - spin_unlock_irqrestore(&q6venc->done_lock, flags); - pr_err("%s: encoding not stopped, and is not done.\n", - __func__); - err = -EIO; - break; - } +static long q6venc_ioctl(struct file *file, u32 cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct venc_dev *dvenc = file->private_data; - memcpy(&frame_done, &q6venc->done_frame, - sizeof(struct frame_type)); - q6venc->encode_done = false; - spin_unlock_irqrestore(&q6venc->done_lock, flags); - mutex_unlock(&q6venc->lock); + switch (cmd) { + case VENC_IOCTL_SET_INPUT_BUFFER: + return venc_set_buffer(dvenc, argp, VENC_BUFFER_TYPE_INPUT); + case VENC_IOCTL_SET_OUTPUT_BUFFER: + return venc_set_buffer(dvenc, argp, VENC_BUFFER_TYPE_OUTPUT); + case VENC_IOCTL_GET_SEQUENCE_HDR: + return venc_get_sequence_hdr(dvenc, argp); + case VENC_IOCTL_SET_QP_RANGE: + return venc_set_qp_range(dvenc, argp); + case VENC_IOCTL_SET_INTRA_PERIOD: + return venc_set_intra_period(dvenc, argp); + case VENC_IOCTL_SET_INTRA_REFRESH: + return venc_set_intra_refresh(dvenc, argp); + case VENC_IOCTL_SET_FRAME_RATE: + return venc_set_frame_rate(dvenc, argp); + case VENC_IOCTL_SET_TARGET_BITRATE: + return venc_set_target_bitrate(dvenc, argp); + case VENC_IOCTL_CMD_REQUEST_IFRAME: + if (dvenc->state == VENC_STATE_START) + return venc_request_iframe(dvenc); + else + return 0; + case VENC_IOCTL_CMD_START: + return venc_start(dvenc, argp); + case VENC_IOCTL_CMD_STOP: + return venc_stop(dvenc); + case VENC_IOCTL_CMD_PAUSE: + return venc_pause(dvenc); + case VENC_IOCTL_CMD_RESUME: + return venc_resume(dvenc); + case VENC_IOCTL_CMD_ENCODE_FRAME: + return venc_encode_frame(dvenc, argp); + case VENC_IOCTL_CMD_FILL_OUTPUT_BUFFER: + return venc_fill_output(dvenc, argp); + case VENC_IOCTL_CMD_FLUSH: + return venc_flush(dvenc, argp); + case VENC_IOCTL_CMD_READ_NEXT_MSG: + return venc_read_next_msg(dvenc, argp); + case VENC_IOCTL_CMD_STOP_READ_MSG: + return venc_stop_read_msg(dvenc); + default: + pr_err("%s: invalid ioctl code (%d)\n", __func__, cmd); + return -EINVAL; + } +} - if (frame_done.q6_frame_type.frame_len == 0) { - pr_debug("%s: got incorrect address from q6\n", - __func__); - err = -EIO; - break; +static int q6venc_open(struct inode *inode, struct file *file) +{ + int i; + int ret = 0; + struct venc_dev *dvenc; + struct venc_qmsg *msg; +#if VERSION_CHECK + struct dal_info version_info; +#endif + + dvenc = kzalloc(sizeof(struct venc_dev), GFP_KERNEL); + if (!dvenc) + return -ENOMEM; + + file->private_data = dvenc; + INIT_LIST_HEAD(&dvenc->msg_pool); + INIT_LIST_HEAD(&dvenc->msg_queue); + INIT_LIST_HEAD(&dvenc->venc_pmem_list_head); + init_waitqueue_head(&dvenc->venc_msg_evt); + spin_lock_init(&dvenc->msg_lock); + spin_lock_init(&dvenc->venc_pmem_list_lock); + venc_ref++; + + for (i = 0; i < VENC_MSG_MAX; i++) { + msg = kzalloc(sizeof(struct venc_qmsg), GFP_KERNEL); + if (msg == NULL) { + ret = -ENOMEM; + goto fail_list_alloc; } + venc_free_msg(dvenc, msg); + } - pr_debug("%s: done encoding \n", __func__); - if (copy_to_user((void __user *)arg, &frame_done, - sizeof(struct frame_type))) - err = -EFAULT; - break; + dvenc->q6_handle = dal_attach(DALDEVICEID_VENC_DEVICE, + DALDEVICEID_VENC_PORTNAME, + venc_q6_callback, (void *)dvenc); - case VENC_IOCTL_STOP_ENCODE: - pr_debug("%s: Stop encode event \n", __func__); - mutex_lock(&q6venc->lock); - q6venc->stop_encode = true; - wake_up_interruptible(&q6venc->encode_wq); - mutex_unlock(&q6venc->lock); - break; + if (!(dvenc->q6_handle)) { + pr_err("%s: daldevice_attach failed (%d)\n", __func__, ret); + goto fail_list_alloc; + } - default: - err = -ENOTTY; - break; +#if VERSION_CHECK + ret = dal_call_f9(dvenc->q6_handle, DAL_OP_INFO, &version_info, + sizeof(struct dal_info)); + if (ret) { + pr_err("%s: failed to get version\n", __func__); + ret = -EINVAL; + goto fail_open; } + if (venc_check_version(VENC_INTERFACE_VERSION, version_info.version)) { + pr_err("%s: driver version mismatch\n", __func__); + ret = -EINVAL; + goto fail_open; + } +#endif + ret = dal_call_f0(dvenc->q6_handle, DAL_OP_OPEN, 1); + if (ret) { + pr_err("%s: dal_call_open failed (%d)\n", __func__, ret); + goto fail_open; + } + dvenc->state = VENC_STATE_STOP; + prevent_sleep(); + return ret; + +fail_open: + dal_detach(dvenc->q6_handle); + +fail_list_alloc: + while ((msg = venc_alloc_msg(dvenc))) + kfree(msg); - return err; + kfree(dvenc); + venc_ref--; + return ret; } -static const struct file_operations q6venc_dev_fops = { - .owner = THIS_MODULE, - .open = q6venc_open, - .release = q6venc_release, - .ioctl = q6venc_ioctl, -}; +static int q6venc_release(struct inode *inode, struct file *file) +{ + int ret = 0; + struct venc_pmem_list *plist, *m; + struct venc_dev *dvenc; + struct venc_qmsg *msg; + + venc_ref--; + + dvenc = file->private_data; + wake_up_all(&dvenc->venc_msg_evt); + if (!dvenc->stop_called) + dal_call_f0(dvenc->q6_handle, VENC_DALRPC_STOP, 1); + dal_call_f0(dvenc->q6_handle, DAL_OP_CLOSE, 1); + dal_detach(dvenc->q6_handle); + + + /* free all messages in the pool */ + while ((msg = venc_alloc_msg(dvenc))) + kfree(msg); -static struct miscdevice q6venc_misc = { - .minor = MISC_DYNAMIC_MINOR, - .name = "q6venc", - .fops = &q6venc_dev_fops, + /* free all messages sitting in the queue */ + while ((msg = venc_recv_msg(dvenc))) + kfree(msg); + + list_for_each_entry_safe(plist, m, &dvenc->venc_pmem_list_head, list) { + put_pmem_file(plist->buf.file); + list_del(&plist->list); + kfree(plist); + } + kfree(dvenc); + allow_sleep(); + return ret; +} + +const struct file_operations q6venc_fops = { + .owner = THIS_MODULE, + .open = q6venc_open, + .release = q6venc_release, + .unlocked_ioctl = q6venc_ioctl, }; static int __init q6venc_init(void) { - int rc = 0; + int ret = 0; - rc = misc_register(&q6venc_misc); - if (rc) - pr_err("%s: Unable to register q6venc misc device\n", __func__); - return rc; + venc_device_p = kzalloc(sizeof(struct venc_dev), GFP_KERNEL); + if (!venc_device_p) { + pr_err("%s: unable to allocate memory for venc_device_p\n", + __func__); + return -ENOMEM; + } + wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "venc_idle"); + wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "venc_suspend"); + + ret = alloc_chrdev_region(&venc_dev_num, 0, 1, VENC_NAME); + if (ret < 0) { + pr_err("%s: alloc_chrdev_region failed (%d)\n", __func__, + ret); + return ret; + } + venc_class = class_create(THIS_MODULE, VENC_NAME); + if (IS_ERR(venc_class)) { + ret = PTR_ERR(venc_class); + pr_err("%s: failed to create venc_class (%d)\n", + __func__, ret); + goto err_venc_class_create; + } + venc_device_p->class_devp = + device_create(venc_class, NULL, venc_dev_num, NULL, + VENC_NAME); + if (IS_ERR(venc_device_p->class_devp)) { + ret = PTR_ERR(venc_device_p->class_devp); + pr_err("%s: failed to create class_device (%d)\n", __func__, + ret); + goto err_venc_class_device_create; + } + cdev_init(&cdev, &q6venc_fops); + cdev.owner = THIS_MODULE; + ret = cdev_add(&cdev, venc_dev_num, 1); + if (ret < 0) { + pr_err("%s: cdev_add failed (%d)\n", __func__, ret); + goto err_venc_cdev_add; + } + init_waitqueue_head(&venc_device_p->venc_msg_evt); + return ret; + +err_venc_cdev_add: + device_destroy(venc_class, venc_dev_num); +err_venc_class_device_create: + class_destroy(venc_class); +err_venc_class_create: + unregister_chrdev_region(venc_dev_num, 1); + return ret; } static void __exit q6venc_exit(void) { - misc_deregister(&q6venc_misc); + cdev_del(&(cdev)); + device_destroy(venc_class, venc_dev_num); + class_destroy(venc_class); + unregister_chrdev_region(venc_dev_num, 1); } -MODULE_DESCRIPTION("video encoder driver for QSD platform"); +MODULE_DESCRIPTION("Video encoder driver for QDSP6"); MODULE_VERSION("2.0"); - module_init(q6venc_init); module_exit(q6venc_exit); diff --git a/include/linux/msm_q6venc.h b/include/linux/msm_q6venc.h index 2ca4832d9a5e0..db31332fecbea 100755 --- a/include/linux/msm_q6venc.h +++ b/include/linux/msm_q6venc.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -7,119 +7,315 @@ * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * * Neither the name of Code Aurora Forum nor + * * Neither the name of Code Aurora nor * the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ + #ifndef _MSM_VENC_H_ #define _MSM_VENC_H_ #include -struct venc_buf { - unsigned int src_id; +#define VENC_MAX_RECON_BUFFERS 2 + +#define VENC_FLAG_EOS 0x00000001 +#define VENC_FLAG_END_OF_FRAME 0x00000010 +#define VENC_FLAG_SYNC_FRAME 0x00000020 +#define VENC_FLAG_EXTRA_DATA 0x00000040 +#define VENC_FLAG_CODEC_CONFIG 0x00000080 + +enum venc_flush_type { + VENC_FLUSH_INPUT, + VENC_FLUSH_OUTPUT, + VENC_FLUSH_ALL +}; + +enum venc_state_type { + VENC_STATE_PAUSE = 0x1, + VENC_STATE_START = 0x2, + VENC_STATE_STOP = 0x4 +}; + +enum venc_event_type_enum { + VENC_EVENT_START_STATUS, + VENC_EVENT_STOP_STATUS, + VENC_EVENT_SUSPEND_STATUS, + VENC_EVENT_RESUME_STATUS, + VENC_EVENT_FLUSH_STATUS, + VENC_EVENT_RELEASE_INPUT, + VENC_EVENT_DELIVER_OUTPUT, + VENC_EVENT_UNKNOWN_STATUS +}; + +enum venc_status_code { + VENC_STATUS_SUCCESS, + VENC_STATUS_ERROR, + VENC_STATUS_INVALID_STATE, + VENC_STATUS_FLUSHING, + VENC_STATUS_INVALID_PARAM, + VENC_STATUS_CMD_QUEUE_FULL, + VENC_STATUS_CRITICAL, + VENC_STATUS_INSUFFICIENT_RESOURCES, + VENC_STATUS_TIMEOUT +}; + +enum venc_msg_code { + VENC_MSG_INDICATION, + VENC_MSG_INPUT_BUFFER_DONE, + VENC_MSG_OUTPUT_BUFFER_DONE, + VENC_MSG_NEED_OUTPUT_BUFFER, + VENC_MSG_FLUSH, + VENC_MSG_START, + VENC_MSG_STOP, + VENC_MSG_PAUSE, + VENC_MSG_RESUME, + VENC_MSG_STOP_READING_MSG +}; + +enum venc_error_code { + VENC_S_SUCCESS, + VENC_S_EFAIL, + VENC_S_EFATAL, + VENC_S_EBADPARAM, + VENC_S_EINVALSTATE, + VENC_S_ENOSWRES, + VENC_S_ENOHWRES, + VENC_S_EBUFFREQ, + VENC_S_EINVALCMD, + VENC_S_ETIMEOUT, + VENC_S_ENOREATMPT, + VENC_S_ENOPREREQ, + VENC_S_ECMDQFULL, + VENC_S_ENOTSUPP, + VENC_S_ENOTIMPL, + VENC_S_ENOTPMEM, + VENC_S_EFLUSHED, + VENC_S_EINSUFBUF, + VENC_S_ESAMESTATE, + VENC_S_EINVALTRANS +}; + +enum venc_mem_region_enum { + VENC_PMEM_EBI1, + VENC_PMEM_SMI +}; + +struct venc_buf_type { + unsigned int region; + unsigned int phys; + unsigned int size; + int offset; +}; + +struct venc_qp_range { + unsigned int min_qp; + unsigned int max_qp; +}; + +struct venc_frame_rate { + unsigned int frame_rate_num; + unsigned int frame_rate_den; +}; + +struct venc_slice_info { + unsigned int slice_mode; + unsigned int units_per_slice; +}; + +struct venc_extra_data { + unsigned int slice_extra_data_flag; + unsigned int slice_client_data1; + unsigned int slice_client_data2; + unsigned int slice_client_data3; + unsigned int none_extra_data_flag; + unsigned int none_client_data1; + unsigned int none_client_data2; + unsigned int none_client_data3; +}; + +struct venc_common_config { + unsigned int standard; + unsigned int input_frame_height; + unsigned int input_frame_width; + unsigned int output_frame_height; + unsigned int output_frame_width; + unsigned int rotation_angle; + unsigned int intra_period; + unsigned int rate_control; + struct venc_frame_rate frame_rate; + unsigned int bitrate; + struct venc_qp_range qp_range; + unsigned int iframe_qp; + unsigned int pframe_qp; + struct venc_slice_info slice_config; + struct venc_extra_data extra_data; +}; + +struct venc_nonio_buf_config { + struct venc_buf_type recon_buf1; + struct venc_buf_type recon_buf2; + struct venc_buf_type wb_buf; + struct venc_buf_type cmd_buf; + struct venc_buf_type vlc_buf; +}; + +struct venc_mpeg4_config { + unsigned int profile; + unsigned int level; + unsigned int time_resolution; + unsigned int ac_prediction; + unsigned int hec_interval; + unsigned int data_partition; + unsigned int short_header; + unsigned int rvlc_enable; +}; + +struct venc_h263_config { + unsigned int profile; + unsigned int level; +}; + +struct venc_h264_config { + unsigned int profile; + unsigned int level; + unsigned int max_nal; + unsigned int idr_period; +}; + +struct venc_pmem { + int src; int fd; - unsigned long offset; - unsigned long size; + unsigned int offset; + void *virt; + void *phys; + unsigned int size; +}; + +struct venc_buffer { + unsigned char *ptr_buffer; + unsigned int size; + unsigned int len; + unsigned int offset; + long long time_stamp; + unsigned int flags; + unsigned int client_data; }; -struct q6_init_config { - unsigned short venc_standard; - unsigned short partial_run_length_flag; - unsigned short h263_annex_ispt; - unsigned short h263_annex_jspt; - unsigned short h263_annex_tspt; - unsigned short rc_flag; - unsigned short one_mv_flag; - unsigned short acdc_pred_enable; - unsigned short rounding_bit_ctrl; - unsigned short rotation_flag; - unsigned short max_mvx; - unsigned short max_mvy; - unsigned short enc_frame_height_inmb; - unsigned short enc_frame_width_inmb; - unsigned short dvs_frame_height; - unsigned short dvs_frame_width; +struct venc_buffers { + struct venc_pmem recon_buf[VENC_MAX_RECON_BUFFERS]; + struct venc_pmem wb_buf; + struct venc_pmem cmd_buf; + struct venc_pmem vlc_buf; +}; - /* unused by userspace, filled in by kernel */ - unsigned int ref_frame_buf1_phy; - unsigned int ref_frame_buf2_phy; - unsigned int rlc_buf1_phy; - unsigned int rlc_buf2_phy; - unsigned int rlc_buf_length; +struct venc_buffer_flush { + unsigned int flush_mode; }; -struct init_config { - struct venc_buf ref_frame_buf1; - struct venc_buf ref_frame_buf2; - struct venc_buf rlc_buf1; - struct venc_buf rlc_buf2; - struct q6_init_config q6_init_config; +union venc_msg_data { + struct venc_buffer buf; + struct venc_buffer_flush flush_ret; }; -struct q6_encode_param { - unsigned int luma_addr; - unsigned int chroma_addr; - unsigned int x_offset; - unsigned int y_offset; - unsigned int frame_rho_budget; - unsigned int frame_type; - unsigned int qp; +struct venc_msg { + unsigned int status_code; + unsigned int msg_code; + union venc_msg_data msg_data; + unsigned int msg_data_size; }; -struct encode_param { - struct venc_buf y_addr; - unsigned long uv_offset; - struct q6_encode_param q6_encode_param; +union venc_codec_config { + struct venc_mpeg4_config mpeg4_params; + struct venc_h263_config h263_params; + struct venc_h264_config h264_params; }; -struct intra_refresh { - unsigned int intra_refresh_enable; - unsigned int intra_mb_num; +struct venc_q6_config { + struct venc_common_config config_params; + union venc_codec_config codec_params; + struct venc_nonio_buf_config buf_params; + void *callback_event; }; -struct rc_config { - unsigned short max_frame_qp_up_delta; - unsigned short max_frame_qp_down_delta; - unsigned short min_frame_qp; - unsigned short max_frame_qp; +struct venc_hdr_config { + struct venc_common_config config_params; + union venc_codec_config codec_params; }; -struct q6_frame_type { - unsigned int frame_type; - unsigned int frame_len; - unsigned int frame_addr; - unsigned int map_table; +struct venc_init_config { + struct venc_q6_config q6_config; + struct venc_buffers q6_bufs; }; -struct frame_type { - struct venc_buf frame_addr; - struct q6_frame_type q6_frame_type; +struct venc_seq_config { + int size; + struct venc_pmem buf; + struct venc_q6_config q6_config; }; #define VENC_IOCTL_MAGIC 'V' -#define VENC_IOCTL_INITIALIZE _IOW(VENC_IOCTL_MAGIC, 1, struct init_config) -#define VENC_IOCTL_ENCODE _IOW(VENC_IOCTL_MAGIC, 2, struct encode_param) -#define VENC_IOCTL_INTRA_REFRESH _IOW(VENC_IOCTL_MAGIC, 3, struct intra_refresh) -#define VENC_IOCTL_RC_CONFIG _IOW(VENC_IOCTL_MAGIC, 4, struct rc_config) -#define VENC_IOCTL_ENCODE_CONFIG _IOW(VENC_IOCTL_MAGIC, 5, struct init_config) -#define VENC_IOCTL_STOP _IO(VENC_IOCTL_MAGIC, 6) -#define VENC_IOCTL_WAIT_FOR_ENCODE _IOR(VENC_IOCTL_MAGIC, 7, struct frame_type) -#define VENC_IOCTL_STOP_ENCODE _IO(VENC_IOCTL_MAGIC, 8) +#define VENC_IOCTL_CMD_READ_NEXT_MSG \ + _IOWR(VENC_IOCTL_MAGIC, 1, struct venc_msg) + +#define VENC_IOCTL_CMD_STOP_READ_MSG _IO(VENC_IOCTL_MAGIC, 2) + +#define VENC_IOCTL_SET_INPUT_BUFFER \ + _IOW(VENC_IOCTL_MAGIC, 3, struct venc_pmem) + +#define VENC_IOCTL_SET_OUTPUT_BUFFER \ + _IOW(VENC_IOCTL_MAGIC, 4, struct venc_pmem) + +#define VENC_IOCTL_CMD_START _IOW(VENC_IOCTL_MAGIC, 5, struct venc_init_config) + +#define VENC_IOCTL_CMD_ENCODE_FRAME \ + _IOW(VENC_IOCTL_MAGIC, 6, struct venc_buffer) + +#define VENC_IOCTL_CMD_FILL_OUTPUT_BUFFER \ + _IOW(VENC_IOCTL_MAGIC, 7, struct venc_buffer) + +#define VENC_IOCTL_CMD_FLUSH \ + _IOW(VENC_IOCTL_MAGIC, 8, struct venc_buffer_flush) + +#define VENC_IOCTL_CMD_PAUSE _IO(VENC_IOCTL_MAGIC, 9) + +#define VENC_IOCTL_CMD_RESUME _IO(VENC_IOCTL_MAGIC, 10) + +#define VENC_IOCTL_CMD_STOP _IO(VENC_IOCTL_MAGIC, 11) + +#define VENC_IOCTL_SET_INTRA_PERIOD \ + _IOW(VENC_IOCTL_MAGIC, 12, int) + +#define VENC_IOCTL_CMD_REQUEST_IFRAME _IO(VENC_IOCTL_MAGIC, 13) + +#define VENC_IOCTL_GET_SEQUENCE_HDR \ + _IOWR(VENC_IOCTL_MAGIC, 14, struct venc_seq_config) + +#define VENC_IOCTL_SET_INTRA_REFRESH \ + _IOW(VENC_IOCTL_MAGIC, 15, int) + +#define VENC_IOCTL_SET_FRAME_RATE \ + _IOW(VENC_IOCTL_MAGIC, 16, struct venc_frame_rate) + +#define VENC_IOCTL_SET_TARGET_BITRATE \ + _IOW(VENC_IOCTL_MAGIC, 17, int) + +#define VENC_IOCTL_SET_QP_RANGE \ + _IOW(VENC_IOCTL_MAGIC, 18, struct venc_qp_range) -#endif /* _MSM_VENC_H_ */ +#endif From 8f33c1edeff7899ce9ba4e843d0508b34805cee2 Mon Sep 17 00:00:00 2001 From: Kuma Chang Date: Wed, 3 Mar 2010 14:23:45 -0800 Subject: [PATCH 0552/2556] [ARM] msm: usb: diag: add support for getting qdsp6 logs for QXDM Signed-off-by: Iliyan Malchev --- drivers/usb/gadget/diag.c | 78 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/drivers/usb/gadget/diag.c b/drivers/usb/gadget/diag.c index ed1c150f69cc1..fdf424e08eef5 100644 --- a/drivers/usb/gadget/diag.c +++ b/drivers/usb/gadget/diag.c @@ -104,6 +104,10 @@ struct diag_context #endif smd_channel_t *ch; int in_busy; +#ifdef CONFIG_ARCH_QSD8X50 + smd_channel_t *ch_dsp; + int in_busy_dsp; +#endif int online; /* assembly buffer for USB->A9 HDLC frames */ @@ -179,6 +183,9 @@ static inline struct diag_context *func_to_dev(struct usb_function *f) } static void smd_try_to_send(struct diag_context *ctxt); +#ifdef CONFIG_ARCH_QSD8X50 +static void dsp_try_to_send(struct diag_context *ctxt); +#endif static void diag_queue_out(struct diag_context *ctxt); @@ -437,6 +444,18 @@ static void diag_in_complete(struct usb_ep *ept, struct usb_request *req) smd_try_to_send(ctxt); } +#ifdef CONFIG_ARCH_QSD8X50 +static void diag_dsp_in_complete(struct usb_ep *ept, struct usb_request *req) +{ + struct diag_context *ctxt = req->context; + + ctxt->in_busy_dsp = 0; + req_put(ctxt, &ctxt->tx_req_idle, req); + dsp_try_to_send(ctxt); + wake_up(&ctxt->write_wq); +} +#endif + static void diag_process_hdlc(struct diag_context *ctxt, void *_data, unsigned len) { unsigned char *data = _data; @@ -601,6 +620,53 @@ static void smd_diag_notify(void *priv, unsigned event) smd_try_to_send(ctxt); } +#ifdef CONFIG_ARCH_QSD8X50 +static void dsp_try_to_send(struct diag_context *ctxt) +{ +again: + if (ctxt->ch_dsp && (!ctxt->in_busy_dsp)) { + int r = smd_read_avail(ctxt->ch_dsp); + + if (r > TX_REQ_BUF_SZ) { + return; + } + if (r > 0) { + struct usb_request *req; + req = req_get(ctxt, &ctxt->tx_req_idle); + if (!req) { + pr_err("%s: tx req queue is out of buffers\n", + __func__); + return; + } + smd_read(ctxt->ch_dsp, req->buf, r); + + if (!ctxt->online) { +// printk("$$$ discard %d\n", r); + goto again; + } + req->complete = diag_dsp_in_complete; + req->context = ctxt; + req->length = r; + + TRACE("Q6>", req->buf, r, 1); + ctxt->in_busy_dsp = 1; + r = usb_ep_queue(ctxt->in, req, GFP_ATOMIC); + if (r < 0) { + pr_err("%s: usb_ep_queue failed: %d\n", + __func__, r); + req_put(ctxt, &ctxt->tx_req_idle, req); + } + } + } +} + +static void dsp_diag_notify(void *priv, unsigned event) +{ + struct diag_context *ctxt = priv; + dsp_try_to_send(ctxt); +} +#endif + static int __init create_bulk_endpoints(struct diag_context *ctxt, struct usb_endpoint_descriptor *in_desc, struct usb_endpoint_descriptor *out_desc) @@ -753,6 +819,10 @@ static int diag_function_set_alt(struct usb_function *f, diag_queue_out(ctxt); smd_try_to_send(ctxt); +#ifdef CONFIG_ARCH_QSD8X50 + dsp_try_to_send(ctxt); +#endif + #if ROUTE_TO_USERSPACE wake_up(&ctxt->read_wq); #endif @@ -806,6 +876,14 @@ int diag_bind_config(struct usb_configuration *c) if (ret) return ret; +#ifdef CONFIG_ARCH_QSD8X50 + ret = smd_open("DSP_DIAG", &ctxt->ch_dsp, ctxt, dsp_diag_notify); + if (ret) { + pr_err("%s: smd_open failed (DSP_DIAG)\n", __func__); + return ret; + } +#endif + ctxt->cdev = c->cdev; ctxt->function.name = "diag"; ctxt->function.descriptors = fs_diag_descs; From b9f1695ecd5d3c1f257de96b54510c21fa3d62ee Mon Sep 17 00:00:00 2001 From: Stanley Chu Date: Thu, 4 Mar 2010 10:59:22 -0800 Subject: [PATCH 0553/2556] [ARM] msm: usb: diag: fix a memory leak in tx_req_idle Signed-off-by: Iliyan Malchev --- drivers/usb/gadget/diag.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/gadget/diag.c b/drivers/usb/gadget/diag.c index fdf424e08eef5..8c1df2a8ec536 100644 --- a/drivers/usb/gadget/diag.c +++ b/drivers/usb/gadget/diag.c @@ -596,6 +596,7 @@ static void smd_try_to_send(struct diag_context *ctxt) if (!ctxt->online) { // printk("$$$ discard %d\n", r); + req_put(ctxt, &ctxt->tx_req_idle, req); goto again; } req->complete = diag_in_complete; @@ -609,6 +610,7 @@ static void smd_try_to_send(struct diag_context *ctxt) pr_err("%s: usb_ep_queue failed: %d\n", __func__, r); req_put(ctxt, &ctxt->tx_req_idle, req); + ctxt->in_busy = 0; } } } @@ -642,6 +644,7 @@ static void dsp_try_to_send(struct diag_context *ctxt) if (!ctxt->online) { // printk("$$$ discard %d\n", r); + req_put(ctxt, &ctxt->tx_req_idle, req); goto again; } req->complete = diag_dsp_in_complete; @@ -655,6 +658,7 @@ static void dsp_try_to_send(struct diag_context *ctxt) pr_err("%s: usb_ep_queue failed: %d\n", __func__, r); req_put(ctxt, &ctxt->tx_req_idle, req); + ctxt->in_busy_dsp = 0; } } } From 07f61a092f534d9dc67ce7064d2d6bc6dca7b1b8 Mon Sep 17 00:00:00 2001 From: Davy Lin Date: Mon, 8 Mar 2010 14:57:33 -0800 Subject: [PATCH 0554/2556] [ARM] qsd8k: mahimahi: fix modify the remote-key ADC table and handling code On some headsets/earphones, remote keys cannot be detected correctly on mahimahi CDMA, and remote_key_adc_table needs to be adjusted slightly to fit for both versions of mahimahi. Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi-microp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi-microp.c b/arch/arm/mach-msm/board-mahimahi-microp.c index 1f0d23f00e04f..5c81412fa56d8 100644 --- a/arch/arm/mach-msm/board-mahimahi-microp.c +++ b/arch/arm/mach-msm/board-mahimahi-microp.c @@ -216,7 +216,7 @@ static uint16_t lsensor_adc_table[10] = { }; static uint16_t remote_key_adc_table[6] = { - 0, 31, 43, 98, 129, 192 + 0, 33, 43, 110, 129, 220 }; static uint32_t golden_adc = 0xC0; @@ -632,9 +632,9 @@ static ssize_t microp_i2c_remotekey_adc_show(struct device *dev, microp_read_adc(MICROP_REMOTE_KEY_ADC_CHAN, &value); - for (i = 0; i < 3; i += 2) { - if ((value > remote_key_adc_table[i]) && - (value < remote_key_adc_table[i])) { + for (i = 0; i < 3; i++) { + if ((value >= remote_key_adc_table[2 * i]) && + (value <= remote_key_adc_table[2 * i + 1])) { button = i + 1; } From 6fb623bcf111ce8dcb5f0c7498f30d3cd12175f9 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 9 Mar 2010 12:01:14 -0800 Subject: [PATCH 0555/2556] a1026: in-call tuning improvements Signed-off-by: Iliyan Malchev --- drivers/i2c/chips/a1026.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/chips/a1026.c b/drivers/i2c/chips/a1026.c index 1a747760e7a84..7eaa3e1916303 100644 --- a/drivers/i2c/chips/a1026.c +++ b/drivers/i2c/chips/a1026.c @@ -292,9 +292,11 @@ unsigned char phonecall_receiver[] = { 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */ 0x80,0x17,0x00,0x00, /* SetAlgorithmParmID, 0x0000:Suppression Strength */ 0x80,0x18,0x00,0x05, /* SetAlgorithmParm, 0x0005:25dB Max Suppression */ + 0x80,0x17,0x00,0x20, /* SetAlgorithmParmID, 0x0020:Tx PostEq Mode */ + 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002:On always */ 0x80,0x1B,0x00,0x0C, /* SetDigitalInputGain, 0x00:Primay Mic (Tx), 0x0C:(12 dB) */ 0x80,0x1B,0x01,0x0C, /* SetDigitalInputGain, 0x01:Secondary Mic (Tx), 0x0C:(12 dB) */ - 0x80,0x15,0x00,0x00, /* SetDigitalOutputGain, 0x00:Tx, 0x00:(0 dB) */ + 0x80,0x15,0x00,0xFA, /* SetDigitalOutputGain, 0x00:Tx, 0xFA:(-6 dB) */ }; unsigned char phonecall_headset[] = { @@ -309,7 +311,7 @@ unsigned char phonecall_speaker[] = { 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002:1-mic Desktop/Vehicle (DV) */ 0x80,0x1C,0x00,0x01, /* VoiceProcessingOn, 0x0001:Yes */ 0x80,0x17,0x00,0x00, /* SetAlgorithmParmID, 0x0000:Suppression Strength */ - 0x80,0x18,0x00,0x04, /* SetAlgorithmParm, 0x0004:20dB Max Suppression */ + 0x80,0x18,0x00,0x02, /* SetAlgorithmParm, 0x0002 */ 0x80,0x17,0x00,0x04, /* SetAlgorithmParmID, 0x0004:Use AGC */ 0x80,0x18,0x00,0x00, /* SetAlgorithmParm, 0x0000:No */ 0x80,0x17,0x00,0x1A, /* SetAlgorithmParmID, 0x001A:Use ComfortNoise */ From 203726c0eae4332b59205e05f0a35d38e2f22ba5 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Wed, 10 Mar 2010 17:07:02 -0500 Subject: [PATCH 0556/2556] [ARM] msm: mahimahi: Add platform data for RNDIS ethernet USB function Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/board-mahimahi.c | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index da7956e39135f..9219188402b7a 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -209,6 +209,22 @@ static struct platform_device usb_mass_storage_device = { }, }; +#ifdef CONFIG_USB_ANDROID_RNDIS +static struct usb_ether_platform_data rndis_pdata = { + /* ethaddr is filled by board_serialno_setup */ + .vendorID = 0x18d1, + .vendorDescr = "Google, Inc.", +}; + +static struct platform_device rndis_device = { + .name = "rndis", + .id = -1, + .dev = { + .platform_data = &rndis_pdata, + }, +}; +#endif + static struct android_usb_platform_data android_usb_pdata = { .vendor_id = 0x18d1, .product_id = 0x4e11, @@ -816,6 +832,9 @@ static struct platform_device *devices[] __initdata = { &msm_device_nand, &msm_device_hsusb, &usb_mass_storage_device, +#ifdef CONFIG_USB_ANDROID_RNDIS + &rndis_device, +#endif &android_usb_device, &android_pmem_mdp_device, &android_pmem_adsp_device, @@ -915,6 +934,20 @@ __tagtable(ATAG_BDADDR, parse_tag_bdaddr); static int __init board_serialno_setup(char *serialno) { +#ifdef CONFIG_USB_ANDROID_RNDIS + int i; + char *src = serialno; + + /* create a fake MAC address from our serial number. + * first byte is 0x02 to signify locally administered. + */ + rndis_pdata.ethaddr[0] = 0x02; + for (i = 0; *src; i++) { + /* XOR the USB serial across the remaining bytes */ + rndis_pdata.ethaddr[i % (ETH_ALEN - 1) + 1] ^= *src++; + } +#endif + android_usb_pdata.serial_number = serialno; return 1; } From 78abb3fc76cfaa8a3bfdba1e1fa13dd9efc06c59 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Wed, 10 Mar 2010 20:58:15 -0500 Subject: [PATCH 0557/2556] [ARM] msm: htc: Add platform data for RNDIS vendor ID and MAC address. This fixes a kernel panic in rndis.c when receiving the OID_GEN_VENDOR_DESCRIPTION command. Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/devices_htc.c | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c index f66fcbad7af3b..ae53e3c549a97 100644 --- a/arch/arm/mach-msm/devices_htc.c +++ b/arch/arm/mach-msm/devices_htc.c @@ -105,6 +105,22 @@ static struct platform_device usb_mass_storage_device = { }, }; +#ifdef CONFIG_USB_ANDROID_RNDIS +static struct usb_ether_platform_data rndis_pdata = { + /* ethaddr is filled by board_serialno_setup */ + .vendorID = 0x0bb4, + .vendorDescr = "HTC", +}; + +static struct platform_device rndis_device = { + .name = "rndis", + .id = -1, + .dev = { + .platform_data = &rndis_pdata, + }, +}; +#endif + static char *usb_functions_ums[] = { "usb_mass_storage", }; @@ -184,6 +200,9 @@ void __init msm_add_usb_devices(void (*phy_reset) (void)) msm_hsusb_pdata.phy_reset = phy_reset; msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; platform_device_register(&msm_device_hsusb); +#ifdef CONFIG_USB_ANDROID_RNDIS + platform_device_register(&rndis_device); +#endif platform_device_register(&usb_mass_storage_device); platform_device_register(&android_usb_device); } @@ -451,6 +470,10 @@ int board_mfg_mode(void) static int __init board_serialno_setup(char *serialno) { +#ifdef CONFIG_USB_ANDROID_RNDIS + int i; + char *src; +#endif char *str; /* use default serial number when mode is factory2 */ @@ -458,6 +481,18 @@ static int __init board_serialno_setup(char *serialno) str = df_serialno; else str = serialno; + +#ifdef CONFIG_USB_ANDROID_RNDIS + /* create a fake MAC address from our serial number. + * first byte is 0x02 to signify locally administered. + */ + rndis_pdata.ethaddr[0] = 0x02; + src = str; + for (i = 0; *src; i++) { + /* XOR the USB serial across the remaining bytes */ + rndis_pdata.ethaddr[i % (ETH_ALEN - 1) + 1] ^= *src++; + } +#endif android_usb_pdata.serial_number = str; return 1; } From 0c12185d663ed508644c237b8ea7f8f2d695e66b Mon Sep 17 00:00:00 2001 From: Haley Teng Date: Wed, 10 Mar 2010 13:43:33 -0800 Subject: [PATCH 0558/2556] [ARM] msm: usb: diag: fix several deadlocks -- diag_release() can get blocked because user_lock was being acquired before wait_event_interruptible() in diag_write()/diag_read(). -- wake up 'read_wq' and 'write_wq' when usb status changes, to help userspace return from read()/write() when - usb is offline - echo 0 > /sys/module/diag/parameters/enabled Signed-off-by: Iliyan Malchev --- drivers/usb/gadget/diag.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/diag.c b/drivers/usb/gadget/diag.c index 8c1df2a8ec536..3611462fb48d0 100644 --- a/drivers/usb/gadget/diag.c +++ b/drivers/usb/gadget/diag.c @@ -275,8 +275,10 @@ static ssize_t diag_read(struct file *fp, char __user *buf, goto end; } + mutex_unlock(&ctxt->user_lock); ret = wait_event_interruptible(ctxt->read_wq, (req = req_get(ctxt, &ctxt->rx_req_user)) || !ctxt->online); + mutex_lock(&ctxt->user_lock); if (ret < 0) { pr_err("%s: wait_event_interruptible error %d\n", __func__, ret); @@ -322,10 +324,10 @@ static ssize_t diag_write(struct file *fp, const char __user *buf, struct usb_request *req = 0; int ret = 0; - mutex_lock(&ctxt->user_lock); - ret = wait_event_interruptible(ctxt->write_wq, ((req = req_get(ctxt, &ctxt->tx_req_idle)) || !ctxt->online)); + + mutex_lock(&ctxt->user_lock); if (ret < 0) { pr_err("%s: wait_event_interruptible error %d\n", __func__, ret); @@ -829,6 +831,7 @@ static int diag_function_set_alt(struct usb_function *f, #if ROUTE_TO_USERSPACE wake_up(&ctxt->read_wq); + wake_up(&ctxt->write_wq); #endif return 0; @@ -839,6 +842,10 @@ static void diag_function_disable(struct usb_function *f) struct diag_context *ctxt = func_to_dev(f); ctxt->online = 0; +#if ROUTE_TO_USERSPACE + wake_up(&ctxt->read_wq); + wake_up(&ctxt->write_wq); +#endif usb_ep_disable(ctxt->in); usb_ep_disable(ctxt->out); } From 8bbfdd643f2cef5b4a8bc138772fe8ee27feea09 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Thu, 11 Mar 2010 16:09:54 -0800 Subject: [PATCH 0559/2556] [ARM] msm: make hw3d master and client char devices (used to be misc) Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/hw3d.c | 78 +++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 24 deletions(-) diff --git a/arch/arm/mach-msm/hw3d.c b/arch/arm/mach-msm/hw3d.c index 4ac083251aca4..ac09ea06005a5 100644 --- a/arch/arm/mach-msm/hw3d.c +++ b/arch/arm/mach-msm/hw3d.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -55,9 +55,14 @@ struct mem_region { void __iomem *vbase; }; +/* Device minor numbers for master and client */ +#define MINOR_MASTER 0 +#define MINOR_CLIENT 1 + struct hw3d_info { - struct miscdevice master_dev; - struct miscdevice client_dev; + dev_t devno; + struct cdev master_cdev; + struct cdev client_cdev; struct clk *grp_clk; struct clk *imem_clk; @@ -122,13 +127,13 @@ static struct vm_operations_struct hw3d_vm_ops = { static bool is_master(struct hw3d_info *info, struct file *file) { int fmin = MINOR(file->f_dentry->d_inode->i_rdev); - return fmin == info->master_dev.minor; + return fmin == MINOR(info->master_cdev.dev); } static bool is_client(struct hw3d_info *info, struct file *file) { int fmin = MINOR(file->f_dentry->d_inode->i_rdev); - return fmin == info->client_dev.minor; + return fmin == MINOR(info->client_cdev.dev); } inline static void locked_hw3d_irq_disable(struct hw3d_info *info) @@ -282,7 +287,7 @@ static void locked_hw3d_revoke(struct hw3d_info *info) bool is_msm_hw3d_file(struct file *file) { struct hw3d_info *info = hw3d_info; - if (MAJOR(file->f_dentry->d_inode->i_rdev) == MISC_MAJOR && + if (MAJOR(file->f_dentry->d_inode->i_rdev) == MAJOR(info->devno) && (is_master(info, file) || is_client(info, file))) return 1; return 0; @@ -661,6 +666,11 @@ static int hw3d_resume(struct platform_device *pdev) static int __init hw3d_probe(struct platform_device *pdev) { struct hw3d_info *info; +#define DEV_MASTER MKDEV(MAJOR(info->devno), MINOR_MASTER) +#define DEV_CLIENT MKDEV(MAJOR(info->devno), MINOR_CLIENT) + struct class *hw3d_class; + struct device *master_dev; + struct device *client_dev; struct resource *res[HW3D_NUM_REGIONS]; int i; int irq; @@ -722,25 +732,37 @@ static int __init hw3d_probe(struct platform_device *pdev) } } + hw3d_class = class_create(THIS_MODULE, "msm_hw3d"); + if (IS_ERR(hw3d_class)) + goto err_fail_create_class; + + ret = alloc_chrdev_region(&info->devno, 0, 2, "msm_hw3d"); + if (ret < 0) + goto err_fail_alloc_region; + /* register the master/client devices */ - info->master_dev.name = "msm_hw3dm"; - info->master_dev.minor = MISC_DYNAMIC_MINOR; - info->master_dev.fops = &hw3d_fops; - info->master_dev.parent = &pdev->dev; - ret = misc_register(&info->master_dev); - if (ret) { + master_dev = device_create(hw3d_class, &pdev->dev, + DEV_MASTER, "%s", "msm_hw3dm"); + if (IS_ERR(master_dev)) + goto err_dev_master; + cdev_init(&info->master_cdev, &hw3d_fops); + info->master_cdev.owner = THIS_MODULE; + ret = cdev_add(&info->master_cdev, DEV_MASTER, 1); + if (ret < 0) { pr_err("%s: Cannot register master device node\n", __func__); - goto err_misc_reg_master; + goto err_reg_master; } - info->client_dev.name = "msm_hw3dc"; - info->client_dev.minor = MISC_DYNAMIC_MINOR; - info->client_dev.fops = &hw3d_fops; - info->client_dev.parent = &pdev->dev; - ret = misc_register(&info->client_dev); - if (ret) { + client_dev = device_create(hw3d_class, &pdev->dev, + DEV_CLIENT, "%s", "msm_hw3dc"); + if (IS_ERR(client_dev)) + goto err_dev_client; + cdev_init(&info->client_cdev, &hw3d_fops); + info->client_cdev.owner = THIS_MODULE; + ret = cdev_add(&info->client_cdev, DEV_CLIENT, 1); + if (ret < 0) { pr_err("%s: Cannot register client device node\n", __func__); - goto err_misc_reg_client; + goto err_reg_client; } info->early_suspend.suspend = hw3d_early_suspend; @@ -763,10 +785,18 @@ static int __init hw3d_probe(struct platform_device *pdev) err_req_irq: unregister_early_suspend(&info->early_suspend); - misc_deregister(&info->client_dev); -err_misc_reg_client: - misc_deregister(&info->master_dev); -err_misc_reg_master: + cdev_del(&info->client_cdev); +err_reg_client: + device_destroy(hw3d_class, DEV_CLIENT); +err_dev_client: + cdev_del(&info->master_cdev); +err_reg_master: + device_destroy(hw3d_class, DEV_MASTER); +err_dev_master: + unregister_chrdev_region(info->devno, 2); +err_fail_alloc_region: + class_unregister(hw3d_class); +err_fail_create_class: err_remap_region: for (i = 0; i < HW3D_NUM_REGIONS; ++i) if (info->regions[i].vbase != 0) From 5f172a7ce50fafb9484e1e20f4dbfe31bf212f3f Mon Sep 17 00:00:00 2001 From: Andy Yeh Date: Fri, 12 Mar 2010 13:44:13 -0800 Subject: [PATCH 0560/2556] [ARM] msm: camera: fix greenish snapshot in low light (w/o flash) -- stream-on snapshot after writing gain/line -- correct wrong reg 0x3051 to 0x3151 -- set 0x105 to 0x1h to prevent first corrupted frame -- parameter hold/release is removed from snapshot session Signed-off-by: Iliyan Malchev --- drivers/media/video/msm/s5k3e2fx.c | 112 +++++++++++++++-------------- 1 file changed, 60 insertions(+), 52 deletions(-) diff --git a/drivers/media/video/msm/s5k3e2fx.c b/drivers/media/video/msm/s5k3e2fx.c index 679fd4edf9482..bc0b75705d523 100644 --- a/drivers/media/video/msm/s5k3e2fx.c +++ b/drivers/media/video/msm/s5k3e2fx.c @@ -133,7 +133,7 @@ static uint16_t g_usModuleVersion; /*0: rev.4, 1: rev.5 */ #define REG_301B_RESERVED 0x301B #define REG_30BD_RESERVED 0x30BD #define REG_30C2_RESERVED 0x30C2 -#define REG_3051_RESERVED 0x3051 +#define REG_3151_RESERVED 0x3151 #define REG_3029_RESERVED 0x3029 #define REG_30BF_RESERVED 0x30BF #define REG_3022_RESERVED 0x3022 @@ -244,7 +244,7 @@ struct s5k3e2fx_i2c_reg_conf Init_setting[2][NUM_INIT_REG] = { {REG_30BD_RESERVED, 0x06}, {REG_30C2_RESERVED, 0x0b}, {REG_SHADE_CLK_ENABLE, 0x81}, - {REG_3051_RESERVED, 0xe6}, + {REG_3151_RESERVED, 0xe6}, {REG_3029_RESERVED, 0x02}, {REG_30BF_RESERVED, 0x00}, {REG_3022_RESERVED, 0x87}, @@ -358,7 +358,7 @@ struct s5k3e2fx_i2c_reg_conf Init_setting[2][NUM_INIT_REG] = { {REG_30BD_RESERVED, 0x06}, {REG_30C2_RESERVED, 0x0b}, {REG_SHADE_CLK_ENABLE, 0x81}, - {REG_3051_RESERVED, 0xe6}, + {REG_3151_RESERVED, 0xe6}, {REG_3029_RESERVED, 0x02}, {REG_30BF_RESERVED, 0x00}, {REG_3022_RESERVED, 0x87}, @@ -1949,6 +1949,7 @@ static struct i2c_driver s5k3e2fx_i2c_driver = { }, }; +#if 0 static int s5k3e2fx_test(enum msm_s_test_mode mo) { int rc = 0; @@ -1961,7 +1962,7 @@ static int s5k3e2fx_test(enum msm_s_test_mode mo) return rc; } - +#endif static int s5k3e2fx_setting(enum msm_s_reg_update rupdate, enum msm_s_setting rt) { @@ -1972,8 +1973,6 @@ static int s5k3e2fx_setting(enum msm_s_reg_update rupdate, case S_UPDATE_PERIODIC:{ if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) { struct s5k3e2fx_i2c_reg_conf tbl_1[] = { - {S5K3E2FX_REG_MODE_SELECT, - S5K3E2FX_MODE_SELECT_SW_STANDBY}, {REG_X_OUTPUT_SIZE_MSB, s5k3e2fx_reg_pat[rt]. x_output_size_msb}, @@ -2097,6 +2096,13 @@ static int s5k3e2fx_setting(enum msm_s_reg_update rupdate, } }; + rc = s5k3e2fx_i2c_write_b + (s5k3e2fx_client->addr, + S5K3E2FX_REG_MODE_SELECT, + S5K3E2FX_MODE_SELECT_SW_STANDBY); + if (rc < 0) + return rc; + CDBG("Binning_enable = 0x %2x" "[s5k3e2fx.c s5k3e2fx_setting]\r\n", s5k3e2fx_reg_pat[rt].binning_enable); @@ -2146,27 +2152,17 @@ static int s5k3e2fx_setting(enum msm_s_reg_update rupdate, return rc; } - /* Fixes CAMIF errors */ - if (rt == S_RES_CAPTURE) - msleep(25); - - rc = s5k3e2fx_i2c_write_b - (s5k3e2fx_client->addr, - S5K3E2FX_REG_MODE_SELECT, - S5K3E2FX_MODE_SELECT_STREAM); - if (rc < 0) - return rc; - - mdelay(5); - - rc = s5k3e2fx_test(s5k3e2fx_ctrl-> - set_test); - if (rc < 0) - return rc; + if (rt == S_RES_PREVIEW) { + rc = s5k3e2fx_i2c_write_b + (s5k3e2fx_client->addr, + S5K3E2FX_REG_MODE_SELECT, + S5K3E2FX_MODE_SELECT_STREAM); + if (rc < 0) + return rc; } - } + } break; /* UPDATE_PERIODIC */ - + } case S_REG_INIT:{ if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) { struct s5k3e2fx_i2c_reg_conf tbl_3[] = { @@ -2412,6 +2408,9 @@ static int s5k3e2fx_probe_init_lens_correction( s5k3e2fx_i2c_write_table(&lc_setting[g_usModuleVersion][0], NUM_LC_REG); ++ /* Solve EVT5 greenish in lowlight, prevent corrupted frame*/ ++ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x105,0x1); + /*20090811 separates the EVT4/EVT5 sensor init and LC setting end */ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, S5K3E2FX_REG_MODE_SELECT, @@ -2523,7 +2522,8 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) struct s5k3e2fx_i2c_reg_conf tbl[2]; - CDBG("Line:%d s5k3e2fx_write_exp_gain \n", __LINE__); + CDBG("Line:%d s5k3e2fx_write_exp_gain gain %d line %d\n", + __LINE__, gain, line); if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) { @@ -2557,12 +2557,15 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) ll_ratio = 0x400; /* AEC_FLASHING */ - rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_HOLD); - if (rc < 0) { - pr_err("s5k3e2fx_i2c_write_b failed on line %d\n", __LINE__); - return rc; + if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) { + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); + if (rc < 0) { + pr_err("s5k3e2fx_i2c_write_b failed on line %d\n", + __LINE__); + return rc; + } } /* update gain registers */ @@ -2575,7 +2578,7 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); if (rc < 0) goto write_gain_done; -#if 0 /* AEC_FLASHING */ +#if 1 /* Solve EVT5 greenish in lowlight*/ ll_pck = ll_pck * ll_ratio; ll_pck_msb = ((ll_pck / 0x400) & 0xFF00) >> 8; ll_pck_lsb = (ll_pck / 0x400) & 0x00FF; @@ -2587,16 +2590,7 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); if (rc < 0) goto write_gain_done; - - line = line / ll_ratio; - intg_t_msb = (line & 0xFF00) >> 8; - intg_t_lsb = (line & 0x00FF); - tbl[0].waddr = REG_COARSE_INTEGRATION_TIME; - tbl[0].bdata = intg_t_msb; - tbl[1].waddr = REG_COARSE_INTEGRATION_TIME_LSB; - tbl[1].bdata = intg_t_lsb; - rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); -#endif /* AEC_FLASHING */ +#else if (line / 0x400 + offset > fl_lines) ll_pck = line / 0x400 + offset; else @@ -2612,6 +2606,7 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); if (rc < 0) goto write_gain_done; +#endif line = line / 0x400; intg_t_msb = (line & 0xFF00) >> 8; @@ -2622,13 +2617,17 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) tbl[1].bdata = intg_t_lsb; rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); - rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_UPDATE); - if (rc < 0) { - pr_err("s5k3e2fx_i2c_write_b failed on line %d\n", __LINE__); - return rc; + if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) { + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) { + pr_err("s5k3e2fx_i2c_write_b failed on line %d\n", + __LINE__); + return rc; + } } + write_gain_done: return rc; } @@ -2640,6 +2639,14 @@ static int s5k3e2fx_set_pict_exp_gain(uint16_t gain, uint32_t line) CDBG("Line:%d s5k3e2fx_set_pict_exp_gain \n", __LINE__); rc = s5k3e2fx_write_exp_gain(gain, line); + if (rc < 0) + return rc; + +/* Solve EVT5 greenish lowlight*/ + rc = s5k3e2fx_i2c_write_b + (s5k3e2fx_client->addr, + S5K3E2FX_REG_MODE_SELECT, + S5K3E2FX_MODE_SELECT_STREAM); return rc; } @@ -2650,9 +2657,13 @@ static int s5k3e2fx_video_config(int mode, int res) switch (res) { case S_QTR_SIZE: + pr_info("start sensor S_RES_PREVIEW config: %d\n", __LINE__); rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_PREVIEW); if (rc < 0) return rc; + /* only apply my_reg for returning preview*/ + rc = s5k3e2fx_write_exp_gain(s5k3e2fx_ctrl->my_reg_gain, + s5k3e2fx_ctrl->my_reg_line_count); break; case S_FULL_SIZE: @@ -2669,9 +2680,6 @@ static int s5k3e2fx_video_config(int mode, int res) s5k3e2fx_ctrl->curr_res = res; s5k3e2fx_ctrl->sensormode = mode; - rc = s5k3e2fx_write_exp_gain(s5k3e2fx_ctrl->my_reg_gain, - s5k3e2fx_ctrl->my_reg_line_count); - return rc; } From a0ffc4cdec7ed1b22fba9f19a1b748594d080e37 Mon Sep 17 00:00:00 2001 From: Solomon Chiu Date: Wed, 3 Mar 2010 15:47:37 -0800 Subject: [PATCH 0561/2556] [ARM] msm_fb: add definitions for RGB555 and RGB666 output interface formats Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/include/mach/msm_fb.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/mach-msm/include/mach/msm_fb.h b/arch/arm/mach-msm/include/mach/msm_fb.h index 42c5e2888e755..e935a34b3310a 100644 --- a/arch/arm/mach-msm/include/mach/msm_fb.h +++ b/arch/arm/mach-msm/include/mach/msm_fb.h @@ -21,6 +21,10 @@ struct mddi_info; +/* output interface format */ +#define MSM_MDP_OUT_IF_FMT_RGB565 0 +#define MSM_MDP_OUT_IF_FMT_RGB666 1 + struct msm_fb_data { int xres; /* x resolution in pixels */ int yres; /* y resolution in pixels */ From e45d27ea17b0f79db2aadc6cac4e95f7d418745b Mon Sep 17 00:00:00 2001 From: Solomon Chiu Date: Wed, 3 Mar 2010 15:48:34 -0800 Subject: [PATCH 0562/2556] [ARM] msm: mdp_lcdc: support output format rgb666 Signed-off-by: Iliyan Malchev --- drivers/video/msm/mdp_lcdc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/mdp_lcdc.c b/drivers/video/msm/mdp_lcdc.c index a1e526d2f9438..8a90245250dc4 100644 --- a/drivers/video/msm/mdp_lcdc.c +++ b/drivers/video/msm/mdp_lcdc.c @@ -157,7 +157,12 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) DMA_DITHER_EN); dma_cfg |= DMA_OUT_SEL_LCDC; dma_cfg &= ~DMA_DST_BITS_MASK; - dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + + if (fb_panel->fb_data->output_format == MSM_MDP_OUT_IF_FMT_RGB666) + dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + else + dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG); /* enable the lcdc timing generation */ From e7f83aafd26f61746705c3fb7638e4bcbae4877d Mon Sep 17 00:00:00 2001 From: Solomon Chiu Date: Wed, 3 Mar 2010 15:49:57 -0800 Subject: [PATCH 0563/2556] [ARM] qsd8k: mahimahi: enable support for rgb666 on post-EVT1 CDMA revs Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi-panel.c | 39 +++++++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi-panel.c b/arch/arm/mach-msm/board-mahimahi-panel.c index c59c51f9b690b..a7dadcad2f233 100644 --- a/arch/arm/mach-msm/board-mahimahi-panel.c +++ b/arch/arm/mach-msm/board-mahimahi-panel.c @@ -90,7 +90,7 @@ struct lcm_tbl { uint8_t val; }; -static struct lcm_tbl samsung_oled_init_table[] = { +static struct lcm_tbl samsung_oled_rgb565_init_table[] = { { 0x31, 0x08 }, { 0x32, 0x14 }, { 0x30, 0x2 }, @@ -109,6 +109,28 @@ static struct lcm_tbl samsung_oled_init_table[] = { { 0x26, 0xA0 }, }; +static struct lcm_tbl samsung_oled_rgb666_init_table[] = { + { 0x31, 0x08 }, + { 0x32, 0x14 }, + { 0x30, 0x2 }, + { 0x27, 0x1 }, + { 0x12, 0x8 }, + { 0x13, 0x8 }, + { 0x15, 0x0 }, + { 0x16, 0x01 }, + { 0x39, 0x24 }, + { 0x17, 0x22 }, + { 0x18, 0x33 }, + { 0x19, 0x3 }, + { 0x1A, 0x1 }, + { 0x22, 0xA4 }, + { 0x23, 0x0 }, + { 0x26, 0xA0 }, +}; + +static struct lcm_tbl *init_tablep = samsung_oled_rgb565_init_table; +static size_t init_table_sz = ARRAY_SIZE(samsung_oled_rgb565_init_table); + #define OLED_GAMMA_TABLE_SIZE (7 * 3) static struct lcm_tbl samsung_oled_gamma_table[][OLED_GAMMA_TABLE_SIZE] = { /* level 10 */ @@ -455,9 +477,9 @@ static int samsung_oled_panel_unblank(struct msm_lcdc_panel_ops *ops) clk_enable(spi_clk); - for (i = 0; i < ARRAY_SIZE(samsung_oled_init_table); i++) - lcm_writeb(samsung_oled_init_table[i].reg, - samsung_oled_init_table[i].val); + for (i = 0; i < init_table_sz; i++) + lcm_writeb(init_tablep[i].reg, init_tablep[i].val); + lcm_writew(0xef, 0xd0e8); lcm_writeb(0x1d, 0xa0); table_sel_idx = 0; @@ -517,7 +539,7 @@ static struct msm_fb_data mahimahi_lcdc_fb_data = { .yres = 800, .width = 48, .height = 80, - .output_format = 0, + .output_format = MSM_MDP_OUT_IF_FMT_RGB565, }; static struct msm_lcdc_platform_data mahimahi_lcdc_platform_data = { @@ -609,6 +631,13 @@ int __init mahimahi_init_panel(void) if (!machine_is_mahimahi()) return 0; + if (system_rev > 0xC0) { + /* CDMA version (except for EVT1) supports RGB666 */ + init_tablep = samsung_oled_rgb666_init_table; + init_table_sz = ARRAY_SIZE(samsung_oled_rgb666_init_table); + mahimahi_lcdc_fb_data.output_format = MSM_MDP_OUT_IF_FMT_RGB666; + } + ret = platform_device_register(&msm_device_mdp); if (ret != 0) return ret; From 67a639d30f1b30f58a45e193b7688aebdf61606f Mon Sep 17 00:00:00 2001 From: Abhijeet Dharmapurikar Date: Thu, 11 Mar 2010 18:13:40 -0800 Subject: [PATCH 0564/2556] [ARM] msm: swordfish: Add base board support for qsd8x50_ffa Change-Id: Ia660db145e6bebc937f1e9c1f57750be3e66d3a2 Signed-off-by: Abhijeet Dharmapurikar Signed-off-by: Bryan Huntsman --- arch/arm/mach-msm/Kconfig | 7 +++++++ arch/arm/mach-msm/board-swordfish.c | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 53bffe6bfe841..7642df213a282 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -219,6 +219,13 @@ config MACH_MAHIMAHI help Select this to support the Mahi-Mahi device +config MACH_QSD8X50_FFA + depends on ARCH_QSD8X50 + default y + bool "8x50-ffa" + help + Select this to support the 8x50 ffa device + config HTC_HEADSET tristate "HTC 2 Wire detection driver" default n diff --git a/arch/arm/mach-msm/board-swordfish.c b/arch/arm/mach-msm/board-swordfish.c index 3cbc23a72d22c..70fac49f42f8f 100644 --- a/arch/arm/mach-msm/board-swordfish.c +++ b/arch/arm/mach-msm/board-swordfish.c @@ -350,3 +350,16 @@ MACHINE_START(SWORDFISH, "Swordfish Board (QCT SURF8250)") .init_machine = swordfish_init, .timer = &msm_timer, MACHINE_END + +MACHINE_START(QSD8X50_FFA, "qsd8x50 FFA Board (QCT FFA8250)") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x20000100, + .fixup = swordfish_fixup, + .map_io = swordfish_map_io, + .init_irq = msm_init_irq, + .init_machine = swordfish_init, + .timer = &msm_timer, +MACHINE_END From ac35b7d0b60a5627511c17611e22ec5af2a6f9c6 Mon Sep 17 00:00:00 2001 From: Abhijeet Dharmapurikar Date: Thu, 11 Mar 2010 10:27:17 -0800 Subject: [PATCH 0565/2556] [ARM] msm: kgsl: fix compile error when MMU is not enabled kgsl_yamato_setstate should be static inline Change-Id: I7b4f5de07894b3fdb270d9068c18b53cafc54d14 Signed-off-by: Abhijeet Dharmapurikar Signed-off-by: Bryan Huntsman --- drivers/video/msm/gpu/kgsl/kgsl_device.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_device.h b/drivers/video/msm/gpu/kgsl/kgsl_device.h index eefb9e7a86e94..dcbf4db78574d 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_device.h +++ b/drivers/video/msm/gpu/kgsl/kgsl_device.h @@ -132,7 +132,7 @@ int kgsl_yamato_cleanup_pt(struct kgsl_device *device, #ifdef CONFIG_MSM_KGSL_MMU int kgsl_yamato_setstate(struct kgsl_device *device, uint32_t flags); #else -int kgsl_yamato_setstate(struct kgsl_device *device, uint32_t flags) +static inline int kgsl_yamato_setstate(struct kgsl_device *device, uint32_t flags) { return 0; } #endif From 5b5c153e2afc69a2e36f06dc9f2d498cf58bb04c Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Mon, 22 Mar 2010 08:54:25 -0700 Subject: [PATCH 0566/2556] qsd8k: audio: fix incorrect length of data field printed by DAL trace dump. Signed-off-by: Eric Laurent --- arch/arm/mach-msm/qdsp6/dal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/qdsp6/dal.c b/arch/arm/mach-msm/qdsp6/dal.c index 66c2fc6a31045..23aa1551f2e65 100644 --- a/arch/arm/mach-msm/qdsp6/dal.c +++ b/arch/arm/mach-msm/qdsp6/dal.c @@ -161,7 +161,7 @@ void dal_trace_dump(struct dal_client *c) for (n = c->tr_tail; n != c->tr_head; n = (n + 1) & TRACE_LOG_MASK) { dt = c->tr_log + n; - len = dt->hdr.length; + len = dt->hdr.length - sizeof(dt->hdr); if (len > TRACE_DATA_MAX) len = TRACE_DATA_MAX; dal_trace_print(&dt->hdr, dt->data, len, dt->timestamp); From 962c5158d30813e6e91e908aede9d80d599f4b56 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Tue, 23 Mar 2010 18:19:06 -0700 Subject: [PATCH 0567/2556] qsd8k: audio: make dsp_debug work in multi-crash situations If a second thread called q6audio_dsp_not_responding() while the daemon was ram-dumping the dsp, we'd panic the kernel before the ram dump completed. Park any additional threads so that we can ensure we finish the ramdump. Signed-off-by: Brian Swetland --- arch/arm/mach-msm/qdsp6/dsp_debug.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/qdsp6/dsp_debug.c b/arch/arm/mach-msm/qdsp6/dsp_debug.c index 70a038a9822b5..71c10ebef2b1b 100644 --- a/arch/arm/mach-msm/qdsp6/dsp_debug.c +++ b/arch/arm/mach-msm/qdsp6/dsp_debug.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "../proc_comm.h" @@ -29,14 +30,24 @@ static wait_queue_head_t dsp_wait; static int dsp_has_crashed; static int dsp_wait_count; +static atomic_t dsp_crash_count = ATOMIC_INIT(0); + void q6audio_dsp_not_responding(void) { + + if (atomic_add_return(1, &dsp_crash_count) != 1) { + pr_err("q6audio_dsp_not_responding() - parking additional crasher...\n"); + for (;;) + msleep(1000); + } if (dsp_wait_count) { dsp_has_crashed = 1; wake_up(&dsp_wait); while (dsp_has_crashed != 2) wait_event(dsp_wait, dsp_has_crashed == 2); + } else { + pr_err("q6audio_dsp_not_responding() - no waiter?\n"); } BUG(); } @@ -65,9 +76,10 @@ static ssize_t dsp_write(struct file *file, const char __user *buf, int res; dsp_wait_count++; res = wait_event_interruptible(dsp_wait, dsp_has_crashed); - dsp_wait_count--; - if (res < 0) + if (res < 0) { + dsp_wait_count--; return res; + } } #if defined(CONFIG_MACH_MAHIMAHI) /* assert DSP NMI */ From 6597bc90b3a4c36866f7e6643d850b5b28cdc199 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Tue, 23 Mar 2010 20:11:00 -0400 Subject: [PATCH 0568/2556] [ARM] msm: msm7k_udc: Remove dead request tracking logic. This code was broken as there was no safe way to handle requests being freed from completion routines. It was also unnecessary because the gadget framework does not allow freeing busy requests anyway. This fixes a race condition that could result in a use after free bug when requests are freed via a completion routine. This change also fixes updating msm_endpoint "last" pointer in msm72k_dequeue(). Signed-off-by: Mike Lockwood --- drivers/usb/gadget/msm72k_udc.c | 41 +++++++++------------------------ 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 78397b3146d6c..f3669507bcd92 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -83,7 +83,6 @@ struct msm_request { unsigned busy:1; unsigned live:1; unsigned alloced:1; - unsigned dead:1; dma_addr_t dma; dma_addr_t item_dma; @@ -340,16 +339,6 @@ struct usb_request *usb_ept_alloc_req(struct msm_endpoint *ept, return 0; } -static void do_free_req(struct usb_info *ui, struct msm_request *req) -{ - if (req->alloced) - kfree(req->req.buf); - - dma_pool_free(ui->pool, req->item, req->item_dma); - kfree(req); -} - - static void usb_ept_enable(struct msm_endpoint *ept, int yes, unsigned char ep_type) { @@ -801,9 +790,6 @@ static void handle_endpoint(struct usb_info *ui, unsigned bit) req->req.complete(&ept->ep, &req->req); spin_lock_irqsave(&ui->lock, flags); } - - if (req->dead) - do_free_req(ui, req); } spin_unlock_irqrestore(&ui->lock, flags); } @@ -876,8 +862,6 @@ static void flush_endpoint_sw(struct msm_endpoint *ept) req->req.complete(&ept->ep, &req->req); spin_lock_irqsave(&ui->lock, flags); } - if (req->dead) - do_free_req(ui, req); req = req->next; } spin_unlock_irqrestore(&ui->lock, flags); @@ -1496,18 +1480,14 @@ msm72k_free_request(struct usb_ep *_ep, struct usb_request *_req) struct msm_request *req = to_msm_request(_req); struct msm_endpoint *ept = to_msm_endpoint(_ep); struct usb_info *ui = ept->ui; - unsigned long flags; - int dead = 0; - spin_lock_irqsave(&ui->lock, flags); - /* defer freeing resources if request is still busy */ - if (req->busy) - dead = req->dead = 1; - spin_unlock_irqrestore(&ui->lock, flags); + /* request should not be busy */ + BUG_ON(req->busy); - /* if req->dead, then we will clean up when the request finishes */ - if (!dead) - do_free_req(ui, req); + if (req->alloced) + kfree(req->req.buf); + dma_pool_free(ui->pool, req->item, req->item_dma); + kfree(req); } static int @@ -1563,18 +1543,19 @@ static int msm72k_dequeue(struct usb_ep *_ep, struct usb_request *_req) req->req.complete(&ep->ep, &req->req); spin_lock_irqsave(&ui->lock, flags); } - if (req->dead) - do_free_req(ui, req); /* remove from linked list */ if (prev) prev->next = cur->next; else ep->req = cur->next; - prev = cur; + if (ep->last == cur) + ep->last = prev; /* break from loop */ cur = NULL; - } else + } else { + prev = cur; cur = cur->next; + } } spin_unlock_irqrestore(&ui->lock, flags); From 68c2c462b528d9d182b8ecd02284f8eb3e40a482 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 31 Mar 2010 14:24:31 -0700 Subject: [PATCH 0569/2556] msm_serial_hs: Use dedicated workqueue thread for tty RX. Previously the tty layer used the global workqueue, which can introduce high latency on the RX path if other global workqueue users block. Introduce a dedicated msm_serial_hs workqueue thread - managed by the msm_serial_hs drvier - to call tty_flip_buffer_push(). Set tty low_latency to 1 so that the tty layer does not use its own work queue. This patch fixes an issue where A2DP would skip when the screen turns on. Change-Id: I28253f8e5b0f577c7fc635ff5b10cf2abc042245 Signed-off-by: Nick Pelly --- drivers/tty/serial/msm_serial_hs.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 2ae173861f9d4..927f295191104 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -116,6 +117,7 @@ struct msm_hs_rx { unsigned char *buffer; struct dma_pool *pool; struct wake_lock wake_lock; + struct work_struct tty_work; }; /* optional RX GPIO IRQ low power wakeup */ @@ -162,6 +164,7 @@ static struct msm_hs_port q_uart_port[UARTDM_NR]; static struct platform_driver msm_serial_hs_platform_driver; static struct uart_driver msm_hs_driver; static struct uart_ops msm_hs_ops; +static struct workqueue_struct *msm_hs_workqueue; #define UARTDM_TO_MSM(uart_port) \ container_of((uart_port), struct msm_hs_port, uport) @@ -762,11 +765,19 @@ static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr, /* release wakelock in 500ms, not immediately, because higher layers * don't always take wakelocks when they should */ wake_lock_timeout(&msm_uport->rx.wake_lock, HZ / 2); - /* tty_flip_buffer_push() might call msm_hs_start(), so unlock */ spin_unlock_irqrestore(&uport->lock, flags); if (flush < FLUSH_DATA_INVALID) - tty_flip_buffer_push(tty); + queue_work(msm_hs_workqueue, &msm_uport->rx.tty_work); +} + +static void msm_hs_tty_flip_buffer_work(struct work_struct *work) +{ + struct msm_hs_port *msm_uport = + container_of(work, struct msm_hs_port, rx.tty_work); + struct tty_struct *tty = msm_uport->uport.state->port.tty; + + tty_flip_buffer_push(tty); } /* @@ -1130,13 +1141,12 @@ static irqreturn_t msm_hs_rx_wakeup_isr(int irq, void *dev) tty_insert_flip_char(tty, msm_uport->rx_wakeup.rx_to_inject, TTY_NORMAL); + queue_work(msm_hs_workqueue, &msm_uport->rx.tty_work); } } spin_unlock_irqrestore(&uport->lock, flags); - if (wakeup && msm_uport->rx_wakeup.inject_rx) - tty_flip_buffer_push(tty); return IRQ_HANDLED; } @@ -1164,6 +1174,10 @@ static int msm_hs_startup(struct uart_port *uport) tx->dma_base = dma_map_single(uport->dev, tx_buf->buf, UART_XMIT_SIZE, DMA_TO_DEVICE); + /* do not let tty layer execute RX in global workqueue, use a + * dedicated workqueue managed by this driver */ + uport->state->port.tty->low_latency = 1; + /* turn on uart clk */ ret = msm_hs_init_clk_locked(uport); if (unlikely(ret)) @@ -1320,6 +1334,8 @@ static int uartdm_init_port(struct uart_port *uport) sizeof(u32 *), DMA_TO_DEVICE); rx->xfer.cmdptr = DMOV_CMD_ADDR(rx->cmdptr_dmaaddr); + INIT_WORK(&rx->tty_work, msm_hs_tty_flip_buffer_work); + return 0; } @@ -1425,6 +1441,8 @@ static int __init msm_serial_hs_init(void) for (i = 0; i < UARTDM_NR; i++) q_uart_port[i].uport.type = PORT_UNKNOWN; + msm_hs_workqueue = create_singlethread_workqueue("msm_serial_hs"); + ret = uart_register_driver(&msm_hs_driver); if (unlikely(ret)) { printk(KERN_ERR "%s failed to load\n", __FUNCTION__); @@ -1482,6 +1500,9 @@ static void msm_hs_shutdown(struct uart_port *uport) UART_XMIT_SIZE, DMA_TO_DEVICE); spin_unlock_irqrestore(&uport->lock, flags); + + if (cancel_work_sync(&msm_uport->rx.tty_work)) + msm_hs_tty_flip_buffer_work(&msm_uport->rx.tty_work); } static void __exit msm_serial_hs_exit(void) @@ -1489,6 +1510,7 @@ static void __exit msm_serial_hs_exit(void) printk(KERN_INFO "msm_serial_hs module removed\n"); platform_driver_unregister(&msm_serial_hs_platform_driver); uart_unregister_driver(&msm_hs_driver); + destroy_workqueue(msm_hs_workqueue); } static struct platform_driver msm_serial_hs_platform_driver = { From 6bec4650e248b69d1ab67b31ae94a22ac0a814d0 Mon Sep 17 00:00:00 2001 From: Eric Olsen Date: Fri, 2 Apr 2010 16:06:46 -0700 Subject: [PATCH 0570/2556] [ARM] msm: mahimahi: Set initial state of microp LEDs. Signed-off-by: Eric Olsen --- arch/arm/mach-msm/board-mahimahi-microp.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/mach-msm/board-mahimahi-microp.c b/arch/arm/mach-msm/board-mahimahi-microp.c index 5c81412fa56d8..da30672d26a16 100644 --- a/arch/arm/mach-msm/board-mahimahi-microp.c +++ b/arch/arm/mach-msm/board-mahimahi-microp.c @@ -1766,6 +1766,7 @@ static int microp_function_initialize(struct i2c_client *client) uint16_t stat, interrupts = 0; int i; int ret; + struct led_classdev *led_cdev; cdata = i2c_get_clientdata(client); @@ -1832,6 +1833,12 @@ static int microp_function_initialize(struct i2c_client *client) /* SD Card */ interrupts |= IRQ_SDCARD; + /* set LED initial state */ + for (i = 0; i < BLUE_LED; i++) { + led_cdev = &cdata->leds[i].ldev; + microp_i2c_write_led_mode(client, led_cdev, 0, 0xffff); + } + /* enable the interrupts */ ret = microp_interrupt_enable(client, interrupts); if (ret < 0) { From a4aa5f1f66ae300343aa03e8ad0ee619701dffb4 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 30 Mar 2010 09:50:45 -0700 Subject: [PATCH 0571/2556] [ARM] msm: sapphire: allocate gpio TP_LS_EN before using it Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-sapphire.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index d25fe9ef50f2f..9ceb8cf51a200 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -258,13 +258,10 @@ struct platform_device sapphire_reset_keys_device = { .dev.platform_data = &sapphire_reset_keys_pdata, }; +static int gpio_tp_ls_en = SAPPHIRE_TP_LS_EN; + static int sapphire_ts_power(int on) { - int gpio_tp_ls_en = SAPPHIRE_TP_LS_EN; - - if (is_12pin_camera()) - gpio_tp_ls_en = SAPPHIRE20_TP_LS_EN; - if (on) { sapphire_gpio_write(NULL, SAPPHIRE_GPIO_TP_EN, 1); /* touchscreen must be powered before we enable i2c pullup */ @@ -1188,6 +1185,10 @@ static void __init sapphire_init(void) if(system_rev != 0x80) sapphire_search_button_info.keymap = sapphire_search_button_v1; + if (is_12pin_camera()) + gpio_tp_ls_en = SAPPHIRE20_TP_LS_EN; + gpio_request(gpio_tp_ls_en, "tp_ls_en"); + i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); platform_add_devices(devices, ARRAY_SIZE(devices)); } From 49ef152a04bc95126df177567a7fc142f1c2f0b3 Mon Sep 17 00:00:00 2001 From: Andy Yeh Date: Tue, 6 Apr 2010 12:33:03 -0700 Subject: [PATCH 0572/2556] [ARM] msm: camera: s5k3e2fx: revised fix for greenish snapshot in low light The issue affects the Samsung s5k3e2fx camera sensor. The fix consists of: -- stream-on snapshot after writing gain/line -- removing parameter hold/release from snapshot session Signed-off-by: Iliyan Malchev --- drivers/media/video/msm/s5k3e2fx.c | 51 ++++++++++++++---------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/drivers/media/video/msm/s5k3e2fx.c b/drivers/media/video/msm/s5k3e2fx.c index bc0b75705d523..2776405bbaffa 100644 --- a/drivers/media/video/msm/s5k3e2fx.c +++ b/drivers/media/video/msm/s5k3e2fx.c @@ -2095,11 +2095,11 @@ static int s5k3e2fx_setting(enum msm_s_reg_update rupdate, {0x3063, 0x16}, } }; - - rc = s5k3e2fx_i2c_write_b - (s5k3e2fx_client->addr, - S5K3E2FX_REG_MODE_SELECT, - S5K3E2FX_MODE_SELECT_SW_STANDBY); +/* solve greenish: hold for both */ + rc = s5k3e2fx_i2c_write_b( + s5k3e2fx_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_HOLD); if (rc < 0) return rc; @@ -2152,14 +2152,23 @@ static int s5k3e2fx_setting(enum msm_s_reg_update rupdate, return rc; } - if (rt == S_RES_PREVIEW) { - rc = s5k3e2fx_i2c_write_b - (s5k3e2fx_client->addr, - S5K3E2FX_REG_MODE_SELECT, - S5K3E2FX_MODE_SELECT_STREAM); + /* solve greenish: only release for preview */ + if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) + { + rc = s5k3e2fx_i2c_write_b( + s5k3e2fx_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); if (rc < 0) return rc; } + + rc = s5k3e2fx_i2c_write_b + (s5k3e2fx_client->addr, + S5K3E2FX_REG_MODE_SELECT, + S5K3E2FX_MODE_SELECT_STREAM); + if (rc < 0) + return rc; } break; /* UPDATE_PERIODIC */ } @@ -2556,7 +2565,7 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) else ll_ratio = 0x400; -/* AEC_FLASHING */ +/* solve greenish: only release for preview */ if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) { rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, REG_GROUPED_PARAMETER_HOLD, @@ -2617,7 +2626,7 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) tbl[1].bdata = intg_t_lsb; rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); - if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) { +/* solve greenish: release for both */ rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, REG_GROUPED_PARAMETER_HOLD, GROUPED_PARAMETER_UPDATE); @@ -2626,7 +2635,6 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) __LINE__); return rc; } - } write_gain_done: return rc; @@ -2634,21 +2642,10 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) static int s5k3e2fx_set_pict_exp_gain(uint16_t gain, uint32_t line) { - int rc = 0; - - CDBG("Line:%d s5k3e2fx_set_pict_exp_gain \n", __LINE__); + pr_info("s5k3e2fx_set_pict_exp_gain gain %d line %d\n", + gain, line); - rc = s5k3e2fx_write_exp_gain(gain, line); - if (rc < 0) - return rc; - -/* Solve EVT5 greenish lowlight*/ - rc = s5k3e2fx_i2c_write_b - (s5k3e2fx_client->addr, - S5K3E2FX_REG_MODE_SELECT, - S5K3E2FX_MODE_SELECT_STREAM); - - return rc; + return s5k3e2fx_write_exp_gain(gain, line); } static int s5k3e2fx_video_config(int mode, int res) From 80680fd3c6cfcf2171e653168a6fb34a027b6ed5 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 6 Apr 2010 16:23:23 -0700 Subject: [PATCH 0573/2556] [ARM] msm: camera: clean up the 7k VFE driver -- replace unnecessary kmallocs with stack variables -- clean up error handling, remove confusing labels -- remove unecessary local variables Signed-off-by: Iliyan Malchev --- drivers/media/video/msm/msm_vfe7x.c | 226 +++++++++++----------------- 1 file changed, 88 insertions(+), 138 deletions(-) diff --git a/drivers/media/video/msm/msm_vfe7x.c b/drivers/media/video/msm/msm_vfe7x.c index ab3123b720d0f..7ea58f5e51a09 100644 --- a/drivers/media/video/msm/msm_vfe7x.c +++ b/drivers/media/video/msm/msm_vfe7x.c @@ -262,7 +262,7 @@ static int vfe_7x_init(struct msm_vfe_callback *presp, if (presp && presp->vfe_resp) resp = presp; else - return -EFAULT; + return -EIO; /* Bring up all the required GPIOs and Clocks */ rc = msm_camio_enable(dev); @@ -364,77 +364,60 @@ static int vfe_7x_config_axi(int mode, struct axidata *ad, struct axiout *ao) static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) { - struct msm_pmem_region *regptr; - unsigned char buf[256]; + int rc = 0; + int i; - struct vfe_stats_ack sack; - struct axidata *axid; - uint32_t i; + struct msm_pmem_region *regptr = NULL; - struct vfe_stats_we_cfg *scfg = NULL; - struct vfe_stats_af_cfg *sfcfg = NULL; + struct vfe_stats_ack sack; + struct axidata *axid = NULL; - struct axiout *axio = NULL; - void *cmd_data = NULL; + struct axiout axio; void *cmd_data_alloc = NULL; - long rc = 0; - struct msm_vfe_command_7k *vfecmd; - - vfecmd = kmalloc(sizeof(struct msm_vfe_command_7k), GFP_ATOMIC); - if (!vfecmd) { - pr_err("vfecmd alloc failed!\n"); - return -ENOMEM; - } + struct msm_vfe_command_7k vfecmd; if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE && cmd->cmd_type != CMD_STATS_BUF_RELEASE && cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) { - if (copy_from_user(vfecmd, + if (copy_from_user(&vfecmd, (void __user *)(cmd->value), sizeof(struct msm_vfe_command_7k))) { rc = -EFAULT; - goto config_failure; + goto config_error; } } switch (cmd->cmd_type) { case CMD_STATS_AEC_AWB_ENABLE: case CMD_STATS_AXI_CFG:{ + struct vfe_stats_we_cfg scfg; axid = data; if (!axid) { rc = -EFAULT; - goto config_failure; + goto config_error; } - scfg = - kmalloc(sizeof(struct vfe_stats_we_cfg), - GFP_ATOMIC); - if (!scfg) { - rc = -ENOMEM; - goto config_failure; - } - - if (vfecmd->length != sizeof(typeof(*scfg))) { + if (vfecmd.length != sizeof(typeof(scfg))) { rc = -EIO; pr_err ("msm_camera: %s: cmd %d: "\ "user-space data size %d "\ "!= kernel data size %d\n", __func__, - cmd->cmd_type, vfecmd->length, - sizeof(typeof(*scfg))); - goto config_failure; + cmd->cmd_type, vfecmd.length, + sizeof(typeof(scfg))); + goto config_error; } - if (copy_from_user(scfg, - (void __user *)(vfecmd->value), - vfecmd->length)) { + if (copy_from_user(&scfg, + (void __user *)(vfecmd.value), + vfecmd.length)) { rc = -EFAULT; - goto config_done; + goto config_error; } CDBG("STATS_ENABLE: bufnum = %d, enabling = %d\n", - axid->bufnum1, scfg->wb_expstatsenable); + axid->bufnum1, scfg.wb_expstatsenable); if (axid->bufnum1 > 0) { regptr = axid->region; @@ -444,57 +427,48 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) CDBG("STATS_ENABLE, phy = 0x%lx\n", regptr->paddr); - scfg->wb_expstatoutputbuffer[i] = + scfg.wb_expstatoutputbuffer[i] = (void *)regptr->paddr; regptr++; } - cmd_data = scfg; + vfecmd.value = &scfg; } else { rc = -EINVAL; - goto config_done; + goto config_error; } } break; case CMD_STATS_AF_ENABLE: case CMD_STATS_AF_AXI_CFG:{ + struct vfe_stats_af_cfg sfcfg; axid = data; if (!axid) { rc = -EFAULT; - goto config_failure; - } - - sfcfg = - kmalloc(sizeof(struct vfe_stats_af_cfg), - GFP_ATOMIC); - - if (!sfcfg) { - rc = -ENOMEM; - goto config_failure; + goto config_error; } - if (vfecmd->length > sizeof(typeof(*sfcfg))) { + if (vfecmd.length > sizeof(typeof(sfcfg))) { pr_err ("msm_camera: %s: cmd %d: user-space "\ "data %d exceeds kernel buffer %d\n", - __func__, cmd->cmd_type, vfecmd->length, - sizeof(typeof(*sfcfg))); + __func__, cmd->cmd_type, vfecmd.length, + sizeof(typeof(sfcfg))); rc = -EIO; - goto config_failure; + goto config_error; } - if (copy_from_user(sfcfg, - (void __user *)(vfecmd->value), - vfecmd->length)) { - + if (copy_from_user(&sfcfg, + (void __user *)(vfecmd.value), + vfecmd.length)) { rc = -EFAULT; - goto config_done; + goto config_error; } CDBG("AF_ENABLE: bufnum = %d, enabling = %d\n", - axid->bufnum1, sfcfg->af_enable); + axid->bufnum1, sfcfg.af_enable); if (axid->bufnum1 > 0) { regptr = axid->region; @@ -504,17 +478,17 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) CDBG("STATS_ENABLE, phy = 0x%lx\n", regptr->paddr); - sfcfg->af_outbuf[i] = + sfcfg.af_outbuf[i] = (void *)regptr->paddr; regptr++; } - cmd_data = sfcfg; + vfecmd.value = &sfcfg; } else { rc = -EINVAL; - goto config_done; + goto config_error; } } break; @@ -525,7 +499,7 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) struct vfe_outputack fack; if (!data) { rc = -EFAULT; - goto config_failure; + goto config_error; } b = (struct msm_frame *)(cmd->value); @@ -538,9 +512,9 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) fack.output2newcbcrbufferaddress = (void *)(p + b->cbcr_off); - vfecmd->queue = QDSP_CMDQUEUE; - vfecmd->length = sizeof(struct vfe_outputack); - cmd_data = &fack; + vfecmd.queue = QDSP_CMDQUEUE; + vfecmd.length = sizeof(struct vfe_outputack); + vfecmd.value = &fack; } break; @@ -551,15 +525,15 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) CDBG("vfe_7x_config: CMD_STATS_BUF_RELEASE\n"); if (!data) { rc = -EFAULT; - goto config_failure; + goto config_error; } sack.header = STATS_WE_ACK; sack.bufaddr = (void *)*(uint32_t *) data; - vfecmd->queue = QDSP_CMDQUEUE; - vfecmd->length = sizeof(struct vfe_stats_ack); - cmd_data = &sack; + vfecmd.queue = QDSP_CMDQUEUE; + vfecmd.length = sizeof(struct vfe_stats_ack); + vfecmd.value = &sack; } break; @@ -567,41 +541,41 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) CDBG("vfe_7x_config: CMD_STATS_AF_BUF_RELEASE\n"); if (!data) { rc = -EFAULT; - goto config_failure; + goto config_error; } sack.header = STATS_AF_ACK; sack.bufaddr = (void *)*(uint32_t *) data; - vfecmd->queue = QDSP_CMDQUEUE; - vfecmd->length = sizeof(struct vfe_stats_ack); - cmd_data = &sack; + vfecmd.queue = QDSP_CMDQUEUE; + vfecmd.length = sizeof(struct vfe_stats_ack); + vfecmd.value = &sack; } break; case CMD_GENERAL: case CMD_STATS_DISABLE:{ - if (vfecmd->length > sizeof(buf)) { - cmd_data_alloc = - cmd_data = - kmalloc(vfecmd->length, GFP_ATOMIC); - if (!cmd_data) { + uint8_t buf[256]; + void *tmp = buf; + if (vfecmd.length > sizeof(buf)) { + cmd_data_alloc = tmp = + kmalloc(vfecmd.length, GFP_ATOMIC); + if (!cmd_data_alloc) { rc = -ENOMEM; - goto config_failure; + goto config_error; } - } else - cmd_data = buf; - - if (copy_from_user(cmd_data, - (void __user *)(vfecmd->value), - vfecmd->length)) { + } + if (copy_from_user(tmp, + (void __user *)(vfecmd.value), + vfecmd.length)) { rc = -EFAULT; - goto config_done; + goto config_error; } + vfecmd.value = tmp; - if (vfecmd->queue == QDSP_CMDQUEUE) { - switch (*(uint32_t *) cmd_data) { + if (vfecmd.queue == QDSP_CMDQUEUE) { + switch (*(uint32_t *) vfecmd.value) { case VFE_RESET_CMD: msm_camio_vfe_blk_reset(); msm_camio_camif_pad_reg_reset_2(); @@ -628,24 +602,18 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) axid = data; if (!axid) { rc = -EFAULT; - goto config_failure; - } - - axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); - if (!axio) { - rc = -ENOMEM; - goto config_failure; + goto config_error; } - if (copy_from_user(axio, (void *)(vfecmd->value), - sizeof(struct axiout))) { + if (copy_from_user(&axio, (void *)(vfecmd.value), + sizeof(axio))) { rc = -EFAULT; - goto config_done; + goto config_error; } - vfe_7x_config_axi(OUTPUT_1, axid, axio); + vfe_7x_config_axi(OUTPUT_1, axid, &axio); - cmd_data = axio; + vfecmd.value = &axio; } break; @@ -654,23 +622,17 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) axid = data; if (!axid) { rc = -EFAULT; - goto config_failure; + goto config_error; } - axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); - if (!axio) { - rc = -ENOMEM; - goto config_failure; - } - - if (copy_from_user(axio, (void __user *)(vfecmd->value), - sizeof(struct axiout))) { + if (copy_from_user(&axio, (void __user *)(vfecmd.value), + sizeof(axio))) { rc = -EFAULT; - goto config_done; + goto config_error; } - vfe_7x_config_axi(OUTPUT_2, axid, axio); - cmd_data = axio; + vfe_7x_config_axi(OUTPUT_2, axid, &axio); + vfecmd.value = &axio; } break; @@ -678,24 +640,18 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) axid = data; if (!axid) { rc = -EFAULT; - goto config_failure; + goto config_error; } - axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); - if (!axio) { - rc = -ENOMEM; - goto config_failure; - } - - if (copy_from_user(axio, (void __user *)(vfecmd->value), - sizeof(struct axiout))) { + if (copy_from_user(&axio, (void __user *)(vfecmd.value), + sizeof(axio))) { rc = -EFAULT; - goto config_done; + goto config_error; } - vfe_7x_config_axi(OUTPUT_1_AND_2, axid, axio); + vfe_7x_config_axi(OUTPUT_1_AND_2, axid, &axio); - cmd_data = axio; + vfecmd.value = &axio; } break; @@ -704,20 +660,14 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) } /* switch */ if (vfestopped) - goto config_done; + goto config_error; config_send: - CDBG("send adsp command = %d\n", *(uint32_t *) cmd_data); - rc = msm_adsp_write(vfe_mod, vfecmd->queue, cmd_data, vfecmd->length); - -config_done: - if (cmd_data_alloc != NULL) - kfree(cmd_data_alloc); + CDBG("send adsp command = %d\n", *(uint32_t *)vfecmd.value); + rc = msm_adsp_write(vfe_mod, vfecmd.queue, vfecmd.value, vfecmd.length); -config_failure: - kfree(scfg); - kfree(axio); - kfree(vfecmd); +config_error: + kfree(cmd_data_alloc); return rc; } From 57e16c55de13b468303924f20f6ff3d98ecca5a0 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 6 Apr 2010 17:10:36 -0700 Subject: [PATCH 0574/2556] [ARM] msm: camera: request EBI1 to run at 128MHz when camera is in use This prevents the AXI bus frequency from falling below 128MHz when the VFE is in use. The VFE needs the AXI at at least 128MHz. Signed-off-by: Iliyan Malchev --- drivers/media/video/msm/msm_vfe7x.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/msm/msm_vfe7x.c b/drivers/media/video/msm/msm_vfe7x.c index 7ea58f5e51a09..9354ac52cc57c 100644 --- a/drivers/media/video/msm/msm_vfe7x.c +++ b/drivers/media/video/msm/msm_vfe7x.c @@ -21,10 +21,11 @@ #include #include #include -#include #include #include #include +#include +#include #include "msm_vfe7x.h" #define QDSP_CMDQUEUE QDSP_vfeCommandQueue @@ -54,6 +55,9 @@ static uint8_t vfestopped; static struct stop_event stopevent; +static struct clk *ebi1_clk; +static const char *const ebi1_clk_name = "ebi1_clk"; + static void vfe_7x_convert(struct msm_vfe_phy_info *pinfo, enum vfe_resp_msg type, void *data, void **ext, int *elen) @@ -246,6 +250,12 @@ static void vfe_7x_release(struct platform_device *pdev) msm_camio_disable(pdev); + if (ebi1_clk) { + clk_set_rate(ebi1_clk, 0); + clk_put(ebi1_clk); + ebi1_clk = 0; + } + kfree(extdata); extdata = 0; } @@ -264,6 +274,19 @@ static int vfe_7x_init(struct msm_vfe_callback *presp, else return -EIO; + ebi1_clk = clk_get(NULL, ebi1_clk_name); + if (!ebi1_clk) { + pr_err("%s: could not get %s\n", __func__, ebi1_clk_name); + return -EIO; + } + + rc = clk_set_rate(ebi1_clk, 128000000); + if (rc < 0) { + pr_err("%s: clk_set_rate(%s) failed: %d\n", __func__, + ebi1_clk_name, rc); + return rc; + } + /* Bring up all the required GPIOs and Clocks */ rc = msm_camio_enable(dev); if (rc < 0) From 32149cb4fea1c4334ff22af2a7fb47f03a01b3b7 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 7 Apr 2010 00:35:04 -0700 Subject: [PATCH 0575/2556] [ARM] msm: kgsl: bug if we detect a gpu hang Change-Id: I01c5b9fdabab33cd0a4354e391c5e360e362ee29 Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl_yamato.c | 25 ++++++++++-------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c index f29a9d1e9afd0..a396264e4fe48 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c @@ -811,13 +811,8 @@ int kgsl_yamato_idle(struct kgsl_device *device, unsigned int timeout) GSL_RB_GET_READPTR(rb, &rb->rptr); } while (rb->rptr != rb->wptr && idle_count < IDLE_COUNT_MAX); - if (idle_count == IDLE_COUNT_MAX) { - KGSL_DRV_ERR("spun too long waiting for RB to idle\n"); - status = -EINVAL; - kgsl_ringbuffer_dump(rb); - kgsl_mmu_debug(&device->mmu, &mmu_dbg); - goto done; - } + if (idle_count == IDLE_COUNT_MAX) + goto err; } /* now, wait for the GPU to finish its operations */ for (idle_count = 0; idle_count < IDLE_COUNT_MAX; idle_count++) { @@ -825,17 +820,17 @@ int kgsl_yamato_idle(struct kgsl_device *device, unsigned int timeout) if (rbbm_status == 0x110) { status = 0; - break; + goto done; } } - if (idle_count == IDLE_COUNT_MAX) { - KGSL_DRV_ERR("spun too long waiting for RBBM status to idle\n"); - status = -EINVAL; - kgsl_ringbuffer_dump(rb); - kgsl_mmu_debug(&device->mmu, &mmu_dbg); - goto done; - } +err: + KGSL_DRV_ERR("spun too long waiting for RB to idle\n"); + kgsl_register_dump(device); + kgsl_ringbuffer_dump(rb); + kgsl_mmu_debug(&device->mmu, &mmu_dbg); + BUG(); + done: KGSL_DRV_VDBG("return %d\n", status); From 8fe08414e8e180ca6b548a4de93a63e31d30f7ef Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 7 Apr 2010 21:46:31 -0700 Subject: [PATCH 0576/2556] [ARM] msm: hw3d: block new client while suspended instead of failing Also fixes a potential issue where if there were multiple clients trying to open the device, a random process would get it. Now, the last requester always gets it. Change-Id: I5ec36edd368f7a2979c64740b63dd807cc2fa857 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/hw3d.c | 87 +++++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 20 deletions(-) diff --git a/arch/arm/mach-msm/hw3d.c b/arch/arm/mach-msm/hw3d.c index ac09ea06005a5..2cd50140804b5 100644 --- a/arch/arm/mach-msm/hw3d.c +++ b/arch/arm/mach-msm/hw3d.c @@ -80,6 +80,7 @@ struct hw3d_info { struct timer_list revoke_timer; wait_queue_head_t revoke_wq; wait_queue_head_t revoke_done_wq; + unsigned int waiter_cnt; spinlock_t lock; @@ -376,6 +377,64 @@ static int hw3d_flush(struct file *filp, fl_owner_t id) return 0; } +static int should_wakeup(struct hw3d_info *info, unsigned int cnt) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&info->lock, flags); + ret = (cnt != info->waiter_cnt) || + (!info->suspending && !info->client_file); + spin_unlock_irqrestore(&info->lock, flags); + + return ret; +} + +static int locked_open_wait_for_gpu(struct hw3d_info *info, + unsigned long *flags) +{ + unsigned int my_cnt; + int ret; + + my_cnt = ++info->waiter_cnt; + pr_debug("%s: wait_for_open %d\n", __func__, my_cnt); + + /* in case there are other waiters, wake and release them. */ + wake_up(&info->revoke_done_wq); + + if (info->suspending) + pr_info("%s: suspended, waiting (%d %d)\n", __func__, + current->group_leader->pid, current->pid); + if (info->client_file) + pr_info("%s: has client, waiting (%d %d)\n", __func__, + current->group_leader->pid, current->pid); + spin_unlock_irqrestore(&info->lock, *flags); + + ret = wait_event_interruptible(info->revoke_done_wq, + should_wakeup(info, my_cnt)); + + spin_lock_irqsave(&info->lock, *flags); + pr_debug("%s: woke up (%d %d %p)\n", __func__, + info->waiter_cnt, info->suspending, info->client_file); + + if (ret >= 0) { + if (my_cnt != info->waiter_cnt) { + pr_info("%s: someone else asked for gpu after us %d:%d" + "(%d %d)\n", __func__, + current->group_leader->pid, current->pid, + my_cnt, info->waiter_cnt); + ret = -EBUSY; + } else if (info->suspending || info->client_file) { + pr_err("%s: couldn't get the gpu for %d:%d (%d %p)\n", + __func__, current->group_leader->pid, + current->pid, info->suspending, + info->client_file); + ret = -EBUSY; + } else + ret = 0; + } + return ret; +} static int hw3d_open(struct inode *inode, struct file *file) { @@ -406,31 +465,18 @@ static int hw3d_open(struct inode *inode, struct file *file) return 0; spin_lock_irqsave(&info->lock, flags); - if (info->suspending) { - pr_warning("%s: can't open hw3d while suspending\n", __func__); - ret = -EPERM; - spin_unlock_irqrestore(&info->lock, flags); - goto err; - } - if (info->client_file) { pr_debug("hw3d: have client_file, need revoke\n"); locked_hw3d_revoke(info); + } + + ret = locked_open_wait_for_gpu(info, &flags); + if (ret < 0) { spin_unlock_irqrestore(&info->lock, flags); - ret = wait_event_interruptible(info->revoke_done_wq, - !info->client_file); - if (ret < 0) - goto err; - spin_lock_irqsave(&info->lock, flags); - if (info->client_file) { - /* between is waking up and grabbing the lock, - * someone else tried to open the gpu, and got here - * first, let them have it. */ - spin_unlock_irqrestore(&info->lock, flags); - ret = -EBUSY; - goto err; - } + goto err; } + pr_info("%s: pid %d tid %d got gpu\n", __func__, + current->group_leader->pid, current->pid); info->client_file = file; get_task_struct(current->group_leader); @@ -647,6 +693,7 @@ static void hw3d_late_resume(struct early_suspend *h) if (info->suspending) pr_info("%s: resuming\n", __func__); info->suspending = 0; + wake_up(&info->revoke_done_wq); spin_unlock_irqrestore(&info->lock, flags); } From 07684584563699c65a36bbfd1cc136dcaf9e8291 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 7 Apr 2010 21:52:06 -0700 Subject: [PATCH 0577/2556] [ARM] msm: hw3d: bump the revoke timeout to 2 seconds Change-Id: Id3d5226d85f49bdd2c580d25e58eab72db4a4c0d Signed-off-by: Dima Zavin --- arch/arm/mach-msm/hw3d.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/hw3d.c b/arch/arm/mach-msm/hw3d.c index 2cd50140804b5..fe53951e64084 100644 --- a/arch/arm/mach-msm/hw3d.c +++ b/arch/arm/mach-msm/hw3d.c @@ -273,7 +273,7 @@ static void do_force_revoke(struct hw3d_info *info) spin_unlock_irqrestore(&info->lock, flags); } -#define REVOKE_TIMEOUT (HZ / 2) +#define REVOKE_TIMEOUT (2 * HZ) static void locked_hw3d_revoke(struct hw3d_info *info) { /* force us to wait to suspend until the revoke is done. If the From 2d003f87d7e3f06f6e61a4ae0eb2e5c326dbece7 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 8 Apr 2010 00:09:00 -0700 Subject: [PATCH 0578/2556] [ARM] msm: mdp: Work around hardware bugs in mdp 3.1 Adds two workarounds for MDP 3.1 bugs. 1. When blitting to a destination in a 32bpp buffer with width%8==6, split the blit into two vertical stripes 2. When blitting to a destination buffer with height%32==1 or 3, split the blit into two horizontal stripes. Change-Id: I4368f6f3cf8ce3046ac83b22fc11693608ccfb51 Signed-off-by: Colin Cross --- drivers/video/msm/mdp.c | 101 ++++++++++++++----- drivers/video/msm/mdp_hw.h | 1 + drivers/video/msm/mdp_ppp.c | 19 +--- drivers/video/msm/mdp_ppp.h | 12 +++ drivers/video/msm/mdp_ppp31.c | 181 ++++++++++++++++++++++++++++++++++ include/linux/msm_mdp.h | 15 +++ 6 files changed, 286 insertions(+), 43 deletions(-) diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 8e81f87738031..cdb57405c3ebc 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -425,9 +425,36 @@ int mdp_set_output_format(struct mdp_device *mdp_dev, int bpp) return 0; } +static void dump_req(struct mdp_blit_req *req, + unsigned long src_start, unsigned long src_len, + unsigned long dst_start, unsigned long dst_len) { + pr_err("flags: 0x%x\n", req->flags); + pr_err("src_start: 0x%08lx\n", src_start); + pr_err("src_len: 0x%08lx\n", src_len); + pr_err("src.offset: 0x%x\n", req->src.offset); + pr_err("src.format: 0x%x\n", req->src.format); + pr_err("src.width: %d\n", req->src.width); + pr_err("src.height: %d\n", req->src.height); + pr_err("src_rect.x: %d\n", req->src_rect.x); + pr_err("src_rect.y: %d\n", req->src_rect.y); + pr_err("src_rect.w: %d\n", req->src_rect.w); + pr_err("src_rect.h: %d\n", req->src_rect.h); + + pr_err("dst_start: 0x%08lx\n", dst_start); + pr_err("dst_len: 0x%08lx\n", dst_len); + pr_err("dst.offset: 0x%x\n", req->dst.offset); + pr_err("dst.format: 0x%x\n", req->dst.format); + pr_err("dst.width: %d\n", req->dst.width); + pr_err("dst.height: %d\n", req->dst.height); + pr_err("dst_rect.x: %d\n", req->dst_rect.x); + pr_err("dst_rect.y: %d\n", req->dst_rect.y); + pr_err("dst_rect.w: %d\n", req->dst_rect.w); + pr_err("dst_rect.h: %d\n", req->dst_rect.h); +} + int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, - struct file *src_file, unsigned long src_start, unsigned long src_len, - struct file *dst_file, unsigned long dst_start, unsigned long dst_len) + struct file *src_file, unsigned long src_start, unsigned long src_len, + struct file *dst_file, unsigned long dst_start, unsigned long dst_len) { int ret; enable_mdp_irq(mdp, DL0_ROI_DONE); @@ -441,30 +468,11 @@ int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, ret = mdp_ppp_wait(mdp); if (unlikely(ret)) { printk(KERN_ERR "%s: failed!\n", __func__); - - printk(KERN_ERR "flags: 0x%x\n", req->flags); - printk(KERN_ERR "src_start: 0x%08lx\n", src_start); - printk(KERN_ERR "src_len: 0x%08lx\n", src_len); - printk(KERN_ERR "src.offset: 0x%x\n", req->src.offset); - printk(KERN_ERR "src.format: 0x%x\n", req->src.format); - printk(KERN_ERR "src.width: %d\n", req->src.width); - printk(KERN_ERR "src.height: %d\n", req->src.height); - printk(KERN_ERR "src_rect.x: %d\n", req->src_rect.x); - printk(KERN_ERR "src_rect.y: %d\n", req->src_rect.y); - printk(KERN_ERR "src_rect.w: %d\n", req->src_rect.w); - printk(KERN_ERR "src_rect.h: %d\n", req->src_rect.h); - - printk(KERN_ERR "dst_start: 0x%08lx\n", dst_start); - printk(KERN_ERR "dst_len: 0x%08lx\n", dst_len); - printk(KERN_ERR "dst.offset: 0x%x\n", req->dst.offset); - printk(KERN_ERR "dst.format: 0x%x\n", req->dst.format); - printk(KERN_ERR "dst.width: %d\n", req->dst.width); - printk(KERN_ERR "dst.height: %d\n", req->dst.height); - printk(KERN_ERR "dst_rect.x: %d\n", req->dst_rect.x); - printk(KERN_ERR "dst_rect.y: %d\n", req->dst_rect.y); - printk(KERN_ERR "dst_rect.w: %d\n", req->dst_rect.w); - printk(KERN_ERR "dst_rect.h: %d\n", req->dst_rect.h); - + if (mdp->req) + pr_err("original request:\n"); + dump_req(mdp->req, src_start, src_len, dst_start, dst_len); + pr_err("dead request:\n"); + dump_req(mdp->req, src_start, src_len, dst_start, dst_len); BUG(); return ret; } @@ -479,6 +487,28 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); struct file *src_file = 0, *dst_file = 0; +#ifdef CONFIG_MSM_MDP31 + if (req->flags & MDP_ROT_90) { + if (unlikely(((req->dst_rect.h == 1) && + ((req->src_rect.w != 1) || + (req->dst_rect.w != req->src_rect.h))) || + ((req->dst_rect.w == 1) && ((req->src_rect.h != 1) || + (req->dst_rect.h != req->src_rect.w))))) { + pr_err("mpd_ppp: error scaling when size is 1!\n"); + return -EINVAL; + } + } else { + if (unlikely(((req->dst_rect.w == 1) && + ((req->src_rect.w != 1) || + (req->dst_rect.h != req->src_rect.h))) || + ((req->dst_rect.h == 1) && ((req->src_rect.h != 1) || + (req->dst_rect.h != req->src_rect.h))))) { + pr_err("mpd_ppp: error scaling when size is 1!\n"); + return -EINVAL; + } + } +#endif + /* WORKAROUND FOR HARDWARE BUG IN BG TILE FETCH */ if (unlikely(req->src_rect.h == 0 || req->src_rect.w == 0)) { @@ -507,6 +537,7 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, /* transp_masking unimplemented */ req->transp_mask = MDP_TRANSP_NOP; + mdp->req = req; #ifndef CONFIG_MSM_MDP31 if (unlikely((req->transp_mask != MDP_TRANSP_NOP || req->alpha != MDP_ALPHA_NOP || @@ -532,6 +563,24 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, req->src_rect.w = remainder*req->src_rect.w / req->dst_rect.h; req->dst_rect.h = remainder; } +#else + /* Workarounds for MDP 3.1 hardware bugs */ + if (unlikely((msm_bytes_per_pixel[req->dst.format] == 4) && + (req->dst_rect.w != 1) && + (((req->dst_rect.w % 8) == 6) || + ((req->dst_rect.w % 32) == 3) || + ((req->dst_rect.w % 32) == 1)))) { + ret = mdp_ppp_blit_split_width(mdp, req, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); + goto end; + } else if (unlikely((req->dst_rect.w != 1) && (req->dst_rect.h != 1) && + ((req->dst_rect.h % 32) == 3 || + (req->dst_rect.h % 32) == 1))) { + ret = mdp_ppp_blit_split_height(mdp, req, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); + } #endif ret = mdp_blit_and_wait(mdp, req, src_file, src_start, src_len, diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h index f35a757d4731e..d80be50e786d4 100644 --- a/drivers/video/msm/mdp_hw.h +++ b/drivers/video/msm/mdp_hw.h @@ -52,6 +52,7 @@ struct mdp_info { int format; int pack_pattern; bool dma_config_dirty; + struct mdp_blit_req *req; }; extern int mdp_out_if_register(struct mdp_device *mdp_dev, int interface, diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c index f13e63c6d664a..d9af83ca88f13 100644 --- a/drivers/video/msm/mdp_ppp.c +++ b/drivers/video/msm/mdp_ppp.c @@ -52,21 +52,6 @@ static uint32_t dst_img_cfg[] = { PPP_ARRAY1(CFG, DST) }; -static uint32_t bytes_per_pixel[] = { - [MDP_RGB_565] = 2, - [MDP_RGB_888] = 3, - [MDP_XRGB_8888] = 4, - [MDP_ARGB_8888] = 4, - [MDP_RGBA_8888] = 4, - [MDP_BGRA_8888] = 4, - [MDP_RGBX_8888] = 4, - [MDP_Y_CBCR_H2V1] = 1, - [MDP_Y_CBCR_H2V2] = 1, - [MDP_Y_CRCB_H2V1] = 1, - [MDP_Y_CRCB_H2V2] = 1, - [MDP_YCRYCB_H2V1] = 2 -}; - static uint32_t dst_op_chroma[] = { PPP_ARRAY1(CHROMA_SAMP, DST) }; @@ -568,7 +553,7 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, regs.dst_pack = pack_pattern[req->dst.format]; /* set src, bpp, start pixel and ystride */ - regs.src_bpp = bytes_per_pixel[req->src.format]; + regs.src_bpp = msm_bytes_per_pixel[req->src.format]; luma_base = src_start + req->src.offset; regs.src0 = luma_base + get_luma_offset(&req->src, &req->src_rect, regs.src_bpp); @@ -578,7 +563,7 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, set_src_region(&req->src, &req->src_rect, ®s); /* set dst, bpp, start pixel and ystride */ - regs.dst_bpp = bytes_per_pixel[req->dst.format]; + regs.dst_bpp = msm_bytes_per_pixel[req->dst.format]; luma_base = dst_start + req->dst.offset; regs.dst0 = luma_base + get_luma_offset(&req->dst, &req->dst_rect, regs.dst_bpp); diff --git a/drivers/video/msm/mdp_ppp.h b/drivers/video/msm/mdp_ppp.h index e04564347bdfe..9269c44733f5d 100644 --- a/drivers/video/msm/mdp_ppp.h +++ b/drivers/video/msm/mdp_ppp.h @@ -72,6 +72,14 @@ int mdp_ppp_load_blur(const struct mdp_info *mdp); #ifndef CONFIG_MSM_MDP31 int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs); #else + +int mdp_ppp_blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, unsigned long src_len, + struct file *dst_file, unsigned long dst_start, unsigned long dst_len); +int mdp_ppp_blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, unsigned long src_len, + struct file *dst_file, unsigned long dst_start, unsigned long dst_len); + static inline int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs) { @@ -79,4 +87,8 @@ static inline int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, } #endif +int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, unsigned long src_len, + struct file *dst_file, unsigned long dst_start, unsigned long dst_len); + #endif /* _VIDEO_MSM_MDP_PPP_H_ */ diff --git a/drivers/video/msm/mdp_ppp31.c b/drivers/video/msm/mdp_ppp31.c index 0a2833cb1cdd6..4c85b7e1a7827 100644 --- a/drivers/video/msm/mdp_ppp31.c +++ b/drivers/video/msm/mdp_ppp31.c @@ -330,3 +330,184 @@ void mdp_ppp_init_scale(const struct mdp_info *mdp) for (scale = 0; scale < MDP_SCALE_MAX; ++scale) load_table(mdp, scale, 0); } + +/* Splits a blit into two horizontal stripes. Used to work around MDP bugs */ +int mdp_ppp_blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, unsigned long src_len, + struct file *dst_file, unsigned long dst_start, unsigned long dst_len) +{ + int ret; + struct mdp_blit_req splitreq; + int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1; + int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1; + + splitreq = *req; + /* break dest roi at height*/ + d_x_0 = d_x_1 = req->dst_rect.x; + d_w_0 = d_w_1 = req->dst_rect.w; + d_y_0 = req->dst_rect.y; + if (req->dst_rect.h % 32 == 3) + d_h_1 = (req->dst_rect.h - 3) / 2 - 1; + else + d_h_1 = (req->dst_rect.h - 1) / 2 - 1; + d_h_0 = req->dst_rect.h - d_h_1; + d_y_1 = d_y_0 + d_h_0; + if (req->dst_rect.h == 3) { + d_h_1 = 2; + d_h_0 = 2; + d_y_1 = d_y_0 + 1; + } + /* break source roi */ + if (splitreq.flags & MDP_ROT_90) { + s_y_0 = s_y_1 = req->src_rect.y; + s_h_0 = s_h_1 = req->src_rect.h; + s_x_0 = req->src_rect.x; + s_w_1 = (req->src_rect.w * d_h_1) / req->dst_rect.h; + s_w_0 = req->src_rect.w - s_w_1; + s_x_1 = s_x_0 + s_w_0; + if (d_h_1 >= 8 * s_w_1) { + s_w_1++; + s_x_1--; + } + } else { + s_x_0 = s_x_1 = req->src_rect.x; + s_w_0 = s_w_1 = req->src_rect.w; + s_y_0 = req->src_rect.y; + s_h_1 = (req->src_rect.h * d_h_1) / req->dst_rect.h; + s_h_0 = req->src_rect.h - s_h_1; + s_y_1 = s_y_0 + s_h_0; + if (d_h_1 >= 8 * s_h_1) { + s_h_1++; + s_y_1--; + } + } + + /* blit first region */ + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + ret = mdp_blit_and_wait(mdp, &splitreq, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); + if (ret) + return ret; + + /* blit second region */ + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + ret = mdp_blit_and_wait(mdp, &splitreq, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); + return ret; +} + +/* Splits a blit into two vertical stripes. Used to work around MDP bugs */ +int mdp_ppp_blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, unsigned long src_len, + struct file *dst_file, unsigned long dst_start, unsigned long dst_len) +{ + int ret; + struct mdp_blit_req splitreq; + int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1; + int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1; + splitreq = *req; + + /* break dest roi at width*/ + d_y_0 = d_y_1 = req->dst_rect.y; + d_h_0 = d_h_1 = req->dst_rect.h; + d_x_0 = req->dst_rect.x; + if (req->dst_rect.w % 32 == 6) + d_w_1 = req->dst_rect.w / 2 - 1; + else if (req->dst_rect.w % 2 == 0) + d_w_1 = req->dst_rect.w / 2; + else if (req->dst_rect.w % 32 == 3) + d_w_1 = (req->dst_rect.w - 3) / 2 - 1; + else + d_w_1 = (req->dst_rect.w - 1) / 2 - 1; + d_w_0 = req->dst_rect.w - d_w_1; + d_x_1 = d_x_0 + d_w_0; + if (req->dst_rect.w == 3) { + d_w_1 = 2; + d_w_0 = 2; + d_x_1 = d_x_0 + 1; + } + + /* break src roi at height or width*/ + if (splitreq.flags & MDP_ROT_90) { + s_x_0 = s_x_1 = req->src_rect.x; + s_w_0 = s_w_1 = req->src_rect.w; + s_y_0 = req->src_rect.y; + s_h_1 = (req->src_rect.h * d_w_1) / req->dst_rect.w; + s_h_0 = req->src_rect.h - s_h_1; + s_y_1 = s_y_0 + s_h_0; + if (d_w_1 >= 8 * s_h_1) { + s_h_1++; + s_y_1--; + } + } else { + s_y_0 = s_y_1 = req->src_rect.y; + s_h_0 = s_h_1 = req->src_rect.h; + s_x_0 = req->src_rect.x; + s_w_1 = (req->src_rect.w * d_w_1) / req->dst_rect.w; + s_w_0 = req->src_rect.w - s_w_1; + s_x_1 = s_x_0 + s_w_0; + if (d_w_1 >= 8 * s_w_1) { + s_w_1++; + s_x_1--; + } + } + + /* blit first region */ + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + + if (unlikely((splitreq.dst_rect.h != 1) && + ((splitreq.dst_rect.h % 32 == 3) || + (splitreq.dst_rect.h % 32) == 1))) + ret = mdp_ppp_blit_split_height(mdp, &splitreq, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); + else + ret = mdp_blit_and_wait(mdp, &splitreq, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); + if (ret) + return ret; + /* blit second region */ + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + if (unlikely((splitreq.dst_rect.h != 1) && + ((splitreq.dst_rect.h % 32 == 3) || + (splitreq.dst_rect.h % 32) == 1))) + ret = mdp_ppp_blit_split_height(mdp, &splitreq, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); + else + ret = mdp_blit_and_wait(mdp, &splitreq, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); + return ret; +} diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h index 32ada8376c663..b27de28803b2d 100644 --- a/include/linux/msm_mdp.h +++ b/include/linux/msm_mdp.h @@ -36,6 +36,21 @@ enum { MDP_IMGTYPE_LIMIT /* Non valid image type after this enum */ }; +static const uint32_t msm_bytes_per_pixel[] = { + [MDP_RGB_565] = 2, + [MDP_XRGB_8888] = 4, + [MDP_Y_CBCR_H2V2] = 1, + [MDP_ARGB_8888] = 4, + [MDP_RGB_888] = 3, + [MDP_Y_CRCB_H2V2] = 1, + [MDP_YCRYCB_H2V1] = 2, + [MDP_Y_CRCB_H2V1] = 1, + [MDP_Y_CBCR_H2V1] = 1, + [MDP_RGBA_8888] = 4, + [MDP_BGRA_8888] = 4, + [MDP_RGBX_8888] = 4, + }; + enum { PMEM_IMG, FB_IMG, From e792450d412abe43184755e7196b64f6aef9d55b Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 8 Apr 2010 15:20:54 -0700 Subject: [PATCH 0579/2556] [ARM] msm: mahimahi: Switch to generic wlan_plat.h header Signed-off-by: Dmitry Shmidt --- arch/arm/mach-msm/board-mahimahi-wifi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-mahimahi-wifi.c b/arch/arm/mach-msm/board-mahimahi-wifi.c index 28bdc55add81f..1657092527ee8 100644 --- a/arch/arm/mach-msm/board-mahimahi-wifi.c +++ b/arch/arm/mach-msm/board-mahimahi-wifi.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include "board-mahimahi.h" From ff71187a1cc8d927b45899e41557dceef8107f92 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 8 Apr 2010 13:26:05 -0700 Subject: [PATCH 0580/2556] [ARM] msm: mdp: Fix workarounds in MDP 3.1 There were a few problems with the last patch that are fixed by this patch: 1. Missing "goto end" would cause the bad blit to be executed after the workaround in one case 2. Incorrect printing of failed blit on a crash 3. Fix split blits when rotating Change-Id: Ie3d97cc1bd6d7b5efde5b0343c776d02dabd7edb Signed-off-by: Colin Cross --- drivers/video/msm/mdp.c | 13 ++-- drivers/video/msm/mdp_ppp.c | 25 +++++++- drivers/video/msm/mdp_ppp.h | 1 + drivers/video/msm/mdp_ppp31.c | 114 ++++++++++++++++++++++++---------- include/linux/msm_mdp.h | 16 +---- 5 files changed, 114 insertions(+), 55 deletions(-) diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index cdb57405c3ebc..42e86a61a023e 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -427,7 +427,8 @@ int mdp_set_output_format(struct mdp_device *mdp_dev, int bpp) static void dump_req(struct mdp_blit_req *req, unsigned long src_start, unsigned long src_len, - unsigned long dst_start, unsigned long dst_len) { + unsigned long dst_start, unsigned long dst_len) +{ pr_err("flags: 0x%x\n", req->flags); pr_err("src_start: 0x%08lx\n", src_start); pr_err("src_len: 0x%08lx\n", src_len); @@ -468,11 +469,10 @@ int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, ret = mdp_ppp_wait(mdp); if (unlikely(ret)) { printk(KERN_ERR "%s: failed!\n", __func__); - if (mdp->req) - pr_err("original request:\n"); - dump_req(mdp->req, src_start, src_len, dst_start, dst_len); - pr_err("dead request:\n"); + pr_err("original request:\n"); dump_req(mdp->req, src_start, src_len, dst_start, dst_len); + pr_err("dead request:\n"); + dump_req(req, src_start, src_len, dst_start, dst_len); BUG(); return ret; } @@ -565,7 +565,7 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, } #else /* Workarounds for MDP 3.1 hardware bugs */ - if (unlikely((msm_bytes_per_pixel[req->dst.format] == 4) && + if (unlikely((mdp_get_bytes_per_pixel(req->dst.format) == 4) && (req->dst_rect.w != 1) && (((req->dst_rect.w % 8) == 6) || ((req->dst_rect.w % 32) == 3) || @@ -580,6 +580,7 @@ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, ret = mdp_ppp_blit_split_height(mdp, req, src_file, src_start, src_len, dst_file, dst_start, dst_len); + goto end; } #endif ret = mdp_blit_and_wait(mdp, req, diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c index d9af83ca88f13..53e3ec5b1477b 100644 --- a/drivers/video/msm/mdp_ppp.c +++ b/drivers/video/msm/mdp_ppp.c @@ -52,6 +52,21 @@ static uint32_t dst_img_cfg[] = { PPP_ARRAY1(CFG, DST) }; +static const uint32_t bytes_per_pixel[] = { + [MDP_RGB_565] = 2, + [MDP_XRGB_8888] = 4, + [MDP_Y_CBCR_H2V2] = 1, + [MDP_ARGB_8888] = 4, + [MDP_RGB_888] = 3, + [MDP_Y_CRCB_H2V2] = 1, + [MDP_YCRYCB_H2V1] = 2, + [MDP_Y_CRCB_H2V1] = 1, + [MDP_Y_CBCR_H2V1] = 1, + [MDP_RGBA_8888] = 4, + [MDP_BGRA_8888] = 4, + [MDP_RGBX_8888] = 4, +}; + static uint32_t dst_op_chroma[] = { PPP_ARRAY1(CHROMA_SAMP, DST) }; @@ -553,7 +568,7 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, regs.dst_pack = pack_pattern[req->dst.format]; /* set src, bpp, start pixel and ystride */ - regs.src_bpp = msm_bytes_per_pixel[req->src.format]; + regs.src_bpp = mdp_get_bytes_per_pixel(req->src.format); luma_base = src_start + req->src.offset; regs.src0 = luma_base + get_luma_offset(&req->src, &req->src_rect, regs.src_bpp); @@ -563,7 +578,7 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, set_src_region(&req->src, &req->src_rect, ®s); /* set dst, bpp, start pixel and ystride */ - regs.dst_bpp = msm_bytes_per_pixel[req->dst.format]; + regs.dst_bpp = mdp_get_bytes_per_pixel(req->dst.format); luma_base = dst_start + req->dst.offset; regs.dst0 = luma_base + get_luma_offset(&req->dst, &req->dst_rect, regs.dst_bpp); @@ -621,6 +636,12 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, return 0; } +int mdp_get_bytes_per_pixel(int format) +{ + if (format < 0 || format >= MDP_IMGTYPE_LIMIT) + return -1; + return bytes_per_pixel[format]; +} #define mdp_dump_register(mdp, reg) \ printk(# reg ": %08x\n", mdp_readl((mdp), (reg))) diff --git a/drivers/video/msm/mdp_ppp.h b/drivers/video/msm/mdp_ppp.h index 9269c44733f5d..dda21eca1eb52 100644 --- a/drivers/video/msm/mdp_ppp.h +++ b/drivers/video/msm/mdp_ppp.h @@ -87,6 +87,7 @@ static inline int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, } #endif +int mdp_get_bytes_per_pixel(int format); int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, struct file *src_file, unsigned long src_start, unsigned long src_len, struct file *dst_file, unsigned long dst_start, unsigned long dst_len); diff --git a/drivers/video/msm/mdp_ppp31.c b/drivers/video/msm/mdp_ppp31.c index 4c85b7e1a7827..9e0a3a605d1ee 100644 --- a/drivers/video/msm/mdp_ppp31.c +++ b/drivers/video/msm/mdp_ppp31.c @@ -383,14 +383,26 @@ int mdp_ppp_blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *r } /* blit first region */ - splitreq.src_rect.h = s_h_0; - splitreq.src_rect.y = s_y_0; - splitreq.dst_rect.h = d_h_0; - splitreq.dst_rect.y = d_y_0; - splitreq.src_rect.x = s_x_0; - splitreq.src_rect.w = s_w_0; - splitreq.dst_rect.x = d_x_0; - splitreq.dst_rect.w = d_w_0; + if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_90) || + ((splitreq.flags & MDP_ROT_MASK) == 0x0)) { + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } else { + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } ret = mdp_blit_and_wait(mdp, &splitreq, src_file, src_start, src_len, dst_file, dst_start, dst_len); @@ -398,14 +410,26 @@ int mdp_ppp_blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *r return ret; /* blit second region */ - splitreq.src_rect.h = s_h_1; - splitreq.src_rect.y = s_y_1; - splitreq.dst_rect.h = d_h_1; - splitreq.dst_rect.y = d_y_1; - splitreq.src_rect.x = s_x_1; - splitreq.src_rect.w = s_w_1; - splitreq.dst_rect.x = d_x_1; - splitreq.dst_rect.w = d_w_1; + if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_90) || + ((splitreq.flags & MDP_ROT_MASK) == 0x0)) { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } else { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } ret = mdp_blit_and_wait(mdp, &splitreq, src_file, src_start, src_len, dst_file, dst_start, dst_len); @@ -469,14 +493,26 @@ int mdp_ppp_blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *re } /* blit first region */ - splitreq.src_rect.h = s_h_0; - splitreq.src_rect.y = s_y_0; - splitreq.dst_rect.h = d_h_0; - splitreq.dst_rect.y = d_y_0; - splitreq.src_rect.x = s_x_0; - splitreq.src_rect.w = s_w_0; - splitreq.dst_rect.x = d_x_0; - splitreq.dst_rect.w = d_w_0; + if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_270) || + ((splitreq.flags & MDP_ROT_MASK) == 0x0)) { + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } else { + splitreq.src_rect.h = s_h_0; + splitreq.src_rect.y = s_y_0; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_0; + splitreq.src_rect.w = s_w_0; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } if (unlikely((splitreq.dst_rect.h != 1) && ((splitreq.dst_rect.h % 32 == 3) || @@ -490,15 +526,29 @@ int mdp_ppp_blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *re dst_file, dst_start, dst_len); if (ret) return ret; + /* blit second region */ - splitreq.src_rect.h = s_h_1; - splitreq.src_rect.y = s_y_1; - splitreq.dst_rect.h = d_h_1; - splitreq.dst_rect.y = d_y_1; - splitreq.src_rect.x = s_x_1; - splitreq.src_rect.w = s_w_1; - splitreq.dst_rect.x = d_x_1; - splitreq.dst_rect.w = d_w_1; + if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_270) || + ((splitreq.flags & MDP_ROT_MASK) == 0x0)) { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_1; + splitreq.dst_rect.y = d_y_1; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_1; + splitreq.dst_rect.w = d_w_1; + } else { + splitreq.src_rect.h = s_h_1; + splitreq.src_rect.y = s_y_1; + splitreq.dst_rect.h = d_h_0; + splitreq.dst_rect.y = d_y_0; + splitreq.src_rect.x = s_x_1; + splitreq.src_rect.w = s_w_1; + splitreq.dst_rect.x = d_x_0; + splitreq.dst_rect.w = d_w_0; + } + if (unlikely((splitreq.dst_rect.h != 1) && ((splitreq.dst_rect.h % 32 == 3) || (splitreq.dst_rect.h % 32) == 1))) diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h index b27de28803b2d..03d8fdec2a3e6 100644 --- a/include/linux/msm_mdp.h +++ b/include/linux/msm_mdp.h @@ -36,21 +36,6 @@ enum { MDP_IMGTYPE_LIMIT /* Non valid image type after this enum */ }; -static const uint32_t msm_bytes_per_pixel[] = { - [MDP_RGB_565] = 2, - [MDP_XRGB_8888] = 4, - [MDP_Y_CBCR_H2V2] = 1, - [MDP_ARGB_8888] = 4, - [MDP_RGB_888] = 3, - [MDP_Y_CRCB_H2V2] = 1, - [MDP_YCRYCB_H2V1] = 2, - [MDP_Y_CRCB_H2V1] = 1, - [MDP_Y_CBCR_H2V1] = 1, - [MDP_RGBA_8888] = 4, - [MDP_BGRA_8888] = 4, - [MDP_RGBX_8888] = 4, - }; - enum { PMEM_IMG, FB_IMG, @@ -63,6 +48,7 @@ enum { #define MDP_ROT_90 0x4 #define MDP_ROT_180 (MDP_FLIP_UD|MDP_FLIP_LR) #define MDP_ROT_270 (MDP_ROT_90|MDP_FLIP_UD|MDP_FLIP_LR) +#define MDP_ROT_MASK 0x7 #define MDP_DITHER 0x8 #define MDP_BLUR 0x10 #define MDP_BLEND_FG_PREMULT 0x20000 From a6d5b969f46949d195dda854c61fed816f48484f Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Thu, 8 Apr 2010 17:11:57 -0700 Subject: [PATCH 0581/2556] [ARM] msm: kgsl: Invalidate base addrs when setting page tables or flushing TLBs Change-Id: I17917aac393bec76950cbdc1978f11ee734b836b Signed-off-by: Shubhraprakash Das Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl_yamato.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c index a396264e4fe48..af5a6ff223cba 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c @@ -398,6 +398,12 @@ int kgsl_yamato_setstate(struct kgsl_device *device, uint32_t flags) sizedwords += 21; } + if (flags & (KGSL_MMUFLAGS_PTUPDATE | KGSL_MMUFLAGS_TLBFLUSH)) { + *cmds++ = pm4_type3_packet(PM4_INVALIDATE_STATE, 1); + *cmds++ = 0x7fff; /* invalidate all base pointers */ + sizedwords += 2; + } + kgsl_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, &link[0], sizedwords); } else { From 40ed8bd39042259a7c8d5c2bb1a702c44e11ca75 Mon Sep 17 00:00:00 2001 From: Andy Yeh Date: Sat, 3 Apr 2010 22:18:52 +0800 Subject: [PATCH 0582/2556] [ARM] msm: camera: s5k3e2fx: Fix the reddish color shading issue modify the EVT5 lens correction setting Signed-off-by: Paul Eastham --- drivers/media/video/msm/s5k3e2fx.c | 150 ++++++++++++++--------------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/drivers/media/video/msm/s5k3e2fx.c b/drivers/media/video/msm/s5k3e2fx.c index 2776405bbaffa..84a8a9516a88f 100644 --- a/drivers/media/video/msm/s5k3e2fx.c +++ b/drivers/media/video/msm/s5k3e2fx.c @@ -957,113 +957,113 @@ struct s5k3e2fx_i2c_reg_conf lc_setting[2][NUM_LC_REG] = { {0x326a, 0x09}, {0x326b, 0xcc}, {0x326c, 0x00}, - {0x326d, 0x9b}, - {0x326e, 0x73}, + {0x326d, 0x99}, + {0x326e, 0x45}, {0x326f, 0x0f}, - {0x3270, 0xd4}, - {0x3271, 0x9e}, + {0x3270, 0xd3}, + {0x3271, 0x80}, {0x3272, 0x00}, - {0x3273, 0x1a}, - {0x3274, 0x87}, + {0x3273, 0x20}, + {0x3274, 0xf7}, {0x3275, 0x0f}, - {0x3276, 0xfd}, - {0x3277, 0xeb}, - {0x3278, 0x0f}, - {0x3279, 0xf5}, - {0x327a, 0xb4}, + {0x3276, 0xef}, + {0x3277, 0x0d}, + {0x3278, 0x00}, + {0x3279, 0x09}, + {0x327a, 0x3c}, {0x327b, 0x00}, - {0x327c, 0x0d}, - {0x327d, 0x8c}, + {0x327c, 0x01}, + {0x327d, 0x16}, {0x327e, 0x0f}, {0x327f, 0xc9}, - {0x3280, 0x4d}, + {0x3280, 0x36}, {0x3281, 0x00}, - {0x3282, 0x1d}, - {0x3283, 0x2d}, + {0x3282, 0x21}, + {0x3283, 0xff}, {0x3284, 0x0f}, - {0x3285, 0xea}, - {0x3286, 0x5b}, + {0x3285, 0xdc}, + {0x3286, 0xc2}, {0x3287, 0x00}, - {0x3288, 0x04}, - {0x3289, 0x76}, - {0x328a, 0x00}, - {0x328b, 0x10}, - {0x328c, 0x2d}, + {0x3288, 0x1e}, + {0x3289, 0xc0}, + {0x328a, 0x0f}, + {0x328b, 0xf0}, + {0x328c, 0xa7}, {0x328d, 0x0f}, - {0x328e, 0xe6}, - {0x328f, 0xde}, + {0x328e, 0xf9}, + {0x328f, 0x2a}, {0x3290, 0x00}, - {0x3291, 0x26}, - {0x3292, 0x85}, + {0x3291, 0x29}, + {0x3292, 0x5c}, {0x3293, 0x0f}, - {0x3294, 0xcf}, - {0x3295, 0x12}, + {0x3294, 0xc9}, + {0x3295, 0x2a}, {0x3296, 0x00}, - {0x3297, 0x14}, - {0x3298, 0x0f}, - {0x3299, 0x00}, - {0x329a, 0x0b}, - {0x329b, 0x36}, + {0x3297, 0x1f}, + {0x3298, 0x5c}, + {0x3299, 0x0f}, + {0x329a, 0xfa}, + {0x329b, 0x0c}, {0x329c, 0x0f}, - {0x329d, 0xe4}, - {0x329e, 0xa4}, + {0x329d, 0xf3}, + {0x329e, 0x94}, {0x329f, 0x00}, - {0x32a0, 0x21}, - {0x32a1, 0x1f}, + {0x32a0, 0x1c}, + {0x32a1, 0xce}, {0x32a2, 0x0f}, - {0x32a3, 0xf3}, - {0x32a4, 0x99}, + {0x32a3, 0xed}, + {0x32a4, 0xb7}, {0x32a5, 0x00}, - {0x32a6, 0x30}, - {0x32a7, 0x8f}, + {0x32a6, 0x34}, + {0x32a7, 0x51}, {0x32a8, 0x0f}, - {0x32a9, 0xf9}, - {0x32aa, 0x35}, + {0x32a9, 0xfa}, + {0x32aa, 0x7d}, {0x32ab, 0x0f}, - {0x32ac, 0xee}, - {0x32ad, 0x6e}, + {0x32ac, 0xe6}, + {0x32ad, 0xbf}, {0x32ae, 0x00}, - {0x32af, 0x09}, - {0x32b0, 0x19}, + {0x32af, 0x18}, + {0x32b0, 0xc6}, {0x32b1, 0x0f}, - {0x32b2, 0xf0}, - {0x32b3, 0x57}, + {0x32b2, 0xe0}, + {0x32b3, 0x72}, {0x32b4, 0x00}, - {0x32b5, 0x01}, - {0x32b6, 0xcc}, + {0x32b5, 0x08}, + {0x32b6, 0x23}, {0x32b7, 0x0f}, {0x32b8, 0xf1}, - {0x32b9, 0x0b}, + {0x32b9, 0x54}, {0x32ba, 0x0f}, - {0x32bb, 0xee}, - {0x32bc, 0x99}, + {0x32bb, 0xe1}, + {0x32bc, 0x84}, {0x32bd, 0x00}, - {0x32be, 0x11}, - {0x32bf, 0x3d}, - {0x32c0, 0x00}, - {0x32c1, 0x10}, - {0x32c2, 0x64}, - {0x32c3, 0x0f}, - {0x32c4, 0xf6}, - {0x32c5, 0xab}, - {0x32c6, 0x00}, - {0x32c7, 0x03}, - {0x32c8, 0x19}, + {0x32be, 0x26}, + {0x32bf, 0xb1}, + {0x32c0, 0x0f}, + {0x32c1, 0xfa}, + {0x32c2, 0xc2}, + {0x32c3, 0x00}, + {0x32c4, 0x05}, + {0x32c5, 0x3d}, + {0x32c6, 0x0f}, + {0x32c7, 0xff}, + {0x32c8, 0xaf}, {0x32c9, 0x0f}, - {0x32ca, 0xf3}, - {0x32cb, 0xc9}, + {0x32ca, 0xf1}, + {0x32cb, 0xe5}, {0x32cc, 0x00}, - {0x32cd, 0x17}, - {0x32ce, 0xb3}, + {0x32cd, 0x21}, + {0x32ce, 0xdd}, {0x32cf, 0x0f}, - {0x32d0, 0xf2}, - {0x32d1, 0x3d}, + {0x32d0, 0xe8}, + {0x32d1, 0x6a}, {0x32d2, 0x0f}, {0x32d3, 0xf4}, - {0x32d4, 0x7e}, + {0x32d4, 0xfb}, {0x32d5, 0x00}, - {0x32d6, 0x09}, - {0x32d7, 0x46}, + {0x32d6, 0x0c}, + {0x32d7, 0x89}, {0x32d8, 0x00}, {0x32d9, 0x7c}, {0x32da, 0x79}, From 98c0482ad8f9493621c691ef4ae4eba74eb9fefb Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Wed, 21 Apr 2010 17:26:08 -0700 Subject: [PATCH 0583/2556] [ARM] msm7k: hw3d: add more security checks -- check the VMA size against the GPU-memory-region size -- in mmap(), clear EBI and SMI memory regions before handing them to the user; this prevents a malicious client from stealing the previous clients data by doing a forced revoke and mmapping the GPU memory. Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/hw3d.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm/mach-msm/hw3d.c b/arch/arm/mach-msm/hw3d.c index fe53951e64084..aef7f37bf0164 100644 --- a/arch/arm/mach-msm/hw3d.c +++ b/arch/arm/mach-msm/hw3d.c @@ -584,6 +584,11 @@ static int hw3d_mmap(struct file *file, struct vm_area_struct *vma) pr_err("%s: Trying to mmap unknown region %d\n", __func__, region); return -EINVAL; + } else if (vma_size > info->regions[region].size) { + pr_err("%s: VMA size %ld exceeds region %d size %ld\n", + __func__, vma_size, region, + info->regions[region].size); + return -EINVAL; } else if (REGION_PAGE_OFFS(vma->vm_pgoff) != 0 || (vma_size & ~PAGE_MASK)) { pr_err("%s: Can't remap part of the region %d\n", __func__, @@ -625,6 +630,13 @@ static int hw3d_mmap(struct file *file, struct vm_area_struct *vma) goto done; } + /* Prevent a malicious client from stealing another client's data + * by forcing a revoke on it and then mmapping the GPU buffers. + */ + if (region != HW3D_REGS) + memset(info->regions[region].vbase, 0, + info->regions[region].size); + vma->vm_ops = &hw3d_vm_ops; /* mark this region as mapped */ From 3a70483fab65a211f1057b8729c0e3c1b55792d4 Mon Sep 17 00:00:00 2001 From: Wu-cheng Li Date: Sat, 24 Apr 2010 10:02:46 -0700 Subject: [PATCH 0584/2556] [ARM] msm: stop the VFE before releasing the sensor On msm7k, the sensor needs to be released after stopping the VFE, but before releasing the ADSP VFE and QCAM tasks. Otherwise, the DSP locks up. The code for qsd8k is modified accordingly. Signed-off-by: Iliyan Malchev --- drivers/media/video/msm/msm_camera.c | 2 -- drivers/media/video/msm/msm_vfe7x.c | 6 ++++++ drivers/media/video/msm/msm_vfe8x.c | 7 +++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c index cababe070c3f4..f0731b6203233 100644 --- a/drivers/media/video/msm/msm_camera.c +++ b/drivers/media/video/msm/msm_camera.c @@ -1858,8 +1858,6 @@ static int __msm_release(struct msm_sync *sync) sync->opencnt--; if (!sync->opencnt) { - /*sensor release*/ - sync->sctrl.s_release(); /* need to clean up system resource */ if (sync->vfefn.vfe_release) sync->vfefn.vfe_release(sync->pdev); diff --git a/drivers/media/video/msm/msm_vfe7x.c b/drivers/media/video/msm/msm_vfe7x.c index 9354ac52cc57c..0812bf58cef9a 100644 --- a/drivers/media/video/msm/msm_vfe7x.c +++ b/drivers/media/video/msm/msm_vfe7x.c @@ -232,6 +232,9 @@ static int vfe_7x_stop(void) static void vfe_7x_release(struct platform_device *pdev) { + struct msm_sensor_ctrl *sctrl = + &((struct msm_sync *)vfe_syncdata)->sctrl; + mutex_lock(&vfe_lock); vfe_syncdata = NULL; mutex_unlock(&vfe_lock); @@ -245,6 +248,9 @@ static void vfe_7x_release(struct platform_device *pdev) msm_adsp_disable(qcam_mod); msm_adsp_disable(vfe_mod); + if (sctrl) + sctrl->s_release(); + msm_adsp_put(qcam_mod); msm_adsp_put(vfe_mod); diff --git a/drivers/media/video/msm/msm_vfe8x.c b/drivers/media/video/msm/msm_vfe8x.c index 267d99d0fb92c..d955c64b0710f 100644 --- a/drivers/media/video/msm/msm_vfe8x.c +++ b/drivers/media/video/msm/msm_vfe8x.c @@ -70,11 +70,18 @@ static int vfe_init(struct msm_vfe_callback *presp, struct platform_device *dev) static void vfe_release(struct platform_device *dev) { + struct msm_sensor_ctrl *sctrl = + &((struct msm_sync *)vfe_syncdata)->sctrl; + if (ebi1_clk) { clk_set_rate(ebi1_clk, 0); clk_put(ebi1_clk); ebi1_clk = 0; } + + if (sctrl) + sctrl->s_release(); + msm_camio_disable(dev); vfe_cmd_release(dev); vfe_syncdata = NULL; From 85360e2e2ec9761a5225e8916ecd4358881fca6e Mon Sep 17 00:00:00 2001 From: Christopher Lais Date: Sat, 1 May 2010 18:19:23 -0500 Subject: [PATCH 0585/2556] [ARM] msm: trout/sapphire: move *_CPLD_BASE outside the vmalloc range SAPPHIRE_CPLD_BASE and TROUT_CPLD_BASE were inside the range of memory allocated by the kernel for virtual mappings, causing the IO mapping to be overwritten if the kernel allocated the space. To reproduce, allocate then deallocate all virtual kernel memory. The mapping will be removed and the kernel will crash when any of the IO memory is accessed (e.g. for changing phone brightness). Change-Id: I49b3b8af5257ba77998e03d0ecff0f909ddc752c Signed-off-by: Christopher Lais --- arch/arm/mach-msm/board-sapphire.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-sapphire.h b/arch/arm/mach-msm/board-sapphire.h index 3327dfeafde0b..170309362d1ac 100644 --- a/arch/arm/mach-msm/board-sapphire.h +++ b/arch/arm/mach-msm/board-sapphire.h @@ -106,7 +106,7 @@ ** Sapphire Altera CPLD can keep the registers value and ** doesn't need a shadow to backup. **/ -#define SAPPHIRE_CPLD_BASE 0xE8100000 /* VA */ +#define SAPPHIRE_CPLD_BASE 0xFA000000 /* VA */ #define SAPPHIRE_CPLD_START 0x98000000 /* PA */ #define SAPPHIRE_CPLD_SIZE SZ_4K From 876e2d3c26cbaf8f7762dd6f58f0e26b98802dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 11 Jun 2010 19:35:13 -0700 Subject: [PATCH 0586/2556] hack: fix serial debugger build Change-Id: I72c3d65869b5147656daa07c66864affd0d60acc --- drivers/tty/serial/msm_serial.h | 2 ++ drivers/tty/serial/msm_serial_debugger.c | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h index f6ca9ca79e98f..059a69efc14bd 100644 --- a/drivers/tty/serial/msm_serial.h +++ b/drivers/tty/serial/msm_serial.h @@ -114,6 +114,7 @@ #define UART_MISR 0x0010 #define UART_ISR 0x0014 +#ifndef BUILD_SERIAL_DEBUGGER #define UART_TO_MSM(uart_port) ((struct msm_port *) uart_port) static inline @@ -169,5 +170,6 @@ void msm_serial_set_mnd_regs_from_uartclk(struct uart_port *port) #else #define msm_serial_set_mnd_regs msm_serial_set_mnd_regs_from_uartclk #endif +#endif #endif /* __DRIVERS_SERIAL_MSM_SERIAL_H */ diff --git a/drivers/tty/serial/msm_serial_debugger.c b/drivers/tty/serial/msm_serial_debugger.c index a355e58f90d1c..f3214d26c74c6 100644 --- a/drivers/tty/serial/msm_serial_debugger.c +++ b/drivers/tty/serial/msm_serial_debugger.c @@ -36,6 +36,7 @@ #include #include +#define BUILD_SERIAL_DEBUGGER #include "msm_serial.h" #include From 467da7ac0db71e4e83d8b8c839ba024811cf2e01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 11 Jun 2010 20:18:13 -0700 Subject: [PATCH 0587/2556] [ARM] msm: Delete irq-vic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't need a separate implementation to support 4 banks instead of 2. Change-Id: I8af6e3a1a1f38c6ea13e52e9a2fedde54683c968 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/Kconfig | 5 - arch/arm/mach-msm/Makefile | 4 - arch/arm/mach-msm/irq-vic.c | 364 ------------------------------------ 3 files changed, 373 deletions(-) delete mode 100644 arch/arm/mach-msm/irq-vic.c diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 7642df213a282..57b8d790d244c 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -19,7 +19,6 @@ config ARCH_MSM7X30 select MACH_MSM7X30_SURF # if ! select ARCH_MSM_SCORPION select MSM_SMD - select MSM_VIC select CPU_V7 select MSM_GPIOMUX select MSM_PROC_COMM @@ -31,7 +30,6 @@ config ARCH_QSD8X50 select MACH_QSD8X50_SURF if !MACH_QSD8X50A_ST1_5 select ARCH_MSM_SCORPION select MSM_SMD - select MSM_VIC select CPU_V7 select MSM_GPIOMUX select MSM_PROC_COMM @@ -65,9 +63,6 @@ config ARCH_MSM_SCORPION config HAS_MSM_DEBUG_UART_PHYS bool -config MSM_VIC - bool - config MSM_MDP22 bool depends on ARCH_MSM7X00A diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 415f94c33705b..f653f2e4f8fbb 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -11,13 +11,9 @@ obj-$(CONFIG_ARCH_MSM_ARM11) += acpuclock-arm11.o idle.o obj-$(CONFIG_ARCH_MSM_SCORPION) += acpuclock-scorpion.o idle-v7.o obj-$(CONFIG_ARCH_MSM_SCORPION) += arch-init-scorpion.o -ifdef CONFIG_MSM_VIC -obj-y += irq-vic.o -else ifndef CONFIG_ARCH_MSM8X60 obj-y += irq.o endif -endif obj-$(CONFIG_ARCH_MSM8X60) += clock-dummy.o iommu.o iommu_dev.o devices-msm8x60-iommu.o obj-$(CONFIG_MSM_PROC_COMM) += proc_comm.o clock-pcom.o vreg.o diff --git a/arch/arm/mach-msm/irq-vic.c b/arch/arm/mach-msm/irq-vic.c deleted file mode 100644 index 68c28bbdc9695..0000000000000 --- a/arch/arm/mach-msm/irq-vic.c +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2009, Code Aurora Forum. All rights reserved. - * - * 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 "smd_private.h" - -enum { - IRQ_DEBUG_SLEEP_INT_TRIGGER = 1U << 0, - IRQ_DEBUG_SLEEP_INT = 1U << 1, - IRQ_DEBUG_SLEEP_ABORT = 1U << 2, - IRQ_DEBUG_SLEEP = 1U << 3, - IRQ_DEBUG_SLEEP_REQUEST = 1U << 4, -}; -static int msm_irq_debug_mask; -module_param_named(debug_mask, msm_irq_debug_mask, int, - S_IRUGO | S_IWUSR | S_IWGRP); - -#define VIC_REG(off) (MSM_VIC_BASE + (off)) -#define VIC_INT_TO_REG_ADDR(base, irq) (base + (irq / 32) * 4) -#define VIC_INT_TO_REG_INDEX(irq) ((irq >> 5) & 3) - -#define VIC_INT_SELECT0 VIC_REG(0x0000) /* 1: FIQ, 0: IRQ */ -#define VIC_INT_SELECT1 VIC_REG(0x0004) /* 1: FIQ, 0: IRQ */ -#define VIC_INT_SELECT2 VIC_REG(0x0008) /* 1: FIQ, 0: IRQ */ -#define VIC_INT_SELECT3 VIC_REG(0x000C) /* 1: FIQ, 0: IRQ */ -#define VIC_INT_EN0 VIC_REG(0x0010) -#define VIC_INT_EN1 VIC_REG(0x0014) -#define VIC_INT_EN2 VIC_REG(0x0018) -#define VIC_INT_EN3 VIC_REG(0x001C) -#define VIC_INT_ENCLEAR0 VIC_REG(0x0020) -#define VIC_INT_ENCLEAR1 VIC_REG(0x0024) -#define VIC_INT_ENCLEAR2 VIC_REG(0x0028) -#define VIC_INT_ENCLEAR3 VIC_REG(0x002C) -#define VIC_INT_ENSET0 VIC_REG(0x0030) -#define VIC_INT_ENSET1 VIC_REG(0x0034) -#define VIC_INT_ENSET2 VIC_REG(0x0038) -#define VIC_INT_ENSET3 VIC_REG(0x003C) -#define VIC_INT_TYPE0 VIC_REG(0x0040) /* 1: EDGE, 0: LEVEL */ -#define VIC_INT_TYPE1 VIC_REG(0x0044) /* 1: EDGE, 0: LEVEL */ -#define VIC_INT_TYPE2 VIC_REG(0x0048) /* 1: EDGE, 0: LEVEL */ -#define VIC_INT_TYPE3 VIC_REG(0x004C) /* 1: EDGE, 0: LEVEL */ -#define VIC_INT_POLARITY0 VIC_REG(0x0050) /* 1: NEG, 0: POS */ -#define VIC_INT_POLARITY1 VIC_REG(0x0054) /* 1: NEG, 0: POS */ -#define VIC_INT_POLARITY2 VIC_REG(0x0058) /* 1: NEG, 0: POS */ -#define VIC_INT_POLARITY3 VIC_REG(0x005C) /* 1: NEG, 0: POS */ -#define VIC_NO_PEND_VAL VIC_REG(0x0060) - -#if defined(CONFIG_ARCH_MSM_SCORPION) -#define VIC_NO_PEND_VAL_FIQ VIC_REG(0x0064) -#define VIC_INT_MASTEREN VIC_REG(0x0068) /* 1: IRQ, 2: FIQ */ -#define VIC_CONFIG VIC_REG(0x006C) /* 1: USE SC VIC */ -#else -#define VIC_INT_MASTEREN VIC_REG(0x0064) /* 1: IRQ, 2: FIQ */ -#define VIC_PROTECTION VIC_REG(0x006C) /* 1: ENABLE */ -#define VIC_CONFIG VIC_REG(0x0068) /* 1: USE ARM1136 VIC */ -#endif - -#define VIC_IRQ_STATUS0 VIC_REG(0x0080) -#define VIC_IRQ_STATUS1 VIC_REG(0x0084) -#define VIC_IRQ_STATUS2 VIC_REG(0x0088) -#define VIC_IRQ_STATUS3 VIC_REG(0x008C) -#define VIC_FIQ_STATUS0 VIC_REG(0x0090) -#define VIC_FIQ_STATUS1 VIC_REG(0x0094) -#define VIC_FIQ_STATUS2 VIC_REG(0x0098) -#define VIC_FIQ_STATUS3 VIC_REG(0x009C) -#define VIC_RAW_STATUS0 VIC_REG(0x00A0) -#define VIC_RAW_STATUS1 VIC_REG(0x00A4) -#define VIC_RAW_STATUS2 VIC_REG(0x00A8) -#define VIC_RAW_STATUS3 VIC_REG(0x00AC) -#define VIC_INT_CLEAR0 VIC_REG(0x00B0) -#define VIC_INT_CLEAR1 VIC_REG(0x00B4) -#define VIC_INT_CLEAR2 VIC_REG(0x00B8) -#define VIC_INT_CLEAR3 VIC_REG(0x00BC) -#define VIC_SOFTINT0 VIC_REG(0x00C0) -#define VIC_SOFTINT1 VIC_REG(0x00C4) -#define VIC_SOFTINT2 VIC_REG(0x00C8) -#define VIC_SOFTINT3 VIC_REG(0x00CC) -#define VIC_IRQ_VEC_RD VIC_REG(0x00D0) /* pending int # */ -#define VIC_IRQ_VEC_PEND_RD VIC_REG(0x00D4) /* pending vector addr */ -#define VIC_IRQ_VEC_WR VIC_REG(0x00D8) - -#if defined(CONFIG_ARCH_MSM_SCORPION) -#define VIC_FIQ_VEC_RD VIC_REG(0x00DC) -#define VIC_FIQ_VEC_PEND_RD VIC_REG(0x00E0) -#define VIC_FIQ_VEC_WR VIC_REG(0x00E4) -#define VIC_IRQ_IN_SERVICE VIC_REG(0x00E8) -#define VIC_IRQ_IN_STACK VIC_REG(0x00EC) -#define VIC_FIQ_IN_SERVICE VIC_REG(0x00F0) -#define VIC_FIQ_IN_STACK VIC_REG(0x00F4) -#define VIC_TEST_BUS_SEL VIC_REG(0x00F8) -#define VIC_IRQ_CTRL_CONFIG VIC_REG(0x00FC) -#else -#define VIC_IRQ_IN_SERVICE VIC_REG(0x00E0) -#define VIC_IRQ_IN_STACK VIC_REG(0x00E4) -#define VIC_TEST_BUS_SEL VIC_REG(0x00E8) -#endif - -#define VIC_VECTPRIORITY(n) VIC_REG(0x0200+((n) * 4)) -#define VIC_VECTADDR(n) VIC_REG(0x0400+((n) * 4)) - -#if defined(CONFIG_ARCH_MSM7X30) -#define VIC_NUM_REGS 4 -#else -#define VIC_NUM_REGS 2 -#endif - -#if VIC_NUM_REGS == 2 -#define DPRINT_REGS(base_reg, format, ...) \ - printk(KERN_INFO format " %x %x\n", ##__VA_ARGS__, \ - readl(base_reg ## 0), readl(base_reg ## 1)) -#define DPRINT_ARRAY(array, format, ...) \ - printk(KERN_INFO format " %x %x\n", ##__VA_ARGS__, \ - array[0], array[1]) -#elif VIC_NUM_REGS == 4 -#define DPRINT_REGS(base_reg, format, ...) \ - printk(KERN_INFO format " %x %x %x %x\n", ##__VA_ARGS__, \ - readl(base_reg ## 0), readl(base_reg ## 1), \ - readl(base_reg ## 2), readl(base_reg ## 3)) -#define DPRINT_ARRAY(array, format, ...) \ - printk(KERN_INFO format " %x %x %x %x\n", ##__VA_ARGS__, \ - array[0], array[1], \ - array[2], array[3]) -#else -#error "VIC_NUM_REGS set to illegal value" -#endif - -static uint32_t msm_irq_smsm_wake_enable[2]; -static struct { - uint32_t int_en[2]; - uint32_t int_type; - uint32_t int_polarity; - uint32_t int_select; -} msm_irq_shadow_reg[VIC_NUM_REGS]; -static uint32_t msm_irq_idle_disable[VIC_NUM_REGS]; - -#define SMSM_FAKE_IRQ (0xff) -static uint8_t msm_irq_to_smsm[NR_IRQS] = { - [INT_MDDI_EXT] = 1, - [INT_MDDI_PRI] = 2, - [INT_MDDI_CLIENT] = 3, - [INT_USB_OTG] = 4, - - [INT_PWB_I2C] = 5, - [INT_SDC1_0] = 6, - [INT_SDC1_1] = 7, - [INT_SDC2_0] = 8, - - [INT_SDC2_1] = 9, - [INT_ADSP_A9_A11] = 10, - [INT_UART1] = 11, - [INT_UART2] = 12, - - [INT_UART3] = 13, - [INT_UART1_RX] = 14, - [INT_UART2_RX] = 15, - [INT_UART3_RX] = 16, - - [INT_UART1DM_IRQ] = 17, - [INT_UART1DM_RX] = 18, - [INT_KEYSENSE] = 19, -#if !defined(CONFIG_ARCH_MSM7X30) - [INT_AD_HSSD] = 20, -#endif - - [INT_NAND_WR_ER_DONE] = 21, - [INT_NAND_OP_DONE] = 22, - [INT_TCHSCRN1] = 23, - [INT_TCHSCRN2] = 24, - - [INT_TCHSCRN_SSBI] = 25, - [INT_USB_HS] = 26, - [INT_UART2DM_RX] = 27, - [INT_UART2DM_IRQ] = 28, - - [INT_SDC4_1] = 29, - [INT_SDC4_0] = 30, - [INT_SDC3_1] = 31, - [INT_SDC3_0] = 32, - - /* fake wakeup interrupts */ - [INT_GPIO_GROUP1] = SMSM_FAKE_IRQ, - [INT_GPIO_GROUP2] = SMSM_FAKE_IRQ, - [INT_A9_M2A_0] = SMSM_FAKE_IRQ, - [INT_A9_M2A_1] = SMSM_FAKE_IRQ, - [INT_A9_M2A_5] = SMSM_FAKE_IRQ, - [INT_GP_TIMER_EXP] = SMSM_FAKE_IRQ, - [INT_DEBUG_TIMER_EXP] = SMSM_FAKE_IRQ, - [INT_ADSP_A11] = SMSM_FAKE_IRQ, -#ifdef CONFIG_ARCH_QSD8X50 - [INT_SIRC_0] = SMSM_FAKE_IRQ, - [INT_SIRC_1] = SMSM_FAKE_IRQ, -#endif -}; - -static inline void msm_irq_write_all_regs(void __iomem *base, unsigned int val) -{ - int i; - - for (i = 0; i < VIC_NUM_REGS; i++) - writel(val, base + (i * 4)); -} - -static void msm_irq_ack(struct irq_data *d) -{ - void __iomem *reg = VIC_INT_TO_REG_ADDR(VIC_INT_CLEAR0, d->irq); - writel(1 << (d->irq & 31), reg); -} - -static void msm_irq_mask(struct irq_data *d) -{ - void __iomem *reg = VIC_INT_TO_REG_ADDR(VIC_INT_ENCLEAR0, d->irq); - unsigned index = VIC_INT_TO_REG_INDEX(d->irq); - uint32_t mask = 1UL << (d->irq & 31); - int smsm_irq = msm_irq_to_smsm[d->irq]; - - msm_irq_shadow_reg[index].int_en[0] &= ~mask; - writel(mask, reg); - if (smsm_irq == 0) - msm_irq_idle_disable[index] &= ~mask; - else { - mask = 1UL << (smsm_irq - 1); - msm_irq_smsm_wake_enable[0] &= ~mask; - } -} - -static void msm_irq_unmask(struct irq_data *d) -{ - void __iomem *reg = VIC_INT_TO_REG_ADDR(VIC_INT_ENSET0, d->irq); - unsigned index = VIC_INT_TO_REG_INDEX(d->irq); - uint32_t mask = 1UL << (d->irq & 31); - int smsm_irq = msm_irq_to_smsm[d->irq]; - - msm_irq_shadow_reg[index].int_en[0] |= mask; - writel(mask, reg); - - if (smsm_irq == 0) - msm_irq_idle_disable[index] |= mask; - else { - mask = 1UL << (smsm_irq - 1); - msm_irq_smsm_wake_enable[0] |= mask; - } -} - -static int msm_irq_set_wake(struct irq_data *d, unsigned int on) -{ - unsigned index = VIC_INT_TO_REG_INDEX(d->irq); - uint32_t mask = 1UL << (d->irq & 31); - int smsm_irq = msm_irq_to_smsm[d->irq]; - - if (smsm_irq == 0) { - printk(KERN_ERR "msm_irq_set_wake: bad wakeup irq %d\n", d->irq); - return -EINVAL; - } - if (on) - msm_irq_shadow_reg[index].int_en[1] |= mask; - else - msm_irq_shadow_reg[index].int_en[1] &= ~mask; - - if (smsm_irq == SMSM_FAKE_IRQ) - return 0; - - mask = 1UL << (smsm_irq - 1); - if (on) - msm_irq_smsm_wake_enable[1] |= mask; - else - msm_irq_smsm_wake_enable[1] &= ~mask; - return 0; -} - -static int msm_irq_set_type(struct irq_data *d, unsigned int flow_type) -{ - void __iomem *treg = VIC_INT_TO_REG_ADDR(VIC_INT_TYPE0, d->irq); - void __iomem *preg = VIC_INT_TO_REG_ADDR(VIC_INT_POLARITY0, d->irq); - unsigned index = VIC_INT_TO_REG_INDEX(d->irq); - int b = 1 << (d->irq & 31); - uint32_t polarity; - uint32_t type; - - polarity = msm_irq_shadow_reg[index].int_polarity; - if (flow_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW)) - polarity |= b; - if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH)) - polarity &= ~b; - writel(polarity, preg); - msm_irq_shadow_reg[index].int_polarity = polarity; - - type = msm_irq_shadow_reg[index].int_type; - if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { - type |= b; - irq_desc[d->irq].handle_irq = handle_edge_irq; - } - if (flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW)) { - type &= ~b; - irq_desc[d->irq].handle_irq = handle_level_irq; - } - writel(type, treg); - msm_irq_shadow_reg[index].int_type = type; - return 0; -} - -static struct irq_chip msm_irq_chip = { - .name = "msm", - .irq_disable = msm_irq_mask, - .irq_ack = msm_irq_ack, - .irq_mask = msm_irq_mask, - .irq_unmask = msm_irq_unmask, - .irq_set_wake = msm_irq_set_wake, - .irq_set_type = msm_irq_set_type, -}; - -void __init msm_init_irq(void) -{ - unsigned n; - - /* select level interrupts */ - msm_irq_write_all_regs(VIC_INT_TYPE0, 0); - - /* select highlevel interrupts */ - msm_irq_write_all_regs(VIC_INT_POLARITY0, 0); - - /* select IRQ for all INTs */ - msm_irq_write_all_regs(VIC_INT_SELECT0, 0); - - /* disable all INTs */ - msm_irq_write_all_regs(VIC_INT_EN0, 0); - - /* don't use vic */ - writel(0, VIC_CONFIG); - - /* enable interrupt controller */ - writel(3, VIC_INT_MASTEREN); - - for (n = 0; n < NR_MSM_IRQS; n++) { - set_irq_chip(n, &msm_irq_chip); - set_irq_handler(n, handle_level_irq); - set_irq_flags(n, IRQF_VALID); - } -} From 58cbf3cc1b61a3980675c8e48a353c91c380c8a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 15 Jun 2010 18:22:14 -0700 Subject: [PATCH 0588/2556] [ARM] msm: clock: Don't get unused ebi1 clock in msm_clock_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the clock is shared this require a memory allocation which does not work this early in the boot process. Change-Id: Ia7e8875e7294de53f0354b1124ad8f2677a04f91 Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/clock.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c index 9f131a3d3ea55..50bff73dd2d1c 100644 --- a/arch/arm/mach-msm/clock.c +++ b/arch/arm/mach-msm/clock.c @@ -292,13 +292,6 @@ int clks_print_running(void) } EXPORT_SYMBOL(clks_print_running); -/* EBI1 is the only shared clock that several clients want to vote on as of - * this commit. If this changes in the future, then it might be better to - * make clk_min_rate handle the voting or make ebi1_clk_set_min_rate more - * generic to support different clocks. - */ -static struct clk *ebi1_clk; - static void __init set_clock_ops(struct clk *clk) { if (!clk->ops) { @@ -320,10 +313,6 @@ void __init msm_clock_init(struct clk *clock_tbl, unsigned num_clocks) hlist_add_head(&msm_clocks[n].list, &clocks); } mutex_unlock(&clocks_mutex); - - ebi1_clk = clk_get(NULL, "ebi1_clk"); - BUG_ON(ebi1_clk == NULL); - } #if defined(CONFIG_DEBUG_FS) From 69884b95102c2e187d04ce17142b31f0c8ce2cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 16 Jun 2010 22:02:28 -0700 Subject: [PATCH 0589/2556] Revert "drivers: misc: pass miscdevice pointer via file private data" This reverts commit fa1f68db6ca7ebb6fc4487ac215bffba06c01c28. This broke the msm kgsl gpu driver. Change-Id: I7108a4744374fe7dd396712a7fcc918ab6f9bd0d --- drivers/char/misc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 778273c93242f..d8efcf1716933 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -144,7 +144,6 @@ static int misc_open(struct inode * inode, struct file * file) old_fops = file->f_op; file->f_op = new_fops; if (file->f_op->open) { - file->private_data = c; err=file->f_op->open(inode,file); if (err) { fops_put(file->f_op); From 4906c4776165bbf95ec535fb71324dad3d296f0b Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 10 Feb 2010 21:11:42 -0800 Subject: [PATCH 0590/2556] [ARM] msm: nand_part: Pass partition size/offset in blocks instead of bytes This is necessary in order to support devices with different page sizes. Change-Id: I466aa95f4873ce7b0e09bd454645b1af2016ee76 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/nand_partitions.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/nand_partitions.c b/arch/arm/mach-msm/nand_partitions.c index 14c579264ec33..72d3f8ba6bae0 100644 --- a/arch/arm/mach-msm/nand_partitions.c +++ b/arch/arm/mach-msm/nand_partitions.c @@ -75,8 +75,8 @@ static int __init parse_tag_msm_partition(const struct tag *tag) have_kpanic = 1; ptn->name = name; - ptn->offset = entry->offset * 64 * 2048; - ptn->size = entry->size * 64 * 2048; + ptn->offset = entry->offset; + ptn->size = entry->size; name += 16; entry++; From c8e67874bb99db2609ce2b33f7fd1799ddc14a31 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 10 Feb 2010 21:22:03 -0800 Subject: [PATCH 0591/2556] [ARM] mtd: msm_nand: platform partition data is now in units of blocks, not bytes Change-Id: Id7848cae982d882c66acc6f5572804dbe531b9d5 Signed-off-by: Dima Zavin --- drivers/mtd/devices/msm_nand.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index 4ae24d1bdbdbd..86825308dce70 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -1574,9 +1574,13 @@ static int __devinit msm_nand_probe(struct platform_device *pdev) err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); if (err > 0) add_mtd_partitions(&info->mtd, info->parts, err); - else if (err <= 0 && pdata && pdata->parts) + else if (err <= 0 && pdata && pdata->parts) { + for (i = 0; i < pdata->nr_parts; ++i) { + pdata->parts[i].offset *= info->mtd.erasesize; + pdata->parts[i].size *= info->mtd.erasesize; + } add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); - else + } else #endif err = add_mtd_device(&info->mtd); From b7bbb203b8ca75eadd37ec911cb7545f227ae031 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 22 Jan 2010 02:20:05 -0800 Subject: [PATCH 0592/2556] [ARM] qsd8x50: rename acpuclock-scorpion to acpuclock-qsd8x50 Newer devices with the scoprion have different pll/clock control, so the current code is really only representative of 8x50. Name it as such. Change-Id: I3499c8715e8c28c59fb49d06a9113f3f33ca90cc Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Makefile | 4 +++- .../mach-msm/{acpuclock-scorpion.c => acpuclock-qsd8x50.c} | 0 2 files changed, 3 insertions(+), 1 deletion(-) rename arch/arm/mach-msm/{acpuclock-scorpion.c => acpuclock-qsd8x50.c} (100%) diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index f653f2e4f8fbb..cbb0cb14064e2 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -8,9 +8,11 @@ obj-y += nand_partitions.o obj-y += pmic.o obj-$(CONFIG_ARCH_MSM_ARM11) += acpuclock-arm11.o idle.o -obj-$(CONFIG_ARCH_MSM_SCORPION) += acpuclock-scorpion.o idle-v7.o +obj-$(CONFIG_ARCH_MSM_SCORPION) += idle-v7.o obj-$(CONFIG_ARCH_MSM_SCORPION) += arch-init-scorpion.o +obj-$(CONFIG_ARCH_QSD8X50) += acpuclock-qsd8x50.o + ifndef CONFIG_ARCH_MSM8X60 obj-y += irq.o endif diff --git a/arch/arm/mach-msm/acpuclock-scorpion.c b/arch/arm/mach-msm/acpuclock-qsd8x50.c similarity index 100% rename from arch/arm/mach-msm/acpuclock-scorpion.c rename to arch/arm/mach-msm/acpuclock-qsd8x50.c From 1070f714f1dfe8b32888d1cbcd6ecd02e0af6318 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 28 Dec 2010 12:44:44 -0800 Subject: [PATCH 0593/2556] ARM: msm: irq: add msm7x30 support Change-Id: I3763c3345509a8c67de9658c1b39005c50b5bf38 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/irq.c | 187 ++++++++++++++++++++++------------------ 1 file changed, 101 insertions(+), 86 deletions(-) diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c index 69914557194b6..98f060d17bdff 100644 --- a/arch/arm/mach-msm/irq.c +++ b/arch/arm/mach-msm/irq.c @@ -45,19 +45,14 @@ static int msm_irq_debug_mask; module_param_named(debug_mask, msm_irq_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); #define VIC_REG(off) (MSM_VIC_BASE + (off)) - -#define VIC_INT_SELECT0 VIC_REG(0x0000) /* 1: FIQ, 0: IRQ */ -#define VIC_INT_SELECT1 VIC_REG(0x0004) /* 1: FIQ, 0: IRQ */ -#define VIC_INT_EN0 VIC_REG(0x0010) -#define VIC_INT_EN1 VIC_REG(0x0014) -#define VIC_INT_ENCLEAR0 VIC_REG(0x0020) -#define VIC_INT_ENCLEAR1 VIC_REG(0x0024) -#define VIC_INT_ENSET0 VIC_REG(0x0030) -#define VIC_INT_ENSET1 VIC_REG(0x0034) -#define VIC_INT_TYPE0 VIC_REG(0x0040) /* 1: EDGE, 0: LEVEL */ -#define VIC_INT_TYPE1 VIC_REG(0x0044) /* 1: EDGE, 0: LEVEL */ -#define VIC_INT_POLARITY0 VIC_REG(0x0050) /* 1: NEG, 0: POS */ -#define VIC_INT_POLARITY1 VIC_REG(0x0054) /* 1: NEG, 0: POS */ +#define __bank(irq) (((irq) / 32) & 0x3) + +#define VIC_INT_SELECT(n) VIC_REG(0x0000+((n) * 4)) /* 1: FIQ, 0: IRQ */ +#define VIC_INT_EN(n) VIC_REG(0x0010+((n) * 4)) +#define VIC_INT_ENCLEAR(n) VIC_REG(0x0020+((n) * 4)) +#define VIC_INT_ENSET(n) VIC_REG(0x0030+((n) * 4)) +#define VIC_INT_TYPE(n) VIC_REG(0x0040+((n) * 4)) /* 1: EDGE, 0: LEVEL */ +#define VIC_INT_POLARITY(n) VIC_REG(0x0050+((n) * 4)) /* 1: NEG, 0: POS */ #define VIC_NO_PEND_VAL VIC_REG(0x0060) #if defined(CONFIG_ARCH_MSM_SCORPION) @@ -69,16 +64,11 @@ module_param_named(debug_mask, msm_irq_debug_mask, int, S_IRUGO | S_IWUSR | S_IW #define VIC_CONFIG VIC_REG(0x0068) /* 1: USE ARM1136 VIC */ #define VIC_PROTECTION VIC_REG(0x006C) /* 1: ENABLE */ #endif -#define VIC_IRQ_STATUS0 VIC_REG(0x0080) -#define VIC_IRQ_STATUS1 VIC_REG(0x0084) -#define VIC_FIQ_STATUS0 VIC_REG(0x0090) -#define VIC_FIQ_STATUS1 VIC_REG(0x0094) -#define VIC_RAW_STATUS0 VIC_REG(0x00A0) -#define VIC_RAW_STATUS1 VIC_REG(0x00A4) -#define VIC_INT_CLEAR0 VIC_REG(0x00B0) -#define VIC_INT_CLEAR1 VIC_REG(0x00B4) -#define VIC_SOFTINT0 VIC_REG(0x00C0) -#define VIC_SOFTINT1 VIC_REG(0x00C4) +#define VIC_IRQ_STATUS(n) VIC_REG(0x0080+((n) * 4)) +#define VIC_FIQ_STATUS(n) VIC_REG(0x0090+((n) * 4)) +#define VIC_RAW_STATUS(n) VIC_REG(0x00A0+((n) * 4)) +#define VIC_INT_CLEAR(n) VIC_REG(0x00B0+((n) * 4)) +#define VIC_SOFTINT(n) VIC_REG(0x00C0+((n) * 4)) #define VIC_IRQ_VEC_RD VIC_REG(0x00D0) /* pending int # */ #define VIC_IRQ_VEC_PEND_RD VIC_REG(0x00D4) /* pending vector addr */ #define VIC_IRQ_VEC_WR VIC_REG(0x00D8) @@ -102,14 +92,20 @@ module_param_named(debug_mask, msm_irq_debug_mask, int, S_IRUGO | S_IWUSR | S_IW #define VIC_VECTPRIORITY(n) VIC_REG(0x0200+((n) * 4)) #define VIC_VECTADDR(n) VIC_REG(0x0400+((n) * 4)) +#if defined(CONFIG_ARCH_MSM7X30) +#define VIC_NUM_BANKS 4 +#else +#define VIC_NUM_BANKS 2 +#endif + static uint32_t msm_irq_smsm_wake_enable[2]; static struct { uint32_t int_en[2]; uint32_t int_type; uint32_t int_polarity; uint32_t int_select; -} msm_irq_shadow_reg[2]; -static uint32_t msm_irq_idle_disable[2]; +} msm_irq_shadow_reg[VIC_NUM_BANKS]; +static uint32_t msm_irq_idle_disable[VIC_NUM_BANKS]; #ifndef CONFIG_ARCH_MSM_SCORPION #define INT_INFO_SMSM_ID SMEM_SMSM_INT_INFO @@ -145,7 +141,9 @@ static uint8_t msm_irq_to_smsm[NR_MSM_IRQS + NR_SIRC_IRQS] = { [INT_UART1DM_IRQ] = 17, [INT_UART1DM_RX] = 18, [INT_KEYSENSE] = 19, +#if !defined(CONFIG_ARCH_MSM7X30) [INT_AD_HSSD] = 20, +#endif [INT_NAND_WR_ER_DONE] = 21, [INT_NAND_OP_DONE] = 22, @@ -171,7 +169,8 @@ static uint8_t msm_irq_to_smsm[NR_MSM_IRQS + NR_SIRC_IRQS] = { [INT_GP_TIMER_EXP] = SMSM_FAKE_IRQ, [INT_DEBUG_TIMER_EXP] = SMSM_FAKE_IRQ, [INT_ADSP_A11] = SMSM_FAKE_IRQ, -#ifdef CONFIG_ARCH_MSM_SCORPION + +#if defined(CONFIG_ARCH_QSD8X50) [INT_SIRC_0] = SMSM_FAKE_IRQ, [INT_SIRC_1] = SMSM_FAKE_IRQ, #endif @@ -179,14 +178,14 @@ static uint8_t msm_irq_to_smsm[NR_MSM_IRQS + NR_SIRC_IRQS] = { static void msm_irq_ack(struct irq_data *d) { - void __iomem *reg = VIC_INT_CLEAR0 + ((d->irq & 32) ? 4 : 0); + void __iomem *reg = VIC_INT_CLEAR(__bank(d->irq)); writel(1 << (d->irq & 31), reg); } static void msm_irq_mask(struct irq_data *d) { - void __iomem *reg = VIC_INT_ENCLEAR0 + ((d->irq & 32) ? 4 : 0); - unsigned index = (d->irq >> 5) & 1; + void __iomem *reg = VIC_INT_ENCLEAR(__bank(d->irq)); + unsigned index = __bank(d->irq); uint32_t mask = 1UL << (d->irq & 31); int smsm_irq = msm_irq_to_smsm[d->irq]; @@ -202,8 +201,8 @@ static void msm_irq_mask(struct irq_data *d) static void msm_irq_unmask(struct irq_data *d) { - void __iomem *reg = VIC_INT_ENSET0 + ((d->irq & 32) ? 4 : 0); - unsigned index = (d->irq >> 5) & 1; + void __iomem *reg = VIC_INT_ENSET(__bank(d->irq)); + unsigned index = __bank(d->irq); uint32_t mask = 1UL << (d->irq & 31); int smsm_irq = msm_irq_to_smsm[d->irq]; @@ -221,7 +220,7 @@ static void msm_irq_unmask(struct irq_data *d) static int msm_irq_set_wake(struct irq_data *d, unsigned int on) { unsigned irq = d->irq; - unsigned index = (irq >> 5) & 1; + unsigned index = __bank(irq); uint32_t mask = 1UL << (irq & 31); int smsm_irq = msm_irq_to_smsm[irq]; @@ -247,9 +246,9 @@ static int msm_irq_set_wake(struct irq_data *d, unsigned int on) static int msm_irq_set_type(struct irq_data *d, unsigned int flow_type) { - void __iomem *treg = VIC_INT_TYPE0 + ((d->irq & 32) ? 4 : 0); - void __iomem *preg = VIC_INT_POLARITY0 + ((d->irq & 32) ? 4 : 0); - unsigned index = (d->irq >> 5) & 1; + void __iomem *treg = VIC_INT_TYPE(__bank(d->irq)); + void __iomem *preg = VIC_INT_POLARITY(__bank(d->irq)); + unsigned index = __bank(d->irq); int b = 1 << (d->irq & 31); uint32_t polarity; uint32_t type; @@ -278,16 +277,26 @@ static int msm_irq_set_type(struct irq_data *d, unsigned int flow_type) int msm_irq_pending(void) { - return readl(VIC_IRQ_STATUS0) || readl(VIC_IRQ_STATUS1); + int i; + + for (i = 0; i < VIC_NUM_BANKS; ++i) + if (readl(VIC_IRQ_STATUS(i))) + return 1; + return 0; } int msm_irq_idle_sleep_allowed(void) { + int i; + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_REQUEST) printk(KERN_INFO "msm_irq_idle_sleep_allowed: disable %x %x\n", msm_irq_idle_disable[0], msm_irq_idle_disable[1]); - return !(msm_irq_idle_disable[0] || msm_irq_idle_disable[1] || - !smsm_int_info); + + for (i = 0; i < VIC_NUM_BANKS; ++i) + if (msm_irq_idle_disable[i]) + return 0; + return !!smsm_int_info; } /* If arm9_wake is set: pass control to the other core. @@ -304,7 +313,9 @@ void msm_irq_enter_sleep1(bool arm9_wake, int from_idle) int msm_irq_enter_sleep2(bool arm9_wake, int from_idle) { int limit = 10; - uint32_t pending0, pending1; + uint32_t pending[VIC_NUM_BANKS]; + int i; + uint32_t any = 0; if (from_idle && !arm9_wake) return 0; @@ -314,22 +325,28 @@ int msm_irq_enter_sleep2(bool arm9_wake, int from_idle) if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) printk(KERN_INFO "msm_irq_enter_sleep change irq, pend %x %x\n", - readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1)); - pending0 = readl(VIC_IRQ_STATUS0); - pending1 = readl(VIC_IRQ_STATUS1); - pending0 &= msm_irq_shadow_reg[0].int_en[!from_idle]; + readl(VIC_IRQ_STATUS(0)), readl(VIC_IRQ_STATUS(1))); + pending[0] = readl(VIC_IRQ_STATUS(0)); + pending[0] &= msm_irq_shadow_reg[0].int_en[!from_idle]; /* Clear INT_A9_M2A_5 since requesting sleep triggers it */ - pending0 &= ~(1U << INT_A9_M2A_5); - pending1 &= msm_irq_shadow_reg[1].int_en[!from_idle]; - if (pending0 || pending1) { + pending[0] &= ~(1U << INT_A9_M2A_5); + any = pending[0]; + + for (i = 1; i < VIC_NUM_BANKS; ++i) { + pending[i] = readl(VIC_IRQ_STATUS(i)); + pending[i] &= msm_irq_shadow_reg[i].int_en[!from_idle]; + any |= pending[i]; + } + + if (any) { if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_ABORT) printk(KERN_INFO "msm_irq_enter_sleep2 abort %x %x\n", - pending0, pending1); + pending[0], pending[1]); return -EAGAIN; } - - writel(0, VIC_INT_EN0); - writel(0, VIC_INT_EN1); + + for (i = 0; i < VIC_NUM_BANKS; ++i) + writel(0, VIC_INT_EN(i)); while (limit-- > 0) { int pend_irq; @@ -346,10 +363,10 @@ int msm_irq_enter_sleep2(bool arm9_wake, int from_idle) msm_irq_set_type(irq_get_irq_data(INT_A9_M2A_6), IRQF_TRIGGER_RISING); msm_irq_ack(irq_get_irq_data(INT_A9_M2A_6)); - writel(1U << INT_A9_M2A_6, VIC_INT_ENSET0); + writel(1U << INT_A9_M2A_6, VIC_INT_ENSET(0)); } else { - writel(msm_irq_shadow_reg[0].int_en[1], VIC_INT_ENSET0); - writel(msm_irq_shadow_reg[1].int_en[1], VIC_INT_ENSET1); + for (i = 0; i < VIC_NUM_BANKS; ++i) + writel(msm_irq_shadow_reg[i].int_en[1], VIC_INT_ENSET(i)); } return 0; } @@ -360,11 +377,11 @@ void msm_irq_exit_sleep1(void) msm_irq_ack(irq_get_irq_data(INT_A9_M2A_6)); msm_irq_ack(irq_get_irq_data(INT_PWB_I2C)); - for (i = 0; i < 2; i++) { - writel(msm_irq_shadow_reg[i].int_type, VIC_INT_TYPE0 + i * 4); - writel(msm_irq_shadow_reg[i].int_polarity, VIC_INT_POLARITY0 + i * 4); - writel(msm_irq_shadow_reg[i].int_en[0], VIC_INT_EN0 + i * 4); - writel(msm_irq_shadow_reg[i].int_select, VIC_INT_SELECT0 + i * 4); + for (i = 0; i < VIC_NUM_BANKS; i++) { + writel(msm_irq_shadow_reg[i].int_type, VIC_INT_TYPE(i)); + writel(msm_irq_shadow_reg[i].int_polarity, VIC_INT_POLARITY(i)); + writel(msm_irq_shadow_reg[i].int_en[0], VIC_INT_EN(i)); + writel(msm_irq_shadow_reg[i].int_select, VIC_INT_SELECT(i)); } writel(3, VIC_INT_MASTEREN); if (!smsm_int_info) { @@ -376,7 +393,7 @@ void msm_irq_exit_sleep1(void) smsm_int_info->interrupt_mask, smsm_int_info->pending_interrupts, smsm_int_info->wakeup_reason, - readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1)); + readl(VIC_IRQ_STATUS(0)), readl(VIC_IRQ_STATUS(1))); } void msm_irq_exit_sleep2(void) @@ -393,10 +410,10 @@ void msm_irq_exit_sleep2(void) smsm_int_info->interrupt_mask, smsm_int_info->pending_interrupts, smsm_int_info->wakeup_reason, - readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1)); + readl(VIC_IRQ_STATUS(0)), readl(VIC_IRQ_STATUS(1))); pending = smsm_int_info->pending_interrupts; for (i = 0; pending && i < ARRAY_SIZE(msm_irq_to_smsm); i++) { - unsigned reg_offset = (i & 32) ? 4 : 0; + unsigned reg_offset = VIC_IRQ_STATUS(__bank(i)) - VIC_IRQ_STATUS(0); uint32_t reg_mask = 1UL << (i & 31); int smsm_irq = msm_irq_to_smsm[i]; uint32_t smsm_mask; @@ -409,18 +426,18 @@ void msm_irq_exit_sleep2(void) if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT) printk(KERN_INFO "msm_irq_exit_sleep2: irq %d " "still pending %x now %x %x\n", i, pending, - readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1)); + readl(VIC_IRQ_STATUS(0)), readl(VIC_IRQ_STATUS(1))); #if 0 /* debug intetrrupt trigger */ - if (readl(VIC_IRQ_STATUS0 + reg_offset) & reg_mask) - writel(reg_mask, VIC_INT_CLEAR0 + reg_offset); + if (readl(VIC_IRQ_STATUS(0) + reg_offset) & reg_mask) + writel(reg_mask, VIC_INT_CLEAR(0) + reg_offset); #endif - if (readl(VIC_IRQ_STATUS0 + reg_offset) & reg_mask) + if (readl(VIC_IRQ_STATUS(0) + reg_offset) & reg_mask) continue; - writel(reg_mask, VIC_SOFTINT0 + reg_offset); + writel(reg_mask, VIC_SOFTINT(0) + reg_offset); if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT_TRIGGER) printk(KERN_INFO "msm_irq_exit_sleep2: irq %d need " "trigger, now %x %x\n", i, - readl(VIC_IRQ_STATUS0), readl(VIC_IRQ_STATUS1)); + readl(VIC_IRQ_STATUS(0)), readl(VIC_IRQ_STATUS(1))); } } @@ -434,8 +451,8 @@ void msm_irq_exit_sleep3(void) printk(KERN_INFO "msm_irq_exit_sleep3 %x %x %x now %x %x " "state %x\n", smsm_int_info->interrupt_mask, smsm_int_info->pending_interrupts, - smsm_int_info->wakeup_reason, readl(VIC_IRQ_STATUS0), - readl(VIC_IRQ_STATUS1), + smsm_int_info->wakeup_reason, readl(VIC_IRQ_STATUS(0)), + readl(VIC_IRQ_STATUS(1)), smsm_get_state(SMSM_STATE_MODEM)); } @@ -453,21 +470,19 @@ void __init msm_init_irq(void) { unsigned n; - /* select level interrupts */ - writel(0, VIC_INT_TYPE0); - writel(0, VIC_INT_TYPE1); + for (n = 0; n < VIC_NUM_BANKS; ++n) { + /* select level interrupts */ + writel(0, VIC_INT_TYPE(n)); - /* select highlevel interrupts */ - writel(0, VIC_INT_POLARITY0); - writel(0, VIC_INT_POLARITY1); + /* select highlevel interrupts */ + writel(0, VIC_INT_POLARITY(n)); - /* select IRQ for all INTs */ - writel(0, VIC_INT_SELECT0); - writel(0, VIC_INT_SELECT1); + /* select IRQ for all INTs */ + writel(0, VIC_INT_SELECT(n)); - /* disable all INTs */ - writel(0, VIC_INT_EN0); - writel(0, VIC_INT_EN1); + /* disable all INTs */ + writel(0, VIC_INT_EN(n)); + } /* don't use 1136 vic */ writel(0, VIC_CONFIG); @@ -496,7 +511,7 @@ late_initcall(msm_init_irq_late); #if defined(CONFIG_MSM_FIQ_SUPPORT) void msm_trigger_irq(int irq) { - void __iomem *reg = VIC_SOFTINT0 + ((irq & 32) ? 4 : 0); + void __iomem *reg = VIC_SOFTINT(__bank(irq)); uint32_t mask = 1UL << (irq & 31); writel(mask, reg); } @@ -521,8 +536,8 @@ void msm_fiq_disable(int irq) static void _msm_fiq_select(int irq) { - void __iomem *reg = VIC_INT_SELECT0 + ((irq & 32) ? 4 : 0); - unsigned index = (irq >> 5) & 1; + void __iomem *reg = VIC_INT_SELECT(__bank(irq)); + unsigned index = __bank(irq); uint32_t mask = 1UL << (irq & 31); unsigned long flags; @@ -534,8 +549,8 @@ static void _msm_fiq_select(int irq) static void _msm_fiq_unselect(int irq) { - void __iomem *reg = VIC_INT_SELECT0 + ((irq & 32) ? 4 : 0); - unsigned index = (irq >> 5) & 1; + void __iomem *reg = VIC_INT_SELECT(__bank(irq)); + unsigned index = __bank(irq); uint32_t mask = 1UL << (irq & 31); unsigned long flags; From c3c482597734ca7c6a811af32fd953bc06a7f1e2 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 28 Dec 2010 12:46:29 -0800 Subject: [PATCH 0594/2556] ARM: msm: acpuclock: add a stub msm7x30 implementation Change-Id: Id4c0af2b2812a8f2c0198eeebbf2632c1638d678 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/acpuclock-7x30.c | 70 ++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 arch/arm/mach-msm/acpuclock-7x30.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index cbb0cb14064e2..ccb7de2ff6ee3 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_ARCH_MSM_SCORPION) += idle-v7.o obj-$(CONFIG_ARCH_MSM_SCORPION) += arch-init-scorpion.o obj-$(CONFIG_ARCH_QSD8X50) += acpuclock-qsd8x50.o +obj-$(CONFIG_ARCH_MSM7X30) += acpuclock-7x30.o ifndef CONFIG_ARCH_MSM8X60 obj-y += irq.o diff --git a/arch/arm/mach-msm/acpuclock-7x30.c b/arch/arm/mach-msm/acpuclock-7x30.c new file mode 100644 index 0000000000000..03087e06ac498 --- /dev/null +++ b/arch/arm/mach-msm/acpuclock-7x30.c @@ -0,0 +1,70 @@ +/* + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. + * + * 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 "acpuclock.h" + +unsigned long acpuclk_power_collapse(void) +{ + return 0; +} + +unsigned long acpuclk_wait_for_irq(void) +{ + return 0; +} + +unsigned long acpuclk_get_wfi_rate(void) +{ + return 0; +} + +int acpuclk_set_rate(unsigned long rate, int for_power_collapse) +{ + return 0; +} + +unsigned long acpuclk_get_rate(void) +{ + return 0; +} + +uint32_t acpuclk_get_switch_time(void) +{ + return 0; +} + +static void __init acpuclk_init(void) +{ +} + +void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *clkdata) +{ + pr_info("acpu_clock_init()\n"); + acpuclk_init(); +} From 9388ee9092180cfd0a151cfff433eb49deec306d Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 28 Dec 2010 12:47:09 -0800 Subject: [PATCH 0595/2556] ARM: msm: provide correct load addrs for msm7x30 Change-Id: Id55f2f43446de5f0993b659f76c6eb63592f6d00 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Makefile.boot | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot index 326672707ce7c..94c82c6004557 100644 --- a/arch/arm/mach-msm/Makefile.boot +++ b/arch/arm/mach-msm/Makefile.boot @@ -6,3 +6,7 @@ initrd_phys-y := 0x10800000 zreladdr-$(CONFIG_ARCH_QSD8X50) := 0x20008000 params_phys-$(CONFIG_ARCH_QSD8X50) := 0x20000100 initrd_phys-$(CONFIG_ARCH_QSD8X50) := 0x21000000 + + zreladdr-$(CONFIG_ARCH_MSM7X30) := 0x00208000 +params_phys-$(CONFIG_ARCH_MSM7X30) := 0x00200100 +initrd_phys-$(CONFIG_ARCH_MSM7X30) := 0x0A000000 From 11c2b29f4306db39a20a69ed3642f6315ebcf77c Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 28 Jan 2010 23:19:44 -0800 Subject: [PATCH 0596/2556] [ARM] msm: add msm7x30 SURF support Change-Id: I62ea4a6d0aaa12fbb91906c455085305b27555ba Signed-off-by: Dima Zavin [ARM] msm: configs: update surf7x30 defconfig Change-Id: Ie5f7c6188709c6b2de0eb76c0e938d03e3c22fb1 Signed-off-by: Dima Zavin [ARM] msm: configs: update surf7x30 config to not use mem= Change-Id: I1d06b4b35b44f113878c9e317a5beda3809a3f7b Signed-off-by: Dima Zavin --- arch/arm/configs/surf7x30_defconfig | 210 +++++++++++++++++++++ arch/arm/mach-msm/Kconfig | 7 + arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/board-surf7x30.c | 276 ++++++++++++++++++++++++++++ 4 files changed, 494 insertions(+) create mode 100644 arch/arm/configs/surf7x30_defconfig create mode 100644 arch/arm/mach-msm/board-surf7x30.c diff --git a/arch/arm/configs/surf7x30_defconfig b/arch/arm/configs/surf7x30_defconfig new file mode 100644 index 0000000000000..4538e223214d1 --- /dev/null +++ b/arch/arm/configs/surf7x30_defconfig @@ -0,0 +1,210 @@ +CONFIG_EXPERIMENTAL=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_CGROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EMBEDDED=y +# CONFIG_SYSCTL_SYSCALL is not set +# CONFIG_ELF_CORE is not set +CONFIG_ASHMEM=y +CONFIG_SLAB=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_ARCH_MSM=y +CONFIG_ARCH_MSM7X30=y +CONFIG_MSM_DEBUG_UART1=y +# CONFIG_HTC_PWRSPLY is not set +# CONFIG_HTC_PWRSINK is not set +CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT=y +CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT=y +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000 +# CONFIG_MSM_FIQ_SUPPORT is not set +# CONFIG_MSM_HW3D is not set +CONFIG_WIFI_CONTROL_FUNC=y +CONFIG_ARM_THUMBEE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_HIGHMEM=y +CONFIG_VMALLOC_RESERVE=0x30000000 +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="console=ttyMSM,115200n8" +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_PM=y +CONFIG_WAKELOCK=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_INET_ESP=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_LL=y +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_MTD=y +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_KERNEL_DEBUGGER_CORE=y +CONFIG_UID_STAT=y +CONFIG_APANIC=y +CONFIG_APANIC_PLABEL="crashdata" +CONFIG_MD=y +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +CONFIG_NET_ETHERNET=y +CONFIG_SMC91X=y +CONFIG_PPP=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +# CONFIG_INPUT_MOUSEDEV is not set +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYRESET=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_MSM=y +CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_KEYCHORD=y +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +CONFIG_INPUT_CAPELLA_CM3602=y +# CONFIG_SERIO is not set +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_HW_RANDOM is not set +CONFIG_I2C=y +CONFIG_W1=y +CONFIG_W1_MASTER_DS2482=y +CONFIG_POWER_SUPPLY=y +# CONFIG_HWMON is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +CONFIG_MEDIA_SUPPORT=y +CONFIG_VIDEO_DEV=y +# CONFIG_VIDEO_ALLOW_V4L1 is not set +CONFIG_DAB=y +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_USB_GADGET=y +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_ANDROID=y +CONFIG_USB_ANDROID_ACM=y +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_DIAG=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_SLEEP=y +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +# CONFIG_DNOTIFY is not set +CONFIG_INOTIFY=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_YAFFS_FS=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_PRINTK_TIME=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_KERNEL=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +CONFIG_DEBUG_SLAB=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_SPINLOCK_SLEEP=y +CONFIG_DEBUG_HIGHMEM=y +CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_VM=y +CONFIG_DEBUG_SG=y +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_TWOFISH=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 57b8d790d244c..3080f8f4b9e9c 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -221,6 +221,13 @@ config MACH_QSD8X50_FFA help Select this to support the 8x50 ffa device +config MACH_SURF7X30 + depends on ARCH_MSM7X30 + default y + bool "QCT SURF7x30 Board" + help + Select this to support the Qualcomm SURF7X30 development board + config HTC_HEADSET tristate "HTC 2 Wire detection driver" default n diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index ccb7de2ff6ee3..0af423364cda7 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-keypad.o fish_battery.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-panel.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-mmc.o +obj-$(CONFIG_MACH_SURF7X30) += board-surf7x30.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o obj-$(CONFIG_ARCH_QSD8X50) += board-qsd8x50.o devices-qsd8x50.o obj-$(CONFIG_ARCH_MSM8X60) += board-msm8x60.o diff --git a/arch/arm/mach-msm/board-surf7x30.c b/arch/arm/mach-msm/board-surf7x30.c new file mode 100644 index 0000000000000..e90be714aab75 --- /dev/null +++ b/arch/arm/mach-msm/board-surf7x30.c @@ -0,0 +1,276 @@ +/* linux/arch/arm/mach-msm/board-surf7x30.c + * + * Copyright (C) 2010 Google, Inc. + * Author: Dima Zavin + * + * 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 "devices.h" +#include "proc_comm.h" + +static struct resource smc91x_resources[] = { + [0] = { + .start = 0x8A000300, + .end = 0x8A0003ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MSM_GPIO_TO_INT(156), + .end = MSM_GPIO_TO_INT(156), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static int surf7x30_phy_init_seq[] = { + 0x0C, 0x31, + 0x31, 0x32, + 0x1D, 0x0D, + 0x1D, 0x10, + -1 }; + +static void surf7x30_usb_phy_reset(void) +{ + u32 id; + int ret; + + id = PCOM_CLKRGM_APPS_RESET_USB_PHY; + ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_ASSERT, &id, NULL); + if (ret) { + pr_err("%s: Cannot assert (%d)\n", __func__, ret); + return; + } + + msleep(1); + + id = PCOM_CLKRGM_APPS_RESET_USB_PHY; + ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_DEASSERT, &id, NULL); + if (ret) { + pr_err("%s: Cannot assert (%d)\n", __func__, ret); + return; + } +} + +static void surf7x30_usb_hw_reset(bool enable) +{ + u32 id; + int ret; + u32 func; + + id = PCOM_CLKRGM_APPS_RESET_USBH; + if (enable) + func = PCOM_CLK_REGIME_SEC_RESET_ASSERT; + else + func = PCOM_CLK_REGIME_SEC_RESET_DEASSERT; + ret = msm_proc_comm(func, &id, NULL); + if (ret) + pr_err("%s: Cannot set reset to %d (%d)\n", __func__, enable, + ret); +} + +static struct msm_hsusb_platform_data msm_hsusb_pdata = { + .phy_init_seq = surf7x30_phy_init_seq, + .phy_reset = surf7x30_usb_phy_reset, + .hw_reset = surf7x30_usb_hw_reset, +}; + +static char *usb_functions[] = { + "usb_mass_storage", +#ifdef CONFIG_USB_ANDROID_RNDIS + "rndis", +#endif +#ifdef CONFIG_USB_ANDROID_ACM + "acm", +#endif +}; + +static char *usb_functions_adb[] = { + "usb_mass_storage", + "adb", +#ifdef CONFIG_USB_ANDROID_RNDIS + "rndis", +#endif +#ifdef CONFIG_USB_ANDROID_ACM + "acm", +#endif +}; + +#ifdef CONFIG_USB_ANDROID_DIAG +static char *usb_functions_adb_diag[] = { + "usb_mass_storage", + "adb", + "diag", +}; +#endif + +static char *usb_functions_all[] = { + "usb_mass_storage", + "adb", +#ifdef CONFIG_USB_ANDROID_ACM + "acm", +#endif +#ifdef CONFIG_USB_ANDROID_RNDIS + "rndis", +#endif +#ifdef CONFIG_USB_ANDROID_DIAG + "diag", +#endif +}; + +static struct android_usb_product usb_products[] = { + { +#ifdef CONFIG_USB_ANDROID_ACM + .product_id = 0x4e21, +#else + .product_id = 0x4e11, +#endif + .num_functions = ARRAY_SIZE(usb_functions), + .functions = usb_functions, + }, + { +#ifdef CONFIG_USB_ANDROID_ACM + .product_id = 0x4e22, +#else + .product_id = 0x4e12, +#endif + .num_functions = ARRAY_SIZE(usb_functions_adb), + .functions = usb_functions_adb, + }, +#ifdef CONFIG_USB_ANDROID_DIAG + { + .product_id = 0x4e17, + .num_functions = ARRAY_SIZE(usb_functions_adb_diag), + .functions = usb_functions_adb_diag, + }, +#endif +}; + +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .vendor = "Qualcomm, Inc.", + .product = "Surf7x30", + .release = 0x0100, +}; + +static struct platform_device usb_mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &mass_storage_pdata, + }, +}; + +static struct android_usb_platform_data android_usb_pdata = { + .vendor_id = 0x18d1, + .product_id = 0x4e11, + .version = 0x0100, + .product_name = "Surf7x30", + .manufacturer_name = "Qualcomm, Inc.", + .num_products = ARRAY_SIZE(usb_products), + .products = usb_products, + .num_functions = ARRAY_SIZE(usb_functions_all), + .functions = usb_functions_all, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; + +static struct platform_device *devices[] __initdata = { +#if !defined(CONFIG_MSM_SERIAL_DEBUGGER) + &msm_device_uart1, +#endif + &msm_device_smd, + &msm_device_nand, + &msm_device_hsusb, + &usb_mass_storage_device, + &android_usb_device, + &smc91x_device, +}; + +extern struct sys_timer msm_timer; + +void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq); + +static void __init surf7x30_init(void) +{ + printk("%s()\n", __func__); + +#if defined(CONFIG_MSM_SERIAL_DEBUGGER) + msm_serial_debug_init(MSM_UART1_PHYS, INT_UART1, + &msm_device_uart1.dev, 1); +#endif + + msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; + platform_add_devices(devices, ARRAY_SIZE(devices)); + msm_hsusb_set_vbus_state(1); + msm_hsusb_set_vbus_state(0); + msm_hsusb_set_vbus_state(1); +} + +static void __init surf7x30_fixup(struct machine_desc *desc, struct tag *tags, + char **cmdline, struct meminfo *mi) +{ + mi->nr_banks = 1; + mi->bank[0].start = PHYS_OFFSET; + mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); + mi->bank[0].size = (101*1024*1024); +} + +static void __init surf7x30_map_io(void) +{ + msm_map_msm7x30_io(); + msm_clock_init(msm_clocks_7x30, msm_num_clocks_7x30); +} + +MACHINE_START(MSM7X30_SURF, "QCT SURF7X30 Development Board") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x00200100, + .fixup = surf7x30_fixup, + .map_io = surf7x30_map_io, + .init_irq = msm_init_irq, + .init_machine = surf7x30_init, + .timer = &msm_timer, +MACHINE_END From d3f59903ee0b8ca91de08e4832263aa85a33b864 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 29 Jan 2010 19:02:57 -0800 Subject: [PATCH 0597/2556] arm: msm: use correct regs for scorpion powerup/powerdown for pm Adds prepare/restore functions to write the correct set of registers, instead of having a giant ifdef mess inline. Change-Id: I2b7057b422c631d71407c83d43f1b4651483fcdf Signed-off-by: Dima Zavin --- arch/arm/mach-msm/pm.c | 55 ++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index 2c426d46ce58c..a888d91bc1192 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -67,10 +67,16 @@ module_param_named(idle_sleep_min_time, msm_pm_idle_sleep_min_time, int, S_IRUGO static int msm_pm_idle_spin_time = CONFIG_MSM7X00A_IDLE_SPIN_TIME; module_param_named(idle_spin_time, msm_pm_idle_spin_time, int, S_IRUGO | S_IWUSR | S_IWGRP); +#if defined(CONFIG_ARCH_MSM7X30) +#define A11S_CLK_SLEEP_EN (MSM_GCC_BASE + 0x020) +#define A11S_PWRDOWN (MSM_ACC_BASE + 0x01c) +#define A11S_SECOP (MSM_TCSR_BASE + 0x038) +#else #define A11S_CLK_SLEEP_EN (MSM_CSR_BASE + 0x11c) #define A11S_PWRDOWN (MSM_CSR_BASE + 0x440) #define A11S_STANDBY_CTL (MSM_CSR_BASE + 0x108) #define A11RAMBACKBIAS (MSM_CSR_BASE + 0x508) +#endif #define DEM_MASTER_BITS_PER_CPU 6 @@ -201,6 +207,39 @@ msm_pm_wait_state(uint32_t wait_all_set, uint32_t wait_all_clear, return -ETIMEDOUT; } +static void +msm_pm_enter_prep_hw(void) +{ +#if defined(CONFIG_ARCH_MSM7X30) + writel(1, A11S_PWRDOWN); + writel(4, A11S_SECOP); +#else +#if defined(CONFIG_ARCH_QSD8X50) + writel(0x1b, A11S_CLK_SLEEP_EN); +#else + writel(0x1f, A11S_CLK_SLEEP_EN); +#endif + writel(1, A11S_PWRDOWN); + writel(0, A11S_STANDBY_CTL); + +#if defined(CONFIG_ARCH_MSM_ARM11) + writel(0, A11RAMBACKBIAS); +#endif +#endif +} + +static void +msm_pm_exit_restore_hw(void) +{ +#if defined(CONFIG_ARCH_MSM7X30) + writel(0, A11S_SECOP); + writel(0, A11S_PWRDOWN); +#else + writel(0x00, A11S_CLK_SLEEP_EN); + writel(0, A11S_PWRDOWN); +#endif +} + #ifdef CONFIG_MSM_FIQ_SUPPORT void msm_fiq_exit_sleep(void); #else @@ -325,17 +364,7 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) goto enter_failed; if (enter_state) { -#ifdef CONFIG_ARCH_MSM_SCORPION - writel(0x1b, A11S_CLK_SLEEP_EN); -#else - writel(0x1f, A11S_CLK_SLEEP_EN); -#endif - writel(1, A11S_PWRDOWN); - - writel(0, A11S_STANDBY_CTL); -#ifndef CONFIG_ARCH_MSM_SCORPION - writel(0, A11RAMBACKBIAS); -#endif + msm_pm_enter_prep_hw(); if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) printk(KERN_INFO "msm_sleep(): enter " @@ -408,8 +437,8 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) msm_irq_exit_sleep1(); enter_failed: if (enter_state) { - writel(0x00, A11S_CLK_SLEEP_EN); - writel(0, A11S_PWRDOWN); + msm_pm_exit_restore_hw(); + smsm_change_state(PM_SMSM_WRITE_STATE, enter_state, exit_state); msm_pm_wait_state(0, exit_wait_clear, exit_wait_any_set, 0); if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) From d12ff4b575f87a0e5c1187024bdef43ae872f51b Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 2 Feb 2010 19:39:56 -0800 Subject: [PATCH 0598/2556] arm: msm: usb: add usb core clk management, as found on 7x30 Change-Id: Iee547f15f581977eb9e4791470ca32bd9cfc5f79 Signed-off-by: Dima Zavin --- drivers/usb/gadget/msm72k_udc.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index f3669507bcd92..1d99b8134eb1a 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -176,6 +176,7 @@ struct usb_info { #define ep0in ept[16] struct clk *clk; + struct clk *coreclk; struct clk *pclk; struct clk *otgclk; struct clk *ebi1clk; @@ -1163,6 +1164,8 @@ static int usb_free(struct usb_info *ui, int ret) clk_put(ui->pclk); if (ui->otgclk) clk_put(ui->otgclk); + if (ui->coreclk) + clk_put(ui->coreclk); if (ui->ebi1clk) clk_put(ui->ebi1clk); kfree(ui); @@ -1205,6 +1208,8 @@ static void usb_do_work(struct work_struct *w) pr_info("msm72k_udc: IDLE -> ONLINE\n"); clk_set_rate(ui->ebi1clk, 128000000); udelay(10); + if (ui->coreclk) + clk_enable(ui->coreclk); clk_enable(ui->clk); clk_enable(ui->pclk); if (ui->otgclk) @@ -1249,6 +1254,8 @@ static void usb_do_work(struct work_struct *w) clk_disable(ui->clk); if (ui->otgclk) clk_disable(ui->otgclk); + if (ui->coreclk) + clk_disable(ui->coreclk); clk_set_rate(ui->ebi1clk, 0); spin_unlock_irqrestore(&ui->lock, iflags); @@ -1271,6 +1278,8 @@ static void usb_do_work(struct work_struct *w) pr_info("msm72k_udc: OFFLINE -> ONLINE\n"); clk_set_rate(ui->ebi1clk, 128000000); udelay(10); + if (ui->coreclk) + clk_enable(ui->coreclk); clk_enable(ui->clk); clk_enable(ui->pclk); if (ui->otgclk) @@ -1757,17 +1766,25 @@ static int msm72k_probe(struct platform_device *pdev) if (IS_ERR(ui->otgclk)) ui->otgclk = NULL; + ui->coreclk = clk_get(&pdev->dev, "usb_hs_core_clk"); + if (IS_ERR(ui->coreclk)) + ui->coreclk = NULL; + ui->ebi1clk = clk_get(NULL, "ebi1_clk"); if (IS_ERR(ui->ebi1clk)) return usb_free(ui, PTR_ERR(ui->ebi1clk)); /* clear interrupts before requesting irq */ + if (ui->coreclk) + clk_enable(ui->coreclk); clk_enable(ui->clk); clk_enable(ui->pclk); if (ui->otgclk) clk_enable(ui->otgclk); writel(0, USB_USBINTR); writel(0, USB_OTGSC); + if (ui->coreclk) + clk_disable(ui->coreclk); if (ui->otgclk) clk_disable(ui->otgclk); clk_disable(ui->pclk); From ccd09b8455b98dd822a6d25f6a975ed9f1b0668a Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 3 Feb 2010 18:07:05 -0800 Subject: [PATCH 0599/2556] arm: msm: usb: use the 8x50 phy init for 7x30 as well Change-Id: I4b70b3ecd59cbeaa3f1f6070a171f5a77436f7aa Signed-off-by: Dima Zavin --- drivers/usb/gadget/msm72k_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 1d99b8134eb1a..7ea8308371fb8 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -984,7 +984,7 @@ static void usb_prepare(struct usb_info *ui) static void usb_suspend_phy(struct usb_info *ui) { -#if defined(CONFIG_ARCH_QSD8X50) +#if defined(CONFIG_ARCH_QSD8X50) || defined(CONFIG_ARCH_MSM7X30) /* clear VBusValid and SessionEnd rising interrupts */ ulpi_write(ui, (1 << 1) | (1 << 3), 0x0f); /* clear VBusValid and SessionEnd falling interrupts */ From f8acef167328daa5137656b52b06a903fa246096 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 10 Feb 2010 21:15:28 -0800 Subject: [PATCH 0600/2556] [ARM] mtd: msm_nand: Add support for 4K page size NAND devices Signed-off-by: Dima Zavin --- drivers/mtd/devices/msm_nand.c | 146 +++++++++++++++++++++------------ 1 file changed, 95 insertions(+), 51 deletions(-) diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index 86825308dce70..d6f35ad8f3332 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -52,6 +52,9 @@ struct msm_nand_chip { uint8_t *dma_buffer; dma_addr_t dma_addr; unsigned CFG0, CFG1; + unsigned page_shift; + unsigned last_sector; + unsigned last_sectorsz; #if SUPPORT_WRONG_ECC_CONFIG uint32_t ecc_buf_cfg; uint32_t saved_ecc_buf_cfg; @@ -75,19 +78,23 @@ struct msm_nand_chip { * msm_nand_oob_64 - oob info for large (2KB) page */ static struct nand_ecclayout msm_nand_oob_64 = { - .eccbytes = 40, - .eccpos = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - }, .oobavail = 16, .oobfree = { {30, 16}, } }; +/* + * msm_nand_oob_128 - oob info for 4KB page + */ +static struct nand_ecclayout msm_nand_oob_128 = { + .oobavail = 32, + .oobfree = { + {70, 32}, + } +}; + + static void *msm_nand_get_dma_buffer(struct msm_nand_chip *chip, size_t size) { unsigned int bitmask, free_bitmask, old_bitmask; @@ -360,7 +367,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, struct msm_nand_chip *chip = mtd->priv; struct { - dmov_s cmd[4 * 5 + 3]; + dmov_s cmd[8 * 5 + 3]; unsigned cmdptr; struct { uint32_t cmd; @@ -377,12 +384,12 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, struct { uint32_t flash_status; uint32_t buffer_status; - } result[4]; + } result[8]; } data; } *dma_buffer; dmov_s *cmd; unsigned n; - unsigned page = from / 2048; + unsigned page = from >> chip->page_shift; uint32_t oob_len = ops->ooblen; uint32_t sectordatasize; uint32_t sectoroobsize; @@ -430,7 +437,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, } if (ops->oobbuf && !ops->datbuf && ops->mode == MTD_OOB_AUTO) - start_sector = 3; + start_sector = chip->last_sector; if (ops->oobbuf && !ops->datbuf) { unsigned tmpoobsz = (ops->mode == MTD_OOB_AUTO) ? @@ -501,11 +508,14 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, if (ops->mode != MTD_OOB_RAW) { dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ_ECC; dma_buffer->data.cfg0 = - (chip->CFG0 & ~(7U << 6)) | ((3U - start_sector) << 6); + (chip->CFG0 & ~(7U << 6)) | + ((chip->last_sector - start_sector) << 6); dma_buffer->data.cfg1 = chip->CFG1; } else { dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; - dma_buffer->data.cfg0 = MSM_NAND_CFG0_RAW; + dma_buffer->data.cfg0 = + (MSM_NAND_CFG0_RAW & ~(7U << 6)) | + (chip->last_sector << 6); dma_buffer->data.cfg1 = MSM_NAND_CFG1_RAW | (chip->CFG1 & CFG1_WIDE_FLASH); } @@ -521,9 +531,9 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, dma_buffer->data.exec = 1; - BUILD_BUG_ON(4 != ARRAY_SIZE(dma_buffer->data.result)); + BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.result)); - for (n = start_sector; n < 4; n++) { + for (n = start_sector; n <= chip->last_sector; n++) { /* flash + buffer status return words */ dma_buffer->data.result[n].flash_status = 0xeeeeeeee; dma_buffer->data.result[n].buffer_status = 0xeeeeeeee; @@ -587,7 +597,9 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, */ if (ops->datbuf) { if (ops->mode != MTD_OOB_RAW) - sectordatasize = (n < 3) ? 516 : 500; + sectordatasize = + (n < chip->last_sector) ? + 516 : chip->last_sectorsz; else sectordatasize = 528; @@ -599,12 +611,14 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, cmd++; } - if (ops->oobbuf && - (n == 3 || ops->mode != MTD_OOB_AUTO)) { + if (ops->oobbuf && (n == chip->last_sector || + ops->mode != MTD_OOB_AUTO)) { cmd->cmd = 0; - if (n == 3) { - cmd->src = MSM_NAND_FLASH_BUFFER + 500; - sectoroobsize = 16; + if (n == chip->last_sector) { + cmd->src = MSM_NAND_FLASH_BUFFER + + chip->last_sectorsz; + sectoroobsize = + (chip->last_sector + 1) * 4; if (ops->mode != MTD_OOB_AUTO) sectoroobsize += 10; } else { @@ -636,7 +650,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, } #endif - BUILD_BUG_ON(4 * 5 + 3 != ARRAY_SIZE(dma_buffer->cmd)); + BUILD_BUG_ON(8 * 5 + 3 != ARRAY_SIZE(dma_buffer->cmd)); BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); dma_buffer->cmd[0].cmd |= CMD_OCB; cmd[-1].cmd |= CMD_OCU | CMD_LC; @@ -654,7 +668,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, */ pageerr = 0; page_corrected = 0; - for (n = start_sector; n < 4; n++) { + for (n = start_sector; n <= chip->last_sector; n++) { if (dma_buffer->data.result[n].buffer_status & 0x8) { total_uncorrected++; uncorrected[BIT_WORD(pages_read)] |= @@ -681,7 +695,8 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, err = pageerr; #if VERBOSE - pr_info("status: %x %x %x %x %x %x %x %x\n", + pr_info("status: %x %x %x %x %x %x %x %x " + "%x %x %x %x %x %x %x %x\n", dma_buffer->data.result[0].flash_status, dma_buffer->data.result[0].buffer_status, dma_buffer->data.result[1].flash_status, @@ -689,7 +704,15 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, dma_buffer->data.result[2].flash_status, dma_buffer->data.result[2].buffer_status, dma_buffer->data.result[3].flash_status, - dma_buffer->data.result[3].buffer_status); + dma_buffer->data.result[3].buffer_status, + dma_buffer->data.result[4].flash_status, + dma_buffer->data.result[4].buffer_status, + dma_buffer->data.result[5].flash_status, + dma_buffer->data.result[5].buffer_status, + dma_buffer->data.result[6].flash_status, + dma_buffer->data.result[6].buffer_status, + dma_buffer->data.result[7].flash_status, + dma_buffer->data.result[7].buffer_status); #endif if (err && err != -EUCLEAN && err != -EBADMSG) break; @@ -755,7 +778,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { struct msm_nand_chip *chip = mtd->priv; struct { - dmov_s cmd[4 * 6 + 3]; + dmov_s cmd[8 * 6 + 3]; unsigned cmdptr; struct { uint32_t cmd; @@ -769,13 +792,13 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) uint32_t ecccfg; uint32_t ecccfg_restore; #endif - uint32_t flash_status[4]; + uint32_t flash_status[8]; uint32_t zeroes; } data; } *dma_buffer; dmov_s *cmd; unsigned n; - unsigned page = to / 2048; + unsigned page = to >> chip->page_shift; uint32_t oob_len = ops->ooblen; uint32_t sectordatawritesize; int err; @@ -865,9 +888,11 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) dma_buffer->data.cfg0 = chip->CFG0; dma_buffer->data.cfg1 = chip->CFG1; } else { - dma_buffer->data.cfg0 = MSM_NAND_CFG0_RAW; + dma_buffer->data.cfg0 = + (MSM_NAND_CFG0_RAW & ~(7U << 6)) | + (chip->last_sector << 6); dma_buffer->data.cfg1 = MSM_NAND_CFG1_RAW | - (chip->CFG1 & CFG1_WIDE_FLASH); + (chip->CFG1 & CFG1_WIDE_FLASH); } dma_buffer->data.cmd = MSM_NAND_CMD_PRG_PAGE; @@ -880,9 +905,9 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) /* GO bit for the EXEC register */ dma_buffer->data.exec = 1; - BUILD_BUG_ON(4 != ARRAY_SIZE(dma_buffer->data.flash_status)); + BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.flash_status)); - for (n = 0; n < 4; n++) { + for (n = 0; n <= chip->last_sector ; n++) { /* status return words */ dma_buffer->data.flash_status[n] = 0xeeeeeeee; /* block on cmd ready, then @@ -922,7 +947,8 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) /* write data block */ if (ops->mode != MTD_OOB_RAW) - sectordatawritesize = (n < 3) ? 516 : 500; + sectordatawritesize = (n < chip->last_sector) ? + 516 : chip->last_sectorsz; else sectordatawritesize = 528; @@ -934,13 +960,13 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) cmd++; if (ops->oobbuf) { - if (n == 3) { + if (n == chip->last_sector) { cmd->cmd = 0; cmd->src = oob_dma_addr_curr; - cmd->dst = MSM_NAND_FLASH_BUFFER + 500; - if (16 < oob_len) - cmd->len = 16; - else + cmd->dst = MSM_NAND_FLASH_BUFFER + + chip->last_sectorsz; + cmd->len = 516 - chip->last_sectorsz; + if (oob_len <= cmd->len) cmd->len = oob_len; oob_dma_addr_curr += cmd->len; oob_len -= cmd->len; @@ -1000,7 +1026,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) #endif dma_buffer->cmd[0].cmd |= CMD_OCB; cmd[-1].cmd |= CMD_OCU | CMD_LC; - BUILD_BUG_ON(4 * 6 + 3 != ARRAY_SIZE(dma_buffer->cmd)); + BUILD_BUG_ON(8 * 6 + 3 != ARRAY_SIZE(dma_buffer->cmd)); BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | @@ -1015,7 +1041,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) * bit (0x80) is unset, we lose */ err = 0; - for (n = 0; n < 4; n++) { + for (n = 0; n <= chip->last_sector ; n++) { if (dma_buffer->data.flash_status[n] & 0x110) { if (dma_buffer->data.flash_status[n] & 0x10) pr_err("msm_nand: critical write error," @@ -1030,11 +1056,15 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) } #if VERBOSE - pr_info("write page %d: status: %x %x %x %x\n", page, - dma_buffer->data.flash_status[0], + pr_info("write page %d: status: %x %x %x %x %x %x %x %x\n", + page, dma_buffer->data.flash_status[0], dma_buffer->data.flash_status[1], dma_buffer->data.flash_status[2], - dma_buffer->data.flash_status[3]); + dma_buffer->data.flash_status[3], + dma_buffer->data.flash_status[4], + dma_buffer->data.flash_status[5], + dma_buffer->data.flash_status[6], + dma_buffer->data.flash_status[7]); #endif if (err) break; @@ -1090,7 +1120,7 @@ msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) unsigned cmdptr; unsigned data[9]; } *dma_buffer; - unsigned page = instr->addr / 2048; + unsigned page = instr->addr >> chip->page_shift; if (instr->addr & (mtd->erasesize - 1)) { pr_err("%s: unsupported erase address, 0x%llx\n", @@ -1204,7 +1234,7 @@ msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) } *dma_buffer; dmov_s *cmd; uint8_t *buf; - unsigned page = ofs / 2048; + unsigned page = ofs >> chip->page_shift; /* Check for invalid offset */ if (ofs > mtd->size) @@ -1231,9 +1261,11 @@ msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) dma_buffer->data.cfg1 = MSM_NAND_CFG1_RAW | (chip->CFG1 & CFG1_WIDE_FLASH); if (chip->CFG1 & CFG1_WIDE_FLASH) - dma_buffer->data.addr0 = (page << 16) | (0x630 >> 1); + dma_buffer->data.addr0 = (page << 16) | + ((528 * chip->last_sector) >> 1); else - dma_buffer->data.addr0 = (page << 16) | 0x630; + dma_buffer->data.addr0 = (page << 16) | + (528 * chip->last_sector); dma_buffer->data.addr1 = (page >> 16) & 0xff; dma_buffer->data.chipsel = 0 | 4; @@ -1268,7 +1300,8 @@ msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) cmd++; cmd->cmd = 0; - cmd->src = MSM_NAND_FLASH_BUFFER + 464; + cmd->src = MSM_NAND_FLASH_BUFFER + + (mtd->writesize - 528 * chip->last_sector); cmd->dst = msm_virt_to_dma(chip, buf); cmd->len = 4; cmd++; @@ -1422,14 +1455,23 @@ int msm_nand_scan(struct mtd_info *mtd, int maxchips) "size %llx\n", flashman->name, flashman->id, flashdev->id, mtd->erasesize, mtd->writesize, mtd->size); - if (mtd->writesize != 2048) { + if (mtd->writesize == 2048) { + chip->page_shift = 11; + } else if (mtd->writesize == 4096) { + chip->page_shift = 12; + } else { pr_err("%s: Unsupported page size (%d)\n", __func__, mtd->writesize); return -EINVAL; } + chip->last_sector = (mtd->writesize / 512) - 1; + chip->last_sectorsz = mtd->writesize - chip->last_sector * 516; + if (mtd->oobsize == 64) { mtd->ecclayout = &msm_nand_oob_64; + } else if (mtd->oobsize == 128) { + mtd->ecclayout = &msm_nand_oob_128; } else { pr_err("%s: Unsupported oob size (%d)\n", __func__, mtd->oobsize); @@ -1437,7 +1479,7 @@ int msm_nand_scan(struct mtd_info *mtd, int maxchips) } mtd->oobavail = mtd->ecclayout->oobavail; - chip->CFG0 = (3 << 6) /* 4 codeword per page for 2k nand */ + chip->CFG0 = (chip->last_sector << 6) /* codewords per page */ | (516 << 9) /* 516 user data bytes */ | (10 << 19) /* 10 parity bytes */ | (5 << 27) /* 5 address cycles */ @@ -1448,7 +1490,8 @@ int msm_nand_scan(struct mtd_info *mtd, int maxchips) chip->CFG1 = (0 << 0) /* Enable ecc */ | (7 << 2) /* 8 recovery cycles */ | (0 << 5) /* Allow CS deassertion */ - | (465 << 6) /* Bad block marker location */ + | ((mtd->writesize - (528 * chip->last_sector) + 1) << 6) + /* Bad block marker location */ | (0 << 16) /* Bad block in user data area */ | (2 << 17) /* 6 cycle tWB/tRB */ | (busw16 & CFG1_WIDE_FLASH); /* preserve wide flag */ @@ -1530,6 +1573,7 @@ static int __devinit msm_nand_probe(struct platform_device *pdev) struct msm_nand_info *info; struct flash_platform_data *pdata = pdev->dev.platform_data; int err; + int i; if (pdev->num_resources != 1) { pr_err("invalid num_resources"); From 70aa46b2c928a7acfbe8659bbb192a13554449c0 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 10 Feb 2010 21:46:44 -0800 Subject: [PATCH 0601/2556] [ARM] mtd: msm_nand: Use the correct base address from the iomap Change-Id: I566dac664453418be4c202f81e65164449ffd219 Signed-off-by: Dima Zavin --- drivers/mtd/devices/msm_nand.c | 1 - drivers/mtd/devices/msm_nand.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index d6f35ad8f3332..acd02942efeee 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -30,7 +30,6 @@ #include -#define MSM_NAND_BASE 0xA0A00000 #include "msm_nand.h" #define MSM_NAND_DMA_BUFFER_SIZE SZ_4K diff --git a/drivers/mtd/devices/msm_nand.h b/drivers/mtd/devices/msm_nand.h index 18a0a06c9288a..18893f182c7cb 100644 --- a/drivers/mtd/devices/msm_nand.h +++ b/drivers/mtd/devices/msm_nand.h @@ -18,7 +18,7 @@ #include -#define MSM_NAND_REG(off) (MSM_NAND_BASE + (off)) +#define MSM_NAND_REG(off) (MSM_NAND_PHYS + (off)) #define MSM_NAND_FLASH_CMD MSM_NAND_REG(0x0000) #define MSM_NAND_ADDR0 MSM_NAND_REG(0x0004) From b4d37500a90badde650568942f5ceefe3b31907f Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 12 Feb 2010 18:44:13 -0800 Subject: [PATCH 0602/2556] [ARM] usb: msm72k_udc: Force the phy to non-driving mode after reset While resetting the controller, after ensuring the phy reset was successful, force the phy to issue disconnect event to the host by toggling non-driving mode. Change-Id: Iaa028be864b2cd60db7215ab0106ff7f73e4736a Signed-off-by: Dima Zavin --- drivers/usb/gadget/msm72k_udc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 7ea8308371fb8..281ecdebe4584 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -1085,6 +1085,13 @@ static void usb_reset(struct usb_info *ui) msleep(100); + /* toggle non-driving mode after phy reset to ensure that + * we cause a disconnect event to the host */ + ulpi_write(ui, 0x18, 0x6); + msleep(1); + ulpi_write(ui, 0x8, 0x5); + msleep(1); + /* RESET */ writel(2, USB_USBCMD); msleep(10); @@ -1661,7 +1668,7 @@ static int msm72k_pullup(struct usb_gadget *_gadget, int is_active) pr_info("msm_hsusb: disable pullup\n"); writel(cmd, USB_USBCMD); -#if defined(CONFIG_ARCH_QSD8X50) +#if defined(CONFIG_ARCH_QSD8X50) || defined(CONFIG_ARCH_MSM7X30) ulpi_write(ui, 0x48, 0x04); #endif } From 05d29d8c9b6ca3bc911eea45344c9ef35ea30f2c Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 24 Feb 2010 20:13:00 -0800 Subject: [PATCH 0603/2556] [ARM] msm: add a remote spinlock implementation for sync with modem processor Change-Id: Ic8f5f34b58837b98cbf9d1830df481125f0d3f00 Signed-off-by: Dima Zavin --- .../mach-msm/include/mach/remote_spinlock.h | 92 +++++++ arch/arm/mach-msm/remote_spinlock.c | 226 ++++++++++++++++++ 2 files changed, 318 insertions(+) create mode 100644 arch/arm/mach-msm/include/mach/remote_spinlock.h create mode 100644 arch/arm/mach-msm/remote_spinlock.c diff --git a/arch/arm/mach-msm/include/mach/remote_spinlock.h b/arch/arm/mach-msm/include/mach/remote_spinlock.h new file mode 100644 index 0000000000000..6b25ec20dbaaf --- /dev/null +++ b/arch/arm/mach-msm/include/mach/remote_spinlock.h @@ -0,0 +1,92 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ASM__ARCH_MSM_REMOTE_SPINLOCK_H +#define __ASM__ARCH_MSM_REMOTE_SPINLOCK_H + +#include +#include + +/* Remote spinlock type definitions. */ +struct raw_remote_spinlock; +typedef struct { + spinlock_t local; +#if defined(CONFIG_MSM_REMOTE_SPINLOCK) + struct raw_remote_spinlock *remote; +#endif +} remote_spinlock_t; + +#if defined(CONFIG_MSM_REMOTE_SPINLOCK) +int _remote_spin_lock_init(remote_spinlock_t *lock, const char *name); +void _remote_spin_lock(remote_spinlock_t *lock); +void _remote_spin_unlock(remote_spinlock_t *lock); +#else +static inline int _remote_spin_lock_init(remote_spinlock_t *lock, + const char *name) { return 0; } +static inline void _remote_spin_lock(remote_spinlock_t *lock) { } +static inline void _remote_spin_unlock(remote_spinlock_t *lock) { } +#endif + +/* Note: only the below functions constitute the supported interface */ +static inline int remote_spin_lock_init(remote_spinlock_t *lock, + const char *name) +{ + spin_lock_init(&lock->local); + return _remote_spin_lock_init(lock, name); +} + +#define remote_spin_lock(lock) \ + do { \ + typecheck(remote_spinlock_t *, lock); \ + spin_lock(&((lock)->local)); \ + _remote_spin_lock(lock); \ + } while (0) + +#define remote_spin_unlock(lock) \ + do { \ + typecheck(remote_spinlock_t *, lock); \ + _remote_spin_unlock(lock); \ + spin_unlock(&((lock)->local)); \ + } while (0) + + +#define remote_spin_lock_irqsave(lock,flags) \ + do { \ + typecheck(remote_spinlock_t *, lock); \ + spin_lock_irqsave(&((lock)->local), (flags)); \ + _remote_spin_lock(lock); \ + } while (0) + +#define remote_spin_unlock_irqrestore(lock,flags) \ + do { \ + typecheck(remote_spinlock_t *, lock); \ + _remote_spin_unlock(lock); \ + spin_unlock_irqrestore(&((lock)->local), (flags)); \ + } while (0) + +#endif /* __ASM__ARCH_MSM_REMOTE_SPINLOCK_H */ diff --git a/arch/arm/mach-msm/remote_spinlock.c b/arch/arm/mach-msm/remote_spinlock.c new file mode 100644 index 0000000000000..75f6140ca0ccd --- /dev/null +++ b/arch/arm/mach-msm/remote_spinlock.c @@ -0,0 +1,226 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include +#include "smd_private.h" + +#define SMEM_SPINLOCK_COUNT 8 +#define SMEM_SPINLOCK_ARRAY_SIZE (SMEM_SPINLOCK_COUNT * sizeof(uint32_t)) + +struct raw_remote_spinlock { + union { + volatile u32 lock; + struct { + volatile u8 self_lock; + volatile u8 other_lock; + volatile u8 next_yield; + u8 pad; + } dek; + }; +}; + +static inline void __raw_remote_ex_spin_lock(struct raw_remote_spinlock *lock) +{ + unsigned long tmp; + + asm volatile ( + "1: ldrex %0, [%1]\n" + " teq %0, #0\n" + " strexeq %0, %2, [%1]\n" + " teqeq %0, #0\n" + " bne 1b" + : "=&r" (tmp) + : "r" (&lock->lock), "r" (1) + : "cc"); + + smp_mb(); +} + +static inline void __raw_remote_ex_spin_unlock(struct raw_remote_spinlock *lock) +{ + smp_mb(); + + asm volatile ( + " str %1, [%0]\n" + : + : "r" (&lock->lock), "r" (0) + : "cc"); +} + +static inline void __raw_remote_swp_spin_lock(struct raw_remote_spinlock *lock) +{ + unsigned long tmp; + + asm volatile ( + "1: swp %0, %2, [%1]\n" + " teq %0, #0\n" + " bne 1b" + : "=&r" (tmp) + : "r" (&lock->lock), "r" (1) + : "cc"); + + smp_mb(); +} + +static inline void __raw_remote_swp_spin_unlock(struct raw_remote_spinlock *lock) +{ + smp_mb(); + + asm volatile ( + " str %1, [%0]" + : + : "r" (&lock->lock), "r" (0) + : "cc"); +} + +#define DEK_LOCK_REQUEST 1 +#define DEK_LOCK_YIELD (!DEK_LOCK_REQUEST) +#define DEK_YIELD_TURN_SELF 0 +static void __raw_remote_dek_spin_lock(struct raw_remote_spinlock *lock) +{ + lock->dek.self_lock = DEK_LOCK_REQUEST; + + while (lock->dek.other_lock) { + + if (lock->dek.next_yield == DEK_YIELD_TURN_SELF) + lock->dek.self_lock = DEK_LOCK_YIELD; + + while (lock->dek.other_lock) + ; + + lock->dek.self_lock = DEK_LOCK_REQUEST; + } + lock->dek.next_yield = DEK_YIELD_TURN_SELF; + + smp_mb(); +} + +static void __raw_remote_dek_spin_unlock(struct raw_remote_spinlock *lock) +{ + smp_mb(); + + lock->dek.self_lock = DEK_LOCK_YIELD; +} + +#if defined(CONFIG_MSM_REMOTE_SPINLOCK_DEKKERS) +/* Use Dekker's algorithm when LDREX/STREX and SWP are unavailable for + * shared memory */ +#define _raw_remote_spin_lock(lock) __raw_remote_dek_spin_lock(lock) +#define _raw_remote_spin_unlock(lock) __raw_remote_dek_spin_unlock(lock) +#elif defined(CONFIG_MSM_REMOTE_SPINLOCK_SWP) +/* Use SWP-based locks when LDREX/STREX are unavailable for shared memory. */ +#define _raw_remote_spin_lock(lock) __raw_remote_swp_spin_lock(lock) +#define _raw_remote_spin_unlock(lock) __raw_remote_swp_spin_unlock(lock) +#else +/* Use LDREX/STREX for shared memory locking, when available */ +#define _raw_remote_spin_lock(lock) __raw_remote_ex_spin_lock(lock) +#define _raw_remote_spin_unlock(lock) __raw_remote_ex_spin_unlock(lock) +#endif + +void _remote_spin_lock(remote_spinlock_t *lock) +{ + _raw_remote_spin_lock(lock->remote); +} +EXPORT_SYMBOL(_remote_spin_lock); + +void _remote_spin_unlock(remote_spinlock_t *lock) +{ + _raw_remote_spin_unlock(lock->remote); +} +EXPORT_SYMBOL(_remote_spin_unlock); + +static int remote_spin_lock_smem_init(remote_spinlock_t *lock, int id) +{ + void *start; + + if (id >= SMEM_SPINLOCK_COUNT) + return -EINVAL; + + start = smem_alloc(SMEM_SPINLOCK_ARRAY, SMEM_SPINLOCK_ARRAY_SIZE); + if (start == NULL) + return -ENXIO; + + lock->remote = + (struct raw_remote_spinlock *)(start + id * sizeof(uint32_t)); + return 0; +} + +#define DAL_CHUNK_NAME_LENGTH 12 +struct dal_chunk_header { + uint32_t size; + char name[DAL_CHUNK_NAME_LENGTH]; + uint32_t lock; + uint32_t reserved; + uint32_t type; + uint32_t version; +}; + +static int remote_spin_lock_dal_init(remote_spinlock_t *lock, const char *name) +{ + unsigned long start; + unsigned long end; + unsigned size; + struct dal_chunk_header *cur_hdr; + + if (!name) + return -EINVAL; + + start = (unsigned long)smem_item(SMEM_DAL_AREA, &size); + if (!start) + return -ENXIO; + + end = start + size; + + /* Find first chunk header */ + cur_hdr = (struct dal_chunk_header *)ALIGN(start, 4096); + lock->remote = NULL; + while (((unsigned long)(cur_hdr + 1) <= end) && (cur_hdr->size != 0)) { + if (!strncmp(cur_hdr->name, name, DAL_CHUNK_NAME_LENGTH)) { + lock->remote = + (struct raw_remote_spinlock *)&cur_hdr->lock; + return 0; + } + cur_hdr = (void *)cur_hdr + cur_hdr->size; + } + + pr_err("%s: DAL remote spin lock '%s' not found.\n", __func__, name); + return -EINVAL; +} + +int _remote_spin_lock_init(remote_spinlock_t *lock, const char *name) +{ + BUG_ON(name == NULL); + + /* remote spinlocks can be one of two formats: + * D: + * S: + */ + if (!strncmp(name, "D:", 2)) { + return remote_spin_lock_dal_init(lock, &name[2]); + } else if (!strncmp(name, "S:", 2)) { + BUG_ON(name[3] != '\0'); + return remote_spin_lock_smem_init(lock, (uint8_t)(name[2]-'0')); + } + + return -EINVAL; +} +EXPORT_SYMBOL(_remote_spin_lock_init); From ab6f8e163b273d6d5f0fe862fb3fa668afc5e376 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 24 Feb 2010 20:15:53 -0800 Subject: [PATCH 0604/2556] [ARM] msm: add remote spinlcok config options and build rule Change-Id: Ibeda30088a5a1aad541b17f9eff454917aa360a6 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Kconfig | 15 +++++++++++++++ arch/arm/mach-msm/Makefile | 1 + 2 files changed, 16 insertions(+) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 3080f8f4b9e9c..a80a9ea5f3c34 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -73,6 +73,21 @@ config MSM_MDP31 depends on ARCH_QSD8X50 default y +config MSM_REMOTE_SPINLOCK_DEKKERS + bool + +config MSM_REMOTE_SPINLOCK_SWP + bool + +config MSM_REMOTE_SPINLOCK_LDREX + bool + +config MSM_REMOTE_SPINLOCK + bool + depends on MSM_REMOTE_SPINLOCK_LDREX || MSM_REMOTE_SPINLOCK_SWP || \ + MSM_REMOTE_SPINLOCK_DEKKERS + default y + config MSM_AMSS_VERSION int default 6210 if MSM_AMSS_VERSION_6210 diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 0af423364cda7..98184fccafe8b 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_MSM_QDSP6) += qdsp6/ obj-$(CONFIG_MSM_HW3D) += hw3d.o obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o +obj-$(CONFIG_MSM_REMOTE_SPINLOCK) += remote_spinlock.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o From f3f9169fc4f7f59ef950ed3a447de7b7ae38f688 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 26 Feb 2010 12:51:21 -0800 Subject: [PATCH 0605/2556] [ARM] msm: add single-wire serial bus interface (SSBI) driver SSBI is the Qualcomm proprietary single-wire serial bus interface used to connect the MSM devices to the PMIC and Audio codecs. Since SSBI only supports a single slave, the driver get the name of the slave device passed in from the board file through the master device's platform data. SSBI registers pretty early (postcore), so that the PMIC can come up before the board init. This is useful if the board init requires the use of gpios that are connected through the PMIC. Change-Id: I07016241a0652ee93aafa855a760b8f99ad245a8 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/include/mach/msm_ssbi.h | 35 +++ arch/arm/mach-msm/ssbi.c | 304 ++++++++++++++++++++++ 2 files changed, 339 insertions(+) create mode 100644 arch/arm/mach-msm/include/mach/msm_ssbi.h create mode 100644 arch/arm/mach-msm/ssbi.c diff --git a/arch/arm/mach-msm/include/mach/msm_ssbi.h b/arch/arm/mach-msm/include/mach/msm_ssbi.h new file mode 100644 index 0000000000000..4d79078c33237 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_ssbi.h @@ -0,0 +1,35 @@ +/* arch/arm/mach-msm/include/mach/msm_ssbi.h + * + * Copyright (C) 2010 Google, Inc. + * Author: Dima Zavin + * + * 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 __ASM_ARCH_MSM_SSBI_H +#define __ASM_ARCH_MSM_SSBI_H + +#include + +struct msm_ssbi_slave_info { + const char *name; + int irq; + void *platform_data; +}; + +struct msm_ssbi_platform_data { + struct msm_ssbi_slave_info slave; + const char *rspinlock_name; +}; + +int msm_ssbi_write(struct device *dev, u16 addr, u8 *buf, int len); +int msm_ssbi_read(struct device *dev, u16 addr, u8 *buf, int len); +#endif diff --git a/arch/arm/mach-msm/ssbi.c b/arch/arm/mach-msm/ssbi.c new file mode 100644 index 0000000000000..b9cefa29ba02e --- /dev/null +++ b/arch/arm/mach-msm/ssbi.c @@ -0,0 +1,304 @@ +/* arch/arm/mach-msm/ssbi.c + * + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Copyright (c) 2010, Google Inc. + * + * Original authors: Code Aurura Forum + * + * Author: Dima Zavin + * - Largely rewritten from original to not be an i2c driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* SSBI 2.0 controller registers */ +#define SSBI2_CTL 0x0000 +#define SSBI2_RESET 0x0004 +#define SSBI2_CMD 0x0008 +#define SSBI2_RD 0x0010 +#define SSBI2_STATUS 0x0014 +#define SSBI2_PRIORITIES 0x0018 +#define SSBI2_MODE2 0x001C + +/* SSBI_CMD fields */ +#define SSBI_CMD_SEND_TERM_SYM (1 << 27) +#define SSBI_CMD_WAKEUP_SLAVE (1 << 26) +#define SSBI_CMD_USE_ENABLE (1 << 25) +#define SSBI_CMD_RDWRN (1 << 24) + +/* SSBI_STATUS fields */ +#define SSBI_STATUS_DATA_IN (1 << 4) +#define SSBI_STATUS_RD_CLOBBERED (1 << 3) +#define SSBI_STATUS_RD_READY (1 << 2) +#define SSBI_STATUS_READY (1 << 1) +#define SSBI_STATUS_MCHN_BUSY (1 << 0) + +/* SSBI_RD fields */ +#define SSBI_RD_USE_ENABLE (1 << 25) +#define SSBI_RD_RDWRN (1 << 24) + +/* SSBI_MODE2 fields */ +#define SSBI_MODE2_SSBI2_MODE (1 << 0) + +#define SSBI_TIMEOUT_US 100 + +struct msm_ssbi { + struct device *dev; + struct device *slave; + void __iomem *base; + remote_spinlock_t rspin_lock; +}; + +#define to_msm_ssbi(dev) platform_get_drvdata(to_platform_device(dev)) + +static inline u32 ssbi_readl(struct msm_ssbi *ssbi, unsigned long reg) +{ + return readl(ssbi->base + reg); +} + +static inline void ssbi_writel(struct msm_ssbi *ssbi, u32 val, + unsigned long reg) +{ + writel(val, ssbi->base + reg); +} + +//poll_for_device_ready === SSBI_STATUS_READY +//poll_for_transfer_completed === SSBI_STATUS_MCHN_BUSY +//poll_for_read_completed === SSBI_STATUS_RD_READY +static int ssbi_wait_mask(struct msm_ssbi *ssbi, u32 set_mask, u32 clr_mask) +{ + u32 timeout = SSBI_TIMEOUT_US; + u32 val; + + while (timeout--) { + val = ssbi_readl(ssbi, SSBI2_STATUS); + if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0)) + return 0; + udelay(1); + } + + dev_err(ssbi->dev, "%s: timeout (status %x set_mask %x clr_mask %x)\n", + __func__, ssbi_readl(ssbi, SSBI2_STATUS), set_mask, clr_mask); + return -ETIMEDOUT; +} + +int msm_ssbi_read(struct device *dev, u16 addr, u8 *buf, int len) +{ + struct msm_ssbi *ssbi = to_msm_ssbi(dev); + unsigned long flags; + u32 read_cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16); + u32 mode2; + int ret = 0; + + BUG_ON(ssbi->dev != dev); + + remote_spin_lock_irqsave(&ssbi->rspin_lock, flags); + + mode2 = ssbi_readl(ssbi, SSBI2_MODE2); + if (mode2 & SSBI_MODE2_SSBI2_MODE) { + mode2 = (mode2 & 0xf) | (((addr >> 8) & 0x7f) << 4); + ssbi_writel(ssbi, mode2, SSBI2_MODE2); + } + + while (len) { + ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0); + if (ret) + goto err; + + ssbi_writel(ssbi, read_cmd, SSBI2_CMD); + ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, 0); + if (ret) + goto err; + *buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff; + len--; + } + +err: + remote_spin_unlock_irqrestore(&ssbi->rspin_lock, flags); + return ret; +} +EXPORT_SYMBOL(msm_ssbi_read); + +int msm_ssbi_write(struct device *dev, u16 addr, u8 *buf, int len) +{ + struct msm_ssbi *ssbi = to_msm_ssbi(dev); + unsigned long flags; + u32 mode2; + int ret = 0; + + BUG_ON(ssbi->dev != dev); + + remote_spin_lock_irqsave(&ssbi->rspin_lock, flags); + + mode2 = readl(ssbi->base + SSBI2_MODE2); + if (mode2 & SSBI_MODE2_SSBI2_MODE) { + mode2 = (mode2 & 0xf) | (((addr >> 8) & 0x7f) << 4); + ssbi_writel(ssbi, mode2, SSBI2_MODE2); + } + + while (len) { + ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0); + if (ret) + goto err; + + ssbi_writel(ssbi, ((addr & 0xff) << 16) | *buf, SSBI2_CMD); + ret = ssbi_wait_mask(ssbi, 0, SSBI_STATUS_MCHN_BUSY); + if (ret) + goto err; + buf++; + len--; + } + +err: + remote_spin_unlock_irqrestore(&ssbi->rspin_lock, flags); + return ret; +} +EXPORT_SYMBOL(msm_ssbi_write); + +static int __init msm_ssbi_add_slave(struct msm_ssbi *ssbi, + struct msm_ssbi_slave_info *slave) +{ + struct platform_device *slave_pdev; + struct resource slave_irq_res; + int ret; + + if (ssbi->slave) { + pr_err("%s: slave already attached??\n", __func__); + return -EBUSY; + } + + slave_pdev = platform_device_alloc(slave->name, -1); + if (!slave_pdev) { + pr_err("%s: cannot allocate pdev for slave '%s'", __func__, + slave->name); + ret = -ENOMEM; + goto err; + } + + slave_pdev->dev.parent = ssbi->dev; + slave_pdev->dev.platform_data = slave->platform_data; + + memset(&slave_irq_res, 0, sizeof(struct resource)); + slave_irq_res.start = slave->irq; + slave_irq_res.end = slave->irq; + slave_irq_res.flags = IORESOURCE_IRQ; + ret = platform_device_add_resources(slave_pdev, &slave_irq_res, 1); + if (ret) { + pr_err("%s: can't add irq resource for '%s'\n", __func__, + slave->name); + goto err; + } + + ret = platform_device_add(slave_pdev); + if (ret) { + pr_err("%s: cannot add slave platform device for '%s'\n", + __func__, slave->name); + goto err; + } + + ssbi->slave = &slave_pdev->dev; + return 0; + +err: + if (slave_pdev) + platform_device_put(slave_pdev); + return ret; +} + +static int __init msm_ssbi_probe(struct platform_device *pdev) +{ + struct msm_ssbi_platform_data *pdata = pdev->dev.platform_data; + struct resource *mem_res; + struct msm_ssbi *ssbi; + int ret = 0; + + if (!pdata) { + pr_err("%s: missing platform data\n", __func__); + return -EINVAL; + } + + ssbi = kzalloc(sizeof(struct msm_ssbi), GFP_KERNEL); + if (!ssbi) { + pr_err("%s: cannot allocate ssbi_data\n", __func__); + return -ENOMEM; + } + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem_res) { + pr_err("%s: missing mem resource\n", __func__); + ret = -EINVAL; + goto err_get_mem_res; + } + + ssbi->base = ioremap(mem_res->start, resource_size(mem_res)); + if (!ssbi->base) { + pr_err("%s: ioremap of 0x%p failed\n", __func__, + (void *)mem_res->start); + ret = -EINVAL; + goto err_ioremap; + } + ssbi->dev = &pdev->dev; + platform_set_drvdata(pdev, ssbi); + + ret = remote_spin_lock_init(&ssbi->rspin_lock, pdata->rspinlock_name); + if (ret) { + pr_err("%s: cannot init remote spinlock '%s'\n", __func__, + pdata->rspinlock_name); + goto err_remote_spinlock_init; + } + + ret = msm_ssbi_add_slave(ssbi, &pdata->slave); + if (ret) + goto err_ssbi_add_slave; + + pr_info("msm_ssbi: io=%08x rsl='%s'\n", mem_res->start, + pdata->rspinlock_name); + + return 0; + +err_remote_spinlock_init: + platform_set_drvdata(pdev, NULL); +err_ssbi_add_slave: + iounmap(ssbi->base); +err_ioremap: +err_get_mem_res: + kfree(ssbi); + return ret; +} + +static struct platform_driver msm_ssbi_driver = { + .probe = msm_ssbi_probe, + .driver = { + .name = "msm_ssbi", + .owner = THIS_MODULE, + }, +}; + +static int __init msm_ssbi_init(void) +{ + return platform_driver_register(&msm_ssbi_driver); +} + +postcore_initcall(msm_ssbi_init); From a9efee55d48e8909e9a133cce30579421ffbcc44 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 26 Feb 2010 13:11:42 -0800 Subject: [PATCH 0606/2556] [ARM] msm: add ssbi to Kconfig and Makefile Change-Id: I42876006decc309e362211b35e3c5794d80441e2 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Kconfig | 8 ++++++++ arch/arm/mach-msm/Makefile | 1 + 2 files changed, 9 insertions(+) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index a80a9ea5f3c34..b111031ee1a68 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -523,6 +523,14 @@ config MSM_QDSP6 help Enable support for qdsp6. This provides audio and video functionality. +config MSM_SSBI + tristate "SSBI support" + depends on ARCH_MSM7X30 + default n + help + Enable support for SSBI bus. This is required for communicatinig with + Qualcomm PMICs and Audio codecs. + config WIFI_CONTROL_FUNC bool "Enable WiFi control function abstraction" help diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 98184fccafe8b..df20a60d72795 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_MSM_HW3D) += hw3d.o obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o obj-$(CONFIG_MSM_REMOTE_SPINLOCK) += remote_spinlock.o +obj-$(CONFIG_MSM_SSBI) += ssbi.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o From ea9ce73bdb1a22a0203e9460b0260dda2ef8aa14 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 26 Feb 2010 13:10:56 -0800 Subject: [PATCH 0607/2556] [ARM] msm: devices: add the pmic ssbi device and resources Change-Id: I019794fbb6a1900ebdee7e33981e44b56a79f7fc Signed-off-by: Dima Zavin --- arch/arm/mach-msm/devices.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h index 0559a1f7e53f2..b8bd49759e442 100644 --- a/arch/arm/mach-msm/devices.h +++ b/arch/arm/mach-msm/devices.h @@ -48,6 +48,7 @@ extern struct platform_device msm_device_mddi1; extern struct platform_device msm_device_mdp; extern struct platform_device msm_device_touchscreen; extern struct platform_device msm_device_spi; +extern struct platform_device msm_device_ssbi_pmic; extern struct clk msm_clocks_7x01a[]; extern unsigned msm_num_clocks_7x01a; From 44681109279a5e180e3405052466a6680ae20f9a Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sat, 27 Feb 2010 16:28:20 -0800 Subject: [PATCH 0608/2556] mfd: add a driver for the Qualcomm PM8058 power management I/C This includes core support for gpios, irqs, and pinmuxing functions in the pm8058. TODO: properly implement wake support Change-Id: I6ad80b546bf3b9f80ae2a054b96d67201d6adb8e Signed-off-by: Dima Zavin --- drivers/mfd/pm8058-core.c | 762 +++++++++++++++++++++++++++++++++++++ include/linux/mfd/pm8058.h | 132 +++++++ 2 files changed, 894 insertions(+) create mode 100644 drivers/mfd/pm8058-core.c create mode 100644 include/linux/mfd/pm8058.h diff --git a/drivers/mfd/pm8058-core.c b/drivers/mfd/pm8058-core.c new file mode 100644 index 0000000000000..b334a6f3445d8 --- /dev/null +++ b/drivers/mfd/pm8058-core.c @@ -0,0 +1,762 @@ +/* + * Copyright (c) 2010 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Author: Dima Zavin + * - Based on a driver from Code Aurora Forum. + * + * 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 + +enum { + DEBUG_IRQS = 1U << 0, +}; +static int debug_mask = 0; +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define REG_HWREV 0x0002 /* PMIC4 revision */ + +#define REG_IRQ_ROOT 0x01bb +#define REG_IRQ_M_STATUS1 0x01bc +#define REG_IRQ_M_STATUS2 0x01bd +#define REG_IRQ_M_STATUS3 0x01be +#define REG_IRQ_M_STATUS4 0x01bf +#define REG_IRQ_BLK_SEL 0x01c0 +#define REG_IRQ_IT_STATUS 0x01c1 +#define REG_IRQ_CONFIG 0x01c2 +#define REG_IRQ_RT_STATUS 0x01c3 +#define REG_GPIO_CTRL(x) (0x0150 + (x)) + +#define IRQ_CFG_CLR (1 << 3) +#define IRQ_CFG_MASK_RE (1 << 2) +#define IRQ_CFG_MASK_FE (1 << 1) +#define IRQ_CFG_LVL_SEL (1 << 0) + +#define NUM_BLOCKS 32 +#define IRQS_PER_BLOCK 8 +#define NUM_PMIRQS (NUM_BLOCKS * IRQS_PER_BLOCK) + +/* XXX: why are mpp's different than gpios? should we just put them into + * the gpio space? */ +#define MPP_IRQ_OFFSET (16 * 8) +#define GPIO_IRQ_OFFSET (24 * 8) +#define KEYPAD_IRQ_OFFSET (9 * 8 + 2) + +/* this defines banks of irq space. We want to provide a compact irq space + * to the kernel, but there several ranges of irqs in an otherwise sparse + * map of available/accessible irqs on the pm8058. So, + * + * bank 0 - GPIO IRQs start=(24 * 8) cnt=40 (gpios 0-39) + * bank 1 - MPP IRQs start=(16 * 8) cnt=12 (mpps 0-11) + * bank 2 - keypad irqs start=(9*8 + 1) cnt=2 + * + */ +struct pm8058_irq_bank { + unsigned int start; /* will be added to the chip irq_base */ + unsigned int cnt; + unsigned int offset; /* offset into device's real irq map */ +}; + +static struct pm8058_irq_bank pm8058_irq_banks[] = { + { + .start = PM8058_FIRST_GPIO_IRQ, + .cnt = PM8058_NUM_GPIO_IRQS, + .offset = GPIO_IRQ_OFFSET, + }, + { + .start = PM8058_FIRST_MPP_IRQ, + .cnt = PM8058_NUM_MPP_IRQS, + .offset = MPP_IRQ_OFFSET, + }, + { + .start = PM8058_FIRST_KEYPAD_IRQ, + .cnt = PM8058_NUM_KEYPAD_IRQS, + .offset = KEYPAD_IRQ_OFFSET, + }, +}; +#define NUM_IRQ_BANKS ARRAY_SIZE(pm8058_irq_banks) + +struct pm8058_irq_group { + u16 stat_reg; + u8 valid_mask; + u8 root_mask; + u8 block_offset; +}; + +static const struct pm8058_irq_group pm8058_irq_groups[] = { + { + .stat_reg = REG_IRQ_M_STATUS2, + .valid_mask = 0x2, + .root_mask = 0x4, + .block_offset = 8, + }, + { + .stat_reg = REG_IRQ_M_STATUS4, + .valid_mask = 0x1f, + .root_mask = 0x10, + .block_offset = 24, + }, +}; +#define NUM_ROOT_GROUPS ARRAY_SIZE(pm8058_irq_groups) + +struct pm8058_irq_info { + u8 cfg; + u8 cfg_val; + u8 mask; + u8 blk; + u8 blk_bit; + u8 wake; +}; + +struct pm8058 { + struct device *dev; + unsigned int devirq; + + spinlock_t lock; + + unsigned int irq_base; + struct pm8058_irq_info irqs[PM8058_NUM_IRQS]; + unsigned int pmirqs[NUM_PMIRQS]; + int wake_cnt; + + struct gpio_chip gpio_chip; + u8 gpio_flags[PM8058_NUM_GPIOS]; +}; + +static int read_irq_block_reg(struct pm8058 *pmic, u8 blk, u16 reg, u8 *val); + +int pm8058_readb(struct device *dev, u16 addr, u8 *val) +{ + struct pm8058 *pmic = dev_get_drvdata(dev); + + return msm_ssbi_read(pmic->dev->parent, addr, val, 1); +} +EXPORT_SYMBOL(pm8058_readb); + +int pm8058_writeb(struct device *dev, u16 addr, u8 val) +{ + struct pm8058 *pmic = dev_get_drvdata(dev); + + return msm_ssbi_write(pmic->dev->parent, addr, &val, 1); +} +EXPORT_SYMBOL(pm8058_writeb); + +int pm8058_write_buf(struct device *dev, u16 addr, u8 *buf, int cnt) +{ + struct pm8058 *pmic = dev_get_drvdata(dev); + + return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt); +} +EXPORT_SYMBOL(pm8058_write_buf); + +int pm8058_read_buf(struct device *dev, u16 addr, u8 *buf, int cnt) +{ + struct pm8058 *pmic = dev_get_drvdata(dev); + + return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt); +} +EXPORT_SYMBOL(pm8058_read_buf); + +static int _dir_map[] = { + [0] = 0x3, + [PM8058_GPIO_INPUT] = 0x0, + [PM8058_GPIO_OUTPUT] = 0x2, + [PM8058_GPIO_OUTPUT_HIGH] = 0x2, +}; + +int pm8058_gpio_mux_cfg(struct device *dev, unsigned int gpio, + struct pm8058_pin_config *cfg) +{ + struct pm8058 *pmic = dev_get_drvdata(dev); + unsigned long flags; + int ret; + u8 bank[6]; + + gpio -= pmic->gpio_chip.base; + + /* bit 7 - write + * bit 6:4 - bank select */ + bank[0] = ((1 << 7) | (0 << 4) | ((cfg->vin_src & 0x7) << 1) | 0x1); + bank[1] = ((1 << 7) | (1 << 4) | (_dir_map[cfg->dir] << 2) | + ((cfg->flags & PM8058_GPIO_OPEN_DRAIN ? 0x1 : 0) << 1) | + ((cfg->dir & PM8058_GPIO_OUTPUT_HIGH ? 0x1 : 0) << 0)); + bank[2] = ((1 << 7) | (2 << 4) | ((cfg->pull_up & 0x7) << 1)); + bank[3] = ((1 << 7) | (3 << 4) | + ((cfg->strength & 0x3) << 2) | + ((cfg->flags & PM8058_GPIO_HIGH_Z ? 0x1 : 0x0) << 0)); + bank[4] = ((1 << 7) | (4 << 4) | ((cfg->func & 0x7) << 1)); + bank[5] = ((1 << 7) | (5 << 4) | + ((cfg->flags & PM8058_GPIO_INV_IRQ_POL ? 0 : 1) << 3)); + + spin_lock_irqsave(&pmic->lock, flags); + + pmic->gpio_flags[gpio] = cfg->flags | PM8058_GPIO_CONFIGURED; + ret = pm8058_write_buf(pmic->dev, REG_GPIO_CTRL(gpio), + bank, sizeof(bank)); + + spin_unlock_irqrestore(&pmic->lock, flags); + + if (ret) + pr_err("%s: failed writing config for gpio %d (%d)\n", __func__, + gpio, ret); + return ret; +} +EXPORT_SYMBOL(pm8058_gpio_mux_cfg); + +/* gpio funcs */ +static int read_gpio_bank(struct pm8058 *pmic, unsigned gpio, u8 bank, u8 *val) +{ + int ret; + + ret = pm8058_writeb(pmic->dev, REG_GPIO_CTRL(gpio), (bank & 0x7) << 4); + if (ret) + goto out; + ret = pm8058_readb(pmic->dev, REG_GPIO_CTRL(gpio), val); + if (ret) + goto out; +out: + return ret; +} + +static int pm8058_gpio_request(struct gpio_chip *chip, unsigned gpio) +{ + struct pm8058 *pmic = container_of(chip, struct pm8058, gpio_chip); + unsigned long flags; + int ret; + u8 bank1; + u8 bank3; + u8 bank5; + + spin_lock_irqsave(&pmic->lock, flags); + if (pmic->gpio_flags[gpio] & PM8058_GPIO_CONFIGURED) { + ret = 0; + goto out; + } + + ret = read_gpio_bank(pmic, gpio, 1, &bank1); + if (ret) { + pr_err("%s: can't read bank 1\n", __func__); + goto out; + } + + ret = read_gpio_bank(pmic, gpio, 3, &bank3); + if (ret) { + pr_err("%s: can't read bank 3\n", __func__); + goto out; + } + + ret = read_gpio_bank(pmic, gpio, 5, &bank5); + if (ret) { + pr_err("%s: can't read bank 5\n", __func__); + goto out; + } + + pmic->gpio_flags[gpio] = bank1 & 0x2 ? PM8058_GPIO_OPEN_DRAIN : 0; + pmic->gpio_flags[gpio] |= bank3 & 0x1 ? PM8058_GPIO_HIGH_Z : 0; + pmic->gpio_flags[gpio] |= bank5 & 0x8 ? 0 : PM8058_GPIO_INV_IRQ_POL; + pmic->gpio_flags[gpio] |= PM8058_GPIO_CONFIGURED; + +out: + spin_unlock_irqrestore(&pmic->lock, flags); + return 0; +} + +static void pm8058_gpio_free(struct gpio_chip *chip, unsigned gpio) +{ + struct pm8058 *pmic = container_of(chip, struct pm8058, gpio_chip); + unsigned long flags; + + /* XXX: set high Z maybe?? */ + spin_lock_irqsave(&pmic->lock, flags); + pmic->gpio_flags[gpio] = 0; + spin_unlock_irqrestore(&pmic->lock, flags); +} + +static int gpio_set_dir(struct pm8058 *pmic, unsigned gpio, int dir) +{ + unsigned long flags; + int ret; + u8 val; + + spin_lock_irqsave(&pmic->lock, flags); + /* only need to write bank1 */ + val = (pmic->gpio_flags[gpio] & PM8058_GPIO_OPEN_DRAIN ? 0x1 : 0) << 1; + val |= ((1 << 7) | (1 << 4) | (_dir_map[dir] << 2) | + (dir & PM8058_GPIO_OUTPUT_HIGH ? 0x1 : 0x0)); + ret = pm8058_writeb(pmic->dev, REG_GPIO_CTRL(gpio), val); + if (ret) + pr_err("%s: erorr setting dir %x (%d)\n", __func__, dir, ret); + + spin_unlock_irqrestore(&pmic->lock, flags); + return ret; +} + +static int pm8058_gpio_direction_in(struct gpio_chip *chip, unsigned gpio) +{ + struct pm8058 *pmic = container_of(chip, struct pm8058, gpio_chip); + + return gpio_set_dir(pmic, gpio, PM8058_GPIO_INPUT); +} + +static int pm8058_gpio_direction_out(struct gpio_chip *chip, unsigned gpio, + int val) +{ + struct pm8058 *pmic = container_of(chip, struct pm8058, gpio_chip); + + val = val ? PM8058_GPIO_OUTPUT_HIGH : PM8058_GPIO_OUTPUT; + return gpio_set_dir(pmic, gpio, val); +} + +static void pm8058_gpio_set(struct gpio_chip *chip, unsigned gpio, int val) +{ + struct pm8058 *pmic = container_of(chip, struct pm8058, gpio_chip); + + /* XXX: for now, let's always force the gpio to be an output when + * the user calls this func. I'm not even sure that it's wrong to + * assume that. */ + val = val ? PM8058_GPIO_OUTPUT_HIGH : PM8058_GPIO_OUTPUT; + gpio_set_dir(pmic, gpio, val); +} + +static int pm8058_gpio_get(struct gpio_chip *chip, unsigned gpio) +{ + struct pm8058 *pmic = container_of(chip, struct pm8058, gpio_chip); + int ret; + u8 val; + + /* XXX: assumes gpio maps 1:1 to irq @ 0 */ + ret = read_irq_block_reg(pmic, pmic->irqs[gpio].blk, REG_IRQ_RT_STATUS, + &val); + if (ret) { + pr_err("%s: can't read block status\n", __func__); + goto done; + } + + ret = !!(val & (1 << pmic->irqs[gpio].blk_bit)); + +done: + return ret; +} + +static int pm8058_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) +{ + struct pm8058 *pmic = container_of(chip, struct pm8058, gpio_chip); + return pmic->irq_base + gpio; +} + +static struct gpio_chip pm8058_base_gpio_chip = { + .label = "pm8058", + .owner = THIS_MODULE, + .request = pm8058_gpio_request, + .free = pm8058_gpio_free, + .direction_input = pm8058_gpio_direction_in, + .get = pm8058_gpio_get, + .direction_output = pm8058_gpio_direction_out, + .set = pm8058_gpio_set, + .to_irq = pm8058_gpio_to_irq, +}; + +/* irq funcs */ +static int read_irq_block_reg(struct pm8058 *pmic, u8 blk, u16 reg, u8 *val) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&pmic->lock, flags); + ret = pm8058_writeb(pmic->dev, REG_IRQ_BLK_SEL, blk); + if (ret) { + pr_err("%s: error setting block select (%d)\n", __func__, ret); + goto done; + } + + ret = pm8058_readb(pmic->dev, reg, val); + if (ret) + pr_err("%s: error setting bit select (%d)\n", __func__, ret); + +done: + spin_unlock_irqrestore(&pmic->lock, flags); + return ret; +} + +static int _write_irq_blk_bit_cfg(struct pm8058 *pmic, u8 blk, u8 bit, u8 cfg) +{ + int ret; + + ret = pm8058_writeb(pmic->dev, REG_IRQ_BLK_SEL, blk); + if (ret) { + pr_err("%s: error setting block select (%d)\n", __func__, ret); + goto done; + } + + cfg = (1 << 7) | (cfg & 0xf) | (bit << 4); + ret = pm8058_writeb(pmic->dev, REG_IRQ_CONFIG, cfg); + if (ret) + pr_err("%s: error writing irq cfg (%d)\n", __func__, ret); + +done: + return ret; +} + +static int write_irq_config_locked(struct pm8058 *pmic, unsigned int irq, + u8 cfg) +{ + return _write_irq_blk_bit_cfg(pmic, pmic->irqs[irq].blk, + pmic->irqs[irq].blk_bit, cfg); +} + +static int do_irq_master(struct pm8058 *pmic, int group) +{ + int i; + int j; + int ret; + u8 val; + unsigned long flags; + unsigned long stat; + + ret = pm8058_readb(pmic->dev, pm8058_irq_groups[group].stat_reg, &val); + if (ret) { + pr_err("%s: Can't read master status\n", __func__); + goto done; + } + + if (debug_mask & DEBUG_IRQS) + pr_info("%s: master %d %02x\n", __func__, group, val); + stat = val & pm8058_irq_groups[group].valid_mask; + for_each_set_bit(i, &stat, BITS_PER_BYTE) { + u8 blk = pm8058_irq_groups[group].block_offset + i; + unsigned long blk_stat; + + ret = read_irq_block_reg(pmic, blk, REG_IRQ_IT_STATUS, &val); + if (ret) { + pr_err("%s: can't read block status\n", __func__); + goto done; + } + + blk_stat = val; + for_each_set_bit(j, &blk_stat, BITS_PER_BYTE) { + u8 irq = blk * 8 + j; + + /* XXX: we should mask these out and count em' */ + if (pmic->pmirqs[irq] == 0xffffffffU) { + pr_warning("Unexpected pmirq %d\n", irq); + continue; + } + + local_irq_save(flags); + generic_handle_irq(pmic->pmirqs[irq] + pmic->irq_base); + local_irq_restore(flags); + } + } + +done: + return ret; +} + +static irqreturn_t pm8058_irq_handler(int irq, void *dev) +{ + struct pm8058 *pmic = dev; + int ret; + int i; + u8 root; + + ret = pm8058_readb(pmic->dev, REG_IRQ_ROOT, &root); + if (ret) { + pr_err("%s: Can't read root status\n", __func__); + goto done; + } + + if (debug_mask & DEBUG_IRQS) + pr_info("%s: root %02x\n", __func__, root); + for (i = 0; i < NUM_ROOT_GROUPS; ++i) { + if (root & pm8058_irq_groups[i].root_mask) + do_irq_master(pmic, i); + } + +done: + return IRQ_HANDLED; +} + +static void pm8058_irq_ack(unsigned int _irq) +{ + struct pm8058 *pmic = get_irq_chip_data(_irq); + unsigned int irq = _irq - pmic->irq_base; + unsigned long flags; + + spin_lock_irqsave(&pmic->lock, flags); + write_irq_config_locked(pmic, irq, + pmic->irqs[irq].cfg_val | IRQ_CFG_CLR); + spin_unlock_irqrestore(&pmic->lock, flags); +} + +static void pm8058_irq_mask(unsigned int _irq) +{ + struct pm8058 *pmic = get_irq_chip_data(_irq); + unsigned int irq = _irq - pmic->irq_base; + struct pm8058_irq_info *irq_info = &pmic->irqs[irq]; + unsigned long flags; + + spin_lock_irqsave(&pmic->lock, flags); + irq_info->mask = IRQ_CFG_MASK_FE | IRQ_CFG_MASK_RE; + irq_info->cfg_val = irq_info->cfg | irq_info->mask; + write_irq_config_locked(pmic, irq, irq_info->cfg_val); + spin_unlock_irqrestore(&pmic->lock, flags); +} + +static void pm8058_irq_unmask(unsigned int _irq) +{ + struct pm8058 *pmic = get_irq_chip_data(_irq); + unsigned int irq = _irq - pmic->irq_base; + struct pm8058_irq_info *irq_info = &pmic->irqs[irq]; + unsigned long flags; + + spin_lock_irqsave(&pmic->lock, flags); + irq_info->mask = 0; + irq_info->cfg_val = irq_info->cfg; + write_irq_config_locked(pmic, irq, irq_info->cfg_val); + spin_unlock_irqrestore(&pmic->lock, flags); +} + +static void pm8058_irq_disable(unsigned int irq) +{ + struct irq_desc *desc = irq_to_desc(irq); + + pm8058_irq_mask(irq); + desc->status |= IRQ_MASKED; +} + +static void pm8058_irq_enable(unsigned int irq) +{ + struct irq_desc *desc = irq_to_desc(irq); + + pm8058_irq_unmask(irq); + desc->status &= ~IRQ_MASKED; +} + +static int pm8058_irq_set_type(unsigned int _irq, unsigned int flow_type) +{ + struct pm8058 *pmic = get_irq_chip_data(_irq); + unsigned int irq = _irq - pmic->irq_base; + struct pm8058_irq_info *irq_info = &pmic->irqs[irq]; + unsigned long flags; + int ret; + + spin_lock_irqsave(&pmic->lock, flags); + irq_info->cfg = IRQ_CFG_MASK_RE | IRQ_CFG_MASK_FE; + + if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { + if (flow_type & IRQF_TRIGGER_RISING) + irq_info->cfg &= ~IRQ_CFG_MASK_RE; + if (flow_type & IRQF_TRIGGER_FALLING) + irq_info->cfg &= ~IRQ_CFG_MASK_FE; + } else { + irq_info->cfg |= IRQ_CFG_LVL_SEL; + if (flow_type & IRQF_TRIGGER_HIGH) + irq_info->cfg &= ~IRQ_CFG_MASK_RE; + else + irq_info->cfg &= ~IRQ_CFG_MASK_FE; + } + + /* in case the irq was masked when the type was set, we don't want + * to unmask it */ + irq_info->cfg_val = irq_info->cfg | irq_info->mask; + ret = write_irq_config_locked(pmic, irq, + irq_info->cfg_val | IRQ_CFG_CLR); + spin_unlock_irqrestore(&pmic->lock, flags); + + return ret; +} + +static int pm8058_irq_set_wake(unsigned int _irq, unsigned int on) +{ + struct pm8058 *pmic = get_irq_chip_data(_irq); + unsigned int irq = _irq - pmic->irq_base; + struct pm8058_irq_info *irq_info = &pmic->irqs[irq]; + unsigned long flags; + + spin_lock_irqsave(&pmic->lock, flags); + if (on) { + if (!irq_info->wake) { + irq_info->wake = 1; + pmic->wake_cnt++; + } + } else { + if (irq_info->wake) { + irq_info->wake = 0; + pmic->wake_cnt--; + } + } + spin_unlock_irqrestore(&pmic->lock, flags); + + return 0; +} + +static struct irq_chip pm8058_irq_chip = { + .name = "pm8058", + .ack = pm8058_irq_ack, + .mask = pm8058_irq_mask, + .unmask = pm8058_irq_unmask, + .disable = pm8058_irq_disable, + .enable = pm8058_irq_enable, + .set_type = pm8058_irq_set_type, + .set_wake = pm8058_irq_set_wake, +}; + +static int pm8058_irq_init(struct pm8058 *pmic, unsigned int irq_base) +{ + int i; + int j; + + /* mask/clear all the irqs */ + for (i = 0; i < NUM_BLOCKS; ++i) + for (j = 0; j < IRQS_PER_BLOCK; ++j) + _write_irq_blk_bit_cfg(pmic, i, j, (IRQ_CFG_MASK_RE | + IRQ_CFG_MASK_FE | + IRQ_CFG_CLR)); + + memset(pmic->pmirqs, 0xff, NUM_PMIRQS * sizeof(pmic->pmirqs[0])); + for (i = 0; i < NUM_IRQ_BANKS; ++i) { + struct pm8058_irq_bank *bank = &pm8058_irq_banks[i]; + + for (j = 0; j < bank->cnt; ++j) { + unsigned int irq = bank->start + j; + unsigned int pmirq = bank->offset + j; + + BUG_ON(irq >= PM8058_NUM_IRQS); + + /* by default mask the irq */ + pmic->irqs[irq].cfg = 0; + pmic->irqs[irq].mask = + IRQ_CFG_MASK_RE | IRQ_CFG_MASK_FE; + pmic->irqs[irq].cfg_val = pmic->irqs[irq].mask; + pmic->irqs[irq].blk = pmirq / 8; + pmic->irqs[irq].blk_bit = pmirq % 8; + pmic->pmirqs[pmirq] = irq; + + BUG_ON(pmic->irqs[irq].blk >= NUM_BLOCKS); + + set_irq_chip(irq_base + irq, &pm8058_irq_chip); + set_irq_chip_data(irq_base + irq, pmic); + set_irq_handler(irq_base + irq, handle_edge_irq); + set_irq_flags(irq_base + irq, IRQF_VALID); + } + } + + return 0; +} + +static int pm8058_probe(struct platform_device *pdev) +{ + struct pm8058_platform_data *pdata = pdev->dev.platform_data; + struct pm8058 *pmic; + int devirq; + int ret; + u8 val; + + if (!pdata) { + pr_err("%s: no platform data\n", __func__); + return -EINVAL; + } + + devirq = platform_get_irq(pdev, 0); + if (devirq < 0) { + pr_err("%s: missing devirq\n", __func__); + return devirq; + } + + pmic = kzalloc(sizeof(struct pm8058), GFP_KERNEL); + if (!pmic) { + pr_err("%s: Cannot alloc pm8058 struct\n", __func__); + return -ENOMEM; + } + + /* Read PMIC chip revision */ + ret = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val)); + if (ret) + goto err_read_rev; + pr_info("%s: PMIC revision: %x\n", __func__, val); + + pmic->dev = &pdev->dev; + pmic->irq_base = pdata->irq_base; + pmic->devirq = devirq; + spin_lock_init(&pmic->lock); + platform_set_drvdata(pdev, pmic); + + ret = pm8058_irq_init(pmic, pmic->irq_base); + if (ret) + goto err_irq_init; + + memcpy(&pmic->gpio_chip, &pm8058_base_gpio_chip, + sizeof(struct gpio_chip)); + pmic->gpio_chip.dev = pmic->dev; + pmic->gpio_chip.base = pdata->gpio_base; + pmic->gpio_chip.ngpio = PM8058_NUM_GPIOS; + + ret = gpiochip_add(&pmic->gpio_chip); + if (ret) { + pr_err("%s: can't register gpio chip\n", __func__); + goto err_gpiochip_add; + } + + ret = request_irq(devirq, pm8058_irq_handler, IRQF_TRIGGER_LOW, + "pm8058-irq", pmic); + if (ret) { + pr_err("%s: can't request device irq\n", __func__); + goto err_request_irq; + } + + if (pdata->init) { + ret = pdata->init(pmic->dev); + if (ret) { + pr_err("%s: error in board init\n", __func__); + goto err_pdata_init; + } + } + + return 0; + +err_pdata_init: + free_irq(devirq, pmic); +err_request_irq: + WARN_ON(gpiochip_remove(&pmic->gpio_chip)); +err_gpiochip_add: +err_irq_init: + platform_set_drvdata(pdev, NULL); +err_read_rev: + kfree(pmic); + return ret; +} + +static struct platform_driver pm8058_driver = { + .probe = pm8058_probe, + .driver = { + .name = "pm8058-core", + .owner = THIS_MODULE, + }, +}; + +static int __init pm8058_init(void) +{ + return platform_driver_register(&pm8058_driver); +} +postcore_initcall(pm8058_init); diff --git a/include/linux/mfd/pm8058.h b/include/linux/mfd/pm8058.h new file mode 100644 index 0000000000000..dd83e0f8f1cbb --- /dev/null +++ b/include/linux/mfd/pm8058.h @@ -0,0 +1,132 @@ +/* include/linux/mfd/pm8058.h + * + * Copyright (C) 2010 Google, Inc. + * Author: Dima Zavin + * + * 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_MFD_PM8058_CORE_H +#define __LINUX_MFD_PM8058_CORE_H + +#include + +#define PM8058_NUM_GPIO_IRQS 40 +#define PM8058_NUM_MPP_IRQS 12 +#define PM8058_NUM_KEYPAD_IRQS 2 +#define PM8058_NUM_IRQS (PM8058_NUM_GPIO_IRQS + \ + PM8058_NUM_MPP_IRQS + \ + PM8058_NUM_KEYPAD_IRQS) + +/* be careful if you change this since this is used to map irq <-> gpio */ +#define PM8058_FIRST_GPIO_IRQ 0 +#define PM8058_FIRST_MPP_IRQ (PM8058_FIRST_GPIO_IRQ + \ + PM8058_NUM_GPIO_IRQS) +#define PM8058_FIRST_KEYPAD_IRQ (PM8058_FIRST_MPP_IRQ + \ + PM8058_NUM_MPP_IRQS) + +#define PM8058_KEYPAD_STUCK_IRQ (PM8058_FIRST_KEYPAD_IRQ + 0) +#define PM8058_KEYPAD_IRQ (PM8058_FIRST_KEYPAD_IRQ + 1) + +#define PM8058_GPIO_TO_IRQ(base,gpio) (PM8058_FIRST_GPIO_IRQ + \ + (base) + (gpio)) + +/* these need to match the irq counts/offsets above above */ +#define PM8058_FIRST_GPIO PM8058_FIRST_GPIO_IRQ +#define PM8058_NUM_GPIOS PM8058_NUM_GPIO_IRQS +#define PM8058_FIRST_MPP PM8058_FIRST_MPP_IRQ +#define PM8058_NUM_MPP PM8058_NUM_MPP_IRQS + +#define PM8058_GPIO(base,gpio) ((base) + (gpio) + PM8058_FIRST_GPIO) +/*#define PM8058_MPP(base,mpp) ((base) + (mpp) + PM8058_FIRST_MPP)*/ + +struct pm8058_platform_data { + unsigned int irq_base; + unsigned int gpio_base; + int (*init)(struct device *dev); +}; + +#define PM8058_GPIO_VIN_SRC_VPH_PWR 0x0 /* VDD_L6_L7 */ +#define PM8058_GPIO_VIN_SRC_VREG_BB 0x1 /* VDD_L3_L4_L5 */ +#define PM8058_GPIO_VIN_SRC_VREG_S3 0x2 /* VDD_L0_L1_LVS, 1.8V */ +#define PM8058_GPIO_VIN_SRC_VREG_L3 0x3 /* 1.8V or 2.85 */ +#define PM8058_GPIO_VIN_SRC_VREG_L7 0x4 /* 1.8V */ +#define PM8058_GPIO_VIN_SRC_VREG_L6 0x5 /* 3.3V */ +#define PM8058_GPIO_VIN_SRC_VREG_L5 0x6 /* 2.85V */ +#define PM8058_GPIO_VIN_SRC_VREG_L2 0x7 /* 2.6V */ + +#define PM8058_GPIO_INPUT 0x01 +#define PM8058_GPIO_OUTPUT 0x02 +#define PM8058_GPIO_OUTPUT_HIGH 0x04 + +#define PM8058_GPIO_STRENGTH_OFF 0x0 +#define PM8058_GPIO_STRENGTH_HIGH 0x1 +#define PM8058_GPIO_STRENGTH_MED 0x2 +#define PM8058_GPIO_STRENGTH_LOW 0x3 + +#define PM8058_GPIO_PULL_UP_30 0x0 +#define PM8058_GPIO_PULL_UP_1P5 0x1 +#define PM8058_GPIO_PULL_UP_31P5 0x2 +#define PM8058_GPIO_PULL_UP_1P5_30 0x3 +#define PM8058_GPIO_PULL_DOWN 0x4 +#define PM8058_GPIO_PULL_NONE 0x5 + +#define PM8058_GPIO_FUNC_NORMAL 0x0 +#define PM8058_GPIO_FUNC_PAIRED 0x1 +#define PM8058_GPIO_FUNC_1 0x2 +#define PM8058_GPIO_FUNC_2 0x3 + +/* gpio pin flags */ +#define PM8058_GPIO_OPEN_DRAIN 0x10 +#define PM8058_GPIO_HIGH_Z 0x20 +#define PM8058_GPIO_INV_IRQ_POL 0x40 +#define PM8058_GPIO_CONFIGURED 0x80 + +struct pm8058_pin_config { + u8 vin_src; + u8 dir; + u8 pull_up; + u8 strength; + u8 func; + u8 flags; +}; + +#define PM8058_GPIO_PIN_CONFIG(v,d,p,s,fn,fl) \ + { \ + .vin_src = (v), \ + .dir = (d), \ + .pull_up = (p), \ + .strength = (s), \ + .func = (fn), \ + .flags = (fl), \ + } + +#ifdef CONFIG_PM8058 +int pm8058_readb(struct device *dev, u16 addr, u8 *val); +int pm8058_writeb(struct device *dev, u16 addr, u8 val); +int pm8058_write_buf(struct device *dev, u16 addr, u8 *buf, int cnt); +int pm8058_read_buf(struct device *dev, u16 addr, u8 *buf, int cnt); +int pm8058_gpio_mux_cfg(struct device *dev, unsigned int gpio, + struct pm8058_pin_config *cfg); +#else +static inline int pm8058_readb(struct device *dev, u16 addr, u8 *val) +{ return 0; } +static inline int pm8058_writeb(struct device *dev, u16 addr, u8 val) +{ return 0; } +static inline int pm8058_write_buf(struct device *dev, u16 addr, u8 *buf, + int cnt) { return 0; } +static inline int pm8058_read_buf(struct device *dev, u16 addr, u8 *buf, + int cnt) { return 0; } +static inline int pm8058_gpio_mux_cfg(struct device *dev, unsigned int gpio, + struct pm8058_pin_config *cfg) { return 0; } +#endif + +#endif From 612b78cd27e93a499bddcc2406cd5946b2ca71ff Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 6 Apr 2010 04:08:26 -0700 Subject: [PATCH 0609/2556] mfd: Add pm8058 to Kconfig and Makefile Change-Id: I7b0eaeaaee9d62ae2bac29307aa3ce3d135522b5 Signed-off-by: Dima Zavin --- drivers/mfd/Kconfig | 8 ++++++++ drivers/mfd/Makefile | 1 + 2 files changed, 9 insertions(+) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index fd018366d6701..58d2a62e1d671 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -624,6 +624,14 @@ config MFD_WL1273_CORE driver connects the radio-wl1273 V4L2 module and the wl1273 audio codec. +config PM8058 + bool "Qualcomm PM8058 Power Management IC" + depends on MSM_SSBI && ARCH_MSM7X30 + default y if MSM_SSBI && ARCH_MSM7X30 + help + Say yes here if your board is equipped with the Qualcomm + PM8058 PMIC. + endif # MFD_SUPPORT menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index a54e2c7c6a1c5..d534b8bb5a927 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -82,4 +82,5 @@ obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o obj-$(CONFIG_MFD_VX855) += vx855.o obj-$(CONFIG_MFD_WL1273_CORE) += wl1273-core.o +obj-$(CONFIG_PM8058) += pm8058-core.o obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o From bd5494bac8c468d8d1f3d750896bb6272fe6a13b Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 6 Apr 2010 03:28:35 -0700 Subject: [PATCH 0610/2556] input: keyboard: add driver for the keypad matrix in Qualcomm PM8058 Change-Id: Ic112638227f7388a22a391c7a4780b16a8be0c43 Signed-off-by: Dima Zavin --- drivers/input/keyboard/pm8058-keypad.c | 417 +++++++++++++++++++++++++ drivers/mfd/pm8058-core.c | 54 ++++ include/linux/mfd/pm8058.h | 27 +- 3 files changed, 495 insertions(+), 3 deletions(-) create mode 100644 drivers/input/keyboard/pm8058-keypad.c diff --git a/drivers/input/keyboard/pm8058-keypad.c b/drivers/input/keyboard/pm8058-keypad.c new file mode 100644 index 0000000000000..f1aefd42463ee --- /dev/null +++ b/drivers/input/keyboard/pm8058-keypad.c @@ -0,0 +1,417 @@ +/* drivers/input/keyboard/pm8058-keypad.c + * + * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2009 Code Aurora Forum + * + * Author: Dima Zavin + * - Heavily based on the driver from the Code Aurora Forum. + * + * 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 + +enum { + DEBUG_IRQ = 1U << 0, + DEBUG_KEYS = 1U << 1, +}; +static int debug_mask = 0; +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define REG_KEYP_CTRL 0x148 +#define REG_KEYP_SCAN 0x149 +#define REG_KEYP_TEST 0x14a +#define REG_KEYP_NEW_DATA 0x14b +#define REG_KEYP_OLD_DATA 0x14c + +#define KP_SNS_MIN 5 +#define KP_SNS_MAX 8 +#define KP_DRV_MIN 5 +#define KP_DRV_MAX 18 + +#define KP_CLOCK_FREQ 32768 + +#define KPF_HAS_SYNC_READ 0x00000001 + +struct pm8058_keypad { + struct device *dev; + struct input_dev *input_dev; + int sense_irq; + int stuck_irq; + + int num_sns; + int num_drv; + const unsigned short *keymap; + + u8 key_state[KP_DRV_MAX]; + u8 stuck_state[KP_DRV_MAX]; + + u32 flags; +}; + +/* convenience wrapers */ +static inline int kp_writeb(struct pm8058_keypad *kp, u16 addr, u8 val) +{ + return pm8058_writeb(kp->dev->parent, addr, val); +} + +static inline int kp_readb(struct pm8058_keypad *kp, u16 addr, u8 *val) +{ + return pm8058_readb(kp->dev->parent, addr, val); +} + +static inline int kp_read_buf(struct pm8058_keypad *kp, u16 addr, + u8 *buf, int cnt) +{ + return pm8058_read_buf(kp->dev->parent, addr, buf, cnt); +} + +static int kp_hw_init(struct pm8058_keypad *kp, + struct pm8058_keypad_platform_data *pdata) +{ + int ret; + u8 val; + u8 drv_bits[] = { + 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, + }; + u8 sns_bits[] = { + 0, 0, 0, 0, 0, 0, 1, 2, 3, + }; + + val = ((drv_bits[pdata->num_drv] << 2) | + (sns_bits[pdata->num_sns] << 5)); + ret = kp_writeb(kp, REG_KEYP_CTRL, val); + if (ret) { + pr_err("%s: can't write kp ctrl\n", __func__); + goto out; + } + + val = ((((pdata->drv_hold_clks - 1) & 0x3) << 6) | + ((pdata->scan_delay_shift & 0x7) << 3) | + ((((pdata->debounce_ms / 5) & 0x3)) << 1)); + ret = kp_writeb(kp, REG_KEYP_SCAN, val); + if (ret) { + pr_err("%s: can't write kp scan\n", __func__); + goto out; + } + +out: + return ret; +} + +static int kp_get_scan_data(struct pm8058_keypad *kp, u8 *old, u8 *new) +{ + int ret; + u8 val; + + /* XXX: B0 only? */ + if (kp->flags & KPF_HAS_SYNC_READ) { + ret = kp_readb(kp, REG_KEYP_SCAN, &val); + if (ret) + goto err; + ret = kp_writeb(kp, REG_KEYP_SCAN, val | 1); + if (ret) + goto err; + /* 2 * 32KHz clocks */ + udelay((2 * USEC_PER_SEC / KP_CLOCK_FREQ) + 1); + } + + if (old) { + ret = kp_read_buf(kp, REG_KEYP_OLD_DATA, old, kp->num_drv); + if (ret) + goto done; + } + + ret = kp_read_buf(kp, REG_KEYP_NEW_DATA, new, kp->num_drv); + if (ret) + goto done; + +done: + if (kp->flags & KPF_HAS_SYNC_READ) { + /* 4 * 32KHz clocks */ + udelay((4 * USEC_PER_SEC / KP_CLOCK_FREQ) + 1); + ret = kp_readb(kp, REG_KEYP_SCAN, &val); + if (ret) + goto err; + ret = kp_writeb(kp, REG_KEYP_SCAN, val & (~0x1)); + if (ret) + goto err; + } + +err: + if (ret) + pr_err("%s: can't get scan data\n", __func__); + return ret; +} + +static int kp_process_scan_data(struct pm8058_keypad *kp, u8 *old, u8 *new) +{ + int drv; + int sns; + + for (drv = 0; drv < kp->num_drv; ++drv) { + unsigned long bits_changed = (new[drv] ^ old[drv]) & 0xff; + + for_each_set_bit(sns, &bits_changed, kp->num_sns) { + int key_idx = drv * kp->num_sns + sns; + unsigned int code = kp->keymap[key_idx] ?: KEY_UNKNOWN; + int down = !(new[drv] & (1 << sns)); + + if (debug_mask & DEBUG_KEYS) + pr_info("%s: key [%d:%d] %s\n", __func__, + drv, sns, down ? "down" : "up"); + input_event(kp->input_dev, EV_MSC, MSC_SCAN, key_idx); + input_report_key(kp->input_dev, code, down); + input_sync(kp->input_dev); + } + } + + return 0; +} + +/* + * NOTE: We are reading recent and old data registers blindly + * whenever key-stuck interrupt happens, because events counter doesn't + * get updated when this interrupt happens due to key stuck doesn't get + * considered as key state change. + * + * We are not using old data register contents after they are being read + * because it might report the key which was pressed before the key being stuck + * as stuck key because it's pressed status is stored in the old data + * register. + */ +static irqreturn_t kp_stuck_irq_handler(int irq, void *dev_id) +{ + struct pm8058_keypad *kp = dev_id; + u8 old[KP_DRV_MAX]; + u8 new[KP_DRV_MAX]; + int ret; + + if (debug_mask & DEBUG_IRQ) + pr_info("%s: key stuck!\n", __func__); + + ret = kp_get_scan_data(kp, old, new); + if (ret) { + pr_err("%s: couldn't get scan data\n", __func__); + goto out; + } + kp_process_scan_data(kp, kp->stuck_state, new); + +out: + return IRQ_HANDLED; +} + +static irqreturn_t kp_sense_irq_handler(int irq, void *dev_id) +{ + struct pm8058_keypad *kp = dev_id; + int ret; + u8 old[KP_DRV_MAX]; + u8 new[KP_DRV_MAX]; + u8 val; + + if (debug_mask & DEBUG_IRQ) + pr_info("%s: key event!!\n", __func__); + ret = kp_readb(kp, REG_KEYP_CTRL, &val); + if (ret) { + pr_err("%s: can't read events\n", __func__); + goto out; + } + + /* events counter is gray coded */ + switch(val & 0x3) { + case 0x1: + ret = kp_get_scan_data(kp, NULL, new); + if (ret) + goto out; + kp_process_scan_data(kp, kp->key_state, new); + memcpy(kp->key_state, new, sizeof(new)); + break; + + case 0x2: + pr_debug("%s: some key events were missed\n", __func__); + case 0x3: + ret = kp_get_scan_data(kp, old, new); + if (ret) + goto out; + /* first process scan data in relation to last known + * key state */ + kp_process_scan_data(kp, kp->key_state, old); + kp_process_scan_data(kp, old, new); + memcpy(kp->key_state, new, sizeof(new)); + break; + + case 0x0: + pr_warning("%s: interrupt without any events?!\n", __func__); + break; + } + +out: + if (ret) + pr_err("%s: couldn't get scan data\n", __func__); + + return IRQ_HANDLED; +} + +static int pm8058_keypad_probe(struct platform_device *pdev) +{ + struct pm8058_keypad_platform_data *pdata = pdev->dev.platform_data; + struct pm8058_keypad *kp; + int sense_irq; + int stuck_irq; + int ret; + int i; + u8 val; + + sense_irq = platform_get_irq_byname(pdev, "kp_sense"); + stuck_irq = platform_get_irq_byname(pdev, "kp_stuck"); + + if (!pdata || sense_irq < 0 || stuck_irq < 0) { + pr_err("%s: missing platform data/resources\n", __func__); + return -EINVAL; + } + + if (pdata->num_sns > KP_SNS_MAX || pdata->num_drv > KP_DRV_MAX || + (pdata->drv_hold_clks == 0) || !pdata->keymap) { + pr_err("%s: invalid plaform data\n", __func__); + return -EINVAL; + } + + kp = kzalloc(sizeof(*kp), GFP_KERNEL); + if (!kp) { + pr_err("%s: can't allocate memory for kp struct\n", __func__); + return -ENOMEM; + } + + platform_set_drvdata(pdev, kp); + kp->dev = &pdev->dev; + kp->num_sns = pdata->num_sns; + kp->num_drv = pdata->num_drv; + kp->keymap = pdata->keymap; + kp->sense_irq = sense_irq; + kp->stuck_irq = stuck_irq; + + memset(kp->key_state, 0xff, sizeof(kp->key_state)); + memset(kp->stuck_state, 0xff, sizeof(kp->stuck_state)); + + /* b0 and up have sync_read support */ + kp->flags = KPF_HAS_SYNC_READ; + + kp->input_dev = input_allocate_device(); + if (!kp->input_dev) { + ret = -ENOMEM; + pr_err("%s: Failed to allocate input device\n", __func__); + goto err_input_dev_alloc; + } + + kp->input_dev->name = pdata->name; + input_set_capability(kp->input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(kp->input_dev, kp); + + for (i = 0; i < kp->num_drv * kp->num_sns; ++i) { + unsigned short keycode = kp->keymap[i]; + BUG_ON(keycode && keycode > KEY_MAX); + if (keycode) + input_set_capability(kp->input_dev, EV_KEY, keycode); + } + + ret = input_register_device(kp->input_dev); + if (ret) { + pr_err("%s: can't register input device '%s'\n", __func__, + pdata->name); + goto err_input_dev_reg; + } + + ret = kp_hw_init(kp, pdata); + if (ret) { + pr_err("%s: can't initialize keypad hardware\n", __func__); + goto err_kp_hw_init; + } + + if (pdata->init) { + ret = pdata->init(kp->dev); + if (ret) { + pr_err("%s: can't call board's init\n", __func__); + goto err_pdata_init; + } + } + + ret = request_threaded_irq(kp->sense_irq, NULL, kp_sense_irq_handler, + IRQF_TRIGGER_RISING, "pm8058-keypad-sense", kp); + if (ret) { + pr_err("%s: can't request sense_irq\n", __func__); + goto err_req_sense_irq; + } + ret = request_threaded_irq(kp->stuck_irq, NULL, kp_stuck_irq_handler, + IRQF_TRIGGER_RISING, "pm8058-keypad-stuck", kp); + if (ret) { + pr_err("%s: can't request stuck\n", __func__); + goto err_req_stuck_irq; + } + + enable_irq_wake(kp->sense_irq); + + ret = kp_readb(kp, REG_KEYP_CTRL, &val); + if (ret) { + pr_err("%s: can't read kp ctrl\n", __func__); + goto err_read_kp_ctrl; + } + val |= 1 << 7; + ret = kp_writeb(kp, REG_KEYP_CTRL, val); + if (ret) { + pr_err("%s: can't enable kp\n", __func__); + goto err_kp_enable; + } + + pr_info("%s: %dx%d matrix keypad '%s' registered\n", __func__, + kp->num_drv, kp->num_sns, pdata->name); + + return 0; + +err_kp_enable: +err_read_kp_ctrl: + disable_irq_wake(kp->sense_irq); + free_irq(kp->stuck_irq, kp); +err_req_stuck_irq: + free_irq(kp->sense_irq, kp); +err_req_sense_irq: +err_pdata_init: +err_kp_hw_init: + input_unregister_device(kp->input_dev); +err_input_dev_reg: + input_free_device(kp->input_dev); +err_input_dev_alloc: + platform_set_drvdata(pdev, NULL); + kfree(kp); + return ret; +} + +static struct platform_driver pm8058_keypad_driver = { + .probe = pm8058_keypad_probe, + .driver = { + .name = "pm8058-keypad", + .owner = THIS_MODULE, + }, +}; + +static int __init pm8058_keypad_init(void) +{ + return platform_driver_register(&pm8058_keypad_driver); +} +device_initcall(pm8058_keypad_init); diff --git a/drivers/mfd/pm8058-core.c b/drivers/mfd/pm8058-core.c index b334a6f3445d8..1068ccbc1bb30 100644 --- a/drivers/mfd/pm8058-core.c +++ b/drivers/mfd/pm8058-core.c @@ -665,6 +665,51 @@ static int pm8058_irq_init(struct pm8058 *pmic, unsigned int irq_base) return 0; } +static int add_keypad_device(struct pm8058 *pmic, void *pdata) +{ + struct platform_device *pdev; + struct resource irq_res[2]; + int ret; + + pdev = platform_device_alloc("pm8058-keypad", -1); + if (!pdev) { + pr_err("%s: cannot allocate pdev for keypad\n", __func__); + return -ENOMEM; + } + + pdev->dev.parent = pmic->dev; + pdev->dev.platform_data = pdata; + + memset(&irq_res, 0, sizeof(irq_res)); + irq_res[0].start = pmic->irq_base + PM8058_KEYPAD_IRQ; + irq_res[0].end = irq_res[0].start; + irq_res[0].flags = IORESOURCE_IRQ; + irq_res[0].name = "kp_sense"; + irq_res[1].start = pmic->irq_base + PM8058_KEYPAD_STUCK_IRQ; + irq_res[1].end = irq_res[1].start; + irq_res[1].flags = IORESOURCE_IRQ; + irq_res[1].name = "kp_stuck"; + ret = platform_device_add_resources(pdev, irq_res, ARRAY_SIZE(irq_res)); + if (ret) { + pr_err("%s: can't add irq resources for keypad'\n", __func__); + goto err; + } + + ret = platform_device_add(pdev); + if (ret) { + pr_err("%s: cannot add child keypad platform device for\n", + __func__); + goto err; + } + + return 0; + +err: + if (pdev) + platform_device_put(pdev); + return ret; +} + static int pm8058_probe(struct platform_device *pdev) { struct pm8058_platform_data *pdata = pdev->dev.platform_data; @@ -733,8 +778,17 @@ static int pm8058_probe(struct platform_device *pdev) } } + if (pdata->keypad_pdata) { + ret = add_keypad_device(pmic, pdata->keypad_pdata); + if (ret) { + pr_err("%s: can't add child keypad device\n", __func__); + goto err_add_kp_dev; + } + } + return 0; +err_add_kp_dev: err_pdata_init: free_irq(devirq, pmic); err_request_irq: diff --git a/include/linux/mfd/pm8058.h b/include/linux/mfd/pm8058.h index dd83e0f8f1cbb..0b0b3dcb3d5f5 100644 --- a/include/linux/mfd/pm8058.h +++ b/include/linux/mfd/pm8058.h @@ -33,8 +33,8 @@ #define PM8058_FIRST_KEYPAD_IRQ (PM8058_FIRST_MPP_IRQ + \ PM8058_NUM_MPP_IRQS) -#define PM8058_KEYPAD_STUCK_IRQ (PM8058_FIRST_KEYPAD_IRQ + 0) -#define PM8058_KEYPAD_IRQ (PM8058_FIRST_KEYPAD_IRQ + 1) +#define PM8058_KEYPAD_IRQ (PM8058_FIRST_KEYPAD_IRQ + 0) +#define PM8058_KEYPAD_STUCK_IRQ (PM8058_FIRST_KEYPAD_IRQ + 1) #define PM8058_GPIO_TO_IRQ(base,gpio) (PM8058_FIRST_GPIO_IRQ + \ (base) + (gpio)) @@ -48,10 +48,31 @@ #define PM8058_GPIO(base,gpio) ((base) + (gpio) + PM8058_FIRST_GPIO) /*#define PM8058_MPP(base,mpp) ((base) + (mpp) + PM8058_FIRST_MPP)*/ +struct pm8058_keypad_platform_data { + const char *name; + int num_drv; + int num_sns; + /* delay in ms = 1 << scan_delay_shift, 0-7 */ + int scan_delay_shift; + /* # of 32kHz clock cycles, 1-4 */ + int drv_hold_clks; + /* in increments of 5ms, max 20ms */ + int debounce_ms; + + /* size must be num_drv * num_sns + * index is (drv * num_sns + sns) */ + const unsigned short *keymap; + + int (*init)(struct device *dev); +}; + struct pm8058_platform_data { unsigned int irq_base; unsigned int gpio_base; int (*init)(struct device *dev); + + /* child devices */ + struct pm8058_keypad_platform_data *keypad_pdata; }; #define PM8058_GPIO_VIN_SRC_VPH_PWR 0x0 /* VDD_L6_L7 */ @@ -88,7 +109,7 @@ struct pm8058_platform_data { #define PM8058_GPIO_OPEN_DRAIN 0x10 #define PM8058_GPIO_HIGH_Z 0x20 #define PM8058_GPIO_INV_IRQ_POL 0x40 -#define PM8058_GPIO_CONFIGURED 0x80 +#define PM8058_GPIO_CONFIGURED 0x80 /* FOR INTERNAL USE ONLY */ struct pm8058_pin_config { u8 vin_src; From 8a15165923df7b84eaaaa378a26e2a348a23e9fb Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 6 Apr 2010 01:42:29 -0700 Subject: [PATCH 0611/2556] input: keyboard: add the pm8058 keypard driver to Kconfig and Makefile Change-Id: Ia66e460259d38ac199ff4bc377a41b561b25b097 Signed-off-by: Dima Zavin --- drivers/input/keyboard/Kconfig | 7 +++++++ drivers/input/keyboard/Makefile | 1 + 2 files changed, 8 insertions(+) diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index c7a92028f4509..c3a7a6711ffb4 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -362,6 +362,13 @@ config KEYBOARD_OPENCORES To compile this driver as a module, choose M here; the module will be called opencores-kbd. +config KEYBOARD_PM8058 + bool "Qualcomm PM8058 Matrix Keypad support" + depends on PM8058 + help + Say Y here to enable the driver for the keypad matrix interface + on the Qualcomm PM8058 power management I/C device. + config KEYBOARD_PXA27x tristate "PXA27x/PXA3xx keypad support" depends on PXA27x || PXA3xx || ARCH_MMP diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 468c627a28447..a085153e2522e 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o +obj-$(CONFIG_KEYBOARD_PM8058) += pm8058-keypad.o obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o From 86d15c502a0dd3ebbf7f536458d54a071c43dcee Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sat, 27 Feb 2010 16:32:09 -0800 Subject: [PATCH 0612/2556] [ARM] msm: surf7x30: add ssbi and pm8058 devices Change-Id: Ie9bac364166916422fd35ae3b32d1bd2b7c88d9d Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-surf7x30.c | 49 ++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/arch/arm/mach-msm/board-surf7x30.c b/arch/arm/mach-msm/board-surf7x30.c index e90be714aab75..ab2ef1d5b5994 100644 --- a/arch/arm/mach-msm/board-surf7x30.c +++ b/arch/arm/mach-msm/board-surf7x30.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -28,14 +29,21 @@ #include #include +#include #include #include #include #include +#include #include "devices.h" #include "proc_comm.h" +#define SURF7X30_PM8058_GPIO_BASE FIRST_BOARD_GPIO +#define SURF7X30_PM8058_IRQ_BASE FIRST_BOARD_IRQ + +#define SURF7X30_GPIO_PMIC_INT_N 27 + static struct resource smc91x_resources[] = { [0] = { .start = 0x8A000300, @@ -262,6 +270,47 @@ static void __init surf7x30_map_io(void) msm_clock_init(msm_clocks_7x30, msm_num_clocks_7x30); } +static int surf7x30_pmic_init(void) +{ + int ret; + u32 id; + + pr_info("%s()\n", __func__); + id = PCOM_GPIO_CFG(SURF7X30_GPIO_PMIC_INT_N, 1, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA); + ret = msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + if (ret) + pr_err("%s: gpio %d cfg failed\n", __func__, + SURF7X30_GPIO_PMIC_INT_N); + return ret; +} + +static struct pm8058_platform_data surf7x30_pm8058_pdata = { + .irq_base = SURF7X30_PM8058_IRQ_BASE, + .gpio_base = SURF7X30_PM8058_GPIO_BASE, + .init = surf7x30_pmic_init, +}; + +static struct msm_ssbi_platform_data surf7x30_ssbi_pmic_pdata = { + .slave = { + .name = "pm8058-core", + .irq = MSM_GPIO_TO_INT(SURF7X30_GPIO_PMIC_INT_N), + .platform_data = &surf7x30_pm8058_pdata, + }, + .rspinlock_name = "D:PMIC_SSBI", +}; + +static int __init surf7x30_ssbi_pmic_init(void) +{ + int ret; + ret = gpiochip_reserve(surf7x30_pm8058_pdata.gpio_base, + PM8058_NUM_GPIOS); + WARN(ret, "can't reserve pm8058 gpios. badness will ensue...\n"); + msm_device_ssbi_pmic.dev.platform_data = &surf7x30_ssbi_pmic_pdata; + return platform_device_register(&msm_device_ssbi_pmic); +} +postcore_initcall(surf7x30_ssbi_pmic_init); + MACHINE_START(MSM7X30_SURF, "QCT SURF7X30 Development Board") #ifdef CONFIG_MSM_DEBUG_UART .phys_io = MSM_DEBUG_UART_PHYS, From e3c3ddfd652a4893312cbcd0980829defc7dfbcb Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 9 Apr 2010 02:05:53 -0700 Subject: [PATCH 0613/2556] surf7x30: move pmic/ssbi init into board_init Change-Id: Ibce1fddb49c65ed87d12b597328fe6345e13db27 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-surf7x30.c | 87 ++++++++++++++---------------- 1 file changed, 41 insertions(+), 46 deletions(-) diff --git a/arch/arm/mach-msm/board-surf7x30.c b/arch/arm/mach-msm/board-surf7x30.c index ab2ef1d5b5994..bcf16d205dd15 100644 --- a/arch/arm/mach-msm/board-surf7x30.c +++ b/arch/arm/mach-msm/board-surf7x30.c @@ -222,6 +222,45 @@ static struct platform_device android_usb_device = { }, }; +static struct pm8058_platform_data surf7x30_pm8058_pdata = { + .irq_base = SURF7X30_PM8058_IRQ_BASE, + .gpio_base = SURF7X30_PM8058_GPIO_BASE, +}; + +static struct msm_ssbi_platform_data surf7x30_ssbi_pmic_pdata = { + .slave = { + .name = "pm8058-core", + .irq = MSM_GPIO_TO_INT(SURF7X30_GPIO_PMIC_INT_N), + .platform_data = &surf7x30_pm8058_pdata, + }, + .rspinlock_name = "D:PMIC_SSBI", +}; + +static int __init surf7x30_ssbi_pmic_init(void) +{ + int ret; + u32 id; + + pr_info("%s()\n", __func__); + id = PCOM_GPIO_CFG(SURF7X30_GPIO_PMIC_INT_N, 1, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA); + ret = msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + if (ret) + pr_err("%s: gpio %d cfg failed\n", __func__, + SURF7X30_GPIO_PMIC_INT_N); + + ret = gpiochip_reserve(surf7x30_pm8058_pdata.gpio_base, + PM8058_NUM_GPIOS); + WARN(ret, "can't reserve pm8058 gpios. badness will ensue...\n"); + msm_device_ssbi_pmic.dev.platform_data = &surf7x30_ssbi_pmic_pdata; + return platform_device_register(&msm_device_ssbi_pmic); +} + +extern struct sys_timer msm_timer; + +void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq); + static struct platform_device *devices[] __initdata = { #if !defined(CONFIG_MSM_SERIAL_DEBUGGER) &msm_device_uart1, @@ -234,11 +273,6 @@ static struct platform_device *devices[] __initdata = { &smc91x_device, }; -extern struct sys_timer msm_timer; - -void msm_serial_debug_init(unsigned int base, int irq, - struct device *clk_device, int signal_irq); - static void __init surf7x30_init(void) { printk("%s()\n", __func__); @@ -247,6 +281,8 @@ static void __init surf7x30_init(void) msm_serial_debug_init(MSM_UART1_PHYS, INT_UART1, &msm_device_uart1.dev, 1); #endif + /* do this as early as possible to use pmic gpio/mux facilities */ + surf7x30_ssbi_pmic_init(); msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; platform_add_devices(devices, ARRAY_SIZE(devices)); @@ -270,47 +306,6 @@ static void __init surf7x30_map_io(void) msm_clock_init(msm_clocks_7x30, msm_num_clocks_7x30); } -static int surf7x30_pmic_init(void) -{ - int ret; - u32 id; - - pr_info("%s()\n", __func__); - id = PCOM_GPIO_CFG(SURF7X30_GPIO_PMIC_INT_N, 1, GPIO_INPUT, - GPIO_NO_PULL, GPIO_2MA); - ret = msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); - if (ret) - pr_err("%s: gpio %d cfg failed\n", __func__, - SURF7X30_GPIO_PMIC_INT_N); - return ret; -} - -static struct pm8058_platform_data surf7x30_pm8058_pdata = { - .irq_base = SURF7X30_PM8058_IRQ_BASE, - .gpio_base = SURF7X30_PM8058_GPIO_BASE, - .init = surf7x30_pmic_init, -}; - -static struct msm_ssbi_platform_data surf7x30_ssbi_pmic_pdata = { - .slave = { - .name = "pm8058-core", - .irq = MSM_GPIO_TO_INT(SURF7X30_GPIO_PMIC_INT_N), - .platform_data = &surf7x30_pm8058_pdata, - }, - .rspinlock_name = "D:PMIC_SSBI", -}; - -static int __init surf7x30_ssbi_pmic_init(void) -{ - int ret; - ret = gpiochip_reserve(surf7x30_pm8058_pdata.gpio_base, - PM8058_NUM_GPIOS); - WARN(ret, "can't reserve pm8058 gpios. badness will ensue...\n"); - msm_device_ssbi_pmic.dev.platform_data = &surf7x30_ssbi_pmic_pdata; - return platform_device_register(&msm_device_ssbi_pmic); -} -postcore_initcall(surf7x30_ssbi_pmic_init); - MACHINE_START(MSM7X30_SURF, "QCT SURF7X30 Development Board") #ifdef CONFIG_MSM_DEBUG_UART .phys_io = MSM_DEBUG_UART_PHYS, From 2ec0263f131e437bc2fed4f942b952bc7fffb952 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 6 Apr 2010 01:43:01 -0700 Subject: [PATCH 0614/2556] [ARM] msm: surf7x30: add pmic keypad support Change-Id: I55ac1a4214995ad40650ccaddc930b9264f55a7b Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-surf7x30.c | 71 ++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/arch/arm/mach-msm/board-surf7x30.c b/arch/arm/mach-msm/board-surf7x30.c index bcf16d205dd15..533fe82ebdad3 100644 --- a/arch/arm/mach-msm/board-surf7x30.c +++ b/arch/arm/mach-msm/board-surf7x30.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ #include "proc_comm.h" #define SURF7X30_PM8058_GPIO_BASE FIRST_BOARD_GPIO +#define SURF7X30_PM8058_GPIO(x) (SURF7X30_PM8058_GPIO_BASE + (x)) #define SURF7X30_PM8058_IRQ_BASE FIRST_BOARD_IRQ #define SURF7X30_GPIO_PMIC_INT_N 27 @@ -222,9 +224,78 @@ static struct platform_device android_usb_device = { }, }; +static struct pm8058_pin_config surf7x30_kpd_input_gpio_cfg = { + .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3, + .dir = PM8058_GPIO_INPUT, + .pull_up = PM8058_GPIO_PULL_UP_31P5, + .strength = PM8058_GPIO_STRENGTH_OFF, + .func = PM8058_GPIO_FUNC_NORMAL, + .flags = PM8058_GPIO_INV_IRQ_POL +}; + +static struct pm8058_pin_config surf7x30_kpd_output_gpio_cfg = { + .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3, + .dir = PM8058_GPIO_OUTPUT, + .pull_up = PM8058_GPIO_PULL_NONE, + .strength = PM8058_GPIO_STRENGTH_LOW, + .func = PM8058_GPIO_FUNC_1, + .flags = (PM8058_GPIO_OPEN_DRAIN | + PM8058_GPIO_INV_IRQ_POL), +}; + +static unsigned int surf7x30_pmic_col_gpios[] = { + SURF7X30_PM8058_GPIO(0), SURF7X30_PM8058_GPIO(1), + SURF7X30_PM8058_GPIO(2), SURF7X30_PM8058_GPIO(3), + SURF7X30_PM8058_GPIO(4), SURF7X30_PM8058_GPIO(5), + SURF7X30_PM8058_GPIO(6), SURF7X30_PM8058_GPIO(7), +}; +static unsigned int surf7x30_pmic_row_gpios[] = { + SURF7X30_PM8058_GPIO(8), SURF7X30_PM8058_GPIO(9), + SURF7X30_PM8058_GPIO(10), SURF7X30_PM8058_GPIO(11), + SURF7X30_PM8058_GPIO(12), SURF7X30_PM8058_GPIO(13), + SURF7X30_PM8058_GPIO(14), SURF7X30_PM8058_GPIO(15), + SURF7X30_PM8058_GPIO(16), SURF7X30_PM8058_GPIO(17), + SURF7X30_PM8058_GPIO(18), SURF7X30_PM8058_GPIO(19), +}; + +#define KEYMAP_NUM_ROWS ARRAY_SIZE(surf7x30_pmic_row_gpios) +#define KEYMAP_NUM_COLS ARRAY_SIZE(surf7x30_pmic_col_gpios) +#define KEYMAP_INDEX(row, col) (((row) * KEYMAP_NUM_COLS) + (col)) +#define KEYMAP_SIZE (KEYMAP_NUM_ROWS * KEYMAP_NUM_COLS) + +static int surf7x30_pmic_keypad_init(struct device *dev) +{ + int i; + + for (i = 0; i < KEYMAP_NUM_COLS; ++i) + pm8058_gpio_mux_cfg(dev, surf7x30_pmic_col_gpios[i], + &surf7x30_kpd_input_gpio_cfg); + for (i = 0; i < KEYMAP_NUM_ROWS; ++i) + pm8058_gpio_mux_cfg(dev, surf7x30_pmic_row_gpios[i], + &surf7x30_kpd_output_gpio_cfg); + return 0; +} + +static const unsigned short surf7x30_pmic_keymap[KEYMAP_SIZE] = { + [KEYMAP_INDEX(0, 6)] = KEY_BACK, +}; + +static struct pm8058_keypad_platform_data surf7x30_pmic_keypad_pdata = { + .name = "surf7x30-keypad", + .num_drv = KEYMAP_NUM_ROWS, + .num_sns = KEYMAP_NUM_COLS, + .scan_delay_shift = 5, + .drv_hold_clks = 4, + .debounce_ms = 10, + .keymap = surf7x30_pmic_keymap, + .init = surf7x30_pmic_keypad_init, +}; + static struct pm8058_platform_data surf7x30_pm8058_pdata = { .irq_base = SURF7X30_PM8058_IRQ_BASE, .gpio_base = SURF7X30_PM8058_GPIO_BASE, + + .keypad_pdata = &surf7x30_pmic_keypad_pdata, }; static struct msm_ssbi_platform_data surf7x30_ssbi_pmic_pdata = { From 7eae9eb3a03f1cb19b98151755010c716bf15be9 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 9 Apr 2010 01:55:02 -0700 Subject: [PATCH 0615/2556] [ARM] msm: surf7x30: add support for gpio_matrix for keypad, off by default Change-Id: Ife42d1b505851ff611993678850e560093c3c94f Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-surf7x30.c | 84 +++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-surf7x30.c b/arch/arm/mach-msm/board-surf7x30.c index 533fe82ebdad3..16826b183a5ea 100644 --- a/arch/arm/mach-msm/board-surf7x30.c +++ b/arch/arm/mach-msm/board-surf7x30.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -46,6 +47,8 @@ #define SURF7X30_GPIO_PMIC_INT_N 27 +#define SURF7X30_USE_PMIC_KEYPAD 1 + static struct resource smc91x_resources[] = { [0] = { .start = 0x8A000300, @@ -224,6 +227,7 @@ static struct platform_device android_usb_device = { }, }; +#if SURF7X30_USE_PMIC_KEYPAD static struct pm8058_pin_config surf7x30_kpd_input_gpio_cfg = { .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3, .dir = PM8058_GPIO_INPUT, @@ -243,6 +247,28 @@ static struct pm8058_pin_config surf7x30_kpd_output_gpio_cfg = { PM8058_GPIO_INV_IRQ_POL), }; +#else + +static struct pm8058_pin_config surf7x30_kpd_input_gpio_cfg = { + .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3, + .dir = PM8058_GPIO_INPUT, + .pull_up = PM8058_GPIO_PULL_UP_31P5, + .strength = PM8058_GPIO_STRENGTH_OFF, + .func = PM8058_GPIO_FUNC_NORMAL, + .flags = PM8058_GPIO_INV_IRQ_POL +}; + +static struct pm8058_pin_config surf7x30_kpd_output_gpio_cfg = { + .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3, + .dir = PM8058_GPIO_OUTPUT, + .pull_up = PM8058_GPIO_PULL_NONE, + .strength = PM8058_GPIO_STRENGTH_LOW, + .func = PM8058_GPIO_FUNC_NORMAL, + .flags = (PM8058_GPIO_OPEN_DRAIN | + PM8058_GPIO_INV_IRQ_POL), +}; +#endif + static unsigned int surf7x30_pmic_col_gpios[] = { SURF7X30_PM8058_GPIO(0), SURF7X30_PM8058_GPIO(1), SURF7X30_PM8058_GPIO(2), SURF7X30_PM8058_GPIO(3), @@ -263,7 +289,7 @@ static unsigned int surf7x30_pmic_row_gpios[] = { #define KEYMAP_INDEX(row, col) (((row) * KEYMAP_NUM_COLS) + (col)) #define KEYMAP_SIZE (KEYMAP_NUM_ROWS * KEYMAP_NUM_COLS) -static int surf7x30_pmic_keypad_init(struct device *dev) +static int mux_keypad_gpios(struct device *dev) { int i; @@ -276,6 +302,13 @@ static int surf7x30_pmic_keypad_init(struct device *dev) return 0; } +/* if we are using the pmic matrix h/w, we need to do the muxing inside the + * keypad probe function once the keypad has been setup. */ +static int surf7x30_pmic_keypad_init(struct device *dev) +{ + return mux_keypad_gpios(dev->parent); +} + static const unsigned short surf7x30_pmic_keymap[KEYMAP_SIZE] = { [KEYMAP_INDEX(0, 6)] = KEY_BACK, }; @@ -291,11 +324,57 @@ static struct pm8058_keypad_platform_data surf7x30_pmic_keypad_pdata = { .init = surf7x30_pmic_keypad_init, }; +static struct gpio_event_matrix_info surf7x30_keypad_matrix_info = { + .info.func = gpio_event_matrix_func, + .keymap = surf7x30_pmic_keymap, + .output_gpios = surf7x30_pmic_row_gpios, + .input_gpios = surf7x30_pmic_col_gpios, + .noutputs = KEYMAP_NUM_ROWS, + .ninputs = KEYMAP_NUM_COLS, + .settle_time.tv.nsec = 40 * NSEC_PER_USEC, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_REMOVE_PHANTOM_KEYS |GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/ +}; + +static struct gpio_event_info *surf7x30_keypad_info[] = { + &surf7x30_keypad_matrix_info.info, +}; + +static struct gpio_event_platform_data surf7x30_keypad_data = { + .name = "surf7x30-keypad", + .info = surf7x30_keypad_info, + .info_count = ARRAY_SIZE(surf7x30_keypad_info) +}; + +static struct platform_device surf7x30_keypad_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = 0, + .dev = { + .platform_data = &surf7x30_keypad_data, + }, +}; + +static int surf7x30_pmic_init(struct device *dev) +{ + int ret = 0; + +#if !SURF7X30_USE_PMIC_KEYPAD + /* if we are not using the keypad matrix h/w, but are using the + * gpio_matrix, do the pimuxing here, which is called from pmic's + * probe */ + ret = mux_keypad_gpios(dev); +#endif + return ret; +} + static struct pm8058_platform_data surf7x30_pm8058_pdata = { .irq_base = SURF7X30_PM8058_IRQ_BASE, .gpio_base = SURF7X30_PM8058_GPIO_BASE, + .init = surf7x30_pmic_init, +#if SURF7X30_USE_PMIC_KEYPAD .keypad_pdata = &surf7x30_pmic_keypad_pdata, +#endif }; static struct msm_ssbi_platform_data surf7x30_ssbi_pmic_pdata = { @@ -342,6 +421,9 @@ static struct platform_device *devices[] __initdata = { &usb_mass_storage_device, &android_usb_device, &smc91x_device, +#if !SURF7X30_USE_PMIC_KEYPAD + &surf7x30_keypad_device, +#endif }; static void __init surf7x30_init(void) From dad0c05676b05ab763d2d8ab4e2ac8a91d0595cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 29 Mar 2010 19:46:46 -0700 Subject: [PATCH 0616/2556] [ARM] msm: iomap: move MSM_SHARED_RAM_PHYS for 7x30 (for AMSS 1170/1200) Change-Id: I811c62aa1c3b4cd37e72326c43fb11cffe360ea5 --- arch/arm/mach-msm/include/mach/msm_iomap-7x30.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h b/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h index 9166b4c12a33a..53fb9d2cd4177 100644 --- a/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h +++ b/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h @@ -87,7 +87,7 @@ #define MSM_TCSR_SIZE SZ_4K #define MSM_SHARED_RAM_BASE IOMEM(0xF8100000) -#define MSM_SHARED_RAM_PHYS 0x00100000 +#define MSM_SHARED_RAM_PHYS 0x00000000 #define MSM_SHARED_RAM_SIZE SZ_1M #define MSM_UART1_PHYS 0xACA00000 From 3b057c2d711d72528f2c9f9055c23e7b82ec9f72 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 13 Apr 2010 16:43:39 -0700 Subject: [PATCH 0617/2556] [ARM] msm: 7x30: Move initrd to memory that the Scorpion actually owns Change-Id: I4bb67973ab2b01d9f2df66fa8e2b6c31d81f117c Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Makefile.boot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot index 94c82c6004557..4f02ff6431cd5 100644 --- a/arch/arm/mach-msm/Makefile.boot +++ b/arch/arm/mach-msm/Makefile.boot @@ -9,4 +9,4 @@ initrd_phys-$(CONFIG_ARCH_QSD8X50) := 0x21000000 zreladdr-$(CONFIG_ARCH_MSM7X30) := 0x00208000 params_phys-$(CONFIG_ARCH_MSM7X30) := 0x00200100 -initrd_phys-$(CONFIG_ARCH_MSM7X30) := 0x0A000000 +initrd_phys-$(CONFIG_ARCH_MSM7X30) := 0x01200000 From 83679c290c25f028e8b877f1c3edc5597c73f35e Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 13 Apr 2010 16:44:21 -0700 Subject: [PATCH 0618/2556] [ARM] msm: surf7x30: only use 51M in bank1 Change-Id: I80a94a69f2ec732909028572f45f5199eb2cbbd4 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-surf7x30.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-surf7x30.c b/arch/arm/mach-msm/board-surf7x30.c index 16826b183a5ea..bcae33d2009e7 100644 --- a/arch/arm/mach-msm/board-surf7x30.c +++ b/arch/arm/mach-msm/board-surf7x30.c @@ -450,7 +450,7 @@ static void __init surf7x30_fixup(struct machine_desc *desc, struct tag *tags, mi->nr_banks = 1; mi->bank[0].start = PHYS_OFFSET; mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); - mi->bank[0].size = (101*1024*1024); + mi->bank[0].size = (51*1024*1024); } static void __init surf7x30_map_io(void) From 902d161cc549c246d7bda91543d278a831c60508 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 13 Apr 2010 17:58:21 -0700 Subject: [PATCH 0619/2556] [ARM] msm: surf7x30: use the correct machine id config name Change-Id: I2fb5ded1b91577ff2946f89d1858e060410f9567 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Kconfig | 2 +- arch/arm/mach-msm/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index b111031ee1a68..3b84a535228f3 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -236,7 +236,7 @@ config MACH_QSD8X50_FFA help Select this to support the 8x50 ffa device -config MACH_SURF7X30 +config MACH_MSM7X30_SURF depends on ARCH_MSM7X30 default y bool "QCT SURF7x30 Board" diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index df20a60d72795..707e0339f0c4e 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -72,7 +72,7 @@ obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-keypad.o fish_battery.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-panel.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-mmc.o -obj-$(CONFIG_MACH_SURF7X30) += board-surf7x30.o +obj-$(CONFIG_MACH_MSM7X30_SURF) += board-surf7x30.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o obj-$(CONFIG_ARCH_QSD8X50) += board-qsd8x50.o devices-qsd8x50.o obj-$(CONFIG_ARCH_MSM8X60) += board-msm8x60.o From 2408c29a4b1245e76b61a26804a5c8e437e6570b Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 13 Apr 2010 02:03:41 -0700 Subject: [PATCH 0620/2556] mfd: pm8058: add an anonymous interface to pinmux Change-Id: I67eebaf7de81f9436458241f07d39d3850c9ea4d Signed-off-by: Dima Zavin --- drivers/mfd/pm8058-core.c | 13 +++++++++++++ include/linux/mfd/pm8058.h | 3 +++ 2 files changed, 16 insertions(+) diff --git a/drivers/mfd/pm8058-core.c b/drivers/mfd/pm8058-core.c index 1068ccbc1bb30..3bbde02f17cb8 100644 --- a/drivers/mfd/pm8058-core.c +++ b/drivers/mfd/pm8058-core.c @@ -144,6 +144,8 @@ struct pm8058 { u8 gpio_flags[PM8058_NUM_GPIOS]; }; +static struct pm8058 *the_pm8058; + static int read_irq_block_reg(struct pm8058 *pmic, u8 blk, u16 reg, u8 *val); int pm8058_readb(struct device *dev, u16 addr, u8 *val) @@ -224,6 +226,14 @@ int pm8058_gpio_mux_cfg(struct device *dev, unsigned int gpio, } EXPORT_SYMBOL(pm8058_gpio_mux_cfg); +int pm8058_gpio_mux(unsigned int gpio, struct pm8058_pin_config *cfg) +{ + if (!the_pm8058) + return -ENODEV; + return pm8058_gpio_mux_cfg(the_pm8058->dev, gpio, cfg); +} +EXPORT_SYMBOL(pm8058_gpio_mux); + /* gpio funcs */ static int read_gpio_bank(struct pm8058 *pmic, unsigned gpio, u8 bank, u8 *val) { @@ -770,6 +780,8 @@ static int pm8058_probe(struct platform_device *pdev) goto err_request_irq; } + the_pm8058 = pmic; + if (pdata->init) { ret = pdata->init(pmic->dev); if (ret) { @@ -790,6 +802,7 @@ static int pm8058_probe(struct platform_device *pdev) err_add_kp_dev: err_pdata_init: + the_pm8058 = NULL; free_irq(devirq, pmic); err_request_irq: WARN_ON(gpiochip_remove(&pmic->gpio_chip)); diff --git a/include/linux/mfd/pm8058.h b/include/linux/mfd/pm8058.h index 0b0b3dcb3d5f5..bcb3e61d1570f 100644 --- a/include/linux/mfd/pm8058.h +++ b/include/linux/mfd/pm8058.h @@ -137,6 +137,7 @@ int pm8058_write_buf(struct device *dev, u16 addr, u8 *buf, int cnt); int pm8058_read_buf(struct device *dev, u16 addr, u8 *buf, int cnt); int pm8058_gpio_mux_cfg(struct device *dev, unsigned int gpio, struct pm8058_pin_config *cfg); +int pm8058_gpio_mux(unsigned int gpio, struct pm8058_pin_config *cfg); #else static inline int pm8058_readb(struct device *dev, u16 addr, u8 *val) { return 0; } @@ -148,6 +149,8 @@ static inline int pm8058_read_buf(struct device *dev, u16 addr, u8 *buf, int cnt) { return 0; } static inline int pm8058_gpio_mux_cfg(struct device *dev, unsigned int gpio, struct pm8058_pin_config *cfg) { return 0; } +static inline int pm8058_gpio_mux(unsigned int gpio, + struct pm8058_pin_config *cfg) { return 0; } #endif #endif From 41c4ec2353f339d3378240f28238147aeae5b3d3 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 21 Apr 2010 19:29:57 -0700 Subject: [PATCH 0621/2556] [ARM] mtd: msm_nand: use the right bits for detecting ecc errors on 7x30 Change-Id: Iaa50c818629f085500d8ca794e6cff569ab8fcb3 Signed-off-by: Dima Zavin --- drivers/mtd/devices/msm_nand.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index acd02942efeee..a379a3588dd0f 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -62,6 +62,15 @@ struct msm_nand_chip { #define CFG1_WIDE_FLASH (1U << 1) +#ifdef CONFIG_ARCH_MSM7X30 +#define BUF_STAT_UNCORRECTABLE (1U << 8) +#define BUF_STAT_NUM_ERRS_MASK (0xf) +#else +#define BUF_STAT_UNCORRECTABLE (1U << 3) +#define BUF_STAT_NUM_ERRS_MASK (0x7) +#endif + + /* TODO: move datamover code out */ #define SRC_CRCI_NAND_CMD CMD_SRC_CRCI(DMOV_NAND_CRCI_CMD) @@ -668,7 +677,9 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, pageerr = 0; page_corrected = 0; for (n = start_sector; n <= chip->last_sector; n++) { - if (dma_buffer->data.result[n].buffer_status & 0x8) { + uint32_t buf_stat = + dma_buffer->data.result[n].buffer_status; + if (buf_stat & BUF_STAT_UNCORRECTABLE) { total_uncorrected++; uncorrected[BIT_WORD(pages_read)] |= BIT_MASK(pages_read); @@ -679,8 +690,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, pageerr = -EIO; break; } - sector_corrected = - dma_buffer->data.result[n].buffer_status & 0x7; + sector_corrected =buf_stat & BUF_STAT_NUM_ERRS_MASK; page_corrected += sector_corrected; if (sector_corrected > 1) pageerr = -EUCLEAN; From 91e8216be11ea58a9d3589af5352ca553e98bb00 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 20 Apr 2010 00:53:25 -0700 Subject: [PATCH 0622/2556] [ARM] video: msm: Move the PPP blit code out of mdp.c Change-Id: Ic2dde10a448888b672e82121a05e66044fc7649d Signed-off-by: Dima Zavin --- drivers/video/msm/Kconfig | 5 + drivers/video/msm/Makefile | 4 +- drivers/video/msm/mdp.c | 245 ++++-------------------------------- drivers/video/msm/mdp_hw.h | 9 +- drivers/video/msm/mdp_ppp.c | 244 +++++++++++++++++++++++++++++++++-- drivers/video/msm/mdp_ppp.h | 30 +++-- drivers/video/msm/msm_fb.c | 4 +- 7 files changed, 291 insertions(+), 250 deletions(-) diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig index aef8db52181c1..920af5437dfb7 100644 --- a/drivers/video/msm/Kconfig +++ b/drivers/video/msm/Kconfig @@ -6,6 +6,11 @@ config FB_MSM select FB_CFB_IMAGEBLIT default y +config FB_MSM_MDP_PPP + bool + depends on FB_MSM && (MSM_MDP31 || MSM_MDP22) + default y + config FB_MSM_LCDC bool "Support for integrated LCD controller in qsd8x50" depends on FB_MSM && MSM_MDP31 diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile index 40bc6c9bcfaa6..dc0da6f5f14c6 100644 --- a/drivers/video/msm/Makefile +++ b/drivers/video/msm/Makefile @@ -8,9 +8,9 @@ endif # MDP DMA/PPP engine # -obj-y += mdp.o mdp_ppp.o - +obj-y += mdp.o +obj-$(CONFIG_FB_MSM_MDP_PPP) += mdp_ppp.o obj-$(CONFIG_MSM_MDP22) += mdp_ppp22.o obj-$(CONFIG_MSM_MDP31) += mdp_ppp31.o diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 42e86a61a023e..59db3e3a7e278 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include @@ -38,10 +37,8 @@ struct class *mdp_class; #define MDP_CMD_DEBUG_ACCESS_BASE (0x10000) -static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue); static unsigned int mdp_irq_mask; struct clk *mdp_clk_to_disable_later = 0; -DEFINE_MUTEX(mdp_mutex); static int locked_enable_mdp_irq(struct mdp_info *mdp, uint32_t mask) { @@ -146,8 +143,7 @@ static irqreturn_t mdp_isr(int irq, void *data) } } - if (status & DL0_ROI_DONE) - wake_up(&mdp_ppp_waitqueue); + mdp_ppp_handle_isr(mdp, status); if (status) locked_disable_mdp_irq(mdp, status); @@ -167,25 +163,31 @@ static uint32_t mdp_check_mask(struct mdp_info *mdp, uint32_t mask) return ret; } -static int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq) +int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq) { int ret = 0; unsigned long irq_flags; // pr_info("%s: WAITING for 0x%x\n", __func__, mask); - wait_event_timeout(*wq, !mdp_check_mask(mdp, mask), HZ); + wait_event_timeout(*wq, !mdp_check_mask(mdp, mask), + msecs_to_jiffies(1000)); spin_lock_irqsave(&mdp->lock, irq_flags); if (mdp_irq_mask & mask) { pr_warning("%s: timeout waiting for mdp to complete 0x%x\n", __func__, mask); - printk("GLBL_CLK_ENA: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0000)); - printk("GLBL_CLK_STATE: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0004)); - printk("GLBL_SLEEP_EN: %08X\n", readl(MSM_CLK_CTL_BASE + 0x001C)); - printk("GLBL_CLK_ENA_2: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0220)); - printk("GLBL_CLK_STATE_2: %08X\n", readl(MSM_CLK_CTL_BASE + 0x0224)); - printk("GLBL_CLK_SLEEP_EN_2: %08X\n", readl(MSM_CLK_CTL_BASE + 0x023C)); - mdp_ppp_dump_debug(mdp); + pr_info("GLBL_CLK_ENA: %08X\n", + readl(MSM_CLK_CTL_BASE + 0x0000)); + pr_info("GLBL_CLK_STATE: %08X\n", + readl(MSM_CLK_CTL_BASE + 0x0004)); + pr_info("GLBL_SLEEP_EN: %08X\n", + readl(MSM_CLK_CTL_BASE + 0x001C)); + pr_info("GLBL_CLK_ENA_2: %08X\n", + readl(MSM_CLK_CTL_BASE + 0x0220)); + pr_info("GLBL_CLK_STATE_2: %08X\n", + readl(MSM_CLK_CTL_BASE + 0x0224)); + pr_info("GLBL_CLK_SLEEP_EN_2: %08X\n", + readl(MSM_CLK_CTL_BASE + 0x023C)); locked_disable_mdp_irq(mdp, mask); ret = -ETIMEDOUT; } else { @@ -196,7 +198,7 @@ static int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq) return ret; } -void mdp_dma_wait(struct mdp_device *mdp_dev, int interface) +static void mdp_dma_wait(struct mdp_device *mdp_dev, int interface) { #define MDP_MAX_TIMEOUTS 20 static int timeout_count; @@ -229,11 +231,6 @@ void mdp_dma_wait(struct mdp_device *mdp_dev, int interface) } } -static int mdp_ppp_wait(struct mdp_info *mdp) -{ - return mdp_wait(mdp, DL0_ROI_DONE, &mdp_ppp_waitqueue); -} - static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, uint32_t width, uint32_t height, uint32_t x, uint32_t y) @@ -293,7 +290,7 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, #endif } -void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride, +static void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride, uint32_t width, uint32_t height, uint32_t x, uint32_t y, struct msmfb_callback *callback, int interface) { @@ -320,44 +317,6 @@ void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride, spin_unlock_irqrestore(&mdp->lock, flags); } -static int get_img(struct mdp_img *img, struct fb_info *info, - unsigned long *start, unsigned long *len, - struct file** filep) -{ - int put_needed, ret = 0; - struct file *file; - - if (!get_pmem_file(img->memory_id, start, &vstart, len, filep)) - return 0; - else if (!get_msm_hw3d_file(img->memory_id, &img->offset, start, len, - filep)) - return 0; - - file = fget_light(img->memory_id, &put_needed); - if (file == NULL) - return -1; - - if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) { - *start = info->fix.smem_start; - *len = info->fix.smem_len; - ret = 0; - } else - ret = -1; - fput_light(file, put_needed); - - return ret; -} - -static void put_img(struct file *file) -{ - if (file) { - if (is_pmem_file(file)) - put_pmem_file(file); - else if (is_msm_hw3d_file(file)) - put_msm_hw3d_file(file); - } -} - void mdp_configure_dma(struct mdp_device *mdp_dev) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); @@ -388,7 +347,7 @@ int mdp_check_output_format(struct mdp_device *mdp_dev, int bpp) return 0; } -int mdp_set_output_format(struct mdp_device *mdp_dev, int bpp) +static int mdp_set_output_format(struct mdp_device *mdp_dev, int bpp) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); uint32_t format, pack_pattern; @@ -425,172 +384,11 @@ int mdp_set_output_format(struct mdp_device *mdp_dev, int bpp) return 0; } -static void dump_req(struct mdp_blit_req *req, - unsigned long src_start, unsigned long src_len, - unsigned long dst_start, unsigned long dst_len) -{ - pr_err("flags: 0x%x\n", req->flags); - pr_err("src_start: 0x%08lx\n", src_start); - pr_err("src_len: 0x%08lx\n", src_len); - pr_err("src.offset: 0x%x\n", req->src.offset); - pr_err("src.format: 0x%x\n", req->src.format); - pr_err("src.width: %d\n", req->src.width); - pr_err("src.height: %d\n", req->src.height); - pr_err("src_rect.x: %d\n", req->src_rect.x); - pr_err("src_rect.y: %d\n", req->src_rect.y); - pr_err("src_rect.w: %d\n", req->src_rect.w); - pr_err("src_rect.h: %d\n", req->src_rect.h); - - pr_err("dst_start: 0x%08lx\n", dst_start); - pr_err("dst_len: 0x%08lx\n", dst_len); - pr_err("dst.offset: 0x%x\n", req->dst.offset); - pr_err("dst.format: 0x%x\n", req->dst.format); - pr_err("dst.width: %d\n", req->dst.width); - pr_err("dst.height: %d\n", req->dst.height); - pr_err("dst_rect.x: %d\n", req->dst_rect.x); - pr_err("dst_rect.y: %d\n", req->dst_rect.y); - pr_err("dst_rect.w: %d\n", req->dst_rect.w); - pr_err("dst_rect.h: %d\n", req->dst_rect.h); -} - -int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, - struct file *src_file, unsigned long src_start, unsigned long src_len, - struct file *dst_file, unsigned long dst_start, unsigned long dst_len) -{ - int ret; - enable_mdp_irq(mdp, DL0_ROI_DONE); - ret = mdp_ppp_blit(mdp, req, - src_file, src_start, src_len, - dst_file, dst_start, dst_len); - if (unlikely(ret)) { - disable_mdp_irq(mdp, DL0_ROI_DONE); - return ret; - } - ret = mdp_ppp_wait(mdp); - if (unlikely(ret)) { - printk(KERN_ERR "%s: failed!\n", __func__); - pr_err("original request:\n"); - dump_req(mdp->req, src_start, src_len, dst_start, dst_len); - pr_err("dead request:\n"); - dump_req(req, src_start, src_len, dst_start, dst_len); - BUG(); - return ret; - } - return 0; -} - int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, struct mdp_blit_req *req) { - int ret; - unsigned long src_start = 0, src_len = 0, dst_start = 0, dst_len = 0; struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); - struct file *src_file = 0, *dst_file = 0; - -#ifdef CONFIG_MSM_MDP31 - if (req->flags & MDP_ROT_90) { - if (unlikely(((req->dst_rect.h == 1) && - ((req->src_rect.w != 1) || - (req->dst_rect.w != req->src_rect.h))) || - ((req->dst_rect.w == 1) && ((req->src_rect.h != 1) || - (req->dst_rect.h != req->src_rect.w))))) { - pr_err("mpd_ppp: error scaling when size is 1!\n"); - return -EINVAL; - } - } else { - if (unlikely(((req->dst_rect.w == 1) && - ((req->src_rect.w != 1) || - (req->dst_rect.h != req->src_rect.h))) || - ((req->dst_rect.h == 1) && ((req->src_rect.h != 1) || - (req->dst_rect.h != req->src_rect.h))))) { - pr_err("mpd_ppp: error scaling when size is 1!\n"); - return -EINVAL; - } - } -#endif - - /* WORKAROUND FOR HARDWARE BUG IN BG TILE FETCH */ - if (unlikely(req->src_rect.h == 0 || - req->src_rect.w == 0)) { - printk(KERN_ERR "mdp_ppp: src img of zero size!\n"); - return -EINVAL; - } - if (unlikely(req->dst_rect.h == 0 || - req->dst_rect.w == 0)) - return -EINVAL; - - /* do this first so that if this fails, the caller can always - * safely call put_img */ - if (unlikely(get_img(&req->src, fb, &src_start, &src_len, &src_file))) { - printk(KERN_ERR "mdp_ppp: could not retrieve src image from " - "memory\n"); - return -EINVAL; - } - - if (unlikely(get_img(&req->dst, fb, &dst_start, &dst_len, &dst_file))) { - printk(KERN_ERR "mdp_ppp: could not retrieve dst image from " - "memory\n"); - put_img(src_file); - return -EINVAL; - } - mutex_lock(&mdp_mutex); - - /* transp_masking unimplemented */ - req->transp_mask = MDP_TRANSP_NOP; - mdp->req = req; -#ifndef CONFIG_MSM_MDP31 - if (unlikely((req->transp_mask != MDP_TRANSP_NOP || - req->alpha != MDP_ALPHA_NOP || - HAS_ALPHA(req->src.format)) && - (req->flags & MDP_ROT_90 && - req->dst_rect.w <= 16 && req->dst_rect.h >= 16))) { - int i; - unsigned int tiles = req->dst_rect.h / 16; - unsigned int remainder = req->dst_rect.h % 16; - req->src_rect.w = 16*req->src_rect.w / req->dst_rect.h; - req->dst_rect.h = 16; - for (i = 0; i < tiles; i++) { - ret = mdp_blit_and_wait(mdp, req, - src_file, src_start, src_len, - dst_file, dst_start, dst_len); - if (ret) - goto end; - req->dst_rect.y += 16; - req->src_rect.x += req->src_rect.w; - } - if (!remainder) - goto end; - req->src_rect.w = remainder*req->src_rect.w / req->dst_rect.h; - req->dst_rect.h = remainder; - } -#else - /* Workarounds for MDP 3.1 hardware bugs */ - if (unlikely((mdp_get_bytes_per_pixel(req->dst.format) == 4) && - (req->dst_rect.w != 1) && - (((req->dst_rect.w % 8) == 6) || - ((req->dst_rect.w % 32) == 3) || - ((req->dst_rect.w % 32) == 1)))) { - ret = mdp_ppp_blit_split_width(mdp, req, - src_file, src_start, src_len, - dst_file, dst_start, dst_len); - goto end; - } else if (unlikely((req->dst_rect.w != 1) && (req->dst_rect.h != 1) && - ((req->dst_rect.h % 32) == 3 || - (req->dst_rect.h % 32) == 1))) { - ret = mdp_ppp_blit_split_height(mdp, req, - src_file, src_start, src_len, - dst_file, dst_start, dst_len); - goto end; - } -#endif - ret = mdp_blit_and_wait(mdp, req, - src_file, src_start, src_len, - dst_file, dst_start, dst_len); -end: - put_img(src_file); - put_img(dst_file); - mutex_unlock(&mdp_mutex); - return ret; + return mdp_ppp_blit(mdp, fb, req); } void mdp_set_grp_disp(struct mdp_device *mdp_dev, unsigned disp_id) @@ -791,6 +589,9 @@ int mdp_probe(struct platform_device *pdev) mdp->mdp_dev.check_output_format = mdp_check_output_format; mdp->mdp_dev.configure_dma = mdp_configure_dma; + mdp->enable_irq = enable_mdp_irq; + mdp->disable_irq = disable_mdp_irq; + ret = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp, MDP_DMA_P_DONE, mdp_dma_to_mddi); if (ret) diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h index d80be50e786d4..a2caa9809eb66 100644 --- a/drivers/video/msm/mdp_hw.h +++ b/drivers/video/msm/mdp_hw.h @@ -53,6 +53,9 @@ struct mdp_info { int pack_pattern; bool dma_config_dirty; struct mdp_blit_req *req; + + int (*enable_irq)(struct mdp_info *mdp, uint32_t mask); + int (*disable_irq)(struct mdp_info *mdp, uint32_t mask); }; extern int mdp_out_if_register(struct mdp_device *mdp_dev, int interface, @@ -64,12 +67,8 @@ extern int mdp_out_if_req_irq(struct mdp_device *mdp_dev, int interface, struct mdp_blit_req; struct mdp_device; -int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, - struct file *src_file, unsigned long src_start, - unsigned long src_len, struct file *dst_file, - unsigned long dst_start, unsigned long dst_len); -void mdp_ppp_dump_debug(const struct mdp_info *mdp); +int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #define mdp_writel(mdp, value, offset) writel(value, mdp->base + offset) #define mdp_readl(mdp, offset) readl(mdp->base + offset) diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c index 53e3ec5b1477b..1fd8b1120f243 100644 --- a/drivers/video/msm/mdp_ppp.c +++ b/drivers/video/msm/mdp_ppp.c @@ -15,8 +15,12 @@ #include #include #include +#include +#include #include +#include #include +#include #include #include "mdp_hw.h" @@ -79,6 +83,9 @@ static uint32_t bg_op_chroma[] = { PPP_ARRAY1(CHROMA_SAMP, BG) }; +static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue); +DEFINE_MUTEX(mdp_mutex); + static uint32_t get_luma_offset(struct mdp_img *img, struct mdp_rect *rect, uint32_t bpp) { @@ -434,6 +441,13 @@ static uint32_t get_chroma_base(struct mdp_img *img, uint32_t base, return addr; } +static int mdp_get_bytes_per_pixel(int format) +{ + if (format < 0 || format >= MDP_IMGTYPE_LIMIT) + return -1; + return bytes_per_pixel[format]; +} + #if PPP_DUMP_BLITS #define mdp_writel_dbg(mdp, val, reg) do { \ pr_info("%s: writing 0x%08x=0x%08x\n", __func__, (reg), (val));\ @@ -524,7 +538,7 @@ static void mdp_dump_blit(struct mdp_blit_req *req) } #endif -int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, +static int process_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, struct file *src_file, unsigned long src_start, unsigned long src_len, struct file *dst_file, unsigned long dst_start, unsigned long dst_len) { @@ -636,13 +650,6 @@ int mdp_ppp_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, return 0; } -int mdp_get_bytes_per_pixel(int format) -{ - if (format < 0 || format >= MDP_IMGTYPE_LIMIT) - return -1; - return bytes_per_pixel[format]; -} - #define mdp_dump_register(mdp, reg) \ printk(# reg ": %08x\n", mdp_readl((mdp), (reg))) @@ -659,3 +666,224 @@ void mdp_ppp_dump_debug(const struct mdp_info *mdp) mdp_dump_register(mdp, MDP_INTR_STATUS); mdp_dump_register(mdp, MDP_INTR_ENABLE); } + +static int mdp_ppp_wait(struct mdp_info *mdp) +{ + int ret; + + ret = mdp_wait(mdp, DL0_ROI_DONE, &mdp_ppp_waitqueue); + if (ret) + mdp_ppp_dump_debug(mdp); + return ret; +} + +static int get_img(struct mdp_img *img, struct fb_info *info, + unsigned long *start, unsigned long *len, + struct file** filep) +{ + int put_needed, ret = 0; + struct file *file; + unsigned long vstart; + + if (!get_pmem_file(img->memory_id, start, &vstart, len, filep)) + return 0; + else if (!get_msm_hw3d_file(img->memory_id, &img->offset, start, len, + filep)) + return 0; + + file = fget_light(img->memory_id, &put_needed); + if (file == NULL) + return -1; + + if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) { + *start = info->fix.smem_start; + *len = info->fix.smem_len; + ret = 0; + } else + ret = -1; + fput_light(file, put_needed); + + return ret; +} + +static void put_img(struct file *file) +{ + if (file) { + if (is_pmem_file(file)) + put_pmem_file(file); + else if (is_msm_hw3d_file(file)) + put_msm_hw3d_file(file); + } +} + +static void dump_req(struct mdp_blit_req *req, + unsigned long src_start, unsigned long src_len, + unsigned long dst_start, unsigned long dst_len) +{ + pr_err("flags: 0x%x\n", req->flags); + pr_err("src_start: 0x%08lx\n", src_start); + pr_err("src_len: 0x%08lx\n", src_len); + pr_err("src.offset: 0x%x\n", req->src.offset); + pr_err("src.format: 0x%x\n", req->src.format); + pr_err("src.width: %d\n", req->src.width); + pr_err("src.height: %d\n", req->src.height); + pr_err("src_rect.x: %d\n", req->src_rect.x); + pr_err("src_rect.y: %d\n", req->src_rect.y); + pr_err("src_rect.w: %d\n", req->src_rect.w); + pr_err("src_rect.h: %d\n", req->src_rect.h); + + pr_err("dst_start: 0x%08lx\n", dst_start); + pr_err("dst_len: 0x%08lx\n", dst_len); + pr_err("dst.offset: 0x%x\n", req->dst.offset); + pr_err("dst.format: 0x%x\n", req->dst.format); + pr_err("dst.width: %d\n", req->dst.width); + pr_err("dst.height: %d\n", req->dst.height); + pr_err("dst_rect.x: %d\n", req->dst_rect.x); + pr_err("dst_rect.y: %d\n", req->dst_rect.y); + pr_err("dst_rect.w: %d\n", req->dst_rect.w); + pr_err("dst_rect.h: %d\n", req->dst_rect.h); +} + +int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, unsigned long src_len, + struct file *dst_file, unsigned long dst_start, unsigned long dst_len) +{ + int ret; + mdp->enable_irq(mdp, DL0_ROI_DONE); + ret = process_blit(mdp, req, src_file, src_start, src_len, + dst_file, dst_start, dst_len); + if (unlikely(ret)) { + mdp->disable_irq(mdp, DL0_ROI_DONE); + return ret; + } + ret = mdp_ppp_wait(mdp); + if (unlikely(ret)) { + printk(KERN_ERR "%s: failed!\n", __func__); + pr_err("original request:\n"); + dump_req(mdp->req, src_start, src_len, dst_start, dst_len); + pr_err("dead request:\n"); + dump_req(req, src_start, src_len, dst_start, dst_len); + BUG(); + return ret; + } + return 0; +} + +int mdp_ppp_blit(struct mdp_info *mdp, struct fb_info *fb, + struct mdp_blit_req *req) +{ + int ret; + unsigned long src_start = 0, src_len = 0, dst_start = 0, dst_len = 0; + struct file *src_file = 0, *dst_file = 0; + +#ifdef CONFIG_MSM_MDP31 + if (req->flags & MDP_ROT_90) { + if (unlikely(((req->dst_rect.h == 1) && + ((req->src_rect.w != 1) || + (req->dst_rect.w != req->src_rect.h))) || + ((req->dst_rect.w == 1) && ((req->src_rect.h != 1) || + (req->dst_rect.h != req->src_rect.w))))) { + pr_err("mpd_ppp: error scaling when size is 1!\n"); + return -EINVAL; + } + } else { + if (unlikely(((req->dst_rect.w == 1) && + ((req->src_rect.w != 1) || + (req->dst_rect.h != req->src_rect.h))) || + ((req->dst_rect.h == 1) && ((req->src_rect.h != 1) || + (req->dst_rect.h != req->src_rect.h))))) { + pr_err("mpd_ppp: error scaling when size is 1!\n"); + return -EINVAL; + } + } +#endif + + /* WORKAROUND FOR HARDWARE BUG IN BG TILE FETCH */ + if (unlikely(req->src_rect.h == 0 || + req->src_rect.w == 0)) { + printk(KERN_ERR "mdp_ppp: src img of zero size!\n"); + return -EINVAL; + } + if (unlikely(req->dst_rect.h == 0 || + req->dst_rect.w == 0)) + return -EINVAL; + + /* do this first so that if this fails, the caller can always + * safely call put_img */ + if (unlikely(get_img(&req->src, fb, &src_start, &src_len, &src_file))) { + printk(KERN_ERR "mdp_ppp: could not retrieve src image from " + "memory\n"); + return -EINVAL; + } + + if (unlikely(get_img(&req->dst, fb, &dst_start, &dst_len, &dst_file))) { + printk(KERN_ERR "mdp_ppp: could not retrieve dst image from " + "memory\n"); + put_img(src_file); + return -EINVAL; + } + mutex_lock(&mdp_mutex); + + /* transp_masking unimplemented */ + req->transp_mask = MDP_TRANSP_NOP; + mdp->req = req; +#ifndef CONFIG_MSM_MDP31 + if (unlikely((req->transp_mask != MDP_TRANSP_NOP || + req->alpha != MDP_ALPHA_NOP || + HAS_ALPHA(req->src.format)) && + (req->flags & MDP_ROT_90 && + req->dst_rect.w <= 16 && req->dst_rect.h >= 16))) { + int i; + unsigned int tiles = req->dst_rect.h / 16; + unsigned int remainder = req->dst_rect.h % 16; + req->src_rect.w = 16*req->src_rect.w / req->dst_rect.h; + req->dst_rect.h = 16; + for (i = 0; i < tiles; i++) { + ret = mdp_blit_and_wait(mdp, req, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); + if (ret) + goto end; + req->dst_rect.y += 16; + req->src_rect.x += req->src_rect.w; + } + if (!remainder) + goto end; + req->src_rect.w = remainder*req->src_rect.w / req->dst_rect.h; + req->dst_rect.h = remainder; + } +#else + /* Workarounds for MDP 3.1 hardware bugs */ + if (unlikely((mdp_get_bytes_per_pixel(req->dst.format) == 4) && + (req->dst_rect.w != 1) && + (((req->dst_rect.w % 8) == 6) || + ((req->dst_rect.w % 32) == 3) || + ((req->dst_rect.w % 32) == 1)))) { + ret = mdp_ppp_blit_split_width(mdp, req, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); + goto end; + } else if (unlikely((req->dst_rect.w != 1) && (req->dst_rect.h != 1) && + ((req->dst_rect.h % 32) == 3 || + (req->dst_rect.h % 32) == 1))) { + ret = mdp_ppp_blit_split_height(mdp, req, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); + goto end; + } +#endif + ret = mdp_blit_and_wait(mdp, req, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); +end: + put_img(src_file); + put_img(dst_file); + mutex_unlock(&mdp_mutex); + return ret; +} + +void mdp_ppp_handle_isr(struct mdp_info *mdp, uint32_t mask) +{ + if (mask & DL0_ROI_DONE) + wake_up(&mdp_ppp_waitqueue); +} diff --git a/drivers/video/msm/mdp_ppp.h b/drivers/video/msm/mdp_ppp.h index dda21eca1eb52..ddd7650b6d651 100644 --- a/drivers/video/msm/mdp_ppp.h +++ b/drivers/video/msm/mdp_ppp.h @@ -62,34 +62,42 @@ struct ppp_regs { struct mdp_info; struct mdp_rect; struct mdp_blit_req; +struct fb_info; + +#ifdef CONFIG_FB_MSM_MDP_PPP void mdp_ppp_init_scale(const struct mdp_info *mdp); int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs, struct mdp_rect *src_rect, struct mdp_rect *dst_rect, uint32_t src_format, uint32_t dst_format); int mdp_ppp_load_blur(const struct mdp_info *mdp); +int mdp_ppp_blit(struct mdp_info *mdp, struct fb_info *fb, + struct mdp_blit_req *req); +void mdp_ppp_handle_isr(struct mdp_info *mdp, uint32_t mask); + +int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, unsigned long src_len, + struct file *dst_file, unsigned long dst_start, unsigned long dst_len); #ifndef CONFIG_MSM_MDP31 int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs); #else - int mdp_ppp_blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *req, struct file *src_file, unsigned long src_start, unsigned long src_len, struct file *dst_file, unsigned long dst_start, unsigned long dst_len); int mdp_ppp_blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *req, struct file *src_file, unsigned long src_start, unsigned long src_len, struct file *dst_file, unsigned long dst_start, unsigned long dst_len); - static inline int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, - struct ppp_regs *regs) -{ - return 0; -} -#endif + struct ppp_regs *regs) { return 0; } +#endif /* CONFIG_MSM_MDP31 */ + +#else /* !CONFIG_FB_MSM_MDP_PPP */ + +static inline int mdp_ppp_blit(struct mdp_info *mdp, struct fb_info *fb, + struct mdp_blit_req *req) { return -EINVAL; } +static inline void mdp_ppp_handle_isr(struct mdp_info *mdp, uint32_t mask) {} +#endif /* CONFIG_FB_MSM_MDP_PPP */ -int mdp_get_bytes_per_pixel(int format); -int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, - struct file *src_file, unsigned long src_start, unsigned long src_len, - struct file *dst_file, unsigned long dst_start, unsigned long dst_len); #endif /* _VIDEO_MSM_MDP_PPP_H_ */ diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index 76f2c9eba6148..a21a44a08d742 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -574,13 +574,13 @@ static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg) t1 = ktime_get(); #endif ret = msmfb_blit(p, argp); - if (ret) - return ret; #if PRINT_BLIT_TIME t2 = ktime_get(); DLOG(BLIT_TIME, "total %lld\n", ktime_to_ns(t2) - ktime_to_ns(t1)); #endif + if (ret) + return ret; break; default: printk(KERN_INFO "msmfb unknown ioctl: %d\n", cmd); From a5dec36a962e484bd0e9b1bb2b59f687fab006b0 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 20 Apr 2010 14:24:48 -0700 Subject: [PATCH 0623/2556] [ARM] video: msm: Split out more h/w specific code Change-Id: I0faac53eb6894c7450350ea59ec70a87e19b0579 Signed-off-by: Dima Zavin --- drivers/video/msm/Kconfig | 7 ++- drivers/video/msm/Makefile | 2 + drivers/video/msm/mdp.c | 67 -------------------- drivers/video/msm/mdp_hw.h | 2 + drivers/video/msm/mdp_hw_legacy.c | 87 +++++++++++++++++++++++++ drivers/video/msm/mdp_ppp.c | 101 +++++------------------------- drivers/video/msm/mdp_ppp.h | 61 +++++++++++------- drivers/video/msm/mdp_ppp22.c | 60 +++++++++++++++++- drivers/video/msm/mdp_ppp31.c | 96 ++++++++++++++++++++++++---- 9 files changed, 292 insertions(+), 191 deletions(-) create mode 100644 drivers/video/msm/mdp_hw_legacy.c diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig index 920af5437dfb7..beb0c23e8bdfe 100644 --- a/drivers/video/msm/Kconfig +++ b/drivers/video/msm/Kconfig @@ -6,11 +6,16 @@ config FB_MSM select FB_CFB_IMAGEBLIT default y -config FB_MSM_MDP_PPP +config FB_MSM_LEGACY_MDP bool depends on FB_MSM && (MSM_MDP31 || MSM_MDP22) default y +config FB_MSM_MDP_PPP + bool + depends on FB_MSM_LEGACY_MDP + default y + config FB_MSM_LCDC bool "Support for integrated LCD controller in qsd8x50" depends on FB_MSM && MSM_MDP31 diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile index dc0da6f5f14c6..1eef543b16d2c 100644 --- a/drivers/video/msm/Makefile +++ b/drivers/video/msm/Makefile @@ -10,6 +10,8 @@ endif # obj-y += mdp.o +obj-$(CONFIG_FB_MSM_LEGACY_MDP) += mdp_hw_legacy.o + obj-$(CONFIG_FB_MSM_MDP_PPP) += mdp_ppp.o obj-$(CONFIG_MSM_MDP22) += mdp_ppp22.o obj-$(CONFIG_MSM_MDP31) += mdp_ppp31.o diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 59db3e3a7e278..459d98265ed71 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -481,73 +481,6 @@ int register_mdp_client(struct class_interface *cint) return class_interface_register(cint); } -#include "mdp_csc_table.h" - -void mdp_hw_init(struct mdp_info *mdp) -{ - int n; - - mdp_irq_mask = 0; - - mdp_writel(mdp, 0, MDP_INTR_ENABLE); - - /* debug interface write access */ - mdp_writel(mdp, 1, 0x60); - mdp_writel(mdp, 1, MDP_EBI2_PORTMAP_MODE); - -#ifndef CONFIG_MSM_MDP22 - /* disable lcdc */ - mdp_writel(mdp, 0, MDP_LCDC_EN); - /* enable auto clock gating for all blocks by default */ - mdp_writel(mdp, 0xffffffff, MDP_CGC_EN); - /* reset color/gamma correct parms */ - mdp_writel(mdp, 0, MDP_DMA_P_COLOR_CORRECT_CONFIG); -#endif - - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01f8); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01fc); - mdp_writel(mdp, 1, 0x60); - - for (n = 0; n < ARRAY_SIZE(csc_color_lut); n++) - mdp_writel(mdp, csc_color_lut[n].val, csc_color_lut[n].reg); - - /* clear up unused fg/main registers */ - /* comp.plane 2&3 ystride */ - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0120); - - /* unpacked pattern */ - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x012c); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0130); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0134); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0158); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x015c); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0160); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0170); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0174); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x017c); - - /* comp.plane 2 & 3 */ - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0114); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0118); - - /* clear unused bg registers */ - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01c8); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01d0); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01dc); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e0); - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e4); - - for (n = 0; n < ARRAY_SIZE(csc_matrix_config_table); n++) - mdp_writel(mdp, csc_matrix_config_table[n].val, - csc_matrix_config_table[n].reg); - - mdp_ppp_init_scale(mdp); - -#ifndef CONFIG_MSM_MDP31 - mdp_writel(mdp, 0x04000400, MDP_COMMAND_CONFIG); -#endif -} - int mdp_probe(struct platform_device *pdev) { struct resource *resource; diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h index a2caa9809eb66..dd46f6bbea87c 100644 --- a/drivers/video/msm/mdp_hw.h +++ b/drivers/video/msm/mdp_hw.h @@ -68,6 +68,8 @@ extern int mdp_out_if_req_irq(struct mdp_device *mdp_dev, int interface, struct mdp_blit_req; struct mdp_device; +int mdp_hw_init(struct mdp_info *mdp); + int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #define mdp_writel(mdp, value, offset) writel(value, mdp->base + offset) diff --git a/drivers/video/msm/mdp_hw_legacy.c b/drivers/video/msm/mdp_hw_legacy.c new file mode 100644 index 0000000000000..446118887ae74 --- /dev/null +++ b/drivers/video/msm/mdp_hw_legacy.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010 Google, Inc. + * Author: Dima Zavin + * + * 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 "mdp_hw.h" +#include "mdp_ppp.h" +#include "mdp_csc_table.h" + +#define MDP_CMD_DEBUG_ACCESS_BASE (0x10000) + +int mdp_hw_init(struct mdp_info *mdp) +{ + int n; + + mdp_writel(mdp, 0, MDP_INTR_ENABLE); + + /* debug interface write access */ + mdp_writel(mdp, 1, 0x60); + mdp_writel(mdp, 1, MDP_EBI2_PORTMAP_MODE); + +#ifndef CONFIG_MSM_MDP22 + /* disable lcdc */ + mdp_writel(mdp, 0, MDP_LCDC_EN); + /* enable auto clock gating for all blocks by default */ + mdp_writel(mdp, 0xffffffff, MDP_CGC_EN); + /* reset color/gamma correct parms */ + mdp_writel(mdp, 0, MDP_DMA_P_COLOR_CORRECT_CONFIG); +#endif + + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01f8); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01fc); + mdp_writel(mdp, 1, 0x60); + + for (n = 0; n < ARRAY_SIZE(csc_color_lut); n++) + mdp_writel(mdp, csc_color_lut[n].val, csc_color_lut[n].reg); + + /* clear up unused fg/main registers */ + /* comp.plane 2&3 ystride */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0120); + + /* unpacked pattern */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x012c); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0130); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0134); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0158); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x015c); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0160); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0170); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0174); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x017c); + + /* comp.plane 2 & 3 */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0114); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0118); + + /* clear unused bg registers */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01c8); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01d0); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01dc); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e0); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e4); + + for (n = 0; n < ARRAY_SIZE(csc_matrix_config_table); n++) + mdp_writel(mdp, csc_matrix_config_table[n].val, + csc_matrix_config_table[n].reg); + + mdp_ppp_init_scale(mdp); + +#ifndef CONFIG_MSM_MDP31 + mdp_writel(mdp, 0x04000400, MDP_COMMAND_CONFIG); +#endif + + return 0; +} diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c index 1fd8b1120f243..8f563236366bb 100644 --- a/drivers/video/msm/mdp_ppp.c +++ b/drivers/video/msm/mdp_ppp.c @@ -311,7 +311,7 @@ static void blit_blend(struct mdp_blit_req *req, struct ppp_regs *regs) set_blend_region(&req->dst, &req->dst_rect, regs); } -static int blit_scale(const struct mdp_info *mdp, struct mdp_blit_req *req, +static int blit_scale(struct mdp_info *mdp, struct mdp_blit_req *req, struct ppp_regs *regs) { struct mdp_rect dst_rect; @@ -332,7 +332,7 @@ static int blit_scale(const struct mdp_info *mdp, struct mdp_blit_req *req, } if (mdp_ppp_cfg_scale(mdp, regs, &req->src_rect, &dst_rect, - req->src.format, req->dst.format)) { + req->src.format, req->dst.format)) { DLOG("crap, bad scale\n"); return -1; } @@ -341,7 +341,7 @@ static int blit_scale(const struct mdp_info *mdp, struct mdp_blit_req *req, return 0; } -static void blit_blur(const struct mdp_info *mdp, struct mdp_blit_req *req, +static void blit_blur(struct mdp_info *mdp, struct mdp_blit_req *req, struct ppp_regs *regs) { int ret; @@ -441,7 +441,7 @@ static uint32_t get_chroma_base(struct mdp_img *img, uint32_t base, return addr; } -static int mdp_get_bytes_per_pixel(int format) +int mdp_get_bytes_per_pixel(int format) { if (format < 0 || format >= MDP_IMGTYPE_LIMIT) return -1; @@ -538,7 +538,7 @@ static void mdp_dump_blit(struct mdp_blit_req *req) } #endif -static int process_blit(const struct mdp_info *mdp, struct mdp_blit_req *req, +static int process_blit(struct mdp_info *mdp, struct mdp_blit_req *req, struct file *src_file, unsigned long src_start, unsigned long src_len, struct file *dst_file, unsigned long dst_start, unsigned long dst_len) { @@ -744,7 +744,7 @@ static void dump_req(struct mdp_blit_req *req, pr_err("dst_rect.h: %d\n", req->dst_rect.h); } -int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, +int mdp_ppp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, struct file *src_file, unsigned long src_start, unsigned long src_len, struct file *dst_file, unsigned long dst_start, unsigned long dst_len) { @@ -776,37 +776,9 @@ int mdp_ppp_blit(struct mdp_info *mdp, struct fb_info *fb, unsigned long src_start = 0, src_len = 0, dst_start = 0, dst_len = 0; struct file *src_file = 0, *dst_file = 0; -#ifdef CONFIG_MSM_MDP31 - if (req->flags & MDP_ROT_90) { - if (unlikely(((req->dst_rect.h == 1) && - ((req->src_rect.w != 1) || - (req->dst_rect.w != req->src_rect.h))) || - ((req->dst_rect.w == 1) && ((req->src_rect.h != 1) || - (req->dst_rect.h != req->src_rect.w))))) { - pr_err("mpd_ppp: error scaling when size is 1!\n"); - return -EINVAL; - } - } else { - if (unlikely(((req->dst_rect.w == 1) && - ((req->src_rect.w != 1) || - (req->dst_rect.h != req->src_rect.h))) || - ((req->dst_rect.h == 1) && ((req->src_rect.h != 1) || - (req->dst_rect.h != req->src_rect.h))))) { - pr_err("mpd_ppp: error scaling when size is 1!\n"); - return -EINVAL; - } - } -#endif - - /* WORKAROUND FOR HARDWARE BUG IN BG TILE FETCH */ - if (unlikely(req->src_rect.h == 0 || - req->src_rect.w == 0)) { - printk(KERN_ERR "mdp_ppp: src img of zero size!\n"); - return -EINVAL; - } - if (unlikely(req->dst_rect.h == 0 || - req->dst_rect.w == 0)) - return -EINVAL; + ret = mdp_ppp_validate_blit(mdp, req); + if (ret) + return ret; /* do this first so that if this fails, the caller can always * safely call put_img */ @@ -827,55 +799,10 @@ int mdp_ppp_blit(struct mdp_info *mdp, struct fb_info *fb, /* transp_masking unimplemented */ req->transp_mask = MDP_TRANSP_NOP; mdp->req = req; -#ifndef CONFIG_MSM_MDP31 - if (unlikely((req->transp_mask != MDP_TRANSP_NOP || - req->alpha != MDP_ALPHA_NOP || - HAS_ALPHA(req->src.format)) && - (req->flags & MDP_ROT_90 && - req->dst_rect.w <= 16 && req->dst_rect.h >= 16))) { - int i; - unsigned int tiles = req->dst_rect.h / 16; - unsigned int remainder = req->dst_rect.h % 16; - req->src_rect.w = 16*req->src_rect.w / req->dst_rect.h; - req->dst_rect.h = 16; - for (i = 0; i < tiles; i++) { - ret = mdp_blit_and_wait(mdp, req, - src_file, src_start, src_len, - dst_file, dst_start, dst_len); - if (ret) - goto end; - req->dst_rect.y += 16; - req->src_rect.x += req->src_rect.w; - } - if (!remainder) - goto end; - req->src_rect.w = remainder*req->src_rect.w / req->dst_rect.h; - req->dst_rect.h = remainder; - } -#else - /* Workarounds for MDP 3.1 hardware bugs */ - if (unlikely((mdp_get_bytes_per_pixel(req->dst.format) == 4) && - (req->dst_rect.w != 1) && - (((req->dst_rect.w % 8) == 6) || - ((req->dst_rect.w % 32) == 3) || - ((req->dst_rect.w % 32) == 1)))) { - ret = mdp_ppp_blit_split_width(mdp, req, - src_file, src_start, src_len, - dst_file, dst_start, dst_len); - goto end; - } else if (unlikely((req->dst_rect.w != 1) && (req->dst_rect.h != 1) && - ((req->dst_rect.h % 32) == 3 || - (req->dst_rect.h % 32) == 1))) { - ret = mdp_ppp_blit_split_height(mdp, req, - src_file, src_start, src_len, - dst_file, dst_start, dst_len); - goto end; - } -#endif - ret = mdp_blit_and_wait(mdp, req, - src_file, src_start, src_len, - dst_file, dst_start, dst_len); -end: + + ret = mdp_ppp_do_blit(mdp, req, src_file, src_start, src_len, + dst_file, dst_start, dst_len); + put_img(src_file); put_img(dst_file); mutex_unlock(&mdp_mutex); @@ -887,3 +814,5 @@ void mdp_ppp_handle_isr(struct mdp_info *mdp, uint32_t mask) if (mask & DL0_ROI_DONE) wake_up(&mdp_ppp_waitqueue); } + + diff --git a/drivers/video/msm/mdp_ppp.h b/drivers/video/msm/mdp_ppp.h index ddd7650b6d651..03a1506807b98 100644 --- a/drivers/video/msm/mdp_ppp.h +++ b/drivers/video/msm/mdp_ppp.h @@ -65,39 +65,54 @@ struct mdp_blit_req; struct fb_info; #ifdef CONFIG_FB_MSM_MDP_PPP - -void mdp_ppp_init_scale(const struct mdp_info *mdp); -int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs, - struct mdp_rect *src_rect, struct mdp_rect *dst_rect, - uint32_t src_format, uint32_t dst_format); -int mdp_ppp_load_blur(const struct mdp_info *mdp); +int mdp_get_bytes_per_pixel(int format); int mdp_ppp_blit(struct mdp_info *mdp, struct fb_info *fb, struct mdp_blit_req *req); void mdp_ppp_handle_isr(struct mdp_info *mdp, uint32_t mask); +int mdp_ppp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, + unsigned long src_len, struct file *dst_file, + unsigned long dst_start, unsigned long dst_len); -int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, - struct file *src_file, unsigned long src_start, unsigned long src_len, - struct file *dst_file, unsigned long dst_start, unsigned long dst_len); - -#ifndef CONFIG_MSM_MDP31 +/* these must be provided by h/w specific ppp files */ +void mdp_ppp_init_scale(struct mdp_info *mdp); +int mdp_ppp_cfg_scale(struct mdp_info *mdp, struct ppp_regs *regs, + struct mdp_rect *src_rect, struct mdp_rect *dst_rect, + uint32_t src_format, uint32_t dst_format); +int mdp_ppp_load_blur(struct mdp_info *mdp); int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs); -#else -int mdp_ppp_blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *req, - struct file *src_file, unsigned long src_start, unsigned long src_len, - struct file *dst_file, unsigned long dst_start, unsigned long dst_len); -int mdp_ppp_blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *req, - struct file *src_file, unsigned long src_start, unsigned long src_len, - struct file *dst_file, unsigned long dst_start, unsigned long dst_len); -static inline int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, - struct ppp_regs *regs) { return 0; } -#endif /* CONFIG_MSM_MDP31 */ +int mdp_ppp_validate_blit(struct mdp_info *mdp, struct mdp_blit_req *req); +int mdp_ppp_do_blit(struct mdp_info *mdp, struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, + unsigned long src_len, struct file *dst_file, + unsigned long dst_start, unsigned long dst_len); -#else /* !CONFIG_FB_MSM_MDP_PPP */ +#else +static inline int mdp_get_bytes_per_pixel(int format) { return -1; } static inline int mdp_ppp_blit(struct mdp_info *mdp, struct fb_info *fb, struct mdp_blit_req *req) { return -EINVAL; } static inline void mdp_ppp_handle_isr(struct mdp_info *mdp, uint32_t mask) {} -#endif /* CONFIG_FB_MSM_MDP_PPP */ +static inline int mdp_ppp_blit_and_wait(struct mdp_info *mdp, + struct mdp_blit_req *req, struct file *src_file, + unsigned long src_start, unsigned long src_len, + struct file *dst_file, unsigned long dst_start, + unsigned long dst_len) { return 0; } + +static inline void mdp_ppp_init_scale(struct mdp_info *mdp) {} +static inline int mdp_ppp_cfg_scale(struct mdp_info *mdp, struct ppp_regs *regs, + struct mdp_rect *src_rect, struct mdp_rect *dst_rect, + uint32_t src_format, uint32_t dst_format) { return 0; } +static inline int mdp_ppp_load_blur(struct mdp_info *mdp) { return 0; } +static inline int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs) { return 0; } +static inline int mdp_ppp_validate_blit(struct mdp_info *mdp, struct mdp_blit_req *req) { return -EINVAL; } +static inline int mdp_ppp_do_blit(struct mdp_info *mdp, + struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, + unsigned long src_len, struct file *dst_file, + unsigned long dst_start, unsigned long dst_len) { return 0; } +#endif /* CONFIG_FB_MSM_MDP_PPP */ + #endif /* _VIDEO_MSM_MDP_PPP_H_ */ diff --git a/drivers/video/msm/mdp_ppp22.c b/drivers/video/msm/mdp_ppp22.c index 02fca9a3d11a8..dc4cc2794c052 100644 --- a/drivers/video/msm/mdp_ppp22.c +++ b/drivers/video/msm/mdp_ppp22.c @@ -1016,7 +1016,7 @@ static int scale_params(uint32_t dim_in, uint32_t dim_out, uint32_t origin, return 0; } -int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs, +int mdp_ppp_cfg_scale(struct mdp_info *mdp, struct ppp_regs *regs, struct mdp_rect *src_rect, struct mdp_rect *dst_rect, uint32_t src_format, uint32_t dst_format) { @@ -1070,7 +1070,7 @@ int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs, } -int mdp_ppp_load_blur(const struct mdp_info *mdp) +int mdp_ppp_load_blur(struct mdp_info *mdp) { if (!(downscale_x_table == MDP_DOWNSCALE_BLUR && downscale_y_table == MDP_DOWNSCALE_BLUR)) { @@ -1082,10 +1082,64 @@ int mdp_ppp_load_blur(const struct mdp_info *mdp) return 0; } -void mdp_ppp_init_scale(const struct mdp_info *mdp) +void mdp_ppp_init_scale(struct mdp_info *mdp) { downscale_x_table = MDP_DOWNSCALE_MAX; downscale_y_table = MDP_DOWNSCALE_MAX; load_table(mdp, mdp_upscale_table, ARRAY_SIZE(mdp_upscale_table)); } + +int mdp_ppp_validate_blit(struct mdp_info *mdp, struct mdp_blit_req *req) +{ + /* WORKAROUND FOR HARDWARE BUG IN BG TILE FETCH */ + if (unlikely(req->src_rect.h == 0 || + req->src_rect.w == 0)) { + pr_info("mdp_ppp: src img of zero size!\n"); + return -EINVAL; + } + if (unlikely(req->dst_rect.h == 0 || + req->dst_rect.w == 0)) + return -EINVAL; + + return 0; +} + +int mdp_ppp_do_blit(struct mdp_info *mdp, struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, + unsigned long src_len, struct file *dst_file, + unsigned long dst_start, unsigned long dst_len) +{ + int ret; + + if (unlikely((req->transp_mask != MDP_TRANSP_NOP || + req->alpha != MDP_ALPHA_NOP || + HAS_ALPHA(req->src.format)) && + (req->flags & MDP_ROT_90 && + req->dst_rect.w <= 16 && req->dst_rect.h >= 16))) { + int i; + unsigned int tiles = req->dst_rect.h / 16; + unsigned int remainder = req->dst_rect.h % 16; + req->src_rect.w = 16*req->src_rect.w / req->dst_rect.h; + req->dst_rect.h = 16; + for (i = 0; i < tiles; i++) { + ret = mdp_ppp_blit_and_wait(mdp, req, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); + if (ret) + goto end; + req->dst_rect.y += 16; + req->src_rect.x += req->src_rect.w; + } + if (!remainder) + goto end; + req->src_rect.w = remainder*req->src_rect.w / req->dst_rect.h; + req->dst_rect.h = remainder; + } + + ret = mdp_ppp_blit_and_wait(mdp, req, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); +end: + return ret; +} diff --git a/drivers/video/msm/mdp_ppp31.c b/drivers/video/msm/mdp_ppp31.c index 9e0a3a605d1ee..da4bb0230b880 100644 --- a/drivers/video/msm/mdp_ppp31.c +++ b/drivers/video/msm/mdp_ppp31.c @@ -276,7 +276,7 @@ static int scale_idx(int factor) return idx; } -int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs, +int mdp_ppp_cfg_scale(struct mdp_info *mdp, struct ppp_regs *regs, struct mdp_rect *src_rect, struct mdp_rect *dst_rect, uint32_t src_format, uint32_t dst_format) { @@ -319,12 +319,17 @@ int mdp_ppp_cfg_scale(const struct mdp_info *mdp, struct ppp_regs *regs, return 0; } -int mdp_ppp_load_blur(const struct mdp_info *mdp) +int mdp_ppp_load_blur(struct mdp_info *mdp) { return -ENOTSUPP; } -void mdp_ppp_init_scale(const struct mdp_info *mdp) +int mdp_ppp_cfg_edge_cond(struct mdp_blit_req *req, struct ppp_regs *regs) +{ + return 0; +} + +void mdp_ppp_init_scale(struct mdp_info *mdp) { int scale; for (scale = 0; scale < MDP_SCALE_MAX; ++scale) @@ -332,7 +337,7 @@ void mdp_ppp_init_scale(const struct mdp_info *mdp) } /* Splits a blit into two horizontal stripes. Used to work around MDP bugs */ -int mdp_ppp_blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *req, +static int blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *req, struct file *src_file, unsigned long src_start, unsigned long src_len, struct file *dst_file, unsigned long dst_start, unsigned long dst_len) { @@ -403,7 +408,7 @@ int mdp_ppp_blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *r splitreq.dst_rect.x = d_x_1; splitreq.dst_rect.w = d_w_1; } - ret = mdp_blit_and_wait(mdp, &splitreq, + ret = mdp_ppp_blit_and_wait(mdp, &splitreq, src_file, src_start, src_len, dst_file, dst_start, dst_len); if (ret) @@ -430,14 +435,14 @@ int mdp_ppp_blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *r splitreq.dst_rect.x = d_x_0; splitreq.dst_rect.w = d_w_0; } - ret = mdp_blit_and_wait(mdp, &splitreq, + ret = mdp_ppp_blit_and_wait(mdp, &splitreq, src_file, src_start, src_len, dst_file, dst_start, dst_len); return ret; } /* Splits a blit into two vertical stripes. Used to work around MDP bugs */ -int mdp_ppp_blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *req, +static int blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *req, struct file *src_file, unsigned long src_start, unsigned long src_len, struct file *dst_file, unsigned long dst_start, unsigned long dst_len) { @@ -517,11 +522,11 @@ int mdp_ppp_blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *re if (unlikely((splitreq.dst_rect.h != 1) && ((splitreq.dst_rect.h % 32 == 3) || (splitreq.dst_rect.h % 32) == 1))) - ret = mdp_ppp_blit_split_height(mdp, &splitreq, + ret = blit_split_height(mdp, &splitreq, src_file, src_start, src_len, dst_file, dst_start, dst_len); else - ret = mdp_blit_and_wait(mdp, &splitreq, + ret = mdp_ppp_blit_and_wait(mdp, &splitreq, src_file, src_start, src_len, dst_file, dst_start, dst_len); if (ret) @@ -552,12 +557,81 @@ int mdp_ppp_blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *re if (unlikely((splitreq.dst_rect.h != 1) && ((splitreq.dst_rect.h % 32 == 3) || (splitreq.dst_rect.h % 32) == 1))) - ret = mdp_ppp_blit_split_height(mdp, &splitreq, + ret = blit_split_height(mdp, &splitreq, src_file, src_start, src_len, dst_file, dst_start, dst_len); else - ret = mdp_blit_and_wait(mdp, &splitreq, + ret = mdp_ppp_blit_and_wait(mdp, &splitreq, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); + return ret; +} + + +int mdp_ppp_validate_blit(struct mdp_info *mdp, struct mdp_blit_req *req) +{ + if (req->flags & MDP_ROT_90) { + if (unlikely(((req->dst_rect.h == 1) && + ((req->src_rect.w != 1) || + (req->dst_rect.w != req->src_rect.h))) || + ((req->dst_rect.w == 1) && ((req->src_rect.h != 1) || + (req->dst_rect.h != req->src_rect.w))))) { + pr_err("mpd_ppp: error scaling when size is 1!\n"); + return -EINVAL; + } + } else { + if (unlikely(((req->dst_rect.w == 1) && + ((req->src_rect.w != 1) || + (req->dst_rect.h != req->src_rect.h))) || + ((req->dst_rect.h == 1) && ((req->src_rect.h != 1) || + (req->dst_rect.h != req->src_rect.h))))) { + pr_err("mpd_ppp: error scaling when size is 1!\n"); + return -EINVAL; + } + } + + /* WORKAROUND FOR HARDWARE BUG IN BG TILE FETCH */ + if (unlikely(req->src_rect.h == 0 || + req->src_rect.w == 0)) { + pr_info("mdp_ppp: src img of zero size!\n"); + return -EINVAL; + } + if (unlikely(req->dst_rect.h == 0 || + req->dst_rect.w == 0)) + return -EINVAL; + + return 0; +} + +int mdp_ppp_do_blit(struct mdp_info *mdp, struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, + unsigned long src_len, struct file *dst_file, + unsigned long dst_start, unsigned long dst_len) +{ + int ret; + + /* Workarounds for MDP 3.1 hardware bugs */ + if (unlikely((mdp_get_bytes_per_pixel(req->dst.format) == 4) && + (req->dst_rect.w != 1) && + (((req->dst_rect.w % 8) == 6) || + ((req->dst_rect.w % 32) == 3) || + ((req->dst_rect.w % 32) == 1)))) { + ret = blit_split_width(mdp, req, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); + goto end; + } else if (unlikely((req->dst_rect.w != 1) && (req->dst_rect.h != 1) && + ((req->dst_rect.h % 32) == 3 || + (req->dst_rect.h % 32) == 1))) { + ret = blit_split_height(mdp, req, src_file, src_start, src_len, dst_file, dst_start, dst_len); + goto end; + } + + ret = mdp_ppp_blit_and_wait(mdp, req, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); +end: return ret; } From eb2d5a34cb1edc1a87782280a919f4069bf5ec6f Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sun, 25 Apr 2010 23:24:57 -0700 Subject: [PATCH 0624/2556] [ARM] video: msm: small cleanup of setting dma fb format Change-Id: I4262ef2cb019d8aa363f226e15e6682a0bb5498d Signed-off-by: Dima Zavin --- drivers/video/msm/mdp.c | 20 ++++++++++---------- drivers/video/msm/mdp_hw.h | 8 +++++--- drivers/video/msm/mdp_lcdc.c | 6 ++---- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 459d98265ed71..c4d41ef600e62 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -245,7 +245,7 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, DMA_OUT_SEL_AHB | DMA_IBUF_NONCONTIGUOUS; - dma2_cfg |= mdp->format; + dma2_cfg |= mdp->dma_format; dma2_cfg |= DMA_OUT_SEL_MDDI; @@ -317,19 +317,19 @@ static void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride, spin_unlock_irqrestore(&mdp->lock, flags); } -void mdp_configure_dma(struct mdp_device *mdp_dev) +void mdp_configure_dma_format(struct mdp_device *mdp_dev) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); uint32_t dma_cfg; - if (!mdp->dma_config_dirty) + if (!mdp->dma_format_dirty) return; dma_cfg = mdp_readl(mdp, MDP_DMA_P_CONFIG); dma_cfg &= ~DMA_IBUF_FORMAT_MASK; dma_cfg &= ~DMA_PACK_PATTERN_MASK; - dma_cfg |= (mdp->format | mdp->pack_pattern); + dma_cfg |= (mdp->dma_format | mdp->dma_pack_pattern); mdp_writel(mdp, dma_cfg, MDP_DMA_P_CONFIG); - mdp->dma_config_dirty = false; + mdp->dma_format_dirty = false; return; } @@ -375,10 +375,11 @@ static int mdp_set_output_format(struct mdp_device *mdp_dev, int bpp) default: return -EINVAL; } - if (format != mdp->format || pack_pattern != mdp->pack_pattern) { - mdp->format = format; - mdp->pack_pattern = pack_pattern; - mdp->dma_config_dirty = true; + if (format != mdp->dma_format || + pack_pattern != mdp->dma_pack_pattern) { + mdp->dma_format = format; + mdp->dma_pack_pattern = pack_pattern; + mdp->dma_format_dirty = true; } return 0; @@ -520,7 +521,6 @@ int mdp_probe(struct platform_device *pdev) mdp->mdp_dev.set_grp_disp = mdp_set_grp_disp; mdp->mdp_dev.set_output_format = mdp_set_output_format; mdp->mdp_dev.check_output_format = mdp_check_output_format; - mdp->mdp_dev.configure_dma = mdp_configure_dma; mdp->enable_irq = enable_mdp_irq; mdp->disable_irq = disable_mdp_irq; diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h index dd46f6bbea87c..43a7da044f632 100644 --- a/drivers/video/msm/mdp_hw.h +++ b/drivers/video/msm/mdp_hw.h @@ -49,15 +49,17 @@ struct mdp_info { struct clk *clk; struct clk *ebi1_clk; struct mdp_out_interface out_if[MSM_MDP_NUM_INTERFACES]; - int format; - int pack_pattern; - bool dma_config_dirty; + int dma_format; + int dma_pack_pattern; + bool dma_format_dirty; struct mdp_blit_req *req; int (*enable_irq)(struct mdp_info *mdp, uint32_t mask); int (*disable_irq)(struct mdp_info *mdp, uint32_t mask); }; +void mdp_configure_dma_format(struct mdp_device *mdp_dev); + extern int mdp_out_if_register(struct mdp_device *mdp_dev, int interface, void *private_data, uint32_t dma_mask, mdp_dma_start_func_t dma_start); diff --git a/drivers/video/msm/mdp_lcdc.c b/drivers/video/msm/mdp_lcdc.c index 8a90245250dc4..a88840aa45a54 100644 --- a/drivers/video/msm/mdp_lcdc.c +++ b/drivers/video/msm/mdp_lcdc.c @@ -220,12 +220,10 @@ static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride, { struct mdp_lcdc_info *lcdc = priv; - struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); - if (mdp->dma_config_dirty) - { + if (lcdc->mdp->dma_format_dirty) { mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); mdelay(20); - mdp_dev->configure_dma(mdp_dev); + mdp_configure_dma_format(mdp_dev); mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); } mdp_writel(lcdc->mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE); From 0b1ac49ed2f0a4a882f1fd25028287a5404ca5b3 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sun, 25 Apr 2010 23:25:52 -0700 Subject: [PATCH 0625/2556] [ARM] msm: remove unneeded cfg_dma func ptr in mdp_device Change-Id: I5acf853c6c817fe55c279f5770a963e45d9ecf67 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/include/mach/msm_fb.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-msm/include/mach/msm_fb.h b/arch/arm/mach-msm/include/mach/msm_fb.h index e935a34b3310a..eb8602f828012 100644 --- a/arch/arm/mach-msm/include/mach/msm_fb.h +++ b/arch/arm/mach-msm/include/mach/msm_fb.h @@ -159,7 +159,6 @@ struct mdp_device { int (*blit)(struct mdp_device *mdp, struct fb_info *fb, struct mdp_blit_req *req); void (*set_grp_disp)(struct mdp_device *mdp, uint32_t disp_id); - void (*configure_dma)(struct mdp_device *mdp); int (*check_output_format)(struct mdp_device *mdp, int bpp); int (*set_output_format)(struct mdp_device *mdp, int bpp); }; From a65afff1c75c12bfffea5a950266e6a898147df0 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 26 Apr 2010 16:39:28 -0700 Subject: [PATCH 0626/2556] [ARM] video: msm: use the configured pack pattern for mddi Change-Id: Ib44d5a64db4be41604f81e8149e9471e0c850488 Signed-off-by: Dima Zavin --- drivers/video/msm/mdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index c4d41ef600e62..e533d266b51a1 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -241,11 +241,11 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, dma2_cfg = DMA_PACK_TIGHT | DMA_PACK_ALIGN_LSB | - DMA_PACK_PATTERN_RGB | DMA_OUT_SEL_AHB | DMA_IBUF_NONCONTIGUOUS; dma2_cfg |= mdp->dma_format; + dma2_cfg |= mdp->dma_pack_pattern; dma2_cfg |= DMA_OUT_SEL_MDDI; From 84dfa6ed53ae8f6615e961e8d1412e851dcfb7ac Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 26 Apr 2010 17:24:48 -0700 Subject: [PATCH 0627/2556] [ARM] msm: Add config variable for MDP 4.0, implied by msm7x30 Change-Id: Ia0ed35e649e682c639e2529c6cc24a9041dc8322 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Kconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 3b84a535228f3..500ca2c86d0f6 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -73,6 +73,11 @@ config MSM_MDP31 depends on ARCH_QSD8X50 default y +config MSM_MDP40 + bool + depends on ARCH_MSM7X30 + default y + config MSM_REMOTE_SPINLOCK_DEKKERS bool From 92408efd9b5b78dc0a86e099eeb8ecc065e0cb3d Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 28 Apr 2010 18:10:23 -0700 Subject: [PATCH 0628/2556] [ARM] msm: fb: let board specify vsync irq, panel caps for mddi Change-Id: I9fe94cb0b8dac920f453714606ae1c43f260dc0e Signed-off-by: Dima Zavin --- arch/arm/mach-msm/include/mach/msm_fb.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/mach-msm/include/mach/msm_fb.h b/arch/arm/mach-msm/include/mach/msm_fb.h index eb8602f828012..339fa463a455e 100644 --- a/arch/arm/mach-msm/include/mach/msm_fb.h +++ b/arch/arm/mach-msm/include/mach/msm_fb.h @@ -92,6 +92,8 @@ struct msm_mddi_platform_data { /* fixup the mfr name, product id */ void (*fixup)(uint16_t *mfr_name, uint16_t *product_id); + int vsync_irq; + struct resource *fb_resource; /*optional*/ /* number of clients in the list that follows */ int num_clients; @@ -180,6 +182,9 @@ struct msm_mddi_bridge_platform_data { int (*unblank)(struct msm_mddi_bridge_platform_data *, struct msm_mddi_client_data *); struct msm_fb_data fb_data; + + /* board file will identify what capabilities the panel supports */ + uint32_t panel_caps; }; From a8f27b539cdcc74221b307e26b33591160e0fb24 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 28 Apr 2010 18:12:20 -0700 Subject: [PATCH 0629/2556] [ARM] video: msm: add vsync irq resource to mddi client pdev if present Change-Id: I911dda1ebd45e2ee33da6724a4b1f2607ae9906d Signed-off-by: Dima Zavin --- drivers/video/msm/mddi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c index 5ad08a4b49358..df5f808a6c096 100644 --- a/drivers/video/msm/mddi.c +++ b/drivers/video/msm/mddi.c @@ -101,6 +101,7 @@ struct mddi_info { char client_name[20]; struct platform_device client_pdev; + struct resource client_vsync_res; }; static void mddi_init_rev_encap(struct mddi_info *mddi); @@ -877,6 +878,15 @@ static int __devinit mddi_probe(struct platform_device *pdev) goto error_mddi_interface; } + if (pdata->vsync_irq) { + mddi->client_vsync_res.start = pdata->vsync_irq; + mddi->client_vsync_res.end = pdata->vsync_irq; + mddi->client_vsync_res.flags = IORESOURCE_IRQ; + mddi->client_vsync_res.name = "vsync"; + mddi->client_pdev.resource = &mddi->client_vsync_res; + mddi->client_pdev.num_resources = 1; + } + mddi->client_pdev.dev.platform_data = &mddi->client_data; printk(KERN_INFO "mddi: publish: %s\n", mddi->client_name); platform_device_register(&mddi->client_pdev); From a189d0022195ad6973712d1ab61ba28e8b4d0e2c Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 3 May 2010 21:26:08 -0700 Subject: [PATCH 0630/2556] [ARM] video: msm: rename dummy mddi client to simple. Change-Id: If2eed4f1c8e07574ae2f6f10715834365db9270f Signed-off-by: Dima Zavin --- drivers/video/msm/Makefile | 2 +- drivers/video/msm/{mddi_client_dummy.c => mddi_client_simple.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename drivers/video/msm/{mddi_client_dummy.c => mddi_client_simple.c} (100%) diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile index 1eef543b16d2c..b0863661040d5 100644 --- a/drivers/video/msm/Makefile +++ b/drivers/video/msm/Makefile @@ -22,7 +22,7 @@ obj-y += mddi.o # MDDI client/panel drivers # -obj-y += mddi_client_dummy.o +obj-y += mddi_client_simple.o obj-y += mddi_client_toshiba.o obj-y += mddi_client_nt35399.o diff --git a/drivers/video/msm/mddi_client_dummy.c b/drivers/video/msm/mddi_client_simple.c similarity index 100% rename from drivers/video/msm/mddi_client_dummy.c rename to drivers/video/msm/mddi_client_simple.c From 03fdc120ce23b1dea500f79041a59e22e51007df Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 4 May 2010 01:27:57 -0700 Subject: [PATCH 0631/2556] [ARM] video: msm: add basic bridge support to simple mddi client Change-Id: I5afbd2ff7f26c649054c880940df4bfa302a9237 Signed-off-by: Dima Zavin --- drivers/video/msm/mddi_client_simple.c | 208 ++++++++++++++++++++----- 1 file changed, 173 insertions(+), 35 deletions(-) diff --git a/drivers/video/msm/mddi_client_simple.c b/drivers/video/msm/mddi_client_simple.c index d2a091cebe2c3..1613fca03344f 100644 --- a/drivers/video/msm/mddi_client_simple.c +++ b/drivers/video/msm/mddi_client_simple.c @@ -1,9 +1,11 @@ -/* drivers/video/msm_fb/mddi_client_dummy.c +/* drivers/video/msm_fb/mddi_client_simple.c * - * Support for "dummy" mddi client devices which require no - * special initialization code. + * Support for simple mddi client devices which require no special + * initialization code except for what may be provided in the board file. + * If the clients do not provide board specific code, this driver's + * panel operations are no-ops. * - * Copyright (C) 2007 Google Incorporated + * Copyright (C) 2007-2010, Google Incorporated * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -15,84 +17,220 @@ * GNU General Public License for more details. */ -#include -#include +#include #include +#include #include +#include +#include +#include #include struct panel_info { - struct platform_device pdev; - struct msm_panel_data panel_data; + struct platform_device pdev; + struct msm_mddi_client_data *client_data; + struct msm_panel_data panel_data; + struct msmfb_callback *fb_callback; + wait_queue_head_t vsync_wait; + int got_vsync; + int irq; }; -static int mddi_dummy_suspend(struct msm_panel_data *panel_data) +#define to_panel_info(pd) container_of((pd), struct panel_info, panel_data) + +static void mddi_simple_request_vsync(struct msm_panel_data *panel_data, + struct msmfb_callback *callback) { - return 0; + struct panel_info *panel = to_panel_info(panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + panel->fb_callback = callback; + if (panel->got_vsync) { + panel->got_vsync = 0; + client_data->activate_link(client_data); /* clears interrupt */ + } } -static int mddi_dummy_resume(struct msm_panel_data *panel_data) +static void mddi_simple_wait_vsync(struct msm_panel_data *panel_data) { - return 0; + struct panel_info *panel = to_panel_info(panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + int ret; + + if (panel->got_vsync) { + panel->got_vsync = 0; + client_data->activate_link(client_data); /* clears interrupt */ + } + + ret = wait_event_timeout(panel->vsync_wait, panel->got_vsync, HZ/2); + if (!ret && !panel->got_vsync) + pr_err("mddi_client_simple: timeout waiting for vsync\n"); + + panel->got_vsync = 0; + /* interrupt clears when screen dma starts */ } -static int mddi_dummy_blank(struct msm_panel_data *panel_data) + +static int mddi_simple_suspend(struct msm_panel_data *panel_data) { + struct panel_info *panel = to_panel_info(panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + int ret; + + if (!bridge_data->uninit) + return 0; + + ret = bridge_data->uninit(bridge_data, client_data); + if (ret) { + pr_info("%s: non zero return from uninit\n", __func__); + return ret; + } + client_data->suspend(client_data); return 0; } -static int mddi_dummy_unblank(struct msm_panel_data *panel_data) +static int mddi_simple_resume(struct msm_panel_data *panel_data) { - return 0; + struct panel_info *panel = to_panel_info(panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + if (!bridge_data->init) + return 0; + + client_data->resume(client_data); + return bridge_data->init(bridge_data, client_data); +} + +static int mddi_simple_blank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = to_panel_info(panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + if (!bridge_data->blank) + return 0; + return bridge_data->blank(bridge_data, client_data); +} + +static int mddi_simple_unblank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = to_panel_info(panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + if (!bridge_data->unblank) + return 0; + return bridge_data->unblank(bridge_data, client_data); +} + +static irqreturn_t handle_vsync_irq(int irq, void *data) +{ + struct panel_info *panel = data; + + panel->got_vsync = 1; + if (panel->fb_callback) { + panel->fb_callback->func(panel->fb_callback); + panel->fb_callback = NULL; + } + + wake_up(&panel->vsync_wait); + return IRQ_HANDLED; } -static int mddi_dummy_probe(struct platform_device *pdev) +static int mddi_simple_probe(struct platform_device *pdev) { struct msm_mddi_client_data *client_data = pdev->dev.platform_data; - struct panel_info *panel = - kzalloc(sizeof(struct panel_info), GFP_KERNEL); + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + struct panel_info *panel; int ret; + + pr_debug("%s()\n", __func__); + + panel = kzalloc(sizeof(struct panel_info), GFP_KERNEL); if (!panel) return -ENOMEM; + platform_set_drvdata(pdev, panel); - panel->panel_data.suspend = mddi_dummy_suspend; - panel->panel_data.resume = mddi_dummy_resume; - panel->panel_data.blank = mddi_dummy_blank; - panel->panel_data.unblank = mddi_dummy_unblank; - panel->panel_data.caps = MSMFB_CAP_PARTIAL_UPDATES; + + init_waitqueue_head(&panel->vsync_wait); + + panel->irq = platform_get_irq_byname(pdev, "vsync"); + if (panel->irq >= 0) { + ret = request_irq(panel->irq, handle_vsync_irq, + IRQF_TRIGGER_RISING, "mddi_c_simple_vsync", + panel); + if (ret) { + pr_err("%s: request vsync irq %d failed (%d)\n", + __func__, panel->irq, ret); + goto err_req_irq; + } + + panel->panel_data.wait_vsync = mddi_simple_wait_vsync; + panel->panel_data.request_vsync = mddi_simple_request_vsync; + } + + panel->client_data = client_data; + panel->panel_data.suspend = mddi_simple_suspend; + panel->panel_data.resume = mddi_simple_resume; + panel->panel_data.blank = mddi_simple_blank; + panel->panel_data.unblank = mddi_simple_unblank; + panel->panel_data.caps = bridge_data->panel_caps; + panel->panel_data.fb_data = &bridge_data->fb_data; + panel->pdev.name = "msm_panel"; panel->pdev.id = pdev->id; platform_device_add_resources(&panel->pdev, client_data->fb_resource, 1); - panel->panel_data.fb_data = client_data->private_client_data; panel->pdev.dev.platform_data = &panel->panel_data; + + if (bridge_data->init) + bridge_data->init(bridge_data, client_data); + ret = platform_device_register(&panel->pdev); if (ret) { - kfree(panel); - return ret; + pr_err("%s: Can't register platform device\n", __func__); + goto err_plat_dev_reg; } + return 0; + +err_plat_dev_reg: + if (panel->irq >= 0) + free_irq(panel->irq, panel); +err_req_irq: + platform_set_drvdata(pdev, NULL); + kfree(panel); + return ret; } -static int mddi_dummy_remove(struct platform_device *pdev) +static int mddi_simple_remove(struct platform_device *pdev) { struct panel_info *panel = platform_get_drvdata(pdev); kfree(panel); return 0; } -static struct platform_driver mddi_client_dummy = { - .probe = mddi_dummy_probe, - .remove = mddi_dummy_remove, - .driver = { .name = "mddi_c_dummy" }, +static struct platform_driver mddi_client_simple = { + .probe = mddi_simple_probe, + .remove = mddi_simple_remove, + .driver = { + .owner = THIS_MODULE, + .name = "mddi_c_simple" + }, }; -static int __init mddi_client_dummy_init(void) +static int __init mddi_client_simple_init(void) { - platform_driver_register(&mddi_client_dummy); + platform_driver_register(&mddi_client_simple); return 0; } -module_init(mddi_client_dummy_init); - +module_init(mddi_client_simple_init); From f23a49f72ee2db5268e063d4261ddca01fe2616d Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 4 May 2010 14:26:18 -0700 Subject: [PATCH 0632/2556] [ARM] video: msm: remove nt35399 mddi client driver This driver has been obsoleted by the mddi simple client. Change-Id: Iacbe178ffc0fb413ba217a74947c77730c39f411 Signed-off-by: Dima Zavin --- drivers/video/msm/Makefile | 1 - drivers/video/msm/mddi_client_nt35399.c | 257 ------------------------ 2 files changed, 258 deletions(-) delete mode 100644 drivers/video/msm/mddi_client_nt35399.c diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile index b0863661040d5..44e0038942fd1 100644 --- a/drivers/video/msm/Makefile +++ b/drivers/video/msm/Makefile @@ -24,7 +24,6 @@ obj-y += mddi.o # obj-y += mddi_client_simple.o obj-y += mddi_client_toshiba.o -obj-y += mddi_client_nt35399.o # MDP LCD controller driver obj-$(CONFIG_FB_MSM_LCDC) += mdp_lcdc.o diff --git a/drivers/video/msm/mddi_client_nt35399.c b/drivers/video/msm/mddi_client_nt35399.c deleted file mode 100644 index f239f4a25e014..0000000000000 --- a/drivers/video/msm/mddi_client_nt35399.c +++ /dev/null @@ -1,257 +0,0 @@ -/* drivers/video/msm_fb/mddi_client_nt35399.c - * - * Support for Novatek NT35399 MDDI client of Sapphire - * - * Copyright (C) 2008 HTC Incorporated - * Author: Solomon Chiu (solomon_chiu@htc.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 - -static DECLARE_WAIT_QUEUE_HEAD(nt35399_vsync_wait); - -struct panel_info { - struct msm_mddi_client_data *client_data; - struct platform_device pdev; - struct msm_panel_data panel_data; - struct msmfb_callback *fb_callback; - struct work_struct panel_work; - struct workqueue_struct *fb_wq; - int nt35399_got_int; -}; - -static void -nt35399_request_vsync(struct msm_panel_data *panel_data, - struct msmfb_callback *callback) -{ - struct panel_info *panel = container_of(panel_data, struct panel_info, - panel_data); - struct msm_mddi_client_data *client_data = panel->client_data; - - panel->fb_callback = callback; - if (panel->nt35399_got_int) { - panel->nt35399_got_int = 0; - client_data->activate_link(client_data); /* clears interrupt */ - } -} - -static void nt35399_wait_vsync(struct msm_panel_data *panel_data) -{ - struct panel_info *panel = container_of(panel_data, struct panel_info, - panel_data); - struct msm_mddi_client_data *client_data = panel->client_data; - - if (panel->nt35399_got_int) { - panel->nt35399_got_int = 0; - client_data->activate_link(client_data); /* clears interrupt */ - } - - if (wait_event_timeout(nt35399_vsync_wait, panel->nt35399_got_int, - HZ/2) == 0) - printk(KERN_ERR "timeout waiting for VSYNC\n"); - - panel->nt35399_got_int = 0; - /* interrupt clears when screen dma starts */ -} - -static int nt35399_suspend(struct msm_panel_data *panel_data) -{ - struct panel_info *panel = container_of(panel_data, struct panel_info, - panel_data); - struct msm_mddi_client_data *client_data = panel->client_data; - - struct msm_mddi_bridge_platform_data *bridge_data = - client_data->private_client_data; - int ret; - - ret = bridge_data->uninit(bridge_data, client_data); - if (ret) { - printk(KERN_INFO "mddi nt35399 client: non zero return from " - "uninit\n"); - return ret; - } - client_data->suspend(client_data); - return 0; -} - -static int nt35399_resume(struct msm_panel_data *panel_data) -{ - struct panel_info *panel = container_of(panel_data, struct panel_info, - panel_data); - struct msm_mddi_client_data *client_data = panel->client_data; - - struct msm_mddi_bridge_platform_data *bridge_data = - client_data->private_client_data; - int ret; - - client_data->resume(client_data); - ret = bridge_data->init(bridge_data, client_data); - if (ret) - return ret; - return 0; -} - -static int nt35399_blank(struct msm_panel_data *panel_data) -{ - struct panel_info *panel = container_of(panel_data, struct panel_info, - panel_data); - struct msm_mddi_client_data *client_data = panel->client_data; - struct msm_mddi_bridge_platform_data *bridge_data = - client_data->private_client_data; - - return bridge_data->blank(bridge_data, client_data); -} - -static int nt35399_unblank(struct msm_panel_data *panel_data) -{ - struct panel_info *panel = container_of(panel_data, struct panel_info, - panel_data); - struct msm_mddi_client_data *client_data = panel->client_data; - struct msm_mddi_bridge_platform_data *bridge_data = - client_data->private_client_data; - - return bridge_data->unblank(bridge_data, client_data); -} - -irqreturn_t nt35399_vsync_interrupt(int irq, void *data) -{ - struct panel_info *panel = data; - - panel->nt35399_got_int = 1; - - if (panel->fb_callback) { - panel->fb_callback->func(panel->fb_callback); - panel->fb_callback = NULL; - } - - wake_up(&nt35399_vsync_wait); - - return IRQ_HANDLED; -} - -static int setup_vsync(struct panel_info *panel, int init) -{ - int ret; - int gpio = 97; - unsigned int irq; - - if (!init) { - ret = 0; - goto uninit; - } - ret = gpio_request(gpio, "vsync"); - if (ret) - goto err_request_gpio_failed; - - ret = gpio_direction_input(gpio); - if (ret) - goto err_gpio_direction_input_failed; - - ret = irq = gpio_to_irq(gpio); - if (ret < 0) - goto err_get_irq_num_failed; - - ret = request_irq(irq, nt35399_vsync_interrupt, IRQF_TRIGGER_RISING, - "vsync", panel); - if (ret) - goto err_request_irq_failed; - - printk(KERN_INFO "vsync on gpio %d now %d\n", - gpio, gpio_get_value(gpio)); - return 0; - -uninit: - free_irq(gpio_to_irq(gpio), panel->client_data); -err_request_irq_failed: -err_get_irq_num_failed: -err_gpio_direction_input_failed: - gpio_free(gpio); -err_request_gpio_failed: - return ret; -} - -static int mddi_nt35399_probe(struct platform_device *pdev) -{ - struct msm_mddi_client_data *client_data = pdev->dev.platform_data; - struct msm_mddi_bridge_platform_data *bridge_data = - client_data->private_client_data; - - int ret; - - struct panel_info *panel = kzalloc(sizeof(struct panel_info), - GFP_KERNEL); - - printk(KERN_DEBUG "%s: enter.\n", __func__); - - if (!panel) - return -ENOMEM; - platform_set_drvdata(pdev, panel); - - ret = setup_vsync(panel, 1); - if (ret) { - dev_err(&pdev->dev, "mddi_nt35399_setup_vsync failed\n"); - return ret; - } - - panel->client_data = client_data; - panel->panel_data.suspend = nt35399_suspend; - panel->panel_data.resume = nt35399_resume; - panel->panel_data.wait_vsync = nt35399_wait_vsync; - panel->panel_data.request_vsync = nt35399_request_vsync; - panel->panel_data.blank = nt35399_blank; - panel->panel_data.unblank = nt35399_unblank; - panel->panel_data.fb_data = &bridge_data->fb_data; - panel->panel_data.caps = 0; - - panel->pdev.name = "msm_panel"; - panel->pdev.id = pdev->id; - panel->pdev.resource = client_data->fb_resource; - panel->pdev.num_resources = 1; - panel->pdev.dev.platform_data = &panel->panel_data; - - if (bridge_data->init) - bridge_data->init(bridge_data, client_data); - - platform_device_register(&panel->pdev); - - return 0; -} - -static int mddi_nt35399_remove(struct platform_device *pdev) -{ - struct panel_info *panel = platform_get_drvdata(pdev); - - setup_vsync(panel, 0); - kfree(panel); - return 0; -} - -static struct platform_driver mddi_client_0bda_8a47 = { - .probe = mddi_nt35399_probe, - .remove = mddi_nt35399_remove, - .driver = { .name = "mddi_c_0bda_8a47" }, -}; - -static int __init mddi_client_nt35399_init(void) -{ - return platform_driver_register(&mddi_client_0bda_8a47); -} - -module_init(mddi_client_nt35399_init); - From 40e27aacda409f9d832ca00021e79138d491e0da Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 28 Apr 2010 18:17:39 -0700 Subject: [PATCH 0633/2556] [ARM] video: msm: make toshiba mddi bridge use the vsync irq resource Change-Id: Ic144c00907cd2e02df85b71a7ca53bc065aa7b95 Signed-off-by: Dima Zavin --- drivers/video/msm/mddi_client_toshiba.c | 60 +++++++------------------ 1 file changed, 16 insertions(+), 44 deletions(-) diff --git a/drivers/video/msm/mddi_client_toshiba.c b/drivers/video/msm/mddi_client_toshiba.c index f9bc932ac46b3..21e0ac73c4e06 100644 --- a/drivers/video/msm/mddi_client_toshiba.c +++ b/drivers/video/msm/mddi_client_toshiba.c @@ -60,6 +60,7 @@ struct panel_info { struct msm_panel_data panel_data; struct msmfb_callback *toshiba_callback; int toshiba_got_int; + int irq; }; @@ -175,47 +176,6 @@ irqreturn_t toshiba_vsync_interrupt(int irq, void *data) return IRQ_HANDLED; } -static int setup_vsync(struct panel_info *panel, - int init) -{ - int ret; - int gpio = 97; - unsigned int irq; - - if (!init) { - ret = 0; - goto uninit; - } - ret = gpio_request(gpio, "vsync"); - if (ret) - goto err_request_gpio_failed; - - ret = gpio_direction_input(gpio); - if (ret) - goto err_gpio_direction_input_failed; - - ret = irq = gpio_to_irq(gpio); - if (ret < 0) - goto err_get_irq_num_failed; - - ret = request_irq(irq, toshiba_vsync_interrupt, IRQF_TRIGGER_RISING, - "vsync", panel); - if (ret) - goto err_request_irq_failed; - printk(KERN_INFO "vsync on gpio %d now %d\n", - gpio, gpio_get_value(gpio)); - return 0; - -uninit: - free_irq(gpio_to_irq(gpio), panel); -err_request_irq_failed: -err_get_irq_num_failed: -err_gpio_direction_input_failed: - gpio_free(gpio); -err_request_gpio_failed: - return ret; -} - static int mddi_toshiba_probe(struct platform_device *pdev) { int ret; @@ -232,10 +192,16 @@ static int mddi_toshiba_probe(struct platform_device *pdev) client_data->remote_write(client_data, GPIOSEL_VWAKEINT, GPIOSEL); client_data->remote_write(client_data, INTMASK_VWAKEOUT, INTMASK); - ret = setup_vsync(panel, 1); + ret = platform_get_irq_byname(pdev, "vsync"); + if (ret < 0) + goto err_plat_get_irq; + + panel->irq = ret; + ret = request_irq(panel->irq, toshiba_vsync_interrupt, + IRQF_TRIGGER_RISING, "vsync", panel); if (ret) { dev_err(&pdev->dev, "mddi_bridge_setup_vsync failed\n"); - return ret; + goto err_req_irq; } panel->client_data = client_data; @@ -258,13 +224,19 @@ static int mddi_toshiba_probe(struct platform_device *pdev) platform_device_register(&panel->pdev); return 0; + +err_req_irq: +err_plat_get_irq: + kfree(panel); + return ret; } static int mddi_toshiba_remove(struct platform_device *pdev) { struct panel_info *panel = platform_get_drvdata(pdev); - setup_vsync(panel, 0); + platform_set_drvdata(pdev, NULL); + free_irq(panel->irq, panel); kfree(panel); return 0; } From ff53379768591f86de582280f3078c7d66c11614 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 28 Apr 2010 18:13:45 -0700 Subject: [PATCH 0634/2556] [ARM] msm: sapphire: use simple mddi client instead of nt35399, use vsync irq Change-Id: I53139f77d925ca034c8242dab1c1a32e06ae0dc4 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-sapphire-panel.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-sapphire-panel.c b/arch/arm/mach-msm/board-sapphire-panel.c index b2030c0913376..f71ca0113c5fe 100644 --- a/arch/arm/mach-msm/board-sapphire-panel.c +++ b/arch/arm/mach-msm/board-sapphire-panel.c @@ -36,6 +36,8 @@ #define DEBUG_SAPPHIRE_PANEL 0 #define userid 0xD10 +#define VSYNC_GPIO 97 + enum sapphire_panel_type { SAPPHIRE_PANEL_SHARP = 0, SAPPHIRE_PANEL_TOPPOLY, @@ -1199,6 +1201,7 @@ static struct msm_mddi_platform_data mddi_pdata = { .clk_rate = 122880000, .power_client = sapphire_mddi_power_client, .fixup = nt35399_fixup, + .vsync_irq = MSM_GPIO_TO_INT(VSYNC_GPIO), .fb_resource = resources_msm_fb, .num_clients = 2, .client_platform_data = { @@ -1212,7 +1215,7 @@ static struct msm_mddi_platform_data mddi_pdata = { { .product_id = (NT35399_MFR_NAME << 16 | NT35399_PRODUCT_CODE), - .name = "mddi_c_0bda_8a47" , + .name = "mddi_c_simple" , .id = 0, .client_data = &nt35399_client_data, .clk_rate = 0, @@ -1249,6 +1252,12 @@ int __init sapphire_init_panel(void) resources_msm_fb[0].end = SMI32_MSM_FB_BASE + SMI32_MSM_FB_SIZE - 1; } + rc = gpio_request(VSYNC_GPIO, "vsync"); + if (rc) + return rc; + rc = gpio_direction_input(VSYNC_GPIO); + if (rc) + return rc; rc = platform_device_register(&msm_device_mdp); if (rc) return rc; From b8f94dcc338668b9be2258e010e89151d947792b Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 28 Apr 2010 18:13:32 -0700 Subject: [PATCH 0635/2556] [ARM] msm: trout: specify panel vsync gpio irq Change-Id: Id823d6737757ebea66018dc46a4dc2d323ab011b Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-trout-panel.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/mach-msm/board-trout-panel.c b/arch/arm/mach-msm/board-trout-panel.c index f4bc83a85a229..7e978b88cb13c 100644 --- a/arch/arm/mach-msm/board-trout-panel.c +++ b/arch/arm/mach-msm/board-trout-panel.c @@ -23,6 +23,7 @@ #include "devices.h" #define TROUT_DEFAULT_BACKLIGHT_BRIGHTNESS 255 +#define VSYNC_GPIO 97 static struct clk *gp_clk; static int trout_backlight_off; @@ -573,6 +574,7 @@ struct msm_mddi_bridge_platform_data toshiba_client_data = { static struct msm_mddi_platform_data mddi_pdata = { .clk_rate = 122880000, .power_client = trout_mddi_power_client, + .vsync_irq = MSM_GPIO_TO_INT(VSYNC_GPIO), .fb_resource = resources_msm_fb, .num_clients = 1, .client_platform_data = { @@ -628,6 +630,12 @@ int __init trout_init_panel(void) "failed\n"); } + rc = gpio_request(VSYNC_GPIO, "vsync"); + if (rc) + return rc; + rc = gpio_direction_input(VSYNC_GPIO); + if (rc) + return rc; rc = platform_device_register(&msm_device_mdp); if (rc) return rc; From a7f8cad629b775ad5b6870b4a99fd7a7c71162b3 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 3 May 2010 17:07:49 -0700 Subject: [PATCH 0636/2556] [ARM] video: msm: Manage mdp/mddi pclk clocks, if present Change-Id: I12ed78f71936e1ac11926fabd7feb5023cbb36db Signed-off-by: Dima Zavin --- drivers/video/msm/mddi.c | 21 ++++++++++++++++++--- drivers/video/msm/mdp.c | 28 ++++++++++++++++++++++++---- drivers/video/msm/mdp_hw.h | 1 + 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c index df5f808a6c096..921a1c3bf1a35 100644 --- a/drivers/video/msm/mddi.c +++ b/drivers/video/msm/mddi.c @@ -67,6 +67,7 @@ struct mddi_info { char __iomem *base; int irq; struct clk *clk; + struct clk *pclk; struct msm_mddi_client_data client_data; /* buffer for rev encap packets */ @@ -449,6 +450,8 @@ static void mddi_suspend(struct msm_mddi_client_data *cdata) mddi_writel(MDDI_CMD_RESET, CMD); mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); /* turn off the clock */ + if (mddi->pclk) + clk_disable(mddi->pclk); clk_disable(mddi->clk); wake_unlock(&mddi->idle_lock); } @@ -464,6 +467,8 @@ static void mddi_resume(struct msm_mddi_client_data *cdata) mddi->power_client(&mddi->client_data, 1); /* turn on the clock */ clk_enable(mddi->clk); + if (mddi->pclk) + clk_enable(mddi->pclk); /* set up the local registers */ mddi->rev_data_curr = 0; mddi_init_registers(mddi); @@ -717,15 +722,25 @@ static int __init mddi_clk_setup(struct platform_device *pdev, printk(KERN_INFO "mddi: failed to get clock\n"); return PTR_ERR(mddi->clk); } - ret = clk_enable(mddi->clk); - if (ret) - goto fail; + mddi->pclk = clk_get(&pdev->dev, "mddi_pclk"); + if (IS_ERR(mddi->pclk)) + mddi->pclk = NULL; + + clk_enable(mddi->clk); + if (mddi->pclk) + clk_enable(mddi->pclk); + ret = clk_set_rate(mddi->clk, clk_rate); if (ret) goto fail; return 0; fail: + if (mddi->pclk) { + clk_disable(mddi->pclk); + clk_put(mddi->pclk); + } + clk_disable(mddi->clk); clk_put(mddi->clk); return ret; } diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index e533d266b51a1..43af448c6854c 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -38,7 +38,7 @@ struct class *mdp_class; #define MDP_CMD_DEBUG_ACCESS_BASE (0x10000) static unsigned int mdp_irq_mask; -struct clk *mdp_clk_to_disable_later = 0; +static struct mdp_info *the_mdp; static int locked_enable_mdp_irq(struct mdp_info *mdp, uint32_t mask) { @@ -54,6 +54,8 @@ static int locked_enable_mdp_irq(struct mdp_info *mdp, uint32_t mask) if (!mdp_irq_mask) { clk_set_rate(mdp->ebi1_clk, 128000000); clk_enable(mdp->clk); + if (mdp->pclk) + clk_enable(mdp->pclk); enable_irq(mdp->irq); } @@ -94,6 +96,8 @@ static int locked_disable_mdp_irq(struct mdp_info *mdp, uint32_t mask) /* if no one is waiting on the interrupt, disable it */ if (!mdp_irq_mask) { disable_irq_nosync(mdp->irq); + if (mdp->pclk) + clk_disable(mdp->pclk); if (mdp->clk) clk_disable(mdp->clk); clk_set_rate(mdp->ebi1_clk, 0); @@ -537,6 +541,10 @@ int mdp_probe(struct platform_device *pdev) goto error_get_mdp_clk; } + mdp->pclk = clk_get(&pdev->dev, "mdp_pclk"); + if (IS_ERR(mdp->pclk)) + mdp->pclk = NULL; + mdp->ebi1_clk = clk_get(NULL, "ebi1_clk"); if (IS_ERR(mdp->ebi1_clk)) { pr_err("mdp: failed to get ebi1 clk\n"); @@ -551,7 +559,8 @@ int mdp_probe(struct platform_device *pdev) disable_irq(mdp->irq); clk_enable(mdp->clk); - mdp_clk_to_disable_later = mdp->clk; + if (mdp->pclk) + clk_enable(mdp->pclk); mdp_hw_init(mdp); /* register mdp device */ @@ -567,15 +576,22 @@ int mdp_probe(struct platform_device *pdev) if (ret) goto error_device_register; + the_mdp = mdp; + pr_info("%s: initialized\n", __func__); return 0; error_device_register: + if (mdp->pclk) + clk_disable(mdp->pclk); + clk_disable(mdp->clk); free_irq(mdp->irq, mdp); error_request_irq: clk_put(mdp->ebi1_clk); error_get_ebi1_clk: + if (mdp->pclk) + clk_put(mdp->pclk); clk_put(mdp->clk); error_get_mdp_clk: error_mddi_pmdh_register: @@ -593,8 +609,12 @@ static struct platform_driver msm_mdp_driver = { static int __init mdp_lateinit(void) { - if (mdp_clk_to_disable_later) - clk_disable(mdp_clk_to_disable_later); + struct mdp_info *mdp = the_mdp; + if (the_mdp) { + if (mdp->pclk) + clk_disable(mdp->pclk); + clk_disable(mdp->clk); + } return 0; } diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h index 43a7da044f632..adbc215415371 100644 --- a/drivers/video/msm/mdp_hw.h +++ b/drivers/video/msm/mdp_hw.h @@ -47,6 +47,7 @@ struct mdp_info { char * __iomem base; int irq; struct clk *clk; + struct clk *pclk; struct clk *ebi1_clk; struct mdp_out_interface out_if[MSM_MDP_NUM_INTERFACES]; int dma_format; From e2c8f74d9ba3ca0a7992a7df7c7e816c1c2004cd Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 26 Apr 2010 17:25:17 -0700 Subject: [PATCH 0637/2556] [ARM] video: msm: Add basic MDP 4.0 support Forces the overlays into manual mode. This makes it compatible with previous versions of the MDP where the driver kickstarts the DMA. - Verified that the MDDI interface works correctly. Change-Id: Idb169b4bb647f5475b1cac13256e0d6030d0a112 Signed-off-by: Dima Zavin --- drivers/video/msm/Makefile | 2 + drivers/video/msm/mddi.c | 14 ++++++ drivers/video/msm/mddi_hw.h | 9 ++++ drivers/video/msm/mdp.c | 1 + drivers/video/msm/mdp_hw.h | 86 +++++++++++++++++++++++++++--------- drivers/video/msm/mdp_hw40.c | 47 ++++++++++++++++++++ 6 files changed, 139 insertions(+), 20 deletions(-) create mode 100644 drivers/video/msm/mdp_hw40.c diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile index 44e0038942fd1..52f5f651e8972 100644 --- a/drivers/video/msm/Makefile +++ b/drivers/video/msm/Makefile @@ -10,6 +10,8 @@ endif # obj-y += mdp.o +obj-$(CONFIG_MSM_MDP40) += mdp_hw40.o + obj-$(CONFIG_FB_MSM_LEGACY_MDP) += mdp_hw_legacy.o obj-$(CONFIG_FB_MSM_MDP_PPP) += mdp_ppp.o diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c index 921a1c3bf1a35..671722ca6e9f3 100644 --- a/drivers/video/msm/mddi.c +++ b/drivers/video/msm/mddi.c @@ -400,7 +400,10 @@ static uint16_t mddi_init_registers(struct mddi_info *mddi) mddi_writel(MDDI_HOST_TA2_LEN, TA2_LEN); mddi_writel(0x0096, DRIVE_HI); /* 0x32 normal, 0x50 for Toshiba display */ + + /* XXX: should we use 0x32? */ mddi_writel(0x0050, DRIVE_LO); + mddi_writel(0x003C, DISP_WAKE); /* wakeup counter */ mddi_writel(MDDI_HOST_REV_RATE_DIV, REV_RATE_DIV); @@ -418,9 +421,15 @@ static uint16_t mddi_init_registers(struct mddi_info *mddi) udelay(5); } + /* XXX: NEED SUPPORT FOR 1.2 */ + /* Recommendation from PAD hw team */ mddi_writel(0xa850f, PAD_CTL); +#if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP40) + mddi_writel(0x00320000, PAD_IO_CTL); + mddi_writel(0x00220020, PAD_CAL); +#endif /* Need an even number for counts */ mddi_writel(0x60006, DRIVER_START_CNT); @@ -840,6 +849,8 @@ static int __devinit mddi_probe(struct platform_device *pdev) goto error_mddi_version; } + printk(KERN_INFO "mddi: host core version: 0x%02x\n", mddi->version); + /* read the capabilities off the client */ if (!mddi_get_client_caps(mddi)) { printk(KERN_INFO "mddi: no client found\n"); @@ -852,6 +863,9 @@ static int __devinit mddi_probe(struct platform_device *pdev) } mddi_set_auto_hibernate(&mddi->client_data, 1); + pr_info("%s: got mfr %04x product %04x\n", __func__, + mddi->caps.Mfr_Name, mddi->caps.Product_Code); + if (mddi->caps.Mfr_Name == 0 && mddi->caps.Product_Code == 0) pdata->fixup(&mddi->caps.Mfr_Name, &mddi->caps.Product_Code); diff --git a/drivers/video/msm/mddi_hw.h b/drivers/video/msm/mddi_hw.h index 45cc01fc1e7fc..47bb4494c5f71 100644 --- a/drivers/video/msm/mddi_hw.h +++ b/drivers/video/msm/mddi_hw.h @@ -53,6 +53,9 @@ #define MDDI_MF_CNT 0x0084 #define MDDI_CURR_REV_PTR 0x0088 #define MDDI_CORE_VER 0x008c +#define MDDI_FIFO_ALLOC 0x0090 +#define MDDI_PAD_IO_CTL 0x00a0 +#define MDDI_PAD_CAL 0x00a4 #define MDDI_INT_PRI_PTR_READ 0x0001 #define MDDI_INT_SEC_PTR_READ 0x0002 @@ -125,8 +128,14 @@ /* MDP sends 256 pixel packets, so lower value hibernates more without * significantly increasing latency of waiting for next subframe */ #define MDDI_HOST_BYTES_PER_SUBFRAME 0x3C00 + +#if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP40) +#define MDDI_HOST_TA2_LEN 0x001a +#define MDDI_HOST_REV_RATE_DIV 0x0004 +#else #define MDDI_HOST_TA2_LEN 0x000c #define MDDI_HOST_REV_RATE_DIV 0x0002 +#endif struct __attribute__((packed)) mddi_rev_packet { diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 43af448c6854c..02bb422b503c3 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -289,6 +289,7 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM, MDP_MDDI_PARAM); + mdp_writel(mdp, 0x1, MDP_MDDI_DATA_XFR); mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG); mdp_writel(mdp, 0, MDP_DMA_P_START); #endif diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h index adbc215415371..ba29454e8b6a8 100644 --- a/drivers/video/msm/mdp_hw.h +++ b/drivers/video/msm/mdp_hw.h @@ -78,6 +78,7 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #define mdp_writel(mdp, value, offset) writel(value, mdp->base + offset) #define mdp_readl(mdp, offset) readl(mdp->base + offset) +#if defined(CONFIG_MSM_MDP22) #define MDP_SYNC_CONFIG_0 (0x00000) #define MDP_SYNC_CONFIG_1 (0x00004) #define MDP_SYNC_CONFIG_2 (0x00008) @@ -86,6 +87,18 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #define MDP_SYNC_STATUS_2 (0x00014) #define MDP_SYNC_THRESH_0 (0x00018) #define MDP_SYNC_THRESH_1 (0x0001c) +#endif + +#if defined(CONFIG_MSM_MDP40) +#define MDP_DISP_INTF_SEL (0x00038) +#define MDP_MAX_RD_PENDING_CMD_CONFIG (0x0004c) +#define MDP_INTR_ENABLE (0x00050) +#define MDP_INTR_STATUS (0x00054) +#define MDP_INTR_CLEAR (0x00058) +#define MDP_EBI2_LCD0 (0x00060) +#define MDP_EBI2_LCD1 (0x00064) +#define MDP_EBI2_PORTMAP_MODE (0x00070) +#else #define MDP_INTR_ENABLE (0x00020) #define MDP_INTR_STATUS (0x00024) #define MDP_INTR_CLEAR (0x00028) @@ -95,17 +108,19 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #define MDP_EBI2_LCD0 (0x0003c) #define MDP_EBI2_LCD1 (0x00040) #define MDP_EBI2_PORTMAP_MODE (0x0005c) +#endif -#ifndef CONFIG_MSM_MDP31 +#if defined(CONFIG_MSM_MDP22) #define MDP_DISPLAY0_ADDR (0x00054) #define MDP_DISPLAY1_ADDR (0x00058) #define MDP_PPP_CMD_MODE (0x00060) -#else +#elif defined(CONFIG_MSM_MDP31) #define MDP_DISPLAY0_ADDR (0x10000) #define MDP_DISPLAY1_ADDR (0x10004) #define MDP_PPP_CMD_MODE (0x10060) #endif +#if defined(CONFIG_MSM_MDP22) #define MDP_TV_OUT_STATUS (0x00064) #define MDP_HW_VERSION (0x00070) #define MDP_SW_RESET (0x00074) @@ -115,8 +130,20 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #define MDP_SECONDARY_VSYNC_OUT_CTRL (0x00084) #define MDP_EXTERNAL_VSYNC_OUT_CTRL (0x00088) #define MDP_VSYNC_CTRL (0x0008c) +#endif + +#if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP40) #define MDP_MDDI_PARAM_WR_SEL (0x00090) #define MDP_MDDI_PARAM (0x00094) +#define MDP_MDDI_DATA_XFR (0x00098) +#endif + +#if defined(CONFIG_MSM_MDP40) +#define MDP_LAYERMIXER_IN_CFG (0x10100) +#define MDP_OVERLAYPROC0_CFG (0x10004) +#define MDP_OVERLAYPROC1_CFG (0x18004) +#endif + #define MDP_CGC_EN (0x00100) #define MDP_CMD_STATUS (0x10008) #define MDP_PROFILE_EN (0x10010) @@ -232,29 +259,46 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #define MDP_TEST_CAPTURED_DCLK (0xd0210) #define MDP_TEST_MISR_CAPT_VAL_DCLK (0xd0214) -#define MDP_DMA_P_START (0x00044) #define MDP_DMA_P_CONFIG (0x90000) #define MDP_DMA_P_SIZE (0x90004) #define MDP_DMA_P_IBUF_ADDR (0x90008) #define MDP_DMA_P_IBUF_Y_STRIDE (0x9000c) #define MDP_DMA_P_OUT_XY (0x90010) + +#ifdef CONFIG_MSM_MDP40 +#define MDP_DMA_P_START (0x0000c) +#define MDP_DMA_P_FETCH_CFG (0x91004) +#define MDP_DMA_P_HIST_INTR_STATUS (0x95014) +#define MDP_DMA_P_HIST_INTR_CLEAR (0x95018) +#define MDP_DMA_P_HIST_INTR_ENABLE (0x9501C) +#else +#define MDP_DMA_P_START (0x00044) #define MDP_DMA_P_COLOR_CORRECT_CONFIG (0x90070) +#endif -#define MDP_LCDC_EN (0xe0000) -#define MDP_LCDC_HSYNC_CTL (0xe0004) -#define MDP_LCDC_VSYNC_PERIOD (0xe0008) -#define MDP_LCDC_VSYNC_PULSE_WIDTH (0xe000c) -#define MDP_LCDC_DISPLAY_HCTL (0xe0010) -#define MDP_LCDC_DISPLAY_V_START (0xe0014) -#define MDP_LCDC_DISPLAY_V_END (0xe0018) -#define MDP_LCDC_ACTIVE_HCTL (0xe001c) -#define MDP_LCDC_ACTIVE_V_START (0xe0020) -#define MDP_LCDC_ACTIVE_V_END (0xe0024) -#define MDP_LCDC_BORDER_CLR (0xe0028) -#define MDP_LCDC_UNDERFLOW_CTL (0xe002c) -#define MDP_LCDC_HSYNC_SKEW (0xe0030) -#define MDP_LCDC_TEST_CTL (0xe0034) -#define MDP_LCDC_CTL_POLARITY (0xe0038) +#if defined(CONFIG_MSM_MDP31) +#define MDP_LCDC_BASE (0xe0000) +#elif defined(CONFIG_MSM_MDP40) +#define MDP_LCDC_BASE (0xc0000) +#endif + +#ifdef MDP_LCDC_BASE +#define MDP_LCDC_EN (MDP_LCDC_BASE + 0x00) +#define MDP_LCDC_HSYNC_CTL (MDP_LCDC_BASE + 0x04) +#define MDP_LCDC_VSYNC_PERIOD (MDP_LCDC_BASE + 0x08) +#define MDP_LCDC_VSYNC_PULSE_WIDTH (MDP_LCDC_BASE + 0x0c) +#define MDP_LCDC_DISPLAY_HCTL (MDP_LCDC_BASE + 0x10) +#define MDP_LCDC_DISPLAY_V_START (MDP_LCDC_BASE + 0x14) +#define MDP_LCDC_DISPLAY_V_END (MDP_LCDC_BASE + 0x18) +#define MDP_LCDC_ACTIVE_HCTL (MDP_LCDC_BASE + 0x1c) +#define MDP_LCDC_ACTIVE_V_START (MDP_LCDC_BASE + 0x20) +#define MDP_LCDC_ACTIVE_V_END (MDP_LCDC_BASE + 0x24) +#define MDP_LCDC_BORDER_CLR (MDP_LCDC_BASE + 0x28) +#define MDP_LCDC_UNDERFLOW_CTL (MDP_LCDC_BASE + 0x2c) +#define MDP_LCDC_HSYNC_SKEW (MDP_LCDC_BASE + 0x30) +#define MDP_LCDC_TEST_CTL (MDP_LCDC_BASE + 0x34) +#define MDP_LCDC_CTL_POLARITY (MDP_LCDC_BASE + 0x38) +#endif #define MDP_PPP_SCALE_STATUS (0x50000) #define MDP_PPP_BLEND_STATUS (0x70000) @@ -267,9 +311,11 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #define TV_OUT_DMA3_DONE (1<<6) #define TV_ENC_UNDERRUN (1<<7) -#ifdef CONFIG_MSM_MDP22 +#if defined(CONFIG_MSM_MDP40) +#define MDP_DMA_P_DONE (1 << 4) +#elif defined(CONFIG_MSM_MDP22) #define MDP_DMA_P_DONE (1 << 2) -#else /* CONFIG_MSM_MDP31 */ +#elif defined(CONFIG_MSM_MDP31) #define MDP_DMA_P_DONE (1 << 14) #define MDP_LCDC_UNDERFLOW (1 << 16) #define MDP_LCDC_FRAME_START (1 << 15) diff --git a/drivers/video/msm/mdp_hw40.c b/drivers/video/msm/mdp_hw40.c new file mode 100644 index 0000000000000..8e28f945f5f68 --- /dev/null +++ b/drivers/video/msm/mdp_hw40.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2010 Google, Inc. + * Author: Dima Zavin + * + * Based on code from Code Aurora Forum. + * + * 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 "mdp_hw.h" + +int mdp_hw_init(struct mdp_info *mdp) +{ + mdp_writel(mdp, 0, MDP_INTR_ENABLE); + mdp_writel(mdp, 0, MDP_DMA_P_HIST_INTR_ENABLE); + + /* TODO: Configure the VG/RGB pipes fetch data */ + + /* this should work for any mdp_clk freq. + * TODO: use different value for mdp_clk freqs >= 90Mhz */ + mdp_writel(mdp, 0x27, MDP_DMA_P_FETCH_CFG); /* 8 bytes-burst x 8 req */ + + mdp_writel(mdp, 0x3, MDP_EBI2_PORTMAP_MODE); + + /* 3 pending requests */ + mdp_writel(mdp, 0x02222, MDP_MAX_RD_PENDING_CMD_CONFIG); + + /* no overlay processing, sw controls everything */ + mdp_writel(mdp, 0, MDP_LAYERMIXER_IN_CFG); + mdp_writel(mdp, 1 << 3, MDP_OVERLAYPROC0_CFG); + mdp_writel(mdp, 1 << 3, MDP_OVERLAYPROC1_CFG); + + /* XXX: HACK! hardcode to do mddi on primary */ + mdp_writel(mdp, 0x2, MDP_DISP_INTF_SEL); + return 0; +} + From 767b35392a63699ca50e46a47fbd1f43c5154da5 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 3 May 2010 16:47:57 -0700 Subject: [PATCH 0638/2556] [ARM] video: msm: set mdp_clk rate to 122.88Mhz for MDP 4.0 Change-Id: I1f6928c137115695701ad512f07bdcff5e2170bb Signed-off-by: Dima Zavin --- drivers/video/msm/mdp_hw40.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/video/msm/mdp_hw40.c b/drivers/video/msm/mdp_hw40.c index 8e28f945f5f68..035160638347b 100644 --- a/drivers/video/msm/mdp_hw40.c +++ b/drivers/video/msm/mdp_hw40.c @@ -15,6 +15,7 @@ * */ +#include #include #include "mdp_hw.h" @@ -24,6 +25,11 @@ int mdp_hw_init(struct mdp_info *mdp) mdp_writel(mdp, 0, MDP_INTR_ENABLE); mdp_writel(mdp, 0, MDP_DMA_P_HIST_INTR_ENABLE); + /* XXX: why set this? QCT says it should be > mdp_pclk, + * but they never set the clkrate of pclk */ + clk_set_rate(mdp->clk, 122880000); /* 122.88 Mhz */ + pr_info("%s: mdp_clk=%lu\n", __func__, clk_get_rate(mdp->clk)); + /* TODO: Configure the VG/RGB pipes fetch data */ /* this should work for any mdp_clk freq. From e1a5355114dce575c133c1df545d93fcae94433d Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 7 May 2010 15:33:13 -0700 Subject: [PATCH 0639/2556] [ARM] msm: Do not assume reset vector is at 0x0 Change-Id: I42ef3472b9a6f616decd13ab2d094c926c900384 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/include/mach/memory.h | 4 ++++ arch/arm/mach-msm/pm.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h index 9ddbdd7930335..fb31ab5309a68 100644 --- a/arch/arm/mach-msm/include/mach/memory.h +++ b/arch/arm/mach-msm/include/mach/memory.h @@ -21,12 +21,16 @@ #define PHYS_OFFSET UL(0x00000000) #elif defined(CONFIG_ARCH_QSD8X50) #define PHYS_OFFSET UL(0x20000000) +#define RESET_VECTOR UL(0x00000000) #elif defined(CONFIG_ARCH_MSM7X30) #define PHYS_OFFSET UL(0x00200000) +#define RESET_VECTOR UL(0x00100000) #elif defined(CONFIG_ARCH_MSM8X60) #define PHYS_OFFSET UL(0x40200000) +#define RESET_VECTOR UL(0x00000000) #else #define PHYS_OFFSET UL(0x10000000) +#define RESET_VECTOR UL(0x00000000) #endif #define HAS_ARCH_IO_REMAP_PFN_RANGE diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index a888d91bc1192..6f2bfcdee7ab5 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -776,7 +776,7 @@ static int __init msm_pm_init(void) register_reboot_notifier(&msm_reboot_notifier); - msm_pm_reset_vector = ioremap(0, PAGE_SIZE); + msm_pm_reset_vector = ioremap(RESET_VECTOR, PAGE_SIZE); if (msm_pm_reset_vector == NULL) { printk(KERN_ERR "msm_pm_init: failed to map reset vector\n"); return -ENODEV; From 4b84c6d21c794588e5a63cbed002b723b47bb393 Mon Sep 17 00:00:00 2001 From: Qualcomm Innovation Center Date: Sat, 2 May 2009 12:49:33 -0700 Subject: [PATCH 0640/2556] spi_qsd: Import Qualcomm spi driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inludes changes up to: commit 7f91e51ccbe2c05cdef5b569a55b184e25393767 Author: Lena Salman Date: Tue Jan 12 20:59:42 2010 +0200 spi_qsd: Changing max DMA chunk to 4032 bytes Increasing DMA chunks to be sent in case of large transactions to 4032 bytes instead of 3K. Signed-off-by: Lena Salman Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/include/mach/msm_spi.h | 37 + drivers/spi/Kconfig | 5 + drivers/spi/Makefile | 1 + drivers/spi/spi_qsd.c | 1606 ++++++++++++++++++++++ 4 files changed, 1649 insertions(+) create mode 100644 arch/arm/mach-msm/include/mach/msm_spi.h create mode 100644 drivers/spi/spi_qsd.c diff --git a/arch/arm/mach-msm/include/mach/msm_spi.h b/arch/arm/mach-msm/include/mach/msm_spi.h new file mode 100644 index 0000000000000..433b2bdd57c16 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_spi.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * SPI driver for Qualcomm QSD platforms. + */ + +struct msm_spi_platform_data { + u32 max_clock_speed; + int (*gpio_config)(void); + void (*gpio_release)(void); + int (*dma_config)(void); +}; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index bb233a9cbad2f..5451dcd53066a 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -288,6 +288,11 @@ config SPI_PXA2XX config SPI_PXA2XX_PCI def_bool SPI_PXA2XX && X86_32 && PCI +config SPI_QSD + tristate "Qualcomm MSM SPI support" + default n + depends on ARCH_MSM_SCORPION + config SPI_S3C24XX tristate "Samsung S3C24XX series SPI" depends on ARCH_S3C2410 && EXPERIMENTAL diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 86d1b5f9bbd9a..881d84be4267e 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_SPI_FSL_LIB) += spi_fsl_lib.o obj-$(CONFIG_SPI_FSL_ESPI) += spi_fsl_espi.o obj-$(CONFIG_SPI_FSL_SPI) += spi_fsl_spi.o obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o +obj-$(CONFIG_SPI_QSD) += spi_qsd.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o obj-$(CONFIG_SPI_S3C64XX) += spi_s3c64xx.o diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c new file mode 100644 index 0000000000000..848ed2e4cefdb --- /dev/null +++ b/drivers/spi/spi_qsd.c @@ -0,0 +1,1606 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * SPI driver for Qualcomm QSD platforms + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPI_CONFIG 0x0000 +#define SPI_IO_CONTROL 0x0004 +#define SPI_IO_MODES 0x0008 +#define SPI_SW_RESET 0x000C +#define SPI_TIME_OUT 0x0010 +#define SPI_TIME_OUT_CURRENT 0x0014 +#define SPI_MX_OUTPUT_COUNT 0x0018 +#define SPI_MX_OUTPUT_CNT_CURRENT 0x001C +#define SPI_MX_INPUT_COUNT 0x0020 +#define SPI_MX_INPUT_CNT_CURRENT 0x0024 +#define SPI_MX_READ_COUNT 0x0028 +#define SPI_MX_READ_CNT_CURRENT 0x002C +#define SPI_OPERATIONAL 0x0030 +#define SPI_ERROR_FLAGS 0x0034 +#define SPI_ERROR_FLAGS_EN 0x0038 +#define SPI_DEASSERT_WAIT 0x003C +#define SPI_OUTPUT_DEBUG 0x0040 +#define SPI_INPUT_DEBUG 0x0044 +#define SPI_FIFO_WORD_CNT 0x0048 +#define SPI_TEST_CTRL 0x004C +#define SPI_OUTPUT_FIFO 0x0100 +#define SPI_INPUT_FIFO 0x0200 + +/* SPI_CONFIG fields */ +#define SPI_CFG_INPUT_FIRST 0x00000200 +#define SPI_NO_INPUT 0x00000080 +#define SPI_NO_OUTPUT 0x00000040 +#define SPI_CFG_LOOPBACK 0x00000100 +#define SPI_CFG_N 0x0000001F + +/* SPI_IO_CONTROL fields */ +#define SPI_IO_C_CLK_IDLE_HIGH 0x00000400 +#define SPI_IO_C_MX_CS_MODE 0x00000100 +#define SPI_IO_C_CS_N_POLARITY 0x000000F0 +#define SPI_IO_C_CS_N_POLARITY_0 0x00000010 +#define SPI_IO_C_CS_SELECT 0x0000000C +#define SPI_IO_C_TRISTATE_CS 0x00000002 +#define SPI_IO_C_NO_TRI_STATE 0x00000001 + +/* SPI_IO_MODES fields */ +#define SPI_IO_M_OUTPUT_BIT_SHIFT_EN 0x00004000 +#define SPI_IO_M_PACK_EN 0x00002000 +#define SPI_IO_M_UNPACK_EN 0x00001000 +#define SPI_IO_M_INPUT_MODE 0x00000C00 +#define SPI_IO_M_OUTPUT_MODE 0x00000300 +#define SPI_IO_M_INPUT_FIFO_SIZE 0x000000C0 +#define SPI_IO_M_INPUT_BLOCK_SIZE 0x00000030 +#define SPI_IO_M_OUTPUT_FIFO_SIZE 0x0000000C +#define SPI_IO_M_OUTPUT_BLOCK_SIZE 0x00000003 + +/* SPI_OPERATIONAL fields */ +#define SPI_OP_MAX_INPUT_DONE_FLAG 0x00000800 +#define SPI_OP_MAX_OUTPUT_DONE_FLAG 0x00000400 +#define SPI_OP_INPUT_SERVICE_FLAG 0x00000200 +#define SPI_OP_OUTPUT_SERVICE_FLAG 0x00000100 +#define SPI_OP_INPUT_FIFO_FULL 0x00000080 +#define SPI_OP_OUTPUT_FIFO_FULL 0x00000040 +#define SPI_OP_IP_FIFO_NOT_EMPTY 0x00000020 +#define SPI_OP_OP_FIFO_NOT_EMPTY 0x00000010 +#define SPI_OP_STATE_VALID 0x00000004 +#define SPI_OP_STATE 0x00000003 +#define SPI_OP_STATE_RESET 0x00000000 +#define SPI_OP_STATE_RUN 0x00000001 +#define SPI_OP_STATE_PAUSE 0x00000003 + +/* SPI_ERROR_FLAGS fields */ +#define SPI_ERR_TIME_OUT_ERR 0x00000040 +#define SPI_ERR_OUTPUT_OVER_RUN_ERR 0x00000020 +#define SPI_ERR_INPUT_UNDER_RUN_ERR 0x00000010 +#define SPI_ERR_OUTPUT_UNDER_RUN_ERR 0x00000008 +#define SPI_ERR_INPUT_OVER_RUN_ERR 0x00000004 +#define SPI_ERR_CLK_OVER_RUN_ERR 0x00000002 +#define SPI_ERR_CLK_UNDER_RUN_ERR 0x00000001 +#define SPI_ERR_MASK 0x0000007F + +/* We don't allow transactions larger than 4K-64 */ +#define SPI_MAX_TRANSFERS 0x000FC0 +#define SPI_MAX_LEN (SPI_MAX_TRANSFERS * dd->bytes_per_word) +#define SPI_MAX_TIMEOUT 0x00010000 +#define SPI_MIN_TRANS_TIME 50 + +#define SPI_NUM_CHIPSELECTS 4 +#define SPI_QSD_NAME "spi_qsd" + +/* Data Mover burst size */ +#define DM_BURST_SIZE 16 +/* Data Mover commands should be aligned to 64 bit(8 bytes) */ +#define DM_BYTE_ALIGN 8 + +enum msm_spi_mode { + SPI_FIFO_MODE = 0x0, /* 00 */ + SPI_BLOCK_MODE = 0x1, /* 01 */ + SPI_DMOV_MODE = 0x2, /* 10 */ + SPI_MODE_NONE = 0xFF, /* invalid value */ +}; + +/* Structures for Data Mover */ +struct spi_dmov_cmd { + dmov_box box; /* data aligned to max(dm_burst_size, block_size) + (<= fifo_size) */ + dmov_s single_pad; /* data unaligned to max(dm_burst_size, block_size) + padded to fit */ + dma_addr_t cmd_ptr; +}; + +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.2"); +MODULE_ALIAS("platform:spi_qsd"); + +#ifdef CONFIG_DEBUG_FS +/* Used to create debugfs entries */ +static const struct { + const char *name; + mode_t mode; + int offset; +} debugfs_spi_regs[] = { + {"config", S_IRUGO | S_IWUSR, SPI_CONFIG}, + {"io_control", S_IRUGO | S_IWUSR, SPI_IO_CONTROL}, + {"io_modes", S_IRUGO | S_IWUSR, SPI_IO_MODES}, + {"sw_reset", S_IWUSR, SPI_SW_RESET}, + {"time_out", S_IRUGO | S_IWUSR, SPI_TIME_OUT}, + {"time_out_current", S_IRUGO, SPI_TIME_OUT_CURRENT}, + {"mx_output_count", S_IRUGO | S_IWUSR, SPI_MX_OUTPUT_COUNT}, + {"mx_output_cnt_current", S_IRUGO, SPI_MX_OUTPUT_CNT_CURRENT}, + {"mx_input_count", S_IRUGO | S_IWUSR, SPI_MX_INPUT_COUNT}, + {"mx_input_cnt_current", S_IRUGO, SPI_MX_INPUT_CNT_CURRENT}, + {"mx_read_count", S_IRUGO | S_IWUSR, SPI_MX_READ_COUNT}, + {"mx_read_cnt_current", S_IRUGO, SPI_MX_READ_CNT_CURRENT}, + {"operational", S_IRUGO | S_IWUSR, SPI_OPERATIONAL}, + {"error_flags", S_IRUGO | S_IWUSR, SPI_ERROR_FLAGS}, + {"error_flags_en", S_IRUGO | S_IWUSR, SPI_ERROR_FLAGS_EN}, + {"deassert_wait", S_IRUGO | S_IWUSR, SPI_DEASSERT_WAIT}, + {"output_debug", S_IRUGO, SPI_OUTPUT_DEBUG}, + {"input_debug", S_IRUGO, SPI_INPUT_DEBUG}, + {"fifo_word_cnt", S_IRUGO, SPI_FIFO_WORD_CNT}, + {"test_ctrl", S_IRUGO | S_IWUSR, SPI_TEST_CTRL}, + {"output_fifo", S_IWUSR, SPI_OUTPUT_FIFO}, + {"input_fifo" , S_IRUSR, SPI_INPUT_FIFO}, +}; +#endif + +struct msm_spi { + u8 *read_buf; + const u8 *write_buf; + void __iomem *base; + struct device *dev; + spinlock_t queue_lock; + struct list_head queue; + struct workqueue_struct *workqueue; + struct work_struct work_data; + struct spi_message *cur_msg; + struct spi_transfer *cur_transfer; + struct completion transfer_complete; + struct clk *clk; + struct clk *pclk; + int max_clock_speed; + unsigned long mem_phys_addr; + size_t mem_size; + u32 rx_bytes_remaining; + u32 tx_bytes_remaining; + u32 clock_speed; + u32 irq_in; + u32 irq_out; + u32 irq_err; + int bytes_per_word; + bool suspended; + bool transfer_in_progress; + /* DMA data */ + enum msm_spi_mode mode; + bool use_dma; + int tx_dma_chan; + int tx_dma_crci; + int rx_dma_chan; + int rx_dma_crci; + /* Data Mover Commands */ + struct spi_dmov_cmd *tx_dmov_cmd; + struct spi_dmov_cmd *rx_dmov_cmd; + /* Physical address of the tx dmov box command */ + dma_addr_t tx_dmov_cmd_dma; + dma_addr_t rx_dmov_cmd_dma; + struct msm_dmov_cmd tx_hdr; + struct msm_dmov_cmd rx_hdr; + int block_size; + int burst_size; + atomic_t rx_irq_called; + /* Used to pad messages unaligned to block size */ + u8 *tx_padding; + dma_addr_t tx_padding_dma; + u8 *rx_padding; + dma_addr_t rx_padding_dma; + u32 unaligned_len; + /* DMA statistics */ + u32 stat_dmov_err; + u32 stat_rx; + u32 stat_tx; +#ifdef CONFIG_DEBUG_FS + struct dentry *dent_spi; + struct dentry *debugfs_spi_regs[ARRAY_SIZE(debugfs_spi_regs)]; +#endif +}; + +static int input_fifo_size; + +static void msm_spi_clock_set(struct msm_spi *dd, int speed) +{ + int rc; + + rc = clk_set_rate(dd->clk, speed); + if (!rc) + dd->clock_speed = speed; +} + +/* Assumption: input_fifo=output_fifo */ +static void __init msm_spi_calculate_fifo_size(struct msm_spi *dd) +{ + u32 spi_iom; + int block; + int mult; + int words; + + spi_iom = readl(dd->base + SPI_IO_MODES); + block = (spi_iom & SPI_IO_M_INPUT_BLOCK_SIZE) >> 4; + mult = (spi_iom & SPI_IO_M_INPUT_FIFO_SIZE) >> 6; + switch (block) { + case 0: + words = 1; + break; + case 1: + words = 4; + break; + case 2: + words = 8; + break; + default: + goto fifo_size_err; + } + switch (mult) { + case 0: + input_fifo_size = words * 2; + break; + case 1: + input_fifo_size = words * 4; + break; + case 2: + input_fifo_size = words * 8; + break; + default: + goto fifo_size_err; + } + + /* we can't use dma with 8 bytes of fifo since dm burst size is 16 */ + if (input_fifo_size*sizeof(u32) < DM_BURST_SIZE) + dd->use_dma = 0; + if (dd->use_dma) { + dd->block_size = words*sizeof(u32); /* in bytes */ + dd->burst_size = max(dd->block_size, DM_BURST_SIZE); + } + + return; + +fifo_size_err: + printk(KERN_WARNING "%s: invalid FIFO size, SPI_IO_MODES=0x%x\n", + __func__, spi_iom); + return; +} + +static void msm_spi_read_word_from_fifo(struct msm_spi *dd) +{ + u32 data_in; + int i; + int shift; + + data_in = readl(dd->base + SPI_INPUT_FIFO); + if (dd->read_buf) { + for (i = 0; (i < dd->bytes_per_word) && + dd->rx_bytes_remaining; i++) { + /* The data format depends on bytes_per_word: + 4 bytes: 0x12345678 + 3 bytes: 0x00123456 + 2 bytes: 0x00001234 + 1 byte : 0x00000012 + */ + shift = 8 * (dd->bytes_per_word - i - 1); + *dd->read_buf++ = (data_in & (0xFF << shift)) >> shift; + dd->rx_bytes_remaining--; + } + } else { + if (dd->rx_bytes_remaining >= dd->bytes_per_word) + dd->rx_bytes_remaining -= dd->bytes_per_word; + else + dd->rx_bytes_remaining = 0; + } +} + +static void msm_spi_setup_dm_transfer(struct msm_spi *dd) +{ + dmov_box *box; + int bytes_to_send, num_rows, bytes_sent; + u32 num_transfers, timeout; + + atomic_set(&dd->rx_irq_called, 0); + bytes_sent = dd->cur_transfer->len - dd->tx_bytes_remaining; + /* We'll send in chunks of SPI_MAX_LEN if larger */ + bytes_to_send = dd->tx_bytes_remaining / SPI_MAX_LEN ? + SPI_MAX_LEN : dd->tx_bytes_remaining; + num_transfers = DIV_ROUND_UP(bytes_to_send, dd->bytes_per_word); + dd->unaligned_len = bytes_to_send % dd->burst_size; + /* We multiply by 8bitsinbyte and multiply by 1.5 for safety */ + timeout = bytes_to_send * 12 > SPI_MAX_TIMEOUT ? + 0 : roundup(bytes_to_send * 12, SPI_MIN_TRANS_TIME); + num_rows = bytes_to_send / dd->burst_size; + + dd->mode = SPI_DMOV_MODE; + + if (num_rows) { + /* src in 16 MSB, dst in 16 LSB */ + box = &dd->tx_dmov_cmd->box; + box->src_row_addr = dd->cur_transfer->tx_dma + bytes_sent; + box->src_dst_len = (dd->burst_size << 16) | dd->burst_size; + box->num_rows = (num_rows << 16) | num_rows; + box->row_offset = (dd->burst_size << 16) | 0; + + box = &dd->rx_dmov_cmd->box; + box->dst_row_addr = dd->cur_transfer->rx_dma + bytes_sent; + box->src_dst_len = (dd->burst_size << 16) | dd->burst_size; + box->num_rows = (num_rows << 16) | num_rows; + box->row_offset = (0 << 16) | dd->burst_size; + + dd->tx_dmov_cmd->cmd_ptr = CMD_PTR_LP | + DMOV_CMD_ADDR(dd->tx_dmov_cmd_dma + + offsetof(struct spi_dmov_cmd, box)); + dd->rx_dmov_cmd->cmd_ptr = CMD_PTR_LP | + DMOV_CMD_ADDR(dd->rx_dmov_cmd_dma + + offsetof(struct spi_dmov_cmd, box)); + } else { + dd->tx_dmov_cmd->cmd_ptr = CMD_PTR_LP | + DMOV_CMD_ADDR(dd->tx_dmov_cmd_dma + + offsetof(struct spi_dmov_cmd, single_pad)); + dd->rx_dmov_cmd->cmd_ptr = CMD_PTR_LP | + DMOV_CMD_ADDR(dd->rx_dmov_cmd_dma + + offsetof(struct spi_dmov_cmd, single_pad)); + } + + if (!dd->unaligned_len) { + dd->tx_dmov_cmd->box.cmd |= CMD_LC; + dd->rx_dmov_cmd->box.cmd |= CMD_LC; + } else { + dmov_s *tx_cmd = &(dd->tx_dmov_cmd->single_pad); + dmov_s *rx_cmd = &(dd->rx_dmov_cmd->single_pad); + u32 offset = dd->cur_transfer->len - dd->unaligned_len; + + dd->tx_dmov_cmd->box.cmd &= ~CMD_LC; + dd->rx_dmov_cmd->box.cmd &= ~CMD_LC; + + memset(dd->tx_padding, 0, dd->burst_size); + memset(dd->rx_padding, 0, dd->burst_size); + if (dd->write_buf) + memcpy(dd->tx_padding, dd->write_buf + offset, + dd->unaligned_len); + + tx_cmd->src = dd->tx_padding_dma; + rx_cmd->dst = dd->rx_padding_dma; + tx_cmd->len = rx_cmd->len = dd->burst_size; + } + /* This also takes care of the padding dummy buf + Since this is set to the correct length, the + dummy bytes won't be actually sent */ + if (dd->write_buf) + writel(num_transfers, dd->base + SPI_MX_OUTPUT_COUNT); + if (dd->read_buf) + writel(num_transfers, dd->base + SPI_MX_INPUT_COUNT); + /* Write timeout */ + writel(timeout, dd->base + SPI_TIME_OUT); +} + +static void msm_spi_enqueue_dm_commands(struct msm_spi *dd) +{ + dma_coherent_pre_ops(); + if (dd->write_buf) + msm_dmov_enqueue_cmd(dd->tx_dma_chan, &dd->tx_hdr); + if (dd->read_buf) + msm_dmov_enqueue_cmd(dd->rx_dma_chan, &dd->rx_hdr); +} + +/* SPI core can send maximum of 4K transfers, because there is HW problem + with infinite mode. + Therefore, we are sending several chunks of 3K or less (depending on how + much is left). + Upon completion we send the next chunk, or complete the transfer if + everything is finished. +*/ +static int msm_spi_dm_send_next(struct msm_spi *dd) +{ + /* By now we should have sent all the bytes in FIFO mode, + * However to make things right, we'll check anyway. + */ + if (dd->mode != SPI_DMOV_MODE) + return 0; + + /* We need to send more chunks, if we sent max last time */ + if (dd->tx_bytes_remaining > SPI_MAX_LEN) { + dd->tx_bytes_remaining -= SPI_MAX_LEN; + writel((readl(dd->base + SPI_OPERATIONAL) + & ~SPI_OP_STATE) | SPI_OP_STATE_PAUSE, + dd->base + SPI_OPERATIONAL); + msm_spi_setup_dm_transfer(dd); + msm_spi_enqueue_dm_commands(dd); + + writel((readl(dd->base + SPI_OPERATIONAL) + & ~SPI_OP_STATE) | SPI_OP_STATE_RUN, + dd->base + SPI_OPERATIONAL); + return 1; + } + + return 0; +} + +static inline void msm_spi_ack_transfer(struct msm_spi *dd) +{ + writel(SPI_OP_MAX_INPUT_DONE_FLAG | SPI_OP_MAX_OUTPUT_DONE_FLAG, + dd->base + SPI_OPERATIONAL); + writel(0, dd->base + SPI_TIME_OUT); +} + +static irqreturn_t msm_spi_input_irq(int irq, void *dev_id) +{ + struct msm_spi *dd = dev_id; + + dd->stat_rx++; + + if (dd->mode == SPI_MODE_NONE) + return IRQ_HANDLED; + + if (dd->mode == SPI_DMOV_MODE) { + u32 op = readl(dd->base + SPI_OPERATIONAL); + if ((!dd->read_buf || op & SPI_OP_MAX_INPUT_DONE_FLAG) && + (!dd->write_buf || op & SPI_OP_MAX_OUTPUT_DONE_FLAG)) { + msm_spi_ack_transfer(dd); + if (dd->unaligned_len == 0) { + if (atomic_inc_return(&dd->rx_irq_called) == 1) + return IRQ_HANDLED; + } + complete(&dd->transfer_complete); + return IRQ_HANDLED; + } + return IRQ_NONE; + } + + /* fifo mode */ + while ((readl(dd->base + SPI_OPERATIONAL) & SPI_OP_IP_FIFO_NOT_EMPTY) && + (dd->rx_bytes_remaining > 0)) { + msm_spi_read_word_from_fifo(dd); + } + if (dd->rx_bytes_remaining == 0) + complete(&dd->transfer_complete); + + return IRQ_HANDLED; +} + +static void msm_spi_write_word_to_fifo(struct msm_spi *dd) +{ + u32 word; + u8 byte; + int i; + + word = 0; + if (dd->write_buf) { + for (i = 0; (i < dd->bytes_per_word) && + dd->tx_bytes_remaining; i++) { + dd->tx_bytes_remaining--; + byte = *dd->write_buf++; + word |= (byte << (BITS_PER_BYTE * (3 - i))); + } + } else + if (dd->tx_bytes_remaining > dd->bytes_per_word) + dd->tx_bytes_remaining -= dd->bytes_per_word; + else + dd->tx_bytes_remaining = 0; + writel(word, dd->base + SPI_OUTPUT_FIFO); +} + +static irqreturn_t msm_spi_output_irq(int irq, void *dev_id) +{ + struct msm_spi *dd = dev_id; + int count = 0; + + dd->stat_tx++; + + if (dd->mode == SPI_MODE_NONE) + return IRQ_HANDLED; + + if (dd->mode == SPI_DMOV_MODE) { + /* TX_ONLY transaction is handled here + This is the only place we send complete at tx and not rx */ + if (dd->read_buf == NULL && readl(dd->base + SPI_OPERATIONAL) & + SPI_OP_MAX_OUTPUT_DONE_FLAG) { + msm_spi_ack_transfer(dd); + complete(&dd->transfer_complete); + return IRQ_HANDLED; + } + return IRQ_NONE; + } + + /* Output FIFO is empty. Transmit any outstanding write data. */ + /* There could be one word in input FIFO, so don't send more */ + /* than input_fifo_size - 1 more words. */ + while ((dd->tx_bytes_remaining > 0) && + (count < input_fifo_size - 1) && + !(readl(dd->base + SPI_OPERATIONAL) & SPI_OP_OUTPUT_FIFO_FULL)) { + msm_spi_write_word_to_fifo(dd); + count++; + } + + return IRQ_HANDLED; +} + +static irqreturn_t msm_spi_error_irq(int irq, void *dev_id) +{ + struct spi_master *master = dev_id; + struct msm_spi *dd = spi_master_get_devdata(master); + u32 spi_err; + + spi_err = readl(dd->base + SPI_ERROR_FLAGS); + if (spi_err & SPI_ERR_TIME_OUT_ERR) { + dev_warn(master->dev.parent, "SPI timeout error\n"); + msm_dmov_flush(dd->tx_dma_chan); + msm_dmov_flush(dd->rx_dma_chan); + } + if (spi_err & SPI_ERR_OUTPUT_OVER_RUN_ERR) + dev_warn(master->dev.parent, "SPI output overrun error\n"); + if (spi_err & SPI_ERR_INPUT_UNDER_RUN_ERR) + dev_warn(master->dev.parent, "SPI input underrun error\n"); + if (spi_err & SPI_ERR_OUTPUT_UNDER_RUN_ERR) + dev_warn(master->dev.parent, "SPI output underrun error\n"); + if (spi_err & SPI_ERR_INPUT_OVER_RUN_ERR) + dev_warn(master->dev.parent, "SPI input overrun error\n"); + if (spi_err & SPI_ERR_CLK_OVER_RUN_ERR) + dev_warn(master->dev.parent, "SPI clock overrun error\n"); + if (spi_err & SPI_ERR_CLK_UNDER_RUN_ERR) + dev_warn(master->dev.parent, "SPI clock underrun error\n"); + writel(SPI_ERR_MASK, dd->base + SPI_ERROR_FLAGS); + return IRQ_HANDLED; +} + +static void msm_spi_unmap_dma_buffers(struct msm_spi *dd) +{ + struct device *dev; + + dev = &dd->cur_msg->spi->dev; + if (!dd->cur_msg->is_dma_mapped) { + if (dd->cur_transfer->rx_buf) + dma_unmap_single(dev, dd->cur_transfer->rx_dma, + dd->cur_transfer->len, + DMA_FROM_DEVICE); + if (dd->cur_transfer->tx_buf) + dma_unmap_single(dev, dd->cur_transfer->tx_dma, + dd->cur_transfer->len, + DMA_TO_DEVICE); + } + /* If we padded the transfer, we copy it from the padding buf */ + if (dd->unaligned_len && dd->read_buf) { + u32 offset = dd->cur_transfer->len - dd->unaligned_len; + dma_coherent_post_ops(); + memcpy(dd->read_buf + offset, dd->rx_padding, + dd->unaligned_len); + } +} + +/** + * msm_use_dm - decides whether to use data mover for this + * transfer + * @dd: device + * @tr: transfer + * + * Start using DM if: + * 1. Transfer is longer than 3*block size. + * 2. Buffers should be aligned to cache line. + * 3. If Transfer is bigger than max_output_count, we accept only aligned to + * block size transfers. + */ +static inline int msm_use_dm(struct msm_spi *dd, struct spi_transfer *tr) +{ + u32 cache_line = dma_get_cache_alignment(); + + if (!dd->use_dma) + return 0; + + if (tr->len < 3*dd->block_size) + return 0; + + if (tr->tx_buf) { + if (!IS_ALIGNED((size_t)tr->tx_buf, cache_line)) + return 0; + } + if (tr->rx_buf) { + if (!IS_ALIGNED((size_t)tr->rx_buf, cache_line)) + return 0; + } + return 1; +} + +static void msm_spi_process_transfer(struct msm_spi *dd) +{ + u8 bpw; + u32 spi_config; + u32 spi_ioc; + u32 spi_iom; + u32 spi_ioc_orig; + u32 max_speed; + u32 chip_select; + u32 read_count; + u32 timeout; + + if (!dd->cur_transfer->len) + return; + dd->tx_bytes_remaining = dd->cur_transfer->len; + dd->rx_bytes_remaining = dd->cur_transfer->len; + dd->read_buf = dd->cur_transfer->rx_buf; + dd->write_buf = dd->cur_transfer->tx_buf; + init_completion(&dd->transfer_complete); + if (dd->cur_transfer->bits_per_word) + bpw = dd->cur_transfer->bits_per_word; + else + if (dd->cur_msg->spi->bits_per_word) + bpw = dd->cur_msg->spi->bits_per_word; + else + bpw = 8; + dd->bytes_per_word = (bpw + 7) / 8; + + if (dd->cur_transfer->speed_hz) + max_speed = dd->cur_transfer->speed_hz; + else + max_speed = dd->cur_msg->spi->max_speed_hz; + if (!dd->clock_speed || max_speed < dd->clock_speed) + msm_spi_clock_set(dd, max_speed); + + read_count = DIV_ROUND_UP(dd->cur_transfer->len, dd->bytes_per_word); + if (!msm_use_dm(dd, dd->cur_transfer)) { + dd->mode = SPI_FIFO_MODE; + /* read_count cannot exceed fifo_size, and only one READ COUNT + interrupt is generated per transaction, so for transactions + larger than fifo size READ COUNT must be disabled. + For those transactions we usually move to Data Mover mode. + */ + if (read_count <= input_fifo_size) + writel(read_count, dd->base + SPI_MX_READ_COUNT); + else + writel(0, dd->base + SPI_MX_READ_COUNT); + } else + dd->mode = SPI_DMOV_MODE; + + /* Write mode - fifo or data mover*/ + spi_iom = readl(dd->base + SPI_IO_MODES); + spi_iom &= ~(SPI_IO_M_INPUT_MODE | SPI_IO_M_OUTPUT_MODE); + spi_iom = (spi_iom | (dd->mode << 10)); + spi_iom = (spi_iom | (dd->mode << 8)); + /* Turn on packing for data mover */ + if (dd->mode == SPI_DMOV_MODE) + spi_iom |= SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN; + else + spi_iom &= ~(SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN); + writel(spi_iom, dd->base + SPI_IO_MODES); + + spi_config = readl(dd->base + SPI_CONFIG); + if ((bpw - 1) != (spi_config & SPI_CFG_N)) + spi_config = (spi_config & ~SPI_CFG_N) | (bpw - 1); + if (dd->cur_msg->spi->mode & SPI_CPHA) + spi_config &= ~SPI_CFG_INPUT_FIRST; + else + spi_config |= SPI_CFG_INPUT_FIRST; + if (dd->cur_msg->spi->mode & SPI_LOOP) + spi_config |= SPI_CFG_LOOPBACK; + else + spi_config &= ~SPI_CFG_LOOPBACK; + spi_config &= ~(SPI_NO_INPUT|SPI_NO_OUTPUT); + if (dd->mode == SPI_DMOV_MODE) { + if (dd->read_buf == NULL) + spi_config |= SPI_NO_INPUT; + if (dd->write_buf == NULL) + spi_config |= SPI_NO_OUTPUT; + } + writel(spi_config, dd->base + SPI_CONFIG); + + spi_ioc = readl(dd->base + SPI_IO_CONTROL); + spi_ioc_orig = spi_ioc; + if (dd->cur_msg->spi->mode & SPI_CPOL) + spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH; + else + spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH; + chip_select = dd->cur_msg->spi->chip_select << 2; + if ((spi_ioc & SPI_IO_C_CS_SELECT) != chip_select) + spi_ioc = (spi_ioc & ~SPI_IO_C_CS_SELECT) | chip_select; + if (!dd->cur_transfer->cs_change) + spi_ioc |= SPI_IO_C_MX_CS_MODE; + if (spi_ioc != spi_ioc_orig) + writel(spi_ioc, dd->base + SPI_IO_CONTROL); + + if (dd->mode == SPI_DMOV_MODE) { + msm_spi_setup_dm_transfer(dd); + msm_spi_enqueue_dm_commands(dd); + } + /* The output fifo interrupt handler will handle all writes after + the first. Restricting this to one write avoids contention + issues and race conditions between this thread and the int handler + */ + else if (dd->mode == SPI_FIFO_MODE) + msm_spi_write_word_to_fifo(dd); + + /* Only enter the RUN state after the first word is written into + the output FIFO. Otherwise, the output FIFO EMPTY interrupt + might fire before the first word is written resulting in a + possible race condition. + */ + writel((readl(dd->base + SPI_OPERATIONAL) + & ~SPI_OP_STATE) | SPI_OP_STATE_RUN, + dd->base + SPI_OPERATIONAL); + + timeout = 100 * msecs_to_jiffies( + DIV_ROUND_UP(dd->cur_transfer->len * 8, + max_speed / MSEC_PER_SEC)); + do { + if (!wait_for_completion_timeout(&dd->transfer_complete, + timeout)) { + dev_err(dd->dev, "%s: SPI transaction " + "timeout\n", __func__); + dd->cur_msg->status = -EIO; + if (dd->mode == SPI_DMOV_MODE) { + writel(0, dd->base + SPI_TIME_OUT); + msm_dmov_flush(dd->tx_dma_chan); + msm_dmov_flush(dd->rx_dma_chan); + } + break; + } + } while (msm_spi_dm_send_next(dd)); + + if (dd->mode == SPI_DMOV_MODE) + msm_spi_unmap_dma_buffers(dd); + dd->mode = SPI_MODE_NONE; + + writel(spi_ioc & ~SPI_IO_C_MX_CS_MODE, dd->base + SPI_IO_CONTROL); + writel((readl(dd->base + SPI_OPERATIONAL) + & ~SPI_OP_STATE) | SPI_OP_STATE_RESET, + dd->base + SPI_OPERATIONAL); +} + +/* workqueue - pull messages from queue & process */ +static void msm_spi_workq(struct work_struct *work) +{ + struct msm_spi *dd = + container_of(work, struct msm_spi, work_data); + unsigned long flags; + u32 spi_op; + bool status_error = 0; + + spi_op = readl(dd->base + SPI_OPERATIONAL); + if (spi_op & SPI_OP_STATE_VALID) { + spi_op &= ~SPI_OP_STATE; + spi_op |= SPI_OP_STATE_RUN; + } else { + dev_err(dd->dev, "%s: SPI operational state not valid\n", + __func__); + status_error = 1; + } + + dd->transfer_in_progress = 1; + spin_lock_irqsave(&dd->queue_lock, flags); + while (!list_empty(&dd->queue)) { + dd->cur_msg = list_entry(dd->queue.next, + struct spi_message, queue); + list_del_init(&dd->cur_msg->queue); + spin_unlock_irqrestore(&dd->queue_lock, flags); + if (status_error) + dd->cur_msg->status = -EIO; + else { + list_for_each_entry(dd->cur_transfer, + &dd->cur_msg->transfers, + transfer_list) { + msm_spi_process_transfer(dd); + } + } + if (dd->cur_msg->complete) + dd->cur_msg->complete(dd->cur_msg->context); + spin_lock_irqsave(&dd->queue_lock, flags); + } + spin_unlock_irqrestore(&dd->queue_lock, flags); + dd->transfer_in_progress = 0; +} + +static int msm_spi_transfer(struct spi_device *spi, struct spi_message *msg) +{ + struct msm_spi *dd; + unsigned long flags; + struct spi_transfer *tr; + + dd = spi_master_get_devdata(spi->master); + if (dd->suspended) + return -EBUSY; + + if (list_empty(&msg->transfers) || !msg->complete) + return -EINVAL; + + list_for_each_entry(tr, &msg->transfers, transfer_list) { + void *tx_buf = (void *)tr->tx_buf; + void *rx_buf = tr->rx_buf; + unsigned len = tr->len; + + /* Check message parameters */ + if (tr->speed_hz > dd->max_clock_speed || (tr->bits_per_word && + (tr->bits_per_word < 4 || tr->bits_per_word > 32)) || + (tx_buf == NULL && rx_buf == NULL)) { + dev_err(&spi->dev, "Invalid transfer: %d Hz, %d bpw" + "tx=%p, rx=%p\n", + tr->speed_hz, tr->bits_per_word, + tx_buf, rx_buf); + goto error; + } + + if (!msm_use_dm(dd, tr) || msg->is_dma_mapped) + continue; + + /* Do DMA mapping "early" for better error reporting */ + if (tx_buf != NULL) { + tr->tx_dma = dma_map_single(&spi->dev, tx_buf, len, + DMA_TO_DEVICE); + if (dma_mapping_error(NULL, tr->tx_dma)) { + dev_err(&spi->dev, "dma %cX %d bytes error\n", + 'T', len); + goto error; + } + } + + if (rx_buf != NULL) { + tr->rx_dma = dma_map_single(&spi->dev, rx_buf, len, + DMA_FROM_DEVICE); + if (dma_mapping_error(NULL, tr->rx_dma)) { + dev_err(&spi->dev, "dma %cX %d bytes error\n", + 'R', len); + if (tx_buf != NULL) + dma_unmap_single(NULL, tr->tx_dma, + len, DMA_TO_DEVICE); + goto error; + } + } + } + + spin_lock_irqsave(&dd->queue_lock, flags); + list_add_tail(&msg->queue, &dd->queue); + spin_unlock_irqrestore(&dd->queue_lock, flags); + queue_work(dd->workqueue, &dd->work_data); + return 0; + +error: + list_for_each_entry_continue_reverse(tr, &msg->transfers, transfer_list) + { + if (msm_use_dm(dd, tr) && !msg->is_dma_mapped) { + if (tr->rx_buf != NULL) + dma_unmap_single(&spi->dev, tr->rx_dma, tr->len, + DMA_TO_DEVICE); + if (tr->tx_buf != NULL) + dma_unmap_single(&spi->dev, tr->tx_dma, tr->len, + DMA_FROM_DEVICE); + } + } + return -EINVAL; +} + +static int msm_spi_setup(struct spi_device *spi) +{ + struct msm_spi *dd; + int rc = 0; + u32 spi_ioc; + u32 spi_config; + u32 mask; + + if (!spi->bits_per_word) + spi->bits_per_word = 8; + if (spi->bits_per_word < 4 || spi->bits_per_word > 32) { + dev_err(&spi->dev, "%s: invalid bits_per_word %d\n", + __func__, spi->bits_per_word); + rc = -EINVAL; + } + if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP)) { + dev_err(&spi->dev, "%s, unsupported mode bits %x\n", + __func__, + spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_CS_HIGH + | SPI_LOOP)); + rc = -EINVAL; + } + if (spi->chip_select > SPI_NUM_CHIPSELECTS-1) { + dev_err(&spi->dev, "%s, chip select %d exceeds max value %d\n", + __func__, spi->chip_select, SPI_NUM_CHIPSELECTS - 1); + rc = -EINVAL; + } + + if (rc) + goto err_setup_exit; + + dd = spi_master_get_devdata(spi->master); + spi_ioc = readl(dd->base + SPI_IO_CONTROL); + mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select; + if (spi->mode & SPI_CS_HIGH) + spi_ioc |= mask; + else + spi_ioc &= ~mask; + if (spi->mode & SPI_CPOL) + spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH; + else + spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH; + writel(spi_ioc, dd->base + SPI_IO_CONTROL); + + spi_config = readl(dd->base + SPI_CONFIG); + if (spi->mode & SPI_LOOP) + spi_config |= SPI_CFG_LOOPBACK; + else + spi_config &= ~SPI_CFG_LOOPBACK; + if (spi->mode & SPI_CPHA) + spi_config &= ~SPI_CFG_INPUT_FIRST; + else + spi_config |= SPI_CFG_INPUT_FIRST; + writel(spi_config, dd->base + SPI_CONFIG); + +err_setup_exit: + return rc; +} + +#ifdef CONFIG_DEBUG_FS +static int debugfs_iomem_x32_set(void *data, u64 val) +{ + iowrite32(val, data); + wmb(); + return 0; +} + +static int debugfs_iomem_x32_get(void *data, u64 *val) +{ + *val = ioread32(data); + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, debugfs_iomem_x32_get, + debugfs_iomem_x32_set, "0x%08llx\n"); + +static void spi_debugfs_init(struct msm_spi *dd) +{ + dd->dent_spi = debugfs_create_dir(dev_name(dd->dev), NULL); + if (dd->dent_spi) { + int i; + for (i = 0; i < ARRAY_SIZE(debugfs_spi_regs); i++) { + dd->debugfs_spi_regs[i] = + debugfs_create_file( + debugfs_spi_regs[i].name, + debugfs_spi_regs[i].mode, + dd->dent_spi, + dd->base + debugfs_spi_regs[i].offset, + &fops_iomem_x32); + } + } +} + +static void spi_debugfs_exit(struct msm_spi *dd) +{ + if (dd->dent_spi) { + int i; + debugfs_remove_recursive(dd->dent_spi); + dd->dent_spi = NULL; + for (i = 0; i < ARRAY_SIZE(debugfs_spi_regs); i++) + dd->debugfs_spi_regs[i] = NULL; + } +} +#else +static void spi_debugfs_init(struct msm_spi *dd) {} +static void spi_debugfs_exit(struct msm_spi *dd) {} +#endif + +/* ===Device attributes begin=== */ +static ssize_t show_stats(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct msm_spi *dd = spi_master_get_devdata(master); + + return snprintf(buf, PAGE_SIZE, + "Device %s\n" + "use_dma ? %s\n" + "DMA configuration:\n" + "tx_ch=%d, rx_ch=%d, tx_crci= %d, rx_crci=%d\n" + "--statistics--\n" + "Rx isrs = %d\n" + "Tx isrs = %d\n" + "DMA error = %d\n" + "--debug--\n" + "NA yet\n", + dev_name(dev), + dd->use_dma ? "yes" : "no", + dd->tx_dma_chan, + dd->rx_dma_chan, + dd->tx_dma_crci, + dd->rx_dma_crci, + dd->stat_rx, + dd->stat_tx, + dd->stat_dmov_err + ); +} + +/* Reset statistics on write */ +static ssize_t set_stats(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct msm_spi *dd = dev_get_drvdata(dev); + dd->stat_rx = 0; + dd->stat_tx = 0; + dd->stat_dmov_err = 0; + return count; +} + +static DEVICE_ATTR(stats, S_IRUGO | S_IWUSR, show_stats, set_stats); + +static struct attribute *dev_attrs[] = { + &dev_attr_stats.attr, + NULL, +}; + +static struct attribute_group dev_attr_grp = { + .attrs = dev_attrs, +}; +/* ===Device attributes end=== */ + +/** + * spi_dmov_tx_complete_func - DataMover tx completion callback + * + * Executed in IRQ context (Data Mover's IRQ) DataMover's + * spinlock @msm_dmov_lock held. + */ +static void spi_dmov_tx_complete_func(struct msm_dmov_cmd *cmd, + unsigned int result, + struct msm_dmov_errdata *err) +{ + struct msm_spi *dd; + + if (!(result & DMOV_RSLT_VALID)) { + pr_err("Invalid DMOV result: rc=0x%08x, cmd = %p", result, cmd); + return; + } + /* restore original context */ + dd = container_of(cmd, struct msm_spi, tx_hdr); + if (result & DMOV_RSLT_DONE) + dd->stat_tx++; + else { + /* Error or flush */ + if (result & DMOV_RSLT_ERROR) { + dev_err(dd->dev, "DMA error (0x%08x)\n", result); + dd->stat_dmov_err++; + } + if (result & DMOV_RSLT_FLUSH) { + /* + * Flushing normally happens in process of + * removing, when we are waiting for outstanding + * DMA commands to be flushed. + */ + dev_info(dd->dev, + "DMA channel flushed (0x%08x)\n", result); + } + if (err) + dev_err(dd->dev, + "Flush data(%08x %08x %08x %08x %08x %08x)\n", + err->flush[0], err->flush[1], err->flush[2], + err->flush[3], err->flush[4], err->flush[5]); + dd->cur_msg->status = -EIO; + writel(0, dd->base + SPI_TIME_OUT); + complete(&dd->transfer_complete); + } +} + +/** + * spi_dmov_rx_complete_func - DataMover rx completion callback + * + * Executed in IRQ context (Data Mover's IRQ) + * DataMover's spinlock @msm_dmov_lock held. + */ +static void spi_dmov_rx_complete_func(struct msm_dmov_cmd *cmd, + unsigned int result, + struct msm_dmov_errdata *err) +{ + struct msm_spi *dd; + + if (!(result & DMOV_RSLT_VALID)) { + pr_err("Invalid DMOV result(rc = 0x%08x, cmd = %p)", + result, cmd); + return; + } + /* restore original context */ + dd = container_of(cmd, struct msm_spi, rx_hdr); + if (result & DMOV_RSLT_DONE) { + dd->stat_rx++; + if (atomic_inc_return(&dd->rx_irq_called) == 1) + return; + complete(&dd->transfer_complete); + } else { + /** Error or flush */ + if (result & DMOV_RSLT_ERROR) { + dev_err(dd->dev, "DMA error(0x%08x)\n", result); + dd->stat_dmov_err++; + } + if (result & DMOV_RSLT_FLUSH) { + dev_info(dd->dev, + "DMA channel flushed(0x%08x)\n", result); + } + if (err) + dev_err(dd->dev, + "Flush data(%08x %08x %08x %08x %08x %08x)\n", + err->flush[0], err->flush[1], err->flush[2], + err->flush[3], err->flush[4], err->flush[5]); + dd->cur_msg->status = -EIO; + writel(0, dd->base + SPI_TIME_OUT); + complete(&dd->transfer_complete); + } +} + +static inline u32 get_chunk_size(struct msm_spi *dd) +{ + u32 cache_line = dma_get_cache_alignment(); + + return (roundup(sizeof(struct spi_dmov_cmd), DM_BYTE_ALIGN) + + roundup(dd->burst_size, cache_line))*2; +} + +static void msm_spi_teardown_dma(struct msm_spi *dd) +{ + int limit = 0; + + if (!dd->use_dma) + return; + + while (dd->mode == SPI_DMOV_MODE && limit++ < 50) { + msm_dmov_flush(dd->tx_dma_chan); + msm_dmov_flush(dd->rx_dma_chan); + msleep(10); + } + + dma_free_coherent(NULL, get_chunk_size(dd), dd->tx_dmov_cmd, + dd->tx_dmov_cmd_dma); + dd->tx_dmov_cmd = dd->rx_dmov_cmd = NULL; + dd->tx_padding = dd->rx_padding = NULL; +} + +static __init int msm_spi_init_dma(struct msm_spi *dd) +{ + dmov_box *box; + u32 cache_line = dma_get_cache_alignment(); + + /* Allocate all as one chunk, since all is smaller than page size */ + + /* We send NULL device, since it requires coherent_dma_mask id + device definition, we're okay with using system pool */ + dd->tx_dmov_cmd = dma_alloc_coherent(NULL, get_chunk_size(dd), + &dd->tx_dmov_cmd_dma, GFP_KERNEL); + if (dd->tx_dmov_cmd == NULL) + return -ENOMEM; + + /* DMA addresses should be 64 bit aligned aligned */ + dd->rx_dmov_cmd = (struct spi_dmov_cmd *) + ALIGN((size_t)&dd->tx_dmov_cmd[1], DM_BYTE_ALIGN); + dd->rx_dmov_cmd_dma = ALIGN(dd->tx_dmov_cmd_dma + + sizeof(struct spi_dmov_cmd), DM_BYTE_ALIGN); + + /* Buffers should be aligned to cache line */ + dd->tx_padding = (u8 *)ALIGN((size_t)&dd->rx_dmov_cmd[1], cache_line); + dd->tx_padding_dma = ALIGN(dd->rx_dmov_cmd_dma + + sizeof(struct spi_dmov_cmd), cache_line); + dd->rx_padding = (u8 *)ALIGN((size_t)(dd->tx_padding + dd->burst_size), + cache_line); + dd->rx_padding_dma = ALIGN(dd->tx_padding_dma + dd->burst_size, + cache_line); + + /* Setup DM commands */ + box = &(dd->rx_dmov_cmd->box); + box->cmd = CMD_MODE_BOX | CMD_SRC_CRCI(dd->rx_dma_crci); + box->src_row_addr = (uint32_t)dd->mem_phys_addr + SPI_INPUT_FIFO; + dd->rx_hdr.cmdptr = DMOV_CMD_PTR_LIST | + DMOV_CMD_ADDR(dd->rx_dmov_cmd_dma + + offsetof(struct spi_dmov_cmd, cmd_ptr)); + dd->rx_hdr.complete_func = spi_dmov_rx_complete_func; + + box = &(dd->tx_dmov_cmd->box); + box->cmd = CMD_MODE_BOX | CMD_DST_CRCI(dd->tx_dma_crci); + box->dst_row_addr = (uint32_t)dd->mem_phys_addr + SPI_OUTPUT_FIFO; + dd->tx_hdr.cmdptr = DMOV_CMD_PTR_LIST | + DMOV_CMD_ADDR(dd->tx_dmov_cmd_dma + + offsetof(struct spi_dmov_cmd, cmd_ptr)); + dd->tx_hdr.complete_func = spi_dmov_tx_complete_func; + + dd->tx_dmov_cmd->single_pad.cmd = CMD_MODE_SINGLE | CMD_LC | + CMD_DST_CRCI(dd->tx_dma_crci); + dd->tx_dmov_cmd->single_pad.dst = (uint32_t)dd->mem_phys_addr + + SPI_OUTPUT_FIFO; + dd->rx_dmov_cmd->single_pad.cmd = CMD_MODE_SINGLE | CMD_LC | + CMD_SRC_CRCI(dd->rx_dma_crci); + dd->rx_dmov_cmd->single_pad.src = (uint32_t)dd->mem_phys_addr + + SPI_INPUT_FIFO; + + /* Clear remaining activities on channel */ + msm_dmov_flush(dd->tx_dma_chan); + msm_dmov_flush(dd->rx_dma_chan); + + return 0; +} + +static int __init msm_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct msm_spi *dd; + struct resource *resource; + int rc = 0; + struct clk *pclk; + struct msm_spi_platform_data *pdata = pdev->dev.platform_data; + + master = spi_alloc_master(&pdev->dev, sizeof(struct msm_spi)); + if (!master) { + rc = -ENOMEM; + dev_err(&pdev->dev, "master allocation failed\n"); + goto err_probe_exit; + } + + master->bus_num = pdev->id; + master->num_chipselect = SPI_NUM_CHIPSELECTS; + master->setup = msm_spi_setup; + master->transfer = msm_spi_transfer; + platform_set_drvdata(pdev, master); + dd = spi_master_get_devdata(master); + + dd->irq_in = platform_get_irq_byname(pdev, "spi_irq_in"); + dd->irq_out = platform_get_irq_byname(pdev, "spi_irq_out"); + dd->irq_err = platform_get_irq_byname(pdev, "spi_irq_err"); + if ((dd->irq_in < 0) || (dd->irq_out < 0) || (dd->irq_err < 0)) + goto err_probe_res; + + resource = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "spi_base"); + if (!resource) { + rc = -ENXIO; + goto err_probe_res; + } + dd->mem_phys_addr = resource->start; + dd->mem_size = (resource->end - resource->start) + 1; + + if (pdata && pdata->dma_config) { + rc = pdata->dma_config(); + if (!rc) { + resource = platform_get_resource_byname(pdev, + IORESOURCE_DMA, "spidm_channels"); + if (resource) { + dd->rx_dma_chan = resource->start; + dd->tx_dma_chan = resource->end; + + resource = platform_get_resource_byname(pdev, + IORESOURCE_DMA, "spidm_crci"); + if (!resource) { + rc = -ENXIO; + goto err_probe_res; + } + dd->rx_dma_crci = resource->start; + dd->tx_dma_crci = resource->end; + dd->use_dma = 1; + } + } + } + + if (pdata && pdata->gpio_config) { + rc = pdata->gpio_config(); + if (rc) { + dev_err(&pdev->dev, "%s: error configuring GPIOs\n", + __func__); + goto err_probe_gpio; + } + } + + spin_lock_init(&dd->queue_lock); + INIT_LIST_HEAD(&dd->queue); + INIT_WORK(&dd->work_data, msm_spi_workq); + dd->workqueue = create_singlethread_workqueue( + master->dev.parent->bus_id); + if (!dd->workqueue) + goto err_probe_workq; + + if (!request_mem_region(dd->mem_phys_addr, dd->mem_size, + SPI_QSD_NAME)) { + rc = -ENXIO; + goto err_probe_reqmem; + } + + dd->base = ioremap(dd->mem_phys_addr, dd->mem_size); + if (!dd->base) + goto err_probe_ioremap; + + dd->dev = &pdev->dev; + dd->clk = clk_get(&pdev->dev, "spi_clk"); + if (IS_ERR(dd->clk)) { + dev_err(&pdev->dev, "%s: unable to get spi_clk\n", __func__); + rc = PTR_ERR(dd->clk); + goto err_probe_clk_get; + } + rc = clk_enable(dd->clk); + if (rc) { + dev_err(&pdev->dev, "%s: unable to enable spi_clk\n", + __func__); + goto err_probe_clk_enable; + } + pclk = clk_get(&pdev->dev, "spi_pclk"); + if (!IS_ERR(pclk)) { + dd->pclk = pclk; + rc = clk_enable(dd->pclk); + if (rc) { + dev_err(&pdev->dev, "%s: unable to enable spi_pclk\n", + __func__); + goto err_probe_pclk_enable; + } + } + if (pdata && pdata->max_clock_speed) { + msm_spi_clock_set(dd, pdata->max_clock_speed); + dd->max_clock_speed = pdata->max_clock_speed; + } + msm_spi_calculate_fifo_size(dd); + writel(0x1, dd->base + SPI_SW_RESET); + if (dd->use_dma) { + rc = msm_spi_init_dma(dd); + if (rc) + goto err_probe_dma; + } + writel(0x00000000, dd->base + SPI_OPERATIONAL); + writel(0x00000000, dd->base + SPI_CONFIG); + writel(0x00000000, dd->base + SPI_IO_MODES); + writel(SPI_IO_C_NO_TRI_STATE, dd->base + SPI_IO_CONTROL); + if (!(readl(dd->base + SPI_OPERATIONAL) & SPI_OP_STATE_VALID)) { + dev_err(&pdev->dev, "%s: SPI operational state not valid\n", + __func__); + rc = -1; + goto err_probe_state; + } + writel(SPI_OP_STATE_RUN, dd->base + SPI_OPERATIONAL); + + dd->suspended = 0; + dd->transfer_in_progress = 0; + dd->mode = SPI_MODE_NONE; + + rc = request_irq(dd->irq_in, msm_spi_input_irq, IRQF_TRIGGER_RISING, + pdev->name, dd); + if (rc) + goto err_probe_irq1; + rc = request_irq(dd->irq_out, msm_spi_output_irq, IRQF_TRIGGER_RISING, + pdev->name, dd); + if (rc) + goto err_probe_irq2; + rc = request_irq(dd->irq_err, msm_spi_error_irq, IRQF_TRIGGER_RISING, + pdev->name, master); + if (rc) + goto err_probe_irq3; + + rc = spi_register_master(master); + if (rc) + goto err_probe_reg_master; + + rc = sysfs_create_group(&(dd->dev->kobj), &dev_attr_grp); + if (rc) { + dev_err(&pdev->dev, "failed to create dev. attrs : %d\n", rc); + goto err_attrs; + } + + spi_debugfs_init(dd); + + return 0; + +err_attrs: +err_probe_reg_master: + free_irq(dd->irq_err, master); +err_probe_irq3: + free_irq(dd->irq_out, dd); +err_probe_irq2: + free_irq(dd->irq_in, dd); +err_probe_irq1: +err_probe_state: + msm_spi_teardown_dma(dd); +err_probe_dma: + if (dd->pclk) + clk_disable(dd->pclk); +err_probe_pclk_enable: + if (dd->pclk) + clk_put(dd->pclk); + clk_disable(dd->clk); +err_probe_clk_enable: + clk_put(dd->clk); +err_probe_clk_get: + iounmap(dd->base); +err_probe_ioremap: + release_mem_region(dd->mem_phys_addr, dd->mem_size); +err_probe_reqmem: + destroy_workqueue(dd->workqueue); +err_probe_workq: +err_probe_gpio: + if (pdata && pdata->gpio_release) + pdata->gpio_release(); +err_probe_res: + spi_master_put(master); +err_probe_exit: + return rc; +} + +#ifdef CONFIG_PM +static int msm_spi_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct msm_spi *dd; + int limit = 0; + + if (!master) + goto suspend_exit; + dd = spi_master_get_devdata(master); + if (!dd) + goto suspend_exit; + dd->suspended = 1; + while ((!list_empty(&dd->queue) || dd->transfer_in_progress) && + limit < 50) { + if (dd->mode == SPI_DMOV_MODE) { + msm_dmov_flush(dd->tx_dma_chan); + msm_dmov_flush(dd->rx_dma_chan); + } + limit++; + msleep(1); + } + + disable_irq(dd->irq_in); + disable_irq(dd->irq_out); + disable_irq(dd->irq_err); + clk_disable(dd->clk); + +suspend_exit: + return 0; +} + +static int msm_spi_resume(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct msm_spi *dd; + int rc; + + if (!master) + goto resume_exit; + dd = spi_master_get_devdata(master); + if (!dd) + goto resume_exit; + + rc = clk_enable(dd->clk); + if (rc) { + dev_err(dd->dev, "%s: unable to enable spi_clk\n", + __func__); + goto resume_exit; + } + + enable_irq(dd->irq_in); + enable_irq(dd->irq_out); + enable_irq(dd->irq_err); + dd->suspended = 0; +resume_exit: + return 0; +} +#else +#define msm_spi_suspend NULL +#define msm_spi_resume NULL +#endif /* CONFIG_PM */ + +static int __devexit msm_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct msm_spi *dd = spi_master_get_devdata(master); + struct msm_spi_platform_data *pdata = pdev->dev.platform_data; + + spi_debugfs_exit(dd); + sysfs_remove_group(&pdev->dev.kobj, &dev_attr_grp); + + free_irq(dd->irq_in, dd); + free_irq(dd->irq_out, dd); + free_irq(dd->irq_err, master); + + msm_spi_teardown_dma(dd); + + if (pdata && pdata->gpio_release) + pdata->gpio_release(); + + iounmap(dd->base); + release_mem_region(dd->mem_phys_addr, dd->mem_size); + clk_disable(dd->clk); + clk_put(dd->clk); + if (dd->pclk) { + clk_disable(dd->pclk); + clk_put(dd->pclk); + } + destroy_workqueue(dd->workqueue); + platform_set_drvdata(pdev, 0); + spi_unregister_master(master); + spi_master_put(master); + + return 0; +} + +static struct platform_driver msm_spi_driver = { + .probe = msm_spi_probe, + .driver = { + .name = "spi_qsd", + .owner = THIS_MODULE, + }, + .suspend = msm_spi_suspend, + .resume = msm_spi_resume, + .remove = __exit_p(msm_spi_remove), +}; + +static int __init msm_spi_init(void) +{ + return platform_driver_register(&msm_spi_driver); +} +module_init(msm_spi_init); + +static void __exit msm_spi_exit(void) +{ + platform_driver_unregister(&msm_spi_driver); +} +module_exit(msm_spi_exit); From 79ff2d33ff298f2edf64a6eda396124c8941f4ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 12 Apr 2010 18:47:34 -0700 Subject: [PATCH 0641/2556] spi_qsd: Fix to compile and run. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use dev_name instead of bus_id, set status on success and remove call to Qualcomm specific memory barrier api. The datamover already has the needed barriers. Signed-off-by: Arve Hjønnevåg --- drivers/spi/spi_qsd.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c index 848ed2e4cefdb..1392d29841182 100644 --- a/drivers/spi/spi_qsd.c +++ b/drivers/spi/spi_qsd.c @@ -456,7 +456,6 @@ static void msm_spi_setup_dm_transfer(struct msm_spi *dd) static void msm_spi_enqueue_dm_commands(struct msm_spi *dd) { - dma_coherent_pre_ops(); if (dd->write_buf) msm_dmov_enqueue_cmd(dd->tx_dma_chan, &dd->tx_hdr); if (dd->read_buf) @@ -641,7 +640,6 @@ static void msm_spi_unmap_dma_buffers(struct msm_spi *dd) /* If we padded the transfer, we copy it from the padding buf */ if (dd->unaligned_len && dd->read_buf) { u32 offset = dd->cur_transfer->len - dd->unaligned_len; - dma_coherent_post_ops(); memcpy(dd->read_buf + offset, dd->rx_padding, dd->unaligned_len); } @@ -857,6 +855,8 @@ static void msm_spi_workq(struct work_struct *work) &dd->cur_msg->transfers, transfer_list) { msm_spi_process_transfer(dd); + if (dd->cur_msg->status == -EINPROGRESS) + dd->cur_msg->status = 0; } } if (dd->cur_msg->complete) @@ -1309,14 +1309,13 @@ static int __init msm_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); dd = spi_master_get_devdata(master); - dd->irq_in = platform_get_irq_byname(pdev, "spi_irq_in"); - dd->irq_out = platform_get_irq_byname(pdev, "spi_irq_out"); - dd->irq_err = platform_get_irq_byname(pdev, "spi_irq_err"); + dd->irq_in = platform_get_irq_byname(pdev, "irq_in"); + dd->irq_out = platform_get_irq_byname(pdev, "irq_out"); + dd->irq_err = platform_get_irq_byname(pdev, "irq_err"); if ((dd->irq_in < 0) || (dd->irq_out < 0) || (dd->irq_err < 0)) goto err_probe_res; - resource = platform_get_resource_byname(pdev, - IORESOURCE_MEM, "spi_base"); + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!resource) { rc = -ENXIO; goto err_probe_res; @@ -1359,7 +1358,7 @@ static int __init msm_spi_probe(struct platform_device *pdev) INIT_LIST_HEAD(&dd->queue); INIT_WORK(&dd->work_data, msm_spi_workq); dd->workqueue = create_singlethread_workqueue( - master->dev.parent->bus_id); + dev_name(master->dev.parent)); if (!dd->workqueue) goto err_probe_workq; @@ -1585,7 +1584,7 @@ static int __devexit msm_spi_remove(struct platform_device *pdev) static struct platform_driver msm_spi_driver = { .probe = msm_spi_probe, .driver = { - .name = "spi_qsd", + .name = "msm_spi", .owner = THIS_MODULE, }, .suspend = msm_spi_suspend, From 9747d16a15fafc0e33a256e4932f8ba42161bcad Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Tue, 11 May 2010 13:30:43 -0700 Subject: [PATCH 0642/2556] ARM: msm7x30: add 2nd i2c controller and audio clock defs Signed-off-by: Brian Swetland --- arch/arm/mach-msm/devices.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h index b8bd49759e442..825f69103ed1d 100644 --- a/arch/arm/mach-msm/devices.h +++ b/arch/arm/mach-msm/devices.h @@ -35,6 +35,7 @@ extern struct platform_device msm_device_otg; extern struct platform_device msm_device_hsusb_host; extern struct platform_device msm_device_i2c; +extern struct platform_device msm_device_i2c2; extern struct platform_device msm_device_smd; From 8b96d30a102b79a305373c7ab2af0e3b54213083 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Mon, 8 Mar 2010 17:09:43 -0800 Subject: [PATCH 0643/2556] msm: move DAL support out of qdsp6 subdirectory Since 7x30/qdsp5v2 uses DAL as well, we will move this into the common directory for the msm architecture. Change-Id: I79a5ca71171a0c6cbaf82bef8e72fea51d617d75 Signed-off-by: Brian Swetland --- arch/arm/mach-msm/Kconfig | 10 ++++++++++ arch/arm/mach-msm/Makefile | 2 ++ arch/arm/mach-msm/{qdsp6 => }/dal.c | 2 ++ arch/arm/mach-msm/{qdsp6 => }/dal.h | 0 arch/arm/mach-msm/qdsp6/Makefile | 1 - arch/arm/mach-msm/qdsp6/dal_adie.h | 2 +- arch/arm/mach-msm/qdsp6/msm_q6vdec.c | 2 +- arch/arm/mach-msm/qdsp6/msm_q6venc.c | 3 ++- arch/arm/mach-msm/qdsp6/q6audio.c | 2 +- 9 files changed, 19 insertions(+), 5 deletions(-) rename arch/arm/mach-msm/{qdsp6 => }/dal.c (99%) rename arch/arm/mach-msm/{qdsp6 => }/dal.h (100%) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 500ca2c86d0f6..a4810540d47a5 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -24,6 +24,7 @@ config ARCH_MSM7X30 select MSM_PROC_COMM select HAS_MSM_DEBUG_UART_PHYS select VERIFY_PERMISSION_FAULT + select MSM_DAL config ARCH_QSD8X50 bool "QSD8X50" @@ -35,6 +36,7 @@ config ARCH_QSD8X50 select MSM_PROC_COMM select HAS_MSM_DEBUG_UART_PHYS select VERIFY_PERMISSION_FAULT + select MSM_DAL config ARCH_MSM8X60 bool "MSM8X60" @@ -464,6 +466,14 @@ config IOMMU_API config MSM_SCM bool +config MSM_DAL + default n + bool "MSM Driver Access Layer (DAL RPC)" + help + Support for the DAL RPC interface used to communicate with + the baseband processor or DSP in newer Qualcomm MSM/QSD + chips. + config MSM_ONCRPCROUTER depends on MSM_SMD default y diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 707e0339f0c4e..fcd4245b2fea1 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -31,6 +31,8 @@ obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_SMP) += headsmp.o platsmp.o +obj-$(CONFIG_MSM_DAL) += dal.o + obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter.o obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_device.o obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o diff --git a/arch/arm/mach-msm/qdsp6/dal.c b/arch/arm/mach-msm/dal.c similarity index 99% rename from arch/arm/mach-msm/qdsp6/dal.c rename to arch/arm/mach-msm/dal.c index 23aa1551f2e65..7d37efc2a3c28 100644 --- a/arch/arm/mach-msm/qdsp6/dal.c +++ b/arch/arm/mach-msm/dal.c @@ -366,7 +366,9 @@ int dal_call_raw(struct dal_client *client, dal_trace_dump(client); pr_err("dal: call timed out. dsp is probably dead.\n"); dal_trace_print(hdr, data, data_len, 0); +#if defined(CONFIG_MSM_QDSP6) q6audio_dsp_not_responding(); +#endif } return client->status; diff --git a/arch/arm/mach-msm/qdsp6/dal.h b/arch/arm/mach-msm/dal.h similarity index 100% rename from arch/arm/mach-msm/qdsp6/dal.h rename to arch/arm/mach-msm/dal.h diff --git a/arch/arm/mach-msm/qdsp6/Makefile b/arch/arm/mach-msm/qdsp6/Makefile index 1be03b90d4c87..05cb351e7b45e 100644 --- a/arch/arm/mach-msm/qdsp6/Makefile +++ b/arch/arm/mach-msm/qdsp6/Makefile @@ -1,4 +1,3 @@ -obj-y += dal.o obj-y += q6audio.o obj-y += pcm_out.o obj-y += pcm_in.o diff --git a/arch/arm/mach-msm/qdsp6/dal_adie.h b/arch/arm/mach-msm/qdsp6/dal_adie.h index b7f58456e5b19..99e3c63f5cda9 100644 --- a/arch/arm/mach-msm/qdsp6/dal_adie.h +++ b/arch/arm/mach-msm/qdsp6/dal_adie.h @@ -29,7 +29,7 @@ #ifndef _MACH_MSM_QDSP6_ADIE_ #define _MACH_MSM_QDSP6_ADIE_ -#include "dal.h" +#include "../dal.h" #define ADIE_DAL_DEVICE 0x02000029 #define ADIE_DAL_PORT "SMD_DAL_AM_AUD" diff --git a/arch/arm/mach-msm/qdsp6/msm_q6vdec.c b/arch/arm/mach-msm/qdsp6/msm_q6vdec.c index 04ac8a7bf9bf7..a719dbdbcd912 100644 --- a/arch/arm/mach-msm/qdsp6/msm_q6vdec.c +++ b/arch/arm/mach-msm/qdsp6/msm_q6vdec.c @@ -49,7 +49,7 @@ #include #include -#include "dal.h" +#include "../dal.h" #define DALDEVICEID_VDEC_DEVICE 0x02000026 #define DALDEVICEID_VDEC_PORTNAME "DSP_DAL_AQ_VID" diff --git a/arch/arm/mach-msm/qdsp6/msm_q6venc.c b/arch/arm/mach-msm/qdsp6/msm_q6venc.c index 462a24f029bc8..7b4eb85d8b6d6 100644 --- a/arch/arm/mach-msm/qdsp6/msm_q6venc.c +++ b/arch/arm/mach-msm/qdsp6/msm_q6venc.c @@ -35,7 +35,8 @@ #include #include -#include "dal.h" +//#include +#include "../dal.h" #define DALDEVICEID_VENC_DEVICE 0x0200002D #define DALDEVICEID_VENC_PORTNAME "DSP_DAL_AQ_VID" diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c index ae78079f45fdb..26c202bc2bc61 100644 --- a/arch/arm/mach-msm/qdsp6/q6audio.c +++ b/arch/arm/mach-msm/qdsp6/q6audio.c @@ -26,7 +26,7 @@ #include #include -#include "dal.h" +#include "../dal.h" #include "dal_audio.h" #include "dal_audio_format.h" #include "dal_acdb.h" From 42bc71dd7ce3e03172f27e3f8833435f545061df Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 5 May 2010 20:04:40 -0700 Subject: [PATCH 0644/2556] [ARM] mfd: pm8058: need to set the perm bits to have access to some irqs Change-Id: I88c557425b0b134bd78ee4e7611803f6567b6959 --- drivers/mfd/pm8058-core.c | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/drivers/mfd/pm8058-core.c b/drivers/mfd/pm8058-core.c index 3bbde02f17cb8..fbc2a5aedc4ce 100644 --- a/drivers/mfd/pm8058-core.c +++ b/drivers/mfd/pm8058-core.c @@ -37,6 +37,8 @@ module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); #define REG_HWREV 0x0002 /* PMIC4 revision */ +#define REG_IRQ_PERM 0x01a6 +#define REG_IRQ_PERM_BLK_SEL 0x01ac #define REG_IRQ_ROOT 0x01bb #define REG_IRQ_M_STATUS1 0x01bc #define REG_IRQ_M_STATUS2 0x01bd @@ -409,6 +411,35 @@ static int read_irq_block_reg(struct pm8058 *pmic, u8 blk, u16 reg, u8 *val) return ret; } +static int cfg_irq_blk_bit_perm(struct pm8058 *pmic, u8 blk, u8 mask) +{ + int ret; + unsigned long flags; + u8 tmp; + + spin_lock_irqsave(&pmic->lock, flags); + ret = pm8058_writeb(pmic->dev, REG_IRQ_PERM_BLK_SEL, blk); + if (ret) { + pr_err("%s: error setting block select (%d)\n", __func__, ret); + goto done; + } + + ret = pm8058_readb(pmic->dev, REG_IRQ_PERM, &tmp); + if (ret) { + pr_err("%s: error getting (%d)\n", __func__, ret); + goto done; + } + + ret = pm8058_writeb(pmic->dev, REG_IRQ_PERM, tmp | mask); + if (ret) + pr_err("%s: error writing %d 0x%x 0x%x (0x%x)\n", __func__, + ret, blk, REG_IRQ_PERM, mask); + +done: + spin_unlock_irqrestore(&pmic->lock, flags); + return ret; +} + static int _write_irq_blk_bit_cfg(struct pm8058 *pmic, u8 blk, u8 bit, u8 cfg) { int ret; @@ -665,11 +696,21 @@ static int pm8058_irq_init(struct pm8058 *pmic, unsigned int irq_base) BUG_ON(pmic->irqs[irq].blk >= NUM_BLOCKS); + /* XXX: slightly inefficient since we can end up + * doing it 8 times per block per bank, but it's + * the easiet. Optimize if gets too slow. */ + + /* ensure we set the permissions for the irqs in + * this bank */ + cfg_irq_blk_bit_perm(pmic, pmic->irqs[irq].blk, + 1 << pmic->irqs[irq].blk_bit); + set_irq_chip(irq_base + irq, &pm8058_irq_chip); set_irq_chip_data(irq_base + irq, pmic); set_irq_handler(irq_base + irq, handle_edge_irq); set_irq_flags(irq_base + irq, IRQF_VALID); } + } return 0; From a42a4ba106e578be6c64a87c825d6cc85fa64144 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 11 May 2010 19:57:42 -0700 Subject: [PATCH 0645/2556] [ARM] video: msm: move mddi interface registration to H/W specific files Change-Id: I9b6aa83412b4d9a979310236fbe26f27f261d00f Signed-off-by: Dima Zavin --- drivers/video/msm/mdp.c | 66 ------------------------------- drivers/video/msm/mdp_hw40.c | 41 +++++++++++++++++++ drivers/video/msm/mdp_hw_legacy.c | 66 +++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 66 deletions(-) diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 02bb422b503c3..9f32396aa689a 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -235,66 +235,6 @@ static void mdp_dma_wait(struct mdp_device *mdp_dev, int interface) } } -static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, - uint32_t width, uint32_t height, uint32_t x, - uint32_t y) -{ - struct mdp_info *mdp = priv; - uint32_t dma2_cfg; - uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */ - - dma2_cfg = DMA_PACK_TIGHT | - DMA_PACK_ALIGN_LSB | - DMA_OUT_SEL_AHB | - DMA_IBUF_NONCONTIGUOUS; - - dma2_cfg |= mdp->dma_format; - dma2_cfg |= mdp->dma_pack_pattern; - - dma2_cfg |= DMA_OUT_SEL_MDDI; - - dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY; - - dma2_cfg |= DMA_DITHER_EN; - - /* 666 18BPP */ - dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; - -#ifdef CONFIG_MSM_MDP22 - /* setup size, address, and stride */ - mdp_writel(mdp, (height << 16) | (width), - MDP_CMD_DEBUG_ACCESS_BASE + 0x0184); - mdp_writel(mdp, addr, MDP_CMD_DEBUG_ACCESS_BASE + 0x0188); - mdp_writel(mdp, stride, MDP_CMD_DEBUG_ACCESS_BASE + 0x018C); - - /* set y & x offset and MDDI transaction parameters */ - mdp_writel(mdp, (y << 16) | (x), MDP_CMD_DEBUG_ACCESS_BASE + 0x0194); - mdp_writel(mdp, ld_param, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0); - mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM, - MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4); - - mdp_writel(mdp, dma2_cfg, MDP_CMD_DEBUG_ACCESS_BASE + 0x0180); - - /* start DMA2 */ - mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0044); -#else - /* setup size, address, and stride */ - mdp_writel(mdp, (height << 16) | (width), MDP_DMA_P_SIZE); - mdp_writel(mdp, addr, MDP_DMA_P_IBUF_ADDR); - mdp_writel(mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE); - - /* set y & x offset and MDDI transaction parameters */ - mdp_writel(mdp, (y << 16) | (x), MDP_DMA_P_OUT_XY); - mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL); - mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM, - MDP_MDDI_PARAM); - - mdp_writel(mdp, 0x1, MDP_MDDI_DATA_XFR); - mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG); - mdp_writel(mdp, 0, MDP_DMA_P_START); -#endif -} - static void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride, uint32_t width, uint32_t height, uint32_t x, uint32_t y, struct msmfb_callback *callback, int interface) @@ -530,11 +470,6 @@ int mdp_probe(struct platform_device *pdev) mdp->enable_irq = enable_mdp_irq; mdp->disable_irq = disable_mdp_irq; - ret = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp, - MDP_DMA_P_DONE, mdp_dma_to_mddi); - if (ret) - goto error_mddi_pmdh_register; - mdp->clk = clk_get(&pdev->dev, "mdp_clk"); if (IS_ERR(mdp->clk)) { printk(KERN_INFO "mdp: failed to get mdp clk"); @@ -595,7 +530,6 @@ int mdp_probe(struct platform_device *pdev) clk_put(mdp->pclk); clk_put(mdp->clk); error_get_mdp_clk: -error_mddi_pmdh_register: iounmap(mdp->base); error_ioremap: error_get_irq: diff --git a/drivers/video/msm/mdp_hw40.c b/drivers/video/msm/mdp_hw40.c index 035160638347b..a642c9bcf7188 100644 --- a/drivers/video/msm/mdp_hw40.c +++ b/drivers/video/msm/mdp_hw40.c @@ -20,8 +20,49 @@ #include "mdp_hw.h" +static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, + uint32_t width, uint32_t height, uint32_t x, + uint32_t y) +{ + struct mdp_info *mdp = priv; + uint32_t dma2_cfg; + uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */ + + dma2_cfg = DMA_PACK_TIGHT | + DMA_PACK_ALIGN_LSB; + + dma2_cfg |= mdp->dma_format; + dma2_cfg |= mdp->dma_pack_pattern; + dma2_cfg |= DMA_DITHER_EN; + + /* 666 18BPP */ + dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + + /* setup size, address, and stride */ + mdp_writel(mdp, (height << 16) | (width), MDP_DMA_P_SIZE); + mdp_writel(mdp, addr, MDP_DMA_P_IBUF_ADDR); + mdp_writel(mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE); + + /* set y & x offset and MDDI transaction parameters */ + mdp_writel(mdp, (y << 16) | (x), MDP_DMA_P_OUT_XY); + mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL); + mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM, + MDP_MDDI_PARAM); + + mdp_writel(mdp, 0x1, MDP_MDDI_DATA_XFR); + mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG); + mdp_writel(mdp, 0, MDP_DMA_P_START); +} + int mdp_hw_init(struct mdp_info *mdp) { + int ret; + + ret = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp, + MDP_DMA_P_DONE, mdp_dma_to_mddi); + if (ret) + return ret; + mdp_writel(mdp, 0, MDP_INTR_ENABLE); mdp_writel(mdp, 0, MDP_DMA_P_HIST_INTR_ENABLE); diff --git a/drivers/video/msm/mdp_hw_legacy.c b/drivers/video/msm/mdp_hw_legacy.c index 446118887ae74..cf9eb1a51ccca 100644 --- a/drivers/video/msm/mdp_hw_legacy.c +++ b/drivers/video/msm/mdp_hw_legacy.c @@ -21,10 +21,76 @@ #define MDP_CMD_DEBUG_ACCESS_BASE (0x10000) +static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, + uint32_t width, uint32_t height, uint32_t x, + uint32_t y) +{ + struct mdp_info *mdp = priv; + uint32_t dma2_cfg; + uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */ + + dma2_cfg = DMA_PACK_TIGHT | + DMA_PACK_ALIGN_LSB | + DMA_OUT_SEL_AHB | + DMA_IBUF_NONCONTIGUOUS; + + dma2_cfg |= mdp->dma_format; + dma2_cfg |= mdp->dma_pack_pattern; + + dma2_cfg |= DMA_OUT_SEL_MDDI; + + dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY; + + dma2_cfg |= DMA_DITHER_EN; + + /* 666 18BPP */ + dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + +#ifdef CONFIG_MSM_MDP22 + /* setup size, address, and stride */ + mdp_writel(mdp, (height << 16) | (width), + MDP_CMD_DEBUG_ACCESS_BASE + 0x0184); + mdp_writel(mdp, addr, MDP_CMD_DEBUG_ACCESS_BASE + 0x0188); + mdp_writel(mdp, stride, MDP_CMD_DEBUG_ACCESS_BASE + 0x018C); + + /* set y & x offset and MDDI transaction parameters */ + mdp_writel(mdp, (y << 16) | (x), MDP_CMD_DEBUG_ACCESS_BASE + 0x0194); + mdp_writel(mdp, ld_param, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0); + mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM, + MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4); + + mdp_writel(mdp, dma2_cfg, MDP_CMD_DEBUG_ACCESS_BASE + 0x0180); + + /* start DMA2 */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0044); +#else + /* setup size, address, and stride */ + mdp_writel(mdp, (height << 16) | (width), MDP_DMA_P_SIZE); + mdp_writel(mdp, addr, MDP_DMA_P_IBUF_ADDR); + mdp_writel(mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE); + + /* set y & x offset and MDDI transaction parameters */ + mdp_writel(mdp, (y << 16) | (x), MDP_DMA_P_OUT_XY); + mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL); + mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM, + MDP_MDDI_PARAM); + + mdp_writel(mdp, 0x1, MDP_MDDI_DATA_XFR); + mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG); + mdp_writel(mdp, 0, MDP_DMA_P_START); +#endif +} + + int mdp_hw_init(struct mdp_info *mdp) { int n; + n = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp, + MDP_DMA_P_DONE, mdp_dma_to_mddi); + if (n) + return n; + mdp_writel(mdp, 0, MDP_INTR_ENABLE); /* debug interface write access */ From 70f03ea69f2867ecab055e08503e90b6645f38a3 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 13 May 2010 10:27:36 -0700 Subject: [PATCH 0646/2556] [ARM] video: msm: kgsl: enable the driver for msm7x30 devices Change-Id: I804fe8e98c89f53b6d4fc1f828ce998e61a725ed Signed-off-by: Dima Zavin --- drivers/video/msm/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig index beb0c23e8bdfe..938065a5a9c4e 100644 --- a/drivers/video/msm/Kconfig +++ b/drivers/video/msm/Kconfig @@ -22,9 +22,9 @@ config FB_MSM_LCDC default y config GPU_MSM_KGSL - tristate "MSM 3D Graphics driver for QSD8x50 and MSM7x27" + tristate "MSM 3D Graphics driver for Adreno class GPUs" default n - depends on FB_MSM && ARCH_QSD8X50 + depends on FB_MSM && (ARCH_QSD8X50 || ARCH_MSM7X30) select GENERIC_ALLOCATOR select CONFIG_FW_LOADER help From 2724e84349d09631bb54681a2a2428cb14e7e8b7 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 13 May 2010 10:36:14 -0700 Subject: [PATCH 0647/2556] [ARM] video: msm: kgsl: manage the grp pclk, if present Change-Id: I950437ed8169897613651580d111c7a0c9cad638 Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl.c | 11 +++++++++++ drivers/video/msm/gpu/kgsl/kgsl.h | 1 + 2 files changed, 12 insertions(+) diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c index a2044251cbfab..031e2285e7f71 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.c +++ b/drivers/video/msm/gpu/kgsl/kgsl.c @@ -118,12 +118,16 @@ static void kgsl_clk_enable(void) { clk_set_rate(kgsl_driver.ebi1_clk, 128000000); clk_enable(kgsl_driver.imem_clk); + if (kgsl_driver.grp_pclk) + clk_enable(kgsl_driver.grp_pclk); clk_enable(kgsl_driver.grp_clk); } static void kgsl_clk_disable(void) { clk_disable(kgsl_driver.grp_clk); + if (kgsl_driver.grp_pclk) + clk_disable(kgsl_driver.grp_pclk); clk_disable(kgsl_driver.imem_clk); clk_set_rate(kgsl_driver.ebi1_clk, 0); } @@ -1152,6 +1156,13 @@ static int __devinit kgsl_platform_probe(struct platform_device *pdev) } kgsl_driver.grp_clk = clk; + clk = clk_get(&pdev->dev, "grp_pclk"); + if (IS_ERR(clk)) { + KGSL_DRV_ERR("no grp_pclk, continuing\n"); + clk = NULL; + } + kgsl_driver.grp_pclk = clk; + clk = clk_get(&pdev->dev, "imem_clk"); if (IS_ERR(clk)) { result = PTR_ERR(clk); diff --git a/drivers/video/msm/gpu/kgsl/kgsl.h b/drivers/video/msm/gpu/kgsl/kgsl.h index c445f75d33b82..e9f0d7ccb0679 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.h +++ b/drivers/video/msm/gpu/kgsl/kgsl.h @@ -45,6 +45,7 @@ struct kgsl_driver { int have_irq; struct clk *grp_clk; + struct clk *grp_pclk; struct clk *imem_clk; struct clk *ebi1_clk; From 49aba3e8375a6cda88677593d12c2043e5ee112c Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 17 May 2010 18:33:12 -0700 Subject: [PATCH 0648/2556] [ARM] video: msm: mddi: enable clocks before touching registers on resume Change-Id: I68772adde4d5c4daace9339bfa6fbc4cb558ecad Signed-off-by: Dima Zavin --- drivers/video/msm/mddi.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c index 671722ca6e9f3..47000a53a63ea 100644 --- a/drivers/video/msm/mddi.c +++ b/drivers/video/msm/mddi.c @@ -470,14 +470,13 @@ static void mddi_resume(struct msm_mddi_client_data *cdata) struct mddi_info *mddi = container_of(cdata, struct mddi_info, client_data); wake_lock(&mddi->idle_lock); + clk_enable(mddi->clk); + if (mddi->pclk) + clk_enable(mddi->pclk); mddi_set_auto_hibernate(&mddi->client_data, 0); /* turn on the client */ if (mddi->power_client) mddi->power_client(&mddi->client_data, 1); - /* turn on the clock */ - clk_enable(mddi->clk); - if (mddi->pclk) - clk_enable(mddi->pclk); /* set up the local registers */ mddi->rev_data_curr = 0; mddi_init_registers(mddi); From d486a246b20dfaa3efdcbe7f4d6cecd636874a9d Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 17 May 2010 20:55:55 -0700 Subject: [PATCH 0649/2556] [ARM] video: msm: drop mddi clock request and power down pad drivers on suspend Change-Id: Ib02178f708624fab62e6b9a1a5fb8281f6903f5f Signed-off-by: Dima Zavin --- drivers/video/msm/mddi.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c index 47000a53a63ea..4f136dfed8911 100644 --- a/drivers/video/msm/mddi.c +++ b/drivers/video/msm/mddi.c @@ -68,6 +68,7 @@ struct mddi_info { int irq; struct clk *clk; struct clk *pclk; + unsigned long clk_rate; struct msm_mddi_client_data client_data; /* buffer for rev encap packets */ @@ -96,6 +97,9 @@ struct mddi_info { void (*power_client)(struct msm_mddi_client_data *, int); + /* used to save/restore pad config during suspend */ + uint32_t pad_ctrl; + /* client device published to bind us to the * appropriate mddi_client driver */ @@ -458,6 +462,11 @@ static void mddi_suspend(struct msm_mddi_client_data *cdata) /* turn off the link */ mddi_writel(MDDI_CMD_RESET, CMD); mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + /* save pad ctrl and power down the drivers */ + mddi->pad_ctrl = mddi_readl(PAD_CTL); + mddi_writel(0, PAD_CTL); + /* release rate request to not hold any high speed plls */ + clk_set_rate(mddi->clk, 0); /* turn off the clock */ if (mddi->pclk) clk_disable(mddi->pclk); @@ -473,6 +482,8 @@ static void mddi_resume(struct msm_mddi_client_data *cdata) clk_enable(mddi->clk); if (mddi->pclk) clk_enable(mddi->pclk); + clk_set_rate(mddi->clk, mddi->clk_rate); + mddi_writel(mddi->pad_ctrl, PAD_CTL); mddi_set_auto_hibernate(&mddi->client_data, 0); /* turn on the client */ if (mddi->power_client) @@ -738,7 +749,8 @@ static int __init mddi_clk_setup(struct platform_device *pdev, if (mddi->pclk) clk_enable(mddi->pclk); - ret = clk_set_rate(mddi->clk, clk_rate); + mddi->clk_rate = clk_rate; + ret = clk_set_rate(mddi->clk, mddi->clk_rate); if (ret) goto fail; return 0; From 2b7b1e350a1e489a760c1db5300c8d762526b0c1 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 20 May 2010 18:22:22 -0700 Subject: [PATCH 0650/2556] mfd: pm8058: set the correct irq handler when changing trigger type Change-Id: If9851e02eb629323137fd8c9d1743cf3e120de76 Signed-off-by: Dima Zavin --- drivers/mfd/pm8058-core.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/mfd/pm8058-core.c b/drivers/mfd/pm8058-core.c index fbc2a5aedc4ce..4ccc20312073f 100644 --- a/drivers/mfd/pm8058-core.c +++ b/drivers/mfd/pm8058-core.c @@ -601,25 +601,28 @@ static int pm8058_irq_set_type(unsigned int _irq, unsigned int flow_type) struct pm8058_irq_info *irq_info = &pmic->irqs[irq]; unsigned long flags; int ret; + u8 cfg; - spin_lock_irqsave(&pmic->lock, flags); - irq_info->cfg = IRQ_CFG_MASK_RE | IRQ_CFG_MASK_FE; - + cfg = IRQ_CFG_MASK_RE | IRQ_CFG_MASK_FE; if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { if (flow_type & IRQF_TRIGGER_RISING) - irq_info->cfg &= ~IRQ_CFG_MASK_RE; + cfg &= ~IRQ_CFG_MASK_RE; if (flow_type & IRQF_TRIGGER_FALLING) - irq_info->cfg &= ~IRQ_CFG_MASK_FE; + cfg &= ~IRQ_CFG_MASK_FE; + __set_irq_handler_unlocked(_irq, handle_edge_irq); } else { - irq_info->cfg |= IRQ_CFG_LVL_SEL; + cfg |= IRQ_CFG_LVL_SEL; if (flow_type & IRQF_TRIGGER_HIGH) - irq_info->cfg &= ~IRQ_CFG_MASK_RE; + cfg &= ~IRQ_CFG_MASK_RE; else - irq_info->cfg &= ~IRQ_CFG_MASK_FE; + cfg &= ~IRQ_CFG_MASK_FE; + __set_irq_handler_unlocked(_irq, handle_level_irq); } /* in case the irq was masked when the type was set, we don't want * to unmask it */ + spin_lock_irqsave(&pmic->lock, flags); + irq_info->cfg = cfg; irq_info->cfg_val = irq_info->cfg | irq_info->mask; ret = write_irq_config_locked(pmic, irq, irq_info->cfg_val | IRQ_CFG_CLR); From f34523fda74bdef212e13f9357f197d53690ca65 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 21 May 2010 15:19:13 -0700 Subject: [PATCH 0651/2556] mfd: pm8058: make the main irq dispatch be a chained handler Change-Id: I4fd6829d6d8becf0fede9b6a8fba0a423553b148 Signed-off-by: Dima Zavin --- drivers/mfd/pm8058-core.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/drivers/mfd/pm8058-core.c b/drivers/mfd/pm8058-core.c index 4ccc20312073f..102488fbf4ab9 100644 --- a/drivers/mfd/pm8058-core.c +++ b/drivers/mfd/pm8058-core.c @@ -472,7 +472,6 @@ static int do_irq_master(struct pm8058 *pmic, int group) int j; int ret; u8 val; - unsigned long flags; unsigned long stat; ret = pm8058_readb(pmic->dev, pm8058_irq_groups[group].stat_reg, &val); @@ -503,10 +502,7 @@ static int do_irq_master(struct pm8058 *pmic, int group) pr_warning("Unexpected pmirq %d\n", irq); continue; } - - local_irq_save(flags); generic_handle_irq(pmic->pmirqs[irq] + pmic->irq_base); - local_irq_restore(flags); } } @@ -514,17 +510,18 @@ static int do_irq_master(struct pm8058 *pmic, int group) return ret; } -static irqreturn_t pm8058_irq_handler(int irq, void *dev) +static void pm8058_irq_handler(unsigned int irq, struct irq_desc *desc) { - struct pm8058 *pmic = dev; + struct pm8058 *pmic = get_irq_data(irq); int ret; int i; u8 root; + desc->chip->ack(irq); ret = pm8058_readb(pmic->dev, REG_IRQ_ROOT, &root); if (ret) { pr_err("%s: Can't read root status\n", __func__); - goto done; + return; } if (debug_mask & DEBUG_IRQS) @@ -533,9 +530,6 @@ static irqreturn_t pm8058_irq_handler(int irq, void *dev) if (root & pm8058_irq_groups[i].root_mask) do_irq_master(pmic, i); } - -done: - return IRQ_HANDLED; } static void pm8058_irq_ack(unsigned int _irq) @@ -817,12 +811,10 @@ static int pm8058_probe(struct platform_device *pdev) goto err_gpiochip_add; } - ret = request_irq(devirq, pm8058_irq_handler, IRQF_TRIGGER_LOW, - "pm8058-irq", pmic); - if (ret) { - pr_err("%s: can't request device irq\n", __func__); - goto err_request_irq; - } + set_irq_type(devirq, IRQ_TYPE_LEVEL_LOW); + set_irq_data(devirq, pmic); + set_irq_chained_handler(devirq, pm8058_irq_handler); + set_irq_wake(devirq, 1); the_pm8058 = pmic; @@ -847,8 +839,8 @@ static int pm8058_probe(struct platform_device *pdev) err_add_kp_dev: err_pdata_init: the_pm8058 = NULL; - free_irq(devirq, pmic); -err_request_irq: + set_irq_wake(devirq, 0); + set_irq_chained_handler(devirq, NULL); WARN_ON(gpiochip_remove(&pmic->gpio_chip)); err_gpiochip_add: err_irq_init: From 6afe150f504f5ef18276eb3acb9dbbd5ae9514c1 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 20 May 2010 21:35:41 -0700 Subject: [PATCH 0652/2556] mfd: pm8058: support/expose the charger_valid irq Change-Id: Id50475acdd54c6eef85163ae2d45f455916fdbcc Signed-off-by: Dima Zavin --- drivers/mfd/pm8058-core.c | 13 +++++++++++++ include/linux/mfd/pm8058.h | 7 ++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/pm8058-core.c b/drivers/mfd/pm8058-core.c index 102488fbf4ab9..dab970b60b889 100644 --- a/drivers/mfd/pm8058-core.c +++ b/drivers/mfd/pm8058-core.c @@ -64,6 +64,7 @@ module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); #define MPP_IRQ_OFFSET (16 * 8) #define GPIO_IRQ_OFFSET (24 * 8) #define KEYPAD_IRQ_OFFSET (9 * 8 + 2) +#define CHARGER_IRQ_OFFSET (1 * 8 + 7) /* this defines banks of irq space. We want to provide a compact irq space * to the kernel, but there several ranges of irqs in an otherwise sparse @@ -72,6 +73,7 @@ module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); * bank 0 - GPIO IRQs start=(24 * 8) cnt=40 (gpios 0-39) * bank 1 - MPP IRQs start=(16 * 8) cnt=12 (mpps 0-11) * bank 2 - keypad irqs start=(9*8 + 1) cnt=2 + * bank 3 - charger irqs start=(1*8 + 7) cnt=1 * */ struct pm8058_irq_bank { @@ -96,6 +98,11 @@ static struct pm8058_irq_bank pm8058_irq_banks[] = { .cnt = PM8058_NUM_KEYPAD_IRQS, .offset = KEYPAD_IRQ_OFFSET, }, + { + .start = PM8058_FIRST_CHARGER_IRQ, + .cnt = PM8058_NUM_CHARGER_IRQS, + .offset = CHARGER_IRQ_OFFSET, + }, }; #define NUM_IRQ_BANKS ARRAY_SIZE(pm8058_irq_banks) @@ -107,6 +114,12 @@ struct pm8058_irq_group { }; static const struct pm8058_irq_group pm8058_irq_groups[] = { + { + .stat_reg = REG_IRQ_M_STATUS1, + .valid_mask = 0x2, + .root_mask = 0x2, + .block_offset = 0, + }, { .stat_reg = REG_IRQ_M_STATUS2, .valid_mask = 0x2, diff --git a/include/linux/mfd/pm8058.h b/include/linux/mfd/pm8058.h index bcb3e61d1570f..4272aa6207e9d 100644 --- a/include/linux/mfd/pm8058.h +++ b/include/linux/mfd/pm8058.h @@ -22,9 +22,11 @@ #define PM8058_NUM_GPIO_IRQS 40 #define PM8058_NUM_MPP_IRQS 12 #define PM8058_NUM_KEYPAD_IRQS 2 +#define PM8058_NUM_CHARGER_IRQS 1 #define PM8058_NUM_IRQS (PM8058_NUM_GPIO_IRQS + \ PM8058_NUM_MPP_IRQS + \ - PM8058_NUM_KEYPAD_IRQS) + PM8058_NUM_KEYPAD_IRQS + \ + PM8058_NUM_CHARGER_IRQS) /* be careful if you change this since this is used to map irq <-> gpio */ #define PM8058_FIRST_GPIO_IRQ 0 @@ -32,9 +34,12 @@ PM8058_NUM_GPIO_IRQS) #define PM8058_FIRST_KEYPAD_IRQ (PM8058_FIRST_MPP_IRQ + \ PM8058_NUM_MPP_IRQS) +#define PM8058_FIRST_CHARGER_IRQ (PM8058_FIRST_KEYPAD_IRQ + \ + PM8058_NUM_KEYPAD_IRQS) #define PM8058_KEYPAD_IRQ (PM8058_FIRST_KEYPAD_IRQ + 0) #define PM8058_KEYPAD_STUCK_IRQ (PM8058_FIRST_KEYPAD_IRQ + 1) +#define PM8058_CHGVAL_IRQ (PM8058_FIRST_CHARGER_IRQ + 0) #define PM8058_GPIO_TO_IRQ(base,gpio) (PM8058_FIRST_GPIO_IRQ + \ (base) + (gpio)) From 3faaff28201f4f0f2e7408a995855404a77a86a5 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 20 May 2010 22:03:49 -0700 Subject: [PATCH 0653/2556] mfd: pm8058: add helper function for registering child devices Change-Id: I4e56ad25d6756e25bf31b37b9d769dcc217f4dee Signed-off-by: Dima Zavin --- drivers/mfd/pm8058-core.c | 63 ++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/drivers/mfd/pm8058-core.c b/drivers/mfd/pm8058-core.c index dab970b60b889..8fee08b19813d 100644 --- a/drivers/mfd/pm8058-core.c +++ b/drivers/mfd/pm8058-core.c @@ -157,6 +157,8 @@ struct pm8058 { struct gpio_chip gpio_chip; u8 gpio_flags[PM8058_NUM_GPIOS]; + + struct platform_device *kp_pdev; }; static struct pm8058 *the_pm8058; @@ -726,49 +728,68 @@ static int pm8058_irq_init(struct pm8058 *pmic, unsigned int irq_base) return 0; } -static int add_keypad_device(struct pm8058 *pmic, void *pdata) +static struct platform_device *add_child_device( + struct pm8058 *pmic, const char *name, void *pdata, + struct resource *res, int num_res) { struct platform_device *pdev; - struct resource irq_res[2]; int ret; - pdev = platform_device_alloc("pm8058-keypad", -1); + pdev = platform_device_alloc(name, -1); if (!pdev) { - pr_err("%s: cannot allocate pdev for keypad\n", __func__); - return -ENOMEM; + pr_err("%s: cannot allocate pdev for '%s'\n", __func__, name); + ret = -ENOMEM; + goto err; } pdev->dev.parent = pmic->dev; pdev->dev.platform_data = pdata; - memset(&irq_res, 0, sizeof(irq_res)); - irq_res[0].start = pmic->irq_base + PM8058_KEYPAD_IRQ; - irq_res[0].end = irq_res[0].start; - irq_res[0].flags = IORESOURCE_IRQ; - irq_res[0].name = "kp_sense"; - irq_res[1].start = pmic->irq_base + PM8058_KEYPAD_STUCK_IRQ; - irq_res[1].end = irq_res[1].start; - irq_res[1].flags = IORESOURCE_IRQ; - irq_res[1].name = "kp_stuck"; - ret = platform_device_add_resources(pdev, irq_res, ARRAY_SIZE(irq_res)); + ret = platform_device_add_resources(pdev, res, num_res); if (ret) { - pr_err("%s: can't add irq resources for keypad'\n", __func__); + pr_err("%s: can't add resources for '%s'\n", __func__, name); goto err; } ret = platform_device_add(pdev); if (ret) { - pr_err("%s: cannot add child keypad platform device for\n", - __func__); + pr_err("%s: cannot add child platform device '%s'\n", __func__, + name); goto err; } - - return 0; + return pdev; err: if (pdev) platform_device_put(pdev); - return ret; + return ERR_PTR(ret); +} + +static int add_keypad_device(struct pm8058 *pmic, void *pdata) +{ + struct platform_device *pdev; + struct resource irq_res[] = { + { + .start = pmic->irq_base + PM8058_KEYPAD_IRQ, + .end = pmic->irq_base + PM8058_KEYPAD_IRQ, + .flags = IORESOURCE_IRQ, + .name = "kp_sense", + }, + { + .start = pmic->irq_base + PM8058_KEYPAD_STUCK_IRQ, + .end = pmic->irq_base + PM8058_KEYPAD_STUCK_IRQ, + .flags = IORESOURCE_IRQ, + .name = "kp_stuck", + } + }; + + pdev = add_child_device(pmic, "pm8058-keypad", pdata, irq_res, + ARRAY_SIZE(irq_res)); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + pmic->kp_pdev = pdev; + return 0; } static int pm8058_probe(struct platform_device *pdev) From 02651dd7b58bf97b07f8abd8dc7231260e6e55dc Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 21 May 2010 17:20:20 -0700 Subject: [PATCH 0654/2556] mfd: pm8058: add vbus detection support Change-Id: I94001ebc95f497b4fc75e177470c096646dd9dcb Signed-off-by: Dima Zavin --- drivers/mfd/pm8058-core.c | 74 +++++++++++++++++++++++++++++++------- include/linux/mfd/pm8058.h | 3 ++ 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/drivers/mfd/pm8058-core.c b/drivers/mfd/pm8058-core.c index 8fee08b19813d..066abba6b920b 100644 --- a/drivers/mfd/pm8058-core.c +++ b/drivers/mfd/pm8058-core.c @@ -158,12 +158,15 @@ struct pm8058 { struct gpio_chip gpio_chip; u8 gpio_flags[PM8058_NUM_GPIOS]; + struct pm8058_platform_data *pdata; + struct platform_device *kp_pdev; }; static struct pm8058 *the_pm8058; static int read_irq_block_reg(struct pm8058 *pmic, u8 blk, u16 reg, u8 *val); +static int get_curr_irq_stat(struct pm8058 *pmic, unsigned int irq); int pm8058_readb(struct device *dev, u16 addr, u8 *val) { @@ -369,21 +372,9 @@ static void pm8058_gpio_set(struct gpio_chip *chip, unsigned gpio, int val) static int pm8058_gpio_get(struct gpio_chip *chip, unsigned gpio) { struct pm8058 *pmic = container_of(chip, struct pm8058, gpio_chip); - int ret; - u8 val; /* XXX: assumes gpio maps 1:1 to irq @ 0 */ - ret = read_irq_block_reg(pmic, pmic->irqs[gpio].blk, REG_IRQ_RT_STATUS, - &val); - if (ret) { - pr_err("%s: can't read block status\n", __func__); - goto done; - } - - ret = !!(val & (1 << pmic->irqs[gpio].blk_bit)); - -done: - return ret; + return get_curr_irq_stat(pmic, gpio); } static int pm8058_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) @@ -426,6 +417,24 @@ static int read_irq_block_reg(struct pm8058 *pmic, u8 blk, u16 reg, u8 *val) return ret; } +static int get_curr_irq_stat(struct pm8058 *pmic, unsigned int irq) +{ + int ret; + u8 val; + + ret = read_irq_block_reg(pmic, pmic->irqs[irq].blk, REG_IRQ_RT_STATUS, + &val); + if (ret) { + pr_err("%s: can't read irq %d status\n", __func__, irq); + goto done; + } + + ret = !!(val & (1 << pmic->irqs[irq].blk_bit)); + +done: + return ret; +} + static int cfg_irq_blk_bit_perm(struct pm8058 *pmic, u8 blk, u8 mask) { int ret; @@ -792,6 +801,27 @@ static int add_keypad_device(struct pm8058 *pmic, void *pdata) return 0; } +/* vbus detection helper */ +static void check_vbus(struct pm8058 *pmic) +{ + int ret; + + ret = get_curr_irq_stat(pmic, PM8058_CHGVAL_IRQ); + if (ret >= 0) + pmic->pdata->vbus_present(ret); + else + pr_err("%s: can't read status!! ignoring event?!\n", __func__); + /* XXX: maybe add some retries? */ +} + +static irqreturn_t pm8058_vbus_irq_handler(int irq, void *dev) +{ + struct pm8058 *pmic = dev; + + check_vbus(pmic); + return IRQ_HANDLED; +} + static int pm8058_probe(struct platform_device *pdev) { struct pm8058_platform_data *pdata = pdev->dev.platform_data; @@ -827,6 +857,7 @@ static int pm8058_probe(struct platform_device *pdev) pmic->irq_base = pdata->irq_base; pmic->devirq = devirq; spin_lock_init(&pmic->lock); + pmic->pdata = pdata; platform_set_drvdata(pdev, pmic); ret = pm8058_irq_init(pmic, pmic->irq_base); @@ -868,8 +899,25 @@ static int pm8058_probe(struct platform_device *pdev) } } + if (pdata->vbus_present) { + int vbus_irq = pmic->irq_base + PM8058_CHGVAL_IRQ; + ret = request_threaded_irq(vbus_irq, NULL, + pm8058_vbus_irq_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "pm8058-vbus", pmic); + if (ret) { + pr_err("%s: can't request vbus irq\n", __func__); + goto err_req_vbus_irq; + } + set_irq_wake(vbus_irq, 1); + /* run once to handle the case where vbus is already present */ + check_vbus(pmic); + } return 0; +err_req_vbus_irq: + if (pmic->kp_pdev) + platform_device_put(pmic->kp_pdev); err_add_kp_dev: err_pdata_init: the_pm8058 = NULL; diff --git a/include/linux/mfd/pm8058.h b/include/linux/mfd/pm8058.h index 4272aa6207e9d..85b119e0d30e5 100644 --- a/include/linux/mfd/pm8058.h +++ b/include/linux/mfd/pm8058.h @@ -76,6 +76,9 @@ struct pm8058_platform_data { unsigned int gpio_base; int (*init)(struct device *dev); + /* function to call on vbus detect */ + void (*vbus_present)(bool present); + /* child devices */ struct pm8058_keypad_platform_data *keypad_pdata; }; From 861671c3bd7eb5dccfc3980842e3e8e6318cc07f Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 24 May 2010 14:20:19 -0700 Subject: [PATCH 0655/2556] [ARM] msm: correctly print the debug info for all the irq banks Change-Id: I2f8d5f291197ec9d4da42a3e0213d96f99a0b09f Signed-off-by: Dima Zavin --- arch/arm/mach-msm/irq.c | 91 ++++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 32 deletions(-) diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c index 98f060d17bdff..40e2dd5ff8c8d 100644 --- a/arch/arm/mach-msm/irq.c +++ b/arch/arm/mach-msm/irq.c @@ -285,13 +285,32 @@ int msm_irq_pending(void) return 0; } +static void print_vic_irq_stat(void) +{ + int i; + + for (i = 0; i < VIC_NUM_BANKS; i++) + printk(" %x", readl(VIC_IRQ_STATUS(i))); + printk("\n"); +} + +static void print_irq_array(uint32_t *arr, int cnt) +{ + int i; + + for (i = 0; i < cnt; i++) + printk(" %x", arr[i]); + printk("\n"); +} + int msm_irq_idle_sleep_allowed(void) { int i; - if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_REQUEST) - printk(KERN_INFO "msm_irq_idle_sleep_allowed: disable %x %x\n", - msm_irq_idle_disable[0], msm_irq_idle_disable[1]); + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_REQUEST) { + printk(KERN_INFO "%s: disable", __func__); + print_irq_array(msm_irq_idle_disable, VIC_NUM_BANKS); + } for (i = 0; i < VIC_NUM_BANKS; ++i) if (msm_irq_idle_disable[i]) @@ -323,9 +342,11 @@ int msm_irq_enter_sleep2(bool arm9_wake, int from_idle) /* edge triggered interrupt may get lost if this mode is used */ WARN_ON_ONCE(!arm9_wake && !from_idle); - if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) - printk(KERN_INFO "msm_irq_enter_sleep change irq, pend %x %x\n", - readl(VIC_IRQ_STATUS(0)), readl(VIC_IRQ_STATUS(1))); + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) { + printk(KERN_INFO "%s: change irq, pend", __func__); + print_vic_irq_stat(); + } + pending[0] = readl(VIC_IRQ_STATUS(0)); pending[0] &= msm_irq_shadow_reg[0].int_en[!from_idle]; /* Clear INT_A9_M2A_5 since requesting sleep triggers it */ @@ -339,9 +360,10 @@ int msm_irq_enter_sleep2(bool arm9_wake, int from_idle) } if (any) { - if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_ABORT) - printk(KERN_INFO "msm_irq_enter_sleep2 abort %x %x\n", - pending[0], pending[1]); + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_ABORT) { + printk(KERN_INFO "%s abort", __func__); + print_irq_array(pending, VIC_NUM_BANKS); + } return -EAGAIN; } @@ -388,12 +410,13 @@ void msm_irq_exit_sleep1(void) printk(KERN_ERR "msm_irq_exit_sleep \n"); return; } - if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) - printk(KERN_INFO "msm_irq_exit_sleep1 %x %x %x now %x %x\n", - smsm_int_info->interrupt_mask, - smsm_int_info->pending_interrupts, - smsm_int_info->wakeup_reason, - readl(VIC_IRQ_STATUS(0)), readl(VIC_IRQ_STATUS(1))); + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) { + printk(KERN_INFO "%s %x %x %x now", __func__, + smsm_int_info->interrupt_mask, + smsm_int_info->pending_interrupts, + smsm_int_info->wakeup_reason); + print_vic_irq_stat(); + } } void msm_irq_exit_sleep2(void) @@ -405,12 +428,13 @@ void msm_irq_exit_sleep2(void) printk(KERN_ERR "msm_irq_exit_sleep \n"); return; } - if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) - printk(KERN_INFO "msm_irq_exit_sleep2 %x %x %x now %x %x\n", + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) { + printk(KERN_INFO "%s %x %x %x now", __func__, smsm_int_info->interrupt_mask, smsm_int_info->pending_interrupts, - smsm_int_info->wakeup_reason, - readl(VIC_IRQ_STATUS(0)), readl(VIC_IRQ_STATUS(1))); + smsm_int_info->wakeup_reason); + print_vic_irq_stat(); + } pending = smsm_int_info->pending_interrupts; for (i = 0; pending && i < ARRAY_SIZE(msm_irq_to_smsm); i++) { unsigned reg_offset = VIC_IRQ_STATUS(__bank(i)) - VIC_IRQ_STATUS(0); @@ -423,10 +447,11 @@ void msm_irq_exit_sleep2(void) if (!(pending & smsm_mask)) continue; pending &= ~smsm_mask; - if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT) - printk(KERN_INFO "msm_irq_exit_sleep2: irq %d " - "still pending %x now %x %x\n", i, pending, - readl(VIC_IRQ_STATUS(0)), readl(VIC_IRQ_STATUS(1))); + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT) { + printk(KERN_INFO "%s: irq %d still pending %x now", + __func__, i, pending); + print_vic_irq_stat(); + } #if 0 /* debug intetrrupt trigger */ if (readl(VIC_IRQ_STATUS(0) + reg_offset) & reg_mask) writel(reg_mask, VIC_INT_CLEAR(0) + reg_offset); @@ -434,10 +459,11 @@ void msm_irq_exit_sleep2(void) if (readl(VIC_IRQ_STATUS(0) + reg_offset) & reg_mask) continue; writel(reg_mask, VIC_SOFTINT(0) + reg_offset); - if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT_TRIGGER) - printk(KERN_INFO "msm_irq_exit_sleep2: irq %d need " - "trigger, now %x %x\n", i, - readl(VIC_IRQ_STATUS(0)), readl(VIC_IRQ_STATUS(1))); + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT_TRIGGER) { + printk(KERN_INFO "%s: irq %d need trigger, now", + __func__, i); + print_vic_irq_stat(); + } } } @@ -447,13 +473,14 @@ void msm_irq_exit_sleep3(void) printk(KERN_ERR "msm_irq_exit_sleep \n"); return; } - if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) - printk(KERN_INFO "msm_irq_exit_sleep3 %x %x %x now %x %x " - "state %x\n", smsm_int_info->interrupt_mask, + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) { + printk(KERN_INFO "%s %x %x %x state %x now", __func__, + smsm_int_info->interrupt_mask, smsm_int_info->pending_interrupts, - smsm_int_info->wakeup_reason, readl(VIC_IRQ_STATUS(0)), - readl(VIC_IRQ_STATUS(1)), + smsm_int_info->wakeup_reason, smsm_get_state(SMSM_STATE_MODEM)); + print_vic_irq_stat(); + } } static struct irq_chip msm_irq_chip = { From eb81ce936985064dcbd82a4bcb74105f6444b3eb Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 24 May 2010 14:31:07 -0700 Subject: [PATCH 0656/2556] [ARM] msm: small cleanup of irq bank handling Change-Id: I50083a74672fe7081fc478fa6fea7731867a46fd Signed-off-by: Dima Zavin --- arch/arm/mach-msm/irq.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c index 40e2dd5ff8c8d..8b8bd9b5cb727 100644 --- a/arch/arm/mach-msm/irq.c +++ b/arch/arm/mach-msm/irq.c @@ -347,15 +347,12 @@ int msm_irq_enter_sleep2(bool arm9_wake, int from_idle) print_vic_irq_stat(); } - pending[0] = readl(VIC_IRQ_STATUS(0)); - pending[0] &= msm_irq_shadow_reg[0].int_en[!from_idle]; - /* Clear INT_A9_M2A_5 since requesting sleep triggers it */ - pending[0] &= ~(1U << INT_A9_M2A_5); - any = pending[0]; - - for (i = 1; i < VIC_NUM_BANKS; ++i) { + for (i = 0; i < VIC_NUM_BANKS; ++i) { pending[i] = readl(VIC_IRQ_STATUS(i)); pending[i] &= msm_irq_shadow_reg[i].int_en[!from_idle]; + /* Clear INT_A9_M2A_5 since requesting sleep triggers it */ + if (i == (INT_A9_M2A_5 / 32)) + pending[i] &= ~(1U << (INT_A9_M2A_5 % 32)); any |= pending[i]; } @@ -388,8 +385,10 @@ int msm_irq_enter_sleep2(bool arm9_wake, int from_idle) writel(1U << INT_A9_M2A_6, VIC_INT_ENSET(0)); } else { for (i = 0; i < VIC_NUM_BANKS; ++i) - writel(msm_irq_shadow_reg[i].int_en[1], VIC_INT_ENSET(i)); + writel(msm_irq_shadow_reg[i].int_en[1], + VIC_INT_ENSET(i)); } + return 0; } @@ -437,7 +436,7 @@ void msm_irq_exit_sleep2(void) } pending = smsm_int_info->pending_interrupts; for (i = 0; pending && i < ARRAY_SIZE(msm_irq_to_smsm); i++) { - unsigned reg_offset = VIC_IRQ_STATUS(__bank(i)) - VIC_IRQ_STATUS(0); + unsigned bank = __bank(i); uint32_t reg_mask = 1UL << (i & 31); int smsm_irq = msm_irq_to_smsm[i]; uint32_t smsm_mask; @@ -453,12 +452,12 @@ void msm_irq_exit_sleep2(void) print_vic_irq_stat(); } #if 0 /* debug intetrrupt trigger */ - if (readl(VIC_IRQ_STATUS(0) + reg_offset) & reg_mask) - writel(reg_mask, VIC_INT_CLEAR(0) + reg_offset); + if (readl(VIC_IRQ_STATUS(bank)) & reg_mask) + writel(reg_mask, VIC_INT_CLEAR(bank)); #endif - if (readl(VIC_IRQ_STATUS(0) + reg_offset) & reg_mask) + if (readl(VIC_IRQ_STATUS(bank)) & reg_mask) continue; - writel(reg_mask, VIC_SOFTINT(0) + reg_offset); + writel(reg_mask, VIC_SOFTINT(bank)); if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT_TRIGGER) { printk(KERN_INFO "%s: irq %d need trigger, now", __func__, i); From 085290439f09c0fd81b00cfeae8b81df6de498bc Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 26 May 2010 12:55:37 -0700 Subject: [PATCH 0657/2556] drivers: mmc: msm: Change max frequency to 49152000 Signed-off-by: Dmitry Shmidt --- drivers/mmc/host/msm_sdcc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 199d131e3eb2d..0c6e2632ec20e 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -53,7 +53,7 @@ #define BUSCLK_PWRSAVE 1 #define BUSCLK_TIMEOUT (HZ) static unsigned int msmsdcc_fmin = 144000; -static unsigned int msmsdcc_fmax = 50000000; +static unsigned int msmsdcc_fmax = 49152000; static unsigned int msmsdcc_4bit = 1; static unsigned int msmsdcc_pwrsave = 1; static unsigned int msmsdcc_piopoll = 1; From bc2082119787723a7bd2017e1833dab6ff8ec9e2 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 26 May 2010 15:51:29 -0700 Subject: [PATCH 0658/2556] [ARM] msm: Update the msm7x30 memory map for AMSS 1220+ Change-Id: I71dfa47cb37da5161d3534190d4128ea2b3f6569 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/include/mach/memory.h | 2 +- arch/arm/mach-msm/include/mach/msm_iomap-7x30.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h index fb31ab5309a68..d28b0531c2454 100644 --- a/arch/arm/mach-msm/include/mach/memory.h +++ b/arch/arm/mach-msm/include/mach/memory.h @@ -24,7 +24,7 @@ #define RESET_VECTOR UL(0x00000000) #elif defined(CONFIG_ARCH_MSM7X30) #define PHYS_OFFSET UL(0x00200000) -#define RESET_VECTOR UL(0x00100000) +#define RESET_VECTOR UL(0x00000000) #elif defined(CONFIG_ARCH_MSM8X60) #define PHYS_OFFSET UL(0x40200000) #define RESET_VECTOR UL(0x00000000) diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h b/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h index 53fb9d2cd4177..9166b4c12a33a 100644 --- a/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h +++ b/arch/arm/mach-msm/include/mach/msm_iomap-7x30.h @@ -87,7 +87,7 @@ #define MSM_TCSR_SIZE SZ_4K #define MSM_SHARED_RAM_BASE IOMEM(0xF8100000) -#define MSM_SHARED_RAM_PHYS 0x00000000 +#define MSM_SHARED_RAM_PHYS 0x00100000 #define MSM_SHARED_RAM_SIZE SZ_1M #define MSM_UART1_PHYS 0xACA00000 From 6b5221122cb3d89e88f31517277984dc85461247 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Thu, 10 Jun 2010 16:34:48 -0700 Subject: [PATCH 0659/2556] msm72k_udc: disable excessive debug chatter Signed-off-by: Brian Swetland --- drivers/usb/gadget/msm72k_udc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 281ecdebe4584..9b0e697f972b3 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -252,7 +252,7 @@ static void ulpi_init(struct usb_info *ui) return; while (seq[0] >= 0) { - INFO("ulpi: write 0x%02x to 0x%02x\n", seq[0], seq[1]); +// INFO("ulpi: write 0x%02x to 0x%02x\n", seq[0], seq[1]); ulpi_write(ui, seq[0], seq[1]); seq += 2; } @@ -295,10 +295,12 @@ static void config_ept(struct msm_endpoint *ept) ept->head->config = cfg; ept->head->next = TERMINATE; +#if 0 if (ept->ep.maxpacket) INFO("ept #%d %s max:%d head:%p bit:%d\n", ept->num, (ept->flags & EPT_FLAG_IN) ? "in" : "out", ept->ep.maxpacket, ept->head, ept->bit); +#endif } static void configure_endpoints(struct usb_info *ui) From b339cacb60882829644016c04e9407182b293a89 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Wed, 9 Jun 2010 22:27:40 -0700 Subject: [PATCH 0660/2556] msm: adsp5v2: bootstrap adsp5v2 work Change-Id: Ib0f8d1ed6a106ffb2ca23480fd0c3ac95fb2c5a9 Signed-off-by: Brian Swetland --- arch/arm/mach-msm/qdsp5v2/Makefile | 5 + arch/arm/mach-msm/qdsp5v2/adsp.c | 692 +++++++++ arch/arm/mach-msm/qdsp5v2/adsp.h | 42 + arch/arm/mach-msm/qdsp5v2/adsp_audio.c | 460 ++++++ arch/arm/mach-msm/qdsp5v2/adsp_audio.h | 44 + arch/arm/mach-msm/qdsp5v2/adsp_module_afe.h | 80 ++ .../mach-msm/qdsp5v2/adsp_module_audplay.h | 184 +++ arch/arm/mach-msm/qdsp5v2/adsp_module_audpp.h | 1250 +++++++++++++++++ arch/arm/mach-msm/qdsp5v2/adsp_private.h | 163 +++ arch/arm/mach-msm/qdsp5v2/audio_glue.c | 540 +++++++ arch/arm/mach-msm/qdsp5v2/audio_out.c | 242 ++++ arch/arm/mach-msm/qdsp5v2/marimba.c | 322 +++++ 12 files changed, 4024 insertions(+) create mode 100644 arch/arm/mach-msm/qdsp5v2/Makefile create mode 100644 arch/arm/mach-msm/qdsp5v2/adsp.c create mode 100644 arch/arm/mach-msm/qdsp5v2/adsp.h create mode 100644 arch/arm/mach-msm/qdsp5v2/adsp_audio.c create mode 100644 arch/arm/mach-msm/qdsp5v2/adsp_audio.h create mode 100644 arch/arm/mach-msm/qdsp5v2/adsp_module_afe.h create mode 100644 arch/arm/mach-msm/qdsp5v2/adsp_module_audplay.h create mode 100644 arch/arm/mach-msm/qdsp5v2/adsp_module_audpp.h create mode 100644 arch/arm/mach-msm/qdsp5v2/adsp_private.h create mode 100644 arch/arm/mach-msm/qdsp5v2/audio_glue.c create mode 100644 arch/arm/mach-msm/qdsp5v2/audio_out.c create mode 100644 arch/arm/mach-msm/qdsp5v2/marimba.c diff --git a/arch/arm/mach-msm/qdsp5v2/Makefile b/arch/arm/mach-msm/qdsp5v2/Makefile new file mode 100644 index 0000000000000..75016db475e7c --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/Makefile @@ -0,0 +1,5 @@ +obj-y += adsp.o +obj-y += adsp_audio.o +obj-y += audio_glue.o +obj-y += audio_out.o +obj-y += marimba.o diff --git a/arch/arm/mach-msm/qdsp5v2/adsp.c b/arch/arm/mach-msm/qdsp5v2/adsp.c new file mode 100644 index 0000000000000..8d742412673aa --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/adsp.c @@ -0,0 +1,692 @@ +/* arch/arm/mach-msm/qdsp5v2/adsp.c + * + * Copyright (C) 2010 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 +#include +#include +#include +#include + +#include + +#include "../dal.h" + +#include "adsp.h" +#include "adsp_private.h" + +struct msm_adsp_queue { + const char *name; + uint32_t offset; + uint32_t max_size; + uint32_t flags; +}; + +struct msm_adsp_module { + msm_adsp_callback func; + void *cookie; + + wait_queue_head_t wait; + struct msm_adsp *adsp; + uint32_t id; + + unsigned active; + + const char *name; + struct msm_adsp_module *next; + struct msm_adsp_queue queue[ADSP_QUEUES_MAX]; +}; + +struct msm_adsp { + /* DSP "registers" */ + void *read_ctrl; + void *write_ctrl; + void *send_irq; + void *base; + + /* DAL client handle for DSP control service */ + struct dal_client *client; + + spinlock_t callback_lock; + spinlock_t write_lock; + spinlock_t event_lock; + + wait_queue_head_t callback_wq; + + /* list of all existing dsp modules */ + struct msm_adsp_module *all_modules; + + /* map from dsp rtos task IDs to modules */ + struct msm_adsp_module *task_to_module[ADSP_TASKS_MAX]; + + /* used during initialization */ + struct adsp_module_info tmpmodule; + +}; + +static struct msm_adsp the_adsp; + +static struct msm_adsp_module *id_to_module(struct msm_adsp *adsp, unsigned id) +{ + struct msm_adsp_module *module; + + for (module = adsp->all_modules; module; module = module->next) + if (module->id == id) + return module; + return NULL; +} + +int msm_adsp_get(const char *name, struct msm_adsp_module **module, + msm_adsp_callback func, void *cookie) +{ + struct msm_adsp *adsp = &the_adsp; + unsigned long flags; + int ret = -ENODEV; + struct msm_adsp_module *m; + + for (m = adsp->all_modules; m; m = m->next) { + if (!strcmp(m->name, name)) { + spin_lock_irqsave(&m->adsp->callback_lock, flags); + if (m->func == 0) { + m->func = func; + m->cookie = cookie; + *module = m; + ret = 0; + } else { + ret = -EBUSY; + } + spin_unlock_irqrestore(&m->adsp->callback_lock, flags); + break; + } + } + return ret; +} + +void msm_adsp_put(struct msm_adsp_module *m) +{ + unsigned long flags; + + spin_lock_irqsave(&m->adsp->callback_lock, flags); + m->func = 0; + m->cookie = 0; + spin_unlock_irqrestore(&m->adsp->callback_lock, flags); +} + + +int msm_adsp_lookup_queue(struct msm_adsp_module *module, const char *name) +{ + int n; + for (n = 0; n < ADSP_QUEUES_MAX; n++) { + if (!module->queue[n].name) + break; + if (!strcmp(name, module->queue[n].name)) + return n; + } + return -ENODEV; +} + +static int msm_adsp_command(struct msm_adsp_module *module, unsigned cmd_id) +{ + struct adsp_dal_cmd cmd; + int ret; + + cmd.cmd = cmd_id; + cmd.proc_id = ADSP_PROC_APPS; + cmd.module = module->id; + cmd.cookie = 0; + + ret = dal_call_f5(module->adsp->client, ADSP_DAL_COMMAND, + &cmd, sizeof(cmd)); + if (ret) + return -EIO; + + return 0; +} + +int msm_adsp_enable(struct msm_adsp_module *module) +{ + int ret; + /* XXX interlock? */ + + ret = msm_adsp_command(module, ADSP_CMD_ENABLE); + if (ret < 0) { + pr_err("msm_adsp_enable: error enabling %s %d\n", + module->name, ret); + return -EIO; + } + ret = wait_event_timeout(module->adsp->callback_wq, + module->active, 5 * HZ); + if (!ret) { + pr_err("msm_adsp_enable: timeout enabling %s\n", + module->name); + return -ETIMEDOUT; + } + + printk("msm_adsp_enable: %s enabled.\n", module->name); + return 0; +} + +int msm_adsp_disable(struct msm_adsp_module *module) +{ + /* XXX interlock? */ + return msm_adsp_command(module, ADSP_CMD_DISABLE); +} + +int msm_adsp_write(struct msm_adsp_module *module, unsigned queue_idx, + void *cmd_buf, size_t cmd_size) +{ + struct msm_adsp *adsp; + uint32_t val; + uint32_t dsp_q_addr; + uint32_t dsp_addr; + uint32_t cmd_id = 0; + int cnt = 0; + int ret = 0; + unsigned long flags; + + if (!module || !cmd_size || (queue_idx >= ADSP_QUEUES_MAX)) + return -EINVAL; + + if (module->queue[queue_idx].name == NULL) + return -EINVAL; + + adsp = module->adsp; + + spin_lock_irqsave(&adsp->write_lock, flags); + +#if 0 + if (module->state != ADSP_STATE_ENABLED) { + ret = -ENODEV; + goto done; + } +#endif + + dsp_q_addr = module->queue[queue_idx].offset; + dsp_q_addr &= ADSP_WRITE_CTRL_DSP_ADDR_M; + + /* Poll until the ADSP is ready to accept a command. + * Wait for 100us, return error if it's not responding. + * If this returns an error, we need to disable ALL modules and + * then retry. + */ + while (((val = readl(adsp->write_ctrl)) & + ADSP_WRITE_CTRL_READY_M) != + ADSP_WRITE_CTRL_READY_V) { + if (cnt > 50) { + pr_err("timeout waiting for DSP write ready\n"); + ret = -EIO; + goto done; + } + udelay(2); + cnt++; + } + + /* Set the mutex bits */ + val &= ~(ADSP_WRITE_CTRL_MUTEX_M); + val |= ADSP_WRITE_CTRL_MUTEX_NAVAIL_V; + + /* Clear the command bits */ + val &= ~(ADSP_WRITE_CTRL_CMD_M); + + /* Set the queue address bits */ + val &= ~(ADSP_WRITE_CTRL_DSP_ADDR_M); + val |= dsp_q_addr; + + writel(val, adsp->write_ctrl); + + /* Generate an interrupt to the DSP. This notifies the DSP that + * we are about to send a command on this particular queue. The + * DSP will in response change its state. + */ + writel(1, adsp->send_irq); + + /* Poll until the adsp responds to the interrupt; this does not + * generate an interrupt from the adsp. This should happen within + * 5ms. + */ + cnt = 0; + while ((readl(adsp->write_ctrl) & + ADSP_WRITE_CTRL_MUTEX_M) == + ADSP_WRITE_CTRL_MUTEX_NAVAIL_V) { + if (cnt > 2500) { + pr_err("timeout waiting for adsp ack\n"); + ret = -EIO; + goto done; + } + udelay(2); + cnt++; + } + + /* Read the ctrl word */ + val = readl(adsp->write_ctrl); + + if ((val & ADSP_WRITE_CTRL_STATUS_M) != + ADSP_WRITE_CTRL_NO_ERR_V) { + ret = -EIO; + pr_err("failed to write queue %x, retry\n", dsp_q_addr); + goto done; + } + + /* No error */ + /* Get the DSP buffer address */ + dsp_addr = (val & ADSP_WRITE_CTRL_DSP_ADDR_M) + + (uint32_t)MSM_AD5_BASE; + + if (dsp_addr < (uint32_t)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) { + uint16_t *buf_ptr = (uint16_t *) cmd_buf; + uint16_t *dsp_addr16 = (uint16_t *)dsp_addr; + cmd_size /= sizeof(uint16_t); + + /* Save the command ID */ + cmd_id = (uint32_t) buf_ptr[0]; + + /* Copy the command to DSP memory */ + cmd_size++; + while (--cmd_size) + *dsp_addr16++ = *buf_ptr++; + } else { + uint32_t *buf_ptr = (uint32_t *) cmd_buf; + uint32_t *dsp_addr32 = (uint32_t *)dsp_addr; + cmd_size /= sizeof(uint32_t); + + /* Save the command ID */ + cmd_id = buf_ptr[0]; + + cmd_size++; + while (--cmd_size) + *dsp_addr32++ = *buf_ptr++; + } + + /* Set the mutex bits */ + val &= ~(ADSP_WRITE_CTRL_MUTEX_M); + val |= ADSP_WRITE_CTRL_MUTEX_NAVAIL_V; + + /* Set the command bits to write done */ + val &= ~(ADSP_WRITE_CTRL_CMD_M); + val |= ADSP_WRITE_CTRL_CMD_WRITE_DONE_V; + + /* Set the queue address bits */ + val &= ~(ADSP_WRITE_CTRL_DSP_ADDR_M); + val |= dsp_q_addr; + + writel(val, adsp->write_ctrl); + + /* Generate an interrupt to the DSP. It does not respond with + * an interrupt, and we do not need to wait for it to + * acknowledge, because it will hold the mutex lock until it's + * ready to receive more commands again. + */ + writel(1, adsp->send_irq); + +// module->num_commands++; + +done: + spin_unlock_irqrestore(&adsp->write_lock, flags); + return ret; +} + +static int adsp_read_task_to_host(struct msm_adsp *adsp, void *dsp_addr) +{ + struct msm_adsp_module *module; + unsigned task_id; + unsigned msg_id; + unsigned msg_length; + unsigned n; + unsigned tmp; + union { + u32 data32[16]; + u16 data16[32]; + } u; + + if (dsp_addr >= (void *)(MSM_AD5_BASE + QDSP_RAMC_OFFSET)) { + uint32_t *dsp_addr32 = dsp_addr; + tmp = *dsp_addr32++; + task_id = (tmp & ADSP_READ_CTRL_TASK_ID_M) >> 8; + msg_id = (tmp & ADSP_READ_CTRL_MSG_ID_M); + tmp >>= 16; + if (tmp > 16) { + pr_err("adsp: message too large (%d x 32)\n", tmp); + tmp = 16; + } + msg_length = tmp * sizeof(uint32_t); + for (n = 0; n < tmp; n++) + u.data32[n] = *dsp_addr32++; + } else { + uint16_t *dsp_addr16 = dsp_addr; + tmp = *dsp_addr16++; + task_id = (tmp & ADSP_READ_CTRL_TASK_ID_M) >> 8; + msg_id = tmp & ADSP_READ_CTRL_MSG_ID_M; + tmp = *dsp_addr16++; + if (tmp > 32) { + pr_err("adsp: message too large (%d x 16)\n", tmp); + tmp = 32; + } + msg_length = tmp * sizeof(uint16_t); + for (n = 0; n < tmp; n++) + u.data16[n] = *dsp_addr16++; + } + +#if 0 + pr_info("ADSP EVENT TASK %d MSG %d SIZE %d\n", + task_id, msg_id, msg_length); +#endif + if (task_id > ADSP_TASKS_MAX) { + pr_err("adsp: bogus task id %d\n", task_id); + return 0; + } + module = adsp->task_to_module[task_id]; + + if (!module) { + pr_err("adsp: no module for task id %d\n", task_id); + return 0; + } + + if (!module->func) { + pr_err("module %s is not open\n", module->name); + return 0; + } + + module->func(msg_id, u.data32, msg_length, module->cookie); + return 0; +} + +static int adsp_get_event(struct msm_adsp *adsp) +{ + uint32_t val; + uint32_t ready; + void *dsp_addr; + uint32_t cmd_type; + int cnt; + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&adsp->event_lock, flags); + + /* Whenever the DSP has a message, it updates this control word + * and generates an interrupt. When we receive the interrupt, we + * read this register to find out what ADSP task the command is + * comming from. + * + * The ADSP should *always* be ready on the first call, but the + * irq handler calls us in a loop (to handle back-to-back command + * processing), so we give the DSP some time to return to the + * ready state. The DSP will not issue another IRQ for events + * pending between the first IRQ and the event queue being drained, + * unfortunately. + */ + + for (cnt = 0; cnt < 50; cnt++) { + val = readl(adsp->read_ctrl); + + if ((val & ADSP_READ_CTRL_FLAG_M) == + ADSP_READ_CTRL_FLAG_UP_CONT_V) + goto ready; + + udelay(2); + } + pr_err("adsp_get_event: not ready after 100uS\n"); + rc = -EBUSY; + goto done; + +ready: + /* Here we check to see if there are pending messages. If there are + * none, we siply return -EAGAIN to indicate that there are no more + * messages pending. + */ + ready = val & ADSP_READ_CTRL_READY_M; + if ((ready != ADSP_READ_CTRL_READY_V) && + (ready != ADSP_READ_CTRL_CONT_V)) { + rc = -EAGAIN; + goto done; + } + + /* DSP says that there are messages waiting for the host to read */ + + /* Get the Command Type */ + cmd_type = val & ADSP_READ_CTRL_CMD_TYPE_M; + + /* Get the DSP buffer address */ + dsp_addr = (void *)((val & + ADSP_READ_CTRL_DSP_ADDR_M) + + (uint32_t)MSM_AD5_BASE); + + /* We can only handle Task-to-Host messages */ + if (cmd_type != ADSP_READ_CTRL_CMD_TASK_TO_H_V) { + rc = -EIO; + goto done; + } + + adsp_read_task_to_host(adsp, dsp_addr); + + val = readl(adsp->read_ctrl); + val &= ~ADSP_READ_CTRL_READY_M; + + /* Write ctrl word to the DSP */ + writel(val, adsp->read_ctrl); + + /* Generate an interrupt to the DSP */ + writel(1, adsp->send_irq); + +done: + spin_unlock_irqrestore(&adsp->event_lock, flags); + return rc; +} + +static irqreturn_t adsp_irq_handler(int irq, void *data) +{ + struct msm_adsp *adsp = &the_adsp; + int count = 0; + for (count = 0; count < 15; count++) + if (adsp_get_event(adsp) < 0) + break; +#if 0 + if (count > adsp->event_backlog_max) + adsp->event_backlog_max = count; + adsp->events_received += count; +#endif + if (count == 15) + pr_err("too many (%d) events for single irq!\n", count); + return IRQ_HANDLED; +} + +static void adsp_dal_callback(void *data, int len, void *cookie) +{ + struct msm_adsp *adsp = cookie; + struct adsp_dal_event *e = data; + struct msm_adsp_module *m; +#if 0 + pr_info("adsp: h %08x c %08x l %08x\n", + e->evt_handle, e->evt_cookie, e->evt_length); + pr_info(" : e %08x v %08x p %08x\n", + e->event, e->version, e->proc_id); + pr_info(" : m %08x i %08x a %08x\n", + e->u.info.module, e->u.info.image, e->u.info.apps_okts); +#endif + + switch (e->event) { + case ADSP_EVT_INIT_INFO: + memcpy(&adsp->tmpmodule, &e->u.module, + sizeof(adsp->tmpmodule)); + break; + case ADSP_EVT_MOD_READY: + m = id_to_module(adsp, e->u.info.module); + if (m) { + pr_info("adsp: %s READY\n", m->name); + m->active = 1; + } + break; + case ADSP_EVT_MOD_DISABLE: + /* does not actually happen in adsp5v2 */ + m = id_to_module(adsp, e->u.info.module); + if (m) + pr_info("adsp: %s DISABLED\n", m->name); + break; + case ADSP_EVT_DISABLE_FAIL: + m = id_to_module(adsp, e->u.info.module); + if (m) + pr_info("adsp: %s DISABLE FAILED\n", m->name); + break; + default: + pr_err("adsp_dal_callback: unknown event %d\n", e->event); + } + wake_up(&adsp->callback_wq); +} + +static void adsp_add_module(struct msm_adsp *adsp, struct adsp_module_info *mi) +{ + struct msm_adsp_module *module; + int n; + + if (mi->task_id >= ADSP_TASKS_MAX) { + pr_err("adsp: module '%s' task id %d is invalid\n", + mi->name, mi->task_id); + return; + } + if (mi->q_cnt > ADSP_QUEUES_MAX) { + pr_err("adsp: module '%s' q_cnt %d is invalid\n", + mi->name, mi->q_cnt); + return; + } + + module = kzalloc(sizeof(*module), GFP_KERNEL); + if (!module) + return; + + module->name = kstrdup(mi->name, GFP_KERNEL); + if (!module->name) + goto fail_module_name; + + for (n = 0; n < mi->q_cnt; n++) { + struct msm_adsp_queue *queue = module->queue + n; + queue->name = kstrdup(mi->queue[n].name, GFP_KERNEL); + if (!queue->name) + goto fail_queue_name; + queue->offset = mi->queue[n].offset; + queue->max_size = mi->queue[n].max_size; + queue->flags = mi->queue[n].flag; + } + + init_waitqueue_head(&module->wait); + module->id = mi->uuid; + module->adsp = adsp; + + module->next = adsp->all_modules; + adsp->all_modules = module; + + adsp->task_to_module[mi->task_id] = module; +#if 0 + pr_info("adsp: module '%s' id 0x%x task %d\n", + module->name, module->id, mi->task_id); + for (n = 0; (n < ADSP_TASKS_MAX) && module->queue[n].name; n++) + pr_info(" queue '%s' off 0x%x size %d flags %x", + module->queue[n].name, module->queue[n].offset, + module->queue[n].max_size, module->queue[n].flags); +#endif + return; + +fail_queue_name: + for (n = 0; n < mi->q_cnt; n++) + if (module->queue[n].name) + kfree(module->queue[n].name); +fail_module_name: + kfree(module); +} + +static int adsp_probe(struct platform_device *pdev) { + struct msm_adsp *adsp = &the_adsp; + struct adsp_dal_cmd cmd; + int ret, n; + + pr_info("*** adsp_probe() ***\n"); + + adsp->base = MSM_AD5_BASE; + adsp->read_ctrl = adsp->base + ADSP_READ_CTRL_OFFSET; + adsp->write_ctrl = adsp->base + ADSP_WRITE_CTRL_OFFSET; + adsp->send_irq = adsp->base + ADSP_SEND_IRQ_OFFSET; + + adsp->client = dal_attach(ADSP_DAL_DEVICE, ADSP_DAL_PORT, + adsp_dal_callback, adsp); + if (!adsp->client) { + pr_err("adsp_probe: cannot attach to dal device\n"); + return -ENODEV; + } + + cmd.cmd = ADSP_CMD_GET_INIT_INFO; + cmd.proc_id = ADSP_PROC_APPS; + cmd.module = 0; + cmd.cookie = 0; + + for (n = 0; n < 64; n++) { + adsp->tmpmodule.uuid = 0xffffffff; + ret = dal_call_f5(adsp->client, ADSP_DAL_COMMAND, + &cmd, sizeof(cmd)); + if (ret) { + pr_err("adsp_probe() get info dal call failed\n"); + break; + } + ret = wait_event_timeout(adsp->callback_wq, + (adsp->tmpmodule.uuid != 0xffffffff), + 5*HZ); + if (ret == 0) { + pr_err("adsp_probe() timed out getting module info\n"); + break; + } + if (adsp->tmpmodule.uuid == 0x7fffffff) + break; + if (adsp->tmpmodule.task_id == 0xffff) + continue; +// adsp_print_module(&adsp->tmpmodule); + adsp_add_module(adsp, &adsp->tmpmodule); + } + + ret = request_irq(INT_AD5A_MPROC_APPS_0, adsp_irq_handler, + IRQF_TRIGGER_RISING, "adsp", 0); + if (ret < 0) + return ret; + + pr_info("*** adsp_probe() done ***\n"); + return 0; +} + +static struct platform_driver adsp_driver = { + .probe = adsp_probe, + .driver = { + .name = "SMD_DAL00", + .owner = THIS_MODULE, + }, +}; + +extern int msm_codec_init(void); + +static int __init adsp_init(void) +{ + struct msm_adsp *adsp = &the_adsp; + + pr_info("*** adsp_init() ***\n"); + + init_waitqueue_head(&adsp->callback_wq); + spin_lock_init(&adsp->callback_lock); + spin_lock_init(&adsp->write_lock); + spin_lock_init(&adsp->event_lock); + + msm_codec_init(); + + return platform_driver_register(&adsp_driver); +} + +module_init(adsp_init); diff --git a/arch/arm/mach-msm/qdsp5v2/adsp.h b/arch/arm/mach-msm/qdsp5v2/adsp.h new file mode 100644 index 0000000000000..c37588f936a23 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/adsp.h @@ -0,0 +1,42 @@ +/* arch/arm/mach-msm/qdsp5v2/adsp.h + * + * Copyright (C) 2010 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 _MSM_ADSP_5V2_H_ +#define _MSM_ADSP_5V2_H_ + +struct msm_adsp_module; + +typedef void (*msm_adsp_callback)(unsigned id, void *event, + size_t len, void *cookie); + + +int msm_adsp_get(const char *name, struct msm_adsp_module **module, + msm_adsp_callback callback, void *cookie); + +void msm_adsp_put(struct msm_adsp_module *module); + +/* find queue index for a named module command queue */ +int msm_adsp_lookup_queue(struct msm_adsp_module *module, const char *name); + +int msm_adsp_enable(struct msm_adsp_module *module); +int msm_adsp_disable(struct msm_adsp_module *module); + +/* write is safe to call from atomic context. All other msm_adsp_* + * calls may block. + */ +int msm_adsp_write(struct msm_adsp_module *module, unsigned queue_idx, + void *data, size_t len); + +#endif diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_audio.c b/arch/arm/mach-msm/qdsp5v2/adsp_audio.c new file mode 100644 index 0000000000000..80dde0a1ee4fa --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/adsp_audio.c @@ -0,0 +1,460 @@ +/* arch/arm/mach-msm/qdsp5v2/adsp_audio.c + * + * Copyright (C) 2010 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 "adsp.h" +#include "adsp_module_afe.h" +#include "adsp_module_audpp.h" +#include "adsp_module_audplay.h" + +#include "adsp_audio.h" + + +#define AUDDEC_DEC_PCM 0 + +/* Decoder status received from AUDPPTASK */ +#define STATUS_SLEEP 0 +#define STATUS_INIT 1 +#define STATUS_CONFIG 2 +#define STATUS_PLAY 3 + + +#define MAX_AUDPLAY_TASKS 5 + +struct audplay { + struct msm_adsp_module *module; + wait_queue_head_t wait; + int q1; + int active; + int id; + int status; + struct audpp *audpp; + + void (*callback)(void *cookie); + void *cookie; +}; + +struct audpp { + struct msm_adsp_module *module; + wait_queue_head_t wait; + struct mutex lock; + int q1, q2, q3; + unsigned count; + struct audplay audplay[MAX_AUDPLAY_TASKS]; +}; + +struct afe_info { + struct msm_adsp_module *module; + wait_queue_head_t wait; + struct mutex lock; + unsigned count; + u8 active[AFE_DEVICE_ID_MAX + 1]; +}; + +struct afe_info the_afe_info; +static struct audpp the_audpp; + + +static void afe_callback(unsigned id, void *event, size_t len, void *cookie) +{ + struct afe_info *afe = cookie; + struct afe_msg_codec_config_ack *msg = event; + + printk("afe_callback id=%d len=%d\n", id, len); + + if (id != AFE_MSG_CODEC_CONFIG_ACK) + return; + + if (msg->device_id > AFE_DEVICE_ID_MAX) + return; + + if (msg->device_activity == AFE_MSG_CODEC_CONFIG_ENABLED) + afe->active[msg->device_id] = 1; + else + afe->active[msg->device_id] = 0; + + wake_up(&afe->wait); +} + +int afe_enable(unsigned device, unsigned rate, unsigned channels) +{ + struct afe_info *afe = &the_afe_info; + struct afe_cmd_codec_config cmd; + int ret = 0; + + /* rate must be one of the following: + * 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 + */ + cmd.cmd_id = AFE_CMD_CODEC_CONFIG_CMD; + cmd.device_id = device; + cmd.activity = 1; + cmd.sample_rate = rate / 1000; + cmd.channel_mode = channels; + cmd.volume = AFE_VOLUME_UNITY; + cmd.reserved = 0; + + mutex_lock(&afe->lock); + + if (!afe->module) { + ret = msm_adsp_get("AFE", &afe->module, afe_callback, afe); + if (ret) + goto done; + } + + if (afe->active[device]) { + pr_err("afe_enable: device %d already enabled\n", device); + ret = -EBUSY; + goto done; + } + + if (++afe->count == 1) { + pr_info("AFE ENABLE!\n"); + ret = msm_adsp_enable(afe->module); + if (ret < 0) { + pr_err("afe_enable: cannot enable module\n"); + afe->count--; + goto done; + } + } + + ret = msm_adsp_write(afe->module, 0, &cmd, sizeof(cmd)); + if (ret < 0) { + printk("afe_enable: command write failed\n"); + goto done; + } + + ret = wait_event_timeout(afe->wait, afe->active[device], 5 * HZ); + if (!ret) { + pr_err("afe_enable: command timeout\n"); + ret = -EIO; + } else { + printk("afe_enable: device %d active\n", cmd.device_id); + } +done: + mutex_unlock(&afe->lock); + return ret; +} + +int afe_disable(unsigned device) +{ + struct afe_info *afe = &the_afe_info; + struct afe_cmd_codec_config cmd; + int ret = 0; + + memset(&cmd, sizeof(cmd), 0); + cmd.cmd_id = AFE_CMD_CODEC_CONFIG_CMD; + cmd.device_id = device; + cmd.activity = 0; + + mutex_lock(&afe->lock); + + if (!afe->active[device]) { + pr_err("afe_disable: device %d already disabled\n", device); + goto done; + } + + ret = msm_adsp_write(afe->module, 0, &cmd, sizeof(cmd)); + if (ret < 0) { + printk("afe_disable: command write failed\n"); + goto done; + } + + ret = wait_event_timeout(afe->wait, !afe->active[device], 5 * HZ); + if (!ret) { + pr_err("afe_disable: command timeout\n"); + ret = -EIO; + } else { + printk("afe_disable: device %d inactive\n", cmd.device_id); + if (--afe->count == 0) { + pr_info("AFE DISABLE!\n"); + msm_adsp_disable(afe->module); + } + } +done: + mutex_unlock(&afe->lock); + return ret; +} + +static void audpp_callback(unsigned id, void *event, size_t len, void *cookie) +{ + struct audpp *audpp = cookie; + + if (id == AUDPP_MSG_STATUS_MSG) { + struct audpp_msg_status_msg *msg = event; + pr_info("audpp STATUS id=%d status=%d reason=%d\n", + msg->dec_id, msg->status, msg->reason); + if (msg->dec_id < MAX_AUDPLAY_TASKS) { + audpp->audplay[msg->dec_id].status = msg->status; + wake_up(&audpp->audplay[msg->dec_id].wait); + } + + } else { + pr_info("audpp cb %d %d\n", id, len); + } +} + +static int audpp_get(struct audpp *audpp) +{ + int ret = 0; + + if (++audpp->count > 1) + return 0; + + ret = msm_adsp_get("AUDPP", &audpp->module, audpp_callback, audpp); + if (ret < 0) { + pr_err("audpp_get: could not get AUDPP\n"); + goto fail_get_module; + } + + audpp->q1 = msm_adsp_lookup_queue(audpp->module, "AudPPCmd1"); + audpp->q2 = msm_adsp_lookup_queue(audpp->module, "AudPPCmd2"); + audpp->q3 = msm_adsp_lookup_queue(audpp->module, "AudPPCmd3"); + if ((audpp->q1 < 0) || (audpp->q2 < 0) || (audpp->q3 < 0)) { + pr_err("audpp_get: could not get queues\n"); + ret = -ENODEV; + goto fail_enable_module; + } + + ret = msm_adsp_enable(audpp->module); + if (ret < 0) + goto fail_enable_module; + + return 0; + +fail_enable_module: + msm_adsp_put(audpp->module); + audpp->module = NULL; +fail_get_module: + audpp->count--; + return ret; +} + +static void audpp_put(struct audpp *audpp) +{ + if (--audpp->count > 0) + return; + + msm_adsp_disable(audpp->module); + msm_adsp_put(audpp->module); + audpp->module = NULL; +} + + +static void audplay_callback(unsigned id, void *event, size_t len, void *cookie) +{ + struct audplay *audplay = cookie; + if (id == AUDPLAY_MSG_DEC_NEEDS_DATA) { +#if 0 + struct audplay_msg_dec_needs_data *msg = event; + pr_info("audplay NEEDDATA id=%d off=%d sz=%d %d %d %d %d\n", + msg->dec_id, msg->adecDataReadPtrOffset, + msg->adecDataBufSize, msg->bitstream_free_len, + msg->bitstream_write_ptr, msg->bitstream_buf_start, + msg->bitstream_buf_len); +#endif + audplay->callback(audplay->cookie); + } else { + pr_info("audplay cb %d %d\n", id, len); + } +} + +struct audplay *audplay_get(void (*cb)(void *cookie), void *cookie) +{ + struct audpp *audpp = &the_audpp; + struct audplay *audplay = 0; + char buf[32]; + unsigned n; + int ret; + + mutex_lock(&audpp->lock); + + for (n = 0; n < MAX_AUDPLAY_TASKS; n++) + if (audpp->audplay[n].active == 0) break; + + if (n == MAX_AUDPLAY_TASKS) + goto done; + + if (audpp_get(audpp)) + goto done; + + audplay = audpp->audplay + n; + sprintf(buf, "AUDPLAY%d", n); + ret = msm_adsp_get(buf, &audplay->module, audplay_callback, audplay); + if (ret < 0) + goto fail_audplay_get; + + sprintf(buf,"AudPlay%dBitStreamCtrl", n); + audplay->q1 = msm_adsp_lookup_queue(audplay->module, buf); + if (audplay->q1 < 0) + goto fail_audplay_enable; + + ret = msm_adsp_enable(audplay->module); + if (ret < 0) + goto fail_audplay_enable; + + audplay->active = 1; + audplay->callback = cb; + audplay->cookie = cookie; + goto done; + +fail_audplay_enable: + msm_adsp_put(audplay->module); + audplay->module = NULL; + audplay->callback = NULL; +fail_audplay_get: + audplay = NULL; + audpp_put(audpp); +done: + mutex_unlock(&audpp->lock); + return audplay; +} + +void audplay_put(struct audplay *audplay) +{ + mutex_lock(&audplay->audpp->lock); + audplay->active = 0; + msm_adsp_disable(audplay->module); + msm_adsp_put(audplay->module); + audplay->module = NULL; + audplay->callback = NULL; + audpp_put(audplay->audpp); + mutex_unlock(&audplay->audpp->lock); +} + +static void inline audplay_send_q1(struct audplay *audplay, void *cmd, int len) +{ + msm_adsp_write(audplay->module, audplay->q1, cmd, len); +} + +static void inline audpp_send_q1(struct audpp *audpp, void *cmd, int len) +{ + msm_adsp_write(audpp->module, audpp->q1, cmd, len); +} + +static void inline audpp_send_q2(struct audpp *audpp, void *cmd, int len) +{ + msm_adsp_write(audpp->module, audpp->q2, cmd, len); +} + +static void inline audpp_send_q3(struct audpp *audpp, void *cmd, int len) +{ + msm_adsp_write(audpp->module, audpp->q3, cmd, len); +} + + +void audplay_config_pcm(struct audplay *audplay, + unsigned rate, unsigned width, unsigned channels) +{ + struct audpp_cmd_cfg_adec_params_wav cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.common.cmd_id = AUDPP_CMD_CFG_ADEC_PARAMS; + cmd.common.length = AUDPP_CMD_CFG_ADEC_PARAMS_WAV_LEN >> 1; + cmd.common.dec_id = audplay->id; + cmd.common.input_sampling_frequency = rate; + cmd.stereo_cfg = channels; + cmd.pcm_width = 1; + cmd.sign = 0; /* really? */ + audpp_send_q2(audplay->audpp, &cmd, sizeof(cmd)); /* sizeof(cmd)?!*/ +} + +void audplay_dsp_config(struct audplay *audplay, int enable) +{ + struct audpp_cmd_cfg_dec_type cmd; + int next; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_CFG_DEC_TYPE; + cmd.dec_cfg = AUDPP_CMD_UPDATDE_CFG_DEC; + if (enable) { + cmd.dec_cfg |= AUDPP_CMD_ENA_DEC_V | AUDDEC_DEC_PCM; + next = STATUS_INIT; + } else { + cmd.dec_cfg |= AUDPP_CMD_DIS_DEC_V; + next = STATUS_SLEEP; + } + cmd.dm_mode = 0; + cmd.stream_id = audplay->id; + + mutex_lock(&audplay->audpp->lock); + audpp_send_q1(audplay->audpp, &cmd, sizeof(cmd)); + wait_event_timeout(audplay->wait, audplay->status == next, 5 * HZ); + mutex_unlock(&audplay->audpp->lock); +} + +void audplay_send_data(struct audplay *audplay, unsigned phys, unsigned len) +{ + struct audplay_cmd_bitstream_data_avail cmd; + + cmd.cmd_id = AUDPLAY_CMD_BITSTREAM_DATA_AVAIL; + cmd.decoder_id = audplay->id; + cmd.buf_ptr = phys; + cmd.buf_size = len/2; + cmd.partition_number = 0; + + mutex_lock(&audplay->audpp->lock); + audplay_send_q1(audplay, &cmd, sizeof(cmd)); + wait_event_timeout(audplay->wait, audplay->status == STATUS_PLAY, 5 * HZ); + mutex_unlock(&audplay->audpp->lock); +} + +void audplay_mix_select(struct audplay *audplay, unsigned mix) +{ + struct audpp_cmd_cfg_dev_mixer_params cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_id = AUDPP_CMD_CFG_DEV_MIXER; + cmd.stream_id = audplay->id; + cmd.mixer_cmd = mix; + audpp_send_q1(audplay->audpp, &cmd, sizeof(cmd)); +} + +void audplay_volume_pan(struct audplay *audplay, unsigned volume, unsigned pan) +{ +#define AUDPP_CMD_VOLUME_PAN 0 +#define AUDPP_CMD_CFG_OBJ_UPDATE 0x8000 + uint16_t cmd[7]; + cmd[0] = AUDPP_CMD_CFG_OBJECT_PARAMS; + cmd[1] = AUDPP_CMD_POPP_STREAM; + cmd[2] = audplay->id; + cmd[3] = AUDPP_CMD_CFG_OBJ_UPDATE; + cmd[4] = AUDPP_CMD_VOLUME_PAN; + cmd[5] = volume; + cmd[6] = pan; + audpp_send_q3(audplay->audpp, cmd, sizeof(cmd)); +} + + + +void adsp_audio_init(void) +{ + struct afe_info *afe = &the_afe_info; + struct audpp *audpp = &the_audpp; + int n; + + mutex_init(&audpp->lock); + init_waitqueue_head(&audpp->wait); + for (n = 0; n < MAX_AUDPLAY_TASKS; n++) { + struct audplay *audplay = audpp->audplay + n; + audplay->id = n; + audplay->audpp = audpp; + init_waitqueue_head(&audplay->wait); + } + + mutex_init(&afe->lock); + init_waitqueue_head(&afe->wait); +} diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_audio.h b/arch/arm/mach-msm/qdsp5v2/adsp_audio.h new file mode 100644 index 0000000000000..4d52e7cfe0bcd --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/adsp_audio.h @@ -0,0 +1,44 @@ +/* arch/arm/mach-msm/qdsp5v2/adsp_audio.h + * + * Copyright (C) 2010 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 _MSM_ADSP_AUDIO_H_ +#define _MSM_ADSP_AUDIO_H_ + +struct audplay; +struct audpp; + +struct audplay *audplay_get(void (*cb)(void *cookie), void *cookie); +void audplay_put(struct audplay *audplay); + +void audplay_send_data(struct audplay *audplay, unsigned phys, unsigned len); + +void audplay_dsp_config(struct audplay *audplay, int enable); +void audplay_config_pcm(struct audplay *audplay, + unsigned rate, unsigned width, unsigned channels); + + +void audplay_mix_select(struct audplay *audplay, unsigned mix); +void audplay_volume_pan(struct audplay *audplay, unsigned volume, unsigned pan); + + +int afe_enable(unsigned device, unsigned rate, unsigned channels); +int afe_disable(unsigned device); + + +int msm_codec_output(int enable); + +void adsp_audio_init(void); + +#endif diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_module_afe.h b/arch/arm/mach-msm/qdsp5v2/adsp_module_afe.h new file mode 100644 index 0000000000000..eaa6139c64cb9 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/adsp_module_afe.h @@ -0,0 +1,80 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __ADSP_MODULE_AFE_H +#define __ADSP_MODULE_AFE_H + +#define AFE_DEVICE_MI2S_CODEC_RX 1 /* internal codec rx path */ +#define AFE_DEVICE_MI2S_CODEC_TX 2 /* internal codec tx path */ +#define AFE_DEVICE_AUX_CODEC_RX 3 /* external codec rx path */ +#define AFE_DEVICE_AUX_CODEC_TX 4 /* external codec tx path */ +#define AFE_DEVICE_MI2S_HDMI_RX 5 /* HDMI/FM block rx path */ +#define AFE_DEVICE_MI2S_HDMI_TX 6 /* HDMI/FM block tx path */ +#define AFE_DEVICE_ID_MAX 7 + +#define AFE_VOLUME_UNITY 0x4000 /* Q14 format */ + +#define AFE_CMD_CODEC_CONFIG_CMD 0x1 +#define AFE_CMD_CODEC_CONFIG_LEN sizeof(struct afe_cmd_codec_config) + +struct afe_cmd_codec_config{ + uint16_t cmd_id; + uint16_t device_id; + uint16_t activity; + uint16_t sample_rate; + uint16_t channel_mode; + uint16_t volume; + uint16_t reserved; +} __attribute__ ((packed)); + +#define AFE_CMD_AUX_CODEC_CONFIG_CMD 0x3 +#define AFE_CMD_AUX_CODEC_CONFIG_LEN sizeof(struct afe_cmd_aux_codec_config) + +struct afe_cmd_aux_codec_config{ + uint16_t cmd_id; + uint16_t dma_path_ctl; + uint16_t pcm_ctl; + uint16_t eight_khz_int_mode; + uint16_t aux_codec_intf_ctl; + uint16_t data_format_padding_info; +} __attribute__ ((packed)); + +#define AFE_MSG_CODEC_CONFIG_ACK 0x0001 +#define AFE_MSG_CODEC_CONFIG_ACK_LEN \ + sizeof(struct afe_msg_codec_config_ack) + +#define AFE_MSG_CODEC_CONFIG_ENABLED 0x1 +#define AFE_MSG_CODEC_CONFIG_DISABLED 0xFFFF + +struct afe_msg_codec_config_ack { + uint16_t device_id; + uint16_t device_activity; +} __attribute__((packed)); + + +#endif diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_module_audplay.h b/arch/arm/mach-msm/qdsp5v2/adsp_module_audplay.h new file mode 100644 index 0000000000000..7a67dedc4b2bd --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/adsp_module_audplay.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + * + * 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 __ADSP_MODULE_AUDPLAY +#define __ADSP_MODULE_AUDPLAY + +#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL 0x0000 +#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_LEN \ + sizeof(struct audplay_cmd_bitstream_data_avail) + +/* Type specification of dec_data_avail message sent to AUDPLAYTASK +*/ +struct audplay_cmd_bitstream_data_avail{ + /*command ID*/ + unsigned int cmd_id; + + /* Decoder ID for which message is being sent */ + unsigned int decoder_id; + + /* Start address of data in ARM global memory */ + unsigned int buf_ptr; + + /* Number of 16-bit words of bit-stream data contiguously + * available at the above-mentioned address + */ + unsigned int buf_size; + + /* Partition number used by audPlayTask to communicate with DSP's RTOS + * kernel + */ + unsigned int partition_number; + +} __attribute__((packed)); + +#define AUDPLAY_CMD_CHANNEL_INFO 0x0001 +#define AUDPLAY_CMD_CHANNEL_INFO_LEN \ + sizeof(struct audplay_cmd_channel_info) + +struct audplay_cmd_channel_select { + unsigned int cmd_id; + unsigned int stream_id; + unsigned int channel_select; +} __attribute__((packed)); + +struct audplay_cmd_threshold_update { + unsigned int cmd_id; + unsigned int threshold_update; + unsigned int threshold_value; +} __attribute__((packed)); + +union audplay_cmd_channel_info { + struct audplay_cmd_channel_select ch_select; + struct audplay_cmd_threshold_update thr_update; +}; + +#define AUDPLAY_CMD_HPCM_BUF_CFG 0x0003 +#define AUDPLAY_CMD_HPCM_BUF_CFG_LEN \ + sizeof(struct audplay_cmd_hpcm_buf_cfg) + +struct audplay_cmd_hpcm_buf_cfg { + unsigned int cmd_id; + unsigned int hostpcm_config; + unsigned int feedback_frequency; + unsigned int byte_swap; + unsigned int max_buffers; + unsigned int partition_number; +} __attribute__((packed)); + +#define AUDPLAY_CMD_BUFFER_REFRESH 0x0004 +#define AUDPLAY_CMD_BUFFER_REFRESH_LEN \ + sizeof(struct audplay_cmd_buffer_update) + +struct audplay_cmd_buffer_refresh { + unsigned int cmd_id; + unsigned int num_buffers; + unsigned int buf_read_count; + unsigned int buf0_address; + unsigned int buf0_length; + unsigned int buf1_address; + unsigned int buf1_length; +} __attribute__((packed)); + +#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2 0x0005 +#define AUDPLAY_CMD_BITSTREAM_DATA_AVAIL_NT2_LEN \ + sizeof(struct audplay_cmd_bitstream_data_avail_nt2) + +/* Type specification of dec_data_avail message sent to AUDPLAYTASK + * for NT2 */ +struct audplay_cmd_bitstream_data_avail_nt2 { + /*command ID*/ + unsigned int cmd_id; + + /* Decoder ID for which message is being sent */ + unsigned int decoder_id; + + /* Start address of data in ARM global memory */ + unsigned int buf_ptr; + + /* Number of 16-bit words of bit-stream data contiguously + * available at the above-mentioned address + */ + unsigned int buf_size; + + /* Partition number used by audPlayTask to communicate with DSP's RTOS + * kernel + */ + unsigned int partition_number; + + /* bitstream write pointer */ + unsigned int dspBitstreamWritePtr; + +} __attribute__((packed)); + +#define AUDPLAY_CMD_OUTPORT_FLUSH 0x0006 + +struct audplay_cmd_outport_flush { + unsigned int cmd_id; +} __attribute__((packed)); + + +/* messages from dsp to apps */ + +#define AUDPLAY_MSG_DEC_NEEDS_DATA 0x0001 +#define AUDPLAY_MSG_DEC_NEEDS_DATA_MSG_LEN \ + sizeof(audplay_msg_dec_needs_data) + +struct audplay_msg_dec_needs_data { + /* reserved*/ + unsigned int dec_id; + + /*The read pointer offset of external memory till which bitstream + has been dmed in*/ + unsigned int adecDataReadPtrOffset; + + /*The buffer size of external memory. */ + unsigned int adecDataBufSize; + + unsigned int bitstream_free_len; + unsigned int bitstream_write_ptr; + unsigned int bitstream_buf_start; + unsigned int bitstream_buf_len; +} __attribute__((packed)); + +#define AUDPLAY_UP_STREAM_INFO 0x0003 +#define AUDPLAY_UP_STREAM_INFO_LEN \ + sizeof(struct audplay_msg_stream_info) + +struct audplay_msg_stream_info { + unsigned int decoder_id; + unsigned int channel_info; + unsigned int sample_freq; + unsigned int bitstream_info; + unsigned int bit_rate; +} __attribute__((packed)); + +#define AUDPLAY_MSG_BUFFER_UPDATE 0x0004 +#define AUDPLAY_MSG_BUFFER_UPDATE_LEN \ + sizeof(struct audplay_msg_buffer_update) + +struct audplay_msg_buffer_update { + unsigned int buffer_write_count; + unsigned int num_of_buffer; + unsigned int buf0_address; + unsigned int buf0_length; + unsigned int buf1_address; + unsigned int buf1_length; +} __attribute__((packed)); + +#define AUDPLAY_UP_OUTPORT_FLUSH_ACK 0x0005 + +#define ADSP_MESSAGE_ID 0xFFFF + +#endif diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_module_audpp.h b/arch/arm/mach-msm/qdsp5v2/adsp_module_audpp.h new file mode 100644 index 0000000000000..8f278cf69f5f7 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/adsp_module_audpp.h @@ -0,0 +1,1250 @@ +/* + * Copyright (c) 1992-2009, Code Aurora Forum. All rights reserved. + * + * 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 __ADSP_MODULE_AUDPP +#define __ADSP_MODULE_AUDPP + +/* + * ARM to AUDPPTASK Commands + * + * ARM uses three command queues to communicate with AUDPPTASK + * 1)uPAudPPCmd1Queue : Used for more frequent and shorter length commands + * Location : MEMA + * Buffer Size : 6 words + * No of buffers in a queue : 20 for gaming audio and 5 for other images + * 2)uPAudPPCmd2Queue : Used for commands which are not much lengthier + * Location : MEMA + * Buffer Size : 23 + * No of buffers in a queue : 2 + * 3)uPAudOOCmd3Queue : Used for lengthier and more frequent commands + * Location : MEMA + * Buffer Size : 145 + * No of buffers in a queue : 3 + */ + +/* + * Commands Related to uPAudPPCmd1Queue + */ + +/* + * Command Structure to enable or disable the active decoders + */ + +#define AUDPP_CMD_CFG_DEC_TYPE 0x0001 +#define AUDPP_CMD_CFG_DEC_TYPE_LEN sizeof(struct audpp_cmd_cfg_dec_type) + +/* Enable the decoder */ +#define AUDPP_CMD_DEC_TYPE_M 0x000F + +#define AUDPP_CMD_ENA_DEC_V 0x4000 +#define AUDPP_CMD_DIS_DEC_V 0x0000 +#define AUDPP_CMD_DEC_STATE_M 0x4000 + +#define AUDPP_CMD_UPDATDE_CFG_DEC 0x8000 +#define AUDPP_CMD_DONT_UPDATE_CFG_DEC 0x0000 + + +/* Type specification of cmd_cfg_dec */ + +struct audpp_cmd_cfg_dec_type { + unsigned short cmd_id; + unsigned short stream_id; + unsigned short dec_cfg; + unsigned short dm_mode; +} __attribute__((packed)); + +/* + * Command Structure to Pause , Resume and flushes the selected audio decoders + */ + +#define AUDPP_CMD_DEC_CTRL 0x0002 +#define AUDPP_CMD_DEC_CTRL_LEN sizeof(struct audpp_cmd_dec_ctrl) + +/* Decoder control commands for pause, resume and flush */ +#define AUDPP_CMD_FLUSH_V 0x2000 + +#define AUDPP_CMD_PAUSE_V 0x4000 +#define AUDPP_CMD_RESUME_V 0x0000 + +#define AUDPP_CMD_UPDATE_V 0x8000 +#define AUDPP_CMD_IGNORE_V 0x0000 + + +/* Type Spec for decoder control command*/ + +struct audpp_cmd_dec_ctrl{ + unsigned short cmd_id; + unsigned short stream_id; + unsigned short dec_ctrl; +} __attribute__((packed)); + +/* + * Command Structure to Configure the AVSync FeedBack Mechanism + */ + +#define AUDPP_CMD_AVSYNC 0x0003 +#define AUDPP_CMD_AVSYNC_LEN sizeof(struct audpp_cmd_avsync) + +struct audpp_cmd_avsync{ + unsigned short cmd_id; + unsigned short stream_id; + unsigned short interrupt_interval; + unsigned short sample_counter_dlsw; + unsigned short sample_counter_dmsw; + unsigned short sample_counter_msw; + unsigned short byte_counter_dlsw; + unsigned short byte_counter_dmsw; + unsigned short byte_counter_msw; +} __attribute__((packed)); + +/* + * Command Structure to enable or disable(sleep) the AUDPPTASK + */ + +#define AUDPP_CMD_CFG 0x0004 +#define AUDPP_CMD_CFG_LEN sizeof(struct audpp_cmd_cfg) + +#define AUDPP_CMD_CFG_SLEEP 0x0000 +#define AUDPP_CMD_CFG_ENABLE 0xFFFF + +struct audpp_cmd_cfg { + unsigned short cmd_id; + unsigned short cfg; +} __attribute__((packed)); + +/* + * Command Structure to Inject or drop the specified no of samples + */ + +#define AUDPP_CMD_ADJUST_SAMP 0x0005 +#define AUDPP_CMD_ADJUST_SAMP_LEN sizeof(struct audpp_cmd_adjust_samp) + +#define AUDPP_CMD_SAMP_DROP -1 +#define AUDPP_CMD_SAMP_INSERT 0x0001 + +#define AUDPP_CMD_NUM_SAMPLES 0x0001 + +struct audpp_cmd_adjust_samp { + unsigned short cmd_id; + unsigned short object_no; + signed short sample_insert_or_drop; + unsigned short num_samples; +} __attribute__((packed)); + +/* + * Command Structure to Configure AVSync Feedback Mechanism + */ + +#define AUDPP_CMD_ROUTING_MODE 0x0007 +#define AUDPP_CMD_ROUTING_MODE_LEN \ +sizeof(struct audpp_cmd_routing_mode) + +struct audpp_cmd_routing_mode { + unsigned short cmd_id; + unsigned short object_number; + unsigned short routing_mode; +} __attribute__((packed)); + +/* + * Commands Related to uPAudPPCmd2Queue + */ + +/* + * Command Structure to configure Per decoder Parameters (Common) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS 0x0000 +#define AUDPP_CMD_CFG_ADEC_PARAMS_COMMON_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_common) + +#define AUDPP_CMD_STATUS_MSG_FLAG_ENA_FCM 0x4000 +#define AUDPP_CMD_STATUS_MSG_FLAG_DIS_FCM 0x0000 + +#define AUDPP_CMD_STATUS_MSG_FLAG_ENA_DCM 0x8000 +#define AUDPP_CMD_STATUS_MSG_FLAG_DIS_DCM 0x0000 + +/* Sampling frequency*/ +#define AUDPP_CMD_SAMP_RATE_96000 0x0000 +#define AUDPP_CMD_SAMP_RATE_88200 0x0001 +#define AUDPP_CMD_SAMP_RATE_64000 0x0002 +#define AUDPP_CMD_SAMP_RATE_48000 0x0003 +#define AUDPP_CMD_SAMP_RATE_44100 0x0004 +#define AUDPP_CMD_SAMP_RATE_32000 0x0005 +#define AUDPP_CMD_SAMP_RATE_24000 0x0006 +#define AUDPP_CMD_SAMP_RATE_22050 0x0007 +#define AUDPP_CMD_SAMP_RATE_16000 0x0008 +#define AUDPP_CMD_SAMP_RATE_12000 0x0009 +#define AUDPP_CMD_SAMP_RATE_11025 0x000A +#define AUDPP_CMD_SAMP_RATE_8000 0x000B + + +/* + * Type specification of cmd_adec_cfg sent to all decoder + */ + +struct audpp_cmd_cfg_adec_params_common { + unsigned short cmd_id; + unsigned short dec_id; + unsigned short length; + unsigned short reserved; + unsigned short input_sampling_frequency; +} __attribute__((packed)); + +/* + * Command Structure to configure Per decoder Parameters (Wav) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_WAV_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_wav) + + +#define AUDPP_CMD_WAV_STEREO_CFG_MONO 0x0001 +#define AUDPP_CMD_WAV_STEREO_CFG_STEREO 0x0002 + +#define AUDPP_CMD_WAV_PCM_WIDTH_8 0x0000 +#define AUDPP_CMD_WAV_PCM_WIDTH_16 0x0001 +#define AUDPP_CMD_WAV_PCM_WIDTH_24 0x0002 + +struct audpp_cmd_cfg_adec_params_wav { + struct audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; + unsigned short pcm_width; + unsigned short sign; +} __attribute__((packed)); + +/* + * Command Structure for CMD_CFG_DEV_MIXER + */ + +#define AUDPP_CMD_CFG_DEV_MIXER_PARAMS_LEN \ + sizeof(struct audpp_cmd_cfg_dev_mixer_params) + +#define AUDPP_CMD_CFG_DEV_MIXER 0x0008 + +#define AUDPP_CMD_CFG_DEV_MIXER_DEV_NONE 0x0000 +#define AUDPP_CMD_CFG_DEV_MIXER_DEV_0 0x0001 +#define AUDPP_CMD_CFG_DEV_MIXER_DEV_1 0x0002 +#define AUDPP_CMD_CFG_DEV_MIXER_DEV_2 0x0004 +#define AUDPP_CMD_CFG_DEV_MIXER_DEV_3 0x0008 +#define AUDPP_CMD_CFG_DEV_MIXER_DEV_4 0x0010 + +struct audpp_cmd_cfg_dev_mixer_params { + unsigned short cmd_id; + unsigned short stream_id; + unsigned short mixer_cmd; +} __attribute__((packed)); + + +/* + * Command Structure to configure Per decoder Parameters (ADPCM) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_ADPCM_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_adpcm) + + +#define AUDPP_CMD_ADPCM_STEREO_CFG_MONO 0x0001 +#define AUDPP_CMD_ADPCM_STEREO_CFG_STEREO 0x0002 + +struct audpp_cmd_cfg_adec_params_adpcm { + struct audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; + unsigned short block_size; +} __attribute__((packed)); + +/* + * Command Structure to configure Per decoder Parameters (WMA) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_WMA_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_wma) + +struct audpp_cmd_cfg_adec_params_wma { + struct audpp_cmd_cfg_adec_params_common common; + unsigned short armdatareqthr; + unsigned short channelsdecoded; + unsigned short wmabytespersec; + unsigned short wmasamplingfreq; + unsigned short wmaencoderopts; +} __attribute__((packed)); + + +/* + * Command Structure to configure Per decoder Parameters (MP3) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_MP3_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_mp3) + +struct audpp_cmd_cfg_adec_params_mp3 { + struct audpp_cmd_cfg_adec_params_common common; +} __attribute__((packed)); + + +/* + * Command Structure to configure Per decoder Parameters (AAC) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_AAC_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_aac) + + +#define AUDPP_CMD_AAC_FORMAT_ADTS -1 +#define AUDPP_CMD_AAC_FORMAT_RAW 0x0000 +#define AUDPP_CMD_AAC_FORMAT_PSUEDO_RAW 0x0001 +#define AUDPP_CMD_AAC_FORMAT_LOAS 0x0002 + +#define AUDPP_CMD_AAC_AUDIO_OBJECT_LC 0x0002 +#define AUDPP_CMD_AAC_AUDIO_OBJECT_LTP 0x0004 +#define AUDPP_CMD_AAC_AUDIO_OBJECT_ERLC 0x0011 + +#define AUDPP_CMD_AAC_SBR_ON_FLAG_ON 0x0001 +#define AUDPP_CMD_AAC_SBR_ON_FLAG_OFF 0x0000 + +#define AUDPP_CMD_AAC_SBR_PS_ON_FLAG_ON 0x0001 +#define AUDPP_CMD_AAC_SBR_PS_ON_FLAG_OFF 0x0000 + +struct audpp_cmd_cfg_adec_params_aac { + struct audpp_cmd_cfg_adec_params_common common; + signed short format; + unsigned short audio_object; + unsigned short ep_config; + unsigned short aac_section_data_resilience_flag; + unsigned short aac_scalefactor_data_resilience_flag; + unsigned short aac_spectral_data_resilience_flag; + unsigned short sbr_on_flag; + unsigned short sbr_ps_on_flag; + unsigned short channel_configuration; +} __attribute__((packed)); + +/* + * Command Structure to configure Per decoder Parameters (V13K) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_V13K_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_v13k) + + +#define AUDPP_CMD_STEREO_CFG_MONO 0x0001 +#define AUDPP_CMD_STEREO_CFG_STEREO 0x0002 + +struct audpp_cmd_cfg_adec_params_v13k { + struct audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; +} __attribute__((packed)); + +#define AUDPP_CMD_CFG_ADEC_PARAMS_EVRC_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_evrc) + +struct audpp_cmd_cfg_adec_params_evrc { + struct audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; +} __attribute__ ((packed)); + +/* + * Command Structure to configure Per decoder Parameters (AMRWB) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_AMRWB_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_amrwb) + +struct audpp_cmd_cfg_adec_params_amrwb { + struct audpp_cmd_cfg_adec_params_common common; + unsigned short stereo_cfg; +} __attribute__((packed)); + +/* + * Command Structure to configure Per decoder Parameters (WMAPRO) + */ + +#define AUDPP_CMD_CFG_ADEC_PARAMS_WMAPRO_LEN \ + sizeof(struct audpp_cmd_cfg_adec_params_wmapro) + +struct audpp_cmd_cfg_adec_params_wmapro { + struct audpp_cmd_cfg_adec_params_common common; + unsigned short armdatareqthr; + uint8_t validbitspersample; + uint8_t numchannels; + unsigned short formattag; + unsigned short samplingrate; + unsigned short avgbytespersecond; + unsigned short asfpacketlength; + unsigned short channelmask; + unsigned short encodeopt; + unsigned short advancedencodeopt; + uint32_t advancedencodeopt2; +} __attribute__((packed)); + +/* + * Command Structure to configure the HOST PCM interface + */ + +#define AUDPP_CMD_PCM_INTF 0x0001 +#define AUDPP_CMD_PCM_INTF_2 0x0002 +#define AUDPP_CMD_PCM_INTF_LEN sizeof(struct audpp_cmd_pcm_intf) + +#define AUDPP_CMD_PCM_INTF_MONO_V 0x0001 +#define AUDPP_CMD_PCM_INTF_STEREO_V 0x0002 + +/* These two values differentiate the two types of commands that could be issued + * Interface configuration command and Buffer update command */ + +#define AUDPP_CMD_PCM_INTF_CONFIG_CMD_V 0x0000 +#define AUDPP_CMD_PCM_INTF_BUFFER_CMD_V -1 + +#define AUDPP_CMD_PCM_INTF_RX_ENA_M 0x000F +#define AUDPP_CMD_PCM_INTF_RX_ENA_ARMTODSP_V 0x0008 +#define AUDPP_CMD_PCM_INTF_RX_ENA_DSPTOARM_V 0x0004 + +/* These flags control the enabling and disabling of the interface together + * with host interface bit mask. */ + +#define AUDPP_CMD_PCM_INTF_ENA_V -1 +#define AUDPP_CMD_PCM_INTF_DIS_V 0x0000 + + +#define AUDPP_CMD_PCM_INTF_FULL_DUPLEX 0x0 +#define AUDPP_CMD_PCM_INTF_HALF_DUPLEX_TODSP 0x1 + + +#define AUDPP_CMD_PCM_INTF_OBJECT_NUM 0x5 +#define AUDPP_CMD_PCM_INTF_COMMON_OBJECT_NUM 0x6 + +struct audpp_cmd_pcm_intf { + unsigned short cmd_id; + unsigned short stream; + unsigned short stream_id; + signed short config; + unsigned short intf_type; + + /* DSP -> ARM Configuration */ + unsigned short read_buf1LSW; + unsigned short read_buf1MSW; + unsigned short read_buf1_len; + + unsigned short read_buf2LSW; + unsigned short read_buf2MSW; + unsigned short read_buf2_len; + /* 0:HOST_PCM_INTF disable + ** 0xFFFF: HOST_PCM_INTF enable + */ + signed short dsp_to_arm_flag; + unsigned short partition_number; + + /* ARM -> DSP Configuration */ + unsigned short write_buf1LSW; + unsigned short write_buf1MSW; + unsigned short write_buf1_len; + + unsigned short write_buf2LSW; + unsigned short write_buf2MSW; + unsigned short write_buf2_len; + + /* 0:HOST_PCM_INTF disable + ** 0xFFFF: HOST_PCM_INTF enable + */ + signed short arm_to_rx_flag; + unsigned short weight_decoder_to_rx; + unsigned short weight_arm_to_rx; + + unsigned short partition_number_arm_to_dsp; + unsigned short sample_rate; + unsigned short channel_mode; +} __attribute__((packed)); + +/* + ** BUFFER UPDATE COMMAND + */ +#define AUDPP_CMD_PCM_INTF_SEND_BUF_PARAMS_LEN \ + sizeof(struct audpp_cmd_pcm_intf_send_buffer) + +struct audpp_cmd_pcm_intf_send_buffer { + unsigned short cmd_id; + unsigned short stream; + unsigned short stream_id; + /* set config = 0xFFFF for configuration*/ + signed short config; + unsigned short intf_type; + unsigned short dsp_to_arm_buf_id; + unsigned short arm_to_dsp_buf_id; + unsigned short arm_to_dsp_buf_len; +} __attribute__((packed)); + + +/* + * Commands Related to uPAudPPCmd3Queue + */ + +/* + * Command Structure to configure post processing params (Commmon) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS 0x0000 +#define AUDPP_CMD_CFG_OBJECT_PARAMS_COMMON_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_common) + +#define AUDPP_CMD_OBJ0_UPDATE 0x8000 +#define AUDPP_CMD_OBJ0_DONT_UPDATE 0x0000 + + +#define AUDPP_CMD_OBJ2_UPDATE 0x8000 +#define AUDPP_CMD_OBJ2_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_OBJ3_UPDATE 0x8000 +#define AUDPP_CMD_OBJ3_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_OBJ4_UPDATE 0x8000 +#define AUDPP_CMD_OBJ4_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_HPCM_UPDATE 0x8000 +#define AUDPP_CMD_HPCM_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_COMMON_CFG_UPDATE 0x8000 +#define AUDPP_CMD_COMMON_CFG_DONT_UPDATE 0x0000 + +#define AUDPP_CMD_POPP_STREAM 0xFFFF +#define AUDPP_CMD_COPP_STREAM 0x0000 + +struct audpp_cmd_cfg_object_params_common{ + unsigned short cmd_id; + unsigned short stream; + unsigned short stream_id; + unsigned short obj_cfg; + unsigned short command_type; +} __attribute__((packed)); + +/* + * Command Structure to configure post processing params (Volume) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_VOLUME_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_volume) + +struct audpp_cmd_cfg_object_params_volume { + struct audpp_cmd_cfg_object_params_common common; + unsigned short volume; + unsigned short pan; +} __attribute__((packed)); + +/* + * Command Structure to configure post processing params (PCM Filter) + */ + +struct numerator { + unsigned short numerator_b0_filter_lsw; + unsigned short numerator_b0_filter_msw; + unsigned short numerator_b1_filter_lsw; + unsigned short numerator_b1_filter_msw; + unsigned short numerator_b2_filter_lsw; + unsigned short numerator_b2_filter_msw; +} __attribute__((packed)); + +struct denominator { + unsigned short denominator_a0_filter_lsw; + unsigned short denominator_a0_filter_msw; + unsigned short denominator_a1_filter_lsw; + unsigned short denominator_a1_filter_msw; +} __attribute__((packed)); + +struct shift_factor { + unsigned short shift_factor_0; +} __attribute__((packed)); + +struct pan { + unsigned short pan_filter_0; +} __attribute__((packed)); + +struct filter_1 { + struct numerator numerator_filter; + struct denominator denominator_filter; + struct shift_factor shift_factor_filter; + struct pan pan_filter; +} __attribute__((packed)); + +struct filter_2 { + struct numerator numerator_filter[2]; + struct denominator denominator_filter[2]; + struct shift_factor shift_factor_filter[2]; + struct pan pan_filter[2]; +} __attribute__((packed)); + +struct filter_3 { + struct numerator numerator_filter[3]; + struct denominator denominator_filter[3]; + struct shift_factor shift_factor_filter[3]; + struct pan pan_filter[3]; +} __attribute__((packed)); + +struct filter_4 { + struct numerator numerator_filter[4]; + struct denominator denominator_filter[4]; + struct shift_factor shift_factor_filter[4]; + struct pan pan_filter[4]; +} __attribute__((packed)); + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_PCM_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_pcm) + + +struct audpp_cmd_cfg_object_params_pcm { + struct audpp_cmd_cfg_object_params_common common; + signed short active_flag; + unsigned short num_bands; + union { + struct filter_1 filter_1_params; + struct filter_2 filter_2_params; + struct filter_3 filter_3_params; + struct filter_4 filter_4_params; + } __attribute__((packed)) params_filter; +} __attribute__((packed)); + + +/* + * Command Structure to configure post processing parameters (equalizer) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_EQALIZER_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_eqalizer) + +struct eq_numerator { + unsigned short numerator_coeff_0_lsw; + unsigned short numerator_coeff_0_msw; + unsigned short numerator_coeff_1_lsw; + unsigned short numerator_coeff_1_msw; + unsigned short numerator_coeff_2_lsw; + unsigned short numerator_coeff_2_msw; +} __attribute__((packed)); + +struct eq_denominator { + unsigned short denominator_coeff_0_lsw; + unsigned short denominator_coeff_0_msw; + unsigned short denominator_coeff_1_lsw; + unsigned short denominator_coeff_1_msw; +} __attribute__((packed)); + +struct eq_shiftfactor { + unsigned short shift_factor; +} __attribute__((packed)); + +struct eq_coeff_1 { + struct eq_numerator numerator; + struct eq_denominator denominator; + struct eq_shiftfactor shiftfactor; +} __attribute__((packed)); + +struct eq_coeff_2 { + struct eq_numerator numerator[2]; + struct eq_denominator denominator[2]; + struct eq_shiftfactor shiftfactor[2]; +} __attribute__((packed)); + +struct eq_coeff_3 { + struct eq_numerator numerator[3]; + struct eq_denominator denominator[3]; + struct eq_shiftfactor shiftfactor[3]; +} __attribute__((packed)); + +struct eq_coeff_4 { + struct eq_numerator numerator[4]; + struct eq_denominator denominator[4]; + struct eq_shiftfactor shiftfactor[4]; +} __attribute__((packed)); + +struct eq_coeff_5 { + struct eq_numerator numerator[5]; + struct eq_denominator denominator[5]; + struct eq_shiftfactor shiftfactor[5]; +} __attribute__((packed)); + +struct eq_coeff_6 { + struct eq_numerator numerator[6]; + struct eq_denominator denominator[6]; + struct eq_shiftfactor shiftfactor[6]; +} __attribute__((packed)); + +struct eq_coeff_7 { + struct eq_numerator numerator[7]; + struct eq_denominator denominator[7]; + struct eq_shiftfactor shiftfactor[7]; +} __attribute__((packed)); + +struct eq_coeff_8 { + struct eq_numerator numerator[8]; + struct eq_denominator denominator[8]; + struct eq_shiftfactor shiftfactor[8]; +} __attribute__((packed)); + +struct eq_coeff_9 { + struct eq_numerator numerator[9]; + struct eq_denominator denominator[9]; + struct eq_shiftfactor shiftfactor[9]; +} __attribute__((packed)); + +struct eq_coeff_10 { + struct eq_numerator numerator[10]; + struct eq_denominator denominator[10]; + struct eq_shiftfactor shiftfactor[10]; +} __attribute__((packed)); + +struct eq_coeff_11 { + struct eq_numerator numerator[11]; + struct eq_denominator denominator[11]; + struct eq_shiftfactor shiftfactor[11]; +} __attribute__((packed)); + +struct eq_coeff_12 { + struct eq_numerator numerator[12]; + struct eq_denominator denominator[12]; + struct eq_shiftfactor shiftfactor[12]; +} __attribute__((packed)); + + +struct audpp_cmd_cfg_object_params_eqalizer { + struct audpp_cmd_cfg_object_params_common common; + signed short eq_flag; + unsigned short num_bands; + union { + struct eq_coeff_1 eq_coeffs_1; + struct eq_coeff_2 eq_coeffs_2; + struct eq_coeff_3 eq_coeffs_3; + struct eq_coeff_4 eq_coeffs_4; + struct eq_coeff_5 eq_coeffs_5; + struct eq_coeff_6 eq_coeffs_6; + struct eq_coeff_7 eq_coeffs_7; + struct eq_coeff_8 eq_coeffs_8; + struct eq_coeff_9 eq_coeffs_9; + struct eq_coeff_10 eq_coeffs_10; + struct eq_coeff_11 eq_coeffs_11; + struct eq_coeff_12 eq_coeffs_12; + } __attribute__((packed)) eq_coeff; +} __attribute__((packed)); + +/* + * Command Structure to configure post processing parameters (ADRC) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_ADRC_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_adrc) + + +#define AUDPP_CMD_ADRC_FLAG_DIS 0x0000 +#define AUDPP_CMD_ADRC_FLAG_ENA -1 + +struct audpp_cmd_cfg_object_params_adrc { + struct audpp_cmd_cfg_object_params_common common; + signed short adrc_flag; + unsigned short compression_th; + unsigned short compression_slope; + unsigned short rms_time; + unsigned short attack_const_lsw; + unsigned short attack_const_msw; + unsigned short release_const_lsw; + unsigned short release_const_msw; + unsigned short adrc_delay; +}; + +/* + * Command Structure to configure post processing parameters (MB - ADRC) + */ + +#define AUDPP_MAX_MBADRC_BANDS 5 + +struct adrc_config { + uint16_t subband_enable; + uint16_t adrc_sub_mute; + uint16_t rms_time; + uint16_t compression_th; + uint16_t compression_slope; + uint16_t attack_const_lsw; + uint16_t attack_const_msw; + uint16_t release_const_lsw; + uint16_t release_const_msw; + uint16_t makeup_gain; +}; + +struct audpp_cmd_cfg_object_params_mbadrc { + struct audpp_cmd_cfg_object_params_common common; + uint16_t enable; + uint16_t num_bands; + uint16_t down_samp_level; + uint16_t adrc_delay; + uint16_t ext_buf_size; + uint16_t ext_partition; + uint16_t ext_buf_msw; + uint16_t ext_buf_lsw; + struct adrc_config adrc_band[AUDPP_MAX_MBADRC_BANDS]; +} __attribute__((packed)); + +/* + * Command Structure to configure post processing parameters(Spectrum Analizer) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_SPECTRAM_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_spectram) + + +struct audpp_cmd_cfg_object_params_spectram { + struct audpp_cmd_cfg_object_params_common common; + unsigned short sample_interval; + unsigned short num_coeff; +} __attribute__((packed)); + +/* + * Command Structure to configure post processing parameters (QConcert) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_QCONCERT_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_qconcert) + + +#define AUDPP_CMD_QCON_ENA_FLAG_ENA -1 +#define AUDPP_CMD_QCON_ENA_FLAG_DIS 0x0000 + +#define AUDPP_CMD_QCON_OP_MODE_HEADPHONE -1 +#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_FRONT 0x0000 +#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_SIDE 0x0001 +#define AUDPP_CMD_QCON_OP_MODE_SPEAKER_DESKTOP 0x0002 + +#define AUDPP_CMD_QCON_GAIN_UNIT 0x7FFF +#define AUDPP_CMD_QCON_GAIN_SIX_DB 0x4027 + + +#define AUDPP_CMD_QCON_EXPANSION_MAX 0x7FFF + + +struct audpp_cmd_cfg_object_params_qconcert { + struct audpp_cmd_cfg_object_params_common common; + signed short enable_flag; + signed short op_mode; + signed short gain; + signed short expansion; + signed short delay; + unsigned short stages_per_mode; + unsigned short reverb_enable; + unsigned short decay_msw; + unsigned short decay_lsw; + unsigned short decay_time_ratio_msw; + unsigned short decay_time_ratio_lsw; + unsigned short reflection_delay_time; + unsigned short late_reverb_gain; + unsigned short late_reverb_delay; + unsigned short delay_buff_size_msw; + unsigned short delay_buff_size_lsw; + unsigned short partition_num; + unsigned short delay_buff_start_msw; + unsigned short delay_buff_start_lsw; +} __attribute__((packed)); + +/* + * Command Structure to configure post processing parameters (Side Chain) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_SIDECHAIN_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_sidechain) + + +#define AUDPP_CMD_SIDECHAIN_ACTIVE_FLAG_DIS 0x0000 +#define AUDPP_CMD_SIDECHAIN_ACTIVE_FLAG_ENA -1 + +struct audpp_cmd_cfg_object_params_sidechain { + struct audpp_cmd_cfg_object_params_common common; + signed short active_flag; + unsigned short num_bands; + union { + struct filter_1 filter_1_params; + struct filter_2 filter_2_params; + struct filter_3 filter_3_params; + struct filter_4 filter_4_params; + } __attribute__((packed)) params_filter; +} __attribute__((packed)); + + +/* + * Command Structure to configure post processing parameters (QAFX) + */ + +#define AUDPP_CMD_CFG_OBJECT_PARAMS_QAFX_LEN \ + sizeof(struct audpp_cmd_cfg_object_params_qafx) + +#define AUDPP_CMD_QAFX_ENA_DISA 0x0000 +#define AUDPP_CMD_QAFX_ENA_ENA_CFG -1 +#define AUDPP_CMD_QAFX_ENA_DIS_CFG 0x0001 + +#define AUDPP_CMD_QAFX_CMD_TYPE_ENV 0x0100 +#define AUDPP_CMD_QAFX_CMD_TYPE_OBJ 0x0010 +#define AUDPP_CMD_QAFX_CMD_TYPE_QUERY 0x1000 + +#define AUDPP_CMD_QAFX_CMDS_ENV_OP_MODE 0x0100 +#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_POS 0x0101 +#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_ORI 0x0102 +#define AUDPP_CMD_QAFX_CMDS_ENV_LIS_VEL 0X0103 +#define AUDPP_CMD_QAFX_CMDS_ENV_ENV_RES 0x0107 + +#define AUDPP_CMD_QAFX_CMDS_OBJ_SAMP_FREQ 0x0010 +#define AUDPP_CMD_QAFX_CMDS_OBJ_VOL 0x0011 +#define AUDPP_CMD_QAFX_CMDS_OBJ_DIST 0x0012 +#define AUDPP_CMD_QAFX_CMDS_OBJ_POS 0x0013 +#define AUDPP_CMD_QAFX_CMDS_OBJ_VEL 0x0014 + + +struct audpp_cmd_cfg_object_params_qafx { + struct audpp_cmd_cfg_object_params_common common; + signed short enable; + unsigned short command_type; + unsigned short num_commands; + unsigned short commands; +} __attribute__((packed)); + +/* + * Command Structure to enable , disable or configure the reverberation effect + * (REVERB) (Common) + */ + +#define AUDPP_CMD_REVERB_CONFIG 0x0001 +#define AUDPP_CMD_REVERB_CONFIG_COMMON_LEN \ + sizeof(struct audpp_cmd_reverb_config_common) + +#define AUDPP_CMD_ENA_ENA 0xFFFF +#define AUDPP_CMD_ENA_DIS 0x0000 +#define AUDPP_CMD_ENA_CFG 0x0001 + +#define AUDPP_CMD_CMD_TYPE_ENV 0x0104 +#define AUDPP_CMD_CMD_TYPE_OBJ 0x0015 +#define AUDPP_CMD_CMD_TYPE_QUERY 0x1000 + + +struct audpp_cmd_reverb_config_common { + unsigned short cmd_id; + unsigned short enable; + unsigned short cmd_type; +} __attribute__((packed)); + +/* + * Command Structure to enable , disable or configure the reverberation effect + * (ENV-0x0104) + */ + +#define AUDPP_CMD_REVERB_CONFIG_ENV_104_LEN \ + sizeof(struct audpp_cmd_reverb_config_env_104) + +struct audpp_cmd_reverb_config_env_104 { + struct audpp_cmd_reverb_config_common common; + unsigned short env_gain; + unsigned short decay_msw; + unsigned short decay_lsw; + unsigned short decay_timeratio_msw; + unsigned short decay_timeratio_lsw; + unsigned short delay_time; + unsigned short reverb_gain; + unsigned short reverb_delay; +} __attribute__((packed)); + +/* + * Command Structure to enable , disable or configure the reverberation effect + * (ENV-0x0015) + */ + +#define AUDPP_CMD_REVERB_CONFIG_ENV_15_LEN \ + sizeof(struct audpp_cmd_reverb_config_env_15) + +struct audpp_cmd_reverb_config_env_15 { + struct audpp_cmd_reverb_config_common common; + unsigned short object_num; + unsigned short absolute_gain; +} __attribute__((packed)); + + +/* messages from dsp to apps */ + + +/* + * AUDPPTASK uses audPPuPRlist to send messages to the ARM + * Location : MEMA + * Buffer Size : 45 + * No of Buffers in a queue : 5 for gaming audio and 1 for other images + */ + +/* + * MSG to Informs the ARM os Success/Failure of bringing up the decoder + */ + +#define AUDPP_MSG_STATUS_MSG 0x0001 +#define AUDPP_MSG_STATUS_MSG_LEN \ + sizeof(struct audpp_msg_status_msg) + +#define AUDPP_MSG_STATUS_SLEEP 0x0000 +#define AUDPP_MSG_STATUS_INIT 0x0001 +#define AUDPP_MSG_STATUS_CFG 0x0002 +#define AUDPP_MSG_STATUS_PLAY 0x0003 + +#define AUDPP_MSG_REASON_NONE 0x0000 +#define AUDPP_MSG_REASON_MEM 0x0001 +#define AUDPP_MSG_REASON_NODECODER 0x0002 + +struct audpp_msg_status_msg { + unsigned short dec_id; + unsigned short status; + unsigned short reason; +} __attribute__((packed)); + +/* + * MSG to communicate the spectrum analyzer output bands to the ARM + */ +#define AUDPP_MSG_SPA_BANDS 0x0002 +#define AUDPP_MSG_SPA_BANDS_LEN \ + sizeof(struct audpp_msg_spa_bands) + +struct audpp_msg_spa_bands { + unsigned short current_object; + unsigned short spa_band_1; + unsigned short spa_band_2; + unsigned short spa_band_3; + unsigned short spa_band_4; + unsigned short spa_band_5; + unsigned short spa_band_6; + unsigned short spa_band_7; + unsigned short spa_band_8; + unsigned short spa_band_9; + unsigned short spa_band_10; + unsigned short spa_band_11; + unsigned short spa_band_12; + unsigned short spa_band_13; + unsigned short spa_band_14; + unsigned short spa_band_15; + unsigned short spa_band_16; + unsigned short spa_band_17; + unsigned short spa_band_18; + unsigned short spa_band_19; + unsigned short spa_band_20; + unsigned short spa_band_21; + unsigned short spa_band_22; + unsigned short spa_band_23; + unsigned short spa_band_24; + unsigned short spa_band_25; + unsigned short spa_band_26; + unsigned short spa_band_27; + unsigned short spa_band_28; + unsigned short spa_band_29; + unsigned short spa_band_30; + unsigned short spa_band_31; + unsigned short spa_band_32; +} __attribute__((packed)); + +/* + * MSG to communicate the PCM I/O buffer status to ARM + */ +#define AUDPP_MSG_HOST_PCM_INTF_MSG 0x0003 +#define AUDPP_MSG_HOST_PCM_INTF_MSG_LEN \ + sizeof(struct audpp_msg_host_pcm_intf_msg) + +#define AUDPP_MSG_HOSTPCM_ID_TX_ARM 0x0000 +#define AUDPP_MSG_HOSTPCM_ID_ARM_TX 0x0001 +#define AUDPP_MSG_HOSTPCM_ID_RX_ARM 0x0002 +#define AUDPP_MSG_HOSTPCM_ID_ARM_RX 0x0003 + +#define AUDPP_MSG_SAMP_FREQ_INDX_96000 0x0000 +#define AUDPP_MSG_SAMP_FREQ_INDX_88200 0x0001 +#define AUDPP_MSG_SAMP_FREQ_INDX_64000 0x0002 +#define AUDPP_MSG_SAMP_FREQ_INDX_48000 0x0003 +#define AUDPP_MSG_SAMP_FREQ_INDX_44100 0x0004 +#define AUDPP_MSG_SAMP_FREQ_INDX_32000 0x0005 +#define AUDPP_MSG_SAMP_FREQ_INDX_24000 0x0006 +#define AUDPP_MSG_SAMP_FREQ_INDX_22050 0x0007 +#define AUDPP_MSG_SAMP_FREQ_INDX_16000 0x0008 +#define AUDPP_MSG_SAMP_FREQ_INDX_12000 0x0009 +#define AUDPP_MSG_SAMP_FREQ_INDX_11025 0x000A +#define AUDPP_MSG_SAMP_FREQ_INDX_8000 0x000B + +#define AUDPP_MSG_CHANNEL_MODE_MONO 0x0001 +#define AUDPP_MSG_CHANNEL_MODE_STEREO 0x0002 + +struct audpp_msg_host_pcm_intf_msg { + unsigned short obj_num; + unsigned short numbers_of_samples; + unsigned short host_pcm_id; + unsigned short buf_indx; + unsigned short samp_freq_indx; + unsigned short channel_mode; +} __attribute__((packed)); + + +/* + * MSG to communicate 3D position of the source and listener , source volume + * source rolloff, source orientation + */ + +#define AUDPP_MSG_QAFX_POS 0x0004 +#define AUDPP_MSG_QAFX_POS_LEN \ + sizeof(struct audpp_msg_qafx_pos) + +struct audpp_msg_qafx_pos { + unsigned short current_object; + unsigned short x_pos_lis_msw; + unsigned short x_pos_lis_lsw; + unsigned short y_pos_lis_msw; + unsigned short y_pos_lis_lsw; + unsigned short z_pos_lis_msw; + unsigned short z_pos_lis_lsw; + unsigned short x_fwd_msw; + unsigned short x_fwd_lsw; + unsigned short y_fwd_msw; + unsigned short y_fwd_lsw; + unsigned short z_fwd_msw; + unsigned short z_fwd_lsw; + unsigned short x_up_msw; + unsigned short x_up_lsw; + unsigned short y_up_msw; + unsigned short y_up_lsw; + unsigned short z_up_msw; + unsigned short z_up_lsw; + unsigned short x_vel_lis_msw; + unsigned short x_vel_lis_lsw; + unsigned short y_vel_lis_msw; + unsigned short y_vel_lis_lsw; + unsigned short z_vel_lis_msw; + unsigned short z_vel_lis_lsw; + unsigned short threed_enable_flag; + unsigned short volume; + unsigned short x_pos_source_msw; + unsigned short x_pos_source_lsw; + unsigned short y_pos_source_msw; + unsigned short y_pos_source_lsw; + unsigned short z_pos_source_msw; + unsigned short z_pos_source_lsw; + unsigned short max_dist_0_msw; + unsigned short max_dist_0_lsw; + unsigned short min_dist_0_msw; + unsigned short min_dist_0_lsw; + unsigned short roll_off_factor; + unsigned short mute_after_max_flag; + unsigned short x_vel_source_msw; + unsigned short x_vel_source_lsw; + unsigned short y_vel_source_msw; + unsigned short y_vel_source_lsw; + unsigned short z_vel_source_msw; + unsigned short z_vel_source_lsw; +} __attribute__((packed)); + +/* + * MSG to provide AVSYNC feedback from DSP to ARM + */ + +#define AUDPP_MSG_AVSYNC_MSG 0x0005 +#define AUDPP_MSG_AVSYNC_MSG_LEN \ + sizeof(struct audpp_msg_avsync_msg) + +struct audpp_msg_avsync_msg { + unsigned short active_flag; + unsigned short num_samples_counter0_HSW; + unsigned short num_samples_counter0_MSW; + unsigned short num_samples_counter0_LSW; + unsigned short num_bytes_counter0_HSW; + unsigned short num_bytes_counter0_MSW; + unsigned short num_bytes_counter0_LSW; + unsigned short samp_freq_obj_0; + unsigned short samp_freq_obj_1; + unsigned short samp_freq_obj_2; + unsigned short samp_freq_obj_3; + unsigned short samp_freq_obj_4; + unsigned short samp_freq_obj_5; + unsigned short samp_freq_obj_6; + unsigned short samp_freq_obj_7; + unsigned short samp_freq_obj_8; + unsigned short samp_freq_obj_9; + unsigned short samp_freq_obj_10; + unsigned short samp_freq_obj_11; + unsigned short samp_freq_obj_12; + unsigned short samp_freq_obj_13; + unsigned short samp_freq_obj_14; + unsigned short samp_freq_obj_15; + unsigned short num_samples_counter4_HSW; + unsigned short num_samples_counter4_MSW; + unsigned short num_samples_counter4_LSW; + unsigned short num_bytes_counter4_HSW; + unsigned short num_bytes_counter4_MSW; + unsigned short num_bytes_counter4_LSW; +} __attribute__((packed)); + +/* + * MSG to provide PCM DMA Missed feedback from the DSP to ARM + */ + +#define AUDPP_MSG_PCMDMAMISSED 0x0006 +#define AUDPP_MSG_PCMDMAMISSED_LEN \ + sizeof(struct audpp_msg_pcmdmamissed); + +struct audpp_msg_pcmdmamissed { + /* + ** Bit 0 0 = PCM DMA not missed for object 0 + ** 1 = PCM DMA missed for object0 + ** Bit 1 0 = PCM DMA not missed for object 1 + ** 1 = PCM DMA missed for object1 + ** Bit 2 0 = PCM DMA not missed for object 2 + ** 1 = PCM DMA missed for object2 + ** Bit 3 0 = PCM DMA not missed for object 3 + ** 1 = PCM DMA missed for object3 + ** Bit 4 0 = PCM DMA not missed for object 4 + ** 1 = PCM DMA missed for object4 + */ + unsigned short pcmdmamissed; +} __attribute__((packed)); + +/* + * MSG to AUDPP enable or disable feedback form DSP to ARM + */ + +#define AUDPP_MSG_CFG_MSG 0x0007 +#define AUDPP_MSG_CFG_MSG_LEN \ + sizeof(struct audpp_msg_cfg_msg) + +#define AUDPP_MSG_ENA_ENA 0xFFFF +#define AUDPP_MSG_ENA_DIS 0x0000 + +struct audpp_msg_cfg_msg { + /* Enabled - 0xffff + ** Disabled - 0 + */ + unsigned short enabled; +} __attribute__((packed)); + +/* + * MSG to communicate the reverb per object volume + */ + +#define AUDPP_MSG_QREVERB_VOLUME 0x0008 +#define AUDPP_MSG_QREVERB_VOLUME_LEN \ + sizeof(struct audpp_msg_qreverb_volume) + + +struct audpp_msg_qreverb_volume { + unsigned short obj_0_gain; + unsigned short obj_1_gain; + unsigned short obj_2_gain; + unsigned short obj_3_gain; + unsigned short obj_4_gain; + unsigned short hpcm_obj_volume; +} __attribute__((packed)); + +#define AUDPP_MSG_ROUTING_ACK 0x0009 +#define AUDPP_MSG_ROUTING_ACK_LEN \ + sizeof(struct audpp_msg_routing_ack) + +struct audpp_msg_routing_ack { + unsigned short dec_id; + unsigned short routing_mode; +} __attribute__((packed)); + +#define AUDPP_MSG_FLUSH_ACK 0x000A + +#endif diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_private.h b/arch/arm/mach-msm/qdsp5v2/adsp_private.h new file mode 100644 index 0000000000000..badfb0daa5fc9 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/adsp_private.h @@ -0,0 +1,163 @@ +/* arch/arm/mach-msm/qdsp5v2/adsp_private.h + * + * Copyright (C) 2010 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 _MSM_ADSP_5V2_PRIVATE_H_ +#define _MSM_ADSP_5V2_PRIVATE_H_ + +/* adsp rtos / hardware memory map */ + +#define QDSP_RAMC_OFFSET 0x00400000 +#define ADSP_READ_CTRL_OFFSET 0x00400038 +#define ADSP_WRITE_CTRL_OFFSET 0x00400034 +#define ADSP_SEND_IRQ_OFFSET 0x00c00200 + + +/* adsp rtos hardware / shared memory interface */ + +#define ADSP_WRITE_CTRL_MUTEX_M 0x80000000U +#define ADSP_WRITE_CTRL_MUTEX_NAVAIL_V 0x80000000U +#define ADSP_WRITE_CTRL_MUTEX_AVAIL_V 0x00000000U + +#define ADSP_WRITE_CTRL_CMD_M 0x70000000U +#define ADSP_WRITE_CTRL_CMD_WRITE_REQ_V 0x00000000U +#define ADSP_WRITE_CTRL_CMD_WRITE_DONE_V 0x10000000U +#define ADSP_WRITE_CTRL_CMD_NO_CMD_V 0x70000000U + +#define ADSP_WRITE_CTRL_STATUS_M 0x0E000000U +#define ADSP_WRITE_CTRL_NO_ERR_V 0x00000000U +#define ADSP_WRITE_CTRL_NO_FREE_BUF_V 0x02000000U + +#define ADSP_WRITE_CTRL_DSP_ADDR_M 0x00FFFFFFU + +#define ADSP_WRITE_CTRL_HTOD_CMD_ID_M 0x00FFFFFFU + +/* Combination of MUTEX and CMD bits to check if the DSP is busy */ +#define ADSP_WRITE_CTRL_READY_M 0xF0000000U +#define ADSP_WRITE_CTRL_READY_V 0x70000000U + +/* RTOS to Host processor command mask values */ +#define ADSP_READ_CTRL_FLAG_M 0x80000000U +#define ADSP_READ_CTRL_FLAG_UP_WAIT_V 0x00000000U +#define ADSP_READ_CTRL_FLAG_UP_CONT_V 0x80000000U + +#define ADSP_READ_CTRL_CMD_M 0x60000000U +#define ADSP_READ_CTRL_READ_DONE_V 0x00000000U +#define ADSP_READ_CTRL_READ_REQ_V 0x20000000U +#define ADSP_READ_CTRL_NO_CMD_V 0x60000000U + +/* Combination of FLAG and COMMAND bits to check if MSG ready */ +#define ADSP_READ_CTRL_READY_M 0xE0000000U +#define ADSP_READ_CTRL_READY_V 0xA0000000U +#define ADSP_READ_CTRL_CONT_V 0xC0000000U +#define ADSP_READ_CTRL_DONE_V 0xE0000000U + +#define ADSP_READ_CTRL_STATUS_M 0x18000000U +#define ADSP_READ_CTRL_NO_ERR_V 0x00000000U + +#define ADSP_READ_CTRL_IN_PROG_M 0x04000000U +#define ADSP_READ_CTRL_NO_READ_IN_PROG_V 0x00000000U +#define ADSP_READ_CTRL_READ_IN_PROG_V 0x04000000U + +#define ADSP_READ_CTRL_CMD_TYPE_M 0x03000000U +#define ADSP_READ_CTRL_CMD_TASK_TO_H_V 0x00000000U + +#define ADSP_READ_CTRL_DSP_ADDR_M 0x00FFFFFFU + +#define ADSP_READ_CTRL_MSG_ID_M 0x000000FFU +#define ADSP_READ_CTRL_TASK_ID_M 0x0000FF00U + + +/* modem adsp management DAL service interface */ + +#define ADSP_DAL_DEVICE 0x0200009A +#define ADSP_DAL_PORT "SMD_DAL00" +#define ADSP_DAL_COMMAND (DAL_OP_FIRST_DEVICE_API | 0x80000000) + +struct adsp_dal_cmd { + uint32_t cmd; + uint32_t proc_id; + uint32_t module; + void *cookie; +}; + +#define ADSP_PROC_NONE 0 +#define ADSP_PROC_MODEM 1 +#define ADSP_PROC_APPS 2 + +#define ADSP_CMD_ENABLE 1 +#define ADSP_CMD_DISABLE 2 +#define ADSP_CMD_DISABLE_EVENT_RSP 6 +#define ADSP_CMD_GET_INIT_INFO 11 + +#define ADSP_EVT_MOD_READY 0 +#define ADSP_EVT_MOD_DISABLE 1 +#define ADSP_EVT_INIT_INFO 6 +#define ADSP_EVT_DISABLE_FAIL 7 + +#define ADSP_TASKS_MAX 64 +#define ADSP_QUEUES_MAX 4 + +#define MODULE_NAME_MAX 32 +#define QUEUE_NAME_MAX 32 + +#define ADSP_QUEUE_FLAG_16BIT 0 +#define ADSP_QUEUE_FLAG_32BIT 1 + +struct adsp_queue_info { + uint8_t name[QUEUE_NAME_MAX]; + uint32_t offset; /* Queue Offset in DSP memory */ + uint16_t idx; /* Global queue identifier */ + uint16_t max_size; /* Max allowed size in bytes for a queue */ + uint16_t flag; /* queue is 32bit Vs 16 bits */ + uint16_t rvd1; + uint32_t rvd2; +}; + +struct adsp_module_info +{ + uint8_t name[MODULE_NAME_MAX]; + uint32_t uuid; + uint16_t task_id; + uint16_t q_cnt; + struct adsp_queue_info queue[ADSP_QUEUES_MAX]; + uint32_t rvd1; + uint32_t rvd2; +}; + +struct adsp_evt_info { + uint32_t module; + uint32_t image; + uint32_t apps_okts; /* wtf is an okts? */ +}; + +struct adsp_dal_event { + /* DAL common event header */ + uint32_t evt_handle; + uint32_t evt_cookie; + uint32_t evt_length; + + /* ADSP event header */ + uint32_t event; + uint32_t version; + uint32_t proc_id; + + /* payload */ + union { + struct adsp_module_info module; + struct adsp_evt_info info; + } u; +}; + +#endif diff --git a/arch/arm/mach-msm/qdsp5v2/audio_glue.c b/arch/arm/mach-msm/qdsp5v2/audio_glue.c new file mode 100644 index 0000000000000..49b9841ce4d5c --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_glue.c @@ -0,0 +1,540 @@ +/* arch/arm/mach-msm/qdsp5v2/audio_glue.c + * + * Copyright (C) 2010 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 + +/* the audio codec control consists of + * - mi2s transports (x3) + * - lpa (low power audio) frontend for mi2s tx + * - various related clocks + */ +struct msm_codec { + void *tx_base; + void *rx_base; + void *lpa_base; + + struct clk *rx_mclk; + struct clk *rx_sclk; + struct clk *tx_mclk; + struct clk *tx_sclk; + + struct clk *lpa_codec_clk; + struct clk *lpa_core_clk; + struct clk *lpa_pclk; + + struct clk *adsp_clk; +}; + +#define LPA_MAX_BUF_SIZE 0x30000 + +#define LPA_CONTROL 0x00000000 +#define LPA_CODEC 0x00000004 +#define LPA_HLB_MIN_ADDR 0x00000008 +#define LPA_HLB_MAX_ADDR 0x0000000C +#define LPA_HLB_WPTR 0x00000010 +#define LPA_HLB_VOLUME_CONTROL 0x00000014 +#define LPA_LLB_MIN_ADDR 0x00000018 +#define LPA_LLB_MAX_ADDR 0x0000001C +#define LPA_SB_MIN_ADDR 0x00000020 +#define LPA_SB_MAX_ADDR 0x00000024 +#define LPA_INTR_ENABLE 0x00000028 +#define LPA_INTR_STATUS 0x0000002C +#define LPA_WMARK_ASSIGN 0x00000030 +#define LPA_WMARK_0_LLB 0x00000034 +#define LPA_WMARK_1_LLB 0x00000038 +#define LPA_WMARK_2_LLB 0x0000003C +#define LPA_WMARK_3_LLB 0x00000040 +#define LPA_WMARK_HLB 0x00000044 +#define LPA_WMARK_SB 0x00000048 +#define LPA_RDPTR_LLB 0x0000004C +#define LPA_RDPTR_HLB 0x00000050 +#define LPA_WRPTR_SB 0x00000054 +#define LPA_UTC_CONFIG 0x00000058 +#define LPA_UTC_INTR_LOW 0x0000005C +#define LPA_UTC_INTR_HIGH 0x00000060 +#define LPA_UTC_LOW 0x00000064 +#define LPA_UTC_HIGH 0x00000068 +#define LPA_MISR 0x0000006C +#define LPA_STATUS 0x00000070 +#define LPA_ACK 0x00000074 +#define LPA_MEMORY_CONTROL 0x00000078 +#define LPA_MEMORY_STATUS 0x0000007C +#define LPA_MEMORY_TIME_CONTROL 0x00000080 +#define LPA_ACC_LV 0x00000084 +#define LPA_ACC_HV 0x0000008c +#define LPA_RESETS 0x00000090 +#define LPA_TESTBUS 0x00000094 + +#define LPA_AICTL 0x00000100 + +/* OBUF_CODEC */ +#define LPA_CODEC_LOAD 0x200000 +#define LPA_CODEC_INTF_EN 0x100000 +#define LPA_CODEC_CFG_MASK 0x0FC07F + +#define LPA_SAMPLE_RATE_8KHZ 0x000000 +#define LPA_SAMPLE_RATE_11P025KHZ 0x010000 +#define LPA_SAMPLE_RATE_16KHZ 0x020000 +#define LPA_SAMPLE_RATE_22P05KHZ 0x030000 +#define LPA_SAMPLE_RATE_32KHZ 0x040000 +#define LPA_SAMPLE_RATE_44P1KHZ 0x050000 +#define LPA_SAMPLE_RATE_48KHZ 0x060000 +#define LPA_SAMPLE_RATE_64KHZ 0x070000 +#define LPA_SAMPLE_RATE_96KHZ 0x080000 + +#define LPA_BITS_PER_CHAN_16BITS 0x000000 +#define LPA_BITS_PER_CHAN_24BITS 0x004000 +#define LPA_BITS_PER_CHAN_32BITS 0x008000 +#define LPA_BITS_PER_CHAN_RESERVED 0x00C000 + +#define LPA_INTF_SDAC 0x000010 +#define LPA_INTF_MI2S 0x000020 +#define LPA_INTF_WB_CODEC 0x000030 + +/* WB_CODEC & SDAC can only support 16bit mono/stereo. + * MI2S can bit format and number of channel + */ +#define LPA_NUM_CHAN_MONO 0x000000 +#define LPA_NUM_CHAN_STEREO 0x000001 +#define LPA_NUM_CHAN_5P1 0x000002 +#define LPA_NUM_CHAN_7P1 0x000003 +#define LPA_NUM_CHAN_4_CHANNEL 0x000004 + +/* OBUF_CONTROL */ +#define LPA_CONTROL_TEST_EN 0x100 +#define LPA_CONTROL_LLB_CLR_CMD 0x080 +#define LPA_CONTROL_SB_SAT_EN 0x040 +#define LPA_CONTROL_LLB_SAT_EN 0x020 +#define LPA_CONTROL_LLB_ACC_EN 0x008 +#define LPA_CONTROL_HLB_EN 0x004 +#define LPA_CONTROL_LLB_EN 0x002 +#define LPA_CONTROL_SB_EN 0x001 + +/* OBUF_RESET definition */ +#define LPA_RESETS_MISR 0x1 +#define LPA_RESETS_OVERALL 0x2 + +/* OBUF_STATUS definition */ +#define LPA_STATUS_RESET_DONE 0x80000 +#define LPA_STATUS_LLB_CLR 0x40000 + +/* OBUF_HLB_MIN_ADDR definition */ +#define LPA_HLB_MIN_ADDR_LOAD 0x40000 +#define LPA_HLB_MIN_ADDR_SEG_MASK 0x3e000 + +/* OBUF_HLB_MAX_ADDR definition */ +#define LPA_HLB_MAX_ADDR_SEG_MASK 0x3fff8 + +/* OBUF_LLB_MIN_ADDR definition */ +#define LPA_LLB_MIN_ADDR_LOAD 0x40000 +#define LPA_LLB_MIN_ADDR_SEG_BMSK 0x3e000 + +/* OBUF_LLB_MAX_ADDR definition */ +#define LPA_LLB_MAX_ADDR_SEG_MASK 0x3ff8 +#define LPA_LLB_MAX_ADDR_SEG_SHFT 0x3 + +/* OBUF_SB_MIN_ADDR definition */ +#define LPA_SB_MIN_ADDR_LOAD 0x4000 +#define LPA_SB_MIN_ADDR_SEG_BMSK 0x3e00 + +/* OBUF_SB_MAX_ADDR definition */ +#define LPA_SB_MAX_ADDR_SEG_BMSK 0x3ff8 + +/* OBUF_MEMORY_CONTROL definition */ +#define LPA_MEM_CTL_PWRUP 0xfff + +/* OBUF_INTR_ENABLE definition */ +#define LPA_INTR_EN 0x3 + +/* OBUF_WMARK_ASSIGN definition */ +#define LPA_WMARK_ASSIGN_BMSK 0xF +#define LPA_WMARK_ASSIGN_DONE 0xF + +/* OBUF_WMARK_n_LLB definition */ +#define LPA_WMARK_n_LLB_ADDR(n) (0x00000034 + 0x4 * (n)) +#define LPA_WMARK_CTRL_MASK 0x0c0000 +#define LPA_WMARK_CTRL_SHFT 0x12 +#define LPA_WMARK_MAP_MASK 0xf00000 +#define LPA_WMARK_MAP_SHFT 0x14 + +#define LPA_WMARK_CTL_DISABLED 0x0 +#define LPA_WMARK_CTL_NON_BLOCK 0x1 +#define LPA_WMARK_CTL_ZERO_INSERT 0x2 +#define LPA_WMARK_CTL_RESERVED 0x3 + +/* OBUF_UTC_CONFIG definition */ +#define LPA_UTC_CONFIG_MAP_MASK 0xf0 +#define LPA_UTC_CONFIG_MAP_SHFT 0x4 +#define LPA_UTC_CONFIG_EN 0x1 +#define LPA_UTC_CONFIG_NO_INTR 0xF + +/* OBUF_ACK definition */ +#define LPA_ACK_RESET_DONE 0x80000 + + +#define LPA_BUF_ID_HLB 0 /* HLB buffer */ +#define LPA_BUF_ID_LLB 1 /* LLB buffer */ +#define LPA_BUF_ID_SB 2 /* SB buffer */ +#define LPA_BUF_ID_UTC 3 + + +/* from board file in qct tree */ + +#define LPA_HLB_SIZE 0x2BFF8 + +#define LPA_ID_DSP 0 +#define LPA_ID_APP 2 + +#if 0 +#define CFG_LLB_MIN_ADDR 0x0000 +#define CFG_LLB_MAX_ADDR 0x3ff8 +#define CFG_SB_MIN_ADDR 0 +#define CFG_SB_MAX_ADDR 0 +#else +#define CFG_LLB_MIN_ADDR 0x0000 +#define CFG_LLB_MAX_ADDR 0x37f8 +#define CFG_SB_MIN_ADDR 0x3800 +#define CFG_SB_MAX_ADDR 0x3ff8 +#endif + +#define CFG_HLB_MIN_ADDR 0x00000 +#define CFG_HLB_MAX_ADDR 0x2BFF8 + +/* 7x30 MI2S Registers */ + +/* MI2S Registers are named from the MI2S block's point of view: + * - TX = transmit from SoC to external codec + * - RX = receive from external codec to SoC + */ +#define MI2S_RESET 0x00 +#define MI2S_MODE 0x04 +#define MI2S_TX_MODE 0x08 +#define MI2S_RX_MODE 0x0C + +#define MI2S_RESET_RESET 1 + +#define MI2S_MODE_MASTER 0x1000 +#define MI2S_MODE_16BIT 0x0100 +#define MI2S_MODE_24BIT 0x0200 +#define MI2S_MODE_32BIT 0x0300 +#define MI2S_MODE_EN_3 0x0080 +#define MI2S_MODE_EN_2 0x0040 +#define MI2S_MODE_EN_1 0x0020 +#define MI2S_MODE_EN_0 0x0010 +#define MI2S_MODE_TX_3 0x0008 +#define MI2S_MODE_TX_2 0x0004 +#define MI2S_MODE_TX_1 0x0002 +#define MI2S_MODE_TX_0 0x0001 + +#define MI2S_TX_MODE_2CH 0x0000 +#define MI2S_TX_MODE_4CH 0x0008 +#define MI2S_TX_MODE_6CH 0x0010 +#define MI2S_TX_MODE_8CH 0x0018 +#define MI2S_TX_MODE_STEREO 0x0004 +#define MI2S_TX_MODE_MONO_PACK 0x0002 /* 2 mono samples packed together */ +#define MI2S_TX_MODE_DMA_SYNC 0x0001 /* sync dma ack clocks */ + +#define MI2S_RX_MODE_2CH 0x0000 +#define MI2S_RX_MODE_4CH 0x0008 +#define MI2S_RX_MODE_6CH 0x0010 +#define MI2S_RX_MODE_8CH 0x0018 +#define MI2S_RX_MODE_STEREO 0x0004 +#define MI2S_RX_MODE_MONO_PACK 0x0002 /* 2 mono samples packed together */ +#define MI2S_RX_MODE_DMA_SYNC 0x0001 /* sync dma ack clocks */ + +static int mi2s_set_output(struct msm_codec *mc, + unsigned channels, unsigned bitdepth) +{ + unsigned mode = 0; + unsigned tx_mode = 0; + + if (channels != 2 || bitdepth != 16) + return -EINVAL; + + /* TODO: support non stereo-16 (does the DSP even do that?) */ + + mode |= MI2S_MODE_MASTER; + mode |= MI2S_MODE_16BIT; + mode |= MI2S_MODE_EN_0; + mode |= MI2S_MODE_TX_0; + + tx_mode |= MI2S_TX_MODE_STEREO; + tx_mode |= MI2S_TX_MODE_2CH; + tx_mode |= MI2S_RX_MODE_DMA_SYNC; + + writel(1, mc->tx_base + MI2S_RESET); + writel(mode, mc->tx_base + MI2S_MODE); + writel(tx_mode, mc->tx_base + MI2S_TX_MODE); + writel(0, mc->tx_base + MI2S_RESET); + + return 0; +} + +static int mi2s_set_input(struct msm_codec *mc, + unsigned channels, unsigned bitdepth) +{ + unsigned mode = 0; + unsigned rx_mode = 0; + + if (channels != 2 || bitdepth != 16) + return -EINVAL; + + /* TODO: support non stereo-16 */ + /* TODO: packed mono mode? */ + + mode |= MI2S_MODE_MASTER; + mode |= MI2S_MODE_16BIT; + mode |= MI2S_MODE_EN_0; + + rx_mode |= MI2S_RX_MODE_STEREO; + rx_mode |= MI2S_RX_MODE_2CH; + rx_mode |= MI2S_RX_MODE_DMA_SYNC; + + writel(1, mc->rx_base + MI2S_RESET); + writel(mode, mc->rx_base + MI2S_MODE); + writel(rx_mode, mc->rx_base + MI2S_RX_MODE); + writel(0, mc->rx_base + MI2S_RESET); + + return 0; +} + +void lpa_enable(struct msm_codec *mc) +{ + unsigned val; + + /* for "hardware reasons" we must ensure the + * adsp clock is on during this reset sequence. + */ + clk_enable(mc->adsp_clk); + + /* disable codec */ + writel(LPA_CODEC_LOAD, mc->lpa_base + LPA_CODEC); + + writel(LPA_RESETS_MISR | LPA_RESETS_OVERALL, + mc->lpa_base + LPA_RESETS); + + while (!(readl(mc->lpa_base + LPA_STATUS) & LPA_STATUS_RESET_DONE)) + ; + + writel(LPA_ACK_RESET_DONE, mc->lpa_base + LPA_ACK); + + clk_disable(mc->adsp_clk); + + /* configure memory buffers */ + writel(CFG_LLB_MIN_ADDR | LPA_LLB_MIN_ADDR_LOAD, + mc->lpa_base + LPA_LLB_MIN_ADDR); + writel(CFG_LLB_MAX_ADDR, mc->lpa_base + LPA_LLB_MAX_ADDR); + + writel(CFG_SB_MIN_ADDR | LPA_SB_MIN_ADDR_LOAD, + mc->lpa_base + LPA_SB_MIN_ADDR); + writel(CFG_SB_MAX_ADDR, mc->lpa_base + LPA_SB_MAX_ADDR); + + writel(CFG_HLB_MIN_ADDR | LPA_HLB_MIN_ADDR_LOAD, + mc->lpa_base + LPA_HLB_MIN_ADDR); + writel(CFG_HLB_MAX_ADDR, mc->lpa_base + LPA_HLB_MAX_ADDR); + + writel(LPA_MEM_CTL_PWRUP, mc->lpa_base + LPA_MEMORY_CONTROL); + + + while (readl(mc->lpa_base + LPA_WMARK_ASSIGN) != LPA_WMARK_ASSIGN_DONE) + ; + + /* setup watermark ownership */ + writel(LPA_ID_DSP << LPA_WMARK_MAP_SHFT, + mc->lpa_base + LPA_WMARK_0_LLB); + writel(LPA_ID_DSP << LPA_WMARK_MAP_SHFT, + mc->lpa_base + LPA_WMARK_1_LLB); + writel(LPA_ID_APP << LPA_WMARK_MAP_SHFT, + mc->lpa_base + LPA_WMARK_2_LLB); + writel(LPA_ID_APP << LPA_WMARK_MAP_SHFT, + mc->lpa_base + LPA_WMARK_3_LLB); + writel(LPA_ID_DSP << LPA_WMARK_MAP_SHFT, + mc->lpa_base + LPA_WMARK_HLB); + writel(LPA_ID_DSP << LPA_WMARK_MAP_SHFT, + mc->lpa_base + LPA_WMARK_SB); + writel(0, mc->lpa_base + LPA_UTC_CONFIG); + + + val = readl(mc->lpa_base + LPA_CONTROL); + val |= LPA_CONTROL_LLB_EN; + val |= LPA_CONTROL_LLB_SAT_EN; + val |= LPA_CONTROL_SB_EN; + val |= LPA_CONTROL_SB_SAT_EN; + writel(val, mc->lpa_base + LPA_CONTROL); + + writel(1 << LPA_ID_DSP, mc->lpa_base + LPA_INTR_ENABLE); +} + +void lpa_start(struct msm_codec *mc) +{ + unsigned val, codec; + + codec = LPA_CODEC_LOAD; + codec |= LPA_NUM_CHAN_STEREO; + codec |= LPA_SAMPLE_RATE_48KHZ; + codec |= LPA_BITS_PER_CHAN_16BITS; + codec |= LPA_INTF_WB_CODEC; + writel(codec, mc->lpa_base + LPA_CODEC); + + /* clear LLB */ + val = readl(mc->lpa_base + LPA_CONTROL); + writel(val | LPA_CONTROL_LLB_CLR_CMD, mc->lpa_base + LPA_CONTROL); + + while (!(readl(mc->lpa_base + LPA_STATUS) & LPA_STATUS_LLB_CLR)) + udelay(100); + + /* enable codec */ + codec |= LPA_CODEC_INTF_EN; + writel(codec, mc->lpa_base + LPA_CODEC); +} + +void lpa_disable(struct msm_codec *mc) +{ + writel(LPA_CODEC_LOAD, mc->lpa_base + LPA_CODEC); +} + +int msm_codec_output_enable(struct msm_codec *mc) +{ + unsigned rate, val; + + pr_info("msm_codec_output_enable()\n"); + + /* yes rx clks for tx codec -- the clocks + * are named from the opposite POV of the + * codec for some reason... + */ + + + /* bitrate * bits * channels * 8 */ + rate = 48000 * 16 * 2 * 8; + clk_set_rate(mc->rx_mclk, rate); + + printk("RATE %d\n", clk_get_rate(mc->rx_mclk)); + + clk_enable(mc->rx_mclk); + clk_enable(mc->rx_sclk); + + clk_enable(mc->lpa_pclk); + clk_enable(mc->lpa_codec_clk); + clk_enable(mc->lpa_core_clk); + /* LPA init */ + + lpa_enable(mc); + + /* interconnect reg -> LPA */ + val = readl(mc->lpa_base + LPA_AICTL); + writel(val | 4, mc->lpa_base + LPA_AICTL); + + /* fire up mi2s transport */ + mi2s_set_output(mc, 2, 16); + + lpa_start(mc); + + /* AFE enable */ + + /* ADIE enable */ + + /* AMP enable */ + + return 0; +} + +int msm_codec_output_disable(struct msm_codec *mc) +{ + pr_info("msm_codec_output_disable()\n"); + /* AMP disable */ + /* ADIE disable */ + /* AFE disable */ + /* LPA disable */ + + clk_disable(mc->lpa_core_clk); + clk_disable(mc->lpa_codec_clk); + clk_disable(mc->lpa_pclk); + + clk_disable(mc->rx_sclk); + clk_disable(mc->rx_mclk); + + return 0; +} + + +static struct msm_codec the_msm_codec; + +int msm_codec_output(int enable) +{ + struct msm_codec *mc = &the_msm_codec; + if (enable) + return msm_codec_output_enable(mc); + else + return msm_codec_output_disable(mc); +} + +/* 7x30 memory map */ + +#define PHYS_ADDR_LPA 0xA5000000 +#define PHYS_SIZE_LPA 0x00000800 + +#define PHYS_ADDR_MI2S_HDMI 0xAC900000 +#define PHYS_ADDR_MI2S_CODEC_RX 0xAC940040 +#define PHYS_ADDR_MI2S_CODEC_TX 0xAC980080 +#define PHYS_SIZE_MI2S 0x00000040 + +int msm_codec_init(void) +{ + struct msm_codec *mc = &the_msm_codec; + + printk("msm_codec_init()\n"); + + mc->rx_mclk = clk_get(NULL, "mi2s_codec_rx_mclk"); + if (IS_ERR(mc->rx_mclk)) + return -ENODEV; + mc->rx_sclk = clk_get(NULL, "mi2s_codec_rx_sclk"); + if (IS_ERR(mc->rx_sclk)) + return -ENODEV; + mc->tx_mclk = clk_get(NULL, "mi2s_codec_tx_mclk"); + if (IS_ERR(mc->tx_mclk)) + return -ENODEV; + mc->tx_sclk = clk_get(NULL, "mi2s_codec_tx_sclk"); + if (IS_ERR(mc->tx_sclk)) + return -ENODEV; + mc->lpa_codec_clk = clk_get(NULL, "lpa_codec_clk"); + if (IS_ERR(mc->lpa_codec_clk)) + return -ENODEV; + mc->lpa_core_clk = clk_get(NULL, "lpa_core_clk"); + if (IS_ERR(mc->lpa_core_clk)) + return -ENODEV; + mc->lpa_pclk = clk_get(NULL, "lpa_pclk"); + if (IS_ERR(mc->lpa_pclk)) + return -ENODEV; + mc->adsp_clk = clk_get(NULL, "adsp_clk"); + if (IS_ERR(mc->adsp_clk)) + return -ENODEV; + + mc->lpa_base = ioremap(PHYS_ADDR_LPA, PHYS_SIZE_LPA); + if (!mc->lpa_base) + return -ENODEV; + mc->rx_base = ioremap(PHYS_ADDR_MI2S_CODEC_RX, PHYS_SIZE_MI2S); + if (!mc->rx_base) + return -ENODEV; + mc->tx_base = ioremap(PHYS_ADDR_MI2S_CODEC_TX, PHYS_SIZE_MI2S); + if (!mc->tx_base) + return -ENODEV; + + return 0; +} diff --git a/arch/arm/mach-msm/qdsp5v2/audio_out.c b/arch/arm/mach-msm/qdsp5v2/audio_out.c new file mode 100644 index 0000000000000..4e63e601d6736 --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/audio_out.c @@ -0,0 +1,242 @@ +/* arch/arm/mach-msm/qdsp5v2/audio_out.c + * + * Copyright (C) 2010 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 +#include +#include +#include + +#include + +#include + +#include "adsp.h" +#include "adsp_audio.h" + +#include "adsp_module_afe.h" + + +void adie_enable(void); + +struct audio_buffer { + dma_addr_t phys; + void *data; + uint32_t size; + uint32_t used; +}; + +struct audio { + struct audio_buffer buf[2]; + + int cpu_buf; + int dsp_buf; + int running; + int session; + + wait_queue_head_t wait; + struct audplay *audplay; + void *data; + dma_addr_t phys; +}; + +static void audio_send_data(void *cookie) +{ + struct audio *audio = cookie; + struct audio_buffer *ab = audio->buf + audio->dsp_buf; + + if (ab->used) { + ab->used = 0; + audio->dsp_buf ^= 1; + wake_up(&audio->wait); + } +} + + +static int need_init = 1; + +static int audio_open(struct inode *inode, struct file *file) +{ + int ret; + struct audio *audio; + + if (need_init) { + msm_codec_output(1); + afe_enable(AFE_DEVICE_MI2S_CODEC_RX, 48000, 2); + adie_enable(); + need_init = 0; + } + +#if 0 + msleep(5000); + afe_disable(AFE_DEVICE_MI2S_CODEC_RX); + msm_codec_output(0); + + msleep(5000); + msm_codec_output(1); + afe_enable(AFE_DEVICE_MI2S_CODEC_RX, 48000, 2); + return 0; +#endif + + audio = kzalloc(sizeof(*audio), GFP_KERNEL); + if (!audio) + return -ENOMEM; + + audio->data = dma_alloc_coherent(NULL, 8192, &audio->phys, GFP_KERNEL); + if (!audio->data) { + pr_err("audio: could not allocate DMA buffers\n"); + kfree(audio); + return -ENOMEM; + } + + init_waitqueue_head(&audio->wait); + + audio->buf[0].phys = audio->phys; + audio->buf[0].data = audio->data; + audio->buf[0].size = 4096; + audio->buf[0].used = 0; + audio->buf[1].phys = audio->phys + 4096; + audio->buf[1].data = audio->data + 4096; + audio->buf[1].size = 4096; + audio->buf[1].used = 0; + + audio->audplay = audplay_get(audio_send_data, audio); + if (!audio->audplay) { + kfree(audio); + return -ENODEV; + } + + audplay_dsp_config(audio->audplay, 1); + + audplay_config_pcm(audio->audplay, 44100, 16, 2); + + audplay_mix_select(audio->audplay, 1); + audplay_volume_pan(audio->audplay, 0x2000, 0); + + file->private_data = audio; + return 0; +} + +static ssize_t audio_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct audio *audio = file->private_data; + struct audio_buffer *ab; + const char __user *start = buf; + int xfer; + + while (count > 0) { + ab = audio->buf + audio->cpu_buf; + + if (ab->used) + if (!wait_event_timeout(audio->wait, + (ab->used == 0), 5*HZ)) { + pr_err("audio_write: timeout. dsp dead?\n"); + return -EIO; + } + + xfer = count; + if (xfer > ab->size) + xfer = ab->size; + + if (copy_from_user(ab->data, buf, xfer)) + return -EFAULT; + + buf += xfer; + count -= xfer; + + ab->used = xfer; + audplay_send_data(audio->audplay, ab->phys, ab->used); + audio->cpu_buf ^= 1; + } + + return buf - start; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct audio *audio = file->private_data; + pr_info("audio_release()\n"); + audplay_dsp_config(audio->audplay, 0); + audplay_put(audio->audplay); + kfree(audio); + return 0; +} + +static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct audio *audio = file->private_data; + int rc = 0; + + switch (cmd) { + case AUDIO_SET_VOLUME: { + int vol; + if (copy_from_user(&vol, (void*) arg, sizeof(vol))) { + rc = -EFAULT; + break; + } + pr_info("audio_out: volume %d\n", vol); + break; + } + case AUDIO_GET_STATS: + case AUDIO_START: + case AUDIO_STOP: + case AUDIO_FLUSH: + case AUDIO_SET_CONFIG: + /* implement me! */ + break; + case AUDIO_GET_CONFIG: { + struct msm_audio_config config; + config.buffer_size = 4096; + config.buffer_count = 2; + config.sample_rate = 44100; + config.channel_count = 2; + config.unused[0] = 0; + config.unused[1] = 0; + config.unused[2] = 0; + if (copy_to_user((void*) arg, &config, sizeof(config))) { + rc = -EFAULT; + } + break; + } + } + return rc; +} + + +static const struct file_operations audio_out_fops = { + .owner = THIS_MODULE, + .open = audio_open, + .release = audio_release, + .write = audio_write, + .unlocked_ioctl = audio_ioctl, +}; + +struct miscdevice audio_out_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msm_pcm_out", + .fops = &audio_out_fops, +}; + +static int __init audio_init(void) +{ + adsp_audio_init(); + return misc_register(&audio_out_misc); +} + +device_initcall(audio_init); diff --git a/arch/arm/mach-msm/qdsp5v2/marimba.c b/arch/arm/mach-msm/qdsp5v2/marimba.c new file mode 100644 index 0000000000000..fb1e18a3b583f --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/marimba.c @@ -0,0 +1,322 @@ +/* arch/arm/mach-msm/qdsp5v2/marimba.c + * + * Copyright (C) 2010 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. + * + */ + +struct codec_reg { + unsigned char addr, mask, val; +}; + +static struct codec_reg init_rx[] = { + { 0x23, 0xF8, 0x00 }, + { 0x24, 0x6F, 0x00 }, + { 0x25, 0x7F, 0x00 }, + { 0x26, 0xFC, 0x00 }, + { 0x28, 0xFE, 0x00 }, + { 0x29, 0xFE, 0x00 }, + { 0x33, 0xFF, 0x00 }, + { 0x34, 0xFF, 0x00 }, + { 0x35, 0xFC, 0x00 }, + { 0x36, 0xFE, 0x00 }, + { 0x37, 0xFE, 0x00 }, + { 0x38, 0xFE, 0x00 }, + { 0x39, 0xF0, 0x00 }, + { 0x3A, 0xFF, 0x0A }, + { 0x3B, 0xFC, 0xAC }, + { 0x3C, 0xFC, 0xAC }, + { 0x3D, 0xFF, 0x55 }, + { 0x3E, 0xFF, 0x55 }, + { 0x3F, 0xCF, 0x00 }, + { 0x40, 0x3F, 0x00 }, + { 0x41, 0x3F, 0x00 }, + { 0x42, 0xFF, 0x00 }, + { 0x43, 0xF7, 0x00 }, + { 0x43, 0xF7, 0x00 }, + { 0x43, 0xF7, 0x00 }, + { 0x43, 0xF7, 0x00 }, + { 0x44, 0xF7, 0x00 }, + { 0x45, 0xFF, 0x00 }, + { 0x46, 0xFF, 0x00 }, + { 0x47, 0xF7, 0x00 }, + { 0x48, 0xF7, 0x00 }, + { 0x49, 0xFF, 0x00 }, + { 0x4A, 0xFF, 0x00 }, + { 0x80, 0x02, 0x00 }, + { 0x81, 0xFF, 0x4C }, + { 0x83, 0x23, 0x00 }, + { 0x84, 0xFF, 0xAC }, + { 0x85, 0xFF, 0xAC }, + { 0x88, 0xFF, 0xFF }, + { 0x8A, 0x0F, 0x03 }, + { 0x8B, 0xFF, 0xAC }, + { 0x8C, 0x03, 0x01 }, + { 0x8D, 0xFF, 0x00 }, + { 0x8E, 0xFF, 0x00 }, + +/* lb regs */ + { 0x2B, 0x8F, 0x02 }, + { 0x2C, 0x8F, 0x02 }, + + { 0xFF, 0x00, 0x00 }, +}; + +static struct codec_reg init_handset_48k_256_mono[] = { + { 0x80, 0x02, 0x02 }, + { 0x80, 0x02, 0x00 }, + + { 0x24, 0x6F, 0x44 }, + { 0x04, 0xFF, 0x8C }, + { 0x81, 0xFF, 0x4e }, + { 0x25, 0x0F, 0x0b }, + { 0x26, 0xfc, 0xfc }, + { 0x36, 0xc0, 0x80 }, + { 0x3A, 0xFF, 0x2B }, + { 0x23, 0xff, 0x20 }, + { 0x3d, 0xFF, 0x55 }, + { 0x83, 0x21, 0x21 }, + { 0x33, 0x80, 0x80 }, + + { 0xFF, 0x00, 10 }, + + { 0x33, 0x40, 0x40 }, + { 0x84, 0xff, 0x00 }, + { 0x8A, 0x05, 0x04 }, + + { 0xFF, 0x00, 0x00 }, +}; + +static struct codec_reg init_speaker_48k_256_stereo[] = { + { 0x80, 0x02, 0x02 }, + { 0x80, 0x02, 0x00 }, + + { 0x24, 0x6F, 0x64 }, + { 0x25, 0x0F, 0x0B }, + { 0x26, 0xfc, 0xfc }, + { 0x37, 0xe6, 0x80 }, + { 0x3A, 0xFF, 0x2B }, + { 0x3d, 0xFF, 0x55 }, + { 0x83, 0x23, 0x23 }, + { 0x23, 0xff, 0x20 }, + { 0x33, 0x8a, 0x8a }, + { 0x33, 0x05, 0x05 }, + + { 0xFF, 0x00, 30 }, + + { 0x84, 0xff, 0x03 }, + { 0x85, 0xff, 0x03 }, + { 0x8A, 0x0f, 0x0c }, + + { 0xFF, 0x00, 0x00 }, +}; + + +#include +#include +#include +#include +#include + +#include + +static struct vreg *vreg_marimba1; +static struct vreg *vreg_marimba2; +static struct vreg *vreg_marimba3; + +static int marimba_vreg_init(void) +{ + vreg_marimba1 = vreg_get(NULL, "s2"); + if (IS_ERR(vreg_marimba1)) + return PTR_ERR(vreg_marimba1); + vreg_marimba2 = vreg_get(NULL, "gp16"); + if (IS_ERR(vreg_marimba2)) + return PTR_ERR(vreg_marimba2); + /* codec vreg */ + vreg_marimba3 = vreg_get(NULL, "s4"); + if (IS_ERR(vreg_marimba3)) + return PTR_ERR(vreg_marimba3); + return 0; +} + +static void marimba_vreg_enable(void) +{ + vreg_enable(vreg_marimba1); + vreg_enable(vreg_marimba2); + vreg_enable(vreg_marimba3); +} + +#define MARIMBA_ADDR_MARIMBA 0x0C +#define MARIMBA_ADDR_FM 0x2A +#define MARIMBA_ADDR_CDC 0x77 +#define MARIMBA_ADDR_QMEMBIST 0X66 + +#define MARIMBA_REG_ID_FM 0x01 +#define MARIMBA_REG_ID_CDC 0x02 +#define MARIMBA_REG_ID_QMEMBIST 0x03 +#define MARIMBA_REG_ID_TSADC 0x04 + +static int marimba_raw_write(struct i2c_client *client, + u8 addr, u8 reg, u8 value) +{ + struct i2c_msg msg; + u8 data[2]; + int ret; + + msg.addr = addr; + msg.flags = 0; + msg.len = 2; + msg.buf = data; + data[0] = reg; + data[1] = value; + + ret = i2c_transfer(client->adapter, &msg, 1); + + if (ret != 1) + pr_err("marimba_write: fail %d\n", ret); + + return ret; +} + +static int marimba_raw_read(struct i2c_client *client, u8 addr, u8 reg) +{ + struct i2c_msg msg[2]; + u8 value; + int ret; + + msg[0].addr = addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = ® + + msg[1].addr = addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 1; + msg[1].buf = &value; + + ret = i2c_transfer(client->adapter, msg, 2); + + if (ret != 2) + pr_err("marimba_read: fail %d\n", ret); + + if (ret == 2) + return value; + return ret; +} + +static u8 marimba_shadow[256]; + +static int marimba_write(struct i2c_client *client, u8 reg, u8 value) +{ + marimba_shadow[reg] = value; + return marimba_raw_write(client, client->addr, reg, value); +} + +static int marimba_write_mask(struct i2c_client *client, u8 reg, u8 mask, u8 value) +{ + value = (marimba_shadow[reg] & (~mask)) | (value & mask); + marimba_shadow[reg] = value; + return marimba_raw_write(client, client->addr, reg, value); +} + +static int marimba_read(struct i2c_client *client, u8 reg) +{ + return marimba_raw_read(client, client->addr, reg); +} + + +static struct i2c_client *marimba_client; + +void adie_load(struct i2c_client *client, struct codec_reg *regs) +{ + int n; + for (n = 0;; n++) { + if (regs[n].addr == 0xff) { + if (regs[n].val == 0) + return; + msleep(regs[n].val); + continue; + } + marimba_write_mask(client, regs[n].addr, + regs[n].mask, regs[n].val); + } +} + +void adie_enable(void) +{ + struct i2c_client *client = marimba_client; + + marimba_vreg_enable(); + + marimba_write(client, 0xff, 0x08); /* bring up codec */ + marimba_write(client, 0xff, 0x0a); /* GDFS_EN_FEW=1 */ + marimba_write(client, 0xff, 0x0e); /* GDFS_EN_REST=1 */ + marimba_write(client, 0xff, 0x07); /* RESET_N=1 */ + marimba_write(client, 0xff, 0x17); /* clock enable */ + marimba_write(client, 0x03, 0x04); /* enable band gap */ + marimba_write(client, 0x8F, 0x44); /* dither delay select, dmic gain bypass */ + + msleep(100); + + adie_load(client, init_rx); + adie_load(client, init_speaker_48k_256_stereo); +} + +static int marimba_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + + marimba_client = client; + + printk("*** marimba probe %p '%s' @ 0x%x ** *\n", + client, client->name, client->addr); + + /* 0x10 -> MARIMBA_MODE ?! */ + marimba_raw_write(client, MARIMBA_ADDR_MARIMBA, 0x00, 0x10); + + /* program address into marimba master device */ + ret = marimba_raw_write(client, MARIMBA_ADDR_MARIMBA, + MARIMBA_REG_ID_CDC, client->addr); + + if (ret != 1) { + pr_err("marimba_probe() cannot set address\n"); + return ret; + } + + return 0; +} + + +static const struct i2c_device_id marimba_id[] = { + { "marimba-codec", 0 }, + { } +}; + +static struct i2c_driver marimba_driver = { + .probe = marimba_probe, + .id_table = marimba_id, + .driver = { + .name = "marimba", + }, +}; + + +static int marimba_init(void) +{ + int ret; + ret = marimba_vreg_init(); + if (ret) + return ret; + return i2c_add_driver(&marimba_driver); +} + +module_init(marimba_init); From a635ac9cfb4d463b64c183fa07c472d7b9ec1021 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Thu, 10 Jun 2010 16:57:30 -0700 Subject: [PATCH 0661/2556] ARM: msm: add QDSP5V2 option for MSM7X30 Signed-off-by: Brian Swetland --- arch/arm/mach-msm/Kconfig | 7 +++++++ arch/arm/mach-msm/Makefile | 1 + 2 files changed, 8 insertions(+) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index a4810540d47a5..f2a2421883685 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -538,6 +538,13 @@ config MSM_QDSP6 help Enable support for qdsp6. This provides audio and video functionality. +config MSM_QDSP5V2 + tristate "QDSP5V2 support" + depends on ARCH_MSM7X30 + default y + help + Enable support for qdsp5v2, which provides audio processing on 7x30. + config MSM_SSBI tristate "SSBI support" depends on ARCH_MSM7X30 diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index fcd4245b2fea1..31658aef2d735 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_dog_keepalive.o obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_time_remote.o obj-$(CONFIG_MSM_ADSP) += qdsp5/ +obj-$(CONFIG_MSM_QDSP5V2) += qdsp5v2/ obj-$(CONFIG_MSM_QDSP6) += qdsp6/ obj-$(CONFIG_MSM_HW3D) += hw3d.o obj-$(CONFIG_PM) += pm.o From 316a69517a6ff26795433b559672b54a703fac3e Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Sun, 6 Jun 2010 14:27:50 -0700 Subject: [PATCH 0662/2556] msm: surf7x30: marimba codec is on I2C bus 2 Signed-off-by: Brian Swetland --- arch/arm/mach-msm/board-surf7x30.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm/mach-msm/board-surf7x30.c b/arch/arm/mach-msm/board-surf7x30.c index bcae33d2009e7..fe52633f506a0 100644 --- a/arch/arm/mach-msm/board-surf7x30.c +++ b/arch/arm/mach-msm/board-surf7x30.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -406,6 +407,14 @@ static int __init surf7x30_ssbi_pmic_init(void) return platform_device_register(&msm_device_ssbi_pmic); } + +static struct i2c_board_info surf_i2c_devices[] = { + /* marimba master is implied at 0x0c */ + { + I2C_BOARD_INFO("marimba-codec", 0x77), + }, +}; + extern struct sys_timer msm_timer; void msm_serial_debug_init(unsigned int base, int irq, @@ -421,6 +430,7 @@ static struct platform_device *devices[] __initdata = { &usb_mass_storage_device, &android_usb_device, &smc91x_device, + &msm_device_i2c2, #if !SURF7X30_USE_PMIC_KEYPAD &surf7x30_keypad_device, #endif @@ -439,6 +449,9 @@ static void __init surf7x30_init(void) msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; platform_add_devices(devices, ARRAY_SIZE(devices)); + + i2c_register_board_info(1, surf_i2c_devices, + ARRAY_SIZE(surf_i2c_devices)); msm_hsusb_set_vbus_state(1); msm_hsusb_set_vbus_state(0); msm_hsusb_set_vbus_state(1); From 60b886e82fcbf8ed9a08b9c468de4125156c2c6c Mon Sep 17 00:00:00 2001 From: Sagar Dharia Date: Wed, 23 Sep 2009 13:29:15 -0600 Subject: [PATCH 0663/2556] qup_i2c: Add clock, resources entries for QUP i2c mini-core device Change-Id: Iab948cdecbc8155b8c00c4d25c0150e71b9a0bab Signed-off-by: Sagar Dharia --- arch/arm/mach-msm/devices.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h index 825f69103ed1d..35d148fd96297 100644 --- a/arch/arm/mach-msm/devices.h +++ b/arch/arm/mach-msm/devices.h @@ -37,6 +37,8 @@ extern struct platform_device msm_device_hsusb_host; extern struct platform_device msm_device_i2c; extern struct platform_device msm_device_i2c2; +extern struct platform_device msm_device_qup_i2c; + extern struct platform_device msm_device_smd; extern struct platform_device msm_device_nand; From 1c3177727e3687a2a28cc4cd317dc87f321c833e Mon Sep 17 00:00:00 2001 From: Sagar Dharia Date: Wed, 23 Sep 2009 13:27:27 -0600 Subject: [PATCH 0664/2556] qup_i2c: Initial implementation of I2C mini-core driver for QUP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QUP (Qualcomm Universal Peripheral engine) hardware provides FIFO based data path to mini cores like I2C. i2c-qup driver communicates with the QUP and its FIFOs. Advantages of this driver include 1-interrupt per FIFO/BLOCK number of byte unlike 1-interrupt-per-byte of i2c-msm. Since the QUP hardware communicates with I2C mini core, and this driver communicates with QUP hardware, driver communicating with I2C hardware directly (like i2c-msm) can't be used to communicate with QUP. QUP has FIFO mode and BLOCK mode. FIFO mode can be used if the data transfer size is less than FIFO size. BLOCK mode should be used for transfers greater than FIFO size. FIFO mode is supported in this initial implementation. Signed-off-by: Sagar Dharia Added mach/msm_qup.h for platform data. Signed-off-by: Arve Hjønnevåg qup_i2c: Make sure that READ start and READ size tags are in same FIFO There is a known hardware restriction with I2C mini core, where for a READ command, start and size tags have to be specified in same FIFO. Failing to do so may cause QUP to generate extra read cycle. To avoid this, 1 NOP is introduced at the end of WRITE preceding READ, if those WRITE command(s) leave only 1 space in the FIFO. Signed-off-by: Sagar Dharia qup_i2c: Disable QUP interrupts when QUP is not in use. QUP (and its interrupts) is only used when a client initiates an I2C transaction. We disable QUP interrupts when QUP is not in use so that idle power collapse can be done. Signed-off-by: Sagar Dharia qup_i2c: Write correct value to output FIFO for odd number of bytes This commit rectifies the last-value written to output FIFO when odd number of bytes are written to QUP's I2C mini core. CRs Fixed: 220647 Signed-off-by: Sagar Dharia qup_i2c: Move QUP register initialization to first transfer QUP registers are also clocked by QUP client clock. The register reads may fail if the client clock is not on during QUP probe. Moving the register reads to first transfer ensures that the client has called QUP transfer after it has enabled QUP client clock and the register reads won't fail. Signed-off-by: Sagar Dharia qup_i2c: Use DEBUG correctly as a flag and not as a value. The DEBUG flag was being used inconsistently as value and flag, which was causing printing of messages when not desired. This commit takes care of that inconsistency. Correct usage of DEBUG as flag also cleans up the code and eliminates computation that was being done when debugging was disabled. Signed-off-by: Sagar Dharia qup_i2c: Use PCLK if needed by I2C QUP driver. QUP hardware block on some boards need PCLK as well. (e.g. 7x30) QUP driver will use PCLK if it's specified by the platform data structure in the boards file. CRs-fixed: 224242 Signed-off-by: Sagar Dharia --- arch/arm/mach-msm/include/mach/msm_qup.h | 17 + drivers/i2c/busses/Kconfig | 8 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-qup.c | 936 +++++++++++++++++++++++ 4 files changed, 962 insertions(+) create mode 100644 arch/arm/mach-msm/include/mach/msm_qup.h create mode 100644 drivers/i2c/busses/i2c-qup.c diff --git a/arch/arm/mach-msm/include/mach/msm_qup.h b/arch/arm/mach-msm/include/mach/msm_qup.h new file mode 100644 index 0000000000000..d884c20718eb7 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_qup.h @@ -0,0 +1,17 @@ +#ifndef _MACH_MSM_QUP_H +#define _MACH_MSM_QUP_H + +struct msm_qup_i2c_platform_data { + int clk_freq; + uint32_t rmutex; + const char *rsl_id; + uint32_t pm_lat; + int pri_clk; + int pri_dat; + int aux_clk; + int aux_dat; + const char *pclk; + void (*msm_i2c_config_gpio)(int iface, int config_type); +}; + +#endif diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 78a35c66695ce..9e883c57f55e1 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -450,6 +450,14 @@ config I2C_MSM If you say yes to this option, support will be included for the built-in I2C interface on the MSM7X00A family processors. +config I2C_QUP + tristate "I2C_QUP" + depends on I2C && ARCH_MSM7X30 + default y + help + If you say yes to this option, support will be included for the + built-in I2C interface on the MSM family processors. + config I2C_MV64XXX tristate "Marvell mv64xxx I2C Controller" depends on (MV64X60 || PLAT_ORION) && EXPERIMENTAL diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index ede592bd89370..060bb9292521f 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o obj-$(CONFIG_I2C_MSM) += i2c-msm.o +obj-$(CONFIG_I2C_QUP) += i2c-qup.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o obj-$(CONFIG_I2C_NOMADIK) += i2c-nomadik.o obj-$(CONFIG_I2C_NUC900) += i2c-nuc900.o diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c new file mode 100644 index 0000000000000..2480f697b92c2 --- /dev/null +++ b/drivers/i2c/busses/i2c-qup.c @@ -0,0 +1,936 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * QUP driver for Qualcomm MSM platforms + * + */ + +/* #define DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION("0.2"); +MODULE_ALIAS("platform:i2c_qup"); + +/* QUP Registers */ +enum { + QUP_CONFIG = 0x0, + QUP_STATE = 0x4, + QUP_IO_MODE = 0x8, + QUP_SW_RESET = 0xC, + QUP_OPERATIONAL = 0x18, + QUP_ERROR_FLAGS = 0x1C, + QUP_ERROR_FLAGS_EN = 0x20, + QUP_MX_READ_CNT = 0x208, + QUP_MX_INPUT_CNT = 0x200, + QUP_OUT_DEBUG = 0x108, + QUP_OUT_FIFO_CNT = 0x10C, + QUP_OUT_FIFO_BASE = 0x110, + QUP_IN_READ_CUR = 0x20C, + QUP_IN_DEBUG = 0x210, + QUP_IN_FIFO_CNT = 0x214, + QUP_IN_FIFO_BASE = 0x218, + QUP_I2C_CLK_CTL = 0x400, + QUP_I2C_STATUS = 0x404, +}; + +/* QUP States and reset values */ +enum { + QUP_RESET_STATE = 0, + QUP_RUN_STATE = 1U, + QUP_STATE_MASK = 3U, + QUP_PAUSE_STATE = 3U, + QUP_STATE_VALID = 1U << 2, + QUP_I2C_MAST_GEN = 1U << 4, + QUP_OPERATIONAL_RESET = 0xFF0, + QUP_I2C_STATUS_RESET = 0xFFFFFC, +}; + +/* I2C mini core related values */ +enum { + I2C_MINI_CORE = 2U << 8, + I2C_N_VAL = 0xF, + +}; + +/* Packing Unpacking words in FIFOs */ +enum { + QUP_UNPACK_EN = 1U << 14, + QUP_PACK_EN = 1U << 15, +}; + +/* QUP tags */ +enum { + QUP_OUT_NOP = 0, + QUP_OUT_START = 1U << 8, + QUP_OUT_DATA = 2U << 8, + QUP_OUT_STOP = 3U << 8, + QUP_OUT_REC = 4U << 8, + QUP_IN_DATA = 5U << 8, + QUP_IN_STOP = 6U << 8, + QUP_IN_NACK = 7U << 8, +}; + +/* Status, Error flags */ +enum { + I2C_STATUS_WR_BUFFER_FULL = 1U << 0, + I2C_STATUS_ERROR_MASK = 0xfc, + QUP_IN_NOT_EMPTY = 1U << 5, + QUP_STATUS_ERROR_MASK = 0x7F, + QUP_STATUS_ERROR_FLAGS = 0x7C, +}; + +struct qup_i2c_dev { + struct device *dev; + void __iomem *base; /* virtual */ + void __iomem *gsbi; /* virtual */ + int in_irq; + int out_irq; + int err_irq; + struct clk *clk; + struct clk *pclk; + struct i2c_adapter adapter; + + struct i2c_msg *msg; + int pos; + int cnt; + int err; + int mode; + int clk_ctl; + int out_fifo_sz; + int in_fifo_sz; + int out_blk_sz; + int in_blk_sz; + struct msm_qup_i2c_platform_data *pdata; + void *complete; +}; + +#ifdef DEBUG +static void +qup_print_status(struct qup_i2c_dev *dev) +{ + uint32_t val; + val = readl(dev->base+QUP_CONFIG); + dev_dbg(dev->dev, "Qup config is :0x%x\n", val); + val = readl(dev->base+QUP_STATE); + dev_dbg(dev->dev, "Qup state is :0x%x\n", val); + val = readl(dev->base+QUP_IO_MODE); + dev_dbg(dev->dev, "Qup mode is :0x%x\n", val); +} +#else +static inline void qup_print_status(struct qup_i2c_dev *dev) +{ +} +#endif + +static irqreturn_t +qup_i2c_interrupt(int irq, void *devid) +{ + struct qup_i2c_dev *dev = devid; + uint32_t status = readl(dev->base + QUP_I2C_STATUS); + uint32_t status1 = readl(dev->base + QUP_ERROR_FLAGS); + int err = 0; + + if (status & I2C_STATUS_ERROR_MASK) { + dev_err(dev->dev, "QUP: Got i2c error :0x%x\n", status); + err = -status; + goto intr_done; + } + + if (status1 & 0x7F) { + dev_err(dev->dev, "QUP: Got QUP error :0x%x\n", status1); + err = -status1; + goto intr_done; + } + + /* Ignore output buffer empty interrupt for READ transaction */ + if (dev->msg && dev->msg->flags == I2C_M_RD && irq == dev->out_irq) + return IRQ_HANDLED; + else if (!dev->msg) + return IRQ_HANDLED; + +intr_done: + dev_dbg(dev->dev, "QUP intr= %d, i2c status=0x%x, qup status = 0x%x\n", + irq, status, status1); + qup_print_status(dev); + dev->err = err; + complete(dev->complete); + return IRQ_HANDLED; +} + +static int +qup_i2c_poll_writeready(struct qup_i2c_dev *dev) +{ + uint32_t retries = 0; + + while (retries != 2000) { + uint32_t status = readl(dev->base + QUP_I2C_STATUS); + + if (!(status & I2C_STATUS_WR_BUFFER_FULL)) + return 0; + if (retries++ == 1000) + udelay(100); + } + qup_print_status(dev); + return -ETIMEDOUT; +} + +static int +qup_i2c_poll_state(struct qup_i2c_dev *dev, uint32_t state) +{ + uint32_t retries = 0; + + dev_dbg(dev->dev, "Polling Status for state:0x%x\n", state); + + while (retries != 2000) { + uint32_t status = readl(dev->base + QUP_STATE); + + if ((status & (QUP_STATE_VALID | state)) == + (QUP_STATE_VALID | state)) + return 0; + else if (retries++ == 1000) + udelay(100); + } + return -ETIMEDOUT; +} + +#ifdef DEBUG +static void qup_verify_fifo(struct qup_i2c_dev *dev, uint32_t val, + uint32_t addr, int rdwr) +{ + if (rdwr) + dev_dbg(dev->dev, "RD:Wrote 0x%x to out_ff:0x%x\n", val, addr); + else + dev_dbg(dev->dev, "WR:Wrote 0x%x to out_ff:0x%x\n", val, addr); +} +#else +static inline void qup_verify_fifo(struct qup_i2c_dev *dev, uint32_t val, + uint32_t addr, int rdwr) +{ +} +#endif + +static void +qup_issue_read(struct qup_i2c_dev *dev, struct i2c_msg *msg, int *idx, + uint32_t carry_over) +{ + uint16_t addr = (msg->addr << 1) | 1; + + /* QUP limit 256 bytes per read */ + if (*idx % 4) { + writel(carry_over | ((QUP_OUT_START | addr) << 16), + dev->base + QUP_OUT_FIFO_BASE);/* + (*idx-2)); */ + + qup_verify_fifo(dev, carry_over | + ((QUP_OUT_START | addr) << 16), (uint32_t)dev->base + + QUP_OUT_FIFO_BASE + (*idx - 2), 1); + writel((QUP_OUT_REC | dev->cnt), + dev->base + QUP_OUT_FIFO_BASE);/* + (*idx+2)); */ + + qup_verify_fifo(dev, (QUP_OUT_REC | dev->cnt), + (uint32_t)dev->base + QUP_OUT_FIFO_BASE + (*idx + 2), 1); + } else { + writel(((QUP_OUT_REC | dev->cnt) << 16) | QUP_OUT_START | addr, + dev->base + QUP_OUT_FIFO_BASE);/* + (*idx)); */ + + qup_verify_fifo(dev, QUP_OUT_REC << 16 | dev->cnt << 16 | + QUP_OUT_START | addr, + (uint32_t)dev->base + QUP_OUT_FIFO_BASE + (*idx), 1); + } + *idx += 4; +} + +static void +qup_issue_write(struct qup_i2c_dev *dev, struct i2c_msg *msg, int rem, + int *idx, uint32_t *carry_over) +{ + int entries = dev->cnt; + int i = 0; + uint32_t val = 0; + uint32_t last_entry = 0; + uint16_t addr = msg->addr << 1; + if (dev->pos == 0) + entries++; + + if (dev->pos == 0) { + if (*idx % 4) { + writel(*carry_over | ((QUP_OUT_START | addr) << 16), + dev->base + QUP_OUT_FIFO_BASE); + + qup_verify_fifo(dev, *carry_over | QUP_OUT_DATA << 16 | + addr << 16, (uint32_t)dev->base + + QUP_OUT_FIFO_BASE + (*idx) - 2, 0); + } else + val = QUP_OUT_START | addr; + *idx += 2; + i++; + } else if (*idx % 4) { + val = (QUP_OUT_NOP | 1); + i++; + } + + for (; i < (entries - 1); i++) { + if (*idx % 4) { + writel(val | ((QUP_OUT_DATA | + msg->buf[dev->pos]) << 16), + dev->base + QUP_OUT_FIFO_BASE); + + qup_verify_fifo(dev, val | QUP_OUT_DATA << 16 | + msg->buf[dev->pos] << 16, (uint32_t)dev->base + + QUP_OUT_FIFO_BASE + (*idx) - 2, 0); + } else + val = QUP_OUT_DATA | msg->buf[dev->pos]; + (*idx) += 2; + dev->pos++; + } + if (dev->pos < (dev->cnt - 1)) + last_entry = QUP_OUT_DATA; + else if (rem > 1) /* not last array entry */ + last_entry = QUP_OUT_DATA; + else + last_entry = QUP_OUT_STOP; + if ((*idx % 4) == 0) { + /* + * If read-start and read-command end up in different fifos, it + * may result in extra-byte being read due to extra-read cycle. + * Avoid that by inserting NOP as the last entry of fifo only + * if write command(s) leave 1 space in fifo. + */ + if (rem > 1) { + struct i2c_msg *next = msg + 1; + if (next->addr == msg->addr && (next->flags | I2C_M_RD) + && *idx == ((dev->out_fifo_sz*2) - 4)) { + writel(((last_entry | msg->buf[dev->pos]) | + ((1 | QUP_OUT_NOP) << 16)), dev->base + + QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */ + *idx += 2; + } else + *carry_over = (last_entry | msg->buf[dev->pos]); + } else { + writel((last_entry | msg->buf[dev->pos]), + dev->base + QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */ + + qup_verify_fifo(dev, last_entry | msg->buf[dev->pos], + (uint32_t)dev->base + QUP_OUT_FIFO_BASE + + (*idx), 0); + } + } else { + writel(val | ((last_entry | msg->buf[dev->pos]) << 16), + dev->base + QUP_OUT_FIFO_BASE);/* + (*idx) - 2); */ + + qup_verify_fifo(dev, val | (last_entry << 16) | + (msg->buf[dev->pos] << 16), (uint32_t)dev->base + + QUP_OUT_FIFO_BASE + (*idx) - 2, 0); + } + + *idx += 2; + dev->pos++; + dev->cnt = msg->len - dev->pos; +} + +static int +qup_update_state(struct qup_i2c_dev *dev, uint32_t state) +{ + if (qup_i2c_poll_state(dev, 0) != 0) + return -EIO; + writel(state, dev->base + QUP_STATE); + if (qup_i2c_poll_state(dev, state) != 0) + return -EIO; + return 0; +} + +static int +qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + DECLARE_COMPLETION_ONSTACK(complete); + struct qup_i2c_dev *dev = i2c_get_adapdata(adap); + int ret; + int rem = num; + long timeout; + int err; + + /* Initialize QUP registers during first transfer */ + if (dev->clk_ctl == 0) { + int fs_div; + int hs_div; + int i2c_clk; + uint32_t fifo_reg; + writel(0x2 << 4, dev->gsbi); + + i2c_clk = 19200000; /* input clock */ + fs_div = ((i2c_clk / dev->pdata->clk_freq) / 2) - 3; + hs_div = 3; + dev->clk_ctl = ((hs_div & 0x7) << 8) | (fs_div & 0xff); + fifo_reg = readl(dev->base + QUP_IO_MODE); + if (fifo_reg & 0x3) + dev->out_blk_sz = (fifo_reg & 0x3) * 16; + else + dev->out_blk_sz = 16; + if (fifo_reg & 0x60) + dev->in_blk_sz = ((fifo_reg & 0x60) >> 5) * 16; + else + dev->in_blk_sz = 16; + /* + * The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag' + * associated with each byte written/received + */ + dev->out_blk_sz /= 2; + dev->in_blk_sz /= 2; + dev->out_fifo_sz = dev->out_blk_sz * + (2 << ((fifo_reg & 0x1C) >> 2)); + dev->in_fifo_sz = dev->in_blk_sz * + (2 << ((fifo_reg & 0x380) >> 7)); + dev_dbg(dev->dev, "QUP IN:bl:%d, ff:%d, OUT:bl:%d, ff:%d\n", + dev->in_blk_sz, dev->in_fifo_sz, + dev->out_blk_sz, dev->out_fifo_sz); + } + + enable_irq(dev->in_irq); + enable_irq(dev->out_irq); + enable_irq(dev->err_irq); + writel(QUP_RESET_STATE, dev->base + QUP_STATE); + ret = qup_i2c_poll_state(dev, QUP_RESET_STATE); + if (ret) { + dev_err(dev->dev, "QUP Busy:Trying to recover\n"); + goto out_err; + } + + /* Initialize QUP registers */ + writel(1, dev->base + QUP_SW_RESET); + writel(0, dev->base + QUP_CONFIG); + writel(QUP_OPERATIONAL_RESET, dev->base + QUP_OPERATIONAL); + writel(QUP_STATUS_ERROR_FLAGS, dev->base + QUP_ERROR_FLAGS_EN); + + writel(QUP_PACK_EN | QUP_UNPACK_EN, dev->base + QUP_IO_MODE); + writel(I2C_MINI_CORE | I2C_N_VAL, dev->base + QUP_CONFIG); + + /* Initialize I2C mini core registers */ + writel(0, dev->base + QUP_I2C_CLK_CTL); + writel(QUP_I2C_STATUS_RESET, dev->base + QUP_I2C_STATUS); + + dev->cnt = msgs->len; + dev->pos = 0; + dev->msg = msgs; + while (rem) { + bool filled = false; + + /* Wait for WR buffer not full */ + ret = qup_i2c_poll_writeready(dev); + if (ret) { + dev_err(dev->dev, + "Error waiting for write ready before addr\n"); + goto out_err; + } + + dev->err = 0; + dev->complete = &complete; + + if (qup_i2c_poll_state(dev, QUP_I2C_MAST_GEN) != 0) { + ret = -EIO; + goto out_err; + } + + qup_print_status(dev); + /* HW limits Read upto 256 bytes in 1 read without stop + * only FIFO mode supported right now, so read size of + * in_fifo supported in 1 read + */ + if (dev->msg->flags == I2C_M_RD) { + if (dev->cnt > dev->in_fifo_sz) { + dev_err(dev->dev, "No Block mode support\n"); + ret = -EPROTONOSUPPORT; + goto out_err; + } + writel(dev->cnt, dev->base + QUP_MX_READ_CNT); + } else { + if (dev->cnt > dev->out_fifo_sz) { + dev_err(dev->dev, "No Block mode support\n"); + ret = -EPROTONOSUPPORT; + goto out_err; + } else if (rem > 1) { + struct i2c_msg *next = msgs + 1; + if (next->addr == msgs->addr && + next->flags == I2C_M_RD) { + if (next->len > dev->in_fifo_sz) { + dev_err(dev->dev, + "No Block mode support\n"); + ret = -EPROTONOSUPPORT; + goto out_err; + } + writel(next->len, dev->base + + QUP_MX_READ_CNT); + } + } + } + + err = qup_update_state(dev, QUP_RUN_STATE); + if (err < 0) { + ret = err; + goto out_err; + } + + qup_print_status(dev); + writel(dev->clk_ctl, dev->base + QUP_I2C_CLK_CTL); + + do { + int idx = 0; + uint32_t carry_over = 0; + + /* Transition to PAUSE state only possible from RUN */ + err = qup_update_state(dev, QUP_PAUSE_STATE); + if (err < 0) { + ret = err; + goto out_err; + } + + qup_print_status(dev); + /* This operation is Write, check the next operation + * and decide mode + */ + while (filled == false) { + if (msgs->flags & I2C_M_RD) + qup_issue_read(dev, msgs, &idx, + carry_over); + else + qup_issue_write(dev, msgs, rem, &idx, + &carry_over); + if (idx >= dev->out_fifo_sz) + filled = true; + /* Start new message */ + if (filled == false) { + if (msgs->flags & I2C_M_RD) + filled = true; + else if (rem > 1) { + /* Only combine operations with + * same address + */ + struct i2c_msg *next = msgs + 1; + if (next->addr != msgs->addr) + filled = true; + else { + rem--; + msgs++; + dev->msg = msgs; + dev->pos = 0; + dev->cnt = msgs->len; + } + } else + filled = true; + } + } + err = qup_update_state(dev, QUP_RUN_STATE); + if (err < 0) { + ret = err; + goto out_err; + } + dev_dbg(dev->dev, "idx:%d, rem:%d, num:%d, mode:%d\n", + idx, rem, num, dev->mode); + + qup_print_status(dev); + timeout = wait_for_completion_timeout(&complete, + msecs_to_jiffies(dev->out_fifo_sz)); + if (!timeout) { + dev_err(dev->dev, "Transaction timed out\n"); + writel(1, dev->base + QUP_SW_RESET); + msleep(10); + ret = -ETIMEDOUT; + goto out_err; + } + if (dev->err) { + dev_err(dev->dev, + "Error during data xfer (%d)\n", + dev->err); + ret = dev->err; + goto out_err; + } + if (dev->msg->flags & I2C_M_RD) { + int i; + uint32_t dval = 0; + for (i = 0; dev->pos < dev->msg->len; i++, + dev->pos++) { + uint32_t rd_status = readl(dev->base + + QUP_OPERATIONAL); + if (i % 2 == 0) { + if ((rd_status & + QUP_IN_NOT_EMPTY) == 0) + break; + dval = readl(dev->base + + QUP_IN_FIFO_BASE); + dev->msg->buf[dev->pos] = + dval & 0xFF; + } else + dev->msg->buf[dev->pos] = + ((dval & 0xFF0000) >> + 16); + } + dev->cnt -= i; + } else + filled = false; /* refill output FIFO */ + } while (dev->cnt > 0); + if (dev->cnt == 0) { + rem--; + msgs++; + if (rem) { + dev->pos = 0; + dev->cnt = msgs->len; + dev->msg = msgs; + } + } + } + + ret = num; + out_err: + dev->complete = NULL; + dev->msg = NULL; + dev->pos = 0; + dev->err = 0; + dev->cnt = 0; + disable_irq(dev->err_irq); + disable_irq(dev->in_irq); + disable_irq(dev->out_irq); + return ret; +} + +static u32 +qup_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); +} + +static const struct i2c_algorithm qup_i2c_algo = { + .master_xfer = qup_i2c_xfer, + .functionality = qup_i2c_func, +}; + +static int __devinit +qup_i2c_probe(struct platform_device *pdev) +{ + struct qup_i2c_dev *dev; + struct resource *qup_mem, *gsbi_mem, *qup_io, *gsbi_io; + struct resource *in_irq, *out_irq, *err_irq; + struct clk *clk, *pclk; + int ret = 0; + struct msm_qup_i2c_platform_data *pdata; + + dev_dbg(&pdev->dev, "qup_i2c_probe\n"); + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "platform data not initialized\n"); + return -ENOSYS; + } + qup_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "qup_phys_addr"); + if (!qup_mem) { + dev_err(&pdev->dev, "no qup mem resource?\n"); + return -ENODEV; + } + gsbi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "gsbi_qup_i2c_addr"); + if (!gsbi_mem) { + dev_err(&pdev->dev, "no gsbi mem resource?\n"); + return -ENODEV; + } + + in_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + "qup_in_intr"); + if (!in_irq) { + dev_err(&pdev->dev, "no input irq resource?\n"); + return -ENODEV; + } + out_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + "qup_out_intr"); + if (!out_irq) { + dev_err(&pdev->dev, "no output irq resource?\n"); + return -ENODEV; + } + err_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + "qup_err_intr"); + if (!err_irq) { + dev_err(&pdev->dev, "no error irq resource?\n"); + return -ENODEV; + } + + qup_io = request_mem_region(qup_mem->start, resource_size(qup_mem), + pdev->name); + if (!qup_io) { + dev_err(&pdev->dev, "QUP region already claimed\n"); + return -EBUSY; + } + gsbi_io = request_mem_region(gsbi_mem->start, resource_size(gsbi_mem), + pdev->name); + if (!gsbi_io) { + dev_err(&pdev->dev, "GSBI region already claimed\n"); + return -EBUSY; + } + + clk = clk_get(&pdev->dev, "qup_clk"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Could not get clock\n"); + ret = PTR_ERR(clk); + goto err_clk_get_failed; + } + + if (pdata->pclk != NULL) { + pclk = clk_get(&pdev->dev, pdata->pclk); + if (IS_ERR(pclk)) { + dev_err(&pdev->dev, "Could not get pclock\n"); + ret = PTR_ERR(pclk); + clk_put(clk); + goto err_clk_get_failed; + } + } else + pclk = NULL; + + if (!(pdata->msm_i2c_config_gpio)) { + dev_err(&pdev->dev, "config_gpio function not initialized\n"); + ret = -ENOSYS; + goto err_config_failed; + } + + /* We support frequencies upto FAST Mode(400KHz) */ + if (pdata->clk_freq <= 0 || + pdata->clk_freq > 400000) { + dev_err(&pdev->dev, "clock frequency not supported\n"); + ret = -EIO; + goto err_config_failed; + } + + dev = kzalloc(sizeof(struct qup_i2c_dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto err_alloc_dev_failed; + } + + dev->dev = &pdev->dev; + dev->in_irq = in_irq->start; + dev->out_irq = out_irq->start; + dev->err_irq = err_irq->start; + dev->clk = clk; + dev->pclk = pclk; + dev->base = ioremap(qup_mem->start, resource_size(qup_mem)); + if (!dev->base) { + ret = -ENOMEM; + goto err_ioremap_failed; + } + + /* Configure GSBI block to use I2C functionality */ + dev->gsbi = ioremap(gsbi_mem->start, resource_size(gsbi_mem)); + if (!dev->gsbi) { + ret = -ENOMEM; + goto err_gsbi_failed; + } + + platform_set_drvdata(pdev, dev); + + clk_enable(clk); + if (pclk) + clk_enable(pclk); + dev->pdata = pdata; + dev->clk_ctl = 0; + + i2c_set_adapdata(&dev->adapter, dev); + dev->adapter.algo = &qup_i2c_algo; + strlcpy(dev->adapter.name, + "QUP I2C adapter", + sizeof(dev->adapter.name)); + + dev->adapter.nr = pdev->id; + ret = i2c_add_numbered_adapter(&dev->adapter); + if (ret) { + dev_err(&pdev->dev, "i2c_add_adapter failed\n"); + goto err_i2c_add_adapter_failed; + } + + ret = request_irq(dev->in_irq, qup_i2c_interrupt, + IRQF_TRIGGER_RISING, "qup_in_intr", dev); + if (ret) { + dev_err(&pdev->dev, "request_out_irq failed\n"); + goto err_request_irq_failed; + } + ret = request_irq(dev->out_irq, qup_i2c_interrupt, + IRQF_TRIGGER_RISING, "qup_out_intr", dev); + if (ret) { + dev_err(&pdev->dev, "request_in_irq failed\n"); + free_irq(dev->in_irq, dev); + goto err_request_irq_failed; + } + ret = request_irq(dev->err_irq, qup_i2c_interrupt, + IRQF_TRIGGER_RISING, "qup_err_intr", dev); + if (ret) { + dev_err(&pdev->dev, "request_err_irq failed\n"); + free_irq(dev->out_irq, dev); + free_irq(dev->in_irq, dev); + goto err_request_irq_failed; + } + disable_irq(dev->err_irq); + disable_irq(dev->in_irq); + disable_irq(dev->out_irq); + pdata->msm_i2c_config_gpio(dev->adapter.nr, 1); + + return 0; + +err_request_irq_failed: + i2c_del_adapter(&dev->adapter); +err_i2c_add_adapter_failed: + clk_disable(clk); + if (pclk) + clk_disable(pclk); + iounmap(dev->gsbi); +err_gsbi_failed: + iounmap(dev->base); +err_ioremap_failed: + kfree(dev); +err_alloc_dev_failed: +err_config_failed: + clk_put(clk); + if (pclk) + clk_put(pclk); +err_clk_get_failed: + release_mem_region(gsbi_mem->start, resource_size(gsbi_mem)); + release_mem_region(qup_mem->start, resource_size(qup_mem)); + return ret; +} + +static int __devexit +qup_i2c_remove(struct platform_device *pdev) +{ + struct qup_i2c_dev *dev = platform_get_drvdata(pdev); + struct resource *qup_mem, *gsbi_mem; + + platform_set_drvdata(pdev, NULL); + free_irq(dev->out_irq, dev); + free_irq(dev->in_irq, dev); + free_irq(dev->err_irq, dev); + i2c_del_adapter(&dev->adapter); + clk_disable(dev->clk); + clk_put(dev->clk); + if (dev->pclk) { + clk_disable(dev->pclk); + clk_put(dev->pclk); + } + iounmap(dev->gsbi); + iounmap(dev->base); + kfree(dev); + gsbi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "gsbi_qup_i2c_addr"); + release_mem_region(gsbi_mem->start, resource_size(gsbi_mem)); + qup_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "qup_phys_addr"); + release_mem_region(qup_mem->start, resource_size(qup_mem)); + return 0; +} + +#ifdef CONFIG_PM +static int qup_i2c_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct qup_i2c_dev *dev = platform_get_drvdata(pdev); + + clk_disable(dev->clk); + if (dev->pclk) + clk_disable(dev->pclk); + return 0; +} + +static int qup_i2c_resume(struct platform_device *pdev) +{ + struct qup_i2c_dev *dev = platform_get_drvdata(pdev); + + clk_enable(dev->clk); + if (dev->pclk) + clk_enable(dev->pclk); + return 0; +} +#else +#define qup_i2c_suspend NULL +#define qup_i2c_resume NULL +#endif /* CONFIG_PM */ + +static struct platform_driver qup_i2c_driver = { + .probe = qup_i2c_probe, + .remove = __devexit_p(qup_i2c_remove), + .suspend = qup_i2c_suspend, + .resume = qup_i2c_resume, + .driver = { + .name = "qup_i2c", + .owner = THIS_MODULE, + }, +}; + +/* QUP may be needed to bring up other drivers */ +static int __init +qup_i2c_init_driver(void) +{ + return platform_driver_register(&qup_i2c_driver); +} +subsys_initcall(qup_i2c_init_driver); + +static void __exit qup_i2c_exit_driver(void) +{ + platform_driver_unregister(&qup_i2c_driver); +} +module_exit(qup_i2c_exit_driver); + From 01d51a892a840345f3afe08c45a19ada20dfaeba Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 23 Jun 2010 21:12:30 -0700 Subject: [PATCH 0665/2556] qup_i2c: make compile for 2.6.35 Change-Id: Id583e78494c0501bcbff78d86412fdba34a267ab Signed-off-by: Dima Zavin --- drivers/i2c/busses/i2c-qup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index 2480f697b92c2..6c2d7fa7e4506 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -67,6 +67,7 @@ #include #include #include +#include #include #include #include From 84c6a2bd0afd8f029cb4b430b86ac5b773480ae3 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 3 Jun 2010 01:50:28 -0700 Subject: [PATCH 0666/2556] [ARM] msm: only build the legacy rpc servers for msm7x01a devices Change-Id: Ia4ba851ad8913c2e7123e18136f46a54901c38c4 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index f2a2421883685..75a2de1d5ec80 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -483,7 +483,7 @@ config MSM_ONCRPCROUTER the ARM9 and ARM11 config MSM_RPCSERVERS - depends on MSM_ONCRPCROUTER + depends on MSM_ONCRPCROUTER && ARCH_MSM7X00A default y bool "Kernel side RPC server bundle" help From eb2d45af0870f9438d88f4f6d0d7cde77aa065d2 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 3 Jun 2010 01:46:03 -0700 Subject: [PATCH 0667/2556] [ARM] msm: add a config option for legacy AMSS versions Older msm7x01a AMSS versions (<= 6225, 6225 is dream/sapphire) require special handling since they don't support backwards-compatible RPC versioning and some proc_comm clocks were numbered differently. Change-Id: I296e153e8e2047c26d88a0121d69de0bebaaa136 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Kconfig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 75a2de1d5ec80..3fedb2191dbbe 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -95,6 +95,9 @@ config MSM_REMOTE_SPINLOCK MSM_REMOTE_SPINLOCK_DEKKERS default y +config MSM_LEGACY_7X00A_AMSS + bool + config MSM_AMSS_VERSION int default 6210 if MSM_AMSS_VERSION_6210 @@ -105,16 +108,20 @@ config MSM_AMSS_VERSION choice prompt "AMSS modem firmware version" + depends on ARCH_MSM7X00A default MSM_AMSS_VERSION_6225 config MSM_AMSS_VERSION_6210 bool "6.2.10" + select MSM_LEGACY_7X00A_AMSS config MSM_AMSS_VERSION_6220 bool "6.2.20" + select MSM_LEGACY_7X00A_AMSS config MSM_AMSS_VERSION_6225 bool "6.2.20 + New ADSP" + select MSM_LEGACY_7X00A_AMSS config MSM_AMSS_VERSION_6350 bool "6.3.50" From f4e19e09b7d03af0927268b6de88cb6a0c3f3abd Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 3 Jun 2010 01:55:57 -0700 Subject: [PATCH 0668/2556] [ARM] msm: smd/clock: use config var to decide rpc versioning scheme Change-Id: Ieb3b50a2c3c1f4bf17a4260a1c8667b4f3c75bb7 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/clock-pcom.h | 2 +- arch/arm/mach-msm/include/mach/msm_rpcrouter.h | 2 +- arch/arm/mach-msm/smd_rpcrouter.c | 6 +++--- arch/arm/mach-msm/smd_rpcrouter_device.c | 4 ++-- arch/arm/mach-msm/smd_rpcrouter_servers.c | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-msm/clock-pcom.h b/arch/arm/mach-msm/clock-pcom.h index b2a04d048a491..2968325e9cbf1 100644 --- a/arch/arm/mach-msm/clock-pcom.h +++ b/arch/arm/mach-msm/clock-pcom.h @@ -72,7 +72,7 @@ #define P_USB_HS_P_CLK 37 /* High speed USB pbus clock */ #define P_USB_OTG_CLK 38 /* Full speed USB clock */ #define P_VDC_CLK 39 /* Video controller clock */ -#if CONFIG_MSM_AMSS_VERSION >= 6350 || defined(CONFIG_ARCH_QSD8X50) +#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS) #define P_VFE_MDC_CLK 40 /* VFE MDDI client clock */ #define P_VFE_CLK 41 /* Camera / Video Front End clock */ #else/* For radio code base others */ diff --git a/arch/arm/mach-msm/include/mach/msm_rpcrouter.h b/arch/arm/mach-msm/include/mach/msm_rpcrouter.h index d46deba8a00c0..b2f8e3ed0278c 100644 --- a/arch/arm/mach-msm/include/mach/msm_rpcrouter.h +++ b/arch/arm/mach-msm/include/mach/msm_rpcrouter.h @@ -22,7 +22,7 @@ #include #include -#if (CONFIG_MSM_AMSS_VERSION >= 6350) || defined(CONFIG_ARCH_QSD8X50) +#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS) /* RPC API version structure * Version bit 31 : 1->hashkey versioning, * 0->major-minor (backward compatible) versioning diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c index fba8075dc3702..92a30bb75a12c 100644 --- a/arch/arm/mach-msm/smd_rpcrouter.c +++ b/arch/arm/mach-msm/smd_rpcrouter.c @@ -776,7 +776,7 @@ int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count) return -ENOTCONN; } -#if CONFIG_MSM_AMSS_VERSION >= 6350 || defined(CONFIG_ARCH_QSD8X50) +#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS) if ((ept->dst_prog != rq->prog) || !msm_rpc_is_compatible_version( be32_to_cpu(ept->dst_vers), @@ -1113,7 +1113,7 @@ int __msm_rpc_read(struct msm_rpc_endpoint *ept, return rc; } -#if CONFIG_MSM_AMSS_VERSION >= 6350 || defined(CONFIG_ARCH_QSD8X50) +#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS) int msm_rpc_is_compatible_version(uint32_t server_version, uint32_t client_version) { @@ -1159,7 +1159,7 @@ struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned struct msm_rpc_endpoint *ept; struct rr_server *server; -#if CONFIG_MSM_AMSS_VERSION >= 6350 || defined(CONFIG_ARCH_QSD8X50) +#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS) if (!(vers & RPC_VERSION_MODE_MASK)) { uint32_t found_vers; if (msm_rpc_get_compatible_server(prog, vers, &found_vers) < 0) diff --git a/arch/arm/mach-msm/smd_rpcrouter_device.c b/arch/arm/mach-msm/smd_rpcrouter_device.c index f88bf00495105..1ae83b5aded6e 100644 --- a/arch/arm/mach-msm/smd_rpcrouter_device.c +++ b/arch/arm/mach-msm/smd_rpcrouter_device.c @@ -247,7 +247,7 @@ int msm_rpcrouter_create_server_cdev(struct rr_server *server) return -ENOBUFS; } -#if CONFIG_MSM_AMSS_VERSION >= 6350 || defined(CONFIG_ARCH_QSD8X50) +#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS) /* Servers with bit 31 set are remote msm servers with hashkey version. * Servers with bit 31 not set are remote msm servers with * backwards compatible version type in which case the minor number @@ -297,7 +297,7 @@ int msm_rpcrouter_create_server_pdev(struct rr_server *server) { sprintf(server->pdev_name, "rs%.8x:%.8x", server->prog, -#if CONFIG_MSM_AMSS_VERSION >= 6350 || defined(CONFIG_ARCH_QSD8X50) +#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS) (server->vers & RPC_VERSION_MODE_MASK) ? server->vers : (server->vers & RPC_VERSION_MAJOR_MASK)); #else diff --git a/arch/arm/mach-msm/smd_rpcrouter_servers.c b/arch/arm/mach-msm/smd_rpcrouter_servers.c index fe6e3c91e8495..839600221631a 100644 --- a/arch/arm/mach-msm/smd_rpcrouter_servers.c +++ b/arch/arm/mach-msm/smd_rpcrouter_servers.c @@ -60,7 +60,7 @@ static struct msm_rpc_server *rpc_server_find(uint32_t prog, uint32_t vers) mutex_lock(&rpc_server_list_lock); list_for_each_entry(server, &rpc_server_list, list) { if ((server->prog == prog) && -#if CONFIG_MSM_AMSS_VERSION >= 6350 || defined(CONFIG_ARCH_QSD8X50) +#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS) msm_rpc_is_compatible_version(server->vers, vers)) { #else server->vers == vers) { From 35397519d1e1d048b5a7a5f42e8ff423aec82b17 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 3 Jun 2010 01:59:32 -0700 Subject: [PATCH 0669/2556] [ARM] rtc: msm7x00a: use config var to decide rpc versioning scheme Change-Id: Ic246b3ef3f1e0fc57338c948064bf994bcbcb45a Signed-off-by: Dima Zavin --- drivers/rtc/rtc-msm7x00a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-msm7x00a.c b/drivers/rtc/rtc-msm7x00a.c index 690bc398cd6c0..5cc3c80ac7aa5 100644 --- a/drivers/rtc/rtc-msm7x00a.c +++ b/drivers/rtc/rtc-msm7x00a.c @@ -30,7 +30,7 @@ extern void msm_pm_set_max_sleep_time(int64_t sleep_time_ns); -#if CONFIG_MSM_AMSS_VERSION >= 6350 || defined(CONFIG_ARCH_QSD8X50) +#if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS) #define APP_TIMEREMOTE_PDEV_NAME "rs30000048:00010000" #else #define APP_TIMEREMOTE_PDEV_NAME "rs30000048:0da5b528" From f1aef9ce8fac489ee618c3b73e7867fa4efd4120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 18 Jun 2010 19:31:19 -0700 Subject: [PATCH 0670/2556] msm_iomap-8x50.h: Rename MSM_SMI_BASE to avoid conflict Change-Id: I65805cccf946eaa7df813bc3fbeef73497492c39 --- arch/arm/mach-msm/include/mach/msm_iomap-8x50.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h b/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h index ed772ddc70d91..eed8606bfb075 100644 --- a/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h +++ b/arch/arm/mach-msm/include/mach/msm_iomap-8x50.h @@ -79,13 +79,13 @@ #define MSM_TCSR_SIZE SZ_4K #ifdef CONFIG_MSM_SOC_REV_A -#define MSM_SMI_BASE 0xE0000000 +#define MSM_8K_SMI_BASE 0xE0000000 #else -#define MSM_SMI_BASE 0x00000000 +#define MSM_8K_SMI_BASE 0x00000000 #endif #define MSM_SHARED_RAM_BASE IOMEM(0xF8100000) -#define MSM_SHARED_RAM_PHYS (MSM_SMI_BASE + 0x00100000) +#define MSM_SHARED_RAM_PHYS (MSM_8K_SMI_BASE + 0x00100000) #define MSM_SHARED_RAM_SIZE SZ_1M #define MSM_UART1_PHYS 0xA9A00000 From 050fc7a9a271813cf50035b679a5ab26f7d20768 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 23 Jun 2010 17:49:37 -0700 Subject: [PATCH 0671/2556] [ARM] msm: rename camif_pad_pclk to its actual function, qup_pclk Change-Id: Ib725ef6df93615a18ac987e1ca5f4a92b0411a87 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/clock-pcom.h | 2 +- arch/arm/mach-msm/devices-msm7x30.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/clock-pcom.h b/arch/arm/mach-msm/clock-pcom.h index 2968325e9cbf1..04f73322adcb9 100644 --- a/arch/arm/mach-msm/clock-pcom.h +++ b/arch/arm/mach-msm/clock-pcom.h @@ -94,7 +94,7 @@ #define P_USB_HS2_CORE_CLK 54 /* High speed USB 2 core clock */ #define P_USB_HS3_CORE_CLK 55 /* High speed USB 3 core clock */ #define P_CAM_M_CLK 56 -#define P_CAMIF_PAD_P_CLK 57 +#define P_QUP_I2C_P_CLK 57 #define P_GRP_2D_CLK 58 #define P_GRP_2D_P_CLK 59 #define P_I2S_CLK 60 diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c index 5fc05b23f796f..aeb380bc2f583 100644 --- a/arch/arm/mach-msm/devices-msm7x30.c +++ b/arch/arm/mach-msm/devices-msm7x30.c @@ -688,7 +688,6 @@ struct clk msm_clocks_7x30[] = { CLK_PCOM("adm_clk", ADM_CLK, NULL, 0), CLK_PCOM("adsp_clk", ADSP_CLK, NULL, 0), CLK_PCOM("cam_m_clk", CAM_M_CLK, NULL, 0), - CLK_PCOM("camif_pad_pclk", CAMIF_PAD_P_CLK, NULL, OFF), CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN), CLK_PCOM("ecodec_clk", ECODEC_CLK, NULL, 0), CLK_PCOM("emdh_clk", EMDH_CLK, &msm_device_mddi1.dev, OFF | CLK_MINMAX), @@ -766,6 +765,7 @@ struct clk msm_clocks_7x30[] = { CLK_PCOM("vfe_pclk", VFE_P_CLK, NULL, OFF), CLK_PCOM("vpe_clk", VPE_CLK, NULL, 0), CLK_PCOM("qup_clk", QUP_I2C_CLK, &msm_device_qup_i2c.dev, OFF), + CLK_PCOM("qup_pclk", QUP_I2C_P_CLK, &msm_device_qup_i2c.dev, OFF), /* 7x30 v2 hardware only. */ CLK_PCOM("csi_clk", CSI0_CLK, NULL, 0), From 30b4be40065b17337377fd86ab26546e051111cd Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 23 Jun 2010 18:04:33 -0700 Subject: [PATCH 0672/2556] [ARM] i2c: qup (msm): Do not take pclk name from pdata, request it normally Change-Id: I2edcb4a66f6cb1d3cfdb19fd725095d0d60c4b93 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/include/mach/msm_qup.h | 1 - drivers/i2c/busses/i2c-qup.c | 11 ++--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/msm_qup.h b/arch/arm/mach-msm/include/mach/msm_qup.h index d884c20718eb7..e95b5d662f5be 100644 --- a/arch/arm/mach-msm/include/mach/msm_qup.h +++ b/arch/arm/mach-msm/include/mach/msm_qup.h @@ -10,7 +10,6 @@ struct msm_qup_i2c_platform_data { int pri_dat; int aux_clk; int aux_dat; - const char *pclk; void (*msm_i2c_config_gpio)(int iface, int config_type); }; diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index 6c2d7fa7e4506..9983dd8956576 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -735,15 +735,8 @@ qup_i2c_probe(struct platform_device *pdev) goto err_clk_get_failed; } - if (pdata->pclk != NULL) { - pclk = clk_get(&pdev->dev, pdata->pclk); - if (IS_ERR(pclk)) { - dev_err(&pdev->dev, "Could not get pclock\n"); - ret = PTR_ERR(pclk); - clk_put(clk); - goto err_clk_get_failed; - } - } else + pclk = clk_get(&pdev->dev, "qup_pclk"); + if (IS_ERR(clk)) pclk = NULL; if (!(pdata->msm_i2c_config_gpio)) { From 6e3d328b7c25e95973b42ff62ed51e2ceda18f1e Mon Sep 17 00:00:00 2001 From: James Jacobsson Date: Wed, 9 Jun 2010 20:22:12 -0700 Subject: [PATCH 0673/2556] [ARM] msm: msm_vibrator: use correct values for msm7x30 Change-Id: Ib5dda4c6a2e9cf8cfd7a40c2bcb2b15657b54695 Signed-off-by: James Jacobsson --- arch/arm/mach-msm/msm_vibrator.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/msm_vibrator.c b/arch/arm/mach-msm/msm_vibrator.c index 1adca194fbb70..8b8174145a141 100644 --- a/arch/arm/mach-msm/msm_vibrator.c +++ b/arch/arm/mach-msm/msm_vibrator.c @@ -23,13 +23,15 @@ #include #define PM_LIBPROG 0x30000061 -#if (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225) +#if defined(CONFIG_ARCH_MSM7X30) +#define PM_LIBVERS 0x00030001 +#elif defined(CONFIG_MSM_LEGACY_7X00A_AMSS) #define PM_LIBVERS 0xfb837d0b #else #define PM_LIBVERS MSM_RPC_VERS(1,1) #endif -#ifdef CONFIG_ARCH_QSD8X50 +#if defined(CONFIG_ARCH_QSD8X50) || defined(CONFIG_ARCH_MSM7X30) #define HTC_PROCEDURE_SET_VIB_ON_OFF 22 #else #define HTC_PROCEDURE_SET_VIB_ON_OFF 21 From 3d4b066c828d3f7a74f82e687570a6d66e8c15da Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 24 Jun 2010 13:31:10 -0700 Subject: [PATCH 0674/2556] [ARM] msm: mmc: Fix built-in device detect delay Signed-off-by: Dmitry Shmidt --- arch/arm/include/asm/mach/mmc.h | 1 + arch/arm/mach-msm/include/mach/mmc.h | 1 + drivers/mmc/host/msm_sdcc.c | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h index 8948dec26067a..f8d391ad92037 100644 --- a/arch/arm/include/asm/mach/mmc.h +++ b/arch/arm/include/asm/mach/mmc.h @@ -17,6 +17,7 @@ struct embedded_sdio_data { struct mmc_platform_data { unsigned int ocr_mask; /* available voltages */ + int built_in; /* built-in device flag */ u32 (*translate_vdd)(struct device *, unsigned int); unsigned int (*status)(struct device *); struct embedded_sdio_data *embedded_sdio; diff --git a/arch/arm/mach-msm/include/mach/mmc.h b/arch/arm/mach-msm/include/mach/mmc.h index d54b6b086cff4..77515ab374b8b 100644 --- a/arch/arm/mach-msm/include/mach/mmc.h +++ b/arch/arm/mach-msm/include/mach/mmc.h @@ -17,6 +17,7 @@ struct embedded_sdio_data { struct msm_mmc_platform_data { unsigned int ocr_mask; /* available voltages */ + int built_in; /* built-in device flag */ u32 (*translate_vdd)(struct device *, unsigned int); unsigned int (*status)(struct device *); struct embedded_sdio_data *embedded_sdio; diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 0c6e2632ec20e..4290c1cd375c3 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1045,7 +1045,7 @@ msmsdcc_check_status(unsigned long data) if (status ^ host->oldstat) { pr_info("%s: Slot status change detected (%d -> %d)\n", mmc_hostname(host->mmc), host->oldstat, status); - if (status) + if (status && !host->plat->built_in) mmc_detect_change(host->mmc, (5 * HZ) / 2); else mmc_detect_change(host->mmc, 0); From be32c6a6757bfc1692a86476c3a5738ab50fa178 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 24 Jun 2010 14:24:47 -0700 Subject: [PATCH 0675/2556] [ARM] msm: mahimahi: Set built-in flag for wlan device Signed-off-by: Dmitry Shmidt --- arch/arm/mach-msm/board-mahimahi-mmc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/board-mahimahi-mmc.c b/arch/arm/mach-msm/board-mahimahi-mmc.c index 5b04878cce01c..78ed97fc49d39 100644 --- a/arch/arm/mach-msm/board-mahimahi-mmc.c +++ b/arch/arm/mach-msm/board-mahimahi-mmc.c @@ -238,6 +238,7 @@ static unsigned int mahimahi_wifi_status(struct device *dev) static struct mmc_platform_data mahimahi_wifi_data = { .ocr_mask = MMC_VDD_28_29, + .built_in = 1, .status = mahimahi_wifi_status, .register_status_notify = mahimahi_wifi_status_register, .embedded_sdio = &mahimahi_wifi_emb_data, From 25885bc596f0c0992e346bbd176fae0090bad97f Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 28 Jun 2010 17:28:07 -0400 Subject: [PATCH 0676/2556] [ARM] msm: usb: diag: usb_function.hidden renamed to usb_function.disabled Signed-off-by: Mike Lockwood --- drivers/usb/gadget/diag.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/diag.c b/drivers/usb/gadget/diag.c index 3611462fb48d0..c100ed40d7678 100644 --- a/drivers/usb/gadget/diag.c +++ b/drivers/usb/gadget/diag.c @@ -870,7 +870,7 @@ module_param_call(tx_rx_count, NULL, diag_get_tx_rx_count, NULL, 0444); static int diag_get_enabled(char *buffer, struct kernel_param *kp) { - buffer[0] = '0' + !_context.function.hidden; + buffer[0] = '0' + !_context.function.disabled; return 1; } module_param_call(enabled, diag_set_enabled, diag_get_enabled, NULL, 0664); @@ -904,7 +904,7 @@ int diag_bind_config(struct usb_configuration *c) ctxt->function.set_alt = diag_function_set_alt; ctxt->function.disable = diag_function_disable; - ctxt->function.hidden = !_context.function_enable; + ctxt->function.disabled = !_context.function_enable; return usb_add_function(c, &ctxt->function); } From b0bc44a9b6e51addc3c58085f3dde4c6ed5e090a Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sun, 27 Jun 2010 22:26:17 -0700 Subject: [PATCH 0677/2556] mfd: pm8058: export function to read real-time irq status The pm8058 hardware blocks do not contain separate status bits outside the irq block. Therefore, the child devices must be able to directly read the irq block's real-time status registers (i.e. the unlatched irq status) one way or another. Change-Id: I7014b838e7c1e310cbc0557e858f6a6a32679428 Signed-off-by: Dima Zavin --- drivers/mfd/pm8058-core.c | 10 ++++++++++ include/linux/mfd/pm8058.h | 3 +++ 2 files changed, 13 insertions(+) diff --git a/drivers/mfd/pm8058-core.c b/drivers/mfd/pm8058-core.c index 066abba6b920b..8fda157c95eab 100644 --- a/drivers/mfd/pm8058-core.c +++ b/drivers/mfd/pm8058-core.c @@ -435,6 +435,16 @@ static int get_curr_irq_stat(struct pm8058 *pmic, unsigned int irq) return ret; } +int pm8058_irq_get_status(struct device *dev, unsigned int irq) +{ + struct pm8058 *pmic = dev_get_drvdata(dev); + + if (irq >= PM8058_NUM_IRQS) + return -EINVAL; + return get_curr_irq_stat(pmic, irq); +} +EXPORT_SYMBOL(pm8058_irq_get_status); + static int cfg_irq_blk_bit_perm(struct pm8058 *pmic, u8 blk, u8 mask) { int ret; diff --git a/include/linux/mfd/pm8058.h b/include/linux/mfd/pm8058.h index 85b119e0d30e5..5e8dcbf3034be 100644 --- a/include/linux/mfd/pm8058.h +++ b/include/linux/mfd/pm8058.h @@ -146,6 +146,7 @@ int pm8058_read_buf(struct device *dev, u16 addr, u8 *buf, int cnt); int pm8058_gpio_mux_cfg(struct device *dev, unsigned int gpio, struct pm8058_pin_config *cfg); int pm8058_gpio_mux(unsigned int gpio, struct pm8058_pin_config *cfg); +int pm8058_irq_get_status(struct device *dev, unsigned int irq); #else static inline int pm8058_readb(struct device *dev, u16 addr, u8 *val) { return 0; } @@ -159,6 +160,8 @@ static inline int pm8058_gpio_mux_cfg(struct device *dev, unsigned int gpio, struct pm8058_pin_config *cfg) { return 0; } static inline int pm8058_gpio_mux(unsigned int gpio, struct pm8058_pin_config *cfg) { return 0; } +static inline int pm8058_irq_get_status(struct device *dev, unsigned int irq) +{ return 0; } #endif #endif From b9e8fe3e78d7eb9cf0bc1d3892c25a2236f117ef Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 30 Jun 2010 21:29:58 -0700 Subject: [PATCH 0678/2556] mfd: pm8058: expose more charger irqs Expose a few more irqs from the irq map in the pmic. This will facilitate detecting charging on/off, fast charge, etc. Change-Id: I1cbf5c2276b66c4935c8d0d40f43e93c37599032 Signed-off-by: Dima Zavin --- drivers/mfd/pm8058-core.c | 4 ++-- include/linux/mfd/pm8058.h | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/pm8058-core.c b/drivers/mfd/pm8058-core.c index 8fda157c95eab..76c76e8a5dbd4 100644 --- a/drivers/mfd/pm8058-core.c +++ b/drivers/mfd/pm8058-core.c @@ -73,7 +73,7 @@ module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); * bank 0 - GPIO IRQs start=(24 * 8) cnt=40 (gpios 0-39) * bank 1 - MPP IRQs start=(16 * 8) cnt=12 (mpps 0-11) * bank 2 - keypad irqs start=(9*8 + 1) cnt=2 - * bank 3 - charger irqs start=(1*8 + 7) cnt=1 + * bank 3 - charger irqs start=(1*8 + 7) cnt=7 (ends at 2*8 + 5) * */ struct pm8058_irq_bank { @@ -116,7 +116,7 @@ struct pm8058_irq_group { static const struct pm8058_irq_group pm8058_irq_groups[] = { { .stat_reg = REG_IRQ_M_STATUS1, - .valid_mask = 0x2, + .valid_mask = 0x6, .root_mask = 0x2, .block_offset = 0, }, diff --git a/include/linux/mfd/pm8058.h b/include/linux/mfd/pm8058.h index 5e8dcbf3034be..329b5be9a5349 100644 --- a/include/linux/mfd/pm8058.h +++ b/include/linux/mfd/pm8058.h @@ -22,7 +22,7 @@ #define PM8058_NUM_GPIO_IRQS 40 #define PM8058_NUM_MPP_IRQS 12 #define PM8058_NUM_KEYPAD_IRQS 2 -#define PM8058_NUM_CHARGER_IRQS 1 +#define PM8058_NUM_CHARGER_IRQS 7 #define PM8058_NUM_IRQS (PM8058_NUM_GPIO_IRQS + \ PM8058_NUM_MPP_IRQS + \ PM8058_NUM_KEYPAD_IRQS + \ @@ -39,7 +39,12 @@ #define PM8058_KEYPAD_IRQ (PM8058_FIRST_KEYPAD_IRQ + 0) #define PM8058_KEYPAD_STUCK_IRQ (PM8058_FIRST_KEYPAD_IRQ + 1) + #define PM8058_CHGVAL_IRQ (PM8058_FIRST_CHARGER_IRQ + 0) +#define PM8058_CHGEND_IRQ (PM8058_FIRST_CHARGER_IRQ + 1) +#define PM8058_FASTCHG_IRQ (PM8058_FIRST_CHARGER_IRQ + 2) +#define PM8058_CHGFAIL_IRQ (PM8058_FIRST_CHARGER_IRQ + 5) +#define PM8058_CHGDONE_IRQ (PM8058_FIRST_CHARGER_IRQ + 6) #define PM8058_GPIO_TO_IRQ(base,gpio) (PM8058_FIRST_GPIO_IRQ + \ (base) + (gpio)) From a37f79ac40d9f0de0aae33b252bda5e5c5b2033e Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 30 Jun 2010 22:04:23 -0700 Subject: [PATCH 0679/2556] mfd: pm8058: remove vbus detection from the main pmic driver Change-Id: Ib13ece8022f7000f4666a1cce54c2cff29d21a01 Signed-off-by: Dima Zavin --- drivers/mfd/pm8058-core.c | 38 -------------------------------------- include/linux/mfd/pm8058.h | 3 --- 2 files changed, 41 deletions(-) diff --git a/drivers/mfd/pm8058-core.c b/drivers/mfd/pm8058-core.c index 76c76e8a5dbd4..0dee322c5dc62 100644 --- a/drivers/mfd/pm8058-core.c +++ b/drivers/mfd/pm8058-core.c @@ -811,27 +811,6 @@ static int add_keypad_device(struct pm8058 *pmic, void *pdata) return 0; } -/* vbus detection helper */ -static void check_vbus(struct pm8058 *pmic) -{ - int ret; - - ret = get_curr_irq_stat(pmic, PM8058_CHGVAL_IRQ); - if (ret >= 0) - pmic->pdata->vbus_present(ret); - else - pr_err("%s: can't read status!! ignoring event?!\n", __func__); - /* XXX: maybe add some retries? */ -} - -static irqreturn_t pm8058_vbus_irq_handler(int irq, void *dev) -{ - struct pm8058 *pmic = dev; - - check_vbus(pmic); - return IRQ_HANDLED; -} - static int pm8058_probe(struct platform_device *pdev) { struct pm8058_platform_data *pdata = pdev->dev.platform_data; @@ -909,25 +888,8 @@ static int pm8058_probe(struct platform_device *pdev) } } - if (pdata->vbus_present) { - int vbus_irq = pmic->irq_base + PM8058_CHGVAL_IRQ; - ret = request_threaded_irq(vbus_irq, NULL, - pm8058_vbus_irq_handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "pm8058-vbus", pmic); - if (ret) { - pr_err("%s: can't request vbus irq\n", __func__); - goto err_req_vbus_irq; - } - set_irq_wake(vbus_irq, 1); - /* run once to handle the case where vbus is already present */ - check_vbus(pmic); - } return 0; -err_req_vbus_irq: - if (pmic->kp_pdev) - platform_device_put(pmic->kp_pdev); err_add_kp_dev: err_pdata_init: the_pm8058 = NULL; diff --git a/include/linux/mfd/pm8058.h b/include/linux/mfd/pm8058.h index 329b5be9a5349..a75cab02a5140 100644 --- a/include/linux/mfd/pm8058.h +++ b/include/linux/mfd/pm8058.h @@ -81,9 +81,6 @@ struct pm8058_platform_data { unsigned int gpio_base; int (*init)(struct device *dev); - /* function to call on vbus detect */ - void (*vbus_present)(bool present); - /* child devices */ struct pm8058_keypad_platform_data *keypad_pdata; }; From dedfc634003bff068e8c63fd61924ffd45129cc2 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 30 Jun 2010 22:09:24 -0700 Subject: [PATCH 0680/2556] power: add a driver for the battery charger on PM8058 This driver also handles vbus detection. Change-Id: I145267ae035e313b006d4da28afd1cc0624af613 Signed-off-by: Dima Zavin --- drivers/power/Kconfig | 6 + drivers/power/Makefile | 1 + drivers/power/pm8058-charger.c | 320 +++++++++++++++++++++++++++++++++ include/linux/mfd/pm8058.h | 17 ++ 4 files changed, 344 insertions(+) create mode 100644 drivers/power/pm8058-charger.c diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index f017a6e2a09ed..ae211c3ce9d09 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -211,4 +211,10 @@ config CHARGER_GPIO This driver can be build as a module. If so, the module will be called gpio-charger. +config CHARGER_PM8058 + bool "Qualcomm PM8058 pmic charger driver" + depends on PM8058 + help + Say Y to include support for the pm8058 charge controller + endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 5cbbddf1d4541..b0d77944cd230 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -35,3 +35,4 @@ obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o +obj-$(CONFIG_CHARGER_PM8058) += pm8058-charger.o diff --git a/drivers/power/pm8058-charger.c b/drivers/power/pm8058-charger.c new file mode 100644 index 0000000000000..0642274d52e58 --- /dev/null +++ b/drivers/power/pm8058-charger.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2010 Google, Inc. + * + * Author: Dima Zavin + * + * 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 + + +struct pm8058_charger { + struct device *pmic_dev; + int chgval_irq; + int fastchg_irq; + + struct power_supply ac_supply; + struct power_supply usb_supply; + + struct pm8058_charger_platform_data *pdata; + + spinlock_t lock; + bool can_charge; + bool is_ac; + bool is_online; + bool vbus_present; + int charge_type; + u32 max_current; +}; + +static struct pm8058_charger *the_pm8058_charger; + +/* TODO: the usb core driver should provide the maximum current draw value to us + * for charging */ + +void pm8058_notify_charger_connected(int status) +{ + struct pm8058_charger *charger = the_pm8058_charger; + u32 max_current = 0; + bool is_ac; + bool is_online; + bool change = false; + unsigned long flags; + + if (!charger) + return; + + printk("### %s(%d) ###\n", __func__, status); + if (status && !charger->vbus_present) + pr_warning("%s: cable status mismatch %d %d\n", __func__, + status, charger->vbus_present); + + switch (status) { + case 1: + /* usb (pc) charging */ + max_current = 500; + is_ac = false; + is_online = true; + break; + case 2: + /* wall charger */ + max_current = 1500; + is_ac = true; + is_online = true; + break; + case 0: + default: + /* disable charging */ + max_current = 0; + is_ac = false; + is_online = false; + break; + } + spin_lock_irqsave(&charger->lock, flags); + if (max_current != charger->max_current || + is_ac != charger->is_ac || is_online != charger->is_online) { + charger->max_current = max_current; + charger->is_ac = is_ac; + charger->is_online = is_online; + change = true; + } + spin_unlock_irqrestore(&charger->lock, flags); + /* for now, charge control is done on the modem side, so we have to + * delegate to the board file. Eventually, all charge control will + * be done in this driver */ + if (change && charger->pdata->charge) + charger->pdata->charge(max_current, is_ac); + + power_supply_changed(&charger->ac_supply); + power_supply_changed(&charger->usb_supply); +} +EXPORT_SYMBOL_GPL(pm8058_notify_charger_connected); + +static void check_chgval(struct pm8058_charger *charger) +{ + int ret; + unsigned long flags; + + ret = pm8058_irq_get_status(charger->pmic_dev, PM8058_CHGVAL_IRQ); + if (ret >= 0) { + spin_lock_irqsave(&charger->lock, flags); + charger->vbus_present = !!ret; + spin_unlock_irqrestore(&charger->lock, flags); + charger->pdata->vbus_present(ret); + } else { + pr_err("%s: can't read status!! ignoring event?!\n", __func__); + } +} + +static irqreturn_t chgval_irq_handler(int irq, void *dev_id) +{ + struct pm8058_charger *charger = dev_id; + + check_chgval(charger); + return IRQ_HANDLED; +} + +/* should only get this irq when we are plugged in */ +static irqreturn_t fastchg_irq_handler(int irq, void *dev_id) +{ + struct pm8058_charger *charger = dev_id; + int ret; + bool fast_charging; + unsigned long flags; + + ret = pm8058_irq_get_status(charger->pmic_dev, PM8058_FASTCHG_IRQ); + if (ret < 0) + return IRQ_HANDLED; + fast_charging = !!ret; + + spin_lock_irqsave(&charger->lock, flags); + if (fast_charging) { + if (!charger->vbus_present) { + pr_err("%s: charging without vbus?!\n", __func__); + goto done; + } + charger->charge_type = POWER_SUPPLY_CHARGE_TYPE_FAST; + } else { + /* charging is either stopped (done/overtemp/etc.), or we + * are trickle charging. */ + /* TODO: detect trickle charging mode */ + if (charger->is_online) + charger->charge_type = POWER_SUPPLY_CHARGE_TYPE_NONE; + else + charger->charge_type = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + } + +done: + spin_unlock_irqrestore(&charger->lock, flags); + + power_supply_changed(&charger->ac_supply); + power_supply_changed(&charger->usb_supply); + return IRQ_HANDLED; +} + +static int power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct pm8058_charger *charger; + + if (psy->type == POWER_SUPPLY_TYPE_MAINS) + charger = container_of(psy, struct pm8058_charger, ac_supply); + else + charger = container_of(psy, struct pm8058_charger, usb_supply); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if (psy->type == POWER_SUPPLY_TYPE_MAINS) + val->intval = charger->is_online && charger->is_ac; + else + val->intval = charger->is_online && !charger->is_ac; + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + /* for now, fake fast charge all the time if we're on */ + if (psy->type == POWER_SUPPLY_TYPE_MAINS) + val->intval = charger->is_ac ? charger->charge_type : + POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + else + val->intval = charger->is_online && !charger->is_ac ? + charger->charge_type : + POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + break; + default: + return -EINVAL; + } + + return 0; +} + +static enum power_supply_property power_properties[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CHARGE_TYPE, +}; + +static int __init pm8058_charger_probe(struct platform_device *pdev) +{ + struct pm8058_charger_platform_data *pdata = pdev->dev.platform_data; + struct pm8058_charger *charger; + int chgval_irq; + int fastchg_irq; + int ret; + + chgval_irq = platform_get_irq_byname(pdev, "chgval_irq"); + fastchg_irq = platform_get_irq_byname(pdev, "fastchg_irq"); + + if (!pdata || chgval_irq < 0 || fastchg_irq < 0) { + pr_err("%s: missing platform data/resources\n", __func__); + return -EINVAL; + } + + charger = kzalloc(sizeof(struct pm8058_charger), GFP_KERNEL); + if (!charger) { + pr_err("%s: can't alloc mem for charger struct\n", __func__); + return -ENOMEM; + } + + charger->pmic_dev = pdev->dev.parent; + charger->pdata = pdata; + platform_set_drvdata(pdev, charger); + spin_lock_init(&charger->lock); + + the_pm8058_charger = charger; + + charger->ac_supply.name = "ac"; + charger->ac_supply.type = POWER_SUPPLY_TYPE_MAINS; + charger->ac_supply.supplied_to = pdata->supplied_to; + charger->ac_supply.num_supplicants = pdata->num_supplicants; + charger->ac_supply.properties = power_properties; + charger->ac_supply.num_properties = ARRAY_SIZE(power_properties); + charger->ac_supply.get_property = power_get_property; + + charger->usb_supply.name = "usb"; + charger->usb_supply.type = POWER_SUPPLY_TYPE_USB; + charger->usb_supply.supplied_to = pdata->supplied_to; + charger->usb_supply.num_supplicants = pdata->num_supplicants; + charger->usb_supply.properties = power_properties; + charger->usb_supply.num_properties = ARRAY_SIZE(power_properties); + charger->usb_supply.get_property = power_get_property; + + ret = power_supply_register(&pdev->dev, &charger->ac_supply); + if (ret) + goto err_reg_ac_supply; + ret = power_supply_register(&pdev->dev, &charger->usb_supply); + if (ret) + goto err_reg_usb_supply; + + ret = request_threaded_irq(chgval_irq, NULL, chgval_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "pm8058-charger-valid", charger); + if (ret) { + pr_err("%s: can't request chgval_irq\n", __func__); + goto err_req_chgval_irq; + } + charger->chgval_irq = chgval_irq; + + ret = request_threaded_irq(fastchg_irq, NULL, fastchg_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "pm8058-charger-fastchg", charger); + if (ret) { + pr_err("%s: can't request stuck\n", __func__); + goto err_req_fastchg_irq; + } + charger->fastchg_irq = fastchg_irq; + enable_irq_wake(charger->chgval_irq); + + pr_info("%s: driver initialized\n", __func__); + check_chgval(charger); + + return 0; + +err_req_fastchg_irq: + free_irq(chgval_irq, charger); +err_req_chgval_irq: + power_supply_unregister(&charger->usb_supply); +err_reg_usb_supply: + power_supply_unregister(&charger->ac_supply); +err_reg_ac_supply: + platform_set_drvdata(pdev, NULL); + the_pm8058_charger = NULL; + kfree(charger); + return ret; +} + +static struct platform_driver pm8058_charger_driver = { + .probe = pm8058_charger_probe, + .driver = { + .name = "pm8058-charger", + .owner = THIS_MODULE, + }, +}; + +static int __init pm8058_charger_init(void) +{ + return platform_driver_register(&pm8058_charger_driver); +} + +module_init(pm8058_charger_init); +MODULE_DESCRIPTION("PM8058 Charger Driver"); +MODULE_AUTHOR("Dima Zavin "); +MODULE_LICENSE("GPL"); + diff --git a/include/linux/mfd/pm8058.h b/include/linux/mfd/pm8058.h index a75cab02a5140..4e0ffdd8ea1e7 100644 --- a/include/linux/mfd/pm8058.h +++ b/include/linux/mfd/pm8058.h @@ -76,6 +76,16 @@ struct pm8058_keypad_platform_data { int (*init)(struct device *dev); }; +struct pm8058_charger_platform_data { + /* function to call on vbus detect */ + void (*vbus_present)(bool present); + + int (*charge)(u32 max_current, bool is_ac); + + char **supplied_to; + int num_supplicants; +}; + struct pm8058_platform_data { unsigned int irq_base; unsigned int gpio_base; @@ -83,6 +93,7 @@ struct pm8058_platform_data { /* child devices */ struct pm8058_keypad_platform_data *keypad_pdata; + struct pm8058_charger_platform_data *charger_pdata; }; #define PM8058_GPIO_VIN_SRC_VPH_PWR 0x0 /* VDD_L6_L7 */ @@ -166,4 +177,10 @@ static inline int pm8058_irq_get_status(struct device *dev, unsigned int irq) { return 0; } #endif +#ifdef CONFIG_CHARGER_PM8058 +void pm8058_notify_charger_connected(int status); +#else +static inline void pm8058_notify_charger_connected(int status) {} +#endif + #endif From 95b51c5af2cc6ffcffd3eabe9aed046a413ff7a6 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 30 Jun 2010 22:10:13 -0700 Subject: [PATCH 0681/2556] mfd: pm8058: publish the pm8058-charger device and platform_data Change-Id: I1042f3d0ec6aa951bc7526c10206b88769a0a8b3 Signed-off-by: Dima Zavin --- drivers/mfd/pm8058-core.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/mfd/pm8058-core.c b/drivers/mfd/pm8058-core.c index 0dee322c5dc62..24410b5877c96 100644 --- a/drivers/mfd/pm8058-core.c +++ b/drivers/mfd/pm8058-core.c @@ -161,6 +161,7 @@ struct pm8058 { struct pm8058_platform_data *pdata; struct platform_device *kp_pdev; + struct platform_device *charger_pdev; }; static struct pm8058 *the_pm8058; @@ -811,6 +812,33 @@ static int add_keypad_device(struct pm8058 *pmic, void *pdata) return 0; } +static int add_charger_device(struct pm8058 *pmic, void *pdata) +{ + struct platform_device *pdev; + struct resource irq_res[] = { + { + .start = pmic->irq_base + PM8058_CHGVAL_IRQ, + .end = pmic->irq_base + PM8058_CHGVAL_IRQ, + .flags = IORESOURCE_IRQ, + .name = "chgval_irq", + }, + { + .start = pmic->irq_base + PM8058_FASTCHG_IRQ, + .end = pmic->irq_base + PM8058_FASTCHG_IRQ, + .flags = IORESOURCE_IRQ, + .name = "fastchg_irq", + } + }; + + pdev = add_child_device(pmic, "pm8058-charger", pdata, irq_res, + ARRAY_SIZE(irq_res)); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + pmic->charger_pdev = pdev; + return 0; +} + static int pm8058_probe(struct platform_device *pdev) { struct pm8058_platform_data *pdata = pdev->dev.platform_data; @@ -888,8 +916,19 @@ static int pm8058_probe(struct platform_device *pdev) } } + if (pdata->charger_pdata) { + ret = add_charger_device(pmic, pdata->charger_pdata); + if (ret) { + pr_err("%s: can't add child charger dev\n", __func__); + goto err_add_charger_dev; + } + } + return 0; +err_add_charger_dev: + if (pmic->kp_pdev) + platform_device_put(pmic->kp_pdev); err_add_kp_dev: err_pdata_init: the_pm8058 = NULL; From eadd9ee20d036fb80ec3c178e4772bd819336566 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 1 Jul 2010 23:21:33 -0700 Subject: [PATCH 0682/2556] [ARM] msm: tag ebi1 clk on 7x30 as shared to allow for handles Change-Id: I81eb99d3d9c60fd6c4b15f18128164e1d4be274e Signed-off-by: Dima Zavin --- arch/arm/mach-msm/devices-msm7x30.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c index aeb380bc2f583..6fe5d20a7f429 100644 --- a/arch/arm/mach-msm/devices-msm7x30.c +++ b/arch/arm/mach-msm/devices-msm7x30.c @@ -688,7 +688,7 @@ struct clk msm_clocks_7x30[] = { CLK_PCOM("adm_clk", ADM_CLK, NULL, 0), CLK_PCOM("adsp_clk", ADSP_CLK, NULL, 0), CLK_PCOM("cam_m_clk", CAM_M_CLK, NULL, 0), - CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN), + CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN | CLKFLAG_SHARED), CLK_PCOM("ecodec_clk", ECODEC_CLK, NULL, 0), CLK_PCOM("emdh_clk", EMDH_CLK, &msm_device_mddi1.dev, OFF | CLK_MINMAX), CLK_PCOM("emdh_pclk", EMDH_P_CLK, &msm_device_mddi1.dev, OFF), From d9fc1d1bfa0a2872303cf4adcc29220ef1116c57 Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Fri, 2 Jul 2010 14:43:45 -0700 Subject: [PATCH 0683/2556] [ARM] video: msm: kgsl: Enable shadow writes for context switching Using shadow writes for context switching gives better performance. Change-Id: Id901fba7ae7be57518ec1e800c891b0438828807 Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c index 4489dd4eaa77c..3e5262e8f00fa 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c @@ -28,7 +28,6 @@ #include "kgsl_pm4types.h" #include "kgsl_cmdstream.h" -#define DISABLE_SHADOW_WRITES /* * * Memory Map for Register, Constant & Instruction Shadow, and Command Buffers From 3d761060382914c4af5c83e230783fba3d8891d5 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 2 Jul 2010 15:08:47 -0700 Subject: [PATCH 0684/2556] [ARM] msm: cpufreq: Fix section mismatch warning Signed-off-by: Dmitry Shmidt --- arch/arm/mach-msm/cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c index 5317991240227..fd38a5ad58c11 100644 --- a/arch/arm/mach-msm/cpufreq.c +++ b/arch/arm/mach-msm/cpufreq.c @@ -83,7 +83,7 @@ static int msm_cpufreq_verify(struct cpufreq_policy *policy) return 0; } -static int __init msm_cpufreq_init(struct cpufreq_policy *policy) +static int msm_cpufreq_init(struct cpufreq_policy *policy) { struct cpufreq_frequency_table *table = cpufreq_frequency_get_table(smp_processor_id()); From 2979914737dcd5903cf436a8854d8b3836789d69 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 2 Jul 2010 15:18:23 -0700 Subject: [PATCH 0685/2556] serial_hs: msm: Fix section mismatch warning Signed-off-by: Dmitry Shmidt --- drivers/tty/serial/msm_serial_hs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 927f295191104..305b8b885cad7 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -1339,7 +1339,7 @@ static int uartdm_init_port(struct uart_port *uport) return 0; } -static int __init msm_hs_probe(struct platform_device *pdev) +static int msm_hs_probe(struct platform_device *pdev) { int ret; struct uart_port *uport; From 3f4d55b4056b588bc3d19b9f3138e56d7bd7b730 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 7 Jul 2010 11:13:59 -0700 Subject: [PATCH 0686/2556] rtc: msm7x00a: Register for multiple rpc versions Some newer AMSS versions have added new features to the protocol and rev'd the major version. The subset we use is still the same so the driver does not need need any modifications except to properly pick up the working RPC major versions. Change-Id: I01a296989cb82c53d4dcd6ce8851b44220926d82 Signed-off-by: Dima Zavin --- drivers/rtc/rtc-msm7x00a.c | 52 ++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/drivers/rtc/rtc-msm7x00a.c b/drivers/rtc/rtc-msm7x00a.c index 5cc3c80ac7aa5..f14608a2591a6 100644 --- a/drivers/rtc/rtc-msm7x00a.c +++ b/drivers/rtc/rtc-msm7x00a.c @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -30,11 +31,14 @@ extern void msm_pm_set_max_sleep_time(int64_t sleep_time_ns); +static const char *rpc_versions[] = { #if !defined(CONFIG_MSM_LEGACY_7X00A_AMSS) -#define APP_TIMEREMOTE_PDEV_NAME "rs30000048:00010000" + "rs30000048:00040000", + "rs30000048:00010000", #else -#define APP_TIMEREMOTE_PDEV_NAME "rs30000048:0da5b528" + "rs30000048:0da5b528", #endif +}; #define TIMEREMOTE_PROCEEDURE_SET_JULIAN 6 #define TIMEREMOTE_PROCEEDURE_GET_JULIAN 7 @@ -198,6 +202,9 @@ msmrtc_probe(struct platform_device *pdev) struct rpcsvr_platform_device *rdev = container_of(pdev, struct rpcsvr_platform_device, base); + if (rtc) + return -EBUSY; + ep = msm_rpc_connect(rdev->prog, rdev->vers, 0); if (IS_ERR(ep)) { printk(KERN_ERR "%s: init rpc failed! rc = %ld\n", @@ -257,20 +264,39 @@ msmrtc_resume(struct platform_device *dev) return 0; } -static struct platform_driver msmrtc_driver = { - .probe = msmrtc_probe, - .suspend = msmrtc_suspend, - .resume = msmrtc_resume, - .driver = { - .name = APP_TIMEREMOTE_PDEV_NAME, - .owner = THIS_MODULE, - }, -}; - static int __init msmrtc_init(void) { + int i; + int ret; + struct platform_driver *pdrv[ARRAY_SIZE(rpc_versions)]; + rtcalarm_time = 0; - return platform_driver_register(&msmrtc_driver); + + /* register the devices for all the major versions we support, only + * one should match */ + for (i = 0; i < ARRAY_SIZE(rpc_versions); i++) { + pdrv[i] = kzalloc(sizeof(struct platform_driver), GFP_KERNEL); + if (!pdrv[i]) { + ret = -ENOMEM; + goto err; + } + pdrv[i]->probe = msmrtc_probe; + pdrv[i]->suspend = msmrtc_suspend; + pdrv[i]->resume = msmrtc_resume; + pdrv[i]->driver.name = rpc_versions[i]; + pdrv[i]->driver.owner = THIS_MODULE; + ret = platform_driver_register(pdrv[i]); + if (ret) { + kfree(pdrv[i]); + goto err; + } + } + return 0; + +err: + for (--i; i >= 0; i--) + platform_driver_unregister(pdrv[i]); + return ret; } module_init(msmrtc_init); From 64362ec9c24b4f0bc23827272dcf67c34901f8bb Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Wed, 7 Jul 2010 15:07:55 -0700 Subject: [PATCH 0687/2556] [ARM] msm: smd_rpcrouter: handle large message writes For messages > RPCROUTER_DATASIZE_MAX, we must fragment the message into multiple packets all sharing the same MID, but with the first and last packet flagged. Change-Id: Idd5e4689c33a2c7ddbb43676b01e4f2de9189c4a Signed-off-by: Brian Swetland --- arch/arm/mach-msm/smd_rpcrouter.c | 128 ++++++++++++++++++------------ arch/arm/mach-msm/smd_rpcrouter.h | 1 + 2 files changed, 80 insertions(+), 49 deletions(-) diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c index 92a30bb75a12c..ada118cab41c7 100644 --- a/arch/arm/mach-msm/smd_rpcrouter.c +++ b/arch/arm/mach-msm/smd_rpcrouter.c @@ -102,7 +102,7 @@ static struct wake_lock rpcrouter_wake_lock; static int rpcrouter_need_len; static atomic_t next_xid = ATOMIC_INIT(1); -static uint8_t next_pacmarkid; +static atomic_t next_mid = ATOMIC_INIT(0); static void do_read_data(struct work_struct *work); static void do_create_pdevs(struct work_struct *work); @@ -742,19 +742,69 @@ int msm_rpc_close(struct msm_rpc_endpoint *ept) } EXPORT_SYMBOL(msm_rpc_close); +static int msm_rpc_write_pkt(struct msm_rpc_endpoint *ept, + struct rr_remote_endpoint *r_ept, + struct rr_header *hdr, + uint32_t pacmark, + void *buffer, int count) +{ + DEFINE_WAIT(__wait); + unsigned long flags; + int needed; + + for (;;) { + prepare_to_wait(&r_ept->quota_wait, &__wait, + TASK_INTERRUPTIBLE); + spin_lock_irqsave(&r_ept->quota_lock, flags); + if (r_ept->tx_quota_cntr < RPCROUTER_DEFAULT_RX_QUOTA) + break; + if (signal_pending(current) && + (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) + break; + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + schedule(); + } + finish_wait(&r_ept->quota_wait, &__wait); + + if (signal_pending(current) && + (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) { + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + return -ERESTARTSYS; + } + r_ept->tx_quota_cntr++; + if (r_ept->tx_quota_cntr == RPCROUTER_DEFAULT_RX_QUOTA) + hdr->confirm_rx = 1; + + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + + spin_lock_irqsave(&smd_lock, flags); + + needed = sizeof(*hdr) + hdr->size; + while (smd_write_avail(smd_channel) < needed) { + spin_unlock_irqrestore(&smd_lock, flags); + msleep(250); + spin_lock_irqsave(&smd_lock, flags); + } + + /* TODO: deal with full fifo */ + smd_write(smd_channel, hdr, sizeof(*hdr)); + smd_write(smd_channel, &pacmark, sizeof(pacmark)); + smd_write(smd_channel, buffer, count); + + spin_unlock_irqrestore(&smd_lock, flags); + + return 0; +} + int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count) { struct rr_header hdr; uint32_t pacmark; + uint32_t mid; struct rpc_request_hdr *rq = buffer; struct rr_remote_endpoint *r_ept; - unsigned long flags; - int needed; - DEFINE_WAIT(__wait); - - /* TODO: fragmentation for large outbound packets */ - if (count > (RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t)) || !count) - return -EINVAL; + int ret; + int total; /* snoop the RPC packet and enforce permissions */ @@ -838,56 +888,36 @@ int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count) hdr.version = RPCROUTER_VERSION; hdr.src_pid = ept->pid; hdr.src_cid = ept->cid; - hdr.confirm_rx = 0; - hdr.size = count + sizeof(uint32_t); - for (;;) { - prepare_to_wait(&r_ept->quota_wait, &__wait, - TASK_INTERRUPTIBLE); - spin_lock_irqsave(&r_ept->quota_lock, flags); - if (r_ept->tx_quota_cntr < RPCROUTER_DEFAULT_RX_QUOTA) - break; - if (signal_pending(current) && - (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) - break; - spin_unlock_irqrestore(&r_ept->quota_lock, flags); - schedule(); - } - finish_wait(&r_ept->quota_wait, &__wait); + total = count; - if (signal_pending(current) && - (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) { - spin_unlock_irqrestore(&r_ept->quota_lock, flags); - return -ERESTARTSYS; - } - r_ept->tx_quota_cntr++; - if (r_ept->tx_quota_cntr == RPCROUTER_DEFAULT_RX_QUOTA) - hdr.confirm_rx = 1; + mid = atomic_add_return(1, &next_mid) & 0xFF; - /* bump pacmark while interrupts disabled to avoid race - * probably should be atomic op instead - */ - pacmark = PACMARK(count, ++next_pacmarkid, 0, 1); + while (count > 0) { + unsigned xfer; - spin_unlock_irqrestore(&r_ept->quota_lock, flags); + if (count > RPCROUTER_DATASIZE_MAX) + xfer = RPCROUTER_DATASIZE_MAX; + else + xfer = count; - spin_lock_irqsave(&smd_lock, flags); + hdr.confirm_rx = 0; + hdr.size = xfer + sizeof(uint32_t); - needed = sizeof(hdr) + hdr.size; - while (smd_write_avail(smd_channel) < needed) { - spin_unlock_irqrestore(&smd_lock, flags); - msleep(250); - spin_lock_irqsave(&smd_lock, flags); - } + /* total == count -> must be first packet + * xfer == count -> must be last packet + */ + pacmark = PACMARK(xfer, mid, (total == count), (xfer == count)); - /* TODO: deal with full fifo */ - smd_write(smd_channel, &hdr, sizeof(hdr)); - smd_write(smd_channel, &pacmark, sizeof(pacmark)); - smd_write(smd_channel, buffer, count); + ret = msm_rpc_write_pkt(ept, r_ept, &hdr, pacmark, buffer, xfer); + if (ret < 0) + return ret; - spin_unlock_irqrestore(&smd_lock, flags); + buffer += xfer; + count -= xfer; + } - return count; + return total; } EXPORT_SYMBOL(msm_rpc_write); diff --git a/arch/arm/mach-msm/smd_rpcrouter.h b/arch/arm/mach-msm/smd_rpcrouter.h index a7416a2ec58cf..d63e95e81fed1 100644 --- a/arch/arm/mach-msm/smd_rpcrouter.h +++ b/arch/arm/mach-msm/smd_rpcrouter.h @@ -32,6 +32,7 @@ #define RPCROUTER_VERSION 1 #define RPCROUTER_PROCESSORS_MAX 4 #define RPCROUTER_MSGSIZE_MAX 512 +#define RPCROUTER_DATASIZE_MAX 500 #define RPCROUTER_CLIENT_BCAST_ID 0xffffffff #define RPCROUTER_ROUTER_ADDRESS 0xfffffffe From 2171468bb812f8d4ab7fa255003628e510c7471f Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Wed, 7 Jul 2010 16:51:05 -0700 Subject: [PATCH 0688/2556] [ARM] msm: rpc_router: handle multiple outstanding replies This is not ideal. Technically userspace should be replying to messages before reading more inbound messages, but something's out of whack down there and this gets us beyond that. I'd prefer to get to the bottom of the userspace issue and revert this change though, as it's rather ugly. Change-Id: I29a4a45aebbb66f4f3889363c9ef7cd336e72c7d Signed-off-by: Brian Swetland --- arch/arm/mach-msm/smd_rpcrouter.c | 42 ++++++++++++++++--------------- arch/arm/mach-msm/smd_rpcrouter.h | 22 ++++++++++------ 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c index ada118cab41c7..6e0cf27f2b57b 100644 --- a/arch/arm/mach-msm/smd_rpcrouter.c +++ b/arch/arm/mach-msm/smd_rpcrouter.c @@ -253,6 +253,7 @@ struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev) { struct msm_rpc_endpoint *ept; unsigned long flags; + int i; ept = kmalloc(sizeof(struct msm_rpc_endpoint), GFP_KERNEL); if (!ept) @@ -260,7 +261,9 @@ struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev) memset(ept, 0, sizeof(struct msm_rpc_endpoint)); /* mark no reply outstanding */ - ept->reply_pid = 0xffffffff; + ept->next_rroute = 0; + for (i = 0; i < MAX_REPLY_ROUTE; i++) + ept->rroute[i].pid = 0xffffffff; ept->cid = (uint32_t) ept; ept->pid = RPCROUTER_PID_LOCAL; @@ -852,23 +855,21 @@ int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count) } else { /* RPC REPLY */ /* TODO: locking */ - if (ept->reply_pid == 0xffffffff) { - printk(KERN_ERR - "rr_write: rejecting unexpected reply\n"); - return -EINVAL; - } - if (ept->reply_xid != rq->xid) { - printk(KERN_ERR - "rr_write: rejecting packet w/ bad xid\n"); - return -EINVAL; - } - - hdr.dst_pid = ept->reply_pid; - hdr.dst_cid = ept->reply_cid; + for (ret = 0; ret < MAX_REPLY_ROUTE; ret++) + if (ept->rroute[ret].xid == rq->xid) { + if (ept->rroute[ret].pid == 0xffffffff) + continue; + hdr.dst_pid = ept->rroute[ret].pid; + hdr.dst_cid = ept->rroute[ret].cid; + /* consume this reply */ + ept->rroute[ret].pid = 0xffffffff; + goto found_rroute; + } - /* consume this reply */ - ept->reply_pid = 0xffffffff; + printk(KERN_ERR "rr_write: rejecting packet w/ bad xid\n"); + return -EINVAL; +found_rroute: IO("REPLY on ept %p to xid=%d @ %d:%08x (%d bytes)\n", ept, be32_to_cpu(rq->xid), hdr.dst_pid, hdr.dst_cid, count); @@ -1124,14 +1125,15 @@ int __msm_rpc_read(struct msm_rpc_endpoint *ept, be32_to_cpu(rq->procedure), be32_to_cpu(rq->xid)); /* RPC CALL */ - if (ept->reply_pid != 0xffffffff) { + if (ept->rroute[ept->next_rroute].pid != 0xffffffff) { printk(KERN_WARNING "rr_read: lost previous reply xid...\n"); } /* TODO: locking? */ - ept->reply_pid = pkt->hdr.src_pid; - ept->reply_cid = pkt->hdr.src_cid; - ept->reply_xid = rq->xid; + ept->rroute[ept->next_rroute].pid = pkt->hdr.src_pid; + ept->rroute[ept->next_rroute].cid = pkt->hdr.src_cid; + ept->rroute[ept->next_rroute].xid = rq->xid; + ept->next_rroute = (ept->next_rroute + 1) & (MAX_REPLY_ROUTE - 1); } #if TRACE_RPC_MSG else if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 1)) diff --git a/arch/arm/mach-msm/smd_rpcrouter.h b/arch/arm/mach-msm/smd_rpcrouter.h index d63e95e81fed1..2bf541acac259 100644 --- a/arch/arm/mach-msm/smd_rpcrouter.h +++ b/arch/arm/mach-msm/smd_rpcrouter.h @@ -136,6 +136,15 @@ struct rr_remote_endpoint { struct list_head list; }; +struct msm_reply_route { + uint32_t xid; + uint32_t pid; + uint32_t cid; + uint32_t unused; +}; + +#define MAX_REPLY_ROUTE 4 + struct msm_rpc_endpoint { struct list_head list; @@ -162,15 +171,12 @@ struct msm_rpc_endpoint { uint32_t dst_prog; /* be32 */ uint32_t dst_vers; /* be32 */ - /* reply remote address - * if reply_pid == 0xffffffff, none available - * RPC_REPLY writes may only go to the pid/cid/xid of the - * last RPC_CALL we received. + /* RPC_REPLY writes must be routed to the pid/cid of the + * RPC_CALL they are in reply to. Keep a cache of valid + * xid/pid/cid groups. pid 0xffffffff -> not valid. */ - uint32_t reply_pid; - uint32_t reply_cid; - uint32_t reply_xid; /* be32 */ - uint32_t next_pm; /* Pacmark sequence */ + unsigned next_rroute; + struct msm_reply_route rroute[MAX_REPLY_ROUTE]; /* device node if this endpoint is accessed via userspace */ dev_t dev; From 1f16ead168c4a068bb0edfbfa0d383434b0315ec Mon Sep 17 00:00:00 2001 From: Wu-cheng Li Date: Thu, 8 Jul 2010 09:12:01 -0700 Subject: [PATCH 0689/2556] media: video: msm: improvements to msm_camera to support 7x30. The baseline is M7630AABBQMLZA1250. Signed-off-by: Wu-cheng Li --- arch/arm/mach-msm/include/mach/camera.h | 43 ++++- drivers/media/video/msm/msm_camera.c | 222 ++++++++++++++++++++++-- include/media/msm_camera.h | 78 ++++++++- 3 files changed, 316 insertions(+), 27 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/camera.h b/arch/arm/mach-msm/include/mach/camera.h index 3518b5c35a149..00c7cd5745af4 100644 --- a/arch/arm/mach-msm/include/mach/camera.h +++ b/arch/arm/mach-msm/include/mach/camera.h @@ -1,5 +1,19 @@ -/* - * Copyright (C) 2008-2009 QUALCOMM Incorporated. +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ #ifndef __ASM__ARCH_CAMERA_H @@ -26,6 +40,7 @@ #define NUM_WB_EXP_NEUTRAL_REGION_LINES 4 #define NUM_WB_EXP_STAT_OUTPUT_BUFFERS 3 #define NUM_AUTOFOCUS_MULTI_WINDOW_GRIDS 16 +#define NUM_STAT_OUTPUT_BUFFERS 3 #define NUM_AF_STAT_OUTPUT_BUFFERS 3 enum msm_queue { @@ -42,13 +57,24 @@ enum vfe_resp_msg { VFE_MSG_OUTPUT1, VFE_MSG_OUTPUT2, VFE_MSG_STATS_AF, - VFE_MSG_STATS_WE, + VFE_MSG_STATS_WE, /* AEC + AWB */ + VFE_MSG_OUTPUT_P, /* preview (continuous mode ) */ + VFE_MSG_OUTPUT_T, /* thumbnail (snapshot mode )*/ + VFE_MSG_OUTPUT_S, /* main image (snapshot mode )*/ + VFE_MSG_OUTPUT_V, /* video (continuous mode ) */ + VFE_MSG_STATS_AEC, + VFE_MSG_STATS_AWB, + VFE_MSG_STATS_RS, + VFE_MSG_STATS_CS, + VFE_MSG_STATS_IHIST, + VFE_MSG_STATS_SKIN, }; struct msm_vfe_phy_info { uint32_t sbuf_phy; uint32_t y_phy; uint32_t cbcr_phy; + uint8_t output_id; /* OUTPUT_MODE_P/T/S/V */ }; struct msm_vfe_resp { @@ -92,6 +118,7 @@ struct msm_queue_cmd { enum msm_queue type; void *command; int on_heap; + struct timespec ts; }; struct msm_device_queue { @@ -151,6 +178,7 @@ struct msm_sync { struct mutex lock; struct list_head list; + int get_pic_abort; }; #define MSM_APPS_ID_V4L2 "msm_v4l2" @@ -198,6 +226,7 @@ struct axidata { uint32_t bufnum1; uint32_t bufnum2; struct msm_pmem_region *region; + uint32_t bufnum3; }; /* Below functions are added for V4L2 kernel APIs */ @@ -230,6 +259,13 @@ enum msm_camio_clk_type { CAMIO_VFE_CLK, CAMIO_VFE_AXI_CLK, + CAMIO_VFE_CAMIF_CLK, + CAMIO_VFE_PBDG_CLK, + CAMIO_CAM_MCLK_CLK, + CAMIO_CAMIF_PAD_PBDG_CLK, + CAMIO_CSI_CLK, + CAMIO_CSI_VFE_CLK, + CAMIO_CSI_PCLK, CAMIO_MAX_CLK }; @@ -275,6 +311,7 @@ int msm_camio_clk_disable(enum msm_camio_clk_type clk); int msm_camio_clk_config(uint32_t freq); void msm_camio_clk_rate_set(int rate); void msm_camio_clk_axi_rate_set(int rate); +void msm_disable_io_gpio_clk(struct platform_device *); void msm_camio_camif_pad_reg_reset(void); void msm_camio_camif_pad_reg_reset_2(void); diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c index f0731b6203233..342a2249a1964 100644 --- a/drivers/media/video/msm/msm_camera.c +++ b/drivers/media/video/msm/msm_camera.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -37,6 +38,7 @@ #define dmac_inv_range(a,b) WARN(1, "need cache invalidate\n") #define MSM_MAX_CAMERA_SENSORS 5 +#define CAMERA_STOP_SNAPSHOT 42 #define ERR_USER_COPY(to) pr_err("%s(%d): copy %s user\n", \ __func__, __LINE__, ((to) ? "to" : "from")) @@ -366,6 +368,8 @@ static int __msm_pmem_table_del(struct msm_sync *sync, switch (pinfo->type) { case MSM_PMEM_OUTPUT1: case MSM_PMEM_OUTPUT2: + case MSM_PMEM_VIDEO: + case MSM_PMEM_PREVIEW: case MSM_PMEM_THUMBNAIL: case MSM_PMEM_MAINIMG: case MSM_PMEM_RAW_MAINIMG: @@ -452,10 +456,12 @@ static int __msm_get_frame(struct msm_sync *sync, goto err; } + frame->ts = qcmd->ts; frame->buffer = (unsigned long)region->info.vaddr; frame->y_off = region->info.y_off; frame->cbcr_off = region->info.cbcr_off; frame->fd = region->info.fd; + frame->path = vdata->phy.output_id; CDBG("%s: y %x, cbcr %x, qcmd %x, virt_addr %x\n", __func__, @@ -642,7 +648,8 @@ static int msm_control(struct msm_control_device *ctrl_pmsm, uptr = udata.value; udata.value = data; - + if (udata.type == CAMERA_STOP_SNAPSHOT) + sync->get_pic_abort = 1; qcmd.on_heap = 0; qcmd.type = MSM_CAM_Q_CTRL; qcmd.command = &udata; @@ -871,9 +878,11 @@ static int msm_get_stats(struct msm_sync *sync, void __user *arg) se.stats_event.len, se.stats_event.msg_id); - if ((data->type == VFE_MSG_STATS_AF) || - (data->type == VFE_MSG_STATS_WE)) { - + if (data->type == VFE_MSG_STATS_AF || + data->type == VFE_MSG_STATS_WE || + (data->type >= VFE_MSG_STATS_AEC && + data->type <= VFE_MSG_STATS_SKIN)) { + /* the check above includes all stats type. */ stats.buffer = msm_pmem_stats_ptov_lookup(sync, data->phy.sbuf_phy, @@ -904,10 +913,12 @@ static int msm_get_stats(struct msm_sync *sync, void __user *arg) } else { if ((sync->pp_mask & PP_PREV) && (data->type == VFE_MSG_OUTPUT1 || - data->type == VFE_MSG_OUTPUT2)) + data->type == VFE_MSG_OUTPUT2 || + data->type == VFE_MSG_OUTPUT_P)) rc = msm_divert_frame(sync, data, &se); else if ((sync->pp_mask & (PP_SNAP|PP_RAW_SNAP)) && - data->type == VFE_MSG_SNAPSHOT) + (data->type == VFE_MSG_SNAPSHOT || + data->type == VFE_MSG_OUTPUT_S)) rc = msm_divert_snapshot(sync, data, &se); } @@ -1045,11 +1056,11 @@ static int msm_config_vfe(struct msm_sync *sync, void __user *arg) axi_data.bufnum1 = msm_pmem_region_lookup(&sync->pmem_stats, MSM_PMEM_AEC_AWB, ®ion[0], - NUM_WB_EXP_STAT_OUTPUT_BUFFERS); + NUM_STAT_OUTPUT_BUFFERS); axi_data.bufnum2 = msm_pmem_region_lookup(&sync->pmem_stats, MSM_PMEM_AF, ®ion[axi_data.bufnum1], - NUM_AF_STAT_OUTPUT_BUFFERS); + NUM_STAT_OUTPUT_BUFFERS); if (!axi_data.bufnum1 || !axi_data.bufnum2) { pr_err("%s: pmem region lookup error\n", __func__); return -EINVAL; @@ -1060,7 +1071,7 @@ static int msm_config_vfe(struct msm_sync *sync, void __user *arg) axi_data.bufnum1 = msm_pmem_region_lookup(&sync->pmem_stats, MSM_PMEM_AF, ®ion[0], - NUM_AF_STAT_OUTPUT_BUFFERS); + NUM_STAT_OUTPUT_BUFFERS); if (!axi_data.bufnum1) { pr_err("%s %d: pmem region lookup error\n", __func__, __LINE__); @@ -1072,7 +1083,31 @@ static int msm_config_vfe(struct msm_sync *sync, void __user *arg) axi_data.bufnum1 = msm_pmem_region_lookup(&sync->pmem_stats, MSM_PMEM_AEC_AWB, ®ion[0], - NUM_WB_EXP_STAT_OUTPUT_BUFFERS); + NUM_STAT_OUTPUT_BUFFERS); + if (!axi_data.bufnum1) { + pr_err("%s %d: pmem region lookup error\n", + __func__, __LINE__); + return -EINVAL; + } + axi_data.region = ®ion[0]; + return sync->vfefn.vfe_config(&cfgcmd, &axi_data); + case CMD_STATS_AEC_ENABLE: + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->pmem_stats, + MSM_PMEM_AEC, ®ion[0], + NUM_STAT_OUTPUT_BUFFERS); + if (!axi_data.bufnum1) { + pr_err("%s %d: pmem region lookup error\n", + __func__, __LINE__); + return -EINVAL; + } + axi_data.region = ®ion[0]; + return sync->vfefn.vfe_config(&cfgcmd, &axi_data); + case CMD_STATS_AWB_ENABLE: + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->pmem_stats, + MSM_PMEM_AWB, ®ion[0], + NUM_STAT_OUTPUT_BUFFERS); if (!axi_data.bufnum1) { pr_err("%s %d: pmem region lookup error\n", __func__, __LINE__); @@ -1080,6 +1115,47 @@ static int msm_config_vfe(struct msm_sync *sync, void __user *arg) } axi_data.region = ®ion[0]; return sync->vfefn.vfe_config(&cfgcmd, &axi_data); + + + case CMD_STATS_IHIST_ENABLE: + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->pmem_stats, + MSM_PMEM_IHIST, ®ion[0], + NUM_STAT_OUTPUT_BUFFERS); + if (!axi_data.bufnum1) { + pr_err("%s %d: pmem region lookup error\n", + __func__, __LINE__); + return -EINVAL; + } + axi_data.region = ®ion[0]; + return sync->vfefn.vfe_config(&cfgcmd, &axi_data); + + case CMD_STATS_RS_ENABLE: + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->pmem_stats, + MSM_PMEM_RS, ®ion[0], + NUM_STAT_OUTPUT_BUFFERS); + if (!axi_data.bufnum1) { + pr_err("%s %d: pmem region lookup error\n", + __func__, __LINE__); + return -EINVAL; + } + axi_data.region = ®ion[0]; + return sync->vfefn.vfe_config(&cfgcmd, &axi_data); + + case CMD_STATS_CS_ENABLE: + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->pmem_stats, + MSM_PMEM_CS, ®ion[0], + NUM_STAT_OUTPUT_BUFFERS); + if (!axi_data.bufnum1) { + pr_err("%s %d: pmem region lookup error\n", + __func__, __LINE__); + return -EINVAL; + } + axi_data.region = ®ion[0]; + return sync->vfefn.vfe_config(&cfgcmd, &axi_data); + case CMD_GENERAL: case CMD_STATS_DISABLE: return sync->vfefn.vfe_config(&cfgcmd, NULL); @@ -1128,6 +1204,43 @@ static int msm_frame_axi_cfg(struct msm_sync *sync, } break; + case CMD_AXI_CFG_PREVIEW: + pmem_type = MSM_PMEM_PREVIEW; + axi_data.bufnum2 = + msm_pmem_region_lookup(&sync->pmem_frames, pmem_type, + ®ion[0], 8); + if (!axi_data.bufnum2) { + pr_err("%s %d: pmem region lookup error (empty %d)\n", + __func__, __LINE__, + hlist_empty(&sync->pmem_frames)); + return -EINVAL; + } + break; + + case CMD_AXI_CFG_VIDEO: + pmem_type = MSM_PMEM_PREVIEW; + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->pmem_frames, pmem_type, + ®ion[0], 8); + if (!axi_data.bufnum1) { + pr_err("%s %d: pmem region lookup error\n", + __func__, __LINE__); + return -EINVAL; + } + + pmem_type = MSM_PMEM_VIDEO; + axi_data.bufnum2 = + msm_pmem_region_lookup(&sync->pmem_frames, pmem_type, + ®ion[axi_data.bufnum1], + (8-(axi_data.bufnum1))); + if (!axi_data.bufnum2) { + pr_err("%s %d: pmem region lookup error\n", + __func__, __LINE__); + return -EINVAL; + } + break; + + case CMD_AXI_CFG_SNAP: case CMD_AXI_CFG_SNAP_O1_AND_O2: pmem_type = MSM_PMEM_THUMBNAIL; axi_data.bufnum1 = @@ -1142,7 +1255,8 @@ static int msm_frame_axi_cfg(struct msm_sync *sync, pmem_type = MSM_PMEM_MAINIMG; axi_data.bufnum2 = msm_pmem_region_lookup(&sync->pmem_frames, pmem_type, - ®ion[axi_data.bufnum1], 8); + ®ion[axi_data.bufnum1], + (8-(axi_data.bufnum1))); if (!axi_data.bufnum2) { pr_err("%s %d: pmem region lookup error\n", __func__, __LINE__); @@ -1264,6 +1378,8 @@ static int __msm_register_pmem(struct msm_sync *sync, switch (pinfo->type) { case MSM_PMEM_OUTPUT1: case MSM_PMEM_OUTPUT2: + case MSM_PMEM_VIDEO: + case MSM_PMEM_PREVIEW: case MSM_PMEM_THUMBNAIL: case MSM_PMEM_MAINIMG: case MSM_PMEM_RAW_MAINIMG: @@ -1272,6 +1388,13 @@ static int __msm_register_pmem(struct msm_sync *sync, case MSM_PMEM_AEC_AWB: case MSM_PMEM_AF: + case MSM_PMEM_AEC: + case MSM_PMEM_AWB: + case MSM_PMEM_RS: + case MSM_PMEM_CS: + case MSM_PMEM_IHIST: + case MSM_PMEM_SKIN: + rc = msm_pmem_table_add(&sync->pmem_stats, pinfo); break; @@ -1326,7 +1449,7 @@ static int msm_stats_axi_cfg(struct msm_sync *sync, if (cfgcmd->cmd_type != CMD_GENERAL) { axi_data.bufnum1 = msm_pmem_region_lookup(&sync->pmem_stats, pmem_type, - ®ion[0], NUM_WB_EXP_STAT_OUTPUT_BUFFERS); + ®ion[0], NUM_STAT_OUTPUT_BUFFERS); if (!axi_data.bufnum1) { pr_err("%s %d: pmem region lookup error\n", __func__, __LINE__); @@ -1364,6 +1487,17 @@ static int msm_put_stats_buffer(struct msm_sync *sync, void __user *arg) cfgcmd.cmd_type = CMD_STATS_BUF_RELEASE; else if (buf.type == STAT_AF) cfgcmd.cmd_type = CMD_STATS_AF_BUF_RELEASE; + else if (buf.type == STAT_AEC) + cfgcmd.cmd_type = CMD_STATS_AEC_BUF_RELEASE; + else if (buf.type == STAT_AWB) + cfgcmd.cmd_type = CMD_STATS_AWB_BUF_RELEASE; + else if (buf.type == STAT_IHIST) + cfgcmd.cmd_type = CMD_STATS_IHIST_BUF_RELEASE; + else if (buf.type == STAT_RS) + cfgcmd.cmd_type = CMD_STATS_RS_BUF_RELEASE; + else if (buf.type == STAT_CS) + cfgcmd.cmd_type = CMD_STATS_CS_BUF_RELEASE; + else { pr_err("%s: invalid buf type %d\n", __func__, @@ -1403,6 +1537,9 @@ static int msm_axi_config(struct msm_sync *sync, void __user *arg) case CMD_AXI_CFG_OUT1: case CMD_AXI_CFG_OUT2: case CMD_AXI_CFG_SNAP_O1_AND_O2: + case CMD_AXI_CFG_VIDEO: + case CMD_AXI_CFG_PREVIEW: + case CMD_AXI_CFG_SNAP: case CMD_RAW_PICT_AXI_CFG: return msm_frame_axi_cfg(sync, &cfgcmd); @@ -1431,8 +1568,14 @@ static int __msm_get_pic(struct msm_sync *sync, struct msm_ctrl_cmd *ctrl) rc = wait_event_interruptible_timeout( sync->pict_q.wait, - !list_empty_careful(&sync->pict_q.list), + !list_empty_careful( + &sync->pict_q.list) || sync->get_pic_abort, msecs_to_jiffies(tm)); + + if (sync->get_pic_abort == 1) { + sync->get_pic_abort = 0; + return -ENODATA; + } if (list_empty_careful(&sync->pict_q.list)) { if (rc == 0) return -ETIMEDOUT; @@ -1879,9 +2022,6 @@ static int __msm_release(struct msm_sync *sync) put_pmem_file(region->file); kfree(region); } - - msm_queue_drain(&sync->event_q, list_config); - msm_queue_drain(&sync->frame_q, list_frame); msm_queue_drain(&sync->pict_q, list_pict); wake_unlock(&sync->wake_lock); @@ -1900,8 +2040,10 @@ static int msm_release_config(struct inode *node, struct file *filep) struct msm_device *pmsm = filep->private_data; CDBG("%s: %s\n", __func__, filep->f_path.dentry->d_name.name); rc = __msm_release(pmsm->sync); - if (!rc) + if (!rc) { + msm_queue_drain(&pmsm->sync->event_q, list_config); atomic_set(&pmsm->opened, 0); + } return rc; } @@ -1925,8 +2067,10 @@ static int msm_release_frame(struct inode *node, struct file *filep) struct msm_device *pmsm = filep->private_data; CDBG("%s: %s\n", __func__, filep->f_path.dentry->d_name.name); rc = __msm_release(pmsm->sync); - if (!rc) + if (!rc) { + msm_queue_drain(&pmsm->sync->frame_q, list_frame); atomic_set(&pmsm->opened, 0); + } return rc; } @@ -2024,6 +2168,8 @@ static void msm_vfe_sync(struct msm_vfe_resp *vdata, qcmd->type = qtype; qcmd->command = vdata; + ktime_get_ts(&(qcmd->ts)); + if (qtype != MSM_CAM_Q_VFE_MSG) goto for_config; @@ -2031,6 +2177,7 @@ static void msm_vfe_sync(struct msm_vfe_resp *vdata, switch (vdata->type) { case VFE_MSG_OUTPUT1: case VFE_MSG_OUTPUT2: + case VFE_MSG_OUTPUT_P: if (sync->pp_mask & PP_PREV) { CDBG("%s: PP_PREV in progress: phy_y %x phy_cbcr %x\n", __func__, @@ -2053,6 +2200,13 @@ static void msm_vfe_sync(struct msm_vfe_resp *vdata, msm_enqueue(&sync->frame_q, &qcmd->list_frame); return; + case VFE_MSG_OUTPUT_V: + CDBG("%s: msm_enqueue video frame_q\n", __func__); + if (qcmd->on_heap) + qcmd->on_heap++; + msm_enqueue(&sync->frame_q, &qcmd->list_frame); + break; + case VFE_MSG_SNAPSHOT: if (sync->pp_mask & (PP_SNAP | PP_RAW_SNAP)) { CDBG("%s: PP_SNAP in progress: pp_mask %x\n", @@ -2070,6 +2224,37 @@ static void msm_vfe_sync(struct msm_vfe_resp *vdata, msm_enqueue(&sync->pict_q, &qcmd->list_pict); break; + case VFE_MSG_STATS_AWB: + CDBG("%s: qtype %d, AWB stats, enqueue event_q.\n", + __func__, vdata->type); + break; + + case VFE_MSG_STATS_AEC: + CDBG("%s: qtype %d, AEC stats, enqueue event_q.\n", + __func__, vdata->type); + break; + + case VFE_MSG_STATS_IHIST: + CDBG("%s: qtype %d, ihist stats, enqueue event_q.\n", + __func__, vdata->type); + break; + + case VFE_MSG_STATS_RS: + CDBG("%s: qtype %d, rs stats, enqueue event_q.\n", + __func__, vdata->type); + break; + + case VFE_MSG_STATS_CS: + CDBG("%s: qtype %d, cs stats, enqueue event_q.\n", + __func__, vdata->type); + break; + + + case VFE_MSG_GENERAL: + CDBG("%s: qtype %d, general msg, enqueue event_q.\n", + __func__, vdata->type); + break; + default: CDBG("%s: qtype %d not handled\n", __func__, vdata->type); /* fall through, send to config. */ @@ -2108,6 +2293,7 @@ static int __msm_open(struct msm_sync *sync, const char *const apps_id) msm_camvfe_fn_init(&sync->vfefn, sync); if (sync->vfefn.vfe_init) { + sync->get_pic_abort = 0; rc = sync->vfefn.vfe_init(&msm_vfe_s, sync->pdev); if (rc < 0) { diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h index 60a54355491ce..11da5ead25db1 100644 --- a/include/media/msm_camera.h +++ b/include/media/msm_camera.h @@ -1,6 +1,21 @@ -/* - * Copyright (C) 2008-2009 QUALCOMM Incorporated. +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * */ + #ifndef __LINUX_MSM_CAMERA_H #define __LINUX_MSM_CAMERA_H @@ -89,6 +104,11 @@ #define MSM_CAM_IOCTL_ENABLE_OUTPUT_IND \ _IOW(MSM_CAM_IOCTL_MAGIC, 25, uint32_t *) +#define MSM_CAM_IOCTL_AF_CTRL \ + _IOR(MSM_CAM_IOCTL_MAGIC, 26, struct msm_ctrl_cmt_t *) +#define MSM_CAM_IOCTL_AF_CTRL_DONE \ + _IOW(MSM_CAM_IOCTL_MAGIC, 27, struct msm_ctrl_cmt_t *) + #define MAX_SENSOR_NUM 3 #define MAX_SENSOR_NAME 32 @@ -179,6 +199,30 @@ struct msm_camera_cfg_cmd { #define CMD_STATS_ENABLE 18 #define UPDATE_STATS_INVALID 19 +#define CMD_STATS_AEC_ENABLE 20 +#define CMD_STATS_AWB_ENABLE 21 +#define CMD_STATS_AEC_AXI_CFG 22 +#define CMD_STATS_AWB_AXI_CFG 23 +#define CMD_STATS_RS_AXI_CFG 24 +#define CMD_STATS_CS_AXI_CFG 25 +#define CMD_STATS_IHIST_AXI_CFG 26 +#define CMD_STATS_SKIN_AXI_CFG 27 +#define CMD_STATS_AEC_BUF_RELEASE 28 +#define CMD_STATS_AWB_BUF_RELEASE 29 +#define CMD_STATS_RS_BUF_RELEASE 30 +#define CMD_STATS_CS_BUF_RELEASE 31 +#define CMD_STATS_IHIST_BUF_RELEASE 32 +#define CMD_STATS_SKIN_BUF_RELEASE 33 + +#define CMD_AXI_CFG_SNAP_GEMINI 34 +#define CMD_AXI_CFG_SNAP 35 +#define CMD_AXI_CFG_PREVIEW 36 +#define CMD_AXI_CFG_VIDEO 37 + +#define CMD_STATS_IHIST_ENABLE 38 +#define CMD_STATS_RS_ENABLE 39 +#define CMD_STATS_CS_ENABLE 40 + /* vfe config command: config command(from config thread)*/ struct msm_vfe_cfg_cmd { int cmd_type; @@ -199,12 +243,20 @@ struct camera_enable_cmd { #define MSM_PMEM_RAW_MAINIMG 5 #define MSM_PMEM_AEC_AWB 6 #define MSM_PMEM_AF 7 -#define MSM_PMEM_MAX 8 +#define MSM_PMEM_AEC 8 +#define MSM_PMEM_AWB 9 +#define MSM_PMEM_RS 10 +#define MSM_PMEM_CS 11 +#define MSM_PMEM_IHIST 12 +#define MSM_PMEM_SKIN 13 +#define MSM_PMEM_VIDEO 14 +#define MSM_PMEM_PREVIEW 15 +#define MSM_PMEM_MAX 16 #define FRAME_PREVIEW_OUTPUT1 0 #define FRAME_PREVIEW_OUTPUT2 1 #define FRAME_SNAPSHOT 2 -#define FRAME_THUMBAIL 3 +#define FRAME_THUMBNAIL 3 #define FRAME_RAW_SNAPSHOT 4 #define FRAME_MAX 5 @@ -233,12 +285,19 @@ struct outputCfg { #define CAMIF_TO_AXI_VIA_OUTPUT_2 3 #define OUTPUT_1_AND_CAMIF_TO_AXI_VIA_OUTPUT_2 4 #define OUTPUT_2_AND_CAMIF_TO_AXI_VIA_OUTPUT_1 5 -#define LAST_AXI_OUTPUT_MODE_ENUM = OUTPUT_2_AND_CAMIF_TO_AXI_VIA_OUTPUT_1 6 +#define OUTPUT_1_AND_3 6 +#define LAST_AXI_OUTPUT_MODE_ENUM = OUTPUT_1_AND_3 7 /* video */ + #define MSM_FRAME_PREV_1 0 #define MSM_FRAME_PREV_2 1 #define MSM_FRAME_ENC 2 +#define OUTPUT_TYPE_P 1 +#define OUTPUT_TYPE_T 2 +#define OUTPUT_TYPE_S 3 +#define OUTPUT_TYPE_V 4 + struct msm_frame { int path; unsigned long buffer; @@ -248,11 +307,18 @@ struct msm_frame { void *cropinfo; int croplen; + struct timespec ts; }; #define STAT_AEAW 0 #define STAT_AF 1 -#define STAT_MAX 2 +#define STAT_AEC 2 +#define STAT_AWB 3 +#define STAT_RS 4 +#define STAT_CS 5 +#define STAT_IHIST 6 +#define STAT_SKIN 7 +#define STAT_MAX 8 struct msm_stats_buf { int type; From 1d1fbd32df8c9de1217d088d790b3ceb2922bc82 Mon Sep 17 00:00:00 2001 From: Lars Finander Date: Mon, 12 Jul 2010 15:23:29 -0700 Subject: [PATCH 0690/2556] media: video: msm: add acceptance of RAW image type to msm_get_pic Main image buffer can be MSM_PMEM_MAINIMG or MSM_PMEM_RAW_MAINIMG. Signed-off-by: Lars Finander --- drivers/media/video/msm/msm_camera.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c index 342a2249a1964..4d2b0d99a929f 100644 --- a/drivers/media/video/msm/msm_camera.c +++ b/drivers/media/video/msm/msm_camera.c @@ -1643,8 +1643,12 @@ static int msm_get_pic(struct msm_sync *sync, void __user *arg) if (msm_pmem_region_lookup(&sync->pmem_frames, MSM_PMEM_MAINIMG, &pic_pmem_region, 1) == 0) { - pr_err("%s pmem region lookup error\n", __func__); - return -EIO; + if(msm_pmem_region_lookup(&sync->pmem_frames, + MSM_PMEM_RAW_MAINIMG, + &pic_pmem_region, 1) == 0) { + pr_err("%s pmem region lookup error\n", __func__); + return -EIO; + } } cline_mask = cache_line_size() - 1; From 6dcd99b0861f3aa907c655f338db3d2a01f68d51 Mon Sep 17 00:00:00 2001 From: Wu-cheng Li Date: Thu, 15 Jul 2010 11:38:53 -0700 Subject: [PATCH 0691/2556] [ARM] msm: camera: Remove timestamp of frames. Timestamp is not used for now. Signed-off-by: Wu-cheng Li --- arch/arm/mach-msm/include/mach/camera.h | 1 - drivers/media/video/msm/msm_camera.c | 3 --- include/media/msm_camera.h | 1 - 3 files changed, 5 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/camera.h b/arch/arm/mach-msm/include/mach/camera.h index 00c7cd5745af4..f4ad525ac7334 100644 --- a/arch/arm/mach-msm/include/mach/camera.h +++ b/arch/arm/mach-msm/include/mach/camera.h @@ -118,7 +118,6 @@ struct msm_queue_cmd { enum msm_queue type; void *command; int on_heap; - struct timespec ts; }; struct msm_device_queue { diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c index 4d2b0d99a929f..9c636a0d6afdd 100644 --- a/drivers/media/video/msm/msm_camera.c +++ b/drivers/media/video/msm/msm_camera.c @@ -456,7 +456,6 @@ static int __msm_get_frame(struct msm_sync *sync, goto err; } - frame->ts = qcmd->ts; frame->buffer = (unsigned long)region->info.vaddr; frame->y_off = region->info.y_off; frame->cbcr_off = region->info.cbcr_off; @@ -2172,8 +2171,6 @@ static void msm_vfe_sync(struct msm_vfe_resp *vdata, qcmd->type = qtype; qcmd->command = vdata; - ktime_get_ts(&(qcmd->ts)); - if (qtype != MSM_CAM_Q_VFE_MSG) goto for_config; diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h index 11da5ead25db1..6505af9a0d9ac 100644 --- a/include/media/msm_camera.h +++ b/include/media/msm_camera.h @@ -307,7 +307,6 @@ struct msm_frame { void *cropinfo; int croplen; - struct timespec ts; }; #define STAT_AEAW 0 From a5f07477114444de6d93410e4fd00e1b6d4cd666 Mon Sep 17 00:00:00 2001 From: Mike Chan Date: Fri, 16 Jul 2010 11:52:28 -0700 Subject: [PATCH 0692/2556] [ARM] msm: cpufreq: Use policy->cpu instead of smp_processor_id() Need this for SMP support. Change-Id: I0b6f4389ddb932fb7d6ebe8854357e09d73de890 Signed-off-by: Mike Chan --- arch/arm/mach-msm/cpufreq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c index fd38a5ad58c11..aaa30bb3df778 100644 --- a/arch/arm/mach-msm/cpufreq.c +++ b/arch/arm/mach-msm/cpufreq.c @@ -52,7 +52,7 @@ static int msm_cpufreq_target(struct cpufreq_policy *policy, int index; struct cpufreq_freqs freqs; struct cpufreq_frequency_table *table = - cpufreq_frequency_get_table(smp_processor_id()); + cpufreq_frequency_get_table(policy->cpu); if (cpufreq_frequency_table_target(policy, table, target_freq, relation, &index)) { @@ -69,7 +69,7 @@ static int msm_cpufreq_target(struct cpufreq_policy *policy, #endif freqs.old = policy->cur; freqs.new = table[index].frequency; - freqs.cpu = smp_processor_id(); + freqs.cpu = policy->cpu; cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); acpuclk_set_rate(table[index].frequency * 1000, 0); cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); @@ -86,7 +86,7 @@ static int msm_cpufreq_verify(struct cpufreq_policy *policy) static int msm_cpufreq_init(struct cpufreq_policy *policy) { struct cpufreq_frequency_table *table = - cpufreq_frequency_get_table(smp_processor_id()); + cpufreq_frequency_get_table(policy->cpu); BUG_ON(cpufreq_frequency_table_cpuinfo(policy, table)); policy->cur = acpuclk_get_rate(); From 76cf72e5519f6192748c1b74400c9bf274154580 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 26 May 2010 16:54:53 -0700 Subject: [PATCH 0693/2556] vidc_720p: add drivers/misc/video_core/720p Verbatim copy from Qualcomm's codeaurora repository. The driver needs a lot of cleanup though, it won't even compile right now. Change-Id: Ifac24c3d11adebce45cbab4af6680d700bde854e Signed-off-by: Nick Pelly --- drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + drivers/misc/video_core/720p/Kconfig | 35 + drivers/misc/video_core/720p/Makefile | 42 + drivers/misc/video_core/720p/ddl/vcd_ddl.c | 636 ++++ drivers/misc/video_core/720p/ddl/vcd_ddl.h | 295 ++ .../misc/video_core/720p/ddl/vcd_ddl_api.h | 69 + .../misc/video_core/720p/ddl/vcd_ddl_core.h | 119 + .../misc/video_core/720p/ddl/vcd_ddl_errors.c | 509 +++ .../video_core/720p/ddl/vcd_ddl_firmware.c | 353 ++ .../video_core/720p/ddl/vcd_ddl_firmware.h | 67 + .../misc/video_core/720p/ddl/vcd_ddl_hal.c | 941 +++++ .../misc/video_core/720p/ddl/vcd_ddl_helper.c | 289 ++ .../720p/ddl/vcd_ddl_internal_property.h | 91 + .../720p/ddl/vcd_ddl_interrupt_handler.c | 1061 ++++++ .../video_core/720p/ddl/vcd_ddl_metadata.c | 607 +++ .../video_core/720p/ddl/vcd_ddl_metadata.h | 95 + .../video_core/720p/ddl/vcd_ddl_properties.c | 1925 ++++++++++ .../misc/video_core/720p/ddl/vcd_ddl_utils.c | 223 ++ .../misc/video_core/720p/ddl/vcd_ddl_utils.h | 60 + .../video_core/720p/ddl/video_core_720p.c | 800 ++++ .../video_core/720p/ddl/video_core_720p.h | 2707 ++++++++++++++ drivers/misc/video_core/720p/dec/vdec.c | 1580 ++++++++ .../misc/video_core/720p/dec/vdec_internal.h | 61 + drivers/misc/video_core/720p/enc/venc.c | 1752 +++++++++ .../misc/video_core/720p/enc/venc_internal.c | 1716 +++++++++ .../misc/video_core/720p/enc/venc_internal.h | 156 + .../video_core/720p/init/video_core_init.c | 937 +++++ .../video_core/720p/init/video_core_init.h | 98 + .../720p/init/video_core_init_internal.h | 63 + .../720p/resource_tracker/vcd_res_tracker.c | 281 ++ .../720p/resource_tracker/vcd_res_tracker.h | 56 + .../resource_tracker/vcd_res_tracker_api.h | 43 + .../720p/scheduler/vid_frame_scheduler.c | 1247 +++++++ .../720p/scheduler/vid_frame_scheduler.h | 138 + .../720p/scheduler/vid_frame_scheduler_api.c | 426 +++ .../720p/scheduler/vid_frame_scheduler_api.h | 150 + .../scheduler/vid_frame_scheduler_utils.c | 154 + .../scheduler/vid_frame_scheduler_utils.h | 78 + drivers/misc/video_core/720p/vcd/vcd.h | 358 ++ drivers/misc/video_core/720p/vcd/vcd_api.c | 905 +++++ drivers/misc/video_core/720p/vcd/vcd_api.h | 151 + .../misc/video_core/720p/vcd/vcd_client_sm.c | 1770 +++++++++ .../misc/video_core/720p/vcd/vcd_client_sm.h | 126 + drivers/misc/video_core/720p/vcd/vcd_core.h | 245 ++ .../misc/video_core/720p/vcd/vcd_device_sm.c | 1218 ++++++ .../misc/video_core/720p/vcd/vcd_device_sm.h | 114 + .../misc/video_core/720p/vcd/vcd_power_sm.c | 387 ++ .../misc/video_core/720p/vcd/vcd_power_sm.h | 59 + .../misc/video_core/720p/vcd/vcd_property.h | 312 ++ drivers/misc/video_core/720p/vcd/vcd_status.h | 69 + drivers/misc/video_core/720p/vcd/vcd_sub.c | 3255 +++++++++++++++++ drivers/misc/video_core/720p/vcd/vcd_util.c | 111 + drivers/misc/video_core/720p/vcd/vcd_util.h | 87 + .../video_core/720p/vcd/video_core_type.h | 52 + include/linux/msm_vidc_dec.h | 526 +++ include/linux/msm_vidc_enc.h | 592 +++ 57 files changed, 30199 insertions(+) create mode 100644 drivers/misc/video_core/720p/Kconfig create mode 100644 drivers/misc/video_core/720p/Makefile create mode 100644 drivers/misc/video_core/720p/ddl/vcd_ddl.c create mode 100644 drivers/misc/video_core/720p/ddl/vcd_ddl.h create mode 100644 drivers/misc/video_core/720p/ddl/vcd_ddl_api.h create mode 100644 drivers/misc/video_core/720p/ddl/vcd_ddl_core.h create mode 100644 drivers/misc/video_core/720p/ddl/vcd_ddl_errors.c create mode 100644 drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.c create mode 100644 drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.h create mode 100644 drivers/misc/video_core/720p/ddl/vcd_ddl_hal.c create mode 100644 drivers/misc/video_core/720p/ddl/vcd_ddl_helper.c create mode 100644 drivers/misc/video_core/720p/ddl/vcd_ddl_internal_property.h create mode 100644 drivers/misc/video_core/720p/ddl/vcd_ddl_interrupt_handler.c create mode 100644 drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.c create mode 100644 drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.h create mode 100644 drivers/misc/video_core/720p/ddl/vcd_ddl_properties.c create mode 100644 drivers/misc/video_core/720p/ddl/vcd_ddl_utils.c create mode 100644 drivers/misc/video_core/720p/ddl/vcd_ddl_utils.h create mode 100644 drivers/misc/video_core/720p/ddl/video_core_720p.c create mode 100644 drivers/misc/video_core/720p/ddl/video_core_720p.h create mode 100644 drivers/misc/video_core/720p/dec/vdec.c create mode 100644 drivers/misc/video_core/720p/dec/vdec_internal.h create mode 100644 drivers/misc/video_core/720p/enc/venc.c create mode 100644 drivers/misc/video_core/720p/enc/venc_internal.c create mode 100644 drivers/misc/video_core/720p/enc/venc_internal.h create mode 100644 drivers/misc/video_core/720p/init/video_core_init.c create mode 100644 drivers/misc/video_core/720p/init/video_core_init.h create mode 100644 drivers/misc/video_core/720p/init/video_core_init_internal.h create mode 100644 drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.c create mode 100644 drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.h create mode 100644 drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker_api.h create mode 100644 drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.c create mode 100644 drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.h create mode 100644 drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.c create mode 100644 drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.h create mode 100644 drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.c create mode 100644 drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.h create mode 100644 drivers/misc/video_core/720p/vcd/vcd.h create mode 100644 drivers/misc/video_core/720p/vcd/vcd_api.c create mode 100644 drivers/misc/video_core/720p/vcd/vcd_api.h create mode 100644 drivers/misc/video_core/720p/vcd/vcd_client_sm.c create mode 100644 drivers/misc/video_core/720p/vcd/vcd_client_sm.h create mode 100644 drivers/misc/video_core/720p/vcd/vcd_core.h create mode 100644 drivers/misc/video_core/720p/vcd/vcd_device_sm.c create mode 100644 drivers/misc/video_core/720p/vcd/vcd_device_sm.h create mode 100644 drivers/misc/video_core/720p/vcd/vcd_power_sm.c create mode 100644 drivers/misc/video_core/720p/vcd/vcd_power_sm.h create mode 100644 drivers/misc/video_core/720p/vcd/vcd_property.h create mode 100644 drivers/misc/video_core/720p/vcd/vcd_status.h create mode 100644 drivers/misc/video_core/720p/vcd/vcd_sub.c create mode 100644 drivers/misc/video_core/720p/vcd/vcd_util.c create mode 100644 drivers/misc/video_core/720p/vcd/vcd_util.h create mode 100644 drivers/misc/video_core/720p/vcd/video_core_type.h create mode 100644 include/linux/msm_vidc_dec.h create mode 100644 include/linux/msm_vidc_enc.h diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index d1593da4037a8..1d9eb0639b307 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -509,5 +509,6 @@ source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" source "drivers/misc/iwmc3200top/Kconfig" source "drivers/misc/ti-st/Kconfig" +source "drivers/misc/video_core/720p/Kconfig" endif # MISC_DEVICES diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index d8e6f0c430c38..c6f6832a1278f 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -49,3 +49,4 @@ obj-$(CONFIG_WL127X_RFKILL) += wl127x-rfkill.o obj-$(CONFIG_APANIC) += apanic.o obj-$(CONFIG_SENSORS_AK8975) += akm8975.o obj-$(CONFIG_SENSORS_AKM8973) += akm8973.o +obj-$(CONFIG_MSM_720P_CORE) += video_core/720p/ diff --git a/drivers/misc/video_core/720p/Kconfig b/drivers/misc/video_core/720p/Kconfig new file mode 100644 index 0000000000000..53808f9da83fb --- /dev/null +++ b/drivers/misc/video_core/720p/Kconfig @@ -0,0 +1,35 @@ +# +# VIDEO CORE +# +menuconfig MSM_720P_CORE + bool "720P Core Video Driver" + depends on ARCH_MSM7X30 + default n + ---help--- + Say Y here to see options for video device drivers. + If you say N, all options in this submenu will be skipped and disabled. + +if MSM_720P_CORE + +config MSM_VIDEO_CORE_REG + tristate "MSM Video core registration" + depends on MSM_720P_CORE + default n + help + This option enables support for Video core. + +config MSM_VIDEO_CORE_VENC + tristate "Video encoder" + depends on MSM_VIDEO_CORE_REG + default n + help + This option enables support for Video encoder. + +config MSM_VIDEO_CORE_VDEC + tristate "Video decoder" + depends on MSM_VIDEO_CORE_REG + default n + help + This option enables support for Video decoder. + +endif # MSM_720P_CORE diff --git a/drivers/misc/video_core/720p/Makefile b/drivers/misc/video_core/720p/Makefile new file mode 100644 index 0000000000000..ce42bd8a6bf8d --- /dev/null +++ b/drivers/misc/video_core/720p/Makefile @@ -0,0 +1,42 @@ + +EXTRA_CFLAGS += -Idrivers/misc/video_core/720p +EXTRA_CFLAGS += -Idrivers/misc/video_core/720p/ddl +EXTRA_CFLAGS += -Idrivers/misc/video_core/720p/dec +EXTRA_CFLAGS += -Idrivers/misc/video_core/720p/enc +EXTRA_CFLAGS += -Idrivers/misc/video_core/720p/resource_tracker +EXTRA_CFLAGS += -Idrivers/misc/video_core/720p/scheduler +EXTRA_CFLAGS += -Idrivers/misc/video_core/720p/vcd +EXTRA_CFLAGS += -Idrivers/misc/video_core/720p/init + +obj-$(CONFIG_MSM_VIDEO_CORE_REG) += video_corereg.o +video_corereg-objs := ddl/vcd_ddl_firmware.o \ + ddl/vcd_ddl_metadata.o \ + ddl/video_core_720p.o \ + ddl/vcd_ddl_utils.o \ + ddl/vcd_ddl.o \ + ddl/vcd_ddl_helper.o \ + ddl/vcd_ddl_interrupt_handler.o \ + ddl/vcd_ddl_hal.o \ + ddl/vcd_ddl_properties.o \ + init/video_core_init.o \ + resource_tracker/vcd_res_tracker.o \ + scheduler/vid_frame_scheduler_utils.o \ + scheduler/vid_frame_scheduler.o \ + scheduler/vid_frame_scheduler_api.o \ + vcd/vcd_api.o \ + vcd/vcd_power_sm.o \ + vcd/vcd_client_sm.o \ + vcd/vcd_device_sm.o \ + vcd/vcd_sub.o \ + vcd/vcd_util.o \ + ddl/vcd_ddl_errors.o + + +obj-$(CONFIG_MSM_VIDEO_CORE_VDEC) += video_decoder.o + +video_decoder-objs := dec/vdec.o + +obj-$(CONFIG_MSM_VIDEO_CORE_VENC) += video_encoder.o + +video_encoder-objs := enc/venc.o \ + enc/venc_internal.o diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl.c b/drivers/misc/video_core/720p/ddl/vcd_ddl.c new file mode 100644 index 0000000000000..bf5ec31347953 --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl.c @@ -0,0 +1,636 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" +#include "vcd_ddl_utils.h" +#include "vcd_ddl_metadata.h" + +u32 ddl_device_init(struct ddl_init_config_type *p_ddl_init_config, + void *p_client_data) +{ + struct ddl_context_type *p_ddl_context; + u32 status = VCD_S_SUCCESS; + + if ((!p_ddl_init_config) || + (!p_ddl_init_config->ddl_callback) || + (!p_ddl_init_config->p_core_virtual_base_addr) + ) { + VIDC_LOGERR_STRING("ddl_dev_init:Bad_argument"); + return VCD_ERR_ILLEGAL_PARM; + } + + p_ddl_context = ddl_get_context(); + + if (DDL_IS_INITIALIZED(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_dev_init:Multiple_init"); + return VCD_ERR_ILLEGAL_OP; + } + if (DDL_IS_BUSY(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_dev_init:Ddl_busy"); + return VCD_ERR_BUSY; + } + + DDL_MEMSET(p_ddl_context, 0, sizeof(struct ddl_context_type)); + + DDL_BUSY(p_ddl_context); + + p_ddl_context->ddl_callback = p_ddl_init_config->ddl_callback; + p_ddl_context->pf_interrupt_clr = p_ddl_init_config->pf_interrupt_clr; + p_ddl_context->p_core_virtual_base_addr = + p_ddl_init_config->p_core_virtual_base_addr; + p_ddl_context->p_client_data = p_client_data; + + p_ddl_context->intr_status = DDL_INVALID_INTR_STATUS; + + vidc_720p_set_device_virtual_base(p_ddl_context-> + p_core_virtual_base_addr); + + p_ddl_context->p_current_ddl = NULL; + ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + + ddl_client_transact(DDL_INIT_CLIENTS, NULL); + + ddl_pmem_alloc(&p_ddl_context->context_buf_addr, + DDL_CONTEXT_MEMORY, DDL_LINEAR_BUFFER_ALIGN_BYTES); + if (!p_ddl_context->context_buf_addr.p_virtual_base_addr) { + VIDC_LOGERR_STRING("ddl_dev_init:Context_alloc_fail"); + status = VCD_ERR_ALLOC_FAIL; + } + if (!status) { + ddl_pmem_alloc(&p_ddl_context->db_line_buffer, + DDL_DB_LINE_BUF_SIZE, + DDL_TILE_BUFFER_ALIGN_BYTES); + if (!p_ddl_context->db_line_buffer.p_virtual_base_addr) { + VIDC_LOGERR_STRING("ddl_dev_init:Line_buf_alloc_fail"); + status = VCD_ERR_ALLOC_FAIL; + } + } + + if (!status) { + ddl_pmem_alloc(&p_ddl_context->data_partition_tempbuf, + DDL_MPEG4_DATA_PARTITION_BUF_SIZE, + DDL_TILE_BUFFER_ALIGN_BYTES); + if (p_ddl_context->data_partition_tempbuf.p_virtual_base_addr \ + == NULL) { + VIDC_LOGERR_STRING + ("ddl_dev_init:Data_partition_buf_alloc_fail"); + status = VCD_ERR_ALLOC_FAIL; + } + } + + if (!status) { + + ddl_pmem_alloc(&p_ddl_context->metadata_shared_input, + DDL_METADATA_TOTAL_INPUTBUFSIZE, + DDL_LINEAR_BUFFER_ALIGN_BYTES); + if (!p_ddl_context->metadata_shared_input.p_virtual_base_addr) { + VIDC_LOGERR_STRING + ("ddl_dev_init:metadata_shared_input_alloc_fail"); + status = VCD_ERR_ALLOC_FAIL; + } + } + + if (!status) { + ddl_pmem_alloc(&p_ddl_context->dbg_core_dump, \ + DDL_DBG_CORE_DUMP_SIZE, \ + DDL_LINEAR_BUFFER_ALIGN_BYTES); + if (!p_ddl_context->dbg_core_dump.p_virtual_base_addr) { + VIDC_LOGERR_STRING + ("ddl_dev_init:dbg_core_dump_alloc_failed"); + status = VCD_ERR_ALLOC_FAIL; + } + p_ddl_context->enable_dbg_core_dump = 0; + } + + if (!status && !vcd_fw_init()) { + VIDC_LOGERR_STRING("ddl_dev_init:fw_init_failed"); + status = VCD_ERR_ALLOC_FAIL; + } + if (status) { + ddl_release_context_buffers(p_ddl_context); + DDL_IDLE(p_ddl_context); + return status; + } + + ddl_move_command_state(p_ddl_context, DDL_CMD_DMA_INIT); + + ddl_core_init(p_ddl_context); + + return status; +} + +u32 ddl_device_release(void *p_client_data) +{ + struct ddl_context_type *p_ddl_context; + + p_ddl_context = ddl_get_context(); + + if (DDL_IS_BUSY(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_dev_rel:Ddl_busy"); + return VCD_ERR_BUSY; + } + + if (!DDL_IS_INITIALIZED(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_dev_rel:Not_inited"); + return VCD_ERR_ILLEGAL_OP; + } + + if (!ddl_client_transact(DDL_ACTIVE_CLIENT, NULL)) { + VIDC_LOGERR_STRING("ddl_dev_rel:Client_present_err"); + return VCD_ERR_CLIENT_PRESENT; + } + DDL_BUSY(p_ddl_context); + + p_ddl_context->n_device_state = DDL_DEVICE_NOTINIT; + p_ddl_context->p_client_data = p_client_data; + ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + vidc_720p_stop_fw(); + + VIDC_LOG_STRING("FW_ENDDONE"); + ddl_release_context_buffers(p_ddl_context); + + DDL_IDLE(p_ddl_context); + + return VCD_S_SUCCESS; +} + +u32 ddl_open(u32 **p_ddl_handle, u32 b_decoding) +{ + struct ddl_context_type *p_ddl_context; + struct ddl_client_context_type *p_ddl; + u32 status; + + if (!p_ddl_handle) { + VIDC_LOGERR_STRING("ddl_open:Bad_handle"); + return VCD_ERR_BAD_HANDLE; + } + + p_ddl_context = ddl_get_context(); + + if (!DDL_IS_INITIALIZED(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_open:Not_inited"); + return VCD_ERR_ILLEGAL_OP; + } + + status = ddl_client_transact(DDL_GET_CLIENT, &p_ddl); + + if (status) { + VIDC_LOGERR_STRING("ddl_open:Client_trasac_failed"); + return status; + } + + ddl_move_client_state(p_ddl, DDL_CLIENT_OPEN); + + p_ddl->codec_data.hdr.b_decoding = b_decoding; + p_ddl->b_decoding = b_decoding; + + ddl_set_default_meta_data_hdr(p_ddl); + + ddl_set_initial_default_values(p_ddl); + + *p_ddl_handle = (u32 *) p_ddl; + return VCD_S_SUCCESS; +} + +u32 ddl_close(u32 **p_ddl_handle) +{ + struct ddl_context_type *p_ddl_context; + struct ddl_client_context_type **pp_ddl = + (struct ddl_client_context_type **)p_ddl_handle; + + if (!pp_ddl || !*pp_ddl) { + VIDC_LOGERR_STRING("ddl_close:Bad_handle"); + return VCD_ERR_BAD_HANDLE; + } + + p_ddl_context = ddl_get_context(); + + if (!DDL_IS_INITIALIZED(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_close:Not_inited"); + return VCD_ERR_ILLEGAL_OP; + } + + if (!DDLCLIENT_STATE_IS(*pp_ddl, DDL_CLIENT_OPEN)) { + VIDC_LOGERR_STRING("ddl_close:Not_in_open_state"); + return VCD_ERR_ILLEGAL_OP; + } + + ddl_move_client_state(*pp_ddl, DDL_CLIENT_INVALID); + if ((*pp_ddl)->b_decoding) { + vcd_fw_transact(FALSE, TRUE, + (*pp_ddl)->codec_data.decoder.codec_type.e_codec); + } else { + vcd_fw_transact(FALSE, FALSE, + (*pp_ddl)->codec_data.encoder.codec_type.e_codec); + } + ddl_client_transact(DDL_FREE_CLIENT, pp_ddl); + + return VCD_S_SUCCESS; +} + +u32 ddl_encode_start(u32 *ddl_handle, void *p_client_data) +{ + struct ddl_client_context_type *p_ddl = + (struct ddl_client_context_type *)ddl_handle; + struct ddl_context_type *p_ddl_context; + struct ddl_encoder_data_type *p_encoder; + u32 n_dpb_size; + + p_ddl_context = ddl_get_context(); + + if (!DDL_IS_INITIALIZED(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_enc_start:Not_inited"); + return VCD_ERR_ILLEGAL_OP; + } + if (DDL_IS_BUSY(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_enc_start:Ddl_busy"); + return VCD_ERR_BUSY; + } + if (!p_ddl || p_ddl->b_decoding) { + VIDC_LOGERR_STRING("ddl_enc_start:Bad_handle"); + return VCD_ERR_BAD_HANDLE; + } + + if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_OPEN)) { + VIDC_LOGERR_STRING("ddl_enc_start:Not_opened"); + return VCD_ERR_ILLEGAL_OP; + } + + if (!ddl_encoder_ready_to_start(p_ddl)) { + VIDC_LOGERR_STRING("ddl_enc_start:Err_param_settings"); + return VCD_ERR_ILLEGAL_OP; + } + + p_encoder = &p_ddl->codec_data.encoder; + + n_dpb_size = ddl_get_yuv_buffer_size(&p_encoder->frame_size, + &p_encoder->re_con_buf_format, FALSE); + + n_dpb_size *= DDL_ENC_NUM_DPB_BUFFERS; + ddl_pmem_alloc(&p_encoder->enc_dpb_addr, + n_dpb_size, DDL_TILE_BUFFER_ALIGN_BYTES); + if (!p_encoder->enc_dpb_addr.p_virtual_base_addr) { + VIDC_LOGERR_STRING("ddl_enc_start:Dpb_alloc_failed"); + return VCD_ERR_ALLOC_FAIL; + } + + if ((p_encoder->codec_type.e_codec == VCD_CODEC_MPEG4 && + !p_encoder->short_header.b_short_header) || + p_encoder->codec_type.e_codec == VCD_CODEC_H264) { + ddl_pmem_alloc(&p_encoder->seq_header, + DDL_ENC_SEQHEADER_SIZE, + DDL_LINEAR_BUFFER_ALIGN_BYTES); + if (!p_encoder->seq_header.p_virtual_base_addr) { + ddl_pmem_free(p_encoder->enc_dpb_addr); + VIDC_LOGERR_STRING + ("ddl_enc_start:Seq_hdr_alloc_failed"); + return VCD_ERR_ALLOC_FAIL; + } + } else { + p_encoder->seq_header.n_buffer_size = 0; + p_encoder->seq_header.p_virtual_base_addr = 0; + } + + DDL_BUSY(p_ddl_context); + + p_ddl_context->p_current_ddl = p_ddl; + p_ddl_context->p_client_data = p_client_data; + ddl_channel_set(p_ddl); + return VCD_S_SUCCESS; +} + +u32 ddl_decode_start(u32 *ddl_handle, + struct vcd_sequence_hdr_type *p_header, void *p_client_data) +{ + struct ddl_client_context_type *p_ddl = + (struct ddl_client_context_type *)ddl_handle; + struct ddl_context_type *p_ddl_context; + struct ddl_decoder_data_type *p_decoder; + + p_ddl_context = ddl_get_context(); + + if (!DDL_IS_INITIALIZED(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_dec_start:Not_inited"); + return VCD_ERR_ILLEGAL_OP; + } + if (DDL_IS_BUSY(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_dec_start:Ddl_busy"); + return VCD_ERR_BUSY; + } + if (!p_ddl || !p_ddl->b_decoding) { + VIDC_LOGERR_STRING("ddl_dec_start:Bad_handle"); + return VCD_ERR_BAD_HANDLE; + } + if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_OPEN)) { + VIDC_LOGERR_STRING("ddl_dec_start:Not_in_opened_state"); + return VCD_ERR_ILLEGAL_OP; + } + + if ((p_header) && + ((!p_header->n_sequence_header_len) || + (!p_header->p_sequence_header) + ) + ) { + VIDC_LOGERR_STRING("ddl_dec_start:Bad_param_seq_header"); + return VCD_ERR_ILLEGAL_PARM; + } + + if (!ddl_decoder_ready_to_start(p_ddl, p_header)) { + VIDC_LOGERR_STRING("ddl_dec_start:Err_param_settings"); + return VCD_ERR_ILLEGAL_OP; + } + + DDL_BUSY(p_ddl_context); + + p_decoder = &p_ddl->codec_data.decoder; + if (p_header) { + p_decoder->b_header_in_start = TRUE; + p_decoder->decode_config = *p_header; + } else { + p_decoder->b_header_in_start = FALSE; + p_decoder->decode_config.n_sequence_header_len = 0; + } + + if (p_decoder->codec_type.e_codec == VCD_CODEC_H264) { + ddl_pmem_alloc(&p_decoder->h264Vsp_temp_buffer, + DDL_DECODE_H264_VSPTEMP_BUFSIZE, + DDL_LINEAR_BUFFER_ALIGN_BYTES); + if (!p_decoder->h264Vsp_temp_buffer.p_virtual_base_addr) { + DDL_IDLE(p_ddl_context); + VIDC_LOGERR_STRING + ("ddl_dec_start:H264Sps_alloc_failed"); + return VCD_ERR_ALLOC_FAIL; + } + } + + p_ddl_context->p_current_ddl = p_ddl; + p_ddl_context->p_client_data = p_client_data; + + ddl_channel_set(p_ddl); + return VCD_S_SUCCESS; +} + +u32 ddl_decode_frame(u32 *ddl_handle, + struct ddl_frame_data_type_tag *p_input_bits, void *p_client_data) +{ + u32 vcd_status = VCD_S_SUCCESS; + struct ddl_client_context_type *p_ddl = + (struct ddl_client_context_type *)ddl_handle; + struct ddl_context_type *p_ddl_context = ddl_get_context(); + +#ifdef CORE_TIMING_INFO + ddl_get_core_start_time(0); +#endif + + if (!DDL_IS_INITIALIZED(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_dec_frame:Not_inited"); + return VCD_ERR_ILLEGAL_OP; + } + if (DDL_IS_BUSY(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_dec_frame:Ddl_busy"); + return VCD_ERR_BUSY; + } + if (!p_ddl || !p_ddl->b_decoding) { + VIDC_LOGERR_STRING("ddl_dec_frame:Bad_handle"); + return VCD_ERR_BAD_HANDLE; + } + if (!p_input_bits || + ((!p_input_bits->vcd_frm.p_physical || + !p_input_bits->vcd_frm.n_data_len) && + (!(VCD_FRAME_FLAG_EOS & p_input_bits->vcd_frm.n_flags)) + ) + ) { + VIDC_LOGERR_STRING("ddl_dec_frame:Bad_input_param"); + return VCD_ERR_ILLEGAL_PARM; + } + + DDL_BUSY(p_ddl_context); + + p_ddl_context->p_current_ddl = p_ddl; + p_ddl_context->p_client_data = p_client_data; + + p_ddl->input_frame = *p_input_bits; + + if (DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME)) { + ddl_decode_frame_run(p_ddl); + } else { + if (!p_ddl->codec_data.decoder.dp_buf.n_no_of_dec_pic_buf) { + VIDC_LOGERR_STRING("ddl_dec_frame:Dpbs_requied"); + vcd_status = VCD_ERR_ILLEGAL_OP; + } else if (DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_DPB)) { + vcd_status = ddl_decode_set_buffers(p_ddl); + } else + if (DDLCLIENT_STATE_IS + (p_ddl, DDL_CLIENT_WAIT_FOR_INITCODEC)) { + p_ddl->codec_data.decoder.decode_config. + p_sequence_header = + p_ddl->input_frame.vcd_frm.p_physical; + p_ddl->codec_data.decoder.decode_config. + n_sequence_header_len = + p_ddl->input_frame.vcd_frm.n_data_len; + ddl_decode_init_codec(p_ddl); + } else { + VIDC_LOGERR_STRING("Dec_frame:Wrong_state"); + vcd_status = VCD_ERR_ILLEGAL_OP; + } + if (vcd_status) + DDL_IDLE(p_ddl_context); + } + return vcd_status; +} + +u32 ddl_encode_frame(u32 *ddl_handle, + struct ddl_frame_data_type_tag *p_input_frame, + struct ddl_frame_data_type_tag *p_output_bit, void *p_client_data) +{ + struct ddl_client_context_type *p_ddl = + (struct ddl_client_context_type *)ddl_handle; + struct ddl_context_type *p_ddl_context = ddl_get_context(); + +#ifdef CORE_TIMING_INFO + ddl_get_core_start_time(1); +#endif + + if (!DDL_IS_INITIALIZED(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_enc_frame:Not_inited"); + return VCD_ERR_ILLEGAL_OP; + } + if (DDL_IS_BUSY(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_enc_frame:Ddl_busy"); + return VCD_ERR_BUSY; + } + if (!p_ddl || p_ddl->b_decoding) { + VIDC_LOGERR_STRING("ddl_enc_frame:Bad_handle"); + return VCD_ERR_BAD_HANDLE; + } + if (!p_input_frame || + !p_input_frame->vcd_frm.p_physical || + p_ddl->codec_data.encoder.input_buf_req.n_size != + p_input_frame->vcd_frm.n_data_len) { + VIDC_LOGERR_STRING("ddl_enc_frame:Bad_input_params"); + return VCD_ERR_ILLEGAL_PARM; + } + if ((((u32) p_input_frame->vcd_frm.p_physical + + p_input_frame->vcd_frm.n_offset) & + (DDL_STREAMBUF_ALIGN_GUARD_BYTES) + ) + ) { + VIDC_LOGERR_STRING + ("ddl_enc_frame:Un_aligned_yuv_start_address"); + return VCD_ERR_ILLEGAL_PARM; + } + if (!p_output_bit || + !p_output_bit->vcd_frm.p_physical || + !p_output_bit->vcd_frm.n_alloc_len) { + VIDC_LOGERR_STRING("ddl_enc_frame:Bad_output_params"); + return VCD_ERR_ILLEGAL_PARM; + } + if ((p_ddl->codec_data.encoder.output_buf_req.n_size + + p_output_bit->vcd_frm.n_offset) > + p_output_bit->vcd_frm.n_alloc_len) { + VIDC_LOGERR_STRING + ("ddl_enc_frame:n_offset_large, Exceeds_min_buf_size"); + } + if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME)) { + VIDC_LOGERR_STRING("ddl_enc_frame:Wrong_state"); + return VCD_ERR_ILLEGAL_OP; + } + + DDL_BUSY(p_ddl_context); + + p_ddl_context->p_current_ddl = p_ddl; + p_ddl_context->p_client_data = p_client_data; + + p_ddl->input_frame = *p_input_frame; + p_ddl->output_frame = *p_output_bit; + + ddl_encode_frame_run(p_ddl); + return VCD_S_SUCCESS; +} + +u32 ddl_decode_end(u32 *ddl_handle, void *p_client_data) +{ + struct ddl_client_context_type *p_ddl = + (struct ddl_client_context_type *)ddl_handle; + struct ddl_context_type *p_ddl_context; + + p_ddl_context = ddl_get_context(); + +#ifdef CORE_TIMING_INFO + ddl_reset_time_variables(0); +#endif + + if (!DDL_IS_INITIALIZED(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_dec_end:Not_inited"); + return VCD_ERR_ILLEGAL_OP; + } + if (DDL_IS_BUSY(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_dec_end:Ddl_busy"); + return VCD_ERR_BUSY; + } + if (!p_ddl || !p_ddl->b_decoding) { + VIDC_LOGERR_STRING("ddl_dec_end:Bad_handle"); + return VCD_ERR_BAD_HANDLE; + } + if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME) && + !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_INITCODEC) && + !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_DPB) && + !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_FATAL_ERROR) + ) { + VIDC_LOGERR_STRING("ddl_dec_end:Wrong_state"); + return VCD_ERR_ILLEGAL_OP; + } + DDL_BUSY(p_ddl_context); + + p_ddl_context->p_current_ddl = p_ddl; + p_ddl_context->p_client_data = p_client_data; + + ddl_channel_end(p_ddl); + return VCD_S_SUCCESS; +} + +u32 ddl_encode_end(u32 *ddl_handle, void *p_client_data) +{ + struct ddl_client_context_type *p_ddl = + (struct ddl_client_context_type *)ddl_handle; + struct ddl_context_type *p_ddl_context; + + p_ddl_context = ddl_get_context(); + +#ifdef CORE_TIMING_INFO + ddl_reset_time_variables(1); +#endif + + if (!DDL_IS_INITIALIZED(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_enc_end:Not_inited"); + return VCD_ERR_ILLEGAL_OP; + } + if (DDL_IS_BUSY(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_enc_end:Ddl_busy"); + return VCD_ERR_BUSY; + } + if (!p_ddl || p_ddl->b_decoding) { + VIDC_LOGERR_STRING("ddl_enc_end:Bad_handle"); + return VCD_ERR_BAD_HANDLE; + } + if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME) && + !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_INITCODEC) && + !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_FATAL_ERROR)) { + VIDC_LOGERR_STRING("ddl_enc_end:Wrong_state"); + return VCD_ERR_ILLEGAL_OP; + } + DDL_BUSY(p_ddl_context); + + p_ddl_context->p_current_ddl = p_ddl; + p_ddl_context->p_client_data = p_client_data; + + ddl_channel_end(p_ddl); + return VCD_S_SUCCESS; +} + +u32 ddl_reset_hw(u32 n_mode) +{ + struct ddl_context_type *p_ddl_context; + struct ddl_client_context_type *p_ddl; + int i_client_num; + + VIDC_LOG_STRING("ddl_reset_hw:called"); + p_ddl_context = ddl_get_context(); + ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + DDL_BUSY(p_ddl_context); + + if (p_ddl_context->p_core_virtual_base_addr) + vidc_720p_do_sw_reset(); + + p_ddl_context->n_device_state = DDL_DEVICE_NOTINIT; + for (i_client_num = 0; i_client_num < VCD_MAX_NO_CLIENT; + ++i_client_num) { + p_ddl = p_ddl_context->a_ddl_clients[i_client_num]; + p_ddl_context->a_ddl_clients[i_client_num] = NULL; + if (p_ddl) { + ddl_release_client_internal_buffers(p_ddl); + ddl_client_transact(DDL_FREE_CLIENT, &p_ddl); + } + } + + ddl_release_context_buffers(p_ddl_context); + DDL_MEMSET(p_ddl_context, 0, sizeof(struct ddl_context_type)); + + VIDC_LOG_BUFFER_INIT; + return TRUE; +} diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl.h b/drivers/misc/video_core/720p/ddl/vcd_ddl.h new file mode 100644 index 0000000000000..8b808b2983f8f --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl.h @@ -0,0 +1,295 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VCD_DDL_H_ +#define _VCD_DDL_H_ +#include "vcd_ddl_api.h" +#include "vcd_ddl_utils.h" +#include "vcd_ddl_firmware.h" +#include "video_core_720p.h" + +#undef DDL_INLINE +#define DDL_INLINE + +#define DDL_BUSY_STATE 1 +#define DDL_IDLE_STATE 0 +#define DDL_ERROR_STATE 2 +#define DDL_IS_BUSY(p_ddl_context) \ + (((p_ddl_context)->n_ddl_busy != DDL_IDLE_STATE)) +#define DDL_BUSY(p_ddl_context) \ + ((p_ddl_context)->n_ddl_busy = DDL_BUSY_STATE) +#define DDL_IDLE(p_ddl_context) \ + ((p_ddl_context)->n_ddl_busy = DDL_IDLE_STATE) +#define DDL_ERROR(p_ddl_context) \ + ((p_ddl_context)->n_ddl_busy = DDL_ERROR_STATE) + +#define DDL_DEVICE_NOTINIT 0 +#define DDL_DEVICE_INITED 1 +#define DDL_DEVICE_HWFATAL 2 +#define DDL_IS_INITIALIZED(p_ddl_context) \ +(p_ddl_context->n_device_state == DDL_DEVICE_INITED) + +#define DDLCOMMAND_STATE_IS(p_ddl_context, command_state) \ +(command_state == (p_ddl_context)->e_cmd_state) + +#define DDLCLIENT_STATE_IS(p_ddl, client_state) \ +(client_state == (p_ddl)->e_client_state) + +#define DDL_DPB_OP_INIT 1 +#define DDL_DPB_OP_MARK_FREE 2 +#define DDL_DPB_OP_MARK_BUSY 3 +#define DDL_DPB_OP_SET_MASK 4 +#define DDL_DPB_OP_RETRIEVE 5 + +#define DDL_INIT_CLIENTS 0 +#define DDL_GET_CLIENT 1 +#define DDL_FREE_CLIENT 2 +#define DDL_ACTIVE_CLIENT 3 + +#define DDL_INVALID_CHANNEL_ID ((u32)~0) +#define DDL_INVALID_CODEC_TYPE ((u32)~0) +#define DDL_INVALID_INTR_STATUS ((u32)~0) + +#define DDL_ENC_REQ_IFRAME 0x1 +#define DDL_ENC_CHANGE_IPERIOD 0x2 +#define DDL_ENC_CHANGE_BITRATE 0x4 +#define DDL_ENC_CHANGE_FRAMERATE 0x8 + +#define DDL_DEC_REQ_OUTPUT_FLUSH 0x1 + +struct ddl_buf_addr_type { + u32 *p_physical_base_addr; + u32 *p_virtual_base_addr; + u32 *p_align_physical_addr; + u32 *p_align_virtual_addr; + u32 n_buffer_size; +}; + +enum ddl_cmd_state_type { + DDL_CMD_INVALID = 0x0, + DDL_CMD_DMA_INIT = 0x1, + DDL_CMD_CPU_RESET = 0x2, + DDL_CMD_CHANNEL_SET = 0x3, + DDL_CMD_INIT_CODEC = 0x4, + DDL_CMD_HEADER_PARSE = 0x5, + DDL_CMD_DECODE_SET_DPB = 0x6, + DDL_CMD_DECODE_FRAME = 0x7, + DDL_CMD_ENCODE_FRAME = 0x8, + DDL_CMD_EOS = 0x9, + DDL_CMD_CHANNEL_END = 0xA, + DDL_CMD_32BIT = 0x7FFFFFFF +}; + +enum ddl_client_state_type { + DDL_CLIENT_INVALID = 0x0, + DDL_CLIENT_OPEN = 0x1, + DDL_CLIENT_WAIT_FOR_CHDONE = 0x2, + DDL_CLIENT_WAIT_FOR_INITCODEC = 0x3, + DDL_CLIENT_WAIT_FOR_INITCODECDONE = 0x4, + DDL_CLIENT_WAIT_FOR_DPB = 0x5, + DDL_CLIENT_WAIT_FOR_DPBDONE = 0x6, + DDL_CLIENT_WAIT_FOR_FRAME = 0x7, + DDL_CLIENT_WAIT_FOR_FRAME_DONE = 0x8, + DDL_CLIENT_WAIT_FOR_EOS_DONE = 0x9, + DDL_CLIENT_WAIT_FOR_CHEND = 0xA, + DDL_CLIENT_FATAL_ERROR = 0xB, + DDL_CLIENT_32BIT = 0x7FFFFFFF +}; + +struct ddl_mask_type { + u32 n_client_mask; + u32 n_hw_mask; +}; + +struct ddl_context_type; + +struct ddl_client_context_type; + +struct ddl_codec_data_hdr_type { + u32 b_decoding; +}; + +struct ddl_encoder_data_type { + struct ddl_codec_data_hdr_type hdr; + struct vcd_property_codec_type codec_type; + struct vcd_property_frame_size_type frame_size; + struct vcd_property_frame_rate_type frame_rate; + struct vcd_property_target_bitrate_type target_bit_rate; + struct vcd_property_profile_type profile; + struct vcd_property_level_type level; + struct vcd_property_rate_control_type rc_type; + struct vcd_property_multi_slice_type multi_slice; + u32 n_meta_data_enable_flag; + u32 n_suffix; + struct ddl_buf_addr_type meta_data_input; + u32 n_meta_data_offset; + struct vcd_property_short_header_type short_header; + struct vcd_property_vop_timing_type vop_timing; + u32 n_hdr_ext_control; + struct vcd_property_db_config_type db_control; + struct vcd_property_entropy_control_type entropy_control; + struct vcd_property_i_period_type i_period; + struct vcd_property_session_qp_type session_qp; + struct vcd_property_qp_range_type qp_range; + struct vcd_property_rc_level_type rc_level; + u32 n_r_cframe_skip; + u32 n_vb_vbuffer_size; + struct vcd_property_frame_level_rc_params_type frame_level_rc; + struct vcd_property_adaptive_rc_params_type adaptive_rc; + struct vcd_property_intra_refresh_mb_number_type intra_refresh; + struct vcd_property_buffer_format_type buf_format; + struct vcd_property_buffer_format_type re_con_buf_format; + u32 n_dynamic_prop_change; + u32 b_dynmic_prop_change_req; + u32 n_ext_enc_control_val; + struct vidc_720p_enc_frame_info_type enc_frame_info; + struct ddl_buf_addr_type enc_dpb_addr; + struct ddl_buf_addr_type seq_header; + struct vcd_buffer_requirement_type input_buf_req; + struct vcd_buffer_requirement_type output_buf_req; + struct vcd_buffer_requirement_type client_input_buf_req; + struct vcd_buffer_requirement_type client_output_buf_req; +}; + +struct ddl_decoder_data_type { + struct ddl_codec_data_hdr_type hdr; + struct vcd_property_codec_type codec_type; + struct vcd_property_buffer_format_type buf_format; + struct vcd_property_frame_size_type frame_size; + struct vcd_property_frame_size_type client_frame_size; + struct vcd_property_profile_type profile; + struct vcd_property_level_type level; + u32 n_progressive_only; + u32 n_meta_data_enable_flag; + u32 n_suffix; + struct ddl_buf_addr_type meta_data_input; + struct ddl_buf_addr_type ref_buffer; + u32 n_meta_data_offset; + struct vcd_property_post_filter_type post_filter; + struct vcd_sequence_hdr_type decode_config; + u32 b_header_in_start; + u32 n_min_dpb_num; + u32 n_y_cb_cr_size; + struct ddl_property_dec_pic_buffers_type dp_buf; + struct ddl_mask_type dpb_mask; + u32 n_dynamic_prop_change; + u32 b_dynmic_prop_change_req; + struct vidc_720p_dec_disp_info_type dec_disp_info; + struct ddl_buf_addr_type dpb_comv_buffer; + struct ddl_buf_addr_type h264Vsp_temp_buffer; + struct vcd_buffer_requirement_type actual_input_buf_req; + struct vcd_buffer_requirement_type min_input_buf_req; + struct vcd_buffer_requirement_type client_input_buf_req; + struct vcd_buffer_requirement_type actual_output_buf_req; + struct vcd_buffer_requirement_type min_output_buf_req; + struct vcd_buffer_requirement_type client_output_buf_req; +}; + +union ddl_codec_data_type { + struct ddl_codec_data_hdr_type hdr; + struct ddl_decoder_data_type decoder; + struct ddl_encoder_data_type encoder; +}; + +struct ddl_context_type { + u8 *p_core_virtual_base_addr; + void (*ddl_callback) (u32 event, u32 status, void *payload, u32 size, + u32 *p_ddl_handle, void *const p_client_data); + void *p_client_data; + void (*pf_interrupt_clr) (void); + enum ddl_cmd_state_type e_cmd_state; + struct ddl_client_context_type *p_current_ddl; + struct ddl_buf_addr_type context_buf_addr; + struct ddl_buf_addr_type db_line_buffer; + struct ddl_buf_addr_type data_partition_tempbuf; + struct ddl_buf_addr_type metadata_shared_input; + struct ddl_buf_addr_type dbg_core_dump; + u32 enable_dbg_core_dump; + struct ddl_client_context_type *a_ddl_clients[VCD_MAX_NO_CLIENT]; + u32 n_device_state; + u32 n_ddl_busy; + u32 intr_status; + u32 n_cmd_err_status; + u32 n_disp_pic_err_status; + u32 n_op_failed; +}; + +struct ddl_client_context_type { + struct ddl_context_type *p_ddl_context; + enum ddl_client_state_type e_client_state; + u32 b_decoding; + u32 n_channel_id; + struct ddl_frame_data_type_tag input_frame; + struct ddl_frame_data_type_tag output_frame; + union ddl_codec_data_type codec_data; +}; + +DDL_INLINE struct ddl_context_type *ddl_get_context(void); +DDL_INLINE void ddl_move_command_state(struct ddl_context_type *p_ddl_context, + enum ddl_cmd_state_type e_command_state); +DDL_INLINE void ddl_move_client_state(struct ddl_client_context_type *p_ddl, + enum ddl_client_state_type client_state); +void ddl_core_init(struct ddl_context_type *); +void ddl_core_start_cpu(struct ddl_context_type *); +void ddl_channel_set(struct ddl_client_context_type *); +void ddl_channel_end(struct ddl_client_context_type *); +void ddl_encode_init_codec(struct ddl_client_context_type *); +void ddl_decode_init_codec(struct ddl_client_context_type *); +void ddl_encode_frame_run(struct ddl_client_context_type *); +void ddl_decode_frame_run(struct ddl_client_context_type *); +void ddl_decode_eos_run(struct ddl_client_context_type *); +void ddl_release_context_buffers(struct ddl_context_type *); +void ddl_release_client_internal_buffers(struct ddl_client_context_type *p_ddl); +u32 ddl_decode_set_buffers(struct ddl_client_context_type *); +u32 ddl_decoder_dpb_transact(struct ddl_decoder_data_type *p_decoder, + struct ddl_frame_data_type_tag *p_in_out_frame, + u32 n_operation); +u32 ddl_client_transact(u32, struct ddl_client_context_type **); +void ddl_set_default_decoder_buffer_req + (struct ddl_decoder_data_type *p_decoder, u32 b_estimate); +void ddl_set_default_encoder_buffer_req + (struct ddl_encoder_data_type *p_encoder); +void ddl_set_default_dec_property(struct ddl_client_context_type *); +u32 ddl_encoder_ready_to_start(struct ddl_client_context_type *); +u32 ddl_decoder_ready_to_start(struct ddl_client_context_type *, + struct vcd_sequence_hdr_type *); +u32 ddl_get_yuv_buffer_size + (struct vcd_property_frame_size_type *p_frame_size, + struct vcd_property_buffer_format_type *p_buf_format, u32 inter_lace); +void ddl_calculate_stride(struct vcd_property_frame_size_type *p_frame_size, + u32 inter_lace); +void ddl_encode_dynamic_property(struct ddl_client_context_type *p_ddl, + u32 b_enable); +void ddl_decode_dynamic_property(struct ddl_client_context_type *p_ddl, + u32 b_enable); +void ddl_set_initial_default_values(struct ddl_client_context_type *p_ddl); +u32 ddl_handle_core_errors(struct ddl_context_type *p_ddl_context); +void ddl_client_fatal_cb(struct ddl_context_type *p_ddl_context); +void ddl_hw_fatal_cb(struct ddl_context_type *p_ddl_context); +u32 ddl_hal_engine_reset(struct ddl_context_type *p_ddl_context); +#endif diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_api.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_api.h new file mode 100644 index 0000000000000..3a492d00ab774 --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_api.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VCD_DDL_API_H_ +#define _VCD_DDL_API_H_ +#include "vcd_ddl_internal_property.h" + +struct ddl_init_config_type { + u8 *p_core_virtual_base_addr; + void (*pf_interrupt_clr) (void); + void (*ddl_callback) (u32 event, u32 status, void *payload, u32 size, + u32 *p_ddl_handle, void *const p_client_data); +}; + +struct ddl_frame_data_type_tag { + struct vcd_frame_data_type vcd_frm; + u32 n_intrlcd_ip_frm_tag; + u32 b_frm_trans_end; + u32 n_frm_delta; +}; + +u32 ddl_device_init(struct ddl_init_config_type *p_ddl_init_config, + void *p_client_data); +u32 ddl_device_release(void *p_client_data); +u32 ddl_open(u32 **p_ddl_handle, u32 b_decoding); +u32 ddl_close(u32 **p_ddl_handle); +u32 ddl_encode_start(u32 *ddl_handle, void *p_client_data); +u32 ddl_encode_frame(u32 *ddl_handle, + struct ddl_frame_data_type_tag *p_input_frame, + struct ddl_frame_data_type_tag *p_output_bit, void *p_client_data); +u32 ddl_encode_end(u32 *ddl_handle, void *p_client_data); +u32 ddl_decode_start(u32 *ddl_handle, struct vcd_sequence_hdr_type *p_header, + void *p_client_data); +u32 ddl_decode_frame(u32 *ddl_handle, + struct ddl_frame_data_type_tag *p_input_bits, void *p_client_data); +u32 ddl_decode_end(u32 *ddl_handle, void *p_client_data); +u32 ddl_set_property(u32 *ddl_handle, + struct vcd_property_hdr_type *p_property_hdr, void *p_property_value); +u32 ddl_get_property(u32 *ddl_handle, + struct vcd_property_hdr_type *p_property_hdr, void *p_property_value); +void ddl_read_and_clear_interrupt(void); +u32 ddl_process_core_response(void); +u32 ddl_reset_hw(u32 n_mode); +#endif diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_core.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_core.h new file mode 100644 index 0000000000000..cbdb3c9390831 --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_core.h @@ -0,0 +1,119 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VCD_DDL_CORE_H_ +#define _VCD_DDL_CORE_H_ + +#define DDL_LINEAR_BUF_ALIGN_MASK 0xFFFFFFF8U +#define DDL_LINEAR_BUF_ALIGN_GUARD_BYTES 0x7 +#define DDL_LINEAR_BUFFER_ALIGN_BYTES 8 + +#define DDL_TILE_BUF_ALIGN_MASK 0xFFFFE000U +#define DDL_TILE_BUF_ALIGN_GUARD_BYTES 0x1FFF +#define DDL_TILE_BUFFER_ALIGN_BYTES 8192 + +#define DDL_MAX_FRAME_WIDTH 1280 +#define DDL_MAX_FRAME_HEIGHT 720 + +#define DDL_MAX_DP_FRAME_WIDTH 352 +#define DDL_MAX_DP_FRAME_HEIGHT 288 + +#define DDL_SW_RESET_SLEEP 10 + +#define VCD_MAX_NO_CLIENT 4 +#define VCD_FRAME_COMMAND_DEPTH 1 +#define VCD_GENERAL_COMMAND_DEPTH 1 +#define VCD_COMMAND_EXCLUSIVE TRUE + +#define DDL_HW_TIMEOUT_IN_MS 1000 + +#define DDL_STREAMBUF_ALIGN_GUARD_BYTES 0x7 + +#define DDL_CONTEXT_MEMORY (1024 * 15 * (VCD_MAX_NO_CLIENT + 1)) +#define DDL_DB_LINE_BUF_SIZE \ +(((((DDL_MAX_FRAME_WIDTH * 4) - 1) / 256) + 1) * 8 * 1024) +#define DDL_MPEG4_DATA_PARTITION_BUF_SIZE (64 * 1024) +#define DDL_DECODE_H264_VSPTEMP_BUFSIZE 0x51c00 +#define DDL_ENC_NUM_DPB_BUFFERS 2 + +#define DDL_DBG_CORE_DUMP_SIZE (10 * 1024) + +#define DDL_BUFEND_PAD 256 +#define DDL_ENC_SEQHEADER_SIZE (256+DDL_BUFEND_PAD) +#define DDL_MAX_BUFFER_COUNT 32 + +#define DDL_MPEG_REFBUF_COUNT 2 + +#define DDL_MPEG_COMV_BUF_NO 2 +#define DDL_H263_COMV_BUF_NO 0 +#define DDL_COMV_BUFLINE_NO 128 +#define DDL_VC1_COMV_BUFLINE_NO 32 +#define DDL_MINIMUM_BYTE_PER_SLICE 1920 + +#define DDL_MAX_H264_QP 51 +#define DDL_MAX_MPEG4_QP 31 + +#define DDL_PADDING_HACK(addr) \ + (addr) = (u32)((((u32)(addr) + DDL_STREAMBUF_ALIGN_GUARD_BYTES) & \ + ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES)) + DDL_BUFEND_PAD) + +#define DDL_FRAMESIZE_DIV_FACTOR 0xF +#define DDL_ALLOW_DEC_FRAMESIZE(n_width, n_height) \ +(\ + (\ + (n_width <= DDL_MAX_FRAME_WIDTH) && \ + (n_height <= DDL_MAX_FRAME_HEIGHT) \ + ) && \ + (\ + (n_width >= 32 && n_height >= 16) || \ + (n_width >= 16 && n_height >= 32) \ + )\ +) + +#define DDL_ALLOW_ENC_FRAMESIZE(n_width, n_height) \ +(\ + (\ + (n_width <= DDL_MAX_FRAME_WIDTH) && \ + (n_height <= DDL_MAX_FRAME_HEIGHT) \ + ) && \ + (\ + (n_width >= 32 && n_height >= 32) \ + ) && \ + (\ + !(n_width & DDL_FRAMESIZE_DIV_FACTOR) && \ + !(n_height & DDL_FRAMESIZE_DIV_FACTOR) \ + ) \ +) + +#define DDL_TILE_ALIGN_WIDTH 128 +#define DDL_TILE_ALIGN_HEIGHT 32 +#define DDL_TILE_MULTIPLY_FACTOR 8192 +#define DDL_TILE_ALIGN(val, grid) \ + (((val) + (grid) - 1) / (grid) * (grid)) + +#endif diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_errors.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_errors.c new file mode 100644 index 0000000000000..e95ae10114875 --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_errors.c @@ -0,0 +1,509 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" +#include "vcd_ddl_utils.h" +#include "vcd_ddl.h" + +#if DEBUG +#define DBG(x...) printk(KERN_DEBUG x) +#else +#define DBG(x...) +#endif + +#define ERR(x...) printk(KERN_ERR x) + +#define INVALID_CHANNEL_NUMBER 1 +#define INVALID_COMMAND_ID 2 +#define CHANNEL_ALREADY_IN_USE 3 +#define CHANNEL_NOT_SET_BEFORE_CHANNEL_CLOSE 4 +#define CHANNEL_SET_ERROR_INIT_CODEC 5 +#define INIT_CODEC_ALREADY_CALLED 6 +#define CHANNEL_SET_ERROR_INIT_BUFFERS 7 +#define INIT_CODEC_ERROR_INIT_BUFFERS 8 +#define INIT_BUFFER_ALREADY_CALLED 9 +#define CHANNEL_SET_ERROR_FRAME_RUN 10 +#define INIT_CODEC_ERROR_FRAME_RUN 11 +#define INIT_BUFFERS_ERROR_FRAME_RUN 12 +#define CODEC_LIMIT_EXCEEDED 13 +#define FIRMWARE_SIZE_ZERO 14 +#define FIRMWARE_ADDRESS_EXT_ZERO 15 +#define CONTEXT_DMA_IN_ERROR 16 +#define CONTEXT_DMA_OUT_ERROR 17 +#define PROGRAM_DMA_ERROR 18 +#define CONTEXT_STORE_EXT_ADD_ZERO 19 +#define MEM_ALLOCATION_FAILED 20 + + +#define UNSUPPORTED_FEATURE_IN_PROFILE 27 +#define RESOLUTION_NOT_SUPPORTED 28 +#define HEADER_NOT_FOUND 52 +#define MB_NUM_INVALID 61 +#define FRAME_RATE_NOT_SUPPORTED 62 +#define INVALID_QP_VALUE 63 +#define INVALID_RC_REACTION_COEFFICIENT 64 +#define INVALID_CPB_SIZE_AT_GIVEN_LEVEL 65 + +#define ALLOC_DPB_SIZE_NOT_SUFFICIENT 71 +#define ALLOC_DB_SIZE_NOT_SUFFICIENT 72 +#define ALLOC_COMV_SIZE_NOT_SUFFICIENT 73 +#define NUM_BUF_OUT_OF_RANGE 74 +#define NULL_CONTEXT_POINTER 75 +#define NULL_COMAMND_CONTROL_COMM_POINTER 76 +#define NULL_METADATA_INPUT_POINTER 77 +#define NULL_DPB_POINTER 78 +#define NULL_DB_POINTER 79 +#define NULL_COMV_POINTER 80 + +#define DIVIDE_BY_ZERO 81 +#define BIT_STREAM_BUF_EXHAUST 82 +#define DMA_NOT_STOPPED 83 +#define DMA_TX_NOT_COMPLETE 84 + +#define MB_HEADER_NOT_DONE 85 +#define MB_COEFF_NOT_DONE 86 +#define CODEC_SLICE_NOT_DONE 87 +#define VME_NOT_READY 88 +#define VC1_BITPLANE_DECODE_ERR 89 + + +#define VSP_NOT_READY 90 +#define BUFFER_FULL_STATE 91 + +#define RESOLUTION_MISMATCH 112 +#define NV_QUANT_ERR 113 +#define SYNC_MARKER_ERR 114 +#define FEATURE_NOT_SUPPORTED 115 +#define MEM_CORRUPTION 116 +#define INVALID_REFERENCE_FRAME 117 +#define PICTURE_CODING_TYPE_ERR 118 +#define MV_RANGE_ERR 119 +#define PICTURE_STRUCTURE_ERR 120 +#define SLICE_ADDR_INVALID 121 +#define NON_PAIRED_FIELD_NOT_SUPPORTED 122 +#define NON_FRAME_DATA_RECEIVED 123 +#define INCOMPLETE_FRAME 124 +#define NO_BUFFER_RELEASED_FROM_HOST 125 +#define PICTURE_MANAGEMENT_ERROR 128 +#define INVALID_MMCO 129 +#define INVALID_PIC_REORDERING 130 +#define INVALID_POC_TYPE 131 +#define ACTIVE_SPS_NOT_PRESENT 132 +#define ACTIVE_PPS_NOT_PRESENT 133 +#define INVALID_SPS_ID 134 +#define INVALID_PPS_ID 135 + + +#define METADATA_NO_SPACE_QP 151 +#define METADATA_NO_SAPCE_CONCEAL_MB 152 +#define METADATA_NO_SPACE_VC1_PARAM 153 +#define METADATA_NO_SPACE_SEI 154 +#define METADATA_NO_SPACE_VUI 155 +#define METADATA_NO_SPACE_EXTRA 156 +#define METADATA_NO_SPACE_DATA_NONE 157 +#define FRAME_RATE_UNKNOWN 158 +#define ASPECT_RATIO_UNKOWN 159 +#define COLOR_PRIMARIES_UNKNOWN 160 +#define TRANSFER_CHAR_UNKWON 161 +#define MATRIX_COEFF_UNKNOWN 162 +#define NON_SEQ_SLICE_ADDR 163 +#define BROKEN_LINK 164 +#define FRAME_CONCEALED 165 +#define PROFILE_UNKOWN 166 +#define LEVEL_UNKOWN 167 +#define BIT_RATE_NOT_SUPPORTED 168 +#define COLOR_DIFF_FORMAT_NOT_SUPPORTED 169 +#define NULL_EXTRA_METADATA_POINTER 170 +#define SYNC_POINT_NOT_RECEIVED_STARTED_DECODING 171 +#define NULL_FW_DEBUG_INFO_POINTER 172 +#define ALLOC_DEBUG_INFO_SIZE_INSUFFICIENT 173 +#define MAX_STAGE_COUNTER_EXCEEDED 174 + +#define METADATA_NO_SPACE_MB_INFO 180 +#define METADATA_NO_SPACE_SLICE_SIZE 181 +#define RESOLUTION_WARNING 182 + +void ddl_hw_fatal_cb(struct ddl_context_type *p_ddl_context) +{ + /* Invalidate the command state */ + ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + p_ddl_context->n_device_state = DDL_DEVICE_HWFATAL; + + /* callback to the client to indicate hw fatal error */ + p_ddl_context->ddl_callback(VCD_EVT_IND_HWERRFATAL, + VCD_ERR_HW_FATAL, NULL, 0, + (void *)p_ddl_context->p_current_ddl, + p_ddl_context->p_client_data); + + DDL_IDLE(p_ddl_context); +} + +static u32 ddl_handle_hw_fatal_errors(struct ddl_context_type + *p_ddl_context) +{ + u32 b_status = FALSE; + + switch (p_ddl_context->n_cmd_err_status) { + + case INVALID_CHANNEL_NUMBER: + case INVALID_COMMAND_ID: + case CHANNEL_ALREADY_IN_USE: + case CHANNEL_NOT_SET_BEFORE_CHANNEL_CLOSE: + case CHANNEL_SET_ERROR_INIT_CODEC: + case INIT_CODEC_ALREADY_CALLED: + case CHANNEL_SET_ERROR_INIT_BUFFERS: + case INIT_CODEC_ERROR_INIT_BUFFERS: + case INIT_BUFFER_ALREADY_CALLED: + case CHANNEL_SET_ERROR_FRAME_RUN: + case INIT_CODEC_ERROR_FRAME_RUN: + case INIT_BUFFERS_ERROR_FRAME_RUN: + case CODEC_LIMIT_EXCEEDED: + case FIRMWARE_SIZE_ZERO: + case FIRMWARE_ADDRESS_EXT_ZERO: + + case CONTEXT_DMA_IN_ERROR: + case CONTEXT_DMA_OUT_ERROR: + case PROGRAM_DMA_ERROR: + case CONTEXT_STORE_EXT_ADD_ZERO: + case MEM_ALLOCATION_FAILED: + + case DIVIDE_BY_ZERO: + case DMA_NOT_STOPPED: + case DMA_TX_NOT_COMPLETE: + + case VSP_NOT_READY: + case BUFFER_FULL_STATE: + ERR("HW FATAL ERROR"); + ddl_hw_fatal_cb(p_ddl_context); + b_status = TRUE; + break; + } + return b_status; +} + +void ddl_client_fatal_cb(struct ddl_context_type *p_ddl_context) +{ + struct ddl_client_context_type *p_ddl = + p_ddl_context->p_current_ddl; + + if (p_ddl_context->e_cmd_state == DDL_CMD_DECODE_FRAME) + ddl_decode_dynamic_property(p_ddl, FALSE); + else if (p_ddl_context->e_cmd_state == DDL_CMD_ENCODE_FRAME) + ddl_encode_dynamic_property(p_ddl, FALSE); + + ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + + ddl_move_client_state(p_ddl, DDL_CLIENT_FATAL_ERROR); + + p_ddl_context->ddl_callback + ( + VCD_EVT_IND_HWERRFATAL, + VCD_ERR_CLIENT_FATAL, + NULL, + 0, + (void *)p_ddl, + p_ddl_context->p_client_data + ); + + DDL_IDLE(p_ddl_context); +} + +static u32 ddl_handle_client_fatal_errors(struct ddl_context_type + *p_ddl_context) +{ + u32 b_status = FALSE; + + switch (p_ddl_context->n_cmd_err_status) { + case UNSUPPORTED_FEATURE_IN_PROFILE: + case RESOLUTION_NOT_SUPPORTED: + case HEADER_NOT_FOUND: + case INVALID_SPS_ID: + case INVALID_PPS_ID: + + case MB_NUM_INVALID: + case FRAME_RATE_NOT_SUPPORTED: + case INVALID_QP_VALUE: + case INVALID_RC_REACTION_COEFFICIENT: + case INVALID_CPB_SIZE_AT_GIVEN_LEVEL: + + case ALLOC_DPB_SIZE_NOT_SUFFICIENT: + case ALLOC_DB_SIZE_NOT_SUFFICIENT: + case ALLOC_COMV_SIZE_NOT_SUFFICIENT: + case NUM_BUF_OUT_OF_RANGE: + case NULL_CONTEXT_POINTER: + case NULL_COMAMND_CONTROL_COMM_POINTER: + case NULL_METADATA_INPUT_POINTER: + case NULL_DPB_POINTER: + case NULL_DB_POINTER: + case NULL_COMV_POINTER: + { + b_status = TRUE; + break; + } + } + + if (!b_status) + ERR("UNKNOWN-OP-FAILED"); + + ddl_client_fatal_cb(p_ddl_context); + + return TRUE; +} + +static void ddl_input_failed_cb(struct ddl_context_type *p_ddl_context, + u32 vcd_event, u32 vcd_status) +{ + struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; + + ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + + if (p_ddl->b_decoding) + ddl_decode_dynamic_property(p_ddl, FALSE); + else + ddl_encode_dynamic_property(p_ddl, FALSE); + + p_ddl_context->ddl_callback(vcd_event, + vcd_status, &p_ddl->input_frame, + sizeof(struct ddl_frame_data_type_tag), + (void *)p_ddl, p_ddl_context->p_client_data); + + ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME); +} + +static u32 ddl_handle_core_recoverable_errors(struct ddl_context_type \ + *p_ddl_context) +{ + struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; + u32 vcd_status = VCD_S_SUCCESS; + u32 vcd_event = VCD_EVT_RESP_INPUT_DONE; + u32 b_eos = FALSE, pending_display = 0, release_mask = 0; + + if (p_ddl_context->e_cmd_state != DDL_CMD_DECODE_FRAME && + p_ddl_context->e_cmd_state != DDL_CMD_ENCODE_FRAME) { + return FALSE; + } + switch (p_ddl_context->n_cmd_err_status) { + case NON_PAIRED_FIELD_NOT_SUPPORTED: + { + vcd_status = VCD_ERR_INTRLCD_FIELD_DROP; + break; + } + case NO_BUFFER_RELEASED_FROM_HOST: + { + /* lets check sanity of this error */ + release_mask = + p_ddl->codec_data.decoder.dpb_mask.n_hw_mask; + while (release_mask > 0) { + if ((release_mask & 0x1)) + pending_display += 1; + release_mask >>= 1; + } + + if (pending_display >= + p_ddl->codec_data.decoder.n_min_dpb_num) { + DBG("FWISSUE-REQBUF!!"); + /* callback to client for client fatal error */ + ddl_client_fatal_cb(p_ddl_context); + return TRUE ; + } + vcd_event = VCD_EVT_RESP_OUTPUT_REQ; + break; + } + case BIT_STREAM_BUF_EXHAUST: + case MB_HEADER_NOT_DONE: + case MB_COEFF_NOT_DONE: + case CODEC_SLICE_NOT_DONE: + case VME_NOT_READY: + case VC1_BITPLANE_DECODE_ERR: + { + u32 b_reset_core; + /* need to reset the internal core hw engine */ + b_reset_core = ddl_hal_engine_reset(p_ddl_context); + if (!b_reset_core) + return TRUE; + /* fall through to process bitstream error handling */ + } + case RESOLUTION_MISMATCH: + case NV_QUANT_ERR: + case SYNC_MARKER_ERR: + case FEATURE_NOT_SUPPORTED: + case MEM_CORRUPTION: + case INVALID_REFERENCE_FRAME: + case PICTURE_CODING_TYPE_ERR: + case MV_RANGE_ERR: + case PICTURE_STRUCTURE_ERR: + case SLICE_ADDR_INVALID: + case NON_FRAME_DATA_RECEIVED: + case INCOMPLETE_FRAME: + case PICTURE_MANAGEMENT_ERROR: + case INVALID_MMCO: + case INVALID_PIC_REORDERING: + case INVALID_POC_TYPE: + case ACTIVE_SPS_NOT_PRESENT: + case ACTIVE_PPS_NOT_PRESENT: + { + vcd_status = VCD_ERR_BITSTREAM_ERR; + break; + } + } + + if (!vcd_status && vcd_event == VCD_EVT_RESP_INPUT_DONE) + return FALSE; + + p_ddl->input_frame.b_frm_trans_end = TRUE; + + b_eos = ((vcd_event == VCD_EVT_RESP_INPUT_DONE) && + ((VCD_FRAME_FLAG_EOS & p_ddl->input_frame. + vcd_frm.n_flags))); + + if ((p_ddl->b_decoding && b_eos) || + (!p_ddl->b_decoding)) + p_ddl->input_frame.b_frm_trans_end = FALSE; + + if (vcd_event == VCD_EVT_RESP_INPUT_DONE && + p_ddl->b_decoding && + !p_ddl->codec_data.decoder.b_header_in_start && + !p_ddl->codec_data.decoder.dec_disp_info.n_img_size_x && + !p_ddl->codec_data.decoder.dec_disp_info.n_img_size_y + ) { + /* this is first frame seq. header only case */ + vcd_status = VCD_S_SUCCESS; + p_ddl->input_frame.vcd_frm.n_flags |= + VCD_FRAME_FLAG_CODECCONFIG; + p_ddl->input_frame.b_frm_trans_end = !b_eos; + /* put just some non - zero value */ + p_ddl->codec_data.decoder.dec_disp_info.n_img_size_x = 0xff; + } + /* inform client about input failed */ + ddl_input_failed_cb(p_ddl_context, vcd_event, vcd_status); + + /* for Encoder case, we need to send output done also */ + if (!p_ddl->b_decoding) { + /* transaction is complete after this callback */ + p_ddl->output_frame.b_frm_trans_end = !b_eos; + /* error case: NO data present */ + p_ddl->output_frame.vcd_frm.n_data_len = 0; + /* call back to client for output frame done */ + p_ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE, + VCD_ERR_FAIL, &(p_ddl->output_frame), + sizeof(struct ddl_frame_data_type_tag), + (void *)p_ddl, p_ddl_context->p_client_data); + + if (b_eos) { + DBG("ENC-EOS_DONE"); + /* send client EOS DONE callback */ + p_ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE, + VCD_S_SUCCESS, NULL, 0, (void *)p_ddl, + p_ddl_context->p_client_data); + } + } + + /* if it is decoder EOS case */ + if (p_ddl->b_decoding && b_eos) + ddl_decode_eos_run(p_ddl); + else + DDL_IDLE(p_ddl_context); + + return TRUE; +} + +static u32 ddl_handle_core_warnings(u32 n_err_status) +{ + u32 b_status = FALSE; + + switch (n_err_status) { + case FRAME_RATE_UNKNOWN: + case ASPECT_RATIO_UNKOWN: + case COLOR_PRIMARIES_UNKNOWN: + case TRANSFER_CHAR_UNKWON: + case MATRIX_COEFF_UNKNOWN: + case NON_SEQ_SLICE_ADDR: + case BROKEN_LINK: + case FRAME_CONCEALED: + case PROFILE_UNKOWN: + case LEVEL_UNKOWN: + case BIT_RATE_NOT_SUPPORTED: + case COLOR_DIFF_FORMAT_NOT_SUPPORTED: + case NULL_EXTRA_METADATA_POINTER: + case SYNC_POINT_NOT_RECEIVED_STARTED_DECODING: + + case NULL_FW_DEBUG_INFO_POINTER: + case ALLOC_DEBUG_INFO_SIZE_INSUFFICIENT: + case MAX_STAGE_COUNTER_EXCEEDED: + + case METADATA_NO_SPACE_MB_INFO: + case METADATA_NO_SPACE_SLICE_SIZE: + case RESOLUTION_WARNING: + + /* decoder warnings */ + case METADATA_NO_SPACE_QP: + case METADATA_NO_SAPCE_CONCEAL_MB: + case METADATA_NO_SPACE_VC1_PARAM: + case METADATA_NO_SPACE_SEI: + case METADATA_NO_SPACE_VUI: + case METADATA_NO_SPACE_EXTRA: + case METADATA_NO_SPACE_DATA_NONE: + { + b_status = TRUE; + DBG("CMD-WARNING-IGNORED!!"); + break; + } + } + return b_status; +} + +u32 ddl_handle_core_errors(struct ddl_context_type *p_ddl_context) +{ + u32 b_status = FALSE; + + if (!p_ddl_context->n_cmd_err_status && + !p_ddl_context->n_disp_pic_err_status) + return FALSE; + + if (p_ddl_context->e_cmd_state == DDL_CMD_INVALID) { + DBG("SPURIOUS_INTERRUPT_ERROR"); + return TRUE; + } + + if (!p_ddl_context->n_op_failed) { + u32 b_disp_status; + b_status = ddl_handle_core_warnings(p_ddl_context-> + n_cmd_err_status); + b_disp_status = ddl_handle_core_warnings( + p_ddl_context->n_disp_pic_err_status); + if (!b_status && !b_disp_status) + DBG("ddl_warning:Unknown"); + + return FALSE; + } + + ERR("\n %s(): OPFAILED!!", __func__); + ERR("\n CMD_ERROR_STATUS = %u, DISP_ERR_STATUS = %u", + p_ddl_context->n_cmd_err_status, + p_ddl_context->n_disp_pic_err_status); + + b_status = ddl_handle_hw_fatal_errors(p_ddl_context); + + if (!b_status) + b_status = ddl_handle_core_recoverable_errors(p_ddl_context); + + if (!b_status) + b_status = ddl_handle_client_fatal_errors(p_ddl_context); + + return b_status; +} diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.c new file mode 100644 index 0000000000000..232a5c181cac7 --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.c @@ -0,0 +1,353 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" + +#include "vcd_ddl_firmware.h" +#include "vcd_ddl_utils.h" + +#define VCDFW_TOTALNUM_IMAGE 7 +#define VCDFW_MAX_NO_IMAGE 2 + +struct vcd_firmware_type { + u32 a_active_fw_img[VCDFW_TOTALNUM_IMAGE]; + struct ddl_buf_addr_type boot_code; + + struct ddl_buf_addr_type enc_mpeg4; + struct ddl_buf_addr_type encH264; + + struct ddl_buf_addr_type dec_mpeg4; + struct ddl_buf_addr_type decH264; + struct ddl_buf_addr_type decH263; + struct ddl_buf_addr_type dec_mpeg2; + struct ddl_buf_addr_type dec_vc1; +}; + +static struct vcd_firmware_type vcd_firmware; + + +static void vcd_fw_change_endian(unsigned char *fw, u32 n_fw_size) +{ + u32 i = 0; + unsigned char temp; + for (i = 0; i < n_fw_size; i = i + 4) { + temp = fw[i]; + fw[i] = fw[i + 3]; + fw[i + 3] = temp; + + temp = fw[i + 1]; + fw[i + 1] = fw[i + 2]; + fw[i + 2] = temp; + } + return; +} + +static u32 vcd_fw_prepare(struct ddl_buf_addr_type *fw_details, + const unsigned char fw_array[], + const unsigned int fw_array_size, u32 change_endian) +{ + u32 *buffer; + + ddl_pmem_alloc(fw_details, fw_array_size, + DDL_LINEAR_BUFFER_ALIGN_BYTES); + if (!fw_details->p_virtual_base_addr) + return FALSE; + + fw_details->n_buffer_size = fw_array_size / 4; + + buffer = fw_details->p_align_virtual_addr; + + memcpy(buffer, fw_array, fw_array_size); + if (change_endian) + vcd_fw_change_endian((unsigned char *)buffer, fw_array_size); + return TRUE; +} + +u32 vcd_fw_init(void) +{ + u32 b_status = FALSE; + + b_status = vcd_fw_prepare(&vcd_firmware.boot_code, + vid_c_command_control_fw, + vid_c_command_control_fw_size, FALSE); + + if (b_status) { + b_status = vcd_fw_prepare(&vcd_firmware.dec_mpeg4, + vid_c_mpg4_dec_fw, + vid_c_mpg4_dec_fw_size, TRUE); + } + + if (b_status) { + b_status = vcd_fw_prepare(&vcd_firmware.decH264, + vid_c_h264_dec_fw, + vid_c_h264_dec_fw_size, TRUE); + } + + if (b_status) { + b_status = vcd_fw_prepare(&vcd_firmware.decH263, + vid_c_h263_dec_fw, + vid_c_h263_dec_fw_size, TRUE); + } + + if (b_status) { + b_status = vcd_fw_prepare(&vcd_firmware.enc_mpeg4, + vid_c_mpg4_enc_fw, + vid_c_mpg4_enc_fw_size, TRUE); + } + + if (b_status) { + b_status = vcd_fw_prepare(&vcd_firmware.encH264, + vid_c_h264_enc_fw, + vid_c_h264_enc_fw_size, TRUE); + } + + return b_status; +} + + +static u32 get_dec_fw_image(struct vcd_fw_details_type *p_fw_details) +{ + u32 b_return = TRUE; + switch (p_fw_details->e_codec) { + case VCD_CODEC_DIVX_4: + case VCD_CODEC_DIVX_5: + case VCD_CODEC_DIVX_6: + case VCD_CODEC_XVID: + case VCD_CODEC_MPEG4: + { + p_fw_details->p_fw_buffer_addr = + vcd_firmware.dec_mpeg4.p_align_physical_addr; + p_fw_details->n_fw_size = + vcd_firmware.dec_mpeg4.n_buffer_size; + break; + } + case VCD_CODEC_H264: + { + p_fw_details->p_fw_buffer_addr = + vcd_firmware.decH264.p_align_physical_addr; + p_fw_details->n_fw_size = + vcd_firmware.decH264.n_buffer_size; + break; + } + case VCD_CODEC_VC1: + case VCD_CODEC_VC1_RCV: + { + p_fw_details->p_fw_buffer_addr = + vcd_firmware.dec_vc1.p_align_physical_addr; + p_fw_details->n_fw_size = + vcd_firmware.dec_vc1.n_buffer_size; + break; + } + case VCD_CODEC_MPEG2: + { + p_fw_details->p_fw_buffer_addr = + vcd_firmware.dec_mpeg2.p_align_physical_addr; + p_fw_details->n_fw_size = + vcd_firmware.dec_mpeg2.n_buffer_size; + break; + } + case VCD_CODEC_H263: + { + p_fw_details->p_fw_buffer_addr = + vcd_firmware.decH263.p_align_physical_addr; + p_fw_details->n_fw_size = + vcd_firmware.decH263.n_buffer_size; + break; + } + default: + { + b_return = FALSE; + break; + } + } + return b_return; +} + +static u32 get_enc_fw_image(struct vcd_fw_details_type *p_fw_details) +{ + u32 b_return = TRUE; + switch (p_fw_details->e_codec) { + case VCD_CODEC_H263: + case VCD_CODEC_MPEG4: + { + p_fw_details->p_fw_buffer_addr = + vcd_firmware.enc_mpeg4.p_align_physical_addr; + p_fw_details->n_fw_size = + vcd_firmware.enc_mpeg4.n_buffer_size; + break; + } + case VCD_CODEC_H264: + { + p_fw_details->p_fw_buffer_addr = + vcd_firmware.encH264.p_align_physical_addr; + p_fw_details->n_fw_size = + vcd_firmware.encH264.n_buffer_size; + break; + } + default: + { + b_return = FALSE; + break; + } + } + return b_return; +} + +u32 vcd_get_fw_property(u32 prop_id, void *prop_details) +{ + u32 b_return = TRUE; + struct vcd_fw_details_type *p_fw_details; + switch (prop_id) { + case VCD_FW_ENDIAN: + { + *(u32 *) prop_details = VCD_FW_BIG_ENDIAN; + break; + } + case VCD_FW_BOOTCODE: + { + p_fw_details = + (struct vcd_fw_details_type *)prop_details; + p_fw_details->p_fw_buffer_addr = + vcd_firmware.boot_code.p_align_physical_addr; + p_fw_details->n_fw_size = + vcd_firmware.boot_code.n_buffer_size; + break; + } + case VCD_FW_DECODE: + { + p_fw_details = + (struct vcd_fw_details_type *)prop_details; + b_return = get_dec_fw_image(p_fw_details); + break; + } + case VCD_FW_ENCODE: + { + p_fw_details = + (struct vcd_fw_details_type *)prop_details; + b_return = get_enc_fw_image(p_fw_details); + break; + } + default: + { + b_return = FALSE; + break; + } + } + return b_return; +} + +u32 vcd_fw_transact(u32 b_add, u32 b_decoding, enum vcd_codec_type e_codec) +{ + u32 b_return = TRUE; + u32 n_index = 0, n_active_fw = 0, n_loop_count; + + if (b_decoding) { + switch (e_codec) { + case VCD_CODEC_DIVX_4: + case VCD_CODEC_DIVX_5: + case VCD_CODEC_DIVX_6: + case VCD_CODEC_XVID: + case VCD_CODEC_MPEG4: + { + n_index = 0; + break; + } + case VCD_CODEC_H264: + { + n_index = 1; + break; + } + case VCD_CODEC_H263: + { + n_index = 2; + break; + } + case VCD_CODEC_MPEG2: + { + n_index = 3; + break; + } + case VCD_CODEC_VC1: + case VCD_CODEC_VC1_RCV: + { + n_index = 4; + break; + } + default: + { + b_return = FALSE; + break; + } + } + } else { + switch (e_codec) { + case VCD_CODEC_H263: + case VCD_CODEC_MPEG4: + { + n_index = 5; + break; + } + case VCD_CODEC_H264: + { + n_index = 6; + break; + } + default: + { + b_return = FALSE; + break; + } + } + } + + if (!b_return) + return b_return; + + if (!b_add && + vcd_firmware.a_active_fw_img[n_index] + ) { + --vcd_firmware.a_active_fw_img[n_index]; + return b_return; + } + + for (n_loop_count = 0; n_loop_count < VCDFW_TOTALNUM_IMAGE; + ++n_loop_count) { + if (vcd_firmware.a_active_fw_img[n_loop_count]) + ++n_active_fw; + } + + if (n_active_fw < VCDFW_MAX_NO_IMAGE || + vcd_firmware.a_active_fw_img[n_index] > 0) { + ++vcd_firmware.a_active_fw_img[n_index]; + } else { + b_return = FALSE; + } + return b_return; +} + +void vcd_fw_release(void) +{ + ddl_pmem_free(vcd_firmware.boot_code); + ddl_pmem_free(vcd_firmware.enc_mpeg4); + ddl_pmem_free(vcd_firmware.encH264); + ddl_pmem_free(vcd_firmware.dec_mpeg4); + ddl_pmem_free(vcd_firmware.decH264); + ddl_pmem_free(vcd_firmware.decH263); + ddl_pmem_free(vcd_firmware.dec_mpeg2); + ddl_pmem_free(vcd_firmware.dec_vc1); +} diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.h new file mode 100644 index 0000000000000..27263ad5c765c --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.h @@ -0,0 +1,67 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VCD_DDL_FIRMWARE_H_ +#define _VCD_DDL_FIRMWARE_H_ +#include "vcd_property.h" + +#define VCD_FW_BIG_ENDIAN 0x0 +#define VCD_FW_LITTLE_ENDIAN 0x1 + +struct vcd_fw_details_type { + enum vcd_codec_type e_codec; + u32 *p_fw_buffer_addr; + u32 n_fw_size; +}; + +#define VCD_FW_PROP_BASE 0x0 + +#define VCD_FW_ENDIAN (VCD_FW_PROP_BASE + 0x1) +#define VCD_FW_BOOTCODE (VCD_FW_PROP_BASE + 0x2) +#define VCD_FW_DECODE (VCD_FW_PROP_BASE + 0x3) +#define VCD_FW_ENCODE (VCD_FW_PROP_BASE + 0x4) + +extern unsigned char *vid_c_command_control_fw; +extern u32 vid_c_command_control_fw_size; +extern unsigned char *vid_c_mpg4_dec_fw; +extern u32 vid_c_mpg4_dec_fw_size; +extern unsigned char *vid_c_h263_dec_fw; +extern u32 vid_c_h263_dec_fw_size; +extern unsigned char *vid_c_h264_dec_fw; +extern u32 vid_c_h264_dec_fw_size; +extern unsigned char *vid_c_mpg4_enc_fw; +extern u32 vid_c_mpg4_enc_fw_size; +extern unsigned char *vid_c_h264_enc_fw; +extern u32 vid_c_h264_enc_fw_size; + +u32 vcd_fw_init(void); +u32 vcd_get_fw_property(u32 prop_id, void *prop_details); +u32 vcd_fw_transact(u32 b_add, u32 b_decoding, enum vcd_codec_type e_codec); +void vcd_fw_release(void); + +#endif diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_hal.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_hal.c new file mode 100644 index 0000000000000..76f470a1d0975 --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_hal.c @@ -0,0 +1,941 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" + +#include "vcd_ddl_utils.h" +#include "vcd_ddl_metadata.h" + +#if DEBUG +#define DBG(x...) printk(KERN_DEBUG x) +#else +#define DBG(x...) +#endif + +void ddl_core_init(struct ddl_context_type *p_ddl_context) +{ + char *psz_version; + struct vcd_fw_details_type fw_details; + u32 fw_endianness; + enum vidc_720p_endian_type e_dma_endian; + u32 b_interrupt_off; + enum vidc_720p_interrupt_level_selection_type e_interrupt_sel; + u32 intr_mask = 0x0; + + vcd_get_fw_property(VCD_FW_BOOTCODE, &fw_details); + vcd_get_fw_property(VCD_FW_ENDIAN, &fw_endianness); + if (fw_endianness == VCD_FW_BIG_ENDIAN) + e_dma_endian = VIDC_720P_BIG_ENDIAN; + else + e_dma_endian = VIDC_720P_LITTLE_ENDIAN; + + b_interrupt_off = FALSE; + e_interrupt_sel = VIDC_720P_INTERRUPT_LEVEL_SEL; + + intr_mask |= VIDC_720P_INTR_BUFFER_FULL; + intr_mask |= VIDC_720P_INTR_FW_DONE; + intr_mask |= VIDC_720P_INTR_DMA_DONE; + intr_mask |= VIDC_720P_INTR_FRAME_DONE; + + vidc_720p_do_sw_reset(); + + vidc_720p_init(&psz_version, + fw_details.n_fw_size, + fw_details.p_fw_buffer_addr, + e_dma_endian, + b_interrupt_off, e_interrupt_sel, intr_mask); + return; +} + +void ddl_core_start_cpu(struct ddl_context_type *p_ddl_context) +{ + u32 fw_endianness; + enum vidc_720p_endian_type e_dma_endian; + u32 dbg_core_dump_buf_size = 0; + + vcd_get_fw_property(VCD_FW_ENDIAN, &fw_endianness); + if (fw_endianness == VCD_FW_BIG_ENDIAN) + e_dma_endian = VIDC_720P_LITTLE_ENDIAN; + else + e_dma_endian = VIDC_720P_BIG_ENDIAN; + + ddl_move_command_state(p_ddl_context, DDL_CMD_CPU_RESET); + + DBG("VSP_BUF_ADDR_SIZE %d", + p_ddl_context->context_buf_addr.n_buffer_size); + if (p_ddl_context->enable_dbg_core_dump) { + dbg_core_dump_buf_size = p_ddl_context->dbg_core_dump. + n_buffer_size; + } + + vidc_720p_start_cpu(e_dma_endian, + p_ddl_context->context_buf_addr.p_align_physical_addr, + p_ddl_context->dbg_core_dump.p_align_physical_addr, + dbg_core_dump_buf_size); + + VIDC_DEBUG_REGISTER_LOG; +} + +void ddl_channel_set(struct ddl_client_context_type *p_ddl) +{ + enum vidc_720p_enc_dec_selection_type e_enc_dec_sel; + enum vidc_720p_codec_type e_codec; + enum vcd_codec_type *p_codec; + u32 fw_property_id; + struct vcd_fw_details_type fw_details; + + if (p_ddl->b_decoding) { + e_enc_dec_sel = VIDC_720P_DECODER; + fw_property_id = VCD_FW_DECODE; + p_codec = &(p_ddl->codec_data.decoder.codec_type.e_codec); + } else { + e_enc_dec_sel = VIDC_720P_ENCODER; + fw_property_id = VCD_FW_ENCODE; + p_codec = &(p_ddl->codec_data.encoder.codec_type.e_codec); + } + switch (*p_codec) { + default: + case VCD_CODEC_MPEG4: + { + e_codec = VIDC_720P_MPEG4; + + if (p_ddl->b_decoding) { + vidc_720p_decode_set_mpeg4_data_partitionbuffer + (p_ddl->p_ddl_context->data_partition_tempbuf. + p_align_physical_addr); + } + + break; + } + case VCD_CODEC_H264: + { + e_codec = VIDC_720P_H264; + break; + } + case VCD_CODEC_DIVX_4: + case VCD_CODEC_DIVX_5: + case VCD_CODEC_DIVX_6: + { + e_codec = VIDC_720P_DIVX; + break; + } + case VCD_CODEC_XVID: + { + e_codec = VIDC_720P_XVID; + break; + } + case VCD_CODEC_H263: + { + e_codec = VIDC_720P_H263; + break; + } + case VCD_CODEC_MPEG2: + { + e_codec = VIDC_720P_MPEG2; + break; + } + case VCD_CODEC_VC1: + case VCD_CODEC_VC1_RCV: + { + e_codec = VIDC_720P_VC1; + break; + } + } + + fw_details.e_codec = *p_codec; + vcd_get_fw_property(fw_property_id, &fw_details); + VIDC_DEBUG_REGISTER_LOG; + + ddl_move_command_state(p_ddl->p_ddl_context, DDL_CMD_CHANNEL_SET); + ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_CHDONE); + + vidc_720p_set_channel(p_ddl->n_channel_id, + e_enc_dec_sel, + e_codec, + fw_details.p_fw_buffer_addr, + fw_details.n_fw_size); +} + +void ddl_decode_init_codec(struct ddl_client_context_type *p_ddl) +{ + u32 n_seq_h = 0, n_seq_e = 0, n_start_byte_num = 0; + struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); + struct vcd_sequence_hdr_type *p_seq_hdr = &p_decoder->decode_config; + enum vidc_720p_memory_access_method_type mem_access_method; + + ddl_metadata_enable(p_ddl); + + vidc_720p_decode_set_error_control(TRUE); + + vidc_720p_decode_set_mpeg4Post_filter(p_decoder->post_filter. + b_post_filter); + if (p_decoder->codec_type.e_codec == VCD_CODEC_VC1_RCV) { + vidc_720p_set_frame_size(p_decoder->client_frame_size.n_width, + p_decoder->client_frame_size.n_height); + } else { + vidc_720p_set_frame_size(0x0, 0x0); + } + + switch (p_decoder->buf_format.e_buffer_format) { + default: + case VCD_BUFFER_FORMAT_NV12: + { + mem_access_method = VIDC_720P_TILE_LINEAR; + break; + } + case VCD_BUFFER_FORMAT_TILE_4x2: + { + mem_access_method = VIDC_720P_TILE_64x32; + break; + } + } + VIDC_LOG_STRING("HEADER-PARSE-START"); + VIDC_DEBUG_REGISTER_LOG; + n_seq_h = (u32) p_seq_hdr->p_sequence_header; + n_start_byte_num = 8 - (n_seq_h & DDL_STREAMBUF_ALIGN_GUARD_BYTES); + n_seq_e = n_seq_h + p_seq_hdr->n_sequence_header_len; + n_seq_h &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES); + DDL_PADDING_HACK(n_seq_e); + + ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE); + ddl_move_command_state(p_ddl->p_ddl_context, DDL_CMD_HEADER_PARSE); + + vidc_720p_decode_bitstream_header(p_ddl->n_channel_id, + p_seq_hdr->n_sequence_header_len, + n_start_byte_num, + n_seq_h, + n_seq_e, + mem_access_method); +} + +void ddl_decode_dynamic_property(struct ddl_client_context_type *p_ddl, + u32 b_enable) +{ + uint8_t *p_temp = NULL; + u32 n_extra_datastart = 0; + struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); + struct vcd_frame_data_type *p_bit_stream = + &(p_ddl->input_frame.vcd_frm); + + if (!b_enable) { + if (p_decoder->b_dynmic_prop_change_req) { + p_decoder->b_dynmic_prop_change_req = FALSE; + vidc_720p_decode_dynamic_req_reset(); + } + return; + } + if ((p_decoder->n_dynamic_prop_change & + DDL_DEC_REQ_OUTPUT_FLUSH)) { + p_decoder->b_dynmic_prop_change_req = TRUE; + p_decoder->n_dynamic_prop_change &= ~(DDL_DEC_REQ_OUTPUT_FLUSH); + p_decoder->dpb_mask.n_hw_mask = 0; + vidc_720p_decode_dynamic_req_set(VIDC_720P_FLUSH_REQ); + } + if (((p_decoder->n_meta_data_enable_flag & VCD_METADATA_PASSTHROUGH)) + && ((VCD_FRAME_FLAG_EXTRADATA & p_bit_stream->n_flags)) + ) { + + p_temp = ((uint8_t *)p_bit_stream->p_physical + + p_bit_stream->n_offset + + p_bit_stream->n_data_len + 3); + + n_extra_datastart = (u32) ((u32)p_temp & ~3); + p_decoder->b_dynmic_prop_change_req = TRUE; + + vidc_720p_decode_setpassthrough_start(n_extra_datastart); + + vidc_720p_decode_dynamic_req_set(VIDC_720P_EXTRADATA); + } +} + +void ddl_encode_dynamic_property(struct ddl_client_context_type *p_ddl, + u32 b_enable) +{ + struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); + u32 n_enc_param_change = 0; + + if (!b_enable) { + if (p_encoder->b_dynmic_prop_change_req) { + p_encoder->b_dynmic_prop_change_req = FALSE; + p_encoder->n_ext_enc_control_val &= + ~(VIDC_720P_ENC_IFRAME_REQ); + vidc_720p_encode_set_control_param + (p_encoder->n_ext_enc_control_val); + vidc_720p_encoder_set_param_change(n_enc_param_change); + } + return; + } + if ((p_encoder->n_dynamic_prop_change & DDL_ENC_REQ_IFRAME)) { + p_encoder->n_dynamic_prop_change &= ~(DDL_ENC_REQ_IFRAME); + p_encoder->n_ext_enc_control_val |= VIDC_720P_ENC_IFRAME_REQ; + vidc_720p_encode_set_control_param + (p_encoder->n_ext_enc_control_val); + } + if ((p_encoder->n_dynamic_prop_change & DDL_ENC_CHANGE_BITRATE)) { + vidc_720p_encode_set_bit_rate( + p_encoder->target_bit_rate.n_target_bitrate); + n_enc_param_change |= VIDC_720P_ENC_BITRATE_CHANGE; + p_encoder->n_dynamic_prop_change &= ~(DDL_ENC_CHANGE_BITRATE); + } + if ((p_encoder->n_dynamic_prop_change & DDL_ENC_CHANGE_IPERIOD)) { + vidc_720p_encode_set_i_period + (p_encoder->i_period.n_p_frames); + n_enc_param_change |= VIDC_720P_ENC_IPERIOD_CHANGE; + p_encoder->n_dynamic_prop_change &= ~(DDL_ENC_CHANGE_IPERIOD); + } + if ((p_encoder->n_dynamic_prop_change & + DDL_ENC_CHANGE_FRAMERATE)) { + vidc_720p_encode_set_fps + ((p_encoder->frame_rate.n_fps_numerator * 1000) / + p_encoder->frame_rate.n_fps_denominator); + n_enc_param_change |= VIDC_720P_ENC_FRAMERATE_CHANGE; + p_encoder->n_dynamic_prop_change &= ~(DDL_ENC_CHANGE_FRAMERATE); + } + if (n_enc_param_change) + vidc_720p_encoder_set_param_change(n_enc_param_change); +} + +static void ddl_encode_set_profile_level(struct ddl_client_context_type *p_ddl) +{ + struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); + u32 profile; + u32 n_level; + + switch (p_encoder->profile.e_profile) { + default: + case VCD_PROFILE_MPEG4_SP: + { + profile = VIDC_720P_PROFILE_MPEG4_SP; + break; + } + case VCD_PROFILE_MPEG4_ASP: + { + profile = VIDC_720P_PROFILE_MPEG4_ASP; + break; + } + case VCD_PROFILE_H264_BASELINE: + { + profile = VIDC_720P_PROFILE_H264_BASELINE; + break; + } + case VCD_PROFILE_H264_MAIN: + { + profile = VIDC_720P_PROFILE_H264_MAIN; + break; + } + case VCD_PROFILE_H264_HIGH: + { + profile = VIDC_720P_PROFILE_H264_HIGH; + break; + } + case VCD_PROFILE_H263_BASELINE: + { + profile = VIDC_720P_PROFILE_H263_BASELINE; + break; + } + } + switch (p_encoder->level.e_level) { + default: + case VCD_LEVEL_MPEG4_0: + { + n_level = VIDC_720P_MPEG4_LEVEL0; + break; + } + case VCD_LEVEL_MPEG4_0b: + { + n_level = VIDC_720P_MPEG4_LEVEL0b; + break; + } + case VCD_LEVEL_MPEG4_1: + { + n_level = VIDC_720P_MPEG4_LEVEL1; + break; + } + case VCD_LEVEL_MPEG4_2: + { + n_level = VIDC_720P_MPEG4_LEVEL2; + break; + } + case VCD_LEVEL_MPEG4_3: + { + n_level = VIDC_720P_MPEG4_LEVEL3; + break; + } + case VCD_LEVEL_MPEG4_3b: + { + n_level = VIDC_720P_MPEG4_LEVEL3b; + break; + } + + case VCD_LEVEL_MPEG4_4: + case VCD_LEVEL_MPEG4_4a: + { + n_level = VIDC_720P_MPEG4_LEVEL4a; + break; + } + case VCD_LEVEL_MPEG4_5: + { + n_level = VIDC_720P_MPEG4_LEVEL5; + break; + } + case VCD_LEVEL_MPEG4_6: + { + n_level = VIDC_720P_MPEG4_LEVEL6; + break; + } + case VCD_LEVEL_H264_1: + { + n_level = VIDC_720P_H264_LEVEL1; + break; + } + case VCD_LEVEL_H264_1b: + { + n_level = VIDC_720P_H264_LEVEL1b; + break; + } + case VCD_LEVEL_H264_1p1: + { + n_level = VIDC_720P_H264_LEVEL1p1; + break; + } + case VCD_LEVEL_H264_1p2: + { + n_level = VIDC_720P_H264_LEVEL1p2; + break; + } + case VCD_LEVEL_H264_1p3: + { + n_level = VIDC_720P_H264_LEVEL1p3; + break; + } + case VCD_LEVEL_H264_2: + { + n_level = VIDC_720P_H264_LEVEL2; + break; + } + case VCD_LEVEL_H264_2p1: + { + n_level = VIDC_720P_H264_LEVEL2p1; + break; + } + case VCD_LEVEL_H264_2p2: + { + n_level = VIDC_720P_H264_LEVEL2p2; + break; + } + case VCD_LEVEL_H264_3: + { + n_level = VIDC_720P_H264_LEVEL3; + break; + } + case VCD_LEVEL_H264_3p1: + { + n_level = VIDC_720P_H264_LEVEL3p1; + break; + } + case VCD_LEVEL_H263_10: + { + n_level = VIDC_720P_H263_LEVEL10; + break; + } + case VCD_LEVEL_H263_20: + { + n_level = VIDC_720P_H263_LEVEL20; + break; + } + case VCD_LEVEL_H263_30: + { + n_level = VIDC_720P_H263_LEVEL30; + break; + } + case VCD_LEVEL_H263_40: + { + n_level = VIDC_720P_H263_LEVEL40; + break; + } + case VCD_LEVEL_H263_45: + { + n_level = VIDC_720P_H263_LEVEL45; + break; + } + case VCD_LEVEL_H263_50: + { + n_level = VIDC_720P_H263_LEVEL50; + break; + } + case VCD_LEVEL_H263_60: + { + n_level = VIDC_720P_H263_LEVEL60; + break; + } + case VCD_LEVEL_H263_70: + { + n_level = VIDC_720P_H263_LEVEL70; + break; + } + } + vidc_720p_encode_set_profile(profile, n_level); +} + +void ddl_encode_init_codec(struct ddl_client_context_type *p_ddl) +{ + struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); + enum vidc_720p_memory_access_method_type mem_access_method; + enum vidc_720p_DBConfig_type e_db_config; + enum vidc_720p_MSlice_selection_type e_m_slice_sel; + + ddl_encode_set_profile_level(p_ddl); + + vidc_720p_set_frame_size + (p_encoder->frame_size.n_width, p_encoder->frame_size.n_height); + vidc_720p_encode_set_qp_params + (p_encoder->qp_range.n_max_qp, p_encoder->qp_range.n_min_qp); + vidc_720p_encode_set_rc_config + (p_encoder->rc_level.b_frame_level_rc, + p_encoder->rc_level.b_mb_level_rc, + p_encoder->session_qp.n_i_frame_qp, + p_encoder->session_qp.n_p_frame_qp); + + if (p_encoder->n_r_cframe_skip) { + if (p_encoder->n_vb_vbuffer_size) { + p_encoder->n_ext_enc_control_val = (0x2 << 0x2) | + (p_encoder->n_vb_vbuffer_size << 0x10); + } else + p_encoder->n_ext_enc_control_val = (0x1 << 2); + } else + p_encoder->n_ext_enc_control_val = 0; + + vidc_720p_encode_set_fps + ((p_encoder->frame_rate.n_fps_numerator * 1000) / + p_encoder->frame_rate.n_fps_denominator); + + vidc_720p_encode_set_vop_time( + p_encoder->vop_timing.n_vop_time_resolution, 0); + + if (p_encoder->rc_level.b_frame_level_rc) { + vidc_720p_encode_set_bit_rate + (p_encoder->target_bit_rate.n_target_bitrate); + + vidc_720p_encode_set_frame_level_rc_params + (p_encoder->frame_level_rc.n_reaction_coeff); + } + if (p_encoder->rc_level.b_mb_level_rc) { + vidc_720p_encode_set_mb_level_rc_params + (p_encoder->adaptive_rc.b_dark_region_as_flag, + p_encoder->adaptive_rc.b_smooth_region_as_flag, + p_encoder->adaptive_rc.b_static_region_as_flag, + p_encoder->adaptive_rc.b_activity_region_flag); + } + if (p_encoder->codec_type.e_codec == VCD_CODEC_MPEG4) { + vidc_720p_encode_set_short_header + (p_encoder->short_header.b_short_header); + + if (p_encoder->n_hdr_ext_control) { + vidc_720p_encode_set_hec_period + (p_encoder->n_hdr_ext_control); + p_encoder->n_ext_enc_control_val |= (0x1 << 0x1); + } + } + /* set extended encoder control settings */ + vidc_720p_encode_set_control_param + (p_encoder->n_ext_enc_control_val); + + if (p_encoder->codec_type.e_codec == VCD_CODEC_H264) { + enum vidc_720p_entropy_sel_type e_entropy_sel; + enum vidc_720p_cabac_model_type e_cabac_model_number; + switch (p_encoder->entropy_control.e_entropy_sel) { + default: + case VCD_ENTROPY_SEL_CAVLC: + { + e_entropy_sel = VIDC_720P_ENTROPY_SEL_CAVLC; + break; + } + case VCD_ENTROPY_SEL_CABAC: + { + e_entropy_sel = VIDC_720P_ENTROPY_SEL_CABAC; + break; + } + } + switch (p_encoder->entropy_control.e_cabac_model) { + default: + case VCD_CABAC_MODEL_NUMBER_0: + { + e_cabac_model_number = + VIDC_720P_CABAC_MODEL_NUMBER_0; + break; + } + case VCD_CABAC_MODEL_NUMBER_1: + { + e_cabac_model_number = + VIDC_720P_CABAC_MODEL_NUMBER_1; + break; + } + case VCD_CABAC_MODEL_NUMBER_2: + { + e_cabac_model_number = + VIDC_720P_CABAC_MODEL_NUMBER_2; + break; + } + } + vidc_720p_encode_set_entropy_control + (e_entropy_sel, e_cabac_model_number); + switch (p_encoder->db_control.e_db_config) { + default: + case VCD_DB_ALL_BLOCKING_BOUNDARY: + { + e_db_config = + VIDC_720P_DB_ALL_BLOCKING_BOUNDARY; + break; + } + case VCD_DB_DISABLE: + { + e_db_config = + VIDC_720P_DB_DISABLE; + break; + } + case VCD_DB_SKIP_SLICE_BOUNDARY: + { + e_db_config = + VIDC_720P_DB_SKIP_SLICE_BOUNDARY; + break; + } + } + vidc_720p_encode_set_db_filter_control + (e_db_config, + p_encoder->db_control.n_slice_alpha_offset, + p_encoder->db_control.n_slice_beta_offset); + } + + vidc_720p_encode_set_intra_refresh_mb_number + (p_encoder->intra_refresh.n_cir_mb_number); + + switch (p_encoder->multi_slice.e_m_slice_sel) { + default: + case VCD_MSLICE_OFF: + e_m_slice_sel = VIDC_720P_MSLICE_OFF; + break; + case VCD_MSLICE_BY_MB_COUNT: + { + e_m_slice_sel = VIDC_720P_MSLICE_BY_MB_COUNT; + break; + } + case VCD_MSLICE_BY_BYTE_COUNT: + { + e_m_slice_sel = VIDC_720P_MSLICE_BY_BYTE_COUNT; + break; + } + case VCD_MSLICE_BY_GOB: + { + e_m_slice_sel = VIDC_720P_MSLICE_BY_GOB; + break; + } + } + vidc_720p_encode_set_multi_slice_info + (e_m_slice_sel, p_encoder->multi_slice.n_m_slice_size); + + vidc_720p_encode_set_dpb_buffer + (p_encoder->enc_dpb_addr.p_align_physical_addr, + p_encoder->enc_dpb_addr.n_buffer_size); + + VIDC_LOG1("ENC_DPB_ADDR_SIZE", p_encoder->enc_dpb_addr.n_buffer_size); + + vidc_720p_encode_set_i_period(p_encoder->i_period.n_p_frames); + + ddl_metadata_enable(p_ddl); + + if (p_encoder->seq_header.p_virtual_base_addr) { + u32 n_ext_buffer_start, n_ext_buffer_end, n_start_byte_num; + n_ext_buffer_start = + (u32) p_encoder->seq_header.p_align_physical_addr; + n_ext_buffer_end = + n_ext_buffer_start + p_encoder->seq_header.n_buffer_size; + n_start_byte_num = + (n_ext_buffer_start & DDL_STREAMBUF_ALIGN_GUARD_BYTES); + n_ext_buffer_start &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES); + n_ext_buffer_end &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES); + VIDC_LOG1("ENC_SEQHDR_ALLOC_SIZE", + p_encoder->seq_header.n_buffer_size); + vidc_720p_encode_set_seq_header_buffer(n_ext_buffer_start, + n_ext_buffer_end, + n_start_byte_num); + } + + if (p_encoder->re_con_buf_format.e_buffer_format == + VCD_BUFFER_FORMAT_NV12) + mem_access_method = VIDC_720P_TILE_LINEAR; + else + mem_access_method = VIDC_720P_TILE_16x16; + + ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE); + ddl_move_command_state(p_ddl->p_ddl_context, DDL_CMD_INIT_CODEC); + + vidc_720p_encode_init_codec(p_ddl->n_channel_id, mem_access_method); +} + +void ddl_channel_end(struct ddl_client_context_type *p_ddl) +{ + VIDC_DEBUG_REGISTER_LOG; + + ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_CHEND); + ddl_move_command_state(p_ddl->p_ddl_context, DDL_CMD_CHANNEL_END); + + vidc_720p_submit_command(p_ddl->n_channel_id, VIDC_720P_CMD_CHEND); +} + +void ddl_encode_frame_run(struct ddl_client_context_type *p_ddl) +{ + u32 n_ext_buffer_start, n_ext_buffer_end; + u32 n_y_addr, n_c_addr; + u32 n_start_byte_number = 0; + struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); + struct vcd_frame_data_type *p_stream = &(p_ddl->output_frame.vcd_frm); + + n_ext_buffer_start = (u32) p_stream->p_physical + p_stream->n_offset; + n_ext_buffer_end = ddl_encode_set_metadata_output_buf(p_ddl); + n_start_byte_number = + (n_ext_buffer_start & DDL_STREAMBUF_ALIGN_GUARD_BYTES); + if (n_start_byte_number) { + u32 n_upper_data, n_lower_data; + u32 *p_align_virtual_addr; + n_ext_buffer_start &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES); + p_align_virtual_addr = (u32 *) (((u32) p_stream->p_virtual + + p_stream->n_offset) - + n_start_byte_number); + n_upper_data = *p_align_virtual_addr; + p_align_virtual_addr++; + n_lower_data = *p_align_virtual_addr; + vidc_720p_encode_unalign_bitstream(n_upper_data, n_lower_data); + } + + n_y_addr = (u32) p_ddl->input_frame.vcd_frm.p_physical + + p_ddl->input_frame.vcd_frm.n_offset; + n_c_addr = (n_y_addr + (p_encoder->frame_size.n_height * + p_encoder->frame_size.n_width)); + ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE); + ddl_move_command_state(p_ddl->p_ddl_context, DDL_CMD_ENCODE_FRAME); + + if (p_encoder->n_dynamic_prop_change) { + p_encoder->b_dynmic_prop_change_req = TRUE; + ddl_encode_dynamic_property(p_ddl, TRUE); + } + vidc_720p_encode_set_vop_time( + p_encoder->vop_timing.n_vop_time_resolution, + p_ddl->input_frame.n_frm_delta + ); + + vidc_720p_encode_frame(p_ddl->n_channel_id, + n_ext_buffer_start, + n_ext_buffer_end, + n_start_byte_number, n_y_addr, n_c_addr); +} + +u32 ddl_decode_set_buffers(struct ddl_client_context_type *p_ddl) +{ + struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); + u32 n_comv_buf_size = DDL_COMV_BUFLINE_NO, n_comv_buf_no = 0; + u32 n_ref_buf_no = 0; + + if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_DPB)) { + VIDC_LOG_STRING("STATE-CRITICAL"); + return VCD_ERR_FAIL; + } + + switch (p_decoder->codec_type.e_codec) { + default: + case VCD_CODEC_DIVX_4: + case VCD_CODEC_DIVX_5: + case VCD_CODEC_DIVX_6: + case VCD_CODEC_XVID: + case VCD_CODEC_MPEG2: + case VCD_CODEC_MPEG4: + { + n_comv_buf_no = DDL_MPEG_COMV_BUF_NO; + n_ref_buf_no = DDL_MPEG_REFBUF_COUNT; + break; + } + case VCD_CODEC_H263: + { + n_comv_buf_no = DDL_H263_COMV_BUF_NO; + break; + } + case VCD_CODEC_VC1: + case VCD_CODEC_VC1_RCV: + { + n_comv_buf_no = + p_decoder->client_output_buf_req.n_actual_count + 1; + n_comv_buf_size = DDL_VC1_COMV_BUFLINE_NO; + break; + } + case VCD_CODEC_H264: + { + n_comv_buf_no = + p_decoder->client_output_buf_req.n_actual_count; + break; + } + } + + if (n_comv_buf_no) { + n_comv_buf_size *= (n_comv_buf_no * + (((p_decoder->client_frame_size. + n_width + 15) >> 4)) * + (((p_decoder->client_frame_size. + n_height + 15) >> 4) + 1)); + ddl_pmem_alloc(&p_decoder->dpb_comv_buffer, n_comv_buf_size, + DDL_LINEAR_BUFFER_ALIGN_BYTES); + if (!p_decoder->dpb_comv_buffer.p_virtual_base_addr) { + VIDC_LOGERR_STRING + ("Dec_set_buf:Comv_buf_alloc_failed"); + return VCD_ERR_ALLOC_FAIL; + } + vidc_720p_decode_set_comv_buffer(p_decoder->dpb_comv_buffer. + p_align_physical_addr, + p_decoder->dpb_comv_buffer. + n_buffer_size); + } + p_decoder->ref_buffer.p_align_physical_addr = NULL; + if (n_ref_buf_no) { + u32 n_size, n_yuv_size, n_align_bytes; + n_yuv_size = ddl_get_yuv_buffer_size(&p_decoder-> + client_frame_size, &p_decoder->buf_format, + (!p_decoder->n_progressive_only)); + n_size = n_yuv_size * n_ref_buf_no; + if (p_decoder->buf_format.e_buffer_format == + VCD_BUFFER_FORMAT_NV12) + n_align_bytes = DDL_LINEAR_BUFFER_ALIGN_BYTES; + else + n_align_bytes = DDL_TILE_BUFFER_ALIGN_BYTES; + + ddl_pmem_alloc(&p_decoder->ref_buffer, n_size, n_align_bytes); + if (!p_decoder->ref_buffer.p_virtual_base_addr) { + ddl_pmem_free(p_decoder->dpb_comv_buffer); + VIDC_LOGERR_STRING + ("Dec_set_buf:mpeg_ref_buf_alloc_failed"); + return VCD_ERR_ALLOC_FAIL; + } + } + ddl_decode_set_metadata_output(p_decoder); + + ddl_decoder_dpb_transact(p_decoder, NULL, DDL_DPB_OP_INIT); + + if (p_decoder->codec_type.e_codec == VCD_CODEC_H264) { + vidc_720p_decode_setH264VSPBuffer(p_decoder-> + h264Vsp_temp_buffer. + p_align_physical_addr); + VIDC_LOG1("VSP_BUF_ADDR_SIZE", + p_decoder->h264Vsp_temp_buffer.n_buffer_size); + } + + ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_DPBDONE); + ddl_move_command_state(p_ddl->p_ddl_context, DDL_CMD_DECODE_SET_DPB); + + vidc_720p_submit_command(p_ddl->n_channel_id, + VIDC_720P_CMD_INITBUFFERS); + return VCD_S_SUCCESS; +} + +void ddl_decode_frame_run(struct ddl_client_context_type *p_ddl) +{ + u32 n_ext_buffer_start = 0, n_ext_buffer_end = 0; + u32 n_start_byte_num = 8; + struct ddl_decoder_data_type *p_decoder = &p_ddl->codec_data.decoder; + struct vcd_frame_data_type *p_bit_stream = + &(p_ddl->input_frame.vcd_frm); + + if (!p_bit_stream->n_data_len || + !p_bit_stream->p_physical) { + ddl_decode_eos_run(p_ddl); + return; + } + + ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE); + + ddl_decode_dynamic_property(p_ddl, TRUE); + + ddl_decoder_dpb_transact(p_decoder, NULL, DDL_DPB_OP_SET_MASK); + + n_ext_buffer_start = (u32)p_bit_stream->p_physical + + p_bit_stream->n_offset; + n_start_byte_num = 8 - (n_ext_buffer_start & + DDL_STREAMBUF_ALIGN_GUARD_BYTES); + n_ext_buffer_end = n_ext_buffer_start + p_bit_stream->n_data_len; + n_ext_buffer_start &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES); + DDL_PADDING_HACK(n_ext_buffer_end); + + ddl_move_command_state(p_ddl->p_ddl_context, DDL_CMD_DECODE_FRAME); + + vidc_720p_decode_frame(p_ddl->n_channel_id, + n_ext_buffer_start, + n_ext_buffer_end, + p_bit_stream->n_data_len, + n_start_byte_num, p_bit_stream->n_ip_frm_tag); +} + +void ddl_decode_eos_run(struct ddl_client_context_type *p_ddl) +{ + struct ddl_decoder_data_type *p_decoder = &p_ddl->codec_data.decoder; + + ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE); + + ddl_decode_dynamic_property(p_ddl, TRUE); + + ddl_decoder_dpb_transact(p_decoder, NULL, DDL_DPB_OP_SET_MASK); + + p_decoder->b_dynmic_prop_change_req = TRUE; + + ddl_move_command_state(p_ddl->p_ddl_context, DDL_CMD_EOS); + + vidc_720p_issue_eos(p_ddl->n_channel_id); +} + +u32 ddl_hal_engine_reset(struct ddl_context_type *p_ddl_context) +{ + u32 b_eng_reset; + u32 n_channel_id = 0; + u32 fw_endianness; + enum vidc_720p_endian_type e_dma_endian; + enum vidc_720p_interrupt_level_selection_type e_interrupt_sel; + u32 intr_mask = 0x0; + + if (p_ddl_context->p_current_ddl) + n_channel_id = p_ddl_context->p_current_ddl->n_channel_id; + + e_interrupt_sel = VIDC_720P_INTERRUPT_LEVEL_SEL; + /* Enable all the supported interrupt */ + intr_mask |= VIDC_720P_INTR_BUFFER_FULL; + intr_mask |= VIDC_720P_INTR_FW_DONE; + intr_mask |= VIDC_720P_INTR_DMA_DONE; + intr_mask |= VIDC_720P_INTR_FRAME_DONE; + + vcd_get_fw_property(VCD_FW_ENDIAN, &fw_endianness); + /* Reverse the endianness settings after boot code download */ + if (fw_endianness == VCD_FW_BIG_ENDIAN) + e_dma_endian = VIDC_720P_LITTLE_ENDIAN; + else + e_dma_endian = VIDC_720P_BIG_ENDIAN; + + /* Need to reset MFC silently */ + b_eng_reset = vidc_720p_engine_reset( + n_channel_id, + e_dma_endian, e_interrupt_sel, + intr_mask); + if (!b_eng_reset) { + /* call the hw fatal callback if engine reset fails */ + ddl_hw_fatal_cb(p_ddl_context); + } + return b_eng_reset ; +} diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_helper.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_helper.c new file mode 100644 index 0000000000000..7199758409229 --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_helper.c @@ -0,0 +1,289 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" + +#include "vcd_ddl_utils.h" + +DDL_INLINE struct ddl_context_type *ddl_get_context(void) +{ + static struct ddl_context_type ddl_context; + return &ddl_context; +} + +DDL_INLINE void ddl_move_client_state(struct ddl_client_context_type *p_ddl, + enum ddl_client_state_type e_client_state) +{ + p_ddl->e_client_state = e_client_state; +} + +DDL_INLINE void ddl_move_command_state(struct ddl_context_type *p_ddl_context, + enum ddl_cmd_state_type e_command_state) +{ + p_ddl_context->e_cmd_state = e_command_state; +} + +u32 ddl_client_transact(u32 operation, + struct ddl_client_context_type **pddl_client) +{ + u32 ret_status = VCD_ERR_FAIL; + u32 n_counter; + struct ddl_context_type *p_ddl_context; + + p_ddl_context = ddl_get_context(); + switch (operation) { + case DDL_FREE_CLIENT: + { + if (pddl_client && *pddl_client) { + u32 n_channel_id; + n_channel_id = (*pddl_client)->n_channel_id; + if (n_channel_id < VCD_MAX_NO_CLIENT) { + p_ddl_context-> + a_ddl_clients[n_channel_id] = NULL; + } else { + VIDC_LOG_STRING("CHID_CORRUPTION"); + } + DDL_FREE(*pddl_client); + ret_status = VCD_S_SUCCESS; + } + break; + } + case DDL_GET_CLIENT: + { + ret_status = VCD_ERR_MAX_CLIENT; + for (n_counter = 0; n_counter < VCD_MAX_NO_CLIENT && + ret_status == VCD_ERR_MAX_CLIENT; ++n_counter) { + if (!p_ddl_context->a_ddl_clients[n_counter]) { + *pddl_client = + (struct ddl_client_context_type *) + DDL_MALLOC(sizeof + (struct ddl_client_context_type) + ); + if (!*pddl_client) { + ret_status = VCD_ERR_ALLOC_FAIL; + } else { + DDL_MEMSET(*pddl_client, 0, + sizeof(struct + ddl_client_context_type)); + p_ddl_context-> + a_ddl_clients[n_counter] = + *pddl_client; + (*pddl_client)->n_channel_id = + n_counter; + (*pddl_client)->p_ddl_context = + p_ddl_context; + ret_status = VCD_S_SUCCESS; + } + } + } + break; + } + case DDL_INIT_CLIENTS: + { + for (n_counter = 0; n_counter < VCD_MAX_NO_CLIENT; + ++n_counter) { + p_ddl_context->a_ddl_clients[n_counter] = NULL; + } + ret_status = VCD_S_SUCCESS; + break; + } + case DDL_ACTIVE_CLIENT: + { + for (n_counter = 0; n_counter < VCD_MAX_NO_CLIENT; + ++n_counter) { + if (p_ddl_context->a_ddl_clients[n_counter]) { + ret_status = VCD_S_SUCCESS; + break; + } + } + break; + } + default: + { + ret_status = VCD_ERR_ILLEGAL_PARM; + break; + } + } + return ret_status; +} + +u32 ddl_decoder_dpb_transact(struct ddl_decoder_data_type *p_decoder, + struct ddl_frame_data_type_tag *p_in_out_frame, + u32 n_operation) +{ + u32 vcd_status = VCD_S_SUCCESS; + u32 n_loopc; + struct ddl_frame_data_type_tag *p_found_frame = NULL; + struct ddl_mask_type *p_dpb_mask = &p_decoder->dpb_mask; + + switch (n_operation) { + case DDL_DPB_OP_MARK_BUSY: + case DDL_DPB_OP_MARK_FREE: + { + for (n_loopc = 0; !p_found_frame && + n_loopc < p_decoder->dp_buf.n_no_of_dec_pic_buf; + ++n_loopc) { + if (p_in_out_frame->vcd_frm.p_physical == + p_decoder->dp_buf. + a_dec_pic_buffers[n_loopc].vcd_frm. + p_physical) { + p_found_frame = + &(p_decoder->dp_buf. + a_dec_pic_buffers[n_loopc]); + break; + } + } + + if (p_found_frame) { + if (n_operation == DDL_DPB_OP_MARK_BUSY) { + p_dpb_mask->n_hw_mask &= + (~(0x1 << n_loopc)); + *p_in_out_frame = *p_found_frame; + } else if (n_operation == + DDL_DPB_OP_MARK_FREE) { + p_dpb_mask->n_client_mask |= + (0x1 << n_loopc); + *p_found_frame = *p_in_out_frame; + } + } else { + p_in_out_frame->vcd_frm.p_physical = NULL; + vcd_status = VCD_ERR_BAD_POINTER; + VIDC_LOG_STRING("BUF_NOT_FOUND"); + } + break; + } + case DDL_DPB_OP_SET_MASK: + { + p_dpb_mask->n_hw_mask |= p_dpb_mask->n_client_mask; + p_dpb_mask->n_client_mask = 0; + vidc_720p_decode_set_dpb_release_buffer_mask + (p_dpb_mask->n_hw_mask); + break; + } + case DDL_DPB_OP_INIT: + { + u32 n_dpb_size; + n_dpb_size = (!p_decoder->n_meta_data_offset) ? + p_decoder->dp_buf.a_dec_pic_buffers[0].vcd_frm. + n_alloc_len : p_decoder->n_meta_data_offset; + vidc_720p_decode_set_dpb_details(p_decoder->dp_buf. + n_no_of_dec_pic_buf, + n_dpb_size, + p_decoder->ref_buffer. + p_align_physical_addr); + for (n_loopc = 0; + n_loopc < p_decoder->dp_buf.n_no_of_dec_pic_buf; + ++n_loopc) { + vidc_720p_decode_set_dpb_buffers(n_loopc, + (u32 *) + p_decoder-> + dp_buf. + a_dec_pic_buffers + [n_loopc]. + vcd_frm. + p_physical); + VIDC_LOG1("DEC_DPB_BUFn_SIZE", + p_decoder->dp_buf. + a_dec_pic_buffers[n_loopc].vcd_frm. + n_alloc_len); + } + break; + } + case DDL_DPB_OP_RETRIEVE: + { + u32 n_position; + if (p_dpb_mask->n_client_mask) { + n_position = 0x1; + for (n_loopc = 0; + n_loopc < + p_decoder->dp_buf.n_no_of_dec_pic_buf + && !p_found_frame; ++n_loopc) { + if (p_dpb_mask-> + n_client_mask & n_position) { + p_found_frame = + &p_decoder->dp_buf. + a_dec_pic_buffers[n_loopc]; + p_dpb_mask->n_client_mask &= + ~(n_position); + } + n_position <<= 1; + } + } else if (p_dpb_mask->n_hw_mask) { + n_position = 0x1; + for (n_loopc = 0; + n_loopc < + p_decoder->dp_buf.n_no_of_dec_pic_buf + && !p_found_frame; ++n_loopc) { + if (p_dpb_mask->n_hw_mask + & n_position) { + p_found_frame = + &p_decoder->dp_buf. + a_dec_pic_buffers[n_loopc]; + p_dpb_mask->n_hw_mask &= + ~(n_position); + } + n_position <<= 1; + } + } + if (p_found_frame) + *p_in_out_frame = *p_found_frame; + else + p_in_out_frame->vcd_frm.p_physical = NULL; + break; + } + } + return vcd_status; +} + +void ddl_release_context_buffers(struct ddl_context_type *p_ddl_context) +{ + ddl_pmem_free(p_ddl_context->context_buf_addr); + ddl_pmem_free(p_ddl_context->db_line_buffer); + ddl_pmem_free(p_ddl_context->data_partition_tempbuf); + ddl_pmem_free(p_ddl_context->metadata_shared_input); + ddl_pmem_free(p_ddl_context->dbg_core_dump); + + vcd_fw_release(); +} + +void ddl_release_client_internal_buffers(struct ddl_client_context_type *p_ddl) +{ + if (p_ddl->b_decoding) { + struct ddl_decoder_data_type *p_decoder = + &(p_ddl->codec_data.decoder); + ddl_pmem_free(p_decoder->h264Vsp_temp_buffer); + ddl_pmem_free(p_decoder->dpb_comv_buffer); + ddl_pmem_free(p_decoder->ref_buffer); + DDL_FREE(p_decoder->dp_buf.a_dec_pic_buffers); + ddl_decode_dynamic_property(p_ddl, FALSE); + p_decoder->decode_config.n_sequence_header_len = 0; + p_decoder->decode_config.p_sequence_header = NULL; + p_decoder->dpb_mask.n_client_mask = 0; + p_decoder->dpb_mask.n_hw_mask = 0; + p_decoder->dp_buf.n_no_of_dec_pic_buf = 0; + p_decoder->n_dynamic_prop_change = 0; + + } else { + struct ddl_encoder_data_type *p_encoder = + &(p_ddl->codec_data.encoder); + ddl_pmem_free(p_encoder->enc_dpb_addr); + ddl_pmem_free(p_encoder->seq_header); + ddl_encode_dynamic_property(p_ddl, FALSE); + p_encoder->n_dynamic_prop_change = 0; + } +} diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_internal_property.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_internal_property.h new file mode 100644 index 0000000000000..a81295468ec33 --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_internal_property.h @@ -0,0 +1,91 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VCD_DDL_INTERNAL_PROPERTY_H_ +#define _VCD_DDL_INTERNAL_PROPERTY_H_ +#include "vcd_api.h" + +#define VCD_EVT_RESP_DDL_BASE 0x3000 +#define VCD_EVT_RESP_DEVICE_INIT (VCD_EVT_RESP_DDL_BASE + 0x1) +#define VCD_EVT_RESP_OUTPUT_REQ (VCD_EVT_RESP_DDL_BASE + 0x2) +#define VCD_EVT_RESP_EOS_DONE (VCD_EVT_RESP_DDL_BASE + 0x3) +#define VCD_EVT_RESP_TRANSACTION_PENDING (VCD_EVT_RESP_DDL_BASE + 0x4) + +#define VCD_S_DDL_ERR_BASE 0x90000000 +#define VCD_ERR_MAX_NO_CODEC (VCD_S_DDL_ERR_BASE + 0x1) +#define VCD_ERR_CLIENT_PRESENT (VCD_S_DDL_ERR_BASE + 0x2) +#define VCD_ERR_CLIENT_FATAL (VCD_S_DDL_ERR_BASE + 0x3) + +#define VCD_I_CUSTOM_BASE (VCD_I_RESERVED_BASE) +#define VCD_I_RC_LEVEL_CONFIG (VCD_I_CUSTOM_BASE + 0x1) +#define VCD_I_FRAME_LEVEL_RC (VCD_I_CUSTOM_BASE + 0x2) +#define VCD_I_ADAPTIVE_RC (VCD_I_CUSTOM_BASE + 0x3) +#define VCD_I_CUSTOM_DDL_BASE (VCD_I_RESERVED_BASE + 0x100) +#define DDL_I_INPUT_BUF_REQ (VCD_I_CUSTOM_DDL_BASE + 0x1) +#define DDL_I_OUTPUT_BUF_REQ (VCD_I_CUSTOM_DDL_BASE + 0x2) +#define DDL_I_DPB (VCD_I_CUSTOM_DDL_BASE + 0x3) +#define DDL_I_DPB_RELEASE (VCD_I_CUSTOM_DDL_BASE + 0x4) +#define DDL_I_DPB_RETRIEVE (VCD_I_CUSTOM_DDL_BASE + 0x5) +#define DDL_I_REQ_OUTPUT_FLUSH (VCD_I_CUSTOM_DDL_BASE + 0x6) +#define DDL_I_SEQHDR_ALIGN_BYTES (VCD_I_CUSTOM_DDL_BASE + 0x7) +#define DDL_I_SEQHDR_PRESENT (VCD_I_CUSTOM_DDL_BASE + 0xb) +#define DDL_I_CAPABILITY (VCD_I_CUSTOM_DDL_BASE + 0x8) +#define DDL_I_FRAME_PROC_UNITS (VCD_I_CUSTOM_DDL_BASE + 0x9) + +struct vcd_property_rc_level_type { + u32 b_frame_level_rc; + u32 b_mb_level_rc; +}; + +struct vcd_property_frame_level_rc_params_type { + u32 n_reaction_coeff; +}; + +struct vcd_property_adaptive_rc_params_type { + u32 b_dark_region_as_flag; + u32 b_smooth_region_as_flag; + u32 b_static_region_as_flag; + u32 b_activity_region_flag; +}; + +struct ddl_frame_data_type_tag; + +struct ddl_property_dec_pic_buffers_type { + struct ddl_frame_data_type_tag *a_dec_pic_buffers; + u32 n_no_of_dec_pic_buf; +}; + +struct ddl_property_capability_type { + u32 n_max_num_client; + u32 n_general_command_depth; + u32 n_frame_command_depth; + u32 b_exclusive; + u32 n_ddl_time_out_in_ms; +}; + +#endif diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_interrupt_handler.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_interrupt_handler.c new file mode 100644 index 0000000000000..cccb2b588a213 --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_interrupt_handler.c @@ -0,0 +1,1061 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" + +#include "vcd_ddl_utils.h" +#include "vcd_ddl_metadata.h" + +#if DEBUG +#define DBG(x...) printk(KERN_DEBUG x) +#else +#define DBG(x...) +#endif + +static void ddl_decoder_input_done_callback( + struct ddl_client_context_type *p_ddl, u32 b_frame_transact_end); +static void ddl_decoder_ouput_done_callback( + struct ddl_client_context_type *p_ddl, u32 b_frame_transact_end); + +static u32 ddl_get_frame_type + (struct vcd_frame_data_type *p_frame, u32 n_frame_type); + +static void ddl_getdec_profilelevel +(struct ddl_decoder_data_type *p_decoder, u32 n_profile, u32 n_level); + +static void ddl_dma_done_callback(struct ddl_context_type *p_ddl_context) +{ + if (!DDLCOMMAND_STATE_IS(p_ddl_context, DDL_CMD_DMA_INIT)) { + VIDC_LOG_STRING("UNKWN_DMADONE"); + return; + } + ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + VIDC_LOG_STRING("DMA_DONE"); + ddl_core_start_cpu(p_ddl_context); +} + +static void ddl_cpu_started_callback(struct ddl_context_type *p_ddl_context) +{ + ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + VIDC_LOG_STRING("CPU-STARTED"); + + if (!vidc_720p_cpu_start()) { + ddl_hw_fatal_cb(p_ddl_context); + return; + } + + vidc_720p_set_deblock_line_buffer( + p_ddl_context->db_line_buffer.p_align_physical_addr, + p_ddl_context->db_line_buffer.n_buffer_size); + p_ddl_context->n_device_state = DDL_DEVICE_INITED; + p_ddl_context->ddl_callback(VCD_EVT_RESP_DEVICE_INIT, VCD_S_SUCCESS, + NULL, 0, NULL, p_ddl_context->p_client_data); + DDL_IDLE(p_ddl_context); +} + + +static void ddl_eos_done_callback(struct ddl_context_type *p_ddl_context) +{ + struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; + u32 n_displaystatus; + + if (!DDLCOMMAND_STATE_IS(p_ddl_context, DDL_CMD_EOS)) { + VIDC_LOG_STRING("UNKWN_EOSDONE"); + ddl_client_fatal_cb(p_ddl_context); + return; + } + + if (!p_ddl || + !p_ddl->b_decoding || + !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE) + ) { + VIDC_LOG_STRING("STATE-CRITICAL-EOSDONE"); + ddl_client_fatal_cb(p_ddl_context); + return; + } + ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + + vidc_720p_eos_info(&n_displaystatus); + if ((enum vidc_720p_display_status_type)n_displaystatus + != VIDC_720P_EMPTY_BUFFER) { + VIDC_LOG_STRING("EOSDONE-EMPTYBUF-ISSUE"); + } + + ddl_decode_dynamic_property(p_ddl, FALSE); + ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME); + VIDC_LOG_STRING("EOS_DONE"); + p_ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE, VCD_S_SUCCESS, + NULL, 0, (u32 *) p_ddl, p_ddl_context->p_client_data); + + DDL_IDLE(p_ddl_context); +} + +static u32 ddl_channel_set_callback(struct ddl_context_type *p_ddl_context) +{ + struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; + u32 return_status = FALSE; + + ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + VIDC_DEBUG_REGISTER_LOG; + + if (!p_ddl || + !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_CHDONE) + ) { + VIDC_LOG_STRING("STATE-CRITICAL-CHSET"); + DDL_IDLE(p_ddl_context); + return return_status; + } + VIDC_LOG_STRING("Channel-set"); + ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_INITCODEC); + + if (p_ddl->b_decoding) { + if (p_ddl->codec_data.decoder.b_header_in_start) { + ddl_decode_init_codec(p_ddl); + } else { + p_ddl_context->ddl_callback(VCD_EVT_RESP_START, + VCD_S_SUCCESS, NULL, + 0, (u32 *) p_ddl, + p_ddl_context-> + p_client_data); + + DDL_IDLE(p_ddl_context); + return_status = TRUE; + } + } else { + ddl_encode_init_codec(p_ddl); + } + return return_status; +} + +static void ddl_init_codec_done_callback(struct ddl_context_type *p_ddl_context) +{ + struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; + struct ddl_encoder_data_type *p_encoder; + + if (!p_ddl || + p_ddl->b_decoding || + !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE) + ) { + VIDC_LOG_STRING("STATE-CRITICAL-INITCODEC"); + ddl_client_fatal_cb(p_ddl_context); + return; + } + ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME); + VIDC_LOG_STRING("INIT_CODEC_DONE"); + + p_encoder = &p_ddl->codec_data.encoder; + if (p_encoder->seq_header.p_virtual_base_addr) { + vidc_720p_encode_get_header(&p_encoder->seq_header. + n_buffer_size); + } + + p_ddl_context->ddl_callback(VCD_EVT_RESP_START, VCD_S_SUCCESS, NULL, + 0, (u32 *) p_ddl, p_ddl_context->p_client_data); + + DDL_IDLE(p_ddl_context); +} + +static u32 ddl_header_done_callback(struct ddl_context_type *p_ddl_context) +{ + struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; + struct ddl_decoder_data_type *p_decoder; + struct vidc_720p_seq_hdr_info_type seq_hdr_info; + u32 vcd_event = VCD_EVT_RESP_START; + u32 vcd_status = VCD_S_SUCCESS; + u32 b_req_cb = TRUE, bret = TRUE; + + if (!DDLCOMMAND_STATE_IS(p_ddl_context, DDL_CMD_HEADER_PARSE)) { + VIDC_LOG_STRING("UNKWN_HEADERDONE"); + ddl_client_fatal_cb(p_ddl_context); + return TRUE; + } + + if (!p_ddl || + !p_ddl->b_decoding || + !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE) + ) { + VIDC_LOG_STRING("STATE-CRITICAL-HDDONE"); + ddl_client_fatal_cb(p_ddl_context); + return TRUE; + } + ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_DPB); + VIDC_LOG_STRING("HEADER_DONE"); + VIDC_DEBUG_REGISTER_LOG; + + vidc_720p_decode_get_seq_hdr_info(&seq_hdr_info); + + p_decoder = &(p_ddl->codec_data.decoder); + p_decoder->frame_size.n_width = seq_hdr_info.n_img_size_x; + p_decoder->frame_size.n_height = seq_hdr_info.n_img_size_y; + p_decoder->n_min_dpb_num = seq_hdr_info.n_min_num_dpb; + p_decoder->n_y_cb_cr_size = seq_hdr_info.n_min_dpb_size; + p_decoder->n_progressive_only = 1 - seq_hdr_info.n_progressive; + ddl_getdec_profilelevel(p_decoder, seq_hdr_info.n_profile, + seq_hdr_info.n_level); + ddl_calculate_stride(&p_decoder->frame_size, + !p_decoder->n_progressive_only); + if (seq_hdr_info.n_crop_exists) { + p_decoder->frame_size.n_width -= + (seq_hdr_info.n_crop_right_offset + + seq_hdr_info.n_crop_left_offset); + p_decoder->frame_size.n_height -= + (seq_hdr_info.n_crop_top_offset + + seq_hdr_info.n_crop_bottom_offset); + } + ddl_set_default_decoder_buffer_req(p_decoder, FALSE); + if (seq_hdr_info.n_data_partitioned == 0x1 && + p_decoder->codec_type.e_codec == VCD_CODEC_MPEG4 && + seq_hdr_info.n_img_size_x > DDL_MAX_DP_FRAME_WIDTH && + seq_hdr_info.n_img_size_y > DDL_MAX_DP_FRAME_HEIGHT) { + ddl_client_fatal_cb(p_ddl_context); + return TRUE; + } + + + if (p_decoder->b_header_in_start) { + p_decoder->client_frame_size = p_decoder->frame_size; + p_decoder->client_output_buf_req = + p_decoder->actual_output_buf_req; + if ((p_decoder->frame_size.n_width * + p_decoder->frame_size.n_height) >= (800*480)) { + if ((p_decoder->actual_output_buf_req.\ + n_actual_count + 2) < 10) + p_decoder->client_output_buf_req.\ + n_actual_count = 10; + else + p_decoder->client_output_buf_req.\ + n_actual_count += 2; + } else + p_decoder->client_output_buf_req.\ + n_actual_count = + p_decoder->actual_output_buf_req.\ + n_actual_count + 5; + + p_decoder->client_input_buf_req = + p_decoder->actual_input_buf_req; + } else { + DBG("%s(): n_width = %d client_frame_size.n_width = %d\n", + __func__, p_decoder->frame_size.n_width, + p_decoder->client_frame_size.n_width); + DBG("%s(): n_height = %d client_frame_size.n_height = %d\n", + __func__, p_decoder->frame_size.n_height, + p_decoder->client_frame_size.n_height); + DBG("%s(): n_size = %d client_frame_size n_size = %d\n", + __func__, p_decoder->actual_output_buf_req.n_size, + p_decoder->client_output_buf_req.n_size); + DBG("%s(): n_min_dpb_num = %d n_actual_count = %d\n", __func__, + p_decoder->n_min_dpb_num, + p_decoder->client_output_buf_req.n_actual_count); + + bret = FALSE; + + if (p_decoder->frame_size.n_width == + p_decoder->client_frame_size.n_width + && p_decoder->frame_size.n_height == + p_decoder->client_frame_size.n_height + && p_decoder->actual_output_buf_req.n_size <= + p_decoder->client_output_buf_req.n_size + && p_decoder->n_min_dpb_num <= + p_decoder->client_output_buf_req.n_actual_count) { + vcd_status = ddl_decode_set_buffers(p_ddl); + if (!vcd_status) + b_req_cb = FALSE; + else{ + ddl_client_fatal_cb(p_ddl_context); + b_req_cb = TRUE; + } + } else { + p_decoder->client_frame_size = p_decoder->frame_size; + p_decoder->client_output_buf_req = + p_decoder->actual_output_buf_req; + p_decoder->client_input_buf_req = + p_decoder->actual_input_buf_req; + VIDC_LOGERR_STRING + ("ddlhdr_done_cb:Decode_reconfig_not_supported"); + vcd_event = VCD_EVT_IND_RECONFIG; + } + } + + if (b_req_cb) { + p_ddl_context->ddl_callback(vcd_event, vcd_status, + NULL, 0, (u32 *) p_ddl, p_ddl_context->p_client_data); + + DDL_IDLE(p_ddl_context); + } + return bret; +} + +static u32 ddl_dpb_buffers_set_done_callback(struct ddl_context_type + *p_ddl_context) +{ + struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; + + ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + if (!p_ddl || + !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_DPBDONE) + ) { + VIDC_LOG_STRING("STATE-CRITICAL-DPBDONE"); + ddl_client_fatal_cb(p_ddl_context); + return TRUE; + } + VIDC_LOG_STRING("INTR_DPBDONE"); + ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME); + ddl_decode_frame_run(p_ddl); + return FALSE; +} + +static void ddl_encoder_frame_run_callback(struct ddl_context_type + *p_ddl_context) +{ + struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; + struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); + u32 b_eos_present = FALSE; + + if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE) + ) { + VIDC_LOG_STRING("STATE-CRITICAL-ENCFRMRUN"); + ddl_client_fatal_cb(p_ddl_context); + return; + } + + VIDC_LOG_STRING("ENC_FRM_RUN_DONE"); + + ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + vidc_720p_enc_frame_info(&p_encoder->enc_frame_info); + + p_ddl->output_frame.vcd_frm.n_ip_frm_tag = + p_ddl->input_frame.vcd_frm.n_ip_frm_tag; + p_ddl->output_frame.vcd_frm.n_data_len = + p_encoder->enc_frame_info.n_enc_size; + p_ddl->output_frame.vcd_frm.n_flags |= VCD_FRAME_FLAG_ENDOFFRAME; + ddl_get_frame_type + (&(p_ddl->output_frame.vcd_frm), + p_encoder->enc_frame_info.n_frame_type); + ddl_process_encoder_metadata(p_ddl); + + ddl_encode_dynamic_property(p_ddl, FALSE); + + p_ddl->input_frame.b_frm_trans_end = FALSE; + p_ddl_context->ddl_callback(VCD_EVT_RESP_INPUT_DONE, VCD_S_SUCCESS, + &(p_ddl->input_frame), sizeof(struct ddl_frame_data_type_tag), + (u32 *) p_ddl, p_ddl_context->p_client_data); + +#ifdef CORE_TIMING_INFO + ddl_calc_core_time(1); +#endif + /* check the presence of EOS */ + b_eos_present = + ((VCD_FRAME_FLAG_EOS & p_ddl->input_frame.vcd_frm.n_flags)); + + p_ddl->output_frame.b_frm_trans_end = !b_eos_present; + p_ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS, + &(p_ddl->output_frame), sizeof(struct ddl_frame_data_type_tag), + (u32 *) p_ddl, p_ddl_context->p_client_data); + + if (b_eos_present) { + VIDC_LOG_STRING("ENC-EOS_DONE"); + p_ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE, + VCD_S_SUCCESS, NULL, 0, (u32 *)p_ddl, + p_ddl_context->p_client_data); + } + + ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME); + DDL_IDLE(p_ddl_context); +} + +static u32 ddl_decoder_frame_run_callback(struct ddl_context_type + *p_ddl_context) +{ + struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; + struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); + struct vidc_720p_dec_disp_info_type *p_dec_disp_info = + &(p_decoder->dec_disp_info); + u32 b_callback_end = FALSE; + u32 status = TRUE, eos_present = FALSE;; + + if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE)) { + VIDC_LOG_STRING("STATE-CRITICAL-DECFRMRUN"); + ddl_client_fatal_cb(p_ddl_context); + return TRUE; + } + + VIDC_LOG_STRING("DEC_FRM_RUN_DONE"); + + ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + + vidc_720p_decode_display_info(p_dec_disp_info); + + ddl_decode_dynamic_property(p_ddl, FALSE); + + if (p_dec_disp_info->n_resl_change) { + VIDC_LOGERR_STRING + ("ddl_dec_frm_done:Dec_reconfig_no_tsupported"); + } + + if ((VCD_FRAME_FLAG_EOS & p_ddl->input_frame.vcd_frm.n_flags)) { + b_callback_end = FALSE; + eos_present = TRUE; + } + + + if (p_dec_disp_info->e_disp_status == VIDC_720P_DECODE_ONLY || + p_dec_disp_info->e_disp_status + == VIDC_720P_DECODE_AND_DISPLAY) { + if (!eos_present) + b_callback_end = (p_dec_disp_info->e_disp_status + == VIDC_720P_DECODE_ONLY); + + ddl_decoder_input_done_callback(p_ddl, b_callback_end); + } + + if (p_dec_disp_info->e_disp_status == VIDC_720P_DECODE_AND_DISPLAY + || p_dec_disp_info->e_disp_status == VIDC_720P_DISPLAY_ONLY) { + if (!eos_present) + b_callback_end = + (p_dec_disp_info->e_disp_status + == VIDC_720P_DECODE_AND_DISPLAY); + + ddl_decoder_ouput_done_callback(p_ddl, b_callback_end); + } + + if (p_dec_disp_info->e_disp_status == VIDC_720P_DISPLAY_ONLY) { + /* send the same input once again for decoding */ + ddl_decode_frame_run(p_ddl); + /* client need to ignore the interrupt */ + status = FALSE; + } else if (eos_present) { + /* send EOS command to HW */ + ddl_decode_eos_run(p_ddl); + /* client need to ignore the interrupt */ + status = FALSE; + } else { + ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME); + /* move to Idle */ + DDL_IDLE(p_ddl_context); + } + return status; +} + +static u32 ddl_eos_frame_done_callback(struct ddl_context_type *p_ddl_context) +{ + struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; + struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); + struct vidc_720p_dec_disp_info_type *p_dec_disp_info = + &(p_decoder->dec_disp_info); + + if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE)) { + VIDC_LOGERR_STRING("STATE-CRITICAL-EOSFRMRUN"); + ddl_client_fatal_cb(p_ddl_context); + return TRUE; + } + VIDC_LOG_STRING("EOS_FRM_RUN_DONE"); + + ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + + vidc_720p_decode_display_info(p_dec_disp_info); + + ddl_decode_dynamic_property(p_ddl, FALSE); + + if (p_ddl_context->n_op_failed == 0x1) + VIDC_LOGERR_STRING("ddl_eos_frm_done:OPFAILED!!"); + else if (p_dec_disp_info->n_resl_change) + VIDC_LOGERR_STRING("ddl_eos_frm_done:Dec_reconfig!!"); + + if (p_dec_disp_info->e_disp_status == VIDC_720P_DISPLAY_ONLY) + ddl_decoder_ouput_done_callback(p_ddl, FALSE); + else + VIDC_LOG_STRING("STATE-CRITICAL-WRONG-DISP-STATUS"); + + ddl_decoder_dpb_transact(p_decoder, NULL, DDL_DPB_OP_SET_MASK); + ddl_move_command_state(p_ddl_context, DDL_CMD_EOS); + vidc_720p_submit_command(p_ddl->n_channel_id, + VIDC_720P_CMD_FRAMERUN); + return FALSE; +} + +static void ddl_channel_end_callback(struct ddl_context_type *p_ddl_context) +{ + struct ddl_client_context_type *p_ddl; + + ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + VIDC_LOG_STRING("CH_END_DONE"); + + p_ddl = p_ddl_context->p_current_ddl; + if (!p_ddl || + !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_CHEND) + ) { + VIDC_LOG_STRING("STATE-CRITICAL-CHEND"); + DDL_IDLE(p_ddl_context); + return; + } + + ddl_release_client_internal_buffers(p_ddl); + p_ddl_context->ddl_callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, + NULL, 0, (u32 *) p_ddl, p_ddl_context->p_client_data); + ddl_move_client_state(p_ddl, DDL_CLIENT_OPEN); + DDL_IDLE(p_ddl_context); +} + +static u32 ddl_operation_done_callback(struct ddl_context_type *p_ddl_context) +{ + u32 b_return_status = TRUE; + + switch (p_ddl_context->e_cmd_state) { + case DDL_CMD_DECODE_FRAME: + { + b_return_status = ddl_decoder_frame_run_callback( + p_ddl_context); + break; + } + case DDL_CMD_ENCODE_FRAME: + { + ddl_encoder_frame_run_callback(p_ddl_context); + break; + } + case DDL_CMD_CHANNEL_SET: + { + b_return_status = ddl_channel_set_callback( + p_ddl_context); + break; + } + case DDL_CMD_INIT_CODEC: + { + ddl_init_codec_done_callback(p_ddl_context); + break; + } + case DDL_CMD_HEADER_PARSE: + { + b_return_status = ddl_header_done_callback( + p_ddl_context); + break; + } + case DDL_CMD_DECODE_SET_DPB: + { + b_return_status = ddl_dpb_buffers_set_done_callback( + p_ddl_context); + break; + } + case DDL_CMD_CHANNEL_END: + { + ddl_channel_end_callback(p_ddl_context); + break; + } + case DDL_CMD_EOS: + { + b_return_status = ddl_eos_frame_done_callback( + p_ddl_context); + break; + } + case DDL_CMD_CPU_RESET: + { + ddl_cpu_started_callback(p_ddl_context); + break; + } + default: + { + VIDC_LOG_STRING("UNKWN_OPDONE"); + b_return_status = FALSE; + break; + } + } + return b_return_status; +} + +static u32 ddl_process_intr_status(struct ddl_context_type *p_ddl_context, + u32 int_status) +{ + u32 b_status = TRUE; + switch (int_status) { + case VIDC_720P_INTR_FRAME_DONE: + { + b_status = ddl_operation_done_callback(p_ddl_context); + break; + } + case VIDC_720P_INTR_DMA_DONE: + { + ddl_dma_done_callback(p_ddl_context); + b_status = FALSE; + break; + } + case VIDC_720P_INTR_FW_DONE: + { + ddl_eos_done_callback(p_ddl_context); + break; + } + case VIDC_720P_INTR_BUFFER_FULL: + { + VIDC_LOGERR_STRING("BUF_FULL_INTR"); + break; + } + default: + { + VIDC_LOGERR_STRING("UNKWN_INTR"); + break; + } + } + return b_status; +} + +void ddl_read_and_clear_interrupt(void) +{ + struct ddl_context_type *p_ddl_context; + + p_ddl_context = ddl_get_context(); + if (!p_ddl_context->p_core_virtual_base_addr) { + VIDC_LOGERR_STRING("SPURIOUS_INTERRUPT"); + return; + } + vidc_720p_get_interrupt_status(&p_ddl_context->intr_status, + &p_ddl_context->n_cmd_err_status, + &p_ddl_context->n_disp_pic_err_status, + &p_ddl_context->n_op_failed + ); + + vidc_720p_interrupt_done_clear(); + +} + +u32 ddl_process_core_response(void) +{ + struct ddl_context_type *p_ddl_context; + u32 b_return_status = TRUE; + + p_ddl_context = ddl_get_context(); + if (!p_ddl_context->p_core_virtual_base_addr) { + VIDC_LOGERR_STRING("UNKWN_INTR"); + return FALSE; + } + if (p_ddl_context->intr_status == DDL_INVALID_INTR_STATUS) { + VIDC_LOGERR_STRING("INTERRUPT_NOT_READ"); + return FALSE; + } + + if (!ddl_handle_core_errors(p_ddl_context)) { + b_return_status = ddl_process_intr_status(p_ddl_context, + p_ddl_context->intr_status); + } + + if (p_ddl_context->pf_interrupt_clr) + (*p_ddl_context->pf_interrupt_clr)(); + + p_ddl_context->intr_status = DDL_INVALID_INTR_STATUS; + return b_return_status; +} + +static void ddl_decoder_input_done_callback( + struct ddl_client_context_type *p_ddl, u32 b_frame_transact_end) +{ + struct vidc_720p_dec_disp_info_type *p_dec_disp_info = + &(p_ddl->codec_data.decoder.dec_disp_info); + struct vcd_frame_data_type *p_input_vcd_frm = + &(p_ddl->input_frame.vcd_frm); + ddl_get_frame_type(p_input_vcd_frm, p_dec_disp_info-> + n_input_frame_type); + + p_input_vcd_frm->b_interlaced = (p_dec_disp_info-> + n_input_is_interlace); + + p_input_vcd_frm->n_offset += p_dec_disp_info->n_input_bytes_consumed; + p_input_vcd_frm->n_data_len -= p_dec_disp_info->n_input_bytes_consumed; + + p_ddl->input_frame.b_frm_trans_end = b_frame_transact_end; + p_ddl->p_ddl_context->ddl_callback( + VCD_EVT_RESP_INPUT_DONE, + VCD_S_SUCCESS, + &p_ddl->input_frame, + sizeof(struct ddl_frame_data_type_tag), + (void *)p_ddl, + p_ddl->p_ddl_context->p_client_data); +} + +static void ddl_decoder_ouput_done_callback( + struct ddl_client_context_type *p_ddl, + u32 b_frame_transact_end) +{ + struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); + struct vidc_720p_dec_disp_info_type *p_dec_disp_info = + &(p_decoder->dec_disp_info); + struct ddl_frame_data_type_tag *p_output_frame = + &p_ddl->output_frame; + struct vcd_frame_data_type *p_output_vcd_frm = + &(p_output_frame->vcd_frm); + u32 vcd_status; + u32 n_free_luma_dpb = 0; + + p_output_vcd_frm->p_physical = (u8 *)p_dec_disp_info->n_y_addr; + + if (p_decoder->codec_type.e_codec == VCD_CODEC_MPEG4 || + p_decoder->codec_type.e_codec == VCD_CODEC_VC1 || + p_decoder->codec_type.e_codec == VCD_CODEC_VC1_RCV){ + vidc_720p_decode_skip_frm_details(&n_free_luma_dpb); + if (n_free_luma_dpb) + p_output_vcd_frm->p_physical = (u8 *) n_free_luma_dpb; + } + + + vcd_status = ddl_decoder_dpb_transact( + p_decoder, + p_output_frame, + DDL_DPB_OP_MARK_BUSY); + + + p_output_vcd_frm->n_ip_frm_tag = p_dec_disp_info->n_tag_top; + if (p_dec_disp_info->n_crop_exists == 0x1) { + p_output_vcd_frm->dec_op_prop.disp_frm.n_left = + p_dec_disp_info->n_crop_left_offset; + p_output_vcd_frm->dec_op_prop.disp_frm.n_top = + p_dec_disp_info->n_crop_top_offset; + p_output_vcd_frm->dec_op_prop.disp_frm.n_right = + p_dec_disp_info->n_img_size_x - + p_dec_disp_info->n_crop_right_offset; + p_output_vcd_frm->dec_op_prop.disp_frm.n_bottom = + p_dec_disp_info->n_img_size_y - + p_dec_disp_info->n_crop_bottom_offset; + } else { + p_output_vcd_frm->dec_op_prop.disp_frm.n_left = 0; + p_output_vcd_frm->dec_op_prop.disp_frm.n_top = 0; + p_output_vcd_frm->dec_op_prop.disp_frm.n_right = + p_dec_disp_info->n_img_size_x; + p_output_vcd_frm->dec_op_prop.disp_frm.n_bottom = + p_dec_disp_info->n_img_size_y; + } + if (!p_dec_disp_info->n_disp_is_interlace) { + p_output_vcd_frm->b_interlaced = FALSE; + p_output_frame->n_intrlcd_ip_frm_tag = VCD_FRAMETAG_INVALID; + } else { + p_output_vcd_frm->b_interlaced = TRUE; + p_output_frame->n_intrlcd_ip_frm_tag = + p_dec_disp_info->n_tag_bottom; + } + + p_output_vcd_frm->n_offset = 0; + p_output_vcd_frm->n_data_len = p_decoder->n_y_cb_cr_size; + + if (n_free_luma_dpb) + p_output_vcd_frm->n_data_len = 0; + + p_output_vcd_frm->n_flags |= VCD_FRAME_FLAG_ENDOFFRAME; + + if (!vcd_status) + ddl_process_decoder_metadata(p_ddl); + p_output_frame->b_frm_trans_end = b_frame_transact_end; + +#ifdef CORE_TIMING_INFO + ddl_calc_core_time(0); +#endif + + p_ddl->p_ddl_context->ddl_callback( + VCD_EVT_RESP_OUTPUT_DONE, + vcd_status, + p_output_frame, + sizeof(struct ddl_frame_data_type_tag), + (void *)p_ddl, + p_ddl->p_ddl_context->p_client_data); +} + +static u32 ddl_get_frame_type + (struct vcd_frame_data_type *p_frame, u32 n_frame_type) { + enum vidc_720p_frame_type e_frame_type = + (enum vidc_720p_frame_type)n_frame_type; + u32 b_status = TRUE; + + switch (e_frame_type) { + case VIDC_720P_IFRAME: + { + p_frame->n_flags |= VCD_FRAME_FLAG_SYNCFRAME; + p_frame->e_frame_type = VCD_FRAME_I; + break; + } + case VIDC_720P_PFRAME: + { + p_frame->e_frame_type = VCD_FRAME_P; + break; + } + case VIDC_720P_BFRAME: + { + p_frame->e_frame_type = VCD_FRAME_B; + break; + } + case VIDC_720P_NOTCODED: + { + p_frame->n_data_len = 0; + break; + } + default: + { + VIDC_LOG_STRING("CRITICAL-FRAMETYPE"); + b_status = FALSE; + break; + } + } + return b_status; +} + +static void ddl_getmpeg4_declevel(enum vcd_codec_level_type *p_level, + u32 n_level) +{ + switch (n_level) { + case VIDC_720P_MPEG4_LEVEL0: + { + *p_level = VCD_LEVEL_MPEG4_0; + break; + } + case VIDC_720P_MPEG4_LEVEL0b: + { + *p_level = VCD_LEVEL_MPEG4_0b; + break; + } + case VIDC_720P_MPEG4_LEVEL1: + { + *p_level = VCD_LEVEL_MPEG4_1; + break; + } + case VIDC_720P_MPEG4_LEVEL2: + { + *p_level = VCD_LEVEL_MPEG4_2; + break; + } + case VIDC_720P_MPEG4_LEVEL3: + { + *p_level = VCD_LEVEL_MPEG4_3; + break; + } + case VIDC_720P_MPEG4_LEVEL3b: + { + *p_level = VCD_LEVEL_MPEG4_3b; + break; + } + case VIDC_720P_MPEG4_LEVEL4a: + { + *p_level = VCD_LEVEL_MPEG4_4a; + break; + } + case VIDC_720P_MPEG4_LEVEL5: + { + *p_level = VCD_LEVEL_MPEG4_5; + break; + } + case VIDC_720P_MPEG4_LEVEL6: + { + *p_level = VCD_LEVEL_MPEG4_6; + break; + } + } +} + +static void ddl_geth264_declevel(enum vcd_codec_level_type *p_level, + u32 n_level) +{ + switch (n_level) { + case VIDC_720P_H264_LEVEL1: + { + *p_level = VCD_LEVEL_H264_1; + break; + } + case VIDC_720P_H264_LEVEL1b: + { + *p_level = VCD_LEVEL_H264_1b; + break; + } + case VIDC_720P_H264_LEVEL1p1: + { + *p_level = VCD_LEVEL_H264_1p1; + break; + } + case VIDC_720P_H264_LEVEL1p2: + { + *p_level = VCD_LEVEL_H264_1p2; + break; + } + case VIDC_720P_H264_LEVEL1p3: + { + *p_level = VCD_LEVEL_H264_1p3; + break; + } + case VIDC_720P_H264_LEVEL2: + { + *p_level = VCD_LEVEL_H264_2; + break; + } + case VIDC_720P_H264_LEVEL2p1: + { + *p_level = VCD_LEVEL_H264_2p1; + break; + } + case VIDC_720P_H264_LEVEL2p2: + { + *p_level = VCD_LEVEL_H264_2p2; + break; + } + case VIDC_720P_H264_LEVEL3: + { + *p_level = VCD_LEVEL_H264_3; + break; + } + case VIDC_720P_H264_LEVEL3p1: + { + *p_level = VCD_LEVEL_H264_3p1; + break; + } + case VIDC_720P_H264_LEVEL3p2: + { + *p_level = VCD_LEVEL_H264_3p2; + break; + } + + } +} + +static void ddl_get_vc1_dec_level( + enum vcd_codec_level_type *p_level, u32 level, + enum vcd_codec_profile_type vc1_profile) +{ + if (vc1_profile == VCD_PROFILE_VC1_ADVANCE) { + switch (level) { + case VIDC_720P_VC1_LEVEL0: + { + *p_level = VCD_LEVEL_VC1_0; + break; + } + case VIDC_720P_VC1_LEVEL1: + { + *p_level = VCD_LEVEL_VC1_1; + break; + } + case VIDC_720P_VC1_LEVEL2: + { + *p_level = VCD_LEVEL_VC1_2; + break; + } + case VIDC_720P_VC1_LEVEL3: + { + *p_level = VCD_LEVEL_VC1_3; + break; + } + case VIDC_720P_VC1_LEVEL4: + { + *p_level = VCD_LEVEL_VC1_4; + break; + } + } + return; + } + + /* now determine the Main and Simple profile level */ + switch (level) { + case VIDC_720P_VC1_LEVEL_LOW: + { + *p_level = VCD_LEVEL_VC1_LOW; + break; + } + case VIDC_720P_VC1_LEVEL_MED: + { + *p_level = VCD_LEVEL_VC1_MEDIUM; + break; + } + case VIDC_720P_VC1_LEVEL_HIGH: + { + *p_level = VCD_LEVEL_VC1_HIGH; + break; + } + } +} + +static void ddl_get_mpeg2_dec_level(enum vcd_codec_level_type *p_level, + u32 level) +{ + switch (level) { + case VIDCL_720P_MPEG2_LEVEL_LOW: + { + *p_level = VCD_LEVEL_MPEG2_LOW; + break; + } + case VIDCL_720P_MPEG2_LEVEL_MAIN: + { + *p_level = VCD_LEVEL_MPEG2_MAIN; + break; + } + case VIDCL_720P_MPEG2_LEVEL_HIGH14: + { + *p_level = VCD_LEVEL_MPEG2_HIGH_14; + break; + } + } +} + +static void ddl_getdec_profilelevel(struct ddl_decoder_data_type *p_decoder, + u32 n_profile, u32 n_level) +{ + enum vcd_codec_profile_type profile = VCD_PROFILE_UNKNOWN; + enum vcd_codec_level_type level = VCD_LEVEL_UNKNOWN; + + switch (p_decoder->codec_type.e_codec) { + case VCD_CODEC_MPEG4: + { + if (n_profile == VIDC_720P_PROFILE_MPEG4_SP) + profile = VCD_PROFILE_MPEG4_SP; + else if (n_profile == VIDC_720P_PROFILE_MPEG4_ASP) + profile = VCD_PROFILE_MPEG4_ASP; + + ddl_getmpeg4_declevel(&level, n_level); + break; + } + case VCD_CODEC_H264: + { + if (n_profile == VIDC_720P_PROFILE_H264_BASELINE) + profile = VCD_PROFILE_H264_BASELINE; + else if (n_profile == VIDC_720P_PROFILE_H264_MAIN) + profile = VCD_PROFILE_H264_MAIN; + else if (n_profile == VIDC_720P_PROFILE_H264_HIGH) + profile = VCD_PROFILE_H264_HIGH; + ddl_geth264_declevel(&level, n_level); + break; + } + default: + case VCD_CODEC_H263: + { + break; + } + case VCD_CODEC_VC1: + case VCD_CODEC_VC1_RCV: + { + if (n_profile == VIDC_720P_PROFILE_VC1_SP) + profile = VCD_PROFILE_VC1_SIMPLE; + else if (n_profile == VIDC_720P_PROFILE_VC1_MAIN) + profile = VCD_PROFILE_VC1_MAIN; + else if (n_profile == VIDC_720P_PROFILE_VC1_ADV) + profile = VCD_PROFILE_VC1_ADVANCE; + ddl_get_vc1_dec_level(&level, n_level, profile); + break; + } + case VCD_CODEC_MPEG2: + { + if (n_profile == VIDC_720P_PROFILE_MPEG2_MAIN) + profile = VCD_PROFILE_MPEG2_MAIN; + else if (n_profile == VIDC_720P_PROFILE_MPEG2_SP) + profile = VCD_PROFILE_MPEG2_SIMPLE; + ddl_get_mpeg2_dec_level(&level, n_level); + break; + } + } + + p_decoder->profile.e_profile = profile; + p_decoder->level.e_level = level; +} diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.c new file mode 100644 index 0000000000000..140a5c8878b7b --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.c @@ -0,0 +1,607 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" +#include "vcd_ddl_utils.h" +#include "vcd_ddl_metadata.h" + +static u32 *ddl_metadata_hdr_entry(struct ddl_client_context_type *p_ddl, + u32 n_meta_data_type) +{ + u32 n_skip_words = 0; + u32 *p_buffer; + + if (p_ddl->b_decoding) { + p_buffer = (u32 *) + p_ddl->codec_data.decoder.meta_data_input. + p_align_virtual_addr; + n_skip_words = 32 + 1; + p_buffer += n_skip_words; + + switch (n_meta_data_type) { + default: + case VCD_METADATA_DATANONE: + { + n_skip_words = 0; + break; + } + case VCD_METADATA_QPARRAY: + { + n_skip_words = 3; + break; + } + case VCD_METADATA_CONCEALMB: + { + n_skip_words = 6; + break; + } + case VCD_METADATA_VC1: + { + n_skip_words = 9; + break; + } + case VCD_METADATA_SEI: + { + n_skip_words = 12; + break; + } + case VCD_METADATA_VUI: + { + n_skip_words = 15; + break; + } + case VCD_METADATA_PASSTHROUGH: + { + n_skip_words = 18; + break; + } + case VCD_METADATA_QCOMFILLER: + { + n_skip_words = 21; + break; + } + } + } else { + p_buffer = (u32 *) + p_ddl->codec_data.encoder.meta_data_input. + p_align_virtual_addr; + n_skip_words = 2; + p_buffer += n_skip_words; + + switch (n_meta_data_type) { + default: + case VCD_METADATA_DATANONE: + { + n_skip_words = 0; + break; + } + case VCD_METADATA_ENC_SLICE: + { + n_skip_words = 3; + break; + } + case VCD_METADATA_QCOMFILLER: + { + n_skip_words = 6; + break; + } + } + + } + + p_buffer += n_skip_words; + return p_buffer; +} + +void ddl_set_default_meta_data_hdr(struct ddl_client_context_type *p_ddl) +{ + struct ddl_buf_addr_type *p_main_buffer = + &p_ddl->p_ddl_context->metadata_shared_input; + struct ddl_buf_addr_type *p_client_buffer; + u32 *p_hdr_entry; + + if (p_ddl->b_decoding) + p_client_buffer = &(p_ddl->codec_data.decoder.meta_data_input); + else + p_client_buffer = &(p_ddl->codec_data.encoder.meta_data_input); + + DDL_METADATA_CLIENT_INPUTBUF(p_main_buffer, p_client_buffer, + p_ddl->n_channel_id); + + p_hdr_entry = ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_QCOMFILLER); + p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 1; + p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1; + p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_QCOMFILLER; + + p_hdr_entry = ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_DATANONE); + p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 2; + p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 2; + p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_DATANONE; + + if (p_ddl->b_decoding) { + p_hdr_entry = + ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_QPARRAY); + p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 3; + p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 3; + p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_QPARRAY; + + p_hdr_entry = + ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_CONCEALMB); + p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 4; + p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 4; + p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = + VCD_METADATA_CONCEALMB; + + p_hdr_entry = ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_SEI); + p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 5; + p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 5; + p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_SEI; + + p_hdr_entry = ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_VUI); + p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 6; + p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 6; + p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_VUI; + + p_hdr_entry = ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_VC1); + p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 7; + p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 7; + p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_VC1; + + p_hdr_entry = + ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_PASSTHROUGH); + p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 8; + p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 8; + p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = + VCD_METADATA_PASSTHROUGH; + + } else { + p_hdr_entry = + ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_ENC_SLICE); + p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 9; + p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 9; + p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = + VCD_METADATA_ENC_SLICE; + } +} + +static u32 ddl_supported_metadata_flag(struct ddl_client_context_type *p_ddl) +{ + u32 n_flag = 0; + + if (p_ddl->b_decoding) { + enum vcd_codec_type e_codec = + p_ddl->codec_data.decoder.codec_type.e_codec; + + n_flag |= (VCD_METADATA_CONCEALMB | + VCD_METADATA_PASSTHROUGH | VCD_METADATA_QPARRAY); + if (e_codec == VCD_CODEC_H264) { + n_flag |= (VCD_METADATA_SEI | VCD_METADATA_VUI); + } else if (e_codec == VCD_CODEC_VC1 || + e_codec == VCD_CODEC_VC1_RCV) { + n_flag |= VCD_METADATA_VC1; + } + } else { + n_flag |= VCD_METADATA_ENC_SLICE; + } + + return n_flag; +} + +void ddl_set_default_metadata_flag(struct ddl_client_context_type *p_ddl) +{ + if (p_ddl->b_decoding) + p_ddl->codec_data.decoder.n_meta_data_enable_flag = 0; + else + p_ddl->codec_data.encoder.n_meta_data_enable_flag = 0; +} + +void ddl_set_default_decoder_metadata_buffer_size( + struct ddl_decoder_data_type *p_decoder, + struct vcd_property_frame_size_type *p_frame_size, + struct vcd_buffer_requirement_type *p_output_buf_req) +{ + u32 n_flag = p_decoder->n_meta_data_enable_flag; + u32 n_suffix = 0; + u32 n_size = 0; + + if (!n_flag) { + p_decoder->n_suffix = 0; + return; + } + + if (n_flag & VCD_METADATA_QPARRAY) { + u32 n_num_of_mb = + ((p_frame_size->n_width * p_frame_size->n_height) >> 8); + n_size = DDL_METADATA_HDR_SIZE; + n_size += n_num_of_mb; + DDL_METADATA_ALIGNSIZE(n_size); + n_suffix += n_size; + } + if (n_flag & VCD_METADATA_CONCEALMB) { + u32 n_num_of_mb = + ((p_frame_size->n_width * p_frame_size->n_height) >> 8); + n_size = DDL_METADATA_HDR_SIZE; + n_size *= (4 * n_num_of_mb / 2); + DDL_METADATA_ALIGNSIZE(n_size); + n_suffix += n_size; + } + if (n_flag & VCD_METADATA_VC1) { + n_size = DDL_METADATA_HDR_SIZE; + n_size += DDL_METADATA_VC1_PAYLOAD_SIZE; + DDL_METADATA_ALIGNSIZE(n_size); + n_suffix += n_size; + } + if (n_flag & VCD_METADATA_SEI) { + n_size = DDL_METADATA_HDR_SIZE; + n_size += DDL_METADATA_SEI_PAYLOAD_SIZE; + DDL_METADATA_ALIGNSIZE(n_size); + n_suffix += (n_size * DDL_METADATA_SEI_MAX); + } + if (n_flag & VCD_METADATA_VUI) { + n_size = DDL_METADATA_HDR_SIZE; + n_size += DDL_METADATA_VUI_PAYLOAD_SIZE; + DDL_METADATA_ALIGNSIZE(n_size); + n_suffix += (n_size); + } + if (n_flag & VCD_METADATA_PASSTHROUGH) { + n_size = DDL_METADATA_HDR_SIZE; + n_size += DDL_METADATA_PASSTHROUGH_PAYLOAD_SIZE; + DDL_METADATA_ALIGNSIZE(n_size); + n_suffix += (n_size); + } + n_size = DDL_METADATA_EXTRADATANONE_SIZE; + DDL_METADATA_ALIGNSIZE(n_size); + n_suffix += (n_size); + + n_suffix += DDL_METADATA_EXTRAPAD_SIZE; + DDL_METADATA_ALIGNSIZE(n_suffix); + + p_decoder->n_suffix = n_suffix; + p_output_buf_req->n_size += n_suffix; + return; +} + +void ddl_set_default_encoder_metadata_buffer_size(struct ddl_encoder_data_type + *p_encoder) +{ + u32 n_flag = p_encoder->n_meta_data_enable_flag; + u32 n_suffix = 0; + u32 n_size = 0; + + if (!n_flag) { + p_encoder->n_suffix = 0; + return; + } + + if (n_flag & VCD_METADATA_ENC_SLICE) { + u32 n_num_of_mb = (p_encoder->frame_size.n_width * + p_encoder->frame_size.n_height / 16 / 16); + n_size = DDL_METADATA_HDR_SIZE; + + n_size += 4; + + n_size += (8 * n_num_of_mb); + DDL_METADATA_ALIGNSIZE(n_size); + n_suffix += n_size; + } + + n_size = DDL_METADATA_EXTRADATANONE_SIZE; + DDL_METADATA_ALIGNSIZE(n_size); + n_suffix += (n_size); + + n_suffix += DDL_METADATA_EXTRAPAD_SIZE; + DDL_METADATA_ALIGNSIZE(n_suffix); + + p_encoder->n_suffix = n_suffix; + p_encoder->output_buf_req.n_size += n_suffix; +} + +u32 ddl_set_metadata_params(struct ddl_client_context_type *p_ddl, + struct vcd_property_hdr_type *p_property_hdr, + void *p_property_value) +{ + u32 vcd_status = VCD_ERR_ILLEGAL_PARM; + if (p_property_hdr->prop_id == VCD_I_METADATA_ENABLE) { + struct vcd_property_meta_data_enable_type *p_meta_data_enable = + (struct vcd_property_meta_data_enable_type *) + p_property_value; + u32 *p_meta_data_enable_flag; + enum vcd_codec_type e_codec; + if (p_ddl->b_decoding) { + p_meta_data_enable_flag = + &(p_ddl->codec_data.decoder. + n_meta_data_enable_flag); + e_codec = p_ddl->codec_data.decoder.codec_type.e_codec; + } else { + p_meta_data_enable_flag = + &(p_ddl->codec_data.encoder. + n_meta_data_enable_flag); + e_codec = p_ddl->codec_data.encoder.codec_type.e_codec; + } + if (sizeof(struct vcd_property_meta_data_enable_type) == + p_property_hdr->n_size && + DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_OPEN) && + e_codec) { + if (!p_meta_data_enable->n_meta_data_enable_flag) { + *p_meta_data_enable_flag = 0; + if (p_ddl->b_decoding) { + ddl_set_default_decoder_buffer_req + (&p_ddl->codec_data.decoder, TRUE); + } else { + ddl_set_default_encoder_buffer_req + (&p_ddl->codec_data.encoder); + } + + } else { + u32 n_flag = ddl_supported_metadata_flag(p_ddl); + n_flag &= + (p_meta_data_enable-> + n_meta_data_enable_flag); + if (n_flag) { + n_flag |= DDL_METADATA_MANDATORY; + if (n_flag != + *p_meta_data_enable_flag) { + *p_meta_data_enable_flag = + n_flag; + + if (p_ddl->b_decoding) { + ddl_set_default_decoder_buffer_req + (&p_ddl->codec_data. + decoder, TRUE); + } else { + ddl_set_default_encoder_buffer_req + (&p_ddl->codec_data. + encoder); + } + + } + } + } + vcd_status = VCD_S_SUCCESS; + } + } else if (p_property_hdr->prop_id == VCD_I_METADATA_HEADER) { + struct vcd_property_metadata_hdr_type *p_hdr = + (struct vcd_property_metadata_hdr_type *)p_property_value; + if (sizeof(struct vcd_property_metadata_hdr_type) == + p_property_hdr->n_size) { + u32 n_flag = ddl_supported_metadata_flag(p_ddl); + n_flag |= DDL_METADATA_MANDATORY; + n_flag &= p_hdr->n_meta_data_id_type; + if (!(n_flag & (n_flag - 1))) { + u32 *p_hdr_entry = + ddl_metadata_hdr_entry(p_ddl, n_flag); + p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = + p_hdr->n_version; + p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = + p_hdr->n_port_index; + p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = + p_hdr->e_type; + vcd_status = VCD_S_SUCCESS; + } + } + } + return vcd_status; +} + +u32 ddl_get_metadata_params(struct ddl_client_context_type *p_ddl, + struct vcd_property_hdr_type *p_property_hdr, + void *p_property_value) +{ + u32 vcd_status = VCD_ERR_ILLEGAL_PARM ; + if (p_property_hdr->prop_id == VCD_I_METADATA_ENABLE && + sizeof(struct vcd_property_meta_data_enable_type) + == p_property_hdr->n_size) { + struct vcd_property_meta_data_enable_type *p_meta_data_enable = + (struct vcd_property_meta_data_enable_type *) + p_property_value; + p_meta_data_enable->n_meta_data_enable_flag = + ((p_ddl->b_decoding) ? + (p_ddl->codec_data.decoder.n_meta_data_enable_flag) + : (p_ddl->codec_data.encoder.n_meta_data_enable_flag)); + vcd_status = VCD_S_SUCCESS; + } else if (p_property_hdr->prop_id == VCD_I_METADATA_HEADER && + sizeof(struct vcd_property_metadata_hdr_type) == + p_property_hdr->n_size) { + struct vcd_property_metadata_hdr_type *p_hdr = + (struct vcd_property_metadata_hdr_type *) + p_property_value; + u32 n_flag = ddl_supported_metadata_flag(p_ddl); + n_flag |= DDL_METADATA_MANDATORY; + n_flag &= p_hdr->n_meta_data_id_type; + if (!(n_flag & (n_flag - 1))) { + u32 *p_hdr_entry = ddl_metadata_hdr_entry(p_ddl, + n_flag); + p_hdr->n_version = + p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX]; + p_hdr->n_port_index = + p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX]; + p_hdr->e_type = + p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX]; + vcd_status = VCD_S_SUCCESS; + } + } + return vcd_status; +} + +void ddl_metadata_enable(struct ddl_client_context_type *p_ddl) +{ + u32 n_flag, n_hal_flag = 0; + u32 *p_metadata_input; + if (p_ddl->b_decoding) { + n_flag = p_ddl->codec_data.decoder.n_meta_data_enable_flag; + p_metadata_input = + p_ddl->codec_data.decoder.meta_data_input. + p_align_physical_addr; + } else { + n_flag = p_ddl->codec_data.encoder.n_meta_data_enable_flag; + p_metadata_input = + p_ddl->codec_data.encoder.meta_data_input. + p_align_physical_addr; + } + if (n_flag) { + if (n_flag & VCD_METADATA_QPARRAY) + n_hal_flag |= VIDC_720P_METADATA_ENABLE_QP; + if (n_flag & VCD_METADATA_CONCEALMB) + n_hal_flag |= VIDC_720P_METADATA_ENABLE_CONCEALMB; + if (n_flag & VCD_METADATA_VC1) + n_hal_flag |= VIDC_720P_METADATA_ENABLE_VC1; + if (n_flag & VCD_METADATA_SEI) + n_hal_flag |= VIDC_720P_METADATA_ENABLE_SEI; + if (n_flag & VCD_METADATA_VUI) + n_hal_flag |= VIDC_720P_METADATA_ENABLE_VUI; + if (n_flag & VCD_METADATA_ENC_SLICE) + n_hal_flag |= VIDC_720P_METADATA_ENABLE_ENCSLICE; + if (n_flag & VCD_METADATA_PASSTHROUGH) + n_hal_flag |= VIDC_720P_METADATA_ENABLE_PASSTHROUGH; + } else { + p_metadata_input = 0; + } + vidc_720p_metadata_enable(n_hal_flag, p_metadata_input); +} + +u32 ddl_encode_set_metadata_output_buf(struct ddl_client_context_type *p_ddl) +{ + struct ddl_encoder_data_type *p_encoder = &p_ddl->codec_data.encoder; + u32 *p_buffer; + struct vcd_frame_data_type *p_stream = &(p_ddl->output_frame.vcd_frm); + u32 n_ext_buffer_end, n_hw_metadata_start; + + n_ext_buffer_end = (u32) p_stream->p_physical + p_stream->n_alloc_len; + if (!p_encoder->n_meta_data_enable_flag) { + n_ext_buffer_end &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES); + return n_ext_buffer_end; + } + n_hw_metadata_start = (n_ext_buffer_end - p_encoder->n_suffix) & + ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES); + + n_ext_buffer_end = (n_hw_metadata_start - 1) & + ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES); + + p_buffer = p_encoder->meta_data_input.p_align_virtual_addr; + + *p_buffer++ = p_encoder->n_suffix; + + *p_buffer = n_hw_metadata_start; + + p_encoder->n_meta_data_offset = + n_hw_metadata_start - (u32) p_stream->p_physical; + + return n_ext_buffer_end; +} + +void ddl_decode_set_metadata_output(struct ddl_decoder_data_type *p_decoder) +{ + u32 *p_buffer; + u32 n_loopc; + + if (!p_decoder->n_meta_data_enable_flag) { + p_decoder->n_meta_data_offset = 0; + return; + } + + p_decoder->n_meta_data_offset = ddl_get_yuv_buffer_size( + &p_decoder->client_frame_size, &p_decoder->buf_format, + (!p_decoder->n_progressive_only)); + + p_buffer = p_decoder->meta_data_input.p_align_virtual_addr; + + *p_buffer++ = p_decoder->n_suffix; + + for (n_loopc = 0; n_loopc < p_decoder->dp_buf.n_no_of_dec_pic_buf; + ++n_loopc) { + *p_buffer++ = (u32) (p_decoder->n_meta_data_offset + (u8 *) + p_decoder->dp_buf. + a_dec_pic_buffers[n_loopc].vcd_frm. + p_physical); + } +} + +void ddl_process_encoder_metadata(struct ddl_client_context_type *p_ddl) +{ + struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); + struct vcd_frame_data_type *p_out_frame = + &(p_ddl->output_frame.vcd_frm); + u32 *p_qfiller_hdr, *p_qfiller, n_start_addr; + u32 n_qfiller_size; + + if (!p_encoder->n_meta_data_enable_flag) { + p_out_frame->n_flags &= ~(VCD_FRAME_FLAG_EXTRADATA); + return; + } + + if (!p_encoder->enc_frame_info.n_metadata_exists) { + p_out_frame->n_flags &= ~(VCD_FRAME_FLAG_EXTRADATA); + return; + } + p_out_frame->n_flags |= VCD_FRAME_FLAG_EXTRADATA; + + n_start_addr = (u32) ((u8 *) p_out_frame->p_virtual + + p_out_frame->n_offset); + p_qfiller = (u32 *) ((p_out_frame->n_data_len + n_start_addr + 3) & ~3); + + n_qfiller_size = (u32) ((p_encoder->n_meta_data_offset + + (u8 *) p_out_frame->p_virtual) - + (u8 *) p_qfiller); + + p_qfiller_hdr = ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_QCOMFILLER); + + *p_qfiller++ = n_qfiller_size; + *p_qfiller++ = p_qfiller_hdr[DDL_METADATA_HDR_VERSION_INDEX]; + *p_qfiller++ = p_qfiller_hdr[DDL_METADATA_HDR_PORT_INDEX]; + *p_qfiller++ = p_qfiller_hdr[DDL_METADATA_HDR_TYPE_INDEX]; + *p_qfiller = (u32) (n_qfiller_size - DDL_METADATA_HDR_SIZE); +} + +void ddl_process_decoder_metadata(struct ddl_client_context_type *p_ddl) +{ + struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); + struct vcd_frame_data_type *p_output_frame = + &(p_ddl->output_frame.vcd_frm); + u32 *p_qfiller_hdr, *p_qfiller; + u32 n_qfiller_size; + + if (!p_decoder->n_meta_data_enable_flag) { + p_output_frame->n_flags &= ~(VCD_FRAME_FLAG_EXTRADATA); + return; + } + + if (!p_decoder->dec_disp_info.n_metadata_exists) { + p_output_frame->n_flags &= ~(VCD_FRAME_FLAG_EXTRADATA); + return; + } + p_output_frame->n_flags |= VCD_FRAME_FLAG_EXTRADATA; + + if (p_output_frame->n_data_len != p_decoder->n_meta_data_offset) { + p_qfiller = (u32 *) ((u32) ((p_output_frame->n_data_len + + p_output_frame->n_offset + + (u8 *) p_output_frame->p_virtual) + + 3) & ~3); + + n_qfiller_size = (u32) ((p_decoder->n_meta_data_offset + + (u8 *) p_output_frame->p_virtual) - + (u8 *) p_qfiller); + + p_qfiller_hdr = + ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_QCOMFILLER); + *p_qfiller++ = n_qfiller_size; + *p_qfiller++ = p_qfiller_hdr[DDL_METADATA_HDR_VERSION_INDEX]; + *p_qfiller++ = p_qfiller_hdr[DDL_METADATA_HDR_PORT_INDEX]; + *p_qfiller++ = p_qfiller_hdr[DDL_METADATA_HDR_TYPE_INDEX]; + *p_qfiller = (u32) (n_qfiller_size - DDL_METADATA_HDR_SIZE); + } +} diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.h new file mode 100644 index 0000000000000..c8073ca1952e0 --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.h @@ -0,0 +1,95 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VCD_DDL_METADATA_H_ +#define _VCD_DDL_METADATA_H_ + +#define DDL_MAX_DEC_METADATATYPE (8) +#define DDL_MAX_ENC_METADATATYPE (3) + +#define DDL_METADATA_EXTRAPAD_SIZE (256) +#define DDL_METADATA_HDR_SIZE (20) + +#define DDL_METADATA_EXTRADATANONE_SIZE (24) + +#define DDL_METADATA_ALIGNSIZE(x) ((x) = (((x) + 0x7) & ~0x7)) + +#define DDL_METADATA_MANDATORY (VCD_METADATA_DATANONE | \ + VCD_METADATA_QCOMFILLER) + +#define DDL_METADATA_VC1_PAYLOAD_SIZE (38*4) + +#define DDL_METADATA_SEI_PAYLOAD_SIZE (100) +#define DDL_METADATA_SEI_MAX (5) + +#define DDL_METADATA_VUI_PAYLOAD_SIZE (256) + +#define DDL_METADATA_PASSTHROUGH_PAYLOAD_SIZE (68) + +#define DDL_METADATA_CLIENT_INPUTBUFSIZE (256) +#define DDL_METADATA_TOTAL_INPUTBUFSIZE \ + (DDL_METADATA_CLIENT_INPUTBUFSIZE * VCD_MAX_NO_CLIENT) + +#define DDL_METADATA_CLIENT_INPUTBUF(p_main_buffer, p_client_buffer, \ + n_channel_id) \ +{ \ + (p_client_buffer)->p_align_physical_addr = (u32 *)\ + ((u8 *)(p_main_buffer)->p_align_physical_addr + \ + (DDL_METADATA_CLIENT_INPUTBUFSIZE * (n_channel_id)) \ + ); \ + (p_client_buffer)->p_align_virtual_addr = (u32 *)\ + ((u8 *)(p_main_buffer)->p_align_virtual_addr + \ + (DDL_METADATA_CLIENT_INPUTBUFSIZE * (n_channel_id)) \ + ); \ + (p_client_buffer)->p_virtual_base_addr = 0; \ +} + +#define DDL_METADATA_HDR_VERSION_INDEX 0 +#define DDL_METADATA_HDR_PORT_INDEX 1 +#define DDL_METADATA_HDR_TYPE_INDEX 2 + + +void ddl_set_default_meta_data_hdr(struct ddl_client_context_type *p_ddl); +u32 ddl_get_metadata_params(struct ddl_client_context_type *p_ddl, + struct vcd_property_hdr_type *p_property_hdr, void *p_property_value); +u32 ddl_set_metadata_params(struct ddl_client_context_type *p_ddl, + struct vcd_property_hdr_type *p_property_hdr, + void *p_property_value); +void ddl_set_default_metadata_flag(struct ddl_client_context_type *p_ddl); +void ddl_set_default_decoder_metadata_buffer_size + (struct ddl_decoder_data_type *p_decoder, + struct vcd_property_frame_size_type *p_frame_size, + struct vcd_buffer_requirement_type *p_output_buf_req); +void ddl_set_default_encoder_metadata_buffer_size(struct ddl_encoder_data_type + *p_encoder); +void ddl_metadata_enable(struct ddl_client_context_type *p_ddl); +u32 ddl_encode_set_metadata_output_buf(struct ddl_client_context_type *p_ddl); +void ddl_decode_set_metadata_output(struct ddl_decoder_data_type *p_decoder); +void ddl_process_encoder_metadata(struct ddl_client_context_type *p_ddl); +void ddl_process_decoder_metadata(struct ddl_client_context_type *p_ddl); +#endif diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_properties.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_properties.c new file mode 100644 index 0000000000000..211b33765096f --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_properties.c @@ -0,0 +1,1925 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" + +#include "vcd_ddl_utils.h" +#include "vcd_ddl_metadata.h" + +static u32 ddl_set_dec_property(struct ddl_client_context_type *pddl, + struct vcd_property_hdr_type *p_property_hdr, + void *p_property_value); +static u32 ddl_set_enc_property(struct ddl_client_context_type *pddl, + struct vcd_property_hdr_type *p_property_hdr, + void *p_property_value); +static u32 ddl_get_dec_property(struct ddl_client_context_type *pddl, + struct vcd_property_hdr_type *p_property_hdr, + void *p_property_value); +static u32 ddl_get_enc_property(struct ddl_client_context_type *pddl, + struct vcd_property_hdr_type *p_property_hdr, + void *p_property_value); +static u32 ddl_set_enc_dynamic_property(struct ddl_encoder_data_type *p_encoder, + struct vcd_property_hdr_type + *p_property_hdr, + void *p_property_value); +static void ddl_set_default_enc_property(struct ddl_client_context_type *p_ddl); +static void ddl_set_default_enc_profile(struct ddl_encoder_data_type + *p_encoder); +static void ddl_set_default_enc_level(struct ddl_encoder_data_type *p_encoder); +static void ddl_set_default_enc_vop_timing(struct ddl_encoder_data_type + *p_encoder); +static void ddl_set_default_enc_intra_period(struct ddl_encoder_data_type + *p_encoder); +static void ddl_set_default_enc_rc_params(struct ddl_encoder_data_type + *p_encoder); +static u32 ddl_valid_buffer_requirement(struct vcd_buffer_requirement_type + *original_buf_req, + struct vcd_buffer_requirement_type + *req_buf_req); +static u32 ddl_decoder_min_num_dpb(struct ddl_decoder_data_type *p_decoder); +static u32 ddl_set_dec_buffers + (struct ddl_decoder_data_type *p_decoder, + struct ddl_property_dec_pic_buffers_type *p_dpb); + +u32 ddl_set_property(u32 *ddl_handle, + struct vcd_property_hdr_type *p_property_hdr, void *p_property_value) +{ + u32 vcd_status; + struct ddl_context_type *p_ddl_context; + struct ddl_client_context_type *p_ddl = + (struct ddl_client_context_type *)ddl_handle; + + if (!p_property_hdr || !p_property_value) { + VIDC_LOGERR_STRING("ddl_set_prop:Bad_argument"); + return VCD_ERR_ILLEGAL_PARM; + } + p_ddl_context = ddl_get_context(); + + if (!DDL_IS_INITIALIZED(p_ddl_context)) { + VIDC_LOGERR_STRING("ddl_set_prop:Not_inited"); + return VCD_ERR_ILLEGAL_OP; + } + + if (!p_ddl) { + VIDC_LOGERR_STRING("ddl_set_prop:Bad_handle"); + return VCD_ERR_BAD_HANDLE; + } + if (p_ddl->b_decoding) { + vcd_status = + ddl_set_dec_property(p_ddl, p_property_hdr, + p_property_value); + } else { + vcd_status = + ddl_set_enc_property(p_ddl, p_property_hdr, + p_property_value); + } + if (vcd_status) + VIDC_LOGERR_STRING("ddl_set_prop:FAILED"); + + return vcd_status; +} + +u32 ddl_get_property(u32 *ddl_handle, + struct vcd_property_hdr_type *p_property_hdr, void *p_property_value) +{ + + u32 vcd_status = VCD_ERR_ILLEGAL_PARM; + struct ddl_context_type *p_ddl_context; + struct ddl_client_context_type *p_ddl = + (struct ddl_client_context_type *)ddl_handle; + + if (!p_property_hdr || !p_property_value) + return VCD_ERR_ILLEGAL_PARM; + + if (p_property_hdr->prop_id == DDL_I_CAPABILITY) { + if (sizeof(struct ddl_property_capability_type) == + p_property_hdr->n_size) { + struct ddl_property_capability_type *p_ddl_capability = + (struct ddl_property_capability_type *) + p_property_value; + p_ddl_capability->n_max_num_client = VCD_MAX_NO_CLIENT; + p_ddl_capability->b_exclusive = + VCD_COMMAND_EXCLUSIVE; + p_ddl_capability->n_frame_command_depth = + VCD_FRAME_COMMAND_DEPTH; + p_ddl_capability->n_general_command_depth = + VCD_GENERAL_COMMAND_DEPTH; + p_ddl_capability->n_ddl_time_out_in_ms = + DDL_HW_TIMEOUT_IN_MS; + vcd_status = VCD_S_SUCCESS; + } + return vcd_status; + } + p_ddl_context = ddl_get_context(); + if (!DDL_IS_INITIALIZED(p_ddl_context)) + return VCD_ERR_ILLEGAL_OP; + + if (!p_ddl) + return VCD_ERR_BAD_HANDLE; + + if (p_ddl->b_decoding) { + vcd_status = + ddl_get_dec_property(p_ddl, p_property_hdr, + p_property_value); + } else { + vcd_status = + ddl_get_enc_property(p_ddl, p_property_hdr, + p_property_value); + } + if (vcd_status) + VIDC_LOGERR_STRING("ddl_get_prop:FAILED"); + + return vcd_status; +} + +u32 ddl_decoder_ready_to_start(struct ddl_client_context_type *p_ddl, + struct vcd_sequence_hdr_type *p_header) +{ + struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); + if (!p_decoder->codec_type.e_codec) { + VIDC_LOGERR_STRING("ddl_dec_start_check:Codec_not_set"); + return FALSE; + } + if ((!p_header) && + (!p_decoder->client_frame_size.n_height || + !p_decoder->client_frame_size.n_width) + ) { + VIDC_LOGERR_STRING + ("ddl_dec_start_check:Client_height_width_default"); + return FALSE; + } + return TRUE; +} + +u32 ddl_encoder_ready_to_start(struct ddl_client_context_type *p_ddl) +{ + struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); + + if (!p_encoder->codec_type.e_codec || + !p_encoder->frame_size.n_height || + !p_encoder->frame_size.n_width || + !p_encoder->frame_rate.n_fps_denominator || + !p_encoder->frame_rate.n_fps_numerator || + !p_encoder->target_bit_rate.n_target_bitrate) { + return FALSE; + } + return TRUE; +} + +static u32 ddl_set_dec_property + (struct ddl_client_context_type *p_ddl, + struct vcd_property_hdr_type *p_property_hdr, void *p_property_value) { + u32 vcd_status = VCD_ERR_ILLEGAL_PARM; + struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); + switch (p_property_hdr->prop_id) { + case DDL_I_DPB_RELEASE: + { + if (sizeof(struct ddl_frame_data_type_tag) == + p_property_hdr->n_size + && p_decoder->dp_buf.n_no_of_dec_pic_buf) { + vcd_status = + ddl_decoder_dpb_transact(p_decoder, + (struct ddl_frame_data_type_tag *) + p_property_value, + DDL_DPB_OP_MARK_FREE); + } + break; + } + case DDL_I_DPB: + { + struct ddl_property_dec_pic_buffers_type *p_dpb = + (struct ddl_property_dec_pic_buffers_type *) + p_property_value; + + if (sizeof(struct ddl_property_dec_pic_buffers_type) == + p_property_hdr->n_size && + (DDLCLIENT_STATE_IS + (p_ddl, DDL_CLIENT_WAIT_FOR_INITCODEC) + || DDLCLIENT_STATE_IS(p_ddl, + DDL_CLIENT_WAIT_FOR_DPB) + ) && + p_dpb->n_no_of_dec_pic_buf >= + p_decoder->client_output_buf_req.n_actual_count) { + vcd_status = + ddl_set_dec_buffers(p_decoder, p_dpb); + } + break; + } + case DDL_I_REQ_OUTPUT_FLUSH: + { + if (sizeof(u32) == p_property_hdr->n_size) { + p_decoder->n_dynamic_prop_change |= + DDL_DEC_REQ_OUTPUT_FLUSH; + p_decoder->dpb_mask.n_client_mask = 0; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case DDL_I_INPUT_BUF_REQ: + { + struct vcd_buffer_requirement_type *p_buffer_req = + (struct vcd_buffer_requirement_type *) + p_property_value; + if (sizeof(struct vcd_buffer_requirement_type) == + p_property_hdr->n_size && + (ddl_valid_buffer_requirement( + &p_decoder->min_input_buf_req, + p_buffer_req))) { + p_decoder->client_input_buf_req = *p_buffer_req; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case DDL_I_OUTPUT_BUF_REQ: + { + struct vcd_buffer_requirement_type *p_buffer_req = + (struct vcd_buffer_requirement_type *) + p_property_value; + if (sizeof(struct vcd_buffer_requirement_type) == + p_property_hdr->n_size && + (ddl_valid_buffer_requirement( + &p_decoder->min_output_buf_req, + p_buffer_req))) { + p_decoder->client_output_buf_req = + *p_buffer_req; + vcd_status = VCD_S_SUCCESS; + } + break; + } + + case VCD_I_CODEC: + { + struct vcd_property_codec_type *p_codec = + (struct vcd_property_codec_type *)p_property_value; + if (sizeof(struct vcd_property_codec_type) == + p_property_hdr->n_size + && DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_OPEN) + ) { + u32 b_return; + vcd_fw_transact(FALSE, TRUE, + p_decoder->codec_type.e_codec); + b_return = vcd_fw_transact(TRUE, TRUE, + p_codec->e_codec); + if (b_return) { + p_decoder->codec_type = *p_codec; + ddl_set_default_dec_property(p_ddl); + vcd_status = VCD_S_SUCCESS; + } else { + b_return = vcd_fw_transact(TRUE, TRUE, + p_decoder->codec_type.e_codec); + vcd_status = VCD_ERR_NOT_SUPPORTED; + } + } + break; + } + case VCD_I_POST_FILTER: + { + if (sizeof(struct vcd_property_post_filter_type) == + p_property_hdr->n_size + && DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_OPEN) && + (p_decoder->codec_type.e_codec == VCD_CODEC_MPEG4 || + p_decoder->codec_type.e_codec == VCD_CODEC_MPEG2) + ) { + p_decoder->post_filter = + *(struct vcd_property_post_filter_type *) + p_property_value; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_FRAME_SIZE: + { + struct vcd_property_frame_size_type *p_frame_size = + (struct vcd_property_frame_size_type *) + p_property_value; + + if ((sizeof(struct vcd_property_frame_size_type) == + p_property_hdr->n_size) && + (DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_OPEN))) { + if (p_decoder->client_frame_size.n_height != + p_frame_size->n_height + || p_decoder->client_frame_size.n_width != + p_frame_size->n_width) { + p_decoder->client_frame_size = + *p_frame_size; + ddl_calculate_stride( + &p_decoder->client_frame_size, + !p_decoder->n_progressive_only); + ddl_set_default_decoder_buffer_req + (p_decoder, TRUE); + } + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_BUFFER_FORMAT: + { + struct vcd_property_buffer_format_type *p_tile = + (struct vcd_property_buffer_format_type *) + p_property_value; + if (sizeof(struct vcd_property_buffer_format_type) == + p_property_hdr->n_size && + DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_OPEN) && + (p_tile->e_buffer_format == VCD_BUFFER_FORMAT_NV12 + || p_tile->e_buffer_format == + VCD_BUFFER_FORMAT_TILE_4x2) + ) { + if (p_tile->e_buffer_format != + p_decoder->buf_format.e_buffer_format) { + p_decoder->buf_format = *p_tile; + ddl_set_default_decoder_buffer_req + (p_decoder, TRUE); + } + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_METADATA_ENABLE: + case VCD_I_METADATA_HEADER: + { + vcd_status = ddl_set_metadata_params(p_ddl, + p_property_hdr, + p_property_value); + break; + } + default: + { + vcd_status = VCD_ERR_ILLEGAL_OP; + break; + } + } + return vcd_status; +} + +static u32 ddl_set_enc_property(struct ddl_client_context_type *p_ddl, + struct vcd_property_hdr_type *p_property_hdr, void *p_property_value) +{ + u32 vcd_status = VCD_ERR_ILLEGAL_PARM; + struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); + + if (DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME)) { + vcd_status = ddl_set_enc_dynamic_property(p_encoder, + p_property_hdr, p_property_value); + return vcd_status; + } + + if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_OPEN)) { + VIDC_LOGERR_STRING + ("ddl_set_enc_property:Fails_as_not_in_open_state"); + return VCD_ERR_ILLEGAL_OP; + } + + switch (p_property_hdr->prop_id) { + case VCD_I_TARGET_BITRATE: + { + struct vcd_property_target_bitrate_type *p_bitrate = + (struct vcd_property_target_bitrate_type *) + p_property_value; + if (sizeof(struct vcd_property_target_bitrate_type) == + p_property_hdr->n_size && + p_bitrate->n_target_bitrate) { + p_encoder->target_bit_rate = *p_bitrate; + vcd_status = VCD_S_SUCCESS; + } + break; + } + + case VCD_I_FRAME_RATE: + { + struct vcd_property_frame_rate_type *p_framerate = + (struct vcd_property_frame_rate_type *) + p_property_value; + if (sizeof(struct vcd_property_frame_rate_type) + == p_property_hdr->n_size && + p_framerate->n_fps_denominator && + p_framerate->n_fps_numerator) { + p_encoder->frame_rate = *p_framerate; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_FRAME_SIZE: + { + struct vcd_property_frame_size_type *p_framesize = + (struct vcd_property_frame_size_type *) + p_property_value; + + if ((sizeof(struct vcd_property_frame_size_type) + == p_property_hdr->n_size) && + (DDL_ALLOW_ENC_FRAMESIZE(p_framesize->n_width, + p_framesize->n_height)) + ) { + p_encoder->frame_size = *p_framesize; + ddl_calculate_stride(&p_encoder->frame_size, + FALSE); + ddl_set_default_encoder_buffer_req(p_encoder); + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_CODEC: + { + struct vcd_property_codec_type *p_codec = + (struct vcd_property_codec_type *) + p_property_value; + if (sizeof(struct vcd_property_codec_type) == + p_property_hdr->n_size) { + u32 b_return; + + vcd_fw_transact(FALSE, FALSE, + p_encoder->codec_type.e_codec); + + b_return = vcd_fw_transact(TRUE, FALSE, + p_codec->e_codec); + if (b_return) { + p_encoder->codec_type = *p_codec; + ddl_set_default_enc_property(p_ddl); + vcd_status = VCD_S_SUCCESS; + } else { + b_return = vcd_fw_transact(TRUE, FALSE, + p_encoder->codec_type.e_codec); + vcd_status = VCD_ERR_NOT_SUPPORTED; + } + } + break; + } + case VCD_I_REQ_IFRAME: + { + vcd_status = VCD_S_SUCCESS; + break; + } + case VCD_I_INTRA_PERIOD: + { + struct vcd_property_i_period_type *p_iperiod = + (struct vcd_property_i_period_type *) + p_property_value; + if ((sizeof(struct vcd_property_i_period_type) == + p_property_hdr->n_size) && + (!p_iperiod->n_b_frames)) { + p_encoder->i_period = *p_iperiod; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_PROFILE: + { + struct vcd_property_profile_type *p_profile = + (struct vcd_property_profile_type *) + p_property_value; + if ( + (sizeof(struct vcd_property_profile_type) == + p_property_hdr->n_size) && + ( + ( + (p_encoder->codec_type. + e_codec == VCD_CODEC_MPEG4) && + ( + p_profile->e_profile == VCD_PROFILE_MPEG4_SP + || p_profile->e_profile == + VCD_PROFILE_MPEG4_ASP + ) + ) || + ( + ( + (p_encoder->codec_type. + e_codec == VCD_CODEC_H264) && + (p_profile->e_profile >= + VCD_PROFILE_H264_BASELINE) + && (p_profile->e_profile <= + VCD_PROFILE_H264_HIGH) + ) + ) || + ( + (p_encoder->codec_type. + e_codec == VCD_CODEC_H263) && + (p_profile->e_profile == + VCD_PROFILE_H263_BASELINE) + ) + ) + ) { + p_encoder->profile = *p_profile; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_LEVEL: + { + struct vcd_property_level_type *p_level = + (struct vcd_property_level_type *) + p_property_value; + if ( + (sizeof(struct vcd_property_level_type) == + p_property_hdr->n_size + ) && + ( + ( + (p_encoder->codec_type. + e_codec == VCD_CODEC_MPEG4) && + (p_level->e_level >= VCD_LEVEL_MPEG4_0) && + (p_level->e_level <= VCD_LEVEL_MPEG4_6) + ) || + ( + (p_encoder->codec_type. + e_codec == VCD_CODEC_H264) && + (p_level->e_level >= VCD_LEVEL_H264_1) && + (p_level->e_level <= VCD_LEVEL_H264_3p1) + ) || + ( + (p_encoder->codec_type. + e_codec == VCD_CODEC_H263) && + (p_level->e_level >= VCD_LEVEL_H263_10) && + (p_level->e_level <= VCD_LEVEL_H263_70) + ) + ) + ) { + p_encoder->level = *p_level; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_MULTI_SLICE: + { + struct vcd_property_multi_slice_type *p_multislice = + (struct vcd_property_multi_slice_type *) + p_property_value; + switch (p_multislice->e_m_slice_sel) { + case VCD_MSLICE_OFF: + { + vcd_status = VCD_S_SUCCESS; + break; + } + case VCD_MSLICE_BY_GOB: + { + if (p_encoder->codec_type.e_codec == + VCD_CODEC_H263) + vcd_status = VCD_S_SUCCESS; + break; + } + case VCD_MSLICE_BY_MB_COUNT: + { + if (p_multislice->n_m_slice_size + >= 1 && (p_multislice-> + n_m_slice_size <= + (p_encoder->frame_size.n_height + * p_encoder->frame_size.n_width + / 16 / 16)) + ) { + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_MSLICE_BY_BYTE_COUNT: + { + if (p_multislice->n_m_slice_size < + DDL_MINIMUM_BYTE_PER_SLICE) { + vcd_status = VCD_S_SUCCESS; + break; + } + } + default: + { + break; + } + } + if (sizeof(struct vcd_property_multi_slice_type) == + p_property_hdr->n_size && + !vcd_status) { + p_encoder->multi_slice = *p_multislice; + } + break; + } + case VCD_I_RATE_CONTROL: + { + struct vcd_property_rate_control_type + *p_ratecontrol_type = + (struct vcd_property_rate_control_type *) + p_property_value; + if (sizeof(struct vcd_property_rate_control_type) == + p_property_hdr->n_size && + p_ratecontrol_type-> + e_rate_control >= VCD_RATE_CONTROL_OFF && + p_ratecontrol_type-> + e_rate_control <= VCD_RATE_CONTROL_CBR_CFR + ) { + p_encoder->rc_type = *p_ratecontrol_type; + ddl_set_default_enc_rc_params(p_encoder); + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_SHORT_HEADER: + { + + if (sizeof(struct vcd_property_short_header_type) == + p_property_hdr->n_size && + p_encoder->codec_type.e_codec == VCD_CODEC_MPEG4) { + p_encoder->short_header = + *(struct vcd_property_short_header_type *) + p_property_value; + vcd_status = VCD_S_SUCCESS; + } + + break; + } + case VCD_I_VOP_TIMING: + { + struct vcd_property_vop_timing_type *p_voptime = + (struct vcd_property_vop_timing_type *) + p_property_value; + if ( + (sizeof(struct vcd_property_vop_timing_type) == + p_property_hdr->n_size + ) && + (p_encoder->frame_rate.n_fps_numerator <= + p_voptime->n_vop_time_resolution) + ) { + p_encoder->vop_timing = *p_voptime; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_HEADER_EXTENSION: + { + if (sizeof(u32) == p_property_hdr->n_size && + p_encoder->codec_type.e_codec == VCD_CODEC_MPEG4 + ) { + p_encoder->n_hdr_ext_control = *(u32 *) + p_property_value; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_ENTROPY_CTRL: + { + struct vcd_property_entropy_control_type + *p_entropy_control = + (struct vcd_property_entropy_control_type *) + p_property_value; + if (sizeof(struct vcd_property_entropy_control_type) == + p_property_hdr->n_size && + p_encoder->codec_type.e_codec == VCD_CODEC_H264 + && p_entropy_control-> + e_entropy_sel >= VCD_ENTROPY_SEL_CAVLC && + p_entropy_control->e_entropy_sel <= + VCD_ENTROPY_SEL_CABAC) { + p_encoder->entropy_control = *p_entropy_control; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_DEBLOCKING: + { + struct vcd_property_db_config_type *p_dbconfig = + (struct vcd_property_db_config_type *) + p_property_value; + if (sizeof(struct vcd_property_db_config_type) == + p_property_hdr->n_size && + p_encoder->codec_type.e_codec == VCD_CODEC_H264 + && p_dbconfig->e_db_config >= + VCD_DB_ALL_BLOCKING_BOUNDARY + && p_dbconfig->e_db_config <= + VCD_DB_SKIP_SLICE_BOUNDARY + ) { + p_encoder->db_control = *p_dbconfig; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_QP_RANGE: + { + struct vcd_property_qp_range_type *p_qp = + (struct vcd_property_qp_range_type *) + p_property_value; + if ((sizeof(struct vcd_property_qp_range_type) == + p_property_hdr->n_size) && + (p_qp->n_min_qp <= p_qp->n_max_qp) && + ( + (p_encoder->codec_type.e_codec == VCD_CODEC_H264 + && p_qp->n_max_qp <= DDL_MAX_H264_QP) || + (p_qp->n_max_qp <= DDL_MAX_MPEG4_QP) + ) + ) { + p_encoder->qp_range = *p_qp; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_SESSION_QP: + { + struct vcd_property_session_qp_type *p_qp = + (struct vcd_property_session_qp_type *) + p_property_value; + + if ((sizeof(struct vcd_property_session_qp_type) == + p_property_hdr->n_size) && + (p_qp->n_i_frame_qp >= p_encoder->qp_range.n_min_qp) && + (p_qp->n_i_frame_qp <= p_encoder->qp_range.n_max_qp) && + (p_qp->n_p_frame_qp >= p_encoder->qp_range.n_min_qp) && + (p_qp->n_p_frame_qp <= p_encoder->qp_range.n_max_qp) + ) { + p_encoder->session_qp = *p_qp; + vcd_status = VCD_S_SUCCESS; + } + + break; + } + case VCD_I_RC_LEVEL_CONFIG: + { + struct vcd_property_rc_level_type *p_rc_level = + (struct vcd_property_rc_level_type *) + p_property_value; + if (sizeof(struct vcd_property_rc_level_type) == + p_property_hdr->n_size && + ( + p_encoder->rc_type. + e_rate_control >= VCD_RATE_CONTROL_VBR_VFR || + p_encoder->rc_type. + e_rate_control <= VCD_RATE_CONTROL_CBR_VFR + ) && + (!p_rc_level->b_mb_level_rc || + p_encoder->codec_type.e_codec == VCD_CODEC_H264 + ) + ) { + p_encoder->rc_level = *p_rc_level; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_FRAME_LEVEL_RC: + { + + struct vcd_property_frame_level_rc_params_type + *p_frame_levelrc = + (struct vcd_property_frame_level_rc_params_type *) + p_property_value; + + if ((sizeof(struct + vcd_property_frame_level_rc_params_type) + == p_property_hdr->n_size) && + (p_frame_levelrc->n_reaction_coeff) && + (p_encoder->rc_level.b_frame_level_rc) + ) { + p_encoder->frame_level_rc = *p_frame_levelrc; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_ADAPTIVE_RC: + { + + if ((sizeof(struct + vcd_property_adaptive_rc_params_type) + == p_property_hdr->n_size) && + (p_encoder->codec_type. + e_codec == VCD_CODEC_H264) && + (p_encoder->rc_level.b_mb_level_rc)) { + + p_encoder->adaptive_rc = + *(struct vcd_property_adaptive_rc_params_type *) + p_property_value; + + vcd_status = VCD_S_SUCCESS; + } + + break; + } + case VCD_I_INTRA_REFRESH: + { + + struct vcd_property_intra_refresh_mb_number_type + *p_intra_refresh_mbnum = + (struct vcd_property_intra_refresh_mb_number_type *) + p_property_value; + + u32 n_frame_mbnum = + (p_encoder->frame_size.n_width / 16) * + (p_encoder->frame_size.n_height / 16); + if (sizeof(struct + vcd_property_intra_refresh_mb_number_type) + == p_property_hdr->n_size && + p_intra_refresh_mbnum->n_cir_mb_number <= + n_frame_mbnum) { + p_encoder->intra_refresh = + *p_intra_refresh_mbnum; + vcd_status = VCD_S_SUCCESS; + } + + break; + } + case VCD_I_BUFFER_FORMAT: + { + struct vcd_property_buffer_format_type *p_tile = + (struct vcd_property_buffer_format_type *) + p_property_value; + if (sizeof(struct vcd_property_buffer_format_type) == + p_property_hdr->n_size && + p_tile->e_buffer_format == + VCD_BUFFER_FORMAT_NV12) { + p_encoder->buf_format = *p_tile; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case DDL_I_INPUT_BUF_REQ: + { + struct vcd_buffer_requirement_type *p_buffer_req = + (struct vcd_buffer_requirement_type *) + p_property_value; + if (sizeof(struct vcd_buffer_requirement_type) == + p_property_hdr->n_size && + (ddl_valid_buffer_requirement( + &p_encoder->input_buf_req, p_buffer_req)) + ) { + p_encoder->client_input_buf_req = *p_buffer_req; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case DDL_I_OUTPUT_BUF_REQ: + { + struct vcd_buffer_requirement_type *p_buffer_req = + (struct vcd_buffer_requirement_type *) + p_property_value; + if (sizeof(struct vcd_buffer_requirement_type) == + p_property_hdr->n_size && + (ddl_valid_buffer_requirement( + &p_encoder->output_buf_req, p_buffer_req)) + ) { + p_encoder->client_output_buf_req = + *p_buffer_req; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_METADATA_ENABLE: + case VCD_I_METADATA_HEADER: + { + vcd_status = ddl_set_metadata_params( + p_ddl, p_property_hdr, p_property_value); + break; + } + default: + { + vcd_status = VCD_ERR_ILLEGAL_OP; + break; + } + } + return vcd_status; +} + +static u32 ddl_get_dec_property + (struct ddl_client_context_type *p_ddl, + struct vcd_property_hdr_type *p_property_hdr, void *p_property_value) { + u32 vcd_status = VCD_ERR_ILLEGAL_PARM; + struct ddl_decoder_data_type *p_decoder = &p_ddl->codec_data.decoder; + + switch (p_property_hdr->prop_id) { + case VCD_I_FRAME_SIZE: + { + if (sizeof(struct vcd_property_frame_size_type) == + p_property_hdr->n_size) { + if (p_decoder->client_frame_size.n_width) { + *(struct vcd_property_frame_size_type *) + p_property_value = + p_decoder->client_frame_size; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; + } + } + break; + } + case VCD_I_PROFILE: + { + if (sizeof(struct vcd_property_profile_type) == + p_property_hdr->n_size) { + *(struct vcd_property_profile_type *) + p_property_value = p_decoder->profile; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_LEVEL: + { + if (sizeof(struct vcd_property_level_type) == + p_property_hdr->n_size) { + *(struct vcd_property_level_type *) + p_property_value = p_decoder->level; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_PROGRESSIVE_ONLY: + { + if (sizeof(u32) == p_property_hdr->n_size) { + *(u32 *) p_property_value = + p_decoder->n_progressive_only; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case DDL_I_INPUT_BUF_REQ: + { + if (sizeof(struct vcd_buffer_requirement_type) == + p_property_hdr->n_size) { + if (p_decoder-> + client_input_buf_req.n_size) { + *(struct vcd_buffer_requirement_type *) + p_property_value = + p_decoder->client_input_buf_req; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; + } + } + break; + } + case DDL_I_OUTPUT_BUF_REQ: + { + if (sizeof(struct vcd_buffer_requirement_type) == + p_property_hdr->n_size) { + if (p_decoder->client_output_buf_req.n_size) { + *(struct vcd_buffer_requirement_type *) + p_property_value = + p_decoder->client_output_buf_req; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; + } + } + break; + } + case VCD_I_CODEC: + { + if (sizeof(struct vcd_property_codec_type) == + p_property_hdr->n_size) { + if (p_decoder->codec_type.e_codec) { + *(struct vcd_property_codec_type *) + p_property_value = + p_decoder->codec_type; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; + } + } + break; + } + case VCD_I_BUFFER_FORMAT: + { + if (sizeof(struct vcd_property_buffer_format_type) == + p_property_hdr->n_size) { + *(struct vcd_property_buffer_format_type *) + p_property_value = p_decoder->buf_format; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_POST_FILTER: + { + if (sizeof(struct vcd_property_post_filter_type) == + p_property_hdr->n_size) { + *(struct vcd_property_post_filter_type *) + p_property_value = p_decoder->post_filter; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case DDL_I_SEQHDR_ALIGN_BYTES: + { + if (sizeof(u32) == p_property_hdr->n_size) { + *(u32 *) p_property_value = + DDL_LINEAR_BUFFER_ALIGN_BYTES; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case DDL_I_FRAME_PROC_UNITS: + { + if (sizeof(u32) == p_property_hdr->n_size && + p_decoder->client_frame_size.n_width && + p_decoder->client_frame_size.n_height) { + *(u32 *) p_property_value = + ((p_decoder->client_frame_size. + n_width >> 4) * + (p_decoder->client_frame_size. + n_height >> 4) + ); + vcd_status = VCD_S_SUCCESS; + } + break; + } + case DDL_I_DPB_RETRIEVE: + { + if (sizeof(struct ddl_frame_data_type_tag) == + p_property_hdr->n_size) { + vcd_status = + ddl_decoder_dpb_transact(p_decoder, + (struct ddl_frame_data_type_tag *) + p_property_value, + DDL_DPB_OP_RETRIEVE); + } + break; + } + case VCD_I_METADATA_ENABLE: + case VCD_I_METADATA_HEADER: + { + vcd_status = ddl_get_metadata_params( + p_ddl, + p_property_hdr, + p_property_value); + break; + } + default: + { + vcd_status = VCD_ERR_ILLEGAL_OP; + break; + } + } + return vcd_status; +} + +static u32 ddl_get_enc_property + (struct ddl_client_context_type *p_ddl, + struct vcd_property_hdr_type *p_property_hdr, void *p_property_value) { + u32 vcd_status = VCD_ERR_ILLEGAL_PARM; + struct ddl_encoder_data_type *p_encoder = &p_ddl->codec_data.encoder; + + struct vcd_property_entropy_control_type *entropy_control; + struct vcd_property_intra_refresh_mb_number_type *intra_refresh; + + switch (p_property_hdr->prop_id) { + case VCD_I_CODEC: + { + if (sizeof(struct vcd_property_codec_type) == + p_property_hdr->n_size) { + *(struct vcd_property_codec_type *) + p_property_value = + p_encoder->codec_type; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_FRAME_SIZE: + { + if (sizeof(struct vcd_property_frame_size_type) == + p_property_hdr->n_size) { + *(struct vcd_property_frame_size_type *) + p_property_value = + p_encoder->frame_size; + + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_FRAME_RATE: + { + if (sizeof(struct vcd_property_frame_rate_type) == + p_property_hdr->n_size) { + + *(struct vcd_property_frame_rate_type *) + p_property_value = + p_encoder->frame_rate; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_TARGET_BITRATE: + { + + if (sizeof(struct vcd_property_target_bitrate_type) == + p_property_hdr->n_size) { + *(struct vcd_property_target_bitrate_type *) + p_property_value = + p_encoder->target_bit_rate; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_RATE_CONTROL: + { + if (sizeof(struct vcd_property_rate_control_type) == + p_property_hdr->n_size) { + *(struct vcd_property_rate_control_type *) + p_property_value = p_encoder->rc_type; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_PROFILE: + { + if (sizeof(struct vcd_property_profile_type) == + p_property_hdr->n_size) { + *(struct vcd_property_profile_type *) + p_property_value = p_encoder->profile; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_LEVEL: + { + if (sizeof(struct vcd_property_level_type) == + p_property_hdr->n_size) { + *(struct vcd_property_level_type *) + p_property_value = p_encoder->level; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_MULTI_SLICE: + { + if (sizeof(struct vcd_property_multi_slice_type) == + p_property_hdr->n_size) { + *(struct vcd_property_multi_slice_type *) + p_property_value = p_encoder->multi_slice; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_SEQ_HEADER: + { + struct vcd_sequence_hdr_type *p_seq_hdr = + (struct vcd_sequence_hdr_type *)p_property_value; + if (p_encoder->seq_header.n_buffer_size && + sizeof(struct vcd_sequence_hdr_type) == + p_property_hdr->n_size + && p_encoder->seq_header.n_buffer_size <= + p_seq_hdr->n_sequence_header_len) { + DDL_MEMCPY(p_seq_hdr->p_sequence_header, + p_encoder->seq_header. + p_align_virtual_addr, + p_encoder->seq_header.n_buffer_size); + p_seq_hdr->n_sequence_header_len = + p_encoder->seq_header.n_buffer_size; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case DDL_I_SEQHDR_PRESENT: + { + if (sizeof(u32) == p_property_hdr->n_size) { + if ((p_encoder->codec_type. + e_codec == VCD_CODEC_MPEG4 && + !p_encoder->short_header.b_short_header) + || p_encoder->codec_type.e_codec == + VCD_CODEC_H264) { + *(u32 *)p_property_value = 0x1; + } else { + *(u32 *)p_property_value = 0x0; + } + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_VOP_TIMING: + { + if (sizeof(struct vcd_property_vop_timing_type) == + p_property_hdr->n_size) { + *(struct vcd_property_vop_timing_type *) + p_property_value = p_encoder->vop_timing; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_SHORT_HEADER: + { + if (sizeof(struct vcd_property_short_header_type) == + p_property_hdr->n_size) { + if (p_encoder->codec_type.e_codec == + VCD_CODEC_MPEG4) { + *(struct vcd_property_short_header_type + *)p_property_value = + p_encoder->short_header; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; + } + } + break; + } + case VCD_I_ENTROPY_CTRL: + { + entropy_control = p_property_value; + if (sizeof(struct vcd_property_entropy_control_type) == + p_property_hdr->n_size) { + if (p_encoder->codec_type.e_codec == + VCD_CODEC_H264) { + *entropy_control = + p_encoder->entropy_control; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; + } + } + break; + } + case VCD_I_DEBLOCKING: + { + if (sizeof(struct vcd_property_db_config_type) == + p_property_hdr->n_size) { + if (p_encoder->codec_type.e_codec == + VCD_CODEC_H264) { + *(struct vcd_property_db_config_type *) + p_property_value = + p_encoder->db_control; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; + } + } + break; + } + case VCD_I_INTRA_PERIOD: + { + if (sizeof(struct vcd_property_i_period_type) == + p_property_hdr->n_size) { + *(struct vcd_property_i_period_type *) + p_property_value = p_encoder->i_period; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_QP_RANGE: + { + if (sizeof(struct vcd_property_qp_range_type) == + p_property_hdr->n_size) { + *(struct vcd_property_qp_range_type *) + p_property_value = p_encoder->qp_range; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_SESSION_QP: + { + if (sizeof(struct vcd_property_session_qp_type) == + p_property_hdr->n_size) { + *(struct vcd_property_session_qp_type *) + p_property_value = p_encoder->session_qp; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_RC_LEVEL_CONFIG: + { + if (sizeof(struct vcd_property_rc_level_type) == + p_property_hdr->n_size) { + *(struct vcd_property_rc_level_type *) + p_property_value = p_encoder->rc_level; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_FRAME_LEVEL_RC: + { + if (sizeof + (struct vcd_property_frame_level_rc_params_type) == + p_property_hdr->n_size) { + *(struct vcd_property_frame_level_rc_params_type + *)p_property_value = + p_encoder->frame_level_rc; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_ADAPTIVE_RC: + { + if (sizeof(struct vcd_property_adaptive_rc_params_type) + == p_property_hdr->n_size) { + *(struct vcd_property_adaptive_rc_params_type *) + p_property_value = p_encoder->adaptive_rc; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_INTRA_REFRESH: + { + intra_refresh = p_property_value; + if (sizeof + (struct vcd_property_intra_refresh_mb_number_type) + == p_property_hdr->n_size) { + *intra_refresh = p_encoder->intra_refresh; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case DDL_I_INPUT_BUF_REQ: + { + if (sizeof(struct vcd_buffer_requirement_type) == + p_property_hdr->n_size) { + if (p_encoder->output_buf_req.n_size) { + *(struct vcd_buffer_requirement_type *) + p_property_value = + p_encoder->client_input_buf_req; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; + } + } + break; + } + case DDL_I_OUTPUT_BUF_REQ: + { + if (sizeof(struct vcd_buffer_requirement_type) == + p_property_hdr->n_size) { + if (p_encoder->output_buf_req.n_size) { + *(struct vcd_buffer_requirement_type *) + p_property_value = + p_encoder->client_output_buf_req; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; + } + } + break; + } + case VCD_I_BUFFER_FORMAT: + { + if (sizeof(struct vcd_property_buffer_format_type) == + p_property_hdr->n_size) { + *(struct vcd_property_buffer_format_type *) + p_property_value = p_encoder->buf_format; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case DDL_I_FRAME_PROC_UNITS: + { + if (sizeof(u32) == p_property_hdr->n_size && + p_encoder->frame_size.n_width && + p_encoder->frame_size.n_height) { + *(u32 *) p_property_value = + ((p_encoder->frame_size.n_width >> 4) * + (p_encoder->frame_size.n_height >> 4) + ); + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_HEADER_EXTENSION: + { + if (sizeof(u32) == p_property_hdr->n_size && + p_encoder->codec_type.e_codec == VCD_CODEC_MPEG4) { + *(u32 *) p_property_value = + p_encoder->n_hdr_ext_control; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_METADATA_ENABLE: + case VCD_I_METADATA_HEADER: + { + vcd_status = ddl_get_metadata_params( + p_ddl, + p_property_hdr, + p_property_value); + break; + } + default: + { + vcd_status = VCD_ERR_ILLEGAL_OP; + break; + } + } + return vcd_status; +} + +static u32 ddl_set_enc_dynamic_property + (struct ddl_encoder_data_type *p_encoder, + struct vcd_property_hdr_type *p_property_hdr, void *p_property_value) { + u32 vcd_status = VCD_ERR_ILLEGAL_PARM; + switch (p_property_hdr->prop_id) { + case VCD_I_REQ_IFRAME: + { + if (sizeof(struct vcd_property_req_i_frame_type) == + p_property_hdr->n_size) { + p_encoder->n_dynamic_prop_change |= + DDL_ENC_REQ_IFRAME; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_TARGET_BITRATE: + { + if (sizeof(struct vcd_property_target_bitrate_type) == + p_property_hdr->n_size) { + p_encoder->target_bit_rate = + *(struct vcd_property_target_bitrate_type *) + p_property_value; + p_encoder->n_dynamic_prop_change |= + DDL_ENC_CHANGE_BITRATE; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_INTRA_PERIOD: + { + struct vcd_property_i_period_type *p_iperiod = + (struct vcd_property_i_period_type *) + p_property_value; + if (sizeof(struct vcd_property_i_period_type) == + p_property_hdr->n_size && + !p_iperiod->n_b_frames) { + p_encoder->i_period = *p_iperiod; + p_encoder->n_dynamic_prop_change |= + DDL_ENC_CHANGE_IPERIOD; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_FRAME_RATE: + { + struct vcd_property_frame_rate_type *p_frame_rate = + (struct vcd_property_frame_rate_type *) + p_property_value; + if (sizeof(struct vcd_property_frame_rate_type) + == p_property_hdr->n_size && + p_frame_rate->n_fps_denominator && + p_frame_rate->n_fps_numerator && + p_frame_rate->n_fps_denominator <= + p_frame_rate->n_fps_numerator) { + p_encoder->frame_rate = *p_frame_rate; + p_encoder->n_dynamic_prop_change |= + DDL_ENC_CHANGE_FRAMERATE; + vcd_status = VCD_S_SUCCESS; + } + break; + } + default: + { + vcd_status = VCD_ERR_ILLEGAL_OP; + break; + } + } + return vcd_status; +} + +void ddl_set_default_dec_property(struct ddl_client_context_type *p_ddl) +{ + struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); + + if (p_decoder->codec_type.e_codec == VCD_CODEC_MPEG4 || + p_decoder->codec_type.e_codec == VCD_CODEC_MPEG2) { + p_decoder->post_filter.b_post_filter = TRUE; + } else { + p_decoder->post_filter.b_post_filter = FALSE; + } + p_decoder->buf_format.e_buffer_format = VCD_BUFFER_FORMAT_NV12; + p_decoder->client_frame_size.n_height = 144; + p_decoder->client_frame_size.n_width = 176; + p_decoder->client_frame_size.n_stride = 176; + p_decoder->client_frame_size.n_scan_lines = 144; + p_decoder->n_progressive_only = 1; + ddl_set_default_metadata_flag(p_ddl); + + ddl_set_default_decoder_buffer_req(p_decoder, TRUE); + +} + +static void ddl_set_default_enc_property(struct ddl_client_context_type *p_ddl) +{ + struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); + + ddl_set_default_enc_profile(p_encoder); + ddl_set_default_enc_level(p_encoder); + + p_encoder->rc_type.e_rate_control = VCD_RATE_CONTROL_VBR_VFR; + ddl_set_default_enc_rc_params(p_encoder); + + ddl_set_default_enc_intra_period(p_encoder); + + p_encoder->intra_refresh.n_cir_mb_number = 0; + ddl_set_default_enc_vop_timing(p_encoder); + + p_encoder->multi_slice.n_m_slice_size = VCD_MSLICE_OFF; + p_encoder->short_header.b_short_header = FALSE; + + p_encoder->entropy_control.e_entropy_sel = VCD_ENTROPY_SEL_CAVLC; + p_encoder->entropy_control.e_cabac_model = VCD_CABAC_MODEL_NUMBER_0; + p_encoder->db_control.e_db_config = VCD_DB_ALL_BLOCKING_BOUNDARY; + p_encoder->db_control.n_slice_alpha_offset = 0; + p_encoder->db_control.n_slice_beta_offset = 0; + + p_encoder->re_con_buf_format.e_buffer_format = + VCD_BUFFER_FORMAT_TILE_4x2; + + p_encoder->buf_format.e_buffer_format = VCD_BUFFER_FORMAT_NV12; + + p_encoder->n_hdr_ext_control = 0; + + ddl_set_default_metadata_flag(p_ddl); + + ddl_set_default_encoder_buffer_req(p_encoder); +} + +static void ddl_set_default_enc_profile(struct ddl_encoder_data_type *p_encoder) +{ + enum vcd_codec_type e_codec = p_encoder->codec_type.e_codec; + if (e_codec == VCD_CODEC_MPEG4) + p_encoder->profile.e_profile = VCD_PROFILE_MPEG4_SP; + else if (e_codec == VCD_CODEC_H264) + p_encoder->profile.e_profile = VCD_PROFILE_H264_BASELINE; + else + p_encoder->profile.e_profile = VCD_PROFILE_H263_BASELINE; +} + +static void ddl_set_default_enc_level(struct ddl_encoder_data_type *p_encoder) +{ + enum vcd_codec_type e_codec = p_encoder->codec_type.e_codec; + if (e_codec == VCD_CODEC_MPEG4) + p_encoder->level.e_level = VCD_LEVEL_MPEG4_1; + else if (e_codec == VCD_CODEC_H264) + p_encoder->level.e_level = VCD_LEVEL_H264_1; + else + p_encoder->level.e_level = VCD_LEVEL_H263_10; +} + +static void ddl_set_default_enc_vop_timing + (struct ddl_encoder_data_type *p_encoder) +{ + p_encoder->vop_timing.n_vop_time_resolution = + (2 * p_encoder->frame_rate.n_fps_numerator) / + p_encoder->frame_rate.n_fps_denominator; +} + +static void ddl_set_default_enc_intra_period( + struct ddl_encoder_data_type *p_encoder) +{ + switch (p_encoder->rc_type.e_rate_control) { + default: + case VCD_RATE_CONTROL_VBR_VFR: + case VCD_RATE_CONTROL_VBR_CFR: + case VCD_RATE_CONTROL_CBR_VFR: + case VCD_RATE_CONTROL_OFF: + { + p_encoder->i_period.n_p_frames = + ((p_encoder->frame_rate.n_fps_numerator << 1) / + p_encoder->frame_rate.n_fps_denominator) - 1; + break; + } + case VCD_RATE_CONTROL_CBR_CFR: + { + p_encoder->i_period.n_p_frames = + ((p_encoder->frame_rate.n_fps_numerator >> 1) / + p_encoder->frame_rate.n_fps_denominator) - 1; + break; + } + } + p_encoder->i_period.n_b_frames = 0; +} + +static void ddl_set_default_enc_rc_params( + struct ddl_encoder_data_type *p_encoder) +{ + enum vcd_codec_type e_codec = p_encoder->codec_type.e_codec; + + p_encoder->rc_level.b_frame_level_rc = TRUE; + p_encoder->qp_range.n_min_qp = 0x1; + + if (e_codec == VCD_CODEC_H264) { + p_encoder->qp_range.n_max_qp = 0x33; + p_encoder->session_qp.n_i_frame_qp = 0x19; + p_encoder->session_qp.n_p_frame_qp = 0x19; + + p_encoder->rc_level.b_mb_level_rc = TRUE; + p_encoder->adaptive_rc.b_activity_region_flag = TRUE; + p_encoder->adaptive_rc.b_dark_region_as_flag = TRUE; + p_encoder->adaptive_rc.b_smooth_region_as_flag = TRUE; + p_encoder->adaptive_rc.b_static_region_as_flag = TRUE; + } else { + p_encoder->qp_range.n_max_qp = 0x1f; + p_encoder->session_qp.n_i_frame_qp = 0x14; + p_encoder->session_qp.n_p_frame_qp = 0x14; + p_encoder->rc_level.b_mb_level_rc = FALSE; + } + + switch (p_encoder->rc_type.e_rate_control) { + default: + case VCD_RATE_CONTROL_VBR_VFR: + { + p_encoder->n_r_cframe_skip = 1; + p_encoder->frame_level_rc.n_reaction_coeff = 0x1f4; + break; + } + case VCD_RATE_CONTROL_VBR_CFR: + { + p_encoder->n_r_cframe_skip = 0; + p_encoder->frame_level_rc.n_reaction_coeff = 0x1f4; + break; + } + case VCD_RATE_CONTROL_CBR_VFR: + { + p_encoder->n_r_cframe_skip = 1; + if (e_codec != VCD_CODEC_H264) { + p_encoder->session_qp.n_i_frame_qp = 0xf; + p_encoder->session_qp.n_p_frame_qp = 0xf; + } + + p_encoder->frame_level_rc.n_reaction_coeff = 0x6; + break; + } + case VCD_RATE_CONTROL_CBR_CFR: + { + p_encoder->n_r_cframe_skip = 0; + p_encoder->frame_level_rc.n_reaction_coeff = 0x6; + break; + } + case VCD_RATE_CONTROL_OFF: + { + p_encoder->n_r_cframe_skip = 0; + p_encoder->rc_level.b_frame_level_rc = FALSE; + p_encoder->rc_level.b_mb_level_rc = FALSE; + break; + } + } +} + +void ddl_set_default_encoder_buffer_req(struct ddl_encoder_data_type *p_encoder) +{ + u32 n_y_cb_cr_size; + + n_y_cb_cr_size = ddl_get_yuv_buffer_size(&p_encoder->frame_size, + &p_encoder->buf_format, FALSE); + + memset(&p_encoder->input_buf_req, 0, + sizeof(struct vcd_buffer_requirement_type)); + + p_encoder->input_buf_req.n_min_count = 1; + p_encoder->input_buf_req.n_actual_count = + p_encoder->input_buf_req.n_min_count; + p_encoder->input_buf_req.n_max_count = DDL_MAX_BUFFER_COUNT; + p_encoder->input_buf_req.n_size = n_y_cb_cr_size; + p_encoder->input_buf_req.n_align = DDL_LINEAR_BUFFER_ALIGN_BYTES; + + p_encoder->client_input_buf_req = p_encoder->input_buf_req; + + memset(&p_encoder->output_buf_req, 0, + sizeof(struct vcd_buffer_requirement_type)); + + p_encoder->output_buf_req.n_min_count = 2; + p_encoder->output_buf_req.n_actual_count = + p_encoder->output_buf_req.n_min_count; + p_encoder->output_buf_req.n_max_count = DDL_MAX_BUFFER_COUNT; + p_encoder->output_buf_req.n_align = DDL_LINEAR_BUFFER_ALIGN_BYTES; + p_encoder->output_buf_req.n_size = n_y_cb_cr_size; + ddl_set_default_encoder_metadata_buffer_size(p_encoder); + p_encoder->client_output_buf_req = p_encoder->output_buf_req; +} + +void ddl_set_default_decoder_buffer_req(struct ddl_decoder_data_type *p_decoder, + u32 b_estimate) +{ + u32 n_y_cb_cr_size, n_min_dpb; + struct vcd_property_frame_size_type *p_frame_size; + struct vcd_buffer_requirement_type *p_output_buf_req, *p_input_buf_req; + + if (!p_decoder->codec_type.e_codec) + return; + + if (b_estimate) { + p_frame_size = &p_decoder->client_frame_size; + p_output_buf_req = &p_decoder->client_output_buf_req; + p_input_buf_req = &p_decoder->client_input_buf_req; + n_min_dpb = ddl_decoder_min_num_dpb(p_decoder); + n_y_cb_cr_size = ddl_get_yuv_buffer_size(p_frame_size, + &p_decoder->buf_format, + (!p_decoder->n_progressive_only)); + } else { + p_frame_size = &p_decoder->frame_size; + p_output_buf_req = &p_decoder->actual_output_buf_req; + p_input_buf_req = &p_decoder->actual_input_buf_req; + n_y_cb_cr_size = p_decoder->n_y_cb_cr_size; + n_min_dpb = p_decoder->n_min_dpb_num; + } + + memset(p_output_buf_req, 0, sizeof(struct vcd_buffer_requirement_type)); + + p_output_buf_req->n_min_count = n_min_dpb; + p_output_buf_req->n_actual_count = p_output_buf_req->n_min_count; + p_output_buf_req->n_max_count = DDL_MAX_BUFFER_COUNT; + p_output_buf_req->n_size = n_y_cb_cr_size; + if (p_decoder->buf_format.e_buffer_format != VCD_BUFFER_FORMAT_NV12) + p_output_buf_req->n_align = DDL_TILE_BUFFER_ALIGN_BYTES; + else + p_output_buf_req->n_align = DDL_LINEAR_BUFFER_ALIGN_BYTES; + + ddl_set_default_decoder_metadata_buffer_size(p_decoder, + p_frame_size, p_output_buf_req); + + p_decoder->min_output_buf_req = *p_output_buf_req; + + memset(p_input_buf_req, 0, sizeof(struct vcd_buffer_requirement_type)); + + p_input_buf_req->n_min_count = 1; + p_input_buf_req->n_actual_count = p_input_buf_req->n_min_count; + p_input_buf_req->n_max_count = DDL_MAX_BUFFER_COUNT; + p_input_buf_req->n_size = n_y_cb_cr_size; + + if (p_input_buf_req->n_size >= ((1280*720*3) >> 1)) + p_input_buf_req->n_size = (p_input_buf_req->n_size >> 1); + + p_input_buf_req->n_align = DDL_LINEAR_BUFFER_ALIGN_BYTES; + + p_decoder->min_input_buf_req = *p_input_buf_req; + +} + +u32 ddl_get_yuv_buffer_size(struct vcd_property_frame_size_type *p_frame_size, + struct vcd_property_buffer_format_type *p_buf_format, u32 inter_lace) +{ + u32 n_width = p_frame_size->n_stride; + u32 n_height = p_frame_size->n_scan_lines; + u32 n_total_memory_size; + + if (p_buf_format->e_buffer_format != VCD_BUFFER_FORMAT_NV12) { + u32 n_component_mem_size; + u32 n_width_round_up; + u32 n_height_round_up; + u32 n_height_chroma = (n_height >> 1); + + n_width_round_up = + DDL_TILE_ALIGN(n_width, DDL_TILE_ALIGN_WIDTH); + n_height_round_up = + DDL_TILE_ALIGN(n_height, DDL_TILE_ALIGN_HEIGHT); + + n_component_mem_size = n_width_round_up * n_height_round_up; + n_component_mem_size = DDL_TILE_ALIGN(n_component_mem_size, + DDL_TILE_MULTIPLY_FACTOR); + + n_total_memory_size = ((n_component_mem_size + + DDL_TILE_BUF_ALIGN_GUARD_BYTES) & + DDL_TILE_BUF_ALIGN_MASK); + + n_height_round_up = + DDL_TILE_ALIGN(n_height_chroma, DDL_TILE_ALIGN_HEIGHT); + n_component_mem_size = n_width_round_up * n_height_round_up; + n_component_mem_size = DDL_TILE_ALIGN(n_component_mem_size, + DDL_TILE_MULTIPLY_FACTOR); + n_total_memory_size += n_component_mem_size; + } else { + n_total_memory_size = n_height * n_width; + n_total_memory_size += (n_total_memory_size >> 1); + } + return n_total_memory_size; +} + +void ddl_calculate_stride(struct vcd_property_frame_size_type *p_frame_size, + u32 b_interlace) +{ + p_frame_size->n_stride = ((p_frame_size->n_width + 15) >> 4) << 4; + + if (b_interlace) { + p_frame_size->n_scan_lines = + ((p_frame_size->n_height + 31) >> 5) << 5; + } else { + p_frame_size->n_scan_lines = + ((p_frame_size->n_height + 15) >> 4) << 4; + } + +} + +static u32 ddl_valid_buffer_requirement + (struct vcd_buffer_requirement_type *original_buf_req, + struct vcd_buffer_requirement_type *req_buf_req) +{ + u32 b_status = FALSE; + if ( + original_buf_req->n_max_count >= req_buf_req->n_actual_count + && original_buf_req->n_actual_count <= + req_buf_req->n_actual_count && + original_buf_req->n_align <= req_buf_req->n_align && + original_buf_req->n_size <= req_buf_req->n_size) { + b_status = TRUE; + } else { + VIDC_LOGERR_STRING("ddl_valid_buf_req:Failed"); + } + return b_status; +} + +static u32 ddl_decoder_min_num_dpb(struct ddl_decoder_data_type *p_decoder) +{ + u32 n_min_dpb = 0; + switch (p_decoder->codec_type.e_codec) { + default: + case VCD_CODEC_MPEG4: + case VCD_CODEC_MPEG2: + case VCD_CODEC_DIVX_4: + case VCD_CODEC_DIVX_5: + case VCD_CODEC_DIVX_6: + case VCD_CODEC_XVID: + { + n_min_dpb = 3; + break; + } + case VCD_CODEC_H263: + { + n_min_dpb = 2; + break; + } + case VCD_CODEC_VC1: + case VCD_CODEC_VC1_RCV: + { + n_min_dpb = 4; + break; + } + case VCD_CODEC_H264: + { + u32 n_yuv_size = + ((p_decoder->client_frame_size.n_height * + p_decoder->client_frame_size.n_width * 3) >> 1); + n_min_dpb = 6912000 / n_yuv_size; + if (n_min_dpb > 16) + n_min_dpb = 16; + + n_min_dpb += 2; + break; + } + } + return n_min_dpb; +} + +static u32 ddl_set_dec_buffers + (struct ddl_decoder_data_type *p_decoder, + struct ddl_property_dec_pic_buffers_type *p_dpb) { + u32 vcd_status = VCD_S_SUCCESS; + u32 n_loopc; + for (n_loopc = 0; !vcd_status && + n_loopc < p_dpb->n_no_of_dec_pic_buf; ++n_loopc) { + if ((!DDL_ADDR_IS_ALIGNED + (p_dpb->a_dec_pic_buffers[n_loopc].vcd_frm.p_physical, + p_decoder->client_output_buf_req.n_align) + ) + || (p_dpb->a_dec_pic_buffers[n_loopc].vcd_frm.n_alloc_len < + p_decoder->client_output_buf_req.n_size) + ) { + vcd_status = VCD_ERR_ILLEGAL_PARM; + } + } + if (vcd_status) { + VIDC_LOGERR_STRING + ("ddl_set_prop:Dpb_align_fail_or_alloc_size_small"); + return vcd_status; + } + if (p_decoder->dp_buf.n_no_of_dec_pic_buf) { + DDL_FREE(p_decoder->dp_buf.a_dec_pic_buffers); + p_decoder->dp_buf.n_no_of_dec_pic_buf = 0; + } + p_decoder->dp_buf.a_dec_pic_buffers = + DDL_MALLOC(p_dpb->n_no_of_dec_pic_buf * + sizeof(struct ddl_frame_data_type_tag)); + + if (!p_decoder->dp_buf.a_dec_pic_buffers) { + VIDC_LOGERR_STRING + ("ddl_dec_set_prop:Dpb_container_alloc_failed"); + return VCD_ERR_ALLOC_FAIL; + } + p_decoder->dp_buf.n_no_of_dec_pic_buf = p_dpb->n_no_of_dec_pic_buf; + for (n_loopc = 0; n_loopc < p_dpb->n_no_of_dec_pic_buf; ++n_loopc) { + p_decoder->dp_buf.a_dec_pic_buffers[n_loopc] = + p_dpb->a_dec_pic_buffers[n_loopc]; + } + p_decoder->dpb_mask.n_client_mask = 0; + p_decoder->dpb_mask.n_hw_mask = 0; + p_decoder->n_dynamic_prop_change = 0; + return VCD_S_SUCCESS; +} + +void ddl_set_initial_default_values(struct ddl_client_context_type *p_ddl) +{ + if (p_ddl->b_decoding) { + p_ddl->codec_data.decoder.codec_type.e_codec = VCD_CODEC_MPEG4; + vcd_fw_transact(TRUE, TRUE, + p_ddl->codec_data.decoder.codec_type.e_codec); + ddl_set_default_dec_property(p_ddl); + } else { + struct ddl_encoder_data_type *p_encoder = + &(p_ddl->codec_data.encoder); + p_encoder->codec_type.e_codec = VCD_CODEC_MPEG4; + vcd_fw_transact(TRUE, FALSE, + p_encoder->codec_type.e_codec); + + p_encoder->target_bit_rate.n_target_bitrate = 64000; + p_encoder->frame_size.n_width = 176; + p_encoder->frame_size.n_height = 144; + p_encoder->frame_size.n_stride = 176; + p_encoder->frame_size.n_scan_lines = 144; + p_encoder->frame_rate.n_fps_numerator = 30; + p_encoder->frame_rate.n_fps_denominator = 1; + ddl_set_default_enc_property(p_ddl); + } + + return; +} diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.c new file mode 100644 index 0000000000000..a2df26aadb484 --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.c @@ -0,0 +1,223 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" +#include "vcd_ddl_utils.h" + +#if DEBUG +#define DBG(x...) printk(KERN_DEBUG x) +#else +#define DBG(x...) +#endif + +#define ERR(x...) printk(KERN_ERR x) + +static unsigned int g_ddl_dec_t1, g_ddl_enc_t1; +static unsigned int g_ddl_dec_ttotal, g_ddl_enc_ttotal; +static unsigned int g_ddl_dec_count, g_ddl_enc_count; + +#ifdef NO_IN_KERNEL_PMEM + +void ddl_pmem_alloc(struct ddl_buf_addr_type *buff_addr, u32 size, u32 align) +{ + u32 n_guard_bytes, n_align_mask; + u32 n_physical_addr, n_align_offset; + dma_addr_t phy_addr; + + if (align == DDL_LINEAR_BUFFER_ALIGN_BYTES) { + + n_guard_bytes = 31; + n_align_mask = 0xFFFFFFE0U; + + } else { + + n_guard_bytes = DDL_TILE_BUF_ALIGN_GUARD_BYTES; + n_align_mask = DDL_TILE_BUF_ALIGN_MASK; + } + + buff_addr->p_virtual_base_addr = + kmalloc((size + n_guard_bytes), GFP_KERNEL); + + if (!buff_addr->p_virtual_base_addr) { + ERR("\n ERROR %s:%u kamlloc fails to allocate" + " size + n_guard_bytes = %u\n", __func__, __LINE__, + (size + n_guard_bytes)); + return; + } + + phy_addr = dma_map_single(NULL, buff_addr->p_virtual_base_addr, + size + n_guard_bytes, DMA_TO_DEVICE); + + buff_addr->n_buffer_size = size; + n_physical_addr = (u32) phy_addr; + buff_addr->p_align_physical_addr = + (u32 *) ((n_physical_addr + n_guard_bytes) & n_align_mask); + n_align_offset = + (u32) (buff_addr->p_align_physical_addr) - n_physical_addr; + buff_addr->p_align_virtual_addr = + (u32 *) ((u32) (buff_addr->p_virtual_base_addr) + + n_align_offset); +} + +void ddl_pmem_free(struct ddl_buf_addr_type buff_addr) +{ + kfree(buff_addr.p_virtual_base_addr); + buff_addr.n_buffer_size = 0; + buff_addr.p_virtual_base_addr = NULL; +} + +#else + +void ddl_pmem_alloc(struct ddl_buf_addr_type *buff_addr, u32 size, u32 align) +{ + u32 n_guard_bytes, n_align_mask; + s32 n_physical_addr; + u32 n_align_offset; + + if (align == DDL_LINEAR_BUFFER_ALIGN_BYTES) { + + n_guard_bytes = 31; + n_align_mask = 0xFFFFFFE0U; + + } else { + + n_guard_bytes = DDL_TILE_BUF_ALIGN_GUARD_BYTES; + n_align_mask = DDL_TILE_BUF_ALIGN_MASK; + } + + n_physical_addr = pmem_kalloc((size + n_guard_bytes), + PMEM_MEMTYPE_EBI1 | PMEM_ALIGNMENT_4K); + buff_addr->p_physical_base_addr = (u32 *)n_physical_addr; + + if (IS_ERR((void *)n_physical_addr)) { + pr_err("%s(): could not allocte in kernel pmem buffers\n", + __func__); + return; + } + + buff_addr->p_virtual_base_addr = + (u32 *) ioremap((unsigned long)n_physical_addr, + size + n_guard_bytes); + memset(buff_addr->p_virtual_base_addr, 0 , size + n_guard_bytes); + if (!buff_addr->p_virtual_base_addr) { + + pr_err("%s: could not ioremap in kernel pmem buffers\n", + __func__); + pmem_kfree(n_physical_addr); + return; + } + + buff_addr->n_buffer_size = size; + + buff_addr->p_align_physical_addr = + (u32 *) ((n_physical_addr + n_guard_bytes) & n_align_mask); + + n_align_offset = + (u32) (buff_addr->p_align_physical_addr) - n_physical_addr; + + buff_addr->p_align_virtual_addr = + (u32 *) ((u32) (buff_addr->p_virtual_base_addr) + + n_align_offset); + + pr_debug("%s(): phy addr 0x%08x kernel addr 0x%08x\n", __func__, + (u32) buff_addr->p_align_physical_addr, + (u32) buff_addr->p_align_physical_addr); + + return; +} + +void ddl_pmem_free(struct ddl_buf_addr_type buff_addr) +{ + DBG("\n %s(): ddl_pmem_free v_address %p p_address %p", + __func__, buff_addr.p_physical_base_addr, + buff_addr.p_virtual_base_addr); + + if (buff_addr.p_virtual_base_addr) + iounmap((void *)buff_addr.p_virtual_base_addr); + + if ((buff_addr.p_physical_base_addr) && + pmem_kfree((s32) buff_addr.p_physical_base_addr)) { + ERR("\n %s(): Error in Freeing ddl_pmem_free " + "Physical Address %p", __func__, + buff_addr.p_physical_base_addr); + } + + buff_addr.n_buffer_size = 0; + buff_addr.p_virtual_base_addr = NULL; +} +#endif + +void ddl_get_core_start_time(u8 codec_type) +{ + u32 *p_ddl_t1 = NULL; + if (!codec_type) + p_ddl_t1 = &g_ddl_dec_t1; + else if (codec_type == 1) + p_ddl_t1 = &g_ddl_enc_t1; + + if (!*p_ddl_t1) { + struct timeval ddl_tv; + do_gettimeofday(&ddl_tv); + *p_ddl_t1 = (ddl_tv.tv_sec * 1000) + (ddl_tv.tv_usec / 1000); + } +} + +void ddl_calc_core_time(u8 codec_type) +{ + u32 *p_ddl_t1 = NULL, *p_ddl_ttotal = NULL, + *p_ddl_count = NULL; + if (!codec_type) { + DBG("\n720p Core Decode "); + p_ddl_t1 = &g_ddl_dec_t1; + p_ddl_ttotal = &g_ddl_dec_ttotal; + p_ddl_count = &g_ddl_dec_count; + } else if (codec_type == 1) { + DBG("\n720p Core Encode "); + p_ddl_t1 = &g_ddl_enc_t1; + p_ddl_ttotal = &g_ddl_enc_ttotal; + p_ddl_count = &g_ddl_enc_count; + } + + if (*p_ddl_t1) { + int ddl_t2; + struct timeval ddl_tv; + do_gettimeofday(&ddl_tv); + ddl_t2 = (ddl_tv.tv_sec * 1000) + (ddl_tv.tv_usec / 1000); + *p_ddl_ttotal += (ddl_t2 - *p_ddl_t1); + *p_ddl_count = *p_ddl_count + 1; + DBG("time %u, average time %u, count %u", + ddl_t2 - *p_ddl_t1, (*p_ddl_ttotal)/(*p_ddl_count), + *p_ddl_count); + *p_ddl_t1 = 0; + } +} + +void ddl_reset_time_variables(u8 codec_type) +{ + if (!codec_type) { + DBG("\n Reset Decoder time variables"); + g_ddl_dec_t1 = 0; + g_ddl_dec_ttotal = 0; + g_ddl_dec_count = 0; + } else if (codec_type == 1) { + DBG("\n Reset Encoder time variables "); + g_ddl_enc_t1 = 0; + g_ddl_enc_ttotal = 0; + g_ddl_enc_count = 0; + } +} diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.h new file mode 100644 index 0000000000000..ee0cf9574b4d6 --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.h @@ -0,0 +1,60 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VCD_DDL_UTILS_H_ +#define _VCD_DDL_UTILS_H_ + +#include "vcd_ddl_core.h" +#include "vcd_ddl.h" + +#define DDL_INLINE + +#define DDL_ALIGN_SIZE(n_size, n_guard_bytes, n_align_mask) \ + (((u32)(n_size) + n_guard_bytes) & n_align_mask) + +#define DDL_MALLOC(x) kmalloc(x, GFP_KERNEL) +#define DDL_FREE(x) { if ((x)) kfree((x)); (x) = NULL; } + +void ddl_pmem_alloc(struct ddl_buf_addr_type *, u32, u32); + +void ddl_pmem_free(struct ddl_buf_addr_type); + +void ddl_get_core_start_time(u8 codec_type); + +void ddl_calc_core_time(u8 codec_type); + +void ddl_reset_time_variables(u8 codec_type); + +#define DDL_ASSERT(x) +#define DDL_MEMSET(src, value, len) memset((src), (value), (len)) +#define DDL_MEMCPY(dest, src, len) memcpy((dest), (src), (len)) + +#define DDL_ADDR_IS_ALIGNED(addr, align_bytes) \ +(!((u32)(addr) & ((align_bytes) - 1))) + +#endif diff --git a/drivers/misc/video_core/720p/ddl/video_core_720p.c b/drivers/misc/video_core/720p/ddl/video_core_720p.c new file mode 100644 index 0000000000000..656651a57dffe --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/video_core_720p.c @@ -0,0 +1,800 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include + +#include "video_core_type.h" +#include "video_core_720p.h" + +#if DEBUG +#define DBG(x...) printk(KERN_DEBUG x) +#else +#define DBG(x...) +#endif + +#define VIDC_720P_VERSION_STRING "VIDC_V1.0" +u8 *vid_c_base_addr; + +#ifdef VIDC_REGISTER_LOG_INTO_BUFFER +char vidclog[VIDC_REGLOG_BUFSIZE]; +unsigned int vidclog_index; +#endif + +void vidc_720p_set_device_virtual_base(u8 *p_core_virtual_base_addr) +{ + vid_c_base_addr = p_core_virtual_base_addr; +} + +void vidc_720p_init(char **ppsz_version, u32 i_firmware_size, + u32 *pi_firmware_address, + enum vidc_720p_endian_type e_dma_endian, + u32 b_interrupt_off, + enum vidc_720p_interrupt_level_selection_type + e_interrupt_sel, u32 interrupt_mask) +{ + if (ppsz_version) + *ppsz_version = VIDC_720P_VERSION_STRING; + + if (e_interrupt_sel == VIDC_720P_INTERRUPT_LEVEL_SEL) + VIDC_IO_OUT(REG_491082, 0); + else + VIDC_IO_OUT(REG_491082, 1); + + if (b_interrupt_off) + VIDC_IO_OUT(REG_609676, 1); + else + VIDC_IO_OUT(REG_609676, 0); + + VIDC_IO_OUT(REG_614776, 1); + + VIDC_IO_OUT(REG_418173, 0); + + VIDC_IO_OUT(REG_418173, interrupt_mask); + + VIDC_IO_OUT(REG_736316, e_dma_endian); + + VIDC_IO_OUT(REG_215724, 0); + + VIDC_IO_OUT(REG_361582, 1); + + VIDC_IO_OUT(REG_591577, i_firmware_size); + + VIDC_IO_OUT(REG_203921, pi_firmware_address); + + VIDC_IO_OUT(REG_531515_ADDR, 0); + + VIDC_IO_OUT(REG_614413, 1); +} + +u32 vidc_720p_do_sw_reset(void) +{ + + u32 n_fw_start = 0; + VIDC_BUSY_WAIT(5); + VIDC_IO_OUT(REG_224135, 0); + VIDC_BUSY_WAIT(5); + VIDC_IO_OUT(REG_193553, 0); + VIDC_BUSY_WAIT(5); + VIDC_IO_OUT(REG_141269, 1); + VIDC_BUSY_WAIT(15); + VIDC_IO_OUT(REG_141269, 0); + VIDC_BUSY_WAIT(5); + VIDC_IO_IN(REG_193553, &n_fw_start); + + if (!n_fw_start) { + DBG("\n VIDC-SW-RESET-FAILS!"); + return FALSE; + } + return TRUE; +} + +u32 vidc_720p_reset_is_success() +{ + u32 n_stagecounter = 0; + VIDC_IO_IN(REG_352831, &n_stagecounter); + n_stagecounter &= 0xff; + if (n_stagecounter != 0xe5) { + DBG("\n VIDC-CPU_RESET-FAILS!"); + VIDC_IO_OUT(REG_224135, 0); + msleep(10); + return FALSE; + } + return TRUE; +} + +void vidc_720p_start_cpu(enum vidc_720p_endian_type e_dma_endian, + u32 *p_icontext_bufferstart, + u32 *p_debug_core_dump_addr, + u32 debug_buffer_size) +{ + u32 dbg_info_input0_reg = 0x1; + VIDC_IO_OUT(REG_361582, 0); + VIDC_IO_OUT(REG_958768, p_icontext_bufferstart); + VIDC_IO_OUT(REG_736316, e_dma_endian); + if (debug_buffer_size) { + dbg_info_input0_reg = (debug_buffer_size << 0x10) + | (0x2 << 1) | 0x1; + VIDC_IO_OUT(REG_166247, p_debug_core_dump_addr); + } + VIDC_IO_OUT(REG_699747, dbg_info_input0_reg); + VIDC_IO_OUT(REG_224135, 1); +} + +u32 vidc_720p_cpu_start() +{ + u32 fw_status = 0x0; + VIDC_IO_IN(REG_381535, &fw_status); + if (fw_status != 0x02) + return FALSE; + return TRUE; +} + + +void vidc_720p_stop_fw(void) +{ + VIDC_IO_OUT(REG_193553, 0); + VIDC_IO_OUT(REG_224135, 0); +} + +void vidc_720p_get_interrupt_status(u32 *p_interrupt_status, + u32 *p_cmd_err_status, u32 *p_disp_pic_err_status, u32 *p_op_failed) +{ + u32 err_status; + VIDC_IO_IN(REG_512143, p_interrupt_status); + VIDC_IO_IN(REG_300310, &err_status); + *p_cmd_err_status = err_status & 0xffff; + *p_disp_pic_err_status = (err_status & 0xffff0000) >> 16; + VIDC_IO_INF(REG_724381, OPERATION_FAILED, \ + p_op_failed); +} + +void vidc_720p_interrupt_done_clear(void) +{ + VIDC_IO_OUT(REG_614776, 1); + VIDC_IO_OUT(REG_97293, 4); +} + +void vidc_720p_submit_command(u32 ch_id, u32 n_cmd_id) +{ + u32 fw_status; + VIDC_IO_OUT(REG_97293, ch_id); + VIDC_IO_OUT(REG_62325, n_cmd_id); + VIDC_DEBUG_REGISTER_LOG; + VIDC_IO_IN(REG_381535, &fw_status); + VIDC_IO_OUT(REG_926519, fw_status); +} + +u32 vidc_720p_engine_reset(u32 n_ch_id, + enum vidc_720p_endian_type e_dma_endian, + enum vidc_720p_interrupt_level_selection_type e_interrupt_sel, + u32 interrupt_mask +) +{ + u32 n_op_done = 0; + u32 n_counter = 0; + + VIDC_LOGERR_STRING("ENG-RESET!!"); + /* issue the engine reset command */ + vidc_720p_submit_command(n_ch_id, VIDC_720P_CMD_MFC_ENGINE_RESET); + + do { + VIDC_BUSY_WAIT(20); + VIDC_IO_IN(REG_982553, &n_op_done); + n_counter++; + } while (!n_op_done && n_counter < 10); + + if (!n_op_done) { + /* Reset fails */ + return FALSE ; + } + + /* write invalid channel id */ + VIDC_IO_OUT(REG_97293, 4); + + /* Set INT_PULSE_SEL */ + if (e_interrupt_sel == VIDC_720P_INTERRUPT_LEVEL_SEL) + VIDC_IO_OUT(REG_491082, 0); + else + VIDC_IO_OUT(REG_491082, 1); + + if (!interrupt_mask) { + /* Disable interrupt */ + VIDC_IO_OUT(REG_609676, 1); + } else { + /* Enable interrupt */ + VIDC_IO_OUT(REG_609676, 0); + } + + /* Clear any pending interrupt */ + VIDC_IO_OUT(REG_614776, 1); + + /* Set INT_ENABLE_REG */ + VIDC_IO_OUT(REG_418173, interrupt_mask); + + /*Sets the DMA endianness */ + VIDC_IO_OUT(REG_736316, e_dma_endian); + + /* retun engine reset success */ + return TRUE ; +} + +void vidc_720p_set_channel(u32 i_ch_id, + enum vidc_720p_enc_dec_selection_type + e_enc_dec_sel, enum vidc_720p_codec_type e_codec, + u32 *pi_fw, u32 i_firmware_size) +{ + u32 std_sel = 0; + VIDC_IO_OUT(REG_661565, 0); + + if (e_enc_dec_sel) + std_sel = VIDC_REG_713080_ENC_ON_BMSK; + + std_sel |= (u32) e_codec; + + VIDC_IO_OUT(REG_713080, std_sel); + + switch (e_codec) { + default: + case VIDC_720P_DIVX: + case VIDC_720P_XVID: + case VIDC_720P_MPEG4: + { + if (e_enc_dec_sel == VIDC_720P_ENCODER) + VIDC_IO_OUT(REG_765787, pi_fw); + else + VIDC_IO_OUT(REG_225040, pi_fw); + break; + } + case VIDC_720P_H264: + { + if (e_enc_dec_sel == VIDC_720P_ENCODER) + VIDC_IO_OUT(REG_942456, pi_fw); + else + VIDC_IO_OUT(REG_942170_ADDR_3, pi_fw); + break; + } + case VIDC_720P_H263: + { + if (e_enc_dec_sel == VIDC_720P_ENCODER) + VIDC_IO_OUT(REG_765787, pi_fw); + else + VIDC_IO_OUT(REG_942170_ADDR_6, pi_fw); + break; + } + case VIDC_720P_VC1: + { + VIDC_IO_OUT(REG_880188, pi_fw); + break; + } + case VIDC_720P_MPEG2: + { + VIDC_IO_OUT(REG_40293, pi_fw); + break; + } + } + VIDC_IO_OUT(REG_591577, i_firmware_size); + + vidc_720p_submit_command(i_ch_id, VIDC_720P_CMD_CHSET); +} + +void vidc_720p_encode_set_profile(u32 i_profile, u32 i_level) +{ + u32 profile_level = i_profile|(i_level << 0x8); + VIDC_IO_OUT(REG_839021, profile_level); +} + +void vidc_720p_set_frame_size(u32 i_size_x, u32 i_size_y) +{ + VIDC_IO_OUT(REG_999267, i_size_x); + + VIDC_IO_OUT(REG_345712, i_size_y); +} + +void vidc_720p_encode_set_fps(u32 i_rc_frame_rate) +{ + VIDC_IO_OUT(REG_625444, i_rc_frame_rate); +} + +void vidc_720p_encode_set_short_header(u32 i_short_header) +{ + VIDC_IO_OUT(REG_314290, i_short_header); +} + +void vidc_720p_encode_set_vop_time(u32 n_vop_time_resolution, + u32 n_vop_time_increment) +{ + u32 enable_vop, vop_timing_reg; + if (!n_vop_time_resolution) + VIDC_IO_OUT(REG_64895, 0x0); + else { + enable_vop = 0x1; + vop_timing_reg = (enable_vop << 0x1f) | + (n_vop_time_resolution << 0x10) | n_vop_time_increment; + VIDC_IO_OUT(REG_64895, vop_timing_reg); + } +} + +void vidc_720p_encode_set_hec_period(u32 n_hec_period) +{ + VIDC_IO_OUT(REG_407718, n_hec_period); +} + +void vidc_720p_encode_set_qp_params(u32 i_max_qp, u32 i_min_qp) +{ + u32 qp = i_min_qp | (i_max_qp << 0x8); + VIDC_IO_OUT(REG_734318, qp); +} + +void vidc_720p_encode_set_rc_config(u32 b_enable_frame_level_rc, + u32 b_enable_mb_level_rc_flag, + u32 i_frame_qp, u32 n_pframe_qp) +{ + u32 n_rc_config = i_frame_qp; + + if (b_enable_frame_level_rc) + n_rc_config |= (0x1 << 0x9); + + if (b_enable_mb_level_rc_flag) + n_rc_config |= (0x1 << 0x8); + + VIDC_IO_OUT(REG_58211, n_rc_config); + VIDC_IO_OUT(REG_548359, n_pframe_qp); +} + +void vidc_720p_encode_set_bit_rate(u32 i_target_bitrate) +{ + VIDC_IO_OUT(REG_174150, i_target_bitrate); +} + +void vidc_720p_encoder_set_param_change(u32 n_enc_param_change) +{ + VIDC_IO_OUT(REG_804959, n_enc_param_change); +} + +void vidc_720p_encode_set_control_param(u32 n_param_val) +{ + VIDC_IO_OUT(REG_128234, n_param_val); +} + +void vidc_720p_encode_set_frame_level_rc_params(u32 i_reaction_coeff) +{ + VIDC_IO_OUT(REG_677784, i_reaction_coeff); +} + +void vidc_720p_encode_set_mb_level_rc_params(u32 b_dark_region_as_flag, + u32 b_smooth_region_as_flag, + u32 b_static_region_as_flag, + u32 b_activity_region_flag) +{ + u32 n_mb_level_rc = 0x0; + if (b_activity_region_flag) + n_mb_level_rc |= 0x1; + if (b_static_region_as_flag) + n_mb_level_rc |= (0x1 << 0x1); + if (b_smooth_region_as_flag) + n_mb_level_rc |= (0x1 << 0x2); + if (b_dark_region_as_flag) + n_mb_level_rc |= (0x1 << 0x3); + /* Write MB level rate control */ + VIDC_IO_OUT(REG_995041, n_mb_level_rc); +} + +void vidc_720p_encode_set_entropy_control(enum vidc_720p_entropy_sel_type + e_entropy_sel, + enum vidc_720p_cabac_model_type + e_cabac_model_number) +{ + u32 n_num; + u32 n_entropy_params = (u32)e_entropy_sel; + /* Set Model Number */ + if (e_entropy_sel == VIDC_720P_ENTROPY_SEL_CABAC) { + n_num = (u32)e_cabac_model_number; + n_entropy_params |= (n_num << 0x2); + } + /* Set Entropy parameters */ + VIDC_IO_OUT(REG_504878, n_entropy_params); +} + +void vidc_720p_encode_set_db_filter_control(enum vidc_720p_DBConfig_type + e_db_config, + u32 i_slice_alpha_offset, + u32 i_slice_beta_offset) +{ + u32 n_deblock_params; + n_deblock_params = (u32)e_db_config; + n_deblock_params |= + ((i_slice_beta_offset << 0x2) | (i_slice_alpha_offset << 0x7)); + + /* Write deblocking control settings */ + VIDC_IO_OUT(REG_458130, n_deblock_params); +} + +void vidc_720p_encode_set_intra_refresh_mb_number(u32 i_cir_mb_number) +{ + VIDC_IO_OUT(REG_857491, i_cir_mb_number); +} + +void vidc_720p_encode_set_multi_slice_info(enum + vidc_720p_MSlice_selection_type + e_m_slice_sel, + u32 n_multi_slice_size) +{ + switch (e_m_slice_sel) { + case VIDC_720P_MSLICE_BY_MB_COUNT: + { + VIDC_IO_OUT(REG_588301, 0x1); + VIDC_IO_OUT(REG_1517, e_m_slice_sel); + VIDC_IO_OUT(REG_105335, n_multi_slice_size); + break; + } + case VIDC_720P_MSLICE_BY_BYTE_COUNT: + { + VIDC_IO_OUT(REG_588301, 0x1); + VIDC_IO_OUT(REG_1517, e_m_slice_sel); + VIDC_IO_OUT(REG_561679, n_multi_slice_size); + break; + } + case VIDC_720P_MSLICE_BY_GOB: + { + VIDC_IO_OUT(REG_588301, 0x1); + break; + } + default: + case VIDC_720P_MSLICE_OFF: + { + VIDC_IO_OUT(REG_588301, 0x0); + break; + } + } +} + +void vidc_720p_encode_set_dpb_buffer(u32 *pi_enc_dpb_addr, u32 alloc_len) +{ + VIDC_IO_OUT(REG_341928_ADDR, pi_enc_dpb_addr); + VIDC_IO_OUT(REG_319934, alloc_len); +} + +void vidc_720p_encode_set_i_period(u32 i_i_period) +{ + VIDC_IO_OUT(REG_950374, i_i_period); +} + +void vidc_720p_encode_init_codec(u32 i_ch_id, + enum vidc_720p_memory_access_method_type + e_memory_access_model) +{ + + VIDC_IO_OUT(REG_841539, e_memory_access_model); + vidc_720p_submit_command(i_ch_id, VIDC_720P_CMD_INITCODEC); +} + +void vidc_720p_encode_unalign_bitstream(u32 n_upper_unalign_word, + u32 n_lower_unalign_word) +{ + VIDC_IO_OUT(REG_792026, n_upper_unalign_word); + VIDC_IO_OUT(REG_844152, n_lower_unalign_word); +} + +void vidc_720p_encode_set_seq_header_buffer(u32 n_ext_buffer_start, + u32 n_ext_buffer_end, + u32 n_start_byte_num) +{ + VIDC_IO_OUT(REG_275113_ADDR, n_ext_buffer_start); + + VIDC_IO_OUT(REG_87912, n_ext_buffer_start); + + VIDC_IO_OUT(REG_988007_ADDR, n_ext_buffer_end); + + VIDC_IO_OUT(REG_66693, n_start_byte_num); +} + +void vidc_720p_encode_frame(u32 n_ch_id, + u32 n_ext_buffer_start, + u32 n_ext_buffer_end, + u32 n_start_byte_number, u32 n_y_addr, + u32 n_c_addr) +{ + VIDC_IO_OUT(REG_275113_ADDR, n_ext_buffer_start); + + VIDC_IO_OUT(REG_988007_ADDR, n_ext_buffer_end); + + VIDC_IO_OUT(REG_87912, n_ext_buffer_start); + + VIDC_IO_OUT(REG_66693, n_start_byte_number); + + VIDC_IO_OUT(REG_99105, n_y_addr); + + VIDC_IO_OUT(REG_777113_ADDR, n_c_addr); + + vidc_720p_submit_command(n_ch_id, VIDC_720P_CMD_FRAMERUN); +} + +void vidc_720p_encode_get_header(u32 *pi_enc_header_size) +{ + VIDC_IO_IN(REG_114286, pi_enc_header_size); +} + +void vidc_720p_enc_frame_info(struct vidc_720p_enc_frame_info_type + *p_enc_frame_info) +{ + VIDC_IO_IN(REG_782249, &p_enc_frame_info->n_enc_size); + + VIDC_IO_IN(REG_441270, &p_enc_frame_info->n_frame_type); + + p_enc_frame_info->n_frame_type &= 0x03; + + VIDC_IO_IN(REG_613254, + &p_enc_frame_info->n_metadata_exists); +} + +void vidc_720p_decode_bitstream_header(u32 n_ch_id, + u32 n_dec_unit_size, + u32 n_start_byte_num, + u32 n_ext_buffer_start, + u32 n_ext_buffer_end, + enum + vidc_720p_memory_access_method_type + e_memory_access_model) +{ + VIDC_IO_OUT(REG_965480, 0x0); + + VIDC_IO_OUT(REG_275113_ADDR, n_ext_buffer_start); + + VIDC_IO_OUT(REG_988007_ADDR, n_ext_buffer_end); + + VIDC_IO_OUT(REG_87912, n_ext_buffer_end); + + VIDC_IO_OUT(REG_761892, n_dec_unit_size); + + VIDC_IO_OUT(REG_66693, n_start_byte_num); + + VIDC_IO_OUT(REG_841539, e_memory_access_model); + + vidc_720p_submit_command(n_ch_id, VIDC_720P_CMD_INITCODEC); +} + +void vidc_720p_decode_get_seq_hdr_info(struct vidc_720p_seq_hdr_info_type + *p_seq_hdr_info) +{ + u32 n_display_status; + VIDC_IO_IN(REG_999267, &p_seq_hdr_info->n_img_size_x); + + VIDC_IO_IN(REG_345712, &p_seq_hdr_info->n_img_size_y); + + VIDC_IO_IN(REG_257463, &p_seq_hdr_info->n_min_num_dpb); + + VIDC_IO_IN(REG_854281, &p_seq_hdr_info->n_min_dpb_size); + + VIDC_IO_IN(REG_580603, &p_seq_hdr_info->n_dec_frm_size); + + VIDC_IO_INF(REG_606447, DISP_PIC_PROFILE, + &p_seq_hdr_info->n_profile); + + VIDC_IO_INF(REG_606447, DIS_PIC_LEVEL, + &p_seq_hdr_info->n_level); + + VIDC_IO_INF(REG_612715, DISPLAY_STATUS, + &n_display_status); + p_seq_hdr_info->n_progressive = + ((n_display_status & 0x4) >> 2); + /* bit 3 is for crop existence */ + p_seq_hdr_info->n_crop_exists = ((n_display_status & 0x8) >> 3); + + if (p_seq_hdr_info->n_crop_exists) { + /* read the cropping information */ + VIDC_IO_INF(REG_881638, CROP_RIGHT_OFFSET, \ + &p_seq_hdr_info->n_crop_right_offset); + VIDC_IO_INF(REG_881638, CROP_LEFT_OFFSET, \ + &p_seq_hdr_info->n_crop_left_offset); + VIDC_IO_INF(REG_161486, CROP_BOTTOM_OFFSET, \ + &p_seq_hdr_info->n_crop_bottom_offset); + VIDC_IO_INF(REG_161486, CROP_TOP_OFFSET, \ + &p_seq_hdr_info->n_crop_top_offset); + } + /* Read the MPEG4 data partitioning indication */ + VIDC_IO_INF(REG_441270, DATA_PARTITIONED, \ + &p_seq_hdr_info->n_data_partitioned); + +} + +void vidc_720p_decode_set_dpb_release_buffer_mask(u32 + i_dpb_release_buffer_mask) +{ + VIDC_IO_OUT(REG_603032, i_dpb_release_buffer_mask); +} + +void vidc_720p_decode_set_dpb_buffers(u32 i_buf_index, u32 *pi_dpb_buffer) +{ + VIDC_IO_OUTI(REG_615716, i_buf_index, pi_dpb_buffer); +} + +void vidc_720p_decode_set_comv_buffer(u32 *pi_dpb_comv_buffer, + u32 n_alloc_len) +{ + VIDC_IO_OUT(REG_456376_ADDR, pi_dpb_comv_buffer); + + VIDC_IO_OUT(REG_490443, n_alloc_len); +} + +void vidc_720p_decode_set_dpb_details(u32 n_num_dpb, u32 n_alloc_len, + u32 *p_ref_buffer) +{ + VIDC_IO_OUT(REG_518133, p_ref_buffer); + + VIDC_IO_OUT(REG_267567, 0); + + VIDC_IO_OUT(REG_883500, n_num_dpb); + + VIDC_IO_OUT(REG_319934, n_alloc_len); +} + +void vidc_720p_decode_set_mpeg4Post_filter(u32 b_enable_post_filter) +{ + if (b_enable_post_filter) + VIDC_IO_OUT(REG_443811, 0x1); + else + VIDC_IO_OUT(REG_443811, 0x0); +} + +void vidc_720p_decode_set_error_control(u32 b_enable_error_control) +{ + if (b_enable_error_control) + VIDC_IO_OUT(REG_846346, 0); + else + VIDC_IO_OUT(REG_846346, 1); +} + +void vidc_720p_set_deblock_line_buffer(u32 *pi_deblock_line_buffer_start, + u32 n_alloc_len) +{ + VIDC_IO_OUT(REG_979942, pi_deblock_line_buffer_start); + + VIDC_IO_OUT(REG_101184, n_alloc_len); +} + +void vidc_720p_decode_set_mpeg4_data_partitionbuffer(u32 *p_vsp_buf_start) +{ + VIDC_IO_OUT(REG_958768, p_vsp_buf_start); +} + +void vidc_720p_decode_setH264VSPBuffer(u32 *pi_vsp_temp_buffer_start) +{ + VIDC_IO_OUT(REG_958768, pi_vsp_temp_buffer_start); +} + +void vidc_720p_decode_frame(u32 n_ch_id, u32 n_ext_buffer_start, + u32 n_ext_buffer_end, u32 n_dec_unit_size, + u32 n_start_byte_num, u32 n_input_frame_tag) +{ + VIDC_IO_OUT(REG_275113_ADDR, n_ext_buffer_start); + + VIDC_IO_OUT(REG_988007_ADDR, n_ext_buffer_end); + + VIDC_IO_OUT(REG_87912, n_ext_buffer_end); + + VIDC_IO_OUT(REG_66693, n_start_byte_num); + + VIDC_IO_OUT(REG_94750, n_input_frame_tag); + + VIDC_IO_OUT(REG_761892, n_dec_unit_size); + + vidc_720p_submit_command(n_ch_id, VIDC_720P_CMD_FRAMERUN); +} + +void vidc_720p_issue_eos(u32 i_ch_id) +{ + VIDC_IO_OUT(REG_896825, 0x1); + + VIDC_IO_OUT(REG_761892, 0); + + vidc_720p_submit_command(i_ch_id, VIDC_720P_CMD_FRAMERUN); +} + +void vidc_720p_eos_info(u32 *p_disp_status) +{ + VIDC_IO_INF(REG_612715, DISPLAY_STATUS, p_disp_status); + (*p_disp_status) = (*p_disp_status) & 0x3; +} + +void vidc_720p_decode_display_info(struct vidc_720p_dec_disp_info_type + *p_disp_info) +{ + u32 display_status = 0; + VIDC_IO_INF(REG_612715, DISPLAY_STATUS, &display_status); + + p_disp_info->e_disp_status = + (enum vidc_720p_display_status_type)((display_status & 0x3)); + + p_disp_info->n_disp_is_interlace = ((display_status & 0x4) >> 2); + p_disp_info->n_crop_exists = ((display_status & 0x8) >> 3); + + p_disp_info->n_resl_change = ((display_status & 0x30) >> 4); + + VIDC_IO_INF(REG_724381, RESOLUTION_CHANGE, + &p_disp_info->n_reconfig_flush_done); + + VIDC_IO_IN(REG_999267, &p_disp_info->n_img_size_x); + + VIDC_IO_IN(REG_345712, &p_disp_info->n_img_size_y); + VIDC_IO_IN(REG_151345, &p_disp_info->n_y_addr); + VIDC_IO_IN(REG_293983, &p_disp_info->n_c_addr); + VIDC_IO_IN(REG_370409, &p_disp_info->n_tag_top); + VIDC_IO_IN(REG_438677, &p_disp_info->n_tag_bottom); + VIDC_IO_IN(REG_679165, &p_disp_info->n_pic_time_top); + VIDC_IO_IN(REG_374150, &p_disp_info->n_pic_time_bottom); + + if (p_disp_info->n_crop_exists) { + VIDC_IO_INF(REG_881638, CROP_RIGHT_OFFSET, + &p_disp_info->n_crop_right_offset); + VIDC_IO_INF(REG_881638, CROP_LEFT_OFFSET, + &p_disp_info->n_crop_left_offset); + VIDC_IO_INF(REG_161486, CROP_BOTTOM_OFFSET, + &p_disp_info->n_crop_bottom_offset); + VIDC_IO_INF(REG_161486, CROP_TOP_OFFSET, + &p_disp_info->n_crop_top_offset); + } + VIDC_IO_IN(REG_613254, &p_disp_info->n_metadata_exists); + + VIDC_IO_IN(REG_580603, + &p_disp_info->n_input_bytes_consumed); + + VIDC_IO_IN(REG_757835, &p_disp_info->n_input_frame_num); + + VIDC_IO_INF(REG_441270, FRAME_TYPE, + &p_disp_info->n_input_frame_type); + + p_disp_info->n_input_is_interlace = + ((p_disp_info->n_input_frame_type & 0x4) >> 2); + + p_disp_info->n_input_frame_type &= 0x3; +} + +void vidc_720p_decode_skip_frm_details(u32 *p_free_luma_dpb) +{ + u32 n_disp_frm_type; + VIDC_IO_IN(REG_697961, &n_disp_frm_type); + + if (n_disp_frm_type == VIDC_720P_NOTCODED) + VIDC_IO_IN(REG_347105, p_free_luma_dpb); +} + +void vidc_720p_metadata_enable(u32 n_flag, u32 *p_input_buffer) +{ + VIDC_IO_OUT(REG_854681, n_flag); + VIDC_IO_OUT(REG_988552, p_input_buffer); +} + +void vidc_720p_decode_dynamic_req_reset(void) +{ + VIDC_IO_OUT(REG_76706, 0x0); + VIDC_IO_OUT(REG_147682, 0x0); + VIDC_IO_OUT(REG_896825, 0x0); +} + +void vidc_720p_decode_dynamic_req_set(u32 n_property) +{ + if (n_property == VIDC_720P_FLUSH_REQ) + VIDC_IO_OUT(REG_76706, 0x1); + else if (n_property == VIDC_720P_EXTRADATA) + VIDC_IO_OUT(REG_147682, 0x1); +} + +void vidc_720p_decode_setpassthrough_start(u32 n_pass_startaddr) +{ + VIDC_IO_OUT(REG_486169, n_pass_startaddr); +} diff --git a/drivers/misc/video_core/720p/ddl/video_core_720p.h b/drivers/misc/video_core/720p/ddl/video_core_720p.h new file mode 100644 index 0000000000000..a4d8840654045 --- /dev/null +++ b/drivers/misc/video_core/720p/ddl/video_core_720p.h @@ -0,0 +1,2707 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef VID_C_720P_H +#define VID_C_720P_H +#include +#include +#include + +#define VIDC_720P_IN(reg) VIDC_##reg##_IN +#define VIDC_720P_INM(reg, mask) VIDC_##reg##_INM(mask) +#define VIDC_720P_OUT(reg, val) VIDC_##reg##_OUT(val) +#define VIDC_720P_OUTI(reg, index, val) VIDC_##reg##_OUTI(index, val) +#define VIDC_720P_OUTM(reg, mask, val) VIDC_##reg##_OUTM(mask, val) +#define VIDC_720P_SHFT(reg, field) VIDC_##reg##_##field##_SHFT +#define VIDC_720P_FMSK(reg, field) VIDC_##reg##_##field##_BMSK + +#define VIDC_720P_INF(io, field) (VIDC_720P_INM(io, VIDC_720P_FMSK(io, field)) \ + >> VIDC_720P_SHFT(io, field)) +#define VIDC_720P_OUTF(io, field, val) \ + VIDC_720P_OUTM(io, VIDC_720P_FMSK(io, field), \ + val << VIDC_720P_SHFT(io, field)) + +#define __inpdw(port) ioread32(port) +#define __outpdw(port, val) iowrite32(val, port) + +#define in_dword_masked(addr, mask) (__inpdw(addr) & (mask)) + +#define out_dword(addr, val) __outpdw(addr, val) + +#define out_dword_masked(io, mask, val, shadow) \ +do { \ + shadow = (shadow & (u32)(~(mask))) | ((u32)((val) & (mask))); \ + (void) out_dword(io, shadow); \ +} while (0) + +#define out_dword_masked_ns(io, mask, val, current_reg_content) \ + (void) out_dword(io, ((current_reg_content & (u32)(~(mask))) | \ + ((u32)((val) & (mask))))) + +extern u8 *vid_c_base_addr; + +#define VIDC720P_BASE vid_c_base_addr +#define VIDC_720P_WRAPPER_REG_BASE (VIDC720P_BASE + \ + 0x00000000) +#define VIDC_720P_WRAPPER_REG_BASE_PHYS VIDC_720P_BASE_PHYS + +#define VIDC_REG_614413_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 00000000) +#define VIDC_REG_614413_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 00000000) +#define VIDC_REG_614413_RMSK 0x1 +#define VIDC_REG_614413_SHFT 0 +#define VIDC_REG_614413_IN \ + in_dword_masked(VIDC_REG_614413_ADDR, \ + VIDC_REG_614413_RMSK) +#define VIDC_REG_614413_INM(m) \ + in_dword_masked(VIDC_REG_614413_ADDR, m) +#define VIDC_REG_614413_OUT(v) \ + out_dword(VIDC_REG_614413_ADDR, v) +#define VIDC_REG_614413_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_614413_ADDR, m, v, \ + VIDC_REG_614413_IN); \ +} while (0) +#define VIDC_REG_614413_DMA_START_BMSK 0x1 +#define VIDC_REG_614413_DMA_START_SHFT 0 + +#define VIDC_REG_591577_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x0000000c) +#define VIDC_REG_591577_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000000c) +#define VIDC_REG_591577_RMSK 0xffffffff +#define VIDC_REG_591577_SHFT 0 +#define VIDC_REG_591577_IN \ + in_dword_masked(VIDC_REG_591577_ADDR, \ + VIDC_REG_591577_RMSK) +#define VIDC_REG_591577_INM(m) \ + in_dword_masked(VIDC_REG_591577_ADDR, m) +#define VIDC_REG_591577_OUT(v) \ + out_dword(VIDC_REG_591577_ADDR, v) +#define VIDC_REG_591577_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_591577_ADDR, m, v, \ + VIDC_REG_591577_IN); \ +} while (0) +#define VIDC_REG_591577_BOOTCODE_SIZE_BMSK 0xffffffff +#define VIDC_REG_591577_BOOTCODE_SIZE_SHFT 0 + +#define VIDC_REG_203921_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000014) +#define VIDC_REG_203921_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000014) +#define VIDC_REG_203921_RMSK 0xffffffff +#define VIDC_REG_203921_SHFT 0 +#define VIDC_REG_203921_IN \ + in_dword_masked(VIDC_REG_203921_ADDR, \ + VIDC_REG_203921_RMSK) +#define VIDC_REG_203921_INM(m) \ + in_dword_masked(VIDC_REG_203921_ADDR, m) +#define VIDC_REG_203921_OUT(v) \ + out_dword(VIDC_REG_203921_ADDR, v) +#define VIDC_REG_203921_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_203921_ADDR, m, v, \ + VIDC_REG_203921_IN); \ +} while (0) +#define VIDC_REG_203921_DMA_EXTADDR_BMSK 0xffffffff +#define VIDC_REG_203921_DMA_EXTADDR_SHFT 0 + +#define VIDC_REG_275113_ADDR_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000018) +#define VIDC_REG_275113_ADDR_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000018) +#define VIDC_REG_275113_ADDR_RMSK 0xffffffff +#define VIDC_REG_275113_ADDR_SHFT 0 +#define VIDC_REG_275113_ADDR_IN \ + in_dword_masked(VIDC_REG_275113_ADDR_ADDR, \ + VIDC_REG_275113_ADDR_RMSK) +#define VIDC_REG_275113_ADDR_INM(m) \ + in_dword_masked(VIDC_REG_275113_ADDR_ADDR, m) +#define VIDC_REG_275113_ADDR_OUT(v) \ + out_dword(VIDC_REG_275113_ADDR_ADDR, v) +#define VIDC_REG_275113_ADDR_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_275113_ADDR_ADDR, m, v, \ + VIDC_REG_275113_ADDR_IN); \ +} while (0) +#define VIDC_REG_742076_ADDR_BMSK 0xffffffff +#define VIDC_REG_742076_ADDR_SHFT 0 + +#define VIDC_REG_988007_ADDR_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x0000001c) +#define VIDC_REG_988007_ADDR_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000001c) +#define VIDC_REG_988007_ADDR_RMSK 0xffffffff +#define VIDC_REG_988007_ADDR_SHFT 0 +#define VIDC_REG_988007_ADDR_IN \ + in_dword_masked(VIDC_REG_988007_ADDR_ADDR, \ + VIDC_REG_988007_ADDR_RMSK) +#define VIDC_REG_988007_ADDR_INM(m) \ + in_dword_masked(VIDC_REG_988007_ADDR_ADDR, m) +#define VIDC_REG_988007_ADDR_OUT(v) \ + out_dword(VIDC_REG_988007_ADDR_ADDR, v) +#define VIDC_REG_988007_ADDR_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_988007_ADDR_ADDR, m, v, \ + VIDC_REG_988007_ADDR_IN); \ +} while (0) +#define VIDC_REG_988007_ADDR_EXT_BUF_END_ADDR_BMSK 0xffffffff +#define VIDC_REG_988007_ADDR_EXT_BUF_END_ADDR_SHFT 0 + +#define VIDC_REG_531515_ADDR_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000020) +#define VIDC_REG_531515_ADDR_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000020) +#define VIDC_REG_531515_ADDR_RMSK 0xffffffff +#define VIDC_REG_531515_ADDR_SHFT 0 +#define VIDC_REG_531515_ADDR_IN \ + in_dword_masked(VIDC_REG_531515_ADDR_ADDR, \ + VIDC_REG_531515_ADDR_RMSK) +#define VIDC_REG_531515_ADDR_INM(m) \ + in_dword_masked(VIDC_REG_531515_ADDR_ADDR, m) +#define VIDC_REG_531515_ADDR_OUT(v) \ + out_dword(VIDC_REG_531515_ADDR_ADDR, v) +#define VIDC_REG_531515_ADDR_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_531515_ADDR_ADDR, m, v, \ + VIDC_REG_531515_ADDR_IN); \ +} while (0) +#define VIDC_REG_531515_ADDR_DMA_INT_ADDR_BMSK 0xffffffff +#define VIDC_REG_531515_ADDR_DMA_INT_ADDR_SHFT 0 + +#define VIDC_REG_87912_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000024) +#define VIDC_REG_87912_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000024) +#define VIDC_REG_87912_RMSK 0xffffffff +#define VIDC_REG_87912_SHFT 0 +#define VIDC_REG_87912_IN \ + in_dword_masked(VIDC_REG_87912_ADDR, \ + VIDC_REG_87912_RMSK) +#define VIDC_REG_87912_INM(m) \ + in_dword_masked(VIDC_REG_87912_ADDR, m) +#define VIDC_REG_87912_OUT(v) \ + out_dword(VIDC_REG_87912_ADDR, v) +#define VIDC_REG_87912_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_87912_ADDR, m, v, \ + VIDC_REG_87912_IN); \ +} while (0) +#define VIDC_REG_87912_HOST_PTR_ADDR_BMSK 0xffffffff +#define VIDC_REG_87912_HOST_PTR_ADDR_SHFT 0 + +#define VIDC_REG_896825_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000028) +#define VIDC_REG_896825_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000028) +#define VIDC_REG_896825_RMSK 0x1 +#define VIDC_REG_896825_SHFT 0 +#define VIDC_REG_896825_IN \ + in_dword_masked(VIDC_REG_896825_ADDR, \ + VIDC_REG_896825_RMSK) +#define VIDC_REG_896825_INM(m) \ + in_dword_masked(VIDC_REG_896825_ADDR, m) +#define VIDC_REG_896825_OUT(v) \ + out_dword(VIDC_REG_896825_ADDR, v) +#define VIDC_REG_896825_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_896825_ADDR, m, v, \ + VIDC_REG_896825_IN); \ +} while (0) +#define VIDC_REG_896825_LAST_DEC_BMSK 0x1 +#define VIDC_REG_896825_LAST_DEC_SHFT 0 + +#define VIDC_REG_174526_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x0000002c) +#define VIDC_REG_174526_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000002c) +#define VIDC_REG_174526_RMSK 0x1 +#define VIDC_REG_174526_SHFT 0 +#define VIDC_REG_174526_IN \ + in_dword_masked(VIDC_REG_174526_ADDR, VIDC_REG_174526_RMSK) +#define VIDC_REG_174526_INM(m) \ + in_dword_masked(VIDC_REG_174526_ADDR, m) +#define VIDC_REG_174526_DONE_M_BMSK 0x1 +#define VIDC_REG_174526_DONE_M_SHFT 0 + +#define VIDC_REG_736316_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000044) +#define VIDC_REG_736316_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000044) +#define VIDC_REG_736316_RMSK 0x1 +#define VIDC_REG_736316_SHFT 0 +#define VIDC_REG_736316_IN \ + in_dword_masked(VIDC_REG_736316_ADDR, \ + VIDC_REG_736316_RMSK) +#define VIDC_REG_736316_INM(m) \ + in_dword_masked(VIDC_REG_736316_ADDR, m) +#define VIDC_REG_736316_OUT(v) \ + out_dword(VIDC_REG_736316_ADDR, v) +#define VIDC_REG_736316_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_736316_ADDR, m, v, \ + VIDC_REG_736316_IN); \ +} while (0) +#define VIDC_REG_736316_BITS_ENDIAN_BMSK 0x1 +#define VIDC_REG_736316_BITS_ENDIAN_SHFT 0 + +#define VIDC_REG_761892_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000054) +#define VIDC_REG_761892_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000054) +#define VIDC_REG_761892_RMSK 0xffffffff +#define VIDC_REG_761892_SHFT 0 +#define VIDC_REG_761892_IN \ + in_dword_masked(VIDC_REG_761892_ADDR, \ + VIDC_REG_761892_RMSK) +#define VIDC_REG_761892_INM(m) \ + in_dword_masked(VIDC_REG_761892_ADDR, m) +#define VIDC_REG_761892_OUT(v) \ + out_dword(VIDC_REG_761892_ADDR, v) +#define VIDC_REG_761892_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_761892_ADDR, m, v, \ + VIDC_REG_761892_IN); \ +} while (0) +#define VIDC_REG_761892_DEC_UNIT_SIZE_BMSK 0xffffffff +#define VIDC_REG_761892_DEC_UNIT_SIZE_SHFT 0 + +#define VIDC_REG_782249_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000058) +#define VIDC_REG_782249_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000058) +#define VIDC_REG_782249_RMSK 0xffffffff +#define VIDC_REG_782249_SHFT 0 +#define VIDC_REG_782249_IN \ + in_dword_masked(VIDC_REG_782249_ADDR, \ + VIDC_REG_782249_RMSK) +#define VIDC_REG_782249_INM(m) \ + in_dword_masked(VIDC_REG_782249_ADDR, m) +#define VIDC_REG_782249_ENC_UNIT_SIZE_BMSK 0xffffffff +#define VIDC_REG_782249_ENC_UNIT_SIZE_SHFT 0 + +#define VIDC_REG_66693_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x0000005c) +#define VIDC_REG_66693_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000005c) +#define VIDC_REG_66693_RMSK 0xf +#define VIDC_REG_66693_SHFT 0 +#define VIDC_REG_66693_IN \ + in_dword_masked(VIDC_REG_66693_ADDR, \ + VIDC_REG_66693_RMSK) +#define VIDC_REG_66693_INM(m) \ + in_dword_masked(VIDC_REG_66693_ADDR, m) +#define VIDC_REG_66693_OUT(v) \ + out_dword(VIDC_REG_66693_ADDR, v) +#define VIDC_REG_66693_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_66693_ADDR, m, v, \ + VIDC_REG_66693_IN); \ +} while (0) +#define VIDC_REG_66693_START_BYTE_NUM_BMSK 0xf +#define VIDC_REG_66693_START_BYTE_NUM_SHFT 0 + +#define VIDC_REG_114286_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000060) +#define VIDC_REG_114286_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000060) +#define VIDC_REG_114286_RMSK 0xffffffff +#define VIDC_REG_114286_SHFT 0 +#define VIDC_REG_114286_IN \ + in_dword_masked(VIDC_REG_114286_ADDR, \ + VIDC_REG_114286_RMSK) +#define VIDC_REG_114286_INM(m) \ + in_dword_masked(VIDC_REG_114286_ADDR, m) +#define VIDC_REG_114286_ENC_HEADER_SIZE_BMSK 0xffffffff +#define VIDC_REG_114286_ENC_HEADER_SIZE_SHFT 0 + +#define VIDC_REG_713080_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000100) +#define VIDC_REG_713080_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000100) +#define VIDC_REG_713080_RMSK 0x1f +#define VIDC_REG_713080_SHFT 0 +#define VIDC_REG_713080_IN \ + in_dword_masked(VIDC_REG_713080_ADDR, \ + VIDC_REG_713080_RMSK) +#define VIDC_REG_713080_INM(m) \ + in_dword_masked(VIDC_REG_713080_ADDR, m) +#define VIDC_REG_713080_OUT(v) \ + out_dword(VIDC_REG_713080_ADDR, v) +#define VIDC_REG_713080_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_713080_ADDR, m, v, \ + VIDC_REG_713080_IN); \ +} while (0) +#define VIDC_REG_713080_ENC_ON_BMSK 0x10 +#define VIDC_REG_713080_ENC_ON_SHFT 0x4 +#define VIDC_REG_713080_STANDARD_SEL_BMSK 0xf +#define VIDC_REG_713080_STANDARD_SEL_SHFT 0 + +#define VIDC_REG_97293_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000104) +#define VIDC_REG_97293_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000104) +#define VIDC_REG_97293_RMSK 0x1f +#define VIDC_REG_97293_SHFT 0 +#define VIDC_REG_97293_IN \ + in_dword_masked(VIDC_REG_97293_ADDR, VIDC_REG_97293_RMSK) +#define VIDC_REG_97293_INM(m) \ + in_dword_masked(VIDC_REG_97293_ADDR, m) +#define VIDC_REG_97293_OUT(v) \ + out_dword(VIDC_REG_97293_ADDR, v) +#define VIDC_REG_97293_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_97293_ADDR, m, v, \ + VIDC_REG_97293_IN); \ +} while (0) +#define VIDC_REG_97293_CH_ID_BMSK 0x1f +#define VIDC_REG_97293_CH_ID_SHFT 0 + +#define VIDC_REG_224135_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000108) +#define VIDC_REG_224135_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000108) +#define VIDC_REG_224135_RMSK 0x1 +#define VIDC_REG_224135_SHFT 0 +#define VIDC_REG_224135_IN \ + in_dword_masked(VIDC_REG_224135_ADDR, \ + VIDC_REG_224135_RMSK) +#define VIDC_REG_224135_INM(m) \ + in_dword_masked(VIDC_REG_224135_ADDR, m) +#define VIDC_REG_224135_OUT(v) \ + out_dword(VIDC_REG_224135_ADDR, v) +#define VIDC_REG_224135_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_224135_ADDR, m, v, \ + VIDC_REG_224135_IN); \ +} while (0) +#define VIDC_REG_224135_CPU_RESET_BMSK 0x1 +#define VIDC_REG_224135_CPU_RESET_SHFT 0 + +#define VIDC_REG_832522_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x0000010c) +#define VIDC_REG_832522_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000010c) +#define VIDC_REG_832522_RMSK 0x1 +#define VIDC_REG_832522_SHFT 0 +#define VIDC_REG_832522_IN \ + in_dword_masked(VIDC_REG_832522_ADDR, VIDC_REG_832522_RMSK) +#define VIDC_REG_832522_INM(m) \ + in_dword_masked(VIDC_REG_832522_ADDR, m) +#define VIDC_REG_832522_OUT(v) \ + out_dword(VIDC_REG_832522_ADDR, v) +#define VIDC_REG_832522_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_832522_ADDR, m, v, \ + VIDC_REG_832522_IN); \ +} while (0) +#define VIDC_REG_832522_FW_END_BMSK 0x1 +#define VIDC_REG_832522_FW_END_SHFT 0 + +#define VIDC_REG_361582_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000110) +#define VIDC_REG_361582_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000110) +#define VIDC_REG_361582_RMSK 0x1 +#define VIDC_REG_361582_SHFT 0 +#define VIDC_REG_361582_IN \ + in_dword_masked(VIDC_REG_361582_ADDR, \ + VIDC_REG_361582_RMSK) +#define VIDC_REG_361582_INM(m) \ + in_dword_masked(VIDC_REG_361582_ADDR, m) +#define VIDC_REG_361582_OUT(v) \ + out_dword(VIDC_REG_361582_ADDR, v) +#define VIDC_REG_361582_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_361582_ADDR, m, v, \ + VIDC_REG_361582_IN); \ +} while (0) +#define VIDC_REG_361582_BUS_MASTER_BMSK 0x1 +#define VIDC_REG_361582_BUS_MASTER_SHFT 0 + +#define VIDC_REG_314435_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000114) +#define VIDC_REG_314435_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000114) +#define VIDC_REG_314435_RMSK 0x1 +#define VIDC_REG_314435_SHFT 0 +#define VIDC_REG_314435_IN \ + in_dword_masked(VIDC_REG_314435_ADDR, \ + VIDC_REG_314435_RMSK) +#define VIDC_REG_314435_INM(m) \ + in_dword_masked(VIDC_REG_314435_ADDR, m) +#define VIDC_REG_314435_OUT(v) \ + out_dword(VIDC_REG_314435_ADDR, v) +#define VIDC_REG_314435_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_314435_ADDR, m, v, \ + VIDC_REG_314435_IN); \ +} while (0) +#define VIDC_REG_314435_FRAME_START_BMSK 0x1 +#define VIDC_REG_314435_FRAME_START_SHFT 0 + +#define VIDC_REG_999267_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000118) +#define VIDC_REG_999267_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000118) +#define VIDC_REG_999267_RMSK 0xffff +#define VIDC_REG_999267_SHFT 0 +#define VIDC_REG_999267_IN \ + in_dword_masked(VIDC_REG_999267_ADDR, \ + VIDC_REG_999267_RMSK) +#define VIDC_REG_999267_INM(m) \ + in_dword_masked(VIDC_REG_999267_ADDR, m) +#define VIDC_REG_999267_OUT(v) \ + out_dword(VIDC_REG_999267_ADDR, v) +#define VIDC_REG_999267_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_999267_ADDR, m, v, \ + VIDC_REG_999267_IN); \ +} while (0) +#define VIDC_REG_999267_IMG_SIZE_X_BMSK 0xffff +#define VIDC_REG_999267_IMG_SIZE_X_SHFT 0 + +#define VIDC_REG_345712_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x0000011c) +#define VIDC_REG_345712_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000011c) +#define VIDC_REG_345712_RMSK 0xffff +#define VIDC_REG_345712_SHFT 0 +#define VIDC_REG_345712_IN \ + in_dword_masked(VIDC_REG_345712_ADDR, \ + VIDC_REG_345712_RMSK) +#define VIDC_REG_345712_INM(m) \ + in_dword_masked(VIDC_REG_345712_ADDR, m) +#define VIDC_REG_345712_OUT(v) \ + out_dword(VIDC_REG_345712_ADDR, v) +#define VIDC_REG_345712_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_345712_ADDR, m, v, \ + VIDC_REG_345712_IN); \ +} while (0) +#define VIDC_REG_345712_IMG_SIZE_Y_BMSK 0xffff +#define VIDC_REG_345712_IMG_SIZE_Y_SHFT 0 + +#define VIDC_REG_443811_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000124) +#define VIDC_REG_443811_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000124) +#define VIDC_REG_443811_RMSK 0x1 +#define VIDC_REG_443811_SHFT 0 +#define VIDC_REG_443811_IN \ + in_dword_masked(VIDC_REG_443811_ADDR, VIDC_REG_443811_RMSK) +#define VIDC_REG_443811_INM(m) \ + in_dword_masked(VIDC_REG_443811_ADDR, m) +#define VIDC_REG_443811_OUT(v) \ + out_dword(VIDC_REG_443811_ADDR, v) +#define VIDC_REG_443811_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_443811_ADDR, m, v, \ + VIDC_REG_443811_IN); \ +} while (0) +#define VIDC_REG_443811_POST_ON_BMSK 0x1 +#define VIDC_REG_443811_POST_ON_SHFT 0 + +#define VIDC_REG_538267_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000128) +#define VIDC_REG_538267_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000128) +#define VIDC_REG_538267_RMSK 0xffffffff +#define VIDC_REG_538267_SHFT 0 +#define VIDC_REG_538267_IN \ + in_dword_masked(VIDC_REG_538267_ADDR, \ + VIDC_REG_538267_RMSK) +#define VIDC_REG_538267_INM(m) \ + in_dword_masked(VIDC_REG_538267_ADDR, m) +#define VIDC_REG_538267_OUT(v) \ + out_dword(VIDC_REG_538267_ADDR, v) +#define VIDC_REG_538267_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_538267_ADDR, m, v, \ + VIDC_REG_538267_IN); \ +} while (0) +#define VIDC_REG_538267_QUOTIENT_VAL_BMSK 0xffff0000 +#define VIDC_REG_538267_QUOTIENT_VAL_SHFT 0x10 +#define VIDC_REG_538267_REMAINDER_VAL_BMSK 0xffff +#define VIDC_REG_538267_REMAINDER_VAL_SHFT 0 + +#define VIDC_REG_661565_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x0000012c) +#define VIDC_REG_661565_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000012c) +#define VIDC_REG_661565_RMSK 0x1 +#define VIDC_REG_661565_SHFT 0 +#define VIDC_REG_661565_IN \ + in_dword_masked(VIDC_REG_661565_ADDR, \ + VIDC_REG_661565_RMSK) +#define VIDC_REG_661565_INM(m) \ + in_dword_masked(VIDC_REG_661565_ADDR, m) +#define VIDC_REG_661565_OUT(v) \ + out_dword(VIDC_REG_661565_ADDR, v) +#define VIDC_REG_661565_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_661565_ADDR, m, v, \ + VIDC_REG_661565_IN); \ +} while (0) +#define VIDC_REG_661565_SEQUENCE_START_BMSK 0x1 +#define VIDC_REG_661565_SEQUENCE_START_SHFT 0 + +#define VIDC_REG_141269_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000130) +#define VIDC_REG_141269_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000130) +#define VIDC_REG_141269_RMSK 0x1 +#define VIDC_REG_141269_SHFT 0 +#define VIDC_REG_141269_IN \ + in_dword_masked(VIDC_REG_141269_ADDR, \ + VIDC_REG_141269_RMSK) +#define VIDC_REG_141269_INM(m) \ + in_dword_masked(VIDC_REG_141269_ADDR, m) +#define VIDC_REG_141269_OUT(v) \ + out_dword(VIDC_REG_141269_ADDR, v) +#define VIDC_REG_141269_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_141269_ADDR, m, v, \ + VIDC_REG_141269_IN); \ +} while (0) +#define VIDC_REG_141269_SW_RESET_BMSK 0x1 +#define VIDC_REG_141269_SW_RESET_SHFT 0 + +#define VIDC_REG_193553_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000134) +#define VIDC_REG_193553_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000134) +#define VIDC_REG_193553_RMSK 0x1 +#define VIDC_REG_193553_SHFT 0 +#define VIDC_REG_193553_IN \ + in_dword_masked(VIDC_REG_193553_ADDR, \ + VIDC_REG_193553_RMSK) +#define VIDC_REG_193553_INM(m) \ + in_dword_masked(VIDC_REG_193553_ADDR, m) +#define VIDC_REG_193553_OUT(v) \ + out_dword(VIDC_REG_193553_ADDR, v) +#define VIDC_REG_193553_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_193553_ADDR, m, v, \ + VIDC_REG_193553_IN); \ +} while (0) +#define VIDC_REG_193553_FW_START_BMSK 0x1 +#define VIDC_REG_193553_FW_START_SHFT 0 + +#define VIDC_REG_215724_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000138) +#define VIDC_REG_215724_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000138) +#define VIDC_REG_215724_RMSK 0x1 +#define VIDC_REG_215724_SHFT 0 +#define VIDC_REG_215724_IN \ + in_dword_masked(VIDC_REG_215724_ADDR, \ + VIDC_REG_215724_RMSK) +#define VIDC_REG_215724_INM(m) \ + in_dword_masked(VIDC_REG_215724_ADDR, m) +#define VIDC_REG_215724_OUT(v) \ + out_dword(VIDC_REG_215724_ADDR, v) +#define VIDC_REG_215724_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_215724_ADDR, m, v, \ + VIDC_REG_215724_IN); \ +} while (0) +#define VIDC_REG_215724_ARM_ENDIAN_BMSK 0x1 +#define VIDC_REG_215724_ARM_ENDIAN_SHFT 0 + +#define VIDC_REG_846346_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x0000013c) +#define VIDC_REG_846346_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000013c) +#define VIDC_REG_846346_RMSK 0x1 +#define VIDC_REG_846346_SHFT 0 +#define VIDC_REG_846346_IN \ + in_dword_masked(VIDC_REG_846346_ADDR, \ + VIDC_REG_846346_RMSK) +#define VIDC_REG_846346_INM(m) \ + in_dword_masked(VIDC_REG_846346_ADDR, m) +#define VIDC_REG_846346_OUT(v) \ + out_dword(VIDC_REG_846346_ADDR, v) +#define VIDC_REG_846346_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_846346_ADDR, m, v, \ + VIDC_REG_846346_IN); \ +} while (0) +#define VIDC_REG_846346_ERR_CTRL_BMSK 0x1 +#define VIDC_REG_846346_ERR_CTRL_SHFT 0 + +#define VIDC_REG_765787_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000200) +#define VIDC_REG_765787_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000200) +#define VIDC_REG_765787_RMSK 0xffffffff +#define VIDC_REG_765787_SHFT 0 +#define VIDC_REG_765787_IN \ + in_dword_masked(VIDC_REG_765787_ADDR, \ + VIDC_REG_765787_RMSK) +#define VIDC_REG_765787_INM(m) \ + in_dword_masked(VIDC_REG_765787_ADDR, m) +#define VIDC_REG_765787_OUT(v) \ + out_dword(VIDC_REG_765787_ADDR, v) +#define VIDC_REG_765787_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_765787_ADDR, m, v, \ + VIDC_REG_765787_IN); \ +} while (0) +#define VIDC_REG_765787_FW_STT_ADDR_0_BMSK 0xffffffff +#define VIDC_REG_765787_FW_STT_ADDR_0_SHFT 0 + +#define VIDC_REG_225040_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000204) +#define VIDC_REG_225040_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000204) +#define VIDC_REG_225040_RMSK 0xffffffff +#define VIDC_REG_225040_SHFT 0 +#define VIDC_REG_225040_IN \ + in_dword_masked(VIDC_REG_225040_ADDR, \ + VIDC_REG_225040_RMSK) +#define VIDC_REG_225040_INM(m) \ + in_dword_masked(VIDC_REG_225040_ADDR, m) +#define VIDC_REG_225040_OUT(v) \ + out_dword(VIDC_REG_225040_ADDR, v) +#define VIDC_REG_225040_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_225040_ADDR, m, v, \ + VIDC_REG_225040_IN); \ +} while (0) +#define VIDC_REG_225040_FW_STT_ADDR_1_BMSK 0xffffffff +#define VIDC_REG_225040_FW_STT_ADDR_1_SHFT 0 + +#define VIDC_REG_942456_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000208) +#define VIDC_REG_942456_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000208) +#define VIDC_REG_942456_RMSK 0xffffffff +#define VIDC_REG_942456_SHFT 0 +#define VIDC_REG_942456_IN \ + in_dword_masked(VIDC_REG_942456_ADDR, \ + VIDC_REG_942456_RMSK) +#define VIDC_REG_942456_INM(m) \ + in_dword_masked(VIDC_REG_942456_ADDR, m) +#define VIDC_REG_942456_OUT(v) \ + out_dword(VIDC_REG_942456_ADDR, v) +#define VIDC_REG_942456_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_942456_ADDR, m, v, \ + VIDC_REG_942456_IN); \ +} while (0) +#define VIDC_REG_942456_FW_STT_ADDR_2_BMSK 0xffffffff +#define VIDC_REG_942456_FW_STT_ADDR_2_SHFT 0 + +#define VIDC_REG_942170_ADDR_3_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x0000020c) +#define VIDC_REG_942170_ADDR_3_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000020c) +#define VIDC_REG_942170_ADDR_3_RMSK 0xffffffff +#define VIDC_REG_942170_ADDR_3_SHFT 0 +#define VIDC_REG_942170_ADDR_3_IN \ + in_dword_masked(VIDC_REG_942170_ADDR_3_ADDR, \ + VIDC_REG_942170_ADDR_3_RMSK) +#define VIDC_REG_942170_ADDR_3_INM(m) \ + in_dword_masked(VIDC_REG_942170_ADDR_3_ADDR, m) +#define VIDC_REG_942170_ADDR_3_OUT(v) \ + out_dword(VIDC_REG_942170_ADDR_3_ADDR, v) +#define VIDC_REG_942170_ADDR_3_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_942170_ADDR_3_ADDR, m, v, \ + VIDC_REG_942170_ADDR_3_IN); \ +} while (0) +#define VIDC_REG_942170_ADDR_3_FW_STT_ADDR_3_BMSK 0xffffffff +#define VIDC_REG_942170_ADDR_3_FW_STT_ADDR_3_SHFT 0 + +#define VIDC_REG_880188_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000210) +#define VIDC_REG_880188_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000210) +#define VIDC_REG_880188_RMSK 0xffffffff +#define VIDC_REG_880188_SHFT 0 +#define VIDC_REG_880188_IN \ + in_dword_masked(VIDC_REG_880188_ADDR, \ + VIDC_REG_880188_RMSK) +#define VIDC_REG_880188_INM(m) \ + in_dword_masked(VIDC_REG_880188_ADDR, m) +#define VIDC_REG_880188_OUT(v) \ + out_dword(VIDC_REG_880188_ADDR, v) +#define VIDC_REG_880188_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_880188_ADDR, m, v, \ + VIDC_REG_880188_IN); \ +} while (0) +#define VIDC_REG_880188_FW_STT_ADDR_4_BMSK 0xffffffff +#define VIDC_REG_880188_FW_STT_ADDR_4_SHFT 0 + +#define VIDC_REG_40293_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000214) +#define VIDC_REG_40293_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000214) +#define VIDC_REG_40293_RMSK 0xffffffff +#define VIDC_REG_40293_SHFT 0 +#define VIDC_REG_40293_IN \ + in_dword_masked(VIDC_REG_40293_ADDR, \ + VIDC_REG_40293_RMSK) +#define VIDC_REG_40293_INM(m) \ + in_dword_masked(VIDC_REG_40293_ADDR, m) +#define VIDC_REG_40293_OUT(v) \ + out_dword(VIDC_REG_40293_ADDR, v) +#define VIDC_REG_40293_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_40293_ADDR, m, v, \ + VIDC_REG_40293_IN); \ +} while (0) +#define VIDC_REG_40293_FW_STT_ADDR_5_BMSK 0xffffffff +#define VIDC_REG_40293_FW_STT_ADDR_5_SHFT 0 + +#define VIDC_REG_942170_ADDR_6_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000218) +#define VIDC_REG_942170_ADDR_6_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000218) +#define VIDC_REG_942170_ADDR_6_RMSK 0xffffffff +#define VIDC_REG_942170_ADDR_6_SHFT 0 +#define VIDC_REG_942170_ADDR_6_IN \ + in_dword_masked(VIDC_REG_942170_ADDR_6_ADDR, \ + VIDC_REG_942170_ADDR_6_RMSK) +#define VIDC_REG_942170_ADDR_6_INM(m) \ + in_dword_masked(VIDC_REG_942170_ADDR_6_ADDR, m) +#define VIDC_REG_942170_ADDR_6_OUT(v) \ + out_dword(VIDC_REG_942170_ADDR_6_ADDR, v) +#define VIDC_REG_942170_ADDR_6_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_942170_ADDR_6_ADDR, m, v, \ + VIDC_REG_942170_ADDR_6_IN); \ +} while (0) +#define VIDC_REG_942170_ADDR_6_FW_STT_ADDR_6_BMSK 0xffffffff +#define VIDC_REG_942170_ADDR_6_FW_STT_ADDR_6_SHFT 0 + +#define VIDC_REG_958768_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000230) +#define VIDC_REG_958768_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000230) +#define VIDC_REG_958768_RMSK 0xffffffff +#define VIDC_REG_958768_SHFT 0 +#define VIDC_REG_958768_IN \ + in_dword_masked(VIDC_REG_958768_ADDR, \ + VIDC_REG_958768_RMSK) +#define VIDC_REG_958768_INM(m) \ + in_dword_masked(VIDC_REG_958768_ADDR, m) +#define VIDC_REG_958768_OUT(v) \ + out_dword(VIDC_REG_958768_ADDR, v) +#define VIDC_REG_958768_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_958768_ADDR, m, v, \ + VIDC_REG_958768_IN); \ +} while (0) +#define VIDC_REG_699384_ADDR_BMSK 0xffffffff +#define VIDC_REG_699384_ADDR_SHFT 0 + +#define VIDC_REG_979942_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000234) +#define VIDC_REG_979942_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000234) +#define VIDC_REG_979942_RMSK 0xffffffff +#define VIDC_REG_979942_SHFT 0 +#define VIDC_REG_979942_IN \ + in_dword_masked(VIDC_REG_979942_ADDR, \ + VIDC_REG_979942_RMSK) +#define VIDC_REG_979942_INM(m) \ + in_dword_masked(VIDC_REG_979942_ADDR, m) +#define VIDC_REG_979942_OUT(v) \ + out_dword(VIDC_REG_979942_ADDR, v) +#define VIDC_REG_979942_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_979942_ADDR, m, v, \ + VIDC_REG_979942_IN); \ +} while (0) +#define VIDC_REG_979942_DB_STT_ADDR_BMSK 0xffffffff +#define VIDC_REG_979942_DB_STT_ADDR_SHFT 0 + +#define VIDC_REG_839021_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000300) +#define VIDC_REG_839021_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000300) +#define VIDC_REG_839021_RMSK 0xff1f +#define VIDC_REG_839021_SHFT 0 +#define VIDC_REG_839021_IN \ + in_dword_masked(VIDC_REG_839021_ADDR, VIDC_REG_839021_RMSK) +#define VIDC_REG_839021_INM(m) \ + in_dword_masked(VIDC_REG_839021_ADDR, m) +#define VIDC_REG_839021_OUT(v) \ + out_dword(VIDC_REG_839021_ADDR, v) +#define VIDC_REG_839021_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_839021_ADDR, m, v, \ + VIDC_REG_839021_IN); \ +} while (0) +#define VIDC_REG_839021_LEVEL_BMSK 0xff00 +#define VIDC_REG_839021_LEVEL_SHFT 0x8 +#define VIDC_REG_839021_PROFILE_BMSK 0x1f +#define VIDC_REG_839021_PROFILE_SHFT 0 + +#define VIDC_REG_950374_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000308) +#define VIDC_REG_950374_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000308) +#define VIDC_REG_950374_RMSK 0xffff +#define VIDC_REG_950374_SHFT 0 +#define VIDC_REG_950374_IN \ + in_dword_masked(VIDC_REG_950374_ADDR, \ + VIDC_REG_950374_RMSK) +#define VIDC_REG_950374_INM(m) \ + in_dword_masked(VIDC_REG_950374_ADDR, m) +#define VIDC_REG_950374_OUT(v) \ + out_dword(VIDC_REG_950374_ADDR, v) +#define VIDC_REG_950374_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_950374_ADDR, m, v, \ + VIDC_REG_950374_IN); \ +} while (0) +#define VIDC_REG_950374_I_PERIOD_BMSK 0xffff +#define VIDC_REG_950374_I_PERIOD_SHFT 0 + +#define VIDC_REG_504878_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000310) +#define VIDC_REG_504878_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000310) +#define VIDC_REG_504878_RMSK 0xd +#define VIDC_REG_504878_SHFT 0 +#define VIDC_REG_504878_IN \ + in_dword_masked(VIDC_REG_504878_ADDR, \ + VIDC_REG_504878_RMSK) +#define VIDC_REG_504878_INM(m) \ + in_dword_masked(VIDC_REG_504878_ADDR, m) +#define VIDC_REG_504878_OUT(v) \ + out_dword(VIDC_REG_504878_ADDR, v) +#define VIDC_REG_504878_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_504878_ADDR, m, v, \ + VIDC_REG_504878_IN); \ +} while (0) +#define VIDC_REG_504878_FIXED_NUMBER_BMSK 0xc +#define VIDC_REG_504878_FIXED_NUMBER_SHFT 0x2 +#define VIDC_REG_504878_ENTROPY_SEL_BMSK 0x1 +#define VIDC_REG_504878_ENTROPY_SEL_SHFT 0 + +#define VIDC_REG_458130_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000314) +#define VIDC_REG_458130_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000314) +#define VIDC_REG_458130_RMSK 0xfff +#define VIDC_REG_458130_SHFT 0 +#define VIDC_REG_458130_IN \ + in_dword_masked(VIDC_REG_458130_ADDR, \ + VIDC_REG_458130_RMSK) +#define VIDC_REG_458130_INM(m) \ + in_dword_masked(VIDC_REG_458130_ADDR, m) +#define VIDC_REG_458130_OUT(v) \ + out_dword(VIDC_REG_458130_ADDR, v) +#define VIDC_REG_458130_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_458130_ADDR, m, v, \ + VIDC_REG_458130_IN); \ +} while (0) +#define VIDC_REG_458130_SLICE_ALPHA_C0_OFFSET_DIV2_BMSK \ + 0xf80 +#define VIDC_REG_458130_SLICE_ALPHA_C0_OFFSET_DIV2_SHFT \ + 0x7 +#define VIDC_REG_458130_SLICE_BETA_OFFSET_DIV2_BMSK 0x7c +#define VIDC_REG_458130_SLICE_BETA_OFFSET_DIV2_SHFT 0x2 +#define \ + \ +VIDC_REG_458130_DISABLE_DEBLOCKING_FILTER_IDC_BMSK 0x3 +#define \ + \ +VIDC_REG_458130_DISABLE_DEBLOCKING_FILTER_IDC_SHFT 0 + +#define VIDC_REG_314290_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000318) +#define VIDC_REG_314290_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000318) +#define VIDC_REG_314290_RMSK 0x1 +#define VIDC_REG_314290_SHFT 0 +#define VIDC_REG_314290_IN \ + in_dword_masked(VIDC_REG_314290_ADDR, \ + VIDC_REG_314290_RMSK) +#define VIDC_REG_314290_INM(m) \ + in_dword_masked(VIDC_REG_314290_ADDR, m) +#define VIDC_REG_314290_OUT(v) \ + out_dword(VIDC_REG_314290_ADDR, v) +#define VIDC_REG_314290_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_314290_ADDR, m, v, \ + VIDC_REG_314290_IN); \ +} while (0) +#define VIDC_REG_314290_SHORT_HD_ON_BMSK 0x1 +#define VIDC_REG_314290_SHORT_HD_ON_SHFT 0 + +#define VIDC_REG_588301_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x0000031c) +#define VIDC_REG_588301_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000031c) +#define VIDC_REG_588301_RMSK 0x1 +#define VIDC_REG_588301_SHFT 0 +#define VIDC_REG_588301_IN \ + in_dword_masked(VIDC_REG_588301_ADDR, \ + VIDC_REG_588301_RMSK) +#define VIDC_REG_588301_INM(m) \ + in_dword_masked(VIDC_REG_588301_ADDR, m) +#define VIDC_REG_588301_OUT(v) \ + out_dword(VIDC_REG_588301_ADDR, v) +#define VIDC_REG_588301_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_588301_ADDR, m, v, \ + VIDC_REG_588301_IN); \ +} while (0) +#define VIDC_REG_588301_MSLICE_ENA_BMSK 0x1 +#define VIDC_REG_588301_MSLICE_ENA_SHFT 0 + +#define VIDC_REG_1517_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000320) +#define VIDC_REG_1517_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000320) +#define VIDC_REG_1517_RMSK 0x3 +#define VIDC_REG_1517_SHFT 0 +#define VIDC_REG_1517_IN \ + in_dword_masked(VIDC_REG_1517_ADDR, \ + VIDC_REG_1517_RMSK) +#define VIDC_REG_1517_INM(m) \ + in_dword_masked(VIDC_REG_1517_ADDR, m) +#define VIDC_REG_1517_OUT(v) \ + out_dword(VIDC_REG_1517_ADDR, v) +#define VIDC_REG_1517_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_1517_ADDR, m, v, \ + VIDC_REG_1517_IN); \ +} while (0) +#define VIDC_REG_1517_MSLICE_SEL_BMSK 0x3 +#define VIDC_REG_1517_MSLICE_SEL_SHFT 0 + +#define VIDC_REG_105335_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000324) +#define VIDC_REG_105335_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000324) +#define VIDC_REG_105335_RMSK 0xffffffff +#define VIDC_REG_105335_SHFT 0 +#define VIDC_REG_105335_IN \ + in_dword_masked(VIDC_REG_105335_ADDR, \ + VIDC_REG_105335_RMSK) +#define VIDC_REG_105335_INM(m) \ + in_dword_masked(VIDC_REG_105335_ADDR, m) +#define VIDC_REG_105335_OUT(v) \ + out_dword(VIDC_REG_105335_ADDR, v) +#define VIDC_REG_105335_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_105335_ADDR, m, v, \ + VIDC_REG_105335_IN); \ +} while (0) +#define VIDC_REG_105335_MSLICE_MB_BMSK 0xffffffff +#define VIDC_REG_105335_MSLICE_MB_SHFT 0 + +#define VIDC_REG_561679_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000328) +#define VIDC_REG_561679_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000328) +#define VIDC_REG_561679_RMSK 0xffffffff +#define VIDC_REG_561679_SHFT 0 +#define VIDC_REG_561679_IN \ + in_dword_masked(VIDC_REG_561679_ADDR, \ + VIDC_REG_561679_RMSK) +#define VIDC_REG_561679_INM(m) \ + in_dword_masked(VIDC_REG_561679_ADDR, m) +#define VIDC_REG_561679_OUT(v) \ + out_dword(VIDC_REG_561679_ADDR, v) +#define VIDC_REG_561679_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_561679_ADDR, m, v, \ + VIDC_REG_561679_IN); \ +} while (0) +#define VIDC_REG_561679_MSLICE_BYTE_BMSK 0xffffffff +#define VIDC_REG_561679_MSLICE_BYTE_SHFT 0 + +#define VIDC_REG_151345_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000400) +#define VIDC_REG_151345_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000400) +#define VIDC_REG_151345_RMSK 0xffffffff +#define VIDC_REG_151345_SHFT 0 +#define VIDC_REG_151345_IN \ + in_dword_masked(VIDC_REG_151345_ADDR, \ + VIDC_REG_151345_RMSK) +#define VIDC_REG_151345_INM(m) \ + in_dword_masked(VIDC_REG_151345_ADDR, m) +#define VIDC_REG_151345_DISPLAY_Y_ADR_BMSK 0xffffffff +#define VIDC_REG_151345_DISPLAY_Y_ADR_SHFT 0 + +#define VIDC_REG_293983_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000404) +#define VIDC_REG_293983_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000404) +#define VIDC_REG_293983_RMSK 0xffffffff +#define VIDC_REG_293983_SHFT 0 +#define VIDC_REG_293983_IN \ + in_dword_masked(VIDC_REG_293983_ADDR, \ + VIDC_REG_293983_RMSK) +#define VIDC_REG_293983_INM(m) \ + in_dword_masked(VIDC_REG_293983_ADDR, m) +#define VIDC_REG_293983_DISPLAY_C_ADR_BMSK 0xffffffff +#define VIDC_REG_293983_DISPLAY_C_ADR_SHFT 0 + +#define VIDC_REG_612715_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000408) +#define VIDC_REG_612715_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000408) +#define VIDC_REG_612715_RMSK 0x3f +#define VIDC_REG_612715_SHFT 0 +#define VIDC_REG_612715_IN \ + in_dword_masked(VIDC_REG_612715_ADDR, \ + VIDC_REG_612715_RMSK) +#define VIDC_REG_612715_INM(m) \ + in_dword_masked(VIDC_REG_612715_ADDR, m) +#define VIDC_REG_612715_DISPLAY_STATUS_BMSK 0x3f +#define VIDC_REG_612715_DISPLAY_STATUS_SHFT 0 + +#define VIDC_REG_209364_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x0000040c) +#define VIDC_REG_209364_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000040c) +#define VIDC_REG_209364_RMSK 0x1 +#define VIDC_REG_209364_SHFT 0 +#define VIDC_REG_209364_IN \ + in_dword_masked(VIDC_REG_209364_ADDR, \ + VIDC_REG_209364_RMSK) +#define VIDC_REG_209364_INM(m) \ + in_dword_masked(VIDC_REG_209364_ADDR, m) +#define VIDC_REG_209364_HEADER_DONE_BMSK 0x1 +#define VIDC_REG_209364_HEADER_DONE_SHFT 0 + +#define VIDC_REG_757835_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000410) +#define VIDC_REG_757835_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000410) +#define VIDC_REG_757835_RMSK 0xffffffff +#define VIDC_REG_757835_SHFT 0 +#define VIDC_REG_757835_IN \ + in_dword_masked(VIDC_REG_757835_ADDR, \ + VIDC_REG_757835_RMSK) +#define VIDC_REG_757835_INM(m) \ + in_dword_masked(VIDC_REG_757835_ADDR, m) +#define VIDC_REG_757835_FRAME_NUM_BMSK 0xffffffff +#define VIDC_REG_757835_FRAME_NUM_SHFT 0 + +#define VIDC_REG_352831_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000414) +#define VIDC_REG_352831_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000414) +#define VIDC_REG_352831_RMSK 0xffffffff +#define VIDC_REG_352831_SHFT 0 +#define VIDC_REG_352831_IN \ + in_dword_masked(VIDC_REG_352831_ADDR, \ + VIDC_REG_352831_RMSK) +#define VIDC_REG_352831_INM(m) \ + in_dword_masked(VIDC_REG_352831_ADDR, m) +#define VIDC_REG_352831_DBG_INFO_OUTPUT0_BMSK 0xffffffff +#define VIDC_REG_352831_DBG_INFO_OUTPUT0_SHFT 0 + +#define VIDC_REG_668634_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000418) +#define VIDC_REG_668634_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000418) +#define VIDC_REG_668634_RMSK 0xffffffff +#define VIDC_REG_668634_SHFT 0 +#define VIDC_REG_668634_IN \ + in_dword_masked(VIDC_REG_668634_ADDR, \ + VIDC_REG_668634_RMSK) +#define VIDC_REG_668634_INM(m) \ + in_dword_masked(VIDC_REG_668634_ADDR, m) +#define VIDC_REG_668634_DBG_INFO_OUTPUT1_BMSK 0xffffffff +#define VIDC_REG_668634_DBG_INFO_OUTPUT1_SHFT 0 + +#define VIDC_REG_609676_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000500) +#define VIDC_REG_609676_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000500) +#define VIDC_REG_609676_RMSK 0x1 +#define VIDC_REG_609676_SHFT 0 +#define VIDC_REG_609676_IN \ + in_dword_masked(VIDC_REG_609676_ADDR, VIDC_REG_609676_RMSK) +#define VIDC_REG_609676_INM(m) \ + in_dword_masked(VIDC_REG_609676_ADDR, m) +#define VIDC_REG_609676_OUT(v) \ + out_dword(VIDC_REG_609676_ADDR, v) +#define VIDC_REG_609676_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_609676_ADDR, m, v, \ + VIDC_REG_609676_IN); \ +} while (0) +#define VIDC_REG_609676_INT_OFF_BMSK 0x1 +#define VIDC_REG_609676_INT_OFF_SHFT 0 + +#define VIDC_REG_491082_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000504) +#define VIDC_REG_491082_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000504) +#define VIDC_REG_491082_RMSK 0x1 +#define VIDC_REG_491082_SHFT 0 +#define VIDC_REG_491082_IN \ + in_dword_masked(VIDC_REG_491082_ADDR, \ + VIDC_REG_491082_RMSK) +#define VIDC_REG_491082_INM(m) \ + in_dword_masked(VIDC_REG_491082_ADDR, m) +#define VIDC_REG_491082_OUT(v) \ + out_dword(VIDC_REG_491082_ADDR, v) +#define VIDC_REG_491082_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_491082_ADDR, m, v, \ + VIDC_REG_491082_IN); \ +} while (0) +#define VIDC_REG_491082_INT_PULSE_SEL_BMSK 0x1 +#define VIDC_REG_491082_INT_PULSE_SEL_SHFT 0 + +#define VIDC_REG_614776_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000508) +#define VIDC_REG_614776_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000508) +#define VIDC_REG_614776_RMSK 0x1 +#define VIDC_REG_614776_SHFT 0 +#define VIDC_REG_614776_IN \ + in_dword_masked(VIDC_REG_614776_ADDR, \ + VIDC_REG_614776_RMSK) +#define VIDC_REG_614776_INM(m) \ + in_dword_masked(VIDC_REG_614776_ADDR, m) +#define VIDC_REG_614776_OUT(v) \ + out_dword(VIDC_REG_614776_ADDR, v) +#define VIDC_REG_614776_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_614776_ADDR, m, v, \ + VIDC_REG_614776_IN); \ +} while (0) +#define VIDC_REG_614776_INT_DONE_CLEAR_BMSK 0x1 +#define VIDC_REG_614776_INT_DONE_CLEAR_SHFT 0 + +#define VIDC_REG_982553_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x0000050c) +#define VIDC_REG_982553_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000050c) +#define VIDC_REG_982553_RMSK 0x1 +#define VIDC_REG_982553_SHFT 0 +#define VIDC_REG_982553_IN \ + in_dword_masked(VIDC_REG_982553_ADDR, \ + VIDC_REG_982553_RMSK) +#define VIDC_REG_982553_INM(m) \ + in_dword_masked(VIDC_REG_982553_ADDR, m) +#define VIDC_REG_982553_OPERATION_DONE_BMSK 0x1 +#define VIDC_REG_982553_OPERATION_DONE_SHFT 0 + +#define VIDC_REG_259967_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000510) +#define VIDC_REG_259967_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000510) +#define VIDC_REG_259967_RMSK 0x1 +#define VIDC_REG_259967_SHFT 0 +#define VIDC_REG_259967_IN \ + in_dword_masked(VIDC_REG_259967_ADDR, VIDC_REG_259967_RMSK) +#define VIDC_REG_259967_INM(m) \ + in_dword_masked(VIDC_REG_259967_ADDR, m) +#define VIDC_REG_259967_FW_DONE_BMSK 0x1 +#define VIDC_REG_259967_FW_DONE_SHFT 0 + +#define VIDC_REG_512143_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000514) +#define VIDC_REG_512143_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000514) +#define VIDC_REG_512143_RMSK 0x1f8 +#define VIDC_REG_512143_SHFT 0 +#define VIDC_REG_512143_IN \ + in_dword_masked(VIDC_REG_512143_ADDR, \ + VIDC_REG_512143_RMSK) +#define VIDC_REG_512143_INM(m) \ + in_dword_masked(VIDC_REG_512143_ADDR, m) +#define VIDC_REG_512143_FRAME_DONE_STAT_BMSK 0x100 +#define VIDC_REG_512143_FRAME_DONE_STAT_SHFT 0x8 +#define VIDC_REG_512143_DMA_DONE_STAT_BMSK 0x80 +#define VIDC_REG_512143_DMA_DONE_STAT_SHFT 0x7 +#define VIDC_REG_512143_HEADER_DONE_STAT_BMSK 0x40 +#define VIDC_REG_512143_HEADER_DONE_STAT_SHFT 0x6 +#define VIDC_REG_512143_FW_DONE_STAT_BMSK 0x20 +#define VIDC_REG_512143_FW_DONE_STAT_SHFT 0x5 +#define VIDC_REG_512143_OPERATION_FAILED_BMSK 0x10 +#define VIDC_REG_512143_OPERATION_FAILED_SHFT 0x4 +#define VIDC_REG_512143_STREAM_HDR_CHANGED_BMSK 0x8 +#define VIDC_REG_512143_STREAM_HDR_CHANGED_SHFT 0x3 + +#define VIDC_REG_418173_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000518) +#define VIDC_REG_418173_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000518) +#define VIDC_REG_418173_RMSK 0x1fa +#define VIDC_REG_418173_SHFT 0 +#define VIDC_REG_418173_IN \ + in_dword_masked(VIDC_REG_418173_ADDR, \ + VIDC_REG_418173_RMSK) +#define VIDC_REG_418173_INM(m) \ + in_dword_masked(VIDC_REG_418173_ADDR, m) +#define VIDC_REG_418173_OUT(v) \ + out_dword(VIDC_REG_418173_ADDR, v) +#define VIDC_REG_418173_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_418173_ADDR, m, v, \ + VIDC_REG_418173_IN); \ +} while (0) +#define VIDC_REG_418173_FRAME_DONE_ENABLE_BMSK 0x100 +#define VIDC_REG_418173_FRAME_DONE_ENABLE_SHFT 0x8 +#define VIDC_REG_418173_DMA_DONE_ENABLE_BMSK 0x80 +#define VIDC_REG_418173_DMA_DONE_ENABLE_SHFT 0x7 +#define VIDC_REG_418173_HEADER_DONE_ENABLE_BMSK 0x40 +#define VIDC_REG_418173_HEADER_DONE_ENABLE_SHFT 0x6 +#define VIDC_REG_418173_FW_DONE_ENABLE_BMSK 0x20 +#define VIDC_REG_418173_FW_DONE_ENABLE_SHFT 0x5 +#define VIDC_REG_418173_OPERATION_FAILED_ENABLE_BMSK 0x10 +#define VIDC_REG_418173_OPERATION_FAILED_ENABLE_SHFT 0x4 +#define VIDC_REG_418173_STREAM_HDR_CHANGED_ENABLE_BMSK 0x8 +#define VIDC_REG_418173_STREAM_HDR_CHANGED_ENABLE_SHFT 0x3 +#define VIDC_REG_418173_BUFFER_FULL_ENABLE_BMSK 0x2 +#define VIDC_REG_418173_BUFFER_FULL_ENABLE_SHFT 0x1 + +#define VIDC_REG_841539_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000600) +#define VIDC_REG_841539_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000600) +#define VIDC_REG_841539_RMSK 0x3 +#define VIDC_REG_841539_SHFT 0 +#define VIDC_REG_841539_IN \ + in_dword_masked(VIDC_REG_841539_ADDR, \ + VIDC_REG_841539_RMSK) +#define VIDC_REG_841539_INM(m) \ + in_dword_masked(VIDC_REG_841539_ADDR, m) +#define VIDC_REG_841539_OUT(v) \ + out_dword(VIDC_REG_841539_ADDR, v) +#define VIDC_REG_841539_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_841539_ADDR, m, v, \ + VIDC_REG_841539_IN); \ +} while (0) +#define VIDC_REG_841539_TILE_MODE_BMSK 0x3 +#define VIDC_REG_841539_TILE_MODE_SHFT 0 + +#define VIDC_REG_99105_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000800) +#define VIDC_REG_99105_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000800) +#define VIDC_REG_99105_RMSK 0xffffffff +#define VIDC_REG_99105_SHFT 0 +#define VIDC_REG_99105_IN \ + in_dword_masked(VIDC_REG_99105_ADDR, \ + VIDC_REG_99105_RMSK) +#define VIDC_REG_99105_INM(m) \ + in_dword_masked(VIDC_REG_99105_ADDR, m) +#define VIDC_REG_99105_OUT(v) \ + out_dword(VIDC_REG_99105_ADDR, v) +#define VIDC_REG_99105_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_99105_ADDR, m, v, \ + VIDC_REG_99105_IN); \ +} while (0) +#define VIDC_REG_99105_ENC_CUR_Y_ADDR_BMSK 0xffffffff +#define VIDC_REG_99105_ENC_CUR_Y_ADDR_SHFT 0 + +#define VIDC_REG_777113_ADDR_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000804) +#define VIDC_REG_777113_ADDR_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000804) +#define VIDC_REG_777113_ADDR_RMSK 0xffffffff +#define VIDC_REG_777113_ADDR_SHFT 0 +#define VIDC_REG_777113_ADDR_IN \ + in_dword_masked(VIDC_REG_777113_ADDR_ADDR, \ + VIDC_REG_777113_ADDR_RMSK) +#define VIDC_REG_777113_ADDR_INM(m) \ + in_dword_masked(VIDC_REG_777113_ADDR_ADDR, m) +#define VIDC_REG_777113_ADDR_OUT(v) \ + out_dword(VIDC_REG_777113_ADDR_ADDR, v) +#define VIDC_REG_777113_ADDR_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_777113_ADDR_ADDR, m, v, \ + VIDC_REG_777113_ADDR_IN); \ +} while (0) +#define VIDC_REG_777113_ADDR_ENC_CUR_C_ADDR_BMSK 0xffffffff +#define VIDC_REG_777113_ADDR_ENC_CUR_C_ADDR_SHFT 0 + +#define VIDC_REG_341928_ADDR_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x0000080c) +#define VIDC_REG_341928_ADDR_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000080c) +#define VIDC_REG_341928_ADDR_RMSK 0xffffffff +#define VIDC_REG_341928_ADDR_SHFT 0 +#define VIDC_REG_341928_ADDR_IN \ + in_dword_masked(VIDC_REG_341928_ADDR_ADDR, \ + VIDC_REG_341928_ADDR_RMSK) +#define VIDC_REG_341928_ADDR_INM(m) \ + in_dword_masked(VIDC_REG_341928_ADDR_ADDR, m) +#define VIDC_REG_341928_ADDR_OUT(v) \ + out_dword(VIDC_REG_341928_ADDR_ADDR, v) +#define VIDC_REG_341928_ADDR_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_341928_ADDR_ADDR, m, v, \ + VIDC_REG_341928_ADDR_IN); \ +} while (0) +#define VIDC_REG_341928_ADDR_ENC_DPB_ADR_BMSK 0xffffffff +#define VIDC_REG_341928_ADDR_ENC_DPB_ADR_SHFT 0 + +#define VIDC_REG_857491_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000810) +#define VIDC_REG_857491_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000810) +#define VIDC_REG_857491_RMSK 0xfff +#define VIDC_REG_857491_SHFT 0 +#define VIDC_REG_857491_IN \ + in_dword_masked(VIDC_REG_857491_ADDR, \ + VIDC_REG_857491_RMSK) +#define VIDC_REG_857491_INM(m) \ + in_dword_masked(VIDC_REG_857491_ADDR, m) +#define VIDC_REG_857491_OUT(v) \ + out_dword(VIDC_REG_857491_ADDR, v) +#define VIDC_REG_857491_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_857491_ADDR, m, v, \ + VIDC_REG_857491_IN); \ +} while (0) +#define VIDC_REG_857491_CIR_MB_NUM_BMSK 0xfff +#define VIDC_REG_857491_CIR_MB_NUM_SHFT 0 + +#define VIDC_REG_518133_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000900) +#define VIDC_REG_518133_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000900) +#define VIDC_REG_518133_RMSK 0xffffffff +#define VIDC_REG_518133_SHFT 0 +#define VIDC_REG_518133_IN \ + in_dword_masked(VIDC_REG_518133_ADDR, \ + VIDC_REG_518133_RMSK) +#define VIDC_REG_518133_INM(m) \ + in_dword_masked(VIDC_REG_518133_ADDR, m) +#define VIDC_REG_518133_OUT(v) \ + out_dword(VIDC_REG_518133_ADDR, v) +#define VIDC_REG_518133_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_518133_ADDR, m, v, \ + VIDC_REG_518133_IN); \ +} while (0) +#define VIDC_REG_518133_DEC_DPB_ADDR_BMSK 0xffffffff +#define VIDC_REG_518133_DEC_DPB_ADDR_SHFT 0 + +#define VIDC_REG_456376_ADDR_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000904) +#define VIDC_REG_456376_ADDR_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000904) +#define VIDC_REG_456376_ADDR_RMSK 0xffffffff +#define VIDC_REG_456376_ADDR_SHFT 0 +#define VIDC_REG_456376_ADDR_IN \ + in_dword_masked(VIDC_REG_456376_ADDR_ADDR, \ + VIDC_REG_456376_ADDR_RMSK) +#define VIDC_REG_456376_ADDR_INM(m) \ + in_dword_masked(VIDC_REG_456376_ADDR_ADDR, m) +#define VIDC_REG_456376_ADDR_OUT(v) \ + out_dword(VIDC_REG_456376_ADDR_ADDR, v) +#define VIDC_REG_456376_ADDR_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_456376_ADDR_ADDR, m, v, \ + VIDC_REG_456376_ADDR_IN); \ +} while (0) +#define VIDC_REG_456376_ADDR_DPB_COMV_ADDR_BMSK 0xffffffff +#define VIDC_REG_456376_ADDR_DPB_COMV_ADDR_SHFT 0 + +#define VIDC_REG_267567_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000908) +#define VIDC_REG_267567_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000908) +#define VIDC_REG_267567_RMSK 0xffffffff +#define VIDC_REG_267567_SHFT 0 +#define VIDC_REG_267567_IN \ + in_dword_masked(VIDC_REG_267567_ADDR, \ + VIDC_REG_267567_RMSK) +#define VIDC_REG_267567_INM(m) \ + in_dword_masked(VIDC_REG_267567_ADDR, m) +#define VIDC_REG_267567_OUT(v) \ + out_dword(VIDC_REG_267567_ADDR, v) +#define VIDC_REG_267567_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_267567_ADDR, m, v, \ + VIDC_REG_267567_IN); \ +} while (0) +#define VIDC_REG_798486_ADDR_BMSK 0xffffffff +#define VIDC_REG_798486_ADDR_SHFT 0 + +#define VIDC_REG_105770_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x0000090c) +#define VIDC_REG_105770_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000090c) +#define VIDC_REG_105770_RMSK 0xff +#define VIDC_REG_105770_SHFT 0 +#define VIDC_REG_105770_IN \ + in_dword_masked(VIDC_REG_105770_ADDR, \ + VIDC_REG_105770_RMSK) +#define VIDC_REG_105770_INM(m) \ + in_dword_masked(VIDC_REG_105770_ADDR, m) +#define VIDC_REG_105770_DPB_SIZE_BMSK 0xff +#define VIDC_REG_105770_DPB_SIZE_SHFT 0 + +#define VIDC_REG_58211_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000a00) +#define VIDC_REG_58211_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a00) +#define VIDC_REG_58211_RMSK 0x33f +#define VIDC_REG_58211_SHFT 0 +#define VIDC_REG_58211_IN \ + in_dword_masked(VIDC_REG_58211_ADDR, \ + VIDC_REG_58211_RMSK) +#define VIDC_REG_58211_INM(m) \ + in_dword_masked(VIDC_REG_58211_ADDR, m) +#define VIDC_REG_58211_OUT(v) \ + out_dword(VIDC_REG_58211_ADDR, v) +#define VIDC_REG_58211_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_58211_ADDR, m, v, \ + VIDC_REG_58211_IN); \ +} while (0) +#define VIDC_REG_58211_FR_RC_EN_BMSK 0x200 +#define VIDC_REG_58211_FR_RC_EN_SHFT 0x9 +#define VIDC_REG_58211_MB_RC_EN_BMSK 0x100 +#define VIDC_REG_58211_MB_RC_EN_SHFT 0x8 +#define VIDC_REG_58211_FRAME_QP_BMSK 0x3f +#define VIDC_REG_58211_FRAME_QP_SHFT 0 + +#define VIDC_REG_548359_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000a04) +#define VIDC_REG_548359_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a04) +#define VIDC_REG_548359_RMSK 0x3f +#define VIDC_REG_548359_SHFT 0 +#define VIDC_REG_548359_IN \ + in_dword_masked(VIDC_REG_548359_ADDR, \ + VIDC_REG_548359_RMSK) +#define VIDC_REG_548359_INM(m) \ + in_dword_masked(VIDC_REG_548359_ADDR, m) +#define VIDC_REG_548359_OUT(v) \ + out_dword(VIDC_REG_548359_ADDR, v) +#define VIDC_REG_548359_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_548359_ADDR, m, v, \ + VIDC_REG_548359_IN); \ +} while (0) +#define VIDC_REG_548359_P_FRAME_QP_BMSK 0x3f +#define VIDC_REG_548359_P_FRAME_QP_SHFT 0 + +#define VIDC_REG_174150_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000a08) +#define VIDC_REG_174150_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a08) +#define VIDC_REG_174150_RMSK 0xffffffff +#define VIDC_REG_174150_SHFT 0 +#define VIDC_REG_174150_IN \ + in_dword_masked(VIDC_REG_174150_ADDR, \ + VIDC_REG_174150_RMSK) +#define VIDC_REG_174150_INM(m) \ + in_dword_masked(VIDC_REG_174150_ADDR, m) +#define VIDC_REG_174150_OUT(v) \ + out_dword(VIDC_REG_174150_ADDR, v) +#define VIDC_REG_174150_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_174150_ADDR, m, v, \ + VIDC_REG_174150_IN); \ +} while (0) +#define VIDC_REG_174150_BIT_RATE_BMSK 0xffffffff +#define VIDC_REG_174150_BIT_RATE_SHFT 0 + +#define VIDC_REG_734318_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000a0c) +#define VIDC_REG_734318_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a0c) +#define VIDC_REG_734318_RMSK 0x3f3f +#define VIDC_REG_734318_SHFT 0 +#define VIDC_REG_734318_IN \ + in_dword_masked(VIDC_REG_734318_ADDR, \ + VIDC_REG_734318_RMSK) +#define VIDC_REG_734318_INM(m) \ + in_dword_masked(VIDC_REG_734318_ADDR, m) +#define VIDC_REG_734318_OUT(v) \ + out_dword(VIDC_REG_734318_ADDR, v) +#define VIDC_REG_734318_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_734318_ADDR, m, v, \ + VIDC_REG_734318_IN); \ +} while (0) +#define VIDC_REG_734318_MAX_QP_BMSK 0x3f00 +#define VIDC_REG_734318_MAX_QP_SHFT 0x8 +#define VIDC_REG_734318_MIN_QP_BMSK 0x3f +#define VIDC_REG_734318_MIN_QP_SHFT 0 + +#define VIDC_REG_677784_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000a10) +#define VIDC_REG_677784_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a10) +#define VIDC_REG_677784_RMSK 0xffff +#define VIDC_REG_677784_SHFT 0 +#define VIDC_REG_677784_IN \ + in_dword_masked(VIDC_REG_677784_ADDR, \ + VIDC_REG_677784_RMSK) +#define VIDC_REG_677784_INM(m) \ + in_dword_masked(VIDC_REG_677784_ADDR, m) +#define VIDC_REG_677784_OUT(v) \ + out_dword(VIDC_REG_677784_ADDR, v) +#define VIDC_REG_677784_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_677784_ADDR, m, v, \ + VIDC_REG_677784_IN); \ +} while (0) +#define VIDC_REG_677784_REACT_PARA_BMSK 0xffff +#define VIDC_REG_677784_REACT_PARA_SHFT 0 + +#define VIDC_REG_995041_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000a14) +#define VIDC_REG_995041_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a14) +#define VIDC_REG_995041_RMSK 0xf +#define VIDC_REG_995041_SHFT 0 +#define VIDC_REG_995041_IN \ + in_dword_masked(VIDC_REG_995041_ADDR, \ + VIDC_REG_995041_RMSK) +#define VIDC_REG_995041_INM(m) \ + in_dword_masked(VIDC_REG_995041_ADDR, m) +#define VIDC_REG_995041_OUT(v) \ + out_dword(VIDC_REG_995041_ADDR, v) +#define VIDC_REG_995041_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_995041_ADDR, m, v, \ + VIDC_REG_995041_IN); \ +} while (0) +#define VIDC_REG_995041_DARK_DISABLE_BMSK 0x8 +#define VIDC_REG_995041_DARK_DISABLE_SHFT 0x3 +#define VIDC_REG_995041_SMOOTH_DISABLE_BMSK 0x4 +#define VIDC_REG_995041_SMOOTH_DISABLE_SHFT 0x2 +#define VIDC_REG_995041_STATIC_DISABLE_BMSK 0x2 +#define VIDC_REG_995041_STATIC_DISABLE_SHFT 0x1 +#define VIDC_REG_995041_ACT_DISABLE_BMSK 0x1 +#define VIDC_REG_995041_ACT_DISABLE_SHFT 0 + +#define VIDC_REG_273649_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000a18) +#define VIDC_REG_273649_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a18) +#define VIDC_REG_273649_RMSK 0x3f +#define VIDC_REG_273649_SHFT 0 +#define VIDC_REG_273649_IN \ + in_dword_masked(VIDC_REG_273649_ADDR, VIDC_REG_273649_RMSK) +#define VIDC_REG_273649_INM(m) \ + in_dword_masked(VIDC_REG_273649_ADDR, m) +#define VIDC_REG_273649_QP_OUT_BMSK 0x3f +#define VIDC_REG_273649_QP_OUT_SHFT 0 + +#define VIDC_REG_548823_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000b00) +#define VIDC_REG_548823_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000b00) +#define VIDC_REG_548823_RMSK 0xffffffff +#define VIDC_REG_548823_SHFT 0 +#define VIDC_REG_548823_IN \ + in_dword_masked(VIDC_REG_548823_ADDR, \ + VIDC_REG_548823_RMSK) +#define VIDC_REG_548823_INM(m) \ + in_dword_masked(VIDC_REG_548823_ADDR, m) +#define VIDC_REG_548823_720P_VERSION_BMSK 0xffffffff +#define VIDC_REG_548823_720P_VERSION_SHFT 0 + +#define VIDC_REG_881638_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000c00) +#define VIDC_REG_881638_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c00) +#define VIDC_REG_881638_RMSK 0xffffffff +#define VIDC_REG_881638_SHFT 0 +#define VIDC_REG_881638_IN \ + in_dword_masked(VIDC_REG_881638_ADDR, \ + VIDC_REG_881638_RMSK) +#define VIDC_REG_881638_INM(m) \ + in_dword_masked(VIDC_REG_881638_ADDR, m) +#define VIDC_REG_881638_CROP_RIGHT_OFFSET_BMSK 0xffff0000 +#define VIDC_REG_881638_CROP_RIGHT_OFFSET_SHFT 0x10 +#define VIDC_REG_881638_CROP_LEFT_OFFSET_BMSK 0xffff +#define VIDC_REG_881638_CROP_LEFT_OFFSET_SHFT 0 + +#define VIDC_REG_161486_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000c04) +#define VIDC_REG_161486_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c04) +#define VIDC_REG_161486_RMSK 0xffffffff +#define VIDC_REG_161486_SHFT 0 +#define VIDC_REG_161486_IN \ + in_dword_masked(VIDC_REG_161486_ADDR, \ + VIDC_REG_161486_RMSK) +#define VIDC_REG_161486_INM(m) \ + in_dword_masked(VIDC_REG_161486_ADDR, m) +#define VIDC_REG_161486_CROP_BOTTOM_OFFSET_BMSK 0xffff0000 +#define VIDC_REG_161486_CROP_BOTTOM_OFFSET_SHFT 0x10 +#define VIDC_REG_161486_CROP_TOP_OFFSET_BMSK 0xffff +#define VIDC_REG_161486_CROP_TOP_OFFSET_SHFT 0 + +#define VIDC_REG_580603_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000c08) +#define VIDC_REG_580603_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c08) +#define VIDC_REG_580603_RMSK 0xffffffff +#define VIDC_REG_580603_SHFT 0 +#define VIDC_REG_580603_IN \ + in_dword_masked(VIDC_REG_580603_ADDR, \ + VIDC_REG_580603_RMSK) +#define VIDC_REG_580603_INM(m) \ + in_dword_masked(VIDC_REG_580603_ADDR, m) +#define VIDC_REG_580603_720P_DEC_FRM_SIZE_BMSK 0xffffffff +#define VIDC_REG_580603_720P_DEC_FRM_SIZE_SHFT 0 + + +#define VIDC_REG_606447_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000c0c) +#define VIDC_REG_606447_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c0c) +#define VIDC_REG_606447_RMSK 0xff1f +#define VIDC_REG_606447_SHFT 0 +#define VIDC_REG_606447_IN \ + in_dword_masked(VIDC_REG_606447_ADDR, \ + VIDC_REG_606447_RMSK) +#define VIDC_REG_606447_INM(m) \ + in_dword_masked(VIDC_REG_606447_ADDR, m) +#define VIDC_REG_606447_OUT(v) \ + out_dword(VIDC_REG_606447_ADDR, v) +#define VIDC_REG_606447_OUTM(m, v) \ + out_dword_masked_ns(VIDC_REG_606447_ADDR, \ + m, v, VIDC_REG_606447_IN); \ + +#define VIDC_REG_606447_DIS_PIC_LEVEL_BMSK 0xff00 +#define VIDC_REG_606447_DIS_PIC_LEVEL_SHFT 0x8 +#define VIDC_REG_606447_DISP_PIC_PROFILE_BMSK 0x1f +#define VIDC_REG_606447_DISP_PIC_PROFILE_SHFT 0 + +#define VIDC_REG_854281_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000c10) +#define VIDC_REG_854281_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c10) +#define VIDC_REG_854281_RMSK 0xffffffff +#define VIDC_REG_854281_SHFT 0 +#define VIDC_REG_854281_IN \ + in_dword_masked(VIDC_REG_854281_ADDR, \ + VIDC_REG_854281_RMSK) +#define VIDC_REG_854281_INM(m) \ + in_dword_masked(VIDC_REG_854281_ADDR, m) +#define VIDC_REG_854281_MIN_DPB_SIZE_BMSK 0xffffffff +#define VIDC_REG_854281_MIN_DPB_SIZE_SHFT 0 + + +#define VIDC_REG_381535_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000c14) +#define VIDC_REG_381535_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c14) +#define VIDC_REG_381535_RMSK 0xffffffff +#define VIDC_REG_381535_SHFT 0 +#define VIDC_REG_381535_IN \ + in_dword_masked(VIDC_REG_381535_ADDR, \ + VIDC_REG_381535_RMSK) +#define VIDC_REG_381535_INM(m) \ + in_dword_masked(VIDC_REG_381535_ADDR, m) +#define VIDC_REG_381535_720P_FW_STATUS_BMSK 0xffffffff +#define VIDC_REG_381535_720P_FW_STATUS_SHFT 0 + + +#define VIDC_REG_347105_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000c18) +#define VIDC_REG_347105_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c18) +#define VIDC_REG_347105_RMSK 0xffffffff +#define VIDC_REG_347105_SHFT 0 +#define VIDC_REG_347105_IN \ + in_dword_masked(VIDC_REG_347105_ADDR, \ + VIDC_REG_347105_RMSK) +#define VIDC_REG_347105_INM(m) \ + in_dword_masked(VIDC_REG_347105_ADDR, m) +#define VIDC_REG_347105_FREE_LUMA_DPB_BMSK 0xffffffff +#define VIDC_REG_347105_FREE_LUMA_DPB_SHFT 0 + + +#define VIDC_REG_62325_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000d00) +#define VIDC_REG_62325_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d00) +#define VIDC_REG_62325_RMSK 0xf +#define VIDC_REG_62325_SHFT 0 +#define VIDC_REG_62325_IN \ + in_dword_masked(VIDC_REG_62325_ADDR, \ + VIDC_REG_62325_RMSK) +#define VIDC_REG_62325_INM(m) \ + in_dword_masked(VIDC_REG_62325_ADDR, m) +#define VIDC_REG_62325_OUT(v) \ + out_dword(VIDC_REG_62325_ADDR, v) +#define VIDC_REG_62325_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_62325_ADDR, m, v, \ + VIDC_REG_62325_IN); \ +} while (0) +#define VIDC_REG_62325_COMMAND_TYPE_BMSK 0xf +#define VIDC_REG_62325_COMMAND_TYPE_SHFT 0 + +#define VIDC_REG_101184_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000d04) +#define VIDC_REG_101184_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d04) +#define VIDC_REG_101184_RMSK 0xffffffff +#define VIDC_REG_101184_SHFT 0 +#define VIDC_REG_101184_OUT(v) \ + out_dword(VIDC_REG_101184_ADDR, v) + +#define VIDC_REG_490443_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000d08) +#define VIDC_REG_490443_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d08) +#define VIDC_REG_490443_RMSK \ + 0xffffffff +#define \ + \ +VIDC_REG_490443_SHFT 0 +#define VIDC_REG_490443_OUT(v) \ + out_dword(VIDC_REG_490443_ADDR, v) + +#define VIDC_REG_625444_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000d14) +#define VIDC_REG_625444_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d14) +#define VIDC_REG_625444_RMSK 0xffffffff +#define VIDC_REG_625444_SHFT 0 +#define VIDC_REG_625444_IN \ + in_dword_masked(VIDC_REG_625444_ADDR, \ + VIDC_REG_625444_RMSK) +#define VIDC_REG_625444_INM(m) \ + in_dword_masked(VIDC_REG_625444_ADDR, m) +#define VIDC_REG_625444_OUT(v) \ + out_dword(VIDC_REG_625444_ADDR, v) +#define VIDC_REG_625444_OUTM(m, v) \ +do { \ + out_dword_masked_ns(VIDC_REG_625444_ADDR, m, v, \ + VIDC_REG_625444_IN); \ +} while (0) +#define VIDC_REG_625444_FRAME_RATE_BMSK 0xffffffff +#define VIDC_REG_625444_FRAME_RATE_SHFT 0 + +#define VIDC_REG_64895_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000e00) +#define VIDC_REG_64895_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e00) +#define VIDC_REG_64895_RMSK 0xffffffff +#define VIDC_REG_64895_SHFT 0 +#define VIDC_REG_64895_OUT(v) \ + out_dword(VIDC_REG_64895_ADDR, v) + +#define VIDC_REG_965480_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000e04) +#define VIDC_REG_965480_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e04) +#define VIDC_REG_965480_RMSK 0x1 +#define VIDC_REG_965480_SHFT 0 +#define VIDC_REG_965480_OUT(v) \ + out_dword(VIDC_REG_965480_ADDR, v) + +#define VIDC_REG_804959_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000e08) +#define VIDC_REG_804959_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e08) +#define VIDC_REG_804959_RMSK 0x7 +#define VIDC_REG_804959_SHFT 0 +#define VIDC_REG_804959_OUT(v) \ + out_dword(VIDC_REG_804959_ADDR, v) + +#define VIDC_REG_257463_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000e10) +#define VIDC_REG_257463_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e10) +#define VIDC_REG_257463_RMSK 0xffffffff +#define VIDC_REG_257463_SHFT 0 +#define VIDC_REG_257463_IN \ + in_dword_masked(VIDC_REG_257463_ADDR, \ + VIDC_REG_257463_RMSK) +#define VIDC_REG_257463_INM(m) \ + in_dword_masked(VIDC_REG_257463_ADDR, m) +#define VIDC_REG_257463_MIN_NUM_DPB_BMSK 0xffffffff +#define VIDC_REG_257463_MIN_NUM_DPB_SHFT 0 + +#define VIDC_REG_883500_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000e14) +#define VIDC_REG_883500_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e14) +#define VIDC_REG_883500_RMSK 0xffffffff +#define VIDC_REG_883500_SHFT 0 +#define VIDC_REG_883500_OUT(v) \ + out_dword(VIDC_REG_883500_ADDR, v) + +#define VIDC_REG_615716_ADDR(n) \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000e18 + 4 * (n)) +#define VIDC_REG_615716_PHYS(n) \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e18 + 4 * (n)) +#define VIDC_REG_615716_RMSK 0xffffffff +#define VIDC_REG_615716_SHFT 0 +#define VIDC_REG_615716_OUTI(n, v) \ + out_dword(VIDC_REG_615716_ADDR(n), v) + +#define VIDC_REG_603032_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000e98) +#define VIDC_REG_603032_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e98) +#define VIDC_REG_603032_RMSK 0xffffffff +#define VIDC_REG_603032_SHFT 0 +#define VIDC_REG_603032_OUT(v) \ + out_dword(VIDC_REG_603032_ADDR, v) + +#define VIDC_REG_300310_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000e9c) +#define VIDC_REG_300310_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e9c) +#define VIDC_REG_300310_RMSK 0xffffffff +#define VIDC_REG_300310_SHFT 0 +#define VIDC_REG_300310_IN \ + in_dword_masked(VIDC_REG_300310_ADDR, \ + VIDC_REG_300310_RMSK) +#define VIDC_REG_300310_INM(m) \ + in_dword_masked(VIDC_REG_300310_ADDR, m) +#define VIDC_REG_300310_ERROR_STATUS_BMSK 0xffffffff +#define VIDC_REG_300310_ERROR_STATUS_SHFT 0 + +#define VIDC_REG_792026_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000ea0) +#define VIDC_REG_792026_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ea0) +#define VIDC_REG_792026_RMSK 0xffffffff +#define VIDC_REG_792026_SHFT 0 +#define VIDC_REG_792026_OUT(v) \ + out_dword(VIDC_REG_792026_ADDR, v) + +#define VIDC_REG_844152_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000ea4) +#define VIDC_REG_844152_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ea4) +#define VIDC_REG_844152_RMSK 0xffffffff +#define VIDC_REG_844152_SHFT 0 +#define VIDC_REG_844152_OUT(v) \ + out_dword(VIDC_REG_844152_ADDR, v) + +#define VIDC_REG_370409_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000ea8) +#define VIDC_REG_370409_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ea8) +#define VIDC_REG_370409_RMSK 0xffffffff +#define VIDC_REG_370409_SHFT 0 +#define VIDC_REG_370409_IN \ + in_dword_masked(VIDC_REG_370409_ADDR, \ + VIDC_REG_370409_RMSK) +#define VIDC_REG_370409_INM(m) \ + in_dword_masked(VIDC_REG_370409_ADDR, m) +#define VIDC_REG_370409_GET_FRAME_TAG_TOP_BMSK 0xffffffff +#define VIDC_REG_370409_GET_FRAME_TAG_TOP_SHFT 0 + +#define VIDC_REG_147682_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000eac) +#define VIDC_REG_147682_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000eac) +#define VIDC_REG_147682_RMSK 0x1 +#define VIDC_REG_147682_SHFT 0 +#define VIDC_REG_147682_OUT(v) \ + out_dword(VIDC_REG_147682_ADDR, v) + +#define VIDC_REG_407718_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000eb0) +#define VIDC_REG_407718_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000eb0) +#define VIDC_REG_407718_RMSK 0xffffffff +#define VIDC_REG_407718_SHFT 0 +#define VIDC_REG_407718_OUT(v) \ + out_dword(VIDC_REG_407718_ADDR, v) + +#define VIDC_REG_697961_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000eb4) +#define VIDC_REG_697961_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000eb4) +#define VIDC_REG_697961_RMSK 0x3 +#define VIDC_REG_697961_SHFT 0 +#define VIDC_REG_697961_IN \ + in_dword_masked(VIDC_REG_697961_ADDR, \ + VIDC_REG_697961_RMSK) +#define VIDC_REG_697961_INM(m) \ + in_dword_masked(VIDC_REG_697961_ADDR, m) +#define VIDC_REG_697961_FRAME_TYPE_BMSK 0x3 +#define VIDC_REG_697961_FRAME_TYPE_SHFT 0 + + +#define VIDC_REG_613254_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000eb8) +#define VIDC_REG_613254_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000eb8) +#define VIDC_REG_613254_RMSK 0x1 +#define VIDC_REG_613254_SHFT 0 +#define VIDC_REG_613254_IN \ + in_dword_masked(VIDC_REG_613254_ADDR, \ + VIDC_REG_613254_RMSK) +#define VIDC_REG_613254_INM(m) \ + in_dword_masked(VIDC_REG_613254_ADDR, m) +#define VIDC_REG_613254_METADATA_STATUS_BMSK 0x1 +#define VIDC_REG_613254_METADATA_STATUS_SHFT 0 +#define VIDC_REG_441270_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000ebc) +#define VIDC_REG_441270_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ebc) +#define VIDC_REG_441270_RMSK 0xf +#define VIDC_REG_441270_SHFT 0 +#define VIDC_REG_441270_IN \ + in_dword_masked(VIDC_REG_441270_ADDR, \ + VIDC_REG_441270_RMSK) +#define VIDC_REG_441270_INM(m) \ + in_dword_masked(VIDC_REG_441270_ADDR, m) +#define VIDC_REG_441270_DATA_PARTITIONED_BMSK 0x8 +#define VIDC_REG_441270_DATA_PARTITIONED_SHFT 0x3 + +#define VIDC_REG_441270_FRAME_TYPE_BMSK 0x7 +#define VIDC_REG_441270_FRAME_TYPE_SHFT 0 + +#define VIDC_REG_724381_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000ec0) +#define VIDC_REG_724381_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ec0) +#define VIDC_REG_724381_RMSK 0x3 +#define VIDC_REG_724381_SHFT 0 +#define VIDC_REG_724381_IN \ + in_dword_masked(VIDC_REG_724381_ADDR, \ + VIDC_REG_724381_RMSK) +#define VIDC_REG_724381_INM(m) \ + in_dword_masked(VIDC_REG_724381_ADDR, m) +#define VIDC_REG_724381_OPERATION_FAILED_BMSK 0x2 +#define VIDC_REG_724381_OPERATION_FAILED_SHFT 0x1 +#define VIDC_REG_724381_RESOLUTION_CHANGE_BMSK 0x1 +#define VIDC_REG_724381_RESOLUTION_CHANGE_SHFT 0 + +#define VIDC_REG_854681_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000ec4) +#define VIDC_REG_854681_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ec4) +#define VIDC_REG_854681_RMSK 0x7f +#define VIDC_REG_854681_SHFT 0 +#define VIDC_REG_854681_OUT(v) \ + out_dword(VIDC_REG_854681_ADDR, v) + +#define VIDC_REG_128234_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000ec8) +#define VIDC_REG_128234_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ec8) +#define VIDC_REG_128234_RMSK 0xffff000f +#define VIDC_REG_128234_SHFT 0 +#define VIDC_REG_128234_OUT(v) \ + out_dword(VIDC_REG_128234_ADDR, v) + +#define VIDC_REG_1137_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000ecc) +#define VIDC_REG_1137_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ecc) +#define VIDC_REG_1137_RMSK 0xffffffff +#define VIDC_REG_1137_SHFT 0 +#define VIDC_REG_1137_IN \ + in_dword_masked(VIDC_REG_1137_ADDR, \ + VIDC_REG_1137_RMSK) +#define VIDC_REG_1137_INM(m) \ + in_dword_masked(VIDC_REG_1137_ADDR, m) +#define VIDC_REG_1137_METADATA_DISPLAY_INDEX_BMSK \ + 0xffffffff +#define \ + \ +VIDC_REG_1137_METADATA_DISPLAY_INDEX_SHFT 0 + +#define VIDC_REG_988552_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000ed0) +#define VIDC_REG_988552_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ed0) +#define VIDC_REG_988552_RMSK 0xffffffff +#define VIDC_REG_988552_SHFT 0 +#define VIDC_REG_988552_OUT(v) \ + out_dword(VIDC_REG_988552_ADDR, v) + +#define VIDC_REG_319934_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000ed4) +#define VIDC_REG_319934_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ed4) +#define VIDC_REG_319934_RMSK 0xffffffff +#define VIDC_REG_319934_SHFT 0 +#define VIDC_REG_319934_OUT(v) \ + out_dword(VIDC_REG_319934_ADDR, v) + +#define VIDC_REG_679165_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000ed8) +#define VIDC_REG_679165_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ed8) +#define VIDC_REG_679165_RMSK 0xffffffff +#define VIDC_REG_679165_SHFT 0 +#define VIDC_REG_679165_IN \ + in_dword_masked(VIDC_REG_679165_ADDR, \ + VIDC_REG_679165_RMSK) +#define VIDC_REG_679165_INM(m) \ + in_dword_masked(VIDC_REG_679165_ADDR, m) +#define VIDC_REG_679165_PIC_TIME_TOP_BMSK 0xffffffff +#define VIDC_REG_679165_PIC_TIME_TOP_SHFT 0 + +#define VIDC_REG_374150_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000edc) +#define VIDC_REG_374150_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000edc) +#define VIDC_REG_374150_RMSK 0xffffffff +#define VIDC_REG_374150_SHFT 0 +#define VIDC_REG_374150_IN \ + in_dword_masked(VIDC_REG_374150_ADDR, \ + VIDC_REG_374150_RMSK) +#define VIDC_REG_374150_INM(m) \ + in_dword_masked(VIDC_REG_374150_ADDR, m) +#define VIDC_REG_374150_PIC_TIME_BOTTOM_BMSK 0xffffffff +#define VIDC_REG_374150_PIC_TIME_BOTTOM_SHFT 0 + +#define VIDC_REG_94750_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000ee0) +#define VIDC_REG_94750_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ee0) +#define VIDC_REG_94750_RMSK 0xffffffff +#define VIDC_REG_94750_SHFT 0 +#define VIDC_REG_94750_OUT(v) \ + out_dword(VIDC_REG_94750_ADDR, v) + +#define VIDC_REG_438677_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000ee4) +#define VIDC_REG_438677_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ee4) +#define VIDC_REG_438677_RMSK 0xffffffff +#define VIDC_REG_438677_SHFT 0 +#define VIDC_REG_438677_IN \ + in_dword_masked(VIDC_REG_438677_ADDR, \ + VIDC_REG_438677_RMSK) +#define VIDC_REG_438677_INM(m) \ + in_dword_masked(VIDC_REG_438677_ADDR, m) +#define VIDC_REG_438677_GET_FRAME_TAG_BOTTOM_BMSK 0xffffffff +#define VIDC_REG_438677_GET_FRAME_TAG_BOTTOM_SHFT 0 + +#define VIDC_REG_76706_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000ee8) +#define VIDC_REG_76706_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ee8) +#define VIDC_REG_76706_RMSK 0x1 +#define VIDC_REG_76706_SHFT 0 +#define VIDC_REG_76706_OUT(v) \ + out_dword(VIDC_REG_76706_ADDR, v) + +#define VIDC_REG_809984_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00001000) +#define VIDC_REG_809984_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00001000) +#define VIDC_REG_809984_RMSK 0xffff0007 +#define VIDC_REG_809984_SHFT 0 +#define VIDC_REG_809984_IN \ + in_dword_masked(VIDC_REG_809984_ADDR, VIDC_REG_809984_RMSK) +#define VIDC_REG_809984_INM(m) \ + in_dword_masked(VIDC_REG_809984_ADDR, m) +#define VIDC_REG_809984_720PV_720P_WRAPPER_VERSION_BMSK 0xffff0000 +#define VIDC_REG_809984_720PV_720P_WRAPPER_VERSION_SHFT 0x10 +#define VIDC_REG_809984_TEST_MUX_SEL_BMSK 0x7 +#define VIDC_REG_809984_TEST_MUX_SEL_SHFT 0 + + +#define VIDC_REG_699747_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000d0c) +#define VIDC_REG_699747_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d0c) +#define VIDC_REG_699747_RMSK 0xffffffff +#define VIDC_REG_699747_SHFT 0 +#define VIDC_REG_699747_OUT(v) \ + out_dword(VIDC_REG_699747_ADDR, v) + +#define VIDC_REG_166247_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000d10) +#define VIDC_REG_166247_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d10) +#define VIDC_REG_166247_RMSK 0xffffffff +#define VIDC_REG_166247_SHFT 0 +#define VIDC_REG_166247_OUT(v) \ + out_dword(VIDC_REG_166247_ADDR, v) + +#define VIDC_REG_486169_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000d18) +#define VIDC_REG_486169_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d18) +#define VIDC_REG_486169_RMSK 0xffffffff +#define VIDC_REG_486169_SHFT 0 +#define VIDC_REG_486169_OUT(v) \ + out_dword(VIDC_REG_486169_ADDR, v) + +#define VIDC_REG_926519_ADDR \ + (VIDC_720P_WRAPPER_REG_BASE + 0x00000d1c) +#define VIDC_REG_926519_PHYS \ + (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d1c) +#define VIDC_REG_926519_RMSK 0xffffffff +#define VIDC_REG_926519_SHFT 0 +#define VIDC_REG_926519_OUT(v) \ + out_dword(VIDC_REG_926519_ADDR, v) + +/** List all the levels and their register valus */ + +#define VIDC_720P_PROFILE_MPEG4_SP 0 +#define VIDC_720P_PROFILE_MPEG4_ASP 1 +#define VIDC_720P_PROFILE_H264_BASELINE 0 +#define VIDC_720P_PROFILE_H264_MAIN 1 +#define VIDC_720P_PROFILE_H264_HIGH 2 +#define VIDC_720P_PROFILE_H263_BASELINE 0 + +#define VIDC_720P_PROFILE_VC1_SP 0 +#define VIDC_720P_PROFILE_VC1_MAIN 1 +#define VIDC_720P_PROFILE_VC1_ADV 2 +#define VIDC_720P_PROFILE_MPEG2_MAIN 4 +#define VIDC_720P_PROFILE_MPEG2_SP 5 + +#define VIDC_720P_MPEG4_LEVEL0 0 +#define VIDC_720P_MPEG4_LEVEL0b 9 +#define VIDC_720P_MPEG4_LEVEL1 1 +#define VIDC_720P_MPEG4_LEVEL2 2 +#define VIDC_720P_MPEG4_LEVEL3 3 +#define VIDC_720P_MPEG4_LEVEL3b 7 +#define VIDC_720P_MPEG4_LEVEL4a 4 +#define VIDC_720P_MPEG4_LEVEL5 5 +#define VIDC_720P_MPEG4_LEVEL6 6 + +#define VIDC_720P_H264_LEVEL1 10 +#define VIDC_720P_H264_LEVEL1b 9 +#define VIDC_720P_H264_LEVEL1p1 11 +#define VIDC_720P_H264_LEVEL1p2 12 +#define VIDC_720P_H264_LEVEL1p3 13 +#define VIDC_720P_H264_LEVEL2 20 +#define VIDC_720P_H264_LEVEL2p1 21 +#define VIDC_720P_H264_LEVEL2p2 22 +#define VIDC_720P_H264_LEVEL3 30 +#define VIDC_720P_H264_LEVEL3p1 31 +#define VIDC_720P_H264_LEVEL3p2 32 + +#define VIDC_720P_H263_LEVEL10 10 +#define VIDC_720P_H263_LEVEL20 20 +#define VIDC_720P_H263_LEVEL30 30 +#define VIDC_720P_H263_LEVEL40 40 +#define VIDC_720P_H263_LEVEL45 45 +#define VIDC_720P_H263_LEVEL50 50 +#define VIDC_720P_H263_LEVEL60 60 +#define VIDC_720P_H263_LEVEL70 70 + +#define VIDC_720P_VC1_LEVEL_LOW 0 +#define VIDC_720P_VC1_LEVEL_MED 2 +#define VIDC_720P_VC1_LEVEL_HIGH 4 +#define VIDC_720P_VC1_LEVEL0 0 +#define VIDC_720P_VC1_LEVEL1 1 +#define VIDC_720P_VC1_LEVEL2 2 +#define VIDC_720P_VC1_LEVEL3 3 +#define VIDC_720P_VC1_LEVEL4 4 + +#define VIDCL_720P_MPEG2_LEVEL_LOW 10 +#define VIDCL_720P_MPEG2_LEVEL_MAIN 8 +#define VIDCL_720P_MPEG2_LEVEL_HIGH14 6 + +#define VIDC_720P_CMD_CHSET 0x0 +#define VIDC_720P_CMD_CHEND 0x2 +#define VIDC_720P_CMD_INITCODEC 0x3 +#define VIDC_720P_CMD_FRAMERUN 0x4 +#define VIDC_720P_CMD_INITBUFFERS 0x5 +#define VIDC_720P_CMD_FRAMERUN_REALLOCATE 0x6 +#define VIDC_720P_CMD_MFC_ENGINE_RESET 0x7 + +enum vidc_720p_endian_type { + VIDC_720P_BIG_ENDIAN = 0x0, + VIDC_720P_LITTLE_ENDIAN = 0x1 +}; + +enum vidc_720p_memory_access_method_type { + VIDC_720P_TILE_LINEAR = 0, + VIDC_720P_TILE_16x16 = 2, + VIDC_720P_TILE_64x32 = 3 +}; + +enum vidc_720p_interrupt_control_mode_type { + VIDC_720P_INTERRUPT_MODE = 0, + VIDC_720P_POLL_MODE = 1 +}; + +enum vidc_720p_interrupt_level_selection_type { + VIDC_720P_INTERRUPT_LEVEL_SEL = 0, + VIDC_720P_INTERRUPT_PULSE_SEL = 1 +}; + +#define VIDC_720P_INTR_BUFFER_FULL 0x002 +#define VIDC_720P_INTR_FW_DONE 0x020 +#define VIDC_720P_INTR_HEADER_DONE 0x040 +#define VIDC_720P_INTR_DMA_DONE 0x080 +#define VIDC_720P_INTR_FRAME_DONE 0x100 + +enum vidc_720p_enc_dec_selection_type { + VIDC_720P_DECODER = 0, + VIDC_720P_ENCODER = 1 +}; + +enum vidc_720p_codec_type { + VIDC_720P_MPEG4 = 0, + VIDC_720P_H264 = 1, + VIDC_720P_DIVX = 2, + VIDC_720P_XVID = 3, + VIDC_720P_H263 = 4, + VIDC_720P_MPEG2 = 5, + VIDC_720P_VC1 = 6 +}; + +enum vidc_720p_frame_type { + VIDC_720P_NOTCODED = 0, + VIDC_720P_IFRAME = 1, + VIDC_720P_PFRAME = 2, + VIDC_720P_BFRAME = 3 +}; + +enum vidc_720p_entropy_sel_type { + VIDC_720P_ENTROPY_SEL_CAVLC = 0, + VIDC_720P_ENTROPY_SEL_CABAC = 1 +}; + +enum vidc_720p_cabac_model_type { + VIDC_720P_CABAC_MODEL_NUMBER_0 = 0, + VIDC_720P_CABAC_MODEL_NUMBER_1 = 1, + VIDC_720P_CABAC_MODEL_NUMBER_2 = 2 +}; + +enum vidc_720p_DBConfig_type { + VIDC_720P_DB_ALL_BLOCKING_BOUNDARY = 0, + VIDC_720P_DB_DISABLE = 1, + VIDC_720P_DB_SKIP_SLICE_BOUNDARY = 2 +}; + +enum vidc_720p_MSlice_selection_type { + VIDC_720P_MSLICE_BY_MB_COUNT = 0, + VIDC_720P_MSLICE_BY_BYTE_COUNT = 1, + VIDC_720P_MSLICE_BY_GOB = 2, + VIDC_720P_MSLICE_OFF = 3 +}; + +enum vidc_720p_display_status_type { + VIDC_720P_DECODE_ONLY = 0, + VIDC_720P_DECODE_AND_DISPLAY = 1, + VIDC_720P_DISPLAY_ONLY = 2, + VIDC_720P_EMPTY_BUFFER = 3 +}; + +#define VIDC_720P_ENC_IFRAME_REQ 0x1 +#define VIDC_720P_ENC_IPERIOD_CHANGE 0x1 +#define VIDC_720P_ENC_FRAMERATE_CHANGE 0x2 +#define VIDC_720P_ENC_BITRATE_CHANGE 0x4 + +#define VIDC_720P_FLUSH_REQ 0x1 +#define VIDC_720P_EXTRADATA 0x2 + +#define VIDC_720P_METADATA_ENABLE_QP 0x01 +#define VIDC_720P_METADATA_ENABLE_CONCEALMB 0x02 +#define VIDC_720P_METADATA_ENABLE_VC1 0x04 +#define VIDC_720P_METADATA_ENABLE_SEI 0x08 +#define VIDC_720P_METADATA_ENABLE_VUI 0x10 +#define VIDC_720P_METADATA_ENABLE_ENCSLICE 0x20 +#define VIDC_720P_METADATA_ENABLE_PASSTHROUGH 0x40 + +struct vidc_720p_dec_disp_info_type { + enum vidc_720p_display_status_type e_disp_status; + u32 n_resl_change; + u32 n_reconfig_flush_done; + u32 n_img_size_x; + u32 n_img_size_y; + u32 n_y_addr; + u32 n_c_addr; + u32 n_tag_top; + u32 n_pic_time_top; + u32 n_disp_is_interlace; + u32 n_tag_bottom; + u32 n_pic_time_bottom; + u32 n_metadata_exists; + u32 n_crop_exists; + u32 n_crop_right_offset; + u32 n_crop_left_offset; + u32 n_crop_bottom_offset; + u32 n_crop_top_offset; + u32 n_input_frame_type; + u32 n_input_bytes_consumed; + u32 n_input_is_interlace; + u32 n_input_frame_num; +}; + +struct vidc_720p_seq_hdr_info_type { + u32 n_img_size_x; + u32 n_img_size_y; + u32 n_dec_frm_size; + u32 n_min_num_dpb; + u32 n_min_dpb_size; + u32 n_profile; + u32 n_level; + u32 n_progressive; + u32 n_data_partitioned; + u32 n_crop_exists; + u32 n_crop_right_offset; + u32 n_crop_left_offset; + u32 n_crop_bottom_offset; + u32 n_crop_top_offset; +}; + +struct vidc_720p_enc_frame_info_type { + u32 n_enc_size; + u32 n_frame_type; + u32 n_metadata_exists; +}; + +void vidc_720p_set_device_virtual_base(u8 *p_core_virtual_base_addr); + +void vidc_720p_init(char **ppsz_version, u32 i_firmware_size, + u32 *pi_firmware_address, enum vidc_720p_endian_type e_dma_endian, + u32 b_interrupt_off, + enum vidc_720p_interrupt_level_selection_type e_interrupt_sel, + u32 interrupt_mask); + +u32 vidc_720p_do_sw_reset(void); + +u32 vidc_720p_reset_is_success(void); + +void vidc_720p_start_cpu(enum vidc_720p_endian_type e_dma_endian, + u32 *p_icontext_bufferstart, u32 *p_debug_core_dump_addr, + u32 debug_buffer_size); + +u32 vidc_720p_cpu_start(void); + +void vidc_720p_stop_fw(void); + +void vidc_720p_get_interrupt_status(u32 *p_interrupt_status, + u32 *p_cmd_err_status, u32 *p_disp_pic_err_status, + u32 *p_op_failed); + +void vidc_720p_interrupt_done_clear(void); + +void vidc_720p_submit_command(u32 ch_id, u32 n_cmd_id); + + +void vidc_720p_set_channel(u32 i_ch_id, + enum vidc_720p_enc_dec_selection_type e_enc_dec_sel, + enum vidc_720p_codec_type e_codec, u32 *pi_fw, u32 i_firmware_size); + +u32 vidc_720p_engine_reset(u32 n_ch_id, + enum vidc_720p_endian_type e_dma_endian, + enum vidc_720p_interrupt_level_selection_type e_interrupt_sel, + u32 interrupt_mask +); + +void vidc_720p_encode_set_profile(u32 i_profile, u32 i_level); + +void vidc_720p_set_frame_size(u32 i_size_x, u32 i_size_y); + +void vidc_720p_encode_set_fps(u32 i_rc_frame_rate); + +void vidc_720p_encode_set_vop_time(u32 n_vop_time_resolution, + u32 n_vop_time_increment); + +void vidc_720p_encode_set_hec_period(u32 n_hec_period); + +void vidc_720p_encode_set_short_header(u32 i_short_header); + +void vidc_720p_encode_set_qp_params(u32 i_max_qp, u32 i_min_qp); + +void vidc_720p_encode_set_rc_config(u32 b_enable_frame_level_rc, + u32 b_enable_mb_level_rc_flag, u32 i_frame_qp, u32 n_pframe_qp); + +void vidc_720p_encode_set_bit_rate(u32 i_target_bitrate); + +void vidc_720p_encoder_set_param_change(u32 n_enc_param_change); + +void vidc_720p_encode_set_control_param(u32 n_param_val); + +void vidc_720p_encode_set_frame_level_rc_params(u32 i_reaction_coeff); + +void vidc_720p_encode_set_mb_level_rc_params(u32 b_dark_region_as_flag, + u32 b_smooth_region_as_flag, u32 b_static_region_as_flag, + u32 b_activity_region_flag); + +void vidc_720p_encode_set_entropy_control(enum vidc_720p_entropy_sel_type \ + e_entropy_sel, + enum vidc_720p_cabac_model_type e_cabac_model_number); + +void vidc_720p_encode_set_db_filter_control(enum vidc_720p_DBConfig_type + e_db_config, u32 i_slice_alpha_offset, u32 i_slice_beta_offset); + +void vidc_720p_encode_set_intra_refresh_mb_number(u32 i_cir_mb_number); + +void vidc_720p_encode_set_multi_slice_info( + enum vidc_720p_MSlice_selection_type e_m_slice_sel, + u32 n_multi_slice_size); + +void vidc_720p_encode_set_dpb_buffer(u32 *pi_enc_dpb_addr, u32 alloc_len); + +void vidc_720p_set_deblock_line_buffer(u32 *pi_deblock_line_buffer_start, + u32 n_alloc_len); + +void vidc_720p_encode_set_i_period(u32 i_i_period); + +void vidc_720p_encode_init_codec(u32 i_ch_id, + enum vidc_720p_memory_access_method_type e_memory_access_model); + +void vidc_720p_encode_unalign_bitstream(u32 n_upper_unalign_word, + u32 n_lower_unalign_word); + +void vidc_720p_encode_set_seq_header_buffer(u32 n_ext_buffer_start, + u32 n_ext_buffer_end, u32 n_start_byte_num); + +void vidc_720p_encode_frame(u32 n_ch_id, u32 n_ext_buffer_start, + u32 n_ext_buffer_end, u32 n_start_byte_number, + u32 n_y_addr, u32 n_c_addr); + +void vidc_720p_encode_get_header(u32 *pi_enc_header_size); + +void vidc_720p_enc_frame_info + (struct vidc_720p_enc_frame_info_type *p_enc_frame_info); + +void vidc_720p_decode_bitstream_header(u32 n_ch_id, u32 n_dec_unit_size, + u32 n_start_byte_num, u32 n_ext_buffer_start, u32 n_ext_buffer_end, + enum vidc_720p_memory_access_method_type e_memory_access_model); + +void vidc_720p_decode_get_seq_hdr_info + (struct vidc_720p_seq_hdr_info_type *p_seq_hdr_info); + +void vidc_720p_decode_set_dpb_release_buffer_mask + (u32 i_dpb_release_buffer_mask); + +void vidc_720p_decode_set_dpb_buffers(u32 i_buf_index, u32 *pi_dpb_buffer); + +void vidc_720p_decode_set_comv_buffer + (u32 *pi_dpb_comv_buffer, u32 n_alloc_len); + +void vidc_720p_decode_set_dpb_details + (u32 n_num_dpb, u32 n_alloc_len, u32 *p_ref_buffer); + +void vidc_720p_decode_set_mpeg4Post_filter(u32 b_enable_post_filter); + +void vidc_720p_decode_set_error_control(u32 b_enable_error_control); + +void vidc_720p_decode_set_mpeg4_data_partitionbuffer(u32 *p_vsp_buf_start); + +void vidc_720p_decode_setH264VSPBuffer(u32 *pi_vsp_temp_buffer_start); + +void vidc_720p_decode_frame(u32 n_ch_id, u32 n_ext_buffer_start, + u32 n_ext_buffer_end, u32 n_dec_unit_size, + u32 n_start_byte_num, u32 n_input_frame_tag); + +void vidc_720p_issue_eos(u32 i_ch_id); +void vidc_720p_eos_info(u32 *p_disp_status); + +void vidc_720p_decode_display_info + (struct vidc_720p_dec_disp_info_type *p_disp_info); + +void vidc_720p_decode_skip_frm_details(u32 *p_free_luma_dpb); + +void vidc_720p_metadata_enable(u32 n_flag, u32 *p_input_buffer); + +void vidc_720p_decode_dynamic_req_reset(void); + +void vidc_720p_decode_dynamic_req_set(u32 n_property); + +void vidc_720p_decode_setpassthrough_start(u32 n_pass_startaddr); + + + +#define DDL_720P_REG_BASE VIDC_720P_WRAPPER_REG_BASE +#define VIDC_BUSY_WAIT(n) udelay(n) + +#undef VIDC_REGISTER_LOG_MSG +#undef VIDC_REGISTER_LOG_INTO_BUFFER + +#ifdef VIDC_REGISTER_LOG_MSG +#define VIDC_MSG1(msg_format, a) printk(KERN_INFO msg_format, a) +#define VIDC_MSG2(msg_format, a, b) printk(KERN_INFO msg_format, a, b) +#define VIDC_MSG3(msg_format, a, b, c) printk(KERN_INFO msg_format, a, b, c) +#else +#define VIDC_MSG1(msg_format, a) +#define VIDC_MSG2(msg_format, a, b) +#define VIDC_MSG3(msg_format, a, b, c) +#endif + +#ifdef VIDC_REGISTER_LOG_INTO_BUFFER + +#define VIDC_REGLOG_BUFSIZE 200000 +#define VIDC_REGLOG_MAX_PRINT_SIZE 100 +extern char vidclog[VIDC_REGLOG_BUFSIZE]; +extern unsigned int vidclog_index; + +#define VIDC_LOG_BUFFER_INIT \ +{if (vidclog_index) \ + memset(vidclog, 0, vidclog_index+1); \ + vidclog_index = 0; } + +#define VIDC_REGLOG_CHECK_BUFINDEX(req_size) \ + vidclog_index = \ + (vidclog_index+(req_size) < VIDC_REGLOG_BUFSIZE) ? vidclog_index : 0; + +#define VIDC_LOG_WRITE(reg, val) \ +{unsigned int len; \ + VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \ + len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \ + "(0x%x:"#reg"=0x%x)" , VIDC_##reg##_ADDR - DDL_720P_REG_BASE, val);\ + vidclog_index += len; } + +#define VIDC_LOG_WRITEI(reg, index, val) \ +{unsigned int len; \ + VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \ + len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \ + "(0x%x:"#reg"=0x%x)" , VIDC_##reg##_ADDR(index)-DDL_720P_REG_BASE, \ + val); vidclog_index += len; } + +#define VIDC_LOG_WRITEF(reg, field, val) \ +{unsigned int len; \ + VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \ + len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \ + "(0x%x:"#reg":0x%x:=0x%x)" , VIDC_##reg##_ADDR - DDL_720P_REG_BASE, \ + VIDC_##reg##_##field##_BMSK, val);\ + vidclog_index += len; } + +#define VIDC_LOG_READ(reg, pval) \ +{ unsigned int len; \ + VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \ + len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \ + "(0x%x:"#reg"==0x%x)" , VIDC_##reg##_ADDR - DDL_720P_REG_BASE, \ + (u32)*pval); \ + vidclog_index += len; } + +#define VIDC_STR_LOGBUFFER(str) \ +{ unsigned int len; \ + VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \ + len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \ + "<%s>" , str); vidclog_index += len; } + +#define VIDC_LONG_LOGBUFFER(str, arg1) \ +{ unsigned int len; \ + VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \ + len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \ + "<%s=0x%x>" , str, arg1); vidclog_index += len; } + +#define VIDC_DEBUG_REGISTER_LOG \ +{ u32 val; unsigned int len; \ + val = VIDC_720P_IN(REG_881638); \ + VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \ + len = snprintf(&vidclog[vidclog_index], 50, "[dbg1=%x]" , val); \ + vidclog_index += len; \ + val = VIDC_720P_IN(REG_161486); \ + VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \ + len = snprintf(&vidclog[vidclog_index], 50, "[dbg2=%x]" , val); \ + vidclog_index += len; } + +#else +#define VIDC_LOG_WRITE(reg, val) +#define VIDC_LOG_WRITEI(reg, index, val) +#define VIDC_LOG_WRITEF(reg, field, val) +#define VIDC_LOG_READ(reg, pval) +#define VIDC_LOG_BUFFER_INIT +#define VIDC_STR_LOGBUFFER(str) +#define VIDC_LONG_LOGBUFFER(str, arg1) +#define VIDC_DEBUG_REGISTER_LOG +#endif + +void vidcputlog(char *str); +void vidcput_debug_reglog(void); + +#define VIDC_LOGERR_STRING(str) \ +do { \ + VIDC_STR_LOGBUFFER(str); \ + VIDC_MSG1("\n<%s>", str); \ +} while (0) + +#define VIDC_LOG_STRING(str) \ +do { \ + VIDC_STR_LOGBUFFER(str); \ + VIDC_MSG1("\n<%s>", str); \ +} while (0) + +#define VIDC_LOG1(str, arg1) \ +do { \ + VIDC_LONG_LOGBUFFER(str, arg1); \ + VIDC_MSG2("\n<%s=0x%08x>", str, arg1); \ +} while (0) + +#define VIDC_IO_OUT(reg, val) \ +do { \ + VIDC_LOG_WRITE(reg, (u32)val); \ + VIDC_MSG2("\n(0x%08x:"#reg"=0x%08x)", \ + (u32)(VIDC_##reg##_ADDR - DDL_720P_REG_BASE), (u32)val); \ + mb(); \ + VIDC_720P_OUT(reg, val); \ +} while (0) + +#define VIDC_IO_OUTI(reg, index, val) \ +do { \ + VIDC_LOG_WRITEI(reg, index, (u32)val); \ + VIDC_MSG2("\n(0x%08x:"#reg"=0x%08x)", \ + (u32)(VIDC_##reg##_ADDR(index)-DDL_720P_REG_BASE), (u32)val); \ + mb(); \ + VIDC_720P_OUTI(reg, index, val); \ +} while (0) + +#define VIDC_IO_OUTF(reg, field, val) \ +do { \ + VIDC_LOG_WRITEF(reg, field, val); \ + VIDC_MSG3("\n(0x%08x:"#reg":0x%x:=0x%08x)", \ + (u32)(VIDC_##reg##_ADDR - DDL_720P_REG_BASE), \ + VIDC_##reg##_##field##_BMSK, (u32)val); \ + mb(); \ + VIDC_720P_OUTF(reg, field, val); \ +} while (0) + +#define VIDC_IO_IN(reg, pval) \ +do { \ + mb(); \ + *pval = (u32) VIDC_720P_IN(reg); \ + VIDC_LOG_READ(reg, pval); \ + VIDC_MSG2("\n(0x%08x:"#reg"==0x%08x)", \ + (u32)(VIDC_##reg##_ADDR - DDL_720P_REG_BASE), (u32) *pval); \ +} while (0) + +#define VIDC_IO_INF(reg, mask, pval) \ +do { \ + mb(); \ + *pval = VIDC_720P_INF(reg, mask); \ + VIDC_LOG_READ(reg, pval); \ + VIDC_MSG2("\n(0x%08x:"#reg"==0x%08x)", \ + (u32)(VIDC_##reg##_ADDR - DDL_720P_REG_BASE), *pval); \ +} while (0) + +#endif diff --git a/drivers/misc/video_core/720p/dec/vdec.c b/drivers/misc/video_core/720p/dec/vdec.c new file mode 100644 index 0000000000000..3468fbafb629d --- /dev/null +++ b/drivers/misc/video_core/720p/dec/vdec.c @@ -0,0 +1,1580 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "video_core_type.h" +#include "vcd_api.h" +#include "vdec_internal.h" +#include "video_core_init.h" + + +#define VID_C_HCLK_RATE 170667000 + +#if DEBUG +#define DBG(x...) printk(KERN_DEBUG x) +#else +#define DBG(x...) +#endif + +#define INFO(x...) printk(KERN_INFO x) +#define ERR(x...) printk(KERN_ERR x) + +#define VID_DEC_NAME "msm_vidc_dec" + +static struct vid_dec_dev *vid_dec_device_p; +static dev_t vid_dec_dev_num; +static struct class *vid_dec_class; + +static s32 vid_dec_get_empty_client_index(void) +{ + u32 i, found = FALSE; + + for (i = 0; i < VID_DEC_MAX_DECODER_CLIENTS; i++) { + if (!vid_dec_device_p->vdec_clients[i].vcd_handle) { + found = TRUE; + break; + } + } + if (!found) { + ERR("%s():ERROR No space for new client\n", __func__); + return -1; + } else { + DBG("%s(): available client index = %u\n", __func__, i); + return i; + } +} + +u32 vid_dec_get_status(u32 status) +{ + u32 vdec_status; + + switch (status) { + case VCD_ERR_BITSTREAM_ERR: + case VCD_S_SUCCESS: + vdec_status = VDEC_S_SUCCESS; + break; + case VCD_ERR_FAIL: + vdec_status = VDEC_S_EFAIL; + break; + case VCD_ERR_ALLOC_FAIL: + vdec_status = VDEC_S_ENOSWRES; + break; + case VCD_ERR_ILLEGAL_OP: + vdec_status = VDEC_S_EINVALCMD; + break; + case VCD_ERR_ILLEGAL_PARM: + vdec_status = VDEC_S_EBADPARAM; + break; + case VCD_ERR_BAD_POINTER: + case VCD_ERR_BAD_HANDLE: + vdec_status = VDEC_S_EFATAL; + break; + case VCD_ERR_NOT_SUPPORTED: + vdec_status = VDEC_S_ENOTSUPP; + break; + case VCD_ERR_BAD_STATE: + vdec_status = VDEC_S_EINVALSTATE; + break; + case VCD_ERR_BUSY: + vdec_status = VDEC_S_BUSY; + break; + case VCD_ERR_MAX_CLIENT: + vdec_status = VDEC_S_ENOHWRES; + break; + default: + vdec_status = VDEC_S_EFAIL; + break; + } + + return vdec_status; +} + +static void vid_dec_notify_client(struct video_client_ctx *client_ctx) +{ + if (client_ctx) + complete(&client_ctx->event); +} + +void vid_dec_vcd_open_done(struct video_client_ctx *client_ctx, + struct vcd_handle_container_type *handle_container) +{ + DBG("vid_dec_vcd_open_done\n"); + + if (client_ctx) { + if (handle_container) + client_ctx->vcd_handle = handle_container->handle; + else + ERR("%s(): ERROR. handle_container is NULL\n", + __func__); + + vid_dec_notify_client(client_ctx); + } else + ERR("%s(): ERROR. client_ctx is NULL\n", __func__); +} + +static void vid_dec_input_frame_done(struct video_client_ctx *client_ctx, + u32 event, u32 status, + struct vcd_frame_data_type *vcd_frame_data) +{ + struct vid_dec_msg *vdec_msg; + + if (!client_ctx || !vcd_frame_data) { + ERR("vid_dec_input_frame_done() NULL pointer \n"); + return; + } + + vdec_msg = kzalloc(sizeof(struct vid_dec_msg), GFP_KERNEL); + if (!vdec_msg) { + ERR("vid_dec_input_frame_done(): cannot allocate vid_dec_msg " + " buffer\n"); + return; + } + + vdec_msg->vdec_msg_info.status_code = vid_dec_get_status(status); + + if (event == VCD_EVT_RESP_INPUT_DONE) { + vdec_msg->vdec_msg_info.msgcode = + VDEC_MSG_RESP_INPUT_BUFFER_DONE; + DBG("Send INPUT_DON message to client = %p\n", client_ctx); + + } else if (event == VCD_EVT_RESP_INPUT_FLUSHED) { + vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_INPUT_FLUSHED; + DBG("Send INPUT_FLUSHED message to client = %p\n", client_ctx); + } else { + ERR("vid_dec_input_frame_done(): invalid event type\n"); + return; + } + + vdec_msg->vdec_msg_info.msgdata.input_frame_clientdata = + (void *)vcd_frame_data->n_frm_clnt_data; + vdec_msg->vdec_msg_info.msgdatasize = sizeof(void *); + + mutex_lock(&client_ctx->msg_queue_lock); + list_add_tail(&vdec_msg->list, &client_ctx->msg_queue); + mutex_unlock(&client_ctx->msg_queue_lock); + wake_up(&client_ctx->msg_wait); +} + +static void vid_dec_output_frame_done(struct video_client_ctx *client_ctx, + u32 event, u32 status, + struct vcd_frame_data_type *vcd_frame_data) +{ + struct vid_dec_msg *vdec_msg; + + unsigned long kernel_vaddr, phy_addr, user_vaddr; + int pmem_fd; + struct file *file; + s32 buffer_index = -1; + + if (!client_ctx || !vcd_frame_data) { + ERR("vid_dec_input_frame_done() NULL pointer \n"); + return; + } + + vdec_msg = kzalloc(sizeof(struct vid_dec_msg), GFP_KERNEL); + if (!vdec_msg) { + ERR("vid_dec_input_frame_done(): cannot allocate vid_dec_msg " + " buffer\n"); + return; + } + + vdec_msg->vdec_msg_info.status_code = vid_dec_get_status(status); + + if (event == VCD_EVT_RESP_OUTPUT_DONE) + vdec_msg->vdec_msg_info.msgcode = + VDEC_MSG_RESP_OUTPUT_BUFFER_DONE; + + else if (event == VCD_EVT_RESP_OUTPUT_FLUSHED) + vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_OUTPUT_FLUSHED; + else { + ERR("QVD: vid_dec_output_frame_done invalid cmd type \n"); + return; + } + + kernel_vaddr = (unsigned long)vcd_frame_data->p_virtual; + + if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, + FALSE, &user_vaddr, &kernel_vaddr, + &phy_addr, &pmem_fd, &file, + &buffer_index)) { + + /* Buffer address in user space */ + vdec_msg->vdec_msg_info.msgdata.output_frame.bufferaddr = + (u8 *) user_vaddr; + /* Buffer address in user space */ + vdec_msg->vdec_msg_info.msgdata.output_frame.phy_addr = + vcd_frame_data->p_physical; + /* Data length */ + vdec_msg->vdec_msg_info.msgdata.output_frame.len = + vcd_frame_data->n_data_len; + vdec_msg->vdec_msg_info.msgdata.output_frame.flags = + vcd_frame_data->n_flags; + /* Timestamp pass-through from input frame */ + vdec_msg->vdec_msg_info.msgdata.output_frame.time_stamp = + vcd_frame_data->time_stamp; + /* Output frame client data */ + vdec_msg->vdec_msg_info.msgdata.output_frame.client_data = + (void *)vcd_frame_data->n_frm_clnt_data; + /* Associated input frame client data */ + vdec_msg->vdec_msg_info.msgdata.output_frame. + input_frame_clientdata = + (void *)vcd_frame_data->n_ip_frm_tag; + /* Decoded picture width and height */ + vdec_msg->vdec_msg_info.msgdata.output_frame.framesize. + n_bottom = + vcd_frame_data->dec_op_prop.disp_frm.n_bottom; + vdec_msg->vdec_msg_info.msgdata.output_frame.framesize.n_left = + vcd_frame_data->dec_op_prop.disp_frm.n_left; + vdec_msg->vdec_msg_info.msgdata.output_frame.framesize.n_right = + vcd_frame_data->dec_op_prop.disp_frm.n_right; + vdec_msg->vdec_msg_info.msgdata.output_frame.framesize.n_top = + vcd_frame_data->dec_op_prop.disp_frm.n_top; + vdec_msg->vdec_msg_info.msgdatasize = + sizeof(struct vdec_output_frameinfo); + } else { + ERR("vid_dec_output_frame_done UVA can not be found\n"); + vdec_msg->vdec_msg_info.status_code = VDEC_S_EFATAL; + } + + mutex_lock(&client_ctx->msg_queue_lock); + list_add_tail(&vdec_msg->list, &client_ctx->msg_queue); + mutex_unlock(&client_ctx->msg_queue_lock); + wake_up(&client_ctx->msg_wait); +} + +static void vid_dec_lean_event(struct video_client_ctx *client_ctx, + u32 event, u32 status) +{ + struct vid_dec_msg *vdec_msg; + + if (!client_ctx) { + ERR("%s(): !client_ctx pointer \n", __func__); + return; + } + + vdec_msg = kzalloc(sizeof(struct vid_dec_msg), GFP_KERNEL); + if (!vdec_msg) { + ERR("%s(): cannot allocate vid_dec_msg buffer\n", __func__); + return; + } + + vdec_msg->vdec_msg_info.status_code = vid_dec_get_status(status); + + switch (event) { + case VCD_EVT_IND_RECONFIG: + INFO("\n msm_vidc_dec: Sending VDEC_MSG_EVT_CONFIG_CHANGED" + " to client"); + vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_EVT_CONFIG_CHANGED; + break; + case VCD_EVT_IND_RESOURCES_LOST: + INFO("\n msm_vidc_dec: Sending VDEC_EVT_RESOURCES_LOST" + " to client"); + vdec_msg->vdec_msg_info.msgcode = VDEC_EVT_RESOURCES_LOST; + break; + case VCD_EVT_RESP_FLUSH_INPUT_DONE: + INFO("\n msm_vidc_dec: Sending VDEC_MSG_RESP_FLUSH_INPUT_DONE" + " to client"); + vdec_msg->vdec_msg_info.msgcode = + VDEC_MSG_RESP_FLUSH_INPUT_DONE; + break; + case VCD_EVT_RESP_FLUSH_OUTPUT_DONE: + INFO("\n msm_vidc_dec: Sending VDEC_MSG_RESP_FLUSH_OUTPUT_DONE" + " to client"); + vdec_msg->vdec_msg_info.msgcode = + VDEC_MSG_RESP_FLUSH_OUTPUT_DONE; + break; + case VCD_EVT_IND_HWERRFATAL: + INFO("\n msm_vidc_dec: Sending VDEC_MSG_EVT_HW_ERROR" + " to client"); + vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_EVT_HW_ERROR; + break; + case VCD_EVT_RESP_START: + INFO("\n msm_vidc_dec: Sending VDEC_MSG_RESP_START_DONE" + " to client"); + vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_START_DONE; + break; + case VCD_EVT_RESP_STOP: + INFO("\n msm_vidc_dec: Sending VDEC_MSG_RESP_STOP_DONE" + " to client"); + vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_STOP_DONE; + break; + case VCD_EVT_RESP_PAUSE: + INFO("\n msm_vidc_dec: Sending VDEC_MSG_RESP_PAUSE_DONE" + " to client"); + vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_PAUSE_DONE; + break; + default: + ERR("%s() : unknown event type \n", __func__); + break; + } + + vdec_msg->vdec_msg_info.msgdatasize = 0; + mutex_lock(&client_ctx->msg_queue_lock); + list_add_tail(&vdec_msg->list, &client_ctx->msg_queue); + mutex_unlock(&client_ctx->msg_queue_lock); + wake_up(&client_ctx->msg_wait); +} + + +void vid_dec_vcd_cb(u32 event, u32 status, + void *info, u32 size, void *handle, void *const client_data) +{ + struct video_client_ctx *client_ctx = + (struct video_client_ctx *)client_data; + + DBG("Entering %s()\n", __func__); + + if (!client_ctx) { + ERR("%s(): client_ctx is NULL \n", __func__); + return; + } + + client_ctx->event_status = status; + + switch (event) { + case VCD_EVT_RESP_OPEN: + vid_dec_vcd_open_done(client_ctx, + (struct vcd_handle_container_type *) + info); + break; + case VCD_EVT_RESP_INPUT_DONE: + case VCD_EVT_RESP_INPUT_FLUSHED: + vid_dec_input_frame_done(client_ctx, event, status, + (struct vcd_frame_data_type *)info); + break; + case VCD_EVT_RESP_OUTPUT_DONE: + case VCD_EVT_RESP_OUTPUT_FLUSHED: + vid_dec_output_frame_done(client_ctx, event, status, + (struct vcd_frame_data_type *)info); + break; + case VCD_EVT_RESP_PAUSE: + case VCD_EVT_RESP_STOP: + case VCD_EVT_RESP_FLUSH_INPUT_DONE: + case VCD_EVT_RESP_FLUSH_OUTPUT_DONE: + case VCD_EVT_IND_RECONFIG: + case VCD_EVT_IND_HWERRFATAL: + case VCD_EVT_IND_RESOURCES_LOST: + vid_dec_lean_event(client_ctx, event, status); + break; + case VCD_EVT_RESP_START: + if (!client_ctx->seq_header_set) + vid_dec_lean_event(client_ctx, event, status); + else + vid_dec_notify_client(client_ctx); + break; + default: + ERR("%s() : Error - Invalid event type =%u\n", __func__, + event); + break; + } +} + +static u32 vid_dec_set_codec(struct video_client_ctx *client_ctx, + enum vdec_codec *vdec_codec_type) +{ + u32 result = TRUE; + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_codec_type codec_type; + u32 vcd_status = VCD_ERR_FAIL; + + if (!client_ctx || !vdec_codec_type) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_CODEC; + vcd_property_hdr.n_size = sizeof(struct vcd_property_codec_type); + + switch (*vdec_codec_type) { + case VDEC_CODECTYPE_MPEG4: + codec_type.e_codec = VCD_CODEC_MPEG4; + break; + case VDEC_CODECTYPE_H264: + codec_type.e_codec = VCD_CODEC_H264; + break; + case VDEC_CODECTYPE_DIVX_3: + codec_type.e_codec = VCD_CODEC_DIVX_3; + break; + case VDEC_CODECTYPE_XVID: + codec_type.e_codec = VCD_CODEC_XVID; + break; + case VDEC_CODECTYPE_H263: + codec_type.e_codec = VCD_CODEC_H263; + break; + case VDEC_CODECTYPE_MPEG2: + codec_type.e_codec = VCD_CODEC_MPEG2; + break; + case VDEC_CODECTYPE_VC1: + codec_type.e_codec = VCD_CODEC_VC1; + break; + default: + result = FALSE; + break; + } + + if (result) { + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &codec_type); + if (vcd_status) + result = FALSE; + } + return result; +} + +static u32 vid_dec_set_output_format(struct video_client_ctx *client_ctx, + enum vdec_output_fromat *output_format) +{ + u32 result = TRUE; + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_buffer_format_type vcd_prop_buffer_format; + u32 vcd_status = VCD_ERR_FAIL; + + if (!client_ctx || !output_format) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_BUFFER_FORMAT;; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_buffer_format_type); + + switch (*output_format) { + case VDEC_YUV_FORMAT_NV12: + vcd_prop_buffer_format.e_buffer_format = VCD_BUFFER_FORMAT_NV12; + break; + case VDEC_YUV_FORMAT_TILE_4x2: + vcd_prop_buffer_format.e_buffer_format = + VCD_BUFFER_FORMAT_TILE_4x2; + break; + default: + result = FALSE; + break; + } + + if (result) + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, + &vcd_prop_buffer_format); + + if (vcd_status) + return FALSE; + else + return TRUE; +} + +static u32 vid_dec_set_frame_resolution(struct video_client_ctx *client_ctx, + struct vdec_picsize *video_resoultion) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_frame_size_type frame_resolution; + u32 vcd_status = VCD_ERR_FAIL; + + if (!client_ctx || !video_resoultion) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_FRAME_SIZE; + vcd_property_hdr.n_size = sizeof(struct vcd_property_frame_size_type); + frame_resolution.n_width = video_resoultion->frame_width; + frame_resolution.n_height = video_resoultion->frame_height; + + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &frame_resolution); + + if (vcd_status) + return FALSE; + else + return TRUE; +} + +static u32 vid_dec_get_frame_resolution(struct video_client_ctx *client_ctx, + struct vdec_picsize *video_resoultion) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_frame_size_type frame_resolution; + u32 vcd_status = VCD_ERR_FAIL; + + if (!client_ctx || !video_resoultion) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_FRAME_SIZE; + vcd_property_hdr.n_size = sizeof(struct vcd_property_frame_size_type); + + vcd_status = vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr, + &frame_resolution); + + video_resoultion->frame_width = frame_resolution.n_width; + video_resoultion->frame_height = frame_resolution.n_height; + video_resoultion->scan_lines = frame_resolution.n_scan_lines; + video_resoultion->stride = frame_resolution.n_stride; + + if (vcd_status) + return FALSE; + else + return TRUE; +} + +static u32 vid_dec_get_buffer_req(struct video_client_ctx *client_ctx, + struct vdec_allocatorproperty *vdec_buf_req) +{ + u32 vcd_status = VCD_ERR_FAIL; + struct vcd_buffer_requirement_type vcd_buf_req; + + if (!client_ctx || !vdec_buf_req) + return FALSE; + + if (vdec_buf_req->buffer_type == VDEC_BUFFER_TYPE_INPUT) { + vcd_status = vcd_get_buffer_requirements(client_ctx->vcd_handle, + VCD_BUFFER_INPUT, + &vcd_buf_req); + } else { + vcd_status = vcd_get_buffer_requirements(client_ctx->vcd_handle, + VCD_BUFFER_OUTPUT, + &vcd_buf_req); + } + + if (vcd_status) { + return FALSE; + } else { + vdec_buf_req->mincount = vcd_buf_req.n_min_count; + vdec_buf_req->maxcount = vcd_buf_req.n_max_count; + vdec_buf_req->actualcount = vcd_buf_req.n_actual_count; + vdec_buf_req->buffer_size = vcd_buf_req.n_size; + vdec_buf_req->alignment = vcd_buf_req.n_align; + vdec_buf_req->buf_poolid = vcd_buf_req.n_buf_pool_id; + + return TRUE; + } +} + +static u32 vid_dec_set_buffer(struct video_client_ctx *client_ctx, + struct vdec_setbuffer_cmd *buffer_info) +{ + enum vcd_buffer_type buffer_type; + enum buffer_dir dir_buffer = BUFFER_TYPE_INPUT; + u32 vcd_status = VCD_ERR_FAIL; + unsigned long user_vaddr, kernel_vaddr, phy_addr, len; + int pmem_fd; + struct file *file; + struct buf_addr_table *buf_addr_table; + s32 buffer_index = -1; + + if (!client_ctx || !buffer_info) + return FALSE; + + user_vaddr = (unsigned long)buffer_info->buffer.bufferaddr; + + if (buffer_info->buffer_type == VDEC_BUFFER_TYPE_OUTPUT) + dir_buffer = BUFFER_TYPE_OUTPUT; + + /*If buffer already set, ignore */ + if (vid_c_lookup_addr_table(client_ctx, dir_buffer, + TRUE, &user_vaddr, &kernel_vaddr, + &phy_addr, &pmem_fd, &file, + &buffer_index)) { + DBG("%s() : user_virt_addr = 0x%08lx is alreday set.", + __func__, user_vaddr); + return TRUE; + } + + if (get_pmem_file(buffer_info->buffer.pmem_fd, + &phy_addr, &kernel_vaddr, &len, &file)) { + ERR("%s(): get_pmem_file failed\n", __func__); + return FALSE; + } + put_pmem_file(file); + if (buffer_info->buffer_type == VDEC_BUFFER_TYPE_INPUT) { + buffer_type = VCD_BUFFER_INPUT; + client_ctx->num_of_input_buffers++; + if (client_ctx->num_of_input_buffers > + MAX_VIDEO_NUM_OF_BUFF) { + ERR("%s(): num_of_input_buffers reached max value" + " MAX_VIDEO_NUM_OF_BUFF \n", __func__); + client_ctx->num_of_input_buffers--; + return FALSE; + } + buffer_index = client_ctx->num_of_input_buffers - 1; + buf_addr_table = + &client_ctx->input_buf_addr_table[buffer_index]; + buf_addr_table->user_vaddr = + (unsigned long)buffer_info->buffer.bufferaddr; + buf_addr_table->kernel_vaddr = kernel_vaddr; + buf_addr_table->phy_addr = phy_addr; + buf_addr_table->pmem_fd = buffer_info->buffer.pmem_fd; + buf_addr_table->file = file; + } else { + buffer_type = VCD_BUFFER_OUTPUT; + client_ctx->num_of_output_buffers++; + if (client_ctx->num_of_output_buffers > + MAX_VIDEO_NUM_OF_BUFF) { + ERR("%s(): num_of_outut_buffers reached max value" + " MAX_VIDEO_NUM_OF_BUFF \n", __func__); + client_ctx->num_of_output_buffers--; + return FALSE; + } + buffer_index = client_ctx->num_of_output_buffers - 1; + buf_addr_table = + &client_ctx->output_buf_addr_table[buffer_index]; + kernel_vaddr += (unsigned long)buffer_info->buffer.offset; + phy_addr += (unsigned long)buffer_info->buffer.offset; + buf_addr_table->user_vaddr = + (unsigned long)buffer_info->buffer.bufferaddr; + buf_addr_table->kernel_vaddr = kernel_vaddr; + buf_addr_table->phy_addr = phy_addr; + buf_addr_table->pmem_fd = buffer_info->buffer.pmem_fd; + buf_addr_table->file = file; + } + + vcd_status = vcd_set_buffer(client_ctx->vcd_handle, + buffer_type, (u8 *) kernel_vaddr, + buffer_info->buffer.buffer_len); + + if (!vcd_status) + return TRUE; + else + return FALSE; +} + + +static u32 vid_dec_free_buffer(struct video_client_ctx *client_ctx, + struct vdec_setbuffer_cmd *buffer_info) +{ + enum vcd_buffer_type buffer_type; + enum buffer_dir dir_buffer = BUFFER_TYPE_INPUT; + u32 vcd_status = VCD_ERR_FAIL; + unsigned long user_vaddr, kernel_vaddr, phy_addr; + int pmem_fd; + struct file *file; + s32 buffer_index = -1; + + if (!client_ctx || !buffer_info) + return FALSE; + + user_vaddr = (unsigned long)buffer_info->buffer.bufferaddr; + + if (buffer_info->buffer_type == VDEC_BUFFER_TYPE_OUTPUT) + dir_buffer = BUFFER_TYPE_OUTPUT; + + /*If buffer already set, ignore */ + if (!vid_c_lookup_addr_table(client_ctx, dir_buffer, + TRUE, &user_vaddr, &kernel_vaddr, + &phy_addr, &pmem_fd, &file, + &buffer_index)) { + + DBG("%s() : user_virt_addr = 0x%08lx is alreday set.", + __func__, user_vaddr); + return TRUE; + } + + if (buffer_info->buffer_type == VDEC_BUFFER_TYPE_INPUT) + buffer_type = VCD_BUFFER_INPUT; + else + buffer_type = VCD_BUFFER_OUTPUT; + vcd_status = vcd_free_buffer(client_ctx->vcd_handle, buffer_type, + (u8 *)kernel_vaddr); + + if (!vcd_status) + return TRUE; + else + return FALSE; +} + +static u32 vid_dec_pause_resume(struct video_client_ctx *client_ctx, u32 pause) +{ + u32 vcd_status; + + if (!client_ctx) { + ERR("\n %s(): Invalid client_ctx", __func__); + return FALSE; + } + + if (pause) { + INFO("\n msm_vidc_dec: PAUSE command from client = %p\n", + client_ctx); + vcd_status = vcd_pause(client_ctx->vcd_handle); + } else{ + INFO("\n msm_vidc_dec: RESUME command from client = %p\n", + client_ctx); + vcd_status = vcd_resume(client_ctx->vcd_handle); + } + + if (vcd_status) + return FALSE; + + return TRUE; + +} + +static u32 vid_dec_start_stop(struct video_client_ctx *client_ctx, u32 start) +{ + struct vid_dec_msg *vdec_msg = NULL; + u32 vcd_status; + + INFO("\n msm_vidc_dec: Inside %s()", __func__); + if (!client_ctx) { + ERR("\n Invalid client_ctx"); + return FALSE; + } + + if (start) { + if (client_ctx->seq_header_set) { + INFO("\n %s(): Seq Hdr set: Send START_DONE to client", + __func__); + vdec_msg = kzalloc(sizeof(*vdec_msg), GFP_KERNEL); + if (!vdec_msg) { + ERR("vid_dec_start_stop: cannot allocate" + "buffer\n"); + return FALSE; + } + vdec_msg->vdec_msg_info.msgcode = + VDEC_MSG_RESP_START_DONE; + vdec_msg->vdec_msg_info.status_code = VDEC_S_SUCCESS; + vdec_msg->vdec_msg_info.msgdatasize = 0; + mutex_lock(&client_ctx->msg_queue_lock); + list_add_tail(&vdec_msg->list, &client_ctx->msg_queue); + mutex_unlock(&client_ctx->msg_queue_lock); + + wake_up(&client_ctx->msg_wait); + + DBG("Send START_DONE message to client = %p\n", + client_ctx); + + } else { + INFO("\n %s(): Calling decode_start()", __func__); + vcd_status = + vcd_decode_start(client_ctx->vcd_handle, NULL); + + if (vcd_status) { + ERR("%s(): vcd_decode_start failed." + " vcd_status = %u\n", __func__, vcd_status); + return FALSE; + } + } + } else { + INFO("\n %s(): Calling vcd_stop()", __func__); + vcd_status = vcd_stop(client_ctx->vcd_handle); + if (vcd_status) { + + ERR("%s(): vcd_stop failed. vcd_status = %u\n", + __func__, vcd_status); + return FALSE; + } + DBG("Send STOP_DONE message to client = %p\n", client_ctx); + } + return TRUE; +} + +static u32 vid_dec_decode_frame(struct video_client_ctx *client_ctx, + struct vdec_input_frameinfo *input_frame_info) +{ + struct vcd_frame_data_type vcd_input_buffer; + unsigned long kernel_vaddr, phy_addr, user_vaddr; + int pmem_fd; + struct file *file; + s32 buffer_index = -1; + u32 vcd_status = VCD_ERR_FAIL; + + if (!client_ctx || !input_frame_info) + return FALSE; + + user_vaddr = (unsigned long)input_frame_info->bufferaddr; + + if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_INPUT, + TRUE, &user_vaddr, &kernel_vaddr, + &phy_addr, &pmem_fd, &file, + &buffer_index)) { + + /* kernel_vaddr is found. send the frame to VCD */ + memset((void *)&vcd_input_buffer, 0, + sizeof(struct vcd_frame_data_type)); + vcd_input_buffer.p_virtual = + (u8 *) (kernel_vaddr + input_frame_info->pmem_offset); + vcd_input_buffer.n_offset = input_frame_info->offset; + vcd_input_buffer.n_frm_clnt_data = + (u32) input_frame_info->client_data; + vcd_input_buffer.n_ip_frm_tag = + (u32) input_frame_info->client_data; + vcd_input_buffer.n_data_len = input_frame_info->datalen; + vcd_input_buffer.time_stamp = input_frame_info->timestamp; + /* Rely on VCD using the same flags as OMX */ + vcd_input_buffer.n_flags = input_frame_info->flags; + + vcd_status = vcd_decode_frame(client_ctx->vcd_handle, + &vcd_input_buffer); + if (!vcd_status) + return TRUE; + else { + ERR("%s(): vcd_decode_frame failed = %u\n", __func__, + vcd_status); + return FALSE; + } + + } else { + ERR("%s(): kernel_vaddr not found\n", __func__); + return FALSE; + } +} + +static u32 vid_dec_fill_output_buffer(struct video_client_ctx *client_ctx, + struct vdec_fillbuffer_cmd *fill_buffer_cmd) +{ + unsigned long kernel_vaddr, phy_addr, user_vaddr; + int pmem_fd; + struct file *file; + s32 buffer_index = -1; + u32 vcd_status = VCD_ERR_FAIL; + + struct vcd_frame_data_type vcd_frame; + + if (!client_ctx || !fill_buffer_cmd) + return FALSE; + + user_vaddr = (unsigned long)fill_buffer_cmd->buffer.bufferaddr; + + if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, + TRUE, &user_vaddr, &kernel_vaddr, + &phy_addr, &pmem_fd, &file, + &buffer_index)) { + + memset((void *)&vcd_frame, 0, + sizeof(struct vcd_frame_data_type)); + vcd_frame.p_virtual = (u8 *) kernel_vaddr; + vcd_frame.n_frm_clnt_data = (u32) fill_buffer_cmd->client_data; + vcd_frame.n_alloc_len = fill_buffer_cmd->buffer.buffer_len; + + vcd_status = vcd_fill_output_buffer(client_ctx->vcd_handle, + &vcd_frame); + if (!vcd_status) + return TRUE; + else { + ERR("%s(): vcd_fill_output_buffer failed = %u\n", + __func__, vcd_status); + return FALSE; + } + } else { + ERR("%s(): kernel_vaddr not found\n", __func__); + return FALSE; + } +} + + +static u32 vid_dec_flush(struct video_client_ctx *client_ctx, + enum vdec_bufferflush flush_dir) +{ + u32 vcd_status = VCD_ERR_FAIL; + + INFO("\n msm_vidc_dec: %s() called with dir = %u", __func__, + flush_dir); + if (!client_ctx) { + ERR("\n Invalid client_ctx"); + return FALSE; + } + + switch (flush_dir) { + case VDEC_FLUSH_TYPE_INPUT: + vcd_status = vcd_flush(client_ctx->vcd_handle, VCD_FLUSH_INPUT); + break; + case VDEC_FLUSH_TYPE_OUTPUT: + vcd_status = vcd_flush(client_ctx->vcd_handle, + VCD_FLUSH_OUTPUT); + break; + case VDEC_FLUSH_TYPE_ALL: + vcd_status = vcd_flush(client_ctx->vcd_handle, VCD_FLUSH_ALL); + break; + default: + ERR("%s(): Inavlid flush cmd. flush_dir = %u\n", __func__, + flush_dir); + return FALSE; + break; + } + + if (!vcd_status) + return TRUE; + else { + ERR("%s(): vcd_flush failed. vcd_status = %u " + " flush_dir = %u\n", __func__, vcd_status, flush_dir); + return FALSE; + } +} + +static u32 vid_dec_msg_pending(struct video_client_ctx *client_ctx) +{ + u32 islist_empty = 0; + mutex_lock(&client_ctx->msg_queue_lock); + islist_empty = list_empty(&client_ctx->msg_queue); + mutex_unlock(&client_ctx->msg_queue_lock); + + if (islist_empty) { + DBG("%s(): vid_dec msg queue empty\n", __func__); + if (client_ctx->stop_msg) { + DBG("%s(): List empty and Stop Msg set\n", + __func__); + return client_ctx->stop_msg; + } + } else + DBG("%s(): vid_dec msg queue Not empty\n", __func__); + + return !islist_empty; +} + +static u32 vid_dec_get_next_msg(struct video_client_ctx *client_ctx, + struct vdec_msginfo *vdec_msg_info) +{ + int rc; + struct vid_dec_msg *vid_dec_msg = NULL; + + if (!client_ctx) + return FALSE; + + rc = wait_event_interruptible(client_ctx->msg_wait, + vid_dec_msg_pending(client_ctx)); + if (rc < 0 || client_ctx->stop_msg) { + DBG("rc = %d, stop_msg = %u \n", rc, client_ctx->stop_msg); + return FALSE; + } + + mutex_lock(&client_ctx->msg_queue_lock); + if (!list_empty(&client_ctx->msg_queue)) { + DBG("%s(): After Wait \n", __func__); + vid_dec_msg = list_first_entry(&client_ctx->msg_queue, + struct vid_dec_msg, list); + list_del(&vid_dec_msg->list); + memcpy(vdec_msg_info, &vid_dec_msg->vdec_msg_info, + sizeof(struct vdec_msginfo)); + kfree(vid_dec_msg); + } + mutex_unlock(&client_ctx->msg_queue_lock); + return TRUE; +} + +static int vid_dec_ioctl(struct inode *inode, struct file *file, + unsigned cmd, unsigned long arg) +{ + struct video_client_ctx *client_ctx = NULL; + struct vdec_ioctl_msg vdec_msg; + u32 vcd_status; + unsigned long kernel_vaddr, phy_addr, len; + struct file *pmem_file; + u32 result = TRUE; + enum vdec_codec vdec_codec_type; + enum vdec_output_fromat output_format; + struct vdec_picsize video_resoultion; + struct vdec_allocatorproperty vdec_buf_req; + struct vdec_setbuffer_cmd setbuffer; + struct vdec_input_frameinfo input_frame_info; + struct vdec_fillbuffer_cmd fill_buffer_cmd; + enum vdec_bufferflush flush_dir; + struct vdec_msginfo vdec_msg_info; + struct vcd_buffer_requirement_type Buffer_req; + struct vcd_property_meta_data_enable_type metdata_disable; + struct vcd_property_hdr_type header_type; + struct vdec_seqheader seq_header; + struct vcd_sequence_hdr_type vcd_seq_hdr; + + DBG("%s\n", __func__); + + if (_IOC_TYPE(cmd) != VDEC_IOCTL_MAGIC) + return -ENOTTY; + + client_ctx = (struct video_client_ctx *)file->private_data; + if (!client_ctx) { + ERR("!client_ctx. Cannot attach to device handle\n"); + return -ENODEV; + } + + switch (cmd) { + case VDEC_IOCTL_SET_CODEC: + DBG("VDEC_IOCTL_SET_CODEC\n"); + if (copy_from_user(&vdec_msg, (void __user *)arg, + sizeof(vdec_msg))) + return -EFAULT; + if (copy_from_user(&vdec_codec_type, + (void __user *)vdec_msg.inputparam, + sizeof(vdec_codec_type))) + return -EFAULT; + DBG("setting code type = %u\n", vdec_codec_type); + result = vid_dec_set_codec(client_ctx, &vdec_codec_type); + if (!result) + return -EIO; + metdata_disable.n_meta_data_enable_flag = 0; + header_type.n_size = sizeof(metdata_disable); + header_type.prop_id = VCD_I_METADATA_ENABLE; + + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &header_type, + (void *)&metdata_disable); + + if (vcd_status) { + ERR("%s() : vcd_set_property Failed for" + "Meta Data Disable\n", __func__); + return -ENODEV; + } + DBG("Disabled Meta Data \n"); + break; + case VDEC_IOCTL_SET_OUTPUT_FORMAT: + DBG("VDEC_IOCTL_SET_OUTPUT_FORMAT\n"); + if (copy_from_user(&vdec_msg, (void __user *)arg, + sizeof(vdec_msg))) + return -EFAULT; + if (copy_from_user(&output_format, + (void __user *)vdec_msg.inputparam, + sizeof(output_format))) + return -EFAULT; + + result = vid_dec_set_output_format(client_ctx, &output_format); + + if (!result) + return -EIO; + break; + case VDEC_IOCTL_SET_PICRES: + DBG("VDEC_IOCTL_SET_PICRES\n"); + if (copy_from_user(&vdec_msg, (void __user *)arg, + sizeof(vdec_msg))) + return -EFAULT; + if (copy_from_user(&video_resoultion, + (void __user *)vdec_msg.inputparam, + sizeof(video_resoultion))) + return -EFAULT; + result = + vid_dec_set_frame_resolution(client_ctx, &video_resoultion); + if (!result) + return -EIO; + break; + case VDEC_IOCTL_GET_PICRES: + DBG("VDEC_IOCTL_GET_PICRES\n"); + if (copy_from_user(&vdec_msg, (void __user *)arg, + sizeof(vdec_msg))) + return -EFAULT; + if (copy_from_user(&video_resoultion, + (void __user *)vdec_msg.outputparam, + sizeof(video_resoultion))) + return -EFAULT; + + result = vid_dec_get_frame_resolution(client_ctx, + &video_resoultion); + + if (result) { + if (copy_to_user((void __user *)vdec_msg.outputparam, + &video_resoultion, sizeof(video_resoultion))) + return -EFAULT; + } else + return -EIO; + break; + case VDEC_IOCTL_SET_BUFFER_REQ: + if (copy_from_user(&vdec_msg, (void __user *)arg, + sizeof(vdec_msg))) + return -EFAULT; + + if (copy_from_user(&vdec_buf_req, + (void __user *)vdec_msg.inputparam, + sizeof(vdec_buf_req))) + return -EFAULT; + + Buffer_req.n_actual_count = vdec_buf_req.actualcount; + Buffer_req.n_align = vdec_buf_req.alignment; + Buffer_req.n_max_count = vdec_buf_req.maxcount; + Buffer_req.n_min_count = vdec_buf_req.mincount; + Buffer_req.n_size = vdec_buf_req.buffer_size; + + switch (vdec_buf_req.buffer_type) { + case VDEC_BUFFER_TYPE_INPUT: + vcd_status = + vcd_set_buffer_requirements(client_ctx->vcd_handle, + VCD_BUFFER_INPUT, + &Buffer_req); + break; + case VDEC_BUFFER_TYPE_OUTPUT: + vcd_status = + vcd_set_buffer_requirements(client_ctx->vcd_handle, + VCD_BUFFER_OUTPUT, + &Buffer_req); + break; + default: + vcd_status = VCD_ERR_BAD_POINTER; + break; + } + + if (vcd_status) + return -EFAULT; + break; + case VDEC_IOCTL_GET_BUFFER_REQ: + DBG("VDEC_IOCTL_GET_BUFFER_REQ\n"); + if (copy_from_user(&vdec_msg, (void __user *)arg, + sizeof(vdec_msg))) + return -EFAULT; + if (copy_from_user(&vdec_buf_req, + (void __user *)vdec_msg.outputparam, + sizeof(vdec_buf_req))) + return -EFAULT; + + result = vid_dec_get_buffer_req(client_ctx, &vdec_buf_req); + + if (result) { + if (copy_to_user((void __user *)vdec_msg.outputparam, + &vdec_buf_req, sizeof(vdec_buf_req))) + return -EFAULT; + } else + return -EIO; + break; + case VDEC_IOCTL_SET_BUFFER: + DBG("VDEC_IOCTL_SET_BUFFER\n"); + if (copy_from_user(&vdec_msg, (void __user *)arg, + sizeof(vdec_msg))) + return -EFAULT; + if (copy_from_user(&setbuffer, + (void __user *)vdec_msg.inputparam, sizeof(setbuffer))) + return -EFAULT; + result = vid_dec_set_buffer(client_ctx, &setbuffer); + break; + case VDEC_IOCTL_FREE_BUFFER: + DBG("VDEC_IOCTL_FREE_BUFFER\n"); + if (copy_from_user(&vdec_msg, (void __user *)arg, + sizeof(vdec_msg))) + return -EFAULT; + if (copy_from_user(&setbuffer, + (void __user *)vdec_msg.inputparam, sizeof(setbuffer))) + return -EFAULT; + + result = vid_dec_free_buffer(client_ctx, &setbuffer); + + if (!result) + return -EIO; + break; + + + case VDEC_IOCTL_CMD_START: + DBG(" VDEC_IOCTL_CMD_START\n"); + result = vid_dec_start_stop(client_ctx, TRUE); + + if (!result) + return -EIO; + break; + case VDEC_IOCTL_CMD_STOP: + + DBG("VDEC_IOCTL_CMD_STOP\n"); + result = vid_dec_start_stop(client_ctx, FALSE); + + if (!result) + return -EIO; + break; + case VDEC_IOCTL_CMD_PAUSE: + result = vid_dec_pause_resume(client_ctx, TRUE); + + if (!result) + return -EIO; + break; + case VDEC_IOCTL_CMD_RESUME: + DBG("VDEC_IOCTL_CMD_PAUSE\n"); + result = vid_dec_pause_resume(client_ctx, FALSE); + + if (!result) + return -EIO; + break; + case VDEC_IOCTL_DECODE_FRAME: + DBG("VDEC_IOCTL_DECODE_FRAME\n"); + if (copy_from_user(&vdec_msg, (void __user *)arg, + sizeof(vdec_msg))) + return -EFAULT; + if (copy_from_user(&input_frame_info, + (void __user *)vdec_msg.inputparam, + sizeof(input_frame_info))) + return -EFAULT; + + result = vid_dec_decode_frame(client_ctx, &input_frame_info); + + if (!result) + return -EIO; + break; + case VDEC_IOCTL_FILL_OUTPUT_BUFFER: + DBG("VDEC_IOCTL_FILL_OUTPUT_BUFFER\n"); + if (copy_from_user(&vdec_msg, (void __user *)arg, + sizeof(vdec_msg))) + return -EFAULT; + if (copy_from_user(&fill_buffer_cmd, + (void __user *)vdec_msg.inputparam, + sizeof(fill_buffer_cmd))) + return -EFAULT; + result = vid_dec_fill_output_buffer(client_ctx, + &fill_buffer_cmd); + if (!result) + return -EIO; + break; + case VDEC_IOCTL_CMD_FLUSH: + DBG("VDEC_IOCTL_CMD_FLUSH\n"); + if (copy_from_user(&vdec_msg, (void __user *)arg, + sizeof(vdec_msg))) + return -EFAULT; + if (copy_from_user(&flush_dir, + (void __user *)vdec_msg.inputparam, + sizeof(flush_dir))) + return -EFAULT; + + result = vid_dec_flush(client_ctx, flush_dir); + + if (!result) + return -EIO; + break; + case VDEC_IOCTL_GET_NEXT_MSG: + DBG("VDEC_IOCTL_GET_NEXT_MSG\n"); + if (copy_from_user(&vdec_msg, (void __user *)arg, + sizeof(vdec_msg))) + return -EFAULT; + result = vid_dec_get_next_msg(client_ctx, &vdec_msg_info); + + if (!result) + return -EIO; + if (copy_to_user((void __user *)vdec_msg.outputparam, + &vdec_msg_info, sizeof(vdec_msg_info))) + return -EFAULT; + break; + case VDEC_IOCTL_STOP_NEXT_MSG: + DBG("VDEC_IOCTL_STOP_NEXT_MSG\n"); + client_ctx->stop_msg = 1; + wake_up(&client_ctx->msg_wait); + break; + case VDEC_IOCTL_SET_SEQUENCE_HEADER: + DBG("VDEC_IOCTL_SET_SEQUENCE_HEADER\n"); + if (copy_from_user(&vdec_msg, (void __user *)arg, + sizeof(vdec_msg))) { + ERR("Copy from user vdec_msg failed \n"); + return -EFAULT; + } + if (copy_from_user(&seq_header, + (void __user *)vdec_msg.inputparam, + sizeof(seq_header))) { + ERR("Copy from user seq_header failed \n"); + return -EFAULT; + } + if (!seq_header.seq_header_len) { + ERR("Seq Len is Zero \n"); + return -EFAULT; + } + + if (get_pmem_file(seq_header.pmem_fd, + &phy_addr, &kernel_vaddr, &len, &pmem_file)) { + ERR("%s(): get_pmem_file failed\n", __func__); + return FALSE; + } + put_pmem_file(pmem_file); + + vcd_seq_hdr.n_sequence_header_len = seq_header.seq_header_len; + kernel_vaddr += (unsigned long)seq_header.pmem_offset; + vcd_seq_hdr.p_sequence_header = (u8 *)kernel_vaddr; + if (!vcd_seq_hdr.p_sequence_header) { + ERR("Sequence Header pointer failed\n"); + return -EFAULT; + } + client_ctx->seq_header_set = TRUE; + if (vcd_decode_start(client_ctx->vcd_handle, &vcd_seq_hdr)) { + ERR("Decode start Failed \n"); + client_ctx->seq_header_set = FALSE; + return -EFAULT; + } + DBG("Wait Client completion Sequence Header \n"); + wait_for_completion(&client_ctx->event); + vcd_seq_hdr.p_sequence_header = NULL; + if (client_ctx->event_status) { + ERR("Set Seq Header status is failed"); + return -EFAULT; + } + break; + case VDEC_IOCTL_GET_NUMBER_INSTANCES: + DBG("VDEC_IOCTL_GET_NUMBER_INSTANCES\n"); + if (copy_from_user(&vdec_msg, (void __user *)arg, + sizeof(vdec_msg))) + return -EFAULT; + if (copy_to_user((void __user *)vdec_msg.outputparam, + &vid_dec_device_p->num_clients, sizeof(u32))) + return -EFAULT; + break; + + default: + ERR("%s(): Unsupported ioctl\n", __func__); + return -ENOTTY; + + break; + } + + return 0; +} + +static u32 vid_dec_close_client(struct video_client_ctx *client_ctx) +{ + u32 vcd_status; + int rc; + + INFO("\n msm_vidc_dec: Inside %s()", __func__); + if (!client_ctx || (!client_ctx->vcd_handle)) { + ERR("\n Invalid client_ctx"); + return FALSE; + } + + mutex_lock(&vid_dec_device_p->lock); + vcd_status = vcd_stop(client_ctx->vcd_handle); + + if (!vcd_status) { + rc = wait_for_completion_timeout(&client_ctx->event, + (5 * HZ)/10); + if (!rc) + DBG("%s:ERROR vcd_stop time out rc = %d\n", + __func__, rc); + + if (client_ctx->event_status) + ERR("%s:ERROR vcd_stop event_status failure\n", + __func__); + } + vcd_status = vcd_close(client_ctx->vcd_handle); + + if (vcd_status) { + mutex_unlock(&vid_dec_device_p->lock); + return FALSE; + } + memset((void *)client_ctx, 0, sizeof(struct video_client_ctx)); + vid_dec_device_p->num_clients--; + mutex_unlock(&vid_dec_device_p->lock); + return TRUE; +} + +static int vid_dec_open(struct inode *inode, struct file *file) +{ + s32 client_index; + struct video_client_ctx *client_ctx; + u32 vcd_status = VCD_ERR_FAIL; + + INFO("\n msm_vidc_dec: Inside %s()", __func__); + mutex_lock(&vid_dec_device_p->lock); + + if (vid_dec_device_p->num_clients == VID_DEC_MAX_DECODER_CLIENTS) { + ERR("ERROR : vid_dec_open() max number of clients" + "limit reached\n"); + mutex_unlock(&vid_dec_device_p->lock); + return -ENODEV; + } + +#ifndef USE_RES_TRACKER + DBG("Resource Tracker not in use"); + if (!vid_c_enable_clk(VID_C_HCLK_RATE)) { + ERR("ERROR : vid_dec_open() clock enabled failed\n"); + mutex_unlock(&vid_dec_device_p->lock); + return -ENODEV; + } +#endif + + DBG(" Virtual Address of ioremap is %p\n", vid_dec_device_p->virt_base); + if (!vid_dec_device_p->num_clients) { + if (!vid_c_load_firmware()) + return -ENODEV; + } + + client_index = vid_dec_get_empty_client_index(); + if (client_index == -1) { + ERR("%s() : No free clients client_index == -1\n", __func__); + return -ENODEV; + } + client_ctx = &vid_dec_device_p->vdec_clients[client_index]; + vid_dec_device_p->num_clients++; + init_completion(&client_ctx->event); + mutex_init(&client_ctx->msg_queue_lock); + INIT_LIST_HEAD(&client_ctx->msg_queue); + init_waitqueue_head(&client_ctx->msg_wait); + client_ctx->stop_msg = 0; + + vcd_status = vcd_open(vid_dec_device_p->device_handle, TRUE, + vid_dec_vcd_cb, client_ctx); + wait_for_completion(&client_ctx->event); + client_ctx->seq_header_set = FALSE; + file->private_data = client_ctx; + mutex_unlock(&vid_dec_device_p->lock); + return 0; +} + +static int vid_dec_release(struct inode *inode, struct file *file) +{ + struct video_client_ctx *client_ctx = file->private_data; + + INFO("\n msm_vidc_dec: Inside %s()", __func__); + vid_dec_close_client(client_ctx); + vid_c_release_firmware(); +#ifndef USE_RES_TRACKER + vid_c_disable_clk(); +#endif + INFO("\n msm_vidc_dec: Return from %s()", __func__); + return 0; +} + +static const struct file_operations vid_dec_fops = { + .owner = THIS_MODULE, + .open = vid_dec_open, + .release = vid_dec_release, + .ioctl = vid_dec_ioctl, +}; + +void vid_dec_interrupt_deregister(void) +{ +} + +void vid_dec_interrupt_register(void *device_name) +{ +} + +void vid_dec_interrupt_clear(void) +{ +} + +void *vid_dec_map_dev_base_addr(void *device_name) +{ + return vid_dec_device_p->virt_base; +} + +static int vid_dec_vcd_init(void) +{ + int rc; + struct vcd_init_config_type vcd_init_config; + u32 i; + + /* init_timer(&hw_timer); */ + INFO("\n msm_vidc_dec: Inside %s()", __func__); + vid_dec_device_p->num_clients = 0; + + for (i = 0; i < VID_DEC_MAX_DECODER_CLIENTS; i++) { + memset((void *)&vid_dec_device_p->vdec_clients[i], 0, + sizeof(vid_dec_device_p->vdec_clients[i])); + } + + mutex_init(&vid_dec_device_p->lock); + vid_dec_device_p->virt_base = vid_c_get_ioaddr(); + DBG("%s() : base address for VIDC core %u\n", __func__, \ + (int)vid_dec_device_p->virt_base); + + if (!vid_dec_device_p->virt_base) { + ERR("%s() : ioremap failed\n", __func__); + return -ENOMEM; + } + + vcd_init_config.p_device_name = "VID_C"; + vcd_init_config.pf_map_dev_base_addr = vid_dec_map_dev_base_addr; + vcd_init_config.pf_interrupt_clr = vid_dec_interrupt_clear; + vcd_init_config.pf_register_isr = vid_dec_interrupt_register; + vcd_init_config.pf_deregister_isr = vid_dec_interrupt_deregister; + vcd_init_config.pf_timer_create = vid_c_timer_create; + vcd_init_config.pf_timer_release = vid_c_timer_release; + vcd_init_config.pf_timer_start = vid_c_timer_start; + vcd_init_config.pf_timer_stop = vid_c_timer_stop; + + rc = vcd_init(&vcd_init_config, &vid_dec_device_p->device_handle); + + if (rc) { + ERR("%s() : vcd_init failed\n", __func__); + return -ENODEV; + } + return 0; +} + +static int __init vid_dec_init(void) +{ + int rc = 0; + struct device *class_devp; + + INFO("\n msm_vidc_dec: Inside %s()", __func__); + vid_dec_device_p = kzalloc(sizeof(struct vid_dec_dev), GFP_KERNEL); + if (!vid_dec_device_p) { + ERR("%s Unable to allocate memory for vid_dec_dev\n", + __func__); + return -ENOMEM; + } + + rc = alloc_chrdev_region(&vid_dec_dev_num, 0, 1, VID_DEC_NAME); + if (rc < 0) { + ERR("%s: alloc_chrdev_region Failed rc = %d\n", + __func__, rc); + goto error_vid_dec_alloc_chrdev_region; + } + + vid_dec_class = class_create(THIS_MODULE, VID_DEC_NAME); + if (IS_ERR(vid_dec_class)) { + rc = PTR_ERR(vid_dec_class); + ERR("%s: couldn't create vid_dec_class rc = %d\n", + __func__, rc); + + goto error_vid_dec_class_create; + } + + class_devp = device_create(vid_dec_class, NULL, vid_dec_dev_num, NULL, + VID_DEC_NAME); + + if (IS_ERR(class_devp)) { + rc = PTR_ERR(class_devp); + ERR("%s: class device_create failed %d\n", + __func__, rc); + goto error_vid_dec_class_device_create; + } + + vid_dec_device_p->device = class_devp; + + cdev_init(&vid_dec_device_p->cdev, &vid_dec_fops); + vid_dec_device_p->cdev.owner = THIS_MODULE; + rc = cdev_add(&(vid_dec_device_p->cdev), vid_dec_dev_num, 1); + + if (rc < 0) { + ERR("%s: cdev_add failed %d\n", __func__, rc); + goto error_vid_dec_cdev_add; + } + vid_dec_vcd_init(); + return 0; + +error_vid_dec_cdev_add: + device_destroy(vid_dec_class, vid_dec_dev_num); +error_vid_dec_class_device_create: + class_destroy(vid_dec_class); +error_vid_dec_class_create: + unregister_chrdev_region(vid_dec_dev_num, 1); +error_vid_dec_alloc_chrdev_region: + kfree(vid_dec_device_p); + + return rc; +} + +static void __exit vid_dec_exit(void) +{ + INFO("\n msm_vidc_dec: Inside %s()", __func__); + cdev_del(&(vid_dec_device_p->cdev)); + device_destroy(vid_dec_class, vid_dec_dev_num); + class_destroy(vid_dec_class); + unregister_chrdev_region(vid_dec_dev_num, 1); + kfree(vid_dec_device_p); + INFO("\n msm_vidc_dec: Return from %s()", __func__); +} + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Video decoder driver"); +MODULE_VERSION("1.0"); + +module_init(vid_dec_init); +module_exit(vid_dec_exit); diff --git a/drivers/misc/video_core/720p/dec/vdec_internal.h b/drivers/misc/video_core/720p/dec/vdec_internal.h new file mode 100644 index 0000000000000..f6ce51027fa0c --- /dev/null +++ b/drivers/misc/video_core/720p/dec/vdec_internal.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef VDEC_INTERNAL_H +#define VDEC_INTERNAL_H + +#include +#include +#include "video_core_init.h" + +#define VID_DEC_MAX_DECODER_CLIENTS 16 + +struct vid_dec_msg { + struct list_head list; + struct vdec_msginfo vdec_msg_info; +}; + +struct vid_dec_dev { + struct cdev cdev; + struct device *device; + resource_size_t phys_base; + void __iomem *virt_base; + unsigned int irq; + struct clk *hclk; + struct clk *hclk_div2; + struct clk *pclk; + unsigned long hclk_rate; + struct mutex lock; + s32 device_handle; + struct video_client_ctx vdec_clients[VID_DEC_MAX_DECODER_CLIENTS]; + u32 num_clients; + void(*pf_timer_handler)(void *); +}; + +#endif diff --git a/drivers/misc/video_core/720p/enc/venc.c b/drivers/misc/video_core/720p/enc/venc.c new file mode 100644 index 0000000000000..1790af4fa788f --- /dev/null +++ b/drivers/misc/video_core/720p/enc/venc.c @@ -0,0 +1,1752 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "video_core_type.h" +#include "vcd_api.h" +#include "venc_internal.h" +#include "video_core_init.h" + +#define VID_ENC_NAME "msm_vidc_enc" +#define VID_C_HCLK_RATE 170667000 + +#if DEBUG +#define DBG(x...) printk(KERN_DEBUG x) +#else +#define DBG(x...) +#endif + +#define INFO(x...) printk(KERN_INFO x) +#define ERR(x...) printk(KERN_ERR x) + +static struct vid_enc_dev *vid_enc_device_p; +static dev_t vid_enc_dev_num; +static struct class *vid_enc_class; +static int vid_enc_ioctl(struct inode *inode, struct file *file, + unsigned cmd, unsigned long arg); +static int stop_cmd; + +static s32 vid_enc_get_empty_client_index(void) +{ + u32 i; + u32 found = FALSE; + + for (i = 0; i < VID_ENC_MAX_ENCODER_CLIENTS; i++) { + if (!vid_enc_device_p->venc_clients[i].vcd_handle) { + found = TRUE; + break; + } + } + if (!found) { + ERR("%s():ERROR No space for new client\n", + __func__); + return -1; + } else { + DBG("%s(): available client index = %u\n", + __func__, i); + return i; + } +} + + +u32 vid_enc_get_status(u32 status) +{ + u32 venc_status; + + switch (status) { + case VCD_S_SUCCESS: + venc_status = VEN_S_SUCCESS; + break; + case VCD_ERR_FAIL: + venc_status = VEN_S_EFAIL; + break; + case VCD_ERR_ALLOC_FAIL: + venc_status = VEN_S_ENOSWRES; + break; + case VCD_ERR_ILLEGAL_OP: + venc_status = VEN_S_EINVALCMD; + break; + case VCD_ERR_ILLEGAL_PARM: + venc_status = VEN_S_EBADPARAM; + break; + case VCD_ERR_BAD_POINTER: + case VCD_ERR_BAD_HANDLE: + venc_status = VEN_S_EFATAL; + break; + case VCD_ERR_NOT_SUPPORTED: + venc_status = VEN_S_ENOTSUPP; + break; + case VCD_ERR_BAD_STATE: + venc_status = VEN_S_EINVALSTATE; + break; + case VCD_ERR_MAX_CLIENT: + venc_status = VEN_S_ENOHWRES; + break; + default: + venc_status = VEN_S_EFAIL; + break; + } + return venc_status; +} + +static void vid_enc_notify_client(struct video_client_ctx *client_ctx) +{ + if (client_ctx) + complete(&client_ctx->event); +} + +void vid_enc_vcd_open_done(struct video_client_ctx *client_ctx, + struct vcd_handle_container_type *handle_container) +{ + DBG("vid_enc_vcd_open_done\n"); + + if (client_ctx) { + if (handle_container) + client_ctx->vcd_handle = handle_container->handle; + else + ERR("%s(): ERROR. handle_container is NULL\n", + __func__); + vid_enc_notify_client(client_ctx); + } else + ERR("%s(): ERROR. client_ctx is NULL\n", + __func__); +} + +static void vid_enc_input_frame_done(struct video_client_ctx *client_ctx, + u32 event, u32 status, + struct vcd_frame_data_type *vcd_frame_data) +{ + struct vid_enc_msg *venc_msg; + + if (!client_ctx || !vcd_frame_data) { + ERR("vid_enc_input_frame_done() NULL pointer \n"); + return; + } + + venc_msg = kzalloc(sizeof(struct vid_enc_msg), + GFP_KERNEL); + if (!venc_msg) { + ERR("vid_enc_input_frame_done(): cannot allocate vid_enc_msg " + " buffer\n"); + return; + } + + venc_msg->venc_msg_info.statuscode = vid_enc_get_status(status); + + if (event == VCD_EVT_RESP_INPUT_DONE) { + venc_msg->venc_msg_info.msgcode = + VEN_MSG_INPUT_BUFFER_DONE; + DBG("Send INPUT_DON message to client = %p\n", + client_ctx); + } else if (event == VCD_EVT_RESP_INPUT_FLUSHED) { + venc_msg->venc_msg_info.msgcode = VEN_MSG_INPUT_BUFFER_DONE; + DBG("Send INPUT_FLUSHED message to client = %p\n", + client_ctx); + } else { + ERR("vid_enc_input_frame_done(): invalid event type\n"); + return; + } + + venc_msg->venc_msg_info.buf.clientdata = + (void *)vcd_frame_data->n_frm_clnt_data; + venc_msg->venc_msg_info.msgdata_size = + sizeof(struct vid_enc_msg); + + mutex_lock(&client_ctx->msg_queue_lock); + list_add_tail(&venc_msg->list, &client_ctx->msg_queue); + mutex_unlock(&client_ctx->msg_queue_lock); + wake_up(&client_ctx->msg_wait); +} + +static void vid_enc_output_frame_done(struct video_client_ctx *client_ctx, + u32 event, u32 status, + struct vcd_frame_data_type *vcd_frame_data) +{ + struct vid_enc_msg *venc_msg; + unsigned long kernel_vaddr, phy_addr, user_vaddr; + int pmem_fd; + struct file *file; + s32 buffer_index = -1; + + if (!client_ctx || !vcd_frame_data) { + ERR("vid_enc_input_frame_done() NULL pointer \n"); + return; + } + + venc_msg = kzalloc(sizeof(struct vid_enc_msg), + GFP_KERNEL); + if (!venc_msg) { + ERR("vid_enc_input_frame_done(): cannot allocate vid_enc_msg " + " buffer\n"); + return; + } + + venc_msg->venc_msg_info.statuscode = vid_enc_get_status(status); + + if (event == VCD_EVT_RESP_OUTPUT_DONE) + venc_msg->venc_msg_info.msgcode = + VEN_MSG_OUTPUT_BUFFER_DONE; + + else if (event == VCD_EVT_RESP_OUTPUT_FLUSHED) + venc_msg->venc_msg_info.msgcode = + VEN_MSG_OUTPUT_BUFFER_DONE; + else { + ERR("QVD: vid_enc_output_frame_done invalid cmd type \n"); + return; + } + + kernel_vaddr = + (unsigned long)vcd_frame_data->p_virtual; + + if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, + FALSE, &user_vaddr, &kernel_vaddr, + &phy_addr, &pmem_fd, &file, + &buffer_index)) { + + /* Buffer address in user space */ + venc_msg->venc_msg_info.buf.ptrbuffer = (u8 *) user_vaddr; + /* Buffer address in user space */ + venc_msg->venc_msg_info.buf.clientdata = (void *) + vcd_frame_data->n_frm_clnt_data; + /* Data length */ + venc_msg->venc_msg_info.buf.len = + vcd_frame_data->n_data_len; + venc_msg->venc_msg_info.buf.flags = + vcd_frame_data->n_flags; + /* Timestamp pass-through from input frame */ + venc_msg->venc_msg_info.buf.timestamp = + vcd_frame_data->time_stamp; + + /* Decoded picture width and height */ + venc_msg->venc_msg_info.msgdata_size = + sizeof(struct venc_buffer); + } else { + ERR("vid_enc_output_frame_done UVA can not be found\n"); + venc_msg->venc_msg_info.statuscode = + VEN_S_EFATAL; + } + + mutex_lock(&client_ctx->msg_queue_lock); + list_add_tail(&venc_msg->list, &client_ctx->msg_queue); + mutex_unlock(&client_ctx->msg_queue_lock); + wake_up(&client_ctx->msg_wait); +} + +static void vid_enc_lean_event(struct video_client_ctx *client_ctx, + u32 event, u32 status) +{ + struct vid_enc_msg *venc_msg; + if (!client_ctx) { + ERR("%s(): !client_ctx pointer \n", + __func__); + return; + } + + venc_msg = kzalloc(sizeof(struct vid_enc_msg), + GFP_KERNEL); + if (!venc_msg) { + ERR("%s(): cannot allocate vid_enc_msg buffer\n", + __func__); + return; + } + + venc_msg->venc_msg_info.statuscode = + vid_enc_get_status(status); + + switch (event) { + case VCD_EVT_RESP_FLUSH_INPUT_DONE: + INFO("\n msm_vidc_enc: Sending VCD_EVT_RESP_FLUSH_INPUT_DONE" + " to client"); + venc_msg->venc_msg_info.msgcode = + VEN_MSG_FLUSH_INPUT_DONE; + break; + case VCD_EVT_RESP_FLUSH_OUTPUT_DONE: + INFO("\n msm_vidc_enc: Sending VCD_EVT_RESP_FLUSH_OUTPUT_DONE" + " to client"); + venc_msg->venc_msg_info.msgcode = + VEN_MSG_FLUSH_OUPUT_DONE; + break; + + case VCD_EVT_RESP_START: + INFO("\n msm_vidc_enc: Sending VCD_EVT_RESP_START" + " to client"); + venc_msg->venc_msg_info.msgcode = + VEN_MSG_START; + break; + + case VCD_EVT_RESP_STOP: + INFO("\n msm_vidc_enc: Sending VCD_EVT_RESP_STOP" + " to client"); + venc_msg->venc_msg_info.msgcode = + VEN_MSG_STOP; + break; + + case VCD_EVT_RESP_PAUSE: + INFO("\n msm_vidc_enc: Sending VCD_EVT_RESP_PAUSE" + " to client"); + venc_msg->venc_msg_info.msgcode = + VEN_MSG_PAUSE; + break; + + default: + ERR("%s() : unknown event type \n", + __func__); + break; + } + + venc_msg->venc_msg_info.msgdata_size = 0; + + mutex_lock(&client_ctx->msg_queue_lock); + list_add_tail(&venc_msg->list, &client_ctx->msg_queue); + mutex_unlock(&client_ctx->msg_queue_lock); + wake_up(&client_ctx->msg_wait); +} + + +void vid_enc_vcd_cb(u32 event, u32 status, + void *info, u32 size, void *handle, + void *const client_data) +{ + struct video_client_ctx *client_ctx = + (struct video_client_ctx *)client_data; + + DBG("Entering %s()\n", __func__); + + if (!client_ctx) { + ERR("%s(): client_ctx is NULL \n", __func__); + return; + } + + client_ctx->event_status = status; + + switch (event) { + case VCD_EVT_RESP_OPEN: + vid_enc_vcd_open_done(client_ctx, + (struct vcd_handle_container_type *)info); + break; + + case VCD_EVT_RESP_INPUT_DONE: + case VCD_EVT_RESP_INPUT_FLUSHED: + vid_enc_input_frame_done(client_ctx, event, + status, (struct vcd_frame_data_type *)info); + break; + + case VCD_EVT_RESP_OUTPUT_DONE: + case VCD_EVT_RESP_OUTPUT_FLUSHED: + vid_enc_output_frame_done(client_ctx, event, status, + (struct vcd_frame_data_type *)info); + break; + + case VCD_EVT_RESP_PAUSE: + case VCD_EVT_RESP_START: + case VCD_EVT_RESP_STOP: + case VCD_EVT_RESP_FLUSH_INPUT_DONE: + case VCD_EVT_RESP_FLUSH_OUTPUT_DONE: + case VCD_EVT_IND_RECONFIG: + case VCD_EVT_IND_HWERRFATAL: + case VCD_EVT_IND_RESOURCES_LOST: + vid_enc_lean_event(client_ctx, event, status); + break; + + default: + ERR("%s() : Error - Invalid event type =%u\n", + __func__, event); + break; + } +} + +static u32 vid_enc_msg_pending(struct video_client_ctx *client_ctx) +{ + u32 islist_empty = 0; + + mutex_lock(&client_ctx->msg_queue_lock); + islist_empty = list_empty(&client_ctx->msg_queue); + mutex_unlock(&client_ctx->msg_queue_lock); + + if (islist_empty) { + DBG("%s(): vid_enc msg queue empty\n", + __func__); + if (client_ctx->stop_msg) { + DBG("%s(): List empty and Stop Msg set\n", + __func__); + return client_ctx->stop_msg; + } + } else + DBG("%s(): vid_enc msg queue Not empty\n", + __func__); + + return !islist_empty; +} + +static u32 vid_enc_get_next_msg(struct video_client_ctx *client_ctx, + struct venc_msg *venc_msg_info) +{ + int rc; + struct vid_enc_msg *vid_enc_msg = NULL; + + if (!client_ctx) + return FALSE; + + rc = wait_event_interruptible(client_ctx->msg_wait, + vid_enc_msg_pending(client_ctx)); + + if (rc < 0 || client_ctx->stop_msg) { + DBG("rc = %d, stop_msg = %u\n", rc, client_ctx->stop_msg); + return FALSE; + } + + mutex_lock(&client_ctx->msg_queue_lock); + + if (!list_empty(&client_ctx->msg_queue)) { + DBG("%s(): After Wait \n", __func__); + vid_enc_msg = list_first_entry(&client_ctx->msg_queue, + struct vid_enc_msg, list); + list_del(&vid_enc_msg->list); + memcpy(venc_msg_info, &vid_enc_msg->venc_msg_info, + sizeof(struct venc_msg)); + kfree(vid_enc_msg); + } + mutex_unlock(&client_ctx->msg_queue_lock); + return TRUE; +} + +static u32 vid_enc_close_client(struct video_client_ctx *client_ctx) +{ + u32 vcd_status; + + int rc; + + INFO("\n msm_vidc_enc: Inside %s()", __func__); + if (!client_ctx || (!client_ctx->vcd_handle)) { + ERR("\n %s(): Invalid client_ctx", __func__); + return FALSE; + } + + mutex_lock(&vid_enc_device_p->lock); + + if (!stop_cmd) { + vcd_status = vcd_stop(client_ctx->vcd_handle); + DBG("Waiting for VCD_STOP: Before Timeout\n"); + if (!vcd_status) { + rc = wait_for_completion_timeout(&client_ctx->event, + 5 * HZ); + if (!rc) { + ERR("%s:ERROR vcd_stop time out" + "rc = %d\n", __func__, rc); + } + + if (client_ctx->event_status) { + ERR("%s:ERROR " + "vcd_stop Not successs \n", __func__); + } + } + } + DBG("VCD_STOPPED: After Timeout, calling VCD_CLOSE\n"); + vcd_status = vcd_close(client_ctx->vcd_handle); + + if (vcd_status) { + mutex_unlock(&vid_enc_device_p->lock); + return FALSE; + } + + memset((void *)client_ctx, 0, + sizeof(struct video_client_ctx)); + + vid_enc_device_p->num_clients--; + stop_cmd = 0; + mutex_unlock(&vid_enc_device_p->lock); + return TRUE; +} + + +static int vid_enc_open(struct inode *inode, struct file *file) +{ + s32 client_index; + struct video_client_ctx *client_ctx; + u32 vcd_status = VCD_ERR_FAIL; + + INFO("\n msm_vidc_enc: Inside %s()", __func__); + + mutex_lock(&vid_enc_device_p->lock); + + stop_cmd = 0; + if (vid_enc_device_p->num_clients == VID_ENC_MAX_ENCODER_CLIENTS) { + ERR("ERROR : vid_enc_open() max number of clients" + "limit reached\n"); + mutex_unlock(&vid_enc_device_p->lock); + return -ENODEV; + } + +#ifndef USE_RES_TRACKER + DBG("Resource Tracker not in use"); + if (!vid_c_enable_clk(VID_C_HCLK_RATE)) { + ERR("ERROR : vid_enc_open() clock enabled failed\n"); + mutex_unlock(&vid_enc_device_p->lock); + return -ENODEV; + } +#endif + + DBG(" Virtual Address of ioremap is %p\n", vid_enc_device_p->virt_base); + if (!vid_enc_device_p->num_clients) { + if (!vid_c_load_firmware()) + return -ENODEV; + } + + client_index = vid_enc_get_empty_client_index(); + + if (client_index == -1) { + ERR("%s() : No free clients client_index == -1\n", + __func__); + return -ENODEV; + } + + client_ctx = + &vid_enc_device_p->venc_clients[client_index]; + vid_enc_device_p->num_clients++; + + init_completion(&client_ctx->event); + mutex_init(&client_ctx->msg_queue_lock); + INIT_LIST_HEAD(&client_ctx->msg_queue); + init_waitqueue_head(&client_ctx->msg_wait); + vcd_status = vcd_open(vid_enc_device_p->device_handle, FALSE, + vid_enc_vcd_cb, client_ctx); + client_ctx->stop_msg = 0; + + wait_for_completion(&client_ctx->event); + file->private_data = client_ctx; + mutex_unlock(&vid_enc_device_p->lock); + return 0; +} + +static int vid_enc_release(struct inode *inode, struct file *file) +{ + struct video_client_ctx *client_ctx = file->private_data; + INFO("\n msm_vidc_enc: Inside %s()", __func__); + vid_enc_close_client(client_ctx); + vid_c_release_firmware(); +#ifndef USE_RES_TRACKER + vid_c_disable_clk(); +#endif + INFO("\n msm_vidc_enc: Return from %s()", __func__); + return 0; +} + +static const struct file_operations vid_enc_fops = { + .owner = THIS_MODULE, + .open = vid_enc_open, + .release = vid_enc_release, + .ioctl = vid_enc_ioctl +}; + +void vid_enc_interrupt_deregister(void) +{ +} + +void vid_enc_interrupt_register(void *device_name) +{ +} + +void vid_enc_interrupt_clear(void) +{ +} + +void *vid_enc_map_dev_base_addr(void *device_name) +{ + return vid_enc_device_p->virt_base; +} + +static int vid_enc_vcd_init(void) +{ + int rc; + struct vcd_init_config_type vcd_init_config; + u32 i; + + INFO("\n msm_vidc_enc: Inside %s()", __func__); + vid_enc_device_p->num_clients = 0; + + for (i = 0; i < VID_ENC_MAX_ENCODER_CLIENTS; i++) { + memset((void *)&vid_enc_device_p->venc_clients[i], 0, + sizeof(vid_enc_device_p->venc_clients[i])); + } + + mutex_init(&vid_enc_device_p->lock); + vid_enc_device_p->virt_base = vid_c_get_ioaddr(); + + if (!vid_enc_device_p->virt_base) { + ERR("%s() : ioremap failed\n", __func__); + return -ENOMEM; + } + + vcd_init_config.p_device_name = "VID_C"; + vcd_init_config.pf_map_dev_base_addr = + vid_enc_map_dev_base_addr; + vcd_init_config.pf_interrupt_clr = + vid_enc_interrupt_clear; + vcd_init_config.pf_register_isr = + vid_enc_interrupt_register; + vcd_init_config.pf_deregister_isr = + vid_enc_interrupt_deregister; + + rc = vcd_init(&vcd_init_config, + &vid_enc_device_p->device_handle); + + if (rc) { + ERR("%s() : vcd_init failed\n", + __func__); + return -ENODEV; + } + return 0; +} + +static int __init vid_enc_init(void) +{ + int rc = 0; + struct device *class_devp; + + INFO("\n msm_vidc_enc: Inside %s()", __func__); + vid_enc_device_p = kzalloc(sizeof(struct vid_enc_dev), + GFP_KERNEL); + if (!vid_enc_device_p) { + ERR("%s Unable to allocate memory for vid_enc_dev\n", + __func__); + return -ENOMEM; + } + + rc = alloc_chrdev_region(&vid_enc_dev_num, 0, 1, VID_ENC_NAME); + if (rc < 0) { + ERR("%s: alloc_chrdev_region Failed rc = %d\n", + __func__, rc); + goto error_vid_enc_alloc_chrdev_region; + } + + vid_enc_class = class_create(THIS_MODULE, VID_ENC_NAME); + if (IS_ERR(vid_enc_class)) { + rc = PTR_ERR(vid_enc_class); + ERR("%s: couldn't create vid_enc_class rc = %d\n", + __func__, rc); + goto error_vid_enc_class_create; + } + + class_devp = device_create(vid_enc_class, NULL, + vid_enc_dev_num, NULL, VID_ENC_NAME); + + if (IS_ERR(class_devp)) { + rc = PTR_ERR(class_devp); + ERR("%s: class device_create failed %d\n", + __func__, rc); + goto error_vid_enc_class_device_create; + } + + vid_enc_device_p->device = class_devp; + + cdev_init(&vid_enc_device_p->cdev, &vid_enc_fops); + vid_enc_device_p->cdev.owner = THIS_MODULE; + rc = cdev_add(&(vid_enc_device_p->cdev), vid_enc_dev_num, 1); + + if (rc < 0) { + ERR("%s: cdev_add failed %d\n", + __func__, rc); + goto error_vid_enc_cdev_add; + } + vid_enc_vcd_init(); + return 0; + +error_vid_enc_cdev_add: + device_destroy(vid_enc_class, vid_enc_dev_num); +error_vid_enc_class_device_create: + class_destroy(vid_enc_class); +error_vid_enc_class_create: + unregister_chrdev_region(vid_enc_dev_num, 1); +error_vid_enc_alloc_chrdev_region: + kfree(vid_enc_device_p); + + return rc; +} + +static void __exit vid_enc_exit(void) +{ + INFO("\n msm_vidc_enc: Inside %s()", __func__); + cdev_del(&(vid_enc_device_p->cdev)); + device_destroy(vid_enc_class, vid_enc_dev_num); + class_destroy(vid_enc_class); + unregister_chrdev_region(vid_enc_dev_num, 1); + kfree(vid_enc_device_p); + INFO("\n msm_vidc_enc: Return from %s()", __func__); +} +static int vid_enc_ioctl(struct inode *inode, struct file *file, + unsigned cmd, unsigned long arg) +{ + struct video_client_ctx *client_ctx = NULL; + struct venc_ioctl_msg venc_msg; + struct venc_basecfg base_config; + struct venc_switch encoder_switch; + struct venc_profile profile; + struct ven_profilelevel profile_level; + struct venc_sessionqp session_qp; + struct venc_intraperiod intraperiod; + struct venc_seqheader seq_header; + struct venc_seqheader seq_header_user; + struct venc_entropycfg entropy_cfg; + struct venc_dbcfg dbcfg; + struct venc_intrarefresh intrarefresh; + struct venc_multiclicecfg multiclicecfg; + struct venc_ratectrlcfg ratectrlcfg; + struct venc_voptimingcfg voptimingcfg; + struct venc_framerate framerate; + struct venc_targetbitrate targetbitrate; + struct venc_headerextension headerextension; + struct venc_qprange qprange; + struct venc_bufferflush bufferflush; + struct venc_allocatorproperty allocatorproperty; + struct venc_bufferpayload buffer_info; + enum venc_buffer_dir buffer_dir; + struct venc_buffer enc_buffer; + struct venc_msg cb_msg; + u32 result = TRUE; + + DBG("%s\n", __func__); + + client_ctx = (struct video_client_ctx *)file->private_data; + if (!client_ctx) { + ERR("!client_ctx. Cannot attach to device handle\n"); + return -ENODEV; + } + + switch (cmd) { + case VEN_IOCTL_CMD_READ_NEXT_MSG: + if (copy_from_user(&venc_msg, (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + DBG("VEN_IOCTL_CMD_READ_NEXT_MSG\n"); + result = vid_enc_get_next_msg(client_ctx, &cb_msg); + if (!result) { + ERR("VEN_IOCTL_CMD_READ_NEXT_MSG failed\n"); + return -EIO; + } else + if (copy_to_user((void __user *) \ + venc_msg.outputparam, &cb_msg, + sizeof(cb_msg))) + return -EFAULT; + break; + + case VEN_IOCTL_CMD_STOP_READ_MSG: + DBG("VEN_IOCTL_CMD_STOP_READ_MSG\n"); + client_ctx->stop_msg = 1; + wake_up(&client_ctx->msg_wait); + break; + + case VEN_IOCTL_CMD_ENCODE_FRAME: + case VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER: + if (copy_from_user(&venc_msg, (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_CMD_ENCODE_FRAME" + "/VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER\n"); + + if (copy_from_user(&enc_buffer, + (void __user *)venc_msg.inputparam, sizeof(enc_buffer))) + return -EFAULT; + + if (cmd == VEN_IOCTL_CMD_ENCODE_FRAME) + result = vid_enc_encode_frame(client_ctx, + &enc_buffer); + else + result = vid_enc_fill_output_buffer(client_ctx, + &enc_buffer); + + if (!result) { + DBG(" \n VEN_IOCTL_CMD_ENCODE_FRAME/" + "VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER failed"); + return -EIO; + } + break; + + case VEN_IOCTL_SET_INPUT_BUFFER: + case VEN_IOCTL_SET_OUTPUT_BUFFER: + + if (copy_from_user(&venc_msg, + (void __user *)arg, sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_SET_INPUT_BUFFER/VEN_IOCTL_SET_OUTPUT_BUFFER\n"); + + if (copy_from_user(&buffer_info, + (void __user *)venc_msg.inputparam, + sizeof(buffer_info))) + return -EFAULT; + + buffer_dir = VEN_BUFFER_TYPE_INPUT; + if (cmd == VEN_IOCTL_SET_OUTPUT_BUFFER) + buffer_dir = VEN_BUFFER_TYPE_OUTPUT; + + result = vid_enc_set_buffer(client_ctx, &buffer_info, + buffer_dir); + if (!result) { + DBG("\n VEN_IOCTL_SET_INPUT_BUFFER" + "/VEN_IOCTL_SET_OUTPUT_BUFFER failed"); + return -EIO; + } + break; + case VEN_IOCTL_SET_INPUT_BUFFER_REQ: + case VEN_IOCTL_SET_OUTPUT_BUFFER_REQ: + if (copy_from_user(&venc_msg, + (void __user *)arg, sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_SET_INPUT_BUFFER_REQ" + "/VEN_IOCTL_SET_OUTPUT_BUFFER_REQ\n"); + + if (copy_from_user(&allocatorproperty, + (void __user *)venc_msg.inputparam, + sizeof(allocatorproperty))) + return -EFAULT; + + if (cmd == VEN_IOCTL_SET_OUTPUT_BUFFER_REQ) + result = vid_enc_set_buffer_req(client_ctx, + &allocatorproperty, FALSE); + else + result = vid_enc_set_buffer_req(client_ctx, + &allocatorproperty, TRUE); + if (!result) { + DBG("setting VEN_IOCTL_SET_OUTPUT_BUFFER_REQ/" + "VEN_IOCTL_SET_INPUT_BUFFER_REQ failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_GET_INPUT_BUFFER_REQ: + case VEN_IOCTL_GET_OUTPUT_BUFFER_REQ: + if (copy_from_user(&venc_msg, (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_GET_INPUT_BUFFER_REQ/" + "VEN_IOCTL_GET_OUTPUT_BUFFER_REQ \n"); + + if (cmd == VEN_IOCTL_GET_OUTPUT_BUFFER_REQ) + result = vid_enc_get_buffer_req(client_ctx, + &allocatorproperty, FALSE); + else + result = vid_enc_get_buffer_req(client_ctx, + &allocatorproperty, TRUE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &allocatorproperty, + sizeof(allocatorproperty))) + return -EFAULT; + } else + return -EIO; + break; + + case VEN_IOCTL_CMD_FLUSH: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_CMD_FLUSH\n"); + + if (copy_from_user(&bufferflush, + (void __user *)venc_msg.inputparam, + sizeof(bufferflush))) + return -EFAULT; + + INFO("\n %s(): Calling vid_enc_flush with mode = %lu", + __func__, bufferflush.flush_mode); + result = vid_enc_flush(client_ctx, &bufferflush); + + if (!result) { + ERR("setting VEN_IOCTL_CMD_FLUSH failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_CMD_START: + INFO("\n %s(): Executing VEN_IOCTL_CMD_START", __func__); + result = vid_enc_start_stop(client_ctx, TRUE); + if (!result) { + ERR("setting VEN_IOCTL_CMD_START failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_CMD_STOP: + INFO("\n %s(): Executing VEN_IOCTL_CMD_STOP", __func__); + result = vid_enc_start_stop(client_ctx, FALSE); + if (!result) { + ERR("setting VEN_IOCTL_CMD_STOP failed\n"); + return -EIO; + } + stop_cmd = 1; + break; + + case VEN_IOCTL_CMD_PAUSE: + INFO("\n %s(): Executing VEN_IOCTL_CMD_PAUSE", __func__); + result = vid_enc_pause_resume(client_ctx, TRUE); + if (!result) { + ERR("setting VEN_IOCTL_CMD_PAUSE failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_CMD_RESUME: + INFO("\n %s(): Executing VEN_IOCTL_CMD_RESUME", __func__); + result = vid_enc_pause_resume(client_ctx, FALSE); + if (!result) { + ERR("setting VEN_IOCTL_CMD_RESUME failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_SET_QP_RANGE: + + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_SET_QP_RANGE\n"); + + if (copy_from_user(&qprange, + (void __user *)venc_msg.inputparam, + sizeof(qprange))) + return -EFAULT; + + result = vid_enc_set_get_qprange(client_ctx, + &qprange, TRUE); + + if (!result) { + ERR("setting VEN_IOCTL_SET_QP_RANGE failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_GET_QP_RANGE: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_GET_QP_RANGE\n"); + result = vid_enc_set_get_qprange(client_ctx, + &qprange, FALSE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &qprange, + sizeof(qprange))) + return -EFAULT; + } else + return -EIO; + break; + + case VEN_IOCTL_SET_HEC: + if (copy_from_user(&venc_msg, (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_SET_HEC\n"); + + if (copy_from_user(&headerextension, + (void __user *)venc_msg.inputparam, + sizeof(headerextension))) + return -EFAULT; + + result = vid_enc_set_get_headerextension(client_ctx, + &headerextension, TRUE); + + if (!result) { + ERR("setting VEN_IOCTL_SET_HEC failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_GET_HEC: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_GET_HEC\n"); + result = vid_enc_set_get_headerextension(client_ctx, + &headerextension, FALSE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &headerextension, + sizeof(headerextension))) + return -EFAULT; + } else + return -EIO; + break; + + case VEN_IOCTL_SET_TARGET_BITRATE: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_SET_TARGET_BITRATE\n"); + + if (copy_from_user(&targetbitrate, + (void __user *)venc_msg.inputparam, + sizeof(targetbitrate))) + return -EFAULT; + + result = vid_enc_set_get_bitrate(client_ctx, + &targetbitrate, TRUE); + + if (!result) { + ERR("setting VEN_IOCTL_SET_TARGET_BITRATE failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_GET_TARGET_BITRATE: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_GET_TARGET_BITRATE\n"); + result = vid_enc_set_get_bitrate(client_ctx, + &targetbitrate, FALSE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &targetbitrate, + sizeof(targetbitrate))) + return -EFAULT; + } else + return -EIO; + break; + + case VEN_IOCTL_SET_FRAME_RATE: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_SET_FRAME_RATE\n"); + + if (copy_from_user(&framerate, + (void __user *)venc_msg.inputparam, + sizeof(framerate))) + return -EFAULT; + + result = vid_enc_set_get_framerate(client_ctx, + &framerate, TRUE); + + if (!result) { + ERR("setting VEN_IOCTL_SET_FRAME_RATE failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_GET_FRAME_RATE: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_GET_FRAME_RATE\n"); + result = vid_enc_set_get_framerate(client_ctx, &framerate, + FALSE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &framerate, + sizeof(framerate))) + return -EFAULT; + } else + return -EIO; + break; + + case VEN_IOCTL_SET_VOP_TIMING_CFG: + + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_SET_VOP_TIMING_CFG\n"); + + if (copy_from_user( + &voptimingcfg, (void __user *)venc_msg.inputparam, + sizeof(voptimingcfg))) + return -EFAULT; + + result = vid_enc_set_get_voptimingcfg(client_ctx, + &voptimingcfg, TRUE); + + if (!result) { + ERR("setting VEN_IOCTL_SET_VOP_TIMING_CFG failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_GET_VOP_TIMING_CFG: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_GET_VOP_TIMING_CFG\n"); + result = vid_enc_set_get_voptimingcfg(client_ctx, + &voptimingcfg, FALSE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &voptimingcfg, + sizeof(voptimingcfg))) + return -EFAULT; + } else + return -EIO; + break; + + case VEN_IOCTL_SET_RATE_CTRL_CFG: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_SET_RATE_CTRL_CFG\n"); + + if (copy_from_user(&ratectrlcfg, + (void __user *)venc_msg.inputparam, + sizeof(ratectrlcfg))) + return -EFAULT; + + result = vid_enc_set_get_ratectrlcfg(client_ctx, + &ratectrlcfg, TRUE); + + if (!result) { + ERR("setting VEN_IOCTL_SET_RATE_CTRL_CFG failed\n"); + return -EIO; + } + + break; + + case VEN_IOCTL_GET_RATE_CTRL_CFG: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_SET_RATE_CTRL_CFG\n"); + result = vid_enc_set_get_ratectrlcfg(client_ctx, + &ratectrlcfg, FALSE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &ratectrlcfg, + sizeof(ratectrlcfg))) + return -EFAULT; + } else + return -EIO; + break; + + case VEN_IOCTL_SET_MULTI_SLICE_CFG: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_SET_MULTI_SLICE_CFG\n"); + + if (copy_from_user(&multiclicecfg, + (void __user *)venc_msg.inputparam, + sizeof(multiclicecfg))) + return -EFAULT; + + result = vid_enc_set_get_multiclicecfg(client_ctx, + &multiclicecfg, TRUE); + + if (!result) { + ERR("setting VEN_IOCTL_SET_MULTI_SLICE_CFG failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_GET_MULTI_SLICE_CFG: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_GET_MULTI_SLICE_CFG\n"); + result = vid_enc_set_get_multiclicecfg(client_ctx, + &multiclicecfg, FALSE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &multiclicecfg, + sizeof(multiclicecfg))) + return -EFAULT; + } else + return -EIO; + break; + case VEN_IOCTL_SET_INTRA_REFRESH: + + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_SET_INTRA_REFRESH\n"); + + if (copy_from_user(&intrarefresh, + (void __user *)venc_msg.inputparam, + sizeof(intrarefresh))) + return -EFAULT; + + result = vid_enc_set_get_intrarefresh(client_ctx, + &intrarefresh, TRUE); + + if (!result) { + ERR("setting VEN_IOCTL_SET_INTRA_REFRESH failed\n"); + return -EIO; + } + + break; + + case VEN_IOCTL_GET_INTRA_REFRESH: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_GET_DEBLOCKING_CFG\n"); + result = vid_enc_set_get_intrarefresh(client_ctx, + &intrarefresh, FALSE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &intrarefresh, + sizeof(intrarefresh))) + return -EFAULT; + } else + return -EIO; + break; + + case VEN_IOCTL_SET_DEBLOCKING_CFG: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_SET_DEBLOCKING_CFG\n"); + + if (copy_from_user(&dbcfg, + (void __user *)venc_msg.inputparam, + sizeof(dbcfg))) + return -EFAULT; + + result = vid_enc_set_get_dbcfg(client_ctx, + &dbcfg, TRUE); + + if (!result) { + ERR("setting VEN_IOCTL_SET_DEBLOCKING_CFG failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_GET_DEBLOCKING_CFG: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_GET_DEBLOCKING_CFG\n"); + result = vid_enc_set_get_dbcfg(client_ctx, + &dbcfg, FALSE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &dbcfg, + sizeof(dbcfg))) + return -EFAULT; + } else + return -EIO; + + break; + case VEN_IOCTL_SET_ENTROPY_CFG: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_SET_ENTROPY_CFG\n"); + + if (copy_from_user(&entropy_cfg, + (void __user *)venc_msg.inputparam, + sizeof(entropy_cfg))) + return -EFAULT; + + result = vid_enc_set_get_entropy_cfg(client_ctx, + &entropy_cfg, TRUE); + + if (!result) { + ERR("setting VEN_IOCTL_SET_ENTROPY_CFG failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_GET_ENTROPY_CFG: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_GET_ENTROPY_CFG\n"); + + result = vid_enc_set_get_entropy_cfg(client_ctx, + &entropy_cfg, FALSE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &entropy_cfg, + sizeof(entropy_cfg))) + return -EFAULT; + } else + return -EIO; + + break; + + case VEN_IOCTL_GET_SEQUENCE_HDR: + + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_GET_SEQUENCE_HDR\n"); + + if (copy_from_user(&seq_header, + (void __user *)venc_msg.inputparam, + sizeof(seq_header))) + return -EFAULT; + + if (copy_from_user(&seq_header_user, + (void __user *)venc_msg.inputparam, + sizeof(seq_header_user))) + return -EFAULT; + + seq_header.hdrbufptr = NULL; + result = vid_enc_get_sequence_header(client_ctx, + &seq_header); + + if (result) { + if ((copy_to_user( + (void __user *)seq_header_user.hdrbufptr, + seq_header.hdrbufptr, seq_header.hdrlen)) || + (copy_to_user( + (void __user *)&seq_header_user.hdrlen, + &seq_header.hdrlen, + sizeof(seq_header.hdrlen))) + ) { + kfree(seq_header.hdrbufptr); + seq_header.hdrbufptr = NULL; + return -EFAULT; + } + } else { + kfree(seq_header.hdrbufptr); + seq_header.hdrbufptr = NULL; + return -EIO; + } + + kfree(seq_header.hdrbufptr); + seq_header.hdrbufptr = NULL; + + break; + + case VEN_IOCTL_GET_CAPABILITY: + return -EIO; + break; + case VEN_IOCTL_CMD_REQUEST_IFRAME: + result = vid_enc_request_iframe(client_ctx); + if (!result) { + ERR("setting VEN_IOCTL_CMD_REQUEST_IFRAME failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_SET_INTRA_PERIOD: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_SET_INTRA_PERIOD\n"); + + if (copy_from_user(&intraperiod, + (void __user *)venc_msg.inputparam, + sizeof(intraperiod))) + return -EFAULT; + + result = vid_enc_set_get_intraperiod(client_ctx, + &intraperiod, TRUE); + + if (!result) { + ERR("setting VEN_IOCTL_SET_INTRA_PERIOD failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_GET_INTRA_PERIOD: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_GET_SESSION_QP\n"); + + result = vid_enc_set_get_intraperiod(client_ctx, + &intraperiod, FALSE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &intraperiod, + sizeof(intraperiod))) + return -EFAULT; + } else + return -EIO; + break; + + case VEN_IOCTL_SET_SESSION_QP: + if (copy_from_user( + &venc_msg, (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_SET_SESSION_QP\n"); + + if (copy_from_user(&session_qp, + (void __user *)venc_msg.inputparam, + sizeof(session_qp))) + return -EFAULT; + + result = vid_enc_set_get_session_qp(client_ctx, + &session_qp, TRUE); + + if (!result) { + ERR("setting VEN_IOCTL_SET_SESSION_QP failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_GET_SESSION_QP: + if (copy_from_user( + &venc_msg, (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_GET_SESSION_QP\n"); + + result = vid_enc_set_get_session_qp(client_ctx, + &session_qp, FALSE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &session_qp, + sizeof(session_qp))) + return -EFAULT; + } else + return -EIO; + break; + + case VEN_IOCTL_SET_PROFILE_LEVEL: + + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_SET_PROFILE_LEVEL\n"); + + if (copy_from_user(&profile_level, + (void __user *)venc_msg.inputparam, + sizeof(profile_level))) + return -EFAULT; + + result = vid_enc_set_get_profile_level(client_ctx, + &profile_level, TRUE); + + if (!result) { + ERR("setting VEN_IOCTL_SET_PROFILE_LEVEL failed\n"); + return -EIO; + } + + break; + case VEN_IOCTL_GET_PROFILE_LEVEL: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_GET_CODEC_PROFILE\n"); + + result = vid_enc_set_get_profile_level(client_ctx, + &profile_level, FALSE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &profile_level, + sizeof(profile_level))) + return -EFAULT; + } else + return -EIO; + break; + + case VEN_IOCTL_SET_CODEC_PROFILE: + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_SET_CODEC_PROFILE\n"); + + if (copy_from_user(&profile, + (void __user *)venc_msg.inputparam, + sizeof(profile))) + return -EFAULT; + + result = vid_enc_set_get_profile(client_ctx, + &profile, TRUE); + + if (!result) { + ERR("setting VEN_IOCTL_SET_CODEC_PROFILE failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_GET_CODEC_PROFILE: + if (copy_from_user( + &venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_GET_CODEC_PROFILE\n"); + + result = vid_enc_set_get_profile(client_ctx, + &profile, FALSE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &profile, + sizeof(profile))) + return -EFAULT; + } else + return -EIO; + break; + + + case VEN_IOCTL_SET_SHORT_HDR: + if (copy_from_user(&venc_msg, + (void __user *)arg, sizeof(venc_msg))) + return -EFAULT; + + DBG("Getting VEN_IOCTL_SET_SHORT_HDR\n"); + + if (copy_from_user( + &encoder_switch, + (void __user *)venc_msg.inputparam, + sizeof(encoder_switch))) + return -EFAULT; + + result = vid_enc_set_get_short_header(client_ctx, + &encoder_switch, TRUE); + + if (!result) { + ERR("setting VEN_IOCTL_SET_SHORT_HDR failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_GET_SHORT_HDR: + if (copy_from_user(&venc_msg, + (void __user *)arg, sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_GET_LIVE_MODE\n"); + + result = vid_enc_set_get_short_header(client_ctx, + &encoder_switch, FALSE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &encoder_switch, + sizeof(encoder_switch))) + return -EFAULT; + } else + return -EIO; + + break; + + case VEN_IOCTL_SET_BASE_CFG: + + DBG("VEN_IOCTL_SET_BASE_CFG\n"); + + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + if (copy_from_user(&base_config, + (void __user *)venc_msg.inputparam, + sizeof(base_config))) + return -EFAULT; + + DBG("setting VEN_IOCTL_SET_BASE_CFG\n"); + + result = vid_enc_set_get_base_cfg(client_ctx, + &base_config, TRUE); + + if (!result) { + ERR("setting VEN_IOCTL_SET_BASE_CFG failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_GET_BASE_CFG: + DBG("VEN_IOCTL_GET_BASE_CFG\n"); + + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("Getting VEN_IOCTL_SET_BASE_CFG\n"); + + result = vid_enc_set_get_base_cfg(client_ctx, + &base_config, FALSE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &base_config, + sizeof(base_config))) + return -EFAULT; + } else + return -EIO; + + break; + + case VEN_IOCTL_SET_LIVE_MODE: + + if (copy_from_user(&venc_msg, + (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("Getting VEN_IOCTL_SET_LIVE_MODE\n"); + + if (copy_from_user(&encoder_switch, + (void __user *)venc_msg.inputparam, + sizeof(encoder_switch))) + return -EFAULT; + + result = vid_enc_set_get_live_mode(client_ctx, + &encoder_switch, TRUE); + + if (!result) { + ERR("setting VEN_IOCTL_SET_LIVE_MODE failed\n"); + return -EIO; + } + break; + + case VEN_IOCTL_GET_LIVE_MODE: + + if (copy_from_user( + &venc_msg, (void __user *)arg, + sizeof(venc_msg))) + return -EFAULT; + + DBG("VEN_IOCTL_GET_LIVE_MODE\n"); + + result = vid_enc_set_get_live_mode(client_ctx, + &encoder_switch, FALSE); + + if (result) { + if (copy_to_user( + (void __user *)venc_msg.outputparam, + &encoder_switch, + sizeof(encoder_switch))) + return -EFAULT; + } else + return -EIO; + + break; + case VEN_IOCTL_SET_AC_PREDICTION: + case VEN_IOCTL_GET_AC_PREDICTION: + case VEN_IOCTL_SET_RVLC: + case VEN_IOCTL_GET_RVLC: + case VEN_IOCTL_SET_ROTATION: + case VEN_IOCTL_GET_ROTATION: + case VEN_IOCTL_SET_DATA_PARTITION: + case VEN_IOCTL_GET_DATA_PARTITION: + default: + ERR("%s(): Unsupported ioctl %d\n", __func__, cmd); + return -ENOTTY; + + break; + } + return 0; +} + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Video encoder driver"); +MODULE_VERSION("1.0"); + +module_init(vid_enc_init); +module_exit(vid_enc_exit); diff --git a/drivers/misc/video_core/720p/enc/venc_internal.c b/drivers/misc/video_core/720p/enc/venc_internal.c new file mode 100644 index 0000000000000..2b84441ee1ee0 --- /dev/null +++ b/drivers/misc/video_core/720p/enc/venc_internal.c @@ -0,0 +1,1716 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "video_core_type.h" +#include "vcd_api.h" +#include "venc_internal.h" +#include "video_core_init.h" + +#if DEBUG +#define DBG(x...) printk(KERN_DEBUG x) +#else +#define DBG(x...) +#endif + +#define ERR(x...) printk(KERN_ERR x) + +u32 vid_enc_set_get_base_cfg(struct video_client_ctx *client_ctx, + struct venc_basecfg *base_config, u32 set_flag) +{ + struct venc_targetbitrate venc_bitrate; + struct venc_framerate frame_rate; + u32 current_codec_type; + + if (!client_ctx || !base_config) + return FALSE; + + if (!vid_enc_set_get_codec(client_ctx, ¤t_codec_type, FALSE)) + return FALSE; + + DBG("%s(): Current Codec Type = %u\n", __func__, current_codec_type); + if (current_codec_type != base_config->codectype) { + if (!vid_enc_set_get_codec(client_ctx, + (u32 *)&base_config->codectype, set_flag)) + return FALSE; + } + + if (!vid_enc_set_get_inputformat(client_ctx, + (u32 *)&base_config->inputformat, set_flag)) + return FALSE; + + if (!vid_enc_set_get_framesize(client_ctx, + (u32 *)&base_config->input_height, + (u32 *)&base_config->input_width, set_flag)) + return FALSE; + + if (set_flag) + venc_bitrate.target_bitrate = base_config->targetbitrate; + + if (!vid_enc_set_get_bitrate(client_ctx, &venc_bitrate, set_flag)) + return FALSE; + + if (!set_flag) + base_config->targetbitrate = venc_bitrate.target_bitrate; + + if (set_flag) { + frame_rate.fps_denominator = base_config->fps_den; + frame_rate.fps_numerator = base_config->fps_num; + } + + if (!vid_enc_set_get_framerate(client_ctx, &frame_rate, set_flag)) + return FALSE; + + if (!set_flag) { + base_config->fps_den = frame_rate.fps_denominator; + base_config->fps_num = frame_rate.fps_numerator; + } + + return TRUE; +} + +u32 vid_enc_set_get_inputformat(struct video_client_ctx *client_ctx, + u32 *input_format, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_buffer_format_type format_type; + u32 vcd_status = VCD_ERR_FAIL; + u32 status = TRUE; + + if (!client_ctx || !input_format) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_BUFFER_FORMAT; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_buffer_format_type); + + if (set_flag) { + switch (*input_format) { + case VEN_INPUTFMT_NV12: + format_type.e_buffer_format = VCD_BUFFER_FORMAT_NV12; + break; + case VEN_INPUTFMT_NV21: + format_type.e_buffer_format = + VCD_BUFFER_FORMAT_TILE_4x2; + break; + default: + status = FALSE; + break; + } + + if (status) { + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &format_type); + if (vcd_status) { + status = FALSE; + ERR("%s(): Set VCD_I_BUFFER_FORMAT Failed\n", + __func__); + } + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &format_type); + + if (vcd_status) { + status = FALSE; + ERR("%s(): Get VCD_I_BUFFER_FORMAT Failed\n", __func__); + } else { + switch (format_type.e_buffer_format) { + case VCD_BUFFER_FORMAT_NV12: + *input_format = VEN_INPUTFMT_NV12; + break; + case VCD_BUFFER_FORMAT_TILE_4x2: + *input_format = VEN_INPUTFMT_NV21; + break; + default: + status = FALSE; + break; + } + } + } + return status; +} + +u32 vid_enc_set_get_codec(struct video_client_ctx *client_ctx, u32 *codec_type, + u32 set_flag) +{ + struct vcd_property_codec_type vcd_property_codec; + struct vcd_property_hdr_type vcd_property_hdr; + u32 vcd_status = VCD_ERR_FAIL; + u32 status = TRUE; + + if (!client_ctx || !codec_type) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_CODEC; + vcd_property_hdr.n_size = sizeof(struct vcd_property_codec_type); + + if (set_flag) { + switch (*codec_type) { + case VEN_CODEC_MPEG4: + vcd_property_codec.e_codec = VCD_CODEC_MPEG4; + break; + case VEN_CODEC_H263: + vcd_property_codec.e_codec = VCD_CODEC_H263; + break; + case VEN_CODEC_H264: + vcd_property_codec.e_codec = VCD_CODEC_H264; + break; + default: + status = FALSE; + break; + } + + if (status) { + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &vcd_property_codec); + if (vcd_status) { + status = FALSE; + ERR("%s(): Set VCD_I_CODEC Failed\n", __func__); + } + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &vcd_property_codec); + + if (vcd_status) { + status = FALSE; + ERR("%s(): Get VCD_I_CODEC Failed\n", + __func__); + } else { + switch (vcd_property_codec.e_codec) { + case VCD_CODEC_H263: + *codec_type = VEN_CODEC_H263; + break; + case VCD_CODEC_H264: + *codec_type = VEN_CODEC_H264; + break; + case VCD_CODEC_MPEG4: + *codec_type = VEN_CODEC_MPEG4; + break; + case VCD_CODEC_DIVX_3: + case VCD_CODEC_DIVX_4: + case VCD_CODEC_DIVX_5: + case VCD_CODEC_DIVX_6: + case VCD_CODEC_MPEG1: + case VCD_CODEC_MPEG2: + case VCD_CODEC_VC1: + case VCD_CODEC_VC1_RCV: + case VCD_CODEC_XVID: + default: + status = FALSE; + break; + } + } + } + return status; +} + +u32 vid_enc_set_get_framesize(struct video_client_ctx *client_ctx, + u32 *height, u32 *width, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_frame_size_type frame_size; + u32 vcd_status = VCD_ERR_FAIL; + + if (!client_ctx || !height || !width) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_FRAME_SIZE; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_frame_size_type); + + if (set_flag) { + frame_size.n_height = *height; + frame_size.n_width = *width; + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &frame_size); + + if (vcd_status) { + ERR("%s(): Set VCD_I_FRAME_SIZE Failed\n", + __func__); + return FALSE; + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &frame_size); + + if (vcd_status) { + ERR("%s(): Get VCD_I_FRAME_SIZE Failed\n", + __func__); + return FALSE; + } + *height = frame_size.n_height; + *width = frame_size.n_width; + } + return TRUE; +} + +u32 vid_enc_set_get_bitrate(struct video_client_ctx *client_ctx, + struct venc_targetbitrate *venc_bitrate, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_target_bitrate_type bit_rate; + u32 vcd_status = VCD_ERR_FAIL; + + if (!client_ctx || !venc_bitrate) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_TARGET_BITRATE; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_target_bitrate_type); + if (set_flag) { + bit_rate.n_target_bitrate = venc_bitrate->target_bitrate; + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &bit_rate); + + if (vcd_status) { + ERR("%s(): Set VCD_I_TARGET_BITRATE Failed\n", + __func__); + return FALSE; + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &bit_rate); + + if (vcd_status) { + ERR("%s(): Get VCD_I_TARGET_BITRATE Failed\n", + __func__); + return FALSE; + } + venc_bitrate->target_bitrate = bit_rate.n_target_bitrate; + } + return TRUE; +} + +u32 vid_enc_set_get_framerate(struct video_client_ctx *client_ctx, + struct venc_framerate *frame_rate, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_frame_rate_type vcd_frame_rate; + u32 vcd_status = VCD_ERR_FAIL; + + if (!client_ctx || !frame_rate) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_FRAME_RATE; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_frame_rate_type); + + if (set_flag) { + vcd_frame_rate.n_fps_denominator = frame_rate->fps_denominator; + vcd_frame_rate.n_fps_numerator = frame_rate->fps_numerator; + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &vcd_frame_rate); + + if (vcd_status) { + ERR("%s(): Set VCD_I_FRAME_RATE Failed\n", + __func__); + return FALSE; + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &vcd_frame_rate); + + if (vcd_status) { + ERR("%s(): Get VCD_I_FRAME_RATE Failed\n", + __func__); + return FALSE; + } + frame_rate->fps_denominator = vcd_frame_rate.n_fps_denominator; + frame_rate->fps_numerator = vcd_frame_rate.n_fps_numerator; + } + return TRUE; +} + +u32 vid_enc_set_get_live_mode(struct video_client_ctx *client_ctx, + struct venc_switch *encoder_switch, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_live_type live_mode; + u32 vcd_status = VCD_ERR_FAIL; + + if (!client_ctx) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_LIVE; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_live_type); + + if (set_flag) { + live_mode.b_live = 1; + if (!encoder_switch->status) + live_mode.b_live = 0; + + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &live_mode); + if (vcd_status) { + ERR("%s(): Set VCD_I_LIVE Failed\n", + __func__); + return FALSE; + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &live_mode); + + if (vcd_status) { + ERR("%s(): Get VCD_I_LIVE Failed\n", + __func__); + return FALSE; + } else { + encoder_switch->status = 1; + if (!live_mode.b_live) + encoder_switch->status = 0; + } + } + return TRUE; +} + +u32 vid_enc_set_get_short_header(struct video_client_ctx *client_ctx, + struct venc_switch *encoder_switch, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_short_header_type short_header; + u32 vcd_status = VCD_ERR_FAIL; + + if (!client_ctx || !encoder_switch) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_SHORT_HEADER; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_short_header_type); + + if (set_flag) { + short_header.b_short_header = (u32) encoder_switch->status; + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &short_header); + + if (vcd_status) { + ERR("%s(): Set VCD_I_SHORT_HEADER Failed\n", + __func__); + return FALSE; + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &short_header); + + if (vcd_status) { + ERR("%s(): Get VCD_I_SHORT_HEADER Failed\n", + __func__); + return FALSE; + } else { + encoder_switch->status = + (u8) short_header.b_short_header; + } + } + return TRUE; +} + +u32 vid_enc_set_get_profile(struct video_client_ctx *client_ctx, + struct venc_profile *profile, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_profile_type profile_type; + u32 vcd_status = VCD_ERR_FAIL; + u32 status = TRUE; + + if (!client_ctx || !profile) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_PROFILE; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_profile_type); + + if (set_flag) { + switch (profile->profile) { + case VEN_PROFILE_MPEG4_SP: + profile_type.e_profile = VCD_PROFILE_MPEG4_SP; + break; + case VEN_PROFILE_MPEG4_ASP: + profile_type.e_profile = VCD_PROFILE_MPEG4_ASP; + break; + case VEN_PROFILE_H264_BASELINE: + profile_type.e_profile = VCD_PROFILE_H264_BASELINE; + break; + case VEN_PROFILE_H264_MAIN: + profile_type.e_profile = VCD_PROFILE_H264_MAIN; + break; + case VEN_PROFILE_H264_HIGH: + profile_type.e_profile = VCD_PROFILE_H264_HIGH; + break; + case VEN_PROFILE_H263_BASELINE: + profile_type.e_profile = VCD_PROFILE_H263_BASELINE; + break; + default: + status = FALSE; + break; + } + + if (status) { + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &profile_type); + + if (vcd_status) { + ERR("%s(): Set VCD_I_PROFILE Failed\n", + __func__); + return FALSE; + } + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &profile_type); + + if (vcd_status) { + ERR("%s(): Get VCD_I_PROFILE Failed\n", + __func__); + return FALSE; + } else { + switch (profile_type.e_profile) { + case VCD_PROFILE_H263_BASELINE: + profile->profile = VEN_PROFILE_H263_BASELINE; + break; + case VCD_PROFILE_H264_BASELINE: + profile->profile = VEN_PROFILE_H264_BASELINE; + break; + case VCD_PROFILE_H264_HIGH: + profile->profile = VEN_PROFILE_H264_HIGH; + break; + case VCD_PROFILE_H264_MAIN: + profile->profile = VEN_PROFILE_H264_MAIN; + break; + case VCD_PROFILE_MPEG4_ASP: + profile->profile = VEN_PROFILE_MPEG4_ASP; + break; + case VCD_PROFILE_MPEG4_SP: + profile->profile = VEN_PROFILE_MPEG4_SP; + break; + default: + status = FALSE; + break; + } + } + } + return status; +} + +u32 vid_enc_set_get_profile_level(struct video_client_ctx *client_ctx, + struct ven_profilelevel *profile_level, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_level_type level_type; + u32 vcd_status = VCD_ERR_FAIL; + u32 status = TRUE; + + if (!client_ctx || !profile_level) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_LEVEL; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_level_type); + + if (set_flag) { + switch (profile_level->level) { + case VEN_LEVEL_MPEG4_0: + level_type.e_level = VCD_LEVEL_MPEG4_0; + break; + case VEN_LEVEL_MPEG4_1: + level_type.e_level = VCD_LEVEL_MPEG4_1; + break; + case VEN_LEVEL_MPEG4_2: + level_type.e_level = VCD_LEVEL_MPEG4_2; + break; + case VEN_LEVEL_MPEG4_3: + level_type.e_level = VCD_LEVEL_MPEG4_3; + break; + case VEN_LEVEL_MPEG4_4: + level_type.e_level = VCD_LEVEL_MPEG4_4; + break; + case VEN_LEVEL_MPEG4_5: + level_type.e_level = VCD_LEVEL_MPEG4_5; + break; + case VEN_LEVEL_MPEG4_3b: + level_type.e_level = VCD_LEVEL_MPEG4_3b; + break; + case VEN_LEVEL_MPEG4_6: + level_type.e_level = VCD_LEVEL_MPEG4_6; + break; + case VEN_LEVEL_H264_1: + level_type.e_level = VCD_LEVEL_H264_1; + break; + case VEN_LEVEL_H264_1b: + level_type.e_level = VCD_LEVEL_H264_1b; + break; + case VEN_LEVEL_H264_1p1: + level_type.e_level = VCD_LEVEL_H264_1p1; + break; + case VEN_LEVEL_H264_1p2: + level_type.e_level = VCD_LEVEL_H264_1p2; + break; + case VEN_LEVEL_H264_1p3: + level_type.e_level = VCD_LEVEL_H264_1p3; + break; + case VEN_LEVEL_H264_2: + level_type.e_level = VCD_LEVEL_H264_2; + break; + case VEN_LEVEL_H264_2p1: + level_type.e_level = VCD_LEVEL_H264_2p1; + break; + case VEN_LEVEL_H264_2p2: + level_type.e_level = VCD_LEVEL_H264_2p2; + break; + case VEN_LEVEL_H264_3: + level_type.e_level = VCD_LEVEL_H264_3; + break; + case VEN_LEVEL_H264_3p1: + level_type.e_level = VCD_LEVEL_H264_3p1; + break; + + case VEN_LEVEL_H263_10: + level_type.e_level = VCD_LEVEL_H263_10; + break; + case VEN_LEVEL_H263_20: + level_type.e_level = VCD_LEVEL_H263_20; + break; + case VEN_LEVEL_H263_30: + level_type.e_level = VCD_LEVEL_H263_30; + break; + case VEN_LEVEL_H263_40: + level_type.e_level = VCD_LEVEL_H263_40; + break; + case VEN_LEVEL_H263_45: + level_type.e_level = VCD_LEVEL_H263_45; + break; + case VEN_LEVEL_H263_50: + level_type.e_level = VCD_LEVEL_H263_50; + break; + case VEN_LEVEL_H263_60: + level_type.e_level = VCD_LEVEL_H263_60; + break; + case VEN_LEVEL_H263_70: + level_type.e_level = VCD_LEVEL_H263_70; + break; + default: + status = FALSE; + break; + } + if (status) { + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &level_type); + + if (vcd_status) { + ERR("%s(): Set VCD_I_LEVEL Failed\n", + __func__); + return FALSE; + } + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &level_type); + + if (vcd_status) { + ERR("%s(): Get VCD_I_LEVEL Failed\n", + __func__); + return FALSE; + } else { + switch (level_type.e_level) { + case VCD_LEVEL_MPEG4_0: + profile_level->level = VEN_LEVEL_MPEG4_0; + break; + case VCD_LEVEL_MPEG4_1: + profile_level->level = VEN_LEVEL_MPEG4_1; + break; + case VCD_LEVEL_MPEG4_2: + profile_level->level = VEN_LEVEL_MPEG4_2; + break; + case VCD_LEVEL_MPEG4_3: + profile_level->level = VEN_LEVEL_MPEG4_3; + break; + case VCD_LEVEL_MPEG4_4: + profile_level->level = VEN_LEVEL_MPEG4_4; + break; + case VCD_LEVEL_MPEG4_5: + profile_level->level = VEN_LEVEL_MPEG4_5; + break; + case VCD_LEVEL_MPEG4_3b: + profile_level->level = VEN_LEVEL_MPEG4_3b; + break; + case VCD_LEVEL_H264_1: + profile_level->level = VEN_LEVEL_H264_1; + break; + case VCD_LEVEL_H264_1b: + profile_level->level = VEN_LEVEL_H264_1b; + break; + case VCD_LEVEL_H264_1p1: + profile_level->level = VEN_LEVEL_H264_1p1; + break; + case VCD_LEVEL_H264_1p2: + profile_level->level = VEN_LEVEL_H264_1p2; + break; + case VCD_LEVEL_H264_1p3: + profile_level->level = VEN_LEVEL_H264_1p3; + break; + case VCD_LEVEL_H264_2: + profile_level->level = VEN_LEVEL_H264_2; + break; + case VCD_LEVEL_H264_2p1: + profile_level->level = VEN_LEVEL_H264_2p1; + break; + case VCD_LEVEL_H264_2p2: + profile_level->level = VEN_LEVEL_H264_2p2; + break; + case VCD_LEVEL_H264_3: + profile_level->level = VEN_LEVEL_H264_3; + break; + case VCD_LEVEL_H264_3p1: + profile_level->level = VEN_LEVEL_H264_3p1; + break; + case VCD_LEVEL_H264_3p2: + status = FALSE; + break; + case VCD_LEVEL_H264_4: + status = FALSE; + break; + case VCD_LEVEL_H263_10: + profile_level->level = VEN_LEVEL_H263_10; + break; + case VCD_LEVEL_H263_20: + profile_level->level = VEN_LEVEL_H263_20; + break; + case VCD_LEVEL_H263_30: + profile_level->level = VEN_LEVEL_H263_30; + break; + case VCD_LEVEL_H263_40: + profile_level->level = VEN_LEVEL_H263_40; + break; + case VCD_LEVEL_H263_45: + profile_level->level = VEN_LEVEL_H263_45; + break; + case VCD_LEVEL_H263_50: + profile_level->level = VEN_LEVEL_H263_50; + break; + case VCD_LEVEL_H263_60: + profile_level->level = VEN_LEVEL_H263_60; + break; + case VCD_LEVEL_H263_70: + status = FALSE; + break; + default: + status = FALSE; + break; + } + } + } + return status; +} + +u32 vid_enc_set_get_session_qp(struct video_client_ctx *client_ctx, + struct venc_sessionqp *session_qp, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_session_qp_type qp_type; + u32 vcd_status = VCD_ERR_FAIL; + + if (!client_ctx || !session_qp) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_SESSION_QP; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_session_qp_type); + + if (set_flag) { + qp_type.n_i_frame_qp = session_qp->iframeqp; + qp_type.n_p_frame_qp = session_qp->pframqp; + + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &qp_type); + + if (vcd_status) { + ERR("%s(): Set VCD_I_SESSION_QP Failed\n", + __func__); + return FALSE; + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &qp_type); + + if (vcd_status) { + ERR("%s(): Set VCD_I_SESSION_QP Failed\n", + __func__); + return FALSE; + } else { + session_qp->iframeqp = qp_type.n_i_frame_qp; + session_qp->pframqp = qp_type.n_p_frame_qp; + } + } + return TRUE; +} + +u32 vid_enc_set_get_intraperiod(struct video_client_ctx *client_ctx, + struct venc_intraperiod *intraperiod, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_i_period_type period_type; + u32 vcd_status = VCD_ERR_FAIL; + + if (!client_ctx || !intraperiod) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_INTRA_PERIOD; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_i_period_type); + + if (set_flag) { + period_type.n_p_frames = intraperiod->num_pframes; + period_type.n_b_frames = 0; + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &period_type); + + if (vcd_status) { + ERR("%s(): Set VCD_I_INTRA_PERIOD Failed\n", + __func__); + return FALSE; + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &period_type); + + if (vcd_status) { + ERR("%s(): Get VCD_I_INTRA_PERIOD Failed\n", + __func__); + return FALSE; + } else + intraperiod->num_pframes = period_type.n_p_frames; + } + return TRUE; +} + +u32 vid_enc_request_iframe(struct video_client_ctx *client_ctx) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_req_i_frame_type request; + u32 vcd_status = VCD_ERR_FAIL; + u32 status = TRUE; + + if (!client_ctx) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_REQ_IFRAME; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_req_i_frame_type); + request.b_req_i_frame = 1; + + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &request); + + if (vcd_status) { + ERR("%s(): Set VCD_I_REQ_IFRAME Failed\n", + __func__); + return FALSE; + } + return status; +} + +u32 vid_enc_get_sequence_header(struct video_client_ctx *client_ctx, + struct venc_seqheader *seq_header) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_sequence_hdr_type hdr_type; + u32 vcd_status = VCD_ERR_FAIL; + u32 status = TRUE; + + if (!client_ctx || + !seq_header || !seq_header->bufsize) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_SEQ_HEADER; + vcd_property_hdr.n_size = + sizeof(struct vcd_sequence_hdr_type); + + hdr_type.p_sequence_header = + kzalloc(seq_header->bufsize, GFP_KERNEL); + seq_header->hdrbufptr = hdr_type.p_sequence_header; + + if (!hdr_type.p_sequence_header) + return FALSE; + hdr_type.n_sequence_header_len = seq_header->bufsize; + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &hdr_type); + + if (vcd_status) { + ERR("%s(): Get VCD_I_SEQ_HEADER Failed\n", + __func__); + status = FALSE; + } + return TRUE; +} + +u32 vid_enc_set_get_entropy_cfg(struct video_client_ctx *client_ctx, + struct venc_entropycfg *entropy_cfg, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_entropy_control_type control_type; + u32 vcd_status = VCD_ERR_FAIL; + u32 status = TRUE; + + if (!client_ctx || !entropy_cfg) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_ENTROPY_CTRL; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_entropy_control_type); + + if (set_flag) { + switch (entropy_cfg->cabacmodel) { + case VEN_ENTROPY_MODEL_CAVLC: + control_type.e_entropy_sel = VCD_ENTROPY_SEL_CAVLC; + break; + case VEN_ENTROPY_MODEL_CABAC: + control_type.e_entropy_sel = VCD_ENTROPY_SEL_CABAC; + break; + default: + status = FALSE; + break; + } + + if (status && entropy_cfg->cabacmodel == + VCD_ENTROPY_SEL_CABAC) { + switch (entropy_cfg->cabacmodel) { + case VEN_CABAC_MODEL_0: + control_type.e_cabac_model = + VCD_CABAC_MODEL_NUMBER_0; + break; + case VEN_CABAC_MODEL_1: + control_type.e_cabac_model = + VCD_CABAC_MODEL_NUMBER_1; + break; + case VEN_CABAC_MODEL_2: + control_type.e_cabac_model = + VCD_CABAC_MODEL_NUMBER_2; + break; + default: + status = FALSE; + break; + } + } + if (status) { + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &control_type); + + if (vcd_status) { + ERR("%s(): Set VCD_I_ENTROPY_CTRL Failed\n", + __func__); + status = FALSE; + } + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &control_type); + + if (vcd_status) { + ERR("%s(): Get VCD_I_ENTROPY_CTRL Failed\n", + __func__); + status = FALSE; + } else { + switch (control_type.e_entropy_sel) { + case VCD_ENTROPY_SEL_CABAC: + entropy_cfg->cabacmodel = + VEN_ENTROPY_MODEL_CABAC; + break; + case VCD_ENTROPY_SEL_CAVLC: + entropy_cfg->cabacmodel = + VEN_ENTROPY_MODEL_CAVLC; + break; + default: + status = FALSE; + break; + } + + if (status && control_type.e_entropy_sel == + VCD_ENTROPY_SEL_CABAC) { + switch (control_type.e_cabac_model) { + case VCD_CABAC_MODEL_NUMBER_0: + entropy_cfg->cabacmodel = + VEN_CABAC_MODEL_0; + break; + case VCD_CABAC_MODEL_NUMBER_1: + entropy_cfg->cabacmodel = + VEN_CABAC_MODEL_1; + break; + case VCD_CABAC_MODEL_NUMBER_2: + entropy_cfg->cabacmodel = + VEN_CABAC_MODEL_2; + break; + default: + status = FALSE; + break; + } + } + } + } + return status; +} + +u32 vid_enc_set_get_dbcfg(struct video_client_ctx *client_ctx, + struct venc_dbcfg *dbcfg, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_db_config_type control_type; + u32 vcd_status = VCD_ERR_FAIL; + u32 status = TRUE; + + if (!client_ctx || !dbcfg) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_DEBLOCKING; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_db_config_type); + + if (set_flag) { + switch (dbcfg->db_mode) { + case VEN_DB_DISABLE: + control_type.e_db_config = VCD_DB_DISABLE; + break; + case VEN_DB_ALL_BLKG_BNDRY: + control_type.e_db_config = VCD_DB_ALL_BLOCKING_BOUNDARY; + break; + case VEN_DB_SKIP_SLICE_BNDRY: + control_type.e_db_config = VCD_DB_SKIP_SLICE_BOUNDARY; + break; + default: + status = FALSE; + break; + } + + if (status) { + control_type.n_slice_alpha_offset = + dbcfg->slicealpha_offset; + control_type.n_slice_beta_offset = + dbcfg->slicebeta_offset; + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &control_type); + if (vcd_status) { + ERR("%s(): Set VCD_I_DEBLOCKING Failed\n", + __func__); + status = FALSE; + } + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &control_type); + if (vcd_status) { + ERR("%s(): Get VCD_I_DEBLOCKING Failed\n", + __func__); + status = FALSE; + } else { + switch (control_type.e_db_config) { + case VCD_DB_ALL_BLOCKING_BOUNDARY: + dbcfg->db_mode = VEN_DB_ALL_BLKG_BNDRY; + break; + case VCD_DB_DISABLE: + dbcfg->db_mode = VEN_DB_DISABLE; + break; + case VCD_DB_SKIP_SLICE_BOUNDARY: + dbcfg->db_mode = VEN_DB_SKIP_SLICE_BNDRY; + break; + default: + status = FALSE; + break; + } + dbcfg->slicealpha_offset = + control_type.n_slice_alpha_offset; + dbcfg->slicebeta_offset = + control_type.n_slice_beta_offset; + } + } + return status; +} + +u32 vid_enc_set_get_intrarefresh(struct video_client_ctx *client_ctx, + struct venc_intrarefresh *intrarefresh, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_intra_refresh_mb_number_type control_type; + u32 vcd_status = VCD_ERR_FAIL; + + if (!client_ctx || !intrarefresh) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_INTRA_REFRESH; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_intra_refresh_mb_number_type); + + if (set_flag) { + control_type.n_cir_mb_number = intrarefresh->mbcount; + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &control_type); + + if (vcd_status) { + ERR("%s(): Set VCD_I_INTRA_REFRESH Failed\n", + __func__); + return FALSE; + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &control_type); + + if (vcd_status) { + ERR("%s(): Set VCD_I_INTRA_REFRESH Failed\n", + __func__); + return FALSE; + } else + intrarefresh->mbcount = control_type.n_cir_mb_number; + } + return TRUE; +} + +u32 vid_enc_set_get_multiclicecfg(struct video_client_ctx *client_ctx, + struct venc_multiclicecfg *multiclicecfg, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_multi_slice_type control_type; + u32 vcd_status = VCD_ERR_FAIL; + u32 status = TRUE; + + if (!client_ctx || !multiclicecfg) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_MULTI_SLICE; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_multi_slice_type); + + if (set_flag) { + switch (multiclicecfg->mslice_mode) { + case VEN_MSLICE_OFF: + control_type.e_m_slice_sel = + VCD_MSLICE_OFF; + break; + case VEN_MSLICE_CNT_MB: + control_type.e_m_slice_sel = + VCD_MSLICE_BY_MB_COUNT; + break; + case VEN_MSLICE_CNT_BYTE: + control_type.e_m_slice_sel = + VCD_MSLICE_BY_BYTE_COUNT; + break; + case VEN_MSLICE_GOB: + control_type.e_m_slice_sel = + VCD_MSLICE_BY_GOB; + break; + default: + status = FALSE; + break; + } + + if (status) { + control_type.n_m_slice_size = + multiclicecfg->mslice_size; + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &control_type); + + if (vcd_status) { + ERR("%s(): Set VCD_I_MULTI_SLICE Failed\n", + __func__); + status = FALSE; + } + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &control_type); + + if (vcd_status) { + ERR("%s(): Get VCD_I_MULTI_SLICE Failed\n", + __func__); + status = FALSE; + } else { + multiclicecfg->mslice_size = + control_type.n_m_slice_size; + switch (control_type.e_m_slice_sel) { + case VCD_MSLICE_OFF: + multiclicecfg->mslice_mode = VEN_MSLICE_OFF; + break; + case VCD_MSLICE_BY_MB_COUNT: + multiclicecfg->mslice_mode = VEN_MSLICE_CNT_MB; + break; + case VCD_MSLICE_BY_BYTE_COUNT: + multiclicecfg->mslice_mode = + VEN_MSLICE_CNT_BYTE; + break; + case VCD_MSLICE_BY_GOB: + multiclicecfg->mslice_mode = + VEN_MSLICE_GOB; + break; + default: + status = FALSE; + break; + } + } + } + return status; +} + +u32 vid_enc_set_get_ratectrlcfg(struct video_client_ctx *client_ctx, + struct venc_ratectrlcfg *ratectrlcfg, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_rate_control_type control_type; + u32 vcd_status = VCD_ERR_FAIL; + u32 status = TRUE; + + if (!client_ctx || !ratectrlcfg) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_RATE_CONTROL; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_rate_control_type); + + if (set_flag) { + switch (ratectrlcfg->rcmode) { + case VEN_RC_OFF: + control_type.e_rate_control = VCD_RATE_CONTROL_OFF; + break; + case VEN_RC_CBR_VFR: + control_type.e_rate_control = VCD_RATE_CONTROL_CBR_VFR; + break; + case VEN_RC_VBR_CFR: + control_type.e_rate_control = VCD_RATE_CONTROL_VBR_CFR; + break; + case VEN_RC_VBR_VFR: + control_type.e_rate_control = VCD_RATE_CONTROL_VBR_VFR; + break; + default: + status = FALSE; + break; + } + + if (status) { + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &control_type); + if (vcd_status) { + ERR("%s(): Set VCD_I_RATE_CONTROL Failed\n", + __func__); + status = FALSE; + } + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &control_type); + + if (vcd_status) { + ERR("%s(): Get VCD_I_RATE_CONTROL Failed\n", + __func__); + status = FALSE; + } else { + switch (control_type.e_rate_control) { + case VCD_RATE_CONTROL_OFF: + ratectrlcfg->rcmode = VEN_RC_OFF; + break; + case VCD_RATE_CONTROL_CBR_VFR: + ratectrlcfg->rcmode = VEN_RC_CBR_VFR; + break; + case VCD_RATE_CONTROL_VBR_CFR: + ratectrlcfg->rcmode = VEN_RC_VBR_CFR; + break; + case VCD_RATE_CONTROL_VBR_VFR: + ratectrlcfg->rcmode = VEN_RC_VBR_VFR; + break; + default: + status = FALSE; + break; + } + } + } + return status; +} + +u32 vid_enc_set_get_voptimingcfg(struct video_client_ctx *client_ctx, + struct venc_voptimingcfg *voptimingcfg, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_vop_timing_type control_type; + u32 vcd_status = VCD_ERR_FAIL; + u32 status = TRUE; + + if (!client_ctx || !voptimingcfg) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_VOP_TIMING; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_vop_timing_type); + + if (set_flag) { + control_type.n_vop_time_resolution = + voptimingcfg->voptime_resolution; + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &control_type); + + if (vcd_status) { + ERR("%s(): Set VCD_I_VOP_TIMING Failed\n", + __func__); + status = FALSE; + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &control_type); + if (vcd_status) { + ERR("%s(): Get VCD_I_VOP_TIMING Failed\n", + __func__); + status = FALSE; + } else + voptimingcfg->voptime_resolution = + control_type.n_vop_time_resolution; + } + return status; +} + +u32 vid_enc_set_get_headerextension(struct video_client_ctx *client_ctx, + struct venc_headerextension *headerextension, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + u32 control_type; + u32 vcd_status = VCD_ERR_FAIL; + u32 status = TRUE; + + if (!client_ctx || !headerextension) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_HEADER_EXTENSION; + vcd_property_hdr.n_size = sizeof(u32); + + if (set_flag) { + control_type = headerextension->header_extension; + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &control_type); + if (vcd_status) { + ERR("%s(): Set VCD_I_HEADER_EXTENSION Failed\n", + __func__); + status = FALSE; + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &control_type); + if (vcd_status) { + ERR("%s(): Get VCD_I_HEADER_EXTENSION Failed\n", + __func__); + status = FALSE; + } else { + headerextension->header_extension = control_type; + } + } + return status; +} + +u32 vid_enc_set_get_qprange(struct video_client_ctx *client_ctx, + struct venc_qprange *qprange, u32 set_flag) +{ + struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_qp_range_type control_type; + u32 vcd_status = VCD_ERR_FAIL; + u32 status = TRUE; + + if (!client_ctx || !qprange) + return FALSE; + + vcd_property_hdr.prop_id = VCD_I_QP_RANGE; + vcd_property_hdr.n_size = + sizeof(struct vcd_property_qp_range_type); + + if (set_flag) { + control_type.n_max_qp = qprange->maxqp; + control_type.n_min_qp = qprange->minqp; + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &control_type); + + if (vcd_status) { + ERR("%s(): Set VCD_I_QP_RANGE Failed\n", + __func__); + status = FALSE; + } + } else { + vcd_status = vcd_get_property(client_ctx->vcd_handle, + &vcd_property_hdr, &control_type); + if (vcd_status) { + ERR("%s(): Get VCD_I_QP_RANGE Failed\n", + __func__); + status = FALSE; + } else { + qprange->maxqp = control_type.n_max_qp; + qprange->minqp = control_type.n_min_qp; + } + } + return status; +} + +u32 vid_enc_start_stop(struct video_client_ctx *client_ctx, u32 start) +{ + u32 vcd_status; + + if (!client_ctx) + return FALSE; + + if (start) { + vcd_status = vcd_encode_start(client_ctx->vcd_handle); + + if (vcd_status) { + ERR("%s(): vcd_encode_start failed." + " vcd_status = %u\n", __func__, vcd_status); + return FALSE; + } + } else { + vcd_status = vcd_stop(client_ctx->vcd_handle); + if (vcd_status) { + ERR("%s(): vcd_stop failed. vcd_status = %u\n", + __func__, vcd_status); + return FALSE; + } + DBG("Send STOP_DONE message to client = %p\n", + client_ctx); + } + return TRUE; +} + +u32 vid_enc_pause_resume(struct video_client_ctx *client_ctx, u32 pause) +{ + u32 vcd_status; + + if (!client_ctx) + return FALSE; + + if (pause) { + DBG("PAUSE command from client = %p\n", + client_ctx); + vcd_status = vcd_pause(client_ctx->vcd_handle); + } else { + DBG("Resume command from client = %p\n", + client_ctx); + vcd_status = vcd_resume(client_ctx->vcd_handle); + } + + if (vcd_status) + return FALSE; + + return TRUE; +} + +u32 vid_enc_flush(struct video_client_ctx *client_ctx, + struct venc_bufferflush *bufferflush) +{ + u32 status = TRUE, n_mode, vcd_status; + + if (!client_ctx || !bufferflush) + return FALSE; + + switch (bufferflush->flush_mode) { + case VEN_FLUSH_INPUT: + n_mode = VCD_FLUSH_INPUT; + break; + case VEN_FLUSH_OUTPUT: + n_mode = VCD_FLUSH_OUTPUT; + break; + case VEN_FLUSH_ALL: + n_mode = VCD_FLUSH_ALL; + break; + default: + status = FALSE; + break; + } + if (status) { + vcd_status = vcd_flush(client_ctx->vcd_handle, n_mode); + if (vcd_status) + status = FALSE; + } + return status; +} + +u32 vid_enc_get_buffer_req(struct video_client_ctx *client_ctx, + struct venc_allocatorproperty *venc_buf_req, u32 input_dir) +{ + enum vcd_buffer_type e_buffer; + struct vcd_buffer_requirement_type buffer_req; + u32 status = TRUE; + u32 vcd_status; + + if (!client_ctx || !venc_buf_req) + return FALSE; + + e_buffer = VCD_BUFFER_OUTPUT; + if (input_dir) + e_buffer = VCD_BUFFER_INPUT; + + vcd_status = vcd_get_buffer_requirements(client_ctx->vcd_handle, + e_buffer, &buffer_req); + + if (vcd_status) + status = FALSE; + + if (status) { + venc_buf_req->actualcount = buffer_req.n_actual_count; + venc_buf_req->alignment = buffer_req.n_align; + venc_buf_req->datasize = buffer_req.n_size; + venc_buf_req->mincount = buffer_req.n_min_count; + venc_buf_req->maxcount = buffer_req.n_max_count; + venc_buf_req->alignment = buffer_req.n_align; + venc_buf_req->bufpoolid = buffer_req.n_buf_pool_id; + venc_buf_req->suffixsize = 0; + } + return status; +} + +u32 vid_enc_set_buffer_req(struct video_client_ctx *client_ctx, + struct venc_allocatorproperty *venc_buf_req, u32 input_dir) +{ + enum vcd_buffer_type e_buffer; + struct vcd_buffer_requirement_type buffer_req; + u32 status = TRUE; + u32 vcd_status; + + if (!client_ctx || !venc_buf_req) + return FALSE; + + e_buffer = VCD_BUFFER_OUTPUT; + if (input_dir) + e_buffer = VCD_BUFFER_INPUT; + + buffer_req.n_actual_count = venc_buf_req->actualcount; + buffer_req.n_align = venc_buf_req->alignment; + buffer_req.n_size = venc_buf_req->datasize; + buffer_req.n_min_count = venc_buf_req->mincount; + buffer_req.n_max_count = venc_buf_req->maxcount; + buffer_req.n_align = venc_buf_req->alignment; + buffer_req.n_buf_pool_id = 0; + + vcd_status = vcd_set_buffer_requirements(client_ctx->vcd_handle, + e_buffer, &buffer_req); + + if (vcd_status) + status = FALSE; + return status; +} + +u32 vid_enc_set_buffer(struct video_client_ctx *client_ctx, + struct venc_bufferpayload *buffer_info, + enum venc_buffer_dir buffer_type) +{ + u32 vcd_status = VCD_ERR_FAIL; + enum vcd_buffer_type buffer_vcd_type; + enum buffer_dir dir_buffer = BUFFER_TYPE_INPUT; + unsigned long user_vaddr, kernel_vaddr, phy_addr, len; + int pmem_fd; + struct file *file; + struct buf_addr_table *buf_addr_table; + + s32 buffer_index = -1; + + if (!client_ctx || !buffer_info) + return FALSE; + + user_vaddr = (unsigned long)buffer_info->pbuffer; + + if (buffer_type == VEN_BUFFER_TYPE_OUTPUT) + dir_buffer = BUFFER_TYPE_OUTPUT; + + /*If buffer already set, ignore */ + if (vid_c_lookup_addr_table(client_ctx, dir_buffer, + TRUE, &user_vaddr, &kernel_vaddr, + &phy_addr, &pmem_fd, &file, + &buffer_index)) { + + DBG("%s() : user_virt_addr = 0x%08lx is already set.", + __func__, user_vaddr); + return TRUE; + } + + if (get_pmem_file(buffer_info->fd, + &phy_addr, &kernel_vaddr, &len, &file)) { + ERR("%s(): get_pmem_file failed\n", __func__); + return FALSE; + } + put_pmem_file(file); + if (buffer_type == VEN_BUFFER_TYPE_INPUT) { + buffer_vcd_type = VCD_BUFFER_INPUT; + client_ctx->num_of_input_buffers++; + + if (client_ctx->num_of_input_buffers > + VID_ENC_MAX_NUM_OF_BUFF) { + ERR("%s(): num_of_input_buffers reached max value" + " VID_ENC_MAX_NUM_OF_BUFF \n", __func__); + client_ctx->num_of_input_buffers--; + return FALSE; + } + + buffer_index = client_ctx->num_of_input_buffers - 1; + buf_addr_table = + &client_ctx->input_buf_addr_table[buffer_index]; + + buf_addr_table->user_vaddr = + (unsigned long)buffer_info->pbuffer; + kernel_vaddr += (unsigned long)buffer_info->offset; + phy_addr += (unsigned long)buffer_info->offset; + buf_addr_table->kernel_vaddr = kernel_vaddr; + buf_addr_table->phy_addr = phy_addr; + buf_addr_table->pmem_fd = buffer_info->fd; + buf_addr_table->file = file; + } else { + buffer_vcd_type = VCD_BUFFER_OUTPUT; + + client_ctx->num_of_output_buffers++; + + if (client_ctx->num_of_output_buffers > + VID_ENC_MAX_NUM_OF_BUFF) { + ERR("%s(): num_of_outut_buffers reached max value" + " VID_ENC_MAX_NUM_OF_BUFF \n", __func__); + client_ctx->num_of_output_buffers--; + return FALSE; + } + + buffer_index = client_ctx->num_of_output_buffers - 1; + + buf_addr_table = + &client_ctx->output_buf_addr_table[buffer_index]; + kernel_vaddr += (unsigned long)buffer_info->offset; + phy_addr += (unsigned long)buffer_info->offset; + buf_addr_table->user_vaddr = + (unsigned long)buffer_info->pbuffer; + buf_addr_table->kernel_vaddr = kernel_vaddr; + buf_addr_table->phy_addr = phy_addr; + buf_addr_table->pmem_fd = buffer_info->fd; + buf_addr_table->file = file; + } + + vcd_status = vcd_set_buffer(client_ctx->vcd_handle, + buffer_vcd_type, (u8 *) kernel_vaddr, + buffer_info->nsize); + + if (!vcd_status) + return TRUE; + else + return FALSE; +} + +u32 vid_enc_encode_frame(struct video_client_ctx *client_ctx, + struct venc_buffer *input_frame_info) +{ + struct vcd_frame_data_type vcd_input_buffer; + unsigned long kernel_vaddr, phy_addr, user_vaddr; + int pmem_fd; + struct file *file; + s32 buffer_index = -1; + + u32 vcd_status = VCD_ERR_FAIL; + + if (!client_ctx || !input_frame_info) + return FALSE; + + user_vaddr = (unsigned long)input_frame_info->ptrbuffer; + + if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_INPUT, + TRUE, &user_vaddr, &kernel_vaddr, + &phy_addr, &pmem_fd, &file, + &buffer_index)) { + + /* kernel_vaddr is found. send the frame to VCD */ + memset((void *)&vcd_input_buffer, 0, + sizeof(struct vcd_frame_data_type)); + + vcd_input_buffer.p_virtual = + (u8 *) (kernel_vaddr + input_frame_info->offset); + + vcd_input_buffer.n_offset = input_frame_info->offset; + vcd_input_buffer.n_frm_clnt_data = + (u32) input_frame_info->clientdata; + vcd_input_buffer.n_ip_frm_tag = + (u32) input_frame_info->clientdata; + vcd_input_buffer.n_data_len = input_frame_info->len; + vcd_input_buffer.time_stamp = input_frame_info->timestamp; + + /* Rely on VCD using the same flags as OMX */ + vcd_input_buffer.n_flags = input_frame_info->flags; + + vcd_status = vcd_encode_frame(client_ctx->vcd_handle, + &vcd_input_buffer); + if (!vcd_status) + return TRUE; + else { + ERR("%s(): vcd_encode_frame failed = %u\n", + __func__, vcd_status); + return FALSE; + } + + } else { + ERR("%s(): kernel_vaddr not found\n", + __func__); + return FALSE; + } +} + +u32 vid_enc_fill_output_buffer(struct video_client_ctx *client_ctx, + struct venc_buffer *output_frame_info) +{ + unsigned long kernel_vaddr, phy_addr, user_vaddr; + int pmem_fd; + struct file *file; + s32 buffer_index = -1; + u32 vcd_status = VCD_ERR_FAIL; + + struct vcd_frame_data_type vcd_frame; + + if (!client_ctx || !output_frame_info) + return FALSE; + + user_vaddr = (unsigned long)output_frame_info->ptrbuffer; + + if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, + TRUE, &user_vaddr, &kernel_vaddr, + &phy_addr, &pmem_fd, &file, + &buffer_index)) { + + memset((void *)&vcd_frame, 0, + sizeof(struct vcd_frame_data_type)); + vcd_frame.p_virtual = (u8 *) kernel_vaddr; + vcd_frame.n_frm_clnt_data = (u32) output_frame_info->clientdata; + vcd_frame.n_alloc_len = output_frame_info->size; + + vcd_status = vcd_fill_output_buffer(client_ctx->vcd_handle, + &vcd_frame); + if (!vcd_status) + return TRUE; + else { + ERR("%s(): vcd_fill_output_buffer failed = %u\n", + __func__, vcd_status); + return FALSE; + } + } else { + ERR("%s(): kernel_vaddr not found\n", __func__); + return FALSE; + } +} diff --git a/drivers/misc/video_core/720p/enc/venc_internal.h b/drivers/misc/video_core/720p/enc/venc_internal.h new file mode 100644 index 0000000000000..ee74ff68cf186 --- /dev/null +++ b/drivers/misc/video_core/720p/enc/venc_internal.h @@ -0,0 +1,156 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef VENC_INTERNAL_H +#define VENC_INTERNAL_H + +#include +#include + +#include "video_core_init.h" + +#define VID_ENC_MAX_ENCODER_CLIENTS 16 +#define VID_ENC_MAX_NUM_OF_BUFF 100 + +enum venc_buffer_dir{ + VEN_BUFFER_TYPE_INPUT, + VEN_BUFFER_TYPE_OUTPUT +}; + +struct vid_enc_msg { + struct list_head list; + struct venc_msg venc_msg_info; +}; + +struct vid_enc_dev { + + struct cdev cdev; + struct device *device; + resource_size_t phys_base; + void __iomem *virt_base; + unsigned int irq; + struct clk *hclk; + struct clk *hclk_div2; + struct clk *pclk; + unsigned long hclk_rate; + struct mutex lock; + s32 device_handle; + struct video_client_ctx venc_clients[VID_ENC_MAX_ENCODER_CLIENTS]; + u32 num_clients; +}; + +u32 vid_enc_set_get_base_cfg(struct video_client_ctx *client_ctx, + struct venc_basecfg *base_config, u32 set_flag); + +u32 vid_enc_set_get_inputformat(struct video_client_ctx *client_ctx, + u32 *input_format, u32 set_flag); + +u32 vid_enc_set_get_codec(struct video_client_ctx *client_ctx, u32 *codec_type, + u32 set_flag); + +u32 vid_enc_set_get_framesize(struct video_client_ctx *client_ctx, + u32 *height, u32 *width, u32 set_flag); + +u32 vid_enc_set_get_bitrate(struct video_client_ctx *client_ctx, + struct venc_targetbitrate *venc_bitrate, u32 set_flag); + +u32 vid_enc_set_get_framerate(struct video_client_ctx *client_ctx, + struct venc_framerate *frame_rate, u32 set_flag); + +u32 vid_enc_set_get_live_mode(struct video_client_ctx *client_ctx, + struct venc_switch *encoder_switch, u32 set_flag); + +u32 vid_enc_set_get_short_header(struct video_client_ctx *client_ctx, + struct venc_switch *encoder_switch, u32 set_flag); + +u32 vid_enc_set_get_profile(struct video_client_ctx *client_ctx, + struct venc_profile *profile, u32 set_flag); + +u32 vid_enc_set_get_profile_level(struct video_client_ctx *client_ctx, + struct ven_profilelevel *profile_level, u32 set_flag); + +u32 vid_enc_set_get_session_qp(struct video_client_ctx *client_ctx, + struct venc_sessionqp *session_qp, u32 set_flag); + +u32 vid_enc_set_get_intraperiod(struct video_client_ctx *client_ctx, + struct venc_intraperiod *intraperiod, u32 set_flag); + +u32 vid_enc_request_iframe(struct video_client_ctx *client_ctx); + +u32 vid_enc_get_sequence_header(struct video_client_ctx *client_ctx, + struct venc_seqheader *seq_header); + +u32 vid_enc_set_get_entropy_cfg(struct video_client_ctx *client_ctx, + struct venc_entropycfg *entropy_cfg, u32 set_flag); + +u32 vid_enc_set_get_dbcfg(struct video_client_ctx *client_ctx, + struct venc_dbcfg *dbcfg, u32 set_flag); + +u32 vid_enc_set_get_intrarefresh(struct video_client_ctx *client_ctx, + struct venc_intrarefresh *intrarefresh, u32 set_flag); + +u32 vid_enc_set_get_multiclicecfg(struct video_client_ctx *client_ctx, + struct venc_multiclicecfg *multiclicecfg, u32 set_flag); + +u32 vid_enc_set_get_ratectrlcfg(struct video_client_ctx *client_ctx, + struct venc_ratectrlcfg *ratectrlcfg, u32 set_flag); + +u32 vid_enc_set_get_voptimingcfg(struct video_client_ctx *client_ctx, + struct venc_voptimingcfg *voptimingcfg, u32 set_flag); + +u32 vid_enc_set_get_headerextension(struct video_client_ctx *client_ctx, + struct venc_headerextension *headerextension, u32 set_flag); + +u32 vid_enc_set_get_qprange(struct video_client_ctx *client_ctx, + struct venc_qprange *qprange, u32 set_flag); + +u32 vid_enc_start_stop(struct video_client_ctx *client_ctx, u32 start); + +u32 vid_enc_pause_resume(struct video_client_ctx *client_ctx, u32 pause); + +u32 vid_enc_flush(struct video_client_ctx *client_ctx, + struct venc_bufferflush *bufferflush); + +u32 vid_enc_get_buffer_req(struct video_client_ctx *client_ctx, + struct venc_allocatorproperty *venc_buf_req, u32 input_dir); + +u32 vid_enc_set_buffer_req(struct video_client_ctx *client_ctx, + struct venc_allocatorproperty *venc_buf_req, u32 input_dir); + +u32 vid_enc_set_buffer(struct video_client_ctx *client_ctx, + struct venc_bufferpayload *buffer_info, + enum venc_buffer_dir buffer_type); + +u32 vid_enc_encode_frame(struct video_client_ctx *client_ctx, + struct venc_buffer *input_frame_info); + +u32 vid_enc_fill_output_buffer(struct video_client_ctx *client_ctx, + struct venc_buffer *output_frame_info); + +#endif diff --git a/drivers/misc/video_core/720p/init/video_core_init.c b/drivers/misc/video_core/720p/init/video_core_init.c new file mode 100644 index 0000000000000..d0461e35178dc --- /dev/null +++ b/drivers/misc/video_core/720p/init/video_core_init.c @@ -0,0 +1,937 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vcd_api.h" +#include "video_core_init_internal.h" +#include "video_core_init.h" + +#if DEBUG +#define DBG(x...) printk(KERN_DEBUG x) +#else +#define DBG(x...) +#endif + +#define VID_C_NAME "msm_vidc_reg" + +#define ERR(x...) printk(KERN_ERR x) + +static struct vid_c_dev *vid_c_device_p; +static dev_t vid_c_dev_num; +static struct class *vid_c_class; + +static const struct file_operations vid_c_fops = { + .owner = THIS_MODULE, + .open = NULL, + .release = NULL, + .ioctl = NULL, +}; + +struct workqueue_struct *vid_c_wq; +struct workqueue_struct *vidc_timer_wq; +static irqreturn_t vid_c_isr(int irq, void *dev); +static spinlock_t vidc_spin_lock; + +#define VIDC_BOOT_FW "vidc_720p_command_control.fw" +#define VIDC_MPG4_DEC_FW "vidc_720p_mp4_dec_mc.fw" +#define VIDC_H263_DEC_FW "vidc_720p_h263_dec_mc.fw" +#define VIDC_H264_DEC_FW "vidc_720p_h264_dec_mc.fw" +#define VIDC_MPG4_ENC_FW "vidc_720p_mp4_enc_mc.fw" +#define VIDC_H264_ENC_FW "vidc_720p_h264_enc_mc.fw" + +static void vid_c_timer_fn(unsigned long data) +{ + unsigned long flag; + struct vid_c_timer *hw_timer = NULL; + + DBG("%s() Timer expired \n", __func__); + spin_lock_irqsave(&vidc_spin_lock, flag); + hw_timer = (struct vid_c_timer *)data; + list_add_tail(&hw_timer->list, &vid_c_device_p->vidc_timer_queue); + spin_unlock_irqrestore(&vidc_spin_lock, flag); + DBG("Queue the work for timer \n"); + queue_work(vidc_timer_wq, &vid_c_device_p->vidc_timer_worker); +} + +static void vid_c_timer_handler(struct work_struct *work) +{ + unsigned long flag = 0; + u32 islist_empty = 0; + struct vid_c_timer *hw_timer = NULL; + + DBG("%s() Timer expired \n", __func__); + do { + spin_lock_irqsave(&vidc_spin_lock, flag); + islist_empty = list_empty(&vid_c_device_p->vidc_timer_queue); + if (!islist_empty) { + hw_timer = list_first_entry( + &vid_c_device_p->vidc_timer_queue, + struct vid_c_timer, list); + list_del(&hw_timer->list); + } + spin_unlock_irqrestore(&vidc_spin_lock, flag); + if (!islist_empty && hw_timer && hw_timer->cb_func) + hw_timer->cb_func(hw_timer->userdata); + } while (!islist_empty); +} + +static void vid_c_work_handler(struct work_struct *work) +{ + DBG("vid_c_work_handler()"); + vcd_read_and_clear_interrupt(); + vcd_response_handler(); + enable_irq(vid_c_device_p->irq); + DBG("vid_c_work_handler() done"); +} + +static DECLARE_WORK(vid_c_work, vid_c_work_handler); + +static int __init vid_c_720p_probe(struct platform_device *pdev) +{ + struct resource *resource; + DBG("Enter %s()\n", __func__); + + if (pdev->id) { + ERR("Invalid plaform device ID = %d\n", pdev->id); + return -EINVAL; + } + vid_c_device_p->irq = platform_get_irq(pdev, 0); + if (unlikely(vid_c_device_p->irq < 0)) { + ERR("%s(): Invalid irq = %d\n", __func__, + vid_c_device_p->irq); + return -ENXIO; + } + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!resource)) { + ERR("%s(): Invalid resource \n", __func__); + return -ENXIO; + } + + vid_c_device_p->phys_base = resource->start; + vid_c_device_p->virt_base = ioremap(resource->start, + resource->end - resource->start + 1); + + if (!vid_c_device_p->virt_base) { + ERR("%s() : ioremap failed\n", __func__); + return -ENOMEM; + } + vid_c_device_p->device = &pdev->dev; + mutex_init(&vid_c_device_p->lock); + + vid_c_wq = create_singlethread_workqueue("vid_c_worker_queue"); + if (!vid_c_wq) { + ERR("%s: create workque failed \n", __func__); + return -ENOMEM; + } + return 0; +} + +static int __devexit vid_c_720p_remove(struct platform_device *pdev) +{ + if (pdev->id) { + ERR("Invalid plaform device ID = %d\n", pdev->id); + return -EINVAL; + } + return 0; +} + + +static struct platform_driver msm_vid_c_720p_platform_driver = { + .probe = vid_c_720p_probe, + .remove = vid_c_720p_remove, + .driver = { + .name = "msm_vidc_720p", + }, +}; + +static void __exit vid_c_exit(void) +{ + platform_driver_unregister(&msm_vid_c_720p_platform_driver); +} + +static irqreturn_t vid_c_isr(int irq, void *dev) +{ + DBG("\n vid_c_isr() %d ", irq); + disable_irq_nosync(irq); + queue_work(vid_c_wq, &vid_c_work); + return IRQ_HANDLED; +} + +static int __init vid_c_init(void) +{ + int rc = 0; + struct device *class_devp; + + vid_c_device_p = kzalloc(sizeof(struct vid_c_dev), GFP_KERNEL); + if (!vid_c_device_p) { + ERR("%s Unable to allocate memory for vid_c_dev\n", + __func__); + return -ENOMEM; + } + + rc = alloc_chrdev_region(&vid_c_dev_num, 0, 1, VID_C_NAME); + if (rc < 0) { + ERR("%s: alloc_chrdev_region Failed rc = %d\n", + __func__, rc); + goto error_vid_c_alloc_chrdev_region; + } + + vid_c_class = class_create(THIS_MODULE, VID_C_NAME); + if (IS_ERR(vid_c_class)) { + rc = PTR_ERR(vid_c_class); + ERR("%s: couldn't create vid_c_class rc = %d\n", + __func__, rc); + + goto error_vid_c_class_create; + } + + class_devp = device_create(vid_c_class, NULL, vid_c_dev_num, NULL, + VID_C_NAME); + + if (IS_ERR(class_devp)) { + rc = PTR_ERR(class_devp); + ERR("%s: class device_create failed %d\n", + __func__, rc); + goto error_vid_c_class_device_create; + } + + cdev_init(&vid_c_device_p->cdev, &vid_c_fops); + vid_c_device_p->cdev.owner = THIS_MODULE; + rc = cdev_add(&(vid_c_device_p->cdev), vid_c_dev_num, 1); + + if (rc < 0) { + ERR("%s: cdev_add failed %d\n", __func__, rc); + goto error_vid_c_cdev_add; + } + + rc = platform_driver_register(&msm_vid_c_720p_platform_driver); + if (rc) { + ERR("%s failed to load\n", __func__); + goto error_vid_c_platfom_register; + } + + rc = request_irq(vid_c_device_p->irq, vid_c_isr, IRQF_TRIGGER_HIGH, + "vid_c", vid_c_device_p->device); + + if (unlikely(rc)) { + ERR("%s() :request_irq failed\n", __func__); + goto error_vid_c_platfom_register; + } + + vidc_timer_wq = create_singlethread_workqueue("vidc_timer_wq"); + if (!vidc_timer_wq) { + ERR("%s: create workque failed \n", __func__); + rc = -ENOMEM; + goto error_vid_c_platfom_register; + } + + DBG("Disabling IRQ in %s()\n", __func__); + disable_irq_nosync(vid_c_device_p->irq); + INIT_WORK(&vid_c_device_p->vidc_timer_worker, + vid_c_timer_handler); + spin_lock_init(&vidc_spin_lock); + INIT_LIST_HEAD(&vid_c_device_p->vidc_timer_queue); + vid_c_device_p->clock_enabled = 0; + vid_c_device_p->ref_count = 0; + vid_c_device_p->firmware_refcount = 0; + vid_c_device_p->get_firmware = 0; + + return 0; + +error_vid_c_platfom_register: + cdev_del(&(vid_c_device_p->cdev)); +error_vid_c_cdev_add: + device_destroy(vid_c_class, vid_c_dev_num); +error_vid_c_class_device_create: + class_destroy(vid_c_class); +error_vid_c_class_create: + unregister_chrdev_region(vid_c_dev_num, 1); +error_vid_c_alloc_chrdev_region: + kfree(vid_c_device_p); + + return rc; +} + +void __iomem *vid_c_get_ioaddr(void) +{ + return (u8 *)vid_c_device_p->virt_base; +} +EXPORT_SYMBOL(vid_c_get_ioaddr); +#ifdef USE_RES_TRACKER + +u32 vid_c_enable_pwr_rail(void) +{ + int rc = -1; + mutex_lock(&vid_c_device_p->lock); + + if (!vid_c_device_p->rail_enabled) { + rc = internal_pwr_rail_mode(PWR_RAIL_MFC_CLK, + PWR_RAIL_CTL_MANUAL); + if (rc) { + ERR("%s(): internal_pwr_rail_mode failed %d \n", + __func__, rc); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + DBG("%s(): internal_pwr_rail_mode Success %d \n", + __func__, rc); + + vid_c_device_p->pclk = clk_get(vid_c_device_p->device, + "mfc_pclk"); + + if (IS_ERR(vid_c_device_p->pclk)) { + ERR("%s(): mfc_pclk get failed \n", __func__); + + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + + vid_c_device_p->hclk = clk_get(vid_c_device_p->device, + "mfc_clk"); + + if (IS_ERR(vid_c_device_p->hclk)) { + ERR("%s(): mfc_clk get failed \n", __func__); + + clk_put(vid_c_device_p->pclk); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + + vid_c_device_p->hclk_div2 = + clk_get(vid_c_device_p->device, "mfc_div2_clk"); + + if (IS_ERR(vid_c_device_p->pclk)) { + ERR("%s(): mfc_div2_clk get failed \n", __func__); + + clk_put(vid_c_device_p->pclk); + clk_put(vid_c_device_p->hclk); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + + rc = internal_pwr_rail_ctl(PWR_RAIL_MFC_CLK, 1); + if (rc) { + ERR("\n internal_pwr_rail_ctl failed %d\n", rc); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + DBG("%s(): internal_pwr_rail_ctl Success %d \n", __func__, rc); + msleep(20); + + rc = clk_reset(vid_c_device_p->pclk, CLK_RESET_DEASSERT); + if (rc) { + ERR("\n clk_reset failed %d\n", rc); + return FALSE; + } + msleep(20); + } + vid_c_device_p->rail_enabled = 1; + mutex_unlock(&vid_c_device_p->lock); + return TRUE; +} +EXPORT_SYMBOL(vid_c_enable_pwr_rail); + +u32 vid_c_disable_pwr_rail(void) +{ + int rc = -1; + mutex_lock(&vid_c_device_p->lock); + + if (vid_c_device_p->clock_enabled) { + mutex_unlock(&vid_c_device_p->lock); + DBG("\n Calling CLK disable in Power Down \n"); + vid_c_disable_clk(); + mutex_lock(&vid_c_device_p->lock); + } + + if (!vid_c_device_p->rail_enabled) { + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + + vid_c_device_p->rail_enabled = 0; + rc = clk_reset(vid_c_device_p->pclk, CLK_RESET_ASSERT); + if (rc) { + ERR("\n clk_reset failed %d\n", rc); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + msleep(20); + + rc = internal_pwr_rail_ctl(PWR_RAIL_MFC_CLK, 0); + if (rc) { + ERR("\n clk_reset failed %d\n", rc); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + + clk_put(vid_c_device_p->hclk_div2); + clk_put(vid_c_device_p->hclk); + clk_put(vid_c_device_p->pclk); + + mutex_unlock(&vid_c_device_p->lock); + + return TRUE; +} +EXPORT_SYMBOL(vid_c_disable_pwr_rail); + +u32 vid_c_enable_clk(void) +{ + mutex_lock(&vid_c_device_p->lock); + + if (!vid_c_device_p->clock_enabled) { + DBG("Enabling IRQ in %s()\n", __func__); + enable_irq(vid_c_device_p->irq); + + DBG("%s(): Enabling the clocks ...\n", __func__); + + if (clk_enable(vid_c_device_p->pclk)) { + ERR("vidc pclk Enable failed \n"); + + clk_put(vid_c_device_p->hclk); + clk_put(vid_c_device_p->hclk_div2); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + + if (clk_enable(vid_c_device_p->hclk)) { + ERR("vidc hclk Enable failed \n"); + clk_put(vid_c_device_p->pclk); + clk_put(vid_c_device_p->hclk_div2); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + + if (clk_enable(vid_c_device_p->hclk_div2)) { + ERR("vidc hclk Enable failed \n"); + clk_put(vid_c_device_p->hclk); + clk_put(vid_c_device_p->pclk); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + } + + vid_c_device_p->clock_enabled = 1; + mutex_unlock(&vid_c_device_p->lock); + return TRUE; +} +EXPORT_SYMBOL(vid_c_enable_clk); + +u32 vid_c_sel_clk_rate(unsigned long hclk_rate) +{ + mutex_lock(&vid_c_device_p->lock); + if (clk_set_rate(vid_c_device_p->hclk, + hclk_rate)) { + ERR("vidc hclk set rate failed \n"); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + vid_c_device_p->hclk_rate = hclk_rate; + mutex_unlock(&vid_c_device_p->lock); + return TRUE; +} +EXPORT_SYMBOL(vid_c_sel_clk_rate); + +u32 vid_c_get_clk_rate(unsigned long *phclk_rate) +{ + if (!phclk_rate) { + ERR("vid_c_get_clk_rate(): phclk_rate is NULL\n"); + return FALSE; + } + mutex_lock(&vid_c_device_p->lock); + *phclk_rate = clk_get_rate(vid_c_device_p->hclk); + if (!(*phclk_rate)) { + ERR("vidc hclk get rate failed \n"); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + mutex_unlock(&vid_c_device_p->lock); + return TRUE; +} +EXPORT_SYMBOL(vid_c_get_clk_rate); + +u32 vid_c_disable_clk(void) +{ + mutex_lock(&vid_c_device_p->lock); + + if (!vid_c_device_p->clock_enabled) { + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + + DBG("Disabling IRQ in %s()\n", __func__); + disable_irq_nosync(vid_c_device_p->irq); + DBG("%s(): Disabling the clocks ...\n", __func__); + + vid_c_device_p->clock_enabled = 0; + clk_disable(vid_c_device_p->hclk); + clk_disable(vid_c_device_p->hclk_div2); + clk_disable(vid_c_device_p->pclk); + + mutex_unlock(&vid_c_device_p->lock); + + return TRUE; +} +EXPORT_SYMBOL(vid_c_disable_clk); + +#else + +u32 vid_c_enable_clk(unsigned long hclk_rate) +{ + int rc = -1; + mutex_lock(&vid_c_device_p->lock); + vid_c_device_p->ref_count++; + + if (!vid_c_device_p->clock_enabled) { + DBG("Enabling IRQ in %s()\n", __func__); + enable_irq(vid_c_device_p->irq); + + rc = internal_pwr_rail_mode + (PWR_RAIL_MFC_CLK, PWR_RAIL_CTL_MANUAL); + if (rc) { + ERR("%s(): internal_pwr_rail_mode failed %d \n", + __func__, rc); + return FALSE; + } + DBG("%s(): internal_pwr_rail_mode Success %d \n", + __func__, rc); + + vid_c_device_p->pclk = + clk_get(vid_c_device_p->device, "mfc_pclk"); + + if (IS_ERR(vid_c_device_p->pclk)) { + ERR("%s(): mfc_pclk get failed \n", __func__); + + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + + vid_c_device_p->hclk = + clk_get(vid_c_device_p->device, "mfc_clk"); + + if (IS_ERR(vid_c_device_p->hclk)) { + ERR("%s(): mfc_clk get failed \n", __func__); + + clk_put(vid_c_device_p->pclk); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + + vid_c_device_p->hclk_div2 = + clk_get(vid_c_device_p->device, "mfc_div2_clk"); + + if (IS_ERR(vid_c_device_p->pclk)) { + ERR("%s(): mfc_div2_clk get failed \n", __func__); + + clk_put(vid_c_device_p->pclk); + clk_put(vid_c_device_p->hclk); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + + vid_c_device_p->hclk_rate = hclk_rate; + + if (clk_set_rate(vid_c_device_p->hclk, + vid_c_device_p->hclk_rate)) { + ERR("vid_c hclk set rate failed \n"); + clk_put(vid_c_device_p->pclk); + clk_put(vid_c_device_p->hclk); + clk_put(vid_c_device_p->hclk_div2); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + + if (clk_enable(vid_c_device_p->pclk)) { + ERR("vid_c pclk Enable failed \n"); + + clk_put(vid_c_device_p->hclk); + clk_put(vid_c_device_p->hclk_div2); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + + if (clk_enable(vid_c_device_p->hclk)) { + ERR("vid_c hclk Enable failed \n"); + clk_put(vid_c_device_p->pclk); + clk_put(vid_c_device_p->hclk_div2); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + + if (clk_enable(vid_c_device_p->hclk_div2)) { + ERR("vid_c hclk Enable failed \n"); + clk_put(vid_c_device_p->hclk); + clk_put(vid_c_device_p->pclk); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + msleep(20); + rc = internal_pwr_rail_ctl(PWR_RAIL_MFC_CLK, 1); + if (rc) { + ERR("\n internal_pwr_rail_ctl failed %d\n", rc); + return FALSE; + } + DBG("%s(): internal_pwr_rail_ctl Success %d \n", + __func__, rc); + msleep(20); + rc = clk_reset(vid_c_device_p->pclk, CLK_RESET_DEASSERT); + if (rc) { + ERR("\n clk_reset failed %d\n", rc); + return FALSE; + } + msleep(20); + } + vid_c_device_p->clock_enabled = 1; + mutex_unlock(&vid_c_device_p->lock); + return TRUE; +} +EXPORT_SYMBOL(vid_c_enable_clk); + +u32 vid_c_disable_clk(void) +{ + int rc = -1; + mutex_lock(&vid_c_device_p->lock); + + if (!vid_c_device_p->ref_count || + !vid_c_device_p->clock_enabled) { + return FALSE; + } + + if (vid_c_device_p->ref_count > 0) + vid_c_device_p->ref_count--; + + if (!vid_c_device_p->ref_count) { + DBG("Disabling IRQ in %s()\n", __func__); + disable_irq_nosync(vid_c_device_p->irq); + rc = clk_reset(vid_c_device_p->pclk, CLK_RESET_ASSERT); + if (rc) { + ERR("\n clk_reset failed %d\n", rc); + return FALSE; + } + msleep(20); + + rc = internal_pwr_rail_ctl(PWR_RAIL_MFC_CLK, 0); + if (rc) { + ERR("\n internal_pwr_rail_ctl failed %d\n", rc); + return FALSE; + } + + vid_c_device_p->clock_enabled = 0; + clk_disable(vid_c_device_p->hclk); + clk_disable(vid_c_device_p->hclk_div2); + clk_disable(vid_c_device_p->pclk); + + clk_put(vid_c_device_p->hclk_div2); + clk_put(vid_c_device_p->hclk); + clk_put(vid_c_device_p->pclk); + + } + mutex_unlock(&vid_c_device_p->lock); + return TRUE; +} +EXPORT_SYMBOL(vid_c_disable_clk); + +#endif +unsigned char *vid_c_command_control_fw; +u32 vid_c_command_control_fw_size; + +unsigned char *vid_c_mpg4_dec_fw; +u32 vid_c_mpg4_dec_fw_size; + +unsigned char *vid_c_h263_dec_fw; +u32 vid_c_h263_dec_fw_size; + +unsigned char *vid_c_h264_dec_fw; +u32 vid_c_h264_dec_fw_size; + +unsigned char *vid_c_mpg4_enc_fw; +u32 vid_c_mpg4_enc_fw_size; + + +unsigned char *vid_c_h264_enc_fw; +u32 vid_c_h264_enc_fw_size; + + +int vid_c_load_firmware(void) +{ + int rc = 0; + const struct firmware *fw_boot = NULL; + const struct firmware *fw_mpg4_dec = NULL; + const struct firmware *fw_h263_dec = NULL; + const struct firmware *fw_h264_dec = NULL; + const struct firmware *fw_mpg4_enc = NULL; + const struct firmware *fw_h264_enc = NULL; + + u32 status = TRUE; + + mutex_lock(&vid_c_device_p->lock); + + if (!vid_c_device_p->get_firmware) { + rc = request_firmware(&fw_boot, + VIDC_BOOT_FW, vid_c_device_p->device); + if (rc) { + ERR("request_firmware for %s failed with error %d\n", + VIDC_BOOT_FW, rc); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; + } + vid_c_command_control_fw = (unsigned char *)fw_boot->data; + vid_c_command_control_fw_size = (u32) fw_boot->size; + + rc = request_firmware(&fw_mpg4_dec, VIDC_MPG4_DEC_FW, + vid_c_device_p->device); + if (rc) { + ERR("request_firmware for %s failed with error %d\n", + VIDC_BOOT_FW, rc); + status = FALSE; + goto boot_fw_free; + } + vid_c_mpg4_dec_fw = (unsigned char *)fw_mpg4_dec->data; + vid_c_mpg4_dec_fw_size = (u32) fw_mpg4_dec->size; + + + rc = request_firmware(&fw_h263_dec, VIDC_H263_DEC_FW, + vid_c_device_p->device); + if (rc) { + ERR("request_firmware for %s failed with error %d\n", + VIDC_BOOT_FW, rc); + status = FALSE; + goto mp4dec_fw_free; + } + vid_c_h263_dec_fw = (unsigned char *)fw_h263_dec->data; + vid_c_h263_dec_fw_size = (u32) fw_h263_dec->size; + + rc = request_firmware(&fw_h264_dec, VIDC_H264_DEC_FW, + vid_c_device_p->device); + if (rc) { + ERR("request_firmware for %s failed with error %d\n", + VIDC_BOOT_FW, rc); + status = FALSE; + goto h263dec_fw_free; + } + vid_c_h264_dec_fw = (unsigned char *)fw_h264_dec->data; + vid_c_h264_dec_fw_size = (u32) fw_h264_dec->size; + + rc = request_firmware(&fw_mpg4_enc, VIDC_MPG4_ENC_FW, + vid_c_device_p->device); + if (rc) { + ERR("request_firmware for %s failed with error %d\n", + VIDC_BOOT_FW, rc); + status = FALSE; + goto h264dec_fw_free; + } + vid_c_mpg4_enc_fw = (unsigned char *)fw_mpg4_enc->data; + vid_c_mpg4_enc_fw_size = (u32) fw_mpg4_enc->size; + + rc = request_firmware(&fw_h264_enc, VIDC_H264_ENC_FW, + vid_c_device_p->device); + if (rc) { + ERR("request_firmware for %s failed with error %d\n", + VIDC_BOOT_FW, rc); + status = FALSE; + goto mp4enc_fw_free; + } + vid_c_h264_enc_fw = (unsigned char *)fw_h264_enc->data; + vid_c_h264_enc_fw_size = (u32) fw_h264_enc->size; + vid_c_device_p->get_firmware = 1; + } + + vid_c_device_p->firmware_refcount++; + + mutex_unlock(&vid_c_device_p->lock); + return status; + + release_firmware(fw_h264_enc); +mp4enc_fw_free: + release_firmware(fw_mpg4_enc); +h264dec_fw_free: + release_firmware(fw_h264_dec); +h263dec_fw_free: + release_firmware(fw_h263_dec); +mp4dec_fw_free: + release_firmware(fw_mpg4_dec); +boot_fw_free: + release_firmware(fw_boot); + mutex_unlock(&vid_c_device_p->lock); + return FALSE; +} +EXPORT_SYMBOL(vid_c_load_firmware); + +void vid_c_release_firmware(void) +{ + mutex_lock(&vid_c_device_p->lock); + if (vid_c_device_p->firmware_refcount > 0) + vid_c_device_p->firmware_refcount--; + else + vid_c_device_p->firmware_refcount = 0; + mutex_unlock(&vid_c_device_p->lock); +} +EXPORT_SYMBOL(vid_c_release_firmware); + +u32 vid_c_lookup_addr_table(struct video_client_ctx *client_ctx, + enum buffer_dir buffer_type, + u32 search_with_user_vaddr, + unsigned long *user_vaddr, + unsigned long *kernel_vaddr, + unsigned long *phy_addr, int *pmem_fd, + struct file **file, s32 *buffer_index) +{ + u32 num_of_buffers; + u32 i; + struct buf_addr_table *buf_addr_table; + u32 found = FALSE; + + if (!client_ctx) + return FALSE; + + if (buffer_type == BUFFER_TYPE_INPUT) { + buf_addr_table = client_ctx->input_buf_addr_table; + num_of_buffers = client_ctx->num_of_input_buffers; + DBG("%s(): buffer_type = INPUT \n", __func__); + + } else { + buf_addr_table = client_ctx->output_buf_addr_table; + num_of_buffers = client_ctx->num_of_output_buffers; + DBG("%s(): buffer_type = OUTPUT \n", __func__); + } + + for (i = 0; i < num_of_buffers; ++i) { + if (search_with_user_vaddr) { + if (*user_vaddr == buf_addr_table[i].user_vaddr) { + *kernel_vaddr = buf_addr_table[i].kernel_vaddr; + found = TRUE; + DBG("%s() : client_ctx = %p." + " user_virt_addr = 0x%08lx is found", + __func__, client_ctx, *user_vaddr); + break; + } + } else { + if (*kernel_vaddr == buf_addr_table[i].kernel_vaddr) { + *user_vaddr = buf_addr_table[i].user_vaddr; + found = TRUE; + DBG("%s() : client_ctx = %p." + " kernel_virt_addr = 0x%08lx is found", + __func__, client_ctx, *kernel_vaddr); + break; + } + } + } + + if (found) { + *phy_addr = buf_addr_table[i].phy_addr; + *pmem_fd = buf_addr_table[i].pmem_fd; + *file = buf_addr_table[i].file; + *buffer_index = i; + + if (search_with_user_vaddr) + DBG("kernel_vaddr = 0x%08lx, phy_addr = 0x%08lx " + " pmem_fd = %d, struct *file = %p " + "buffer_index = %d \n", *kernel_vaddr, + *phy_addr, *pmem_fd, *file, *buffer_index); + else + DBG("user_vaddr = 0x%08lx, phy_addr = 0x%08lx " + " pmem_fd = %d, struct *file = %p " + "buffer_index = %d \n", *user_vaddr, *phy_addr, + *pmem_fd, *file, *buffer_index); + return TRUE; + } else { + if (search_with_user_vaddr) + DBG("%s() : client_ctx = %p user_virt_addr = 0x%08lx" + " Not Found.\n", __func__, client_ctx, *user_vaddr); + else + DBG("%s() : client_ctx = %p kernel_virt_addr = 0x%08lx" + " Not Found.\n", __func__, client_ctx, + *kernel_vaddr); + return FALSE; + } +} +EXPORT_SYMBOL(vid_c_lookup_addr_table); + +u32 vid_c_timer_create(void (*pf_timer_handler)(void *), + void *p_user_data, void **pp_timer_handle) +{ + struct vid_c_timer *hw_timer = NULL; + if (!pf_timer_handler || !pp_timer_handle) { + DBG("%s(): timer creation failed \n ", __func__); + return FALSE; + } + hw_timer = kzalloc(sizeof(struct vid_c_timer), GFP_KERNEL); + if (!hw_timer) { + DBG("%s(): timer creation failed in allocation \n ", __func__); + return FALSE; + } + init_timer(&hw_timer->hw_timeout); + hw_timer->hw_timeout.data = (unsigned long)hw_timer; + hw_timer->hw_timeout.function = vid_c_timer_fn; + hw_timer->cb_func = pf_timer_handler; + hw_timer->userdata = p_user_data; + *pp_timer_handle = hw_timer; + return TRUE; +} +EXPORT_SYMBOL(vid_c_timer_create); + +void vid_c_timer_release(void *p_timer_handle) +{ + kfree(p_timer_handle); +} +EXPORT_SYMBOL(vid_c_timer_release); + +void vid_c_timer_start(void *p_timer_handle, u32 n_time_out) +{ + struct vid_c_timer *hw_timer = (struct vid_c_timer *)p_timer_handle; + DBG("%s(): start timer\n ", __func__); + if (hw_timer) { + hw_timer->hw_timeout.expires = jiffies + 1*HZ; + add_timer(&hw_timer->hw_timeout); + } +} +EXPORT_SYMBOL(vid_c_timer_start); + +void vid_c_timer_stop(void *p_timer_handle) +{ + struct vid_c_timer *hw_timer = (struct vid_c_timer *)p_timer_handle; + DBG("%s(): stop timer\n ", __func__); + if (hw_timer) + del_timer(&hw_timer->hw_timeout); +} +EXPORT_SYMBOL(vid_c_timer_stop); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Video decoder/encoder driver Init Module"); +MODULE_VERSION("1.0"); +module_init(vid_c_init); +module_exit(vid_c_exit); diff --git a/drivers/misc/video_core/720p/init/video_core_init.h b/drivers/misc/video_core/720p/init/video_core_init.h new file mode 100644 index 0000000000000..1fdc017ef4b63 --- /dev/null +++ b/drivers/misc/video_core/720p/init/video_core_init.h @@ -0,0 +1,98 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef VIDEO_CORE_INIT_H +#define VIDEO_CORE_INIT_H + +#include "video_core_type.h" + +#define MAX_VIDEO_NUM_OF_BUFF 100 + +enum buffer_dir { + BUFFER_TYPE_INPUT, + BUFFER_TYPE_OUTPUT +}; + +struct buf_addr_table { + unsigned long user_vaddr; + unsigned long kernel_vaddr; + unsigned long phy_addr; + int pmem_fd; + struct file *file; +}; + +struct video_client_ctx { + void *vcd_handle; + u32 num_of_input_buffers; + u32 num_of_output_buffers; + struct buf_addr_table input_buf_addr_table[MAX_VIDEO_NUM_OF_BUFF]; + struct buf_addr_table output_buf_addr_table[MAX_VIDEO_NUM_OF_BUFF]; + struct list_head msg_queue; + struct mutex msg_queue_lock; + wait_queue_head_t msg_wait; + struct completion event; + u32 event_status; + u32 seq_header_set; + u32 stop_msg; +}; + +void __iomem *vid_c_get_ioaddr(void); + +#ifdef USE_RES_TRACKER + +u32 vid_c_sel_clk_rate(unsigned long hclk_rate); +u32 vid_c_get_clk_rate(unsigned long *phclk_rate); +u32 vid_c_enable_clk(void); +u32 vid_c_disable_clk(void); +u32 vid_c_enable_pwr_rail(void); +u32 vid_c_disable_pwr_rail(void); + +#else + +u32 vid_c_enable_clk(unsigned long hclk_rate); +u32 vid_c_disable_clk(void); + +#endif + +int vid_c_load_firmware(void); +void vid_c_release_firmware(void); +u32 vid_c_lookup_addr_table(struct video_client_ctx *client_ctx, +enum buffer_dir buffer_type, u32 search_with_user_vaddr, +unsigned long *user_vaddr, unsigned long *kernel_vaddr, +unsigned long *phy_addr, int *pmem_fd, struct file **file, +s32 *buffer_index); + +u32 vid_c_timer_create(void (*pf_timer_handler)(void *), + void *p_user_data, void **pp_timer_handle); +void vid_c_timer_release(void *p_timer_handle); +void vid_c_timer_start(void *p_timer_handle, u32 n_time_out); +void vid_c_timer_stop(void *p_timer_handle); + + +#endif diff --git a/drivers/misc/video_core/720p/init/video_core_init_internal.h b/drivers/misc/video_core/720p/init/video_core_init_internal.h new file mode 100644 index 0000000000000..b72e9f26f811d --- /dev/null +++ b/drivers/misc/video_core/720p/init/video_core_init_internal.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef VIDEO_CORE_INIT_INTERNAL_H +#define VIDEO_CORE_INIT_INTERNAL_H + +#include + +struct vid_c_timer { + struct list_head list; + struct timer_list hw_timeout; + void (*cb_func)(void *); + void *userdata; +}; + +struct vid_c_dev { + struct cdev cdev; + struct device *device; + resource_size_t phys_base; + void __iomem *virt_base; + unsigned int irq; + struct clk *hclk; + struct clk *hclk_div2; + struct clk *pclk; + unsigned long hclk_rate; + unsigned int clock_enabled; + unsigned int rail_enabled; + unsigned int ref_count; + unsigned int firmware_refcount; + unsigned int get_firmware; + struct mutex lock; + s32 device_handle; + struct list_head vidc_timer_queue; + struct work_struct vidc_timer_worker; +}; + +#endif diff --git a/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.c b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.c new file mode 100644 index 0000000000000..40d4886347c1a --- /dev/null +++ b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.c @@ -0,0 +1,281 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" +#include "vcd_res_tracker.h" +#include "video_core_init.h" + +#include +#include + +#define MSM_AXI_QOS_NAME "msm_vidc_reg" + +#define QVGA_PERF_LEVEL (300 * 30) +#define VGA_PERF_LEVEL (1200 * 30) +#define WVGA_PERF_LEVEL (1500 * 30) + +static unsigned int mfc_clk_freq_table[3] = { + 61440000, 122880000, 170667000 +}; + +#ifndef CONFIG_MSM_NPA_SYSTEM_BUS +static unsigned int axi_clk_freq_table_enc[2] = { + 122880, 192000 +}; +static unsigned int axi_clk_freq_table_dec[2] = { + 122880, 192000 +}; +#else +static unsigned int axi_clk_freq_table_enc[2] = { + MSM_AXI_FLOW_VIDEO_RECORDING_720P, + MSM_AXI_FLOW_VIDEO_RECORDING_720P +}; +static unsigned int axi_clk_freq_table_dec[2] = { + MSM_AXI_FLOW_VIDEO_PLAYBACK_720P, + MSM_AXI_FLOW_VIDEO_PLAYBACK_720P +}; +#endif +static u32 res_trk_convert_freq_to_perf_lvl(u64 n_freq) +{ + u64 n_perf_lvl; + u64 n_temp; + + VCDRES_MSG_MED("\n %s():: n_freq = %u\n", __func__, (u32)n_freq); + + if (!n_freq) + return 0; + + n_temp = n_freq * 1000; + do_div(n_temp, VCD_RESTRK_HZ_PER_1000_PERFLVL); + n_perf_lvl = (u32)n_temp; + VCDRES_MSG_MED("\n %s(): n_perf_lvl = %u\n", __func__, + (u32)n_perf_lvl); + + return (u32)n_perf_lvl; +} + +static u32 res_trk_convert_perf_lvl_to_freq(u64 n_perf_lvl) +{ + u64 n_freq, n_temp; + + VCDRES_MSG_MED("\n %s():: n_perf_lvl = %u\n", __func__, + (u32)n_perf_lvl); + n_temp = (n_perf_lvl * VCD_RESTRK_HZ_PER_1000_PERFLVL) + 999; + do_div(n_temp, 1000); + n_freq = (u32)n_temp; + VCDRES_MSG_MED("\n %s(): n_freq = %u\n", __func__, (u32)n_freq); + + return (u32)n_freq; +} + +u32 res_trk_power_up(void) +{ + VCDRES_MSG_LOW("clk_regime_rail_enable"); + VCDRES_MSG_LOW("clk_regime_sel_rail_control"); +#ifdef AXI_CLK_SCALING +{ + int rc; + VCDRES_MSG_MED("\n res_trk_power_up():: " + "Calling AXI add requirement\n"); + rc = pm_qos_add_requirement(PM_QOS_SYSTEM_BUS_FREQ, + MSM_AXI_QOS_NAME, PM_QOS_DEFAULT_VALUE); + if (rc < 0) { + VCDRES_MSG_ERROR("Request AXI bus QOS fails. rc = %d\n", + rc); + return FALSE; + } +} +#endif + +#ifdef USE_RES_TRACKER + VCDRES_MSG_MED("\n res_trk_power_up():: Calling " + "vid_c_enable_pwr_rail()\n"); + return vid_c_enable_pwr_rail(); +#endif + return TRUE; +} + +u32 res_trk_power_down(void) +{ + VCDRES_MSG_LOW("clk_regime_rail_disable"); +#ifdef AXI_CLK_SCALING + VCDRES_MSG_MED("\n res_trk_power_down()::" + "Calling AXI remove requirement\n"); + pm_qos_remove_requirement(PM_QOS_SYSTEM_BUS_FREQ, + MSM_AXI_QOS_NAME); +#endif + +#ifdef USE_RES_TRACKER + VCDRES_MSG_MED("\n res_trk_power_down():: Calling " + "vid_c_disable_pwr_rail()\n"); + return vid_c_disable_pwr_rail(); +#endif + return TRUE; +} + +u32 res_trk_enable_clocks(void) +{ + VCDRES_MSG_LOW("clk_regime_msm_enable"); +#ifdef USE_RES_TRACKER + VCDRES_MSG_MED("\n res_trk_enable_clocks():: Calling " + "vid_c_enable_clk()\n"); + return vid_c_enable_clk(); +#endif + return TRUE; +} + +u32 res_trk_disable_clocks(void) +{ + VCDRES_MSG_LOW("clk_regime_msm_disable"); + +#ifdef USE_RES_TRACKER + VCDRES_MSG_MED("\n res_trk_disable_clocks():: Calling " + "vid_c_disable_clk()\n"); + return vid_c_disable_clk(); +#endif + return TRUE; +} + +u32 res_trk_get_max_perf_level(u32 *pn_max_perf_lvl) +{ + if (!pn_max_perf_lvl) { + VCDRES_MSG_ERROR("%s(): pn_max_perf_lvl is NULL\n", + __func__); + return FALSE; + } + + *pn_max_perf_lvl = VCD_RESTRK_MAX_PERF_LEVEL; + return TRUE; +} + +u32 res_trk_set_perf_level(u32 n_req_perf_lvl, u32 *pn_set_perf_lvl, + struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + u32 axi_freq = 0, mfc_freq = 0, calc_mfc_freq = 0; + int rc = -1; + + if (!pn_set_perf_lvl) { + VCDRES_MSG_ERROR("%s(): pn_perf_lvl is NULL\n", + __func__); + return FALSE; + } + + VCDRES_MSG_LOW("%s(), n_req_perf_lvl = %d", __func__, n_req_perf_lvl); + if (p_cctxt) { + calc_mfc_freq = res_trk_convert_perf_lvl_to_freq( + (u64)n_req_perf_lvl); + + if (calc_mfc_freq < VCD_RESTRK_MIN_FREQ_POINT) + calc_mfc_freq = VCD_RESTRK_MIN_FREQ_POINT; + else if (calc_mfc_freq > VCD_RESTRK_MAX_FREQ_POINT) + calc_mfc_freq = VCD_RESTRK_MAX_FREQ_POINT; + + if (!p_cctxt->b_decoding) { + if (n_req_perf_lvl >= VGA_PERF_LEVEL) { + mfc_freq = mfc_clk_freq_table[2]; + axi_freq = axi_clk_freq_table_enc[1]; + } else { + mfc_freq = mfc_clk_freq_table[0]; + axi_freq = axi_clk_freq_table_enc[0]; + } + VCDRES_MSG_HIGH("\n ENCODER: axi_freq = %u" + ", mfc_freq = %u, calc_mfc_freq = %u," + " n_req_perf_lvl = %u", axi_freq, + mfc_freq, calc_mfc_freq, + n_req_perf_lvl); + } else { + if (n_req_perf_lvl <= QVGA_PERF_LEVEL) { + mfc_freq = mfc_clk_freq_table[0]; + axi_freq = axi_clk_freq_table_dec[0]; + } else { + axi_freq = axi_clk_freq_table_dec[0]; + if (n_req_perf_lvl <= VGA_PERF_LEVEL) + mfc_freq = mfc_clk_freq_table[0]; + else if (n_req_perf_lvl <= WVGA_PERF_LEVEL) + mfc_freq = mfc_clk_freq_table[1]; + else { + mfc_freq = mfc_clk_freq_table[2]; + axi_freq = axi_clk_freq_table_dec[1]; + } + } + VCDRES_MSG_HIGH("\n DECODER: axi_freq = %u" + ", mfc_freq = %u, calc_mfc_freq = %u," + " n_req_perf_lvl = %u", axi_freq, + mfc_freq, calc_mfc_freq, + n_req_perf_lvl); + } + } else { + VCDRES_MSG_HIGH("%s() WARNING:: p_cctxt is NULL", __func__); + return TRUE; + } + +#ifdef AXI_CLK_SCALING + if (n_req_perf_lvl != VCD_RESTRK_MIN_PERF_LEVEL) { + VCDRES_MSG_HIGH("\n %s(): Setting AXI freq to %u", + __func__, axi_freq); + rc = pm_qos_update_requirement(PM_QOS_SYSTEM_BUS_FREQ, + MSM_AXI_QOS_NAME, axi_freq); + + if (rc < 0) { + VCDRES_MSG_ERROR("\n Update AXI bus QOS fails," + "rc = %d\n", rc); + return FALSE; + } + } +#endif + +#ifdef USE_RES_TRACKER + if (n_req_perf_lvl != VCD_RESTRK_MIN_PERF_LEVEL) { + VCDRES_MSG_HIGH("\n %s(): Setting MFC freq to %u", + __func__, mfc_freq); + if (!vid_c_sel_clk_rate(mfc_freq)) { + VCDRES_MSG_ERROR("%s(): vid_c_sel_clk_rate FAILED\n", + __func__); + *pn_set_perf_lvl = 0; + return FALSE; + } + } +#endif + + *pn_set_perf_lvl = + res_trk_convert_freq_to_perf_lvl((u64) mfc_freq); + return TRUE; +} + +u32 res_trk_get_curr_perf_level(u32 *pn_perf_lvl) +{ + unsigned long n_freq; + + if (!pn_perf_lvl) { + VCDRES_MSG_ERROR("%s(): pn_perf_lvl is NULL\n", + __func__); + return FALSE; + } + VCDRES_MSG_LOW("clk_regime_msm_get_clk_freq_hz"); + if (!vid_c_get_clk_rate(&n_freq)) { + VCDRES_MSG_ERROR("%s(): vid_c_get_clk_rate FAILED\n", + __func__); + *pn_perf_lvl = 0; + return FALSE; + } + + *pn_perf_lvl = res_trk_convert_freq_to_perf_lvl((u64) n_freq); + VCDRES_MSG_MED("%s(): n_freq = %lu, *pn_perf_lvl = %u", n_freq, + *pn_perf_lvl); + return TRUE; +} diff --git a/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.h b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.h new file mode 100644 index 0000000000000..8e3093f098f18 --- /dev/null +++ b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VIDEO_720P_RESOURCE_TRACKER_H_ +#define _VIDEO_720P_RESOURCE_TRACKER_H_ + +#include "vcd_res_tracker_api.h" + +#define VCD_RESTRK_MIN_PERF_LEVEL 37900 +#define VCD_RESTRK_MAX_PERF_LEVEL 108000 +#define VCD_RESTRK_MIN_FREQ_POINT 61440000 +#define VCD_RESTRK_MAX_FREQ_POINT 170667000 +#define VCD_RESTRK_HZ_PER_1000_PERFLVL 1580250 + +#if DEBUG + +#define VCDRES_MSG_LOW(xx_fmt...) printk(KERN_INFO "\n\t* " xx_fmt) +#define VCDRES_MSG_MED(xx_fmt...) printk(KERN_INFO "\n * " xx_fmt) + +#else + +#define VCDRES_MSG_LOW(xx_fmt...) +#define VCDRES_MSG_MED(xx_fmt...) + +#endif + +#define VCDRES_MSG_HIGH(xx_fmt...) printk(KERN_WARNING "\n" xx_fmt) +#define VCDRES_MSG_ERROR(xx_fmt...) printk(KERN_ERR "\n err: " xx_fmt) +#define VCDRES_MSG_FATAL(xx_fmt...) printk(KERN_ERR "\n " xx_fmt) + +#endif diff --git a/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker_api.h b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker_api.h new file mode 100644 index 0000000000000..6e93ef9bb6382 --- /dev/null +++ b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker_api.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VIDEO_720P_RESOURCE_TRACKER_API_H_ +#define _VIDEO_720P_RESOURCE_TRACKER_API_H_ + +#include "vcd_core.h" + +u32 res_trk_power_up(void); +u32 res_trk_power_down(void); +u32 res_trk_enable_clocks(void); +u32 res_trk_disable_clocks(void); +u32 res_trk_get_max_perf_level(u32 *pn_max_perf_lvl); +u32 res_trk_set_perf_level(u32 n_req_perf_lvl, u32 *pn_set_perf_lvl, + struct vcd_clnt_ctxt_type_t *p_cctxt); +u32 res_trk_get_curr_perf_level(u32 *pn_perf_lvl); + +#endif diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.c b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.c new file mode 100644 index 0000000000000..c824e045bf077 --- /dev/null +++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.c @@ -0,0 +1,1247 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" + +#include "vid_frame_scheduler_api.h" +#include "vid_frame_scheduler.h" + +static const u32 SCHED_TKNBKT_SIZE_FACTOR = 5; +static const u32 SCHED_TKNBKT_FILL_NORMLZ_SCALE = 100; +static const u32 SCHED_TIME_MAX = 0xffffffff; + + +SCHED_INLINE u32 SCHED_SUCCEEDED(enum sched_status_type status) +{ + SCHED_MSG_LOW("SCHED_SUCCEEDED check: status = %d", status); + + if (status == SCHED_S_OK) + return TRUE; + else + return FALSE; + +} + +SCHED_INLINE u32 SCHED_FAILED(enum sched_status_type status) +{ + SCHED_MSG_LOW("SCHED_FAILED check: status = %d", status); + + if (status >= SCHED_S_EFAIL) + return TRUE; + else + return FALSE; + +} + +static void sched_clear_clnt_ctx(struct sched_clnt_ctx_type *p_ctx) +{ + if (p_ctx->a_clnt_frm_q) + SCHED_FREE(p_ctx->a_clnt_frm_q); + (void)SCHED_CRITSEC_RELEASE(p_ctx->clnt_cs); +} + +SCHED_INLINE void sched_free_clnt_node( + struct _sched_clnt_list_node_type *p_clnt_node) +{ + sched_clear_clnt_ctx(&p_clnt_node->data); + SCHED_FREE(p_clnt_node); + +} + +enum sched_status_type sched_clear_clnt_list( + struct _sched_clnt_list_node_type *p_clnt_lst) { + struct _sched_clnt_list_node_type *p_clnt_node; + + while (p_clnt_lst) { + (void)SCHED_CRITSEC_ENTER(p_clnt_lst->data.clnt_cs); + p_clnt_node = p_clnt_lst; + p_clnt_lst = p_clnt_lst->p_next; + sched_free_clnt_node(p_clnt_node); + } + return SCHED_S_OK; +} + +static SCHED_INLINE enum sched_status_type sched_alloc_frm_q( + struct sched_clnt_ctx_type *p_ctx) +{ + p_ctx->a_clnt_frm_q = (struct sched_clnt_q_elem *) + SCHED_MALLOC(sizeof(struct sched_clnt_q_elem) * + p_ctx->n_max_queue_len); + + if (!p_ctx->a_clnt_frm_q) { + SCHED_MSG_ERR("Could not allocate clnt frm Q. Out of memory"); + return SCHED_S_ENOMEM; + } + + SCHED_MEMSET(p_ctx->a_clnt_frm_q, + 0, sizeof(struct sched_clnt_q_elem) * p_ctx->n_max_queue_len); + p_ctx->n_q_head = 0; + p_ctx->n_q_tail = -1; + p_ctx->n_q_len = 0; + SCHED_MSG_MED("Clnt frm Q allocted & initialized"); + return SCHED_S_OK; + +} + +static SCHED_INLINE void sched_de_q_head_frm + (struct sched_clnt_ctx_type *p_ctx, + struct sched_clnt_q_elem *p_q_elem) { + *p_q_elem = p_ctx->a_clnt_frm_q[p_ctx->n_q_head]; + + memset(&p_ctx->a_clnt_frm_q[p_ctx->n_q_head], 0, + sizeof(struct sched_clnt_q_elem)); + + /*Update the circular queue head index.*/ + p_ctx->n_q_head = (p_ctx->n_q_head + 1) % p_ctx->n_max_queue_len; + p_ctx->n_q_len--; +} + +static SCHED_INLINE void sched_tkn_bkt_fill_normalize + (struct sched_clnt_ctx_type *p_ctx) +{ + p_ctx->n_bkt_curr_tkns_nmlzd = + (p_ctx->n_bkt_curr_tkns * SCHED_TKNBKT_FILL_NORMLZ_SCALE) / + p_ctx->n_p_tkn_per_frm; +} + +static void sched_tkn_bkt_config(struct sched_clnt_ctx_type *p_ctx) +{ + p_ctx->n_bkt_size = p_ctx->n_p_tkn_per_frm * SCHED_TKNBKT_SIZE_FACTOR; + p_ctx->n_bkt_quies_cap = p_ctx->n_bkt_size; + p_ctx->n_bkt_curr_tkns = + SCHED_MIN(p_ctx->n_bkt_curr_tkns, p_ctx->n_bkt_size); +} + +static void sched_tkn_bkt_supply( + struct sched_clnt_ctx_type *p_ctx, u32 n_curr_time) +{ + u32 n_delta; + u32 n_num_tkns; + + /*Check if there's time wrap-around since last token supply time.*/ + if (n_curr_time < p_ctx->n_bkt_lst_sup_time) { + SCHED_MSG_HIGH("Current time wrap around detected"); + n_delta = + SCHED_TIME_MAX - p_ctx->n_bkt_lst_sup_time + n_curr_time; + } else + n_delta = n_curr_time - p_ctx->n_bkt_lst_sup_time; + + /*Proceed only if there is any time elapsed since our last supply + time.*/ + if (n_delta > 0) { + /*Calculate the number of tokens that we can supply based on + time elapsed and the client's token supply rate.*/ + n_num_tkns = n_delta * p_ctx->n_curr_p_tkn_rate / 1000; + + if (n_num_tkns > 0) { + p_ctx->n_bkt_curr_tkns = SCHED_MIN(p_ctx->n_bkt_size, + p_ctx->n_bkt_curr_tkns + n_num_tkns); + + if ((n_delta * p_ctx->n_curr_p_tkn_rate % 1000)) { + n_delta = (n_num_tkns * 1000 + + (p_ctx->n_curr_p_tkn_rate >> 1)) + / p_ctx->n_curr_p_tkn_rate; + if ((SCHED_TIME_MAX - + p_ctx->n_bkt_lst_sup_time) < n_delta) { + SCHED_MSG_HIGH + ("Handling for current time wrap " + "around"); + + p_ctx->n_bkt_lst_sup_time = n_delta - + (SCHED_TIME_MAX - + p_ctx->n_bkt_lst_sup_time); + } else + p_ctx->n_bkt_lst_sup_time += n_delta; + } else + p_ctx->n_bkt_lst_sup_time = n_curr_time; + + if (p_ctx->n_bkt_curr_tkns > + (s32) p_ctx->n_bkt_quies_cap) { + SCHED_MSG_HIGH + ("Client Quiesence detected. Capping " + "n_bkt_curr_tkns"); + p_ctx->n_bkt_curr_tkns = p_ctx->n_p_tkn_per_frm; + } + sched_tkn_bkt_fill_normalize(p_ctx); + } + } +} + +static SCHED_INLINE void sched_tkn_bkt_consume( + struct sched_clnt_ctx_type *p_ctx) { + p_ctx->n_bkt_curr_tkns -= p_ctx->n_p_tkn_per_frm; +} + +static SCHED_INLINE u32 sched_clnt_frm_is_cnfmnt + (struct sched_clnt_ctx_type *p_ctx) +{ + if (p_ctx->n_bkt_curr_tkns >= (s32) p_ctx->n_p_tkn_per_frm) + return TRUE; + else + return FALSE; +} /* end of sched_clnt_frm_is_conformant */ + +static struct sched_clnt_ctx_type *sched_elect_cnfmnt + (struct sched_clnt_ctx_type *p_prov_elect, + struct sched_clnt_ctx_type *p_new_cand) { + + /*If there is no provisional elect client then the new candidate + becomes the first one.*/ + if (!p_prov_elect) + return p_new_cand; + + + /*Here we want to pick the client who has accumulated the most tokens + from the time of attaining single frame conformance. + Since we are comparing between clients we use the available normalized + token bucket occupancy value.*/ + if (p_prov_elect->n_bkt_curr_tkns_nmlzd >= + p_new_cand->n_bkt_curr_tkns_nmlzd) { + return p_prov_elect; + } else { + /*We had held on to this provisional elect conformant + client critical section. Since new candidate has won the + election leave critical section of earlier provisional + elect. + */ + (void)SCHED_CRITSEC_LEAVE(p_prov_elect->clnt_cs); + return p_new_cand; + } +} + +static struct sched_clnt_ctx_type *sched_elect_non_cnfmnt + (struct sched_clnt_ctx_type *p_prov_elect, + struct sched_clnt_ctx_type *p_new_cand) { + + /*If there is no provisional elect client then the new candidate + becomes the first one.*/ + if (!p_prov_elect) + return p_new_cand; + /*Here we want to pick the client who is closest to attaining a single + frame conformance. + Since we are comparing between clients we use the available + normalized token bucket occupancy value. + Also if the provisional elect or the new contender (in that order) + have an end of frame marker set we give it priority over deciding + by frame conformance method mentiond earlier.*/ + if (p_prov_elect->n_eof_marker > 0) { + return p_prov_elect; + } else if (p_new_cand->n_eof_marker > 0) { + /*We had held on to this provisional elect non conformant client + critical section. Since new candidate has won the election + leave critical section of earlier provisional elect. + */ + (void)SCHED_CRITSEC_LEAVE(p_prov_elect->clnt_cs); + + return p_new_cand; + } else if (p_prov_elect->n_bkt_curr_tkns_nmlzd >= + p_new_cand->n_bkt_curr_tkns_nmlzd) { + return p_prov_elect; + } else { + /*Similar to above case leave critical section of earlier + provisional elect.*/ + (void)SCHED_CRITSEC_LEAVE(p_prov_elect->clnt_cs); + return p_new_cand; + } + +} + +static struct sched_clnt_ctx_type *sched_elect_non_rt + (struct sched_ctx_type *p_sched_ctx) { + struct _sched_clnt_list_node_type *p_node = NULL; + struct _sched_clnt_list_node_type *p_start_node = NULL; + u32 b_found = FALSE; + + /*For non real time clients we are using a round robin election + algorithm. + Based on the last scheduled client we find the next to schedule + and return its context. + We also need to skip the client if certain conditions (mentioned below) + are not met*/ + if (!p_sched_ctx->p_n_rt_last_sched) + p_start_node = p_node = p_sched_ctx->p_n_rt_head; + else { + if (!p_sched_ctx->p_n_rt_last_sched->p_next) + p_start_node = p_sched_ctx->p_n_rt_head; + else + p_start_node = p_sched_ctx->p_n_rt_last_sched->p_next; + + p_node = p_start_node; + } + + do { + + (void)SCHED_CRITSEC_ENTER(p_node->data.clnt_cs); + + /*Check if the client can be considered for this round of scheduling.*/ + if (sched_consider_clnt_for_sched(&p_node->data)) { + b_found = TRUE; + p_sched_ctx->p_n_rt_last_sched = p_node; + } + + /*If this client is not the election winner then leave its critical + section. + If we have found a winner we want to hold on to its critical + section. We would leave its critical section after we are done + with dequeueing a frame from the client context.*/ + if (!b_found) + (void)SCHED_CRITSEC_LEAVE(p_node->data.clnt_cs); + + if (!p_node->p_next) + p_node = p_sched_ctx->p_n_rt_head; + else + p_node = p_node->p_next; + + } while (p_node != p_start_node); + + if (b_found) { + SCHED_MSG_LOW("Non real time client selected"); + + return &p_sched_ctx->p_n_rt_last_sched->data; + } else { + SCHED_MSG_MED + ("No non-real time client available for scheduling"); + + return NULL; + } + +} + +static enum sched_status_type sched_process_set_p_tkn_rate( + struct sched_ctx_type *p_sched_ctx, + struct sched_clnt_ctx_type *p_clnt_ctx, + union sched_value_type *p_param_value) { + u32 n_curr_time = 0; + + if (p_param_value->un_value == p_clnt_ctx->n_curr_p_tkn_rate) + return SCHED_S_OK; + + + if ((p_sched_ctx->n_total_clnt_bw - p_clnt_ctx->n_curr_p_tkn_rate + + p_param_value->un_value) > p_sched_ctx->n_perf_lvl) { + SCHED_MSG_HIGH + ("Perf level insufficient for requested P Tkn rate"); + + } + + /*Get current time. We need this for token supply. + If we didn't get a valid current time value just return*/ + if (SCHED_FAILED(SCHED_GET_CURRENT_TIME(&n_curr_time))) { + SCHED_MSG_ERR("Get current time failed"); + + return SCHED_S_EFAIL; + } + + /*Before we go ahead and update the Current p_tkn rate, we fill + the token bucket upto current time instance.*/ + sched_tkn_bkt_supply(p_clnt_ctx, n_curr_time); + + /*Next, update the current value of total client bandwidth with + the new p_tkn rate of the client.*/ + p_sched_ctx->n_total_clnt_bw = p_sched_ctx->n_total_clnt_bw - + p_clnt_ctx->n_curr_p_tkn_rate + p_param_value->un_value; + p_clnt_ctx->n_curr_p_tkn_rate = p_param_value->un_value; + + /*Since the current Ptkn rate (i.e. current alloted bandwidth) + of the client has changed we need to update client's token + bucket configuration*/ + sched_tkn_bkt_config(p_clnt_ctx); + return SCHED_S_OK; +} + +static enum sched_status_type sched_process_add_rt_clnt( + struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node) { + enum sched_status_type status; + struct sched_clnt_ctx_type *p_clnt_ctx = &p_clnt_node->data; + struct _sched_clnt_list_node_type *p_tmp_node; + + /*Validate real time client specific parameters.*/ + if (!p_clnt_ctx->n_curr_p_tkn_rate) + SCHED_MSG_HIGH("Allocated token rate is zero"); + + /*Check if our performance level setting can sustain the new client*/ + if (p_sched_ctx->n_total_clnt_bw + p_clnt_ctx->n_curr_p_tkn_rate > + p_sched_ctx->n_perf_lvl) { + SCHED_MSG_HIGH("Not enough bandwidth to support client"); + SCHED_MSG_HIGH + ("curr_perflvl=%d, curr_bw=%d, newclnt_ptknrate=%d", + p_sched_ctx->n_perf_lvl, p_sched_ctx->n_total_clnt_bw, + p_clnt_ctx->n_curr_p_tkn_rate); + + } + /*Allocate the client frame queue*/ + status = sched_alloc_frm_q(p_clnt_ctx); + + if (SCHED_SUCCEEDED(status)) { + /*Allocate the token bucket*/ + sched_tkn_bkt_config(p_clnt_ctx); + /*We start with empty token bucket*/ + p_clnt_ctx->n_bkt_curr_tkns = 0; + p_clnt_ctx->n_bkt_curr_tkns_nmlzd = 0; + /*Add the client to the real time client list and increase the + total client bandwidth.*/ + p_tmp_node = p_sched_ctx->p_rt_head; + p_sched_ctx->p_rt_head = p_clnt_node; + p_sched_ctx->p_rt_head->p_next = p_tmp_node; + p_sched_ctx->n_rt_clnts++; + p_sched_ctx->n_total_clnt_bw += p_clnt_ctx->n_curr_p_tkn_rate; + } + return status; +} + +static enum sched_status_type sched_process_add_non_rt_clnt( + struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node) { + enum sched_status_type status; + struct sched_clnt_ctx_type *p_clnt_ctx = &p_clnt_node->data; + struct _sched_clnt_list_node_type *p_tmp_node; + + /*Allocate the client frame queue*/ + status = sched_alloc_frm_q(p_clnt_ctx); + if (SCHED_SUCCEEDED(status)) { + /*Add the client to the real time client list and increase the + total client bandwidth.*/ + p_tmp_node = p_sched_ctx->p_n_rt_head; + p_sched_ctx->p_n_rt_head = p_clnt_node; + p_sched_ctx->p_n_rt_head->p_next = p_tmp_node; + p_sched_ctx->n_n_rt_clnts++; + } + return status; +} + +enum sched_status_type sched_process_add_clnt( + struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node, + struct sched_client_init_param_type *p_init_param) { + enum sched_status_type status = SCHED_S_OK; + + SCHED_MEMSET(p_clnt_node, 0, sizeof(struct _sched_clnt_list_node_type)); + + /*Validate all initialization parameters*/ + if (!p_init_param->n_p_tkn_per_frm || + !p_init_param->frm_rate.n_numer || + !p_init_param->frm_rate.n_denom || + !p_init_param->n_max_queue_len || + !p_init_param->n_o_tkn_max || + !p_init_param->n_o_tkn_per_ip_frm || + p_init_param->n_o_tkn_init > p_init_param->n_o_tkn_max || + p_init_param->n_o_tkn_per_ip_frm > p_init_param->n_o_tkn_max) { + SCHED_MSG_ERR("Bad initialization parameters"); + return SCHED_S_EBADPARM; + } + + /*Store all initialization parameters*/ + p_clnt_node->data.client_ctgy = p_init_param->client_ctgy; + p_clnt_node->data.n_curr_p_tkn_rate = p_init_param->n_alloc_p_tkn_rate; + p_clnt_node->data.frm_rate = p_init_param->frm_rate; + p_clnt_node->data.n_max_queue_len = p_init_param->n_max_queue_len; + p_clnt_node->data.n_o_tkn_max = p_init_param->n_o_tkn_max; + p_clnt_node->data.n_o_tkn_per_ip_frm = p_init_param->n_o_tkn_per_ip_frm; + p_clnt_node->data.n_curr_o_tkns = p_init_param->n_o_tkn_init; + p_clnt_node->data.n_p_tkn_per_frm = p_init_param->n_p_tkn_per_frm; + p_clnt_node->data.p_client_data = p_init_param->p_client_data; + p_clnt_node->data.b_sched_state = TRUE; + + SCHED_MSG_HIGH("Adding new client of category %d", + p_clnt_node->data.client_ctgy); + SCHED_MSG_MED("Allocated P token rate (per sec) = %d", + p_clnt_node->data.n_curr_p_tkn_rate); + SCHED_MSG_MED("Frame rate = %d / %d", + p_clnt_node->data.frm_rate.n_numer, + p_clnt_node->data.frm_rate.n_denom); + SCHED_MSG_MED("Max_queue_len = %d", p_clnt_node->data.n_max_queue_len); + SCHED_MSG_MED("Max O tokens = %d", p_clnt_node->data.n_o_tkn_max); + SCHED_MSG_MED("O tokens threshold = %d", + p_clnt_node->data.n_o_tkn_per_ip_frm); + SCHED_MSG_MED("P tokens per frame = %d", + p_clnt_node->data.n_p_tkn_per_frm); + SCHED_MSG_MED("Client data ptr = %p", p_clnt_node->data.p_client_data); + + if (SCHED_FAILED(SCHED_CRITSEC_CREATE(&p_clnt_node->data.clnt_cs))) + return SCHED_S_EFAIL; + + /*Configure the client context based on client category.*/ + switch (p_clnt_node->data.client_ctgy) { + case SCHED_CLNT_RT_BUFF: + case SCHED_CLNT_RT_NOBUFF: + { + status = + sched_process_add_rt_clnt(p_sched_ctx, p_clnt_node); + break; + } + + case SCHED_CLNT_NONRT: + { + status = + sched_process_add_non_rt_clnt(p_sched_ctx, + p_clnt_node); + break; + } + + default: + { + status = SCHED_S_EBADPARM; + break; + } + + } + return status; +} + +enum sched_status_type sched_process_remove_clnt( + struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node) { + + (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); + + /*Handling if the client frame queue is not empty. Just return + and let Codec driver dequeue all frames for this client + before calling remove client*/ + if (p_clnt_node->data.n_q_len) { + SCHED_MSG_ERR("Cannot remove client. Queue is not empty"); + return SCHED_S_EINVALST; + } + + /*Based on client category, remove the client node from the + appropriate scheduler client list*/ + switch (p_clnt_node->data.client_ctgy) { + case SCHED_CLNT_RT_BUFF: + case SCHED_CLNT_RT_NOBUFF: + { + + sched_remove_node_from_list(&p_sched_ctx->p_rt_head, + p_clnt_node); + p_sched_ctx->n_rt_clnts--; + p_sched_ctx->n_total_clnt_bw -= + p_clnt_node->data.n_curr_p_tkn_rate; + break; + } + + case SCHED_CLNT_NONRT: + { + sched_remove_node_from_list(&p_sched_ctx->p_n_rt_head, + p_clnt_node); + p_sched_ctx->n_n_rt_clnts--; + break; + } + + default: + { + SCHED_ASSERT(0); + break; + } + } + + /*Now that client node is off the scheduler client list free up + resources that its been using.*/ + SCHED_MSG_HIGH("Removing new client of category %d", + p_clnt_node->data.client_ctgy); + SCHED_MSG_MED("Allocated P token rate (per sec) = %d", + p_clnt_node->data.n_curr_p_tkn_rate); + SCHED_MSG_MED("Frame rate = %d / %d", + p_clnt_node->data.frm_rate.n_numer, + p_clnt_node->data.frm_rate.n_denom); + SCHED_MSG_MED("Max_queue_len = %d", p_clnt_node->data.n_max_queue_len); + SCHED_MSG_MED("Max O tokens = %d", p_clnt_node->data.n_o_tkn_max); + SCHED_MSG_MED("P tokens per frame = %d", + p_clnt_node->data.n_p_tkn_per_frm); + SCHED_MSG_MED("Client data ptr = %p", p_clnt_node->data.p_client_data); + sched_free_clnt_node(p_clnt_node); + return SCHED_S_OK; +} + +enum sched_status_type sched_process_flush_clnt_buff( + struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node, void **pp_frm_data) { + struct sched_clnt_ctx_type *p_clnt_ctx; + enum sched_status_type status = SCHED_S_OK; + struct sched_clnt_q_elem q_elem; + + p_clnt_ctx = &p_clnt_node->data; + + /*If the client queue is empty just return an QEMPTY status*/ + if (!p_clnt_ctx->n_q_len) { + status = SCHED_S_QEMPTY; + } else { + p_clnt_ctx->b_flushing = TRUE; + + /*If the client queue is not empty just remove and return the + element at the front of the queue.*/ + sched_de_q_head_frm(p_clnt_ctx, &q_elem); + *pp_frm_data = q_elem.p_frm_data; + } + + /*If the Queue was orginially empty OR if it got empty after latest + De_queue we reset the flushing and First_frame flags. + Token bucket contents are also emptied.Queue pointers are reset. + o_tkns are restored.*/ + if (!p_clnt_ctx->n_q_len) { + p_clnt_ctx->b_flushing = FALSE; + p_clnt_ctx->b_first_frm = FALSE; + p_clnt_ctx->n_bkt_curr_tkns = 0; + p_clnt_ctx->n_bkt_curr_tkns_nmlzd = 0; + p_clnt_ctx->n_bkt_lst_sup_time = 0; + p_clnt_ctx->n_q_head = 0; + p_clnt_ctx->n_q_tail = -1; + SCHED_MSG_HIGH + ("Client flushed and re-initialized. Client category %d", + p_clnt_ctx->client_ctgy); + SCHED_MSG_MED("Client allocated P token rate (per sec) = %d", + p_clnt_ctx->n_curr_p_tkn_rate); + SCHED_MSG_MED("Client frame rate = %d / %d", + p_clnt_ctx->frm_rate.n_numer, + p_clnt_ctx->frm_rate.n_denom); + SCHED_MSG_MED("Client P tokens per frame = %d", + p_clnt_ctx->n_p_tkn_per_frm); + } + return status; +} + +SCHED_INLINE enum sched_status_type sched_process_mark_clnt_eof( + struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node) { + + if (!p_clnt_node->data.n_q_len) + return SCHED_S_QEMPTY; + + + if (!p_clnt_node->data.a_clnt_frm_q[p_clnt_node->data.n_q_tail].b_eof) { + /*Just increment the EOF marker count in the client context.*/ + p_clnt_node->data.n_eof_marker++; + p_clnt_node->data.a_clnt_frm_q[p_clnt_node->data.n_q_tail]. + b_eof = TRUE; + } else + SCHED_MSG_HIGH("Current frame is already marked EOF"); + + SCHED_MSG_HIGH("Client marked for end of frames. Client category %d", + p_clnt_node->data.client_ctgy); + SCHED_MSG_MED("Client allocated P token rate (per sec) = %d", + p_clnt_node->data.n_curr_p_tkn_rate); + SCHED_MSG_MED("Client frame rate = %d / %d", + p_clnt_node->data.frm_rate.n_numer, + p_clnt_node->data.frm_rate.n_denom); + SCHED_MSG_MED("Client P tokens per frame = %d", + p_clnt_node->data.n_p_tkn_per_frm); + return SCHED_S_OK; +} + +enum sched_status_type sched_process_update_clnt_o_tkn( + struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node, + u32 b_type, u32 n_o_tkn) { + + /*Act based on the type of update.*/ + + if (b_type) { + /*Just replenish the output tokens the client currently has with + the provided number while not going over the max value.*/ + p_clnt_node->data.n_curr_o_tkns = + SCHED_MIN(p_clnt_node->data.n_curr_o_tkns + n_o_tkn, + p_clnt_node->data.n_o_tkn_max); + } else { + /*Just subtract the give number of output tokens from the count + the client currently has while not going less than 0.*/ + if (n_o_tkn >= p_clnt_node->data.n_curr_o_tkns) + p_clnt_node->data.n_curr_o_tkns = 0; + else + p_clnt_node->data.n_curr_o_tkns -= n_o_tkn; + + } + + SCHED_MSG_LOW("%d O tokens restored for client", n_o_tkn); + SCHED_MSG_LOW("Client Curr_o_tkns = %d", + p_clnt_node->data.n_curr_o_tkns); + SCHED_MSG_LOW("Client category = %d", p_clnt_node->data.client_ctgy); + SCHED_MSG_LOW("Client allocated P token rate (per sec) = %d", + p_clnt_node->data.n_curr_p_tkn_rate); + SCHED_MSG_LOW("Client frame rate = %d / %d", + p_clnt_node->data.frm_rate.n_numer, + p_clnt_node->data.frm_rate.n_denom); + SCHED_MSG_LOW("Client P tokens per frame = %d", + p_clnt_node->data.n_p_tkn_per_frm); + return SCHED_S_OK; +} + +enum sched_status_type sched_process_en_q_frm( + struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node, void *p_frm_data) { + struct sched_clnt_ctx_type *p_clnt_ctx; + u32 n_curr_time = 0; + + p_clnt_ctx = &p_clnt_node->data; + + /*Check if the client queue is full already*/ + if (p_clnt_ctx->n_q_len == p_clnt_ctx->n_max_queue_len) { + SCHED_MSG_HIGH("Cannot enqueue. Client queue is full"); + + return SCHED_S_QFULL; + } + + /*Check if the client queue is being flushed.*/ + if (p_clnt_ctx->b_flushing) { + SCHED_MSG_ERR("Cannot enqueue. Client queue is being flushed"); + + return SCHED_S_EINVALST; + } + + /*Reposition tail, increase Q length and add the frame data to Q*/ + p_clnt_ctx->n_q_tail = + (p_clnt_ctx->n_q_tail + 1) % p_clnt_ctx->n_max_queue_len; + + p_clnt_ctx->n_q_len++; + + p_clnt_ctx->a_clnt_frm_q[p_clnt_ctx->n_q_tail].p_frm_data = p_frm_data; + p_clnt_ctx->a_clnt_frm_q[p_clnt_ctx->n_q_tail].b_eof = FALSE; + + /*If this is the first frame being queued for this client then, + get current time. We now start the token supply clock for the client. + Supply tokens required for a single frame processing while storing + the current time as the last supply time and marking that first + frame is received.*/ + if (!p_clnt_ctx->b_first_frm) { + SCHED_MSG_HIGH("Client first frame enqueued"); + if (p_clnt_ctx->client_ctgy != SCHED_CLNT_NONRT) { + if (SCHED_SUCCEEDED + (SCHED_GET_CURRENT_TIME(&n_curr_time))) { + p_clnt_ctx->n_bkt_curr_tkns = + p_clnt_ctx->n_p_tkn_per_frm; + p_clnt_ctx->n_bkt_lst_sup_time = n_curr_time; + p_clnt_ctx->b_first_frm = TRUE; + } + } else + p_clnt_ctx->b_first_frm = TRUE; + } + + SCHED_MSG_LOW("Client frame enqueued. Queue fill status = %d / %d", + p_clnt_ctx->n_q_len, p_clnt_ctx->n_max_queue_len); + SCHED_MSG_LOW("Client category = %d", p_clnt_ctx->client_ctgy); + SCHED_MSG_LOW("Client allocated P token rate (per sec) = %d", + p_clnt_ctx->n_curr_p_tkn_rate); + SCHED_MSG_LOW("Client frame rate = %d / %d", + p_clnt_ctx->frm_rate.n_numer, + p_clnt_ctx->frm_rate.n_denom); + SCHED_MSG_LOW("Client P tokens per frame = %d", + p_clnt_ctx->n_p_tkn_per_frm); + + return SCHED_S_OK; + +} + +enum sched_status_type sched_process_re_en_q_frm( + struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node, + void *p_frm_data) +{ + struct sched_clnt_ctx_type *p_clnt_ctx; + u32 n_curr_time = 0; + + p_clnt_ctx = &p_clnt_node->data; + + if (p_clnt_ctx->n_q_len == p_clnt_ctx->n_max_queue_len) { + SCHED_MSG_ERR("Cannot re-enqueue. Client queue is full"); + return SCHED_S_QFULL; + } + + if (p_clnt_ctx->b_flushing) { + SCHED_MSG_ERR("Cannot re-enqueue. Client" + " queue is being flushed"); + return SCHED_S_EINVALST; + } + + p_clnt_ctx->n_q_head = + (p_clnt_ctx->n_q_head + p_clnt_ctx->n_max_queue_len - 1) % + p_clnt_ctx->n_max_queue_len; + + p_clnt_ctx->n_q_len++; + + p_clnt_ctx->a_clnt_frm_q[p_clnt_ctx->n_q_head].p_frm_data = + p_frm_data; + p_clnt_ctx->a_clnt_frm_q[p_clnt_ctx->n_q_head].b_eof = + FALSE; + + if (p_clnt_ctx->client_ctgy != SCHED_CLNT_NONRT) { + if (!p_clnt_ctx->b_first_frm) { + SCHED_MSG_HIGH("Client frame " + "re-enqueued as first frame"); + if (SCHED_SUCCEEDED + (SCHED_GET_CURRENT_TIME(&n_curr_time))) { + p_clnt_ctx->n_bkt_curr_tkns = + p_clnt_ctx->n_p_tkn_per_frm; + p_clnt_ctx->n_bkt_lst_sup_time = + n_curr_time; + p_clnt_ctx->b_first_frm = + TRUE; + } + } else + p_clnt_ctx->n_bkt_curr_tkns += + p_clnt_ctx->n_p_tkn_per_frm; + } else + p_clnt_ctx->b_first_frm = TRUE; + + + SCHED_MSG_LOW("Client frame re-enqueued. Queue fill status = %d / %d", + p_clnt_ctx->n_q_len, p_clnt_ctx->n_max_queue_len); + SCHED_MSG_LOW("Client category = %d", p_clnt_ctx->client_ctgy); + SCHED_MSG_LOW("Client allocated P token rate (per sec) = %d", + p_clnt_ctx->n_curr_p_tkn_rate); + SCHED_MSG_LOW("Client frame rate = %d / %d", + p_clnt_ctx->frm_rate.n_numer, + p_clnt_ctx->frm_rate.n_denom); + SCHED_MSG_LOW("Client P tokens per frame = %d", + p_clnt_ctx->n_p_tkn_per_frm); + + return SCHED_S_OK; + +} + +enum sched_status_type sched_process_de_q_frm_rt_clnt( + struct sched_ctx_type *p_sched_ctx, + struct sched_clnt_ctx_type **pp_conf_elect_ctx, + struct sched_clnt_ctx_type **pp_non_conf_elect_ctx) { + u32 n_curr_time = 0; + struct _sched_clnt_list_node_type *p_clnt_node; + struct sched_clnt_ctx_type *p_clnt_ctx; + + *pp_conf_elect_ctx = NULL; + *pp_non_conf_elect_ctx = NULL; + + /*Get current time. We need this for token supply. + If we didn't get a valid current time value just return*/ + if (SCHED_FAILED(SCHED_GET_CURRENT_TIME(&n_curr_time))) { + SCHED_MSG_ERR("Get current time failed"); + + return SCHED_S_EFAIL; + } + + /*Run through the list of real time clients. + Consider only the clients that have queued atleast one frame since + being admitted into the scheduler. + Supply tokens equivalent to elapsed time since last supply. + Also in this same pass, check if each client has a conformant + frame or not.*/ + p_clnt_node = p_sched_ctx->p_rt_head; + while (p_clnt_node) { + p_clnt_ctx = &p_clnt_node->data; + + (void)SCHED_CRITSEC_ENTER(p_clnt_ctx->clnt_cs); + + if (sched_consider_clnt_for_sched(p_clnt_ctx)) { + sched_tkn_bkt_supply(p_clnt_ctx, n_curr_time); + if (sched_clnt_frm_is_cnfmnt(p_clnt_ctx)) { + *pp_conf_elect_ctx = + sched_elect_cnfmnt(*pp_conf_elect_ctx, + p_clnt_ctx); + } else { + if (!*pp_conf_elect_ctx) { + *pp_non_conf_elect_ctx = + sched_elect_non_cnfmnt + (*pp_non_conf_elect_ctx, + p_clnt_ctx); + } else if (*pp_non_conf_elect_ctx) { + (void) + SCHED_CRITSEC_LEAVE( + (*pp_non_conf_elect_ctx)->clnt_cs); + *pp_non_conf_elect_ctx = NULL; + + } + } + } + if (p_clnt_ctx != *pp_conf_elect_ctx + && p_clnt_ctx != *pp_non_conf_elect_ctx) + (void)SCHED_CRITSEC_LEAVE(p_clnt_ctx->clnt_cs); + p_clnt_node = p_clnt_node->p_next; + } + + return SCHED_S_OK; + +} + +enum sched_status_type sched_process_de_q_frm( + struct sched_ctx_type *p_sched_ctx, + void **pp_frm_data, void **pp_client_data) { + enum sched_status_type status; + struct sched_clnt_ctx_type *p_sched_clnt_ctx = NULL; + struct sched_clnt_ctx_type *p_conf_elect_ctx; + struct sched_clnt_ctx_type *p_non_conf_elect_ctx; + struct sched_clnt_q_elem q_elem; + + status = sched_process_de_q_frm_rt_clnt(p_sched_ctx, + &p_conf_elect_ctx, + &p_non_conf_elect_ctx); + if (SCHED_FAILED(status)) { + SCHED_MSG_ERR("sched_process_de_q_frm_rt_clnt ret err=%d", + status); + + return status; + } + + /*At this point we have looked at all real time clients in the + scheduler list and have run their elections. + We used the following frame service order to pick the client to + schedule: + a) client with conformant frame + b) client with non-conformant frame + c) non real-time client*/ + if (p_conf_elect_ctx) { + SCHED_MSG_LOW("Conformant frame client selected"); + sched_tkn_bkt_consume(p_conf_elect_ctx); + p_sched_clnt_ctx = p_conf_elect_ctx; + } else if (p_non_conf_elect_ctx) { + SCHED_MSG_LOW("Non-Conformant frame client selected"); + sched_tkn_bkt_consume(p_non_conf_elect_ctx); + p_sched_clnt_ctx = p_non_conf_elect_ctx; + } else if (p_sched_ctx->n_n_rt_clnts) + p_sched_clnt_ctx = sched_elect_non_rt(p_sched_ctx); + + /*If we have a client that we can schedule, then dequeue the frame + at the head of its queue.*/ + if (p_sched_clnt_ctx) { + *pp_client_data = p_sched_clnt_ctx->p_client_data; + + sched_de_q_head_frm(p_sched_clnt_ctx, &q_elem); + + *pp_frm_data = q_elem.p_frm_data; + + p_sched_clnt_ctx->n_curr_o_tkns -= + p_sched_clnt_ctx->n_o_tkn_per_ip_frm; + + /*If the dequeued frame was marked EOF we need to decrement the + eof_marker count.*/ + if (q_elem.b_eof) { + SCHED_MSG_MED + ("Last frame for EOF marked client dequeued"); + + p_sched_clnt_ctx->n_eof_marker--; + + status = SCHED_S_EOF; + } + + SCHED_MSG_LOW + ("Client frame Dequeued. Queue fill status = %d / %d", + p_sched_clnt_ctx->n_q_len, + p_sched_clnt_ctx->n_max_queue_len); + SCHED_MSG_LOW("Client category = %d", + p_sched_clnt_ctx->client_ctgy); + SCHED_MSG_LOW("Client allocated P token rate (per sec) = %d", + p_sched_clnt_ctx->n_curr_p_tkn_rate); + SCHED_MSG_LOW("Client frame rate = %d / %d", + p_sched_clnt_ctx->frm_rate.n_numer, + p_sched_clnt_ctx->frm_rate.n_denom); + SCHED_MSG_LOW("Client P tokens per frame = %d", + p_sched_clnt_ctx->n_p_tkn_per_frm); + + /*We had held on to the election winning client critical + section. Leave client critical section before we exit.*/ + (void)SCHED_CRITSEC_LEAVE(p_sched_clnt_ctx->clnt_cs); + } else { + status = SCHED_S_QEMPTY; + } + + return status; + +} + +enum sched_status_type sched_process_sched_lvl_get_param( + struct sched_ctx_type *p_sched_ctx, + enum sched_index_type param_index, + union sched_value_type *p_param_value) +{ + enum sched_status_type status = SCHED_S_OK; + + switch (param_index) { + case SCHED_I_PERFLEVEL: + { + p_param_value->un_value = p_sched_ctx->n_perf_lvl; + break; + } + + default: + { + status = SCHED_S_EBADPARM; + break; + } + } + return status; +} + +enum sched_status_type sched_process_sched_lvl_set_param( + struct sched_ctx_type *p_sched_ctx, + enum sched_index_type param_index, + union sched_value_type *p_param_value) +{ + enum sched_status_type status = SCHED_S_OK; + + SCHED_MSG_HIGH("Set_sched_param index = %u, value = %p", + param_index, (void *)p_param_value); + + switch (param_index) { + case SCHED_I_PERFLEVEL: + { + if (p_sched_ctx->n_total_clnt_bw > + p_param_value->un_value) { + SCHED_MSG_HIGH + ("Perf level being lowered than current " + "bandwidth"); + SCHED_MSG_HIGH + ("curr_perflvl=%d, new_perflvl=%d, " + "curr_bw=%d", + p_sched_ctx->n_perf_lvl, + p_param_value->un_value, + p_sched_ctx->n_total_clnt_bw); + } + + p_sched_ctx->n_perf_lvl = p_param_value->un_value; + + break; + } + + default: + { + status = SCHED_S_EBADPARM; + break; + } + } + return status; +} + +enum sched_status_type sched_process_clnt_lvl_get_param( + struct sched_ctx_type *p_sched_ctx, + struct sched_clnt_ctx_type *p_clnt_ctx, + enum sched_index_type param_index, + union sched_value_type *p_param_value) { + enum sched_status_type status = SCHED_S_OK; + + switch (param_index) { + case SCHED_I_CLNT_CURRQLEN: + { + p_param_value->un_value = p_clnt_ctx->n_q_len; + break; + } + + case SCHED_I_CLNT_PTKNRATE: + { + p_param_value->un_value = p_clnt_ctx->n_curr_p_tkn_rate; + break; + } + + case SCHED_I_CLNT_PTKNPERFRM: + { + p_param_value->un_value = p_clnt_ctx->n_p_tkn_per_frm; + break; + } + + case SCHED_I_CLNT_FRAMERATE: + { + p_param_value->frm_rate = p_clnt_ctx->frm_rate; + break; + } + + case SCHED_I_CLNT_OTKNMAX: + { + p_param_value->un_value = p_clnt_ctx->n_o_tkn_max; + break; + } + + case SCHED_I_CLNT_OTKNPERIPFRM: + { + p_param_value->un_value = + p_clnt_ctx->n_o_tkn_per_ip_frm; + break; + } + + case SCHED_I_CLNT_OTKNCURRENT: + { + p_param_value->un_value = p_clnt_ctx->n_curr_o_tkns; + break; + } + + default: + { + status = SCHED_S_EBADPARM; + break; + } + } + return status; +} + +enum sched_status_type sched_process_clnt_lvl_set_param( + struct sched_ctx_type *p_sched_ctx, + struct sched_clnt_ctx_type *p_clnt_ctx, + enum sched_index_type param_index, + union sched_value_type *p_param_value) +{ + enum sched_status_type status = SCHED_S_OK; + + SCHED_MSG_HIGH("Set_clnt_param index = %u, value = %p", + param_index, (void *)p_param_value); + + switch (param_index) { + case SCHED_I_CLNT_CURRQLEN: + case SCHED_I_CLNT_OTKNCURRENT: + { + status = SCHED_S_EINVALOP; + break; + } + + case SCHED_I_CLNT_PTKNRATE: + { + status = + sched_process_set_p_tkn_rate(p_sched_ctx, + p_clnt_ctx, + p_param_value); + break; + } + + case SCHED_I_CLNT_PTKNPERFRM: + { + + p_clnt_ctx->n_p_tkn_per_frm = p_param_value->un_value; + sched_tkn_bkt_config(p_clnt_ctx); + break; + } + + case SCHED_I_CLNT_FRAMERATE: + { + p_clnt_ctx->frm_rate = p_param_value->frm_rate; + break; + } + + case SCHED_I_CLNT_OTKNMAX: + { + if (p_param_value->un_value < + p_clnt_ctx->n_o_tkn_per_ip_frm) { + status = SCHED_S_EBADPARM; + } else { + p_clnt_ctx->n_o_tkn_max = + p_param_value->un_value; + + p_clnt_ctx->n_curr_o_tkns = + SCHED_MIN(p_clnt_ctx->n_curr_o_tkns, + p_clnt_ctx->n_o_tkn_max); + } + break; + } + + case SCHED_I_CLNT_OTKNPERIPFRM: + { + if (p_param_value->un_value > p_clnt_ctx->n_o_tkn_max) { + status = SCHED_S_EBADPARM; + } else { + p_clnt_ctx->n_o_tkn_per_ip_frm = + p_param_value->un_value; + } + break; + } + + default: + { + status = SCHED_S_EBADPARM; + break; + } + } + + return status; + +} + +enum sched_status_type sched_process_suspend_resume_clnt( + struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node, u32 b_state) { + u32 n_curr_time; + struct sched_clnt_ctx_type *p_clnt_ctx = &p_clnt_node->data; + + SCHED_MSG_HIGH("Current client sched_state=%d. Requested state=%d", + p_clnt_ctx->b_sched_state, b_state); + + if (p_clnt_ctx->b_sched_state == b_state) + return SCHED_S_OK; + + + p_clnt_ctx->b_sched_state = b_state; + + if (!SCHED_SUCCEEDED(SCHED_GET_CURRENT_TIME(&n_curr_time))) { + SCHED_MSG_ERR("Get current time failed"); + + return SCHED_S_OK; + } + + /* RESUME */ + if (b_state) { + p_clnt_ctx->n_bkt_lst_sup_time = n_curr_time; + } else { /* SUSPEND */ + /*As we are suspending the client we fill the token bucket upto + current time instance.*/ + sched_tkn_bkt_supply(p_clnt_ctx, n_curr_time); + } + + SCHED_MSG_MED("Client category %d", p_clnt_ctx->client_ctgy); + SCHED_MSG_MED("Client allocated P token rate (per sec) = %d", + p_clnt_ctx->n_curr_p_tkn_rate); + SCHED_MSG_MED("Client frame rate = %d / %d", + p_clnt_ctx->frm_rate.n_numer, + p_clnt_ctx->frm_rate.n_denom); + SCHED_MSG_MED("Client P tokens per frame = %d", + p_clnt_ctx->n_p_tkn_per_frm); + + return SCHED_S_OK; + +} + +void sched_remove_node_from_list( + struct _sched_clnt_list_node_type **pp_head, + struct _sched_clnt_list_node_type *p_node) +{ + u32 b_found = FALSE; + struct _sched_clnt_list_node_type *p_curr = *pp_head; + + if (!*pp_head || !p_node) { + SCHED_MSG_ERR("Bad params. p_head %p, p_node %p", *pp_head, + p_node); + return; + } + + if (p_node == *pp_head) { + *pp_head = p_node->p_next; + return; + } + + while (!b_found && p_curr) { + if (p_node == p_curr->p_next) { + p_curr->p_next = p_node->p_next; + b_found = TRUE; + } + + p_curr = p_curr->p_next; + } + +} + +SCHED_INLINE u32 sched_consider_clnt_for_sched( + struct sched_clnt_ctx_type *p_clnt_ctx) +{ + if (p_clnt_ctx->b_first_frm && + p_clnt_ctx->b_sched_state && + !p_clnt_ctx->b_flushing && + p_clnt_ctx->n_q_len && + p_clnt_ctx->n_curr_o_tkns >= p_clnt_ctx->n_o_tkn_per_ip_frm) { + return TRUE; + } else { + return FALSE; + } +} diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.h b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.h new file mode 100644 index 0000000000000..5f509e19b0506 --- /dev/null +++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.h @@ -0,0 +1,138 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VID_FRAME_SCHEDULER_H_ +#define _VID_FRAME_SCHEDULER_H_ +#include "vid_frame_scheduler_utils.h" + +struct sched_clnt_q_elem { + void *p_frm_data; + u32 b_eof; + +}; + +struct sched_clnt_ctx_type { + enum sched_client_ctgy_type client_ctgy; + struct sched_client_frm_rate_type frm_rate; + u32 n_p_tkn_per_frm; + u32 n_curr_p_tkn_rate; + u32 n_o_tkn_max; + u32 n_o_tkn_per_ip_frm; + u32 n_curr_o_tkns; + u32 n_bkt_size; + u32 n_bkt_quies_cap; + s32 n_bkt_curr_tkns; + s32 n_bkt_curr_tkns_nmlzd; + u32 n_bkt_lst_sup_time; + u32 n_max_queue_len; + struct sched_clnt_q_elem *a_clnt_frm_q; + s32 n_q_head; + s32 n_q_tail; + u32 n_q_len; + u32 b_first_frm; + u32 n_eof_marker; + u32 b_flushing; + u32 b_sched_state; + void *p_client_data; + u32 *clnt_cs; +}; + +struct _sched_clnt_list_node_type { + struct sched_clnt_ctx_type data; + struct _sched_clnt_list_node_type *p_next; + +}; + +struct _sched_clnt_list_node_type; + +struct sched_ctx_type { + u32 n_perf_lvl; + struct _sched_clnt_list_node_type *p_rt_head; + u32 n_rt_clnts; + struct _sched_clnt_list_node_type *p_n_rt_head; + u32 n_n_rt_clnts; + struct _sched_clnt_list_node_type *p_n_rt_last_sched; + u32 n_total_clnt_bw; + u32 *sched_cs; +}; + +SCHED_INLINE u32 SCHED_SUCCEEDED(enum sched_status_type status); +SCHED_INLINE u32 SCHED_FAILED(enum sched_status_type status); +SCHED_INLINE void sched_free_clnt_node + (struct _sched_clnt_list_node_type *p_clnt_node); +enum sched_status_type sched_clear_clnt_list + (struct _sched_clnt_list_node_type *p_clnt_lst); +enum sched_status_type sched_process_add_clnt + (struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node, + struct sched_client_init_param_type *p_init_param); +enum sched_status_type sched_process_remove_clnt + (struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node); +enum sched_status_type sched_process_flush_clnt_buff + (struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node, void **pp_frm_data); +SCHED_INLINE enum sched_status_type sched_process_mark_clnt_eof + (struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node); +enum sched_status_type sched_process_update_clnt_o_tkn + (struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node, u32 b_type, u32 n_o_tkn); +enum sched_status_type sched_process_en_q_frm + (struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node, void *p_frm_data); +enum sched_status_type sched_process_re_en_q_frm +(struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node, void *p_frm_data); +enum sched_status_type sched_process_de_q_frm + (struct sched_ctx_type *p_sched_ctx, + void **pp_frm_data, void **pp_client_data); +enum sched_status_type sched_process_sched_lvl_get_param + (struct sched_ctx_type *p_sched_ctx, + enum sched_index_type param_index, union sched_value_type *p_param_value); +enum sched_status_type sched_process_sched_lvl_set_param + (struct sched_ctx_type *p_sched_ctx, + enum sched_index_type param_index, union sched_value_type *p_param_value); +enum sched_status_type sched_process_clnt_lvl_get_param + (struct sched_ctx_type *p_sched_ctx, + struct sched_clnt_ctx_type *p_clnt_ctx, + enum sched_index_type param_index, union sched_value_type *p_param_value); +enum sched_status_type sched_process_clnt_lvl_set_param + (struct sched_ctx_type *p_sched_ctx, + struct sched_clnt_ctx_type *p_clnt_ctx, + enum sched_index_type param_index, union sched_value_type *p_param_value); +enum sched_status_type sched_process_suspend_resume_clnt + (struct sched_ctx_type *p_sched_ctx, + struct _sched_clnt_list_node_type *p_clnt_node, u32 b_state); +void sched_remove_node_from_list + (struct _sched_clnt_list_node_type **pp_head, + struct _sched_clnt_list_node_type *p_node); +SCHED_INLINE u32 sched_consider_clnt_for_sched + (struct sched_clnt_ctx_type *p_clnt_ctx); + +#endif diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.c b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.c new file mode 100644 index 0000000000000..6a667856ce277 --- /dev/null +++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.c @@ -0,0 +1,426 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" + +#include "vid_frame_scheduler_api.h" +#include "vid_frame_scheduler.h" + +enum sched_status_type sched_create( + struct sched_init_param_type *p_init_param, void **p_handle) +{ + struct sched_ctx_type *p_sched_ctx; + + SCHED_MSG_HIGH("sched_create API"); + + if (!p_handle || !p_init_param) { + SCHED_MSG_ERR + ("Bad input parameters: p_handle=%p, p_init_param=%p", + p_handle, p_init_param); + return SCHED_S_EBADPARM; + } + + if (!p_init_param->n_perf_lvl) { + SCHED_MSG_ERR("Invalid Perf level=%u", + p_init_param->n_perf_lvl); + return SCHED_S_EBADPARM; + } + + p_sched_ctx = + (struct sched_ctx_type *) + SCHED_MALLOC(sizeof(struct sched_ctx_type)); + + if (!p_sched_ctx) { + SCHED_MSG_ERR("Could not allocate sched ctx. Out of memory"); + return SCHED_S_ENOMEM; + } + + SCHED_MEMSET(p_sched_ctx, 0, sizeof(struct sched_ctx_type)); + p_sched_ctx->n_perf_lvl = p_init_param->n_perf_lvl; + + if (SCHED_FAILED(SCHED_CRITSEC_CREATE(&p_sched_ctx->sched_cs))) { + SCHED_FREE(p_sched_ctx); + return SCHED_S_EFAIL; + } + + *p_handle = p_sched_ctx; + + SCHED_MSG_MED("Sched instance created. All went well"); + + return SCHED_S_OK; + +} + +enum sched_status_type sched_destroy(void *handle) +{ + struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; + + SCHED_MSG_HIGH("sched_destroy API"); + + if (!p_sched_ctx) { + SCHED_MSG_ERR("Bad input parameters"); + return SCHED_S_EBADPARM; + } + (void)SCHED_CRITSEC_ENTER(p_sched_ctx->sched_cs); + (void)sched_clear_clnt_list(p_sched_ctx->p_rt_head); + (void)sched_clear_clnt_list(p_sched_ctx->p_n_rt_head); + SCHED_MSG_MED("Sched clnt lists are cleared & released"); + (void)SCHED_CRITSEC_LEAVE(p_sched_ctx->sched_cs); + (void)SCHED_CRITSEC_RELEASE(p_sched_ctx->sched_cs); + SCHED_MEMSET(p_sched_ctx, 0, sizeof(struct sched_ctx_type)); + SCHED_FREE(p_sched_ctx); + SCHED_MSG_MED("Sched instance deleted"); + return SCHED_S_OK; +} + +enum sched_status_type sched_get_param( + void *handle, + enum sched_index_type param_index, + union sched_value_type *p_param_value) +{ + struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; + enum sched_status_type status; + + SCHED_MSG_HIGH("sched_get_param API"); + + if (!p_sched_ctx || !p_param_value) { + SCHED_MSG_ERR + ("Bad input parameters: p_sched_ctx=%p, p_param_value=%p", + p_sched_ctx, p_param_value); + + return SCHED_S_EBADPARM; + } + + (void)SCHED_CRITSEC_ENTER(p_sched_ctx->sched_cs); + + status = + sched_process_sched_lvl_get_param(p_sched_ctx, param_index, + p_param_value); + + (void)SCHED_CRITSEC_LEAVE(p_sched_ctx->sched_cs); + return status; +} + +enum sched_status_type sched_set_param( + void *handle, + enum sched_index_type param_index, + union sched_value_type *p_param_value) +{ + struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; + enum sched_status_type status; + + SCHED_MSG_HIGH("sched_set_param API"); + + if (!p_sched_ctx || !p_param_value) { + SCHED_MSG_ERR + ("Bad input parameters: p_sched_ctx=%p, p_param_value=%p", + p_sched_ctx, p_param_value); + return SCHED_S_EBADPARM; + } + (void)SCHED_CRITSEC_ENTER(p_sched_ctx->sched_cs); + status = + sched_process_sched_lvl_set_param(p_sched_ctx, param_index, + p_param_value); + (void)SCHED_CRITSEC_LEAVE(p_sched_ctx->sched_cs); + return status; +} + +enum sched_status_type sched_add_client( + void *handle, + struct sched_client_init_param_type *p_init_param, + void **p_client_hdl) +{ + struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; + enum sched_status_type status = SCHED_S_OK; + struct _sched_clnt_list_node_type *p_new_clnt; + + SCHED_MSG_HIGH("sched_add_client API"); + + if (!p_sched_ctx || !p_init_param || + !p_client_hdl) { + SCHED_MSG_ERR("Bad input parameters"); + + return SCHED_S_EBADPARM; + } + + p_new_clnt = (struct _sched_clnt_list_node_type *) + SCHED_MALLOC(sizeof(struct _sched_clnt_list_node_type)); + if (!p_new_clnt) { + SCHED_MSG_ERR("Could not allocate client ctx. Out of memory"); + return SCHED_S_ENOMEM; + } + (void)SCHED_CRITSEC_ENTER(p_sched_ctx->sched_cs); + status = sched_process_add_clnt(p_sched_ctx, p_new_clnt, p_init_param); + + if (SCHED_FAILED(status)) { + SCHED_MSG_ERR("Add_client failed with err=%d", status); + sched_free_clnt_node(p_new_clnt); + p_new_clnt = NULL; + } + + (void)SCHED_CRITSEC_LEAVE(p_sched_ctx->sched_cs); + *p_client_hdl = p_new_clnt; + SCHED_MSG_MED("Sched client instance created. All went well"); + return status; +} + +enum sched_status_type sched_remove_client(void *handle, void *client_hdl) +{ + struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; + struct _sched_clnt_list_node_type *p_clnt_node = + (struct _sched_clnt_list_node_type *)client_hdl; + enum sched_status_type status = SCHED_S_OK; + + SCHED_MSG_HIGH("sched_remove_client API"); + if (!p_sched_ctx || !p_clnt_node) { + SCHED_MSG_ERR + ("Bad input parameters: p_sched_ctx=%p, p_clnt_node=%p", + p_sched_ctx, p_clnt_node); + return SCHED_S_EBADPARM; + } + + (void)SCHED_CRITSEC_ENTER(p_sched_ctx->sched_cs); + status = sched_process_remove_clnt(p_sched_ctx, p_clnt_node); + (void)SCHED_CRITSEC_LEAVE(p_sched_ctx->sched_cs); + return status; +} + +enum sched_status_type sched_flush_client_buffer( + void *handle, void *client_hdl, void **pp_frm_data) +{ + + struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; + struct _sched_clnt_list_node_type *p_clnt_node = + (struct _sched_clnt_list_node_type *)client_hdl; + enum sched_status_type status = SCHED_S_OK; + + SCHED_MSG_HIGH("sched_flush_client_buffer API"); + if (!p_sched_ctx || !p_clnt_node || !pp_frm_data) { + SCHED_MSG_ERR + ("Bad input parameters: p_sched_ctx=%p, p_clnt_node=%p", + p_sched_ctx, p_clnt_node); + return SCHED_S_EBADPARM; + } + + (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); + status = + sched_process_flush_clnt_buff(p_sched_ctx, p_clnt_node, + pp_frm_data); + (void)SCHED_CRITSEC_LEAVE(p_clnt_node->data.clnt_cs); + return status; +} + +enum sched_status_type sched_mark_client_eof(void *handle, void *client_hdl) +{ + struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; + struct _sched_clnt_list_node_type *p_clnt_node = + (struct _sched_clnt_list_node_type *)client_hdl; + enum sched_status_type status = SCHED_S_OK; + + SCHED_MSG_HIGH("sched_mark_client_eof API"); + if (!p_sched_ctx || !p_clnt_node) { + SCHED_MSG_ERR + ("Bad input parameters: p_sched_ctx=%p, p_clnt_node=%p", + p_sched_ctx, p_clnt_node); + return SCHED_S_EBADPARM; + } + + (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); + status = sched_process_mark_clnt_eof(p_sched_ctx, p_clnt_node); + (void)SCHED_CRITSEC_LEAVE(p_clnt_node->data.clnt_cs); + return status; +} + +enum sched_status_type sched_update_client_o_tkn( + void *handle, void *client_hdl, u32 b_type, u32 n_o_tkn) +{ + + struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; + struct _sched_clnt_list_node_type *p_clnt_node = + (struct _sched_clnt_list_node_type *)client_hdl; + enum sched_status_type status = SCHED_S_OK; + + SCHED_MSG_HIGH("sched_restore_client_o_tkn API"); + + if (!p_sched_ctx || !p_clnt_node) { + SCHED_MSG_ERR + ("Bad input parameters: p_sched_ctx=%p, p_clnt_node=%p", + p_sched_ctx, p_clnt_node); + return SCHED_S_EBADPARM; + } + + (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); + status = + sched_process_update_clnt_o_tkn(p_sched_ctx, p_clnt_node, b_type, + n_o_tkn); + (void)SCHED_CRITSEC_LEAVE(p_clnt_node->data.clnt_cs); + return status; +} + +enum sched_status_type sched_queue_frame( + void *handle, void *client_hdl, void *p_frm_data) +{ + struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; + struct _sched_clnt_list_node_type *p_clnt_node = + (struct _sched_clnt_list_node_type *)client_hdl; + enum sched_status_type status = SCHED_S_OK; + + SCHED_MSG_HIGH("sched_queue_frame API"); + if (!p_sched_ctx || !p_clnt_node) { + SCHED_MSG_ERR + ("Bad input parameters: p_sched_ctx=%p, p_clnt_node=%p", + p_sched_ctx, p_clnt_node); + return SCHED_S_EBADPARM; + } + + (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); + status = sched_process_en_q_frm(p_sched_ctx, p_clnt_node, p_frm_data); + (void)SCHED_CRITSEC_LEAVE(p_clnt_node->data.clnt_cs); + return status; +} + +enum sched_status_type sched_re_queue_frame( +void *handle, void *client_hdl, void *p_frm_data) +{ + struct sched_ctx_type* p_sched_ctx = (struct sched_ctx_type *)handle; + struct _sched_clnt_list_node_type *p_clnt_node = + (struct _sched_clnt_list_node_type *)client_hdl; + enum sched_status_type status = SCHED_S_OK; + + SCHED_MSG_HIGH("\n sched_re_queue_frame API"); + if (!p_sched_ctx || !p_clnt_node) { + SCHED_MSG_ERR("Bad input parameters:" + "p_sched_ctx=%p, p_clnt_node=%p", + p_sched_ctx, p_clnt_node); + return SCHED_S_EBADPARM; + } + (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); + status = sched_process_re_en_q_frm(p_sched_ctx, p_clnt_node, + p_frm_data); + (void)SCHED_CRITSEC_LEAVE(p_clnt_node->data.clnt_cs); + return status; +} + +enum sched_status_type sched_de_queue_frame( + void *handle, void **pp_frm_data, void **pp_client_data) +{ + struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; + enum sched_status_type status = SCHED_S_OK; + + SCHED_MSG_HIGH("sched_de_queue_frame API"); + + if (!p_sched_ctx || !pp_frm_data + || !pp_client_data) { + SCHED_MSG_ERR("Bad input parameters: p_sched_ctx=%p, " + "pp_frm_data=%p, pp_client_data=%p", + p_sched_ctx, pp_frm_data, + pp_client_data); + return SCHED_S_EBADPARM; + } + (void)SCHED_CRITSEC_ENTER(p_sched_ctx->sched_cs); + status = + sched_process_de_q_frm(p_sched_ctx, pp_frm_data, pp_client_data); + (void)SCHED_CRITSEC_LEAVE(p_sched_ctx->sched_cs); + return status; +} + +enum sched_status_type sched_get_client_param( + void *handle, void *client_hdl, + enum sched_index_type param_index, + union sched_value_type *p_param_value) +{ + struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; + struct _sched_clnt_list_node_type *p_clnt_node = + (struct _sched_clnt_list_node_type *)client_hdl; + enum sched_status_type status; + + SCHED_MSG_HIGH("sched_get_client_param API"); + + if (!p_sched_ctx || !p_clnt_node || + !p_param_value) { + SCHED_MSG_ERR("Bad input parameters: p_sched_ctx=%p, " + "p_clnt_node=%p, p_param_value=%p", + p_sched_ctx, p_clnt_node, + p_param_value); + + return SCHED_S_EBADPARM; + } + (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); + status = sched_process_clnt_lvl_get_param(p_sched_ctx, + &p_clnt_node->data, + param_index, p_param_value); + (void)SCHED_CRITSEC_LEAVE(p_clnt_node->data.clnt_cs); + return status; +} + +enum sched_status_type sched_set_client_param( + void *handle, void *client_hdl, + enum sched_index_type param_index, + union sched_value_type *p_param_value) +{ + struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; + struct _sched_clnt_list_node_type *p_clnt_node = + (struct _sched_clnt_list_node_type *)client_hdl; + enum sched_status_type status; + + SCHED_MSG_HIGH("sched_set_client_param API"); + + if (!p_sched_ctx || !p_clnt_node || + !p_param_value) { + SCHED_MSG_ERR("Bad input parameters: " + "p_sched_ctx=%p, p_clnt_node=%p, " + "p_param_value=%p", p_sched_ctx, p_clnt_node, + p_param_value); + return SCHED_S_EBADPARM; + } + + (void)SCHED_CRITSEC_ENTER(p_sched_ctx->sched_cs); + (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); + + status = sched_process_clnt_lvl_set_param(p_sched_ctx, + &p_clnt_node->data, param_index, p_param_value); + + (void)SCHED_CRITSEC_LEAVE(p_clnt_node->data.clnt_cs); + (void)SCHED_CRITSEC_LEAVE(p_sched_ctx->sched_cs); + return status; +} + +enum sched_status_type sched_suspend_resume_client( + void *handle, void *client_hdl, u32 b_state) +{ + struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; + struct _sched_clnt_list_node_type *p_clnt_node = + (struct _sched_clnt_list_node_type *)client_hdl; + enum sched_status_type status; + + SCHED_MSG_HIGH("sched_client_suspend_resume API"); + + if (!p_sched_ctx || !p_clnt_node) { + SCHED_MSG_ERR + ("Bad input parameters: p_sched_ctx=%p, p_clnt_node=%p", + p_sched_ctx, p_clnt_node); + return SCHED_S_EBADPARM; + } + + (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); + status = + sched_process_suspend_resume_clnt(p_sched_ctx, p_clnt_node, + b_state); + (void)SCHED_CRITSEC_LEAVE(p_clnt_node->data.clnt_cs); + return status; +} diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.h b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.h new file mode 100644 index 0000000000000..34bcd7a7a0ec9 --- /dev/null +++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.h @@ -0,0 +1,150 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _SCHEDULER_API_H_ +#define _SCHEDULER_API_H_ + +enum sched_status_type { + SCHED_S_OK = 0x0, + SCHED_S_NOPTKN, + SCHED_S_NOOTKN, + SCHED_S_SLEEP, + SCHED_S_QEMPTY, + SCHED_S_QFULL, + SCHED_S_EOF, + SCHED_S_EFAIL = 0x64, + SCHED_S_ENOMEM, + SCHED_S_EBADPARM, + SCHED_S_EINVALOP, + SCHED_S_ENOTIMPL, + SCHED_S_ENORES, + SCHED_S_EINVALST, + SCHED_S_MAX = 0x7fffffff +}; + +enum sched_index_type { + SCHED_I_START_UNUSED = 0x0, + SCHED_I_PERFLEVEL, + SCHED_I_CLNT_START_UNUSED = 0x63, + SCHED_I_CLNT_CURRQLEN, + SCHED_I_CLNT_PTKNRATE, + SCHED_I_CLNT_PTKNPERFRM, + SCHED_I_CLNT_FRAMERATE, + SCHED_I_CLNT_OTKNMAX, + SCHED_I_CLNT_OTKNPERIPFRM, + SCHED_I_CLNT_OTKNCURRENT, + SCHED_I_MAX = 0x7fffffff +}; + +struct sched_client_frm_rate_type { + u32 n_numer; + u32 n_denom; + +}; + +union sched_value_type { + u32 un_value; + struct sched_client_frm_rate_type frm_rate; + +}; + +struct sched_init_param_type { + u32 n_perf_lvl; + +}; + +enum sched_client_ctgy_type { + SCHED_CLNT_RT_BUFF = 0, + SCHED_CLNT_RT_NOBUFF, + SCHED_CLNT_NONRT, + SCHED_CLNT_MAX = 0x7fffffff +}; + +struct sched_client_init_param_type { + enum sched_client_ctgy_type client_ctgy; + u32 n_max_queue_len; + struct sched_client_frm_rate_type frm_rate; + u32 n_p_tkn_per_frm; + u32 n_alloc_p_tkn_rate; + u32 n_o_tkn_max; + u32 n_o_tkn_per_ip_frm; + u32 n_o_tkn_init; + + void *p_client_data; + +}; + +enum sched_status_type sched_create + (struct sched_init_param_type *init_param, void **p_handle); + +enum sched_status_type sched_destroy(void *handle); + +enum sched_status_type sched_get_param + (void *handle, + enum sched_index_type param_index, union sched_value_type *p_param_value); + +enum sched_status_type sched_set_param + (void *handle, + enum sched_index_type param_index, union sched_value_type *p_param_value); + +enum sched_status_type sched_add_client + (void *handle, + struct sched_client_init_param_type *init_param, void **p_client_hdl); + +enum sched_status_type sched_remove_client(void *handle, void *client_hdl); + +enum sched_status_type sched_flush_client_buffer + (void *handle, void *client_hdl, void **pp_frm_data); + +enum sched_status_type sched_mark_client_eof(void *handle, void *client_hdl); + +enum sched_status_type sched_update_client_o_tkn + (void *handle, void *client_hdl, u32 b_type, u32 n_o_tkn); + +enum sched_status_type sched_queue_frame + (void *handle, void *client_hdl, void *p_frm_data); +enum sched_status_type sched_re_queue_frame +(void *handle, void *client_hdl, void *p_frm_data); + +enum sched_status_type sched_de_queue_frame + (void *handle, void **pp_frm_data, void **pp_client_data); + +enum sched_status_type sched_get_client_param + (void *handle, + void *client_hdl, + enum sched_index_type param_index, union sched_value_type *p_param_value); + +enum sched_status_type sched_set_client_param + (void *handle, + void *client_hdl, + enum sched_index_type param_index, union sched_value_type *p_param_value); + +enum sched_status_type sched_suspend_resume_client + (void *handle, void *client_hdl, u32 b_state); + +#endif diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.c b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.c new file mode 100644 index 0000000000000..22e8d3171e462 --- /dev/null +++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.c @@ -0,0 +1,154 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" +#include "vid_frame_scheduler_utils.h" + +/** + * SCHED_ASSERT () - This function is a wrapper to underlying ASSERT + * @val: value to be checked for + * function. + * DEPENDENCIES: None + * Returns none + */ +SCHED_INLINE void SCHED_ASSERT(int val) +{ + +} /* end of SCHED_ASSERT */ + +/** + * SCHED_MIN () - This function will find minimum of two values + * @n_x: value 1 + * @n_y: value 2 + * DEPENDENCIES: None + * Returns none + */ +SCHED_INLINE int SCHED_MIN(int n_x, int n_y) +{ + if (n_x < n_y) + return n_x; + else + return n_y; + +} /* end of SCHED_MIN */ + +/** + * SCHED_MALLOC () - This function is a wrapper to underlying malloc + * @size: memory size to be allocated + * function + * DEPENDENCIES: None + * Returns none + */ +SCHED_INLINE void *SCHED_MALLOC(int size) +{ + return kmalloc(size, GFP_KERNEL); +} /* end of SCHED_MALLOC */ + +/** + * SCHED_FREE () - This function is a wrapper to underlying memory free + * @p_ptr: memory to be freed + * function + * DEPENDENCIES: None + * Returns none + */ +SCHED_INLINE void SCHED_FREE(void *p_ptr) +{ + kfree(p_ptr); +} /* end of SCHED_FREE */ + +/** + * SCHED_MEMSET () - This function is a wrapper to underlying memory set + * @ptr: ptr to memory + * @val: value to be set + * @size: memory size to be set + * function + * DEPENDENCIES: None + * Returns none + */ +SCHED_INLINE void *SCHED_MEMSET(void *ptr, int val, int size) +{ + return memset(ptr, val, size); +} /* end of SCHED_MEMSET */ + +/** + * SCHED_GET_CURRENT_TIME () - This function is a wrapper to underlying get time + * @pn_time: ptr time value in milliseconds + * function + * DEPENDENCIES: None + * Returns SCHED_S_OK on success + */ +SCHED_INLINE enum sched_status_type SCHED_GET_CURRENT_TIME(u32 *pn_time) +{ + struct timeval tv; + do_gettimeofday(&tv); + *pn_time = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + return SCHED_S_OK; + +} /* end of SCHED_GET_CURRENT_TIME */ + +/** + * SCHED_CRITSEC_CREATE () - This function is a wrapper to creating a critical + * @p_cs: ptr to a critical section type + * section + * DEPENDENCIES: None + * Returns SCHED_S_OK on success + */ +SCHED_INLINE enum sched_status_type SCHED_CRITSEC_CREATE(u32 **p_cs) +{ + return SCHED_S_OK; + +} /* end of SCHED_CRITSEC_CREATE */ + +/** + * SCHED_CRITSEC_RELEASE () - This function is a wrapper to releasing a critical + * @cs: critical section handle type + * section resource + * DEPENDENCIES: None + * Returns SCHED_S_OK on success + */ +SCHED_INLINE enum sched_status_type SCHED_CRITSEC_RELEASE(u32 *cs) +{ + return SCHED_S_OK; + +} /* end of SCHED_CRITSEC_RELEASE */ + +/** + * SCHED_CRITSEC_ENTER () - This function is a wrapper to enter a critical + * @cs: critical section handle type + * section + * DEPENDENCIES: None + * Returns SCHED_S_OK on success + */ +SCHED_INLINE enum sched_status_type SCHED_CRITSEC_ENTER(u32 *cs) +{ + return SCHED_S_OK; + +} /* end of SCHED_CRITSEC_ENTER */ + +/** + * SCHED_CRITSEC_LEAVE () - This function is a wrapper to leave a critical + * @cs: critical section handle type + * section + * DEPENDENCIES: None + * Returns SCHED_S_OK on success + */ +SCHED_INLINE enum sched_status_type SCHED_CRITSEC_LEAVE(u32 *cs) +{ + return SCHED_S_OK; + +} /* end of SCHED_CRITSEC_LEAVE */ diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.h b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.h new file mode 100644 index 0000000000000..b013d27d7fdbb --- /dev/null +++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.h @@ -0,0 +1,78 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _SCHEDULER_UTILS_H_ +#define _SCHEDULER_UTILS_H_ + +#include "vid_frame_scheduler_api.h" + +#define SCHED_INLINE + +#if DEBUG + +#define SCHED_MSG_LOW(xx_fmt, ...) printk(KERN_INFO "\n " \ + xx_fmt, ## __VA_ARGS__) +#define SCHED_MSG_MED(xx_fmt, ...) printk(KERN_INFO "\n" \ + xx_fmt, ## __VA_ARGS__) +#define SCHED_MSG_HIGH(xx_fmt, ...) printk(KERN_WARNING "\n" \ + xx_fmt, ## __VA_ARGS__) + +#else + +#define SCHED_MSG_LOW(xx_fmt...) +#define SCHED_MSG_MED(xx_fmt...) +#define SCHED_MSG_HIGH(xx_fmt...) + +#endif + +#define SCHED_MSG_ERR(xx_fmt, ...) printk(KERN_ERR "\n err: " \ + xx_fmt, ## __VA_ARGS__) +#define SCHED_MSG_FATAL(xx_fmt, ...) printk(KERN_ERR "\n " \ + xx_fmt, ## __VA_ARGS__) + +SCHED_INLINE void SCHED_ASSERT(int val); + +SCHED_INLINE int SCHED_MIN(int x, int y); + +SCHED_INLINE enum sched_status_type SCHED_CRITSEC_CREATE(u32 **p_cs); + +SCHED_INLINE enum sched_status_type SCHED_CRITSEC_RELEASE(u32 *cs); + +SCHED_INLINE enum sched_status_type SCHED_CRITSEC_ENTER(u32 *cs); + +SCHED_INLINE enum sched_status_type SCHED_CRITSEC_LEAVE(u32 *cs); + +SCHED_INLINE void *SCHED_MALLOC(int size); + +SCHED_INLINE void SCHED_FREE(void *ptr); + +SCHED_INLINE void *SCHED_MEMSET(void *ptr, int val, int size); + +SCHED_INLINE enum sched_status_type SCHED_GET_CURRENT_TIME(u32 *pn_time); + +#endif diff --git a/drivers/misc/video_core/720p/vcd/vcd.h b/drivers/misc/video_core/720p/vcd/vcd.h new file mode 100644 index 0000000000000..d9087eb765990 --- /dev/null +++ b/drivers/misc/video_core/720p/vcd/vcd.h @@ -0,0 +1,358 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VCD_H_ +#define _VCD_H_ + +#include "vcd_api.h" +#include "vid_frame_scheduler_api.h" +#include "vcd_ddl_api.h" +#include "vcd_res_tracker_api.h" +#include "vcd_util.h" +#include "vcd_client_sm.h" +#include "vcd_core.h" +#include "vcd_device_sm.h" + +void vcd_reset_device_channels(struct vcd_dev_ctxt_type *p_dev_ctxt); + +u32 vcd_get_command_channel + (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type **pp_transc); + +u32 vcd_get_command_channel_in_loop + (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type **pp_transc); + +void vcd_mark_command_channel + (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type *p_transc); + +void vcd_release_command_channel + (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type *p_transc); + +void vcd_release_multiple_command_channels(struct vcd_dev_ctxt_type *p_dev_ctxt, + u32 n_channels); + +void vcd_release_interim_command_channels(struct vcd_dev_ctxt_type *p_dev_ctxt); + +u32 vcd_get_frame_channel + (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type **pp_transc); + +u32 vcd_get_frame_channel_in_loop + (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type **pp_transc); + +void vcd_mark_frame_channel(struct vcd_dev_ctxt_type *p_dev_ctxt); + +void vcd_release_frame_channel + (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type *p_transc); + +void vcd_release_multiple_frame_channels(struct vcd_dev_ctxt_type *p_dev_ctxt, + u32 n_channels); + +void vcd_release_interim_frame_channels(struct vcd_dev_ctxt_type *p_dev_ctxt); +u32 vcd_core_is_busy(struct vcd_dev_ctxt_type *p_dev_ctxt); + +void vcd_device_timer_start(struct vcd_dev_ctxt_type *p_dev_ctxt); +void vcd_device_timer_stop(struct vcd_dev_ctxt_type *p_dev_ctxt); + + +u32 vcd_init_device_context + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, u32 n_ev_code); + +u32 vcd_deinit_device_context + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, u32 n_ev_code); + +u32 vcd_init_client_context(struct vcd_clnt_ctxt_type_t *p_cctxt); + +void vcd_destroy_client_context(struct vcd_clnt_ctxt_type_t *p_cctxt); + +u32 vcd_check_for_client_context + (struct vcd_dev_ctxt_type *p_dev_ctxt, s32 driver_id); + +u32 vcd_validate_driver_handle + (struct vcd_dev_ctxt_type *p_dev_ctxt, s32 driver_handle); + +void vcd_handle_for_last_clnt_close + (struct vcd_dev_ctxt_type *p_dev_ctxt, u32 b_send_deinit); + +u32 vcd_common_allocate_set_buffer + (struct vcd_clnt_ctxt_type_t *p_cctxt, + enum vcd_buffer_type e_buffer, + u32 n_buf_size, struct vcd_buffer_pool_type **pp_buf_pool); + +u32 vcd_set_buffer_internal + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_buffer_pool_type *p_buf_pool, u8 *p_buffer, u32 n_buf_size); + +u32 vcd_allocate_buffer_internal + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_buffer_pool_type *p_buf_pool, + u32 n_buf_size, u8 **pp_vir_buf_addr, u8 **pp_phy_buf_addr); + +u32 vcd_free_one_buffer_internal + (struct vcd_clnt_ctxt_type_t *p_cctxt, + enum vcd_buffer_type e_buffer, u8 *p_buffer); + +u32 vcd_free_buffers_internal + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_buffer_pool_type *p_buf_pool); + +u32 vcd_alloc_buffer_pool_entries + (struct vcd_buffer_pool_type *p_buf_pool, + struct vcd_buffer_requirement_type *p_buf_req); + +void vcd_free_buffer_pool_entries(struct vcd_buffer_pool_type *p_buf_pool); + +void vcd_flush_in_use_buffer_pool_entries(struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_buffer_pool_type *p_buf_pool, u32 event); + +void vcd_reset_buffer_pool_for_reuse(struct vcd_buffer_pool_type *p_buf_pool); + +struct vcd_buffer_entry_type *vcd_get_free_buffer_pool_entry + (struct vcd_buffer_pool_type *p_pool); + +struct vcd_buffer_entry_type *vcd_find_buffer_pool_entry + (struct vcd_buffer_pool_type *p_pool, u8 *p_v_addr); + +struct vcd_buffer_entry_type *vcd_buffer_pool_entry_de_q + (struct vcd_buffer_pool_type *p_pool); + +u32 vcd_buffer_pool_entry_en_q + (struct vcd_buffer_pool_type *p_pool, + struct vcd_buffer_entry_type *p_entry); + +u32 vcd_client_cmd_en_q + (struct vcd_clnt_ctxt_type_t *p_cctxt, enum vcd_command_type e_command); + +void vcd_client_cmd_flush_and_en_q + (struct vcd_clnt_ctxt_type_t *p_cctxt, enum vcd_command_type e_command); + +u32 vcd_client_cmd_de_q + (struct vcd_clnt_ctxt_type_t *p_cctxt, enum vcd_command_type *p_command); + +u32 vcd_handle_recvd_eos + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_input_frame, u32 * pb_eos_handled); + +u32 vcd_handle_first_decode_frame(struct vcd_clnt_ctxt_type_t *p_cctxt); + +u32 vcd_add_client_to_sched(struct vcd_clnt_ctxt_type_t *p_cctxt); + +u32 vcd_handle_input_frame + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_input_frame); + +u32 vcd_store_seq_hdr + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_sequence_hdr_type *p_seq_hdr); + +u32 vcd_set_frame_size + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_property_frame_size_type *p_frm_size); + +u32 vcd_set_frame_rate + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_property_frame_rate_type *p_fps); + +u32 vcd_calculate_frame_delta + (struct vcd_clnt_ctxt_type_t *p_cctxt, struct vcd_frame_data_type *p_frame); + +struct vcd_buffer_entry_type *vcd_check_fill_output_buffer + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_buffer); + +u32 vcd_requeue_input_frame(struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_clnt_ctxt_type_t *p_cctxt, struct vcd_buffer_entry_type + *p_buf_entry); + +u32 vcd_schedule_frame(struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_clnt_ctxt_type_t **pp_cctxt, struct vcd_buffer_entry_type + **pp_ip_buf_entry); + +u32 vcd_map_sched_status(enum sched_status_type e_sched_status); + +u32 vcd_submit_command_in_continue + (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type *p_transc); + +u32 vcd_submit_cmd_sess_start(struct vcd_transc_type *p_transc); + +u32 vcd_submit_cmd_sess_end(struct vcd_transc_type *p_transc); + +void vcd_submit_cmd_client_close(struct vcd_clnt_ctxt_type_t *p_cctxt); + +u32 vcd_submit_frame + (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type *p_transc); + +u32 vcd_try_submit_frame_in_continue(struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_transc_type *p_transc); + +u32 vcd_process_cmd_sess_start(struct vcd_clnt_ctxt_type_t *p_cctxt); + +void vcd_try_submit_frame(struct vcd_dev_ctxt_type *p_dev_ctxt); + +u32 vcd_setup_with_ddl_capabilities(struct vcd_dev_ctxt_type *p_dev_ctxt); +void vcd_handle_submit_frame_failed(struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_transc_type *p_transc); + +struct vcd_transc_type *vcd_get_free_trans_tbl_entry + (struct vcd_dev_ctxt_type *p_dev_ctxt); + +void vcd_release_trans_tbl_entry(struct vcd_transc_type *p_trans_entry); + +void vcd_release_all_clnt_frm_transc(struct vcd_clnt_ctxt_type_t *p_cctxt); +void vcd_release_all_clnt_def_frm_transc(struct vcd_clnt_ctxt_type_t *p_cctxt); +void vcd_release_all_clnt_transc(struct vcd_clnt_ctxt_type_t *p_cctxt); + +u32 vcd_handle_input_done + (struct vcd_clnt_ctxt_type_t *p_cctxt, + void *p_payload, u32 event, u32 status); + +void vcd_handle_input_done_in_eos + (struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload, u32 status); + +void vcd_handle_input_done_failed + (struct vcd_clnt_ctxt_type_t *p_cctxt, struct vcd_transc_type *p_transc); + +void vcd_handle_input_done_for_interlacing + (struct vcd_clnt_ctxt_type_t *p_cctxt); + +void vcd_handle_input_done_with_trans_end + (struct vcd_clnt_ctxt_type_t *p_cctxt); + +u32 vcd_handle_frame_done + (struct vcd_clnt_ctxt_type_t *p_cctxt, + void *p_payload, u32 event, u32 status); + +void vcd_handle_frame_done_for_interlacing + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_transc_type *p_transc_ip1, + struct ddl_frame_data_type_tag *p_op_frm, u32 status); + +u32 vcd_handle_first_frame_done + (struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload); + +void vcd_handle_frame_done_in_eos + (struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload, u32 status); + +u32 vcd_handle_first_encode_frame_done + (struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload); + +u32 vcd_handle_output_required(struct vcd_clnt_ctxt_type_t *p_cctxt, + void *p_payload, u32 status); + +u32 vcd_handle_output_required_in_flushing(struct vcd_clnt_ctxt_type_t *p_cctxt, + void *p_payload); + +u32 vcd_handle_output_req_tran_end_in_eos(struct vcd_clnt_ctxt_type_t *p_cctxt); + +u32 vcd_validate_io_done_pyld(void *p_payload, u32 status); + +void vcd_handle_eos_trans_end(struct vcd_clnt_ctxt_type_t *p_cctxt); + + +void vcd_handle_eos_done + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_transc_type *p_transc, u32 status); + +void vcd_send_frame_done_in_eos + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_input_frame, u32 valid_opbuf); + +void vcd_send_frame_done_in_eos_for_dec + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_input_frame); + +void vcd_send_frame_done_in_eos_for_enc + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_input_frame); + +void vcd_handle_start_done(struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_transc_type *p_transc, u32 status); + +void vcd_handle_stop_done(struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_transc_type *p_transc, u32 status); + +void vcd_handle_stop_done_in_starting(struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_transc_type *p_transc, u32 status); + +void vcd_handle_stop_done_in_invalid(struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 status); + + +void vcd_send_flush_done(struct vcd_clnt_ctxt_type_t *p_cctxt, u32 status); + +void vcd_process_pending_flush_in_eos(struct vcd_clnt_ctxt_type_t *p_cctxt); + +void vcd_process_pending_stop_in_eos(struct vcd_clnt_ctxt_type_t *p_cctxt); + +void vcd_handle_trans_pending(struct vcd_clnt_ctxt_type_t *p_cctxt); + +void vcd_flush_output_buffers(struct vcd_clnt_ctxt_type_t *p_cctxt); + +u32 vcd_flush_buffers(struct vcd_clnt_ctxt_type_t *p_cctxt, u32 n_mode); +void vcd_flush_buffers_in_err_fatal(struct vcd_clnt_ctxt_type_t *p_cctxt); + +u32 vcd_power_event + (struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_clnt_ctxt_type_t *p_cctxt, u32 event); + +u32 vcd_device_power_event(struct vcd_dev_ctxt_type *p_dev_ctxt, u32 event, + struct vcd_clnt_ctxt_type_t *p_cctxt); + +u32 vcd_client_power_event + (struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_clnt_ctxt_type_t *p_cctxt, u32 event); + +u32 vcd_enable_clock(struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_clnt_ctxt_type_t *p_cctxt); + +u32 vcd_disable_clock(struct vcd_dev_ctxt_type *p_dev_ctxt); + +u32 vcd_set_perf_level(struct vcd_dev_ctxt_type *p_dev_ctxt, + u32 n_perf_lvl, struct vcd_clnt_ctxt_type_t *p_cctxt); + +u32 vcd_update_clnt_perf_lvl + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_property_frame_rate_type *p_fps, u32 n_frm_p_units); + +u32 vcd_gate_clock(struct vcd_dev_ctxt_type *p_dev_ctxt); + +u32 vcd_un_gate_clock(struct vcd_dev_ctxt_type *p_dev_ctxt); + +void vcd_handle_err_fatal(struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 event, u32 status); + +void vcd_handle_device_err_fatal(struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_clnt_ctxt_type_t *p_cctxt); + +void vcd_clnt_handle_device_err_fatal(struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 event); + +void vcd_handle_err_in_starting(struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 status); + +void vcd_handle_ind_hw_err_fatal(struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 event, u32 status); +#endif diff --git a/drivers/misc/video_core/720p/vcd/vcd_api.c b/drivers/misc/video_core/720p/vcd/vcd_api.c new file mode 100644 index 0000000000000..fbe409b9b3a5b --- /dev/null +++ b/drivers/misc/video_core/720p/vcd/vcd_api.c @@ -0,0 +1,905 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" +#include "vcd.h" + +u32 vcd_init(struct vcd_init_config_type *p_config, s32 *p_driver_handle) +{ + u32 rc = VCD_S_SUCCESS; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + + VCD_MSG_MED("vcd_init:"); + + if (!p_config || + !p_driver_handle || !p_config->pf_map_dev_base_addr) { + VCD_MSG_ERROR("Bad parameters"); + + return VCD_ERR_ILLEGAL_PARM; + } + + p_drv_ctxt = vcd_get_drv_context(); + + if (!p_drv_ctxt->dev_cs) + rc = vcd_critical_section_create(&p_drv_ctxt->dev_cs); + + if (rc) + return VCD_ERR_ALLOC_FAIL; + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_drv_ctxt->dev_state.p_state_table->ev_hdlr.pf_init) { + rc = p_drv_ctxt->dev_state.p_state_table->ev_hdlr. + pf_init(p_drv_ctxt, p_config, p_driver_handle); + } else { + VCD_MSG_ERROR("Unsupported API in device state %d", + p_drv_ctxt->dev_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_init); + +u32 vcd_term(s32 driver_handle) +{ + u32 rc = VCD_S_SUCCESS; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + + VCD_MSG_MED("vcd_term:"); + + p_drv_ctxt = vcd_get_drv_context(); + + if (!p_drv_ctxt->dev_cs) { + VCD_MSG_ERROR("No critical section object"); + + return VCD_ERR_BAD_STATE; + } + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_drv_ctxt->dev_state.p_state_table->ev_hdlr.pf_term) { + rc = p_drv_ctxt->dev_state.p_state_table->ev_hdlr. + pf_term(p_drv_ctxt, driver_handle); + } else { + VCD_MSG_ERROR("Unsupported API in device state %d", + p_drv_ctxt->dev_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + if (p_drv_ctxt->dev_state.e_state == VCD_DEVICE_STATE_NULL) { + VCD_MSG_HIGH + ("Device in NULL state. Releasing critical section"); + + vcd_critical_section_release(p_drv_ctxt->dev_cs); + p_drv_ctxt->dev_cs = NULL; + } + + return rc; + +} +EXPORT_SYMBOL(vcd_term); + +u32 vcd_open(s32 driver_handle, u32 b_decoding, + void (*callback) (u32 event, u32 status, void *p_info, u32 n_size, + void *handle, void *const p_client_data), + void *p_client_data) +{ + u32 rc = VCD_S_SUCCESS; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + + VCD_MSG_MED("vcd_open:"); + + if (!callback) { + VCD_MSG_ERROR("Bad parameters"); + + return VCD_ERR_ILLEGAL_PARM; + } + + p_drv_ctxt = vcd_get_drv_context(); + + if (!p_drv_ctxt->dev_cs) { + VCD_MSG_ERROR("No critical section object"); + + return VCD_ERR_BAD_STATE; + } + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_drv_ctxt->dev_state.p_state_table->ev_hdlr.pf_open) { + rc = p_drv_ctxt->dev_state.p_state_table->ev_hdlr. + pf_open(p_drv_ctxt, driver_handle, b_decoding, callback, + p_client_data); + } else { + VCD_MSG_ERROR("Unsupported API in device state %d", + p_drv_ctxt->dev_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_open); + +u32 vcd_close(void *handle) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = + (struct vcd_clnt_ctxt_type_t *)handle; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + u32 rc; + + VCD_MSG_MED("vcd_close:"); + + if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle"); + + return VCD_ERR_BAD_HANDLE; + } + + p_drv_ctxt = vcd_get_drv_context(); + + if (p_drv_ctxt->dev_state.p_state_table->ev_hdlr.pf_close) { + rc = p_drv_ctxt->dev_state.p_state_table->ev_hdlr. + pf_close(p_drv_ctxt, p_cctxt); + } else { + VCD_MSG_ERROR("Unsupported API in device state %d", + p_drv_ctxt->dev_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + return rc; + +} +EXPORT_SYMBOL(vcd_close); + +u32 vcd_encode_start(void *handle) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = + (struct vcd_clnt_ctxt_type_t *)handle; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + u32 rc; + + VCD_MSG_MED("vcd_encode_start:"); + + if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle"); + + return VCD_ERR_BAD_HANDLE; + } + + p_drv_ctxt = vcd_get_drv_context(); + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_encode_start && + p_drv_ctxt->dev_ctxt.e_pwr_state != VCD_PWR_STATE_SLEEP) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_encode_start(p_cctxt); + } else { + VCD_MSG_ERROR + ("Unsupported API in dev power state %d OR client state %d", + p_drv_ctxt->dev_ctxt.e_pwr_state, + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_encode_start); + +u32 vcd_encode_frame(void *handle, struct vcd_frame_data_type *p_input_frame) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = + (struct vcd_clnt_ctxt_type_t *)handle; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + u32 rc; + + VCD_MSG_MED("vcd_encode_frame:"); + + if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle"); + + return VCD_ERR_BAD_HANDLE; + } + + if (!p_input_frame) { + VCD_MSG_ERROR("Bad parameters"); + + return VCD_ERR_BAD_POINTER; + } + + p_drv_ctxt = vcd_get_drv_context(); + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_encode_frame) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_encode_frame(p_cctxt, p_input_frame); + } else { + VCD_MSG_ERROR("Unsupported API in client state %d", + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_encode_frame); + +u32 vcd_decode_start(void *handle, struct vcd_sequence_hdr_type *p_seq_hdr) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = + (struct vcd_clnt_ctxt_type_t *)handle; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + u32 rc; + + VCD_MSG_MED("vcd_decode_start:"); + + if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle"); + + return VCD_ERR_BAD_HANDLE; + } + + p_drv_ctxt = vcd_get_drv_context(); + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_decode_start && + p_drv_ctxt->dev_ctxt.e_pwr_state != VCD_PWR_STATE_SLEEP) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_decode_start(p_cctxt, p_seq_hdr); + } else { + VCD_MSG_ERROR + ("Unsupported API in dev power state %d OR client state %d", + p_drv_ctxt->dev_ctxt.e_pwr_state, + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_decode_start); + +u32 vcd_decode_frame(void *handle, struct vcd_frame_data_type *p_input_frame) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = + (struct vcd_clnt_ctxt_type_t *)handle; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + u32 rc; + + VCD_MSG_MED("vcd_decode_frame:"); + + if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle"); + + return VCD_ERR_BAD_HANDLE; + } + + if (!p_input_frame) { + VCD_MSG_ERROR("Bad parameters"); + + return VCD_ERR_BAD_POINTER; + } + + p_drv_ctxt = vcd_get_drv_context(); + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_decode_frame) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_decode_frame(p_cctxt, p_input_frame); + } else { + VCD_MSG_ERROR("Unsupported API in client state %d", + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_decode_frame); + +u32 vcd_pause(void *handle) +{ + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_clnt_ctxt_type_t *p_cctxt = + (struct vcd_clnt_ctxt_type_t *)handle; + u32 rc; + + VCD_MSG_MED("vcd_pause:"); + + if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle"); + + return VCD_ERR_BAD_HANDLE; + } + + p_drv_ctxt = vcd_get_drv_context(); + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_pause) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_pause(p_cctxt); + } else { + VCD_MSG_ERROR("Unsupported API in client state %d", + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_pause); + +u32 vcd_resume(void *handle) +{ + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_clnt_ctxt_type_t *p_cctxt = + (struct vcd_clnt_ctxt_type_t *)handle; + u32 rc; + + VCD_MSG_MED("vcd_resume:"); + + if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle"); + + return VCD_ERR_BAD_HANDLE; + } + + p_drv_ctxt = vcd_get_drv_context(); + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_drv_ctxt->dev_state.p_state_table->ev_hdlr.pf_resume && + p_drv_ctxt->dev_ctxt.e_pwr_state != VCD_PWR_STATE_SLEEP) { + rc = p_drv_ctxt->dev_state.p_state_table->ev_hdlr. + pf_resume(p_drv_ctxt, p_cctxt); + } else { + VCD_MSG_ERROR + ("Unsupported API in dev power state %d OR client state %d", + p_drv_ctxt->dev_ctxt.e_pwr_state, + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_resume); + +u32 vcd_flush(void *handle, u32 n_mode) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = + (struct vcd_clnt_ctxt_type_t *)handle; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + u32 rc; + + VCD_MSG_MED("vcd_flush:"); + + if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle"); + + return VCD_ERR_BAD_HANDLE; + } + + p_drv_ctxt = vcd_get_drv_context(); + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_flush) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_flush(p_cctxt, n_mode); + } else { + VCD_MSG_ERROR("Unsupported API in client state %d", + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_flush); + +u32 vcd_stop(void *handle) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = + (struct vcd_clnt_ctxt_type_t *)handle; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + u32 rc; + + VCD_MSG_MED("vcd_stop:"); + + if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle"); + + return VCD_ERR_BAD_HANDLE; + } + + p_drv_ctxt = vcd_get_drv_context(); + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_stop && + p_drv_ctxt->dev_ctxt.e_pwr_state != VCD_PWR_STATE_SLEEP) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_stop(p_cctxt); + } else { + VCD_MSG_ERROR + ("Unsupported API in dev power state %d OR client state %d", + p_drv_ctxt->dev_ctxt.e_pwr_state, + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_stop); + +u32 vcd_set_property(void *handle, + struct vcd_property_hdr_type *p_prop_hdr, void *p_prop_val) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = + (struct vcd_clnt_ctxt_type_t *)handle; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + u32 rc; + + VCD_MSG_MED("vcd_set_property:"); + + if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle"); + + return VCD_ERR_BAD_HANDLE; + } + + if (!p_prop_hdr || !p_prop_val) { + VCD_MSG_ERROR("Bad parameters"); + + return VCD_ERR_BAD_POINTER; + } + + p_drv_ctxt = vcd_get_drv_context(); + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_set_property) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_set_property(p_cctxt, p_prop_hdr, p_prop_val); + } else { + VCD_MSG_ERROR("Unsupported API in client state %d", + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_set_property); + +u32 vcd_get_property(void *handle, + struct vcd_property_hdr_type *p_prop_hdr, void *p_prop_val) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = + (struct vcd_clnt_ctxt_type_t *)handle; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + u32 rc; + + VCD_MSG_MED("vcd_get_property:"); + + if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle"); + + return VCD_ERR_BAD_HANDLE; + } + + if (!p_prop_hdr || !p_prop_val) { + VCD_MSG_ERROR("Bad parameters"); + + return VCD_ERR_BAD_POINTER; + } + + p_drv_ctxt = vcd_get_drv_context(); + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_get_property) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_get_property(p_cctxt, p_prop_hdr, p_prop_val); + } else { + VCD_MSG_ERROR("Unsupported API in client state %d", + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_get_property); + +u32 vcd_set_buffer_requirements(void *handle, + enum vcd_buffer_type e_buffer, + struct vcd_buffer_requirement_type *p_buffer_req) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = + (struct vcd_clnt_ctxt_type_t *)handle; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + u32 rc; + + VCD_MSG_MED("vcd_set_buffer_requirements:"); + + if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle"); + + return VCD_ERR_BAD_HANDLE; + } + + if (!p_buffer_req) { + VCD_MSG_ERROR("Bad parameters"); + + return VCD_ERR_BAD_POINTER; + } + + p_drv_ctxt = vcd_get_drv_context(); + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_set_buffer_requirements) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_set_buffer_requirements(p_cctxt, e_buffer, p_buffer_req); + } else { + VCD_MSG_ERROR("Unsupported API in client state %d", + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_set_buffer_requirements); + +u32 vcd_get_buffer_requirements(void *handle, + enum vcd_buffer_type e_buffer, + struct vcd_buffer_requirement_type *p_buffer_req) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = + (struct vcd_clnt_ctxt_type_t *)handle; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + u32 rc; + + VCD_MSG_MED("vcd_get_buffer_requirements:"); + + if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle"); + + return VCD_ERR_BAD_HANDLE; + } + + if (!p_buffer_req) { + VCD_MSG_ERROR("Bad parameters"); + + return VCD_ERR_BAD_POINTER; + } + + p_drv_ctxt = vcd_get_drv_context(); + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_get_buffer_requirements) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_get_buffer_requirements(p_cctxt, e_buffer, p_buffer_req); + } else { + VCD_MSG_ERROR("Unsupported API in client state %d", + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_get_buffer_requirements); + +u32 vcd_set_buffer(void *handle, + enum vcd_buffer_type e_buffer, u8 *p_buffer, u32 n_buf_size) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = + (struct vcd_clnt_ctxt_type_t *)handle; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + u32 rc; + + VCD_MSG_MED("vcd_set_buffer:"); + + if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle"); + + return VCD_ERR_BAD_HANDLE; + } + + if (!p_buffer || !n_buf_size) { + VCD_MSG_ERROR("Bad parameters"); + + return VCD_ERR_BAD_POINTER; + } + + p_drv_ctxt = vcd_get_drv_context(); + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_set_buffer) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_set_buffer(p_cctxt, e_buffer, p_buffer, n_buf_size); + } else { + VCD_MSG_ERROR("Unsupported API in client state %d", + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_set_buffer); + +u32 vcd_allocate_buffer(void *handle, + enum vcd_buffer_type e_buffer, + u32 n_buf_size, u8 **pp_vir_buf_addr, u8 **pp_phy_buf_addr) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = + (struct vcd_clnt_ctxt_type_t *)handle; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + u32 rc; + + VCD_MSG_MED("vcd_allocate_buffer:"); + + if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle"); + + return VCD_ERR_BAD_HANDLE; + } + + if (!pp_vir_buf_addr || !pp_phy_buf_addr + || !n_buf_size) { + VCD_MSG_ERROR("Bad parameters"); + + return VCD_ERR_BAD_POINTER; + } + + p_drv_ctxt = vcd_get_drv_context(); + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_allocate_buffer) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_allocate_buffer(p_cctxt, e_buffer, n_buf_size, + pp_vir_buf_addr, pp_phy_buf_addr); + } else { + VCD_MSG_ERROR("Unsupported API in client state %d", + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_allocate_buffer); + +u32 vcd_free_buffer(void *handle, enum vcd_buffer_type e_buffer, u8 *p_buffer) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = + (struct vcd_clnt_ctxt_type_t *)handle; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + u32 rc; + + VCD_MSG_MED("vcd_free_buffer:"); + + if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle"); + + return VCD_ERR_BAD_HANDLE; + } + + p_drv_ctxt = vcd_get_drv_context(); + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_free_buffer) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_free_buffer(p_cctxt, e_buffer, p_buffer); + } else { + VCD_MSG_ERROR("Unsupported API in client state %d", + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_free_buffer); + +u32 vcd_fill_output_buffer(void *handle, struct vcd_frame_data_type *p_buffer) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = + (struct vcd_clnt_ctxt_type_t *)handle; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + u32 rc; + + VCD_MSG_MED("vcd_fill_output_buffer:"); + + if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle"); + + return VCD_ERR_BAD_HANDLE; + } + + if (!p_buffer) { + VCD_MSG_ERROR("Bad parameters"); + + return VCD_ERR_BAD_POINTER; + } + + p_drv_ctxt = vcd_get_drv_context(); + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_fill_output_buffer) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_fill_output_buffer(p_cctxt, p_buffer); + } else { + VCD_MSG_ERROR("Unsupported API in client state %d", + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_fill_output_buffer); + +u32 vcd_set_device_power(s32 driver_handle, + enum vcd_power_state_type e_pwr_state) +{ + u32 rc = VCD_S_SUCCESS; + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + + VCD_MSG_MED("vcd_set_device_power:"); + + p_drv_ctxt = vcd_get_drv_context(); + + if (!p_drv_ctxt->dev_cs) { + VCD_MSG_ERROR("No critical section object"); + + return VCD_ERR_BAD_STATE; + } + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (p_drv_ctxt->dev_state.p_state_table->ev_hdlr.pf_set_dev_pwr) { + rc = p_drv_ctxt->dev_state.p_state_table->ev_hdlr. + pf_set_dev_pwr(p_drv_ctxt, e_pwr_state); + } else { + VCD_MSG_ERROR("Unsupported API in device state %d", + p_drv_ctxt->dev_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + + return rc; + +} +EXPORT_SYMBOL(vcd_set_device_power); + +void vcd_read_and_clear_interrupt(void) +{ + VCD_MSG_LOW("vcd_read_and_clear_interrupt:"); + ddl_read_and_clear_interrupt(); +} + + +void vcd_response_handler(void) +{ + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + + VCD_MSG_LOW("vcd_response_handler:"); + p_drv_ctxt = vcd_get_drv_context(); + + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + + if (!ddl_process_core_response()) { + VCD_MSG_HIGH + ("ddl_process_core_response indicated no further" + "processing"); + vcd_critical_section_leave(p_drv_ctxt->dev_cs); + return; + } + + if (p_drv_ctxt->dev_ctxt.b_continue) + vcd_continue(); + vcd_critical_section_leave(p_drv_ctxt->dev_cs); +} +EXPORT_SYMBOL(vcd_response_handler); + + + + + + diff --git a/drivers/misc/video_core/720p/vcd/vcd_api.h b/drivers/misc/video_core/720p/vcd/vcd_api.h new file mode 100644 index 0000000000000..12c63fe1bec17 --- /dev/null +++ b/drivers/misc/video_core/720p/vcd/vcd_api.h @@ -0,0 +1,151 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VCD_API_H_ +#define _VCD_API_H_ +#include "vcd_property.h" +#include "vcd_status.h" + +#define VCD_FRAME_FLAG_EOS 0x00000001 +#define VCD_FRAME_FLAG_ENDOFFRAME 0x00000010 +#define VCD_FRAME_FLAG_SYNCFRAME 0x00000020 +#define VCD_FRAME_FLAG_EXTRADATA 0x00000040 +#define VCD_FRAME_FLAG_CODECCONFIG 0x00000080 + +#define VCD_FLUSH_INPUT 0x0001 +#define VCD_FLUSH_OUTPUT 0x0002 +#define VCD_FLUSH_ALL 0x0003 + +#define VCD_FRAMETAG_INVALID 0xffffffff + +struct vcd_handle_container_type { + void *handle; +}; +struct vcd_flush_cmd_type { + u32 n_mode; +}; + +enum vcd_frame_type { + VCD_FRAME_YUV = 1, + VCD_FRAME_I, + VCD_FRAME_P, + VCD_FRAME_B, + VCD_FRAME_32BIT = 0x7fffffff +}; + +enum vcd_power_state_type { + VCD_PWR_STATE_ON = 1, + VCD_PWR_STATE_SLEEP, +}; + +struct vcd_frame_data_type { + u8 *p_virtual; + u8 *p_physical; + u32 n_alloc_len; + u32 n_data_len; + u32 n_offset; + s64 time_stamp; + u32 n_flags; + u32 n_frm_clnt_data; + struct vcd_property_dec_output_buffer_type dec_op_prop; + u32 b_interlaced; + enum vcd_frame_type e_frame_type; + u32 n_ip_frm_tag; +}; + +struct vcd_sequence_hdr_type { + u8 *p_sequence_header; + u32 n_sequence_header_len; + +}; + +enum vcd_buffer_type { + VCD_BUFFER_INPUT = 0x1, + VCD_BUFFER_OUTPUT = 0x2, + VCD_BUFFER_INVALID = 0x3, + VCD_BUFFER_32BIT = 0x7FFFFFFF +}; + +struct vcd_buffer_requirement_type { + u32 n_min_count; + u32 n_actual_count; + u32 n_max_count; + u32 n_size; + u32 n_align; + u32 n_buf_pool_id; +}; + +struct vcd_init_config_type { + void *p_device_name; + void *(*pf_map_dev_base_addr) (void *p_device_name); + void (*pf_un_map_dev_base_addr) (void); + void (*pf_interrupt_clr) (void); + void (*pf_register_isr) (void *p_device_name); + void (*pf_deregister_isr) (void); + u32 (*pf_timer_create) (void (*pf_timer_handler)(void *), + void *p_user_data, void **pp_timer_handle); + void (*pf_timer_release) (void *p_timer_handle); + void (*pf_timer_start) (void *p_timer_handle, u32 n_time_out); + void (*pf_timer_stop) (void *p_timer_handle); +}; + +u32 vcd_init(struct vcd_init_config_type *p_config, s32 *p_driver_handle); +u32 vcd_term(s32 driver_handle); +u32 vcd_open(s32 driver_handle, u32 b_decoding, + void (*callback) (u32 event, u32 status, void *p_info, u32 n_size, + void *handle, void *const p_client_data), void *p_client_data); +u32 vcd_close(void *handle); +u32 vcd_encode_start(void *handle); +u32 vcd_encode_frame(void *handle, struct vcd_frame_data_type *p_input_frame); +u32 vcd_decode_start(void *handle, struct vcd_sequence_hdr_type *p_seq_hdr); +u32 vcd_decode_frame(void *handle, struct vcd_frame_data_type *p_input_frame); +u32 vcd_pause(void *handle); +u32 vcd_resume(void *handle); +u32 vcd_flush(void *handle, u32 n_mode); +u32 vcd_stop(void *handle); +u32 vcd_set_property(void *handle, struct vcd_property_hdr_type *p_prop_hdr, + void *p_prop_val); +u32 vcd_get_property(void *handle, struct vcd_property_hdr_type *p_prop_hdr, + void *p_prop_val); +u32 vcd_set_buffer_requirements(void *handle, enum vcd_buffer_type e_buffer, + struct vcd_buffer_requirement_type *p_buffer_req); +u32 vcd_get_buffer_requirements(void *handle, enum vcd_buffer_type e_buffer, + struct vcd_buffer_requirement_type *p_buffer_req); +u32 vcd_set_buffer(void *handle, enum vcd_buffer_type e_buffer, + u8 *p_buffer, u32 n_buf_size); +u32 vcd_allocate_buffer(void *handle, enum vcd_buffer_type e_buffer, + u32 n_buf_size, u8 **pp_vir_buf_addr, u8 **pp_phy_buf_addr); + +u32 vcd_free_buffer(void *handle, enum vcd_buffer_type e_buffer, u8 *p_buffer); +u32 vcd_fill_output_buffer(void *handle, struct vcd_frame_data_type *p_buffer); +u32 vcd_set_device_power(s32 driver_handle, + enum vcd_power_state_type e_pwr_state); +void vcd_read_and_clear_interrupt(void); +void vcd_response_handler(void); + +#endif diff --git a/drivers/misc/video_core/720p/vcd/vcd_client_sm.c b/drivers/misc/video_core/720p/vcd/vcd_client_sm.c new file mode 100644 index 0000000000000..f1895161a6013 --- /dev/null +++ b/drivers/misc/video_core/720p/vcd/vcd_client_sm.c @@ -0,0 +1,1770 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" +#include "vcd.h" + +static const struct vcd_clnt_state_table_type_t *vcd_clnt_state_table[]; + +void vcd_clnt_handle_device_err_fatal(struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 e_event) +{ + if (p_cctxt->clnt_state.e_state != VCD_CLIENT_STATE_INVALID) { + p_cctxt->callback(e_event, VCD_ERR_HW_FATAL, NULL, 0, + p_cctxt, p_cctxt->p_client_data); + vcd_flush_buffers_in_err_fatal(p_cctxt); + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_INVALID, + CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); + } +} + +static u32 vcd_close_in_open(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_LOW("vcd_close_in_open:"); + if (p_cctxt->in_buf_pool.n_allocated || + p_cctxt->out_buf_pool.n_allocated) { + VCD_MSG_ERROR("\n Allocated buffers are not freed yet"); + return VCD_ERR_ILLEGAL_OP; + } + vcd_destroy_client_context(p_cctxt); + return rc; +} + +static u32 vcd_close_in_invalid(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + VCD_MSG_LOW("vcd_close_in_invalid:"); + if (p_cctxt->in_buf_pool.n_allocated || + p_cctxt->out_buf_pool.n_allocated){ + VCD_MSG_ERROR("Allocated buffers are not freed yet"); + return VCD_ERR_ILLEGAL_OP; + } + + if (p_cctxt->status.b_cleaning_up) + p_cctxt->status.b_close_pending = TRUE; + else + vcd_destroy_client_context(p_cctxt); + return VCD_S_SUCCESS; +} + +static u32 vcd_start_in_run_cmn(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + VCD_MSG_LOW("vcd_start_in_run_cmn:"); + p_cctxt->callback(VCD_EVT_RESP_START, VCD_S_SUCCESS, NULL, 0, + p_cctxt, p_cctxt->p_client_data); + return VCD_S_SUCCESS; + +} + +static u32 vcd_encode_start_in_open(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + u32 rc = VCD_S_SUCCESS; + struct vcd_property_hdr_type prop_hdr; + struct vcd_property_vop_timing_type timing; + + VCD_MSG_LOW("vcd_encode_start_in_open:"); + + if (p_cctxt->b_decoding) { + VCD_MSG_ERROR("vcd_encode_init for decoder client"); + + return VCD_ERR_ILLEGAL_OP; + } + + if (!p_cctxt->in_buf_pool.a_entries || + !p_cctxt->out_buf_pool.a_entries || + p_cctxt->in_buf_pool.n_validated != p_cctxt->in_buf_pool.n_count || + p_cctxt->out_buf_pool.n_validated != + p_cctxt->out_buf_pool.n_count) { + VCD_MSG_ERROR("Buffer pool is not completely setup yet"); + + return VCD_ERR_BAD_STATE; + } + + rc = vcd_add_client_to_sched(p_cctxt); + + VCD_FAILED_RETURN(rc, "Failed: vcd_add_client_to_sched"); + + prop_hdr.prop_id = VCD_I_VOP_TIMING; + prop_hdr.n_size = sizeof(struct vcd_property_vop_timing_type); + rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, &timing); + + VCD_FAILED_RETURN(rc, "Failed: Get VCD_I_VOP_TIMING"); + if (!timing.n_vop_time_resolution) { + VCD_MSG_ERROR("Vop_time_resolution value is zero"); + return VCD_ERR_FAIL; + } + p_cctxt->n_time_resoln = timing.n_vop_time_resolution; + + rc = vcd_process_cmd_sess_start(p_cctxt); + + if (!VCD_FAILED(rc)) { + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_STARTING, + CLIENT_STATE_EVENT_NUMBER + (pf_encode_start)); + } + + return rc; +} + +static u32 vcd_encode_start_in_run(struct vcd_clnt_ctxt_type_t + *p_cctxt) +{ + VCD_MSG_LOW("vcd_encode_start_in_run:"); + (void) vcd_start_in_run_cmn(p_cctxt); + return VCD_S_SUCCESS; +} + + +static u32 vcd_encode_frame_cmn(struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_input_frame) +{ + VCD_MSG_LOW("vcd_encode_frame_cmn in %d:", p_cctxt->clnt_state.e_state); + + if (p_cctxt->b_decoding) { + VCD_MSG_ERROR("vcd_encode_frame for decoder client"); + + return VCD_ERR_ILLEGAL_OP; + } + + return vcd_handle_input_frame(p_cctxt, p_input_frame); +} + +static u32 vcd_decode_start_in_open + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_sequence_hdr_type *p_seq_hdr) +{ + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_LOW("vcd_decode_start_in_open:"); + + if (!p_cctxt->b_decoding) { + VCD_MSG_ERROR("vcd_decode_init for encoder client"); + + return VCD_ERR_ILLEGAL_OP; + } + + if (p_seq_hdr) { + VCD_MSG_HIGH("Seq hdr supplied. len = %d", + p_seq_hdr->n_sequence_header_len); + + rc = vcd_store_seq_hdr(p_cctxt, p_seq_hdr); + + } else { + VCD_MSG_HIGH("Seq hdr not supplied"); + + p_cctxt->seq_hdr.n_sequence_header_len = 0; + p_cctxt->seq_hdr.p_sequence_header = NULL; + } + + VCD_FAILED_RETURN(rc, "Err processing seq hdr"); + + rc = vcd_process_cmd_sess_start(p_cctxt); + + if (!VCD_FAILED(rc)) { + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_STARTING, + CLIENT_STATE_EVENT_NUMBER + (pf_decode_start)); + } + + return rc; +} + +static u32 vcd_decode_start_in_run(struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_sequence_hdr_type *p_seqhdr) +{ + VCD_MSG_LOW("vcd_decode_start_in_run:"); + (void) vcd_start_in_run_cmn(p_cctxt); + return VCD_S_SUCCESS; +} + +static u32 vcd_decode_frame_cmn + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_input_frame) +{ + VCD_MSG_LOW("vcd_decode_frame_cmn in %d:", p_cctxt->clnt_state.e_state); + + if (!p_cctxt->b_decoding) { + VCD_MSG_ERROR("Decode_frame api called for Encoder client"); + + return VCD_ERR_ILLEGAL_OP; + } + + return vcd_handle_input_frame(p_cctxt, p_input_frame); +} + +static u32 vcd_pause_in_run(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_LOW("vcd_pause_in_run:"); + + if (p_cctxt->b_sched_clnt_valid) { + rc = vcd_map_sched_status(sched_suspend_resume_client + (p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, FALSE)); + } + + VCD_FAILED_RETURN(rc, "Failed: sched_suspend_resume_client"); + + if (p_cctxt->status.n_frame_submitted > 0) { + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_PAUSING, + CLIENT_STATE_EVENT_NUMBER + (pf_pause)); + + } else { + VCD_MSG_HIGH("No client frames are currently being processed"); + + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_PAUSED, + CLIENT_STATE_EVENT_NUMBER + (pf_pause)); + + p_cctxt->callback(VCD_EVT_RESP_PAUSE, + VCD_S_SUCCESS, + NULL, 0, p_cctxt, p_cctxt->p_client_data); + + rc = vcd_power_event(p_cctxt->p_dev_ctxt, p_cctxt, + VCD_EVT_PWR_CLNT_PAUSE); + + if (VCD_FAILED(rc)) + VCD_MSG_ERROR("VCD_EVT_PWR_CLNT_PAUSE_END failed"); + + } + + return VCD_S_SUCCESS; +} + +static u32 vcd_resume_in_paused(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_LOW("vcd_resume_in_paused:"); + + if (p_cctxt->b_sched_clnt_valid) { + + rc = vcd_power_event(p_cctxt->p_dev_ctxt, + p_cctxt, VCD_EVT_PWR_CLNT_RESUME); + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("VCD_EVT_PWR_CLNT_RESUME failed"); + } else { + + rc = vcd_map_sched_status(sched_suspend_resume_client + (p_cctxt->p_dev_ctxt-> + sched_hdl, + p_cctxt->sched_clnt_hdl, + TRUE)); + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR + ("rc = 0x%x. Failed: " + "sched_suspend_resume_client", + rc); + } + + } + if (!VCD_FAILED(rc)) + vcd_try_submit_frame(p_dev_ctxt); + } + + if (!VCD_FAILED(rc)) { + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_RUN, + CLIENT_STATE_EVENT_NUMBER + (pf_resume)); + } + + return rc; +} + +static u32 vcd_flush_cmn(struct vcd_clnt_ctxt_type_t *p_cctxt, u32 n_mode) +{ + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_LOW("vcd_flush_cmn in %d:", p_cctxt->clnt_state.e_state); + + rc = vcd_flush_buffers(p_cctxt, n_mode); + + VCD_FAILED_RETURN(rc, "Failed: vcd_flush_buffers"); + + if (p_cctxt->status.n_frame_submitted > 0) { + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_FLUSHING, + CLIENT_STATE_EVENT_NUMBER + (pf_flush)); + } else { + VCD_MSG_HIGH("All buffers are flushed"); + p_cctxt->status.n_flush_mode = n_mode; + vcd_send_flush_done(p_cctxt, VCD_S_SUCCESS); + } + + return rc; +} + +static u32 vcd_flush_inopen(struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 n_mode) +{ + VCD_MSG_LOW("vcd_flush_inopen:"); + p_cctxt->status.n_flush_mode = n_mode; + vcd_send_flush_done(p_cctxt, VCD_S_SUCCESS); + return VCD_S_SUCCESS; +} + +static u32 vcd_flush_in_flushing + (struct vcd_clnt_ctxt_type_t *p_cctxt, u32 n_mode) +{ + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_LOW("vcd_flush_in_flushing:"); + + rc = vcd_flush_buffers(p_cctxt, n_mode); + + return rc; +} + +static u32 vcd_flush_in_eos(struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 n_mode) +{ + VCD_MSG_LOW("vcd_flush_in_eos:"); + + if (n_mode > VCD_FLUSH_ALL || !n_mode) { + VCD_MSG_ERROR("Invalid flush mode %d", n_mode); + + return VCD_ERR_ILLEGAL_PARM; + } + + VCD_MSG_MED("Flush mode requested %d", n_mode); + + p_cctxt->status.n_flush_mode |= n_mode; + + return VCD_S_SUCCESS; +} + +static u32 vcd_flush_in_invalid(struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 mode) +{ + u32 rc = VCD_S_SUCCESS; + VCD_MSG_LOW("vcd_flush_in_invalid:"); + if (!p_cctxt->status.b_cleaning_up) { + rc = vcd_flush_buffers(p_cctxt, mode); + if (!VCD_FAILED(rc)) { + VCD_MSG_HIGH("All buffers are flushed"); + p_cctxt->status.n_flush_mode = mode; + vcd_send_flush_done(p_cctxt, VCD_S_SUCCESS); + } + } + return rc; +} + +static u32 vcd_stop_cmn(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + u32 rc = VCD_S_SUCCESS; + struct vcd_transc_type *p_transc; + + VCD_MSG_LOW("vcd_stop_cmn in %d:", p_cctxt->clnt_state.e_state); + + rc = vcd_flush_buffers(p_cctxt, VCD_FLUSH_ALL); + + VCD_FAILED_RETURN(rc, "Failed: vcd_flush_buffers"); + + if (!p_cctxt->status.n_frame_submitted) { + + if (vcd_get_command_channel(p_dev_ctxt, &p_transc)) { + rc = vcd_power_event(p_dev_ctxt, p_cctxt, + VCD_EVT_PWR_CLNT_CMD_BEGIN); + + if (!VCD_FAILED(rc)) { + p_transc->e_type = VCD_CMD_CODEC_STOP; + p_transc->p_cctxt = p_cctxt; + + rc = vcd_submit_cmd_sess_end(p_transc); + } else { + VCD_MSG_ERROR("Failed:" + " VCD_EVT_PWR_CLNT_CMD_BEGIN"); + } + + if (VCD_FAILED(rc)) { + vcd_release_command_channel(p_dev_ctxt, + p_transc); + } + + } else { + vcd_client_cmd_flush_and_en_q(p_cctxt, + VCD_CMD_CODEC_STOP); + } + } + + if (VCD_FAILED(rc)) { + (void)vcd_power_event(p_dev_ctxt, p_cctxt, + VCD_EVT_PWR_CLNT_CMD_FAIL); + } else { + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_STOPPING, + CLIENT_STATE_EVENT_NUMBER + (pf_stop)); + } + + return rc; +} + + +static u32 vcd_stop_inopen(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + VCD_MSG_LOW("vcd_stop_inopen:"); + + p_cctxt->callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, + NULL, 0, p_cctxt, + p_cctxt->p_client_data); + + return VCD_S_SUCCESS; +} + +static u32 vcd_stop_in_run(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_LOW("vcd_stop_in_run:"); + + rc = vcd_stop_cmn(p_cctxt); + + if (!VCD_FAILED(rc) && p_cctxt->status.b1st_frame_recvd) { + rc = vcd_power_event(p_cctxt->p_dev_ctxt, + p_cctxt, VCD_EVT_PWR_CLNT_LAST_FRAME); + } + + return rc; +} + +static u32 vcd_stop_in_eos(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_LOW("vcd_stop_in_eos:"); + + p_cctxt->status.b_stop_pending = TRUE; + + return rc; +} + +static u32 vcd_stop_in_invalid(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + VCD_MSG_LOW("vcd_stop_in_invalid:"); + if (p_cctxt->status.b_cleaning_up) { + p_cctxt->status.b_stop_pending = TRUE; + } else { + (void) vcd_flush_buffers(p_cctxt, VCD_FLUSH_ALL); + p_cctxt->callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, NULL, + 0, p_cctxt, p_cctxt->p_client_data); + } + return VCD_S_SUCCESS; +} + +static u32 vcd_set_property_cmn + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_property_hdr_type *p_prop_hdr, void *p_prop_val) +{ + u32 rc; + + VCD_MSG_LOW("vcd_set_property_cmn in %d:", p_cctxt->clnt_state.e_state); + VCD_MSG_LOW("property Id = %d", p_prop_hdr->prop_id); + + if (!p_prop_hdr->n_size || !p_prop_hdr->prop_id) { + VCD_MSG_MED("Bad parameters"); + + return VCD_ERR_ILLEGAL_PARM; + } + + rc = ddl_set_property(p_cctxt->ddl_handle, p_prop_hdr, p_prop_val); + + VCD_FAILED_RETURN(rc, "Failed: ddl_set_property"); + + switch (p_prop_hdr->prop_id) { + + case VCD_I_LIVE: + { + struct vcd_property_live_type *p_live = + (struct vcd_property_live_type *)p_prop_val; + + p_cctxt->b_live = p_live->b_live; + + break; + } + + case VCD_I_FRAME_RATE: + { + if (p_cctxt->b_sched_clnt_valid) { + rc = vcd_set_frame_rate(p_cctxt, + (struct vcd_property_frame_rate_type *) + p_prop_val); + } + + break; + } + + case VCD_I_FRAME_SIZE: + { + if (p_cctxt->b_sched_clnt_valid) { + rc = vcd_set_frame_size(p_cctxt, + (struct vcd_property_frame_size_type *) + p_prop_val); + } + + break; + } + + default: + { + break; + } + + } + + return rc; +} + +static u32 vcd_get_property_cmn + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_property_hdr_type *p_prop_hdr, void *p_prop_val) +{ + VCD_MSG_LOW("vcd_get_property_cmn in %d:", p_cctxt->clnt_state.e_state); + VCD_MSG_LOW("property Id = %d", p_prop_hdr->prop_id); + if (!p_prop_hdr->n_size || !p_prop_hdr->prop_id) { + VCD_MSG_MED("Bad parameters"); + + return VCD_ERR_ILLEGAL_PARM; + } + return ddl_get_property(p_cctxt->ddl_handle, p_prop_hdr, p_prop_val); +} + +static u32 vcd_set_buffer_requirements_cmn + (struct vcd_clnt_ctxt_type_t *p_cctxt, + enum vcd_buffer_type e_buffer, + struct vcd_buffer_requirement_type *p_buffer_req) +{ + struct vcd_property_hdr_type Prop_hdr; + u32 rc = VCD_S_SUCCESS; + struct vcd_buffer_pool_type *p_buf_pool; + + VCD_MSG_LOW("vcd_set_buffer_requirements_cmn in %d:", + p_cctxt->clnt_state.e_state); + + if (!p_cctxt->b_decoding && + p_cctxt->clnt_state.e_state != VCD_CLIENT_STATE_OPEN) { + VCD_MSG_ERROR("Bad state (%d) for encoder", + p_cctxt->clnt_state.e_state); + + return VCD_ERR_BAD_STATE; + } + + VCD_MSG_MED("Buffer type = %d", e_buffer); + + if (e_buffer == VCD_BUFFER_INPUT) { + Prop_hdr.prop_id = DDL_I_INPUT_BUF_REQ; + p_buf_pool = &p_cctxt->in_buf_pool; + } else if (e_buffer == VCD_BUFFER_OUTPUT) { + Prop_hdr.prop_id = DDL_I_OUTPUT_BUF_REQ; + p_buf_pool = &p_cctxt->out_buf_pool; + } else { + rc = VCD_ERR_ILLEGAL_PARM; + } + + VCD_FAILED_RETURN(rc, "Invalid buffer type provided"); + + if (p_buf_pool->n_validated > 0) { + VCD_MSG_ERROR("Need to free allocated buffers"); + + return VCD_ERR_ILLEGAL_OP; + } + + Prop_hdr.n_size = sizeof(*p_buffer_req); + + rc = ddl_set_property(p_cctxt->ddl_handle, &Prop_hdr, p_buffer_req); + + VCD_FAILED_RETURN(rc, "Failed: ddl_set_property"); + + if (p_buf_pool->a_entries) { + VCD_MSG_MED("Resetting buffer requirements"); + + vcd_free_buffer_pool_entries(p_buf_pool); + } + + rc = vcd_alloc_buffer_pool_entries(p_buf_pool, p_buffer_req); + + return rc; + +} + +static u32 vcd_get_buffer_requirements_cmn + (struct vcd_clnt_ctxt_type_t *p_cctxt, + enum vcd_buffer_type e_buffer, + struct vcd_buffer_requirement_type *p_buffer_req) +{ + struct vcd_property_hdr_type Prop_hdr; + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_LOW("vcd_get_buffer_requirements_cmn in %d:", + p_cctxt->clnt_state.e_state); + + VCD_MSG_MED("Buffer type = %d", e_buffer); + + if (e_buffer == VCD_BUFFER_INPUT) + Prop_hdr.prop_id = DDL_I_INPUT_BUF_REQ; + else if (e_buffer == VCD_BUFFER_OUTPUT) + Prop_hdr.prop_id = DDL_I_OUTPUT_BUF_REQ; + else + rc = VCD_ERR_ILLEGAL_PARM; + + VCD_FAILED_RETURN(rc, "Invalid buffer type provided"); + + Prop_hdr.n_size = sizeof(*p_buffer_req); + + return ddl_get_property(p_cctxt->ddl_handle, &Prop_hdr, p_buffer_req); + +} + +static u32 vcd_set_buffer_cmn + (struct vcd_clnt_ctxt_type_t *p_cctxt, + enum vcd_buffer_type e_buffer, u8 *p_buffer, u32 n_buf_size) +{ + u32 rc; + struct vcd_buffer_pool_type *p_buf_pool; + + VCD_MSG_LOW("vcd_set_buffer_cmn in %d:", p_cctxt->clnt_state.e_state); + + rc = vcd_common_allocate_set_buffer(p_cctxt, e_buffer, n_buf_size, + &p_buf_pool); + + if (!VCD_FAILED(rc)) { + rc = vcd_set_buffer_internal(p_cctxt, p_buf_pool, p_buffer, + n_buf_size); + } + + return rc; +} + +static u32 vcd_allocate_buffer_cmn + (struct vcd_clnt_ctxt_type_t *p_cctxt, + enum vcd_buffer_type e_buffer, + u32 n_buf_size, u8 **pp_vir_buf_addr, u8 **pp_phy_buf_addr) +{ + u32 rc; + struct vcd_buffer_pool_type *p_buf_pool; + + VCD_MSG_LOW("vcd_allocate_buffer_cmn in %d:", + p_cctxt->clnt_state.e_state); + + rc = vcd_common_allocate_set_buffer(p_cctxt, e_buffer, n_buf_size, + &p_buf_pool); + + if (!VCD_FAILED(rc)) { + rc = vcd_allocate_buffer_internal(p_cctxt, + p_buf_pool, + n_buf_size, + pp_vir_buf_addr, + pp_phy_buf_addr); + } + + return rc; +} + +static u32 vcd_free_buffer_cmn + (struct vcd_clnt_ctxt_type_t *p_cctxt, + enum vcd_buffer_type e_buffer, u8 *p_buffer) +{ + + VCD_MSG_LOW("vcd_free_buffer_cmn in %d:", p_cctxt->clnt_state.e_state); + + return vcd_free_one_buffer_internal(p_cctxt, e_buffer, p_buffer); +} + +static u32 vcd_fill_output_buffer_cmn + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_buffer) +{ + u32 rc = VCD_S_SUCCESS; + struct vcd_buffer_entry_type *p_buf_entry; + struct vcd_frame_data_type *p_frm_entry; + u32 b_q_result = TRUE; + + VCD_MSG_LOW("vcd_fill_output_buffer_cmn in %d:", + p_cctxt->clnt_state.e_state); + + p_buf_entry = vcd_check_fill_output_buffer(p_cctxt, p_buffer); + if (!p_buf_entry) + return VCD_ERR_BAD_POINTER; + + b_q_result = + vcd_buffer_pool_entry_en_q(&p_cctxt->out_buf_pool, p_buf_entry); + + if (!b_q_result && !p_cctxt->b_decoding) { + VCD_MSG_ERROR("Failed: vcd_buffer_pool_entry_en_q"); + + return VCD_ERR_FAIL; + } + + p_frm_entry = &p_buf_entry->frame; + + *p_frm_entry = *p_buffer; + p_frm_entry->p_physical = p_buf_entry->p_physical; + p_frm_entry->n_ip_frm_tag = VCD_FRAMETAG_INVALID; + p_frm_entry->n_data_len = 0; + + if (p_cctxt->b_sched_clnt_valid) { + if (p_cctxt->b_decoding && p_cctxt->status.b1st_frame_recvd) { + struct vcd_property_hdr_type Prop_hdr; + struct ddl_frame_data_type_tag ddl_frm; + + Prop_hdr.prop_id = DDL_I_DPB_RELEASE; + Prop_hdr.n_size = + sizeof(struct ddl_frame_data_type_tag); + + memset(&ddl_frm, 0, sizeof(ddl_frm)); + ddl_frm.vcd_frm = *p_frm_entry; + ddl_frm.n_intrlcd_ip_frm_tag = VCD_FRAMETAG_INVALID; + + rc = ddl_set_property(p_cctxt->ddl_handle, &Prop_hdr, + &ddl_frm); + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("Error returning output buffer to" + " HW. rc = 0x%x", rc); + + p_buf_entry->b_in_use = FALSE; + } else { + p_cctxt->out_buf_pool.n_in_use++; + p_buf_entry->b_in_use = TRUE; + } + } + + if (!VCD_FAILED(rc)) { + rc = vcd_map_sched_status(sched_update_client_o_tkn + (p_cctxt->p_dev_ctxt-> + sched_hdl, + p_cctxt->sched_clnt_hdl, + TRUE, + p_cctxt-> + n_sched_o_tkn_per_ip_frm)); + } + + if (!VCD_FAILED(rc)) + vcd_try_submit_frame(p_cctxt->p_dev_ctxt); + + } + + return rc; +} + +static u32 vcd_fill_output_buffer_in_eos + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_buffer) +{ + u32 rc = VCD_S_SUCCESS; + struct vcd_buffer_entry_type *p_buf_entry; + + VCD_MSG_LOW("vcd_fill_output_buffer_in_eos:"); + + p_buf_entry = vcd_check_fill_output_buffer(p_cctxt, p_buffer); + if (!p_buf_entry) + return VCD_ERR_BAD_POINTER; + + if (p_cctxt->status.b_eos_wait_for_op_buf) { + VCD_MSG_HIGH("Got an output buffer we were waiting for"); + + p_buf_entry->frame = *p_buffer; + + p_buf_entry->frame.n_data_len = 0; + p_buf_entry->frame.n_flags |= VCD_FRAME_FLAG_EOS; + p_buf_entry->frame.n_ip_frm_tag = + p_cctxt->status.eos_trig_ip_frm.n_ip_frm_tag; + p_buf_entry->frame.time_stamp = + p_cctxt->status.eos_trig_ip_frm.time_stamp; + + p_cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, + VCD_S_SUCCESS, + &p_buf_entry->frame, + sizeof(struct vcd_frame_data_type), + p_cctxt, p_cctxt->p_client_data); + + p_cctxt->status.b_eos_wait_for_op_buf = FALSE; + + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_RUN, + CLIENT_STATE_EVENT_NUMBER + (pf_fill_output_buffer)); + + } else { + rc = vcd_fill_output_buffer_cmn(p_cctxt, p_buffer); + } + + return rc; +} + +static void vcd_clnt_cb_in_starting + (struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 event, u32 status, void *p_payload, u32 size, + u32 *ddl_handle, void *const p_client_data) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + struct vcd_transc_type *p_transc = + (struct vcd_transc_type *)p_client_data; + VCD_MSG_LOW("vcd_clnt_cb_in_starting:"); + if (p_cctxt->ddl_handle != ddl_handle) { + VCD_MSG_ERROR("vcd_clnt_cb_in_initing: Wrong DDL handle %p", + ddl_handle); + return; + } + + switch (event) { + case VCD_EVT_RESP_START: + { + vcd_handle_start_done(p_cctxt, + (struct vcd_transc_type *)p_client_data, + status); + break; + } + case VCD_EVT_RESP_STOP: + { + vcd_handle_stop_done_in_starting(p_cctxt, + (struct vcd_transc_type *)p_client_data, + status); + break; + } + case VCD_EVT_IND_HWERRFATAL: + { + p_cctxt->status.n_cmd_submitted--; + vcd_mark_command_channel(p_cctxt->p_dev_ctxt, p_transc); + vcd_handle_err_fatal(p_cctxt, VCD_EVT_RESP_START, + status); + break; + } + default: + { + VCD_MSG_ERROR("Unexpected callback event=%d status=%d " + "from DDL", event, status); + p_dev_ctxt->b_continue = FALSE; + break; + } + } +} + +static void vcd_clnt_cb_in_run + (struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 event, + u32 status, + void *p_payload, u32 n_size, u32 *ddl_handle, void *const p_client_data) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + u32 rc = VCD_S_SUCCESS; + + if (p_cctxt->ddl_handle != ddl_handle) { + VCD_MSG_ERROR("ddl_handle mismatch"); + + return; + } + + switch (event) { + case VCD_EVT_RESP_INPUT_DONE: + { + rc = vcd_handle_input_done(p_cctxt, p_payload, event, + status); + + break; + } + + case VCD_EVT_RESP_OUTPUT_DONE: + { + if (!p_cctxt->status.b1st_op_done_recvd) { + if (!VCD_FAILED(status)) { + rc = vcd_handle_first_frame_done + (p_cctxt, p_payload); + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR + ("rc = 0x%x. Failed: " + "vcd_handle_first_frame_" + "done", rc); + + status = VCD_ERR_FAIL; + } else { + p_cctxt->status. + b1st_op_done_recvd = TRUE; + } + } + } + + rc = vcd_handle_frame_done(p_cctxt, p_payload, event, + status); + + break; + } + case VCD_EVT_RESP_OUTPUT_REQ: + { + rc = vcd_handle_output_required(p_cctxt, p_payload, + status); + break; + } + + case VCD_EVT_IND_RECONFIG: + { + break; + } + case VCD_EVT_RESP_TRANSACTION_PENDING: + { + vcd_handle_trans_pending(p_cctxt); + break; + } + + case VCD_EVT_IND_HWERRFATAL: + { + vcd_handle_ind_hw_err_fatal(p_cctxt, + VCD_EVT_IND_HWERRFATAL, status); + break; + } + default: + { + VCD_MSG_ERROR + ("Unexpected callback event=%d status=%d from DDL", + event, status); + p_dev_ctxt->b_continue = FALSE; + + break; + } + } + + if (!VCD_FAILED(rc) && + (event == VCD_EVT_RESP_INPUT_DONE || + event == VCD_EVT_RESP_OUTPUT_DONE || + event == VCD_EVT_RESP_OUTPUT_REQ)) { + + if (((struct ddl_frame_data_type_tag *) + p_payload)->b_frm_trans_end) + vcd_mark_frame_channel(p_cctxt->p_dev_ctxt); + } +} + +static void vcd_clnt_cb_in_eos + (struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 event, + u32 status, + void *p_payload, u32 n_size, u32 *ddl_handle, void *const p_client_data) { + struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + struct vcd_transc_type *p_transc = NULL; + + if (p_cctxt->ddl_handle != ddl_handle) { + VCD_MSG_ERROR("ddl_handle mismatch"); + + return; + } + + switch (event) { + case VCD_EVT_RESP_INPUT_DONE: + { + vcd_handle_input_done_in_eos(p_cctxt, p_payload, + status); + + break; + } + + case VCD_EVT_RESP_OUTPUT_DONE: + { + vcd_handle_frame_done_in_eos(p_cctxt, p_payload, + status); + + break; + } + case VCD_EVT_RESP_OUTPUT_REQ: + { + (void)vcd_handle_output_required(p_cctxt, p_payload, + status); + break; + } + case VCD_EVT_RESP_EOS_DONE: + { + p_transc = (struct vcd_transc_type *)p_client_data; + + vcd_handle_eos_done(p_cctxt, p_transc, status); + break; + } + case VCD_EVT_IND_HWERRFATAL: + { + vcd_handle_ind_hw_err_fatal(p_cctxt, + VCD_EVT_IND_HWERRFATAL, status); + break; + } + default: + { + VCD_MSG_ERROR + ("Unexpected callback event=%d status=%d from DDL", + event, status); + + p_dev_ctxt->b_continue = FALSE; + + break; + } + + } + if (event == VCD_EVT_RESP_INPUT_DONE || + event == VCD_EVT_RESP_OUTPUT_DONE || + event == VCD_EVT_RESP_OUTPUT_REQ) { + if (p_payload && ((struct ddl_frame_data_type_tag *) + p_payload)->b_frm_trans_end) { + vcd_mark_frame_channel(p_cctxt->p_dev_ctxt); + if (!p_cctxt->status.n_frame_submitted) + vcd_handle_eos_trans_end(p_cctxt); + } + } +} + +static void vcd_clnt_cb_in_flushing + (struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 event, + u32 status, + void *p_payload, u32 size, u32 *ddl_handle, void *const p_client_data) { + struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_LOW("vcd_clnt_cb_in_flushing:"); + + if (p_cctxt->ddl_handle != ddl_handle) { + VCD_MSG_ERROR("ddl_handle mismatch"); + + return; + } + + switch (event) { + case VCD_EVT_RESP_INPUT_DONE: + { + rc = vcd_handle_input_done(p_cctxt, + p_payload, + VCD_EVT_RESP_INPUT_FLUSHED, + status); + + break; + } + + case VCD_EVT_RESP_OUTPUT_DONE: + { + + rc = vcd_handle_frame_done(p_cctxt, + p_payload, + VCD_EVT_RESP_OUTPUT_FLUSHED, + status); + + break; + } + case VCD_EVT_RESP_OUTPUT_REQ: + { + rc = vcd_handle_output_required_in_flushing(p_cctxt, + p_payload); + break; + } + case VCD_EVT_IND_HWERRFATAL: + { + vcd_handle_ind_hw_err_fatal(p_cctxt, + VCD_EVT_IND_HWERRFATAL, status); + break; + } + default: + { + VCD_MSG_ERROR + ("Unexpected callback event=%d status=%d from DDL", + event, status); + + p_dev_ctxt->b_continue = FALSE; + + break; + } + } + if (!VCD_FAILED(rc) && ((event == VCD_EVT_RESP_INPUT_DONE || + event == VCD_EVT_RESP_OUTPUT_DONE) || + event == VCD_EVT_RESP_OUTPUT_REQ)) { + if (((struct ddl_frame_data_type_tag *)p_payload)-> + b_frm_trans_end) { + + vcd_mark_frame_channel(p_cctxt->p_dev_ctxt); + + if (!p_cctxt->status.n_frame_submitted) { + VCD_MSG_HIGH + ("All pending frames recvd from DDL"); + + if (p_cctxt->status. + n_flush_mode & VCD_FLUSH_OUTPUT) { + vcd_flush_output_buffers(p_cctxt); + + vcd_release_all_clnt_frm_transc + (p_cctxt); + + } + + vcd_send_flush_done(p_cctxt, VCD_S_SUCCESS); + vcd_release_interim_frame_channels(p_dev_ctxt); + VCD_MSG_HIGH("Flush complete"); + vcd_release_all_clnt_def_frm_transc(p_cctxt); + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_RUN, + CLIENT_STATE_EVENT_NUMBER + (pf_clnt_cb)); + } + } + } +} + +static void vcd_clnt_cb_in_stopping + (struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 event, + u32 status, + void *p_payload, u32 n_size, u32 *ddl_handle, void *const p_client_data) { + struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_LOW("vcd_clnt_cb_in_stopping:"); + + if (p_cctxt->ddl_handle != ddl_handle) { + VCD_MSG_ERROR("ddl_handle mismatch"); + + return; + } + + switch (event) { + + case VCD_EVT_RESP_INPUT_DONE: + { + rc = vcd_handle_input_done(p_cctxt, + p_payload, + VCD_EVT_RESP_INPUT_FLUSHED, + status); + + break; + } + + case VCD_EVT_RESP_OUTPUT_DONE: + { + + rc = vcd_handle_frame_done(p_cctxt, + p_payload, + VCD_EVT_RESP_OUTPUT_FLUSHED, + status); + + break; + } + case VCD_EVT_RESP_OUTPUT_REQ: + { + rc = vcd_handle_output_required_in_flushing(p_cctxt, + p_payload); + break; + } + case VCD_EVT_RESP_STOP: + { + vcd_handle_stop_done(p_cctxt, + (struct vcd_transc_type *) + p_client_data, status); + + break; + } + case VCD_EVT_IND_HWERRFATAL: + { + vcd_handle_ind_hw_err_fatal(p_cctxt, VCD_EVT_RESP_STOP, + status); + break; + } + + default: + { + VCD_MSG_ERROR + ("Unexpected callback event=%d status=%d from DDL", + event, status); + + p_dev_ctxt->b_continue = FALSE; + + break; + } + } + + if (!VCD_FAILED(rc) && ((event == VCD_EVT_RESP_INPUT_DONE || + event == VCD_EVT_RESP_OUTPUT_DONE) || + event == VCD_EVT_RESP_OUTPUT_REQ)) { + + if (((struct ddl_frame_data_type_tag *)p_payload)-> + b_frm_trans_end) { + + vcd_mark_frame_channel(p_cctxt->p_dev_ctxt); + + if (!p_cctxt->status.n_frame_submitted) { + VCD_MSG_HIGH + ("All pending frames recvd from DDL"); + + vcd_flush_output_buffers(p_cctxt); + + p_cctxt->status.n_flush_mode = 0; + + vcd_release_all_clnt_frm_transc(p_cctxt); + + VCD_MSG_HIGH + ("All buffers flushed. Enqueuing stop cmd"); + + vcd_client_cmd_flush_and_en_q(p_cctxt, + VCD_CMD_CODEC_STOP); + } + + } + } +} + +static void vcd_clnt_cb_in_pausing + (struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 event, + u32 status, + void *p_payload, u32 size, u32 *ddl_handle, void *const p_client_data) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_LOW("vcd_clnt_cb_in_pausing:"); + + if (p_cctxt->ddl_handle != ddl_handle) { + VCD_MSG_ERROR("ddl_handle mismatch"); + + return; + } + + switch (event) { + case VCD_EVT_RESP_INPUT_DONE: + { + rc = vcd_handle_input_done(p_cctxt, p_payload, event, + status); + + break; + } + + case VCD_EVT_RESP_OUTPUT_DONE: + { + rc = vcd_handle_frame_done(p_cctxt, p_payload, event, + status); + break; + } + case VCD_EVT_RESP_OUTPUT_REQ: + { + rc = vcd_handle_output_required(p_cctxt, p_payload, + status); + break; + } + case VCD_EVT_IND_HWERRFATAL: + { + vcd_handle_ind_hw_err_fatal(p_cctxt, + VCD_EVT_RESP_PAUSE, status); + break; + } + default: + { + VCD_MSG_ERROR + ("Unexpected callback event=%d status=%d from DDL", + event, status); + + p_dev_ctxt->b_continue = FALSE; + + break; + } + + } + + if (!VCD_FAILED(rc) && ((event == VCD_EVT_RESP_INPUT_DONE || + event == VCD_EVT_RESP_OUTPUT_DONE) || + event == VCD_EVT_RESP_OUTPUT_REQ)) { + + if (((struct ddl_frame_data_type_tag *)p_payload)-> + b_frm_trans_end) { + + vcd_mark_frame_channel(p_cctxt->p_dev_ctxt); + + if (!p_cctxt->status.n_frame_submitted) { + VCD_MSG_HIGH + ("All pending frames recvd from DDL"); + + p_cctxt->callback(VCD_EVT_RESP_PAUSE, + VCD_S_SUCCESS, + NULL, + 0, + p_cctxt, + p_cctxt->p_client_data); + + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_PAUSED, + CLIENT_STATE_EVENT_NUMBER + (pf_clnt_cb)); + + rc = vcd_power_event(p_cctxt->p_dev_ctxt, + p_cctxt, + VCD_EVT_PWR_CLNT_PAUSE); + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR + ("VCD_EVT_PWR_CLNT_PAUSE_END" + "failed"); + } + } + } + } +} + +static void vcd_clnt_cb_in_invalid( + struct vcd_clnt_ctxt_type_t *p_cctxt, u32 event, u32 status, + void *p_payload, u32 size, u32 *ddl_handle, + void *const p_client_data +) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + VCD_MSG_LOW("vcd_clnt_cb_in_invalid:"); + if (p_cctxt->ddl_handle != ddl_handle) { + VCD_MSG_ERROR("ddl_handle mismatch"); + return; + } + switch (event) { + case VCD_EVT_RESP_STOP: + { + vcd_handle_stop_done_in_invalid(p_cctxt, status); + break; + } + case VCD_EVT_RESP_INPUT_DONE: + case VCD_EVT_RESP_OUTPUT_DONE: + case VCD_EVT_RESP_OUTPUT_REQ: + case VCD_EVT_RESP_TRANSACTION_PENDING: + { + break; + } + case VCD_EVT_IND_HWERRFATAL: + { + if (status == VCD_ERR_HW_FATAL) + vcd_handle_stop_done_in_invalid(p_cctxt, + status); + + break; + } + default: + { + VCD_MSG_ERROR("Unexpected callback event=%d status=%d" + "from DDL", event, status); + p_dev_ctxt->b_continue = FALSE; + break; + } + } +} + +static void vcd_clnt_enter_open + (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { + VCD_MSG_MED("Entering CLIENT_STATE_OPEN on api %d", n_state_event_type); +} + +static void vcd_clnt_enter_starting + (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { + VCD_MSG_MED("Entering CLIENT_STATE_STARTING on api %d", + n_state_event_type); +} + +static void vcd_clnt_enter_run + (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { + VCD_MSG_MED("Entering CLIENT_STATE_RUN on api %d", n_state_event_type); +} + +static void vcd_clnt_enter_flushing + (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { + VCD_MSG_MED("Entering CLIENT_STATE_FLUSHING on api %d", + n_state_event_type); +} + +static void vcd_clnt_enter_stopping + (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { + VCD_MSG_MED("Entering CLIENT_STATE_STOPPING on api %d", + n_state_event_type); +} + +static void vcd_clnt_enter_eos(struct vcd_clnt_ctxt_type_t *p_cctxt, + s32 n_state_event_type) +{ + u32 rc; + + VCD_MSG_MED("Entering CLIENT_STATE_EOS on api %d", n_state_event_type); + rc = vcd_map_sched_status(sched_suspend_resume_client( + p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, FALSE)); + if (VCD_FAILED(rc)) + VCD_MSG_ERROR("Failed: sched_suspend_resume_client." + "rc=0x%x", rc); +} + +static void vcd_clnt_enter_pausing + (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { + VCD_MSG_MED("Entering CLIENT_STATE_PAUSING on api %d", + n_state_event_type); +} + +static void vcd_clnt_enter_paused + (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) +{ + VCD_MSG_MED("Entering CLIENT_STATE_PAUSED on api %d", + n_state_event_type); +} + +static void vcd_clnt_enter_invalid(struct vcd_clnt_ctxt_type_t *p_cctxt, + s32 n_state_event_type) +{ + VCD_MSG_MED("Entering CLIENT_STATE_INVALID on api %d", + n_state_event_type); + + p_cctxt->b_ddl_hdl_valid = FALSE; +} + +static void vcd_clnt_exit_open + (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) +{ + VCD_MSG_MED("Exiting CLIENT_STATE_OPEN on api %d", n_state_event_type); +} + +static void vcd_clnt_exit_starting + (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { + VCD_MSG_MED("Exiting CLIENT_STATE_STARTING on api %d", + n_state_event_type); +} + +static void vcd_clnt_exit_run + (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { + VCD_MSG_MED("Exiting CLIENT_STATE_RUN on api %d", n_state_event_type); +} + +static void vcd_clnt_exit_flushing + (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { + VCD_MSG_MED("Exiting CLIENT_STATE_FLUSHING on api %d", + n_state_event_type); +} + +static void vcd_clnt_exit_stopping + (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { + VCD_MSG_MED("Exiting CLIENT_STATE_STOPPING on api %d", + n_state_event_type); +} + +static void vcd_clnt_exit_eos + (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) +{ + u32 rc; + VCD_MSG_MED("Exiting CLIENT_STATE_EOS on api %d", n_state_event_type); + rc = vcd_map_sched_status(sched_suspend_resume_client( + p_cctxt->p_dev_ctxt->sched_hdl, p_cctxt->sched_clnt_hdl, TRUE)); + if (VCD_FAILED(rc)) + VCD_MSG_ERROR("Failed: sched_suspend_resume_client. rc=0x%x", + rc); +} + +static void vcd_clnt_exit_pausing + (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { + VCD_MSG_MED("Exiting CLIENT_STATE_PAUSING on api %d", + n_state_event_type); +} + +static void vcd_clnt_exit_paused + (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { + VCD_MSG_MED("Exiting CLIENT_STATE_PAUSED on api %d", + n_state_event_type); +} + +static void vcd_clnt_exit_invalid(struct vcd_clnt_ctxt_type_t *p_cctxt, + s32 n_state_event_type) +{ + VCD_MSG_MED("Exiting CLIENT_STATE_INVALID on api %d", + n_state_event_type); +} + +void vcd_do_client_state_transition(struct vcd_clnt_ctxt_type_t *p_cctxt, + enum vcd_clnt_state_enum_type e_to_state, u32 n_ev_code) +{ + struct vcd_clnt_state_ctxt_type_t *p_state_ctxt; + + if (!p_cctxt || e_to_state >= VCD_CLIENT_STATE_MAX) { + VCD_MSG_ERROR("Bad parameters. p_cctxt=%p, e_to_state=%d", + p_cctxt, e_to_state); + } + + p_state_ctxt = &p_cctxt->clnt_state; + + if (p_state_ctxt->e_state == e_to_state) { + VCD_MSG_HIGH("Client already in requested e_to_state=%d", + e_to_state); + + return; + } + + VCD_MSG_MED("vcd_do_client_state_transition: C%d -> C%d, for api %d", + (int)p_state_ctxt->e_state, (int)e_to_state, n_ev_code); + + if (p_state_ctxt->p_state_table->pf_exit) + p_state_ctxt->p_state_table->pf_exit(p_cctxt, n_ev_code); + + + p_state_ctxt->e_state = e_to_state; + p_state_ctxt->p_state_table = vcd_clnt_state_table[e_to_state]; + + if (p_state_ctxt->p_state_table->pf_entry) + p_state_ctxt->p_state_table->pf_entry(p_cctxt, n_ev_code); +} + +const struct vcd_clnt_state_table_type_t *vcd_get_client_state_table + (enum vcd_clnt_state_enum_type e_state) { + return vcd_clnt_state_table[e_state]; +} + +static const struct vcd_clnt_state_table_type_t vcd_clnt_table_open = { + { + vcd_close_in_open, + vcd_encode_start_in_open, + NULL, + vcd_decode_start_in_open, + NULL, + NULL, + NULL, + vcd_flush_inopen, + vcd_stop_inopen, + vcd_set_property_cmn, + vcd_get_property_cmn, + vcd_set_buffer_requirements_cmn, + vcd_get_buffer_requirements_cmn, + vcd_set_buffer_cmn, + vcd_allocate_buffer_cmn, + vcd_free_buffer_cmn, + vcd_fill_output_buffer_cmn, + NULL, + }, + vcd_clnt_enter_open, + vcd_clnt_exit_open +}; + +static const struct vcd_clnt_state_table_type_t vcd_clnt_table_starting = { + { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_clnt_cb_in_starting, + }, + vcd_clnt_enter_starting, + vcd_clnt_exit_starting +}; + +static const struct vcd_clnt_state_table_type_t vcd_clnt_table_run = { + { + NULL, + vcd_encode_start_in_run, + vcd_encode_frame_cmn, + vcd_decode_start_in_run, + vcd_decode_frame_cmn, + vcd_pause_in_run, + NULL, + vcd_flush_cmn, + vcd_stop_in_run, + vcd_set_property_cmn, + vcd_get_property_cmn, + vcd_set_buffer_requirements_cmn, + vcd_get_buffer_requirements_cmn, + vcd_set_buffer_cmn, + vcd_allocate_buffer_cmn, + NULL, + vcd_fill_output_buffer_cmn, + vcd_clnt_cb_in_run, + }, + vcd_clnt_enter_run, + vcd_clnt_exit_run +}; + +static const struct vcd_clnt_state_table_type_t vcd_clnt_table_flushing = { + { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_flush_in_flushing, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_clnt_cb_in_flushing, + }, + vcd_clnt_enter_flushing, + vcd_clnt_exit_flushing +}; + +static const struct vcd_clnt_state_table_type_t vcd_clnt_table_stopping = { + { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_get_property_cmn, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_clnt_cb_in_stopping, + }, + vcd_clnt_enter_stopping, + vcd_clnt_exit_stopping +}; + +static const struct vcd_clnt_state_table_type_t vcd_clnt_table_eos = { + { + NULL, + NULL, + vcd_encode_frame_cmn, + NULL, + vcd_decode_frame_cmn, + NULL, + NULL, + vcd_flush_in_eos, + vcd_stop_in_eos, + NULL, + vcd_get_property_cmn, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_fill_output_buffer_in_eos, + vcd_clnt_cb_in_eos, + }, + vcd_clnt_enter_eos, + vcd_clnt_exit_eos +}; + +static const struct vcd_clnt_state_table_type_t vcd_clnt_table_pausing = { + { + NULL, + NULL, + vcd_encode_frame_cmn, + NULL, + vcd_decode_frame_cmn, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_get_property_cmn, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_fill_output_buffer_cmn, + vcd_clnt_cb_in_pausing, + }, + vcd_clnt_enter_pausing, + vcd_clnt_exit_pausing +}; + +static const struct vcd_clnt_state_table_type_t vcd_clnt_table_paused = { + { + NULL, + NULL, + vcd_encode_frame_cmn, + NULL, + vcd_decode_frame_cmn, + NULL, + vcd_resume_in_paused, + vcd_flush_cmn, + vcd_stop_cmn, + vcd_set_property_cmn, + vcd_get_property_cmn, + vcd_set_buffer_requirements_cmn, + vcd_get_buffer_requirements_cmn, + vcd_set_buffer_cmn, + vcd_allocate_buffer_cmn, + NULL, + vcd_fill_output_buffer_cmn, + NULL, + }, + vcd_clnt_enter_paused, + vcd_clnt_exit_paused +}; +static const struct vcd_clnt_state_table_type_t vcd_clnt_table_invalid = { + { + vcd_close_in_invalid, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_flush_in_invalid, + vcd_stop_in_invalid, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_free_buffer_cmn, + NULL, + vcd_clnt_cb_in_invalid, + }, + vcd_clnt_enter_invalid, + vcd_clnt_exit_invalid +}; + +static const struct vcd_clnt_state_table_type_t *vcd_clnt_state_table[] = { + NULL, + &vcd_clnt_table_open, + &vcd_clnt_table_starting, + &vcd_clnt_table_run, + &vcd_clnt_table_flushing, + &vcd_clnt_table_pausing, + &vcd_clnt_table_paused, + &vcd_clnt_table_stopping, + &vcd_clnt_table_eos, + &vcd_clnt_table_invalid +}; diff --git a/drivers/misc/video_core/720p/vcd/vcd_client_sm.h b/drivers/misc/video_core/720p/vcd/vcd_client_sm.h new file mode 100644 index 0000000000000..c3e37786cf3e6 --- /dev/null +++ b/drivers/misc/video_core/720p/vcd/vcd_client_sm.h @@ -0,0 +1,126 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VCD_CLIENT_SM_H_ +#define _VCD_CLIENT_SM_H_ +#include "vcd_api.h" +#include "vcd_ddl_api.h" + +struct vcd_clnt_state_table_type_t; +struct vcd_clnt_state_ctxt_type_t; +struct vcd_clnt_ctxt_type_t; + +enum vcd_clnt_state_enum_type { + VCD_CLIENT_STATE_NULL = 0, + VCD_CLIENT_STATE_OPEN, + VCD_CLIENT_STATE_STARTING, + VCD_CLIENT_STATE_RUN, + VCD_CLIENT_STATE_FLUSHING, + VCD_CLIENT_STATE_PAUSING, + VCD_CLIENT_STATE_PAUSED, + VCD_CLIENT_STATE_STOPPING, + VCD_CLIENT_STATE_EOS, + VCD_CLIENT_STATE_INVALID, + VCD_CLIENT_STATE_MAX, + VCD_CLIENT_STATE_32BIT = 0x7FFFFFFF +}; + +#define CLIENT_STATE_EVENT_NUMBER(ppf) \ + ((u32 *) (&(((struct vcd_clnt_state_table_type_t*)0)->ev_hdlr.ppf)) - \ + (u32 *) (&(((struct vcd_clnt_state_table_type_t*)0)->ev_hdlr.pf_close)) \ + + 1) + +struct vcd_clnt_state_table_type_t { + struct { + u32(*pf_close) (struct vcd_clnt_ctxt_type_t *p_cctxt); + u32(*pf_encode_start) (struct vcd_clnt_ctxt_type_t *p_cctxt); + u32(*pf_encode_frame) (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_input_frame); + u32(*pf_decode_start) (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_sequence_hdr_type *p_seq_hdr); + u32(*pf_decode_frame) (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_input_frame); + u32(*pf_pause) (struct vcd_clnt_ctxt_type_t *p_cctxt); + u32(*pf_resume) (struct vcd_clnt_ctxt_type_t *p_cctxt); + u32(*pf_flush) (struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 n_mode); + u32(*pf_stop) (struct vcd_clnt_ctxt_type_t *p_cctxt); + u32(*pf_set_property) (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_property_hdr_type *p_prop_hdr, + void *p_prop); + u32(*pf_get_property) (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_property_hdr_type *p_prop_hdr, + void *p_prop); + u32(*pf_set_buffer_requirements) (struct vcd_clnt_ctxt_type_t * + p_cctxt, + enum vcd_buffer_type e_buffer, + struct + vcd_buffer_requirement_type * + p_buffer_req); + u32(*pf_get_buffer_requirements) (struct vcd_clnt_ctxt_type_t * + p_cctxt, + enum vcd_buffer_type e_buffer, + struct + vcd_buffer_requirement_type * + p_buffer_req); + u32(*pf_set_buffer) (struct vcd_clnt_ctxt_type_t *p_cctxt, + enum vcd_buffer_type e_buffer, u8 *p_buffer, + u32 n_buf_size); + u32(*pf_allocate_buffer) (struct vcd_clnt_ctxt_type_t *p_cctxt, + enum vcd_buffer_type e_buffer, u32 n_buf_size, + u8 **pp_vir_buf_addr, u8 **pp_phy_buf_addr); + u32(*pf_free_buffer) (struct vcd_clnt_ctxt_type_t *p_cctxt, + enum vcd_buffer_type e_buffer, u8 *p_buffer); + u32(*pf_fill_output_buffer) ( + struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_buffer); + void (*pf_clnt_cb) (struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 event, u32 status, void *p_payload, + u32 n_size, u32 *ddl_handle, + void *const p_client_data); + } ev_hdlr; + + void (*pf_entry) (struct vcd_clnt_ctxt_type_t *p_cctxt, + s32 n_state_event_type); + void (*pf_exit) (struct vcd_clnt_ctxt_type_t *p_cctxt, + s32 n_state_event_type); +}; + +struct vcd_clnt_state_ctxt_type_t { + const struct vcd_clnt_state_table_type_t *p_state_table; + enum vcd_clnt_state_enum_type e_state; +}; + +extern void vcd_do_client_state_transition + (struct vcd_clnt_ctxt_type_t *p_cctxt, + enum vcd_clnt_state_enum_type e_to_state, u32 n_ev_code); + +extern const struct vcd_clnt_state_table_type_t *vcd_get_client_state_table( + enum vcd_clnt_state_enum_type e_state); + +#endif diff --git a/drivers/misc/video_core/720p/vcd/vcd_core.h b/drivers/misc/video_core/720p/vcd/vcd_core.h new file mode 100644 index 0000000000000..c395b01a3ff5d --- /dev/null +++ b/drivers/misc/video_core/720p/vcd/vcd_core.h @@ -0,0 +1,245 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VCD_CORE_H_ +#define _VCD_CORE_H_ + +#include "vcd_api.h" +#include "vid_frame_scheduler_api.h" +#include "vcd_ddl_api.h" + +#include "vcd_util.h" +#include "vcd_client_sm.h" +#include "vcd_power_sm.h" + +#define VCD_SIGNATURE 0x75017591U + +#define VCD_MIN_PERF_LEVEL 37900 + +#define VCD_MAX_SCHEDULER_QUEUE_DURATION 1 + +#define VCD_MAX_SCHEDULER_QUEUE_SIZE(n_fps_n, n_fps_d) \ + (n_fps_n / n_fps_d * VCD_MAX_SCHEDULER_QUEUE_DURATION) + +#define VCD_SCHEDULER_INITIAL_PERF_LEVEL 108000 + +#define VCD_SCHEDULER_ENC_DFLT_OTKN_PERFRM 1 + +#define VCD_SCHEDULER_DEC_DFLT_OTKN_PERFRM 1 + +#define VCD_DRIVER_INSTANCE_MAX 4 + +#define VCD_MAX_CLIENT_TRANSACTIONS 32 + +#define VCD_SEQ_HDR_PADDING_BYTES 256 + +#define VCD_DEC_NUM_INTERLACED_FIELDS 2 + +#define VCD_TIMESTAMP_RESOLUTION 1000000 +#define VCD_DEC_INITIAL_FRAME_RATE 30 + +#define VCD_S_SCHED_STAT_BASE 0x20000000 +#define VCD_S_SCHED_EOS (VCD_S_SCHED_STAT_BASE + 0x1) +#define VCD_S_SCHED_SLEEP (VCD_S_SCHED_STAT_BASE + 0x2) +#define VCD_S_SCHED_QEMPTY (VCD_S_SCHED_STAT_BASE + 0x3) +#define VCD_S_SCHED_QFULL (VCD_S_SCHED_STAT_BASE + 0x4) + +enum vcd_command_type { + VCD_CMD_NONE, + VCD_CMD_DEVICE_INIT, + VCD_CMD_DEVICE_TERM, + VCD_CMD_DEVICE_RESET, + VCD_CMD_CODEC_START, + VCD_CMD_CODEC_STOP, + VCD_CMD_CODE_FRAME, + VCD_CMD_OUTPUT_FLUSH, + VCD_CMD_CLIENT_CLOSE +}; + +struct vcd_cmd_q_element_type { + enum vcd_command_type e_pending_cmd; +}; + +struct vcd_buffer_entry_type { + u32 b_valid; + u8 *p_alloc; + u8 *p_virtual; + u8 *p_physical; + u32 n_size; + u32 b_allocated; + u32 b_in_use; + struct vcd_frame_data_type frame; + +}; + +struct vcd_buffer_pool_type { + struct vcd_buffer_entry_type *a_entries; + u32 n_count; + struct vcd_buffer_requirement_type buf_req; + u32 n_validated; + u32 n_allocated; + u32 n_in_use; + struct vcd_buffer_entry_type **a_queue; + u16 n_q_len; + u16 n_q_head; + u16 n_q_tail; + +}; + +struct vcd_transc_type { + u32 b_in_use; + enum vcd_command_type e_type; + struct vcd_clnt_ctxt_type_t *p_cctxt; + + struct vcd_buffer_entry_type *p_ip_buf_entry; + + s64 time_stamp; + u32 n_ip_frm_tag; + enum vcd_frame_type e_frame_type; + + struct vcd_buffer_entry_type *p_op_buf_entry; + + u32 b_input_done; + u32 b_frame_done; +}; + +struct vcd_dev_ctxt_type { + u32 b_ddl_cmd_concurrency; + u32 n_ddl_frame_ch_depth; + u32 n_ddl_cmd_ch_depth; + u32 n_ddl_frame_ch_interim; + u32 n_ddl_cmd_ch_interim; + u32 n_ddl_frame_ch_free; + u32 n_ddl_cmd_ch_free; + + void *sched_hdl; + + struct vcd_init_config_type config; + + u32 b_driver_ids[VCD_DRIVER_INSTANCE_MAX]; + u32 n_refs; + u8 *p_device_base_addr; + void *p_hw_timer_handle; + u32 n_hw_time_out; + struct vcd_clnt_ctxt_type_t *p_cctxt_list_head; + + enum vcd_command_type e_pending_cmd; + + u32 b_continue; + + struct vcd_transc_type *a_trans_tbl; + u32 n_trans_tbl_size; + + enum vcd_power_state_type e_pwr_state; + enum vcd_pwr_clk_state_type e_pwr_clk_state; + u32 n_active_clnts; + u32 n_max_perf_lvl; + u32 n_reqd_perf_lvl; + u32 n_curr_perf_lvl; + u32 b_set_perf_lvl_pending; + +}; + +struct vcd_clnt_status_type { + u32 b_req_perf_lvl; + + u32 b1st_frame_recvd; + u32 b1st_ip_done_recvd; + u32 b1st_op_done_recvd; + + u32 n_frame_submitted; + u32 n_frame_delayed; + u32 n_cmd_submitted; + + u32 n_int_field_cnt; + + s64 first_ts; + s64 prev_ts; + u32 n_time_elapsed; + + u32 b_stop_pending; + u32 n_flush_mode; + + u32 b_eos_wait_for_op_buf; + struct vcd_frame_data_type eos_trig_ip_frm; + + u32 b_eos_prev_valid; + struct ddl_frame_data_type_tag eos_prev_op_frm; + u32 e_last_err; + u32 e_last_evt; + u32 b_cleaning_up; + u32 b_close_pending; +}; + +struct vcd_clnt_ctxt_type_t { + u32 n_signature; + struct vcd_clnt_state_ctxt_type_t clnt_state; + + s32 driver_id; + + u32 b_live; + u32 b_decoding; + + struct vcd_property_frame_rate_type frm_rate; + u32 n_frm_p_units; + u32 n_reqd_perf_lvl; + u32 n_time_resoln; + + struct vcd_buffer_pool_type in_buf_pool; + struct vcd_buffer_pool_type out_buf_pool; + + void (*callback) (u32 event, u32 status, void *p_info, u32 n_size, + void *handle, void *const p_client_data); + void *p_client_data; + + u32 b_sched_clnt_valid; + void *sched_clnt_hdl; + u32 n_sched_o_tkn_per_ip_frm; + u32 b_ddl_hdl_valid; + u32 *ddl_handle; + struct vcd_dev_ctxt_type *p_dev_ctxt; + struct vcd_cmd_q_element_type cmd_q; + struct vcd_sequence_hdr_type seq_hdr; + u8 *p_seq_hdr_phy_addr; + struct vcd_clnt_status_type status; + + struct vcd_clnt_ctxt_type_t *p_next; +}; + +#define VCD_BUFFERPOOL_INUSE_DECREMENT(val) \ +do { \ + if ((val) > 0) \ + val--; \ + else { \ + VCD_MSG_ERROR("%s(): Inconsistent val given in " \ + " VCD_BUFFERPOOL_INUSE_DECREMENT\n", __func__); \ + vcd_assert(); \ + } \ +} while (0) + +#endif diff --git a/drivers/misc/video_core/720p/vcd/vcd_device_sm.c b/drivers/misc/video_core/720p/vcd/vcd_device_sm.c new file mode 100644 index 0000000000000..90eb22cefd696 --- /dev/null +++ b/drivers/misc/video_core/720p/vcd/vcd_device_sm.c @@ -0,0 +1,1218 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" +#include "vcd.h" + +static const struct vcd_dev_state_table_type_t *vcd_dev_state_table[]; +static const struct vcd_dev_state_table_type_t vcd_dev_table_null; + +struct vcd_drv_ctxt_type_t *vcd_get_drv_context(void) +{ + static struct vcd_drv_ctxt_type_t drv_context = { + {&vcd_dev_table_null, VCD_DEVICE_STATE_NULL}, + {0}, + 0 + }; + + return &drv_context; + +} + +void vcd_do_device_state_transition(struct vcd_drv_ctxt_type_t *p_drv_ctxt, + enum vcd_dev_state_enum_type e_to_state, u32 n_ev_code) +{ + struct vcd_dev_state_ctxt_type_t *p_state_ctxt; + + if (!p_drv_ctxt || e_to_state >= VCD_DEVICE_STATE_MAX) { + VCD_MSG_ERROR("Bad parameters. p_drv_ctxt=%p, e_to_state=%d", + p_drv_ctxt, e_to_state); + } + + p_state_ctxt = &p_drv_ctxt->dev_state; + + if (p_state_ctxt->e_state == e_to_state) { + VCD_MSG_HIGH("Device already in requested e_to_state=%d", + e_to_state); + + return; + } + + VCD_MSG_MED("vcd_do_device_state_transition: D%d -> D%d, for api %d", + (int)p_state_ctxt->e_state, (int)e_to_state, n_ev_code); + + if (p_state_ctxt->p_state_table->pf_exit) + p_state_ctxt->p_state_table->pf_exit(p_drv_ctxt, n_ev_code); + + + p_state_ctxt->e_state = e_to_state; + p_state_ctxt->p_state_table = vcd_dev_state_table[e_to_state]; + + if (p_state_ctxt->p_state_table->pf_entry) + p_state_ctxt->p_state_table->pf_entry(p_drv_ctxt, n_ev_code); +} + +void vcd_hw_timeout_handler(void *p_user_data) +{ + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + + VCD_MSG_HIGH("vcd_hw_timeout_handler:"); + p_user_data = NULL; + p_drv_ctxt = vcd_get_drv_context(); + vcd_critical_section_enter(p_drv_ctxt->dev_cs); + if (p_drv_ctxt->dev_state.p_state_table->ev_hdlr.pf_timeout) + p_drv_ctxt->dev_state.p_state_table->ev_hdlr. + pf_timeout(p_drv_ctxt, p_user_data); + else + VCD_MSG_ERROR("hw_timeout unsupported in device state %d", + p_drv_ctxt->dev_state.e_state); + vcd_critical_section_leave(p_drv_ctxt->dev_cs); +} + +void vcd_ddl_callback(u32 event, u32 status, void *p_payload, + u32 n_size, u32 *ddl_handle, void *const p_client_data) +{ + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_dev_ctxt_type *p_dev_ctxt; + struct vcd_dev_state_ctxt_type_t *p_dev_state; + struct vcd_clnt_ctxt_type_t *p_cctxt; + struct vcd_transc_type *p_transc; + + VCD_MSG_LOW("vcd_ddl_callback:"); + + VCD_MSG_LOW("event=0x%x status=0x%x", event, status); + + p_drv_ctxt = vcd_get_drv_context(); + p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + p_dev_state = &p_drv_ctxt->dev_state; + + p_dev_ctxt->b_continue = TRUE; + vcd_device_timer_stop(p_dev_ctxt); + + switch (p_dev_state->e_state) { + case VCD_DEVICE_STATE_NULL: + { + VCD_MSG_HIGH("Callback unexpected in NULL state"); + break; + } + + case VCD_DEVICE_STATE_NOT_INIT: + { + VCD_MSG_HIGH("Callback unexpected in NOT_INIT state"); + break; + } + + case VCD_DEVICE_STATE_INITING: + { + if (p_dev_state->p_state_table->ev_hdlr.pf_dev_cb) { + p_dev_state->p_state_table->ev_hdlr. + pf_dev_cb(p_drv_ctxt, event, status, + p_payload, n_size, ddl_handle, + p_client_data); + } else { + VCD_MSG_HIGH("No device handler in %d state", + p_dev_state->e_state); + } + break; + } + + case VCD_DEVICE_STATE_READY: + { + p_transc = (struct vcd_transc_type *)p_client_data; + + if (!p_transc || !p_transc->b_in_use + || !p_transc->p_cctxt) { + VCD_MSG_ERROR("Invalid clientdata " + "received from DDL "); + } else { + p_cctxt = p_transc->p_cctxt; + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_clnt_cb) { + p_cctxt->clnt_state.p_state_table-> + ev_hdlr.pf_clnt_cb(p_cctxt, + event, status, p_payload, + n_size, ddl_handle, + p_client_data); + } else { + VCD_MSG_HIGH + ("No client handler in" + " (dsm:READY, csm:%d) state", + (int)p_cctxt->clnt_state.e_state); + + if (VCD_FAILED(status)) { + VCD_MSG_FATAL("DDL callback" + " returned failure 0x%x", + status); + } + } + } + break; + } + + default: + { + VCD_MSG_ERROR("Unknown state"); + break; + } + + } + +} + +u32 vcd_init_device_context(struct vcd_drv_ctxt_type_t *p_drv_ctxt, + u32 n_ev_code) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + struct sched_init_param_type sched_init; + u32 rc; + struct ddl_init_config_type ddl_init; + + VCD_MSG_LOW("vcd_init_device_context:"); + + p_dev_ctxt->e_pending_cmd = VCD_CMD_NONE; + + rc = vcd_power_event(p_dev_ctxt, NULL, VCD_EVT_PWR_DEV_INIT_BEGIN); + VCD_FAILED_RETURN(rc, "VCD_EVT_PWR_DEV_INIT_BEGIN failed"); + + VCD_MSG_HIGH("Device powered ON and clocked"); + + sched_init.n_perf_lvl = p_dev_ctxt->n_max_perf_lvl; + rc = vcd_map_sched_status(sched_create + (&sched_init, &p_dev_ctxt->sched_hdl)); + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("rc = 0x%x. Failed: sched_create", rc); + + (void)vcd_power_event(p_dev_ctxt, NULL, + VCD_EVT_PWR_DEV_INIT_FAIL); + + return rc; + } + + VCD_MSG_HIGH("Created scheduler instance."); + + ddl_init.p_core_virtual_base_addr = p_dev_ctxt->p_device_base_addr; + ddl_init.pf_interrupt_clr = p_dev_ctxt->config.pf_interrupt_clr; + ddl_init.ddl_callback = vcd_ddl_callback; + + rc = ddl_device_init(&ddl_init, NULL); + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("rc = 0x%x. Failed: ddl_device_init", rc); + + (void)sched_destroy(p_dev_ctxt->sched_hdl); + p_dev_ctxt->sched_hdl = NULL; + + (void)vcd_power_event(p_dev_ctxt, NULL, + VCD_EVT_PWR_DEV_INIT_FAIL); + } else { + vcd_device_timer_start(p_dev_ctxt); + vcd_do_device_state_transition(p_drv_ctxt, + VCD_DEVICE_STATE_INITING, + n_ev_code); + } + + return rc; +} + +void vcd_handle_device_init_failed(struct vcd_drv_ctxt_type_t *p_drv_ctxt, + u32 status) +{ + struct vcd_clnt_ctxt_type_t *p_client; + struct vcd_clnt_ctxt_type_t *p_tmp_client; + + VCD_MSG_ERROR("Device init failed. status = %d", status); + + p_client = p_drv_ctxt->dev_ctxt.p_cctxt_list_head; + while (p_client) { + p_client->callback(VCD_EVT_RESP_OPEN, + status, NULL, 0, 0, p_client->p_client_data); + + p_tmp_client = p_client; + p_client = p_client->p_next; + + vcd_destroy_client_context(p_tmp_client); + } + if (ddl_device_release(NULL)) + VCD_MSG_ERROR("Failed: ddl_device_release"); + + (void)sched_destroy(p_drv_ctxt->dev_ctxt.sched_hdl); + p_drv_ctxt->dev_ctxt.sched_hdl = NULL; + + if (vcd_power_event(&p_drv_ctxt->dev_ctxt, + NULL, VCD_EVT_PWR_DEV_INIT_FAIL)) + VCD_MSG_ERROR("VCD_EVT_PWR_DEV_INIT_FAIL failed"); + + vcd_do_device_state_transition(p_drv_ctxt, + VCD_DEVICE_STATE_NOT_INIT, + DEVICE_STATE_EVENT_NUMBER(pf_dev_cb)); +} + +u32 vcd_deinit_device_context(struct vcd_drv_ctxt_type_t *p_drv_ctxt, + u32 n_ev_code) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_LOW("vcd_deinit_device_context:"); + + rc = vcd_power_event(&p_drv_ctxt->dev_ctxt, NULL, + VCD_EVT_PWR_DEV_TERM_BEGIN); + + VCD_FAILED_RETURN(rc, "VCD_EVT_PWR_DEV_TERM_BEGIN failed"); + + rc = ddl_device_release(NULL); + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("rc = 0x%x. Failed: ddl_device_release", rc); + + (void)vcd_power_event(p_dev_ctxt, NULL, + VCD_EVT_PWR_DEV_TERM_FAIL); + } else { + (void)sched_destroy(p_dev_ctxt->sched_hdl); + p_dev_ctxt->sched_hdl = NULL; + + (void) vcd_power_event(p_dev_ctxt, NULL, + VCD_EVT_PWR_DEV_TERM_END); + + vcd_do_device_state_transition(p_drv_ctxt, + VCD_DEVICE_STATE_NOT_INIT, n_ev_code); + } + return rc; +} + +void vcd_term_driver_context(struct vcd_drv_ctxt_type_t *p_drv_ctxt) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + + VCD_MSG_HIGH("All driver instances terminated"); + + if (p_dev_ctxt->config.pf_deregister_isr) + p_dev_ctxt->config.pf_deregister_isr(); + + if (p_dev_ctxt->config.pf_un_map_dev_base_addr) + p_dev_ctxt->config.pf_un_map_dev_base_addr(); + + if (p_dev_ctxt->config.pf_timer_release) + p_dev_ctxt->config.pf_timer_release( + p_dev_ctxt->p_hw_timer_handle); + + vcd_free(p_dev_ctxt->a_trans_tbl); + + memset(p_dev_ctxt, 0, sizeof(struct vcd_dev_ctxt_type)); + + vcd_do_device_state_transition(p_drv_ctxt, + VCD_DEVICE_STATE_NULL, + DEVICE_STATE_EVENT_NUMBER(pf_term)); + +} + +u32 vcd_reset_device_context(struct vcd_drv_ctxt_type_t *p_drv_ctxt, + u32 ev_code) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_LOW("vcd_reset_device_context:"); + vcd_reset_device_channels(p_dev_ctxt); + rc = vcd_power_event(&p_drv_ctxt->dev_ctxt, NULL, + VCD_EVT_PWR_DEV_TERM_BEGIN); + VCD_FAILED_RETURN(rc, "VCD_EVT_PWR_DEV_TERM_BEGIN failed"); + if (ddl_reset_hw(0)) + VCD_MSG_HIGH("HW Reset done"); + else + VCD_MSG_FATAL("HW Reset failed"); + + (void)vcd_power_event(p_dev_ctxt, NULL, VCD_EVT_PWR_DEV_TERM_END); + + return VCD_S_SUCCESS; +} + +void vcd_handle_device_err_fatal(struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_clnt_ctxt_type_t *p_trig_clnt) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = p_dev_ctxt->p_cctxt_list_head; + VCD_MSG_LOW("vcd_handle_device_err_fatal:"); + while (p_cctxt) { + if (p_cctxt != p_trig_clnt) { + vcd_clnt_handle_device_err_fatal(p_cctxt, + VCD_EVT_IND_HWERRFATAL); + } + p_cctxt = p_cctxt->p_next; + } + p_dev_ctxt->e_pending_cmd = VCD_CMD_DEVICE_RESET; + vcd_do_device_state_transition(vcd_get_drv_context(), + VCD_DEVICE_STATE_INVALID, + DEVICE_STATE_EVENT_NUMBER(pf_dev_cb)); +} + +void vcd_handle_for_last_clnt_close( + struct vcd_dev_ctxt_type *p_dev_ctxt, u32 b_send_deinit) +{ + if (!p_dev_ctxt->p_cctxt_list_head) { + VCD_MSG_HIGH("All clients are closed"); + if (b_send_deinit) + (void) vcd_deinit_device_context( + vcd_get_drv_context(), + DEVICE_STATE_EVENT_NUMBER(pf_close)); + else + p_dev_ctxt->e_pending_cmd = + VCD_CMD_DEVICE_TERM; + } +} +void vcd_continue(void) +{ + struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_dev_ctxt_type *p_dev_ctxt; + u32 b_continue; + struct vcd_transc_type *p_transc; + u32 rc; + VCD_MSG_LOW("vcd_continue:"); + + p_drv_ctxt = vcd_get_drv_context(); + p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + + p_dev_ctxt->b_continue = FALSE; + + if (p_dev_ctxt->e_pending_cmd == VCD_CMD_DEVICE_INIT) { + VCD_MSG_HIGH("VCD_CMD_DEVICE_INIT is pending"); + + p_dev_ctxt->e_pending_cmd = VCD_CMD_NONE; + + (void)vcd_init_device_context(p_drv_ctxt, + DEVICE_STATE_EVENT_NUMBER(pf_open)); + } else if (p_dev_ctxt->e_pending_cmd == VCD_CMD_DEVICE_TERM) { + VCD_MSG_HIGH("VCD_CMD_DEVICE_TERM is pending"); + + p_dev_ctxt->e_pending_cmd = VCD_CMD_NONE; + + (void)vcd_deinit_device_context(p_drv_ctxt, + DEVICE_STATE_EVENT_NUMBER(pf_close)); + } else if (p_dev_ctxt->e_pending_cmd == VCD_CMD_DEVICE_RESET) { + VCD_MSG_HIGH("VCD_CMD_DEVICE_RESET is pending"); + p_dev_ctxt->e_pending_cmd = VCD_CMD_NONE; + (void)vcd_reset_device_context(p_drv_ctxt, + DEVICE_STATE_EVENT_NUMBER(pf_dev_cb)); + } else { + if (p_dev_ctxt->b_set_perf_lvl_pending) { + rc = vcd_power_event(p_dev_ctxt, NULL, + VCD_EVT_PWR_DEV_SET_PERFLVL); + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR + ("VCD_EVT_PWR_CLNT_SET_PERFLVL failed"); + VCD_MSG_HIGH + ("Not running at desired perf level." + "curr=%d, reqd=%d", + p_dev_ctxt->n_curr_perf_lvl, + p_dev_ctxt->n_reqd_perf_lvl); + } else { + p_dev_ctxt->b_set_perf_lvl_pending = FALSE; + } + } + + do { + b_continue = FALSE; + + if (vcd_get_command_channel_in_loop + (p_dev_ctxt, &p_transc)) { + if (vcd_submit_command_in_continue(p_dev_ctxt, + p_transc)) + b_continue = TRUE; + else { + VCD_MSG_MED + ("No more commands to submit"); + + vcd_release_command_channel(p_dev_ctxt, + p_transc); + + vcd_release_interim_command_channels + (p_dev_ctxt); + } + } + } while (b_continue); + + do { + b_continue = FALSE; + + if (vcd_get_frame_channel_in_loop + (p_dev_ctxt, &p_transc)) { + if (vcd_try_submit_frame_in_continue(p_dev_ctxt, + p_transc)) { + b_continue = TRUE; + } else { + VCD_MSG_MED("No more frames to submit"); + + vcd_release_frame_channel(p_dev_ctxt, + p_transc); + + vcd_release_interim_frame_channels + (p_dev_ctxt); + } + } + + } while (b_continue); + + if (!vcd_core_is_busy(p_dev_ctxt)) { + rc = vcd_power_event(p_dev_ctxt, NULL, + VCD_EVT_PWR_CLNT_CMD_END); + + if (VCD_FAILED(rc)) + VCD_MSG_ERROR("Failed:" + "VCD_EVT_PWR_CLNT_CMD_END"); + } + } +} + +static void vcd_pause_all_sessions(struct vcd_dev_ctxt_type *p_dev_ctxt) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = p_dev_ctxt->p_cctxt_list_head; + u32 rc; + + while (p_cctxt) { + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_pause) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_pause(p_cctxt); + + if (VCD_FAILED(rc)) + VCD_MSG_ERROR("Client pause failed"); + + } + + p_cctxt = p_cctxt->p_next; + } +} + +static void vcd_resume_all_sessions(struct vcd_dev_ctxt_type *p_dev_ctxt) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = p_dev_ctxt->p_cctxt_list_head; + u32 rc; + + while (p_cctxt) { + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_resume) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_resume(p_cctxt); + + if (VCD_FAILED(rc)) + VCD_MSG_ERROR("Client resume failed"); + + } + + p_cctxt = p_cctxt->p_next; + } +} + +static u32 vcd_init_cmn + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + struct vcd_init_config_type *p_config, s32 *p_driver_handle) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + s32 driver_id; + + if (p_dev_ctxt->config.pf_interrupt_clr != + p_config->pf_interrupt_clr + || p_dev_ctxt->config.pf_register_isr != + p_config->pf_register_isr + || p_dev_ctxt->config.pf_deregister_isr != + p_config->pf_deregister_isr + || p_dev_ctxt->config.pf_map_dev_base_addr != + p_config->pf_map_dev_base_addr + || p_dev_ctxt->config.pf_un_map_dev_base_addr != + p_config->pf_un_map_dev_base_addr) { + VCD_MSG_ERROR("Device config mismatch"); + VCD_MSG_HIGH("VCD will be using config from 1st vcd_init"); + } + + *p_driver_handle = 0; + + driver_id = 0; + while (driver_id < VCD_DRIVER_INSTANCE_MAX && + p_dev_ctxt->b_driver_ids[driver_id]) { + ++driver_id; + } + + if (driver_id == VCD_DRIVER_INSTANCE_MAX) { + VCD_MSG_ERROR("Max driver instances reached"); + + return VCD_ERR_FAIL; + } + + ++p_dev_ctxt->n_refs; + p_dev_ctxt->b_driver_ids[driver_id] = TRUE; + *p_driver_handle = driver_id + 1; + + VCD_MSG_HIGH("Driver_id = %d. No of driver instances = %d", + driver_id, p_dev_ctxt->n_refs); + + return VCD_S_SUCCESS; + +} + +static u32 vcd_init_in_null + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + struct vcd_init_config_type *p_config, s32 *p_driver_handle) { + u32 rc = VCD_S_SUCCESS; + struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + u32 b_done_create_timer = FALSE; + VCD_MSG_LOW("vcd_init_in_dev_null:"); + + + p_dev_ctxt->config = *p_config; + + p_dev_ctxt->p_device_base_addr = + (u8 *)p_config->pf_map_dev_base_addr( + p_dev_ctxt->config.p_device_name); + + if (!p_dev_ctxt->p_device_base_addr) { + VCD_MSG_ERROR("NULL Device_base_addr"); + + return VCD_ERR_FAIL; + } + + if (p_config->pf_register_isr) { + p_config->pf_register_isr(p_dev_ctxt->config. + p_device_name); + } + + if (p_config->pf_timer_create) { + if (p_config->pf_timer_create(vcd_hw_timeout_handler, + NULL, &p_dev_ctxt->p_hw_timer_handle)) + b_done_create_timer = TRUE; + else { + VCD_MSG_ERROR("timercreate failed"); + return VCD_ERR_FAIL; + } + } + + + rc = vcd_init_cmn(p_drv_ctxt, p_config, p_driver_handle); + + if (!VCD_FAILED(rc)) { + vcd_do_device_state_transition(p_drv_ctxt, + VCD_DEVICE_STATE_NOT_INIT, + DEVICE_STATE_EVENT_NUMBER + (pf_init)); + } else { + if (p_dev_ctxt->config.pf_un_map_dev_base_addr) + p_dev_ctxt->config.pf_un_map_dev_base_addr(); + + if (p_dev_ctxt->config.pf_deregister_isr) + p_dev_ctxt->config.pf_deregister_isr(); + + if (b_done_create_timer && p_dev_ctxt->config.pf_timer_release) + p_dev_ctxt->config.pf_timer_release(p_dev_ctxt-> + p_hw_timer_handle); + + } + + return rc; + +} + +static u32 vcd_init_in_not_init + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + struct vcd_init_config_type *p_config, s32 *p_driver_handle) +{ + + VCD_MSG_LOW("vcd_init_in_dev_not_init:"); + + return vcd_init_cmn(p_drv_ctxt, p_config, p_driver_handle); + +} + +static u32 vcd_init_in_initing + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + struct vcd_init_config_type *p_config, s32 *p_driver_handle) { + + VCD_MSG_LOW("vcd_init_in_dev_initing:"); + + return vcd_init_cmn(p_drv_ctxt, p_config, p_driver_handle); + +} + +static u32 vcd_init_in_ready + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + struct vcd_init_config_type *p_config, s32 *p_driver_handle) +{ + VCD_MSG_LOW("vcd_init_in_dev_ready:"); + + return vcd_init_cmn(p_drv_ctxt, p_config, p_driver_handle); +} + +static u32 vcd_term_cmn + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 driver_handle) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + + if (!vcd_validate_driver_handle(p_dev_ctxt, driver_handle)) { + VCD_MSG_ERROR("Invalid driver handle = %d", driver_handle); + + return VCD_ERR_BAD_HANDLE; + } + + if (vcd_check_for_client_context(p_dev_ctxt, + driver_handle - 1)) { + VCD_MSG_ERROR("Driver has active client"); + + return VCD_ERR_BAD_STATE; + } + + --p_dev_ctxt->n_refs; + p_dev_ctxt->b_driver_ids[driver_handle - 1] = FALSE; + + VCD_MSG_HIGH("Driver_id %d terminated. No of driver instances = %d", + driver_handle - 1, p_dev_ctxt->n_refs); + + return VCD_S_SUCCESS; +} + +static u32 vcd_term_in_not_init + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 driver_handle) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + u32 rc; + + VCD_MSG_LOW("vcd_term_in_dev_not_init:"); + + rc = vcd_term_cmn(p_drv_ctxt, driver_handle); + + if (!VCD_FAILED(rc) && !p_dev_ctxt->n_refs) + vcd_term_driver_context(p_drv_ctxt); + + return rc; +} + +static u32 vcd_term_in_initing + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 driver_handle) +{ + VCD_MSG_LOW("vcd_term_in_dev_initing:"); + + return vcd_term_cmn(p_drv_ctxt, driver_handle); +} + +static u32 vcd_term_in_ready + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 driver_handle) +{ + VCD_MSG_LOW("vcd_term_in_dev_ready:"); + + return vcd_term_cmn(p_drv_ctxt, driver_handle); +} + +static u32 vcd_term_in_invalid(struct vcd_drv_ctxt_type_t *p_drv_ctxt, + s32 driver_handle) +{ + u32 rc; + VCD_MSG_LOW("vcd_term_in_invalid:"); + rc = vcd_term_cmn(p_drv_ctxt, driver_handle); + if (!VCD_FAILED(rc) && !p_drv_ctxt->dev_ctxt.n_refs) + vcd_term_driver_context(p_drv_ctxt); + + return rc; +} + +static u32 vcd_open_cmn + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + s32 driver_handle, + u32 b_decoding, + void (*callback) (u32 event, u32 status, void *p_info, u32 n_size, + void *handle, void *const p_client_data), + void *p_client_data, struct vcd_clnt_ctxt_type_t ** pp_cctxt) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + struct vcd_clnt_ctxt_type_t *p_cctxt; + struct vcd_clnt_ctxt_type_t *p_client; + + if (!vcd_validate_driver_handle(p_dev_ctxt, driver_handle)) { + VCD_MSG_ERROR("Invalid driver handle = %d", driver_handle); + + return VCD_ERR_BAD_HANDLE; + } + + p_cctxt = + (struct vcd_clnt_ctxt_type_t *) + vcd_malloc(sizeof(struct vcd_clnt_ctxt_type_t)); + if (!p_cctxt) { + VCD_MSG_ERROR("No memory for client ctxt"); + + return VCD_ERR_ALLOC_FAIL; + } + + memset(p_cctxt, 0, sizeof(struct vcd_clnt_ctxt_type_t)); + p_cctxt->p_dev_ctxt = p_dev_ctxt; + p_cctxt->driver_id = driver_handle - 1; + p_cctxt->b_decoding = b_decoding; + p_cctxt->callback = callback; + p_cctxt->p_client_data = p_client_data; + + p_client = p_dev_ctxt->p_cctxt_list_head; + p_dev_ctxt->p_cctxt_list_head = p_cctxt; + p_cctxt->p_next = p_client; + + *pp_cctxt = p_cctxt; + + return VCD_S_SUCCESS; + +} + +static u32 vcd_open_in_not_init + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + s32 driver_handle, + u32 b_decoding, + void (*callback) (u32 event, u32 status, void *p_info, u32 n_size, + void *handle, void *const p_client_data), + void *p_client_data) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt; + u32 rc; + + VCD_MSG_LOW("vcd_open_in_dev_not_init:"); + + rc = vcd_open_cmn(p_drv_ctxt, driver_handle, b_decoding, callback, + p_client_data, &p_cctxt); + + VCD_FAILED_RETURN(rc, "Failed: vcd_open_cmn"); + + rc = vcd_init_device_context(p_drv_ctxt, + DEVICE_STATE_EVENT_NUMBER(pf_open)); + + if (VCD_FAILED(rc)) + vcd_destroy_client_context(p_cctxt); + + return rc; +} + +static u32 vcd_open_in_initing(struct vcd_drv_ctxt_type_t *p_drv_ctxt, + s32 driver_handle, u32 b_decoding, + void (*callback) (u32 event, u32 status, void *p_info, u32 n_size, + void *handle, void *const p_client_data), + void *p_client_data) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt; + + VCD_MSG_LOW("vcd_open_in_dev_initing:"); + + return vcd_open_cmn(p_drv_ctxt, driver_handle, b_decoding, callback, + p_client_data, &p_cctxt); +} + +static u32 vcd_open_in_ready + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + s32 driver_handle, + u32 b_decoding, + void (*callback) (u32 event, u32 status, void *p_info, u32 n_size, + void *handle, void *const p_client_data), + void *p_client_data) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt; + struct vcd_handle_container_type container; + u32 rc; + + VCD_MSG_LOW("vcd_open_in_dev_ready:"); + + rc = vcd_open_cmn(p_drv_ctxt, driver_handle, b_decoding, callback, + p_client_data, &p_cctxt); + + VCD_FAILED_RETURN(rc, "Failed: vcd_open_cmn"); + + rc = vcd_init_client_context(p_cctxt); + + if (!VCD_FAILED(rc)) { + container.handle = (void *)p_cctxt; + + callback(VCD_EVT_RESP_OPEN, + VCD_S_SUCCESS, + &container, + sizeof(container), container.handle, p_client_data); + } else { + VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_init_client_context", rc); + + vcd_destroy_client_context(p_cctxt); + } + + return rc; +} + +static u32 vcd_close_in_ready + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + struct vcd_clnt_ctxt_type_t *p_cctxt) { + u32 rc; + + VCD_MSG_LOW("vcd_close_in_dev_ready:"); + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_close) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_close(p_cctxt); + } else { + VCD_MSG_ERROR("Unsupported API in client state %d", + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + if (!VCD_FAILED(rc)) + vcd_handle_for_last_clnt_close(&p_drv_ctxt->dev_ctxt, TRUE); + + return rc; +} + +static u32 vcd_close_in_dev_invalid(struct vcd_drv_ctxt_type_t *p_drv_ctxt, + struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + u32 rc; + VCD_MSG_LOW("vcd_close_in_dev_invalid:"); + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_close) { + rc = p_cctxt->clnt_state.p_state_table-> + ev_hdlr.pf_close(p_cctxt); + } else { + VCD_MSG_ERROR("Unsupported API in client state %d", + p_cctxt->clnt_state.e_state); + rc = VCD_ERR_BAD_STATE; + } + if (!VCD_FAILED(rc) && !p_drv_ctxt->dev_ctxt. + p_cctxt_list_head) { + VCD_MSG_HIGH("All INVALID clients are closed"); + vcd_do_device_state_transition(p_drv_ctxt, + VCD_DEVICE_STATE_NOT_INIT, + DEVICE_STATE_EVENT_NUMBER(pf_close)); + } + return rc; +} + +static u32 vcd_resume_in_ready + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + struct vcd_clnt_ctxt_type_t *p_cctxt) { + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_LOW("vcd_resume_in_ready:"); + + if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_resume) { + rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. + pf_resume(p_cctxt); + } else { + VCD_MSG_ERROR("Unsupported API in client state %d", + p_cctxt->clnt_state.e_state); + + rc = VCD_ERR_BAD_STATE; + } + + return rc; +} + +static u32 vcd_set_dev_pwr_in_ready + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + enum vcd_power_state_type e_pwr_state) +{ + u32 rc = VCD_S_SUCCESS; + struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + + VCD_MSG_LOW("vcd_set_dev_pwr_in_ready:"); + + switch (e_pwr_state) { + case VCD_PWR_STATE_SLEEP: + { + vcd_pause_all_sessions(p_dev_ctxt); + + p_dev_ctxt->e_pwr_state = VCD_PWR_STATE_SLEEP; + + break; + } + + case VCD_PWR_STATE_ON: + { + if (p_dev_ctxt->e_pwr_state == VCD_PWR_STATE_SLEEP) + vcd_resume_all_sessions(p_dev_ctxt); + + + p_dev_ctxt->e_pwr_state = VCD_PWR_STATE_ON; + + break; + } + + default: + { + VCD_MSG_ERROR("Invalid power state requested %d", + e_pwr_state); + break; + } + + } + + return rc; +} + +static void vcd_dev_cb_in_initing + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + u32 event, + u32 status, + void *p_payload, u32 size, u32 *ddl_handle, void *const p_client_data) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt; + struct vcd_clnt_ctxt_type_t *p_client; + struct vcd_clnt_ctxt_type_t *p_tmp_client; + struct vcd_handle_container_type container; + u32 rc = VCD_S_SUCCESS; + u32 b_client_inited = FALSE; + u32 b_fail_all_open = FALSE; + + VCD_MSG_LOW("vcd_dev_cb_in_initing:"); + + if (event != VCD_EVT_RESP_DEVICE_INIT) { + VCD_MSG_ERROR("vcd_dev_cb_in_initing: Unexpected event %d", + (int)event); + return; + } + + p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + + p_dev_ctxt->b_continue = FALSE; + + if (VCD_FAILED(status)) { + vcd_handle_device_init_failed(p_drv_ctxt, status); + + return; + } + + vcd_do_device_state_transition(p_drv_ctxt, + VCD_DEVICE_STATE_READY, + DEVICE_STATE_EVENT_NUMBER(pf_open)); + + if (!p_dev_ctxt->p_cctxt_list_head) { + VCD_MSG_HIGH("All clients are closed"); + + p_dev_ctxt->e_pending_cmd = VCD_CMD_DEVICE_TERM; + + return; + } + + if (!p_dev_ctxt->n_ddl_cmd_ch_depth + || !p_dev_ctxt->a_trans_tbl) + rc = vcd_setup_with_ddl_capabilities(p_dev_ctxt); + + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR + ("rc = 0x%x: Failed vcd_setup_with_ddl_capabilities", + rc); + + b_fail_all_open = TRUE; + } + + p_client = p_dev_ctxt->p_cctxt_list_head; + while (p_client) { + if (!b_fail_all_open) + rc = vcd_init_client_context(p_client); + + + if (!VCD_FAILED(rc)) { + container.handle = (void *)p_client; + p_client->callback(VCD_EVT_RESP_OPEN, + VCD_S_SUCCESS, + &container, + sizeof(container), + container.handle, + p_client->p_client_data); + + p_client = p_client->p_next; + + b_client_inited = TRUE; + } else { + VCD_MSG_ERROR + ("rc = 0x%x, Failed: vcd_init_client_context", + rc); + + p_client->callback(VCD_EVT_RESP_OPEN, + rc, + NULL, 0, 0, p_client->p_client_data); + + p_tmp_client = p_client; + p_client = p_client->p_next; + + vcd_destroy_client_context(p_tmp_client); + } + } + + if (!b_client_inited || b_fail_all_open) { + VCD_MSG_ERROR("All client open requests failed"); + + p_dev_ctxt->e_pending_cmd = VCD_CMD_DEVICE_TERM; + } else { + if (vcd_power_event(p_dev_ctxt, NULL, + VCD_EVT_PWR_DEV_INIT_END)) { + VCD_MSG_ERROR("VCD_EVT_PWR_DEV_INIT_END failed"); + } + } +} + +static void vcd_hw_timeout_cmn(struct vcd_drv_ctxt_type_t *p_drv_ctxt, + void *p_user_data) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + VCD_MSG_LOW("vcd_hw_timeout_cmn:"); + vcd_device_timer_stop(p_dev_ctxt); + + vcd_handle_device_err_fatal(p_dev_ctxt, NULL); + + /* Reset HW. */ + (void) vcd_reset_device_context(p_drv_ctxt, + DEVICE_STATE_EVENT_NUMBER(pf_timeout)); +} + +static void vcd_dev_enter_null + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 n_state_event_type) { + VCD_MSG_MED("Entering DEVICE_STATE_NULL on api %d", n_state_event_type); + +} + +static void vcd_dev_enter_not_init + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 n_state_event_type) { + VCD_MSG_MED("Entering DEVICE_STATE_NOT_INIT on api %d", + n_state_event_type); + +} + +static void vcd_dev_enter_initing + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 n_state_event_type) { + VCD_MSG_MED("Entering DEVICE_STATE_INITING on api %d", + n_state_event_type); + +} + +static void vcd_dev_enter_ready + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 n_state_event_type) { + VCD_MSG_MED("Entering DEVICE_STATE_READY on api %d", + n_state_event_type); +} + +static void vcd_dev_enter_invalid(struct vcd_drv_ctxt_type_t *p_drv_ctxt, + s32 state_event_type) +{ + VCD_MSG_MED("Entering DEVICE_STATE_INVALID on api %d", state_event_type); +} + +static void vcd_dev_exit_null + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 n_state_event_type) { + VCD_MSG_MED("Exiting DEVICE_STATE_NULL on api %d", n_state_event_type); +} + +static void vcd_dev_exit_not_init + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 n_state_event_type) { + VCD_MSG_MED("Exiting DEVICE_STATE_NOT_INIT on api %d", + n_state_event_type); + +} + +static void vcd_dev_exit_initing + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 n_state_event_type) { + VCD_MSG_MED("Exiting DEVICE_STATE_INITING on api %d", + n_state_event_type); +} + +static void vcd_dev_exit_ready + (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 n_state_event_type) { + VCD_MSG_MED("Exiting DEVICE_STATE_READY on api %d", n_state_event_type); +} + +static void vcd_dev_exit_invalid(struct vcd_drv_ctxt_type_t *p_drv_ctxt, + s32 state_event_type) +{ + VCD_MSG_MED("Exiting DEVICE_STATE_INVALID on api %d", state_event_type); +} + +static const struct vcd_dev_state_table_type_t vcd_dev_table_null = { + { + vcd_init_in_null, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + }, + vcd_dev_enter_null, + vcd_dev_exit_null +}; + +static const struct vcd_dev_state_table_type_t vcd_dev_table_not_init = { + { + vcd_init_in_not_init, + vcd_term_in_not_init, + vcd_open_in_not_init, + NULL, + NULL, + NULL, + NULL, + NULL, + }, + vcd_dev_enter_not_init, + vcd_dev_exit_not_init +}; + +static const struct vcd_dev_state_table_type_t vcd_dev_table_initing = { + { + vcd_init_in_initing, + vcd_term_in_initing, + vcd_open_in_initing, + NULL, + NULL, + NULL, + vcd_dev_cb_in_initing, + vcd_hw_timeout_cmn, + }, + vcd_dev_enter_initing, + vcd_dev_exit_initing +}; + +static const struct vcd_dev_state_table_type_t vcd_dev_table_ready = { + { + vcd_init_in_ready, + vcd_term_in_ready, + vcd_open_in_ready, + vcd_close_in_ready, + vcd_resume_in_ready, + vcd_set_dev_pwr_in_ready, + NULL, + vcd_hw_timeout_cmn, + }, + vcd_dev_enter_ready, + vcd_dev_exit_ready +}; + +static const struct vcd_dev_state_table_type_t vcd_dev_table_in_invalid = { + { + NULL, + vcd_term_in_invalid, + NULL, + vcd_close_in_dev_invalid, + NULL, + NULL, + NULL, + NULL, + }, + vcd_dev_enter_invalid, + vcd_dev_exit_invalid +}; + +static const struct vcd_dev_state_table_type_t *vcd_dev_state_table[] = { + &vcd_dev_table_null, + &vcd_dev_table_not_init, + &vcd_dev_table_initing, + &vcd_dev_table_ready, + &vcd_dev_table_in_invalid +}; diff --git a/drivers/misc/video_core/720p/vcd/vcd_device_sm.h b/drivers/misc/video_core/720p/vcd/vcd_device_sm.h new file mode 100644 index 0000000000000..350e763dc0669 --- /dev/null +++ b/drivers/misc/video_core/720p/vcd/vcd_device_sm.h @@ -0,0 +1,114 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VCD_DEVICE_SM_H_ +#define _VCD_DEVICE_SM_H_ + +#include "vcd_api.h" +#include "vcd_ddl_api.h" +#include "vcd_core.h" + +struct vcd_dev_state_table_type_t; +struct vcd_dev_state_ctxt_type_t; +struct vcd_drv_ctxt_type_t; + +enum vcd_dev_state_enum_type { + VCD_DEVICE_STATE_NULL = 0, + VCD_DEVICE_STATE_NOT_INIT, + VCD_DEVICE_STATE_INITING, + VCD_DEVICE_STATE_READY, + VCD_DEVICE_STATE_INVALID, + VCD_DEVICE_STATE_MAX, + VCD_DEVICE_STATE_32BIT = 0x7FFFFFFF +}; + +struct vcd_dev_state_table_type_t { + struct { + u32(*pf_init) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + struct vcd_init_config_type *p_config, + s32 *p_driver_handle); + + u32(*pf_term) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + s32 driver_handle); + + u32(*pf_open) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + s32 driver_handle, u32 b_decoding, + void (*callback) (u32 event, u32 status, + void *p_info, u32 n_size, void *handle, + void *const p_client_data), + void *p_client_data); + + u32(*pf_close) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + struct vcd_clnt_ctxt_type_t *p_cctxt); + + u32(*pf_resume) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + struct vcd_clnt_ctxt_type_t *p_cctxt); + + u32(*pf_set_dev_pwr) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + enum vcd_power_state_type e_pwr_state); + + void (*pf_dev_cb) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + u32 event, u32 status, void *p_payload, + u32 n_size, u32 *ddl_handle, + void *const p_client_data); + + void (*pf_timeout) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + void *p_user_data); + } ev_hdlr; + + void (*pf_entry) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + s32 n_state_event_type); + void (*pf_exit) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, + s32 n_state_event_type); +}; + +#define DEVICE_STATE_EVENT_NUMBER(ppf) \ + ((u32 *) (&(((struct vcd_dev_state_table_type_t*)0)->ev_hdlr.ppf)) - \ + (u32 *) (&(((struct vcd_dev_state_table_type_t*)0)->ev_hdlr.pf_init)) \ + + 1) + +struct vcd_dev_state_ctxt_type_t { + const struct vcd_dev_state_table_type_t *p_state_table; + + enum vcd_dev_state_enum_type e_state; +}; + +struct vcd_drv_ctxt_type_t { + struct vcd_dev_state_ctxt_type_t dev_state; + + struct vcd_dev_ctxt_type dev_ctxt; + + u32 *dev_cs; +}; + + +extern struct vcd_drv_ctxt_type_t *vcd_get_drv_context(void); + +void vcd_continue(void); + +#endif diff --git a/drivers/misc/video_core/720p/vcd/vcd_power_sm.c b/drivers/misc/video_core/720p/vcd/vcd_power_sm.c new file mode 100644 index 0000000000000..34c2da28667eb --- /dev/null +++ b/drivers/misc/video_core/720p/vcd/vcd_power_sm.c @@ -0,0 +1,387 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" +#include "vcd_power_sm.h" +#include "vcd_core.h" +#include "vcd.h" + +u32 vcd_power_event( + struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_clnt_ctxt_type_t *p_cctxt, u32 event) +{ + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_MED("Device power state = %d", p_dev_ctxt->e_pwr_clk_state); + VCD_MSG_MED("event = 0x%x", event); + switch (event) { + + case VCD_EVT_PWR_DEV_INIT_BEGIN: + case VCD_EVT_PWR_DEV_INIT_END: + case VCD_EVT_PWR_DEV_INIT_FAIL: + case VCD_EVT_PWR_DEV_TERM_BEGIN: + case VCD_EVT_PWR_DEV_TERM_END: + case VCD_EVT_PWR_DEV_TERM_FAIL: + case VCD_EVT_PWR_DEV_SLEEP_BEGIN: + case VCD_EVT_PWR_DEV_SLEEP_END: + case VCD_EVT_PWR_DEV_SET_PERFLVL: + case VCD_EVT_PWR_DEV_HWTIMEOUT: + { + rc = vcd_device_power_event(p_dev_ctxt, event, + p_cctxt); + break; + } + + case VCD_EVT_PWR_CLNT_CMD_BEGIN: + case VCD_EVT_PWR_CLNT_CMD_END: + case VCD_EVT_PWR_CLNT_CMD_FAIL: + case VCD_EVT_PWR_CLNT_PAUSE: + case VCD_EVT_PWR_CLNT_RESUME: + case VCD_EVT_PWR_CLNT_FIRST_FRAME: + case VCD_EVT_PWR_CLNT_LAST_FRAME: + case VCD_EVT_PWR_CLNT_ERRFATAL: + { + rc = vcd_client_power_event(p_dev_ctxt, p_cctxt, event); + break; + } + + } + + if (VCD_FAILED(rc)) + VCD_MSG_ERROR("vcd_power_event: event 0x%x failed", event); + + + return rc; + +} + +u32 vcd_device_power_event(struct vcd_dev_ctxt_type *p_dev_ctxt, u32 event, + struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + u32 rc = VCD_ERR_FAIL; + u32 n_set_perf_lvl; + + switch (event) { + + case VCD_EVT_PWR_DEV_INIT_BEGIN: + { + if (p_dev_ctxt->e_pwr_clk_state == + VCD_PWRCLK_STATE_OFF) { + if (res_trk_get_max_perf_level(&p_dev_ctxt-> + n_max_perf_lvl)) { + if (res_trk_power_up()) { + p_dev_ctxt->e_pwr_clk_state = + VCD_PWRCLK_STATE_ON_NOTCLOCKED; + p_dev_ctxt->n_curr_perf_lvl = 0; + p_dev_ctxt->n_reqd_perf_lvl = 0; + p_dev_ctxt->n_active_clnts = 0; + p_dev_ctxt-> + b_set_perf_lvl_pending = FALSE; + rc = vcd_enable_clock(p_dev_ctxt, + p_cctxt); + if (VCD_FAILED(rc)) { + (void)res_trk_power_down(); + p_dev_ctxt->e_pwr_clk_state = + VCD_PWRCLK_STATE_OFF; + } + } + } + } + + break; + } + + case VCD_EVT_PWR_DEV_INIT_END: + case VCD_EVT_PWR_DEV_TERM_FAIL: + case VCD_EVT_PWR_DEV_SLEEP_BEGIN: + case VCD_EVT_PWR_DEV_HWTIMEOUT: + { + rc = vcd_gate_clock(p_dev_ctxt); + + break; + } + + case VCD_EVT_PWR_DEV_INIT_FAIL: + case VCD_EVT_PWR_DEV_TERM_END: + { + if (p_dev_ctxt->e_pwr_clk_state != + VCD_PWRCLK_STATE_OFF) { + (void)vcd_disable_clock(p_dev_ctxt); + (void)res_trk_power_down(); + + p_dev_ctxt->e_pwr_clk_state = + VCD_PWRCLK_STATE_OFF; + p_dev_ctxt->n_curr_perf_lvl = 0; + p_dev_ctxt->n_reqd_perf_lvl = 0; + p_dev_ctxt->n_active_clnts = 0; + p_dev_ctxt->b_set_perf_lvl_pending = FALSE; + rc = VCD_S_SUCCESS; + } + + break; + } + + case VCD_EVT_PWR_DEV_TERM_BEGIN: + case VCD_EVT_PWR_DEV_SLEEP_END: + { + rc = vcd_un_gate_clock(p_dev_ctxt); + + break; + } + + case VCD_EVT_PWR_DEV_SET_PERFLVL: + { + n_set_perf_lvl = + p_dev_ctxt->n_reqd_perf_lvl > + 0 ? p_dev_ctxt-> + n_reqd_perf_lvl : VCD_MIN_PERF_LEVEL; + + rc = vcd_set_perf_level(p_dev_ctxt, n_set_perf_lvl, + p_cctxt); + + break; + } + } + return rc; +} + +u32 vcd_client_power_event( + struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_clnt_ctxt_type_t *p_cctxt, u32 event) +{ + u32 rc = VCD_ERR_FAIL; + + switch (event) { + + case VCD_EVT_PWR_CLNT_CMD_BEGIN: + { + rc = vcd_un_gate_clock(p_dev_ctxt); + break; + } + + case VCD_EVT_PWR_CLNT_CMD_END: + { + rc = vcd_gate_clock(p_dev_ctxt); + break; + } + + case VCD_EVT_PWR_CLNT_CMD_FAIL: + { + if (!vcd_core_is_busy(p_dev_ctxt)) + rc = vcd_gate_clock(p_dev_ctxt); + + break; + } + + case VCD_EVT_PWR_CLNT_PAUSE: + case VCD_EVT_PWR_CLNT_LAST_FRAME: + case VCD_EVT_PWR_CLNT_ERRFATAL: + { + if (p_cctxt) { + rc = VCD_S_SUCCESS; + if (p_cctxt->status.b_req_perf_lvl) { + p_dev_ctxt->n_reqd_perf_lvl -= + p_cctxt->n_reqd_perf_lvl; + p_cctxt->status.b_req_perf_lvl = FALSE; + + rc = vcd_set_perf_level(p_dev_ctxt, + p_dev_ctxt->n_reqd_perf_lvl, + p_cctxt); + } + } + + break; + } + + case VCD_EVT_PWR_CLNT_RESUME: + case VCD_EVT_PWR_CLNT_FIRST_FRAME: + { + if (p_cctxt) { + rc = VCD_S_SUCCESS; + if (!p_cctxt->status.b_req_perf_lvl) { + p_dev_ctxt->n_reqd_perf_lvl += + p_cctxt->n_reqd_perf_lvl; + p_cctxt->status.b_req_perf_lvl = TRUE; + + rc = vcd_set_perf_level(p_dev_ctxt, + p_dev_ctxt->n_reqd_perf_lvl, + p_cctxt); + } + } + break; + } + } + + return rc; +} + +u32 vcd_enable_clock(struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + u32 rc = VCD_S_SUCCESS; + u32 n_set_perf_lvl; + + if (p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_OFF) { + VCD_MSG_ERROR("vcd_enable_clock(): Already in state " + "VCD_PWRCLK_STATE_OFF\n"); + vcd_assert(); + rc = VCD_ERR_FAIL; + } else if (p_dev_ctxt->e_pwr_clk_state == + VCD_PWRCLK_STATE_ON_NOTCLOCKED) { + + n_set_perf_lvl = + p_dev_ctxt->n_reqd_perf_lvl > + 0 ? p_dev_ctxt-> + n_reqd_perf_lvl : VCD_MIN_PERF_LEVEL; + + rc = vcd_set_perf_level(p_dev_ctxt, n_set_perf_lvl, + p_cctxt); + + if (!VCD_FAILED(rc)) { + if (res_trk_enable_clocks()) { + p_dev_ctxt->e_pwr_clk_state = + VCD_PWRCLK_STATE_ON_CLOCKED; + } + } else { + rc = VCD_ERR_FAIL; + } + + } + + if (!VCD_FAILED(rc)) + p_dev_ctxt->n_active_clnts++; + + return rc; +} + +u32 vcd_disable_clock(struct vcd_dev_ctxt_type *p_dev_ctxt) +{ + u32 rc = VCD_S_SUCCESS; + + if (p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_OFF) { + VCD_MSG_ERROR("vcd_disable_clock(): Already in state " + "VCD_PWRCLK_STATE_OFF\n"); + vcd_assert(); + rc = VCD_ERR_FAIL; + } else if (p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKED || + p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKGATED) { + p_dev_ctxt->n_active_clnts--; + + if (!p_dev_ctxt->n_active_clnts) { + if (!res_trk_disable_clocks()) + rc = VCD_ERR_FAIL; + + p_dev_ctxt->e_pwr_clk_state = + VCD_PWRCLK_STATE_ON_NOTCLOCKED; + p_dev_ctxt->n_curr_perf_lvl = 0; + } + } + + return rc; +} + +u32 vcd_set_perf_level(struct vcd_dev_ctxt_type *p_dev_ctxt, + u32 n_perf_lvl, struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + u32 rc = VCD_S_SUCCESS; + + if (!vcd_core_is_busy(p_dev_ctxt)) { + if (res_trk_set_perf_level(n_perf_lvl, + &p_dev_ctxt->n_curr_perf_lvl, p_cctxt)) { + p_dev_ctxt->b_set_perf_lvl_pending = FALSE; + } else { + rc = VCD_ERR_FAIL; + p_dev_ctxt->b_set_perf_lvl_pending = TRUE; + } + + } else { + p_dev_ctxt->b_set_perf_lvl_pending = TRUE; + } + + return rc; +} + +u32 vcd_update_clnt_perf_lvl( + struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_property_frame_rate_type *p_fps, u32 n_frm_p_units) +{ + u32 rc = VCD_S_SUCCESS; + struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + u32 n_new_perf_lvl; + + n_new_perf_lvl = + n_frm_p_units * p_fps->n_fps_numerator / p_fps->n_fps_denominator; + + if (p_cctxt->status.b_req_perf_lvl) { + p_dev_ctxt->n_reqd_perf_lvl = + p_dev_ctxt->n_reqd_perf_lvl - p_cctxt->n_reqd_perf_lvl + + n_new_perf_lvl; + + rc = vcd_set_perf_level(p_cctxt->p_dev_ctxt, + p_dev_ctxt->n_reqd_perf_lvl, p_cctxt); + } + + p_cctxt->n_reqd_perf_lvl = n_new_perf_lvl; + + return rc; +} + +u32 vcd_gate_clock(struct vcd_dev_ctxt_type *p_dev_ctxt) +{ + u32 rc = VCD_S_SUCCESS; + + if (p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_OFF || + p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_ON_NOTCLOCKED) { + VCD_MSG_ERROR("%s(): Clk is Off or Not Clked yet \n", __func__); + vcd_assert(); + return VCD_ERR_FAIL; + } + + if (p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKGATED) + return rc; + + if (res_trk_disable_clocks()) + p_dev_ctxt->e_pwr_clk_state = VCD_PWRCLK_STATE_ON_CLOCKGATED; + else + rc = VCD_ERR_FAIL; + + return rc; +} + +u32 vcd_un_gate_clock(struct vcd_dev_ctxt_type *p_dev_ctxt) +{ + u32 rc = VCD_S_SUCCESS; + + if (p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_OFF || + p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_ON_NOTCLOCKED) { + VCD_MSG_ERROR("%s(): Clk is Off or Not Clked yet \n", __func__); + vcd_assert(); + return VCD_ERR_FAIL; + } + + if (p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKED) + return rc; + + if (res_trk_enable_clocks()) + p_dev_ctxt->e_pwr_clk_state = VCD_PWRCLK_STATE_ON_CLOCKED; + else + rc = VCD_ERR_FAIL; + + return rc; +} + diff --git a/drivers/misc/video_core/720p/vcd/vcd_power_sm.h b/drivers/misc/video_core/720p/vcd/vcd_power_sm.h new file mode 100644 index 0000000000000..b2af5ddad8a7a --- /dev/null +++ b/drivers/misc/video_core/720p/vcd/vcd_power_sm.h @@ -0,0 +1,59 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VCD_POWERSM_H_ +#define _VCD_POWERSM_H_ + +#define VCD_EVT_PWR_BASE 0x5000 +#define VCD_EVT_PWR_DEV_INIT_BEGIN (VCD_EVT_PWR_BASE + 0x1) +#define VCD_EVT_PWR_DEV_INIT_END (VCD_EVT_PWR_BASE + 0x2) +#define VCD_EVT_PWR_DEV_INIT_FAIL (VCD_EVT_PWR_BASE + 0x3) +#define VCD_EVT_PWR_DEV_TERM_BEGIN (VCD_EVT_PWR_BASE + 0x4) +#define VCD_EVT_PWR_DEV_TERM_END (VCD_EVT_PWR_BASE + 0x5) +#define VCD_EVT_PWR_DEV_TERM_FAIL (VCD_EVT_PWR_BASE + 0x6) +#define VCD_EVT_PWR_DEV_SLEEP_BEGIN (VCD_EVT_PWR_BASE + 0x7) +#define VCD_EVT_PWR_DEV_SLEEP_END (VCD_EVT_PWR_BASE + 0x8) +#define VCD_EVT_PWR_DEV_SET_PERFLVL (VCD_EVT_PWR_BASE + 0x9) +#define VCD_EVT_PWR_DEV_HWTIMEOUT (VCD_EVT_PWR_BASE + 0xa) +#define VCD_EVT_PWR_CLNT_CMD_BEGIN (VCD_EVT_PWR_BASE + 0xb) +#define VCD_EVT_PWR_CLNT_CMD_END (VCD_EVT_PWR_BASE + 0xc) +#define VCD_EVT_PWR_CLNT_CMD_FAIL (VCD_EVT_PWR_BASE + 0xd) +#define VCD_EVT_PWR_CLNT_PAUSE (VCD_EVT_PWR_BASE + 0xe) +#define VCD_EVT_PWR_CLNT_RESUME (VCD_EVT_PWR_BASE + 0xf) +#define VCD_EVT_PWR_CLNT_FIRST_FRAME (VCD_EVT_PWR_BASE + 0x10) +#define VCD_EVT_PWR_CLNT_LAST_FRAME (VCD_EVT_PWR_BASE + 0x11) +#define VCD_EVT_PWR_CLNT_ERRFATAL (VCD_EVT_PWR_BASE + 0x12) + +enum vcd_pwr_clk_state_type { + VCD_PWRCLK_STATE_OFF = 0, + VCD_PWRCLK_STATE_ON_NOTCLOCKED, + VCD_PWRCLK_STATE_ON_CLOCKED, + VCD_PWRCLK_STATE_ON_CLOCKGATED +}; + +#endif diff --git a/drivers/misc/video_core/720p/vcd/vcd_property.h b/drivers/misc/video_core/720p/vcd/vcd_property.h new file mode 100644 index 0000000000000..20f49b56a4d5a --- /dev/null +++ b/drivers/misc/video_core/720p/vcd/vcd_property.h @@ -0,0 +1,312 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VCD_DRIVER_PROPERTY_H_ +#define _VCD_DRIVER_PROPERTY_H_ + +#define VCD_START_BASE 0x0 +#define VCD_I_LIVE (VCD_START_BASE + 0x1) +#define VCD_I_CODEC (VCD_START_BASE + 0x2) +#define VCD_I_FRAME_SIZE (VCD_START_BASE + 0x3) +#define VCD_I_METADATA_ENABLE (VCD_START_BASE + 0x4) +#define VCD_I_METADATA_HEADER (VCD_START_BASE + 0x5) +#define VCD_I_PROFILE (VCD_START_BASE + 0x6) +#define VCD_I_LEVEL (VCD_START_BASE + 0x7) +#define VCD_I_BUFFER_FORMAT (VCD_START_BASE + 0x8) +#define VCD_I_FRAME_RATE (VCD_START_BASE + 0x9) +#define VCD_I_TARGET_BITRATE (VCD_START_BASE + 0xA) +#define VCD_I_MULTI_SLICE (VCD_START_BASE + 0xB) +#define VCD_I_ENTROPY_CTRL (VCD_START_BASE + 0xC) +#define VCD_I_DEBLOCKING (VCD_START_BASE + 0xD) +#define VCD_I_RATE_CONTROL (VCD_START_BASE + 0xE) +#define VCD_I_QP_RANGE (VCD_START_BASE + 0xF) +#define VCD_I_SESSION_QP (VCD_START_BASE + 0x10) +#define VCD_I_INTRA_PERIOD (VCD_START_BASE + 0x11) +#define VCD_I_VOP_TIMING (VCD_START_BASE + 0x12) +#define VCD_I_SHORT_HEADER (VCD_START_BASE + 0x13) +#define VCD_I_SEQ_HEADER (VCD_START_BASE + 0x14) +#define VCD_I_HEADER_EXTENSION (VCD_START_BASE + 0x15) +#define VCD_I_INTRA_REFRESH (VCD_START_BASE + 0x16) +#define VCD_I_POST_FILTER (VCD_START_BASE + 0x17) +#define VCD_I_PROGRESSIVE_ONLY (VCD_START_BASE + 0x18) + +#define VCD_START_REQ (VCD_START_BASE + 0x1000) +#define VCD_I_REQ_IFRAME (VCD_START_REQ + 0x1) + +#define VCD_I_RESERVED_BASE (VCD_START_BASE + 0x10000) + +struct vcd_property_hdr_type { + u32 prop_id; + u32 n_size; +}; + +struct vcd_property_live_type { + u32 b_live; +}; + +enum vcd_codec_type { + VCD_CODEC_H264 = 0x1, + VCD_CODEC_H263 = 0x2, + VCD_CODEC_MPEG1 = 0x3, + VCD_CODEC_MPEG2 = 0x4, + VCD_CODEC_MPEG4 = 0x5, + VCD_CODEC_DIVX_3 = 0x6, + VCD_CODEC_DIVX_4 = 0x7, + VCD_CODEC_DIVX_5 = 0x8, + VCD_CODEC_DIVX_6 = 0x9, + VCD_CODEC_XVID = 0xA, + VCD_CODEC_VC1 = 0xB, + VCD_CODEC_VC1_RCV = 0xC +}; + +struct vcd_property_codec_type { + enum vcd_codec_type e_codec; +}; + +struct vcd_property_frame_size_type { + u32 n_width; + u32 n_height; + u32 n_stride; + u32 n_scan_lines; +}; + + +#define VCD_METADATA_DATANONE 0x001 +#define VCD_METADATA_QCOMFILLER 0x002 +#define VCD_METADATA_QPARRAY 0x004 +#define VCD_METADATA_CONCEALMB 0x008 +#define VCD_METADATA_SEI 0x010 +#define VCD_METADATA_VUI 0x020 +#define VCD_METADATA_VC1 0x040 +#define VCD_METADATA_PASSTHROUGH 0x080 +#define VCD_METADATA_ENC_SLICE 0x100 + +struct vcd_property_meta_data_enable_type { + u32 n_meta_data_enable_flag; +}; + +struct vcd_property_metadata_hdr_type { + u32 n_meta_data_id_type; + u32 n_version; + u32 n_port_index; + u32 e_type; +}; + +struct vcd_property_frame_rate_type { + u32 n_fps_denominator; + u32 n_fps_numerator; +}; + +struct vcd_property_target_bitrate_type { + u32 n_target_bitrate; +}; + +enum vcd_yuv_buffer_format_type { + VCD_BUFFER_FORMAT_NV12 = 0x1, + VCD_BUFFER_FORMAT_TILE_4x2 = 0x2, + VCD_BUFFER_FORMAT_NV12_16M2KA = 0x3 +}; + +struct vcd_property_buffer_format_type { + enum vcd_yuv_buffer_format_type e_buffer_format; +}; + +struct vcd_property_post_filter_type { + u32 b_post_filter; +}; + +enum vcd_codec_profile_type { + VCD_PROFILE_UNKNOWN = 0x0, + VCD_PROFILE_MPEG4_SP = 0x1, + VCD_PROFILE_MPEG4_ASP = 0x2, + VCD_PROFILE_H264_BASELINE = 0x3, + VCD_PROFILE_H264_MAIN = 0x4, + VCD_PROFILE_H264_HIGH = 0x5, + VCD_PROFILE_H263_BASELINE = 0x6, + VCD_PROFILE_VC1_SIMPLE = 0x7, + VCD_PROFILE_VC1_MAIN = 0x8, + VCD_PROFILE_VC1_ADVANCE = 0x9, + VCD_PROFILE_MPEG2_MAIN = 0xA, + VCD_PROFILE_MPEG2_SIMPLE = 0xB +}; + +struct vcd_property_profile_type { + enum vcd_codec_profile_type e_profile; +}; + +enum vcd_codec_level_type { + VCD_LEVEL_UNKNOWN = 0x0, + VCD_LEVEL_MPEG4_0 = 0x1, + VCD_LEVEL_MPEG4_0b = 0x2, + VCD_LEVEL_MPEG4_1 = 0x3, + VCD_LEVEL_MPEG4_2 = 0x4, + VCD_LEVEL_MPEG4_3 = 0x5, + VCD_LEVEL_MPEG4_3b = 0x6, + VCD_LEVEL_MPEG4_4 = 0x7, + VCD_LEVEL_MPEG4_4a = 0x8, + VCD_LEVEL_MPEG4_5 = 0x9, + VCD_LEVEL_MPEG4_6 = 0xA, + VCD_LEVEL_MPEG4_7 = 0xB, + VCD_LEVEL_MPEG4_X = 0xC, + VCD_LEVEL_H264_1 = 0x10, + VCD_LEVEL_H264_1b = 0x11, + VCD_LEVEL_H264_1p1 = 0x12, + VCD_LEVEL_H264_1p2 = 0x13, + VCD_LEVEL_H264_1p3 = 0x14, + VCD_LEVEL_H264_2 = 0x15, + VCD_LEVEL_H264_2p1 = 0x16, + VCD_LEVEL_H264_2p2 = 0x17, + VCD_LEVEL_H264_3 = 0x18, + VCD_LEVEL_H264_3p1 = 0x19, + VCD_LEVEL_H264_3p2 = 0x1A, + VCD_LEVEL_H264_4 = 0x1B, + VCD_LEVEL_H264_X = 0x1C, + VCD_LEVEL_H263_10 = 0x20, + VCD_LEVEL_H263_20 = 0x21, + VCD_LEVEL_H263_30 = 0x22, + VCD_LEVEL_H263_40 = 0x23, + VCD_LEVEL_H263_45 = 0x24, + VCD_LEVEL_H263_50 = 0x25, + VCD_LEVEL_H263_60 = 0x26, + VCD_LEVEL_H263_70 = 0x27, + VCD_LEVEL_H263_X = 0x28, + VCD_LEVEL_MPEG2_LOW = 0x30, + VCD_LEVEL_MPEG2_MAIN = 0x31, + VCD_LEVEL_MPEG2_HIGH_14 = 0x32, + VCD_LEVEL_MPEG2_HIGH = 0x33, + VCD_LEVEL_MPEG2_X = 0x34, + VCD_LEVEL_VC1_LOW = 0x40, + VCD_LEVEL_VC1_MEDIUM = 0x41, + VCD_LEVEL_VC1_HIGH = 0x42, + VCD_LEVEL_VC1_0 = 0x43, + VCD_LEVEL_VC1_1 = 0x44, + VCD_LEVEL_VC1_2 = 0x45, + VCD_LEVEL_VC1_3 = 0x46, + VCD_LEVEL_VC1_4 = 0x47, + VCD_LEVEL_VC1_X = 0x48 +}; + +struct vcd_property_level_type { + enum vcd_codec_level_type e_level; +}; + +enum vcd_m_slice_sel_type { + VCD_MSLICE_OFF = 0x1, + VCD_MSLICE_BY_MB_COUNT = 0x2, + VCD_MSLICE_BY_BYTE_COUNT = 0x3, + VCD_MSLICE_BY_GOB = 0x4 +}; + +struct vcd_property_multi_slice_type { + enum vcd_m_slice_sel_type e_m_slice_sel; + u32 n_m_slice_size; +}; + +enum vcd_entropy_sel_type { + VCD_ENTROPY_SEL_CAVLC = 0x1, + VCD_ENTROPY_SEL_CABAC = 0x2 +}; + +enum vcd_cabac_model_type { + VCD_CABAC_MODEL_NUMBER_0 = 0x1, + VCD_CABAC_MODEL_NUMBER_1 = 0x2, + VCD_CABAC_MODEL_NUMBER_2 = 0x3 +}; + +struct vcd_property_entropy_control_type { + enum vcd_entropy_sel_type e_entropy_sel; + enum vcd_cabac_model_type e_cabac_model; +}; + +enum vcd_db_config_type { + VCD_DB_ALL_BLOCKING_BOUNDARY = 0x1, + VCD_DB_DISABLE = 0x2, + VCD_DB_SKIP_SLICE_BOUNDARY = 0x3 +}; +struct vcd_property_db_config_type { + enum vcd_db_config_type e_db_config; + u32 n_slice_alpha_offset; + u32 n_slice_beta_offset; +}; + +enum vcd_rate_control_type { + VCD_RATE_CONTROL_OFF = 0x1, + VCD_RATE_CONTROL_VBR_VFR = 0x2, + VCD_RATE_CONTROL_VBR_CFR = 0x3, + VCD_RATE_CONTROL_CBR_VFR = 0x4, + VCD_RATE_CONTROL_CBR_CFR = 0x5 +}; + +struct vcd_property_rate_control_type { + enum vcd_rate_control_type e_rate_control; +}; + +struct vcd_property_qp_range_type { + u32 n_max_qp; + u32 n_min_qp; +}; + +struct vcd_property_session_qp_type { + u32 n_i_frame_qp; + u32 n_p_frame_qp; + u32 n_b_frame_qp; +}; + +struct vcd_property_i_period_type { + u32 n_p_frames; + u32 n_b_frames; +}; + +struct vcd_property_vop_timing_type { + u32 n_vop_time_resolution; +}; + +struct vcd_property_short_header_type { + u32 b_short_header; +}; + +struct vcd_property_intra_refresh_mb_number_type { + u32 n_cir_mb_number; +}; + +struct vcd_property_req_i_frame_type { + u32 b_req_i_frame; +}; + +struct vcd_frame_rect_type{ + u32 n_left; + u32 n_top; + u32 n_right; + u32 n_bottom; +}; + +struct vcd_property_dec_output_buffer_type { + struct vcd_frame_rect_type disp_frm; +}; + +#endif diff --git a/drivers/misc/video_core/720p/vcd/vcd_status.h b/drivers/misc/video_core/720p/vcd/vcd_status.h new file mode 100644 index 0000000000000..03a3b27414927 --- /dev/null +++ b/drivers/misc/video_core/720p/vcd/vcd_status.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _VCD_ERR_STATUS_H_ +#define _VCD_ERR_STATUS_H_ + +#define VCD_EVT_RESP_BASE 0x1000 +#define VCD_EVT_RESP_OPEN (VCD_EVT_RESP_BASE + 0x1) +#define VCD_EVT_RESP_START (VCD_EVT_RESP_BASE + 0x2) +#define VCD_EVT_RESP_STOP (VCD_EVT_RESP_BASE + 0x3) +#define VCD_EVT_RESP_PAUSE (VCD_EVT_RESP_BASE + 0x4) +#define VCD_EVT_RESP_FLUSH_INPUT_DONE (VCD_EVT_RESP_BASE + 0x5) +#define VCD_EVT_RESP_FLUSH_OUTPUT_DONE (VCD_EVT_RESP_BASE + 0x6) +#define VCD_EVT_RESP_INPUT_FLUSHED (VCD_EVT_RESP_BASE + 0x7) +#define VCD_EVT_RESP_OUTPUT_FLUSHED (VCD_EVT_RESP_BASE + 0x8) +#define VCD_EVT_RESP_INPUT_DONE (VCD_EVT_RESP_BASE + 0x9) +#define VCD_EVT_RESP_OUTPUT_DONE (VCD_EVT_RESP_BASE + 0xa) + +#define VCD_EVT_IND_BASE 0x2000 +#define VCD_EVT_IND_RECONFIG (VCD_EVT_IND_BASE + 0x1) +#define VCD_EVT_IND_HWERRFATAL (VCD_EVT_IND_BASE + 0x2) +#define VCD_EVT_IND_RESOURCES_LOST (VCD_EVT_IND_BASE + 0x3) + +#define VCD_S_SUCCESS 0x0 + +#define VCD_S_ERR_BASE 0x80000000 +#define VCD_ERR_FAIL (VCD_S_ERR_BASE + 0x1) +#define VCD_ERR_ALLOC_FAIL (VCD_S_ERR_BASE + 0x2) +#define VCD_ERR_ILLEGAL_OP (VCD_S_ERR_BASE + 0x3) +#define VCD_ERR_ILLEGAL_PARM (VCD_S_ERR_BASE + 0x4) +#define VCD_ERR_BAD_POINTER (VCD_S_ERR_BASE + 0x5) +#define VCD_ERR_BAD_HANDLE (VCD_S_ERR_BASE + 0x6) +#define VCD_ERR_NOT_SUPPORTED (VCD_S_ERR_BASE + 0x7) +#define VCD_ERR_BAD_STATE (VCD_S_ERR_BASE + 0x8) +#define VCD_ERR_BUSY (VCD_S_ERR_BASE + 0x9) +#define VCD_ERR_MAX_CLIENT (VCD_S_ERR_BASE + 0xa) +#define VCD_ERR_IFRAME_EXPECTED (VCD_S_ERR_BASE + 0xb) +#define VCD_ERR_INTRLCD_FIELD_DROP (VCD_S_ERR_BASE + 0xc) +#define VCD_ERR_HW_FATAL (VCD_S_ERR_BASE + 0xd) +#define VCD_ERR_BITSTREAM_ERR (VCD_S_ERR_BASE + 0xe) +#define VCD_FAILED(rc) ((rc > VCD_S_ERR_BASE) ? TRUE : FALSE) + +#endif diff --git a/drivers/misc/video_core/720p/vcd/vcd_sub.c b/drivers/misc/video_core/720p/vcd/vcd_sub.c new file mode 100644 index 0000000000000..5e1cacc6e6015 --- /dev/null +++ b/drivers/misc/video_core/720p/vcd/vcd_sub.c @@ -0,0 +1,3255 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include + +#include "video_core_type.h" +#include "vcd.h" +#include "vdec_internal.h" + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) + +u8 *vcd_pmem_get_physical(struct video_client_ctx *client_ctx, + unsigned long kernel_vaddr) +{ + unsigned long phy_addr, user_vaddr; + int pmem_fd; + struct file *file; + s32 buffer_index = -1; + + if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_INPUT, + FALSE, &user_vaddr, &kernel_vaddr, + &phy_addr, &pmem_fd, &file, + &buffer_index)) { + + return (u8 *) phy_addr; + } else if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, + FALSE, &user_vaddr, &kernel_vaddr, &phy_addr, &pmem_fd, &file, + &buffer_index)) { + return (u8 *) phy_addr; + } else { + VCD_MSG_ERROR("Couldn't get physical address"); + + return NULL; + } + +} + +void vcd_reset_device_channels(struct vcd_dev_ctxt_type *p_dev_ctxt) +{ + p_dev_ctxt->n_ddl_frame_ch_free = p_dev_ctxt->n_ddl_frame_ch_depth; + p_dev_ctxt->n_ddl_cmd_ch_free = p_dev_ctxt->n_ddl_cmd_ch_depth; + p_dev_ctxt->n_ddl_frame_ch_interim = 0; + p_dev_ctxt->n_ddl_cmd_ch_interim = 0; +} + +u32 vcd_get_command_channel( + struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_transc_type **pp_transc) +{ + u32 b_result = FALSE; + + *pp_transc = NULL; + + if (p_dev_ctxt->n_ddl_cmd_ch_free > 0) { + if (p_dev_ctxt->b_ddl_cmd_concurrency) { + --p_dev_ctxt->n_ddl_cmd_ch_free; + b_result = TRUE; + } else if ((p_dev_ctxt->n_ddl_frame_ch_free + + p_dev_ctxt->n_ddl_frame_ch_interim) + == p_dev_ctxt->n_ddl_frame_ch_depth) { + --p_dev_ctxt->n_ddl_cmd_ch_free; + b_result = TRUE; + } + } + + if (b_result) { + *pp_transc = vcd_get_free_trans_tbl_entry(p_dev_ctxt); + + if (!*pp_transc) { + b_result = FALSE; + + vcd_release_command_channel(p_dev_ctxt, *pp_transc); + } + + } + return b_result; +} + +u32 vcd_get_command_channel_in_loop( + struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_transc_type **pp_transc) +{ + u32 b_result = FALSE; + + *pp_transc = NULL; + + if (p_dev_ctxt->n_ddl_cmd_ch_interim > 0) { + if (p_dev_ctxt->b_ddl_cmd_concurrency) { + --p_dev_ctxt->n_ddl_cmd_ch_interim; + b_result = TRUE; + } else if ((p_dev_ctxt->n_ddl_frame_ch_free + + p_dev_ctxt->n_ddl_frame_ch_interim) + == p_dev_ctxt->n_ddl_frame_ch_depth) { + --p_dev_ctxt->n_ddl_cmd_ch_interim; + b_result = TRUE; + } + } else { + b_result = vcd_get_command_channel(p_dev_ctxt, pp_transc); + } + + if (b_result && !*pp_transc) { + *pp_transc = vcd_get_free_trans_tbl_entry(p_dev_ctxt); + + if (!*pp_transc) { + b_result = FALSE; + + ++p_dev_ctxt->n_ddl_cmd_ch_interim; + } + + } + + return b_result; +} + +void vcd_mark_command_channel(struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_transc_type *p_transc) +{ + ++p_dev_ctxt->n_ddl_cmd_ch_interim; + + vcd_release_trans_tbl_entry(p_transc); + if (p_dev_ctxt->n_ddl_cmd_ch_interim + + p_dev_ctxt->n_ddl_cmd_ch_free > + p_dev_ctxt->n_ddl_cmd_ch_depth) { + VCD_MSG_ERROR("\n Command channel access counters messed up"); + vcd_assert(); + } +} + +void vcd_release_command_channel( + struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type *p_transc) +{ + ++p_dev_ctxt->n_ddl_cmd_ch_free; + + vcd_release_trans_tbl_entry(p_transc); + if (p_dev_ctxt->n_ddl_cmd_ch_interim + p_dev_ctxt->n_ddl_cmd_ch_free > + p_dev_ctxt->n_ddl_cmd_ch_depth) { + VCD_MSG_ERROR("\n Command channel access counters messed up"); + vcd_assert(); + } +} + +void vcd_release_multiple_command_channels(struct vcd_dev_ctxt_type + *p_dev_ctxt, u32 n_channels) +{ + p_dev_ctxt->n_ddl_cmd_ch_free += n_channels; + + if (p_dev_ctxt->n_ddl_cmd_ch_interim + + p_dev_ctxt->n_ddl_cmd_ch_free > + p_dev_ctxt->n_ddl_cmd_ch_depth) { + VCD_MSG_ERROR("\n Command channel access counters messed up"); + vcd_assert(); + } +} + +void vcd_release_interim_command_channels(struct vcd_dev_ctxt_type *p_dev_ctxt) +{ + p_dev_ctxt->n_ddl_cmd_ch_free += p_dev_ctxt->n_ddl_cmd_ch_interim; + p_dev_ctxt->n_ddl_cmd_ch_interim = 0; + + if (p_dev_ctxt->n_ddl_cmd_ch_interim + p_dev_ctxt->n_ddl_cmd_ch_free > + p_dev_ctxt->n_ddl_cmd_ch_depth) { + VCD_MSG_ERROR("\n Command channel access counters messed up"); + vcd_assert(); + } +} + +u32 vcd_get_frame_channel(struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_transc_type **pp_transc) +{ + u32 b_result = FALSE; + + if (p_dev_ctxt->n_ddl_frame_ch_free > 0) { + if (p_dev_ctxt->b_ddl_cmd_concurrency) { + --p_dev_ctxt->n_ddl_frame_ch_free; + b_result = TRUE; + } else if ((p_dev_ctxt->n_ddl_cmd_ch_free + + p_dev_ctxt->n_ddl_cmd_ch_interim) + == p_dev_ctxt->n_ddl_cmd_ch_depth) { + --p_dev_ctxt->n_ddl_frame_ch_free; + b_result = TRUE; + } + } + + if (b_result) { + *pp_transc = vcd_get_free_trans_tbl_entry(p_dev_ctxt); + + if (!*pp_transc) { + b_result = FALSE; + + vcd_release_frame_channel(p_dev_ctxt, *pp_transc); + } else { + (*pp_transc)->e_type = VCD_CMD_CODE_FRAME; + } + + } + + return b_result; +} + +u32 vcd_get_frame_channel_in_loop( + struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_transc_type **pp_transc) +{ + u32 b_result = FALSE; + + *pp_transc = NULL; + + if (p_dev_ctxt->n_ddl_frame_ch_interim > 0) { + if (p_dev_ctxt->b_ddl_cmd_concurrency) { + --p_dev_ctxt->n_ddl_frame_ch_interim; + b_result = TRUE; + } else if ((p_dev_ctxt->n_ddl_cmd_ch_free + + p_dev_ctxt->n_ddl_cmd_ch_interim) + == p_dev_ctxt->n_ddl_cmd_ch_depth) { + --p_dev_ctxt->n_ddl_frame_ch_interim; + b_result = TRUE; + } + } else { + b_result = vcd_get_frame_channel(p_dev_ctxt, pp_transc); + } + + if (b_result && !*pp_transc) { + *pp_transc = vcd_get_free_trans_tbl_entry(p_dev_ctxt); + + if (!*pp_transc) { + b_result = FALSE; + VCD_MSG_FATAL("\n%s: All transactions are busy;" + "Couldnt find free one\n", __func__); + ++p_dev_ctxt->n_ddl_frame_ch_interim; + } + + } + + return b_result; +} + +void vcd_mark_frame_channel(struct vcd_dev_ctxt_type *p_dev_ctxt) +{ + ++p_dev_ctxt->n_ddl_frame_ch_interim; + + if (p_dev_ctxt->n_ddl_frame_ch_interim + + p_dev_ctxt->n_ddl_frame_ch_free > + p_dev_ctxt->n_ddl_cmd_ch_depth) { + VCD_MSG_FATAL("Frame channel access counters messed up"); + vcd_assert(); + } +} + +void vcd_release_frame_channel(struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_transc_type *p_transc) +{ + ++p_dev_ctxt->n_ddl_frame_ch_free; + + vcd_release_trans_tbl_entry(p_transc); + + if (p_dev_ctxt->n_ddl_frame_ch_interim + + p_dev_ctxt->n_ddl_frame_ch_free > + p_dev_ctxt->n_ddl_cmd_ch_depth) { + VCD_MSG_FATAL("Frame channel access counters messed up"); + vcd_assert(); + } +} + +void vcd_release_multiple_frame_channels(struct vcd_dev_ctxt_type + *p_dev_ctxt, u32 n_channels) +{ + p_dev_ctxt->n_ddl_frame_ch_free += n_channels; + + if (p_dev_ctxt->n_ddl_frame_ch_interim + + p_dev_ctxt->n_ddl_frame_ch_free > + p_dev_ctxt->n_ddl_frame_ch_depth) { + VCD_MSG_FATAL("Frame channel access counters messed up"); + vcd_assert(); + } +} + +void vcd_release_interim_frame_channels(struct vcd_dev_ctxt_type + *p_dev_ctxt) +{ + p_dev_ctxt->n_ddl_frame_ch_free += + p_dev_ctxt->n_ddl_frame_ch_interim; + p_dev_ctxt->n_ddl_frame_ch_interim = 0; + + if (p_dev_ctxt->n_ddl_frame_ch_free > + p_dev_ctxt->n_ddl_cmd_ch_depth) { + VCD_MSG_FATAL("Frame channel access counters messed up"); + vcd_assert(); + } +} + +u32 vcd_core_is_busy(struct vcd_dev_ctxt_type *p_dev_ctxt) +{ + if (((p_dev_ctxt->n_ddl_cmd_ch_free + + p_dev_ctxt->n_ddl_cmd_ch_interim) != + p_dev_ctxt->n_ddl_cmd_ch_depth) + || + ((p_dev_ctxt->n_ddl_frame_ch_free + + p_dev_ctxt->n_ddl_frame_ch_interim) != + p_dev_ctxt->n_ddl_frame_ch_depth) + ) { + return TRUE; + } else { + return FALSE; + } +} + +void vcd_device_timer_start(struct vcd_dev_ctxt_type *p_dev_ctxt) +{ + if (p_dev_ctxt->config.pf_timer_start) + p_dev_ctxt->config.pf_timer_start(p_dev_ctxt->p_hw_timer_handle, + p_dev_ctxt->n_hw_time_out); +} + +void vcd_device_timer_stop(struct vcd_dev_ctxt_type *p_dev_ctxt) +{ + if (p_dev_ctxt->config.pf_timer_stop) + p_dev_ctxt->config.pf_timer_stop(p_dev_ctxt->p_hw_timer_handle); +} + + +u32 vcd_common_allocate_set_buffer( + struct vcd_clnt_ctxt_type_t *p_cctxt, + enum vcd_buffer_type e_buffer, + u32 n_buf_size, struct vcd_buffer_pool_type **pp_buf_pool) +{ + u32 rc = VCD_S_SUCCESS; + struct vcd_buffer_requirement_type Buf_req; + struct vcd_property_hdr_type Prop_hdr; + struct vcd_buffer_pool_type *p_buf_pool; + + if (e_buffer == VCD_BUFFER_INPUT) { + Prop_hdr.prop_id = DDL_I_INPUT_BUF_REQ; + p_buf_pool = &p_cctxt->in_buf_pool; + } else if (e_buffer == VCD_BUFFER_OUTPUT) { + Prop_hdr.prop_id = DDL_I_OUTPUT_BUF_REQ; + p_buf_pool = &p_cctxt->out_buf_pool; + } else { + rc = VCD_ERR_ILLEGAL_PARM; + } + + VCD_FAILED_RETURN(rc, "Invalid buffer type provided"); + + *pp_buf_pool = p_buf_pool; + + if (p_buf_pool->n_count > 0 && + p_buf_pool->n_validated == p_buf_pool->n_count) { + VCD_MSG_ERROR("Buffer pool is full"); + + return VCD_ERR_FAIL; + } + + if (!p_buf_pool->a_entries) { + Prop_hdr.n_size = sizeof(Buf_req); + rc = ddl_get_property(p_cctxt->ddl_handle, &Prop_hdr, &Buf_req); + + if (!VCD_FAILED(rc)) { + rc = vcd_alloc_buffer_pool_entries(p_buf_pool, + &Buf_req); + + } else { + VCD_MSG_ERROR("rc = 0x%x. Failed: ddl_get_property", + rc); + } + + } + + if (!VCD_FAILED(rc)) { + if (p_buf_pool->buf_req.n_size > n_buf_size) { + VCD_MSG_ERROR("\n required buffer size %u " + "allocated size %u", p_buf_pool->buf_req. + n_size, n_buf_size); + + rc = VCD_ERR_ILLEGAL_PARM; + } + } + + return rc; +} + +u32 vcd_set_buffer_internal( + struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_buffer_pool_type *p_buf_pool, u8 *p_buffer, u32 n_buf_size) +{ + struct vcd_buffer_entry_type *p_buf_entry; + + p_buf_entry = vcd_find_buffer_pool_entry(p_buf_pool, p_buffer); + if (p_buf_entry) { + VCD_MSG_ERROR("This buffer address already exists"); + + return VCD_ERR_ILLEGAL_OP; + } + + if (((u32) p_buffer % p_buf_pool->buf_req.n_align)) { + VCD_MSG_ERROR("Provided addr is not aligned"); + + return VCD_ERR_BAD_POINTER; + } + + p_buf_entry = vcd_get_free_buffer_pool_entry(p_buf_pool); + if (!p_buf_entry) { + VCD_MSG_ERROR("Can't allocate buffer pool is full"); + + return VCD_ERR_FAIL; + } + + p_buf_entry->p_virtual = p_buffer; + + p_buf_entry->p_physical = + (u8 *) vcd_pmem_get_physical(p_cctxt->p_client_data, + (unsigned long)p_buffer); + + if (!p_buf_entry->p_physical) { + VCD_MSG_ERROR("Couldn't get physical address"); + + return VCD_ERR_BAD_POINTER; + } + + if (((u32)p_buf_entry->p_physical % + p_buf_pool->buf_req.n_align)) { + VCD_MSG_ERROR("Physical addr is not aligned"); + return VCD_ERR_BAD_POINTER ; + } + + p_buf_entry->n_size = n_buf_size; + p_buf_entry->frame.n_alloc_len = n_buf_size; + p_buf_entry->b_allocated = FALSE; + + p_buf_entry->frame.p_virtual = p_buf_entry->p_virtual; + p_buf_entry->frame.p_physical = p_buf_entry->p_physical; + + p_buf_pool->n_validated++; + + return VCD_S_SUCCESS; + +} + +u32 vcd_allocate_buffer_internal( + struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_buffer_pool_type *p_buf_pool, + u32 n_buf_size, u8 **pp_vir_buf_addr, u8 **pp_phy_buf_addr) +{ + struct vcd_buffer_entry_type *p_buf_entry; + struct vcd_buffer_requirement_type *p_buf_req; + u32 n_addr; + int rc = 0; + + p_buf_entry = vcd_get_free_buffer_pool_entry(p_buf_pool); + if (!p_buf_entry) { + VCD_MSG_ERROR("Can't allocate buffer pool is full"); + + return VCD_ERR_FAIL; + } + + p_buf_req = &p_buf_pool->buf_req; + + n_buf_size += p_buf_req->n_align; + + rc = vcd_pmem_alloc(n_buf_size, &p_buf_entry->p_alloc, + &p_buf_entry->p_physical); + + if (rc < 0) { + VCD_MSG_ERROR("Buffer allocation failed"); + + return VCD_ERR_ALLOC_FAIL; + } + + p_buf_entry->n_size = n_buf_size; + p_buf_entry->frame.n_alloc_len = n_buf_size; + + if (!p_buf_entry->p_physical) { + VCD_MSG_ERROR("Couldn't get physical address"); + + return VCD_ERR_BAD_POINTER; + } + + p_buf_entry->b_allocated = TRUE; + + if (p_buf_req->n_align > 0) { + + n_addr = (u32) p_buf_entry->p_physical; + n_addr += p_buf_req->n_align; + n_addr -= (n_addr % p_buf_req->n_align); + p_buf_entry->p_virtual = p_buf_entry->p_alloc; + p_buf_entry->p_virtual += (u32) (n_addr - (u32) + p_buf_entry->p_physical); + p_buf_entry->p_physical = (u8 *) n_addr; + } else { + VCD_MSG_LOW("No buffer alignment required"); + + p_buf_entry->p_virtual = p_buf_entry->p_alloc; + + } + + p_buf_entry->frame.p_virtual = p_buf_entry->p_virtual; + p_buf_entry->frame.p_physical = p_buf_entry->p_physical; + + *pp_vir_buf_addr = p_buf_entry->p_virtual; + *pp_phy_buf_addr = p_buf_entry->p_physical; + + p_buf_pool->n_allocated++; + p_buf_pool->n_validated++; + + return VCD_S_SUCCESS; +} + +u32 vcd_free_one_buffer_internal( + struct vcd_clnt_ctxt_type_t *p_cctxt, + enum vcd_buffer_type e_buffer, u8 *p_buffer) +{ + struct vcd_buffer_pool_type *p_buf_pool; + u32 rc = VCD_S_SUCCESS; + struct vcd_buffer_entry_type *p_buf_entry; + + if (e_buffer == VCD_BUFFER_INPUT) + p_buf_pool = &p_cctxt->in_buf_pool; + else if (e_buffer == VCD_BUFFER_OUTPUT) + p_buf_pool = &p_cctxt->out_buf_pool; + else + rc = VCD_ERR_ILLEGAL_PARM; + + VCD_FAILED_RETURN(rc, "Invalid buffer type provided"); + + p_buf_entry = vcd_find_buffer_pool_entry(p_buf_pool, p_buffer); + if (!p_buf_entry) { + VCD_MSG_ERROR("Buffer addr %p not found. Can't free buffer", + p_buffer); + + return VCD_ERR_ILLEGAL_PARM; + } + if (p_buf_entry->b_in_use) { + VCD_MSG_ERROR("\n Buffer is in use and is not flushed"); + return VCD_ERR_ILLEGAL_OP; + } + + VCD_MSG_LOW("Freeing buffer %p. Allocated %d", + p_buf_entry->p_virtual, p_buf_entry->b_allocated); + + if (p_buf_entry->b_allocated) { + vcd_pmem_free(p_buf_entry->p_alloc, p_buf_entry->p_physical); + + p_buf_pool->n_allocated--; + + } + + memset(p_buf_entry, 0, sizeof(struct vcd_buffer_entry_type)); + + p_buf_pool->n_validated--; + + return VCD_S_SUCCESS; +} + +u32 vcd_free_buffers_internal( + struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_buffer_pool_type *p_buf_pool) +{ + u32 rc = VCD_S_SUCCESS; + u32 i; + + VCD_MSG_LOW("vcd_free_buffers_internal:"); + + if (p_buf_pool->a_entries) { + for (i = 1; i <= p_buf_pool->n_count; i++) { + if (p_buf_pool->a_entries[i].b_valid && + p_buf_pool->a_entries[i].b_allocated) { + vcd_pmem_free(p_buf_pool->a_entries[i].p_alloc, + p_buf_pool->a_entries[i]. + p_physical); + } + } + + } + + vcd_reset_buffer_pool_for_reuse(p_buf_pool); + + return rc; +} + +u32 vcd_alloc_buffer_pool_entries( + struct vcd_buffer_pool_type *p_buf_pool, + struct vcd_buffer_requirement_type *p_buf_req) +{ + + VCD_MSG_LOW("vcd_alloc_buffer_pool_entries:"); + + p_buf_pool->buf_req = *p_buf_req; + + p_buf_pool->n_count = p_buf_req->n_actual_count; + p_buf_pool->a_entries = (struct vcd_buffer_entry_type *) + vcd_malloc(sizeof(struct vcd_buffer_entry_type) * + (p_buf_pool->n_count + 1)); + + if (!p_buf_pool->a_entries) { + VCD_MSG_ERROR("Buf_pool entries alloc failed"); + + return VCD_ERR_ALLOC_FAIL; + } + + p_buf_pool->a_queue = (struct vcd_buffer_entry_type **) + vcd_malloc(sizeof(struct vcd_buffer_entry_type *) * + p_buf_pool->n_count); + + if (!p_buf_pool->a_queue) { + VCD_MSG_ERROR("Buf_pool queue alloc failed"); + + vcd_free(p_buf_pool->a_entries); + + return VCD_ERR_ALLOC_FAIL; + } + + memset(p_buf_pool->a_entries, 0, + sizeof(struct vcd_buffer_entry_type) * + (p_buf_pool->n_count + 1)); + + memset(p_buf_pool->a_queue, + 0, sizeof(struct vcd_buffer_entry_type *) * + p_buf_pool->n_count); + + p_buf_pool->a_entries[0].b_valid = TRUE; + + p_buf_pool->n_q_head = 0; + p_buf_pool->n_q_tail = (u16) (p_buf_pool->n_count - 1); + p_buf_pool->n_q_len = 0; + + p_buf_pool->n_validated = 0; + p_buf_pool->n_allocated = 0; + p_buf_pool->n_in_use = 0; + + return VCD_S_SUCCESS; +} + +void vcd_free_buffer_pool_entries(struct vcd_buffer_pool_type *p_buf_pool) +{ + VCD_MSG_LOW("vcd_free_buffer_pool_entries:"); + + if (p_buf_pool->a_entries) { + vcd_free(p_buf_pool->a_entries); + vcd_free(p_buf_pool->a_queue); + } + + memset(p_buf_pool, 0, sizeof(struct vcd_buffer_pool_type)); +} + +void vcd_flush_in_use_buffer_pool_entries(struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_buffer_pool_type *p_buf_pool, u32 event) +{ + u32 i; + VCD_MSG_LOW("vcd_flush_buffer_pool_entries: event=0x%x", event); + + if (p_buf_pool->a_entries) { + for (i = 0; i <= p_buf_pool->n_count; i++) { + if (p_buf_pool->a_entries[i].p_virtual && + p_buf_pool->a_entries[i].b_in_use) { + p_cctxt->callback(event, VCD_S_SUCCESS, + &p_buf_pool->a_entries[i].frame, + sizeof(struct vcd_frame_data_type), + p_cctxt, p_cctxt->p_client_data); + p_buf_pool->a_entries[i].b_in_use = FALSE; + VCD_BUFFERPOOL_INUSE_DECREMENT( + p_buf_pool->n_in_use); + } + } + } +} + + +void vcd_reset_buffer_pool_for_reuse(struct vcd_buffer_pool_type *p_buf_pool) +{ + VCD_MSG_LOW("vcd_reset_buffer_pool_for_reuse:"); + + memset(&p_buf_pool->a_entries[1], 0, + sizeof(struct vcd_buffer_entry_type) * + p_buf_pool->n_count); + memset(p_buf_pool->a_queue, + 0, sizeof(struct vcd_buffer_entry_type *) * + p_buf_pool->n_count); + + p_buf_pool->n_q_head = 0; + p_buf_pool->n_q_tail = (u16) (p_buf_pool->n_count - 1); + p_buf_pool->n_q_len = 0; + + p_buf_pool->n_validated = 0; + p_buf_pool->n_allocated = 0; + p_buf_pool->n_in_use = 0; + +} + +struct vcd_buffer_entry_type *vcd_get_free_buffer_pool_entry + (struct vcd_buffer_pool_type *p_pool) { + u32 i; + + i = 1; + while (i <= p_pool->n_count && p_pool->a_entries[i].b_valid) + i++; + + + if (i <= p_pool->n_count) { + p_pool->a_entries[i].b_valid = TRUE; + + return &p_pool->a_entries[i]; + } else { + return NULL; + } +} + +struct vcd_buffer_entry_type *vcd_find_buffer_pool_entry + (struct vcd_buffer_pool_type *p_pool, u8 *p_v_addr) +{ + u32 i; + u32 b_found = FALSE; + + for (i = 0; i <= p_pool->n_count && !b_found; i++) { + if (p_pool->a_entries[i].p_virtual == p_v_addr) + b_found = TRUE; + + } + + if (b_found) + return &p_pool->a_entries[i - 1]; + else + return NULL; + +} + +u32 vcd_buffer_pool_entry_en_q( + struct vcd_buffer_pool_type *p_pool, + struct vcd_buffer_entry_type *p_entry) +{ + u16 i; + u16 n_q_cntr; + u32 b_found = FALSE; + + if (p_pool->n_q_len == p_pool->n_count) + return FALSE; + + for (i = 0, n_q_cntr = p_pool->n_q_head; + !b_found && i < p_pool->n_q_len; + i++, n_q_cntr = (n_q_cntr + 1) % p_pool->n_count) { + if (p_pool->a_queue[n_q_cntr] == p_entry) + b_found = TRUE; + } + + if (b_found) { + VCD_MSG_HIGH("\n this output buffer is already present" + " in queue"); + VCD_MSG_HIGH("\n Vir Addr %p Phys Addr %p", + p_entry->p_virtual, p_entry->p_physical); + return FALSE; + } + + p_pool->n_q_tail = (p_pool->n_q_tail + 1) % p_pool->n_count; + p_pool->n_q_len++; + p_pool->a_queue[p_pool->n_q_tail] = p_entry; + + return TRUE; +} + +struct vcd_buffer_entry_type *vcd_buffer_pool_entry_de_q + (struct vcd_buffer_pool_type *p_pool) { + struct vcd_buffer_entry_type *p_entry; + + if (!p_pool || !p_pool->n_q_len) + return NULL; + + p_entry = p_pool->a_queue[p_pool->n_q_head]; + p_pool->n_q_head = (p_pool->n_q_head + 1) % p_pool->n_count; + p_pool->n_q_len--; + + return p_entry; +} + +void vcd_flush_output_buffers(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + struct vcd_buffer_pool_type *p_buf_pool; + struct vcd_buffer_entry_type *p_buf_entry; + u32 n_count = 0; + struct vcd_property_hdr_type prop_hdr; + + VCD_MSG_LOW("vcd_flush_output_buffers:"); + + p_buf_pool = &p_cctxt->out_buf_pool; + + p_buf_entry = vcd_buffer_pool_entry_de_q(p_buf_pool); + while (p_buf_entry) { + if (!p_cctxt->b_decoding || p_buf_entry->b_in_use) { + p_buf_entry->frame.n_data_len = 0; + + p_cctxt->callback(VCD_EVT_RESP_OUTPUT_FLUSHED, + VCD_S_SUCCESS, + &p_buf_entry->frame, + sizeof(struct vcd_frame_data_type), + p_cctxt, p_cctxt->p_client_data); + + p_buf_entry->b_in_use = FALSE; + + n_count++; + } + + p_buf_entry = vcd_buffer_pool_entry_de_q(p_buf_pool); + } + p_buf_pool->n_in_use = 0; + + if (p_cctxt->b_sched_clnt_valid && n_count > 0) { + VCD_MSG_LOW("Updating scheduler O tkns = %u", n_count); + + (void)sched_update_client_o_tkn(p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, + FALSE, + n_count * + p_cctxt-> + n_sched_o_tkn_per_ip_frm); + } + + if (p_cctxt->b_ddl_hdl_valid && p_cctxt->b_decoding) { + prop_hdr.prop_id = DDL_I_REQ_OUTPUT_FLUSH; + prop_hdr.n_size = sizeof(u32); + n_count = 0x1; + + (void)ddl_set_property(p_cctxt->ddl_handle, &prop_hdr, + &n_count); + } +} + +u32 vcd_flush_buffers(struct vcd_clnt_ctxt_type_t *p_cctxt, u32 n_mode) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + u32 rc = VCD_S_SUCCESS; + struct vcd_buffer_entry_type *p_buf_entry; + + VCD_MSG_LOW("vcd_flush_buffers:"); + + if (n_mode > VCD_FLUSH_ALL || !(n_mode & VCD_FLUSH_ALL)) { + VCD_MSG_ERROR("Invalid flush mode %d", n_mode); + + return VCD_ERR_ILLEGAL_PARM; + } + + VCD_MSG_MED("Flush mode %d requested", n_mode); + + if ((n_mode & VCD_FLUSH_INPUT) && p_cctxt->b_sched_clnt_valid) { + rc = vcd_map_sched_status(sched_flush_client_buffer + (p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, + (void **)&p_buf_entry)); + + while (!VCD_FAILED(rc) && + rc != VCD_S_SCHED_QEMPTY && p_buf_entry) { + if (p_buf_entry->p_virtual) { + p_cctxt->callback(VCD_EVT_RESP_INPUT_FLUSHED, + VCD_S_SUCCESS, + &p_buf_entry->frame, + sizeof(struct + vcd_frame_data_type), + p_cctxt, + p_cctxt->p_client_data); + + } + + p_buf_entry->b_in_use = FALSE; + VCD_BUFFERPOOL_INUSE_DECREMENT( + p_cctxt->in_buf_pool.n_in_use); + p_buf_entry = NULL; + rc = vcd_map_sched_status(sched_flush_client_buffer + (p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, + (void **)&p_buf_entry)); + } + + } + VCD_FAILED_RETURN(rc, "Failed: sched_flush_client_buffer"); + + if (p_cctxt->status.n_frame_submitted > 0) { + + p_cctxt->status.n_flush_mode |= n_mode; + + } else { + + if (n_mode & VCD_FLUSH_OUTPUT) { + vcd_flush_output_buffers(p_cctxt); + vcd_release_all_clnt_frm_transc(p_cctxt); + } + + } + + return VCD_S_SUCCESS; +} + +void vcd_flush_buffers_in_err_fatal(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + VCD_MSG_LOW("\n vcd_flush_buffers_in_err_fatal:"); + (void) vcd_flush_buffers(p_cctxt, VCD_FLUSH_ALL); + vcd_flush_in_use_buffer_pool_entries(p_cctxt, + &p_cctxt->in_buf_pool, VCD_EVT_RESP_INPUT_FLUSHED); + vcd_flush_in_use_buffer_pool_entries(p_cctxt, + &p_cctxt->out_buf_pool, VCD_EVT_RESP_OUTPUT_FLUSHED); + p_cctxt->status.n_flush_mode = VCD_FLUSH_ALL; + vcd_send_flush_done(p_cctxt, VCD_S_SUCCESS); +} + +u32 vcd_init_client_context(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + u32 rc; + + VCD_MSG_LOW("vcd_init_client_context:"); + + rc = ddl_open(&p_cctxt->ddl_handle, p_cctxt->b_decoding); + + VCD_FAILED_RETURN(rc, "Failed: ddl_open"); + p_cctxt->b_ddl_hdl_valid = TRUE; + + p_cctxt->clnt_state.e_state = VCD_CLIENT_STATE_OPEN; + p_cctxt->clnt_state.p_state_table = + vcd_get_client_state_table(VCD_CLIENT_STATE_OPEN); + + p_cctxt->n_signature = VCD_SIGNATURE; + p_cctxt->b_live = TRUE; + + p_cctxt->cmd_q.e_pending_cmd = VCD_CMD_NONE; + + return rc; +} + +void vcd_destroy_client_context(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt; + struct vcd_clnt_ctxt_type_t *p_client; + u32 rc = VCD_S_SUCCESS; + int n_idx; + + VCD_MSG_LOW("vcd_destroy_client_context:"); + + p_dev_ctxt = p_cctxt->p_dev_ctxt; + + if (p_cctxt == p_dev_ctxt->p_cctxt_list_head) { + VCD_MSG_MED("Clnt list head clnt being removed"); + + p_dev_ctxt->p_cctxt_list_head = p_cctxt->p_next; + } else { + p_client = p_dev_ctxt->p_cctxt_list_head; + while (p_client && p_cctxt != p_client->p_next) + p_client = p_client->p_next; + + if (p_client) + p_client->p_next = p_cctxt->p_next; + + if (!p_client) { + rc = VCD_ERR_FAIL; + + VCD_MSG_ERROR("Client not found in client list"); + } + } + + if (VCD_FAILED(rc)) + return; + + if (p_cctxt->b_sched_clnt_valid) { + rc = VCD_S_SUCCESS; + while (!VCD_FAILED(rc) && rc != VCD_S_SCHED_QEMPTY) { + + rc = vcd_map_sched_status(sched_flush_client_buffer + (p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, + (void *)&n_idx)); + if (VCD_FAILED(rc)) + VCD_MSG_ERROR("\n Failed: " + "sched_flush_client_buffer"); + } + + rc = vcd_map_sched_status(sched_remove_client + (p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl)); + if (VCD_FAILED(rc)) + VCD_MSG_ERROR("\n Failed: sched_remove_client"); + + p_cctxt->b_sched_clnt_valid = FALSE; + } + + if (p_cctxt->seq_hdr.p_sequence_header) { + vcd_pmem_free(p_cctxt->seq_hdr.p_sequence_header, + p_cctxt->p_seq_hdr_phy_addr); + p_cctxt->seq_hdr.p_sequence_header = NULL; + } + + vcd_free_buffers_internal(p_cctxt, &p_cctxt->in_buf_pool); + vcd_free_buffers_internal(p_cctxt, &p_cctxt->out_buf_pool); + vcd_free_buffer_pool_entries(&p_cctxt->in_buf_pool); + vcd_free_buffer_pool_entries(&p_cctxt->out_buf_pool); + vcd_release_all_clnt_transc(p_cctxt); + + if (p_cctxt->b_ddl_hdl_valid) { + (void)ddl_close(&p_cctxt->ddl_handle); + p_cctxt->b_ddl_hdl_valid = FALSE; + } + vcd_free(p_cctxt); +} + +u32 vcd_check_for_client_context( + struct vcd_dev_ctxt_type *p_dev_ctxt, s32 driver_id) +{ + struct vcd_clnt_ctxt_type_t *p_client; + + p_client = p_dev_ctxt->p_cctxt_list_head; + while (p_client && p_client->driver_id != driver_id) + p_client = p_client->p_next; + + if (!p_client) + return FALSE; + else + return TRUE; +} + +u32 vcd_validate_driver_handle( + struct vcd_dev_ctxt_type *p_dev_ctxt, s32 driver_handle) +{ + driver_handle--; + + if (driver_handle < 0 || + driver_handle >= VCD_DRIVER_INSTANCE_MAX || + !p_dev_ctxt->b_driver_ids[driver_handle]) { + return FALSE; + } else { + return TRUE; + } +} + +u32 vcd_client_cmd_en_q( + struct vcd_clnt_ctxt_type_t *p_cctxt, enum vcd_command_type command) +{ + u32 b_result; + + if (p_cctxt->cmd_q.e_pending_cmd == VCD_CMD_NONE) { + p_cctxt->cmd_q.e_pending_cmd = command; + b_result = TRUE; + } else { + b_result = FALSE; + } + + return b_result; +} + +void vcd_client_cmd_flush_and_en_q( + struct vcd_clnt_ctxt_type_t *p_cctxt, enum vcd_command_type command) +{ + p_cctxt->cmd_q.e_pending_cmd = command; +} + +u32 vcd_client_cmd_de_q(struct vcd_clnt_ctxt_type_t *p_cctxt, + enum vcd_command_type *p_command) +{ + if (p_cctxt->cmd_q.e_pending_cmd == VCD_CMD_NONE) + return FALSE; + + *p_command = p_cctxt->cmd_q.e_pending_cmd; + p_cctxt->cmd_q.e_pending_cmd = VCD_CMD_NONE; + + return TRUE; +} + +u32 vcd_get_next_queued_client_cmd(struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_clnt_ctxt_type_t **p_cctxt, enum vcd_command_type *p_command) +{ + struct vcd_clnt_ctxt_type_t *p_client = p_dev_ctxt->p_cctxt_list_head; + u32 b_result = FALSE; + + while (p_client && !b_result) { + *p_cctxt = p_client; + b_result = vcd_client_cmd_de_q(p_client, p_command); + p_client = p_client->p_next; + } + return b_result; +} + +u32 vcd_map_sched_status(enum sched_status_type sched_status) +{ + u32 rc = VCD_S_SUCCESS; + + switch (sched_status) { + + case SCHED_S_OK: + rc = VCD_S_SUCCESS; + break; + + case SCHED_S_EOF: + rc = VCD_S_SCHED_EOS; + break; + + case SCHED_S_QEMPTY: + rc = VCD_S_SCHED_QEMPTY; + break; + + case SCHED_S_QFULL: + rc = VCD_S_SCHED_QFULL; + break; + + default: + rc = VCD_ERR_FAIL; + break; + } + + return rc; +} + +u32 vcd_submit_cmd_sess_start(struct vcd_transc_type *p_transc) +{ + u32 rc; + struct vcd_sequence_hdr_type Seq_hdr; + + VCD_MSG_LOW("vcd_submit_cmd_sess_start:"); + + if (p_transc->p_cctxt->b_decoding) { + + if (p_transc->p_cctxt->seq_hdr.p_sequence_header) { + Seq_hdr.n_sequence_header_len = + p_transc->p_cctxt->seq_hdr. + n_sequence_header_len; + Seq_hdr.p_sequence_header = + p_transc->p_cctxt->p_seq_hdr_phy_addr; + + rc = ddl_decode_start(p_transc->p_cctxt->ddl_handle, + &Seq_hdr, (void *)p_transc); + } else { + rc = ddl_decode_start(p_transc->p_cctxt->ddl_handle, + NULL, (void *)p_transc); + } + + } else { + rc = ddl_encode_start(p_transc->p_cctxt->ddl_handle, + (void *)p_transc); + } + if (!VCD_FAILED(rc)) { + p_transc->p_cctxt->status.n_cmd_submitted++; + vcd_device_timer_start(p_transc->p_cctxt->p_dev_ctxt); + } else + VCD_MSG_ERROR("rc = 0x%x. Failed: ddl start", rc); + + return rc; +} + +u32 vcd_submit_cmd_sess_end(struct vcd_transc_type *p_transc) +{ + u32 rc; + + VCD_MSG_LOW("vcd_submit_cmd_sess_end:"); + + if (p_transc->p_cctxt->b_decoding) { + rc = ddl_decode_end(p_transc->p_cctxt->ddl_handle, + (void *)p_transc); + } else { + rc = ddl_encode_end(p_transc->p_cctxt->ddl_handle, + (void *)p_transc); + } + if (!VCD_FAILED(rc)) { + p_transc->p_cctxt->status.n_cmd_submitted++; + vcd_device_timer_start(p_transc->p_cctxt->p_dev_ctxt); + } else + VCD_MSG_ERROR("rc = 0x%x. Failed: ddl end", rc); + + return rc; +} + +void vcd_submit_cmd_client_close(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + (void) ddl_close(&p_cctxt->ddl_handle); + p_cctxt->b_ddl_hdl_valid = FALSE; + p_cctxt->status.b_cleaning_up = FALSE; + if (p_cctxt->status.b_close_pending) { + vcd_destroy_client_context(p_cctxt); + vcd_handle_for_last_clnt_close(p_cctxt->p_dev_ctxt, TRUE); + } +} + +u32 vcd_submit_command_in_continue(struct vcd_dev_ctxt_type + *p_dev_ctxt, struct vcd_transc_type *p_transc) +{ + struct vcd_property_hdr_type prop_hdr; + struct vcd_clnt_ctxt_type_t *p_client = NULL; + enum vcd_command_type cmd = VCD_CMD_NONE; + u32 rc = VCD_ERR_FAIL; + u32 b_result = FALSE, n_flush = 0, event = 0; + u32 b_break = FALSE; + + VCD_MSG_LOW("\n vcd_submit_command_in_continue:"); + + while (!b_break) { + b_result = vcd_get_next_queued_client_cmd(p_dev_ctxt, + &p_client, &cmd); + + if (!b_result) + b_break = TRUE; + else { + p_transc->e_type = cmd; + p_transc->p_cctxt = p_client; + + switch (cmd) { + case VCD_CMD_CODEC_START: + { + rc = vcd_submit_cmd_sess_start(p_transc); + event = VCD_EVT_RESP_START; + break; + } + case VCD_CMD_CODEC_STOP: + { + rc = vcd_submit_cmd_sess_end(p_transc); + event = VCD_EVT_RESP_STOP; + break; + } + case VCD_CMD_OUTPUT_FLUSH: + { + prop_hdr.prop_id = DDL_I_REQ_OUTPUT_FLUSH; + prop_hdr.n_size = sizeof(u32); + n_flush = 0x1; + (void) ddl_set_property(p_client->ddl_handle, + &prop_hdr, &n_flush); + vcd_release_command_channel(p_dev_ctxt, + p_transc); + rc = VCD_S_SUCCESS; + break; + } + case VCD_CMD_CLIENT_CLOSE: + { + vcd_submit_cmd_client_close(p_client); + vcd_release_command_channel(p_dev_ctxt, + p_transc); + rc = VCD_S_SUCCESS; + break; + } + default: + { + VCD_MSG_ERROR("\n vcd_submit_command: Unknown" + "command %d", (int)cmd); + vcd_assert(); + break; + } + } + + if (!VCD_FAILED(rc)) { + b_break = TRUE; + } else { + VCD_MSG_ERROR("vcd_submit_command %d: failed 0x%x", + cmd, rc); + p_client->callback(event, rc, NULL, 0, p_client, + p_client->p_client_data); + } + } + } + return b_result; +} + +u32 vcd_schedule_frame(struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_clnt_ctxt_type_t **pp_cctxt, struct vcd_buffer_entry_type + **pp_ip_buf_entry) +{ + u32 rc = VCD_S_SUCCESS; + VCD_MSG_LOW("vcd_schedule_frame:"); + + if (!p_dev_ctxt->p_cctxt_list_head) { + VCD_MSG_HIGH("Client list empty"); + return FALSE; + } + + rc = vcd_map_sched_status(sched_de_queue_frame(p_dev_ctxt->sched_hdl, + (void **) pp_ip_buf_entry, (void **) pp_cctxt)); + if (VCD_FAILED(rc)) { + VCD_MSG_FATAL("vcd_submit_frame: sched_de_queue_frame" + "failed 0x%x", rc); + return FALSE; + } + + if (rc == VCD_S_SCHED_QEMPTY) { + VCD_MSG_HIGH("No frame available. Sched queues are empty"); + return FALSE; + } + + if (!*pp_cctxt || !*pp_ip_buf_entry) { + VCD_MSG_FATAL("Sched returned invalid values. ctxt=%p," + "ipbuf=%p", *pp_cctxt, *pp_ip_buf_entry); + return FALSE; + } + + if (rc == VCD_S_SCHED_EOS) + (*pp_ip_buf_entry)->frame.n_flags |= VCD_FRAME_FLAG_EOS; + + return TRUE; +} + +void vcd_try_submit_frame(struct vcd_dev_ctxt_type *p_dev_ctxt) +{ + struct vcd_transc_type *p_transc; + u32 rc = VCD_S_SUCCESS; + struct vcd_clnt_ctxt_type_t *p_cctxt = NULL; + struct vcd_buffer_entry_type *p_ip_buf_entry = NULL; + u32 b_result = FALSE; + + VCD_MSG_LOW("vcd_try_submit_frame:"); + + if (!vcd_get_frame_channel(p_dev_ctxt, &p_transc)) + return; + + if (!vcd_schedule_frame(p_dev_ctxt, &p_cctxt, &p_ip_buf_entry)) { + vcd_release_frame_channel(p_dev_ctxt, p_transc); + return; + } + + rc = vcd_power_event(p_dev_ctxt, p_cctxt, VCD_EVT_PWR_CLNT_CMD_BEGIN); + + if (!VCD_FAILED(rc)) { + p_transc->p_cctxt = p_cctxt; + p_transc->p_ip_buf_entry = p_ip_buf_entry; + + b_result = vcd_submit_frame(p_dev_ctxt, p_transc); + } else { + VCD_MSG_ERROR("Failed: VCD_EVT_PWR_CLNT_CMD_BEGIN"); + + (void) vcd_requeue_input_frame(p_dev_ctxt, p_cctxt, + p_ip_buf_entry); + + (void) vcd_map_sched_status(sched_update_client_o_tkn( + p_dev_ctxt->sched_hdl, p_cctxt->sched_clnt_hdl, + TRUE, p_cctxt->n_sched_o_tkn_per_ip_frm)); + } + + if (!b_result) { + vcd_release_frame_channel(p_dev_ctxt, p_transc); + (void) vcd_power_event(p_dev_ctxt, p_cctxt, + VCD_EVT_PWR_CLNT_CMD_FAIL); + } +} + +u32 vcd_submit_frame(struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_transc_type *p_transc) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = NULL; + struct vcd_frame_data_type *p_ip_frm_entry; + struct vcd_buffer_entry_type *p_op_buf_entry = NULL; + u32 rc = VCD_S_SUCCESS; + u32 evcode = 0; + struct ddl_frame_data_type_tag ddl_ip_frm; + struct ddl_frame_data_type_tag ddl_op_frm; + + VCD_MSG_LOW("vcd_submit_frame:"); + p_cctxt = p_transc->p_cctxt; + p_ip_frm_entry = &p_transc->p_ip_buf_entry->frame; + + p_transc->p_op_buf_entry = p_op_buf_entry; + p_transc->n_ip_frm_tag = p_ip_frm_entry->n_ip_frm_tag; + p_transc->time_stamp = p_ip_frm_entry->time_stamp; + p_ip_frm_entry->n_ip_frm_tag = (u32) p_transc; + memset(&ddl_ip_frm, 0, sizeof(ddl_ip_frm)); + memset(&ddl_op_frm, 0, sizeof(ddl_op_frm)); + if (p_cctxt->b_decoding) { + evcode = CLIENT_STATE_EVENT_NUMBER(pf_decode_frame); + ddl_ip_frm.vcd_frm = *p_ip_frm_entry; + rc = ddl_decode_frame(p_cctxt->ddl_handle, &ddl_ip_frm, + (void *) p_transc); + } else { + p_op_buf_entry = vcd_buffer_pool_entry_de_q( + &p_cctxt->out_buf_pool); + if (!p_op_buf_entry) { + VCD_MSG_ERROR("Sched provided frame when no" + "op buffer was present"); + rc = VCD_ERR_FAIL; + } else { + p_op_buf_entry->b_in_use = TRUE; + p_cctxt->out_buf_pool.n_in_use++; + ddl_ip_frm.vcd_frm = *p_ip_frm_entry; + ddl_ip_frm.n_frm_delta = + vcd_calculate_frame_delta(p_cctxt, + p_ip_frm_entry); + + ddl_op_frm.vcd_frm = p_op_buf_entry->frame; + + evcode = CLIENT_STATE_EVENT_NUMBER(pf_encode_frame); + + rc = ddl_encode_frame(p_cctxt->ddl_handle, + &ddl_ip_frm, &ddl_op_frm, (void *) p_transc); + } + } + p_ip_frm_entry->n_ip_frm_tag = p_transc->n_ip_frm_tag; + if (!VCD_FAILED(rc)) { + vcd_device_timer_start(p_dev_ctxt); + p_cctxt->status.n_frame_submitted++; + if (p_ip_frm_entry->n_flags & VCD_FRAME_FLAG_EOS) + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_EOS, evcode); + } else { + VCD_MSG_ERROR("Frame submission failed. rc = 0x%x", rc); + vcd_handle_submit_frame_failed(p_dev_ctxt, p_transc); + } + return TRUE; +} + +u32 vcd_try_submit_frame_in_continue(struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_transc_type *p_transc) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = NULL; + struct vcd_buffer_entry_type *p_ip_buf_entry = NULL; + + VCD_MSG_LOW("vcd_try_submit_frame_in_continue:"); + + if (!vcd_schedule_frame(p_dev_ctxt, &p_cctxt, &p_ip_buf_entry)) + return FALSE; + + p_transc->p_cctxt = p_cctxt; + p_transc->p_ip_buf_entry = p_ip_buf_entry; + + return vcd_submit_frame(p_dev_ctxt, p_transc); +} + +u32 vcd_process_cmd_sess_start(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + struct vcd_transc_type *p_transc; + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_LOW("vcd_process_cmd_sess_start:"); + if (vcd_get_command_channel(p_cctxt->p_dev_ctxt, &p_transc)) { + rc = vcd_power_event(p_cctxt->p_dev_ctxt, + p_cctxt, VCD_EVT_PWR_CLNT_CMD_BEGIN); + + if (!VCD_FAILED(rc)) { + p_transc->e_type = VCD_CMD_CODEC_START; + p_transc->p_cctxt = p_cctxt; + rc = vcd_submit_cmd_sess_start(p_transc); + } else { + VCD_MSG_ERROR("Failed: VCD_EVT_PWR_CLNT_CMD_BEGIN"); + } + + if (VCD_FAILED(rc)) { + vcd_release_command_channel(p_cctxt->p_dev_ctxt, + p_transc); + } + } else { + u32 b_result; + + b_result = vcd_client_cmd_en_q(p_cctxt, VCD_CMD_CODEC_START); + if (!b_result) { + rc = VCD_ERR_BUSY; + VCD_MSG_ERROR("%s(): vcd_client_cmd_en_q() " + "failed\n", __func__); + vcd_assert(); + } + } + + if (VCD_FAILED(rc)) { + (void)vcd_power_event(p_cctxt->p_dev_ctxt, + p_cctxt, VCD_EVT_PWR_CLNT_CMD_FAIL); + } + + return rc; +} + +void vcd_send_frame_done_in_eos(struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_input_frame, u32 valid_opbuf) +{ + VCD_MSG_LOW("vcd_send_frame_done_in_eos:"); + + if (!p_input_frame->p_virtual && !valid_opbuf) { + VCD_MSG_MED("Sending NULL output with EOS"); + + p_cctxt->out_buf_pool.a_entries[0].frame.n_flags = + VCD_FRAME_FLAG_EOS; + p_cctxt->out_buf_pool.a_entries[0].frame.n_data_len = 0; + p_cctxt->out_buf_pool.a_entries[0].frame.time_stamp = + p_input_frame->time_stamp; + p_cctxt->out_buf_pool.a_entries[0].frame.n_ip_frm_tag = + p_input_frame->n_ip_frm_tag; + + p_cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, + VCD_S_SUCCESS, + &p_cctxt->out_buf_pool.a_entries[0].frame, + sizeof(struct vcd_frame_data_type), + p_cctxt, p_cctxt->p_client_data); + + memset(&p_cctxt->out_buf_pool.a_entries[0].frame, + 0, sizeof(struct vcd_frame_data_type)); + } else if (!p_input_frame->n_data_len) { + if (p_cctxt->b_decoding) { + vcd_send_frame_done_in_eos_for_dec(p_cctxt, + p_input_frame); + } else { + vcd_send_frame_done_in_eos_for_enc(p_cctxt, + p_input_frame); + } + + } +} + +void vcd_send_frame_done_in_eos_for_dec( + struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_input_frame) +{ + struct vcd_buffer_entry_type *p_buf_entry; + struct vcd_property_hdr_type prop_hdr; + u32 rc; + struct ddl_frame_data_type_tag ddl_frm; + + prop_hdr.prop_id = DDL_I_DPB_RETRIEVE; + prop_hdr.n_size = sizeof(struct ddl_frame_data_type_tag); + memset(&ddl_frm, 0, sizeof(ddl_frm)); + rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, &ddl_frm); + + if (VCD_FAILED(rc) || !ddl_frm.vcd_frm.p_virtual) { + p_cctxt->status.eos_trig_ip_frm = *p_input_frame; + + p_cctxt->status.b_eos_wait_for_op_buf = TRUE; + + return; + } + + p_buf_entry = vcd_find_buffer_pool_entry(&p_cctxt->out_buf_pool, + ddl_frm.vcd_frm.p_virtual); + if (!p_buf_entry) { + VCD_MSG_ERROR("Unrecognized buffer address provided = %p", + ddl_frm.vcd_frm.p_virtual); + + vcd_assert(); + } else { + (void) + vcd_map_sched_status(sched_update_client_o_tkn + (p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, FALSE, + p_cctxt->n_sched_o_tkn_per_ip_frm)); + + VCD_MSG_MED("Sending non-NULL output with EOS"); + + p_buf_entry->frame.n_data_len = 0; + p_buf_entry->frame.n_offset = 0; + p_buf_entry->frame.n_flags |= VCD_FRAME_FLAG_EOS; + p_buf_entry->frame.n_ip_frm_tag = p_input_frame->n_ip_frm_tag; + p_buf_entry->frame.time_stamp = p_input_frame->time_stamp; + + p_cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, + VCD_S_SUCCESS, + &p_buf_entry->frame, + sizeof(struct vcd_frame_data_type), + p_cctxt, p_cctxt->p_client_data); + + p_buf_entry->b_in_use = FALSE; + VCD_BUFFERPOOL_INUSE_DECREMENT(p_cctxt->out_buf_pool.n_in_use); + } +} + +void vcd_send_frame_done_in_eos_for_enc( + struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_input_frame) +{ + struct vcd_buffer_entry_type *p_op_buf_entry; + + if (!p_cctxt->out_buf_pool.n_q_len) { + p_cctxt->status.eos_trig_ip_frm = *p_input_frame; + + p_cctxt->status.b_eos_wait_for_op_buf = TRUE; + + return; + } + + p_op_buf_entry = vcd_buffer_pool_entry_de_q(&p_cctxt->out_buf_pool); + if (!p_op_buf_entry) { + VCD_MSG_ERROR("%s(): vcd_buffer_pool_entry_de_q() " + "failed\n", __func__); + vcd_assert(); + } else { + (void) + vcd_map_sched_status(sched_update_client_o_tkn + (p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, FALSE, + p_cctxt->n_sched_o_tkn_per_ip_frm)); + + VCD_MSG_MED("Sending non-NULL output with EOS"); + + p_op_buf_entry->frame.n_data_len = 0; + p_op_buf_entry->frame.n_flags |= VCD_FRAME_FLAG_EOS; + p_op_buf_entry->frame.n_ip_frm_tag = + p_input_frame->n_ip_frm_tag; + p_op_buf_entry->frame.time_stamp = p_input_frame->time_stamp; + + p_cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, + VCD_S_SUCCESS, + &p_op_buf_entry->frame, + sizeof(struct vcd_frame_data_type), + p_cctxt, p_cctxt->p_client_data); + } +} + +u32 vcd_handle_recvd_eos( + struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_input_frame, u32 *pb_eos_handled) +{ + union sched_value_type sched_val; + u32 rc; + + VCD_MSG_LOW("vcd_handle_recvd_eos:"); + + *pb_eos_handled = FALSE; + + if (p_input_frame->p_virtual && + p_input_frame->n_data_len) + return VCD_S_SUCCESS; + + p_input_frame->n_data_len = 0; + + rc = vcd_map_sched_status(sched_get_client_param + (p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, + SCHED_I_CLNT_CURRQLEN, &sched_val)); + + VCD_FAILED_RETURN(rc, "Failed: sched_get_client_param"); + + if (sched_val.un_value > 0) { + rc = vcd_map_sched_status(sched_mark_client_eof + (p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl)); + + if (!VCD_FAILED(rc)) { + *pb_eos_handled = TRUE; + } else { + VCD_MSG_ERROR("rc = 0x%x. Failed: " + "sched_mark_client_eof", rc); + } + + } else if (p_cctxt->b_decoding && !p_input_frame->p_virtual) { + rc = vcd_map_sched_status(sched_update_client_o_tkn + (p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, TRUE, + p_cctxt->n_sched_o_tkn_per_ip_frm)); + } else if (!p_cctxt->b_decoding) { + + vcd_send_frame_done_in_eos(p_cctxt, p_input_frame, FALSE); + + if (p_cctxt->status.b_eos_wait_for_op_buf) { + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_EOS, + CLIENT_STATE_EVENT_NUMBER + (pf_encode_frame)); + } + + *pb_eos_handled = TRUE; + + } + + if (*pb_eos_handled && + p_input_frame->p_virtual && + !p_input_frame->n_data_len) { + p_cctxt->callback(VCD_EVT_RESP_INPUT_DONE, + VCD_S_SUCCESS, + p_input_frame, + sizeof(struct vcd_frame_data_type), + p_cctxt, p_cctxt->p_client_data); + } + return rc; +} + +u32 vcd_handle_first_decode_frame(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + struct ddl_property_dec_pic_buffers_type dpb; + struct vcd_property_hdr_type prop_hdr; + u32 rc; + u16 i; + u16 n_q_cntr; + struct ddl_frame_data_type_tag *p_frm_entry; + struct ddl_frame_data_type_tag ddl_frm; + struct vcd_buffer_pool_type *p_out_buf_pool; + + VCD_MSG_LOW("vcd_handle_first_decode_frame:"); + + if (!p_cctxt->in_buf_pool.a_entries || + !p_cctxt->out_buf_pool.a_entries || + p_cctxt->in_buf_pool.n_validated != + p_cctxt->in_buf_pool.n_count || + p_cctxt->out_buf_pool.n_validated != + p_cctxt->out_buf_pool.n_count) { + VCD_MSG_ERROR("Buffer pool is not completely setup yet"); + + return VCD_ERR_BAD_STATE; + } + + rc = vcd_add_client_to_sched(p_cctxt); + + VCD_FAILED_RETURN(rc, "Failed: vcd_add_client_to_sched"); + + prop_hdr.prop_id = DDL_I_DPB; + prop_hdr.n_size = sizeof(dpb); + + p_out_buf_pool = &p_cctxt->out_buf_pool; + + p_frm_entry = + (struct ddl_frame_data_type_tag *) + vcd_malloc(sizeof(struct ddl_frame_data_type_tag) * + p_out_buf_pool->n_count); + if (!p_frm_entry) { + VCD_MSG_ERROR("Memory allocation failure"); + + return VCD_ERR_ALLOC_FAIL; + } + + for (i = 1; i <= p_out_buf_pool->n_count; i++) + p_frm_entry[i - 1].vcd_frm = p_out_buf_pool->a_entries[i].frame; + + + dpb.a_dec_pic_buffers = p_frm_entry; + dpb.n_no_of_dec_pic_buf = p_out_buf_pool->n_count; + rc = ddl_set_property(p_cctxt->ddl_handle, &prop_hdr, &dpb); + + vcd_free(p_frm_entry); + + VCD_FAILED_RETURN(rc, "Failed: DDL set DDL_I_DPB"); + + if (p_out_buf_pool->n_q_len > 0) { + prop_hdr.prop_id = DDL_I_DPB_RELEASE; + prop_hdr.n_size = sizeof(struct ddl_frame_data_type_tag); + + for (i = 0, n_q_cntr = p_out_buf_pool->n_q_head; + !VCD_FAILED(rc) && i < p_out_buf_pool->n_q_len; + i++, n_q_cntr = (n_q_cntr + 1) % + p_out_buf_pool->n_count) { + + ddl_frm.vcd_frm = + p_out_buf_pool->a_queue[n_q_cntr]->frame; + + rc = ddl_set_property(p_cctxt->ddl_handle, &prop_hdr, + &ddl_frm); + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR + ("Error returning output buffer to HW"); + + p_out_buf_pool->a_queue[n_q_cntr]->b_in_use = + FALSE; + } else { + p_out_buf_pool->a_queue[n_q_cntr]->b_in_use = + TRUE; + p_out_buf_pool->n_in_use++; + } + } + + if (!VCD_FAILED(rc)) { + rc = vcd_map_sched_status(sched_update_client_o_tkn + (p_cctxt->p_dev_ctxt-> + sched_hdl, + p_cctxt->sched_clnt_hdl, + TRUE, + p_cctxt-> + n_sched_o_tkn_per_ip_frm * + p_out_buf_pool->n_q_len)); + } + } + return rc; +} + +u32 vcd_setup_with_ddl_capabilities(struct vcd_dev_ctxt_type *p_dev_ctxt) +{ + struct vcd_property_hdr_type Prop_hdr; + struct ddl_property_capability_type capability; + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_LOW("vcd_setup_with_ddl_capabilities:"); + + if (!p_dev_ctxt->n_ddl_cmd_ch_depth) { + Prop_hdr.prop_id = DDL_I_CAPABILITY; + Prop_hdr.n_size = sizeof(capability); + + /* + ** Since this is underlying core's property we don't need a + ** ddl client handle. + */ + rc = ddl_get_property(NULL, &Prop_hdr, &capability); + + if (!VCD_FAILED(rc)) { + /* + ** Allocate the transaction table. + */ + p_dev_ctxt->n_trans_tbl_size = + (VCD_MAX_CLIENT_TRANSACTIONS * + capability.n_max_num_client) + + capability.n_general_command_depth; + + p_dev_ctxt->a_trans_tbl = (struct vcd_transc_type *) + vcd_malloc(sizeof(struct vcd_transc_type) * + p_dev_ctxt->n_trans_tbl_size); + + if (!p_dev_ctxt->a_trans_tbl) { + VCD_MSG_ERROR("Transaction table alloc failed"); + + rc = VCD_ERR_ALLOC_FAIL; + } else { + memset(p_dev_ctxt->a_trans_tbl, 0, + sizeof(struct vcd_transc_type) * + p_dev_ctxt->n_trans_tbl_size); + + /* + ** Set the command/frame depth + */ + p_dev_ctxt->b_ddl_cmd_concurrency = + !capability.b_exclusive; + p_dev_ctxt->n_ddl_frame_ch_depth = + capability.n_frame_command_depth; + p_dev_ctxt->n_ddl_cmd_ch_depth = + capability.n_general_command_depth; + + vcd_reset_device_channels(p_dev_ctxt); + + p_dev_ctxt->n_hw_time_out = + capability.n_ddl_time_out_in_ms; + + } + } + } + return rc; +} + +struct vcd_transc_type *vcd_get_free_trans_tbl_entry + (struct vcd_dev_ctxt_type *p_dev_ctxt) { + u8 i; + + if (!p_dev_ctxt->a_trans_tbl) + return NULL; + + i = 0; + while (i < p_dev_ctxt->n_trans_tbl_size && + p_dev_ctxt->a_trans_tbl[i].b_in_use) + i++; + + if (i == p_dev_ctxt->n_trans_tbl_size) { + return NULL; + } else { + memset(&p_dev_ctxt->a_trans_tbl[i], 0, + sizeof(struct vcd_transc_type)); + + p_dev_ctxt->a_trans_tbl[i].b_in_use = TRUE; + + return &p_dev_ctxt->a_trans_tbl[i]; + } +} + +void vcd_release_trans_tbl_entry(struct vcd_transc_type *p_trans_entry) +{ + if (p_trans_entry) + p_trans_entry->b_in_use = FALSE; +} + +u32 vcd_add_client_to_sched(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + struct vcd_property_hdr_type prop_hdr; + struct sched_client_init_param_type sched_input_init; + u32 rc, seqhdr_present = 0;; + + if (p_cctxt->b_sched_clnt_valid) { + VCD_MSG_HIGH("Schedulder client is already added "); + return VCD_S_SUCCESS; + } + + prop_hdr.prop_id = DDL_I_FRAME_PROC_UNITS; + prop_hdr.n_size = sizeof(p_cctxt->n_frm_p_units); + rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, + &p_cctxt->n_frm_p_units); + VCD_FAILED_RETURN(rc, "Failed: Get DDL_I_FRAME_PROC_UNITS"); + + if (p_cctxt->b_decoding) { + p_cctxt->frm_rate.n_fps_numerator = VCD_DEC_INITIAL_FRAME_RATE; + p_cctxt->frm_rate.n_fps_denominator = 1; + + sched_input_init.n_o_tkn_per_ip_frm = + p_cctxt->n_sched_o_tkn_per_ip_frm = + VCD_SCHEDULER_DEC_DFLT_OTKN_PERFRM; + sched_input_init.n_o_tkn_max = + p_cctxt->n_sched_o_tkn_per_ip_frm * + p_cctxt->out_buf_pool.n_count+1; + } else { + sched_input_init.n_o_tkn_per_ip_frm = + p_cctxt->n_sched_o_tkn_per_ip_frm = + VCD_SCHEDULER_ENC_DFLT_OTKN_PERFRM; + prop_hdr.prop_id = DDL_I_SEQHDR_PRESENT; + prop_hdr.n_size = sizeof(seqhdr_present); + rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, + &seqhdr_present); + if (!VCD_FAILED(rc)) { + if (seqhdr_present == 0x1) { + VCD_MSG_MED("Sequence hdr present"); + sched_input_init.n_o_tkn_per_ip_frm++; + } + sched_input_init.n_o_tkn_max = + p_cctxt->out_buf_pool.n_count; + prop_hdr.prop_id = VCD_I_FRAME_RATE; + prop_hdr.n_size = sizeof(p_cctxt->frm_rate); + rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, + &p_cctxt->frm_rate); + } + } + + VCD_FAILED_RETURN(rc, "Failed: DDL get VCD_I_FRAME_RATE"); + + if (p_cctxt->b_live) + sched_input_init.client_ctgy = SCHED_CLNT_RT_NOBUFF; + else + sched_input_init.client_ctgy = SCHED_CLNT_NONRT; + + sched_input_init.n_max_queue_len = MAX(p_cctxt->in_buf_pool.n_count, + VCD_MAX_SCHEDULER_QUEUE_SIZE + (p_cctxt->frm_rate. + n_fps_numerator, + p_cctxt->frm_rate. + n_fps_denominator)); + p_cctxt->n_reqd_perf_lvl = + p_cctxt->n_frm_p_units * p_cctxt->frm_rate.n_fps_numerator / + p_cctxt->frm_rate.n_fps_denominator; + + sched_input_init.frm_rate.n_numer = p_cctxt->frm_rate.n_fps_numerator; + sched_input_init.frm_rate.n_denom = p_cctxt->frm_rate.n_fps_denominator; + sched_input_init.n_p_tkn_per_frm = p_cctxt->n_frm_p_units; + sched_input_init.n_alloc_p_tkn_rate = p_cctxt->n_reqd_perf_lvl; + + sched_input_init.n_o_tkn_init = 0; + + sched_input_init.p_client_data = p_cctxt; + + rc = vcd_map_sched_status(sched_add_client + (p_cctxt->p_dev_ctxt->sched_hdl, + &sched_input_init, + &p_cctxt->sched_clnt_hdl)); + + if (!VCD_FAILED(rc)) + p_cctxt->b_sched_clnt_valid = TRUE; + + return rc; +} + +u32 vcd_handle_input_done( + struct vcd_clnt_ctxt_type_t *p_cctxt, + void *p_payload, u32 event, u32 status) +{ + struct vcd_transc_type *p_transc; + struct ddl_frame_data_type_tag *p_frame = + (struct ddl_frame_data_type_tag *) p_payload; + u32 rc; + + if (!p_cctxt->status.n_frame_submitted && + !p_cctxt->status.n_frame_delayed) { + VCD_MSG_ERROR("Input done was not expected"); + vcd_assert(); + + return VCD_ERR_BAD_STATE; + } + + rc = vcd_validate_io_done_pyld(p_payload, status); + VCD_FAILED_RETURN(rc, "Bad input done payload"); + + p_transc = (struct vcd_transc_type *)p_frame->vcd_frm.n_ip_frm_tag; + + if ((p_transc->p_ip_buf_entry->frame.p_virtual != + p_frame->vcd_frm.p_virtual) + || !p_transc->p_ip_buf_entry->b_in_use) { + VCD_MSG_ERROR("Bad frm transaction state"); + vcd_assert(); + } + + p_frame->vcd_frm.n_ip_frm_tag = p_transc->n_ip_frm_tag; + + p_cctxt->callback(event, + status, + &p_frame->vcd_frm, + sizeof(struct vcd_frame_data_type), + p_cctxt, p_cctxt->p_client_data); + + p_transc->e_frame_type = p_frame->vcd_frm.e_frame_type; + + p_transc->p_ip_buf_entry->b_in_use = FALSE; + VCD_BUFFERPOOL_INUSE_DECREMENT(p_cctxt->in_buf_pool.n_in_use); + p_transc->p_ip_buf_entry = NULL; + p_transc->b_input_done = TRUE; + + if (p_transc->b_input_done && p_transc->b_frame_done) + p_transc->b_in_use = FALSE; + + if (VCD_FAILED(status)) { + VCD_MSG_ERROR("INPUT_DONE returned err = 0x%x", status); + vcd_handle_input_done_failed(p_cctxt, p_transc); + } + + if (p_cctxt->status.n_frame_submitted > 0) + p_cctxt->status.n_frame_submitted--; + else + p_cctxt->status.n_frame_delayed--; + + if (!VCD_FAILED(status) && + p_cctxt->b_decoding) { + if (p_frame->vcd_frm.b_interlaced) + vcd_handle_input_done_for_interlacing(p_cctxt); + if (p_frame->b_frm_trans_end) + vcd_handle_input_done_with_trans_end(p_cctxt); + } + + return VCD_S_SUCCESS; +} + +void vcd_handle_input_done_in_eos( + struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload, u32 status) +{ + struct vcd_transc_type *p_transc; + struct ddl_frame_data_type_tag *p_frame = + (struct ddl_frame_data_type_tag *) p_payload; + + if (VCD_FAILED(vcd_validate_io_done_pyld(p_payload, status))) + return; + + p_transc = (struct vcd_transc_type *)p_frame->vcd_frm.n_ip_frm_tag; + + (void)vcd_handle_input_done(p_cctxt, + p_payload, VCD_EVT_RESP_INPUT_DONE, status); + + if ((p_frame->vcd_frm.n_flags & VCD_FRAME_FLAG_EOS)) { + VCD_MSG_HIGH("Got input done for EOS initiator"); + p_transc->b_input_done = FALSE; + p_transc->b_in_use = TRUE; + } +} + +u32 vcd_validate_io_done_pyld(void *p_payload, u32 status) +{ + struct ddl_frame_data_type_tag *p_frame = + (struct ddl_frame_data_type_tag *) p_payload; + + if (!p_frame) { + VCD_MSG_ERROR("Bad payload from DDL"); + vcd_assert(); + + return VCD_ERR_BAD_POINTER; + } + + if (!p_frame->vcd_frm.n_ip_frm_tag || + p_frame->vcd_frm.n_ip_frm_tag == VCD_FRAMETAG_INVALID) { + VCD_MSG_ERROR("bad input frame tag"); + vcd_assert(); + return VCD_ERR_BAD_POINTER; + } + + if (!p_frame->vcd_frm.p_virtual && + status != VCD_ERR_INTRLCD_FIELD_DROP) + return VCD_ERR_BAD_POINTER; + + return VCD_S_SUCCESS; +} + +void vcd_handle_input_done_failed( + struct vcd_clnt_ctxt_type_t *p_cctxt, struct vcd_transc_type *p_transc) +{ + if (p_cctxt->b_decoding) { + (void)vcd_map_sched_status(sched_update_client_o_tkn + (p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, TRUE, + p_cctxt->n_sched_o_tkn_per_ip_frm)); + + p_transc->b_in_use = FALSE; + } +} + +void vcd_handle_input_done_for_interlacing(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + u32 rc; + + p_cctxt->status.n_int_field_cnt++; + + if (p_cctxt->status.n_int_field_cnt == 1) { + rc = vcd_map_sched_status(sched_update_client_o_tkn + (p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, TRUE, + p_cctxt->n_sched_o_tkn_per_ip_frm)); + + if (VCD_FAILED(rc)) + VCD_MSG_ERROR("sched_update_client_o_tkn failed"); + } else if (p_cctxt->status.n_int_field_cnt == + VCD_DEC_NUM_INTERLACED_FIELDS) + p_cctxt->status.n_int_field_cnt = 0; +} + +void vcd_handle_input_done_with_trans_end( + struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + u32 rc; + union sched_value_type sched_val; + if (!p_cctxt->b_decoding) + return; + + if (p_cctxt->out_buf_pool.n_in_use < + p_cctxt->out_buf_pool.buf_req.n_min_count) + return; + + rc = vcd_map_sched_status(sched_get_client_param( + p_cctxt->p_dev_ctxt->sched_hdl, p_cctxt->sched_clnt_hdl, + SCHED_I_CLNT_OTKNCURRENT, &sched_val)); + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("sched_get_client_param:OTKNCURRENT failed"); + return; + } + + if (!sched_val.un_value) { + VCD_MSG_MED("All output buffers with core are pending display"); + + rc = vcd_map_sched_status(sched_update_client_o_tkn( + p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, + TRUE, + p_cctxt->n_sched_o_tkn_per_ip_frm)); + + if (VCD_FAILED(rc)) + VCD_MSG_ERROR("sched_update_client_o_tkn failed"); + } +} + +u32 vcd_handle_output_required(struct vcd_clnt_ctxt_type_t + *p_cctxt, void *p_payload, u32 status) +{ + struct vcd_transc_type *p_transc; + struct ddl_frame_data_type_tag *p_frame = + (struct ddl_frame_data_type_tag *)p_payload; + u32 rc; + + if (!p_cctxt->status.n_frame_submitted && + !p_cctxt->status.n_frame_delayed) { + VCD_MSG_ERROR("\n Input done was not expected"); + return VCD_ERR_BAD_STATE; + } + + rc = vcd_validate_io_done_pyld(p_payload, status); + VCD_FAILED_RETURN(rc, "\n Bad input done payload"); + + p_transc = (struct vcd_transc_type *)p_frame-> + vcd_frm.n_ip_frm_tag; + + if ((p_transc->p_ip_buf_entry->frame.p_virtual != + p_frame->vcd_frm.p_virtual) || + !p_transc->p_ip_buf_entry->b_in_use) { + VCD_MSG_ERROR("\n Bad frm transaction state"); + return VCD_ERR_BAD_STATE; + } + + rc = vcd_map_sched_status(sched_re_queue_frame( + p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, + (void *) p_transc->p_ip_buf_entry)); + + VCD_FAILED_RETURN(rc, "Failed: sched_queue_frame"); + + if (p_transc->p_ip_buf_entry->frame.n_flags & + VCD_FRAME_FLAG_EOS) { + rc = vcd_map_sched_status(sched_mark_client_eof( + p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl)); + } + + VCD_FAILED_RETURN(rc, "Failed: sched_mark_client_eof"); + + p_transc->p_ip_buf_entry = NULL; + p_transc->b_in_use = FALSE; + p_frame->b_frm_trans_end = TRUE; + + if (VCD_FAILED(status)) + VCD_MSG_ERROR("\n OUTPUT_REQ returned err = 0x%x", + status); + + if (p_cctxt->status.n_frame_submitted > 0) + p_cctxt->status.n_frame_submitted--; + else + p_cctxt->status.n_frame_delayed--; + + if (!VCD_FAILED(status) && + p_cctxt->b_decoding && + p_frame->vcd_frm.b_interlaced) { + if (p_cctxt->status.n_int_field_cnt > 0) + VCD_MSG_ERROR("\n Not expected: OUTPUT_REQ" + "for 2nd interlace field"); + } + + return VCD_S_SUCCESS; +} + + +u32 vcd_handle_output_required_in_flushing( +struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload) +{ + u32 rc; + struct vcd_transc_type *p_transc; + + rc = vcd_validate_io_done_pyld(p_payload, VCD_S_SUCCESS); + VCD_FAILED_RETURN(rc, "Bad input done payload"); + + p_transc = (struct vcd_transc_type *) + (((struct ddl_frame_data_type_tag *)p_payload)-> + vcd_frm.n_ip_frm_tag); + + ((struct ddl_frame_data_type_tag *)p_payload)-> + vcd_frm.b_interlaced = FALSE; + + rc = vcd_handle_input_done(p_cctxt, p_payload, + VCD_EVT_RESP_INPUT_FLUSHED, VCD_S_SUCCESS); + + p_transc->b_in_use = FALSE; + ((struct ddl_frame_data_type_tag *)p_payload)->b_frm_trans_end = TRUE; + + return rc; +} + +u32 vcd_handle_frame_done( + struct vcd_clnt_ctxt_type_t *p_cctxt, + void *p_payload, u32 event, u32 status) +{ + struct vcd_buffer_entry_type *p_op_buf_entry; + struct ddl_frame_data_type_tag *p_op_frm = + (struct ddl_frame_data_type_tag *) p_payload; + struct vcd_transc_type *p_transc; + u32 rc; + + rc = vcd_validate_io_done_pyld(p_payload, status); + VCD_FAILED_RETURN(rc, "Bad payload recvd"); + + p_transc = (struct vcd_transc_type *)p_op_frm->vcd_frm.n_ip_frm_tag; + + if (p_op_frm->vcd_frm.p_virtual) { + + if (!p_transc->p_op_buf_entry) { + p_op_buf_entry = + vcd_find_buffer_pool_entry( + &p_cctxt->out_buf_pool, + p_op_frm->vcd_frm. + p_virtual); + } else { + p_op_buf_entry = p_transc->p_op_buf_entry; + } + + if (!p_op_buf_entry) { + VCD_MSG_ERROR("Invalid output buffer returned" + "from DDL"); + vcd_assert(); + rc = VCD_ERR_BAD_POINTER; + } else if (!p_op_buf_entry->b_in_use) { + VCD_MSG_ERROR("Bad output buffer 0x%p recvd from DDL", + p_op_buf_entry->frame.p_virtual); + vcd_assert(); + rc = VCD_ERR_BAD_POINTER; + } else { + p_op_buf_entry->b_in_use = FALSE; + VCD_BUFFERPOOL_INUSE_DECREMENT( + p_cctxt->out_buf_pool.n_in_use); + VCD_MSG_LOW("outBufPool.InUse = %d", + p_cctxt->out_buf_pool.n_in_use); + } + } + VCD_FAILED_RETURN(rc, "Bad output buffer pointer"); + p_op_frm->vcd_frm.time_stamp = p_transc->time_stamp; + p_op_frm->vcd_frm.n_ip_frm_tag = p_transc->n_ip_frm_tag; + p_op_frm->vcd_frm.e_frame_type = p_transc->e_frame_type; + + p_transc->b_frame_done = TRUE; + + if (p_transc->b_input_done && p_transc->b_frame_done) + p_transc->b_in_use = FALSE; + + if (status == VCD_ERR_INTRLCD_FIELD_DROP || + (p_op_frm->n_intrlcd_ip_frm_tag != VCD_FRAMETAG_INVALID && + p_op_frm->n_intrlcd_ip_frm_tag)) { + vcd_handle_frame_done_for_interlacing(p_cctxt, p_transc, + p_op_frm, status); + } + + if (status != VCD_ERR_INTRLCD_FIELD_DROP) { + p_cctxt->callback(event, + status, + &p_op_frm->vcd_frm, + sizeof(struct vcd_frame_data_type), + p_cctxt, p_cctxt->p_client_data); + } + return VCD_S_SUCCESS; +} + +void vcd_handle_frame_done_in_eos( + struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload, u32 status) +{ + struct ddl_frame_data_type_tag *p_frame = + (struct ddl_frame_data_type_tag *) p_payload; + + VCD_MSG_LOW("vcd_handle_frame_done_in_eos:"); + + if (VCD_FAILED(vcd_validate_io_done_pyld(p_payload, status))) + return; + + if (p_cctxt->status.b_eos_prev_valid) { + (void)vcd_handle_frame_done(p_cctxt, + (void *)&p_cctxt->status. + eos_prev_op_frm, + VCD_EVT_RESP_OUTPUT_DONE, + status); + } + + p_cctxt->status.eos_prev_op_frm = *p_frame; + p_cctxt->status.b_eos_prev_valid = TRUE; +} + +void vcd_handle_frame_done_for_interlacing( + struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_transc_type *p_transc_ip1, + struct ddl_frame_data_type_tag *p_op_frm, u32 status) +{ + struct vcd_transc_type *p_transc_ip2 = + (struct vcd_transc_type *)p_op_frm->n_intrlcd_ip_frm_tag; + + if (status == VCD_ERR_INTRLCD_FIELD_DROP) { + p_cctxt->status.n_int_field_cnt = 0; + return; + } + + p_op_frm->n_intrlcd_ip_frm_tag = p_transc_ip2->n_ip_frm_tag; + + p_transc_ip2->b_frame_done = TRUE; + + if (p_transc_ip2->b_input_done && p_transc_ip2->b_frame_done) + p_transc_ip2->b_in_use = FALSE; + + if (!p_transc_ip1->e_frame_type || + !p_transc_ip2->e_frame_type) { + VCD_MSG_ERROR("DDL didn't provided frame type"); + + return; + } +} + +u32 vcd_handle_first_frame_done( + struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload) +{ + if (!p_cctxt->b_decoding) + return vcd_handle_first_encode_frame_done(p_cctxt, p_payload); + + return VCD_S_SUCCESS; +} + +u32 vcd_handle_first_encode_frame_done( + struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload) +{ + struct vcd_buffer_entry_type *p_buf_entry; + struct vcd_frame_data_type *p_frm_entry; + u32 rc, seqhdr_present; + struct vcd_property_hdr_type prop_hdr; + struct vcd_sequence_hdr_type seq_hdr; + struct vcd_property_codec_type codec; + union sched_value_type sched_val; + struct vcd_transc_type *p_transc; + struct ddl_frame_data_type_tag *p_payload_frm = + (struct ddl_frame_data_type_tag *) p_payload; + VCD_MSG_LOW("vcd_handle_first_encode_frame_done:"); + + rc = vcd_validate_io_done_pyld(p_payload, VCD_S_SUCCESS); + VCD_FAILED_RETURN(rc, "Validate frame done payload failed"); + + p_transc = (struct vcd_transc_type *)p_payload_frm-> + vcd_frm.n_ip_frm_tag; + + prop_hdr.prop_id = DDL_I_SEQHDR_PRESENT; + prop_hdr.n_size = sizeof(seqhdr_present); + rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, + &seqhdr_present); + VCD_FAILED_RETURN(rc, "Failed: DDL_I_SEQHDR_PRESENT"); + if (!seqhdr_present) + return VCD_S_SUCCESS; + + p_buf_entry = vcd_buffer_pool_entry_de_q(&p_cctxt->out_buf_pool); + + if (!p_buf_entry) { + VCD_MSG_ERROR("Sched provided frame when 2nd op buffer " + "was unavailable"); + + rc = VCD_ERR_FAIL; + vcd_assert(); + } else { + p_frm_entry = &p_buf_entry->frame; + prop_hdr.prop_id = VCD_I_CODEC; + prop_hdr.n_size = sizeof(struct vcd_property_codec_type); + + rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, &codec); + + if (!VCD_FAILED(rc)) { + if (codec.e_codec != VCD_CODEC_H263) { + prop_hdr.prop_id = VCD_I_SEQ_HEADER; + prop_hdr.n_size = sizeof(struct + vcd_sequence_hdr_type); + + seq_hdr.p_sequence_header = + p_frm_entry->p_virtual; + seq_hdr.n_sequence_header_len = + p_buf_entry->n_size; + + rc = ddl_get_property(p_cctxt->ddl_handle, + &prop_hdr, &seq_hdr); + } else { + VCD_MSG_LOW("Codec Type is H.263\n"); + } + + if (!VCD_FAILED(rc)) { + sched_val.un_value = + VCD_SCHEDULER_ENC_DFLT_OTKN_PERFRM; + + rc = vcd_map_sched_status(sched_set_client_param + (p_cctxt->p_dev_ctxt-> + sched_hdl, + p_cctxt->sched_clnt_hdl, + SCHED_I_CLNT_OTKNPERIPFRM, + &sched_val)); + + if (!VCD_FAILED(rc)) { + p_frm_entry->n_data_len = + seq_hdr.n_sequence_header_len; + p_frm_entry->time_stamp = + p_transc->time_stamp; + p_frm_entry->n_ip_frm_tag = + p_transc->n_ip_frm_tag; + p_frm_entry->n_flags |= + VCD_FRAME_FLAG_CODECCONFIG; + + p_cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, + VCD_S_SUCCESS, p_frm_entry, + sizeof(struct vcd_frame_data_type), + p_cctxt, + p_cctxt->p_client_data); + + } else { + VCD_MSG_ERROR("rc = 0x%x.Failed: " + "sched_set_client_param", rc); + } + + } else { + VCD_MSG_ERROR + ("rc = 0x%x. Failed: " + "ddl_get_property:VCD_I_SEQ_HEADER", + rc); + } + } else { + VCD_MSG_ERROR + ("rc = 0x%x. Failed: " + "ddl_get_property:VCD_I_CODEC", + rc); + } + + if (VCD_FAILED(rc)) { + (void)vcd_buffer_pool_entry_en_q(&p_cctxt->out_buf_pool, + p_buf_entry); + } + } + + return rc; +} + +void vcd_handle_eos_trans_end(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + if (p_cctxt->status.b_eos_prev_valid) { + (void) vcd_handle_frame_done(p_cctxt, + (void *)&p_cctxt->status.eos_prev_op_frm, + VCD_EVT_RESP_OUTPUT_DONE, + VCD_S_SUCCESS); + + p_cctxt->status.b_eos_prev_valid = FALSE; + } + + if (p_cctxt->status.n_flush_mode) + vcd_process_pending_flush_in_eos(p_cctxt); + + if (p_cctxt->status.b_stop_pending) + vcd_process_pending_stop_in_eos(p_cctxt); + else { + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_RUN, + CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); + } +} + +void vcd_handle_eos_done(struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_transc_type *p_transc, u32 status) +{ + struct vcd_frame_data_type vcd_frm; + VCD_MSG_LOW("vcd_handle_eos_done:"); + + if (VCD_FAILED(status)) + VCD_MSG_ERROR("EOS DONE returned error = 0x%x", status); + + if (p_cctxt->status.b_eos_prev_valid) { + p_cctxt->status.eos_prev_op_frm.vcd_frm.n_flags |= + VCD_FRAME_FLAG_EOS; + + (void)vcd_handle_frame_done(p_cctxt, + (void *)&p_cctxt->status. + eos_prev_op_frm, + VCD_EVT_RESP_OUTPUT_DONE, + VCD_S_SUCCESS); + + p_cctxt->status.b_eos_prev_valid = FALSE; + } else { + if (p_transc->p_ip_buf_entry) { + p_transc->p_ip_buf_entry->frame.n_ip_frm_tag = + p_transc->n_ip_frm_tag; + + vcd_send_frame_done_in_eos(p_cctxt, + &p_transc->p_ip_buf_entry->frame, FALSE); + } else { + memset(&vcd_frm, 0, sizeof(struct vcd_frame_data_type)); + vcd_frm.n_ip_frm_tag = p_transc->n_ip_frm_tag; + vcd_frm.time_stamp = p_transc->time_stamp; + vcd_frm.n_flags = VCD_FRAME_FLAG_EOS; + vcd_send_frame_done_in_eos(p_cctxt, &vcd_frm, TRUE); + } + } + if (p_transc->p_ip_buf_entry) { + if (p_transc->p_ip_buf_entry->frame.p_virtual) { + p_transc->p_ip_buf_entry->frame.n_ip_frm_tag = + p_transc->n_ip_frm_tag; + + p_cctxt->callback(VCD_EVT_RESP_INPUT_DONE, + VCD_S_SUCCESS, + &p_transc->p_ip_buf_entry->frame, + sizeof(struct vcd_frame_data_type), + p_cctxt, p_cctxt->p_client_data); + } + p_transc->p_ip_buf_entry->b_in_use = FALSE; + VCD_BUFFERPOOL_INUSE_DECREMENT(p_cctxt->in_buf_pool.n_in_use); + p_transc->p_ip_buf_entry = NULL; + p_cctxt->status.n_frame_submitted--; + } + + p_transc->b_in_use = FALSE; + vcd_mark_frame_channel(p_cctxt->p_dev_ctxt); + if (p_cctxt->status.n_flush_mode) + vcd_process_pending_flush_in_eos(p_cctxt); + + if (p_cctxt->status.b_stop_pending) { + vcd_process_pending_stop_in_eos(p_cctxt); + } else if (!p_cctxt->status.b_eos_wait_for_op_buf) { + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_RUN, + CLIENT_STATE_EVENT_NUMBER + (pf_clnt_cb)); + } +} + +void vcd_handle_start_done(struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_transc_type *p_transc, u32 status) +{ + p_cctxt->status.n_cmd_submitted--; + vcd_mark_command_channel(p_cctxt->p_dev_ctxt, p_transc); + + if (!VCD_FAILED(status)) { + p_cctxt->callback(VCD_EVT_RESP_START, status, NULL, + 0, p_cctxt, p_cctxt->p_client_data); + + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_RUN, + CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); + } else { + VCD_MSG_ERROR("ddl callback returned failure." + "status = 0x%x", status); + vcd_handle_err_in_starting(p_cctxt, status); + } +} + +void vcd_handle_stop_done(struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_transc_type *p_transc, u32 status) +{ + u32 rc = VCD_S_SUCCESS, seq_hdrpresent = 0; + union sched_value_type sched_val; + struct vcd_property_hdr_type prop_hdr; + VCD_MSG_LOW("vcd_handle_stop_done:"); + p_cctxt->status.n_cmd_submitted--; + vcd_mark_command_channel(p_cctxt->p_dev_ctxt, p_transc); + + if (!VCD_FAILED(status)) { + if (!p_cctxt->b_decoding) { + prop_hdr.prop_id = DDL_I_SEQHDR_PRESENT; + prop_hdr.n_size = sizeof(seq_hdrpresent); + rc = ddl_get_property(p_cctxt->ddl_handle, + &prop_hdr, &seq_hdrpresent); + + if (!VCD_FAILED(rc)) { + if (seq_hdrpresent == 0x1) { + sched_val.un_value = + VCD_SCHEDULER_ENC_DFLT_OTKN_PERFRM + + 1; + + rc = vcd_map_sched_status( + sched_set_client_param( + p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, + SCHED_I_CLNT_OTKNPERIPFRM, + &sched_val) + ); + + if (VCD_FAILED(rc)) + VCD_MSG_ERROR("Failed: " + "sched_set_client_param" + " %d", rc); + } + + } else { + VCD_MSG_ERROR("Failed: DDL Get DDL_I_SEQHDR_" + "PRESENT %d", rc); + } + + } + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_OPEN, + CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); + } else { + VCD_MSG_FATAL("STOP_DONE returned error = 0x%x", status); + status = VCD_ERR_HW_FATAL; + vcd_handle_device_err_fatal(p_cctxt->p_dev_ctxt, p_cctxt); + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_INVALID, + CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); + } + + p_cctxt->callback(VCD_EVT_RESP_STOP, status, NULL, 0, p_cctxt, + p_cctxt->p_client_data); + + memset(&p_cctxt->status, 0, sizeof(struct vcd_clnt_status_type)); +} + +void vcd_handle_stop_done_in_starting(struct vcd_clnt_ctxt_type_t + *p_cctxt, struct vcd_transc_type *p_transc, u32 status) +{ + VCD_MSG_LOW("vcd_handle_stop_done_in_starting:"); + p_cctxt->status.n_cmd_submitted--; + vcd_mark_command_channel(p_cctxt->p_dev_ctxt, p_transc); + if (!VCD_FAILED(status)) { + p_cctxt->callback(VCD_EVT_RESP_START, p_cctxt->status. + e_last_err, NULL, 0, p_cctxt, p_cctxt->p_client_data); + vcd_do_client_state_transition(p_cctxt, VCD_CLIENT_STATE_OPEN, + CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); + } else { + VCD_MSG_FATAL("VCD Cleanup: STOP_DONE returned error " + "= 0x%x", status); + vcd_handle_err_fatal(p_cctxt, VCD_EVT_RESP_START, + VCD_ERR_HW_FATAL); + } +} + +void vcd_handle_stop_done_in_invalid(struct vcd_clnt_ctxt_type_t + *p_cctxt, u32 status) +{ + u32 rc; + VCD_MSG_LOW("vcd_handle_stop_done_in_invalid:"); + if (!VCD_FAILED(status)) { + vcd_client_cmd_flush_and_en_q(p_cctxt, VCD_CMD_CLIENT_CLOSE); + if (p_cctxt->status.n_frame_submitted) { + vcd_release_multiple_frame_channels(p_cctxt->p_dev_ctxt, + p_cctxt->status.n_frame_submitted); + + p_cctxt->status.n_frame_submitted = 0; + p_cctxt->status.n_frame_delayed = 0; + } + if (p_cctxt->status.n_cmd_submitted) { + vcd_release_multiple_command_channels( + p_cctxt->p_dev_ctxt, + p_cctxt->status.n_cmd_submitted); + p_cctxt->status.n_cmd_submitted = 0; + } + } else { + VCD_MSG_FATAL("VCD Cleanup: STOP_DONE returned error " + "= 0x%x", status); + vcd_handle_device_err_fatal(p_cctxt->p_dev_ctxt, p_cctxt); + p_cctxt->status.b_cleaning_up = FALSE; + } + vcd_flush_buffers_in_err_fatal(p_cctxt); + VCD_MSG_HIGH("VCD cleanup: All buffers are returned"); + if (p_cctxt->status.b_stop_pending) { + p_cctxt->callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, NULL, 0, + p_cctxt, p_cctxt->p_client_data); + p_cctxt->status.b_stop_pending = FALSE; + } + rc = vcd_power_event(p_cctxt->p_dev_ctxt, p_cctxt, + VCD_EVT_PWR_CLNT_ERRFATAL); + if (VCD_FAILED(rc)) + VCD_MSG_ERROR("VCD_EVT_PWR_CLNT_ERRFATAL failed"); + if (!p_cctxt->status.b_cleaning_up && + p_cctxt->status.b_close_pending) { + vcd_destroy_client_context(p_cctxt); + vcd_handle_for_last_clnt_close(p_cctxt->p_dev_ctxt, FALSE); + } +} + +u32 vcd_handle_input_frame( + struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_input_frame) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + struct vcd_buffer_entry_type *p_buf_entry; + struct vcd_frame_data_type *p_frm_entry; + u32 rc = VCD_S_SUCCESS; + u32 b_eos_handled = FALSE; + + VCD_MSG_LOW("vcd_handle_input_frame:"); + + VCD_MSG_LOW("input buffer: addr=(0x%p), size=(%d), len=(%d)", + p_input_frame->p_virtual, p_input_frame->n_alloc_len, + p_input_frame->n_data_len); + + if ((!p_input_frame->p_virtual || !p_input_frame->n_data_len) + && !(p_input_frame->n_flags & VCD_FRAME_FLAG_EOS)) { + VCD_MSG_ERROR("Bad frame ptr/len/EOS combination"); + + return VCD_ERR_ILLEGAL_PARM; + } + + if (!p_cctxt->status.b1st_frame_recvd) { + if (p_cctxt->b_decoding) + rc = vcd_handle_first_decode_frame(p_cctxt); + + if (!VCD_FAILED(rc)) { + p_cctxt->status.first_ts = p_input_frame->time_stamp; + p_cctxt->status.prev_ts = p_cctxt->status.first_ts; + + p_cctxt->status.b1st_frame_recvd = TRUE; + + (void)vcd_power_event(p_cctxt->p_dev_ctxt, + p_cctxt, + VCD_EVT_PWR_CLNT_FIRST_FRAME); + } + } + VCD_FAILED_RETURN(rc, "Failed: Frist frame handling"); + + p_buf_entry = vcd_find_buffer_pool_entry(&p_cctxt->in_buf_pool, + p_input_frame->p_virtual); + if (!p_buf_entry) { + VCD_MSG_ERROR("Bad buffer addr: %p", p_input_frame->p_virtual); + return VCD_ERR_FAIL; + } + + if (p_buf_entry->b_in_use) { + VCD_MSG_ERROR("An inuse input frame is being" + "re-queued to scheduler"); + return VCD_ERR_FAIL; + } + + if (p_input_frame->n_alloc_len > p_buf_entry->n_size) { + VCD_MSG_ERROR("Bad buffer Alloc_len %d, Actual size=%d", + p_input_frame->n_alloc_len, p_buf_entry->n_size); + + return VCD_ERR_ILLEGAL_PARM; + } + + p_frm_entry = &p_buf_entry->frame; + + *p_frm_entry = *p_input_frame; + p_frm_entry->p_physical = p_buf_entry->p_physical; + + if (p_input_frame->n_flags & VCD_FRAME_FLAG_EOS) { + rc = vcd_handle_recvd_eos(p_cctxt, p_input_frame, + &b_eos_handled); + } + + if (VCD_FAILED(rc) || b_eos_handled) { + VCD_MSG_HIGH("rc = 0x%x, b_eos_handled = %d", rc, + b_eos_handled); + + return rc; + } + + rc = vcd_map_sched_status(sched_queue_frame(p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, + (void *)p_buf_entry)); + + VCD_FAILED_RETURN(rc, "Failed: sched_queue_frame"); + + p_buf_entry->b_in_use = TRUE; + p_cctxt->in_buf_pool.n_in_use++; + if (p_input_frame->n_flags & VCD_FRAME_FLAG_EOS) { + rc = vcd_map_sched_status(sched_mark_client_eof + (p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl)); + } + + VCD_FAILED_RETURN(rc, "Failed: sched_mark_client_eof"); + + vcd_try_submit_frame(p_dev_ctxt); + return rc; +} + +void vcd_release_all_clnt_frm_transc(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + u8 i; + + VCD_MSG_LOW("vcd_release_all_clnt_frm_transc:"); + + for (i = 0; i < p_dev_ctxt->n_trans_tbl_size; i++) { + if (p_dev_ctxt->a_trans_tbl[i].b_in_use && + p_cctxt == p_dev_ctxt->a_trans_tbl[i].p_cctxt && + p_dev_ctxt->a_trans_tbl[i]. + e_type == VCD_CMD_CODE_FRAME) { + vcd_release_trans_tbl_entry(&p_dev_ctxt-> + a_trans_tbl[i]); + } + } +} + +void vcd_release_all_clnt_def_frm_transc(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + u8 i; + + VCD_MSG_LOW("vcd_release_all_clnt_def_frm_transc:"); + + for (i = 0; i < p_dev_ctxt->n_trans_tbl_size; i++) { + if (p_dev_ctxt->a_trans_tbl[i].b_in_use && + p_cctxt == p_dev_ctxt->a_trans_tbl[i].p_cctxt + && p_dev_ctxt->a_trans_tbl[i].e_type == + VCD_CMD_NONE) { + vcd_release_trans_tbl_entry( + &p_dev_ctxt->a_trans_tbl[i]); + } + } +} + +void vcd_release_all_clnt_transc(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + u8 i; + + VCD_MSG_LOW("vcd_release_all_clnt_def_frm_transc:"); + + for (i = 0; i < p_dev_ctxt->n_trans_tbl_size; i++) { + if (p_dev_ctxt->a_trans_tbl[i].b_in_use && + p_cctxt == p_dev_ctxt->a_trans_tbl[i].p_cctxt) { + vcd_release_trans_tbl_entry( + &p_dev_ctxt->a_trans_tbl[i]); + } + } +} + +void vcd_send_flush_done(struct vcd_clnt_ctxt_type_t *p_cctxt, u32 status) +{ + VCD_MSG_LOW("vcd_send_flush_done:"); + + if (p_cctxt->status.n_flush_mode & VCD_FLUSH_INPUT) { + p_cctxt->callback(VCD_EVT_RESP_FLUSH_INPUT_DONE, + status, NULL, 0, p_cctxt, p_cctxt->p_client_data); + p_cctxt->status.n_flush_mode &= ~VCD_FLUSH_INPUT; + } + + if (p_cctxt->status.n_flush_mode & VCD_FLUSH_OUTPUT) { + p_cctxt->callback(VCD_EVT_RESP_FLUSH_OUTPUT_DONE, + status, NULL, 0, p_cctxt, p_cctxt->p_client_data); + p_cctxt->status.n_flush_mode &= ~VCD_FLUSH_OUTPUT; + } +} + +u32 vcd_store_seq_hdr( + struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_sequence_hdr_type *p_seq_hdr) +{ + u32 rc; + struct vcd_property_hdr_type prop_hdr; + u32 n_align; + u8 *p_virtual_aligned; + u32 n_addr; + int ret = 0; + + if (!p_seq_hdr->n_sequence_header_len + || !p_seq_hdr->p_sequence_header) { + VCD_MSG_ERROR("Bad seq hdr"); + + return VCD_ERR_BAD_POINTER; + } + + if (p_cctxt->seq_hdr.p_sequence_header) { + VCD_MSG_HIGH("Old seq hdr detected"); + + vcd_pmem_free(p_cctxt->seq_hdr.p_sequence_header, + p_cctxt->p_seq_hdr_phy_addr); + p_cctxt->seq_hdr.p_sequence_header = NULL; + } + + p_cctxt->seq_hdr.n_sequence_header_len = + p_seq_hdr->n_sequence_header_len; + + prop_hdr.prop_id = DDL_I_SEQHDR_ALIGN_BYTES; + prop_hdr.n_size = sizeof(u32); + + rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, &n_align); + + VCD_FAILED_RETURN(rc, + "Failed: ddl_get_property DDL_I_SEQHDR_ALIGN_BYTES"); + + VCD_MSG_MED("Seq hdr alignment bytes = %d", n_align); + + ret = vcd_pmem_alloc(p_cctxt->seq_hdr.n_sequence_header_len + n_align + + VCD_SEQ_HDR_PADDING_BYTES, + &(p_cctxt->seq_hdr.p_sequence_header), + &(p_cctxt->p_seq_hdr_phy_addr)); + + if (ret < 0) { + VCD_MSG_ERROR("Seq hdr allocation failed"); + + return VCD_ERR_ALLOC_FAIL; + } + + if (!p_cctxt->p_seq_hdr_phy_addr) { + VCD_MSG_ERROR("Couldn't get physical address"); + + return VCD_ERR_BAD_POINTER; + } + + if (n_align > 0) { + n_addr = (u32) p_cctxt->p_seq_hdr_phy_addr; + n_addr += n_align; + n_addr -= (n_addr % n_align); + p_virtual_aligned = p_cctxt->seq_hdr.p_sequence_header; + p_virtual_aligned += (u32) (n_addr - + (u32) p_cctxt->p_seq_hdr_phy_addr); + p_cctxt->p_seq_hdr_phy_addr = (u8 *) n_addr; + } else { + p_virtual_aligned = p_cctxt->seq_hdr.p_sequence_header; + } + + memcpy(p_virtual_aligned, p_seq_hdr->p_sequence_header, + p_seq_hdr->n_sequence_header_len); + + return VCD_S_SUCCESS; +} + +u32 vcd_set_frame_rate( + struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_property_frame_rate_type *p_fps) +{ + union sched_value_type sched_val; + u32 rc; + + sched_val.frm_rate.n_numer = p_fps->n_fps_numerator; + sched_val.frm_rate.n_denom = p_fps->n_fps_denominator; + p_cctxt->frm_rate = *p_fps; + + rc = vcd_map_sched_status(sched_set_client_param + (p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, + SCHED_I_CLNT_FRAMERATE, &sched_val)); + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("rc = 0x%x. Failed: Set SCHED_I_CLNT_FRAMERATE", + rc); + } + + rc = vcd_update_clnt_perf_lvl(p_cctxt, &p_cctxt->frm_rate, + p_cctxt->n_frm_p_units); + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_update_clnt_perf_lvl", + rc); + } + + sched_val.un_value = p_cctxt->n_reqd_perf_lvl; + + rc = vcd_map_sched_status(sched_set_client_param + (p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, + SCHED_I_CLNT_PTKNRATE, &sched_val)); + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("rc = 0x%x. Failed: Set SCHED_I_CLNT_PTKNRATE", + rc); + } + + return VCD_S_SUCCESS; +} + +u32 vcd_set_frame_size( + struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_property_frame_size_type *p_frm_size) +{ + struct vcd_property_hdr_type prop_hdr; + union sched_value_type sched_val; + u32 rc; + u32 n_frm_p_units; + p_frm_size = NULL; + + prop_hdr.prop_id = DDL_I_FRAME_PROC_UNITS; + prop_hdr.n_size = sizeof(n_frm_p_units); + rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, &n_frm_p_units); + + VCD_FAILED_RETURN(rc, "Failed: Get DDL_I_FRAME_PROC_UNITS"); + + p_cctxt->n_frm_p_units = sched_val.un_value = n_frm_p_units; + + rc = vcd_map_sched_status(sched_set_client_param + (p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, + SCHED_I_CLNT_PTKNPERFRM, &sched_val)); + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("rc = 0x%x. Failed: Set SCHED_I_CLNT_PTKNPERFRM", + rc); + } + + rc = vcd_update_clnt_perf_lvl(p_cctxt, &p_cctxt->frm_rate, + n_frm_p_units); + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_update_clnt_perf_lvl", + rc); + } + + sched_val.un_value = p_cctxt->n_reqd_perf_lvl; + + rc = vcd_map_sched_status(sched_set_client_param + (p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, + SCHED_I_CLNT_PTKNRATE, &sched_val)); + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("rc = 0x%x. Failed: Set SCHED_I_CLNT_PTKNRATE", + rc); + } + + return VCD_S_SUCCESS; +} + +void vcd_process_pending_flush_in_eos(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + u32 rc = VCD_S_SUCCESS; + + VCD_MSG_HIGH("Buffer flush is pending"); + + rc = vcd_flush_buffers(p_cctxt, p_cctxt->status.n_flush_mode); + + if (VCD_FAILED(rc)) + VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_flush_buffers", rc); + + p_cctxt->status.b_eos_wait_for_op_buf = FALSE; + + vcd_send_flush_done(p_cctxt, VCD_S_SUCCESS); +} + +void vcd_process_pending_stop_in_eos(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + u32 rc = VCD_S_SUCCESS; + + rc = vcd_flush_buffers(p_cctxt, VCD_FLUSH_ALL); + + if (VCD_FAILED(rc)) + VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_flush_buffers", rc); + + VCD_MSG_HIGH("All buffers are returned. Enqueuing stop cmd"); + + vcd_client_cmd_flush_and_en_q(p_cctxt, VCD_CMD_CODEC_STOP); + p_cctxt->status.b_stop_pending = FALSE; + + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_STOPPING, + CLIENT_STATE_EVENT_NUMBER(pf_stop)); +} + +u32 vcd_calculate_frame_delta( + struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_frame) +{ + u32 n_frm_delta; + u64 n_temp, temp1; + + n_temp = p_frame->time_stamp - p_cctxt->status.prev_ts; + + VCD_MSG_LOW("Curr_ts=%lld Prev_ts=%lld Diff=%llu", + p_frame->time_stamp, p_cctxt->status.prev_ts, n_temp); + + n_temp = n_temp * p_cctxt->n_time_resoln; + n_temp = (n_temp + (VCD_TIMESTAMP_RESOLUTION >> 1)); + temp1 = do_div(n_temp, VCD_TIMESTAMP_RESOLUTION); + n_frm_delta = n_temp; + VCD_MSG_LOW("temp1=%lld n_temp=%lld", temp1, n_temp); + p_cctxt->status.n_time_elapsed += n_frm_delta; + + n_temp = ((u64)p_cctxt->status.n_time_elapsed \ + * VCD_TIMESTAMP_RESOLUTION); + n_temp = (n_temp + (p_cctxt->n_time_resoln >> 1)); + temp1 = do_div(n_temp, p_cctxt->n_time_resoln); + + p_cctxt->status.prev_ts = p_cctxt->status.first_ts + n_temp; + + VCD_MSG_LOW("Time_elapsed=%u, Drift=%llu, new Prev_ts=%lld", + p_cctxt->status.n_time_elapsed, temp1, + p_cctxt->status.prev_ts); + + return n_frm_delta; +} + +struct vcd_buffer_entry_type *vcd_check_fill_output_buffer + (struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_frame_data_type *p_buffer) { + struct vcd_buffer_pool_type *p_buf_pool = &p_cctxt->out_buf_pool; + struct vcd_buffer_entry_type *p_buf_entry; + + if (!p_buf_pool->a_entries) { + VCD_MSG_ERROR("Buffers not set or allocated yet"); + + return NULL; + } + + if (!p_buffer->p_virtual) { + VCD_MSG_ERROR("NULL buffer address provided"); + return NULL; + } + + p_buf_entry = + vcd_find_buffer_pool_entry(p_buf_pool, p_buffer->p_virtual); + if (!p_buf_entry) { + VCD_MSG_ERROR("Unrecognized buffer address provided = %p", + p_buffer->p_virtual); + return NULL; + } + + if (p_buf_entry->b_in_use) { + VCD_MSG_ERROR + ("An inuse output frame is being provided for reuse"); + return NULL; + } + + if (p_buffer->n_alloc_len < p_buf_pool->buf_req.n_size || + p_buffer->n_alloc_len > p_buf_entry->n_size) { + VCD_MSG_ERROR + ("Bad buffer Alloc_len = %d, Actual size = %d, " + " Min size = %u", + p_buffer->n_alloc_len, p_buf_entry->n_size, + p_buf_pool->buf_req.n_size); + return NULL; + } + + return p_buf_entry; +} + +void vcd_handle_ind_hw_err_fatal(struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 event, u32 status) +{ + if (p_cctxt->status.n_frame_submitted) { + p_cctxt->status.n_frame_submitted--; + vcd_mark_frame_channel(p_cctxt->p_dev_ctxt); + } + vcd_handle_err_fatal(p_cctxt, event, status); +} + +void vcd_handle_err_fatal(struct vcd_clnt_ctxt_type_t *p_cctxt, u32 event, + u32 status) +{ + u32 rc; + VCD_MSG_LOW("vcd_handle_err_fatal: event=%x, err=%x", event, status); + if (!VCD_FAILED_FATAL(status)) + return; + + if (VCD_FAILED_DEVICE_FATAL(status)) { + vcd_clnt_handle_device_err_fatal(p_cctxt, event); + vcd_handle_device_err_fatal(p_cctxt->p_dev_ctxt, p_cctxt); + } else if (VCD_FAILED_CLIENT_FATAL(status)) { + p_cctxt->status.e_last_evt = event; + + if (p_cctxt->b_sched_clnt_valid) { + rc = vcd_map_sched_status(sched_suspend_resume_client( + p_cctxt->p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, FALSE)); + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("Failed: sched_suspend_resume_" + "client rc=0x%x", rc); + } + } + p_cctxt->callback(event, VCD_ERR_HW_FATAL, NULL, 0, p_cctxt, + p_cctxt->p_client_data); + p_cctxt->status.b_cleaning_up = TRUE; + vcd_client_cmd_flush_and_en_q(p_cctxt, VCD_CMD_CODEC_STOP); + vcd_do_client_state_transition(p_cctxt, + VCD_CLIENT_STATE_INVALID, + CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); + } +} + +void vcd_handle_err_in_starting(struct vcd_clnt_ctxt_type_t *p_cctxt, + u32 status) +{ + VCD_MSG_LOW("\n vcd_handle_err_in_starting:"); + if (VCD_FAILED_FATAL(status)) { + vcd_handle_err_fatal(p_cctxt, VCD_EVT_RESP_START, status); + } else { + p_cctxt->status.e_last_err = status; + VCD_MSG_HIGH("\n VCD cleanup: Enqueuing stop cmd"); + vcd_client_cmd_flush_and_en_q(p_cctxt, VCD_CMD_CODEC_STOP); + } +} + +void vcd_handle_trans_pending(struct vcd_clnt_ctxt_type_t *p_cctxt) +{ + if (!p_cctxt->status.n_frame_submitted) { + VCD_MSG_ERROR("Transaction pending response was not expected"); + vcd_assert(); + return; + } + p_cctxt->status.n_frame_submitted--; + p_cctxt->status.n_frame_delayed++; + vcd_mark_frame_channel(p_cctxt->p_dev_ctxt); +} + +u32 vcd_requeue_input_frame(struct vcd_dev_ctxt_type *p_dev_ctxt, + struct vcd_clnt_ctxt_type_t *p_cctxt, + struct vcd_buffer_entry_type *p_buf_entry) +{ + u32 rc; + rc = vcd_map_sched_status(sched_re_queue_frame(p_dev_ctxt->sched_hdl, + p_cctxt->sched_clnt_hdl, (void *) p_buf_entry)); + + VCD_FAILED_RETURN(rc, "Failed: Sched_ReQueueFrame"); + + if (p_buf_entry->frame.n_flags & VCD_FRAME_FLAG_EOS) { + rc = vcd_map_sched_status(sched_mark_client_eof(p_dev_ctxt-> + sched_hdl, p_cctxt->sched_clnt_hdl)); + } + + if (VCD_FAILED(rc)) + VCD_MSG_ERROR("rc = 0x%x: Failed: Sched_MarkClientEOF", rc); + + return rc; +} + +void vcd_handle_submit_frame_failed(struct vcd_dev_ctxt_type + *p_dev_ctxt, struct vcd_transc_type *p_transc) +{ + struct vcd_clnt_ctxt_type_t *p_cctxt = p_transc->p_cctxt; + u32 rc; + + vcd_mark_frame_channel(p_dev_ctxt); + p_transc->b_in_use = FALSE; + + vcd_handle_err_fatal(p_cctxt, VCD_EVT_IND_HWERRFATAL, + VCD_ERR_CLIENT_FATAL); + + if (vcd_get_command_channel(p_dev_ctxt, &p_transc)) { + p_transc->e_type = VCD_CMD_CODEC_STOP; + p_transc->p_cctxt = p_cctxt; + rc = vcd_submit_cmd_sess_end(p_transc); + if (VCD_FAILED(rc)) { + vcd_release_command_channel(p_dev_ctxt, p_transc); + VCD_MSG_ERROR("rc = 0x%x. Failed: VCD_SubmitCmdSessEnd", + rc); + } + } +} diff --git a/drivers/misc/video_core/720p/vcd/vcd_util.c b/drivers/misc/video_core/720p/vcd/vcd_util.c new file mode 100644 index 0000000000000..9a0a52c3c67b3 --- /dev/null +++ b/drivers/misc/video_core/720p/vcd/vcd_util.c @@ -0,0 +1,111 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "video_core_type.h" +#include "vcd_util.h" + +u32 vcd_critical_section_create(u32 **p_cs) +{ + struct mutex *lock; + if (!p_cs) { + VCD_MSG_ERROR("Bad critical section ptr"); + return VCD_ERR_BAD_POINTER; + } else { + lock = kmalloc(sizeof(struct mutex), GFP_KERNEL); + if (!lock) { + VCD_MSG_ERROR("Failed: vcd_critical_section_create"); + return VCD_ERR_ALLOC_FAIL; + } + mutex_init(lock); + *p_cs = (u32 *) lock; + return VCD_S_SUCCESS; + } +} + +u32 vcd_critical_section_release(u32 *cs) +{ + struct mutex *lock = (struct mutex *)cs; + if (!lock) { + VCD_MSG_ERROR("Bad critical section object"); + return VCD_ERR_BAD_POINTER; + } + + mutex_destroy(lock); + kfree(cs); + return VCD_S_SUCCESS; +} + +u32 vcd_critical_section_enter(u32 *cs) +{ + struct mutex *lock = (struct mutex *)cs; + if (!lock) { + VCD_MSG_ERROR("Bad critical section object"); + return VCD_ERR_BAD_POINTER; + } else + mutex_lock(lock); + + return VCD_S_SUCCESS; +} + +u32 vcd_critical_section_leave(u32 *cs) +{ + struct mutex *lock = (struct mutex *)cs; + + if (!lock) { + VCD_MSG_ERROR("Bad critical section object"); + + return VCD_ERR_BAD_POINTER; + } else + mutex_unlock(lock); + + return VCD_S_SUCCESS; +} + +int vcd_pmem_alloc(u32 size, u8 **kernel_vaddr, u8 **phy_addr) +{ + *phy_addr = + (u8 *) pmem_kalloc(size, PMEM_MEMTYPE_EBI1 | PMEM_ALIGNMENT_4K); + + if (!IS_ERR((void *)*phy_addr)) { + + *kernel_vaddr = ioremap((unsigned long)*phy_addr, size); + + if (!*kernel_vaddr) { + pr_err("%s: could not ioremap in kernel pmem buffers\n", + __func__); + pmem_kfree((s32) *phy_addr); + return -ENOMEM; + } + pr_debug("write buf: phy addr 0x%08x kernel addr 0x%08x\n", + (u32) *phy_addr, (u32) *kernel_vaddr); + return 0; + } else { + pr_err("%s: could not allocte in kernel pmem buffers\n", + __func__); + return -ENOMEM; + } + +} + +int vcd_pmem_free(u8 *kernel_vaddr, u8 *phy_addr) +{ + iounmap((void *)kernel_vaddr); + pmem_kfree((s32) phy_addr); + + return 0; +} diff --git a/drivers/misc/video_core/720p/vcd/vcd_util.h b/drivers/misc/video_core/720p/vcd/vcd_util.h new file mode 100644 index 0000000000000..760829579db83 --- /dev/null +++ b/drivers/misc/video_core/720p/vcd/vcd_util.h @@ -0,0 +1,87 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _VCD_UTIL_H_ +#define _VCD_UTIL_H_ + +#include "vcd_api.h" + +#if DEBUG + +#define VCD_MSG_LOW(xx_fmt...) printk(KERN_INFO "\n\t* " xx_fmt) +#define VCD_MSG_MED(xx_fmt...) printk(KERN_INFO "\n * " xx_fmt) +#define VCD_MSG_HIGH(xx_fmt...) printk(KERN_WARNING "\n" xx_fmt) + +#else + +#define VCD_MSG_LOW(xx_fmt...) +#define VCD_MSG_MED(xx_fmt...) +#define VCD_MSG_HIGH(xx_fmt...) + +#endif + +#define VCD_MSG_ERROR(xx_fmt...) printk(KERN_ERR "\n err: " xx_fmt) +#define VCD_MSG_FATAL(xx_fmt...) printk(KERN_ERR "\n " xx_fmt) + +#define VCD_FAILED_RETURN(rc, xx_fmt...) \ + do { \ + if (VCD_FAILED(rc)) { \ + printk(KERN_ERR xx_fmt); \ + return rc; \ + } \ + } while (0) + +#define VCD_FAILED_DEVICE_FATAL(rc) \ + (rc == VCD_ERR_HW_FATAL ? TRUE : FALSE) +#define VCD_FAILED_CLIENT_FATAL(rc) \ + (rc == VCD_ERR_CLIENT_FATAL ? TRUE : FALSE) + +#define VCD_FAILED_FATAL(rc) \ + ((VCD_FAILED_DEVICE_FATAL(rc) || VCD_FAILED_CLIENT_FATAL(rc)) \ + ? TRUE : FALSE) + + +#define vcd_assert() VCD_MSG_FATAL("ASSERT") +#define vcd_malloc(n_bytes) kmalloc(n_bytes, GFP_KERNEL) +#define vcd_free(p_mem) kfree(p_mem) + +#ifdef NO_IN_KERNEL_PMEM + #define VCD_PMEM_malloc(n_bytes) kmalloc(n_bytes, GFP_KERNEL) + #define VCD_PMEM_free(p_mem) kfree(p_mem) + #define VCD_PMEM_get_physical(p_mem) virt_to_phys(p_mem) +#else + int vcd_pmem_alloc(u32 size, u8 **kernel_vaddr, u8 **phy_addr); + int vcd_pmem_free(u8 *kernel_vaddr, u8 *phy_addr); +#endif + +u32 vcd_critical_section_create(u32 **p_cs); +u32 vcd_critical_section_release(u32 *cs); +u32 vcd_critical_section_enter(u32 *cs); +u32 vcd_critical_section_leave(u32 *cs); + +#endif diff --git a/drivers/misc/video_core/720p/vcd/video_core_type.h b/drivers/misc/video_core/720p/vcd/video_core_type.h new file mode 100644 index 0000000000000..6324e0ad64404 --- /dev/null +++ b/drivers/misc/video_core/720p/vcd/video_core_type.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef VIDEO_CORE_TYPE_H +#define VIDEO_CORE_TYPE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TRUE 1 +#define FALSE 0 + +#define DEBUG 0 + +#define USE_RES_TRACKER +#define AXI_CLK_SCALING + +#undef CORE_TIMING_INFO + +#endif diff --git a/include/linux/msm_vidc_dec.h b/include/linux/msm_vidc_dec.h new file mode 100644 index 0000000000000..284660385e0b0 --- /dev/null +++ b/include/linux/msm_vidc_dec.h @@ -0,0 +1,526 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _MSM_VIDC_DEC_H_ +#define _MSM_VIDC_DEC_H_ + +#include +#include + +/* STATUS CODES */ +/* Base value for status codes */ +#define VDEC_S_BASE 0x40000000 +/* Success */ +#define VDEC_S_SUCCESS (VDEC_S_BASE) +/* General failure */ +#define VDEC_S_EFAIL (VDEC_S_BASE + 1) +/* Fatal irrecoverable failure. Need to tear down session. */ +#define VDEC_S_EFATAL (VDEC_S_BASE + 2) +/* Error detected in the passed parameters */ +#define VDEC_S_EBADPARAM (VDEC_S_BASE + 3) +/* Command called in invalid state. */ +#define VDEC_S_EINVALSTATE (VDEC_S_BASE + 4) + /* Insufficient OS resources - thread, memory etc. */ +#define VDEC_S_ENOSWRES (VDEC_S_BASE + 5) + /* Insufficient HW resources - core capacity maxed out. */ +#define VDEC_S_ENOHWRES (VDEC_S_BASE + 6) +/* Invalid command called */ +#define VDEC_S_EINVALCMD (VDEC_S_BASE + 7) +/* Command timeout. */ +#define VDEC_S_ETIMEOUT (VDEC_S_BASE + 8) +/* Pre-requirement is not met for API. */ +#define VDEC_S_ENOPREREQ (VDEC_S_BASE + 9) +/* Command queue is full. */ +#define VDEC_S_ECMDQFULL (VDEC_S_BASE + 10) +/* Command is not supported by this driver */ +#define VDEC_S_ENOTSUPP (VDEC_S_BASE + 11) +/* Command is not implemented by thedriver. */ +#define VDEC_S_ENOTIMPL (VDEC_S_BASE + 12) +/* Command is not implemented by the driver. */ +#define VDEC_S_BUSY (VDEC_S_BASE + 13) + +#define VDEC_INTF_VER 1 +#define VDEC_MSG_BASE 0x0000000 +/* Codes to identify asynchronous message responses and events that driver + wants to communicate to the app.*/ +#define VDEC_MSG_INVALID (VDEC_MSG_BASE + 0) +#define VDEC_MSG_RESP_INPUT_BUFFER_DONE (VDEC_MSG_BASE + 1) +#define VDEC_MSG_RESP_OUTPUT_BUFFER_DONE (VDEC_MSG_BASE + 2) +#define VDEC_MSG_RESP_INPUT_FLUSHED (VDEC_MSG_BASE + 3) +#define VDEC_MSG_RESP_OUTPUT_FLUSHED (VDEC_MSG_BASE + 4) +#define VDEC_MSG_RESP_FLUSH_INPUT_DONE (VDEC_MSG_BASE + 5) +#define VDEC_MSG_RESP_FLUSH_OUTPUT_DONE (VDEC_MSG_BASE + 6) +#define VDEC_MSG_RESP_START_DONE (VDEC_MSG_BASE + 7) +#define VDEC_MSG_RESP_STOP_DONE (VDEC_MSG_BASE + 8) +#define VDEC_MSG_RESP_PAUSE_DONE (VDEC_MSG_BASE + 9) +#define VDEC_MSG_RESP_RESUME_DONE (VDEC_MSG_BASE + 10) +#define VDEC_MSG_RESP_RESOURCE_LOADED (VDEC_MSG_BASE + 11) +#define VDEC_EVT_RESOURCES_LOST (VDEC_MSG_BASE + 12) +#define VDEC_MSG_EVT_CONFIG_CHANGED (VDEC_MSG_BASE + 13) +#define VDEC_MSG_EVT_HW_ERROR (VDEC_MSG_BASE + 14) + +/*Buffer flags bits masks.*/ +#define VDEC_BUFFERFLAG_EOS 0x00000001 +#define VDEC_BUFFERFLAG_DECODEONLY 0x00000004 +#define VDEC_BUFFERFLAG_DATACORRUPT 0x00000008 +#define VDEC_BUFFERFLAG_ENDOFFRAME 0x00000010 +#define VDEC_BUFFERFLAG_SYNCFRAME 0x00000020 +#define VDEC_BUFFERFLAG_EXTRADATA 0x00000040 +#define VDEC_BUFFERFLAG_CODECCONFIG 0x00000080 + +/*Post processing flags bit masks*/ +#define VDEC_EXTRADATA_QP 0x00000001 +#define VDEC_EXTRADATA_SEI 0x00000002 +#define VDEC_EXTRADATA_VUI 0x00000004 +#define VDEC_EXTRADATA_MB_ERROR_MAP 0x00000008 + +#define VDEC_CMDBASE 0x800 +#define VDEC_CMD_SET_INTF_VERSION (VDEC_CMDBASE) + +#define VDEC_IOCTL_MAGIC 'v' + +struct vdec_ioctl_msg { + void *inputparam; + void *outputparam; +}; + +/* CMD params: InputParam:enum vdec_codec + OutputParam: struct vdec_profile_level*/ +#define VDEC_IOCTL_GET_PROFILE_LEVEL_SUPPORTED \ + _IOWR(VDEC_IOCTL_MAGIC, 0, struct vdec_ioctl_msg) + +/*CMD params:InputParam: NULL + OutputParam: uint32_t(bitmask)*/ +#define VDEC_IOCTL_GET_INTERLACE_FORMAT \ + _IOR(VDEC_IOCTL_MAGIC, 1, struct vdec_ioctl_msg) + +/* CMD params: InputParam: enum vdec_codec + OutputParam: struct vdec_profile_level*/ +#define VDEC_IOCTL_GET_CURRENT_PROFILE_LEVEL \ + _IOWR(VDEC_IOCTL_MAGIC, 2, struct vdec_ioctl_msg) + +/*CMD params: SET: InputParam: enum vdec_output_fromat OutputParam: NULL + GET: InputParam: NULL OutputParam: enum vdec_output_fromat*/ +#define VDEC_IOCTL_SET_OUTPUT_FORMAT \ + _IOWR(VDEC_IOCTL_MAGIC, 3, struct vdec_ioctl_msg) +#define VDEC_IOCTL_GET_OUTPUT_FORMAT \ + _IOWR(VDEC_IOCTL_MAGIC, 4, struct vdec_ioctl_msg) + +/*CMD params: SET: InputParam: enum vdec_codec OutputParam: NULL + GET: InputParam: NULL OutputParam: enum vdec_codec*/ +#define VDEC_IOCTL_SET_CODEC \ + _IOW(VDEC_IOCTL_MAGIC, 5, struct vdec_ioctl_msg) +#define VDEC_IOCTL_GET_CODEC \ + _IOR(VDEC_IOCTL_MAGIC, 6, struct vdec_ioctl_msg) + +/*CMD params: SET: InputParam: struct vdec_picsize outputparam: NULL + GET: InputParam: NULL outputparam: struct vdec_picsize*/ +#define VDEC_IOCTL_SET_PICRES \ + _IOW(VDEC_IOCTL_MAGIC, 7, struct vdec_ioctl_msg) +#define VDEC_IOCTL_GET_PICRES \ + _IOR(VDEC_IOCTL_MAGIC, 8, struct vdec_ioctl_msg) + +#define VDEC_IOCTL_SET_EXTRADATA \ + _IOW(VDEC_IOCTL_MAGIC, 9, struct vdec_ioctl_msg) +#define VDEC_IOCTL_GET_EXTRADATA \ + _IOR(VDEC_IOCTL_MAGIC, 10, struct vdec_ioctl_msg) + +#define VDEC_IOCTL_SET_SEQUENCE_HEADER \ + _IOW(VDEC_IOCTL_MAGIC, 11, struct vdec_ioctl_msg) + +/* CMD params: SET: InputParam - vdec_allocatorproperty, OutputParam - NULL + GET: InputParam - NULL, OutputParam - vdec_allocatorproperty*/ +#define VDEC_IOCTL_SET_BUFFER_REQ \ + _IOW(VDEC_IOCTL_MAGIC, 12, struct vdec_ioctl_msg) +#define VDEC_IOCTL_GET_BUFFER_REQ \ + _IOR(VDEC_IOCTL_MAGIC, 13, struct vdec_ioctl_msg) +/* CMD params: InputParam - vdec_buffer, OutputParam - uint8_t** */ +#define VDEC_IOCTL_ALLOCATE_BUFFER \ + _IOWR(VDEC_IOCTL_MAGIC, 14, struct vdec_ioctl_msg) +/* CMD params: InputParam - uint8_t *, OutputParam - NULL.*/ +#define VDEC_IOCTL_FREE_BUFFER \ + _IOW(VDEC_IOCTL_MAGIC, 15, struct vdec_ioctl_msg) + +/*CMD params: CMD: InputParam - struct vdec_setbuffer_cmd, OutputParam - NULL*/ +#define VDEC_IOCTL_SET_BUFFER \ + _IOW(VDEC_IOCTL_MAGIC, 16, struct vdec_ioctl_msg) + +/* CMD params: InputParam - struct vdec_fillbuffer_cmd, OutputParam - NULL*/ +#define VDEC_IOCTL_FILL_OUTPUT_BUFFER \ + _IOW(VDEC_IOCTL_MAGIC, 17, struct vdec_ioctl_msg) + +/*CMD params: InputParam - struct vdec_frameinfo , OutputParam - NULL*/ +#define VDEC_IOCTL_DECODE_FRAME \ + _IOW(VDEC_IOCTL_MAGIC, 18, struct vdec_ioctl_msg) + +#define VDEC_IOCTL_LOAD_RESOURCES _IO(VDEC_IOCTL_MAGIC, 19) +#define VDEC_IOCTL_CMD_START _IO(VDEC_IOCTL_MAGIC, 20) +#define VDEC_IOCTL_CMD_STOP _IO(VDEC_IOCTL_MAGIC, 21) +#define VDEC_IOCTL_CMD_PAUSE _IO(VDEC_IOCTL_MAGIC, 22) +#define VDEC_IOCTL_CMD_RESUME _IO(VDEC_IOCTL_MAGIC, 23) + +/*CMD params: InputParam - enum vdec_bufferflush , OutputParam - NULL */ +#define VDEC_IOCTL_CMD_FLUSH _IOW(VDEC_IOCTL_MAGIC, 24, struct vdec_ioctl_msg) + +/* ======================================================== + * IOCTL for getting asynchronous notification from driver + * ========================================================*/ + +/*IOCTL params: InputParam - NULL, OutputParam - struct vdec_msginfo*/ +#define VDEC_IOCTL_GET_NEXT_MSG \ + _IOR(VDEC_IOCTL_MAGIC, 25, struct vdec_ioctl_msg) + +#define VDEC_IOCTL_STOP_NEXT_MSG _IO(VDEC_IOCTL_MAGIC, 26) + +#define VDEC_IOCTL_GET_NUMBER_INSTANCES \ + _IOR(VDEC_IOCTL_MAGIC, 27, struct vdec_ioctl_msg) + +enum vdec_picture { + PICTURE_TYPE_I, + PICTURE_TYPE_P, + PICTURE_TYPE_B, + PICTURE_TYPE_BI, + PICTURE_TYPE_SKIP, + PICTURE_TYPE_UNKNOWN +}; + +enum vdec_buffer { + VDEC_BUFFER_TYPE_INPUT, + VDEC_BUFFER_TYPE_OUTPUT +}; + +struct vdec_allocatorproperty { + enum vdec_buffer buffer_type; + uint32_t mincount; + uint32_t maxcount; + uint32_t actualcount; + uint32_t buffer_size; + uint32_t alignment; + uint32_t buf_poolid; +}; + +struct vdec_bufferpayload { + uint8_t *bufferaddr; + uint32_t buffer_len; + int pmem_fd; + uint32_t offset; + uint32_t mmaped_size; +}; + +struct vdec_setbuffer_cmd { + enum vdec_buffer buffer_type; + struct vdec_bufferpayload buffer; +}; + +struct vdec_fillbuffer_cmd { + struct vdec_bufferpayload buffer; + void *client_data; +}; + +enum vdec_bufferflush { + VDEC_FLUSH_TYPE_INPUT, + VDEC_FLUSH_TYPE_OUTPUT, + VDEC_FLUSH_TYPE_ALL +}; + +enum vdec_codec { + VDEC_CODECTYPE_H264 = 0x1, + VDEC_CODECTYPE_H263 = 0x2, + VDEC_CODECTYPE_MPEG4 = 0x3, + VDEC_CODECTYPE_DIVX_3 = 0x4, + VDEC_CODECTYPE_DIVX_4 = 0x5, + VDEC_CODECTYPE_DIVX_5 = 0x6, + VDEC_CODECTYPE_DIVX_6 = 0x7, + VDEC_CODECTYPE_XVID = 0x8, + VDEC_CODECTYPE_MPEG1 = 0x9, + VDEC_CODECTYPE_MPEG2 = 0xa, + VDEC_CODECTYPE_VC1 = 0xb, + VDEC_CODECTYPE_VC1_RCV = 0xc +}; + +enum vdec_mpeg2_profile { + VDEC_MPEG2ProfileSimple = 0x1, + VDEC_MPEG2ProfileMain = 0x2, + VDEC_MPEG2Profile422 = 0x4, + VDEC_MPEG2ProfileSNR = 0x8, + VDEC_MPEG2ProfileSpatial = 0x10, + VDEC_MPEG2ProfileHigh = 0x20, + VDEC_MPEG2ProfileKhronosExtensions = 0x6F000000, + VDEC_MPEG2ProfileVendorStartUnused = 0x7F000000, + VDEC_MPEG2ProfileMax = 0x7FFFFFFF +}; + +enum vdec_mpeg2_level { + + VDEC_MPEG2LevelLL = 0x1, + VDEC_MPEG2LevelML = 0x2, + VDEC_MPEG2LevelH14 = 0x4, + VDEC_MPEG2LevelHL = 0x8, + VDEC_MPEG2LevelKhronosExtensions = 0x6F000000, + VDEC_MPEG2LevelVendorStartUnused = 0x7F000000, + VDEC_MPEG2LevelMax = 0x7FFFFFFF +}; + +enum vdec_mpeg4_profile { + VDEC_MPEG4ProfileSimple = 0x01, + VDEC_MPEG4ProfileSimpleScalable = 0x02, + VDEC_MPEG4ProfileCore = 0x04, + VDEC_MPEG4ProfileMain = 0x08, + VDEC_MPEG4ProfileNbit = 0x10, + VDEC_MPEG4ProfileScalableTexture = 0x20, + VDEC_MPEG4ProfileSimpleFace = 0x40, + VDEC_MPEG4ProfileSimpleFBA = 0x80, + VDEC_MPEG4ProfileBasicAnimated = 0x100, + VDEC_MPEG4ProfileHybrid = 0x200, + VDEC_MPEG4ProfileAdvancedRealTime = 0x400, + VDEC_MPEG4ProfileCoreScalable = 0x800, + VDEC_MPEG4ProfileAdvancedCoding = 0x1000, + VDEC_MPEG4ProfileAdvancedCore = 0x2000, + VDEC_MPEG4ProfileAdvancedScalable = 0x4000, + VDEC_MPEG4ProfileAdvancedSimple = 0x8000, + VDEC_MPEG4ProfileKhronosExtensions = 0x6F000000, + VDEC_MPEG4ProfileVendorStartUnused = 0x7F000000, + VDEC_MPEG4ProfileMax = 0x7FFFFFFF +}; + +enum vdec_mpeg4_level { + VDEC_MPEG4Level0 = 0x01, + VDEC_MPEG4Level0b = 0x02, + VDEC_MPEG4Level1 = 0x04, + VDEC_MPEG4Level2 = 0x08, + VDEC_MPEG4Level3 = 0x10, + VDEC_MPEG4Level4 = 0x20, + VDEC_MPEG4Level4a = 0x40, + VDEC_MPEG4Level5 = 0x80, + VDEC_MPEG4LevelKhronosExtensions = 0x6F000000, + VDEC_MPEG4LevelVendorStartUnused = 0x7F000000, + VDEC_MPEG4LevelMax = 0x7FFFFFFF +}; + +enum vdec_avc_profile { + VDEC_AVCProfileBaseline = 0x01, + VDEC_AVCProfileMain = 0x02, + VDEC_AVCProfileExtended = 0x04, + VDEC_AVCProfileHigh = 0x08, + VDEC_AVCProfileHigh10 = 0x10, + VDEC_AVCProfileHigh422 = 0x20, + VDEC_AVCProfileHigh444 = 0x40, + VDEC_AVCProfileKhronosExtensions = 0x6F000000, + VDEC_AVCProfileVendorStartUnused = 0x7F000000, + VDEC_AVCProfileMax = 0x7FFFFFFF +}; + +enum vdec_avc_level { + VDEC_AVCLevel1 = 0x01, + VDEC_AVCLevel1b = 0x02, + VDEC_AVCLevel11 = 0x04, + VDEC_AVCLevel12 = 0x08, + VDEC_AVCLevel13 = 0x10, + VDEC_AVCLevel2 = 0x20, + VDEC_AVCLevel21 = 0x40, + VDEC_AVCLevel22 = 0x80, + VDEC_AVCLevel3 = 0x100, + VDEC_AVCLevel31 = 0x200, + VDEC_AVCLevel32 = 0x400, + VDEC_AVCLevel4 = 0x800, + VDEC_AVCLevel41 = 0x1000, + VDEC_AVCLevel42 = 0x2000, + VDEC_AVCLevel5 = 0x4000, + VDEC_AVCLevel51 = 0x8000, + VDEC_AVCLevelKhronosExtensions = 0x6F000000, + VDEC_AVCLevelVendorStartUnused = 0x7F000000, + VDEC_AVCLevelMax = 0x7FFFFFFF +}; + +enum vdec_divx_profile { + VDEC_DIVXProfile_qMobile = 0x01, + VDEC_DIVXProfile_Mobile = 0x02, + VDEC_DIVXProfile_HD = 0x04, + VDEC_DIVXProfile_Handheld = 0x08, + VDEC_DIVXProfile_Portable = 0x10, + VDEC_DIVXProfile_HomeTheater = 0x20 +}; + +enum vdec_xvid_profile { + VDEC_XVIDProfile_Simple = 0x1, + VDEC_XVIDProfile_Advanced_Realtime_Simple = 0x2, + VDEC_XVIDProfile_Advanced_Simple = 0x4 +}; + +enum vdec_xvid_level { + VDEC_XVID_LEVEL_S_L0 = 0x1, + VDEC_XVID_LEVEL_S_L1 = 0x2, + VDEC_XVID_LEVEL_S_L2 = 0x4, + VDEC_XVID_LEVEL_S_L3 = 0x8, + VDEC_XVID_LEVEL_ARTS_L1 = 0x10, + VDEC_XVID_LEVEL_ARTS_L2 = 0x20, + VDEC_XVID_LEVEL_ARTS_L3 = 0x40, + VDEC_XVID_LEVEL_ARTS_L4 = 0x80, + VDEC_XVID_LEVEL_AS_L0 = 0x100, + VDEC_XVID_LEVEL_AS_L1 = 0x200, + VDEC_XVID_LEVEL_AS_L2 = 0x400, + VDEC_XVID_LEVEL_AS_L3 = 0x800, + VDEC_XVID_LEVEL_AS_L4 = 0x1000 +}; + +enum vdec_h263profile { + VDEC_H263ProfileBaseline = 0x01, + VDEC_H263ProfileH320Coding = 0x02, + VDEC_H263ProfileBackwardCompatible = 0x04, + VDEC_H263ProfileISWV2 = 0x08, + VDEC_H263ProfileISWV3 = 0x10, + VDEC_H263ProfileHighCompression = 0x20, + VDEC_H263ProfileInternet = 0x40, + VDEC_H263ProfileInterlace = 0x80, + VDEC_H263ProfileHighLatency = 0x100, + VDEC_H263ProfileKhronosExtensions = 0x6F000000, + VDEC_H263ProfileVendorStartUnused = 0x7F000000, + VDEC_H263ProfileMax = 0x7FFFFFFF +}; + +enum vdec_h263level { + VDEC_H263Level10 = 0x01, + VDEC_H263Level20 = 0x02, + VDEC_H263Level30 = 0x04, + VDEC_H263Level40 = 0x08, + VDEC_H263Level45 = 0x10, + VDEC_H263Level50 = 0x20, + VDEC_H263Level60 = 0x40, + VDEC_H263Level70 = 0x80, + VDEC_H263LevelKhronosExtensions = 0x6F000000, + VDEC_H263LevelVendorStartUnused = 0x7F000000, + VDEC_H263LevelMax = 0x7FFFFFFF +}; + +enum vdec_wmv_format { + VDEC_WMVFormatUnused = 0x01, + VDEC_WMVFormat7 = 0x02, + VDEC_WMVFormat8 = 0x04, + VDEC_WMVFormat9 = 0x08, + VDEC_WMFFormatKhronosExtensions = 0x6F000000, + VDEC_WMFFormatVendorStartUnused = 0x7F000000, + VDEC_WMVFormatMax = 0x7FFFFFFF +}; + +enum vdec_vc1_profile { + VDEC_VC1ProfileSimple = 0x1, + VDEC_VC1ProfileMain = 0x2, + VDEC_VC1ProfileAdvanced = 0x4 +}; + +enum vdec_vc1_level { + VDEC_VC1_LEVEL_S_Low = 0x1, + VDEC_VC1_LEVEL_S_Medium = 0x2, + VDEC_VC1_LEVEL_M_Low = 0x4, + VDEC_VC1_LEVEL_M_Medium = 0x8, + VDEC_VC1_LEVEL_M_High = 0x10, + VDEC_VC1_LEVEL_A_L0 = 0x20, + VDEC_VC1_LEVEL_A_L1 = 0x40, + VDEC_VC1_LEVEL_A_L2 = 0x80, + VDEC_VC1_LEVEL_A_L3 = 0x100, + VDEC_VC1_LEVEL_A_L4 = 0x200 +}; + +struct vdec_profile_level { + uint32_t profiles; + uint32_t levels; +}; + +enum vdec_interlaced_format { + VDEC_InterlaceFrameProgressive = 0x1, + VDEC_InterlaceInterleaveFrameTopFieldFirst = 0x2, + VDEC_InterlaceInterleaveFrameBottomFieldFirst = 0x4 +}; + +enum vdec_output_fromat { + VDEC_YUV_FORMAT_NV12 = 0x1, + VDEC_YUV_FORMAT_TILE_4x2 = 0x2 +}; + +struct vdec_picsize { + uint32_t frame_width; + uint32_t frame_height; + uint32_t stride; + uint32_t scan_lines; +}; + +struct vdec_seqheader { + uint8_t *ptr_seqheader; + uint32_t seq_header_len; + int pmem_fd; + uint32_t pmem_offset; +}; + +struct vdec_mberror { + uint8_t *ptr_errormap; + uint32_t err_mapsize; +}; + +struct vdec_input_frameinfo { + uint8_t *bufferaddr; + uint32_t offset; + uint32_t datalen; + uint32_t flags; + int64_t timestamp; + void *client_data; + int pmem_fd; + uint32_t pmem_offset; +}; + +struct vdec_framesize { + uint32_t n_left; + uint32_t n_top; + uint32_t n_right; + uint32_t n_bottom; +}; + +struct vdec_output_frameinfo { + uint8_t *phy_addr; + uint8_t *bufferaddr; + uint32_t offset; + uint32_t len; + uint32_t flags; + int64_t time_stamp; + void *client_data; + void *input_frame_clientdata; + struct vdec_framesize framesize; +}; + +union vdec_msgdata { + struct vdec_output_frameinfo output_frame; + void *input_frame_clientdata; +}; + +struct vdec_msginfo { + uint32_t status_code; + uint32_t msgcode; + union vdec_msgdata msgdata; + uint32_t msgdatasize; +}; +#endif /* end of macro _VDECDECODER_H_ */ diff --git a/include/linux/msm_vidc_enc.h b/include/linux/msm_vidc_enc.h new file mode 100644 index 0000000000000..b5cf584901b5f --- /dev/null +++ b/include/linux/msm_vidc_enc.h @@ -0,0 +1,592 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef _MSM_VIDC_ENC_H_ +#define _MSM_VIDC_ENC_H_ + +#include +#include + +/** STATUS CODES*/ +/* Base value for status codes */ +#define VEN_S_BASE 0x00000000 +#define VEN_S_SUCCESS (VEN_S_BASE)/* Success */ +#define VEN_S_EFAIL (VEN_S_BASE+1)/* General failure */ +#define VEN_S_EFATAL (VEN_S_BASE+2)/* Fatal irrecoverable failure*/ +#define VEN_S_EBADPARAM (VEN_S_BASE+3)/* Error passed parameters*/ +/*Command called in invalid state*/ +#define VEN_S_EINVALSTATE (VEN_S_BASE+4) +#define VEN_S_ENOSWRES (VEN_S_BASE+5)/* Insufficient OS resources*/ +#define VEN_S_ENOHWRES (VEN_S_BASE+6)/*Insufficient HW resources */ +#define VEN_S_EBUFFREQ (VEN_S_BASE+7)/* Buffer requirements were not met*/ +#define VEN_S_EINVALCMD (VEN_S_BASE+8)/* Invalid command called */ +#define VEN_S_ETIMEOUT (VEN_S_BASE+9)/* Command timeout. */ +/*Re-attempt was made when multiple invocation not supported for API.*/ +#define VEN_S_ENOREATMPT (VEN_S_BASE+10) +#define VEN_S_ENOPREREQ (VEN_S_BASE+11)/*Pre-requirement is not met for API*/ +#define VEN_S_ECMDQFULL (VEN_S_BASE+12)/*Command queue is full*/ +#define VEN_S_ENOTSUPP (VEN_S_BASE+13)/*Command not supported*/ +#define VEN_S_ENOTIMPL (VEN_S_BASE+14)/*Command not implemented.*/ +#define VEN_S_ENOTPMEM (VEN_S_BASE+15)/*Buffer is not from PMEM*/ +#define VEN_S_EFLUSHED (VEN_S_BASE+16)/*returned buffer was flushed*/ +#define VEN_S_EINSUFBUF (VEN_S_BASE+17)/*provided buffer size insufficient*/ +#define VEN_S_ESAMESTATE (VEN_S_BASE+18) +#define VEN_S_EINVALTRANS (VEN_S_BASE+19) + +#define VEN_INTF_VER 1 + +/*Asynchronous messages from driver*/ +#define VEN_MSG_INDICATION 0 +#define VEN_MSG_INPUT_BUFFER_DONE 1 +#define VEN_MSG_OUTPUT_BUFFER_DONE 2 +#define VEN_MSG_NEED_OUTPUT_BUFFER 3 +#define VEN_MSG_FLUSH_INPUT_DONE 4 +#define VEN_MSG_FLUSH_OUPUT_DONE 5 +#define VEN_MSG_START 6 +#define VEN_MSG_STOP 7 +#define VEN_MSG_PAUSE 8 +#define VEN_MSG_RESUME 9 +#define VEN_MSG_STOP_READING_MSG 10 + +/*Buffer flags bits masks*/ +#define VEN_BUFFLAG_EOS 0x00000001 +#define VEN_BUFFLAG_ENDOFFRAME 0x00000010 +#define VEN_BUFFLAG_SYNCFRAME 0x00000020 +#define VEN_BUFFLAG_EXTRADATA 0x00000040 +#define VEN_BUFFLAG_CODECCONFIG 0x00000080 + +/*ENCODER CONFIGURATION CONSTANTS*/ + +/*Encoded video frame types*/ +#define VEN_FRAME_TYPE_I 1/* I frame type */ +#define VEN_FRAME_TYPE_P 2/* P frame type */ +#define VEN_FRAME_TYPE_B 3/* B frame type */ + +/*Video codec types*/ +#define VEN_CODEC_MPEG4 1/* MPEG4 Codec */ +#define VEN_CODEC_H264 2/* H.264 Codec */ +#define VEN_CODEC_H263 3/* H.263 Codec */ + +/*Video codec profile types.*/ +#define VEN_PROFILE_MPEG4_SP 1/* 1 - MPEG4 SP profile */ +#define VEN_PROFILE_MPEG4_ASP 2/* 2 - MPEG4 ASP profile */ +#define VEN_PROFILE_H264_BASELINE 3/* 3 - H264 Baseline profile */ +#define VEN_PROFILE_H264_MAIN 4/* 4 - H264 Main profile*/ +#define VEN_PROFILE_H264_HIGH 5/* 5 - H264 High profile*/ +#define VEN_PROFILE_H263_BASELINE 6/* 6 - H263 Baseline profile */ + +/*Video codec profile level types.*/ +#define VEN_LEVEL_MPEG4_0 0x1/* MPEG4 Level 0 */ +#define VEN_LEVEL_MPEG4_1 0x2/* MPEG4 Level 1 */ +#define VEN_LEVEL_MPEG4_2 0x3/* MPEG4 Level 2 */ +#define VEN_LEVEL_MPEG4_3 0x4/* MPEG4 Level 3 */ +#define VEN_LEVEL_MPEG4_4 0x5/* MPEG4 Level 4 */ +#define VEN_LEVEL_MPEG4_5 0x6/* MPEG4 Level 5 */ +#define VEN_LEVEL_MPEG4_3b 0x7/* MPEG4 Level 3b */ +#define VEN_LEVEL_MPEG4_6 0x8/* MPEG4 Level 6 */ + +#define VEN_LEVEL_H264_1 0x9/* H.264 Level 1 */ +#define VEN_LEVEL_H264_1b 0xA/* H.264 Level 1b */ +#define VEN_LEVEL_H264_1p1 0xB/* H.264 Level 1.1 */ +#define VEN_LEVEL_H264_1p2 0xC/* H.264 Level 1.2 */ +#define VEN_LEVEL_H264_1p3 0xD/* H.264 Level 1.3 */ +#define VEN_LEVEL_H264_2 0xE/* H.264 Level 2 */ +#define VEN_LEVEL_H264_2p1 0xF/* H.264 Level 2.1 */ +#define VEN_LEVEL_H264_2p2 0x10/* H.264 Level 2.2 */ +#define VEN_LEVEL_H264_3 0x11/* H.264 Level 3 */ +#define VEN_LEVEL_H264_3p1 0x12/* H.264 Level 3.1 */ + +#define VEN_LEVEL_H263_10 0x13/* H.263 Level 10 */ +#define VEN_LEVEL_H263_20 0x14/* H.263 Level 20 */ +#define VEN_LEVEL_H263_30 0x15/* H.263 Level 30 */ +#define VEN_LEVEL_H263_40 0x16/* H.263 Level 40 */ +#define VEN_LEVEL_H263_45 0x17/* H.263 Level 45 */ +#define VEN_LEVEL_H263_50 0x18/* H.263 Level 50 */ +#define VEN_LEVEL_H263_60 0x19/* H.263 Level 60 */ +#define VEN_LEVEL_H263_70 0x1A/* H.263 Level 70 */ + +/*Entropy coding model selection for H.264 encoder.*/ +#define VEN_ENTROPY_MODEL_CAVLC 1 +#define VEN_ENTROPY_MODEL_CABAC 2 +/*Cabac model number (0,1,2) for encoder.*/ +#define VEN_CABAC_MODEL_0 1/* CABAC Model 0. */ +#define VEN_CABAC_MODEL_1 2/* CABAC Model 1. */ +#define VEN_CABAC_MODEL_2 3/* CABAC Model 2. */ + +/*Deblocking filter control type for encoder.*/ +#define VEN_DB_DISABLE 1/* 1 - Disable deblocking filter*/ +#define VEN_DB_ALL_BLKG_BNDRY 2/* 2 - All blocking boundary filtering*/ +#define VEN_DB_SKIP_SLICE_BNDRY 3/* 3 - Filtering except sliceboundary*/ + +/*Different methods of Multi slice selection.*/ +#define VEN_MSLICE_OFF 1 +#define VEN_MSLICE_CNT_MB 2 /*number of MBscount per slice*/ +#define VEN_MSLICE_CNT_BYTE 3 /*number of bytes count per slice.*/ +#define VEN_MSLICE_GOB 4 /*Multi slice by GOB for H.263 only.*/ + +/*Different modes for Rate Control.*/ +#define VEN_RC_OFF 1 +#define VEN_RC_VBR_VFR 2 +#define VEN_RC_VBR_CFR 3 +#define VEN_RC_CBR_VFR 4 + +/*Different modes for flushing buffers*/ +#define VEN_FLUSH_INPUT 1 +#define VEN_FLUSH_OUTPUT 2 +#define VEN_FLUSH_ALL 3 + +/*Different input formats for YUV data.*/ +#define VEN_INPUTFMT_NV12 1/* NV12 Linear */ +#define VEN_INPUTFMT_NV21 2/* NV21 Linear */ + +/*Different allowed rotation modes.*/ +#define VEN_ROTATION_0 1/* 0 degrees */ +#define VEN_ROTATION_90 2/* 90 degrees */ +#define VEN_ROTATION_180 3/* 180 degrees */ +#define VEN_ROTATION_270 4/* 270 degrees */ + +/*IOCTL timeout values*/ +#define VEN_TIMEOUT_INFINITE 0xffffffff + +/*Different allowed intra refresh modes.*/ +#define VEN_IR_OFF 1 +#define VEN_IR_CYCLIC 2 +#define VEN_IR_RANDOM 3 + +/*IOCTL BASE CODES Not to be used directly by the client.*/ +/* Base value for ioctls that are not related to encoder configuration.*/ +#define VEN_IOCTLBASE_NENC 0x800 +/* Base value for encoder configuration ioctls*/ +#define VEN_IOCTLBASE_ENC 0x850 + +struct venc_ioctl_msg{ + void *inputparam; + void *outputparam; +}; + +/*NON ENCODER CONFIGURATION IOCTLs*/ + +/*IOCTL params:SET: InputData - unsigned long, OutputData - NULL*/ +#define VEN_IOCTL_SET_INTF_VERSION \ + _IOW(VEN_IOCTLBASE_NENC, 0, struct venc_ioctl_msg) + +/*IOCTL params:CMD: InputData - venc_timeout, OutputData - venc_msg*/ +#define VEN_IOCTL_CMD_READ_NEXT_MSG \ + _IOWR(VEN_IOCTLBASE_NENC, 1, struct venc_ioctl_msg) + +/*IOCTL params:CMD: InputData - NULL, OutputData - NULL*/ +#define VEN_IOCTL_CMD_STOP_READ_MSG _IO(VEN_IOCTLBASE_NENC, 2) + +/*IOCTL params:SET: InputData - venc_allocatorproperty, OutputData - NULL + GET: InputData - NULL, OutputData - venc_allocatorproperty*/ +#define VEN_IOCTL_SET_INPUT_BUFFER_REQ \ + _IOW(VEN_IOCTLBASE_NENC, 3, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_INPUT_BUFFER_REQ \ + _IOR(VEN_IOCTLBASE_NENC, 4, struct venc_ioctl_msg) + +/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL*/ +#define VEN_IOCTL_CMD_ALLOC_INPUT_BUFFER \ + _IOW(VEN_IOCTLBASE_NENC, 5, struct venc_ioctl_msg) + +/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL*/ +#define VEN_IOCTL_SET_INPUT_BUFFER \ + _IOW(VEN_IOCTLBASE_NENC, 6, struct venc_ioctl_msg) + +/*IOCTL params: CMD: InputData - venc_bufferpayload, OutputData - NULL*/ +#define VEN_IOCTL_CMD_FREE_INPUT_BUFFER \ + _IOW(VEN_IOCTLBASE_NENC, 7, struct venc_ioctl_msg) + +/*IOCTL params:SET: InputData - venc_allocatorproperty, OutputData - NULL + GET: InputData - NULL, OutputData - venc_allocatorproperty*/ +#define VEN_IOCTL_SET_OUTPUT_BUFFER_REQ \ + _IOW(VEN_IOCTLBASE_NENC, 8, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_OUTPUT_BUFFER_REQ \ + _IOR(VEN_IOCTLBASE_NENC, 9, struct venc_ioctl_msg) + +/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL*/ +#define VEN_IOCTL_CMD_ALLOC_OUTPUT_BUFFER \ + _IOW(VEN_IOCTLBASE_NENC, 10, struct venc_ioctl_msg) + + +/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL*/ +#define VEN_IOCTL_SET_OUTPUT_BUFFER \ + _IOW(VEN_IOCTLBASE_NENC, 11, struct venc_ioctl_msg) + +/*IOCTL params:CMD: InputData - venc_bufferpayload, OutputData - NULL.*/ +#define VEN_IOCTL_CMD_FREE_OUTPUT_BUFFER \ + _IOW(VEN_IOCTLBASE_NENC, 12, struct venc_ioctl_msg) + + +/* Asynchronous respone message code:* VEN_MSG_START*/ +#define VEN_IOCTL_CMD_START _IO(VEN_IOCTLBASE_NENC, 13) + + +/*IOCTL params:CMD: InputData - venc_buffer, OutputData - NULL + Asynchronous respone message code:VEN_MSG_INPUT_BUFFER_DONE*/ +#define VEN_IOCTL_CMD_ENCODE_FRAME \ + _IOW(VEN_IOCTLBASE_NENC, 14, struct venc_ioctl_msg) + + +/*IOCTL params:CMD: InputData - venc_buffer, OutputData - NULL + Asynchronous response message code:VEN_MSG_OUTPUT_BUFFER_DONE*/ +#define VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER \ + _IOW(VEN_IOCTLBASE_NENC, 15, struct venc_ioctl_msg) + +/*IOCTL params:CMD: InputData - venc_bufferflush, OutputData - NULL + * Asynchronous response message code:VEN_MSG_INPUT_BUFFER_DONE*/ +#define VEN_IOCTL_CMD_FLUSH \ + _IOW(VEN_IOCTLBASE_NENC, 16, struct venc_ioctl_msg) + + +/*Asynchronous respone message code:VEN_MSG_PAUSE*/ +#define VEN_IOCTL_CMD_PAUSE _IO(VEN_IOCTLBASE_NENC, 17) + +/*Asynchronous respone message code:VEN_MSG_RESUME*/ +#define VEN_IOCTL_CMD_RESUME _IO(VEN_IOCTLBASE_NENC, 18) + +/* Asynchronous respone message code:VEN_MSG_STOP*/ +#define VEN_IOCTL_CMD_STOP _IO(VEN_IOCTLBASE_NENC, 19) + + +/*ENCODER PROPERTY CONFIGURATION & CAPABILITY IOCTLs*/ + +/*IOCTL params:SET: InputData - venc_basecfg, OutputData - NULL + GET: InputData - NULL, OutputData - venc_basecfg*/ +#define VEN_IOCTL_SET_BASE_CFG \ + _IOW(VEN_IOCTLBASE_ENC, 1, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_BASE_CFG \ + _IOR(VEN_IOCTLBASE_ENC, 2, struct venc_ioctl_msg) + +/*IOCTL params:SET: InputData - venc_switch, OutputData - NULL + GET: InputData - NULL, OutputData - venc_switch*/ +#define VEN_IOCTL_SET_LIVE_MODE \ + _IOW(VEN_IOCTLBASE_ENC, 3, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_LIVE_MODE \ + _IOR(VEN_IOCTLBASE_ENC, 4, struct venc_ioctl_msg) + + +/*IOCTL params:SET: InputData - venc_profile, OutputData - NULL + GET: InputData - NULL, OutputData - venc_profile*/ +#define VEN_IOCTL_SET_CODEC_PROFILE \ + _IOW(VEN_IOCTLBASE_ENC, 5, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_CODEC_PROFILE \ + _IOR(VEN_IOCTLBASE_ENC, 6, struct venc_ioctl_msg) + + +/*IOCTL params:SET: InputData - ven_profilelevel, OutputData - NULL + GET: InputData - NULL, OutputData - ven_profilelevel*/ +#define VEN_IOCTL_SET_PROFILE_LEVEL \ + _IOW(VEN_IOCTLBASE_ENC, 7, struct venc_ioctl_msg) + +#define VEN_IOCTL_GET_PROFILE_LEVEL \ + _IOR(VEN_IOCTLBASE_ENC, 8, struct venc_ioctl_msg) + +/*IOCTL params:SET: InputData - venc_switch, OutputData - NULL + GET: InputData - NULL, OutputData - venc_switch*/ +#define VEN_IOCTL_SET_SHORT_HDR \ + _IOW(VEN_IOCTLBASE_ENC, 9, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_SHORT_HDR \ + _IOR(VEN_IOCTLBASE_ENC, 10, struct venc_ioctl_msg) + + +/*IOCTL params: SET: InputData - venc_sessionqp, OutputData - NULL + GET: InputData - NULL, OutputData - venc_sessionqp*/ +#define VEN_IOCTL_SET_SESSION_QP \ + _IOW(VEN_IOCTLBASE_ENC, 11, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_SESSION_QP \ + _IOR(VEN_IOCTLBASE_ENC, 12, struct venc_ioctl_msg) + + +/*IOCTL params:SET: InputData - venc_intraperiod, OutputData - NULL + GET: InputData - NULL, OutputData - venc_intraperiod*/ +#define VEN_IOCTL_SET_INTRA_PERIOD \ + _IOW(VEN_IOCTLBASE_ENC, 13, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_INTRA_PERIOD \ + _IOR(VEN_IOCTLBASE_ENC, 14, struct venc_ioctl_msg) + + +/* Request an Iframe*/ +#define VEN_IOCTL_CMD_REQUEST_IFRAME _IO(VEN_IOCTLBASE_ENC, 15) + +/*IOCTL params:GET: InputData - NULL, OutputData - venc_capability*/ +#define VEN_IOCTL_GET_CAPABILITY \ + _IOR(VEN_IOCTLBASE_ENC, 16, struct venc_ioctl_msg) + + +/*IOCTL params:GET: InputData - NULL, OutputData - venc_seqheader*/ +#define VEN_IOCTL_GET_SEQUENCE_HDR \ + _IOR(VEN_IOCTLBASE_ENC, 17, struct venc_ioctl_msg) + +/*IOCTL params:SET: InputData - venc_entropycfg, OutputData - NULL + GET: InputData - NULL, OutputData - venc_entropycfg*/ +#define VEN_IOCTL_SET_ENTROPY_CFG \ + _IOW(VEN_IOCTLBASE_ENC, 18, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_ENTROPY_CFG \ + _IOR(VEN_IOCTLBASE_ENC, 19, struct venc_ioctl_msg) + +/*IOCTL params:SET: InputData - venc_dbcfg, OutputData - NULL + GET: InputData - NULL, OutputData - venc_dbcfg*/ +#define VEN_IOCTL_SET_DEBLOCKING_CFG \ + _IOW(VEN_IOCTLBASE_ENC, 20, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_DEBLOCKING_CFG \ + _IOR(VEN_IOCTLBASE_ENC, 21, struct venc_ioctl_msg) + + +/*IOCTL params:SET: InputData - venc_intrarefresh, OutputData - NULL + GET: InputData - NULL, OutputData - venc_intrarefresh*/ +#define VEN_IOCTL_SET_INTRA_REFRESH \ + _IOW(VEN_IOCTLBASE_ENC, 22, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_INTRA_REFRESH \ + _IOR(VEN_IOCTLBASE_ENC, 23, struct venc_ioctl_msg) + + +/*IOCTL params:SET: InputData - venc_multiclicecfg, OutputData - NULL + GET: InputData - NULL, OutputData - venc_multiclicecfg*/ +#define VEN_IOCTL_SET_MULTI_SLICE_CFG \ + _IOW(VEN_IOCTLBASE_ENC, 24, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_MULTI_SLICE_CFG \ + _IOR(VEN_IOCTLBASE_ENC, 25, struct venc_ioctl_msg) + +/*IOCTL params:SET: InputData - venc_ratectrlcfg, OutputData - NULL + GET: InputData - NULL, OutputData - venc_ratectrlcfg*/ +#define VEN_IOCTL_SET_RATE_CTRL_CFG \ + _IOW(VEN_IOCTLBASE_ENC, 26, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_RATE_CTRL_CFG \ + _IOR(VEN_IOCTLBASE_ENC, 27, struct venc_ioctl_msg) + + +/*IOCTL params:SET: InputData - venc_voptimingcfg, OutputData - NULL + GET: InputData - NULL, OutputData - venc_voptimingcfg*/ +#define VEN_IOCTL_SET_VOP_TIMING_CFG \ + _IOW(VEN_IOCTLBASE_ENC, 28, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_VOP_TIMING_CFG \ + _IOR(VEN_IOCTLBASE_ENC, 29, struct venc_ioctl_msg) + + +/*IOCTL params:SET: InputData - venc_framerate, OutputData - NULL + GET: InputData - NULL, OutputData - venc_framerate*/ +#define VEN_IOCTL_SET_FRAME_RATE \ + _IOW(VEN_IOCTLBASE_ENC, 30, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_FRAME_RATE \ + _IOR(VEN_IOCTLBASE_ENC, 31, struct venc_ioctl_msg) + + +/*IOCTL params:SET: InputData - venc_targetbitrate, OutputData - NULL + GET: InputData - NULL, OutputData - venc_targetbitrate*/ +#define VEN_IOCTL_SET_TARGET_BITRATE \ + _IOW(VEN_IOCTLBASE_ENC, 32, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_TARGET_BITRATE \ + _IOR(VEN_IOCTLBASE_ENC, 33, struct venc_ioctl_msg) + + +/*IOCTL params:SET: InputData - venc_rotation, OutputData - NULL + GET: InputData - NULL, OutputData - venc_rotation*/ +#define VEN_IOCTL_SET_ROTATION \ + _IOW(VEN_IOCTLBASE_ENC, 34, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_ROTATION \ + _IOR(VEN_IOCTLBASE_ENC, 35, struct venc_ioctl_msg) + + +/*IOCTL params:SET: InputData - venc_headerextension, OutputData - NULL + GET: InputData - NULL, OutputData - venc_headerextension*/ +#define VEN_IOCTL_SET_HEC \ + _IOW(VEN_IOCTLBASE_ENC, 36, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_HEC \ + _IOR(VEN_IOCTLBASE_ENC, 37, struct venc_ioctl_msg) + +/*IOCTL params:SET: InputData - venc_switch, OutputData - NULL + GET: InputData - NULL, OutputData - venc_switch*/ +#define VEN_IOCTL_SET_DATA_PARTITION \ + _IOW(VEN_IOCTLBASE_ENC, 38, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_DATA_PARTITION \ + _IOR(VEN_IOCTLBASE_ENC, 39, struct venc_ioctl_msg) + +/*IOCTL params:SET: InputData - venc_switch, OutputData - NULL + GET: InputData - NULL, OutputData - venc_switch*/ +#define VEN_IOCTL_SET_RVLC \ + _IOW(VEN_IOCTLBASE_ENC, 40, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_RVLC \ + _IOR(VEN_IOCTLBASE_ENC, 41, struct venc_ioctl_msg) + + +/*IOCTL params:SET: InputData - venc_switch, OutputData - NULL + GET: InputData - NULL, OutputData - venc_switch*/ +#define VEN_IOCTL_SET_AC_PREDICTION \ + _IOW(VEN_IOCTLBASE_ENC, 42, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_AC_PREDICTION \ + _IOR(VEN_IOCTLBASE_ENC, 43, struct venc_ioctl_msg) + + +/*IOCTL params:SET: InputData - venc_qprange, OutputData - NULL + GET: InputData - NULL, OutputData - venc_qprange*/ +#define VEN_IOCTL_SET_QP_RANGE \ + _IOW(VEN_IOCTLBASE_ENC, 44, struct venc_ioctl_msg) +#define VEN_IOCTL_GET_QP_RANGE \ + _IOR(VEN_IOCTLBASE_ENC, 45, struct venc_ioctl_msg) + +struct venc_switch{ + unsigned char status; +}; + +struct venc_allocatorproperty{ + unsigned long mincount; + unsigned long maxcount; + unsigned long actualcount; + unsigned long datasize; + unsigned long suffixsize; + unsigned long alignment; + unsigned long bufpoolid; +}; + +struct venc_bufferpayload{ + unsigned char *pbuffer; + unsigned long nsize; + int fd; + unsigned int offset; + unsigned int maped_size; + unsigned long filled_len; +}; + +struct venc_buffer{ + unsigned char *ptrbuffer; + unsigned long size; + unsigned long len; + unsigned long offset; + long long timestamp; + unsigned long flags; + void *clientdata; +}; + +struct venc_basecfg{ + unsigned long input_width; + unsigned long input_height; + unsigned long dvs_width; + unsigned long dvs_height; + unsigned long codectype; + unsigned long fps_num; + unsigned long fps_den; + unsigned long targetbitrate; + unsigned long inputformat; +}; + +struct venc_profile{ + unsigned long profile; +}; +struct ven_profilelevel{ + unsigned long level; +}; + +struct venc_sessionqp{ + unsigned long iframeqp; + unsigned long pframqp; +}; + +struct venc_qprange{ + unsigned long maxqp; + unsigned long minqp; +}; +struct venc_intraperiod{ + unsigned long num_pframes; +}; +struct venc_seqheader{ + unsigned char *hdrbufptr; + unsigned long bufsize; + unsigned long hdrlen; +}; + +struct venc_capability{ + unsigned long codec_types; + unsigned long maxframe_width; + unsigned long maxframe_height; + unsigned long maxtarget_bitrate; + unsigned long maxframe_rate; + unsigned long input_formats; + unsigned char dvs; +}; + +struct venc_entropycfg{ + unsigned longentropysel; + unsigned long cabacmodel; +}; + +struct venc_dbcfg{ + unsigned long db_mode; + unsigned long slicealpha_offset; + unsigned long slicebeta_offset; +}; + +struct venc_intrarefresh{ + unsigned long irmode; + unsigned long mbcount; +}; + +struct venc_multiclicecfg{ + unsigned long mslice_mode; + unsigned long mslice_size; +}; + +struct venc_bufferflush{ + unsigned long flush_mode; +}; + +struct venc_ratectrlcfg{ + unsigned long rcmode; +}; + +struct venc_voptimingcfg{ + unsigned long voptime_resolution; +}; +struct venc_framerate{ + unsigned long fps_denominator; + unsigned long fps_numerator; +}; + +struct venc_targetbitrate{ + unsigned long target_bitrate; +}; + + +struct venc_rotation{ + unsigned long rotation; +}; + +struct venc_timeout{ + unsigned long millisec; +}; + +struct venc_headerextension{ + unsigned long header_extension; +}; + +struct venc_msg{ + unsigned long statuscode; + unsigned long msgcode; + struct venc_buffer buf; + unsigned long msgdata_size; +}; +#endif /* _MSM_VIDC_ENC_H_ */ From ad7cd27da2a5c5288a9262973584ac313ecf62c0 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 26 May 2010 18:35:19 -0700 Subject: [PATCH 0694/2556] vidc_720p: minimum modifications to compile and run o Use dma_alloc_coherent() instead of pmem_alloc() Note: MFC_CLK power rail must be manually turned on from board file. Change-Id: If099b0eda54f1cfe66833fcd6c67b9f1f1a65c48 Signed-off-by: Nick Pelly --- drivers/misc/video_core/720p/ddl/vcd_ddl.h | 1 + .../misc/video_core/720p/ddl/vcd_ddl_utils.c | 39 +++++++------------ .../video_core/720p/init/video_core_init.c | 28 ++----------- .../720p/resource_tracker/vcd_res_tracker.c | 2 + drivers/misc/video_core/720p/vcd/vcd_sub.c | 12 ++++-- drivers/misc/video_core/720p/vcd/vcd_util.c | 22 ++++------- drivers/misc/video_core/720p/vcd/vcd_util.h | 2 +- .../video_core/720p/vcd/video_core_type.h | 2 +- 8 files changed, 39 insertions(+), 69 deletions(-) diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl.h b/drivers/misc/video_core/720p/ddl/vcd_ddl.h index 8b808b2983f8f..21e96dc2f9ea3 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl.h +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl.h @@ -88,6 +88,7 @@ struct ddl_buf_addr_type { u32 *p_align_physical_addr; u32 *p_align_virtual_addr; u32 n_buffer_size; + u32 n_buffer_size_guard; }; enum ddl_cmd_state_type { diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.c index a2df26aadb484..ff1de05b93f84 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.c +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.c @@ -86,7 +86,8 @@ void ddl_pmem_free(struct ddl_buf_addr_type buff_addr) void ddl_pmem_alloc(struct ddl_buf_addr_type *buff_addr, u32 size, u32 align) { u32 n_guard_bytes, n_align_mask; - s32 n_physical_addr; + dma_addr_t n_physical_addr; + void *virtual_addr; u32 n_align_offset; if (align == DDL_LINEAR_BUFFER_ALIGN_BYTES) { @@ -100,29 +101,23 @@ void ddl_pmem_alloc(struct ddl_buf_addr_type *buff_addr, u32 size, u32 align) n_align_mask = DDL_TILE_BUF_ALIGN_MASK; } - n_physical_addr = pmem_kalloc((size + n_guard_bytes), - PMEM_MEMTYPE_EBI1 | PMEM_ALIGNMENT_4K); - buff_addr->p_physical_base_addr = (u32 *)n_physical_addr; + // n_physical_addr = pmem_kalloc((size + n_guard_bytes), + // PMEM_MEMTYPE_EBI1 | PMEM_ALIGNMENT_4K); + virtual_addr = dma_alloc_coherent(NULL, size + n_guard_bytes, + &n_physical_addr, GFP_KERNEL); - if (IS_ERR((void *)n_physical_addr)) { + if (IS_ERR(virtual_addr)) { pr_err("%s(): could not allocte in kernel pmem buffers\n", __func__); return; } - buff_addr->p_virtual_base_addr = - (u32 *) ioremap((unsigned long)n_physical_addr, - size + n_guard_bytes); + buff_addr->p_physical_base_addr = (u32 *) n_physical_addr; + buff_addr->p_virtual_base_addr = virtual_addr; memset(buff_addr->p_virtual_base_addr, 0 , size + n_guard_bytes); - if (!buff_addr->p_virtual_base_addr) { - - pr_err("%s: could not ioremap in kernel pmem buffers\n", - __func__); - pmem_kfree(n_physical_addr); - return; - } buff_addr->n_buffer_size = size; + buff_addr->n_buffer_size_guard = size + n_guard_bytes; buff_addr->p_align_physical_addr = (u32 *) ((n_physical_addr + n_guard_bytes) & n_align_mask); @@ -136,7 +131,7 @@ void ddl_pmem_alloc(struct ddl_buf_addr_type *buff_addr, u32 size, u32 align) pr_debug("%s(): phy addr 0x%08x kernel addr 0x%08x\n", __func__, (u32) buff_addr->p_align_physical_addr, - (u32) buff_addr->p_align_physical_addr); + (u32) buff_addr->p_align_virtual_addr); return; } @@ -147,14 +142,10 @@ void ddl_pmem_free(struct ddl_buf_addr_type buff_addr) __func__, buff_addr.p_physical_base_addr, buff_addr.p_virtual_base_addr); - if (buff_addr.p_virtual_base_addr) - iounmap((void *)buff_addr.p_virtual_base_addr); - - if ((buff_addr.p_physical_base_addr) && - pmem_kfree((s32) buff_addr.p_physical_base_addr)) { - ERR("\n %s(): Error in Freeing ddl_pmem_free " - "Physical Address %p", __func__, - buff_addr.p_physical_base_addr); + if (buff_addr.p_virtual_base_addr) { + dma_free_coherent(NULL, buff_addr.n_buffer_size_guard, + buff_addr.p_virtual_base_addr, + (dma_addr_t) buff_addr.p_physical_base_addr); } buff_addr.n_buffer_size = 0; diff --git a/drivers/misc/video_core/720p/init/video_core_init.c b/drivers/misc/video_core/720p/init/video_core_init.c index d0461e35178dc..5e0bc0975568b 100644 --- a/drivers/misc/video_core/720p/init/video_core_init.c +++ b/drivers/misc/video_core/720p/init/video_core_init.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include "vcd_api.h" @@ -158,6 +157,7 @@ static int __init vid_c_720p_probe(struct platform_device *pdev) ERR("%s: create workque failed \n", __func__); return -ENOMEM; } + return 0; } @@ -300,16 +300,7 @@ u32 vid_c_enable_pwr_rail(void) mutex_lock(&vid_c_device_p->lock); if (!vid_c_device_p->rail_enabled) { - rc = internal_pwr_rail_mode(PWR_RAIL_MFC_CLK, - PWR_RAIL_CTL_MANUAL); - if (rc) { - ERR("%s(): internal_pwr_rail_mode failed %d \n", - __func__, rc); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; - } - DBG("%s(): internal_pwr_rail_mode Success %d \n", - __func__, rc); + //TODO: internal_pwr_rail_mode(MFC_CLK_ID, MANUAL) vid_c_device_p->pclk = clk_get(vid_c_device_p->device, "mfc_pclk"); @@ -344,13 +335,7 @@ u32 vid_c_enable_pwr_rail(void) return FALSE; } - rc = internal_pwr_rail_ctl(PWR_RAIL_MFC_CLK, 1); - if (rc) { - ERR("\n internal_pwr_rail_ctl failed %d\n", rc); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; - } - DBG("%s(): internal_pwr_rail_ctl Success %d \n", __func__, rc); + //TODO: internal_pwr_rail_ctl(MFC_CLK_ID, 1) msleep(20); rc = clk_reset(vid_c_device_p->pclk, CLK_RESET_DEASSERT); @@ -392,12 +377,7 @@ u32 vid_c_disable_pwr_rail(void) } msleep(20); - rc = internal_pwr_rail_ctl(PWR_RAIL_MFC_CLK, 0); - if (rc) { - ERR("\n clk_reset failed %d\n", rc); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; - } + //TODO: internal_pwr_rail_ctl(MFC_CLK_ID, 0) clk_put(vid_c_device_p->hclk_div2); clk_put(vid_c_device_p->hclk); diff --git a/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.c b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.c index 40d4886347c1a..1bf916c7cefba 100644 --- a/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.c +++ b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.c @@ -21,7 +21,9 @@ #include "video_core_init.h" #include +#ifdef AXI_CLK_SCALING #include +#endif #define MSM_AXI_QOS_NAME "msm_vidc_reg" diff --git a/drivers/misc/video_core/720p/vcd/vcd_sub.c b/drivers/misc/video_core/720p/vcd/vcd_sub.c index 5e1cacc6e6015..6f7c4644b0882 100644 --- a/drivers/misc/video_core/720p/vcd/vcd_sub.c +++ b/drivers/misc/video_core/720p/vcd/vcd_sub.c @@ -551,7 +551,8 @@ u32 vcd_free_one_buffer_internal( p_buf_entry->p_virtual, p_buf_entry->b_allocated); if (p_buf_entry->b_allocated) { - vcd_pmem_free(p_buf_entry->p_alloc, p_buf_entry->p_physical); + vcd_pmem_free(p_buf_entry->n_size, p_buf_entry->p_alloc, + p_buf_entry->p_physical); p_buf_pool->n_allocated--; @@ -577,7 +578,8 @@ u32 vcd_free_buffers_internal( for (i = 1; i <= p_buf_pool->n_count; i++) { if (p_buf_pool->a_entries[i].b_valid && p_buf_pool->a_entries[i].b_allocated) { - vcd_pmem_free(p_buf_pool->a_entries[i].p_alloc, + vcd_pmem_free(p_buf_pool->a_entries[i].n_size, + p_buf_pool->a_entries[i].p_alloc, p_buf_pool->a_entries[i]. p_physical); } @@ -990,7 +992,8 @@ void vcd_destroy_client_context(struct vcd_clnt_ctxt_type_t *p_cctxt) } if (p_cctxt->seq_hdr.p_sequence_header) { - vcd_pmem_free(p_cctxt->seq_hdr.p_sequence_header, + vcd_pmem_free(p_cctxt->seq_hdr.n_sequence_header_len, + p_cctxt->seq_hdr.p_sequence_header, p_cctxt->p_seq_hdr_phy_addr); p_cctxt->seq_hdr.p_sequence_header = NULL; } @@ -2881,7 +2884,8 @@ u32 vcd_store_seq_hdr( if (p_cctxt->seq_hdr.p_sequence_header) { VCD_MSG_HIGH("Old seq hdr detected"); - vcd_pmem_free(p_cctxt->seq_hdr.p_sequence_header, + vcd_pmem_free(p_cctxt->seq_hdr.n_sequence_header_len, + p_cctxt->seq_hdr.p_sequence_header, p_cctxt->p_seq_hdr_phy_addr); p_cctxt->seq_hdr.p_sequence_header = NULL; } diff --git a/drivers/misc/video_core/720p/vcd/vcd_util.c b/drivers/misc/video_core/720p/vcd/vcd_util.c index 9a0a52c3c67b3..579ca8aa1316d 100644 --- a/drivers/misc/video_core/720p/vcd/vcd_util.c +++ b/drivers/misc/video_core/720p/vcd/vcd_util.c @@ -78,19 +78,11 @@ u32 vcd_critical_section_leave(u32 *cs) int vcd_pmem_alloc(u32 size, u8 **kernel_vaddr, u8 **phy_addr) { - *phy_addr = - (u8 *) pmem_kalloc(size, PMEM_MEMTYPE_EBI1 | PMEM_ALIGNMENT_4K); + *kernel_vaddr = dma_alloc_coherent(NULL, size, (dma_addr_t *)phy_addr, GFP_KERNEL); +// *phy_addr = +// (u8 *) pmem_kalloc(size, PMEM_MEMTYPE_EBI1 | PMEM_ALIGNMENT_4K); - if (!IS_ERR((void *)*phy_addr)) { - - *kernel_vaddr = ioremap((unsigned long)*phy_addr, size); - - if (!*kernel_vaddr) { - pr_err("%s: could not ioremap in kernel pmem buffers\n", - __func__); - pmem_kfree((s32) *phy_addr); - return -ENOMEM; - } + if (!IS_ERR((void *)*kernel_vaddr)) { pr_debug("write buf: phy addr 0x%08x kernel addr 0x%08x\n", (u32) *phy_addr, (u32) *kernel_vaddr); return 0; @@ -102,10 +94,10 @@ int vcd_pmem_alloc(u32 size, u8 **kernel_vaddr, u8 **phy_addr) } -int vcd_pmem_free(u8 *kernel_vaddr, u8 *phy_addr) +int vcd_pmem_free(u32 size, u8 *kernel_vaddr, u8 *phy_addr) { - iounmap((void *)kernel_vaddr); - pmem_kfree((s32) phy_addr); + dma_free_coherent(NULL, size, (void *)kernel_vaddr, (dma_addr_t)phy_addr); +// pmem_kfree((s32) phy_addr); return 0; } diff --git a/drivers/misc/video_core/720p/vcd/vcd_util.h b/drivers/misc/video_core/720p/vcd/vcd_util.h index 760829579db83..375145945c976 100644 --- a/drivers/misc/video_core/720p/vcd/vcd_util.h +++ b/drivers/misc/video_core/720p/vcd/vcd_util.h @@ -76,7 +76,7 @@ #define VCD_PMEM_get_physical(p_mem) virt_to_phys(p_mem) #else int vcd_pmem_alloc(u32 size, u8 **kernel_vaddr, u8 **phy_addr); - int vcd_pmem_free(u8 *kernel_vaddr, u8 *phy_addr); + int vcd_pmem_free(u32 size, u8 *kernel_vaddr, u8 *phy_addr); #endif u32 vcd_critical_section_create(u32 **p_cs); diff --git a/drivers/misc/video_core/720p/vcd/video_core_type.h b/drivers/misc/video_core/720p/vcd/video_core_type.h index 6324e0ad64404..c87f8351e5924 100644 --- a/drivers/misc/video_core/720p/vcd/video_core_type.h +++ b/drivers/misc/video_core/720p/vcd/video_core_type.h @@ -45,7 +45,7 @@ #define DEBUG 0 #define USE_RES_TRACKER -#define AXI_CLK_SCALING +//#define AXI_CLK_SCALING #undef CORE_TIMING_INFO From 1609c2d7151d9053cf388882bfef2aeefb6b60b4 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Wed, 2 Jun 2010 12:40:47 -0700 Subject: [PATCH 0695/2556] [ARM] msm: add vidc_720p driver to msm7x30 devices Change-Id: I6e35d0cd7af70df84d5e4f6cb8663d521cebaeb6 Signed-off-by: Nick Pelly --- arch/arm/mach-msm/devices-msm7x30.c | 20 ++++++++++++++++++++ arch/arm/mach-msm/devices.h | 1 + 2 files changed, 21 insertions(+) diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c index 6fe5d20a7f429..d3902249f2fa3 100644 --- a/arch/arm/mach-msm/devices-msm7x30.c +++ b/arch/arm/mach-msm/devices-msm7x30.c @@ -613,6 +613,26 @@ struct platform_device msm_device_spi = { .resource = resources_spi, }; +static struct resource msm_vidc_720p_resources[] = { + { + .start = 0xA3B00000, + .end = 0xA3B00000 + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_MFC720, + .end = INT_MFC720, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device msm_device_vidc_720p = { + .name = "msm_vidc_720p", + .id = 0, + .num_resources = ARRAY_SIZE(msm_vidc_720p_resources), + .resource = msm_vidc_720p_resources, +}; + static struct resource resources_otg[] = { { .start = MSM_HSUSB_PHYS, diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h index 35d148fd96297..2337fe4c883e0 100644 --- a/arch/arm/mach-msm/devices.h +++ b/arch/arm/mach-msm/devices.h @@ -61,5 +61,6 @@ extern unsigned msm_num_clocks_7x30; extern struct clk msm_clocks_8x50[]; extern unsigned msm_num_clocks_8x50; +extern struct platform_device msm_device_vidc_720p; #endif From ebfba6163f428ecd9b4c40a866d446b8c075055b Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Fri, 11 Jun 2010 13:30:11 -0700 Subject: [PATCH 0696/2556] vidc_720p: cleanup #1 o Do not compile helpers for CORE_TIMING_INFO when disabled. o ddl_pmem -> ddl_dma o u32 * -> dma_addr_t o u32 * -> void * o Variable name shortening o Style cleanup o Remove type hint from variable names. p_foo -> foo. o Remove type hint from variable names. n_foo -> foo. o Remove type hint from variable names. p_foo -> foo. o Remove type hint from variable names. e_foo -> foo. o Remove type hint from variable names. i_foo -> foo. o Remove type hint from variable names. b_foo -> foo. o Remove type hint from variable names. a_foo -> foo. o Remove DDL_MEMCPY, DDL_MALLOC, DDL_FREE, TRUE, FALSE macros. o Remove vcd_malloc(), vcd_free(). o Turn on verbose logging for now. o Do all allocation at init time - hacky implementation for now. o More vcd/ cleanup o Checkpatch fixes. o More vcd/, enc/, dec/ cleanup o Use void *, void __user *, phys_addr_t for managing buffers. o Use ALIGN() and friends instead of hand-coded alignment routines. o Redo firmware download. o Bring sanity to video_core_720p.h o Replace some custom logging macro's with pr_debug, pr_err etc. Change-Id: Ifec24c1d11adebce45cbab4aff680d700bde854a Signed-off-by: Nick Pelly --- drivers/misc/video_core/720p/Makefile | 2 - drivers/misc/video_core/720p/ddl/vcd_ddl.c | 637 ++-- drivers/misc/video_core/720p/ddl/vcd_ddl.h | 344 +- .../misc/video_core/720p/ddl/vcd_ddl_api.h | 55 +- .../misc/video_core/720p/ddl/vcd_ddl_core.h | 46 +- .../misc/video_core/720p/ddl/vcd_ddl_errors.c | 242 +- .../video_core/720p/ddl/vcd_ddl_firmware.c | 407 +- .../video_core/720p/ddl/vcd_ddl_firmware.h | 47 +- .../misc/video_core/720p/ddl/vcd_ddl_hal.c | 1058 +++--- .../misc/video_core/720p/ddl/vcd_ddl_helper.c | 353 +- .../720p/ddl/vcd_ddl_internal_property.h | 40 +- .../720p/ddl/vcd_ddl_interrupt_handler.c | 1295 +++---- .../video_core/720p/ddl/vcd_ddl_metadata.c | 836 ++--- .../video_core/720p/ddl/vcd_ddl_metadata.h | 51 +- .../video_core/720p/ddl/vcd_ddl_properties.c | 2508 +++++-------- .../misc/video_core/720p/ddl/vcd_ddl_utils.c | 196 +- .../misc/video_core/720p/ddl/vcd_ddl_utils.h | 39 +- .../video_core/720p/ddl/video_core_720p.c | 854 ++--- .../video_core/720p/ddl/video_core_720p.h | 2562 +------------ drivers/misc/video_core/720p/dec/vdec.c | 1281 +++---- drivers/misc/video_core/720p/enc/venc.c | 1388 +++---- .../misc/video_core/720p/enc/venc_internal.c | 1796 +++++---- .../misc/video_core/720p/enc/venc_internal.h | 70 +- .../video_core/720p/init/video_core_init.c | 807 ++-- .../video_core/720p/init/video_core_init.h | 23 +- .../720p/resource_tracker/vcd_res_tracker.c | 170 +- .../720p/resource_tracker/vcd_res_tracker.h | 16 - .../resource_tracker/vcd_res_tracker_api.h | 4 +- .../720p/scheduler/vid_frame_scheduler.c | 996 ++--- .../720p/scheduler/vid_frame_scheduler.h | 172 +- .../720p/scheduler/vid_frame_scheduler_api.c | 394 +- .../720p/scheduler/vid_frame_scheduler_api.h | 88 +- .../scheduler/vid_frame_scheduler_utils.c | 30 +- .../scheduler/vid_frame_scheduler_utils.h | 11 +- drivers/misc/video_core/720p/vcd/vcd.h | 388 +- drivers/misc/video_core/720p/vcd/vcd_api.c | 699 ++-- drivers/misc/video_core/720p/vcd/vcd_api.h | 122 +- .../misc/video_core/720p/vcd/vcd_client_sm.c | 1921 +++++----- .../misc/video_core/720p/vcd/vcd_client_sm.h | 120 +- drivers/misc/video_core/720p/vcd/vcd_core.h | 213 +- .../misc/video_core/720p/vcd/vcd_device_sm.c | 1091 +++--- .../misc/video_core/720p/vcd/vcd_device_sm.h | 84 +- .../misc/video_core/720p/vcd/vcd_power_sm.c | 319 +- .../misc/video_core/720p/vcd/vcd_property.h | 243 +- drivers/misc/video_core/720p/vcd/vcd_status.h | 2 +- drivers/misc/video_core/720p/vcd/vcd_sub.c | 3286 ++++++++--------- drivers/misc/video_core/720p/vcd/vcd_util.c | 103 - drivers/misc/video_core/720p/vcd/vcd_util.h | 36 +- .../video_core/720p/vcd/video_core_type.h | 6 +- include/linux/msm_vidc_dec.h | 40 +- include/linux/msm_vidc_enc.h | 154 +- 51 files changed, 11151 insertions(+), 16494 deletions(-) delete mode 100644 drivers/misc/video_core/720p/vcd/vcd_util.c diff --git a/drivers/misc/video_core/720p/Makefile b/drivers/misc/video_core/720p/Makefile index ce42bd8a6bf8d..77aa694edea00 100644 --- a/drivers/misc/video_core/720p/Makefile +++ b/drivers/misc/video_core/720p/Makefile @@ -28,10 +28,8 @@ video_corereg-objs := ddl/vcd_ddl_firmware.o \ vcd/vcd_client_sm.o \ vcd/vcd_device_sm.o \ vcd/vcd_sub.o \ - vcd/vcd_util.o \ ddl/vcd_ddl_errors.o - obj-$(CONFIG_MSM_VIDEO_CORE_VDEC) += video_decoder.o video_decoder-objs := dec/vdec.o diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl.c b/drivers/misc/video_core/720p/ddl/vcd_ddl.c index bf5ec31347953..22b73621d7381 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl.c +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl.c @@ -20,617 +20,556 @@ #include "vcd_ddl_utils.h" #include "vcd_ddl_metadata.h" -u32 ddl_device_init(struct ddl_init_config_type *p_ddl_init_config, - void *p_client_data) +u32 ddl_device_init(struct ddl_init_config *ddl_init_config, void *client_data) { - struct ddl_context_type *p_ddl_context; + struct ddl_context *ddl_ctxt; u32 status = VCD_S_SUCCESS; - if ((!p_ddl_init_config) || - (!p_ddl_init_config->ddl_callback) || - (!p_ddl_init_config->p_core_virtual_base_addr) - ) { - VIDC_LOGERR_STRING("ddl_dev_init:Bad_argument"); + if (!ddl_init_config || !ddl_init_config->ddl_callback || + !ddl_init_config->core_virtual_base_addr) { + pr_err("ddl_dev_init:Bad_argument\n"); return VCD_ERR_ILLEGAL_PARM; } - p_ddl_context = ddl_get_context(); + ddl_ctxt = ddl_get_context(); - if (DDL_IS_INITIALIZED(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_dev_init:Multiple_init"); + if (DDL_IS_INITIALIZED(ddl_ctxt)) { + pr_err("ddl_dev_init:Multiple_init\n"); return VCD_ERR_ILLEGAL_OP; } - if (DDL_IS_BUSY(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_dev_init:Ddl_busy"); + if (DDL_IS_BUSY(ddl_ctxt)) { + pr_err("ddl_dev_init:Ddl_busy\n"); return VCD_ERR_BUSY; } - DDL_MEMSET(p_ddl_context, 0, sizeof(struct ddl_context_type)); + memset(ddl_ctxt, 0, sizeof(struct ddl_context)); - DDL_BUSY(p_ddl_context); + DDL_BUSY(ddl_ctxt); - p_ddl_context->ddl_callback = p_ddl_init_config->ddl_callback; - p_ddl_context->pf_interrupt_clr = p_ddl_init_config->pf_interrupt_clr; - p_ddl_context->p_core_virtual_base_addr = - p_ddl_init_config->p_core_virtual_base_addr; - p_ddl_context->p_client_data = p_client_data; + ddl_ctxt->ddl_callback = ddl_init_config->ddl_callback; + ddl_ctxt->pf_interrupt_clr = ddl_init_config->pf_interrupt_clr; + ddl_ctxt->core_virtual_base_addr = + ddl_init_config->core_virtual_base_addr; + ddl_ctxt->client_data = client_data; - p_ddl_context->intr_status = DDL_INVALID_INTR_STATUS; + ddl_ctxt->intr_status = DDL_INVALID_INTR_STATUS; - vidc_720p_set_device_virtual_base(p_ddl_context-> - p_core_virtual_base_addr); + vidc_720p_set_device_virtual_base(ddl_ctxt->core_virtual_base_addr); - p_ddl_context->p_current_ddl = NULL; - ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + ddl_ctxt->current_ddl = NULL; + ddl_move_command_state(ddl_ctxt, DDL_CMD_INVALID); ddl_client_transact(DDL_INIT_CLIENTS, NULL); - ddl_pmem_alloc(&p_ddl_context->context_buf_addr, - DDL_CONTEXT_MEMORY, DDL_LINEAR_BUFFER_ALIGN_BYTES); - if (!p_ddl_context->context_buf_addr.p_virtual_base_addr) { - VIDC_LOGERR_STRING("ddl_dev_init:Context_alloc_fail"); + if (!ddl_dma_alloc(&ddl_ctxt->context_buf_addr, DDL_CONTEXT_MEMORY, npelly_context)) { + pr_err("ddl_dev_init:Context_alloc_fail\n"); status = VCD_ERR_ALLOC_FAIL; + goto out; } - if (!status) { - ddl_pmem_alloc(&p_ddl_context->db_line_buffer, - DDL_DB_LINE_BUF_SIZE, - DDL_TILE_BUFFER_ALIGN_BYTES); - if (!p_ddl_context->db_line_buffer.p_virtual_base_addr) { - VIDC_LOGERR_STRING("ddl_dev_init:Line_buf_alloc_fail"); - status = VCD_ERR_ALLOC_FAIL; - } + if (!ddl_dma_alloc(&ddl_ctxt->db_line_buffer, DDL_DB_LINE_BUF_SIZE, npelly_dbl)) { + pr_err("ddl_dev_init:Line_buf_alloc_fail\n"); + status = VCD_ERR_ALLOC_FAIL; + goto out; } - - if (!status) { - ddl_pmem_alloc(&p_ddl_context->data_partition_tempbuf, - DDL_MPEG4_DATA_PARTITION_BUF_SIZE, - DDL_TILE_BUFFER_ALIGN_BYTES); - if (p_ddl_context->data_partition_tempbuf.p_virtual_base_addr \ - == NULL) { - VIDC_LOGERR_STRING - ("ddl_dev_init:Data_partition_buf_alloc_fail"); - status = VCD_ERR_ALLOC_FAIL; - } - } - - if (!status) { - - ddl_pmem_alloc(&p_ddl_context->metadata_shared_input, - DDL_METADATA_TOTAL_INPUTBUFSIZE, - DDL_LINEAR_BUFFER_ALIGN_BYTES); - if (!p_ddl_context->metadata_shared_input.p_virtual_base_addr) { - VIDC_LOGERR_STRING - ("ddl_dev_init:metadata_shared_input_alloc_fail"); - status = VCD_ERR_ALLOC_FAIL; - } - } - - if (!status) { - ddl_pmem_alloc(&p_ddl_context->dbg_core_dump, \ - DDL_DBG_CORE_DUMP_SIZE, \ - DDL_LINEAR_BUFFER_ALIGN_BYTES); - if (!p_ddl_context->dbg_core_dump.p_virtual_base_addr) { - VIDC_LOGERR_STRING - ("ddl_dev_init:dbg_core_dump_alloc_failed"); - status = VCD_ERR_ALLOC_FAIL; - } - p_ddl_context->enable_dbg_core_dump = 0; + if (!ddl_dma_alloc(&ddl_ctxt->data_partition_tempbuf, + DDL_MPEG4_DATA_PARTITION_BUF_SIZE, npelly_mpeg4)) { + pr_err("ddl_dev_init:" + "Data_partition_buf_alloc_fail\n"); + status = VCD_ERR_ALLOC_FAIL; + goto out; } - - if (!status && !vcd_fw_init()) { - VIDC_LOGERR_STRING("ddl_dev_init:fw_init_failed"); + if (!ddl_dma_alloc(&ddl_ctxt->metadata_shared_input, + DDL_METADATA_TOTAL_INPUTBUFSIZE, npelly_meta)) { + pr_err("ddl_dev_init:" + "metadata_shared_input_alloc_fail\n"); + status = VCD_ERR_ALLOC_FAIL; + goto out; + } + if (!ddl_dma_alloc(&ddl_ctxt->dbg_core_dump, DDL_DBG_CORE_DUMP_SIZE, npelly_debug)) { + pr_err("ddl_dev_init:" + "dbg_core_dump_alloc_failed\n"); status = VCD_ERR_ALLOC_FAIL; + ddl_ctxt->enable_dbg_core_dump = 0; + goto out; } + +out: if (status) { - ddl_release_context_buffers(p_ddl_context); - DDL_IDLE(p_ddl_context); + ddl_release_context_buffers(ddl_ctxt); + DDL_IDLE(ddl_ctxt); return status; } - ddl_move_command_state(p_ddl_context, DDL_CMD_DMA_INIT); + ddl_move_command_state(ddl_ctxt, DDL_CMD_DMA_INIT); - ddl_core_init(p_ddl_context); + ddl_core_init(ddl_ctxt); return status; } -u32 ddl_device_release(void *p_client_data) +u32 ddl_device_release(void *client_data) { - struct ddl_context_type *p_ddl_context; + struct ddl_context *ddl_ctxt; - p_ddl_context = ddl_get_context(); + ddl_ctxt = ddl_get_context(); - if (DDL_IS_BUSY(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_dev_rel:Ddl_busy"); + if (DDL_IS_BUSY(ddl_ctxt)) { + pr_err("ddl_dev_rel:Ddl_busy\n"); return VCD_ERR_BUSY; } - if (!DDL_IS_INITIALIZED(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_dev_rel:Not_inited"); + if (!DDL_IS_INITIALIZED(ddl_ctxt)) { + pr_err("ddl_dev_rel:Not_inited\n"); return VCD_ERR_ILLEGAL_OP; } if (!ddl_client_transact(DDL_ACTIVE_CLIENT, NULL)) { - VIDC_LOGERR_STRING("ddl_dev_rel:Client_present_err"); + pr_err("ddl_dev_rel:Client_present_err\n"); return VCD_ERR_CLIENT_PRESENT; } - DDL_BUSY(p_ddl_context); + DDL_BUSY(ddl_ctxt); - p_ddl_context->n_device_state = DDL_DEVICE_NOTINIT; - p_ddl_context->p_client_data = p_client_data; - ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + ddl_ctxt->device_state = DDL_DEVICE_NOTINIT; + ddl_ctxt->client_data = client_data; + ddl_move_command_state(ddl_ctxt, DDL_CMD_INVALID); vidc_720p_stop_fw(); - VIDC_LOG_STRING("FW_ENDDONE"); - ddl_release_context_buffers(p_ddl_context); + pr_debug("FW_ENDDONE\n"); + ddl_release_context_buffers(ddl_ctxt); - DDL_IDLE(p_ddl_context); + DDL_IDLE(ddl_ctxt); return VCD_S_SUCCESS; } -u32 ddl_open(u32 **p_ddl_handle, u32 b_decoding) +u32 ddl_open(u32 **ddl_handle, u32 decoding) { - struct ddl_context_type *p_ddl_context; - struct ddl_client_context_type *p_ddl; + struct ddl_context *ddl_context; + struct ddl_client_context *ddl; u32 status; - if (!p_ddl_handle) { - VIDC_LOGERR_STRING("ddl_open:Bad_handle"); + if (!ddl_handle) { + pr_err("ddl_open:Bad_handle\n"); return VCD_ERR_BAD_HANDLE; } - p_ddl_context = ddl_get_context(); + ddl_context = ddl_get_context(); - if (!DDL_IS_INITIALIZED(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_open:Not_inited"); + if (!DDL_IS_INITIALIZED(ddl_context)) { + pr_err("ddl_open:Not_inited\n"); return VCD_ERR_ILLEGAL_OP; } - status = ddl_client_transact(DDL_GET_CLIENT, &p_ddl); + status = ddl_client_transact(DDL_GET_CLIENT, &ddl); if (status) { - VIDC_LOGERR_STRING("ddl_open:Client_trasac_failed"); + pr_err("ddl_open:Client_trasac_failed\n"); return status; } - ddl_move_client_state(p_ddl, DDL_CLIENT_OPEN); + ddl_move_client_state(ddl, DDL_CLIENT_OPEN); - p_ddl->codec_data.hdr.b_decoding = b_decoding; - p_ddl->b_decoding = b_decoding; + ddl->codec_data.hdr.decoding = decoding; + ddl->decoding = decoding; - ddl_set_default_meta_data_hdr(p_ddl); + ddl_set_default_meta_data_hdr(ddl); - ddl_set_initial_default_values(p_ddl); + ddl_set_initial_default_values(ddl); - *p_ddl_handle = (u32 *) p_ddl; + *ddl_handle = (u32 *) ddl; return VCD_S_SUCCESS; } -u32 ddl_close(u32 **p_ddl_handle) +u32 ddl_close(u32 **ddl_handle) { - struct ddl_context_type *p_ddl_context; - struct ddl_client_context_type **pp_ddl = - (struct ddl_client_context_type **)p_ddl_handle; + struct ddl_context *ddl_context; + struct ddl_client_context **pp_ddl = (struct ddl_client_context **) + ddl_handle; if (!pp_ddl || !*pp_ddl) { - VIDC_LOGERR_STRING("ddl_close:Bad_handle"); + pr_err("ddl_close:Bad_handle\n"); return VCD_ERR_BAD_HANDLE; } - p_ddl_context = ddl_get_context(); + ddl_context = ddl_get_context(); - if (!DDL_IS_INITIALIZED(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_close:Not_inited"); + if (!DDL_IS_INITIALIZED(ddl_context)) { + pr_err("ddl_close:Not_inited\n"); return VCD_ERR_ILLEGAL_OP; } if (!DDLCLIENT_STATE_IS(*pp_ddl, DDL_CLIENT_OPEN)) { - VIDC_LOGERR_STRING("ddl_close:Not_in_open_state"); + pr_err("ddl_close:Not_in_open_state\n"); return VCD_ERR_ILLEGAL_OP; } ddl_move_client_state(*pp_ddl, DDL_CLIENT_INVALID); - if ((*pp_ddl)->b_decoding) { - vcd_fw_transact(FALSE, TRUE, - (*pp_ddl)->codec_data.decoder.codec_type.e_codec); - } else { - vcd_fw_transact(FALSE, FALSE, - (*pp_ddl)->codec_data.encoder.codec_type.e_codec); - } + ddl_client_transact(DDL_FREE_CLIENT, pp_ddl); return VCD_S_SUCCESS; } -u32 ddl_encode_start(u32 *ddl_handle, void *p_client_data) +u32 ddl_encode_start(u32 *ddl_handle, void *client_data) { - struct ddl_client_context_type *p_ddl = - (struct ddl_client_context_type *)ddl_handle; - struct ddl_context_type *p_ddl_context; - struct ddl_encoder_data_type *p_encoder; - u32 n_dpb_size; + struct ddl_client_context *ddl = + (struct ddl_client_context *)ddl_handle; + struct ddl_context *ddl_context; + struct ddl_encoder_data *enc; + u32 dpb_size; - p_ddl_context = ddl_get_context(); + ddl_context = ddl_get_context(); - if (!DDL_IS_INITIALIZED(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_enc_start:Not_inited"); + if (!DDL_IS_INITIALIZED(ddl_context)) { + pr_err("ddl_enc_start:Not_inited\n"); return VCD_ERR_ILLEGAL_OP; } - if (DDL_IS_BUSY(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_enc_start:Ddl_busy"); + if (DDL_IS_BUSY(ddl_context)) { + pr_err("ddl_enc_start:Ddl_busy\n"); return VCD_ERR_BUSY; } - if (!p_ddl || p_ddl->b_decoding) { - VIDC_LOGERR_STRING("ddl_enc_start:Bad_handle"); + if (!ddl || ddl->decoding) { + pr_err("ddl_enc_start:Bad_handle\n"); return VCD_ERR_BAD_HANDLE; } - if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_OPEN)) { - VIDC_LOGERR_STRING("ddl_enc_start:Not_opened"); + if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) { + pr_err("ddl_enc_start:Not_opened\n"); return VCD_ERR_ILLEGAL_OP; } - if (!ddl_encoder_ready_to_start(p_ddl)) { - VIDC_LOGERR_STRING("ddl_enc_start:Err_param_settings"); + if (!ddl_encoder_ready_to_start(ddl)) { + pr_err("ddl_enc_start:Err_param_settings\n"); return VCD_ERR_ILLEGAL_OP; } - p_encoder = &p_ddl->codec_data.encoder; + enc = &ddl->codec_data.encoder; - n_dpb_size = ddl_get_yuv_buffer_size(&p_encoder->frame_size, - &p_encoder->re_con_buf_format, FALSE); + dpb_size = ddl_get_yuv_buffer_size(&enc->frame_size, + &enc->re_con_buf_format, false); - n_dpb_size *= DDL_ENC_NUM_DPB_BUFFERS; - ddl_pmem_alloc(&p_encoder->enc_dpb_addr, - n_dpb_size, DDL_TILE_BUFFER_ALIGN_BYTES); - if (!p_encoder->enc_dpb_addr.p_virtual_base_addr) { - VIDC_LOGERR_STRING("ddl_enc_start:Dpb_alloc_failed"); + dpb_size *= DDL_ENC_NUM_DPB_BUFFERS; + if (!ddl_dma_alloc(&enc->enc_dpb_addr, dpb_size, npelly_enc_dpb)) { + pr_err("ddl_enc_start:Dpb_alloc_failed\n"); return VCD_ERR_ALLOC_FAIL; } - if ((p_encoder->codec_type.e_codec == VCD_CODEC_MPEG4 && - !p_encoder->short_header.b_short_header) || - p_encoder->codec_type.e_codec == VCD_CODEC_H264) { - ddl_pmem_alloc(&p_encoder->seq_header, - DDL_ENC_SEQHEADER_SIZE, - DDL_LINEAR_BUFFER_ALIGN_BYTES); - if (!p_encoder->seq_header.p_virtual_base_addr) { - ddl_pmem_free(p_encoder->enc_dpb_addr); - VIDC_LOGERR_STRING - ("ddl_enc_start:Seq_hdr_alloc_failed"); + if ((enc->codec_type.codec == VCD_CODEC_MPEG4 && + !enc->short_header.short_header) || + enc->codec_type.codec == VCD_CODEC_H264) { + if (!ddl_dma_alloc(&enc->seq_header, DDL_ENC_SEQHEADER_SIZE, npelly_enc_seq)) { + ddl_dma_free(&enc->enc_dpb_addr); + pr_err("ddl_enc_start:Seq_hdr_alloc_failed\n"); return VCD_ERR_ALLOC_FAIL; } } else { - p_encoder->seq_header.n_buffer_size = 0; - p_encoder->seq_header.p_virtual_base_addr = 0; + enc->seq_header.size = 0; + enc->seq_header.virt_addr = NULL; } - DDL_BUSY(p_ddl_context); + DDL_BUSY(ddl_context); - p_ddl_context->p_current_ddl = p_ddl; - p_ddl_context->p_client_data = p_client_data; - ddl_channel_set(p_ddl); + ddl_context->current_ddl = ddl; + ddl_context->client_data = client_data; + ddl_channel_set(ddl); return VCD_S_SUCCESS; } -u32 ddl_decode_start(u32 *ddl_handle, - struct vcd_sequence_hdr_type *p_header, void *p_client_data) +u32 ddl_decode_start(u32 *ddl_handle, struct vcd_phys_sequence_hdr *hdr, + void *client_data) { - struct ddl_client_context_type *p_ddl = - (struct ddl_client_context_type *)ddl_handle; - struct ddl_context_type *p_ddl_context; - struct ddl_decoder_data_type *p_decoder; + struct ddl_client_context *ddl = (struct ddl_client_context *) + ddl_handle; + struct ddl_context *ddl_context; + struct ddl_decoder_data *decoder; - p_ddl_context = ddl_get_context(); + ddl_context = ddl_get_context(); - if (!DDL_IS_INITIALIZED(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_dec_start:Not_inited"); + if (!DDL_IS_INITIALIZED(ddl_context)) { + pr_err("ddl_dec_start:Not_inited\n"); return VCD_ERR_ILLEGAL_OP; } - if (DDL_IS_BUSY(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_dec_start:Ddl_busy"); + if (DDL_IS_BUSY(ddl_context)) { + pr_err("ddl_dec_start:Ddl_busy\n"); return VCD_ERR_BUSY; } - if (!p_ddl || !p_ddl->b_decoding) { - VIDC_LOGERR_STRING("ddl_dec_start:Bad_handle"); + if (!ddl || !ddl->decoding) { + pr_err("ddl_dec_start:Bad_handle\n"); return VCD_ERR_BAD_HANDLE; } - if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_OPEN)) { - VIDC_LOGERR_STRING("ddl_dec_start:Not_in_opened_state"); + if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) { + pr_err("ddl_dec_start:Not_in_opened_state\n"); return VCD_ERR_ILLEGAL_OP; } - if ((p_header) && - ((!p_header->n_sequence_header_len) || - (!p_header->p_sequence_header) - ) - ) { - VIDC_LOGERR_STRING("ddl_dec_start:Bad_param_seq_header"); + if (hdr && (!hdr->sz || !hdr->addr)) { + pr_err("ddl_dec_start:Bad_param_seq_header\n"); return VCD_ERR_ILLEGAL_PARM; } - if (!ddl_decoder_ready_to_start(p_ddl, p_header)) { - VIDC_LOGERR_STRING("ddl_dec_start:Err_param_settings"); + if (!ddl_decoder_ready_to_start(ddl, hdr)) { + pr_err("ddl_dec_start:Err_param_settings\n"); return VCD_ERR_ILLEGAL_OP; } - DDL_BUSY(p_ddl_context); + DDL_BUSY(ddl_context); - p_decoder = &p_ddl->codec_data.decoder; - if (p_header) { - p_decoder->b_header_in_start = TRUE; - p_decoder->decode_config = *p_header; + decoder = &ddl->codec_data.decoder; + if (hdr) { + decoder->header_in_start = true; + decoder->decode_config = *hdr; } else { - p_decoder->b_header_in_start = FALSE; - p_decoder->decode_config.n_sequence_header_len = 0; + decoder->header_in_start = false; + decoder->decode_config.sz = 0; } - if (p_decoder->codec_type.e_codec == VCD_CODEC_H264) { - ddl_pmem_alloc(&p_decoder->h264Vsp_temp_buffer, + if (decoder->codec_type.codec == VCD_CODEC_H264) { + if (!ddl_dma_alloc(&decoder->h264Vsp_temp_buffer, DDL_DECODE_H264_VSPTEMP_BUFSIZE, - DDL_LINEAR_BUFFER_ALIGN_BYTES); - if (!p_decoder->h264Vsp_temp_buffer.p_virtual_base_addr) { - DDL_IDLE(p_ddl_context); - VIDC_LOGERR_STRING - ("ddl_dec_start:H264Sps_alloc_failed"); + npelly_dec_h264)) { + DDL_IDLE(ddl_context); + pr_err("ddl_dec_start:H264Sps_alloc_failed\n"); return VCD_ERR_ALLOC_FAIL; } } - p_ddl_context->p_current_ddl = p_ddl; - p_ddl_context->p_client_data = p_client_data; + ddl_context->current_ddl = ddl; + ddl_context->client_data = client_data; - ddl_channel_set(p_ddl); + ddl_channel_set(ddl); return VCD_S_SUCCESS; } -u32 ddl_decode_frame(u32 *ddl_handle, - struct ddl_frame_data_type_tag *p_input_bits, void *p_client_data) +u32 ddl_decode_frame(u32 *ddl_handle, struct ddl_frame_data_tag *in_bits, + void *client_data) { u32 vcd_status = VCD_S_SUCCESS; - struct ddl_client_context_type *p_ddl = - (struct ddl_client_context_type *)ddl_handle; - struct ddl_context_type *p_ddl_context = ddl_get_context(); + struct ddl_client_context *ddl = (struct ddl_client_context *) + ddl_handle; + struct ddl_context *ddl_context = ddl_get_context(); #ifdef CORE_TIMING_INFO ddl_get_core_start_time(0); #endif - if (!DDL_IS_INITIALIZED(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_dec_frame:Not_inited"); + if (!DDL_IS_INITIALIZED(ddl_context)) { + pr_err("ddl_dec_frame:Not_inited\n"); return VCD_ERR_ILLEGAL_OP; } - if (DDL_IS_BUSY(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_dec_frame:Ddl_busy"); + if (DDL_IS_BUSY(ddl_context)) { + pr_err("ddl_dec_frame:Ddl_busy\n"); return VCD_ERR_BUSY; } - if (!p_ddl || !p_ddl->b_decoding) { - VIDC_LOGERR_STRING("ddl_dec_frame:Bad_handle"); + if (!ddl || !ddl->decoding) { + pr_err("ddl_dec_frame:Bad_handle\n"); return VCD_ERR_BAD_HANDLE; } - if (!p_input_bits || - ((!p_input_bits->vcd_frm.p_physical || - !p_input_bits->vcd_frm.n_data_len) && - (!(VCD_FRAME_FLAG_EOS & p_input_bits->vcd_frm.n_flags)) - ) - ) { - VIDC_LOGERR_STRING("ddl_dec_frame:Bad_input_param"); + if (!in_bits || ((!in_bits->vcd_frm.phys_addr || + !in_bits->vcd_frm.data_len) && + !(VCD_FRAME_FLAG_EOS & in_bits->vcd_frm.flags))) { + pr_err("ddl_dec_frame:Bad_input_param\n"); return VCD_ERR_ILLEGAL_PARM; } - DDL_BUSY(p_ddl_context); + DDL_BUSY(ddl_context); - p_ddl_context->p_current_ddl = p_ddl; - p_ddl_context->p_client_data = p_client_data; + ddl_context->current_ddl = ddl; + ddl_context->client_data = client_data; - p_ddl->input_frame = *p_input_bits; + ddl->input_frame = *in_bits; - if (DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME)) { - ddl_decode_frame_run(p_ddl); + if (DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME)) { + ddl_decode_frame_run(ddl); } else { - if (!p_ddl->codec_data.decoder.dp_buf.n_no_of_dec_pic_buf) { - VIDC_LOGERR_STRING("ddl_dec_frame:Dpbs_requied"); + if (!ddl->codec_data.decoder.dp_buf.no_of_dec_pic_buf) { + pr_err("ddl_dec_frame:Dpbs_requied\n"); vcd_status = VCD_ERR_ILLEGAL_OP; - } else if (DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_DPB)) { - vcd_status = ddl_decode_set_buffers(p_ddl); - } else - if (DDLCLIENT_STATE_IS - (p_ddl, DDL_CLIENT_WAIT_FOR_INITCODEC)) { - p_ddl->codec_data.decoder.decode_config. - p_sequence_header = - p_ddl->input_frame.vcd_frm.p_physical; - p_ddl->codec_data.decoder.decode_config. - n_sequence_header_len = - p_ddl->input_frame.vcd_frm.n_data_len; - ddl_decode_init_codec(p_ddl); + } else if (DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPB)) { + vcd_status = ddl_decode_set_buffers(ddl); + } else if (DDLCLIENT_STATE_IS(ddl, + DDL_CLIENT_WAIT_FOR_INITCODEC)) { + ddl->codec_data.decoder.decode_config.addr = + ddl->input_frame.vcd_frm.phys_addr; + ddl->codec_data.decoder.decode_config.sz = + ddl->input_frame.vcd_frm.data_len; + ddl_decode_init_codec(ddl); } else { - VIDC_LOGERR_STRING("Dec_frame:Wrong_state"); + pr_err("Dec_frame:Wrong_state\n"); vcd_status = VCD_ERR_ILLEGAL_OP; } if (vcd_status) - DDL_IDLE(p_ddl_context); + DDL_IDLE(ddl_context); } return vcd_status; } -u32 ddl_encode_frame(u32 *ddl_handle, - struct ddl_frame_data_type_tag *p_input_frame, - struct ddl_frame_data_type_tag *p_output_bit, void *p_client_data) +u32 ddl_encode_frame(u32 *ddl_handle, struct ddl_frame_data_tag *input_frame, + struct ddl_frame_data_tag *out_bits, void *client_data) { - struct ddl_client_context_type *p_ddl = - (struct ddl_client_context_type *)ddl_handle; - struct ddl_context_type *p_ddl_context = ddl_get_context(); + struct ddl_client_context *ddl = (struct ddl_client_context *) + ddl_handle; + struct ddl_context *ddl_context = ddl_get_context(); #ifdef CORE_TIMING_INFO ddl_get_core_start_time(1); #endif - if (!DDL_IS_INITIALIZED(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_enc_frame:Not_inited"); + if (!DDL_IS_INITIALIZED(ddl_context)) { + pr_err("ddl_encode_frame:Not_inited\n"); return VCD_ERR_ILLEGAL_OP; } - if (DDL_IS_BUSY(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_enc_frame:Ddl_busy"); + if (DDL_IS_BUSY(ddl_context)) { + pr_err("ddl_encode_frame:Ddl_busy\n"); return VCD_ERR_BUSY; } - if (!p_ddl || p_ddl->b_decoding) { - VIDC_LOGERR_STRING("ddl_enc_frame:Bad_handle"); + if (!ddl || ddl->decoding) { + pr_err("ddl_encode_frame:Bad_handle\n"); return VCD_ERR_BAD_HANDLE; } - if (!p_input_frame || - !p_input_frame->vcd_frm.p_physical || - p_ddl->codec_data.encoder.input_buf_req.n_size != - p_input_frame->vcd_frm.n_data_len) { - VIDC_LOGERR_STRING("ddl_enc_frame:Bad_input_params"); + if (!input_frame || !input_frame->vcd_frm.phys_addr || + ddl->codec_data.encoder.input_buf_req.size != + input_frame->vcd_frm.data_len) { + pr_err("ddl_encode_frame:Bad_input_params\n"); return VCD_ERR_ILLEGAL_PARM; } - if ((((u32) p_input_frame->vcd_frm.p_physical + - p_input_frame->vcd_frm.n_offset) & - (DDL_STREAMBUF_ALIGN_GUARD_BYTES) - ) - ) { - VIDC_LOGERR_STRING - ("ddl_enc_frame:Un_aligned_yuv_start_address"); + if ((input_frame->vcd_frm.phys_addr + input_frame->vcd_frm.offset) & + DDL_STREAMBUF_ALIGN_GUARD_BYTES) { + pr_err("ddl_encode_frame:unaligned_yuv_start_addr\n"); return VCD_ERR_ILLEGAL_PARM; } - if (!p_output_bit || - !p_output_bit->vcd_frm.p_physical || - !p_output_bit->vcd_frm.n_alloc_len) { - VIDC_LOGERR_STRING("ddl_enc_frame:Bad_output_params"); + if (!out_bits || !out_bits->vcd_frm.phys_addr || + !out_bits->vcd_frm.alloc_len) { + pr_err("ddl_encode_frame:Bad_output_params\n"); return VCD_ERR_ILLEGAL_PARM; } - if ((p_ddl->codec_data.encoder.output_buf_req.n_size + - p_output_bit->vcd_frm.n_offset) > - p_output_bit->vcd_frm.n_alloc_len) { - VIDC_LOGERR_STRING - ("ddl_enc_frame:n_offset_large, Exceeds_min_buf_size"); + if ((ddl->codec_data.encoder.output_buf_req.size + + out_bits->vcd_frm.offset) > + out_bits->vcd_frm.alloc_len) { + pr_err("ddl_encode_frame:offset > min_buf_size\n"); } - if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME)) { - VIDC_LOGERR_STRING("ddl_enc_frame:Wrong_state"); + if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME)) { + pr_err("ddl_encode_frame:Wrong_state\n"); return VCD_ERR_ILLEGAL_OP; } - DDL_BUSY(p_ddl_context); + DDL_BUSY(ddl_context); - p_ddl_context->p_current_ddl = p_ddl; - p_ddl_context->p_client_data = p_client_data; + ddl_context->current_ddl = ddl; + ddl_context->client_data = client_data; - p_ddl->input_frame = *p_input_frame; - p_ddl->output_frame = *p_output_bit; + ddl->input_frame = *input_frame; + ddl->output_frame = *out_bits; - ddl_encode_frame_run(p_ddl); + ddl_encode_frame_run(ddl); return VCD_S_SUCCESS; } -u32 ddl_decode_end(u32 *ddl_handle, void *p_client_data) +u32 ddl_decode_end(u32 *ddl_handle, void *client_data) { - struct ddl_client_context_type *p_ddl = - (struct ddl_client_context_type *)ddl_handle; - struct ddl_context_type *p_ddl_context; + struct ddl_client_context *ddl = (struct ddl_client_context *) + ddl_handle; + struct ddl_context *ddl_context; - p_ddl_context = ddl_get_context(); + ddl_context = ddl_get_context(); #ifdef CORE_TIMING_INFO ddl_reset_time_variables(0); #endif - if (!DDL_IS_INITIALIZED(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_dec_end:Not_inited"); + if (!DDL_IS_INITIALIZED(ddl_context)) { + pr_err("ddl_dec_end:Not_inited\n"); return VCD_ERR_ILLEGAL_OP; } - if (DDL_IS_BUSY(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_dec_end:Ddl_busy"); + if (DDL_IS_BUSY(ddl_context)) { + pr_err("ddl_dec_end:Ddl_busy\n"); return VCD_ERR_BUSY; } - if (!p_ddl || !p_ddl->b_decoding) { - VIDC_LOGERR_STRING("ddl_dec_end:Bad_handle"); + if (!ddl || !ddl->decoding) { + pr_err("ddl_dec_end:Bad_handle\n"); return VCD_ERR_BAD_HANDLE; } - if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME) && - !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_INITCODEC) && - !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_DPB) && - !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_FATAL_ERROR) - ) { - VIDC_LOGERR_STRING("ddl_dec_end:Wrong_state"); + if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME) && + !DDLCLIENT_STATE_IS(ddl, + DDL_CLIENT_WAIT_FOR_INITCODEC) && + !DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPB) && + !DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_FATAL_ERROR)) { + pr_err("ddl_dec_end:Wrong_state\n"); return VCD_ERR_ILLEGAL_OP; } - DDL_BUSY(p_ddl_context); + DDL_BUSY(ddl_context); - p_ddl_context->p_current_ddl = p_ddl; - p_ddl_context->p_client_data = p_client_data; + ddl_context->current_ddl = ddl; + ddl_context->client_data = client_data; - ddl_channel_end(p_ddl); + ddl_channel_end(ddl); return VCD_S_SUCCESS; } -u32 ddl_encode_end(u32 *ddl_handle, void *p_client_data) +u32 ddl_encode_end(u32 *ddl_handle, void *client_data) { - struct ddl_client_context_type *p_ddl = - (struct ddl_client_context_type *)ddl_handle; - struct ddl_context_type *p_ddl_context; + struct ddl_client_context *ddl = (struct ddl_client_context *) + ddl_handle; + struct ddl_context *ddl_context; - p_ddl_context = ddl_get_context(); + ddl_context = ddl_get_context(); #ifdef CORE_TIMING_INFO ddl_reset_time_variables(1); #endif - if (!DDL_IS_INITIALIZED(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_enc_end:Not_inited"); + if (!DDL_IS_INITIALIZED(ddl_context)) { + pr_err("ddl_enc_end:Not_inited\n"); return VCD_ERR_ILLEGAL_OP; } - if (DDL_IS_BUSY(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_enc_end:Ddl_busy"); + if (DDL_IS_BUSY(ddl_context)) { + pr_err("ddl_enc_end:Ddl_busy\n"); return VCD_ERR_BUSY; } - if (!p_ddl || p_ddl->b_decoding) { - VIDC_LOGERR_STRING("ddl_enc_end:Bad_handle"); + if (!ddl || ddl->decoding) { + pr_err("ddl_enc_end:Bad_handle\n"); return VCD_ERR_BAD_HANDLE; } - if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME) && - !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_INITCODEC) && - !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_FATAL_ERROR)) { - VIDC_LOGERR_STRING("ddl_enc_end:Wrong_state"); + if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME) && + !DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_INITCODEC) && + !DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_FATAL_ERROR)) { + pr_err("ddl_enc_end:Wrong_state\n"); return VCD_ERR_ILLEGAL_OP; } - DDL_BUSY(p_ddl_context); + DDL_BUSY(ddl_context); - p_ddl_context->p_current_ddl = p_ddl; - p_ddl_context->p_client_data = p_client_data; + ddl_context->current_ddl = ddl; + ddl_context->client_data = client_data; - ddl_channel_end(p_ddl); + ddl_channel_end(ddl); return VCD_S_SUCCESS; } -u32 ddl_reset_hw(u32 n_mode) +u32 ddl_reset_hw(u32 mode) { - struct ddl_context_type *p_ddl_context; - struct ddl_client_context_type *p_ddl; - int i_client_num; + struct ddl_context *ddl_context; + struct ddl_client_context *ddl; + int client_num; - VIDC_LOG_STRING("ddl_reset_hw:called"); - p_ddl_context = ddl_get_context(); - ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); - DDL_BUSY(p_ddl_context); + pr_debug("ddl_reset_hw:called\n"); + ddl_context = ddl_get_context(); + ddl_move_command_state(ddl_context, DDL_CMD_INVALID); + DDL_BUSY(ddl_context); - if (p_ddl_context->p_core_virtual_base_addr) + if (ddl_context->core_virtual_base_addr) vidc_720p_do_sw_reset(); - p_ddl_context->n_device_state = DDL_DEVICE_NOTINIT; - for (i_client_num = 0; i_client_num < VCD_MAX_NO_CLIENT; - ++i_client_num) { - p_ddl = p_ddl_context->a_ddl_clients[i_client_num]; - p_ddl_context->a_ddl_clients[i_client_num] = NULL; - if (p_ddl) { - ddl_release_client_internal_buffers(p_ddl); - ddl_client_transact(DDL_FREE_CLIENT, &p_ddl); + ddl_context->device_state = DDL_DEVICE_NOTINIT; + for (client_num = 0; client_num < VCD_MAX_NO_CLIENT; ++client_num) { + ddl = ddl_context->ddl_clients[client_num]; + ddl_context->ddl_clients[client_num] = NULL; + if (ddl) { + ddl_release_client_internal_buffers(ddl); + ddl_client_transact(DDL_FREE_CLIENT, &ddl); } } - ddl_release_context_buffers(p_ddl_context); - DDL_MEMSET(p_ddl_context, 0, sizeof(struct ddl_context_type)); + ddl_release_context_buffers(ddl_context); + memset(ddl_context, 0, sizeof(struct ddl_context)); - VIDC_LOG_BUFFER_INIT; - return TRUE; + return true; } diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl.h b/drivers/misc/video_core/720p/ddl/vcd_ddl.h index 21e96dc2f9ea3..8f4e92438c53f 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl.h +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl.h @@ -33,32 +33,29 @@ #include "vcd_ddl_firmware.h" #include "video_core_720p.h" -#undef DDL_INLINE -#define DDL_INLINE - #define DDL_BUSY_STATE 1 #define DDL_IDLE_STATE 0 #define DDL_ERROR_STATE 2 -#define DDL_IS_BUSY(p_ddl_context) \ - (((p_ddl_context)->n_ddl_busy != DDL_IDLE_STATE)) -#define DDL_BUSY(p_ddl_context) \ - ((p_ddl_context)->n_ddl_busy = DDL_BUSY_STATE) -#define DDL_IDLE(p_ddl_context) \ - ((p_ddl_context)->n_ddl_busy = DDL_IDLE_STATE) -#define DDL_ERROR(p_ddl_context) \ - ((p_ddl_context)->n_ddl_busy = DDL_ERROR_STATE) +#define DDL_IS_BUSY(ddl_context) \ + (((ddl_context)->ddl_busy != DDL_IDLE_STATE)) +#define DDL_BUSY(ddl_context) \ + ((ddl_context)->ddl_busy = DDL_BUSY_STATE) +#define DDL_IDLE(ddl_context) \ + ((ddl_context)->ddl_busy = DDL_IDLE_STATE) +#define DDL_ERROR(ddl_context) \ + ((ddl_context)->ddl_busy = DDL_ERROR_STATE) #define DDL_DEVICE_NOTINIT 0 #define DDL_DEVICE_INITED 1 #define DDL_DEVICE_HWFATAL 2 -#define DDL_IS_INITIALIZED(p_ddl_context) \ -(p_ddl_context->n_device_state == DDL_DEVICE_INITED) +#define DDL_IS_INITIALIZED(ddl_context) \ + (ddl_context->device_state == DDL_DEVICE_INITED) -#define DDLCOMMAND_STATE_IS(p_ddl_context, command_state) \ -(command_state == (p_ddl_context)->e_cmd_state) +#define DDLCOMMAND_STATE_IS(ddl_context, command_state) \ + (command_state == (ddl_context)->cmd_state) -#define DDLCLIENT_STATE_IS(p_ddl, client_state) \ -(client_state == (p_ddl)->e_client_state) +#define DDLCLIENT_STATE_IS(ddl, cs) \ + (cs == (ddl)->client_state) #define DDL_DPB_OP_INIT 1 #define DDL_DPB_OP_MARK_FREE 2 @@ -82,16 +79,13 @@ #define DDL_DEC_REQ_OUTPUT_FLUSH 0x1 -struct ddl_buf_addr_type { - u32 *p_physical_base_addr; - u32 *p_virtual_base_addr; - u32 *p_align_physical_addr; - u32 *p_align_virtual_addr; - u32 n_buffer_size; - u32 n_buffer_size_guard; +struct ddl_dma_buffer { + void *virt_addr; + phys_addr_t phys_addr; + size_t size; }; -enum ddl_cmd_state_type { +enum ddl_cmd_state { DDL_CMD_INVALID = 0x0, DDL_CMD_DMA_INIT = 0x1, DDL_CMD_CPU_RESET = 0x2, @@ -106,7 +100,7 @@ enum ddl_cmd_state_type { DDL_CMD_32BIT = 0x7FFFFFFF }; -enum ddl_client_state_type { +enum ddl_client_state { DDL_CLIENT_INVALID = 0x0, DDL_CLIENT_OPEN = 0x1, DDL_CLIENT_WAIT_FOR_CHDONE = 0x2, @@ -122,175 +116,171 @@ enum ddl_client_state_type { DDL_CLIENT_32BIT = 0x7FFFFFFF }; -struct ddl_mask_type { - u32 n_client_mask; - u32 n_hw_mask; +struct ddl_mask { + u32 client_mask; + u32 hw_mask; }; -struct ddl_context_type; +struct ddl_context; -struct ddl_client_context_type; +struct ddl_client_context; -struct ddl_codec_data_hdr_type { - u32 b_decoding; +struct ddl_codec_data_hdr { + u32 decoding; }; -struct ddl_encoder_data_type { - struct ddl_codec_data_hdr_type hdr; - struct vcd_property_codec_type codec_type; - struct vcd_property_frame_size_type frame_size; - struct vcd_property_frame_rate_type frame_rate; - struct vcd_property_target_bitrate_type target_bit_rate; - struct vcd_property_profile_type profile; - struct vcd_property_level_type level; - struct vcd_property_rate_control_type rc_type; - struct vcd_property_multi_slice_type multi_slice; - u32 n_meta_data_enable_flag; - u32 n_suffix; - struct ddl_buf_addr_type meta_data_input; - u32 n_meta_data_offset; - struct vcd_property_short_header_type short_header; - struct vcd_property_vop_timing_type vop_timing; - u32 n_hdr_ext_control; - struct vcd_property_db_config_type db_control; - struct vcd_property_entropy_control_type entropy_control; - struct vcd_property_i_period_type i_period; - struct vcd_property_session_qp_type session_qp; - struct vcd_property_qp_range_type qp_range; - struct vcd_property_rc_level_type rc_level; - u32 n_r_cframe_skip; - u32 n_vb_vbuffer_size; - struct vcd_property_frame_level_rc_params_type frame_level_rc; - struct vcd_property_adaptive_rc_params_type adaptive_rc; - struct vcd_property_intra_refresh_mb_number_type intra_refresh; - struct vcd_property_buffer_format_type buf_format; - struct vcd_property_buffer_format_type re_con_buf_format; - u32 n_dynamic_prop_change; - u32 b_dynmic_prop_change_req; - u32 n_ext_enc_control_val; - struct vidc_720p_enc_frame_info_type enc_frame_info; - struct ddl_buf_addr_type enc_dpb_addr; - struct ddl_buf_addr_type seq_header; - struct vcd_buffer_requirement_type input_buf_req; - struct vcd_buffer_requirement_type output_buf_req; - struct vcd_buffer_requirement_type client_input_buf_req; - struct vcd_buffer_requirement_type client_output_buf_req; +struct ddl_encoder_data { + struct ddl_codec_data_hdr hdr; + struct vcd_property_codec codec_type; + struct vcd_property_frame_size frame_size; + struct vcd_property_frame_rate frame_rate; + struct vcd_property_target_bitrate target_bit_rate; + struct vcd_property_profile profile; + struct vcd_property_level level; + struct vcd_property_rate_control rc_type; + struct vcd_property_multi_slice multi_slice; + u32 meta_data_enable_flag; + u32 suffix; + struct ddl_dma_buffer meta_data_input; + phys_addr_t meta_data_offset; + struct vcd_property_short_header short_header; + struct vcd_property_vop_timing vop_timing; + u32 hdr_ext_control; + struct vcd_property_db_config db_control; + struct vcd_property_entropy_control entropy_control; + struct vcd_property_i_period period; + struct vcd_property_session_qp session_qp; + struct vcd_property_qp_range qp_range; + struct vcd_property_rc_level rc_level; + u32 r_cframe_skip; + u32 vb_vbuffer_size; + struct vcd_property_frame_level_rc_params frame_level_rc; + struct vcd_property_adaptive_rc_params adaptive_rc; + struct vcd_property_intra_refresh_mb_number intra_refresh; + struct vcd_property_buffer_format buf_format; + struct vcd_property_buffer_format re_con_buf_format; + u32 dynamic_prop_change; + u32 dynmic_prop_change_req; + u32 ext_enc_control_val; + struct vidc_720p_enc_frame_info enc_frame_info; + struct ddl_dma_buffer enc_dpb_addr; + struct ddl_dma_buffer seq_header; + struct vcd_buffer_requirement input_buf_req; + struct vcd_buffer_requirement output_buf_req; + struct vcd_buffer_requirement client_input_buf_req; + struct vcd_buffer_requirement client_output_buf_req; }; -struct ddl_decoder_data_type { - struct ddl_codec_data_hdr_type hdr; - struct vcd_property_codec_type codec_type; - struct vcd_property_buffer_format_type buf_format; - struct vcd_property_frame_size_type frame_size; - struct vcd_property_frame_size_type client_frame_size; - struct vcd_property_profile_type profile; - struct vcd_property_level_type level; - u32 n_progressive_only; - u32 n_meta_data_enable_flag; - u32 n_suffix; - struct ddl_buf_addr_type meta_data_input; - struct ddl_buf_addr_type ref_buffer; - u32 n_meta_data_offset; - struct vcd_property_post_filter_type post_filter; - struct vcd_sequence_hdr_type decode_config; - u32 b_header_in_start; - u32 n_min_dpb_num; - u32 n_y_cb_cr_size; - struct ddl_property_dec_pic_buffers_type dp_buf; - struct ddl_mask_type dpb_mask; - u32 n_dynamic_prop_change; - u32 b_dynmic_prop_change_req; - struct vidc_720p_dec_disp_info_type dec_disp_info; - struct ddl_buf_addr_type dpb_comv_buffer; - struct ddl_buf_addr_type h264Vsp_temp_buffer; - struct vcd_buffer_requirement_type actual_input_buf_req; - struct vcd_buffer_requirement_type min_input_buf_req; - struct vcd_buffer_requirement_type client_input_buf_req; - struct vcd_buffer_requirement_type actual_output_buf_req; - struct vcd_buffer_requirement_type min_output_buf_req; - struct vcd_buffer_requirement_type client_output_buf_req; +struct ddl_decoder_data { + struct ddl_codec_data_hdr hdr; + struct vcd_property_codec codec_type; + struct vcd_property_buffer_format buf_format; + struct vcd_property_frame_size frame_size; + struct vcd_property_frame_size client_frame_size; + struct vcd_property_profile profile; + struct vcd_property_level level; + u32 progressive_only; + u32 meta_data_enable_flag; + u32 suffix; + struct ddl_dma_buffer meta_data_input; + struct ddl_dma_buffer ref_buffer; + size_t meta_data_offset; + struct vcd_property_post_filter post_filter; + struct vcd_phys_sequence_hdr decode_config; + u32 header_in_start; + u32 min_dpb_num; + size_t y_cb_cr_size; + struct ddl_property_dec_pic_buffers dp_buf; + struct ddl_mask dpb_mask; + u32 dynamic_prop_change; + u32 dynmic_prop_change_req; + struct vidc_720p_dec_disp_info dec_disp_info; + struct ddl_dma_buffer dpb_comv_buffer; + struct ddl_dma_buffer h264Vsp_temp_buffer; + struct vcd_buffer_requirement actual_input_buf_req; + struct vcd_buffer_requirement min_input_buf_req; + struct vcd_buffer_requirement client_input_buf_req; + struct vcd_buffer_requirement actual_output_buf_req; + struct vcd_buffer_requirement min_output_buf_req; + struct vcd_buffer_requirement client_output_buf_req; }; -union ddl_codec_data_type { - struct ddl_codec_data_hdr_type hdr; - struct ddl_decoder_data_type decoder; - struct ddl_encoder_data_type encoder; +union ddl_codec_data { + struct ddl_codec_data_hdr hdr; + struct ddl_decoder_data decoder; + struct ddl_encoder_data encoder; }; -struct ddl_context_type { - u8 *p_core_virtual_base_addr; +struct ddl_context { + u8 *core_virtual_base_addr; void (*ddl_callback) (u32 event, u32 status, void *payload, u32 size, - u32 *p_ddl_handle, void *const p_client_data); - void *p_client_data; + u32 *ddl_handle, void *const client_data); + void *client_data; void (*pf_interrupt_clr) (void); - enum ddl_cmd_state_type e_cmd_state; - struct ddl_client_context_type *p_current_ddl; - struct ddl_buf_addr_type context_buf_addr; - struct ddl_buf_addr_type db_line_buffer; - struct ddl_buf_addr_type data_partition_tempbuf; - struct ddl_buf_addr_type metadata_shared_input; - struct ddl_buf_addr_type dbg_core_dump; + enum ddl_cmd_state cmd_state; + struct ddl_client_context *current_ddl; + struct ddl_dma_buffer context_buf_addr; + struct ddl_dma_buffer db_line_buffer; + struct ddl_dma_buffer data_partition_tempbuf; + struct ddl_dma_buffer metadata_shared_input; + struct ddl_dma_buffer dbg_core_dump; u32 enable_dbg_core_dump; - struct ddl_client_context_type *a_ddl_clients[VCD_MAX_NO_CLIENT]; - u32 n_device_state; - u32 n_ddl_busy; + struct ddl_client_context *ddl_clients[VCD_MAX_NO_CLIENT]; + u32 device_state; + u32 ddl_busy; u32 intr_status; - u32 n_cmd_err_status; - u32 n_disp_pic_err_status; - u32 n_op_failed; + u32 cmd_err_status; + u32 disp_pic_err_status; + u32 op_failed; }; -struct ddl_client_context_type { - struct ddl_context_type *p_ddl_context; - enum ddl_client_state_type e_client_state; - u32 b_decoding; - u32 n_channel_id; - struct ddl_frame_data_type_tag input_frame; - struct ddl_frame_data_type_tag output_frame; - union ddl_codec_data_type codec_data; +struct ddl_client_context { + struct ddl_context *ddl_context; + enum ddl_client_state client_state; + u32 decoding; + u32 channel_id; + struct ddl_frame_data_tag input_frame; + struct ddl_frame_data_tag output_frame; + union ddl_codec_data codec_data; }; -DDL_INLINE struct ddl_context_type *ddl_get_context(void); -DDL_INLINE void ddl_move_command_state(struct ddl_context_type *p_ddl_context, - enum ddl_cmd_state_type e_command_state); -DDL_INLINE void ddl_move_client_state(struct ddl_client_context_type *p_ddl, - enum ddl_client_state_type client_state); -void ddl_core_init(struct ddl_context_type *); -void ddl_core_start_cpu(struct ddl_context_type *); -void ddl_channel_set(struct ddl_client_context_type *); -void ddl_channel_end(struct ddl_client_context_type *); -void ddl_encode_init_codec(struct ddl_client_context_type *); -void ddl_decode_init_codec(struct ddl_client_context_type *); -void ddl_encode_frame_run(struct ddl_client_context_type *); -void ddl_decode_frame_run(struct ddl_client_context_type *); -void ddl_decode_eos_run(struct ddl_client_context_type *); -void ddl_release_context_buffers(struct ddl_context_type *); -void ddl_release_client_internal_buffers(struct ddl_client_context_type *p_ddl); -u32 ddl_decode_set_buffers(struct ddl_client_context_type *); -u32 ddl_decoder_dpb_transact(struct ddl_decoder_data_type *p_decoder, - struct ddl_frame_data_type_tag *p_in_out_frame, - u32 n_operation); -u32 ddl_client_transact(u32, struct ddl_client_context_type **); -void ddl_set_default_decoder_buffer_req - (struct ddl_decoder_data_type *p_decoder, u32 b_estimate); -void ddl_set_default_encoder_buffer_req - (struct ddl_encoder_data_type *p_encoder); -void ddl_set_default_dec_property(struct ddl_client_context_type *); -u32 ddl_encoder_ready_to_start(struct ddl_client_context_type *); -u32 ddl_decoder_ready_to_start(struct ddl_client_context_type *, - struct vcd_sequence_hdr_type *); -u32 ddl_get_yuv_buffer_size - (struct vcd_property_frame_size_type *p_frame_size, - struct vcd_property_buffer_format_type *p_buf_format, u32 inter_lace); -void ddl_calculate_stride(struct vcd_property_frame_size_type *p_frame_size, - u32 inter_lace); -void ddl_encode_dynamic_property(struct ddl_client_context_type *p_ddl, - u32 b_enable); -void ddl_decode_dynamic_property(struct ddl_client_context_type *p_ddl, - u32 b_enable); -void ddl_set_initial_default_values(struct ddl_client_context_type *p_ddl); -u32 ddl_handle_core_errors(struct ddl_context_type *p_ddl_context); -void ddl_client_fatal_cb(struct ddl_context_type *p_ddl_context); -void ddl_hw_fatal_cb(struct ddl_context_type *p_ddl_context); -u32 ddl_hal_engine_reset(struct ddl_context_type *p_ddl_context); +struct ddl_context *ddl_get_context(void); +void ddl_move_command_state(struct ddl_context *ddl_context, + enum ddl_cmd_state command_state); +void ddl_move_client_state(struct ddl_client_context *ddl, + enum ddl_client_state client_state); +void ddl_core_init(struct ddl_context *); +void ddl_core_start_cpu(struct ddl_context *); +void ddl_channel_set(struct ddl_client_context *); +void ddl_channel_end(struct ddl_client_context *); +void ddl_encode_init_codec(struct ddl_client_context *); +void ddl_decode_init_codec(struct ddl_client_context *); +void ddl_encode_frame_run(struct ddl_client_context *); +void ddl_decode_frame_run(struct ddl_client_context *); +void ddl_decode_eos_run(struct ddl_client_context *); +void ddl_release_context_buffers(struct ddl_context *); +void ddl_release_client_internal_buffers(struct ddl_client_context *ddl); +u32 ddl_decode_set_buffers(struct ddl_client_context *); +u32 ddl_decoder_dpb_transact(struct ddl_decoder_data *dec, + struct ddl_frame_data_tag *in_out_frame, u32 operation); +u32 ddl_client_transact(u32, struct ddl_client_context **); +void ddl_set_default_decoder_buffer_req(struct ddl_decoder_data *dec, + u32 estimate); +void ddl_set_default_encoder_buffer_req(struct ddl_encoder_data *enc); +void ddl_set_default_dec_property(struct ddl_client_context *); +u32 ddl_encoder_ready_to_start(struct ddl_client_context *); +u32 ddl_decoder_ready_to_start(struct ddl_client_context *, + struct vcd_phys_sequence_hdr *); +size_t ddl_get_yuv_buffer_size(struct vcd_property_frame_size *frame_size, + struct vcd_property_buffer_format *buf_format, u32 interlace); +void ddl_calculate_stride(struct vcd_property_frame_size *frame_size, + u32 interlace); +void ddl_encode_dynamic_property(struct ddl_client_context *ddl, u32 enable); +void ddl_decode_dynamic_property(struct ddl_client_context *ddl, u32 enable); +void ddl_set_initial_default_values(struct ddl_client_context *ddl); +u32 ddl_handle_core_errors(struct ddl_context *ddl_context); +void ddl_client_fatal_cb(struct ddl_context *ddl_context); +void ddl_hw_fatal_cb(struct ddl_context *ddl_context); +u32 ddl_hal_engine_reset(struct ddl_context *ddl_context); + #endif diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_api.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_api.h index 3a492d00ab774..b3ce6b78118cd 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl_api.h +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_api.h @@ -30,40 +30,39 @@ #define _VCD_DDL_API_H_ #include "vcd_ddl_internal_property.h" -struct ddl_init_config_type { - u8 *p_core_virtual_base_addr; +struct ddl_init_config { + u8 *core_virtual_base_addr; void (*pf_interrupt_clr) (void); void (*ddl_callback) (u32 event, u32 status, void *payload, u32 size, - u32 *p_ddl_handle, void *const p_client_data); + u32 *ddl_handle, void *const client_data); }; -struct ddl_frame_data_type_tag { - struct vcd_frame_data_type vcd_frm; - u32 n_intrlcd_ip_frm_tag; - u32 b_frm_trans_end; - u32 n_frm_delta; +struct ddl_frame_data_tag { + struct vcd_frame_data vcd_frm; + u32 intrlcd_ip_frm_tag; + u32 frm_trans_end; + u32 frm_delta; }; -u32 ddl_device_init(struct ddl_init_config_type *p_ddl_init_config, - void *p_client_data); -u32 ddl_device_release(void *p_client_data); -u32 ddl_open(u32 **p_ddl_handle, u32 b_decoding); -u32 ddl_close(u32 **p_ddl_handle); -u32 ddl_encode_start(u32 *ddl_handle, void *p_client_data); -u32 ddl_encode_frame(u32 *ddl_handle, - struct ddl_frame_data_type_tag *p_input_frame, - struct ddl_frame_data_type_tag *p_output_bit, void *p_client_data); -u32 ddl_encode_end(u32 *ddl_handle, void *p_client_data); -u32 ddl_decode_start(u32 *ddl_handle, struct vcd_sequence_hdr_type *p_header, - void *p_client_data); -u32 ddl_decode_frame(u32 *ddl_handle, - struct ddl_frame_data_type_tag *p_input_bits, void *p_client_data); -u32 ddl_decode_end(u32 *ddl_handle, void *p_client_data); -u32 ddl_set_property(u32 *ddl_handle, - struct vcd_property_hdr_type *p_property_hdr, void *p_property_value); -u32 ddl_get_property(u32 *ddl_handle, - struct vcd_property_hdr_type *p_property_hdr, void *p_property_value); +u32 ddl_device_init(struct ddl_init_config *ddl_init_config, void *client_data); +u32 ddl_device_release(void *client_data); +u32 ddl_open(u32 **ddl_handle, u32 decoding); +u32 ddl_close(u32 **ddl_handle); +u32 ddl_encode_start(u32 *ddl_handle, void *client_data); +u32 ddl_encode_frame(u32 *ddl_handle, struct ddl_frame_data_tag *input_frame, + struct ddl_frame_data_tag *output_bit, void *client_data); +u32 ddl_encode_end(u32 *ddl_handle, void *client_data); +u32 ddl_decode_start(u32 *ddl_handle, struct vcd_phys_sequence_hdr *header, + void *client_data); +u32 ddl_decode_frame(u32 *ddl_handle, struct ddl_frame_data_tag *in_bits, + void *client_data); +u32 ddl_decode_end(u32 *ddl_handle, void *client_data); +u32 ddl_set_property(u32 *ddl_handle, struct vcd_property_hdr *property_hdr, + void *property_value); +u32 ddl_get_property(u32 *ddl_handle, struct vcd_property_hdr *property_hdr, + void *property_value); void ddl_read_and_clear_interrupt(void); u32 ddl_process_core_response(void); -u32 ddl_reset_hw(u32 n_mode); +u32 ddl_reset_hw(u32 mode); + #endif diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_core.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_core.h index cbdb3c9390831..8e0cfcdaebee2 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl_core.h +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_core.h @@ -48,7 +48,7 @@ #define VCD_MAX_NO_CLIENT 4 #define VCD_FRAME_COMMAND_DEPTH 1 #define VCD_GENERAL_COMMAND_DEPTH 1 -#define VCD_COMMAND_EXCLUSIVE TRUE +#define VCD_COMMAND_EXCLUSIVE true #define DDL_HW_TIMEOUT_IN_MS 1000 @@ -78,42 +78,28 @@ #define DDL_MAX_H264_QP 51 #define DDL_MAX_MPEG4_QP 31 +//TODO clean this dirty thing #define DDL_PADDING_HACK(addr) \ - (addr) = (u32)((((u32)(addr) + DDL_STREAMBUF_ALIGN_GUARD_BYTES) & \ - ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES)) + DDL_BUFEND_PAD) + (addr) = (u32)((((u32)(addr) + DDL_STREAMBUF_ALIGN_GUARD_BYTES) & \ + ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES)) + DDL_BUFEND_PAD) #define DDL_FRAMESIZE_DIV_FACTOR 0xF -#define DDL_ALLOW_DEC_FRAMESIZE(n_width, n_height) \ -(\ - (\ - (n_width <= DDL_MAX_FRAME_WIDTH) && \ - (n_height <= DDL_MAX_FRAME_HEIGHT) \ - ) && \ - (\ - (n_width >= 32 && n_height >= 16) || \ - (n_width >= 16 && n_height >= 32) \ - )\ -) - -#define DDL_ALLOW_ENC_FRAMESIZE(n_width, n_height) \ -(\ - (\ - (n_width <= DDL_MAX_FRAME_WIDTH) && \ - (n_height <= DDL_MAX_FRAME_HEIGHT) \ - ) && \ - (\ - (n_width >= 32 && n_height >= 32) \ - ) && \ - (\ - !(n_width & DDL_FRAMESIZE_DIV_FACTOR) && \ - !(n_height & DDL_FRAMESIZE_DIV_FACTOR) \ - ) \ -) +#define DDL_ALLOW_DEC_FRAMESIZE(w, h) (\ + (w) <= DDL_MAX_FRAME_WIDTH && \ + (h) <= DDL_MAX_FRAME_HEIGHT && \ + (((w) >= 32 && (h) >= 16) || ((w) >= 16 && (h) >= 32))) + +#define DDL_ALLOW_ENC_FRAMESIZE(w, h) (\ + (w) <= DDL_MAX_FRAME_WIDTH && \ + (h) <= DDL_MAX_FRAME_HEIGHT && \ + (w) >= 32 && (h) >= 32 && \ + !((w) & DDL_FRAMESIZE_DIV_FACTOR) && \ + !((h) & DDL_FRAMESIZE_DIV_FACTOR)) #define DDL_TILE_ALIGN_WIDTH 128 #define DDL_TILE_ALIGN_HEIGHT 32 #define DDL_TILE_MULTIPLY_FACTOR 8192 #define DDL_TILE_ALIGN(val, grid) \ - (((val) + (grid) - 1) / (grid) * (grid)) + (((val) + (grid) - 1) / (grid) * (grid)) #endif diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_errors.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_errors.c index e95ae10114875..1aad338fbadc6 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl_errors.c +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_errors.c @@ -138,27 +138,27 @@ #define METADATA_NO_SPACE_SLICE_SIZE 181 #define RESOLUTION_WARNING 182 -void ddl_hw_fatal_cb(struct ddl_context_type *p_ddl_context) +void ddl_hw_fatal_cb(struct ddl_context *ddl_context) { /* Invalidate the command state */ - ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); - p_ddl_context->n_device_state = DDL_DEVICE_HWFATAL; + ddl_move_command_state(ddl_context, DDL_CMD_INVALID); + ddl_context->device_state = DDL_DEVICE_HWFATAL; /* callback to the client to indicate hw fatal error */ - p_ddl_context->ddl_callback(VCD_EVT_IND_HWERRFATAL, + ddl_context->ddl_callback(VCD_EVT_IND_HWERRFATAL, VCD_ERR_HW_FATAL, NULL, 0, - (void *)p_ddl_context->p_current_ddl, - p_ddl_context->p_client_data); + (void *)ddl_context->current_ddl, + ddl_context->client_data); - DDL_IDLE(p_ddl_context); + DDL_IDLE(ddl_context); } -static u32 ddl_handle_hw_fatal_errors(struct ddl_context_type - *p_ddl_context) +static u32 ddl_handle_hw_fatal_errors(struct ddl_context + *ddl_context) { - u32 b_status = FALSE; + u32 status = false; - switch (p_ddl_context->n_cmd_err_status) { + switch (ddl_context->cmd_err_status) { case INVALID_CHANNEL_NUMBER: case INVALID_COMMAND_ID: @@ -189,46 +189,46 @@ static u32 ddl_handle_hw_fatal_errors(struct ddl_context_type case VSP_NOT_READY: case BUFFER_FULL_STATE: ERR("HW FATAL ERROR"); - ddl_hw_fatal_cb(p_ddl_context); - b_status = TRUE; + ddl_hw_fatal_cb(ddl_context); + status = true; break; } - return b_status; + return status; } -void ddl_client_fatal_cb(struct ddl_context_type *p_ddl_context) +void ddl_client_fatal_cb(struct ddl_context *ddl_context) { - struct ddl_client_context_type *p_ddl = - p_ddl_context->p_current_ddl; + struct ddl_client_context *ddl = + ddl_context->current_ddl; - if (p_ddl_context->e_cmd_state == DDL_CMD_DECODE_FRAME) - ddl_decode_dynamic_property(p_ddl, FALSE); - else if (p_ddl_context->e_cmd_state == DDL_CMD_ENCODE_FRAME) - ddl_encode_dynamic_property(p_ddl, FALSE); + if (ddl_context->cmd_state == DDL_CMD_DECODE_FRAME) + ddl_decode_dynamic_property(ddl, false); + else if (ddl_context->cmd_state == DDL_CMD_ENCODE_FRAME) + ddl_encode_dynamic_property(ddl, false); - ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + ddl_move_command_state(ddl_context, DDL_CMD_INVALID); - ddl_move_client_state(p_ddl, DDL_CLIENT_FATAL_ERROR); + ddl_move_client_state(ddl, DDL_CLIENT_FATAL_ERROR); - p_ddl_context->ddl_callback + ddl_context->ddl_callback ( VCD_EVT_IND_HWERRFATAL, VCD_ERR_CLIENT_FATAL, NULL, 0, - (void *)p_ddl, - p_ddl_context->p_client_data + (void *)ddl, + ddl_context->client_data ); - DDL_IDLE(p_ddl_context); + DDL_IDLE(ddl_context); } -static u32 ddl_handle_client_fatal_errors(struct ddl_context_type - *p_ddl_context) +static u32 ddl_handle_client_fatal_errors(struct ddl_context + *ddl_context) { - u32 b_status = FALSE; + u32 status = false; - switch (p_ddl_context->n_cmd_err_status) { + switch (ddl_context->cmd_err_status) { case UNSUPPORTED_FEATURE_IN_PROFILE: case RESOLUTION_NOT_SUPPORTED: case HEADER_NOT_FOUND: @@ -252,52 +252,52 @@ static u32 ddl_handle_client_fatal_errors(struct ddl_context_type case NULL_DB_POINTER: case NULL_COMV_POINTER: { - b_status = TRUE; + status = true; break; } } - if (!b_status) + if (!status) ERR("UNKNOWN-OP-FAILED"); - ddl_client_fatal_cb(p_ddl_context); + ddl_client_fatal_cb(ddl_context); - return TRUE; + return true; } -static void ddl_input_failed_cb(struct ddl_context_type *p_ddl_context, +static void ddl_input_failed_cb(struct ddl_context *ddl_context, u32 vcd_event, u32 vcd_status) { - struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; + struct ddl_client_context *ddl = ddl_context->current_ddl; - ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + ddl_move_command_state(ddl_context, DDL_CMD_INVALID); - if (p_ddl->b_decoding) - ddl_decode_dynamic_property(p_ddl, FALSE); + if (ddl->decoding) + ddl_decode_dynamic_property(ddl, false); else - ddl_encode_dynamic_property(p_ddl, FALSE); + ddl_encode_dynamic_property(ddl, false); - p_ddl_context->ddl_callback(vcd_event, - vcd_status, &p_ddl->input_frame, - sizeof(struct ddl_frame_data_type_tag), - (void *)p_ddl, p_ddl_context->p_client_data); + ddl_context->ddl_callback(vcd_event, + vcd_status, &ddl->input_frame, + sizeof(struct ddl_frame_data_tag), + (void *)ddl, ddl_context->client_data); - ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME); + ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME); } -static u32 ddl_handle_core_recoverable_errors(struct ddl_context_type \ - *p_ddl_context) +static u32 ddl_handle_core_recoverable_errors(struct ddl_context \ + *ddl_context) { - struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; + struct ddl_client_context *ddl = ddl_context->current_ddl; u32 vcd_status = VCD_S_SUCCESS; u32 vcd_event = VCD_EVT_RESP_INPUT_DONE; - u32 b_eos = FALSE, pending_display = 0, release_mask = 0; + u32 eos = false, pending_display = 0, release_mask = 0; - if (p_ddl_context->e_cmd_state != DDL_CMD_DECODE_FRAME && - p_ddl_context->e_cmd_state != DDL_CMD_ENCODE_FRAME) { - return FALSE; + if (ddl_context->cmd_state != DDL_CMD_DECODE_FRAME && + ddl_context->cmd_state != DDL_CMD_ENCODE_FRAME) { + return false; } - switch (p_ddl_context->n_cmd_err_status) { + switch (ddl_context->cmd_err_status) { case NON_PAIRED_FIELD_NOT_SUPPORTED: { vcd_status = VCD_ERR_INTRLCD_FIELD_DROP; @@ -307,7 +307,7 @@ static u32 ddl_handle_core_recoverable_errors(struct ddl_context_type \ { /* lets check sanity of this error */ release_mask = - p_ddl->codec_data.decoder.dpb_mask.n_hw_mask; + ddl->codec_data.decoder.dpb_mask.hw_mask; while (release_mask > 0) { if ((release_mask & 0x1)) pending_display += 1; @@ -315,11 +315,11 @@ static u32 ddl_handle_core_recoverable_errors(struct ddl_context_type \ } if (pending_display >= - p_ddl->codec_data.decoder.n_min_dpb_num) { + ddl->codec_data.decoder.min_dpb_num) { DBG("FWISSUE-REQBUF!!"); /* callback to client for client fatal error */ - ddl_client_fatal_cb(p_ddl_context); - return TRUE ; + ddl_client_fatal_cb(ddl_context); + return true ; } vcd_event = VCD_EVT_RESP_OUTPUT_REQ; break; @@ -331,11 +331,11 @@ static u32 ddl_handle_core_recoverable_errors(struct ddl_context_type \ case VME_NOT_READY: case VC1_BITPLANE_DECODE_ERR: { - u32 b_reset_core; + u32 reset_core; /* need to reset the internal core hw engine */ - b_reset_core = ddl_hal_engine_reset(p_ddl_context); - if (!b_reset_core) - return TRUE; + reset_core = ddl_hal_engine_reset(ddl_context); + if (!reset_core) + return true; /* fall through to process bitstream error handling */ } case RESOLUTION_MISMATCH: @@ -363,70 +363,70 @@ static u32 ddl_handle_core_recoverable_errors(struct ddl_context_type \ } if (!vcd_status && vcd_event == VCD_EVT_RESP_INPUT_DONE) - return FALSE; + return false; - p_ddl->input_frame.b_frm_trans_end = TRUE; + ddl->input_frame.frm_trans_end = true; - b_eos = ((vcd_event == VCD_EVT_RESP_INPUT_DONE) && - ((VCD_FRAME_FLAG_EOS & p_ddl->input_frame. - vcd_frm.n_flags))); + eos = ((vcd_event == VCD_EVT_RESP_INPUT_DONE) && + ((VCD_FRAME_FLAG_EOS & ddl->input_frame. + vcd_frm.flags))); - if ((p_ddl->b_decoding && b_eos) || - (!p_ddl->b_decoding)) - p_ddl->input_frame.b_frm_trans_end = FALSE; + if ((ddl->decoding && eos) || + (!ddl->decoding)) + ddl->input_frame.frm_trans_end = false; if (vcd_event == VCD_EVT_RESP_INPUT_DONE && - p_ddl->b_decoding && - !p_ddl->codec_data.decoder.b_header_in_start && - !p_ddl->codec_data.decoder.dec_disp_info.n_img_size_x && - !p_ddl->codec_data.decoder.dec_disp_info.n_img_size_y + ddl->decoding && + !ddl->codec_data.decoder.header_in_start && + !ddl->codec_data.decoder.dec_disp_info.img_size_x && + !ddl->codec_data.decoder.dec_disp_info.img_size_y ) { /* this is first frame seq. header only case */ vcd_status = VCD_S_SUCCESS; - p_ddl->input_frame.vcd_frm.n_flags |= + ddl->input_frame.vcd_frm.flags |= VCD_FRAME_FLAG_CODECCONFIG; - p_ddl->input_frame.b_frm_trans_end = !b_eos; + ddl->input_frame.frm_trans_end = !eos; /* put just some non - zero value */ - p_ddl->codec_data.decoder.dec_disp_info.n_img_size_x = 0xff; + ddl->codec_data.decoder.dec_disp_info.img_size_x = 0xff; } /* inform client about input failed */ - ddl_input_failed_cb(p_ddl_context, vcd_event, vcd_status); + ddl_input_failed_cb(ddl_context, vcd_event, vcd_status); /* for Encoder case, we need to send output done also */ - if (!p_ddl->b_decoding) { + if (!ddl->decoding) { /* transaction is complete after this callback */ - p_ddl->output_frame.b_frm_trans_end = !b_eos; + ddl->output_frame.frm_trans_end = !eos; /* error case: NO data present */ - p_ddl->output_frame.vcd_frm.n_data_len = 0; + ddl->output_frame.vcd_frm.data_len = 0; /* call back to client for output frame done */ - p_ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE, - VCD_ERR_FAIL, &(p_ddl->output_frame), - sizeof(struct ddl_frame_data_type_tag), - (void *)p_ddl, p_ddl_context->p_client_data); + ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE, + VCD_ERR_FAIL, &(ddl->output_frame), + sizeof(struct ddl_frame_data_tag), + (void *)ddl, ddl_context->client_data); - if (b_eos) { + if (eos) { DBG("ENC-EOS_DONE"); /* send client EOS DONE callback */ - p_ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE, - VCD_S_SUCCESS, NULL, 0, (void *)p_ddl, - p_ddl_context->p_client_data); + ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE, + VCD_S_SUCCESS, NULL, 0, (void *)ddl, + ddl_context->client_data); } } /* if it is decoder EOS case */ - if (p_ddl->b_decoding && b_eos) - ddl_decode_eos_run(p_ddl); + if (ddl->decoding && eos) + ddl_decode_eos_run(ddl); else - DDL_IDLE(p_ddl_context); + DDL_IDLE(ddl_context); - return TRUE; + return true; } -static u32 ddl_handle_core_warnings(u32 n_err_status) +static u32 ddl_handle_core_warnings(u32 err_status) { - u32 b_status = FALSE; + u32 status = false; - switch (n_err_status) { + switch (err_status) { case FRAME_RATE_UNKNOWN: case ASPECT_RATIO_UNKOWN: case COLOR_PRIMARIES_UNKNOWN: @@ -459,51 +459,51 @@ static u32 ddl_handle_core_warnings(u32 n_err_status) case METADATA_NO_SPACE_EXTRA: case METADATA_NO_SPACE_DATA_NONE: { - b_status = TRUE; + status = true; DBG("CMD-WARNING-IGNORED!!"); break; } } - return b_status; + return status; } -u32 ddl_handle_core_errors(struct ddl_context_type *p_ddl_context) +u32 ddl_handle_core_errors(struct ddl_context *ddl_context) { - u32 b_status = FALSE; + u32 status = false; - if (!p_ddl_context->n_cmd_err_status && - !p_ddl_context->n_disp_pic_err_status) - return FALSE; + if (!ddl_context->cmd_err_status && + !ddl_context->disp_pic_err_status) + return false; - if (p_ddl_context->e_cmd_state == DDL_CMD_INVALID) { + if (ddl_context->cmd_state == DDL_CMD_INVALID) { DBG("SPURIOUS_INTERRUPT_ERROR"); - return TRUE; + return true; } - if (!p_ddl_context->n_op_failed) { - u32 b_disp_status; - b_status = ddl_handle_core_warnings(p_ddl_context-> - n_cmd_err_status); - b_disp_status = ddl_handle_core_warnings( - p_ddl_context->n_disp_pic_err_status); - if (!b_status && !b_disp_status) + if (!ddl_context->op_failed) { + u32 disp_status; + status = ddl_handle_core_warnings(ddl_context-> + cmd_err_status); + disp_status = ddl_handle_core_warnings( + ddl_context->disp_pic_err_status); + if (!status && !disp_status) DBG("ddl_warning:Unknown"); - return FALSE; + return false; } ERR("\n %s(): OPFAILED!!", __func__); ERR("\n CMD_ERROR_STATUS = %u, DISP_ERR_STATUS = %u", - p_ddl_context->n_cmd_err_status, - p_ddl_context->n_disp_pic_err_status); + ddl_context->cmd_err_status, + ddl_context->disp_pic_err_status); - b_status = ddl_handle_hw_fatal_errors(p_ddl_context); + status = ddl_handle_hw_fatal_errors(ddl_context); - if (!b_status) - b_status = ddl_handle_core_recoverable_errors(p_ddl_context); + if (!status) + status = ddl_handle_core_recoverable_errors(ddl_context); - if (!b_status) - b_status = ddl_handle_client_fatal_errors(p_ddl_context); + if (!status) + status = ddl_handle_client_fatal_errors(ddl_context); - return b_status; + return status; } diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.c index 232a5c181cac7..d501893885588 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.c +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.c @@ -16,338 +16,197 @@ * */ -#include "video_core_type.h" +#include +#include "video_core_type.h" #include "vcd_ddl_firmware.h" -#include "vcd_ddl_utils.h" - -#define VCDFW_TOTALNUM_IMAGE 7 -#define VCDFW_MAX_NO_IMAGE 2 - -struct vcd_firmware_type { - u32 a_active_fw_img[VCDFW_TOTALNUM_IMAGE]; - struct ddl_buf_addr_type boot_code; - struct ddl_buf_addr_type enc_mpeg4; - struct ddl_buf_addr_type encH264; - - struct ddl_buf_addr_type dec_mpeg4; - struct ddl_buf_addr_type decH264; - struct ddl_buf_addr_type decH263; - struct ddl_buf_addr_type dec_mpeg2; - struct ddl_buf_addr_type dec_vc1; +struct vcd_firmware_table { + bool prepared; + struct device *dev; + struct vcd_firmware fw[6]; }; -static struct vcd_firmware_type vcd_firmware; +//TODO max_sz is kinda sucky, a better way? +static struct vcd_firmware_table vcd_fw_table = { + .prepared = false, + .fw[0] = { + .filename = "vidc_720p_command_control.fw", + .change_endian = false, + .max_sz = 12288, + }, + .fw[1] = { + .filename = "vidc_720p_mp4_dec_mc.fw", + .change_endian = true, + .max_sz = 32768, + }, + .fw[2] = { + .filename = "vidc_720p_h263_dec_mc.fw", + .change_endian = true, + .max_sz = 24576, + }, + .fw[3] = { + .filename = "vidc_720p_h264_dec_mc.fw", + .change_endian = true, + .max_sz = 45056, + }, + .fw[4] = { + .filename = "vidc_720p_mp4_enc_mc.fw", + .change_endian = true, + .max_sz = 32768, + }, + .fw[5] = { + .filename = "vidc_720p_h264_enc_mc.fw", + .change_endian = true, + .max_sz = 36864, + }, +}; -static void vcd_fw_change_endian(unsigned char *fw, u32 n_fw_size) +static void vcd_fw_change_endian(struct vcd_firmware *vcd_fw) { - u32 i = 0; - unsigned char temp; - for (i = 0; i < n_fw_size; i = i + 4) { - temp = fw[i]; + size_t i; + u8 tmp; + u8 *fw = vcd_fw->virt_addr; + for (i = 0; i < vcd_fw->sz; i += 4) { + tmp = fw[i]; fw[i] = fw[i + 3]; - fw[i + 3] = temp; + fw[i + 3] = tmp; - temp = fw[i + 1]; + tmp = fw[i + 1]; fw[i + 1] = fw[i + 2]; - fw[i + 2] = temp; + fw[i + 2] = tmp; } - return; } -static u32 vcd_fw_prepare(struct ddl_buf_addr_type *fw_details, - const unsigned char fw_array[], - const unsigned int fw_array_size, u32 change_endian) +static int vcd_fw_prepare(struct vcd_firmware *vcd_fw) { - u32 *buffer; + int rc; + const struct firmware *fw; + + rc = request_firmware(&fw, vcd_fw->filename, vcd_fw_table.dev); + if (rc) { + pr_err("request_firmware(%s) failed %d\n", vcd_fw->filename, + rc); + return rc; + } - ddl_pmem_alloc(fw_details, fw_array_size, - DDL_LINEAR_BUFFER_ALIGN_BYTES); - if (!fw_details->p_virtual_base_addr) - return FALSE; + if (fw->size > vcd_fw->max_sz) { + pr_err("firmware %s is larger than allocated size (%u > %u)\n", + vcd_fw->filename, fw->size, vcd_fw->max_sz); + rc = -ENOMEM; + goto out; + } + vcd_fw->sz = fw->size; + memcpy(vcd_fw->virt_addr, fw->data, fw->size); - fw_details->n_buffer_size = fw_array_size / 4; + if (vcd_fw->change_endian) + vcd_fw_change_endian(vcd_fw); - buffer = fw_details->p_align_virtual_addr; + pr_info("prepared firmware %s\n", vcd_fw->filename); - memcpy(buffer, fw_array, fw_array_size); - if (change_endian) - vcd_fw_change_endian((unsigned char *)buffer, fw_array_size); - return TRUE; +out: + release_firmware(fw); + return rc; } -u32 vcd_fw_init(void) +int vcd_fw_prepare_all() { - u32 b_status = FALSE; + int i; + int rc = 0; - b_status = vcd_fw_prepare(&vcd_firmware.boot_code, - vid_c_command_control_fw, - vid_c_command_control_fw_size, FALSE); - - if (b_status) { - b_status = vcd_fw_prepare(&vcd_firmware.dec_mpeg4, - vid_c_mpg4_dec_fw, - vid_c_mpg4_dec_fw_size, TRUE); - } - - if (b_status) { - b_status = vcd_fw_prepare(&vcd_firmware.decH264, - vid_c_h264_dec_fw, - vid_c_h264_dec_fw_size, TRUE); - } - - if (b_status) { - b_status = vcd_fw_prepare(&vcd_firmware.decH263, - vid_c_h263_dec_fw, - vid_c_h263_dec_fw_size, TRUE); - } - - if (b_status) { - b_status = vcd_fw_prepare(&vcd_firmware.enc_mpeg4, - vid_c_mpg4_enc_fw, - vid_c_mpg4_enc_fw_size, TRUE); - } + if (vcd_fw_table.prepared) + goto out; - if (b_status) { - b_status = vcd_fw_prepare(&vcd_firmware.encH264, - vid_c_h264_enc_fw, - vid_c_h264_enc_fw_size, TRUE); + for (i = 0; i < ARRAY_SIZE(vcd_fw_table.fw); i++) { + rc = vcd_fw_prepare(&vcd_fw_table.fw[i]); + if (rc) + goto out; } + vcd_fw_table.prepared = true; - return b_status; +out: + return rc; } - -static u32 get_dec_fw_image(struct vcd_fw_details_type *p_fw_details) -{ - u32 b_return = TRUE; - switch (p_fw_details->e_codec) { - case VCD_CODEC_DIVX_4: - case VCD_CODEC_DIVX_5: - case VCD_CODEC_DIVX_6: - case VCD_CODEC_XVID: - case VCD_CODEC_MPEG4: - { - p_fw_details->p_fw_buffer_addr = - vcd_firmware.dec_mpeg4.p_align_physical_addr; - p_fw_details->n_fw_size = - vcd_firmware.dec_mpeg4.n_buffer_size; - break; - } - case VCD_CODEC_H264: - { - p_fw_details->p_fw_buffer_addr = - vcd_firmware.decH264.p_align_physical_addr; - p_fw_details->n_fw_size = - vcd_firmware.decH264.n_buffer_size; - break; - } - case VCD_CODEC_VC1: - case VCD_CODEC_VC1_RCV: - { - p_fw_details->p_fw_buffer_addr = - vcd_firmware.dec_vc1.p_align_physical_addr; - p_fw_details->n_fw_size = - vcd_firmware.dec_vc1.n_buffer_size; - break; - } - case VCD_CODEC_MPEG2: - { - p_fw_details->p_fw_buffer_addr = - vcd_firmware.dec_mpeg2.p_align_physical_addr; - p_fw_details->n_fw_size = - vcd_firmware.dec_mpeg2.n_buffer_size; - break; - } - case VCD_CODEC_H263: - { - p_fw_details->p_fw_buffer_addr = - vcd_firmware.decH263.p_align_physical_addr; - p_fw_details->n_fw_size = - vcd_firmware.decH263.n_buffer_size; - break; - } - default: - { - b_return = FALSE; - break; +int vcd_fw_init(struct device *dev) { + int i; + vcd_fw_table.dev = dev; + for (i = 0; i < ARRAY_SIZE(vcd_fw_table.fw); i++) { + struct vcd_firmware *fw = &vcd_fw_table.fw[i]; + fw->virt_addr = dma_alloc_coherent(NULL, fw->max_sz, + &fw->phys_addr, GFP_KERNEL); + if (!fw->virt_addr) { + pr_err("failed to allocate %d for %s\n", fw->max_sz, + fw->filename); + vcd_fw_exit(); + return -ENOMEM; } } - return b_return; + return 0; } -static u32 get_enc_fw_image(struct vcd_fw_details_type *p_fw_details) -{ - u32 b_return = TRUE; - switch (p_fw_details->e_codec) { - case VCD_CODEC_H263: - case VCD_CODEC_MPEG4: - { - p_fw_details->p_fw_buffer_addr = - vcd_firmware.enc_mpeg4.p_align_physical_addr; - p_fw_details->n_fw_size = - vcd_firmware.enc_mpeg4.n_buffer_size; - break; - } - case VCD_CODEC_H264: - { - p_fw_details->p_fw_buffer_addr = - vcd_firmware.encH264.p_align_physical_addr; - p_fw_details->n_fw_size = - vcd_firmware.encH264.n_buffer_size; - break; - } - default: - { - b_return = FALSE; - break; - } +void vcd_fw_exit(void) { + int i; + vcd_fw_table.prepared = false; + for (i = 0; i < ARRAY_SIZE(vcd_fw_table.fw); i++) { + struct vcd_firmware *fw = &vcd_fw_table.fw[i]; + if (!fw->virt_addr) + continue; + dma_free_coherent(NULL, fw->max_sz, fw->virt_addr, + fw->phys_addr); } - return b_return; } -u32 vcd_get_fw_property(u32 prop_id, void *prop_details) +struct vcd_firmware *vcd_fw_get_boot_fw(void) { - u32 b_return = TRUE; - struct vcd_fw_details_type *p_fw_details; - switch (prop_id) { - case VCD_FW_ENDIAN: - { - *(u32 *) prop_details = VCD_FW_BIG_ENDIAN; - break; - } - case VCD_FW_BOOTCODE: - { - p_fw_details = - (struct vcd_fw_details_type *)prop_details; - p_fw_details->p_fw_buffer_addr = - vcd_firmware.boot_code.p_align_physical_addr; - p_fw_details->n_fw_size = - vcd_firmware.boot_code.n_buffer_size; - break; - } - case VCD_FW_DECODE: - { - p_fw_details = - (struct vcd_fw_details_type *)prop_details; - b_return = get_dec_fw_image(p_fw_details); - break; - } - case VCD_FW_ENCODE: - { - p_fw_details = - (struct vcd_fw_details_type *)prop_details; - b_return = get_enc_fw_image(p_fw_details); - break; - } - default: - { - b_return = FALSE; - break; - } - } - return b_return; + if (!vcd_fw_table.prepared) + return NULL; + return &vcd_fw_table.fw[0]; } -u32 vcd_fw_transact(u32 b_add, u32 b_decoding, enum vcd_codec_type e_codec) +struct vcd_firmware *vcd_fw_get_fw(bool is_decode, enum vcd_codec codec) { - u32 b_return = TRUE; - u32 n_index = 0, n_active_fw = 0, n_loop_count; + if (!vcd_fw_table.prepared) + return NULL; - if (b_decoding) { - switch (e_codec) { + if (is_decode) { + switch (codec) { case VCD_CODEC_DIVX_4: case VCD_CODEC_DIVX_5: case VCD_CODEC_DIVX_6: case VCD_CODEC_XVID: case VCD_CODEC_MPEG4: - { - n_index = 0; - break; - } + return &vcd_fw_table.fw[1]; case VCD_CODEC_H264: - { - n_index = 1; - break; - } - case VCD_CODEC_H263: - { - n_index = 2; - break; - } - case VCD_CODEC_MPEG2: - { - n_index = 3; - break; - } + return &vcd_fw_table.fw[3]; case VCD_CODEC_VC1: case VCD_CODEC_VC1_RCV: - { - n_index = 4; - break; - } + /* vidc_720p_vc1_dec_mc.fw - untested */ + break; + case VCD_CODEC_MPEG2: + /* vidc_720p_mp2_dec_mc.fw - untested */ + break; + case VCD_CODEC_H263: + return &vcd_fw_table.fw[2]; default: - { - b_return = FALSE; - break; - } + break; } } else { - switch (e_codec) { + switch (codec) { case VCD_CODEC_H263: case VCD_CODEC_MPEG4: - { - n_index = 5; - break; - } + return &vcd_fw_table.fw[4]; case VCD_CODEC_H264: - { - n_index = 6; - break; - } + return &vcd_fw_table.fw[5]; default: - { - b_return = FALSE; - break; - } + break; } } - - if (!b_return) - return b_return; - - if (!b_add && - vcd_firmware.a_active_fw_img[n_index] - ) { - --vcd_firmware.a_active_fw_img[n_index]; - return b_return; - } - - for (n_loop_count = 0; n_loop_count < VCDFW_TOTALNUM_IMAGE; - ++n_loop_count) { - if (vcd_firmware.a_active_fw_img[n_loop_count]) - ++n_active_fw; - } - - if (n_active_fw < VCDFW_MAX_NO_IMAGE || - vcd_firmware.a_active_fw_img[n_index] > 0) { - ++vcd_firmware.a_active_fw_img[n_index]; - } else { - b_return = FALSE; - } - return b_return; + return NULL; } -void vcd_fw_release(void) +bool vcd_fw_is_codec_supported(bool is_decode, enum vcd_codec codec) { - ddl_pmem_free(vcd_firmware.boot_code); - ddl_pmem_free(vcd_firmware.enc_mpeg4); - ddl_pmem_free(vcd_firmware.encH264); - ddl_pmem_free(vcd_firmware.dec_mpeg4); - ddl_pmem_free(vcd_firmware.decH264); - ddl_pmem_free(vcd_firmware.decH263); - ddl_pmem_free(vcd_firmware.dec_mpeg2); - ddl_pmem_free(vcd_firmware.dec_vc1); + return vcd_fw_get_fw(is_decode, codec) != NULL; } diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.h index 27263ad5c765c..467765ab285bb 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.h +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_firmware.h @@ -28,40 +28,25 @@ */ #ifndef _VCD_DDL_FIRMWARE_H_ #define _VCD_DDL_FIRMWARE_H_ -#include "vcd_property.h" - -#define VCD_FW_BIG_ENDIAN 0x0 -#define VCD_FW_LITTLE_ENDIAN 0x1 - -struct vcd_fw_details_type { - enum vcd_codec_type e_codec; - u32 *p_fw_buffer_addr; - u32 n_fw_size; -}; -#define VCD_FW_PROP_BASE 0x0 +#include -#define VCD_FW_ENDIAN (VCD_FW_PROP_BASE + 0x1) -#define VCD_FW_BOOTCODE (VCD_FW_PROP_BASE + 0x2) -#define VCD_FW_DECODE (VCD_FW_PROP_BASE + 0x3) -#define VCD_FW_ENCODE (VCD_FW_PROP_BASE + 0x4) +#include "vcd_property.h" -extern unsigned char *vid_c_command_control_fw; -extern u32 vid_c_command_control_fw_size; -extern unsigned char *vid_c_mpg4_dec_fw; -extern u32 vid_c_mpg4_dec_fw_size; -extern unsigned char *vid_c_h263_dec_fw; -extern u32 vid_c_h263_dec_fw_size; -extern unsigned char *vid_c_h264_dec_fw; -extern u32 vid_c_h264_dec_fw_size; -extern unsigned char *vid_c_mpg4_enc_fw; -extern u32 vid_c_mpg4_enc_fw_size; -extern unsigned char *vid_c_h264_enc_fw; -extern u32 vid_c_h264_enc_fw_size; +struct vcd_firmware { + const char *filename; + bool change_endian; + phys_addr_t phys_addr; + void *virt_addr; + size_t sz; /* real size of firmware (unknown until load time) */ + size_t max_sz; /* size for allocation at init time */ +}; -u32 vcd_fw_init(void); -u32 vcd_get_fw_property(u32 prop_id, void *prop_details); -u32 vcd_fw_transact(u32 b_add, u32 b_decoding, enum vcd_codec_type e_codec); -void vcd_fw_release(void); +int vcd_fw_init(struct device *dev); +void vcd_fw_exit(void); +int vcd_fw_prepare_all(void); +struct vcd_firmware *vcd_fw_get_boot_fw(void); +struct vcd_firmware *vcd_fw_get_fw(bool is_decode, enum vcd_codec codec); +bool vcd_fw_is_codec_supported(bool is_decode, enum vcd_codec codec); #endif diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_hal.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_hal.c index 76f470a1d0975..1b1b488bb22f3 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl_hal.c +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_hal.c @@ -27,25 +27,20 @@ #define DBG(x...) #endif -void ddl_core_init(struct ddl_context_type *p_ddl_context) +void ddl_core_init(struct ddl_context *ddl_context) { char *psz_version; - struct vcd_fw_details_type fw_details; - u32 fw_endianness; - enum vidc_720p_endian_type e_dma_endian; - u32 b_interrupt_off; - enum vidc_720p_interrupt_level_selection_type e_interrupt_sel; + enum vidc_720p_endian_type dma_endian; + u32 interrupt_off; + enum vidc_720p_interrupt_level_selection_type interrupt_sel; u32 intr_mask = 0x0; + struct vcd_firmware *vcd_fw; - vcd_get_fw_property(VCD_FW_BOOTCODE, &fw_details); - vcd_get_fw_property(VCD_FW_ENDIAN, &fw_endianness); - if (fw_endianness == VCD_FW_BIG_ENDIAN) - e_dma_endian = VIDC_720P_BIG_ENDIAN; - else - e_dma_endian = VIDC_720P_LITTLE_ENDIAN; + vcd_fw = vcd_fw_get_boot_fw(); + dma_endian = VIDC_720P_BIG_ENDIAN; /* use default endian */ - b_interrupt_off = FALSE; - e_interrupt_sel = VIDC_720P_INTERRUPT_LEVEL_SEL; + interrupt_off = false; + interrupt_sel = VIDC_720P_INTERRUPT_LEVEL_SEL; intr_mask |= VIDC_720P_INTR_BUFFER_FULL; intr_mask |= VIDC_720P_INTR_FW_DONE; @@ -54,708 +49,543 @@ void ddl_core_init(struct ddl_context_type *p_ddl_context) vidc_720p_do_sw_reset(); - vidc_720p_init(&psz_version, - fw_details.n_fw_size, - fw_details.p_fw_buffer_addr, - e_dma_endian, - b_interrupt_off, e_interrupt_sel, intr_mask); + vidc_720p_init(&psz_version, vcd_fw->sz, vcd_fw->phys_addr, dma_endian, + interrupt_off, interrupt_sel, intr_mask); return; } -void ddl_core_start_cpu(struct ddl_context_type *p_ddl_context) +void ddl_core_start_cpu(struct ddl_context *ddl_context) { - u32 fw_endianness; - enum vidc_720p_endian_type e_dma_endian; + enum vidc_720p_endian_type dma_endian; u32 dbg_core_dump_buf_size = 0; - vcd_get_fw_property(VCD_FW_ENDIAN, &fw_endianness); - if (fw_endianness == VCD_FW_BIG_ENDIAN) - e_dma_endian = VIDC_720P_LITTLE_ENDIAN; - else - e_dma_endian = VIDC_720P_BIG_ENDIAN; + dma_endian = VIDC_720P_LITTLE_ENDIAN; /* use reverse endian */ - ddl_move_command_state(p_ddl_context, DDL_CMD_CPU_RESET); + ddl_move_command_state(ddl_context, DDL_CMD_CPU_RESET); - DBG("VSP_BUF_ADDR_SIZE %d", - p_ddl_context->context_buf_addr.n_buffer_size); - if (p_ddl_context->enable_dbg_core_dump) { - dbg_core_dump_buf_size = p_ddl_context->dbg_core_dump. - n_buffer_size; + DBG("VSP_BUF_ADDR_SIZE %d", ddl_context->context_buf_addr.size); + if (ddl_context->enable_dbg_core_dump) { + dbg_core_dump_buf_size = ddl_context->dbg_core_dump.size; } - vidc_720p_start_cpu(e_dma_endian, - p_ddl_context->context_buf_addr.p_align_physical_addr, - p_ddl_context->dbg_core_dump.p_align_physical_addr, - dbg_core_dump_buf_size); - - VIDC_DEBUG_REGISTER_LOG; + vidc_720p_start_cpu(dma_endian, ddl_context->context_buf_addr.phys_addr, + ddl_context->dbg_core_dump.phys_addr, dbg_core_dump_buf_size); } -void ddl_channel_set(struct ddl_client_context_type *p_ddl) +void ddl_channel_set(struct ddl_client_context *ddl) { - enum vidc_720p_enc_dec_selection_type e_enc_dec_sel; - enum vidc_720p_codec_type e_codec; - enum vcd_codec_type *p_codec; - u32 fw_property_id; - struct vcd_fw_details_type fw_details; - - if (p_ddl->b_decoding) { - e_enc_dec_sel = VIDC_720P_DECODER; - fw_property_id = VCD_FW_DECODE; - p_codec = &(p_ddl->codec_data.decoder.codec_type.e_codec); + enum vidc_720p_enc_dec_selection_type enc_dec_sel; + enum vidc_720p_codec_type codec; + enum vcd_codec vcd_codec; + struct vcd_firmware *vcd_fw; + + if (ddl->decoding) { + enc_dec_sel = VIDC_720P_DECODER; + vcd_codec = ddl->codec_data.decoder.codec_type.codec; } else { - e_enc_dec_sel = VIDC_720P_ENCODER; - fw_property_id = VCD_FW_ENCODE; - p_codec = &(p_ddl->codec_data.encoder.codec_type.e_codec); + enc_dec_sel = VIDC_720P_ENCODER; + vcd_codec = ddl->codec_data.encoder.codec_type.codec; } - switch (*p_codec) { + switch (vcd_codec) { default: case VCD_CODEC_MPEG4: - { - e_codec = VIDC_720P_MPEG4; - - if (p_ddl->b_decoding) { - vidc_720p_decode_set_mpeg4_data_partitionbuffer - (p_ddl->p_ddl_context->data_partition_tempbuf. - p_align_physical_addr); - } - - break; + codec = VIDC_720P_MPEG4; + if (ddl->decoding) { + vidc_720p_decode_set_mpeg4_data_partitionbuffer( + ddl->ddl_context->data_partition_tempbuf.phys_addr); } + break; case VCD_CODEC_H264: - { - e_codec = VIDC_720P_H264; - break; - } + codec = VIDC_720P_H264; + break; case VCD_CODEC_DIVX_4: case VCD_CODEC_DIVX_5: case VCD_CODEC_DIVX_6: - { - e_codec = VIDC_720P_DIVX; - break; - } + codec = VIDC_720P_DIVX; + break; case VCD_CODEC_XVID: - { - e_codec = VIDC_720P_XVID; - break; - } + codec = VIDC_720P_XVID; + break; case VCD_CODEC_H263: - { - e_codec = VIDC_720P_H263; - break; - } + codec = VIDC_720P_H263; + break; case VCD_CODEC_MPEG2: - { - e_codec = VIDC_720P_MPEG2; - break; - } + codec = VIDC_720P_MPEG2; + break; case VCD_CODEC_VC1: case VCD_CODEC_VC1_RCV: - { - e_codec = VIDC_720P_VC1; - break; - } + codec = VIDC_720P_VC1; + break; } - fw_details.e_codec = *p_codec; - vcd_get_fw_property(fw_property_id, &fw_details); - VIDC_DEBUG_REGISTER_LOG; + vcd_fw = vcd_fw_get_fw(ddl->decoding, vcd_codec); - ddl_move_command_state(p_ddl->p_ddl_context, DDL_CMD_CHANNEL_SET); - ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_CHDONE); + ddl_move_command_state(ddl->ddl_context, DDL_CMD_CHANNEL_SET); + ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_CHDONE); - vidc_720p_set_channel(p_ddl->n_channel_id, - e_enc_dec_sel, - e_codec, - fw_details.p_fw_buffer_addr, - fw_details.n_fw_size); + vidc_720p_set_channel(ddl->channel_id, enc_dec_sel, codec, + vcd_fw->phys_addr, vcd_fw->sz); } -void ddl_decode_init_codec(struct ddl_client_context_type *p_ddl) +void ddl_decode_init_codec(struct ddl_client_context *ddl) { - u32 n_seq_h = 0, n_seq_e = 0, n_start_byte_num = 0; - struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); - struct vcd_sequence_hdr_type *p_seq_hdr = &p_decoder->decode_config; + u32 seq_h = 0, seq_e = 0, start_byte_num = 0; + struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder); + struct vcd_phys_sequence_hdr *seq_hdr = &decoder->decode_config; enum vidc_720p_memory_access_method_type mem_access_method; - ddl_metadata_enable(p_ddl); + ddl_metadata_enable(ddl); - vidc_720p_decode_set_error_control(TRUE); + vidc_720p_decode_set_error_control(true); - vidc_720p_decode_set_mpeg4Post_filter(p_decoder->post_filter. - b_post_filter); - if (p_decoder->codec_type.e_codec == VCD_CODEC_VC1_RCV) { - vidc_720p_set_frame_size(p_decoder->client_frame_size.n_width, - p_decoder->client_frame_size.n_height); + vidc_720p_decode_set_mpeg4Post_filter(decoder->post_filter.post_filter); + if (decoder->codec_type.codec == VCD_CODEC_VC1_RCV) { + vidc_720p_set_frame_size(decoder->client_frame_size.width, + decoder->client_frame_size.height); } else { vidc_720p_set_frame_size(0x0, 0x0); } - switch (p_decoder->buf_format.e_buffer_format) { + switch (decoder->buf_format.buffer_format) { default: case VCD_BUFFER_FORMAT_NV12: - { - mem_access_method = VIDC_720P_TILE_LINEAR; - break; - } + mem_access_method = VIDC_720P_TILE_LINEAR; + break; case VCD_BUFFER_FORMAT_TILE_4x2: - { - mem_access_method = VIDC_720P_TILE_64x32; - break; - } + mem_access_method = VIDC_720P_TILE_64x32; + break; } - VIDC_LOG_STRING("HEADER-PARSE-START"); - VIDC_DEBUG_REGISTER_LOG; - n_seq_h = (u32) p_seq_hdr->p_sequence_header; - n_start_byte_num = 8 - (n_seq_h & DDL_STREAMBUF_ALIGN_GUARD_BYTES); - n_seq_e = n_seq_h + p_seq_hdr->n_sequence_header_len; - n_seq_h &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES); - DDL_PADDING_HACK(n_seq_e); - - ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE); - ddl_move_command_state(p_ddl->p_ddl_context, DDL_CMD_HEADER_PARSE); - - vidc_720p_decode_bitstream_header(p_ddl->n_channel_id, - p_seq_hdr->n_sequence_header_len, - n_start_byte_num, - n_seq_h, - n_seq_e, - mem_access_method); + pr_debug("HEADER-PARSE-START\n"); + + seq_h = seq_hdr->addr; + start_byte_num = 8 - (seq_h & DDL_STREAMBUF_ALIGN_GUARD_BYTES); + seq_e = seq_h + seq_hdr->sz; + seq_h &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES); + DDL_PADDING_HACK(seq_e); + + ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE); + ddl_move_command_state(ddl->ddl_context, DDL_CMD_HEADER_PARSE); + + vidc_720p_decode_bitstream_header(ddl->channel_id, seq_hdr->sz, + start_byte_num, seq_h, seq_e, mem_access_method); } -void ddl_decode_dynamic_property(struct ddl_client_context_type *p_ddl, - u32 b_enable) +void ddl_decode_dynamic_property(struct ddl_client_context *ddl, u32 enable) { - uint8_t *p_temp = NULL; - u32 n_extra_datastart = 0; - struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); - struct vcd_frame_data_type *p_bit_stream = - &(p_ddl->input_frame.vcd_frm); - - if (!b_enable) { - if (p_decoder->b_dynmic_prop_change_req) { - p_decoder->b_dynmic_prop_change_req = FALSE; + struct ddl_decoder_data *decoder = &ddl->codec_data.decoder; + struct vcd_frame_data *bit_stream = &ddl->input_frame.vcd_frm; + + if (!enable) { + if (decoder->dynmic_prop_change_req) { + decoder->dynmic_prop_change_req = false; vidc_720p_decode_dynamic_req_reset(); } return; } - if ((p_decoder->n_dynamic_prop_change & - DDL_DEC_REQ_OUTPUT_FLUSH)) { - p_decoder->b_dynmic_prop_change_req = TRUE; - p_decoder->n_dynamic_prop_change &= ~(DDL_DEC_REQ_OUTPUT_FLUSH); - p_decoder->dpb_mask.n_hw_mask = 0; + if ((decoder->dynamic_prop_change & DDL_DEC_REQ_OUTPUT_FLUSH)) { + decoder->dynmic_prop_change_req = true; + decoder->dynamic_prop_change &= ~(DDL_DEC_REQ_OUTPUT_FLUSH); + decoder->dpb_mask.hw_mask = 0; vidc_720p_decode_dynamic_req_set(VIDC_720P_FLUSH_REQ); } - if (((p_decoder->n_meta_data_enable_flag & VCD_METADATA_PASSTHROUGH)) - && ((VCD_FRAME_FLAG_EXTRADATA & p_bit_stream->n_flags)) - ) { - - p_temp = ((uint8_t *)p_bit_stream->p_physical + - p_bit_stream->n_offset + - p_bit_stream->n_data_len + 3); + if ((decoder->meta_data_enable_flag & VCD_METADATA_PASSTHROUGH) && + (VCD_FRAME_FLAG_EXTRADATA & bit_stream->flags)) { + phys_addr_t extra_datastart = bit_stream->phys_addr + + bit_stream->offset + bit_stream->data_len; + extra_datastart = (extra_datastart + 3) & ~0x03; - n_extra_datastart = (u32) ((u32)p_temp & ~3); - p_decoder->b_dynmic_prop_change_req = TRUE; + decoder->dynmic_prop_change_req = true; - vidc_720p_decode_setpassthrough_start(n_extra_datastart); + vidc_720p_decode_setpassthrough_start(extra_datastart); vidc_720p_decode_dynamic_req_set(VIDC_720P_EXTRADATA); } } -void ddl_encode_dynamic_property(struct ddl_client_context_type *p_ddl, - u32 b_enable) +void ddl_encode_dynamic_property(struct ddl_client_context *ddl, u32 enable) { - struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); - u32 n_enc_param_change = 0; + struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder); + u32 enc_param_change = 0; - if (!b_enable) { - if (p_encoder->b_dynmic_prop_change_req) { - p_encoder->b_dynmic_prop_change_req = FALSE; - p_encoder->n_ext_enc_control_val &= + if (!enable) { + if (encoder->dynmic_prop_change_req) { + encoder->dynmic_prop_change_req = false; + encoder->ext_enc_control_val &= ~(VIDC_720P_ENC_IFRAME_REQ); vidc_720p_encode_set_control_param - (p_encoder->n_ext_enc_control_val); - vidc_720p_encoder_set_param_change(n_enc_param_change); + (encoder->ext_enc_control_val); + vidc_720p_encoder_set_param_change(enc_param_change); } return; } - if ((p_encoder->n_dynamic_prop_change & DDL_ENC_REQ_IFRAME)) { - p_encoder->n_dynamic_prop_change &= ~(DDL_ENC_REQ_IFRAME); - p_encoder->n_ext_enc_control_val |= VIDC_720P_ENC_IFRAME_REQ; + if ((encoder->dynamic_prop_change & DDL_ENC_REQ_IFRAME)) { + encoder->dynamic_prop_change &= ~(DDL_ENC_REQ_IFRAME); + encoder->ext_enc_control_val |= VIDC_720P_ENC_IFRAME_REQ; vidc_720p_encode_set_control_param - (p_encoder->n_ext_enc_control_val); + (encoder->ext_enc_control_val); } - if ((p_encoder->n_dynamic_prop_change & DDL_ENC_CHANGE_BITRATE)) { + if ((encoder->dynamic_prop_change & DDL_ENC_CHANGE_BITRATE)) { vidc_720p_encode_set_bit_rate( - p_encoder->target_bit_rate.n_target_bitrate); - n_enc_param_change |= VIDC_720P_ENC_BITRATE_CHANGE; - p_encoder->n_dynamic_prop_change &= ~(DDL_ENC_CHANGE_BITRATE); + encoder->target_bit_rate.target_bitrate); + enc_param_change |= VIDC_720P_ENC_BITRATE_CHANGE; + encoder->dynamic_prop_change &= ~(DDL_ENC_CHANGE_BITRATE); } - if ((p_encoder->n_dynamic_prop_change & DDL_ENC_CHANGE_IPERIOD)) { - vidc_720p_encode_set_i_period - (p_encoder->i_period.n_p_frames); - n_enc_param_change |= VIDC_720P_ENC_IPERIOD_CHANGE; - p_encoder->n_dynamic_prop_change &= ~(DDL_ENC_CHANGE_IPERIOD); + if ((encoder->dynamic_prop_change & DDL_ENC_CHANGE_IPERIOD)) { + vidc_720p_encode_set_i_period(encoder->period.frames); + enc_param_change |= VIDC_720P_ENC_IPERIOD_CHANGE; + encoder->dynamic_prop_change &= ~(DDL_ENC_CHANGE_IPERIOD); } - if ((p_encoder->n_dynamic_prop_change & + if ((encoder->dynamic_prop_change & DDL_ENC_CHANGE_FRAMERATE)) { vidc_720p_encode_set_fps - ((p_encoder->frame_rate.n_fps_numerator * 1000) / - p_encoder->frame_rate.n_fps_denominator); - n_enc_param_change |= VIDC_720P_ENC_FRAMERATE_CHANGE; - p_encoder->n_dynamic_prop_change &= ~(DDL_ENC_CHANGE_FRAMERATE); + ((encoder->frame_rate.fps_numerator * 1000) / + encoder->frame_rate.fps_denominator); + enc_param_change |= VIDC_720P_ENC_FRAMERATE_CHANGE; + encoder->dynamic_prop_change &= ~(DDL_ENC_CHANGE_FRAMERATE); } - if (n_enc_param_change) - vidc_720p_encoder_set_param_change(n_enc_param_change); + if (enc_param_change) + vidc_720p_encoder_set_param_change(enc_param_change); } -static void ddl_encode_set_profile_level(struct ddl_client_context_type *p_ddl) +static void ddl_encode_set_profile_level(struct ddl_client_context *ddl) { - struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); + struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder); u32 profile; - u32 n_level; + u32 level; - switch (p_encoder->profile.e_profile) { + switch (encoder->profile.profile) { default: case VCD_PROFILE_MPEG4_SP: - { - profile = VIDC_720P_PROFILE_MPEG4_SP; - break; - } + profile = VIDC_720P_PROFILE_MPEG4_SP; + break; case VCD_PROFILE_MPEG4_ASP: - { - profile = VIDC_720P_PROFILE_MPEG4_ASP; - break; - } + profile = VIDC_720P_PROFILE_MPEG4_ASP; + break; case VCD_PROFILE_H264_BASELINE: - { - profile = VIDC_720P_PROFILE_H264_BASELINE; - break; - } + profile = VIDC_720P_PROFILE_H264_BASELINE; + break; case VCD_PROFILE_H264_MAIN: - { - profile = VIDC_720P_PROFILE_H264_MAIN; - break; - } + profile = VIDC_720P_PROFILE_H264_MAIN; + break; case VCD_PROFILE_H264_HIGH: - { - profile = VIDC_720P_PROFILE_H264_HIGH; - break; - } + profile = VIDC_720P_PROFILE_H264_HIGH; + break; case VCD_PROFILE_H263_BASELINE: - { - profile = VIDC_720P_PROFILE_H263_BASELINE; - break; - } + profile = VIDC_720P_PROFILE_H263_BASELINE; + break; } - switch (p_encoder->level.e_level) { + switch (encoder->level.level) { default: case VCD_LEVEL_MPEG4_0: - { - n_level = VIDC_720P_MPEG4_LEVEL0; - break; - } + level = VIDC_720P_MPEG4_LEVEL0; + break; case VCD_LEVEL_MPEG4_0b: - { - n_level = VIDC_720P_MPEG4_LEVEL0b; - break; - } + level = VIDC_720P_MPEG4_LEVEL0b; + break; case VCD_LEVEL_MPEG4_1: - { - n_level = VIDC_720P_MPEG4_LEVEL1; - break; - } + level = VIDC_720P_MPEG4_LEVEL1; + break; case VCD_LEVEL_MPEG4_2: - { - n_level = VIDC_720P_MPEG4_LEVEL2; - break; - } + level = VIDC_720P_MPEG4_LEVEL2; + break; case VCD_LEVEL_MPEG4_3: - { - n_level = VIDC_720P_MPEG4_LEVEL3; - break; - } + level = VIDC_720P_MPEG4_LEVEL3; + break; case VCD_LEVEL_MPEG4_3b: - { - n_level = VIDC_720P_MPEG4_LEVEL3b; - break; - } - + level = VIDC_720P_MPEG4_LEVEL3b; + break; case VCD_LEVEL_MPEG4_4: case VCD_LEVEL_MPEG4_4a: - { - n_level = VIDC_720P_MPEG4_LEVEL4a; - break; - } + level = VIDC_720P_MPEG4_LEVEL4a; + break; case VCD_LEVEL_MPEG4_5: - { - n_level = VIDC_720P_MPEG4_LEVEL5; - break; - } + level = VIDC_720P_MPEG4_LEVEL5; + break; case VCD_LEVEL_MPEG4_6: - { - n_level = VIDC_720P_MPEG4_LEVEL6; - break; - } + level = VIDC_720P_MPEG4_LEVEL6; + break; case VCD_LEVEL_H264_1: - { - n_level = VIDC_720P_H264_LEVEL1; - break; - } + level = VIDC_720P_H264_LEVEL1; + break; case VCD_LEVEL_H264_1b: - { - n_level = VIDC_720P_H264_LEVEL1b; - break; - } + level = VIDC_720P_H264_LEVEL1b; + break; case VCD_LEVEL_H264_1p1: - { - n_level = VIDC_720P_H264_LEVEL1p1; - break; - } + level = VIDC_720P_H264_LEVEL1p1; + break; case VCD_LEVEL_H264_1p2: - { - n_level = VIDC_720P_H264_LEVEL1p2; - break; - } + level = VIDC_720P_H264_LEVEL1p2; + break; case VCD_LEVEL_H264_1p3: - { - n_level = VIDC_720P_H264_LEVEL1p3; - break; - } + level = VIDC_720P_H264_LEVEL1p3; + break; case VCD_LEVEL_H264_2: - { - n_level = VIDC_720P_H264_LEVEL2; - break; - } + level = VIDC_720P_H264_LEVEL2; + break; case VCD_LEVEL_H264_2p1: - { - n_level = VIDC_720P_H264_LEVEL2p1; - break; - } + level = VIDC_720P_H264_LEVEL2p1; + break; case VCD_LEVEL_H264_2p2: - { - n_level = VIDC_720P_H264_LEVEL2p2; - break; - } + level = VIDC_720P_H264_LEVEL2p2; + break; case VCD_LEVEL_H264_3: - { - n_level = VIDC_720P_H264_LEVEL3; - break; - } + level = VIDC_720P_H264_LEVEL3; + break; case VCD_LEVEL_H264_3p1: - { - n_level = VIDC_720P_H264_LEVEL3p1; - break; - } + level = VIDC_720P_H264_LEVEL3p1; + break; case VCD_LEVEL_H263_10: - { - n_level = VIDC_720P_H263_LEVEL10; - break; - } + level = VIDC_720P_H263_LEVEL10; + break; case VCD_LEVEL_H263_20: - { - n_level = VIDC_720P_H263_LEVEL20; - break; - } + level = VIDC_720P_H263_LEVEL20; + break; case VCD_LEVEL_H263_30: - { - n_level = VIDC_720P_H263_LEVEL30; - break; - } + level = VIDC_720P_H263_LEVEL30; + break; case VCD_LEVEL_H263_40: - { - n_level = VIDC_720P_H263_LEVEL40; - break; - } + level = VIDC_720P_H263_LEVEL40; + break; case VCD_LEVEL_H263_45: - { - n_level = VIDC_720P_H263_LEVEL45; - break; - } + level = VIDC_720P_H263_LEVEL45; + break; case VCD_LEVEL_H263_50: - { - n_level = VIDC_720P_H263_LEVEL50; - break; - } + level = VIDC_720P_H263_LEVEL50; + break; case VCD_LEVEL_H263_60: - { - n_level = VIDC_720P_H263_LEVEL60; - break; - } + level = VIDC_720P_H263_LEVEL60; + break; case VCD_LEVEL_H263_70: - { - n_level = VIDC_720P_H263_LEVEL70; - break; - } + level = VIDC_720P_H263_LEVEL70; + break; } - vidc_720p_encode_set_profile(profile, n_level); + vidc_720p_encode_set_profile(profile, level); } -void ddl_encode_init_codec(struct ddl_client_context_type *p_ddl) +void ddl_encode_init_codec(struct ddl_client_context *ddl) { - struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); + struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder); enum vidc_720p_memory_access_method_type mem_access_method; - enum vidc_720p_DBConfig_type e_db_config; - enum vidc_720p_MSlice_selection_type e_m_slice_sel; + enum vidc_720p_DBConfig_type db_config; + enum vidc_720p_MSlice_selection_type m_slice_sel; - ddl_encode_set_profile_level(p_ddl); + ddl_encode_set_profile_level(ddl); vidc_720p_set_frame_size - (p_encoder->frame_size.n_width, p_encoder->frame_size.n_height); + (encoder->frame_size.width, encoder->frame_size.height); vidc_720p_encode_set_qp_params - (p_encoder->qp_range.n_max_qp, p_encoder->qp_range.n_min_qp); + (encoder->qp_range.max_qp, encoder->qp_range.min_qp); vidc_720p_encode_set_rc_config - (p_encoder->rc_level.b_frame_level_rc, - p_encoder->rc_level.b_mb_level_rc, - p_encoder->session_qp.n_i_frame_qp, - p_encoder->session_qp.n_p_frame_qp); - - if (p_encoder->n_r_cframe_skip) { - if (p_encoder->n_vb_vbuffer_size) { - p_encoder->n_ext_enc_control_val = (0x2 << 0x2) | - (p_encoder->n_vb_vbuffer_size << 0x10); + (encoder->rc_level.frame_level_rc, + encoder->rc_level.mb_level_rc, + encoder->session_qp.iframe_qp, + encoder->session_qp.frame_qp); + + if (encoder->r_cframe_skip) { + if (encoder->vb_vbuffer_size) { + encoder->ext_enc_control_val = (0x2 << 0x2) | + (encoder->vb_vbuffer_size << 0x10); } else - p_encoder->n_ext_enc_control_val = (0x1 << 2); + encoder->ext_enc_control_val = (0x1 << 2); } else - p_encoder->n_ext_enc_control_val = 0; + encoder->ext_enc_control_val = 0; vidc_720p_encode_set_fps - ((p_encoder->frame_rate.n_fps_numerator * 1000) / - p_encoder->frame_rate.n_fps_denominator); + ((encoder->frame_rate.fps_numerator * 1000) / + encoder->frame_rate.fps_denominator); vidc_720p_encode_set_vop_time( - p_encoder->vop_timing.n_vop_time_resolution, 0); + encoder->vop_timing.vop_time_resolution, 0); - if (p_encoder->rc_level.b_frame_level_rc) { + if (encoder->rc_level.frame_level_rc) { vidc_720p_encode_set_bit_rate - (p_encoder->target_bit_rate.n_target_bitrate); + (encoder->target_bit_rate.target_bitrate); vidc_720p_encode_set_frame_level_rc_params - (p_encoder->frame_level_rc.n_reaction_coeff); + (encoder->frame_level_rc.reaction_coeff); } - if (p_encoder->rc_level.b_mb_level_rc) { + if (encoder->rc_level.mb_level_rc) { vidc_720p_encode_set_mb_level_rc_params - (p_encoder->adaptive_rc.b_dark_region_as_flag, - p_encoder->adaptive_rc.b_smooth_region_as_flag, - p_encoder->adaptive_rc.b_static_region_as_flag, - p_encoder->adaptive_rc.b_activity_region_flag); + (encoder->adaptive_rc.dark_region_as_flag, + encoder->adaptive_rc.smooth_region_as_flag, + encoder->adaptive_rc.static_region_as_flag, + encoder->adaptive_rc.activity_region_flag); } - if (p_encoder->codec_type.e_codec == VCD_CODEC_MPEG4) { + if (encoder->codec_type.codec == VCD_CODEC_MPEG4) { vidc_720p_encode_set_short_header - (p_encoder->short_header.b_short_header); + (encoder->short_header.short_header); - if (p_encoder->n_hdr_ext_control) { + if (encoder->hdr_ext_control) { vidc_720p_encode_set_hec_period - (p_encoder->n_hdr_ext_control); - p_encoder->n_ext_enc_control_val |= (0x1 << 0x1); + (encoder->hdr_ext_control); + encoder->ext_enc_control_val |= (0x1 << 0x1); } } /* set extended encoder control settings */ vidc_720p_encode_set_control_param - (p_encoder->n_ext_enc_control_val); + (encoder->ext_enc_control_val); - if (p_encoder->codec_type.e_codec == VCD_CODEC_H264) { - enum vidc_720p_entropy_sel_type e_entropy_sel; - enum vidc_720p_cabac_model_type e_cabac_model_number; - switch (p_encoder->entropy_control.e_entropy_sel) { + if (encoder->codec_type.codec == VCD_CODEC_H264) { + enum vidc_720p_entropy_sel_type entropy_sel; + enum vidc_720p_cabac_model_type cabac_model_number; + switch (encoder->entropy_control.entropy_sel) { default: case VCD_ENTROPY_SEL_CAVLC: - { - e_entropy_sel = VIDC_720P_ENTROPY_SEL_CAVLC; - break; - } + entropy_sel = VIDC_720P_ENTROPY_SEL_CAVLC; + break; case VCD_ENTROPY_SEL_CABAC: - { - e_entropy_sel = VIDC_720P_ENTROPY_SEL_CABAC; - break; - } + entropy_sel = VIDC_720P_ENTROPY_SEL_CABAC; + break; } - switch (p_encoder->entropy_control.e_cabac_model) { + switch (encoder->entropy_control.cabac_model) { default: case VCD_CABAC_MODEL_NUMBER_0: - { - e_cabac_model_number = - VIDC_720P_CABAC_MODEL_NUMBER_0; - break; - } + cabac_model_number = VIDC_720P_CABAC_MODEL_NUMBER_0; + break; case VCD_CABAC_MODEL_NUMBER_1: - { - e_cabac_model_number = - VIDC_720P_CABAC_MODEL_NUMBER_1; - break; - } + cabac_model_number = VIDC_720P_CABAC_MODEL_NUMBER_1; + break; case VCD_CABAC_MODEL_NUMBER_2: - { - e_cabac_model_number = - VIDC_720P_CABAC_MODEL_NUMBER_2; - break; - } + cabac_model_number = VIDC_720P_CABAC_MODEL_NUMBER_2; + break; } vidc_720p_encode_set_entropy_control - (e_entropy_sel, e_cabac_model_number); - switch (p_encoder->db_control.e_db_config) { + (entropy_sel, cabac_model_number); + switch (encoder->db_control.db_config) { default: case VCD_DB_ALL_BLOCKING_BOUNDARY: - { - e_db_config = - VIDC_720P_DB_ALL_BLOCKING_BOUNDARY; - break; - } + db_config = VIDC_720P_DB_ALL_BLOCKING_BOUNDARY; + break; case VCD_DB_DISABLE: - { - e_db_config = - VIDC_720P_DB_DISABLE; - break; - } + db_config = VIDC_720P_DB_DISABLE; + break; case VCD_DB_SKIP_SLICE_BOUNDARY: - { - e_db_config = - VIDC_720P_DB_SKIP_SLICE_BOUNDARY; - break; - } + db_config = VIDC_720P_DB_SKIP_SLICE_BOUNDARY; + break; } vidc_720p_encode_set_db_filter_control - (e_db_config, - p_encoder->db_control.n_slice_alpha_offset, - p_encoder->db_control.n_slice_beta_offset); + (db_config, + encoder->db_control.slice_alpha_offset, + encoder->db_control.slice_beta_offset); } vidc_720p_encode_set_intra_refresh_mb_number - (p_encoder->intra_refresh.n_cir_mb_number); + (encoder->intra_refresh.cir_mb_number); - switch (p_encoder->multi_slice.e_m_slice_sel) { + switch (encoder->multi_slice.m_slice_sel) { default: case VCD_MSLICE_OFF: - e_m_slice_sel = VIDC_720P_MSLICE_OFF; + m_slice_sel = VIDC_720P_MSLICE_OFF; break; case VCD_MSLICE_BY_MB_COUNT: - { - e_m_slice_sel = VIDC_720P_MSLICE_BY_MB_COUNT; - break; - } + m_slice_sel = VIDC_720P_MSLICE_BY_MB_COUNT; + break; case VCD_MSLICE_BY_BYTE_COUNT: - { - e_m_slice_sel = VIDC_720P_MSLICE_BY_BYTE_COUNT; - break; - } + m_slice_sel = VIDC_720P_MSLICE_BY_BYTE_COUNT; + break; case VCD_MSLICE_BY_GOB: - { - e_m_slice_sel = VIDC_720P_MSLICE_BY_GOB; - break; - } + m_slice_sel = VIDC_720P_MSLICE_BY_GOB; + break; } - vidc_720p_encode_set_multi_slice_info - (e_m_slice_sel, p_encoder->multi_slice.n_m_slice_size); - - vidc_720p_encode_set_dpb_buffer - (p_encoder->enc_dpb_addr.p_align_physical_addr, - p_encoder->enc_dpb_addr.n_buffer_size); - - VIDC_LOG1("ENC_DPB_ADDR_SIZE", p_encoder->enc_dpb_addr.n_buffer_size); - - vidc_720p_encode_set_i_period(p_encoder->i_period.n_p_frames); - - ddl_metadata_enable(p_ddl); - - if (p_encoder->seq_header.p_virtual_base_addr) { - u32 n_ext_buffer_start, n_ext_buffer_end, n_start_byte_num; - n_ext_buffer_start = - (u32) p_encoder->seq_header.p_align_physical_addr; - n_ext_buffer_end = - n_ext_buffer_start + p_encoder->seq_header.n_buffer_size; - n_start_byte_num = - (n_ext_buffer_start & DDL_STREAMBUF_ALIGN_GUARD_BYTES); - n_ext_buffer_start &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES); - n_ext_buffer_end &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES); - VIDC_LOG1("ENC_SEQHDR_ALLOC_SIZE", - p_encoder->seq_header.n_buffer_size); - vidc_720p_encode_set_seq_header_buffer(n_ext_buffer_start, - n_ext_buffer_end, - n_start_byte_num); + vidc_720p_encode_set_multi_slice_info(m_slice_sel, + encoder->multi_slice.m_slice_size); + + vidc_720p_encode_set_dpb_buffer(encoder->enc_dpb_addr.phys_addr, + encoder->enc_dpb_addr.size); + + pr_debug("ENC_DPB_ADDR_SIZE %u\n", encoder->enc_dpb_addr.size); + + vidc_720p_encode_set_i_period(encoder->period.frames); + + ddl_metadata_enable(ddl); + + if (encoder->seq_header.virt_addr) { + phys_addr_t ext_buffer_start; + phys_addr_t ext_buffer_end; + u32 start_byte_num; + ext_buffer_start = encoder->seq_header.phys_addr; + ext_buffer_end = ext_buffer_start + encoder->seq_header.size; + start_byte_num = ext_buffer_start & + DDL_STREAMBUF_ALIGN_GUARD_BYTES; + ext_buffer_start &= ~DDL_STREAMBUF_ALIGN_GUARD_BYTES; + ext_buffer_end &= ~DDL_STREAMBUF_ALIGN_GUARD_BYTES; + pr_debug("ENC_SEQHDR_ALLOC_SIZE %u\n", + encoder->seq_header.size); + vidc_720p_encode_set_seq_header_buffer(ext_buffer_start, + ext_buffer_end, start_byte_num); } - if (p_encoder->re_con_buf_format.e_buffer_format == + if (encoder->re_con_buf_format.buffer_format == VCD_BUFFER_FORMAT_NV12) mem_access_method = VIDC_720P_TILE_LINEAR; else mem_access_method = VIDC_720P_TILE_16x16; - ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE); - ddl_move_command_state(p_ddl->p_ddl_context, DDL_CMD_INIT_CODEC); + ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE); + ddl_move_command_state(ddl->ddl_context, DDL_CMD_INIT_CODEC); - vidc_720p_encode_init_codec(p_ddl->n_channel_id, mem_access_method); + vidc_720p_encode_init_codec(ddl->channel_id, mem_access_method); } -void ddl_channel_end(struct ddl_client_context_type *p_ddl) +void ddl_channel_end(struct ddl_client_context *ddl) { - VIDC_DEBUG_REGISTER_LOG; - - ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_CHEND); - ddl_move_command_state(p_ddl->p_ddl_context, DDL_CMD_CHANNEL_END); + ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_CHEND); + ddl_move_command_state(ddl->ddl_context, DDL_CMD_CHANNEL_END); - vidc_720p_submit_command(p_ddl->n_channel_id, VIDC_720P_CMD_CHEND); + vidc_720p_submit_command(ddl->channel_id, VIDC_720P_CMD_CHEND); } -void ddl_encode_frame_run(struct ddl_client_context_type *p_ddl) +void ddl_encode_frame_run(struct ddl_client_context *ddl) { - u32 n_ext_buffer_start, n_ext_buffer_end; - u32 n_y_addr, n_c_addr; - u32 n_start_byte_number = 0; - struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); - struct vcd_frame_data_type *p_stream = &(p_ddl->output_frame.vcd_frm); - - n_ext_buffer_start = (u32) p_stream->p_physical + p_stream->n_offset; - n_ext_buffer_end = ddl_encode_set_metadata_output_buf(p_ddl); - n_start_byte_number = - (n_ext_buffer_start & DDL_STREAMBUF_ALIGN_GUARD_BYTES); - if (n_start_byte_number) { - u32 n_upper_data, n_lower_data; - u32 *p_align_virtual_addr; - n_ext_buffer_start &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES); - p_align_virtual_addr = (u32 *) (((u32) p_stream->p_virtual + - p_stream->n_offset) - - n_start_byte_number); - n_upper_data = *p_align_virtual_addr; - p_align_virtual_addr++; - n_lower_data = *p_align_virtual_addr; - vidc_720p_encode_unalign_bitstream(n_upper_data, n_lower_data); + phys_addr_t ext_buffer_start; + phys_addr_t ext_buffer_end; + phys_addr_t y_addr; + phys_addr_t c_addr; + u32 start_byte_number; + struct ddl_encoder_data *encoder = &ddl->codec_data.encoder; + struct vcd_frame_data *stream = &ddl->output_frame.vcd_frm; + + ext_buffer_start = stream->phys_addr + stream->offset; + ext_buffer_end = ddl_encode_set_metadata_output_buf(ddl); + start_byte_number = ext_buffer_start & DDL_STREAMBUF_ALIGN_GUARD_BYTES; + if (start_byte_number) { + u32 *data; + ext_buffer_start &= ~DDL_STREAMBUF_ALIGN_GUARD_BYTES; + data = (u32 *)((u32)stream->virt_addr + stream->offset - + start_byte_number); + vidc_720p_encode_unalign_bitstream(data[0], data[1]); } - n_y_addr = (u32) p_ddl->input_frame.vcd_frm.p_physical + - p_ddl->input_frame.vcd_frm.n_offset; - n_c_addr = (n_y_addr + (p_encoder->frame_size.n_height * - p_encoder->frame_size.n_width)); - ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE); - ddl_move_command_state(p_ddl->p_ddl_context, DDL_CMD_ENCODE_FRAME); + y_addr = ddl->input_frame.vcd_frm.phys_addr + + ddl->input_frame.vcd_frm.offset; + c_addr = y_addr + encoder->frame_size.height * + encoder->frame_size.width; + ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE); + ddl_move_command_state(ddl->ddl_context, DDL_CMD_ENCODE_FRAME); - if (p_encoder->n_dynamic_prop_change) { - p_encoder->b_dynmic_prop_change_req = TRUE; - ddl_encode_dynamic_property(p_ddl, TRUE); + if (encoder->dynamic_prop_change) { + encoder->dynmic_prop_change_req = true; + ddl_encode_dynamic_property(ddl, true); } - vidc_720p_encode_set_vop_time( - p_encoder->vop_timing.n_vop_time_resolution, - p_ddl->input_frame.n_frm_delta - ); - - vidc_720p_encode_frame(p_ddl->n_channel_id, - n_ext_buffer_start, - n_ext_buffer_end, - n_start_byte_number, n_y_addr, n_c_addr); + vidc_720p_encode_set_vop_time(encoder->vop_timing.vop_time_resolution, + ddl->input_frame.frm_delta); + + vidc_720p_encode_frame(ddl->channel_id, ext_buffer_start, + ext_buffer_end, start_byte_number, y_addr, c_addr); } -u32 ddl_decode_set_buffers(struct ddl_client_context_type *p_ddl) +u32 ddl_decode_set_buffers(struct ddl_client_context *ddl) { - struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); - u32 n_comv_buf_size = DDL_COMV_BUFLINE_NO, n_comv_buf_no = 0; - u32 n_ref_buf_no = 0; + struct ddl_decoder_data *decoder = &(ddl->codec_data.decoder); + u32 comv_buf_size = DDL_COMV_BUFLINE_NO, comv_buf_no = 0; + u32 ref_buf_no = 0; - if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_DPB)) { - VIDC_LOG_STRING("STATE-CRITICAL"); + if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPB)) { + pr_debug("STATE-CRITICAL\n"); return VCD_ERR_FAIL; } - switch (p_decoder->codec_type.e_codec) { + switch (decoder->codec_type.codec) { default: case VCD_CODEC_DIVX_4: case VCD_CODEC_DIVX_5: @@ -763,179 +593,143 @@ u32 ddl_decode_set_buffers(struct ddl_client_context_type *p_ddl) case VCD_CODEC_XVID: case VCD_CODEC_MPEG2: case VCD_CODEC_MPEG4: - { - n_comv_buf_no = DDL_MPEG_COMV_BUF_NO; - n_ref_buf_no = DDL_MPEG_REFBUF_COUNT; - break; - } + comv_buf_no = DDL_MPEG_COMV_BUF_NO; + ref_buf_no = DDL_MPEG_REFBUF_COUNT; + break; case VCD_CODEC_H263: - { - n_comv_buf_no = DDL_H263_COMV_BUF_NO; - break; - } + comv_buf_no = DDL_H263_COMV_BUF_NO; + break; case VCD_CODEC_VC1: case VCD_CODEC_VC1_RCV: - { - n_comv_buf_no = - p_decoder->client_output_buf_req.n_actual_count + 1; - n_comv_buf_size = DDL_VC1_COMV_BUFLINE_NO; - break; - } + comv_buf_no = decoder->client_output_buf_req.actual_count + 1; + comv_buf_size = DDL_VC1_COMV_BUFLINE_NO; + break; case VCD_CODEC_H264: - { - n_comv_buf_no = - p_decoder->client_output_buf_req.n_actual_count; - break; - } + comv_buf_no = decoder->client_output_buf_req.actual_count; + break; } - if (n_comv_buf_no) { - n_comv_buf_size *= (n_comv_buf_no * - (((p_decoder->client_frame_size. - n_width + 15) >> 4)) * - (((p_decoder->client_frame_size. - n_height + 15) >> 4) + 1)); - ddl_pmem_alloc(&p_decoder->dpb_comv_buffer, n_comv_buf_size, - DDL_LINEAR_BUFFER_ALIGN_BYTES); - if (!p_decoder->dpb_comv_buffer.p_virtual_base_addr) { - VIDC_LOGERR_STRING - ("Dec_set_buf:Comv_buf_alloc_failed"); + if (comv_buf_no) { + comv_buf_size *= (comv_buf_no * + (((decoder->client_frame_size.width + 15) >> 4)) * + (((decoder->client_frame_size.height + 15) >> 4) + 1)); + if (!ddl_dma_alloc(&decoder->dpb_comv_buffer, comv_buf_size, npelly_dec_dpb)) { + pr_err("Dec_set_buf:Comv_buf_alloc_failed\n"); return VCD_ERR_ALLOC_FAIL; } - vidc_720p_decode_set_comv_buffer(p_decoder->dpb_comv_buffer. - p_align_physical_addr, - p_decoder->dpb_comv_buffer. - n_buffer_size); + vidc_720p_decode_set_comv_buffer(decoder->dpb_comv_buffer. + phys_addr, decoder->dpb_comv_buffer.size); } - p_decoder->ref_buffer.p_align_physical_addr = NULL; - if (n_ref_buf_no) { - u32 n_size, n_yuv_size, n_align_bytes; - n_yuv_size = ddl_get_yuv_buffer_size(&p_decoder-> - client_frame_size, &p_decoder->buf_format, - (!p_decoder->n_progressive_only)); - n_size = n_yuv_size * n_ref_buf_no; - if (p_decoder->buf_format.e_buffer_format == - VCD_BUFFER_FORMAT_NV12) - n_align_bytes = DDL_LINEAR_BUFFER_ALIGN_BYTES; - else - n_align_bytes = DDL_TILE_BUFFER_ALIGN_BYTES; - - ddl_pmem_alloc(&p_decoder->ref_buffer, n_size, n_align_bytes); - if (!p_decoder->ref_buffer.p_virtual_base_addr) { - ddl_pmem_free(p_decoder->dpb_comv_buffer); - VIDC_LOGERR_STRING - ("Dec_set_buf:mpeg_ref_buf_alloc_failed"); + decoder->ref_buffer.phys_addr = 0; + if (ref_buf_no) { + u32 size, yuv_size; + yuv_size = ddl_get_yuv_buffer_size(&decoder-> + client_frame_size, &decoder->buf_format, + (!decoder->progressive_only)); + size = yuv_size * ref_buf_no; + + if (!ddl_dma_alloc(&decoder->ref_buffer, size, npelly_dec_ref)) { + ddl_dma_free(&decoder->dpb_comv_buffer); + pr_err("Dec_set_buf:mpeg_ref_buf_alloc_failed\n"); return VCD_ERR_ALLOC_FAIL; } } - ddl_decode_set_metadata_output(p_decoder); + ddl_decode_set_metadata_output(decoder); - ddl_decoder_dpb_transact(p_decoder, NULL, DDL_DPB_OP_INIT); + ddl_decoder_dpb_transact(decoder, NULL, DDL_DPB_OP_INIT); - if (p_decoder->codec_type.e_codec == VCD_CODEC_H264) { - vidc_720p_decode_setH264VSPBuffer(p_decoder-> - h264Vsp_temp_buffer. - p_align_physical_addr); - VIDC_LOG1("VSP_BUF_ADDR_SIZE", - p_decoder->h264Vsp_temp_buffer.n_buffer_size); + if (decoder->codec_type.codec == VCD_CODEC_H264) { + vidc_720p_decode_setH264VSPBuffer( + decoder->h264Vsp_temp_buffer.phys_addr); + pr_debug("VSP_BUF_ADDR_SIZE %u\n", + decoder->h264Vsp_temp_buffer.size); } - ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_DPBDONE); - ddl_move_command_state(p_ddl->p_ddl_context, DDL_CMD_DECODE_SET_DPB); + ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_DPBDONE); + ddl_move_command_state(ddl->ddl_context, DDL_CMD_DECODE_SET_DPB); - vidc_720p_submit_command(p_ddl->n_channel_id, + vidc_720p_submit_command(ddl->channel_id, VIDC_720P_CMD_INITBUFFERS); return VCD_S_SUCCESS; } -void ddl_decode_frame_run(struct ddl_client_context_type *p_ddl) +void ddl_decode_frame_run(struct ddl_client_context *ddl) { - u32 n_ext_buffer_start = 0, n_ext_buffer_end = 0; - u32 n_start_byte_num = 8; - struct ddl_decoder_data_type *p_decoder = &p_ddl->codec_data.decoder; - struct vcd_frame_data_type *p_bit_stream = - &(p_ddl->input_frame.vcd_frm); - - if (!p_bit_stream->n_data_len || - !p_bit_stream->p_physical) { - ddl_decode_eos_run(p_ddl); + phys_addr_t ext_buffer_start; + phys_addr_t ext_buffer_end = 0; + u32 start_byte_num; + struct ddl_decoder_data *decoder = &ddl->codec_data.decoder; + struct vcd_frame_data *bit_stream = &ddl->input_frame.vcd_frm; + + if (!bit_stream->data_len || !bit_stream->phys_addr) { + ddl_decode_eos_run(ddl); return; } - ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE); + ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE); - ddl_decode_dynamic_property(p_ddl, TRUE); + ddl_decode_dynamic_property(ddl, true); - ddl_decoder_dpb_transact(p_decoder, NULL, DDL_DPB_OP_SET_MASK); + ddl_decoder_dpb_transact(decoder, NULL, DDL_DPB_OP_SET_MASK); - n_ext_buffer_start = (u32)p_bit_stream->p_physical + - p_bit_stream->n_offset; - n_start_byte_num = 8 - (n_ext_buffer_start & + ext_buffer_start = bit_stream->phys_addr + bit_stream->offset; + start_byte_num = 8 - (ext_buffer_start & DDL_STREAMBUF_ALIGN_GUARD_BYTES); - n_ext_buffer_end = n_ext_buffer_start + p_bit_stream->n_data_len; - n_ext_buffer_start &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES); - DDL_PADDING_HACK(n_ext_buffer_end); + ext_buffer_end = ext_buffer_start + bit_stream->data_len; + ext_buffer_start &= ~DDL_STREAMBUF_ALIGN_GUARD_BYTES; + DDL_PADDING_HACK(ext_buffer_end); - ddl_move_command_state(p_ddl->p_ddl_context, DDL_CMD_DECODE_FRAME); + ddl_move_command_state(ddl->ddl_context, DDL_CMD_DECODE_FRAME); - vidc_720p_decode_frame(p_ddl->n_channel_id, - n_ext_buffer_start, - n_ext_buffer_end, - p_bit_stream->n_data_len, - n_start_byte_num, p_bit_stream->n_ip_frm_tag); + vidc_720p_decode_frame(ddl->channel_id, ext_buffer_start, + ext_buffer_end, bit_stream->data_len, start_byte_num, + bit_stream->ip_frm_tag); } -void ddl_decode_eos_run(struct ddl_client_context_type *p_ddl) +void ddl_decode_eos_run(struct ddl_client_context *ddl) { - struct ddl_decoder_data_type *p_decoder = &p_ddl->codec_data.decoder; + struct ddl_decoder_data *decoder = &ddl->codec_data.decoder; - ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE); + ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE); - ddl_decode_dynamic_property(p_ddl, TRUE); + ddl_decode_dynamic_property(ddl, true); - ddl_decoder_dpb_transact(p_decoder, NULL, DDL_DPB_OP_SET_MASK); + ddl_decoder_dpb_transact(decoder, NULL, DDL_DPB_OP_SET_MASK); - p_decoder->b_dynmic_prop_change_req = TRUE; + decoder->dynmic_prop_change_req = true; - ddl_move_command_state(p_ddl->p_ddl_context, DDL_CMD_EOS); + ddl_move_command_state(ddl->ddl_context, DDL_CMD_EOS); - vidc_720p_issue_eos(p_ddl->n_channel_id); + vidc_720p_issue_eos(ddl->channel_id); } -u32 ddl_hal_engine_reset(struct ddl_context_type *p_ddl_context) +u32 ddl_hal_engine_reset(struct ddl_context *ddl_context) { - u32 b_eng_reset; - u32 n_channel_id = 0; - u32 fw_endianness; - enum vidc_720p_endian_type e_dma_endian; - enum vidc_720p_interrupt_level_selection_type e_interrupt_sel; + u32 eng_reset; + u32 channel_id = 0; + enum vidc_720p_endian_type dma_endian; + enum vidc_720p_interrupt_level_selection_type interrupt_sel; u32 intr_mask = 0x0; - if (p_ddl_context->p_current_ddl) - n_channel_id = p_ddl_context->p_current_ddl->n_channel_id; + if (ddl_context->current_ddl) + channel_id = ddl_context->current_ddl->channel_id; - e_interrupt_sel = VIDC_720P_INTERRUPT_LEVEL_SEL; + interrupt_sel = VIDC_720P_INTERRUPT_LEVEL_SEL; /* Enable all the supported interrupt */ intr_mask |= VIDC_720P_INTR_BUFFER_FULL; intr_mask |= VIDC_720P_INTR_FW_DONE; intr_mask |= VIDC_720P_INTR_DMA_DONE; intr_mask |= VIDC_720P_INTR_FRAME_DONE; - vcd_get_fw_property(VCD_FW_ENDIAN, &fw_endianness); - /* Reverse the endianness settings after boot code download */ - if (fw_endianness == VCD_FW_BIG_ENDIAN) - e_dma_endian = VIDC_720P_LITTLE_ENDIAN; - else - e_dma_endian = VIDC_720P_BIG_ENDIAN; + /* use reverse endian after boot code download */ + dma_endian = VIDC_720P_LITTLE_ENDIAN; /* Need to reset MFC silently */ - b_eng_reset = vidc_720p_engine_reset( - n_channel_id, - e_dma_endian, e_interrupt_sel, - intr_mask); - if (!b_eng_reset) { + eng_reset = vidc_720p_engine_reset(channel_id, dma_endian, + interrupt_sel, intr_mask); + if (!eng_reset) { /* call the hw fatal callback if engine reset fails */ - ddl_hw_fatal_cb(p_ddl_context); + ddl_hw_fatal_cb(ddl_context); } - return b_eng_reset ; + return eng_reset ; } diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_helper.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_helper.c index 7199758409229..17bf125ecd7e9 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl_helper.c +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_helper.c @@ -17,273 +17,202 @@ */ #include "video_core_type.h" - #include "vcd_ddl_utils.h" -DDL_INLINE struct ddl_context_type *ddl_get_context(void) +struct ddl_context *ddl_get_context(void) { - static struct ddl_context_type ddl_context; + static struct ddl_context ddl_context; return &ddl_context; } -DDL_INLINE void ddl_move_client_state(struct ddl_client_context_type *p_ddl, - enum ddl_client_state_type e_client_state) +void ddl_move_client_state(struct ddl_client_context *ddl, + enum ddl_client_state client_state) { - p_ddl->e_client_state = e_client_state; + ddl->client_state = client_state; } -DDL_INLINE void ddl_move_command_state(struct ddl_context_type *p_ddl_context, - enum ddl_cmd_state_type e_command_state) +void ddl_move_command_state(struct ddl_context *ddl_context, + enum ddl_cmd_state command_state) { - p_ddl_context->e_cmd_state = e_command_state; + ddl_context->cmd_state = command_state; } -u32 ddl_client_transact(u32 operation, - struct ddl_client_context_type **pddl_client) +u32 ddl_client_transact(u32 operation, struct ddl_client_context **pddl_client) { u32 ret_status = VCD_ERR_FAIL; - u32 n_counter; - struct ddl_context_type *p_ddl_context; + u32 i; + struct ddl_context *ddl_context; - p_ddl_context = ddl_get_context(); + ddl_context = ddl_get_context(); switch (operation) { case DDL_FREE_CLIENT: - { - if (pddl_client && *pddl_client) { - u32 n_channel_id; - n_channel_id = (*pddl_client)->n_channel_id; - if (n_channel_id < VCD_MAX_NO_CLIENT) { - p_ddl_context-> - a_ddl_clients[n_channel_id] = NULL; - } else { - VIDC_LOG_STRING("CHID_CORRUPTION"); - } - DDL_FREE(*pddl_client); - ret_status = VCD_S_SUCCESS; - } - break; + if (pddl_client && *pddl_client) { + u32 channel_id; + channel_id = (*pddl_client)->channel_id; + if (channel_id < VCD_MAX_NO_CLIENT) + ddl_context->ddl_clients[channel_id] = NULL; + else + pr_warn("CHID_CORRUPTION\n"); + kfree(*pddl_client); + *pddl_client = NULL; + ret_status = VCD_S_SUCCESS; } + break; case DDL_GET_CLIENT: - { - ret_status = VCD_ERR_MAX_CLIENT; - for (n_counter = 0; n_counter < VCD_MAX_NO_CLIENT && - ret_status == VCD_ERR_MAX_CLIENT; ++n_counter) { - if (!p_ddl_context->a_ddl_clients[n_counter]) { - *pddl_client = - (struct ddl_client_context_type *) - DDL_MALLOC(sizeof - (struct ddl_client_context_type) - ); - if (!*pddl_client) { - ret_status = VCD_ERR_ALLOC_FAIL; - } else { - DDL_MEMSET(*pddl_client, 0, - sizeof(struct - ddl_client_context_type)); - p_ddl_context-> - a_ddl_clients[n_counter] = - *pddl_client; - (*pddl_client)->n_channel_id = - n_counter; - (*pddl_client)->p_ddl_context = - p_ddl_context; - ret_status = VCD_S_SUCCESS; - } + ret_status = VCD_ERR_MAX_CLIENT; + for (i = 0; i < VCD_MAX_NO_CLIENT && ret_status == + VCD_ERR_MAX_CLIENT; ++i) { + if (!ddl_context->ddl_clients[i]) { + *pddl_client = (struct ddl_client_context *) + kzalloc((sizeof( + struct ddl_client_context)), + GFP_KERNEL); + if (!*pddl_client) { + ret_status = VCD_ERR_ALLOC_FAIL; + break; } + ddl_context->ddl_clients[i] = *pddl_client; + (*pddl_client)->channel_id = i; + (*pddl_client)->ddl_context = ddl_context; + ret_status = VCD_S_SUCCESS; } - break; } + break; case DDL_INIT_CLIENTS: - { - for (n_counter = 0; n_counter < VCD_MAX_NO_CLIENT; - ++n_counter) { - p_ddl_context->a_ddl_clients[n_counter] = NULL; - } - ret_status = VCD_S_SUCCESS; - break; - } + for (i = 0; i < VCD_MAX_NO_CLIENT; ++i) + ddl_context->ddl_clients[i] = NULL; + ret_status = VCD_S_SUCCESS; + break; case DDL_ACTIVE_CLIENT: - { - for (n_counter = 0; n_counter < VCD_MAX_NO_CLIENT; - ++n_counter) { - if (p_ddl_context->a_ddl_clients[n_counter]) { - ret_status = VCD_S_SUCCESS; - break; - } + for (i = 0; i < VCD_MAX_NO_CLIENT; ++i) { + if (ddl_context->ddl_clients[i]) { + ret_status = VCD_S_SUCCESS; + break; } - break; } + break; default: - { - ret_status = VCD_ERR_ILLEGAL_PARM; - break; - } + ret_status = VCD_ERR_ILLEGAL_PARM; + break; } return ret_status; } -u32 ddl_decoder_dpb_transact(struct ddl_decoder_data_type *p_decoder, - struct ddl_frame_data_type_tag *p_in_out_frame, - u32 n_operation) +u32 ddl_decoder_dpb_transact(struct ddl_decoder_data *dec, + struct ddl_frame_data_tag *in_out_frame, u32 operation) { u32 vcd_status = VCD_S_SUCCESS; - u32 n_loopc; - struct ddl_frame_data_type_tag *p_found_frame = NULL; - struct ddl_mask_type *p_dpb_mask = &p_decoder->dpb_mask; + u32 i; + struct ddl_frame_data_tag *found_frame = NULL; + struct ddl_mask *dpb_mask = &dec->dpb_mask; - switch (n_operation) { + switch (operation) { case DDL_DPB_OP_MARK_BUSY: case DDL_DPB_OP_MARK_FREE: - { - for (n_loopc = 0; !p_found_frame && - n_loopc < p_decoder->dp_buf.n_no_of_dec_pic_buf; - ++n_loopc) { - if (p_in_out_frame->vcd_frm.p_physical == - p_decoder->dp_buf. - a_dec_pic_buffers[n_loopc].vcd_frm. - p_physical) { - p_found_frame = - &(p_decoder->dp_buf. - a_dec_pic_buffers[n_loopc]); - break; - } + for (i = 0; i < dec->dp_buf.no_of_dec_pic_buf; ++i) { + if (in_out_frame->vcd_frm.phys_addr == dec->dp_buf. + dec_pic_buffers[i].vcd_frm.phys_addr) { + found_frame = &dec->dp_buf.dec_pic_buffers[i]; + break; } + } - if (p_found_frame) { - if (n_operation == DDL_DPB_OP_MARK_BUSY) { - p_dpb_mask->n_hw_mask &= - (~(0x1 << n_loopc)); - *p_in_out_frame = *p_found_frame; - } else if (n_operation == - DDL_DPB_OP_MARK_FREE) { - p_dpb_mask->n_client_mask |= - (0x1 << n_loopc); - *p_found_frame = *p_in_out_frame; - } - } else { - p_in_out_frame->vcd_frm.p_physical = NULL; - vcd_status = VCD_ERR_BAD_POINTER; - VIDC_LOG_STRING("BUF_NOT_FOUND"); - } + if (!found_frame) { + in_out_frame->vcd_frm.phys_addr = 0; + vcd_status = VCD_ERR_BAD_POINTER; + pr_debug("BUF_NOT_FOUND\n"); break; } - case DDL_DPB_OP_SET_MASK: - { - p_dpb_mask->n_hw_mask |= p_dpb_mask->n_client_mask; - p_dpb_mask->n_client_mask = 0; - vidc_720p_decode_set_dpb_release_buffer_mask - (p_dpb_mask->n_hw_mask); - break; + if (operation == DDL_DPB_OP_MARK_BUSY) { + dpb_mask->hw_mask &= ~(0x1 << i); + *in_out_frame = *found_frame; + } else if (operation == DDL_DPB_OP_MARK_FREE) { + dpb_mask->client_mask |= 0x1 << i; + *found_frame = *in_out_frame; } + + break; + case DDL_DPB_OP_SET_MASK: + dpb_mask->hw_mask |= dpb_mask->client_mask; + dpb_mask->client_mask = 0; + vidc_720p_decode_set_dpb_release_buffer_mask(dpb_mask->hw_mask); + break; case DDL_DPB_OP_INIT: - { - u32 n_dpb_size; - n_dpb_size = (!p_decoder->n_meta_data_offset) ? - p_decoder->dp_buf.a_dec_pic_buffers[0].vcd_frm. - n_alloc_len : p_decoder->n_meta_data_offset; - vidc_720p_decode_set_dpb_details(p_decoder->dp_buf. - n_no_of_dec_pic_buf, - n_dpb_size, - p_decoder->ref_buffer. - p_align_physical_addr); - for (n_loopc = 0; - n_loopc < p_decoder->dp_buf.n_no_of_dec_pic_buf; - ++n_loopc) { - vidc_720p_decode_set_dpb_buffers(n_loopc, - (u32 *) - p_decoder-> - dp_buf. - a_dec_pic_buffers - [n_loopc]. - vcd_frm. - p_physical); - VIDC_LOG1("DEC_DPB_BUFn_SIZE", - p_decoder->dp_buf. - a_dec_pic_buffers[n_loopc].vcd_frm. - n_alloc_len); - } - break; + { + size_t dpb_size = !dec->meta_data_offset ? + dec->dp_buf.dec_pic_buffers[0].vcd_frm.alloc_len : + dec->meta_data_offset; + vidc_720p_decode_set_dpb_details(dec->dp_buf.no_of_dec_pic_buf, + dpb_size, dec->ref_buffer.phys_addr); + for (i = 0; i < dec->dp_buf.no_of_dec_pic_buf; ++i) { + vidc_720p_decode_set_dpb_buffers(i, dec->dp_buf. + dec_pic_buffers[i].vcd_frm.phys_addr); + pr_debug("DEC_DPB_BUFn_SIZE %u\n", dec->dp_buf. + dec_pic_buffers[i].vcd_frm.alloc_len); } + break; + } case DDL_DPB_OP_RETRIEVE: - { - u32 n_position; - if (p_dpb_mask->n_client_mask) { - n_position = 0x1; - for (n_loopc = 0; - n_loopc < - p_decoder->dp_buf.n_no_of_dec_pic_buf - && !p_found_frame; ++n_loopc) { - if (p_dpb_mask-> - n_client_mask & n_position) { - p_found_frame = - &p_decoder->dp_buf. - a_dec_pic_buffers[n_loopc]; - p_dpb_mask->n_client_mask &= - ~(n_position); - } - n_position <<= 1; - } - } else if (p_dpb_mask->n_hw_mask) { - n_position = 0x1; - for (n_loopc = 0; - n_loopc < - p_decoder->dp_buf.n_no_of_dec_pic_buf - && !p_found_frame; ++n_loopc) { - if (p_dpb_mask->n_hw_mask - & n_position) { - p_found_frame = - &p_decoder->dp_buf. - a_dec_pic_buffers[n_loopc]; - p_dpb_mask->n_hw_mask &= - ~(n_position); - } - n_position <<= 1; - } - } - if (p_found_frame) - *p_in_out_frame = *p_found_frame; - else - p_in_out_frame->vcd_frm.p_physical = NULL; + { + u32 position; + u32 *mask; + if (dpb_mask->client_mask) { + mask = &dpb_mask->client_mask; + } else if (dpb_mask->hw_mask) { + mask = &dpb_mask->hw_mask; + } else { + in_out_frame->vcd_frm.phys_addr = 0; break; } + position = 0x1; + for (i = 0; i < dec->dp_buf.no_of_dec_pic_buf; ++i) { + if (*mask & position) { + found_frame = &dec->dp_buf.dec_pic_buffers[i]; + *mask &= ~position; + *in_out_frame = *found_frame; + break; + } + position <<= 1; + } + if (!found_frame) + in_out_frame->vcd_frm.phys_addr = 0; + break; + } } return vcd_status; } -void ddl_release_context_buffers(struct ddl_context_type *p_ddl_context) +void ddl_release_context_buffers(struct ddl_context *ddl_context) { - ddl_pmem_free(p_ddl_context->context_buf_addr); - ddl_pmem_free(p_ddl_context->db_line_buffer); - ddl_pmem_free(p_ddl_context->data_partition_tempbuf); - ddl_pmem_free(p_ddl_context->metadata_shared_input); - ddl_pmem_free(p_ddl_context->dbg_core_dump); - - vcd_fw_release(); + ddl_dma_free(&ddl_context->context_buf_addr); + ddl_dma_free(&ddl_context->db_line_buffer); + ddl_dma_free(&ddl_context->data_partition_tempbuf); + ddl_dma_free(&ddl_context->metadata_shared_input); + ddl_dma_free(&ddl_context->dbg_core_dump); } -void ddl_release_client_internal_buffers(struct ddl_client_context_type *p_ddl) +void ddl_release_client_internal_buffers(struct ddl_client_context *ddl) { - if (p_ddl->b_decoding) { - struct ddl_decoder_data_type *p_decoder = - &(p_ddl->codec_data.decoder); - ddl_pmem_free(p_decoder->h264Vsp_temp_buffer); - ddl_pmem_free(p_decoder->dpb_comv_buffer); - ddl_pmem_free(p_decoder->ref_buffer); - DDL_FREE(p_decoder->dp_buf.a_dec_pic_buffers); - ddl_decode_dynamic_property(p_ddl, FALSE); - p_decoder->decode_config.n_sequence_header_len = 0; - p_decoder->decode_config.p_sequence_header = NULL; - p_decoder->dpb_mask.n_client_mask = 0; - p_decoder->dpb_mask.n_hw_mask = 0; - p_decoder->dp_buf.n_no_of_dec_pic_buf = 0; - p_decoder->n_dynamic_prop_change = 0; + if (ddl->decoding) { + struct ddl_decoder_data *dec = &(ddl->codec_data.decoder); + ddl_dma_free(&dec->h264Vsp_temp_buffer); + ddl_dma_free(&dec->dpb_comv_buffer); + ddl_dma_free(&dec->ref_buffer); + kfree(dec->dp_buf.dec_pic_buffers); + dec->dp_buf.dec_pic_buffers = NULL; + ddl_decode_dynamic_property(ddl, false); + dec->decode_config.sz = 0; + dec->decode_config.addr = 0; + dec->dpb_mask.client_mask = 0; + dec->dpb_mask.hw_mask = 0; + dec->dp_buf.no_of_dec_pic_buf = 0; + dec->dynamic_prop_change = 0; } else { - struct ddl_encoder_data_type *p_encoder = - &(p_ddl->codec_data.encoder); - ddl_pmem_free(p_encoder->enc_dpb_addr); - ddl_pmem_free(p_encoder->seq_header); - ddl_encode_dynamic_property(p_ddl, FALSE); - p_encoder->n_dynamic_prop_change = 0; + struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder); + ddl_dma_free(&encoder->enc_dpb_addr); + ddl_dma_free(&encoder->seq_header); + ddl_encode_dynamic_property(ddl, false); + encoder->dynamic_prop_change = 0; } } diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_internal_property.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_internal_property.h index a81295468ec33..3f3c6e2cbb5ec 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl_internal_property.h +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_internal_property.h @@ -57,35 +57,35 @@ #define DDL_I_CAPABILITY (VCD_I_CUSTOM_DDL_BASE + 0x8) #define DDL_I_FRAME_PROC_UNITS (VCD_I_CUSTOM_DDL_BASE + 0x9) -struct vcd_property_rc_level_type { - u32 b_frame_level_rc; - u32 b_mb_level_rc; +struct vcd_property_rc_level { + u32 frame_level_rc; + u32 mb_level_rc; }; -struct vcd_property_frame_level_rc_params_type { - u32 n_reaction_coeff; +struct vcd_property_frame_level_rc_params { + u32 reaction_coeff; }; -struct vcd_property_adaptive_rc_params_type { - u32 b_dark_region_as_flag; - u32 b_smooth_region_as_flag; - u32 b_static_region_as_flag; - u32 b_activity_region_flag; +struct vcd_property_adaptive_rc_params { + u32 dark_region_as_flag; + u32 smooth_region_as_flag; + u32 static_region_as_flag; + u32 activity_region_flag; }; -struct ddl_frame_data_type_tag; +struct ddl_frame_data_tag; -struct ddl_property_dec_pic_buffers_type { - struct ddl_frame_data_type_tag *a_dec_pic_buffers; - u32 n_no_of_dec_pic_buf; +struct ddl_property_dec_pic_buffers { + struct ddl_frame_data_tag *dec_pic_buffers; + u32 no_of_dec_pic_buf; }; -struct ddl_property_capability_type { - u32 n_max_num_client; - u32 n_general_command_depth; - u32 n_frame_command_depth; - u32 b_exclusive; - u32 n_ddl_time_out_in_ms; +struct ddl_property_capability { + u32 max_num_client; + u32 general_command_depth; + u32 frame_command_depth; + u32 exclusive; + u32 ddl_time_out_in_ms; }; #endif diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_interrupt_handler.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_interrupt_handler.c index cccb2b588a213..6269f05a275d1 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl_interrupt_handler.c +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_interrupt_handler.c @@ -27,933 +27,772 @@ #define DBG(x...) #endif -static void ddl_decoder_input_done_callback( - struct ddl_client_context_type *p_ddl, u32 b_frame_transact_end); -static void ddl_decoder_ouput_done_callback( - struct ddl_client_context_type *p_ddl, u32 b_frame_transact_end); +static void ddl_decoder_input_done_callback(struct ddl_client_context *ddl, + u32 frame_transact_end); +static void ddl_decoder_ouput_done_callback(struct ddl_client_context *ddl, + u32 frame_transact_end); -static u32 ddl_get_frame_type - (struct vcd_frame_data_type *p_frame, u32 n_frame_type); +static u32 ddl_get_frame_type(struct vcd_frame_data *frame, u32 frame_type); -static void ddl_getdec_profilelevel -(struct ddl_decoder_data_type *p_decoder, u32 n_profile, u32 n_level); +static void ddl_getdec_profilelevel(struct ddl_decoder_data *dec, + u32 profile, u32 level); -static void ddl_dma_done_callback(struct ddl_context_type *p_ddl_context) +static void ddl_dma_done_callback(struct ddl_context *ddl_context) { - if (!DDLCOMMAND_STATE_IS(p_ddl_context, DDL_CMD_DMA_INIT)) { - VIDC_LOG_STRING("UNKWN_DMADONE"); + if (!DDLCOMMAND_STATE_IS(ddl_context, DDL_CMD_DMA_INIT)) { + pr_debug("UNKNOWN_DMADONE\n"); return; } - ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); - VIDC_LOG_STRING("DMA_DONE"); - ddl_core_start_cpu(p_ddl_context); + ddl_move_command_state(ddl_context, DDL_CMD_INVALID); + pr_debug("DMA_DONE"); + ddl_core_start_cpu(ddl_context); } -static void ddl_cpu_started_callback(struct ddl_context_type *p_ddl_context) +static void ddl_cpu_started_callback(struct ddl_context *ddl_context) { - ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); - VIDC_LOG_STRING("CPU-STARTED"); + ddl_move_command_state(ddl_context, DDL_CMD_INVALID); + pr_debug("CPU-STARTED"); if (!vidc_720p_cpu_start()) { - ddl_hw_fatal_cb(p_ddl_context); + ddl_hw_fatal_cb(ddl_context); return; } - vidc_720p_set_deblock_line_buffer( - p_ddl_context->db_line_buffer.p_align_physical_addr, - p_ddl_context->db_line_buffer.n_buffer_size); - p_ddl_context->n_device_state = DDL_DEVICE_INITED; - p_ddl_context->ddl_callback(VCD_EVT_RESP_DEVICE_INIT, VCD_S_SUCCESS, - NULL, 0, NULL, p_ddl_context->p_client_data); - DDL_IDLE(p_ddl_context); + vidc_720p_set_deblock_line_buffer(ddl_context->db_line_buffer.phys_addr, + ddl_context->db_line_buffer.size); + ddl_context->device_state = DDL_DEVICE_INITED; + ddl_context->ddl_callback(VCD_EVT_RESP_DEVICE_INIT, VCD_S_SUCCESS, + NULL, 0, NULL, ddl_context->client_data); + DDL_IDLE(ddl_context); } -static void ddl_eos_done_callback(struct ddl_context_type *p_ddl_context) +static void ddl_eos_done_callback(struct ddl_context *ddl_context) { - struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; - u32 n_displaystatus; + struct ddl_client_context *ddl = ddl_context->current_ddl; + u32 displaystatus; - if (!DDLCOMMAND_STATE_IS(p_ddl_context, DDL_CMD_EOS)) { - VIDC_LOG_STRING("UNKWN_EOSDONE"); - ddl_client_fatal_cb(p_ddl_context); + if (!DDLCOMMAND_STATE_IS(ddl_context, DDL_CMD_EOS)) { + pr_debug("UNKWN_EOSDONE"); + ddl_client_fatal_cb(ddl_context); return; } - if (!p_ddl || - !p_ddl->b_decoding || - !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE) - ) { - VIDC_LOG_STRING("STATE-CRITICAL-EOSDONE"); - ddl_client_fatal_cb(p_ddl_context); + if (!ddl || !ddl->decoding || !DDLCLIENT_STATE_IS(ddl, + DDL_CLIENT_WAIT_FOR_EOS_DONE)) { + pr_debug("STATE-CRITICAL-EOSDONE"); + ddl_client_fatal_cb(ddl_context); return; } - ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + ddl_move_command_state(ddl_context, DDL_CMD_INVALID); - vidc_720p_eos_info(&n_displaystatus); - if ((enum vidc_720p_display_status_type)n_displaystatus + vidc_720p_eos_info(&displaystatus); + if ((enum vidc_720p_display_status_type) displaystatus != VIDC_720P_EMPTY_BUFFER) { - VIDC_LOG_STRING("EOSDONE-EMPTYBUF-ISSUE"); + pr_debug("EOSDONE-EMPTYBUF-ISSUE"); } - ddl_decode_dynamic_property(p_ddl, FALSE); - ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME); - VIDC_LOG_STRING("EOS_DONE"); - p_ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE, VCD_S_SUCCESS, - NULL, 0, (u32 *) p_ddl, p_ddl_context->p_client_data); + ddl_decode_dynamic_property(ddl, false); + ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME); + pr_debug("EOS_DONE"); + ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE, VCD_S_SUCCESS, NULL, + 0, (u32 *) ddl, ddl_context->client_data); - DDL_IDLE(p_ddl_context); + DDL_IDLE(ddl_context); } -static u32 ddl_channel_set_callback(struct ddl_context_type *p_ddl_context) +static u32 ddl_channel_set_callback(struct ddl_context *ddl_context) { - struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; - u32 return_status = FALSE; + struct ddl_client_context *ddl = ddl_context->current_ddl; + u32 return_status = false; - ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); - VIDC_DEBUG_REGISTER_LOG; + ddl_move_command_state(ddl_context, DDL_CMD_INVALID); - if (!p_ddl || - !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_CHDONE) - ) { - VIDC_LOG_STRING("STATE-CRITICAL-CHSET"); - DDL_IDLE(p_ddl_context); + if (!ddl || !DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_CHDONE)) { + pr_debug("STATE-CRITICAL-CHSET"); + DDL_IDLE(ddl_context); return return_status; } - VIDC_LOG_STRING("Channel-set"); - ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_INITCODEC); + pr_debug("Channel-set"); + ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_INITCODEC); - if (p_ddl->b_decoding) { - if (p_ddl->codec_data.decoder.b_header_in_start) { - ddl_decode_init_codec(p_ddl); + if (ddl->decoding) { + if (ddl->codec_data.decoder.header_in_start) { + ddl_decode_init_codec(ddl); } else { - p_ddl_context->ddl_callback(VCD_EVT_RESP_START, - VCD_S_SUCCESS, NULL, - 0, (u32 *) p_ddl, - p_ddl_context-> - p_client_data); - - DDL_IDLE(p_ddl_context); - return_status = TRUE; + ddl_context->ddl_callback(VCD_EVT_RESP_START, + VCD_S_SUCCESS, NULL, 0, (u32 *) ddl, + ddl_context->client_data); + DDL_IDLE(ddl_context); + return_status = true; } } else { - ddl_encode_init_codec(p_ddl); + ddl_encode_init_codec(ddl); } return return_status; } -static void ddl_init_codec_done_callback(struct ddl_context_type *p_ddl_context) +static void ddl_init_codec_done_callback(struct ddl_context *ddl_context) { - struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; - struct ddl_encoder_data_type *p_encoder; - - if (!p_ddl || - p_ddl->b_decoding || - !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE) - ) { - VIDC_LOG_STRING("STATE-CRITICAL-INITCODEC"); - ddl_client_fatal_cb(p_ddl_context); + struct ddl_client_context *ddl = ddl_context->current_ddl; + struct ddl_encoder_data *encoder; + + if (!ddl || ddl->decoding || !DDLCLIENT_STATE_IS(ddl, + DDL_CLIENT_WAIT_FOR_INITCODECDONE)) { + pr_debug("STATE-CRITICAL-INITCODEC"); + ddl_client_fatal_cb(ddl_context); return; } - ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); - ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME); - VIDC_LOG_STRING("INIT_CODEC_DONE"); - - p_encoder = &p_ddl->codec_data.encoder; - if (p_encoder->seq_header.p_virtual_base_addr) { - vidc_720p_encode_get_header(&p_encoder->seq_header. - n_buffer_size); - } + ddl_move_command_state(ddl_context, DDL_CMD_INVALID); + ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME); + pr_debug("INIT_CODEC_DONE"); - p_ddl_context->ddl_callback(VCD_EVT_RESP_START, VCD_S_SUCCESS, NULL, - 0, (u32 *) p_ddl, p_ddl_context->p_client_data); + encoder = &ddl->codec_data.encoder; + if (encoder->seq_header.virt_addr) + vidc_720p_encode_get_header(&encoder->seq_header.size); - DDL_IDLE(p_ddl_context); + ddl_context->ddl_callback(VCD_EVT_RESP_START, VCD_S_SUCCESS, NULL, 0, + (u32 *) ddl, ddl_context->client_data); + + DDL_IDLE(ddl_context); } -static u32 ddl_header_done_callback(struct ddl_context_type *p_ddl_context) +static u32 ddl_header_done_callback(struct ddl_context *ddl_context) { - struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; - struct ddl_decoder_data_type *p_decoder; + struct ddl_client_context *ddl = ddl_context->current_ddl; + struct ddl_decoder_data *dec; struct vidc_720p_seq_hdr_info_type seq_hdr_info; u32 vcd_event = VCD_EVT_RESP_START; u32 vcd_status = VCD_S_SUCCESS; - u32 b_req_cb = TRUE, bret = TRUE; + u32 req_cb = true, bret = true; - if (!DDLCOMMAND_STATE_IS(p_ddl_context, DDL_CMD_HEADER_PARSE)) { - VIDC_LOG_STRING("UNKWN_HEADERDONE"); - ddl_client_fatal_cb(p_ddl_context); - return TRUE; + if (!DDLCOMMAND_STATE_IS(ddl_context, DDL_CMD_HEADER_PARSE)) { + pr_debug("UNKWN_HEADERDONE"); + ddl_client_fatal_cb(ddl_context); + return true; } - if (!p_ddl || - !p_ddl->b_decoding || - !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_INITCODECDONE) - ) { - VIDC_LOG_STRING("STATE-CRITICAL-HDDONE"); - ddl_client_fatal_cb(p_ddl_context); - return TRUE; + if (!ddl || !ddl->decoding || !DDLCLIENT_STATE_IS(ddl, + DDL_CLIENT_WAIT_FOR_INITCODECDONE)) { + pr_debug("STATE-CRITICAL-HDDONE"); + ddl_client_fatal_cb(ddl_context); + return true; } - ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); - ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_DPB); - VIDC_LOG_STRING("HEADER_DONE"); - VIDC_DEBUG_REGISTER_LOG; + ddl_move_command_state(ddl_context, DDL_CMD_INVALID); + ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_DPB); + pr_debug("HEADER_DONE"); vidc_720p_decode_get_seq_hdr_info(&seq_hdr_info); - p_decoder = &(p_ddl->codec_data.decoder); - p_decoder->frame_size.n_width = seq_hdr_info.n_img_size_x; - p_decoder->frame_size.n_height = seq_hdr_info.n_img_size_y; - p_decoder->n_min_dpb_num = seq_hdr_info.n_min_num_dpb; - p_decoder->n_y_cb_cr_size = seq_hdr_info.n_min_dpb_size; - p_decoder->n_progressive_only = 1 - seq_hdr_info.n_progressive; - ddl_getdec_profilelevel(p_decoder, seq_hdr_info.n_profile, - seq_hdr_info.n_level); - ddl_calculate_stride(&p_decoder->frame_size, - !p_decoder->n_progressive_only); - if (seq_hdr_info.n_crop_exists) { - p_decoder->frame_size.n_width -= - (seq_hdr_info.n_crop_right_offset - + seq_hdr_info.n_crop_left_offset); - p_decoder->frame_size.n_height -= - (seq_hdr_info.n_crop_top_offset + - seq_hdr_info.n_crop_bottom_offset); - } - ddl_set_default_decoder_buffer_req(p_decoder, FALSE); - if (seq_hdr_info.n_data_partitioned == 0x1 && - p_decoder->codec_type.e_codec == VCD_CODEC_MPEG4 && - seq_hdr_info.n_img_size_x > DDL_MAX_DP_FRAME_WIDTH && - seq_hdr_info.n_img_size_y > DDL_MAX_DP_FRAME_HEIGHT) { - ddl_client_fatal_cb(p_ddl_context); - return TRUE; - } - - - if (p_decoder->b_header_in_start) { - p_decoder->client_frame_size = p_decoder->frame_size; - p_decoder->client_output_buf_req = - p_decoder->actual_output_buf_req; - if ((p_decoder->frame_size.n_width * - p_decoder->frame_size.n_height) >= (800*480)) { - if ((p_decoder->actual_output_buf_req.\ - n_actual_count + 2) < 10) - p_decoder->client_output_buf_req.\ - n_actual_count = 10; + dec = &(ddl->codec_data.decoder); + dec->frame_size.width = seq_hdr_info.img_size_x; + dec->frame_size.height = seq_hdr_info.img_size_y; + dec->min_dpb_num = seq_hdr_info.min_num_dpb; + dec->y_cb_cr_size = seq_hdr_info.min_dpb_size; + dec->progressive_only = 1 - seq_hdr_info.progressive; + ddl_getdec_profilelevel(dec, seq_hdr_info.profile, seq_hdr_info.level); + ddl_calculate_stride(&dec->frame_size, !dec->progressive_only); + if (seq_hdr_info.crop_exists) { + dec->frame_size.width -= (seq_hdr_info.crop_right_offset + + seq_hdr_info.crop_left_offset); + dec->frame_size.height -= (seq_hdr_info.crop_top_offset + + seq_hdr_info.crop_bottom_offset); + } + ddl_set_default_decoder_buffer_req(dec, false); + if (seq_hdr_info.data_partitioned == 0x1 && + dec->codec_type.codec == VCD_CODEC_MPEG4 && + seq_hdr_info.img_size_x > DDL_MAX_DP_FRAME_WIDTH && + seq_hdr_info.img_size_y > DDL_MAX_DP_FRAME_HEIGHT) { + ddl_client_fatal_cb(ddl_context); + return true; + } + + + if (dec->header_in_start) { + dec->client_frame_size = dec->frame_size; + dec->client_output_buf_req = dec->actual_output_buf_req; + if ((dec->frame_size.width * dec->frame_size.height) >= + (800*480)) { + if ((dec->actual_output_buf_req.actual_count + 2) < 10) + dec->client_output_buf_req.actual_count = 10; else - p_decoder->client_output_buf_req.\ - n_actual_count += 2; + dec->client_output_buf_req.actual_count += 2; } else - p_decoder->client_output_buf_req.\ - n_actual_count = - p_decoder->actual_output_buf_req.\ - n_actual_count + 5; + dec->client_output_buf_req.actual_count = + dec->actual_output_buf_req.actual_count + 5; - p_decoder->client_input_buf_req = - p_decoder->actual_input_buf_req; + dec->client_input_buf_req = dec->actual_input_buf_req; } else { - DBG("%s(): n_width = %d client_frame_size.n_width = %d\n", - __func__, p_decoder->frame_size.n_width, - p_decoder->client_frame_size.n_width); - DBG("%s(): n_height = %d client_frame_size.n_height = %d\n", - __func__, p_decoder->frame_size.n_height, - p_decoder->client_frame_size.n_height); - DBG("%s(): n_size = %d client_frame_size n_size = %d\n", - __func__, p_decoder->actual_output_buf_req.n_size, - p_decoder->client_output_buf_req.n_size); - DBG("%s(): n_min_dpb_num = %d n_actual_count = %d\n", __func__, - p_decoder->n_min_dpb_num, - p_decoder->client_output_buf_req.n_actual_count); - - bret = FALSE; - - if (p_decoder->frame_size.n_width == - p_decoder->client_frame_size.n_width - && p_decoder->frame_size.n_height == - p_decoder->client_frame_size.n_height - && p_decoder->actual_output_buf_req.n_size <= - p_decoder->client_output_buf_req.n_size - && p_decoder->n_min_dpb_num <= - p_decoder->client_output_buf_req.n_actual_count) { - vcd_status = ddl_decode_set_buffers(p_ddl); - if (!vcd_status) - b_req_cb = FALSE; - else{ - ddl_client_fatal_cb(p_ddl_context); - b_req_cb = TRUE; + DBG("%s(): width = %d client_frame_size.width = %d\n", + __func__, dec->frame_size.width, + dec->client_frame_size.width); + DBG("%s(): height = %d client_frame_size.height = %d\n", + __func__, dec->frame_size.height, + dec->client_frame_size.height); + DBG("%s(): size = %d client_frame_size size = %d\n", + __func__, dec->actual_output_buf_req.size, + dec->client_output_buf_req.size); + DBG("%s(): min_dpb_num = %d actual_count = %d\n", __func__, + dec->min_dpb_num, + dec->client_output_buf_req.actual_count); + + bret = false; + + if (dec->frame_size.width == dec->client_frame_size.width && + dec->frame_size.height == + dec->client_frame_size.height && + dec->actual_output_buf_req.size <= + dec->client_output_buf_req.size && + dec->min_dpb_num <= + dec->client_output_buf_req.actual_count) { + vcd_status = ddl_decode_set_buffers(ddl); + if (!vcd_status) { + req_cb = false; + } else { + ddl_client_fatal_cb(ddl_context); + req_cb = true; } } else { - p_decoder->client_frame_size = p_decoder->frame_size; - p_decoder->client_output_buf_req = - p_decoder->actual_output_buf_req; - p_decoder->client_input_buf_req = - p_decoder->actual_input_buf_req; - VIDC_LOGERR_STRING - ("ddlhdr_done_cb:Decode_reconfig_not_supported"); + dec->client_frame_size = dec->frame_size; + dec->client_output_buf_req = dec->actual_output_buf_req; + dec->client_input_buf_req = dec->actual_input_buf_req; + pr_err("%s:Decode_reconfig_not_supported\n", __func__); vcd_event = VCD_EVT_IND_RECONFIG; } } - if (b_req_cb) { - p_ddl_context->ddl_callback(vcd_event, vcd_status, - NULL, 0, (u32 *) p_ddl, p_ddl_context->p_client_data); - - DDL_IDLE(p_ddl_context); + if (req_cb) { + ddl_context->ddl_callback(vcd_event, vcd_status, NULL, 0, + (u32 *) ddl, ddl_context->client_data); + DDL_IDLE(ddl_context); } return bret; } -static u32 ddl_dpb_buffers_set_done_callback(struct ddl_context_type - *p_ddl_context) +static u32 ddl_dpb_buffers_set_done_callback(struct ddl_context + *ddl_context) { - struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; - - ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); - if (!p_ddl || - !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_DPBDONE) - ) { - VIDC_LOG_STRING("STATE-CRITICAL-DPBDONE"); - ddl_client_fatal_cb(p_ddl_context); - return TRUE; - } - VIDC_LOG_STRING("INTR_DPBDONE"); - ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME); - ddl_decode_frame_run(p_ddl); - return FALSE; + struct ddl_client_context *ddl = ddl_context->current_ddl; + + ddl_move_command_state(ddl_context, DDL_CMD_INVALID); + if (!ddl || !DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_DPBDONE)) { + pr_debug("STATE-CRITICAL-DPBDONE\n"); + ddl_client_fatal_cb(ddl_context); + return true; + } + pr_debug("INTR_DPBDONE\n"); + ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME); + ddl_decode_frame_run(ddl); + return false; } -static void ddl_encoder_frame_run_callback(struct ddl_context_type - *p_ddl_context) +static void ddl_encoder_frame_run_callback(struct ddl_context *ddl_context) { - struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; - struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); - u32 b_eos_present = FALSE; - - if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE) - ) { - VIDC_LOG_STRING("STATE-CRITICAL-ENCFRMRUN"); - ddl_client_fatal_cb(p_ddl_context); + struct ddl_client_context *ddl = ddl_context->current_ddl; + struct ddl_encoder_data *encoder = &(ddl->codec_data.encoder); + u32 eos_present = false; + + if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE)) { + pr_debug("STATE-CRITICAL-ENCFRMRUN\n"); + ddl_client_fatal_cb(ddl_context); return; } - VIDC_LOG_STRING("ENC_FRM_RUN_DONE"); + pr_debug("ENC_FRM_RUN_DONE\n"); - ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); - vidc_720p_enc_frame_info(&p_encoder->enc_frame_info); + ddl_move_command_state(ddl_context, DDL_CMD_INVALID); + vidc_720p_enc_frame_info(&encoder->enc_frame_info); - p_ddl->output_frame.vcd_frm.n_ip_frm_tag = - p_ddl->input_frame.vcd_frm.n_ip_frm_tag; - p_ddl->output_frame.vcd_frm.n_data_len = - p_encoder->enc_frame_info.n_enc_size; - p_ddl->output_frame.vcd_frm.n_flags |= VCD_FRAME_FLAG_ENDOFFRAME; - ddl_get_frame_type - (&(p_ddl->output_frame.vcd_frm), - p_encoder->enc_frame_info.n_frame_type); - ddl_process_encoder_metadata(p_ddl); + ddl->output_frame.vcd_frm.ip_frm_tag = + ddl->input_frame.vcd_frm.ip_frm_tag; + ddl->output_frame.vcd_frm.data_len = + encoder->enc_frame_info.enc_size; + ddl->output_frame.vcd_frm.flags |= VCD_FRAME_FLAG_ENDOFFRAME; + ddl_get_frame_type(&(ddl->output_frame.vcd_frm), + encoder->enc_frame_info.frame_type); + ddl_process_encoder_metadata(ddl); - ddl_encode_dynamic_property(p_ddl, FALSE); + ddl_encode_dynamic_property(ddl, false); - p_ddl->input_frame.b_frm_trans_end = FALSE; - p_ddl_context->ddl_callback(VCD_EVT_RESP_INPUT_DONE, VCD_S_SUCCESS, - &(p_ddl->input_frame), sizeof(struct ddl_frame_data_type_tag), - (u32 *) p_ddl, p_ddl_context->p_client_data); + ddl->input_frame.frm_trans_end = false; + ddl_context->ddl_callback(VCD_EVT_RESP_INPUT_DONE, VCD_S_SUCCESS, + &(ddl->input_frame), sizeof(struct ddl_frame_data_tag), + (u32 *) ddl, ddl_context->client_data); #ifdef CORE_TIMING_INFO ddl_calc_core_time(1); #endif /* check the presence of EOS */ - b_eos_present = - ((VCD_FRAME_FLAG_EOS & p_ddl->input_frame.vcd_frm.n_flags)); - - p_ddl->output_frame.b_frm_trans_end = !b_eos_present; - p_ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS, - &(p_ddl->output_frame), sizeof(struct ddl_frame_data_type_tag), - (u32 *) p_ddl, p_ddl_context->p_client_data); - - if (b_eos_present) { - VIDC_LOG_STRING("ENC-EOS_DONE"); - p_ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE, - VCD_S_SUCCESS, NULL, 0, (u32 *)p_ddl, - p_ddl_context->p_client_data); + eos_present = VCD_FRAME_FLAG_EOS & ddl->input_frame.vcd_frm.flags; + + ddl->output_frame.frm_trans_end = !eos_present; + ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS, + &(ddl->output_frame), sizeof(struct ddl_frame_data_tag), + (u32 *) ddl, ddl_context->client_data); + + if (eos_present) { + pr_debug("ENC-EOS_DONE\n"); + ddl_context->ddl_callback(VCD_EVT_RESP_EOS_DONE, + VCD_S_SUCCESS, NULL, 0, (u32 *)ddl, + ddl_context->client_data); } - ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME); - DDL_IDLE(p_ddl_context); + ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME); + DDL_IDLE(ddl_context); } -static u32 ddl_decoder_frame_run_callback(struct ddl_context_type - *p_ddl_context) +static u32 ddl_decoder_frame_run_callback(struct ddl_context + *ddl_context) { - struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; - struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); - struct vidc_720p_dec_disp_info_type *p_dec_disp_info = - &(p_decoder->dec_disp_info); - u32 b_callback_end = FALSE; - u32 status = TRUE, eos_present = FALSE;; - - if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE)) { - VIDC_LOG_STRING("STATE-CRITICAL-DECFRMRUN"); - ddl_client_fatal_cb(p_ddl_context); - return TRUE; + struct ddl_client_context *ddl = ddl_context->current_ddl; + struct ddl_decoder_data *dec = &(ddl->codec_data.decoder); + struct vidc_720p_dec_disp_info *dec_disp_info = &(dec->dec_disp_info); + u32 callback_end = false; + u32 status = true, eos_present = false;; + + if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME_DONE)) { + pr_debug("STATE-CRITICAL-DECFRMRUN\n"); + ddl_client_fatal_cb(ddl_context); + return true; } - VIDC_LOG_STRING("DEC_FRM_RUN_DONE"); + pr_debug("DEC_FRM_RUN_DONE\n"); - ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + ddl_move_command_state(ddl_context, DDL_CMD_INVALID); - vidc_720p_decode_display_info(p_dec_disp_info); + vidc_720p_decode_display_info(dec_disp_info); - ddl_decode_dynamic_property(p_ddl, FALSE); + ddl_decode_dynamic_property(ddl, false); - if (p_dec_disp_info->n_resl_change) { - VIDC_LOGERR_STRING - ("ddl_dec_frm_done:Dec_reconfig_no_tsupported"); + if (dec_disp_info->resl_change) { + pr_err("ddl_dec_frm_done:" + "Dec_reconfig_no_tsupported\n"); } - if ((VCD_FRAME_FLAG_EOS & p_ddl->input_frame.vcd_frm.n_flags)) { - b_callback_end = FALSE; - eos_present = TRUE; + if ((VCD_FRAME_FLAG_EOS & ddl->input_frame.vcd_frm.flags)) { + callback_end = false; + eos_present = true; } - - if (p_dec_disp_info->e_disp_status == VIDC_720P_DECODE_ONLY || - p_dec_disp_info->e_disp_status - == VIDC_720P_DECODE_AND_DISPLAY) { + if (dec_disp_info->disp_status == VIDC_720P_DECODE_ONLY || + dec_disp_info->disp_status == + VIDC_720P_DECODE_AND_DISPLAY) { if (!eos_present) - b_callback_end = (p_dec_disp_info->e_disp_status - == VIDC_720P_DECODE_ONLY); + callback_end = (dec_disp_info->disp_status == + VIDC_720P_DECODE_ONLY); - ddl_decoder_input_done_callback(p_ddl, b_callback_end); + ddl_decoder_input_done_callback(ddl, callback_end); } - if (p_dec_disp_info->e_disp_status == VIDC_720P_DECODE_AND_DISPLAY - || p_dec_disp_info->e_disp_status == VIDC_720P_DISPLAY_ONLY) { + if (dec_disp_info->disp_status == VIDC_720P_DECODE_AND_DISPLAY || + dec_disp_info->disp_status == VIDC_720P_DISPLAY_ONLY) { if (!eos_present) - b_callback_end = - (p_dec_disp_info->e_disp_status - == VIDC_720P_DECODE_AND_DISPLAY); + callback_end = (dec_disp_info->disp_status == + VIDC_720P_DECODE_AND_DISPLAY); - ddl_decoder_ouput_done_callback(p_ddl, b_callback_end); + ddl_decoder_ouput_done_callback(ddl, callback_end); } - if (p_dec_disp_info->e_disp_status == VIDC_720P_DISPLAY_ONLY) { + if (dec_disp_info->disp_status == VIDC_720P_DISPLAY_ONLY) { /* send the same input once again for decoding */ - ddl_decode_frame_run(p_ddl); + ddl_decode_frame_run(ddl); /* client need to ignore the interrupt */ - status = FALSE; + status = false; } else if (eos_present) { /* send EOS command to HW */ - ddl_decode_eos_run(p_ddl); + ddl_decode_eos_run(ddl); /* client need to ignore the interrupt */ - status = FALSE; + status = false; } else { - ddl_move_client_state(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME); + ddl_move_client_state(ddl, DDL_CLIENT_WAIT_FOR_FRAME); /* move to Idle */ - DDL_IDLE(p_ddl_context); + DDL_IDLE(ddl_context); } return status; } -static u32 ddl_eos_frame_done_callback(struct ddl_context_type *p_ddl_context) +static u32 ddl_eos_frame_done_callback(struct ddl_context *ddl_context) { - struct ddl_client_context_type *p_ddl = p_ddl_context->p_current_ddl; - struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); - struct vidc_720p_dec_disp_info_type *p_dec_disp_info = - &(p_decoder->dec_disp_info); - - if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE)) { - VIDC_LOGERR_STRING("STATE-CRITICAL-EOSFRMRUN"); - ddl_client_fatal_cb(p_ddl_context); - return TRUE; + struct ddl_client_context *ddl = ddl_context->current_ddl; + struct ddl_decoder_data *dec = &(ddl->codec_data.decoder); + struct vidc_720p_dec_disp_info *dec_disp_info = + &(dec->dec_disp_info); + + if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_EOS_DONE)) { + pr_err("STATE-CRITICAL-EOSFRMRUN\n"); + ddl_client_fatal_cb(ddl_context); + return true; } - VIDC_LOG_STRING("EOS_FRM_RUN_DONE"); + pr_debug("EOS_FRM_RUN_DONE\n"); - ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); + ddl_move_command_state(ddl_context, DDL_CMD_INVALID); - vidc_720p_decode_display_info(p_dec_disp_info); + vidc_720p_decode_display_info(dec_disp_info); - ddl_decode_dynamic_property(p_ddl, FALSE); + ddl_decode_dynamic_property(ddl, false); - if (p_ddl_context->n_op_failed == 0x1) - VIDC_LOGERR_STRING("ddl_eos_frm_done:OPFAILED!!"); - else if (p_dec_disp_info->n_resl_change) - VIDC_LOGERR_STRING("ddl_eos_frm_done:Dec_reconfig!!"); + if (ddl_context->op_failed == 0x1) + pr_err("ddl_eos_frm_done:OPFAILED!!\n"); + else if (dec_disp_info->resl_change) + pr_err("ddl_eos_frm_done:Dec_reconfig!!\n"); - if (p_dec_disp_info->e_disp_status == VIDC_720P_DISPLAY_ONLY) - ddl_decoder_ouput_done_callback(p_ddl, FALSE); + if (dec_disp_info->disp_status == VIDC_720P_DISPLAY_ONLY) + ddl_decoder_ouput_done_callback(ddl, false); else - VIDC_LOG_STRING("STATE-CRITICAL-WRONG-DISP-STATUS"); + pr_debug("STATE-CRITICAL-WRONG-DISP-STATUS\n"); - ddl_decoder_dpb_transact(p_decoder, NULL, DDL_DPB_OP_SET_MASK); - ddl_move_command_state(p_ddl_context, DDL_CMD_EOS); - vidc_720p_submit_command(p_ddl->n_channel_id, - VIDC_720P_CMD_FRAMERUN); - return FALSE; + ddl_decoder_dpb_transact(dec, NULL, DDL_DPB_OP_SET_MASK); + ddl_move_command_state(ddl_context, DDL_CMD_EOS); + vidc_720p_submit_command(ddl->channel_id, VIDC_720P_CMD_FRAMERUN); + return false; } -static void ddl_channel_end_callback(struct ddl_context_type *p_ddl_context) +static void ddl_channel_end_callback(struct ddl_context *ddl_context) { - struct ddl_client_context_type *p_ddl; + struct ddl_client_context *ddl; - ddl_move_command_state(p_ddl_context, DDL_CMD_INVALID); - VIDC_LOG_STRING("CH_END_DONE"); + ddl_move_command_state(ddl_context, DDL_CMD_INVALID); + pr_debug("CH_END_DONE\n"); - p_ddl = p_ddl_context->p_current_ddl; - if (!p_ddl || - !DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_CHEND) - ) { - VIDC_LOG_STRING("STATE-CRITICAL-CHEND"); - DDL_IDLE(p_ddl_context); + ddl = ddl_context->current_ddl; + if (!ddl || !DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_CHEND)) { + pr_debug("STATE-CRITICAL-CHEND\n"); + DDL_IDLE(ddl_context); return; } - ddl_release_client_internal_buffers(p_ddl); - p_ddl_context->ddl_callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, - NULL, 0, (u32 *) p_ddl, p_ddl_context->p_client_data); - ddl_move_client_state(p_ddl, DDL_CLIENT_OPEN); - DDL_IDLE(p_ddl_context); + ddl_release_client_internal_buffers(ddl); + ddl_context->ddl_callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, NULL, 0, + (u32 *) ddl, ddl_context->client_data); + ddl_move_client_state(ddl, DDL_CLIENT_OPEN); + DDL_IDLE(ddl_context); } -static u32 ddl_operation_done_callback(struct ddl_context_type *p_ddl_context) +static u32 ddl_operation_done_callback(struct ddl_context *ddl_context) { - u32 b_return_status = TRUE; + u32 return_status = true; - switch (p_ddl_context->e_cmd_state) { + switch (ddl_context->cmd_state) { case DDL_CMD_DECODE_FRAME: - { - b_return_status = ddl_decoder_frame_run_callback( - p_ddl_context); - break; - } + return_status = ddl_decoder_frame_run_callback(ddl_context); + break; case DDL_CMD_ENCODE_FRAME: - { - ddl_encoder_frame_run_callback(p_ddl_context); - break; - } + ddl_encoder_frame_run_callback(ddl_context); + break; case DDL_CMD_CHANNEL_SET: - { - b_return_status = ddl_channel_set_callback( - p_ddl_context); - break; - } + return_status = ddl_channel_set_callback(ddl_context); + break; case DDL_CMD_INIT_CODEC: - { - ddl_init_codec_done_callback(p_ddl_context); - break; - } + ddl_init_codec_done_callback(ddl_context); + break; case DDL_CMD_HEADER_PARSE: - { - b_return_status = ddl_header_done_callback( - p_ddl_context); - break; - } + return_status = ddl_header_done_callback(ddl_context); + break; case DDL_CMD_DECODE_SET_DPB: - { - b_return_status = ddl_dpb_buffers_set_done_callback( - p_ddl_context); - break; - } + return_status = ddl_dpb_buffers_set_done_callback(ddl_context); + break; case DDL_CMD_CHANNEL_END: - { - ddl_channel_end_callback(p_ddl_context); - break; - } + ddl_channel_end_callback(ddl_context); + break; case DDL_CMD_EOS: - { - b_return_status = ddl_eos_frame_done_callback( - p_ddl_context); - break; - } + return_status = ddl_eos_frame_done_callback(ddl_context); + break; case DDL_CMD_CPU_RESET: - { - ddl_cpu_started_callback(p_ddl_context); - break; - } + ddl_cpu_started_callback(ddl_context); + break; default: - { - VIDC_LOG_STRING("UNKWN_OPDONE"); - b_return_status = FALSE; - break; - } + pr_debug("UNKWN_OPDONE\n"); + return_status = false; + break; } - return b_return_status; + return return_status; } -static u32 ddl_process_intr_status(struct ddl_context_type *p_ddl_context, +static u32 ddl_process_intr_status(struct ddl_context *ddl_context, u32 int_status) { - u32 b_status = TRUE; + u32 status = true; switch (int_status) { case VIDC_720P_INTR_FRAME_DONE: - { - b_status = ddl_operation_done_callback(p_ddl_context); - break; - } + status = ddl_operation_done_callback(ddl_context); + break; case VIDC_720P_INTR_DMA_DONE: - { - ddl_dma_done_callback(p_ddl_context); - b_status = FALSE; - break; - } + ddl_dma_done_callback(ddl_context); + status = false; + break; case VIDC_720P_INTR_FW_DONE: - { - ddl_eos_done_callback(p_ddl_context); - break; - } + ddl_eos_done_callback(ddl_context); + break; case VIDC_720P_INTR_BUFFER_FULL: - { - VIDC_LOGERR_STRING("BUF_FULL_INTR"); - break; - } + pr_err("BUF_FULL_INTR\n"); + break; default: - { - VIDC_LOGERR_STRING("UNKWN_INTR"); - break; - } + pr_err("UNKWN_INTR\n"); + break; } - return b_status; + return status; } void ddl_read_and_clear_interrupt(void) { - struct ddl_context_type *p_ddl_context; + struct ddl_context *ddl_context; - p_ddl_context = ddl_get_context(); - if (!p_ddl_context->p_core_virtual_base_addr) { - VIDC_LOGERR_STRING("SPURIOUS_INTERRUPT"); + ddl_context = ddl_get_context(); + if (!ddl_context->core_virtual_base_addr) { + pr_err("SPURIOUS_INTERRUPT\n"); return; } - vidc_720p_get_interrupt_status(&p_ddl_context->intr_status, - &p_ddl_context->n_cmd_err_status, - &p_ddl_context->n_disp_pic_err_status, - &p_ddl_context->n_op_failed - ); + vidc_720p_get_interrupt_status(&ddl_context->intr_status, + &ddl_context->cmd_err_status, + &ddl_context->disp_pic_err_status, + &ddl_context->op_failed); vidc_720p_interrupt_done_clear(); - } u32 ddl_process_core_response(void) { - struct ddl_context_type *p_ddl_context; - u32 b_return_status = TRUE; + struct ddl_context *ddl_context; + u32 return_status = true; - p_ddl_context = ddl_get_context(); - if (!p_ddl_context->p_core_virtual_base_addr) { - VIDC_LOGERR_STRING("UNKWN_INTR"); - return FALSE; + ddl_context = ddl_get_context(); + if (!ddl_context->core_virtual_base_addr) { + pr_err("UNKWN_INTR\n"); + return false; } - if (p_ddl_context->intr_status == DDL_INVALID_INTR_STATUS) { - VIDC_LOGERR_STRING("INTERRUPT_NOT_READ"); - return FALSE; + if (ddl_context->intr_status == DDL_INVALID_INTR_STATUS) { + pr_err("INTERRUPT_NOT_READ\n"); + return false; } - if (!ddl_handle_core_errors(p_ddl_context)) { - b_return_status = ddl_process_intr_status(p_ddl_context, - p_ddl_context->intr_status); + if (!ddl_handle_core_errors(ddl_context)) { + return_status = ddl_process_intr_status(ddl_context, + ddl_context->intr_status); } - if (p_ddl_context->pf_interrupt_clr) - (*p_ddl_context->pf_interrupt_clr)(); + if (ddl_context->pf_interrupt_clr) + (*ddl_context->pf_interrupt_clr)(); - p_ddl_context->intr_status = DDL_INVALID_INTR_STATUS; - return b_return_status; + ddl_context->intr_status = DDL_INVALID_INTR_STATUS; + return return_status; } static void ddl_decoder_input_done_callback( - struct ddl_client_context_type *p_ddl, u32 b_frame_transact_end) + struct ddl_client_context *ddl, u32 frame_transact_end) { - struct vidc_720p_dec_disp_info_type *p_dec_disp_info = - &(p_ddl->codec_data.decoder.dec_disp_info); - struct vcd_frame_data_type *p_input_vcd_frm = - &(p_ddl->input_frame.vcd_frm); - ddl_get_frame_type(p_input_vcd_frm, p_dec_disp_info-> - n_input_frame_type); - - p_input_vcd_frm->b_interlaced = (p_dec_disp_info-> - n_input_is_interlace); - - p_input_vcd_frm->n_offset += p_dec_disp_info->n_input_bytes_consumed; - p_input_vcd_frm->n_data_len -= p_dec_disp_info->n_input_bytes_consumed; - - p_ddl->input_frame.b_frm_trans_end = b_frame_transact_end; - p_ddl->p_ddl_context->ddl_callback( - VCD_EVT_RESP_INPUT_DONE, + struct vidc_720p_dec_disp_info *dec_disp_info = + &(ddl->codec_data.decoder.dec_disp_info); + struct vcd_frame_data *input_vcd_frm = &(ddl->input_frame.vcd_frm); + ddl_get_frame_type(input_vcd_frm, dec_disp_info->input_frame_type); + + input_vcd_frm->interlaced = (dec_disp_info->input_is_interlace); + + input_vcd_frm->offset += dec_disp_info->input_bytes_consumed; + input_vcd_frm->data_len -= dec_disp_info->input_bytes_consumed; + + ddl->input_frame.frm_trans_end = frame_transact_end; + ddl->ddl_context->ddl_callback(VCD_EVT_RESP_INPUT_DONE, VCD_S_SUCCESS, - &p_ddl->input_frame, - sizeof(struct ddl_frame_data_type_tag), - (void *)p_ddl, - p_ddl->p_ddl_context->p_client_data); + &ddl->input_frame, + sizeof(struct ddl_frame_data_tag), + (void *)ddl, + ddl->ddl_context->client_data); } -static void ddl_decoder_ouput_done_callback( - struct ddl_client_context_type *p_ddl, - u32 b_frame_transact_end) +static void ddl_decoder_ouput_done_callback(struct ddl_client_context *ddl, + u32 frame_transact_end) { - struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); - struct vidc_720p_dec_disp_info_type *p_dec_disp_info = - &(p_decoder->dec_disp_info); - struct ddl_frame_data_type_tag *p_output_frame = - &p_ddl->output_frame; - struct vcd_frame_data_type *p_output_vcd_frm = - &(p_output_frame->vcd_frm); + struct ddl_decoder_data *dec = &ddl->codec_data.decoder; + struct vidc_720p_dec_disp_info *dec_disp_info = &dec->dec_disp_info; + struct ddl_frame_data_tag *output_frame = &ddl->output_frame; + struct vcd_frame_data *output_vcd_frm = &output_frame->vcd_frm; u32 vcd_status; - u32 n_free_luma_dpb = 0; - - p_output_vcd_frm->p_physical = (u8 *)p_dec_disp_info->n_y_addr; - - if (p_decoder->codec_type.e_codec == VCD_CODEC_MPEG4 || - p_decoder->codec_type.e_codec == VCD_CODEC_VC1 || - p_decoder->codec_type.e_codec == VCD_CODEC_VC1_RCV){ - vidc_720p_decode_skip_frm_details(&n_free_luma_dpb); - if (n_free_luma_dpb) - p_output_vcd_frm->p_physical = (u8 *) n_free_luma_dpb; - } - - - vcd_status = ddl_decoder_dpb_transact( - p_decoder, - p_output_frame, - DDL_DPB_OP_MARK_BUSY); - - - p_output_vcd_frm->n_ip_frm_tag = p_dec_disp_info->n_tag_top; - if (p_dec_disp_info->n_crop_exists == 0x1) { - p_output_vcd_frm->dec_op_prop.disp_frm.n_left = - p_dec_disp_info->n_crop_left_offset; - p_output_vcd_frm->dec_op_prop.disp_frm.n_top = - p_dec_disp_info->n_crop_top_offset; - p_output_vcd_frm->dec_op_prop.disp_frm.n_right = - p_dec_disp_info->n_img_size_x - - p_dec_disp_info->n_crop_right_offset; - p_output_vcd_frm->dec_op_prop.disp_frm.n_bottom = - p_dec_disp_info->n_img_size_y - - p_dec_disp_info->n_crop_bottom_offset; + phys_addr_t free_luma_dpb = 0; + + output_vcd_frm->phys_addr = dec_disp_info->y_addr; + + if (dec->codec_type.codec == VCD_CODEC_MPEG4 || + dec->codec_type.codec == VCD_CODEC_VC1 || + dec->codec_type.codec == VCD_CODEC_VC1_RCV) { + vidc_720p_decode_skip_frm_details(&free_luma_dpb); + if (free_luma_dpb) + output_vcd_frm->phys_addr = free_luma_dpb; + } + + vcd_status = ddl_decoder_dpb_transact(dec, output_frame, + DDL_DPB_OP_MARK_BUSY); + + output_vcd_frm->ip_frm_tag = dec_disp_info->tag_top; + if (dec_disp_info->crop_exists == 0x1) { + output_vcd_frm->dec_op_prop.disp_frm.left = + dec_disp_info->crop_left_offset; + output_vcd_frm->dec_op_prop.disp_frm.top = + dec_disp_info->crop_top_offset; + output_vcd_frm->dec_op_prop.disp_frm.right = + dec_disp_info->img_size_x - + dec_disp_info->crop_right_offset; + output_vcd_frm->dec_op_prop.disp_frm.bottom = + dec_disp_info->img_size_y - + dec_disp_info->crop_bottom_offset; } else { - p_output_vcd_frm->dec_op_prop.disp_frm.n_left = 0; - p_output_vcd_frm->dec_op_prop.disp_frm.n_top = 0; - p_output_vcd_frm->dec_op_prop.disp_frm.n_right = - p_dec_disp_info->n_img_size_x; - p_output_vcd_frm->dec_op_prop.disp_frm.n_bottom = - p_dec_disp_info->n_img_size_y; - } - if (!p_dec_disp_info->n_disp_is_interlace) { - p_output_vcd_frm->b_interlaced = FALSE; - p_output_frame->n_intrlcd_ip_frm_tag = VCD_FRAMETAG_INVALID; + output_vcd_frm->dec_op_prop.disp_frm.left = 0; + output_vcd_frm->dec_op_prop.disp_frm.top = 0; + output_vcd_frm->dec_op_prop.disp_frm.right = + dec_disp_info->img_size_x; + output_vcd_frm->dec_op_prop.disp_frm.bottom = + dec_disp_info->img_size_y; + } + if (!dec_disp_info->disp_is_interlace) { + output_vcd_frm->interlaced = false; + output_frame->intrlcd_ip_frm_tag = VCD_FRAMETAG_INVALID; } else { - p_output_vcd_frm->b_interlaced = TRUE; - p_output_frame->n_intrlcd_ip_frm_tag = - p_dec_disp_info->n_tag_bottom; + output_vcd_frm->interlaced = true; + output_frame->intrlcd_ip_frm_tag = dec_disp_info->tag_bottom; } - p_output_vcd_frm->n_offset = 0; - p_output_vcd_frm->n_data_len = p_decoder->n_y_cb_cr_size; + output_vcd_frm->offset = 0; + output_vcd_frm->data_len = dec->y_cb_cr_size; - if (n_free_luma_dpb) - p_output_vcd_frm->n_data_len = 0; + if (free_luma_dpb) + output_vcd_frm->data_len = 0; - p_output_vcd_frm->n_flags |= VCD_FRAME_FLAG_ENDOFFRAME; + output_vcd_frm->flags |= VCD_FRAME_FLAG_ENDOFFRAME; if (!vcd_status) - ddl_process_decoder_metadata(p_ddl); - p_output_frame->b_frm_trans_end = b_frame_transact_end; + ddl_process_decoder_metadata(ddl); + output_frame->frm_trans_end = frame_transact_end; #ifdef CORE_TIMING_INFO ddl_calc_core_time(0); #endif - p_ddl->p_ddl_context->ddl_callback( - VCD_EVT_RESP_OUTPUT_DONE, - vcd_status, - p_output_frame, - sizeof(struct ddl_frame_data_type_tag), - (void *)p_ddl, - p_ddl->p_ddl_context->p_client_data); + ddl->ddl_context->ddl_callback(VCD_EVT_RESP_OUTPUT_DONE, + vcd_status, output_frame, sizeof(struct ddl_frame_data_tag), + (void *)ddl, ddl->ddl_context->client_data); } -static u32 ddl_get_frame_type - (struct vcd_frame_data_type *p_frame, u32 n_frame_type) { +static u32 ddl_get_frame_type(struct vcd_frame_data *frame, u32 frame_type) +{ enum vidc_720p_frame_type e_frame_type = - (enum vidc_720p_frame_type)n_frame_type; - u32 b_status = TRUE; + (enum vidc_720p_frame_type)frame_type; + u32 status = true; switch (e_frame_type) { case VIDC_720P_IFRAME: - { - p_frame->n_flags |= VCD_FRAME_FLAG_SYNCFRAME; - p_frame->e_frame_type = VCD_FRAME_I; - break; - } + frame->flags |= VCD_FRAME_FLAG_SYNCFRAME; + frame->frame_type = VCD_FRAME_I; + break; case VIDC_720P_PFRAME: - { - p_frame->e_frame_type = VCD_FRAME_P; - break; - } + frame->frame_type = VCD_FRAME_P; + break; case VIDC_720P_BFRAME: - { - p_frame->e_frame_type = VCD_FRAME_B; - break; - } + frame->frame_type = VCD_FRAME_B; + break; case VIDC_720P_NOTCODED: - { - p_frame->n_data_len = 0; - break; - } + frame->data_len = 0; + break; default: - { - VIDC_LOG_STRING("CRITICAL-FRAMETYPE"); - b_status = FALSE; - break; - } + pr_debug("CRITICAL-FRAMETYPE\n"); + status = false; + break; } - return b_status; + return status; } -static void ddl_getmpeg4_declevel(enum vcd_codec_level_type *p_level, - u32 n_level) +static void ddl_getmpeg4_declevel(enum vcd_codec_level_type *vcd_level, + u32 level) { - switch (n_level) { + switch (level) { case VIDC_720P_MPEG4_LEVEL0: - { - *p_level = VCD_LEVEL_MPEG4_0; - break; - } + *vcd_level = VCD_LEVEL_MPEG4_0; + break; case VIDC_720P_MPEG4_LEVEL0b: - { - *p_level = VCD_LEVEL_MPEG4_0b; - break; - } + *vcd_level = VCD_LEVEL_MPEG4_0b; + break; case VIDC_720P_MPEG4_LEVEL1: - { - *p_level = VCD_LEVEL_MPEG4_1; - break; - } + *vcd_level = VCD_LEVEL_MPEG4_1; + break; case VIDC_720P_MPEG4_LEVEL2: - { - *p_level = VCD_LEVEL_MPEG4_2; - break; - } + *vcd_level = VCD_LEVEL_MPEG4_2; + break; case VIDC_720P_MPEG4_LEVEL3: - { - *p_level = VCD_LEVEL_MPEG4_3; - break; - } + *vcd_level = VCD_LEVEL_MPEG4_3; + break; case VIDC_720P_MPEG4_LEVEL3b: - { - *p_level = VCD_LEVEL_MPEG4_3b; - break; - } + *vcd_level = VCD_LEVEL_MPEG4_3b; + break; case VIDC_720P_MPEG4_LEVEL4a: - { - *p_level = VCD_LEVEL_MPEG4_4a; - break; - } + *vcd_level = VCD_LEVEL_MPEG4_4a; + break; case VIDC_720P_MPEG4_LEVEL5: - { - *p_level = VCD_LEVEL_MPEG4_5; - break; - } + *vcd_level = VCD_LEVEL_MPEG4_5; + break; case VIDC_720P_MPEG4_LEVEL6: - { - *p_level = VCD_LEVEL_MPEG4_6; - break; - } + *vcd_level = VCD_LEVEL_MPEG4_6; + break; } } -static void ddl_geth264_declevel(enum vcd_codec_level_type *p_level, - u32 n_level) +static void ddl_geth264_declevel(enum vcd_codec_level_type *vcd_level, + u32 level) { - switch (n_level) { + switch (level) { case VIDC_720P_H264_LEVEL1: - { - *p_level = VCD_LEVEL_H264_1; - break; - } + *vcd_level = VCD_LEVEL_H264_1; + break; case VIDC_720P_H264_LEVEL1b: - { - *p_level = VCD_LEVEL_H264_1b; - break; - } + *vcd_level = VCD_LEVEL_H264_1b; + break; case VIDC_720P_H264_LEVEL1p1: - { - *p_level = VCD_LEVEL_H264_1p1; - break; - } + *vcd_level = VCD_LEVEL_H264_1p1; + break; case VIDC_720P_H264_LEVEL1p2: - { - *p_level = VCD_LEVEL_H264_1p2; - break; - } + *vcd_level = VCD_LEVEL_H264_1p2; + break; case VIDC_720P_H264_LEVEL1p3: - { - *p_level = VCD_LEVEL_H264_1p3; - break; - } + *vcd_level = VCD_LEVEL_H264_1p3; + break; case VIDC_720P_H264_LEVEL2: - { - *p_level = VCD_LEVEL_H264_2; - break; - } + *vcd_level = VCD_LEVEL_H264_2; + break; case VIDC_720P_H264_LEVEL2p1: - { - *p_level = VCD_LEVEL_H264_2p1; - break; - } + *vcd_level = VCD_LEVEL_H264_2p1; + break; case VIDC_720P_H264_LEVEL2p2: - { - *p_level = VCD_LEVEL_H264_2p2; - break; - } + *vcd_level = VCD_LEVEL_H264_2p2; + break; case VIDC_720P_H264_LEVEL3: - { - *p_level = VCD_LEVEL_H264_3; - break; - } + *vcd_level = VCD_LEVEL_H264_3; + break; case VIDC_720P_H264_LEVEL3p1: - { - *p_level = VCD_LEVEL_H264_3p1; - break; - } + *vcd_level = VCD_LEVEL_H264_3p1; + break; case VIDC_720P_H264_LEVEL3p2: - { - *p_level = VCD_LEVEL_H264_3p2; + *vcd_level = VCD_LEVEL_H264_3p2; break; } - - } } -static void ddl_get_vc1_dec_level( - enum vcd_codec_level_type *p_level, u32 level, - enum vcd_codec_profile_type vc1_profile) +static void ddl_get_vc1_dec_level(enum vcd_codec_level_type *vcd_level, + u32 level, enum vcd_codec_profile_type vc1_profile) { - if (vc1_profile == VCD_PROFILE_VC1_ADVANCE) { + if (vc1_profile == VCD_PROFILE_VC1_ADVANCE) { switch (level) { case VIDC_720P_VC1_LEVEL0: - { - *p_level = VCD_LEVEL_VC1_0; - break; - } + *vcd_level = VCD_LEVEL_VC1_0; + break; case VIDC_720P_VC1_LEVEL1: - { - *p_level = VCD_LEVEL_VC1_1; - break; - } + *vcd_level = VCD_LEVEL_VC1_1; + break; case VIDC_720P_VC1_LEVEL2: - { - *p_level = VCD_LEVEL_VC1_2; - break; - } + *vcd_level = VCD_LEVEL_VC1_2; + break; case VIDC_720P_VC1_LEVEL3: - { - *p_level = VCD_LEVEL_VC1_3; - break; - } + *vcd_level = VCD_LEVEL_VC1_3; + break; case VIDC_720P_VC1_LEVEL4: - { - *p_level = VCD_LEVEL_VC1_4; - break; - } + *vcd_level = VCD_LEVEL_VC1_4; + break; } return; } @@ -961,101 +800,79 @@ static void ddl_get_vc1_dec_level( /* now determine the Main and Simple profile level */ switch (level) { case VIDC_720P_VC1_LEVEL_LOW: - { - *p_level = VCD_LEVEL_VC1_LOW; - break; - } + *vcd_level = VCD_LEVEL_VC1_LOW; + break; case VIDC_720P_VC1_LEVEL_MED: - { - *p_level = VCD_LEVEL_VC1_MEDIUM; - break; - } + *vcd_level = VCD_LEVEL_VC1_MEDIUM; + break; case VIDC_720P_VC1_LEVEL_HIGH: - { - *p_level = VCD_LEVEL_VC1_HIGH; - break; - } + *vcd_level = VCD_LEVEL_VC1_HIGH; + break; } } -static void ddl_get_mpeg2_dec_level(enum vcd_codec_level_type *p_level, - u32 level) +static void ddl_get_mpeg2_dec_level(enum vcd_codec_level_type *vcd_level, + u32 level) { switch (level) { case VIDCL_720P_MPEG2_LEVEL_LOW: - { - *p_level = VCD_LEVEL_MPEG2_LOW; - break; - } + *vcd_level = VCD_LEVEL_MPEG2_LOW; + break; case VIDCL_720P_MPEG2_LEVEL_MAIN: - { - *p_level = VCD_LEVEL_MPEG2_MAIN; - break; - } + *vcd_level = VCD_LEVEL_MPEG2_MAIN; + break; case VIDCL_720P_MPEG2_LEVEL_HIGH14: - { - *p_level = VCD_LEVEL_MPEG2_HIGH_14; - break; - } + *vcd_level = VCD_LEVEL_MPEG2_HIGH_14; + break; } } -static void ddl_getdec_profilelevel(struct ddl_decoder_data_type *p_decoder, - u32 n_profile, u32 n_level) +static void ddl_getdec_profilelevel(struct ddl_decoder_data *dec, + u32 profile, u32 level) { - enum vcd_codec_profile_type profile = VCD_PROFILE_UNKNOWN; - enum vcd_codec_level_type level = VCD_LEVEL_UNKNOWN; + enum vcd_codec_profile_type vcd_profile = VCD_PROFILE_UNKNOWN; + enum vcd_codec_level_type vcd_level = VCD_LEVEL_UNKNOWN; - switch (p_decoder->codec_type.e_codec) { + switch (dec->codec_type.codec) { case VCD_CODEC_MPEG4: - { - if (n_profile == VIDC_720P_PROFILE_MPEG4_SP) - profile = VCD_PROFILE_MPEG4_SP; - else if (n_profile == VIDC_720P_PROFILE_MPEG4_ASP) - profile = VCD_PROFILE_MPEG4_ASP; + if (profile == VIDC_720P_PROFILE_MPEG4_SP) + vcd_profile = VCD_PROFILE_MPEG4_SP; + else if (profile == VIDC_720P_PROFILE_MPEG4_ASP) + vcd_profile = VCD_PROFILE_MPEG4_ASP; - ddl_getmpeg4_declevel(&level, n_level); - break; - } + ddl_getmpeg4_declevel(&vcd_level, level); + break; case VCD_CODEC_H264: - { - if (n_profile == VIDC_720P_PROFILE_H264_BASELINE) - profile = VCD_PROFILE_H264_BASELINE; - else if (n_profile == VIDC_720P_PROFILE_H264_MAIN) - profile = VCD_PROFILE_H264_MAIN; - else if (n_profile == VIDC_720P_PROFILE_H264_HIGH) - profile = VCD_PROFILE_H264_HIGH; - ddl_geth264_declevel(&level, n_level); - break; - } + if (profile == VIDC_720P_PROFILE_H264_BASELINE) + vcd_profile = VCD_PROFILE_H264_BASELINE; + else if (profile == VIDC_720P_PROFILE_H264_MAIN) + vcd_profile = VCD_PROFILE_H264_MAIN; + else if (profile == VIDC_720P_PROFILE_H264_HIGH) + vcd_profile = VCD_PROFILE_H264_HIGH; + ddl_geth264_declevel(&vcd_level, level); + break; default: case VCD_CODEC_H263: - { - break; - } + break; case VCD_CODEC_VC1: case VCD_CODEC_VC1_RCV: - { - if (n_profile == VIDC_720P_PROFILE_VC1_SP) - profile = VCD_PROFILE_VC1_SIMPLE; - else if (n_profile == VIDC_720P_PROFILE_VC1_MAIN) - profile = VCD_PROFILE_VC1_MAIN; - else if (n_profile == VIDC_720P_PROFILE_VC1_ADV) - profile = VCD_PROFILE_VC1_ADVANCE; - ddl_get_vc1_dec_level(&level, n_level, profile); - break; - } + if (profile == VIDC_720P_PROFILE_VC1_SP) + vcd_profile = VCD_PROFILE_VC1_SIMPLE; + else if (profile == VIDC_720P_PROFILE_VC1_MAIN) + vcd_profile = VCD_PROFILE_VC1_MAIN; + else if (profile == VIDC_720P_PROFILE_VC1_ADV) + vcd_profile = VCD_PROFILE_VC1_ADVANCE; + ddl_get_vc1_dec_level(&vcd_level, level, vcd_profile); + break; case VCD_CODEC_MPEG2: - { - if (n_profile == VIDC_720P_PROFILE_MPEG2_MAIN) - profile = VCD_PROFILE_MPEG2_MAIN; - else if (n_profile == VIDC_720P_PROFILE_MPEG2_SP) - profile = VCD_PROFILE_MPEG2_SIMPLE; - ddl_get_mpeg2_dec_level(&level, n_level); - break; - } + if (profile == VIDC_720P_PROFILE_MPEG2_MAIN) + vcd_profile = VCD_PROFILE_MPEG2_MAIN; + else if (profile == VIDC_720P_PROFILE_MPEG2_SP) + vcd_profile = VCD_PROFILE_MPEG2_SIMPLE; + ddl_get_mpeg2_dec_level(&vcd_level, level); + break; } - p_decoder->profile.e_profile = profile; - p_decoder->level.e_level = level; + dec->profile.profile = vcd_profile; + dec->level.level = vcd_level; } diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.c index 140a5c8878b7b..230e7ec774068 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.c +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.c @@ -20,588 +20,526 @@ #include "vcd_ddl_utils.h" #include "vcd_ddl_metadata.h" -static u32 *ddl_metadata_hdr_entry(struct ddl_client_context_type *p_ddl, - u32 n_meta_data_type) +static u32 *ddl_metadata_hdr_entry(struct ddl_client_context *ddl, + u32 meta_data_type) { - u32 n_skip_words = 0; - u32 *p_buffer; + u32 skip_words; + u32 *buffer; - if (p_ddl->b_decoding) { - p_buffer = (u32 *) - p_ddl->codec_data.decoder.meta_data_input. - p_align_virtual_addr; - n_skip_words = 32 + 1; - p_buffer += n_skip_words; + if (ddl->decoding) { + buffer = ddl->codec_data.decoder.meta_data_input.virt_addr; + skip_words = 32 + 1; + buffer += skip_words; - switch (n_meta_data_type) { + switch (meta_data_type) { default: case VCD_METADATA_DATANONE: - { - n_skip_words = 0; - break; - } + skip_words = 0; + break; case VCD_METADATA_QPARRAY: - { - n_skip_words = 3; - break; - } + skip_words = 3; + break; case VCD_METADATA_CONCEALMB: - { - n_skip_words = 6; - break; - } + skip_words = 6; + break; case VCD_METADATA_VC1: - { - n_skip_words = 9; - break; - } + skip_words = 9; + break; case VCD_METADATA_SEI: - { - n_skip_words = 12; - break; - } + skip_words = 12; + break; case VCD_METADATA_VUI: - { - n_skip_words = 15; - break; - } + skip_words = 15; + break; case VCD_METADATA_PASSTHROUGH: - { - n_skip_words = 18; - break; - } + skip_words = 18; + break; case VCD_METADATA_QCOMFILLER: - { - n_skip_words = 21; - break; - } + skip_words = 21; + break; } } else { - p_buffer = (u32 *) - p_ddl->codec_data.encoder.meta_data_input. - p_align_virtual_addr; - n_skip_words = 2; - p_buffer += n_skip_words; + buffer = ddl->codec_data.encoder.meta_data_input.virt_addr; + skip_words = 2; + buffer += skip_words; - switch (n_meta_data_type) { + switch (meta_data_type) { default: case VCD_METADATA_DATANONE: - { - n_skip_words = 0; - break; - } + skip_words = 0; + break; case VCD_METADATA_ENC_SLICE: - { - n_skip_words = 3; - break; - } + skip_words = 3; + break; case VCD_METADATA_QCOMFILLER: - { - n_skip_words = 6; - break; - } + skip_words = 6; + break; } - } - p_buffer += n_skip_words; - return p_buffer; + buffer += skip_words; + return buffer; } -void ddl_set_default_meta_data_hdr(struct ddl_client_context_type *p_ddl) +void ddl_set_default_meta_data_hdr(struct ddl_client_context *ddl) { - struct ddl_buf_addr_type *p_main_buffer = - &p_ddl->p_ddl_context->metadata_shared_input; - struct ddl_buf_addr_type *p_client_buffer; - u32 *p_hdr_entry; + struct ddl_dma_buffer *main_buffer = + &ddl->ddl_context->metadata_shared_input; + struct ddl_dma_buffer *b; + u32 *hdr; - if (p_ddl->b_decoding) - p_client_buffer = &(p_ddl->codec_data.decoder.meta_data_input); + if (ddl->decoding) + b = &ddl->codec_data.decoder.meta_data_input; else - p_client_buffer = &(p_ddl->codec_data.encoder.meta_data_input); - - DDL_METADATA_CLIENT_INPUTBUF(p_main_buffer, p_client_buffer, - p_ddl->n_channel_id); - - p_hdr_entry = ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_QCOMFILLER); - p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 1; - p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 1; - p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_QCOMFILLER; - - p_hdr_entry = ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_DATANONE); - p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 2; - p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 2; - p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_DATANONE; - - if (p_ddl->b_decoding) { - p_hdr_entry = - ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_QPARRAY); - p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 3; - p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 3; - p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_QPARRAY; - - p_hdr_entry = - ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_CONCEALMB); - p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 4; - p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 4; - p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = - VCD_METADATA_CONCEALMB; - - p_hdr_entry = ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_SEI); - p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 5; - p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 5; - p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_SEI; - - p_hdr_entry = ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_VUI); - p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 6; - p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 6; - p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_VUI; - - p_hdr_entry = ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_VC1); - p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 7; - p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 7; - p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_VC1; - - p_hdr_entry = - ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_PASSTHROUGH); - p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 8; - p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 8; - p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = - VCD_METADATA_PASSTHROUGH; + b = &ddl->codec_data.encoder.meta_data_input; + + b->phys_addr = main_buffer->phys_addr + + DDL_METADATA_CLIENT_INPUTBUFSIZE * ddl->channel_id; + b->virt_addr = (void *)((u8 *)main_buffer->virt_addr + + DDL_METADATA_CLIENT_INPUTBUFSIZE * ddl->channel_id); + + hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_QCOMFILLER); + hdr[DDL_METADATA_HDR_VERSION_INDEX] = 1; + hdr[DDL_METADATA_HDR_PORT_INDEX] = 1; + hdr[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_QCOMFILLER; + + hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_DATANONE); + hdr[DDL_METADATA_HDR_VERSION_INDEX] = 2; + hdr[DDL_METADATA_HDR_PORT_INDEX] = 2; + hdr[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_DATANONE; + + if (ddl->decoding) { + hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_QPARRAY); + hdr[DDL_METADATA_HDR_VERSION_INDEX] = 3; + hdr[DDL_METADATA_HDR_PORT_INDEX] = 3; + hdr[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_QPARRAY; + + hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_CONCEALMB); + hdr[DDL_METADATA_HDR_VERSION_INDEX] = 4; + hdr[DDL_METADATA_HDR_PORT_INDEX] = 4; + hdr[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_CONCEALMB; + + hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_SEI); + hdr[DDL_METADATA_HDR_VERSION_INDEX] = 5; + hdr[DDL_METADATA_HDR_PORT_INDEX] = 5; + hdr[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_SEI; + + hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_VUI); + hdr[DDL_METADATA_HDR_VERSION_INDEX] = 6; + hdr[DDL_METADATA_HDR_PORT_INDEX] = 6; + hdr[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_VUI; + + hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_VC1); + hdr[DDL_METADATA_HDR_VERSION_INDEX] = 7; + hdr[DDL_METADATA_HDR_PORT_INDEX] = 7; + hdr[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_VC1; + + hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_PASSTHROUGH); + hdr[DDL_METADATA_HDR_VERSION_INDEX] = 8; + hdr[DDL_METADATA_HDR_PORT_INDEX] = 8; + hdr[DDL_METADATA_HDR_TYPE_INDEX] = + VCD_METADATA_PASSTHROUGH; } else { - p_hdr_entry = - ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_ENC_SLICE); - p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = 9; - p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = 9; - p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = - VCD_METADATA_ENC_SLICE; + hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_ENC_SLICE); + hdr[DDL_METADATA_HDR_VERSION_INDEX] = 9; + hdr[DDL_METADATA_HDR_PORT_INDEX] = 9; + hdr[DDL_METADATA_HDR_TYPE_INDEX] = VCD_METADATA_ENC_SLICE; } } -static u32 ddl_supported_metadata_flag(struct ddl_client_context_type *p_ddl) +static u32 ddl_supported_metadata_flag(struct ddl_client_context *ddl) { - u32 n_flag = 0; - - if (p_ddl->b_decoding) { - enum vcd_codec_type e_codec = - p_ddl->codec_data.decoder.codec_type.e_codec; - - n_flag |= (VCD_METADATA_CONCEALMB | - VCD_METADATA_PASSTHROUGH | VCD_METADATA_QPARRAY); - if (e_codec == VCD_CODEC_H264) { - n_flag |= (VCD_METADATA_SEI | VCD_METADATA_VUI); - } else if (e_codec == VCD_CODEC_VC1 || - e_codec == VCD_CODEC_VC1_RCV) { - n_flag |= VCD_METADATA_VC1; - } + u32 flag = 0; + + if (ddl->decoding) { + enum vcd_codec codec = + ddl->codec_data.decoder.codec_type.codec; + + flag |= VCD_METADATA_CONCEALMB | VCD_METADATA_PASSTHROUGH | + VCD_METADATA_QPARRAY; + if (codec == VCD_CODEC_H264) + flag |= VCD_METADATA_SEI | VCD_METADATA_VUI; + else if (codec == VCD_CODEC_VC1 || codec == VCD_CODEC_VC1_RCV) + flag |= VCD_METADATA_VC1; } else { - n_flag |= VCD_METADATA_ENC_SLICE; + flag |= VCD_METADATA_ENC_SLICE; } - return n_flag; + return flag; } -void ddl_set_default_metadata_flag(struct ddl_client_context_type *p_ddl) +void ddl_set_default_metadata_flag(struct ddl_client_context *ddl) { - if (p_ddl->b_decoding) - p_ddl->codec_data.decoder.n_meta_data_enable_flag = 0; + if (ddl->decoding) + ddl->codec_data.decoder.meta_data_enable_flag = 0; else - p_ddl->codec_data.encoder.n_meta_data_enable_flag = 0; + ddl->codec_data.encoder.meta_data_enable_flag = 0; } -void ddl_set_default_decoder_metadata_buffer_size( - struct ddl_decoder_data_type *p_decoder, - struct vcd_property_frame_size_type *p_frame_size, - struct vcd_buffer_requirement_type *p_output_buf_req) +void ddl_set_default_decoder_metadata_buffer_size(struct ddl_decoder_data *dec, + struct vcd_property_frame_size *frame_size, + struct vcd_buffer_requirement *output_buf_req) { - u32 n_flag = p_decoder->n_meta_data_enable_flag; - u32 n_suffix = 0; - u32 n_size = 0; + u32 flag = dec->meta_data_enable_flag; + u32 suffix = 0; + u32 size = 0; - if (!n_flag) { - p_decoder->n_suffix = 0; + if (!flag) { + dec->suffix = 0; return; } - if (n_flag & VCD_METADATA_QPARRAY) { - u32 n_num_of_mb = - ((p_frame_size->n_width * p_frame_size->n_height) >> 8); - n_size = DDL_METADATA_HDR_SIZE; - n_size += n_num_of_mb; - DDL_METADATA_ALIGNSIZE(n_size); - n_suffix += n_size; + if (flag & VCD_METADATA_QPARRAY) { + u32 num_of_mb = ((frame_size->width * frame_size->height) >> 8); + size = DDL_METADATA_HDR_SIZE; + size += num_of_mb; + DDL_METADATA_ALIGNSIZE(size); + suffix += size; } - if (n_flag & VCD_METADATA_CONCEALMB) { - u32 n_num_of_mb = - ((p_frame_size->n_width * p_frame_size->n_height) >> 8); - n_size = DDL_METADATA_HDR_SIZE; - n_size *= (4 * n_num_of_mb / 2); - DDL_METADATA_ALIGNSIZE(n_size); - n_suffix += n_size; + if (flag & VCD_METADATA_CONCEALMB) { + u32 num_of_mb = ((frame_size->width * frame_size->height) >> 8); + size = DDL_METADATA_HDR_SIZE; + size *= (4 * num_of_mb / 2); + DDL_METADATA_ALIGNSIZE(size); + suffix += size; } - if (n_flag & VCD_METADATA_VC1) { - n_size = DDL_METADATA_HDR_SIZE; - n_size += DDL_METADATA_VC1_PAYLOAD_SIZE; - DDL_METADATA_ALIGNSIZE(n_size); - n_suffix += n_size; + if (flag & VCD_METADATA_VC1) { + size = DDL_METADATA_HDR_SIZE; + size += DDL_METADATA_VC1_PAYLOAD_SIZE; + DDL_METADATA_ALIGNSIZE(size); + suffix += size; } - if (n_flag & VCD_METADATA_SEI) { - n_size = DDL_METADATA_HDR_SIZE; - n_size += DDL_METADATA_SEI_PAYLOAD_SIZE; - DDL_METADATA_ALIGNSIZE(n_size); - n_suffix += (n_size * DDL_METADATA_SEI_MAX); + if (flag & VCD_METADATA_SEI) { + size = DDL_METADATA_HDR_SIZE; + size += DDL_METADATA_SEI_PAYLOAD_SIZE; + DDL_METADATA_ALIGNSIZE(size); + suffix += (size * DDL_METADATA_SEI_MAX); } - if (n_flag & VCD_METADATA_VUI) { - n_size = DDL_METADATA_HDR_SIZE; - n_size += DDL_METADATA_VUI_PAYLOAD_SIZE; - DDL_METADATA_ALIGNSIZE(n_size); - n_suffix += (n_size); + if (flag & VCD_METADATA_VUI) { + size = DDL_METADATA_HDR_SIZE; + size += DDL_METADATA_VUI_PAYLOAD_SIZE; + DDL_METADATA_ALIGNSIZE(size); + suffix += (size); } - if (n_flag & VCD_METADATA_PASSTHROUGH) { - n_size = DDL_METADATA_HDR_SIZE; - n_size += DDL_METADATA_PASSTHROUGH_PAYLOAD_SIZE; - DDL_METADATA_ALIGNSIZE(n_size); - n_suffix += (n_size); + if (flag & VCD_METADATA_PASSTHROUGH) { + size = DDL_METADATA_HDR_SIZE; + size += DDL_METADATA_PASSTHROUGH_PAYLOAD_SIZE; + DDL_METADATA_ALIGNSIZE(size); + suffix += (size); } - n_size = DDL_METADATA_EXTRADATANONE_SIZE; - DDL_METADATA_ALIGNSIZE(n_size); - n_suffix += (n_size); + size = DDL_METADATA_EXTRADATANONE_SIZE; + DDL_METADATA_ALIGNSIZE(size); + suffix += (size); - n_suffix += DDL_METADATA_EXTRAPAD_SIZE; - DDL_METADATA_ALIGNSIZE(n_suffix); + suffix += DDL_METADATA_EXTRAPAD_SIZE; + DDL_METADATA_ALIGNSIZE(suffix); - p_decoder->n_suffix = n_suffix; - p_output_buf_req->n_size += n_suffix; + dec->suffix = suffix; + output_buf_req->size += suffix; return; } -void ddl_set_default_encoder_metadata_buffer_size(struct ddl_encoder_data_type - *p_encoder) +void ddl_set_default_encoder_metadata_buffer_size(struct ddl_encoder_data *enc) { - u32 n_flag = p_encoder->n_meta_data_enable_flag; - u32 n_suffix = 0; - u32 n_size = 0; + u32 flag = enc->meta_data_enable_flag; + u32 suffix = 0; + u32 size = 0; - if (!n_flag) { - p_encoder->n_suffix = 0; + if (!flag) { + enc->suffix = 0; return; } - if (n_flag & VCD_METADATA_ENC_SLICE) { - u32 n_num_of_mb = (p_encoder->frame_size.n_width * - p_encoder->frame_size.n_height / 16 / 16); - n_size = DDL_METADATA_HDR_SIZE; - - n_size += 4; - - n_size += (8 * n_num_of_mb); - DDL_METADATA_ALIGNSIZE(n_size); - n_suffix += n_size; + if (flag & VCD_METADATA_ENC_SLICE) { + u32 num_of_mb = enc->frame_size.width * enc->frame_size.height / + 16 / 16; + size = DDL_METADATA_HDR_SIZE + 4 + 8 * num_of_mb; + DDL_METADATA_ALIGNSIZE(size); + suffix += size; } - n_size = DDL_METADATA_EXTRADATANONE_SIZE; - DDL_METADATA_ALIGNSIZE(n_size); - n_suffix += (n_size); + size = DDL_METADATA_EXTRADATANONE_SIZE; + DDL_METADATA_ALIGNSIZE(size); + suffix += (size); - n_suffix += DDL_METADATA_EXTRAPAD_SIZE; - DDL_METADATA_ALIGNSIZE(n_suffix); + suffix += DDL_METADATA_EXTRAPAD_SIZE; + DDL_METADATA_ALIGNSIZE(suffix); - p_encoder->n_suffix = n_suffix; - p_encoder->output_buf_req.n_size += n_suffix; + enc->suffix = suffix; + enc->output_buf_req.size += suffix; } -u32 ddl_set_metadata_params(struct ddl_client_context_type *p_ddl, - struct vcd_property_hdr_type *p_property_hdr, - void *p_property_value) +static u32 ddl_set_metadata_enable_client_open(struct ddl_client_context *ddl, + struct vcd_property_meta_data_enable *meta_data_enable, + u32 *meta_data_enable_flag) { - u32 vcd_status = VCD_ERR_ILLEGAL_PARM; - if (p_property_hdr->prop_id == VCD_I_METADATA_ENABLE) { - struct vcd_property_meta_data_enable_type *p_meta_data_enable = - (struct vcd_property_meta_data_enable_type *) - p_property_value; - u32 *p_meta_data_enable_flag; - enum vcd_codec_type e_codec; - if (p_ddl->b_decoding) { - p_meta_data_enable_flag = - &(p_ddl->codec_data.decoder. - n_meta_data_enable_flag); - e_codec = p_ddl->codec_data.decoder.codec_type.e_codec; + if (!meta_data_enable->meta_data_enable_flag) { + *meta_data_enable_flag = 0; + if (ddl->decoding) { + ddl_set_default_decoder_buffer_req( + &ddl->codec_data.decoder, true); } else { - p_meta_data_enable_flag = - &(p_ddl->codec_data.encoder. - n_meta_data_enable_flag); - e_codec = p_ddl->codec_data.encoder.codec_type.e_codec; + ddl_set_default_encoder_buffer_req( + &ddl->codec_data.encoder); } - if (sizeof(struct vcd_property_meta_data_enable_type) == - p_property_hdr->n_size && - DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_OPEN) && - e_codec) { - if (!p_meta_data_enable->n_meta_data_enable_flag) { - *p_meta_data_enable_flag = 0; - if (p_ddl->b_decoding) { - ddl_set_default_decoder_buffer_req - (&p_ddl->codec_data.decoder, TRUE); - } else { - ddl_set_default_encoder_buffer_req - (&p_ddl->codec_data.encoder); - } - } else { - u32 n_flag = ddl_supported_metadata_flag(p_ddl); - n_flag &= - (p_meta_data_enable-> - n_meta_data_enable_flag); - if (n_flag) { - n_flag |= DDL_METADATA_MANDATORY; - if (n_flag != - *p_meta_data_enable_flag) { - *p_meta_data_enable_flag = - n_flag; - - if (p_ddl->b_decoding) { - ddl_set_default_decoder_buffer_req - (&p_ddl->codec_data. - decoder, TRUE); + } else { + u32 flag = ddl_supported_metadata_flag(ddl); + flag &= meta_data_enable->meta_data_enable_flag; + if (flag) { + flag |= DDL_METADATA_MANDATORY; + if (flag != *meta_data_enable_flag) { + *meta_data_enable_flag = flag; + + if (ddl->decoding) { + ddl_set_default_decoder_buffer_req( + &ddl->codec_data.decoder, true); } else { - ddl_set_default_encoder_buffer_req - (&p_ddl->codec_data. - encoder); + ddl_set_default_encoder_buffer_req( + &ddl->codec_data.encoder); } - } - } } - vcd_status = VCD_S_SUCCESS; } - } else if (p_property_hdr->prop_id == VCD_I_METADATA_HEADER) { - struct vcd_property_metadata_hdr_type *p_hdr = - (struct vcd_property_metadata_hdr_type *)p_property_value; - if (sizeof(struct vcd_property_metadata_hdr_type) == - p_property_hdr->n_size) { - u32 n_flag = ddl_supported_metadata_flag(p_ddl); - n_flag |= DDL_METADATA_MANDATORY; - n_flag &= p_hdr->n_meta_data_id_type; - if (!(n_flag & (n_flag - 1))) { - u32 *p_hdr_entry = - ddl_metadata_hdr_entry(p_ddl, n_flag); - p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = - p_hdr->n_version; - p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = - p_hdr->n_port_index; - p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = - p_hdr->e_type; - vcd_status = VCD_S_SUCCESS; - } + } + return VCD_S_SUCCESS; +} + +static u32 ddl_set_metadata_header(struct ddl_client_context *ddl, + struct vcd_property_hdr *property_hdr, + struct vcd_property_metadata_hdr *hdr) +{ + u32 flag; + if (sizeof(struct vcd_property_metadata_hdr) != property_hdr->sz) + return VCD_ERR_ILLEGAL_PARM; + + flag = ddl_supported_metadata_flag(ddl); + flag |= DDL_METADATA_MANDATORY; + flag &= hdr->meta_data_id_type; + if (!(flag & (flag - 1))) { + u32 *hdr_entry = ddl_metadata_hdr_entry(ddl, flag); + hdr_entry[DDL_METADATA_HDR_VERSION_INDEX] = hdr->version; + hdr_entry[DDL_METADATA_HDR_PORT_INDEX] = hdr->port_index; + hdr_entry[DDL_METADATA_HDR_TYPE_INDEX] = hdr->type; + return VCD_S_SUCCESS; + } + return VCD_ERR_ILLEGAL_PARM; +} + +u32 ddl_set_metadata_params(struct ddl_client_context *ddl, + struct vcd_property_hdr *prop, void *value) +{ + u32 vcd_status = VCD_ERR_ILLEGAL_PARM; + if (prop->id == VCD_I_METADATA_ENABLE) { + struct vcd_property_meta_data_enable *meta_data_enable = + (struct vcd_property_meta_data_enable *)value; + u32 *meta_data_enable_flag; + enum vcd_codec codec; + if (ddl->decoding) { + meta_data_enable_flag = &ddl->codec_data.decoder. + meta_data_enable_flag; + codec = ddl->codec_data.decoder.codec_type.codec; + } else { + meta_data_enable_flag = &ddl->codec_data.encoder. + meta_data_enable_flag; + codec = ddl->codec_data.encoder.codec_type.codec; + } + if (sizeof(struct vcd_property_meta_data_enable) == prop->sz && + DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) && + codec) { + vcd_status = ddl_set_metadata_enable_client_open(ddl, + meta_data_enable, meta_data_enable_flag); } + } else if (prop->id == VCD_I_METADATA_HEADER) { + vcd_status = ddl_set_metadata_header(ddl, prop, value); } return vcd_status; } -u32 ddl_get_metadata_params(struct ddl_client_context_type *p_ddl, - struct vcd_property_hdr_type *p_property_hdr, - void *p_property_value) +u32 ddl_get_metadata_params(struct ddl_client_context *ddl, + struct vcd_property_hdr *prop, void *value) { - u32 vcd_status = VCD_ERR_ILLEGAL_PARM ; - if (p_property_hdr->prop_id == VCD_I_METADATA_ENABLE && - sizeof(struct vcd_property_meta_data_enable_type) - == p_property_hdr->n_size) { - struct vcd_property_meta_data_enable_type *p_meta_data_enable = - (struct vcd_property_meta_data_enable_type *) - p_property_value; - p_meta_data_enable->n_meta_data_enable_flag = - ((p_ddl->b_decoding) ? - (p_ddl->codec_data.decoder.n_meta_data_enable_flag) - : (p_ddl->codec_data.encoder.n_meta_data_enable_flag)); + u32 vcd_status = VCD_ERR_ILLEGAL_PARM; + struct vcd_property_meta_data_enable *enable; + struct vcd_property_metadata_hdr *hdr; + + if (prop->id == VCD_I_METADATA_ENABLE && prop->sz == sizeof(*enable)) { + enable = value; + enable->meta_data_enable_flag = ddl->decoding ? + ddl->codec_data.decoder.meta_data_enable_flag : + ddl->codec_data.encoder.meta_data_enable_flag; vcd_status = VCD_S_SUCCESS; - } else if (p_property_hdr->prop_id == VCD_I_METADATA_HEADER && - sizeof(struct vcd_property_metadata_hdr_type) == - p_property_hdr->n_size) { - struct vcd_property_metadata_hdr_type *p_hdr = - (struct vcd_property_metadata_hdr_type *) - p_property_value; - u32 n_flag = ddl_supported_metadata_flag(p_ddl); - n_flag |= DDL_METADATA_MANDATORY; - n_flag &= p_hdr->n_meta_data_id_type; - if (!(n_flag & (n_flag - 1))) { - u32 *p_hdr_entry = ddl_metadata_hdr_entry(p_ddl, - n_flag); - p_hdr->n_version = - p_hdr_entry[DDL_METADATA_HDR_VERSION_INDEX]; - p_hdr->n_port_index = - p_hdr_entry[DDL_METADATA_HDR_PORT_INDEX]; - p_hdr->e_type = - p_hdr_entry[DDL_METADATA_HDR_TYPE_INDEX]; + } else if (prop->id == VCD_I_METADATA_HEADER && + sizeof(*hdr) == prop->sz) { + u32 flag = ddl_supported_metadata_flag(ddl); + hdr = value; + flag |= DDL_METADATA_MANDATORY; + flag &= hdr->meta_data_id_type; + if (!(flag & (flag - 1))) { + u32 *hdr_entry = ddl_metadata_hdr_entry(ddl, flag); + hdr->version = + hdr_entry[DDL_METADATA_HDR_VERSION_INDEX]; + hdr->port_index = + hdr_entry[DDL_METADATA_HDR_PORT_INDEX]; + hdr->type = hdr_entry[DDL_METADATA_HDR_TYPE_INDEX]; vcd_status = VCD_S_SUCCESS; } } return vcd_status; } -void ddl_metadata_enable(struct ddl_client_context_type *p_ddl) +void ddl_metadata_enable(struct ddl_client_context *ddl) { - u32 n_flag, n_hal_flag = 0; - u32 *p_metadata_input; - if (p_ddl->b_decoding) { - n_flag = p_ddl->codec_data.decoder.n_meta_data_enable_flag; - p_metadata_input = - p_ddl->codec_data.decoder.meta_data_input. - p_align_physical_addr; + u32 flag, hal_flag = 0; + phys_addr_t input; + if (ddl->decoding) { + flag = ddl->codec_data.decoder.meta_data_enable_flag; + input = ddl->codec_data.decoder.meta_data_input.phys_addr; } else { - n_flag = p_ddl->codec_data.encoder.n_meta_data_enable_flag; - p_metadata_input = - p_ddl->codec_data.encoder.meta_data_input. - p_align_physical_addr; + flag = ddl->codec_data.encoder.meta_data_enable_flag; + input = ddl->codec_data.encoder.meta_data_input.phys_addr; } - if (n_flag) { - if (n_flag & VCD_METADATA_QPARRAY) - n_hal_flag |= VIDC_720P_METADATA_ENABLE_QP; - if (n_flag & VCD_METADATA_CONCEALMB) - n_hal_flag |= VIDC_720P_METADATA_ENABLE_CONCEALMB; - if (n_flag & VCD_METADATA_VC1) - n_hal_flag |= VIDC_720P_METADATA_ENABLE_VC1; - if (n_flag & VCD_METADATA_SEI) - n_hal_flag |= VIDC_720P_METADATA_ENABLE_SEI; - if (n_flag & VCD_METADATA_VUI) - n_hal_flag |= VIDC_720P_METADATA_ENABLE_VUI; - if (n_flag & VCD_METADATA_ENC_SLICE) - n_hal_flag |= VIDC_720P_METADATA_ENABLE_ENCSLICE; - if (n_flag & VCD_METADATA_PASSTHROUGH) - n_hal_flag |= VIDC_720P_METADATA_ENABLE_PASSTHROUGH; + if (flag) { + if (flag & VCD_METADATA_QPARRAY) + hal_flag |= VIDC_720P_METADATA_ENABLE_QP; + if (flag & VCD_METADATA_CONCEALMB) + hal_flag |= VIDC_720P_METADATA_ENABLE_CONCEALMB; + if (flag & VCD_METADATA_VC1) + hal_flag |= VIDC_720P_METADATA_ENABLE_VC1; + if (flag & VCD_METADATA_SEI) + hal_flag |= VIDC_720P_METADATA_ENABLE_SEI; + if (flag & VCD_METADATA_VUI) + hal_flag |= VIDC_720P_METADATA_ENABLE_VUI; + if (flag & VCD_METADATA_ENC_SLICE) + hal_flag |= VIDC_720P_METADATA_ENABLE_ENCSLICE; + if (flag & VCD_METADATA_PASSTHROUGH) + hal_flag |= VIDC_720P_METADATA_ENABLE_PASSTHROUGH; } else { - p_metadata_input = 0; + input = 0; } - vidc_720p_metadata_enable(n_hal_flag, p_metadata_input); + vidc_720p_metadata_enable(hal_flag, input); } -u32 ddl_encode_set_metadata_output_buf(struct ddl_client_context_type *p_ddl) +phys_addr_t ddl_encode_set_metadata_output_buf(struct ddl_client_context *ddl) { - struct ddl_encoder_data_type *p_encoder = &p_ddl->codec_data.encoder; - u32 *p_buffer; - struct vcd_frame_data_type *p_stream = &(p_ddl->output_frame.vcd_frm); - u32 n_ext_buffer_end, n_hw_metadata_start; - - n_ext_buffer_end = (u32) p_stream->p_physical + p_stream->n_alloc_len; - if (!p_encoder->n_meta_data_enable_flag) { - n_ext_buffer_end &= ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES); - return n_ext_buffer_end; + struct ddl_encoder_data *encoder = &ddl->codec_data.encoder; + u32 *buffer; + struct vcd_frame_data *stream = &(ddl->output_frame.vcd_frm); + phys_addr_t ext_buffer_end; + phys_addr_t hw_metadata_start; + + ext_buffer_end = stream->phys_addr + stream->alloc_len; + if (!encoder->meta_data_enable_flag) { + ext_buffer_end &= ~DDL_STREAMBUF_ALIGN_GUARD_BYTES; + return ext_buffer_end; } - n_hw_metadata_start = (n_ext_buffer_end - p_encoder->n_suffix) & - ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES); + hw_metadata_start = (ext_buffer_end - encoder->suffix) & + ~DDL_STREAMBUF_ALIGN_GUARD_BYTES; - n_ext_buffer_end = (n_hw_metadata_start - 1) & - ~(DDL_STREAMBUF_ALIGN_GUARD_BYTES); + ext_buffer_end = (hw_metadata_start - 1) & + ~DDL_STREAMBUF_ALIGN_GUARD_BYTES; - p_buffer = p_encoder->meta_data_input.p_align_virtual_addr; + buffer = encoder->meta_data_input.virt_addr; - *p_buffer++ = p_encoder->n_suffix; + *buffer++ = encoder->suffix; - *p_buffer = n_hw_metadata_start; + *buffer = hw_metadata_start; - p_encoder->n_meta_data_offset = - n_hw_metadata_start - (u32) p_stream->p_physical; + encoder->meta_data_offset = hw_metadata_start - stream->phys_addr; - return n_ext_buffer_end; + return ext_buffer_end; } -void ddl_decode_set_metadata_output(struct ddl_decoder_data_type *p_decoder) +void ddl_decode_set_metadata_output(struct ddl_decoder_data *dec) { - u32 *p_buffer; - u32 n_loopc; + int i; + u32 *buffer; - if (!p_decoder->n_meta_data_enable_flag) { - p_decoder->n_meta_data_offset = 0; + if (!dec->meta_data_enable_flag) { + dec->meta_data_offset = 0; return; } - p_decoder->n_meta_data_offset = ddl_get_yuv_buffer_size( - &p_decoder->client_frame_size, &p_decoder->buf_format, - (!p_decoder->n_progressive_only)); + dec->meta_data_offset = ddl_get_yuv_buffer_size(&dec->client_frame_size, + &dec->buf_format, !dec->progressive_only); - p_buffer = p_decoder->meta_data_input.p_align_virtual_addr; + buffer = dec->meta_data_input.virt_addr; - *p_buffer++ = p_decoder->n_suffix; + *buffer++ = dec->suffix; - for (n_loopc = 0; n_loopc < p_decoder->dp_buf.n_no_of_dec_pic_buf; - ++n_loopc) { - *p_buffer++ = (u32) (p_decoder->n_meta_data_offset + (u8 *) - p_decoder->dp_buf. - a_dec_pic_buffers[n_loopc].vcd_frm. - p_physical); - } + for (i = 0; i < dec->dp_buf.no_of_dec_pic_buf; ++i) + *buffer++ = dec->dp_buf.dec_pic_buffers[i].vcd_frm.phys_addr + + dec->meta_data_offset; } -void ddl_process_encoder_metadata(struct ddl_client_context_type *p_ddl) +//TOOD consider combining ddl_process_xxx_metadata +void ddl_process_encoder_metadata(struct ddl_client_context *ddl) { - struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); - struct vcd_frame_data_type *p_out_frame = - &(p_ddl->output_frame.vcd_frm); - u32 *p_qfiller_hdr, *p_qfiller, n_start_addr; - u32 n_qfiller_size; - - if (!p_encoder->n_meta_data_enable_flag) { - p_out_frame->n_flags &= ~(VCD_FRAME_FLAG_EXTRADATA); + struct ddl_encoder_data *enc = &ddl->codec_data.encoder; + struct vcd_frame_data *frm = &ddl->output_frame.vcd_frm; + u32 *qfill_hdr; + u32 *qfill; + unsigned long tmp; + size_t qfill_sz; + + if (!enc->meta_data_enable_flag) { + frm->flags &= ~VCD_FRAME_FLAG_EXTRADATA; return; } - if (!p_encoder->enc_frame_info.n_metadata_exists) { - p_out_frame->n_flags &= ~(VCD_FRAME_FLAG_EXTRADATA); + if (!enc->enc_frame_info.metadata_exists) { + frm->flags &= ~VCD_FRAME_FLAG_EXTRADATA; return; } - p_out_frame->n_flags |= VCD_FRAME_FLAG_EXTRADATA; + frm->flags |= VCD_FRAME_FLAG_EXTRADATA; - n_start_addr = (u32) ((u8 *) p_out_frame->p_virtual + - p_out_frame->n_offset); - p_qfiller = (u32 *) ((p_out_frame->n_data_len + n_start_addr + 3) & ~3); + tmp = (unsigned long)frm->virt_addr + frm->offset + frm->data_len; + qfill = (u32 *)ALIGN(tmp, 4); - n_qfiller_size = (u32) ((p_encoder->n_meta_data_offset + - (u8 *) p_out_frame->p_virtual) - - (u8 *) p_qfiller); + qfill_sz = enc->meta_data_offset + (u8 *)frm->virt_addr - (u8 *)qfill; - p_qfiller_hdr = ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_QCOMFILLER); + qfill_hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_QCOMFILLER); - *p_qfiller++ = n_qfiller_size; - *p_qfiller++ = p_qfiller_hdr[DDL_METADATA_HDR_VERSION_INDEX]; - *p_qfiller++ = p_qfiller_hdr[DDL_METADATA_HDR_PORT_INDEX]; - *p_qfiller++ = p_qfiller_hdr[DDL_METADATA_HDR_TYPE_INDEX]; - *p_qfiller = (u32) (n_qfiller_size - DDL_METADATA_HDR_SIZE); + *qfill++ = qfill_sz; + *qfill++ = qfill_hdr[DDL_METADATA_HDR_VERSION_INDEX]; + *qfill++ = qfill_hdr[DDL_METADATA_HDR_PORT_INDEX]; + *qfill++ = qfill_hdr[DDL_METADATA_HDR_TYPE_INDEX]; + *qfill = qfill_sz - DDL_METADATA_HDR_SIZE; } -void ddl_process_decoder_metadata(struct ddl_client_context_type *p_ddl) +void ddl_process_decoder_metadata(struct ddl_client_context *ddl) { - struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); - struct vcd_frame_data_type *p_output_frame = - &(p_ddl->output_frame.vcd_frm); - u32 *p_qfiller_hdr, *p_qfiller; - u32 n_qfiller_size; - - if (!p_decoder->n_meta_data_enable_flag) { - p_output_frame->n_flags &= ~(VCD_FRAME_FLAG_EXTRADATA); + struct ddl_decoder_data *dec = &ddl->codec_data.decoder; + struct vcd_frame_data *frm = &ddl->output_frame.vcd_frm; + u32 *qfill_hdr; + u32 *qfill; + size_t qfill_sz; + unsigned long tmp; + + if (!dec->meta_data_enable_flag) { + frm->flags &= ~VCD_FRAME_FLAG_EXTRADATA; return; } - - if (!p_decoder->dec_disp_info.n_metadata_exists) { - p_output_frame->n_flags &= ~(VCD_FRAME_FLAG_EXTRADATA); + if (!dec->dec_disp_info.metadata_exists) { + frm->flags &= ~VCD_FRAME_FLAG_EXTRADATA; return; } - p_output_frame->n_flags |= VCD_FRAME_FLAG_EXTRADATA; - - if (p_output_frame->n_data_len != p_decoder->n_meta_data_offset) { - p_qfiller = (u32 *) ((u32) ((p_output_frame->n_data_len + - p_output_frame->n_offset + - (u8 *) p_output_frame->p_virtual) + - 3) & ~3); - - n_qfiller_size = (u32) ((p_decoder->n_meta_data_offset + - (u8 *) p_output_frame->p_virtual) - - (u8 *) p_qfiller); - - p_qfiller_hdr = - ddl_metadata_hdr_entry(p_ddl, VCD_METADATA_QCOMFILLER); - *p_qfiller++ = n_qfiller_size; - *p_qfiller++ = p_qfiller_hdr[DDL_METADATA_HDR_VERSION_INDEX]; - *p_qfiller++ = p_qfiller_hdr[DDL_METADATA_HDR_PORT_INDEX]; - *p_qfiller++ = p_qfiller_hdr[DDL_METADATA_HDR_TYPE_INDEX]; - *p_qfiller = (u32) (n_qfiller_size - DDL_METADATA_HDR_SIZE); - } + frm->flags |= VCD_FRAME_FLAG_EXTRADATA; + + if (frm->data_len == dec->meta_data_offset) + return; + + tmp = (unsigned long)frm->virt_addr + frm->offset + frm->data_len; + qfill = (u32 *)ALIGN(tmp, 4); + + qfill_sz = dec->meta_data_offset + (u8 *)frm->virt_addr - (u8 *)qfill; + + qfill_hdr = ddl_metadata_hdr_entry(ddl, VCD_METADATA_QCOMFILLER); + + *qfill++ = qfill_sz; + *qfill++ = qfill_hdr[DDL_METADATA_HDR_VERSION_INDEX]; + *qfill++ = qfill_hdr[DDL_METADATA_HDR_PORT_INDEX]; + *qfill++ = qfill_hdr[DDL_METADATA_HDR_TYPE_INDEX]; + *qfill = qfill_sz - DDL_METADATA_HDR_SIZE; } diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.h index c8073ca1952e0..b0fa84c50547a 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.h +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_metadata.h @@ -39,8 +39,7 @@ #define DDL_METADATA_ALIGNSIZE(x) ((x) = (((x) + 0x7) & ~0x7)) -#define DDL_METADATA_MANDATORY (VCD_METADATA_DATANONE | \ - VCD_METADATA_QCOMFILLER) +#define DDL_METADATA_MANDATORY (VCD_METADATA_DATANONE | VCD_METADATA_QCOMFILLER) #define DDL_METADATA_VC1_PAYLOAD_SIZE (38*4) @@ -55,41 +54,25 @@ #define DDL_METADATA_TOTAL_INPUTBUFSIZE \ (DDL_METADATA_CLIENT_INPUTBUFSIZE * VCD_MAX_NO_CLIENT) -#define DDL_METADATA_CLIENT_INPUTBUF(p_main_buffer, p_client_buffer, \ - n_channel_id) \ -{ \ - (p_client_buffer)->p_align_physical_addr = (u32 *)\ - ((u8 *)(p_main_buffer)->p_align_physical_addr + \ - (DDL_METADATA_CLIENT_INPUTBUFSIZE * (n_channel_id)) \ - ); \ - (p_client_buffer)->p_align_virtual_addr = (u32 *)\ - ((u8 *)(p_main_buffer)->p_align_virtual_addr + \ - (DDL_METADATA_CLIENT_INPUTBUFSIZE * (n_channel_id)) \ - ); \ - (p_client_buffer)->p_virtual_base_addr = 0; \ -} - #define DDL_METADATA_HDR_VERSION_INDEX 0 #define DDL_METADATA_HDR_PORT_INDEX 1 #define DDL_METADATA_HDR_TYPE_INDEX 2 -void ddl_set_default_meta_data_hdr(struct ddl_client_context_type *p_ddl); -u32 ddl_get_metadata_params(struct ddl_client_context_type *p_ddl, - struct vcd_property_hdr_type *p_property_hdr, void *p_property_value); -u32 ddl_set_metadata_params(struct ddl_client_context_type *p_ddl, - struct vcd_property_hdr_type *p_property_hdr, - void *p_property_value); -void ddl_set_default_metadata_flag(struct ddl_client_context_type *p_ddl); -void ddl_set_default_decoder_metadata_buffer_size - (struct ddl_decoder_data_type *p_decoder, - struct vcd_property_frame_size_type *p_frame_size, - struct vcd_buffer_requirement_type *p_output_buf_req); -void ddl_set_default_encoder_metadata_buffer_size(struct ddl_encoder_data_type - *p_encoder); -void ddl_metadata_enable(struct ddl_client_context_type *p_ddl); -u32 ddl_encode_set_metadata_output_buf(struct ddl_client_context_type *p_ddl); -void ddl_decode_set_metadata_output(struct ddl_decoder_data_type *p_decoder); -void ddl_process_encoder_metadata(struct ddl_client_context_type *p_ddl); -void ddl_process_decoder_metadata(struct ddl_client_context_type *p_ddl); +void ddl_set_default_meta_data_hdr(struct ddl_client_context *ddl); +u32 ddl_get_metadata_params(struct ddl_client_context *ddl, + struct vcd_property_hdr *property_hdr, void *property_value); +u32 ddl_set_metadata_params(struct ddl_client_context *ddl, + struct vcd_property_hdr *property_hdr, void *property_value); +void ddl_set_default_metadata_flag(struct ddl_client_context *ddl); +void ddl_set_default_decoder_metadata_buffer_size(struct ddl_decoder_data *dec, + struct vcd_property_frame_size *frame_size, + struct vcd_buffer_requirement *output_buf_req); +void ddl_set_default_encoder_metadata_buffer_size(struct ddl_encoder_data *enc); +void ddl_metadata_enable(struct ddl_client_context *ddl); +phys_addr_t ddl_encode_set_metadata_output_buf(struct ddl_client_context *ddl); +void ddl_decode_set_metadata_output(struct ddl_decoder_data *decoder); +void ddl_process_encoder_metadata(struct ddl_client_context *ddl); +void ddl_process_decoder_metadata(struct ddl_client_context *ddl); + #endif diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_properties.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_properties.c index 211b33765096f..6e4037b219677 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl_properties.c +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_properties.c @@ -21,1798 +21,1288 @@ #include "vcd_ddl_utils.h" #include "vcd_ddl_metadata.h" -static u32 ddl_set_dec_property(struct ddl_client_context_type *pddl, - struct vcd_property_hdr_type *p_property_hdr, - void *p_property_value); -static u32 ddl_set_enc_property(struct ddl_client_context_type *pddl, - struct vcd_property_hdr_type *p_property_hdr, - void *p_property_value); -static u32 ddl_get_dec_property(struct ddl_client_context_type *pddl, - struct vcd_property_hdr_type *p_property_hdr, - void *p_property_value); -static u32 ddl_get_enc_property(struct ddl_client_context_type *pddl, - struct vcd_property_hdr_type *p_property_hdr, - void *p_property_value); -static u32 ddl_set_enc_dynamic_property(struct ddl_encoder_data_type *p_encoder, - struct vcd_property_hdr_type - *p_property_hdr, - void *p_property_value); -static void ddl_set_default_enc_property(struct ddl_client_context_type *p_ddl); -static void ddl_set_default_enc_profile(struct ddl_encoder_data_type - *p_encoder); -static void ddl_set_default_enc_level(struct ddl_encoder_data_type *p_encoder); -static void ddl_set_default_enc_vop_timing(struct ddl_encoder_data_type - *p_encoder); -static void ddl_set_default_enc_intra_period(struct ddl_encoder_data_type - *p_encoder); -static void ddl_set_default_enc_rc_params(struct ddl_encoder_data_type - *p_encoder); -static u32 ddl_valid_buffer_requirement(struct vcd_buffer_requirement_type - *original_buf_req, - struct vcd_buffer_requirement_type - *req_buf_req); -static u32 ddl_decoder_min_num_dpb(struct ddl_decoder_data_type *p_decoder); -static u32 ddl_set_dec_buffers - (struct ddl_decoder_data_type *p_decoder, - struct ddl_property_dec_pic_buffers_type *p_dpb); - -u32 ddl_set_property(u32 *ddl_handle, - struct vcd_property_hdr_type *p_property_hdr, void *p_property_value) +static u32 ddl_set_dec_property(struct ddl_client_context *pddl, + struct vcd_property_hdr *hdr, void *value); +static u32 ddl_set_enc_property(struct ddl_client_context *pddl, + struct vcd_property_hdr *hdr, void *value); +static u32 ddl_get_dec_property(struct ddl_client_context *pddl, + struct vcd_property_hdr *hdr, void *value); +static u32 ddl_get_enc_property(struct ddl_client_context *pddl, + struct vcd_property_hdr *hdr, void *value); +static u32 ddl_set_enc_dynamic_property(struct ddl_encoder_data *enc, + struct vcd_property_hdr *hdr, void *value); +static void ddl_set_default_enc_property(struct ddl_client_context *ddl); +static void ddl_set_default_enc_profile(struct ddl_encoder_data *enc); +static void ddl_set_default_enc_level(struct ddl_encoder_data *enc); +static void ddl_set_default_enc_vop_timing(struct ddl_encoder_data *enc); +static void ddl_set_default_enc_intra_period(struct ddl_encoder_data *enc); +static void ddl_set_default_enc_rc_params(struct ddl_encoder_data *enc); +static u32 ddl_valid_buffer_requirement(struct vcd_buffer_requirement + *orig, struct vcd_buffer_requirement *req); +static u32 ddl_decoder_min_num_dpb(struct ddl_decoder_data *dec); +static u32 ddl_set_dec_buffers(struct ddl_decoder_data *dec, + struct ddl_property_dec_pic_buffers *dpb); + +u32 ddl_set_property(u32 *ddl_handle, struct vcd_property_hdr *hdr, + void *value) { u32 vcd_status; - struct ddl_context_type *p_ddl_context; - struct ddl_client_context_type *p_ddl = - (struct ddl_client_context_type *)ddl_handle; + struct ddl_context *ddl_context; + struct ddl_client_context *ddl = (struct ddl_client_context *) + ddl_handle; - if (!p_property_hdr || !p_property_value) { - VIDC_LOGERR_STRING("ddl_set_prop:Bad_argument"); + if (!hdr || !value) { + pr_err("ddl_set_prop:Bad_argument\n"); return VCD_ERR_ILLEGAL_PARM; } - p_ddl_context = ddl_get_context(); + ddl_context = ddl_get_context(); - if (!DDL_IS_INITIALIZED(p_ddl_context)) { - VIDC_LOGERR_STRING("ddl_set_prop:Not_inited"); + if (!DDL_IS_INITIALIZED(ddl_context)) { + pr_err("ddl_set_prop:Not_inited\n"); return VCD_ERR_ILLEGAL_OP; } - if (!p_ddl) { - VIDC_LOGERR_STRING("ddl_set_prop:Bad_handle"); + if (!ddl) { + pr_err("ddl_set_prop:Bad_handle\n"); return VCD_ERR_BAD_HANDLE; } - if (p_ddl->b_decoding) { - vcd_status = - ddl_set_dec_property(p_ddl, p_property_hdr, - p_property_value); - } else { - vcd_status = - ddl_set_enc_property(p_ddl, p_property_hdr, - p_property_value); - } + if (ddl->decoding) + vcd_status = ddl_set_dec_property(ddl, hdr, value); + else + vcd_status = ddl_set_enc_property(ddl, hdr, value); if (vcd_status) - VIDC_LOGERR_STRING("ddl_set_prop:FAILED"); + pr_err("ddl_set_prop:FAILED\n"); return vcd_status; } -u32 ddl_get_property(u32 *ddl_handle, - struct vcd_property_hdr_type *p_property_hdr, void *p_property_value) +u32 ddl_get_property(u32 *ddl_handle, struct vcd_property_hdr *hdr, void *value) { - u32 vcd_status = VCD_ERR_ILLEGAL_PARM; - struct ddl_context_type *p_ddl_context; - struct ddl_client_context_type *p_ddl = - (struct ddl_client_context_type *)ddl_handle; + struct ddl_context *ddl_context; + struct ddl_client_context *ddl = (struct ddl_client_context *) + ddl_handle; - if (!p_property_hdr || !p_property_value) + if (!hdr || !value) return VCD_ERR_ILLEGAL_PARM; - if (p_property_hdr->prop_id == DDL_I_CAPABILITY) { - if (sizeof(struct ddl_property_capability_type) == - p_property_hdr->n_size) { - struct ddl_property_capability_type *p_ddl_capability = - (struct ddl_property_capability_type *) - p_property_value; - p_ddl_capability->n_max_num_client = VCD_MAX_NO_CLIENT; - p_ddl_capability->b_exclusive = - VCD_COMMAND_EXCLUSIVE; - p_ddl_capability->n_frame_command_depth = - VCD_FRAME_COMMAND_DEPTH; - p_ddl_capability->n_general_command_depth = - VCD_GENERAL_COMMAND_DEPTH; - p_ddl_capability->n_ddl_time_out_in_ms = - DDL_HW_TIMEOUT_IN_MS; + if (hdr->id == DDL_I_CAPABILITY) { + struct ddl_property_capability *cap; + if (sizeof(*cap) == hdr->sz) { + cap = value; + cap->max_num_client = VCD_MAX_NO_CLIENT; + cap->exclusive = VCD_COMMAND_EXCLUSIVE; + cap->frame_command_depth = VCD_FRAME_COMMAND_DEPTH; + cap->general_command_depth = VCD_GENERAL_COMMAND_DEPTH; + cap->ddl_time_out_in_ms = DDL_HW_TIMEOUT_IN_MS; vcd_status = VCD_S_SUCCESS; } return vcd_status; } - p_ddl_context = ddl_get_context(); - if (!DDL_IS_INITIALIZED(p_ddl_context)) + ddl_context = ddl_get_context(); + if (!DDL_IS_INITIALIZED(ddl_context)) return VCD_ERR_ILLEGAL_OP; - if (!p_ddl) + if (!ddl) return VCD_ERR_BAD_HANDLE; - if (p_ddl->b_decoding) { - vcd_status = - ddl_get_dec_property(p_ddl, p_property_hdr, - p_property_value); - } else { - vcd_status = - ddl_get_enc_property(p_ddl, p_property_hdr, - p_property_value); - } + if (ddl->decoding) + vcd_status = ddl_get_dec_property(ddl, hdr, value); + else + vcd_status = ddl_get_enc_property(ddl, hdr, value); if (vcd_status) - VIDC_LOGERR_STRING("ddl_get_prop:FAILED"); + pr_err("ddl_get_prop:FAILED\n"); return vcd_status; } -u32 ddl_decoder_ready_to_start(struct ddl_client_context_type *p_ddl, - struct vcd_sequence_hdr_type *p_header) +u32 ddl_decoder_ready_to_start(struct ddl_client_context *ddl, + struct vcd_phys_sequence_hdr *seq_hdr) { - struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); - if (!p_decoder->codec_type.e_codec) { - VIDC_LOGERR_STRING("ddl_dec_start_check:Codec_not_set"); - return FALSE; + struct ddl_decoder_data *dec = &ddl->codec_data.decoder; + if (!dec->codec_type.codec) { + pr_err("ddl_dec_start_check:Codec_not_set\n"); + return false; } - if ((!p_header) && - (!p_decoder->client_frame_size.n_height || - !p_decoder->client_frame_size.n_width) - ) { - VIDC_LOGERR_STRING - ("ddl_dec_start_check:Client_height_width_default"); - return FALSE; + if (!seq_hdr && (!dec->client_frame_size.height || + !dec->client_frame_size.width)) { + pr_err("ddl_dec_start_check:" + "Client_height_width_default\n"); + return false; } - return TRUE; + return true; } -u32 ddl_encoder_ready_to_start(struct ddl_client_context_type *p_ddl) +u32 ddl_encoder_ready_to_start(struct ddl_client_context *ddl) { - struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); - - if (!p_encoder->codec_type.e_codec || - !p_encoder->frame_size.n_height || - !p_encoder->frame_size.n_width || - !p_encoder->frame_rate.n_fps_denominator || - !p_encoder->frame_rate.n_fps_numerator || - !p_encoder->target_bit_rate.n_target_bitrate) { - return FALSE; + struct ddl_encoder_data *enc = &ddl->codec_data.encoder; + + if (!enc->codec_type.codec || !enc->frame_size.height || + !enc->frame_size.width || + !enc->frame_rate.fps_denominator || + !enc->frame_rate.fps_numerator || + !enc->target_bit_rate.target_bitrate) { + return false; } - return TRUE; + return true; } -static u32 ddl_set_dec_property - (struct ddl_client_context_type *p_ddl, - struct vcd_property_hdr_type *p_property_hdr, void *p_property_value) { +static u32 ddl_set_dec_property(struct ddl_client_context *ddl, + struct vcd_property_hdr *hdr, void *value) { u32 vcd_status = VCD_ERR_ILLEGAL_PARM; - struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); - switch (p_property_hdr->prop_id) { + struct ddl_decoder_data *dec = &ddl->codec_data.decoder; + switch (hdr->id) { case DDL_I_DPB_RELEASE: - { - if (sizeof(struct ddl_frame_data_type_tag) == - p_property_hdr->n_size - && p_decoder->dp_buf.n_no_of_dec_pic_buf) { - vcd_status = - ddl_decoder_dpb_transact(p_decoder, - (struct ddl_frame_data_type_tag *) - p_property_value, - DDL_DPB_OP_MARK_FREE); - } - break; + if (sizeof(struct ddl_frame_data_tag) == hdr->sz && + dec->dp_buf.no_of_dec_pic_buf) { + vcd_status = ddl_decoder_dpb_transact(dec, value, + DDL_DPB_OP_MARK_FREE); } + break; case DDL_I_DPB: - { - struct ddl_property_dec_pic_buffers_type *p_dpb = - (struct ddl_property_dec_pic_buffers_type *) - p_property_value; - - if (sizeof(struct ddl_property_dec_pic_buffers_type) == - p_property_hdr->n_size && - (DDLCLIENT_STATE_IS - (p_ddl, DDL_CLIENT_WAIT_FOR_INITCODEC) - || DDLCLIENT_STATE_IS(p_ddl, - DDL_CLIENT_WAIT_FOR_DPB) - ) && - p_dpb->n_no_of_dec_pic_buf >= - p_decoder->client_output_buf_req.n_actual_count) { - vcd_status = - ddl_set_dec_buffers(p_decoder, p_dpb); - } - break; - } + { + struct ddl_property_dec_pic_buffers *dpb = value; + if (sizeof(*dpb) == hdr->sz && + (DDLCLIENT_STATE_IS(ddl, + DDL_CLIENT_WAIT_FOR_INITCODEC) || + DDLCLIENT_STATE_IS(ddl, + DDL_CLIENT_WAIT_FOR_DPB)) && + dpb->no_of_dec_pic_buf >= + dec->client_output_buf_req.actual_count) { + vcd_status = ddl_set_dec_buffers(dec, dpb); + } + break; + } case DDL_I_REQ_OUTPUT_FLUSH: - { - if (sizeof(u32) == p_property_hdr->n_size) { - p_decoder->n_dynamic_prop_change |= - DDL_DEC_REQ_OUTPUT_FLUSH; - p_decoder->dpb_mask.n_client_mask = 0; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(u32) == hdr->sz) { + dec->dynamic_prop_change |= DDL_DEC_REQ_OUTPUT_FLUSH; + dec->dpb_mask.client_mask = 0; + vcd_status = VCD_S_SUCCESS; } + break; case DDL_I_INPUT_BUF_REQ: - { - struct vcd_buffer_requirement_type *p_buffer_req = - (struct vcd_buffer_requirement_type *) - p_property_value; - if (sizeof(struct vcd_buffer_requirement_type) == - p_property_hdr->n_size && - (ddl_valid_buffer_requirement( - &p_decoder->min_input_buf_req, - p_buffer_req))) { - p_decoder->client_input_buf_req = *p_buffer_req; - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_buffer_requirement *buf_req = value; + if (sizeof(*buf_req) == hdr->sz && + ddl_valid_buffer_requirement( + &dec->min_input_buf_req, buf_req)) { + dec->client_input_buf_req = *buf_req; + vcd_status = VCD_S_SUCCESS; } + break; + } case DDL_I_OUTPUT_BUF_REQ: - { - struct vcd_buffer_requirement_type *p_buffer_req = - (struct vcd_buffer_requirement_type *) - p_property_value; - if (sizeof(struct vcd_buffer_requirement_type) == - p_property_hdr->n_size && - (ddl_valid_buffer_requirement( - &p_decoder->min_output_buf_req, - p_buffer_req))) { - p_decoder->client_output_buf_req = - *p_buffer_req; - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_buffer_requirement *buf_req = value; + if (sizeof(*buf_req) == hdr->sz && + ddl_valid_buffer_requirement( + &dec->min_output_buf_req, buf_req)) { + dec->client_output_buf_req = *buf_req; + vcd_status = VCD_S_SUCCESS; } - + break; + } case VCD_I_CODEC: - { - struct vcd_property_codec_type *p_codec = - (struct vcd_property_codec_type *)p_property_value; - if (sizeof(struct vcd_property_codec_type) == - p_property_hdr->n_size - && DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_OPEN) - ) { - u32 b_return; - vcd_fw_transact(FALSE, TRUE, - p_decoder->codec_type.e_codec); - b_return = vcd_fw_transact(TRUE, TRUE, - p_codec->e_codec); - if (b_return) { - p_decoder->codec_type = *p_codec; - ddl_set_default_dec_property(p_ddl); - vcd_status = VCD_S_SUCCESS; - } else { - b_return = vcd_fw_transact(TRUE, TRUE, - p_decoder->codec_type.e_codec); - vcd_status = VCD_ERR_NOT_SUPPORTED; - } - } - break; + { + struct vcd_property_codec *codec = value; + if (sizeof(*codec) == hdr->sz && + DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) { + if (!vcd_fw_is_codec_supported(true, codec->codec)) { + vcd_status = VCD_ERR_NOT_SUPPORTED; + break; + } + dec->codec_type = *codec; + ddl_set_default_dec_property(ddl); + vcd_status = VCD_S_SUCCESS; } + break; + } case VCD_I_POST_FILTER: - { - if (sizeof(struct vcd_property_post_filter_type) == - p_property_hdr->n_size - && DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_OPEN) && - (p_decoder->codec_type.e_codec == VCD_CODEC_MPEG4 || - p_decoder->codec_type.e_codec == VCD_CODEC_MPEG2) - ) { - p_decoder->post_filter = - *(struct vcd_property_post_filter_type *) - p_property_value; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_post_filter) == hdr->sz && + DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) && + (dec->codec_type.codec == VCD_CODEC_MPEG4 || + dec->codec_type.codec == VCD_CODEC_MPEG2)) { + dec->post_filter = *(struct vcd_property_post_filter *) + value; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_FRAME_SIZE: - { - struct vcd_property_frame_size_type *p_frame_size = - (struct vcd_property_frame_size_type *) - p_property_value; - - if ((sizeof(struct vcd_property_frame_size_type) == - p_property_hdr->n_size) && - (DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_OPEN))) { - if (p_decoder->client_frame_size.n_height != - p_frame_size->n_height - || p_decoder->client_frame_size.n_width != - p_frame_size->n_width) { - p_decoder->client_frame_size = - *p_frame_size; - ddl_calculate_stride( - &p_decoder->client_frame_size, - !p_decoder->n_progressive_only); - ddl_set_default_decoder_buffer_req - (p_decoder, TRUE); - } - vcd_status = VCD_S_SUCCESS; + { + struct vcd_property_frame_size *frame_size = value; + if ((sizeof(*frame_size) == hdr->sz) && + DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) { + if (dec->client_frame_size.height != frame_size->height + || dec->client_frame_size.width != + frame_size->width) { + dec->client_frame_size = *frame_size; + ddl_calculate_stride(&dec->client_frame_size, + !dec->progressive_only); + ddl_set_default_decoder_buffer_req(dec, true); } - break; + vcd_status = VCD_S_SUCCESS; } + break; + } case VCD_I_BUFFER_FORMAT: - { - struct vcd_property_buffer_format_type *p_tile = - (struct vcd_property_buffer_format_type *) - p_property_value; - if (sizeof(struct vcd_property_buffer_format_type) == - p_property_hdr->n_size && - DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_OPEN) && - (p_tile->e_buffer_format == VCD_BUFFER_FORMAT_NV12 - || p_tile->e_buffer_format == - VCD_BUFFER_FORMAT_TILE_4x2) - ) { - if (p_tile->e_buffer_format != - p_decoder->buf_format.e_buffer_format) { - p_decoder->buf_format = *p_tile; - ddl_set_default_decoder_buffer_req - (p_decoder, TRUE); - } - vcd_status = VCD_S_SUCCESS; + { + struct vcd_property_buffer_format *tile = value; + if (sizeof(*tile) == hdr->sz && + DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) && + (tile->buffer_format == VCD_BUFFER_FORMAT_NV12 + || tile->buffer_format == + VCD_BUFFER_FORMAT_TILE_4x2)) { + if (tile->buffer_format != + dec->buf_format.buffer_format) { + dec->buf_format = *tile; + ddl_set_default_decoder_buffer_req(dec, true); } - break; + vcd_status = VCD_S_SUCCESS; } + break; + } case VCD_I_METADATA_ENABLE: case VCD_I_METADATA_HEADER: - { - vcd_status = ddl_set_metadata_params(p_ddl, - p_property_hdr, - p_property_value); - break; - } + vcd_status = ddl_set_metadata_params(ddl, hdr, value); + break; default: - { - vcd_status = VCD_ERR_ILLEGAL_OP; - break; - } + vcd_status = VCD_ERR_ILLEGAL_OP; + break; } return vcd_status; } -static u32 ddl_set_enc_property(struct ddl_client_context_type *p_ddl, - struct vcd_property_hdr_type *p_property_hdr, void *p_property_value) +static u32 ddl_set_enc_property(struct ddl_client_context *ddl, + struct vcd_property_hdr *hdr, void *value) { u32 vcd_status = VCD_ERR_ILLEGAL_PARM; - struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); + struct ddl_encoder_data *enc = &ddl->codec_data.encoder; - if (DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_WAIT_FOR_FRAME)) { - vcd_status = ddl_set_enc_dynamic_property(p_encoder, - p_property_hdr, p_property_value); + if (DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_WAIT_FOR_FRAME)) { + vcd_status = ddl_set_enc_dynamic_property(enc, hdr, value); return vcd_status; } - if (!DDLCLIENT_STATE_IS(p_ddl, DDL_CLIENT_OPEN)) { - VIDC_LOGERR_STRING - ("ddl_set_enc_property:Fails_as_not_in_open_state"); + if (!DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN)) { + pr_err("ddl_set_enc_property:" + "Fails_as_not_in_open_state\n"); return VCD_ERR_ILLEGAL_OP; } - switch (p_property_hdr->prop_id) { + switch (hdr->id) { case VCD_I_TARGET_BITRATE: - { - struct vcd_property_target_bitrate_type *p_bitrate = - (struct vcd_property_target_bitrate_type *) - p_property_value; - if (sizeof(struct vcd_property_target_bitrate_type) == - p_property_hdr->n_size && - p_bitrate->n_target_bitrate) { - p_encoder->target_bit_rate = *p_bitrate; - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_property_target_bitrate *bitrate = value; + if (sizeof(*bitrate) == hdr->sz && + bitrate->target_bitrate) { + enc->target_bit_rate = *bitrate; + vcd_status = VCD_S_SUCCESS; } - + break; + } case VCD_I_FRAME_RATE: - { - struct vcd_property_frame_rate_type *p_framerate = - (struct vcd_property_frame_rate_type *) - p_property_value; - if (sizeof(struct vcd_property_frame_rate_type) - == p_property_hdr->n_size && - p_framerate->n_fps_denominator && - p_framerate->n_fps_numerator) { - p_encoder->frame_rate = *p_framerate; - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_property_frame_rate *framerate = value; + if (sizeof(*framerate) == hdr->sz && + framerate->fps_denominator && + framerate->fps_numerator) { + enc->frame_rate = *framerate; + vcd_status = VCD_S_SUCCESS; } + break; + } case VCD_I_FRAME_SIZE: - { - struct vcd_property_frame_size_type *p_framesize = - (struct vcd_property_frame_size_type *) - p_property_value; - - if ((sizeof(struct vcd_property_frame_size_type) - == p_property_hdr->n_size) && - (DDL_ALLOW_ENC_FRAMESIZE(p_framesize->n_width, - p_framesize->n_height)) - ) { - p_encoder->frame_size = *p_framesize; - ddl_calculate_stride(&p_encoder->frame_size, - FALSE); - ddl_set_default_encoder_buffer_req(p_encoder); - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_property_frame_size *framesize = value; + if (sizeof(*framesize) == hdr->sz && + DDL_ALLOW_ENC_FRAMESIZE(framesize->width, + framesize->height)) { + enc->frame_size = *framesize; + ddl_calculate_stride(&enc->frame_size, false); + ddl_set_default_encoder_buffer_req(enc); + vcd_status = VCD_S_SUCCESS; } + break; + } case VCD_I_CODEC: - { - struct vcd_property_codec_type *p_codec = - (struct vcd_property_codec_type *) - p_property_value; - if (sizeof(struct vcd_property_codec_type) == - p_property_hdr->n_size) { - u32 b_return; - - vcd_fw_transact(FALSE, FALSE, - p_encoder->codec_type.e_codec); - - b_return = vcd_fw_transact(TRUE, FALSE, - p_codec->e_codec); - if (b_return) { - p_encoder->codec_type = *p_codec; - ddl_set_default_enc_property(p_ddl); - vcd_status = VCD_S_SUCCESS; - } else { - b_return = vcd_fw_transact(TRUE, FALSE, - p_encoder->codec_type.e_codec); - vcd_status = VCD_ERR_NOT_SUPPORTED; - } - } - break; - } - case VCD_I_REQ_IFRAME: - { + { + struct vcd_property_codec *codec = value; + if (sizeof(*codec) == hdr->sz) { + if (!vcd_fw_is_codec_supported(false, codec->codec)) { + vcd_status = VCD_ERR_NOT_SUPPORTED; + break; + } + enc->codec_type = *codec; + ddl_set_default_enc_property(ddl); vcd_status = VCD_S_SUCCESS; - break; } + break; + } + case VCD_I_REQ_IFRAME: + vcd_status = VCD_S_SUCCESS; + break; case VCD_I_INTRA_PERIOD: - { - struct vcd_property_i_period_type *p_iperiod = - (struct vcd_property_i_period_type *) - p_property_value; - if ((sizeof(struct vcd_property_i_period_type) == - p_property_hdr->n_size) && - (!p_iperiod->n_b_frames)) { - p_encoder->i_period = *p_iperiod; - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_property_i_period *iperiod = value; + if (sizeof(*iperiod) == hdr->sz && !iperiod->bframes) { + enc->period = *iperiod; + vcd_status = VCD_S_SUCCESS; } + break; + } case VCD_I_PROFILE: - { - struct vcd_property_profile_type *p_profile = - (struct vcd_property_profile_type *) - p_property_value; - if ( - (sizeof(struct vcd_property_profile_type) == - p_property_hdr->n_size) && - ( - ( - (p_encoder->codec_type. - e_codec == VCD_CODEC_MPEG4) && - ( - p_profile->e_profile == VCD_PROFILE_MPEG4_SP - || p_profile->e_profile == - VCD_PROFILE_MPEG4_ASP - ) - ) || - ( - ( - (p_encoder->codec_type. - e_codec == VCD_CODEC_H264) && - (p_profile->e_profile >= - VCD_PROFILE_H264_BASELINE) - && (p_profile->e_profile <= - VCD_PROFILE_H264_HIGH) - ) - ) || - ( - (p_encoder->codec_type. - e_codec == VCD_CODEC_H263) && - (p_profile->e_profile == - VCD_PROFILE_H263_BASELINE) - ) - ) + { + struct vcd_property_profile *profile = value; + if (sizeof(*profile) == hdr->sz && + ((enc->codec_type.codec == VCD_CODEC_MPEG4 && + (profile->profile == VCD_PROFILE_MPEG4_SP || + profile->profile == VCD_PROFILE_MPEG4_ASP)) || + ((enc->codec_type.codec == VCD_CODEC_H264 && + profile->profile >= VCD_PROFILE_H264_BASELINE && + profile->profile <= VCD_PROFILE_H264_HIGH)) || + (enc->codec_type.codec == VCD_CODEC_H263 && + profile->profile == VCD_PROFILE_H263_BASELINE)) ) { - p_encoder->profile = *p_profile; + enc->profile = *profile; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_LEVEL: + { + struct vcd_property_level *level = value; + if (sizeof(*level) == hdr->sz && + ((enc->codec_type.codec == VCD_CODEC_MPEG4 && + level->level >= VCD_LEVEL_MPEG4_0 && + level->level <= VCD_LEVEL_MPEG4_6) || + (enc->codec_type.codec == VCD_CODEC_H264 && + level->level >= VCD_LEVEL_H264_1 && + level->level <= VCD_LEVEL_H264_3p1) || + (enc->codec_type.codec == VCD_CODEC_H263 && + level->level >= VCD_LEVEL_H263_10 && + level->level <= VCD_LEVEL_H263_70))) { + enc->level = *level; + vcd_status = VCD_S_SUCCESS; + } + break; + } + case VCD_I_MULTI_SLICE: + { + struct vcd_property_multi_slice *multislice = value; + switch (multislice->m_slice_sel) { + case VCD_MSLICE_OFF: + vcd_status = VCD_S_SUCCESS; + break; + case VCD_MSLICE_BY_GOB: + if (enc->codec_type.codec == VCD_CODEC_H263) + vcd_status = VCD_S_SUCCESS; + break; + case VCD_MSLICE_BY_MB_COUNT: + if (multislice->m_slice_size >= 1 && + (multislice->m_slice_size <= + (enc->frame_size.height + * enc->frame_size.width / 16 / 16))) { vcd_status = VCD_S_SUCCESS; } break; - } - case VCD_I_LEVEL: - { - struct vcd_property_level_type *p_level = - (struct vcd_property_level_type *) - p_property_value; - if ( - (sizeof(struct vcd_property_level_type) == - p_property_hdr->n_size - ) && - ( - ( - (p_encoder->codec_type. - e_codec == VCD_CODEC_MPEG4) && - (p_level->e_level >= VCD_LEVEL_MPEG4_0) && - (p_level->e_level <= VCD_LEVEL_MPEG4_6) - ) || - ( - (p_encoder->codec_type. - e_codec == VCD_CODEC_H264) && - (p_level->e_level >= VCD_LEVEL_H264_1) && - (p_level->e_level <= VCD_LEVEL_H264_3p1) - ) || - ( - (p_encoder->codec_type. - e_codec == VCD_CODEC_H263) && - (p_level->e_level >= VCD_LEVEL_H263_10) && - (p_level->e_level <= VCD_LEVEL_H263_70) - ) - ) - ) { - p_encoder->level = *p_level; + case VCD_MSLICE_BY_BYTE_COUNT: + if (multislice->m_slice_size < + DDL_MINIMUM_BYTE_PER_SLICE) { vcd_status = VCD_S_SUCCESS; + break; } + default: break; } - case VCD_I_MULTI_SLICE: - { - struct vcd_property_multi_slice_type *p_multislice = - (struct vcd_property_multi_slice_type *) - p_property_value; - switch (p_multislice->e_m_slice_sel) { - case VCD_MSLICE_OFF: - { - vcd_status = VCD_S_SUCCESS; - break; - } - case VCD_MSLICE_BY_GOB: - { - if (p_encoder->codec_type.e_codec == - VCD_CODEC_H263) - vcd_status = VCD_S_SUCCESS; - break; - } - case VCD_MSLICE_BY_MB_COUNT: - { - if (p_multislice->n_m_slice_size - >= 1 && (p_multislice-> - n_m_slice_size <= - (p_encoder->frame_size.n_height - * p_encoder->frame_size.n_width - / 16 / 16)) - ) { - vcd_status = VCD_S_SUCCESS; - } - break; - } - case VCD_MSLICE_BY_BYTE_COUNT: - { - if (p_multislice->n_m_slice_size < - DDL_MINIMUM_BYTE_PER_SLICE) { - vcd_status = VCD_S_SUCCESS; - break; - } - } - default: - { - break; - } - } - if (sizeof(struct vcd_property_multi_slice_type) == - p_property_hdr->n_size && + if (sizeof(struct vcd_property_multi_slice) == hdr->sz && !vcd_status) { - p_encoder->multi_slice = *p_multislice; - } - break; + enc->multi_slice = *multislice; } + break; + } case VCD_I_RATE_CONTROL: - { - struct vcd_property_rate_control_type - *p_ratecontrol_type = - (struct vcd_property_rate_control_type *) - p_property_value; - if (sizeof(struct vcd_property_rate_control_type) == - p_property_hdr->n_size && - p_ratecontrol_type-> - e_rate_control >= VCD_RATE_CONTROL_OFF && - p_ratecontrol_type-> - e_rate_control <= VCD_RATE_CONTROL_CBR_CFR - ) { - p_encoder->rc_type = *p_ratecontrol_type; - ddl_set_default_enc_rc_params(p_encoder); - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_property_rate_control *ratecontrol_type = value; + if (sizeof(*ratecontrol_type) == hdr->sz && + ratecontrol_type->rate_control >= + VCD_RATE_CONTROL_OFF && + ratecontrol_type->rate_control <= + VCD_RATE_CONTROL_CBR_CFR) { + enc->rc_type = *ratecontrol_type; + ddl_set_default_enc_rc_params(enc); + vcd_status = VCD_S_SUCCESS; } + break; + } case VCD_I_SHORT_HEADER: - { - - if (sizeof(struct vcd_property_short_header_type) == - p_property_hdr->n_size && - p_encoder->codec_type.e_codec == VCD_CODEC_MPEG4) { - p_encoder->short_header = - *(struct vcd_property_short_header_type *) - p_property_value; + if (sizeof(struct vcd_property_short_header) == hdr->sz && + enc->codec_type.codec == VCD_CODEC_MPEG4) { + enc->short_header = + *(struct vcd_property_short_header *)value; vcd_status = VCD_S_SUCCESS; } - - break; - } + break; case VCD_I_VOP_TIMING: - { - struct vcd_property_vop_timing_type *p_voptime = - (struct vcd_property_vop_timing_type *) - p_property_value; - if ( - (sizeof(struct vcd_property_vop_timing_type) == - p_property_hdr->n_size - ) && - (p_encoder->frame_rate.n_fps_numerator <= - p_voptime->n_vop_time_resolution) - ) { - p_encoder->vop_timing = *p_voptime; - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_property_vop_timing *voptime = value; + if (sizeof(*voptime) == hdr->sz && enc->frame_rate.fps_numerator + <= voptime->vop_time_resolution) { + enc->vop_timing = *voptime; + vcd_status = VCD_S_SUCCESS; } + break; + } case VCD_I_HEADER_EXTENSION: - { - if (sizeof(u32) == p_property_hdr->n_size && - p_encoder->codec_type.e_codec == VCD_CODEC_MPEG4 - ) { - p_encoder->n_hdr_ext_control = *(u32 *) - p_property_value; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(u32) == hdr->sz && enc->codec_type.codec == + VCD_CODEC_MPEG4) { + enc->hdr_ext_control = *(u32 *)value; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_ENTROPY_CTRL: - { - struct vcd_property_entropy_control_type - *p_entropy_control = - (struct vcd_property_entropy_control_type *) - p_property_value; - if (sizeof(struct vcd_property_entropy_control_type) == - p_property_hdr->n_size && - p_encoder->codec_type.e_codec == VCD_CODEC_H264 - && p_entropy_control-> - e_entropy_sel >= VCD_ENTROPY_SEL_CAVLC && - p_entropy_control->e_entropy_sel <= - VCD_ENTROPY_SEL_CABAC) { - p_encoder->entropy_control = *p_entropy_control; - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_property_entropy_control *entropy = value; + if (sizeof(*entropy) == hdr->sz && + enc->codec_type.codec == VCD_CODEC_H264 && + entropy->entropy_sel >= VCD_ENTROPY_SEL_CAVLC && + entropy->entropy_sel <= VCD_ENTROPY_SEL_CABAC) { + enc->entropy_control = *entropy; + vcd_status = VCD_S_SUCCESS; } + break; + } case VCD_I_DEBLOCKING: - { - struct vcd_property_db_config_type *p_dbconfig = - (struct vcd_property_db_config_type *) - p_property_value; - if (sizeof(struct vcd_property_db_config_type) == - p_property_hdr->n_size && - p_encoder->codec_type.e_codec == VCD_CODEC_H264 - && p_dbconfig->e_db_config >= - VCD_DB_ALL_BLOCKING_BOUNDARY - && p_dbconfig->e_db_config <= - VCD_DB_SKIP_SLICE_BOUNDARY - ) { - p_encoder->db_control = *p_dbconfig; - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_property_db_config *db = value; + if (sizeof(*db) == hdr->sz && + enc->codec_type.codec == VCD_CODEC_H264 && + db->db_config >= VCD_DB_ALL_BLOCKING_BOUNDARY && + db->db_config <= VCD_DB_SKIP_SLICE_BOUNDARY) { + enc->db_control = *db; + vcd_status = VCD_S_SUCCESS; } + break; + } case VCD_I_QP_RANGE: - { - struct vcd_property_qp_range_type *p_qp = - (struct vcd_property_qp_range_type *) - p_property_value; - if ((sizeof(struct vcd_property_qp_range_type) == - p_property_hdr->n_size) && - (p_qp->n_min_qp <= p_qp->n_max_qp) && - ( - (p_encoder->codec_type.e_codec == VCD_CODEC_H264 - && p_qp->n_max_qp <= DDL_MAX_H264_QP) || - (p_qp->n_max_qp <= DDL_MAX_MPEG4_QP) - ) - ) { - p_encoder->qp_range = *p_qp; - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_property_qp_range *qp = value; + if (sizeof(*qp) == hdr->sz && qp->min_qp <= qp->max_qp && + ((enc->codec_type.codec == VCD_CODEC_H264 && + qp->max_qp <= DDL_MAX_H264_QP) || + qp->max_qp <= DDL_MAX_MPEG4_QP)) { + enc->qp_range = *qp; + vcd_status = VCD_S_SUCCESS; } + break; + } case VCD_I_SESSION_QP: - { - struct vcd_property_session_qp_type *p_qp = - (struct vcd_property_session_qp_type *) - p_property_value; - - if ((sizeof(struct vcd_property_session_qp_type) == - p_property_hdr->n_size) && - (p_qp->n_i_frame_qp >= p_encoder->qp_range.n_min_qp) && - (p_qp->n_i_frame_qp <= p_encoder->qp_range.n_max_qp) && - (p_qp->n_p_frame_qp >= p_encoder->qp_range.n_min_qp) && - (p_qp->n_p_frame_qp <= p_encoder->qp_range.n_max_qp) - ) { - p_encoder->session_qp = *p_qp; + { + struct vcd_property_session_qp *qp = value; + if ((sizeof(*qp) == hdr->sz) && + qp->iframe_qp >= enc->qp_range.min_qp && + qp->iframe_qp <= enc->qp_range.max_qp && + qp->frame_qp >= enc->qp_range.min_qp && + qp->frame_qp <= enc->qp_range.max_qp) { + enc->session_qp = *qp; vcd_status = VCD_S_SUCCESS; } - - break; - } + break; + } case VCD_I_RC_LEVEL_CONFIG: - { - struct vcd_property_rc_level_type *p_rc_level = - (struct vcd_property_rc_level_type *) - p_property_value; - if (sizeof(struct vcd_property_rc_level_type) == - p_property_hdr->n_size && - ( - p_encoder->rc_type. - e_rate_control >= VCD_RATE_CONTROL_VBR_VFR || - p_encoder->rc_type. - e_rate_control <= VCD_RATE_CONTROL_CBR_VFR - ) && - (!p_rc_level->b_mb_level_rc || - p_encoder->codec_type.e_codec == VCD_CODEC_H264 - ) - ) { - p_encoder->rc_level = *p_rc_level; - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_property_rc_level *rc_level = value; + if (sizeof(*rc_level) == hdr->sz && + (enc->rc_type.rate_control >= + VCD_RATE_CONTROL_VBR_VFR || + enc->rc_type.rate_control <= + VCD_RATE_CONTROL_CBR_VFR) && + (!rc_level->mb_level_rc || + enc->codec_type.codec == VCD_CODEC_H264)) { + enc->rc_level = *rc_level; + vcd_status = VCD_S_SUCCESS; } + break; + } case VCD_I_FRAME_LEVEL_RC: - { - - struct vcd_property_frame_level_rc_params_type - *p_frame_levelrc = - (struct vcd_property_frame_level_rc_params_type *) - p_property_value; - - if ((sizeof(struct - vcd_property_frame_level_rc_params_type) - == p_property_hdr->n_size) && - (p_frame_levelrc->n_reaction_coeff) && - (p_encoder->rc_level.b_frame_level_rc) - ) { - p_encoder->frame_level_rc = *p_frame_levelrc; - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_property_frame_level_rc_params *rc = value; + if (sizeof(*rc) == hdr->sz && rc->reaction_coeff && + enc->rc_level.frame_level_rc) { + enc->frame_level_rc = *rc; + vcd_status = VCD_S_SUCCESS; } + break; + } case VCD_I_ADAPTIVE_RC: - { - - if ((sizeof(struct - vcd_property_adaptive_rc_params_type) - == p_property_hdr->n_size) && - (p_encoder->codec_type. - e_codec == VCD_CODEC_H264) && - (p_encoder->rc_level.b_mb_level_rc)) { - - p_encoder->adaptive_rc = - *(struct vcd_property_adaptive_rc_params_type *) - p_property_value; - + { + struct vcd_property_adaptive_rc_params *rc = value; + if (sizeof(*rc) == hdr->sz && enc->codec_type.codec == + VCD_CODEC_H264 && enc->rc_level.mb_level_rc) { + enc->adaptive_rc = *rc; vcd_status = VCD_S_SUCCESS; } - - break; - } + break; + } case VCD_I_INTRA_REFRESH: - { - - struct vcd_property_intra_refresh_mb_number_type - *p_intra_refresh_mbnum = - (struct vcd_property_intra_refresh_mb_number_type *) - p_property_value; - - u32 n_frame_mbnum = - (p_encoder->frame_size.n_width / 16) * - (p_encoder->frame_size.n_height / 16); - if (sizeof(struct - vcd_property_intra_refresh_mb_number_type) - == p_property_hdr->n_size && - p_intra_refresh_mbnum->n_cir_mb_number <= - n_frame_mbnum) { - p_encoder->intra_refresh = - *p_intra_refresh_mbnum; - vcd_status = VCD_S_SUCCESS; - } - - break; + { + struct vcd_property_intra_refresh_mb_number *mbnum = value; + u32 frame_mbnum = (enc->frame_size.width / 16) * + (enc->frame_size.height / 16); + if (sizeof(*mbnum) == hdr->sz && mbnum->cir_mb_number <= + frame_mbnum) { + enc->intra_refresh = *mbnum; + vcd_status = VCD_S_SUCCESS; } + break; + } case VCD_I_BUFFER_FORMAT: - { - struct vcd_property_buffer_format_type *p_tile = - (struct vcd_property_buffer_format_type *) - p_property_value; - if (sizeof(struct vcd_property_buffer_format_type) == - p_property_hdr->n_size && - p_tile->e_buffer_format == + { + struct vcd_property_buffer_format *tile = value; + if (sizeof(*tile) == hdr->sz && tile->buffer_format == VCD_BUFFER_FORMAT_NV12) { - p_encoder->buf_format = *p_tile; - vcd_status = VCD_S_SUCCESS; - } - break; + enc->buf_format = *tile; + vcd_status = VCD_S_SUCCESS; } + break; + } case DDL_I_INPUT_BUF_REQ: - { - struct vcd_buffer_requirement_type *p_buffer_req = - (struct vcd_buffer_requirement_type *) - p_property_value; - if (sizeof(struct vcd_buffer_requirement_type) == - p_property_hdr->n_size && - (ddl_valid_buffer_requirement( - &p_encoder->input_buf_req, p_buffer_req)) - ) { - p_encoder->client_input_buf_req = *p_buffer_req; - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_buffer_requirement *buf_req = value; + if (sizeof(*buf_req) == hdr->sz && ddl_valid_buffer_requirement( + &enc->input_buf_req, buf_req)) { + enc->client_input_buf_req = *buf_req; + vcd_status = VCD_S_SUCCESS; } + break; + } case DDL_I_OUTPUT_BUF_REQ: - { - struct vcd_buffer_requirement_type *p_buffer_req = - (struct vcd_buffer_requirement_type *) - p_property_value; - if (sizeof(struct vcd_buffer_requirement_type) == - p_property_hdr->n_size && - (ddl_valid_buffer_requirement( - &p_encoder->output_buf_req, p_buffer_req)) - ) { - p_encoder->client_output_buf_req = - *p_buffer_req; - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_buffer_requirement *buf_req = value; + if (sizeof(*buf_req) == hdr->sz && ddl_valid_buffer_requirement( + &enc->output_buf_req, buf_req)) { + enc->client_output_buf_req = *buf_req; + vcd_status = VCD_S_SUCCESS; } + break; + } case VCD_I_METADATA_ENABLE: case VCD_I_METADATA_HEADER: - { - vcd_status = ddl_set_metadata_params( - p_ddl, p_property_hdr, p_property_value); - break; - } + vcd_status = ddl_set_metadata_params(ddl, hdr, value); + break; default: - { - vcd_status = VCD_ERR_ILLEGAL_OP; - break; - } + vcd_status = VCD_ERR_ILLEGAL_OP; + break; } return vcd_status; } -static u32 ddl_get_dec_property - (struct ddl_client_context_type *p_ddl, - struct vcd_property_hdr_type *p_property_hdr, void *p_property_value) { +static u32 ddl_get_dec_property(struct ddl_client_context *ddl, + struct vcd_property_hdr *hdr, void *value) +{ u32 vcd_status = VCD_ERR_ILLEGAL_PARM; - struct ddl_decoder_data_type *p_decoder = &p_ddl->codec_data.decoder; + struct ddl_decoder_data *dec = &ddl->codec_data.decoder; - switch (p_property_hdr->prop_id) { + switch (hdr->id) { case VCD_I_FRAME_SIZE: - { - if (sizeof(struct vcd_property_frame_size_type) == - p_property_hdr->n_size) { - if (p_decoder->client_frame_size.n_width) { - *(struct vcd_property_frame_size_type *) - p_property_value = - p_decoder->client_frame_size; - vcd_status = VCD_S_SUCCESS; - } else { - vcd_status = VCD_ERR_ILLEGAL_OP; - } + if (sizeof(struct vcd_property_frame_size) == hdr->sz) { + if (dec->client_frame_size.width) { + struct vcd_property_frame_size *size = value; + *size = dec->client_frame_size; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; } - break; } + break; case VCD_I_PROFILE: - { - if (sizeof(struct vcd_property_profile_type) == - p_property_hdr->n_size) { - *(struct vcd_property_profile_type *) - p_property_value = p_decoder->profile; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_profile) == hdr->sz) { + *(struct vcd_property_profile *)value = dec->profile; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_LEVEL: - { - if (sizeof(struct vcd_property_level_type) == - p_property_hdr->n_size) { - *(struct vcd_property_level_type *) - p_property_value = p_decoder->level; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_level) == hdr->sz) { + *(struct vcd_property_level *)value = dec->level; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_PROGRESSIVE_ONLY: - { - if (sizeof(u32) == p_property_hdr->n_size) { - *(u32 *) p_property_value = - p_decoder->n_progressive_only; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(u32) == hdr->sz) { + *(u32 *)value = dec->progressive_only; + vcd_status = VCD_S_SUCCESS; } + break; case DDL_I_INPUT_BUF_REQ: - { - if (sizeof(struct vcd_buffer_requirement_type) == - p_property_hdr->n_size) { - if (p_decoder-> - client_input_buf_req.n_size) { - *(struct vcd_buffer_requirement_type *) - p_property_value = - p_decoder->client_input_buf_req; - vcd_status = VCD_S_SUCCESS; - } else { - vcd_status = VCD_ERR_ILLEGAL_OP; - } + if (sizeof(struct vcd_buffer_requirement) == hdr->sz) { + if (dec->client_input_buf_req.size) { + *(struct vcd_buffer_requirement *)value = + dec->client_input_buf_req; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; } - break; } + break; case DDL_I_OUTPUT_BUF_REQ: - { - if (sizeof(struct vcd_buffer_requirement_type) == - p_property_hdr->n_size) { - if (p_decoder->client_output_buf_req.n_size) { - *(struct vcd_buffer_requirement_type *) - p_property_value = - p_decoder->client_output_buf_req; - vcd_status = VCD_S_SUCCESS; - } else { - vcd_status = VCD_ERR_ILLEGAL_OP; - } + if (sizeof(struct vcd_buffer_requirement) == hdr->sz) { + if (dec->client_output_buf_req.size) { + *(struct vcd_buffer_requirement *)value = + dec->client_output_buf_req; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; } - break; } + break; case VCD_I_CODEC: - { - if (sizeof(struct vcd_property_codec_type) == - p_property_hdr->n_size) { - if (p_decoder->codec_type.e_codec) { - *(struct vcd_property_codec_type *) - p_property_value = - p_decoder->codec_type; - vcd_status = VCD_S_SUCCESS; - } else { - vcd_status = VCD_ERR_ILLEGAL_OP; - } + if (sizeof(struct vcd_property_codec) == hdr->sz) { + if (dec->codec_type.codec) { + *(struct vcd_property_codec *)value = + dec->codec_type; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; } - break; } + break; case VCD_I_BUFFER_FORMAT: - { - if (sizeof(struct vcd_property_buffer_format_type) == - p_property_hdr->n_size) { - *(struct vcd_property_buffer_format_type *) - p_property_value = p_decoder->buf_format; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_buffer_format) == hdr->sz) { + *(struct vcd_property_buffer_format *)value = + dec->buf_format; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_POST_FILTER: - { - if (sizeof(struct vcd_property_post_filter_type) == - p_property_hdr->n_size) { - *(struct vcd_property_post_filter_type *) - p_property_value = p_decoder->post_filter; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_post_filter) == hdr->sz) { + *(struct vcd_property_post_filter *)value = + dec->post_filter; + vcd_status = VCD_S_SUCCESS; } + break; case DDL_I_SEQHDR_ALIGN_BYTES: - { - if (sizeof(u32) == p_property_hdr->n_size) { - *(u32 *) p_property_value = - DDL_LINEAR_BUFFER_ALIGN_BYTES; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(u32) == hdr->sz) { + *(u32 *)value = DDL_LINEAR_BUFFER_ALIGN_BYTES; + vcd_status = VCD_S_SUCCESS; } + break; case DDL_I_FRAME_PROC_UNITS: - { - if (sizeof(u32) == p_property_hdr->n_size && - p_decoder->client_frame_size.n_width && - p_decoder->client_frame_size.n_height) { - *(u32 *) p_property_value = - ((p_decoder->client_frame_size. - n_width >> 4) * - (p_decoder->client_frame_size. - n_height >> 4) - ); - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(u32) == hdr->sz && dec->client_frame_size.width && + dec->client_frame_size.height) { + *(u32 *)value = ((dec->client_frame_size.width >> 4) * + (dec->client_frame_size.height >> 4)); + vcd_status = VCD_S_SUCCESS; } + break; case DDL_I_DPB_RETRIEVE: - { - if (sizeof(struct ddl_frame_data_type_tag) == - p_property_hdr->n_size) { - vcd_status = - ddl_decoder_dpb_transact(p_decoder, - (struct ddl_frame_data_type_tag *) - p_property_value, - DDL_DPB_OP_RETRIEVE); - } - break; + if (sizeof(struct ddl_frame_data_tag) == hdr->sz) { + vcd_status = ddl_decoder_dpb_transact(dec, + (struct ddl_frame_data_tag *)value, + DDL_DPB_OP_RETRIEVE); } + break; case VCD_I_METADATA_ENABLE: case VCD_I_METADATA_HEADER: - { - vcd_status = ddl_get_metadata_params( - p_ddl, - p_property_hdr, - p_property_value); - break; - } + vcd_status = ddl_get_metadata_params(ddl, hdr, value); + break; default: - { - vcd_status = VCD_ERR_ILLEGAL_OP; - break; - } + vcd_status = VCD_ERR_ILLEGAL_OP; + break; } return vcd_status; } -static u32 ddl_get_enc_property - (struct ddl_client_context_type *p_ddl, - struct vcd_property_hdr_type *p_property_hdr, void *p_property_value) { +static u32 ddl_get_enc_property(struct ddl_client_context *ddl, + struct vcd_property_hdr *hdr, void *value) +{ u32 vcd_status = VCD_ERR_ILLEGAL_PARM; - struct ddl_encoder_data_type *p_encoder = &p_ddl->codec_data.encoder; + struct ddl_encoder_data *enc = &ddl->codec_data.encoder; - struct vcd_property_entropy_control_type *entropy_control; - struct vcd_property_intra_refresh_mb_number_type *intra_refresh; + struct vcd_property_entropy_control *entropy_control; + struct vcd_property_intra_refresh_mb_number *intra_refresh; - switch (p_property_hdr->prop_id) { + switch (hdr->id) { case VCD_I_CODEC: - { - if (sizeof(struct vcd_property_codec_type) == - p_property_hdr->n_size) { - *(struct vcd_property_codec_type *) - p_property_value = - p_encoder->codec_type; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_codec) == hdr->sz) { + *(struct vcd_property_codec *)value = enc->codec_type; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_FRAME_SIZE: - { - if (sizeof(struct vcd_property_frame_size_type) == - p_property_hdr->n_size) { - *(struct vcd_property_frame_size_type *) - p_property_value = - p_encoder->frame_size; - - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_frame_size) == hdr->sz) { + *(struct vcd_property_frame_size *)value = + enc->frame_size; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_FRAME_RATE: - { - if (sizeof(struct vcd_property_frame_rate_type) == - p_property_hdr->n_size) { - - *(struct vcd_property_frame_rate_type *) - p_property_value = - p_encoder->frame_rate; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_frame_rate) == hdr->sz) { + *(struct vcd_property_frame_rate *)value = + enc->frame_rate; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_TARGET_BITRATE: - { - - if (sizeof(struct vcd_property_target_bitrate_type) == - p_property_hdr->n_size) { - *(struct vcd_property_target_bitrate_type *) - p_property_value = - p_encoder->target_bit_rate; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_target_bitrate) == hdr->sz) { + *(struct vcd_property_target_bitrate *)value = + enc->target_bit_rate; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_RATE_CONTROL: - { - if (sizeof(struct vcd_property_rate_control_type) == - p_property_hdr->n_size) { - *(struct vcd_property_rate_control_type *) - p_property_value = p_encoder->rc_type; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_rate_control) == hdr->sz) { + *(struct vcd_property_rate_control *)value = + enc->rc_type; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_PROFILE: - { - if (sizeof(struct vcd_property_profile_type) == - p_property_hdr->n_size) { - *(struct vcd_property_profile_type *) - p_property_value = p_encoder->profile; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_profile) == hdr->sz) { + *(struct vcd_property_profile *)value = enc->profile; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_LEVEL: - { - if (sizeof(struct vcd_property_level_type) == - p_property_hdr->n_size) { - *(struct vcd_property_level_type *) - p_property_value = p_encoder->level; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_level) == hdr->sz) { + *(struct vcd_property_level *)value = enc->level; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_MULTI_SLICE: - { - if (sizeof(struct vcd_property_multi_slice_type) == - p_property_hdr->n_size) { - *(struct vcd_property_multi_slice_type *) - p_property_value = p_encoder->multi_slice; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_multi_slice) == hdr->sz) { + *(struct vcd_property_multi_slice *)value = + enc->multi_slice; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_SEQ_HEADER: - { - struct vcd_sequence_hdr_type *p_seq_hdr = - (struct vcd_sequence_hdr_type *)p_property_value; - if (p_encoder->seq_header.n_buffer_size && - sizeof(struct vcd_sequence_hdr_type) == - p_property_hdr->n_size - && p_encoder->seq_header.n_buffer_size <= - p_seq_hdr->n_sequence_header_len) { - DDL_MEMCPY(p_seq_hdr->p_sequence_header, - p_encoder->seq_header. - p_align_virtual_addr, - p_encoder->seq_header.n_buffer_size); - p_seq_hdr->n_sequence_header_len = - p_encoder->seq_header.n_buffer_size; - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_sequence_hdr *seq_hdr = value; + if (enc->seq_header.size && sizeof(struct vcd_sequence_hdr) == + hdr->sz && enc->seq_header.size <= + seq_hdr->sz) { + memcpy(seq_hdr->addr, enc->seq_header.virt_addr, + enc->seq_header.size); + seq_hdr->sz = enc->seq_header.size; + vcd_status = VCD_S_SUCCESS; } + break; + } case DDL_I_SEQHDR_PRESENT: - { - if (sizeof(u32) == p_property_hdr->n_size) { - if ((p_encoder->codec_type. - e_codec == VCD_CODEC_MPEG4 && - !p_encoder->short_header.b_short_header) - || p_encoder->codec_type.e_codec == - VCD_CODEC_H264) { - *(u32 *)p_property_value = 0x1; - } else { - *(u32 *)p_property_value = 0x0; - } - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(u32) == hdr->sz) { + if ((enc->codec_type.codec == VCD_CODEC_MPEG4 && + !enc->short_header.short_header) || + enc->codec_type.codec == + VCD_CODEC_H264) + *(u32 *)value = 0x1; + else + *(u32 *)value = 0x0; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_VOP_TIMING: - { - if (sizeof(struct vcd_property_vop_timing_type) == - p_property_hdr->n_size) { - *(struct vcd_property_vop_timing_type *) - p_property_value = p_encoder->vop_timing; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_vop_timing) == hdr->sz) { + *(struct vcd_property_vop_timing *)value = + enc->vop_timing; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_SHORT_HEADER: - { - if (sizeof(struct vcd_property_short_header_type) == - p_property_hdr->n_size) { - if (p_encoder->codec_type.e_codec == - VCD_CODEC_MPEG4) { - *(struct vcd_property_short_header_type - *)p_property_value = - p_encoder->short_header; - vcd_status = VCD_S_SUCCESS; - } else { - vcd_status = VCD_ERR_ILLEGAL_OP; - } + if (sizeof(struct vcd_property_short_header) == hdr->sz) { + if (enc->codec_type.codec == VCD_CODEC_MPEG4) { + *(struct vcd_property_short_header *)value = + enc->short_header; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; } - break; } + break; case VCD_I_ENTROPY_CTRL: - { - entropy_control = p_property_value; - if (sizeof(struct vcd_property_entropy_control_type) == - p_property_hdr->n_size) { - if (p_encoder->codec_type.e_codec == - VCD_CODEC_H264) { - *entropy_control = - p_encoder->entropy_control; - vcd_status = VCD_S_SUCCESS; - } else { - vcd_status = VCD_ERR_ILLEGAL_OP; - } + entropy_control = value; + if (sizeof(struct vcd_property_entropy_control) == hdr->sz) { + if (enc->codec_type.codec == VCD_CODEC_H264) { + *entropy_control = enc->entropy_control; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; } - break; } + break; case VCD_I_DEBLOCKING: - { - if (sizeof(struct vcd_property_db_config_type) == - p_property_hdr->n_size) { - if (p_encoder->codec_type.e_codec == - VCD_CODEC_H264) { - *(struct vcd_property_db_config_type *) - p_property_value = - p_encoder->db_control; - vcd_status = VCD_S_SUCCESS; - } else { - vcd_status = VCD_ERR_ILLEGAL_OP; - } + if (sizeof(struct vcd_property_db_config) == hdr->sz) { + if (enc->codec_type.codec == VCD_CODEC_H264) { + *(struct vcd_property_db_config *)value = + enc->db_control; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; } - break; } + break; case VCD_I_INTRA_PERIOD: - { - if (sizeof(struct vcd_property_i_period_type) == - p_property_hdr->n_size) { - *(struct vcd_property_i_period_type *) - p_property_value = p_encoder->i_period; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_i_period) == hdr->sz) { + *(struct vcd_property_i_period *)value = enc->period; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_QP_RANGE: - { - if (sizeof(struct vcd_property_qp_range_type) == - p_property_hdr->n_size) { - *(struct vcd_property_qp_range_type *) - p_property_value = p_encoder->qp_range; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_qp_range) == hdr->sz) { + *(struct vcd_property_qp_range *)value = enc->qp_range; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_SESSION_QP: - { - if (sizeof(struct vcd_property_session_qp_type) == - p_property_hdr->n_size) { - *(struct vcd_property_session_qp_type *) - p_property_value = p_encoder->session_qp; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_session_qp) == hdr->sz) { + *(struct vcd_property_session_qp *)value = + enc->session_qp; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_RC_LEVEL_CONFIG: - { - if (sizeof(struct vcd_property_rc_level_type) == - p_property_hdr->n_size) { - *(struct vcd_property_rc_level_type *) - p_property_value = p_encoder->rc_level; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_rc_level) == hdr->sz) { + *(struct vcd_property_rc_level *)value = enc->rc_level; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_FRAME_LEVEL_RC: - { - if (sizeof - (struct vcd_property_frame_level_rc_params_type) == - p_property_hdr->n_size) { - *(struct vcd_property_frame_level_rc_params_type - *)p_property_value = - p_encoder->frame_level_rc; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_frame_level_rc_params) == + hdr->sz) { + *(struct vcd_property_frame_level_rc_params *)value = + enc->frame_level_rc; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_ADAPTIVE_RC: - { - if (sizeof(struct vcd_property_adaptive_rc_params_type) - == p_property_hdr->n_size) { - *(struct vcd_property_adaptive_rc_params_type *) - p_property_value = p_encoder->adaptive_rc; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_adaptive_rc_params) == + hdr->sz) { + *(struct vcd_property_adaptive_rc_params *)value = + enc->adaptive_rc; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_INTRA_REFRESH: - { - intra_refresh = p_property_value; - if (sizeof - (struct vcd_property_intra_refresh_mb_number_type) - == p_property_hdr->n_size) { - *intra_refresh = p_encoder->intra_refresh; - vcd_status = VCD_S_SUCCESS; - } - break; + intra_refresh = value; + if (sizeof(struct vcd_property_intra_refresh_mb_number) == + hdr->sz) { + *intra_refresh = enc->intra_refresh; + vcd_status = VCD_S_SUCCESS; } + break; case DDL_I_INPUT_BUF_REQ: - { - if (sizeof(struct vcd_buffer_requirement_type) == - p_property_hdr->n_size) { - if (p_encoder->output_buf_req.n_size) { - *(struct vcd_buffer_requirement_type *) - p_property_value = - p_encoder->client_input_buf_req; - vcd_status = VCD_S_SUCCESS; - } else { - vcd_status = VCD_ERR_ILLEGAL_OP; - } + if (sizeof(struct vcd_buffer_requirement) == hdr->sz) { + if (enc->output_buf_req.size) { + *(struct vcd_buffer_requirement *)value = + enc->client_input_buf_req; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; } - break; } + break; case DDL_I_OUTPUT_BUF_REQ: - { - if (sizeof(struct vcd_buffer_requirement_type) == - p_property_hdr->n_size) { - if (p_encoder->output_buf_req.n_size) { - *(struct vcd_buffer_requirement_type *) - p_property_value = - p_encoder->client_output_buf_req; - vcd_status = VCD_S_SUCCESS; - } else { - vcd_status = VCD_ERR_ILLEGAL_OP; - } + if (sizeof(struct vcd_buffer_requirement) == hdr->sz) { + if (enc->output_buf_req.size) { + *(struct vcd_buffer_requirement *)value = + enc->client_output_buf_req; + vcd_status = VCD_S_SUCCESS; + } else { + vcd_status = VCD_ERR_ILLEGAL_OP; } - break; } + break; case VCD_I_BUFFER_FORMAT: - { - if (sizeof(struct vcd_property_buffer_format_type) == - p_property_hdr->n_size) { - *(struct vcd_property_buffer_format_type *) - p_property_value = p_encoder->buf_format; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_buffer_format) == hdr->sz) { + *(struct vcd_property_buffer_format *)value = + enc->buf_format; + vcd_status = VCD_S_SUCCESS; } + break; case DDL_I_FRAME_PROC_UNITS: - { - if (sizeof(u32) == p_property_hdr->n_size && - p_encoder->frame_size.n_width && - p_encoder->frame_size.n_height) { - *(u32 *) p_property_value = - ((p_encoder->frame_size.n_width >> 4) * - (p_encoder->frame_size.n_height >> 4) - ); - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(u32) == hdr->sz && enc->frame_size.width && + enc->frame_size.height) { + *(u32 *)value = ((enc->frame_size.width >> 4) * + (enc->frame_size.height >> 4)); + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_HEADER_EXTENSION: - { - if (sizeof(u32) == p_property_hdr->n_size && - p_encoder->codec_type.e_codec == VCD_CODEC_MPEG4) { - *(u32 *) p_property_value = - p_encoder->n_hdr_ext_control; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(u32) == hdr->sz && enc->codec_type.codec == + VCD_CODEC_MPEG4) { + *(u32 *)value = enc->hdr_ext_control; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_METADATA_ENABLE: case VCD_I_METADATA_HEADER: - { - vcd_status = ddl_get_metadata_params( - p_ddl, - p_property_hdr, - p_property_value); - break; - } + vcd_status = ddl_get_metadata_params(ddl, hdr, value); + break; default: - { - vcd_status = VCD_ERR_ILLEGAL_OP; - break; - } + vcd_status = VCD_ERR_ILLEGAL_OP; + break; } return vcd_status; } -static u32 ddl_set_enc_dynamic_property - (struct ddl_encoder_data_type *p_encoder, - struct vcd_property_hdr_type *p_property_hdr, void *p_property_value) { +static u32 ddl_set_enc_dynamic_property(struct ddl_encoder_data *enc, + struct vcd_property_hdr *hdr, void *value) +{ u32 vcd_status = VCD_ERR_ILLEGAL_PARM; - switch (p_property_hdr->prop_id) { + switch (hdr->id) { case VCD_I_REQ_IFRAME: - { - if (sizeof(struct vcd_property_req_i_frame_type) == - p_property_hdr->n_size) { - p_encoder->n_dynamic_prop_change |= - DDL_ENC_REQ_IFRAME; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_req_i_frame) == hdr->sz) { + enc->dynamic_prop_change |= DDL_ENC_REQ_IFRAME; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_TARGET_BITRATE: - { - if (sizeof(struct vcd_property_target_bitrate_type) == - p_property_hdr->n_size) { - p_encoder->target_bit_rate = - *(struct vcd_property_target_bitrate_type *) - p_property_value; - p_encoder->n_dynamic_prop_change |= - DDL_ENC_CHANGE_BITRATE; - vcd_status = VCD_S_SUCCESS; - } - break; + if (sizeof(struct vcd_property_target_bitrate) == hdr->sz) { + enc->target_bit_rate = + *(struct vcd_property_target_bitrate *)value; + enc->dynamic_prop_change |= DDL_ENC_CHANGE_BITRATE; + vcd_status = VCD_S_SUCCESS; } + break; case VCD_I_INTRA_PERIOD: - { - struct vcd_property_i_period_type *p_iperiod = - (struct vcd_property_i_period_type *) - p_property_value; - if (sizeof(struct vcd_property_i_period_type) == - p_property_hdr->n_size && - !p_iperiod->n_b_frames) { - p_encoder->i_period = *p_iperiod; - p_encoder->n_dynamic_prop_change |= - DDL_ENC_CHANGE_IPERIOD; - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_property_i_period *iperiod = value; + if (sizeof(struct vcd_property_i_period) == hdr->sz && + !iperiod->bframes) { + enc->period = *iperiod; + enc->dynamic_prop_change |= DDL_ENC_CHANGE_IPERIOD; + vcd_status = VCD_S_SUCCESS; } + break; + } case VCD_I_FRAME_RATE: - { - struct vcd_property_frame_rate_type *p_frame_rate = - (struct vcd_property_frame_rate_type *) - p_property_value; - if (sizeof(struct vcd_property_frame_rate_type) - == p_property_hdr->n_size && - p_frame_rate->n_fps_denominator && - p_frame_rate->n_fps_numerator && - p_frame_rate->n_fps_denominator <= - p_frame_rate->n_fps_numerator) { - p_encoder->frame_rate = *p_frame_rate; - p_encoder->n_dynamic_prop_change |= - DDL_ENC_CHANGE_FRAMERATE; - vcd_status = VCD_S_SUCCESS; - } - break; + { + struct vcd_property_frame_rate *frame_rate = value; + if (sizeof(struct vcd_property_frame_rate) == hdr->sz && + frame_rate->fps_denominator && + frame_rate->fps_numerator && + frame_rate->fps_denominator <= + frame_rate->fps_numerator) { + enc->frame_rate = *frame_rate; + enc->dynamic_prop_change |= DDL_ENC_CHANGE_FRAMERATE; + vcd_status = VCD_S_SUCCESS; } + break; + } default: - { - vcd_status = VCD_ERR_ILLEGAL_OP; - break; - } + vcd_status = VCD_ERR_ILLEGAL_OP; + break; } return vcd_status; } -void ddl_set_default_dec_property(struct ddl_client_context_type *p_ddl) +void ddl_set_default_dec_property(struct ddl_client_context *ddl) { - struct ddl_decoder_data_type *p_decoder = &(p_ddl->codec_data.decoder); + struct ddl_decoder_data *dec = &(ddl->codec_data.decoder); - if (p_decoder->codec_type.e_codec == VCD_CODEC_MPEG4 || - p_decoder->codec_type.e_codec == VCD_CODEC_MPEG2) { - p_decoder->post_filter.b_post_filter = TRUE; + if (dec->codec_type.codec == VCD_CODEC_MPEG4 || + dec->codec_type.codec == VCD_CODEC_MPEG2) { + dec->post_filter.post_filter = true; } else { - p_decoder->post_filter.b_post_filter = FALSE; + dec->post_filter.post_filter = false; } - p_decoder->buf_format.e_buffer_format = VCD_BUFFER_FORMAT_NV12; - p_decoder->client_frame_size.n_height = 144; - p_decoder->client_frame_size.n_width = 176; - p_decoder->client_frame_size.n_stride = 176; - p_decoder->client_frame_size.n_scan_lines = 144; - p_decoder->n_progressive_only = 1; - ddl_set_default_metadata_flag(p_ddl); - - ddl_set_default_decoder_buffer_req(p_decoder, TRUE); - + dec->buf_format.buffer_format = VCD_BUFFER_FORMAT_NV12; + dec->client_frame_size.height = 144; + dec->client_frame_size.width = 176; + dec->client_frame_size.stride = 176; + dec->client_frame_size.scan_lines = 144; + dec->progressive_only = 1; + ddl_set_default_metadata_flag(ddl); + + ddl_set_default_decoder_buffer_req(dec, true); } -static void ddl_set_default_enc_property(struct ddl_client_context_type *p_ddl) +static void ddl_set_default_enc_property(struct ddl_client_context *ddl) { - struct ddl_encoder_data_type *p_encoder = &(p_ddl->codec_data.encoder); + struct ddl_encoder_data *enc = &(ddl->codec_data.encoder); - ddl_set_default_enc_profile(p_encoder); - ddl_set_default_enc_level(p_encoder); + ddl_set_default_enc_profile(enc); + ddl_set_default_enc_level(enc); - p_encoder->rc_type.e_rate_control = VCD_RATE_CONTROL_VBR_VFR; - ddl_set_default_enc_rc_params(p_encoder); + enc->rc_type.rate_control = VCD_RATE_CONTROL_VBR_VFR; + ddl_set_default_enc_rc_params(enc); - ddl_set_default_enc_intra_period(p_encoder); + ddl_set_default_enc_intra_period(enc); - p_encoder->intra_refresh.n_cir_mb_number = 0; - ddl_set_default_enc_vop_timing(p_encoder); + enc->intra_refresh.cir_mb_number = 0; + ddl_set_default_enc_vop_timing(enc); - p_encoder->multi_slice.n_m_slice_size = VCD_MSLICE_OFF; - p_encoder->short_header.b_short_header = FALSE; + enc->multi_slice.m_slice_size = VCD_MSLICE_OFF; + enc->short_header.short_header = false; - p_encoder->entropy_control.e_entropy_sel = VCD_ENTROPY_SEL_CAVLC; - p_encoder->entropy_control.e_cabac_model = VCD_CABAC_MODEL_NUMBER_0; - p_encoder->db_control.e_db_config = VCD_DB_ALL_BLOCKING_BOUNDARY; - p_encoder->db_control.n_slice_alpha_offset = 0; - p_encoder->db_control.n_slice_beta_offset = 0; + enc->entropy_control.entropy_sel = VCD_ENTROPY_SEL_CAVLC; + enc->entropy_control.cabac_model = VCD_CABAC_MODEL_NUMBER_0; + enc->db_control.db_config = VCD_DB_ALL_BLOCKING_BOUNDARY; + enc->db_control.slice_alpha_offset = 0; + enc->db_control.slice_beta_offset = 0; - p_encoder->re_con_buf_format.e_buffer_format = - VCD_BUFFER_FORMAT_TILE_4x2; + enc->re_con_buf_format.buffer_format = VCD_BUFFER_FORMAT_TILE_4x2; - p_encoder->buf_format.e_buffer_format = VCD_BUFFER_FORMAT_NV12; + enc->buf_format.buffer_format = VCD_BUFFER_FORMAT_NV12; - p_encoder->n_hdr_ext_control = 0; + enc->hdr_ext_control = 0; - ddl_set_default_metadata_flag(p_ddl); + ddl_set_default_metadata_flag(ddl); - ddl_set_default_encoder_buffer_req(p_encoder); + ddl_set_default_encoder_buffer_req(enc); } -static void ddl_set_default_enc_profile(struct ddl_encoder_data_type *p_encoder) +static void ddl_set_default_enc_profile(struct ddl_encoder_data *enc) { - enum vcd_codec_type e_codec = p_encoder->codec_type.e_codec; - if (e_codec == VCD_CODEC_MPEG4) - p_encoder->profile.e_profile = VCD_PROFILE_MPEG4_SP; - else if (e_codec == VCD_CODEC_H264) - p_encoder->profile.e_profile = VCD_PROFILE_H264_BASELINE; + enum vcd_codec codec = enc->codec_type.codec; + if (codec == VCD_CODEC_MPEG4) + enc->profile.profile = VCD_PROFILE_MPEG4_SP; + else if (codec == VCD_CODEC_H264) + enc->profile.profile = VCD_PROFILE_H264_BASELINE; else - p_encoder->profile.e_profile = VCD_PROFILE_H263_BASELINE; + enc->profile.profile = VCD_PROFILE_H263_BASELINE; } -static void ddl_set_default_enc_level(struct ddl_encoder_data_type *p_encoder) +static void ddl_set_default_enc_level(struct ddl_encoder_data *enc) { - enum vcd_codec_type e_codec = p_encoder->codec_type.e_codec; - if (e_codec == VCD_CODEC_MPEG4) - p_encoder->level.e_level = VCD_LEVEL_MPEG4_1; - else if (e_codec == VCD_CODEC_H264) - p_encoder->level.e_level = VCD_LEVEL_H264_1; + enum vcd_codec codec = enc->codec_type.codec; + if (codec == VCD_CODEC_MPEG4) + enc->level.level = VCD_LEVEL_MPEG4_1; + else if (codec == VCD_CODEC_H264) + enc->level.level = VCD_LEVEL_H264_1; else - p_encoder->level.e_level = VCD_LEVEL_H263_10; + enc->level.level = VCD_LEVEL_H263_10; } -static void ddl_set_default_enc_vop_timing - (struct ddl_encoder_data_type *p_encoder) +static void ddl_set_default_enc_vop_timing(struct ddl_encoder_data *enc) { - p_encoder->vop_timing.n_vop_time_resolution = - (2 * p_encoder->frame_rate.n_fps_numerator) / - p_encoder->frame_rate.n_fps_denominator; + enc->vop_timing.vop_time_resolution = (2 * + enc->frame_rate.fps_numerator) / + enc->frame_rate.fps_denominator; } -static void ddl_set_default_enc_intra_period( - struct ddl_encoder_data_type *p_encoder) +static void ddl_set_default_enc_intra_period(struct ddl_encoder_data *enc) { - switch (p_encoder->rc_type.e_rate_control) { + switch (enc->rc_type.rate_control) { default: case VCD_RATE_CONTROL_VBR_VFR: case VCD_RATE_CONTROL_VBR_CFR: case VCD_RATE_CONTROL_CBR_VFR: case VCD_RATE_CONTROL_OFF: - { - p_encoder->i_period.n_p_frames = - ((p_encoder->frame_rate.n_fps_numerator << 1) / - p_encoder->frame_rate.n_fps_denominator) - 1; - break; - } + enc->period.frames = ((enc->frame_rate.fps_numerator << 1) / + enc->frame_rate.fps_denominator) - 1; + break; case VCD_RATE_CONTROL_CBR_CFR: - { - p_encoder->i_period.n_p_frames = - ((p_encoder->frame_rate.n_fps_numerator >> 1) / - p_encoder->frame_rate.n_fps_denominator) - 1; - break; - } + enc->period.frames = ((enc->frame_rate.fps_numerator >> 1) / + enc->frame_rate.fps_denominator) - 1; + break; } - p_encoder->i_period.n_b_frames = 0; + enc->period.bframes = 0; } -static void ddl_set_default_enc_rc_params( - struct ddl_encoder_data_type *p_encoder) +static void ddl_set_default_enc_rc_params(struct ddl_encoder_data *enc) { - enum vcd_codec_type e_codec = p_encoder->codec_type.e_codec; + enum vcd_codec codec = enc->codec_type.codec; - p_encoder->rc_level.b_frame_level_rc = TRUE; - p_encoder->qp_range.n_min_qp = 0x1; + enc->rc_level.frame_level_rc = true; + enc->qp_range.min_qp = 0x1; - if (e_codec == VCD_CODEC_H264) { - p_encoder->qp_range.n_max_qp = 0x33; - p_encoder->session_qp.n_i_frame_qp = 0x19; - p_encoder->session_qp.n_p_frame_qp = 0x19; + if (codec == VCD_CODEC_H264) { + enc->qp_range.max_qp = 0x33; + enc->session_qp.iframe_qp = 0x19; + enc->session_qp.frame_qp = 0x19; - p_encoder->rc_level.b_mb_level_rc = TRUE; - p_encoder->adaptive_rc.b_activity_region_flag = TRUE; - p_encoder->adaptive_rc.b_dark_region_as_flag = TRUE; - p_encoder->adaptive_rc.b_smooth_region_as_flag = TRUE; - p_encoder->adaptive_rc.b_static_region_as_flag = TRUE; + enc->rc_level.mb_level_rc = true; + enc->adaptive_rc.activity_region_flag = true; + enc->adaptive_rc.dark_region_as_flag = true; + enc->adaptive_rc.smooth_region_as_flag = true; + enc->adaptive_rc.static_region_as_flag = true; } else { - p_encoder->qp_range.n_max_qp = 0x1f; - p_encoder->session_qp.n_i_frame_qp = 0x14; - p_encoder->session_qp.n_p_frame_qp = 0x14; - p_encoder->rc_level.b_mb_level_rc = FALSE; + enc->qp_range.max_qp = 0x1f; + enc->session_qp.iframe_qp = 0x14; + enc->session_qp.frame_qp = 0x14; + enc->rc_level.mb_level_rc = false; } - switch (p_encoder->rc_type.e_rate_control) { + switch (enc->rc_type.rate_control) { default: case VCD_RATE_CONTROL_VBR_VFR: - { - p_encoder->n_r_cframe_skip = 1; - p_encoder->frame_level_rc.n_reaction_coeff = 0x1f4; - break; - } + enc->r_cframe_skip = 1; + enc->frame_level_rc.reaction_coeff = 0x1f4; + break; case VCD_RATE_CONTROL_VBR_CFR: - { - p_encoder->n_r_cframe_skip = 0; - p_encoder->frame_level_rc.n_reaction_coeff = 0x1f4; - break; - } + enc->r_cframe_skip = 0; + enc->frame_level_rc.reaction_coeff = 0x1f4; + break; case VCD_RATE_CONTROL_CBR_VFR: - { - p_encoder->n_r_cframe_skip = 1; - if (e_codec != VCD_CODEC_H264) { - p_encoder->session_qp.n_i_frame_qp = 0xf; - p_encoder->session_qp.n_p_frame_qp = 0xf; - } - - p_encoder->frame_level_rc.n_reaction_coeff = 0x6; - break; + enc->r_cframe_skip = 1; + if (codec != VCD_CODEC_H264) { + enc->session_qp.iframe_qp = 0xf; + enc->session_qp.frame_qp = 0xf; } + + enc->frame_level_rc.reaction_coeff = 0x6; + break; case VCD_RATE_CONTROL_CBR_CFR: - { - p_encoder->n_r_cframe_skip = 0; - p_encoder->frame_level_rc.n_reaction_coeff = 0x6; - break; - } + enc->r_cframe_skip = 0; + enc->frame_level_rc.reaction_coeff = 0x6; + break; case VCD_RATE_CONTROL_OFF: - { - p_encoder->n_r_cframe_skip = 0; - p_encoder->rc_level.b_frame_level_rc = FALSE; - p_encoder->rc_level.b_mb_level_rc = FALSE; - break; - } + enc->r_cframe_skip = 0; + enc->rc_level.frame_level_rc = false; + enc->rc_level.mb_level_rc = false; + break; } } -void ddl_set_default_encoder_buffer_req(struct ddl_encoder_data_type *p_encoder) +void ddl_set_default_encoder_buffer_req(struct ddl_encoder_data *enc) { - u32 n_y_cb_cr_size; - - n_y_cb_cr_size = ddl_get_yuv_buffer_size(&p_encoder->frame_size, - &p_encoder->buf_format, FALSE); - - memset(&p_encoder->input_buf_req, 0, - sizeof(struct vcd_buffer_requirement_type)); - - p_encoder->input_buf_req.n_min_count = 1; - p_encoder->input_buf_req.n_actual_count = - p_encoder->input_buf_req.n_min_count; - p_encoder->input_buf_req.n_max_count = DDL_MAX_BUFFER_COUNT; - p_encoder->input_buf_req.n_size = n_y_cb_cr_size; - p_encoder->input_buf_req.n_align = DDL_LINEAR_BUFFER_ALIGN_BYTES; - - p_encoder->client_input_buf_req = p_encoder->input_buf_req; - - memset(&p_encoder->output_buf_req, 0, - sizeof(struct vcd_buffer_requirement_type)); - - p_encoder->output_buf_req.n_min_count = 2; - p_encoder->output_buf_req.n_actual_count = - p_encoder->output_buf_req.n_min_count; - p_encoder->output_buf_req.n_max_count = DDL_MAX_BUFFER_COUNT; - p_encoder->output_buf_req.n_align = DDL_LINEAR_BUFFER_ALIGN_BYTES; - p_encoder->output_buf_req.n_size = n_y_cb_cr_size; - ddl_set_default_encoder_metadata_buffer_size(p_encoder); - p_encoder->client_output_buf_req = p_encoder->output_buf_req; + u32 y_cb_cr_size; + + y_cb_cr_size = ddl_get_yuv_buffer_size(&enc->frame_size, + &enc->buf_format, false); + + memset(&enc->input_buf_req, 0, sizeof(struct vcd_buffer_requirement)); + + enc->input_buf_req.min_count = 1; + enc->input_buf_req.actual_count = enc->input_buf_req.min_count; + enc->input_buf_req.max_count = DDL_MAX_BUFFER_COUNT; + enc->input_buf_req.size = y_cb_cr_size; + enc->input_buf_req.align = DDL_LINEAR_BUFFER_ALIGN_BYTES; + + enc->client_input_buf_req = enc->input_buf_req; + + memset(&enc->output_buf_req, 0, sizeof(struct vcd_buffer_requirement)); + + enc->output_buf_req.min_count = 2; + enc->output_buf_req.actual_count = enc->output_buf_req.min_count; + enc->output_buf_req.max_count = DDL_MAX_BUFFER_COUNT; + enc->output_buf_req.align = DDL_LINEAR_BUFFER_ALIGN_BYTES; + enc->output_buf_req.size = y_cb_cr_size; + ddl_set_default_encoder_metadata_buffer_size(enc); + enc->client_output_buf_req = enc->output_buf_req; } -void ddl_set_default_decoder_buffer_req(struct ddl_decoder_data_type *p_decoder, - u32 b_estimate) +void ddl_set_default_decoder_buffer_req(struct ddl_decoder_data *dec, + u32 estimate) { - u32 n_y_cb_cr_size, n_min_dpb; - struct vcd_property_frame_size_type *p_frame_size; - struct vcd_buffer_requirement_type *p_output_buf_req, *p_input_buf_req; + size_t y_cb_cr_size; + u32 min_dpb; + struct vcd_property_frame_size *frame_size; + struct vcd_buffer_requirement *output_buf_req, *input_buf_req; - if (!p_decoder->codec_type.e_codec) + if (!dec->codec_type.codec) return; - if (b_estimate) { - p_frame_size = &p_decoder->client_frame_size; - p_output_buf_req = &p_decoder->client_output_buf_req; - p_input_buf_req = &p_decoder->client_input_buf_req; - n_min_dpb = ddl_decoder_min_num_dpb(p_decoder); - n_y_cb_cr_size = ddl_get_yuv_buffer_size(p_frame_size, - &p_decoder->buf_format, - (!p_decoder->n_progressive_only)); + if (estimate) { + frame_size = &dec->client_frame_size; + output_buf_req = &dec->client_output_buf_req; + input_buf_req = &dec->client_input_buf_req; + min_dpb = ddl_decoder_min_num_dpb(dec); + y_cb_cr_size = ddl_get_yuv_buffer_size(frame_size, + &dec->buf_format, !dec->progressive_only); } else { - p_frame_size = &p_decoder->frame_size; - p_output_buf_req = &p_decoder->actual_output_buf_req; - p_input_buf_req = &p_decoder->actual_input_buf_req; - n_y_cb_cr_size = p_decoder->n_y_cb_cr_size; - n_min_dpb = p_decoder->n_min_dpb_num; + frame_size = &dec->frame_size; + output_buf_req = &dec->actual_output_buf_req; + input_buf_req = &dec->actual_input_buf_req; + y_cb_cr_size = dec->y_cb_cr_size; + min_dpb = dec->min_dpb_num; } - memset(p_output_buf_req, 0, sizeof(struct vcd_buffer_requirement_type)); + memset(output_buf_req, 0, sizeof(struct vcd_buffer_requirement)); - p_output_buf_req->n_min_count = n_min_dpb; - p_output_buf_req->n_actual_count = p_output_buf_req->n_min_count; - p_output_buf_req->n_max_count = DDL_MAX_BUFFER_COUNT; - p_output_buf_req->n_size = n_y_cb_cr_size; - if (p_decoder->buf_format.e_buffer_format != VCD_BUFFER_FORMAT_NV12) - p_output_buf_req->n_align = DDL_TILE_BUFFER_ALIGN_BYTES; + output_buf_req->min_count = min_dpb; + output_buf_req->actual_count = output_buf_req->min_count; + output_buf_req->max_count = DDL_MAX_BUFFER_COUNT; + output_buf_req->size = y_cb_cr_size; + if (dec->buf_format.buffer_format != VCD_BUFFER_FORMAT_NV12) + output_buf_req->align = DDL_TILE_BUFFER_ALIGN_BYTES; else - p_output_buf_req->n_align = DDL_LINEAR_BUFFER_ALIGN_BYTES; + output_buf_req->align = DDL_LINEAR_BUFFER_ALIGN_BYTES; - ddl_set_default_decoder_metadata_buffer_size(p_decoder, - p_frame_size, p_output_buf_req); + ddl_set_default_decoder_metadata_buffer_size(dec, frame_size, + output_buf_req); - p_decoder->min_output_buf_req = *p_output_buf_req; + dec->min_output_buf_req = *output_buf_req; - memset(p_input_buf_req, 0, sizeof(struct vcd_buffer_requirement_type)); + memset(input_buf_req, 0, sizeof(struct vcd_buffer_requirement)); - p_input_buf_req->n_min_count = 1; - p_input_buf_req->n_actual_count = p_input_buf_req->n_min_count; - p_input_buf_req->n_max_count = DDL_MAX_BUFFER_COUNT; - p_input_buf_req->n_size = n_y_cb_cr_size; + input_buf_req->min_count = 1; + input_buf_req->actual_count = input_buf_req->min_count; + input_buf_req->max_count = DDL_MAX_BUFFER_COUNT; + input_buf_req->size = y_cb_cr_size; - if (p_input_buf_req->n_size >= ((1280*720*3) >> 1)) - p_input_buf_req->n_size = (p_input_buf_req->n_size >> 1); + if (input_buf_req->size >= (1280 * 720 * 3) >> 1) + input_buf_req->size >>= 1; - p_input_buf_req->n_align = DDL_LINEAR_BUFFER_ALIGN_BYTES; - - p_decoder->min_input_buf_req = *p_input_buf_req; + input_buf_req->align = DDL_LINEAR_BUFFER_ALIGN_BYTES; + dec->min_input_buf_req = *input_buf_req; } -u32 ddl_get_yuv_buffer_size(struct vcd_property_frame_size_type *p_frame_size, - struct vcd_property_buffer_format_type *p_buf_format, u32 inter_lace) +size_t ddl_get_yuv_buffer_size(struct vcd_property_frame_size *frame_size, + struct vcd_property_buffer_format *buf_format, u32 interlace) { - u32 n_width = p_frame_size->n_stride; - u32 n_height = p_frame_size->n_scan_lines; - u32 n_total_memory_size; - - if (p_buf_format->e_buffer_format != VCD_BUFFER_FORMAT_NV12) { - u32 n_component_mem_size; - u32 n_width_round_up; - u32 n_height_round_up; - u32 n_height_chroma = (n_height >> 1); - - n_width_round_up = - DDL_TILE_ALIGN(n_width, DDL_TILE_ALIGN_WIDTH); - n_height_round_up = - DDL_TILE_ALIGN(n_height, DDL_TILE_ALIGN_HEIGHT); - - n_component_mem_size = n_width_round_up * n_height_round_up; - n_component_mem_size = DDL_TILE_ALIGN(n_component_mem_size, - DDL_TILE_MULTIPLY_FACTOR); - - n_total_memory_size = ((n_component_mem_size + - DDL_TILE_BUF_ALIGN_GUARD_BYTES) & - DDL_TILE_BUF_ALIGN_MASK); - - n_height_round_up = - DDL_TILE_ALIGN(n_height_chroma, DDL_TILE_ALIGN_HEIGHT); - n_component_mem_size = n_width_round_up * n_height_round_up; - n_component_mem_size = DDL_TILE_ALIGN(n_component_mem_size, - DDL_TILE_MULTIPLY_FACTOR); - n_total_memory_size += n_component_mem_size; + u32 width = frame_size->stride; + u32 height = frame_size->scan_lines; + size_t sz; + + if (buf_format->buffer_format != VCD_BUFFER_FORMAT_NV12) { + size_t component_sz; + u32 width_round_up; + u32 height_round_up; + u32 height_chroma = (height >> 1); + + width_round_up = DDL_TILE_ALIGN(width, DDL_TILE_ALIGN_WIDTH); + height_round_up = DDL_TILE_ALIGN(height, DDL_TILE_ALIGN_HEIGHT); + + component_sz = width_round_up * height_round_up; + component_sz = DDL_TILE_ALIGN(component_sz, + DDL_TILE_MULTIPLY_FACTOR); + + sz = (component_sz + DDL_TILE_BUF_ALIGN_GUARD_BYTES) & + DDL_TILE_BUF_ALIGN_MASK; + + height_round_up = DDL_TILE_ALIGN(height_chroma, + DDL_TILE_ALIGN_HEIGHT); + component_sz = width_round_up * height_round_up; + component_sz = DDL_TILE_ALIGN(component_sz, + DDL_TILE_MULTIPLY_FACTOR); + sz += component_sz; } else { - n_total_memory_size = n_height * n_width; - n_total_memory_size += (n_total_memory_size >> 1); + sz = height * width; + sz += sz >> 1; } - return n_total_memory_size; + return sz; } -void ddl_calculate_stride(struct vcd_property_frame_size_type *p_frame_size, - u32 b_interlace) +void ddl_calculate_stride(struct vcd_property_frame_size *frame_size, + u32 interlace) { - p_frame_size->n_stride = ((p_frame_size->n_width + 15) >> 4) << 4; - - if (b_interlace) { - p_frame_size->n_scan_lines = - ((p_frame_size->n_height + 31) >> 5) << 5; - } else { - p_frame_size->n_scan_lines = - ((p_frame_size->n_height + 15) >> 4) << 4; - } + frame_size->stride = ((frame_size->width + 15) >> 4) << 4; + if (interlace) + frame_size->scan_lines = ((frame_size->height + 31) >> 5) << 5; + else + frame_size->scan_lines = ((frame_size->height + 15) >> 4) << 4; } -static u32 ddl_valid_buffer_requirement - (struct vcd_buffer_requirement_type *original_buf_req, - struct vcd_buffer_requirement_type *req_buf_req) +static u32 ddl_valid_buffer_requirement(struct vcd_buffer_requirement + *orig, struct vcd_buffer_requirement *req) { - u32 b_status = FALSE; - if ( - original_buf_req->n_max_count >= req_buf_req->n_actual_count - && original_buf_req->n_actual_count <= - req_buf_req->n_actual_count && - original_buf_req->n_align <= req_buf_req->n_align && - original_buf_req->n_size <= req_buf_req->n_size) { - b_status = TRUE; + u32 status = false; + if (orig->max_count >= req->actual_count && + orig->actual_count <= req->actual_count && + orig->align <= req->align && orig->size <= req->size) { + status = true; } else { - VIDC_LOGERR_STRING("ddl_valid_buf_req:Failed"); + pr_err("ddl_valid_buf_req:Failed\n"); } - return b_status; + return status; } -static u32 ddl_decoder_min_num_dpb(struct ddl_decoder_data_type *p_decoder) +static u32 ddl_decoder_min_num_dpb(struct ddl_decoder_data *dec) { - u32 n_min_dpb = 0; - switch (p_decoder->codec_type.e_codec) { + u32 min_dpb = 0; + switch (dec->codec_type.codec) { default: case VCD_CODEC_MPEG4: case VCD_CODEC_MPEG2: @@ -1820,106 +1310,86 @@ static u32 ddl_decoder_min_num_dpb(struct ddl_decoder_data_type *p_decoder) case VCD_CODEC_DIVX_5: case VCD_CODEC_DIVX_6: case VCD_CODEC_XVID: - { - n_min_dpb = 3; - break; - } + min_dpb = 3; + break; case VCD_CODEC_H263: - { - n_min_dpb = 2; - break; - } + min_dpb = 2; + break; case VCD_CODEC_VC1: case VCD_CODEC_VC1_RCV: - { - n_min_dpb = 4; - break; - } + min_dpb = 4; + break; case VCD_CODEC_H264: - { - u32 n_yuv_size = - ((p_decoder->client_frame_size.n_height * - p_decoder->client_frame_size.n_width * 3) >> 1); - n_min_dpb = 6912000 / n_yuv_size; - if (n_min_dpb > 16) - n_min_dpb = 16; - - n_min_dpb += 2; - break; - } + { + u32 yuv_size = (dec->client_frame_size.height * + dec->client_frame_size.width * 3) >> 1; + min_dpb = 6912000 / yuv_size; + if (min_dpb > 16) + min_dpb = 16; + + min_dpb += 2; + break; + } } - return n_min_dpb; + return min_dpb; } -static u32 ddl_set_dec_buffers - (struct ddl_decoder_data_type *p_decoder, - struct ddl_property_dec_pic_buffers_type *p_dpb) { +static u32 ddl_set_dec_buffers(struct ddl_decoder_data *dec, + struct ddl_property_dec_pic_buffers *dpb) +{ u32 vcd_status = VCD_S_SUCCESS; - u32 n_loopc; - for (n_loopc = 0; !vcd_status && - n_loopc < p_dpb->n_no_of_dec_pic_buf; ++n_loopc) { - if ((!DDL_ADDR_IS_ALIGNED - (p_dpb->a_dec_pic_buffers[n_loopc].vcd_frm.p_physical, - p_decoder->client_output_buf_req.n_align) - ) - || (p_dpb->a_dec_pic_buffers[n_loopc].vcd_frm.n_alloc_len < - p_decoder->client_output_buf_req.n_size) - ) { + u32 i; + for (i = 0; !vcd_status && i < dpb->no_of_dec_pic_buf; ++i) { + if (!IS_ALIGNED(dpb->dec_pic_buffers[i].vcd_frm.phys_addr, + dec->client_output_buf_req.align) || + dpb->dec_pic_buffers[i].vcd_frm.alloc_len < + dec->client_output_buf_req.size) { vcd_status = VCD_ERR_ILLEGAL_PARM; + pr_err("ddl_set_prop:" + "Dpb_align_fail_or_alloc_size_small\n"); + return vcd_status; } } - if (vcd_status) { - VIDC_LOGERR_STRING - ("ddl_set_prop:Dpb_align_fail_or_alloc_size_small"); - return vcd_status; - } - if (p_decoder->dp_buf.n_no_of_dec_pic_buf) { - DDL_FREE(p_decoder->dp_buf.a_dec_pic_buffers); - p_decoder->dp_buf.n_no_of_dec_pic_buf = 0; + + if (dec->dp_buf.no_of_dec_pic_buf) { + kfree(dec->dp_buf.dec_pic_buffers); + dec->dp_buf.dec_pic_buffers = NULL; + dec->dp_buf.no_of_dec_pic_buf = 0; } - p_decoder->dp_buf.a_dec_pic_buffers = - DDL_MALLOC(p_dpb->n_no_of_dec_pic_buf * - sizeof(struct ddl_frame_data_type_tag)); + dec->dp_buf.dec_pic_buffers = kmalloc(dpb->no_of_dec_pic_buf * + sizeof(struct ddl_frame_data_tag), GFP_KERNEL); - if (!p_decoder->dp_buf.a_dec_pic_buffers) { - VIDC_LOGERR_STRING - ("ddl_dec_set_prop:Dpb_container_alloc_failed"); + if (!dec->dp_buf.dec_pic_buffers) { + pr_err("ddl_dec_set_prop:" + "Dpb_container_alloc_failed\n"); return VCD_ERR_ALLOC_FAIL; } - p_decoder->dp_buf.n_no_of_dec_pic_buf = p_dpb->n_no_of_dec_pic_buf; - for (n_loopc = 0; n_loopc < p_dpb->n_no_of_dec_pic_buf; ++n_loopc) { - p_decoder->dp_buf.a_dec_pic_buffers[n_loopc] = - p_dpb->a_dec_pic_buffers[n_loopc]; - } - p_decoder->dpb_mask.n_client_mask = 0; - p_decoder->dpb_mask.n_hw_mask = 0; - p_decoder->n_dynamic_prop_change = 0; + dec->dp_buf.no_of_dec_pic_buf = dpb->no_of_dec_pic_buf; + for (i = 0; i < dpb->no_of_dec_pic_buf; ++i) + dec->dp_buf.dec_pic_buffers[i] = dpb->dec_pic_buffers[i]; + + dec->dpb_mask.client_mask = 0; + dec->dpb_mask.hw_mask = 0; + dec->dynamic_prop_change = 0; return VCD_S_SUCCESS; } -void ddl_set_initial_default_values(struct ddl_client_context_type *p_ddl) +void ddl_set_initial_default_values(struct ddl_client_context *ddl) { - if (p_ddl->b_decoding) { - p_ddl->codec_data.decoder.codec_type.e_codec = VCD_CODEC_MPEG4; - vcd_fw_transact(TRUE, TRUE, - p_ddl->codec_data.decoder.codec_type.e_codec); - ddl_set_default_dec_property(p_ddl); + if (ddl->decoding) { + ddl->codec_data.decoder.codec_type.codec = VCD_CODEC_MPEG4; + ddl_set_default_dec_property(ddl); } else { - struct ddl_encoder_data_type *p_encoder = - &(p_ddl->codec_data.encoder); - p_encoder->codec_type.e_codec = VCD_CODEC_MPEG4; - vcd_fw_transact(TRUE, FALSE, - p_encoder->codec_type.e_codec); - - p_encoder->target_bit_rate.n_target_bitrate = 64000; - p_encoder->frame_size.n_width = 176; - p_encoder->frame_size.n_height = 144; - p_encoder->frame_size.n_stride = 176; - p_encoder->frame_size.n_scan_lines = 144; - p_encoder->frame_rate.n_fps_numerator = 30; - p_encoder->frame_rate.n_fps_denominator = 1; - ddl_set_default_enc_property(p_ddl); + struct ddl_encoder_data *enc = &(ddl->codec_data.encoder); + enc->codec_type.codec = VCD_CODEC_MPEG4; + + enc->target_bit_rate.target_bitrate = 64000; + enc->frame_size.width = 176; + enc->frame_size.height = 144; + enc->frame_size.stride = 176; + enc->frame_size.scan_lines = 144; + enc->frame_rate.fps_numerator = 30; + enc->frame_rate.fps_denominator = 1; + ddl_set_default_enc_property(ddl); } - - return; } diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.c b/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.c index ff1de05b93f84..922f071ed75d5 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.c +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.c @@ -18,6 +18,7 @@ #include "video_core_type.h" #include "vcd_ddl_utils.h" +#include "vcd_ddl_metadata.h" #if DEBUG #define DBG(x...) printk(KERN_DEBUG x) @@ -27,174 +28,112 @@ #define ERR(x...) printk(KERN_ERR x) +#ifdef CORE_TIMING_INFO static unsigned int g_ddl_dec_t1, g_ddl_enc_t1; static unsigned int g_ddl_dec_ttotal, g_ddl_enc_ttotal; static unsigned int g_ddl_dec_count, g_ddl_enc_count; +#endif -#ifdef NO_IN_KERNEL_PMEM - -void ddl_pmem_alloc(struct ddl_buf_addr_type *buff_addr, u32 size, u32 align) -{ - u32 n_guard_bytes, n_align_mask; - u32 n_physical_addr, n_align_offset; - dma_addr_t phy_addr; - - if (align == DDL_LINEAR_BUFFER_ALIGN_BYTES) { - - n_guard_bytes = 31; - n_align_mask = 0xFFFFFFE0U; - - } else { - - n_guard_bytes = DDL_TILE_BUF_ALIGN_GUARD_BYTES; - n_align_mask = DDL_TILE_BUF_ALIGN_MASK; - } - - buff_addr->p_virtual_base_addr = - kmalloc((size + n_guard_bytes), GFP_KERNEL); - - if (!buff_addr->p_virtual_base_addr) { - ERR("\n ERROR %s:%u kamlloc fails to allocate" - " size + n_guard_bytes = %u\n", __func__, __LINE__, - (size + n_guard_bytes)); - return; +size_t npelly_size[] = { + 0x100000, + 0x080000, + 0x51c00, + DDL_CONTEXT_MEMORY, + DDL_DB_LINE_BUF_SIZE, + DDL_MPEG4_DATA_PARTITION_BUF_SIZE, + DDL_METADATA_TOTAL_INPUTBUFSIZE, + DDL_DBG_CORE_DUMP_SIZE, + 0x040000, + DDL_ENC_SEQHEADER_SIZE, +}; + +struct ddl_dma_buffer npelly_b[30]; + +u32 npelly_init(void) { + int i; + printk("\nnpelly npelly max_key = %d\n", npelly_max_key); + for (i=0; isize = npelly_size[i]; + b->virt_addr = dma_alloc_coherent(NULL, b->size, + &b->phys_addr, GFP_KERNEL); + if (!b->virt_addr) { + printk("\nnpelly %s: Could not allocate %d for %d\n", + __FUNCTION__, b->size, i); + return -1; + } + printk("\nnpelly ALLOC %d for %d\n", b->size, i); + memset(b->virt_addr, 0, b->size); } - - phy_addr = dma_map_single(NULL, buff_addr->p_virtual_base_addr, - size + n_guard_bytes, DMA_TO_DEVICE); - - buff_addr->n_buffer_size = size; - n_physical_addr = (u32) phy_addr; - buff_addr->p_align_physical_addr = - (u32 *) ((n_physical_addr + n_guard_bytes) & n_align_mask); - n_align_offset = - (u32) (buff_addr->p_align_physical_addr) - n_physical_addr; - buff_addr->p_align_virtual_addr = - (u32 *) ((u32) (buff_addr->p_virtual_base_addr) - + n_align_offset); -} - -void ddl_pmem_free(struct ddl_buf_addr_type buff_addr) -{ - kfree(buff_addr.p_virtual_base_addr); - buff_addr.n_buffer_size = 0; - buff_addr.p_virtual_base_addr = NULL; + return 0; } -#else - -void ddl_pmem_alloc(struct ddl_buf_addr_type *buff_addr, u32 size, u32 align) +void *ddl_dma_alloc(struct ddl_dma_buffer *b, size_t sz, enum npelly_key key) { - u32 n_guard_bytes, n_align_mask; - dma_addr_t n_physical_addr; - void *virtual_addr; - u32 n_align_offset; + printk("\nnpelly RETRIEVE %d for %d\n", sz, key); - if (align == DDL_LINEAR_BUFFER_ALIGN_BYTES) { - - n_guard_bytes = 31; - n_align_mask = 0xFFFFFFE0U; - - } else { - - n_guard_bytes = DDL_TILE_BUF_ALIGN_GUARD_BYTES; - n_align_mask = DDL_TILE_BUF_ALIGN_MASK; - } - - // n_physical_addr = pmem_kalloc((size + n_guard_bytes), - // PMEM_MEMTYPE_EBI1 | PMEM_ALIGNMENT_4K); - virtual_addr = dma_alloc_coherent(NULL, size + n_guard_bytes, - &n_physical_addr, GFP_KERNEL); - - if (IS_ERR(virtual_addr)) { - pr_err("%s(): could not allocte in kernel pmem buffers\n", - __func__); - return; + if (sz > npelly_b[key].size) { + printk("\nnpelly OH SHIT, %d > %d for %d\n", sz, npelly_b[key].size, key); + BUG_ON(true); } + *b = npelly_b[key]; + b->size = sz; + memset(b->virt_addr, 0, sz); - buff_addr->p_physical_base_addr = (u32 *) n_physical_addr; - buff_addr->p_virtual_base_addr = virtual_addr; - memset(buff_addr->p_virtual_base_addr, 0 , size + n_guard_bytes); - - buff_addr->n_buffer_size = size; - buff_addr->n_buffer_size_guard = size + n_guard_bytes; - - buff_addr->p_align_physical_addr = - (u32 *) ((n_physical_addr + n_guard_bytes) & n_align_mask); - - n_align_offset = - (u32) (buff_addr->p_align_physical_addr) - n_physical_addr; - - buff_addr->p_align_virtual_addr = - (u32 *) ((u32) (buff_addr->p_virtual_base_addr) - + n_align_offset); - - pr_debug("%s(): phy addr 0x%08x kernel addr 0x%08x\n", __func__, - (u32) buff_addr->p_align_physical_addr, - (u32) buff_addr->p_align_virtual_addr); - - return; + return b->virt_addr; } -void ddl_pmem_free(struct ddl_buf_addr_type buff_addr) +void ddl_dma_free(struct ddl_dma_buffer *b) { - DBG("\n %s(): ddl_pmem_free v_address %p p_address %p", - __func__, buff_addr.p_physical_base_addr, - buff_addr.p_virtual_base_addr); - - if (buff_addr.p_virtual_base_addr) { - dma_free_coherent(NULL, buff_addr.n_buffer_size_guard, - buff_addr.p_virtual_base_addr, - (dma_addr_t) buff_addr.p_physical_base_addr); - } + printk("\nnpelly RELEASE %d\n", b->size); - buff_addr.n_buffer_size = 0; - buff_addr.p_virtual_base_addr = NULL; + b->virt_addr = NULL; + b->size = 0; } -#endif +#ifdef CORE_TIMING_INFO void ddl_get_core_start_time(u8 codec_type) { - u32 *p_ddl_t1 = NULL; + u32 *ddl_t1 = NULL; if (!codec_type) - p_ddl_t1 = &g_ddl_dec_t1; + ddl_t1 = &g_ddl_dec_t1; else if (codec_type == 1) - p_ddl_t1 = &g_ddl_enc_t1; + ddl_t1 = &g_ddl_enc_t1; - if (!*p_ddl_t1) { + if (!*ddl_t1) { struct timeval ddl_tv; do_gettimeofday(&ddl_tv); - *p_ddl_t1 = (ddl_tv.tv_sec * 1000) + (ddl_tv.tv_usec / 1000); + *ddl_t1 = (ddl_tv.tv_sec * 1000) + (ddl_tv.tv_usec / 1000); } } void ddl_calc_core_time(u8 codec_type) { - u32 *p_ddl_t1 = NULL, *p_ddl_ttotal = NULL, - *p_ddl_count = NULL; + u32 *ddl_t1 = NULL, *ddl_ttotal = NULL, + *ddl_count = NULL; if (!codec_type) { DBG("\n720p Core Decode "); - p_ddl_t1 = &g_ddl_dec_t1; - p_ddl_ttotal = &g_ddl_dec_ttotal; - p_ddl_count = &g_ddl_dec_count; + ddl_t1 = &g_ddl_dec_t1; + ddl_ttotal = &g_ddl_dec_ttotal; + ddl_count = &g_ddl_dec_count; } else if (codec_type == 1) { DBG("\n720p Core Encode "); - p_ddl_t1 = &g_ddl_enc_t1; - p_ddl_ttotal = &g_ddl_enc_ttotal; - p_ddl_count = &g_ddl_enc_count; + ddl_t1 = &g_ddl_enc_t1; + ddl_ttotal = &g_ddl_enc_ttotal; + ddl_count = &g_ddl_enc_count; } - if (*p_ddl_t1) { + if (*ddl_t1) { int ddl_t2; struct timeval ddl_tv; do_gettimeofday(&ddl_tv); ddl_t2 = (ddl_tv.tv_sec * 1000) + (ddl_tv.tv_usec / 1000); - *p_ddl_ttotal += (ddl_t2 - *p_ddl_t1); - *p_ddl_count = *p_ddl_count + 1; + *ddl_ttotal += (ddl_t2 - *ddl_t1); + *ddl_count = *ddl_count + 1; DBG("time %u, average time %u, count %u", - ddl_t2 - *p_ddl_t1, (*p_ddl_ttotal)/(*p_ddl_count), - *p_ddl_count); - *p_ddl_t1 = 0; + ddl_t2 - *ddl_t1, (*ddl_ttotal)/(*ddl_count), + *ddl_count); + *ddl_t1 = 0; } } @@ -212,3 +151,4 @@ void ddl_reset_time_variables(u8 codec_type) g_ddl_enc_count = 0; } } +#endif diff --git a/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.h b/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.h index ee0cf9574b4d6..bf83a735a7977 100644 --- a/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.h +++ b/drivers/misc/video_core/720p/ddl/vcd_ddl_utils.h @@ -32,29 +32,30 @@ #include "vcd_ddl_core.h" #include "vcd_ddl.h" -#define DDL_INLINE - -#define DDL_ALIGN_SIZE(n_size, n_guard_bytes, n_align_mask) \ - (((u32)(n_size) + n_guard_bytes) & n_align_mask) - -#define DDL_MALLOC(x) kmalloc(x, GFP_KERNEL) -#define DDL_FREE(x) { if ((x)) kfree((x)); (x) = NULL; } - -void ddl_pmem_alloc(struct ddl_buf_addr_type *, u32, u32); - -void ddl_pmem_free(struct ddl_buf_addr_type); - +//TODO get rid of this hack +enum npelly_key { + npelly_dec_dpb = 0, + npelly_dec_ref, + npelly_dec_h264, + npelly_context, + npelly_dbl, + npelly_mpeg4, + npelly_meta, + npelly_debug, + npelly_enc_dpb, + npelly_enc_seq, + npelly_max_key, +}; + +void *ddl_dma_alloc(struct ddl_dma_buffer *, size_t, enum npelly_key key); +void ddl_dma_free(struct ddl_dma_buffer *); + +#ifdef CORE_TIMING_INFO void ddl_get_core_start_time(u8 codec_type); void ddl_calc_core_time(u8 codec_type); void ddl_reset_time_variables(u8 codec_type); - -#define DDL_ASSERT(x) -#define DDL_MEMSET(src, value, len) memset((src), (value), (len)) -#define DDL_MEMCPY(dest, src, len) memcpy((dest), (src), (len)) - -#define DDL_ADDR_IS_ALIGNED(addr, align_bytes) \ -(!((u32)(addr) & ((align_bytes) - 1))) +#endif #endif diff --git a/drivers/misc/video_core/720p/ddl/video_core_720p.c b/drivers/misc/video_core/720p/ddl/video_core_720p.c index 656651a57dffe..053a3a8d7cade 100644 --- a/drivers/misc/video_core/720p/ddl/video_core_720p.c +++ b/drivers/misc/video_core/720p/ddl/video_core_720p.c @@ -21,780 +21,734 @@ #include "video_core_type.h" #include "video_core_720p.h" -#if DEBUG -#define DBG(x...) printk(KERN_DEBUG x) -#else -#define DBG(x...) -#endif - #define VIDC_720P_VERSION_STRING "VIDC_V1.0" -u8 *vid_c_base_addr; -#ifdef VIDC_REGISTER_LOG_INTO_BUFFER -char vidclog[VIDC_REGLOG_BUFSIZE]; -unsigned int vidclog_index; -#endif +unsigned long vid_c_base_addr; + +void vidc_720p_set_device_virtual_base(void *virt_addr) +{ + vid_c_base_addr = (unsigned long)virt_addr; +} + +static inline void vidc_720p_write(unsigned long offset, unsigned long val) +{ + pr_debug("REG 0x%08lx: write 0x%08lx\n", offset, val); + mb(); + iowrite32(val, vid_c_base_addr + offset); +} -void vidc_720p_set_device_virtual_base(u8 *p_core_virtual_base_addr) +static inline unsigned long vidc_720p_read(unsigned long offset) { - vid_c_base_addr = p_core_virtual_base_addr; + unsigned long val; + mb(); + val = ioread32(vid_c_base_addr + offset); + pr_debug("REG 0x%08lx: read 0x%08lx\n", offset, val); + return val; } -void vidc_720p_init(char **ppsz_version, u32 i_firmware_size, - u32 *pi_firmware_address, - enum vidc_720p_endian_type e_dma_endian, - u32 b_interrupt_off, - enum vidc_720p_interrupt_level_selection_type - e_interrupt_sel, u32 interrupt_mask) +void vidc_720p_init(char **ppsz_version, size_t sz, phys_addr_t phys_addr, + enum vidc_720p_endian_type dma_endian, u32 interrupt_off, + enum vidc_720p_interrupt_level_selection_type interrupt_sel, + u32 interrupt_mask) { if (ppsz_version) *ppsz_version = VIDC_720P_VERSION_STRING; - if (e_interrupt_sel == VIDC_720P_INTERRUPT_LEVEL_SEL) - VIDC_IO_OUT(REG_491082, 0); + if (interrupt_sel == VIDC_720P_INTERRUPT_LEVEL_SEL) + vidc_720p_write(0x0504, 0); else - VIDC_IO_OUT(REG_491082, 1); + vidc_720p_write(0x0504, 1); - if (b_interrupt_off) - VIDC_IO_OUT(REG_609676, 1); + if (interrupt_off) + vidc_720p_write(0x0500, 1); else - VIDC_IO_OUT(REG_609676, 0); + vidc_720p_write(0x0500, 0); - VIDC_IO_OUT(REG_614776, 1); + vidc_720p_write(0x0508, 1); - VIDC_IO_OUT(REG_418173, 0); + vidc_720p_write(0x0518, 0); - VIDC_IO_OUT(REG_418173, interrupt_mask); + vidc_720p_write(0x0518, interrupt_mask); - VIDC_IO_OUT(REG_736316, e_dma_endian); + vidc_720p_write(0x0044, dma_endian); - VIDC_IO_OUT(REG_215724, 0); + vidc_720p_write(0x0138, 0); - VIDC_IO_OUT(REG_361582, 1); + vidc_720p_write(0x0110, 1); - VIDC_IO_OUT(REG_591577, i_firmware_size); + vidc_720p_write(0x000C, sz / 4); /* word size */ - VIDC_IO_OUT(REG_203921, pi_firmware_address); + vidc_720p_write(0x0014, phys_addr); - VIDC_IO_OUT(REG_531515_ADDR, 0); + vidc_720p_write(0x0020, 0); - VIDC_IO_OUT(REG_614413, 1); + vidc_720p_write(0x0000, 1); } u32 vidc_720p_do_sw_reset(void) { - - u32 n_fw_start = 0; - VIDC_BUSY_WAIT(5); - VIDC_IO_OUT(REG_224135, 0); - VIDC_BUSY_WAIT(5); - VIDC_IO_OUT(REG_193553, 0); - VIDC_BUSY_WAIT(5); - VIDC_IO_OUT(REG_141269, 1); - VIDC_BUSY_WAIT(15); - VIDC_IO_OUT(REG_141269, 0); - VIDC_BUSY_WAIT(5); - VIDC_IO_IN(REG_193553, &n_fw_start); - - if (!n_fw_start) { - DBG("\n VIDC-SW-RESET-FAILS!"); - return FALSE; + u32 fw_start; + udelay(5); + vidc_720p_write(0x0108, 0); + udelay(5); + vidc_720p_write(0x0134, 0); + udelay(5); + vidc_720p_write(0x0130, 1); + udelay(15); + vidc_720p_write(0x0130, 0); + udelay(5); + fw_start = vidc_720p_read(0x0134); + + if (!fw_start) { + pr_debug("VIDC-SW-RESET-FAILS!\n"); + return false; } - return TRUE; + return true; } u32 vidc_720p_reset_is_success() { - u32 n_stagecounter = 0; - VIDC_IO_IN(REG_352831, &n_stagecounter); - n_stagecounter &= 0xff; - if (n_stagecounter != 0xe5) { - DBG("\n VIDC-CPU_RESET-FAILS!"); - VIDC_IO_OUT(REG_224135, 0); + u32 stagecounter; + stagecounter = vidc_720p_read(0x0414); + stagecounter &= 0xff; + if (stagecounter != 0xe5) { + pr_debug("VIDC-CPU_RESET-FAILS!\n"); + vidc_720p_write(0x0108, 0); msleep(10); - return FALSE; + return false; } - return TRUE; + return true; } -void vidc_720p_start_cpu(enum vidc_720p_endian_type e_dma_endian, - u32 *p_icontext_bufferstart, - u32 *p_debug_core_dump_addr, - u32 debug_buffer_size) +void vidc_720p_start_cpu(enum vidc_720p_endian_type dma_endian, + phys_addr_t icontext_bufferstart, phys_addr_t debug_core_dump_addr, + size_t debug_buffer_size) { u32 dbg_info_input0_reg = 0x1; - VIDC_IO_OUT(REG_361582, 0); - VIDC_IO_OUT(REG_958768, p_icontext_bufferstart); - VIDC_IO_OUT(REG_736316, e_dma_endian); + + vidc_720p_write(0x0110, 0); + vidc_720p_write(0x0230, icontext_bufferstart); + vidc_720p_write(0x0044, dma_endian); if (debug_buffer_size) { dbg_info_input0_reg = (debug_buffer_size << 0x10) | (0x2 << 1) | 0x1; - VIDC_IO_OUT(REG_166247, p_debug_core_dump_addr); + vidc_720p_write(0x0D10, debug_core_dump_addr); } - VIDC_IO_OUT(REG_699747, dbg_info_input0_reg); - VIDC_IO_OUT(REG_224135, 1); + vidc_720p_write(0x0D0C, dbg_info_input0_reg); + vidc_720p_write(0x0108, 1); } u32 vidc_720p_cpu_start() { - u32 fw_status = 0x0; - VIDC_IO_IN(REG_381535, &fw_status); + u32 fw_status; + + fw_status = vidc_720p_read(0x0C14); if (fw_status != 0x02) - return FALSE; - return TRUE; + return false; + return true; } void vidc_720p_stop_fw(void) { - VIDC_IO_OUT(REG_193553, 0); - VIDC_IO_OUT(REG_224135, 0); + vidc_720p_write(0x0134, 0); + vidc_720p_write(0x0108, 0); } -void vidc_720p_get_interrupt_status(u32 *p_interrupt_status, - u32 *p_cmd_err_status, u32 *p_disp_pic_err_status, u32 *p_op_failed) +void vidc_720p_get_interrupt_status(u32 *interrupt_status, + u32 *cmd_err_status, u32 *disp_pic_err_status, u32 *op_failed) { u32 err_status; - VIDC_IO_IN(REG_512143, p_interrupt_status); - VIDC_IO_IN(REG_300310, &err_status); - *p_cmd_err_status = err_status & 0xffff; - *p_disp_pic_err_status = (err_status & 0xffff0000) >> 16; - VIDC_IO_INF(REG_724381, OPERATION_FAILED, \ - p_op_failed); + + *interrupt_status = vidc_720p_read(0x0514); + err_status = vidc_720p_read(0x0E9C); + *cmd_err_status = err_status & 0xffff; + *disp_pic_err_status = (err_status & 0xffff0000) >> 16; + *op_failed = (vidc_720p_read(0x0EC0) & 0x2) >> 1; } void vidc_720p_interrupt_done_clear(void) { - VIDC_IO_OUT(REG_614776, 1); - VIDC_IO_OUT(REG_97293, 4); + vidc_720p_write(0x0508, 1); + vidc_720p_write(0x0104, 4); } -void vidc_720p_submit_command(u32 ch_id, u32 n_cmd_id) +void vidc_720p_submit_command(u32 ch_id, u32 cmd_id) { u32 fw_status; - VIDC_IO_OUT(REG_97293, ch_id); - VIDC_IO_OUT(REG_62325, n_cmd_id); - VIDC_DEBUG_REGISTER_LOG; - VIDC_IO_IN(REG_381535, &fw_status); - VIDC_IO_OUT(REG_926519, fw_status); + + vidc_720p_write(0x0104, ch_id); + vidc_720p_write(0x0D00, cmd_id); + + fw_status = vidc_720p_read(0x0C14); + vidc_720p_write(0x0D1C, fw_status); } -u32 vidc_720p_engine_reset(u32 n_ch_id, - enum vidc_720p_endian_type e_dma_endian, - enum vidc_720p_interrupt_level_selection_type e_interrupt_sel, - u32 interrupt_mask -) +u32 vidc_720p_engine_reset(u32 ch_id, enum vidc_720p_endian_type dma_endian, + enum vidc_720p_interrupt_level_selection_type interrupt_sel, + u32 interrupt_mask) { - u32 n_op_done = 0; - u32 n_counter = 0; + u32 op_done; + u32 counter = 0; - VIDC_LOGERR_STRING("ENG-RESET!!"); + pr_debug("ENG-RESET!!\n"); /* issue the engine reset command */ - vidc_720p_submit_command(n_ch_id, VIDC_720P_CMD_MFC_ENGINE_RESET); + vidc_720p_submit_command(ch_id, VIDC_720P_CMD_MFC_ENGINE_RESET); do { - VIDC_BUSY_WAIT(20); - VIDC_IO_IN(REG_982553, &n_op_done); - n_counter++; - } while (!n_op_done && n_counter < 10); - - if (!n_op_done) { - /* Reset fails */ - return FALSE ; - } + udelay(20); + op_done = vidc_720p_read(0x050C); + counter++; + } while (!op_done && counter < 10); + + if (!op_done) + return false; /* reset fails */ /* write invalid channel id */ - VIDC_IO_OUT(REG_97293, 4); + vidc_720p_write(0x0104, 4); /* Set INT_PULSE_SEL */ - if (e_interrupt_sel == VIDC_720P_INTERRUPT_LEVEL_SEL) - VIDC_IO_OUT(REG_491082, 0); + if (interrupt_sel == VIDC_720P_INTERRUPT_LEVEL_SEL) + vidc_720p_write(0x0504, 0); else - VIDC_IO_OUT(REG_491082, 1); + vidc_720p_write(0x0504, 1); if (!interrupt_mask) { /* Disable interrupt */ - VIDC_IO_OUT(REG_609676, 1); + vidc_720p_write(0x0500, 1); } else { /* Enable interrupt */ - VIDC_IO_OUT(REG_609676, 0); + vidc_720p_write(0x0500, 0); } /* Clear any pending interrupt */ - VIDC_IO_OUT(REG_614776, 1); + vidc_720p_write(0x0508, 1); /* Set INT_ENABLE_REG */ - VIDC_IO_OUT(REG_418173, interrupt_mask); + vidc_720p_write(0x0518, interrupt_mask); - /*Sets the DMA endianness */ - VIDC_IO_OUT(REG_736316, e_dma_endian); + /* Sets the DMA endianness */ + vidc_720p_write(0x0044, dma_endian); - /* retun engine reset success */ - return TRUE ; + /* return engine reset success */ + return true ; } -void vidc_720p_set_channel(u32 i_ch_id, - enum vidc_720p_enc_dec_selection_type - e_enc_dec_sel, enum vidc_720p_codec_type e_codec, - u32 *pi_fw, u32 i_firmware_size) +void vidc_720p_set_channel(u32 ch_id, enum vidc_720p_enc_dec_selection_type + enc_dec_sel, enum vidc_720p_codec_type codec, phys_addr_t pi_fw, + size_t firmware_size) { - u32 std_sel = 0; - VIDC_IO_OUT(REG_661565, 0); + u32 std_sel = codec; - if (e_enc_dec_sel) - std_sel = VIDC_REG_713080_ENC_ON_BMSK; + vidc_720p_write(0x012C, 0); - std_sel |= (u32) e_codec; + if (enc_dec_sel) + std_sel |= 0x10; - VIDC_IO_OUT(REG_713080, std_sel); + vidc_720p_write(0x0100, std_sel); - switch (e_codec) { + switch (codec) { default: case VIDC_720P_DIVX: case VIDC_720P_XVID: case VIDC_720P_MPEG4: - { - if (e_enc_dec_sel == VIDC_720P_ENCODER) - VIDC_IO_OUT(REG_765787, pi_fw); - else - VIDC_IO_OUT(REG_225040, pi_fw); - break; - } + if (enc_dec_sel == VIDC_720P_ENCODER) + vidc_720p_write(0x0200, pi_fw); + else + vidc_720p_write(0x0204, pi_fw); + break; case VIDC_720P_H264: - { - if (e_enc_dec_sel == VIDC_720P_ENCODER) - VIDC_IO_OUT(REG_942456, pi_fw); - else - VIDC_IO_OUT(REG_942170_ADDR_3, pi_fw); - break; - } + if (enc_dec_sel == VIDC_720P_ENCODER) + vidc_720p_write(0x0208, pi_fw); + else + vidc_720p_write(0x020C, pi_fw); + break; case VIDC_720P_H263: - { - if (e_enc_dec_sel == VIDC_720P_ENCODER) - VIDC_IO_OUT(REG_765787, pi_fw); - else - VIDC_IO_OUT(REG_942170_ADDR_6, pi_fw); - break; - } + if (enc_dec_sel == VIDC_720P_ENCODER) + vidc_720p_write(0x0200, pi_fw); + else + vidc_720p_write(0x0218, pi_fw); + break; case VIDC_720P_VC1: - { - VIDC_IO_OUT(REG_880188, pi_fw); - break; - } + vidc_720p_write(0x0210, pi_fw); + break; case VIDC_720P_MPEG2: - { - VIDC_IO_OUT(REG_40293, pi_fw); - break; - } + vidc_720p_write(0x40293, pi_fw); + break; } - VIDC_IO_OUT(REG_591577, i_firmware_size); + vidc_720p_write(0x000C, firmware_size / 4); /* word size */ - vidc_720p_submit_command(i_ch_id, VIDC_720P_CMD_CHSET); + vidc_720p_submit_command(ch_id, VIDC_720P_CMD_CHSET); } -void vidc_720p_encode_set_profile(u32 i_profile, u32 i_level) +void vidc_720p_encode_set_profile(u32 profile, u32 level) { - u32 profile_level = i_profile|(i_level << 0x8); - VIDC_IO_OUT(REG_839021, profile_level); + u32 profile_level = profile|(level << 0x8); + + vidc_720p_write(0x0300, profile_level); } -void vidc_720p_set_frame_size(u32 i_size_x, u32 i_size_y) +void vidc_720p_set_frame_size(u32 size_x, u32 size_y) { - VIDC_IO_OUT(REG_999267, i_size_x); + vidc_720p_write(0x0118, size_x); - VIDC_IO_OUT(REG_345712, i_size_y); + vidc_720p_write(0x011C, size_y); } -void vidc_720p_encode_set_fps(u32 i_rc_frame_rate) +void vidc_720p_encode_set_fps(u32 rc_frame_rate) { - VIDC_IO_OUT(REG_625444, i_rc_frame_rate); + vidc_720p_write(0x0D14, rc_frame_rate); } -void vidc_720p_encode_set_short_header(u32 i_short_header) +void vidc_720p_encode_set_short_header(u32 short_header) { - VIDC_IO_OUT(REG_314290, i_short_header); + vidc_720p_write(0x0318, short_header); } -void vidc_720p_encode_set_vop_time(u32 n_vop_time_resolution, - u32 n_vop_time_increment) +void vidc_720p_encode_set_vop_time(u32 vop_time_resolution, + u32 vop_time_increment) { u32 enable_vop, vop_timing_reg; - if (!n_vop_time_resolution) - VIDC_IO_OUT(REG_64895, 0x0); + + if (!vop_time_resolution) + vidc_720p_write(0x0E00, 0x0); else { enable_vop = 0x1; vop_timing_reg = (enable_vop << 0x1f) | - (n_vop_time_resolution << 0x10) | n_vop_time_increment; - VIDC_IO_OUT(REG_64895, vop_timing_reg); + (vop_time_resolution << 0x10) | vop_time_increment; + vidc_720p_write(0x0E00, vop_timing_reg); } } -void vidc_720p_encode_set_hec_period(u32 n_hec_period) +void vidc_720p_encode_set_hec_period(u32 hec_period) { - VIDC_IO_OUT(REG_407718, n_hec_period); + vidc_720p_write(0x0EB0, hec_period); } -void vidc_720p_encode_set_qp_params(u32 i_max_qp, u32 i_min_qp) +void vidc_720p_encode_set_qp_params(u32 max_qp, u32 min_qp) { - u32 qp = i_min_qp | (i_max_qp << 0x8); - VIDC_IO_OUT(REG_734318, qp); + u32 qp = min_qp | (max_qp << 0x8); + + vidc_720p_write(0x0A0C, qp); } -void vidc_720p_encode_set_rc_config(u32 b_enable_frame_level_rc, - u32 b_enable_mb_level_rc_flag, - u32 i_frame_qp, u32 n_pframe_qp) +void vidc_720p_encode_set_rc_config(u32 enable_frame_level_rc, + u32 enable_mb_level_rc_flag, u32 iframe_qp, u32 pframe_qp) { - u32 n_rc_config = i_frame_qp; + u32 rc_config = iframe_qp; - if (b_enable_frame_level_rc) - n_rc_config |= (0x1 << 0x9); + if (enable_frame_level_rc) + rc_config |= (0x1 << 0x9); - if (b_enable_mb_level_rc_flag) - n_rc_config |= (0x1 << 0x8); + if (enable_mb_level_rc_flag) + rc_config |= (0x1 << 0x8); - VIDC_IO_OUT(REG_58211, n_rc_config); - VIDC_IO_OUT(REG_548359, n_pframe_qp); + vidc_720p_write(0x0A00, rc_config); + vidc_720p_write(0x0A04, pframe_qp); } -void vidc_720p_encode_set_bit_rate(u32 i_target_bitrate) +void vidc_720p_encode_set_bit_rate(u32 target_bitrate) { - VIDC_IO_OUT(REG_174150, i_target_bitrate); + vidc_720p_write(0x0A08, target_bitrate); } -void vidc_720p_encoder_set_param_change(u32 n_enc_param_change) +void vidc_720p_encoder_set_param_change(u32 enc_param_change) { - VIDC_IO_OUT(REG_804959, n_enc_param_change); + vidc_720p_write(0x0E08, enc_param_change); } -void vidc_720p_encode_set_control_param(u32 n_param_val) +void vidc_720p_encode_set_control_param(u32 param_val) { - VIDC_IO_OUT(REG_128234, n_param_val); + vidc_720p_write(0x0EC8, param_val); } -void vidc_720p_encode_set_frame_level_rc_params(u32 i_reaction_coeff) +void vidc_720p_encode_set_frame_level_rc_params(u32 reaction_coeff) { - VIDC_IO_OUT(REG_677784, i_reaction_coeff); + vidc_720p_write(0x0A10, reaction_coeff); } -void vidc_720p_encode_set_mb_level_rc_params(u32 b_dark_region_as_flag, - u32 b_smooth_region_as_flag, - u32 b_static_region_as_flag, - u32 b_activity_region_flag) +void vidc_720p_encode_set_mb_level_rc_params(u32 dark_region_as_flag, + u32 smooth_region_as_flag, u32 static_region_as_flag, + u32 activity_region_flag) { - u32 n_mb_level_rc = 0x0; - if (b_activity_region_flag) - n_mb_level_rc |= 0x1; - if (b_static_region_as_flag) - n_mb_level_rc |= (0x1 << 0x1); - if (b_smooth_region_as_flag) - n_mb_level_rc |= (0x1 << 0x2); - if (b_dark_region_as_flag) - n_mb_level_rc |= (0x1 << 0x3); + u32 mb_level_rc = 0x0; + + if (activity_region_flag) + mb_level_rc |= 0x1; + if (static_region_as_flag) + mb_level_rc |= (0x1 << 0x1); + if (smooth_region_as_flag) + mb_level_rc |= (0x1 << 0x2); + if (dark_region_as_flag) + mb_level_rc |= (0x1 << 0x3); /* Write MB level rate control */ - VIDC_IO_OUT(REG_995041, n_mb_level_rc); + vidc_720p_write(0x0A14, mb_level_rc); } void vidc_720p_encode_set_entropy_control(enum vidc_720p_entropy_sel_type - e_entropy_sel, - enum vidc_720p_cabac_model_type - e_cabac_model_number) + entropy_sel, enum vidc_720p_cabac_model_type cabac_model_number) { - u32 n_num; - u32 n_entropy_params = (u32)e_entropy_sel; + u32 num; + u32 entropy_params = entropy_sel; + /* Set Model Number */ - if (e_entropy_sel == VIDC_720P_ENTROPY_SEL_CABAC) { - n_num = (u32)e_cabac_model_number; - n_entropy_params |= (n_num << 0x2); + if (entropy_sel == VIDC_720P_ENTROPY_SEL_CABAC) { + num = (u32)cabac_model_number; + entropy_params |= (num << 0x2); } /* Set Entropy parameters */ - VIDC_IO_OUT(REG_504878, n_entropy_params); + vidc_720p_write(0x0310, entropy_params); } void vidc_720p_encode_set_db_filter_control(enum vidc_720p_DBConfig_type - e_db_config, - u32 i_slice_alpha_offset, - u32 i_slice_beta_offset) + db_config, u32 slice_alpha_offset, u32 slice_beta_offset) { - u32 n_deblock_params; - n_deblock_params = (u32)e_db_config; - n_deblock_params |= - ((i_slice_beta_offset << 0x2) | (i_slice_alpha_offset << 0x7)); + u32 deblock_params; + + deblock_params = db_config; + deblock_params |= + (slice_beta_offset << 0x2) | (slice_alpha_offset << 0x7); /* Write deblocking control settings */ - VIDC_IO_OUT(REG_458130, n_deblock_params); + vidc_720p_write(0x0314, deblock_params); } -void vidc_720p_encode_set_intra_refresh_mb_number(u32 i_cir_mb_number) +void vidc_720p_encode_set_intra_refresh_mb_number(u32 cir_mb_number) { - VIDC_IO_OUT(REG_857491, i_cir_mb_number); + vidc_720p_write(0x0810, cir_mb_number); } -void vidc_720p_encode_set_multi_slice_info(enum - vidc_720p_MSlice_selection_type - e_m_slice_sel, - u32 n_multi_slice_size) +void vidc_720p_encode_set_multi_slice_info(enum vidc_720p_MSlice_selection_type + m_slice_sel, u32 multi_slice_size) { - switch (e_m_slice_sel) { + switch (m_slice_sel) { case VIDC_720P_MSLICE_BY_MB_COUNT: - { - VIDC_IO_OUT(REG_588301, 0x1); - VIDC_IO_OUT(REG_1517, e_m_slice_sel); - VIDC_IO_OUT(REG_105335, n_multi_slice_size); - break; - } + vidc_720p_write(0x0EA8, 0x1); + vidc_720p_write(0x1517, m_slice_sel); + vidc_720p_write(0x0324, multi_slice_size); + break; case VIDC_720P_MSLICE_BY_BYTE_COUNT: - { - VIDC_IO_OUT(REG_588301, 0x1); - VIDC_IO_OUT(REG_1517, e_m_slice_sel); - VIDC_IO_OUT(REG_561679, n_multi_slice_size); - break; - } + vidc_720p_write(0x0EA8, 0x1); + vidc_720p_write(0x1517, m_slice_sel); + vidc_720p_write(0x0328, multi_slice_size); + break; case VIDC_720P_MSLICE_BY_GOB: - { - VIDC_IO_OUT(REG_588301, 0x1); - break; - } + vidc_720p_write(0x0EA8, 0x1); + break; default: case VIDC_720P_MSLICE_OFF: - { - VIDC_IO_OUT(REG_588301, 0x0); - break; - } + vidc_720p_write(0x0EA8, 0x0); + break; } } -void vidc_720p_encode_set_dpb_buffer(u32 *pi_enc_dpb_addr, u32 alloc_len) +void vidc_720p_encode_set_dpb_buffer(dma_addr_t pi_enc_dpb_addr, + size_t alloc_len) { - VIDC_IO_OUT(REG_341928_ADDR, pi_enc_dpb_addr); - VIDC_IO_OUT(REG_319934, alloc_len); + vidc_720p_write(0x080C, pi_enc_dpb_addr); + vidc_720p_write(0x0ED4, alloc_len); } -void vidc_720p_encode_set_i_period(u32 i_i_period) +void vidc_720p_encode_set_i_period(u32 period) { - VIDC_IO_OUT(REG_950374, i_i_period); + vidc_720p_write(0x0308, period); } -void vidc_720p_encode_init_codec(u32 i_ch_id, - enum vidc_720p_memory_access_method_type - e_memory_access_model) +void vidc_720p_encode_init_codec(u32 ch_id, + enum vidc_720p_memory_access_method_type memory_access_model) { - - VIDC_IO_OUT(REG_841539, e_memory_access_model); - vidc_720p_submit_command(i_ch_id, VIDC_720P_CMD_INITCODEC); + vidc_720p_write(0x0600, memory_access_model); + vidc_720p_submit_command(ch_id, VIDC_720P_CMD_INITCODEC); } -void vidc_720p_encode_unalign_bitstream(u32 n_upper_unalign_word, - u32 n_lower_unalign_word) +void vidc_720p_encode_unalign_bitstream(u32 upper_unalign_word, + u32 lower_unalign_word) { - VIDC_IO_OUT(REG_792026, n_upper_unalign_word); - VIDC_IO_OUT(REG_844152, n_lower_unalign_word); + vidc_720p_write(0x0EA0, upper_unalign_word); + vidc_720p_write(0x0EA4, lower_unalign_word); } -void vidc_720p_encode_set_seq_header_buffer(u32 n_ext_buffer_start, - u32 n_ext_buffer_end, - u32 n_start_byte_num) +void vidc_720p_encode_set_seq_header_buffer(phys_addr_t ext_buffer_start, + phys_addr_t ext_buffer_end, u32 start_byte_num) { - VIDC_IO_OUT(REG_275113_ADDR, n_ext_buffer_start); + vidc_720p_write(0x0018, ext_buffer_start); - VIDC_IO_OUT(REG_87912, n_ext_buffer_start); + vidc_720p_write(0x0024, ext_buffer_start); - VIDC_IO_OUT(REG_988007_ADDR, n_ext_buffer_end); + vidc_720p_write(0x001C, ext_buffer_end); - VIDC_IO_OUT(REG_66693, n_start_byte_num); + vidc_720p_write(0x005C, start_byte_num); } -void vidc_720p_encode_frame(u32 n_ch_id, - u32 n_ext_buffer_start, - u32 n_ext_buffer_end, - u32 n_start_byte_number, u32 n_y_addr, - u32 n_c_addr) +void vidc_720p_encode_frame(u32 ch_id, phys_addr_t ext_buffer_start, + phys_addr_t ext_buffer_end, u32 start_byte_number, phys_addr_t y_addr, + phys_addr_t c_addr) { - VIDC_IO_OUT(REG_275113_ADDR, n_ext_buffer_start); + vidc_720p_write(0x0018, ext_buffer_start); - VIDC_IO_OUT(REG_988007_ADDR, n_ext_buffer_end); + vidc_720p_write(0x001C, ext_buffer_end); - VIDC_IO_OUT(REG_87912, n_ext_buffer_start); + vidc_720p_write(0x0024, ext_buffer_start); - VIDC_IO_OUT(REG_66693, n_start_byte_number); + vidc_720p_write(0x005C, start_byte_number); - VIDC_IO_OUT(REG_99105, n_y_addr); + vidc_720p_write(0x99105, y_addr); - VIDC_IO_OUT(REG_777113_ADDR, n_c_addr); + vidc_720p_write(0x0804, c_addr); - vidc_720p_submit_command(n_ch_id, VIDC_720P_CMD_FRAMERUN); + vidc_720p_submit_command(ch_id, VIDC_720P_CMD_FRAMERUN); } void vidc_720p_encode_get_header(u32 *pi_enc_header_size) { - VIDC_IO_IN(REG_114286, pi_enc_header_size); + *pi_enc_header_size = vidc_720p_read(0x0060); } -void vidc_720p_enc_frame_info(struct vidc_720p_enc_frame_info_type - *p_enc_frame_info) +void vidc_720p_enc_frame_info(struct vidc_720p_enc_frame_info *enc_frame_info) { - VIDC_IO_IN(REG_782249, &p_enc_frame_info->n_enc_size); + enc_frame_info->enc_size = vidc_720p_read(0x0058); - VIDC_IO_IN(REG_441270, &p_enc_frame_info->n_frame_type); + enc_frame_info->frame_type = vidc_720p_read(0x0EBC); - p_enc_frame_info->n_frame_type &= 0x03; + enc_frame_info->frame_type &= 0x03; - VIDC_IO_IN(REG_613254, - &p_enc_frame_info->n_metadata_exists); + enc_frame_info->metadata_exists = vidc_720p_read(0x0EB8); } -void vidc_720p_decode_bitstream_header(u32 n_ch_id, - u32 n_dec_unit_size, - u32 n_start_byte_num, - u32 n_ext_buffer_start, - u32 n_ext_buffer_end, - enum - vidc_720p_memory_access_method_type - e_memory_access_model) +void vidc_720p_decode_bitstream_header(u32 ch_id, u32 dec_unit_size, + u32 start_byte_num, u32 ext_buffer_start, u32 ext_buffer_end, + enum vidc_720p_memory_access_method_type memory_access_model) { - VIDC_IO_OUT(REG_965480, 0x0); + vidc_720p_write(0x0E04, 0x0); - VIDC_IO_OUT(REG_275113_ADDR, n_ext_buffer_start); + vidc_720p_write(0x0018, ext_buffer_start); - VIDC_IO_OUT(REG_988007_ADDR, n_ext_buffer_end); + vidc_720p_write(0x001C, ext_buffer_end); - VIDC_IO_OUT(REG_87912, n_ext_buffer_end); + vidc_720p_write(0x0024, ext_buffer_end); - VIDC_IO_OUT(REG_761892, n_dec_unit_size); + vidc_720p_write(0x0054, dec_unit_size); - VIDC_IO_OUT(REG_66693, n_start_byte_num); + vidc_720p_write(0x005C, start_byte_num); - VIDC_IO_OUT(REG_841539, e_memory_access_model); + vidc_720p_write(0x0600, memory_access_model); - vidc_720p_submit_command(n_ch_id, VIDC_720P_CMD_INITCODEC); + vidc_720p_submit_command(ch_id, VIDC_720P_CMD_INITCODEC); } void vidc_720p_decode_get_seq_hdr_info(struct vidc_720p_seq_hdr_info_type - *p_seq_hdr_info) + *seq_hdr_info) { - u32 n_display_status; - VIDC_IO_IN(REG_999267, &p_seq_hdr_info->n_img_size_x); + unsigned long tmp; - VIDC_IO_IN(REG_345712, &p_seq_hdr_info->n_img_size_y); + seq_hdr_info->img_size_x = vidc_720p_read(0x0118); - VIDC_IO_IN(REG_257463, &p_seq_hdr_info->n_min_num_dpb); + seq_hdr_info->img_size_y = vidc_720p_read(0x011C); - VIDC_IO_IN(REG_854281, &p_seq_hdr_info->n_min_dpb_size); + seq_hdr_info->min_num_dpb = vidc_720p_read(0x0E10); - VIDC_IO_IN(REG_580603, &p_seq_hdr_info->n_dec_frm_size); + seq_hdr_info->min_dpb_size = vidc_720p_read(0x0C10); - VIDC_IO_INF(REG_606447, DISP_PIC_PROFILE, - &p_seq_hdr_info->n_profile); + seq_hdr_info->dec_frm_size = vidc_720p_read(0x0C08); - VIDC_IO_INF(REG_606447, DIS_PIC_LEVEL, - &p_seq_hdr_info->n_level); + tmp = vidc_720p_read(0x0C0C); + seq_hdr_info->profile = tmp & 0x1f; + seq_hdr_info->level = (tmp & 0xff00) >> 8; - VIDC_IO_INF(REG_612715, DISPLAY_STATUS, - &n_display_status); - p_seq_hdr_info->n_progressive = - ((n_display_status & 0x4) >> 2); + tmp = vidc_720p_read(0x0408); + seq_hdr_info->progressive = (tmp & 0x4) >> 2; /* bit 3 is for crop existence */ - p_seq_hdr_info->n_crop_exists = ((n_display_status & 0x8) >> 3); + seq_hdr_info->crop_exists = (tmp & 0x8) >> 3; - if (p_seq_hdr_info->n_crop_exists) { + if (seq_hdr_info->crop_exists) { /* read the cropping information */ - VIDC_IO_INF(REG_881638, CROP_RIGHT_OFFSET, \ - &p_seq_hdr_info->n_crop_right_offset); - VIDC_IO_INF(REG_881638, CROP_LEFT_OFFSET, \ - &p_seq_hdr_info->n_crop_left_offset); - VIDC_IO_INF(REG_161486, CROP_BOTTOM_OFFSET, \ - &p_seq_hdr_info->n_crop_bottom_offset); - VIDC_IO_INF(REG_161486, CROP_TOP_OFFSET, \ - &p_seq_hdr_info->n_crop_top_offset); + tmp = vidc_720p_read(0x0C00); + seq_hdr_info->crop_right_offset = (tmp & 0xffff0000) >> 0x10; + seq_hdr_info->crop_left_offset = tmp & 0xffff; + tmp = vidc_720p_read(0x0C04); + seq_hdr_info->crop_bottom_offset = (tmp & 0xffff0000) >> 0x10; + seq_hdr_info->crop_top_offset = tmp & 0xffff; } /* Read the MPEG4 data partitioning indication */ - VIDC_IO_INF(REG_441270, DATA_PARTITIONED, \ - &p_seq_hdr_info->n_data_partitioned); - + seq_hdr_info->data_partitioned = (vidc_720p_read(0x0EBC) & 0x8) >> 3; } -void vidc_720p_decode_set_dpb_release_buffer_mask(u32 - i_dpb_release_buffer_mask) +void vidc_720p_decode_set_dpb_release_buffer_mask(u32 dpb_release_buffer_mask) { - VIDC_IO_OUT(REG_603032, i_dpb_release_buffer_mask); + vidc_720p_write(0x0E98, dpb_release_buffer_mask); } -void vidc_720p_decode_set_dpb_buffers(u32 i_buf_index, u32 *pi_dpb_buffer) +void vidc_720p_decode_set_dpb_buffers(u32 i, phys_addr_t dpb_buffer) { - VIDC_IO_OUTI(REG_615716, i_buf_index, pi_dpb_buffer); + vidc_720p_write(0x0E18 + sizeof(i) * i, dpb_buffer); } -void vidc_720p_decode_set_comv_buffer(u32 *pi_dpb_comv_buffer, - u32 n_alloc_len) +void vidc_720p_decode_set_comv_buffer(phys_addr_t pi_dpb_comv_buffer, + size_t alloc_len) { - VIDC_IO_OUT(REG_456376_ADDR, pi_dpb_comv_buffer); + vidc_720p_write(0x0904, pi_dpb_comv_buffer); - VIDC_IO_OUT(REG_490443, n_alloc_len); + vidc_720p_write(0x0D08, alloc_len); } -void vidc_720p_decode_set_dpb_details(u32 n_num_dpb, u32 n_alloc_len, - u32 *p_ref_buffer) +void vidc_720p_decode_set_dpb_details(u32 num_dpb, size_t alloc_len, + phys_addr_t ref_buffer) { - VIDC_IO_OUT(REG_518133, p_ref_buffer); + vidc_720p_write(0x0900, ref_buffer); - VIDC_IO_OUT(REG_267567, 0); + vidc_720p_write(0x0908, 0); - VIDC_IO_OUT(REG_883500, n_num_dpb); + vidc_720p_write(0x0E14, num_dpb); - VIDC_IO_OUT(REG_319934, n_alloc_len); + vidc_720p_write(0x0ED4, alloc_len); } -void vidc_720p_decode_set_mpeg4Post_filter(u32 b_enable_post_filter) +void vidc_720p_decode_set_mpeg4Post_filter(u32 enable_post_filter) { - if (b_enable_post_filter) - VIDC_IO_OUT(REG_443811, 0x1); + if (enable_post_filter) + vidc_720p_write(0x0124, 0x1); else - VIDC_IO_OUT(REG_443811, 0x0); + vidc_720p_write(0x0124, 0x0); } -void vidc_720p_decode_set_error_control(u32 b_enable_error_control) +void vidc_720p_decode_set_error_control(u32 enable_error_control) { - if (b_enable_error_control) - VIDC_IO_OUT(REG_846346, 0); + if (enable_error_control) + vidc_720p_write(0x013C, 0); else - VIDC_IO_OUT(REG_846346, 1); + vidc_720p_write(0x013C, 1); } -void vidc_720p_set_deblock_line_buffer(u32 *pi_deblock_line_buffer_start, - u32 n_alloc_len) +void vidc_720p_set_deblock_line_buffer(dma_addr_t pi_deblock_line_buffer_start, + size_t alloc_len) { - VIDC_IO_OUT(REG_979942, pi_deblock_line_buffer_start); + vidc_720p_write(0x0234, pi_deblock_line_buffer_start); - VIDC_IO_OUT(REG_101184, n_alloc_len); + vidc_720p_write(0x0D04, alloc_len); } -void vidc_720p_decode_set_mpeg4_data_partitionbuffer(u32 *p_vsp_buf_start) +void vidc_720p_decode_set_mpeg4_data_partitionbuffer(dma_addr_t vsp_buf_start) { - VIDC_IO_OUT(REG_958768, p_vsp_buf_start); + vidc_720p_write(0x0230, vsp_buf_start); } -void vidc_720p_decode_setH264VSPBuffer(u32 *pi_vsp_temp_buffer_start) +void vidc_720p_decode_setH264VSPBuffer(dma_addr_t pi_vsp_temp_buffer_start) { - VIDC_IO_OUT(REG_958768, pi_vsp_temp_buffer_start); + vidc_720p_write(0x0230, pi_vsp_temp_buffer_start); } -void vidc_720p_decode_frame(u32 n_ch_id, u32 n_ext_buffer_start, - u32 n_ext_buffer_end, u32 n_dec_unit_size, - u32 n_start_byte_num, u32 n_input_frame_tag) +void vidc_720p_decode_frame(u32 ch_id, phys_addr_t ext_buffer_start, + phys_addr_t ext_buffer_end, size_t dec_unit_size, u32 start_byte_num, + u32 input_frame_tag) { - VIDC_IO_OUT(REG_275113_ADDR, n_ext_buffer_start); + vidc_720p_write(0x0018, ext_buffer_start); - VIDC_IO_OUT(REG_988007_ADDR, n_ext_buffer_end); + vidc_720p_write(0x001C, ext_buffer_end); - VIDC_IO_OUT(REG_87912, n_ext_buffer_end); + vidc_720p_write(0x0024, ext_buffer_end); - VIDC_IO_OUT(REG_66693, n_start_byte_num); + vidc_720p_write(0x005C, start_byte_num); - VIDC_IO_OUT(REG_94750, n_input_frame_tag); + vidc_720p_write(0x0EE0, input_frame_tag); - VIDC_IO_OUT(REG_761892, n_dec_unit_size); + vidc_720p_write(0x0054, dec_unit_size); - vidc_720p_submit_command(n_ch_id, VIDC_720P_CMD_FRAMERUN); + vidc_720p_submit_command(ch_id, VIDC_720P_CMD_FRAMERUN); } -void vidc_720p_issue_eos(u32 i_ch_id) +void vidc_720p_issue_eos(u32 ch_id) { - VIDC_IO_OUT(REG_896825, 0x1); + vidc_720p_write(0x0028, 0x1); - VIDC_IO_OUT(REG_761892, 0); + vidc_720p_write(0x0054, 0); - vidc_720p_submit_command(i_ch_id, VIDC_720P_CMD_FRAMERUN); + vidc_720p_submit_command(ch_id, VIDC_720P_CMD_FRAMERUN); } -void vidc_720p_eos_info(u32 *p_disp_status) +void vidc_720p_eos_info(u32 *disp_status) { - VIDC_IO_INF(REG_612715, DISPLAY_STATUS, p_disp_status); - (*p_disp_status) = (*p_disp_status) & 0x3; + *disp_status = vidc_720p_read(0x0408) & 0x3; } -void vidc_720p_decode_display_info(struct vidc_720p_dec_disp_info_type - *p_disp_info) +void vidc_720p_decode_display_info(struct vidc_720p_dec_disp_info *disp_info) { - u32 display_status = 0; - VIDC_IO_INF(REG_612715, DISPLAY_STATUS, &display_status); + unsigned long tmp; - p_disp_info->e_disp_status = - (enum vidc_720p_display_status_type)((display_status & 0x3)); + tmp = vidc_720p_read(0x0408); - p_disp_info->n_disp_is_interlace = ((display_status & 0x4) >> 2); - p_disp_info->n_crop_exists = ((display_status & 0x8) >> 3); + disp_info->disp_status = (enum vidc_720p_display_status_type) + (tmp & 0x3); - p_disp_info->n_resl_change = ((display_status & 0x30) >> 4); + disp_info->disp_is_interlace = (tmp & 0x4) >> 2; + disp_info->crop_exists = (tmp & 0x8) >> 3; - VIDC_IO_INF(REG_724381, RESOLUTION_CHANGE, - &p_disp_info->n_reconfig_flush_done); + disp_info->resl_change = (tmp & 0x30) >> 4; - VIDC_IO_IN(REG_999267, &p_disp_info->n_img_size_x); + disp_info->reconfig_flush_done = vidc_720p_read(0x0EC0) & 0x1; - VIDC_IO_IN(REG_345712, &p_disp_info->n_img_size_y); - VIDC_IO_IN(REG_151345, &p_disp_info->n_y_addr); - VIDC_IO_IN(REG_293983, &p_disp_info->n_c_addr); - VIDC_IO_IN(REG_370409, &p_disp_info->n_tag_top); - VIDC_IO_IN(REG_438677, &p_disp_info->n_tag_bottom); - VIDC_IO_IN(REG_679165, &p_disp_info->n_pic_time_top); - VIDC_IO_IN(REG_374150, &p_disp_info->n_pic_time_bottom); + disp_info->img_size_x = vidc_720p_read(0x0118); + disp_info->img_size_y = vidc_720p_read(0x011C); + disp_info->y_addr = vidc_720p_read(0x0400); + disp_info->c_addr = vidc_720p_read(0x0404); + disp_info->tag_top = vidc_720p_read(0x0EA8); + disp_info->tag_bottom = vidc_720p_read(0x0EE4); + disp_info->pic_time_top = vidc_720p_read(0x0ED8); + disp_info->pic_time_bottom = vidc_720p_read(0x0EDC); - if (p_disp_info->n_crop_exists) { - VIDC_IO_INF(REG_881638, CROP_RIGHT_OFFSET, - &p_disp_info->n_crop_right_offset); - VIDC_IO_INF(REG_881638, CROP_LEFT_OFFSET, - &p_disp_info->n_crop_left_offset); - VIDC_IO_INF(REG_161486, CROP_BOTTOM_OFFSET, - &p_disp_info->n_crop_bottom_offset); - VIDC_IO_INF(REG_161486, CROP_TOP_OFFSET, - &p_disp_info->n_crop_top_offset); + if (disp_info->crop_exists) { + tmp = vidc_720p_read(0x0C00); + disp_info->crop_right_offset = (tmp & 0xffff0000) >> 0x10; + disp_info->crop_left_offset = tmp & 0xffff; + tmp = vidc_720p_read(0x0C04); + disp_info->crop_bottom_offset = (tmp & 0xffff0000) >> 0x10; + disp_info->crop_top_offset = tmp & 0xffff; } - VIDC_IO_IN(REG_613254, &p_disp_info->n_metadata_exists); + disp_info->metadata_exists = vidc_720p_read(0x0EB8); - VIDC_IO_IN(REG_580603, - &p_disp_info->n_input_bytes_consumed); + disp_info->input_bytes_consumed = vidc_720p_read(0x0C08); - VIDC_IO_IN(REG_757835, &p_disp_info->n_input_frame_num); + disp_info->input_frame_num = vidc_720p_read(0x0410); - VIDC_IO_INF(REG_441270, FRAME_TYPE, - &p_disp_info->n_input_frame_type); + disp_info->input_frame_type = vidc_720p_read(0x0EBC) & 0x7; - p_disp_info->n_input_is_interlace = - ((p_disp_info->n_input_frame_type & 0x4) >> 2); + disp_info->input_is_interlace = (disp_info->input_frame_type & 0x4) >> + 2; - p_disp_info->n_input_frame_type &= 0x3; + disp_info->input_frame_type &= 0x3; } -void vidc_720p_decode_skip_frm_details(u32 *p_free_luma_dpb) +void vidc_720p_decode_skip_frm_details(phys_addr_t *free_luma_dpb) { - u32 n_disp_frm_type; - VIDC_IO_IN(REG_697961, &n_disp_frm_type); + u32 disp_frm_type; + + disp_frm_type = vidc_720p_read(0x0EB4); - if (n_disp_frm_type == VIDC_720P_NOTCODED) - VIDC_IO_IN(REG_347105, p_free_luma_dpb); + if (disp_frm_type == VIDC_720P_NOTCODED) + *free_luma_dpb = vidc_720p_read(0x0C18); } -void vidc_720p_metadata_enable(u32 n_flag, u32 *p_input_buffer) +void vidc_720p_metadata_enable(u32 flag, phys_addr_t input_buffer) { - VIDC_IO_OUT(REG_854681, n_flag); - VIDC_IO_OUT(REG_988552, p_input_buffer); + vidc_720p_write(0x0EC4, flag); + vidc_720p_write(0x0ED0, input_buffer); } void vidc_720p_decode_dynamic_req_reset(void) { - VIDC_IO_OUT(REG_76706, 0x0); - VIDC_IO_OUT(REG_147682, 0x0); - VIDC_IO_OUT(REG_896825, 0x0); + vidc_720p_write(0x0EE8, 0x0); + vidc_720p_write(0x0EAC, 0x0); + vidc_720p_write(0x0028, 0x0); } -void vidc_720p_decode_dynamic_req_set(u32 n_property) +void vidc_720p_decode_dynamic_req_set(u32 property) { - if (n_property == VIDC_720P_FLUSH_REQ) - VIDC_IO_OUT(REG_76706, 0x1); - else if (n_property == VIDC_720P_EXTRADATA) - VIDC_IO_OUT(REG_147682, 0x1); + if (property == VIDC_720P_FLUSH_REQ) + vidc_720p_write(0x0EE8, 0x1); + else if (property == VIDC_720P_EXTRADATA) + vidc_720p_write(0x0EAC, 0x1); } -void vidc_720p_decode_setpassthrough_start(u32 n_pass_startaddr) +void vidc_720p_decode_setpassthrough_start(phys_addr_t pass_startaddr) { - VIDC_IO_OUT(REG_486169, n_pass_startaddr); + vidc_720p_write(0x0D18, pass_startaddr); } diff --git a/drivers/misc/video_core/720p/ddl/video_core_720p.h b/drivers/misc/video_core/720p/ddl/video_core_720p.h index a4d8840654045..14853f9502230 100644 --- a/drivers/misc/video_core/720p/ddl/video_core_720p.h +++ b/drivers/misc/video_core/720p/ddl/video_core_720p.h @@ -28,2154 +28,12 @@ */ #ifndef VID_C_720P_H #define VID_C_720P_H + #include #include #include -#define VIDC_720P_IN(reg) VIDC_##reg##_IN -#define VIDC_720P_INM(reg, mask) VIDC_##reg##_INM(mask) -#define VIDC_720P_OUT(reg, val) VIDC_##reg##_OUT(val) -#define VIDC_720P_OUTI(reg, index, val) VIDC_##reg##_OUTI(index, val) -#define VIDC_720P_OUTM(reg, mask, val) VIDC_##reg##_OUTM(mask, val) -#define VIDC_720P_SHFT(reg, field) VIDC_##reg##_##field##_SHFT -#define VIDC_720P_FMSK(reg, field) VIDC_##reg##_##field##_BMSK - -#define VIDC_720P_INF(io, field) (VIDC_720P_INM(io, VIDC_720P_FMSK(io, field)) \ - >> VIDC_720P_SHFT(io, field)) -#define VIDC_720P_OUTF(io, field, val) \ - VIDC_720P_OUTM(io, VIDC_720P_FMSK(io, field), \ - val << VIDC_720P_SHFT(io, field)) - -#define __inpdw(port) ioread32(port) -#define __outpdw(port, val) iowrite32(val, port) - -#define in_dword_masked(addr, mask) (__inpdw(addr) & (mask)) - -#define out_dword(addr, val) __outpdw(addr, val) - -#define out_dword_masked(io, mask, val, shadow) \ -do { \ - shadow = (shadow & (u32)(~(mask))) | ((u32)((val) & (mask))); \ - (void) out_dword(io, shadow); \ -} while (0) - -#define out_dword_masked_ns(io, mask, val, current_reg_content) \ - (void) out_dword(io, ((current_reg_content & (u32)(~(mask))) | \ - ((u32)((val) & (mask))))) - -extern u8 *vid_c_base_addr; - -#define VIDC720P_BASE vid_c_base_addr -#define VIDC_720P_WRAPPER_REG_BASE (VIDC720P_BASE + \ - 0x00000000) -#define VIDC_720P_WRAPPER_REG_BASE_PHYS VIDC_720P_BASE_PHYS - -#define VIDC_REG_614413_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 00000000) -#define VIDC_REG_614413_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 00000000) -#define VIDC_REG_614413_RMSK 0x1 -#define VIDC_REG_614413_SHFT 0 -#define VIDC_REG_614413_IN \ - in_dword_masked(VIDC_REG_614413_ADDR, \ - VIDC_REG_614413_RMSK) -#define VIDC_REG_614413_INM(m) \ - in_dword_masked(VIDC_REG_614413_ADDR, m) -#define VIDC_REG_614413_OUT(v) \ - out_dword(VIDC_REG_614413_ADDR, v) -#define VIDC_REG_614413_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_614413_ADDR, m, v, \ - VIDC_REG_614413_IN); \ -} while (0) -#define VIDC_REG_614413_DMA_START_BMSK 0x1 -#define VIDC_REG_614413_DMA_START_SHFT 0 - -#define VIDC_REG_591577_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x0000000c) -#define VIDC_REG_591577_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000000c) -#define VIDC_REG_591577_RMSK 0xffffffff -#define VIDC_REG_591577_SHFT 0 -#define VIDC_REG_591577_IN \ - in_dword_masked(VIDC_REG_591577_ADDR, \ - VIDC_REG_591577_RMSK) -#define VIDC_REG_591577_INM(m) \ - in_dword_masked(VIDC_REG_591577_ADDR, m) -#define VIDC_REG_591577_OUT(v) \ - out_dword(VIDC_REG_591577_ADDR, v) -#define VIDC_REG_591577_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_591577_ADDR, m, v, \ - VIDC_REG_591577_IN); \ -} while (0) -#define VIDC_REG_591577_BOOTCODE_SIZE_BMSK 0xffffffff -#define VIDC_REG_591577_BOOTCODE_SIZE_SHFT 0 - -#define VIDC_REG_203921_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000014) -#define VIDC_REG_203921_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000014) -#define VIDC_REG_203921_RMSK 0xffffffff -#define VIDC_REG_203921_SHFT 0 -#define VIDC_REG_203921_IN \ - in_dword_masked(VIDC_REG_203921_ADDR, \ - VIDC_REG_203921_RMSK) -#define VIDC_REG_203921_INM(m) \ - in_dword_masked(VIDC_REG_203921_ADDR, m) -#define VIDC_REG_203921_OUT(v) \ - out_dword(VIDC_REG_203921_ADDR, v) -#define VIDC_REG_203921_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_203921_ADDR, m, v, \ - VIDC_REG_203921_IN); \ -} while (0) -#define VIDC_REG_203921_DMA_EXTADDR_BMSK 0xffffffff -#define VIDC_REG_203921_DMA_EXTADDR_SHFT 0 - -#define VIDC_REG_275113_ADDR_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000018) -#define VIDC_REG_275113_ADDR_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000018) -#define VIDC_REG_275113_ADDR_RMSK 0xffffffff -#define VIDC_REG_275113_ADDR_SHFT 0 -#define VIDC_REG_275113_ADDR_IN \ - in_dword_masked(VIDC_REG_275113_ADDR_ADDR, \ - VIDC_REG_275113_ADDR_RMSK) -#define VIDC_REG_275113_ADDR_INM(m) \ - in_dword_masked(VIDC_REG_275113_ADDR_ADDR, m) -#define VIDC_REG_275113_ADDR_OUT(v) \ - out_dword(VIDC_REG_275113_ADDR_ADDR, v) -#define VIDC_REG_275113_ADDR_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_275113_ADDR_ADDR, m, v, \ - VIDC_REG_275113_ADDR_IN); \ -} while (0) -#define VIDC_REG_742076_ADDR_BMSK 0xffffffff -#define VIDC_REG_742076_ADDR_SHFT 0 - -#define VIDC_REG_988007_ADDR_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x0000001c) -#define VIDC_REG_988007_ADDR_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000001c) -#define VIDC_REG_988007_ADDR_RMSK 0xffffffff -#define VIDC_REG_988007_ADDR_SHFT 0 -#define VIDC_REG_988007_ADDR_IN \ - in_dword_masked(VIDC_REG_988007_ADDR_ADDR, \ - VIDC_REG_988007_ADDR_RMSK) -#define VIDC_REG_988007_ADDR_INM(m) \ - in_dword_masked(VIDC_REG_988007_ADDR_ADDR, m) -#define VIDC_REG_988007_ADDR_OUT(v) \ - out_dword(VIDC_REG_988007_ADDR_ADDR, v) -#define VIDC_REG_988007_ADDR_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_988007_ADDR_ADDR, m, v, \ - VIDC_REG_988007_ADDR_IN); \ -} while (0) -#define VIDC_REG_988007_ADDR_EXT_BUF_END_ADDR_BMSK 0xffffffff -#define VIDC_REG_988007_ADDR_EXT_BUF_END_ADDR_SHFT 0 - -#define VIDC_REG_531515_ADDR_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000020) -#define VIDC_REG_531515_ADDR_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000020) -#define VIDC_REG_531515_ADDR_RMSK 0xffffffff -#define VIDC_REG_531515_ADDR_SHFT 0 -#define VIDC_REG_531515_ADDR_IN \ - in_dword_masked(VIDC_REG_531515_ADDR_ADDR, \ - VIDC_REG_531515_ADDR_RMSK) -#define VIDC_REG_531515_ADDR_INM(m) \ - in_dword_masked(VIDC_REG_531515_ADDR_ADDR, m) -#define VIDC_REG_531515_ADDR_OUT(v) \ - out_dword(VIDC_REG_531515_ADDR_ADDR, v) -#define VIDC_REG_531515_ADDR_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_531515_ADDR_ADDR, m, v, \ - VIDC_REG_531515_ADDR_IN); \ -} while (0) -#define VIDC_REG_531515_ADDR_DMA_INT_ADDR_BMSK 0xffffffff -#define VIDC_REG_531515_ADDR_DMA_INT_ADDR_SHFT 0 - -#define VIDC_REG_87912_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000024) -#define VIDC_REG_87912_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000024) -#define VIDC_REG_87912_RMSK 0xffffffff -#define VIDC_REG_87912_SHFT 0 -#define VIDC_REG_87912_IN \ - in_dword_masked(VIDC_REG_87912_ADDR, \ - VIDC_REG_87912_RMSK) -#define VIDC_REG_87912_INM(m) \ - in_dword_masked(VIDC_REG_87912_ADDR, m) -#define VIDC_REG_87912_OUT(v) \ - out_dword(VIDC_REG_87912_ADDR, v) -#define VIDC_REG_87912_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_87912_ADDR, m, v, \ - VIDC_REG_87912_IN); \ -} while (0) -#define VIDC_REG_87912_HOST_PTR_ADDR_BMSK 0xffffffff -#define VIDC_REG_87912_HOST_PTR_ADDR_SHFT 0 - -#define VIDC_REG_896825_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000028) -#define VIDC_REG_896825_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000028) -#define VIDC_REG_896825_RMSK 0x1 -#define VIDC_REG_896825_SHFT 0 -#define VIDC_REG_896825_IN \ - in_dword_masked(VIDC_REG_896825_ADDR, \ - VIDC_REG_896825_RMSK) -#define VIDC_REG_896825_INM(m) \ - in_dword_masked(VIDC_REG_896825_ADDR, m) -#define VIDC_REG_896825_OUT(v) \ - out_dword(VIDC_REG_896825_ADDR, v) -#define VIDC_REG_896825_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_896825_ADDR, m, v, \ - VIDC_REG_896825_IN); \ -} while (0) -#define VIDC_REG_896825_LAST_DEC_BMSK 0x1 -#define VIDC_REG_896825_LAST_DEC_SHFT 0 - -#define VIDC_REG_174526_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x0000002c) -#define VIDC_REG_174526_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000002c) -#define VIDC_REG_174526_RMSK 0x1 -#define VIDC_REG_174526_SHFT 0 -#define VIDC_REG_174526_IN \ - in_dword_masked(VIDC_REG_174526_ADDR, VIDC_REG_174526_RMSK) -#define VIDC_REG_174526_INM(m) \ - in_dword_masked(VIDC_REG_174526_ADDR, m) -#define VIDC_REG_174526_DONE_M_BMSK 0x1 -#define VIDC_REG_174526_DONE_M_SHFT 0 - -#define VIDC_REG_736316_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000044) -#define VIDC_REG_736316_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000044) -#define VIDC_REG_736316_RMSK 0x1 -#define VIDC_REG_736316_SHFT 0 -#define VIDC_REG_736316_IN \ - in_dword_masked(VIDC_REG_736316_ADDR, \ - VIDC_REG_736316_RMSK) -#define VIDC_REG_736316_INM(m) \ - in_dword_masked(VIDC_REG_736316_ADDR, m) -#define VIDC_REG_736316_OUT(v) \ - out_dword(VIDC_REG_736316_ADDR, v) -#define VIDC_REG_736316_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_736316_ADDR, m, v, \ - VIDC_REG_736316_IN); \ -} while (0) -#define VIDC_REG_736316_BITS_ENDIAN_BMSK 0x1 -#define VIDC_REG_736316_BITS_ENDIAN_SHFT 0 - -#define VIDC_REG_761892_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000054) -#define VIDC_REG_761892_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000054) -#define VIDC_REG_761892_RMSK 0xffffffff -#define VIDC_REG_761892_SHFT 0 -#define VIDC_REG_761892_IN \ - in_dword_masked(VIDC_REG_761892_ADDR, \ - VIDC_REG_761892_RMSK) -#define VIDC_REG_761892_INM(m) \ - in_dword_masked(VIDC_REG_761892_ADDR, m) -#define VIDC_REG_761892_OUT(v) \ - out_dword(VIDC_REG_761892_ADDR, v) -#define VIDC_REG_761892_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_761892_ADDR, m, v, \ - VIDC_REG_761892_IN); \ -} while (0) -#define VIDC_REG_761892_DEC_UNIT_SIZE_BMSK 0xffffffff -#define VIDC_REG_761892_DEC_UNIT_SIZE_SHFT 0 - -#define VIDC_REG_782249_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000058) -#define VIDC_REG_782249_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000058) -#define VIDC_REG_782249_RMSK 0xffffffff -#define VIDC_REG_782249_SHFT 0 -#define VIDC_REG_782249_IN \ - in_dword_masked(VIDC_REG_782249_ADDR, \ - VIDC_REG_782249_RMSK) -#define VIDC_REG_782249_INM(m) \ - in_dword_masked(VIDC_REG_782249_ADDR, m) -#define VIDC_REG_782249_ENC_UNIT_SIZE_BMSK 0xffffffff -#define VIDC_REG_782249_ENC_UNIT_SIZE_SHFT 0 - -#define VIDC_REG_66693_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x0000005c) -#define VIDC_REG_66693_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000005c) -#define VIDC_REG_66693_RMSK 0xf -#define VIDC_REG_66693_SHFT 0 -#define VIDC_REG_66693_IN \ - in_dword_masked(VIDC_REG_66693_ADDR, \ - VIDC_REG_66693_RMSK) -#define VIDC_REG_66693_INM(m) \ - in_dword_masked(VIDC_REG_66693_ADDR, m) -#define VIDC_REG_66693_OUT(v) \ - out_dword(VIDC_REG_66693_ADDR, v) -#define VIDC_REG_66693_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_66693_ADDR, m, v, \ - VIDC_REG_66693_IN); \ -} while (0) -#define VIDC_REG_66693_START_BYTE_NUM_BMSK 0xf -#define VIDC_REG_66693_START_BYTE_NUM_SHFT 0 - -#define VIDC_REG_114286_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000060) -#define VIDC_REG_114286_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000060) -#define VIDC_REG_114286_RMSK 0xffffffff -#define VIDC_REG_114286_SHFT 0 -#define VIDC_REG_114286_IN \ - in_dword_masked(VIDC_REG_114286_ADDR, \ - VIDC_REG_114286_RMSK) -#define VIDC_REG_114286_INM(m) \ - in_dword_masked(VIDC_REG_114286_ADDR, m) -#define VIDC_REG_114286_ENC_HEADER_SIZE_BMSK 0xffffffff -#define VIDC_REG_114286_ENC_HEADER_SIZE_SHFT 0 - -#define VIDC_REG_713080_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000100) -#define VIDC_REG_713080_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000100) -#define VIDC_REG_713080_RMSK 0x1f -#define VIDC_REG_713080_SHFT 0 -#define VIDC_REG_713080_IN \ - in_dword_masked(VIDC_REG_713080_ADDR, \ - VIDC_REG_713080_RMSK) -#define VIDC_REG_713080_INM(m) \ - in_dword_masked(VIDC_REG_713080_ADDR, m) -#define VIDC_REG_713080_OUT(v) \ - out_dword(VIDC_REG_713080_ADDR, v) -#define VIDC_REG_713080_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_713080_ADDR, m, v, \ - VIDC_REG_713080_IN); \ -} while (0) -#define VIDC_REG_713080_ENC_ON_BMSK 0x10 -#define VIDC_REG_713080_ENC_ON_SHFT 0x4 -#define VIDC_REG_713080_STANDARD_SEL_BMSK 0xf -#define VIDC_REG_713080_STANDARD_SEL_SHFT 0 - -#define VIDC_REG_97293_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000104) -#define VIDC_REG_97293_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000104) -#define VIDC_REG_97293_RMSK 0x1f -#define VIDC_REG_97293_SHFT 0 -#define VIDC_REG_97293_IN \ - in_dword_masked(VIDC_REG_97293_ADDR, VIDC_REG_97293_RMSK) -#define VIDC_REG_97293_INM(m) \ - in_dword_masked(VIDC_REG_97293_ADDR, m) -#define VIDC_REG_97293_OUT(v) \ - out_dword(VIDC_REG_97293_ADDR, v) -#define VIDC_REG_97293_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_97293_ADDR, m, v, \ - VIDC_REG_97293_IN); \ -} while (0) -#define VIDC_REG_97293_CH_ID_BMSK 0x1f -#define VIDC_REG_97293_CH_ID_SHFT 0 - -#define VIDC_REG_224135_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000108) -#define VIDC_REG_224135_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000108) -#define VIDC_REG_224135_RMSK 0x1 -#define VIDC_REG_224135_SHFT 0 -#define VIDC_REG_224135_IN \ - in_dword_masked(VIDC_REG_224135_ADDR, \ - VIDC_REG_224135_RMSK) -#define VIDC_REG_224135_INM(m) \ - in_dword_masked(VIDC_REG_224135_ADDR, m) -#define VIDC_REG_224135_OUT(v) \ - out_dword(VIDC_REG_224135_ADDR, v) -#define VIDC_REG_224135_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_224135_ADDR, m, v, \ - VIDC_REG_224135_IN); \ -} while (0) -#define VIDC_REG_224135_CPU_RESET_BMSK 0x1 -#define VIDC_REG_224135_CPU_RESET_SHFT 0 - -#define VIDC_REG_832522_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x0000010c) -#define VIDC_REG_832522_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000010c) -#define VIDC_REG_832522_RMSK 0x1 -#define VIDC_REG_832522_SHFT 0 -#define VIDC_REG_832522_IN \ - in_dword_masked(VIDC_REG_832522_ADDR, VIDC_REG_832522_RMSK) -#define VIDC_REG_832522_INM(m) \ - in_dword_masked(VIDC_REG_832522_ADDR, m) -#define VIDC_REG_832522_OUT(v) \ - out_dword(VIDC_REG_832522_ADDR, v) -#define VIDC_REG_832522_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_832522_ADDR, m, v, \ - VIDC_REG_832522_IN); \ -} while (0) -#define VIDC_REG_832522_FW_END_BMSK 0x1 -#define VIDC_REG_832522_FW_END_SHFT 0 - -#define VIDC_REG_361582_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000110) -#define VIDC_REG_361582_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000110) -#define VIDC_REG_361582_RMSK 0x1 -#define VIDC_REG_361582_SHFT 0 -#define VIDC_REG_361582_IN \ - in_dword_masked(VIDC_REG_361582_ADDR, \ - VIDC_REG_361582_RMSK) -#define VIDC_REG_361582_INM(m) \ - in_dword_masked(VIDC_REG_361582_ADDR, m) -#define VIDC_REG_361582_OUT(v) \ - out_dword(VIDC_REG_361582_ADDR, v) -#define VIDC_REG_361582_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_361582_ADDR, m, v, \ - VIDC_REG_361582_IN); \ -} while (0) -#define VIDC_REG_361582_BUS_MASTER_BMSK 0x1 -#define VIDC_REG_361582_BUS_MASTER_SHFT 0 - -#define VIDC_REG_314435_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000114) -#define VIDC_REG_314435_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000114) -#define VIDC_REG_314435_RMSK 0x1 -#define VIDC_REG_314435_SHFT 0 -#define VIDC_REG_314435_IN \ - in_dword_masked(VIDC_REG_314435_ADDR, \ - VIDC_REG_314435_RMSK) -#define VIDC_REG_314435_INM(m) \ - in_dword_masked(VIDC_REG_314435_ADDR, m) -#define VIDC_REG_314435_OUT(v) \ - out_dword(VIDC_REG_314435_ADDR, v) -#define VIDC_REG_314435_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_314435_ADDR, m, v, \ - VIDC_REG_314435_IN); \ -} while (0) -#define VIDC_REG_314435_FRAME_START_BMSK 0x1 -#define VIDC_REG_314435_FRAME_START_SHFT 0 - -#define VIDC_REG_999267_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000118) -#define VIDC_REG_999267_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000118) -#define VIDC_REG_999267_RMSK 0xffff -#define VIDC_REG_999267_SHFT 0 -#define VIDC_REG_999267_IN \ - in_dword_masked(VIDC_REG_999267_ADDR, \ - VIDC_REG_999267_RMSK) -#define VIDC_REG_999267_INM(m) \ - in_dword_masked(VIDC_REG_999267_ADDR, m) -#define VIDC_REG_999267_OUT(v) \ - out_dword(VIDC_REG_999267_ADDR, v) -#define VIDC_REG_999267_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_999267_ADDR, m, v, \ - VIDC_REG_999267_IN); \ -} while (0) -#define VIDC_REG_999267_IMG_SIZE_X_BMSK 0xffff -#define VIDC_REG_999267_IMG_SIZE_X_SHFT 0 - -#define VIDC_REG_345712_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x0000011c) -#define VIDC_REG_345712_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000011c) -#define VIDC_REG_345712_RMSK 0xffff -#define VIDC_REG_345712_SHFT 0 -#define VIDC_REG_345712_IN \ - in_dword_masked(VIDC_REG_345712_ADDR, \ - VIDC_REG_345712_RMSK) -#define VIDC_REG_345712_INM(m) \ - in_dword_masked(VIDC_REG_345712_ADDR, m) -#define VIDC_REG_345712_OUT(v) \ - out_dword(VIDC_REG_345712_ADDR, v) -#define VIDC_REG_345712_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_345712_ADDR, m, v, \ - VIDC_REG_345712_IN); \ -} while (0) -#define VIDC_REG_345712_IMG_SIZE_Y_BMSK 0xffff -#define VIDC_REG_345712_IMG_SIZE_Y_SHFT 0 - -#define VIDC_REG_443811_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000124) -#define VIDC_REG_443811_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000124) -#define VIDC_REG_443811_RMSK 0x1 -#define VIDC_REG_443811_SHFT 0 -#define VIDC_REG_443811_IN \ - in_dword_masked(VIDC_REG_443811_ADDR, VIDC_REG_443811_RMSK) -#define VIDC_REG_443811_INM(m) \ - in_dword_masked(VIDC_REG_443811_ADDR, m) -#define VIDC_REG_443811_OUT(v) \ - out_dword(VIDC_REG_443811_ADDR, v) -#define VIDC_REG_443811_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_443811_ADDR, m, v, \ - VIDC_REG_443811_IN); \ -} while (0) -#define VIDC_REG_443811_POST_ON_BMSK 0x1 -#define VIDC_REG_443811_POST_ON_SHFT 0 - -#define VIDC_REG_538267_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000128) -#define VIDC_REG_538267_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000128) -#define VIDC_REG_538267_RMSK 0xffffffff -#define VIDC_REG_538267_SHFT 0 -#define VIDC_REG_538267_IN \ - in_dword_masked(VIDC_REG_538267_ADDR, \ - VIDC_REG_538267_RMSK) -#define VIDC_REG_538267_INM(m) \ - in_dword_masked(VIDC_REG_538267_ADDR, m) -#define VIDC_REG_538267_OUT(v) \ - out_dword(VIDC_REG_538267_ADDR, v) -#define VIDC_REG_538267_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_538267_ADDR, m, v, \ - VIDC_REG_538267_IN); \ -} while (0) -#define VIDC_REG_538267_QUOTIENT_VAL_BMSK 0xffff0000 -#define VIDC_REG_538267_QUOTIENT_VAL_SHFT 0x10 -#define VIDC_REG_538267_REMAINDER_VAL_BMSK 0xffff -#define VIDC_REG_538267_REMAINDER_VAL_SHFT 0 - -#define VIDC_REG_661565_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x0000012c) -#define VIDC_REG_661565_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000012c) -#define VIDC_REG_661565_RMSK 0x1 -#define VIDC_REG_661565_SHFT 0 -#define VIDC_REG_661565_IN \ - in_dword_masked(VIDC_REG_661565_ADDR, \ - VIDC_REG_661565_RMSK) -#define VIDC_REG_661565_INM(m) \ - in_dword_masked(VIDC_REG_661565_ADDR, m) -#define VIDC_REG_661565_OUT(v) \ - out_dword(VIDC_REG_661565_ADDR, v) -#define VIDC_REG_661565_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_661565_ADDR, m, v, \ - VIDC_REG_661565_IN); \ -} while (0) -#define VIDC_REG_661565_SEQUENCE_START_BMSK 0x1 -#define VIDC_REG_661565_SEQUENCE_START_SHFT 0 - -#define VIDC_REG_141269_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000130) -#define VIDC_REG_141269_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000130) -#define VIDC_REG_141269_RMSK 0x1 -#define VIDC_REG_141269_SHFT 0 -#define VIDC_REG_141269_IN \ - in_dword_masked(VIDC_REG_141269_ADDR, \ - VIDC_REG_141269_RMSK) -#define VIDC_REG_141269_INM(m) \ - in_dword_masked(VIDC_REG_141269_ADDR, m) -#define VIDC_REG_141269_OUT(v) \ - out_dword(VIDC_REG_141269_ADDR, v) -#define VIDC_REG_141269_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_141269_ADDR, m, v, \ - VIDC_REG_141269_IN); \ -} while (0) -#define VIDC_REG_141269_SW_RESET_BMSK 0x1 -#define VIDC_REG_141269_SW_RESET_SHFT 0 - -#define VIDC_REG_193553_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000134) -#define VIDC_REG_193553_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000134) -#define VIDC_REG_193553_RMSK 0x1 -#define VIDC_REG_193553_SHFT 0 -#define VIDC_REG_193553_IN \ - in_dword_masked(VIDC_REG_193553_ADDR, \ - VIDC_REG_193553_RMSK) -#define VIDC_REG_193553_INM(m) \ - in_dword_masked(VIDC_REG_193553_ADDR, m) -#define VIDC_REG_193553_OUT(v) \ - out_dword(VIDC_REG_193553_ADDR, v) -#define VIDC_REG_193553_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_193553_ADDR, m, v, \ - VIDC_REG_193553_IN); \ -} while (0) -#define VIDC_REG_193553_FW_START_BMSK 0x1 -#define VIDC_REG_193553_FW_START_SHFT 0 - -#define VIDC_REG_215724_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000138) -#define VIDC_REG_215724_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000138) -#define VIDC_REG_215724_RMSK 0x1 -#define VIDC_REG_215724_SHFT 0 -#define VIDC_REG_215724_IN \ - in_dword_masked(VIDC_REG_215724_ADDR, \ - VIDC_REG_215724_RMSK) -#define VIDC_REG_215724_INM(m) \ - in_dword_masked(VIDC_REG_215724_ADDR, m) -#define VIDC_REG_215724_OUT(v) \ - out_dword(VIDC_REG_215724_ADDR, v) -#define VIDC_REG_215724_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_215724_ADDR, m, v, \ - VIDC_REG_215724_IN); \ -} while (0) -#define VIDC_REG_215724_ARM_ENDIAN_BMSK 0x1 -#define VIDC_REG_215724_ARM_ENDIAN_SHFT 0 - -#define VIDC_REG_846346_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x0000013c) -#define VIDC_REG_846346_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000013c) -#define VIDC_REG_846346_RMSK 0x1 -#define VIDC_REG_846346_SHFT 0 -#define VIDC_REG_846346_IN \ - in_dword_masked(VIDC_REG_846346_ADDR, \ - VIDC_REG_846346_RMSK) -#define VIDC_REG_846346_INM(m) \ - in_dword_masked(VIDC_REG_846346_ADDR, m) -#define VIDC_REG_846346_OUT(v) \ - out_dword(VIDC_REG_846346_ADDR, v) -#define VIDC_REG_846346_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_846346_ADDR, m, v, \ - VIDC_REG_846346_IN); \ -} while (0) -#define VIDC_REG_846346_ERR_CTRL_BMSK 0x1 -#define VIDC_REG_846346_ERR_CTRL_SHFT 0 - -#define VIDC_REG_765787_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000200) -#define VIDC_REG_765787_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000200) -#define VIDC_REG_765787_RMSK 0xffffffff -#define VIDC_REG_765787_SHFT 0 -#define VIDC_REG_765787_IN \ - in_dword_masked(VIDC_REG_765787_ADDR, \ - VIDC_REG_765787_RMSK) -#define VIDC_REG_765787_INM(m) \ - in_dword_masked(VIDC_REG_765787_ADDR, m) -#define VIDC_REG_765787_OUT(v) \ - out_dword(VIDC_REG_765787_ADDR, v) -#define VIDC_REG_765787_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_765787_ADDR, m, v, \ - VIDC_REG_765787_IN); \ -} while (0) -#define VIDC_REG_765787_FW_STT_ADDR_0_BMSK 0xffffffff -#define VIDC_REG_765787_FW_STT_ADDR_0_SHFT 0 - -#define VIDC_REG_225040_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000204) -#define VIDC_REG_225040_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000204) -#define VIDC_REG_225040_RMSK 0xffffffff -#define VIDC_REG_225040_SHFT 0 -#define VIDC_REG_225040_IN \ - in_dword_masked(VIDC_REG_225040_ADDR, \ - VIDC_REG_225040_RMSK) -#define VIDC_REG_225040_INM(m) \ - in_dword_masked(VIDC_REG_225040_ADDR, m) -#define VIDC_REG_225040_OUT(v) \ - out_dword(VIDC_REG_225040_ADDR, v) -#define VIDC_REG_225040_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_225040_ADDR, m, v, \ - VIDC_REG_225040_IN); \ -} while (0) -#define VIDC_REG_225040_FW_STT_ADDR_1_BMSK 0xffffffff -#define VIDC_REG_225040_FW_STT_ADDR_1_SHFT 0 - -#define VIDC_REG_942456_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000208) -#define VIDC_REG_942456_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000208) -#define VIDC_REG_942456_RMSK 0xffffffff -#define VIDC_REG_942456_SHFT 0 -#define VIDC_REG_942456_IN \ - in_dword_masked(VIDC_REG_942456_ADDR, \ - VIDC_REG_942456_RMSK) -#define VIDC_REG_942456_INM(m) \ - in_dword_masked(VIDC_REG_942456_ADDR, m) -#define VIDC_REG_942456_OUT(v) \ - out_dword(VIDC_REG_942456_ADDR, v) -#define VIDC_REG_942456_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_942456_ADDR, m, v, \ - VIDC_REG_942456_IN); \ -} while (0) -#define VIDC_REG_942456_FW_STT_ADDR_2_BMSK 0xffffffff -#define VIDC_REG_942456_FW_STT_ADDR_2_SHFT 0 - -#define VIDC_REG_942170_ADDR_3_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x0000020c) -#define VIDC_REG_942170_ADDR_3_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000020c) -#define VIDC_REG_942170_ADDR_3_RMSK 0xffffffff -#define VIDC_REG_942170_ADDR_3_SHFT 0 -#define VIDC_REG_942170_ADDR_3_IN \ - in_dword_masked(VIDC_REG_942170_ADDR_3_ADDR, \ - VIDC_REG_942170_ADDR_3_RMSK) -#define VIDC_REG_942170_ADDR_3_INM(m) \ - in_dword_masked(VIDC_REG_942170_ADDR_3_ADDR, m) -#define VIDC_REG_942170_ADDR_3_OUT(v) \ - out_dword(VIDC_REG_942170_ADDR_3_ADDR, v) -#define VIDC_REG_942170_ADDR_3_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_942170_ADDR_3_ADDR, m, v, \ - VIDC_REG_942170_ADDR_3_IN); \ -} while (0) -#define VIDC_REG_942170_ADDR_3_FW_STT_ADDR_3_BMSK 0xffffffff -#define VIDC_REG_942170_ADDR_3_FW_STT_ADDR_3_SHFT 0 - -#define VIDC_REG_880188_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000210) -#define VIDC_REG_880188_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000210) -#define VIDC_REG_880188_RMSK 0xffffffff -#define VIDC_REG_880188_SHFT 0 -#define VIDC_REG_880188_IN \ - in_dword_masked(VIDC_REG_880188_ADDR, \ - VIDC_REG_880188_RMSK) -#define VIDC_REG_880188_INM(m) \ - in_dword_masked(VIDC_REG_880188_ADDR, m) -#define VIDC_REG_880188_OUT(v) \ - out_dword(VIDC_REG_880188_ADDR, v) -#define VIDC_REG_880188_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_880188_ADDR, m, v, \ - VIDC_REG_880188_IN); \ -} while (0) -#define VIDC_REG_880188_FW_STT_ADDR_4_BMSK 0xffffffff -#define VIDC_REG_880188_FW_STT_ADDR_4_SHFT 0 - -#define VIDC_REG_40293_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000214) -#define VIDC_REG_40293_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000214) -#define VIDC_REG_40293_RMSK 0xffffffff -#define VIDC_REG_40293_SHFT 0 -#define VIDC_REG_40293_IN \ - in_dword_masked(VIDC_REG_40293_ADDR, \ - VIDC_REG_40293_RMSK) -#define VIDC_REG_40293_INM(m) \ - in_dword_masked(VIDC_REG_40293_ADDR, m) -#define VIDC_REG_40293_OUT(v) \ - out_dword(VIDC_REG_40293_ADDR, v) -#define VIDC_REG_40293_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_40293_ADDR, m, v, \ - VIDC_REG_40293_IN); \ -} while (0) -#define VIDC_REG_40293_FW_STT_ADDR_5_BMSK 0xffffffff -#define VIDC_REG_40293_FW_STT_ADDR_5_SHFT 0 - -#define VIDC_REG_942170_ADDR_6_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000218) -#define VIDC_REG_942170_ADDR_6_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000218) -#define VIDC_REG_942170_ADDR_6_RMSK 0xffffffff -#define VIDC_REG_942170_ADDR_6_SHFT 0 -#define VIDC_REG_942170_ADDR_6_IN \ - in_dword_masked(VIDC_REG_942170_ADDR_6_ADDR, \ - VIDC_REG_942170_ADDR_6_RMSK) -#define VIDC_REG_942170_ADDR_6_INM(m) \ - in_dword_masked(VIDC_REG_942170_ADDR_6_ADDR, m) -#define VIDC_REG_942170_ADDR_6_OUT(v) \ - out_dword(VIDC_REG_942170_ADDR_6_ADDR, v) -#define VIDC_REG_942170_ADDR_6_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_942170_ADDR_6_ADDR, m, v, \ - VIDC_REG_942170_ADDR_6_IN); \ -} while (0) -#define VIDC_REG_942170_ADDR_6_FW_STT_ADDR_6_BMSK 0xffffffff -#define VIDC_REG_942170_ADDR_6_FW_STT_ADDR_6_SHFT 0 - -#define VIDC_REG_958768_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000230) -#define VIDC_REG_958768_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000230) -#define VIDC_REG_958768_RMSK 0xffffffff -#define VIDC_REG_958768_SHFT 0 -#define VIDC_REG_958768_IN \ - in_dword_masked(VIDC_REG_958768_ADDR, \ - VIDC_REG_958768_RMSK) -#define VIDC_REG_958768_INM(m) \ - in_dword_masked(VIDC_REG_958768_ADDR, m) -#define VIDC_REG_958768_OUT(v) \ - out_dword(VIDC_REG_958768_ADDR, v) -#define VIDC_REG_958768_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_958768_ADDR, m, v, \ - VIDC_REG_958768_IN); \ -} while (0) -#define VIDC_REG_699384_ADDR_BMSK 0xffffffff -#define VIDC_REG_699384_ADDR_SHFT 0 - -#define VIDC_REG_979942_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000234) -#define VIDC_REG_979942_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000234) -#define VIDC_REG_979942_RMSK 0xffffffff -#define VIDC_REG_979942_SHFT 0 -#define VIDC_REG_979942_IN \ - in_dword_masked(VIDC_REG_979942_ADDR, \ - VIDC_REG_979942_RMSK) -#define VIDC_REG_979942_INM(m) \ - in_dword_masked(VIDC_REG_979942_ADDR, m) -#define VIDC_REG_979942_OUT(v) \ - out_dword(VIDC_REG_979942_ADDR, v) -#define VIDC_REG_979942_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_979942_ADDR, m, v, \ - VIDC_REG_979942_IN); \ -} while (0) -#define VIDC_REG_979942_DB_STT_ADDR_BMSK 0xffffffff -#define VIDC_REG_979942_DB_STT_ADDR_SHFT 0 - -#define VIDC_REG_839021_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000300) -#define VIDC_REG_839021_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000300) -#define VIDC_REG_839021_RMSK 0xff1f -#define VIDC_REG_839021_SHFT 0 -#define VIDC_REG_839021_IN \ - in_dword_masked(VIDC_REG_839021_ADDR, VIDC_REG_839021_RMSK) -#define VIDC_REG_839021_INM(m) \ - in_dword_masked(VIDC_REG_839021_ADDR, m) -#define VIDC_REG_839021_OUT(v) \ - out_dword(VIDC_REG_839021_ADDR, v) -#define VIDC_REG_839021_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_839021_ADDR, m, v, \ - VIDC_REG_839021_IN); \ -} while (0) -#define VIDC_REG_839021_LEVEL_BMSK 0xff00 -#define VIDC_REG_839021_LEVEL_SHFT 0x8 -#define VIDC_REG_839021_PROFILE_BMSK 0x1f -#define VIDC_REG_839021_PROFILE_SHFT 0 - -#define VIDC_REG_950374_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000308) -#define VIDC_REG_950374_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000308) -#define VIDC_REG_950374_RMSK 0xffff -#define VIDC_REG_950374_SHFT 0 -#define VIDC_REG_950374_IN \ - in_dword_masked(VIDC_REG_950374_ADDR, \ - VIDC_REG_950374_RMSK) -#define VIDC_REG_950374_INM(m) \ - in_dword_masked(VIDC_REG_950374_ADDR, m) -#define VIDC_REG_950374_OUT(v) \ - out_dword(VIDC_REG_950374_ADDR, v) -#define VIDC_REG_950374_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_950374_ADDR, m, v, \ - VIDC_REG_950374_IN); \ -} while (0) -#define VIDC_REG_950374_I_PERIOD_BMSK 0xffff -#define VIDC_REG_950374_I_PERIOD_SHFT 0 - -#define VIDC_REG_504878_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000310) -#define VIDC_REG_504878_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000310) -#define VIDC_REG_504878_RMSK 0xd -#define VIDC_REG_504878_SHFT 0 -#define VIDC_REG_504878_IN \ - in_dword_masked(VIDC_REG_504878_ADDR, \ - VIDC_REG_504878_RMSK) -#define VIDC_REG_504878_INM(m) \ - in_dword_masked(VIDC_REG_504878_ADDR, m) -#define VIDC_REG_504878_OUT(v) \ - out_dword(VIDC_REG_504878_ADDR, v) -#define VIDC_REG_504878_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_504878_ADDR, m, v, \ - VIDC_REG_504878_IN); \ -} while (0) -#define VIDC_REG_504878_FIXED_NUMBER_BMSK 0xc -#define VIDC_REG_504878_FIXED_NUMBER_SHFT 0x2 -#define VIDC_REG_504878_ENTROPY_SEL_BMSK 0x1 -#define VIDC_REG_504878_ENTROPY_SEL_SHFT 0 - -#define VIDC_REG_458130_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000314) -#define VIDC_REG_458130_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000314) -#define VIDC_REG_458130_RMSK 0xfff -#define VIDC_REG_458130_SHFT 0 -#define VIDC_REG_458130_IN \ - in_dword_masked(VIDC_REG_458130_ADDR, \ - VIDC_REG_458130_RMSK) -#define VIDC_REG_458130_INM(m) \ - in_dword_masked(VIDC_REG_458130_ADDR, m) -#define VIDC_REG_458130_OUT(v) \ - out_dword(VIDC_REG_458130_ADDR, v) -#define VIDC_REG_458130_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_458130_ADDR, m, v, \ - VIDC_REG_458130_IN); \ -} while (0) -#define VIDC_REG_458130_SLICE_ALPHA_C0_OFFSET_DIV2_BMSK \ - 0xf80 -#define VIDC_REG_458130_SLICE_ALPHA_C0_OFFSET_DIV2_SHFT \ - 0x7 -#define VIDC_REG_458130_SLICE_BETA_OFFSET_DIV2_BMSK 0x7c -#define VIDC_REG_458130_SLICE_BETA_OFFSET_DIV2_SHFT 0x2 -#define \ - \ -VIDC_REG_458130_DISABLE_DEBLOCKING_FILTER_IDC_BMSK 0x3 -#define \ - \ -VIDC_REG_458130_DISABLE_DEBLOCKING_FILTER_IDC_SHFT 0 - -#define VIDC_REG_314290_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000318) -#define VIDC_REG_314290_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000318) -#define VIDC_REG_314290_RMSK 0x1 -#define VIDC_REG_314290_SHFT 0 -#define VIDC_REG_314290_IN \ - in_dword_masked(VIDC_REG_314290_ADDR, \ - VIDC_REG_314290_RMSK) -#define VIDC_REG_314290_INM(m) \ - in_dword_masked(VIDC_REG_314290_ADDR, m) -#define VIDC_REG_314290_OUT(v) \ - out_dword(VIDC_REG_314290_ADDR, v) -#define VIDC_REG_314290_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_314290_ADDR, m, v, \ - VIDC_REG_314290_IN); \ -} while (0) -#define VIDC_REG_314290_SHORT_HD_ON_BMSK 0x1 -#define VIDC_REG_314290_SHORT_HD_ON_SHFT 0 - -#define VIDC_REG_588301_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x0000031c) -#define VIDC_REG_588301_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000031c) -#define VIDC_REG_588301_RMSK 0x1 -#define VIDC_REG_588301_SHFT 0 -#define VIDC_REG_588301_IN \ - in_dword_masked(VIDC_REG_588301_ADDR, \ - VIDC_REG_588301_RMSK) -#define VIDC_REG_588301_INM(m) \ - in_dword_masked(VIDC_REG_588301_ADDR, m) -#define VIDC_REG_588301_OUT(v) \ - out_dword(VIDC_REG_588301_ADDR, v) -#define VIDC_REG_588301_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_588301_ADDR, m, v, \ - VIDC_REG_588301_IN); \ -} while (0) -#define VIDC_REG_588301_MSLICE_ENA_BMSK 0x1 -#define VIDC_REG_588301_MSLICE_ENA_SHFT 0 - -#define VIDC_REG_1517_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000320) -#define VIDC_REG_1517_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000320) -#define VIDC_REG_1517_RMSK 0x3 -#define VIDC_REG_1517_SHFT 0 -#define VIDC_REG_1517_IN \ - in_dword_masked(VIDC_REG_1517_ADDR, \ - VIDC_REG_1517_RMSK) -#define VIDC_REG_1517_INM(m) \ - in_dword_masked(VIDC_REG_1517_ADDR, m) -#define VIDC_REG_1517_OUT(v) \ - out_dword(VIDC_REG_1517_ADDR, v) -#define VIDC_REG_1517_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_1517_ADDR, m, v, \ - VIDC_REG_1517_IN); \ -} while (0) -#define VIDC_REG_1517_MSLICE_SEL_BMSK 0x3 -#define VIDC_REG_1517_MSLICE_SEL_SHFT 0 - -#define VIDC_REG_105335_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000324) -#define VIDC_REG_105335_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000324) -#define VIDC_REG_105335_RMSK 0xffffffff -#define VIDC_REG_105335_SHFT 0 -#define VIDC_REG_105335_IN \ - in_dword_masked(VIDC_REG_105335_ADDR, \ - VIDC_REG_105335_RMSK) -#define VIDC_REG_105335_INM(m) \ - in_dword_masked(VIDC_REG_105335_ADDR, m) -#define VIDC_REG_105335_OUT(v) \ - out_dword(VIDC_REG_105335_ADDR, v) -#define VIDC_REG_105335_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_105335_ADDR, m, v, \ - VIDC_REG_105335_IN); \ -} while (0) -#define VIDC_REG_105335_MSLICE_MB_BMSK 0xffffffff -#define VIDC_REG_105335_MSLICE_MB_SHFT 0 - -#define VIDC_REG_561679_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000328) -#define VIDC_REG_561679_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000328) -#define VIDC_REG_561679_RMSK 0xffffffff -#define VIDC_REG_561679_SHFT 0 -#define VIDC_REG_561679_IN \ - in_dword_masked(VIDC_REG_561679_ADDR, \ - VIDC_REG_561679_RMSK) -#define VIDC_REG_561679_INM(m) \ - in_dword_masked(VIDC_REG_561679_ADDR, m) -#define VIDC_REG_561679_OUT(v) \ - out_dword(VIDC_REG_561679_ADDR, v) -#define VIDC_REG_561679_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_561679_ADDR, m, v, \ - VIDC_REG_561679_IN); \ -} while (0) -#define VIDC_REG_561679_MSLICE_BYTE_BMSK 0xffffffff -#define VIDC_REG_561679_MSLICE_BYTE_SHFT 0 - -#define VIDC_REG_151345_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000400) -#define VIDC_REG_151345_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000400) -#define VIDC_REG_151345_RMSK 0xffffffff -#define VIDC_REG_151345_SHFT 0 -#define VIDC_REG_151345_IN \ - in_dword_masked(VIDC_REG_151345_ADDR, \ - VIDC_REG_151345_RMSK) -#define VIDC_REG_151345_INM(m) \ - in_dword_masked(VIDC_REG_151345_ADDR, m) -#define VIDC_REG_151345_DISPLAY_Y_ADR_BMSK 0xffffffff -#define VIDC_REG_151345_DISPLAY_Y_ADR_SHFT 0 - -#define VIDC_REG_293983_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000404) -#define VIDC_REG_293983_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000404) -#define VIDC_REG_293983_RMSK 0xffffffff -#define VIDC_REG_293983_SHFT 0 -#define VIDC_REG_293983_IN \ - in_dword_masked(VIDC_REG_293983_ADDR, \ - VIDC_REG_293983_RMSK) -#define VIDC_REG_293983_INM(m) \ - in_dword_masked(VIDC_REG_293983_ADDR, m) -#define VIDC_REG_293983_DISPLAY_C_ADR_BMSK 0xffffffff -#define VIDC_REG_293983_DISPLAY_C_ADR_SHFT 0 - -#define VIDC_REG_612715_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000408) -#define VIDC_REG_612715_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000408) -#define VIDC_REG_612715_RMSK 0x3f -#define VIDC_REG_612715_SHFT 0 -#define VIDC_REG_612715_IN \ - in_dword_masked(VIDC_REG_612715_ADDR, \ - VIDC_REG_612715_RMSK) -#define VIDC_REG_612715_INM(m) \ - in_dword_masked(VIDC_REG_612715_ADDR, m) -#define VIDC_REG_612715_DISPLAY_STATUS_BMSK 0x3f -#define VIDC_REG_612715_DISPLAY_STATUS_SHFT 0 - -#define VIDC_REG_209364_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x0000040c) -#define VIDC_REG_209364_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000040c) -#define VIDC_REG_209364_RMSK 0x1 -#define VIDC_REG_209364_SHFT 0 -#define VIDC_REG_209364_IN \ - in_dword_masked(VIDC_REG_209364_ADDR, \ - VIDC_REG_209364_RMSK) -#define VIDC_REG_209364_INM(m) \ - in_dword_masked(VIDC_REG_209364_ADDR, m) -#define VIDC_REG_209364_HEADER_DONE_BMSK 0x1 -#define VIDC_REG_209364_HEADER_DONE_SHFT 0 - -#define VIDC_REG_757835_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000410) -#define VIDC_REG_757835_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000410) -#define VIDC_REG_757835_RMSK 0xffffffff -#define VIDC_REG_757835_SHFT 0 -#define VIDC_REG_757835_IN \ - in_dword_masked(VIDC_REG_757835_ADDR, \ - VIDC_REG_757835_RMSK) -#define VIDC_REG_757835_INM(m) \ - in_dword_masked(VIDC_REG_757835_ADDR, m) -#define VIDC_REG_757835_FRAME_NUM_BMSK 0xffffffff -#define VIDC_REG_757835_FRAME_NUM_SHFT 0 - -#define VIDC_REG_352831_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000414) -#define VIDC_REG_352831_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000414) -#define VIDC_REG_352831_RMSK 0xffffffff -#define VIDC_REG_352831_SHFT 0 -#define VIDC_REG_352831_IN \ - in_dword_masked(VIDC_REG_352831_ADDR, \ - VIDC_REG_352831_RMSK) -#define VIDC_REG_352831_INM(m) \ - in_dword_masked(VIDC_REG_352831_ADDR, m) -#define VIDC_REG_352831_DBG_INFO_OUTPUT0_BMSK 0xffffffff -#define VIDC_REG_352831_DBG_INFO_OUTPUT0_SHFT 0 - -#define VIDC_REG_668634_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000418) -#define VIDC_REG_668634_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000418) -#define VIDC_REG_668634_RMSK 0xffffffff -#define VIDC_REG_668634_SHFT 0 -#define VIDC_REG_668634_IN \ - in_dword_masked(VIDC_REG_668634_ADDR, \ - VIDC_REG_668634_RMSK) -#define VIDC_REG_668634_INM(m) \ - in_dword_masked(VIDC_REG_668634_ADDR, m) -#define VIDC_REG_668634_DBG_INFO_OUTPUT1_BMSK 0xffffffff -#define VIDC_REG_668634_DBG_INFO_OUTPUT1_SHFT 0 - -#define VIDC_REG_609676_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000500) -#define VIDC_REG_609676_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000500) -#define VIDC_REG_609676_RMSK 0x1 -#define VIDC_REG_609676_SHFT 0 -#define VIDC_REG_609676_IN \ - in_dword_masked(VIDC_REG_609676_ADDR, VIDC_REG_609676_RMSK) -#define VIDC_REG_609676_INM(m) \ - in_dword_masked(VIDC_REG_609676_ADDR, m) -#define VIDC_REG_609676_OUT(v) \ - out_dword(VIDC_REG_609676_ADDR, v) -#define VIDC_REG_609676_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_609676_ADDR, m, v, \ - VIDC_REG_609676_IN); \ -} while (0) -#define VIDC_REG_609676_INT_OFF_BMSK 0x1 -#define VIDC_REG_609676_INT_OFF_SHFT 0 - -#define VIDC_REG_491082_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000504) -#define VIDC_REG_491082_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000504) -#define VIDC_REG_491082_RMSK 0x1 -#define VIDC_REG_491082_SHFT 0 -#define VIDC_REG_491082_IN \ - in_dword_masked(VIDC_REG_491082_ADDR, \ - VIDC_REG_491082_RMSK) -#define VIDC_REG_491082_INM(m) \ - in_dword_masked(VIDC_REG_491082_ADDR, m) -#define VIDC_REG_491082_OUT(v) \ - out_dword(VIDC_REG_491082_ADDR, v) -#define VIDC_REG_491082_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_491082_ADDR, m, v, \ - VIDC_REG_491082_IN); \ -} while (0) -#define VIDC_REG_491082_INT_PULSE_SEL_BMSK 0x1 -#define VIDC_REG_491082_INT_PULSE_SEL_SHFT 0 - -#define VIDC_REG_614776_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000508) -#define VIDC_REG_614776_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000508) -#define VIDC_REG_614776_RMSK 0x1 -#define VIDC_REG_614776_SHFT 0 -#define VIDC_REG_614776_IN \ - in_dword_masked(VIDC_REG_614776_ADDR, \ - VIDC_REG_614776_RMSK) -#define VIDC_REG_614776_INM(m) \ - in_dword_masked(VIDC_REG_614776_ADDR, m) -#define VIDC_REG_614776_OUT(v) \ - out_dword(VIDC_REG_614776_ADDR, v) -#define VIDC_REG_614776_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_614776_ADDR, m, v, \ - VIDC_REG_614776_IN); \ -} while (0) -#define VIDC_REG_614776_INT_DONE_CLEAR_BMSK 0x1 -#define VIDC_REG_614776_INT_DONE_CLEAR_SHFT 0 - -#define VIDC_REG_982553_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x0000050c) -#define VIDC_REG_982553_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000050c) -#define VIDC_REG_982553_RMSK 0x1 -#define VIDC_REG_982553_SHFT 0 -#define VIDC_REG_982553_IN \ - in_dword_masked(VIDC_REG_982553_ADDR, \ - VIDC_REG_982553_RMSK) -#define VIDC_REG_982553_INM(m) \ - in_dword_masked(VIDC_REG_982553_ADDR, m) -#define VIDC_REG_982553_OPERATION_DONE_BMSK 0x1 -#define VIDC_REG_982553_OPERATION_DONE_SHFT 0 - -#define VIDC_REG_259967_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000510) -#define VIDC_REG_259967_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000510) -#define VIDC_REG_259967_RMSK 0x1 -#define VIDC_REG_259967_SHFT 0 -#define VIDC_REG_259967_IN \ - in_dword_masked(VIDC_REG_259967_ADDR, VIDC_REG_259967_RMSK) -#define VIDC_REG_259967_INM(m) \ - in_dword_masked(VIDC_REG_259967_ADDR, m) -#define VIDC_REG_259967_FW_DONE_BMSK 0x1 -#define VIDC_REG_259967_FW_DONE_SHFT 0 - -#define VIDC_REG_512143_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000514) -#define VIDC_REG_512143_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000514) -#define VIDC_REG_512143_RMSK 0x1f8 -#define VIDC_REG_512143_SHFT 0 -#define VIDC_REG_512143_IN \ - in_dword_masked(VIDC_REG_512143_ADDR, \ - VIDC_REG_512143_RMSK) -#define VIDC_REG_512143_INM(m) \ - in_dword_masked(VIDC_REG_512143_ADDR, m) -#define VIDC_REG_512143_FRAME_DONE_STAT_BMSK 0x100 -#define VIDC_REG_512143_FRAME_DONE_STAT_SHFT 0x8 -#define VIDC_REG_512143_DMA_DONE_STAT_BMSK 0x80 -#define VIDC_REG_512143_DMA_DONE_STAT_SHFT 0x7 -#define VIDC_REG_512143_HEADER_DONE_STAT_BMSK 0x40 -#define VIDC_REG_512143_HEADER_DONE_STAT_SHFT 0x6 -#define VIDC_REG_512143_FW_DONE_STAT_BMSK 0x20 -#define VIDC_REG_512143_FW_DONE_STAT_SHFT 0x5 -#define VIDC_REG_512143_OPERATION_FAILED_BMSK 0x10 -#define VIDC_REG_512143_OPERATION_FAILED_SHFT 0x4 -#define VIDC_REG_512143_STREAM_HDR_CHANGED_BMSK 0x8 -#define VIDC_REG_512143_STREAM_HDR_CHANGED_SHFT 0x3 - -#define VIDC_REG_418173_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000518) -#define VIDC_REG_418173_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000518) -#define VIDC_REG_418173_RMSK 0x1fa -#define VIDC_REG_418173_SHFT 0 -#define VIDC_REG_418173_IN \ - in_dword_masked(VIDC_REG_418173_ADDR, \ - VIDC_REG_418173_RMSK) -#define VIDC_REG_418173_INM(m) \ - in_dword_masked(VIDC_REG_418173_ADDR, m) -#define VIDC_REG_418173_OUT(v) \ - out_dword(VIDC_REG_418173_ADDR, v) -#define VIDC_REG_418173_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_418173_ADDR, m, v, \ - VIDC_REG_418173_IN); \ -} while (0) -#define VIDC_REG_418173_FRAME_DONE_ENABLE_BMSK 0x100 -#define VIDC_REG_418173_FRAME_DONE_ENABLE_SHFT 0x8 -#define VIDC_REG_418173_DMA_DONE_ENABLE_BMSK 0x80 -#define VIDC_REG_418173_DMA_DONE_ENABLE_SHFT 0x7 -#define VIDC_REG_418173_HEADER_DONE_ENABLE_BMSK 0x40 -#define VIDC_REG_418173_HEADER_DONE_ENABLE_SHFT 0x6 -#define VIDC_REG_418173_FW_DONE_ENABLE_BMSK 0x20 -#define VIDC_REG_418173_FW_DONE_ENABLE_SHFT 0x5 -#define VIDC_REG_418173_OPERATION_FAILED_ENABLE_BMSK 0x10 -#define VIDC_REG_418173_OPERATION_FAILED_ENABLE_SHFT 0x4 -#define VIDC_REG_418173_STREAM_HDR_CHANGED_ENABLE_BMSK 0x8 -#define VIDC_REG_418173_STREAM_HDR_CHANGED_ENABLE_SHFT 0x3 -#define VIDC_REG_418173_BUFFER_FULL_ENABLE_BMSK 0x2 -#define VIDC_REG_418173_BUFFER_FULL_ENABLE_SHFT 0x1 - -#define VIDC_REG_841539_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000600) -#define VIDC_REG_841539_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000600) -#define VIDC_REG_841539_RMSK 0x3 -#define VIDC_REG_841539_SHFT 0 -#define VIDC_REG_841539_IN \ - in_dword_masked(VIDC_REG_841539_ADDR, \ - VIDC_REG_841539_RMSK) -#define VIDC_REG_841539_INM(m) \ - in_dword_masked(VIDC_REG_841539_ADDR, m) -#define VIDC_REG_841539_OUT(v) \ - out_dword(VIDC_REG_841539_ADDR, v) -#define VIDC_REG_841539_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_841539_ADDR, m, v, \ - VIDC_REG_841539_IN); \ -} while (0) -#define VIDC_REG_841539_TILE_MODE_BMSK 0x3 -#define VIDC_REG_841539_TILE_MODE_SHFT 0 - -#define VIDC_REG_99105_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000800) -#define VIDC_REG_99105_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000800) -#define VIDC_REG_99105_RMSK 0xffffffff -#define VIDC_REG_99105_SHFT 0 -#define VIDC_REG_99105_IN \ - in_dword_masked(VIDC_REG_99105_ADDR, \ - VIDC_REG_99105_RMSK) -#define VIDC_REG_99105_INM(m) \ - in_dword_masked(VIDC_REG_99105_ADDR, m) -#define VIDC_REG_99105_OUT(v) \ - out_dword(VIDC_REG_99105_ADDR, v) -#define VIDC_REG_99105_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_99105_ADDR, m, v, \ - VIDC_REG_99105_IN); \ -} while (0) -#define VIDC_REG_99105_ENC_CUR_Y_ADDR_BMSK 0xffffffff -#define VIDC_REG_99105_ENC_CUR_Y_ADDR_SHFT 0 - -#define VIDC_REG_777113_ADDR_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000804) -#define VIDC_REG_777113_ADDR_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000804) -#define VIDC_REG_777113_ADDR_RMSK 0xffffffff -#define VIDC_REG_777113_ADDR_SHFT 0 -#define VIDC_REG_777113_ADDR_IN \ - in_dword_masked(VIDC_REG_777113_ADDR_ADDR, \ - VIDC_REG_777113_ADDR_RMSK) -#define VIDC_REG_777113_ADDR_INM(m) \ - in_dword_masked(VIDC_REG_777113_ADDR_ADDR, m) -#define VIDC_REG_777113_ADDR_OUT(v) \ - out_dword(VIDC_REG_777113_ADDR_ADDR, v) -#define VIDC_REG_777113_ADDR_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_777113_ADDR_ADDR, m, v, \ - VIDC_REG_777113_ADDR_IN); \ -} while (0) -#define VIDC_REG_777113_ADDR_ENC_CUR_C_ADDR_BMSK 0xffffffff -#define VIDC_REG_777113_ADDR_ENC_CUR_C_ADDR_SHFT 0 - -#define VIDC_REG_341928_ADDR_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x0000080c) -#define VIDC_REG_341928_ADDR_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000080c) -#define VIDC_REG_341928_ADDR_RMSK 0xffffffff -#define VIDC_REG_341928_ADDR_SHFT 0 -#define VIDC_REG_341928_ADDR_IN \ - in_dword_masked(VIDC_REG_341928_ADDR_ADDR, \ - VIDC_REG_341928_ADDR_RMSK) -#define VIDC_REG_341928_ADDR_INM(m) \ - in_dword_masked(VIDC_REG_341928_ADDR_ADDR, m) -#define VIDC_REG_341928_ADDR_OUT(v) \ - out_dword(VIDC_REG_341928_ADDR_ADDR, v) -#define VIDC_REG_341928_ADDR_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_341928_ADDR_ADDR, m, v, \ - VIDC_REG_341928_ADDR_IN); \ -} while (0) -#define VIDC_REG_341928_ADDR_ENC_DPB_ADR_BMSK 0xffffffff -#define VIDC_REG_341928_ADDR_ENC_DPB_ADR_SHFT 0 - -#define VIDC_REG_857491_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000810) -#define VIDC_REG_857491_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000810) -#define VIDC_REG_857491_RMSK 0xfff -#define VIDC_REG_857491_SHFT 0 -#define VIDC_REG_857491_IN \ - in_dword_masked(VIDC_REG_857491_ADDR, \ - VIDC_REG_857491_RMSK) -#define VIDC_REG_857491_INM(m) \ - in_dword_masked(VIDC_REG_857491_ADDR, m) -#define VIDC_REG_857491_OUT(v) \ - out_dword(VIDC_REG_857491_ADDR, v) -#define VIDC_REG_857491_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_857491_ADDR, m, v, \ - VIDC_REG_857491_IN); \ -} while (0) -#define VIDC_REG_857491_CIR_MB_NUM_BMSK 0xfff -#define VIDC_REG_857491_CIR_MB_NUM_SHFT 0 - -#define VIDC_REG_518133_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000900) -#define VIDC_REG_518133_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000900) -#define VIDC_REG_518133_RMSK 0xffffffff -#define VIDC_REG_518133_SHFT 0 -#define VIDC_REG_518133_IN \ - in_dword_masked(VIDC_REG_518133_ADDR, \ - VIDC_REG_518133_RMSK) -#define VIDC_REG_518133_INM(m) \ - in_dword_masked(VIDC_REG_518133_ADDR, m) -#define VIDC_REG_518133_OUT(v) \ - out_dword(VIDC_REG_518133_ADDR, v) -#define VIDC_REG_518133_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_518133_ADDR, m, v, \ - VIDC_REG_518133_IN); \ -} while (0) -#define VIDC_REG_518133_DEC_DPB_ADDR_BMSK 0xffffffff -#define VIDC_REG_518133_DEC_DPB_ADDR_SHFT 0 - -#define VIDC_REG_456376_ADDR_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000904) -#define VIDC_REG_456376_ADDR_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000904) -#define VIDC_REG_456376_ADDR_RMSK 0xffffffff -#define VIDC_REG_456376_ADDR_SHFT 0 -#define VIDC_REG_456376_ADDR_IN \ - in_dword_masked(VIDC_REG_456376_ADDR_ADDR, \ - VIDC_REG_456376_ADDR_RMSK) -#define VIDC_REG_456376_ADDR_INM(m) \ - in_dword_masked(VIDC_REG_456376_ADDR_ADDR, m) -#define VIDC_REG_456376_ADDR_OUT(v) \ - out_dword(VIDC_REG_456376_ADDR_ADDR, v) -#define VIDC_REG_456376_ADDR_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_456376_ADDR_ADDR, m, v, \ - VIDC_REG_456376_ADDR_IN); \ -} while (0) -#define VIDC_REG_456376_ADDR_DPB_COMV_ADDR_BMSK 0xffffffff -#define VIDC_REG_456376_ADDR_DPB_COMV_ADDR_SHFT 0 - -#define VIDC_REG_267567_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000908) -#define VIDC_REG_267567_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000908) -#define VIDC_REG_267567_RMSK 0xffffffff -#define VIDC_REG_267567_SHFT 0 -#define VIDC_REG_267567_IN \ - in_dword_masked(VIDC_REG_267567_ADDR, \ - VIDC_REG_267567_RMSK) -#define VIDC_REG_267567_INM(m) \ - in_dword_masked(VIDC_REG_267567_ADDR, m) -#define VIDC_REG_267567_OUT(v) \ - out_dword(VIDC_REG_267567_ADDR, v) -#define VIDC_REG_267567_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_267567_ADDR, m, v, \ - VIDC_REG_267567_IN); \ -} while (0) -#define VIDC_REG_798486_ADDR_BMSK 0xffffffff -#define VIDC_REG_798486_ADDR_SHFT 0 - -#define VIDC_REG_105770_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x0000090c) -#define VIDC_REG_105770_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x0000090c) -#define VIDC_REG_105770_RMSK 0xff -#define VIDC_REG_105770_SHFT 0 -#define VIDC_REG_105770_IN \ - in_dword_masked(VIDC_REG_105770_ADDR, \ - VIDC_REG_105770_RMSK) -#define VIDC_REG_105770_INM(m) \ - in_dword_masked(VIDC_REG_105770_ADDR, m) -#define VIDC_REG_105770_DPB_SIZE_BMSK 0xff -#define VIDC_REG_105770_DPB_SIZE_SHFT 0 - -#define VIDC_REG_58211_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000a00) -#define VIDC_REG_58211_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a00) -#define VIDC_REG_58211_RMSK 0x33f -#define VIDC_REG_58211_SHFT 0 -#define VIDC_REG_58211_IN \ - in_dword_masked(VIDC_REG_58211_ADDR, \ - VIDC_REG_58211_RMSK) -#define VIDC_REG_58211_INM(m) \ - in_dword_masked(VIDC_REG_58211_ADDR, m) -#define VIDC_REG_58211_OUT(v) \ - out_dword(VIDC_REG_58211_ADDR, v) -#define VIDC_REG_58211_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_58211_ADDR, m, v, \ - VIDC_REG_58211_IN); \ -} while (0) -#define VIDC_REG_58211_FR_RC_EN_BMSK 0x200 -#define VIDC_REG_58211_FR_RC_EN_SHFT 0x9 -#define VIDC_REG_58211_MB_RC_EN_BMSK 0x100 -#define VIDC_REG_58211_MB_RC_EN_SHFT 0x8 -#define VIDC_REG_58211_FRAME_QP_BMSK 0x3f -#define VIDC_REG_58211_FRAME_QP_SHFT 0 - -#define VIDC_REG_548359_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000a04) -#define VIDC_REG_548359_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a04) -#define VIDC_REG_548359_RMSK 0x3f -#define VIDC_REG_548359_SHFT 0 -#define VIDC_REG_548359_IN \ - in_dword_masked(VIDC_REG_548359_ADDR, \ - VIDC_REG_548359_RMSK) -#define VIDC_REG_548359_INM(m) \ - in_dword_masked(VIDC_REG_548359_ADDR, m) -#define VIDC_REG_548359_OUT(v) \ - out_dword(VIDC_REG_548359_ADDR, v) -#define VIDC_REG_548359_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_548359_ADDR, m, v, \ - VIDC_REG_548359_IN); \ -} while (0) -#define VIDC_REG_548359_P_FRAME_QP_BMSK 0x3f -#define VIDC_REG_548359_P_FRAME_QP_SHFT 0 - -#define VIDC_REG_174150_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000a08) -#define VIDC_REG_174150_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a08) -#define VIDC_REG_174150_RMSK 0xffffffff -#define VIDC_REG_174150_SHFT 0 -#define VIDC_REG_174150_IN \ - in_dword_masked(VIDC_REG_174150_ADDR, \ - VIDC_REG_174150_RMSK) -#define VIDC_REG_174150_INM(m) \ - in_dword_masked(VIDC_REG_174150_ADDR, m) -#define VIDC_REG_174150_OUT(v) \ - out_dword(VIDC_REG_174150_ADDR, v) -#define VIDC_REG_174150_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_174150_ADDR, m, v, \ - VIDC_REG_174150_IN); \ -} while (0) -#define VIDC_REG_174150_BIT_RATE_BMSK 0xffffffff -#define VIDC_REG_174150_BIT_RATE_SHFT 0 - -#define VIDC_REG_734318_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000a0c) -#define VIDC_REG_734318_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a0c) -#define VIDC_REG_734318_RMSK 0x3f3f -#define VIDC_REG_734318_SHFT 0 -#define VIDC_REG_734318_IN \ - in_dword_masked(VIDC_REG_734318_ADDR, \ - VIDC_REG_734318_RMSK) -#define VIDC_REG_734318_INM(m) \ - in_dword_masked(VIDC_REG_734318_ADDR, m) -#define VIDC_REG_734318_OUT(v) \ - out_dword(VIDC_REG_734318_ADDR, v) -#define VIDC_REG_734318_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_734318_ADDR, m, v, \ - VIDC_REG_734318_IN); \ -} while (0) -#define VIDC_REG_734318_MAX_QP_BMSK 0x3f00 -#define VIDC_REG_734318_MAX_QP_SHFT 0x8 -#define VIDC_REG_734318_MIN_QP_BMSK 0x3f -#define VIDC_REG_734318_MIN_QP_SHFT 0 - -#define VIDC_REG_677784_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000a10) -#define VIDC_REG_677784_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a10) -#define VIDC_REG_677784_RMSK 0xffff -#define VIDC_REG_677784_SHFT 0 -#define VIDC_REG_677784_IN \ - in_dword_masked(VIDC_REG_677784_ADDR, \ - VIDC_REG_677784_RMSK) -#define VIDC_REG_677784_INM(m) \ - in_dword_masked(VIDC_REG_677784_ADDR, m) -#define VIDC_REG_677784_OUT(v) \ - out_dword(VIDC_REG_677784_ADDR, v) -#define VIDC_REG_677784_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_677784_ADDR, m, v, \ - VIDC_REG_677784_IN); \ -} while (0) -#define VIDC_REG_677784_REACT_PARA_BMSK 0xffff -#define VIDC_REG_677784_REACT_PARA_SHFT 0 - -#define VIDC_REG_995041_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000a14) -#define VIDC_REG_995041_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a14) -#define VIDC_REG_995041_RMSK 0xf -#define VIDC_REG_995041_SHFT 0 -#define VIDC_REG_995041_IN \ - in_dword_masked(VIDC_REG_995041_ADDR, \ - VIDC_REG_995041_RMSK) -#define VIDC_REG_995041_INM(m) \ - in_dword_masked(VIDC_REG_995041_ADDR, m) -#define VIDC_REG_995041_OUT(v) \ - out_dword(VIDC_REG_995041_ADDR, v) -#define VIDC_REG_995041_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_995041_ADDR, m, v, \ - VIDC_REG_995041_IN); \ -} while (0) -#define VIDC_REG_995041_DARK_DISABLE_BMSK 0x8 -#define VIDC_REG_995041_DARK_DISABLE_SHFT 0x3 -#define VIDC_REG_995041_SMOOTH_DISABLE_BMSK 0x4 -#define VIDC_REG_995041_SMOOTH_DISABLE_SHFT 0x2 -#define VIDC_REG_995041_STATIC_DISABLE_BMSK 0x2 -#define VIDC_REG_995041_STATIC_DISABLE_SHFT 0x1 -#define VIDC_REG_995041_ACT_DISABLE_BMSK 0x1 -#define VIDC_REG_995041_ACT_DISABLE_SHFT 0 - -#define VIDC_REG_273649_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000a18) -#define VIDC_REG_273649_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000a18) -#define VIDC_REG_273649_RMSK 0x3f -#define VIDC_REG_273649_SHFT 0 -#define VIDC_REG_273649_IN \ - in_dword_masked(VIDC_REG_273649_ADDR, VIDC_REG_273649_RMSK) -#define VIDC_REG_273649_INM(m) \ - in_dword_masked(VIDC_REG_273649_ADDR, m) -#define VIDC_REG_273649_QP_OUT_BMSK 0x3f -#define VIDC_REG_273649_QP_OUT_SHFT 0 - -#define VIDC_REG_548823_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000b00) -#define VIDC_REG_548823_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000b00) -#define VIDC_REG_548823_RMSK 0xffffffff -#define VIDC_REG_548823_SHFT 0 -#define VIDC_REG_548823_IN \ - in_dword_masked(VIDC_REG_548823_ADDR, \ - VIDC_REG_548823_RMSK) -#define VIDC_REG_548823_INM(m) \ - in_dword_masked(VIDC_REG_548823_ADDR, m) -#define VIDC_REG_548823_720P_VERSION_BMSK 0xffffffff -#define VIDC_REG_548823_720P_VERSION_SHFT 0 - -#define VIDC_REG_881638_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000c00) -#define VIDC_REG_881638_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c00) -#define VIDC_REG_881638_RMSK 0xffffffff -#define VIDC_REG_881638_SHFT 0 -#define VIDC_REG_881638_IN \ - in_dword_masked(VIDC_REG_881638_ADDR, \ - VIDC_REG_881638_RMSK) -#define VIDC_REG_881638_INM(m) \ - in_dword_masked(VIDC_REG_881638_ADDR, m) -#define VIDC_REG_881638_CROP_RIGHT_OFFSET_BMSK 0xffff0000 -#define VIDC_REG_881638_CROP_RIGHT_OFFSET_SHFT 0x10 -#define VIDC_REG_881638_CROP_LEFT_OFFSET_BMSK 0xffff -#define VIDC_REG_881638_CROP_LEFT_OFFSET_SHFT 0 - -#define VIDC_REG_161486_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000c04) -#define VIDC_REG_161486_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c04) -#define VIDC_REG_161486_RMSK 0xffffffff -#define VIDC_REG_161486_SHFT 0 -#define VIDC_REG_161486_IN \ - in_dword_masked(VIDC_REG_161486_ADDR, \ - VIDC_REG_161486_RMSK) -#define VIDC_REG_161486_INM(m) \ - in_dword_masked(VIDC_REG_161486_ADDR, m) -#define VIDC_REG_161486_CROP_BOTTOM_OFFSET_BMSK 0xffff0000 -#define VIDC_REG_161486_CROP_BOTTOM_OFFSET_SHFT 0x10 -#define VIDC_REG_161486_CROP_TOP_OFFSET_BMSK 0xffff -#define VIDC_REG_161486_CROP_TOP_OFFSET_SHFT 0 - -#define VIDC_REG_580603_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000c08) -#define VIDC_REG_580603_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c08) -#define VIDC_REG_580603_RMSK 0xffffffff -#define VIDC_REG_580603_SHFT 0 -#define VIDC_REG_580603_IN \ - in_dword_masked(VIDC_REG_580603_ADDR, \ - VIDC_REG_580603_RMSK) -#define VIDC_REG_580603_INM(m) \ - in_dword_masked(VIDC_REG_580603_ADDR, m) -#define VIDC_REG_580603_720P_DEC_FRM_SIZE_BMSK 0xffffffff -#define VIDC_REG_580603_720P_DEC_FRM_SIZE_SHFT 0 - - -#define VIDC_REG_606447_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000c0c) -#define VIDC_REG_606447_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c0c) -#define VIDC_REG_606447_RMSK 0xff1f -#define VIDC_REG_606447_SHFT 0 -#define VIDC_REG_606447_IN \ - in_dword_masked(VIDC_REG_606447_ADDR, \ - VIDC_REG_606447_RMSK) -#define VIDC_REG_606447_INM(m) \ - in_dword_masked(VIDC_REG_606447_ADDR, m) -#define VIDC_REG_606447_OUT(v) \ - out_dword(VIDC_REG_606447_ADDR, v) -#define VIDC_REG_606447_OUTM(m, v) \ - out_dword_masked_ns(VIDC_REG_606447_ADDR, \ - m, v, VIDC_REG_606447_IN); \ - -#define VIDC_REG_606447_DIS_PIC_LEVEL_BMSK 0xff00 -#define VIDC_REG_606447_DIS_PIC_LEVEL_SHFT 0x8 -#define VIDC_REG_606447_DISP_PIC_PROFILE_BMSK 0x1f -#define VIDC_REG_606447_DISP_PIC_PROFILE_SHFT 0 - -#define VIDC_REG_854281_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000c10) -#define VIDC_REG_854281_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c10) -#define VIDC_REG_854281_RMSK 0xffffffff -#define VIDC_REG_854281_SHFT 0 -#define VIDC_REG_854281_IN \ - in_dword_masked(VIDC_REG_854281_ADDR, \ - VIDC_REG_854281_RMSK) -#define VIDC_REG_854281_INM(m) \ - in_dword_masked(VIDC_REG_854281_ADDR, m) -#define VIDC_REG_854281_MIN_DPB_SIZE_BMSK 0xffffffff -#define VIDC_REG_854281_MIN_DPB_SIZE_SHFT 0 - - -#define VIDC_REG_381535_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000c14) -#define VIDC_REG_381535_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c14) -#define VIDC_REG_381535_RMSK 0xffffffff -#define VIDC_REG_381535_SHFT 0 -#define VIDC_REG_381535_IN \ - in_dword_masked(VIDC_REG_381535_ADDR, \ - VIDC_REG_381535_RMSK) -#define VIDC_REG_381535_INM(m) \ - in_dword_masked(VIDC_REG_381535_ADDR, m) -#define VIDC_REG_381535_720P_FW_STATUS_BMSK 0xffffffff -#define VIDC_REG_381535_720P_FW_STATUS_SHFT 0 - - -#define VIDC_REG_347105_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000c18) -#define VIDC_REG_347105_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000c18) -#define VIDC_REG_347105_RMSK 0xffffffff -#define VIDC_REG_347105_SHFT 0 -#define VIDC_REG_347105_IN \ - in_dword_masked(VIDC_REG_347105_ADDR, \ - VIDC_REG_347105_RMSK) -#define VIDC_REG_347105_INM(m) \ - in_dword_masked(VIDC_REG_347105_ADDR, m) -#define VIDC_REG_347105_FREE_LUMA_DPB_BMSK 0xffffffff -#define VIDC_REG_347105_FREE_LUMA_DPB_SHFT 0 - - -#define VIDC_REG_62325_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000d00) -#define VIDC_REG_62325_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d00) -#define VIDC_REG_62325_RMSK 0xf -#define VIDC_REG_62325_SHFT 0 -#define VIDC_REG_62325_IN \ - in_dword_masked(VIDC_REG_62325_ADDR, \ - VIDC_REG_62325_RMSK) -#define VIDC_REG_62325_INM(m) \ - in_dword_masked(VIDC_REG_62325_ADDR, m) -#define VIDC_REG_62325_OUT(v) \ - out_dword(VIDC_REG_62325_ADDR, v) -#define VIDC_REG_62325_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_62325_ADDR, m, v, \ - VIDC_REG_62325_IN); \ -} while (0) -#define VIDC_REG_62325_COMMAND_TYPE_BMSK 0xf -#define VIDC_REG_62325_COMMAND_TYPE_SHFT 0 - -#define VIDC_REG_101184_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000d04) -#define VIDC_REG_101184_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d04) -#define VIDC_REG_101184_RMSK 0xffffffff -#define VIDC_REG_101184_SHFT 0 -#define VIDC_REG_101184_OUT(v) \ - out_dword(VIDC_REG_101184_ADDR, v) - -#define VIDC_REG_490443_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000d08) -#define VIDC_REG_490443_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d08) -#define VIDC_REG_490443_RMSK \ - 0xffffffff -#define \ - \ -VIDC_REG_490443_SHFT 0 -#define VIDC_REG_490443_OUT(v) \ - out_dword(VIDC_REG_490443_ADDR, v) - -#define VIDC_REG_625444_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000d14) -#define VIDC_REG_625444_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d14) -#define VIDC_REG_625444_RMSK 0xffffffff -#define VIDC_REG_625444_SHFT 0 -#define VIDC_REG_625444_IN \ - in_dword_masked(VIDC_REG_625444_ADDR, \ - VIDC_REG_625444_RMSK) -#define VIDC_REG_625444_INM(m) \ - in_dword_masked(VIDC_REG_625444_ADDR, m) -#define VIDC_REG_625444_OUT(v) \ - out_dword(VIDC_REG_625444_ADDR, v) -#define VIDC_REG_625444_OUTM(m, v) \ -do { \ - out_dword_masked_ns(VIDC_REG_625444_ADDR, m, v, \ - VIDC_REG_625444_IN); \ -} while (0) -#define VIDC_REG_625444_FRAME_RATE_BMSK 0xffffffff -#define VIDC_REG_625444_FRAME_RATE_SHFT 0 - -#define VIDC_REG_64895_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000e00) -#define VIDC_REG_64895_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e00) -#define VIDC_REG_64895_RMSK 0xffffffff -#define VIDC_REG_64895_SHFT 0 -#define VIDC_REG_64895_OUT(v) \ - out_dword(VIDC_REG_64895_ADDR, v) - -#define VIDC_REG_965480_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000e04) -#define VIDC_REG_965480_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e04) -#define VIDC_REG_965480_RMSK 0x1 -#define VIDC_REG_965480_SHFT 0 -#define VIDC_REG_965480_OUT(v) \ - out_dword(VIDC_REG_965480_ADDR, v) - -#define VIDC_REG_804959_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000e08) -#define VIDC_REG_804959_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e08) -#define VIDC_REG_804959_RMSK 0x7 -#define VIDC_REG_804959_SHFT 0 -#define VIDC_REG_804959_OUT(v) \ - out_dword(VIDC_REG_804959_ADDR, v) - -#define VIDC_REG_257463_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000e10) -#define VIDC_REG_257463_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e10) -#define VIDC_REG_257463_RMSK 0xffffffff -#define VIDC_REG_257463_SHFT 0 -#define VIDC_REG_257463_IN \ - in_dword_masked(VIDC_REG_257463_ADDR, \ - VIDC_REG_257463_RMSK) -#define VIDC_REG_257463_INM(m) \ - in_dword_masked(VIDC_REG_257463_ADDR, m) -#define VIDC_REG_257463_MIN_NUM_DPB_BMSK 0xffffffff -#define VIDC_REG_257463_MIN_NUM_DPB_SHFT 0 - -#define VIDC_REG_883500_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000e14) -#define VIDC_REG_883500_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e14) -#define VIDC_REG_883500_RMSK 0xffffffff -#define VIDC_REG_883500_SHFT 0 -#define VIDC_REG_883500_OUT(v) \ - out_dword(VIDC_REG_883500_ADDR, v) - -#define VIDC_REG_615716_ADDR(n) \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000e18 + 4 * (n)) -#define VIDC_REG_615716_PHYS(n) \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e18 + 4 * (n)) -#define VIDC_REG_615716_RMSK 0xffffffff -#define VIDC_REG_615716_SHFT 0 -#define VIDC_REG_615716_OUTI(n, v) \ - out_dword(VIDC_REG_615716_ADDR(n), v) - -#define VIDC_REG_603032_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000e98) -#define VIDC_REG_603032_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e98) -#define VIDC_REG_603032_RMSK 0xffffffff -#define VIDC_REG_603032_SHFT 0 -#define VIDC_REG_603032_OUT(v) \ - out_dword(VIDC_REG_603032_ADDR, v) - -#define VIDC_REG_300310_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000e9c) -#define VIDC_REG_300310_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000e9c) -#define VIDC_REG_300310_RMSK 0xffffffff -#define VIDC_REG_300310_SHFT 0 -#define VIDC_REG_300310_IN \ - in_dword_masked(VIDC_REG_300310_ADDR, \ - VIDC_REG_300310_RMSK) -#define VIDC_REG_300310_INM(m) \ - in_dword_masked(VIDC_REG_300310_ADDR, m) -#define VIDC_REG_300310_ERROR_STATUS_BMSK 0xffffffff -#define VIDC_REG_300310_ERROR_STATUS_SHFT 0 - -#define VIDC_REG_792026_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000ea0) -#define VIDC_REG_792026_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ea0) -#define VIDC_REG_792026_RMSK 0xffffffff -#define VIDC_REG_792026_SHFT 0 -#define VIDC_REG_792026_OUT(v) \ - out_dword(VIDC_REG_792026_ADDR, v) - -#define VIDC_REG_844152_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000ea4) -#define VIDC_REG_844152_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ea4) -#define VIDC_REG_844152_RMSK 0xffffffff -#define VIDC_REG_844152_SHFT 0 -#define VIDC_REG_844152_OUT(v) \ - out_dword(VIDC_REG_844152_ADDR, v) - -#define VIDC_REG_370409_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000ea8) -#define VIDC_REG_370409_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ea8) -#define VIDC_REG_370409_RMSK 0xffffffff -#define VIDC_REG_370409_SHFT 0 -#define VIDC_REG_370409_IN \ - in_dword_masked(VIDC_REG_370409_ADDR, \ - VIDC_REG_370409_RMSK) -#define VIDC_REG_370409_INM(m) \ - in_dword_masked(VIDC_REG_370409_ADDR, m) -#define VIDC_REG_370409_GET_FRAME_TAG_TOP_BMSK 0xffffffff -#define VIDC_REG_370409_GET_FRAME_TAG_TOP_SHFT 0 - -#define VIDC_REG_147682_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000eac) -#define VIDC_REG_147682_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000eac) -#define VIDC_REG_147682_RMSK 0x1 -#define VIDC_REG_147682_SHFT 0 -#define VIDC_REG_147682_OUT(v) \ - out_dword(VIDC_REG_147682_ADDR, v) - -#define VIDC_REG_407718_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000eb0) -#define VIDC_REG_407718_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000eb0) -#define VIDC_REG_407718_RMSK 0xffffffff -#define VIDC_REG_407718_SHFT 0 -#define VIDC_REG_407718_OUT(v) \ - out_dword(VIDC_REG_407718_ADDR, v) - -#define VIDC_REG_697961_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000eb4) -#define VIDC_REG_697961_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000eb4) -#define VIDC_REG_697961_RMSK 0x3 -#define VIDC_REG_697961_SHFT 0 -#define VIDC_REG_697961_IN \ - in_dword_masked(VIDC_REG_697961_ADDR, \ - VIDC_REG_697961_RMSK) -#define VIDC_REG_697961_INM(m) \ - in_dword_masked(VIDC_REG_697961_ADDR, m) -#define VIDC_REG_697961_FRAME_TYPE_BMSK 0x3 -#define VIDC_REG_697961_FRAME_TYPE_SHFT 0 - - -#define VIDC_REG_613254_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000eb8) -#define VIDC_REG_613254_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000eb8) -#define VIDC_REG_613254_RMSK 0x1 -#define VIDC_REG_613254_SHFT 0 -#define VIDC_REG_613254_IN \ - in_dword_masked(VIDC_REG_613254_ADDR, \ - VIDC_REG_613254_RMSK) -#define VIDC_REG_613254_INM(m) \ - in_dword_masked(VIDC_REG_613254_ADDR, m) -#define VIDC_REG_613254_METADATA_STATUS_BMSK 0x1 -#define VIDC_REG_613254_METADATA_STATUS_SHFT 0 -#define VIDC_REG_441270_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000ebc) -#define VIDC_REG_441270_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ebc) -#define VIDC_REG_441270_RMSK 0xf -#define VIDC_REG_441270_SHFT 0 -#define VIDC_REG_441270_IN \ - in_dword_masked(VIDC_REG_441270_ADDR, \ - VIDC_REG_441270_RMSK) -#define VIDC_REG_441270_INM(m) \ - in_dword_masked(VIDC_REG_441270_ADDR, m) -#define VIDC_REG_441270_DATA_PARTITIONED_BMSK 0x8 -#define VIDC_REG_441270_DATA_PARTITIONED_SHFT 0x3 - -#define VIDC_REG_441270_FRAME_TYPE_BMSK 0x7 -#define VIDC_REG_441270_FRAME_TYPE_SHFT 0 - -#define VIDC_REG_724381_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000ec0) -#define VIDC_REG_724381_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ec0) -#define VIDC_REG_724381_RMSK 0x3 -#define VIDC_REG_724381_SHFT 0 -#define VIDC_REG_724381_IN \ - in_dword_masked(VIDC_REG_724381_ADDR, \ - VIDC_REG_724381_RMSK) -#define VIDC_REG_724381_INM(m) \ - in_dword_masked(VIDC_REG_724381_ADDR, m) -#define VIDC_REG_724381_OPERATION_FAILED_BMSK 0x2 -#define VIDC_REG_724381_OPERATION_FAILED_SHFT 0x1 -#define VIDC_REG_724381_RESOLUTION_CHANGE_BMSK 0x1 -#define VIDC_REG_724381_RESOLUTION_CHANGE_SHFT 0 - -#define VIDC_REG_854681_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000ec4) -#define VIDC_REG_854681_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ec4) -#define VIDC_REG_854681_RMSK 0x7f -#define VIDC_REG_854681_SHFT 0 -#define VIDC_REG_854681_OUT(v) \ - out_dword(VIDC_REG_854681_ADDR, v) - -#define VIDC_REG_128234_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000ec8) -#define VIDC_REG_128234_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ec8) -#define VIDC_REG_128234_RMSK 0xffff000f -#define VIDC_REG_128234_SHFT 0 -#define VIDC_REG_128234_OUT(v) \ - out_dword(VIDC_REG_128234_ADDR, v) - -#define VIDC_REG_1137_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000ecc) -#define VIDC_REG_1137_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ecc) -#define VIDC_REG_1137_RMSK 0xffffffff -#define VIDC_REG_1137_SHFT 0 -#define VIDC_REG_1137_IN \ - in_dword_masked(VIDC_REG_1137_ADDR, \ - VIDC_REG_1137_RMSK) -#define VIDC_REG_1137_INM(m) \ - in_dword_masked(VIDC_REG_1137_ADDR, m) -#define VIDC_REG_1137_METADATA_DISPLAY_INDEX_BMSK \ - 0xffffffff -#define \ - \ -VIDC_REG_1137_METADATA_DISPLAY_INDEX_SHFT 0 - -#define VIDC_REG_988552_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000ed0) -#define VIDC_REG_988552_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ed0) -#define VIDC_REG_988552_RMSK 0xffffffff -#define VIDC_REG_988552_SHFT 0 -#define VIDC_REG_988552_OUT(v) \ - out_dword(VIDC_REG_988552_ADDR, v) - -#define VIDC_REG_319934_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000ed4) -#define VIDC_REG_319934_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ed4) -#define VIDC_REG_319934_RMSK 0xffffffff -#define VIDC_REG_319934_SHFT 0 -#define VIDC_REG_319934_OUT(v) \ - out_dword(VIDC_REG_319934_ADDR, v) - -#define VIDC_REG_679165_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000ed8) -#define VIDC_REG_679165_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ed8) -#define VIDC_REG_679165_RMSK 0xffffffff -#define VIDC_REG_679165_SHFT 0 -#define VIDC_REG_679165_IN \ - in_dword_masked(VIDC_REG_679165_ADDR, \ - VIDC_REG_679165_RMSK) -#define VIDC_REG_679165_INM(m) \ - in_dword_masked(VIDC_REG_679165_ADDR, m) -#define VIDC_REG_679165_PIC_TIME_TOP_BMSK 0xffffffff -#define VIDC_REG_679165_PIC_TIME_TOP_SHFT 0 - -#define VIDC_REG_374150_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000edc) -#define VIDC_REG_374150_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000edc) -#define VIDC_REG_374150_RMSK 0xffffffff -#define VIDC_REG_374150_SHFT 0 -#define VIDC_REG_374150_IN \ - in_dword_masked(VIDC_REG_374150_ADDR, \ - VIDC_REG_374150_RMSK) -#define VIDC_REG_374150_INM(m) \ - in_dword_masked(VIDC_REG_374150_ADDR, m) -#define VIDC_REG_374150_PIC_TIME_BOTTOM_BMSK 0xffffffff -#define VIDC_REG_374150_PIC_TIME_BOTTOM_SHFT 0 - -#define VIDC_REG_94750_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000ee0) -#define VIDC_REG_94750_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ee0) -#define VIDC_REG_94750_RMSK 0xffffffff -#define VIDC_REG_94750_SHFT 0 -#define VIDC_REG_94750_OUT(v) \ - out_dword(VIDC_REG_94750_ADDR, v) - -#define VIDC_REG_438677_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000ee4) -#define VIDC_REG_438677_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ee4) -#define VIDC_REG_438677_RMSK 0xffffffff -#define VIDC_REG_438677_SHFT 0 -#define VIDC_REG_438677_IN \ - in_dword_masked(VIDC_REG_438677_ADDR, \ - VIDC_REG_438677_RMSK) -#define VIDC_REG_438677_INM(m) \ - in_dword_masked(VIDC_REG_438677_ADDR, m) -#define VIDC_REG_438677_GET_FRAME_TAG_BOTTOM_BMSK 0xffffffff -#define VIDC_REG_438677_GET_FRAME_TAG_BOTTOM_SHFT 0 - -#define VIDC_REG_76706_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000ee8) -#define VIDC_REG_76706_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000ee8) -#define VIDC_REG_76706_RMSK 0x1 -#define VIDC_REG_76706_SHFT 0 -#define VIDC_REG_76706_OUT(v) \ - out_dword(VIDC_REG_76706_ADDR, v) - -#define VIDC_REG_809984_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00001000) -#define VIDC_REG_809984_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00001000) -#define VIDC_REG_809984_RMSK 0xffff0007 -#define VIDC_REG_809984_SHFT 0 -#define VIDC_REG_809984_IN \ - in_dword_masked(VIDC_REG_809984_ADDR, VIDC_REG_809984_RMSK) -#define VIDC_REG_809984_INM(m) \ - in_dword_masked(VIDC_REG_809984_ADDR, m) -#define VIDC_REG_809984_720PV_720P_WRAPPER_VERSION_BMSK 0xffff0000 -#define VIDC_REG_809984_720PV_720P_WRAPPER_VERSION_SHFT 0x10 -#define VIDC_REG_809984_TEST_MUX_SEL_BMSK 0x7 -#define VIDC_REG_809984_TEST_MUX_SEL_SHFT 0 - - -#define VIDC_REG_699747_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000d0c) -#define VIDC_REG_699747_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d0c) -#define VIDC_REG_699747_RMSK 0xffffffff -#define VIDC_REG_699747_SHFT 0 -#define VIDC_REG_699747_OUT(v) \ - out_dword(VIDC_REG_699747_ADDR, v) - -#define VIDC_REG_166247_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000d10) -#define VIDC_REG_166247_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d10) -#define VIDC_REG_166247_RMSK 0xffffffff -#define VIDC_REG_166247_SHFT 0 -#define VIDC_REG_166247_OUT(v) \ - out_dword(VIDC_REG_166247_ADDR, v) - -#define VIDC_REG_486169_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000d18) -#define VIDC_REG_486169_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d18) -#define VIDC_REG_486169_RMSK 0xffffffff -#define VIDC_REG_486169_SHFT 0 -#define VIDC_REG_486169_OUT(v) \ - out_dword(VIDC_REG_486169_ADDR, v) - -#define VIDC_REG_926519_ADDR \ - (VIDC_720P_WRAPPER_REG_BASE + 0x00000d1c) -#define VIDC_REG_926519_PHYS \ - (VIDC_720P_WRAPPER_REG_BASE_PHYS + 0x00000d1c) -#define VIDC_REG_926519_RMSK 0xffffffff -#define VIDC_REG_926519_SHFT 0 -#define VIDC_REG_926519_OUT(v) \ - out_dword(VIDC_REG_926519_ADDR, v) - -/** List all the levels and their register valus */ +/** List all the levels and their register values */ #define VIDC_720P_PROFILE_MPEG4_SP 0 #define VIDC_720P_PROFILE_MPEG4_ASP 1 @@ -2319,7 +177,7 @@ enum vidc_720p_display_status_type { VIDC_720P_DECODE_ONLY = 0, VIDC_720P_DECODE_AND_DISPLAY = 1, VIDC_720P_DISPLAY_ONLY = 2, - VIDC_720P_EMPTY_BUFFER = 3 + VIDC_720P_EMPTY_BUFFER = 3 }; #define VIDC_720P_ENC_IFRAME_REQ 0x1 @@ -2338,370 +196,200 @@ enum vidc_720p_display_status_type { #define VIDC_720P_METADATA_ENABLE_ENCSLICE 0x20 #define VIDC_720P_METADATA_ENABLE_PASSTHROUGH 0x40 -struct vidc_720p_dec_disp_info_type { - enum vidc_720p_display_status_type e_disp_status; - u32 n_resl_change; - u32 n_reconfig_flush_done; - u32 n_img_size_x; - u32 n_img_size_y; - u32 n_y_addr; - u32 n_c_addr; - u32 n_tag_top; - u32 n_pic_time_top; - u32 n_disp_is_interlace; - u32 n_tag_bottom; - u32 n_pic_time_bottom; - u32 n_metadata_exists; - u32 n_crop_exists; - u32 n_crop_right_offset; - u32 n_crop_left_offset; - u32 n_crop_bottom_offset; - u32 n_crop_top_offset; - u32 n_input_frame_type; - u32 n_input_bytes_consumed; - u32 n_input_is_interlace; - u32 n_input_frame_num; +struct vidc_720p_dec_disp_info { + enum vidc_720p_display_status_type disp_status; + u32 resl_change; + u32 reconfig_flush_done; + u32 img_size_x; + u32 img_size_y; + phys_addr_t y_addr; + phys_addr_t c_addr; + u32 tag_top; + u32 pic_time_top; + u32 disp_is_interlace; + u32 tag_bottom; + u32 pic_time_bottom; + u32 metadata_exists; + u32 crop_exists; + u32 crop_right_offset; + u32 crop_left_offset; + u32 crop_bottom_offset; + u32 crop_top_offset; + u32 input_frame_type; + u32 input_bytes_consumed; + u32 input_is_interlace; + u32 input_frame_num; }; struct vidc_720p_seq_hdr_info_type { - u32 n_img_size_x; - u32 n_img_size_y; - u32 n_dec_frm_size; - u32 n_min_num_dpb; - u32 n_min_dpb_size; - u32 n_profile; - u32 n_level; - u32 n_progressive; - u32 n_data_partitioned; - u32 n_crop_exists; - u32 n_crop_right_offset; - u32 n_crop_left_offset; - u32 n_crop_bottom_offset; - u32 n_crop_top_offset; + u32 img_size_x; + u32 img_size_y; + u32 dec_frm_size; + u32 min_num_dpb; + u32 min_dpb_size; + u32 profile; + u32 level; + u32 progressive; + u32 data_partitioned; + u32 crop_exists; + u32 crop_right_offset; + u32 crop_left_offset; + u32 crop_bottom_offset; + u32 crop_top_offset; }; -struct vidc_720p_enc_frame_info_type { - u32 n_enc_size; - u32 n_frame_type; - u32 n_metadata_exists; +struct vidc_720p_enc_frame_info { + u32 enc_size; + u32 frame_type; + u32 metadata_exists; }; -void vidc_720p_set_device_virtual_base(u8 *p_core_virtual_base_addr); +void vidc_720p_set_device_virtual_base(void *virt_addr); -void vidc_720p_init(char **ppsz_version, u32 i_firmware_size, - u32 *pi_firmware_address, enum vidc_720p_endian_type e_dma_endian, - u32 b_interrupt_off, - enum vidc_720p_interrupt_level_selection_type e_interrupt_sel, +void vidc_720p_init(char **ppsz_version, size_t sz, phys_addr_t phys_addr, + enum vidc_720p_endian_type dma_endian, u32 interrupt_off, + enum vidc_720p_interrupt_level_selection_type interrupt_sel, u32 interrupt_mask); u32 vidc_720p_do_sw_reset(void); u32 vidc_720p_reset_is_success(void); -void vidc_720p_start_cpu(enum vidc_720p_endian_type e_dma_endian, - u32 *p_icontext_bufferstart, u32 *p_debug_core_dump_addr, - u32 debug_buffer_size); +void vidc_720p_start_cpu(enum vidc_720p_endian_type dma_endian, + phys_addr_t icontext_bufferstart, phys_addr_t debug_core_dump_addr, + size_t debug_buffer_size); u32 vidc_720p_cpu_start(void); void vidc_720p_stop_fw(void); -void vidc_720p_get_interrupt_status(u32 *p_interrupt_status, - u32 *p_cmd_err_status, u32 *p_disp_pic_err_status, - u32 *p_op_failed); +void vidc_720p_get_interrupt_status(u32 *interrupt_status, u32 *cmd_err_status, + u32 *disp_pic_err_status, u32 *op_failed); void vidc_720p_interrupt_done_clear(void); -void vidc_720p_submit_command(u32 ch_id, u32 n_cmd_id); +void vidc_720p_submit_command(u32 ch_id, u32 cmd_id); -void vidc_720p_set_channel(u32 i_ch_id, - enum vidc_720p_enc_dec_selection_type e_enc_dec_sel, - enum vidc_720p_codec_type e_codec, u32 *pi_fw, u32 i_firmware_size); +void vidc_720p_set_channel(u32 ch_id, + enum vidc_720p_enc_dec_selection_type enc_dec_sel, + enum vidc_720p_codec_type codec, dma_addr_t pi_fw, + size_t firmware_size); -u32 vidc_720p_engine_reset(u32 n_ch_id, - enum vidc_720p_endian_type e_dma_endian, - enum vidc_720p_interrupt_level_selection_type e_interrupt_sel, - u32 interrupt_mask +u32 vidc_720p_engine_reset(u32 ch_id, + enum vidc_720p_endian_type dma_endian, + enum vidc_720p_interrupt_level_selection_type interrupt_sel, + u32 interrupt_mask ); -void vidc_720p_encode_set_profile(u32 i_profile, u32 i_level); +void vidc_720p_encode_set_profile(u32 profile, u32 level); -void vidc_720p_set_frame_size(u32 i_size_x, u32 i_size_y); +void vidc_720p_set_frame_size(u32 size_x, u32 size_y); -void vidc_720p_encode_set_fps(u32 i_rc_frame_rate); +void vidc_720p_encode_set_fps(u32 rc_frame_rate); -void vidc_720p_encode_set_vop_time(u32 n_vop_time_resolution, - u32 n_vop_time_increment); +void vidc_720p_encode_set_vop_time(u32 vop_time_resolution, + u32 vop_time_increment); -void vidc_720p_encode_set_hec_period(u32 n_hec_period); +void vidc_720p_encode_set_hec_period(u32 hec_period); -void vidc_720p_encode_set_short_header(u32 i_short_header); +void vidc_720p_encode_set_short_header(u32 short_header); -void vidc_720p_encode_set_qp_params(u32 i_max_qp, u32 i_min_qp); +void vidc_720p_encode_set_qp_params(u32 max_qp, u32 min_qp); -void vidc_720p_encode_set_rc_config(u32 b_enable_frame_level_rc, - u32 b_enable_mb_level_rc_flag, u32 i_frame_qp, u32 n_pframe_qp); +void vidc_720p_encode_set_rc_config(u32 enable_frame_level_rc, + u32 enable_mb_level_rc_flag, u32 iframe_qp, u32 pframe_qp); -void vidc_720p_encode_set_bit_rate(u32 i_target_bitrate); +void vidc_720p_encode_set_bit_rate(u32 target_bitrate); -void vidc_720p_encoder_set_param_change(u32 n_enc_param_change); +void vidc_720p_encoder_set_param_change(u32 enc_param_change); -void vidc_720p_encode_set_control_param(u32 n_param_val); +void vidc_720p_encode_set_control_param(u32 param_val); -void vidc_720p_encode_set_frame_level_rc_params(u32 i_reaction_coeff); +void vidc_720p_encode_set_frame_level_rc_params(u32 reaction_coeff); -void vidc_720p_encode_set_mb_level_rc_params(u32 b_dark_region_as_flag, - u32 b_smooth_region_as_flag, u32 b_static_region_as_flag, - u32 b_activity_region_flag); +void vidc_720p_encode_set_mb_level_rc_params(u32 dark_region_as_flag, + u32 smooth_region_as_flag, u32 static_region_as_flag, + u32 activity_region_flag); -void vidc_720p_encode_set_entropy_control(enum vidc_720p_entropy_sel_type \ - e_entropy_sel, - enum vidc_720p_cabac_model_type e_cabac_model_number); +void vidc_720p_encode_set_entropy_control(enum vidc_720p_entropy_sel_type + entropy_sel, enum vidc_720p_cabac_model_type cabac_model_number); void vidc_720p_encode_set_db_filter_control(enum vidc_720p_DBConfig_type - e_db_config, u32 i_slice_alpha_offset, u32 i_slice_beta_offset); + db_config, u32 slice_alpha_offset, u32 slice_beta_offset); -void vidc_720p_encode_set_intra_refresh_mb_number(u32 i_cir_mb_number); +void vidc_720p_encode_set_intra_refresh_mb_number(u32 cir_mb_number); -void vidc_720p_encode_set_multi_slice_info( - enum vidc_720p_MSlice_selection_type e_m_slice_sel, - u32 n_multi_slice_size); +void vidc_720p_encode_set_multi_slice_info(enum vidc_720p_MSlice_selection_type + m_slice_sel, u32 multi_slice_size); -void vidc_720p_encode_set_dpb_buffer(u32 *pi_enc_dpb_addr, u32 alloc_len); +void vidc_720p_encode_set_dpb_buffer(phys_addr_t pi_enc_dpb_addr, + size_t alloc_len); -void vidc_720p_set_deblock_line_buffer(u32 *pi_deblock_line_buffer_start, - u32 n_alloc_len); +void vidc_720p_set_deblock_line_buffer(phys_addr_t pi_deblock_line_buffer_start, + size_t alloc_len); -void vidc_720p_encode_set_i_period(u32 i_i_period); +void vidc_720p_encode_set_i_period(u32 period); -void vidc_720p_encode_init_codec(u32 i_ch_id, - enum vidc_720p_memory_access_method_type e_memory_access_model); +void vidc_720p_encode_init_codec(u32 ch_id, + enum vidc_720p_memory_access_method_type memory_access_model); -void vidc_720p_encode_unalign_bitstream(u32 n_upper_unalign_word, - u32 n_lower_unalign_word); +void vidc_720p_encode_unalign_bitstream(u32 upper_unalign_word, + u32 lower_unalign_word); -void vidc_720p_encode_set_seq_header_buffer(u32 n_ext_buffer_start, - u32 n_ext_buffer_end, u32 n_start_byte_num); +void vidc_720p_encode_set_seq_header_buffer(phys_addr_t ext_buffer_start, + phys_addr_t ext_buffer_end, u32 start_byte_num); -void vidc_720p_encode_frame(u32 n_ch_id, u32 n_ext_buffer_start, - u32 n_ext_buffer_end, u32 n_start_byte_number, - u32 n_y_addr, u32 n_c_addr); +void vidc_720p_encode_frame(u32 ch_id, phys_addr_t ext_buffer_start, + phys_addr_t ext_buffer_end, u32 start_byte_number, phys_addr_t y_addr, + phys_addr_t c_addr); void vidc_720p_encode_get_header(u32 *pi_enc_header_size); -void vidc_720p_enc_frame_info - (struct vidc_720p_enc_frame_info_type *p_enc_frame_info); +void vidc_720p_enc_frame_info(struct vidc_720p_enc_frame_info *enc_frame_info); -void vidc_720p_decode_bitstream_header(u32 n_ch_id, u32 n_dec_unit_size, - u32 n_start_byte_num, u32 n_ext_buffer_start, u32 n_ext_buffer_end, - enum vidc_720p_memory_access_method_type e_memory_access_model); +void vidc_720p_decode_bitstream_header(u32 ch_id, u32 dec_unit_size, + u32 start_byte_num, u32 ext_buffer_start, u32 ext_buffer_end, + enum vidc_720p_memory_access_method_type memory_access_model); -void vidc_720p_decode_get_seq_hdr_info - (struct vidc_720p_seq_hdr_info_type *p_seq_hdr_info); +void vidc_720p_decode_get_seq_hdr_info(struct vidc_720p_seq_hdr_info_type + *seq_hdr_info); -void vidc_720p_decode_set_dpb_release_buffer_mask - (u32 i_dpb_release_buffer_mask); +void vidc_720p_decode_set_dpb_release_buffer_mask(u32 dpb_release_buffer_mask); -void vidc_720p_decode_set_dpb_buffers(u32 i_buf_index, u32 *pi_dpb_buffer); +void vidc_720p_decode_set_dpb_buffers(u32 buf_index, phys_addr_t pi_dpb_buffer); -void vidc_720p_decode_set_comv_buffer - (u32 *pi_dpb_comv_buffer, u32 n_alloc_len); +void vidc_720p_decode_set_comv_buffer(dma_addr_t pi_dpb_comv_buffer, + size_t alloc_len); -void vidc_720p_decode_set_dpb_details - (u32 n_num_dpb, u32 n_alloc_len, u32 *p_ref_buffer); +void vidc_720p_decode_set_dpb_details(u32 num_dpb, size_t alloc_len, + phys_addr_t ref_buffer); -void vidc_720p_decode_set_mpeg4Post_filter(u32 b_enable_post_filter); +void vidc_720p_decode_set_mpeg4Post_filter(u32 enable_post_filter); -void vidc_720p_decode_set_error_control(u32 b_enable_error_control); +void vidc_720p_decode_set_error_control(u32 enable_error_control); -void vidc_720p_decode_set_mpeg4_data_partitionbuffer(u32 *p_vsp_buf_start); +void vidc_720p_decode_set_mpeg4_data_partitionbuffer(dma_addr_t vsp_buf_start); -void vidc_720p_decode_setH264VSPBuffer(u32 *pi_vsp_temp_buffer_start); +void vidc_720p_decode_setH264VSPBuffer(dma_addr_t pi_vsp_temp_buffer_start); -void vidc_720p_decode_frame(u32 n_ch_id, u32 n_ext_buffer_start, - u32 n_ext_buffer_end, u32 n_dec_unit_size, - u32 n_start_byte_num, u32 n_input_frame_tag); +void vidc_720p_decode_frame(u32 ch_id, phys_addr_t ext_buffer_start, + phys_addr_t ext_buffer_end, size_t dec_unit_size, u32 start_byte_num, + u32 input_frame_tag); -void vidc_720p_issue_eos(u32 i_ch_id); -void vidc_720p_eos_info(u32 *p_disp_status); +void vidc_720p_issue_eos(u32 ch_id); +void vidc_720p_eos_info(u32 *disp_status); -void vidc_720p_decode_display_info - (struct vidc_720p_dec_disp_info_type *p_disp_info); +void vidc_720p_decode_display_info(struct vidc_720p_dec_disp_info *disp_info); -void vidc_720p_decode_skip_frm_details(u32 *p_free_luma_dpb); +void vidc_720p_decode_skip_frm_details(phys_addr_t *free_luma_dpb); -void vidc_720p_metadata_enable(u32 n_flag, u32 *p_input_buffer); +void vidc_720p_metadata_enable(u32 flag, phys_addr_t input_buffer); void vidc_720p_decode_dynamic_req_reset(void); -void vidc_720p_decode_dynamic_req_set(u32 n_property); - -void vidc_720p_decode_setpassthrough_start(u32 n_pass_startaddr); - - - -#define DDL_720P_REG_BASE VIDC_720P_WRAPPER_REG_BASE -#define VIDC_BUSY_WAIT(n) udelay(n) - -#undef VIDC_REGISTER_LOG_MSG -#undef VIDC_REGISTER_LOG_INTO_BUFFER - -#ifdef VIDC_REGISTER_LOG_MSG -#define VIDC_MSG1(msg_format, a) printk(KERN_INFO msg_format, a) -#define VIDC_MSG2(msg_format, a, b) printk(KERN_INFO msg_format, a, b) -#define VIDC_MSG3(msg_format, a, b, c) printk(KERN_INFO msg_format, a, b, c) -#else -#define VIDC_MSG1(msg_format, a) -#define VIDC_MSG2(msg_format, a, b) -#define VIDC_MSG3(msg_format, a, b, c) -#endif - -#ifdef VIDC_REGISTER_LOG_INTO_BUFFER - -#define VIDC_REGLOG_BUFSIZE 200000 -#define VIDC_REGLOG_MAX_PRINT_SIZE 100 -extern char vidclog[VIDC_REGLOG_BUFSIZE]; -extern unsigned int vidclog_index; - -#define VIDC_LOG_BUFFER_INIT \ -{if (vidclog_index) \ - memset(vidclog, 0, vidclog_index+1); \ - vidclog_index = 0; } - -#define VIDC_REGLOG_CHECK_BUFINDEX(req_size) \ - vidclog_index = \ - (vidclog_index+(req_size) < VIDC_REGLOG_BUFSIZE) ? vidclog_index : 0; - -#define VIDC_LOG_WRITE(reg, val) \ -{unsigned int len; \ - VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \ - len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \ - "(0x%x:"#reg"=0x%x)" , VIDC_##reg##_ADDR - DDL_720P_REG_BASE, val);\ - vidclog_index += len; } - -#define VIDC_LOG_WRITEI(reg, index, val) \ -{unsigned int len; \ - VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \ - len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \ - "(0x%x:"#reg"=0x%x)" , VIDC_##reg##_ADDR(index)-DDL_720P_REG_BASE, \ - val); vidclog_index += len; } - -#define VIDC_LOG_WRITEF(reg, field, val) \ -{unsigned int len; \ - VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \ - len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \ - "(0x%x:"#reg":0x%x:=0x%x)" , VIDC_##reg##_ADDR - DDL_720P_REG_BASE, \ - VIDC_##reg##_##field##_BMSK, val);\ - vidclog_index += len; } - -#define VIDC_LOG_READ(reg, pval) \ -{ unsigned int len; \ - VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \ - len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \ - "(0x%x:"#reg"==0x%x)" , VIDC_##reg##_ADDR - DDL_720P_REG_BASE, \ - (u32)*pval); \ - vidclog_index += len; } - -#define VIDC_STR_LOGBUFFER(str) \ -{ unsigned int len; \ - VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \ - len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \ - "<%s>" , str); vidclog_index += len; } - -#define VIDC_LONG_LOGBUFFER(str, arg1) \ -{ unsigned int len; \ - VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \ - len = snprintf(&vidclog[vidclog_index], VIDC_REGLOG_MAX_PRINT_SIZE, \ - "<%s=0x%x>" , str, arg1); vidclog_index += len; } - -#define VIDC_DEBUG_REGISTER_LOG \ -{ u32 val; unsigned int len; \ - val = VIDC_720P_IN(REG_881638); \ - VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \ - len = snprintf(&vidclog[vidclog_index], 50, "[dbg1=%x]" , val); \ - vidclog_index += len; \ - val = VIDC_720P_IN(REG_161486); \ - VIDC_REGLOG_CHECK_BUFINDEX(VIDC_REGLOG_MAX_PRINT_SIZE); \ - len = snprintf(&vidclog[vidclog_index], 50, "[dbg2=%x]" , val); \ - vidclog_index += len; } - -#else -#define VIDC_LOG_WRITE(reg, val) -#define VIDC_LOG_WRITEI(reg, index, val) -#define VIDC_LOG_WRITEF(reg, field, val) -#define VIDC_LOG_READ(reg, pval) -#define VIDC_LOG_BUFFER_INIT -#define VIDC_STR_LOGBUFFER(str) -#define VIDC_LONG_LOGBUFFER(str, arg1) -#define VIDC_DEBUG_REGISTER_LOG -#endif +void vidc_720p_decode_dynamic_req_set(u32 property); -void vidcputlog(char *str); -void vidcput_debug_reglog(void); - -#define VIDC_LOGERR_STRING(str) \ -do { \ - VIDC_STR_LOGBUFFER(str); \ - VIDC_MSG1("\n<%s>", str); \ -} while (0) - -#define VIDC_LOG_STRING(str) \ -do { \ - VIDC_STR_LOGBUFFER(str); \ - VIDC_MSG1("\n<%s>", str); \ -} while (0) - -#define VIDC_LOG1(str, arg1) \ -do { \ - VIDC_LONG_LOGBUFFER(str, arg1); \ - VIDC_MSG2("\n<%s=0x%08x>", str, arg1); \ -} while (0) - -#define VIDC_IO_OUT(reg, val) \ -do { \ - VIDC_LOG_WRITE(reg, (u32)val); \ - VIDC_MSG2("\n(0x%08x:"#reg"=0x%08x)", \ - (u32)(VIDC_##reg##_ADDR - DDL_720P_REG_BASE), (u32)val); \ - mb(); \ - VIDC_720P_OUT(reg, val); \ -} while (0) - -#define VIDC_IO_OUTI(reg, index, val) \ -do { \ - VIDC_LOG_WRITEI(reg, index, (u32)val); \ - VIDC_MSG2("\n(0x%08x:"#reg"=0x%08x)", \ - (u32)(VIDC_##reg##_ADDR(index)-DDL_720P_REG_BASE), (u32)val); \ - mb(); \ - VIDC_720P_OUTI(reg, index, val); \ -} while (0) - -#define VIDC_IO_OUTF(reg, field, val) \ -do { \ - VIDC_LOG_WRITEF(reg, field, val); \ - VIDC_MSG3("\n(0x%08x:"#reg":0x%x:=0x%08x)", \ - (u32)(VIDC_##reg##_ADDR - DDL_720P_REG_BASE), \ - VIDC_##reg##_##field##_BMSK, (u32)val); \ - mb(); \ - VIDC_720P_OUTF(reg, field, val); \ -} while (0) - -#define VIDC_IO_IN(reg, pval) \ -do { \ - mb(); \ - *pval = (u32) VIDC_720P_IN(reg); \ - VIDC_LOG_READ(reg, pval); \ - VIDC_MSG2("\n(0x%08x:"#reg"==0x%08x)", \ - (u32)(VIDC_##reg##_ADDR - DDL_720P_REG_BASE), (u32) *pval); \ -} while (0) - -#define VIDC_IO_INF(reg, mask, pval) \ -do { \ - mb(); \ - *pval = VIDC_720P_INF(reg, mask); \ - VIDC_LOG_READ(reg, pval); \ - VIDC_MSG2("\n(0x%08x:"#reg"==0x%08x)", \ - (u32)(VIDC_##reg##_ADDR - DDL_720P_REG_BASE), *pval); \ -} while (0) +void vidc_720p_decode_setpassthrough_start(phys_addr_t pass_startaddr); #endif diff --git a/drivers/misc/video_core/720p/dec/vdec.c b/drivers/misc/video_core/720p/dec/vdec.c index 3468fbafb629d..36ca5563b7407 100644 --- a/drivers/misc/video_core/720p/dec/vdec.c +++ b/drivers/misc/video_core/720p/dec/vdec.c @@ -34,6 +34,7 @@ #include #include +#include "vcd_ddl_firmware.h" #include "video_core_type.h" #include "vcd_api.h" #include "vdec_internal.h" @@ -51,29 +52,28 @@ #define INFO(x...) printk(KERN_INFO x) #define ERR(x...) printk(KERN_ERR x) -#define VID_DEC_NAME "msm_vidc_dec" +#define VID_DEC_NAME "msm_vidc_dec" -static struct vid_dec_dev *vid_dec_device_p; -static dev_t vid_dec_dev_num; -static struct class *vid_dec_class; +static struct vid_dec_dev *vidc_dec_dev; +static dev_t vidc_dec_dev_num; +static struct class *vidc_dec_class; static s32 vid_dec_get_empty_client_index(void) { - u32 i, found = FALSE; + u32 i, found = false; for (i = 0; i < VID_DEC_MAX_DECODER_CLIENTS; i++) { - if (!vid_dec_device_p->vdec_clients[i].vcd_handle) { - found = TRUE; + if (!vidc_dec_dev->vdec_clients[i].vcd_handle) { + found = true; break; } } if (!found) { ERR("%s():ERROR No space for new client\n", __func__); return -1; - } else { - DBG("%s(): available client index = %u\n", __func__, i); - return i; } + DBG("%s(): available client index = %u\n", __func__, i); + return i; } u32 vid_dec_get_status(u32 status) @@ -128,37 +128,36 @@ static void vid_dec_notify_client(struct video_client_ctx *client_ctx) } void vid_dec_vcd_open_done(struct video_client_ctx *client_ctx, - struct vcd_handle_container_type *handle_container) + struct vcd_handle_container *handle_container) { DBG("vid_dec_vcd_open_done\n"); - if (client_ctx) { - if (handle_container) - client_ctx->vcd_handle = handle_container->handle; - else - ERR("%s(): ERROR. handle_container is NULL\n", - __func__); - - vid_dec_notify_client(client_ctx); - } else + if (!client_ctx) { ERR("%s(): ERROR. client_ctx is NULL\n", __func__); + return; + } + + if (handle_container) + client_ctx->vcd_handle = handle_container->handle; + else + ERR("%s(): ERROR. handle_container is NULL\n", __func__); + + vid_dec_notify_client(client_ctx); } static void vid_dec_input_frame_done(struct video_client_ctx *client_ctx, - u32 event, u32 status, - struct vcd_frame_data_type *vcd_frame_data) + u32 event, u32 status, struct vcd_frame_data *vcd_frame_data) { struct vid_dec_msg *vdec_msg; if (!client_ctx || !vcd_frame_data) { - ERR("vid_dec_input_frame_done() NULL pointer \n"); + ERR("vid_dec_input_frame_done() NULL pointer\n"); return; } vdec_msg = kzalloc(sizeof(struct vid_dec_msg), GFP_KERNEL); if (!vdec_msg) { - ERR("vid_dec_input_frame_done(): cannot allocate vid_dec_msg " - " buffer\n"); + ERR("%s: cannot allocate vid_dec_msg buffer\n", __func__); return; } @@ -166,19 +165,19 @@ static void vid_dec_input_frame_done(struct video_client_ctx *client_ctx, if (event == VCD_EVT_RESP_INPUT_DONE) { vdec_msg->vdec_msg_info.msgcode = - VDEC_MSG_RESP_INPUT_BUFFER_DONE; + VDEC_MSG_RESP_INPUT_BUFFER_DONE; DBG("Send INPUT_DON message to client = %p\n", client_ctx); } else if (event == VCD_EVT_RESP_INPUT_FLUSHED) { vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_INPUT_FLUSHED; DBG("Send INPUT_FLUSHED message to client = %p\n", client_ctx); } else { - ERR("vid_dec_input_frame_done(): invalid event type\n"); + ERR("%s: invalid event type\n", __func__); return; } vdec_msg->vdec_msg_info.msgdata.input_frame_clientdata = - (void *)vcd_frame_data->n_frm_clnt_data; + vcd_frame_data->client_data; vdec_msg->vdec_msg_info.msgdatasize = sizeof(void *); mutex_lock(&client_ctx->msg_queue_lock); @@ -188,86 +187,72 @@ static void vid_dec_input_frame_done(struct video_client_ctx *client_ctx, } static void vid_dec_output_frame_done(struct video_client_ctx *client_ctx, - u32 event, u32 status, - struct vcd_frame_data_type *vcd_frame_data) + u32 event, u32 status, struct vcd_frame_data *vcd_frame_data) { struct vid_dec_msg *vdec_msg; - unsigned long kernel_vaddr, phy_addr, user_vaddr; + void __user *user_addr; + void *kern_addr; + phys_addr_t phys_addr; int pmem_fd; struct file *file; s32 buffer_index = -1; + struct vdec_output_frameinfo *frm; if (!client_ctx || !vcd_frame_data) { - ERR("vid_dec_input_frame_done() NULL pointer \n"); + ERR("%s: NULL pointer\n", __func__); return; } vdec_msg = kzalloc(sizeof(struct vid_dec_msg), GFP_KERNEL); if (!vdec_msg) { - ERR("vid_dec_input_frame_done(): cannot allocate vid_dec_msg " - " buffer\n"); + ERR("%s: cannot allocate vid_dec_msg buffer\n", __func__); return; } vdec_msg->vdec_msg_info.status_code = vid_dec_get_status(status); - if (event == VCD_EVT_RESP_OUTPUT_DONE) + if (event == VCD_EVT_RESP_OUTPUT_DONE) { vdec_msg->vdec_msg_info.msgcode = - VDEC_MSG_RESP_OUTPUT_BUFFER_DONE; - - else if (event == VCD_EVT_RESP_OUTPUT_FLUSHED) + VDEC_MSG_RESP_OUTPUT_BUFFER_DONE; + } else if (event == VCD_EVT_RESP_OUTPUT_FLUSHED) { vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_OUTPUT_FLUSHED; - else { - ERR("QVD: vid_dec_output_frame_done invalid cmd type \n"); + } else { + ERR("QVD: vid_dec_output_frame_done invalid cmd type\n"); return; } - kernel_vaddr = (unsigned long)vcd_frame_data->p_virtual; - - if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, - FALSE, &user_vaddr, &kernel_vaddr, - &phy_addr, &pmem_fd, &file, - &buffer_index)) { - - /* Buffer address in user space */ - vdec_msg->vdec_msg_info.msgdata.output_frame.bufferaddr = - (u8 *) user_vaddr; - /* Buffer address in user space */ - vdec_msg->vdec_msg_info.msgdata.output_frame.phy_addr = - vcd_frame_data->p_physical; - /* Data length */ - vdec_msg->vdec_msg_info.msgdata.output_frame.len = - vcd_frame_data->n_data_len; - vdec_msg->vdec_msg_info.msgdata.output_frame.flags = - vcd_frame_data->n_flags; - /* Timestamp pass-through from input frame */ - vdec_msg->vdec_msg_info.msgdata.output_frame.time_stamp = - vcd_frame_data->time_stamp; - /* Output frame client data */ - vdec_msg->vdec_msg_info.msgdata.output_frame.client_data = - (void *)vcd_frame_data->n_frm_clnt_data; - /* Associated input frame client data */ - vdec_msg->vdec_msg_info.msgdata.output_frame. - input_frame_clientdata = - (void *)vcd_frame_data->n_ip_frm_tag; - /* Decoded picture width and height */ - vdec_msg->vdec_msg_info.msgdata.output_frame.framesize. - n_bottom = - vcd_frame_data->dec_op_prop.disp_frm.n_bottom; - vdec_msg->vdec_msg_info.msgdata.output_frame.framesize.n_left = - vcd_frame_data->dec_op_prop.disp_frm.n_left; - vdec_msg->vdec_msg_info.msgdata.output_frame.framesize.n_right = - vcd_frame_data->dec_op_prop.disp_frm.n_right; - vdec_msg->vdec_msg_info.msgdata.output_frame.framesize.n_top = - vcd_frame_data->dec_op_prop.disp_frm.n_top; - vdec_msg->vdec_msg_info.msgdatasize = - sizeof(struct vdec_output_frameinfo); - } else { + kern_addr = vcd_frame_data->virt_addr; + + if (!vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, false, + &user_addr, &kern_addr, &phys_addr, &pmem_fd, &file, + &buffer_index)) { ERR("vid_dec_output_frame_done UVA can not be found\n"); vdec_msg->vdec_msg_info.status_code = VDEC_S_EFATAL; + goto out; } + frm = &vdec_msg->vdec_msg_info.msgdata.output_frame; + /* Buffer address in user space */ + frm->user_addr = user_addr; + frm->phys_addr = vcd_frame_data->phys_addr; + /* Data length */ + frm->len = vcd_frame_data->data_len; + frm->flags = vcd_frame_data->flags; + /* timestamp pass-through from input frame */ + frm->time_stamp = vcd_frame_data->time_stamp; + /* Output frame client data */ + frm->client_data = vcd_frame_data->client_data; + /* Associated input frame client data */ + frm->input_frame_clientdata = (void *)vcd_frame_data->ip_frm_tag; + /* Decoded picture width and height */ + frm->framesize.bottom = vcd_frame_data->dec_op_prop.disp_frm.bottom; + frm->framesize.left = vcd_frame_data->dec_op_prop.disp_frm.left; + frm->framesize.right = vcd_frame_data->dec_op_prop.disp_frm.right; + frm->framesize.top = vcd_frame_data->dec_op_prop.disp_frm.top; + vdec_msg->vdec_msg_info.msgdatasize = sizeof(*frm); + +out: mutex_lock(&client_ctx->msg_queue_lock); list_add_tail(&vdec_msg->list, &client_ctx->msg_queue); mutex_unlock(&client_ctx->msg_queue_lock); @@ -275,12 +260,12 @@ static void vid_dec_output_frame_done(struct video_client_ctx *client_ctx, } static void vid_dec_lean_event(struct video_client_ctx *client_ctx, - u32 event, u32 status) + u32 event, u32 status) { struct vid_dec_msg *vdec_msg; if (!client_ctx) { - ERR("%s(): !client_ctx pointer \n", __func__); + ERR("%s(): !client_ctx pointer\n", __func__); return; } @@ -294,49 +279,48 @@ static void vid_dec_lean_event(struct video_client_ctx *client_ctx, switch (event) { case VCD_EVT_IND_RECONFIG: - INFO("\n msm_vidc_dec: Sending VDEC_MSG_EVT_CONFIG_CHANGED" - " to client"); + INFO("msm_vidc_dec: Sending VDEC_MSG_EVT_CONFIG_CHANGED" + " to client\n"); vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_EVT_CONFIG_CHANGED; break; case VCD_EVT_IND_RESOURCES_LOST: - INFO("\n msm_vidc_dec: Sending VDEC_EVT_RESOURCES_LOST" - " to client"); + INFO("msm_vidc_dec: Sending VDEC_EVT_RESOURCES_LOST" + " to client\n"); vdec_msg->vdec_msg_info.msgcode = VDEC_EVT_RESOURCES_LOST; break; case VCD_EVT_RESP_FLUSH_INPUT_DONE: - INFO("\n msm_vidc_dec: Sending VDEC_MSG_RESP_FLUSH_INPUT_DONE" - " to client"); + INFO("msm_vidc_dec: Sending VDEC_MSG_RESP_FLUSH_INPUT_DONE" + " to client\n"); vdec_msg->vdec_msg_info.msgcode = - VDEC_MSG_RESP_FLUSH_INPUT_DONE; + VDEC_MSG_RESP_FLUSH_INPUT_DONE; break; case VCD_EVT_RESP_FLUSH_OUTPUT_DONE: - INFO("\n msm_vidc_dec: Sending VDEC_MSG_RESP_FLUSH_OUTPUT_DONE" - " to client"); + INFO("msm_vidc_dec: Sending VDEC_MSG_RESP_FLUSH_OUTPUT_DONE" + " to client\n"); vdec_msg->vdec_msg_info.msgcode = - VDEC_MSG_RESP_FLUSH_OUTPUT_DONE; + VDEC_MSG_RESP_FLUSH_OUTPUT_DONE; break; case VCD_EVT_IND_HWERRFATAL: - INFO("\n msm_vidc_dec: Sending VDEC_MSG_EVT_HW_ERROR" - " to client"); + INFO("msm_vidc_dec: Sending VDEC_MSG_EVT_HW_ERROR to client\n"); vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_EVT_HW_ERROR; break; case VCD_EVT_RESP_START: - INFO("\n msm_vidc_dec: Sending VDEC_MSG_RESP_START_DONE" - " to client"); + INFO("msm_vidc_dec: Sending VDEC_MSG_RESP_START_DONE" + " to client\n"); vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_START_DONE; break; case VCD_EVT_RESP_STOP: - INFO("\n msm_vidc_dec: Sending VDEC_MSG_RESP_STOP_DONE" - " to client"); + INFO("msm_vidc_dec: Sending VDEC_MSG_RESP_STOP_DONE" + " to client\n"); vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_STOP_DONE; break; case VCD_EVT_RESP_PAUSE: - INFO("\n msm_vidc_dec: Sending VDEC_MSG_RESP_PAUSE_DONE" - " to client"); + INFO("msm_vidc_dec: Sending VDEC_MSG_RESP_PAUSE_DONE" + " to client\n"); vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_PAUSE_DONE; break; default: - ERR("%s() : unknown event type \n", __func__); + ERR("%s() : unknown event type\n", __func__); break; } @@ -348,16 +332,16 @@ static void vid_dec_lean_event(struct video_client_ctx *client_ctx, } -void vid_dec_vcd_cb(u32 event, u32 status, - void *info, u32 size, void *handle, void *const client_data) +void vid_dec_vcd_cb(u32 event, u32 status, void *info, u32 size, void *handle, + void *const client_data) { - struct video_client_ctx *client_ctx = - (struct video_client_ctx *)client_data; + struct video_client_ctx *client_ctx = (struct video_client_ctx *) + client_data; DBG("Entering %s()\n", __func__); if (!client_ctx) { - ERR("%s(): client_ctx is NULL \n", __func__); + ERR("%s(): client_ctx is NULL\n", __func__); return; } @@ -365,19 +349,15 @@ void vid_dec_vcd_cb(u32 event, u32 status, switch (event) { case VCD_EVT_RESP_OPEN: - vid_dec_vcd_open_done(client_ctx, - (struct vcd_handle_container_type *) - info); + vid_dec_vcd_open_done(client_ctx, info); break; case VCD_EVT_RESP_INPUT_DONE: case VCD_EVT_RESP_INPUT_FLUSHED: - vid_dec_input_frame_done(client_ctx, event, status, - (struct vcd_frame_data_type *)info); + vid_dec_input_frame_done(client_ctx, event, status, info); break; case VCD_EVT_RESP_OUTPUT_DONE: case VCD_EVT_RESP_OUTPUT_FLUSHED: - vid_dec_output_frame_done(client_ctx, event, status, - (struct vcd_frame_data_type *)info); + vid_dec_output_frame_done(client_ctx, event, status, info); break; case VCD_EVT_RESP_PAUSE: case VCD_EVT_RESP_STOP: @@ -395,302 +375,290 @@ void vid_dec_vcd_cb(u32 event, u32 status, vid_dec_notify_client(client_ctx); break; default: - ERR("%s() : Error - Invalid event type =%u\n", __func__, - event); + ERR("%s(): Error - Invalid event type %u\n", __func__, event); break; } } static u32 vid_dec_set_codec(struct video_client_ctx *client_ctx, - enum vdec_codec *vdec_codec_type) + enum vdec_codec *vdec_codec_type) { - u32 result = TRUE; - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_codec_type codec_type; + u32 result = true; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_codec codec_type; u32 vcd_status = VCD_ERR_FAIL; if (!client_ctx || !vdec_codec_type) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_CODEC; - vcd_property_hdr.n_size = sizeof(struct vcd_property_codec_type); + vcd_property_hdr.id = VCD_I_CODEC; + vcd_property_hdr.sz = sizeof(struct vcd_property_codec); switch (*vdec_codec_type) { case VDEC_CODECTYPE_MPEG4: - codec_type.e_codec = VCD_CODEC_MPEG4; + codec_type.codec = VCD_CODEC_MPEG4; break; case VDEC_CODECTYPE_H264: - codec_type.e_codec = VCD_CODEC_H264; + codec_type.codec = VCD_CODEC_H264; break; case VDEC_CODECTYPE_DIVX_3: - codec_type.e_codec = VCD_CODEC_DIVX_3; + codec_type.codec = VCD_CODEC_DIVX_3; break; case VDEC_CODECTYPE_XVID: - codec_type.e_codec = VCD_CODEC_XVID; + codec_type.codec = VCD_CODEC_XVID; break; case VDEC_CODECTYPE_H263: - codec_type.e_codec = VCD_CODEC_H263; + codec_type.codec = VCD_CODEC_H263; break; case VDEC_CODECTYPE_MPEG2: - codec_type.e_codec = VCD_CODEC_MPEG2; + codec_type.codec = VCD_CODEC_MPEG2; break; case VDEC_CODECTYPE_VC1: - codec_type.e_codec = VCD_CODEC_VC1; + codec_type.codec = VCD_CODEC_VC1; break; default: - result = FALSE; + result = false; break; } if (result) { vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, &codec_type); + &vcd_property_hdr, &codec_type); if (vcd_status) - result = FALSE; + result = false; } return result; } static u32 vid_dec_set_output_format(struct video_client_ctx *client_ctx, - enum vdec_output_fromat *output_format) + enum vdec_output_format *output_format) { - u32 result = TRUE; - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_buffer_format_type vcd_prop_buffer_format; + u32 result = true; + struct vcd_property_hdr prop_hdr; + struct vcd_property_buffer_format buffer_format; u32 vcd_status = VCD_ERR_FAIL; if (!client_ctx || !output_format) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_BUFFER_FORMAT;; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_buffer_format_type); + prop_hdr.id = VCD_I_BUFFER_FORMAT;; + prop_hdr.sz = sizeof(struct vcd_property_buffer_format); switch (*output_format) { case VDEC_YUV_FORMAT_NV12: - vcd_prop_buffer_format.e_buffer_format = VCD_BUFFER_FORMAT_NV12; + buffer_format.buffer_format = VCD_BUFFER_FORMAT_NV12; break; case VDEC_YUV_FORMAT_TILE_4x2: - vcd_prop_buffer_format.e_buffer_format = - VCD_BUFFER_FORMAT_TILE_4x2; + buffer_format.buffer_format = VCD_BUFFER_FORMAT_TILE_4x2; break; default: - result = FALSE; + result = false; break; } - if (result) - vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, - &vcd_prop_buffer_format); + if (!result) + return false; + vcd_status = vcd_set_property(client_ctx->vcd_handle, &prop_hdr, + &buffer_format); + + //TODO fix false/true silliness if (vcd_status) - return FALSE; + return false; else - return TRUE; + return true; } static u32 vid_dec_set_frame_resolution(struct video_client_ctx *client_ctx, - struct vdec_picsize *video_resoultion) + struct vdec_picsize *video_resoultion) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_frame_size_type frame_resolution; + struct vcd_property_hdr prop_hdr; + struct vcd_property_frame_size res; u32 vcd_status = VCD_ERR_FAIL; if (!client_ctx || !video_resoultion) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_FRAME_SIZE; - vcd_property_hdr.n_size = sizeof(struct vcd_property_frame_size_type); - frame_resolution.n_width = video_resoultion->frame_width; - frame_resolution.n_height = video_resoultion->frame_height; + prop_hdr.id = VCD_I_FRAME_SIZE; + prop_hdr.sz = sizeof(struct vcd_property_frame_size); + res.width = video_resoultion->frame_width; + res.height = video_resoultion->frame_height; - vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, &frame_resolution); + vcd_status = vcd_set_property(client_ctx->vcd_handle, &prop_hdr, &res); if (vcd_status) - return FALSE; + return false; else - return TRUE; + return true; } static u32 vid_dec_get_frame_resolution(struct video_client_ctx *client_ctx, - struct vdec_picsize *video_resoultion) + struct vdec_picsize *video_res) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_frame_size_type frame_resolution; + struct vcd_property_hdr prop_hdr; + struct vcd_property_frame_size frame_res; u32 vcd_status = VCD_ERR_FAIL; - if (!client_ctx || !video_resoultion) - return FALSE; + if (!client_ctx || !video_res) + return false; - vcd_property_hdr.prop_id = VCD_I_FRAME_SIZE; - vcd_property_hdr.n_size = sizeof(struct vcd_property_frame_size_type); + prop_hdr.id = VCD_I_FRAME_SIZE; + prop_hdr.sz = sizeof(struct vcd_property_frame_size); - vcd_status = vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr, - &frame_resolution); + vcd_status = vcd_get_property(client_ctx->vcd_handle, &prop_hdr, + &frame_res); - video_resoultion->frame_width = frame_resolution.n_width; - video_resoultion->frame_height = frame_resolution.n_height; - video_resoultion->scan_lines = frame_resolution.n_scan_lines; - video_resoultion->stride = frame_resolution.n_stride; + video_res->frame_width = frame_res.width; + video_res->frame_height = frame_res.height; + video_res->scan_lines = frame_res.scan_lines; + video_res->stride = frame_res.stride; if (vcd_status) - return FALSE; + return false; else - return TRUE; + return true; } static u32 vid_dec_get_buffer_req(struct video_client_ctx *client_ctx, - struct vdec_allocatorproperty *vdec_buf_req) + struct vdec_allocatorproperty *vdec_buf_req) { u32 vcd_status = VCD_ERR_FAIL; - struct vcd_buffer_requirement_type vcd_buf_req; + struct vcd_buffer_requirement vcd_buf_req; if (!client_ctx || !vdec_buf_req) - return FALSE; + return false; if (vdec_buf_req->buffer_type == VDEC_BUFFER_TYPE_INPUT) { vcd_status = vcd_get_buffer_requirements(client_ctx->vcd_handle, - VCD_BUFFER_INPUT, - &vcd_buf_req); + VCD_BUFFER_INPUT, &vcd_buf_req); } else { vcd_status = vcd_get_buffer_requirements(client_ctx->vcd_handle, - VCD_BUFFER_OUTPUT, - &vcd_buf_req); + VCD_BUFFER_OUTPUT, &vcd_buf_req); } - if (vcd_status) { - return FALSE; - } else { - vdec_buf_req->mincount = vcd_buf_req.n_min_count; - vdec_buf_req->maxcount = vcd_buf_req.n_max_count; - vdec_buf_req->actualcount = vcd_buf_req.n_actual_count; - vdec_buf_req->buffer_size = vcd_buf_req.n_size; - vdec_buf_req->alignment = vcd_buf_req.n_align; - vdec_buf_req->buf_poolid = vcd_buf_req.n_buf_pool_id; + if (vcd_status) + return false; - return TRUE; - } + vdec_buf_req->mincount = vcd_buf_req.min_count; + vdec_buf_req->maxcount = vcd_buf_req.max_count; + vdec_buf_req->actualcount = vcd_buf_req.actual_count; + vdec_buf_req->buffer_size = vcd_buf_req.size; + vdec_buf_req->alignment = vcd_buf_req.align; + vdec_buf_req->buf_poolid = vcd_buf_req.buf_pool_id; + + return true; } static u32 vid_dec_set_buffer(struct video_client_ctx *client_ctx, - struct vdec_setbuffer_cmd *buffer_info) + struct vdec_setbuffer_cmd *b_info) { enum vcd_buffer_type buffer_type; enum buffer_dir dir_buffer = BUFFER_TYPE_INPUT; u32 vcd_status = VCD_ERR_FAIL; - unsigned long user_vaddr, kernel_vaddr, phy_addr, len; + + void __user *user_addr; + void *kern_addr; + phys_addr_t phys_addr; + unsigned long len; int pmem_fd; struct file *file; - struct buf_addr_table *buf_addr_table; + struct buf_addr_table *addr_table; s32 buffer_index = -1; - if (!client_ctx || !buffer_info) - return FALSE; + if (!client_ctx || !b_info) + return false; - user_vaddr = (unsigned long)buffer_info->buffer.bufferaddr; + user_addr = b_info->buffer.addr; - if (buffer_info->buffer_type == VDEC_BUFFER_TYPE_OUTPUT) + if (b_info->buffer_type == VDEC_BUFFER_TYPE_OUTPUT) dir_buffer = BUFFER_TYPE_OUTPUT; - /*If buffer already set, ignore */ - if (vid_c_lookup_addr_table(client_ctx, dir_buffer, - TRUE, &user_vaddr, &kernel_vaddr, - &phy_addr, &pmem_fd, &file, - &buffer_index)) { - DBG("%s() : user_virt_addr = 0x%08lx is alreday set.", - __func__, user_vaddr); - return TRUE; + /* if buffer already set, ignore */ + if (vid_c_lookup_addr_table(client_ctx, dir_buffer, true, &user_addr, + &kern_addr, &phys_addr, &pmem_fd, &file, + &buffer_index)) { + DBG("%s: user_addr = %p is already set\n", __func__, user_addr); + return true; } - if (get_pmem_file(buffer_info->buffer.pmem_fd, - &phy_addr, &kernel_vaddr, &len, &file)) { - ERR("%s(): get_pmem_file failed\n", __func__); - return FALSE; + if (get_pmem_file(b_info->buffer.pmem_fd, (unsigned long *)&phys_addr, + (unsigned long *)&kern_addr, &len, &file)) { + ERR("%s: get_pmem_file failed\n", __func__); + return false; } put_pmem_file(file); - if (buffer_info->buffer_type == VDEC_BUFFER_TYPE_INPUT) { + if (b_info->buffer_type == VDEC_BUFFER_TYPE_INPUT) { buffer_type = VCD_BUFFER_INPUT; client_ctx->num_of_input_buffers++; - if (client_ctx->num_of_input_buffers > - MAX_VIDEO_NUM_OF_BUFF) { + if (client_ctx->num_of_input_buffers > MAX_VIDEO_NUM_OF_BUFF) { ERR("%s(): num_of_input_buffers reached max value" - " MAX_VIDEO_NUM_OF_BUFF \n", __func__); + " MAX_VIDEO_NUM_OF_BUFF\n", __func__); client_ctx->num_of_input_buffers--; - return FALSE; + return false; } buffer_index = client_ctx->num_of_input_buffers - 1; - buf_addr_table = - &client_ctx->input_buf_addr_table[buffer_index]; - buf_addr_table->user_vaddr = - (unsigned long)buffer_info->buffer.bufferaddr; - buf_addr_table->kernel_vaddr = kernel_vaddr; - buf_addr_table->phy_addr = phy_addr; - buf_addr_table->pmem_fd = buffer_info->buffer.pmem_fd; - buf_addr_table->file = file; + addr_table = &client_ctx->input_buf_addr_table[buffer_index]; + addr_table->user_addr = b_info->buffer.addr; + addr_table->kern_addr = kern_addr; + addr_table->phys_addr = phys_addr; + addr_table->pmem_fd = b_info->buffer.pmem_fd; + addr_table->file = file; } else { buffer_type = VCD_BUFFER_OUTPUT; client_ctx->num_of_output_buffers++; - if (client_ctx->num_of_output_buffers > - MAX_VIDEO_NUM_OF_BUFF) { + if (client_ctx->num_of_output_buffers > MAX_VIDEO_NUM_OF_BUFF) { ERR("%s(): num_of_outut_buffers reached max value" - " MAX_VIDEO_NUM_OF_BUFF \n", __func__); + " MAX_VIDEO_NUM_OF_BUFF\n", __func__); client_ctx->num_of_output_buffers--; - return FALSE; + return false; } buffer_index = client_ctx->num_of_output_buffers - 1; - buf_addr_table = - &client_ctx->output_buf_addr_table[buffer_index]; - kernel_vaddr += (unsigned long)buffer_info->buffer.offset; - phy_addr += (unsigned long)buffer_info->buffer.offset; - buf_addr_table->user_vaddr = - (unsigned long)buffer_info->buffer.bufferaddr; - buf_addr_table->kernel_vaddr = kernel_vaddr; - buf_addr_table->phy_addr = phy_addr; - buf_addr_table->pmem_fd = buffer_info->buffer.pmem_fd; - buf_addr_table->file = file; - } - - vcd_status = vcd_set_buffer(client_ctx->vcd_handle, - buffer_type, (u8 *) kernel_vaddr, - buffer_info->buffer.buffer_len); + addr_table = &client_ctx->output_buf_addr_table[buffer_index]; + kern_addr = (u8 *)kern_addr + b_info->buffer.offset; + phys_addr += b_info->buffer.offset; + addr_table->user_addr = b_info->buffer.addr; + addr_table->kern_addr = kern_addr; + addr_table->phys_addr = phys_addr; + addr_table->pmem_fd = b_info->buffer.pmem_fd; + addr_table->file = file; + } + + vcd_status = vcd_set_buffer(client_ctx->vcd_handle, buffer_type, + kern_addr, b_info->buffer.sz); if (!vcd_status) - return TRUE; + return true; else - return FALSE; + return false; } - static u32 vid_dec_free_buffer(struct video_client_ctx *client_ctx, - struct vdec_setbuffer_cmd *buffer_info) + struct vdec_setbuffer_cmd *buffer_info) { enum vcd_buffer_type buffer_type; enum buffer_dir dir_buffer = BUFFER_TYPE_INPUT; u32 vcd_status = VCD_ERR_FAIL; - unsigned long user_vaddr, kernel_vaddr, phy_addr; + void __user *user_addr; + void *kern_addr; + phys_addr_t phys_addr; int pmem_fd; struct file *file; s32 buffer_index = -1; if (!client_ctx || !buffer_info) - return FALSE; + return false; - user_vaddr = (unsigned long)buffer_info->buffer.bufferaddr; + user_addr = buffer_info->buffer.addr; if (buffer_info->buffer_type == VDEC_BUFFER_TYPE_OUTPUT) dir_buffer = BUFFER_TYPE_OUTPUT; /*If buffer already set, ignore */ - if (!vid_c_lookup_addr_table(client_ctx, dir_buffer, - TRUE, &user_vaddr, &kernel_vaddr, - &phy_addr, &pmem_fd, &file, - &buffer_index)) { - - DBG("%s() : user_virt_addr = 0x%08lx is alreday set.", - __func__, user_vaddr); - return TRUE; + if (!vid_c_lookup_addr_table(client_ctx, dir_buffer, true, &user_addr, + &kern_addr, &phys_addr, &pmem_fd, &file, + &buffer_index)) { + DBG("%s: user_addr = %p is already set\n", __func__, user_addr); + return true; } if (buffer_info->buffer_type == VDEC_BUFFER_TYPE_INPUT) @@ -698,203 +666,194 @@ static u32 vid_dec_free_buffer(struct video_client_ctx *client_ctx, else buffer_type = VCD_BUFFER_OUTPUT; vcd_status = vcd_free_buffer(client_ctx->vcd_handle, buffer_type, - (u8 *)kernel_vaddr); + kern_addr); if (!vcd_status) - return TRUE; + return true; else - return FALSE; + return false; } static u32 vid_dec_pause_resume(struct video_client_ctx *client_ctx, u32 pause) { - u32 vcd_status; + u32 vcd_status; if (!client_ctx) { - ERR("\n %s(): Invalid client_ctx", __func__); - return FALSE; + ERR("%s: Invalid client_ctx\n", __func__); + return false; } if (pause) { - INFO("\n msm_vidc_dec: PAUSE command from client = %p\n", + INFO("msm_vidc_dec: PAUSE command from client = %p\n", client_ctx); vcd_status = vcd_pause(client_ctx->vcd_handle); - } else{ - INFO("\n msm_vidc_dec: RESUME command from client = %p\n", + } else { + INFO("msm_vidc_dec: RESUME command from client = %p\n", client_ctx); vcd_status = vcd_resume(client_ctx->vcd_handle); } if (vcd_status) - return FALSE; + return false; - return TRUE; + return true; } - -static u32 vid_dec_start_stop(struct video_client_ctx *client_ctx, u32 start) +static u32 vid_dec_start(struct video_client_ctx *client_ctx) { struct vid_dec_msg *vdec_msg = NULL; u32 vcd_status; - INFO("\n msm_vidc_dec: Inside %s()", __func__); + INFO("msm_vidc_dec: Inside %s\n", __func__); if (!client_ctx) { ERR("\n Invalid client_ctx"); - return FALSE; - } - - if (start) { - if (client_ctx->seq_header_set) { - INFO("\n %s(): Seq Hdr set: Send START_DONE to client", - __func__); - vdec_msg = kzalloc(sizeof(*vdec_msg), GFP_KERNEL); - if (!vdec_msg) { - ERR("vid_dec_start_stop: cannot allocate" - "buffer\n"); - return FALSE; - } - vdec_msg->vdec_msg_info.msgcode = - VDEC_MSG_RESP_START_DONE; - vdec_msg->vdec_msg_info.status_code = VDEC_S_SUCCESS; - vdec_msg->vdec_msg_info.msgdatasize = 0; - mutex_lock(&client_ctx->msg_queue_lock); - list_add_tail(&vdec_msg->list, &client_ctx->msg_queue); - mutex_unlock(&client_ctx->msg_queue_lock); - - wake_up(&client_ctx->msg_wait); - - DBG("Send START_DONE message to client = %p\n", - client_ctx); - - } else { - INFO("\n %s(): Calling decode_start()", __func__); - vcd_status = - vcd_decode_start(client_ctx->vcd_handle, NULL); - - if (vcd_status) { - ERR("%s(): vcd_decode_start failed." - " vcd_status = %u\n", __func__, vcd_status); - return FALSE; - } - } - } else { - INFO("\n %s(): Calling vcd_stop()", __func__); - vcd_status = vcd_stop(client_ctx->vcd_handle); - if (vcd_status) { + return false; + } - ERR("%s(): vcd_stop failed. vcd_status = %u\n", - __func__, vcd_status); - return FALSE; + if (!client_ctx->seq_header_set) { + INFO("%s: Calling decode_start()\n", __func__); + vcd_status = vcd_decode_start(client_ctx->vcd_handle, NULL); + + if (vcd_status) { + ERR("%s: vcd_decode_start failed vcd_status = %u\n", + __func__, vcd_status); + return false; } - DBG("Send STOP_DONE message to client = %p\n", client_ctx); + return true; + } + + INFO("%s(): Seq Hdr set: Send START_DONE to client\n", __func__); + vdec_msg = kzalloc(sizeof(*vdec_msg), GFP_KERNEL); + if (!vdec_msg) { + ERR("%s: cannot allocate buffer\n", __func__); + return false; } - return TRUE; + vdec_msg->vdec_msg_info.msgcode = VDEC_MSG_RESP_START_DONE; + vdec_msg->vdec_msg_info.status_code = VDEC_S_SUCCESS; + vdec_msg->vdec_msg_info.msgdatasize = 0; + mutex_lock(&client_ctx->msg_queue_lock); + list_add_tail(&vdec_msg->list, &client_ctx->msg_queue); + mutex_unlock(&client_ctx->msg_queue_lock); + + wake_up(&client_ctx->msg_wait); + + DBG("Send START_DONE message to client = %p\n", client_ctx); + + return true; +} + +static u32 vid_dec_stop(struct video_client_ctx *client_ctx) +{ + u32 vcd_status; + + INFO("msm_vidc_dec: Inside %s\n", __func__); + if (!client_ctx) { + ERR("Invalid client_ctx\n"); + return false; + } + + INFO("%s: Calling vcd_stop()\n", __func__); + vcd_status = vcd_stop(client_ctx->vcd_handle); + if (vcd_status) { + ERR("%s: vcd_stop failed %u\n", __func__, vcd_status); + return false; + } + DBG("Send STOP_DONE message to client = %p\n", client_ctx); + return true; } static u32 vid_dec_decode_frame(struct video_client_ctx *client_ctx, - struct vdec_input_frameinfo *input_frame_info) + struct vdec_input_frameinfo *frm_info) { - struct vcd_frame_data_type vcd_input_buffer; - unsigned long kernel_vaddr, phy_addr, user_vaddr; + struct vcd_frame_data vcd_input_buffer; + void *kern_addr; + void __user *user_addr; + phys_addr_t phys_addr; int pmem_fd; struct file *file; s32 buffer_index = -1; u32 vcd_status = VCD_ERR_FAIL; - if (!client_ctx || !input_frame_info) - return FALSE; - - user_vaddr = (unsigned long)input_frame_info->bufferaddr; - - if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_INPUT, - TRUE, &user_vaddr, &kernel_vaddr, - &phy_addr, &pmem_fd, &file, - &buffer_index)) { - - /* kernel_vaddr is found. send the frame to VCD */ - memset((void *)&vcd_input_buffer, 0, - sizeof(struct vcd_frame_data_type)); - vcd_input_buffer.p_virtual = - (u8 *) (kernel_vaddr + input_frame_info->pmem_offset); - vcd_input_buffer.n_offset = input_frame_info->offset; - vcd_input_buffer.n_frm_clnt_data = - (u32) input_frame_info->client_data; - vcd_input_buffer.n_ip_frm_tag = - (u32) input_frame_info->client_data; - vcd_input_buffer.n_data_len = input_frame_info->datalen; - vcd_input_buffer.time_stamp = input_frame_info->timestamp; - /* Rely on VCD using the same flags as OMX */ - vcd_input_buffer.n_flags = input_frame_info->flags; - - vcd_status = vcd_decode_frame(client_ctx->vcd_handle, - &vcd_input_buffer); - if (!vcd_status) - return TRUE; - else { - ERR("%s(): vcd_decode_frame failed = %u\n", __func__, - vcd_status); - return FALSE; - } + if (!client_ctx || !frm_info) + return false; - } else { - ERR("%s(): kernel_vaddr not found\n", __func__); - return FALSE; + user_addr = frm_info->user_addr; + + if (!vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_INPUT, true, + &user_addr, &kern_addr, &phys_addr, &pmem_fd, &file, + &buffer_index)) { + ERR("%s: kern_addr not found\n", __func__); + return false; } + + /* kernel_vaddr is found. send the frame to VCD */ + memset((void *)&vcd_input_buffer, 0, sizeof(vcd_input_buffer)); + vcd_input_buffer.virt_addr = (u8 *)kern_addr + frm_info->pmem_offset; + vcd_input_buffer.offset = frm_info->offset; + vcd_input_buffer.client_data = frm_info->client_data; + vcd_input_buffer.ip_frm_tag = (u32)frm_info->client_data; + vcd_input_buffer.data_len = frm_info->data_len; + vcd_input_buffer.time_stamp = frm_info->timestamp; + /* Rely on VCD using the same flags as OMX */ + vcd_input_buffer.flags = frm_info->flags; + + vcd_status = vcd_decode_frame(client_ctx->vcd_handle, + &vcd_input_buffer); + + if (vcd_status) { + ERR("%s: vcd_decode_frame failed = %u\n", __func__, vcd_status); + return false; + } + return true; } static u32 vid_dec_fill_output_buffer(struct video_client_ctx *client_ctx, - struct vdec_fillbuffer_cmd *fill_buffer_cmd) + struct vdec_fillbuffer_cmd *fill_buffer_cmd) { - unsigned long kernel_vaddr, phy_addr, user_vaddr; + void *kern_addr; + void __user *user_addr; + phys_addr_t phys_addr; int pmem_fd; struct file *file; s32 buffer_index = -1; u32 vcd_status = VCD_ERR_FAIL; - - struct vcd_frame_data_type vcd_frame; + struct vcd_frame_data vcd_frame; if (!client_ctx || !fill_buffer_cmd) - return FALSE; - - user_vaddr = (unsigned long)fill_buffer_cmd->buffer.bufferaddr; - - if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, - TRUE, &user_vaddr, &kernel_vaddr, - &phy_addr, &pmem_fd, &file, - &buffer_index)) { - - memset((void *)&vcd_frame, 0, - sizeof(struct vcd_frame_data_type)); - vcd_frame.p_virtual = (u8 *) kernel_vaddr; - vcd_frame.n_frm_clnt_data = (u32) fill_buffer_cmd->client_data; - vcd_frame.n_alloc_len = fill_buffer_cmd->buffer.buffer_len; - - vcd_status = vcd_fill_output_buffer(client_ctx->vcd_handle, - &vcd_frame); - if (!vcd_status) - return TRUE; - else { - ERR("%s(): vcd_fill_output_buffer failed = %u\n", - __func__, vcd_status); - return FALSE; - } - } else { - ERR("%s(): kernel_vaddr not found\n", __func__); - return FALSE; + return false; + + user_addr = fill_buffer_cmd->buffer.addr; + + if (!vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, true, + &user_addr, &kern_addr, &phys_addr, &pmem_fd, &file, + &buffer_index)) { + ERR("%s: kern_addr not found\n", __func__); + return false; } -} + memset((void *)&vcd_frame, 0, sizeof(vcd_frame)); + vcd_frame.virt_addr = kern_addr; + vcd_frame.client_data = fill_buffer_cmd->client_data; + vcd_frame.alloc_len = fill_buffer_cmd->buffer.sz; + + vcd_status = vcd_fill_output_buffer(client_ctx->vcd_handle, &vcd_frame); + if (vcd_status) { + ERR("%s: vcd_fill_output_buffer failed = %u\n", __func__, + vcd_status); + return false; + } + return true; +} static u32 vid_dec_flush(struct video_client_ctx *client_ctx, - enum vdec_bufferflush flush_dir) + enum vdec_bufferflush flush_dir) { u32 vcd_status = VCD_ERR_FAIL; - INFO("\n msm_vidc_dec: %s() called with dir = %u", __func__, - flush_dir); + INFO("msm_vidc_dec: %s called with dir = %u\n", __func__, flush_dir); if (!client_ctx) { - ERR("\n Invalid client_ctx"); - return FALSE; + ERR("Invalid client_ctx\n"); + return false; } switch (flush_dir) { @@ -903,25 +862,23 @@ static u32 vid_dec_flush(struct video_client_ctx *client_ctx, break; case VDEC_FLUSH_TYPE_OUTPUT: vcd_status = vcd_flush(client_ctx->vcd_handle, - VCD_FLUSH_OUTPUT); + VCD_FLUSH_OUTPUT); break; case VDEC_FLUSH_TYPE_ALL: vcd_status = vcd_flush(client_ctx->vcd_handle, VCD_FLUSH_ALL); break; default: - ERR("%s(): Inavlid flush cmd. flush_dir = %u\n", __func__, - flush_dir); - return FALSE; - break; + ERR("%s: Invalid flush cmd. flush_dir = %u\n", __func__, + flush_dir); + return false; } - if (!vcd_status) - return TRUE; - else { - ERR("%s(): vcd_flush failed. vcd_status = %u " - " flush_dir = %u\n", __func__, vcd_status, flush_dir); - return FALSE; + if (vcd_status) { + ERR("%s: vcd_flush failed. vcd_status = %u flush_dir = %u\n", + __func__, vcd_status, flush_dir); + return false; } + return true; } static u32 vid_dec_msg_pending(struct video_client_ctx *client_ctx) @@ -932,71 +889,59 @@ static u32 vid_dec_msg_pending(struct video_client_ctx *client_ctx) mutex_unlock(&client_ctx->msg_queue_lock); if (islist_empty) { - DBG("%s(): vid_dec msg queue empty\n", __func__); + DBG("%s: vid_dec msg queue empty\n", __func__); if (client_ctx->stop_msg) { - DBG("%s(): List empty and Stop Msg set\n", - __func__); + DBG("%s: List empty and Stop Msg set\n", __func__); return client_ctx->stop_msg; } - } else - DBG("%s(): vid_dec msg queue Not empty\n", __func__); + } else { + DBG("%s: vid_dec msg queue Not empty\n", __func__); + } return !islist_empty; } static u32 vid_dec_get_next_msg(struct video_client_ctx *client_ctx, - struct vdec_msginfo *vdec_msg_info) + struct vdec_msginfo *vdec_msg_info) { int rc; struct vid_dec_msg *vid_dec_msg = NULL; if (!client_ctx) - return FALSE; + return false; rc = wait_event_interruptible(client_ctx->msg_wait, - vid_dec_msg_pending(client_ctx)); + vid_dec_msg_pending(client_ctx)); if (rc < 0 || client_ctx->stop_msg) { - DBG("rc = %d, stop_msg = %u \n", rc, client_ctx->stop_msg); - return FALSE; + DBG("rc = %d, stop_msg = %u\n", rc, client_ctx->stop_msg); + return false; } mutex_lock(&client_ctx->msg_queue_lock); if (!list_empty(&client_ctx->msg_queue)) { - DBG("%s(): After Wait \n", __func__); + DBG("%s(): After Wait\n", __func__); vid_dec_msg = list_first_entry(&client_ctx->msg_queue, - struct vid_dec_msg, list); + struct vid_dec_msg, list); list_del(&vid_dec_msg->list); memcpy(vdec_msg_info, &vid_dec_msg->vdec_msg_info, sizeof(struct vdec_msginfo)); kfree(vid_dec_msg); } mutex_unlock(&client_ctx->msg_queue_lock); - return TRUE; + return true; } static int vid_dec_ioctl(struct inode *inode, struct file *file, - unsigned cmd, unsigned long arg) + unsigned cmd, unsigned long arg) { struct video_client_ctx *client_ctx = NULL; struct vdec_ioctl_msg vdec_msg; u32 vcd_status; - unsigned long kernel_vaddr, phy_addr, len; + void *kern_addr; + phys_addr_t phys_addr; struct file *pmem_file; - u32 result = TRUE; - enum vdec_codec vdec_codec_type; - enum vdec_output_fromat output_format; - struct vdec_picsize video_resoultion; - struct vdec_allocatorproperty vdec_buf_req; - struct vdec_setbuffer_cmd setbuffer; - struct vdec_input_frameinfo input_frame_info; - struct vdec_fillbuffer_cmd fill_buffer_cmd; - enum vdec_bufferflush flush_dir; - struct vdec_msginfo vdec_msg_info; - struct vcd_buffer_requirement_type Buffer_req; - struct vcd_property_meta_data_enable_type metdata_disable; - struct vcd_property_hdr_type header_type; - struct vdec_seqheader seq_header; - struct vcd_sequence_hdr_type vcd_seq_hdr; + u32 result = true; + void __user *u_arg = (void __user *)arg; DBG("%s\n", __func__); @@ -1011,110 +956,112 @@ static int vid_dec_ioctl(struct inode *inode, struct file *file, switch (cmd) { case VDEC_IOCTL_SET_CODEC: + { + enum vdec_codec codec_type; + struct vcd_property_meta_data_enable metdata_disable; + struct vcd_property_hdr header_type; DBG("VDEC_IOCTL_SET_CODEC\n"); - if (copy_from_user(&vdec_msg, (void __user *)arg, - sizeof(vdec_msg))) + if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg))) return -EFAULT; - if (copy_from_user(&vdec_codec_type, - (void __user *)vdec_msg.inputparam, - sizeof(vdec_codec_type))) + if (copy_from_user(&codec_type, vdec_msg.in, + sizeof(codec_type))) return -EFAULT; - DBG("setting code type = %u\n", vdec_codec_type); - result = vid_dec_set_codec(client_ctx, &vdec_codec_type); + DBG("setting code type = %u\n", codec_type); + result = vid_dec_set_codec(client_ctx, &codec_type); if (!result) return -EIO; - metdata_disable.n_meta_data_enable_flag = 0; - header_type.n_size = sizeof(metdata_disable); - header_type.prop_id = VCD_I_METADATA_ENABLE; + metdata_disable.meta_data_enable_flag = 0; + header_type.sz = sizeof(metdata_disable); + header_type.id = VCD_I_METADATA_ENABLE; vcd_status = vcd_set_property(client_ctx->vcd_handle, - &header_type, - (void *)&metdata_disable); + &header_type, (void *)&metdata_disable); if (vcd_status) { - ERR("%s() : vcd_set_property Failed for" - "Meta Data Disable\n", __func__); + ERR("%s: vcd_set_property Failed for Meta Data Disable" + "\n", __func__); return -ENODEV; } - DBG("Disabled Meta Data \n"); + DBG("Disabled Meta Data\n"); break; + } case VDEC_IOCTL_SET_OUTPUT_FORMAT: + { + enum vdec_output_format out_format; DBG("VDEC_IOCTL_SET_OUTPUT_FORMAT\n"); - if (copy_from_user(&vdec_msg, (void __user *)arg, - sizeof(vdec_msg))) + if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg))) return -EFAULT; - if (copy_from_user(&output_format, - (void __user *)vdec_msg.inputparam, - sizeof(output_format))) + if (copy_from_user(&out_format, vdec_msg.in, + sizeof(out_format))) return -EFAULT; - result = vid_dec_set_output_format(client_ctx, &output_format); + result = vid_dec_set_output_format(client_ctx, &out_format); if (!result) return -EIO; break; + } case VDEC_IOCTL_SET_PICRES: + { + struct vdec_picsize video_res; DBG("VDEC_IOCTL_SET_PICRES\n"); - if (copy_from_user(&vdec_msg, (void __user *)arg, - sizeof(vdec_msg))) + if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg))) return -EFAULT; - if (copy_from_user(&video_resoultion, - (void __user *)vdec_msg.inputparam, - sizeof(video_resoultion))) + if (copy_from_user(&video_res, vdec_msg.in, sizeof(video_res))) return -EFAULT; - result = - vid_dec_set_frame_resolution(client_ctx, &video_resoultion); + result = vid_dec_set_frame_resolution(client_ctx, + &video_res); if (!result) return -EIO; break; + } case VDEC_IOCTL_GET_PICRES: + { + struct vdec_picsize video_res; DBG("VDEC_IOCTL_GET_PICRES\n"); - if (copy_from_user(&vdec_msg, (void __user *)arg, - sizeof(vdec_msg))) + if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg))) return -EFAULT; - if (copy_from_user(&video_resoultion, - (void __user *)vdec_msg.outputparam, - sizeof(video_resoultion))) + if (copy_from_user(&video_res, vdec_msg.out, sizeof(video_res))) return -EFAULT; - result = vid_dec_get_frame_resolution(client_ctx, - &video_resoultion); + result = vid_dec_get_frame_resolution(client_ctx, &video_res); - if (result) { - if (copy_to_user((void __user *)vdec_msg.outputparam, - &video_resoultion, sizeof(video_resoultion))) - return -EFAULT; - } else + if (!result) return -EIO; + + if (copy_to_user(vdec_msg.out, &video_res,sizeof(video_res))) + return -EFAULT; break; + } case VDEC_IOCTL_SET_BUFFER_REQ: - if (copy_from_user(&vdec_msg, (void __user *)arg, - sizeof(vdec_msg))) + { + //TODO unify these types + struct vdec_allocatorproperty vdec_buf_req; + struct vcd_buffer_requirement vcd_buf_req; + + if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg))) return -EFAULT; - if (copy_from_user(&vdec_buf_req, - (void __user *)vdec_msg.inputparam, - sizeof(vdec_buf_req))) + if (copy_from_user(&vdec_buf_req, vdec_msg.in, + sizeof(vdec_buf_req))) return -EFAULT; - Buffer_req.n_actual_count = vdec_buf_req.actualcount; - Buffer_req.n_align = vdec_buf_req.alignment; - Buffer_req.n_max_count = vdec_buf_req.maxcount; - Buffer_req.n_min_count = vdec_buf_req.mincount; - Buffer_req.n_size = vdec_buf_req.buffer_size; + vcd_buf_req.actual_count = vdec_buf_req.actualcount; + vcd_buf_req.align = vdec_buf_req.alignment; + vcd_buf_req.max_count = vdec_buf_req.maxcount; + vcd_buf_req.min_count = vdec_buf_req.mincount; + vcd_buf_req.size = vdec_buf_req.buffer_size; switch (vdec_buf_req.buffer_type) { case VDEC_BUFFER_TYPE_INPUT: - vcd_status = - vcd_set_buffer_requirements(client_ctx->vcd_handle, - VCD_BUFFER_INPUT, - &Buffer_req); + vcd_status = vcd_set_buffer_requirements( + client_ctx->vcd_handle, VCD_BUFFER_INPUT, + &vcd_buf_req); break; case VDEC_BUFFER_TYPE_OUTPUT: - vcd_status = - vcd_set_buffer_requirements(client_ctx->vcd_handle, - VCD_BUFFER_OUTPUT, - &Buffer_req); + vcd_status = vcd_set_buffer_requirements( + client_ctx->vcd_handle, VCD_BUFFER_OUTPUT, + &vcd_buf_req); break; default: vcd_status = VCD_ERR_BAD_POINTER; @@ -1124,42 +1071,44 @@ static int vid_dec_ioctl(struct inode *inode, struct file *file, if (vcd_status) return -EFAULT; break; + } case VDEC_IOCTL_GET_BUFFER_REQ: + { + struct vdec_allocatorproperty vdec_buf_req; DBG("VDEC_IOCTL_GET_BUFFER_REQ\n"); - if (copy_from_user(&vdec_msg, (void __user *)arg, - sizeof(vdec_msg))) + if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg))) return -EFAULT; - if (copy_from_user(&vdec_buf_req, - (void __user *)vdec_msg.outputparam, - sizeof(vdec_buf_req))) + if (copy_from_user(&vdec_buf_req, vdec_msg.out, + sizeof(vdec_buf_req))) return -EFAULT; result = vid_dec_get_buffer_req(client_ctx, &vdec_buf_req); - - if (result) { - if (copy_to_user((void __user *)vdec_msg.outputparam, - &vdec_buf_req, sizeof(vdec_buf_req))) - return -EFAULT; - } else + if (!result) return -EIO; + + if (copy_to_user(vdec_msg.out, &vdec_buf_req, + sizeof(vdec_buf_req))) + return -EFAULT; break; + } case VDEC_IOCTL_SET_BUFFER: + { + struct vdec_setbuffer_cmd setbuffer; DBG("VDEC_IOCTL_SET_BUFFER\n"); - if (copy_from_user(&vdec_msg, (void __user *)arg, - sizeof(vdec_msg))) + if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg))) return -EFAULT; - if (copy_from_user(&setbuffer, - (void __user *)vdec_msg.inputparam, sizeof(setbuffer))) + if (copy_from_user(&setbuffer, vdec_msg.in, sizeof(setbuffer))) return -EFAULT; result = vid_dec_set_buffer(client_ctx, &setbuffer); break; + } case VDEC_IOCTL_FREE_BUFFER: + { + struct vdec_setbuffer_cmd setbuffer; DBG("VDEC_IOCTL_FREE_BUFFER\n"); - if (copy_from_user(&vdec_msg, (void __user *)arg, - sizeof(vdec_msg))) + if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg))) return -EFAULT; - if (copy_from_user(&setbuffer, - (void __user *)vdec_msg.inputparam, sizeof(setbuffer))) + if (copy_from_user(&setbuffer, vdec_msg.in, sizeof(setbuffer))) return -EFAULT; result = vid_dec_free_buffer(client_ctx, &setbuffer); @@ -1167,73 +1116,71 @@ static int vid_dec_ioctl(struct inode *inode, struct file *file, if (!result) return -EIO; break; - - + } case VDEC_IOCTL_CMD_START: - DBG(" VDEC_IOCTL_CMD_START\n"); - result = vid_dec_start_stop(client_ctx, TRUE); + DBG("VDEC_IOCTL_CMD_START\n"); + result = vid_dec_start(client_ctx); if (!result) return -EIO; break; case VDEC_IOCTL_CMD_STOP: - DBG("VDEC_IOCTL_CMD_STOP\n"); - result = vid_dec_start_stop(client_ctx, FALSE); + result = vid_dec_stop(client_ctx); if (!result) return -EIO; break; case VDEC_IOCTL_CMD_PAUSE: - result = vid_dec_pause_resume(client_ctx, TRUE); + DBG("VDEC_IOCTL_CMD_PAUSE\n"); + result = vid_dec_pause_resume(client_ctx, true); if (!result) return -EIO; break; case VDEC_IOCTL_CMD_RESUME: - DBG("VDEC_IOCTL_CMD_PAUSE\n"); - result = vid_dec_pause_resume(client_ctx, FALSE); + DBG("VDEC_IOCTL_CMD_RESUME\n"); + result = vid_dec_pause_resume(client_ctx, false); if (!result) return -EIO; break; case VDEC_IOCTL_DECODE_FRAME: + { + struct vdec_input_frameinfo frm_info; DBG("VDEC_IOCTL_DECODE_FRAME\n"); - if (copy_from_user(&vdec_msg, (void __user *)arg, - sizeof(vdec_msg))) + if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg))) return -EFAULT; - if (copy_from_user(&input_frame_info, - (void __user *)vdec_msg.inputparam, - sizeof(input_frame_info))) + if (copy_from_user(&frm_info, vdec_msg.in, sizeof(frm_info))) return -EFAULT; - result = vid_dec_decode_frame(client_ctx, &input_frame_info); + result = vid_dec_decode_frame(client_ctx, &frm_info); if (!result) return -EIO; break; + } case VDEC_IOCTL_FILL_OUTPUT_BUFFER: + { + struct vdec_fillbuffer_cmd fill_cmd; + DBG("VDEC_IOCTL_FILL_OUTPUT_BUFFER\n"); - if (copy_from_user(&vdec_msg, (void __user *)arg, - sizeof(vdec_msg))) + if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg))) return -EFAULT; - if (copy_from_user(&fill_buffer_cmd, - (void __user *)vdec_msg.inputparam, - sizeof(fill_buffer_cmd))) + if (copy_from_user(&fill_cmd, vdec_msg.in, sizeof(fill_cmd))) return -EFAULT; - result = vid_dec_fill_output_buffer(client_ctx, - &fill_buffer_cmd); + result = vid_dec_fill_output_buffer(client_ctx, &fill_cmd); if (!result) return -EIO; break; + } case VDEC_IOCTL_CMD_FLUSH: + { + enum vdec_bufferflush flush_dir; DBG("VDEC_IOCTL_CMD_FLUSH\n"); - if (copy_from_user(&vdec_msg, (void __user *)arg, - sizeof(vdec_msg))) + if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg))) return -EFAULT; - if (copy_from_user(&flush_dir, - (void __user *)vdec_msg.inputparam, - sizeof(flush_dir))) + if (copy_from_user(&flush_dir, vdec_msg.in, sizeof(flush_dir))) return -EFAULT; result = vid_dec_flush(client_ctx, flush_dir); @@ -1241,85 +1188,87 @@ static int vid_dec_ioctl(struct inode *inode, struct file *file, if (!result) return -EIO; break; + } case VDEC_IOCTL_GET_NEXT_MSG: + { + struct vdec_msginfo msg_info; DBG("VDEC_IOCTL_GET_NEXT_MSG\n"); - if (copy_from_user(&vdec_msg, (void __user *)arg, - sizeof(vdec_msg))) + if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg))) return -EFAULT; - result = vid_dec_get_next_msg(client_ctx, &vdec_msg_info); + result = vid_dec_get_next_msg(client_ctx, &msg_info); if (!result) return -EIO; - if (copy_to_user((void __user *)vdec_msg.outputparam, - &vdec_msg_info, sizeof(vdec_msg_info))) + if (copy_to_user(vdec_msg.out, &msg_info, sizeof(msg_info))) return -EFAULT; break; + } case VDEC_IOCTL_STOP_NEXT_MSG: DBG("VDEC_IOCTL_STOP_NEXT_MSG\n"); client_ctx->stop_msg = 1; wake_up(&client_ctx->msg_wait); break; case VDEC_IOCTL_SET_SEQUENCE_HEADER: + { + struct vdec_seqheader vdec_seq_hdr; + struct vcd_sequence_hdr vcd_seq_hdr; + unsigned long sz; DBG("VDEC_IOCTL_SET_SEQUENCE_HEADER\n"); - if (copy_from_user(&vdec_msg, (void __user *)arg, - sizeof(vdec_msg))) { - ERR("Copy from user vdec_msg failed \n"); + if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg))) { + ERR("Copy from user vdec_msg failed\n"); return -EFAULT; } - if (copy_from_user(&seq_header, - (void __user *)vdec_msg.inputparam, - sizeof(seq_header))) { - ERR("Copy from user seq_header failed \n"); + if (copy_from_user(&vdec_seq_hdr, vdec_msg.in, + sizeof(vdec_seq_hdr))) { + ERR("Copy from user seq_header failed\n"); return -EFAULT; } - if (!seq_header.seq_header_len) { - ERR("Seq Len is Zero \n"); + if (!vdec_seq_hdr.sz) { + ERR("Seq len is zero\n"); return -EFAULT; } - if (get_pmem_file(seq_header.pmem_fd, - &phy_addr, &kernel_vaddr, &len, &pmem_file)) { - ERR("%s(): get_pmem_file failed\n", __func__); - return FALSE; + if (get_pmem_file(vdec_seq_hdr.pmem_fd, + (unsigned long *)&phys_addr, + (unsigned long *)&kern_addr, &sz, &pmem_file)) { + ERR("%s: get_pmem_file failed\n", __func__); + return false; } put_pmem_file(pmem_file); - vcd_seq_hdr.n_sequence_header_len = seq_header.seq_header_len; - kernel_vaddr += (unsigned long)seq_header.pmem_offset; - vcd_seq_hdr.p_sequence_header = (u8 *)kernel_vaddr; - if (!vcd_seq_hdr.p_sequence_header) { + vcd_seq_hdr.sz = vdec_seq_hdr.sz; + kern_addr = (u8 *)kern_addr + vdec_seq_hdr.pmem_offset; + vcd_seq_hdr.addr = kern_addr; + if (!vcd_seq_hdr.addr) { ERR("Sequence Header pointer failed\n"); return -EFAULT; } - client_ctx->seq_header_set = TRUE; + client_ctx->seq_header_set = true; if (vcd_decode_start(client_ctx->vcd_handle, &vcd_seq_hdr)) { - ERR("Decode start Failed \n"); - client_ctx->seq_header_set = FALSE; + ERR("Decode start Failed\n"); + client_ctx->seq_header_set = false; return -EFAULT; } - DBG("Wait Client completion Sequence Header \n"); + DBG("Wait Client completion Sequence Header\n"); wait_for_completion(&client_ctx->event); - vcd_seq_hdr.p_sequence_header = NULL; + vcd_seq_hdr.addr = NULL; if (client_ctx->event_status) { ERR("Set Seq Header status is failed"); return -EFAULT; } break; + } case VDEC_IOCTL_GET_NUMBER_INSTANCES: DBG("VDEC_IOCTL_GET_NUMBER_INSTANCES\n"); - if (copy_from_user(&vdec_msg, (void __user *)arg, - sizeof(vdec_msg))) + if (copy_from_user(&vdec_msg, u_arg, sizeof(vdec_msg))) return -EFAULT; - if (copy_to_user((void __user *)vdec_msg.outputparam, - &vid_dec_device_p->num_clients, sizeof(u32))) + if (copy_to_user(vdec_msg.out, &vidc_dec_dev->num_clients, + sizeof(vidc_dec_dev->num_clients))) return -EFAULT; break; - default: ERR("%s(): Unsupported ioctl\n", __func__); return -ENOTTY; - - break; } return 0; @@ -1330,88 +1279,91 @@ static u32 vid_dec_close_client(struct video_client_ctx *client_ctx) u32 vcd_status; int rc; - INFO("\n msm_vidc_dec: Inside %s()", __func__); - if (!client_ctx || (!client_ctx->vcd_handle)) { - ERR("\n Invalid client_ctx"); - return FALSE; + INFO("msm_vidc_dec: Inside %s\n", __func__); + if (!client_ctx || !client_ctx->vcd_handle) { + ERR("Invalid client_ctx\n"); + return false; } - mutex_lock(&vid_dec_device_p->lock); + mutex_lock(&vidc_dec_dev->lock); vcd_status = vcd_stop(client_ctx->vcd_handle); if (!vcd_status) { rc = wait_for_completion_timeout(&client_ctx->event, - (5 * HZ)/10); + (5 * HZ) / 10); if (!rc) - DBG("%s:ERROR vcd_stop time out rc = %d\n", - __func__, rc); + DBG("%s:ERROR vcd_stop time out rc = %d\n", __func__, + rc); if (client_ctx->event_status) ERR("%s:ERROR vcd_stop event_status failure\n", - __func__); + __func__); } vcd_status = vcd_close(client_ctx->vcd_handle); if (vcd_status) { - mutex_unlock(&vid_dec_device_p->lock); - return FALSE; + mutex_unlock(&vidc_dec_dev->lock); + return false; } - memset((void *)client_ctx, 0, sizeof(struct video_client_ctx)); - vid_dec_device_p->num_clients--; - mutex_unlock(&vid_dec_device_p->lock); - return TRUE; + memset((void *)client_ctx, 0, sizeof(*client_ctx)); + vidc_dec_dev->num_clients--; + mutex_unlock(&vidc_dec_dev->lock); + return true; } static int vid_dec_open(struct inode *inode, struct file *file) { + int rc; s32 client_index; struct video_client_ctx *client_ctx; u32 vcd_status = VCD_ERR_FAIL; - INFO("\n msm_vidc_dec: Inside %s()", __func__); - mutex_lock(&vid_dec_device_p->lock); + INFO("msm_vidc_dec: Inside %s\n", __func__); + mutex_lock(&vidc_dec_dev->lock); - if (vid_dec_device_p->num_clients == VID_DEC_MAX_DECODER_CLIENTS) { - ERR("ERROR : vid_dec_open() max number of clients" - "limit reached\n"); - mutex_unlock(&vid_dec_device_p->lock); + if (vidc_dec_dev->num_clients == VID_DEC_MAX_DECODER_CLIENTS) { + ERR("%s: ERROR: max number of clients limit reached\n", + __func__); + mutex_unlock(&vidc_dec_dev->lock); return -ENODEV; } #ifndef USE_RES_TRACKER DBG("Resource Tracker not in use"); - if (!vid_c_enable_clk(VID_C_HCLK_RATE)) { - ERR("ERROR : vid_dec_open() clock enabled failed\n"); - mutex_unlock(&vid_dec_device_p->lock); - return -ENODEV; + if (!vid_c_enable_clk(VID_C_HCLK_RATE)) { + ERR("%s: ERROR: clock enabled failed\n", __func__); + mutex_unlock(&vidc_dec_dev->lock); + return -ENODEV; } #endif - DBG(" Virtual Address of ioremap is %p\n", vid_dec_device_p->virt_base); - if (!vid_dec_device_p->num_clients) { - if (!vid_c_load_firmware()) - return -ENODEV; + DBG("Virtual Address of ioremap is %p\n", vidc_dec_dev->virt_base); + + if (!vidc_dec_dev->num_clients) { + rc = vcd_fw_prepare_all(); + if (rc) + return rc; } client_index = vid_dec_get_empty_client_index(); if (client_index == -1) { - ERR("%s() : No free clients client_index == -1\n", __func__); + ERR("%s: No free clients client_index == -1\n", __func__); return -ENODEV; } - client_ctx = &vid_dec_device_p->vdec_clients[client_index]; - vid_dec_device_p->num_clients++; + client_ctx = &vidc_dec_dev->vdec_clients[client_index]; + vidc_dec_dev->num_clients++; init_completion(&client_ctx->event); mutex_init(&client_ctx->msg_queue_lock); INIT_LIST_HEAD(&client_ctx->msg_queue); init_waitqueue_head(&client_ctx->msg_wait); client_ctx->stop_msg = 0; - vcd_status = vcd_open(vid_dec_device_p->device_handle, TRUE, - vid_dec_vcd_cb, client_ctx); + vcd_status = vcd_open(vidc_dec_dev->device_handle, true, + vid_dec_vcd_cb, client_ctx); wait_for_completion(&client_ctx->event); - client_ctx->seq_header_set = FALSE; + client_ctx->seq_header_set = false; file->private_data = client_ctx; - mutex_unlock(&vid_dec_device_p->lock); + mutex_unlock(&vidc_dec_dev->lock); return 0; } @@ -1419,13 +1371,12 @@ static int vid_dec_release(struct inode *inode, struct file *file) { struct video_client_ctx *client_ctx = file->private_data; - INFO("\n msm_vidc_dec: Inside %s()", __func__); + INFO("msm_vidc_dec: Inside %s\n", __func__); vid_dec_close_client(client_ctx); - vid_c_release_firmware(); #ifndef USE_RES_TRACKER vid_c_disable_clk(); #endif - INFO("\n msm_vidc_dec: Return from %s()", __func__); + INFO("msm_vidc_dec: Return from %s\n", __func__); return 0; } @@ -1450,35 +1401,35 @@ void vid_dec_interrupt_clear(void) void *vid_dec_map_dev_base_addr(void *device_name) { - return vid_dec_device_p->virt_base; + return vidc_dec_dev->virt_base; } static int vid_dec_vcd_init(void) { int rc; - struct vcd_init_config_type vcd_init_config; + struct vcd_init_config vcd_init_config; u32 i; /* init_timer(&hw_timer); */ - INFO("\n msm_vidc_dec: Inside %s()", __func__); - vid_dec_device_p->num_clients = 0; + INFO("msm_vidc_dec: Inside %s\n", __func__); + vidc_dec_dev->num_clients = 0; for (i = 0; i < VID_DEC_MAX_DECODER_CLIENTS; i++) { - memset((void *)&vid_dec_device_p->vdec_clients[i], 0, - sizeof(vid_dec_device_p->vdec_clients[i])); + memset((void *)&vidc_dec_dev->vdec_clients[i], 0, + sizeof(vidc_dec_dev->vdec_clients[i])); } - mutex_init(&vid_dec_device_p->lock); - vid_dec_device_p->virt_base = vid_c_get_ioaddr(); - DBG("%s() : base address for VIDC core %u\n", __func__, \ - (int)vid_dec_device_p->virt_base); + mutex_init(&vidc_dec_dev->lock); + vidc_dec_dev->virt_base = vid_c_get_ioaddr(); + DBG("%s: base address for VIDC core %p\n", __func__, + vidc_dec_dev->virt_base); - if (!vid_dec_device_p->virt_base) { - ERR("%s() : ioremap failed\n", __func__); + if (!vidc_dec_dev->virt_base) { + ERR("%s: ioremap failed\n", __func__); return -ENOMEM; } - vcd_init_config.p_device_name = "VID_C"; + vcd_init_config.device_name = "VID_C"; vcd_init_config.pf_map_dev_base_addr = vid_dec_map_dev_base_addr; vcd_init_config.pf_interrupt_clr = vid_dec_interrupt_clear; vcd_init_config.pf_register_isr = vid_dec_interrupt_register; @@ -1488,10 +1439,9 @@ static int vid_dec_vcd_init(void) vcd_init_config.pf_timer_start = vid_c_timer_start; vcd_init_config.pf_timer_stop = vid_c_timer_stop; - rc = vcd_init(&vcd_init_config, &vid_dec_device_p->device_handle); - + rc = vcd_init(&vcd_init_config, &vidc_dec_dev->device_handle); if (rc) { - ERR("%s() : vcd_init failed\n", __func__); + ERR("%s: vcd_init failed\n", __func__); return -ENODEV; } return 0; @@ -1502,74 +1452,69 @@ static int __init vid_dec_init(void) int rc = 0; struct device *class_devp; - INFO("\n msm_vidc_dec: Inside %s()", __func__); - vid_dec_device_p = kzalloc(sizeof(struct vid_dec_dev), GFP_KERNEL); - if (!vid_dec_device_p) { - ERR("%s Unable to allocate memory for vid_dec_dev\n", - __func__); + INFO("msm_vidc_dec: Inside %s\n", __func__); + vidc_dec_dev = kzalloc(sizeof(*vidc_dec_dev), GFP_KERNEL); + if (!vidc_dec_dev) { + ERR("%s Unable to allocate memory for vid_dec_dev\n", __func__); return -ENOMEM; } - rc = alloc_chrdev_region(&vid_dec_dev_num, 0, 1, VID_DEC_NAME); + rc = alloc_chrdev_region(&vidc_dec_dev_num, 0, 1, VID_DEC_NAME); if (rc < 0) { - ERR("%s: alloc_chrdev_region Failed rc = %d\n", - __func__, rc); + ERR("%s: alloc_chrdev_region failed rc = %d\n", __func__, rc); goto error_vid_dec_alloc_chrdev_region; } - vid_dec_class = class_create(THIS_MODULE, VID_DEC_NAME); - if (IS_ERR(vid_dec_class)) { - rc = PTR_ERR(vid_dec_class); - ERR("%s: couldn't create vid_dec_class rc = %d\n", - __func__, rc); - + vidc_dec_class = class_create(THIS_MODULE, VID_DEC_NAME); + if (IS_ERR(vidc_dec_class)) { + rc = PTR_ERR(vidc_dec_class); + ERR("%s: couldn't create vid_dec_class %d\n", __func__, rc); goto error_vid_dec_class_create; } - class_devp = device_create(vid_dec_class, NULL, vid_dec_dev_num, NULL, - VID_DEC_NAME); + class_devp = device_create(vidc_dec_class, NULL, vidc_dec_dev_num, NULL, + VID_DEC_NAME); if (IS_ERR(class_devp)) { rc = PTR_ERR(class_devp); - ERR("%s: class device_create failed %d\n", - __func__, rc); + ERR("%s: class device_create failed %d\n", __func__, rc); goto error_vid_dec_class_device_create; } - vid_dec_device_p->device = class_devp; + vidc_dec_dev->device = class_devp; - cdev_init(&vid_dec_device_p->cdev, &vid_dec_fops); - vid_dec_device_p->cdev.owner = THIS_MODULE; - rc = cdev_add(&(vid_dec_device_p->cdev), vid_dec_dev_num, 1); + cdev_init(&vidc_dec_dev->cdev, &vid_dec_fops); + vidc_dec_dev->cdev.owner = THIS_MODULE; + rc = cdev_add(&vidc_dec_dev->cdev, vidc_dec_dev_num, 1); if (rc < 0) { ERR("%s: cdev_add failed %d\n", __func__, rc); goto error_vid_dec_cdev_add; } - vid_dec_vcd_init(); - return 0; + + return vid_dec_vcd_init(); error_vid_dec_cdev_add: - device_destroy(vid_dec_class, vid_dec_dev_num); + device_destroy(vidc_dec_class, vidc_dec_dev_num); error_vid_dec_class_device_create: - class_destroy(vid_dec_class); + class_destroy(vidc_dec_class); error_vid_dec_class_create: - unregister_chrdev_region(vid_dec_dev_num, 1); + unregister_chrdev_region(vidc_dec_dev_num, 1); error_vid_dec_alloc_chrdev_region: - kfree(vid_dec_device_p); + kfree(vidc_dec_dev); return rc; } static void __exit vid_dec_exit(void) { - INFO("\n msm_vidc_dec: Inside %s()", __func__); - cdev_del(&(vid_dec_device_p->cdev)); - device_destroy(vid_dec_class, vid_dec_dev_num); - class_destroy(vid_dec_class); - unregister_chrdev_region(vid_dec_dev_num, 1); - kfree(vid_dec_device_p); - INFO("\n msm_vidc_dec: Return from %s()", __func__); + INFO("msm_vidc_dec: Inside %s\n", __func__); + cdev_del(&(vidc_dec_dev->cdev)); + device_destroy(vidc_dec_class, vidc_dec_dev_num); + class_destroy(vidc_dec_class); + unregister_chrdev_region(vidc_dec_dev_num, 1); + kfree(vidc_dec_dev); + INFO("msm_vidc_dec: Return from %s\n", __func__); } MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/video_core/720p/enc/venc.c b/drivers/misc/video_core/720p/enc/venc.c index 1790af4fa788f..6b81018e782d1 100644 --- a/drivers/misc/video_core/720p/enc/venc.c +++ b/drivers/misc/video_core/720p/enc/venc.c @@ -33,13 +33,14 @@ #include #include +#include "vcd_ddl_firmware.h" #include "video_core_type.h" #include "vcd_api.h" #include "venc_internal.h" #include "video_core_init.h" -#define VID_ENC_NAME "msm_vidc_enc" -#define VID_C_HCLK_RATE 170667000 +#define VID_ENC_NAME "msm_vidc_enc" +#define VID_C_HCLK_RATE 170667000 #if DEBUG #define DBG(x...) printk(KERN_DEBUG x) @@ -50,36 +51,34 @@ #define INFO(x...) printk(KERN_INFO x) #define ERR(x...) printk(KERN_ERR x) -static struct vid_enc_dev *vid_enc_device_p; -static dev_t vid_enc_dev_num; +static struct vid_enc_dev *vidc_enc_dev; +static dev_t vidc_enc_dev_num; static struct class *vid_enc_class; -static int vid_enc_ioctl(struct inode *inode, struct file *file, - unsigned cmd, unsigned long arg); +static int vid_enc_ioctl(struct inode *inode, struct file *file, unsigned cmd, + unsigned long arg); +//TOOD stop_cmd wtf static int stop_cmd; static s32 vid_enc_get_empty_client_index(void) { u32 i; - u32 found = FALSE; + u32 found = false; for (i = 0; i < VID_ENC_MAX_ENCODER_CLIENTS; i++) { - if (!vid_enc_device_p->venc_clients[i].vcd_handle) { - found = TRUE; + if (!vidc_enc_dev->venc_clients[i].vcd_handle) { + found = true; break; } } if (!found) { - ERR("%s():ERROR No space for new client\n", - __func__); + ERR("%s: ERROR No space for new client\n", __func__); return -1; - } else { - DBG("%s(): available client index = %u\n", - __func__, i); - return i; } + DBG("%s: available client index = %u\n", __func__, i); + return i; } - +//TODO collapse this crap u32 vid_enc_get_status(u32 status) { u32 venc_status; @@ -127,61 +126,52 @@ static void vid_enc_notify_client(struct video_client_ctx *client_ctx) } void vid_enc_vcd_open_done(struct video_client_ctx *client_ctx, - struct vcd_handle_container_type *handle_container) + struct vcd_handle_container *handle_container) { DBG("vid_enc_vcd_open_done\n"); - if (client_ctx) { - if (handle_container) - client_ctx->vcd_handle = handle_container->handle; - else - ERR("%s(): ERROR. handle_container is NULL\n", - __func__); - vid_enc_notify_client(client_ctx); - } else - ERR("%s(): ERROR. client_ctx is NULL\n", - __func__); + if (!client_ctx) { + ERR("%s(): ERROR. client_ctx is NULL\n", __func__); + return; + } + if (handle_container) + client_ctx->vcd_handle = handle_container->handle; + else + ERR("%s: ERROR. handle_container is NULL\n", __func__); + vid_enc_notify_client(client_ctx); } static void vid_enc_input_frame_done(struct video_client_ctx *client_ctx, - u32 event, u32 status, - struct vcd_frame_data_type *vcd_frame_data) + u32 event, u32 status, struct vcd_frame_data *vcd_frame_data) { struct vid_enc_msg *venc_msg; if (!client_ctx || !vcd_frame_data) { - ERR("vid_enc_input_frame_done() NULL pointer \n"); + ERR("%s: NULL pointer\n", __func__); return; } - venc_msg = kzalloc(sizeof(struct vid_enc_msg), - GFP_KERNEL); + venc_msg = kzalloc(sizeof(struct vid_enc_msg), GFP_KERNEL); if (!venc_msg) { - ERR("vid_enc_input_frame_done(): cannot allocate vid_enc_msg " - " buffer\n"); + ERR("%s: cannot allocate vid_enc_msg buffer\n", __func__); return; } venc_msg->venc_msg_info.statuscode = vid_enc_get_status(status); if (event == VCD_EVT_RESP_INPUT_DONE) { - venc_msg->venc_msg_info.msgcode = - VEN_MSG_INPUT_BUFFER_DONE; - DBG("Send INPUT_DON message to client = %p\n", - client_ctx); + venc_msg->venc_msg_info.msgcode = VEN_MSG_INPUT_BUFFER_DONE; + DBG("Send INPUT_DON message to client = %p\n", client_ctx); } else if (event == VCD_EVT_RESP_INPUT_FLUSHED) { venc_msg->venc_msg_info.msgcode = VEN_MSG_INPUT_BUFFER_DONE; - DBG("Send INPUT_FLUSHED message to client = %p\n", - client_ctx); + DBG("Send INPUT_FLUSHED message to client = %p\n", client_ctx); } else { ERR("vid_enc_input_frame_done(): invalid event type\n"); return; } - venc_msg->venc_msg_info.buf.clientdata = - (void *)vcd_frame_data->n_frm_clnt_data; - venc_msg->venc_msg_info.msgdata_size = - sizeof(struct vid_enc_msg); + venc_msg->venc_msg_info.buf.clientdata = vcd_frame_data->client_data; + venc_msg->venc_msg_info.msgdata_size = sizeof(struct vid_enc_msg); mutex_lock(&client_ctx->msg_queue_lock); list_add_tail(&venc_msg->list, &client_ctx->msg_queue); @@ -190,73 +180,61 @@ static void vid_enc_input_frame_done(struct video_client_ctx *client_ctx, } static void vid_enc_output_frame_done(struct video_client_ctx *client_ctx, - u32 event, u32 status, - struct vcd_frame_data_type *vcd_frame_data) + u32 event, u32 status, struct vcd_frame_data *frm_data) { struct vid_enc_msg *venc_msg; - unsigned long kernel_vaddr, phy_addr, user_vaddr; + void __user *user_addr; + void *kern_addr; + phys_addr_t phys_addr; int pmem_fd; struct file *file; - s32 buffer_index = -1; + s32 buf_index = -1; - if (!client_ctx || !vcd_frame_data) { - ERR("vid_enc_input_frame_done() NULL pointer \n"); + if (!client_ctx || !frm_data) { + ERR("%s: NULL pointer\n", __func__); return; } - venc_msg = kzalloc(sizeof(struct vid_enc_msg), - GFP_KERNEL); + venc_msg = kzalloc(sizeof(struct vid_enc_msg), GFP_KERNEL); if (!venc_msg) { - ERR("vid_enc_input_frame_done(): cannot allocate vid_enc_msg " - " buffer\n"); + ERR("%s: cannot allocate vid_enc_msg buffer\n", __func__); return; } venc_msg->venc_msg_info.statuscode = vid_enc_get_status(status); - if (event == VCD_EVT_RESP_OUTPUT_DONE) - venc_msg->venc_msg_info.msgcode = - VEN_MSG_OUTPUT_BUFFER_DONE; - - else if (event == VCD_EVT_RESP_OUTPUT_FLUSHED) - venc_msg->venc_msg_info.msgcode = - VEN_MSG_OUTPUT_BUFFER_DONE; - else { - ERR("QVD: vid_enc_output_frame_done invalid cmd type \n"); + if (event == VCD_EVT_RESP_OUTPUT_DONE) { + venc_msg->venc_msg_info.msgcode = VEN_MSG_OUTPUT_BUFFER_DONE; + } else if (event == VCD_EVT_RESP_OUTPUT_FLUSHED) { + venc_msg->venc_msg_info.msgcode = VEN_MSG_OUTPUT_BUFFER_DONE; + } else { + ERR("QVD: vid_enc_output_frame_done invalid cmd type\n"); return; } - kernel_vaddr = - (unsigned long)vcd_frame_data->p_virtual; - - if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, - FALSE, &user_vaddr, &kernel_vaddr, - &phy_addr, &pmem_fd, &file, - &buffer_index)) { - - /* Buffer address in user space */ - venc_msg->venc_msg_info.buf.ptrbuffer = (u8 *) user_vaddr; - /* Buffer address in user space */ - venc_msg->venc_msg_info.buf.clientdata = (void *) - vcd_frame_data->n_frm_clnt_data; - /* Data length */ - venc_msg->venc_msg_info.buf.len = - vcd_frame_data->n_data_len; - venc_msg->venc_msg_info.buf.flags = - vcd_frame_data->n_flags; - /* Timestamp pass-through from input frame */ - venc_msg->venc_msg_info.buf.timestamp = - vcd_frame_data->time_stamp; - - /* Decoded picture width and height */ - venc_msg->venc_msg_info.msgdata_size = - sizeof(struct venc_buffer); - } else { + kern_addr = frm_data->virt_addr; + + if (!vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, false, + &user_addr, &kern_addr, &phys_addr, &pmem_fd, &file, + &buf_index)) { ERR("vid_enc_output_frame_done UVA can not be found\n"); - venc_msg->venc_msg_info.statuscode = - VEN_S_EFATAL; + venc_msg->venc_msg_info.statuscode = VEN_S_EFATAL; + goto out; } + + /* Buffer address in user space */ + venc_msg->venc_msg_info.buf.addr = user_addr; + venc_msg->venc_msg_info.buf.clientdata = frm_data->client_data; + /* Data length */ + venc_msg->venc_msg_info.buf.len = frm_data->data_len; + venc_msg->venc_msg_info.buf.flags = frm_data->flags; + /* time-stamp pass-through from input frame */ + venc_msg->venc_msg_info.buf.timestamp = frm_data->time_stamp; + /* Decoded picture width and height */ + venc_msg->venc_msg_info.msgdata_size = sizeof(struct venc_buffer); + +out: mutex_lock(&client_ctx->msg_queue_lock); list_add_tail(&venc_msg->list, &client_ctx->msg_queue); mutex_unlock(&client_ctx->msg_queue_lock); @@ -268,60 +246,43 @@ static void vid_enc_lean_event(struct video_client_ctx *client_ctx, { struct vid_enc_msg *venc_msg; if (!client_ctx) { - ERR("%s(): !client_ctx pointer \n", - __func__); + ERR("%s(): !client_ctx pointer\n", __func__); return; } - venc_msg = kzalloc(sizeof(struct vid_enc_msg), - GFP_KERNEL); + venc_msg = kzalloc(sizeof(struct vid_enc_msg), GFP_KERNEL); if (!venc_msg) { - ERR("%s(): cannot allocate vid_enc_msg buffer\n", - __func__); + ERR("%s(): cannot allocate vid_enc_msg buffer\n", __func__); return; } - venc_msg->venc_msg_info.statuscode = - vid_enc_get_status(status); + venc_msg->venc_msg_info.statuscode = vid_enc_get_status(status); switch (event) { case VCD_EVT_RESP_FLUSH_INPUT_DONE: - INFO("\n msm_vidc_enc: Sending VCD_EVT_RESP_FLUSH_INPUT_DONE" - " to client"); - venc_msg->venc_msg_info.msgcode = - VEN_MSG_FLUSH_INPUT_DONE; + INFO("%s: Sending VCD_EVT_RESP_FLUSH_INPUT_DONE to client\n", + __func__); + venc_msg->venc_msg_info.msgcode = VEN_MSG_FLUSH_INPUT_DONE; break; case VCD_EVT_RESP_FLUSH_OUTPUT_DONE: - INFO("\n msm_vidc_enc: Sending VCD_EVT_RESP_FLUSH_OUTPUT_DONE" - " to client"); - venc_msg->venc_msg_info.msgcode = - VEN_MSG_FLUSH_OUPUT_DONE; + INFO("%s: Sending VCD_EVT_RESP_FLUSH_OUTPUT_DONE to client\n", + __func__); + venc_msg->venc_msg_info.msgcode = VEN_MSG_FLUSH_OUPUT_DONE; break; - case VCD_EVT_RESP_START: - INFO("\n msm_vidc_enc: Sending VCD_EVT_RESP_START" - " to client"); - venc_msg->venc_msg_info.msgcode = - VEN_MSG_START; + INFO("%s: Sending VCD_EVT_RESP_START to client\n", __func__); + venc_msg->venc_msg_info.msgcode = VEN_MSG_START; break; - case VCD_EVT_RESP_STOP: - INFO("\n msm_vidc_enc: Sending VCD_EVT_RESP_STOP" - " to client"); - venc_msg->venc_msg_info.msgcode = - VEN_MSG_STOP; + INFO("%s: Sending VCD_EVT_RESP_STOP to client\n", __func__); + venc_msg->venc_msg_info.msgcode = VEN_MSG_STOP; break; - case VCD_EVT_RESP_PAUSE: - INFO("\n msm_vidc_enc: Sending VCD_EVT_RESP_PAUSE" - " to client"); - venc_msg->venc_msg_info.msgcode = - VEN_MSG_PAUSE; + INFO("%s: Sending VCD_EVT_RESP_PAUSE to client\n", __func__); + venc_msg->venc_msg_info.msgcode = VEN_MSG_PAUSE; break; - default: - ERR("%s() : unknown event type \n", - __func__); + ERR("%s: unknown event type\n", __func__); break; } @@ -334,17 +295,15 @@ static void vid_enc_lean_event(struct video_client_ctx *client_ctx, } -void vid_enc_vcd_cb(u32 event, u32 status, - void *info, u32 size, void *handle, +void vid_enc_vcd_cb(u32 event, u32 status, void *info, u32 size, void *handle, void *const client_data) { - struct video_client_ctx *client_ctx = - (struct video_client_ctx *)client_data; + struct video_client_ctx *client_ctx = client_data; - DBG("Entering %s()\n", __func__); + DBG("Entering %s\n", __func__); if (!client_ctx) { - ERR("%s(): client_ctx is NULL \n", __func__); + ERR("%s: client_ctx is NULL\n", __func__); return; } @@ -352,22 +311,16 @@ void vid_enc_vcd_cb(u32 event, u32 status, switch (event) { case VCD_EVT_RESP_OPEN: - vid_enc_vcd_open_done(client_ctx, - (struct vcd_handle_container_type *)info); + vid_enc_vcd_open_done(client_ctx, info); break; - case VCD_EVT_RESP_INPUT_DONE: case VCD_EVT_RESP_INPUT_FLUSHED: - vid_enc_input_frame_done(client_ctx, event, - status, (struct vcd_frame_data_type *)info); + vid_enc_input_frame_done(client_ctx, event, status, info); break; - case VCD_EVT_RESP_OUTPUT_DONE: case VCD_EVT_RESP_OUTPUT_FLUSHED: - vid_enc_output_frame_done(client_ctx, event, status, - (struct vcd_frame_data_type *)info); + vid_enc_output_frame_done(client_ctx, event, status, info); break; - case VCD_EVT_RESP_PAUSE: case VCD_EVT_RESP_START: case VCD_EVT_RESP_STOP: @@ -378,10 +331,8 @@ void vid_enc_vcd_cb(u32 event, u32 status, case VCD_EVT_IND_RESOURCES_LOST: vid_enc_lean_event(client_ctx, event, status); break; - default: - ERR("%s() : Error - Invalid event type =%u\n", - __func__, event); + ERR("%s: Error invalid event type %u\n", __func__, event); break; } } @@ -395,50 +346,47 @@ static u32 vid_enc_msg_pending(struct video_client_ctx *client_ctx) mutex_unlock(&client_ctx->msg_queue_lock); if (islist_empty) { - DBG("%s(): vid_enc msg queue empty\n", - __func__); + DBG("%s: vid_enc msg queue empty\n", __func__); if (client_ctx->stop_msg) { - DBG("%s(): List empty and Stop Msg set\n", - __func__); + DBG("%s: List empty and Stop Msg set\n", __func__); return client_ctx->stop_msg; } } else - DBG("%s(): vid_enc msg queue Not empty\n", - __func__); + DBG("%s: vid_enc msg queue Not empty\n", __func__); return !islist_empty; } static u32 vid_enc_get_next_msg(struct video_client_ctx *client_ctx, - struct venc_msg *venc_msg_info) + struct venc_msg *venc_msg_info) { int rc; struct vid_enc_msg *vid_enc_msg = NULL; if (!client_ctx) - return FALSE; + return false; rc = wait_event_interruptible(client_ctx->msg_wait, vid_enc_msg_pending(client_ctx)); if (rc < 0 || client_ctx->stop_msg) { DBG("rc = %d, stop_msg = %u\n", rc, client_ctx->stop_msg); - return FALSE; + return false; } mutex_lock(&client_ctx->msg_queue_lock); if (!list_empty(&client_ctx->msg_queue)) { - DBG("%s(): After Wait \n", __func__); + DBG("%s: After Wait\n", __func__); vid_enc_msg = list_first_entry(&client_ctx->msg_queue, - struct vid_enc_msg, list); + struct vid_enc_msg, list); list_del(&vid_enc_msg->list); memcpy(venc_msg_info, &vid_enc_msg->venc_msg_info, - sizeof(struct venc_msg)); + sizeof(struct venc_msg)); kfree(vid_enc_msg); } mutex_unlock(&client_ctx->msg_queue_lock); - return TRUE; + return true; } static u32 vid_enc_close_client(struct video_client_ctx *client_ctx) @@ -447,13 +395,13 @@ static u32 vid_enc_close_client(struct video_client_ctx *client_ctx) int rc; - INFO("\n msm_vidc_enc: Inside %s()", __func__); - if (!client_ctx || (!client_ctx->vcd_handle)) { - ERR("\n %s(): Invalid client_ctx", __func__); - return FALSE; + INFO("msm_vidc_enc: Inside %s\n", __func__); + if (!client_ctx || !client_ctx->vcd_handle) { + ERR("%s: Invalid client_ctx\n", __func__); + return false; } - mutex_lock(&vid_enc_device_p->lock); + mutex_lock(&vidc_enc_dev->lock); if (!stop_cmd) { vcd_status = vcd_stop(client_ctx->vcd_handle); @@ -462,13 +410,13 @@ static u32 vid_enc_close_client(struct video_client_ctx *client_ctx) rc = wait_for_completion_timeout(&client_ctx->event, 5 * HZ); if (!rc) { - ERR("%s:ERROR vcd_stop time out" - "rc = %d\n", __func__, rc); + ERR("%s: ERROR vcd_stop time out %d\n", + __func__, rc); } if (client_ctx->event_status) { - ERR("%s:ERROR " - "vcd_stop Not successs \n", __func__); + ERR("%s :ERROR vcd_stop Not success\n", + __func__); } } } @@ -476,89 +424,91 @@ static u32 vid_enc_close_client(struct video_client_ctx *client_ctx) vcd_status = vcd_close(client_ctx->vcd_handle); if (vcd_status) { - mutex_unlock(&vid_enc_device_p->lock); - return FALSE; + mutex_unlock(&vidc_enc_dev->lock); + return false; } - memset((void *)client_ctx, 0, - sizeof(struct video_client_ctx)); + memset((void *)client_ctx, 0, sizeof(struct video_client_ctx)); - vid_enc_device_p->num_clients--; + vidc_enc_dev->num_clients--; stop_cmd = 0; - mutex_unlock(&vid_enc_device_p->lock); - return TRUE; + mutex_unlock(&vidc_enc_dev->lock); + return true; } static int vid_enc_open(struct inode *inode, struct file *file) { + int rc = 0; s32 client_index; struct video_client_ctx *client_ctx; u32 vcd_status = VCD_ERR_FAIL; - INFO("\n msm_vidc_enc: Inside %s()", __func__); + INFO("msm_vidc_enc: Inside %s\n", __func__); - mutex_lock(&vid_enc_device_p->lock); + mutex_lock(&vidc_enc_dev->lock); stop_cmd = 0; - if (vid_enc_device_p->num_clients == VID_ENC_MAX_ENCODER_CLIENTS) { - ERR("ERROR : vid_enc_open() max number of clients" - "limit reached\n"); - mutex_unlock(&vid_enc_device_p->lock); - return -ENODEV; + if (vidc_enc_dev->num_clients == VID_ENC_MAX_ENCODER_CLIENTS) { + ERR("ERROR: vid_enc_open() max number of clients limit reached" + "\n"); + rc = -ENODEV; + goto out; } #ifndef USE_RES_TRACKER DBG("Resource Tracker not in use"); if (!vid_c_enable_clk(VID_C_HCLK_RATE)) { - ERR("ERROR : vid_enc_open() clock enabled failed\n"); - mutex_unlock(&vid_enc_device_p->lock); - return -ENODEV; + ERR("ERROR: vid_enc_open() clock enabled failed\n"); + rc = -ENODEV; + goto out; } #endif - DBG(" Virtual Address of ioremap is %p\n", vid_enc_device_p->virt_base); - if (!vid_enc_device_p->num_clients) { - if (!vid_c_load_firmware()) - return -ENODEV; + DBG("Virtual Address of ioremap is %p\n", vidc_enc_dev->virt_base); + + if (!vidc_enc_dev->num_clients) { + rc = vcd_fw_prepare_all(); + if (rc) + goto out; } client_index = vid_enc_get_empty_client_index(); if (client_index == -1) { - ERR("%s() : No free clients client_index == -1\n", - __func__); - return -ENODEV; + ERR("%s: No free clients client_index == -1\n", __func__); + rc = -ENODEV; + goto out; } - client_ctx = - &vid_enc_device_p->venc_clients[client_index]; - vid_enc_device_p->num_clients++; + client_ctx = &vidc_enc_dev->venc_clients[client_index]; + vidc_enc_dev->num_clients++; init_completion(&client_ctx->event); mutex_init(&client_ctx->msg_queue_lock); INIT_LIST_HEAD(&client_ctx->msg_queue); init_waitqueue_head(&client_ctx->msg_wait); - vcd_status = vcd_open(vid_enc_device_p->device_handle, FALSE, + vcd_status = vcd_open(vidc_enc_dev->device_handle, false, vid_enc_vcd_cb, client_ctx); client_ctx->stop_msg = 0; wait_for_completion(&client_ctx->event); file->private_data = client_ctx; - mutex_unlock(&vid_enc_device_p->lock); - return 0; + +out: + mutex_unlock(&vidc_enc_dev->lock); + return rc; } static int vid_enc_release(struct inode *inode, struct file *file) { struct video_client_ctx *client_ctx = file->private_data; - INFO("\n msm_vidc_enc: Inside %s()", __func__); + INFO("msm_vidc_enc: Inside %s\n", __func__); vid_enc_close_client(client_ctx); - vid_c_release_firmware(); #ifndef USE_RES_TRACKER vid_c_disable_clk(); #endif - INFO("\n msm_vidc_enc: Return from %s()", __func__); + INFO("msm_vidc_enc: Return from %s\n", __func__); return 0; } @@ -583,47 +533,40 @@ void vid_enc_interrupt_clear(void) void *vid_enc_map_dev_base_addr(void *device_name) { - return vid_enc_device_p->virt_base; + return vidc_enc_dev->virt_base; } static int vid_enc_vcd_init(void) { int rc; - struct vcd_init_config_type vcd_init_config; + struct vcd_init_config vcd_init_config; u32 i; - INFO("\n msm_vidc_enc: Inside %s()", __func__); - vid_enc_device_p->num_clients = 0; + INFO("msm_vidc_enc: Inside %s\n", __func__); + vidc_enc_dev->num_clients = 0; - for (i = 0; i < VID_ENC_MAX_ENCODER_CLIENTS; i++) { - memset((void *)&vid_enc_device_p->venc_clients[i], 0, - sizeof(vid_enc_device_p->venc_clients[i])); - } + for (i = 0; i < VID_ENC_MAX_ENCODER_CLIENTS; i++) + memset((void *)&vidc_enc_dev->venc_clients[i], 0, + sizeof(vidc_enc_dev->venc_clients[i])); - mutex_init(&vid_enc_device_p->lock); - vid_enc_device_p->virt_base = vid_c_get_ioaddr(); + mutex_init(&vidc_enc_dev->lock); + vidc_enc_dev->virt_base = vid_c_get_ioaddr(); - if (!vid_enc_device_p->virt_base) { - ERR("%s() : ioremap failed\n", __func__); + if (!vidc_enc_dev->virt_base) { + ERR("%s: ioremap failed\n", __func__); return -ENOMEM; } - vcd_init_config.p_device_name = "VID_C"; - vcd_init_config.pf_map_dev_base_addr = - vid_enc_map_dev_base_addr; - vcd_init_config.pf_interrupt_clr = - vid_enc_interrupt_clear; - vcd_init_config.pf_register_isr = - vid_enc_interrupt_register; - vcd_init_config.pf_deregister_isr = - vid_enc_interrupt_deregister; + vcd_init_config.device_name = "VID_C"; + vcd_init_config.pf_map_dev_base_addr = vid_enc_map_dev_base_addr; + vcd_init_config.pf_interrupt_clr = vid_enc_interrupt_clear; + vcd_init_config.pf_register_isr = vid_enc_interrupt_register; + vcd_init_config.pf_deregister_isr = vid_enc_interrupt_deregister; - rc = vcd_init(&vcd_init_config, - &vid_enc_device_p->device_handle); + rc = vcd_init(&vcd_init_config, &vidc_enc_dev->device_handle); if (rc) { - ERR("%s() : vcd_init failed\n", - __func__); + ERR("%s: vcd_init failed\n", __func__); return -ENODEV; } return 0; @@ -634,110 +577,81 @@ static int __init vid_enc_init(void) int rc = 0; struct device *class_devp; - INFO("\n msm_vidc_enc: Inside %s()", __func__); - vid_enc_device_p = kzalloc(sizeof(struct vid_enc_dev), - GFP_KERNEL); - if (!vid_enc_device_p) { - ERR("%s Unable to allocate memory for vid_enc_dev\n", - __func__); + INFO("msm_vidc_enc: Inside %s\n", __func__); + vidc_enc_dev = kzalloc(sizeof(struct vid_enc_dev), GFP_KERNEL); + if (!vidc_enc_dev) { + ERR("%s Unable to allocate memory for vid_enc_dev\n", __func__); return -ENOMEM; } - rc = alloc_chrdev_region(&vid_enc_dev_num, 0, 1, VID_ENC_NAME); + rc = alloc_chrdev_region(&vidc_enc_dev_num, 0, 1, VID_ENC_NAME); if (rc < 0) { - ERR("%s: alloc_chrdev_region Failed rc = %d\n", - __func__, rc); + ERR("%s: alloc_chrdev_region Failed rc = %d\n", __func__, rc); goto error_vid_enc_alloc_chrdev_region; } vid_enc_class = class_create(THIS_MODULE, VID_ENC_NAME); if (IS_ERR(vid_enc_class)) { rc = PTR_ERR(vid_enc_class); - ERR("%s: couldn't create vid_enc_class rc = %d\n", - __func__, rc); + ERR("%s: couldn't create vid_enc_class %d\n", __func__, rc); goto error_vid_enc_class_create; } - class_devp = device_create(vid_enc_class, NULL, - vid_enc_dev_num, NULL, VID_ENC_NAME); - + class_devp = device_create(vid_enc_class, NULL, vidc_enc_dev_num, NULL, + VID_ENC_NAME); if (IS_ERR(class_devp)) { rc = PTR_ERR(class_devp); - ERR("%s: class device_create failed %d\n", - __func__, rc); + ERR("%s: class device_create failed %d\n", __func__, rc); goto error_vid_enc_class_device_create; } - vid_enc_device_p->device = class_devp; + vidc_enc_dev->device = class_devp; - cdev_init(&vid_enc_device_p->cdev, &vid_enc_fops); - vid_enc_device_p->cdev.owner = THIS_MODULE; - rc = cdev_add(&(vid_enc_device_p->cdev), vid_enc_dev_num, 1); + cdev_init(&vidc_enc_dev->cdev, &vid_enc_fops); + vidc_enc_dev->cdev.owner = THIS_MODULE; + rc = cdev_add(&vidc_enc_dev->cdev, vidc_enc_dev_num, 1); if (rc < 0) { - ERR("%s: cdev_add failed %d\n", - __func__, rc); + ERR("%s: cdev_add failed %d\n", __func__, rc); goto error_vid_enc_cdev_add; } vid_enc_vcd_init(); return 0; error_vid_enc_cdev_add: - device_destroy(vid_enc_class, vid_enc_dev_num); + device_destroy(vid_enc_class, vidc_enc_dev_num); error_vid_enc_class_device_create: class_destroy(vid_enc_class); error_vid_enc_class_create: - unregister_chrdev_region(vid_enc_dev_num, 1); + unregister_chrdev_region(vidc_enc_dev_num, 1); error_vid_enc_alloc_chrdev_region: - kfree(vid_enc_device_p); + kfree(vidc_enc_dev); return rc; } static void __exit vid_enc_exit(void) { - INFO("\n msm_vidc_enc: Inside %s()", __func__); - cdev_del(&(vid_enc_device_p->cdev)); - device_destroy(vid_enc_class, vid_enc_dev_num); + INFO("msm_vidc_enc: Inside %s\n", __func__); + cdev_del(&vidc_enc_dev->cdev); + device_destroy(vid_enc_class, vidc_enc_dev_num); class_destroy(vid_enc_class); - unregister_chrdev_region(vid_enc_dev_num, 1); - kfree(vid_enc_device_p); - INFO("\n msm_vidc_enc: Return from %s()", __func__); + unregister_chrdev_region(vidc_enc_dev_num, 1); + kfree(vidc_enc_dev); + INFO("msm_vidc_enc: Return from %s\n", __func__); } + static int vid_enc_ioctl(struct inode *inode, struct file *file, - unsigned cmd, unsigned long arg) + unsigned cmd, unsigned long arg) { - struct video_client_ctx *client_ctx = NULL; + void __user *u_arg = (void __user *)arg; + struct video_client_ctx *client_ctx; struct venc_ioctl_msg venc_msg; - struct venc_basecfg base_config; - struct venc_switch encoder_switch; - struct venc_profile profile; - struct ven_profilelevel profile_level; - struct venc_sessionqp session_qp; - struct venc_intraperiod intraperiod; - struct venc_seqheader seq_header; - struct venc_seqheader seq_header_user; - struct venc_entropycfg entropy_cfg; - struct venc_dbcfg dbcfg; - struct venc_intrarefresh intrarefresh; - struct venc_multiclicecfg multiclicecfg; - struct venc_ratectrlcfg ratectrlcfg; - struct venc_voptimingcfg voptimingcfg; - struct venc_framerate framerate; - struct venc_targetbitrate targetbitrate; - struct venc_headerextension headerextension; - struct venc_qprange qprange; - struct venc_bufferflush bufferflush; - struct venc_allocatorproperty allocatorproperty; - struct venc_bufferpayload buffer_info; - enum venc_buffer_dir buffer_dir; - struct venc_buffer enc_buffer; - struct venc_msg cb_msg; - u32 result = TRUE; + u32 result = true; DBG("%s\n", __func__); - client_ctx = (struct video_client_ctx *)file->private_data; + client_ctx = file->private_data; if (!client_ctx) { ERR("!client_ctx. Cannot attach to device handle\n"); return -ENODEV; @@ -745,668 +659,558 @@ static int vid_enc_ioctl(struct inode *inode, struct file *file, switch (cmd) { case VEN_IOCTL_CMD_READ_NEXT_MSG: - if (copy_from_user(&venc_msg, (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_msg cb_msg; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_CMD_READ_NEXT_MSG\n"); result = vid_enc_get_next_msg(client_ctx, &cb_msg); if (!result) { ERR("VEN_IOCTL_CMD_READ_NEXT_MSG failed\n"); return -EIO; - } else - if (copy_to_user((void __user *) \ - venc_msg.outputparam, &cb_msg, - sizeof(cb_msg))) - return -EFAULT; + } + if (copy_to_user(venc_msg.out, &cb_msg, sizeof(cb_msg))) + return -EFAULT; break; - + } case VEN_IOCTL_CMD_STOP_READ_MSG: DBG("VEN_IOCTL_CMD_STOP_READ_MSG\n"); client_ctx->stop_msg = 1; wake_up(&client_ctx->msg_wait); break; - case VEN_IOCTL_CMD_ENCODE_FRAME: case VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER: - if (copy_from_user(&venc_msg, (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_buffer enc_buf; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; - DBG("VEN_IOCTL_CMD_ENCODE_FRAME" - "/VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER\n"); + DBG("VEN_IOCTL_CMD_ENCODE_FRAME/" + "VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER\n"); - if (copy_from_user(&enc_buffer, - (void __user *)venc_msg.inputparam, sizeof(enc_buffer))) + if (copy_from_user(&enc_buf, venc_msg.in, sizeof(enc_buf))) return -EFAULT; if (cmd == VEN_IOCTL_CMD_ENCODE_FRAME) - result = vid_enc_encode_frame(client_ctx, - &enc_buffer); + result = vid_enc_encode_frame(client_ctx, &enc_buf); else result = vid_enc_fill_output_buffer(client_ctx, - &enc_buffer); + &enc_buf); if (!result) { - DBG(" \n VEN_IOCTL_CMD_ENCODE_FRAME/" - "VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER failed"); + DBG("VEN_IOCTL_CMD_ENCODE_FRAME/" + "VEN_IOCTL_CMD_FILL_OUTPUT_BUFFER failed\n"); return -EIO; } break; - + } case VEN_IOCTL_SET_INPUT_BUFFER: case VEN_IOCTL_SET_OUTPUT_BUFFER: - - if (copy_from_user(&venc_msg, - (void __user *)arg, sizeof(venc_msg))) + { + struct venc_bufferpayload buf_info; + enum venc_buffer_dir buf_dir; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_SET_INPUT_BUFFER/VEN_IOCTL_SET_OUTPUT_BUFFER\n"); - if (copy_from_user(&buffer_info, - (void __user *)venc_msg.inputparam, - sizeof(buffer_info))) + if (copy_from_user(&buf_info, venc_msg.in, sizeof(buf_info))) return -EFAULT; - buffer_dir = VEN_BUFFER_TYPE_INPUT; + buf_dir = VEN_BUFFER_TYPE_INPUT; if (cmd == VEN_IOCTL_SET_OUTPUT_BUFFER) - buffer_dir = VEN_BUFFER_TYPE_OUTPUT; + buf_dir = VEN_BUFFER_TYPE_OUTPUT; - result = vid_enc_set_buffer(client_ctx, &buffer_info, - buffer_dir); + result = vid_enc_set_buffer(client_ctx, &buf_info, buf_dir); if (!result) { - DBG("\n VEN_IOCTL_SET_INPUT_BUFFER" - "/VEN_IOCTL_SET_OUTPUT_BUFFER failed"); + DBG("VEN_IOCTL_SET_INPUT_BUFFER" + "/VEN_IOCTL_SET_OUTPUT_BUFFER failed\n"); return -EIO; } break; + } case VEN_IOCTL_SET_INPUT_BUFFER_REQ: case VEN_IOCTL_SET_OUTPUT_BUFFER_REQ: - if (copy_from_user(&venc_msg, - (void __user *)arg, sizeof(venc_msg))) + { + struct venc_allocatorproperty alloc; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_SET_INPUT_BUFFER_REQ" "/VEN_IOCTL_SET_OUTPUT_BUFFER_REQ\n"); - if (copy_from_user(&allocatorproperty, - (void __user *)venc_msg.inputparam, - sizeof(allocatorproperty))) + if (copy_from_user(&alloc, venc_msg.in, sizeof(alloc))) return -EFAULT; - if (cmd == VEN_IOCTL_SET_OUTPUT_BUFFER_REQ) - result = vid_enc_set_buffer_req(client_ctx, - &allocatorproperty, FALSE); + if (cmd == VEN_IOCTL_SET_OUTPUT_BUFFER_REQ) + result = vid_enc_set_buffer_req(client_ctx, &alloc, + false); else - result = vid_enc_set_buffer_req(client_ctx, - &allocatorproperty, TRUE); + result = vid_enc_set_buffer_req(client_ctx, &alloc, + true); if (!result) { DBG("setting VEN_IOCTL_SET_OUTPUT_BUFFER_REQ/" - "VEN_IOCTL_SET_INPUT_BUFFER_REQ failed\n"); + "VEN_IOCTL_SET_INPUT_BUFFER_REQ failed\n"); return -EIO; } break; - + } case VEN_IOCTL_GET_INPUT_BUFFER_REQ: case VEN_IOCTL_GET_OUTPUT_BUFFER_REQ: - if (copy_from_user(&venc_msg, (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_allocatorproperty alloc; + + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_GET_INPUT_BUFFER_REQ/" - "VEN_IOCTL_GET_OUTPUT_BUFFER_REQ \n"); + "VEN_IOCTL_GET_OUTPUT_BUFFER_REQ\n"); if (cmd == VEN_IOCTL_GET_OUTPUT_BUFFER_REQ) - result = vid_enc_get_buffer_req(client_ctx, - &allocatorproperty, FALSE); + result = vid_enc_get_buffer_req(client_ctx, &alloc, + false); else - result = vid_enc_get_buffer_req(client_ctx, - &allocatorproperty, TRUE); + result = vid_enc_get_buffer_req(client_ctx, &alloc, + true); - if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &allocatorproperty, - sizeof(allocatorproperty))) - return -EFAULT; - } else - return -EIO; + if (!result) + return -EIO; + if (copy_to_user(venc_msg.out, &alloc, sizeof(alloc))) + return -EFAULT; break; - + } case VEN_IOCTL_CMD_FLUSH: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_bufferflush buf_flush; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_CMD_FLUSH\n"); - if (copy_from_user(&bufferflush, - (void __user *)venc_msg.inputparam, - sizeof(bufferflush))) + if (copy_from_user(&buf_flush, venc_msg.in, sizeof(buf_flush))) return -EFAULT; - INFO("\n %s(): Calling vid_enc_flush with mode = %lu", - __func__, bufferflush.flush_mode); - result = vid_enc_flush(client_ctx, &bufferflush); - + INFO("%s: Calling vid_enc_flush with mode = %lu\n", __func__, + buf_flush.flush_mode); + result = vid_enc_flush(client_ctx, &buf_flush); if (!result) { ERR("setting VEN_IOCTL_CMD_FLUSH failed\n"); return -EIO; } break; - + } case VEN_IOCTL_CMD_START: - INFO("\n %s(): Executing VEN_IOCTL_CMD_START", __func__); - result = vid_enc_start_stop(client_ctx, TRUE); + INFO("%s: Executing VEN_IOCTL_CMD_START\n", __func__); + result = vid_enc_start(client_ctx); if (!result) { ERR("setting VEN_IOCTL_CMD_START failed\n"); return -EIO; } break; - case VEN_IOCTL_CMD_STOP: - INFO("\n %s(): Executing VEN_IOCTL_CMD_STOP", __func__); - result = vid_enc_start_stop(client_ctx, FALSE); + INFO("%s: Executing VEN_IOCTL_CMD_STOP", __func__); + result = vid_enc_stop(client_ctx); if (!result) { ERR("setting VEN_IOCTL_CMD_STOP failed\n"); return -EIO; } stop_cmd = 1; break; - case VEN_IOCTL_CMD_PAUSE: - INFO("\n %s(): Executing VEN_IOCTL_CMD_PAUSE", __func__); - result = vid_enc_pause_resume(client_ctx, TRUE); + INFO("%s: Executing VEN_IOCTL_CMD_PAUSE\n", __func__); + result = vid_enc_pause(client_ctx); if (!result) { ERR("setting VEN_IOCTL_CMD_PAUSE failed\n"); return -EIO; } break; - case VEN_IOCTL_CMD_RESUME: - INFO("\n %s(): Executing VEN_IOCTL_CMD_RESUME", __func__); - result = vid_enc_pause_resume(client_ctx, FALSE); + INFO("%s: Executing VEN_IOCTL_CMD_RESUME\n", __func__); + result = vid_enc_resume(client_ctx); if (!result) { ERR("setting VEN_IOCTL_CMD_RESUME failed\n"); return -EIO; } break; - case VEN_IOCTL_SET_QP_RANGE: - - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_qprange qprange; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_SET_QP_RANGE\n"); - if (copy_from_user(&qprange, - (void __user *)venc_msg.inputparam, - sizeof(qprange))) + if (copy_from_user(&qprange, venc_msg.in, sizeof(qprange))) return -EFAULT; - result = vid_enc_set_get_qprange(client_ctx, - &qprange, TRUE); + result = vid_enc_set_get_qprange(client_ctx, &qprange, true); if (!result) { ERR("setting VEN_IOCTL_SET_QP_RANGE failed\n"); return -EIO; } break; - + } case VEN_IOCTL_GET_QP_RANGE: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_qprange qprange; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_GET_QP_RANGE\n"); - result = vid_enc_set_get_qprange(client_ctx, - &qprange, FALSE); + result = vid_enc_set_get_qprange(client_ctx, &qprange, false); - if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &qprange, - sizeof(qprange))) - return -EFAULT; - } else - return -EIO; + if (!result) + return -EIO; + if (copy_to_user(venc_msg.out, &qprange, sizeof(qprange))) + return -EFAULT; break; - + } case VEN_IOCTL_SET_HEC: - if (copy_from_user(&venc_msg, (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_headerextension ext; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_SET_HEC\n"); - if (copy_from_user(&headerextension, - (void __user *)venc_msg.inputparam, - sizeof(headerextension))) + if (copy_from_user(&ext, venc_msg.in, sizeof(ext))) return -EFAULT; - result = vid_enc_set_get_headerextension(client_ctx, - &headerextension, TRUE); + result = vid_enc_set_get_headerextension(client_ctx, &ext, + true); if (!result) { ERR("setting VEN_IOCTL_SET_HEC failed\n"); return -EIO; } break; - + } case VEN_IOCTL_GET_HEC: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_headerextension ext; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_GET_HEC\n"); - result = vid_enc_set_get_headerextension(client_ctx, - &headerextension, FALSE); + result = vid_enc_set_get_headerextension(client_ctx, &ext, + false); - if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &headerextension, - sizeof(headerextension))) - return -EFAULT; - } else - return -EIO; + if (!result) + return -EIO; + if (copy_to_user(venc_msg.out, &ext, sizeof(ext))) + return -EFAULT; break; - + } case VEN_IOCTL_SET_TARGET_BITRATE: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_targetbitrate rate; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_SET_TARGET_BITRATE\n"); - if (copy_from_user(&targetbitrate, - (void __user *)venc_msg.inputparam, - sizeof(targetbitrate))) + if (copy_from_user(&rate, venc_msg.in, sizeof(rate))) return -EFAULT; - result = vid_enc_set_get_bitrate(client_ctx, - &targetbitrate, TRUE); + result = vid_enc_set_get_bitrate(client_ctx, &rate, true); if (!result) { ERR("setting VEN_IOCTL_SET_TARGET_BITRATE failed\n"); return -EIO; } break; - + } case VEN_IOCTL_GET_TARGET_BITRATE: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_targetbitrate rate; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_GET_TARGET_BITRATE\n"); - result = vid_enc_set_get_bitrate(client_ctx, - &targetbitrate, FALSE); + result = vid_enc_set_get_bitrate(client_ctx, &rate, false); - if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &targetbitrate, - sizeof(targetbitrate))) + if (!result) + return -EIO; + if (copy_to_user(venc_msg.out, &rate, sizeof(rate))) return -EFAULT; - } else - return -EIO; break; - + } case VEN_IOCTL_SET_FRAME_RATE: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_framerate frm_rate; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_SET_FRAME_RATE\n"); - if (copy_from_user(&framerate, - (void __user *)venc_msg.inputparam, - sizeof(framerate))) + if (copy_from_user(&frm_rate, venc_msg.in, sizeof(frm_rate))) return -EFAULT; - result = vid_enc_set_get_framerate(client_ctx, - &framerate, TRUE); + result = vid_enc_set_get_framerate(client_ctx, &frm_rate, + true); if (!result) { ERR("setting VEN_IOCTL_SET_FRAME_RATE failed\n"); return -EIO; } break; - + } case VEN_IOCTL_GET_FRAME_RATE: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_framerate frm_rate; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_GET_FRAME_RATE\n"); - result = vid_enc_set_get_framerate(client_ctx, &framerate, - FALSE); + result = vid_enc_set_get_framerate(client_ctx, &frm_rate, + false); if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &framerate, - sizeof(framerate))) + if (copy_to_user(venc_msg.out, + &frm_rate, sizeof(frm_rate))) return -EFAULT; } else - return -EIO; + return -EIO; break; - + } case VEN_IOCTL_SET_VOP_TIMING_CFG: - - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_voptimingcfg timing; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_SET_VOP_TIMING_CFG\n"); - if (copy_from_user( - &voptimingcfg, (void __user *)venc_msg.inputparam, - sizeof(voptimingcfg))) + if (copy_from_user(&timing, venc_msg.in, sizeof(timing))) return -EFAULT; - result = vid_enc_set_get_voptimingcfg(client_ctx, - &voptimingcfg, TRUE); - + result = vid_enc_set_get_voptimingcfg(client_ctx, &timing, + true); if (!result) { ERR("setting VEN_IOCTL_SET_VOP_TIMING_CFG failed\n"); return -EIO; } break; - + } case VEN_IOCTL_GET_VOP_TIMING_CFG: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_voptimingcfg timing; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_GET_VOP_TIMING_CFG\n"); - result = vid_enc_set_get_voptimingcfg(client_ctx, - &voptimingcfg, FALSE); - - if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &voptimingcfg, - sizeof(voptimingcfg))) - return -EFAULT; - } else - return -EIO; + result = vid_enc_set_get_voptimingcfg(client_ctx, &timing, + false); + if (!result) + return -EIO; + if (copy_to_user(venc_msg.out, &timing, sizeof(timing))) + return -EFAULT; break; - + } case VEN_IOCTL_SET_RATE_CTRL_CFG: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_ratectrlcfg rate_ctrl; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_SET_RATE_CTRL_CFG\n"); - if (copy_from_user(&ratectrlcfg, - (void __user *)venc_msg.inputparam, - sizeof(ratectrlcfg))) + if (copy_from_user(&rate_ctrl, venc_msg.in, sizeof(rate_ctrl))) return -EFAULT; - result = vid_enc_set_get_ratectrlcfg(client_ctx, - &ratectrlcfg, TRUE); - + result = vid_enc_set_get_ratectrlcfg(client_ctx, &rate_ctrl, + true); if (!result) { ERR("setting VEN_IOCTL_SET_RATE_CTRL_CFG failed\n"); return -EIO; } - break; - + } case VEN_IOCTL_GET_RATE_CTRL_CFG: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_ratectrlcfg rate_ctrl; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_SET_RATE_CTRL_CFG\n"); - result = vid_enc_set_get_ratectrlcfg(client_ctx, - &ratectrlcfg, FALSE); - - if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &ratectrlcfg, - sizeof(ratectrlcfg))) - return -EFAULT; - } else - return -EIO; + result = vid_enc_set_get_ratectrlcfg(client_ctx, &rate_ctrl, + false); + if (!result) + return -EIO; + if (copy_to_user(venc_msg.out, &rate_ctrl, sizeof(rate_ctrl))) + return -EFAULT; break; - + } case VEN_IOCTL_SET_MULTI_SLICE_CFG: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_multiclicecfg slice; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_SET_MULTI_SLICE_CFG\n"); - if (copy_from_user(&multiclicecfg, - (void __user *)venc_msg.inputparam, - sizeof(multiclicecfg))) + if (copy_from_user(&slice, venc_msg.in, sizeof(slice))) return -EFAULT; - result = vid_enc_set_get_multiclicecfg(client_ctx, - &multiclicecfg, TRUE); - + result = vid_enc_set_get_multiclicecfg(client_ctx, &slice, + true); if (!result) { ERR("setting VEN_IOCTL_SET_MULTI_SLICE_CFG failed\n"); return -EIO; } break; - + } case VEN_IOCTL_GET_MULTI_SLICE_CFG: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_multiclicecfg slice; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_GET_MULTI_SLICE_CFG\n"); - result = vid_enc_set_get_multiclicecfg(client_ctx, - &multiclicecfg, FALSE); - - if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &multiclicecfg, - sizeof(multiclicecfg))) - return -EFAULT; - } else - return -EIO; + result = vid_enc_set_get_multiclicecfg(client_ctx, &slice, + false); + if (!result) + return -EIO; + if (copy_to_user(venc_msg.out, &slice, sizeof(slice))) + return -EFAULT; break; + } case VEN_IOCTL_SET_INTRA_REFRESH: - - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_intrarefresh refresh; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_SET_INTRA_REFRESH\n"); - if (copy_from_user(&intrarefresh, - (void __user *)venc_msg.inputparam, - sizeof(intrarefresh))) + if (copy_from_user(&refresh, venc_msg.in, sizeof(refresh))) return -EFAULT; - result = vid_enc_set_get_intrarefresh(client_ctx, - &intrarefresh, TRUE); - + result = vid_enc_set_get_intrarefresh(client_ctx, &refresh, + true); if (!result) { ERR("setting VEN_IOCTL_SET_INTRA_REFRESH failed\n"); return -EIO; } - break; - + } case VEN_IOCTL_GET_INTRA_REFRESH: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_intrarefresh refresh; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_GET_DEBLOCKING_CFG\n"); - result = vid_enc_set_get_intrarefresh(client_ctx, - &intrarefresh, FALSE); - - if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &intrarefresh, - sizeof(intrarefresh))) - return -EFAULT; - } else - return -EIO; + result = vid_enc_set_get_intrarefresh(client_ctx, &refresh, + false); + if (!result) + return -EIO; + if (copy_to_user(venc_msg.out, &refresh, sizeof(refresh))) + return -EFAULT; break; - + } case VEN_IOCTL_SET_DEBLOCKING_CFG: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_dbcfg dbcfg; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_SET_DEBLOCKING_CFG\n"); - if (copy_from_user(&dbcfg, - (void __user *)venc_msg.inputparam, - sizeof(dbcfg))) + if (copy_from_user(&dbcfg, venc_msg.in, sizeof(dbcfg))) return -EFAULT; - - result = vid_enc_set_get_dbcfg(client_ctx, - &dbcfg, TRUE); + result = vid_enc_set_get_dbcfg(client_ctx, &dbcfg, true); if (!result) { ERR("setting VEN_IOCTL_SET_DEBLOCKING_CFG failed\n"); return -EIO; } break; - + } case VEN_IOCTL_GET_DEBLOCKING_CFG: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_dbcfg dbcfg; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_GET_DEBLOCKING_CFG\n"); - result = vid_enc_set_get_dbcfg(client_ctx, - &dbcfg, FALSE); - - if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &dbcfg, - sizeof(dbcfg))) - return -EFAULT; - } else - return -EIO; - + result = vid_enc_set_get_dbcfg(client_ctx, &dbcfg, false); + if (!result) + return -EIO; + if (copy_to_user(venc_msg.out, &dbcfg, sizeof(dbcfg))) + return -EFAULT; break; + } case VEN_IOCTL_SET_ENTROPY_CFG: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_entropycfg entropy; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_SET_ENTROPY_CFG\n"); - if (copy_from_user(&entropy_cfg, - (void __user *)venc_msg.inputparam, - sizeof(entropy_cfg))) + if (copy_from_user(&entropy, venc_msg.in, sizeof(entropy))) return -EFAULT; - result = vid_enc_set_get_entropy_cfg(client_ctx, - &entropy_cfg, TRUE); + result = vid_enc_set_get_entropy_cfg(client_ctx, &entropy, + true); if (!result) { ERR("setting VEN_IOCTL_SET_ENTROPY_CFG failed\n"); return -EIO; } break; - + } case VEN_IOCTL_GET_ENTROPY_CFG: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_entropycfg entropy; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_GET_ENTROPY_CFG\n"); - result = vid_enc_set_get_entropy_cfg(client_ctx, - &entropy_cfg, FALSE); - - if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &entropy_cfg, - sizeof(entropy_cfg))) - return -EFAULT; - } else + result = vid_enc_set_get_entropy_cfg(client_ctx, &entropy, + false); + if (!result) return -EIO; - + if (copy_to_user(venc_msg.out, &entropy, sizeof(entropy))) + return -EFAULT; break; - + } case VEN_IOCTL_GET_SEQUENCE_HDR: - - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + int rc = 0; + struct venc_seqheader hdr; + struct venc_seqheader hdr_user; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_GET_SEQUENCE_HDR\n"); - if (copy_from_user(&seq_header, - (void __user *)venc_msg.inputparam, - sizeof(seq_header))) + if (copy_from_user(&hdr, venc_msg.in, sizeof(hdr))) return -EFAULT; - - if (copy_from_user(&seq_header_user, - (void __user *)venc_msg.inputparam, - sizeof(seq_header_user))) + if (copy_from_user(&hdr_user, venc_msg.in, sizeof(hdr_user))) return -EFAULT; - seq_header.hdrbufptr = NULL; - result = vid_enc_get_sequence_header(client_ctx, - &seq_header); - - if (result) { - if ((copy_to_user( - (void __user *)seq_header_user.hdrbufptr, - seq_header.hdrbufptr, seq_header.hdrlen)) || - (copy_to_user( - (void __user *)&seq_header_user.hdrlen, - &seq_header.hdrlen, - sizeof(seq_header.hdrlen))) - ) { - kfree(seq_header.hdrbufptr); - seq_header.hdrbufptr = NULL; - return -EFAULT; - } - } else { - kfree(seq_header.hdrbufptr); - seq_header.hdrbufptr = NULL; - return -EIO; - } - - kfree(seq_header.hdrbufptr); - seq_header.hdrbufptr = NULL; + hdr.buf = NULL; + result = vid_enc_get_sequence_header(client_ctx, &hdr); + if (!result) + rc = -EIO; + if (!rc || copy_to_user(hdr_user.buf, hdr.buf, hdr.hdr_len)) + rc = -EFAULT; + if (!rc || copy_to_user(&hdr_user.hdr_len, &hdr.hdr_len, + sizeof(hdr.hdr_len))) + rc = -EFAULT; + kfree(hdr.buf); + hdr.buf = NULL; + if (rc) + return rc; break; - + } case VEN_IOCTL_GET_CAPABILITY: return -EIO; - break; case VEN_IOCTL_CMD_REQUEST_IFRAME: result = vid_enc_request_iframe(client_ctx); if (!result) { @@ -1414,319 +1218,251 @@ static int vid_enc_ioctl(struct inode *inode, struct file *file, return -EIO; } break; - case VEN_IOCTL_SET_INTRA_PERIOD: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_intraperiod period; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_SET_INTRA_PERIOD\n"); - if (copy_from_user(&intraperiod, - (void __user *)venc_msg.inputparam, - sizeof(intraperiod))) + if (copy_from_user(&period, venc_msg.in, sizeof(period))) return -EFAULT; - result = vid_enc_set_get_intraperiod(client_ctx, - &intraperiod, TRUE); - + result = vid_enc_set_get_intraperiod(client_ctx, &period, + true); if (!result) { ERR("setting VEN_IOCTL_SET_INTRA_PERIOD failed\n"); return -EIO; } break; - + } case VEN_IOCTL_GET_INTRA_PERIOD: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_intraperiod period; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_GET_SESSION_QP\n"); - result = vid_enc_set_get_intraperiod(client_ctx, - &intraperiod, FALSE); - - if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &intraperiod, - sizeof(intraperiod))) - return -EFAULT; - } else - return -EIO; + result = vid_enc_set_get_intraperiod(client_ctx, &period, + false); + if (!result) + return -EIO; + if (copy_to_user(venc_msg.out, &period, sizeof(period))) + return -EFAULT; break; - + } case VEN_IOCTL_SET_SESSION_QP: - if (copy_from_user( - &venc_msg, (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_sessionqp qp; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_SET_SESSION_QP\n"); - if (copy_from_user(&session_qp, - (void __user *)venc_msg.inputparam, - sizeof(session_qp))) + if (copy_from_user(&qp, venc_msg.in, sizeof(qp))) return -EFAULT; - result = vid_enc_set_get_session_qp(client_ctx, - &session_qp, TRUE); - + result = vid_enc_set_get_session_qp(client_ctx, &qp, true); if (!result) { ERR("setting VEN_IOCTL_SET_SESSION_QP failed\n"); return -EIO; } break; - + } case VEN_IOCTL_GET_SESSION_QP: - if (copy_from_user( - &venc_msg, (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_sessionqp qp; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_GET_SESSION_QP\n"); - result = vid_enc_set_get_session_qp(client_ctx, - &session_qp, FALSE); + result = vid_enc_set_get_session_qp(client_ctx, &qp, false); - if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &session_qp, - sizeof(session_qp))) - return -EFAULT; - } else - return -EIO; + if (!result) + return -EIO; + if (copy_to_user(venc_msg.out, &qp, sizeof(qp))) + return -EFAULT; break; - + } case VEN_IOCTL_SET_PROFILE_LEVEL: - - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct ven_profilelevel level; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_SET_PROFILE_LEVEL\n"); - if (copy_from_user(&profile_level, - (void __user *)venc_msg.inputparam, - sizeof(profile_level))) + if (copy_from_user(&level, venc_msg.in, sizeof(level))) return -EFAULT; - result = vid_enc_set_get_profile_level(client_ctx, - &profile_level, TRUE); - + result = vid_enc_set_get_profile_level(client_ctx, &level, + true); if (!result) { ERR("setting VEN_IOCTL_SET_PROFILE_LEVEL failed\n"); return -EIO; } - break; + } case VEN_IOCTL_GET_PROFILE_LEVEL: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct ven_profilelevel level; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_GET_CODEC_PROFILE\n"); - result = vid_enc_set_get_profile_level(client_ctx, - &profile_level, FALSE); - - if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &profile_level, - sizeof(profile_level))) - return -EFAULT; - } else - return -EIO; + result = vid_enc_set_get_profile_level(client_ctx, &level, + false); + if (!result) + return -EIO; + if (copy_to_user(venc_msg.out, &level, sizeof(level))) + return -EFAULT; break; - + } case VEN_IOCTL_SET_CODEC_PROFILE: - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_profile profile; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_SET_CODEC_PROFILE\n"); - if (copy_from_user(&profile, - (void __user *)venc_msg.inputparam, - sizeof(profile))) + if (copy_from_user(&profile, venc_msg.in, sizeof(profile))) return -EFAULT; - result = vid_enc_set_get_profile(client_ctx, - &profile, TRUE); - + result = vid_enc_set_get_profile(client_ctx, &profile, true); if (!result) { ERR("setting VEN_IOCTL_SET_CODEC_PROFILE failed\n"); return -EIO; } break; - + } case VEN_IOCTL_GET_CODEC_PROFILE: - if (copy_from_user( - &venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_profile profile; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_GET_CODEC_PROFILE\n"); - result = vid_enc_set_get_profile(client_ctx, - &profile, FALSE); - - if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &profile, - sizeof(profile))) - return -EFAULT; - } else - return -EIO; + result = vid_enc_set_get_profile(client_ctx, &profile, false); + if (!result) + return -EIO; + if (copy_to_user(venc_msg.out, &profile, sizeof(profile))) + return -EFAULT; break; - - + } case VEN_IOCTL_SET_SHORT_HDR: - if (copy_from_user(&venc_msg, - (void __user *)arg, sizeof(venc_msg))) + { + struct venc_switch enc_switch; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("Getting VEN_IOCTL_SET_SHORT_HDR\n"); - if (copy_from_user( - &encoder_switch, - (void __user *)venc_msg.inputparam, - sizeof(encoder_switch))) + if (copy_from_user(&enc_switch, venc_msg.in, + sizeof(enc_switch))) return -EFAULT; - result = vid_enc_set_get_short_header(client_ctx, - &encoder_switch, TRUE); - + result = vid_enc_set_get_short_header(client_ctx, &enc_switch, + true); if (!result) { ERR("setting VEN_IOCTL_SET_SHORT_HDR failed\n"); return -EIO; } break; - + } case VEN_IOCTL_GET_SHORT_HDR: - if (copy_from_user(&venc_msg, - (void __user *)arg, sizeof(venc_msg))) + { + struct venc_switch enc_switch; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_GET_LIVE_MODE\n"); - result = vid_enc_set_get_short_header(client_ctx, - &encoder_switch, FALSE); - - if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &encoder_switch, - sizeof(encoder_switch))) - return -EFAULT; - } else - return -EIO; - + result = vid_enc_set_get_short_header(client_ctx, &enc_switch, + false); + if (!result) + return -EIO; + if (copy_to_user(venc_msg.out, &enc_switch, sizeof(enc_switch))) + return -EFAULT; break; - + } case VEN_IOCTL_SET_BASE_CFG: - + { + struct venc_basecfg base; DBG("VEN_IOCTL_SET_BASE_CFG\n"); - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; - if (copy_from_user(&base_config, - (void __user *)venc_msg.inputparam, - sizeof(base_config))) + if (copy_from_user(&base, venc_msg.in, sizeof(base))) return -EFAULT; DBG("setting VEN_IOCTL_SET_BASE_CFG\n"); - result = vid_enc_set_get_base_cfg(client_ctx, - &base_config, TRUE); - + result = vid_enc_set_get_base_cfg(client_ctx, &base, true); if (!result) { ERR("setting VEN_IOCTL_SET_BASE_CFG failed\n"); return -EIO; } break; - + } case VEN_IOCTL_GET_BASE_CFG: + { + struct venc_basecfg base; DBG("VEN_IOCTL_GET_BASE_CFG\n"); - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("Getting VEN_IOCTL_SET_BASE_CFG\n"); - result = vid_enc_set_get_base_cfg(client_ctx, - &base_config, FALSE); - - if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &base_config, - sizeof(base_config))) - return -EFAULT; - } else + result = vid_enc_set_get_base_cfg(client_ctx, &base, false); + if (!result) return -EIO; - + if (copy_to_user(venc_msg.out, &base, sizeof(base))) + return -EFAULT; break; - + } case VEN_IOCTL_SET_LIVE_MODE: - - if (copy_from_user(&venc_msg, - (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_switch enc_switch; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("Getting VEN_IOCTL_SET_LIVE_MODE\n"); - if (copy_from_user(&encoder_switch, - (void __user *)venc_msg.inputparam, - sizeof(encoder_switch))) + if (copy_from_user(&enc_switch, venc_msg.in, sizeof(enc_switch))) return -EFAULT; - result = vid_enc_set_get_live_mode(client_ctx, - &encoder_switch, TRUE); - + result = vid_enc_set_get_live_mode(client_ctx, &enc_switch, + true); if (!result) { ERR("setting VEN_IOCTL_SET_LIVE_MODE failed\n"); return -EIO; } break; - + } case VEN_IOCTL_GET_LIVE_MODE: - - if (copy_from_user( - &venc_msg, (void __user *)arg, - sizeof(venc_msg))) + { + struct venc_switch enc_switch; + if (copy_from_user(&venc_msg, u_arg, sizeof(venc_msg))) return -EFAULT; DBG("VEN_IOCTL_GET_LIVE_MODE\n"); - result = vid_enc_set_get_live_mode(client_ctx, - &encoder_switch, FALSE); - - if (result) { - if (copy_to_user( - (void __user *)venc_msg.outputparam, - &encoder_switch, - sizeof(encoder_switch))) - return -EFAULT; - } else + result = vid_enc_set_get_live_mode(client_ctx, &enc_switch, + false); + if (!result) return -EIO; - + if (copy_to_user(venc_msg.out, &enc_switch, sizeof(enc_switch))) + return -EFAULT; break; + } case VEN_IOCTL_SET_AC_PREDICTION: case VEN_IOCTL_GET_AC_PREDICTION: case VEN_IOCTL_SET_RVLC: @@ -1736,10 +1472,8 @@ static int vid_enc_ioctl(struct inode *inode, struct file *file, case VEN_IOCTL_SET_DATA_PARTITION: case VEN_IOCTL_GET_DATA_PARTITION: default: - ERR("%s(): Unsupported ioctl %d\n", __func__, cmd); + ERR("%s: Unsupported ioctl %d\n", __func__, cmd); return -ENOTTY; - - break; } return 0; } diff --git a/drivers/misc/video_core/720p/enc/venc_internal.c b/drivers/misc/video_core/720p/enc/venc_internal.c index 2b84441ee1ee0..11e8d6651f4a2 100644 --- a/drivers/misc/video_core/720p/enc/venc_internal.c +++ b/drivers/misc/video_core/720p/enc/venc_internal.c @@ -47,1538 +47,1413 @@ #define ERR(x...) printk(KERN_ERR x) u32 vid_enc_set_get_base_cfg(struct video_client_ctx *client_ctx, - struct venc_basecfg *base_config, u32 set_flag) + struct venc_basecfg *config, u32 set_flag) { struct venc_targetbitrate venc_bitrate; struct venc_framerate frame_rate; u32 current_codec_type; - if (!client_ctx || !base_config) - return FALSE; + if (!client_ctx || !config) + return false; - if (!vid_enc_set_get_codec(client_ctx, ¤t_codec_type, FALSE)) - return FALSE; + if (!vid_enc_set_get_codec(client_ctx, ¤t_codec_type, false)) + return false; - DBG("%s(): Current Codec Type = %u\n", __func__, current_codec_type); - if (current_codec_type != base_config->codectype) { - if (!vid_enc_set_get_codec(client_ctx, - (u32 *)&base_config->codectype, set_flag)) - return FALSE; + DBG("%s: Current Codec Type = %u\n", __func__, current_codec_type); + if (current_codec_type != config->codectype) { + if (!vid_enc_set_get_codec(client_ctx, &config->codectype, + set_flag)) + return false; } - if (!vid_enc_set_get_inputformat(client_ctx, - (u32 *)&base_config->inputformat, set_flag)) - return FALSE; + if (!vid_enc_set_get_inputformat(client_ctx, &config->inputformat, + set_flag)) + return false; - if (!vid_enc_set_get_framesize(client_ctx, - (u32 *)&base_config->input_height, - (u32 *)&base_config->input_width, set_flag)) - return FALSE; + if (!vid_enc_set_get_framesize(client_ctx, &config->input_height, + &config->input_width, set_flag)) + return false; if (set_flag) - venc_bitrate.target_bitrate = base_config->targetbitrate; + venc_bitrate.target_bitrate = config->targetbitrate; if (!vid_enc_set_get_bitrate(client_ctx, &venc_bitrate, set_flag)) - return FALSE; + return false; if (!set_flag) - base_config->targetbitrate = venc_bitrate.target_bitrate; + config->targetbitrate = venc_bitrate.target_bitrate; if (set_flag) { - frame_rate.fps_denominator = base_config->fps_den; - frame_rate.fps_numerator = base_config->fps_num; + frame_rate.fps_denominator = config->fps_den; + frame_rate.fps_numerator = config->fps_num; } if (!vid_enc_set_get_framerate(client_ctx, &frame_rate, set_flag)) - return FALSE; + return false; if (!set_flag) { - base_config->fps_den = frame_rate.fps_denominator; - base_config->fps_num = frame_rate.fps_numerator; + config->fps_den = frame_rate.fps_denominator; + config->fps_num = frame_rate.fps_numerator; } - return TRUE; + return true; } u32 vid_enc_set_get_inputformat(struct video_client_ctx *client_ctx, - u32 *input_format, u32 set_flag) + u32 *input_format, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_buffer_format_type format_type; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_buffer_format format_type; u32 vcd_status = VCD_ERR_FAIL; - u32 status = TRUE; + u32 status = true; if (!client_ctx || !input_format) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_BUFFER_FORMAT; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_buffer_format_type); + vcd_property_hdr.id = VCD_I_BUFFER_FORMAT; + vcd_property_hdr.sz = sizeof(struct vcd_property_buffer_format); if (set_flag) { switch (*input_format) { case VEN_INPUTFMT_NV12: - format_type.e_buffer_format = VCD_BUFFER_FORMAT_NV12; + format_type.buffer_format = VCD_BUFFER_FORMAT_NV12; break; case VEN_INPUTFMT_NV21: - format_type.e_buffer_format = - VCD_BUFFER_FORMAT_TILE_4x2; + format_type.buffer_format = VCD_BUFFER_FORMAT_TILE_4x2; break; default: - status = FALSE; + status = false; break; } - if (status) { - vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, &format_type); - if (vcd_status) { - status = FALSE; - ERR("%s(): Set VCD_I_BUFFER_FORMAT Failed\n", - __func__); - } + if (!status) + return status; + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &format_type); + if (vcd_status) { + status = false; + ERR("%s(): Set VCD_I_BUFFER_FORMAT Failed\n", __func__); } } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &format_type); + &vcd_property_hdr, &format_type); if (vcd_status) { - status = FALSE; + status = false; ERR("%s(): Get VCD_I_BUFFER_FORMAT Failed\n", __func__); - } else { - switch (format_type.e_buffer_format) { - case VCD_BUFFER_FORMAT_NV12: - *input_format = VEN_INPUTFMT_NV12; - break; - case VCD_BUFFER_FORMAT_TILE_4x2: - *input_format = VEN_INPUTFMT_NV21; - break; - default: - status = FALSE; - break; - } + return status; + } + switch (format_type.buffer_format) { + case VCD_BUFFER_FORMAT_NV12: + *input_format = VEN_INPUTFMT_NV12; + break; + case VCD_BUFFER_FORMAT_TILE_4x2: + *input_format = VEN_INPUTFMT_NV21; + break; + default: + status = false; + break; } } return status; } u32 vid_enc_set_get_codec(struct video_client_ctx *client_ctx, u32 *codec_type, - u32 set_flag) + u32 set_flag) { - struct vcd_property_codec_type vcd_property_codec; - struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_codec vcd_property_codec; + struct vcd_property_hdr vcd_property_hdr; u32 vcd_status = VCD_ERR_FAIL; - u32 status = TRUE; + u32 status = true; if (!client_ctx || !codec_type) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_CODEC; - vcd_property_hdr.n_size = sizeof(struct vcd_property_codec_type); + vcd_property_hdr.id = VCD_I_CODEC; + vcd_property_hdr.sz = sizeof(struct vcd_property_codec); if (set_flag) { switch (*codec_type) { case VEN_CODEC_MPEG4: - vcd_property_codec.e_codec = VCD_CODEC_MPEG4; + vcd_property_codec.codec = VCD_CODEC_MPEG4; break; case VEN_CODEC_H263: - vcd_property_codec.e_codec = VCD_CODEC_H263; + vcd_property_codec.codec = VCD_CODEC_H263; break; case VEN_CODEC_H264: - vcd_property_codec.e_codec = VCD_CODEC_H264; + vcd_property_codec.codec = VCD_CODEC_H264; break; default: - status = FALSE; + status = false; break; } - if (status) { - vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, &vcd_property_codec); - if (vcd_status) { - status = FALSE; - ERR("%s(): Set VCD_I_CODEC Failed\n", __func__); - } + if (!status) + return status; + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &vcd_property_codec); + if (vcd_status) { + status = false; + ERR("%s: Set VCD_I_CODEC Failed\n", __func__); } - } else { + } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &vcd_property_codec); + &vcd_property_hdr, &vcd_property_codec); if (vcd_status) { - status = FALSE; - ERR("%s(): Get VCD_I_CODEC Failed\n", - __func__); - } else { - switch (vcd_property_codec.e_codec) { - case VCD_CODEC_H263: - *codec_type = VEN_CODEC_H263; - break; - case VCD_CODEC_H264: - *codec_type = VEN_CODEC_H264; - break; - case VCD_CODEC_MPEG4: - *codec_type = VEN_CODEC_MPEG4; - break; - case VCD_CODEC_DIVX_3: - case VCD_CODEC_DIVX_4: - case VCD_CODEC_DIVX_5: - case VCD_CODEC_DIVX_6: - case VCD_CODEC_MPEG1: - case VCD_CODEC_MPEG2: - case VCD_CODEC_VC1: - case VCD_CODEC_VC1_RCV: - case VCD_CODEC_XVID: - default: - status = FALSE; - break; - } + status = false; + ERR("%s(): Get VCD_I_CODEC Failed\n", __func__); + return status; + } + switch (vcd_property_codec.codec) { + case VCD_CODEC_H263: + *codec_type = VEN_CODEC_H263; + break; + case VCD_CODEC_H264: + *codec_type = VEN_CODEC_H264; + break; + case VCD_CODEC_MPEG4: + *codec_type = VEN_CODEC_MPEG4; + break; + case VCD_CODEC_DIVX_3: + case VCD_CODEC_DIVX_4: + case VCD_CODEC_DIVX_5: + case VCD_CODEC_DIVX_6: + case VCD_CODEC_MPEG1: + case VCD_CODEC_MPEG2: + case VCD_CODEC_VC1: + case VCD_CODEC_VC1_RCV: + case VCD_CODEC_XVID: + default: + status = false; + break; } } return status; } -u32 vid_enc_set_get_framesize(struct video_client_ctx *client_ctx, - u32 *height, u32 *width, u32 set_flag) +u32 vid_enc_set_get_framesize(struct video_client_ctx *client_ctx, u32 *height, + u32 *width, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_frame_size_type frame_size; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_frame_size frame_size; u32 vcd_status = VCD_ERR_FAIL; if (!client_ctx || !height || !width) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_FRAME_SIZE; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_frame_size_type); + vcd_property_hdr.id = VCD_I_FRAME_SIZE; + vcd_property_hdr.sz = sizeof(struct vcd_property_frame_size); if (set_flag) { - frame_size.n_height = *height; - frame_size.n_width = *width; + frame_size.height = *height; + frame_size.width = *width; vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, &frame_size); + &vcd_property_hdr, &frame_size); if (vcd_status) { - ERR("%s(): Set VCD_I_FRAME_SIZE Failed\n", - __func__); - return FALSE; + ERR("%s(): Set VCD_I_FRAME_SIZE Failed\n", __func__); + return false; } - } else { + } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &frame_size); + &vcd_property_hdr, &frame_size); if (vcd_status) { - ERR("%s(): Get VCD_I_FRAME_SIZE Failed\n", - __func__); - return FALSE; + ERR("%s(): Get VCD_I_FRAME_SIZE Failed\n", __func__); + return false; } - *height = frame_size.n_height; - *width = frame_size.n_width; + *height = frame_size.height; + *width = frame_size.width; } - return TRUE; + return true; } u32 vid_enc_set_get_bitrate(struct video_client_ctx *client_ctx, - struct venc_targetbitrate *venc_bitrate, u32 set_flag) + struct venc_targetbitrate *venc_bitrate, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_target_bitrate_type bit_rate; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_target_bitrate bit_rate; u32 vcd_status = VCD_ERR_FAIL; if (!client_ctx || !venc_bitrate) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_TARGET_BITRATE; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_target_bitrate_type); + vcd_property_hdr.id = VCD_I_TARGET_BITRATE; + vcd_property_hdr.sz = sizeof(struct vcd_property_target_bitrate); if (set_flag) { - bit_rate.n_target_bitrate = venc_bitrate->target_bitrate; + bit_rate.target_bitrate = venc_bitrate->target_bitrate; vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, &bit_rate); + &vcd_property_hdr, &bit_rate); if (vcd_status) { ERR("%s(): Set VCD_I_TARGET_BITRATE Failed\n", - __func__); - return FALSE; + __func__); + return false; } } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &bit_rate); + &vcd_property_hdr, &bit_rate); if (vcd_status) { ERR("%s(): Get VCD_I_TARGET_BITRATE Failed\n", - __func__); - return FALSE; + __func__); + return false; } - venc_bitrate->target_bitrate = bit_rate.n_target_bitrate; + venc_bitrate->target_bitrate = bit_rate.target_bitrate; } - return TRUE; + return true; } u32 vid_enc_set_get_framerate(struct video_client_ctx *client_ctx, - struct venc_framerate *frame_rate, u32 set_flag) + struct venc_framerate *frame_rate, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_frame_rate_type vcd_frame_rate; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_frame_rate vcd_frame_rate; u32 vcd_status = VCD_ERR_FAIL; if (!client_ctx || !frame_rate) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_FRAME_RATE; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_frame_rate_type); + vcd_property_hdr.id = VCD_I_FRAME_RATE; + vcd_property_hdr.sz = sizeof(struct vcd_property_frame_rate); if (set_flag) { - vcd_frame_rate.n_fps_denominator = frame_rate->fps_denominator; - vcd_frame_rate.n_fps_numerator = frame_rate->fps_numerator; + vcd_frame_rate.fps_denominator = frame_rate->fps_denominator; + vcd_frame_rate.fps_numerator = frame_rate->fps_numerator; vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, &vcd_frame_rate); + &vcd_property_hdr, &vcd_frame_rate); if (vcd_status) { - ERR("%s(): Set VCD_I_FRAME_RATE Failed\n", - __func__); - return FALSE; + ERR("%s(): Set VCD_I_FRAME_RATE Failed\n", __func__); + return false; } } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &vcd_frame_rate); + &vcd_property_hdr, &vcd_frame_rate); if (vcd_status) { - ERR("%s(): Get VCD_I_FRAME_RATE Failed\n", - __func__); - return FALSE; + ERR("%s(): Get VCD_I_FRAME_RATE Failed\n", __func__); + return false; } - frame_rate->fps_denominator = vcd_frame_rate.n_fps_denominator; - frame_rate->fps_numerator = vcd_frame_rate.n_fps_numerator; + frame_rate->fps_denominator = vcd_frame_rate.fps_denominator; + frame_rate->fps_numerator = vcd_frame_rate.fps_numerator; } - return TRUE; + return true; } u32 vid_enc_set_get_live_mode(struct video_client_ctx *client_ctx, - struct venc_switch *encoder_switch, u32 set_flag) + struct venc_switch *encoder_switch, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_live_type live_mode; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_live live_mode; u32 vcd_status = VCD_ERR_FAIL; if (!client_ctx) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_LIVE; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_live_type); + vcd_property_hdr.id = VCD_I_LIVE; + vcd_property_hdr.sz = sizeof(struct vcd_property_live); if (set_flag) { - live_mode.b_live = 1; + live_mode.live = 1; if (!encoder_switch->status) - live_mode.b_live = 0; + live_mode.live = 0; vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, &live_mode); + &vcd_property_hdr, &live_mode); if (vcd_status) { - ERR("%s(): Set VCD_I_LIVE Failed\n", - __func__); - return FALSE; + ERR("%s(): Set VCD_I_LIVE Failed\n", __func__); + return false; } } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &live_mode); + &vcd_property_hdr, &live_mode); if (vcd_status) { - ERR("%s(): Get VCD_I_LIVE Failed\n", - __func__); - return FALSE; - } else { - encoder_switch->status = 1; - if (!live_mode.b_live) - encoder_switch->status = 0; + ERR("%s(): Get VCD_I_LIVE Failed\n", __func__); + return false; } + encoder_switch->status = 1; + if (!live_mode.live) + encoder_switch->status = 0; } - return TRUE; + return true; } u32 vid_enc_set_get_short_header(struct video_client_ctx *client_ctx, - struct venc_switch *encoder_switch, u32 set_flag) + struct venc_switch *encoder_switch, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_short_header_type short_header; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_short_header short_header; u32 vcd_status = VCD_ERR_FAIL; if (!client_ctx || !encoder_switch) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_SHORT_HEADER; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_short_header_type); + vcd_property_hdr.id = VCD_I_SHORT_HEADER; + vcd_property_hdr.sz = sizeof(struct vcd_property_short_header); if (set_flag) { - short_header.b_short_header = (u32) encoder_switch->status; + short_header.short_header = (u32) encoder_switch->status; vcd_status = vcd_set_property(client_ctx->vcd_handle, &vcd_property_hdr, &short_header); if (vcd_status) { - ERR("%s(): Set VCD_I_SHORT_HEADER Failed\n", - __func__); - return FALSE; + ERR("%s(): Set VCD_I_SHORT_HEADER Failed\n", __func__); + return false; } } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &short_header); + &vcd_property_hdr, &short_header); if (vcd_status) { - ERR("%s(): Get VCD_I_SHORT_HEADER Failed\n", - __func__); - return FALSE; - } else { - encoder_switch->status = - (u8) short_header.b_short_header; + ERR("%s(): Get VCD_I_SHORT_HEADER Failed\n", __func__); + return false; } + encoder_switch->status = (u8)short_header.short_header; } - return TRUE; + return true; } u32 vid_enc_set_get_profile(struct video_client_ctx *client_ctx, - struct venc_profile *profile, u32 set_flag) + struct venc_profile *profile, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_profile_type profile_type; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_profile profile_type; u32 vcd_status = VCD_ERR_FAIL; - u32 status = TRUE; + u32 status = true; if (!client_ctx || !profile) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_PROFILE; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_profile_type); + vcd_property_hdr.id = VCD_I_PROFILE; + vcd_property_hdr.sz = sizeof(struct vcd_property_profile); if (set_flag) { switch (profile->profile) { case VEN_PROFILE_MPEG4_SP: - profile_type.e_profile = VCD_PROFILE_MPEG4_SP; + profile_type.profile = VCD_PROFILE_MPEG4_SP; break; case VEN_PROFILE_MPEG4_ASP: - profile_type.e_profile = VCD_PROFILE_MPEG4_ASP; + profile_type.profile = VCD_PROFILE_MPEG4_ASP; break; case VEN_PROFILE_H264_BASELINE: - profile_type.e_profile = VCD_PROFILE_H264_BASELINE; + profile_type.profile = VCD_PROFILE_H264_BASELINE; break; case VEN_PROFILE_H264_MAIN: - profile_type.e_profile = VCD_PROFILE_H264_MAIN; + profile_type.profile = VCD_PROFILE_H264_MAIN; break; case VEN_PROFILE_H264_HIGH: - profile_type.e_profile = VCD_PROFILE_H264_HIGH; + profile_type.profile = VCD_PROFILE_H264_HIGH; break; case VEN_PROFILE_H263_BASELINE: - profile_type.e_profile = VCD_PROFILE_H263_BASELINE; + profile_type.profile = VCD_PROFILE_H263_BASELINE; break; default: - status = FALSE; + status = false; break; } - if (status) { - vcd_status = vcd_set_property(client_ctx->vcd_handle, + if (!status) + return status; + vcd_status = vcd_set_property(client_ctx->vcd_handle, &vcd_property_hdr, &profile_type); - if (vcd_status) { - ERR("%s(): Set VCD_I_PROFILE Failed\n", - __func__); - return FALSE; - } + if (vcd_status) { + ERR("%s(): Set VCD_I_PROFILE Failed\n", __func__); + return false; } - } else { + } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &profile_type); + &vcd_property_hdr, &profile_type); if (vcd_status) { - ERR("%s(): Get VCD_I_PROFILE Failed\n", - __func__); - return FALSE; - } else { - switch (profile_type.e_profile) { - case VCD_PROFILE_H263_BASELINE: - profile->profile = VEN_PROFILE_H263_BASELINE; - break; - case VCD_PROFILE_H264_BASELINE: - profile->profile = VEN_PROFILE_H264_BASELINE; - break; - case VCD_PROFILE_H264_HIGH: - profile->profile = VEN_PROFILE_H264_HIGH; - break; - case VCD_PROFILE_H264_MAIN: - profile->profile = VEN_PROFILE_H264_MAIN; - break; - case VCD_PROFILE_MPEG4_ASP: - profile->profile = VEN_PROFILE_MPEG4_ASP; - break; - case VCD_PROFILE_MPEG4_SP: - profile->profile = VEN_PROFILE_MPEG4_SP; - break; - default: - status = FALSE; - break; - } + ERR("%s(): Get VCD_I_PROFILE Failed\n", __func__); + return false; + } + switch (profile_type.profile) { + case VCD_PROFILE_H263_BASELINE: + profile->profile = VEN_PROFILE_H263_BASELINE; + break; + case VCD_PROFILE_H264_BASELINE: + profile->profile = VEN_PROFILE_H264_BASELINE; + break; + case VCD_PROFILE_H264_HIGH: + profile->profile = VEN_PROFILE_H264_HIGH; + break; + case VCD_PROFILE_H264_MAIN: + profile->profile = VEN_PROFILE_H264_MAIN; + break; + case VCD_PROFILE_MPEG4_ASP: + profile->profile = VEN_PROFILE_MPEG4_ASP; + break; + case VCD_PROFILE_MPEG4_SP: + profile->profile = VEN_PROFILE_MPEG4_SP; + break; + default: + status = false; + break; } } return status; } u32 vid_enc_set_get_profile_level(struct video_client_ctx *client_ctx, - struct ven_profilelevel *profile_level, u32 set_flag) + struct ven_profilelevel *profile_level, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_level_type level_type; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_level level_type; u32 vcd_status = VCD_ERR_FAIL; - u32 status = TRUE; if (!client_ctx || !profile_level) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_LEVEL; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_level_type); + vcd_property_hdr.id = VCD_I_LEVEL; + vcd_property_hdr.sz = sizeof(struct vcd_property_level); if (set_flag) { switch (profile_level->level) { + //TODO: collapse this crap case VEN_LEVEL_MPEG4_0: - level_type.e_level = VCD_LEVEL_MPEG4_0; + level_type.level = VCD_LEVEL_MPEG4_0; break; case VEN_LEVEL_MPEG4_1: - level_type.e_level = VCD_LEVEL_MPEG4_1; + level_type.level = VCD_LEVEL_MPEG4_1; break; case VEN_LEVEL_MPEG4_2: - level_type.e_level = VCD_LEVEL_MPEG4_2; + level_type.level = VCD_LEVEL_MPEG4_2; break; case VEN_LEVEL_MPEG4_3: - level_type.e_level = VCD_LEVEL_MPEG4_3; + level_type.level = VCD_LEVEL_MPEG4_3; break; case VEN_LEVEL_MPEG4_4: - level_type.e_level = VCD_LEVEL_MPEG4_4; + level_type.level = VCD_LEVEL_MPEG4_4; break; case VEN_LEVEL_MPEG4_5: - level_type.e_level = VCD_LEVEL_MPEG4_5; + level_type.level = VCD_LEVEL_MPEG4_5; break; case VEN_LEVEL_MPEG4_3b: - level_type.e_level = VCD_LEVEL_MPEG4_3b; + level_type.level = VCD_LEVEL_MPEG4_3b; break; case VEN_LEVEL_MPEG4_6: - level_type.e_level = VCD_LEVEL_MPEG4_6; + level_type.level = VCD_LEVEL_MPEG4_6; break; case VEN_LEVEL_H264_1: - level_type.e_level = VCD_LEVEL_H264_1; + level_type.level = VCD_LEVEL_H264_1; break; case VEN_LEVEL_H264_1b: - level_type.e_level = VCD_LEVEL_H264_1b; + level_type.level = VCD_LEVEL_H264_1b; break; case VEN_LEVEL_H264_1p1: - level_type.e_level = VCD_LEVEL_H264_1p1; + level_type.level = VCD_LEVEL_H264_1p1; break; case VEN_LEVEL_H264_1p2: - level_type.e_level = VCD_LEVEL_H264_1p2; + level_type.level = VCD_LEVEL_H264_1p2; break; case VEN_LEVEL_H264_1p3: - level_type.e_level = VCD_LEVEL_H264_1p3; + level_type.level = VCD_LEVEL_H264_1p3; break; case VEN_LEVEL_H264_2: - level_type.e_level = VCD_LEVEL_H264_2; + level_type.level = VCD_LEVEL_H264_2; break; case VEN_LEVEL_H264_2p1: - level_type.e_level = VCD_LEVEL_H264_2p1; + level_type.level = VCD_LEVEL_H264_2p1; break; case VEN_LEVEL_H264_2p2: - level_type.e_level = VCD_LEVEL_H264_2p2; + level_type.level = VCD_LEVEL_H264_2p2; break; case VEN_LEVEL_H264_3: - level_type.e_level = VCD_LEVEL_H264_3; + level_type.level = VCD_LEVEL_H264_3; break; case VEN_LEVEL_H264_3p1: - level_type.e_level = VCD_LEVEL_H264_3p1; + level_type.level = VCD_LEVEL_H264_3p1; break; - case VEN_LEVEL_H263_10: - level_type.e_level = VCD_LEVEL_H263_10; + level_type.level = VCD_LEVEL_H263_10; break; case VEN_LEVEL_H263_20: - level_type.e_level = VCD_LEVEL_H263_20; + level_type.level = VCD_LEVEL_H263_20; break; case VEN_LEVEL_H263_30: - level_type.e_level = VCD_LEVEL_H263_30; + level_type.level = VCD_LEVEL_H263_30; break; case VEN_LEVEL_H263_40: - level_type.e_level = VCD_LEVEL_H263_40; + level_type.level = VCD_LEVEL_H263_40; break; case VEN_LEVEL_H263_45: - level_type.e_level = VCD_LEVEL_H263_45; + level_type.level = VCD_LEVEL_H263_45; break; case VEN_LEVEL_H263_50: - level_type.e_level = VCD_LEVEL_H263_50; + level_type.level = VCD_LEVEL_H263_50; break; case VEN_LEVEL_H263_60: - level_type.e_level = VCD_LEVEL_H263_60; + level_type.level = VCD_LEVEL_H263_60; break; case VEN_LEVEL_H263_70: - level_type.e_level = VCD_LEVEL_H263_70; + level_type.level = VCD_LEVEL_H263_70; break; default: - status = FALSE; - break; + return false; } - if (status) { - vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, &level_type); - - if (vcd_status) { - ERR("%s(): Set VCD_I_LEVEL Failed\n", - __func__); - return FALSE; - } + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &level_type); + + if (vcd_status) { + ERR("%s: Set VCD_I_LEVEL Failed\n", __func__); + return false; } } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &level_type); + &vcd_property_hdr, &level_type); if (vcd_status) { - ERR("%s(): Get VCD_I_LEVEL Failed\n", - __func__); - return FALSE; - } else { - switch (level_type.e_level) { - case VCD_LEVEL_MPEG4_0: - profile_level->level = VEN_LEVEL_MPEG4_0; - break; - case VCD_LEVEL_MPEG4_1: - profile_level->level = VEN_LEVEL_MPEG4_1; - break; - case VCD_LEVEL_MPEG4_2: - profile_level->level = VEN_LEVEL_MPEG4_2; - break; - case VCD_LEVEL_MPEG4_3: - profile_level->level = VEN_LEVEL_MPEG4_3; - break; - case VCD_LEVEL_MPEG4_4: - profile_level->level = VEN_LEVEL_MPEG4_4; - break; - case VCD_LEVEL_MPEG4_5: - profile_level->level = VEN_LEVEL_MPEG4_5; - break; - case VCD_LEVEL_MPEG4_3b: - profile_level->level = VEN_LEVEL_MPEG4_3b; - break; - case VCD_LEVEL_H264_1: - profile_level->level = VEN_LEVEL_H264_1; - break; - case VCD_LEVEL_H264_1b: - profile_level->level = VEN_LEVEL_H264_1b; - break; - case VCD_LEVEL_H264_1p1: - profile_level->level = VEN_LEVEL_H264_1p1; - break; - case VCD_LEVEL_H264_1p2: - profile_level->level = VEN_LEVEL_H264_1p2; - break; - case VCD_LEVEL_H264_1p3: - profile_level->level = VEN_LEVEL_H264_1p3; - break; - case VCD_LEVEL_H264_2: - profile_level->level = VEN_LEVEL_H264_2; - break; - case VCD_LEVEL_H264_2p1: - profile_level->level = VEN_LEVEL_H264_2p1; - break; - case VCD_LEVEL_H264_2p2: - profile_level->level = VEN_LEVEL_H264_2p2; - break; - case VCD_LEVEL_H264_3: - profile_level->level = VEN_LEVEL_H264_3; - break; - case VCD_LEVEL_H264_3p1: - profile_level->level = VEN_LEVEL_H264_3p1; - break; - case VCD_LEVEL_H264_3p2: - status = FALSE; - break; - case VCD_LEVEL_H264_4: - status = FALSE; - break; - case VCD_LEVEL_H263_10: - profile_level->level = VEN_LEVEL_H263_10; - break; - case VCD_LEVEL_H263_20: - profile_level->level = VEN_LEVEL_H263_20; - break; - case VCD_LEVEL_H263_30: - profile_level->level = VEN_LEVEL_H263_30; - break; - case VCD_LEVEL_H263_40: - profile_level->level = VEN_LEVEL_H263_40; - break; - case VCD_LEVEL_H263_45: - profile_level->level = VEN_LEVEL_H263_45; - break; - case VCD_LEVEL_H263_50: - profile_level->level = VEN_LEVEL_H263_50; - break; - case VCD_LEVEL_H263_60: - profile_level->level = VEN_LEVEL_H263_60; - break; - case VCD_LEVEL_H263_70: - status = FALSE; - break; - default: - status = FALSE; - break; - } + ERR("%s(): Get VCD_I_LEVEL Failed\n", __func__); + return false; + } + switch (level_type.level) { + case VCD_LEVEL_MPEG4_0: + profile_level->level = VEN_LEVEL_MPEG4_0; + break; + case VCD_LEVEL_MPEG4_1: + profile_level->level = VEN_LEVEL_MPEG4_1; + break; + case VCD_LEVEL_MPEG4_2: + profile_level->level = VEN_LEVEL_MPEG4_2; + break; + case VCD_LEVEL_MPEG4_3: + profile_level->level = VEN_LEVEL_MPEG4_3; + break; + case VCD_LEVEL_MPEG4_4: + profile_level->level = VEN_LEVEL_MPEG4_4; + break; + case VCD_LEVEL_MPEG4_5: + profile_level->level = VEN_LEVEL_MPEG4_5; + break; + case VCD_LEVEL_MPEG4_3b: + profile_level->level = VEN_LEVEL_MPEG4_3b; + break; + case VCD_LEVEL_H264_1: + profile_level->level = VEN_LEVEL_H264_1; + break; + case VCD_LEVEL_H264_1b: + profile_level->level = VEN_LEVEL_H264_1b; + break; + case VCD_LEVEL_H264_1p1: + profile_level->level = VEN_LEVEL_H264_1p1; + break; + case VCD_LEVEL_H264_1p2: + profile_level->level = VEN_LEVEL_H264_1p2; + break; + case VCD_LEVEL_H264_1p3: + profile_level->level = VEN_LEVEL_H264_1p3; + break; + case VCD_LEVEL_H264_2: + profile_level->level = VEN_LEVEL_H264_2; + break; + case VCD_LEVEL_H264_2p1: + profile_level->level = VEN_LEVEL_H264_2p1; + break; + case VCD_LEVEL_H264_2p2: + profile_level->level = VEN_LEVEL_H264_2p2; + break; + case VCD_LEVEL_H264_3: + profile_level->level = VEN_LEVEL_H264_3; + break; + case VCD_LEVEL_H264_3p1: + profile_level->level = VEN_LEVEL_H264_3p1; + break; + case VCD_LEVEL_H264_3p2: + case VCD_LEVEL_H264_4: + return false; + case VCD_LEVEL_H263_10: + profile_level->level = VEN_LEVEL_H263_10; + break; + case VCD_LEVEL_H263_20: + profile_level->level = VEN_LEVEL_H263_20; + break; + case VCD_LEVEL_H263_30: + profile_level->level = VEN_LEVEL_H263_30; + break; + case VCD_LEVEL_H263_40: + profile_level->level = VEN_LEVEL_H263_40; + break; + case VCD_LEVEL_H263_45: + profile_level->level = VEN_LEVEL_H263_45; + break; + case VCD_LEVEL_H263_50: + profile_level->level = VEN_LEVEL_H263_50; + break; + case VCD_LEVEL_H263_60: + profile_level->level = VEN_LEVEL_H263_60; + break; + case VCD_LEVEL_H263_70: + default: + return false; } } - return status; + return true; } u32 vid_enc_set_get_session_qp(struct video_client_ctx *client_ctx, - struct venc_sessionqp *session_qp, u32 set_flag) + struct venc_sessionqp *session_qp, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_session_qp_type qp_type; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_session_qp qp_type; u32 vcd_status = VCD_ERR_FAIL; if (!client_ctx || !session_qp) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_SESSION_QP; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_session_qp_type); + vcd_property_hdr.id = VCD_I_SESSION_QP; + vcd_property_hdr.sz = sizeof(struct vcd_property_session_qp); if (set_flag) { - qp_type.n_i_frame_qp = session_qp->iframeqp; - qp_type.n_p_frame_qp = session_qp->pframqp; + qp_type.iframe_qp = session_qp->iframeqp; + qp_type.frame_qp = session_qp->pframqp; vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, &qp_type); + &vcd_property_hdr, &qp_type); if (vcd_status) { - ERR("%s(): Set VCD_I_SESSION_QP Failed\n", - __func__); - return FALSE; + ERR("%s(): Set VCD_I_SESSION_QP Failed\n", __func__); + return false; } } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &qp_type); + &vcd_property_hdr, &qp_type); if (vcd_status) { - ERR("%s(): Set VCD_I_SESSION_QP Failed\n", - __func__); - return FALSE; - } else { - session_qp->iframeqp = qp_type.n_i_frame_qp; - session_qp->pframqp = qp_type.n_p_frame_qp; + ERR("%s(): Set VCD_I_SESSION_QP Failed\n", __func__); + return false; } + session_qp->iframeqp = qp_type.iframe_qp; + session_qp->pframqp = qp_type.frame_qp; } - return TRUE; + return true; } u32 vid_enc_set_get_intraperiod(struct video_client_ctx *client_ctx, - struct venc_intraperiod *intraperiod, u32 set_flag) + struct venc_intraperiod *intraperiod, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_i_period_type period_type; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_i_period period_type; u32 vcd_status = VCD_ERR_FAIL; if (!client_ctx || !intraperiod) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_INTRA_PERIOD; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_i_period_type); + vcd_property_hdr.id = VCD_I_INTRA_PERIOD; + vcd_property_hdr.sz = sizeof(struct vcd_property_i_period); if (set_flag) { - period_type.n_p_frames = intraperiod->num_pframes; - period_type.n_b_frames = 0; + period_type.frames = intraperiod->num_pframes; + period_type.bframes = 0; vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, &period_type); + &vcd_property_hdr, &period_type); if (vcd_status) { - ERR("%s(): Set VCD_I_INTRA_PERIOD Failed\n", - __func__); - return FALSE; + ERR("%s(): Set VCD_I_INTRA_PERIOD Failed\n", __func__); + return false; } } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &period_type); + &vcd_property_hdr, &period_type); if (vcd_status) { - ERR("%s(): Get VCD_I_INTRA_PERIOD Failed\n", - __func__); - return FALSE; - } else - intraperiod->num_pframes = period_type.n_p_frames; + ERR("%s(): Get VCD_I_INTRA_PERIOD Failed\n", __func__); + return false; + } + intraperiod->num_pframes = period_type.frames; } - return TRUE; + return true; } u32 vid_enc_request_iframe(struct video_client_ctx *client_ctx) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_req_i_frame_type request; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_req_i_frame request; u32 vcd_status = VCD_ERR_FAIL; - u32 status = TRUE; if (!client_ctx) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_REQ_IFRAME; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_req_i_frame_type); - request.b_req_i_frame = 1; + vcd_property_hdr.id = VCD_I_REQ_IFRAME; + vcd_property_hdr.sz = sizeof(struct vcd_property_req_i_frame); + request.req_i_frame = 1; - vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, &request); + vcd_status = vcd_set_property(client_ctx->vcd_handle, &vcd_property_hdr, + &request); if (vcd_status) { - ERR("%s(): Set VCD_I_REQ_IFRAME Failed\n", - __func__); - return FALSE; + ERR("%s(): Set VCD_I_REQ_IFRAME Failed\n", __func__); + return false; } - return status; + return true; } u32 vid_enc_get_sequence_header(struct video_client_ctx *client_ctx, - struct venc_seqheader *seq_header) + struct venc_seqheader *seq_header) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_sequence_hdr_type hdr_type; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_sequence_hdr hdr_type; u32 vcd_status = VCD_ERR_FAIL; - u32 status = TRUE; - if (!client_ctx || - !seq_header || !seq_header->bufsize) - return FALSE; + if (!client_ctx || !seq_header || !seq_header->buf_sz) + return false; - vcd_property_hdr.prop_id = VCD_I_SEQ_HEADER; - vcd_property_hdr.n_size = - sizeof(struct vcd_sequence_hdr_type); + vcd_property_hdr.id = VCD_I_SEQ_HEADER; + vcd_property_hdr.sz = sizeof(struct vcd_sequence_hdr); - hdr_type.p_sequence_header = - kzalloc(seq_header->bufsize, GFP_KERNEL); - seq_header->hdrbufptr = hdr_type.p_sequence_header; + hdr_type.addr = kzalloc(seq_header->buf_sz, GFP_KERNEL); + seq_header->buf = hdr_type.addr; + if (!hdr_type.addr) + return false; - if (!hdr_type.p_sequence_header) - return FALSE; - hdr_type.n_sequence_header_len = seq_header->bufsize; - vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &hdr_type); + hdr_type.sz = seq_header->buf_sz; + vcd_status = vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr, + &hdr_type); if (vcd_status) { - ERR("%s(): Get VCD_I_SEQ_HEADER Failed\n", - __func__); - status = FALSE; + ERR("%s: Get VCD_I_SEQ_HEADER Failed\n", __func__); + return false; } - return TRUE; + return true; } u32 vid_enc_set_get_entropy_cfg(struct video_client_ctx *client_ctx, - struct venc_entropycfg *entropy_cfg, u32 set_flag) + struct venc_entropycfg *entropy_cfg, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_entropy_control_type control_type; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_entropy_control control_type; u32 vcd_status = VCD_ERR_FAIL; - u32 status = TRUE; if (!client_ctx || !entropy_cfg) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_ENTROPY_CTRL; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_entropy_control_type); + vcd_property_hdr.id = VCD_I_ENTROPY_CTRL; + vcd_property_hdr.sz = sizeof(struct vcd_property_entropy_control); if (set_flag) { switch (entropy_cfg->cabacmodel) { case VEN_ENTROPY_MODEL_CAVLC: - control_type.e_entropy_sel = VCD_ENTROPY_SEL_CAVLC; + control_type.entropy_sel = VCD_ENTROPY_SEL_CAVLC; break; case VEN_ENTROPY_MODEL_CABAC: - control_type.e_entropy_sel = VCD_ENTROPY_SEL_CABAC; + control_type.entropy_sel = VCD_ENTROPY_SEL_CABAC; break; default: - status = FALSE; - break; + return false; } - if (status && entropy_cfg->cabacmodel == - VCD_ENTROPY_SEL_CABAC) { + if (entropy_cfg->cabacmodel == VCD_ENTROPY_SEL_CABAC) { switch (entropy_cfg->cabacmodel) { case VEN_CABAC_MODEL_0: - control_type.e_cabac_model = + control_type.cabac_model = VCD_CABAC_MODEL_NUMBER_0; break; case VEN_CABAC_MODEL_1: - control_type.e_cabac_model = + control_type.cabac_model = VCD_CABAC_MODEL_NUMBER_1; break; case VEN_CABAC_MODEL_2: - control_type.e_cabac_model = + control_type.cabac_model = VCD_CABAC_MODEL_NUMBER_2; break; default: - status = FALSE; - break; + return false; } } - if (status) { - vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, &control_type); - - if (vcd_status) { - ERR("%s(): Set VCD_I_ENTROPY_CTRL Failed\n", - __func__); - status = FALSE; - } + + vcd_status = vcd_set_property(client_ctx->vcd_handle, + &vcd_property_hdr, &control_type); + if (vcd_status) { + ERR("%s(): Set VCD_I_ENTROPY_CTRL Failed\n", __func__); + return false; } } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &control_type); - + &vcd_property_hdr, &control_type); if (vcd_status) { - ERR("%s(): Get VCD_I_ENTROPY_CTRL Failed\n", - __func__); - status = FALSE; - } else { - switch (control_type.e_entropy_sel) { - case VCD_ENTROPY_SEL_CABAC: - entropy_cfg->cabacmodel = - VEN_ENTROPY_MODEL_CABAC; + ERR("%s(): Get VCD_I_ENTROPY_CTRL Failed\n", __func__); + return false; + } + + switch (control_type.entropy_sel) { + case VCD_ENTROPY_SEL_CABAC: + entropy_cfg->cabacmodel = VEN_ENTROPY_MODEL_CABAC; + break; + case VCD_ENTROPY_SEL_CAVLC: + entropy_cfg->cabacmodel = VEN_ENTROPY_MODEL_CAVLC; + break; + default: + return false; + } + + if (control_type.entropy_sel == VCD_ENTROPY_SEL_CABAC) { + switch (control_type.cabac_model) { + case VCD_CABAC_MODEL_NUMBER_0: + entropy_cfg->cabacmodel = VEN_CABAC_MODEL_0; break; - case VCD_ENTROPY_SEL_CAVLC: - entropy_cfg->cabacmodel = - VEN_ENTROPY_MODEL_CAVLC; + case VCD_CABAC_MODEL_NUMBER_1: + entropy_cfg->cabacmodel = VEN_CABAC_MODEL_1; break; - default: - status = FALSE; + case VCD_CABAC_MODEL_NUMBER_2: + entropy_cfg->cabacmodel = VEN_CABAC_MODEL_2; break; - } - - if (status && control_type.e_entropy_sel == - VCD_ENTROPY_SEL_CABAC) { - switch (control_type.e_cabac_model) { - case VCD_CABAC_MODEL_NUMBER_0: - entropy_cfg->cabacmodel = - VEN_CABAC_MODEL_0; - break; - case VCD_CABAC_MODEL_NUMBER_1: - entropy_cfg->cabacmodel = - VEN_CABAC_MODEL_1; - break; - case VCD_CABAC_MODEL_NUMBER_2: - entropy_cfg->cabacmodel = - VEN_CABAC_MODEL_2; - break; - default: - status = FALSE; - break; - } + default: + return false; } } } - return status; + return true; } u32 vid_enc_set_get_dbcfg(struct video_client_ctx *client_ctx, - struct venc_dbcfg *dbcfg, u32 set_flag) + struct venc_dbcfg *dbcfg, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_db_config_type control_type; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_db_config control_type; u32 vcd_status = VCD_ERR_FAIL; - u32 status = TRUE; if (!client_ctx || !dbcfg) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_DEBLOCKING; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_db_config_type); + vcd_property_hdr.id = VCD_I_DEBLOCKING; + vcd_property_hdr.sz = sizeof(struct vcd_property_db_config); if (set_flag) { switch (dbcfg->db_mode) { case VEN_DB_DISABLE: - control_type.e_db_config = VCD_DB_DISABLE; + control_type.db_config = VCD_DB_DISABLE; break; case VEN_DB_ALL_BLKG_BNDRY: - control_type.e_db_config = VCD_DB_ALL_BLOCKING_BOUNDARY; + control_type.db_config = VCD_DB_ALL_BLOCKING_BOUNDARY; break; case VEN_DB_SKIP_SLICE_BNDRY: - control_type.e_db_config = VCD_DB_SKIP_SLICE_BOUNDARY; + control_type.db_config = VCD_DB_SKIP_SLICE_BOUNDARY; break; default: - status = FALSE; - break; + return false; } - if (status) { - control_type.n_slice_alpha_offset = - dbcfg->slicealpha_offset; - control_type.n_slice_beta_offset = - dbcfg->slicebeta_offset; - vcd_status = vcd_set_property(client_ctx->vcd_handle, + control_type.slice_alpha_offset = dbcfg->slicealpha_offset; + control_type.slice_beta_offset = dbcfg->slicebeta_offset; + vcd_status = vcd_set_property(client_ctx->vcd_handle, &vcd_property_hdr, &control_type); - if (vcd_status) { - ERR("%s(): Set VCD_I_DEBLOCKING Failed\n", - __func__); - status = FALSE; - } + if (vcd_status) { + ERR("%s: Set VCD_I_DEBLOCKING Failed\n", __func__); + return false; } } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &control_type); + &vcd_property_hdr, &control_type); if (vcd_status) { - ERR("%s(): Get VCD_I_DEBLOCKING Failed\n", - __func__); - status = FALSE; - } else { - switch (control_type.e_db_config) { - case VCD_DB_ALL_BLOCKING_BOUNDARY: - dbcfg->db_mode = VEN_DB_ALL_BLKG_BNDRY; - break; - case VCD_DB_DISABLE: - dbcfg->db_mode = VEN_DB_DISABLE; - break; - case VCD_DB_SKIP_SLICE_BOUNDARY: - dbcfg->db_mode = VEN_DB_SKIP_SLICE_BNDRY; - break; - default: - status = FALSE; - break; - } - dbcfg->slicealpha_offset = - control_type.n_slice_alpha_offset; - dbcfg->slicebeta_offset = - control_type.n_slice_beta_offset; + ERR("%s: Get VCD_I_DEBLOCKING Failed\n", __func__); + return false; + } + switch (control_type.db_config) { + case VCD_DB_ALL_BLOCKING_BOUNDARY: + dbcfg->db_mode = VEN_DB_ALL_BLKG_BNDRY; + break; + case VCD_DB_DISABLE: + dbcfg->db_mode = VEN_DB_DISABLE; + break; + case VCD_DB_SKIP_SLICE_BOUNDARY: + dbcfg->db_mode = VEN_DB_SKIP_SLICE_BNDRY; + break; + default: + return false; } + dbcfg->slicealpha_offset = control_type.slice_alpha_offset; + dbcfg->slicebeta_offset = control_type.slice_beta_offset; } - return status; + return true; } u32 vid_enc_set_get_intrarefresh(struct video_client_ctx *client_ctx, - struct venc_intrarefresh *intrarefresh, u32 set_flag) + struct venc_intrarefresh *intrarefresh, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_intra_refresh_mb_number_type control_type; + struct vcd_property_hdr prop_hdr; + struct vcd_property_intra_refresh_mb_number control_type; u32 vcd_status = VCD_ERR_FAIL; if (!client_ctx || !intrarefresh) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_INTRA_REFRESH; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_intra_refresh_mb_number_type); + prop_hdr.id = VCD_I_INTRA_REFRESH; + prop_hdr.sz = sizeof(struct vcd_property_intra_refresh_mb_number); if (set_flag) { - control_type.n_cir_mb_number = intrarefresh->mbcount; - vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, &control_type); + control_type.cir_mb_number = intrarefresh->mbcount; + vcd_status = vcd_set_property(client_ctx->vcd_handle, &prop_hdr, + &control_type); if (vcd_status) { - ERR("%s(): Set VCD_I_INTRA_REFRESH Failed\n", - __func__); - return FALSE; + ERR("%s(): Set VCD_I_INTRA_REFRESH Failed\n", __func__); + return false; } } else { - vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &control_type); + vcd_status = vcd_get_property(client_ctx->vcd_handle, &prop_hdr, + &control_type); if (vcd_status) { - ERR("%s(): Set VCD_I_INTRA_REFRESH Failed\n", - __func__); - return FALSE; - } else - intrarefresh->mbcount = control_type.n_cir_mb_number; + ERR("%s(): Set VCD_I_INTRA_REFRESH Failed\n", __func__); + return false; + } + intrarefresh->mbcount = control_type.cir_mb_number; } - return TRUE; + return true; } u32 vid_enc_set_get_multiclicecfg(struct video_client_ctx *client_ctx, - struct venc_multiclicecfg *multiclicecfg, u32 set_flag) + struct venc_multiclicecfg *multiclicecfg, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_multi_slice_type control_type; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_multi_slice control_type; u32 vcd_status = VCD_ERR_FAIL; - u32 status = TRUE; if (!client_ctx || !multiclicecfg) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_MULTI_SLICE; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_multi_slice_type); + vcd_property_hdr.id = VCD_I_MULTI_SLICE; + vcd_property_hdr.sz = sizeof(struct vcd_property_multi_slice); if (set_flag) { switch (multiclicecfg->mslice_mode) { case VEN_MSLICE_OFF: - control_type.e_m_slice_sel = - VCD_MSLICE_OFF; + control_type.m_slice_sel = VCD_MSLICE_OFF; break; case VEN_MSLICE_CNT_MB: - control_type.e_m_slice_sel = - VCD_MSLICE_BY_MB_COUNT; + control_type.m_slice_sel = VCD_MSLICE_BY_MB_COUNT; break; case VEN_MSLICE_CNT_BYTE: - control_type.e_m_slice_sel = - VCD_MSLICE_BY_BYTE_COUNT; + control_type.m_slice_sel = VCD_MSLICE_BY_BYTE_COUNT; break; case VEN_MSLICE_GOB: - control_type.e_m_slice_sel = - VCD_MSLICE_BY_GOB; + control_type.m_slice_sel = VCD_MSLICE_BY_GOB; break; default: - status = FALSE; - break; + return false; } - if (status) { - control_type.n_m_slice_size = - multiclicecfg->mslice_size; - vcd_status = vcd_set_property(client_ctx->vcd_handle, + control_type.m_slice_size = multiclicecfg->mslice_size; + vcd_status = vcd_set_property(client_ctx->vcd_handle, &vcd_property_hdr, &control_type); - if (vcd_status) { - ERR("%s(): Set VCD_I_MULTI_SLICE Failed\n", - __func__); - status = FALSE; - } + if (vcd_status) { + ERR("%s: Set VCD_I_MULTI_SLICE Failed\n", __func__); + return false; } } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr, &control_type); if (vcd_status) { - ERR("%s(): Get VCD_I_MULTI_SLICE Failed\n", - __func__); - status = FALSE; - } else { - multiclicecfg->mslice_size = - control_type.n_m_slice_size; - switch (control_type.e_m_slice_sel) { - case VCD_MSLICE_OFF: - multiclicecfg->mslice_mode = VEN_MSLICE_OFF; - break; - case VCD_MSLICE_BY_MB_COUNT: - multiclicecfg->mslice_mode = VEN_MSLICE_CNT_MB; - break; - case VCD_MSLICE_BY_BYTE_COUNT: - multiclicecfg->mslice_mode = - VEN_MSLICE_CNT_BYTE; - break; - case VCD_MSLICE_BY_GOB: - multiclicecfg->mslice_mode = - VEN_MSLICE_GOB; - break; - default: - status = FALSE; - break; - } + ERR("%s: Get VCD_I_MULTI_SLICE Failed\n", __func__); + return false; + } + multiclicecfg->mslice_size = control_type.m_slice_size; + switch (control_type.m_slice_sel) { + case VCD_MSLICE_OFF: + multiclicecfg->mslice_mode = VEN_MSLICE_OFF; + break; + case VCD_MSLICE_BY_MB_COUNT: + multiclicecfg->mslice_mode = VEN_MSLICE_CNT_MB; + break; + case VCD_MSLICE_BY_BYTE_COUNT: + multiclicecfg->mslice_mode = VEN_MSLICE_CNT_BYTE; + break; + case VCD_MSLICE_BY_GOB: + multiclicecfg->mslice_mode = VEN_MSLICE_GOB; + break; + default: + return false; } } - return status; + return true; } u32 vid_enc_set_get_ratectrlcfg(struct video_client_ctx *client_ctx, - struct venc_ratectrlcfg *ratectrlcfg, u32 set_flag) + struct venc_ratectrlcfg *ratectrlcfg, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_rate_control_type control_type; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_rate_control control_type; u32 vcd_status = VCD_ERR_FAIL; - u32 status = TRUE; if (!client_ctx || !ratectrlcfg) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_RATE_CONTROL; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_rate_control_type); + vcd_property_hdr.id = VCD_I_RATE_CONTROL; + vcd_property_hdr.sz = sizeof(struct vcd_property_rate_control); if (set_flag) { switch (ratectrlcfg->rcmode) { case VEN_RC_OFF: - control_type.e_rate_control = VCD_RATE_CONTROL_OFF; + control_type.rate_control = VCD_RATE_CONTROL_OFF; break; case VEN_RC_CBR_VFR: - control_type.e_rate_control = VCD_RATE_CONTROL_CBR_VFR; + control_type.rate_control = VCD_RATE_CONTROL_CBR_VFR; break; case VEN_RC_VBR_CFR: - control_type.e_rate_control = VCD_RATE_CONTROL_VBR_CFR; + control_type.rate_control = VCD_RATE_CONTROL_VBR_CFR; break; case VEN_RC_VBR_VFR: - control_type.e_rate_control = VCD_RATE_CONTROL_VBR_VFR; + control_type.rate_control = VCD_RATE_CONTROL_VBR_VFR; break; default: - status = FALSE; - break; + return false; } - if (status) { - vcd_status = vcd_set_property(client_ctx->vcd_handle, + vcd_status = vcd_set_property(client_ctx->vcd_handle, &vcd_property_hdr, &control_type); - if (vcd_status) { - ERR("%s(): Set VCD_I_RATE_CONTROL Failed\n", - __func__); - status = FALSE; - } + if (vcd_status) { + ERR("%s(): Set VCD_I_RATE_CONTROL Failed\n", __func__); + return false; } } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &control_type); + &vcd_property_hdr, &control_type); if (vcd_status) { - ERR("%s(): Get VCD_I_RATE_CONTROL Failed\n", - __func__); - status = FALSE; - } else { - switch (control_type.e_rate_control) { - case VCD_RATE_CONTROL_OFF: - ratectrlcfg->rcmode = VEN_RC_OFF; - break; - case VCD_RATE_CONTROL_CBR_VFR: - ratectrlcfg->rcmode = VEN_RC_CBR_VFR; - break; - case VCD_RATE_CONTROL_VBR_CFR: - ratectrlcfg->rcmode = VEN_RC_VBR_CFR; - break; - case VCD_RATE_CONTROL_VBR_VFR: - ratectrlcfg->rcmode = VEN_RC_VBR_VFR; - break; - default: - status = FALSE; - break; - } + ERR("%s(): Get VCD_I_RATE_CONTROL Failed\n", __func__); + return false; + } + + switch (control_type.rate_control) { + case VCD_RATE_CONTROL_OFF: + ratectrlcfg->rcmode = VEN_RC_OFF; + break; + case VCD_RATE_CONTROL_CBR_VFR: + ratectrlcfg->rcmode = VEN_RC_CBR_VFR; + break; + case VCD_RATE_CONTROL_VBR_CFR: + ratectrlcfg->rcmode = VEN_RC_VBR_CFR; + break; + case VCD_RATE_CONTROL_VBR_VFR: + ratectrlcfg->rcmode = VEN_RC_VBR_VFR; + break; + default: + return false; } } - return status; + return true; } u32 vid_enc_set_get_voptimingcfg(struct video_client_ctx *client_ctx, - struct venc_voptimingcfg *voptimingcfg, u32 set_flag) + struct venc_voptimingcfg *venc_timing, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_vop_timing_type control_type; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_vop_timing vcd_timing; u32 vcd_status = VCD_ERR_FAIL; - u32 status = TRUE; - if (!client_ctx || !voptimingcfg) - return FALSE; + if (!client_ctx || !venc_timing) + return false; - vcd_property_hdr.prop_id = VCD_I_VOP_TIMING; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_vop_timing_type); + vcd_property_hdr.id = VCD_I_VOP_TIMING; + vcd_property_hdr.sz = sizeof(struct vcd_property_vop_timing); if (set_flag) { - control_type.n_vop_time_resolution = - voptimingcfg->voptime_resolution; + vcd_timing.vop_time_resolution = + venc_timing->voptime_resolution; vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, &control_type); + &vcd_property_hdr, &vcd_timing); if (vcd_status) { - ERR("%s(): Set VCD_I_VOP_TIMING Failed\n", - __func__); - status = FALSE; + ERR("%s(): Set VCD_I_VOP_TIMING Failed\n", __func__); + return false; } } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &control_type); + &vcd_property_hdr, &vcd_timing); if (vcd_status) { - ERR("%s(): Get VCD_I_VOP_TIMING Failed\n", - __func__); - status = FALSE; - } else - voptimingcfg->voptime_resolution = - control_type.n_vop_time_resolution; + ERR("%s(): Get VCD_I_VOP_TIMING Failed\n", __func__); + return false; + } + venc_timing->voptime_resolution = + vcd_timing.vop_time_resolution; } - return status; + return true; } u32 vid_enc_set_get_headerextension(struct video_client_ctx *client_ctx, - struct venc_headerextension *headerextension, u32 set_flag) + struct venc_headerextension *headerextension, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; + struct vcd_property_hdr vcd_property_hdr; u32 control_type; u32 vcd_status = VCD_ERR_FAIL; - u32 status = TRUE; if (!client_ctx || !headerextension) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_HEADER_EXTENSION; - vcd_property_hdr.n_size = sizeof(u32); + vcd_property_hdr.id = VCD_I_HEADER_EXTENSION; + vcd_property_hdr.sz = sizeof(u32); if (set_flag) { control_type = headerextension->header_extension; vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, &control_type); + &vcd_property_hdr, &control_type); if (vcd_status) { - ERR("%s(): Set VCD_I_HEADER_EXTENSION Failed\n", - __func__); - status = FALSE; + ERR("%s: Set VCD_I_HEADER_EXTENSION Fail\n", __func__); + return false; } } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &control_type); + &vcd_property_hdr, &control_type); if (vcd_status) { - ERR("%s(): Get VCD_I_HEADER_EXTENSION Failed\n", - __func__); - status = FALSE; - } else { - headerextension->header_extension = control_type; + ERR("%s: Get VCD_I_HEADER_EXTENSION Fail\n", __func__); + return false; } + headerextension->header_extension = control_type; } - return status; + return true; } u32 vid_enc_set_get_qprange(struct video_client_ctx *client_ctx, - struct venc_qprange *qprange, u32 set_flag) + struct venc_qprange *qprange, u32 set_flag) { - struct vcd_property_hdr_type vcd_property_hdr; - struct vcd_property_qp_range_type control_type; + struct vcd_property_hdr vcd_property_hdr; + struct vcd_property_qp_range control_type; u32 vcd_status = VCD_ERR_FAIL; - u32 status = TRUE; if (!client_ctx || !qprange) - return FALSE; + return false; - vcd_property_hdr.prop_id = VCD_I_QP_RANGE; - vcd_property_hdr.n_size = - sizeof(struct vcd_property_qp_range_type); + vcd_property_hdr.id = VCD_I_QP_RANGE; + vcd_property_hdr.sz = sizeof(struct vcd_property_qp_range); if (set_flag) { - control_type.n_max_qp = qprange->maxqp; - control_type.n_min_qp = qprange->minqp; + control_type.max_qp = qprange->maxqp; + control_type.min_qp = qprange->minqp; vcd_status = vcd_set_property(client_ctx->vcd_handle, - &vcd_property_hdr, &control_type); + &vcd_property_hdr, &control_type); if (vcd_status) { - ERR("%s(): Set VCD_I_QP_RANGE Failed\n", - __func__); - status = FALSE; + ERR("%s(): Set VCD_I_QP_RANGE Failed\n", __func__); + return false; } } else { vcd_status = vcd_get_property(client_ctx->vcd_handle, - &vcd_property_hdr, &control_type); + &vcd_property_hdr, &control_type); if (vcd_status) { - ERR("%s(): Get VCD_I_QP_RANGE Failed\n", - __func__); - status = FALSE; - } else { - qprange->maxqp = control_type.n_max_qp; - qprange->minqp = control_type.n_min_qp; + ERR("%s(): Get VCD_I_QP_RANGE Failed\n", __func__); + return false; } + qprange->maxqp = control_type.max_qp; + qprange->minqp = control_type.min_qp; } - return status; + return true; } -u32 vid_enc_start_stop(struct video_client_ctx *client_ctx, u32 start) +u32 vid_enc_start(struct video_client_ctx *client_ctx) { u32 vcd_status; if (!client_ctx) - return FALSE; - - if (start) { - vcd_status = vcd_encode_start(client_ctx->vcd_handle); + return false; - if (vcd_status) { - ERR("%s(): vcd_encode_start failed." - " vcd_status = %u\n", __func__, vcd_status); - return FALSE; - } - } else { - vcd_status = vcd_stop(client_ctx->vcd_handle); - if (vcd_status) { - ERR("%s(): vcd_stop failed. vcd_status = %u\n", - __func__, vcd_status); - return FALSE; - } - DBG("Send STOP_DONE message to client = %p\n", - client_ctx); + vcd_status = vcd_encode_start(client_ctx->vcd_handle); + if (vcd_status) { + ERR("%s: vcd_encode_start failed %u\n", __func__, vcd_status); + return false; } - return TRUE; + return true; } -u32 vid_enc_pause_resume(struct video_client_ctx *client_ctx, u32 pause) + +u32 vid_enc_stop(struct video_client_ctx *client_ctx) { u32 vcd_status; if (!client_ctx) - return FALSE; - - if (pause) { - DBG("PAUSE command from client = %p\n", - client_ctx); - vcd_status = vcd_pause(client_ctx->vcd_handle); - } else { - DBG("Resume command from client = %p\n", - client_ctx); - vcd_status = vcd_resume(client_ctx->vcd_handle); + return false; + vcd_status = vcd_stop(client_ctx->vcd_handle); + if (vcd_status) { + ERR("%s: vcd_stop failed %u\n", __func__, vcd_status); + return false; } + DBG("Send STOP_DONE message to client = %p\n", client_ctx); + return true; +} + +u32 vid_enc_pause(struct video_client_ctx *client_ctx) +{ + u32 vcd_status; + + if (!client_ctx) + return false; + DBG("PAUSE command from client = %p\n", client_ctx); + vcd_status = vcd_pause(client_ctx->vcd_handle); if (vcd_status) - return FALSE; + return false; + return true; +} + +u32 vid_enc_resume(struct video_client_ctx *client_ctx) +{ + u32 vcd_status; + + if (!client_ctx) + return false; - return TRUE; + DBG("Resume command from client = %p\n", client_ctx); + vcd_status = vcd_resume(client_ctx->vcd_handle); + if (vcd_status) + return false; + return true; } u32 vid_enc_flush(struct video_client_ctx *client_ctx, - struct venc_bufferflush *bufferflush) + struct venc_bufferflush *bufferflush) { - u32 status = TRUE, n_mode, vcd_status; + u32 mode; + u32 vcd_status; if (!client_ctx || !bufferflush) - return FALSE; + return false; switch (bufferflush->flush_mode) { case VEN_FLUSH_INPUT: - n_mode = VCD_FLUSH_INPUT; + mode = VCD_FLUSH_INPUT; break; case VEN_FLUSH_OUTPUT: - n_mode = VCD_FLUSH_OUTPUT; + mode = VCD_FLUSH_OUTPUT; break; case VEN_FLUSH_ALL: - n_mode = VCD_FLUSH_ALL; + mode = VCD_FLUSH_ALL; break; default: - status = FALSE; + return false; break; } - if (status) { - vcd_status = vcd_flush(client_ctx->vcd_handle, n_mode); - if (vcd_status) - status = FALSE; - } - return status; + vcd_status = vcd_flush(client_ctx->vcd_handle, mode); + if (vcd_status) + return false; + return true; } u32 vid_enc_get_buffer_req(struct video_client_ctx *client_ctx, - struct venc_allocatorproperty *venc_buf_req, u32 input_dir) + struct venc_allocatorproperty *venc_buf_req, u32 input_dir) { - enum vcd_buffer_type e_buffer; - struct vcd_buffer_requirement_type buffer_req; - u32 status = TRUE; + enum vcd_buffer_type buffer; + struct vcd_buffer_requirement buffer_req; u32 vcd_status; if (!client_ctx || !venc_buf_req) - return FALSE; + return false; - e_buffer = VCD_BUFFER_OUTPUT; + buffer = VCD_BUFFER_OUTPUT; if (input_dir) - e_buffer = VCD_BUFFER_INPUT; + buffer = VCD_BUFFER_INPUT; vcd_status = vcd_get_buffer_requirements(client_ctx->vcd_handle, - e_buffer, &buffer_req); - + buffer, &buffer_req); if (vcd_status) - status = FALSE; - - if (status) { - venc_buf_req->actualcount = buffer_req.n_actual_count; - venc_buf_req->alignment = buffer_req.n_align; - venc_buf_req->datasize = buffer_req.n_size; - venc_buf_req->mincount = buffer_req.n_min_count; - venc_buf_req->maxcount = buffer_req.n_max_count; - venc_buf_req->alignment = buffer_req.n_align; - venc_buf_req->bufpoolid = buffer_req.n_buf_pool_id; - venc_buf_req->suffixsize = 0; - } - return status; + return false; + + venc_buf_req->actualcount = buffer_req.actual_count; + venc_buf_req->alignment = buffer_req.align; + venc_buf_req->datasize = buffer_req.size; + venc_buf_req->mincount = buffer_req.min_count; + venc_buf_req->maxcount = buffer_req.max_count; + venc_buf_req->alignment = buffer_req.align; + venc_buf_req->bufpoolid = buffer_req.buf_pool_id; + venc_buf_req->suffixsize = 0; + + return true; } u32 vid_enc_set_buffer_req(struct video_client_ctx *client_ctx, - struct venc_allocatorproperty *venc_buf_req, u32 input_dir) + struct venc_allocatorproperty *venc_buf_req, u32 input_dir) { - enum vcd_buffer_type e_buffer; - struct vcd_buffer_requirement_type buffer_req; - u32 status = TRUE; + enum vcd_buffer_type buffer; + struct vcd_buffer_requirement buffer_req; u32 vcd_status; if (!client_ctx || !venc_buf_req) - return FALSE; + return false; - e_buffer = VCD_BUFFER_OUTPUT; + buffer = VCD_BUFFER_OUTPUT; if (input_dir) - e_buffer = VCD_BUFFER_INPUT; + buffer = VCD_BUFFER_INPUT; - buffer_req.n_actual_count = venc_buf_req->actualcount; - buffer_req.n_align = venc_buf_req->alignment; - buffer_req.n_size = venc_buf_req->datasize; - buffer_req.n_min_count = venc_buf_req->mincount; - buffer_req.n_max_count = venc_buf_req->maxcount; - buffer_req.n_align = venc_buf_req->alignment; - buffer_req.n_buf_pool_id = 0; + buffer_req.actual_count = venc_buf_req->actualcount; + buffer_req.align = venc_buf_req->alignment; + buffer_req.size = venc_buf_req->datasize; + buffer_req.min_count = venc_buf_req->mincount; + buffer_req.max_count = venc_buf_req->maxcount; + buffer_req.align = venc_buf_req->alignment; + buffer_req.buf_pool_id = 0; vcd_status = vcd_set_buffer_requirements(client_ctx->vcd_handle, - e_buffer, &buffer_req); + buffer, &buffer_req); if (vcd_status) - status = FALSE; - return status; + return false; + return true; } u32 vid_enc_set_buffer(struct video_client_ctx *client_ctx, - struct venc_bufferpayload *buffer_info, - enum venc_buffer_dir buffer_type) + struct venc_bufferpayload *buf_info, enum venc_buffer_dir buf_type) { u32 vcd_status = VCD_ERR_FAIL; enum vcd_buffer_type buffer_vcd_type; enum buffer_dir dir_buffer = BUFFER_TYPE_INPUT; - unsigned long user_vaddr, kernel_vaddr, phy_addr, len; + void __user *user_addr; + void *kern_addr; + phys_addr_t phys_addr; + unsigned long len; int pmem_fd; struct file *file; struct buf_addr_table *buf_addr_table; - s32 buffer_index = -1; + s32 buf_index = -1; - if (!client_ctx || !buffer_info) - return FALSE; + if (!client_ctx || !buf_info) + return false; - user_vaddr = (unsigned long)buffer_info->pbuffer; + user_addr = buf_info->buffer; - if (buffer_type == VEN_BUFFER_TYPE_OUTPUT) + if (buf_type == VEN_BUFFER_TYPE_OUTPUT) dir_buffer = BUFFER_TYPE_OUTPUT; - /*If buffer already set, ignore */ - if (vid_c_lookup_addr_table(client_ctx, dir_buffer, - TRUE, &user_vaddr, &kernel_vaddr, - &phy_addr, &pmem_fd, &file, - &buffer_index)) { + /* if buffer already set, ignore */ + if (vid_c_lookup_addr_table(client_ctx, dir_buffer, true, &user_addr, + &kern_addr, &phys_addr, &pmem_fd, &file, + &buf_index)) { - DBG("%s() : user_virt_addr = 0x%08lx is already set.", - __func__, user_vaddr); - return TRUE; + DBG("%s: user_addr = %p is already set\n", __func__, user_addr); + return true; } - if (get_pmem_file(buffer_info->fd, - &phy_addr, &kernel_vaddr, &len, &file)) { - ERR("%s(): get_pmem_file failed\n", __func__); - return FALSE; + if (get_pmem_file(buf_info->fd, (unsigned long *)&phys_addr, + (unsigned long *)&kern_addr, + &len, &file)) { + ERR("%s: get_pmem_file failed\n", __func__); + return false; } put_pmem_file(file); - if (buffer_type == VEN_BUFFER_TYPE_INPUT) { + if (buf_type == VEN_BUFFER_TYPE_INPUT) { buffer_vcd_type = VCD_BUFFER_INPUT; client_ctx->num_of_input_buffers++; - if (client_ctx->num_of_input_buffers > - VID_ENC_MAX_NUM_OF_BUFF) { - ERR("%s(): num_of_input_buffers reached max value" - " VID_ENC_MAX_NUM_OF_BUFF \n", __func__); + if (client_ctx->num_of_input_buffers > VID_ENC_MAX_NUM_OF_BUFF + ) { + ERR("%s: num_of_input_buffers reached max value" + " VID_ENC_MAX_NUM_OF_BUFF\n", __func__); client_ctx->num_of_input_buffers--; - return FALSE; + return false; } - buffer_index = client_ctx->num_of_input_buffers - 1; - buf_addr_table = - &client_ctx->input_buf_addr_table[buffer_index]; - - buf_addr_table->user_vaddr = - (unsigned long)buffer_info->pbuffer; - kernel_vaddr += (unsigned long)buffer_info->offset; - phy_addr += (unsigned long)buffer_info->offset; - buf_addr_table->kernel_vaddr = kernel_vaddr; - buf_addr_table->phy_addr = phy_addr; - buf_addr_table->pmem_fd = buffer_info->fd; + buf_index = client_ctx->num_of_input_buffers - 1; + buf_addr_table = &client_ctx->input_buf_addr_table[buf_index]; + buf_addr_table->user_addr = buf_info->buffer; + kern_addr = (u8 *)kern_addr + buf_info->offset; + phys_addr += buf_info->offset; + buf_addr_table->kern_addr = kern_addr; + buf_addr_table->phys_addr = phys_addr; + buf_addr_table->pmem_fd = buf_info->fd; buf_addr_table->file = file; } else { buffer_vcd_type = VCD_BUFFER_OUTPUT; @@ -1587,41 +1462,40 @@ u32 vid_enc_set_buffer(struct video_client_ctx *client_ctx, if (client_ctx->num_of_output_buffers > VID_ENC_MAX_NUM_OF_BUFF) { - ERR("%s(): num_of_outut_buffers reached max value" - " VID_ENC_MAX_NUM_OF_BUFF \n", __func__); + ERR("%s: num_of_outut_buffers reached max value" + " VID_ENC_MAX_NUM_OF_BUFF\n", __func__); client_ctx->num_of_output_buffers--; - return FALSE; + return false; } - buffer_index = client_ctx->num_of_output_buffers - 1; - - buf_addr_table = - &client_ctx->output_buf_addr_table[buffer_index]; - kernel_vaddr += (unsigned long)buffer_info->offset; - phy_addr += (unsigned long)buffer_info->offset; - buf_addr_table->user_vaddr = - (unsigned long)buffer_info->pbuffer; - buf_addr_table->kernel_vaddr = kernel_vaddr; - buf_addr_table->phy_addr = phy_addr; - buf_addr_table->pmem_fd = buffer_info->fd; + buf_index = client_ctx->num_of_output_buffers - 1; + + buf_addr_table = &client_ctx->output_buf_addr_table[buf_index]; + kern_addr = (u8 *)kern_addr + buf_info->offset; + phys_addr += buf_info->offset; + buf_addr_table->user_addr = buf_info->buffer; + buf_addr_table->kern_addr = kern_addr; + buf_addr_table->phys_addr = phys_addr; + buf_addr_table->pmem_fd = buf_info->fd; buf_addr_table->file = file; } - vcd_status = vcd_set_buffer(client_ctx->vcd_handle, - buffer_vcd_type, (u8 *) kernel_vaddr, - buffer_info->nsize); + vcd_status = vcd_set_buffer(client_ctx->vcd_handle, buffer_vcd_type, + kern_addr, buf_info->sz); if (!vcd_status) - return TRUE; + return true; else - return FALSE; + return false; } u32 vid_enc_encode_frame(struct video_client_ctx *client_ctx, - struct venc_buffer *input_frame_info) + struct venc_buffer *input_frame_info) { - struct vcd_frame_data_type vcd_input_buffer; - unsigned long kernel_vaddr, phy_addr, user_vaddr; + struct vcd_frame_data vcd_input_buffer; + void __user *user_addr; + void *kern_addr; + phys_addr_t phys_addr; int pmem_fd; struct file *file; s32 buffer_index = -1; @@ -1629,88 +1503,74 @@ u32 vid_enc_encode_frame(struct video_client_ctx *client_ctx, u32 vcd_status = VCD_ERR_FAIL; if (!client_ctx || !input_frame_info) - return FALSE; + return false; - user_vaddr = (unsigned long)input_frame_info->ptrbuffer; + user_addr = input_frame_info->addr; - if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_INPUT, - TRUE, &user_vaddr, &kernel_vaddr, - &phy_addr, &pmem_fd, &file, + if (!vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_INPUT, true, + &user_addr, &kern_addr, &phys_addr, &pmem_fd, &file, &buffer_index)) { + ERR("%s: kernel_vaddr not found\n", __func__); + return false; + } - /* kernel_vaddr is found. send the frame to VCD */ - memset((void *)&vcd_input_buffer, 0, - sizeof(struct vcd_frame_data_type)); - - vcd_input_buffer.p_virtual = - (u8 *) (kernel_vaddr + input_frame_info->offset); + /* kern_addr is found. send the frame to VCD */ + memset((void *)&vcd_input_buffer, 0, sizeof(vcd_input_buffer)); - vcd_input_buffer.n_offset = input_frame_info->offset; - vcd_input_buffer.n_frm_clnt_data = - (u32) input_frame_info->clientdata; - vcd_input_buffer.n_ip_frm_tag = - (u32) input_frame_info->clientdata; - vcd_input_buffer.n_data_len = input_frame_info->len; - vcd_input_buffer.time_stamp = input_frame_info->timestamp; + vcd_input_buffer.virt_addr = (u8 *)kern_addr + input_frame_info->offset; + vcd_input_buffer.offset = input_frame_info->offset; + vcd_input_buffer.client_data = input_frame_info->clientdata; + vcd_input_buffer.ip_frm_tag = (u32)input_frame_info->clientdata; + vcd_input_buffer.data_len = input_frame_info->len; + vcd_input_buffer.time_stamp = input_frame_info->timestamp; - /* Rely on VCD using the same flags as OMX */ - vcd_input_buffer.n_flags = input_frame_info->flags; + /* Rely on VCD using the same flags as OMX */ + vcd_input_buffer.flags = input_frame_info->flags; - vcd_status = vcd_encode_frame(client_ctx->vcd_handle, + vcd_status = vcd_encode_frame(client_ctx->vcd_handle, &vcd_input_buffer); - if (!vcd_status) - return TRUE; - else { - ERR("%s(): vcd_encode_frame failed = %u\n", - __func__, vcd_status); - return FALSE; - } - } else { - ERR("%s(): kernel_vaddr not found\n", - __func__); - return FALSE; + if (vcd_status) { + ERR("%s: vcd_encode_frame failed = %u\n", __func__, vcd_status); + return false; } + return true; } u32 vid_enc_fill_output_buffer(struct video_client_ctx *client_ctx, - struct venc_buffer *output_frame_info) + struct venc_buffer *output_frame_info) { - unsigned long kernel_vaddr, phy_addr, user_vaddr; + void __user *user_addr; + void *kern_addr; + phys_addr_t phys_addr; int pmem_fd; struct file *file; s32 buffer_index = -1; u32 vcd_status = VCD_ERR_FAIL; - struct vcd_frame_data_type vcd_frame; + struct vcd_frame_data vcd_frame; if (!client_ctx || !output_frame_info) - return FALSE; + return false; - user_vaddr = (unsigned long)output_frame_info->ptrbuffer; + user_addr = output_frame_info->addr; - if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, - TRUE, &user_vaddr, &kernel_vaddr, - &phy_addr, &pmem_fd, &file, - &buffer_index)) { + if (!vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, + true, &user_addr, &kern_addr, &phys_addr, &pmem_fd, + &file, &buffer_index)) { + ERR("%s: kernel_vaddr not found\n", __func__); + return false; + } - memset((void *)&vcd_frame, 0, - sizeof(struct vcd_frame_data_type)); - vcd_frame.p_virtual = (u8 *) kernel_vaddr; - vcd_frame.n_frm_clnt_data = (u32) output_frame_info->clientdata; - vcd_frame.n_alloc_len = output_frame_info->size; - - vcd_status = vcd_fill_output_buffer(client_ctx->vcd_handle, - &vcd_frame); - if (!vcd_status) - return TRUE; - else { - ERR("%s(): vcd_fill_output_buffer failed = %u\n", - __func__, vcd_status); - return FALSE; - } - } else { - ERR("%s(): kernel_vaddr not found\n", __func__); - return FALSE; + memset((void *)&vcd_frame, 0, sizeof(vcd_frame)); + vcd_frame.virt_addr = kern_addr; + vcd_frame.client_data = output_frame_info->clientdata; + vcd_frame.alloc_len = output_frame_info->sz; + + vcd_status = vcd_fill_output_buffer(client_ctx->vcd_handle, &vcd_frame); + if (vcd_status) { + ERR("%s: vcd_fill_output_buffer %u\n", __func__, vcd_status); + return false; } + return true; } diff --git a/drivers/misc/video_core/720p/enc/venc_internal.h b/drivers/misc/video_core/720p/enc/venc_internal.h index ee74ff68cf186..517783acfbcc0 100644 --- a/drivers/misc/video_core/720p/enc/venc_internal.h +++ b/drivers/misc/video_core/720p/enc/venc_internal.h @@ -38,9 +38,9 @@ #define VID_ENC_MAX_ENCODER_CLIENTS 16 #define VID_ENC_MAX_NUM_OF_BUFF 100 -enum venc_buffer_dir{ - VEN_BUFFER_TYPE_INPUT, - VEN_BUFFER_TYPE_OUTPUT +enum venc_buffer_dir { + VEN_BUFFER_TYPE_INPUT, + VEN_BUFFER_TYPE_OUTPUT }; struct vid_enc_msg { @@ -66,91 +66,95 @@ struct vid_enc_dev { }; u32 vid_enc_set_get_base_cfg(struct video_client_ctx *client_ctx, - struct venc_basecfg *base_config, u32 set_flag); + struct venc_basecfg *base_config, u32 set_flag); u32 vid_enc_set_get_inputformat(struct video_client_ctx *client_ctx, - u32 *input_format, u32 set_flag); + u32 *input_format, u32 set_flag); u32 vid_enc_set_get_codec(struct video_client_ctx *client_ctx, u32 *codec_type, - u32 set_flag); + u32 set_flag); u32 vid_enc_set_get_framesize(struct video_client_ctx *client_ctx, - u32 *height, u32 *width, u32 set_flag); + u32 *height, u32 *width, u32 set_flag); u32 vid_enc_set_get_bitrate(struct video_client_ctx *client_ctx, - struct venc_targetbitrate *venc_bitrate, u32 set_flag); + struct venc_targetbitrate *venc_bitrate, u32 set_flag); u32 vid_enc_set_get_framerate(struct video_client_ctx *client_ctx, - struct venc_framerate *frame_rate, u32 set_flag); + struct venc_framerate *frame_rate, u32 set_flag); u32 vid_enc_set_get_live_mode(struct video_client_ctx *client_ctx, - struct venc_switch *encoder_switch, u32 set_flag); + struct venc_switch *encoder_switch, u32 set_flag); u32 vid_enc_set_get_short_header(struct video_client_ctx *client_ctx, - struct venc_switch *encoder_switch, u32 set_flag); + struct venc_switch *encoder_switch, u32 set_flag); u32 vid_enc_set_get_profile(struct video_client_ctx *client_ctx, - struct venc_profile *profile, u32 set_flag); + struct venc_profile *profile, u32 set_flag); u32 vid_enc_set_get_profile_level(struct video_client_ctx *client_ctx, - struct ven_profilelevel *profile_level, u32 set_flag); + struct ven_profilelevel *profile_level, u32 set_flag); u32 vid_enc_set_get_session_qp(struct video_client_ctx *client_ctx, - struct venc_sessionqp *session_qp, u32 set_flag); + struct venc_sessionqp *session_qp, u32 set_flag); u32 vid_enc_set_get_intraperiod(struct video_client_ctx *client_ctx, - struct venc_intraperiod *intraperiod, u32 set_flag); + struct venc_intraperiod *intraperiod, u32 set_flag); u32 vid_enc_request_iframe(struct video_client_ctx *client_ctx); u32 vid_enc_get_sequence_header(struct video_client_ctx *client_ctx, - struct venc_seqheader *seq_header); + struct venc_seqheader *seq_header); u32 vid_enc_set_get_entropy_cfg(struct video_client_ctx *client_ctx, - struct venc_entropycfg *entropy_cfg, u32 set_flag); + struct venc_entropycfg *entropy_cfg, u32 set_flag); u32 vid_enc_set_get_dbcfg(struct video_client_ctx *client_ctx, - struct venc_dbcfg *dbcfg, u32 set_flag); + struct venc_dbcfg *dbcfg, u32 set_flag); u32 vid_enc_set_get_intrarefresh(struct video_client_ctx *client_ctx, - struct venc_intrarefresh *intrarefresh, u32 set_flag); + struct venc_intrarefresh *intrarefresh, u32 set_flag); u32 vid_enc_set_get_multiclicecfg(struct video_client_ctx *client_ctx, - struct venc_multiclicecfg *multiclicecfg, u32 set_flag); + struct venc_multiclicecfg *multiclicecfg, u32 set_flag); u32 vid_enc_set_get_ratectrlcfg(struct video_client_ctx *client_ctx, - struct venc_ratectrlcfg *ratectrlcfg, u32 set_flag); + struct venc_ratectrlcfg *ratectrlcfg, u32 set_flag); u32 vid_enc_set_get_voptimingcfg(struct video_client_ctx *client_ctx, - struct venc_voptimingcfg *voptimingcfg, u32 set_flag); + struct venc_voptimingcfg *voptimingcfg, u32 set_flag); u32 vid_enc_set_get_headerextension(struct video_client_ctx *client_ctx, - struct venc_headerextension *headerextension, u32 set_flag); + struct venc_headerextension *headerextension, u32 set_flag); u32 vid_enc_set_get_qprange(struct video_client_ctx *client_ctx, - struct venc_qprange *qprange, u32 set_flag); + struct venc_qprange *qprange, u32 set_flag); -u32 vid_enc_start_stop(struct video_client_ctx *client_ctx, u32 start); +u32 vid_enc_start(struct video_client_ctx *client_ctx); -u32 vid_enc_pause_resume(struct video_client_ctx *client_ctx, u32 pause); +u32 vid_enc_stop(struct video_client_ctx *client_ctx); + +u32 vid_enc_pause(struct video_client_ctx *client_ctx); + +u32 vid_enc_resume(struct video_client_ctx *client_ctx); u32 vid_enc_flush(struct video_client_ctx *client_ctx, - struct venc_bufferflush *bufferflush); + struct venc_bufferflush *bufferflush); u32 vid_enc_get_buffer_req(struct video_client_ctx *client_ctx, - struct venc_allocatorproperty *venc_buf_req, u32 input_dir); + struct venc_allocatorproperty *venc_buf_req, u32 input_dir); u32 vid_enc_set_buffer_req(struct video_client_ctx *client_ctx, - struct venc_allocatorproperty *venc_buf_req, u32 input_dir); + struct venc_allocatorproperty *venc_buf_req, u32 input_dir); u32 vid_enc_set_buffer(struct video_client_ctx *client_ctx, - struct venc_bufferpayload *buffer_info, - enum venc_buffer_dir buffer_type); + struct venc_bufferpayload *buffer_info, + enum venc_buffer_dir buffer_type); u32 vid_enc_encode_frame(struct video_client_ctx *client_ctx, - struct venc_buffer *input_frame_info); + struct venc_buffer *input_frame_info); u32 vid_enc_fill_output_buffer(struct video_client_ctx *client_ctx, - struct venc_buffer *output_frame_info); + struct venc_buffer *output_frame_info); #endif diff --git a/drivers/misc/video_core/720p/init/video_core_init.c b/drivers/misc/video_core/720p/init/video_core_init.c index 5e0bc0975568b..9af634cd16c4d 100644 --- a/drivers/misc/video_core/720p/init/video_core_init.c +++ b/drivers/misc/video_core/720p/init/video_core_init.c @@ -35,6 +35,7 @@ #include #include +#include "vcd_ddl_firmware.h" #include "vcd_api.h" #include "video_core_init_internal.h" #include "video_core_init.h" @@ -49,9 +50,9 @@ #define ERR(x...) printk(KERN_ERR x) -static struct vid_c_dev *vid_c_device_p; -static dev_t vid_c_dev_num; -static struct class *vid_c_class; +static struct vid_c_dev *vidc_dev; +static dev_t vidc_dev_num; +static struct class *vidc_class; static const struct file_operations vid_c_fops = { .owner = THIS_MODULE, @@ -65,25 +66,19 @@ struct workqueue_struct *vidc_timer_wq; static irqreturn_t vid_c_isr(int irq, void *dev); static spinlock_t vidc_spin_lock; -#define VIDC_BOOT_FW "vidc_720p_command_control.fw" -#define VIDC_MPG4_DEC_FW "vidc_720p_mp4_dec_mc.fw" -#define VIDC_H263_DEC_FW "vidc_720p_h263_dec_mc.fw" -#define VIDC_H264_DEC_FW "vidc_720p_h264_dec_mc.fw" -#define VIDC_MPG4_ENC_FW "vidc_720p_mp4_enc_mc.fw" -#define VIDC_H264_ENC_FW "vidc_720p_h264_enc_mc.fw" static void vid_c_timer_fn(unsigned long data) { unsigned long flag; struct vid_c_timer *hw_timer = NULL; - DBG("%s() Timer expired \n", __func__); + DBG("%s: Timer expired\n", __func__); spin_lock_irqsave(&vidc_spin_lock, flag); hw_timer = (struct vid_c_timer *)data; - list_add_tail(&hw_timer->list, &vid_c_device_p->vidc_timer_queue); + list_add_tail(&hw_timer->list, &vidc_dev->vidc_timer_queue); spin_unlock_irqrestore(&vidc_spin_lock, flag); - DBG("Queue the work for timer \n"); - queue_work(vidc_timer_wq, &vid_c_device_p->vidc_timer_worker); + DBG("Queue the work for timer\n"); + queue_work(vidc_timer_wq, &vidc_dev->vidc_timer_worker); } static void vid_c_timer_handler(struct work_struct *work) @@ -92,13 +87,12 @@ static void vid_c_timer_handler(struct work_struct *work) u32 islist_empty = 0; struct vid_c_timer *hw_timer = NULL; - DBG("%s() Timer expired \n", __func__); + DBG("%s: Timer expired\n", __func__); do { spin_lock_irqsave(&vidc_spin_lock, flag); - islist_empty = list_empty(&vid_c_device_p->vidc_timer_queue); + islist_empty = list_empty(&vidc_dev->vidc_timer_queue); if (!islist_empty) { - hw_timer = list_first_entry( - &vid_c_device_p->vidc_timer_queue, + hw_timer = list_first_entry(&vidc_dev->vidc_timer_queue, struct vid_c_timer, list); list_del(&hw_timer->list); } @@ -113,7 +107,7 @@ static void vid_c_work_handler(struct work_struct *work) DBG("vid_c_work_handler()"); vcd_read_and_clear_interrupt(); vcd_response_handler(); - enable_irq(vid_c_device_p->irq); + enable_irq(vidc_dev->irq); DBG("vid_c_work_handler() done"); } @@ -121,44 +115,48 @@ static DECLARE_WORK(vid_c_work, vid_c_work_handler); static int __init vid_c_720p_probe(struct platform_device *pdev) { + int rc; struct resource *resource; - DBG("Enter %s()\n", __func__); + DBG("Enter %s\n", __func__); if (pdev->id) { - ERR("Invalid plaform device ID = %d\n", pdev->id); + ERR("Invalid platform device ID = %d\n", pdev->id); return -EINVAL; } - vid_c_device_p->irq = platform_get_irq(pdev, 0); - if (unlikely(vid_c_device_p->irq < 0)) { - ERR("%s(): Invalid irq = %d\n", __func__, - vid_c_device_p->irq); + vidc_dev->irq = platform_get_irq(pdev, 0); + if (unlikely(vidc_dev->irq < 0)) { + ERR("%s: Invalid irq = %d\n", __func__, vidc_dev->irq); return -ENXIO; } resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (unlikely(!resource)) { - ERR("%s(): Invalid resource \n", __func__); + ERR("%s: Invalid resource\n", __func__); return -ENXIO; } - vid_c_device_p->phys_base = resource->start; - vid_c_device_p->virt_base = ioremap(resource->start, + vidc_dev->phys_base = resource->start; + vidc_dev->virt_base = ioremap(resource->start, resource->end - resource->start + 1); - if (!vid_c_device_p->virt_base) { - ERR("%s() : ioremap failed\n", __func__); + if (!vidc_dev->virt_base) { + ERR("%s: ioremap failed\n", __func__); return -ENOMEM; } - vid_c_device_p->device = &pdev->dev; - mutex_init(&vid_c_device_p->lock); + vidc_dev->device = &pdev->dev; + mutex_init(&vidc_dev->lock); vid_c_wq = create_singlethread_workqueue("vid_c_worker_queue"); if (!vid_c_wq) { - ERR("%s: create workque failed \n", __func__); + ERR("%s: create workqueue failed\n", __func__); return -ENOMEM; } - return 0; + rc = vcd_fw_init(vidc_dev->device); + if (rc) + ERR("%s: failed to prepare firmware %d\n", __func__, rc); + + return rc; } static int __devexit vid_c_720p_remove(struct platform_device *pdev) @@ -167,15 +165,15 @@ static int __devexit vid_c_720p_remove(struct platform_device *pdev) ERR("Invalid plaform device ID = %d\n", pdev->id); return -EINVAL; } + vcd_fw_exit(); return 0; } - static struct platform_driver msm_vid_c_720p_platform_driver = { .probe = vid_c_720p_probe, .remove = vid_c_720p_remove, .driver = { - .name = "msm_vidc_720p", + .name = "msm_vidc_720p", }, }; @@ -186,7 +184,7 @@ static void __exit vid_c_exit(void) static irqreturn_t vid_c_isr(int irq, void *dev) { - DBG("\n vid_c_isr() %d ", irq); + DBG("vid_c_isr() %d\n", irq); disable_irq_nosync(irq); queue_work(vid_c_wq, &vid_c_work); return IRQ_HANDLED; @@ -197,42 +195,38 @@ static int __init vid_c_init(void) int rc = 0; struct device *class_devp; - vid_c_device_p = kzalloc(sizeof(struct vid_c_dev), GFP_KERNEL); - if (!vid_c_device_p) { + vidc_dev = kzalloc(sizeof(struct vid_c_dev), GFP_KERNEL); + if (!vidc_dev) { ERR("%s Unable to allocate memory for vid_c_dev\n", __func__); return -ENOMEM; } - rc = alloc_chrdev_region(&vid_c_dev_num, 0, 1, VID_C_NAME); + rc = alloc_chrdev_region(&vidc_dev_num, 0, 1, VID_C_NAME); if (rc < 0) { - ERR("%s: alloc_chrdev_region Failed rc = %d\n", - __func__, rc); + ERR("%s: alloc_chrdev_region failed %d\n", __func__, rc); goto error_vid_c_alloc_chrdev_region; } - vid_c_class = class_create(THIS_MODULE, VID_C_NAME); - if (IS_ERR(vid_c_class)) { - rc = PTR_ERR(vid_c_class); - ERR("%s: couldn't create vid_c_class rc = %d\n", - __func__, rc); - + vidc_class = class_create(THIS_MODULE, VID_C_NAME); + if (IS_ERR(vidc_class)) { + rc = PTR_ERR(vidc_class); + ERR("%s: couldn't create vid_c_class %d\n", __func__, rc); goto error_vid_c_class_create; } - class_devp = device_create(vid_c_class, NULL, vid_c_dev_num, NULL, + class_devp = device_create(vidc_class, NULL, vidc_dev_num, NULL, VID_C_NAME); if (IS_ERR(class_devp)) { rc = PTR_ERR(class_devp); - ERR("%s: class device_create failed %d\n", - __func__, rc); + ERR("%s: class device_create failed %d\n", __func__, rc); goto error_vid_c_class_device_create; } - cdev_init(&vid_c_device_p->cdev, &vid_c_fops); - vid_c_device_p->cdev.owner = THIS_MODULE; - rc = cdev_add(&(vid_c_device_p->cdev), vid_c_dev_num, 1); + cdev_init(&vidc_dev->cdev, &vid_c_fops); + vidc_dev->cdev.owner = THIS_MODULE; + rc = cdev_add(&(vidc_dev->cdev), vidc_dev_num, 1); if (rc < 0) { ERR("%s: cdev_add failed %d\n", __func__, rc); @@ -245,204 +239,202 @@ static int __init vid_c_init(void) goto error_vid_c_platfom_register; } - rc = request_irq(vid_c_device_p->irq, vid_c_isr, IRQF_TRIGGER_HIGH, - "vid_c", vid_c_device_p->device); - + rc = request_irq(vidc_dev->irq, vid_c_isr, IRQF_TRIGGER_HIGH, + "vid_c", vidc_dev->device); if (unlikely(rc)) { - ERR("%s() :request_irq failed\n", __func__); + ERR("%s:request_irq failed\n", __func__); goto error_vid_c_platfom_register; } vidc_timer_wq = create_singlethread_workqueue("vidc_timer_wq"); if (!vidc_timer_wq) { - ERR("%s: create workque failed \n", __func__); + ERR("%s: create workqueue failed\n", __func__); rc = -ENOMEM; goto error_vid_c_platfom_register; } - DBG("Disabling IRQ in %s()\n", __func__); - disable_irq_nosync(vid_c_device_p->irq); - INIT_WORK(&vid_c_device_p->vidc_timer_worker, - vid_c_timer_handler); + DBG("Disabling IRQ in %s\n", __func__); + disable_irq_nosync(vidc_dev->irq); + INIT_WORK(&vidc_dev->vidc_timer_worker, vid_c_timer_handler); spin_lock_init(&vidc_spin_lock); - INIT_LIST_HEAD(&vid_c_device_p->vidc_timer_queue); - vid_c_device_p->clock_enabled = 0; - vid_c_device_p->ref_count = 0; - vid_c_device_p->firmware_refcount = 0; - vid_c_device_p->get_firmware = 0; + INIT_LIST_HEAD(&vidc_dev->vidc_timer_queue); + vidc_dev->clock_enabled = 0; + vidc_dev->ref_count = 0; + vidc_dev->firmware_refcount = 0; + vidc_dev->get_firmware = 0; return 0; error_vid_c_platfom_register: - cdev_del(&(vid_c_device_p->cdev)); + cdev_del(&(vidc_dev->cdev)); error_vid_c_cdev_add: - device_destroy(vid_c_class, vid_c_dev_num); + device_destroy(vidc_class, vidc_dev_num); error_vid_c_class_device_create: - class_destroy(vid_c_class); + class_destroy(vidc_class); error_vid_c_class_create: - unregister_chrdev_region(vid_c_dev_num, 1); + unregister_chrdev_region(vidc_dev_num, 1); error_vid_c_alloc_chrdev_region: - kfree(vid_c_device_p); + kfree(vidc_dev); return rc; } -void __iomem *vid_c_get_ioaddr(void) +void __iomem *vid_c_get_ioaddr() { - return (u8 *)vid_c_device_p->virt_base; + return vidc_dev->virt_base; } EXPORT_SYMBOL(vid_c_get_ioaddr); #ifdef USE_RES_TRACKER -u32 vid_c_enable_pwr_rail(void) +u32 vid_c_enable_pwr_rail() { - int rc = -1; - mutex_lock(&vid_c_device_p->lock); + int rc; - if (!vid_c_device_p->rail_enabled) { - //TODO: internal_pwr_rail_mode(MFC_CLK_ID, MANUAL) + mutex_lock(&vidc_dev->lock); - vid_c_device_p->pclk = clk_get(vid_c_device_p->device, - "mfc_pclk"); - - if (IS_ERR(vid_c_device_p->pclk)) { - ERR("%s(): mfc_pclk get failed \n", __func__); - - mutex_unlock(&vid_c_device_p->lock); - return FALSE; - } - - vid_c_device_p->hclk = clk_get(vid_c_device_p->device, - "mfc_clk"); + if (vidc_dev->rail_enabled) { + mutex_unlock(&vidc_dev->lock); + return true; + } - if (IS_ERR(vid_c_device_p->hclk)) { - ERR("%s(): mfc_clk get failed \n", __func__); + //TODO: internal_pwr_rail_mode(MFC_CLK_ID, MANUAL) - clk_put(vid_c_device_p->pclk); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; - } + vidc_dev->pclk = clk_get(vidc_dev->device, "mfc_pclk"); + if (IS_ERR(vidc_dev->pclk)) { + ERR("%s: mfc_pclk get failed\n", __func__); + goto err; + } - vid_c_device_p->hclk_div2 = - clk_get(vid_c_device_p->device, "mfc_div2_clk"); + vidc_dev->hclk = clk_get(vidc_dev->device, "mfc_clk"); + if (IS_ERR(vidc_dev->hclk)) { + ERR("%s: mfc_clk get failed\n", __func__); + goto err; + } - if (IS_ERR(vid_c_device_p->pclk)) { - ERR("%s(): mfc_div2_clk get failed \n", __func__); + vidc_dev->hclk_div2 = clk_get(vidc_dev->device, "mfc_div2_clk"); + if (IS_ERR(vidc_dev->hclk_div2)) { + ERR("%s: mfc_div2_clk get failed\n", __func__); + goto err; + } - clk_put(vid_c_device_p->pclk); - clk_put(vid_c_device_p->hclk); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; - } + //TODO: internal_pwr_rail_ctl(MFC_CLK_ID, 1) - //TODO: internal_pwr_rail_ctl(MFC_CLK_ID, 1) - msleep(20); + //TODO msleep must die + msleep(20); - rc = clk_reset(vid_c_device_p->pclk, CLK_RESET_DEASSERT); - if (rc) { - ERR("\n clk_reset failed %d\n", rc); - return FALSE; - } - msleep(20); + rc = clk_reset(vidc_dev->pclk, CLK_RESET_DEASSERT); + if (rc) { + ERR("clk_reset failed %d\n", rc); + goto err; } - vid_c_device_p->rail_enabled = 1; - mutex_unlock(&vid_c_device_p->lock); - return TRUE; + //TODO msleep must die + msleep(20); + + vidc_dev->rail_enabled = 1; + mutex_unlock(&vidc_dev->lock); + return true; + +err: + if (!IS_ERR(vidc_dev->pclk)) + clk_put(vidc_dev->pclk); + if (!IS_ERR(vidc_dev->hclk)) + clk_put(vidc_dev->hclk); + if (!IS_ERR(vidc_dev->hclk_div2)) + clk_put(vidc_dev->hclk_div2); + mutex_unlock(&vidc_dev->lock); + return false; } EXPORT_SYMBOL(vid_c_enable_pwr_rail); -u32 vid_c_disable_pwr_rail(void) +u32 vid_c_disable_pwr_rail() { int rc = -1; - mutex_lock(&vid_c_device_p->lock); + mutex_lock(&vidc_dev->lock); - if (vid_c_device_p->clock_enabled) { - mutex_unlock(&vid_c_device_p->lock); - DBG("\n Calling CLK disable in Power Down \n"); + if (vidc_dev->clock_enabled) { + mutex_unlock(&vidc_dev->lock); + DBG("Calling CLK disable in power down\n"); vid_c_disable_clk(); - mutex_lock(&vid_c_device_p->lock); + mutex_lock(&vidc_dev->lock); } - if (!vid_c_device_p->rail_enabled) { - mutex_unlock(&vid_c_device_p->lock); - return FALSE; + if (!vidc_dev->rail_enabled) { + mutex_unlock(&vidc_dev->lock); + return false; } - vid_c_device_p->rail_enabled = 0; - rc = clk_reset(vid_c_device_p->pclk, CLK_RESET_ASSERT); + vidc_dev->rail_enabled = 0; + rc = clk_reset(vidc_dev->pclk, CLK_RESET_ASSERT); if (rc) { - ERR("\n clk_reset failed %d\n", rc); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; + ERR("clk_reset failed %d\n", rc); + mutex_unlock(&vidc_dev->lock); + return false; } msleep(20); //TODO: internal_pwr_rail_ctl(MFC_CLK_ID, 0) - clk_put(vid_c_device_p->hclk_div2); - clk_put(vid_c_device_p->hclk); - clk_put(vid_c_device_p->pclk); + clk_put(vidc_dev->hclk_div2); + clk_put(vidc_dev->hclk); + clk_put(vidc_dev->pclk); - mutex_unlock(&vid_c_device_p->lock); + mutex_unlock(&vidc_dev->lock); - return TRUE; + return true; } EXPORT_SYMBOL(vid_c_disable_pwr_rail); -u32 vid_c_enable_clk(void) +u32 vid_c_enable_clk() { - mutex_lock(&vid_c_device_p->lock); - - if (!vid_c_device_p->clock_enabled) { - DBG("Enabling IRQ in %s()\n", __func__); - enable_irq(vid_c_device_p->irq); + mutex_lock(&vidc_dev->lock); - DBG("%s(): Enabling the clocks ...\n", __func__); + if (!vidc_dev->rail_enabled) { + goto err; + } + if (vidc_dev->clock_enabled) { + mutex_unlock(&vidc_dev->lock); + return true; + } - if (clk_enable(vid_c_device_p->pclk)) { - ERR("vidc pclk Enable failed \n"); + DBG("Enabling IRQ in %s\n", __func__); + enable_irq(vidc_dev->irq); + DBG("%s: Enabling the clocks ...\n", __func__); - clk_put(vid_c_device_p->hclk); - clk_put(vid_c_device_p->hclk_div2); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; - } + if (clk_enable(vidc_dev->pclk)) { + ERR("vidc pclk enable failed\n"); + goto err; + } - if (clk_enable(vid_c_device_p->hclk)) { - ERR("vidc hclk Enable failed \n"); - clk_put(vid_c_device_p->pclk); - clk_put(vid_c_device_p->hclk_div2); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; - } + if (clk_enable(vidc_dev->hclk)) { + ERR("vidc hclk enable failed\n"); + goto err; + } - if (clk_enable(vid_c_device_p->hclk_div2)) { - ERR("vidc hclk Enable failed \n"); - clk_put(vid_c_device_p->hclk); - clk_put(vid_c_device_p->pclk); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; - } + if (clk_enable(vidc_dev->hclk_div2)) { + ERR("vidc hclk_div2 enable failed\n"); + goto err; } - vid_c_device_p->clock_enabled = 1; - mutex_unlock(&vid_c_device_p->lock); - return TRUE; + vidc_dev->clock_enabled = 1; + mutex_unlock(&vidc_dev->lock); + return true; +err: + mutex_unlock(&vidc_dev->lock); + return false; } EXPORT_SYMBOL(vid_c_enable_clk); u32 vid_c_sel_clk_rate(unsigned long hclk_rate) { - mutex_lock(&vid_c_device_p->lock); - if (clk_set_rate(vid_c_device_p->hclk, - hclk_rate)) { - ERR("vidc hclk set rate failed \n"); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; - } - vid_c_device_p->hclk_rate = hclk_rate; - mutex_unlock(&vid_c_device_p->lock); - return TRUE; + mutex_lock(&vidc_dev->lock); + if (clk_set_rate(vidc_dev->hclk, hclk_rate)) { + ERR("vidc hclk set rate failed\n"); + mutex_unlock(&vidc_dev->lock); + return false; + } + vidc_dev->hclk_rate = hclk_rate; + mutex_unlock(&vidc_dev->lock); + return true; } EXPORT_SYMBOL(vid_c_sel_clk_rate); @@ -450,461 +442,318 @@ u32 vid_c_get_clk_rate(unsigned long *phclk_rate) { if (!phclk_rate) { ERR("vid_c_get_clk_rate(): phclk_rate is NULL\n"); - return FALSE; + return false; } - mutex_lock(&vid_c_device_p->lock); - *phclk_rate = clk_get_rate(vid_c_device_p->hclk); + mutex_lock(&vidc_dev->lock); + *phclk_rate = clk_get_rate(vidc_dev->hclk); if (!(*phclk_rate)) { - ERR("vidc hclk get rate failed \n"); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; + ERR("vidc hclk get rate failed\n"); + mutex_unlock(&vidc_dev->lock); + return false; } - mutex_unlock(&vid_c_device_p->lock); - return TRUE; + mutex_unlock(&vidc_dev->lock); + return true; } EXPORT_SYMBOL(vid_c_get_clk_rate); u32 vid_c_disable_clk(void) { - mutex_lock(&vid_c_device_p->lock); + mutex_lock(&vidc_dev->lock); - if (!vid_c_device_p->clock_enabled) { - mutex_unlock(&vid_c_device_p->lock); - return FALSE; + if (!vidc_dev->clock_enabled) { + mutex_unlock(&vidc_dev->lock); + return false; } - DBG("Disabling IRQ in %s()\n", __func__); - disable_irq_nosync(vid_c_device_p->irq); - DBG("%s(): Disabling the clocks ...\n", __func__); + DBG("Disabling IRQ in %s\n", __func__); + disable_irq_nosync(vidc_dev->irq); + DBG("%s: Disabling the clocks ...\n", __func__); - vid_c_device_p->clock_enabled = 0; - clk_disable(vid_c_device_p->hclk); - clk_disable(vid_c_device_p->hclk_div2); - clk_disable(vid_c_device_p->pclk); + vidc_dev->clock_enabled = 0; + clk_disable(vidc_dev->hclk); + clk_disable(vidc_dev->hclk_div2); + clk_disable(vidc_dev->pclk); - mutex_unlock(&vid_c_device_p->lock); + mutex_unlock(&vidc_dev->lock); - return TRUE; + return true; } EXPORT_SYMBOL(vid_c_disable_clk); +//TODO: consider deleting USE_RES_TRACKER #else u32 vid_c_enable_clk(unsigned long hclk_rate) { int rc = -1; - mutex_lock(&vid_c_device_p->lock); - vid_c_device_p->ref_count++; + mutex_lock(&vidc_dev->lock); + vidc_dev->ref_count++; - if (!vid_c_device_p->clock_enabled) { + if (!vidc_dev->clock_enabled) { DBG("Enabling IRQ in %s()\n", __func__); - enable_irq(vid_c_device_p->irq); + enable_irq(vidc_dev->irq); rc = internal_pwr_rail_mode (PWR_RAIL_MFC_CLK, PWR_RAIL_CTL_MANUAL); if (rc) { - ERR("%s(): internal_pwr_rail_mode failed %d \n", + ERR("%s(): internal_pwr_rail_mode failed %d\n", __func__, rc); - return FALSE; + return false; } - DBG("%s(): internal_pwr_rail_mode Success %d \n", + DBG("%s(): internal_pwr_rail_mode Success %d\n", __func__, rc); - vid_c_device_p->pclk = - clk_get(vid_c_device_p->device, "mfc_pclk"); + vidc_dev->pclk = + clk_get(vidc_dev->device, "mfc_pclk"); - if (IS_ERR(vid_c_device_p->pclk)) { - ERR("%s(): mfc_pclk get failed \n", __func__); + if (IS_ERR(vidc_dev->pclk)) { + ERR("%s(): mfc_pclk get failed\n", __func__); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; + mutex_unlock(&vidc_dev->lock); + return false; } - vid_c_device_p->hclk = - clk_get(vid_c_device_p->device, "mfc_clk"); + vidc_dev->hclk = + clk_get(vidc_dev->device, "mfc_clk"); - if (IS_ERR(vid_c_device_p->hclk)) { - ERR("%s(): mfc_clk get failed \n", __func__); + if (IS_ERR(vidc_dev->hclk)) { + ERR("%s(): mfc_clk get failed\n", __func__); - clk_put(vid_c_device_p->pclk); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; + clk_put(vidc_dev->pclk); + mutex_unlock(&vidc_dev->lock); + return false; } - vid_c_device_p->hclk_div2 = - clk_get(vid_c_device_p->device, "mfc_div2_clk"); + vidc_dev->hclk_div2 = + clk_get(vidc_dev->device, "mfc_div2_clk"); - if (IS_ERR(vid_c_device_p->pclk)) { - ERR("%s(): mfc_div2_clk get failed \n", __func__); + if (IS_ERR(vidc_dev->pclk)) { + ERR("%s(): mfc_div2_clk get failed\n", __func__); - clk_put(vid_c_device_p->pclk); - clk_put(vid_c_device_p->hclk); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; + clk_put(vidc_dev->pclk); + clk_put(vidc_dev->hclk); + mutex_unlock(&vidc_dev->lock); + return false; } - vid_c_device_p->hclk_rate = hclk_rate; + vidc_dev->hclk_rate = hclk_rate; - if (clk_set_rate(vid_c_device_p->hclk, - vid_c_device_p->hclk_rate)) { - ERR("vid_c hclk set rate failed \n"); - clk_put(vid_c_device_p->pclk); - clk_put(vid_c_device_p->hclk); - clk_put(vid_c_device_p->hclk_div2); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; + if (clk_set_rate(vidc_dev->hclk, + vidc_dev->hclk_rate)) { + ERR("vid_c hclk set rate failed\n"); + clk_put(vidc_dev->pclk); + clk_put(vidc_dev->hclk); + clk_put(vidc_dev->hclk_div2); + mutex_unlock(&vidc_dev->lock); + return false; } - if (clk_enable(vid_c_device_p->pclk)) { - ERR("vid_c pclk Enable failed \n"); + if (clk_enable(vidc_dev->pclk)) { + ERR("vid_c pclk Enable failed\n"); - clk_put(vid_c_device_p->hclk); - clk_put(vid_c_device_p->hclk_div2); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; + clk_put(vidc_dev->hclk); + clk_put(vidc_dev->hclk_div2); + mutex_unlock(&vidc_dev->lock); + return false; } - if (clk_enable(vid_c_device_p->hclk)) { - ERR("vid_c hclk Enable failed \n"); - clk_put(vid_c_device_p->pclk); - clk_put(vid_c_device_p->hclk_div2); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; + if (clk_enable(vidc_dev->hclk)) { + ERR("vid_c hclk Enable failed\n"); + clk_put(vidc_dev->pclk); + clk_put(vidc_dev->hclk_div2); + mutex_unlock(&vidc_dev->lock); + return false; } - if (clk_enable(vid_c_device_p->hclk_div2)) { - ERR("vid_c hclk Enable failed \n"); - clk_put(vid_c_device_p->hclk); - clk_put(vid_c_device_p->pclk); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; + if (clk_enable(vidc_dev->hclk_div2)) { + ERR("vid_c hclk Enable failed\n"); + clk_put(vidc_dev->hclk); + clk_put(vidc_dev->pclk); + mutex_unlock(&vidc_dev->lock); + return false; } msleep(20); rc = internal_pwr_rail_ctl(PWR_RAIL_MFC_CLK, 1); if (rc) { ERR("\n internal_pwr_rail_ctl failed %d\n", rc); - return FALSE; + return false; } - DBG("%s(): internal_pwr_rail_ctl Success %d \n", + DBG("%s(): internal_pwr_rail_ctl Success %d\n", __func__, rc); msleep(20); - rc = clk_reset(vid_c_device_p->pclk, CLK_RESET_DEASSERT); + rc = clk_reset(vidc_dev->pclk, CLK_RESET_DEASSERT); if (rc) { ERR("\n clk_reset failed %d\n", rc); - return FALSE; + return false; } msleep(20); } - vid_c_device_p->clock_enabled = 1; - mutex_unlock(&vid_c_device_p->lock); - return TRUE; + vidc_dev->clock_enabled = 1; + mutex_unlock(&vidc_dev->lock); + return true; } EXPORT_SYMBOL(vid_c_enable_clk); u32 vid_c_disable_clk(void) { int rc = -1; - mutex_lock(&vid_c_device_p->lock); + mutex_lock(&vidc_dev->lock); - if (!vid_c_device_p->ref_count || - !vid_c_device_p->clock_enabled) { - return FALSE; + if (!vidc_dev->ref_count || + !vidc_dev->clock_enabled) { + return false; } - if (vid_c_device_p->ref_count > 0) - vid_c_device_p->ref_count--; + if (vidc_dev->ref_count > 0) + vidc_dev->ref_count--; - if (!vid_c_device_p->ref_count) { + if (!vidc_dev->ref_count) { DBG("Disabling IRQ in %s()\n", __func__); - disable_irq_nosync(vid_c_device_p->irq); - rc = clk_reset(vid_c_device_p->pclk, CLK_RESET_ASSERT); + disable_irq_nosync(vidc_dev->irq); + rc = clk_reset(vidc_dev->pclk, CLK_RESET_ASSERT); if (rc) { ERR("\n clk_reset failed %d\n", rc); - return FALSE; + return false; } msleep(20); rc = internal_pwr_rail_ctl(PWR_RAIL_MFC_CLK, 0); if (rc) { ERR("\n internal_pwr_rail_ctl failed %d\n", rc); - return FALSE; + return false; } - vid_c_device_p->clock_enabled = 0; - clk_disable(vid_c_device_p->hclk); - clk_disable(vid_c_device_p->hclk_div2); - clk_disable(vid_c_device_p->pclk); + vidc_dev->clock_enabled = 0; + clk_disable(vidc_dev->hclk); + clk_disable(vidc_dev->hclk_div2); + clk_disable(vidc_dev->pclk); - clk_put(vid_c_device_p->hclk_div2); - clk_put(vid_c_device_p->hclk); - clk_put(vid_c_device_p->pclk); + clk_put(vidc_dev->hclk_div2); + clk_put(vidc_dev->hclk); + clk_put(vidc_dev->pclk); } - mutex_unlock(&vid_c_device_p->lock); - return TRUE; + mutex_unlock(&vidc_dev->lock); + return true; } EXPORT_SYMBOL(vid_c_disable_clk); #endif -unsigned char *vid_c_command_control_fw; -u32 vid_c_command_control_fw_size; - -unsigned char *vid_c_mpg4_dec_fw; -u32 vid_c_mpg4_dec_fw_size; - -unsigned char *vid_c_h263_dec_fw; -u32 vid_c_h263_dec_fw_size; - -unsigned char *vid_c_h264_dec_fw; -u32 vid_c_h264_dec_fw_size; - -unsigned char *vid_c_mpg4_enc_fw; -u32 vid_c_mpg4_enc_fw_size; - - -unsigned char *vid_c_h264_enc_fw; -u32 vid_c_h264_enc_fw_size; - - -int vid_c_load_firmware(void) -{ - int rc = 0; - const struct firmware *fw_boot = NULL; - const struct firmware *fw_mpg4_dec = NULL; - const struct firmware *fw_h263_dec = NULL; - const struct firmware *fw_h264_dec = NULL; - const struct firmware *fw_mpg4_enc = NULL; - const struct firmware *fw_h264_enc = NULL; - - u32 status = TRUE; - - mutex_lock(&vid_c_device_p->lock); - - if (!vid_c_device_p->get_firmware) { - rc = request_firmware(&fw_boot, - VIDC_BOOT_FW, vid_c_device_p->device); - if (rc) { - ERR("request_firmware for %s failed with error %d\n", - VIDC_BOOT_FW, rc); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; - } - vid_c_command_control_fw = (unsigned char *)fw_boot->data; - vid_c_command_control_fw_size = (u32) fw_boot->size; - - rc = request_firmware(&fw_mpg4_dec, VIDC_MPG4_DEC_FW, - vid_c_device_p->device); - if (rc) { - ERR("request_firmware for %s failed with error %d\n", - VIDC_BOOT_FW, rc); - status = FALSE; - goto boot_fw_free; - } - vid_c_mpg4_dec_fw = (unsigned char *)fw_mpg4_dec->data; - vid_c_mpg4_dec_fw_size = (u32) fw_mpg4_dec->size; - - - rc = request_firmware(&fw_h263_dec, VIDC_H263_DEC_FW, - vid_c_device_p->device); - if (rc) { - ERR("request_firmware for %s failed with error %d\n", - VIDC_BOOT_FW, rc); - status = FALSE; - goto mp4dec_fw_free; - } - vid_c_h263_dec_fw = (unsigned char *)fw_h263_dec->data; - vid_c_h263_dec_fw_size = (u32) fw_h263_dec->size; - - rc = request_firmware(&fw_h264_dec, VIDC_H264_DEC_FW, - vid_c_device_p->device); - if (rc) { - ERR("request_firmware for %s failed with error %d\n", - VIDC_BOOT_FW, rc); - status = FALSE; - goto h263dec_fw_free; - } - vid_c_h264_dec_fw = (unsigned char *)fw_h264_dec->data; - vid_c_h264_dec_fw_size = (u32) fw_h264_dec->size; - - rc = request_firmware(&fw_mpg4_enc, VIDC_MPG4_ENC_FW, - vid_c_device_p->device); - if (rc) { - ERR("request_firmware for %s failed with error %d\n", - VIDC_BOOT_FW, rc); - status = FALSE; - goto h264dec_fw_free; - } - vid_c_mpg4_enc_fw = (unsigned char *)fw_mpg4_enc->data; - vid_c_mpg4_enc_fw_size = (u32) fw_mpg4_enc->size; - - rc = request_firmware(&fw_h264_enc, VIDC_H264_ENC_FW, - vid_c_device_p->device); - if (rc) { - ERR("request_firmware for %s failed with error %d\n", - VIDC_BOOT_FW, rc); - status = FALSE; - goto mp4enc_fw_free; - } - vid_c_h264_enc_fw = (unsigned char *)fw_h264_enc->data; - vid_c_h264_enc_fw_size = (u32) fw_h264_enc->size; - vid_c_device_p->get_firmware = 1; - } - - vid_c_device_p->firmware_refcount++; - - mutex_unlock(&vid_c_device_p->lock); - return status; - - release_firmware(fw_h264_enc); -mp4enc_fw_free: - release_firmware(fw_mpg4_enc); -h264dec_fw_free: - release_firmware(fw_h264_dec); -h263dec_fw_free: - release_firmware(fw_h263_dec); -mp4dec_fw_free: - release_firmware(fw_mpg4_dec); -boot_fw_free: - release_firmware(fw_boot); - mutex_unlock(&vid_c_device_p->lock); - return FALSE; -} -EXPORT_SYMBOL(vid_c_load_firmware); - -void vid_c_release_firmware(void) -{ - mutex_lock(&vid_c_device_p->lock); - if (vid_c_device_p->firmware_refcount > 0) - vid_c_device_p->firmware_refcount--; - else - vid_c_device_p->firmware_refcount = 0; - mutex_unlock(&vid_c_device_p->lock); -} -EXPORT_SYMBOL(vid_c_release_firmware); u32 vid_c_lookup_addr_table(struct video_client_ctx *client_ctx, - enum buffer_dir buffer_type, - u32 search_with_user_vaddr, - unsigned long *user_vaddr, - unsigned long *kernel_vaddr, - unsigned long *phy_addr, int *pmem_fd, - struct file **file, s32 *buffer_index) + enum buffer_dir buffer_type, u32 search_with_user_vaddr, + void __user **user_addr, void **kern_addr, phys_addr_t *phys_addr, + int *pmem_fd, struct file **file, s32 *buffer_index) { u32 num_of_buffers; u32 i; struct buf_addr_table *buf_addr_table; - u32 found = FALSE; + u32 found = false; if (!client_ctx) - return FALSE; + return false; if (buffer_type == BUFFER_TYPE_INPUT) { buf_addr_table = client_ctx->input_buf_addr_table; num_of_buffers = client_ctx->num_of_input_buffers; - DBG("%s(): buffer_type = INPUT \n", __func__); - + DBG("%s: buffer_type = INPUT\n", __func__); } else { buf_addr_table = client_ctx->output_buf_addr_table; num_of_buffers = client_ctx->num_of_output_buffers; - DBG("%s(): buffer_type = OUTPUT \n", __func__); + DBG("%s: buffer_type = OUTPUT\n", __func__); } for (i = 0; i < num_of_buffers; ++i) { if (search_with_user_vaddr) { - if (*user_vaddr == buf_addr_table[i].user_vaddr) { - *kernel_vaddr = buf_addr_table[i].kernel_vaddr; - found = TRUE; - DBG("%s() : client_ctx = %p." - " user_virt_addr = 0x%08lx is found", - __func__, client_ctx, *user_vaddr); + if (*user_addr == buf_addr_table[i].user_addr) { + *kern_addr = buf_addr_table[i].kern_addr; + found = true; + DBG("%s: client_ctx=%p user_addr=%p is found\n", + __func__, client_ctx, *user_addr); break; } } else { - if (*kernel_vaddr == buf_addr_table[i].kernel_vaddr) { - *user_vaddr = buf_addr_table[i].user_vaddr; - found = TRUE; - DBG("%s() : client_ctx = %p." - " kernel_virt_addr = 0x%08lx is found", - __func__, client_ctx, *kernel_vaddr); + if (*kern_addr == buf_addr_table[i].kern_addr) { + *user_addr = buf_addr_table[i].user_addr; + found = true; + DBG("%s: client_ctx=%p kern_addr=%p is found", + __func__, client_ctx, *kern_addr); break; } } } - if (found) { - *phy_addr = buf_addr_table[i].phy_addr; - *pmem_fd = buf_addr_table[i].pmem_fd; - *file = buf_addr_table[i].file; - *buffer_index = i; - - if (search_with_user_vaddr) - DBG("kernel_vaddr = 0x%08lx, phy_addr = 0x%08lx " - " pmem_fd = %d, struct *file = %p " - "buffer_index = %d \n", *kernel_vaddr, - *phy_addr, *pmem_fd, *file, *buffer_index); - else - DBG("user_vaddr = 0x%08lx, phy_addr = 0x%08lx " - " pmem_fd = %d, struct *file = %p " - "buffer_index = %d \n", *user_vaddr, *phy_addr, - *pmem_fd, *file, *buffer_index); - return TRUE; - } else { + if (!found) { if (search_with_user_vaddr) - DBG("%s() : client_ctx = %p user_virt_addr = 0x%08lx" - " Not Found.\n", __func__, client_ctx, *user_vaddr); + DBG("%s: client_ctx=%p user_addr=%p not found\n", + __func__, client_ctx, *user_addr); else - DBG("%s() : client_ctx = %p kernel_virt_addr = 0x%08lx" - " Not Found.\n", __func__, client_ctx, - *kernel_vaddr); - return FALSE; + DBG("%s: client_ctx=%p kern_addr=%p not found\n", + __func__, client_ctx, *kern_addr); + return false; } + + *phys_addr = buf_addr_table[i].phys_addr; + *pmem_fd = buf_addr_table[i].pmem_fd; + *file = buf_addr_table[i].file; + *buffer_index = i; + + if (search_with_user_vaddr) + DBG("kern_addr=%p phys_addr=%X pmem_fd=%d " + "struct *file=%p buffer_index=%d\n", *kern_addr, + *phys_addr, *pmem_fd, *file, *buffer_index); + else + DBG("user_addr=%p phys_addr=%X pmem_fd=%d, " + "struct *file=%p buffer_index=%d\n", *user_addr, + *phys_addr, *pmem_fd, *file, *buffer_index); + return true; } EXPORT_SYMBOL(vid_c_lookup_addr_table); -u32 vid_c_timer_create(void (*pf_timer_handler)(void *), - void *p_user_data, void **pp_timer_handle) +u32 vid_c_timer_create(void (*pf_timer_handler)(void *), void *user_data, + void **pp_timer_handle) { struct vid_c_timer *hw_timer = NULL; if (!pf_timer_handler || !pp_timer_handle) { - DBG("%s(): timer creation failed \n ", __func__); - return FALSE; + DBG("%s: timer creation failed\n", __func__); + return false; } hw_timer = kzalloc(sizeof(struct vid_c_timer), GFP_KERNEL); if (!hw_timer) { - DBG("%s(): timer creation failed in allocation \n ", __func__); - return FALSE; + DBG("%s: timer creation failed in allocation\n", __func__); + return false; } init_timer(&hw_timer->hw_timeout); hw_timer->hw_timeout.data = (unsigned long)hw_timer; hw_timer->hw_timeout.function = vid_c_timer_fn; hw_timer->cb_func = pf_timer_handler; - hw_timer->userdata = p_user_data; + hw_timer->userdata = user_data; *pp_timer_handle = hw_timer; - return TRUE; + return true; } EXPORT_SYMBOL(vid_c_timer_create); -void vid_c_timer_release(void *p_timer_handle) +void vid_c_timer_release(void *timer_handle) { - kfree(p_timer_handle); + kfree(timer_handle); } EXPORT_SYMBOL(vid_c_timer_release); -void vid_c_timer_start(void *p_timer_handle, u32 n_time_out) +void vid_c_timer_start(void *timer_handle, u32 time_out) { - struct vid_c_timer *hw_timer = (struct vid_c_timer *)p_timer_handle; - DBG("%s(): start timer\n ", __func__); + struct vid_c_timer *hw_timer = timer_handle; + DBG("%s: start timer\n ", __func__); if (hw_timer) { - hw_timer->hw_timeout.expires = jiffies + 1*HZ; + hw_timer->hw_timeout.expires = jiffies + 1 * HZ; add_timer(&hw_timer->hw_timeout); } } EXPORT_SYMBOL(vid_c_timer_start); -void vid_c_timer_stop(void *p_timer_handle) +void vid_c_timer_stop(void *timer_handle) { - struct vid_c_timer *hw_timer = (struct vid_c_timer *)p_timer_handle; - DBG("%s(): stop timer\n ", __func__); + struct vid_c_timer *hw_timer = timer_handle; + DBG("%s: stop timer\n ", __func__); if (hw_timer) del_timer(&hw_timer->hw_timeout); } diff --git a/drivers/misc/video_core/720p/init/video_core_init.h b/drivers/misc/video_core/720p/init/video_core_init.h index 1fdc017ef4b63..d8e40f35aa359 100644 --- a/drivers/misc/video_core/720p/init/video_core_init.h +++ b/drivers/misc/video_core/720p/init/video_core_init.h @@ -40,9 +40,9 @@ enum buffer_dir { }; struct buf_addr_table { - unsigned long user_vaddr; - unsigned long kernel_vaddr; - unsigned long phy_addr; + void __user *user_addr; + void *kern_addr; + phys_addr_t phys_addr; int pmem_fd; struct file *file; }; @@ -74,25 +74,22 @@ u32 vid_c_enable_pwr_rail(void); u32 vid_c_disable_pwr_rail(void); #else - u32 vid_c_enable_clk(unsigned long hclk_rate); u32 vid_c_disable_clk(void); - #endif int vid_c_load_firmware(void); void vid_c_release_firmware(void); u32 vid_c_lookup_addr_table(struct video_client_ctx *client_ctx, -enum buffer_dir buffer_type, u32 search_with_user_vaddr, -unsigned long *user_vaddr, unsigned long *kernel_vaddr, -unsigned long *phy_addr, int *pmem_fd, struct file **file, -s32 *buffer_index); + enum buffer_dir buffer_type, u32 search_with_user_vaddr, + void __user **user_addr, void **kernel_addr, phys_addr_t *phys_addr, + int *pmem_fd, struct file **file, s32 *buffer_index); u32 vid_c_timer_create(void (*pf_timer_handler)(void *), - void *p_user_data, void **pp_timer_handle); -void vid_c_timer_release(void *p_timer_handle); -void vid_c_timer_start(void *p_timer_handle, u32 n_time_out); -void vid_c_timer_stop(void *p_timer_handle); + void *user_data, void **pp_timer_handle); +void vid_c_timer_release(void *timer_handle); +void vid_c_timer_start(void *timer_handle, u32 time_out); +void vid_c_timer_stop(void *timer_handle); #endif diff --git a/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.c b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.c index 1bf916c7cefba..7ad199bf285f6 100644 --- a/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.c +++ b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.c @@ -52,232 +52,224 @@ static unsigned int axi_clk_freq_table_dec[2] = { MSM_AXI_FLOW_VIDEO_PLAYBACK_720P }; #endif -static u32 res_trk_convert_freq_to_perf_lvl(u64 n_freq) +static u32 res_trk_convert_freq_to_perf_lvl(u64 freq) { - u64 n_perf_lvl; - u64 n_temp; + u64 perf_lvl; + u64 temp; - VCDRES_MSG_MED("\n %s():: n_freq = %u\n", __func__, (u32)n_freq); + pr_debug("\n %s():: freq = %u\n", __func__, (u32)freq); - if (!n_freq) + if (!freq) return 0; - n_temp = n_freq * 1000; - do_div(n_temp, VCD_RESTRK_HZ_PER_1000_PERFLVL); - n_perf_lvl = (u32)n_temp; - VCDRES_MSG_MED("\n %s(): n_perf_lvl = %u\n", __func__, - (u32)n_perf_lvl); + temp = freq * 1000; + do_div(temp, VCD_RESTRK_HZ_PER_1000_PERFLVL); + perf_lvl = (u32)temp; + pr_debug("\n %s(): perf_lvl = %u\n", __func__, (u32)perf_lvl); - return (u32)n_perf_lvl; + return (u32)perf_lvl; } -static u32 res_trk_convert_perf_lvl_to_freq(u64 n_perf_lvl) +static u32 res_trk_convert_perf_lvl_to_freq(u64 perf_lvl) { - u64 n_freq, n_temp; + u64 freq, temp; - VCDRES_MSG_MED("\n %s():: n_perf_lvl = %u\n", __func__, - (u32)n_perf_lvl); - n_temp = (n_perf_lvl * VCD_RESTRK_HZ_PER_1000_PERFLVL) + 999; - do_div(n_temp, 1000); - n_freq = (u32)n_temp; - VCDRES_MSG_MED("\n %s(): n_freq = %u\n", __func__, (u32)n_freq); + pr_debug("\n %s():: perf_lvl = %u\n", __func__, + (u32)perf_lvl); + temp = (perf_lvl * VCD_RESTRK_HZ_PER_1000_PERFLVL) + 999; + do_div(temp, 1000); + freq = (u32)temp; + pr_debug("\n %s(): freq = %u\n", __func__, (u32)freq); - return (u32)n_freq; + return (u32)freq; } u32 res_trk_power_up(void) { - VCDRES_MSG_LOW("clk_regime_rail_enable"); - VCDRES_MSG_LOW("clk_regime_sel_rail_control"); + pr_debug("clk_regime_rail_enable\n"); + pr_debug("clk_regime_sel_rail_control\n"); #ifdef AXI_CLK_SCALING { int rc; - VCDRES_MSG_MED("\n res_trk_power_up():: " + pr_debug("\n res_trk_power_up():: " "Calling AXI add requirement\n"); rc = pm_qos_add_requirement(PM_QOS_SYSTEM_BUS_FREQ, MSM_AXI_QOS_NAME, PM_QOS_DEFAULT_VALUE); if (rc < 0) { - VCDRES_MSG_ERROR("Request AXI bus QOS fails. rc = %d\n", - rc); - return FALSE; + pr_err("Request AXI bus QOS fails. rc = %d\n", rc); + return false; } } #endif #ifdef USE_RES_TRACKER - VCDRES_MSG_MED("\n res_trk_power_up():: Calling " + pr_debug("\n res_trk_power_up():: Calling " "vid_c_enable_pwr_rail()\n"); return vid_c_enable_pwr_rail(); #endif - return TRUE; + return true; } u32 res_trk_power_down(void) { - VCDRES_MSG_LOW("clk_regime_rail_disable"); + pr_debug("clk_regime_rail_disable\n"); #ifdef AXI_CLK_SCALING - VCDRES_MSG_MED("\n res_trk_power_down()::" + pr_debug("\n res_trk_power_down()::" "Calling AXI remove requirement\n"); pm_qos_remove_requirement(PM_QOS_SYSTEM_BUS_FREQ, MSM_AXI_QOS_NAME); #endif #ifdef USE_RES_TRACKER - VCDRES_MSG_MED("\n res_trk_power_down():: Calling " + pr_debug("\n res_trk_power_down():: Calling " "vid_c_disable_pwr_rail()\n"); return vid_c_disable_pwr_rail(); #endif - return TRUE; + return true; } u32 res_trk_enable_clocks(void) { - VCDRES_MSG_LOW("clk_regime_msm_enable"); + pr_debug("clk_regime_msm_enable\n"); #ifdef USE_RES_TRACKER - VCDRES_MSG_MED("\n res_trk_enable_clocks():: Calling " + pr_debug("\n res_trk_enable_clocks():: Calling " "vid_c_enable_clk()\n"); return vid_c_enable_clk(); #endif - return TRUE; + return true; } u32 res_trk_disable_clocks(void) { - VCDRES_MSG_LOW("clk_regime_msm_disable"); + pr_debug("clk_regime_msm_disable\n"); #ifdef USE_RES_TRACKER - VCDRES_MSG_MED("\n res_trk_disable_clocks():: Calling " + pr_debug("\n res_trk_disable_clocks():: Calling " "vid_c_disable_clk()\n"); return vid_c_disable_clk(); #endif - return TRUE; + return true; } u32 res_trk_get_max_perf_level(u32 *pn_max_perf_lvl) { if (!pn_max_perf_lvl) { - VCDRES_MSG_ERROR("%s(): pn_max_perf_lvl is NULL\n", - __func__); - return FALSE; + pr_err("%s(): pn_max_perf_lvl is NULL\n", __func__); + return false; } *pn_max_perf_lvl = VCD_RESTRK_MAX_PERF_LEVEL; - return TRUE; + return true; } -u32 res_trk_set_perf_level(u32 n_req_perf_lvl, u32 *pn_set_perf_lvl, - struct vcd_clnt_ctxt_type_t *p_cctxt) +u32 res_trk_set_perf_level(u32 req_perf_lvl, u32 *pn_set_perf_lvl, + struct vcd_clnt_ctxt *cctxt) { u32 axi_freq = 0, mfc_freq = 0, calc_mfc_freq = 0; - int rc = -1; if (!pn_set_perf_lvl) { - VCDRES_MSG_ERROR("%s(): pn_perf_lvl is NULL\n", - __func__); - return FALSE; + pr_err("%s(): pn_perf_lvl is NULL\n", __func__); + return false; } - VCDRES_MSG_LOW("%s(), n_req_perf_lvl = %d", __func__, n_req_perf_lvl); - if (p_cctxt) { + pr_debug("%s(), req_perf_lvl = %d\n", __func__, req_perf_lvl); + if (cctxt) { calc_mfc_freq = res_trk_convert_perf_lvl_to_freq( - (u64)n_req_perf_lvl); + (u64)req_perf_lvl); if (calc_mfc_freq < VCD_RESTRK_MIN_FREQ_POINT) calc_mfc_freq = VCD_RESTRK_MIN_FREQ_POINT; else if (calc_mfc_freq > VCD_RESTRK_MAX_FREQ_POINT) calc_mfc_freq = VCD_RESTRK_MAX_FREQ_POINT; - if (!p_cctxt->b_decoding) { - if (n_req_perf_lvl >= VGA_PERF_LEVEL) { + if (!cctxt->decoding) { + if (req_perf_lvl >= VGA_PERF_LEVEL) { mfc_freq = mfc_clk_freq_table[2]; axi_freq = axi_clk_freq_table_enc[1]; } else { mfc_freq = mfc_clk_freq_table[0]; axi_freq = axi_clk_freq_table_enc[0]; } - VCDRES_MSG_HIGH("\n ENCODER: axi_freq = %u" + pr_debug("\n ENCODER: axi_freq = %u" ", mfc_freq = %u, calc_mfc_freq = %u," - " n_req_perf_lvl = %u", axi_freq, + " req_perf_lvl = %u", axi_freq, mfc_freq, calc_mfc_freq, - n_req_perf_lvl); + req_perf_lvl); } else { - if (n_req_perf_lvl <= QVGA_PERF_LEVEL) { + if (req_perf_lvl <= QVGA_PERF_LEVEL) { mfc_freq = mfc_clk_freq_table[0]; axi_freq = axi_clk_freq_table_dec[0]; } else { axi_freq = axi_clk_freq_table_dec[0]; - if (n_req_perf_lvl <= VGA_PERF_LEVEL) + if (req_perf_lvl <= VGA_PERF_LEVEL) mfc_freq = mfc_clk_freq_table[0]; - else if (n_req_perf_lvl <= WVGA_PERF_LEVEL) + else if (req_perf_lvl <= WVGA_PERF_LEVEL) mfc_freq = mfc_clk_freq_table[1]; else { mfc_freq = mfc_clk_freq_table[2]; axi_freq = axi_clk_freq_table_dec[1]; } } - VCDRES_MSG_HIGH("\n DECODER: axi_freq = %u" + pr_debug("\n DECODER: axi_freq = %u" ", mfc_freq = %u, calc_mfc_freq = %u," - " n_req_perf_lvl = %u", axi_freq, + " req_perf_lvl = %u", axi_freq, mfc_freq, calc_mfc_freq, - n_req_perf_lvl); + req_perf_lvl); } } else { - VCDRES_MSG_HIGH("%s() WARNING:: p_cctxt is NULL", __func__); - return TRUE; + pr_debug("%s() WARNING:: cctxt is NULL\n", __func__); + return true; } #ifdef AXI_CLK_SCALING - if (n_req_perf_lvl != VCD_RESTRK_MIN_PERF_LEVEL) { - VCDRES_MSG_HIGH("\n %s(): Setting AXI freq to %u", + if (req_perf_lvl != VCD_RESTRK_MIN_PERF_LEVEL) { + int rc = -1; + pr_debug("\n %s(): Setting AXI freq to %u", __func__, axi_freq); rc = pm_qos_update_requirement(PM_QOS_SYSTEM_BUS_FREQ, MSM_AXI_QOS_NAME, axi_freq); - if (rc < 0) { - VCDRES_MSG_ERROR("\n Update AXI bus QOS fails," - "rc = %d\n", rc); - return FALSE; + if (rc < 0) { + pr_err("\n Update AXI bus QOS fails,rc = %d\n", rc); + return false; } } #endif #ifdef USE_RES_TRACKER - if (n_req_perf_lvl != VCD_RESTRK_MIN_PERF_LEVEL) { - VCDRES_MSG_HIGH("\n %s(): Setting MFC freq to %u", + if (req_perf_lvl != VCD_RESTRK_MIN_PERF_LEVEL) { + pr_debug("\n %s(): Setting MFC freq to %u", __func__, mfc_freq); if (!vid_c_sel_clk_rate(mfc_freq)) { - VCDRES_MSG_ERROR("%s(): vid_c_sel_clk_rate FAILED\n", - __func__); + pr_err("%s(): vid_c_sel_clk_rate FAILED\n", __func__); *pn_set_perf_lvl = 0; - return FALSE; + return false; } } #endif *pn_set_perf_lvl = res_trk_convert_freq_to_perf_lvl((u64) mfc_freq); - return TRUE; + return true; } u32 res_trk_get_curr_perf_level(u32 *pn_perf_lvl) { - unsigned long n_freq; + unsigned long freq; if (!pn_perf_lvl) { - VCDRES_MSG_ERROR("%s(): pn_perf_lvl is NULL\n", - __func__); - return FALSE; + pr_err("%s(): pn_perf_lvl is NULL\n", __func__); + return false; } - VCDRES_MSG_LOW("clk_regime_msm_get_clk_freq_hz"); - if (!vid_c_get_clk_rate(&n_freq)) { - VCDRES_MSG_ERROR("%s(): vid_c_get_clk_rate FAILED\n", - __func__); + pr_debug("clk_regime_msm_get_clk_freq_hz\n"); + if (!vid_c_get_clk_rate(&freq)) { + pr_err("%s(): vid_c_get_clk_rate FAILED\n", __func__); *pn_perf_lvl = 0; - return FALSE; + return false; } - *pn_perf_lvl = res_trk_convert_freq_to_perf_lvl((u64) n_freq); - VCDRES_MSG_MED("%s(): n_freq = %lu, *pn_perf_lvl = %u", n_freq, - *pn_perf_lvl); - return TRUE; + *pn_perf_lvl = res_trk_convert_freq_to_perf_lvl((u64) freq); + pr_debug("%s(): freq = %lu, *pn_perf_lvl = %u\n", __func__, + freq, *pn_perf_lvl); + return true; } diff --git a/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.h b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.h index 8e3093f098f18..f937e79fcc35f 100644 --- a/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.h +++ b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker.h @@ -37,20 +37,4 @@ #define VCD_RESTRK_MAX_FREQ_POINT 170667000 #define VCD_RESTRK_HZ_PER_1000_PERFLVL 1580250 -#if DEBUG - -#define VCDRES_MSG_LOW(xx_fmt...) printk(KERN_INFO "\n\t* " xx_fmt) -#define VCDRES_MSG_MED(xx_fmt...) printk(KERN_INFO "\n * " xx_fmt) - -#else - -#define VCDRES_MSG_LOW(xx_fmt...) -#define VCDRES_MSG_MED(xx_fmt...) - -#endif - -#define VCDRES_MSG_HIGH(xx_fmt...) printk(KERN_WARNING "\n" xx_fmt) -#define VCDRES_MSG_ERROR(xx_fmt...) printk(KERN_ERR "\n err: " xx_fmt) -#define VCDRES_MSG_FATAL(xx_fmt...) printk(KERN_ERR "\n " xx_fmt) - #endif diff --git a/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker_api.h b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker_api.h index 6e93ef9bb6382..b099d09330f15 100644 --- a/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker_api.h +++ b/drivers/misc/video_core/720p/resource_tracker/vcd_res_tracker_api.h @@ -36,8 +36,8 @@ u32 res_trk_power_down(void); u32 res_trk_enable_clocks(void); u32 res_trk_disable_clocks(void); u32 res_trk_get_max_perf_level(u32 *pn_max_perf_lvl); -u32 res_trk_set_perf_level(u32 n_req_perf_lvl, u32 *pn_set_perf_lvl, - struct vcd_clnt_ctxt_type_t *p_cctxt); +u32 res_trk_set_perf_level(u32 req_perf_lvl, u32 *pn_set_perf_lvl, + struct vcd_clnt_ctxt *cctxt); u32 res_trk_get_curr_perf_level(u32 *pn_perf_lvl); #endif diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.c b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.c index c824e045bf077..169f082f086ba 100644 --- a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.c +++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.c @@ -26,212 +26,212 @@ static const u32 SCHED_TKNBKT_FILL_NORMLZ_SCALE = 100; static const u32 SCHED_TIME_MAX = 0xffffffff; -SCHED_INLINE u32 SCHED_SUCCEEDED(enum sched_status_type status) +SCHED_INLINE u32 SCHED_SUCCEEDED(enum sched_status status) { SCHED_MSG_LOW("SCHED_SUCCEEDED check: status = %d", status); if (status == SCHED_S_OK) - return TRUE; + return true; else - return FALSE; + return false; } -SCHED_INLINE u32 SCHED_FAILED(enum sched_status_type status) +SCHED_INLINE u32 SCHED_FAILED(enum sched_status status) { SCHED_MSG_LOW("SCHED_FAILED check: status = %d", status); if (status >= SCHED_S_EFAIL) - return TRUE; + return true; else - return FALSE; + return false; } -static void sched_clear_clnt_ctx(struct sched_clnt_ctx_type *p_ctx) +static void sched_clear_clnt_ctx(struct sched_clnt_ctx *ctx) { - if (p_ctx->a_clnt_frm_q) - SCHED_FREE(p_ctx->a_clnt_frm_q); - (void)SCHED_CRITSEC_RELEASE(p_ctx->clnt_cs); + if (ctx->clnt_frm_q) + SCHED_FREE(ctx->clnt_frm_q); + (void)SCHED_CRITSEC_RELEASE(ctx->clnt_cs); } SCHED_INLINE void sched_free_clnt_node( - struct _sched_clnt_list_node_type *p_clnt_node) + struct _sched_clnt_list_node *clnt_node) { - sched_clear_clnt_ctx(&p_clnt_node->data); - SCHED_FREE(p_clnt_node); + sched_clear_clnt_ctx(&clnt_node->data); + SCHED_FREE(clnt_node); } -enum sched_status_type sched_clear_clnt_list( - struct _sched_clnt_list_node_type *p_clnt_lst) { - struct _sched_clnt_list_node_type *p_clnt_node; +enum sched_status sched_clear_clnt_list( + struct _sched_clnt_list_node *clnt_lst) { + struct _sched_clnt_list_node *clnt_node; - while (p_clnt_lst) { - (void)SCHED_CRITSEC_ENTER(p_clnt_lst->data.clnt_cs); - p_clnt_node = p_clnt_lst; - p_clnt_lst = p_clnt_lst->p_next; - sched_free_clnt_node(p_clnt_node); + while (clnt_lst) { + (void)SCHED_CRITSEC_ENTER(clnt_lst->data.clnt_cs); + clnt_node = clnt_lst; + clnt_lst = clnt_lst->next; + sched_free_clnt_node(clnt_node); } return SCHED_S_OK; } -static SCHED_INLINE enum sched_status_type sched_alloc_frm_q( - struct sched_clnt_ctx_type *p_ctx) +static SCHED_INLINE enum sched_status sched_alloc_frm_q( + struct sched_clnt_ctx *ctx) { - p_ctx->a_clnt_frm_q = (struct sched_clnt_q_elem *) + ctx->clnt_frm_q = (struct sched_clnt_q_elem *) SCHED_MALLOC(sizeof(struct sched_clnt_q_elem) * - p_ctx->n_max_queue_len); + ctx->max_queue_len); - if (!p_ctx->a_clnt_frm_q) { + if (!ctx->clnt_frm_q) { SCHED_MSG_ERR("Could not allocate clnt frm Q. Out of memory"); return SCHED_S_ENOMEM; } - SCHED_MEMSET(p_ctx->a_clnt_frm_q, - 0, sizeof(struct sched_clnt_q_elem) * p_ctx->n_max_queue_len); - p_ctx->n_q_head = 0; - p_ctx->n_q_tail = -1; - p_ctx->n_q_len = 0; + SCHED_MEMSET(ctx->clnt_frm_q, + 0, sizeof(struct sched_clnt_q_elem) * ctx->max_queue_len); + ctx->q_head = 0; + ctx->q_tail = -1; + ctx->q_len = 0; SCHED_MSG_MED("Clnt frm Q allocted & initialized"); return SCHED_S_OK; } static SCHED_INLINE void sched_de_q_head_frm - (struct sched_clnt_ctx_type *p_ctx, - struct sched_clnt_q_elem *p_q_elem) { - *p_q_elem = p_ctx->a_clnt_frm_q[p_ctx->n_q_head]; + (struct sched_clnt_ctx *ctx, + struct sched_clnt_q_elem *q_elem) { + *q_elem = ctx->clnt_frm_q[ctx->q_head]; - memset(&p_ctx->a_clnt_frm_q[p_ctx->n_q_head], 0, + memset(&ctx->clnt_frm_q[ctx->q_head], 0, sizeof(struct sched_clnt_q_elem)); /*Update the circular queue head index.*/ - p_ctx->n_q_head = (p_ctx->n_q_head + 1) % p_ctx->n_max_queue_len; - p_ctx->n_q_len--; + ctx->q_head = (ctx->q_head + 1) % ctx->max_queue_len; + ctx->q_len--; } static SCHED_INLINE void sched_tkn_bkt_fill_normalize - (struct sched_clnt_ctx_type *p_ctx) + (struct sched_clnt_ctx *ctx) { - p_ctx->n_bkt_curr_tkns_nmlzd = - (p_ctx->n_bkt_curr_tkns * SCHED_TKNBKT_FILL_NORMLZ_SCALE) / - p_ctx->n_p_tkn_per_frm; + ctx->bkt_curr_tkns_nmlzd = + (ctx->bkt_curr_tkns * SCHED_TKNBKT_FILL_NORMLZ_SCALE) / + ctx->tkn_per_frm; } -static void sched_tkn_bkt_config(struct sched_clnt_ctx_type *p_ctx) +static void sched_tkn_bkt_config(struct sched_clnt_ctx *ctx) { - p_ctx->n_bkt_size = p_ctx->n_p_tkn_per_frm * SCHED_TKNBKT_SIZE_FACTOR; - p_ctx->n_bkt_quies_cap = p_ctx->n_bkt_size; - p_ctx->n_bkt_curr_tkns = - SCHED_MIN(p_ctx->n_bkt_curr_tkns, p_ctx->n_bkt_size); + ctx->bkt_size = ctx->tkn_per_frm * SCHED_TKNBKT_SIZE_FACTOR; + ctx->bkt_quies_cap = ctx->bkt_size; + ctx->bkt_curr_tkns = + SCHED_MIN(ctx->bkt_curr_tkns, ctx->bkt_size); } static void sched_tkn_bkt_supply( - struct sched_clnt_ctx_type *p_ctx, u32 n_curr_time) + struct sched_clnt_ctx *ctx, u32 curr_time) { - u32 n_delta; - u32 n_num_tkns; + u32 delta; + u32 num_tkns; /*Check if there's time wrap-around since last token supply time.*/ - if (n_curr_time < p_ctx->n_bkt_lst_sup_time) { + if (curr_time < ctx->bkt_lst_sup_time) { SCHED_MSG_HIGH("Current time wrap around detected"); - n_delta = - SCHED_TIME_MAX - p_ctx->n_bkt_lst_sup_time + n_curr_time; + delta = + SCHED_TIME_MAX - ctx->bkt_lst_sup_time + curr_time; } else - n_delta = n_curr_time - p_ctx->n_bkt_lst_sup_time; + delta = curr_time - ctx->bkt_lst_sup_time; /*Proceed only if there is any time elapsed since our last supply time.*/ - if (n_delta > 0) { + if (delta > 0) { /*Calculate the number of tokens that we can supply based on time elapsed and the client's token supply rate.*/ - n_num_tkns = n_delta * p_ctx->n_curr_p_tkn_rate / 1000; + num_tkns = delta * ctx->curr_p_tkn_rate / 1000; - if (n_num_tkns > 0) { - p_ctx->n_bkt_curr_tkns = SCHED_MIN(p_ctx->n_bkt_size, - p_ctx->n_bkt_curr_tkns + n_num_tkns); + if (num_tkns > 0) { + ctx->bkt_curr_tkns = SCHED_MIN(ctx->bkt_size, + ctx->bkt_curr_tkns + num_tkns); - if ((n_delta * p_ctx->n_curr_p_tkn_rate % 1000)) { - n_delta = (n_num_tkns * 1000 + - (p_ctx->n_curr_p_tkn_rate >> 1)) - / p_ctx->n_curr_p_tkn_rate; + if ((delta * ctx->curr_p_tkn_rate % 1000)) { + delta = (num_tkns * 1000 + + (ctx->curr_p_tkn_rate >> 1)) + / ctx->curr_p_tkn_rate; if ((SCHED_TIME_MAX - - p_ctx->n_bkt_lst_sup_time) < n_delta) { + ctx->bkt_lst_sup_time) < delta) { SCHED_MSG_HIGH ("Handling for current time wrap " "around"); - p_ctx->n_bkt_lst_sup_time = n_delta - + ctx->bkt_lst_sup_time = delta - (SCHED_TIME_MAX - - p_ctx->n_bkt_lst_sup_time); + ctx->bkt_lst_sup_time); } else - p_ctx->n_bkt_lst_sup_time += n_delta; + ctx->bkt_lst_sup_time += delta; } else - p_ctx->n_bkt_lst_sup_time = n_curr_time; + ctx->bkt_lst_sup_time = curr_time; - if (p_ctx->n_bkt_curr_tkns > - (s32) p_ctx->n_bkt_quies_cap) { + if (ctx->bkt_curr_tkns > + (s32) ctx->bkt_quies_cap) { SCHED_MSG_HIGH ("Client Quiesence detected. Capping " - "n_bkt_curr_tkns"); - p_ctx->n_bkt_curr_tkns = p_ctx->n_p_tkn_per_frm; + "bkt_curr_tkns"); + ctx->bkt_curr_tkns = ctx->tkn_per_frm; } - sched_tkn_bkt_fill_normalize(p_ctx); + sched_tkn_bkt_fill_normalize(ctx); } } } static SCHED_INLINE void sched_tkn_bkt_consume( - struct sched_clnt_ctx_type *p_ctx) { - p_ctx->n_bkt_curr_tkns -= p_ctx->n_p_tkn_per_frm; + struct sched_clnt_ctx *ctx) { + ctx->bkt_curr_tkns -= ctx->tkn_per_frm; } static SCHED_INLINE u32 sched_clnt_frm_is_cnfmnt - (struct sched_clnt_ctx_type *p_ctx) + (struct sched_clnt_ctx *ctx) { - if (p_ctx->n_bkt_curr_tkns >= (s32) p_ctx->n_p_tkn_per_frm) - return TRUE; + if (ctx->bkt_curr_tkns >= (s32) ctx->tkn_per_frm) + return true; else - return FALSE; + return false; } /* end of sched_clnt_frm_is_conformant */ -static struct sched_clnt_ctx_type *sched_elect_cnfmnt - (struct sched_clnt_ctx_type *p_prov_elect, - struct sched_clnt_ctx_type *p_new_cand) { +static struct sched_clnt_ctx *sched_elect_cnfmnt + (struct sched_clnt_ctx *prov_elect, + struct sched_clnt_ctx *new_cand) { /*If there is no provisional elect client then the new candidate becomes the first one.*/ - if (!p_prov_elect) - return p_new_cand; + if (!prov_elect) + return new_cand; /*Here we want to pick the client who has accumulated the most tokens from the time of attaining single frame conformance. Since we are comparing between clients we use the available normalized token bucket occupancy value.*/ - if (p_prov_elect->n_bkt_curr_tkns_nmlzd >= - p_new_cand->n_bkt_curr_tkns_nmlzd) { - return p_prov_elect; + if (prov_elect->bkt_curr_tkns_nmlzd >= + new_cand->bkt_curr_tkns_nmlzd) { + return prov_elect; } else { /*We had held on to this provisional elect conformant client critical section. Since new candidate has won the election leave critical section of earlier provisional elect. */ - (void)SCHED_CRITSEC_LEAVE(p_prov_elect->clnt_cs); - return p_new_cand; + (void)SCHED_CRITSEC_LEAVE(prov_elect->clnt_cs); + return new_cand; } } -static struct sched_clnt_ctx_type *sched_elect_non_cnfmnt - (struct sched_clnt_ctx_type *p_prov_elect, - struct sched_clnt_ctx_type *p_new_cand) { +static struct sched_clnt_ctx *sched_elect_non_cnfmnt + (struct sched_clnt_ctx *prov_elect, + struct sched_clnt_ctx *new_cand) { /*If there is no provisional elect client then the new candidate becomes the first one.*/ - if (!p_prov_elect) - return p_new_cand; + if (!prov_elect) + return new_cand; /*Here we want to pick the client who is closest to attaining a single frame conformance. Since we are comparing between clients we use the available @@ -239,33 +239,33 @@ static struct sched_clnt_ctx_type *sched_elect_non_cnfmnt Also if the provisional elect or the new contender (in that order) have an end of frame marker set we give it priority over deciding by frame conformance method mentiond earlier.*/ - if (p_prov_elect->n_eof_marker > 0) { - return p_prov_elect; - } else if (p_new_cand->n_eof_marker > 0) { + if (prov_elect->eof_marker > 0) { + return prov_elect; + } else if (new_cand->eof_marker > 0) { /*We had held on to this provisional elect non conformant client critical section. Since new candidate has won the election leave critical section of earlier provisional elect. */ - (void)SCHED_CRITSEC_LEAVE(p_prov_elect->clnt_cs); + (void)SCHED_CRITSEC_LEAVE(prov_elect->clnt_cs); - return p_new_cand; - } else if (p_prov_elect->n_bkt_curr_tkns_nmlzd >= - p_new_cand->n_bkt_curr_tkns_nmlzd) { - return p_prov_elect; + return new_cand; + } else if (prov_elect->bkt_curr_tkns_nmlzd >= + new_cand->bkt_curr_tkns_nmlzd) { + return prov_elect; } else { /*Similar to above case leave critical section of earlier provisional elect.*/ - (void)SCHED_CRITSEC_LEAVE(p_prov_elect->clnt_cs); - return p_new_cand; + (void)SCHED_CRITSEC_LEAVE(prov_elect->clnt_cs); + return new_cand; } } -static struct sched_clnt_ctx_type *sched_elect_non_rt - (struct sched_ctx_type *p_sched_ctx) { - struct _sched_clnt_list_node_type *p_node = NULL; - struct _sched_clnt_list_node_type *p_start_node = NULL; - u32 b_found = FALSE; +static struct sched_clnt_ctx *sched_elect_non_rt + (struct sched_ctx *sched_ctx) { + struct _sched_clnt_list_node *node = NULL; + struct _sched_clnt_list_node *start_node = NULL; + u32 found = false; /*For non real time clients we are using a round robin election algorithm. @@ -273,25 +273,25 @@ static struct sched_clnt_ctx_type *sched_elect_non_rt and return its context. We also need to skip the client if certain conditions (mentioned below) are not met*/ - if (!p_sched_ctx->p_n_rt_last_sched) - p_start_node = p_node = p_sched_ctx->p_n_rt_head; + if (!sched_ctx->non_rt_last_sched) + start_node = node = sched_ctx->non_rt_head; else { - if (!p_sched_ctx->p_n_rt_last_sched->p_next) - p_start_node = p_sched_ctx->p_n_rt_head; + if (!sched_ctx->non_rt_last_sched->next) + start_node = sched_ctx->non_rt_head; else - p_start_node = p_sched_ctx->p_n_rt_last_sched->p_next; + start_node = sched_ctx->non_rt_last_sched->next; - p_node = p_start_node; + node = start_node; } do { - (void)SCHED_CRITSEC_ENTER(p_node->data.clnt_cs); + (void)SCHED_CRITSEC_ENTER(node->data.clnt_cs); /*Check if the client can be considered for this round of scheduling.*/ - if (sched_consider_clnt_for_sched(&p_node->data)) { - b_found = TRUE; - p_sched_ctx->p_n_rt_last_sched = p_node; + if (sched_consider_clnt_for_sched(&node->data)) { + found = true; + sched_ctx->non_rt_last_sched = node; } /*If this client is not the election winner then leave its critical @@ -299,20 +299,20 @@ static struct sched_clnt_ctx_type *sched_elect_non_rt If we have found a winner we want to hold on to its critical section. We would leave its critical section after we are done with dequeueing a frame from the client context.*/ - if (!b_found) - (void)SCHED_CRITSEC_LEAVE(p_node->data.clnt_cs); + if (!found) + (void)SCHED_CRITSEC_LEAVE(node->data.clnt_cs); - if (!p_node->p_next) - p_node = p_sched_ctx->p_n_rt_head; + if (!node->next) + node = sched_ctx->non_rt_head; else - p_node = p_node->p_next; + node = node->next; - } while (p_node != p_start_node); + } while (node != start_node); - if (b_found) { + if (found) { SCHED_MSG_LOW("Non real time client selected"); - return &p_sched_ctx->p_n_rt_last_sched->data; + return &sched_ctx->non_rt_last_sched->data; } else { SCHED_MSG_MED ("No non-real time client available for scheduling"); @@ -322,18 +322,18 @@ static struct sched_clnt_ctx_type *sched_elect_non_rt } -static enum sched_status_type sched_process_set_p_tkn_rate( - struct sched_ctx_type *p_sched_ctx, - struct sched_clnt_ctx_type *p_clnt_ctx, - union sched_value_type *p_param_value) { - u32 n_curr_time = 0; +static enum sched_status sched_process_set_p_tkn_rate( + struct sched_ctx *sched_ctx, + struct sched_clnt_ctx *clnt_ctx, + union sched_value_type *param_value) { + u32 curr_time = 0; - if (p_param_value->un_value == p_clnt_ctx->n_curr_p_tkn_rate) + if (param_value->un_value == clnt_ctx->curr_p_tkn_rate) return SCHED_S_OK; - if ((p_sched_ctx->n_total_clnt_bw - p_clnt_ctx->n_curr_p_tkn_rate + - p_param_value->un_value) > p_sched_ctx->n_perf_lvl) { + if ((sched_ctx->total_clnt_bw - clnt_ctx->curr_p_tkn_rate + + param_value->un_value) > sched_ctx->perf_lvl) { SCHED_MSG_HIGH ("Perf level insufficient for requested P Tkn rate"); @@ -341,156 +341,156 @@ static enum sched_status_type sched_process_set_p_tkn_rate( /*Get current time. We need this for token supply. If we didn't get a valid current time value just return*/ - if (SCHED_FAILED(SCHED_GET_CURRENT_TIME(&n_curr_time))) { + if (SCHED_FAILED(SCHED_GET_CURRENT_TIME(&curr_time))) { SCHED_MSG_ERR("Get current time failed"); return SCHED_S_EFAIL; } - /*Before we go ahead and update the Current p_tkn rate, we fill + /*Before we go ahead and update the Current tkn rate, we fill the token bucket upto current time instance.*/ - sched_tkn_bkt_supply(p_clnt_ctx, n_curr_time); + sched_tkn_bkt_supply(clnt_ctx, curr_time); /*Next, update the current value of total client bandwidth with - the new p_tkn rate of the client.*/ - p_sched_ctx->n_total_clnt_bw = p_sched_ctx->n_total_clnt_bw - - p_clnt_ctx->n_curr_p_tkn_rate + p_param_value->un_value; - p_clnt_ctx->n_curr_p_tkn_rate = p_param_value->un_value; + the new tkn rate of the client.*/ + sched_ctx->total_clnt_bw = sched_ctx->total_clnt_bw - + clnt_ctx->curr_p_tkn_rate + param_value->un_value; + clnt_ctx->curr_p_tkn_rate = param_value->un_value; /*Since the current Ptkn rate (i.e. current alloted bandwidth) of the client has changed we need to update client's token bucket configuration*/ - sched_tkn_bkt_config(p_clnt_ctx); + sched_tkn_bkt_config(clnt_ctx); return SCHED_S_OK; } -static enum sched_status_type sched_process_add_rt_clnt( - struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node) { - enum sched_status_type status; - struct sched_clnt_ctx_type *p_clnt_ctx = &p_clnt_node->data; - struct _sched_clnt_list_node_type *p_tmp_node; +static enum sched_status sched_process_add_rt_clnt( + struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node) { + enum sched_status status; + struct sched_clnt_ctx *clnt_ctx = &clnt_node->data; + struct _sched_clnt_list_node *tmp_node; /*Validate real time client specific parameters.*/ - if (!p_clnt_ctx->n_curr_p_tkn_rate) + if (!clnt_ctx->curr_p_tkn_rate) SCHED_MSG_HIGH("Allocated token rate is zero"); /*Check if our performance level setting can sustain the new client*/ - if (p_sched_ctx->n_total_clnt_bw + p_clnt_ctx->n_curr_p_tkn_rate > - p_sched_ctx->n_perf_lvl) { + if (sched_ctx->total_clnt_bw + clnt_ctx->curr_p_tkn_rate > + sched_ctx->perf_lvl) { SCHED_MSG_HIGH("Not enough bandwidth to support client"); SCHED_MSG_HIGH ("curr_perflvl=%d, curr_bw=%d, newclnt_ptknrate=%d", - p_sched_ctx->n_perf_lvl, p_sched_ctx->n_total_clnt_bw, - p_clnt_ctx->n_curr_p_tkn_rate); + sched_ctx->perf_lvl, sched_ctx->total_clnt_bw, + clnt_ctx->curr_p_tkn_rate); } /*Allocate the client frame queue*/ - status = sched_alloc_frm_q(p_clnt_ctx); + status = sched_alloc_frm_q(clnt_ctx); if (SCHED_SUCCEEDED(status)) { /*Allocate the token bucket*/ - sched_tkn_bkt_config(p_clnt_ctx); + sched_tkn_bkt_config(clnt_ctx); /*We start with empty token bucket*/ - p_clnt_ctx->n_bkt_curr_tkns = 0; - p_clnt_ctx->n_bkt_curr_tkns_nmlzd = 0; + clnt_ctx->bkt_curr_tkns = 0; + clnt_ctx->bkt_curr_tkns_nmlzd = 0; /*Add the client to the real time client list and increase the total client bandwidth.*/ - p_tmp_node = p_sched_ctx->p_rt_head; - p_sched_ctx->p_rt_head = p_clnt_node; - p_sched_ctx->p_rt_head->p_next = p_tmp_node; - p_sched_ctx->n_rt_clnts++; - p_sched_ctx->n_total_clnt_bw += p_clnt_ctx->n_curr_p_tkn_rate; + tmp_node = sched_ctx->rt_head; + sched_ctx->rt_head = clnt_node; + sched_ctx->rt_head->next = tmp_node; + sched_ctx->rt_clnts++; + sched_ctx->total_clnt_bw += clnt_ctx->curr_p_tkn_rate; } return status; } -static enum sched_status_type sched_process_add_non_rt_clnt( - struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node) { - enum sched_status_type status; - struct sched_clnt_ctx_type *p_clnt_ctx = &p_clnt_node->data; - struct _sched_clnt_list_node_type *p_tmp_node; +static enum sched_status sched_process_add_non_rt_clnt( + struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node) { + enum sched_status status; + struct sched_clnt_ctx *clnt_ctx = &clnt_node->data; + struct _sched_clnt_list_node *tmp_node; /*Allocate the client frame queue*/ - status = sched_alloc_frm_q(p_clnt_ctx); + status = sched_alloc_frm_q(clnt_ctx); if (SCHED_SUCCEEDED(status)) { /*Add the client to the real time client list and increase the total client bandwidth.*/ - p_tmp_node = p_sched_ctx->p_n_rt_head; - p_sched_ctx->p_n_rt_head = p_clnt_node; - p_sched_ctx->p_n_rt_head->p_next = p_tmp_node; - p_sched_ctx->n_n_rt_clnts++; + tmp_node = sched_ctx->non_rt_head; + sched_ctx->non_rt_head = clnt_node; + sched_ctx->non_rt_head->next = tmp_node; + sched_ctx->non_rt_clnts++; } return status; } -enum sched_status_type sched_process_add_clnt( - struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node, - struct sched_client_init_param_type *p_init_param) { - enum sched_status_type status = SCHED_S_OK; +enum sched_status sched_process_add_clnt( + struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node, + struct sched_client_init_param *init_param) { + enum sched_status status = SCHED_S_OK; - SCHED_MEMSET(p_clnt_node, 0, sizeof(struct _sched_clnt_list_node_type)); + SCHED_MEMSET(clnt_node, 0, sizeof(struct _sched_clnt_list_node)); /*Validate all initialization parameters*/ - if (!p_init_param->n_p_tkn_per_frm || - !p_init_param->frm_rate.n_numer || - !p_init_param->frm_rate.n_denom || - !p_init_param->n_max_queue_len || - !p_init_param->n_o_tkn_max || - !p_init_param->n_o_tkn_per_ip_frm || - p_init_param->n_o_tkn_init > p_init_param->n_o_tkn_max || - p_init_param->n_o_tkn_per_ip_frm > p_init_param->n_o_tkn_max) { + if (!init_param->tkn_per_frm || + !init_param->frm_rate.numer || + !init_param->frm_rate.denom || + !init_param->max_queue_len || + !init_param->o_tkn_max || + !init_param->o_tkn_per_ip_frm || + init_param->o_tkn_init > init_param->o_tkn_max || + init_param->o_tkn_per_ip_frm > init_param->o_tkn_max) { SCHED_MSG_ERR("Bad initialization parameters"); return SCHED_S_EBADPARM; } /*Store all initialization parameters*/ - p_clnt_node->data.client_ctgy = p_init_param->client_ctgy; - p_clnt_node->data.n_curr_p_tkn_rate = p_init_param->n_alloc_p_tkn_rate; - p_clnt_node->data.frm_rate = p_init_param->frm_rate; - p_clnt_node->data.n_max_queue_len = p_init_param->n_max_queue_len; - p_clnt_node->data.n_o_tkn_max = p_init_param->n_o_tkn_max; - p_clnt_node->data.n_o_tkn_per_ip_frm = p_init_param->n_o_tkn_per_ip_frm; - p_clnt_node->data.n_curr_o_tkns = p_init_param->n_o_tkn_init; - p_clnt_node->data.n_p_tkn_per_frm = p_init_param->n_p_tkn_per_frm; - p_clnt_node->data.p_client_data = p_init_param->p_client_data; - p_clnt_node->data.b_sched_state = TRUE; + clnt_node->data.client_ctgy = init_param->client_ctgy; + clnt_node->data.curr_p_tkn_rate = init_param->alloc_p_tkn_rate; + clnt_node->data.frm_rate = init_param->frm_rate; + clnt_node->data.max_queue_len = init_param->max_queue_len; + clnt_node->data.o_tkn_max = init_param->o_tkn_max; + clnt_node->data.o_tkn_per_ip_frm = init_param->o_tkn_per_ip_frm; + clnt_node->data.curr_o_tkns = init_param->o_tkn_init; + clnt_node->data.tkn_per_frm = init_param->tkn_per_frm; + clnt_node->data.client_data = init_param->client_data; + clnt_node->data.sched_state = true; SCHED_MSG_HIGH("Adding new client of category %d", - p_clnt_node->data.client_ctgy); + clnt_node->data.client_ctgy); SCHED_MSG_MED("Allocated P token rate (per sec) = %d", - p_clnt_node->data.n_curr_p_tkn_rate); + clnt_node->data.curr_p_tkn_rate); SCHED_MSG_MED("Frame rate = %d / %d", - p_clnt_node->data.frm_rate.n_numer, - p_clnt_node->data.frm_rate.n_denom); - SCHED_MSG_MED("Max_queue_len = %d", p_clnt_node->data.n_max_queue_len); - SCHED_MSG_MED("Max O tokens = %d", p_clnt_node->data.n_o_tkn_max); + clnt_node->data.frm_rate.numer, + clnt_node->data.frm_rate.denom); + SCHED_MSG_MED("Max_queue_len = %d", clnt_node->data.max_queue_len); + SCHED_MSG_MED("Max O tokens = %d", clnt_node->data.o_tkn_max); SCHED_MSG_MED("O tokens threshold = %d", - p_clnt_node->data.n_o_tkn_per_ip_frm); + clnt_node->data.o_tkn_per_ip_frm); SCHED_MSG_MED("P tokens per frame = %d", - p_clnt_node->data.n_p_tkn_per_frm); - SCHED_MSG_MED("Client data ptr = %p", p_clnt_node->data.p_client_data); + clnt_node->data.tkn_per_frm); + SCHED_MSG_MED("Client data ptr = %p", clnt_node->data.client_data); - if (SCHED_FAILED(SCHED_CRITSEC_CREATE(&p_clnt_node->data.clnt_cs))) + if (SCHED_FAILED(SCHED_CRITSEC_CREATE(&clnt_node->data.clnt_cs))) return SCHED_S_EFAIL; /*Configure the client context based on client category.*/ - switch (p_clnt_node->data.client_ctgy) { + switch (clnt_node->data.client_ctgy) { case SCHED_CLNT_RT_BUFF: case SCHED_CLNT_RT_NOBUFF: { status = - sched_process_add_rt_clnt(p_sched_ctx, p_clnt_node); + sched_process_add_rt_clnt(sched_ctx, clnt_node); break; } case SCHED_CLNT_NONRT: { status = - sched_process_add_non_rt_clnt(p_sched_ctx, - p_clnt_node); + sched_process_add_non_rt_clnt(sched_ctx, + clnt_node); break; } @@ -504,40 +504,40 @@ enum sched_status_type sched_process_add_clnt( return status; } -enum sched_status_type sched_process_remove_clnt( - struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node) { +enum sched_status sched_process_remove_clnt( + struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node) { - (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); + (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs); /*Handling if the client frame queue is not empty. Just return and let Codec driver dequeue all frames for this client before calling remove client*/ - if (p_clnt_node->data.n_q_len) { + if (clnt_node->data.q_len) { SCHED_MSG_ERR("Cannot remove client. Queue is not empty"); return SCHED_S_EINVALST; } /*Based on client category, remove the client node from the appropriate scheduler client list*/ - switch (p_clnt_node->data.client_ctgy) { + switch (clnt_node->data.client_ctgy) { case SCHED_CLNT_RT_BUFF: case SCHED_CLNT_RT_NOBUFF: { - sched_remove_node_from_list(&p_sched_ctx->p_rt_head, - p_clnt_node); - p_sched_ctx->n_rt_clnts--; - p_sched_ctx->n_total_clnt_bw -= - p_clnt_node->data.n_curr_p_tkn_rate; + sched_remove_node_from_list(&sched_ctx->rt_head, + clnt_node); + sched_ctx->rt_clnts--; + sched_ctx->total_clnt_bw -= + clnt_node->data.curr_p_tkn_rate; break; } case SCHED_CLNT_NONRT: { - sched_remove_node_from_list(&p_sched_ctx->p_n_rt_head, - p_clnt_node); - p_sched_ctx->n_n_rt_clnts--; + sched_remove_node_from_list(&sched_ctx->non_rt_head, + clnt_node); + sched_ctx->non_rt_clnts--; break; } @@ -551,279 +551,279 @@ enum sched_status_type sched_process_remove_clnt( /*Now that client node is off the scheduler client list free up resources that its been using.*/ SCHED_MSG_HIGH("Removing new client of category %d", - p_clnt_node->data.client_ctgy); + clnt_node->data.client_ctgy); SCHED_MSG_MED("Allocated P token rate (per sec) = %d", - p_clnt_node->data.n_curr_p_tkn_rate); + clnt_node->data.curr_p_tkn_rate); SCHED_MSG_MED("Frame rate = %d / %d", - p_clnt_node->data.frm_rate.n_numer, - p_clnt_node->data.frm_rate.n_denom); - SCHED_MSG_MED("Max_queue_len = %d", p_clnt_node->data.n_max_queue_len); - SCHED_MSG_MED("Max O tokens = %d", p_clnt_node->data.n_o_tkn_max); + clnt_node->data.frm_rate.numer, + clnt_node->data.frm_rate.denom); + SCHED_MSG_MED("Max_queue_len = %d", clnt_node->data.max_queue_len); + SCHED_MSG_MED("Max O tokens = %d", clnt_node->data.o_tkn_max); SCHED_MSG_MED("P tokens per frame = %d", - p_clnt_node->data.n_p_tkn_per_frm); - SCHED_MSG_MED("Client data ptr = %p", p_clnt_node->data.p_client_data); - sched_free_clnt_node(p_clnt_node); + clnt_node->data.tkn_per_frm); + SCHED_MSG_MED("Client data ptr = %p", clnt_node->data.client_data); + sched_free_clnt_node(clnt_node); return SCHED_S_OK; } -enum sched_status_type sched_process_flush_clnt_buff( - struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node, void **pp_frm_data) { - struct sched_clnt_ctx_type *p_clnt_ctx; - enum sched_status_type status = SCHED_S_OK; +enum sched_status sched_process_flush_clnt_buff( + struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node, void **pp_frm_data) { + struct sched_clnt_ctx *clnt_ctx; + enum sched_status status = SCHED_S_OK; struct sched_clnt_q_elem q_elem; - p_clnt_ctx = &p_clnt_node->data; + clnt_ctx = &clnt_node->data; /*If the client queue is empty just return an QEMPTY status*/ - if (!p_clnt_ctx->n_q_len) { + if (!clnt_ctx->q_len) { status = SCHED_S_QEMPTY; } else { - p_clnt_ctx->b_flushing = TRUE; + clnt_ctx->flushing = true; /*If the client queue is not empty just remove and return the element at the front of the queue.*/ - sched_de_q_head_frm(p_clnt_ctx, &q_elem); - *pp_frm_data = q_elem.p_frm_data; + sched_de_q_head_frm(clnt_ctx, &q_elem); + *pp_frm_data = q_elem.frm_data; } /*If the Queue was orginially empty OR if it got empty after latest De_queue we reset the flushing and First_frame flags. Token bucket contents are also emptied.Queue pointers are reset. o_tkns are restored.*/ - if (!p_clnt_ctx->n_q_len) { - p_clnt_ctx->b_flushing = FALSE; - p_clnt_ctx->b_first_frm = FALSE; - p_clnt_ctx->n_bkt_curr_tkns = 0; - p_clnt_ctx->n_bkt_curr_tkns_nmlzd = 0; - p_clnt_ctx->n_bkt_lst_sup_time = 0; - p_clnt_ctx->n_q_head = 0; - p_clnt_ctx->n_q_tail = -1; + if (!clnt_ctx->q_len) { + clnt_ctx->flushing = false; + clnt_ctx->first_frm = false; + clnt_ctx->bkt_curr_tkns = 0; + clnt_ctx->bkt_curr_tkns_nmlzd = 0; + clnt_ctx->bkt_lst_sup_time = 0; + clnt_ctx->q_head = 0; + clnt_ctx->q_tail = -1; SCHED_MSG_HIGH ("Client flushed and re-initialized. Client category %d", - p_clnt_ctx->client_ctgy); + clnt_ctx->client_ctgy); SCHED_MSG_MED("Client allocated P token rate (per sec) = %d", - p_clnt_ctx->n_curr_p_tkn_rate); + clnt_ctx->curr_p_tkn_rate); SCHED_MSG_MED("Client frame rate = %d / %d", - p_clnt_ctx->frm_rate.n_numer, - p_clnt_ctx->frm_rate.n_denom); + clnt_ctx->frm_rate.numer, + clnt_ctx->frm_rate.denom); SCHED_MSG_MED("Client P tokens per frame = %d", - p_clnt_ctx->n_p_tkn_per_frm); + clnt_ctx->tkn_per_frm); } return status; } -SCHED_INLINE enum sched_status_type sched_process_mark_clnt_eof( - struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node) { +SCHED_INLINE enum sched_status sched_process_mark_clnt_eof( + struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node) { - if (!p_clnt_node->data.n_q_len) + if (!clnt_node->data.q_len) return SCHED_S_QEMPTY; - if (!p_clnt_node->data.a_clnt_frm_q[p_clnt_node->data.n_q_tail].b_eof) { + if (!clnt_node->data.clnt_frm_q[clnt_node->data.q_tail].eof) { /*Just increment the EOF marker count in the client context.*/ - p_clnt_node->data.n_eof_marker++; - p_clnt_node->data.a_clnt_frm_q[p_clnt_node->data.n_q_tail]. - b_eof = TRUE; + clnt_node->data.eof_marker++; + clnt_node->data.clnt_frm_q[clnt_node->data.q_tail]. + eof = true; } else SCHED_MSG_HIGH("Current frame is already marked EOF"); SCHED_MSG_HIGH("Client marked for end of frames. Client category %d", - p_clnt_node->data.client_ctgy); + clnt_node->data.client_ctgy); SCHED_MSG_MED("Client allocated P token rate (per sec) = %d", - p_clnt_node->data.n_curr_p_tkn_rate); + clnt_node->data.curr_p_tkn_rate); SCHED_MSG_MED("Client frame rate = %d / %d", - p_clnt_node->data.frm_rate.n_numer, - p_clnt_node->data.frm_rate.n_denom); + clnt_node->data.frm_rate.numer, + clnt_node->data.frm_rate.denom); SCHED_MSG_MED("Client P tokens per frame = %d", - p_clnt_node->data.n_p_tkn_per_frm); + clnt_node->data.tkn_per_frm); return SCHED_S_OK; } -enum sched_status_type sched_process_update_clnt_o_tkn( - struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node, - u32 b_type, u32 n_o_tkn) { +enum sched_status sched_process_update_clnt_o_tkn( + struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node, + u32 type, u32 o_tkn) { /*Act based on the type of update.*/ - if (b_type) { + if (type) { /*Just replenish the output tokens the client currently has with the provided number while not going over the max value.*/ - p_clnt_node->data.n_curr_o_tkns = - SCHED_MIN(p_clnt_node->data.n_curr_o_tkns + n_o_tkn, - p_clnt_node->data.n_o_tkn_max); + clnt_node->data.curr_o_tkns = + SCHED_MIN(clnt_node->data.curr_o_tkns + o_tkn, + clnt_node->data.o_tkn_max); } else { /*Just subtract the give number of output tokens from the count the client currently has while not going less than 0.*/ - if (n_o_tkn >= p_clnt_node->data.n_curr_o_tkns) - p_clnt_node->data.n_curr_o_tkns = 0; + if (o_tkn >= clnt_node->data.curr_o_tkns) + clnt_node->data.curr_o_tkns = 0; else - p_clnt_node->data.n_curr_o_tkns -= n_o_tkn; + clnt_node->data.curr_o_tkns -= o_tkn; } - SCHED_MSG_LOW("%d O tokens restored for client", n_o_tkn); + SCHED_MSG_LOW("%d O tokens restored for client", o_tkn); SCHED_MSG_LOW("Client Curr_o_tkns = %d", - p_clnt_node->data.n_curr_o_tkns); - SCHED_MSG_LOW("Client category = %d", p_clnt_node->data.client_ctgy); + clnt_node->data.curr_o_tkns); + SCHED_MSG_LOW("Client category = %d", clnt_node->data.client_ctgy); SCHED_MSG_LOW("Client allocated P token rate (per sec) = %d", - p_clnt_node->data.n_curr_p_tkn_rate); + clnt_node->data.curr_p_tkn_rate); SCHED_MSG_LOW("Client frame rate = %d / %d", - p_clnt_node->data.frm_rate.n_numer, - p_clnt_node->data.frm_rate.n_denom); + clnt_node->data.frm_rate.numer, + clnt_node->data.frm_rate.denom); SCHED_MSG_LOW("Client P tokens per frame = %d", - p_clnt_node->data.n_p_tkn_per_frm); + clnt_node->data.tkn_per_frm); return SCHED_S_OK; } -enum sched_status_type sched_process_en_q_frm( - struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node, void *p_frm_data) { - struct sched_clnt_ctx_type *p_clnt_ctx; - u32 n_curr_time = 0; +enum sched_status sched_process_en_q_frm( + struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node, void *frm_data) { + struct sched_clnt_ctx *clnt_ctx; + u32 curr_time = 0; - p_clnt_ctx = &p_clnt_node->data; + clnt_ctx = &clnt_node->data; /*Check if the client queue is full already*/ - if (p_clnt_ctx->n_q_len == p_clnt_ctx->n_max_queue_len) { + if (clnt_ctx->q_len == clnt_ctx->max_queue_len) { SCHED_MSG_HIGH("Cannot enqueue. Client queue is full"); return SCHED_S_QFULL; } /*Check if the client queue is being flushed.*/ - if (p_clnt_ctx->b_flushing) { + if (clnt_ctx->flushing) { SCHED_MSG_ERR("Cannot enqueue. Client queue is being flushed"); return SCHED_S_EINVALST; } /*Reposition tail, increase Q length and add the frame data to Q*/ - p_clnt_ctx->n_q_tail = - (p_clnt_ctx->n_q_tail + 1) % p_clnt_ctx->n_max_queue_len; + clnt_ctx->q_tail = + (clnt_ctx->q_tail + 1) % clnt_ctx->max_queue_len; - p_clnt_ctx->n_q_len++; + clnt_ctx->q_len++; - p_clnt_ctx->a_clnt_frm_q[p_clnt_ctx->n_q_tail].p_frm_data = p_frm_data; - p_clnt_ctx->a_clnt_frm_q[p_clnt_ctx->n_q_tail].b_eof = FALSE; + clnt_ctx->clnt_frm_q[clnt_ctx->q_tail].frm_data = frm_data; + clnt_ctx->clnt_frm_q[clnt_ctx->q_tail].eof = false; /*If this is the first frame being queued for this client then, get current time. We now start the token supply clock for the client. Supply tokens required for a single frame processing while storing the current time as the last supply time and marking that first frame is received.*/ - if (!p_clnt_ctx->b_first_frm) { + if (!clnt_ctx->first_frm) { SCHED_MSG_HIGH("Client first frame enqueued"); - if (p_clnt_ctx->client_ctgy != SCHED_CLNT_NONRT) { + if (clnt_ctx->client_ctgy != SCHED_CLNT_NONRT) { if (SCHED_SUCCEEDED - (SCHED_GET_CURRENT_TIME(&n_curr_time))) { - p_clnt_ctx->n_bkt_curr_tkns = - p_clnt_ctx->n_p_tkn_per_frm; - p_clnt_ctx->n_bkt_lst_sup_time = n_curr_time; - p_clnt_ctx->b_first_frm = TRUE; + (SCHED_GET_CURRENT_TIME(&curr_time))) { + clnt_ctx->bkt_curr_tkns = + clnt_ctx->tkn_per_frm; + clnt_ctx->bkt_lst_sup_time = curr_time; + clnt_ctx->first_frm = true; } } else - p_clnt_ctx->b_first_frm = TRUE; + clnt_ctx->first_frm = true; } SCHED_MSG_LOW("Client frame enqueued. Queue fill status = %d / %d", - p_clnt_ctx->n_q_len, p_clnt_ctx->n_max_queue_len); - SCHED_MSG_LOW("Client category = %d", p_clnt_ctx->client_ctgy); + clnt_ctx->q_len, clnt_ctx->max_queue_len); + SCHED_MSG_LOW("Client category = %d", clnt_ctx->client_ctgy); SCHED_MSG_LOW("Client allocated P token rate (per sec) = %d", - p_clnt_ctx->n_curr_p_tkn_rate); + clnt_ctx->curr_p_tkn_rate); SCHED_MSG_LOW("Client frame rate = %d / %d", - p_clnt_ctx->frm_rate.n_numer, - p_clnt_ctx->frm_rate.n_denom); + clnt_ctx->frm_rate.numer, + clnt_ctx->frm_rate.denom); SCHED_MSG_LOW("Client P tokens per frame = %d", - p_clnt_ctx->n_p_tkn_per_frm); + clnt_ctx->tkn_per_frm); return SCHED_S_OK; } -enum sched_status_type sched_process_re_en_q_frm( - struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node, - void *p_frm_data) +enum sched_status sched_process_re_en_q_frm( + struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node, + void *frm_data) { - struct sched_clnt_ctx_type *p_clnt_ctx; - u32 n_curr_time = 0; + struct sched_clnt_ctx *clnt_ctx; + u32 curr_time = 0; - p_clnt_ctx = &p_clnt_node->data; + clnt_ctx = &clnt_node->data; - if (p_clnt_ctx->n_q_len == p_clnt_ctx->n_max_queue_len) { + if (clnt_ctx->q_len == clnt_ctx->max_queue_len) { SCHED_MSG_ERR("Cannot re-enqueue. Client queue is full"); return SCHED_S_QFULL; } - if (p_clnt_ctx->b_flushing) { + if (clnt_ctx->flushing) { SCHED_MSG_ERR("Cannot re-enqueue. Client" " queue is being flushed"); return SCHED_S_EINVALST; } - p_clnt_ctx->n_q_head = - (p_clnt_ctx->n_q_head + p_clnt_ctx->n_max_queue_len - 1) % - p_clnt_ctx->n_max_queue_len; + clnt_ctx->q_head = + (clnt_ctx->q_head + clnt_ctx->max_queue_len - 1) % + clnt_ctx->max_queue_len; - p_clnt_ctx->n_q_len++; + clnt_ctx->q_len++; - p_clnt_ctx->a_clnt_frm_q[p_clnt_ctx->n_q_head].p_frm_data = - p_frm_data; - p_clnt_ctx->a_clnt_frm_q[p_clnt_ctx->n_q_head].b_eof = - FALSE; + clnt_ctx->clnt_frm_q[clnt_ctx->q_head].frm_data = + frm_data; + clnt_ctx->clnt_frm_q[clnt_ctx->q_head].eof = + false; - if (p_clnt_ctx->client_ctgy != SCHED_CLNT_NONRT) { - if (!p_clnt_ctx->b_first_frm) { + if (clnt_ctx->client_ctgy != SCHED_CLNT_NONRT) { + if (!clnt_ctx->first_frm) { SCHED_MSG_HIGH("Client frame " "re-enqueued as first frame"); if (SCHED_SUCCEEDED - (SCHED_GET_CURRENT_TIME(&n_curr_time))) { - p_clnt_ctx->n_bkt_curr_tkns = - p_clnt_ctx->n_p_tkn_per_frm; - p_clnt_ctx->n_bkt_lst_sup_time = - n_curr_time; - p_clnt_ctx->b_first_frm = - TRUE; + (SCHED_GET_CURRENT_TIME(&curr_time))) { + clnt_ctx->bkt_curr_tkns = + clnt_ctx->tkn_per_frm; + clnt_ctx->bkt_lst_sup_time = + curr_time; + clnt_ctx->first_frm = + true; } } else - p_clnt_ctx->n_bkt_curr_tkns += - p_clnt_ctx->n_p_tkn_per_frm; + clnt_ctx->bkt_curr_tkns += + clnt_ctx->tkn_per_frm; } else - p_clnt_ctx->b_first_frm = TRUE; + clnt_ctx->first_frm = true; SCHED_MSG_LOW("Client frame re-enqueued. Queue fill status = %d / %d", - p_clnt_ctx->n_q_len, p_clnt_ctx->n_max_queue_len); - SCHED_MSG_LOW("Client category = %d", p_clnt_ctx->client_ctgy); + clnt_ctx->q_len, clnt_ctx->max_queue_len); + SCHED_MSG_LOW("Client category = %d", clnt_ctx->client_ctgy); SCHED_MSG_LOW("Client allocated P token rate (per sec) = %d", - p_clnt_ctx->n_curr_p_tkn_rate); + clnt_ctx->curr_p_tkn_rate); SCHED_MSG_LOW("Client frame rate = %d / %d", - p_clnt_ctx->frm_rate.n_numer, - p_clnt_ctx->frm_rate.n_denom); + clnt_ctx->frm_rate.numer, + clnt_ctx->frm_rate.denom); SCHED_MSG_LOW("Client P tokens per frame = %d", - p_clnt_ctx->n_p_tkn_per_frm); + clnt_ctx->tkn_per_frm); return SCHED_S_OK; } -enum sched_status_type sched_process_de_q_frm_rt_clnt( - struct sched_ctx_type *p_sched_ctx, - struct sched_clnt_ctx_type **pp_conf_elect_ctx, - struct sched_clnt_ctx_type **pp_non_conf_elect_ctx) { - u32 n_curr_time = 0; - struct _sched_clnt_list_node_type *p_clnt_node; - struct sched_clnt_ctx_type *p_clnt_ctx; +enum sched_status sched_process_de_q_frm_rt_clnt( + struct sched_ctx *sched_ctx, + struct sched_clnt_ctx **pp_conf_elect_ctx, + struct sched_clnt_ctx **pp_non_conf_elect_ctx) { + u32 curr_time = 0; + struct _sched_clnt_list_node *clnt_node; + struct sched_clnt_ctx *clnt_ctx; *pp_conf_elect_ctx = NULL; *pp_non_conf_elect_ctx = NULL; /*Get current time. We need this for token supply. If we didn't get a valid current time value just return*/ - if (SCHED_FAILED(SCHED_GET_CURRENT_TIME(&n_curr_time))) { + if (SCHED_FAILED(SCHED_GET_CURRENT_TIME(&curr_time))) { SCHED_MSG_ERR("Get current time failed"); return SCHED_S_EFAIL; @@ -835,24 +835,24 @@ enum sched_status_type sched_process_de_q_frm_rt_clnt( Supply tokens equivalent to elapsed time since last supply. Also in this same pass, check if each client has a conformant frame or not.*/ - p_clnt_node = p_sched_ctx->p_rt_head; - while (p_clnt_node) { - p_clnt_ctx = &p_clnt_node->data; + clnt_node = sched_ctx->rt_head; + while (clnt_node) { + clnt_ctx = &clnt_node->data; - (void)SCHED_CRITSEC_ENTER(p_clnt_ctx->clnt_cs); + (void)SCHED_CRITSEC_ENTER(clnt_ctx->clnt_cs); - if (sched_consider_clnt_for_sched(p_clnt_ctx)) { - sched_tkn_bkt_supply(p_clnt_ctx, n_curr_time); - if (sched_clnt_frm_is_cnfmnt(p_clnt_ctx)) { + if (sched_consider_clnt_for_sched(clnt_ctx)) { + sched_tkn_bkt_supply(clnt_ctx, curr_time); + if (sched_clnt_frm_is_cnfmnt(clnt_ctx)) { *pp_conf_elect_ctx = sched_elect_cnfmnt(*pp_conf_elect_ctx, - p_clnt_ctx); + clnt_ctx); } else { if (!*pp_conf_elect_ctx) { *pp_non_conf_elect_ctx = sched_elect_non_cnfmnt (*pp_non_conf_elect_ctx, - p_clnt_ctx); + clnt_ctx); } else if (*pp_non_conf_elect_ctx) { (void) SCHED_CRITSEC_LEAVE( @@ -862,28 +862,28 @@ enum sched_status_type sched_process_de_q_frm_rt_clnt( } } } - if (p_clnt_ctx != *pp_conf_elect_ctx - && p_clnt_ctx != *pp_non_conf_elect_ctx) - (void)SCHED_CRITSEC_LEAVE(p_clnt_ctx->clnt_cs); - p_clnt_node = p_clnt_node->p_next; + if (clnt_ctx != *pp_conf_elect_ctx + && clnt_ctx != *pp_non_conf_elect_ctx) + (void)SCHED_CRITSEC_LEAVE(clnt_ctx->clnt_cs); + clnt_node = clnt_node->next; } return SCHED_S_OK; } -enum sched_status_type sched_process_de_q_frm( - struct sched_ctx_type *p_sched_ctx, +enum sched_status sched_process_de_q_frm( + struct sched_ctx *sched_ctx, void **pp_frm_data, void **pp_client_data) { - enum sched_status_type status; - struct sched_clnt_ctx_type *p_sched_clnt_ctx = NULL; - struct sched_clnt_ctx_type *p_conf_elect_ctx; - struct sched_clnt_ctx_type *p_non_conf_elect_ctx; + enum sched_status status; + struct sched_clnt_ctx *sched_clnt_ctx = NULL; + struct sched_clnt_ctx *conf_elect_ctx; + struct sched_clnt_ctx *non_conf_elect_ctx; struct sched_clnt_q_elem q_elem; - status = sched_process_de_q_frm_rt_clnt(p_sched_ctx, - &p_conf_elect_ctx, - &p_non_conf_elect_ctx); + status = sched_process_de_q_frm_rt_clnt(sched_ctx, + &conf_elect_ctx, + &non_conf_elect_ctx); if (SCHED_FAILED(status)) { SCHED_MSG_ERR("sched_process_de_q_frm_rt_clnt ret err=%d", status); @@ -898,57 +898,57 @@ enum sched_status_type sched_process_de_q_frm( a) client with conformant frame b) client with non-conformant frame c) non real-time client*/ - if (p_conf_elect_ctx) { + if (conf_elect_ctx) { SCHED_MSG_LOW("Conformant frame client selected"); - sched_tkn_bkt_consume(p_conf_elect_ctx); - p_sched_clnt_ctx = p_conf_elect_ctx; - } else if (p_non_conf_elect_ctx) { + sched_tkn_bkt_consume(conf_elect_ctx); + sched_clnt_ctx = conf_elect_ctx; + } else if (non_conf_elect_ctx) { SCHED_MSG_LOW("Non-Conformant frame client selected"); - sched_tkn_bkt_consume(p_non_conf_elect_ctx); - p_sched_clnt_ctx = p_non_conf_elect_ctx; - } else if (p_sched_ctx->n_n_rt_clnts) - p_sched_clnt_ctx = sched_elect_non_rt(p_sched_ctx); + sched_tkn_bkt_consume(non_conf_elect_ctx); + sched_clnt_ctx = non_conf_elect_ctx; + } else if (sched_ctx->non_rt_clnts) + sched_clnt_ctx = sched_elect_non_rt(sched_ctx); /*If we have a client that we can schedule, then dequeue the frame at the head of its queue.*/ - if (p_sched_clnt_ctx) { - *pp_client_data = p_sched_clnt_ctx->p_client_data; + if (sched_clnt_ctx) { + *pp_client_data = sched_clnt_ctx->client_data; - sched_de_q_head_frm(p_sched_clnt_ctx, &q_elem); + sched_de_q_head_frm(sched_clnt_ctx, &q_elem); - *pp_frm_data = q_elem.p_frm_data; + *pp_frm_data = q_elem.frm_data; - p_sched_clnt_ctx->n_curr_o_tkns -= - p_sched_clnt_ctx->n_o_tkn_per_ip_frm; + sched_clnt_ctx->curr_o_tkns -= + sched_clnt_ctx->o_tkn_per_ip_frm; /*If the dequeued frame was marked EOF we need to decrement the eof_marker count.*/ - if (q_elem.b_eof) { + if (q_elem.eof) { SCHED_MSG_MED ("Last frame for EOF marked client dequeued"); - p_sched_clnt_ctx->n_eof_marker--; + sched_clnt_ctx->eof_marker--; status = SCHED_S_EOF; } SCHED_MSG_LOW ("Client frame Dequeued. Queue fill status = %d / %d", - p_sched_clnt_ctx->n_q_len, - p_sched_clnt_ctx->n_max_queue_len); + sched_clnt_ctx->q_len, + sched_clnt_ctx->max_queue_len); SCHED_MSG_LOW("Client category = %d", - p_sched_clnt_ctx->client_ctgy); + sched_clnt_ctx->client_ctgy); SCHED_MSG_LOW("Client allocated P token rate (per sec) = %d", - p_sched_clnt_ctx->n_curr_p_tkn_rate); + sched_clnt_ctx->curr_p_tkn_rate); SCHED_MSG_LOW("Client frame rate = %d / %d", - p_sched_clnt_ctx->frm_rate.n_numer, - p_sched_clnt_ctx->frm_rate.n_denom); + sched_clnt_ctx->frm_rate.numer, + sched_clnt_ctx->frm_rate.denom); SCHED_MSG_LOW("Client P tokens per frame = %d", - p_sched_clnt_ctx->n_p_tkn_per_frm); + sched_clnt_ctx->tkn_per_frm); /*We had held on to the election winning client critical section. Leave client critical section before we exit.*/ - (void)SCHED_CRITSEC_LEAVE(p_sched_clnt_ctx->clnt_cs); + (void)SCHED_CRITSEC_LEAVE(sched_clnt_ctx->clnt_cs); } else { status = SCHED_S_QEMPTY; } @@ -957,17 +957,17 @@ enum sched_status_type sched_process_de_q_frm( } -enum sched_status_type sched_process_sched_lvl_get_param( - struct sched_ctx_type *p_sched_ctx, - enum sched_index_type param_index, - union sched_value_type *p_param_value) +enum sched_status sched_process_sched_lvl_get_param( + struct sched_ctx *sched_ctx, + enum sched_index param_index, + union sched_value_type *param_value) { - enum sched_status_type status = SCHED_S_OK; + enum sched_status status = SCHED_S_OK; switch (param_index) { case SCHED_I_PERFLEVEL: { - p_param_value->un_value = p_sched_ctx->n_perf_lvl; + param_value->un_value = sched_ctx->perf_lvl; break; } @@ -980,33 +980,33 @@ enum sched_status_type sched_process_sched_lvl_get_param( return status; } -enum sched_status_type sched_process_sched_lvl_set_param( - struct sched_ctx_type *p_sched_ctx, - enum sched_index_type param_index, - union sched_value_type *p_param_value) +enum sched_status sched_process_sched_lvl_set_param( + struct sched_ctx *sched_ctx, + enum sched_index param_index, + union sched_value_type *param_value) { - enum sched_status_type status = SCHED_S_OK; + enum sched_status status = SCHED_S_OK; SCHED_MSG_HIGH("Set_sched_param index = %u, value = %p", - param_index, (void *)p_param_value); + param_index, (void *)param_value); switch (param_index) { case SCHED_I_PERFLEVEL: { - if (p_sched_ctx->n_total_clnt_bw > - p_param_value->un_value) { + if (sched_ctx->total_clnt_bw > + param_value->un_value) { SCHED_MSG_HIGH ("Perf level being lowered than current " "bandwidth"); SCHED_MSG_HIGH ("curr_perflvl=%d, new_perflvl=%d, " "curr_bw=%d", - p_sched_ctx->n_perf_lvl, - p_param_value->un_value, - p_sched_ctx->n_total_clnt_bw); + sched_ctx->perf_lvl, + param_value->un_value, + sched_ctx->total_clnt_bw); } - p_sched_ctx->n_perf_lvl = p_param_value->un_value; + sched_ctx->perf_lvl = param_value->un_value; break; } @@ -1020,54 +1020,54 @@ enum sched_status_type sched_process_sched_lvl_set_param( return status; } -enum sched_status_type sched_process_clnt_lvl_get_param( - struct sched_ctx_type *p_sched_ctx, - struct sched_clnt_ctx_type *p_clnt_ctx, - enum sched_index_type param_index, - union sched_value_type *p_param_value) { - enum sched_status_type status = SCHED_S_OK; +enum sched_status sched_process_clnt_lvl_get_param( + struct sched_ctx *sched_ctx, + struct sched_clnt_ctx *clnt_ctx, + enum sched_index param_index, + union sched_value_type *param_value) { + enum sched_status status = SCHED_S_OK; switch (param_index) { case SCHED_I_CLNT_CURRQLEN: { - p_param_value->un_value = p_clnt_ctx->n_q_len; + param_value->un_value = clnt_ctx->q_len; break; } case SCHED_I_CLNT_PTKNRATE: { - p_param_value->un_value = p_clnt_ctx->n_curr_p_tkn_rate; + param_value->un_value = clnt_ctx->curr_p_tkn_rate; break; } case SCHED_I_CLNT_PTKNPERFRM: { - p_param_value->un_value = p_clnt_ctx->n_p_tkn_per_frm; + param_value->un_value = clnt_ctx->tkn_per_frm; break; } case SCHED_I_CLNT_FRAMERATE: { - p_param_value->frm_rate = p_clnt_ctx->frm_rate; + param_value->frm_rate = clnt_ctx->frm_rate; break; } case SCHED_I_CLNT_OTKNMAX: { - p_param_value->un_value = p_clnt_ctx->n_o_tkn_max; + param_value->un_value = clnt_ctx->o_tkn_max; break; } case SCHED_I_CLNT_OTKNPERIPFRM: { - p_param_value->un_value = - p_clnt_ctx->n_o_tkn_per_ip_frm; + param_value->un_value = + clnt_ctx->o_tkn_per_ip_frm; break; } case SCHED_I_CLNT_OTKNCURRENT: { - p_param_value->un_value = p_clnt_ctx->n_curr_o_tkns; + param_value->un_value = clnt_ctx->curr_o_tkns; break; } @@ -1080,16 +1080,16 @@ enum sched_status_type sched_process_clnt_lvl_get_param( return status; } -enum sched_status_type sched_process_clnt_lvl_set_param( - struct sched_ctx_type *p_sched_ctx, - struct sched_clnt_ctx_type *p_clnt_ctx, - enum sched_index_type param_index, - union sched_value_type *p_param_value) +enum sched_status sched_process_clnt_lvl_set_param( + struct sched_ctx *sched_ctx, + struct sched_clnt_ctx *clnt_ctx, + enum sched_index param_index, + union sched_value_type *param_value) { - enum sched_status_type status = SCHED_S_OK; + enum sched_status status = SCHED_S_OK; SCHED_MSG_HIGH("Set_clnt_param index = %u, value = %p", - param_index, (void *)p_param_value); + param_index, (void *)param_value); switch (param_index) { case SCHED_I_CLNT_CURRQLEN: @@ -1102,49 +1102,49 @@ enum sched_status_type sched_process_clnt_lvl_set_param( case SCHED_I_CLNT_PTKNRATE: { status = - sched_process_set_p_tkn_rate(p_sched_ctx, - p_clnt_ctx, - p_param_value); + sched_process_set_p_tkn_rate(sched_ctx, + clnt_ctx, + param_value); break; } case SCHED_I_CLNT_PTKNPERFRM: { - p_clnt_ctx->n_p_tkn_per_frm = p_param_value->un_value; - sched_tkn_bkt_config(p_clnt_ctx); + clnt_ctx->tkn_per_frm = param_value->un_value; + sched_tkn_bkt_config(clnt_ctx); break; } case SCHED_I_CLNT_FRAMERATE: { - p_clnt_ctx->frm_rate = p_param_value->frm_rate; + clnt_ctx->frm_rate = param_value->frm_rate; break; } case SCHED_I_CLNT_OTKNMAX: { - if (p_param_value->un_value < - p_clnt_ctx->n_o_tkn_per_ip_frm) { + if (param_value->un_value < + clnt_ctx->o_tkn_per_ip_frm) { status = SCHED_S_EBADPARM; } else { - p_clnt_ctx->n_o_tkn_max = - p_param_value->un_value; + clnt_ctx->o_tkn_max = + param_value->un_value; - p_clnt_ctx->n_curr_o_tkns = - SCHED_MIN(p_clnt_ctx->n_curr_o_tkns, - p_clnt_ctx->n_o_tkn_max); + clnt_ctx->curr_o_tkns = + SCHED_MIN(clnt_ctx->curr_o_tkns, + clnt_ctx->o_tkn_max); } break; } case SCHED_I_CLNT_OTKNPERIPFRM: { - if (p_param_value->un_value > p_clnt_ctx->n_o_tkn_max) { + if (param_value->un_value > clnt_ctx->o_tkn_max) { status = SCHED_S_EBADPARM; } else { - p_clnt_ctx->n_o_tkn_per_ip_frm = - p_param_value->un_value; + clnt_ctx->o_tkn_per_ip_frm = + param_value->un_value; } break; } @@ -1160,88 +1160,88 @@ enum sched_status_type sched_process_clnt_lvl_set_param( } -enum sched_status_type sched_process_suspend_resume_clnt( - struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node, u32 b_state) { - u32 n_curr_time; - struct sched_clnt_ctx_type *p_clnt_ctx = &p_clnt_node->data; +enum sched_status sched_process_suspend_resume_clnt( + struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node, u32 state) { + u32 curr_time; + struct sched_clnt_ctx *clnt_ctx = &clnt_node->data; SCHED_MSG_HIGH("Current client sched_state=%d. Requested state=%d", - p_clnt_ctx->b_sched_state, b_state); + clnt_ctx->sched_state, state); - if (p_clnt_ctx->b_sched_state == b_state) + if (clnt_ctx->sched_state == state) return SCHED_S_OK; - p_clnt_ctx->b_sched_state = b_state; + clnt_ctx->sched_state = state; - if (!SCHED_SUCCEEDED(SCHED_GET_CURRENT_TIME(&n_curr_time))) { + if (!SCHED_SUCCEEDED(SCHED_GET_CURRENT_TIME(&curr_time))) { SCHED_MSG_ERR("Get current time failed"); return SCHED_S_OK; } /* RESUME */ - if (b_state) { - p_clnt_ctx->n_bkt_lst_sup_time = n_curr_time; + if (state) { + clnt_ctx->bkt_lst_sup_time = curr_time; } else { /* SUSPEND */ /*As we are suspending the client we fill the token bucket upto current time instance.*/ - sched_tkn_bkt_supply(p_clnt_ctx, n_curr_time); + sched_tkn_bkt_supply(clnt_ctx, curr_time); } - SCHED_MSG_MED("Client category %d", p_clnt_ctx->client_ctgy); + SCHED_MSG_MED("Client category %d", clnt_ctx->client_ctgy); SCHED_MSG_MED("Client allocated P token rate (per sec) = %d", - p_clnt_ctx->n_curr_p_tkn_rate); + clnt_ctx->curr_p_tkn_rate); SCHED_MSG_MED("Client frame rate = %d / %d", - p_clnt_ctx->frm_rate.n_numer, - p_clnt_ctx->frm_rate.n_denom); + clnt_ctx->frm_rate.numer, + clnt_ctx->frm_rate.denom); SCHED_MSG_MED("Client P tokens per frame = %d", - p_clnt_ctx->n_p_tkn_per_frm); + clnt_ctx->tkn_per_frm); return SCHED_S_OK; } void sched_remove_node_from_list( - struct _sched_clnt_list_node_type **pp_head, - struct _sched_clnt_list_node_type *p_node) + struct _sched_clnt_list_node **pp_head, + struct _sched_clnt_list_node *node) { - u32 b_found = FALSE; - struct _sched_clnt_list_node_type *p_curr = *pp_head; + u32 found = false; + struct _sched_clnt_list_node *curr = *pp_head; - if (!*pp_head || !p_node) { - SCHED_MSG_ERR("Bad params. p_head %p, p_node %p", *pp_head, - p_node); + if (!*pp_head || !node) { + SCHED_MSG_ERR("Bad params. head %p, node %p", *pp_head, + node); return; } - if (p_node == *pp_head) { - *pp_head = p_node->p_next; + if (node == *pp_head) { + *pp_head = node->next; return; } - while (!b_found && p_curr) { - if (p_node == p_curr->p_next) { - p_curr->p_next = p_node->p_next; - b_found = TRUE; + while (!found && curr) { + if (node == curr->next) { + curr->next = node->next; + found = true; } - p_curr = p_curr->p_next; + curr = curr->next; } } SCHED_INLINE u32 sched_consider_clnt_for_sched( - struct sched_clnt_ctx_type *p_clnt_ctx) + struct sched_clnt_ctx *clnt_ctx) { - if (p_clnt_ctx->b_first_frm && - p_clnt_ctx->b_sched_state && - !p_clnt_ctx->b_flushing && - p_clnt_ctx->n_q_len && - p_clnt_ctx->n_curr_o_tkns >= p_clnt_ctx->n_o_tkn_per_ip_frm) { - return TRUE; + if (clnt_ctx->first_frm && + clnt_ctx->sched_state && + !clnt_ctx->flushing && + clnt_ctx->q_len && + clnt_ctx->curr_o_tkns >= clnt_ctx->o_tkn_per_ip_frm) { + return true; } else { - return FALSE; + return false; } } diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.h b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.h index 5f509e19b0506..e38e9af73be33 100644 --- a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.h +++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler.h @@ -31,108 +31,108 @@ #include "vid_frame_scheduler_utils.h" struct sched_clnt_q_elem { - void *p_frm_data; - u32 b_eof; + void *frm_data; + u32 eof; }; -struct sched_clnt_ctx_type { - enum sched_client_ctgy_type client_ctgy; - struct sched_client_frm_rate_type frm_rate; - u32 n_p_tkn_per_frm; - u32 n_curr_p_tkn_rate; - u32 n_o_tkn_max; - u32 n_o_tkn_per_ip_frm; - u32 n_curr_o_tkns; - u32 n_bkt_size; - u32 n_bkt_quies_cap; - s32 n_bkt_curr_tkns; - s32 n_bkt_curr_tkns_nmlzd; - u32 n_bkt_lst_sup_time; - u32 n_max_queue_len; - struct sched_clnt_q_elem *a_clnt_frm_q; - s32 n_q_head; - s32 n_q_tail; - u32 n_q_len; - u32 b_first_frm; - u32 n_eof_marker; - u32 b_flushing; - u32 b_sched_state; - void *p_client_data; +struct sched_clnt_ctx { + enum sched_client_ctgy client_ctgy; + struct sched_client_frm_rate frm_rate; + u32 tkn_per_frm; + u32 curr_p_tkn_rate; + u32 o_tkn_max; + u32 o_tkn_per_ip_frm; + u32 curr_o_tkns; + u32 bkt_size; + u32 bkt_quies_cap; + s32 bkt_curr_tkns; + s32 bkt_curr_tkns_nmlzd; + u32 bkt_lst_sup_time; + u32 max_queue_len; + struct sched_clnt_q_elem *clnt_frm_q; + s32 q_head; + s32 q_tail; + u32 q_len; + u32 first_frm; + u32 eof_marker; + u32 flushing; + u32 sched_state; + void *client_data; u32 *clnt_cs; }; -struct _sched_clnt_list_node_type { - struct sched_clnt_ctx_type data; - struct _sched_clnt_list_node_type *p_next; +struct _sched_clnt_list_node { + struct sched_clnt_ctx data; + struct _sched_clnt_list_node *next; }; -struct _sched_clnt_list_node_type; +struct _sched_clnt_list_node; -struct sched_ctx_type { - u32 n_perf_lvl; - struct _sched_clnt_list_node_type *p_rt_head; - u32 n_rt_clnts; - struct _sched_clnt_list_node_type *p_n_rt_head; - u32 n_n_rt_clnts; - struct _sched_clnt_list_node_type *p_n_rt_last_sched; - u32 n_total_clnt_bw; +struct sched_ctx { + u32 perf_lvl; + struct _sched_clnt_list_node *rt_head; + u32 rt_clnts; + struct _sched_clnt_list_node *non_rt_head; + u32 non_rt_clnts; + struct _sched_clnt_list_node *non_rt_last_sched; + u32 total_clnt_bw; u32 *sched_cs; }; -SCHED_INLINE u32 SCHED_SUCCEEDED(enum sched_status_type status); -SCHED_INLINE u32 SCHED_FAILED(enum sched_status_type status); +SCHED_INLINE u32 SCHED_SUCCEEDED(enum sched_status status); +SCHED_INLINE u32 SCHED_FAILED(enum sched_status status); SCHED_INLINE void sched_free_clnt_node - (struct _sched_clnt_list_node_type *p_clnt_node); -enum sched_status_type sched_clear_clnt_list - (struct _sched_clnt_list_node_type *p_clnt_lst); -enum sched_status_type sched_process_add_clnt - (struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node, - struct sched_client_init_param_type *p_init_param); -enum sched_status_type sched_process_remove_clnt - (struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node); -enum sched_status_type sched_process_flush_clnt_buff - (struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node, void **pp_frm_data); -SCHED_INLINE enum sched_status_type sched_process_mark_clnt_eof - (struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node); -enum sched_status_type sched_process_update_clnt_o_tkn - (struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node, u32 b_type, u32 n_o_tkn); -enum sched_status_type sched_process_en_q_frm - (struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node, void *p_frm_data); -enum sched_status_type sched_process_re_en_q_frm -(struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node, void *p_frm_data); -enum sched_status_type sched_process_de_q_frm - (struct sched_ctx_type *p_sched_ctx, + (struct _sched_clnt_list_node *clnt_node); +enum sched_status sched_clear_clnt_list + (struct _sched_clnt_list_node *clnt_lst); +enum sched_status sched_process_add_clnt + (struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node, + struct sched_client_init_param *init_param); +enum sched_status sched_process_remove_clnt + (struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node); +enum sched_status sched_process_flush_clnt_buff + (struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node, void **pp_frm_data); +SCHED_INLINE enum sched_status sched_process_mark_clnt_eof + (struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node); +enum sched_status sched_process_update_clnt_o_tkn + (struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node, u32 type, u32 o_tkn); +enum sched_status sched_process_en_q_frm + (struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node, void *frm_data); +enum sched_status sched_process_re_en_q_frm +(struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node, void *frm_data); +enum sched_status sched_process_de_q_frm + (struct sched_ctx *sched_ctx, void **pp_frm_data, void **pp_client_data); -enum sched_status_type sched_process_sched_lvl_get_param - (struct sched_ctx_type *p_sched_ctx, - enum sched_index_type param_index, union sched_value_type *p_param_value); -enum sched_status_type sched_process_sched_lvl_set_param - (struct sched_ctx_type *p_sched_ctx, - enum sched_index_type param_index, union sched_value_type *p_param_value); -enum sched_status_type sched_process_clnt_lvl_get_param - (struct sched_ctx_type *p_sched_ctx, - struct sched_clnt_ctx_type *p_clnt_ctx, - enum sched_index_type param_index, union sched_value_type *p_param_value); -enum sched_status_type sched_process_clnt_lvl_set_param - (struct sched_ctx_type *p_sched_ctx, - struct sched_clnt_ctx_type *p_clnt_ctx, - enum sched_index_type param_index, union sched_value_type *p_param_value); -enum sched_status_type sched_process_suspend_resume_clnt - (struct sched_ctx_type *p_sched_ctx, - struct _sched_clnt_list_node_type *p_clnt_node, u32 b_state); +enum sched_status sched_process_sched_lvl_get_param + (struct sched_ctx *sched_ctx, + enum sched_index param_index, union sched_value_type *param_value); +enum sched_status sched_process_sched_lvl_set_param + (struct sched_ctx *sched_ctx, + enum sched_index param_index, union sched_value_type *param_value); +enum sched_status sched_process_clnt_lvl_get_param + (struct sched_ctx *sched_ctx, + struct sched_clnt_ctx *clnt_ctx, + enum sched_index param_index, union sched_value_type *param_value); +enum sched_status sched_process_clnt_lvl_set_param + (struct sched_ctx *sched_ctx, + struct sched_clnt_ctx *clnt_ctx, + enum sched_index param_index, union sched_value_type *param_value); +enum sched_status sched_process_suspend_resume_clnt + (struct sched_ctx *sched_ctx, + struct _sched_clnt_list_node *clnt_node, u32 state); void sched_remove_node_from_list - (struct _sched_clnt_list_node_type **pp_head, - struct _sched_clnt_list_node_type *p_node); + (struct _sched_clnt_list_node **pp_head, + struct _sched_clnt_list_node *node); SCHED_INLINE u32 sched_consider_clnt_for_sched - (struct sched_clnt_ctx_type *p_clnt_ctx); + (struct sched_clnt_ctx *clnt_ctx); #endif diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.c b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.c index 6a667856ce277..7c55784ebaed8 100644 --- a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.c +++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.c @@ -21,44 +21,44 @@ #include "vid_frame_scheduler_api.h" #include "vid_frame_scheduler.h" -enum sched_status_type sched_create( - struct sched_init_param_type *p_init_param, void **p_handle) +enum sched_status sched_create( + struct sched_init_param *init_param, void **handle) { - struct sched_ctx_type *p_sched_ctx; + struct sched_ctx *sched_ctx; SCHED_MSG_HIGH("sched_create API"); - if (!p_handle || !p_init_param) { + if (!handle || !init_param) { SCHED_MSG_ERR - ("Bad input parameters: p_handle=%p, p_init_param=%p", - p_handle, p_init_param); + ("Bad input parameters: handle=%p, init_param=%p", + handle, init_param); return SCHED_S_EBADPARM; } - if (!p_init_param->n_perf_lvl) { + if (!init_param->perf_lvl) { SCHED_MSG_ERR("Invalid Perf level=%u", - p_init_param->n_perf_lvl); + init_param->perf_lvl); return SCHED_S_EBADPARM; } - p_sched_ctx = - (struct sched_ctx_type *) - SCHED_MALLOC(sizeof(struct sched_ctx_type)); + sched_ctx = + (struct sched_ctx *) + SCHED_MALLOC(sizeof(struct sched_ctx)); - if (!p_sched_ctx) { + if (!sched_ctx) { SCHED_MSG_ERR("Could not allocate sched ctx. Out of memory"); return SCHED_S_ENOMEM; } - SCHED_MEMSET(p_sched_ctx, 0, sizeof(struct sched_ctx_type)); - p_sched_ctx->n_perf_lvl = p_init_param->n_perf_lvl; + SCHED_MEMSET(sched_ctx, 0, sizeof(struct sched_ctx)); + sched_ctx->perf_lvl = init_param->perf_lvl; - if (SCHED_FAILED(SCHED_CRITSEC_CREATE(&p_sched_ctx->sched_cs))) { - SCHED_FREE(p_sched_ctx); + if (SCHED_FAILED(SCHED_CRITSEC_CREATE(&sched_ctx->sched_cs))) { + SCHED_FREE(sched_ctx); return SCHED_S_EFAIL; } - *p_handle = p_sched_ctx; + *handle = sched_ctx; SCHED_MSG_MED("Sched instance created. All went well"); @@ -66,361 +66,361 @@ enum sched_status_type sched_create( } -enum sched_status_type sched_destroy(void *handle) +enum sched_status sched_destroy(void *handle) { - struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; + struct sched_ctx *sched_ctx = (struct sched_ctx *)handle; SCHED_MSG_HIGH("sched_destroy API"); - if (!p_sched_ctx) { + if (!sched_ctx) { SCHED_MSG_ERR("Bad input parameters"); return SCHED_S_EBADPARM; } - (void)SCHED_CRITSEC_ENTER(p_sched_ctx->sched_cs); - (void)sched_clear_clnt_list(p_sched_ctx->p_rt_head); - (void)sched_clear_clnt_list(p_sched_ctx->p_n_rt_head); + (void)SCHED_CRITSEC_ENTER(sched_ctx->sched_cs); + (void)sched_clear_clnt_list(sched_ctx->rt_head); + (void)sched_clear_clnt_list(sched_ctx->rt_head); SCHED_MSG_MED("Sched clnt lists are cleared & released"); - (void)SCHED_CRITSEC_LEAVE(p_sched_ctx->sched_cs); - (void)SCHED_CRITSEC_RELEASE(p_sched_ctx->sched_cs); - SCHED_MEMSET(p_sched_ctx, 0, sizeof(struct sched_ctx_type)); - SCHED_FREE(p_sched_ctx); + (void)SCHED_CRITSEC_LEAVE(sched_ctx->sched_cs); + (void)SCHED_CRITSEC_RELEASE(sched_ctx->sched_cs); + SCHED_MEMSET(sched_ctx, 0, sizeof(struct sched_ctx)); + SCHED_FREE(sched_ctx); SCHED_MSG_MED("Sched instance deleted"); return SCHED_S_OK; } -enum sched_status_type sched_get_param( +enum sched_status sched_get_param( void *handle, - enum sched_index_type param_index, - union sched_value_type *p_param_value) + enum sched_index param_index, + union sched_value_type *param_value) { - struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; - enum sched_status_type status; + struct sched_ctx *sched_ctx = (struct sched_ctx *)handle; + enum sched_status status; SCHED_MSG_HIGH("sched_get_param API"); - if (!p_sched_ctx || !p_param_value) { + if (!sched_ctx || !param_value) { SCHED_MSG_ERR - ("Bad input parameters: p_sched_ctx=%p, p_param_value=%p", - p_sched_ctx, p_param_value); + ("Bad input parameters: sched_ctx=%p, param_value=%p", + sched_ctx, param_value); return SCHED_S_EBADPARM; } - (void)SCHED_CRITSEC_ENTER(p_sched_ctx->sched_cs); + (void)SCHED_CRITSEC_ENTER(sched_ctx->sched_cs); status = - sched_process_sched_lvl_get_param(p_sched_ctx, param_index, - p_param_value); + sched_process_sched_lvl_get_param(sched_ctx, param_index, + param_value); - (void)SCHED_CRITSEC_LEAVE(p_sched_ctx->sched_cs); + (void)SCHED_CRITSEC_LEAVE(sched_ctx->sched_cs); return status; } -enum sched_status_type sched_set_param( +enum sched_status sched_set_param( void *handle, - enum sched_index_type param_index, - union sched_value_type *p_param_value) + enum sched_index param_index, + union sched_value_type *param_value) { - struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; - enum sched_status_type status; + struct sched_ctx *sched_ctx = (struct sched_ctx *)handle; + enum sched_status status; SCHED_MSG_HIGH("sched_set_param API"); - if (!p_sched_ctx || !p_param_value) { + if (!sched_ctx || !param_value) { SCHED_MSG_ERR - ("Bad input parameters: p_sched_ctx=%p, p_param_value=%p", - p_sched_ctx, p_param_value); + ("Bad input parameters: sched_ctx=%p, param_value=%p", + sched_ctx, param_value); return SCHED_S_EBADPARM; } - (void)SCHED_CRITSEC_ENTER(p_sched_ctx->sched_cs); + (void)SCHED_CRITSEC_ENTER(sched_ctx->sched_cs); status = - sched_process_sched_lvl_set_param(p_sched_ctx, param_index, - p_param_value); - (void)SCHED_CRITSEC_LEAVE(p_sched_ctx->sched_cs); + sched_process_sched_lvl_set_param(sched_ctx, param_index, + param_value); + (void)SCHED_CRITSEC_LEAVE(sched_ctx->sched_cs); return status; } -enum sched_status_type sched_add_client( +enum sched_status sched_add_client( void *handle, - struct sched_client_init_param_type *p_init_param, - void **p_client_hdl) + struct sched_client_init_param *init_param, + void **client_hdl) { - struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; - enum sched_status_type status = SCHED_S_OK; - struct _sched_clnt_list_node_type *p_new_clnt; + struct sched_ctx *sched_ctx = (struct sched_ctx *)handle; + enum sched_status status = SCHED_S_OK; + struct _sched_clnt_list_node *new_clnt; SCHED_MSG_HIGH("sched_add_client API"); - if (!p_sched_ctx || !p_init_param || - !p_client_hdl) { + if (!sched_ctx || !init_param || + !client_hdl) { SCHED_MSG_ERR("Bad input parameters"); return SCHED_S_EBADPARM; } - p_new_clnt = (struct _sched_clnt_list_node_type *) - SCHED_MALLOC(sizeof(struct _sched_clnt_list_node_type)); - if (!p_new_clnt) { + new_clnt = (struct _sched_clnt_list_node *) + SCHED_MALLOC(sizeof(struct _sched_clnt_list_node)); + if (!new_clnt) { SCHED_MSG_ERR("Could not allocate client ctx. Out of memory"); return SCHED_S_ENOMEM; } - (void)SCHED_CRITSEC_ENTER(p_sched_ctx->sched_cs); - status = sched_process_add_clnt(p_sched_ctx, p_new_clnt, p_init_param); + (void)SCHED_CRITSEC_ENTER(sched_ctx->sched_cs); + status = sched_process_add_clnt(sched_ctx, new_clnt, init_param); if (SCHED_FAILED(status)) { SCHED_MSG_ERR("Add_client failed with err=%d", status); - sched_free_clnt_node(p_new_clnt); - p_new_clnt = NULL; + sched_free_clnt_node(new_clnt); + new_clnt = NULL; } - (void)SCHED_CRITSEC_LEAVE(p_sched_ctx->sched_cs); - *p_client_hdl = p_new_clnt; + (void)SCHED_CRITSEC_LEAVE(sched_ctx->sched_cs); + *client_hdl = new_clnt; SCHED_MSG_MED("Sched client instance created. All went well"); return status; } -enum sched_status_type sched_remove_client(void *handle, void *client_hdl) +enum sched_status sched_remove_client(void *handle, void *client_hdl) { - struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; - struct _sched_clnt_list_node_type *p_clnt_node = - (struct _sched_clnt_list_node_type *)client_hdl; - enum sched_status_type status = SCHED_S_OK; + struct sched_ctx *sched_ctx = (struct sched_ctx *)handle; + struct _sched_clnt_list_node *clnt_node = + (struct _sched_clnt_list_node *)client_hdl; + enum sched_status status = SCHED_S_OK; SCHED_MSG_HIGH("sched_remove_client API"); - if (!p_sched_ctx || !p_clnt_node) { + if (!sched_ctx || !clnt_node) { SCHED_MSG_ERR - ("Bad input parameters: p_sched_ctx=%p, p_clnt_node=%p", - p_sched_ctx, p_clnt_node); + ("Bad input parameters: sched_ctx=%p, clnt_node=%p", + sched_ctx, clnt_node); return SCHED_S_EBADPARM; } - (void)SCHED_CRITSEC_ENTER(p_sched_ctx->sched_cs); - status = sched_process_remove_clnt(p_sched_ctx, p_clnt_node); - (void)SCHED_CRITSEC_LEAVE(p_sched_ctx->sched_cs); + (void)SCHED_CRITSEC_ENTER(sched_ctx->sched_cs); + status = sched_process_remove_clnt(sched_ctx, clnt_node); + (void)SCHED_CRITSEC_LEAVE(sched_ctx->sched_cs); return status; } -enum sched_status_type sched_flush_client_buffer( +enum sched_status sched_flush_client_buffer( void *handle, void *client_hdl, void **pp_frm_data) { - struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; - struct _sched_clnt_list_node_type *p_clnt_node = - (struct _sched_clnt_list_node_type *)client_hdl; - enum sched_status_type status = SCHED_S_OK; + struct sched_ctx *sched_ctx = (struct sched_ctx *)handle; + struct _sched_clnt_list_node *clnt_node = + (struct _sched_clnt_list_node *)client_hdl; + enum sched_status status = SCHED_S_OK; SCHED_MSG_HIGH("sched_flush_client_buffer API"); - if (!p_sched_ctx || !p_clnt_node || !pp_frm_data) { + if (!sched_ctx || !clnt_node || !pp_frm_data) { SCHED_MSG_ERR - ("Bad input parameters: p_sched_ctx=%p, p_clnt_node=%p", - p_sched_ctx, p_clnt_node); + ("Bad input parameters: sched_ctx=%p, clnt_node=%p", + sched_ctx, clnt_node); return SCHED_S_EBADPARM; } - (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); + (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs); status = - sched_process_flush_clnt_buff(p_sched_ctx, p_clnt_node, + sched_process_flush_clnt_buff(sched_ctx, clnt_node, pp_frm_data); - (void)SCHED_CRITSEC_LEAVE(p_clnt_node->data.clnt_cs); + (void)SCHED_CRITSEC_LEAVE(clnt_node->data.clnt_cs); return status; } -enum sched_status_type sched_mark_client_eof(void *handle, void *client_hdl) +enum sched_status sched_mark_client_eof(void *handle, void *client_hdl) { - struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; - struct _sched_clnt_list_node_type *p_clnt_node = - (struct _sched_clnt_list_node_type *)client_hdl; - enum sched_status_type status = SCHED_S_OK; + struct sched_ctx *sched_ctx = (struct sched_ctx *)handle; + struct _sched_clnt_list_node *clnt_node = + (struct _sched_clnt_list_node *)client_hdl; + enum sched_status status = SCHED_S_OK; SCHED_MSG_HIGH("sched_mark_client_eof API"); - if (!p_sched_ctx || !p_clnt_node) { + if (!sched_ctx || !clnt_node) { SCHED_MSG_ERR - ("Bad input parameters: p_sched_ctx=%p, p_clnt_node=%p", - p_sched_ctx, p_clnt_node); + ("Bad input parameters: sched_ctx=%p, clnt_node=%p", + sched_ctx, clnt_node); return SCHED_S_EBADPARM; } - (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); - status = sched_process_mark_clnt_eof(p_sched_ctx, p_clnt_node); - (void)SCHED_CRITSEC_LEAVE(p_clnt_node->data.clnt_cs); + (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs); + status = sched_process_mark_clnt_eof(sched_ctx, clnt_node); + (void)SCHED_CRITSEC_LEAVE(clnt_node->data.clnt_cs); return status; } -enum sched_status_type sched_update_client_o_tkn( - void *handle, void *client_hdl, u32 b_type, u32 n_o_tkn) +enum sched_status sched_update_client_o_tkn( + void *handle, void *client_hdl, u32 type, u32 o_tkn) { - struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; - struct _sched_clnt_list_node_type *p_clnt_node = - (struct _sched_clnt_list_node_type *)client_hdl; - enum sched_status_type status = SCHED_S_OK; + struct sched_ctx *sched_ctx = (struct sched_ctx *)handle; + struct _sched_clnt_list_node *clnt_node = + (struct _sched_clnt_list_node *)client_hdl; + enum sched_status status = SCHED_S_OK; SCHED_MSG_HIGH("sched_restore_client_o_tkn API"); - if (!p_sched_ctx || !p_clnt_node) { + if (!sched_ctx || !clnt_node) { SCHED_MSG_ERR - ("Bad input parameters: p_sched_ctx=%p, p_clnt_node=%p", - p_sched_ctx, p_clnt_node); + ("Bad input parameters: sched_ctx=%p, clnt_node=%p", + sched_ctx, clnt_node); return SCHED_S_EBADPARM; } - (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); + (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs); status = - sched_process_update_clnt_o_tkn(p_sched_ctx, p_clnt_node, b_type, - n_o_tkn); - (void)SCHED_CRITSEC_LEAVE(p_clnt_node->data.clnt_cs); + sched_process_update_clnt_o_tkn(sched_ctx, clnt_node, type, + o_tkn); + (void)SCHED_CRITSEC_LEAVE(clnt_node->data.clnt_cs); return status; } -enum sched_status_type sched_queue_frame( - void *handle, void *client_hdl, void *p_frm_data) +enum sched_status sched_queue_frame( + void *handle, void *client_hdl, void *frm_data) { - struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; - struct _sched_clnt_list_node_type *p_clnt_node = - (struct _sched_clnt_list_node_type *)client_hdl; - enum sched_status_type status = SCHED_S_OK; + struct sched_ctx *sched_ctx = (struct sched_ctx *)handle; + struct _sched_clnt_list_node *clnt_node = + (struct _sched_clnt_list_node *)client_hdl; + enum sched_status status = SCHED_S_OK; SCHED_MSG_HIGH("sched_queue_frame API"); - if (!p_sched_ctx || !p_clnt_node) { + if (!sched_ctx || !clnt_node) { SCHED_MSG_ERR - ("Bad input parameters: p_sched_ctx=%p, p_clnt_node=%p", - p_sched_ctx, p_clnt_node); + ("Bad input parameters: sched_ctx=%p, clnt_node=%p", + sched_ctx, clnt_node); return SCHED_S_EBADPARM; } - (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); - status = sched_process_en_q_frm(p_sched_ctx, p_clnt_node, p_frm_data); - (void)SCHED_CRITSEC_LEAVE(p_clnt_node->data.clnt_cs); + (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs); + status = sched_process_en_q_frm(sched_ctx, clnt_node, frm_data); + (void)SCHED_CRITSEC_LEAVE(clnt_node->data.clnt_cs); return status; } -enum sched_status_type sched_re_queue_frame( -void *handle, void *client_hdl, void *p_frm_data) +enum sched_status sched_re_queue_frame( +void *handle, void *client_hdl, void *frm_data) { - struct sched_ctx_type* p_sched_ctx = (struct sched_ctx_type *)handle; - struct _sched_clnt_list_node_type *p_clnt_node = - (struct _sched_clnt_list_node_type *)client_hdl; - enum sched_status_type status = SCHED_S_OK; + struct sched_ctx* sched_ctx = (struct sched_ctx *)handle; + struct _sched_clnt_list_node *clnt_node = + (struct _sched_clnt_list_node *)client_hdl; + enum sched_status status = SCHED_S_OK; SCHED_MSG_HIGH("\n sched_re_queue_frame API"); - if (!p_sched_ctx || !p_clnt_node) { + if (!sched_ctx || !clnt_node) { SCHED_MSG_ERR("Bad input parameters:" - "p_sched_ctx=%p, p_clnt_node=%p", - p_sched_ctx, p_clnt_node); + "sched_ctx=%p, clnt_node=%p", + sched_ctx, clnt_node); return SCHED_S_EBADPARM; } - (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); - status = sched_process_re_en_q_frm(p_sched_ctx, p_clnt_node, - p_frm_data); - (void)SCHED_CRITSEC_LEAVE(p_clnt_node->data.clnt_cs); + (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs); + status = sched_process_re_en_q_frm(sched_ctx, clnt_node, + frm_data); + (void)SCHED_CRITSEC_LEAVE(clnt_node->data.clnt_cs); return status; } -enum sched_status_type sched_de_queue_frame( +enum sched_status sched_de_queue_frame( void *handle, void **pp_frm_data, void **pp_client_data) { - struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; - enum sched_status_type status = SCHED_S_OK; + struct sched_ctx *sched_ctx = (struct sched_ctx *)handle; + enum sched_status status = SCHED_S_OK; SCHED_MSG_HIGH("sched_de_queue_frame API"); - if (!p_sched_ctx || !pp_frm_data + if (!sched_ctx || !pp_frm_data || !pp_client_data) { - SCHED_MSG_ERR("Bad input parameters: p_sched_ctx=%p, " + SCHED_MSG_ERR("Bad input parameters: sched_ctx=%p, " "pp_frm_data=%p, pp_client_data=%p", - p_sched_ctx, pp_frm_data, + sched_ctx, pp_frm_data, pp_client_data); return SCHED_S_EBADPARM; } - (void)SCHED_CRITSEC_ENTER(p_sched_ctx->sched_cs); + (void)SCHED_CRITSEC_ENTER(sched_ctx->sched_cs); status = - sched_process_de_q_frm(p_sched_ctx, pp_frm_data, pp_client_data); - (void)SCHED_CRITSEC_LEAVE(p_sched_ctx->sched_cs); + sched_process_de_q_frm(sched_ctx, pp_frm_data, pp_client_data); + (void)SCHED_CRITSEC_LEAVE(sched_ctx->sched_cs); return status; } -enum sched_status_type sched_get_client_param( +enum sched_status sched_get_client_param( void *handle, void *client_hdl, - enum sched_index_type param_index, - union sched_value_type *p_param_value) + enum sched_index param_index, + union sched_value_type *param_value) { - struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; - struct _sched_clnt_list_node_type *p_clnt_node = - (struct _sched_clnt_list_node_type *)client_hdl; - enum sched_status_type status; + struct sched_ctx *sched_ctx = (struct sched_ctx *)handle; + struct _sched_clnt_list_node *clnt_node = + (struct _sched_clnt_list_node *)client_hdl; + enum sched_status status; SCHED_MSG_HIGH("sched_get_client_param API"); - if (!p_sched_ctx || !p_clnt_node || - !p_param_value) { - SCHED_MSG_ERR("Bad input parameters: p_sched_ctx=%p, " - "p_clnt_node=%p, p_param_value=%p", - p_sched_ctx, p_clnt_node, - p_param_value); + if (!sched_ctx || !clnt_node || + !param_value) { + SCHED_MSG_ERR("Bad input parameters: sched_ctx=%p, " + "clnt_node=%p, param_value=%p", + sched_ctx, clnt_node, + param_value); return SCHED_S_EBADPARM; } - (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); - status = sched_process_clnt_lvl_get_param(p_sched_ctx, - &p_clnt_node->data, - param_index, p_param_value); - (void)SCHED_CRITSEC_LEAVE(p_clnt_node->data.clnt_cs); + (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs); + status = sched_process_clnt_lvl_get_param(sched_ctx, + &clnt_node->data, + param_index, param_value); + (void)SCHED_CRITSEC_LEAVE(clnt_node->data.clnt_cs); return status; } -enum sched_status_type sched_set_client_param( +enum sched_status sched_set_client_param( void *handle, void *client_hdl, - enum sched_index_type param_index, - union sched_value_type *p_param_value) + enum sched_index param_index, + union sched_value_type *param_value) { - struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; - struct _sched_clnt_list_node_type *p_clnt_node = - (struct _sched_clnt_list_node_type *)client_hdl; - enum sched_status_type status; + struct sched_ctx *sched_ctx = (struct sched_ctx *)handle; + struct _sched_clnt_list_node *clnt_node = + (struct _sched_clnt_list_node *)client_hdl; + enum sched_status status; SCHED_MSG_HIGH("sched_set_client_param API"); - if (!p_sched_ctx || !p_clnt_node || - !p_param_value) { + if (!sched_ctx || !clnt_node || + !param_value) { SCHED_MSG_ERR("Bad input parameters: " - "p_sched_ctx=%p, p_clnt_node=%p, " - "p_param_value=%p", p_sched_ctx, p_clnt_node, - p_param_value); + "sched_ctx=%p, clnt_node=%p, " + "param_value=%p", sched_ctx, clnt_node, + param_value); return SCHED_S_EBADPARM; } - (void)SCHED_CRITSEC_ENTER(p_sched_ctx->sched_cs); - (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); + (void)SCHED_CRITSEC_ENTER(sched_ctx->sched_cs); + (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs); - status = sched_process_clnt_lvl_set_param(p_sched_ctx, - &p_clnt_node->data, param_index, p_param_value); + status = sched_process_clnt_lvl_set_param(sched_ctx, + &clnt_node->data, param_index, param_value); - (void)SCHED_CRITSEC_LEAVE(p_clnt_node->data.clnt_cs); - (void)SCHED_CRITSEC_LEAVE(p_sched_ctx->sched_cs); + (void)SCHED_CRITSEC_LEAVE(clnt_node->data.clnt_cs); + (void)SCHED_CRITSEC_LEAVE(sched_ctx->sched_cs); return status; } -enum sched_status_type sched_suspend_resume_client( - void *handle, void *client_hdl, u32 b_state) +enum sched_status sched_suspend_resume_client( + void *handle, void *client_hdl, u32 state) { - struct sched_ctx_type *p_sched_ctx = (struct sched_ctx_type *)handle; - struct _sched_clnt_list_node_type *p_clnt_node = - (struct _sched_clnt_list_node_type *)client_hdl; - enum sched_status_type status; + struct sched_ctx *sched_ctx = (struct sched_ctx *)handle; + struct _sched_clnt_list_node *clnt_node = + (struct _sched_clnt_list_node *)client_hdl; + enum sched_status status; SCHED_MSG_HIGH("sched_client_suspend_resume API"); - if (!p_sched_ctx || !p_clnt_node) { + if (!sched_ctx || !clnt_node) { SCHED_MSG_ERR - ("Bad input parameters: p_sched_ctx=%p, p_clnt_node=%p", - p_sched_ctx, p_clnt_node); + ("Bad input parameters: sched_ctx=%p, clnt_node=%p", + sched_ctx, clnt_node); return SCHED_S_EBADPARM; } - (void)SCHED_CRITSEC_ENTER(p_clnt_node->data.clnt_cs); + (void)SCHED_CRITSEC_ENTER(clnt_node->data.clnt_cs); status = - sched_process_suspend_resume_clnt(p_sched_ctx, p_clnt_node, - b_state); - (void)SCHED_CRITSEC_LEAVE(p_clnt_node->data.clnt_cs); + sched_process_suspend_resume_clnt(sched_ctx, clnt_node, + state); + (void)SCHED_CRITSEC_LEAVE(clnt_node->data.clnt_cs); return status; } diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.h b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.h index 34bcd7a7a0ec9..29e0692b72e75 100644 --- a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.h +++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_api.h @@ -29,7 +29,7 @@ #ifndef _SCHEDULER_API_H_ #define _SCHEDULER_API_H_ -enum sched_status_type { +enum sched_status { SCHED_S_OK = 0x0, SCHED_S_NOPTKN, SCHED_S_NOOTKN, @@ -47,7 +47,7 @@ enum sched_status_type { SCHED_S_MAX = 0x7fffffff }; -enum sched_index_type { +enum sched_index { SCHED_I_START_UNUSED = 0x0, SCHED_I_PERFLEVEL, SCHED_I_CLNT_START_UNUSED = 0x63, @@ -61,90 +61,90 @@ enum sched_index_type { SCHED_I_MAX = 0x7fffffff }; -struct sched_client_frm_rate_type { - u32 n_numer; - u32 n_denom; +struct sched_client_frm_rate { + u32 numer; + u32 denom; }; union sched_value_type { u32 un_value; - struct sched_client_frm_rate_type frm_rate; + struct sched_client_frm_rate frm_rate; }; -struct sched_init_param_type { - u32 n_perf_lvl; +struct sched_init_param { + u32 perf_lvl; }; -enum sched_client_ctgy_type { +enum sched_client_ctgy { SCHED_CLNT_RT_BUFF = 0, SCHED_CLNT_RT_NOBUFF, SCHED_CLNT_NONRT, SCHED_CLNT_MAX = 0x7fffffff }; -struct sched_client_init_param_type { - enum sched_client_ctgy_type client_ctgy; - u32 n_max_queue_len; - struct sched_client_frm_rate_type frm_rate; - u32 n_p_tkn_per_frm; - u32 n_alloc_p_tkn_rate; - u32 n_o_tkn_max; - u32 n_o_tkn_per_ip_frm; - u32 n_o_tkn_init; +struct sched_client_init_param { + enum sched_client_ctgy client_ctgy; + u32 max_queue_len; + struct sched_client_frm_rate frm_rate; + u32 tkn_per_frm; + u32 alloc_p_tkn_rate; + u32 o_tkn_max; + u32 o_tkn_per_ip_frm; + u32 o_tkn_init; - void *p_client_data; + void *client_data; }; -enum sched_status_type sched_create - (struct sched_init_param_type *init_param, void **p_handle); +enum sched_status sched_create + (struct sched_init_param *init_param, void **handle); -enum sched_status_type sched_destroy(void *handle); +enum sched_status sched_destroy(void *handle); -enum sched_status_type sched_get_param +enum sched_status sched_get_param (void *handle, - enum sched_index_type param_index, union sched_value_type *p_param_value); + enum sched_index param_index, union sched_value_type *param_value); -enum sched_status_type sched_set_param +enum sched_status sched_set_param (void *handle, - enum sched_index_type param_index, union sched_value_type *p_param_value); + enum sched_index param_index, union sched_value_type *param_value); -enum sched_status_type sched_add_client +enum sched_status sched_add_client (void *handle, - struct sched_client_init_param_type *init_param, void **p_client_hdl); + struct sched_client_init_param *init_param, void **client_hdl); -enum sched_status_type sched_remove_client(void *handle, void *client_hdl); +enum sched_status sched_remove_client(void *handle, void *client_hdl); -enum sched_status_type sched_flush_client_buffer +enum sched_status sched_flush_client_buffer (void *handle, void *client_hdl, void **pp_frm_data); -enum sched_status_type sched_mark_client_eof(void *handle, void *client_hdl); +enum sched_status sched_mark_client_eof(void *handle, void *client_hdl); -enum sched_status_type sched_update_client_o_tkn - (void *handle, void *client_hdl, u32 b_type, u32 n_o_tkn); +enum sched_status sched_update_client_o_tkn + (void *handle, void *client_hdl, u32 type, u32 o_tkn); -enum sched_status_type sched_queue_frame - (void *handle, void *client_hdl, void *p_frm_data); -enum sched_status_type sched_re_queue_frame -(void *handle, void *client_hdl, void *p_frm_data); +enum sched_status sched_queue_frame + (void *handle, void *client_hdl, void *frm_data); +enum sched_status sched_re_queue_frame +(void *handle, void *client_hdl, void *frm_data); -enum sched_status_type sched_de_queue_frame +enum sched_status sched_de_queue_frame (void *handle, void **pp_frm_data, void **pp_client_data); -enum sched_status_type sched_get_client_param +enum sched_status sched_get_client_param (void *handle, void *client_hdl, - enum sched_index_type param_index, union sched_value_type *p_param_value); + enum sched_index param_index, union sched_value_type *param_value); -enum sched_status_type sched_set_client_param +enum sched_status sched_set_client_param (void *handle, void *client_hdl, - enum sched_index_type param_index, union sched_value_type *p_param_value); + enum sched_index param_index, union sched_value_type *param_value); -enum sched_status_type sched_suspend_resume_client - (void *handle, void *client_hdl, u32 b_state); +enum sched_status sched_suspend_resume_client + (void *handle, void *client_hdl, u32 state); #endif diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.c b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.c index 22e8d3171e462..f6a7b83b5b56a 100644 --- a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.c +++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.c @@ -33,17 +33,17 @@ SCHED_INLINE void SCHED_ASSERT(int val) /** * SCHED_MIN () - This function will find minimum of two values - * @n_x: value 1 - * @n_y: value 2 + * @x: value 1 + * @y: value 2 * DEPENDENCIES: None * Returns none */ -SCHED_INLINE int SCHED_MIN(int n_x, int n_y) +SCHED_INLINE int SCHED_MIN(int x, int y) { - if (n_x < n_y) - return n_x; + if (x < y) + return x; else - return n_y; + return y; } /* end of SCHED_MIN */ @@ -61,14 +61,14 @@ SCHED_INLINE void *SCHED_MALLOC(int size) /** * SCHED_FREE () - This function is a wrapper to underlying memory free - * @p_ptr: memory to be freed + * @ptr: memory to be freed * function * DEPENDENCIES: None * Returns none */ -SCHED_INLINE void SCHED_FREE(void *p_ptr) +SCHED_INLINE void SCHED_FREE(void *ptr) { - kfree(p_ptr); + kfree(ptr); } /* end of SCHED_FREE */ /** @@ -92,7 +92,7 @@ SCHED_INLINE void *SCHED_MEMSET(void *ptr, int val, int size) * DEPENDENCIES: None * Returns SCHED_S_OK on success */ -SCHED_INLINE enum sched_status_type SCHED_GET_CURRENT_TIME(u32 *pn_time) +SCHED_INLINE enum sched_status SCHED_GET_CURRENT_TIME(u32 *pn_time) { struct timeval tv; do_gettimeofday(&tv); @@ -103,12 +103,12 @@ SCHED_INLINE enum sched_status_type SCHED_GET_CURRENT_TIME(u32 *pn_time) /** * SCHED_CRITSEC_CREATE () - This function is a wrapper to creating a critical - * @p_cs: ptr to a critical section type + * @cs: ptr to a critical section type * section * DEPENDENCIES: None * Returns SCHED_S_OK on success */ -SCHED_INLINE enum sched_status_type SCHED_CRITSEC_CREATE(u32 **p_cs) +SCHED_INLINE enum sched_status SCHED_CRITSEC_CREATE(u32 **cs) { return SCHED_S_OK; @@ -121,7 +121,7 @@ SCHED_INLINE enum sched_status_type SCHED_CRITSEC_CREATE(u32 **p_cs) * DEPENDENCIES: None * Returns SCHED_S_OK on success */ -SCHED_INLINE enum sched_status_type SCHED_CRITSEC_RELEASE(u32 *cs) +SCHED_INLINE enum sched_status SCHED_CRITSEC_RELEASE(u32 *cs) { return SCHED_S_OK; @@ -134,7 +134,7 @@ SCHED_INLINE enum sched_status_type SCHED_CRITSEC_RELEASE(u32 *cs) * DEPENDENCIES: None * Returns SCHED_S_OK on success */ -SCHED_INLINE enum sched_status_type SCHED_CRITSEC_ENTER(u32 *cs) +SCHED_INLINE enum sched_status SCHED_CRITSEC_ENTER(u32 *cs) { return SCHED_S_OK; @@ -147,7 +147,7 @@ SCHED_INLINE enum sched_status_type SCHED_CRITSEC_ENTER(u32 *cs) * DEPENDENCIES: None * Returns SCHED_S_OK on success */ -SCHED_INLINE enum sched_status_type SCHED_CRITSEC_LEAVE(u32 *cs) +SCHED_INLINE enum sched_status SCHED_CRITSEC_LEAVE(u32 *cs) { return SCHED_S_OK; diff --git a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.h b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.h index b013d27d7fdbb..f5e1d7dede4d8 100644 --- a/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.h +++ b/drivers/misc/video_core/720p/scheduler/vid_frame_scheduler_utils.h @@ -31,6 +31,7 @@ #include "vid_frame_scheduler_api.h" +//TODO lots of low hanging fruit in here #define SCHED_INLINE #if DEBUG @@ -59,13 +60,13 @@ SCHED_INLINE void SCHED_ASSERT(int val); SCHED_INLINE int SCHED_MIN(int x, int y); -SCHED_INLINE enum sched_status_type SCHED_CRITSEC_CREATE(u32 **p_cs); +SCHED_INLINE enum sched_status SCHED_CRITSEC_CREATE(u32 **cs); -SCHED_INLINE enum sched_status_type SCHED_CRITSEC_RELEASE(u32 *cs); +SCHED_INLINE enum sched_status SCHED_CRITSEC_RELEASE(u32 *cs); -SCHED_INLINE enum sched_status_type SCHED_CRITSEC_ENTER(u32 *cs); +SCHED_INLINE enum sched_status SCHED_CRITSEC_ENTER(u32 *cs); -SCHED_INLINE enum sched_status_type SCHED_CRITSEC_LEAVE(u32 *cs); +SCHED_INLINE enum sched_status SCHED_CRITSEC_LEAVE(u32 *cs); SCHED_INLINE void *SCHED_MALLOC(int size); @@ -73,6 +74,6 @@ SCHED_INLINE void SCHED_FREE(void *ptr); SCHED_INLINE void *SCHED_MEMSET(void *ptr, int val, int size); -SCHED_INLINE enum sched_status_type SCHED_GET_CURRENT_TIME(u32 *pn_time); +SCHED_INLINE enum sched_status SCHED_GET_CURRENT_TIME(u32 *pn_time); #endif diff --git a/drivers/misc/video_core/720p/vcd/vcd.h b/drivers/misc/video_core/720p/vcd/vcd.h index d9087eb765990..1367e7d4a73f3 100644 --- a/drivers/misc/video_core/720p/vcd/vcd.h +++ b/drivers/misc/video_core/720p/vcd/vcd.h @@ -38,321 +38,283 @@ #include "vcd_core.h" #include "vcd_device_sm.h" -void vcd_reset_device_channels(struct vcd_dev_ctxt_type *p_dev_ctxt); +void vcd_reset_device_channels(struct vcd_dev_ctxt *dev_ctxt); -u32 vcd_get_command_channel - (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type **pp_transc); +u32 vcd_get_command_channel(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc **pp_transc); -u32 vcd_get_command_channel_in_loop - (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type **pp_transc); +u32 vcd_get_command_channel_in_loop(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc **pp_transc); -void vcd_mark_command_channel - (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type *p_transc); +void vcd_mark_command_channel(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc *transc); -void vcd_release_command_channel - (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type *p_transc); +void vcd_release_command_channel(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc *transc); -void vcd_release_multiple_command_channels(struct vcd_dev_ctxt_type *p_dev_ctxt, - u32 n_channels); +void vcd_release_multiple_command_channels(struct vcd_dev_ctxt *dev_ctxt, + u32 channels); -void vcd_release_interim_command_channels(struct vcd_dev_ctxt_type *p_dev_ctxt); +void vcd_release_interim_command_channels(struct vcd_dev_ctxt *dev_ctxt); -u32 vcd_get_frame_channel - (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type **pp_transc); +u32 vcd_get_frame_channel(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc **pp_transc); -u32 vcd_get_frame_channel_in_loop - (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type **pp_transc); +u32 vcd_get_frame_channel_in_loop(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc **pp_transc); -void vcd_mark_frame_channel(struct vcd_dev_ctxt_type *p_dev_ctxt); +void vcd_mark_frame_channel(struct vcd_dev_ctxt *dev_ctxt); -void vcd_release_frame_channel - (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type *p_transc); +void vcd_release_frame_channel(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc *transc); -void vcd_release_multiple_frame_channels(struct vcd_dev_ctxt_type *p_dev_ctxt, - u32 n_channels); +void vcd_release_multiple_frame_channels(struct vcd_dev_ctxt *dev_ctxt, + u32 channels); -void vcd_release_interim_frame_channels(struct vcd_dev_ctxt_type *p_dev_ctxt); -u32 vcd_core_is_busy(struct vcd_dev_ctxt_type *p_dev_ctxt); +void vcd_release_interim_frame_channels(struct vcd_dev_ctxt *dev_ctxt); +u32 vcd_core_is_busy(struct vcd_dev_ctxt *dev_ctxt); -void vcd_device_timer_start(struct vcd_dev_ctxt_type *p_dev_ctxt); -void vcd_device_timer_stop(struct vcd_dev_ctxt_type *p_dev_ctxt); +void vcd_device_timer_start(struct vcd_dev_ctxt *dev_ctxt); +void vcd_device_timer_stop(struct vcd_dev_ctxt *dev_ctxt); +u32 vcd_init_device_context(struct vcd_drv_ctxt *drv_ctxt, u32 ev_code); -u32 vcd_init_device_context - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, u32 n_ev_code); +u32 vcd_deinit_device_context(struct vcd_drv_ctxt *drv_ctxt, u32 ev_code); -u32 vcd_deinit_device_context - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, u32 n_ev_code); +u32 vcd_init_client_context(struct vcd_clnt_ctxt *cctxt); -u32 vcd_init_client_context(struct vcd_clnt_ctxt_type_t *p_cctxt); +void vcd_destroy_client_context(struct vcd_clnt_ctxt *cctxt); -void vcd_destroy_client_context(struct vcd_clnt_ctxt_type_t *p_cctxt); +u32 vcd_check_for_client_context(struct vcd_dev_ctxt *dev_ctxt, s32 driver_id); -u32 vcd_check_for_client_context - (struct vcd_dev_ctxt_type *p_dev_ctxt, s32 driver_id); +u32 vcd_validate_driver_handle(struct vcd_dev_ctxt *dev_ctxt, + s32 driver_handle); -u32 vcd_validate_driver_handle - (struct vcd_dev_ctxt_type *p_dev_ctxt, s32 driver_handle); +void vcd_handle_for_last_clnt_close(struct vcd_dev_ctxt *dev_ctxt, + u32 send_deinit); -void vcd_handle_for_last_clnt_close - (struct vcd_dev_ctxt_type *p_dev_ctxt, u32 b_send_deinit); +u32 vcd_common_allocate_set_buffer(struct vcd_clnt_ctxt *cctxt, + enum vcd_buffer_type buffer, size_t sz, + struct vcd_buffer_pool **pp_buf_pool); -u32 vcd_common_allocate_set_buffer - (struct vcd_clnt_ctxt_type_t *p_cctxt, - enum vcd_buffer_type e_buffer, - u32 n_buf_size, struct vcd_buffer_pool_type **pp_buf_pool); +u32 vcd_set_buffer_internal(struct vcd_clnt_ctxt *cctxt, + struct vcd_buffer_pool *buf_pool, void *buf, size_t sz); -u32 vcd_set_buffer_internal - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_buffer_pool_type *p_buf_pool, u8 *p_buffer, u32 n_buf_size); +u32 vcd_allocate_buffer_internal(struct vcd_clnt_ctxt *cctxt, + struct vcd_buffer_pool *buf_pool, size_t buf_size, void **virt_addr, + phys_addr_t *phys_addr); -u32 vcd_allocate_buffer_internal - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_buffer_pool_type *p_buf_pool, - u32 n_buf_size, u8 **pp_vir_buf_addr, u8 **pp_phy_buf_addr); +u32 vcd_free_one_buffer_internal(struct vcd_clnt_ctxt *cctxt, + enum vcd_buffer_type vcd_buffer_type, u8 *buffer); -u32 vcd_free_one_buffer_internal - (struct vcd_clnt_ctxt_type_t *p_cctxt, - enum vcd_buffer_type e_buffer, u8 *p_buffer); +u32 vcd_free_buffers_internal(struct vcd_clnt_ctxt *cctxt, + struct vcd_buffer_pool *buf_pool); -u32 vcd_free_buffers_internal - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_buffer_pool_type *p_buf_pool); +u32 vcd_alloc_buffer_pool_entries(struct vcd_buffer_pool *buf_pool, + struct vcd_buffer_requirement *buf_req); -u32 vcd_alloc_buffer_pool_entries - (struct vcd_buffer_pool_type *p_buf_pool, - struct vcd_buffer_requirement_type *p_buf_req); +void vcd_free_buffer_pool_entries(struct vcd_buffer_pool *buf_pool); -void vcd_free_buffer_pool_entries(struct vcd_buffer_pool_type *p_buf_pool); +void vcd_flush_in_use_buffer_pool_entries(struct vcd_clnt_ctxt *cctxt, + struct vcd_buffer_pool *buf_pool, u32 event); -void vcd_flush_in_use_buffer_pool_entries(struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_buffer_pool_type *p_buf_pool, u32 event); +void vcd_reset_buffer_pool_for_reuse(struct vcd_buffer_pool *buf_pool); -void vcd_reset_buffer_pool_for_reuse(struct vcd_buffer_pool_type *p_buf_pool); +struct vcd_buffer_entry *vcd_get_free_buffer_pool_entry( + struct vcd_buffer_pool *pool); -struct vcd_buffer_entry_type *vcd_get_free_buffer_pool_entry - (struct vcd_buffer_pool_type *p_pool); +struct vcd_buffer_entry *vcd_find_buffer_pool_entry(struct vcd_buffer_pool + *pool, void *virt_addr); -struct vcd_buffer_entry_type *vcd_find_buffer_pool_entry - (struct vcd_buffer_pool_type *p_pool, u8 *p_v_addr); +struct vcd_buffer_entry *vcd_buffer_pool_entry_de_q( + struct vcd_buffer_pool *pool); -struct vcd_buffer_entry_type *vcd_buffer_pool_entry_de_q - (struct vcd_buffer_pool_type *p_pool); +u32 vcd_buffer_pool_entry_en_q(struct vcd_buffer_pool *pool, + struct vcd_buffer_entry *entry); -u32 vcd_buffer_pool_entry_en_q - (struct vcd_buffer_pool_type *p_pool, - struct vcd_buffer_entry_type *p_entry); +u32 vcd_client_cmd_en_q(struct vcd_clnt_ctxt *cctxt, + enum vcd_command_type command); -u32 vcd_client_cmd_en_q - (struct vcd_clnt_ctxt_type_t *p_cctxt, enum vcd_command_type e_command); +void vcd_client_cmd_flush_and_en_q(struct vcd_clnt_ctxt *cctxt, + enum vcd_command_type command); -void vcd_client_cmd_flush_and_en_q - (struct vcd_clnt_ctxt_type_t *p_cctxt, enum vcd_command_type e_command); +u32 vcd_client_cmd_de_q(struct vcd_clnt_ctxt *cctxt, + enum vcd_command_type *command); -u32 vcd_client_cmd_de_q - (struct vcd_clnt_ctxt_type_t *p_cctxt, enum vcd_command_type *p_command); +u32 vcd_handle_recvd_eos(struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *input_frame, u32 * pb_eos_handled); -u32 vcd_handle_recvd_eos - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_input_frame, u32 * pb_eos_handled); +u32 vcd_handle_first_decode_frame(struct vcd_clnt_ctxt *cctxt); -u32 vcd_handle_first_decode_frame(struct vcd_clnt_ctxt_type_t *p_cctxt); +u32 vcd_add_client_to_sched(struct vcd_clnt_ctxt *cctxt); -u32 vcd_add_client_to_sched(struct vcd_clnt_ctxt_type_t *p_cctxt); +u32 vcd_handle_input_frame(struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *input_frame); -u32 vcd_handle_input_frame - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_input_frame); +u32 vcd_store_seq_hdr(struct vcd_clnt_ctxt *cctxt, + struct vcd_sequence_hdr *seq_hdr); -u32 vcd_store_seq_hdr - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_sequence_hdr_type *p_seq_hdr); +u32 vcd_set_frame_size(struct vcd_clnt_ctxt *cctxt, + struct vcd_property_frame_size *frm_size); -u32 vcd_set_frame_size - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_property_frame_size_type *p_frm_size); +u32 vcd_set_frame_rate(struct vcd_clnt_ctxt *cctxt, + struct vcd_property_frame_rate *fps); -u32 vcd_set_frame_rate - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_property_frame_rate_type *p_fps); +u32 vcd_calculate_frame_delta(struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *frame); -u32 vcd_calculate_frame_delta - (struct vcd_clnt_ctxt_type_t *p_cctxt, struct vcd_frame_data_type *p_frame); +struct vcd_buffer_entry *vcd_check_fill_output_buffer( + struct vcd_clnt_ctxt *cctxt, struct vcd_frame_data *buffer); -struct vcd_buffer_entry_type *vcd_check_fill_output_buffer - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_buffer); +u32 vcd_requeue_input_frame(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_clnt_ctxt *cctxt, struct vcd_buffer_entry *buf_entry); -u32 vcd_requeue_input_frame(struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_clnt_ctxt_type_t *p_cctxt, struct vcd_buffer_entry_type - *p_buf_entry); +u32 vcd_schedule_frame(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_clnt_ctxt **pp_cctxt, + struct vcd_buffer_entry **pp_ip_buf_entry); -u32 vcd_schedule_frame(struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_clnt_ctxt_type_t **pp_cctxt, struct vcd_buffer_entry_type - **pp_ip_buf_entry); +u32 vcd_map_sched_status(enum sched_status sched_status); -u32 vcd_map_sched_status(enum sched_status_type e_sched_status); +u32 vcd_submit_command_in_continue(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc *transc); -u32 vcd_submit_command_in_continue - (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type *p_transc); +u32 vcd_submit_cmd_sess_start(struct vcd_transc *transc); -u32 vcd_submit_cmd_sess_start(struct vcd_transc_type *p_transc); +u32 vcd_submit_cmd_sess_end(struct vcd_transc *transc); -u32 vcd_submit_cmd_sess_end(struct vcd_transc_type *p_transc); +void vcd_submit_cmd_client_close(struct vcd_clnt_ctxt *cctxt); -void vcd_submit_cmd_client_close(struct vcd_clnt_ctxt_type_t *p_cctxt); +u32 vcd_submit_frame(struct vcd_dev_ctxt *dev_ctxt, struct vcd_transc *transc); -u32 vcd_submit_frame - (struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type *p_transc); +u32 vcd_try_submit_frame_in_continue(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc *transc); -u32 vcd_try_submit_frame_in_continue(struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_transc_type *p_transc); +u32 vcd_process_cmd_sess_start(struct vcd_clnt_ctxt *cctxt); -u32 vcd_process_cmd_sess_start(struct vcd_clnt_ctxt_type_t *p_cctxt); +void vcd_try_submit_frame(struct vcd_dev_ctxt *dev_ctxt); -void vcd_try_submit_frame(struct vcd_dev_ctxt_type *p_dev_ctxt); +u32 vcd_setup_with_ddl_capabilities(struct vcd_dev_ctxt *dev_ctxt); +void vcd_handle_submit_frame_failed(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc *transc); -u32 vcd_setup_with_ddl_capabilities(struct vcd_dev_ctxt_type *p_dev_ctxt); -void vcd_handle_submit_frame_failed(struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_transc_type *p_transc); +struct vcd_transc *vcd_get_free_trans_tbl_entry(struct vcd_dev_ctxt *dev_ctxt); -struct vcd_transc_type *vcd_get_free_trans_tbl_entry - (struct vcd_dev_ctxt_type *p_dev_ctxt); +void vcd_release_trans_tbl_entry(struct vcd_transc *trans_entry); -void vcd_release_trans_tbl_entry(struct vcd_transc_type *p_trans_entry); +void vcd_release_all_clnt_frm_transc(struct vcd_clnt_ctxt *cctxt); +void vcd_release_all_clnt_def_frm_transc(struct vcd_clnt_ctxt *cctxt); +void vcd_release_all_clnt_transc(struct vcd_clnt_ctxt *cctxt); -void vcd_release_all_clnt_frm_transc(struct vcd_clnt_ctxt_type_t *p_cctxt); -void vcd_release_all_clnt_def_frm_transc(struct vcd_clnt_ctxt_type_t *p_cctxt); -void vcd_release_all_clnt_transc(struct vcd_clnt_ctxt_type_t *p_cctxt); +u32 vcd_handle_input_done(struct vcd_clnt_ctxt *cctxt, void *payload, + u32 event, u32 status); -u32 vcd_handle_input_done - (struct vcd_clnt_ctxt_type_t *p_cctxt, - void *p_payload, u32 event, u32 status); +void vcd_handle_input_done_in_eos(struct vcd_clnt_ctxt *cctxt, void *payload, + u32 status); -void vcd_handle_input_done_in_eos - (struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload, u32 status); +void vcd_handle_input_done_failed(struct vcd_clnt_ctxt *cctxt, + struct vcd_transc *transc); -void vcd_handle_input_done_failed - (struct vcd_clnt_ctxt_type_t *p_cctxt, struct vcd_transc_type *p_transc); +void vcd_handle_input_done_for_interlacing(struct vcd_clnt_ctxt *cctxt); -void vcd_handle_input_done_for_interlacing - (struct vcd_clnt_ctxt_type_t *p_cctxt); +void vcd_handle_input_done_with_trans_end(struct vcd_clnt_ctxt *cctxt); -void vcd_handle_input_done_with_trans_end - (struct vcd_clnt_ctxt_type_t *p_cctxt); +u32 vcd_handle_frame_done(struct vcd_clnt_ctxt *cctxt, void *payload, + u32 event, u32 status); -u32 vcd_handle_frame_done - (struct vcd_clnt_ctxt_type_t *p_cctxt, - void *p_payload, u32 event, u32 status); +void vcd_handle_frame_done_for_interlacing(struct vcd_clnt_ctxt *cctxt, + struct vcd_transc *transc_ip1, struct ddl_frame_data_tag *op_frm, + u32 status); -void vcd_handle_frame_done_for_interlacing - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_transc_type *p_transc_ip1, - struct ddl_frame_data_type_tag *p_op_frm, u32 status); +u32 vcd_handle_first_frame_done(struct vcd_clnt_ctxt *cctxt, void *payload); -u32 vcd_handle_first_frame_done - (struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload); +void vcd_handle_frame_done_in_eos(struct vcd_clnt_ctxt *cctxt, void *payload, + u32 status); -void vcd_handle_frame_done_in_eos - (struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload, u32 status); +u32 vcd_handle_first_encode_frame_done(struct vcd_clnt_ctxt *cctxt, + void *payload); -u32 vcd_handle_first_encode_frame_done - (struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload); +u32 vcd_handle_output_required(struct vcd_clnt_ctxt *cctxt, void *payload, + u32 status); -u32 vcd_handle_output_required(struct vcd_clnt_ctxt_type_t *p_cctxt, - void *p_payload, u32 status); +u32 vcd_handle_output_required_in_flushing(struct vcd_clnt_ctxt *cctxt, + void *payload); -u32 vcd_handle_output_required_in_flushing(struct vcd_clnt_ctxt_type_t *p_cctxt, - void *p_payload); +u32 vcd_handle_output_req_tran_end_in_eos(struct vcd_clnt_ctxt *cctxt); -u32 vcd_handle_output_req_tran_end_in_eos(struct vcd_clnt_ctxt_type_t *p_cctxt); +u32 vcd_validate_io_done_pyld(void *payload, u32 status); -u32 vcd_validate_io_done_pyld(void *p_payload, u32 status); +void vcd_handle_eos_trans_end(struct vcd_clnt_ctxt *cctxt); -void vcd_handle_eos_trans_end(struct vcd_clnt_ctxt_type_t *p_cctxt); +void vcd_handle_eos_done(struct vcd_clnt_ctxt *cctxt, + struct vcd_transc *transc, u32 status); +void vcd_send_frame_done_in_eos(struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *input_frame, u32 valid_opbuf); -void vcd_handle_eos_done - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_transc_type *p_transc, u32 status); +void vcd_send_frame_done_in_eos_for_dec(struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *input_frame); -void vcd_send_frame_done_in_eos - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_input_frame, u32 valid_opbuf); +void vcd_send_frame_done_in_eos_for_enc(struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *input_frame); -void vcd_send_frame_done_in_eos_for_dec - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_input_frame); +void vcd_handle_start_done(struct vcd_clnt_ctxt *cctxt, + struct vcd_transc *transc, u32 status); -void vcd_send_frame_done_in_eos_for_enc - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_input_frame); +void vcd_handle_stop_done(struct vcd_clnt_ctxt *cctxt, + struct vcd_transc *transc, u32 status); -void vcd_handle_start_done(struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_transc_type *p_transc, u32 status); +void vcd_handle_stop_done_in_starting(struct vcd_clnt_ctxt *cctxt, + struct vcd_transc *transc, u32 status); -void vcd_handle_stop_done(struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_transc_type *p_transc, u32 status); +void vcd_handle_stop_done_in_invalid(struct vcd_clnt_ctxt *cctxt, u32 status); -void vcd_handle_stop_done_in_starting(struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_transc_type *p_transc, u32 status); +void vcd_send_flush_done(struct vcd_clnt_ctxt *cctxt, u32 status); -void vcd_handle_stop_done_in_invalid(struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 status); +void vcd_process_pending_flush_in_eos(struct vcd_clnt_ctxt *cctxt); +void vcd_process_pending_stop_in_eos(struct vcd_clnt_ctxt *cctxt); -void vcd_send_flush_done(struct vcd_clnt_ctxt_type_t *p_cctxt, u32 status); +void vcd_handle_trans_pending(struct vcd_clnt_ctxt *cctxt); -void vcd_process_pending_flush_in_eos(struct vcd_clnt_ctxt_type_t *p_cctxt); +void vcd_flush_output_buffers(struct vcd_clnt_ctxt *cctxt); -void vcd_process_pending_stop_in_eos(struct vcd_clnt_ctxt_type_t *p_cctxt); +u32 vcd_flush_buffers(struct vcd_clnt_ctxt *cctxt, u32 mode); +void vcd_flush_buffers_in_err_fatal(struct vcd_clnt_ctxt *cctxt); -void vcd_handle_trans_pending(struct vcd_clnt_ctxt_type_t *p_cctxt); +u32 vcd_power_event(struct vcd_dev_ctxt *dev_ctxt, struct vcd_clnt_ctxt *cctxt, + u32 event); -void vcd_flush_output_buffers(struct vcd_clnt_ctxt_type_t *p_cctxt); +u32 vcd_device_power_event(struct vcd_dev_ctxt *dev_ctxt, u32 event, + struct vcd_clnt_ctxt *cctxt); -u32 vcd_flush_buffers(struct vcd_clnt_ctxt_type_t *p_cctxt, u32 n_mode); -void vcd_flush_buffers_in_err_fatal(struct vcd_clnt_ctxt_type_t *p_cctxt); +u32 vcd_client_power_event(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_clnt_ctxt *cctxt, u32 event); -u32 vcd_power_event - (struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_clnt_ctxt_type_t *p_cctxt, u32 event); +u32 vcd_enable_clock(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_clnt_ctxt *cctxt); -u32 vcd_device_power_event(struct vcd_dev_ctxt_type *p_dev_ctxt, u32 event, - struct vcd_clnt_ctxt_type_t *p_cctxt); +u32 vcd_disable_clock(struct vcd_dev_ctxt *dev_ctxt); -u32 vcd_client_power_event - (struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_clnt_ctxt_type_t *p_cctxt, u32 event); +u32 vcd_set_perf_level(struct vcd_dev_ctxt *dev_ctxt, u32 perf_lvl, + struct vcd_clnt_ctxt *cctxt); -u32 vcd_enable_clock(struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_clnt_ctxt_type_t *p_cctxt); +u32 vcd_update_clnt_perf_lvl(struct vcd_clnt_ctxt *cctxt, + struct vcd_property_frame_rate *fps, u32 frm_p_units); -u32 vcd_disable_clock(struct vcd_dev_ctxt_type *p_dev_ctxt); +u32 vcd_gate_clock(struct vcd_dev_ctxt *dev_ctxt); -u32 vcd_set_perf_level(struct vcd_dev_ctxt_type *p_dev_ctxt, - u32 n_perf_lvl, struct vcd_clnt_ctxt_type_t *p_cctxt); +u32 vcd_un_gate_clock(struct vcd_dev_ctxt *dev_ctxt); -u32 vcd_update_clnt_perf_lvl - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_property_frame_rate_type *p_fps, u32 n_frm_p_units); +void vcd_handle_err_fatal(struct vcd_clnt_ctxt *cctxt, u32 event, u32 status); -u32 vcd_gate_clock(struct vcd_dev_ctxt_type *p_dev_ctxt); +void vcd_handle_device_err_fatal(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_clnt_ctxt *cctxt); -u32 vcd_un_gate_clock(struct vcd_dev_ctxt_type *p_dev_ctxt); +void vcd_clnt_handle_device_err_fatal(struct vcd_clnt_ctxt *cctxt, u32 event); -void vcd_handle_err_fatal(struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 event, u32 status); +void vcd_handle_err_in_starting(struct vcd_clnt_ctxt *cctxt, u32 status); -void vcd_handle_device_err_fatal(struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_clnt_ctxt_type_t *p_cctxt); +void vcd_handle_ind_hw_err_fatal(struct vcd_clnt_ctxt *cctxt, u32 event, + u32 status); -void vcd_clnt_handle_device_err_fatal(struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 event); - -void vcd_handle_err_in_starting(struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 status); - -void vcd_handle_ind_hw_err_fatal(struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 event, u32 status); #endif diff --git a/drivers/misc/video_core/720p/vcd/vcd_api.c b/drivers/misc/video_core/720p/vcd/vcd_api.c index fbe409b9b3a5b..202f61d88a77c 100644 --- a/drivers/misc/video_core/720p/vcd/vcd_api.c +++ b/drivers/misc/video_core/720p/vcd/vcd_api.c @@ -19,41 +19,43 @@ #include "video_core_type.h" #include "vcd.h" -u32 vcd_init(struct vcd_init_config_type *p_config, s32 *p_driver_handle) +u32 vcd_init(struct vcd_init_config *config, s32 *driver_handle) { u32 rc = VCD_S_SUCCESS; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_drv_ctxt *drv_ctxt; VCD_MSG_MED("vcd_init:"); - if (!p_config || - !p_driver_handle || !p_config->pf_map_dev_base_addr) { + if (!config || !driver_handle || !config->pf_map_dev_base_addr) { VCD_MSG_ERROR("Bad parameters"); return VCD_ERR_ILLEGAL_PARM; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - if (!p_drv_ctxt->dev_cs) - rc = vcd_critical_section_create(&p_drv_ctxt->dev_cs); - - if (rc) - return VCD_ERR_ALLOC_FAIL; + if (!drv_ctxt->dev_mutex) { + drv_ctxt->dev_mutex = kmalloc(sizeof(struct mutex), GFP_KERNEL); + if (!drv_ctxt->dev_mutex) { + VCD_MSG_ERROR("Failed: vcd_critical_section_create"); + return VCD_ERR_ALLOC_FAIL; + } + mutex_init(drv_ctxt->dev_mutex); + } - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_drv_ctxt->dev_state.p_state_table->ev_hdlr.pf_init) { - rc = p_drv_ctxt->dev_state.p_state_table->ev_hdlr. - pf_init(p_drv_ctxt, p_config, p_driver_handle); + if (drv_ctxt->dev_state.state_table->ev_hdlr.pf_init) { + rc = drv_ctxt->dev_state.state_table->ev_hdlr. + pf_init(drv_ctxt, config, driver_handle); } else { - VCD_MSG_ERROR("Unsupported API in device state %d", - p_drv_ctxt->dev_state.e_state); + VCD_MSG_ERROR("Unsupported API in device state %d\n", + drv_ctxt->dev_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; @@ -63,38 +65,39 @@ EXPORT_SYMBOL(vcd_init); u32 vcd_term(s32 driver_handle) { u32 rc = VCD_S_SUCCESS; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_drv_ctxt *drv_ctxt; VCD_MSG_MED("vcd_term:"); - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - if (!p_drv_ctxt->dev_cs) { + if (!drv_ctxt->dev_mutex) { VCD_MSG_ERROR("No critical section object"); return VCD_ERR_BAD_STATE; } - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_drv_ctxt->dev_state.p_state_table->ev_hdlr.pf_term) { - rc = p_drv_ctxt->dev_state.p_state_table->ev_hdlr. - pf_term(p_drv_ctxt, driver_handle); + if (drv_ctxt->dev_state.state_table->ev_hdlr.pf_term) { + rc = drv_ctxt->dev_state.state_table->ev_hdlr. + pf_term(drv_ctxt, driver_handle); } else { - VCD_MSG_ERROR("Unsupported API in device state %d", - p_drv_ctxt->dev_state.e_state); + VCD_MSG_ERROR("Unsupported API in device state %d\n", + drv_ctxt->dev_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); - if (p_drv_ctxt->dev_state.e_state == VCD_DEVICE_STATE_NULL) { + if (drv_ctxt->dev_state.state == VCD_DEVICE_STATE_NULL) { VCD_MSG_HIGH - ("Device in NULL state. Releasing critical section"); + ("Device in NULL state. Releasing critical section\n"); - vcd_critical_section_release(p_drv_ctxt->dev_cs); - p_drv_ctxt->dev_cs = NULL; + mutex_destroy(drv_ctxt->dev_mutex); + kfree(drv_ctxt->dev_mutex); + drv_ctxt->dev_mutex = NULL; } return rc; @@ -102,44 +105,43 @@ u32 vcd_term(s32 driver_handle) } EXPORT_SYMBOL(vcd_term); -u32 vcd_open(s32 driver_handle, u32 b_decoding, - void (*callback) (u32 event, u32 status, void *p_info, u32 n_size, - void *handle, void *const p_client_data), - void *p_client_data) +u32 vcd_open(s32 driver_handle, u32 decoding, + void (*callback) (u32 event, u32 status, void *info, u32 size, + void *handle, void *const client_data), void *client_data) { u32 rc = VCD_S_SUCCESS; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_drv_ctxt *drv_ctxt; - VCD_MSG_MED("vcd_open:"); + VCD_MSG_MED("vcd_open:\n"); if (!callback) { - VCD_MSG_ERROR("Bad parameters"); + VCD_MSG_ERROR("Bad parameters\n"); return VCD_ERR_ILLEGAL_PARM; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - if (!p_drv_ctxt->dev_cs) { - VCD_MSG_ERROR("No critical section object"); + if (!drv_ctxt->dev_mutex) { + VCD_MSG_ERROR("No critical section object\n"); return VCD_ERR_BAD_STATE; } - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_drv_ctxt->dev_state.p_state_table->ev_hdlr.pf_open) { - rc = p_drv_ctxt->dev_state.p_state_table->ev_hdlr. - pf_open(p_drv_ctxt, driver_handle, b_decoding, callback, - p_client_data); + if (drv_ctxt->dev_state.state_table->ev_hdlr.pf_open) { + rc = drv_ctxt->dev_state.state_table->ev_hdlr. + pf_open(drv_ctxt, driver_handle, decoding, callback, + client_data); } else { - VCD_MSG_ERROR("Unsupported API in device state %d", - p_drv_ctxt->dev_state.e_state); + VCD_MSG_ERROR("Unsupported API in device state %d\n", + drv_ctxt->dev_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; @@ -148,27 +150,26 @@ EXPORT_SYMBOL(vcd_open); u32 vcd_close(void *handle) { - struct vcd_clnt_ctxt_type_t *p_cctxt = - (struct vcd_clnt_ctxt_type_t *)handle; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle; + struct vcd_drv_ctxt *drv_ctxt; u32 rc; VCD_MSG_MED("vcd_close:"); - if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { - VCD_MSG_ERROR("Bad client handle"); + if (!cctxt || cctxt->signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle\n"); return VCD_ERR_BAD_HANDLE; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - if (p_drv_ctxt->dev_state.p_state_table->ev_hdlr.pf_close) { - rc = p_drv_ctxt->dev_state.p_state_table->ev_hdlr. - pf_close(p_drv_ctxt, p_cctxt); + if (drv_ctxt->dev_state.state_table->ev_hdlr.pf_close) { + rc = drv_ctxt->dev_state.state_table->ev_hdlr. + pf_close(drv_ctxt, cctxt); } else { - VCD_MSG_ERROR("Unsupported API in device state %d", - p_drv_ctxt->dev_state.e_state); + VCD_MSG_ERROR("Unsupported API in device state %d\n", + drv_ctxt->dev_state.state); rc = VCD_ERR_BAD_STATE; } @@ -180,160 +181,157 @@ EXPORT_SYMBOL(vcd_close); u32 vcd_encode_start(void *handle) { - struct vcd_clnt_ctxt_type_t *p_cctxt = - (struct vcd_clnt_ctxt_type_t *)handle; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle; + struct vcd_drv_ctxt *drv_ctxt; u32 rc; VCD_MSG_MED("vcd_encode_start:"); - if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { - VCD_MSG_ERROR("Bad client handle"); + if (!cctxt || cctxt->signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle\n"); return VCD_ERR_BAD_HANDLE; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_encode_start && - p_drv_ctxt->dev_ctxt.e_pwr_state != VCD_PWR_STATE_SLEEP) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_encode_start(p_cctxt); + if (cctxt->clnt_state.state_table->ev_hdlr.pf_encode_start && + drv_ctxt->dev_ctxt.pwr_state != VCD_PWR_STATE_SLEEP) { + rc = cctxt->clnt_state.state_table->ev_hdlr. + pf_encode_start(cctxt); } else { VCD_MSG_ERROR - ("Unsupported API in dev power state %d OR client state %d", - p_drv_ctxt->dev_ctxt.e_pwr_state, - p_cctxt->clnt_state.e_state); + ("Unsupported API dev power state %d OR client state %d\n", + drv_ctxt->dev_ctxt.pwr_state, + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; } EXPORT_SYMBOL(vcd_encode_start); -u32 vcd_encode_frame(void *handle, struct vcd_frame_data_type *p_input_frame) +u32 vcd_encode_frame(void *handle, struct vcd_frame_data *input_frame) { - struct vcd_clnt_ctxt_type_t *p_cctxt = - (struct vcd_clnt_ctxt_type_t *)handle; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle; + struct vcd_drv_ctxt *drv_ctxt; u32 rc; - VCD_MSG_MED("vcd_encode_frame:"); + VCD_MSG_MED("vcd_encode_frame:\n"); - if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { - VCD_MSG_ERROR("Bad client handle"); + if (!cctxt || cctxt->signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle\n"); return VCD_ERR_BAD_HANDLE; } - if (!p_input_frame) { - VCD_MSG_ERROR("Bad parameters"); + if (!input_frame) { + VCD_MSG_ERROR("Bad parameters\n"); return VCD_ERR_BAD_POINTER; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_encode_frame) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_encode_frame(p_cctxt, p_input_frame); + if (cctxt->clnt_state.state_table->ev_hdlr.pf_encode_frame) { + rc = cctxt->clnt_state.state_table->ev_hdlr. + pf_encode_frame(cctxt, input_frame); } else { - VCD_MSG_ERROR("Unsupported API in client state %d", - p_cctxt->clnt_state.e_state); + VCD_MSG_ERROR("Unsupported API in client state %d\n", + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; } EXPORT_SYMBOL(vcd_encode_frame); -u32 vcd_decode_start(void *handle, struct vcd_sequence_hdr_type *p_seq_hdr) +u32 vcd_decode_start(void *handle, struct vcd_sequence_hdr *seq_hdr) { - struct vcd_clnt_ctxt_type_t *p_cctxt = - (struct vcd_clnt_ctxt_type_t *)handle; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle; + struct vcd_drv_ctxt *drv_ctxt; u32 rc; - VCD_MSG_MED("vcd_decode_start:"); + VCD_MSG_MED("vcd_decode_start:\n"); - if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { - VCD_MSG_ERROR("Bad client handle"); + if (!cctxt || cctxt->signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle\n"); return VCD_ERR_BAD_HANDLE; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_decode_start && - p_drv_ctxt->dev_ctxt.e_pwr_state != VCD_PWR_STATE_SLEEP) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_decode_start(p_cctxt, p_seq_hdr); + if (cctxt->clnt_state.state_table->ev_hdlr.pf_decode_start && + drv_ctxt->dev_ctxt.pwr_state != VCD_PWR_STATE_SLEEP) { + rc = cctxt->clnt_state.state_table->ev_hdlr. + pf_decode_start(cctxt, seq_hdr); } else { VCD_MSG_ERROR - ("Unsupported API in dev power state %d OR client state %d", - p_drv_ctxt->dev_ctxt.e_pwr_state, - p_cctxt->clnt_state.e_state); + ("Unsupported API dev power state %d OR client state %d\n", + drv_ctxt->dev_ctxt.pwr_state, + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; } EXPORT_SYMBOL(vcd_decode_start); -u32 vcd_decode_frame(void *handle, struct vcd_frame_data_type *p_input_frame) +u32 vcd_decode_frame(void *handle, struct vcd_frame_data *input_frame) { - struct vcd_clnt_ctxt_type_t *p_cctxt = - (struct vcd_clnt_ctxt_type_t *)handle; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_clnt_ctxt *cctxt = + (struct vcd_clnt_ctxt *)handle; + struct vcd_drv_ctxt *drv_ctxt; u32 rc; - VCD_MSG_MED("vcd_decode_frame:"); + VCD_MSG_MED("vcd_decode_frame:\n"); - if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { - VCD_MSG_ERROR("Bad client handle"); + if (!cctxt || cctxt->signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle\n"); return VCD_ERR_BAD_HANDLE; } - if (!p_input_frame) { - VCD_MSG_ERROR("Bad parameters"); + if (!input_frame) { + VCD_MSG_ERROR("Bad parameters\n"); return VCD_ERR_BAD_POINTER; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_decode_frame) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_decode_frame(p_cctxt, p_input_frame); + if (cctxt->clnt_state.state_table->ev_hdlr.pf_decode_frame) { + rc = cctxt->clnt_state.state_table->ev_hdlr. + pf_decode_frame(cctxt, input_frame); } else { - VCD_MSG_ERROR("Unsupported API in client state %d", - p_cctxt->clnt_state.e_state); + VCD_MSG_ERROR("Unsupported API in client state %d\n", + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; @@ -342,34 +340,34 @@ EXPORT_SYMBOL(vcd_decode_frame); u32 vcd_pause(void *handle) { - struct vcd_drv_ctxt_type_t *p_drv_ctxt; - struct vcd_clnt_ctxt_type_t *p_cctxt = - (struct vcd_clnt_ctxt_type_t *)handle; + struct vcd_drv_ctxt *drv_ctxt; + struct vcd_clnt_ctxt *cctxt = + (struct vcd_clnt_ctxt *)handle; u32 rc; - VCD_MSG_MED("vcd_pause:"); + VCD_MSG_MED("vcd_pause:\n"); - if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { - VCD_MSG_ERROR("Bad client handle"); + if (!cctxt || cctxt->signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle\n"); return VCD_ERR_BAD_HANDLE; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_pause) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_pause(p_cctxt); + if (cctxt->clnt_state.state_table->ev_hdlr.pf_pause) { + rc = cctxt->clnt_state.state_table->ev_hdlr. + pf_pause(cctxt); } else { - VCD_MSG_ERROR("Unsupported API in client state %d", - p_cctxt->clnt_state.e_state); + VCD_MSG_ERROR("Unsupported API in client state %d\n", + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; @@ -378,73 +376,71 @@ EXPORT_SYMBOL(vcd_pause); u32 vcd_resume(void *handle) { - struct vcd_drv_ctxt_type_t *p_drv_ctxt; - struct vcd_clnt_ctxt_type_t *p_cctxt = - (struct vcd_clnt_ctxt_type_t *)handle; + struct vcd_drv_ctxt *drv_ctxt; + struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle; u32 rc; - VCD_MSG_MED("vcd_resume:"); + VCD_MSG_MED("vcd_resume:\n"); - if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { - VCD_MSG_ERROR("Bad client handle"); + if (!cctxt || cctxt->signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle\n"); return VCD_ERR_BAD_HANDLE; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_drv_ctxt->dev_state.p_state_table->ev_hdlr.pf_resume && - p_drv_ctxt->dev_ctxt.e_pwr_state != VCD_PWR_STATE_SLEEP) { - rc = p_drv_ctxt->dev_state.p_state_table->ev_hdlr. - pf_resume(p_drv_ctxt, p_cctxt); + if (drv_ctxt->dev_state.state_table->ev_hdlr.pf_resume && + drv_ctxt->dev_ctxt.pwr_state != VCD_PWR_STATE_SLEEP) { + rc = drv_ctxt->dev_state.state_table->ev_hdlr. + pf_resume(drv_ctxt, cctxt); } else { VCD_MSG_ERROR - ("Unsupported API in dev power state %d OR client state %d", - p_drv_ctxt->dev_ctxt.e_pwr_state, - p_cctxt->clnt_state.e_state); + ("Unsupported API dev power state %d OR client state %d\n", + drv_ctxt->dev_ctxt.pwr_state, + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; } EXPORT_SYMBOL(vcd_resume); -u32 vcd_flush(void *handle, u32 n_mode) +u32 vcd_flush(void *handle, u32 mode) { - struct vcd_clnt_ctxt_type_t *p_cctxt = - (struct vcd_clnt_ctxt_type_t *)handle; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle; + struct vcd_drv_ctxt *drv_ctxt; u32 rc; - VCD_MSG_MED("vcd_flush:"); + VCD_MSG_MED("vcd_flush:\n"); - if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { - VCD_MSG_ERROR("Bad client handle"); + if (!cctxt || cctxt->signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle\n"); return VCD_ERR_BAD_HANDLE; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_flush) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_flush(p_cctxt, n_mode); + if (cctxt->clnt_state.state_table->ev_hdlr.pf_flush) { + rc = cctxt->clnt_state.state_table->ev_hdlr. + pf_flush(cctxt, mode); } else { - VCD_MSG_ERROR("Unsupported API in client state %d", - p_cctxt->clnt_state.e_state); + VCD_MSG_ERROR("Unsupported API in client state %d\n", + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; @@ -453,415 +449,397 @@ EXPORT_SYMBOL(vcd_flush); u32 vcd_stop(void *handle) { - struct vcd_clnt_ctxt_type_t *p_cctxt = - (struct vcd_clnt_ctxt_type_t *)handle; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle; + struct vcd_drv_ctxt *drv_ctxt; u32 rc; - VCD_MSG_MED("vcd_stop:"); + VCD_MSG_MED("vcd_stop:\n"); - if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { - VCD_MSG_ERROR("Bad client handle"); + if (!cctxt || cctxt->signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle\n"); return VCD_ERR_BAD_HANDLE; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_stop && - p_drv_ctxt->dev_ctxt.e_pwr_state != VCD_PWR_STATE_SLEEP) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_stop(p_cctxt); + if (cctxt->clnt_state.state_table->ev_hdlr.pf_stop && + drv_ctxt->dev_ctxt.pwr_state != VCD_PWR_STATE_SLEEP) { + rc = cctxt->clnt_state.state_table->ev_hdlr. + pf_stop(cctxt); } else { VCD_MSG_ERROR - ("Unsupported API in dev power state %d OR client state %d", - p_drv_ctxt->dev_ctxt.e_pwr_state, - p_cctxt->clnt_state.e_state); + ("Unsupported API dev power state %d OR client state %d\n", + drv_ctxt->dev_ctxt.pwr_state, + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; } EXPORT_SYMBOL(vcd_stop); -u32 vcd_set_property(void *handle, - struct vcd_property_hdr_type *p_prop_hdr, void *p_prop_val) +u32 vcd_set_property(void *handle, struct vcd_property_hdr *prop_hdr, + void *prop_val) { - struct vcd_clnt_ctxt_type_t *p_cctxt = - (struct vcd_clnt_ctxt_type_t *)handle; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle; + struct vcd_drv_ctxt *drv_ctxt; u32 rc; - VCD_MSG_MED("vcd_set_property:"); + VCD_MSG_MED("vcd_set_property:\n"); - if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { - VCD_MSG_ERROR("Bad client handle"); + if (!cctxt || cctxt->signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle\n"); return VCD_ERR_BAD_HANDLE; } - if (!p_prop_hdr || !p_prop_val) { - VCD_MSG_ERROR("Bad parameters"); + if (!prop_hdr || !prop_val) { + VCD_MSG_ERROR("Bad parameters\n"); return VCD_ERR_BAD_POINTER; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_set_property) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_set_property(p_cctxt, p_prop_hdr, p_prop_val); + if (cctxt->clnt_state.state_table->ev_hdlr.pf_set_property) { + rc = cctxt->clnt_state.state_table->ev_hdlr. + pf_set_property(cctxt, prop_hdr, prop_val); } else { - VCD_MSG_ERROR("Unsupported API in client state %d", - p_cctxt->clnt_state.e_state); + VCD_MSG_ERROR("Unsupported API in client state %d\n", + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; } EXPORT_SYMBOL(vcd_set_property); -u32 vcd_get_property(void *handle, - struct vcd_property_hdr_type *p_prop_hdr, void *p_prop_val) +u32 vcd_get_property(void *handle, struct vcd_property_hdr *prop_hdr, + void *prop_val) { - struct vcd_clnt_ctxt_type_t *p_cctxt = - (struct vcd_clnt_ctxt_type_t *)handle; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle; + struct vcd_drv_ctxt *drv_ctxt; u32 rc; - VCD_MSG_MED("vcd_get_property:"); + VCD_MSG_MED("vcd_get_property:\n"); - if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { - VCD_MSG_ERROR("Bad client handle"); + if (!cctxt || cctxt->signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle\n"); return VCD_ERR_BAD_HANDLE; } - if (!p_prop_hdr || !p_prop_val) { - VCD_MSG_ERROR("Bad parameters"); + if (!prop_hdr || !prop_val) { + VCD_MSG_ERROR("Bad parameters\n"); return VCD_ERR_BAD_POINTER; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_get_property) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_get_property(p_cctxt, p_prop_hdr, p_prop_val); + if (cctxt->clnt_state.state_table->ev_hdlr.pf_get_property) { + rc = cctxt->clnt_state.state_table->ev_hdlr. + pf_get_property(cctxt, prop_hdr, prop_val); } else { - VCD_MSG_ERROR("Unsupported API in client state %d", - p_cctxt->clnt_state.e_state); + VCD_MSG_ERROR("Unsupported API in client state %d\n", + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; } EXPORT_SYMBOL(vcd_get_property); -u32 vcd_set_buffer_requirements(void *handle, - enum vcd_buffer_type e_buffer, - struct vcd_buffer_requirement_type *p_buffer_req) +u32 vcd_set_buffer_requirements(void *handle, enum vcd_buffer_type buffer_type, + struct vcd_buffer_requirement *buffer_req) { - struct vcd_clnt_ctxt_type_t *p_cctxt = - (struct vcd_clnt_ctxt_type_t *)handle; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle; + struct vcd_drv_ctxt *drv_ctxt; u32 rc; - VCD_MSG_MED("vcd_set_buffer_requirements:"); + VCD_MSG_MED("vcd_set_buffer_requirements:\n"); - if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { - VCD_MSG_ERROR("Bad client handle"); + if (!cctxt || cctxt->signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle\n"); return VCD_ERR_BAD_HANDLE; } - if (!p_buffer_req) { - VCD_MSG_ERROR("Bad parameters"); + if (!buffer_req) { + VCD_MSG_ERROR("Bad parameters\n"); return VCD_ERR_BAD_POINTER; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr. + if (cctxt->clnt_state.state_table->ev_hdlr. pf_set_buffer_requirements) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_set_buffer_requirements(p_cctxt, e_buffer, p_buffer_req); + rc = cctxt->clnt_state.state_table->ev_hdlr. + pf_set_buffer_requirements(cctxt, buffer_type, buffer_req); } else { - VCD_MSG_ERROR("Unsupported API in client state %d", - p_cctxt->clnt_state.e_state); + VCD_MSG_ERROR("Unsupported API in client state %d\n", + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; } EXPORT_SYMBOL(vcd_set_buffer_requirements); -u32 vcd_get_buffer_requirements(void *handle, - enum vcd_buffer_type e_buffer, - struct vcd_buffer_requirement_type *p_buffer_req) +u32 vcd_get_buffer_requirements(void *handle, enum vcd_buffer_type buffer_type, + struct vcd_buffer_requirement *buffer_req) { - struct vcd_clnt_ctxt_type_t *p_cctxt = - (struct vcd_clnt_ctxt_type_t *)handle; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_clnt_ctxt *cctxt = (struct vcd_clnt_ctxt *)handle; + struct vcd_drv_ctxt *drv_ctxt; u32 rc; - VCD_MSG_MED("vcd_get_buffer_requirements:"); + VCD_MSG_MED("vcd_get_buffer_requirements:\n"); - if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { - VCD_MSG_ERROR("Bad client handle"); + if (!cctxt || cctxt->signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle\n"); return VCD_ERR_BAD_HANDLE; } - if (!p_buffer_req) { - VCD_MSG_ERROR("Bad parameters"); + if (!buffer_req) { + VCD_MSG_ERROR("Bad parameters\n"); return VCD_ERR_BAD_POINTER; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr. + if (cctxt->clnt_state.state_table->ev_hdlr. pf_get_buffer_requirements) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_get_buffer_requirements(p_cctxt, e_buffer, p_buffer_req); + rc = cctxt->clnt_state.state_table->ev_hdlr. + pf_get_buffer_requirements(cctxt, buffer_type, buffer_req); } else { - VCD_MSG_ERROR("Unsupported API in client state %d", - p_cctxt->clnt_state.e_state); + VCD_MSG_ERROR("Unsupported API in client state %d\n", + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; } EXPORT_SYMBOL(vcd_get_buffer_requirements); -u32 vcd_set_buffer(void *handle, - enum vcd_buffer_type e_buffer, u8 *p_buffer, u32 n_buf_size) +u32 vcd_set_buffer(void *handle, enum vcd_buffer_type buffer_type, void *buffer, + size_t buf_size) { - struct vcd_clnt_ctxt_type_t *p_cctxt = - (struct vcd_clnt_ctxt_type_t *)handle; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_clnt_ctxt *cctxt = handle; + struct vcd_drv_ctxt *drv_ctxt; u32 rc; - VCD_MSG_MED("vcd_set_buffer:"); + VCD_MSG_MED("vcd_set_buffer:\n"); - if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { - VCD_MSG_ERROR("Bad client handle"); + if (!cctxt || cctxt->signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle\n"); return VCD_ERR_BAD_HANDLE; } - if (!p_buffer || !n_buf_size) { - VCD_MSG_ERROR("Bad parameters"); + if (!buffer || !buf_size) { + VCD_MSG_ERROR("Bad parameters\n"); return VCD_ERR_BAD_POINTER; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_set_buffer) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_set_buffer(p_cctxt, e_buffer, p_buffer, n_buf_size); + if (cctxt->clnt_state.state_table->ev_hdlr.pf_set_buffer) { + rc = cctxt->clnt_state.state_table->ev_hdlr. + pf_set_buffer(cctxt, buffer_type, buffer, buf_size); } else { - VCD_MSG_ERROR("Unsupported API in client state %d", - p_cctxt->clnt_state.e_state); + VCD_MSG_ERROR("Unsupported API in client state %d\n", + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; } EXPORT_SYMBOL(vcd_set_buffer); -u32 vcd_allocate_buffer(void *handle, - enum vcd_buffer_type e_buffer, - u32 n_buf_size, u8 **pp_vir_buf_addr, u8 **pp_phy_buf_addr) +u32 vcd_allocate_buffer(void *handle, enum vcd_buffer_type buffer_type, + size_t sz, void **virt_addr, phys_addr_t *phys_addr) { - struct vcd_clnt_ctxt_type_t *p_cctxt = - (struct vcd_clnt_ctxt_type_t *)handle; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_clnt_ctxt *cctxt = handle; + struct vcd_drv_ctxt *drv_ctxt; u32 rc; - VCD_MSG_MED("vcd_allocate_buffer:"); - - if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { - VCD_MSG_ERROR("Bad client handle"); + VCD_MSG_MED("vcd_allocate_buffer:\n"); + if (!cctxt || cctxt->signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle\n"); return VCD_ERR_BAD_HANDLE; } - if (!pp_vir_buf_addr || !pp_phy_buf_addr - || !n_buf_size) { - VCD_MSG_ERROR("Bad parameters"); - + if (!virt_addr || !phys_addr || !sz) { + VCD_MSG_ERROR("Bad parameters\n"); return VCD_ERR_BAD_POINTER; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_allocate_buffer) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_allocate_buffer(p_cctxt, e_buffer, n_buf_size, - pp_vir_buf_addr, pp_phy_buf_addr); + if (cctxt->clnt_state.state_table->ev_hdlr.pf_allocate_buffer) { + rc = cctxt->clnt_state.state_table->ev_hdlr.pf_allocate_buffer( + cctxt, buffer_type, sz, virt_addr, phys_addr); } else { - VCD_MSG_ERROR("Unsupported API in client state %d", - p_cctxt->clnt_state.e_state); + VCD_MSG_ERROR("Unsupported API in client state %d\n", + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; } EXPORT_SYMBOL(vcd_allocate_buffer); -u32 vcd_free_buffer(void *handle, enum vcd_buffer_type e_buffer, u8 *p_buffer) +u32 vcd_free_buffer(void *handle, enum vcd_buffer_type buffer_type, void *buf) { - struct vcd_clnt_ctxt_type_t *p_cctxt = - (struct vcd_clnt_ctxt_type_t *)handle; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_clnt_ctxt *cctxt = handle; + struct vcd_drv_ctxt *drv_ctxt; u32 rc; VCD_MSG_MED("vcd_free_buffer:"); - if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { - VCD_MSG_ERROR("Bad client handle"); - + if (!cctxt || cctxt->signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle\n"); return VCD_ERR_BAD_HANDLE; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_free_buffer) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_free_buffer(p_cctxt, e_buffer, p_buffer); + if (cctxt->clnt_state.state_table->ev_hdlr.pf_free_buffer) { + rc = cctxt->clnt_state.state_table->ev_hdlr. + pf_free_buffer(cctxt, buffer_type, buf); } else { - VCD_MSG_ERROR("Unsupported API in client state %d", - p_cctxt->clnt_state.e_state); - + VCD_MSG_ERROR("Unsupported API in client state %d\n", + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; } EXPORT_SYMBOL(vcd_free_buffer); -u32 vcd_fill_output_buffer(void *handle, struct vcd_frame_data_type *p_buffer) +u32 vcd_fill_output_buffer(void *handle, struct vcd_frame_data *buffer) { - struct vcd_clnt_ctxt_type_t *p_cctxt = - (struct vcd_clnt_ctxt_type_t *)handle; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_clnt_ctxt *cctxt = + (struct vcd_clnt_ctxt *)handle; + struct vcd_drv_ctxt *drv_ctxt; u32 rc; - VCD_MSG_MED("vcd_fill_output_buffer:"); + VCD_MSG_MED("vcd_fill_output_buffer:\n"); - if (!p_cctxt || p_cctxt->n_signature != VCD_SIGNATURE) { - VCD_MSG_ERROR("Bad client handle"); + if (!cctxt || cctxt->signature != VCD_SIGNATURE) { + VCD_MSG_ERROR("Bad client handle\n"); return VCD_ERR_BAD_HANDLE; } - if (!p_buffer) { - VCD_MSG_ERROR("Bad parameters"); + if (!buffer) { + VCD_MSG_ERROR("Bad parameters\n"); return VCD_ERR_BAD_POINTER; } - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_fill_output_buffer) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_fill_output_buffer(p_cctxt, p_buffer); + if (cctxt->clnt_state.state_table->ev_hdlr.pf_fill_output_buffer) { + rc = cctxt->clnt_state.state_table->ev_hdlr. + pf_fill_output_buffer(cctxt, buffer); } else { - VCD_MSG_ERROR("Unsupported API in client state %d", - p_cctxt->clnt_state.e_state); + VCD_MSG_ERROR("Unsupported API in client state %d\n", + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; } EXPORT_SYMBOL(vcd_fill_output_buffer); -u32 vcd_set_device_power(s32 driver_handle, - enum vcd_power_state_type e_pwr_state) +u32 vcd_set_device_power(s32 driver_handle, enum vcd_power_state pwr_state) { u32 rc = VCD_S_SUCCESS; - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_drv_ctxt *drv_ctxt; - VCD_MSG_MED("vcd_set_device_power:"); + VCD_MSG_MED("vcd_set_device_power:\n"); - p_drv_ctxt = vcd_get_drv_context(); + drv_ctxt = vcd_get_drv_context(); - if (!p_drv_ctxt->dev_cs) { - VCD_MSG_ERROR("No critical section object"); + if (!drv_ctxt->dev_mutex) { + VCD_MSG_ERROR("No critical section object\n"); return VCD_ERR_BAD_STATE; } - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); - if (p_drv_ctxt->dev_state.p_state_table->ev_hdlr.pf_set_dev_pwr) { - rc = p_drv_ctxt->dev_state.p_state_table->ev_hdlr. - pf_set_dev_pwr(p_drv_ctxt, e_pwr_state); + if (drv_ctxt->dev_state.state_table->ev_hdlr.pf_set_dev_pwr) { + rc = drv_ctxt->dev_state.state_table->ev_hdlr. + pf_set_dev_pwr(drv_ctxt, pwr_state); } else { - VCD_MSG_ERROR("Unsupported API in device state %d", - p_drv_ctxt->dev_state.e_state); + VCD_MSG_ERROR("Unsupported API in device state %d\n", + drv_ctxt->dev_state.state); rc = VCD_ERR_BAD_STATE; } - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); return rc; @@ -870,31 +848,30 @@ EXPORT_SYMBOL(vcd_set_device_power); void vcd_read_and_clear_interrupt(void) { - VCD_MSG_LOW("vcd_read_and_clear_interrupt:"); - ddl_read_and_clear_interrupt(); + VCD_MSG_LOW("vcd_read_and_clear_interrupt:\n"); + ddl_read_and_clear_interrupt(); } void vcd_response_handler(void) { - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_drv_ctxt *drv_ctxt; - VCD_MSG_LOW("vcd_response_handler:"); - p_drv_ctxt = vcd_get_drv_context(); + VCD_MSG_LOW("vcd_response_handler:\n"); + drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); + mutex_lock(drv_ctxt->dev_mutex); if (!ddl_process_core_response()) { - VCD_MSG_HIGH - ("ddl_process_core_response indicated no further" - "processing"); - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + VCD_MSG_HIGH("ddl_process_core_response indicated no further" + "processing\n"); + mutex_unlock(drv_ctxt->dev_mutex); return; } - if (p_drv_ctxt->dev_ctxt.b_continue) + if (drv_ctxt->dev_ctxt.cont) vcd_continue(); - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + mutex_unlock(drv_ctxt->dev_mutex); } EXPORT_SYMBOL(vcd_response_handler); diff --git a/drivers/misc/video_core/720p/vcd/vcd_api.h b/drivers/misc/video_core/720p/vcd/vcd_api.h index 12c63fe1bec17..436833142680b 100644 --- a/drivers/misc/video_core/720p/vcd/vcd_api.h +++ b/drivers/misc/video_core/720p/vcd/vcd_api.h @@ -43,14 +43,14 @@ #define VCD_FRAMETAG_INVALID 0xffffffff -struct vcd_handle_container_type { +struct vcd_handle_container { void *handle; }; -struct vcd_flush_cmd_type { - u32 n_mode; +struct vcd_flush_cmd { + u32 mode; }; -enum vcd_frame_type { +enum vcd_frame { VCD_FRAME_YUV = 1, VCD_FRAME_I, VCD_FRAME_P, @@ -58,30 +58,34 @@ enum vcd_frame_type { VCD_FRAME_32BIT = 0x7fffffff }; -enum vcd_power_state_type { +enum vcd_power_state { VCD_PWR_STATE_ON = 1, VCD_PWR_STATE_SLEEP, }; -struct vcd_frame_data_type { - u8 *p_virtual; - u8 *p_physical; - u32 n_alloc_len; - u32 n_data_len; - u32 n_offset; +struct vcd_frame_data { + void *virt_addr; + phys_addr_t phys_addr; + size_t alloc_len; + size_t data_len; + size_t offset; s64 time_stamp; - u32 n_flags; - u32 n_frm_clnt_data; - struct vcd_property_dec_output_buffer_type dec_op_prop; - u32 b_interlaced; - enum vcd_frame_type e_frame_type; - u32 n_ip_frm_tag; + u32 flags; + void *client_data; + struct vcd_property_dec_output_buffer dec_op_prop; + u32 interlaced; + enum vcd_frame frame_type; + u32 ip_frm_tag; }; -struct vcd_sequence_hdr_type { - u8 *p_sequence_header; - u32 n_sequence_header_len; +struct vcd_phys_sequence_hdr { + phys_addr_t addr; + size_t sz; +}; +struct vcd_sequence_hdr { + void *addr; + size_t sz; }; enum vcd_buffer_type { @@ -91,60 +95,58 @@ enum vcd_buffer_type { VCD_BUFFER_32BIT = 0x7FFFFFFF }; -struct vcd_buffer_requirement_type { - u32 n_min_count; - u32 n_actual_count; - u32 n_max_count; - u32 n_size; - u32 n_align; - u32 n_buf_pool_id; +struct vcd_buffer_requirement { + u32 min_count; + u32 actual_count; + u32 max_count; + size_t size; + u32 align; + u32 buf_pool_id; }; -struct vcd_init_config_type { - void *p_device_name; - void *(*pf_map_dev_base_addr) (void *p_device_name); +struct vcd_init_config { + void *device_name; + void *(*pf_map_dev_base_addr) (void *device_name); void (*pf_un_map_dev_base_addr) (void); void (*pf_interrupt_clr) (void); - void (*pf_register_isr) (void *p_device_name); + void (*pf_register_isr) (void *device_name); void (*pf_deregister_isr) (void); u32 (*pf_timer_create) (void (*pf_timer_handler)(void *), - void *p_user_data, void **pp_timer_handle); - void (*pf_timer_release) (void *p_timer_handle); - void (*pf_timer_start) (void *p_timer_handle, u32 n_time_out); - void (*pf_timer_stop) (void *p_timer_handle); + void *user_data, void **pp_timer_handle); + void (*pf_timer_release) (void *timer_handle); + void (*pf_timer_start) (void *timer_handle, u32 time_out); + void (*pf_timer_stop) (void *timer_handle); }; -u32 vcd_init(struct vcd_init_config_type *p_config, s32 *p_driver_handle); +u32 vcd_init(struct vcd_init_config *config, s32 *driver_handle); u32 vcd_term(s32 driver_handle); -u32 vcd_open(s32 driver_handle, u32 b_decoding, - void (*callback) (u32 event, u32 status, void *p_info, u32 n_size, - void *handle, void *const p_client_data), void *p_client_data); +u32 vcd_open(s32 driver_handle, u32 decoding, + void (*callback) (u32 event, u32 status, void *info, u32 size, + void *handle, void *const client_data), void *client_data); u32 vcd_close(void *handle); u32 vcd_encode_start(void *handle); -u32 vcd_encode_frame(void *handle, struct vcd_frame_data_type *p_input_frame); -u32 vcd_decode_start(void *handle, struct vcd_sequence_hdr_type *p_seq_hdr); -u32 vcd_decode_frame(void *handle, struct vcd_frame_data_type *p_input_frame); +u32 vcd_encode_frame(void *handle, struct vcd_frame_data *input_frame); +u32 vcd_decode_start(void *handle, struct vcd_sequence_hdr *seq_hdr); +u32 vcd_decode_frame(void *handle, struct vcd_frame_data *input_frame); u32 vcd_pause(void *handle); u32 vcd_resume(void *handle); -u32 vcd_flush(void *handle, u32 n_mode); +u32 vcd_flush(void *handle, u32 mode); u32 vcd_stop(void *handle); -u32 vcd_set_property(void *handle, struct vcd_property_hdr_type *p_prop_hdr, - void *p_prop_val); -u32 vcd_get_property(void *handle, struct vcd_property_hdr_type *p_prop_hdr, - void *p_prop_val); -u32 vcd_set_buffer_requirements(void *handle, enum vcd_buffer_type e_buffer, - struct vcd_buffer_requirement_type *p_buffer_req); -u32 vcd_get_buffer_requirements(void *handle, enum vcd_buffer_type e_buffer, - struct vcd_buffer_requirement_type *p_buffer_req); -u32 vcd_set_buffer(void *handle, enum vcd_buffer_type e_buffer, - u8 *p_buffer, u32 n_buf_size); -u32 vcd_allocate_buffer(void *handle, enum vcd_buffer_type e_buffer, - u32 n_buf_size, u8 **pp_vir_buf_addr, u8 **pp_phy_buf_addr); - -u32 vcd_free_buffer(void *handle, enum vcd_buffer_type e_buffer, u8 *p_buffer); -u32 vcd_fill_output_buffer(void *handle, struct vcd_frame_data_type *p_buffer); -u32 vcd_set_device_power(s32 driver_handle, - enum vcd_power_state_type e_pwr_state); +u32 vcd_set_property(void *handle, struct vcd_property_hdr *prop_hdr, + void *prop_val); +u32 vcd_get_property(void *handle, struct vcd_property_hdr *prop_hdr, + void *prop_val); +u32 vcd_set_buffer_requirements(void *handle, enum vcd_buffer_type buffer_type, + struct vcd_buffer_requirement *buffer_req); +u32 vcd_get_buffer_requirements(void *handle, enum vcd_buffer_type buffer_type, + struct vcd_buffer_requirement *buffer_req); +u32 vcd_set_buffer(void *handle, enum vcd_buffer_type buffer_type, + void *buffer, size_t buf_size); +u32 vcd_allocate_buffer(void *handle, enum vcd_buffer_type buffer_type, + size_t sz, void **virt_addr, phys_addr_t *phys_addr); +u32 vcd_free_buffer(void *handle, enum vcd_buffer_type buffer_type, void *buf); +u32 vcd_fill_output_buffer(void *handle, struct vcd_frame_data *buffer); +u32 vcd_set_device_power(s32 driver_handle, enum vcd_power_state pwr_state); void vcd_read_and_clear_interrupt(void); void vcd_response_handler(void); diff --git a/drivers/misc/video_core/720p/vcd/vcd_client_sm.c b/drivers/misc/video_core/720p/vcd/vcd_client_sm.c index f1895161a6013..4617f8079539f 100644 --- a/drivers/misc/video_core/720p/vcd/vcd_client_sm.c +++ b/drivers/misc/video_core/720p/vcd/vcd_client_sm.c @@ -19,1744 +19,1473 @@ #include "video_core_type.h" #include "vcd.h" -static const struct vcd_clnt_state_table_type_t *vcd_clnt_state_table[]; +static const struct vcd_clnt_state_table *vcd_clnt_state_table[]; -void vcd_clnt_handle_device_err_fatal(struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 e_event) +void vcd_clnt_handle_device_err_fatal(struct vcd_clnt_ctxt *cctxt, u32 event) { - if (p_cctxt->clnt_state.e_state != VCD_CLIENT_STATE_INVALID) { - p_cctxt->callback(e_event, VCD_ERR_HW_FATAL, NULL, 0, - p_cctxt, p_cctxt->p_client_data); - vcd_flush_buffers_in_err_fatal(p_cctxt); - vcd_do_client_state_transition(p_cctxt, + if (cctxt->clnt_state.state != VCD_CLIENT_STATE_INVALID) { + cctxt->callback(event, VCD_ERR_HW_FATAL, NULL, 0, + cctxt, cctxt->client_data); + vcd_flush_buffers_in_err_fatal(cctxt); + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_INVALID, CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); } } -static u32 vcd_close_in_open(struct vcd_clnt_ctxt_type_t *p_cctxt) +static u32 vcd_close_in_open(struct vcd_clnt_ctxt *cctxt) { u32 rc = VCD_S_SUCCESS; VCD_MSG_LOW("vcd_close_in_open:"); - if (p_cctxt->in_buf_pool.n_allocated || - p_cctxt->out_buf_pool.n_allocated) { - VCD_MSG_ERROR("\n Allocated buffers are not freed yet"); + if (cctxt->in_buf_pool.allocated || cctxt->out_buf_pool.allocated) { + VCD_MSG_ERROR("Allocated buffers are not freed yet\n"); return VCD_ERR_ILLEGAL_OP; } - vcd_destroy_client_context(p_cctxt); + vcd_destroy_client_context(cctxt); return rc; } -static u32 vcd_close_in_invalid(struct vcd_clnt_ctxt_type_t *p_cctxt) +static u32 vcd_close_in_invalid(struct vcd_clnt_ctxt *cctxt) { - VCD_MSG_LOW("vcd_close_in_invalid:"); - if (p_cctxt->in_buf_pool.n_allocated || - p_cctxt->out_buf_pool.n_allocated){ - VCD_MSG_ERROR("Allocated buffers are not freed yet"); + VCD_MSG_LOW("vcd_close_in_invalid:\n"); + if (cctxt->in_buf_pool.allocated || cctxt->out_buf_pool.allocated) { + VCD_MSG_ERROR("Allocated buffers are not freed yet\n"); return VCD_ERR_ILLEGAL_OP; } - if (p_cctxt->status.b_cleaning_up) - p_cctxt->status.b_close_pending = TRUE; + if (cctxt->status.cleaning_up) + cctxt->status.close_pending = true; else - vcd_destroy_client_context(p_cctxt); + vcd_destroy_client_context(cctxt); return VCD_S_SUCCESS; } -static u32 vcd_start_in_run_cmn(struct vcd_clnt_ctxt_type_t *p_cctxt) +static u32 vcd_start_in_run_cmn(struct vcd_clnt_ctxt *cctxt) { - VCD_MSG_LOW("vcd_start_in_run_cmn:"); - p_cctxt->callback(VCD_EVT_RESP_START, VCD_S_SUCCESS, NULL, 0, - p_cctxt, p_cctxt->p_client_data); + VCD_MSG_LOW("vcd_start_in_run_cmn:\n"); + cctxt->callback(VCD_EVT_RESP_START, VCD_S_SUCCESS, NULL, 0, cctxt, + cctxt->client_data); return VCD_S_SUCCESS; - } -static u32 vcd_encode_start_in_open(struct vcd_clnt_ctxt_type_t *p_cctxt) +static u32 vcd_encode_start_in_open(struct vcd_clnt_ctxt *cctxt) { u32 rc = VCD_S_SUCCESS; - struct vcd_property_hdr_type prop_hdr; - struct vcd_property_vop_timing_type timing; + struct vcd_property_hdr prop_hdr; + struct vcd_property_vop_timing timing; - VCD_MSG_LOW("vcd_encode_start_in_open:"); + VCD_MSG_LOW("vcd_encode_start_in_open:\n"); - if (p_cctxt->b_decoding) { - VCD_MSG_ERROR("vcd_encode_init for decoder client"); + if (cctxt->decoding) { + VCD_MSG_ERROR("vcd_encode_init for decoder client\n"); return VCD_ERR_ILLEGAL_OP; } - if (!p_cctxt->in_buf_pool.a_entries || - !p_cctxt->out_buf_pool.a_entries || - p_cctxt->in_buf_pool.n_validated != p_cctxt->in_buf_pool.n_count || - p_cctxt->out_buf_pool.n_validated != - p_cctxt->out_buf_pool.n_count) { - VCD_MSG_ERROR("Buffer pool is not completely setup yet"); + if (!cctxt->in_buf_pool.entries || !cctxt->out_buf_pool.entries || + cctxt->in_buf_pool.validated != + cctxt->in_buf_pool.count || + cctxt->out_buf_pool.validated != + cctxt->out_buf_pool.count) { + VCD_MSG_ERROR("Buffer pool is not completely setup yet\n"); return VCD_ERR_BAD_STATE; } - rc = vcd_add_client_to_sched(p_cctxt); + rc = vcd_add_client_to_sched(cctxt); - VCD_FAILED_RETURN(rc, "Failed: vcd_add_client_to_sched"); + VCD_FAILED_RETURN(rc, "Failed: vcd_add_client_to_sched\n"); - prop_hdr.prop_id = VCD_I_VOP_TIMING; - prop_hdr.n_size = sizeof(struct vcd_property_vop_timing_type); - rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, &timing); + prop_hdr.id = VCD_I_VOP_TIMING; + prop_hdr.sz = sizeof(struct vcd_property_vop_timing); + rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &timing); - VCD_FAILED_RETURN(rc, "Failed: Get VCD_I_VOP_TIMING"); - if (!timing.n_vop_time_resolution) { - VCD_MSG_ERROR("Vop_time_resolution value is zero"); + VCD_FAILED_RETURN(rc, "Failed: Get VCD_I_VOP_TIMING\n"); + if (!timing.vop_time_resolution) { + VCD_MSG_ERROR("Vop_time_resolution value is zero\n"); return VCD_ERR_FAIL; } - p_cctxt->n_time_resoln = timing.n_vop_time_resolution; + cctxt->time_resoln = timing.vop_time_resolution; - rc = vcd_process_cmd_sess_start(p_cctxt); + rc = vcd_process_cmd_sess_start(cctxt); if (!VCD_FAILED(rc)) { - vcd_do_client_state_transition(p_cctxt, - VCD_CLIENT_STATE_STARTING, - CLIENT_STATE_EVENT_NUMBER - (pf_encode_start)); + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_STARTING, + CLIENT_STATE_EVENT_NUMBER(pf_encode_start)); } return rc; } -static u32 vcd_encode_start_in_run(struct vcd_clnt_ctxt_type_t - *p_cctxt) +static u32 vcd_encode_start_in_run(struct vcd_clnt_ctxt *cctxt) { - VCD_MSG_LOW("vcd_encode_start_in_run:"); - (void) vcd_start_in_run_cmn(p_cctxt); + VCD_MSG_LOW("vcd_encode_start_in_run:\n"); + vcd_start_in_run_cmn(cctxt); return VCD_S_SUCCESS; } -static u32 vcd_encode_frame_cmn(struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_input_frame) +static u32 vcd_encode_frame_cmn(struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *input_frame) { - VCD_MSG_LOW("vcd_encode_frame_cmn in %d:", p_cctxt->clnt_state.e_state); + VCD_MSG_LOW("vcd_encode_frame_cmn in %d:", cctxt->clnt_state.state); - if (p_cctxt->b_decoding) { - VCD_MSG_ERROR("vcd_encode_frame for decoder client"); + if (cctxt->decoding) { + VCD_MSG_ERROR("vcd_encode_frame for decoder client\n"); return VCD_ERR_ILLEGAL_OP; } - return vcd_handle_input_frame(p_cctxt, p_input_frame); + return vcd_handle_input_frame(cctxt, input_frame); } -static u32 vcd_decode_start_in_open - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_sequence_hdr_type *p_seq_hdr) +static u32 vcd_decode_start_in_open(struct vcd_clnt_ctxt *cctxt, + struct vcd_sequence_hdr *seq_hdr) { u32 rc = VCD_S_SUCCESS; - VCD_MSG_LOW("vcd_decode_start_in_open:"); + VCD_MSG_LOW("vcd_decode_start_in_open:\n"); - if (!p_cctxt->b_decoding) { - VCD_MSG_ERROR("vcd_decode_init for encoder client"); + if (!cctxt->decoding) { + VCD_MSG_ERROR("vcd_decode_init for encoder client\n"); return VCD_ERR_ILLEGAL_OP; } - if (p_seq_hdr) { - VCD_MSG_HIGH("Seq hdr supplied. len = %d", - p_seq_hdr->n_sequence_header_len); - - rc = vcd_store_seq_hdr(p_cctxt, p_seq_hdr); + if (seq_hdr) { + VCD_MSG_HIGH("Seq hdr supplied. len = %d\n", seq_hdr->sz); + rc = vcd_store_seq_hdr(cctxt, seq_hdr); } else { - VCD_MSG_HIGH("Seq hdr not supplied"); - - p_cctxt->seq_hdr.n_sequence_header_len = 0; - p_cctxt->seq_hdr.p_sequence_header = NULL; + VCD_MSG_HIGH("Seq hdr not supplied\n"); + cctxt->seq_hdr.sz = 0; + cctxt->seq_hdr.addr = NULL; } - VCD_FAILED_RETURN(rc, "Err processing seq hdr"); + VCD_FAILED_RETURN(rc, "Err processing seq hdr\n"); - rc = vcd_process_cmd_sess_start(p_cctxt); + rc = vcd_process_cmd_sess_start(cctxt); if (!VCD_FAILED(rc)) { - vcd_do_client_state_transition(p_cctxt, - VCD_CLIENT_STATE_STARTING, - CLIENT_STATE_EVENT_NUMBER - (pf_decode_start)); + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_STARTING, + CLIENT_STATE_EVENT_NUMBER(pf_decode_start)); } return rc; } -static u32 vcd_decode_start_in_run(struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_sequence_hdr_type *p_seqhdr) +static u32 vcd_decode_start_in_run(struct vcd_clnt_ctxt *cctxt, + struct vcd_sequence_hdr *seqhdr) { - VCD_MSG_LOW("vcd_decode_start_in_run:"); - (void) vcd_start_in_run_cmn(p_cctxt); - return VCD_S_SUCCESS; + VCD_MSG_LOW("vcd_decode_start_in_run:\n"); + vcd_start_in_run_cmn(cctxt); + return VCD_S_SUCCESS; } -static u32 vcd_decode_frame_cmn - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_input_frame) +static u32 vcd_decode_frame_cmn(struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *input_frame) { - VCD_MSG_LOW("vcd_decode_frame_cmn in %d:", p_cctxt->clnt_state.e_state); + VCD_MSG_LOW("vcd_decode_frame_cmn in %d:\n", cctxt->clnt_state.state); - if (!p_cctxt->b_decoding) { - VCD_MSG_ERROR("Decode_frame api called for Encoder client"); + if (!cctxt->decoding) { + VCD_MSG_ERROR("Decode_frame api called for Encoder client\n"); return VCD_ERR_ILLEGAL_OP; } - return vcd_handle_input_frame(p_cctxt, p_input_frame); + return vcd_handle_input_frame(cctxt, input_frame); } -static u32 vcd_pause_in_run(struct vcd_clnt_ctxt_type_t *p_cctxt) +static u32 vcd_pause_in_run(struct vcd_clnt_ctxt *cctxt) { u32 rc = VCD_S_SUCCESS; - VCD_MSG_LOW("vcd_pause_in_run:"); + VCD_MSG_LOW("vcd_pause_in_run:\n"); - if (p_cctxt->b_sched_clnt_valid) { - rc = vcd_map_sched_status(sched_suspend_resume_client - (p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, FALSE)); + if (cctxt->sched_clnt_valid) { + rc = vcd_map_sched_status(sched_suspend_resume_client( + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, + false)); } - VCD_FAILED_RETURN(rc, "Failed: sched_suspend_resume_client"); + VCD_FAILED_RETURN(rc, "Failed: sched_suspend_resume_client\n"); - if (p_cctxt->status.n_frame_submitted > 0) { - vcd_do_client_state_transition(p_cctxt, - VCD_CLIENT_STATE_PAUSING, - CLIENT_STATE_EVENT_NUMBER - (pf_pause)); + if (cctxt->status.frame_submitted > 0) { + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_PAUSING, + CLIENT_STATE_EVENT_NUMBER(pf_pause)); } else { - VCD_MSG_HIGH("No client frames are currently being processed"); + VCD_MSG_HIGH("No client frames are currently being processed\n"); - vcd_do_client_state_transition(p_cctxt, - VCD_CLIENT_STATE_PAUSED, - CLIENT_STATE_EVENT_NUMBER - (pf_pause)); + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_PAUSED, + CLIENT_STATE_EVENT_NUMBER(pf_pause)); - p_cctxt->callback(VCD_EVT_RESP_PAUSE, - VCD_S_SUCCESS, - NULL, 0, p_cctxt, p_cctxt->p_client_data); + cctxt->callback(VCD_EVT_RESP_PAUSE, VCD_S_SUCCESS, NULL, 0, + cctxt, cctxt->client_data); - rc = vcd_power_event(p_cctxt->p_dev_ctxt, p_cctxt, - VCD_EVT_PWR_CLNT_PAUSE); + rc = vcd_power_event(cctxt->dev_ctxt, cctxt, + VCD_EVT_PWR_CLNT_PAUSE); if (VCD_FAILED(rc)) - VCD_MSG_ERROR("VCD_EVT_PWR_CLNT_PAUSE_END failed"); + VCD_MSG_ERROR("VCD_EVT_PWR_CLNT_PAUSE_END failed\n"); } return VCD_S_SUCCESS; } -static u32 vcd_resume_in_paused(struct vcd_clnt_ctxt_type_t *p_cctxt) +static u32 vcd_resume_in_paused(struct vcd_clnt_ctxt *cctxt) { - struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt; u32 rc = VCD_S_SUCCESS; - VCD_MSG_LOW("vcd_resume_in_paused:"); + VCD_MSG_LOW("vcd_resume_in_paused:\n"); - if (p_cctxt->b_sched_clnt_valid) { + if (cctxt->sched_clnt_valid) { - rc = vcd_power_event(p_cctxt->p_dev_ctxt, - p_cctxt, VCD_EVT_PWR_CLNT_RESUME); + rc = vcd_power_event(cctxt->dev_ctxt, cctxt, + VCD_EVT_PWR_CLNT_RESUME); if (VCD_FAILED(rc)) { - VCD_MSG_ERROR("VCD_EVT_PWR_CLNT_RESUME failed"); + VCD_MSG_ERROR("VCD_EVT_PWR_CLNT_RESUME failed\n"); } else { - - rc = vcd_map_sched_status(sched_suspend_resume_client - (p_cctxt->p_dev_ctxt-> - sched_hdl, - p_cctxt->sched_clnt_hdl, - TRUE)); + rc = vcd_map_sched_status(sched_suspend_resume_client( + cctxt->dev_ctxt->sched_hdl, + cctxt->sched_clnt_hdl, true)); if (VCD_FAILED(rc)) { - VCD_MSG_ERROR - ("rc = 0x%x. Failed: " - "sched_suspend_resume_client", - rc); + VCD_MSG_ERROR("rc = 0x%x. Failed: " + "sched_suspend_resume_client\n", rc); } } if (!VCD_FAILED(rc)) - vcd_try_submit_frame(p_dev_ctxt); + vcd_try_submit_frame(dev_ctxt); } if (!VCD_FAILED(rc)) { - vcd_do_client_state_transition(p_cctxt, - VCD_CLIENT_STATE_RUN, - CLIENT_STATE_EVENT_NUMBER - (pf_resume)); + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_RUN, + CLIENT_STATE_EVENT_NUMBER(pf_resume)); } return rc; } -static u32 vcd_flush_cmn(struct vcd_clnt_ctxt_type_t *p_cctxt, u32 n_mode) +static u32 vcd_flush_cmn(struct vcd_clnt_ctxt *cctxt, u32 mode) { u32 rc = VCD_S_SUCCESS; - VCD_MSG_LOW("vcd_flush_cmn in %d:", p_cctxt->clnt_state.e_state); + VCD_MSG_LOW("vcd_flush_cmn in %d:\n", cctxt->clnt_state.state); - rc = vcd_flush_buffers(p_cctxt, n_mode); + rc = vcd_flush_buffers(cctxt, mode); - VCD_FAILED_RETURN(rc, "Failed: vcd_flush_buffers"); + VCD_FAILED_RETURN(rc, "Failed: vcd_flush_buffers\n"); - if (p_cctxt->status.n_frame_submitted > 0) { - vcd_do_client_state_transition(p_cctxt, - VCD_CLIENT_STATE_FLUSHING, - CLIENT_STATE_EVENT_NUMBER - (pf_flush)); + if (cctxt->status.frame_submitted > 0) { + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_FLUSHING, + CLIENT_STATE_EVENT_NUMBER(pf_flush)); } else { - VCD_MSG_HIGH("All buffers are flushed"); - p_cctxt->status.n_flush_mode = n_mode; - vcd_send_flush_done(p_cctxt, VCD_S_SUCCESS); + VCD_MSG_HIGH("All buffers are flushed\n"); + cctxt->status.flush_mode = mode; + vcd_send_flush_done(cctxt, VCD_S_SUCCESS); } return rc; } -static u32 vcd_flush_inopen(struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 n_mode) +static u32 vcd_flush_inopen(struct vcd_clnt_ctxt *cctxt, u32 mode) { - VCD_MSG_LOW("vcd_flush_inopen:"); - p_cctxt->status.n_flush_mode = n_mode; - vcd_send_flush_done(p_cctxt, VCD_S_SUCCESS); - return VCD_S_SUCCESS; + VCD_MSG_LOW("vcd_flush_inopen:\n"); + cctxt->status.flush_mode = mode; + vcd_send_flush_done(cctxt, VCD_S_SUCCESS); + return VCD_S_SUCCESS; } -static u32 vcd_flush_in_flushing - (struct vcd_clnt_ctxt_type_t *p_cctxt, u32 n_mode) +static u32 vcd_flush_in_flushing(struct vcd_clnt_ctxt *cctxt, u32 mode) { u32 rc = VCD_S_SUCCESS; - VCD_MSG_LOW("vcd_flush_in_flushing:"); + VCD_MSG_LOW("vcd_flush_in_flushing:\n"); - rc = vcd_flush_buffers(p_cctxt, n_mode); + rc = vcd_flush_buffers(cctxt, mode); return rc; } -static u32 vcd_flush_in_eos(struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 n_mode) +static u32 vcd_flush_in_eos(struct vcd_clnt_ctxt *cctxt, u32 mode) { - VCD_MSG_LOW("vcd_flush_in_eos:"); + VCD_MSG_LOW("vcd_flush_in_eos:\n"); - if (n_mode > VCD_FLUSH_ALL || !n_mode) { - VCD_MSG_ERROR("Invalid flush mode %d", n_mode); + if (mode > VCD_FLUSH_ALL || !mode) { + VCD_MSG_ERROR("Invalid flush mode %d\n", mode); return VCD_ERR_ILLEGAL_PARM; } - VCD_MSG_MED("Flush mode requested %d", n_mode); + VCD_MSG_MED("Flush mode requested %d\n", mode); - p_cctxt->status.n_flush_mode |= n_mode; + cctxt->status.flush_mode |= mode; return VCD_S_SUCCESS; } -static u32 vcd_flush_in_invalid(struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 mode) +static u32 vcd_flush_in_invalid(struct vcd_clnt_ctxt *cctxt, u32 mode) { u32 rc = VCD_S_SUCCESS; - VCD_MSG_LOW("vcd_flush_in_invalid:"); - if (!p_cctxt->status.b_cleaning_up) { - rc = vcd_flush_buffers(p_cctxt, mode); + VCD_MSG_LOW("vcd_flush_in_invalid:\n"); + if (!cctxt->status.cleaning_up) { + rc = vcd_flush_buffers(cctxt, mode); if (!VCD_FAILED(rc)) { - VCD_MSG_HIGH("All buffers are flushed"); - p_cctxt->status.n_flush_mode = mode; - vcd_send_flush_done(p_cctxt, VCD_S_SUCCESS); + VCD_MSG_HIGH("All buffers are flushed\n"); + cctxt->status.flush_mode = mode; + vcd_send_flush_done(cctxt, VCD_S_SUCCESS); } } return rc; } -static u32 vcd_stop_cmn(struct vcd_clnt_ctxt_type_t *p_cctxt) +static u32 vcd_stop_cmn(struct vcd_clnt_ctxt *cctxt) { - struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt; u32 rc = VCD_S_SUCCESS; - struct vcd_transc_type *p_transc; + struct vcd_transc *transc; - VCD_MSG_LOW("vcd_stop_cmn in %d:", p_cctxt->clnt_state.e_state); + VCD_MSG_LOW("vcd_stop_cmn in %d:\n", cctxt->clnt_state.state); - rc = vcd_flush_buffers(p_cctxt, VCD_FLUSH_ALL); + rc = vcd_flush_buffers(cctxt, VCD_FLUSH_ALL); - VCD_FAILED_RETURN(rc, "Failed: vcd_flush_buffers"); + VCD_FAILED_RETURN(rc, "Failed: vcd_flush_buffers\n"); - if (!p_cctxt->status.n_frame_submitted) { + if (!cctxt->status.frame_submitted) { - if (vcd_get_command_channel(p_dev_ctxt, &p_transc)) { - rc = vcd_power_event(p_dev_ctxt, p_cctxt, + if (vcd_get_command_channel(dev_ctxt, &transc)) { + rc = vcd_power_event(dev_ctxt, cctxt, VCD_EVT_PWR_CLNT_CMD_BEGIN); if (!VCD_FAILED(rc)) { - p_transc->e_type = VCD_CMD_CODEC_STOP; - p_transc->p_cctxt = p_cctxt; + transc->type = VCD_CMD_CODEC_STOP; + transc->cctxt = cctxt; - rc = vcd_submit_cmd_sess_end(p_transc); + rc = vcd_submit_cmd_sess_end(transc); } else { VCD_MSG_ERROR("Failed:" - " VCD_EVT_PWR_CLNT_CMD_BEGIN"); + " VCD_EVT_PWR_CLNT_CMD_BEGIN\n"); } - if (VCD_FAILED(rc)) { - vcd_release_command_channel(p_dev_ctxt, - p_transc); - } + if (VCD_FAILED(rc)) + vcd_release_command_channel(dev_ctxt, transc); } else { - vcd_client_cmd_flush_and_en_q(p_cctxt, - VCD_CMD_CODEC_STOP); + vcd_client_cmd_flush_and_en_q(cctxt, + VCD_CMD_CODEC_STOP); } } if (VCD_FAILED(rc)) { - (void)vcd_power_event(p_dev_ctxt, p_cctxt, - VCD_EVT_PWR_CLNT_CMD_FAIL); + vcd_power_event(dev_ctxt, cctxt, VCD_EVT_PWR_CLNT_CMD_FAIL); } else { - vcd_do_client_state_transition(p_cctxt, - VCD_CLIENT_STATE_STOPPING, - CLIENT_STATE_EVENT_NUMBER - (pf_stop)); + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_STOPPING, + CLIENT_STATE_EVENT_NUMBER(pf_stop)); } return rc; } -static u32 vcd_stop_inopen(struct vcd_clnt_ctxt_type_t *p_cctxt) +static u32 vcd_stop_inopen(struct vcd_clnt_ctxt *cctxt) { - VCD_MSG_LOW("vcd_stop_inopen:"); + VCD_MSG_LOW("vcd_stop_inopen:\n"); - p_cctxt->callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, - NULL, 0, p_cctxt, - p_cctxt->p_client_data); + cctxt->callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, NULL, 0, cctxt, + cctxt->client_data); return VCD_S_SUCCESS; } -static u32 vcd_stop_in_run(struct vcd_clnt_ctxt_type_t *p_cctxt) +static u32 vcd_stop_in_run(struct vcd_clnt_ctxt *cctxt) { u32 rc = VCD_S_SUCCESS; - VCD_MSG_LOW("vcd_stop_in_run:"); + VCD_MSG_LOW("vcd_stop_in_run:\n"); - rc = vcd_stop_cmn(p_cctxt); + rc = vcd_stop_cmn(cctxt); - if (!VCD_FAILED(rc) && p_cctxt->status.b1st_frame_recvd) { - rc = vcd_power_event(p_cctxt->p_dev_ctxt, - p_cctxt, VCD_EVT_PWR_CLNT_LAST_FRAME); + if (!VCD_FAILED(rc) && cctxt->status.b1st_frame_recvd) { + rc = vcd_power_event(cctxt->dev_ctxt, cctxt, + VCD_EVT_PWR_CLNT_LAST_FRAME); } return rc; } -static u32 vcd_stop_in_eos(struct vcd_clnt_ctxt_type_t *p_cctxt) +static u32 vcd_stop_in_eos(struct vcd_clnt_ctxt *cctxt) { u32 rc = VCD_S_SUCCESS; - VCD_MSG_LOW("vcd_stop_in_eos:"); + VCD_MSG_LOW("vcd_stop_in_eos:\n"); - p_cctxt->status.b_stop_pending = TRUE; + cctxt->status.stop_pending = true; return rc; } -static u32 vcd_stop_in_invalid(struct vcd_clnt_ctxt_type_t *p_cctxt) +static u32 vcd_stop_in_invalid(struct vcd_clnt_ctxt *cctxt) { - VCD_MSG_LOW("vcd_stop_in_invalid:"); - if (p_cctxt->status.b_cleaning_up) { - p_cctxt->status.b_stop_pending = TRUE; + VCD_MSG_LOW("vcd_stop_in_invalid:\n"); + if (cctxt->status.cleaning_up) { + cctxt->status.stop_pending = true; } else { - (void) vcd_flush_buffers(p_cctxt, VCD_FLUSH_ALL); - p_cctxt->callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, NULL, - 0, p_cctxt, p_cctxt->p_client_data); + vcd_flush_buffers(cctxt, VCD_FLUSH_ALL); + cctxt->callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, NULL, 0, + cctxt, cctxt->client_data); } return VCD_S_SUCCESS; } -static u32 vcd_set_property_cmn - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_property_hdr_type *p_prop_hdr, void *p_prop_val) +static u32 vcd_set_property_cmn(struct vcd_clnt_ctxt *cctxt, + struct vcd_property_hdr *prop_hdr, void *prop_val) { u32 rc; - VCD_MSG_LOW("vcd_set_property_cmn in %d:", p_cctxt->clnt_state.e_state); - VCD_MSG_LOW("property Id = %d", p_prop_hdr->prop_id); + VCD_MSG_LOW("vcd_set_property_cmn in %d:\n", cctxt->clnt_state.state); + VCD_MSG_LOW("property Id = %d\n", prop_hdr->id); - if (!p_prop_hdr->n_size || !p_prop_hdr->prop_id) { - VCD_MSG_MED("Bad parameters"); + if (!prop_hdr->sz || !prop_hdr->id) { + VCD_MSG_MED("Bad parameters\n"); return VCD_ERR_ILLEGAL_PARM; } - rc = ddl_set_property(p_cctxt->ddl_handle, p_prop_hdr, p_prop_val); + rc = ddl_set_property(cctxt->ddl_handle, prop_hdr, prop_val); - VCD_FAILED_RETURN(rc, "Failed: ddl_set_property"); - - switch (p_prop_hdr->prop_id) { + VCD_FAILED_RETURN(rc, "Failed: ddl_set_property\n"); + switch (prop_hdr->id) { case VCD_I_LIVE: - { - struct vcd_property_live_type *p_live = - (struct vcd_property_live_type *)p_prop_val; - - p_cctxt->b_live = p_live->b_live; - - break; - } - + { + struct vcd_property_live *live = (struct vcd_property_live *) + prop_val; + cctxt->live = live->live; + break; + } case VCD_I_FRAME_RATE: - { - if (p_cctxt->b_sched_clnt_valid) { - rc = vcd_set_frame_rate(p_cctxt, - (struct vcd_property_frame_rate_type *) - p_prop_val); - } - - break; + if (cctxt->sched_clnt_valid) { + rc = vcd_set_frame_rate(cctxt, + (struct vcd_property_frame_rate *)prop_val); } - + break; case VCD_I_FRAME_SIZE: - { - if (p_cctxt->b_sched_clnt_valid) { - rc = vcd_set_frame_size(p_cctxt, - (struct vcd_property_frame_size_type *) - p_prop_val); - } - - break; + if (cctxt->sched_clnt_valid) { + rc = vcd_set_frame_size(cctxt, + (struct vcd_property_frame_size *)prop_val); } - + break; default: - { - break; - } - + break; } return rc; } -static u32 vcd_get_property_cmn - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_property_hdr_type *p_prop_hdr, void *p_prop_val) +static u32 vcd_get_property_cmn(struct vcd_clnt_ctxt *cctxt, + struct vcd_property_hdr *prop_hdr, void *prop_val) { - VCD_MSG_LOW("vcd_get_property_cmn in %d:", p_cctxt->clnt_state.e_state); - VCD_MSG_LOW("property Id = %d", p_prop_hdr->prop_id); - if (!p_prop_hdr->n_size || !p_prop_hdr->prop_id) { - VCD_MSG_MED("Bad parameters"); + VCD_MSG_LOW("vcd_get_property_cmn in %d:\n", cctxt->clnt_state.state); + VCD_MSG_LOW("property id = %d\n", prop_hdr->id); + if (!prop_hdr->sz || !prop_hdr->id) { + VCD_MSG_MED("Bad parameters\n"); return VCD_ERR_ILLEGAL_PARM; } - return ddl_get_property(p_cctxt->ddl_handle, p_prop_hdr, p_prop_val); + return ddl_get_property(cctxt->ddl_handle, prop_hdr, prop_val); } -static u32 vcd_set_buffer_requirements_cmn - (struct vcd_clnt_ctxt_type_t *p_cctxt, - enum vcd_buffer_type e_buffer, - struct vcd_buffer_requirement_type *p_buffer_req) +static u32 vcd_set_buffer_requirements_cmn(struct vcd_clnt_ctxt *cctxt, + enum vcd_buffer_type vcd_buffer_type, + struct vcd_buffer_requirement *buffer_req) { - struct vcd_property_hdr_type Prop_hdr; + struct vcd_property_hdr prop_hdr; u32 rc = VCD_S_SUCCESS; - struct vcd_buffer_pool_type *p_buf_pool; + struct vcd_buffer_pool *buf_pool; - VCD_MSG_LOW("vcd_set_buffer_requirements_cmn in %d:", - p_cctxt->clnt_state.e_state); + VCD_MSG_LOW("vcd_set_buffer_requirements_cmn in %d:\n", + cctxt->clnt_state.state); - if (!p_cctxt->b_decoding && - p_cctxt->clnt_state.e_state != VCD_CLIENT_STATE_OPEN) { - VCD_MSG_ERROR("Bad state (%d) for encoder", - p_cctxt->clnt_state.e_state); + if (!cctxt->decoding && cctxt->clnt_state.state != + VCD_CLIENT_STATE_OPEN) { + VCD_MSG_ERROR("Bad state (%d) for encoder\n", + cctxt->clnt_state.state); return VCD_ERR_BAD_STATE; } - VCD_MSG_MED("Buffer type = %d", e_buffer); + VCD_MSG_MED("Buffer type = %d\n", vcd_buffer_type); - if (e_buffer == VCD_BUFFER_INPUT) { - Prop_hdr.prop_id = DDL_I_INPUT_BUF_REQ; - p_buf_pool = &p_cctxt->in_buf_pool; - } else if (e_buffer == VCD_BUFFER_OUTPUT) { - Prop_hdr.prop_id = DDL_I_OUTPUT_BUF_REQ; - p_buf_pool = &p_cctxt->out_buf_pool; + if (vcd_buffer_type == VCD_BUFFER_INPUT) { + prop_hdr.id = DDL_I_INPUT_BUF_REQ; + buf_pool = &cctxt->in_buf_pool; + } else if (vcd_buffer_type == VCD_BUFFER_OUTPUT) { + prop_hdr.id = DDL_I_OUTPUT_BUF_REQ; + buf_pool = &cctxt->out_buf_pool; } else { rc = VCD_ERR_ILLEGAL_PARM; } - VCD_FAILED_RETURN(rc, "Invalid buffer type provided"); + VCD_FAILED_RETURN(rc, "Invalid buffer type provided\n"); - if (p_buf_pool->n_validated > 0) { - VCD_MSG_ERROR("Need to free allocated buffers"); + if (buf_pool->validated > 0) { + VCD_MSG_ERROR("Need to free allocated buffers\n"); return VCD_ERR_ILLEGAL_OP; } - Prop_hdr.n_size = sizeof(*p_buffer_req); + prop_hdr.sz = sizeof(*buffer_req); - rc = ddl_set_property(p_cctxt->ddl_handle, &Prop_hdr, p_buffer_req); + rc = ddl_set_property(cctxt->ddl_handle, &prop_hdr, buffer_req); - VCD_FAILED_RETURN(rc, "Failed: ddl_set_property"); + VCD_FAILED_RETURN(rc, "Failed: ddl_set_property\n"); - if (p_buf_pool->a_entries) { - VCD_MSG_MED("Resetting buffer requirements"); + if (buf_pool->entries) { + VCD_MSG_MED("Resetting buffer requirements\n"); - vcd_free_buffer_pool_entries(p_buf_pool); + vcd_free_buffer_pool_entries(buf_pool); } - rc = vcd_alloc_buffer_pool_entries(p_buf_pool, p_buffer_req); + rc = vcd_alloc_buffer_pool_entries(buf_pool, buffer_req); return rc; - } -static u32 vcd_get_buffer_requirements_cmn - (struct vcd_clnt_ctxt_type_t *p_cctxt, - enum vcd_buffer_type e_buffer, - struct vcd_buffer_requirement_type *p_buffer_req) +static u32 vcd_get_buffer_requirements_cmn(struct vcd_clnt_ctxt *cctxt, + enum vcd_buffer_type vcd_buffer_type, + struct vcd_buffer_requirement *buffer_req) { - struct vcd_property_hdr_type Prop_hdr; + struct vcd_property_hdr prop_hdr; u32 rc = VCD_S_SUCCESS; - VCD_MSG_LOW("vcd_get_buffer_requirements_cmn in %d:", - p_cctxt->clnt_state.e_state); + VCD_MSG_LOW("vcd_get_buffer_requirements_cmn in %d:\n", + cctxt->clnt_state.state); - VCD_MSG_MED("Buffer type = %d", e_buffer); + VCD_MSG_MED("Buffer type = %d\n", vcd_buffer_type); - if (e_buffer == VCD_BUFFER_INPUT) - Prop_hdr.prop_id = DDL_I_INPUT_BUF_REQ; - else if (e_buffer == VCD_BUFFER_OUTPUT) - Prop_hdr.prop_id = DDL_I_OUTPUT_BUF_REQ; + if (vcd_buffer_type == VCD_BUFFER_INPUT) + prop_hdr.id = DDL_I_INPUT_BUF_REQ; + else if (vcd_buffer_type == VCD_BUFFER_OUTPUT) + prop_hdr.id = DDL_I_OUTPUT_BUF_REQ; else rc = VCD_ERR_ILLEGAL_PARM; - VCD_FAILED_RETURN(rc, "Invalid buffer type provided"); + VCD_FAILED_RETURN(rc, "Invalid buffer type provided\n"); - Prop_hdr.n_size = sizeof(*p_buffer_req); + prop_hdr.sz = sizeof(*buffer_req); - return ddl_get_property(p_cctxt->ddl_handle, &Prop_hdr, p_buffer_req); + return ddl_get_property(cctxt->ddl_handle, &prop_hdr, buffer_req); } -static u32 vcd_set_buffer_cmn - (struct vcd_clnt_ctxt_type_t *p_cctxt, - enum vcd_buffer_type e_buffer, u8 *p_buffer, u32 n_buf_size) +static u32 vcd_set_buffer_cmn(struct vcd_clnt_ctxt *cctxt, + enum vcd_buffer_type vcd_buffer_type, void *buf, size_t sz) { u32 rc; - struct vcd_buffer_pool_type *p_buf_pool; + struct vcd_buffer_pool *buf_pool; - VCD_MSG_LOW("vcd_set_buffer_cmn in %d:", p_cctxt->clnt_state.e_state); + VCD_MSG_LOW("vcd_set_buffer_cmn in %d:\n", cctxt->clnt_state.state); - rc = vcd_common_allocate_set_buffer(p_cctxt, e_buffer, n_buf_size, - &p_buf_pool); + rc = vcd_common_allocate_set_buffer(cctxt, vcd_buffer_type, sz, + &buf_pool); if (!VCD_FAILED(rc)) { - rc = vcd_set_buffer_internal(p_cctxt, p_buf_pool, p_buffer, - n_buf_size); + rc = vcd_set_buffer_internal(cctxt, buf_pool, buf, sz); } return rc; } -static u32 vcd_allocate_buffer_cmn - (struct vcd_clnt_ctxt_type_t *p_cctxt, - enum vcd_buffer_type e_buffer, - u32 n_buf_size, u8 **pp_vir_buf_addr, u8 **pp_phy_buf_addr) +static u32 vcd_allocate_buffer_cmn(struct vcd_clnt_ctxt *cctxt, + enum vcd_buffer_type vcd_buffer_type, size_t sz, void **virt_addr, + phys_addr_t *phys_addr) { u32 rc; - struct vcd_buffer_pool_type *p_buf_pool; + struct vcd_buffer_pool *buf_pool; - VCD_MSG_LOW("vcd_allocate_buffer_cmn in %d:", - p_cctxt->clnt_state.e_state); + VCD_MSG_LOW("vcd_allocate_buffer_cmn in %d:\n", + cctxt->clnt_state.state); - rc = vcd_common_allocate_set_buffer(p_cctxt, e_buffer, n_buf_size, - &p_buf_pool); + rc = vcd_common_allocate_set_buffer(cctxt, vcd_buffer_type, sz, + &buf_pool); if (!VCD_FAILED(rc)) { - rc = vcd_allocate_buffer_internal(p_cctxt, - p_buf_pool, - n_buf_size, - pp_vir_buf_addr, - pp_phy_buf_addr); + rc = vcd_allocate_buffer_internal(cctxt, buf_pool, sz, + virt_addr, phys_addr); } return rc; } -static u32 vcd_free_buffer_cmn - (struct vcd_clnt_ctxt_type_t *p_cctxt, - enum vcd_buffer_type e_buffer, u8 *p_buffer) +static u32 vcd_free_buffer_cmn(struct vcd_clnt_ctxt *cctxt, + enum vcd_buffer_type vcd_buffer_type, void *buf) { + VCD_MSG_LOW("vcd_free_buffer_cmn in %d:\n", cctxt->clnt_state.state); - VCD_MSG_LOW("vcd_free_buffer_cmn in %d:", p_cctxt->clnt_state.e_state); - - return vcd_free_one_buffer_internal(p_cctxt, e_buffer, p_buffer); + return vcd_free_one_buffer_internal(cctxt, vcd_buffer_type, buf); } -static u32 vcd_fill_output_buffer_cmn - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_buffer) +static u32 vcd_fill_output_buffer_cmn(struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *buffer) { u32 rc = VCD_S_SUCCESS; - struct vcd_buffer_entry_type *p_buf_entry; - struct vcd_frame_data_type *p_frm_entry; - u32 b_q_result = TRUE; + struct vcd_buffer_entry *buf_entry; + struct vcd_frame_data *frm_entry; + u32 q_result = true; - VCD_MSG_LOW("vcd_fill_output_buffer_cmn in %d:", - p_cctxt->clnt_state.e_state); + VCD_MSG_LOW("vcd_fill_output_buffer_cmn in %d:\n", + cctxt->clnt_state.state); - p_buf_entry = vcd_check_fill_output_buffer(p_cctxt, p_buffer); - if (!p_buf_entry) + buf_entry = vcd_check_fill_output_buffer(cctxt, buffer); + if (!buf_entry) return VCD_ERR_BAD_POINTER; - b_q_result = - vcd_buffer_pool_entry_en_q(&p_cctxt->out_buf_pool, p_buf_entry); + q_result = vcd_buffer_pool_entry_en_q(&cctxt->out_buf_pool, buf_entry); - if (!b_q_result && !p_cctxt->b_decoding) { - VCD_MSG_ERROR("Failed: vcd_buffer_pool_entry_en_q"); + if (!q_result && !cctxt->decoding) { + VCD_MSG_ERROR("Failed: vcd_buffer_pool_entry_en_q\n"); return VCD_ERR_FAIL; } - p_frm_entry = &p_buf_entry->frame; + frm_entry = &buf_entry->frame; - *p_frm_entry = *p_buffer; - p_frm_entry->p_physical = p_buf_entry->p_physical; - p_frm_entry->n_ip_frm_tag = VCD_FRAMETAG_INVALID; - p_frm_entry->n_data_len = 0; + *frm_entry = *buffer; + frm_entry->phys_addr = buf_entry->phys_addr; + frm_entry->ip_frm_tag = VCD_FRAMETAG_INVALID; + frm_entry->data_len = 0; - if (p_cctxt->b_sched_clnt_valid) { - if (p_cctxt->b_decoding && p_cctxt->status.b1st_frame_recvd) { - struct vcd_property_hdr_type Prop_hdr; - struct ddl_frame_data_type_tag ddl_frm; + if (cctxt->sched_clnt_valid) { + if (cctxt->decoding && cctxt->status.b1st_frame_recvd) { + struct vcd_property_hdr prop_hdr; + struct ddl_frame_data_tag ddl_frm; - Prop_hdr.prop_id = DDL_I_DPB_RELEASE; - Prop_hdr.n_size = - sizeof(struct ddl_frame_data_type_tag); + prop_hdr.id = DDL_I_DPB_RELEASE; + prop_hdr.sz = sizeof(struct ddl_frame_data_tag); memset(&ddl_frm, 0, sizeof(ddl_frm)); - ddl_frm.vcd_frm = *p_frm_entry; - ddl_frm.n_intrlcd_ip_frm_tag = VCD_FRAMETAG_INVALID; + ddl_frm.vcd_frm = *frm_entry; + ddl_frm.intrlcd_ip_frm_tag = VCD_FRAMETAG_INVALID; - rc = ddl_set_property(p_cctxt->ddl_handle, &Prop_hdr, - &ddl_frm); + rc = ddl_set_property(cctxt->ddl_handle, &prop_hdr, + &ddl_frm); if (VCD_FAILED(rc)) { VCD_MSG_ERROR("Error returning output buffer to" - " HW. rc = 0x%x", rc); + " HW. rc = 0x%x\n", rc); - p_buf_entry->b_in_use = FALSE; + buf_entry->in_use = false; } else { - p_cctxt->out_buf_pool.n_in_use++; - p_buf_entry->b_in_use = TRUE; + cctxt->out_buf_pool.in_use++; + buf_entry->in_use = true; } } if (!VCD_FAILED(rc)) { - rc = vcd_map_sched_status(sched_update_client_o_tkn - (p_cctxt->p_dev_ctxt-> - sched_hdl, - p_cctxt->sched_clnt_hdl, - TRUE, - p_cctxt-> - n_sched_o_tkn_per_ip_frm)); + rc = vcd_map_sched_status(sched_update_client_o_tkn( + cctxt->dev_ctxt->sched_hdl, + cctxt->sched_clnt_hdl, true, + cctxt->sched_o_tkn_per_ip_frm)); } if (!VCD_FAILED(rc)) - vcd_try_submit_frame(p_cctxt->p_dev_ctxt); + vcd_try_submit_frame(cctxt->dev_ctxt); } return rc; } -static u32 vcd_fill_output_buffer_in_eos - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_buffer) +static u32 vcd_fill_output_buffer_in_eos(struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *buffer) { u32 rc = VCD_S_SUCCESS; - struct vcd_buffer_entry_type *p_buf_entry; + struct vcd_buffer_entry *buf_entry; - VCD_MSG_LOW("vcd_fill_output_buffer_in_eos:"); + VCD_MSG_LOW("vcd_fill_output_buffer_in_eos:\n"); - p_buf_entry = vcd_check_fill_output_buffer(p_cctxt, p_buffer); - if (!p_buf_entry) + buf_entry = vcd_check_fill_output_buffer(cctxt, buffer); + if (!buf_entry) return VCD_ERR_BAD_POINTER; - if (p_cctxt->status.b_eos_wait_for_op_buf) { - VCD_MSG_HIGH("Got an output buffer we were waiting for"); + if (cctxt->status.eos_wait_for_op_buf) { + VCD_MSG_HIGH("Got an output buffer we were waiting for\n"); - p_buf_entry->frame = *p_buffer; + buf_entry->frame = *buffer; - p_buf_entry->frame.n_data_len = 0; - p_buf_entry->frame.n_flags |= VCD_FRAME_FLAG_EOS; - p_buf_entry->frame.n_ip_frm_tag = - p_cctxt->status.eos_trig_ip_frm.n_ip_frm_tag; - p_buf_entry->frame.time_stamp = - p_cctxt->status.eos_trig_ip_frm.time_stamp; + buf_entry->frame.data_len = 0; + buf_entry->frame.flags |= VCD_FRAME_FLAG_EOS; + buf_entry->frame.ip_frm_tag = + cctxt->status.eos_trig_ip_frm.ip_frm_tag; + buf_entry->frame.time_stamp = + cctxt->status.eos_trig_ip_frm.time_stamp; - p_cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, - VCD_S_SUCCESS, - &p_buf_entry->frame, - sizeof(struct vcd_frame_data_type), - p_cctxt, p_cctxt->p_client_data); + cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS, + &buf_entry->frame, sizeof(struct vcd_frame_data), + cctxt, cctxt->client_data); - p_cctxt->status.b_eos_wait_for_op_buf = FALSE; + cctxt->status.eos_wait_for_op_buf = false; - vcd_do_client_state_transition(p_cctxt, - VCD_CLIENT_STATE_RUN, - CLIENT_STATE_EVENT_NUMBER - (pf_fill_output_buffer)); + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_RUN, + CLIENT_STATE_EVENT_NUMBER(pf_fill_output_buffer)); } else { - rc = vcd_fill_output_buffer_cmn(p_cctxt, p_buffer); + rc = vcd_fill_output_buffer_cmn(cctxt, buffer); } return rc; } -static void vcd_clnt_cb_in_starting - (struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 event, u32 status, void *p_payload, u32 size, - u32 *ddl_handle, void *const p_client_data) +static void vcd_clnt_cb_in_starting(struct vcd_clnt_ctxt *cctxt, u32 event, + u32 status, void *payload, u32 size, u32 *ddl_handle, + void *const client_data) { - struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; - struct vcd_transc_type *p_transc = - (struct vcd_transc_type *)p_client_data; - VCD_MSG_LOW("vcd_clnt_cb_in_starting:"); - if (p_cctxt->ddl_handle != ddl_handle) { - VCD_MSG_ERROR("vcd_clnt_cb_in_initing: Wrong DDL handle %p", + struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt; + struct vcd_transc *transc = (struct vcd_transc *)client_data; + VCD_MSG_LOW("vcd_clnt_cb_in_starting:\n"); + if (cctxt->ddl_handle != ddl_handle) { + VCD_MSG_ERROR("vcd_clnt_cb_in_initing: Wrong DDL handle %p\n", ddl_handle); return; } switch (event) { case VCD_EVT_RESP_START: - { - vcd_handle_start_done(p_cctxt, - (struct vcd_transc_type *)p_client_data, - status); - break; - } + vcd_handle_start_done(cctxt, (struct vcd_transc *)client_data, + status); + break; case VCD_EVT_RESP_STOP: - { - vcd_handle_stop_done_in_starting(p_cctxt, - (struct vcd_transc_type *)p_client_data, - status); - break; - } + vcd_handle_stop_done_in_starting(cctxt, (struct vcd_transc *) + client_data, status); + break; case VCD_EVT_IND_HWERRFATAL: - { - p_cctxt->status.n_cmd_submitted--; - vcd_mark_command_channel(p_cctxt->p_dev_ctxt, p_transc); - vcd_handle_err_fatal(p_cctxt, VCD_EVT_RESP_START, - status); - break; - } + cctxt->status.cmd_submitted--; + vcd_mark_command_channel(cctxt->dev_ctxt, transc); + vcd_handle_err_fatal(cctxt, VCD_EVT_RESP_START, status); + break; default: - { - VCD_MSG_ERROR("Unexpected callback event=%d status=%d " - "from DDL", event, status); - p_dev_ctxt->b_continue = FALSE; - break; - } + VCD_MSG_ERROR("Unexpected callback event=%d status=%d " + "from DDL\n", event, status); + dev_ctxt->cont = false; + break; } } -static void vcd_clnt_cb_in_run - (struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 event, - u32 status, - void *p_payload, u32 n_size, u32 *ddl_handle, void *const p_client_data) +static void vcd_clnt_cb_in_run(struct vcd_clnt_ctxt *cctxt, u32 event, + u32 status, void *payload, u32 size, u32 *ddl_handle, + void *const client_data) { - struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt; u32 rc = VCD_S_SUCCESS; - if (p_cctxt->ddl_handle != ddl_handle) { - VCD_MSG_ERROR("ddl_handle mismatch"); + if (cctxt->ddl_handle != ddl_handle) { + VCD_MSG_ERROR("ddl_handle mismatch\n"); return; } switch (event) { case VCD_EVT_RESP_INPUT_DONE: - { - rc = vcd_handle_input_done(p_cctxt, p_payload, event, - status); - - break; - } - + rc = vcd_handle_input_done(cctxt, payload, event, status); + break; case VCD_EVT_RESP_OUTPUT_DONE: - { - if (!p_cctxt->status.b1st_op_done_recvd) { - if (!VCD_FAILED(status)) { - rc = vcd_handle_first_frame_done - (p_cctxt, p_payload); - - if (VCD_FAILED(rc)) { - VCD_MSG_ERROR - ("rc = 0x%x. Failed: " - "vcd_handle_first_frame_" - "done", rc); - - status = VCD_ERR_FAIL; - } else { - p_cctxt->status. - b1st_op_done_recvd = TRUE; - } + if (!cctxt->status.b1st_op_done_recvd) { + if (!VCD_FAILED(status)) { + rc = vcd_handle_first_frame_done(cctxt, + payload); + + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("rc = 0x%x. Failed: " + "vcd_handle_first_frame_" + "done\n", rc); + status = VCD_ERR_FAIL; + } else { + cctxt->status.b1st_op_done_recvd = true; } } + } - rc = vcd_handle_frame_done(p_cctxt, p_payload, event, - status); + rc = vcd_handle_frame_done(cctxt, payload, event, status); - break; - } + break; case VCD_EVT_RESP_OUTPUT_REQ: - { - rc = vcd_handle_output_required(p_cctxt, p_payload, - status); - break; - } - + rc = vcd_handle_output_required(cctxt, payload, status); + break; case VCD_EVT_IND_RECONFIG: - { - break; - } + break; case VCD_EVT_RESP_TRANSACTION_PENDING: - { - vcd_handle_trans_pending(p_cctxt); - break; - } - + vcd_handle_trans_pending(cctxt); + break; case VCD_EVT_IND_HWERRFATAL: - { - vcd_handle_ind_hw_err_fatal(p_cctxt, - VCD_EVT_IND_HWERRFATAL, status); - break; - } + vcd_handle_ind_hw_err_fatal(cctxt, VCD_EVT_IND_HWERRFATAL, + status); + break; default: - { - VCD_MSG_ERROR - ("Unexpected callback event=%d status=%d from DDL", - event, status); - p_dev_ctxt->b_continue = FALSE; - - break; - } + VCD_MSG_ERROR("Unexpected callback event=%d status=%d from DDL\n", + event, status); + dev_ctxt->cont = false; + break; } - if (!VCD_FAILED(rc) && - (event == VCD_EVT_RESP_INPUT_DONE || - event == VCD_EVT_RESP_OUTPUT_DONE || - event == VCD_EVT_RESP_OUTPUT_REQ)) { + if (!VCD_FAILED(rc) && (event == VCD_EVT_RESP_INPUT_DONE || + event == VCD_EVT_RESP_OUTPUT_DONE || + event == VCD_EVT_RESP_OUTPUT_REQ)) { - if (((struct ddl_frame_data_type_tag *) - p_payload)->b_frm_trans_end) - vcd_mark_frame_channel(p_cctxt->p_dev_ctxt); + if (((struct ddl_frame_data_tag *)payload)->frm_trans_end) + vcd_mark_frame_channel(cctxt->dev_ctxt); } } -static void vcd_clnt_cb_in_eos - (struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 event, - u32 status, - void *p_payload, u32 n_size, u32 *ddl_handle, void *const p_client_data) { - struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; - struct vcd_transc_type *p_transc = NULL; +static void vcd_clnt_cb_in_eos(struct vcd_clnt_ctxt *cctxt, u32 event, + u32 status, void *payload, u32 size, u32 *ddl_handle, + void *const client_data) +{ + struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt; + struct vcd_transc *transc = NULL; - if (p_cctxt->ddl_handle != ddl_handle) { - VCD_MSG_ERROR("ddl_handle mismatch"); + if (cctxt->ddl_handle != ddl_handle) { + VCD_MSG_ERROR("ddl_handle mismatch\n"); return; } switch (event) { case VCD_EVT_RESP_INPUT_DONE: - { - vcd_handle_input_done_in_eos(p_cctxt, p_payload, - status); - - break; - } - + vcd_handle_input_done_in_eos(cctxt, payload, status); + break; case VCD_EVT_RESP_OUTPUT_DONE: - { - vcd_handle_frame_done_in_eos(p_cctxt, p_payload, - status); - - break; - } + vcd_handle_frame_done_in_eos(cctxt, payload, status); + break; case VCD_EVT_RESP_OUTPUT_REQ: - { - (void)vcd_handle_output_required(p_cctxt, p_payload, - status); - break; - } + vcd_handle_output_required(cctxt, payload, status); + break; case VCD_EVT_RESP_EOS_DONE: - { - p_transc = (struct vcd_transc_type *)p_client_data; - - vcd_handle_eos_done(p_cctxt, p_transc, status); - break; - } + transc = (struct vcd_transc *)client_data; + vcd_handle_eos_done(cctxt, transc, status); + break; case VCD_EVT_IND_HWERRFATAL: - { - vcd_handle_ind_hw_err_fatal(p_cctxt, - VCD_EVT_IND_HWERRFATAL, status); - break; - } + vcd_handle_ind_hw_err_fatal(cctxt, VCD_EVT_IND_HWERRFATAL, + status); + break; default: - { - VCD_MSG_ERROR - ("Unexpected callback event=%d status=%d from DDL", - event, status); - - p_dev_ctxt->b_continue = FALSE; - - break; - } + VCD_MSG_ERROR("Unexpected callback event=%d status=%d from DDL\n", + event, status); + dev_ctxt->cont = false; + break; } + if (event == VCD_EVT_RESP_INPUT_DONE || - event == VCD_EVT_RESP_OUTPUT_DONE || - event == VCD_EVT_RESP_OUTPUT_REQ) { - if (p_payload && ((struct ddl_frame_data_type_tag *) - p_payload)->b_frm_trans_end) { - vcd_mark_frame_channel(p_cctxt->p_dev_ctxt); - if (!p_cctxt->status.n_frame_submitted) - vcd_handle_eos_trans_end(p_cctxt); + event == VCD_EVT_RESP_OUTPUT_DONE || + event == VCD_EVT_RESP_OUTPUT_REQ) { + if (payload && ((struct ddl_frame_data_tag *) + payload)->frm_trans_end) { + vcd_mark_frame_channel(cctxt->dev_ctxt); + if (!cctxt->status.frame_submitted) + vcd_handle_eos_trans_end(cctxt); } } } -static void vcd_clnt_cb_in_flushing - (struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 event, - u32 status, - void *p_payload, u32 size, u32 *ddl_handle, void *const p_client_data) { - struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; +static void vcd_clnt_cb_in_flushing(struct vcd_clnt_ctxt *cctxt, u32 event, + u32 status, void *payload, u32 size, u32 *ddl_handle, + void *const client_data) +{ + struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt; u32 rc = VCD_S_SUCCESS; - VCD_MSG_LOW("vcd_clnt_cb_in_flushing:"); - - if (p_cctxt->ddl_handle != ddl_handle) { - VCD_MSG_ERROR("ddl_handle mismatch"); + VCD_MSG_LOW("vcd_clnt_cb_in_flushing:\n"); + if (cctxt->ddl_handle != ddl_handle) { + VCD_MSG_ERROR("ddl_handle mismatch\n"); return; } switch (event) { case VCD_EVT_RESP_INPUT_DONE: - { - rc = vcd_handle_input_done(p_cctxt, - p_payload, - VCD_EVT_RESP_INPUT_FLUSHED, - status); - - break; - } - + rc = vcd_handle_input_done(cctxt, payload, + VCD_EVT_RESP_INPUT_FLUSHED, status); + break; case VCD_EVT_RESP_OUTPUT_DONE: - { - - rc = vcd_handle_frame_done(p_cctxt, - p_payload, - VCD_EVT_RESP_OUTPUT_FLUSHED, - status); - - break; - } + rc = vcd_handle_frame_done(cctxt, payload, + VCD_EVT_RESP_OUTPUT_FLUSHED, status); + break; case VCD_EVT_RESP_OUTPUT_REQ: - { - rc = vcd_handle_output_required_in_flushing(p_cctxt, - p_payload); - break; - } + rc = vcd_handle_output_required_in_flushing(cctxt, payload); + break; case VCD_EVT_IND_HWERRFATAL: - { - vcd_handle_ind_hw_err_fatal(p_cctxt, - VCD_EVT_IND_HWERRFATAL, status); - break; - } + vcd_handle_ind_hw_err_fatal(cctxt, VCD_EVT_IND_HWERRFATAL, + status); + break; default: - { - VCD_MSG_ERROR - ("Unexpected callback event=%d status=%d from DDL", - event, status); - - p_dev_ctxt->b_continue = FALSE; - - break; - } + VCD_MSG_ERROR("Unexpected callback event=%d status=%d from DDL\n", + event, status); + dev_ctxt->cont = false; + break; } - if (!VCD_FAILED(rc) && ((event == VCD_EVT_RESP_INPUT_DONE || - event == VCD_EVT_RESP_OUTPUT_DONE) || - event == VCD_EVT_RESP_OUTPUT_REQ)) { - if (((struct ddl_frame_data_type_tag *)p_payload)-> - b_frm_trans_end) { - - vcd_mark_frame_channel(p_cctxt->p_dev_ctxt); - if (!p_cctxt->status.n_frame_submitted) { - VCD_MSG_HIGH - ("All pending frames recvd from DDL"); + if (!VCD_FAILED(rc) && (event == VCD_EVT_RESP_INPUT_DONE || + event == VCD_EVT_RESP_OUTPUT_DONE || + event == VCD_EVT_RESP_OUTPUT_REQ) && + ((struct ddl_frame_data_tag *)payload)->frm_trans_end) { - if (p_cctxt->status. - n_flush_mode & VCD_FLUSH_OUTPUT) { - vcd_flush_output_buffers(p_cctxt); + vcd_mark_frame_channel(cctxt->dev_ctxt); - vcd_release_all_clnt_frm_transc - (p_cctxt); + if (!cctxt->status.frame_submitted) { + VCD_MSG_HIGH("All pending frames recvd from DDL\n"); - } + if (cctxt->status.flush_mode & VCD_FLUSH_OUTPUT) { + vcd_flush_output_buffers(cctxt); - vcd_send_flush_done(p_cctxt, VCD_S_SUCCESS); - vcd_release_interim_frame_channels(p_dev_ctxt); - VCD_MSG_HIGH("Flush complete"); - vcd_release_all_clnt_def_frm_transc(p_cctxt); - vcd_do_client_state_transition(p_cctxt, - VCD_CLIENT_STATE_RUN, - CLIENT_STATE_EVENT_NUMBER - (pf_clnt_cb)); + vcd_release_all_clnt_frm_transc(cctxt); } + + vcd_send_flush_done(cctxt, VCD_S_SUCCESS); + vcd_release_interim_frame_channels(dev_ctxt); + VCD_MSG_HIGH("Flush complete\n"); + vcd_release_all_clnt_def_frm_transc(cctxt); + vcd_do_client_state_transition(cctxt, + VCD_CLIENT_STATE_RUN, + CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); } } } -static void vcd_clnt_cb_in_stopping - (struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 event, - u32 status, - void *p_payload, u32 n_size, u32 *ddl_handle, void *const p_client_data) { - struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; +static void vcd_clnt_cb_in_stopping(struct vcd_clnt_ctxt *cctxt, u32 event, + u32 status, void *payload, u32 size, u32 *ddl_handle, + void *const client_data) +{ + struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt; u32 rc = VCD_S_SUCCESS; - VCD_MSG_LOW("vcd_clnt_cb_in_stopping:"); - - if (p_cctxt->ddl_handle != ddl_handle) { - VCD_MSG_ERROR("ddl_handle mismatch"); + VCD_MSG_LOW("vcd_clnt_cb_in_stopping:\n"); + if (cctxt->ddl_handle != ddl_handle) { + VCD_MSG_ERROR("ddl_handle mismatch\n"); return; } switch (event) { - case VCD_EVT_RESP_INPUT_DONE: - { - rc = vcd_handle_input_done(p_cctxt, - p_payload, - VCD_EVT_RESP_INPUT_FLUSHED, - status); - - break; - } + rc = vcd_handle_input_done(cctxt, payload, + VCD_EVT_RESP_INPUT_FLUSHED, status); + break; case VCD_EVT_RESP_OUTPUT_DONE: - { - - rc = vcd_handle_frame_done(p_cctxt, - p_payload, - VCD_EVT_RESP_OUTPUT_FLUSHED, - status); + rc = vcd_handle_frame_done(cctxt, payload, + VCD_EVT_RESP_OUTPUT_FLUSHED, status); - break; - } + break; case VCD_EVT_RESP_OUTPUT_REQ: - { - rc = vcd_handle_output_required_in_flushing(p_cctxt, - p_payload); - break; - } + rc = vcd_handle_output_required_in_flushing(cctxt, payload); + break; case VCD_EVT_RESP_STOP: - { - vcd_handle_stop_done(p_cctxt, - (struct vcd_transc_type *) - p_client_data, status); - - break; - } + vcd_handle_stop_done(cctxt, (struct vcd_transc *)client_data, + status); + break; case VCD_EVT_IND_HWERRFATAL: - { - vcd_handle_ind_hw_err_fatal(p_cctxt, VCD_EVT_RESP_STOP, - status); - break; - } - + vcd_handle_ind_hw_err_fatal(cctxt, VCD_EVT_RESP_STOP, status); + break; default: - { - VCD_MSG_ERROR - ("Unexpected callback event=%d status=%d from DDL", - event, status); + VCD_MSG_ERROR("Unexpected callback event=%d status=%d from DDL\n", + event, status); - p_dev_ctxt->b_continue = FALSE; - - break; - } + dev_ctxt->cont = false; + break; } - if (!VCD_FAILED(rc) && ((event == VCD_EVT_RESP_INPUT_DONE || - event == VCD_EVT_RESP_OUTPUT_DONE) || - event == VCD_EVT_RESP_OUTPUT_REQ)) { - - if (((struct ddl_frame_data_type_tag *)p_payload)-> - b_frm_trans_end) { - - vcd_mark_frame_channel(p_cctxt->p_dev_ctxt); + if (!VCD_FAILED(rc) && (event == VCD_EVT_RESP_INPUT_DONE || + event == VCD_EVT_RESP_OUTPUT_DONE || + event == VCD_EVT_RESP_OUTPUT_REQ) && + ((struct ddl_frame_data_tag *)payload)->frm_trans_end) { - if (!p_cctxt->status.n_frame_submitted) { - VCD_MSG_HIGH - ("All pending frames recvd from DDL"); + vcd_mark_frame_channel(cctxt->dev_ctxt); - vcd_flush_output_buffers(p_cctxt); + if (!cctxt->status.frame_submitted) { + VCD_MSG_HIGH("All pending frames recvd from DDL\n"); - p_cctxt->status.n_flush_mode = 0; + vcd_flush_output_buffers(cctxt); - vcd_release_all_clnt_frm_transc(p_cctxt); + cctxt->status.flush_mode = 0; - VCD_MSG_HIGH - ("All buffers flushed. Enqueuing stop cmd"); + vcd_release_all_clnt_frm_transc(cctxt); - vcd_client_cmd_flush_and_en_q(p_cctxt, - VCD_CMD_CODEC_STOP); - } + VCD_MSG_HIGH("All buffers flushed. Enqueuing stop cmd\n"); + vcd_client_cmd_flush_and_en_q(cctxt, + VCD_CMD_CODEC_STOP); } } } -static void vcd_clnt_cb_in_pausing - (struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 event, - u32 status, - void *p_payload, u32 size, u32 *ddl_handle, void *const p_client_data) +static void vcd_clnt_cb_in_pausing(struct vcd_clnt_ctxt *cctxt, u32 event, + u32 status, void *payload, u32 size, u32 *ddl_handle, + void *const client_data) { - struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt; u32 rc = VCD_S_SUCCESS; - VCD_MSG_LOW("vcd_clnt_cb_in_pausing:"); + VCD_MSG_LOW("vcd_clnt_cb_in_pausing:\n"); - if (p_cctxt->ddl_handle != ddl_handle) { - VCD_MSG_ERROR("ddl_handle mismatch"); + if (cctxt->ddl_handle != ddl_handle) { + VCD_MSG_ERROR("ddl_handle mismatch\n"); return; } switch (event) { case VCD_EVT_RESP_INPUT_DONE: - { - rc = vcd_handle_input_done(p_cctxt, p_payload, event, - status); - - break; - } - + rc = vcd_handle_input_done(cctxt, payload, event, status); + break; case VCD_EVT_RESP_OUTPUT_DONE: - { - rc = vcd_handle_frame_done(p_cctxt, p_payload, event, - status); - break; - } + rc = vcd_handle_frame_done(cctxt, payload, event, status); + break; case VCD_EVT_RESP_OUTPUT_REQ: - { - rc = vcd_handle_output_required(p_cctxt, p_payload, - status); - break; - } + rc = vcd_handle_output_required(cctxt, payload, status); + break; case VCD_EVT_IND_HWERRFATAL: - { - vcd_handle_ind_hw_err_fatal(p_cctxt, - VCD_EVT_RESP_PAUSE, status); - break; - } + vcd_handle_ind_hw_err_fatal(cctxt, VCD_EVT_RESP_PAUSE, status); + break; default: - { - VCD_MSG_ERROR - ("Unexpected callback event=%d status=%d from DDL", - event, status); - - p_dev_ctxt->b_continue = FALSE; - - break; - } + VCD_MSG_ERROR("Unexpected callback event=%d status=%d from DDL\n", + event, status); + dev_ctxt->cont = false; + break; } - if (!VCD_FAILED(rc) && ((event == VCD_EVT_RESP_INPUT_DONE || - event == VCD_EVT_RESP_OUTPUT_DONE) || - event == VCD_EVT_RESP_OUTPUT_REQ)) { + if (!VCD_FAILED(rc) && (event == VCD_EVT_RESP_INPUT_DONE || + event == VCD_EVT_RESP_OUTPUT_DONE || + event == VCD_EVT_RESP_OUTPUT_REQ) && + ((struct ddl_frame_data_tag *)payload)->frm_trans_end) { - if (((struct ddl_frame_data_type_tag *)p_payload)-> - b_frm_trans_end) { + vcd_mark_frame_channel(cctxt->dev_ctxt); - vcd_mark_frame_channel(p_cctxt->p_dev_ctxt); + if (!cctxt->status.frame_submitted) { + VCD_MSG_HIGH("All pending frames recvd from DDL\n"); - if (!p_cctxt->status.n_frame_submitted) { - VCD_MSG_HIGH - ("All pending frames recvd from DDL"); + cctxt->callback(VCD_EVT_RESP_PAUSE, VCD_S_SUCCESS, NULL, + 0, cctxt, cctxt->client_data); - p_cctxt->callback(VCD_EVT_RESP_PAUSE, - VCD_S_SUCCESS, - NULL, - 0, - p_cctxt, - p_cctxt->p_client_data); + vcd_do_client_state_transition(cctxt, + VCD_CLIENT_STATE_PAUSED, + CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); - vcd_do_client_state_transition(p_cctxt, - VCD_CLIENT_STATE_PAUSED, - CLIENT_STATE_EVENT_NUMBER - (pf_clnt_cb)); + rc = vcd_power_event(cctxt->dev_ctxt, cctxt, + VCD_EVT_PWR_CLNT_PAUSE); - rc = vcd_power_event(p_cctxt->p_dev_ctxt, - p_cctxt, - VCD_EVT_PWR_CLNT_PAUSE); - - if (VCD_FAILED(rc)) { - VCD_MSG_ERROR - ("VCD_EVT_PWR_CLNT_PAUSE_END" - "failed"); - } + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("VCD_EVT_PWR_CLNT_PAUSE_END " + "failed\n"); } } } } -static void vcd_clnt_cb_in_invalid( - struct vcd_clnt_ctxt_type_t *p_cctxt, u32 event, u32 status, - void *p_payload, u32 size, u32 *ddl_handle, - void *const p_client_data -) +static void vcd_clnt_cb_in_invalid(struct vcd_clnt_ctxt *cctxt, u32 event, + u32 status, void *payload, u32 size, u32 *ddl_handle, + void *const client_data) { - struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; - VCD_MSG_LOW("vcd_clnt_cb_in_invalid:"); - if (p_cctxt->ddl_handle != ddl_handle) { - VCD_MSG_ERROR("ddl_handle mismatch"); + struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt; + VCD_MSG_LOW("vcd_clnt_cb_in_invalid:\n"); + if (cctxt->ddl_handle != ddl_handle) { + VCD_MSG_ERROR("ddl_handle mismatch\n"); return; } switch (event) { case VCD_EVT_RESP_STOP: - { - vcd_handle_stop_done_in_invalid(p_cctxt, status); - break; - } + vcd_handle_stop_done_in_invalid(cctxt, status); + break; case VCD_EVT_RESP_INPUT_DONE: case VCD_EVT_RESP_OUTPUT_DONE: case VCD_EVT_RESP_OUTPUT_REQ: case VCD_EVT_RESP_TRANSACTION_PENDING: - { - break; - } + break; case VCD_EVT_IND_HWERRFATAL: - { - if (status == VCD_ERR_HW_FATAL) - vcd_handle_stop_done_in_invalid(p_cctxt, - status); + if (status == VCD_ERR_HW_FATAL) + vcd_handle_stop_done_in_invalid(cctxt, status); - break; - } + break; default: - { - VCD_MSG_ERROR("Unexpected callback event=%d status=%d" - "from DDL", event, status); - p_dev_ctxt->b_continue = FALSE; - break; - } + VCD_MSG_ERROR("Unexpected callback event=%d status=%d from DDL\n", + event, status); + dev_ctxt->cont = false; + break; } } -static void vcd_clnt_enter_open - (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { - VCD_MSG_MED("Entering CLIENT_STATE_OPEN on api %d", n_state_event_type); +static void vcd_clnt_enter_open(struct vcd_clnt_ctxt *cctxt, s32 ev_code) +{ + VCD_MSG_MED("Entering CLIENT_STATE_OPEN on api %d\n", ev_code); } -static void vcd_clnt_enter_starting - (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { - VCD_MSG_MED("Entering CLIENT_STATE_STARTING on api %d", - n_state_event_type); +static void vcd_clnt_enter_starting(struct vcd_clnt_ctxt *cctxt, s32 ev_code) +{ + VCD_MSG_MED("Entering CLIENT_STATE_STARTING on api %d\n", ev_code); } -static void vcd_clnt_enter_run - (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { - VCD_MSG_MED("Entering CLIENT_STATE_RUN on api %d", n_state_event_type); +static void vcd_clnt_enter_run(struct vcd_clnt_ctxt *cctxt, s32 ev_code) +{ + VCD_MSG_MED("Entering CLIENT_STATE_RUN on api %d\n", ev_code); } -static void vcd_clnt_enter_flushing - (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { - VCD_MSG_MED("Entering CLIENT_STATE_FLUSHING on api %d", - n_state_event_type); +static void vcd_clnt_enter_flushing(struct vcd_clnt_ctxt *cctxt, s32 ev_code) +{ + VCD_MSG_MED("Entering CLIENT_STATE_FLUSHING on api %d\n", ev_code); } -static void vcd_clnt_enter_stopping - (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { - VCD_MSG_MED("Entering CLIENT_STATE_STOPPING on api %d", - n_state_event_type); +static void vcd_clnt_enter_stopping(struct vcd_clnt_ctxt *cctxt, s32 ev_code) +{ + VCD_MSG_MED("Entering CLIENT_STATE_STOPPING on api %d\n", ev_code); } -static void vcd_clnt_enter_eos(struct vcd_clnt_ctxt_type_t *p_cctxt, - s32 n_state_event_type) +static void vcd_clnt_enter_eos(struct vcd_clnt_ctxt *cctxt, s32 ev_code) { - u32 rc; + u32 rc; - VCD_MSG_MED("Entering CLIENT_STATE_EOS on api %d", n_state_event_type); + VCD_MSG_MED("Entering CLIENT_STATE_EOS on api %d\n", ev_code); rc = vcd_map_sched_status(sched_suspend_resume_client( - p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, FALSE)); + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, false)); if (VCD_FAILED(rc)) - VCD_MSG_ERROR("Failed: sched_suspend_resume_client." - "rc=0x%x", rc); + VCD_MSG_ERROR("Failed: sched_suspend_resume_client. rc=0x%x\n", + rc); } -static void vcd_clnt_enter_pausing - (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { - VCD_MSG_MED("Entering CLIENT_STATE_PAUSING on api %d", - n_state_event_type); +static void vcd_clnt_enter_pausing(struct vcd_clnt_ctxt *cctxt, s32 ev_code) +{ + VCD_MSG_MED("Entering CLIENT_STATE_PAUSING on api %d\n", ev_code); } -static void vcd_clnt_enter_paused - (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) +static void vcd_clnt_enter_paused(struct vcd_clnt_ctxt *cctxt, s32 ev_code) { - VCD_MSG_MED("Entering CLIENT_STATE_PAUSED on api %d", - n_state_event_type); + VCD_MSG_MED("Entering CLIENT_STATE_PAUSED on api %d\n", ev_code); } -static void vcd_clnt_enter_invalid(struct vcd_clnt_ctxt_type_t *p_cctxt, - s32 n_state_event_type) +static void vcd_clnt_enter_invalid(struct vcd_clnt_ctxt *cctxt, s32 ev_code) { - VCD_MSG_MED("Entering CLIENT_STATE_INVALID on api %d", - n_state_event_type); - - p_cctxt->b_ddl_hdl_valid = FALSE; + VCD_MSG_MED("Entering CLIENT_STATE_INVALID on api %d\n", ev_code); + cctxt->ddl_hdl_valid = false; } -static void vcd_clnt_exit_open - (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) +static void vcd_clnt_exit_open(struct vcd_clnt_ctxt *cctxt, s32 ev_code) { - VCD_MSG_MED("Exiting CLIENT_STATE_OPEN on api %d", n_state_event_type); + VCD_MSG_MED("Exiting CLIENT_STATE_OPEN on api %d\n", ev_code); } -static void vcd_clnt_exit_starting - (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { - VCD_MSG_MED("Exiting CLIENT_STATE_STARTING on api %d", - n_state_event_type); +static void vcd_clnt_exit_starting(struct vcd_clnt_ctxt *cctxt, s32 ev_code) +{ + VCD_MSG_MED("Exiting CLIENT_STATE_STARTING on api %d\n", ev_code); } -static void vcd_clnt_exit_run - (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { - VCD_MSG_MED("Exiting CLIENT_STATE_RUN on api %d", n_state_event_type); +static void vcd_clnt_exit_run(struct vcd_clnt_ctxt *cctxt, s32 ev_code) +{ + VCD_MSG_MED("Exiting CLIENT_STATE_RUN on api %d\n", ev_code); } -static void vcd_clnt_exit_flushing - (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { - VCD_MSG_MED("Exiting CLIENT_STATE_FLUSHING on api %d", - n_state_event_type); +static void vcd_clnt_exit_flushing(struct vcd_clnt_ctxt *cctxt, s32 ev_code) +{ + VCD_MSG_MED("Exiting CLIENT_STATE_FLUSHING on api %d\n", ev_code); } -static void vcd_clnt_exit_stopping - (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { - VCD_MSG_MED("Exiting CLIENT_STATE_STOPPING on api %d", - n_state_event_type); +static void vcd_clnt_exit_stopping(struct vcd_clnt_ctxt *cctxt, s32 ev_code) +{ + VCD_MSG_MED("Exiting CLIENT_STATE_STOPPING on api %d\n", ev_code); } -static void vcd_clnt_exit_eos - (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) +static void vcd_clnt_exit_eos(struct vcd_clnt_ctxt *cctxt, s32 ev_code) { u32 rc; - VCD_MSG_MED("Exiting CLIENT_STATE_EOS on api %d", n_state_event_type); + VCD_MSG_MED("Exiting CLIENT_STATE_EOS on api %d\n", ev_code); rc = vcd_map_sched_status(sched_suspend_resume_client( - p_cctxt->p_dev_ctxt->sched_hdl, p_cctxt->sched_clnt_hdl, TRUE)); + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, true)); if (VCD_FAILED(rc)) - VCD_MSG_ERROR("Failed: sched_suspend_resume_client. rc=0x%x", + VCD_MSG_ERROR("Failed: sched_suspend_resume_client. rc=0x%x\n", rc); } -static void vcd_clnt_exit_pausing - (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { - VCD_MSG_MED("Exiting CLIENT_STATE_PAUSING on api %d", - n_state_event_type); +static void vcd_clnt_exit_pausing(struct vcd_clnt_ctxt *cctxt, s32 ev_code) +{ + VCD_MSG_MED("Exiting CLIENT_STATE_PAUSING on api %d\n", ev_code); } -static void vcd_clnt_exit_paused - (struct vcd_clnt_ctxt_type_t *p_cctxt, s32 n_state_event_type) { - VCD_MSG_MED("Exiting CLIENT_STATE_PAUSED on api %d", - n_state_event_type); +static void vcd_clnt_exit_paused(struct vcd_clnt_ctxt *cctxt, s32 ev_code) +{ + VCD_MSG_MED("Exiting CLIENT_STATE_PAUSED on api %d\n", ev_code); } -static void vcd_clnt_exit_invalid(struct vcd_clnt_ctxt_type_t *p_cctxt, - s32 n_state_event_type) +static void vcd_clnt_exit_invalid(struct vcd_clnt_ctxt *cctxt, s32 ev_code) { - VCD_MSG_MED("Exiting CLIENT_STATE_INVALID on api %d", - n_state_event_type); + VCD_MSG_MED("Exiting CLIENT_STATE_INVALID on api %d\n", ev_code); } -void vcd_do_client_state_transition(struct vcd_clnt_ctxt_type_t *p_cctxt, - enum vcd_clnt_state_enum_type e_to_state, u32 n_ev_code) +void vcd_do_client_state_transition(struct vcd_clnt_ctxt *cctxt, + enum vcd_clnt_state_enum to_state, u32 ev_code) { - struct vcd_clnt_state_ctxt_type_t *p_state_ctxt; + struct vcd_clnt_state_ctxt *state_ctxt; - if (!p_cctxt || e_to_state >= VCD_CLIENT_STATE_MAX) { - VCD_MSG_ERROR("Bad parameters. p_cctxt=%p, e_to_state=%d", - p_cctxt, e_to_state); + if (!cctxt || to_state >= VCD_CLIENT_STATE_MAX) { + VCD_MSG_ERROR("Bad parameters. cctxt=%p, to_state=%d\n", cctxt, + to_state); } - p_state_ctxt = &p_cctxt->clnt_state; - - if (p_state_ctxt->e_state == e_to_state) { - VCD_MSG_HIGH("Client already in requested e_to_state=%d", - e_to_state); + state_ctxt = &cctxt->clnt_state; + if (state_ctxt->state == to_state) { + VCD_MSG_HIGH("Client already in requested to_state=%d\n", + to_state); return; } - VCD_MSG_MED("vcd_do_client_state_transition: C%d -> C%d, for api %d", - (int)p_state_ctxt->e_state, (int)e_to_state, n_ev_code); + VCD_MSG_MED("vcd_do_client_state_transition: C%d -> C%d, for api %d\n", + (int)state_ctxt->state, (int)to_state, ev_code); - if (p_state_ctxt->p_state_table->pf_exit) - p_state_ctxt->p_state_table->pf_exit(p_cctxt, n_ev_code); + if (state_ctxt->state_table->pf_exit) + state_ctxt->state_table->pf_exit(cctxt, ev_code); - p_state_ctxt->e_state = e_to_state; - p_state_ctxt->p_state_table = vcd_clnt_state_table[e_to_state]; + state_ctxt->state = to_state; + state_ctxt->state_table = vcd_clnt_state_table[to_state]; - if (p_state_ctxt->p_state_table->pf_entry) - p_state_ctxt->p_state_table->pf_entry(p_cctxt, n_ev_code); + if (state_ctxt->state_table->pf_entry) + state_ctxt->state_table->pf_entry(cctxt, ev_code); } -const struct vcd_clnt_state_table_type_t *vcd_get_client_state_table - (enum vcd_clnt_state_enum_type e_state) { - return vcd_clnt_state_table[e_state]; +const struct vcd_clnt_state_table *vcd_get_client_state_table( + enum vcd_clnt_state_enum state) +{ + return vcd_clnt_state_table[state]; } -static const struct vcd_clnt_state_table_type_t vcd_clnt_table_open = { +static const struct vcd_clnt_state_table vcd_clnt_table_open = { { - vcd_close_in_open, - vcd_encode_start_in_open, - NULL, - vcd_decode_start_in_open, - NULL, - NULL, - NULL, - vcd_flush_inopen, - vcd_stop_inopen, - vcd_set_property_cmn, - vcd_get_property_cmn, - vcd_set_buffer_requirements_cmn, - vcd_get_buffer_requirements_cmn, - vcd_set_buffer_cmn, - vcd_allocate_buffer_cmn, - vcd_free_buffer_cmn, - vcd_fill_output_buffer_cmn, - NULL, - }, + vcd_close_in_open, + vcd_encode_start_in_open, + NULL, + vcd_decode_start_in_open, + NULL, + NULL, + NULL, + vcd_flush_inopen, + vcd_stop_inopen, + vcd_set_property_cmn, + vcd_get_property_cmn, + vcd_set_buffer_requirements_cmn, + vcd_get_buffer_requirements_cmn, + vcd_set_buffer_cmn, + vcd_allocate_buffer_cmn, + vcd_free_buffer_cmn, + vcd_fill_output_buffer_cmn, + NULL, + }, vcd_clnt_enter_open, vcd_clnt_exit_open }; -static const struct vcd_clnt_state_table_type_t vcd_clnt_table_starting = { +static const struct vcd_clnt_state_table vcd_clnt_table_starting = { { - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - vcd_clnt_cb_in_starting, - }, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_clnt_cb_in_starting, + }, vcd_clnt_enter_starting, vcd_clnt_exit_starting }; -static const struct vcd_clnt_state_table_type_t vcd_clnt_table_run = { +static const struct vcd_clnt_state_table vcd_clnt_table_run = { { - NULL, - vcd_encode_start_in_run, - vcd_encode_frame_cmn, - vcd_decode_start_in_run, - vcd_decode_frame_cmn, - vcd_pause_in_run, - NULL, - vcd_flush_cmn, - vcd_stop_in_run, - vcd_set_property_cmn, - vcd_get_property_cmn, - vcd_set_buffer_requirements_cmn, - vcd_get_buffer_requirements_cmn, - vcd_set_buffer_cmn, - vcd_allocate_buffer_cmn, - NULL, - vcd_fill_output_buffer_cmn, - vcd_clnt_cb_in_run, - }, + NULL, + vcd_encode_start_in_run, + vcd_encode_frame_cmn, + vcd_decode_start_in_run, + vcd_decode_frame_cmn, + vcd_pause_in_run, + NULL, + vcd_flush_cmn, + vcd_stop_in_run, + vcd_set_property_cmn, + vcd_get_property_cmn, + vcd_set_buffer_requirements_cmn, + vcd_get_buffer_requirements_cmn, + vcd_set_buffer_cmn, + vcd_allocate_buffer_cmn, + NULL, + vcd_fill_output_buffer_cmn, + vcd_clnt_cb_in_run, + }, vcd_clnt_enter_run, vcd_clnt_exit_run }; -static const struct vcd_clnt_state_table_type_t vcd_clnt_table_flushing = { +static const struct vcd_clnt_state_table vcd_clnt_table_flushing = { { - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - vcd_flush_in_flushing, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - vcd_clnt_cb_in_flushing, - }, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_flush_in_flushing, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_clnt_cb_in_flushing, + }, vcd_clnt_enter_flushing, vcd_clnt_exit_flushing }; -static const struct vcd_clnt_state_table_type_t vcd_clnt_table_stopping = { +static const struct vcd_clnt_state_table vcd_clnt_table_stopping = { { - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - vcd_get_property_cmn, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - vcd_clnt_cb_in_stopping, - }, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_get_property_cmn, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_clnt_cb_in_stopping, + }, vcd_clnt_enter_stopping, vcd_clnt_exit_stopping }; -static const struct vcd_clnt_state_table_type_t vcd_clnt_table_eos = { +static const struct vcd_clnt_state_table vcd_clnt_table_eos = { { - NULL, - NULL, - vcd_encode_frame_cmn, - NULL, - vcd_decode_frame_cmn, - NULL, - NULL, - vcd_flush_in_eos, - vcd_stop_in_eos, - NULL, - vcd_get_property_cmn, - NULL, - NULL, - NULL, - NULL, - NULL, - vcd_fill_output_buffer_in_eos, - vcd_clnt_cb_in_eos, - }, + NULL, + NULL, + vcd_encode_frame_cmn, + NULL, + vcd_decode_frame_cmn, + NULL, + NULL, + vcd_flush_in_eos, + vcd_stop_in_eos, + NULL, + vcd_get_property_cmn, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_fill_output_buffer_in_eos, + vcd_clnt_cb_in_eos, + }, vcd_clnt_enter_eos, vcd_clnt_exit_eos }; -static const struct vcd_clnt_state_table_type_t vcd_clnt_table_pausing = { +static const struct vcd_clnt_state_table vcd_clnt_table_pausing = { { - NULL, - NULL, - vcd_encode_frame_cmn, - NULL, - vcd_decode_frame_cmn, - NULL, - NULL, - NULL, - NULL, - NULL, - vcd_get_property_cmn, - NULL, - NULL, - NULL, - NULL, - NULL, - vcd_fill_output_buffer_cmn, - vcd_clnt_cb_in_pausing, - }, + NULL, + NULL, + vcd_encode_frame_cmn, + NULL, + vcd_decode_frame_cmn, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_get_property_cmn, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_fill_output_buffer_cmn, + vcd_clnt_cb_in_pausing, + }, vcd_clnt_enter_pausing, vcd_clnt_exit_pausing }; -static const struct vcd_clnt_state_table_type_t vcd_clnt_table_paused = { +static const struct vcd_clnt_state_table vcd_clnt_table_paused = { { - NULL, - NULL, - vcd_encode_frame_cmn, - NULL, - vcd_decode_frame_cmn, - NULL, - vcd_resume_in_paused, - vcd_flush_cmn, - vcd_stop_cmn, - vcd_set_property_cmn, - vcd_get_property_cmn, - vcd_set_buffer_requirements_cmn, - vcd_get_buffer_requirements_cmn, - vcd_set_buffer_cmn, - vcd_allocate_buffer_cmn, - NULL, - vcd_fill_output_buffer_cmn, - NULL, - }, + NULL, + NULL, + vcd_encode_frame_cmn, + NULL, + vcd_decode_frame_cmn, + NULL, + vcd_resume_in_paused, + vcd_flush_cmn, + vcd_stop_cmn, + vcd_set_property_cmn, + vcd_get_property_cmn, + vcd_set_buffer_requirements_cmn, + vcd_get_buffer_requirements_cmn, + vcd_set_buffer_cmn, + vcd_allocate_buffer_cmn, + NULL, + vcd_fill_output_buffer_cmn, + NULL, + }, vcd_clnt_enter_paused, vcd_clnt_exit_paused }; -static const struct vcd_clnt_state_table_type_t vcd_clnt_table_invalid = { - { - vcd_close_in_invalid, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - vcd_flush_in_invalid, - vcd_stop_in_invalid, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - vcd_free_buffer_cmn, - NULL, - vcd_clnt_cb_in_invalid, - }, - vcd_clnt_enter_invalid, - vcd_clnt_exit_invalid +static const struct vcd_clnt_state_table vcd_clnt_table_invalid = { + { + vcd_close_in_invalid, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_flush_in_invalid, + vcd_stop_in_invalid, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + vcd_free_buffer_cmn, + NULL, + vcd_clnt_cb_in_invalid, + }, + vcd_clnt_enter_invalid, + vcd_clnt_exit_invalid }; -static const struct vcd_clnt_state_table_type_t *vcd_clnt_state_table[] = { +static const struct vcd_clnt_state_table *vcd_clnt_state_table[] = { NULL, &vcd_clnt_table_open, &vcd_clnt_table_starting, @@ -1766,5 +1495,5 @@ static const struct vcd_clnt_state_table_type_t *vcd_clnt_state_table[] = { &vcd_clnt_table_paused, &vcd_clnt_table_stopping, &vcd_clnt_table_eos, - &vcd_clnt_table_invalid + &vcd_clnt_table_invalid }; diff --git a/drivers/misc/video_core/720p/vcd/vcd_client_sm.h b/drivers/misc/video_core/720p/vcd/vcd_client_sm.h index c3e37786cf3e6..8f3a975544431 100644 --- a/drivers/misc/video_core/720p/vcd/vcd_client_sm.h +++ b/drivers/misc/video_core/720p/vcd/vcd_client_sm.h @@ -31,11 +31,11 @@ #include "vcd_api.h" #include "vcd_ddl_api.h" -struct vcd_clnt_state_table_type_t; -struct vcd_clnt_state_ctxt_type_t; -struct vcd_clnt_ctxt_type_t; +struct vcd_clnt_state_table; +struct vcd_clnt_state_ctxt; +struct vcd_clnt_ctxt; -enum vcd_clnt_state_enum_type { +enum vcd_clnt_state_enum { VCD_CLIENT_STATE_NULL = 0, VCD_CLIENT_STATE_OPEN, VCD_CLIENT_STATE_STARTING, @@ -50,77 +50,63 @@ enum vcd_clnt_state_enum_type { VCD_CLIENT_STATE_32BIT = 0x7FFFFFFF }; -#define CLIENT_STATE_EVENT_NUMBER(ppf) \ - ((u32 *) (&(((struct vcd_clnt_state_table_type_t*)0)->ev_hdlr.ppf)) - \ - (u32 *) (&(((struct vcd_clnt_state_table_type_t*)0)->ev_hdlr.pf_close)) \ - + 1) +#define CLIENT_STATE_EVENT_NUMBER(ppf) \ + ((u32 *) (&(((struct vcd_clnt_state_table*)0)->ev_hdlr.ppf)) - \ + (u32 *) (&(((struct vcd_clnt_state_table*)0)->ev_hdlr.pf_close)) + 1) -struct vcd_clnt_state_table_type_t { +struct vcd_clnt_state_table { struct { - u32(*pf_close) (struct vcd_clnt_ctxt_type_t *p_cctxt); - u32(*pf_encode_start) (struct vcd_clnt_ctxt_type_t *p_cctxt); - u32(*pf_encode_frame) (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_input_frame); - u32(*pf_decode_start) (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_sequence_hdr_type *p_seq_hdr); - u32(*pf_decode_frame) (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_input_frame); - u32(*pf_pause) (struct vcd_clnt_ctxt_type_t *p_cctxt); - u32(*pf_resume) (struct vcd_clnt_ctxt_type_t *p_cctxt); - u32(*pf_flush) (struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 n_mode); - u32(*pf_stop) (struct vcd_clnt_ctxt_type_t *p_cctxt); - u32(*pf_set_property) (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_property_hdr_type *p_prop_hdr, - void *p_prop); - u32(*pf_get_property) (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_property_hdr_type *p_prop_hdr, - void *p_prop); - u32(*pf_set_buffer_requirements) (struct vcd_clnt_ctxt_type_t * - p_cctxt, - enum vcd_buffer_type e_buffer, - struct - vcd_buffer_requirement_type * - p_buffer_req); - u32(*pf_get_buffer_requirements) (struct vcd_clnt_ctxt_type_t * - p_cctxt, - enum vcd_buffer_type e_buffer, - struct - vcd_buffer_requirement_type * - p_buffer_req); - u32(*pf_set_buffer) (struct vcd_clnt_ctxt_type_t *p_cctxt, - enum vcd_buffer_type e_buffer, u8 *p_buffer, - u32 n_buf_size); - u32(*pf_allocate_buffer) (struct vcd_clnt_ctxt_type_t *p_cctxt, - enum vcd_buffer_type e_buffer, u32 n_buf_size, - u8 **pp_vir_buf_addr, u8 **pp_phy_buf_addr); - u32(*pf_free_buffer) (struct vcd_clnt_ctxt_type_t *p_cctxt, - enum vcd_buffer_type e_buffer, u8 *p_buffer); - u32(*pf_fill_output_buffer) ( - struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_buffer); - void (*pf_clnt_cb) (struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 event, u32 status, void *p_payload, - u32 n_size, u32 *ddl_handle, - void *const p_client_data); + u32(*pf_close) (struct vcd_clnt_ctxt *cctxt); + u32(*pf_encode_start) (struct vcd_clnt_ctxt *cctxt); + u32(*pf_encode_frame) (struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *input_frame); + u32(*pf_decode_start) (struct vcd_clnt_ctxt *cctxt, + struct vcd_sequence_hdr *seq_hdr); + u32(*pf_decode_frame) (struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *input_frame); + u32(*pf_pause) (struct vcd_clnt_ctxt *cctxt); + u32(*pf_resume) (struct vcd_clnt_ctxt *cctxt); + u32(*pf_flush) (struct vcd_clnt_ctxt *cctxt, u32 mode); + u32(*pf_stop) (struct vcd_clnt_ctxt *cctxt); + u32(*pf_set_property) (struct vcd_clnt_ctxt *cctxt, + struct vcd_property_hdr *prop_hdr, void *prop); + u32(*pf_get_property) (struct vcd_clnt_ctxt *cctxt, + struct vcd_property_hdr *prop_hdr, + void *prop); + u32(*pf_set_buffer_requirements) (struct vcd_clnt_ctxt *cctxt, + enum vcd_buffer_type vcd_buffer_type, + struct vcd_buffer_requirement *buffer_req); + u32(*pf_get_buffer_requirements) (struct vcd_clnt_ctxt *cctxt, + enum vcd_buffer_type vcd_buffer_type, + struct vcd_buffer_requirement *buffer_req); + u32(*pf_set_buffer) (struct vcd_clnt_ctxt *cctxt, + enum vcd_buffer_type vcd_buffer_type, void *buffer, + size_t buf_size); + u32(*pf_allocate_buffer) (struct vcd_clnt_ctxt *cctxt, + enum vcd_buffer_type vcd_buffer_type, size_t sz, + void **virt_addr, phys_addr_t *phys_addr); + u32(*pf_free_buffer) (struct vcd_clnt_ctxt *cctxt, + enum vcd_buffer_type vcd_buffer_type, void *buf); + u32(*pf_fill_output_buffer) (struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *buffer); + void (*pf_clnt_cb) (struct vcd_clnt_ctxt *cctxt, u32 event, + u32 status, void *payload, u32 size, u32 *ddl_handle, + void *const client_data); } ev_hdlr; - void (*pf_entry) (struct vcd_clnt_ctxt_type_t *p_cctxt, - s32 n_state_event_type); - void (*pf_exit) (struct vcd_clnt_ctxt_type_t *p_cctxt, - s32 n_state_event_type); + void (*pf_entry) (struct vcd_clnt_ctxt *cctxt, s32 state_event_type); + void (*pf_exit) (struct vcd_clnt_ctxt *cctxt, s32 state_event_type); }; -struct vcd_clnt_state_ctxt_type_t { - const struct vcd_clnt_state_table_type_t *p_state_table; - enum vcd_clnt_state_enum_type e_state; +struct vcd_clnt_state_ctxt { + const struct vcd_clnt_state_table *state_table; + enum vcd_clnt_state_enum state; }; -extern void vcd_do_client_state_transition - (struct vcd_clnt_ctxt_type_t *p_cctxt, - enum vcd_clnt_state_enum_type e_to_state, u32 n_ev_code); +extern void vcd_do_client_state_transition(struct vcd_clnt_ctxt *cctxt, + enum vcd_clnt_state_enum to_state, u32 ev_code); -extern const struct vcd_clnt_state_table_type_t *vcd_get_client_state_table( - enum vcd_clnt_state_enum_type e_state); +extern const struct vcd_clnt_state_table *vcd_get_client_state_table( + enum vcd_clnt_state_enum state); #endif diff --git a/drivers/misc/video_core/720p/vcd/vcd_core.h b/drivers/misc/video_core/720p/vcd/vcd_core.h index c395b01a3ff5d..f855d7bda9add 100644 --- a/drivers/misc/video_core/720p/vcd/vcd_core.h +++ b/drivers/misc/video_core/720p/vcd/vcd_core.h @@ -43,8 +43,8 @@ #define VCD_MAX_SCHEDULER_QUEUE_DURATION 1 -#define VCD_MAX_SCHEDULER_QUEUE_SIZE(n_fps_n, n_fps_d) \ - (n_fps_n / n_fps_d * VCD_MAX_SCHEDULER_QUEUE_DURATION) +#define VCD_MAX_SCHEDULER_QUEUE_SIZE(fps_n, fps_d) \ + (fps_n / fps_d * VCD_MAX_SCHEDULER_QUEUE_DURATION) #define VCD_SCHEDULER_INITIAL_PERF_LEVEL 108000 @@ -81,154 +81,167 @@ enum vcd_command_type { VCD_CMD_CLIENT_CLOSE }; -struct vcd_cmd_q_element_type { - enum vcd_command_type e_pending_cmd; +//TODO: remove this +struct vcd_cmd_q_element { + enum vcd_command_type pending_cmd; }; -struct vcd_buffer_entry_type { - u32 b_valid; - u8 *p_alloc; - u8 *p_virtual; - u8 *p_physical; - u32 n_size; - u32 b_allocated; - u32 b_in_use; - struct vcd_frame_data_type frame; +struct vcd_dma_buffer { + void *virt_addr; + phys_addr_t phys_addr; + size_t size; +}; + +struct vcd_buffer_entry { + u32 valid; + struct vcd_dma_buffer buffer; + void *virt_addr; + phys_addr_t phys_addr; + size_t size; +// u8 *alloc; +// u8 *virtual; // aligned so == alloc +// u8 *physical; +// u32 size; + u32 allocated; // true when allocated + u32 in_use; + struct vcd_frame_data frame; }; -struct vcd_buffer_pool_type { - struct vcd_buffer_entry_type *a_entries; - u32 n_count; - struct vcd_buffer_requirement_type buf_req; - u32 n_validated; - u32 n_allocated; - u32 n_in_use; - struct vcd_buffer_entry_type **a_queue; - u16 n_q_len; - u16 n_q_head; - u16 n_q_tail; +struct vcd_buffer_pool { + struct vcd_buffer_entry *entries; + u32 count; + struct vcd_buffer_requirement buf_req; + u32 validated; + u32 allocated; + u32 in_use; + struct vcd_buffer_entry **queue; + u16 q_len; + u16 q_head; + u16 q_tail; }; -struct vcd_transc_type { - u32 b_in_use; - enum vcd_command_type e_type; - struct vcd_clnt_ctxt_type_t *p_cctxt; +struct vcd_transc { + u32 in_use; + enum vcd_command_type type; + struct vcd_clnt_ctxt *cctxt; - struct vcd_buffer_entry_type *p_ip_buf_entry; + struct vcd_buffer_entry *ip_buf_entry; s64 time_stamp; - u32 n_ip_frm_tag; - enum vcd_frame_type e_frame_type; + u32 ip_frm_tag; + enum vcd_frame frame_type; - struct vcd_buffer_entry_type *p_op_buf_entry; + struct vcd_buffer_entry *op_buf_entry; - u32 b_input_done; - u32 b_frame_done; + u32 input_done; + u32 frame_done; }; -struct vcd_dev_ctxt_type { - u32 b_ddl_cmd_concurrency; - u32 n_ddl_frame_ch_depth; - u32 n_ddl_cmd_ch_depth; - u32 n_ddl_frame_ch_interim; - u32 n_ddl_cmd_ch_interim; - u32 n_ddl_frame_ch_free; - u32 n_ddl_cmd_ch_free; +struct vcd_dev_ctxt { + u32 ddl_cmd_concurrency; + u32 ddl_frame_ch_depth; + u32 ddl_cmd_ch_depth; + u32 ddl_frame_ch_interim; + u32 ddl_cmd_ch_interim; + u32 ddl_frame_ch_free; + u32 ddl_cmd_ch_free; void *sched_hdl; - struct vcd_init_config_type config; + struct vcd_init_config config; - u32 b_driver_ids[VCD_DRIVER_INSTANCE_MAX]; - u32 n_refs; - u8 *p_device_base_addr; - void *p_hw_timer_handle; - u32 n_hw_time_out; - struct vcd_clnt_ctxt_type_t *p_cctxt_list_head; + u32 driver_ids[VCD_DRIVER_INSTANCE_MAX]; + u32 refs; + u8 *device_base_addr; + void *hw_timer_handle; + u32 hw_time_out; + struct vcd_clnt_ctxt *cctxt_list_head; - enum vcd_command_type e_pending_cmd; + enum vcd_command_type pending_cmd; - u32 b_continue; + u32 cont; - struct vcd_transc_type *a_trans_tbl; - u32 n_trans_tbl_size; + struct vcd_transc *trans_tbl; + u32 trans_tbl_size; - enum vcd_power_state_type e_pwr_state; - enum vcd_pwr_clk_state_type e_pwr_clk_state; - u32 n_active_clnts; - u32 n_max_perf_lvl; - u32 n_reqd_perf_lvl; - u32 n_curr_perf_lvl; - u32 b_set_perf_lvl_pending; + enum vcd_power_state pwr_state; + enum vcd_pwr_clk_state_type pwr_clk_state; + u32 active_clnts; + u32 max_perf_lvl; + u32 reqd_perf_lvl; + u32 curr_perf_lvl; + u32 set_perf_lvl_pending; }; -struct vcd_clnt_status_type { - u32 b_req_perf_lvl; +struct vcd_clnt_status { + u32 req_perf_lvl; u32 b1st_frame_recvd; u32 b1st_ip_done_recvd; u32 b1st_op_done_recvd; - u32 n_frame_submitted; - u32 n_frame_delayed; - u32 n_cmd_submitted; + u32 frame_submitted; + u32 frame_delayed; + u32 cmd_submitted; - u32 n_int_field_cnt; + u32 int_field_cnt; s64 first_ts; s64 prev_ts; - u32 n_time_elapsed; + u32 time_elapsed; - u32 b_stop_pending; - u32 n_flush_mode; + u32 stop_pending; + u32 flush_mode; - u32 b_eos_wait_for_op_buf; - struct vcd_frame_data_type eos_trig_ip_frm; + u32 eos_wait_for_op_buf; + struct vcd_frame_data eos_trig_ip_frm; - u32 b_eos_prev_valid; - struct ddl_frame_data_type_tag eos_prev_op_frm; - u32 e_last_err; - u32 e_last_evt; - u32 b_cleaning_up; - u32 b_close_pending; + u32 eos_prev_valid; + struct ddl_frame_data_tag eos_prev_op_frm; + u32 last_err; + u32 last_evt; + u32 cleaning_up; + u32 close_pending; }; -struct vcd_clnt_ctxt_type_t { - u32 n_signature; - struct vcd_clnt_state_ctxt_type_t clnt_state; +struct vcd_clnt_ctxt { + u32 signature; + struct vcd_clnt_state_ctxt clnt_state; s32 driver_id; - u32 b_live; - u32 b_decoding; + u32 live; + u32 decoding; - struct vcd_property_frame_rate_type frm_rate; - u32 n_frm_p_units; - u32 n_reqd_perf_lvl; - u32 n_time_resoln; + struct vcd_property_frame_rate frm_rate; + u32 frm_p_units; + u32 reqd_perf_lvl; + u32 time_resoln; - struct vcd_buffer_pool_type in_buf_pool; - struct vcd_buffer_pool_type out_buf_pool; + struct vcd_buffer_pool in_buf_pool; + struct vcd_buffer_pool out_buf_pool; - void (*callback) (u32 event, u32 status, void *p_info, u32 n_size, - void *handle, void *const p_client_data); - void *p_client_data; + void (*callback) (u32 event, u32 status, void *info, u32 size, + void *handle, void *const client_data); + void *client_data; - u32 b_sched_clnt_valid; + u32 sched_clnt_valid; void *sched_clnt_hdl; - u32 n_sched_o_tkn_per_ip_frm; - u32 b_ddl_hdl_valid; + u32 sched_o_tkn_per_ip_frm; + u32 ddl_hdl_valid; u32 *ddl_handle; - struct vcd_dev_ctxt_type *p_dev_ctxt; - struct vcd_cmd_q_element_type cmd_q; - struct vcd_sequence_hdr_type seq_hdr; - u8 *p_seq_hdr_phy_addr; - struct vcd_clnt_status_type status; + struct vcd_dev_ctxt *dev_ctxt; + struct vcd_cmd_q_element cmd_q; + + struct vcd_sequence_hdr seq_hdr; + phys_addr_t seq_hdr_phys_addr; + + struct vcd_clnt_status status; - struct vcd_clnt_ctxt_type_t *p_next; + struct vcd_clnt_ctxt *next; }; #define VCD_BUFFERPOOL_INUSE_DECREMENT(val) \ diff --git a/drivers/misc/video_core/720p/vcd/vcd_device_sm.c b/drivers/misc/video_core/720p/vcd/vcd_device_sm.c index 90eb22cefd696..9e3a6a7530d0a 100644 --- a/drivers/misc/video_core/720p/vcd/vcd_device_sm.c +++ b/drivers/misc/video_core/720p/vcd/vcd_device_sm.c @@ -19,12 +19,12 @@ #include "video_core_type.h" #include "vcd.h" -static const struct vcd_dev_state_table_type_t *vcd_dev_state_table[]; -static const struct vcd_dev_state_table_type_t vcd_dev_table_null; +static const struct vcd_dev_state_table *vcd_dev_state_table[]; +static const struct vcd_dev_state_table vcd_dev_table_null; -struct vcd_drv_ctxt_type_t *vcd_get_drv_context(void) +struct vcd_drv_ctxt *vcd_get_drv_context(void) { - static struct vcd_drv_ctxt_type_t drv_context = { + static struct vcd_drv_ctxt drv_context = { {&vcd_dev_table_null, VCD_DEVICE_STATE_NULL}, {0}, 0 @@ -34,181 +34,157 @@ struct vcd_drv_ctxt_type_t *vcd_get_drv_context(void) } -void vcd_do_device_state_transition(struct vcd_drv_ctxt_type_t *p_drv_ctxt, - enum vcd_dev_state_enum_type e_to_state, u32 n_ev_code) +void vcd_do_device_state_transition(struct vcd_drv_ctxt *drv_ctxt, + enum vcd_dev_state_enum to_state, u32 ev_code) { - struct vcd_dev_state_ctxt_type_t *p_state_ctxt; + struct vcd_dev_state_ctxt *state_ctxt; - if (!p_drv_ctxt || e_to_state >= VCD_DEVICE_STATE_MAX) { - VCD_MSG_ERROR("Bad parameters. p_drv_ctxt=%p, e_to_state=%d", - p_drv_ctxt, e_to_state); + if (!drv_ctxt || to_state >= VCD_DEVICE_STATE_MAX) { + VCD_MSG_ERROR("Bad parameters. drv_ctxt=%p, to_state=%d", + drv_ctxt, to_state); } - p_state_ctxt = &p_drv_ctxt->dev_state; + state_ctxt = &drv_ctxt->dev_state; - if (p_state_ctxt->e_state == e_to_state) { - VCD_MSG_HIGH("Device already in requested e_to_state=%d", - e_to_state); + if (state_ctxt->state == to_state) { + VCD_MSG_HIGH("Device already in requested to_state=%d", + to_state); return; } VCD_MSG_MED("vcd_do_device_state_transition: D%d -> D%d, for api %d", - (int)p_state_ctxt->e_state, (int)e_to_state, n_ev_code); + (int)state_ctxt->state, (int)to_state, ev_code); - if (p_state_ctxt->p_state_table->pf_exit) - p_state_ctxt->p_state_table->pf_exit(p_drv_ctxt, n_ev_code); + if (state_ctxt->state_table->pf_exit) + state_ctxt->state_table->pf_exit(drv_ctxt, ev_code); - p_state_ctxt->e_state = e_to_state; - p_state_ctxt->p_state_table = vcd_dev_state_table[e_to_state]; + state_ctxt->state = to_state; + state_ctxt->state_table = vcd_dev_state_table[to_state]; - if (p_state_ctxt->p_state_table->pf_entry) - p_state_ctxt->p_state_table->pf_entry(p_drv_ctxt, n_ev_code); + if (state_ctxt->state_table->pf_entry) + state_ctxt->state_table->pf_entry(drv_ctxt, ev_code); } -void vcd_hw_timeout_handler(void *p_user_data) +void vcd_hw_timeout_handler(void *user_data) { - struct vcd_drv_ctxt_type_t *p_drv_ctxt; + struct vcd_drv_ctxt *drv_ctxt; VCD_MSG_HIGH("vcd_hw_timeout_handler:"); - p_user_data = NULL; - p_drv_ctxt = vcd_get_drv_context(); - vcd_critical_section_enter(p_drv_ctxt->dev_cs); - if (p_drv_ctxt->dev_state.p_state_table->ev_hdlr.pf_timeout) - p_drv_ctxt->dev_state.p_state_table->ev_hdlr. - pf_timeout(p_drv_ctxt, p_user_data); + user_data = NULL; + drv_ctxt = vcd_get_drv_context(); + mutex_lock(drv_ctxt->dev_mutex); + if (drv_ctxt->dev_state.state_table->ev_hdlr.pf_timeout) + drv_ctxt->dev_state.state_table->ev_hdlr.pf_timeout(drv_ctxt, + user_data); else VCD_MSG_ERROR("hw_timeout unsupported in device state %d", - p_drv_ctxt->dev_state.e_state); - vcd_critical_section_leave(p_drv_ctxt->dev_cs); + drv_ctxt->dev_state.state); + mutex_unlock(drv_ctxt->dev_mutex); } -void vcd_ddl_callback(u32 event, u32 status, void *p_payload, - u32 n_size, u32 *ddl_handle, void *const p_client_data) +void vcd_ddl_callback(u32 event, u32 status, void *payload, + u32 size, u32 *ddl_handle, void *const client_data) { - struct vcd_drv_ctxt_type_t *p_drv_ctxt; - struct vcd_dev_ctxt_type *p_dev_ctxt; - struct vcd_dev_state_ctxt_type_t *p_dev_state; - struct vcd_clnt_ctxt_type_t *p_cctxt; - struct vcd_transc_type *p_transc; + struct vcd_drv_ctxt *drv_ctxt; + struct vcd_dev_ctxt *dev_ctxt; + struct vcd_dev_state_ctxt *dev_state; + struct vcd_clnt_ctxt *cctxt; + struct vcd_transc *transc; VCD_MSG_LOW("vcd_ddl_callback:"); VCD_MSG_LOW("event=0x%x status=0x%x", event, status); - p_drv_ctxt = vcd_get_drv_context(); - p_dev_ctxt = &p_drv_ctxt->dev_ctxt; - p_dev_state = &p_drv_ctxt->dev_state; + drv_ctxt = vcd_get_drv_context(); + dev_ctxt = &drv_ctxt->dev_ctxt; + dev_state = &drv_ctxt->dev_state; - p_dev_ctxt->b_continue = TRUE; - vcd_device_timer_stop(p_dev_ctxt); + dev_ctxt->cont = true; + vcd_device_timer_stop(dev_ctxt); - switch (p_dev_state->e_state) { + switch (dev_state->state) { case VCD_DEVICE_STATE_NULL: - { - VCD_MSG_HIGH("Callback unexpected in NULL state"); - break; - } - + VCD_MSG_HIGH("Callback unexpected in NULL state"); + break; case VCD_DEVICE_STATE_NOT_INIT: - { - VCD_MSG_HIGH("Callback unexpected in NOT_INIT state"); - break; - } - + VCD_MSG_HIGH("Callback unexpected in NOT_INIT state"); + break; case VCD_DEVICE_STATE_INITING: - { - if (p_dev_state->p_state_table->ev_hdlr.pf_dev_cb) { - p_dev_state->p_state_table->ev_hdlr. - pf_dev_cb(p_drv_ctxt, event, status, - p_payload, n_size, ddl_handle, - p_client_data); - } else { - VCD_MSG_HIGH("No device handler in %d state", - p_dev_state->e_state); - } - break; + if (dev_state->state_table->ev_hdlr.pf_dev_cb) { + dev_state->state_table->ev_hdlr.pf_dev_cb(drv_ctxt, + event, status, payload, size, ddl_handle, + client_data); + } else { + VCD_MSG_HIGH("No device handler in %d state", + dev_state->state); } - + break; case VCD_DEVICE_STATE_READY: - { - p_transc = (struct vcd_transc_type *)p_client_data; + transc = (struct vcd_transc *)client_data; + + if (!transc || !transc->in_use || !transc->cctxt) { + VCD_MSG_ERROR("Invalid clientdata received from DDL "); + } else { + cctxt = transc->cctxt; - if (!p_transc || !p_transc->b_in_use - || !p_transc->p_cctxt) { - VCD_MSG_ERROR("Invalid clientdata " - "received from DDL "); + if (cctxt->clnt_state.state_table->ev_hdlr.pf_clnt_cb) { + cctxt->clnt_state.state_table->ev_hdlr. + pf_clnt_cb(cctxt, event, status, + payload, size, ddl_handle, client_data); } else { - p_cctxt = p_transc->p_cctxt; - - if (p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_clnt_cb) { - p_cctxt->clnt_state.p_state_table-> - ev_hdlr.pf_clnt_cb(p_cctxt, - event, status, p_payload, - n_size, ddl_handle, - p_client_data); - } else { - VCD_MSG_HIGH - ("No client handler in" + VCD_MSG_HIGH("No client handler in" " (dsm:READY, csm:%d) state", - (int)p_cctxt->clnt_state.e_state); + (int)cctxt->clnt_state.state); - if (VCD_FAILED(status)) { - VCD_MSG_FATAL("DDL callback" + if (VCD_FAILED(status)) { + VCD_MSG_FATAL("DDL callback" " returned failure 0x%x", status); - } } } - break; } - + break; default: - { - VCD_MSG_ERROR("Unknown state"); - break; - } - + VCD_MSG_ERROR("Unknown state"); + break; } } -u32 vcd_init_device_context(struct vcd_drv_ctxt_type_t *p_drv_ctxt, - u32 n_ev_code) +u32 vcd_init_device_context(struct vcd_drv_ctxt *drv_ctxt, u32 ev_code) { - struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; - struct sched_init_param_type sched_init; + struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; + struct sched_init_param sched_init; u32 rc; - struct ddl_init_config_type ddl_init; + struct ddl_init_config ddl_init; VCD_MSG_LOW("vcd_init_device_context:"); - p_dev_ctxt->e_pending_cmd = VCD_CMD_NONE; + dev_ctxt->pending_cmd = VCD_CMD_NONE; - rc = vcd_power_event(p_dev_ctxt, NULL, VCD_EVT_PWR_DEV_INIT_BEGIN); + rc = vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_INIT_BEGIN); VCD_FAILED_RETURN(rc, "VCD_EVT_PWR_DEV_INIT_BEGIN failed"); VCD_MSG_HIGH("Device powered ON and clocked"); - sched_init.n_perf_lvl = p_dev_ctxt->n_max_perf_lvl; - rc = vcd_map_sched_status(sched_create - (&sched_init, &p_dev_ctxt->sched_hdl)); + sched_init.perf_lvl = dev_ctxt->max_perf_lvl; + rc = vcd_map_sched_status(sched_create(&sched_init, + &dev_ctxt->sched_hdl)); if (VCD_FAILED(rc)) { VCD_MSG_ERROR("rc = 0x%x. Failed: sched_create", rc); - (void)vcd_power_event(p_dev_ctxt, NULL, - VCD_EVT_PWR_DEV_INIT_FAIL); + vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_INIT_FAIL); return rc; } VCD_MSG_HIGH("Created scheduler instance."); - ddl_init.p_core_virtual_base_addr = p_dev_ctxt->p_device_base_addr; - ddl_init.pf_interrupt_clr = p_dev_ctxt->config.pf_interrupt_clr; + ddl_init.core_virtual_base_addr = dev_ctxt->device_base_addr; + ddl_init.pf_interrupt_clr = dev_ctxt->config.pf_interrupt_clr; ddl_init.ddl_callback = vcd_ddl_callback; rc = ddl_device_init(&ddl_init, NULL); @@ -216,64 +192,59 @@ u32 vcd_init_device_context(struct vcd_drv_ctxt_type_t *p_drv_ctxt, if (VCD_FAILED(rc)) { VCD_MSG_ERROR("rc = 0x%x. Failed: ddl_device_init", rc); - (void)sched_destroy(p_dev_ctxt->sched_hdl); - p_dev_ctxt->sched_hdl = NULL; + sched_destroy(dev_ctxt->sched_hdl); + dev_ctxt->sched_hdl = NULL; - (void)vcd_power_event(p_dev_ctxt, NULL, - VCD_EVT_PWR_DEV_INIT_FAIL); + vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_INIT_FAIL); } else { - vcd_device_timer_start(p_dev_ctxt); - vcd_do_device_state_transition(p_drv_ctxt, - VCD_DEVICE_STATE_INITING, - n_ev_code); + vcd_device_timer_start(dev_ctxt); + vcd_do_device_state_transition(drv_ctxt, + VCD_DEVICE_STATE_INITING, ev_code); } return rc; } -void vcd_handle_device_init_failed(struct vcd_drv_ctxt_type_t *p_drv_ctxt, - u32 status) +void vcd_handle_device_init_failed(struct vcd_drv_ctxt *drv_ctxt, u32 status) { - struct vcd_clnt_ctxt_type_t *p_client; - struct vcd_clnt_ctxt_type_t *p_tmp_client; + struct vcd_clnt_ctxt *client; + struct vcd_clnt_ctxt *tmp_client; VCD_MSG_ERROR("Device init failed. status = %d", status); - p_client = p_drv_ctxt->dev_ctxt.p_cctxt_list_head; - while (p_client) { - p_client->callback(VCD_EVT_RESP_OPEN, - status, NULL, 0, 0, p_client->p_client_data); + client = drv_ctxt->dev_ctxt.cctxt_list_head; + while (client) { + client->callback(VCD_EVT_RESP_OPEN, status, NULL, 0, 0, + client->client_data); - p_tmp_client = p_client; - p_client = p_client->p_next; + tmp_client = client; + client = client->next; - vcd_destroy_client_context(p_tmp_client); + vcd_destroy_client_context(tmp_client); } if (ddl_device_release(NULL)) VCD_MSG_ERROR("Failed: ddl_device_release"); - (void)sched_destroy(p_drv_ctxt->dev_ctxt.sched_hdl); - p_drv_ctxt->dev_ctxt.sched_hdl = NULL; + (void)sched_destroy(drv_ctxt->dev_ctxt.sched_hdl); + drv_ctxt->dev_ctxt.sched_hdl = NULL; - if (vcd_power_event(&p_drv_ctxt->dev_ctxt, - NULL, VCD_EVT_PWR_DEV_INIT_FAIL)) + if (vcd_power_event(&drv_ctxt->dev_ctxt, NULL, + VCD_EVT_PWR_DEV_INIT_FAIL)) VCD_MSG_ERROR("VCD_EVT_PWR_DEV_INIT_FAIL failed"); - vcd_do_device_state_transition(p_drv_ctxt, - VCD_DEVICE_STATE_NOT_INIT, + vcd_do_device_state_transition(drv_ctxt, VCD_DEVICE_STATE_NOT_INIT, DEVICE_STATE_EVENT_NUMBER(pf_dev_cb)); } -u32 vcd_deinit_device_context(struct vcd_drv_ctxt_type_t *p_drv_ctxt, - u32 n_ev_code) +u32 vcd_deinit_device_context(struct vcd_drv_ctxt *drv_ctxt, u32 ev_code) { - struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; u32 rc = VCD_S_SUCCESS; VCD_MSG_LOW("vcd_deinit_device_context:"); - rc = vcd_power_event(&p_drv_ctxt->dev_ctxt, NULL, - VCD_EVT_PWR_DEV_TERM_BEGIN); + rc = vcd_power_event(&drv_ctxt->dev_ctxt, NULL, + VCD_EVT_PWR_DEV_TERM_BEGIN); VCD_FAILED_RETURN(rc, "VCD_EVT_PWR_DEV_TERM_BEGIN failed"); @@ -282,195 +253,185 @@ u32 vcd_deinit_device_context(struct vcd_drv_ctxt_type_t *p_drv_ctxt, if (VCD_FAILED(rc)) { VCD_MSG_ERROR("rc = 0x%x. Failed: ddl_device_release", rc); - (void)vcd_power_event(p_dev_ctxt, NULL, - VCD_EVT_PWR_DEV_TERM_FAIL); + vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_TERM_FAIL); } else { - (void)sched_destroy(p_dev_ctxt->sched_hdl); - p_dev_ctxt->sched_hdl = NULL; + sched_destroy(dev_ctxt->sched_hdl); + dev_ctxt->sched_hdl = NULL; - (void) vcd_power_event(p_dev_ctxt, NULL, - VCD_EVT_PWR_DEV_TERM_END); + vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_TERM_END); - vcd_do_device_state_transition(p_drv_ctxt, - VCD_DEVICE_STATE_NOT_INIT, n_ev_code); + vcd_do_device_state_transition(drv_ctxt, + VCD_DEVICE_STATE_NOT_INIT, ev_code); } return rc; } -void vcd_term_driver_context(struct vcd_drv_ctxt_type_t *p_drv_ctxt) +void vcd_term_driver_context(struct vcd_drv_ctxt *drv_ctxt) { - struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; VCD_MSG_HIGH("All driver instances terminated"); - if (p_dev_ctxt->config.pf_deregister_isr) - p_dev_ctxt->config.pf_deregister_isr(); + if (dev_ctxt->config.pf_deregister_isr) + dev_ctxt->config.pf_deregister_isr(); - if (p_dev_ctxt->config.pf_un_map_dev_base_addr) - p_dev_ctxt->config.pf_un_map_dev_base_addr(); + if (dev_ctxt->config.pf_un_map_dev_base_addr) + dev_ctxt->config.pf_un_map_dev_base_addr(); - if (p_dev_ctxt->config.pf_timer_release) - p_dev_ctxt->config.pf_timer_release( - p_dev_ctxt->p_hw_timer_handle); + if (dev_ctxt->config.pf_timer_release) + dev_ctxt->config.pf_timer_release(dev_ctxt->hw_timer_handle); - vcd_free(p_dev_ctxt->a_trans_tbl); + kfree(dev_ctxt->trans_tbl); - memset(p_dev_ctxt, 0, sizeof(struct vcd_dev_ctxt_type)); + memset(dev_ctxt, 0, sizeof(struct vcd_dev_ctxt)); - vcd_do_device_state_transition(p_drv_ctxt, - VCD_DEVICE_STATE_NULL, - DEVICE_STATE_EVENT_NUMBER(pf_term)); + vcd_do_device_state_transition(drv_ctxt, VCD_DEVICE_STATE_NULL, + DEVICE_STATE_EVENT_NUMBER(pf_term)); } -u32 vcd_reset_device_context(struct vcd_drv_ctxt_type_t *p_drv_ctxt, - u32 ev_code) +u32 vcd_reset_device_context(struct vcd_drv_ctxt *drv_ctxt, u32 ev_code) { - struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; u32 rc = VCD_S_SUCCESS; VCD_MSG_LOW("vcd_reset_device_context:"); - vcd_reset_device_channels(p_dev_ctxt); - rc = vcd_power_event(&p_drv_ctxt->dev_ctxt, NULL, - VCD_EVT_PWR_DEV_TERM_BEGIN); + vcd_reset_device_channels(dev_ctxt); + rc = vcd_power_event(&drv_ctxt->dev_ctxt, NULL, + VCD_EVT_PWR_DEV_TERM_BEGIN); VCD_FAILED_RETURN(rc, "VCD_EVT_PWR_DEV_TERM_BEGIN failed"); if (ddl_reset_hw(0)) VCD_MSG_HIGH("HW Reset done"); else VCD_MSG_FATAL("HW Reset failed"); - (void)vcd_power_event(p_dev_ctxt, NULL, VCD_EVT_PWR_DEV_TERM_END); + vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_TERM_END); return VCD_S_SUCCESS; } -void vcd_handle_device_err_fatal(struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_clnt_ctxt_type_t *p_trig_clnt) +void vcd_handle_device_err_fatal(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_clnt_ctxt *trig_clnt) { - struct vcd_clnt_ctxt_type_t *p_cctxt = p_dev_ctxt->p_cctxt_list_head; + struct vcd_clnt_ctxt *cctxt = dev_ctxt->cctxt_list_head; VCD_MSG_LOW("vcd_handle_device_err_fatal:"); - while (p_cctxt) { - if (p_cctxt != p_trig_clnt) { - vcd_clnt_handle_device_err_fatal(p_cctxt, + while (cctxt) { + if (cctxt != trig_clnt) { + vcd_clnt_handle_device_err_fatal(cctxt, VCD_EVT_IND_HWERRFATAL); } - p_cctxt = p_cctxt->p_next; + cctxt = cctxt->next; } - p_dev_ctxt->e_pending_cmd = VCD_CMD_DEVICE_RESET; + dev_ctxt->pending_cmd = VCD_CMD_DEVICE_RESET; vcd_do_device_state_transition(vcd_get_drv_context(), - VCD_DEVICE_STATE_INVALID, - DEVICE_STATE_EVENT_NUMBER(pf_dev_cb)); + VCD_DEVICE_STATE_INVALID, DEVICE_STATE_EVENT_NUMBER(pf_dev_cb)); } void vcd_handle_for_last_clnt_close( - struct vcd_dev_ctxt_type *p_dev_ctxt, u32 b_send_deinit) + struct vcd_dev_ctxt *dev_ctxt, u32 send_deinit) { - if (!p_dev_ctxt->p_cctxt_list_head) { + if (!dev_ctxt->cctxt_list_head) { VCD_MSG_HIGH("All clients are closed"); - if (b_send_deinit) - (void) vcd_deinit_device_context( - vcd_get_drv_context(), + if (send_deinit) + vcd_deinit_device_context(vcd_get_drv_context(), DEVICE_STATE_EVENT_NUMBER(pf_close)); else - p_dev_ctxt->e_pending_cmd = - VCD_CMD_DEVICE_TERM; + dev_ctxt->pending_cmd = VCD_CMD_DEVICE_TERM; } } void vcd_continue(void) { - struct vcd_drv_ctxt_type_t *p_drv_ctxt; - struct vcd_dev_ctxt_type *p_dev_ctxt; - u32 b_continue; - struct vcd_transc_type *p_transc; + struct vcd_drv_ctxt *drv_ctxt; + struct vcd_dev_ctxt *dev_ctxt; + u32 cont; + struct vcd_transc *transc; u32 rc; VCD_MSG_LOW("vcd_continue:"); - p_drv_ctxt = vcd_get_drv_context(); - p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + drv_ctxt = vcd_get_drv_context(); + dev_ctxt = &drv_ctxt->dev_ctxt; - p_dev_ctxt->b_continue = FALSE; + dev_ctxt->cont = false; - if (p_dev_ctxt->e_pending_cmd == VCD_CMD_DEVICE_INIT) { + if (dev_ctxt->pending_cmd == VCD_CMD_DEVICE_INIT) { VCD_MSG_HIGH("VCD_CMD_DEVICE_INIT is pending"); - p_dev_ctxt->e_pending_cmd = VCD_CMD_NONE; + dev_ctxt->pending_cmd = VCD_CMD_NONE; - (void)vcd_init_device_context(p_drv_ctxt, + vcd_init_device_context(drv_ctxt, DEVICE_STATE_EVENT_NUMBER(pf_open)); - } else if (p_dev_ctxt->e_pending_cmd == VCD_CMD_DEVICE_TERM) { + } else if (dev_ctxt->pending_cmd == VCD_CMD_DEVICE_TERM) { VCD_MSG_HIGH("VCD_CMD_DEVICE_TERM is pending"); - p_dev_ctxt->e_pending_cmd = VCD_CMD_NONE; + dev_ctxt->pending_cmd = VCD_CMD_NONE; - (void)vcd_deinit_device_context(p_drv_ctxt, + vcd_deinit_device_context(drv_ctxt, DEVICE_STATE_EVENT_NUMBER(pf_close)); - } else if (p_dev_ctxt->e_pending_cmd == VCD_CMD_DEVICE_RESET) { + } else if (dev_ctxt->pending_cmd == VCD_CMD_DEVICE_RESET) { VCD_MSG_HIGH("VCD_CMD_DEVICE_RESET is pending"); - p_dev_ctxt->e_pending_cmd = VCD_CMD_NONE; - (void)vcd_reset_device_context(p_drv_ctxt, + dev_ctxt->pending_cmd = VCD_CMD_NONE; + vcd_reset_device_context(drv_ctxt, DEVICE_STATE_EVENT_NUMBER(pf_dev_cb)); } else { - if (p_dev_ctxt->b_set_perf_lvl_pending) { - rc = vcd_power_event(p_dev_ctxt, NULL, - VCD_EVT_PWR_DEV_SET_PERFLVL); + if (dev_ctxt->set_perf_lvl_pending) { + rc = vcd_power_event(dev_ctxt, NULL, + VCD_EVT_PWR_DEV_SET_PERFLVL); if (VCD_FAILED(rc)) { VCD_MSG_ERROR ("VCD_EVT_PWR_CLNT_SET_PERFLVL failed"); - VCD_MSG_HIGH - ("Not running at desired perf level." - "curr=%d, reqd=%d", - p_dev_ctxt->n_curr_perf_lvl, - p_dev_ctxt->n_reqd_perf_lvl); + VCD_MSG_HIGH("Not running at desired perf " + "level.curr=%d, reqd=%d", + dev_ctxt->curr_perf_lvl, + dev_ctxt->reqd_perf_lvl); } else { - p_dev_ctxt->b_set_perf_lvl_pending = FALSE; + dev_ctxt->set_perf_lvl_pending = false; } } do { - b_continue = FALSE; + cont = false; - if (vcd_get_command_channel_in_loop - (p_dev_ctxt, &p_transc)) { - if (vcd_submit_command_in_continue(p_dev_ctxt, - p_transc)) - b_continue = TRUE; + if (vcd_get_command_channel_in_loop(dev_ctxt, + &transc)) { + if (vcd_submit_command_in_continue(dev_ctxt, + transc)) + cont = true; else { VCD_MSG_MED ("No more commands to submit"); - vcd_release_command_channel(p_dev_ctxt, - p_transc); + vcd_release_command_channel(dev_ctxt, + transc); - vcd_release_interim_command_channels - (p_dev_ctxt); + vcd_release_interim_command_channels( + dev_ctxt); } } - } while (b_continue); + } while (cont); do { - b_continue = FALSE; + cont = false; - if (vcd_get_frame_channel_in_loop - (p_dev_ctxt, &p_transc)) { - if (vcd_try_submit_frame_in_continue(p_dev_ctxt, - p_transc)) { - b_continue = TRUE; + if (vcd_get_frame_channel_in_loop(dev_ctxt, &transc)) { + if (vcd_try_submit_frame_in_continue(dev_ctxt, + transc)) { + cont = true; } else { VCD_MSG_MED("No more frames to submit"); - vcd_release_frame_channel(p_dev_ctxt, - p_transc); + vcd_release_frame_channel(dev_ctxt, + transc); - vcd_release_interim_frame_channels - (p_dev_ctxt); + vcd_release_interim_frame_channels( + dev_ctxt); } } - } while (b_continue); + } while (cont); - if (!vcd_core_is_busy(p_dev_ctxt)) { - rc = vcd_power_event(p_dev_ctxt, NULL, + if (!vcd_core_is_busy(dev_ctxt)) { + rc = vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_CLNT_CMD_END); if (VCD_FAILED(rc)) @@ -480,70 +441,68 @@ void vcd_continue(void) } } -static void vcd_pause_all_sessions(struct vcd_dev_ctxt_type *p_dev_ctxt) +static void vcd_pause_all_sessions(struct vcd_dev_ctxt *dev_ctxt) { - struct vcd_clnt_ctxt_type_t *p_cctxt = p_dev_ctxt->p_cctxt_list_head; + struct vcd_clnt_ctxt *cctxt = dev_ctxt->cctxt_list_head; u32 rc; - while (p_cctxt) { - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_pause) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_pause(p_cctxt); + while (cctxt) { + if (cctxt->clnt_state.state_table->ev_hdlr.pf_pause) { + rc = cctxt->clnt_state.state_table->ev_hdlr. + pf_pause(cctxt); if (VCD_FAILED(rc)) VCD_MSG_ERROR("Client pause failed"); } - p_cctxt = p_cctxt->p_next; + cctxt = cctxt->next; } } -static void vcd_resume_all_sessions(struct vcd_dev_ctxt_type *p_dev_ctxt) +static void vcd_resume_all_sessions(struct vcd_dev_ctxt *dev_ctxt) { - struct vcd_clnt_ctxt_type_t *p_cctxt = p_dev_ctxt->p_cctxt_list_head; + struct vcd_clnt_ctxt *cctxt = dev_ctxt->cctxt_list_head; u32 rc; - while (p_cctxt) { - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_resume) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_resume(p_cctxt); + while (cctxt) { + if (cctxt->clnt_state.state_table->ev_hdlr.pf_resume) { + rc = cctxt->clnt_state.state_table->ev_hdlr. + pf_resume(cctxt); if (VCD_FAILED(rc)) VCD_MSG_ERROR("Client resume failed"); } - p_cctxt = p_cctxt->p_next; + cctxt = cctxt->next; } } -static u32 vcd_init_cmn - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - struct vcd_init_config_type *p_config, s32 *p_driver_handle) +static u32 vcd_init_cmn(struct vcd_drv_ctxt *drv_ctxt, + struct vcd_init_config *config, s32 *driver_handle) { - struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; s32 driver_id; - if (p_dev_ctxt->config.pf_interrupt_clr != - p_config->pf_interrupt_clr - || p_dev_ctxt->config.pf_register_isr != - p_config->pf_register_isr - || p_dev_ctxt->config.pf_deregister_isr != - p_config->pf_deregister_isr - || p_dev_ctxt->config.pf_map_dev_base_addr != - p_config->pf_map_dev_base_addr - || p_dev_ctxt->config.pf_un_map_dev_base_addr != - p_config->pf_un_map_dev_base_addr) { + if (dev_ctxt->config.pf_interrupt_clr != config->pf_interrupt_clr || + dev_ctxt->config.pf_register_isr != + config->pf_register_isr || + dev_ctxt->config.pf_deregister_isr != + config->pf_deregister_isr || + dev_ctxt->config.pf_map_dev_base_addr != + config->pf_map_dev_base_addr || + dev_ctxt->config.pf_un_map_dev_base_addr != + config->pf_un_map_dev_base_addr) { VCD_MSG_ERROR("Device config mismatch"); VCD_MSG_HIGH("VCD will be using config from 1st vcd_init"); } - *p_driver_handle = 0; + *driver_handle = 0; driver_id = 0; while (driver_id < VCD_DRIVER_INSTANCE_MAX && - p_dev_ctxt->b_driver_ids[driver_id]) { + dev_ctxt->driver_ids[driver_id]) { ++driver_id; } @@ -553,47 +512,42 @@ static u32 vcd_init_cmn return VCD_ERR_FAIL; } - ++p_dev_ctxt->n_refs; - p_dev_ctxt->b_driver_ids[driver_id] = TRUE; - *p_driver_handle = driver_id + 1; + ++dev_ctxt->refs; + dev_ctxt->driver_ids[driver_id] = true; + *driver_handle = driver_id + 1; VCD_MSG_HIGH("Driver_id = %d. No of driver instances = %d", - driver_id, p_dev_ctxt->n_refs); + driver_id, dev_ctxt->refs); return VCD_S_SUCCESS; } -static u32 vcd_init_in_null - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - struct vcd_init_config_type *p_config, s32 *p_driver_handle) { +static u32 vcd_init_in_null(struct vcd_drv_ctxt *drv_ctxt, + struct vcd_init_config *config, s32 *driver_handle) { u32 rc = VCD_S_SUCCESS; - struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; - u32 b_done_create_timer = FALSE; + struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; + u32 done_create_timer = false; VCD_MSG_LOW("vcd_init_in_dev_null:"); + dev_ctxt->config = *config; - p_dev_ctxt->config = *p_config; - - p_dev_ctxt->p_device_base_addr = - (u8 *)p_config->pf_map_dev_base_addr( - p_dev_ctxt->config.p_device_name); + dev_ctxt->device_base_addr = (u8 *)config->pf_map_dev_base_addr( + dev_ctxt->config.device_name); - if (!p_dev_ctxt->p_device_base_addr) { + if (!dev_ctxt->device_base_addr) { VCD_MSG_ERROR("NULL Device_base_addr"); return VCD_ERR_FAIL; } - if (p_config->pf_register_isr) { - p_config->pf_register_isr(p_dev_ctxt->config. - p_device_name); - } + if (config->pf_register_isr) + config->pf_register_isr(dev_ctxt->config.device_name); - if (p_config->pf_timer_create) { - if (p_config->pf_timer_create(vcd_hw_timeout_handler, - NULL, &p_dev_ctxt->p_hw_timer_handle)) - b_done_create_timer = TRUE; + if (config->pf_timer_create) { + if (config->pf_timer_create(vcd_hw_timeout_handler, NULL, + &dev_ctxt->hw_timer_handle)) + done_create_timer = true; else { VCD_MSG_ERROR("timercreate failed"); return VCD_ERR_FAIL; @@ -601,312 +555,287 @@ static u32 vcd_init_in_null } - rc = vcd_init_cmn(p_drv_ctxt, p_config, p_driver_handle); + rc = vcd_init_cmn(drv_ctxt, config, driver_handle); if (!VCD_FAILED(rc)) { - vcd_do_device_state_transition(p_drv_ctxt, - VCD_DEVICE_STATE_NOT_INIT, - DEVICE_STATE_EVENT_NUMBER - (pf_init)); + vcd_do_device_state_transition(drv_ctxt, + VCD_DEVICE_STATE_NOT_INIT, + DEVICE_STATE_EVENT_NUMBER(pf_init)); } else { - if (p_dev_ctxt->config.pf_un_map_dev_base_addr) - p_dev_ctxt->config.pf_un_map_dev_base_addr(); + if (dev_ctxt->config.pf_un_map_dev_base_addr) + dev_ctxt->config.pf_un_map_dev_base_addr(); - if (p_dev_ctxt->config.pf_deregister_isr) - p_dev_ctxt->config.pf_deregister_isr(); - - if (b_done_create_timer && p_dev_ctxt->config.pf_timer_release) - p_dev_ctxt->config.pf_timer_release(p_dev_ctxt-> - p_hw_timer_handle); + if (dev_ctxt->config.pf_deregister_isr) + dev_ctxt->config.pf_deregister_isr(); + if (done_create_timer && dev_ctxt->config.pf_timer_release) + dev_ctxt->config.pf_timer_release( + dev_ctxt->hw_timer_handle); } return rc; } -static u32 vcd_init_in_not_init - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - struct vcd_init_config_type *p_config, s32 *p_driver_handle) -{ +u32 npelly_init(void); +static u32 vcd_init_in_not_init(struct vcd_drv_ctxt *drv_ctxt, + struct vcd_init_config *config, s32 *driver_handle) +{ + u32 rc; VCD_MSG_LOW("vcd_init_in_dev_not_init:"); - return vcd_init_cmn(p_drv_ctxt, p_config, p_driver_handle); + rc = npelly_init(); -} + if (rc) + return rc; + return vcd_init_cmn(drv_ctxt, config, driver_handle); -static u32 vcd_init_in_initing - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - struct vcd_init_config_type *p_config, s32 *p_driver_handle) { +} +static u32 vcd_init_in_initing(struct vcd_drv_ctxt *drv_ctxt, + struct vcd_init_config *config, s32 *driver_handle) +{ VCD_MSG_LOW("vcd_init_in_dev_initing:"); - return vcd_init_cmn(p_drv_ctxt, p_config, p_driver_handle); + return vcd_init_cmn(drv_ctxt, config, driver_handle); } -static u32 vcd_init_in_ready - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - struct vcd_init_config_type *p_config, s32 *p_driver_handle) +static u32 vcd_init_in_ready(struct vcd_drv_ctxt *drv_ctxt, + struct vcd_init_config *config, s32 *driver_handle) { VCD_MSG_LOW("vcd_init_in_dev_ready:"); - return vcd_init_cmn(p_drv_ctxt, p_config, p_driver_handle); + return vcd_init_cmn(drv_ctxt, config, driver_handle); } -static u32 vcd_term_cmn - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 driver_handle) +static u32 vcd_term_cmn(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle) { - struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; - if (!vcd_validate_driver_handle(p_dev_ctxt, driver_handle)) { + if (!vcd_validate_driver_handle(dev_ctxt, driver_handle)) { VCD_MSG_ERROR("Invalid driver handle = %d", driver_handle); return VCD_ERR_BAD_HANDLE; } - if (vcd_check_for_client_context(p_dev_ctxt, - driver_handle - 1)) { + if (vcd_check_for_client_context(dev_ctxt, driver_handle - 1)) { VCD_MSG_ERROR("Driver has active client"); return VCD_ERR_BAD_STATE; } - --p_dev_ctxt->n_refs; - p_dev_ctxt->b_driver_ids[driver_handle - 1] = FALSE; + --dev_ctxt->refs; + dev_ctxt->driver_ids[driver_handle - 1] = false; VCD_MSG_HIGH("Driver_id %d terminated. No of driver instances = %d", - driver_handle - 1, p_dev_ctxt->n_refs); + driver_handle - 1, dev_ctxt->refs); return VCD_S_SUCCESS; } -static u32 vcd_term_in_not_init - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 driver_handle) +static u32 vcd_term_in_not_init(struct vcd_drv_ctxt *drv_ctxt, + s32 driver_handle) { - struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; u32 rc; VCD_MSG_LOW("vcd_term_in_dev_not_init:"); - rc = vcd_term_cmn(p_drv_ctxt, driver_handle); + rc = vcd_term_cmn(drv_ctxt, driver_handle); - if (!VCD_FAILED(rc) && !p_dev_ctxt->n_refs) - vcd_term_driver_context(p_drv_ctxt); + if (!VCD_FAILED(rc) && !dev_ctxt->refs) + vcd_term_driver_context(drv_ctxt); return rc; } -static u32 vcd_term_in_initing - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 driver_handle) +static u32 vcd_term_in_initing(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle) { VCD_MSG_LOW("vcd_term_in_dev_initing:"); - return vcd_term_cmn(p_drv_ctxt, driver_handle); + return vcd_term_cmn(drv_ctxt, driver_handle); } -static u32 vcd_term_in_ready - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 driver_handle) +static u32 vcd_term_in_ready(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle) { VCD_MSG_LOW("vcd_term_in_dev_ready:"); - return vcd_term_cmn(p_drv_ctxt, driver_handle); + return vcd_term_cmn(drv_ctxt, driver_handle); } -static u32 vcd_term_in_invalid(struct vcd_drv_ctxt_type_t *p_drv_ctxt, - s32 driver_handle) +static u32 vcd_term_in_invalid(struct vcd_drv_ctxt *drv_ctxt, + s32 driver_handle) { u32 rc; VCD_MSG_LOW("vcd_term_in_invalid:"); - rc = vcd_term_cmn(p_drv_ctxt, driver_handle); - if (!VCD_FAILED(rc) && !p_drv_ctxt->dev_ctxt.n_refs) - vcd_term_driver_context(p_drv_ctxt); + rc = vcd_term_cmn(drv_ctxt, driver_handle); + if (!VCD_FAILED(rc) && !drv_ctxt->dev_ctxt.refs) + vcd_term_driver_context(drv_ctxt); return rc; } -static u32 vcd_open_cmn - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - s32 driver_handle, - u32 b_decoding, - void (*callback) (u32 event, u32 status, void *p_info, u32 n_size, - void *handle, void *const p_client_data), - void *p_client_data, struct vcd_clnt_ctxt_type_t ** pp_cctxt) +static u32 vcd_open_cmn(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle, + u32 decoding, void (*callback) (u32 event, u32 status, void *info, + u32 size, void *handle, void *const client_data), void *client_data, + struct vcd_clnt_ctxt **pp_cctxt) { - struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; - struct vcd_clnt_ctxt_type_t *p_cctxt; - struct vcd_clnt_ctxt_type_t *p_client; + struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; + struct vcd_clnt_ctxt *cctxt; + struct vcd_clnt_ctxt *client; - if (!vcd_validate_driver_handle(p_dev_ctxt, driver_handle)) { + if (!vcd_validate_driver_handle(dev_ctxt, driver_handle)) { VCD_MSG_ERROR("Invalid driver handle = %d", driver_handle); return VCD_ERR_BAD_HANDLE; } - p_cctxt = - (struct vcd_clnt_ctxt_type_t *) - vcd_malloc(sizeof(struct vcd_clnt_ctxt_type_t)); - if (!p_cctxt) { + cctxt = kzalloc(sizeof(struct vcd_clnt_ctxt), GFP_KERNEL); + if (!cctxt) { VCD_MSG_ERROR("No memory for client ctxt"); - return VCD_ERR_ALLOC_FAIL; } - memset(p_cctxt, 0, sizeof(struct vcd_clnt_ctxt_type_t)); - p_cctxt->p_dev_ctxt = p_dev_ctxt; - p_cctxt->driver_id = driver_handle - 1; - p_cctxt->b_decoding = b_decoding; - p_cctxt->callback = callback; - p_cctxt->p_client_data = p_client_data; + cctxt->dev_ctxt = dev_ctxt; + cctxt->driver_id = driver_handle - 1; + cctxt->decoding = decoding; + cctxt->callback = callback; + cctxt->client_data = client_data; - p_client = p_dev_ctxt->p_cctxt_list_head; - p_dev_ctxt->p_cctxt_list_head = p_cctxt; - p_cctxt->p_next = p_client; + client = dev_ctxt->cctxt_list_head; + dev_ctxt->cctxt_list_head = cctxt; + cctxt->next = client; - *pp_cctxt = p_cctxt; + *pp_cctxt = cctxt; return VCD_S_SUCCESS; } -static u32 vcd_open_in_not_init - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - s32 driver_handle, - u32 b_decoding, - void (*callback) (u32 event, u32 status, void *p_info, u32 n_size, - void *handle, void *const p_client_data), - void *p_client_data) +static u32 vcd_open_in_not_init(struct vcd_drv_ctxt *drv_ctxt, + s32 driver_handle, u32 decoding, void (*callback) (u32 event, + u32 status, void *info, u32 size, void *handle, + void *const client_data), void *client_data) { - struct vcd_clnt_ctxt_type_t *p_cctxt; + struct vcd_clnt_ctxt *cctxt; u32 rc; VCD_MSG_LOW("vcd_open_in_dev_not_init:"); - rc = vcd_open_cmn(p_drv_ctxt, driver_handle, b_decoding, callback, - p_client_data, &p_cctxt); + rc = vcd_open_cmn(drv_ctxt, driver_handle, decoding, callback, + client_data, &cctxt); VCD_FAILED_RETURN(rc, "Failed: vcd_open_cmn"); - rc = vcd_init_device_context(p_drv_ctxt, - DEVICE_STATE_EVENT_NUMBER(pf_open)); + rc = vcd_init_device_context(drv_ctxt, + DEVICE_STATE_EVENT_NUMBER(pf_open)); if (VCD_FAILED(rc)) - vcd_destroy_client_context(p_cctxt); + vcd_destroy_client_context(cctxt); return rc; } -static u32 vcd_open_in_initing(struct vcd_drv_ctxt_type_t *p_drv_ctxt, - s32 driver_handle, u32 b_decoding, - void (*callback) (u32 event, u32 status, void *p_info, u32 n_size, - void *handle, void *const p_client_data), - void *p_client_data) +static u32 vcd_open_in_initing(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle, + u32 decoding, void (*callback) (u32 event, u32 status, void *info, + u32 size, void *handle, void *const client_data), void *client_data) { - struct vcd_clnt_ctxt_type_t *p_cctxt; + struct vcd_clnt_ctxt *cctxt; VCD_MSG_LOW("vcd_open_in_dev_initing:"); - return vcd_open_cmn(p_drv_ctxt, driver_handle, b_decoding, callback, - p_client_data, &p_cctxt); + return vcd_open_cmn(drv_ctxt, driver_handle, decoding, callback, + client_data, &cctxt); } -static u32 vcd_open_in_ready - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - s32 driver_handle, - u32 b_decoding, - void (*callback) (u32 event, u32 status, void *p_info, u32 n_size, - void *handle, void *const p_client_data), - void *p_client_data) +static u32 vcd_open_in_ready(struct vcd_drv_ctxt *drv_ctxt, s32 driver_handle, + u32 decoding, void (*callback) (u32 event, u32 status, void *info, + u32 size, void *handle, void *const client_data), void *client_data) { - struct vcd_clnt_ctxt_type_t *p_cctxt; - struct vcd_handle_container_type container; + struct vcd_clnt_ctxt *cctxt; + struct vcd_handle_container container; u32 rc; VCD_MSG_LOW("vcd_open_in_dev_ready:"); - rc = vcd_open_cmn(p_drv_ctxt, driver_handle, b_decoding, callback, - p_client_data, &p_cctxt); + rc = vcd_open_cmn(drv_ctxt, driver_handle, decoding, callback, + client_data, &cctxt); VCD_FAILED_RETURN(rc, "Failed: vcd_open_cmn"); - rc = vcd_init_client_context(p_cctxt); + rc = vcd_init_client_context(cctxt); if (!VCD_FAILED(rc)) { - container.handle = (void *)p_cctxt; + container.handle = (void *)cctxt; - callback(VCD_EVT_RESP_OPEN, - VCD_S_SUCCESS, - &container, - sizeof(container), container.handle, p_client_data); + callback(VCD_EVT_RESP_OPEN, VCD_S_SUCCESS, &container, + sizeof(container), container.handle, client_data); } else { VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_init_client_context", rc); - vcd_destroy_client_context(p_cctxt); + vcd_destroy_client_context(cctxt); } return rc; } -static u32 vcd_close_in_ready - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - struct vcd_clnt_ctxt_type_t *p_cctxt) { +static u32 vcd_close_in_ready(struct vcd_drv_ctxt *drv_ctxt, + struct vcd_clnt_ctxt *cctxt) { u32 rc; VCD_MSG_LOW("vcd_close_in_dev_ready:"); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_close) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_close(p_cctxt); + if (cctxt->clnt_state.state_table->ev_hdlr.pf_close) { + rc = cctxt->clnt_state.state_table->ev_hdlr. + pf_close(cctxt); } else { VCD_MSG_ERROR("Unsupported API in client state %d", - p_cctxt->clnt_state.e_state); + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } if (!VCD_FAILED(rc)) - vcd_handle_for_last_clnt_close(&p_drv_ctxt->dev_ctxt, TRUE); + vcd_handle_for_last_clnt_close(&drv_ctxt->dev_ctxt, true); return rc; } -static u32 vcd_close_in_dev_invalid(struct vcd_drv_ctxt_type_t *p_drv_ctxt, - struct vcd_clnt_ctxt_type_t *p_cctxt) +static u32 vcd_close_in_dev_invalid(struct vcd_drv_ctxt *drv_ctxt, + struct vcd_clnt_ctxt *cctxt) { u32 rc; VCD_MSG_LOW("vcd_close_in_dev_invalid:"); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_close) { - rc = p_cctxt->clnt_state.p_state_table-> - ev_hdlr.pf_close(p_cctxt); + if (cctxt->clnt_state.state_table->ev_hdlr.pf_close) { + rc = cctxt->clnt_state.state_table->ev_hdlr.pf_close(cctxt); } else { VCD_MSG_ERROR("Unsupported API in client state %d", - p_cctxt->clnt_state.e_state); + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } - if (!VCD_FAILED(rc) && !p_drv_ctxt->dev_ctxt. - p_cctxt_list_head) { + if (!VCD_FAILED(rc) && !drv_ctxt->dev_ctxt.cctxt_list_head) { VCD_MSG_HIGH("All INVALID clients are closed"); - vcd_do_device_state_transition(p_drv_ctxt, + vcd_do_device_state_transition(drv_ctxt, VCD_DEVICE_STATE_NOT_INIT, DEVICE_STATE_EVENT_NUMBER(pf_close)); } return rc; } -static u32 vcd_resume_in_ready - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - struct vcd_clnt_ctxt_type_t *p_cctxt) { +static u32 vcd_resume_in_ready(struct vcd_drv_ctxt *drv_ctxt, + struct vcd_clnt_ctxt *cctxt) +{ u32 rc = VCD_S_SUCCESS; VCD_MSG_LOW("vcd_resume_in_ready:"); - if (p_cctxt->clnt_state.p_state_table->ev_hdlr.pf_resume) { - rc = p_cctxt->clnt_state.p_state_table->ev_hdlr. - pf_resume(p_cctxt); + if (cctxt->clnt_state.state_table->ev_hdlr.pf_resume) { + rc = cctxt->clnt_state.state_table->ev_hdlr.pf_resume(cctxt); } else { VCD_MSG_ERROR("Unsupported API in client state %d", - p_cctxt->clnt_state.e_state); + cctxt->clnt_state.state); rc = VCD_ERR_BAD_STATE; } @@ -914,287 +843,249 @@ static u32 vcd_resume_in_ready return rc; } -static u32 vcd_set_dev_pwr_in_ready - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - enum vcd_power_state_type e_pwr_state) +static u32 vcd_set_dev_pwr_in_ready(struct vcd_drv_ctxt *drv_ctxt, + enum vcd_power_state pwr_state) { u32 rc = VCD_S_SUCCESS; - struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; VCD_MSG_LOW("vcd_set_dev_pwr_in_ready:"); - switch (e_pwr_state) { + switch (pwr_state) { case VCD_PWR_STATE_SLEEP: - { - vcd_pause_all_sessions(p_dev_ctxt); - - p_dev_ctxt->e_pwr_state = VCD_PWR_STATE_SLEEP; + vcd_pause_all_sessions(dev_ctxt); - break; - } + dev_ctxt->pwr_state = VCD_PWR_STATE_SLEEP; + break; case VCD_PWR_STATE_ON: - { - if (p_dev_ctxt->e_pwr_state == VCD_PWR_STATE_SLEEP) - vcd_resume_all_sessions(p_dev_ctxt); - - - p_dev_ctxt->e_pwr_state = VCD_PWR_STATE_ON; - - break; - } + if (dev_ctxt->pwr_state == VCD_PWR_STATE_SLEEP) + vcd_resume_all_sessions(dev_ctxt); + dev_ctxt->pwr_state = VCD_PWR_STATE_ON; + break; default: - { - VCD_MSG_ERROR("Invalid power state requested %d", - e_pwr_state); - break; - } - + VCD_MSG_ERROR("Invalid power state requested %d", + pwr_state); + break; } - return rc; } -static void vcd_dev_cb_in_initing - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - u32 event, - u32 status, - void *p_payload, u32 size, u32 *ddl_handle, void *const p_client_data) +static void vcd_dev_cb_in_initing(struct vcd_drv_ctxt *drv_ctxt, u32 event, + u32 status, void *payload, u32 size, u32 *ddl_handle, + void *const client_data) { - struct vcd_dev_ctxt_type *p_dev_ctxt; - struct vcd_clnt_ctxt_type_t *p_client; - struct vcd_clnt_ctxt_type_t *p_tmp_client; - struct vcd_handle_container_type container; + struct vcd_dev_ctxt *dev_ctxt; + struct vcd_clnt_ctxt *client; + struct vcd_clnt_ctxt *tmp_client; + struct vcd_handle_container container; u32 rc = VCD_S_SUCCESS; - u32 b_client_inited = FALSE; - u32 b_fail_all_open = FALSE; + u32 client_inited = false; + u32 fail_all_open = false; VCD_MSG_LOW("vcd_dev_cb_in_initing:"); if (event != VCD_EVT_RESP_DEVICE_INIT) { VCD_MSG_ERROR("vcd_dev_cb_in_initing: Unexpected event %d", - (int)event); + (int)event); return; } - p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + dev_ctxt = &drv_ctxt->dev_ctxt; - p_dev_ctxt->b_continue = FALSE; + dev_ctxt->cont = false; if (VCD_FAILED(status)) { - vcd_handle_device_init_failed(p_drv_ctxt, status); + vcd_handle_device_init_failed(drv_ctxt, status); return; } - vcd_do_device_state_transition(p_drv_ctxt, - VCD_DEVICE_STATE_READY, - DEVICE_STATE_EVENT_NUMBER(pf_open)); + vcd_do_device_state_transition(drv_ctxt, VCD_DEVICE_STATE_READY, + DEVICE_STATE_EVENT_NUMBER(pf_open)); - if (!p_dev_ctxt->p_cctxt_list_head) { + if (!dev_ctxt->cctxt_list_head) { VCD_MSG_HIGH("All clients are closed"); - p_dev_ctxt->e_pending_cmd = VCD_CMD_DEVICE_TERM; + dev_ctxt->pending_cmd = VCD_CMD_DEVICE_TERM; return; } - if (!p_dev_ctxt->n_ddl_cmd_ch_depth - || !p_dev_ctxt->a_trans_tbl) - rc = vcd_setup_with_ddl_capabilities(p_dev_ctxt); + if (!dev_ctxt->ddl_cmd_ch_depth || !dev_ctxt->trans_tbl) + rc = vcd_setup_with_ddl_capabilities(dev_ctxt); if (VCD_FAILED(rc)) { - VCD_MSG_ERROR - ("rc = 0x%x: Failed vcd_setup_with_ddl_capabilities", - rc); + VCD_MSG_ERROR("rc = 0x%x: Failed " + "vcd_setup_with_ddl_capabilities", rc); - b_fail_all_open = TRUE; + fail_all_open = true; } - p_client = p_dev_ctxt->p_cctxt_list_head; - while (p_client) { - if (!b_fail_all_open) - rc = vcd_init_client_context(p_client); + client = dev_ctxt->cctxt_list_head; + while (client) { + if (!fail_all_open) + rc = vcd_init_client_context(client); if (!VCD_FAILED(rc)) { - container.handle = (void *)p_client; - p_client->callback(VCD_EVT_RESP_OPEN, - VCD_S_SUCCESS, - &container, - sizeof(container), - container.handle, - p_client->p_client_data); + container.handle = (void *)client; + client->callback(VCD_EVT_RESP_OPEN, VCD_S_SUCCESS, + &container, sizeof(container), container.handle, + client->client_data); - p_client = p_client->p_next; + client = client->next; - b_client_inited = TRUE; + client_inited = true; } else { - VCD_MSG_ERROR - ("rc = 0x%x, Failed: vcd_init_client_context", - rc); + VCD_MSG_ERROR("rc = 0x%x, Failed: " + "vcd_init_client_context", rc); - p_client->callback(VCD_EVT_RESP_OPEN, - rc, - NULL, 0, 0, p_client->p_client_data); + client->callback(VCD_EVT_RESP_OPEN, rc, NULL, 0, 0, + client->client_data); - p_tmp_client = p_client; - p_client = p_client->p_next; + tmp_client = client; + client = client->next; - vcd_destroy_client_context(p_tmp_client); + vcd_destroy_client_context(tmp_client); } } - if (!b_client_inited || b_fail_all_open) { + if (!client_inited || fail_all_open) { VCD_MSG_ERROR("All client open requests failed"); - p_dev_ctxt->e_pending_cmd = VCD_CMD_DEVICE_TERM; - } else { - if (vcd_power_event(p_dev_ctxt, NULL, - VCD_EVT_PWR_DEV_INIT_END)) { - VCD_MSG_ERROR("VCD_EVT_PWR_DEV_INIT_END failed"); - } + dev_ctxt->pending_cmd = VCD_CMD_DEVICE_TERM; + } else if (vcd_power_event(dev_ctxt, NULL, VCD_EVT_PWR_DEV_INIT_END)) { + VCD_MSG_ERROR("VCD_EVT_PWR_DEV_INIT_END failed"); } } -static void vcd_hw_timeout_cmn(struct vcd_drv_ctxt_type_t *p_drv_ctxt, - void *p_user_data) +static void vcd_hw_timeout_cmn(struct vcd_drv_ctxt *drv_ctxt, void *user_data) { - struct vcd_dev_ctxt_type *p_dev_ctxt = &p_drv_ctxt->dev_ctxt; + struct vcd_dev_ctxt *dev_ctxt = &drv_ctxt->dev_ctxt; VCD_MSG_LOW("vcd_hw_timeout_cmn:"); - vcd_device_timer_stop(p_dev_ctxt); + vcd_device_timer_stop(dev_ctxt); - vcd_handle_device_err_fatal(p_dev_ctxt, NULL); + vcd_handle_device_err_fatal(dev_ctxt, NULL); /* Reset HW. */ - (void) vcd_reset_device_context(p_drv_ctxt, - DEVICE_STATE_EVENT_NUMBER(pf_timeout)); + vcd_reset_device_context(drv_ctxt, DEVICE_STATE_EVENT_NUMBER( + pf_timeout)); } -static void vcd_dev_enter_null - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 n_state_event_type) { - VCD_MSG_MED("Entering DEVICE_STATE_NULL on api %d", n_state_event_type); - +static void vcd_dev_enter_null(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) +{ + VCD_MSG_MED("Entering DEVICE_STATE_NULL on api %d", ev_code); } -static void vcd_dev_enter_not_init - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 n_state_event_type) { - VCD_MSG_MED("Entering DEVICE_STATE_NOT_INIT on api %d", - n_state_event_type); - +static void vcd_dev_enter_not_init(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) +{ + VCD_MSG_MED("Entering DEVICE_STATE_NOT_INIT on api %d", ev_code); } -static void vcd_dev_enter_initing - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 n_state_event_type) { - VCD_MSG_MED("Entering DEVICE_STATE_INITING on api %d", - n_state_event_type); - +static void vcd_dev_enter_initing(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) +{ + VCD_MSG_MED("Entering DEVICE_STATE_INITING on api %d", ev_code); } -static void vcd_dev_enter_ready - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 n_state_event_type) { - VCD_MSG_MED("Entering DEVICE_STATE_READY on api %d", - n_state_event_type); +static void vcd_dev_enter_ready(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) +{ + VCD_MSG_MED("Entering DEVICE_STATE_READY on api %d", ev_code); } -static void vcd_dev_enter_invalid(struct vcd_drv_ctxt_type_t *p_drv_ctxt, - s32 state_event_type) +static void vcd_dev_enter_invalid(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) { - VCD_MSG_MED("Entering DEVICE_STATE_INVALID on api %d", state_event_type); + VCD_MSG_MED("Entering DEVICE_STATE_INVALID on api %d", ev_code); } -static void vcd_dev_exit_null - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 n_state_event_type) { - VCD_MSG_MED("Exiting DEVICE_STATE_NULL on api %d", n_state_event_type); +static void vcd_dev_exit_null(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) +{ + VCD_MSG_MED("Exiting DEVICE_STATE_NULL on api %d", ev_code); } -static void vcd_dev_exit_not_init - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 n_state_event_type) { - VCD_MSG_MED("Exiting DEVICE_STATE_NOT_INIT on api %d", - n_state_event_type); - +static void vcd_dev_exit_not_init(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) +{ + VCD_MSG_MED("Exiting DEVICE_STATE_NOT_INIT on api %d", ev_code); } -static void vcd_dev_exit_initing - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 n_state_event_type) { - VCD_MSG_MED("Exiting DEVICE_STATE_INITING on api %d", - n_state_event_type); +static void vcd_dev_exit_initing(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) +{ + VCD_MSG_MED("Exiting DEVICE_STATE_INITING on api %d", ev_code); } -static void vcd_dev_exit_ready - (struct vcd_drv_ctxt_type_t *p_drv_ctxt, s32 n_state_event_type) { - VCD_MSG_MED("Exiting DEVICE_STATE_READY on api %d", n_state_event_type); +static void vcd_dev_exit_ready(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) +{ + VCD_MSG_MED("Exiting DEVICE_STATE_READY on api %d", ev_code); } -static void vcd_dev_exit_invalid(struct vcd_drv_ctxt_type_t *p_drv_ctxt, - s32 state_event_type) +static void vcd_dev_exit_invalid(struct vcd_drv_ctxt *drv_ctxt, s32 ev_code) { - VCD_MSG_MED("Exiting DEVICE_STATE_INVALID on api %d", state_event_type); + VCD_MSG_MED("Exiting DEVICE_STATE_INVALID on api %d", ev_code); } -static const struct vcd_dev_state_table_type_t vcd_dev_table_null = { +static const struct vcd_dev_state_table vcd_dev_table_null = { { - vcd_init_in_null, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - }, + vcd_init_in_null, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + }, vcd_dev_enter_null, vcd_dev_exit_null }; -static const struct vcd_dev_state_table_type_t vcd_dev_table_not_init = { +static const struct vcd_dev_state_table vcd_dev_table_not_init = { { - vcd_init_in_not_init, - vcd_term_in_not_init, - vcd_open_in_not_init, - NULL, - NULL, - NULL, - NULL, - NULL, - }, + vcd_init_in_not_init, + vcd_term_in_not_init, + vcd_open_in_not_init, + NULL, + NULL, + NULL, + NULL, + NULL, + }, vcd_dev_enter_not_init, vcd_dev_exit_not_init }; -static const struct vcd_dev_state_table_type_t vcd_dev_table_initing = { +static const struct vcd_dev_state_table vcd_dev_table_initing = { { - vcd_init_in_initing, - vcd_term_in_initing, - vcd_open_in_initing, - NULL, - NULL, - NULL, - vcd_dev_cb_in_initing, - vcd_hw_timeout_cmn, - }, + vcd_init_in_initing, + vcd_term_in_initing, + vcd_open_in_initing, + NULL, + NULL, + NULL, + vcd_dev_cb_in_initing, + vcd_hw_timeout_cmn, + }, vcd_dev_enter_initing, vcd_dev_exit_initing }; -static const struct vcd_dev_state_table_type_t vcd_dev_table_ready = { +static const struct vcd_dev_state_table vcd_dev_table_ready = { { - vcd_init_in_ready, - vcd_term_in_ready, - vcd_open_in_ready, - vcd_close_in_ready, - vcd_resume_in_ready, - vcd_set_dev_pwr_in_ready, - NULL, - vcd_hw_timeout_cmn, - }, + vcd_init_in_ready, + vcd_term_in_ready, + vcd_open_in_ready, + vcd_close_in_ready, + vcd_resume_in_ready, + vcd_set_dev_pwr_in_ready, + NULL, + vcd_hw_timeout_cmn, + }, vcd_dev_enter_ready, vcd_dev_exit_ready }; -static const struct vcd_dev_state_table_type_t vcd_dev_table_in_invalid = { +static const struct vcd_dev_state_table vcd_dev_table_in_invalid = { { NULL, vcd_term_in_invalid, @@ -1209,7 +1100,7 @@ static const struct vcd_dev_state_table_type_t vcd_dev_table_in_invalid = { vcd_dev_exit_invalid }; -static const struct vcd_dev_state_table_type_t *vcd_dev_state_table[] = { +static const struct vcd_dev_state_table *vcd_dev_state_table[] = { &vcd_dev_table_null, &vcd_dev_table_not_init, &vcd_dev_table_initing, diff --git a/drivers/misc/video_core/720p/vcd/vcd_device_sm.h b/drivers/misc/video_core/720p/vcd/vcd_device_sm.h index 350e763dc0669..0ba70d25b949a 100644 --- a/drivers/misc/video_core/720p/vcd/vcd_device_sm.h +++ b/drivers/misc/video_core/720p/vcd/vcd_device_sm.h @@ -33,11 +33,11 @@ #include "vcd_ddl_api.h" #include "vcd_core.h" -struct vcd_dev_state_table_type_t; -struct vcd_dev_state_ctxt_type_t; -struct vcd_drv_ctxt_type_t; +struct vcd_dev_state_table; +struct vcd_dev_state_ctxt; +struct vcd_drv_ctxt; -enum vcd_dev_state_enum_type { +enum vcd_dev_state_enum { VCD_DEVICE_STATE_NULL = 0, VCD_DEVICE_STATE_NOT_INIT, VCD_DEVICE_STATE_INITING, @@ -47,67 +47,59 @@ enum vcd_dev_state_enum_type { VCD_DEVICE_STATE_32BIT = 0x7FFFFFFF }; -struct vcd_dev_state_table_type_t { +struct vcd_dev_state_table { struct { - u32(*pf_init) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - struct vcd_init_config_type *p_config, - s32 *p_driver_handle); + u32(*pf_init) (struct vcd_drv_ctxt *drv_ctxt, + struct vcd_init_config *config, s32 *driver_handle); - u32(*pf_term) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - s32 driver_handle); + u32(*pf_term) (struct vcd_drv_ctxt *drv_ctxt, + s32 driver_handle); - u32(*pf_open) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - s32 driver_handle, u32 b_decoding, - void (*callback) (u32 event, u32 status, - void *p_info, u32 n_size, void *handle, - void *const p_client_data), - void *p_client_data); + u32(*pf_open) (struct vcd_drv_ctxt *drv_ctxt, + s32 driver_handle, u32 decoding, + void (*callback) (u32 event, u32 status, void *info, + u32 size, void *handle, void *const client_data), + void *client_data); - u32(*pf_close) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - struct vcd_clnt_ctxt_type_t *p_cctxt); + u32(*pf_close) (struct vcd_drv_ctxt *drv_ctxt, + struct vcd_clnt_ctxt *cctxt); - u32(*pf_resume) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - struct vcd_clnt_ctxt_type_t *p_cctxt); + u32(*pf_resume) (struct vcd_drv_ctxt *drv_ctxt, + struct vcd_clnt_ctxt *cctxt); - u32(*pf_set_dev_pwr) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - enum vcd_power_state_type e_pwr_state); + u32(*pf_set_dev_pwr) (struct vcd_drv_ctxt *drv_ctxt, + enum vcd_power_state pwr_state); - void (*pf_dev_cb) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - u32 event, u32 status, void *p_payload, - u32 n_size, u32 *ddl_handle, - void *const p_client_data); + void (*pf_dev_cb) (struct vcd_drv_ctxt *drv_ctxt, + u32 event, u32 status, void *payload, u32 size, + u32 *ddl_handle, void *const client_data); - void (*pf_timeout) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - void *p_user_data); + void (*pf_timeout) (struct vcd_drv_ctxt *drv_ctxt, + void *user_data); } ev_hdlr; - void (*pf_entry) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - s32 n_state_event_type); - void (*pf_exit) (struct vcd_drv_ctxt_type_t *p_drv_ctxt, - s32 n_state_event_type); + void (*pf_entry) (struct vcd_drv_ctxt *drv_ctxt, s32 state_event_type); + void (*pf_exit) (struct vcd_drv_ctxt *drv_ctxt, s32 state_event_type); }; -#define DEVICE_STATE_EVENT_NUMBER(ppf) \ - ((u32 *) (&(((struct vcd_dev_state_table_type_t*)0)->ev_hdlr.ppf)) - \ - (u32 *) (&(((struct vcd_dev_state_table_type_t*)0)->ev_hdlr.pf_init)) \ +#define DEVICE_STATE_EVENT_NUMBER(ppf) \ + ((u32 *) (&(((struct vcd_dev_state_table*)0)->ev_hdlr.ppf)) - \ + (u32 *) (&(((struct vcd_dev_state_table*)0)->ev_hdlr.pf_init)) \ + 1) -struct vcd_dev_state_ctxt_type_t { - const struct vcd_dev_state_table_type_t *p_state_table; +struct vcd_dev_state_ctxt { + const struct vcd_dev_state_table *state_table; - enum vcd_dev_state_enum_type e_state; + enum vcd_dev_state_enum state; }; -struct vcd_drv_ctxt_type_t { - struct vcd_dev_state_ctxt_type_t dev_state; - - struct vcd_dev_ctxt_type dev_ctxt; - - u32 *dev_cs; +struct vcd_drv_ctxt { + struct vcd_dev_state_ctxt dev_state; + struct vcd_dev_ctxt dev_ctxt; + struct mutex *dev_mutex; }; - -extern struct vcd_drv_ctxt_type_t *vcd_get_drv_context(void); +extern struct vcd_drv_ctxt *vcd_get_drv_context(void); void vcd_continue(void); diff --git a/drivers/misc/video_core/720p/vcd/vcd_power_sm.c b/drivers/misc/video_core/720p/vcd/vcd_power_sm.c index 34c2da28667eb..56854170f4bd2 100644 --- a/drivers/misc/video_core/720p/vcd/vcd_power_sm.c +++ b/drivers/misc/video_core/720p/vcd/vcd_power_sm.c @@ -21,16 +21,14 @@ #include "vcd_core.h" #include "vcd.h" -u32 vcd_power_event( - struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_clnt_ctxt_type_t *p_cctxt, u32 event) +u32 vcd_power_event(struct vcd_dev_ctxt *dev_ctxt, struct vcd_clnt_ctxt *cctxt, + u32 event) { u32 rc = VCD_S_SUCCESS; - VCD_MSG_MED("Device power state = %d", p_dev_ctxt->e_pwr_clk_state); - VCD_MSG_MED("event = 0x%x", event); + VCD_MSG_MED("Device power state = %d\n", dev_ctxt->pwr_clk_state); + VCD_MSG_MED("event = 0x%x\n", event); switch (event) { - case VCD_EVT_PWR_DEV_INIT_BEGIN: case VCD_EVT_PWR_DEV_INIT_END: case VCD_EVT_PWR_DEV_INIT_FAIL: @@ -41,12 +39,8 @@ u32 vcd_power_event( case VCD_EVT_PWR_DEV_SLEEP_END: case VCD_EVT_PWR_DEV_SET_PERFLVL: case VCD_EVT_PWR_DEV_HWTIMEOUT: - { - rc = vcd_device_power_event(p_dev_ctxt, event, - p_cctxt); - break; - } - + rc = vcd_device_power_event(dev_ctxt, event, cctxt); + break; case VCD_EVT_PWR_CLNT_CMD_BEGIN: case VCD_EVT_PWR_CLNT_CMD_END: case VCD_EVT_PWR_CLNT_CMD_FAIL: @@ -55,207 +49,144 @@ u32 vcd_power_event( case VCD_EVT_PWR_CLNT_FIRST_FRAME: case VCD_EVT_PWR_CLNT_LAST_FRAME: case VCD_EVT_PWR_CLNT_ERRFATAL: - { - rc = vcd_client_power_event(p_dev_ctxt, p_cctxt, event); - break; - } - + rc = vcd_client_power_event(dev_ctxt, cctxt, event); + break; } if (VCD_FAILED(rc)) - VCD_MSG_ERROR("vcd_power_event: event 0x%x failed", event); - + VCD_MSG_ERROR("vcd_power_event: event 0x%x failed\n", event); return rc; } -u32 vcd_device_power_event(struct vcd_dev_ctxt_type *p_dev_ctxt, u32 event, - struct vcd_clnt_ctxt_type_t *p_cctxt) +u32 vcd_device_power_event(struct vcd_dev_ctxt *dev_ctxt, u32 event, + struct vcd_clnt_ctxt *cctxt) { u32 rc = VCD_ERR_FAIL; - u32 n_set_perf_lvl; + u32 set_perf_lvl; switch (event) { - case VCD_EVT_PWR_DEV_INIT_BEGIN: - { - if (p_dev_ctxt->e_pwr_clk_state == - VCD_PWRCLK_STATE_OFF) { - if (res_trk_get_max_perf_level(&p_dev_ctxt-> - n_max_perf_lvl)) { - if (res_trk_power_up()) { - p_dev_ctxt->e_pwr_clk_state = - VCD_PWRCLK_STATE_ON_NOTCLOCKED; - p_dev_ctxt->n_curr_perf_lvl = 0; - p_dev_ctxt->n_reqd_perf_lvl = 0; - p_dev_ctxt->n_active_clnts = 0; - p_dev_ctxt-> - b_set_perf_lvl_pending = FALSE; - rc = vcd_enable_clock(p_dev_ctxt, - p_cctxt); - if (VCD_FAILED(rc)) { - (void)res_trk_power_down(); - p_dev_ctxt->e_pwr_clk_state = - VCD_PWRCLK_STATE_OFF; - } - } + if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_OFF && + res_trk_get_max_perf_level( + &dev_ctxt->max_perf_lvl) && + res_trk_power_up()) { + dev_ctxt->pwr_clk_state = + VCD_PWRCLK_STATE_ON_NOTCLOCKED; + dev_ctxt->curr_perf_lvl = 0; + dev_ctxt->reqd_perf_lvl = 0; + dev_ctxt->active_clnts = 0; + dev_ctxt->set_perf_lvl_pending = false; + rc = vcd_enable_clock(dev_ctxt, cctxt); + if (VCD_FAILED(rc)) { + res_trk_power_down(); + dev_ctxt->pwr_clk_state = VCD_PWRCLK_STATE_OFF; } } - break; - } - case VCD_EVT_PWR_DEV_INIT_END: case VCD_EVT_PWR_DEV_TERM_FAIL: case VCD_EVT_PWR_DEV_SLEEP_BEGIN: case VCD_EVT_PWR_DEV_HWTIMEOUT: - { - rc = vcd_gate_clock(p_dev_ctxt); - - break; - } - + rc = vcd_gate_clock(dev_ctxt); + break; case VCD_EVT_PWR_DEV_INIT_FAIL: case VCD_EVT_PWR_DEV_TERM_END: - { - if (p_dev_ctxt->e_pwr_clk_state != - VCD_PWRCLK_STATE_OFF) { - (void)vcd_disable_clock(p_dev_ctxt); - (void)res_trk_power_down(); - - p_dev_ctxt->e_pwr_clk_state = - VCD_PWRCLK_STATE_OFF; - p_dev_ctxt->n_curr_perf_lvl = 0; - p_dev_ctxt->n_reqd_perf_lvl = 0; - p_dev_ctxt->n_active_clnts = 0; - p_dev_ctxt->b_set_perf_lvl_pending = FALSE; - rc = VCD_S_SUCCESS; - } - - break; + if (dev_ctxt->pwr_clk_state != VCD_PWRCLK_STATE_OFF) { + vcd_disable_clock(dev_ctxt); + res_trk_power_down(); + + dev_ctxt->pwr_clk_state = VCD_PWRCLK_STATE_OFF; + dev_ctxt->curr_perf_lvl = 0; + dev_ctxt->reqd_perf_lvl = 0; + dev_ctxt->active_clnts = 0; + dev_ctxt->set_perf_lvl_pending = false; + rc = VCD_S_SUCCESS; } - + break; case VCD_EVT_PWR_DEV_TERM_BEGIN: case VCD_EVT_PWR_DEV_SLEEP_END: - { - rc = vcd_un_gate_clock(p_dev_ctxt); - - break; - } - + rc = vcd_un_gate_clock(dev_ctxt); + break; case VCD_EVT_PWR_DEV_SET_PERFLVL: - { - n_set_perf_lvl = - p_dev_ctxt->n_reqd_perf_lvl > - 0 ? p_dev_ctxt-> - n_reqd_perf_lvl : VCD_MIN_PERF_LEVEL; - - rc = vcd_set_perf_level(p_dev_ctxt, n_set_perf_lvl, - p_cctxt); - - break; - } + set_perf_lvl = dev_ctxt->reqd_perf_lvl > 0 ? + dev_ctxt->reqd_perf_lvl : VCD_MIN_PERF_LEVEL; + rc = vcd_set_perf_level(dev_ctxt, set_perf_lvl, cctxt); + break; } return rc; } -u32 vcd_client_power_event( - struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_clnt_ctxt_type_t *p_cctxt, u32 event) +u32 vcd_client_power_event(struct vcd_dev_ctxt *dev_ctxt, struct vcd_clnt_ctxt + *cctxt, u32 event) { u32 rc = VCD_ERR_FAIL; switch (event) { - case VCD_EVT_PWR_CLNT_CMD_BEGIN: - { - rc = vcd_un_gate_clock(p_dev_ctxt); - break; - } - + rc = vcd_un_gate_clock(dev_ctxt); + break; case VCD_EVT_PWR_CLNT_CMD_END: - { - rc = vcd_gate_clock(p_dev_ctxt); - break; - } - + rc = vcd_gate_clock(dev_ctxt); + break; case VCD_EVT_PWR_CLNT_CMD_FAIL: - { - if (!vcd_core_is_busy(p_dev_ctxt)) - rc = vcd_gate_clock(p_dev_ctxt); - - break; - } - + if (!vcd_core_is_busy(dev_ctxt)) + rc = vcd_gate_clock(dev_ctxt); + break; case VCD_EVT_PWR_CLNT_PAUSE: case VCD_EVT_PWR_CLNT_LAST_FRAME: case VCD_EVT_PWR_CLNT_ERRFATAL: - { - if (p_cctxt) { - rc = VCD_S_SUCCESS; - if (p_cctxt->status.b_req_perf_lvl) { - p_dev_ctxt->n_reqd_perf_lvl -= - p_cctxt->n_reqd_perf_lvl; - p_cctxt->status.b_req_perf_lvl = FALSE; - - rc = vcd_set_perf_level(p_dev_ctxt, - p_dev_ctxt->n_reqd_perf_lvl, - p_cctxt); - } - } - + if (!cctxt) break; - } + rc = VCD_S_SUCCESS; + if (cctxt->status.req_perf_lvl) { + dev_ctxt->reqd_perf_lvl -= cctxt->reqd_perf_lvl; + cctxt->status.req_perf_lvl = false; + rc = vcd_set_perf_level(dev_ctxt, + dev_ctxt->reqd_perf_lvl, cctxt); + } + break; case VCD_EVT_PWR_CLNT_RESUME: case VCD_EVT_PWR_CLNT_FIRST_FRAME: - { - if (p_cctxt) { - rc = VCD_S_SUCCESS; - if (!p_cctxt->status.b_req_perf_lvl) { - p_dev_ctxt->n_reqd_perf_lvl += - p_cctxt->n_reqd_perf_lvl; - p_cctxt->status.b_req_perf_lvl = TRUE; - - rc = vcd_set_perf_level(p_dev_ctxt, - p_dev_ctxt->n_reqd_perf_lvl, - p_cctxt); - } - } + if (!cctxt) break; + rc = VCD_S_SUCCESS; + if (!cctxt->status.req_perf_lvl) { + dev_ctxt->reqd_perf_lvl += cctxt->reqd_perf_lvl; + cctxt->status.req_perf_lvl = true; + + rc = vcd_set_perf_level(dev_ctxt, + dev_ctxt->reqd_perf_lvl, cctxt); } + break; } return rc; } -u32 vcd_enable_clock(struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_clnt_ctxt_type_t *p_cctxt) +u32 vcd_enable_clock(struct vcd_dev_ctxt *dev_ctxt, struct vcd_clnt_ctxt *cctxt) { u32 rc = VCD_S_SUCCESS; - u32 n_set_perf_lvl; + u32 set_perf_lvl; - if (p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_OFF) { + if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_OFF) { VCD_MSG_ERROR("vcd_enable_clock(): Already in state " "VCD_PWRCLK_STATE_OFF\n"); vcd_assert(); rc = VCD_ERR_FAIL; - } else if (p_dev_ctxt->e_pwr_clk_state == - VCD_PWRCLK_STATE_ON_NOTCLOCKED) { + } else if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_ON_NOTCLOCKED) { - n_set_perf_lvl = - p_dev_ctxt->n_reqd_perf_lvl > - 0 ? p_dev_ctxt-> - n_reqd_perf_lvl : VCD_MIN_PERF_LEVEL; + set_perf_lvl = dev_ctxt->reqd_perf_lvl > 0 ? + dev_ctxt->reqd_perf_lvl : VCD_MIN_PERF_LEVEL; - rc = vcd_set_perf_level(p_dev_ctxt, n_set_perf_lvl, - p_cctxt); + rc = vcd_set_perf_level(dev_ctxt, set_perf_lvl, cctxt); if (!VCD_FAILED(rc)) { if (res_trk_enable_clocks()) { - p_dev_ctxt->e_pwr_clk_state = - VCD_PWRCLK_STATE_ON_CLOCKED; + dev_ctxt->pwr_clk_state = + VCD_PWRCLK_STATE_ON_CLOCKED; } } else { rc = VCD_ERR_FAIL; @@ -264,124 +195,122 @@ u32 vcd_enable_clock(struct vcd_dev_ctxt_type *p_dev_ctxt, } if (!VCD_FAILED(rc)) - p_dev_ctxt->n_active_clnts++; + dev_ctxt->active_clnts++; return rc; } -u32 vcd_disable_clock(struct vcd_dev_ctxt_type *p_dev_ctxt) +u32 vcd_disable_clock(struct vcd_dev_ctxt *dev_ctxt) { u32 rc = VCD_S_SUCCESS; - if (p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_OFF) { + if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_OFF) { VCD_MSG_ERROR("vcd_disable_clock(): Already in state " "VCD_PWRCLK_STATE_OFF\n"); vcd_assert(); rc = VCD_ERR_FAIL; - } else if (p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKED || - p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKGATED) { - p_dev_ctxt->n_active_clnts--; + } else if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKED || + dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKGATED) { + dev_ctxt->active_clnts--; - if (!p_dev_ctxt->n_active_clnts) { + if (!dev_ctxt->active_clnts) { if (!res_trk_disable_clocks()) rc = VCD_ERR_FAIL; - p_dev_ctxt->e_pwr_clk_state = - VCD_PWRCLK_STATE_ON_NOTCLOCKED; - p_dev_ctxt->n_curr_perf_lvl = 0; + dev_ctxt->pwr_clk_state = + VCD_PWRCLK_STATE_ON_NOTCLOCKED; + dev_ctxt->curr_perf_lvl = 0; } } return rc; } -u32 vcd_set_perf_level(struct vcd_dev_ctxt_type *p_dev_ctxt, - u32 n_perf_lvl, struct vcd_clnt_ctxt_type_t *p_cctxt) +u32 vcd_set_perf_level(struct vcd_dev_ctxt *dev_ctxt, u32 perf_lvl, + struct vcd_clnt_ctxt *cctxt) { u32 rc = VCD_S_SUCCESS; - if (!vcd_core_is_busy(p_dev_ctxt)) { - if (res_trk_set_perf_level(n_perf_lvl, - &p_dev_ctxt->n_curr_perf_lvl, p_cctxt)) { - p_dev_ctxt->b_set_perf_lvl_pending = FALSE; + if (!vcd_core_is_busy(dev_ctxt)) { + if (res_trk_set_perf_level(perf_lvl, &dev_ctxt->curr_perf_lvl, + cctxt)) { + dev_ctxt->set_perf_lvl_pending = false; } else { rc = VCD_ERR_FAIL; - p_dev_ctxt->b_set_perf_lvl_pending = TRUE; + dev_ctxt->set_perf_lvl_pending = true; } } else { - p_dev_ctxt->b_set_perf_lvl_pending = TRUE; + dev_ctxt->set_perf_lvl_pending = true; } return rc; } -u32 vcd_update_clnt_perf_lvl( - struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_property_frame_rate_type *p_fps, u32 n_frm_p_units) +u32 vcd_update_clnt_perf_lvl(struct vcd_clnt_ctxt *cctxt, + struct vcd_property_frame_rate *fps, u32 frm_p_units) { u32 rc = VCD_S_SUCCESS; - struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; - u32 n_new_perf_lvl; + struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt; + u32 new_perf_lvl; - n_new_perf_lvl = - n_frm_p_units * p_fps->n_fps_numerator / p_fps->n_fps_denominator; + new_perf_lvl = frm_p_units * fps->fps_numerator / fps->fps_denominator; - if (p_cctxt->status.b_req_perf_lvl) { - p_dev_ctxt->n_reqd_perf_lvl = - p_dev_ctxt->n_reqd_perf_lvl - p_cctxt->n_reqd_perf_lvl + - n_new_perf_lvl; + if (cctxt->status.req_perf_lvl) { + dev_ctxt->reqd_perf_lvl = dev_ctxt->reqd_perf_lvl - + cctxt->reqd_perf_lvl + new_perf_lvl; - rc = vcd_set_perf_level(p_cctxt->p_dev_ctxt, - p_dev_ctxt->n_reqd_perf_lvl, p_cctxt); + rc = vcd_set_perf_level(cctxt->dev_ctxt, + dev_ctxt->reqd_perf_lvl, cctxt); } - p_cctxt->n_reqd_perf_lvl = n_new_perf_lvl; + cctxt->reqd_perf_lvl = new_perf_lvl; return rc; } -u32 vcd_gate_clock(struct vcd_dev_ctxt_type *p_dev_ctxt) +u32 vcd_gate_clock(struct vcd_dev_ctxt *dev_ctxt) { u32 rc = VCD_S_SUCCESS; - if (p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_OFF || - p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_ON_NOTCLOCKED) { - VCD_MSG_ERROR("%s(): Clk is Off or Not Clked yet \n", __func__); + if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_OFF || + dev_ctxt->pwr_clk_state == + VCD_PWRCLK_STATE_ON_NOTCLOCKED) { + VCD_MSG_ERROR("%s: Clk is Off or Not Clked yet\n", __func__); vcd_assert(); return VCD_ERR_FAIL; } - if (p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKGATED) + if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKGATED) return rc; if (res_trk_disable_clocks()) - p_dev_ctxt->e_pwr_clk_state = VCD_PWRCLK_STATE_ON_CLOCKGATED; + dev_ctxt->pwr_clk_state = VCD_PWRCLK_STATE_ON_CLOCKGATED; else rc = VCD_ERR_FAIL; return rc; } -u32 vcd_un_gate_clock(struct vcd_dev_ctxt_type *p_dev_ctxt) +u32 vcd_un_gate_clock(struct vcd_dev_ctxt *dev_ctxt) { u32 rc = VCD_S_SUCCESS; - if (p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_OFF || - p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_ON_NOTCLOCKED) { - VCD_MSG_ERROR("%s(): Clk is Off or Not Clked yet \n", __func__); + if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_OFF || + dev_ctxt->pwr_clk_state == + VCD_PWRCLK_STATE_ON_NOTCLOCKED) { + VCD_MSG_ERROR("%s: Clk is Off or Not Clked yet\n", __func__); vcd_assert(); return VCD_ERR_FAIL; } - if (p_dev_ctxt->e_pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKED) + if (dev_ctxt->pwr_clk_state == VCD_PWRCLK_STATE_ON_CLOCKED) return rc; if (res_trk_enable_clocks()) - p_dev_ctxt->e_pwr_clk_state = VCD_PWRCLK_STATE_ON_CLOCKED; + dev_ctxt->pwr_clk_state = VCD_PWRCLK_STATE_ON_CLOCKED; else rc = VCD_ERR_FAIL; return rc; } - diff --git a/drivers/misc/video_core/720p/vcd/vcd_property.h b/drivers/misc/video_core/720p/vcd/vcd_property.h index 20f49b56a4d5a..4df31b672bcad 100644 --- a/drivers/misc/video_core/720p/vcd/vcd_property.h +++ b/drivers/misc/video_core/720p/vcd/vcd_property.h @@ -60,16 +60,17 @@ #define VCD_I_RESERVED_BASE (VCD_START_BASE + 0x10000) -struct vcd_property_hdr_type { - u32 prop_id; - u32 n_size; +struct vcd_property_hdr { + u32 id; + size_t sz; }; -struct vcd_property_live_type { - u32 b_live; +//TODO: Remove? +struct vcd_property_live { + u32 live; }; -enum vcd_codec_type { +enum vcd_codec { VCD_CODEC_H264 = 0x1, VCD_CODEC_H263 = 0x2, VCD_CODEC_MPEG1 = 0x3, @@ -84,15 +85,15 @@ enum vcd_codec_type { VCD_CODEC_VC1_RCV = 0xC }; -struct vcd_property_codec_type { - enum vcd_codec_type e_codec; +struct vcd_property_codec { + enum vcd_codec codec; }; -struct vcd_property_frame_size_type { - u32 n_width; - u32 n_height; - u32 n_stride; - u32 n_scan_lines; +struct vcd_property_frame_size { + u32 width; + u32 height; + u32 stride; + u32 scan_lines; }; @@ -106,24 +107,24 @@ struct vcd_property_frame_size_type { #define VCD_METADATA_PASSTHROUGH 0x080 #define VCD_METADATA_ENC_SLICE 0x100 -struct vcd_property_meta_data_enable_type { - u32 n_meta_data_enable_flag; +struct vcd_property_meta_data_enable { + u32 meta_data_enable_flag; }; -struct vcd_property_metadata_hdr_type { - u32 n_meta_data_id_type; - u32 n_version; - u32 n_port_index; - u32 e_type; +struct vcd_property_metadata_hdr { + u32 meta_data_id_type; + u32 version; + u32 port_index; + u32 type; }; -struct vcd_property_frame_rate_type { - u32 n_fps_denominator; - u32 n_fps_numerator; +struct vcd_property_frame_rate { + u32 fps_denominator; + u32 fps_numerator; }; -struct vcd_property_target_bitrate_type { - u32 n_target_bitrate; +struct vcd_property_target_bitrate { + u32 target_bitrate; }; enum vcd_yuv_buffer_format_type { @@ -132,12 +133,12 @@ enum vcd_yuv_buffer_format_type { VCD_BUFFER_FORMAT_NV12_16M2KA = 0x3 }; -struct vcd_property_buffer_format_type { - enum vcd_yuv_buffer_format_type e_buffer_format; +struct vcd_property_buffer_format { + enum vcd_yuv_buffer_format_type buffer_format; }; -struct vcd_property_post_filter_type { - u32 b_post_filter; +struct vcd_property_post_filter { + u32 post_filter; }; enum vcd_codec_profile_type { @@ -155,64 +156,64 @@ enum vcd_codec_profile_type { VCD_PROFILE_MPEG2_SIMPLE = 0xB }; -struct vcd_property_profile_type { - enum vcd_codec_profile_type e_profile; +struct vcd_property_profile { + enum vcd_codec_profile_type profile; }; enum vcd_codec_level_type { - VCD_LEVEL_UNKNOWN = 0x0, - VCD_LEVEL_MPEG4_0 = 0x1, - VCD_LEVEL_MPEG4_0b = 0x2, - VCD_LEVEL_MPEG4_1 = 0x3, - VCD_LEVEL_MPEG4_2 = 0x4, - VCD_LEVEL_MPEG4_3 = 0x5, - VCD_LEVEL_MPEG4_3b = 0x6, - VCD_LEVEL_MPEG4_4 = 0x7, - VCD_LEVEL_MPEG4_4a = 0x8, - VCD_LEVEL_MPEG4_5 = 0x9, - VCD_LEVEL_MPEG4_6 = 0xA, - VCD_LEVEL_MPEG4_7 = 0xB, - VCD_LEVEL_MPEG4_X = 0xC, - VCD_LEVEL_H264_1 = 0x10, - VCD_LEVEL_H264_1b = 0x11, - VCD_LEVEL_H264_1p1 = 0x12, - VCD_LEVEL_H264_1p2 = 0x13, - VCD_LEVEL_H264_1p3 = 0x14, - VCD_LEVEL_H264_2 = 0x15, - VCD_LEVEL_H264_2p1 = 0x16, - VCD_LEVEL_H264_2p2 = 0x17, - VCD_LEVEL_H264_3 = 0x18, - VCD_LEVEL_H264_3p1 = 0x19, - VCD_LEVEL_H264_3p2 = 0x1A, - VCD_LEVEL_H264_4 = 0x1B, - VCD_LEVEL_H264_X = 0x1C, - VCD_LEVEL_H263_10 = 0x20, - VCD_LEVEL_H263_20 = 0x21, - VCD_LEVEL_H263_30 = 0x22, - VCD_LEVEL_H263_40 = 0x23, - VCD_LEVEL_H263_45 = 0x24, - VCD_LEVEL_H263_50 = 0x25, - VCD_LEVEL_H263_60 = 0x26, - VCD_LEVEL_H263_70 = 0x27, - VCD_LEVEL_H263_X = 0x28, - VCD_LEVEL_MPEG2_LOW = 0x30, - VCD_LEVEL_MPEG2_MAIN = 0x31, - VCD_LEVEL_MPEG2_HIGH_14 = 0x32, - VCD_LEVEL_MPEG2_HIGH = 0x33, - VCD_LEVEL_MPEG2_X = 0x34, - VCD_LEVEL_VC1_LOW = 0x40, - VCD_LEVEL_VC1_MEDIUM = 0x41, - VCD_LEVEL_VC1_HIGH = 0x42, - VCD_LEVEL_VC1_0 = 0x43, - VCD_LEVEL_VC1_1 = 0x44, - VCD_LEVEL_VC1_2 = 0x45, - VCD_LEVEL_VC1_3 = 0x46, - VCD_LEVEL_VC1_4 = 0x47, - VCD_LEVEL_VC1_X = 0x48 -}; - -struct vcd_property_level_type { - enum vcd_codec_level_type e_level; + VCD_LEVEL_UNKNOWN = 0x0, + VCD_LEVEL_MPEG4_0 = 0x1, + VCD_LEVEL_MPEG4_0b = 0x2, + VCD_LEVEL_MPEG4_1 = 0x3, + VCD_LEVEL_MPEG4_2 = 0x4, + VCD_LEVEL_MPEG4_3 = 0x5, + VCD_LEVEL_MPEG4_3b = 0x6, + VCD_LEVEL_MPEG4_4 = 0x7, + VCD_LEVEL_MPEG4_4a = 0x8, + VCD_LEVEL_MPEG4_5 = 0x9, + VCD_LEVEL_MPEG4_6 = 0xA, + VCD_LEVEL_MPEG4_7 = 0xB, + VCD_LEVEL_MPEG4_X = 0xC, + VCD_LEVEL_H264_1 = 0x10, + VCD_LEVEL_H264_1b = 0x11, + VCD_LEVEL_H264_1p1 = 0x12, + VCD_LEVEL_H264_1p2 = 0x13, + VCD_LEVEL_H264_1p3 = 0x14, + VCD_LEVEL_H264_2 = 0x15, + VCD_LEVEL_H264_2p1 = 0x16, + VCD_LEVEL_H264_2p2 = 0x17, + VCD_LEVEL_H264_3 = 0x18, + VCD_LEVEL_H264_3p1 = 0x19, + VCD_LEVEL_H264_3p2 = 0x1A, + VCD_LEVEL_H264_4 = 0x1B, + VCD_LEVEL_H264_X = 0x1C, + VCD_LEVEL_H263_10 = 0x20, + VCD_LEVEL_H263_20 = 0x21, + VCD_LEVEL_H263_30 = 0x22, + VCD_LEVEL_H263_40 = 0x23, + VCD_LEVEL_H263_45 = 0x24, + VCD_LEVEL_H263_50 = 0x25, + VCD_LEVEL_H263_60 = 0x26, + VCD_LEVEL_H263_70 = 0x27, + VCD_LEVEL_H263_X = 0x28, + VCD_LEVEL_MPEG2_LOW = 0x30, + VCD_LEVEL_MPEG2_MAIN = 0x31, + VCD_LEVEL_MPEG2_HIGH_14 = 0x32, + VCD_LEVEL_MPEG2_HIGH = 0x33, + VCD_LEVEL_MPEG2_X = 0x34, + VCD_LEVEL_VC1_LOW = 0x40, + VCD_LEVEL_VC1_MEDIUM = 0x41, + VCD_LEVEL_VC1_HIGH = 0x42, + VCD_LEVEL_VC1_0 = 0x43, + VCD_LEVEL_VC1_1 = 0x44, + VCD_LEVEL_VC1_2 = 0x45, + VCD_LEVEL_VC1_3 = 0x46, + VCD_LEVEL_VC1_4 = 0x47, + VCD_LEVEL_VC1_X = 0x48 +}; + +struct vcd_property_level { + enum vcd_codec_level_type level; }; enum vcd_m_slice_sel_type { @@ -222,9 +223,9 @@ enum vcd_m_slice_sel_type { VCD_MSLICE_BY_GOB = 0x4 }; -struct vcd_property_multi_slice_type { - enum vcd_m_slice_sel_type e_m_slice_sel; - u32 n_m_slice_size; +struct vcd_property_multi_slice { + enum vcd_m_slice_sel_type m_slice_sel; + u32 m_slice_size; }; enum vcd_entropy_sel_type { @@ -238,9 +239,9 @@ enum vcd_cabac_model_type { VCD_CABAC_MODEL_NUMBER_2 = 0x3 }; -struct vcd_property_entropy_control_type { - enum vcd_entropy_sel_type e_entropy_sel; - enum vcd_cabac_model_type e_cabac_model; +struct vcd_property_entropy_control { + enum vcd_entropy_sel_type entropy_sel; + enum vcd_cabac_model_type cabac_model; }; enum vcd_db_config_type { @@ -248,10 +249,10 @@ enum vcd_db_config_type { VCD_DB_DISABLE = 0x2, VCD_DB_SKIP_SLICE_BOUNDARY = 0x3 }; -struct vcd_property_db_config_type { - enum vcd_db_config_type e_db_config; - u32 n_slice_alpha_offset; - u32 n_slice_beta_offset; +struct vcd_property_db_config { + enum vcd_db_config_type db_config; + u32 slice_alpha_offset; + u32 slice_beta_offset; }; enum vcd_rate_control_type { @@ -262,51 +263,51 @@ enum vcd_rate_control_type { VCD_RATE_CONTROL_CBR_CFR = 0x5 }; -struct vcd_property_rate_control_type { - enum vcd_rate_control_type e_rate_control; +struct vcd_property_rate_control { + enum vcd_rate_control_type rate_control; }; -struct vcd_property_qp_range_type { - u32 n_max_qp; - u32 n_min_qp; +struct vcd_property_qp_range { + u32 max_qp; + u32 min_qp; }; -struct vcd_property_session_qp_type { - u32 n_i_frame_qp; - u32 n_p_frame_qp; - u32 n_b_frame_qp; +struct vcd_property_session_qp { + u32 iframe_qp; + u32 frame_qp; + u32 bframe_qp; }; -struct vcd_property_i_period_type { - u32 n_p_frames; - u32 n_b_frames; +struct vcd_property_i_period { + u32 frames; + u32 bframes; }; -struct vcd_property_vop_timing_type { - u32 n_vop_time_resolution; +struct vcd_property_vop_timing { + u32 vop_time_resolution; }; -struct vcd_property_short_header_type { - u32 b_short_header; +struct vcd_property_short_header { + u32 short_header; }; -struct vcd_property_intra_refresh_mb_number_type { - u32 n_cir_mb_number; +struct vcd_property_intra_refresh_mb_number { + u32 cir_mb_number; }; -struct vcd_property_req_i_frame_type { - u32 b_req_i_frame; +struct vcd_property_req_i_frame { + u32 req_i_frame; }; -struct vcd_frame_rect_type{ - u32 n_left; - u32 n_top; - u32 n_right; - u32 n_bottom; +struct vcd_frame_rect { + u32 left; + u32 top; + u32 right; + u32 bottom; }; -struct vcd_property_dec_output_buffer_type { - struct vcd_frame_rect_type disp_frm; +struct vcd_property_dec_output_buffer { + struct vcd_frame_rect disp_frm; }; #endif diff --git a/drivers/misc/video_core/720p/vcd/vcd_status.h b/drivers/misc/video_core/720p/vcd/vcd_status.h index 03a3b27414927..702ed549da753 100644 --- a/drivers/misc/video_core/720p/vcd/vcd_status.h +++ b/drivers/misc/video_core/720p/vcd/vcd_status.h @@ -64,6 +64,6 @@ #define VCD_ERR_INTRLCD_FIELD_DROP (VCD_S_ERR_BASE + 0xc) #define VCD_ERR_HW_FATAL (VCD_S_ERR_BASE + 0xd) #define VCD_ERR_BITSTREAM_ERR (VCD_S_ERR_BASE + 0xe) -#define VCD_FAILED(rc) ((rc > VCD_S_ERR_BASE) ? TRUE : FALSE) +#define VCD_FAILED(rc) ((rc > VCD_S_ERR_BASE) ? true : false) #endif diff --git a/drivers/misc/video_core/720p/vcd/vcd_sub.c b/drivers/misc/video_core/720p/vcd/vcd_sub.c index 6f7c4644b0882..a088cbc26709a 100644 --- a/drivers/misc/video_core/720p/vcd/vcd_sub.c +++ b/drivers/misc/video_core/720p/vcd/vcd_sub.c @@ -22,369 +22,341 @@ #include "vcd.h" #include "vdec_internal.h" -#define MAX(x, y) (((x) > (y)) ? (x) : (y)) - -u8 *vcd_pmem_get_physical(struct video_client_ctx *client_ctx, - unsigned long kernel_vaddr) +static phys_addr_t vcd_pmem_get_physical(struct video_client_ctx *client_ctx, + void *kern_addr) { - unsigned long phy_addr, user_vaddr; + phys_addr_t phys_addr; + void __user *user_addr; int pmem_fd; struct file *file; s32 buffer_index = -1; if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_INPUT, - FALSE, &user_vaddr, &kernel_vaddr, - &phy_addr, &pmem_fd, &file, - &buffer_index)) { - - return (u8 *) phy_addr; - } else if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, - FALSE, &user_vaddr, &kernel_vaddr, &phy_addr, &pmem_fd, &file, - &buffer_index)) { - return (u8 *) phy_addr; - } else { - VCD_MSG_ERROR("Couldn't get physical address"); - - return NULL; + false, &user_addr, &kern_addr, &phys_addr, &pmem_fd, + &file, &buffer_index)) { + return phys_addr; } - + if (vid_c_lookup_addr_table(client_ctx, BUFFER_TYPE_OUTPUT, + false, &user_addr, &kern_addr, &phys_addr, &pmem_fd, + &file, &buffer_index)) { + return phys_addr; + } + VCD_MSG_ERROR("Couldn't get physical address"); + return 0; } -void vcd_reset_device_channels(struct vcd_dev_ctxt_type *p_dev_ctxt) +void vcd_reset_device_channels(struct vcd_dev_ctxt *dev_ctxt) { - p_dev_ctxt->n_ddl_frame_ch_free = p_dev_ctxt->n_ddl_frame_ch_depth; - p_dev_ctxt->n_ddl_cmd_ch_free = p_dev_ctxt->n_ddl_cmd_ch_depth; - p_dev_ctxt->n_ddl_frame_ch_interim = 0; - p_dev_ctxt->n_ddl_cmd_ch_interim = 0; + dev_ctxt->ddl_frame_ch_free = dev_ctxt->ddl_frame_ch_depth; + dev_ctxt->ddl_cmd_ch_free = dev_ctxt->ddl_cmd_ch_depth; + dev_ctxt->ddl_frame_ch_interim = 0; + dev_ctxt->ddl_cmd_ch_interim = 0; } -u32 vcd_get_command_channel( - struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_transc_type **pp_transc) +u32 vcd_get_command_channel(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc **pp_transc) { - u32 b_result = FALSE; + u32 result = false; *pp_transc = NULL; - if (p_dev_ctxt->n_ddl_cmd_ch_free > 0) { - if (p_dev_ctxt->b_ddl_cmd_concurrency) { - --p_dev_ctxt->n_ddl_cmd_ch_free; - b_result = TRUE; - } else if ((p_dev_ctxt->n_ddl_frame_ch_free + - p_dev_ctxt->n_ddl_frame_ch_interim) - == p_dev_ctxt->n_ddl_frame_ch_depth) { - --p_dev_ctxt->n_ddl_cmd_ch_free; - b_result = TRUE; + if (dev_ctxt->ddl_cmd_ch_free > 0) { + if (dev_ctxt->ddl_cmd_concurrency) { + --dev_ctxt->ddl_cmd_ch_free; + result = true; + } else if ((dev_ctxt->ddl_frame_ch_free + + dev_ctxt->ddl_frame_ch_interim) == + dev_ctxt->ddl_frame_ch_depth) { + --dev_ctxt->ddl_cmd_ch_free; + result = true; } } - if (b_result) { - *pp_transc = vcd_get_free_trans_tbl_entry(p_dev_ctxt); + if (result) { + *pp_transc = vcd_get_free_trans_tbl_entry(dev_ctxt); if (!*pp_transc) { - b_result = FALSE; + result = false; - vcd_release_command_channel(p_dev_ctxt, *pp_transc); + vcd_release_command_channel(dev_ctxt, *pp_transc); } } - return b_result; + return result; } -u32 vcd_get_command_channel_in_loop( - struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_transc_type **pp_transc) +u32 vcd_get_command_channel_in_loop(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc **pp_transc) { - u32 b_result = FALSE; + u32 result = false; *pp_transc = NULL; - if (p_dev_ctxt->n_ddl_cmd_ch_interim > 0) { - if (p_dev_ctxt->b_ddl_cmd_concurrency) { - --p_dev_ctxt->n_ddl_cmd_ch_interim; - b_result = TRUE; - } else if ((p_dev_ctxt->n_ddl_frame_ch_free + - p_dev_ctxt->n_ddl_frame_ch_interim) - == p_dev_ctxt->n_ddl_frame_ch_depth) { - --p_dev_ctxt->n_ddl_cmd_ch_interim; - b_result = TRUE; + if (dev_ctxt->ddl_cmd_ch_interim > 0) { + if (dev_ctxt->ddl_cmd_concurrency) { + --dev_ctxt->ddl_cmd_ch_interim; + result = true; + } else if ((dev_ctxt->ddl_frame_ch_free + + dev_ctxt->ddl_frame_ch_interim) + == dev_ctxt->ddl_frame_ch_depth) { + --dev_ctxt->ddl_cmd_ch_interim; + result = true; } } else { - b_result = vcd_get_command_channel(p_dev_ctxt, pp_transc); + result = vcd_get_command_channel(dev_ctxt, pp_transc); } - if (b_result && !*pp_transc) { - *pp_transc = vcd_get_free_trans_tbl_entry(p_dev_ctxt); + if (result && !*pp_transc) { + *pp_transc = vcd_get_free_trans_tbl_entry(dev_ctxt); if (!*pp_transc) { - b_result = FALSE; + result = false; - ++p_dev_ctxt->n_ddl_cmd_ch_interim; + ++dev_ctxt->ddl_cmd_ch_interim; } - } - return b_result; + return result; } -void vcd_mark_command_channel(struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_transc_type *p_transc) +void vcd_mark_command_channel(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc *transc) { - ++p_dev_ctxt->n_ddl_cmd_ch_interim; + ++dev_ctxt->ddl_cmd_ch_interim; - vcd_release_trans_tbl_entry(p_transc); - if (p_dev_ctxt->n_ddl_cmd_ch_interim + - p_dev_ctxt->n_ddl_cmd_ch_free > - p_dev_ctxt->n_ddl_cmd_ch_depth) { + vcd_release_trans_tbl_entry(transc); + if (dev_ctxt->ddl_cmd_ch_interim + dev_ctxt->ddl_cmd_ch_free > + dev_ctxt->ddl_cmd_ch_depth) { VCD_MSG_ERROR("\n Command channel access counters messed up"); vcd_assert(); } } -void vcd_release_command_channel( - struct vcd_dev_ctxt_type *p_dev_ctxt, struct vcd_transc_type *p_transc) +void vcd_release_command_channel(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc *transc) { - ++p_dev_ctxt->n_ddl_cmd_ch_free; + ++dev_ctxt->ddl_cmd_ch_free; - vcd_release_trans_tbl_entry(p_transc); - if (p_dev_ctxt->n_ddl_cmd_ch_interim + p_dev_ctxt->n_ddl_cmd_ch_free > - p_dev_ctxt->n_ddl_cmd_ch_depth) { + vcd_release_trans_tbl_entry(transc); + if (dev_ctxt->ddl_cmd_ch_interim + dev_ctxt->ddl_cmd_ch_free > + dev_ctxt->ddl_cmd_ch_depth) { VCD_MSG_ERROR("\n Command channel access counters messed up"); vcd_assert(); } } -void vcd_release_multiple_command_channels(struct vcd_dev_ctxt_type - *p_dev_ctxt, u32 n_channels) +void vcd_release_multiple_command_channels(struct vcd_dev_ctxt *dev_ctxt, + u32 channels) { - p_dev_ctxt->n_ddl_cmd_ch_free += n_channels; + dev_ctxt->ddl_cmd_ch_free += channels; - if (p_dev_ctxt->n_ddl_cmd_ch_interim + - p_dev_ctxt->n_ddl_cmd_ch_free > - p_dev_ctxt->n_ddl_cmd_ch_depth) { + if (dev_ctxt->ddl_cmd_ch_interim + dev_ctxt->ddl_cmd_ch_free > + dev_ctxt->ddl_cmd_ch_depth) { VCD_MSG_ERROR("\n Command channel access counters messed up"); vcd_assert(); } } -void vcd_release_interim_command_channels(struct vcd_dev_ctxt_type *p_dev_ctxt) +void vcd_release_interim_command_channels(struct vcd_dev_ctxt *dev_ctxt) { - p_dev_ctxt->n_ddl_cmd_ch_free += p_dev_ctxt->n_ddl_cmd_ch_interim; - p_dev_ctxt->n_ddl_cmd_ch_interim = 0; + dev_ctxt->ddl_cmd_ch_free += dev_ctxt->ddl_cmd_ch_interim; + dev_ctxt->ddl_cmd_ch_interim = 0; - if (p_dev_ctxt->n_ddl_cmd_ch_interim + p_dev_ctxt->n_ddl_cmd_ch_free > - p_dev_ctxt->n_ddl_cmd_ch_depth) { + if (dev_ctxt->ddl_cmd_ch_interim + dev_ctxt->ddl_cmd_ch_free > + dev_ctxt->ddl_cmd_ch_depth) { VCD_MSG_ERROR("\n Command channel access counters messed up"); vcd_assert(); } } -u32 vcd_get_frame_channel(struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_transc_type **pp_transc) +u32 vcd_get_frame_channel(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc **pp_transc) { - u32 b_result = FALSE; + u32 result = false; - if (p_dev_ctxt->n_ddl_frame_ch_free > 0) { - if (p_dev_ctxt->b_ddl_cmd_concurrency) { - --p_dev_ctxt->n_ddl_frame_ch_free; - b_result = TRUE; - } else if ((p_dev_ctxt->n_ddl_cmd_ch_free + - p_dev_ctxt->n_ddl_cmd_ch_interim) - == p_dev_ctxt->n_ddl_cmd_ch_depth) { - --p_dev_ctxt->n_ddl_frame_ch_free; - b_result = TRUE; + if (dev_ctxt->ddl_frame_ch_free > 0) { + if (dev_ctxt->ddl_cmd_concurrency) { + --dev_ctxt->ddl_frame_ch_free; + result = true; + } else if ((dev_ctxt->ddl_cmd_ch_free + + dev_ctxt->ddl_cmd_ch_interim) + == dev_ctxt->ddl_cmd_ch_depth) { + --dev_ctxt->ddl_frame_ch_free; + result = true; } } - if (b_result) { - *pp_transc = vcd_get_free_trans_tbl_entry(p_dev_ctxt); + if (result) { + *pp_transc = vcd_get_free_trans_tbl_entry(dev_ctxt); if (!*pp_transc) { - b_result = FALSE; + result = false; - vcd_release_frame_channel(p_dev_ctxt, *pp_transc); + vcd_release_frame_channel(dev_ctxt, *pp_transc); } else { - (*pp_transc)->e_type = VCD_CMD_CODE_FRAME; + (*pp_transc)->type = VCD_CMD_CODE_FRAME; } } - return b_result; + return result; } -u32 vcd_get_frame_channel_in_loop( - struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_transc_type **pp_transc) +u32 vcd_get_frame_channel_in_loop(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc **pp_transc) { - u32 b_result = FALSE; + u32 result = false; *pp_transc = NULL; - if (p_dev_ctxt->n_ddl_frame_ch_interim > 0) { - if (p_dev_ctxt->b_ddl_cmd_concurrency) { - --p_dev_ctxt->n_ddl_frame_ch_interim; - b_result = TRUE; - } else if ((p_dev_ctxt->n_ddl_cmd_ch_free + - p_dev_ctxt->n_ddl_cmd_ch_interim) - == p_dev_ctxt->n_ddl_cmd_ch_depth) { - --p_dev_ctxt->n_ddl_frame_ch_interim; - b_result = TRUE; + if (dev_ctxt->ddl_frame_ch_interim > 0) { + if (dev_ctxt->ddl_cmd_concurrency) { + --dev_ctxt->ddl_frame_ch_interim; + result = true; + } else if ((dev_ctxt->ddl_cmd_ch_free + + dev_ctxt->ddl_cmd_ch_interim) == + dev_ctxt->ddl_cmd_ch_depth) { + --dev_ctxt->ddl_frame_ch_interim; + result = true; } } else { - b_result = vcd_get_frame_channel(p_dev_ctxt, pp_transc); + result = vcd_get_frame_channel(dev_ctxt, pp_transc); } - if (b_result && !*pp_transc) { - *pp_transc = vcd_get_free_trans_tbl_entry(p_dev_ctxt); + if (result && !*pp_transc) { + *pp_transc = vcd_get_free_trans_tbl_entry(dev_ctxt); if (!*pp_transc) { - b_result = FALSE; + result = false; VCD_MSG_FATAL("\n%s: All transactions are busy;" "Couldnt find free one\n", __func__); - ++p_dev_ctxt->n_ddl_frame_ch_interim; + ++dev_ctxt->ddl_frame_ch_interim; } } - return b_result; + return result; } -void vcd_mark_frame_channel(struct vcd_dev_ctxt_type *p_dev_ctxt) +void vcd_mark_frame_channel(struct vcd_dev_ctxt *dev_ctxt) { - ++p_dev_ctxt->n_ddl_frame_ch_interim; + ++dev_ctxt->ddl_frame_ch_interim; - if (p_dev_ctxt->n_ddl_frame_ch_interim + - p_dev_ctxt->n_ddl_frame_ch_free > - p_dev_ctxt->n_ddl_cmd_ch_depth) { + if (dev_ctxt->ddl_frame_ch_interim + dev_ctxt->ddl_frame_ch_free > + dev_ctxt->ddl_cmd_ch_depth) { VCD_MSG_FATAL("Frame channel access counters messed up"); vcd_assert(); } } -void vcd_release_frame_channel(struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_transc_type *p_transc) +void vcd_release_frame_channel(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc *transc) { - ++p_dev_ctxt->n_ddl_frame_ch_free; + ++dev_ctxt->ddl_frame_ch_free; - vcd_release_trans_tbl_entry(p_transc); + vcd_release_trans_tbl_entry(transc); - if (p_dev_ctxt->n_ddl_frame_ch_interim + - p_dev_ctxt->n_ddl_frame_ch_free > - p_dev_ctxt->n_ddl_cmd_ch_depth) { + if (dev_ctxt->ddl_frame_ch_interim + dev_ctxt->ddl_frame_ch_free > + dev_ctxt->ddl_cmd_ch_depth) { VCD_MSG_FATAL("Frame channel access counters messed up"); vcd_assert(); } } -void vcd_release_multiple_frame_channels(struct vcd_dev_ctxt_type - *p_dev_ctxt, u32 n_channels) +void vcd_release_multiple_frame_channels(struct vcd_dev_ctxt + *dev_ctxt, u32 channels) { - p_dev_ctxt->n_ddl_frame_ch_free += n_channels; + dev_ctxt->ddl_frame_ch_free += channels; - if (p_dev_ctxt->n_ddl_frame_ch_interim + - p_dev_ctxt->n_ddl_frame_ch_free > - p_dev_ctxt->n_ddl_frame_ch_depth) { + if (dev_ctxt->ddl_frame_ch_interim + dev_ctxt->ddl_frame_ch_free > + dev_ctxt->ddl_frame_ch_depth) { VCD_MSG_FATAL("Frame channel access counters messed up"); vcd_assert(); } } -void vcd_release_interim_frame_channels(struct vcd_dev_ctxt_type - *p_dev_ctxt) +void vcd_release_interim_frame_channels(struct vcd_dev_ctxt + *dev_ctxt) { - p_dev_ctxt->n_ddl_frame_ch_free += - p_dev_ctxt->n_ddl_frame_ch_interim; - p_dev_ctxt->n_ddl_frame_ch_interim = 0; + dev_ctxt->ddl_frame_ch_free += dev_ctxt->ddl_frame_ch_interim; + dev_ctxt->ddl_frame_ch_interim = 0; - if (p_dev_ctxt->n_ddl_frame_ch_free > - p_dev_ctxt->n_ddl_cmd_ch_depth) { + if (dev_ctxt->ddl_frame_ch_free > dev_ctxt->ddl_cmd_ch_depth) { VCD_MSG_FATAL("Frame channel access counters messed up"); vcd_assert(); } } -u32 vcd_core_is_busy(struct vcd_dev_ctxt_type *p_dev_ctxt) +u32 vcd_core_is_busy(struct vcd_dev_ctxt *dev_ctxt) { - if (((p_dev_ctxt->n_ddl_cmd_ch_free + - p_dev_ctxt->n_ddl_cmd_ch_interim) != - p_dev_ctxt->n_ddl_cmd_ch_depth) - || - ((p_dev_ctxt->n_ddl_frame_ch_free + - p_dev_ctxt->n_ddl_frame_ch_interim) != - p_dev_ctxt->n_ddl_frame_ch_depth) - ) { - return TRUE; + if (((dev_ctxt->ddl_cmd_ch_free + dev_ctxt->ddl_cmd_ch_interim) != + dev_ctxt->ddl_cmd_ch_depth) || + ((dev_ctxt->ddl_frame_ch_free + + dev_ctxt->ddl_frame_ch_interim) != + dev_ctxt->ddl_frame_ch_depth)) { + return true; } else { - return FALSE; + return false; } } -void vcd_device_timer_start(struct vcd_dev_ctxt_type *p_dev_ctxt) +void vcd_device_timer_start(struct vcd_dev_ctxt *dev_ctxt) { - if (p_dev_ctxt->config.pf_timer_start) - p_dev_ctxt->config.pf_timer_start(p_dev_ctxt->p_hw_timer_handle, - p_dev_ctxt->n_hw_time_out); + if (dev_ctxt->config.pf_timer_start) + dev_ctxt->config.pf_timer_start(dev_ctxt->hw_timer_handle, + dev_ctxt->hw_time_out); } -void vcd_device_timer_stop(struct vcd_dev_ctxt_type *p_dev_ctxt) +void vcd_device_timer_stop(struct vcd_dev_ctxt *dev_ctxt) { - if (p_dev_ctxt->config.pf_timer_stop) - p_dev_ctxt->config.pf_timer_stop(p_dev_ctxt->p_hw_timer_handle); + if (dev_ctxt->config.pf_timer_stop) + dev_ctxt->config.pf_timer_stop(dev_ctxt->hw_timer_handle); } -u32 vcd_common_allocate_set_buffer( - struct vcd_clnt_ctxt_type_t *p_cctxt, - enum vcd_buffer_type e_buffer, - u32 n_buf_size, struct vcd_buffer_pool_type **pp_buf_pool) +u32 vcd_common_allocate_set_buffer(struct vcd_clnt_ctxt *cctxt, + enum vcd_buffer_type buffer, size_t sz, + struct vcd_buffer_pool **pp_buf_pool) { u32 rc = VCD_S_SUCCESS; - struct vcd_buffer_requirement_type Buf_req; - struct vcd_property_hdr_type Prop_hdr; - struct vcd_buffer_pool_type *p_buf_pool; - - if (e_buffer == VCD_BUFFER_INPUT) { - Prop_hdr.prop_id = DDL_I_INPUT_BUF_REQ; - p_buf_pool = &p_cctxt->in_buf_pool; - } else if (e_buffer == VCD_BUFFER_OUTPUT) { - Prop_hdr.prop_id = DDL_I_OUTPUT_BUF_REQ; - p_buf_pool = &p_cctxt->out_buf_pool; + struct vcd_buffer_requirement buf_req; + struct vcd_property_hdr prop_hdr; + struct vcd_buffer_pool *buf_pool; + + if (buffer == VCD_BUFFER_INPUT) { + prop_hdr.id = DDL_I_INPUT_BUF_REQ; + buf_pool = &cctxt->in_buf_pool; + } else if (buffer == VCD_BUFFER_OUTPUT) { + prop_hdr.id = DDL_I_OUTPUT_BUF_REQ; + buf_pool = &cctxt->out_buf_pool; } else { rc = VCD_ERR_ILLEGAL_PARM; } VCD_FAILED_RETURN(rc, "Invalid buffer type provided"); - *pp_buf_pool = p_buf_pool; + *pp_buf_pool = buf_pool; - if (p_buf_pool->n_count > 0 && - p_buf_pool->n_validated == p_buf_pool->n_count) { + if (buf_pool->count > 0 && buf_pool->validated == buf_pool->count) { VCD_MSG_ERROR("Buffer pool is full"); return VCD_ERR_FAIL; } - if (!p_buf_pool->a_entries) { - Prop_hdr.n_size = sizeof(Buf_req); - rc = ddl_get_property(p_cctxt->ddl_handle, &Prop_hdr, &Buf_req); - - if (!VCD_FAILED(rc)) { - rc = vcd_alloc_buffer_pool_entries(p_buf_pool, - &Buf_req); - - } else { - VCD_MSG_ERROR("rc = 0x%x. Failed: ddl_get_property", - rc); - } + if (!buf_pool->entries) { + prop_hdr.sz = sizeof(buf_req); + rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &buf_req); + if (!VCD_FAILED(rc)) + rc = vcd_alloc_buffer_pool_entries(buf_pool, &buf_req); + else + VCD_MSG_ERROR("rc = 0x%x. Failed ddl_get_property", rc); } if (!VCD_FAILED(rc)) { - if (p_buf_pool->buf_req.n_size > n_buf_size) { - VCD_MSG_ERROR("\n required buffer size %u " - "allocated size %u", p_buf_pool->buf_req. - n_size, n_buf_size); - + if (buf_pool->buf_req.size > sz) { + VCD_MSG_ERROR("required buffer size %u allocated size " + "%u", buf_pool->buf_req.size, sz); rc = VCD_ERR_ILLEGAL_PARM; } } @@ -392,508 +364,423 @@ u32 vcd_common_allocate_set_buffer( return rc; } -u32 vcd_set_buffer_internal( - struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_buffer_pool_type *p_buf_pool, u8 *p_buffer, u32 n_buf_size) +u32 vcd_set_buffer_internal(struct vcd_clnt_ctxt *cctxt, + struct vcd_buffer_pool *buf_pool, void *buf, size_t sz) { - struct vcd_buffer_entry_type *p_buf_entry; + struct vcd_buffer_entry *buf_entry; - p_buf_entry = vcd_find_buffer_pool_entry(p_buf_pool, p_buffer); - if (p_buf_entry) { + buf_entry = vcd_find_buffer_pool_entry(buf_pool, buf); + if (buf_entry) { VCD_MSG_ERROR("This buffer address already exists"); - return VCD_ERR_ILLEGAL_OP; } - if (((u32) p_buffer % p_buf_pool->buf_req.n_align)) { + if (!IS_ALIGNED((unsigned long)buf, buf_pool->buf_req.align)) { VCD_MSG_ERROR("Provided addr is not aligned"); - return VCD_ERR_BAD_POINTER; } - p_buf_entry = vcd_get_free_buffer_pool_entry(p_buf_pool); - if (!p_buf_entry) { + buf_entry = vcd_get_free_buffer_pool_entry(buf_pool); + if (!buf_entry) { VCD_MSG_ERROR("Can't allocate buffer pool is full"); - return VCD_ERR_FAIL; } - p_buf_entry->p_virtual = p_buffer; + printk("npelly adding %p to buf_pool %p\n", buf, buf_entry); + buf_entry->virt_addr = buf; - p_buf_entry->p_physical = - (u8 *) vcd_pmem_get_physical(p_cctxt->p_client_data, - (unsigned long)p_buffer); + buf_entry->phys_addr = vcd_pmem_get_physical(cctxt->client_data, buf); - if (!p_buf_entry->p_physical) { + if (!buf_entry->phys_addr) { VCD_MSG_ERROR("Couldn't get physical address"); - return VCD_ERR_BAD_POINTER; } - if (((u32)p_buf_entry->p_physical % - p_buf_pool->buf_req.n_align)) { + if (!IS_ALIGNED((unsigned long)buf_entry->phys_addr, + buf_pool->buf_req.align)) { VCD_MSG_ERROR("Physical addr is not aligned"); - return VCD_ERR_BAD_POINTER ; + return VCD_ERR_BAD_POINTER; } - p_buf_entry->n_size = n_buf_size; - p_buf_entry->frame.n_alloc_len = n_buf_size; - p_buf_entry->b_allocated = FALSE; + buf_entry->size = sz; + buf_entry->frame.alloc_len = sz; + buf_entry->allocated = false; - p_buf_entry->frame.p_virtual = p_buf_entry->p_virtual; - p_buf_entry->frame.p_physical = p_buf_entry->p_physical; + buf_entry->frame.virt_addr = buf_entry->virt_addr; + buf_entry->frame.phys_addr = buf_entry->phys_addr; - p_buf_pool->n_validated++; + buf_pool->validated++; return VCD_S_SUCCESS; } -u32 vcd_allocate_buffer_internal( - struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_buffer_pool_type *p_buf_pool, - u32 n_buf_size, u8 **pp_vir_buf_addr, u8 **pp_phy_buf_addr) +u32 vcd_allocate_buffer_internal(struct vcd_clnt_ctxt *cctxt, + struct vcd_buffer_pool *buf_pool, size_t buf_size, void **virt_addr, + phys_addr_t *phys_addr) { - struct vcd_buffer_entry_type *p_buf_entry; - struct vcd_buffer_requirement_type *p_buf_req; - u32 n_addr; - int rc = 0; + struct vcd_buffer_entry *buf_entry; + struct vcd_buffer_requirement *buf_req; +// u32 addr; +// int rc = 0; - p_buf_entry = vcd_get_free_buffer_pool_entry(p_buf_pool); - if (!p_buf_entry) { + buf_entry = vcd_get_free_buffer_pool_entry(buf_pool); + if (!buf_entry) { VCD_MSG_ERROR("Can't allocate buffer pool is full"); - return VCD_ERR_FAIL; } - p_buf_req = &p_buf_pool->buf_req; - - n_buf_size += p_buf_req->n_align; + buf_req = &buf_pool->buf_req; - rc = vcd_pmem_alloc(n_buf_size, &p_buf_entry->p_alloc, - &p_buf_entry->p_physical); + //TODO strip align crap +// buf_size += buf_req->align; - if (rc < 0) { + buf_entry->buffer.virt_addr = dma_alloc_coherent(NULL, buf_size, + &buf_entry->buffer.phys_addr, GFP_KERNEL); + if (!buf_entry->buffer.virt_addr) { VCD_MSG_ERROR("Buffer allocation failed"); - return VCD_ERR_ALLOC_FAIL; } - p_buf_entry->n_size = n_buf_size; - p_buf_entry->frame.n_alloc_len = n_buf_size; + buf_entry->buffer.size = buf_size; + buf_entry->allocated = true; - if (!p_buf_entry->p_physical) { - VCD_MSG_ERROR("Couldn't get physical address"); + buf_entry->frame.alloc_len = buf_entry->buffer.size; + buf_entry->frame.virt_addr = buf_entry->buffer.virt_addr; + buf_entry->frame.phys_addr = buf_entry->buffer.phys_addr; - return VCD_ERR_BAD_POINTER; - } - - p_buf_entry->b_allocated = TRUE; - - if (p_buf_req->n_align > 0) { - - n_addr = (u32) p_buf_entry->p_physical; - n_addr += p_buf_req->n_align; - n_addr -= (n_addr % p_buf_req->n_align); - p_buf_entry->p_virtual = p_buf_entry->p_alloc; - p_buf_entry->p_virtual += (u32) (n_addr - (u32) - p_buf_entry->p_physical); - p_buf_entry->p_physical = (u8 *) n_addr; - } else { - VCD_MSG_LOW("No buffer alignment required"); - - p_buf_entry->p_virtual = p_buf_entry->p_alloc; + *virt_addr = buf_entry->buffer.virt_addr; + *phys_addr = buf_entry->buffer.phys_addr; - } - - p_buf_entry->frame.p_virtual = p_buf_entry->p_virtual; - p_buf_entry->frame.p_physical = p_buf_entry->p_physical; - - *pp_vir_buf_addr = p_buf_entry->p_virtual; - *pp_phy_buf_addr = p_buf_entry->p_physical; - - p_buf_pool->n_allocated++; - p_buf_pool->n_validated++; + buf_pool->allocated++; + buf_pool->validated++; return VCD_S_SUCCESS; } -u32 vcd_free_one_buffer_internal( - struct vcd_clnt_ctxt_type_t *p_cctxt, - enum vcd_buffer_type e_buffer, u8 *p_buffer) +u32 vcd_free_one_buffer_internal(struct vcd_clnt_ctxt *cctxt, + enum vcd_buffer_type vcd_buffer_type, u8 *buffer) { - struct vcd_buffer_pool_type *p_buf_pool; + struct vcd_buffer_pool *buf_pool; u32 rc = VCD_S_SUCCESS; - struct vcd_buffer_entry_type *p_buf_entry; + struct vcd_buffer_entry *buf_entry; - if (e_buffer == VCD_BUFFER_INPUT) - p_buf_pool = &p_cctxt->in_buf_pool; - else if (e_buffer == VCD_BUFFER_OUTPUT) - p_buf_pool = &p_cctxt->out_buf_pool; + if (vcd_buffer_type == VCD_BUFFER_INPUT) + buf_pool = &cctxt->in_buf_pool; + else if (vcd_buffer_type == VCD_BUFFER_OUTPUT) + buf_pool = &cctxt->out_buf_pool; else rc = VCD_ERR_ILLEGAL_PARM; VCD_FAILED_RETURN(rc, "Invalid buffer type provided"); - p_buf_entry = vcd_find_buffer_pool_entry(p_buf_pool, p_buffer); - if (!p_buf_entry) { + buf_entry = vcd_find_buffer_pool_entry(buf_pool, buffer); + if (!buf_entry) { VCD_MSG_ERROR("Buffer addr %p not found. Can't free buffer", - p_buffer); + buffer); return VCD_ERR_ILLEGAL_PARM; } - if (p_buf_entry->b_in_use) { + if (buf_entry->in_use) { VCD_MSG_ERROR("\n Buffer is in use and is not flushed"); return VCD_ERR_ILLEGAL_OP; } - VCD_MSG_LOW("Freeing buffer %p. Allocated %d", - p_buf_entry->p_virtual, p_buf_entry->b_allocated); - - if (p_buf_entry->b_allocated) { - vcd_pmem_free(p_buf_entry->n_size, p_buf_entry->p_alloc, - p_buf_entry->p_physical); + VCD_MSG_LOW("Freeing buffer %p. Allocated %d", buf_entry->virt_addr, + buf_entry->allocated); - p_buf_pool->n_allocated--; + if (buf_entry->allocated) { + dma_free_coherent(NULL, buf_entry->size, buf_entry->virt_addr, + buf_entry->phys_addr); + buf_entry->virt_addr = NULL; + buf_pool->allocated--; } - memset(p_buf_entry, 0, sizeof(struct vcd_buffer_entry_type)); + memset(buf_entry, 0, sizeof(struct vcd_buffer_entry)); - p_buf_pool->n_validated--; + buf_pool->validated--; return VCD_S_SUCCESS; } -u32 vcd_free_buffers_internal( - struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_buffer_pool_type *p_buf_pool) +u32 vcd_free_buffers_internal(struct vcd_clnt_ctxt *cctxt, + struct vcd_buffer_pool *buf_pool) { u32 rc = VCD_S_SUCCESS; u32 i; VCD_MSG_LOW("vcd_free_buffers_internal:"); - if (p_buf_pool->a_entries) { - for (i = 1; i <= p_buf_pool->n_count; i++) { - if (p_buf_pool->a_entries[i].b_valid && - p_buf_pool->a_entries[i].b_allocated) { - vcd_pmem_free(p_buf_pool->a_entries[i].n_size, - p_buf_pool->a_entries[i].p_alloc, - p_buf_pool->a_entries[i]. - p_physical); - } - } + if (!buf_pool->entries) + return rc; + for (i = 1; i <= buf_pool->count; i++) { + struct vcd_buffer_entry *b = &buf_pool->entries[i]; + if (!b->valid || !b->allocated) + continue; + dma_free_coherent(NULL, b->size, b->virt_addr, b->phys_addr); } - vcd_reset_buffer_pool_for_reuse(p_buf_pool); + vcd_reset_buffer_pool_for_reuse(buf_pool); return rc; } -u32 vcd_alloc_buffer_pool_entries( - struct vcd_buffer_pool_type *p_buf_pool, - struct vcd_buffer_requirement_type *p_buf_req) +u32 vcd_alloc_buffer_pool_entries(struct vcd_buffer_pool *buf_pool, + struct vcd_buffer_requirement *buf_req) { VCD_MSG_LOW("vcd_alloc_buffer_pool_entries:"); - p_buf_pool->buf_req = *p_buf_req; + buf_pool->buf_req = *buf_req; - p_buf_pool->n_count = p_buf_req->n_actual_count; - p_buf_pool->a_entries = (struct vcd_buffer_entry_type *) - vcd_malloc(sizeof(struct vcd_buffer_entry_type) * - (p_buf_pool->n_count + 1)); + buf_pool->count = buf_req->actual_count; + buf_pool->entries = kzalloc(sizeof(struct vcd_buffer_entry) * + (buf_pool->count + 1), GFP_KERNEL); - if (!p_buf_pool->a_entries) { + if (!buf_pool->entries) { VCD_MSG_ERROR("Buf_pool entries alloc failed"); - return VCD_ERR_ALLOC_FAIL; } - p_buf_pool->a_queue = (struct vcd_buffer_entry_type **) - vcd_malloc(sizeof(struct vcd_buffer_entry_type *) * - p_buf_pool->n_count); + buf_pool->queue = kzalloc(sizeof(struct vcd_buffer_entry *) * + buf_pool->count, GFP_KERNEL); - if (!p_buf_pool->a_queue) { + if (!buf_pool->queue) { VCD_MSG_ERROR("Buf_pool queue alloc failed"); - - vcd_free(p_buf_pool->a_entries); - + kfree(buf_pool->entries); return VCD_ERR_ALLOC_FAIL; } - memset(p_buf_pool->a_entries, 0, - sizeof(struct vcd_buffer_entry_type) * - (p_buf_pool->n_count + 1)); - - memset(p_buf_pool->a_queue, - 0, sizeof(struct vcd_buffer_entry_type *) * - p_buf_pool->n_count); + buf_pool->entries[0].valid = true; - p_buf_pool->a_entries[0].b_valid = TRUE; + buf_pool->q_head = 0; + buf_pool->q_tail = (u16) (buf_pool->count - 1); + buf_pool->q_len = 0; - p_buf_pool->n_q_head = 0; - p_buf_pool->n_q_tail = (u16) (p_buf_pool->n_count - 1); - p_buf_pool->n_q_len = 0; - - p_buf_pool->n_validated = 0; - p_buf_pool->n_allocated = 0; - p_buf_pool->n_in_use = 0; + buf_pool->validated = 0; + buf_pool->allocated = 0; + buf_pool->in_use = 0; return VCD_S_SUCCESS; } -void vcd_free_buffer_pool_entries(struct vcd_buffer_pool_type *p_buf_pool) +void vcd_free_buffer_pool_entries(struct vcd_buffer_pool *buf_pool) { VCD_MSG_LOW("vcd_free_buffer_pool_entries:"); - if (p_buf_pool->a_entries) { - vcd_free(p_buf_pool->a_entries); - vcd_free(p_buf_pool->a_queue); - } + kfree(buf_pool->entries); + kfree(buf_pool->queue); - memset(p_buf_pool, 0, sizeof(struct vcd_buffer_pool_type)); + memset(buf_pool, 0, sizeof(struct vcd_buffer_pool)); } -void vcd_flush_in_use_buffer_pool_entries(struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_buffer_pool_type *p_buf_pool, u32 event) +void vcd_flush_in_use_buffer_pool_entries(struct vcd_clnt_ctxt *cctxt, + struct vcd_buffer_pool *buf_pool, u32 event) { u32 i; VCD_MSG_LOW("vcd_flush_buffer_pool_entries: event=0x%x", event); - if (p_buf_pool->a_entries) { - for (i = 0; i <= p_buf_pool->n_count; i++) { - if (p_buf_pool->a_entries[i].p_virtual && - p_buf_pool->a_entries[i].b_in_use) { - p_cctxt->callback(event, VCD_S_SUCCESS, - &p_buf_pool->a_entries[i].frame, - sizeof(struct vcd_frame_data_type), - p_cctxt, p_cctxt->p_client_data); - p_buf_pool->a_entries[i].b_in_use = FALSE; - VCD_BUFFERPOOL_INUSE_DECREMENT( - p_buf_pool->n_in_use); - } + if (!buf_pool->entries) + return; + + for (i = 0; i <= buf_pool->count; i++) { + if (buf_pool->entries[i].virt_addr && + buf_pool->entries[i].in_use) { + cctxt->callback(event, VCD_S_SUCCESS, + &buf_pool->entries[i].frame, + sizeof(struct vcd_frame_data), cctxt, + cctxt->client_data); + buf_pool->entries[i].in_use = false; + VCD_BUFFERPOOL_INUSE_DECREMENT(buf_pool->in_use); } } } -void vcd_reset_buffer_pool_for_reuse(struct vcd_buffer_pool_type *p_buf_pool) +void vcd_reset_buffer_pool_for_reuse(struct vcd_buffer_pool *buf_pool) { VCD_MSG_LOW("vcd_reset_buffer_pool_for_reuse:"); - memset(&p_buf_pool->a_entries[1], 0, - sizeof(struct vcd_buffer_entry_type) * - p_buf_pool->n_count); - memset(p_buf_pool->a_queue, - 0, sizeof(struct vcd_buffer_entry_type *) * - p_buf_pool->n_count); + memset(&buf_pool->entries[1], 0, sizeof(struct vcd_buffer_entry) * + buf_pool->count); + memset(buf_pool->queue, 0, sizeof(struct vcd_buffer_entry *) * + buf_pool->count); - p_buf_pool->n_q_head = 0; - p_buf_pool->n_q_tail = (u16) (p_buf_pool->n_count - 1); - p_buf_pool->n_q_len = 0; + buf_pool->q_head = 0; + buf_pool->q_tail = (u16) (buf_pool->count - 1); + buf_pool->q_len = 0; - p_buf_pool->n_validated = 0; - p_buf_pool->n_allocated = 0; - p_buf_pool->n_in_use = 0; + buf_pool->validated = 0; + buf_pool->allocated = 0; + buf_pool->in_use = 0; } -struct vcd_buffer_entry_type *vcd_get_free_buffer_pool_entry - (struct vcd_buffer_pool_type *p_pool) { - u32 i; - - i = 1; - while (i <= p_pool->n_count && p_pool->a_entries[i].b_valid) - i++; - - - if (i <= p_pool->n_count) { - p_pool->a_entries[i].b_valid = TRUE; - - return &p_pool->a_entries[i]; - } else { - return NULL; +struct vcd_buffer_entry *vcd_get_free_buffer_pool_entry(struct vcd_buffer_pool + *pool) +{ + int i; + for (i = 1; i <= pool->count; i++) { + if (!pool->entries[i].valid) { + pool->entries[i].valid = true; + return &pool->entries[i]; + } } + return NULL; } -struct vcd_buffer_entry_type *vcd_find_buffer_pool_entry - (struct vcd_buffer_pool_type *p_pool, u8 *p_v_addr) +struct vcd_buffer_entry *vcd_find_buffer_pool_entry(struct vcd_buffer_pool + *pool, void *virt_addr) { - u32 i; - u32 b_found = FALSE; - - for (i = 0; i <= p_pool->n_count && !b_found; i++) { - if (p_pool->a_entries[i].p_virtual == p_v_addr) - b_found = TRUE; - - } - - if (b_found) - return &p_pool->a_entries[i - 1]; - else - return NULL; - + int i; + for (i = 0; i <= pool->count; i++) + if (pool->entries[i].virt_addr == virt_addr) + return &pool->entries[i]; + return NULL; } -u32 vcd_buffer_pool_entry_en_q( - struct vcd_buffer_pool_type *p_pool, - struct vcd_buffer_entry_type *p_entry) +u32 vcd_buffer_pool_entry_en_q(struct vcd_buffer_pool *pool, + struct vcd_buffer_entry *entry) { u16 i; - u16 n_q_cntr; - u32 b_found = FALSE; + u16 q_cntr; + u32 found = false; - if (p_pool->n_q_len == p_pool->n_count) - return FALSE; + if (pool->q_len == pool->count) + return false; - for (i = 0, n_q_cntr = p_pool->n_q_head; - !b_found && i < p_pool->n_q_len; - i++, n_q_cntr = (n_q_cntr + 1) % p_pool->n_count) { - if (p_pool->a_queue[n_q_cntr] == p_entry) - b_found = TRUE; + for (i = 0, q_cntr = pool->q_head; !found && i < pool->q_len; + i++, q_cntr = (q_cntr + 1) % pool->count) { + if (pool->queue[q_cntr] == entry) + found = true; } - if (b_found) { - VCD_MSG_HIGH("\n this output buffer is already present" - " in queue"); - VCD_MSG_HIGH("\n Vir Addr %p Phys Addr %p", - p_entry->p_virtual, p_entry->p_physical); - return FALSE; + if (found) { + VCD_MSG_HIGH("this output buffer is already present in queue"); + VCD_MSG_HIGH("virt_addr %p phys_addr %x", entry->virt_addr, + entry->phys_addr); + return false; } - p_pool->n_q_tail = (p_pool->n_q_tail + 1) % p_pool->n_count; - p_pool->n_q_len++; - p_pool->a_queue[p_pool->n_q_tail] = p_entry; + pool->q_tail = (pool->q_tail + 1) % pool->count; + pool->q_len++; + pool->queue[pool->q_tail] = entry; - return TRUE; + return true; } -struct vcd_buffer_entry_type *vcd_buffer_pool_entry_de_q - (struct vcd_buffer_pool_type *p_pool) { - struct vcd_buffer_entry_type *p_entry; +struct vcd_buffer_entry *vcd_buffer_pool_entry_de_q(struct vcd_buffer_pool + *pool) +{ + struct vcd_buffer_entry *entry; - if (!p_pool || !p_pool->n_q_len) + if (!pool || !pool->q_len) return NULL; - p_entry = p_pool->a_queue[p_pool->n_q_head]; - p_pool->n_q_head = (p_pool->n_q_head + 1) % p_pool->n_count; - p_pool->n_q_len--; + entry = pool->queue[pool->q_head]; + pool->q_head = (pool->q_head + 1) % pool->count; + pool->q_len--; - return p_entry; + return entry; } -void vcd_flush_output_buffers(struct vcd_clnt_ctxt_type_t *p_cctxt) +void vcd_flush_output_buffers(struct vcd_clnt_ctxt *cctxt) { - struct vcd_buffer_pool_type *p_buf_pool; - struct vcd_buffer_entry_type *p_buf_entry; - u32 n_count = 0; - struct vcd_property_hdr_type prop_hdr; + struct vcd_buffer_pool *buf_pool; + struct vcd_buffer_entry *buf_entry; + u32 count = 0; + struct vcd_property_hdr prop_hdr; VCD_MSG_LOW("vcd_flush_output_buffers:"); - p_buf_pool = &p_cctxt->out_buf_pool; + buf_pool = &cctxt->out_buf_pool; - p_buf_entry = vcd_buffer_pool_entry_de_q(p_buf_pool); - while (p_buf_entry) { - if (!p_cctxt->b_decoding || p_buf_entry->b_in_use) { - p_buf_entry->frame.n_data_len = 0; + buf_entry = vcd_buffer_pool_entry_de_q(buf_pool); + while (buf_entry) { + if (!cctxt->decoding || buf_entry->in_use) { + buf_entry->frame.data_len = 0; - p_cctxt->callback(VCD_EVT_RESP_OUTPUT_FLUSHED, - VCD_S_SUCCESS, - &p_buf_entry->frame, - sizeof(struct vcd_frame_data_type), - p_cctxt, p_cctxt->p_client_data); + cctxt->callback(VCD_EVT_RESP_OUTPUT_FLUSHED, + VCD_S_SUCCESS, &buf_entry->frame, + sizeof(struct vcd_frame_data), + cctxt, cctxt->client_data); - p_buf_entry->b_in_use = FALSE; + buf_entry->in_use = false; - n_count++; + count++; } - p_buf_entry = vcd_buffer_pool_entry_de_q(p_buf_pool); + buf_entry = vcd_buffer_pool_entry_de_q(buf_pool); } - p_buf_pool->n_in_use = 0; + buf_pool->in_use = 0; - if (p_cctxt->b_sched_clnt_valid && n_count > 0) { - VCD_MSG_LOW("Updating scheduler O tkns = %u", n_count); + if (cctxt->sched_clnt_valid && count > 0) { + VCD_MSG_LOW("Updating scheduler O tkns = %u", count); - (void)sched_update_client_o_tkn(p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, - FALSE, - n_count * - p_cctxt-> - n_sched_o_tkn_per_ip_frm); + sched_update_client_o_tkn(cctxt->dev_ctxt->sched_hdl, + cctxt->sched_clnt_hdl, false, + count * cctxt->sched_o_tkn_per_ip_frm); } - if (p_cctxt->b_ddl_hdl_valid && p_cctxt->b_decoding) { - prop_hdr.prop_id = DDL_I_REQ_OUTPUT_FLUSH; - prop_hdr.n_size = sizeof(u32); - n_count = 0x1; + if (cctxt->ddl_hdl_valid && cctxt->decoding) { + prop_hdr.id = DDL_I_REQ_OUTPUT_FLUSH; + prop_hdr.sz = sizeof(u32); + count = 0x1; - (void)ddl_set_property(p_cctxt->ddl_handle, &prop_hdr, - &n_count); + ddl_set_property(cctxt->ddl_handle, &prop_hdr, &count); } } -u32 vcd_flush_buffers(struct vcd_clnt_ctxt_type_t *p_cctxt, u32 n_mode) +u32 vcd_flush_buffers(struct vcd_clnt_ctxt *cctxt, u32 mode) { - struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt; u32 rc = VCD_S_SUCCESS; - struct vcd_buffer_entry_type *p_buf_entry; + struct vcd_buffer_entry *buf_entry; VCD_MSG_LOW("vcd_flush_buffers:"); - if (n_mode > VCD_FLUSH_ALL || !(n_mode & VCD_FLUSH_ALL)) { - VCD_MSG_ERROR("Invalid flush mode %d", n_mode); + if (mode > VCD_FLUSH_ALL || !(mode & VCD_FLUSH_ALL)) { + VCD_MSG_ERROR("Invalid flush mode %d", mode); return VCD_ERR_ILLEGAL_PARM; } - VCD_MSG_MED("Flush mode %d requested", n_mode); + VCD_MSG_MED("Flush mode %d requested", mode); - if ((n_mode & VCD_FLUSH_INPUT) && p_cctxt->b_sched_clnt_valid) { - rc = vcd_map_sched_status(sched_flush_client_buffer - (p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, - (void **)&p_buf_entry)); - - while (!VCD_FAILED(rc) && - rc != VCD_S_SCHED_QEMPTY && p_buf_entry) { - if (p_buf_entry->p_virtual) { - p_cctxt->callback(VCD_EVT_RESP_INPUT_FLUSHED, - VCD_S_SUCCESS, - &p_buf_entry->frame, - sizeof(struct - vcd_frame_data_type), - p_cctxt, - p_cctxt->p_client_data); + if ((mode & VCD_FLUSH_INPUT) && cctxt->sched_clnt_valid) { + rc = vcd_map_sched_status(sched_flush_client_buffer( + dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, + (void **)&buf_entry)); + while (!VCD_FAILED(rc) && rc != VCD_S_SCHED_QEMPTY && + buf_entry) { + if (buf_entry->virt_addr) { + cctxt->callback(VCD_EVT_RESP_INPUT_FLUSHED, + VCD_S_SUCCESS, &buf_entry->frame, + sizeof(struct vcd_frame_data), cctxt, + cctxt->client_data); } - p_buf_entry->b_in_use = FALSE; + buf_entry->in_use = false; VCD_BUFFERPOOL_INUSE_DECREMENT( - p_cctxt->in_buf_pool.n_in_use); - p_buf_entry = NULL; - rc = vcd_map_sched_status(sched_flush_client_buffer - (p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, - (void **)&p_buf_entry)); + cctxt->in_buf_pool.in_use); + buf_entry = NULL; + rc = vcd_map_sched_status(sched_flush_client_buffer( + dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, + (void **)&buf_entry)); } } VCD_FAILED_RETURN(rc, "Failed: sched_flush_client_buffer"); - if (p_cctxt->status.n_frame_submitted > 0) { - - p_cctxt->status.n_flush_mode |= n_mode; - + if (cctxt->status.frame_submitted > 0) { + cctxt->status.flush_mode |= mode; } else { - - if (n_mode & VCD_FLUSH_OUTPUT) { - vcd_flush_output_buffers(p_cctxt); - vcd_release_all_clnt_frm_transc(p_cctxt); + if (mode & VCD_FLUSH_OUTPUT) { + vcd_flush_output_buffers(cctxt); + vcd_release_all_clnt_frm_transc(cctxt); } } @@ -901,65 +788,65 @@ u32 vcd_flush_buffers(struct vcd_clnt_ctxt_type_t *p_cctxt, u32 n_mode) return VCD_S_SUCCESS; } -void vcd_flush_buffers_in_err_fatal(struct vcd_clnt_ctxt_type_t *p_cctxt) +void vcd_flush_buffers_in_err_fatal(struct vcd_clnt_ctxt *cctxt) { VCD_MSG_LOW("\n vcd_flush_buffers_in_err_fatal:"); - (void) vcd_flush_buffers(p_cctxt, VCD_FLUSH_ALL); - vcd_flush_in_use_buffer_pool_entries(p_cctxt, - &p_cctxt->in_buf_pool, VCD_EVT_RESP_INPUT_FLUSHED); - vcd_flush_in_use_buffer_pool_entries(p_cctxt, - &p_cctxt->out_buf_pool, VCD_EVT_RESP_OUTPUT_FLUSHED); - p_cctxt->status.n_flush_mode = VCD_FLUSH_ALL; - vcd_send_flush_done(p_cctxt, VCD_S_SUCCESS); + vcd_flush_buffers(cctxt, VCD_FLUSH_ALL); + vcd_flush_in_use_buffer_pool_entries(cctxt, &cctxt->in_buf_pool, + VCD_EVT_RESP_INPUT_FLUSHED); + vcd_flush_in_use_buffer_pool_entries(cctxt, &cctxt->out_buf_pool, + VCD_EVT_RESP_OUTPUT_FLUSHED); + cctxt->status.flush_mode = VCD_FLUSH_ALL; + vcd_send_flush_done(cctxt, VCD_S_SUCCESS); } -u32 vcd_init_client_context(struct vcd_clnt_ctxt_type_t *p_cctxt) +u32 vcd_init_client_context(struct vcd_clnt_ctxt *cctxt) { u32 rc; VCD_MSG_LOW("vcd_init_client_context:"); - rc = ddl_open(&p_cctxt->ddl_handle, p_cctxt->b_decoding); + rc = ddl_open(&cctxt->ddl_handle, cctxt->decoding); VCD_FAILED_RETURN(rc, "Failed: ddl_open"); - p_cctxt->b_ddl_hdl_valid = TRUE; + cctxt->ddl_hdl_valid = true; - p_cctxt->clnt_state.e_state = VCD_CLIENT_STATE_OPEN; - p_cctxt->clnt_state.p_state_table = - vcd_get_client_state_table(VCD_CLIENT_STATE_OPEN); + cctxt->clnt_state.state = VCD_CLIENT_STATE_OPEN; + cctxt->clnt_state.state_table = vcd_get_client_state_table( + VCD_CLIENT_STATE_OPEN); - p_cctxt->n_signature = VCD_SIGNATURE; - p_cctxt->b_live = TRUE; + cctxt->signature = VCD_SIGNATURE; + cctxt->live = true; - p_cctxt->cmd_q.e_pending_cmd = VCD_CMD_NONE; + cctxt->cmd_q.pending_cmd = VCD_CMD_NONE; return rc; } -void vcd_destroy_client_context(struct vcd_clnt_ctxt_type_t *p_cctxt) +void vcd_destroy_client_context(struct vcd_clnt_ctxt *cctxt) { - struct vcd_dev_ctxt_type *p_dev_ctxt; - struct vcd_clnt_ctxt_type_t *p_client; + struct vcd_dev_ctxt *dev_ctxt; + struct vcd_clnt_ctxt *client; u32 rc = VCD_S_SUCCESS; - int n_idx; + int idx; VCD_MSG_LOW("vcd_destroy_client_context:"); - p_dev_ctxt = p_cctxt->p_dev_ctxt; + dev_ctxt = cctxt->dev_ctxt; - if (p_cctxt == p_dev_ctxt->p_cctxt_list_head) { + if (cctxt == dev_ctxt->cctxt_list_head) { VCD_MSG_MED("Clnt list head clnt being removed"); - p_dev_ctxt->p_cctxt_list_head = p_cctxt->p_next; + dev_ctxt->cctxt_list_head = cctxt->next; } else { - p_client = p_dev_ctxt->p_cctxt_list_head; - while (p_client && p_cctxt != p_client->p_next) - p_client = p_client->p_next; + client = dev_ctxt->cctxt_list_head; + while (client && cctxt != client->next) + client = client->next; - if (p_client) - p_client->p_next = p_cctxt->p_next; + if (client) + client->next = cctxt->next; - if (!p_client) { + if (!client) { rc = VCD_ERR_FAIL; VCD_MSG_ERROR("Client not found in client list"); @@ -969,125 +856,119 @@ void vcd_destroy_client_context(struct vcd_clnt_ctxt_type_t *p_cctxt) if (VCD_FAILED(rc)) return; - if (p_cctxt->b_sched_clnt_valid) { + if (cctxt->sched_clnt_valid) { rc = VCD_S_SUCCESS; while (!VCD_FAILED(rc) && rc != VCD_S_SCHED_QEMPTY) { - rc = vcd_map_sched_status(sched_flush_client_buffer - (p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, - (void *)&n_idx)); + rc = vcd_map_sched_status(sched_flush_client_buffer( + dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, + (void *)&idx)); if (VCD_FAILED(rc)) VCD_MSG_ERROR("\n Failed: " "sched_flush_client_buffer"); } - rc = vcd_map_sched_status(sched_remove_client - (p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl)); + rc = vcd_map_sched_status(sched_remove_client( + dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl)); if (VCD_FAILED(rc)) VCD_MSG_ERROR("\n Failed: sched_remove_client"); - p_cctxt->b_sched_clnt_valid = FALSE; + cctxt->sched_clnt_valid = false; } - if (p_cctxt->seq_hdr.p_sequence_header) { - vcd_pmem_free(p_cctxt->seq_hdr.n_sequence_header_len, - p_cctxt->seq_hdr.p_sequence_header, - p_cctxt->p_seq_hdr_phy_addr); - p_cctxt->seq_hdr.p_sequence_header = NULL; + if (cctxt->seq_hdr.addr) { + dma_free_coherent(NULL, cctxt->seq_hdr.sz, cctxt->seq_hdr.addr, + cctxt->seq_hdr_phys_addr); + cctxt->seq_hdr.addr = NULL; } - vcd_free_buffers_internal(p_cctxt, &p_cctxt->in_buf_pool); - vcd_free_buffers_internal(p_cctxt, &p_cctxt->out_buf_pool); - vcd_free_buffer_pool_entries(&p_cctxt->in_buf_pool); - vcd_free_buffer_pool_entries(&p_cctxt->out_buf_pool); - vcd_release_all_clnt_transc(p_cctxt); + vcd_free_buffers_internal(cctxt, &cctxt->in_buf_pool); + vcd_free_buffers_internal(cctxt, &cctxt->out_buf_pool); + vcd_free_buffer_pool_entries(&cctxt->in_buf_pool); + vcd_free_buffer_pool_entries(&cctxt->out_buf_pool); + vcd_release_all_clnt_transc(cctxt); - if (p_cctxt->b_ddl_hdl_valid) { - (void)ddl_close(&p_cctxt->ddl_handle); - p_cctxt->b_ddl_hdl_valid = FALSE; + if (cctxt->ddl_hdl_valid) { + ddl_close(&cctxt->ddl_handle); + cctxt->ddl_hdl_valid = false; } - vcd_free(p_cctxt); + kfree(cctxt); } -u32 vcd_check_for_client_context( - struct vcd_dev_ctxt_type *p_dev_ctxt, s32 driver_id) +u32 vcd_check_for_client_context(struct vcd_dev_ctxt *dev_ctxt, s32 driver_id) { - struct vcd_clnt_ctxt_type_t *p_client; + struct vcd_clnt_ctxt *client; - p_client = p_dev_ctxt->p_cctxt_list_head; - while (p_client && p_client->driver_id != driver_id) - p_client = p_client->p_next; + client = dev_ctxt->cctxt_list_head; + while (client && client->driver_id != driver_id) + client = client->next; - if (!p_client) - return FALSE; + if (!client) + return false; else - return TRUE; + return true; } -u32 vcd_validate_driver_handle( - struct vcd_dev_ctxt_type *p_dev_ctxt, s32 driver_handle) +u32 vcd_validate_driver_handle(struct vcd_dev_ctxt *dev_ctxt, s32 driver_handle) { driver_handle--; - if (driver_handle < 0 || - driver_handle >= VCD_DRIVER_INSTANCE_MAX || - !p_dev_ctxt->b_driver_ids[driver_handle]) { - return FALSE; + if (driver_handle < 0 || driver_handle >= VCD_DRIVER_INSTANCE_MAX || + !dev_ctxt->driver_ids[driver_handle]) { + return false; } else { - return TRUE; + return true; } } -u32 vcd_client_cmd_en_q( - struct vcd_clnt_ctxt_type_t *p_cctxt, enum vcd_command_type command) +u32 vcd_client_cmd_en_q(struct vcd_clnt_ctxt *cctxt, + enum vcd_command_type command) { - u32 b_result; + u32 result; - if (p_cctxt->cmd_q.e_pending_cmd == VCD_CMD_NONE) { - p_cctxt->cmd_q.e_pending_cmd = command; - b_result = TRUE; + if (cctxt->cmd_q.pending_cmd == VCD_CMD_NONE) { + cctxt->cmd_q.pending_cmd = command; + result = true; } else { - b_result = FALSE; + result = false; } - return b_result; + return result; } -void vcd_client_cmd_flush_and_en_q( - struct vcd_clnt_ctxt_type_t *p_cctxt, enum vcd_command_type command) +void vcd_client_cmd_flush_and_en_q(struct vcd_clnt_ctxt *cctxt, + enum vcd_command_type command) { - p_cctxt->cmd_q.e_pending_cmd = command; + cctxt->cmd_q.pending_cmd = command; } -u32 vcd_client_cmd_de_q(struct vcd_clnt_ctxt_type_t *p_cctxt, - enum vcd_command_type *p_command) +u32 vcd_client_cmd_de_q(struct vcd_clnt_ctxt *cctxt, + enum vcd_command_type *command) { - if (p_cctxt->cmd_q.e_pending_cmd == VCD_CMD_NONE) - return FALSE; + if (cctxt->cmd_q.pending_cmd == VCD_CMD_NONE) + return false; - *p_command = p_cctxt->cmd_q.e_pending_cmd; - p_cctxt->cmd_q.e_pending_cmd = VCD_CMD_NONE; + *command = cctxt->cmd_q.pending_cmd; + cctxt->cmd_q.pending_cmd = VCD_CMD_NONE; - return TRUE; + return true; } -u32 vcd_get_next_queued_client_cmd(struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_clnt_ctxt_type_t **p_cctxt, enum vcd_command_type *p_command) +u32 vcd_get_next_queued_client_cmd(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_clnt_ctxt **cctxt, enum vcd_command_type *command) { - struct vcd_clnt_ctxt_type_t *p_client = p_dev_ctxt->p_cctxt_list_head; - u32 b_result = FALSE; + struct vcd_clnt_ctxt *client = dev_ctxt->cctxt_list_head; + u32 result = false; - while (p_client && !b_result) { - *p_cctxt = p_client; - b_result = vcd_client_cmd_de_q(p_client, p_command); - p_client = p_client->p_next; + while (client && !result) { + *cctxt = client; + result = vcd_client_cmd_de_q(client, command); + client = client->next; } - return b_result; + return result; } -u32 vcd_map_sched_status(enum sched_status_type sched_status) +u32 vcd_map_sched_status(enum sched_status sched_status) { u32 rc = VCD_S_SUCCESS; @@ -1117,339 +998,317 @@ u32 vcd_map_sched_status(enum sched_status_type sched_status) return rc; } -u32 vcd_submit_cmd_sess_start(struct vcd_transc_type *p_transc) +u32 vcd_submit_cmd_sess_start(struct vcd_transc *transc) { u32 rc; - struct vcd_sequence_hdr_type Seq_hdr; + struct vcd_phys_sequence_hdr seq_hdr; VCD_MSG_LOW("vcd_submit_cmd_sess_start:"); - if (p_transc->p_cctxt->b_decoding) { + if (transc->cctxt->decoding) { - if (p_transc->p_cctxt->seq_hdr.p_sequence_header) { - Seq_hdr.n_sequence_header_len = - p_transc->p_cctxt->seq_hdr. - n_sequence_header_len; - Seq_hdr.p_sequence_header = - p_transc->p_cctxt->p_seq_hdr_phy_addr; + if (transc->cctxt->seq_hdr.addr) { + seq_hdr.sz = transc->cctxt->seq_hdr.sz; + seq_hdr.addr = transc->cctxt->seq_hdr_phys_addr; - rc = ddl_decode_start(p_transc->p_cctxt->ddl_handle, - &Seq_hdr, (void *)p_transc); + rc = ddl_decode_start(transc->cctxt->ddl_handle, + &seq_hdr, (void *)transc); } else { - rc = ddl_decode_start(p_transc->p_cctxt->ddl_handle, - NULL, (void *)p_transc); + rc = ddl_decode_start(transc->cctxt->ddl_handle, NULL, + (void *)transc); } } else { - rc = ddl_encode_start(p_transc->p_cctxt->ddl_handle, - (void *)p_transc); + rc = ddl_encode_start(transc->cctxt->ddl_handle, + (void *)transc); } if (!VCD_FAILED(rc)) { - p_transc->p_cctxt->status.n_cmd_submitted++; - vcd_device_timer_start(p_transc->p_cctxt->p_dev_ctxt); + transc->cctxt->status.cmd_submitted++; + vcd_device_timer_start(transc->cctxt->dev_ctxt); } else VCD_MSG_ERROR("rc = 0x%x. Failed: ddl start", rc); return rc; } -u32 vcd_submit_cmd_sess_end(struct vcd_transc_type *p_transc) +u32 vcd_submit_cmd_sess_end(struct vcd_transc *transc) { u32 rc; VCD_MSG_LOW("vcd_submit_cmd_sess_end:"); - if (p_transc->p_cctxt->b_decoding) { - rc = ddl_decode_end(p_transc->p_cctxt->ddl_handle, - (void *)p_transc); + if (transc->cctxt->decoding) { + rc = ddl_decode_end(transc->cctxt->ddl_handle, + (void *)transc); } else { - rc = ddl_encode_end(p_transc->p_cctxt->ddl_handle, - (void *)p_transc); + rc = ddl_encode_end(transc->cctxt->ddl_handle, + (void *)transc); } if (!VCD_FAILED(rc)) { - p_transc->p_cctxt->status.n_cmd_submitted++; - vcd_device_timer_start(p_transc->p_cctxt->p_dev_ctxt); + transc->cctxt->status.cmd_submitted++; + vcd_device_timer_start(transc->cctxt->dev_ctxt); } else VCD_MSG_ERROR("rc = 0x%x. Failed: ddl end", rc); return rc; } -void vcd_submit_cmd_client_close(struct vcd_clnt_ctxt_type_t *p_cctxt) +void vcd_submit_cmd_client_close(struct vcd_clnt_ctxt *cctxt) { - (void) ddl_close(&p_cctxt->ddl_handle); - p_cctxt->b_ddl_hdl_valid = FALSE; - p_cctxt->status.b_cleaning_up = FALSE; - if (p_cctxt->status.b_close_pending) { - vcd_destroy_client_context(p_cctxt); - vcd_handle_for_last_clnt_close(p_cctxt->p_dev_ctxt, TRUE); + ddl_close(&cctxt->ddl_handle); + cctxt->ddl_hdl_valid = false; + cctxt->status.cleaning_up = false; + if (cctxt->status.close_pending) { + vcd_destroy_client_context(cctxt); + vcd_handle_for_last_clnt_close(cctxt->dev_ctxt, true); } } -u32 vcd_submit_command_in_continue(struct vcd_dev_ctxt_type - *p_dev_ctxt, struct vcd_transc_type *p_transc) +u32 vcd_submit_command_in_continue(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc *transc) { - struct vcd_property_hdr_type prop_hdr; - struct vcd_clnt_ctxt_type_t *p_client = NULL; + struct vcd_property_hdr prop_hdr; + struct vcd_clnt_ctxt *client = NULL; enum vcd_command_type cmd = VCD_CMD_NONE; u32 rc = VCD_ERR_FAIL; - u32 b_result = FALSE, n_flush = 0, event = 0; - u32 b_break = FALSE; + u32 result = false; + u32 flush = 0; + u32 event = 0; VCD_MSG_LOW("\n vcd_submit_command_in_continue:"); - while (!b_break) { - b_result = vcd_get_next_queued_client_cmd(p_dev_ctxt, - &p_client, &cmd); - - if (!b_result) - b_break = TRUE; - else { - p_transc->e_type = cmd; - p_transc->p_cctxt = p_client; - - switch (cmd) { - case VCD_CMD_CODEC_START: - { - rc = vcd_submit_cmd_sess_start(p_transc); - event = VCD_EVT_RESP_START; - break; - } - case VCD_CMD_CODEC_STOP: - { - rc = vcd_submit_cmd_sess_end(p_transc); - event = VCD_EVT_RESP_STOP; - break; - } - case VCD_CMD_OUTPUT_FLUSH: - { - prop_hdr.prop_id = DDL_I_REQ_OUTPUT_FLUSH; - prop_hdr.n_size = sizeof(u32); - n_flush = 0x1; - (void) ddl_set_property(p_client->ddl_handle, - &prop_hdr, &n_flush); - vcd_release_command_channel(p_dev_ctxt, - p_transc); - rc = VCD_S_SUCCESS; - break; - } - case VCD_CMD_CLIENT_CLOSE: - { - vcd_submit_cmd_client_close(p_client); - vcd_release_command_channel(p_dev_ctxt, - p_transc); - rc = VCD_S_SUCCESS; - break; - } - default: - { - VCD_MSG_ERROR("\n vcd_submit_command: Unknown" - "command %d", (int)cmd); - vcd_assert(); - break; - } - } + while (1) { + result = vcd_get_next_queued_client_cmd(dev_ctxt, &client, + &cmd); + + if (!result) + break; + + transc->type = cmd; + transc->cctxt = client; + + switch (cmd) { + case VCD_CMD_CODEC_START: + rc = vcd_submit_cmd_sess_start(transc); + event = VCD_EVT_RESP_START; + break; + case VCD_CMD_CODEC_STOP: + rc = vcd_submit_cmd_sess_end(transc); + event = VCD_EVT_RESP_STOP; + break; + case VCD_CMD_OUTPUT_FLUSH: + prop_hdr.id = DDL_I_REQ_OUTPUT_FLUSH; + prop_hdr.sz = sizeof(u32); + flush = 0x1; + ddl_set_property(client->ddl_handle, &prop_hdr, &flush); + vcd_release_command_channel(dev_ctxt, transc); + rc = VCD_S_SUCCESS; + break; + case VCD_CMD_CLIENT_CLOSE: + vcd_submit_cmd_client_close(client); + vcd_release_command_channel(dev_ctxt, transc); + rc = VCD_S_SUCCESS; + break; + default: + VCD_MSG_ERROR("\n vcd_submit_command: Unknown" + "command %d", (int)cmd); + vcd_assert(); + break; + } - if (!VCD_FAILED(rc)) { - b_break = TRUE; - } else { + if (!VCD_FAILED(rc)) { + break; + } else { VCD_MSG_ERROR("vcd_submit_command %d: failed 0x%x", cmd, rc); - p_client->callback(event, rc, NULL, 0, p_client, - p_client->p_client_data); - } - } + client->callback(event, rc, NULL, 0, client, + client->client_data); + } } - return b_result; + return result; } -u32 vcd_schedule_frame(struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_clnt_ctxt_type_t **pp_cctxt, struct vcd_buffer_entry_type - **pp_ip_buf_entry) +u32 vcd_schedule_frame(struct vcd_dev_ctxt *dev_ctxt, struct vcd_clnt_ctxt + **pp_cctxt, struct vcd_buffer_entry **pp_ip_buf_entry) { u32 rc = VCD_S_SUCCESS; VCD_MSG_LOW("vcd_schedule_frame:"); - if (!p_dev_ctxt->p_cctxt_list_head) { + if (!dev_ctxt->cctxt_list_head) { VCD_MSG_HIGH("Client list empty"); - return FALSE; + return false; } - rc = vcd_map_sched_status(sched_de_queue_frame(p_dev_ctxt->sched_hdl, + rc = vcd_map_sched_status(sched_de_queue_frame(dev_ctxt->sched_hdl, (void **) pp_ip_buf_entry, (void **) pp_cctxt)); if (VCD_FAILED(rc)) { VCD_MSG_FATAL("vcd_submit_frame: sched_de_queue_frame" "failed 0x%x", rc); - return FALSE; + return false; } if (rc == VCD_S_SCHED_QEMPTY) { VCD_MSG_HIGH("No frame available. Sched queues are empty"); - return FALSE; + return false; } if (!*pp_cctxt || !*pp_ip_buf_entry) { VCD_MSG_FATAL("Sched returned invalid values. ctxt=%p," - "ipbuf=%p", *pp_cctxt, *pp_ip_buf_entry); - return FALSE; + "ipbuf=%p", *pp_cctxt, *pp_ip_buf_entry); + return false; } if (rc == VCD_S_SCHED_EOS) - (*pp_ip_buf_entry)->frame.n_flags |= VCD_FRAME_FLAG_EOS; + (*pp_ip_buf_entry)->frame.flags |= VCD_FRAME_FLAG_EOS; - return TRUE; + return true; } -void vcd_try_submit_frame(struct vcd_dev_ctxt_type *p_dev_ctxt) +void vcd_try_submit_frame(struct vcd_dev_ctxt *dev_ctxt) { - struct vcd_transc_type *p_transc; + struct vcd_transc *transc; u32 rc = VCD_S_SUCCESS; - struct vcd_clnt_ctxt_type_t *p_cctxt = NULL; - struct vcd_buffer_entry_type *p_ip_buf_entry = NULL; - u32 b_result = FALSE; + struct vcd_clnt_ctxt *cctxt = NULL; + struct vcd_buffer_entry *ip_buf_entry = NULL; + u32 result = false; VCD_MSG_LOW("vcd_try_submit_frame:"); - if (!vcd_get_frame_channel(p_dev_ctxt, &p_transc)) + if (!vcd_get_frame_channel(dev_ctxt, &transc)) return; - if (!vcd_schedule_frame(p_dev_ctxt, &p_cctxt, &p_ip_buf_entry)) { - vcd_release_frame_channel(p_dev_ctxt, p_transc); + if (!vcd_schedule_frame(dev_ctxt, &cctxt, &ip_buf_entry)) { + vcd_release_frame_channel(dev_ctxt, transc); return; } - rc = vcd_power_event(p_dev_ctxt, p_cctxt, VCD_EVT_PWR_CLNT_CMD_BEGIN); + rc = vcd_power_event(dev_ctxt, cctxt, VCD_EVT_PWR_CLNT_CMD_BEGIN); if (!VCD_FAILED(rc)) { - p_transc->p_cctxt = p_cctxt; - p_transc->p_ip_buf_entry = p_ip_buf_entry; + transc->cctxt = cctxt; + transc->ip_buf_entry = ip_buf_entry; - b_result = vcd_submit_frame(p_dev_ctxt, p_transc); + result = vcd_submit_frame(dev_ctxt, transc); } else { VCD_MSG_ERROR("Failed: VCD_EVT_PWR_CLNT_CMD_BEGIN"); - (void) vcd_requeue_input_frame(p_dev_ctxt, p_cctxt, - p_ip_buf_entry); + vcd_requeue_input_frame(dev_ctxt, cctxt, ip_buf_entry); - (void) vcd_map_sched_status(sched_update_client_o_tkn( - p_dev_ctxt->sched_hdl, p_cctxt->sched_clnt_hdl, - TRUE, p_cctxt->n_sched_o_tkn_per_ip_frm)); + vcd_map_sched_status(sched_update_client_o_tkn( + dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, + true, cctxt->sched_o_tkn_per_ip_frm)); } - if (!b_result) { - vcd_release_frame_channel(p_dev_ctxt, p_transc); - (void) vcd_power_event(p_dev_ctxt, p_cctxt, - VCD_EVT_PWR_CLNT_CMD_FAIL); + if (!result) { + vcd_release_frame_channel(dev_ctxt, transc); + vcd_power_event(dev_ctxt, cctxt, VCD_EVT_PWR_CLNT_CMD_FAIL); } } -u32 vcd_submit_frame(struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_transc_type *p_transc) +u32 vcd_submit_frame(struct vcd_dev_ctxt *dev_ctxt, struct vcd_transc *transc) { - struct vcd_clnt_ctxt_type_t *p_cctxt = NULL; - struct vcd_frame_data_type *p_ip_frm_entry; - struct vcd_buffer_entry_type *p_op_buf_entry = NULL; + struct vcd_clnt_ctxt *cctxt = NULL; + struct vcd_frame_data *ip_frm_entry; + struct vcd_buffer_entry *op_buf_entry = NULL; u32 rc = VCD_S_SUCCESS; u32 evcode = 0; - struct ddl_frame_data_type_tag ddl_ip_frm; - struct ddl_frame_data_type_tag ddl_op_frm; + struct ddl_frame_data_tag ddl_ip_frm; + struct ddl_frame_data_tag ddl_op_frm; VCD_MSG_LOW("vcd_submit_frame:"); - p_cctxt = p_transc->p_cctxt; - p_ip_frm_entry = &p_transc->p_ip_buf_entry->frame; + cctxt = transc->cctxt; + ip_frm_entry = &transc->ip_buf_entry->frame; - p_transc->p_op_buf_entry = p_op_buf_entry; - p_transc->n_ip_frm_tag = p_ip_frm_entry->n_ip_frm_tag; - p_transc->time_stamp = p_ip_frm_entry->time_stamp; - p_ip_frm_entry->n_ip_frm_tag = (u32) p_transc; + transc->op_buf_entry = op_buf_entry; + transc->ip_frm_tag = ip_frm_entry->ip_frm_tag; + transc->time_stamp = ip_frm_entry->time_stamp; + ip_frm_entry->ip_frm_tag = (u32) transc; memset(&ddl_ip_frm, 0, sizeof(ddl_ip_frm)); memset(&ddl_op_frm, 0, sizeof(ddl_op_frm)); - if (p_cctxt->b_decoding) { + if (cctxt->decoding) { evcode = CLIENT_STATE_EVENT_NUMBER(pf_decode_frame); - ddl_ip_frm.vcd_frm = *p_ip_frm_entry; - rc = ddl_decode_frame(p_cctxt->ddl_handle, &ddl_ip_frm, - (void *) p_transc); + ddl_ip_frm.vcd_frm = *ip_frm_entry; + rc = ddl_decode_frame(cctxt->ddl_handle, &ddl_ip_frm, + (void *) transc); } else { - p_op_buf_entry = vcd_buffer_pool_entry_de_q( - &p_cctxt->out_buf_pool); - if (!p_op_buf_entry) { + op_buf_entry = vcd_buffer_pool_entry_de_q(&cctxt->out_buf_pool); + if (!op_buf_entry) { VCD_MSG_ERROR("Sched provided frame when no" "op buffer was present"); rc = VCD_ERR_FAIL; } else { - p_op_buf_entry->b_in_use = TRUE; - p_cctxt->out_buf_pool.n_in_use++; - ddl_ip_frm.vcd_frm = *p_ip_frm_entry; - ddl_ip_frm.n_frm_delta = - vcd_calculate_frame_delta(p_cctxt, - p_ip_frm_entry); + op_buf_entry->in_use = true; + cctxt->out_buf_pool.in_use++; + ddl_ip_frm.vcd_frm = *ip_frm_entry; + ddl_ip_frm.frm_delta = vcd_calculate_frame_delta(cctxt, + ip_frm_entry); - ddl_op_frm.vcd_frm = p_op_buf_entry->frame; + ddl_op_frm.vcd_frm = op_buf_entry->frame; evcode = CLIENT_STATE_EVENT_NUMBER(pf_encode_frame); - rc = ddl_encode_frame(p_cctxt->ddl_handle, - &ddl_ip_frm, &ddl_op_frm, (void *) p_transc); + rc = ddl_encode_frame(cctxt->ddl_handle, &ddl_ip_frm, + &ddl_op_frm, (void *) transc); } } - p_ip_frm_entry->n_ip_frm_tag = p_transc->n_ip_frm_tag; + ip_frm_entry->ip_frm_tag = transc->ip_frm_tag; if (!VCD_FAILED(rc)) { - vcd_device_timer_start(p_dev_ctxt); - p_cctxt->status.n_frame_submitted++; - if (p_ip_frm_entry->n_flags & VCD_FRAME_FLAG_EOS) - vcd_do_client_state_transition(p_cctxt, + vcd_device_timer_start(dev_ctxt); + cctxt->status.frame_submitted++; + if (ip_frm_entry->flags & VCD_FRAME_FLAG_EOS) + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_EOS, evcode); } else { VCD_MSG_ERROR("Frame submission failed. rc = 0x%x", rc); - vcd_handle_submit_frame_failed(p_dev_ctxt, p_transc); + vcd_handle_submit_frame_failed(dev_ctxt, transc); } - return TRUE; + return true; } -u32 vcd_try_submit_frame_in_continue(struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_transc_type *p_transc) +u32 vcd_try_submit_frame_in_continue(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc *transc) { - struct vcd_clnt_ctxt_type_t *p_cctxt = NULL; - struct vcd_buffer_entry_type *p_ip_buf_entry = NULL; + struct vcd_clnt_ctxt *cctxt = NULL; + struct vcd_buffer_entry *ip_buf_entry = NULL; VCD_MSG_LOW("vcd_try_submit_frame_in_continue:"); - if (!vcd_schedule_frame(p_dev_ctxt, &p_cctxt, &p_ip_buf_entry)) - return FALSE; + if (!vcd_schedule_frame(dev_ctxt, &cctxt, &ip_buf_entry)) + return false; - p_transc->p_cctxt = p_cctxt; - p_transc->p_ip_buf_entry = p_ip_buf_entry; + transc->cctxt = cctxt; + transc->ip_buf_entry = ip_buf_entry; - return vcd_submit_frame(p_dev_ctxt, p_transc); + return vcd_submit_frame(dev_ctxt, transc); } -u32 vcd_process_cmd_sess_start(struct vcd_clnt_ctxt_type_t *p_cctxt) +u32 vcd_process_cmd_sess_start(struct vcd_clnt_ctxt *cctxt) { - struct vcd_transc_type *p_transc; + struct vcd_transc *transc; u32 rc = VCD_S_SUCCESS; VCD_MSG_LOW("vcd_process_cmd_sess_start:"); - if (vcd_get_command_channel(p_cctxt->p_dev_ctxt, &p_transc)) { - rc = vcd_power_event(p_cctxt->p_dev_ctxt, - p_cctxt, VCD_EVT_PWR_CLNT_CMD_BEGIN); + if (vcd_get_command_channel(cctxt->dev_ctxt, &transc)) { + rc = vcd_power_event(cctxt->dev_ctxt, cctxt, + VCD_EVT_PWR_CLNT_CMD_BEGIN); if (!VCD_FAILED(rc)) { - p_transc->e_type = VCD_CMD_CODEC_START; - p_transc->p_cctxt = p_cctxt; - rc = vcd_submit_cmd_sess_start(p_transc); + transc->type = VCD_CMD_CODEC_START; + transc->cctxt = cctxt; + rc = vcd_submit_cmd_sess_start(transc); } else { VCD_MSG_ERROR("Failed: VCD_EVT_PWR_CLNT_CMD_BEGIN"); } if (VCD_FAILED(rc)) { - vcd_release_command_channel(p_cctxt->p_dev_ctxt, - p_transc); + vcd_release_command_channel(cctxt->dev_ctxt, + transc); } } else { - u32 b_result; + u32 result; - b_result = vcd_client_cmd_en_q(p_cctxt, VCD_CMD_CODEC_START); - if (!b_result) { + result = vcd_client_cmd_en_q(cctxt, VCD_CMD_CODEC_START); + if (!result) { rc = VCD_ERR_BUSY; VCD_MSG_ERROR("%s(): vcd_client_cmd_en_q() " "failed\n", __func__); @@ -1458,650 +1317,575 @@ u32 vcd_process_cmd_sess_start(struct vcd_clnt_ctxt_type_t *p_cctxt) } if (VCD_FAILED(rc)) { - (void)vcd_power_event(p_cctxt->p_dev_ctxt, - p_cctxt, VCD_EVT_PWR_CLNT_CMD_FAIL); + vcd_power_event(cctxt->dev_ctxt, cctxt, + VCD_EVT_PWR_CLNT_CMD_FAIL); } return rc; } -void vcd_send_frame_done_in_eos(struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_input_frame, u32 valid_opbuf) +void vcd_send_frame_done_in_eos(struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *input_frame, u32 valid_opbuf) { VCD_MSG_LOW("vcd_send_frame_done_in_eos:"); - if (!p_input_frame->p_virtual && !valid_opbuf) { + if (!input_frame->virt_addr && !valid_opbuf) { VCD_MSG_MED("Sending NULL output with EOS"); - p_cctxt->out_buf_pool.a_entries[0].frame.n_flags = - VCD_FRAME_FLAG_EOS; - p_cctxt->out_buf_pool.a_entries[0].frame.n_data_len = 0; - p_cctxt->out_buf_pool.a_entries[0].frame.time_stamp = - p_input_frame->time_stamp; - p_cctxt->out_buf_pool.a_entries[0].frame.n_ip_frm_tag = - p_input_frame->n_ip_frm_tag; - - p_cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, - VCD_S_SUCCESS, - &p_cctxt->out_buf_pool.a_entries[0].frame, - sizeof(struct vcd_frame_data_type), - p_cctxt, p_cctxt->p_client_data); - - memset(&p_cctxt->out_buf_pool.a_entries[0].frame, - 0, sizeof(struct vcd_frame_data_type)); - } else if (!p_input_frame->n_data_len) { - if (p_cctxt->b_decoding) { - vcd_send_frame_done_in_eos_for_dec(p_cctxt, - p_input_frame); - } else { - vcd_send_frame_done_in_eos_for_enc(p_cctxt, - p_input_frame); - } + cctxt->out_buf_pool.entries[0].frame.flags = VCD_FRAME_FLAG_EOS; + cctxt->out_buf_pool.entries[0].frame.data_len = 0; + cctxt->out_buf_pool.entries[0].frame.time_stamp = + input_frame->time_stamp; + cctxt->out_buf_pool.entries[0].frame.ip_frm_tag = + input_frame->ip_frm_tag; + + cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS, + &cctxt->out_buf_pool.entries[0].frame, + sizeof(struct vcd_frame_data), cctxt, + cctxt->client_data); + memset(&cctxt->out_buf_pool.entries[0].frame, 0, + sizeof(struct vcd_frame_data)); + } else if (!input_frame->data_len) { + if (cctxt->decoding) + vcd_send_frame_done_in_eos_for_dec(cctxt, input_frame); + else + vcd_send_frame_done_in_eos_for_enc(cctxt, input_frame); } } -void vcd_send_frame_done_in_eos_for_dec( - struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_input_frame) +void vcd_send_frame_done_in_eos_for_dec(struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *input_frame) { - struct vcd_buffer_entry_type *p_buf_entry; - struct vcd_property_hdr_type prop_hdr; + struct vcd_buffer_entry *buf_entry; + struct vcd_property_hdr prop_hdr; u32 rc; - struct ddl_frame_data_type_tag ddl_frm; + struct ddl_frame_data_tag ddl_frm; - prop_hdr.prop_id = DDL_I_DPB_RETRIEVE; - prop_hdr.n_size = sizeof(struct ddl_frame_data_type_tag); + prop_hdr.id = DDL_I_DPB_RETRIEVE; + prop_hdr.sz = sizeof(struct ddl_frame_data_tag); memset(&ddl_frm, 0, sizeof(ddl_frm)); - rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, &ddl_frm); + rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &ddl_frm); - if (VCD_FAILED(rc) || !ddl_frm.vcd_frm.p_virtual) { - p_cctxt->status.eos_trig_ip_frm = *p_input_frame; - - p_cctxt->status.b_eos_wait_for_op_buf = TRUE; + if (VCD_FAILED(rc) || !ddl_frm.vcd_frm.virt_addr) { + cctxt->status.eos_trig_ip_frm = *input_frame; + cctxt->status.eos_wait_for_op_buf = true; return; } - p_buf_entry = vcd_find_buffer_pool_entry(&p_cctxt->out_buf_pool, - ddl_frm.vcd_frm.p_virtual); - if (!p_buf_entry) { - VCD_MSG_ERROR("Unrecognized buffer address provided = %p", - ddl_frm.vcd_frm.p_virtual); - + buf_entry = vcd_find_buffer_pool_entry(&cctxt->out_buf_pool, + ddl_frm.vcd_frm.virt_addr); + if (!buf_entry) { + VCD_MSG_ERROR("Unrecognized buffer address provided %p", + ddl_frm.vcd_frm.virt_addr); vcd_assert(); } else { - (void) - vcd_map_sched_status(sched_update_client_o_tkn - (p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, FALSE, - p_cctxt->n_sched_o_tkn_per_ip_frm)); + vcd_map_sched_status(sched_update_client_o_tkn( + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl,\ + false, cctxt->sched_o_tkn_per_ip_frm)); VCD_MSG_MED("Sending non-NULL output with EOS"); - p_buf_entry->frame.n_data_len = 0; - p_buf_entry->frame.n_offset = 0; - p_buf_entry->frame.n_flags |= VCD_FRAME_FLAG_EOS; - p_buf_entry->frame.n_ip_frm_tag = p_input_frame->n_ip_frm_tag; - p_buf_entry->frame.time_stamp = p_input_frame->time_stamp; + buf_entry->frame.data_len = 0; + buf_entry->frame.offset = 0; + buf_entry->frame.flags |= VCD_FRAME_FLAG_EOS; + buf_entry->frame.ip_frm_tag = input_frame->ip_frm_tag; + buf_entry->frame.time_stamp = input_frame->time_stamp; - p_cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, - VCD_S_SUCCESS, - &p_buf_entry->frame, - sizeof(struct vcd_frame_data_type), - p_cctxt, p_cctxt->p_client_data); + cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS, + &buf_entry->frame, sizeof(struct vcd_frame_data), + cctxt, cctxt->client_data); - p_buf_entry->b_in_use = FALSE; - VCD_BUFFERPOOL_INUSE_DECREMENT(p_cctxt->out_buf_pool.n_in_use); + buf_entry->in_use = false; + VCD_BUFFERPOOL_INUSE_DECREMENT(cctxt->out_buf_pool.in_use); } } -void vcd_send_frame_done_in_eos_for_enc( - struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_input_frame) +void vcd_send_frame_done_in_eos_for_enc(struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *input_frame) { - struct vcd_buffer_entry_type *p_op_buf_entry; + struct vcd_buffer_entry *op_buf_entry; - if (!p_cctxt->out_buf_pool.n_q_len) { - p_cctxt->status.eos_trig_ip_frm = *p_input_frame; + if (!cctxt->out_buf_pool.q_len) { + cctxt->status.eos_trig_ip_frm = *input_frame; - p_cctxt->status.b_eos_wait_for_op_buf = TRUE; + cctxt->status.eos_wait_for_op_buf = true; return; } - p_op_buf_entry = vcd_buffer_pool_entry_de_q(&p_cctxt->out_buf_pool); - if (!p_op_buf_entry) { + op_buf_entry = vcd_buffer_pool_entry_de_q(&cctxt->out_buf_pool); + if (!op_buf_entry) { VCD_MSG_ERROR("%s(): vcd_buffer_pool_entry_de_q() " "failed\n", __func__); vcd_assert(); } else { - (void) - vcd_map_sched_status(sched_update_client_o_tkn - (p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, FALSE, - p_cctxt->n_sched_o_tkn_per_ip_frm)); + vcd_map_sched_status(sched_update_client_o_tkn( + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, + false, cctxt->sched_o_tkn_per_ip_frm)); VCD_MSG_MED("Sending non-NULL output with EOS"); - p_op_buf_entry->frame.n_data_len = 0; - p_op_buf_entry->frame.n_flags |= VCD_FRAME_FLAG_EOS; - p_op_buf_entry->frame.n_ip_frm_tag = - p_input_frame->n_ip_frm_tag; - p_op_buf_entry->frame.time_stamp = p_input_frame->time_stamp; + op_buf_entry->frame.data_len = 0; + op_buf_entry->frame.flags |= VCD_FRAME_FLAG_EOS; + op_buf_entry->frame.ip_frm_tag = input_frame->ip_frm_tag; + op_buf_entry->frame.time_stamp = input_frame->time_stamp; - p_cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, - VCD_S_SUCCESS, - &p_op_buf_entry->frame, - sizeof(struct vcd_frame_data_type), - p_cctxt, p_cctxt->p_client_data); + cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS, + &op_buf_entry->frame, sizeof(struct vcd_frame_data), + cctxt, cctxt->client_data); } } -u32 vcd_handle_recvd_eos( - struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_input_frame, u32 *pb_eos_handled) +u32 vcd_handle_recvd_eos(struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *input_frame, u32 *pb_eos_handled) { union sched_value_type sched_val; u32 rc; VCD_MSG_LOW("vcd_handle_recvd_eos:"); - *pb_eos_handled = FALSE; + *pb_eos_handled = false; - if (p_input_frame->p_virtual && - p_input_frame->n_data_len) + if (input_frame->virt_addr && input_frame->data_len) return VCD_S_SUCCESS; - p_input_frame->n_data_len = 0; + input_frame->data_len = 0; - rc = vcd_map_sched_status(sched_get_client_param - (p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, - SCHED_I_CLNT_CURRQLEN, &sched_val)); + rc = vcd_map_sched_status(sched_get_client_param( + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, + SCHED_I_CLNT_CURRQLEN, &sched_val)); VCD_FAILED_RETURN(rc, "Failed: sched_get_client_param"); if (sched_val.un_value > 0) { - rc = vcd_map_sched_status(sched_mark_client_eof - (p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl)); + rc = vcd_map_sched_status(sched_mark_client_eof( + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl)); if (!VCD_FAILED(rc)) { - *pb_eos_handled = TRUE; + *pb_eos_handled = true; } else { VCD_MSG_ERROR("rc = 0x%x. Failed: " "sched_mark_client_eof", rc); } - } else if (p_cctxt->b_decoding && !p_input_frame->p_virtual) { - rc = vcd_map_sched_status(sched_update_client_o_tkn - (p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, TRUE, - p_cctxt->n_sched_o_tkn_per_ip_frm)); - } else if (!p_cctxt->b_decoding) { + } else if (cctxt->decoding && !input_frame->virt_addr) { + rc = vcd_map_sched_status(sched_update_client_o_tkn( + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, true, + cctxt->sched_o_tkn_per_ip_frm)); + } else if (!cctxt->decoding) { - vcd_send_frame_done_in_eos(p_cctxt, p_input_frame, FALSE); + vcd_send_frame_done_in_eos(cctxt, input_frame, false); - if (p_cctxt->status.b_eos_wait_for_op_buf) { - vcd_do_client_state_transition(p_cctxt, + if (cctxt->status.eos_wait_for_op_buf) { + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_EOS, - CLIENT_STATE_EVENT_NUMBER - (pf_encode_frame)); + CLIENT_STATE_EVENT_NUMBER(pf_encode_frame)); } - *pb_eos_handled = TRUE; + *pb_eos_handled = true; } - if (*pb_eos_handled && - p_input_frame->p_virtual && - !p_input_frame->n_data_len) { - p_cctxt->callback(VCD_EVT_RESP_INPUT_DONE, - VCD_S_SUCCESS, - p_input_frame, - sizeof(struct vcd_frame_data_type), - p_cctxt, p_cctxt->p_client_data); + if (*pb_eos_handled && input_frame->virt_addr && + !input_frame->data_len) { + cctxt->callback(VCD_EVT_RESP_INPUT_DONE, VCD_S_SUCCESS, + input_frame, sizeof(struct vcd_frame_data), cctxt, + cctxt->client_data); } return rc; } -u32 vcd_handle_first_decode_frame(struct vcd_clnt_ctxt_type_t *p_cctxt) +u32 vcd_handle_first_decode_frame(struct vcd_clnt_ctxt *cctxt) { - struct ddl_property_dec_pic_buffers_type dpb; - struct vcd_property_hdr_type prop_hdr; + struct ddl_property_dec_pic_buffers dpb; + struct vcd_property_hdr prop_hdr; u32 rc; u16 i; - u16 n_q_cntr; - struct ddl_frame_data_type_tag *p_frm_entry; - struct ddl_frame_data_type_tag ddl_frm; - struct vcd_buffer_pool_type *p_out_buf_pool; + u16 q_cntr; + struct ddl_frame_data_tag *frm_entry; + struct ddl_frame_data_tag ddl_frm; + struct vcd_buffer_pool *out_buf_pool; VCD_MSG_LOW("vcd_handle_first_decode_frame:"); - if (!p_cctxt->in_buf_pool.a_entries || - !p_cctxt->out_buf_pool.a_entries || - p_cctxt->in_buf_pool.n_validated != - p_cctxt->in_buf_pool.n_count || - p_cctxt->out_buf_pool.n_validated != - p_cctxt->out_buf_pool.n_count) { + if (!cctxt->in_buf_pool.entries || !cctxt->out_buf_pool.entries || + cctxt->in_buf_pool.validated != + cctxt->in_buf_pool.count || + cctxt->out_buf_pool.validated != + cctxt->out_buf_pool.count) { VCD_MSG_ERROR("Buffer pool is not completely setup yet"); return VCD_ERR_BAD_STATE; } - rc = vcd_add_client_to_sched(p_cctxt); + rc = vcd_add_client_to_sched(cctxt); VCD_FAILED_RETURN(rc, "Failed: vcd_add_client_to_sched"); - prop_hdr.prop_id = DDL_I_DPB; - prop_hdr.n_size = sizeof(dpb); + prop_hdr.id = DDL_I_DPB; + prop_hdr.sz = sizeof(dpb); - p_out_buf_pool = &p_cctxt->out_buf_pool; + out_buf_pool = &cctxt->out_buf_pool; - p_frm_entry = - (struct ddl_frame_data_type_tag *) - vcd_malloc(sizeof(struct ddl_frame_data_type_tag) * - p_out_buf_pool->n_count); - if (!p_frm_entry) { + frm_entry = kmalloc(sizeof(struct ddl_frame_data_tag) * + out_buf_pool->count, GFP_KERNEL); + if (!frm_entry) { VCD_MSG_ERROR("Memory allocation failure"); - return VCD_ERR_ALLOC_FAIL; } - for (i = 1; i <= p_out_buf_pool->n_count; i++) - p_frm_entry[i - 1].vcd_frm = p_out_buf_pool->a_entries[i].frame; + for (i = 1; i <= out_buf_pool->count; i++) + frm_entry[i - 1].vcd_frm = out_buf_pool->entries[i].frame; + dpb.dec_pic_buffers = frm_entry; + dpb.no_of_dec_pic_buf = out_buf_pool->count; + rc = ddl_set_property(cctxt->ddl_handle, &prop_hdr, &dpb); - dpb.a_dec_pic_buffers = p_frm_entry; - dpb.n_no_of_dec_pic_buf = p_out_buf_pool->n_count; - rc = ddl_set_property(p_cctxt->ddl_handle, &prop_hdr, &dpb); - - vcd_free(p_frm_entry); + kfree(frm_entry); VCD_FAILED_RETURN(rc, "Failed: DDL set DDL_I_DPB"); - if (p_out_buf_pool->n_q_len > 0) { - prop_hdr.prop_id = DDL_I_DPB_RELEASE; - prop_hdr.n_size = sizeof(struct ddl_frame_data_type_tag); + if (out_buf_pool->q_len > 0) { + prop_hdr.id = DDL_I_DPB_RELEASE; + prop_hdr.sz = sizeof(struct ddl_frame_data_tag); - for (i = 0, n_q_cntr = p_out_buf_pool->n_q_head; - !VCD_FAILED(rc) && i < p_out_buf_pool->n_q_len; - i++, n_q_cntr = (n_q_cntr + 1) % - p_out_buf_pool->n_count) { + for (i = 0, q_cntr = out_buf_pool->q_head; !VCD_FAILED(rc) && + i < out_buf_pool->q_len; i++, + q_cntr = (q_cntr + 1) % out_buf_pool->count) { - ddl_frm.vcd_frm = - p_out_buf_pool->a_queue[n_q_cntr]->frame; + ddl_frm.vcd_frm = out_buf_pool->queue[q_cntr]->frame; - rc = ddl_set_property(p_cctxt->ddl_handle, &prop_hdr, - &ddl_frm); + rc = ddl_set_property(cctxt->ddl_handle, &prop_hdr, + &ddl_frm); if (VCD_FAILED(rc)) { VCD_MSG_ERROR ("Error returning output buffer to HW"); - p_out_buf_pool->a_queue[n_q_cntr]->b_in_use = - FALSE; + out_buf_pool->queue[q_cntr]->in_use = false; } else { - p_out_buf_pool->a_queue[n_q_cntr]->b_in_use = - TRUE; - p_out_buf_pool->n_in_use++; + out_buf_pool->queue[q_cntr]->in_use = true; + out_buf_pool->in_use++; } } - if (!VCD_FAILED(rc)) { - rc = vcd_map_sched_status(sched_update_client_o_tkn - (p_cctxt->p_dev_ctxt-> - sched_hdl, - p_cctxt->sched_clnt_hdl, - TRUE, - p_cctxt-> - n_sched_o_tkn_per_ip_frm * - p_out_buf_pool->n_q_len)); - } + if (VCD_FAILED(rc)) + return rc; + rc = vcd_map_sched_status(sched_update_client_o_tkn( + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, true, + cctxt->sched_o_tkn_per_ip_frm * out_buf_pool->q_len)); } return rc; } -u32 vcd_setup_with_ddl_capabilities(struct vcd_dev_ctxt_type *p_dev_ctxt) +u32 vcd_setup_with_ddl_capabilities(struct vcd_dev_ctxt *dev_ctxt) { - struct vcd_property_hdr_type Prop_hdr; - struct ddl_property_capability_type capability; + struct vcd_property_hdr prop_hdr; + struct ddl_property_capability capability; u32 rc = VCD_S_SUCCESS; VCD_MSG_LOW("vcd_setup_with_ddl_capabilities:"); - if (!p_dev_ctxt->n_ddl_cmd_ch_depth) { - Prop_hdr.prop_id = DDL_I_CAPABILITY; - Prop_hdr.n_size = sizeof(capability); + if (dev_ctxt->ddl_cmd_ch_depth) + goto out; - /* - ** Since this is underlying core's property we don't need a - ** ddl client handle. - */ - rc = ddl_get_property(NULL, &Prop_hdr, &capability); + prop_hdr.id = DDL_I_CAPABILITY; + prop_hdr.sz = sizeof(capability); - if (!VCD_FAILED(rc)) { - /* - ** Allocate the transaction table. - */ - p_dev_ctxt->n_trans_tbl_size = - (VCD_MAX_CLIENT_TRANSACTIONS * - capability.n_max_num_client) + - capability.n_general_command_depth; - - p_dev_ctxt->a_trans_tbl = (struct vcd_transc_type *) - vcd_malloc(sizeof(struct vcd_transc_type) * - p_dev_ctxt->n_trans_tbl_size); - - if (!p_dev_ctxt->a_trans_tbl) { - VCD_MSG_ERROR("Transaction table alloc failed"); - - rc = VCD_ERR_ALLOC_FAIL; - } else { - memset(p_dev_ctxt->a_trans_tbl, 0, - sizeof(struct vcd_transc_type) * - p_dev_ctxt->n_trans_tbl_size); - - /* - ** Set the command/frame depth - */ - p_dev_ctxt->b_ddl_cmd_concurrency = - !capability.b_exclusive; - p_dev_ctxt->n_ddl_frame_ch_depth = - capability.n_frame_command_depth; - p_dev_ctxt->n_ddl_cmd_ch_depth = - capability.n_general_command_depth; - - vcd_reset_device_channels(p_dev_ctxt); - - p_dev_ctxt->n_hw_time_out = - capability.n_ddl_time_out_in_ms; + /* + * Since this is underlying core's property we don't need a + * ddl client handle. + */ + rc = ddl_get_property(NULL, &prop_hdr, &capability); - } - } + if (VCD_FAILED(rc)) + goto out; + + /* + ** Allocate the transaction table. + */ + dev_ctxt->trans_tbl_size = VCD_MAX_CLIENT_TRANSACTIONS * + capability.max_num_client + capability.general_command_depth; + + dev_ctxt->trans_tbl = kzalloc(sizeof(struct vcd_transc) * + dev_ctxt->trans_tbl_size, GFP_KERNEL); + if (!dev_ctxt->trans_tbl) { + VCD_MSG_ERROR("Transaction table alloc failed"); + rc = VCD_ERR_ALLOC_FAIL; + goto out; } + + /* + ** Set the command/frame depth + */ + dev_ctxt->ddl_cmd_concurrency = !capability.exclusive; + dev_ctxt->ddl_frame_ch_depth = capability.frame_command_depth; + dev_ctxt->ddl_cmd_ch_depth = capability.general_command_depth; + + vcd_reset_device_channels(dev_ctxt); + + dev_ctxt->hw_time_out = capability.ddl_time_out_in_ms; + +out: return rc; } -struct vcd_transc_type *vcd_get_free_trans_tbl_entry - (struct vcd_dev_ctxt_type *p_dev_ctxt) { +struct vcd_transc *vcd_get_free_trans_tbl_entry(struct vcd_dev_ctxt *dev_ctxt) +{ u8 i; - if (!p_dev_ctxt->a_trans_tbl) + if (!dev_ctxt->trans_tbl) return NULL; i = 0; - while (i < p_dev_ctxt->n_trans_tbl_size && - p_dev_ctxt->a_trans_tbl[i].b_in_use) + while (i < dev_ctxt->trans_tbl_size && dev_ctxt->trans_tbl[i].in_use) i++; - if (i == p_dev_ctxt->n_trans_tbl_size) { + if (i == dev_ctxt->trans_tbl_size) { return NULL; } else { - memset(&p_dev_ctxt->a_trans_tbl[i], 0, - sizeof(struct vcd_transc_type)); + memset(&dev_ctxt->trans_tbl[i], 0, sizeof(struct vcd_transc)); - p_dev_ctxt->a_trans_tbl[i].b_in_use = TRUE; + dev_ctxt->trans_tbl[i].in_use = true; - return &p_dev_ctxt->a_trans_tbl[i]; + return &dev_ctxt->trans_tbl[i]; } } -void vcd_release_trans_tbl_entry(struct vcd_transc_type *p_trans_entry) +void vcd_release_trans_tbl_entry(struct vcd_transc *trans_entry) { - if (p_trans_entry) - p_trans_entry->b_in_use = FALSE; + if (trans_entry) + trans_entry->in_use = false; } -u32 vcd_add_client_to_sched(struct vcd_clnt_ctxt_type_t *p_cctxt) +u32 vcd_add_client_to_sched(struct vcd_clnt_ctxt *cctxt) { - struct vcd_property_hdr_type prop_hdr; - struct sched_client_init_param_type sched_input_init; + struct vcd_property_hdr prop_hdr; + struct sched_client_init_param sched_input_init; u32 rc, seqhdr_present = 0;; - if (p_cctxt->b_sched_clnt_valid) { + if (cctxt->sched_clnt_valid) { VCD_MSG_HIGH("Schedulder client is already added "); return VCD_S_SUCCESS; } - prop_hdr.prop_id = DDL_I_FRAME_PROC_UNITS; - prop_hdr.n_size = sizeof(p_cctxt->n_frm_p_units); - rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, - &p_cctxt->n_frm_p_units); + prop_hdr.id = DDL_I_FRAME_PROC_UNITS; + prop_hdr.sz = sizeof(cctxt->frm_p_units); + rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, + &cctxt->frm_p_units); VCD_FAILED_RETURN(rc, "Failed: Get DDL_I_FRAME_PROC_UNITS"); - if (p_cctxt->b_decoding) { - p_cctxt->frm_rate.n_fps_numerator = VCD_DEC_INITIAL_FRAME_RATE; - p_cctxt->frm_rate.n_fps_denominator = 1; + if (cctxt->decoding) { + cctxt->frm_rate.fps_numerator = VCD_DEC_INITIAL_FRAME_RATE; + cctxt->frm_rate.fps_denominator = 1; - sched_input_init.n_o_tkn_per_ip_frm = - p_cctxt->n_sched_o_tkn_per_ip_frm = + sched_input_init.o_tkn_per_ip_frm = + VCD_SCHEDULER_DEC_DFLT_OTKN_PERFRM; + cctxt->sched_o_tkn_per_ip_frm = VCD_SCHEDULER_DEC_DFLT_OTKN_PERFRM; - sched_input_init.n_o_tkn_max = - p_cctxt->n_sched_o_tkn_per_ip_frm * - p_cctxt->out_buf_pool.n_count+1; + + sched_input_init.o_tkn_max = cctxt->sched_o_tkn_per_ip_frm * + cctxt->out_buf_pool.count+1; } else { - sched_input_init.n_o_tkn_per_ip_frm = - p_cctxt->n_sched_o_tkn_per_ip_frm = + sched_input_init.o_tkn_per_ip_frm = + VCD_SCHEDULER_ENC_DFLT_OTKN_PERFRM; + cctxt->sched_o_tkn_per_ip_frm = VCD_SCHEDULER_ENC_DFLT_OTKN_PERFRM; - prop_hdr.prop_id = DDL_I_SEQHDR_PRESENT; - prop_hdr.n_size = sizeof(seqhdr_present); - rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, - &seqhdr_present); + prop_hdr.id = DDL_I_SEQHDR_PRESENT; + prop_hdr.sz = sizeof(seqhdr_present); + rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, + &seqhdr_present); if (!VCD_FAILED(rc)) { if (seqhdr_present == 0x1) { VCD_MSG_MED("Sequence hdr present"); - sched_input_init.n_o_tkn_per_ip_frm++; + sched_input_init.o_tkn_per_ip_frm++; } - sched_input_init.n_o_tkn_max = - p_cctxt->out_buf_pool.n_count; - prop_hdr.prop_id = VCD_I_FRAME_RATE; - prop_hdr.n_size = sizeof(p_cctxt->frm_rate); - rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, - &p_cctxt->frm_rate); + sched_input_init.o_tkn_max = cctxt->out_buf_pool.count; + prop_hdr.id = VCD_I_FRAME_RATE; + prop_hdr.sz = sizeof(cctxt->frm_rate); + rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, + &cctxt->frm_rate); } } VCD_FAILED_RETURN(rc, "Failed: DDL get VCD_I_FRAME_RATE"); - if (p_cctxt->b_live) + if (cctxt->live) sched_input_init.client_ctgy = SCHED_CLNT_RT_NOBUFF; else sched_input_init.client_ctgy = SCHED_CLNT_NONRT; - sched_input_init.n_max_queue_len = MAX(p_cctxt->in_buf_pool.n_count, - VCD_MAX_SCHEDULER_QUEUE_SIZE - (p_cctxt->frm_rate. - n_fps_numerator, - p_cctxt->frm_rate. - n_fps_denominator)); - p_cctxt->n_reqd_perf_lvl = - p_cctxt->n_frm_p_units * p_cctxt->frm_rate.n_fps_numerator / - p_cctxt->frm_rate.n_fps_denominator; + sched_input_init.max_queue_len = max(cctxt->in_buf_pool.count, + VCD_MAX_SCHEDULER_QUEUE_SIZE(cctxt->frm_rate.fps_numerator, + cctxt->frm_rate.fps_denominator)); + cctxt->reqd_perf_lvl = cctxt->frm_p_units * + cctxt->frm_rate.fps_numerator / cctxt->frm_rate.fps_denominator; - sched_input_init.frm_rate.n_numer = p_cctxt->frm_rate.n_fps_numerator; - sched_input_init.frm_rate.n_denom = p_cctxt->frm_rate.n_fps_denominator; - sched_input_init.n_p_tkn_per_frm = p_cctxt->n_frm_p_units; - sched_input_init.n_alloc_p_tkn_rate = p_cctxt->n_reqd_perf_lvl; + sched_input_init.frm_rate.numer = cctxt->frm_rate.fps_numerator; + sched_input_init.frm_rate.denom = cctxt->frm_rate.fps_denominator; + sched_input_init.tkn_per_frm = cctxt->frm_p_units; + sched_input_init.alloc_p_tkn_rate = cctxt->reqd_perf_lvl; - sched_input_init.n_o_tkn_init = 0; + sched_input_init.o_tkn_init = 0; - sched_input_init.p_client_data = p_cctxt; + sched_input_init.client_data = cctxt; - rc = vcd_map_sched_status(sched_add_client - (p_cctxt->p_dev_ctxt->sched_hdl, - &sched_input_init, - &p_cctxt->sched_clnt_hdl)); + rc = vcd_map_sched_status(sched_add_client(cctxt->dev_ctxt->sched_hdl, + &sched_input_init, &cctxt->sched_clnt_hdl)); if (!VCD_FAILED(rc)) - p_cctxt->b_sched_clnt_valid = TRUE; + cctxt->sched_clnt_valid = true; return rc; } -u32 vcd_handle_input_done( - struct vcd_clnt_ctxt_type_t *p_cctxt, - void *p_payload, u32 event, u32 status) +u32 vcd_handle_input_done(struct vcd_clnt_ctxt *cctxt, void *payload, u32 event, + u32 status) { - struct vcd_transc_type *p_transc; - struct ddl_frame_data_type_tag *p_frame = - (struct ddl_frame_data_type_tag *) p_payload; + struct vcd_transc *transc; + struct ddl_frame_data_tag *frame = (struct ddl_frame_data_tag *)payload; u32 rc; - if (!p_cctxt->status.n_frame_submitted && - !p_cctxt->status.n_frame_delayed) { + if (!cctxt->status.frame_submitted && !cctxt->status.frame_delayed) { VCD_MSG_ERROR("Input done was not expected"); vcd_assert(); return VCD_ERR_BAD_STATE; } - rc = vcd_validate_io_done_pyld(p_payload, status); + rc = vcd_validate_io_done_pyld(payload, status); VCD_FAILED_RETURN(rc, "Bad input done payload"); - p_transc = (struct vcd_transc_type *)p_frame->vcd_frm.n_ip_frm_tag; + transc = (struct vcd_transc *)frame->vcd_frm.ip_frm_tag; - if ((p_transc->p_ip_buf_entry->frame.p_virtual != - p_frame->vcd_frm.p_virtual) - || !p_transc->p_ip_buf_entry->b_in_use) { + if (transc->ip_buf_entry->frame.virt_addr != frame->vcd_frm.virt_addr || + !transc->ip_buf_entry->in_use) { VCD_MSG_ERROR("Bad frm transaction state"); vcd_assert(); } - p_frame->vcd_frm.n_ip_frm_tag = p_transc->n_ip_frm_tag; + frame->vcd_frm.ip_frm_tag = transc->ip_frm_tag; - p_cctxt->callback(event, - status, - &p_frame->vcd_frm, - sizeof(struct vcd_frame_data_type), - p_cctxt, p_cctxt->p_client_data); + cctxt->callback(event, status, &frame->vcd_frm, + sizeof(struct vcd_frame_data), cctxt, cctxt->client_data); - p_transc->e_frame_type = p_frame->vcd_frm.e_frame_type; + transc->frame_type = frame->vcd_frm.frame_type; - p_transc->p_ip_buf_entry->b_in_use = FALSE; - VCD_BUFFERPOOL_INUSE_DECREMENT(p_cctxt->in_buf_pool.n_in_use); - p_transc->p_ip_buf_entry = NULL; - p_transc->b_input_done = TRUE; + transc->ip_buf_entry->in_use = false; + VCD_BUFFERPOOL_INUSE_DECREMENT(cctxt->in_buf_pool.in_use); + transc->ip_buf_entry = NULL; + transc->input_done = true; - if (p_transc->b_input_done && p_transc->b_frame_done) - p_transc->b_in_use = FALSE; + if (transc->input_done && transc->frame_done) + transc->in_use = false; if (VCD_FAILED(status)) { VCD_MSG_ERROR("INPUT_DONE returned err = 0x%x", status); - vcd_handle_input_done_failed(p_cctxt, p_transc); + vcd_handle_input_done_failed(cctxt, transc); } - if (p_cctxt->status.n_frame_submitted > 0) - p_cctxt->status.n_frame_submitted--; + if (cctxt->status.frame_submitted > 0) + cctxt->status.frame_submitted--; else - p_cctxt->status.n_frame_delayed--; + cctxt->status.frame_delayed--; - if (!VCD_FAILED(status) && - p_cctxt->b_decoding) { - if (p_frame->vcd_frm.b_interlaced) - vcd_handle_input_done_for_interlacing(p_cctxt); - if (p_frame->b_frm_trans_end) - vcd_handle_input_done_with_trans_end(p_cctxt); + if (!VCD_FAILED(status) && cctxt->decoding) { + if (frame->vcd_frm.interlaced) + vcd_handle_input_done_for_interlacing(cctxt); + if (frame->frm_trans_end) + vcd_handle_input_done_with_trans_end(cctxt); } return VCD_S_SUCCESS; } -void vcd_handle_input_done_in_eos( - struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload, u32 status) +void vcd_handle_input_done_in_eos(struct vcd_clnt_ctxt *cctxt, void *payload, + u32 status) { - struct vcd_transc_type *p_transc; - struct ddl_frame_data_type_tag *p_frame = - (struct ddl_frame_data_type_tag *) p_payload; + struct vcd_transc *transc; + struct ddl_frame_data_tag *frame = (struct ddl_frame_data_tag *)payload; - if (VCD_FAILED(vcd_validate_io_done_pyld(p_payload, status))) + if (VCD_FAILED(vcd_validate_io_done_pyld(payload, status))) return; - p_transc = (struct vcd_transc_type *)p_frame->vcd_frm.n_ip_frm_tag; + transc = (struct vcd_transc *)frame->vcd_frm.ip_frm_tag; - (void)vcd_handle_input_done(p_cctxt, - p_payload, VCD_EVT_RESP_INPUT_DONE, status); + vcd_handle_input_done(cctxt, payload, VCD_EVT_RESP_INPUT_DONE, status); - if ((p_frame->vcd_frm.n_flags & VCD_FRAME_FLAG_EOS)) { + if ((frame->vcd_frm.flags & VCD_FRAME_FLAG_EOS)) { VCD_MSG_HIGH("Got input done for EOS initiator"); - p_transc->b_input_done = FALSE; - p_transc->b_in_use = TRUE; + transc->input_done = false; + transc->in_use = true; } } -u32 vcd_validate_io_done_pyld(void *p_payload, u32 status) +u32 vcd_validate_io_done_pyld(void *payload, u32 status) { - struct ddl_frame_data_type_tag *p_frame = - (struct ddl_frame_data_type_tag *) p_payload; + struct ddl_frame_data_tag *frame = (struct ddl_frame_data_tag *)payload; - if (!p_frame) { + if (!frame) { VCD_MSG_ERROR("Bad payload from DDL"); vcd_assert(); return VCD_ERR_BAD_POINTER; } - if (!p_frame->vcd_frm.n_ip_frm_tag || - p_frame->vcd_frm.n_ip_frm_tag == VCD_FRAMETAG_INVALID) { + if (!frame->vcd_frm.ip_frm_tag || frame->vcd_frm.ip_frm_tag == + VCD_FRAMETAG_INVALID) { VCD_MSG_ERROR("bad input frame tag"); vcd_assert(); return VCD_ERR_BAD_POINTER; } - if (!p_frame->vcd_frm.p_virtual && - status != VCD_ERR_INTRLCD_FIELD_DROP) + if (!frame->vcd_frm.virt_addr && status != VCD_ERR_INTRLCD_FIELD_DROP) return VCD_ERR_BAD_POINTER; return VCD_S_SUCCESS; } -void vcd_handle_input_done_failed( - struct vcd_clnt_ctxt_type_t *p_cctxt, struct vcd_transc_type *p_transc) +void vcd_handle_input_done_failed(struct vcd_clnt_ctxt *cctxt, + struct vcd_transc *transc) { - if (p_cctxt->b_decoding) { - (void)vcd_map_sched_status(sched_update_client_o_tkn - (p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, TRUE, - p_cctxt->n_sched_o_tkn_per_ip_frm)); + if (cctxt->decoding) { + vcd_map_sched_status(sched_update_client_o_tkn( + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, true, + cctxt->sched_o_tkn_per_ip_frm)); - p_transc->b_in_use = FALSE; + transc->in_use = false; } } -void vcd_handle_input_done_for_interlacing(struct vcd_clnt_ctxt_type_t *p_cctxt) +void vcd_handle_input_done_for_interlacing(struct vcd_clnt_ctxt *cctxt) { u32 rc; - p_cctxt->status.n_int_field_cnt++; + cctxt->status.int_field_cnt++; - if (p_cctxt->status.n_int_field_cnt == 1) { - rc = vcd_map_sched_status(sched_update_client_o_tkn - (p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, TRUE, - p_cctxt->n_sched_o_tkn_per_ip_frm)); + if (cctxt->status.int_field_cnt == 1) { + rc = vcd_map_sched_status(sched_update_client_o_tkn( + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, true, + cctxt->sched_o_tkn_per_ip_frm)); if (VCD_FAILED(rc)) VCD_MSG_ERROR("sched_update_client_o_tkn failed"); - } else if (p_cctxt->status.n_int_field_cnt == - VCD_DEC_NUM_INTERLACED_FIELDS) - p_cctxt->status.n_int_field_cnt = 0; + } else if (cctxt->status.int_field_cnt == VCD_DEC_NUM_INTERLACED_FIELDS) + cctxt->status.int_field_cnt = 0; } -void vcd_handle_input_done_with_trans_end( - struct vcd_clnt_ctxt_type_t *p_cctxt) +void vcd_handle_input_done_with_trans_end(struct vcd_clnt_ctxt *cctxt) { u32 rc; union sched_value_type sched_val; - if (!p_cctxt->b_decoding) + if (!cctxt->decoding) return; - if (p_cctxt->out_buf_pool.n_in_use < - p_cctxt->out_buf_pool.buf_req.n_min_count) + if (cctxt->out_buf_pool.in_use < cctxt->out_buf_pool.buf_req.min_count) return; rc = vcd_map_sched_status(sched_get_client_param( - p_cctxt->p_dev_ctxt->sched_hdl, p_cctxt->sched_clnt_hdl, + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, SCHED_I_CLNT_OTKNCURRENT, &sched_val)); if (VCD_FAILED(rc)) { @@ -2113,76 +1897,66 @@ void vcd_handle_input_done_with_trans_end( VCD_MSG_MED("All output buffers with core are pending display"); rc = vcd_map_sched_status(sched_update_client_o_tkn( - p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, - TRUE, - p_cctxt->n_sched_o_tkn_per_ip_frm)); + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, true, + cctxt->sched_o_tkn_per_ip_frm)); if (VCD_FAILED(rc)) VCD_MSG_ERROR("sched_update_client_o_tkn failed"); } } -u32 vcd_handle_output_required(struct vcd_clnt_ctxt_type_t - *p_cctxt, void *p_payload, u32 status) +u32 vcd_handle_output_required(struct vcd_clnt_ctxt *cctxt, void *payload, + u32 status) { - struct vcd_transc_type *p_transc; - struct ddl_frame_data_type_tag *p_frame = - (struct ddl_frame_data_type_tag *)p_payload; + struct vcd_transc *transc; + struct ddl_frame_data_tag *frame = (struct ddl_frame_data_tag *)payload; u32 rc; - if (!p_cctxt->status.n_frame_submitted && - !p_cctxt->status.n_frame_delayed) { + if (!cctxt->status.frame_submitted && !cctxt->status.frame_delayed) { VCD_MSG_ERROR("\n Input done was not expected"); return VCD_ERR_BAD_STATE; } - rc = vcd_validate_io_done_pyld(p_payload, status); + rc = vcd_validate_io_done_pyld(payload, status); VCD_FAILED_RETURN(rc, "\n Bad input done payload"); - p_transc = (struct vcd_transc_type *)p_frame-> - vcd_frm.n_ip_frm_tag; + transc = (struct vcd_transc *)frame->vcd_frm.ip_frm_tag; - if ((p_transc->p_ip_buf_entry->frame.p_virtual != - p_frame->vcd_frm.p_virtual) || - !p_transc->p_ip_buf_entry->b_in_use) { + if (transc->ip_buf_entry->frame.virt_addr != frame->vcd_frm.virt_addr || + !transc->ip_buf_entry->in_use) { VCD_MSG_ERROR("\n Bad frm transaction state"); return VCD_ERR_BAD_STATE; } rc = vcd_map_sched_status(sched_re_queue_frame( - p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, - (void *) p_transc->p_ip_buf_entry)); + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, + (void *) transc->ip_buf_entry)); VCD_FAILED_RETURN(rc, "Failed: sched_queue_frame"); - if (p_transc->p_ip_buf_entry->frame.n_flags & - VCD_FRAME_FLAG_EOS) { + if (transc->ip_buf_entry->frame.flags & VCD_FRAME_FLAG_EOS) { rc = vcd_map_sched_status(sched_mark_client_eof( - p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl)); + cctxt->dev_ctxt->sched_hdl, + cctxt->sched_clnt_hdl)); } VCD_FAILED_RETURN(rc, "Failed: sched_mark_client_eof"); - p_transc->p_ip_buf_entry = NULL; - p_transc->b_in_use = FALSE; - p_frame->b_frm_trans_end = TRUE; + transc->ip_buf_entry = NULL; + transc->in_use = false; + frame->frm_trans_end = true; if (VCD_FAILED(status)) - VCD_MSG_ERROR("\n OUTPUT_REQ returned err = 0x%x", - status); + VCD_MSG_ERROR("\n OUTPUT_REQ returned err = 0x%x", status); - if (p_cctxt->status.n_frame_submitted > 0) - p_cctxt->status.n_frame_submitted--; + if (cctxt->status.frame_submitted > 0) + cctxt->status.frame_submitted--; else - p_cctxt->status.n_frame_delayed--; + cctxt->status.frame_delayed--; - if (!VCD_FAILED(status) && - p_cctxt->b_decoding && - p_frame->vcd_frm.b_interlaced) { - if (p_cctxt->status.n_int_field_cnt > 0) + if (!VCD_FAILED(status) && cctxt->decoding && + frame->vcd_frm.interlaced) { + if (cctxt->status.int_field_cnt > 0) VCD_MSG_ERROR("\n Not expected: OUTPUT_REQ" "for 2nd interlace field"); } @@ -2190,789 +1964,693 @@ u32 vcd_handle_output_required(struct vcd_clnt_ctxt_type_t return VCD_S_SUCCESS; } - -u32 vcd_handle_output_required_in_flushing( -struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload) +u32 vcd_handle_output_required_in_flushing(struct vcd_clnt_ctxt *cctxt, + void *payload) { u32 rc; - struct vcd_transc_type *p_transc; + struct vcd_transc *transc; - rc = vcd_validate_io_done_pyld(p_payload, VCD_S_SUCCESS); + rc = vcd_validate_io_done_pyld(payload, VCD_S_SUCCESS); VCD_FAILED_RETURN(rc, "Bad input done payload"); - p_transc = (struct vcd_transc_type *) - (((struct ddl_frame_data_type_tag *)p_payload)-> - vcd_frm.n_ip_frm_tag); + transc = (struct vcd_transc *) (((struct ddl_frame_data_tag *)payload)-> + vcd_frm.ip_frm_tag); - ((struct ddl_frame_data_type_tag *)p_payload)-> - vcd_frm.b_interlaced = FALSE; + ((struct ddl_frame_data_tag *)payload)->vcd_frm.interlaced = false; - rc = vcd_handle_input_done(p_cctxt, p_payload, - VCD_EVT_RESP_INPUT_FLUSHED, VCD_S_SUCCESS); + rc = vcd_handle_input_done(cctxt, payload, VCD_EVT_RESP_INPUT_FLUSHED, + VCD_S_SUCCESS); - p_transc->b_in_use = FALSE; - ((struct ddl_frame_data_type_tag *)p_payload)->b_frm_trans_end = TRUE; + transc->in_use = false; + ((struct ddl_frame_data_tag *)payload)->frm_trans_end = true; return rc; } -u32 vcd_handle_frame_done( - struct vcd_clnt_ctxt_type_t *p_cctxt, - void *p_payload, u32 event, u32 status) +u32 vcd_handle_frame_done(struct vcd_clnt_ctxt *cctxt, void *payload, u32 event, + u32 status) { - struct vcd_buffer_entry_type *p_op_buf_entry; - struct ddl_frame_data_type_tag *p_op_frm = - (struct ddl_frame_data_type_tag *) p_payload; - struct vcd_transc_type *p_transc; + struct vcd_buffer_entry *op_buf_entry; + struct ddl_frame_data_tag *op_frm = (struct ddl_frame_data_tag *) + payload; + struct vcd_transc *transc; u32 rc; - rc = vcd_validate_io_done_pyld(p_payload, status); + rc = vcd_validate_io_done_pyld(payload, status); VCD_FAILED_RETURN(rc, "Bad payload recvd"); - p_transc = (struct vcd_transc_type *)p_op_frm->vcd_frm.n_ip_frm_tag; + transc = (struct vcd_transc *)op_frm->vcd_frm.ip_frm_tag; - if (p_op_frm->vcd_frm.p_virtual) { + if (op_frm->vcd_frm.virt_addr) { - if (!p_transc->p_op_buf_entry) { - p_op_buf_entry = - vcd_find_buffer_pool_entry( - &p_cctxt->out_buf_pool, - p_op_frm->vcd_frm. - p_virtual); + if (!transc->op_buf_entry) { + op_buf_entry = vcd_find_buffer_pool_entry( + &cctxt->out_buf_pool, op_frm->vcd_frm.virt_addr); } else { - p_op_buf_entry = p_transc->p_op_buf_entry; + op_buf_entry = transc->op_buf_entry; } - if (!p_op_buf_entry) { + if (!op_buf_entry) { VCD_MSG_ERROR("Invalid output buffer returned" "from DDL"); vcd_assert(); rc = VCD_ERR_BAD_POINTER; - } else if (!p_op_buf_entry->b_in_use) { - VCD_MSG_ERROR("Bad output buffer 0x%p recvd from DDL", - p_op_buf_entry->frame.p_virtual); + } else if (!op_buf_entry->in_use) { + VCD_MSG_ERROR("Bad output buffer %p recv from DDL", + op_buf_entry->frame.virt_addr); vcd_assert(); rc = VCD_ERR_BAD_POINTER; } else { - p_op_buf_entry->b_in_use = FALSE; + op_buf_entry->in_use = false; VCD_BUFFERPOOL_INUSE_DECREMENT( - p_cctxt->out_buf_pool.n_in_use); + cctxt->out_buf_pool.in_use); VCD_MSG_LOW("outBufPool.InUse = %d", - p_cctxt->out_buf_pool.n_in_use); + cctxt->out_buf_pool.in_use); } } VCD_FAILED_RETURN(rc, "Bad output buffer pointer"); - p_op_frm->vcd_frm.time_stamp = p_transc->time_stamp; - p_op_frm->vcd_frm.n_ip_frm_tag = p_transc->n_ip_frm_tag; - p_op_frm->vcd_frm.e_frame_type = p_transc->e_frame_type; + op_frm->vcd_frm.time_stamp = transc->time_stamp; + op_frm->vcd_frm.ip_frm_tag = transc->ip_frm_tag; + op_frm->vcd_frm.frame_type = transc->frame_type; - p_transc->b_frame_done = TRUE; + transc->frame_done = true; - if (p_transc->b_input_done && p_transc->b_frame_done) - p_transc->b_in_use = FALSE; + if (transc->input_done && transc->frame_done) + transc->in_use = false; if (status == VCD_ERR_INTRLCD_FIELD_DROP || - (p_op_frm->n_intrlcd_ip_frm_tag != VCD_FRAMETAG_INVALID && - p_op_frm->n_intrlcd_ip_frm_tag)) { - vcd_handle_frame_done_for_interlacing(p_cctxt, p_transc, - p_op_frm, status); + (op_frm->intrlcd_ip_frm_tag != VCD_FRAMETAG_INVALID && + op_frm->intrlcd_ip_frm_tag)) { + vcd_handle_frame_done_for_interlacing(cctxt, transc, op_frm, + status); } if (status != VCD_ERR_INTRLCD_FIELD_DROP) { - p_cctxt->callback(event, - status, - &p_op_frm->vcd_frm, - sizeof(struct vcd_frame_data_type), - p_cctxt, p_cctxt->p_client_data); + cctxt->callback(event, status, &op_frm->vcd_frm, + sizeof(struct vcd_frame_data), cctxt, + cctxt->client_data); } return VCD_S_SUCCESS; } -void vcd_handle_frame_done_in_eos( - struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload, u32 status) +void vcd_handle_frame_done_in_eos(struct vcd_clnt_ctxt *cctxt, void *payload, + u32 status) { - struct ddl_frame_data_type_tag *p_frame = - (struct ddl_frame_data_type_tag *) p_payload; + struct ddl_frame_data_tag *frame = (struct ddl_frame_data_tag *)payload; VCD_MSG_LOW("vcd_handle_frame_done_in_eos:"); - if (VCD_FAILED(vcd_validate_io_done_pyld(p_payload, status))) + if (VCD_FAILED(vcd_validate_io_done_pyld(payload, status))) return; - if (p_cctxt->status.b_eos_prev_valid) { - (void)vcd_handle_frame_done(p_cctxt, - (void *)&p_cctxt->status. - eos_prev_op_frm, - VCD_EVT_RESP_OUTPUT_DONE, - status); + if (cctxt->status.eos_prev_valid) { + vcd_handle_frame_done(cctxt, + (void *)&cctxt->status.eos_prev_op_frm, + VCD_EVT_RESP_OUTPUT_DONE, status); } - p_cctxt->status.eos_prev_op_frm = *p_frame; - p_cctxt->status.b_eos_prev_valid = TRUE; + cctxt->status.eos_prev_op_frm = *frame; + cctxt->status.eos_prev_valid = true; } -void vcd_handle_frame_done_for_interlacing( - struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_transc_type *p_transc_ip1, - struct ddl_frame_data_type_tag *p_op_frm, u32 status) +void vcd_handle_frame_done_for_interlacing(struct vcd_clnt_ctxt *cctxt, + struct vcd_transc *transc_ip1, struct ddl_frame_data_tag *op_frm, + u32 status) { - struct vcd_transc_type *p_transc_ip2 = - (struct vcd_transc_type *)p_op_frm->n_intrlcd_ip_frm_tag; + struct vcd_transc *transc_ip2 = (struct vcd_transc *) + op_frm->intrlcd_ip_frm_tag; if (status == VCD_ERR_INTRLCD_FIELD_DROP) { - p_cctxt->status.n_int_field_cnt = 0; + cctxt->status.int_field_cnt = 0; return; } - p_op_frm->n_intrlcd_ip_frm_tag = p_transc_ip2->n_ip_frm_tag; + op_frm->intrlcd_ip_frm_tag = transc_ip2->ip_frm_tag; - p_transc_ip2->b_frame_done = TRUE; + transc_ip2->frame_done = true; - if (p_transc_ip2->b_input_done && p_transc_ip2->b_frame_done) - p_transc_ip2->b_in_use = FALSE; + if (transc_ip2->input_done && transc_ip2->frame_done) + transc_ip2->in_use = false; - if (!p_transc_ip1->e_frame_type || - !p_transc_ip2->e_frame_type) { + if (!transc_ip1->frame_type || !transc_ip2->frame_type) { VCD_MSG_ERROR("DDL didn't provided frame type"); - return; } } -u32 vcd_handle_first_frame_done( - struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload) +u32 vcd_handle_first_frame_done(struct vcd_clnt_ctxt *cctxt, void *payload) { - if (!p_cctxt->b_decoding) - return vcd_handle_first_encode_frame_done(p_cctxt, p_payload); + if (!cctxt->decoding) + return vcd_handle_first_encode_frame_done(cctxt, payload); return VCD_S_SUCCESS; } -u32 vcd_handle_first_encode_frame_done( - struct vcd_clnt_ctxt_type_t *p_cctxt, void *p_payload) +u32 vcd_handle_first_encode_frame_done(struct vcd_clnt_ctxt *cctxt, + void *payload) { - struct vcd_buffer_entry_type *p_buf_entry; - struct vcd_frame_data_type *p_frm_entry; + struct vcd_buffer_entry *buf_entry; + struct vcd_frame_data *frm_entry; u32 rc, seqhdr_present; - struct vcd_property_hdr_type prop_hdr; - struct vcd_sequence_hdr_type seq_hdr; - struct vcd_property_codec_type codec; + struct vcd_property_hdr prop_hdr; + struct vcd_sequence_hdr seq_hdr; + struct vcd_property_codec codec; union sched_value_type sched_val; - struct vcd_transc_type *p_transc; - struct ddl_frame_data_type_tag *p_payload_frm = - (struct ddl_frame_data_type_tag *) p_payload; + struct vcd_transc *transc; + struct ddl_frame_data_tag *payload_frm = (struct ddl_frame_data_tag *) + payload; VCD_MSG_LOW("vcd_handle_first_encode_frame_done:"); - rc = vcd_validate_io_done_pyld(p_payload, VCD_S_SUCCESS); + rc = vcd_validate_io_done_pyld(payload, VCD_S_SUCCESS); VCD_FAILED_RETURN(rc, "Validate frame done payload failed"); - p_transc = (struct vcd_transc_type *)p_payload_frm-> - vcd_frm.n_ip_frm_tag; + transc = (struct vcd_transc *)payload_frm->vcd_frm.ip_frm_tag; - prop_hdr.prop_id = DDL_I_SEQHDR_PRESENT; - prop_hdr.n_size = sizeof(seqhdr_present); - rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, - &seqhdr_present); + prop_hdr.id = DDL_I_SEQHDR_PRESENT; + prop_hdr.sz = sizeof(seqhdr_present); + rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &seqhdr_present); VCD_FAILED_RETURN(rc, "Failed: DDL_I_SEQHDR_PRESENT"); if (!seqhdr_present) return VCD_S_SUCCESS; - p_buf_entry = vcd_buffer_pool_entry_de_q(&p_cctxt->out_buf_pool); + buf_entry = vcd_buffer_pool_entry_de_q(&cctxt->out_buf_pool); - if (!p_buf_entry) { + if (!buf_entry) { VCD_MSG_ERROR("Sched provided frame when 2nd op buffer " "was unavailable"); rc = VCD_ERR_FAIL; vcd_assert(); - } else { - p_frm_entry = &p_buf_entry->frame; - prop_hdr.prop_id = VCD_I_CODEC; - prop_hdr.n_size = sizeof(struct vcd_property_codec_type); + return rc; + } - rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, &codec); + frm_entry = &buf_entry->frame; + prop_hdr.id = VCD_I_CODEC; + prop_hdr.sz = sizeof(struct vcd_property_codec); - if (!VCD_FAILED(rc)) { - if (codec.e_codec != VCD_CODEC_H263) { - prop_hdr.prop_id = VCD_I_SEQ_HEADER; - prop_hdr.n_size = sizeof(struct - vcd_sequence_hdr_type); - - seq_hdr.p_sequence_header = - p_frm_entry->p_virtual; - seq_hdr.n_sequence_header_len = - p_buf_entry->n_size; - - rc = ddl_get_property(p_cctxt->ddl_handle, - &prop_hdr, &seq_hdr); - } else { - VCD_MSG_LOW("Codec Type is H.263\n"); - } + rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &codec); + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("rc = 0x%x. Failed: ddl_get_property:VCD_I_CODEC", + rc); + goto out; + } - if (!VCD_FAILED(rc)) { - sched_val.un_value = - VCD_SCHEDULER_ENC_DFLT_OTKN_PERFRM; - - rc = vcd_map_sched_status(sched_set_client_param - (p_cctxt->p_dev_ctxt-> - sched_hdl, - p_cctxt->sched_clnt_hdl, - SCHED_I_CLNT_OTKNPERIPFRM, - &sched_val)); - - if (!VCD_FAILED(rc)) { - p_frm_entry->n_data_len = - seq_hdr.n_sequence_header_len; - p_frm_entry->time_stamp = - p_transc->time_stamp; - p_frm_entry->n_ip_frm_tag = - p_transc->n_ip_frm_tag; - p_frm_entry->n_flags |= - VCD_FRAME_FLAG_CODECCONFIG; - - p_cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, - VCD_S_SUCCESS, p_frm_entry, - sizeof(struct vcd_frame_data_type), - p_cctxt, - p_cctxt->p_client_data); - - } else { - VCD_MSG_ERROR("rc = 0x%x.Failed: " - "sched_set_client_param", rc); - } + if (codec.codec != VCD_CODEC_H263) { + prop_hdr.id = VCD_I_SEQ_HEADER; + prop_hdr.sz = sizeof(struct vcd_sequence_hdr); - } else { - VCD_MSG_ERROR - ("rc = 0x%x. Failed: " - "ddl_get_property:VCD_I_SEQ_HEADER", - rc); - } - } else { - VCD_MSG_ERROR - ("rc = 0x%x. Failed: " - "ddl_get_property:VCD_I_CODEC", - rc); - } + seq_hdr.addr = frm_entry->virt_addr; + seq_hdr.sz = buf_entry->size; + rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &seq_hdr); if (VCD_FAILED(rc)) { - (void)vcd_buffer_pool_entry_en_q(&p_cctxt->out_buf_pool, - p_buf_entry); + VCD_MSG_ERROR("rc = 0x%x. Failed: " + "ddl_get_property:VCD_I_SEQ_HEADER", rc); + goto out; } + } else { + VCD_MSG_LOW("Codec Type is H.263\n"); } + sched_val.un_value = VCD_SCHEDULER_ENC_DFLT_OTKN_PERFRM; + + rc = vcd_map_sched_status(sched_set_client_param( + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, + SCHED_I_CLNT_OTKNPERIPFRM, &sched_val)); + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("rc = 0x%x.Failed: sched_set_client_param", rc); + goto out; + } + + frm_entry->data_len = seq_hdr.sz; + frm_entry->time_stamp = transc->time_stamp; + frm_entry->ip_frm_tag = transc->ip_frm_tag; + frm_entry->flags |= VCD_FRAME_FLAG_CODECCONFIG; + + cctxt->callback(VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS, frm_entry, + sizeof(struct vcd_frame_data), cctxt, cctxt->client_data); + +out: + if (VCD_FAILED(rc)) + vcd_buffer_pool_entry_en_q(&cctxt->out_buf_pool, buf_entry); + return rc; } -void vcd_handle_eos_trans_end(struct vcd_clnt_ctxt_type_t *p_cctxt) +void vcd_handle_eos_trans_end(struct vcd_clnt_ctxt *cctxt) { - if (p_cctxt->status.b_eos_prev_valid) { - (void) vcd_handle_frame_done(p_cctxt, - (void *)&p_cctxt->status.eos_prev_op_frm, - VCD_EVT_RESP_OUTPUT_DONE, - VCD_S_SUCCESS); + if (cctxt->status.eos_prev_valid) { + vcd_handle_frame_done(cctxt, + (void *)&cctxt->status.eos_prev_op_frm, + VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS); - p_cctxt->status.b_eos_prev_valid = FALSE; + cctxt->status.eos_prev_valid = false; } - if (p_cctxt->status.n_flush_mode) - vcd_process_pending_flush_in_eos(p_cctxt); + if (cctxt->status.flush_mode) + vcd_process_pending_flush_in_eos(cctxt); - if (p_cctxt->status.b_stop_pending) - vcd_process_pending_stop_in_eos(p_cctxt); + if (cctxt->status.stop_pending) + vcd_process_pending_stop_in_eos(cctxt); else { - vcd_do_client_state_transition(p_cctxt, - VCD_CLIENT_STATE_RUN, + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_RUN, CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); } } -void vcd_handle_eos_done(struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_transc_type *p_transc, u32 status) +void vcd_handle_eos_done(struct vcd_clnt_ctxt *cctxt, struct vcd_transc *transc, + u32 status) { - struct vcd_frame_data_type vcd_frm; + struct vcd_frame_data vcd_frm; VCD_MSG_LOW("vcd_handle_eos_done:"); if (VCD_FAILED(status)) VCD_MSG_ERROR("EOS DONE returned error = 0x%x", status); - if (p_cctxt->status.b_eos_prev_valid) { - p_cctxt->status.eos_prev_op_frm.vcd_frm.n_flags |= + if (cctxt->status.eos_prev_valid) { + cctxt->status.eos_prev_op_frm.vcd_frm.flags |= VCD_FRAME_FLAG_EOS; - (void)vcd_handle_frame_done(p_cctxt, - (void *)&p_cctxt->status. - eos_prev_op_frm, - VCD_EVT_RESP_OUTPUT_DONE, - VCD_S_SUCCESS); + vcd_handle_frame_done(cctxt, + (void *)&cctxt->status.eos_prev_op_frm, + VCD_EVT_RESP_OUTPUT_DONE, VCD_S_SUCCESS); - p_cctxt->status.b_eos_prev_valid = FALSE; + cctxt->status.eos_prev_valid = false; } else { - if (p_transc->p_ip_buf_entry) { - p_transc->p_ip_buf_entry->frame.n_ip_frm_tag = - p_transc->n_ip_frm_tag; + if (transc->ip_buf_entry) { + transc->ip_buf_entry->frame.ip_frm_tag = + transc->ip_frm_tag; - vcd_send_frame_done_in_eos(p_cctxt, - &p_transc->p_ip_buf_entry->frame, FALSE); + vcd_send_frame_done_in_eos(cctxt, + &transc->ip_buf_entry->frame, false); } else { - memset(&vcd_frm, 0, sizeof(struct vcd_frame_data_type)); - vcd_frm.n_ip_frm_tag = p_transc->n_ip_frm_tag; - vcd_frm.time_stamp = p_transc->time_stamp; - vcd_frm.n_flags = VCD_FRAME_FLAG_EOS; - vcd_send_frame_done_in_eos(p_cctxt, &vcd_frm, TRUE); + memset(&vcd_frm, 0, sizeof(struct vcd_frame_data)); + vcd_frm.ip_frm_tag = transc->ip_frm_tag; + vcd_frm.time_stamp = transc->time_stamp; + vcd_frm.flags = VCD_FRAME_FLAG_EOS; + vcd_send_frame_done_in_eos(cctxt, &vcd_frm, true); } } - if (p_transc->p_ip_buf_entry) { - if (p_transc->p_ip_buf_entry->frame.p_virtual) { - p_transc->p_ip_buf_entry->frame.n_ip_frm_tag = - p_transc->n_ip_frm_tag; + if (transc->ip_buf_entry) { + if (transc->ip_buf_entry->frame.virt_addr) { + transc->ip_buf_entry->frame.ip_frm_tag = + transc->ip_frm_tag; - p_cctxt->callback(VCD_EVT_RESP_INPUT_DONE, - VCD_S_SUCCESS, - &p_transc->p_ip_buf_entry->frame, - sizeof(struct vcd_frame_data_type), - p_cctxt, p_cctxt->p_client_data); + cctxt->callback(VCD_EVT_RESP_INPUT_DONE, + VCD_S_SUCCESS, &transc->ip_buf_entry->frame, + sizeof(struct vcd_frame_data), cctxt, + cctxt->client_data); } - p_transc->p_ip_buf_entry->b_in_use = FALSE; - VCD_BUFFERPOOL_INUSE_DECREMENT(p_cctxt->in_buf_pool.n_in_use); - p_transc->p_ip_buf_entry = NULL; - p_cctxt->status.n_frame_submitted--; + transc->ip_buf_entry->in_use = false; + VCD_BUFFERPOOL_INUSE_DECREMENT(cctxt->in_buf_pool.in_use); + transc->ip_buf_entry = NULL; + cctxt->status.frame_submitted--; } - p_transc->b_in_use = FALSE; - vcd_mark_frame_channel(p_cctxt->p_dev_ctxt); - if (p_cctxt->status.n_flush_mode) - vcd_process_pending_flush_in_eos(p_cctxt); + transc->in_use = false; + vcd_mark_frame_channel(cctxt->dev_ctxt); + if (cctxt->status.flush_mode) + vcd_process_pending_flush_in_eos(cctxt); - if (p_cctxt->status.b_stop_pending) { - vcd_process_pending_stop_in_eos(p_cctxt); - } else if (!p_cctxt->status.b_eos_wait_for_op_buf) { - vcd_do_client_state_transition(p_cctxt, - VCD_CLIENT_STATE_RUN, - CLIENT_STATE_EVENT_NUMBER - (pf_clnt_cb)); + if (cctxt->status.stop_pending) { + vcd_process_pending_stop_in_eos(cctxt); + } else if (!cctxt->status.eos_wait_for_op_buf) { + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_RUN, + CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); } } -void vcd_handle_start_done(struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_transc_type *p_transc, u32 status) +void vcd_handle_start_done(struct vcd_clnt_ctxt *cctxt, + struct vcd_transc *transc, u32 status) { - p_cctxt->status.n_cmd_submitted--; - vcd_mark_command_channel(p_cctxt->p_dev_ctxt, p_transc); + cctxt->status.cmd_submitted--; + vcd_mark_command_channel(cctxt->dev_ctxt, transc); if (!VCD_FAILED(status)) { - p_cctxt->callback(VCD_EVT_RESP_START, status, NULL, - 0, p_cctxt, p_cctxt->p_client_data); + cctxt->callback(VCD_EVT_RESP_START, status, NULL, 0, cctxt, + cctxt->client_data); - vcd_do_client_state_transition(p_cctxt, - VCD_CLIENT_STATE_RUN, + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_RUN, CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); } else { - VCD_MSG_ERROR("ddl callback returned failure." - "status = 0x%x", status); - vcd_handle_err_in_starting(p_cctxt, status); + VCD_MSG_ERROR("ddl callback returned failure.status = 0x%x", + status); + vcd_handle_err_in_starting(cctxt, status); } } -void vcd_handle_stop_done(struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_transc_type *p_transc, u32 status) +void vcd_handle_stop_done(struct vcd_clnt_ctxt *cctxt, + struct vcd_transc *transc, u32 status) { - u32 rc = VCD_S_SUCCESS, seq_hdrpresent = 0; + u32 rc = VCD_S_SUCCESS; + u32 seq_hdrpresent = 0; union sched_value_type sched_val; - struct vcd_property_hdr_type prop_hdr; + struct vcd_property_hdr prop_hdr; VCD_MSG_LOW("vcd_handle_stop_done:"); - p_cctxt->status.n_cmd_submitted--; - vcd_mark_command_channel(p_cctxt->p_dev_ctxt, p_transc); + cctxt->status.cmd_submitted--; + vcd_mark_command_channel(cctxt->dev_ctxt, transc); - if (!VCD_FAILED(status)) { - if (!p_cctxt->b_decoding) { - prop_hdr.prop_id = DDL_I_SEQHDR_PRESENT; - prop_hdr.n_size = sizeof(seq_hdrpresent); - rc = ddl_get_property(p_cctxt->ddl_handle, - &prop_hdr, &seq_hdrpresent); - - if (!VCD_FAILED(rc)) { - if (seq_hdrpresent == 0x1) { - sched_val.un_value = - VCD_SCHEDULER_ENC_DFLT_OTKN_PERFRM - + 1; - - rc = vcd_map_sched_status( - sched_set_client_param( - p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, - SCHED_I_CLNT_OTKNPERIPFRM, - &sched_val) - ); - - if (VCD_FAILED(rc)) - VCD_MSG_ERROR("Failed: " - "sched_set_client_param" - " %d", rc); - } - - } else { - VCD_MSG_ERROR("Failed: DDL Get DDL_I_SEQHDR_" - "PRESENT %d", rc); - } - - } - vcd_do_client_state_transition(p_cctxt, - VCD_CLIENT_STATE_OPEN, - CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); - } else { + if (VCD_FAILED(status)) { VCD_MSG_FATAL("STOP_DONE returned error = 0x%x", status); status = VCD_ERR_HW_FATAL; - vcd_handle_device_err_fatal(p_cctxt->p_dev_ctxt, p_cctxt); - vcd_do_client_state_transition(p_cctxt, - VCD_CLIENT_STATE_INVALID, + vcd_handle_device_err_fatal(cctxt->dev_ctxt, cctxt); + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_INVALID, CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); + goto out; } - p_cctxt->callback(VCD_EVT_RESP_STOP, status, NULL, 0, p_cctxt, - p_cctxt->p_client_data); + if (!cctxt->decoding) { + prop_hdr.id = DDL_I_SEQHDR_PRESENT; + prop_hdr.sz = sizeof(seq_hdrpresent); + rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, + &seq_hdrpresent); + if (VCD_FAILED(rc)) { + VCD_MSG_ERROR("Failed: DDL Get DDL_I_SEQHDR_PRESENT %d", + rc); + goto open_out; + } + if (seq_hdrpresent == 0x1) { + sched_val.un_value = VCD_SCHEDULER_ENC_DFLT_OTKN_PERFRM + + 1; + + rc = vcd_map_sched_status(sched_set_client_param( + cctxt->dev_ctxt->sched_hdl, + cctxt->sched_clnt_hdl, + SCHED_I_CLNT_OTKNPERIPFRM, &sched_val)); + if (VCD_FAILED(rc)) + VCD_MSG_ERROR("Failed: sched_set_client_param " + "%d", rc); + } + } +open_out: + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_OPEN, + CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); - memset(&p_cctxt->status, 0, sizeof(struct vcd_clnt_status_type)); +out: + cctxt->callback(VCD_EVT_RESP_STOP, status, NULL, 0, cctxt, + cctxt->client_data); + + memset(&cctxt->status, 0, sizeof(struct vcd_clnt_status)); } -void vcd_handle_stop_done_in_starting(struct vcd_clnt_ctxt_type_t - *p_cctxt, struct vcd_transc_type *p_transc, u32 status) +void vcd_handle_stop_done_in_starting(struct vcd_clnt_ctxt *cctxt, + struct vcd_transc *transc, u32 status) { VCD_MSG_LOW("vcd_handle_stop_done_in_starting:"); - p_cctxt->status.n_cmd_submitted--; - vcd_mark_command_channel(p_cctxt->p_dev_ctxt, p_transc); + cctxt->status.cmd_submitted--; + vcd_mark_command_channel(cctxt->dev_ctxt, transc); if (!VCD_FAILED(status)) { - p_cctxt->callback(VCD_EVT_RESP_START, p_cctxt->status. - e_last_err, NULL, 0, p_cctxt, p_cctxt->p_client_data); - vcd_do_client_state_transition(p_cctxt, VCD_CLIENT_STATE_OPEN, + cctxt->callback(VCD_EVT_RESP_START, cctxt->status.last_err, + NULL, 0, cctxt, cctxt->client_data); + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_OPEN, CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); } else { VCD_MSG_FATAL("VCD Cleanup: STOP_DONE returned error " "= 0x%x", status); - vcd_handle_err_fatal(p_cctxt, VCD_EVT_RESP_START, + vcd_handle_err_fatal(cctxt, VCD_EVT_RESP_START, VCD_ERR_HW_FATAL); } } -void vcd_handle_stop_done_in_invalid(struct vcd_clnt_ctxt_type_t - *p_cctxt, u32 status) +void vcd_handle_stop_done_in_invalid(struct vcd_clnt_ctxt *cctxt, u32 status) { u32 rc; VCD_MSG_LOW("vcd_handle_stop_done_in_invalid:"); if (!VCD_FAILED(status)) { - vcd_client_cmd_flush_and_en_q(p_cctxt, VCD_CMD_CLIENT_CLOSE); - if (p_cctxt->status.n_frame_submitted) { - vcd_release_multiple_frame_channels(p_cctxt->p_dev_ctxt, - p_cctxt->status.n_frame_submitted); + vcd_client_cmd_flush_and_en_q(cctxt, VCD_CMD_CLIENT_CLOSE); + if (cctxt->status.frame_submitted) { + vcd_release_multiple_frame_channels(cctxt->dev_ctxt, + cctxt->status.frame_submitted); - p_cctxt->status.n_frame_submitted = 0; - p_cctxt->status.n_frame_delayed = 0; + cctxt->status.frame_submitted = 0; + cctxt->status.frame_delayed = 0; } - if (p_cctxt->status.n_cmd_submitted) { - vcd_release_multiple_command_channels( - p_cctxt->p_dev_ctxt, - p_cctxt->status.n_cmd_submitted); - p_cctxt->status.n_cmd_submitted = 0; + if (cctxt->status.cmd_submitted) { + vcd_release_multiple_command_channels(cctxt->dev_ctxt, + cctxt->status.cmd_submitted); + cctxt->status.cmd_submitted = 0; } } else { VCD_MSG_FATAL("VCD Cleanup: STOP_DONE returned error " "= 0x%x", status); - vcd_handle_device_err_fatal(p_cctxt->p_dev_ctxt, p_cctxt); - p_cctxt->status.b_cleaning_up = FALSE; + vcd_handle_device_err_fatal(cctxt->dev_ctxt, cctxt); + cctxt->status.cleaning_up = false; } - vcd_flush_buffers_in_err_fatal(p_cctxt); + vcd_flush_buffers_in_err_fatal(cctxt); VCD_MSG_HIGH("VCD cleanup: All buffers are returned"); - if (p_cctxt->status.b_stop_pending) { - p_cctxt->callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, NULL, 0, - p_cctxt, p_cctxt->p_client_data); - p_cctxt->status.b_stop_pending = FALSE; + if (cctxt->status.stop_pending) { + cctxt->callback(VCD_EVT_RESP_STOP, VCD_S_SUCCESS, NULL, 0, + cctxt, cctxt->client_data); + cctxt->status.stop_pending = false; } - rc = vcd_power_event(p_cctxt->p_dev_ctxt, p_cctxt, - VCD_EVT_PWR_CLNT_ERRFATAL); + rc = vcd_power_event(cctxt->dev_ctxt, cctxt, VCD_EVT_PWR_CLNT_ERRFATAL); if (VCD_FAILED(rc)) VCD_MSG_ERROR("VCD_EVT_PWR_CLNT_ERRFATAL failed"); - if (!p_cctxt->status.b_cleaning_up && - p_cctxt->status.b_close_pending) { - vcd_destroy_client_context(p_cctxt); - vcd_handle_for_last_clnt_close(p_cctxt->p_dev_ctxt, FALSE); + if (!cctxt->status.cleaning_up && + cctxt->status.close_pending) { + vcd_destroy_client_context(cctxt); + vcd_handle_for_last_clnt_close(cctxt->dev_ctxt, false); } } -u32 vcd_handle_input_frame( - struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_input_frame) +u32 vcd_handle_input_frame(struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *input_frame) { - struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; - struct vcd_buffer_entry_type *p_buf_entry; - struct vcd_frame_data_type *p_frm_entry; + struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt; + struct vcd_buffer_entry *buf_entry; + struct vcd_frame_data *frm_entry; u32 rc = VCD_S_SUCCESS; - u32 b_eos_handled = FALSE; + u32 eos_handled = false; VCD_MSG_LOW("vcd_handle_input_frame:"); VCD_MSG_LOW("input buffer: addr=(0x%p), size=(%d), len=(%d)", - p_input_frame->p_virtual, p_input_frame->n_alloc_len, - p_input_frame->n_data_len); + input_frame->virt_addr, input_frame->alloc_len, + input_frame->data_len); - if ((!p_input_frame->p_virtual || !p_input_frame->n_data_len) - && !(p_input_frame->n_flags & VCD_FRAME_FLAG_EOS)) { + if ((!input_frame->virt_addr || !input_frame->data_len) && + !(input_frame->flags & VCD_FRAME_FLAG_EOS)) { VCD_MSG_ERROR("Bad frame ptr/len/EOS combination"); return VCD_ERR_ILLEGAL_PARM; } - if (!p_cctxt->status.b1st_frame_recvd) { - if (p_cctxt->b_decoding) - rc = vcd_handle_first_decode_frame(p_cctxt); + if (!cctxt->status.b1st_frame_recvd) { + if (cctxt->decoding) + rc = vcd_handle_first_decode_frame(cctxt); if (!VCD_FAILED(rc)) { - p_cctxt->status.first_ts = p_input_frame->time_stamp; - p_cctxt->status.prev_ts = p_cctxt->status.first_ts; + cctxt->status.first_ts = input_frame->time_stamp; + cctxt->status.prev_ts = cctxt->status.first_ts; - p_cctxt->status.b1st_frame_recvd = TRUE; + cctxt->status.b1st_frame_recvd = true; - (void)vcd_power_event(p_cctxt->p_dev_ctxt, - p_cctxt, - VCD_EVT_PWR_CLNT_FIRST_FRAME); + vcd_power_event(cctxt->dev_ctxt, cctxt, + VCD_EVT_PWR_CLNT_FIRST_FRAME); } } VCD_FAILED_RETURN(rc, "Failed: Frist frame handling"); - p_buf_entry = vcd_find_buffer_pool_entry(&p_cctxt->in_buf_pool, - p_input_frame->p_virtual); - if (!p_buf_entry) { - VCD_MSG_ERROR("Bad buffer addr: %p", p_input_frame->p_virtual); + buf_entry = vcd_find_buffer_pool_entry(&cctxt->in_buf_pool, + input_frame->virt_addr); + if (!buf_entry) { + VCD_MSG_ERROR("Bad buffer addr: %p", input_frame->virt_addr); return VCD_ERR_FAIL; } - if (p_buf_entry->b_in_use) { - VCD_MSG_ERROR("An inuse input frame is being" - "re-queued to scheduler"); + if (buf_entry->in_use) { + VCD_MSG_ERROR("An inuse input frame is being re-queued to " + "scheduler"); return VCD_ERR_FAIL; } - if (p_input_frame->n_alloc_len > p_buf_entry->n_size) { + if (input_frame->alloc_len > buf_entry->size) { VCD_MSG_ERROR("Bad buffer Alloc_len %d, Actual size=%d", - p_input_frame->n_alloc_len, p_buf_entry->n_size); + input_frame->alloc_len, buf_entry->size); return VCD_ERR_ILLEGAL_PARM; } - p_frm_entry = &p_buf_entry->frame; + frm_entry = &buf_entry->frame; - *p_frm_entry = *p_input_frame; - p_frm_entry->p_physical = p_buf_entry->p_physical; + *frm_entry = *input_frame; + frm_entry->phys_addr = buf_entry->phys_addr; - if (p_input_frame->n_flags & VCD_FRAME_FLAG_EOS) { - rc = vcd_handle_recvd_eos(p_cctxt, p_input_frame, - &b_eos_handled); - } + if (input_frame->flags & VCD_FRAME_FLAG_EOS) + rc = vcd_handle_recvd_eos(cctxt, input_frame, &eos_handled); - if (VCD_FAILED(rc) || b_eos_handled) { - VCD_MSG_HIGH("rc = 0x%x, b_eos_handled = %d", rc, - b_eos_handled); + if (VCD_FAILED(rc) || eos_handled) { + VCD_MSG_HIGH("rc = 0x%x, eos_handled = %d", rc, eos_handled); return rc; } - rc = vcd_map_sched_status(sched_queue_frame(p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, - (void *)p_buf_entry)); + rc = vcd_map_sched_status(sched_queue_frame(dev_ctxt->sched_hdl, + cctxt->sched_clnt_hdl, (void *)buf_entry)); VCD_FAILED_RETURN(rc, "Failed: sched_queue_frame"); - p_buf_entry->b_in_use = TRUE; - p_cctxt->in_buf_pool.n_in_use++; - if (p_input_frame->n_flags & VCD_FRAME_FLAG_EOS) { - rc = vcd_map_sched_status(sched_mark_client_eof - (p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl)); + buf_entry->in_use = true; + cctxt->in_buf_pool.in_use++; + if (input_frame->flags & VCD_FRAME_FLAG_EOS) { + rc = vcd_map_sched_status(sched_mark_client_eof( + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl)); } VCD_FAILED_RETURN(rc, "Failed: sched_mark_client_eof"); - vcd_try_submit_frame(p_dev_ctxt); + vcd_try_submit_frame(dev_ctxt); return rc; } -void vcd_release_all_clnt_frm_transc(struct vcd_clnt_ctxt_type_t *p_cctxt) +void vcd_release_all_clnt_frm_transc(struct vcd_clnt_ctxt *cctxt) { - struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; + struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt; u8 i; VCD_MSG_LOW("vcd_release_all_clnt_frm_transc:"); - for (i = 0; i < p_dev_ctxt->n_trans_tbl_size; i++) { - if (p_dev_ctxt->a_trans_tbl[i].b_in_use && - p_cctxt == p_dev_ctxt->a_trans_tbl[i].p_cctxt && - p_dev_ctxt->a_trans_tbl[i]. - e_type == VCD_CMD_CODE_FRAME) { - vcd_release_trans_tbl_entry(&p_dev_ctxt-> - a_trans_tbl[i]); + for (i = 0; i < dev_ctxt->trans_tbl_size; i++) { + if (dev_ctxt->trans_tbl[i].in_use && + cctxt == dev_ctxt->trans_tbl[i].cctxt && + dev_ctxt->trans_tbl[i].type == + VCD_CMD_CODE_FRAME) { + vcd_release_trans_tbl_entry(&dev_ctxt->trans_tbl[i]); } } } -void vcd_release_all_clnt_def_frm_transc(struct vcd_clnt_ctxt_type_t *p_cctxt) +void vcd_release_all_clnt_def_frm_transc(struct vcd_clnt_ctxt *cctxt) { - struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; - u8 i; + struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt; + u8 i; - VCD_MSG_LOW("vcd_release_all_clnt_def_frm_transc:"); + VCD_MSG_LOW("vcd_release_all_clnt_def_frm_transc:"); - for (i = 0; i < p_dev_ctxt->n_trans_tbl_size; i++) { - if (p_dev_ctxt->a_trans_tbl[i].b_in_use && - p_cctxt == p_dev_ctxt->a_trans_tbl[i].p_cctxt - && p_dev_ctxt->a_trans_tbl[i].e_type == - VCD_CMD_NONE) { - vcd_release_trans_tbl_entry( - &p_dev_ctxt->a_trans_tbl[i]); - } + for (i = 0; i < dev_ctxt->trans_tbl_size; i++) { + if (dev_ctxt->trans_tbl[i].in_use && + cctxt == dev_ctxt->trans_tbl[i].cctxt && + dev_ctxt->trans_tbl[i].type == VCD_CMD_NONE) { + vcd_release_trans_tbl_entry(&dev_ctxt->trans_tbl[i]); } + } } -void vcd_release_all_clnt_transc(struct vcd_clnt_ctxt_type_t *p_cctxt) +void vcd_release_all_clnt_transc(struct vcd_clnt_ctxt *cctxt) { - struct vcd_dev_ctxt_type *p_dev_ctxt = p_cctxt->p_dev_ctxt; - u8 i; + struct vcd_dev_ctxt *dev_ctxt = cctxt->dev_ctxt; + u8 i; - VCD_MSG_LOW("vcd_release_all_clnt_def_frm_transc:"); + VCD_MSG_LOW("vcd_release_all_clnt_def_frm_transc:"); - for (i = 0; i < p_dev_ctxt->n_trans_tbl_size; i++) { - if (p_dev_ctxt->a_trans_tbl[i].b_in_use && - p_cctxt == p_dev_ctxt->a_trans_tbl[i].p_cctxt) { - vcd_release_trans_tbl_entry( - &p_dev_ctxt->a_trans_tbl[i]); - } + for (i = 0; i < dev_ctxt->trans_tbl_size; i++) { + if (dev_ctxt->trans_tbl[i].in_use && + cctxt == dev_ctxt->trans_tbl[i].cctxt) { + vcd_release_trans_tbl_entry(&dev_ctxt->trans_tbl[i]); } + } } -void vcd_send_flush_done(struct vcd_clnt_ctxt_type_t *p_cctxt, u32 status) +void vcd_send_flush_done(struct vcd_clnt_ctxt *cctxt, u32 status) { VCD_MSG_LOW("vcd_send_flush_done:"); - if (p_cctxt->status.n_flush_mode & VCD_FLUSH_INPUT) { - p_cctxt->callback(VCD_EVT_RESP_FLUSH_INPUT_DONE, - status, NULL, 0, p_cctxt, p_cctxt->p_client_data); - p_cctxt->status.n_flush_mode &= ~VCD_FLUSH_INPUT; + if (cctxt->status.flush_mode & VCD_FLUSH_INPUT) { + cctxt->callback(VCD_EVT_RESP_FLUSH_INPUT_DONE, status, NULL, 0, + cctxt, cctxt->client_data); + cctxt->status.flush_mode &= ~VCD_FLUSH_INPUT; } - if (p_cctxt->status.n_flush_mode & VCD_FLUSH_OUTPUT) { - p_cctxt->callback(VCD_EVT_RESP_FLUSH_OUTPUT_DONE, - status, NULL, 0, p_cctxt, p_cctxt->p_client_data); - p_cctxt->status.n_flush_mode &= ~VCD_FLUSH_OUTPUT; + if (cctxt->status.flush_mode & VCD_FLUSH_OUTPUT) { + cctxt->callback(VCD_EVT_RESP_FLUSH_OUTPUT_DONE, status, NULL, 0, + cctxt, cctxt->client_data); + cctxt->status.flush_mode &= ~VCD_FLUSH_OUTPUT; } } -u32 vcd_store_seq_hdr( - struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_sequence_hdr_type *p_seq_hdr) +u32 vcd_store_seq_hdr(struct vcd_clnt_ctxt *cctxt, + struct vcd_sequence_hdr *seq_hdr) { - u32 rc; - struct vcd_property_hdr_type prop_hdr; - u32 n_align; - u8 *p_virtual_aligned; - u32 n_addr; - int ret = 0; - - if (!p_seq_hdr->n_sequence_header_len - || !p_seq_hdr->p_sequence_header) { - VCD_MSG_ERROR("Bad seq hdr"); +// u32 rc; +// struct vcd_property_hdr prop_hdr; +// u32 align; +// u32 addr; +// int ret = 0; + if (!seq_hdr->sz || !seq_hdr->addr) { + VCD_MSG_ERROR("Bad seq hdr"); return VCD_ERR_BAD_POINTER; } - if (p_cctxt->seq_hdr.p_sequence_header) { + if (cctxt->seq_hdr.addr) { VCD_MSG_HIGH("Old seq hdr detected"); - vcd_pmem_free(p_cctxt->seq_hdr.n_sequence_header_len, - p_cctxt->seq_hdr.p_sequence_header, - p_cctxt->p_seq_hdr_phy_addr); - p_cctxt->seq_hdr.p_sequence_header = NULL; + dma_free_coherent(NULL, cctxt->seq_hdr.sz + + VCD_SEQ_HDR_PADDING_BYTES, cctxt->seq_hdr.addr, + cctxt->seq_hdr_phys_addr); + cctxt->seq_hdr.addr = NULL; } - p_cctxt->seq_hdr.n_sequence_header_len = - p_seq_hdr->n_sequence_header_len; + cctxt->seq_hdr.sz = seq_hdr->sz; - prop_hdr.prop_id = DDL_I_SEQHDR_ALIGN_BYTES; - prop_hdr.n_size = sizeof(u32); + //TODO strip out all this alignment crap? +#if 0 + prop_hdr.id = DDL_I_SEQHDR_ALIGN_BYTES; + prop_hdr.size = sizeof(u32); - rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, &n_align); + rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &align); VCD_FAILED_RETURN(rc, - "Failed: ddl_get_property DDL_I_SEQHDR_ALIGN_BYTES"); - - VCD_MSG_MED("Seq hdr alignment bytes = %d", n_align); + "Failed: ddl_get_property DDL_I_SEQHDR_ALIGN_BYTES"); - ret = vcd_pmem_alloc(p_cctxt->seq_hdr.n_sequence_header_len + n_align + - VCD_SEQ_HDR_PADDING_BYTES, - &(p_cctxt->seq_hdr.p_sequence_header), - &(p_cctxt->p_seq_hdr_phy_addr)); + VCD_MSG_MED("Seq hdr alignment bytes = %d", align); +#endif - if (ret < 0) { + cctxt->seq_hdr.addr = dma_alloc_coherent(NULL, + cctxt->seq_hdr.sz + VCD_SEQ_HDR_PADDING_BYTES, + &cctxt->seq_hdr_phys_addr, GFP_KERNEL); + if (!cctxt->seq_hdr.addr) { VCD_MSG_ERROR("Seq hdr allocation failed"); - return VCD_ERR_ALLOC_FAIL; } - if (!p_cctxt->p_seq_hdr_phy_addr) { - VCD_MSG_ERROR("Couldn't get physical address"); - - return VCD_ERR_BAD_POINTER; - } - - if (n_align > 0) { - n_addr = (u32) p_cctxt->p_seq_hdr_phy_addr; - n_addr += n_align; - n_addr -= (n_addr % n_align); - p_virtual_aligned = p_cctxt->seq_hdr.p_sequence_header; - p_virtual_aligned += (u32) (n_addr - - (u32) p_cctxt->p_seq_hdr_phy_addr); - p_cctxt->p_seq_hdr_phy_addr = (u8 *) n_addr; - } else { - p_virtual_aligned = p_cctxt->seq_hdr.p_sequence_header; - } - - memcpy(p_virtual_aligned, p_seq_hdr->p_sequence_header, - p_seq_hdr->n_sequence_header_len); + memset(cctxt->seq_hdr.addr, 0, + cctxt->seq_hdr.sz + VCD_SEQ_HDR_PADDING_BYTES); + memcpy(cctxt->seq_hdr.addr, seq_hdr->addr, seq_hdr->sz); return VCD_S_SUCCESS; } -u32 vcd_set_frame_rate( - struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_property_frame_rate_type *p_fps) +u32 vcd_set_frame_rate(struct vcd_clnt_ctxt *cctxt, + struct vcd_property_frame_rate *fps) { union sched_value_type sched_val; u32 rc; - sched_val.frm_rate.n_numer = p_fps->n_fps_numerator; - sched_val.frm_rate.n_denom = p_fps->n_fps_denominator; - p_cctxt->frm_rate = *p_fps; - - rc = vcd_map_sched_status(sched_set_client_param - (p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, - SCHED_I_CLNT_FRAMERATE, &sched_val)); + sched_val.frm_rate.numer = fps->fps_numerator; + sched_val.frm_rate.denom = fps->fps_denominator; + cctxt->frm_rate = *fps; + rc = vcd_map_sched_status(sched_set_client_param( + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, + SCHED_I_CLNT_FRAMERATE, &sched_val)); if (VCD_FAILED(rc)) { VCD_MSG_ERROR("rc = 0x%x. Failed: Set SCHED_I_CLNT_FRAMERATE", - rc); + rc); } - rc = vcd_update_clnt_perf_lvl(p_cctxt, &p_cctxt->frm_rate, - p_cctxt->n_frm_p_units); - + rc = vcd_update_clnt_perf_lvl(cctxt, &cctxt->frm_rate, + cctxt->frm_p_units); if (VCD_FAILED(rc)) { VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_update_clnt_perf_lvl", rc); } - sched_val.un_value = p_cctxt->n_reqd_perf_lvl; + sched_val.un_value = cctxt->reqd_perf_lvl; - rc = vcd_map_sched_status(sched_set_client_param - (p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, - SCHED_I_CLNT_PTKNRATE, &sched_val)); + rc = vcd_map_sched_status(sched_set_client_param( + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, + SCHED_I_CLNT_PTKNRATE, &sched_val)); if (VCD_FAILED(rc)) { VCD_MSG_ERROR("rc = 0x%x. Failed: Set SCHED_I_CLNT_PTKNRATE", @@ -2982,48 +2660,44 @@ u32 vcd_set_frame_rate( return VCD_S_SUCCESS; } -u32 vcd_set_frame_size( - struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_property_frame_size_type *p_frm_size) +u32 vcd_set_frame_size(struct vcd_clnt_ctxt *cctxt, + struct vcd_property_frame_size *frm_size) { - struct vcd_property_hdr_type prop_hdr; + struct vcd_property_hdr prop_hdr; union sched_value_type sched_val; u32 rc; - u32 n_frm_p_units; - p_frm_size = NULL; + u32 frm_p_units; + frm_size = NULL; - prop_hdr.prop_id = DDL_I_FRAME_PROC_UNITS; - prop_hdr.n_size = sizeof(n_frm_p_units); - rc = ddl_get_property(p_cctxt->ddl_handle, &prop_hdr, &n_frm_p_units); + prop_hdr.id = DDL_I_FRAME_PROC_UNITS; + prop_hdr.sz = sizeof(frm_p_units); + rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &frm_p_units); VCD_FAILED_RETURN(rc, "Failed: Get DDL_I_FRAME_PROC_UNITS"); - p_cctxt->n_frm_p_units = sched_val.un_value = n_frm_p_units; + cctxt->frm_p_units = sched_val.un_value = frm_p_units; - rc = vcd_map_sched_status(sched_set_client_param - (p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, - SCHED_I_CLNT_PTKNPERFRM, &sched_val)); + rc = vcd_map_sched_status(sched_set_client_param( + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, + SCHED_I_CLNT_PTKNPERFRM, &sched_val)); if (VCD_FAILED(rc)) { VCD_MSG_ERROR("rc = 0x%x. Failed: Set SCHED_I_CLNT_PTKNPERFRM", rc); } - rc = vcd_update_clnt_perf_lvl(p_cctxt, &p_cctxt->frm_rate, - n_frm_p_units); + rc = vcd_update_clnt_perf_lvl(cctxt, &cctxt->frm_rate, frm_p_units); if (VCD_FAILED(rc)) { VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_update_clnt_perf_lvl", rc); } - sched_val.un_value = p_cctxt->n_reqd_perf_lvl; + sched_val.un_value = cctxt->reqd_perf_lvl; - rc = vcd_map_sched_status(sched_set_client_param - (p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, - SCHED_I_CLNT_PTKNRATE, &sched_val)); + rc = vcd_map_sched_status(sched_set_client_param( + cctxt->dev_ctxt->sched_hdl, cctxt->sched_clnt_hdl, + SCHED_I_CLNT_PTKNRATE, &sched_val)); if (VCD_FAILED(rc)) { VCD_MSG_ERROR("rc = 0x%x. Failed: Set SCHED_I_CLNT_PTKNRATE", @@ -3033,130 +2707,121 @@ u32 vcd_set_frame_size( return VCD_S_SUCCESS; } -void vcd_process_pending_flush_in_eos(struct vcd_clnt_ctxt_type_t *p_cctxt) +void vcd_process_pending_flush_in_eos(struct vcd_clnt_ctxt *cctxt) { u32 rc = VCD_S_SUCCESS; VCD_MSG_HIGH("Buffer flush is pending"); - rc = vcd_flush_buffers(p_cctxt, p_cctxt->status.n_flush_mode); + rc = vcd_flush_buffers(cctxt, cctxt->status.flush_mode); if (VCD_FAILED(rc)) VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_flush_buffers", rc); - p_cctxt->status.b_eos_wait_for_op_buf = FALSE; + cctxt->status.eos_wait_for_op_buf = false; - vcd_send_flush_done(p_cctxt, VCD_S_SUCCESS); + vcd_send_flush_done(cctxt, VCD_S_SUCCESS); } -void vcd_process_pending_stop_in_eos(struct vcd_clnt_ctxt_type_t *p_cctxt) +void vcd_process_pending_stop_in_eos(struct vcd_clnt_ctxt *cctxt) { u32 rc = VCD_S_SUCCESS; - rc = vcd_flush_buffers(p_cctxt, VCD_FLUSH_ALL); + rc = vcd_flush_buffers(cctxt, VCD_FLUSH_ALL); if (VCD_FAILED(rc)) VCD_MSG_ERROR("rc = 0x%x. Failed: vcd_flush_buffers", rc); VCD_MSG_HIGH("All buffers are returned. Enqueuing stop cmd"); - vcd_client_cmd_flush_and_en_q(p_cctxt, VCD_CMD_CODEC_STOP); - p_cctxt->status.b_stop_pending = FALSE; + vcd_client_cmd_flush_and_en_q(cctxt, VCD_CMD_CODEC_STOP); + cctxt->status.stop_pending = false; - vcd_do_client_state_transition(p_cctxt, - VCD_CLIENT_STATE_STOPPING, - CLIENT_STATE_EVENT_NUMBER(pf_stop)); + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_STOPPING, + CLIENT_STATE_EVENT_NUMBER(pf_stop)); } -u32 vcd_calculate_frame_delta( - struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_frame) +u32 vcd_calculate_frame_delta(struct vcd_clnt_ctxt *cctxt, + struct vcd_frame_data *frame) { - u32 n_frm_delta; - u64 n_temp, temp1; + u32 frm_delta; + u64 temp, temp1; - n_temp = p_frame->time_stamp - p_cctxt->status.prev_ts; + temp = frame->time_stamp - cctxt->status.prev_ts; - VCD_MSG_LOW("Curr_ts=%lld Prev_ts=%lld Diff=%llu", - p_frame->time_stamp, p_cctxt->status.prev_ts, n_temp); + VCD_MSG_LOW("Curr_ts=%lld Prev_ts=%lld Diff=%llu", frame->time_stamp, + cctxt->status.prev_ts, temp); - n_temp = n_temp * p_cctxt->n_time_resoln; - n_temp = (n_temp + (VCD_TIMESTAMP_RESOLUTION >> 1)); - temp1 = do_div(n_temp, VCD_TIMESTAMP_RESOLUTION); - n_frm_delta = n_temp; - VCD_MSG_LOW("temp1=%lld n_temp=%lld", temp1, n_temp); - p_cctxt->status.n_time_elapsed += n_frm_delta; + temp = temp * cctxt->time_resoln; + temp = (temp + (VCD_TIMESTAMP_RESOLUTION >> 1)); + temp1 = do_div(temp, VCD_TIMESTAMP_RESOLUTION); + frm_delta = temp; + VCD_MSG_LOW("temp1=%lld temp=%lld", temp1, temp); + cctxt->status.time_elapsed += frm_delta; - n_temp = ((u64)p_cctxt->status.n_time_elapsed \ - * VCD_TIMESTAMP_RESOLUTION); - n_temp = (n_temp + (p_cctxt->n_time_resoln >> 1)); - temp1 = do_div(n_temp, p_cctxt->n_time_resoln); + temp = ((u64)cctxt->status.time_elapsed * VCD_TIMESTAMP_RESOLUTION); + temp = (temp + (cctxt->time_resoln >> 1)); + temp1 = do_div(temp, cctxt->time_resoln); - p_cctxt->status.prev_ts = p_cctxt->status.first_ts + n_temp; + cctxt->status.prev_ts = cctxt->status.first_ts + temp; VCD_MSG_LOW("Time_elapsed=%u, Drift=%llu, new Prev_ts=%lld", - p_cctxt->status.n_time_elapsed, temp1, - p_cctxt->status.prev_ts); + cctxt->status.time_elapsed, temp1, cctxt->status.prev_ts); - return n_frm_delta; + return frm_delta; } -struct vcd_buffer_entry_type *vcd_check_fill_output_buffer - (struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_frame_data_type *p_buffer) { - struct vcd_buffer_pool_type *p_buf_pool = &p_cctxt->out_buf_pool; - struct vcd_buffer_entry_type *p_buf_entry; +struct vcd_buffer_entry *vcd_check_fill_output_buffer(struct vcd_clnt_ctxt + *cctxt, struct vcd_frame_data *buffer) +{ + struct vcd_buffer_pool *buf_pool = &cctxt->out_buf_pool; + struct vcd_buffer_entry *buf_entry; - if (!p_buf_pool->a_entries) { + if (!buf_pool->entries) { VCD_MSG_ERROR("Buffers not set or allocated yet"); - return NULL; } - if (!p_buffer->p_virtual) { + if (!buffer->virt_addr) { VCD_MSG_ERROR("NULL buffer address provided"); return NULL; } - p_buf_entry = - vcd_find_buffer_pool_entry(p_buf_pool, p_buffer->p_virtual); - if (!p_buf_entry) { - VCD_MSG_ERROR("Unrecognized buffer address provided = %p", - p_buffer->p_virtual); + buf_entry = vcd_find_buffer_pool_entry(buf_pool, buffer->virt_addr); + if (!buf_entry) { + VCD_MSG_ERROR("Unrecognized buffer address provided %p", + buffer->virt_addr); return NULL; } - if (p_buf_entry->b_in_use) { - VCD_MSG_ERROR - ("An inuse output frame is being provided for reuse"); + if (buf_entry->in_use) { + VCD_MSG_ERROR("An inuse output frame is being provided for " + "reuse"); return NULL; } - if (p_buffer->n_alloc_len < p_buf_pool->buf_req.n_size || - p_buffer->n_alloc_len > p_buf_entry->n_size) { - VCD_MSG_ERROR - ("Bad buffer Alloc_len = %d, Actual size = %d, " - " Min size = %u", - p_buffer->n_alloc_len, p_buf_entry->n_size, - p_buf_pool->buf_req.n_size); + if (buffer->alloc_len < buf_pool->buf_req.size || + buffer->alloc_len > buf_entry->size) { + VCD_MSG_ERROR("Bad buffer Alloc_len = %d, Actual size = %d, " + " Min size = %u", buffer->alloc_len, buf_entry->size, + buf_pool->buf_req.size); return NULL; } - return p_buf_entry; + return buf_entry; } -void vcd_handle_ind_hw_err_fatal(struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 event, u32 status) +void vcd_handle_ind_hw_err_fatal(struct vcd_clnt_ctxt *cctxt, u32 event, + u32 status) { - if (p_cctxt->status.n_frame_submitted) { - p_cctxt->status.n_frame_submitted--; - vcd_mark_frame_channel(p_cctxt->p_dev_ctxt); + if (cctxt->status.frame_submitted) { + cctxt->status.frame_submitted--; + vcd_mark_frame_channel(cctxt->dev_ctxt); } - vcd_handle_err_fatal(p_cctxt, event, status); + vcd_handle_err_fatal(cctxt, event, status); } -void vcd_handle_err_fatal(struct vcd_clnt_ctxt_type_t *p_cctxt, u32 event, - u32 status) +void vcd_handle_err_fatal(struct vcd_clnt_ctxt *cctxt, u32 event, u32 status) { u32 rc; VCD_MSG_LOW("vcd_handle_err_fatal: event=%x, err=%x", event, status); @@ -3164,68 +2829,65 @@ void vcd_handle_err_fatal(struct vcd_clnt_ctxt_type_t *p_cctxt, u32 event, return; if (VCD_FAILED_DEVICE_FATAL(status)) { - vcd_clnt_handle_device_err_fatal(p_cctxt, event); - vcd_handle_device_err_fatal(p_cctxt->p_dev_ctxt, p_cctxt); + vcd_clnt_handle_device_err_fatal(cctxt, event); + vcd_handle_device_err_fatal(cctxt->dev_ctxt, cctxt); } else if (VCD_FAILED_CLIENT_FATAL(status)) { - p_cctxt->status.e_last_evt = event; + cctxt->status.last_evt = event; - if (p_cctxt->b_sched_clnt_valid) { + if (cctxt->sched_clnt_valid) { rc = vcd_map_sched_status(sched_suspend_resume_client( - p_cctxt->p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, FALSE)); + cctxt->dev_ctxt->sched_hdl, + cctxt->sched_clnt_hdl, false)); if (VCD_FAILED(rc)) { VCD_MSG_ERROR("Failed: sched_suspend_resume_" "client rc=0x%x", rc); } } - p_cctxt->callback(event, VCD_ERR_HW_FATAL, NULL, 0, p_cctxt, - p_cctxt->p_client_data); - p_cctxt->status.b_cleaning_up = TRUE; - vcd_client_cmd_flush_and_en_q(p_cctxt, VCD_CMD_CODEC_STOP); - vcd_do_client_state_transition(p_cctxt, - VCD_CLIENT_STATE_INVALID, + cctxt->callback(event, VCD_ERR_HW_FATAL, NULL, 0, cctxt, + cctxt->client_data); + cctxt->status.cleaning_up = true; + vcd_client_cmd_flush_and_en_q(cctxt, VCD_CMD_CODEC_STOP); + vcd_do_client_state_transition(cctxt, VCD_CLIENT_STATE_INVALID, CLIENT_STATE_EVENT_NUMBER(pf_clnt_cb)); } } -void vcd_handle_err_in_starting(struct vcd_clnt_ctxt_type_t *p_cctxt, - u32 status) +void vcd_handle_err_in_starting(struct vcd_clnt_ctxt *cctxt, u32 status) { VCD_MSG_LOW("\n vcd_handle_err_in_starting:"); if (VCD_FAILED_FATAL(status)) { - vcd_handle_err_fatal(p_cctxt, VCD_EVT_RESP_START, status); + vcd_handle_err_fatal(cctxt, VCD_EVT_RESP_START, status); } else { - p_cctxt->status.e_last_err = status; + cctxt->status.last_err = status; VCD_MSG_HIGH("\n VCD cleanup: Enqueuing stop cmd"); - vcd_client_cmd_flush_and_en_q(p_cctxt, VCD_CMD_CODEC_STOP); + vcd_client_cmd_flush_and_en_q(cctxt, VCD_CMD_CODEC_STOP); } } -void vcd_handle_trans_pending(struct vcd_clnt_ctxt_type_t *p_cctxt) +void vcd_handle_trans_pending(struct vcd_clnt_ctxt *cctxt) { - if (!p_cctxt->status.n_frame_submitted) { + if (!cctxt->status.frame_submitted) { VCD_MSG_ERROR("Transaction pending response was not expected"); vcd_assert(); return; } - p_cctxt->status.n_frame_submitted--; - p_cctxt->status.n_frame_delayed++; - vcd_mark_frame_channel(p_cctxt->p_dev_ctxt); + cctxt->status.frame_submitted--; + cctxt->status.frame_delayed++; + vcd_mark_frame_channel(cctxt->dev_ctxt); } -u32 vcd_requeue_input_frame(struct vcd_dev_ctxt_type *p_dev_ctxt, - struct vcd_clnt_ctxt_type_t *p_cctxt, - struct vcd_buffer_entry_type *p_buf_entry) +u32 vcd_requeue_input_frame(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_clnt_ctxt *cctxt, struct vcd_buffer_entry *buf_entry) { u32 rc; - rc = vcd_map_sched_status(sched_re_queue_frame(p_dev_ctxt->sched_hdl, - p_cctxt->sched_clnt_hdl, (void *) p_buf_entry)); + rc = vcd_map_sched_status(sched_re_queue_frame(dev_ctxt->sched_hdl, + cctxt->sched_clnt_hdl, (void *) buf_entry)); VCD_FAILED_RETURN(rc, "Failed: Sched_ReQueueFrame"); - if (p_buf_entry->frame.n_flags & VCD_FRAME_FLAG_EOS) { - rc = vcd_map_sched_status(sched_mark_client_eof(p_dev_ctxt-> - sched_hdl, p_cctxt->sched_clnt_hdl)); + if (buf_entry->frame.flags & VCD_FRAME_FLAG_EOS) { + rc = vcd_map_sched_status(sched_mark_client_eof(dev_ctxt-> + sched_hdl, cctxt->sched_clnt_hdl)); } if (VCD_FAILED(rc)) @@ -3234,24 +2896,24 @@ u32 vcd_requeue_input_frame(struct vcd_dev_ctxt_type *p_dev_ctxt, return rc; } -void vcd_handle_submit_frame_failed(struct vcd_dev_ctxt_type - *p_dev_ctxt, struct vcd_transc_type *p_transc) +void vcd_handle_submit_frame_failed(struct vcd_dev_ctxt *dev_ctxt, + struct vcd_transc *transc) { - struct vcd_clnt_ctxt_type_t *p_cctxt = p_transc->p_cctxt; + struct vcd_clnt_ctxt *cctxt = transc->cctxt; u32 rc; - vcd_mark_frame_channel(p_dev_ctxt); - p_transc->b_in_use = FALSE; + vcd_mark_frame_channel(dev_ctxt); + transc->in_use = false; - vcd_handle_err_fatal(p_cctxt, VCD_EVT_IND_HWERRFATAL, + vcd_handle_err_fatal(cctxt, VCD_EVT_IND_HWERRFATAL, VCD_ERR_CLIENT_FATAL); - if (vcd_get_command_channel(p_dev_ctxt, &p_transc)) { - p_transc->e_type = VCD_CMD_CODEC_STOP; - p_transc->p_cctxt = p_cctxt; - rc = vcd_submit_cmd_sess_end(p_transc); - if (VCD_FAILED(rc)) { - vcd_release_command_channel(p_dev_ctxt, p_transc); + if (vcd_get_command_channel(dev_ctxt, &transc)) { + transc->type = VCD_CMD_CODEC_STOP; + transc->cctxt = cctxt; + rc = vcd_submit_cmd_sess_end(transc); + if (VCD_FAILED(rc)) { + vcd_release_command_channel(dev_ctxt, transc); VCD_MSG_ERROR("rc = 0x%x. Failed: VCD_SubmitCmdSessEnd", rc); } diff --git a/drivers/misc/video_core/720p/vcd/vcd_util.c b/drivers/misc/video_core/720p/vcd/vcd_util.c deleted file mode 100644 index 579ca8aa1316d..0000000000000 --- a/drivers/misc/video_core/720p/vcd/vcd_util.c +++ /dev/null @@ -1,103 +0,0 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - */ - -#include "video_core_type.h" -#include "vcd_util.h" - -u32 vcd_critical_section_create(u32 **p_cs) -{ - struct mutex *lock; - if (!p_cs) { - VCD_MSG_ERROR("Bad critical section ptr"); - return VCD_ERR_BAD_POINTER; - } else { - lock = kmalloc(sizeof(struct mutex), GFP_KERNEL); - if (!lock) { - VCD_MSG_ERROR("Failed: vcd_critical_section_create"); - return VCD_ERR_ALLOC_FAIL; - } - mutex_init(lock); - *p_cs = (u32 *) lock; - return VCD_S_SUCCESS; - } -} - -u32 vcd_critical_section_release(u32 *cs) -{ - struct mutex *lock = (struct mutex *)cs; - if (!lock) { - VCD_MSG_ERROR("Bad critical section object"); - return VCD_ERR_BAD_POINTER; - } - - mutex_destroy(lock); - kfree(cs); - return VCD_S_SUCCESS; -} - -u32 vcd_critical_section_enter(u32 *cs) -{ - struct mutex *lock = (struct mutex *)cs; - if (!lock) { - VCD_MSG_ERROR("Bad critical section object"); - return VCD_ERR_BAD_POINTER; - } else - mutex_lock(lock); - - return VCD_S_SUCCESS; -} - -u32 vcd_critical_section_leave(u32 *cs) -{ - struct mutex *lock = (struct mutex *)cs; - - if (!lock) { - VCD_MSG_ERROR("Bad critical section object"); - - return VCD_ERR_BAD_POINTER; - } else - mutex_unlock(lock); - - return VCD_S_SUCCESS; -} - -int vcd_pmem_alloc(u32 size, u8 **kernel_vaddr, u8 **phy_addr) -{ - *kernel_vaddr = dma_alloc_coherent(NULL, size, (dma_addr_t *)phy_addr, GFP_KERNEL); -// *phy_addr = -// (u8 *) pmem_kalloc(size, PMEM_MEMTYPE_EBI1 | PMEM_ALIGNMENT_4K); - - if (!IS_ERR((void *)*kernel_vaddr)) { - pr_debug("write buf: phy addr 0x%08x kernel addr 0x%08x\n", - (u32) *phy_addr, (u32) *kernel_vaddr); - return 0; - } else { - pr_err("%s: could not allocte in kernel pmem buffers\n", - __func__); - return -ENOMEM; - } - -} - -int vcd_pmem_free(u32 size, u8 *kernel_vaddr, u8 *phy_addr) -{ - dma_free_coherent(NULL, size, (void *)kernel_vaddr, (dma_addr_t)phy_addr); -// pmem_kfree((s32) phy_addr); - - return 0; -} diff --git a/drivers/misc/video_core/720p/vcd/vcd_util.h b/drivers/misc/video_core/720p/vcd/vcd_util.h index 375145945c976..2d90bf5bdd36a 100644 --- a/drivers/misc/video_core/720p/vcd/vcd_util.h +++ b/drivers/misc/video_core/720p/vcd/vcd_util.h @@ -33,9 +33,10 @@ #if DEBUG -#define VCD_MSG_LOW(xx_fmt...) printk(KERN_INFO "\n\t* " xx_fmt) -#define VCD_MSG_MED(xx_fmt...) printk(KERN_INFO "\n * " xx_fmt) -#define VCD_MSG_HIGH(xx_fmt...) printk(KERN_WARNING "\n" xx_fmt) +//TODO what a load of crap in here +#define VCD_MSG_LOW(xx_fmt...) printk(KERN_INFO "\t* " xx_fmt) +#define VCD_MSG_MED(xx_fmt...) printk(KERN_INFO " * " xx_fmt) +#define VCD_MSG_HIGH(xx_fmt...) printk(KERN_WARNING xx_fmt) #else @@ -45,8 +46,8 @@ #endif -#define VCD_MSG_ERROR(xx_fmt...) printk(KERN_ERR "\n err: " xx_fmt) -#define VCD_MSG_FATAL(xx_fmt...) printk(KERN_ERR "\n " xx_fmt) +#define VCD_MSG_ERROR(xx_fmt...) printk(KERN_ERR "err: " xx_fmt) +#define VCD_MSG_FATAL(xx_fmt...) printk(KERN_ERR " " xx_fmt) #define VCD_FAILED_RETURN(rc, xx_fmt...) \ do { \ @@ -57,31 +58,14 @@ } while (0) #define VCD_FAILED_DEVICE_FATAL(rc) \ - (rc == VCD_ERR_HW_FATAL ? TRUE : FALSE) + (rc == VCD_ERR_HW_FATAL ? true : false) #define VCD_FAILED_CLIENT_FATAL(rc) \ - (rc == VCD_ERR_CLIENT_FATAL ? TRUE : FALSE) + (rc == VCD_ERR_CLIENT_FATAL ? true : false) #define VCD_FAILED_FATAL(rc) \ ((VCD_FAILED_DEVICE_FATAL(rc) || VCD_FAILED_CLIENT_FATAL(rc)) \ - ? TRUE : FALSE) + ? true : false) - -#define vcd_assert() VCD_MSG_FATAL("ASSERT") -#define vcd_malloc(n_bytes) kmalloc(n_bytes, GFP_KERNEL) -#define vcd_free(p_mem) kfree(p_mem) - -#ifdef NO_IN_KERNEL_PMEM - #define VCD_PMEM_malloc(n_bytes) kmalloc(n_bytes, GFP_KERNEL) - #define VCD_PMEM_free(p_mem) kfree(p_mem) - #define VCD_PMEM_get_physical(p_mem) virt_to_phys(p_mem) -#else - int vcd_pmem_alloc(u32 size, u8 **kernel_vaddr, u8 **phy_addr); - int vcd_pmem_free(u32 size, u8 *kernel_vaddr, u8 *phy_addr); -#endif - -u32 vcd_critical_section_create(u32 **p_cs); -u32 vcd_critical_section_release(u32 *cs); -u32 vcd_critical_section_enter(u32 *cs); -u32 vcd_critical_section_leave(u32 *cs); +#define vcd_assert() VCD_MSG_FATAL("ASSERT") #endif diff --git a/drivers/misc/video_core/720p/vcd/video_core_type.h b/drivers/misc/video_core/720p/vcd/video_core_type.h index c87f8351e5924..febd4cf724caf 100644 --- a/drivers/misc/video_core/720p/vcd/video_core_type.h +++ b/drivers/misc/video_core/720p/vcd/video_core_type.h @@ -39,13 +39,9 @@ #include #include -#define TRUE 1 -#define FALSE 0 - -#define DEBUG 0 +#define DEBUG 1 #define USE_RES_TRACKER -//#define AXI_CLK_SCALING #undef CORE_TIMING_INFO diff --git a/include/linux/msm_vidc_dec.h b/include/linux/msm_vidc_dec.h index 284660385e0b0..5d6233a12290b 100644 --- a/include/linux/msm_vidc_dec.h +++ b/include/linux/msm_vidc_dec.h @@ -106,8 +106,8 @@ #define VDEC_IOCTL_MAGIC 'v' struct vdec_ioctl_msg { - void *inputparam; - void *outputparam; + void __user *in; + void __user *out; }; /* CMD params: InputParam:enum vdec_codec @@ -226,11 +226,11 @@ struct vdec_allocatorproperty { }; struct vdec_bufferpayload { - uint8_t *bufferaddr; - uint32_t buffer_len; + void __user *addr; + size_t sz; int pmem_fd; - uint32_t offset; - uint32_t mmaped_size; + size_t offset; + size_t mmaped_sz; }; struct vdec_setbuffer_cmd { @@ -458,7 +458,7 @@ enum vdec_interlaced_format { VDEC_InterlaceInterleaveFrameBottomFieldFirst = 0x4 }; -enum vdec_output_fromat { +enum vdec_output_format { VDEC_YUV_FORMAT_NV12 = 0x1, VDEC_YUV_FORMAT_TILE_4x2 = 0x2 }; @@ -471,10 +471,10 @@ struct vdec_picsize { }; struct vdec_seqheader { - uint8_t *ptr_seqheader; - uint32_t seq_header_len; + void *addr; + size_t sz; int pmem_fd; - uint32_t pmem_offset; + size_t pmem_offset; }; struct vdec_mberror { @@ -483,26 +483,26 @@ struct vdec_mberror { }; struct vdec_input_frameinfo { - uint8_t *bufferaddr; - uint32_t offset; - uint32_t datalen; + void __user *user_addr; + size_t offset; + size_t data_len; uint32_t flags; int64_t timestamp; void *client_data; int pmem_fd; - uint32_t pmem_offset; + size_t pmem_offset; }; struct vdec_framesize { - uint32_t n_left; - uint32_t n_top; - uint32_t n_right; - uint32_t n_bottom; + uint32_t left; + uint32_t top; + uint32_t right; + uint32_t bottom; }; struct vdec_output_frameinfo { - uint8_t *phy_addr; - uint8_t *bufferaddr; + phys_addr_t phys_addr; + void __user *user_addr; uint32_t offset; uint32_t len; uint32_t flags; diff --git a/include/linux/msm_vidc_enc.h b/include/linux/msm_vidc_enc.h index b5cf584901b5f..f7f398c297728 100644 --- a/include/linux/msm_vidc_enc.h +++ b/include/linux/msm_vidc_enc.h @@ -184,9 +184,9 @@ /* Base value for encoder configuration ioctls*/ #define VEN_IOCTLBASE_ENC 0x850 -struct venc_ioctl_msg{ - void *inputparam; - void *outputparam; +struct venc_ioctl_msg { + void __user *in; + void __user *out; }; /*NON ENCODER CONFIGURATION IOCTLs*/ @@ -449,77 +449,77 @@ struct venc_ioctl_msg{ #define VEN_IOCTL_GET_QP_RANGE \ _IOR(VEN_IOCTLBASE_ENC, 45, struct venc_ioctl_msg) -struct venc_switch{ - unsigned char status; +struct venc_switch { + unsigned char status; }; -struct venc_allocatorproperty{ - unsigned long mincount; - unsigned long maxcount; - unsigned long actualcount; - unsigned long datasize; - unsigned long suffixsize; - unsigned long alignment; - unsigned long bufpoolid; +struct venc_allocatorproperty { + u32 mincount; + u32 maxcount; + u32 actualcount; + u32 datasize; + u32 suffixsize; + u32 alignment; + u32 bufpoolid; }; -struct venc_bufferpayload{ - unsigned char *pbuffer; - unsigned long nsize; - int fd; - unsigned int offset; - unsigned int maped_size; - unsigned long filled_len; +struct venc_bufferpayload { + void __user *buffer; + size_t sz; + int fd; + size_t offset; + unsigned int maped_size; + unsigned long filled_len; }; -struct venc_buffer{ - unsigned char *ptrbuffer; - unsigned long size; - unsigned long len; - unsigned long offset; - long long timestamp; - unsigned long flags; - void *clientdata; +struct venc_buffer { + void __user *addr; + size_t sz; + size_t len; + size_t offset; + long long timestamp; + u32 flags; + void *clientdata; }; -struct venc_basecfg{ - unsigned long input_width; - unsigned long input_height; - unsigned long dvs_width; - unsigned long dvs_height; - unsigned long codectype; - unsigned long fps_num; - unsigned long fps_den; - unsigned long targetbitrate; - unsigned long inputformat; +struct venc_basecfg { + u32 input_width; + u32 input_height; + u32 dvs_width; + u32 dvs_height; + u32 codectype; + u32 fps_num; + u32 fps_den; + u32 targetbitrate; + u32 inputformat; }; -struct venc_profile{ +struct venc_profile { unsigned long profile; }; -struct ven_profilelevel{ +struct ven_profilelevel { unsigned long level; }; -struct venc_sessionqp{ +struct venc_sessionqp { unsigned long iframeqp; unsigned long pframqp; }; -struct venc_qprange{ - unsigned long maxqp; - unsigned long minqp; +struct venc_qprange { + u32 maxqp; + u32 minqp; }; -struct venc_intraperiod{ +struct venc_intraperiod { unsigned long num_pframes; }; -struct venc_seqheader{ - unsigned char *hdrbufptr; - unsigned long bufsize; - unsigned long hdrlen; +struct venc_seqheader { + void *buf; + size_t buf_sz; + size_t hdr_len; }; -struct venc_capability{ +struct venc_capability { unsigned long codec_types; unsigned long maxframe_width; unsigned long maxframe_height; @@ -529,64 +529,64 @@ struct venc_capability{ unsigned char dvs; }; -struct venc_entropycfg{ +struct venc_entropycfg { unsigned longentropysel; unsigned long cabacmodel; }; -struct venc_dbcfg{ - unsigned long db_mode; - unsigned long slicealpha_offset; - unsigned long slicebeta_offset; +struct venc_dbcfg { + u32 db_mode; + u32 slicealpha_offset; + u32 slicebeta_offset; }; -struct venc_intrarefresh{ +struct venc_intrarefresh { unsigned long irmode; unsigned long mbcount; }; -struct venc_multiclicecfg{ +struct venc_multiclicecfg { unsigned long mslice_mode; unsigned long mslice_size; }; -struct venc_bufferflush{ +struct venc_bufferflush { unsigned long flush_mode; }; -struct venc_ratectrlcfg{ +struct venc_ratectrlcfg { unsigned long rcmode; }; -struct venc_voptimingcfg{ - unsigned long voptime_resolution; +struct venc_voptimingcfg { + u32 voptime_resolution; }; -struct venc_framerate{ - unsigned long fps_denominator; - unsigned long fps_numerator; +struct venc_framerate { + u32 fps_denominator; + u32 fps_numerator; }; +//TODO remove these stupid structs struct venc_targetbitrate{ - unsigned long target_bitrate; + u32 target_bitrate; }; - -struct venc_rotation{ - unsigned long rotation; +struct venc_rotation { + u32 rotation; }; -struct venc_timeout{ - unsigned long millisec; +struct venc_timeout { + u32 millisec; }; -struct venc_headerextension{ - unsigned long header_extension; +struct venc_headerextension { + unsigned long header_extension; }; -struct venc_msg{ - unsigned long statuscode; - unsigned long msgcode; - struct venc_buffer buf; - unsigned long msgdata_size; +struct venc_msg { + unsigned long statuscode; + unsigned long msgcode; + struct venc_buffer buf; + size_t msgdata_size; }; #endif /* _MSM_VIDC_ENC_H_ */ From dca94d5ed6c3387caad329d85596690c20b497d3 Mon Sep 17 00:00:00 2001 From: Wu-cheng Li Date: Thu, 8 Jul 2010 13:59:06 -0700 Subject: [PATCH 0697/2556] [ARM] media: video: msm: Add vfe 3.1 files for 7x30. Qualcomm baseline is M7630AABBQMLZA1250. Signed-off-by: Wu-cheng Li --- arch/arm/mach-msm/include/mach/board.h | 2 + drivers/media/video/msm/Makefile | 1 + drivers/media/video/msm/msm_io_vfe31.c | 285 +++ drivers/media/video/msm/msm_vfe31.c | 2314 ++++++++++++++++++++++++ drivers/media/video/msm/msm_vfe31.h | 1052 +++++++++++ 5 files changed, 3654 insertions(+) create mode 100644 drivers/media/video/msm/msm_io_vfe31.c create mode 100644 drivers/media/video/msm/msm_vfe31.c create mode 100644 drivers/media/video/msm/msm_vfe31.h diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index 926a2dfddb103..f1558704b1088 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -37,6 +37,8 @@ struct msm_camera_io_ext { uint32_t mdcsz; uint32_t appphy; uint32_t appsz; + unsigned long camifpadphy; + unsigned long camifpadsz; }; struct msm_camera_device_platform_data { diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile index 37d6037e0470c..b59be469e1562 100755 --- a/drivers/media/video/msm/Makefile +++ b/drivers/media/video/msm/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_MSM_CAMERA) += msm_camera.o msm_v4l2.o obj-$(CONFIG_S5K3E2FX) += s5k3e2fx.o obj-$(CONFIG_ARCH_MSM7X00A) += msm_vfe7x.o msm_io7x.o obj-$(CONFIG_ARCH_QSD8X50) += msm_vfe8x.o msm_vfe8x_proc.o msm_io8x.o +obj-$(CONFIG_ARCH_MSM7X30) += msm_vfe31.o msm_io_vfe31.o diff --git a/drivers/media/video/msm/msm_io_vfe31.c b/drivers/media/video/msm/msm_io_vfe31.c new file mode 100644 index 0000000000000..523c7f1a0d8fd --- /dev/null +++ b/drivers/media/video/msm/msm_io_vfe31.c @@ -0,0 +1,285 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define CAMIF_CFG_RMSK 0x1fffff +#define CAM_SEL_BMSK 0x2 +#define CAM_PCLK_SRC_SEL_BMSK 0x60000 +#define CAM_PCLK_INVERT_BMSK 0x80000 +#define CAM_PAD_REG_SW_RESET_BMSK 0x100000 + +#define EXT_CAM_HSYNC_POL_SEL_BMSK 0x10000 +#define EXT_CAM_VSYNC_POL_SEL_BMSK 0x8000 +#define MDDI_CLK_CHICKEN_BIT_BMSK 0x80 + +#define CAM_SEL_SHFT 0x1 +#define CAM_PCLK_SRC_SEL_SHFT 0x11 +#define CAM_PCLK_INVERT_SHFT 0x13 +#define CAM_PAD_REG_SW_RESET_SHFT 0x14 + +#define EXT_CAM_HSYNC_POL_SEL_SHFT 0x10 +#define EXT_CAM_VSYNC_POL_SEL_SHFT 0xF +#define MDDI_CLK_CHICKEN_BIT_SHFT 0x7 + +static struct clk *camio_vfe_mdc_clk; +static struct clk *camio_mdc_clk; +static struct clk *camio_vfe_clk; +static struct clk *camio_vfe_camif_clk; +static struct clk *camio_vfe_pbdg_clk; +static struct clk *camio_cam_m_clk; +static struct clk *camio_camif_pad_pbdg_clk; +static struct msm_camera_io_ext camio_ext; +static struct resource *camifpadio; +void __iomem *camifpadbase; + +static void camif_io_w(u32 data) +{ + writel(data, camifpadbase); + wmb(); +} + +static u32 camif_io_r(void) +{ + uint32_t data = readl(camifpadbase); + rmb(); + return data; +} + +int msm_camio_clk_enable(enum msm_camio_clk_type clktype) +{ + int rc = 0; + struct clk *clk = NULL; + + switch (clktype) { + case CAMIO_VFE_MDC_CLK: + camio_vfe_mdc_clk = clk = clk_get(NULL, "vfe_mdc_clk"); + break; + + case CAMIO_MDC_CLK: + camio_mdc_clk = clk = clk_get(NULL, "mdc_clk"); + break; + + case CAMIO_VFE_CLK: + camio_vfe_clk = clk = clk_get(NULL, "vfe_clk"); + clk_set_rate(clk, 122880000); + break; + + case CAMIO_VFE_CAMIF_CLK: + camio_vfe_camif_clk = clk = clk_get(NULL, "vfe_camif_clk"); + break; + + case CAMIO_VFE_PBDG_CLK: + camio_vfe_pbdg_clk = clk = clk_get(NULL, "vfe_pclk"); + break; + + case CAMIO_CAM_MCLK_CLK: + camio_cam_m_clk = clk = clk_get(NULL, "cam_m_clk"); + clk_set_rate(clk, 24000000); + break; + + case CAMIO_CAMIF_PAD_PBDG_CLK: + camio_camif_pad_pbdg_clk = clk = clk_get(NULL, "camif_pad_pclk"); + break; + + default: + break; + } + + if (!IS_ERR(clk)) + clk_enable(clk); + else + rc = -1; + return rc; +} + +int msm_camio_clk_disable(enum msm_camio_clk_type clktype) +{ + int rc = 0; + struct clk *clk = NULL; + + switch (clktype) { + case CAMIO_VFE_MDC_CLK: + clk = camio_vfe_mdc_clk; + break; + + case CAMIO_MDC_CLK: + clk = camio_mdc_clk; + break; + + case CAMIO_VFE_CLK: + clk = camio_vfe_clk; + break; + + case CAMIO_VFE_CAMIF_CLK: + clk = camio_vfe_camif_clk; + break; + + case CAMIO_VFE_PBDG_CLK: + clk = camio_vfe_pbdg_clk; + break; + + case CAMIO_CAM_MCLK_CLK: + clk = camio_cam_m_clk; + break; + + case CAMIO_CAMIF_PAD_PBDG_CLK: + clk = camio_camif_pad_pbdg_clk; + break; + + default: + break; + } + + if (!IS_ERR(clk)) { + clk_disable(clk); + clk_put(clk); + } else + rc = -1; + + return rc; +} + +void msm_camio_clk_rate_set(int rate) +{ + struct clk *clk = camio_cam_m_clk; + clk_set_rate(clk, rate); +} + +int msm_camio_enable(struct platform_device *pdev) +{ + int rc = 0; + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + + camio_ext = camdev->ioext; + + camdev->camera_gpio_on(); + msm_camio_clk_enable(CAMIO_VFE_PBDG_CLK); + msm_camio_clk_enable(CAMIO_CAMIF_PAD_PBDG_CLK); + msm_camio_clk_enable(CAMIO_CAM_MCLK_CLK); + msm_camio_clk_enable(CAMIO_VFE_CLK); + camifpadio = request_mem_region(camio_ext.camifpadphy, + camio_ext.camifpadsz, pdev->name); + if (!camifpadio) { + rc = -EBUSY; + goto common_fail; + } + camifpadbase = ioremap(camio_ext.camifpadphy, camio_ext.camifpadsz); + if (!camifpadbase) { + rc = -ENOMEM; + goto parallel_busy; + } + msm_camio_clk_enable(CAMIO_VFE_CAMIF_CLK); + return 0; + +parallel_busy: + release_mem_region(camio_ext.camifpadphy, camio_ext.camifpadsz); +common_fail: + msm_camio_clk_disable(CAMIO_VFE_CLK); + msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK); + msm_camio_clk_disable(CAMIO_CAMIF_PAD_PBDG_CLK); + msm_camio_clk_disable(CAMIO_VFE_PBDG_CLK); + camdev->camera_gpio_off(); + return rc; +} + +void msm_camio_disable(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + + msm_camio_clk_disable(CAMIO_VFE_CAMIF_CLK); + iounmap(camifpadbase); + release_mem_region(camio_ext.camifpadphy, camio_ext.camifpadsz); + CDBG("disable clocks\n"); + + msm_camio_clk_disable(CAMIO_VFE_CLK); + msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK); + msm_camio_clk_disable(CAMIO_CAMIF_PAD_PBDG_CLK); + msm_camio_clk_disable(CAMIO_VFE_PBDG_CLK); + camdev->camera_gpio_off(); +} + +void msm_camio_camif_pad_reg_reset(void) +{ + uint32_t reg; + + msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_INTERNAL); + msleep(10); + + reg = camif_io_r() & CAMIF_CFG_RMSK; + reg |= 0x3; + camif_io_w(reg); + msleep(10); + + reg = camif_io_r() & CAMIF_CFG_RMSK; + reg |= 0x10; + camif_io_w(reg); + msleep(10); + + reg = camif_io_r() & CAMIF_CFG_RMSK; + /* Need to be uninverted*/ + reg &= 0x03; + camif_io_w(reg); + msleep(10); +} + +void msm_camio_vfe_blk_reset(void) +{ + return; +} + +void msm_camio_clk_sel(enum msm_camio_clk_src_type srctype) +{ + if (camio_vfe_clk != NULL) { + switch (srctype) { + case MSM_CAMIO_CLK_SRC_INTERNAL: + clk_set_flags(camio_vfe_clk, 0x00000100 << 1); + break; + + case MSM_CAMIO_CLK_SRC_EXTERNAL: + clk_set_flags(camio_vfe_clk, 0x00000100); + break; + + default: + break; + } + } +} +int msm_camio_probe_on(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + camdev->camera_gpio_on(); + return msm_camio_clk_enable(CAMIO_CAM_MCLK_CLK); +} + +int msm_camio_probe_off(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_device_platform_data *camdev = sinfo->pdata; + camdev->camera_gpio_off(); + return msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK); +} diff --git a/drivers/media/video/msm/msm_vfe31.c b/drivers/media/video/msm/msm_vfe31.c new file mode 100644 index 0000000000000..71602293e3356 --- /dev/null +++ b/drivers/media/video/msm/msm_vfe31.c @@ -0,0 +1,2314 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include "msm_vfe31.h" +#include +#include + +#define CHECKED_COPY_FROM_USER(in) { \ + if (copy_from_user((in), (void __user *)cmd->value, \ + cmd->length)) { \ + rc = -EFAULT; \ + break; \ + } \ +} + +#define vfe31_get_ch_ping_addr(chn) \ + (vfe_io_r(VFE_AXI_OFFSET + 0x18 * (chn))) +#define vfe31_get_ch_pong_addr(chn) \ + (vfe_io_r(VFE_AXI_OFFSET + 0x18 * (chn) + 4)) +#define vfe31_get_ch_addr(ping_pong, chn) \ + (((ping_pong) & (1 << (chn))) == 0 ? \ + vfe31_get_ch_pong_addr(chn) : vfe31_get_ch_ping_addr(chn)) + +#define vfe31_put_ch_ping_addr(chn, addr) \ + (vfe_io_w((addr), VFE_AXI_OFFSET + 0x18 * (chn))) +#define vfe31_put_ch_pong_addr(chn, addr) \ + (vfe_io_w((addr), VFE_AXI_OFFSET + 0x18 * (chn) + 4)) +#define vfe31_put_ch_addr(ping_pong, chn, addr) \ + (((ping_pong) & (1 << (chn))) == 0 ? \ + vfe31_put_ch_pong_addr((chn), (addr)) : \ + vfe31_put_ch_ping_addr((chn), (addr))) + +static struct vfe31_ctrl_type *vfe31_ctrl; +static void *vfe_syncdata; + +struct vfe31_isr_queue_cmd { + struct list_head list; + uint32_t vfeInterruptStatus0; + uint32_t vfeInterruptStatus1; + struct vfe_frame_asf_info vfeAsfFrameInfo; + struct vfe_frame_bpc_info vfeBpcFrameInfo; + struct vfe_msg_camif_status vfeCamifStatusLocal; +}; + +static struct vfe31_cmd_type vfe31_cmd[] = { +/* 0*/ {V31_DUMMY_0}, + {V31_SET_CLK}, + {V31_RESET}, + {V31_START}, + {V31_TEST_GEN_START}, +/* 5*/ {V31_OPERATION_CFG, V31_OPERATION_CFG_LEN}, + {V31_AXI_OUT_CFG, V31_AXI_OUT_LEN, V31_AXI_OUT_OFF, 0xFF}, + {V31_CAMIF_CFG, V31_CAMIF_LEN, V31_CAMIF_OFF, 0xFF}, + {V31_AXI_INPUT_CFG}, + {V31_BLACK_LEVEL_CFG, V31_BLACK_LEVEL_LEN, V31_BLACK_LEVEL_OFF, + 0xFF}, +/*10*/ {V31_ROLL_OFF_CFG, V31_ROLL_OFF_CFG_LEN, V31_ROLL_OFF_CFG_OFF, + 0xFF}, + {V31_DEMUX_CFG, V31_DEMUX_LEN, V31_DEMUX_OFF, 0xFF}, + {V31_DEMOSAIC_0_CFG, V31_DEMOSAIC_0_LEN, V31_DEMOSAIC_0_OFF, + 0xFF}, + {V31_DEMOSAIC_1_CFG, V31_DEMOSAIC_1_LEN, V31_DEMOSAIC_1_OFF, + 0xFF}, + {V31_DEMOSAIC_2_CFG, V31_DEMOSAIC_2_LEN, V31_DEMOSAIC_2_OFF, + 0xFF}, +/*15*/ {V31_FOV_CFG, V31_FOV_LEN, V31_FOV_OFF, 0xFF}, + {V31_MAIN_SCALER_CFG, V31_MAIN_SCALER_LEN, V31_MAIN_SCALER_OFF, + 0xFF}, + {V31_WB_CFG, V31_WB_LEN, V31_WB_OFF, 0xFF}, + {V31_COLOR_COR_CFG, V31_COLOR_COR_LEN, V31_COLOR_COR_OFF, 0xFF}, + {V31_RGB_G_CFG, V31_RGB_G_LEN, V31_RGB_G_OFF, 0xFF}, +/*20*/ {V31_LA_CFG, V31_LA_LEN, V31_LA_OFF, 0xFF }, + {V31_CHROMA_EN_CFG, V31_CHROMA_EN_LEN, V31_CHROMA_EN_OFF, 0xFF}, + {V31_CHROMA_SUP_CFG, V31_CHROMA_SUP_LEN, V31_CHROMA_SUP_OFF, + 0xFF}, + {V31_MCE_CFG, V31_MCE_LEN, V31_MCE_OFF, 0xFF}, + {V31_SK_ENHAN_CFG}, +/*25*/ {V31_ASF_CFG, V31_ASF_LEN, V31_ASF_OFF, 0xFF}, + {V31_S2Y_CFG, V31_S2Y_LEN, V31_S2Y_OFF, 0xFF}, + {V31_S2CbCr_CFG, V31_S2CbCr_LEN, V31_S2CbCr_OFF, 0xFF}, + {V31_CHROMA_SUBS_CFG, V31_CHROMA_SUBS_LEN, V31_CHROMA_SUBS_OFF, + 0xFF}, + {V31_OUT_CLAMP_CFG, V31_OUT_CLAMP_LEN, V31_OUT_CLAMP_OFF, 0xFF}, +/*30*/ {V31_FRAME_SKIP_CFG, V31_FRAME_SKIP_LEN, V31_FRAME_SKIP_OFF, 0xFF}, + {V31_DUMMY_1}, + {V31_DUMMY_2}, + {V31_DUMMY_3}, + {V31_UPDATE}, +/*35*/ {V31_BL_LVL_UPDATE, V31_BLACK_LEVEL_LEN, V31_BLACK_LEVEL_OFF, + 0xFF}, + {V31_DEMUX_UPDATE, V31_DEMUX_LEN, V31_DEMUX_OFF, 0xFF}, + {V31_DEMOSAIC_1_UPDATE, V31_DEMOSAIC_1_LEN, V31_DEMOSAIC_1_OFF, + 0xFF}, + {V31_DEMOSAIC_2_UPDATE, V31_DEMOSAIC_2_LEN, V31_DEMOSAIC_2_OFF, + 0xFF}, + {V31_FOV_UPDATE, V31_FOV_LEN, V31_FOV_OFF, 0xFF}, +/*40*/ {V31_MAIN_SCALER_UPDATE, V31_MAIN_SCALER_LEN, V31_MAIN_SCALER_OFF, + 0xFF}, + {V31_WB_UPDATE, V31_WB_LEN, V31_WB_OFF, 0xFF}, + {V31_COLOR_COR_UPDATE, V31_COLOR_COR_LEN, V31_COLOR_COR_OFF, + 0xFF}, + {V31_RGB_G_UPDATE, V31_RGB_G_LEN, V31_CHROMA_EN_OFF, 0xFF}, + {V31_LA_UPDATE, V31_LA_LEN, V31_LA_OFF, 0xFF }, +/*45*/ {V31_CHROMA_EN_UPDATE, V31_CHROMA_EN_LEN, V31_CHROMA_EN_OFF, 0xFF}, + {V31_CHROMA_SUP_UPDATE, V31_CHROMA_SUP_LEN, V31_CHROMA_SUP_OFF, + 0xFF}, + {V31_MCE_UPDATE, V31_MCE_LEN, V31_MCE_OFF, 0xFF}, + {V31_SK_ENHAN_UPDATE}, + {V31_S2CbCr_UPDATE, V31_S2CbCr_LEN, V31_S2CbCr_OFF, 0xFF}, +/*50*/ {V31_S2Y_UPDATE, V31_S2Y_LEN, V31_S2Y_OFF, 0xFF}, + {V31_ASF_UPDATE, V31_ASF_UPDATE_LEN, V31_ASF_OFF, 0xFF}, + {V31_FRAME_SKIP_UPDATE}, + {V31_CAMIF_FRAME_UPDATE}, + {V31_STATS_AF_UPDATE, V31_STATS_AF_LEN, V31_STATS_AF_OFF}, +/*55*/ {V31_STATS_AE_UPDATE, V31_STATS_AE_LEN, V31_STATS_AE_OFF}, + {V31_STATS_AWB_UPDATE, V31_STATS_AWB_LEN, V31_STATS_AWB_OFF}, + {V31_STATS_RS_UPDATE, V31_STATS_RS_LEN, V31_STATS_RS_OFF}, + {V31_STATS_CS_UPDATE, V31_STATS_CS_LEN, V31_STATS_CS_OFF}, + {V31_STATS_SKIN_UPDATE}, +/*60*/ {V31_STATS_IHIST_UPDATE, V31_STATS_IHIST_LEN, V31_STATS_IHIST_OFF}, + {V31_DUMMY_4}, + {V31_EPOCH1_ACK}, + {V31_EPOCH2_ACK}, + {V31_START_RECORDING}, +/*65*/ {V31_STOP_RECORDING}, + {V31_DUMMY_5}, + {V31_DUMMY_6}, + {V31_CAPTURE, V31_CAPTURE_LEN, 0xFF}, + {V31_DUMMY_7}, +/*70*/ {V31_STOP}, + {V31_GET_HW_VERSION}, + {V31_GET_FRAME_SKIP_COUNTS}, + {V31_OUTPUT1_BUFFER_ENQ}, + {V31_OUTPUT2_BUFFER_ENQ}, +/*75*/ {V31_OUTPUT3_BUFFER_ENQ}, + {V31_JPEG_OUT_BUF_ENQ}, + {V31_RAW_OUT_BUF_ENQ}, + {V31_RAW_IN_BUF_ENQ}, + {V31_STATS_AF_ENQ}, +/*80*/ {V31_STATS_AE_ENQ}, + {V31_STATS_AWB_ENQ}, + {V31_STATS_RS_ENQ}, + {V31_STATS_CS_ENQ}, + {V31_STATS_SKIN_ENQ}, +/*85*/ {V31_STATS_IHIST_ENQ}, + {V31_DUMMY_8}, + {V31_JPEG_ENC_CFG}, + {V31_DUMMY_9}, + {V31_STATS_AF_START, V31_STATS_AF_LEN, V31_STATS_AF_OFF}, +/*90*/ {V31_STATS_AF_STOP}, + {V31_STATS_AE_START, V31_STATS_AE_LEN, V31_STATS_AE_OFF}, + {V31_STATS_AE_STOP}, + {V31_STATS_AWB_START, V31_STATS_AWB_LEN, V31_STATS_AWB_OFF}, + {V31_STATS_AWB_STOP}, +/*95*/ {V31_STATS_RS_START, V31_STATS_RS_LEN, V31_STATS_RS_OFF}, + {V31_STATS_RS_STOP}, + {V31_STATS_CS_START, V31_STATS_CS_LEN, V31_STATS_CS_OFF}, + {V31_STATS_CS_STOP}, + {V31_STATS_SKIN_START, V31_STATS_IHIST_LEN, + V31_STATS_IHIST_OFF}, +/*100*/ {V31_STATS_SKIN_STOP}, + {V31_STATS_IHIST_START}, + {V31_STATS_IHIST_STOP}, + {V31_DUMMY_10}, + {V31_SYNC_TIMER_SETTING}, +/*105*/ {V31_ASYNC_TIMER_SETTING}, +}; + +static void vfe_io_w(u32 data, unsigned long offset) +{ + writel(data, vfe31_ctrl->vfebase + offset); + wmb(); +} + +static u32 vfe_io_r(unsigned long offset) +{ + uint32_t data = readl(vfe31_ctrl->vfebase + offset); + rmb(); + return data; +} + +static void msm_io_memcpy(void __iomem *dest_addr, + void __iomem *src_addr, u32 len) +{ + int i; + u32 *d = (u32 *) dest_addr; + u32 *s = (u32 *) src_addr; + /* memcpy_toio does not work. Use writel for now */ + for (i = 0; i < len / 4; i++) { + writel(*s++, d++); + wmb(); + } +} + +static void vfe_io_w_axi_out_chan(u32 data, int8_t chan) +{ + uint32_t temp; + vfe_io_w(data, V31_AXI_OUT_OFF + 20 + 24 * chan); + temp = vfe_io_r(V31_AXI_OUT_OFF + 20 + 24 * chan); +} + +static void vfe_msg_get_phy_addr(struct msm_vfe_phy_info *pinfo, + struct vfe_message *data) +{ + pinfo->y_phy = data->_u.msgOut.yBuffer; + pinfo->cbcr_phy = data->_u.msgOut.cbcrBuffer; +} + +static void vfe31_proc_ops(enum VFE31_MESSAGE_ID id, void *msg, size_t len) +{ + struct msm_vfe_resp *rp; + struct vfe_message *vfe_msg_data = NULL; + + rp = vfe31_ctrl->resp->vfe_alloc(sizeof(struct msm_vfe_resp), + vfe31_ctrl->syncdata, GFP_ATOMIC); + if (!rp) { + pr_err("rp: cannot allocate buffer\n"); + return; + } + CDBG("vfe31_proc_ops, msgId = %d\n", id); + rp->evt_msg.type = MSM_CAMERA_MSG; + rp->evt_msg.msg_id = id; + rp->evt_msg.len = len; + rp->evt_msg.data = msg; + vfe_msg_data = (struct vfe_message *)rp->evt_msg.data; + + switch (rp->evt_msg.msg_id) { + case MSG_ID_SNAPSHOT_DONE: + rp->type = VFE_MSG_SNAPSHOT; + break; + + case MSG_ID_OUTPUT_P: + rp->type = VFE_MSG_OUTPUT_P; + rp->phy.output_id = OUTPUT_TYPE_P; + vfe_msg_get_phy_addr(&rp->phy, vfe_msg_data); + break; + + case MSG_ID_OUTPUT_T: + rp->type = VFE_MSG_OUTPUT_T; + rp->phy.output_id = OUTPUT_TYPE_T; + vfe_msg_get_phy_addr(&rp->phy, vfe_msg_data); + break; + + case MSG_ID_OUTPUT_S: + rp->type = VFE_MSG_OUTPUT_S; + rp->phy.output_id = OUTPUT_TYPE_S; + vfe_msg_get_phy_addr(&rp->phy, vfe_msg_data); + break; + + case MSG_ID_OUTPUT_V: + rp->type = VFE_MSG_OUTPUT_V; + rp->phy.output_id = OUTPUT_TYPE_V; + vfe_msg_get_phy_addr(&rp->phy, vfe_msg_data); + break; + + case MSG_ID_STATS_AF: + rp->type = VFE_MSG_STATS_AF; + rp->phy.sbuf_phy = vfe_msg_data->_u.msgStats.buffer; + break; + + case MSG_ID_STATS_AWB: + rp->type = VFE_MSG_STATS_AWB; + rp->phy.sbuf_phy = vfe_msg_data->_u.msgStats.buffer; + break; + + case MSG_ID_STATS_AEC: + rp->type = VFE_MSG_STATS_AEC; + rp->phy.sbuf_phy = vfe_msg_data->_u.msgStats.buffer; + break; + + default: + rp->type = VFE_MSG_GENERAL; + break; + } + vfe31_ctrl->resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, vfe31_ctrl->syncdata, + GFP_ATOMIC); +} + +static void vfe_send_outmsg(uint8_t msgid, uint32_t pyaddr, uint32_t pcbcraddr) +{ + struct vfe_message msg; + msg._d = msgid; + msg._u.msgOut.yBuffer = pyaddr; + msg._u.msgOut.cbcrBuffer = pcbcraddr; + vfe31_proc_ops(msgid, &msg, sizeof(struct vfe_message)); +} + +static int vfe31_enable(struct camera_enable_cmd *enable) +{ + return 0; +} + +void vfe_stop(void) +{ + uint8_t axiBusyFlag = true; + unsigned long flags; + + /* for reset hw modules, and send msg when reset_irq comes. */ + spin_lock_irqsave(&vfe31_ctrl->stop_flag_lock, flags); + vfe31_ctrl->stop_ack_pending = TRUE; + spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags); + + /* disable all interrupts. */ + vfe_io_w(VFE_DISABLE_ALL_IRQS, VFE_IRQ_MASK_0); + vfe_io_w(VFE_DISABLE_ALL_IRQS, VFE_IRQ_MASK_1); + + /* clear all pending interrupts*/ + vfe_io_w(VFE_CLEAR_ALL_IRQS, VFE_IRQ_CLEAR_0); + vfe_io_w(VFE_CLEAR_ALL_IRQS, VFE_IRQ_CLEAR_1); + vfe_io_w(1, VFE_IRQ_CMD); + + /* in either continuous or snapshot mode, stop command can be issued + * at any time. stop camif immediately. */ + vfe_io_w(CAMIF_COMMAND_STOP_IMMEDIATELY, VFE_CAMIF_COMMAND); + + /* axi halt command. */ + vfe_io_w(AXI_HALT, VFE_AXI_CMD); + + while (axiBusyFlag) { + if (vfe_io_r(VFE_AXI_STATUS) & 0x1) { + axiBusyFlag = false; + } + } + vfe_io_w(AXI_HALT_CLEAR, VFE_AXI_CMD); + + /* after axi halt, then ok to apply global reset. */ + /* enable reset_ack and async timer interrupt only while + stopping the pipeline.*/ + vfe_io_w(0xf0000000, VFE_IRQ_MASK_0); + vfe_io_w(VFE_IMASK_WHILE_STOPPING_1, VFE_IRQ_MASK_1); + vfe_io_w(VFE_RESET_UPON_STOP_CMD, VFE_GLOBAL_RESET); +} + +static int vfe31_disable(struct camera_enable_cmd *enable, + struct platform_device *dev) +{ + vfe_stop(); + msm_camio_disable(dev); + return 0; +} + +static void vfe31_release(struct platform_device *pdev) +{ + struct msm_sensor_ctrl *sctrl = + &((struct msm_sync *)vfe_syncdata)->sctrl; + struct resource *vfemem, *vfeio; + + if (sctrl) + sctrl->s_release(); + + vfemem = vfe31_ctrl->vfemem; + vfeio = vfe31_ctrl->vfeio; + + kfree(vfe31_ctrl->extdata); + free_irq(vfe31_ctrl->vfeirq, 0); + iounmap(vfe31_ctrl->vfebase); + kfree(vfe31_ctrl); + vfe31_ctrl = NULL; + release_mem_region(vfemem->start, (vfemem->end - vfemem->start) + 1); + msm_camio_disable(pdev); + vfe_syncdata = NULL; +} + +static int vfe31_config_axi(int mode, struct axidata *ad, uint32_t *ao) +{ + int i; + uint32_t *p, *p1, *p2; + struct vfe31_output_ch *outp1 = NULL, *outp2 = NULL; + struct msm_pmem_region *regp1 = NULL, *regp2 = NULL; + + p = ao + 2; + + CDBG("vfe31_config_axi: mode = %d, bufnum1 = %d, bufnum2 = %d\n", + mode, ad->bufnum1, ad->bufnum2); + + switch (mode) { + case OUTPUT_2: + if (ad->bufnum2 != 3) + return -EINVAL; + *p = 0x200; /* preview with wm0 & wm1 */ + + vfe31_ctrl->outpath.out0.ch0 = 0; /* luma */ + vfe31_ctrl->outpath.out0.ch1 = 1; /* chroma */ + regp1 = &(ad->region[ad->bufnum1]); + outp1 = &(vfe31_ctrl->outpath.out0); + vfe31_ctrl->outpath.output_mode |= VFE31_OUTPUT_MODE_PT; + + for (i = 0; i < 2; i++) { + p1 = ao + 6 + i; /* wm0 for y */ + *p1 = (regp1->paddr + regp1->info.y_off); + + p1 = ao + 12 + i; /* wm1 for cbcr */ + *p1 = (regp1->paddr + regp1->info.cbcr_off); + regp1++; + } + outp1->free_buf.available = 1; + outp1->free_buf.paddr = regp1->paddr; + outp1->free_buf.y_off = regp1->info.y_off; + outp1->free_buf.cbcr_off = regp1->info.cbcr_off; + + CDBG("vfe31_config_axi: free_buf paddr = 0x%x, y_off = %d," + " cbcr_off = %d\n", + outp1->free_buf.paddr, outp1->free_buf.y_off, + outp1->free_buf.cbcr_off); + break; + + case OUTPUT_1_AND_2: + /* use wm0& 4 for thumbnail, wm1&5 for main image.*/ + if ((ad->bufnum1 < 1) || (ad->bufnum2 < 1)) + return -EINVAL; + /* at least one frame for snapshot. */ + *p++ = 0x1; /* xbar cfg0 */ + *p = 0x203; /* xbar cfg1 */ + vfe31_ctrl->outpath.out0.ch0 = 0; /* thumbnail luma */ + vfe31_ctrl->outpath.out0.ch1 = 4; /* thumbnail chroma */ + vfe31_ctrl->outpath.out1.ch0 = 1; /* main image luma */ + vfe31_ctrl->outpath.out1.ch1 = 5; /* main image chroma */ + vfe31_ctrl->outpath.output_mode |= + VFE31_OUTPUT_MODE_S; /* main image.*/ + vfe31_ctrl->outpath.output_mode |= + VFE31_OUTPUT_MODE_PT; /* thumbnail. */ + + regp1 = &(ad->region[0]); /* this is thumbnail buffer. */ + /* this is main image buffer. */ + regp2 = &(ad->region[ad->bufnum1]); + outp1 = &(vfe31_ctrl->outpath.out0); + outp2 = &(vfe31_ctrl->outpath.out1); /* snapshot */ + + p1 = ao + 6; /* wm0 ping */ + *p1++ = (regp1->paddr + regp1->info.y_off); + /* this is to duplicate ping address to pong.*/ + *p1 = (regp1->paddr + regp1->info.y_off); + p1 = ao + 30; /* wm4 ping */ + *p1++ = (regp1->paddr + regp1->info.cbcr_off); + /* this is to duplicate ping address to pong.*/ + *p1 = (regp1->paddr + regp1->info.cbcr_off); + p1 = ao + 12; /* wm1 ping */ + *p1++ = (regp2->paddr + regp2->info.y_off); + /* pong = ping,*/ + *p1 = (regp2->paddr + regp2->info.y_off); + p1 = ao + 36; /* wm5 */ + *p1++ = (regp2->paddr + regp2->info.cbcr_off); + *p1 = (regp2->paddr + regp2->info.cbcr_off); + break; + + case OUTPUT_1_AND_3: + /* use wm0& 4 for preview, wm1&5 for video.*/ + if ((ad->bufnum1 < 2) || (ad->bufnum2 < 2)) + return -EINVAL; + *p++ = 0x1; /* xbar cfg0 */ + *p = 0x1a03; /* xbar cfg1 */ + vfe31_ctrl->outpath.out0.ch0 = 0; /* preview luma */ + vfe31_ctrl->outpath.out0.ch1 = 4; /* preview chroma */ + vfe31_ctrl->outpath.out2.ch0 = 1; /* video luma */ + vfe31_ctrl->outpath.out2.ch1 = 5; /* video chroma */ + vfe31_ctrl->outpath.output_mode |= + VFE31_OUTPUT_MODE_V; /* video*/ + vfe31_ctrl->outpath.output_mode |= + VFE31_OUTPUT_MODE_PT; /* preview */ + + regp1 = &(ad->region[0]); /* this is preview buffer. */ + regp2 = &(ad->region[ad->bufnum1]);/* this is video buffer. */ + outp1 = &(vfe31_ctrl->outpath.out0); /* preview */ + outp2 = &(vfe31_ctrl->outpath.out2); /* video */ + + + for (i = 0; i < 2; i++) { + p1 = ao + 6 + i; /* wm0 for y */ + *p1 = (regp1->paddr + regp1->info.y_off); + + p1 = ao + 30 + i; /* wm1 for cbcr */ + *p1 = (regp1->paddr + regp1->info.cbcr_off); + regp1++; + } + + for (i = 0; i < 2; i++) { + p2 = ao + 12 + i; /* wm0 for y */ + *p2 = (regp2->paddr + regp2->info.y_off); + + p2 = ao + 36 + i; /* wm1 for cbcr */ + *p2 = (regp2->paddr + regp2->info.cbcr_off); + regp2++; + } + outp1->free_buf.available = 1; + outp1->free_buf.paddr = regp1->paddr; + outp1->free_buf.y_off = regp1->info.y_off; + outp1->free_buf.cbcr_off = regp1->info.cbcr_off; + + outp2->free_buf.available = 1; + outp2->free_buf.paddr = regp2->paddr; + outp2->free_buf.y_off = regp2->info.y_off; + outp2->free_buf.cbcr_off = regp2->info.cbcr_off; + CDBG("vfe31_config_axi: preview free_buf" + "paddr = 0x%x, y_off = %d, cbcr_off = %d\n", + outp1->free_buf.paddr, outp1->free_buf.y_off, + outp1->free_buf.cbcr_off); + CDBG("vfe31_config_axi: video free_buf" + "paddr = 0x%x,y_off = %d, cbcr_off = %d\n", + outp2->free_buf.paddr, outp2->free_buf.y_off, + outp2->free_buf.cbcr_off); + break; + + case CAMIF_TO_AXI_VIA_OUTPUT_2: /* use wm0 only */ + if (ad->bufnum2 < 1) + return -EINVAL; + CDBG("config axi for raw snapshot.\n"); + *p = 0x60; /* raw snapshot with wm0 */ + vfe31_ctrl->outpath.out1.ch0 = 0; /* raw */ + regp1 = &(ad->region[ad->bufnum1]); + vfe31_ctrl->outpath.output_mode |= VFE31_OUTPUT_MODE_S; + p1 = ao + 6; /* wm0 for y */ + *p1 = (regp1->paddr + regp1->info.y_off); + break; + + default: + break; + } + msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[V31_AXI_OUT_CFG].offset, + ao, vfe31_cmd[V31_AXI_OUT_CFG].length); + return 0; +} + +static void vfe31_reset_internal_variables(void) +{ + unsigned long flags; + + vfe31_ctrl->vfeImaskCompositePacked = 0; + /* state control variables */ + vfe31_ctrl->start_ack_pending = FALSE; + + spin_lock_irqsave(&vfe31_ctrl->stop_flag_lock, flags); + vfe31_ctrl->stop_ack_pending = FALSE; + spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags); + + vfe31_ctrl->reset_ack_pending = FALSE; + + spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags); + vfe31_ctrl->update_ack_pending = FALSE; + spin_unlock_irqrestore(&vfe31_ctrl->update_ack_lock, flags); + + vfe31_ctrl->req_stop_video_rec = FALSE; + vfe31_ctrl->req_start_video_rec = FALSE; + + spin_lock_irqsave(&vfe31_ctrl->state_lock, flags); + vfe31_ctrl->vstate = VFE_STATE_IDLE; + spin_unlock_irqrestore(&vfe31_ctrl->state_lock, flags); + + /* 0 for continuous mode, 1 for snapshot mode */ + vfe31_ctrl->operation_mode = 0; + vfe31_ctrl->outpath.output_mode = 0; + vfe31_ctrl->vfe_capture_count = 0; + + /* this is unsigned 32 bit integer. */ + vfe31_ctrl->vfeFrameId = 0; + vfe31_ctrl->output1Pattern = 0xffffffff; + vfe31_ctrl->output1Period = 31; + vfe31_ctrl->output2Pattern = 0xffffffff; + vfe31_ctrl->output2Period = 31; + vfe31_ctrl->vfeFrameSkipCount = 0; + vfe31_ctrl->vfeFrameSkipPeriod = 31; + + /* Stats control variables. */ + memset(&vfe31_ctrl->afStatsControl, 0, + sizeof(struct vfe_stats_control)); + memset(&vfe31_ctrl->awbStatsControl, 0, + sizeof(struct vfe_stats_control)); + memset(&vfe31_ctrl->aecStatsControl, 0, + sizeof(struct vfe_stats_control)); + memset(&vfe31_ctrl->ihistStatsControl, 0, + sizeof(struct vfe_stats_control)); + memset(&vfe31_ctrl->rsStatsControl, 0, + sizeof(struct vfe_stats_control)); + memset(&vfe31_ctrl->csStatsControl, 0, + sizeof(struct vfe_stats_control)); +} + +static void vfe31_reset(void) +{ + uint32_t vfe_version; + + vfe31_reset_internal_variables(); + vfe_version = vfe_io_r(VFE_VERSION); + CDBG("vfe_version = 0x%x\n", vfe_version); + /* disable all interrupts. vfeImaskLocal is also reset to 0 + * to begin with. */ + vfe_io_w(VFE_DISABLE_ALL_IRQS, VFE_IRQ_MASK_0); + vfe_io_w(VFE_DISABLE_ALL_IRQS, VFE_IRQ_MASK_1); + + /* clear all pending interrupts*/ + vfe_io_w(VFE_CLEAR_ALL_IRQS, VFE_IRQ_CLEAR_0); + vfe_io_w(VFE_CLEAR_ALL_IRQS, VFE_IRQ_CLEAR_1); + vfe_io_w(1, VFE_IRQ_CMD); + + /* enable reset_ack interrupt. */ + vfe_io_w(VFE_IMASK_WHILE_STOPPING_1, VFE_IRQ_MASK_1); + + /* Write to VFE_GLOBAL_RESET_CMD to reset the vfe hardware. Once reset + * is done, hardware interrupt will be generated. VFE ist processes + * the interrupt to complete the function call. Note that the reset + * function is synchronous. */ + vfe_io_w(VFE_RESET_UPON_RESET_CMD, VFE_GLOBAL_RESET); +} + +static int vfe31_operation_config(uint32_t *cmd) +{ + uint32_t *p = cmd; + + vfe31_ctrl->operation_mode = *p; + vfe31_ctrl->stats_comp = *(++p); + + vfe_io_w(*(++p), VFE_CFG_OFF); + vfe_io_w(*(++p), VFE_MODULE_CFG); + vfe_io_w(*(++p), VFE_REALIGN_BUF); + vfe_io_w(*(++p), VFE_CHROMA_UP); + vfe_io_w(*(++p), VFE_STATS_CFG); + return 0; +} + +static uint32_t vfe_stats_awb_buf_init(struct vfe_cmd_stats_buf *in) +{ + uint32_t *ptr = in->statsBuf; + uint32_t addr; + + addr = ptr[0]; + vfe_io_w(addr, VFE_BUS_STATS_AWB_WR_PING_ADDR); + addr = ptr[1]; + vfe_io_w(addr, VFE_BUS_STATS_AWB_WR_PONG_ADDR); + vfe31_ctrl->awbStatsControl.nextFrameAddrBuf = in->statsBuf[2]; + return 0; +} + +static uint32_t vfe_stats_aec_buf_init(struct vfe_cmd_stats_buf *in) +{ + uint32_t *ptr = in->statsBuf; + uint32_t addr; + + addr = ptr[0]; + vfe_io_w(addr, VFE_BUS_STATS_AEC_WR_PING_ADDR); + addr = ptr[1]; + vfe_io_w(addr, VFE_BUS_STATS_AEC_WR_PONG_ADDR); + vfe31_ctrl->aecStatsControl.nextFrameAddrBuf = in->statsBuf[2]; + return 0; +} + +static uint32_t vfe_stats_af_buf_init(struct vfe_cmd_stats_buf *in) +{ + uint32_t *ptr = in->statsBuf; + uint32_t addr; + + addr = ptr[0]; + vfe_io_w(addr, VFE_BUS_STATS_AF_WR_PING_ADDR); + addr = ptr[1]; + vfe_io_w(addr, VFE_BUS_STATS_AF_WR_PONG_ADDR); + vfe31_ctrl->afStatsControl.nextFrameAddrBuf = in->statsBuf[2]; + return 0; +} + +static uint32_t vfe_stats_ihist_buf_init(struct vfe_cmd_stats_buf *in) +{ + uint32_t *ptr = in->statsBuf; + uint32_t addr; + + addr = ptr[0]; + vfe_io_w(addr, VFE_BUS_STATS_HIST_WR_PING_ADDR); + addr = ptr[1]; + vfe_io_w(addr, VFE_BUS_STATS_HIST_WR_PONG_ADDR); + vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf = in->statsBuf[2]; + return 0; +} + +static uint32_t vfe_stats_rs_buf_init(struct vfe_cmd_stats_buf *in) +{ + uint32_t *ptr = in->statsBuf; + uint32_t addr; + + addr = ptr[0]; + vfe_io_w(addr, VFE_BUS_STATS_RS_WR_PING_ADDR); + addr = ptr[1]; + vfe_io_w(addr, VFE_BUS_STATS_RS_WR_PONG_ADDR); + vfe31_ctrl->rsStatsControl.nextFrameAddrBuf = in->statsBuf[2]; + return 0; +} + +static uint32_t vfe_stats_cs_buf_init(struct vfe_cmd_stats_buf *in) +{ + uint32_t *ptr = in->statsBuf; + uint32_t addr; + + addr = ptr[0]; + vfe_io_w(addr, VFE_BUS_STATS_CS_WR_PING_ADDR); + addr = ptr[1]; + vfe_io_w(addr, VFE_BUS_STATS_CS_WR_PONG_ADDR); + vfe31_ctrl->csStatsControl.nextFrameAddrBuf = in->statsBuf[2]; + return 0; +} + +static void vfe31_start_common(void) +{ + unsigned long flags; + + vfe31_ctrl->start_ack_pending = TRUE; + CDBG("VFE opertaion mode = 0x%x.\n", vfe31_ctrl->operation_mode); + CDBG("VFE output path out mode = 0x%x.\n", + vfe31_ctrl->outpath.output_mode); + vfe_io_w(0x00EFE021, VFE_IRQ_MASK_0); + vfe_io_w(VFE_IMASK_WHILE_STOPPING_1, VFE_IRQ_MASK_1); + + vfe_io_w(1, VFE_REG_UPDATE_CMD); + vfe_io_w(1, VFE_CAMIF_COMMAND); + + spin_lock_irqsave(&vfe31_ctrl->state_lock, flags); + vfe31_ctrl->vstate = VFE_STATE_ACTIVE; + spin_unlock_irqrestore(&vfe31_ctrl->state_lock, flags); +} + +static int vfe31_start_recording(void) +{ + vfe31_ctrl->req_start_video_rec = TRUE; + return 0; +} + +static int vfe31_stop_recording(void) +{ + vfe31_ctrl->req_stop_video_rec = TRUE; + return 0; +} + +static int vfe31_capture(uint32_t num_frames_capture) +{ + uint32_t irq_comp_mask = 0; + + /* capture command is valid for both idle and active state. */ + vfe31_ctrl->outpath.out1.capture_cnt = num_frames_capture; + if (vfe31_ctrl->operation_mode == 1) { + vfe31_ctrl->outpath.out0.capture_cnt = num_frames_capture; + } + vfe31_ctrl->vfe_capture_count = num_frames_capture; + irq_comp_mask = vfe_io_r(VFE_IRQ_COMP_MASK); + + if (vfe31_ctrl->operation_mode == 1) { + if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) { + irq_comp_mask |= (0x1 << vfe31_ctrl->outpath.out0.ch0 | + 0x1 << vfe31_ctrl->outpath.out0.ch1); + } + if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) { + irq_comp_mask |= + (0x1 << (vfe31_ctrl->outpath.out1.ch0 + 8) | + 0x1 << (vfe31_ctrl->outpath.out1.ch1 + 8)); + } + if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) { + vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out0.ch0); + vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out0.ch1); + } + if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) { + vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out1.ch0); + vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out1.ch1); + } + } else { /* this is raw snapshot mode. */ + CDBG("config the comp imask for raw snapshot mode.\n"); + if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) { + irq_comp_mask |= + (0x1 << (vfe31_ctrl->outpath.out1.ch0 + 8)); + vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out1.ch0); + } + } + vfe_io_w(irq_comp_mask, VFE_IRQ_COMP_MASK); + vfe_io_r(VFE_IRQ_COMP_MASK); + vfe31_start_common(); + vfe_io_r(VFE_IRQ_COMP_MASK); + /* for debug */ + vfe_io_w(1, 0x18C); + vfe_io_w(1, 0x188); + return 0; +} + +static int vfe31_start(void) +{ + uint32_t irq_comp_mask = 0; + + /* start command now is only good for continuous mode. */ + if (vfe31_ctrl->operation_mode & 1) + return 0; + irq_comp_mask = vfe_io_r(VFE_IRQ_COMP_MASK); + + if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) { + irq_comp_mask |= (0x1 << vfe31_ctrl->outpath.out0.ch0 | + 0x1 << vfe31_ctrl->outpath.out0.ch1); + } + + if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_V) { + irq_comp_mask |= (0x1 << (vfe31_ctrl->outpath.out2.ch0 + 16) | + 0x1 << (vfe31_ctrl->outpath.out2.ch1 + 16)); + } + + vfe_io_w(irq_comp_mask, VFE_IRQ_COMP_MASK); + + if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) { + vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out0.ch0); + vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out0.ch1); + } + vfe31_start_common(); + return 0; +} + +static void vfe31_update(void) +{ + unsigned long flags; + + spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags); + vfe31_ctrl->update_ack_pending = TRUE; + spin_unlock_irqrestore(&vfe31_ctrl->update_ack_lock, flags); + vfe_io_w(1, VFE_REG_UPDATE_CMD); +} + +void vfe31_program_dmi_cfg(enum VFE31_DMI_RAM_SEL bankSel) +{ + /* set bit 8 for auto increment. */ + uint32_t value = VFE_DMI_CFG_DEFAULT; + value += (uint32_t)bankSel; + + vfe_io_w(value, VFE_DMI_CFG); + /* by default, always starts with offset 0. */ + vfe_io_w(0, VFE_DMI_ADDR); +} + +void vfe31_write_gamma_cfg(enum VFE31_DMI_RAM_SEL channel_sel, + const uint32_t *tbl) +{ + int i; + uint32_t value, value1, value2; + + vfe31_program_dmi_cfg(channel_sel); + /* for loop for extracting init table. */ + for (i = 0 ; i < (VFE31_GAMMA_NUM_ENTRIES/2) ; i++) { + value = *tbl++; + value1 = value & 0x0000FFFF; + value2 = (value & 0xFFFF0000)>>16; + vfe_io_w(value1, VFE_DMI_DATA_LO); + vfe_io_w(value2, VFE_DMI_DATA_LO); + } + vfe31_program_dmi_cfg(NO_MEM_SELECTED); +} + +static int vfe31_proc_general(struct msm_vfe31_cmd *cmd) +{ + int i, rc = 0; + uint32_t old_val = 0, new_val = 0; + uint32_t *cmdp = NULL; + uint32_t *cmdp_local = NULL; + + CDBG("vfe31_proc_general: cmdID = %d, length = %d\n", + cmd->id, cmd->length); + switch (cmd->id) { + case V31_RESET: + vfe31_reset(); + break; + case V31_START: + rc = vfe31_start(); + break; + case V31_UPDATE: + vfe31_update(); + break; + case V31_CAPTURE: + rc = vfe31_capture(1); + break; + case V31_START_RECORDING: + rc = vfe31_start_recording(); + break; + case V31_STOP_RECORDING: + rc = vfe31_stop_recording(); + break; + case V31_OPERATION_CFG: + if (cmd->length != V31_OPERATION_CFG_LEN) { + rc = -EINVAL; + goto proc_general_done; + } + cmdp = kmalloc(V31_OPERATION_CFG_LEN, GFP_KERNEL); + if (copy_from_user(cmdp, (void __user *)cmd->value, + V31_OPERATION_CFG_LEN)) { + rc = -EFAULT; + goto proc_general_done; + } + rc = vfe31_operation_config(cmdp); + break; + + case V31_STATS_AE_START: + cmdp = kmalloc(cmd->length, GFP_KERNEL); + if (!cmdp) { + rc = -ENOMEM; + goto proc_general_done; + } + if (copy_from_user(cmdp, (void __user *)cmd->value, + cmd->length)) { + rc = -EFAULT; + goto proc_general_done; + } + old_val = vfe_io_r(VFE_MODULE_CFG); + old_val |= AE_ENABLE_MASK; + vfe_io_w(old_val, VFE_MODULE_CFG); + msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, + cmdp, vfe31_cmd[cmd->id].length); + break; + + case V31_STATS_AF_START: + cmdp = kmalloc(cmd->length, GFP_KERNEL); + if (!cmdp) { + rc = -ENOMEM; + goto proc_general_done; + } + if (copy_from_user(cmdp, (void __user *)cmd->value, + cmd->length)) { + rc = -EFAULT; + goto proc_general_done; + } + old_val = vfe_io_r(VFE_MODULE_CFG); + old_val |= AF_ENABLE_MASK; + vfe_io_w(old_val, VFE_MODULE_CFG); + msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, + cmdp, vfe31_cmd[cmd->id].length); + break; + + case V31_STATS_AWB_START: + cmdp = kmalloc(cmd->length, GFP_KERNEL); + if (!cmdp) { + rc = -ENOMEM; + goto proc_general_done; + } + if (copy_from_user(cmdp, (void __user *)cmd->value, + cmd->length)) { + rc = -EFAULT; + goto proc_general_done; + } + old_val = vfe_io_r(VFE_MODULE_CFG); + old_val |= AWB_ENABLE_MASK; + vfe_io_w(old_val, VFE_MODULE_CFG); + msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, + cmdp, vfe31_cmd[cmd->id].length); + break; + + case V31_STATS_IHIST_START: + cmdp = kmalloc(cmd->length, GFP_KERNEL); + if (!cmdp) { + rc = -ENOMEM; + goto proc_general_done; + } + if (copy_from_user(cmdp, (void __user *)cmd->value, + cmd->length)) { + rc = -EFAULT; + goto proc_general_done; + } + old_val = vfe_io_r(VFE_MODULE_CFG); + old_val |= IHIST_ENABLE_MASK; + vfe_io_w(old_val, VFE_MODULE_CFG); + msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, + cmdp, vfe31_cmd[cmd->id].length); + break; + + + case V31_STATS_RS_START: + cmdp = kmalloc(cmd->length, GFP_KERNEL); + if (!cmdp) { + rc = -ENOMEM; + goto proc_general_done; + } + if (copy_from_user(cmdp, (void __user *)cmd->value, + cmd->length)) { + rc = -EFAULT; + goto proc_general_done; + } + old_val = vfe_io_r(VFE_MODULE_CFG); + old_val |= RS_ENABLE_MASK; + vfe_io_w(old_val, VFE_MODULE_CFG); + msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, + cmdp, vfe31_cmd[cmd->id].length); + break; + + case V31_STATS_CS_START: + cmdp = kmalloc(cmd->length, GFP_KERNEL); + if (!cmdp) { + rc = -ENOMEM; + goto proc_general_done; + } + if (copy_from_user(cmdp, (void __user *)cmd->value, + cmd->length)) { + rc = -EFAULT; + goto proc_general_done; + } + old_val = vfe_io_r(VFE_MODULE_CFG); + old_val |= CS_ENABLE_MASK; + vfe_io_w(old_val, VFE_MODULE_CFG); + msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, + cmdp, vfe31_cmd[cmd->id].length); + break; + + case V31_MCE_UPDATE: + case V31_MCE_CFG: + cmdp = kmalloc(cmd->length, GFP_KERNEL); + if (!cmdp) { + rc = -ENOMEM; + goto proc_general_done; + } + /* Incrementing with 4 so as to point to the 2nd Register as + the 2nd register has the mce_enable bit */ + old_val = vfe_io_r(V31_CHROMA_EN_OFF + 4); + if (copy_from_user(cmdp, (void __user *)cmd->value, + cmd->length)) { + rc = -EFAULT; + goto proc_general_done; + } + cmdp_local = cmdp; + new_val = *cmdp_local; + old_val &= MCE_EN_MASK; + new_val = new_val | old_val; + msm_io_memcpy(vfe31_ctrl->vfebase + V31_CHROMA_EN_OFF + 4, + &new_val, 4); + cmdp_local += 1; + + old_val = vfe_io_r(V31_CHROMA_EN_OFF + 8); + new_val = *cmdp_local; + old_val &= MCE_Q_K_MASK; + new_val = new_val | old_val; + msm_io_memcpy(vfe31_ctrl->vfebase + V31_CHROMA_EN_OFF + 8, + &new_val, 4); + cmdp_local += 1; + msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, + cmdp_local, vfe31_cmd[cmd->id].length); + break; + + case V31_DEMOSAIC_2_UPDATE: /* 38 BPC update */ + case V31_DEMOSAIC_2_CFG: /* 14 BPC config */ + cmdp = kmalloc(cmd->length, GFP_KERNEL); + if (!cmdp) { + rc = -ENOMEM; + goto proc_general_done; + } + if (copy_from_user(cmdp, (void __user *)cmd->value, + cmd->length)) { + rc = -EFAULT; + goto proc_general_done; + } + cmdp_local = cmdp; + new_val = *cmdp_local; + + old_val = vfe_io_r(V31_DEMOSAIC_0_OFF); + old_val &= BPC_MASK; + + new_val = new_val | old_val; + *cmdp_local = new_val; + msm_io_memcpy(vfe31_ctrl->vfebase + V31_DEMOSAIC_0_OFF, + cmdp_local, 4); + cmdp_local += 1; + msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, + cmdp_local, vfe31_cmd[cmd->id].length); + break; + + case V31_DEMOSAIC_1_UPDATE:/* 37 ABF update */ + case V31_DEMOSAIC_1_CFG: /* 13 ABF config */ + cmdp = kmalloc(cmd->length, GFP_KERNEL); + if (!cmdp) { + rc = -ENOMEM; + goto proc_general_done; + } + if (copy_from_user(cmdp, (void __user *)cmd->value, + cmd->length)) { + rc = -EFAULT; + goto proc_general_done; + } + cmdp_local = cmdp; + new_val = *cmdp_local; + + old_val = vfe_io_r(V31_DEMOSAIC_0_OFF); + old_val &= ABF_MASK; + new_val = new_val | old_val; + *cmdp_local = new_val; + + msm_io_memcpy(vfe31_ctrl->vfebase + V31_DEMOSAIC_0_OFF, + cmdp_local, 4); + + cmdp_local += 1; + msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, + cmdp_local, vfe31_cmd[cmd->id].length); + break; + + case V31_ROLL_OFF_CFG: + cmdp = kmalloc(cmd->length, GFP_KERNEL); + if (!cmdp) { + rc = -ENOMEM; + goto proc_general_done; + } + if (copy_from_user(cmdp, (void __user *)cmd->value, + cmd->length)) { + rc = -EFAULT; + goto proc_general_done; + } + cmdp_local = cmdp; + msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, + cmdp_local, 16); + cmdp_local += 4; + vfe31_program_dmi_cfg(ROLLOFF_RAM); + /* for loop for extrcting init table. */ + for (i = 0 ; i < (VFE31_ROLL_OFF_INIT_TABLE_SIZE * 2) ; i++) { + vfe_io_w(*cmdp_local, VFE_DMI_DATA_LO); + cmdp_local++; + } + CDBG("done writing init table\n"); + /* by default, always starts with offset 0. */ + vfe_io_w(LENS_ROLL_OFF_DELTA_TABLE_OFFSET, VFE_DMI_ADDR); + /* for loop for extracting delta table. */ + for (i = 0 ; i < (VFE31_ROLL_OFF_DELTA_TABLE_SIZE * 2) ; i++) { + vfe_io_w(*cmdp_local, VFE_DMI_DATA_LO); + cmdp_local++; + } + vfe31_program_dmi_cfg(NO_MEM_SELECTED); + break; + + case V31_LA_CFG: + case V31_LA_UPDATE: + cmdp = kmalloc(cmd->length, GFP_KERNEL); + if (!cmdp) { + rc = -ENOMEM; + goto proc_general_done; + } + if (copy_from_user(cmdp, (void __user *)cmd->value, + cmd->length)) { + rc = -EFAULT; + goto proc_general_done; + } + msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, + cmdp, vfe31_cmd[cmd->id].length); + + /* If the value is 0x00 then write in to LUT bank0 + if the value is 0x01 then write in to LUT bank1 */ + if (*cmdp == 0x0) + vfe31_program_dmi_cfg(LUMA_ADAPT_LUT_RAM_BANK0); + else + vfe31_program_dmi_cfg(LUMA_ADAPT_LUT_RAM_BANK1); + cmdp += 1; + /* for loop for extracting init table. */ + for (i = 0 ; i < VFE31_LA_TABLE_LENGTH ; i++) { + vfe_io_w(*cmdp, VFE_DMI_DATA_LO); + cmdp++; + } + vfe31_program_dmi_cfg(NO_MEM_SELECTED); + cmdp -= 1; + break; + + case V31_RGB_G_CFG: + cmdp = kmalloc(cmd->length, GFP_KERNEL); + if (!cmdp) { + rc = -ENOMEM; + goto proc_general_done; + } + if (copy_from_user(cmdp, (void __user *)cmd->value, + cmd->length)) { + rc = -EFAULT; + goto proc_general_done; + } + msm_io_memcpy(vfe31_ctrl->vfebase + V31_RGB_G_OFF, cmdp, 4); + cmdp += 1; + vfe31_write_gamma_cfg(RGBLUT_RAM_CH0_BANK0 , cmdp); + vfe31_write_gamma_cfg(RGBLUT_RAM_CH1_BANK0 , cmdp); + vfe31_write_gamma_cfg(RGBLUT_RAM_CH2_BANK0 , cmdp); + cmdp -= 1; + break; + + case V31_RGB_G_UPDATE: + cmdp = kmalloc(cmd->length, GFP_KERNEL); + if (!cmdp) { + rc = -ENOMEM; + goto proc_general_done; + } + if (copy_from_user(cmdp, (void __user *)cmd->value, + cmd->length)) { + rc = -EFAULT; + goto proc_general_done; + } + + msm_io_memcpy(vfe31_ctrl->vfebase + V31_RGB_G_OFF, cmdp, 4); + old_val = *cmdp; + cmdp += 1; + + if (old_val) { + vfe31_write_gamma_cfg(RGBLUT_RAM_CH0_BANK1, cmdp); + vfe31_write_gamma_cfg(RGBLUT_RAM_CH1_BANK1, cmdp); + vfe31_write_gamma_cfg(RGBLUT_RAM_CH2_BANK1, cmdp); + } else { + vfe31_write_gamma_cfg(RGBLUT_RAM_CH0_BANK0, cmdp); + vfe31_write_gamma_cfg(RGBLUT_RAM_CH1_BANK0, cmdp); + vfe31_write_gamma_cfg(RGBLUT_RAM_CH2_BANK0, cmdp); + } + cmdp -= 1; + break; + + case V31_STATS_AWB_STOP: + old_val = vfe_io_r(VFE_MODULE_CFG); + old_val &= ~AWB_ENABLE_MASK; + vfe_io_w(old_val, VFE_MODULE_CFG); + break; + + case V31_STATS_AE_STOP: + old_val = vfe_io_r(VFE_MODULE_CFG); + old_val &= ~AE_ENABLE_MASK; + vfe_io_w(old_val, VFE_MODULE_CFG); + break; + + case V31_STATS_AF_STOP: + old_val = vfe_io_r(VFE_MODULE_CFG); + old_val &= ~AF_ENABLE_MASK; + vfe_io_w(old_val, VFE_MODULE_CFG); + break; + + case V31_STATS_IHIST_STOP: + old_val = vfe_io_r(VFE_MODULE_CFG); + old_val &= ~IHIST_ENABLE_MASK; + vfe_io_w(old_val, VFE_MODULE_CFG); + break; + + case V31_STATS_RS_STOP: + old_val = vfe_io_r(VFE_MODULE_CFG); + old_val &= ~RS_ENABLE_MASK; + vfe_io_w(old_val, VFE_MODULE_CFG); + break; + + case V31_STATS_CS_STOP: + old_val = vfe_io_r(VFE_MODULE_CFG); + old_val &= ~CS_ENABLE_MASK; + vfe_io_w(old_val, VFE_MODULE_CFG); + break; + + case V31_STOP: + vfe_stop(); + break; + + default: + if (cmd->length != vfe31_cmd[cmd->id].length) + return -EINVAL; + + cmdp = kmalloc(vfe31_cmd[cmd->id].length, GFP_KERNEL); + if (!cmdp) { + rc = -ENOMEM; + goto proc_general_done; + } + + CHECKED_COPY_FROM_USER(cmdp); + msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, + cmdp, vfe31_cmd[cmd->id].length); + break; + } + +proc_general_done: + kfree(cmdp); + + return rc; +} + +static void vfe31_stats_af_ack(struct vfe_cmd_stats_ack *pAck) +{ + unsigned long flags; + spin_lock_irqsave(&vfe31_ctrl->af_ack_lock, flags); + vfe31_ctrl->afStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf; + vfe31_ctrl->afStatsControl.ackPending = FALSE; + spin_unlock_irqrestore(&vfe31_ctrl->af_ack_lock, flags); +} + +static void vfe31_stats_awb_ack(struct vfe_cmd_stats_ack *pAck) +{ + unsigned long flags; + spin_lock_irqsave(&vfe31_ctrl->awb_ack_lock, flags); + vfe31_ctrl->awbStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf; + vfe31_ctrl->awbStatsControl.ackPending = FALSE; + spin_unlock_irqrestore(&vfe31_ctrl->awb_ack_lock, flags); +} + +static void vfe31_stats_aec_ack(struct vfe_cmd_stats_ack *pAck) +{ + unsigned long flags; + spin_lock_irqsave(&vfe31_ctrl->aec_ack_lock, flags); + vfe31_ctrl->aecStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf; + vfe31_ctrl->aecStatsControl.ackPending = FALSE; + spin_unlock_irqrestore(&vfe31_ctrl->aec_ack_lock, flags); +} + +static void vfe31_stats_ihist_ack(struct vfe_cmd_stats_ack *pAck) +{ + unsigned long flags; + spin_lock_irqsave(&vfe31_ctrl->ihist_ack_lock, flags); + vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf; + vfe31_ctrl->ihistStatsControl.ackPending = FALSE; + spin_unlock_irqrestore(&vfe31_ctrl->ihist_ack_lock, flags); +} + +static void vfe31_stats_rs_ack(struct vfe_cmd_stats_ack *pAck) +{ + unsigned long flags; + spin_lock_irqsave(&vfe31_ctrl->rs_ack_lock, flags); + vfe31_ctrl->rsStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf; + vfe31_ctrl->rsStatsControl.ackPending = FALSE; + spin_unlock_irqrestore(&vfe31_ctrl->rs_ack_lock, flags); +} + +static void vfe31_stats_cs_ack(struct vfe_cmd_stats_ack *pAck) +{ + unsigned long flags; + spin_lock_irqsave(&vfe31_ctrl->cs_ack_lock, flags); + vfe31_ctrl->csStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf; + vfe31_ctrl->csStatsControl.ackPending = FALSE; + spin_unlock_irqrestore(&vfe31_ctrl->cs_ack_lock, flags); +} + +static int vfe31_config(struct msm_vfe_cfg_cmd *cmd, void *data) +{ + struct msm_vfe31_cmd vfecmd; + long rc = 0; + uint32_t i = 0; + struct vfe_cmd_stats_buf *scfg = NULL; + struct msm_pmem_region *regptr = NULL; + struct vfe_cmd_stats_ack *sack = NULL; + + if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE && + cmd->cmd_type != CMD_STATS_AEC_BUF_RELEASE && + cmd->cmd_type != CMD_STATS_AWB_BUF_RELEASE && + cmd->cmd_type != CMD_STATS_IHIST_BUF_RELEASE && + cmd->cmd_type != CMD_STATS_RS_BUF_RELEASE && + cmd->cmd_type != CMD_STATS_CS_BUF_RELEASE && + cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) { + if (copy_from_user(&vfecmd, (void __user *)cmd->value, + sizeof(vfecmd))) { + pr_err("%s %d: copy_from_user failed\n", __func__, + __LINE__); + return -EFAULT; + } + } else if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE) { + /* This must be stats release. */ + if (!data) + return -EFAULT; + sack = kmalloc(sizeof(struct vfe_cmd_stats_ack), GFP_KERNEL); + if (!sack) + return -ENOMEM; + sack->nextStatsBuf = *(uint32_t *)data; + } + + CDBG("%s: cmdType = %d\n", __func__, cmd->cmd_type); + + switch (cmd->cmd_type) { + case CMD_GENERAL: + rc = vfe31_proc_general(&vfecmd); + break; + case CMD_FRAME_BUF_RELEASE: { + struct msm_frame *b; + unsigned long p; + struct vfe31_free_buf *fbuf = NULL; + if (!data) + return -EFAULT; + + b = (struct msm_frame *)(cmd->value); + p = *(unsigned long *)data; + + CDBG("CMD_FRAME_BUF_RELEASE b->path = %d\n", b->path); + + if (b->path & OUTPUT_TYPE_P) { + CDBG("CMD_FRAME_BUF_RELEASE got free buffer\n"); + fbuf = &vfe31_ctrl->outpath.out0.free_buf; + } else if (b->path & OUTPUT_TYPE_S) { + fbuf = &vfe31_ctrl->outpath.out1.free_buf; + } else if (b->path & OUTPUT_TYPE_V) { + fbuf = &vfe31_ctrl->outpath.out2.free_buf; + } else + return -EFAULT; + + fbuf->paddr = p; + fbuf->y_off = b->y_off; + fbuf->cbcr_off = b->cbcr_off; + fbuf->available = 1; + } + break; + + case CMD_SNAP_BUF_RELEASE: + break; + case CMD_STATS_AEC_BUF_RELEASE: + vfe31_stats_aec_ack(sack); + break; + case CMD_STATS_AF_BUF_RELEASE: + vfe31_stats_af_ack(sack); + break; + case CMD_STATS_AWB_BUF_RELEASE: + vfe31_stats_awb_ack(sack); + break; + case CMD_STATS_IHIST_BUF_RELEASE: + vfe31_stats_ihist_ack(sack); + break; + case CMD_STATS_RS_BUF_RELEASE: + vfe31_stats_rs_ack(sack); + break; + case CMD_STATS_CS_BUF_RELEASE: + vfe31_stats_cs_ack(sack); + break; + + case CMD_AXI_CFG_PREVIEW: { + struct axidata *axid; + uint32_t *axio = NULL; + axid = data; + if (!axid) + return -EFAULT; + axio = kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length, GFP_KERNEL); + if (!axio) + return -ENOMEM; + + if (copy_from_user(axio, (void __user *)vfecmd.value, + vfe31_cmd[V31_AXI_OUT_CFG].length)) { + kfree(axio); + return -EFAULT; + } + vfe31_config_axi(OUTPUT_2, axid, axio); + kfree(axio); + } + break; + + case CMD_RAW_PICT_AXI_CFG: { + struct axidata *axid; + uint32_t *axio = NULL; + axid = data; + if (!axid) + return -EFAULT; + axio = kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length, GFP_KERNEL); + if (!axio) + return -ENOMEM; + + if (copy_from_user(axio, (void __user *)vfecmd.value, + vfe31_cmd[V31_AXI_OUT_CFG].length)) { + kfree(axio); + return -EFAULT; + } + vfe31_config_axi(CAMIF_TO_AXI_VIA_OUTPUT_2, axid, axio); + kfree(axio); + } + break; + + case CMD_AXI_CFG_SNAP: { + struct axidata *axid; + uint32_t *axio = NULL; + axid = data; + if (!axid) + return -EFAULT; + axio = kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length, GFP_KERNEL); + if (!axio) + return -ENOMEM; + + if (copy_from_user(axio, (void __user *)vfecmd.value, + vfe31_cmd[V31_AXI_OUT_CFG].length)) { + kfree(axio); + return -EFAULT; + } + vfe31_config_axi(OUTPUT_1_AND_2, axid, axio); + kfree(axio); + } + break; + + case CMD_AXI_CFG_VIDEO: { + struct axidata *axid; + uint32_t *axio = NULL; + axid = data; + if (!axid) + return -EFAULT; + axio = kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length, GFP_KERNEL); + if (!axio) + return -ENOMEM; + + if (copy_from_user(axio, (void __user *)vfecmd.value, + vfe31_cmd[V31_AXI_OUT_CFG].length)) { + kfree(axio); + return -EFAULT; + } + vfe31_config_axi(OUTPUT_1_AND_3, axid, axio); + kfree(axio); + } + break; + + case CMD_STATS_AF_ENABLE: + case CMD_STATS_AWB_ENABLE: + case CMD_STATS_IHIST_ENABLE: + case CMD_STATS_RS_ENABLE: + case CMD_STATS_CS_ENABLE: + case CMD_STATS_AEC_ENABLE: { + struct axidata *axid; + axid = (struct axidata *)data; + if (!axid) + return -EFAULT; + + scfg = kmalloc(sizeof(struct vfe_cmd_stats_buf), GFP_KERNEL); + if (!scfg) + return -ENOMEM; + regptr = axid->region; + if (axid->bufnum1 > 0) { + for (i = 0; i < axid->bufnum1; i++) { + scfg->statsBuf[i] = (uint32_t)(regptr->paddr); + regptr++; + } + } + /* individual */ + switch (cmd->cmd_type) { + case CMD_STATS_AEC_ENABLE: + rc = vfe_stats_aec_buf_init(scfg); + break; + case CMD_STATS_AF_ENABLE: + rc = vfe_stats_af_buf_init(scfg); + break; + case CMD_STATS_AWB_ENABLE: + rc = vfe_stats_awb_buf_init(scfg); + break; + case CMD_STATS_IHIST_ENABLE: + rc = vfe_stats_ihist_buf_init(scfg); + break; + case CMD_STATS_RS_ENABLE: + rc = vfe_stats_rs_buf_init(scfg); + break; + case CMD_STATS_CS_ENABLE: + rc = vfe_stats_cs_buf_init(scfg); + break; + } + } + + default: + rc = -EINVAL; + break; + } + kfree(scfg); + kfree(sack); + CDBG("%s done: rc = %d\n", __func__, (int) rc); + return rc; +} + +static inline void vfe31_read_irq_status(struct vfe31_irq_status *out) +{ + memset(out, 0, sizeof(struct vfe31_irq_status)); + out->vfeIrqStatus0 = vfe_io_r(VFE_IRQ_STATUS_0); + out->vfeIrqStatus1 = vfe_io_r(VFE_IRQ_STATUS_1); + out->camifStatus = vfe_io_r(VFE_CAMIF_STATUS); + CDBG("camifStatus = 0x%x\n", out->camifStatus); +} + +static void vfe31_send_msg_no_payload(enum VFE31_MESSAGE_ID id) +{ + struct vfe_message msg; + + CDBG("vfe31_send_msg_no_payload\n"); + msg._d = id; + vfe31_proc_ops(id, &msg, 0); +} + +static void vfe31_process_reg_update_irq(void) +{ + unsigned long flags; + uint32_t temp; + + if (vfe31_ctrl->req_start_video_rec) { + if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_V) { + vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out2.ch0); + vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out2.ch1); + } + vfe31_ctrl->req_start_video_rec = FALSE; + CDBG("start video triggered .\n"); + } else if (vfe31_ctrl->req_stop_video_rec) { + if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_V) { + vfe_io_w_axi_out_chan(0, vfe31_ctrl->outpath.out2.ch0); + vfe_io_w_axi_out_chan(0, vfe31_ctrl->outpath.out2.ch1); + } + vfe31_ctrl->req_stop_video_rec = FALSE; + CDBG("stop video triggered .\n"); + } + if (vfe31_ctrl->start_ack_pending == TRUE) { + vfe31_send_msg_no_payload(MSG_ID_START_ACK); + vfe31_ctrl->start_ack_pending = FALSE; + } else { + spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags); + if (vfe31_ctrl->update_ack_pending == TRUE) { + spin_unlock_irqrestore( + &vfe31_ctrl->update_ack_lock, flags); + vfe31_send_msg_no_payload(MSG_ID_UPDATE_ACK); + spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags); + vfe31_ctrl->update_ack_pending = FALSE; + spin_unlock_irqrestore( + &vfe31_ctrl->update_ack_lock, flags); + } else { + spin_unlock_irqrestore( + &vfe31_ctrl->update_ack_lock, flags); + } + } + if (vfe31_ctrl->operation_mode & 1) { /* in snapshot mode */ + /* later we need to add check for live snapshot mode. */ + vfe31_ctrl->vfe_capture_count--; + /* if last frame to be captured: */ + if (vfe31_ctrl->vfe_capture_count == 0) { + /* stop the bus output: write master enable = 0*/ + if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) { + vfe_io_w_axi_out_chan(0, + vfe31_ctrl->outpath.out0.ch0); + vfe_io_w_axi_out_chan(0, + vfe31_ctrl->outpath.out0.ch1); + } + if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) { + vfe_io_w_axi_out_chan(0, + vfe31_ctrl->outpath.out1.ch0); + vfe_io_w_axi_out_chan(0, + vfe31_ctrl->outpath.out1.ch1); + } + vfe_io_w(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY, + VFE_CAMIF_COMMAND); + + temp = vfe_io_r(VFE_CAMIF_COMMAND); + /* then do reg_update. */ + vfe_io_w(1, VFE_REG_UPDATE_CMD); + } + } /* if snapshot mode. */ +} + +static void vfe31_set_default_reg_values(void) +{ + vfe_io_w(0x800080, VFE_DEMUX_GAIN_0); + vfe_io_w(0x800080, VFE_DEMUX_GAIN_1); + vfe_io_w(0xFFFFF, VFE_CGC_OVERRIDE); + + /* default frame drop period and pattern */ + vfe_io_w(0x1f, VFE_FRAMEDROP_ENC_Y_CFG); + vfe_io_w(0x1f, VFE_FRAMEDROP_ENC_CBCR_CFG); + vfe_io_w(0xFFFFFFFF, VFE_FRAMEDROP_ENC_Y_PATTERN); + vfe_io_w(0xFFFFFFFF, VFE_FRAMEDROP_ENC_CBCR_PATTERN); + vfe_io_w(0x1f, VFE_FRAMEDROP_VIEW_Y); + vfe_io_w(0x1f, VFE_FRAMEDROP_VIEW_CBCR); + vfe_io_w(0xFFFFFFFF, VFE_FRAMEDROP_VIEW_Y_PATTERN); + vfe_io_w(0xFFFFFFFF, VFE_FRAMEDROP_VIEW_CBCR_PATTERN); + vfe_io_w(0, VFE_CLAMP_MIN); + vfe_io_w(0xFFFFFF, VFE_CLAMP_MAX); + + /* stats UB config */ + vfe_io_w(0x3900007, VFE_BUS_STATS_AEC_UB_CFG); + vfe_io_w(0x3980007, VFE_BUS_STATS_AF_UB_CFG); + vfe_io_w(0x3A0000F, VFE_BUS_STATS_AWB_UB_CFG); + vfe_io_w(0x3B00007, VFE_BUS_STATS_RS_UB_CFG); + vfe_io_w(0x3B8001F, VFE_BUS_STATS_CS_UB_CFG); + vfe_io_w(0x3D8001F, VFE_BUS_STATS_HIST_UB_CFG); + vfe_io_w(0x3F80007, VFE_BUS_STATS_SKIN_UB_CFG); +} + +static void vfe31_process_reset_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&vfe31_ctrl->state_lock, flags); + vfe31_ctrl->vstate = VFE_STATE_IDLE; + spin_unlock_irqrestore(&vfe31_ctrl->state_lock, flags); + + spin_lock_irqsave(&vfe31_ctrl->stop_flag_lock, flags); + if (vfe31_ctrl->stop_ack_pending) { + vfe31_ctrl->stop_ack_pending = FALSE; + spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags); + vfe31_send_msg_no_payload(MSG_ID_STOP_ACK); + } else { + spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags); + /* this is from reset command. */ + vfe31_set_default_reg_values(); + + /* reload all write masters. (frame & line)*/ + vfe_io_w(0x7FFF, VFE_BUS_CMD); + vfe31_send_msg_no_payload(MSG_ID_RESET_ACK); + } +} + +static void vfe31_process_camif_sof_irq(void) +{ + uint32_t temp; + + if (vfe31_ctrl->operation_mode == 3) { /* in raw snapshot mode */ + if (vfe31_ctrl->start_ack_pending) { + vfe31_send_msg_no_payload(MSG_ID_START_ACK); + vfe31_ctrl->start_ack_pending = FALSE; + } + vfe31_ctrl->vfe_capture_count--; + /* if last frame to be captured: */ + if (vfe31_ctrl->vfe_capture_count == 0) { + vfe_io_w(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY, VFE_CAMIF_COMMAND); + temp = vfe_io_r(VFE_CAMIF_COMMAND); + } + } /* if raw snapshot mode. */ + + vfe31_ctrl->vfeFrameId++; + CDBG("camif_sof_irq, frameId = %d\n", vfe31_ctrl->vfeFrameId); +} + +static void vfe31_process_output_path_irq_0(void) +{ + uint32_t ping_pong; + uint32_t pyaddr, pcbcraddr; + uint8_t out_bool = 0; + + /* we render frames in the following conditions: + 1. Continuous mode and the free buffer is avaialable. + 2. In snapshot shot mode, free buffer is not always available. + when pending snapshot count is <=1, then no need to use + free buffer. + */ + out_bool = + ((vfe31_ctrl->operation_mode & 1) && + (vfe31_ctrl->vfe_capture_count <= 1)) || + (vfe31_ctrl->outpath.out0.free_buf.available); + if (out_bool) { + ping_pong = vfe_io_r(VFE_BUS_PING_PONG_STATUS); + + /* Y channel */ + pyaddr = vfe31_get_ch_addr(ping_pong, + vfe31_ctrl->outpath.out0.ch0); + /* Chroma channel */ + pcbcraddr = vfe31_get_ch_addr(ping_pong, + vfe31_ctrl->outpath.out0.ch1); + + CDBG("output path 0, pyaddr = 0x%x, pcbcraddr = 0x%x\n", + pyaddr, pcbcraddr); + if (vfe31_ctrl->outpath.out0.free_buf.available) { + /* Y channel */ + vfe31_put_ch_addr(ping_pong, + vfe31_ctrl->outpath.out0.ch0, + vfe31_ctrl->outpath.out0.free_buf.paddr + + vfe31_ctrl->outpath.out0.free_buf.y_off); + /* Chroma channel */ + vfe31_put_ch_addr(ping_pong, + vfe31_ctrl->outpath.out0.ch1, + vfe31_ctrl->outpath.out0.free_buf.paddr + + vfe31_ctrl->outpath.out0.free_buf.cbcr_off); + vfe31_ctrl->outpath.out0.free_buf.available = 0; + } + if (vfe31_ctrl->operation_mode & 1) { + /* will add message for multi-shot. */ + vfe31_ctrl->outpath.out0.capture_cnt--; + } else { + /* always send message for continous mode. */ + /* if continuous mode, this is for display. (preview) */ + vfe_send_outmsg(MSG_ID_OUTPUT_P, pyaddr, pcbcraddr); + } + } else { + vfe31_ctrl->outpath.out0.frame_drop_cnt++; + CDBG("path_irq_0 - no free buffer!\n"); + } +} + +static void vfe31_process_output_path_irq_1(void) +{ + uint32_t ping_pong; + uint32_t pyaddr, pcbcraddr; + /* this must be snapshot main image output. */ + uint8_t out_bool; + + /* we render frames in the following conditions: + 1. Continuous mode and the free buffer is avaialable. + 2. In snapshot shot mode, free buffer is not always available. + -- when pending snapshot count is <=1, then no need to use + free buffer. + */ + out_bool = + ((vfe31_ctrl->operation_mode & 1) && + (vfe31_ctrl->vfe_capture_count <= 1)) || + (vfe31_ctrl->outpath.out1.free_buf.available); + if (out_bool) { + ping_pong = vfe_io_r(VFE_BUS_PING_PONG_STATUS); + + /* Y channel */ + pyaddr = vfe31_get_ch_addr(ping_pong, + vfe31_ctrl->outpath.out1.ch0); + /* Chroma channel */ + pcbcraddr = vfe31_get_ch_addr(ping_pong, + vfe31_ctrl->outpath.out1.ch1); + + CDBG("snapshot main, pyaddr = 0x%x, pcbcraddr = 0x%x\n", + pyaddr, pcbcraddr); + if (vfe31_ctrl->outpath.out1.free_buf.available) { + /* Y channel */ + vfe31_put_ch_addr(ping_pong, + vfe31_ctrl->outpath.out1.ch0, + vfe31_ctrl->outpath.out1.free_buf.paddr + + vfe31_ctrl->outpath.out1.free_buf.y_off); + /* Chroma channel */ + vfe31_put_ch_addr(ping_pong, + vfe31_ctrl->outpath.out1.ch1, + vfe31_ctrl->outpath.out1.free_buf.paddr + + vfe31_ctrl->outpath.out1.free_buf.cbcr_off); + vfe31_ctrl->outpath.out1.free_buf.available = 0; + } + + vfe31_ctrl->outpath.out1.capture_cnt--; + } else { + vfe31_ctrl->outpath.out1.frame_drop_cnt++; + CDBG("path_irq_1 - no free buffer!\n"); + } +} + +static void vfe31_process_output_path_irq_2(void) +{ + uint32_t ping_pong; + uint32_t pyaddr, pcbcraddr; + uint8_t out_bool; + + /* we render frames in the following conditions: + 1. Continuous mode and the free buffer is avaialable. + 2. In snapshot shot mode, free buffer is not always available. + -- when pending snapshot count is <=1, then no need to use + free buffer. + */ + out_bool = + ((vfe31_ctrl->operation_mode & 1) && + (vfe31_ctrl->vfe_capture_count <= 1)) || + (vfe31_ctrl->outpath.out2.free_buf.available); + if (out_bool) { + ping_pong = vfe_io_r(VFE_BUS_PING_PONG_STATUS); + + /* Y channel */ + pyaddr = vfe31_get_ch_addr(ping_pong, + vfe31_ctrl->outpath.out2.ch0); + /* Chroma channel */ + pcbcraddr = vfe31_get_ch_addr(ping_pong, + vfe31_ctrl->outpath.out2.ch1); + + CDBG("video output, pyaddr = 0x%x, pcbcraddr = 0x%x\n", + pyaddr, pcbcraddr); + + if (vfe31_ctrl->outpath.out2.free_buf.available) { + /* Y channel */ + vfe31_put_ch_addr(ping_pong, + vfe31_ctrl->outpath.out2.ch0, + vfe31_ctrl->outpath.out2.free_buf.paddr + + vfe31_ctrl->outpath.out2.free_buf.y_off); + /* Chroma channel */ + vfe31_put_ch_addr(ping_pong, + vfe31_ctrl->outpath.out2.ch1, + vfe31_ctrl->outpath.out2.free_buf.paddr + + vfe31_ctrl->outpath.out2.free_buf.cbcr_off); + vfe31_ctrl->outpath.out2.free_buf.available = 0; + } + vfe_send_outmsg(MSG_ID_OUTPUT_V, pyaddr, pcbcraddr); + } else { + vfe31_ctrl->outpath.out2.frame_drop_cnt++; + CDBG("path_irq_2 - no free buffer!\n"); + } +} + +static void vfe31_process_stats_comb_irq(uint32_t *irqstatus) +{ + return; +} + +static uint32_t vfe31_process_stats_irq_common(uint32_t statsNum, + uint32_t newAddr) { + uint32_t pingpongStatus; + uint32_t returnAddr; + uint32_t pingpongAddrOffset; + + /* must be 0=ping, 1=pong */ + pingpongStatus = + (vfe_io_r(VFE_BUS_PING_PONG_STATUS) + & ((uint32_t)(1<<(statsNum + 7)))) >> (statsNum + 7); + /* stats bits starts at 7 */ + CDBG("statsNum %d, pingpongStatus %d\n", statsNum, pingpongStatus); + pingpongAddrOffset = + ((uint32_t)(VFE_BUS_STATS_PING_PONG_BASE)) + + (3*statsNum)*4 + (1-pingpongStatus)*4; + returnAddr = vfe_io_r(pingpongAddrOffset); + vfe_io_w(newAddr, pingpongAddrOffset); + return returnAddr; +} + +static void vfe_send_stats_msg(uint32_t bufAddress, uint32_t statsNum) +{ + struct vfe_message msg; + + /* fill message with right content. */ + msg._u.msgStats.frameCounter = vfe31_ctrl->vfeFrameId; + msg._u.msgStats.buffer = bufAddress; + + switch (statsNum) { + case statsAeNum: + msg._d = MSG_ID_STATS_AEC; + break; + case statsAfNum: + msg._d = MSG_ID_STATS_AF; + break; + case statsAwbNum: + msg._d = MSG_ID_STATS_AWB; + break; + case statsIhistNum: + msg._d = MSG_ID_STATS_IHIST; + break; + case statsRsNum: + msg._d = MSG_ID_STATS_RS; + break; + case statsCsNum: + msg._d = MSG_ID_STATS_CS; + break; + default: + goto stats_done; + } + + vfe31_proc_ops(msg._d, &msg, sizeof(struct vfe_message)); +stats_done: + return; +} + +static void vfe31_process_stats_ae_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&vfe31_ctrl->aec_ack_lock, flags); + if (!vfe31_ctrl->aecStatsControl.ackPending) { + vfe31_ctrl->aecStatsControl.ackPending = TRUE; + spin_unlock_irqrestore(&vfe31_ctrl->aec_ack_lock, flags); + vfe31_ctrl->aecStatsControl.bufToRender = + vfe31_process_stats_irq_common(statsAeNum, + vfe31_ctrl->aecStatsControl.nextFrameAddrBuf); + + vfe_send_stats_msg(vfe31_ctrl->aecStatsControl.bufToRender, + statsAeNum); + } else { + spin_unlock_irqrestore(&vfe31_ctrl->aec_ack_lock, flags); + vfe31_ctrl->aecStatsControl.droppedStatsFrameCount++; + } + +} + +static void vfe31_process_stats_af_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&vfe31_ctrl->af_ack_lock, flags); + if (!vfe31_ctrl->afStatsControl.ackPending) { + vfe31_ctrl->afStatsControl.ackPending = TRUE; + spin_unlock_irqrestore(&vfe31_ctrl->af_ack_lock, flags); + vfe31_ctrl->afStatsControl.bufToRender = + vfe31_process_stats_irq_common(statsAfNum, + vfe31_ctrl->afStatsControl.nextFrameAddrBuf); + + vfe_send_stats_msg(vfe31_ctrl->afStatsControl.bufToRender, + statsAfNum); + } else { + spin_unlock_irqrestore(&vfe31_ctrl->af_ack_lock, flags); + vfe31_ctrl->afStatsControl.droppedStatsFrameCount++; + } + +} + +static void vfe31_process_stats_awb_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&vfe31_ctrl->awb_ack_lock, flags); + if (!vfe31_ctrl->awbStatsControl.ackPending) { + vfe31_ctrl->awbStatsControl.ackPending = TRUE; + spin_unlock_irqrestore(&vfe31_ctrl->awb_ack_lock, flags); + vfe31_ctrl->awbStatsControl.bufToRender = + vfe31_process_stats_irq_common(statsAwbNum, + vfe31_ctrl->awbStatsControl.nextFrameAddrBuf); + + vfe_send_stats_msg(vfe31_ctrl->awbStatsControl.bufToRender, + statsAwbNum); + } else { + spin_unlock_irqrestore(&vfe31_ctrl->awb_ack_lock, flags); + vfe31_ctrl->awbStatsControl.droppedStatsFrameCount++; + } + +} + +static void vfe31_process_stats_ihist_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&vfe31_ctrl->ihist_ack_lock, flags); + if (!vfe31_ctrl->ihistStatsControl.ackPending) { + vfe31_ctrl->ihistStatsControl.ackPending = TRUE; + spin_unlock_irqrestore(&vfe31_ctrl->ihist_ack_lock, flags); + vfe31_ctrl->ihistStatsControl.bufToRender = + vfe31_process_stats_irq_common(statsIhistNum, + vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf); + + vfe_send_stats_msg(vfe31_ctrl->ihistStatsControl.bufToRender, + statsIhistNum); + } else { + spin_unlock_irqrestore(&vfe31_ctrl->ihist_ack_lock, flags); + vfe31_ctrl->ihistStatsControl.droppedStatsFrameCount++; + } +} + + +static void vfe31_process_stats_rs_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&vfe31_ctrl->rs_ack_lock, flags); + if (!vfe31_ctrl->rsStatsControl.ackPending) { + vfe31_ctrl->rsStatsControl.ackPending = TRUE; + spin_unlock_irqrestore(&vfe31_ctrl->rs_ack_lock, flags); + vfe31_ctrl->rsStatsControl.bufToRender = + vfe31_process_stats_irq_common(statsRsNum, + vfe31_ctrl->rsStatsControl.nextFrameAddrBuf); + + vfe_send_stats_msg(vfe31_ctrl->rsStatsControl.bufToRender, + statsRsNum); + } else { + spin_unlock_irqrestore(&vfe31_ctrl->rs_ack_lock, flags); + vfe31_ctrl->rsStatsControl.droppedStatsFrameCount++; + } +} + +static void vfe31_process_stats_cs_irq(void) +{ + unsigned long flags; + + spin_lock_irqsave(&vfe31_ctrl->cs_ack_lock, flags); + if (!vfe31_ctrl->csStatsControl.ackPending) { + vfe31_ctrl->csStatsControl.ackPending = TRUE; + spin_unlock_irqrestore(&vfe31_ctrl->cs_ack_lock, flags); + vfe31_ctrl->csStatsControl.bufToRender = + vfe31_process_stats_irq_common(statsCsNum, + vfe31_ctrl->csStatsControl.nextFrameAddrBuf); + + vfe_send_stats_msg(vfe31_ctrl->csStatsControl.bufToRender, + statsCsNum); + } else { + spin_unlock_irqrestore(&vfe31_ctrl->cs_ack_lock, flags); + vfe31_ctrl->csStatsControl.droppedStatsFrameCount++; + } +} + +static void vfe31_do_tasklet(unsigned long data) +{ + unsigned long flags; + struct vfe31_isr_queue_cmd *qcmd = NULL; + + CDBG("=== vfe31_do_tasklet start ===\n"); + + spin_lock_irqsave(&vfe31_ctrl->tasklet_lock, flags); + qcmd = list_first_entry(&vfe31_ctrl->tasklet_q, + struct vfe31_isr_queue_cmd, list); + + if (!qcmd) { + spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock, flags); + return; + } + + list_del(&qcmd->list); + spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock, flags); + + /* interrupt to be processed, *qcmd has the payload. */ + if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_REG_UPDATE_MASK) { + CDBG("irq regUpdateIrq\n"); + vfe31_process_reg_update_irq(); + } + + if (qcmd->vfeInterruptStatus1 & VFE_IMASK_WHILE_STOPPING_1) { + CDBG("irq resetAckIrq\n"); + vfe31_process_reset_irq(); + } + + spin_lock_irqsave(&vfe31_ctrl->state_lock, flags); + if (vfe31_ctrl->vstate == VFE_STATE_ACTIVE) { + /* irqs below are only valid when in active state. */ + spin_unlock_irqrestore(&vfe31_ctrl->state_lock, flags); + /* next, check output path related interrupts. */ + if (qcmd->vfeInterruptStatus0 & + VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE0_MASK) { + vfe31_process_output_path_irq_0(); + } + if (qcmd->vfeInterruptStatus0 & + VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE1_MASK) { + vfe31_process_output_path_irq_1(); + } + if (qcmd->vfeInterruptStatus0 & + VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE2_MASK) { + vfe31_process_output_path_irq_2(); + } + /* in snapshot mode if done then send snapshot done message */ + if (vfe31_ctrl->operation_mode & 1) { + if ((vfe31_ctrl->outpath.out0.capture_cnt == 0) && + (vfe31_ctrl->outpath.out1.capture_cnt == 0)) { + vfe31_send_msg_no_payload(MSG_ID_SNAPSHOT_DONE); + vfe_io_w(CAMIF_COMMAND_STOP_IMMEDIATELY, VFE_CAMIF_COMMAND); + } + } + /* then process stats irq. */ + if (vfe31_ctrl->stats_comp) { + /* process stats comb interrupt. */ + if (qcmd->vfeInterruptStatus0 & + VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK) { + CDBG("Stats composite irq occured.\n"); + vfe31_process_stats_comb_irq( + &qcmd->vfeInterruptStatus0); + } + } else { + /* process individual stats interrupt. */ + if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_AEC) { + CDBG("Stats AEC irq occured.\n"); + vfe31_process_stats_ae_irq(); + } + if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_AWB) { + CDBG("Stats AWB irq occured.\n"); + vfe31_process_stats_awb_irq(); + } + if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_AF) { + CDBG("Stats AF irq occured.\n"); + vfe31_process_stats_af_irq(); + } + if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_IHIST) { + CDBG("Stats IHIST irq occured.\n"); + vfe31_process_stats_ihist_irq(); + } + if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_RS) { + CDBG("Stats RS irq occured.\n"); + vfe31_process_stats_rs_irq(); + } + if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_CS) { + CDBG("Stats CS irq occured.\n"); + vfe31_process_stats_cs_irq(); + } + } + } else { + /* do we really need spin lock for state? */ + spin_unlock_irqrestore(&vfe31_ctrl->state_lock, flags); + } + if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_CAMIF_SOF_MASK) { + vfe31_process_camif_sof_irq(); + } + kfree(qcmd); + CDBG("=== vfe31_do_tasklet end ===\n"); +} + +DECLARE_TASKLET(vfe31_tasklet, vfe31_do_tasklet, 0); + +static irqreturn_t vfe31_parse_irq(int irq_num, void *data) +{ + unsigned long flags; + struct vfe31_irq_status irq; + struct vfe31_isr_queue_cmd *qcmd; + + CDBG("vfe_parse_irq\n"); + vfe31_read_irq_status(&irq); + + if ((irq.vfeIrqStatus0 == 0) && (irq.vfeIrqStatus1 == 0)) { + CDBG("vfe_parse_irq: vfeIrqStatus0 & 1 are both 0!\n"); + return IRQ_HANDLED; + } + + qcmd = kzalloc(sizeof(struct vfe31_isr_queue_cmd), GFP_ATOMIC); + if (!qcmd) { + pr_err("vfe_parse_irq: qcmd malloc failed!\n"); + return IRQ_HANDLED; + } + + spin_lock_irqsave(&vfe31_ctrl->stop_flag_lock, flags); + if (vfe31_ctrl->stop_ack_pending) { + irq.vfeIrqStatus0 &= VFE_IMASK_WHILE_STOPPING_0; + irq.vfeIrqStatus1 &= VFE_IMASK_WHILE_STOPPING_1; + } + spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags); + + CDBG("vfe_parse_irq: Irq_status0 = 0x%x, Irq_status1 = 0x%x.\n", + irq.vfeIrqStatus0, irq.vfeIrqStatus1); + + qcmd->vfeInterruptStatus0 = irq.vfeIrqStatus0; + qcmd->vfeInterruptStatus1 = irq.vfeIrqStatus1; + + spin_lock_irqsave(&vfe31_ctrl->tasklet_lock, flags); + list_add_tail(&qcmd->list, &vfe31_ctrl->tasklet_q); + spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock, flags); + tasklet_schedule(&vfe31_tasklet); + + /* clear the pending interrupt of the same kind.*/ + vfe_io_w(irq.vfeIrqStatus0, VFE_IRQ_CLEAR_0); + vfe_io_w(irq.vfeIrqStatus1, VFE_IRQ_CLEAR_1); + + vfe_io_w(1, VFE_IRQ_CMD); + + return IRQ_HANDLED; +} + +static int vfe31_resource_init(struct msm_vfe_callback *presp, + struct platform_device *pdev, void *sdata) +{ + struct resource *vfemem, *vfeirq, *vfeio; + int rc; + struct msm_camera_sensor_info *s_info; + s_info = pdev->dev.platform_data; + + pdev->resource = s_info->resource; + pdev->num_resources = s_info->num_resources; + + vfemem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!vfemem) { + pr_err("%s: no mem resource?\n", __func__); + return -ENODEV; + } + + vfeirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!vfeirq) { + pr_err("%s: no irq resource?\n", __func__); + return -ENODEV; + } + + vfeio = request_mem_region(vfemem->start, + resource_size(vfemem), pdev->name); + if (!vfeio) { + pr_err("%s: VFE region already claimed\n", __func__); + return -EBUSY; + } + + vfe31_ctrl = kzalloc(sizeof(struct vfe31_ctrl_type), GFP_KERNEL); + if (!vfe31_ctrl) { + rc = -ENOMEM; + goto cmd_init_failed1; + } + + vfe31_ctrl->vfeirq = vfeirq->start; + + vfe31_ctrl->vfebase = + ioremap(vfemem->start, (vfemem->end - vfemem->start) + 1); + if (!vfe31_ctrl->vfebase) { + rc = -ENOMEM; + pr_err("%s: vfe ioremap failed\n", __func__); + goto cmd_init_failed2; + } + + rc = request_irq(vfe31_ctrl->vfeirq, vfe31_parse_irq, + IRQF_TRIGGER_RISING, "vfe", 0); + if (rc < 0) + goto cmd_init_failed2; + + if (presp && presp->vfe_resp) + vfe31_ctrl->resp = presp; + else { + rc = -EINVAL; + goto cmd_init_failed3; + } + + vfe31_ctrl->extdata = kmalloc(sizeof(struct vfe31_frame_extra), + GFP_KERNEL); + if (!vfe31_ctrl->extdata) { + rc = -ENOMEM; + goto cmd_init_failed3; + } + + vfe31_ctrl->extlen = sizeof(struct vfe31_frame_extra); + + spin_lock_init(&vfe31_ctrl->stop_flag_lock); + spin_lock_init(&vfe31_ctrl->state_lock); + spin_lock_init(&vfe31_ctrl->update_ack_lock); + spin_lock_init(&vfe31_ctrl->tasklet_lock); + + spin_lock_init(&vfe31_ctrl->aec_ack_lock); + spin_lock_init(&vfe31_ctrl->awb_ack_lock); + spin_lock_init(&vfe31_ctrl->af_ack_lock); + spin_lock_init(&vfe31_ctrl->ihist_ack_lock); + spin_lock_init(&vfe31_ctrl->rs_ack_lock); + spin_lock_init(&vfe31_ctrl->cs_ack_lock); + INIT_LIST_HEAD(&vfe31_ctrl->tasklet_q); + + vfe31_ctrl->syncdata = sdata; + vfe31_ctrl->vfemem = vfemem; + vfe31_ctrl->vfeio = vfeio; + return 0; + +cmd_init_failed3: + free_irq(vfe31_ctrl->vfeirq, 0); + iounmap(vfe31_ctrl->vfebase); +cmd_init_failed2: + kfree(vfe31_ctrl); +cmd_init_failed1: + release_mem_region(vfemem->start, (vfemem->end - vfemem->start) + 1); + return rc; +} + +static int vfe31_init(struct msm_vfe_callback *presp, + struct platform_device *dev) +{ + int rc = 0; + rc = vfe31_resource_init(presp, dev, vfe_syncdata); + if (rc < 0) + return rc; + + /* Bring up all the required GPIOs and Clocks */ + rc = msm_camio_enable(dev); + return rc; +} + +void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data) +{ + fptr->vfe_init = vfe31_init; + fptr->vfe_enable = vfe31_enable; + fptr->vfe_config = vfe31_config; + fptr->vfe_disable = vfe31_disable; + fptr->vfe_release = vfe31_release; + vfe_syncdata = data; +} diff --git a/drivers/media/video/msm/msm_vfe31.h b/drivers/media/video/msm/msm_vfe31.h new file mode 100644 index 0000000000000..2a12ac443eb13 --- /dev/null +++ b/drivers/media/video/msm/msm_vfe31.h @@ -0,0 +1,1052 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __MSM_VFE31_H__ +#define __MSM_VFE31_H__ + +#define TRUE 1 +#define FALSE 0 + +/* at start of camif, bit 1:0 = 0x01:enable + * image data capture at frame boundary. */ +#define CAMIF_COMMAND_START 0x00000005 + +/* bit 2= 0x1:clear the CAMIF_STATUS register + * value. */ +#define CAMIF_COMMAND_CLEAR 0x00000004 + +/* at stop of vfe pipeline, for now it is assumed + * that camif will stop at any time. Bit 1:0 = 0x10: + * disable image data capture immediately. */ +#define CAMIF_COMMAND_STOP_IMMEDIATELY 0x00000002 + +/* at stop of vfe pipeline, for now it is assumed + * that camif will stop at any time. Bit 1:0 = 0x00: + * disable image data capture at frame boundary */ +#define CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY 0x00000000 + +/* to halt axi bridge */ +#define AXI_HALT 0x00000001 + +/* clear the halt bit. */ +#define AXI_HALT_CLEAR 0x00000000 + +/* reset the pipeline when stop command is issued. + * (without reset the register.) bit 26-31 = 0, + * domain reset, bit 0-9 = 1 for module reset, except + * register module. */ +#define VFE_RESET_UPON_STOP_CMD 0x000003ef + +/* reset the pipeline when reset command. + * bit 26-31 = 0, domain reset, bit 0-9 = 1 for module reset. */ +#define VFE_RESET_UPON_RESET_CMD 0x000003ff + +/* bit 5 is for axi status idle or busy. + * 1 = halted, 0 = busy */ +#define AXI_STATUS_BUSY_MASK 0x00000020 + +/* bit 0 & bit 1 = 1, both y and cbcr irqs need to be present + * for frame done interrupt */ +#define VFE_COMP_IRQ_BOTH_Y_CBCR 3 + +/* bit 1 = 1, only cbcr irq triggers frame done interrupt */ +#define VFE_COMP_IRQ_CBCR_ONLY 2 + +/* bit 0 = 1, only y irq triggers frame done interrupt */ +#define VFE_COMP_IRQ_Y_ONLY 1 + +/* bit 0 = 1, PM go; bit1 = 1, PM stop */ +#define VFE_PERFORMANCE_MONITOR_GO 0x00000001 +#define VFE_PERFORMANCE_MONITOR_STOP 0x00000002 + +/* bit 0 = 1, test gen go; bit1 = 1, test gen stop */ +#define VFE_TEST_GEN_GO 0x00000001 +#define VFE_TEST_GEN_STOP 0x00000002 + +/* the chroma is assumed to be interpolated between + * the luma samples. JPEG 4:2:2 */ +#define VFE_CHROMA_UPSAMPLE_INTERPOLATED 0 + +/* constants for irq registers */ +#define VFE_DISABLE_ALL_IRQS 0 +/* bit =1 is to clear the corresponding bit in VFE_IRQ_STATUS. */ +#define VFE_CLEAR_ALL_IRQS 0xffffffff + +#define VFE_IRQ_STATUS0_CAMIF_SOF_MASK 0x00000001 +#define VFE_IRQ_STATUS0_REG_UPDATE_MASK 0x00000020 +#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE0_MASK 0x00200000 +#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE1_MASK 0x00400000 +#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE2_MASK 0x00800000 +#define VFE_IRQ_STATUS1_RESET_AXI_HALT_ACK_MASK 0x00800000 +#define VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK 0x01000000 + +#define VFE_IRQ_STATUS0_STATS_AEC 0x2000 /* bit 13 */ +#define VFE_IRQ_STATUS0_STATS_AF 0x4000 /* bit 14 */ +#define VFE_IRQ_STATUS0_STATS_AWB 0x8000 /* bit 15 */ +#define VFE_IRQ_STATUS0_STATS_RS 0x10000 /* bit 16 */ +#define VFE_IRQ_STATUS0_STATS_CS 0x20000 /* bit 17 */ +#define VFE_IRQ_STATUS0_STATS_IHIST 0x40000 /* bit 18 */ + +/* imask for while waiting for stop ack, driver has already + * requested stop, waiting for reset irq, and async timer irq. + * For irq_status_0, bit 28-31 are for async timer. For + * irq_status_1, bit 22 for reset irq, bit 23 for axi_halt_ack + irq */ +#define VFE_IMASK_WHILE_STOPPING_0 0xF0000000 +#define VFE_IMASK_WHILE_STOPPING_1 0x00400000 + +/* no error irq in mask 0 */ +#define VFE_IMASK_ERROR_ONLY_0 0x0 +/* when normal case, don't want to block error status. */ +/* bit 0-21 are error irq bits */ +#define VFE_IMASK_ERROR_ONLY_1 0x003fffff + +/* For BPC bit 0,bit 12-17 and bit 26 -20 are set to zero and other's 1 */ +#define BPC_MASK 0xF80C0FFE + +/* For BPC bit 1 and 2 are set to zero and other's 1 */ +#define ABF_MASK 0xFFFFFFF9 + +/* For MCE enable bit 28 set to zero and other's 1 */ +#define MCE_EN_MASK 0xEFFFFFFF + +/* For MCE Q_K bit 28 to 31 set to zero and other's 1 */ +#define MCE_Q_K_MASK 0x0FFFFFFF + +#define AWB_ENABLE_MASK 0x00000080 /* bit 7 */ +#define AF_ENABLE_MASK 0x00000040 /* bit 6 */ +#define AE_ENABLE_MASK 0x00000020 /* bit 5 */ +#define IHIST_ENABLE_MASK 0x00008000 /* bit 15 */ +#define RS_ENABLE_MASK 0x00000100 /* bit 8 */ +#define CS_ENABLE_MASK 0x00000200 /* bit 9 */ + + +#define VFE_REG_UPDATE_TRIGGER 1 +#define VFE_PM_BUF_MAX_CNT_MASK 0xFF +#define VFE_DMI_CFG_DEFAULT 0x00000100 +#define LENS_ROLL_OFF_DELTA_TABLE_OFFSET 32 +#define VFE_AE_PINGPONG_STATUS_BIT 0x80 +#define VFE_AF_PINGPONG_STATUS_BIT 0x100 +#define VFE_AWB_PINGPONG_STATUS_BIT 0x200 + + +enum VFE31_DMI_RAM_SEL { + NO_MEM_SELECTED = 0, + ROLLOFF_RAM = 0x1, + RGBLUT_RAM_CH0_BANK0 = 0x2, + RGBLUT_RAM_CH0_BANK1 = 0x3, + RGBLUT_RAM_CH1_BANK0 = 0x4, + RGBLUT_RAM_CH1_BANK1 = 0x5, + RGBLUT_RAM_CH2_BANK0 = 0x6, + RGBLUT_RAM_CH2_BANK1 = 0x7, + STATS_HIST_CB_EVEN_RAM = 0x8, + STATS_HIST_CB_ODD_RAM = 0x9, + STATS_HIST_CR_EVEN_RAM = 0xa, + STATS_HIST_CR_ODD_RAM = 0xb, + RGBLUT_CHX_BANK0 = 0xc, + RGBLUT_CHX_BANK1 = 0xd, + LUMA_ADAPT_LUT_RAM_BANK0 = 0xe, + LUMA_ADAPT_LUT_RAM_BANK1 = 0xf + +}; + +enum VFE_STATE { + VFE_STATE_IDLE, + VFE_STATE_ACTIVE +}; + +#define V31_DUMMY_0 0 +#define V31_SET_CLK 1 +#define V31_RESET 2 +#define V31_START 3 +#define V31_TEST_GEN_START 4 +#define V31_OPERATION_CFG 5 +#define V31_AXI_OUT_CFG 6 +#define V31_CAMIF_CFG 7 +#define V31_AXI_INPUT_CFG 8 +#define V31_BLACK_LEVEL_CFG 9 +#define V31_ROLL_OFF_CFG 10 +#define V31_DEMUX_CFG 11 +#define V31_DEMOSAIC_0_CFG 12 /* general */ +#define V31_DEMOSAIC_1_CFG 13 /* ABF */ +#define V31_DEMOSAIC_2_CFG 14 /* BPC */ +#define V31_FOV_CFG 15 +#define V31_MAIN_SCALER_CFG 16 +#define V31_WB_CFG 17 +#define V31_COLOR_COR_CFG 18 +#define V31_RGB_G_CFG 19 +#define V31_LA_CFG 20 +#define V31_CHROMA_EN_CFG 21 +#define V31_CHROMA_SUP_CFG 22 +#define V31_MCE_CFG 23 +#define V31_SK_ENHAN_CFG 24 +#define V31_ASF_CFG 25 +#define V31_S2Y_CFG 26 +#define V31_S2CbCr_CFG 27 +#define V31_CHROMA_SUBS_CFG 28 +#define V31_OUT_CLAMP_CFG 29 +#define V31_FRAME_SKIP_CFG 30 +#define V31_DUMMY_1 31 +#define V31_DUMMY_2 32 +#define V31_DUMMY_3 33 +#define V31_UPDATE 34 +#define V31_BL_LVL_UPDATE 35 +#define V31_DEMUX_UPDATE 36 +#define V31_DEMOSAIC_1_UPDATE 37 /* BPC */ +#define V31_DEMOSAIC_2_UPDATE 38 /* ABF */ +#define V31_FOV_UPDATE 39 +#define V31_MAIN_SCALER_UPDATE 40 +#define V31_WB_UPDATE 41 +#define V31_COLOR_COR_UPDATE 42 +#define V31_RGB_G_UPDATE 43 +#define V31_LA_UPDATE 44 +#define V31_CHROMA_EN_UPDATE 45 +#define V31_CHROMA_SUP_UPDATE 46 +#define V31_MCE_UPDATE 47 +#define V31_SK_ENHAN_UPDATE 48 +#define V31_S2CbCr_UPDATE 49 +#define V31_S2Y_UPDATE 50 +#define V31_ASF_UPDATE 51 +#define V31_FRAME_SKIP_UPDATE 52 +#define V31_CAMIF_FRAME_UPDATE 53 +#define V31_STATS_AF_UPDATE 54 +#define V31_STATS_AE_UPDATE 55 +#define V31_STATS_AWB_UPDATE 56 +#define V31_STATS_RS_UPDATE 57 +#define V31_STATS_CS_UPDATE 58 +#define V31_STATS_SKIN_UPDATE 59 +#define V31_STATS_IHIST_UPDATE 60 +#define V31_DUMMY_4 61 +#define V31_EPOCH1_ACK 62 +#define V31_EPOCH2_ACK 63 +#define V31_START_RECORDING 64 +#define V31_STOP_RECORDING 65 +#define V31_DUMMY_5 66 +#define V31_DUMMY_6 67 +#define V31_CAPTURE 68 +#define V31_DUMMY_7 69 +#define V31_STOP 70 +#define V31_GET_HW_VERSION 71 +#define V31_GET_FRAME_SKIP_COUNTS 72 +#define V31_OUTPUT1_BUFFER_ENQ 73 +#define V31_OUTPUT2_BUFFER_ENQ 74 +#define V31_OUTPUT3_BUFFER_ENQ 75 +#define V31_JPEG_OUT_BUF_ENQ 76 +#define V31_RAW_OUT_BUF_ENQ 77 +#define V31_RAW_IN_BUF_ENQ 78 +#define V31_STATS_AF_ENQ 79 +#define V31_STATS_AE_ENQ 80 +#define V31_STATS_AWB_ENQ 81 +#define V31_STATS_RS_ENQ 82 +#define V31_STATS_CS_ENQ 83 +#define V31_STATS_SKIN_ENQ 84 +#define V31_STATS_IHIST_ENQ 85 +#define V31_DUMMY_8 86 +#define V31_JPEG_ENC_CFG 87 +#define V31_DUMMY_9 88 +#define V31_STATS_AF_START 89 +#define V31_STATS_AF_STOP 90 +#define V31_STATS_AE_START 91 +#define V31_STATS_AE_STOP 92 +#define V31_STATS_AWB_START 93 +#define V31_STATS_AWB_STOP 94 +#define V31_STATS_RS_START 95 +#define V31_STATS_RS_STOP 96 +#define V31_STATS_CS_START 97 +#define V31_STATS_CS_STOP 98 +#define V31_STATS_SKIN_START 99 +#define V31_STATS_SKIN_STOP 100 +#define V31_STATS_IHIST_START 101 +#define V31_STATS_IHIST_STOP 102 +#define V31_DUMMY_10 103 +#define V31_SYNC_TIMER_SETTING 104 +#define V31_ASYNC_TIMER_SETTING 105 +#define V31_CAMIF_OFF 0x000001E4 +#define V31_CAMIF_LEN 32 + +#define V31_DEMUX_OFF 0x00000284 +#define V31_DEMUX_LEN 20 + +#define V31_DEMOSAIC_0_OFF 0x00000298 +#define V31_DEMOSAIC_0_LEN 4 +/* ABF */ +#define V31_DEMOSAIC_1_OFF 0x000002A4 +#define V31_DEMOSAIC_1_LEN 180 +/* BPC */ +#define V31_DEMOSAIC_2_OFF 0x0000029C +#define V31_DEMOSAIC_2_LEN 8 + +#define V31_OUT_CLAMP_OFF 0x00000524 +#define V31_OUT_CLAMP_LEN 8 + +#define V31_OPERATION_CFG_LEN 28 + +#define V31_AXI_OUT_OFF 0x00000038 +#define V31_AXI_OUT_LEN 188 + +#define V31_FRAME_SKIP_OFF 0x00000504 +#define V31_FRAME_SKIP_LEN 32 + +#define V31_CHROMA_SUBS_OFF 0x000004F8 +#define V31_CHROMA_SUBS_LEN 12 + +#define V31_FOV_OFF 0x00000360 +#define V31_FOV_LEN 8 + +#define V31_MAIN_SCALER_OFF 0x00000368 +#define V31_MAIN_SCALER_LEN 28 + +#define V31_S2Y_OFF 0x000004D0 +#define V31_S2Y_LEN 20 + +#define V31_S2CbCr_OFF 0x000004E4 +#define V31_S2CbCr_LEN 20 + +#define V31_CHROMA_EN_OFF 0x000003C4 +#define V31_CHROMA_EN_LEN 36 + +#define V31_BLACK_LEVEL_OFF 0x00000264 +#define V31_BLACK_LEVEL_LEN 16 + +#define V31_ROLL_OFF_CFG_OFF 0x00000274 +#define V31_ROLL_OFF_CFG_LEN 16 + +#define V31_COLOR_COR_OFF 0x00000388 +#define V31_COLOR_COR_LEN 52 + +#define V31_WB_OFF 0x00000384 +#define V31_WB_LEN 4 + +#define V31_RGB_G_OFF 0x000003BC +#define V31_RGB_G_LEN 4 + +#define V31_LA_OFF 0x000003C0 +#define V31_LA_LEN 4 + +#define V31_CHROMA_SUP_OFF 0x000003E8 +#define V31_CHROMA_SUP_LEN 12 + +#define V31_MCE_OFF 0x000003E8 +#define V31_MCE_LEN 36 +#define V31_STATS_AF_OFF 0x0000053c +#define V31_STATS_AF_LEN 16 + +#define V31_STATS_AE_OFF 0x00000534 +#define V31_STATS_AE_LEN 8 + +#define V31_STATS_AWB_OFF 0x0000054c +#define V31_STATS_AWB_LEN 32 + +#define V31_STATS_IHIST_OFF 0x0000057c +#define V31_STATS_IHIST_LEN 8 + +#define V31_STATS_RS_OFF 0x0000056c +#define V31_STATS_RS_LEN 8 + +#define V31_STATS_CS_OFF 0x00000574 +#define V31_STATS_CS_LEN 8 + + +#define V31_ASF_OFF 0x000004A0 +#define V31_ASF_LEN 48 +#define V31_ASF_UPDATE_LEN 36 + +#define V31_CAPTURE_LEN 4 + +struct vfe_cmd_hw_version { + uint32_t minorVersion; + uint32_t majorVersion; + uint32_t coreVersion; +}; + +enum VFE_AXI_OUTPUT_MODE { + VFE_AXI_OUTPUT_MODE_Output1, + VFE_AXI_OUTPUT_MODE_Output2, + VFE_AXI_OUTPUT_MODE_Output1AndOutput2, + VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2, + VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1, + VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2, + VFE_AXI_LAST_OUTPUT_MODE_ENUM +}; + +enum VFE_RAW_WR_PATH_SEL { + VFE_RAW_OUTPUT_DISABLED, + VFE_RAW_OUTPUT_ENC_CBCR_PATH, + VFE_RAW_OUTPUT_VIEW_CBCR_PATH, + VFE_RAW_OUTPUT_PATH_INVALID +}; + + +#define VFE_AXI_OUTPUT_BURST_LENGTH 4 +#define VFE_MAX_NUM_FRAGMENTS_PER_FRAME 4 +#define VFE_AXI_OUTPUT_CFG_FRAME_COUNT 3 + +struct vfe_cmds_per_write_master { + uint16_t imageWidth; + uint16_t imageHeight; + uint16_t outRowCount; + uint16_t outRowIncrement; + uint32_t outFragments[VFE_AXI_OUTPUT_CFG_FRAME_COUNT] + [VFE_MAX_NUM_FRAGMENTS_PER_FRAME]; +}; + +struct vfe_cmds_axi_per_output_path { + uint8_t fragmentCount; + struct vfe_cmds_per_write_master firstWM; + struct vfe_cmds_per_write_master secondWM; +}; + +enum VFE_AXI_BURST_LENGTH { + VFE_AXI_BURST_LENGTH_IS_2 = 2, + VFE_AXI_BURST_LENGTH_IS_4 = 4, + VFE_AXI_BURST_LENGTH_IS_8 = 8, + VFE_AXI_BURST_LENGTH_IS_16 = 16 +}; + + +struct vfe_cmd_fov_crop_config { + uint8_t enable; + uint16_t firstPixel; + uint16_t lastPixel; + uint16_t firstLine; + uint16_t lastLine; +}; + +struct vfe_cmds_main_scaler_stripe_init { + uint16_t MNCounterInit; + uint16_t phaseInit; +}; + +struct vfe_cmds_scaler_one_dimension { + uint8_t enable; + uint16_t inputSize; + uint16_t outputSize; + uint32_t phaseMultiplicationFactor; + uint8_t interpolationResolution; +}; + +struct vfe_cmd_main_scaler_config { + uint8_t enable; + struct vfe_cmds_scaler_one_dimension hconfig; + struct vfe_cmds_scaler_one_dimension vconfig; + struct vfe_cmds_main_scaler_stripe_init MNInitH; + struct vfe_cmds_main_scaler_stripe_init MNInitV; +}; + +struct vfe_cmd_scaler2_config { + uint8_t enable; + struct vfe_cmds_scaler_one_dimension hconfig; + struct vfe_cmds_scaler_one_dimension vconfig; +}; + + +struct vfe_cmd_frame_skip_update { + uint32_t output1Pattern; + uint32_t output2Pattern; +}; + +struct vfe_cmd_output_clamp_config { + uint8_t minCh0; + uint8_t minCh1; + uint8_t minCh2; + uint8_t maxCh0; + uint8_t maxCh1; + uint8_t maxCh2; +}; + +struct vfe_cmd_chroma_subsample_config { + uint8_t enable; + uint8_t cropEnable; + uint8_t vsubSampleEnable; + uint8_t hsubSampleEnable; + uint8_t vCosited; + uint8_t hCosited; + uint8_t vCositedPhase; + uint8_t hCositedPhase; + uint16_t cropWidthFirstPixel; + uint16_t cropWidthLastPixel; + uint16_t cropHeightFirstLine; + uint16_t cropHeightLastLine; +}; + +enum VFE_START_INPUT_SOURCE { + VFE_START_INPUT_SOURCE_CAMIF, + VFE_START_INPUT_SOURCE_TESTGEN, + VFE_START_INPUT_SOURCE_AXI, + VFE_START_INPUT_SOURCE_INVALID +}; + +enum VFE_START_OPERATION_MODE { + VFE_START_OPERATION_MODE_CONTINUOUS, + VFE_START_OPERATION_MODE_SNAPSHOT +}; + +enum VFE_START_PIXEL_PATTERN { + VFE_BAYER_RGRGRG, + VFE_BAYER_GRGRGR, + VFE_BAYER_BGBGBG, + VFE_BAYER_GBGBGB, + VFE_YUV_YCbYCr, + VFE_YUV_YCrYCb, + VFE_YUV_CbYCrY, + VFE_YUV_CrYCbY +}; + +enum VFE_BUS_RD_INPUT_PIXEL_PATTERN { + VFE_BAYER_RAW, + VFE_YUV_INTERLEAVED, + VFE_YUV_PSEUDO_PLANAR_Y, + VFE_YUV_PSEUDO_PLANAR_CBCR +}; + +enum VFE_YUV_INPUT_COSITING_MODE { + VFE_YUV_COSITED, + VFE_YUV_INTERPOLATED +}; + + +/* 13*1 */ +#define VFE31_ROLL_OFF_INIT_TABLE_SIZE 13 +/* 13*16 */ +#define VFE31_ROLL_OFF_DELTA_TABLE_SIZE 208 + +#define VFE31_GAMMA_NUM_ENTRIES 64 + +#define VFE31_LA_TABLE_LENGTH 64 + +struct vfe_cmds_demosaic_abf { + uint8_t enable; + uint8_t forceOn; + uint8_t shift; + uint16_t lpThreshold; + uint16_t max; + uint16_t min; + uint8_t ratio; +}; + +struct vfe_cmds_demosaic_bpc { + uint8_t enable; + uint16_t fmaxThreshold; + uint16_t fminThreshold; + uint16_t redDiffThreshold; + uint16_t blueDiffThreshold; + uint16_t greenDiffThreshold; +}; + +struct vfe_cmd_demosaic_config { + uint8_t enable; + uint8_t slopeShift; + struct vfe_cmds_demosaic_abf abfConfig; + struct vfe_cmds_demosaic_bpc bpcConfig; +}; + +struct vfe_cmd_demosaic_bpc_update { + struct vfe_cmds_demosaic_bpc bpcUpdate; +}; + +struct vfe_cmd_demosaic_abf_update { + struct vfe_cmds_demosaic_abf abfUpdate; +}; + +struct vfe_cmd_white_balance_config { + uint8_t enable; + uint16_t ch2Gain; + uint16_t ch1Gain; + uint16_t ch0Gain; +}; + +enum VFE_COLOR_CORRECTION_COEF_QFACTOR { + COEF_IS_Q7_SIGNED, + COEF_IS_Q8_SIGNED, + COEF_IS_Q9_SIGNED, + COEF_IS_Q10_SIGNED +}; + +struct vfe_cmd_color_correction_config { + uint8_t enable; + enum VFE_COLOR_CORRECTION_COEF_QFACTOR coefQFactor; + int16_t C0; + int16_t C1; + int16_t C2; + int16_t C3; + int16_t C4; + int16_t C5; + int16_t C6; + int16_t C7; + int16_t C8; + int16_t K0; + int16_t K1; + int16_t K2; +}; + +#define VFE_LA_TABLE_LENGTH 256 +struct vfe_cmd_la_config { + uint8_t enable; + int16_t table[VFE_LA_TABLE_LENGTH]; +}; + +#define VFE_GAMMA_TABLE_LENGTH 256 +enum VFE_RGB_GAMMA_TABLE_SELECT { + RGB_GAMMA_CH0_SELECTED, + RGB_GAMMA_CH1_SELECTED, + RGB_GAMMA_CH2_SELECTED, + RGB_GAMMA_CH0_CH1_SELECTED, + RGB_GAMMA_CH0_CH2_SELECTED, + RGB_GAMMA_CH1_CH2_SELECTED, + RGB_GAMMA_CH0_CH1_CH2_SELECTED +}; + +struct vfe_cmd_rgb_gamma_config { + uint8_t enable; + enum VFE_RGB_GAMMA_TABLE_SELECT channelSelect; + int16_t table[VFE_GAMMA_TABLE_LENGTH]; +}; + +struct vfe_cmd_chroma_enhan_config { + uint8_t enable; + int16_t am; + int16_t ap; + int16_t bm; + int16_t bp; + int16_t cm; + int16_t cp; + int16_t dm; + int16_t dp; + int16_t kcr; + int16_t kcb; + int16_t RGBtoYConversionV0; + int16_t RGBtoYConversionV1; + int16_t RGBtoYConversionV2; + uint8_t RGBtoYConversionOffset; +}; + +struct vfe_cmd_chroma_suppression_config { + uint8_t enable; + uint8_t m1; + uint8_t m3; + uint8_t n1; + uint8_t n3; + uint8_t nn1; + uint8_t mm1; +}; + +struct vfe_cmd_asf_config { + uint8_t enable; + uint8_t smoothFilterEnabled; + uint8_t sharpMode; + uint8_t smoothCoefCenter; + uint8_t smoothCoefSurr; + uint8_t normalizeFactor; + uint8_t sharpK1; + uint8_t sharpK2; + uint8_t sharpThreshE1; + int8_t sharpThreshE2; + int8_t sharpThreshE3; + int8_t sharpThreshE4; + int8_t sharpThreshE5; + int8_t filter1Coefficients[9]; + int8_t filter2Coefficients[9]; + uint8_t cropEnable; + uint16_t cropFirstPixel; + uint16_t cropLastPixel; + uint16_t cropFirstLine; + uint16_t cropLastLine; +}; + +struct vfe_cmd_asf_update { + uint8_t enable; + uint8_t smoothFilterEnabled; + uint8_t sharpMode; + uint8_t smoothCoefCenter; + uint8_t smoothCoefSurr; + uint8_t normalizeFactor; + uint8_t sharpK1; + uint8_t sharpK2; + uint8_t sharpThreshE1; + int8_t sharpThreshE2; + int8_t sharpThreshE3; + int8_t sharpThreshE4; + int8_t sharpThreshE5; + int8_t filter1Coefficients[9]; + int8_t filter2Coefficients[9]; + uint8_t cropEnable; +}; + +enum VFE_TEST_GEN_SYNC_EDGE { + VFE_TEST_GEN_SYNC_EDGE_ActiveHigh, + VFE_TEST_GEN_SYNC_EDGE_ActiveLow +}; + + +struct vfe_cmd_bus_pm_start { + uint8_t output2YWrPmEnable; + uint8_t output2CbcrWrPmEnable; + uint8_t output1YWrPmEnable; + uint8_t output1CbcrWrPmEnable; +}; + +struct vfe_cmd_sync_timer_setting { + uint8_t whichSyncTimer; + uint8_t operation; + uint8_t polarity; + uint16_t repeatCount; + uint16_t hsyncCount; + uint32_t pclkCount; + uint32_t outputDuration; +}; + +struct vfe_cmd_async_timer_setting { + uint8_t whichAsyncTimer; + uint8_t operation; + uint8_t polarity; + uint16_t repeatCount; + uint16_t inactiveCount; + uint32_t activeCount; +}; + +struct vfe_frame_skip_counts { + uint32_t totalFrameCount; + uint32_t output1Count; + uint32_t output2Count; +}; + +enum VFE_AXI_RD_UNPACK_HBI_SEL { + VFE_AXI_RD_HBI_32_CLOCK_CYCLES, + VFE_AXI_RD_HBI_64_CLOCK_CYCLES, + VFE_AXI_RD_HBI_128_CLOCK_CYCLES, + VFE_AXI_RD_HBI_256_CLOCK_CYCLES, + VFE_AXI_RD_HBI_512_CLOCK_CYCLES, + VFE_AXI_RD_HBI_1024_CLOCK_CYCLES, + VFE_AXI_RD_HBI_2048_CLOCK_CYCLES, + VFE_AXI_RD_HBI_4096_CLOCK_CYCLES +}; + +enum VFE31_MESSAGE_ID { + MSG_ID_RESET_ACK, + MSG_ID_START_ACK, + MSG_ID_STOP_ACK, + MSG_ID_UPDATE_ACK, + MSG_ID_OUTPUT_P, + MSG_ID_OUTPUT_T, + MSG_ID_OUTPUT_S, + MSG_ID_OUTPUT_V, + MSG_ID_SNAPSHOT_DONE, + MSG_ID_STATS_AEC, + MSG_ID_STATS_AF, + MSG_ID_STATS_AWB, /* 8 */ + MSG_ID_STATS_RS, + MSG_ID_STATS_CS, + MSG_ID_STATS_IHIST, + MSG_ID_STATS_SKIN, + MSG_ID_EPOCH1, + MSG_ID_EPOCH2, + MSG_ID_SYNC_TIMER0_DONE, + MSG_ID_SYNC_TIMER1_DONE, + MSG_ID_SYNC_TIMER2_DONE, + MSG_ID_ASYNC_TIMER0_DONE, + MSG_ID_ASYNC_TIMER1_DONE, + MSG_ID_ASYNC_TIMER2_DONE, + MSG_ID_ASYNC_TIMER3_DONE, + MSG_ID_AE_OVERFLOW, + MSG_ID_AF_OVERFLOW, + MSG_ID_AWB_OVERFLOW, + MSG_ID_RS_OVERFLOW, + MSG_ID_CS_OVERFLOW, + MSG_ID_IHIST_OVERFLOW, + MSG_ID_SKIN_OVERFLOW, + MSG_ID_AXI_ERROR, + MSG_ID_CAMIF_OVERFLOW, + MSG_ID_VIOLATION, + MSG_ID_CAMIF_ERROR, + MSG_ID_BUS_OVERFLOW, +}; + +struct vfe_msg_stats{ + uint32_t buffer; + uint32_t frameCounter; +}; + + +struct vfe_frame_bpc_info { + uint32_t greenDefectPixelCount; + uint32_t redBlueDefectPixelCount; +}; + +struct vfe_frame_asf_info { + uint32_t asfMaxEdge; + uint32_t asfHbiCount; +}; + +struct vfe_msg_camif_status { + uint8_t camifState; + uint32_t pixelCount; + uint32_t lineCount; +}; + + +struct vfe31_irq_status { + uint32_t vfeIrqStatus0; + uint32_t vfeIrqStatus1; + uint32_t camifStatus; + uint32_t demosaicStatus; + uint32_t asfMaxEdge; +}; + +struct vfe_msg_output { + uint8_t output_id; + uint32_t yBuffer; + uint32_t cbcrBuffer; + struct vfe_frame_bpc_info bpcInfo; + struct vfe_frame_asf_info asfInfo; + uint32_t frameCounter; +}; + +struct vfe_message { + enum VFE31_MESSAGE_ID _d; + union { + struct vfe_msg_output msgOut; + struct vfe_msg_stats msgStats; + struct vfe_msg_camif_status msgCamifError; + } _u; +}; + +/* New one for 7x30 */ +struct msm_vfe31_cmd { + int32_t id; + uint16_t length; + void *value; +}; + +#define V31_PREVIEW_AXI_FLAG 0x00000001 +#define V31_SNAPSHOT_AXI_FLAG (0x00000001<<1) + +struct vfe31_cmd_type { + uint16_t id; + uint32_t length; + uint32_t offset; + uint32_t flag; +}; + +struct vfe31_free_buf { + spinlock_t f_lock; + uint8_t available; + uint32_t paddr; + uint32_t y_off; + uint32_t cbcr_off; +}; + +struct vfe31_output_ch { + struct vfe31_free_buf free_buf; + uint16_t output_fmt; + int8_t ch0; + int8_t ch1; + int8_t ch2; + uint32_t capture_cnt; + uint32_t frame_drop_cnt; +}; + +/* no error irq in mask 0 */ +#define VFE31_IMASK_ERROR_ONLY_0 0x0 +/* when normal case, don't want to block error status. */ +/* bit 0-21 are error irq bits */ +#define VFE31_IMASK_ERROR_ONLY_1 0x003fffff + +struct vfe31_output_path { + uint16_t output_mode; /* bitmask */ + + struct vfe31_output_ch out0; /* preview and thumbnail */ + struct vfe31_output_ch out1; /* snapshot */ + struct vfe31_output_ch out2; /* video */ +}; + +struct vfe31_frame_extra { + uint32_t greenDefectPixelCount; + uint32_t redBlueDefectPixelCount; + + uint32_t asfMaxEdge; + uint32_t asfHbiCount; + + uint32_t yWrPmStats0; + uint32_t yWrPmStats1; + uint32_t cbcrWrPmStats0; + uint32_t cbcrWrPmStats1; + + uint32_t frameCounter; +}; + +#define VFE_DISABLE_ALL_IRQS 0 +#define VFE_CLEAR_ALL_IRQS 0xffffffff + +#define VFE_VERSION 0x00000000 +#define VFE_GLOBAL_RESET 0x00000004 +#define VFE_CGC_OVERRIDE 0x0000000C +#define VFE_MODULE_CFG 0x00000010 +#define VFE_CFG_OFF 0x00000014 +#define VFE_IRQ_CMD 0x00000018 +#define VFE_IRQ_MASK_0 0x0000001C +#define VFE_IRQ_MASK_1 0x00000020 +#define VFE_IRQ_CLEAR_0 0x00000024 +#define VFE_IRQ_CLEAR_1 0x00000028 +#define VFE_IRQ_STATUS_0 0x0000002C +#define VFE_IRQ_STATUS_1 0x00000030 +#define VFE_IRQ_COMP_MASK 0x00000034 +#define VFE_BUS_CMD 0x00000038 +#define VFE_AXI_OFFSET 0x00000050 +#define VFE_BUS_STATS_PING_PONG_BASE 0x000000F4 +#define VFE_BUS_STATS_AEC_WR_PING_ADDR 0x000000F4 +#define VFE_BUS_STATS_AEC_WR_PONG_ADDR 0x000000F8 +#define VFE_BUS_STATS_AEC_UB_CFG 0x000000FC +#define VFE_BUS_STATS_AF_WR_PING_ADDR 0x00000100 +#define VFE_BUS_STATS_AF_WR_PONG_ADDR 0x00000104 +#define VFE_BUS_STATS_AF_UB_CFG 0x00000108 +#define VFE_BUS_STATS_AWB_WR_PING_ADDR 0x0000010C +#define VFE_BUS_STATS_AWB_WR_PONG_ADDR 0x00000110 +#define VFE_BUS_STATS_AWB_UB_CFG 0x00000114 +#define VFE_BUS_STATS_RS_WR_PING_ADDR 0x00000118 +#define VFE_BUS_STATS_RS_WR_PONG_ADDR 0x0000011C +#define VFE_BUS_STATS_RS_UB_CFG 0x00000120 +#define VFE_BUS_STATS_CS_WR_PING_ADDR 0x00000124 +#define VFE_BUS_STATS_CS_WR_PONG_ADDR 0x00000128 +#define VFE_BUS_STATS_CS_UB_CFG 0x0000012C +#define VFE_BUS_STATS_HIST_WR_PING_ADDR 0x00000130 +#define VFE_BUS_STATS_HIST_WR_PONG_ADDR 0x00000134 +#define VFE_BUS_STATS_HIST_UB_CFG 0x00000138 +#define VFE_BUS_STATS_SKIN_WR_PING_ADDR 0x0000013C +#define VFE_BUS_STATS_SKIN_WR_PONG_ADDR 0x00000140 +#define VFE_BUS_STATS_SKIN_UB_CFG 0x00000144 +#define VFE_BUS_PING_PONG_STATUS 0x00000180 +#define VFE_AXI_CMD 0x000001D8 +#define VFE_AXI_STATUS 0x000001DC +#define VFE_CAMIF_COMMAND 0x000001E0 +#define VFE_CAMIF_STATUS 0x00000204 +#define VFE_REG_UPDATE_CMD 0x00000260 +#define VFE_DEMUX_GAIN_0 0x00000288 +#define VFE_DEMUX_GAIN_1 0x0000028C +#define VFE_CHROMA_UP 0x0000035C +#define VFE_FRAMEDROP_ENC_Y_CFG 0x00000504 +#define VFE_FRAMEDROP_ENC_CBCR_CFG 0x00000508 +#define VFE_FRAMEDROP_ENC_Y_PATTERN 0x0000050C +#define VFE_FRAMEDROP_ENC_CBCR_PATTERN 0x00000510 +#define VFE_FRAMEDROP_VIEW_Y 0x00000514 +#define VFE_FRAMEDROP_VIEW_CBCR 0x00000518 +#define VFE_FRAMEDROP_VIEW_Y_PATTERN 0x0000051C +#define VFE_FRAMEDROP_VIEW_CBCR_PATTERN 0x00000520 +#define VFE_CLAMP_MAX 0x00000524 +#define VFE_CLAMP_MIN 0x00000528 +#define VFE_REALIGN_BUF 0x0000052C +#define VFE_STATS_CFG 0x00000530 +#define VFE_DMI_CFG 0x00000598 +#define VFE_DMI_ADDR 0x0000059C +#define VFE_DMI_DATA_LO 0x000005A4 + +struct vfe_stats_control { + uint8_t ackPending; + uint32_t nextFrameAddrBuf; + uint32_t droppedStatsFrameCount; + uint32_t bufToRender; +}; + +struct vfe31_ctrl_type { + uint16_t operation_mode; /* streaming or snapshot */ + struct vfe31_output_path outpath; + + uint32_t vfeImaskCompositePacked; + + spinlock_t stop_flag_lock; /* protects stop_ack_pending */ + spinlock_t update_ack_lock; /* protects update_ack_pending */ + spinlock_t state_lock; /* protects vstate */ + spinlock_t aec_ack_lock; /* protects aecStatsControl.ackPending */ + spinlock_t awb_ack_lock; /* protects awbStatsControl.ackPending */ + spinlock_t af_ack_lock; /* protects afStatsControl.ackPending */ + spinlock_t ihist_ack_lock; /* protects ihistStatsControl.ackPending */ + spinlock_t rs_ack_lock; /* protects rsStatsControl.ackPending */ + spinlock_t cs_ack_lock; /* protects csStatsControl.ackPending */ + + struct msm_vfe_callback *resp; + uint32_t extlen; + void *extdata; + + int8_t start_ack_pending; + int8_t stop_ack_pending; + int8_t reset_ack_pending; + int8_t update_ack_pending; + int8_t req_start_video_rec; + int8_t req_stop_video_rec; + + spinlock_t tasklet_lock; + struct list_head tasklet_q; + int vfeirq; + void __iomem *vfebase; + void *syncdata; + + struct resource *vfemem; + struct resource *vfeio; + + uint32_t stats_comp; + uint8_t vstate; + uint32_t vfe_capture_count; + + uint32_t vfeFrameId; + uint32_t output1Pattern; + uint32_t output1Period; + uint32_t output2Pattern; + uint32_t output2Period; + uint32_t vfeFrameSkipCount; + uint32_t vfeFrameSkipPeriod; + struct vfe_stats_control afStatsControl; + struct vfe_stats_control awbStatsControl; + struct vfe_stats_control aecStatsControl; + struct vfe_stats_control ihistStatsControl; + struct vfe_stats_control rsStatsControl; + struct vfe_stats_control csStatsControl; +}; + +#define statsAeNum 0 +#define statsAfNum 1 +#define statsAwbNum 2 +#define statsRsNum 3 +#define statsCsNum 4 +#define statsIhistNum 5 +#define statsSkinNum 6 + +struct vfe_cmd_stats_ack{ + uint32_t nextStatsBuf; +}; + +#define VFE_STATS_BUFFER_COUNT 3 + +struct vfe_cmd_stats_buf{ + uint32_t statsBuf[VFE_STATS_BUFFER_COUNT]; +}; + +#define VFE31_OUTPUT_MODE_PT (0x1 << 0) +#define VFE31_OUTPUT_MODE_S (0x1 << 1) +#define VFE31_OUTPUT_MODE_V (0x1 << 2) + +#endif /* __MSM_VFE31_H__ */ From 23523f0eca712248730bbf06b41f6e2f1645668f Mon Sep 17 00:00:00 2001 From: Wu-cheng Li Date: Thu, 22 Jul 2010 15:32:17 -0700 Subject: [PATCH 0698/2556] media: video: msm: Enable two VFE outputs for 8k Signed-off-by: Wu-cheng Li --- drivers/media/video/msm/msm_camera.c | 29 ++++++++++++++++++++++-- drivers/media/video/msm/msm_vfe8x.c | 7 +++--- drivers/media/video/msm/msm_vfe8x_proc.c | 3 ++- include/media/msm_camera.h | 1 + 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c index 9c636a0d6afdd..18513aa321581 100644 --- a/drivers/media/video/msm/msm_camera.c +++ b/drivers/media/video/msm/msm_camera.c @@ -1239,6 +1239,29 @@ static int msm_frame_axi_cfg(struct msm_sync *sync, } break; + case CMD_AXI_CFG_O1_AND_O2: + pmem_type = MSM_PMEM_OUTPUT1; + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->pmem_frames, pmem_type, + ®ion[0], 8); + if (!axi_data.bufnum1) { + pr_err("%s %d: pmem region lookup error\n", + __func__, __LINE__); + return -EINVAL; + } + + pmem_type = MSM_PMEM_OUTPUT2; + axi_data.bufnum2 = + msm_pmem_region_lookup(&sync->pmem_frames, pmem_type, + ®ion[axi_data.bufnum1], + (8-(axi_data.bufnum1))); + if (!axi_data.bufnum2) { + pr_err("%s %d: pmem region lookup error\n", + __func__, __LINE__); + return -EINVAL; + } + break; + case CMD_AXI_CFG_SNAP: case CMD_AXI_CFG_SNAP_O1_AND_O2: pmem_type = MSM_PMEM_THUMBNAIL; @@ -1347,8 +1370,9 @@ static int __msm_put_frame_buf(struct msm_sync *sync, if (sync->vfefn.vfe_config) rc = sync->vfefn.vfe_config(&cfgcmd, &pphy); } else { - pr_err("%s: msm_pmem_frame_vtop_lookup failed\n", - __func__); + pr_err("%s: msm_pmem_frame_vtop_lookup failed. " + "buffer=0x%lx, y_off=%d, cbcr_off=%d, fd=%d\n", + __func__, pb->buffer, pb->y_off, pb->cbcr_off, pb->fd); rc = -EINVAL; } @@ -1535,6 +1559,7 @@ static int msm_axi_config(struct msm_sync *sync, void __user *arg) switch (cfgcmd.cmd_type) { case CMD_AXI_CFG_OUT1: case CMD_AXI_CFG_OUT2: + case CMD_AXI_CFG_O1_AND_O2: case CMD_AXI_CFG_SNAP_O1_AND_O2: case CMD_AXI_CFG_VIDEO: case CMD_AXI_CFG_PREVIEW: diff --git a/drivers/media/video/msm/msm_vfe8x.c b/drivers/media/video/msm/msm_vfe8x.c index d955c64b0710f..b4943bec4bebe 100644 --- a/drivers/media/video/msm/msm_vfe8x.c +++ b/drivers/media/video/msm/msm_vfe8x.c @@ -622,8 +622,6 @@ static int vfe_config(struct msm_vfe_cfg_cmd *cmd, void *data) b = (struct msm_frame *)(cmd->value); p = *(unsigned long *)data; - b->path = MSM_FRAME_ENC; - fack.ybufaddr[0] = (uint32_t) (p + b->y_off); fack.chromabufaddr[0] = (uint32_t) (p + b->cbcr_off); @@ -634,6 +632,8 @@ static int vfe_config(struct msm_vfe_cfg_cmd *cmd, void *data) if (b->path == MSM_FRAME_ENC || b->path == MSM_FRAME_PREV_2) vfe_output2_ack(&fack); + + } break; @@ -693,7 +693,8 @@ static int vfe_config(struct msm_vfe_cfg_cmd *cmd, void *data) } break; - case CMD_AXI_CFG_SNAP_O1_AND_O2:{ + case CMD_AXI_CFG_O1_AND_O2: + case CMD_AXI_CFG_SNAP_O1_AND_O2: { BUG_ON(!axid); diff --git a/drivers/media/video/msm/msm_vfe8x_proc.c b/drivers/media/video/msm/msm_vfe8x_proc.c index 1edbacba0ae65..23ea197785b13 100644 --- a/drivers/media/video/msm/msm_vfe8x_proc.c +++ b/drivers/media/video/msm/msm_vfe8x_proc.c @@ -1337,6 +1337,7 @@ static boolean vfe_send_output2_msg(struct msm_vfe_resp *rp, (void *)pPayload, sizeof(struct vfe_msg_output)); ctrl->encPath.ackPending = TRUE; + rp->phy.output_id = MSM_FRAME_PREV_2; if (!(ctrl->vfeRequestedSnapShotCount <= 3) && (ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT)) @@ -1359,7 +1360,7 @@ static boolean vfe_send_output1_msg(struct msm_vfe_resp *rp, memcpy(&(msg->_u), (void *)pPayload, sizeof(struct vfe_msg_output)); ctrl->viewPath.ackPending = TRUE; - + rp->phy.output_id = MSM_FRAME_PREV_1; if (!(ctrl->vfeRequestedSnapShotCount <= 3) && (ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT)) ctrl->viewPath.ackPending = TRUE; diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h index 6505af9a0d9ac..c0165405e3c99 100644 --- a/include/media/msm_camera.h +++ b/include/media/msm_camera.h @@ -222,6 +222,7 @@ struct msm_camera_cfg_cmd { #define CMD_STATS_IHIST_ENABLE 38 #define CMD_STATS_RS_ENABLE 39 #define CMD_STATS_CS_ENABLE 40 +#define CMD_AXI_CFG_O1_AND_O2 41 /* output1 and output2 */ /* vfe config command: config command(from config thread)*/ struct msm_vfe_cfg_cmd { From fdbd7e4d09fea3e7a22b91eda1ea6cccdc569798 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 27 Jul 2010 11:54:56 -0700 Subject: [PATCH 0699/2556] [ARM] video: msm: Do not always compile in the MDDI support Only boards that need it should enable it. Change-Id: I837b839fffe027c9e1079291b2cea71a76027fc5 Signed-off-by: Dima Zavin --- drivers/video/msm/Kconfig | 5 +++++ drivers/video/msm/Makefile | 6 +++--- drivers/video/msm/mdp_hw_legacy.c | 5 ++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig index 938065a5a9c4e..fe5ee262d3a71 100644 --- a/drivers/video/msm/Kconfig +++ b/drivers/video/msm/Kconfig @@ -21,6 +21,11 @@ config FB_MSM_LCDC depends on FB_MSM && MSM_MDP31 default y +config FB_MSM_MDDI + bool "Support for MSM MDDI controllers" + depends on FB_MSM + default y + config GPU_MSM_KGSL tristate "MSM 3D Graphics driver for Adreno class GPUs" default n diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile index 52f5f651e8972..3fcfbd88b0480 100644 --- a/drivers/video/msm/Makefile +++ b/drivers/video/msm/Makefile @@ -20,12 +20,12 @@ obj-$(CONFIG_MSM_MDP31) += mdp_ppp31.o # MDDI interface # -obj-y += mddi.o +obj-$(CONFIG_FB_MSM_MDDI) += mddi.o # MDDI client/panel drivers # -obj-y += mddi_client_simple.o -obj-y += mddi_client_toshiba.o +obj-$(CONFIG_FB_MSM_MDDI) += mddi_client_simple.o +obj-$(CONFIG_FB_MSM_MDDI) += mddi_client_toshiba.o # MDP LCD controller driver obj-$(CONFIG_FB_MSM_LCDC) += mdp_lcdc.o diff --git a/drivers/video/msm/mdp_hw_legacy.c b/drivers/video/msm/mdp_hw_legacy.c index cf9eb1a51ccca..fdde9ffeb96bc 100644 --- a/drivers/video/msm/mdp_hw_legacy.c +++ b/drivers/video/msm/mdp_hw_legacy.c @@ -21,6 +21,7 @@ #define MDP_CMD_DEBUG_ACCESS_BASE (0x10000) +#ifdef CONFIG_FB_MSM_MDDI static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, uint32_t width, uint32_t height, uint32_t x, uint32_t y) @@ -80,16 +81,18 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, mdp_writel(mdp, 0, MDP_DMA_P_START); #endif } - +#endif int mdp_hw_init(struct mdp_info *mdp) { int n; +#ifdef CONFIG_FB_MSM_MDDI n = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp, MDP_DMA_P_DONE, mdp_dma_to_mddi); if (n) return n; +#endif mdp_writel(mdp, 0, MDP_INTR_ENABLE); From 1a0bd96fb4fd8f3bf52af22b0de04d01d7ecde32 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 27 Jul 2010 15:08:13 -0700 Subject: [PATCH 0700/2556] [ARM] media: video: msm: Don't force V4L2 on when using MSM camera Currently, the primary MSM camera driver does not rely on V4L2. A V4L2 interface does exist, but is very minimal and is not well tested. Hence, we add a separate config option for enabling a V4L2 driver interface, and allow the main MSM camera code to be compiled without V4L2. Change-Id: Idc01e75a42c1906d8cd4eff757a15b7c0d942f69 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/include/mach/camera.h | 2 ++ drivers/media/video/Kconfig | 18 ++---------------- drivers/media/video/msm/Kconfig | 24 ++++++++++++++++++++++++ drivers/media/video/msm/Makefile | 3 ++- drivers/media/video/msm/msm_camera.c | 6 ++++++ 5 files changed, 36 insertions(+), 17 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/camera.h b/arch/arm/mach-msm/include/mach/camera.h index f4ad525ac7334..1da814f3d3fbf 100644 --- a/arch/arm/mach-msm/include/mach/camera.h +++ b/arch/arm/mach-msm/include/mach/camera.h @@ -228,6 +228,7 @@ struct axidata { uint32_t bufnum3; }; +#ifdef CONFIG_MSM_CAMERA_V4L2 /* Below functions are added for V4L2 kernel APIs */ struct msm_v4l2_driver { struct msm_sync *sync; @@ -244,6 +245,7 @@ struct msm_v4l2_driver { int msm_v4l2_register(struct msm_v4l2_driver *); int msm_v4l2_unregister(struct msm_v4l2_driver *); +#endif void msm_camvfe_init(void); int msm_camvfe_check(void *); diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 2e2c0416b2cb0..3459b7814989f 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -951,25 +951,11 @@ config USB_S2255 endif # V4L_USB_DRIVERS -comment "Qualcomm MSM Camera And Video" - -menuconfig MSM_CAMERA - bool "Qualcomm MSM camera and video capture support" - depends on ARCH_MSM - help - Say Y here to enable selecting the video adapters for - Qualcomm msm camera and video encoding - -config MSM_CAMERA_DEBUG - bool "Qualcomm MSM camera debugging with printk" - depends on MSM_CAMERA - help - Enable printk() debug for msm camera +endif # VIDEO_CAPTURE_DRIVERS +# MSM camera does not require V4L2 source "drivers/media/video/msm/Kconfig" -endif # VIDEO_CAPTURE_DRIVERS - menuconfig V4L_MEM2MEM_DRIVERS bool "Memory-to-memory multimedia devices" depends on VIDEO_V4L2 diff --git a/drivers/media/video/msm/Kconfig b/drivers/media/video/msm/Kconfig index 00499987ed46b..b4796d33ccb13 100644 --- a/drivers/media/video/msm/Kconfig +++ b/drivers/media/video/msm/Kconfig @@ -1,3 +1,27 @@ +comment "Qualcomm MSM Camera And Video" + +menuconfig MSM_CAMERA + bool "Qualcomm MSM camera and video capture support" + depends on ARCH_MSM + help + Say Y here to enable selecting the video adapters for + Qualcomm msm camera and video encoding + +config MSM_CAMERA_V4L2 + bool "Video For Linux interface to MSM camera" + depends on MSM_CAMERA && VIDEO_V4L2 && EXPERIMENTAL + default y + help + Say Y here to enable the V4L2 interface for the MSM camera. + Not everything works through this interface and it has not + been thoroughly tested. + +config MSM_CAMERA_DEBUG + bool "Qualcomm MSM camera debugging with printk" + depends on MSM_CAMERA + help + Enable printk() debug for msm camera + comment "Camera Sensor Selection" config MT9T013 bool "Sensor mt9t013 (BAYER 3M)" diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile index b59be469e1562..61e370c921d91 100755 --- a/drivers/media/video/msm/Makefile +++ b/drivers/media/video/msm/Makefile @@ -1,7 +1,8 @@ obj-$(CONFIG_MT9T013) += mt9t013.o mt9t013_reg.o obj-$(CONFIG_MT9D112) += mt9d112.o mt9d112_reg.o obj-$(CONFIG_MT9P012) += mt9p012_fox.o mt9p012_reg.o -obj-$(CONFIG_MSM_CAMERA) += msm_camera.o msm_v4l2.o +obj-$(CONFIG_MSM_CAMERA) += msm_camera.o +obj-$(CONFIG_MSM_CAMERA_V4L2) += msm_v4l2.o obj-$(CONFIG_S5K3E2FX) += s5k3e2fx.o obj-$(CONFIG_ARCH_MSM7X00A) += msm_vfe7x.o msm_io7x.o obj-$(CONFIG_ARCH_QSD8X50) += msm_vfe8x.o msm_vfe8x_proc.o msm_io8x.o diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c index 18513aa321581..f44db8168bee5 100644 --- a/drivers/media/video/msm/msm_camera.c +++ b/drivers/media/video/msm/msm_camera.c @@ -948,6 +948,7 @@ static int msm_get_stats(struct msm_sync *sync, void __user *arg) se.ctrl_cmd.resp_fd = ctrl->resp_fd; break; +#ifdef CONFIG_MSM_CAMERA_V4L2 case MSM_CAM_Q_V4L2_REQ: /* control command from v4l2 client */ ctrl = (struct msm_ctrl_cmd *)(qcmd->command); @@ -970,6 +971,7 @@ static int msm_get_stats(struct msm_sync *sync, void __user *arg) se.ctrl_cmd.type = ctrl->type; se.ctrl_cmd.length = ctrl->length; break; +#endif default: rc = -EFAULT; @@ -2413,6 +2415,7 @@ static int msm_open_control(struct inode *inode, struct file *filep) return rc; } +#ifdef CONFIG_MSM_CAMERA_V4L2 static int __msm_v4l2_control(struct msm_sync *sync, struct msm_ctrl_cmd *out) { @@ -2449,6 +2452,7 @@ static int __msm_v4l2_control(struct msm_sync *sync, CDBG("%s: rc %d\n", __func__, rc); return rc; } +#endif static const struct file_operations msm_fops_config = { .owner = THIS_MODULE, @@ -2511,6 +2515,7 @@ static int msm_tear_down_cdev(struct msm_device *msm, dev_t devno) return 0; } +#ifdef CONFIG_MSM_CAMERA_V4L2 int msm_v4l2_register(struct msm_v4l2_driver *drv) { /* FIXME: support multiple sensors */ @@ -2537,6 +2542,7 @@ int msm_v4l2_unregister(struct msm_v4l2_driver *drv) return 0; } EXPORT_SYMBOL(msm_v4l2_unregister); +#endif static int msm_sync_init(struct msm_sync *sync, struct platform_device *pdev, From f197d216d267b5dcecc1be57b06b5490cb5e3337 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 26 Jul 2010 16:12:33 -0700 Subject: [PATCH 0701/2556] [ARM] msm: mahimahi: turn off unneeded options in defconfig Change-Id: Iecd777cdcc55900ee11e2a43b4c87c8f55f8a4b8 Signed-off-by: Dima Zavin --- arch/arm/configs/mahimahi_defconfig | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/arch/arm/configs/mahimahi_defconfig b/arch/arm/configs/mahimahi_defconfig index 7acd4e1672a2b..ade2ec5e4e0ed 100644 --- a/arch/arm/configs/mahimahi_defconfig +++ b/arch/arm/configs/mahimahi_defconfig @@ -169,8 +169,6 @@ CONFIG_DM_UEVENT=y CONFIG_NETDEVICES=y CONFIG_IFB=y CONFIG_DUMMY=y -CONFIG_NET_ETHERNET=y -CONFIG_SMC91X=y CONFIG_BCM4329=m CONFIG_PPP=y CONFIG_PPP_ASYNC=y @@ -185,7 +183,6 @@ CONFIG_INPUT_KEYRESET=y # CONFIG_INPUT_KEYBOARD is not set # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_TOUCHSCREEN=y -CONFIG_TOUCHSCREEN_MSM=y CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y CONFIG_INPUT_MISC=y CONFIG_INPUT_KEYCHORD=y @@ -212,31 +209,12 @@ CONFIG_REGULATOR=y CONFIG_REGULATOR_DEBUG=y CONFIG_REGULATOR_TPS65023=y CONFIG_MEDIA_SUPPORT=y -CONFIG_VIDEO_DEV=y -# CONFIG_VIDEO_ALLOW_V4L1 is not set -CONFIG_MEDIA_TUNER_CUSTOMISE=y -# CONFIG_MEDIA_TUNER_SIMPLE is not set -# CONFIG_MEDIA_TUNER_TDA8290 is not set -# CONFIG_MEDIA_TUNER_TDA827X is not set -# CONFIG_MEDIA_TUNER_TDA18271 is not set -# CONFIG_MEDIA_TUNER_TDA9887 is not set -# CONFIG_MEDIA_TUNER_TEA5761 is not set -# CONFIG_MEDIA_TUNER_TEA5767 is not set -# CONFIG_MEDIA_TUNER_MT20XX is not set -# CONFIG_MEDIA_TUNER_MT2060 is not set -# CONFIG_MEDIA_TUNER_MT2266 is not set -# CONFIG_MEDIA_TUNER_MT2131 is not set -# CONFIG_MEDIA_TUNER_QT1010 is not set -# CONFIG_MEDIA_TUNER_XC2028 is not set -# CONFIG_MEDIA_TUNER_XC5000 is not set -# CONFIG_MEDIA_TUNER_MXL5005S is not set -# CONFIG_MEDIA_TUNER_MXL5007T is not set -# CONFIG_MEDIA_TUNER_MC44S803 is not set +# CONFIG_IR_CORE is not set CONFIG_MSM_CAMERA=y CONFIG_S5K3E2FX=y -CONFIG_DAB=y CONFIG_VIDEO_OUTPUT_CONTROL=y CONFIG_FB=y +# CONFIG_FB_MSM_MDDI is not set CONFIG_GPU_MSM_KGSL=y CONFIG_MSM_KGSL_MMU=y CONFIG_USB_GADGET=y From 79432bd13924c16dead6abbd8fc0ebfaacf33b3a Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 28 Jul 2010 12:35:43 -0700 Subject: [PATCH 0702/2556] misc: move akm8976 driver out of deprecated i2c/chips into misc Change-Id: If9e79d8f56eae3894edc75e14049273f74f4e952 Signed-off-by: Dima Zavin --- drivers/i2c/chips/Kconfig | 6 ------ drivers/i2c/chips/Makefile | 1 - drivers/misc/Kconfig | 6 ++++++ drivers/misc/Makefile | 1 + drivers/{i2c/chips => misc}/akm8976.c | 0 5 files changed, 7 insertions(+), 7 deletions(-) rename drivers/{i2c/chips => misc}/akm8976.c (100%) diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index f63300040528c..394b2c4bf3f2c 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -6,12 +6,6 @@ menu "Miscellaneous I2C Chip support" -config SENSORS_AKM8976 - tristate "AKM8976 Compass Driver" - depends on I2C - help - AKM8976 Compass Driver implemented by HTC. - config SENSORS_MT9T013 tristate "MT9T013 Camera Driver" depends on I2C diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index 30edf8a07439c..de0eecdff7f12 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -10,7 +10,6 @@ # * I/O expander drivers go to drivers/gpio # -obj-$(CONFIG_SENSORS_AKM8976) += akm8976.o obj-$(CONFIG_SENSORS_MT9T013) += mt9t013.o obj-$(CONFIG_VP_A1026) += a1026.o diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 1d9eb0639b307..8bb48e4bd8463 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -396,6 +396,12 @@ config SENSORS_AKM8973 help AKM8973 Compass Driver implemented by HTC. +config SENSORS_AKM8976 + tristate "AKM8976 Compass Driver" + depends on I2C + help + AKM8976 Compass Driver implemented by HTC. + config EP93XX_PWM tristate "EP93xx PWM support" depends on ARCH_EP93XX diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index c6f6832a1278f..201af28e38305 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -49,4 +49,5 @@ obj-$(CONFIG_WL127X_RFKILL) += wl127x-rfkill.o obj-$(CONFIG_APANIC) += apanic.o obj-$(CONFIG_SENSORS_AK8975) += akm8975.o obj-$(CONFIG_SENSORS_AKM8973) += akm8973.o +obj-$(CONFIG_SENSORS_AKM8976) += akm8976.o obj-$(CONFIG_MSM_720P_CORE) += video_core/720p/ diff --git a/drivers/i2c/chips/akm8976.c b/drivers/misc/akm8976.c similarity index 100% rename from drivers/i2c/chips/akm8976.c rename to drivers/misc/akm8976.c From f5fde172c2209d749da7b2c58b28f9dd15a69f2c Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 28 Jul 2010 12:37:38 -0700 Subject: [PATCH 0703/2556] misc: move a1026 driver out of deprecated i2c/chips into misc Change-Id: Ia16f915e5e6edbc6f9a5de984206a8e7a15b466d Signed-off-by: Dima Zavin --- drivers/i2c/chips/Kconfig | 6 ------ drivers/i2c/chips/Makefile | 1 - drivers/misc/Kconfig | 6 ++++++ drivers/misc/Makefile | 1 + drivers/{i2c/chips => misc}/a1026.c | 0 5 files changed, 7 insertions(+), 7 deletions(-) rename drivers/{i2c/chips => misc}/a1026.c (100%) diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index 394b2c4bf3f2c..f58e49a6d2cb0 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -13,10 +13,4 @@ config SENSORS_MT9T013 help MT9T013 Camera Driver implemented by HTC. -config VP_A1026 - tristate "A1026 Voice Processor Driver" - depends on I2C - help - A1026 Voice Processor Driver implemented by HTC. - endmenu diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index de0eecdff7f12..23b7b7b2b7ea4 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -11,7 +11,6 @@ # obj-$(CONFIG_SENSORS_MT9T013) += mt9t013.o -obj-$(CONFIG_VP_A1026) += a1026.o ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) EXTRA_CFLAGS += -DDEBUG diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 8bb48e4bd8463..0ba84caf4fd36 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -402,6 +402,12 @@ config SENSORS_AKM8976 help AKM8976 Compass Driver implemented by HTC. +config VP_A1026 + tristate "A1026 Voice Processor Driver" + depends on I2C + help + A1026 Voice Processor Driver implemented by HTC. + config EP93XX_PWM tristate "EP93xx PWM support" depends on ARCH_EP93XX diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 201af28e38305..8d61e582a0beb 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -50,4 +50,5 @@ obj-$(CONFIG_APANIC) += apanic.o obj-$(CONFIG_SENSORS_AK8975) += akm8975.o obj-$(CONFIG_SENSORS_AKM8973) += akm8973.o obj-$(CONFIG_SENSORS_AKM8976) += akm8976.o +obj-$(CONFIG_VP_A1026) += a1026.o obj-$(CONFIG_MSM_720P_CORE) += video_core/720p/ diff --git a/drivers/i2c/chips/a1026.c b/drivers/misc/a1026.c similarity index 100% rename from drivers/i2c/chips/a1026.c rename to drivers/misc/a1026.c From 9d0b67d385a028e1fc10b4aec3cd9a282e3ae0fb Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 28 Jul 2010 12:41:55 -0700 Subject: [PATCH 0704/2556] [ARM] msm: mahimahi: add akm8973/a1026 to defconfig Change-Id: I08095a0e5c77bb4dae63e609206d5584b973b9a6 Signed-off-by: Dima Zavin --- arch/arm/configs/mahimahi_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/configs/mahimahi_defconfig b/arch/arm/configs/mahimahi_defconfig index ade2ec5e4e0ed..fcb9c10d516f0 100644 --- a/arch/arm/configs/mahimahi_defconfig +++ b/arch/arm/configs/mahimahi_defconfig @@ -161,6 +161,8 @@ CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y CONFIG_BLK_DEV_LOOP=y CONFIG_UID_STAT=y +CONFIG_SENSORS_AKM8973=y +CONFIG_VP_A1026=y CONFIG_MD=y CONFIG_BLK_DEV_DM=y CONFIG_DM_DEBUG=y From 0a70c174f494c3449af8f3fe6d44ebc3032ddde1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 29 Jul 2010 18:47:21 -0700 Subject: [PATCH 0705/2556] [ARM] msm: camera: Replace dmac_inv_range hack with dmac_map_area hack Change-Id: I2a8a0659df765bc710d4568b2dc9779c0242f259 --- drivers/media/video/msm/msm_camera.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c index f44db8168bee5..9353695a83fa5 100644 --- a/drivers/media/video/msm/msm_camera.c +++ b/drivers/media/video/msm/msm_camera.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -35,7 +36,6 @@ #include #include -#define dmac_inv_range(a,b) WARN(1, "need cache invalidate\n") #define MSM_MAX_CAMERA_SENSORS 5 #define CAMERA_STOP_SNAPSHOT 42 @@ -1685,8 +1685,9 @@ static int msm_get_pic(struct msm_sync *sync, void __user *arg) __func__, pic_pmem_region.kvaddr, end); - dmac_inv_range((const void *)pic_pmem_region.kvaddr, - (const void *)end); + /* HACK: Invalidate buffer */ + dmac_map_area((void*)pic_pmem_region.kvaddr, pic_pmem_region.len, + DMA_FROM_DEVICE); CDBG("%s: copy snapshot frame to user\n", __func__); if (copy_to_user((void *)arg, From bb809a1ef8d7729a706f7b9fbb2981b39da0c3ff Mon Sep 17 00:00:00 2001 From: Wu-cheng Li Date: Fri, 30 Jul 2010 09:58:11 -0700 Subject: [PATCH 0706/2556] msm: camera: invalidate preview, snapshot and stats buffers Signed-off-by: Wu-cheng Li --- drivers/media/video/msm/msm_camera.c | 46 ++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c index 9353695a83fa5..42cd3adb75054 100644 --- a/drivers/media/video/msm/msm_camera.c +++ b/drivers/media/video/msm/msm_camera.c @@ -244,6 +244,10 @@ static int msm_pmem_table_add(struct hlist_head *ptype, region->file = file; memcpy(®ion->info, info, sizeof(region->info)); + if (info->vfe_can_write) { + dmac_map_area((void*)region->kvaddr, region->len, DMA_FROM_DEVICE); + } + hlist_add_head(&(region->list), ptype); return 0; @@ -289,6 +293,8 @@ static int msm_pmem_frame_ptov_lookup(struct msm_sync *sync, region->info.cbcr_off) && region->info.vfe_can_write) { *pmem_region = region; + dmac_unmap_area((void*)region->kvaddr, region->len, + DMA_FROM_DEVICE); region->info.vfe_can_write = !take_from_vfe; return 0; } @@ -308,6 +314,8 @@ static unsigned long msm_pmem_stats_ptov_lookup(struct msm_sync *sync, /* offset since we could pass vaddr inside a * registered pmem buffer */ *fd = region->info.fd; + dmac_unmap_area((void*)region->kvaddr, region->len, + DMA_FROM_DEVICE); region->info.vfe_can_write = 0; return (unsigned long)(region->info.vaddr); } @@ -330,6 +338,7 @@ static unsigned long msm_pmem_frame_vtop_lookup(struct msm_sync *sync, (region->info.cbcr_off == cbcroff) && (region->info.fd == fd) && (region->info.vfe_can_write == 0)) { + dmac_map_area((void*)region->kvaddr, region->len, DMA_FROM_DEVICE); region->info.vfe_can_write = 1; return region->paddr; } @@ -350,6 +359,7 @@ static unsigned long msm_pmem_stats_vtop_lookup( if (((unsigned long)(region->info.vaddr) == buffer) && (region->info.fd == fd) && region->info.vfe_can_write == 0) { + dmac_map_area((void*)region->kvaddr, region->len, DMA_FROM_DEVICE); region->info.vfe_can_write = 1; return region->paddr; } @@ -381,6 +391,10 @@ static int __msm_pmem_table_del(struct msm_sync *sync, pinfo->fd == region->info.fd) { hlist_del(node); put_pmem_file(region->file); + if (region->info.vfe_can_write) { + dmac_unmap_area((void*)region->kvaddr, region->len, + DMA_FROM_DEVICE); + } kfree(region); } } @@ -396,6 +410,10 @@ static int __msm_pmem_table_del(struct msm_sync *sync, pinfo->fd == region->info.fd) { hlist_del(node); put_pmem_file(region->file); + if (region->info.vfe_can_write) { + dmac_unmap_area((void*)region->kvaddr, region->len, + DMA_FROM_DEVICE); + } kfree(region); } } @@ -1634,7 +1652,8 @@ static int __msm_get_pic(struct msm_sync *sync, struct msm_ctrl_cmd *ctrl) static int msm_get_pic(struct msm_sync *sync, void __user *arg) { struct msm_ctrl_cmd ctrlcmd; - struct msm_pmem_region pic_pmem_region; + struct msm_pmem_region *pic_pmem_region = NULL, *region; + struct hlist_node *node, *n; int rc; unsigned long end; int cline_mask; @@ -1666,28 +1685,31 @@ static int msm_get_pic(struct msm_sync *sync, void __user *arg) } } - if (msm_pmem_region_lookup(&sync->pmem_frames, - MSM_PMEM_MAINIMG, - &pic_pmem_region, 1) == 0) { - if(msm_pmem_region_lookup(&sync->pmem_frames, - MSM_PMEM_RAW_MAINIMG, - &pic_pmem_region, 1) == 0) { - pr_err("%s pmem region lookup error\n", __func__); - return -EIO; + hlist_for_each_entry_safe(region, node, n, &sync->pmem_frames, list) { + if (region->info.vfe_can_write && + (region->info.type == MSM_PMEM_MAINIMG || + region->info.type == MSM_PMEM_RAW_MAINIMG)) { + pic_pmem_region = region; + break; } } + if (!pic_pmem_region) { + pr_err("%s pmem region lookup error\n", __func__); + return -EIO; + } cline_mask = cache_line_size() - 1; - end = pic_pmem_region.kvaddr + pic_pmem_region.len; + end = pic_pmem_region->kvaddr + pic_pmem_region->len; end = (end + cline_mask) & ~cline_mask; pr_info("%s: flushing cache for [%08lx, %08lx)\n", __func__, - pic_pmem_region.kvaddr, end); + pic_pmem_region->kvaddr, end); /* HACK: Invalidate buffer */ - dmac_map_area((void*)pic_pmem_region.kvaddr, pic_pmem_region.len, + dmac_unmap_area((void*)pic_pmem_region->kvaddr, pic_pmem_region->len, DMA_FROM_DEVICE); + pic_pmem_region->info.vfe_can_write = 0; CDBG("%s: copy snapshot frame to user\n", __func__); if (copy_to_user((void *)arg, From 3550104a02103980798aebd225b2994a76f651e8 Mon Sep 17 00:00:00 2001 From: Wu-cheng Li Date: Wed, 28 Jul 2010 10:02:01 -0700 Subject: [PATCH 0707/2556] msm: camera: fix frame drop duing preview. Without this fix, one frame was dropped when exposure time and gain were written. The preview was not smooth. Signed-off-by: Wu-cheng Li --- drivers/media/video/msm/s5k3e2fx.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/msm/s5k3e2fx.c b/drivers/media/video/msm/s5k3e2fx.c index 84a8a9516a88f..a2ab9ba179dcd 100644 --- a/drivers/media/video/msm/s5k3e2fx.c +++ b/drivers/media/video/msm/s5k3e2fx.c @@ -43,6 +43,11 @@ static uint16_t g_usModuleVersion; /*0: rev.4, 1: rev.5 */ #define GROUPED_PARAMETER_HOLD 0x01 #define GROUPED_PARAMETER_UPDATE 0x00 +/* Greenish in low light */ +#define REG_MASK_CORRUPTED_FRAMES 0x0105 +#define MASK 0x01 +#define NO_MASK 0x00 + /* PLL Registers */ #define REG_PRE_PLL_CLK_DIV 0x0305 #define REG_PLL_MULTIPLIER_MSB 0x0306 @@ -2095,6 +2100,27 @@ static int s5k3e2fx_setting(enum msm_s_reg_update rupdate, {0x3063, 0x16}, } }; + + /* Most registers are directly applied at next frame after + writing except shutter and analog gain. Shutter and gain are + applied at 2nd or 1st frame later depending on register + writing time. When the camera is switched from preview to + snapshot, the first frame may have wrong shutter/gain and + should be discarded. The register REG_MASK_CORRUPTED_FRAMES + can discard the frame that has wrong shutter/gain. But in + preview mode, the frames should not be dropped. Otherwise + the preview will not be smooth. */ + if (rt == S_RES_PREVIEW) { + /* Frames will be not discarded after exposure and gain are + written. */ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_MASK_CORRUPTED_FRAMES, NO_MASK); + } else { + /* Solve greenish in lowlight. Prevent corrupted frame */ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_MASK_CORRUPTED_FRAMES, MASK); + } + /* solve greenish: hold for both */ rc = s5k3e2fx_i2c_write_b( s5k3e2fx_client->addr, @@ -2417,9 +2443,6 @@ static int s5k3e2fx_probe_init_lens_correction( s5k3e2fx_i2c_write_table(&lc_setting[g_usModuleVersion][0], NUM_LC_REG); -+ /* Solve EVT5 greenish in lowlight, prevent corrupted frame*/ -+ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x105,0x1); - /*20090811 separates the EVT4/EVT5 sensor init and LC setting end */ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, S5K3E2FX_REG_MODE_SELECT, From cab2dfb854ccfd08b3ad25c46eb63c8a7c2806c4 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Thu, 12 Aug 2010 17:32:11 -0400 Subject: [PATCH 0708/2556] input: misc: cm6302: Fix return values for capella_cm3602_enable and disable Signed-off-by: Mike Lockwood --- drivers/input/misc/capella_cm3602.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/input/misc/capella_cm3602.c b/drivers/input/misc/capella_cm3602.c index 55de6b3d51e65..b03eb62482ef2 100644 --- a/drivers/input/misc/capella_cm3602.c +++ b/drivers/input/misc/capella_cm3602.c @@ -60,30 +60,27 @@ static irqreturn_t capella_cm3602_irq_handler(int irq, void *data) static int capella_cm3602_enable(struct capella_cm3602_data *data) { - int rc; D("%s\n", __func__); if (data->enabled) { D("%s: already enabled\n", __func__); - return 0; - } - data->pdata->power(1); - data->enabled = !rc; - if (!rc) + } else { + data->pdata->power(1); + data->enabled = 1; capella_cm3602_report(data); - return rc; + } + return 0; } static int capella_cm3602_disable(struct capella_cm3602_data *data) { - int rc = -EIO; D("%s\n", __func__); - if (!data->enabled) { + if (data->enabled) { + data->pdata->power(0); + data->enabled = 0; + } else { D("%s: already disabled\n", __func__); - return 0; } - data->pdata->power(0); - data->enabled = 0; - return rc; + return 0; } static int capella_cm3602_setup(struct capella_cm3602_data *ip) From a3c933b03bfa691f877a4661f2e2f6df68cf4bc4 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 27 Aug 2010 15:30:54 -0700 Subject: [PATCH 0709/2556] [ARM] msm: mahimahi: Switch to level interrupt for wlan Signed-off-by: Dmitry Shmidt --- arch/arm/mach-msm/board-mahimahi-wifi.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi-wifi.c b/arch/arm/mach-msm/board-mahimahi-wifi.c index 1657092527ee8..1ad2e6e896886 100644 --- a/arch/arm/mach-msm/board-mahimahi-wifi.c +++ b/arch/arm/mach-msm/board-mahimahi-wifi.c @@ -77,7 +77,7 @@ static struct resource mahimahi_wifi_resources[] = { .name = "bcm4329_wlan_irq", .start = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_WIFI_IRQ), .end = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_WIFI_IRQ), - .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE, + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE, }, }; @@ -101,7 +101,7 @@ static struct platform_device mahimahi_wifi_device = { extern unsigned char *get_wifi_nvs_ram(void); extern int wifi_calibration_size_set(void); -static unsigned mahimahi_wifi_update_nvs(char *str) +static unsigned mahimahi_wifi_update_nvs(char *str, int add_flag) { #define NVS_LEN_OFFSET 0x0C #define NVS_DATA_OFFSET 0x40 @@ -116,8 +116,13 @@ static unsigned mahimahi_wifi_update_nvs(char *str) /* if the last byte in NVRAM is 0, trim it */ if (ptr[NVS_DATA_OFFSET + len - 1] == 0) len -= 1; - strcpy(ptr + NVS_DATA_OFFSET + len, str); - len += strlen(str); + if (add_flag) { + strcpy(ptr + NVS_DATA_OFFSET + len, str); + len += strlen(str); + } else { + if (strnstr(ptr + NVS_DATA_OFFSET, str, len)) + len -= strlen(str); + } memcpy(ptr + NVS_LEN_OFFSET, &len, sizeof(len)); wifi_calibration_size_set(); return 0; @@ -131,7 +136,7 @@ static int __init mahimahi_wifi_init(void) return 0; printk("%s: start\n", __func__); - mahimahi_wifi_update_nvs("sd_oobonly=1\r\n"); + mahimahi_wifi_update_nvs("sd_oobonly=1\r\n", 0); mahimahi_init_wifi_mem(); ret = platform_device_register(&mahimahi_wifi_device); return ret; From bec5a63c18ad704e77e00a43586b2f94eb3db310 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 2 Sep 2010 17:37:30 -0700 Subject: [PATCH 0710/2556] [ARM] msm: mahimahi: wlan: Fix BT-coex in A2DP mode Signed-off-by: Dmitry Shmidt --- arch/arm/mach-msm/board-mahimahi-wifi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/board-mahimahi-wifi.c b/arch/arm/mach-msm/board-mahimahi-wifi.c index 1ad2e6e896886..8cd24766b03c4 100644 --- a/arch/arm/mach-msm/board-mahimahi-wifi.c +++ b/arch/arm/mach-msm/board-mahimahi-wifi.c @@ -137,6 +137,7 @@ static int __init mahimahi_wifi_init(void) printk("%s: start\n", __func__); mahimahi_wifi_update_nvs("sd_oobonly=1\r\n", 0); + mahimahi_wifi_update_nvs("btc_params70=0x32\r\n", 1); mahimahi_init_wifi_mem(); ret = platform_device_register(&mahimahi_wifi_device); return ret; From 07f6bfa199bca0b83092adeff8d7fcfc9a829ae9 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Mon, 20 Sep 2010 10:36:44 -0700 Subject: [PATCH 0711/2556] ARM: mahimahi: config: Enable L2TP Signed-off-by: Dmitry Shmidt --- arch/arm/configs/mahimahi_defconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/configs/mahimahi_defconfig b/arch/arm/configs/mahimahi_defconfig index fcb9c10d516f0..22757338996e2 100644 --- a/arch/arm/configs/mahimahi_defconfig +++ b/arch/arm/configs/mahimahi_defconfig @@ -131,6 +131,10 @@ CONFIG_IP_NF_TARGET_REDIRECT=y CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_L2TP=y +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=y +CONFIG_L2TP_ETH=y CONFIG_NET_SCHED=y CONFIG_NET_SCH_HTB=y CONFIG_NET_SCH_INGRESS=y From 7d0a2dc95109ca1f75d0643ac8008305f46662c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 3 Jun 2010 21:34:56 -0700 Subject: [PATCH 0712/2556] [ARM] msm: mahimahi: Debounce power key. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ifbe88223e566b7f5627e19a03a600cdfa079522a Signed-off-by: Arve Hjønnevåg --- arch/arm/mach-msm/board-mahimahi-keypad.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/board-mahimahi-keypad.c b/arch/arm/mach-msm/board-mahimahi-keypad.c index 65078cb7e8f7b..ab38847d15e21 100644 --- a/arch/arm/mach-msm/board-mahimahi-keypad.c +++ b/arch/arm/mach-msm/board-mahimahi-keypad.c @@ -88,6 +88,7 @@ static struct gpio_event_direct_entry mahimahi_keypad_key_map[] = { static struct gpio_event_input_info mahimahi_keypad_key_info = { .info.func = gpio_event_input_func, .info.no_suspend = true, + .debounce_time.tv.nsec = 5 * NSEC_PER_MSEC, .flags = 0, .type = EV_KEY, .keymap = mahimahi_keypad_key_map, From 3365436e938ce274ec6428d810a9b759be36591f Mon Sep 17 00:00:00 2001 From: "Kevin.Ch Lee" Date: Mon, 7 Jun 2010 13:42:32 -0700 Subject: [PATCH 0713/2556] [ARM] msm: sapphire: give 3MB back to the system (with radio update) Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-sapphire.c | 4 ++-- arch/arm/mach-msm/board-sapphire.h | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index 9ceb8cf51a200..0117c503e6972 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -1264,13 +1264,13 @@ static void __init sapphire_fixup(struct machine_desc *desc, struct tag *tags, if (smi_sz == 32) { mi->bank[0].size = (84*1024*1024); } else if (smi_sz == 64) { - mi->bank[0].size = (101*1024*1024); + mi->bank[0].size = (104*1024*1024); } else { printk(KERN_ERR "can not get smi size\n"); /*Give a default value when not get smi size*/ smi_sz = 64; - mi->bank[0].size = (101*1024*1024); + mi->bank[0].size = (104*1024*1024); printk(KERN_ERR "use default : smisize=%d\n", smi_sz); } } diff --git a/arch/arm/mach-msm/board-sapphire.h b/arch/arm/mach-msm/board-sapphire.h index 170309362d1ac..2e6f6764b09ac 100644 --- a/arch/arm/mach-msm/board-sapphire.h +++ b/arch/arm/mach-msm/board-sapphire.h @@ -20,7 +20,7 @@ #define MSM_SMI_SIZE 0x00800000 #define MSM_EBI_BASE 0x10000000 -#define MSM_EBI_SIZE 0x06e00000 +#define MSM_EBI_SIZE 0x07100000 #define MSM_PMEM_GPU0_BASE 0x00000000 #define MSM_PMEM_GPU0_SIZE 0x00700000 @@ -28,7 +28,7 @@ #define SMI64_MSM_PMEM_MDP_BASE 0x02000000 #define SMI64_MSM_PMEM_MDP_SIZE 0x00800000 -#define SMI64_MSM_PMEM_ADSP_BASE 0x02800000 +#define SMI64_MSM_PMEM_ADSP_BASE 0x02800000 #define SMI64_MSM_PMEM_ADSP_SIZE 0x00800000 #define SMI64_MSM_PMEM_CAMERA_BASE 0x03000000 @@ -38,7 +38,7 @@ #define SMI64_MSM_FB_SIZE 0x00100000 #define SMI64_MSM_LINUX_BASE MSM_EBI_BASE -#define SMI64_MSM_LINUX_SIZE 0x06500000 +#define SMI64_MSM_LINUX_SIZE 0x068e0000 #define SMI32_MSM_LINUX_BASE MSM_EBI_BASE @@ -55,9 +55,9 @@ #define MSM_PMEM_GPU1_SIZE 0x800000 -#define MSM_PMEM_GPU1_BASE MSM_RAM_CONSOLE_BASE - MSM_PMEM_GPU1_SIZE +#define MSM_PMEM_GPU1_BASE (MSM_RAM_CONSOLE_BASE + MSM_RAM_CONSOLE_SIZE) -#define MSM_RAM_CONSOLE_BASE MSM_EBI_BASE + 0x6d00000 +#define MSM_RAM_CONSOLE_BASE (MSM_EBI_BASE + SMI64_MSM_LINUX_SIZE) #define MSM_RAM_CONSOLE_SIZE 128 * SZ_1K #if (SMI32_MSM_FB_BASE + SMI32_MSM_FB_SIZE) >= (MSM_PMEM_GPU1_BASE) From 13e5e5bfaf8e0661fd4882aa7132d8d5687c24b5 Mon Sep 17 00:00:00 2001 From: Roy Chiang Date: Thu, 10 Jun 2010 14:29:52 -0700 Subject: [PATCH 0714/2556] [ARM] mahimahi: add support for Sony TFT panel Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-mahimahi-panel.c | 354 ++++++++++++++++++++++- arch/arm/mach-msm/board-mahimahi.h | 27 +- 2 files changed, 370 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi-panel.c b/arch/arm/mach-msm/board-mahimahi-panel.c index a7dadcad2f233..64b66b7a37d4b 100644 --- a/arch/arm/mach-msm/board-mahimahi-panel.c +++ b/arch/arm/mach-msm/board-mahimahi-panel.c @@ -29,6 +29,8 @@ #include #include +#include +#include "proc_comm.h" #include "board-mahimahi.h" #include "devices.h" @@ -43,6 +45,8 @@ static void __iomem *spi_base; static struct clk *spi_clk ; +static struct vreg *vreg_lcm_rftx_2v6; +static struct vreg *vreg_lcm_aux_2v6; static int qspi_send(uint32_t id, uint8_t data) { @@ -62,6 +66,24 @@ static int qspi_send(uint32_t id, uint8_t data) return 0; } +static int qspi_send_9bit(uint32_t id, uint8_t data) +{ + uint32_t err; + + while (readl(spi_base + SPI_OPERATIONAL) & (1<<5)) { + err = readl(spi_base + SPI_ERROR_FLAGS); + if (err) { + pr_err("%s: ERROR: SPI_ERROR_FLAGS=0x%08x\n", __func__, + err); + return -EIO; + } + } + writel(((id << 8) | data) << 23, spi_base + SPI_OUTPUT_FIFO); + udelay(100); + + return 0; +} + static int lcm_writeb(uint8_t reg, uint8_t val) { qspi_send(0x0, reg); @@ -397,6 +419,15 @@ static struct lcm_tbl samsung_oled_gamma_table[][OLED_GAMMA_TABLE_SIZE] = { SAMSUNG_OLED_MIN_VAL) / \ (SAMSUNG_OLED_NUM_LEVELS - 1)) + +#define SONY_TFT_DEF_USER_VAL 102 +#define SONY_TFT_MIN_USER_VAL 30 +#define SONY_TFT_MAX_USER_VAL 255 +#define SONY_TFT_DEF_PANEL_VAL 155 +#define SONY_TFT_MIN_PANEL_VAL 26 +#define SONY_TFT_MAX_PANEL_VAL 255 + + static DEFINE_MUTEX(panel_lock); static struct work_struct brightness_delayed_work; static DEFINE_SPINLOCK(brightness_lock); @@ -404,6 +435,8 @@ static uint8_t new_val = SAMSUNG_OLED_DEFAULT_VAL; static uint8_t last_val = SAMSUNG_OLED_DEFAULT_VAL; static uint8_t table_sel_vals[] = { 0x43, 0x34 }; static int table_sel_idx = 0; +static uint8_t tft_panel_on; + static void gamma_table_bank_select(void) { lcm_writeb(0x39, table_sel_vals[table_sel_idx]); @@ -514,13 +547,252 @@ static int samsung_oled_panel_blank(struct msm_lcdc_panel_ops *ops) return 0; } -static struct msm_lcdc_panel_ops mahimahi_lcdc_panel_ops = { +struct lcm_cmd { + int reg; + uint32_t val; + unsigned delay; +}; + +#define LCM_GPIO_CFG(gpio, func, str) \ + PCOM_GPIO_CFG(gpio, func, GPIO_OUTPUT, GPIO_NO_PULL, str) + +static uint32_t sony_tft_display_on_gpio_table[] = { + LCM_GPIO_CFG(MAHIMAHI_LCD_R1, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_R2, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_R3, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_R4, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_R5, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G0, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G1, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G2, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G3, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G4, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G5, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B1, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B2, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B3, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B4, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B5, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_PCLK, 1, GPIO_4MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_VSYNC, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_HSYNC, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_DE, 1, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CLK, 1, GPIO_4MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_DO, 1, GPIO_4MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CSz, 1, GPIO_4MA), +}; + +static uint32_t sony_tft_display_off_gpio_table[] = { + LCM_GPIO_CFG(MAHIMAHI_LCD_R1, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_R2, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_R3, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_R4, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_R5, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G0, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G1, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G2, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G3, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G4, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_G5, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B1, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B2, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B3, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B4, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_B5, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_PCLK, 0, GPIO_4MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_VSYNC, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_HSYNC, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_DE, 0, GPIO_8MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CLK, 0, GPIO_4MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_DO, 0, GPIO_4MA), + LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CSz, 0, GPIO_4MA), +}; + +#undef LCM_GPIO_CFG + +#define SONY_TFT_DEF_PANEL_DELTA \ + (SONY_TFT_DEF_PANEL_VAL - SONY_TFT_MIN_PANEL_VAL) +#define SONY_TFT_DEF_USER_DELTA \ + (SONY_TFT_DEF_USER_VAL - SONY_TFT_MIN_USER_VAL) + +static void sony_tft_set_pwm_val(int val) +{ + pr_info("%s: %d\n", __func__, val); + + last_val = val; + + if (!tft_panel_on) + return; + + if (val <= SONY_TFT_DEF_USER_VAL) { + if (val <= SONY_TFT_MIN_USER_VAL) + val = SONY_TFT_MIN_PANEL_VAL; + else + val = SONY_TFT_DEF_PANEL_DELTA * + (val - SONY_TFT_MIN_USER_VAL) / + SONY_TFT_DEF_USER_DELTA + + SONY_TFT_MIN_PANEL_VAL; + } else + val = (SONY_TFT_MAX_PANEL_VAL - SONY_TFT_DEF_PANEL_VAL) * + (val - SONY_TFT_DEF_USER_VAL) / + (SONY_TFT_MAX_USER_VAL - SONY_TFT_DEF_USER_VAL) + + SONY_TFT_DEF_PANEL_VAL; + + clk_enable(spi_clk); + qspi_send_9bit(0x0, 0x51); + qspi_send_9bit(0x1, val); + qspi_send_9bit(0x0, 0x53); + qspi_send_9bit(0x1, 0x24); + clk_disable(spi_clk); +} + +#undef SONY_TFT_DEF_PANEL_DELTA +#undef SONY_TFT_DEF_USER_DELTA + +static void sony_tft_panel_config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for (n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + + +static int sony_tft_panel_power(int on) +{ + unsigned id, on_off; + + if (on) { + on_off = 0; + + vreg_enable(vreg_lcm_aux_2v6); + vreg_enable(vreg_lcm_rftx_2v6); + + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + + id = PM_VREG_PDOWN_RFTX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + mdelay(10); + gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1); + mdelay(10); + gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0); + udelay(500); + gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1); + mdelay(10); + sony_tft_panel_config_gpio_table( + sony_tft_display_on_gpio_table, + ARRAY_SIZE(sony_tft_display_on_gpio_table)); + } else { + on_off = 1; + + gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0); + + mdelay(120); + + vreg_disable(vreg_lcm_rftx_2v6); + vreg_disable(vreg_lcm_aux_2v6); + + id = PM_VREG_PDOWN_RFTX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + sony_tft_panel_config_gpio_table( + sony_tft_display_off_gpio_table, + ARRAY_SIZE(sony_tft_display_off_gpio_table)); + } + return 0; +} + +static int sony_tft_panel_init(struct msm_lcdc_panel_ops *ops) +{ + return 0; +} + +static int sony_tft_panel_unblank(struct msm_lcdc_panel_ops *ops) +{ + pr_info("%s: +()\n", __func__); + + mutex_lock(&panel_lock); + + if (tft_panel_on) { + pr_info("%s: -() already unblanked\n", __func__); + goto done; + } + + sony_tft_panel_power(1); + msleep(45); + + clk_enable(spi_clk); + qspi_send_9bit(0x0, 0x11); + msleep(5); + qspi_send_9bit(0x0, 0x3a); + qspi_send_9bit(0x1, 0x05); + msleep(100); + qspi_send_9bit(0x0, 0x29); + /* unlock register page for pwm setting */ + qspi_send_9bit(0x0, 0xf0); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x0, 0xf1); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x0, 0xd0); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x1, 0x5a); + + qspi_send_9bit(0x0, 0xc2); + qspi_send_9bit(0x1, 0x53); + qspi_send_9bit(0x1, 0x12); + clk_disable(spi_clk); + msleep(100); + tft_panel_on = 1; + sony_tft_set_pwm_val(last_val); + + pr_info("%s: -()\n", __func__); +done: + mutex_unlock(&panel_lock); + return 0; +} + +static int sony_tft_panel_blank(struct msm_lcdc_panel_ops *ops) +{ + pr_info("%s: +()\n", __func__); + + mutex_lock(&panel_lock); + + clk_enable(spi_clk); + qspi_send_9bit(0x0, 0x28); + qspi_send_9bit(0x0, 0x10); + clk_disable(spi_clk); + + msleep(40); + sony_tft_panel_power(0); + tft_panel_on = 0; + + mutex_unlock(&panel_lock); + + pr_info("%s: -()\n", __func__); + return 0; +} + +static struct msm_lcdc_panel_ops mahimahi_lcdc_amoled_panel_ops = { .init = samsung_oled_panel_init, .blank = samsung_oled_panel_blank, .unblank = samsung_oled_panel_unblank, }; -static struct msm_lcdc_timing mahimahi_lcdc_timing = { +static struct msm_lcdc_panel_ops mahimahi_lcdc_tft_panel_ops = { + .init = sony_tft_panel_init, + .blank = sony_tft_panel_blank, + .unblank = sony_tft_panel_unblank, +}; + + +static struct msm_lcdc_timing mahimahi_lcdc_amoled_timing = { .clk_rate = 24576000, .hsync_pulse_width = 4, .hsync_back_porch = 8, @@ -534,6 +806,20 @@ static struct msm_lcdc_timing mahimahi_lcdc_timing = { .den_act_low = 1, }; +static struct msm_lcdc_timing mahimahi_lcdc_tft_timing = { + .clk_rate = 24576000, + .hsync_pulse_width = 2, + .hsync_back_porch = 20, + .hsync_front_porch = 20, + .hsync_skew = 0, + .vsync_pulse_width = 2, + .vsync_back_porch = 6, + .vsync_front_porch = 4, + .vsync_act_low = 1, + .hsync_act_low = 1, + .den_act_low = 0, +}; + static struct msm_fb_data mahimahi_lcdc_fb_data = { .xres = 480, .yres = 800, @@ -542,19 +828,35 @@ static struct msm_fb_data mahimahi_lcdc_fb_data = { .output_format = MSM_MDP_OUT_IF_FMT_RGB565, }; -static struct msm_lcdc_platform_data mahimahi_lcdc_platform_data = { - .panel_ops = &mahimahi_lcdc_panel_ops, - .timing = &mahimahi_lcdc_timing, +static struct msm_lcdc_platform_data mahimahi_lcdc_amoled_platform_data = { + .panel_ops = &mahimahi_lcdc_amoled_panel_ops, + .timing = &mahimahi_lcdc_amoled_timing, + .fb_id = 0, + .fb_data = &mahimahi_lcdc_fb_data, + .fb_resource = &resources_msm_fb[0], +}; + +static struct msm_lcdc_platform_data mahimahi_lcdc_tft_platform_data = { + .panel_ops = &mahimahi_lcdc_tft_panel_ops, + .timing = &mahimahi_lcdc_tft_timing, .fb_id = 0, .fb_data = &mahimahi_lcdc_fb_data, .fb_resource = &resources_msm_fb[0], }; -static struct platform_device mahimahi_lcdc_device = { +static struct platform_device mahimahi_lcdc_amoled_device = { + .name = "msm_mdp_lcdc", + .id = -1, + .dev = { + .platform_data = &mahimahi_lcdc_amoled_platform_data, + }, +}; + +static struct platform_device mahimahi_lcdc_tft_device = { .name = "msm_mdp_lcdc", .id = -1, .dev = { - .platform_data = &mahimahi_lcdc_platform_data, + .platform_data = &mahimahi_lcdc_tft_platform_data, }, }; @@ -604,7 +906,7 @@ static void mahimahi_brightness_set(struct led_classdev *led_cdev, schedule_work(&brightness_delayed_work); } -static void mahimahi_brightness_set_work(struct work_struct *work_ptr) +static void mahimahi_brightness_amoled_set_work(struct work_struct *work_ptr) { unsigned long flags; uint8_t val; @@ -618,6 +920,20 @@ static void mahimahi_brightness_set_work(struct work_struct *work_ptr) mutex_unlock(&panel_lock); } +static void mahimahi_brightness_tft_set_work(struct work_struct *work_ptr) +{ + unsigned long flags; + uint8_t val; + + spin_lock_irqsave(&brightness_lock, flags); + val = new_val; + spin_unlock_irqrestore(&brightness_lock, flags); + + mutex_lock(&panel_lock); + sony_tft_set_pwm_val(val); + mutex_unlock(&panel_lock); +} + static struct led_classdev mahimahi_brightness_led = { .name = "lcd-backlight", .brightness = LED_FULL, @@ -646,9 +962,27 @@ int __init mahimahi_init_panel(void) if (ret != 0) return ret; - INIT_WORK(&brightness_delayed_work, mahimahi_brightness_set_work); + if (gpio_get_value(MAHIMAHI_GPIO_LCD_ID0)) { + pr_info("%s: tft panel\n", __func__); + vreg_lcm_rftx_2v6 = vreg_get(0, "rftx"); + if (IS_ERR(vreg_lcm_rftx_2v6)) + return PTR_ERR(vreg_lcm_rftx_2v6); + vreg_set_level(vreg_lcm_rftx_2v6, 2600); + + vreg_lcm_aux_2v6 = vreg_get(0, "gp4"); + if (IS_ERR(vreg_lcm_aux_2v6)) + return PTR_ERR(vreg_lcm_aux_2v6); + + if (gpio_get_value(MAHIMAHI_GPIO_LCD_RST_N)) + tft_panel_on = 1; + ret = platform_device_register(&mahimahi_lcdc_tft_device); + INIT_WORK(&brightness_delayed_work, mahimahi_brightness_tft_set_work); + } else { + pr_info("%s: amoled panel\n", __func__); + ret = platform_device_register(&mahimahi_lcdc_amoled_device); + INIT_WORK(&brightness_delayed_work, mahimahi_brightness_amoled_set_work); + } - ret = platform_device_register(&mahimahi_lcdc_device); if (ret != 0) return ret; diff --git a/arch/arm/mach-msm/board-mahimahi.h b/arch/arm/mach-msm/board-mahimahi.h index db810129a5a17..9696a47c4006a 100644 --- a/arch/arm/mach-msm/board-mahimahi.h +++ b/arch/arm/mach-msm/board-mahimahi.h @@ -126,6 +126,7 @@ #define MAHIMAHI_GPIO_LED_3V3_EN 85 #define MAHIMAHI_GPIO_LCD_RST_N 29 +#define MAHIMAHI_GPIO_LCD_ID0 147 /* 3.5mm remote control key interrupt shutdown signal */ #define MAHIMAHI_GPIO_35MM_KEY_INT_SHUTDOWN 19 @@ -144,7 +145,31 @@ #define MAHIMAHI_CDMA_SD_2V85_EN 100 #define MAHIMAHI_CDMA_JOG_2V6_EN 150 - +/* display relative */ +#define MAHIMAHI_LCD_SPI_CLK (17) +#define MAHIMAHI_LCD_SPI_DO (18) +#define MAHIMAHI_LCD_SPI_CSz (20) +#define MAHIMAHI_LCD_RSTz (29) +#define MAHIMAHI_LCD_R1 (114) +#define MAHIMAHI_LCD_R2 (115) +#define MAHIMAHI_LCD_R3 (116) +#define MAHIMAHI_LCD_R4 (117) +#define MAHIMAHI_LCD_R5 (118) +#define MAHIMAHI_LCD_G0 (121) +#define MAHIMAHI_LCD_G1 (122) +#define MAHIMAHI_LCD_G2 (123) +#define MAHIMAHI_LCD_G3 (124) +#define MAHIMAHI_LCD_G4 (125) +#define MAHIMAHI_LCD_G5 (126) +#define MAHIMAHI_LCD_B1 (130) +#define MAHIMAHI_LCD_B2 (131) +#define MAHIMAHI_LCD_B3 (132) +#define MAHIMAHI_LCD_B4 (133) +#define MAHIMAHI_LCD_B5 (134) +#define MAHIMAHI_LCD_PCLK (135) +#define MAHIMAHI_LCD_VSYNC (136) +#define MAHIMAHI_LCD_HSYNC (137) +#define MAHIMAHI_LCD_DE (138) #define is_cdma_version(rev) (((rev) & 0xF0) == 0xC0) #endif /* __ARCH_ARM_MACH_MSM_BOARD_MAHIMAHI_H */ From 4f1120731eb0cdf4a465f4e0edc3e04737d1428d Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 13 Jul 2010 11:33:23 -0700 Subject: [PATCH 0715/2556] [ARM] msm: sapphire: make MACH_SAPPHIRE mutually exclusive with trout and halibut Kernel changes are coming which will render the sapphire kernel not bootable on other msm7k platforms (halibut and trout). This patch sets halibut and trout builds to no by default, enables sapphire only, and adds some files specifically for MACH_SAPPHIRE which before depended only on MACH_TROUT. Change-Id: Iaeb06683551571ae60adfad4626453c78f99a6fd Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/Kconfig | 6 ++++-- arch/arm/mach-msm/Makefile | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 3fedb2191dbbe..2deb0de34a71e 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -132,6 +132,7 @@ menu "Qualcomm MSM Board Type" config MACH_HALIBUT depends on ARCH_MSM depends on ARCH_MSM7X00A + default n bool "Halibut Board (QCT SURF7201A)" help Support for the Qualcomm SURF7201A eval board. @@ -139,6 +140,7 @@ config MACH_HALIBUT config MACH_TROUT depends on ARCH_MSM depends on ARCH_MSM7X00A + default n bool "HTC Dream (aka trout)" help Support for the HTC Dream, T-Mobile G1, Android ADP1 devices. @@ -232,7 +234,7 @@ config MSM_PROC_COMM bool config MACH_SAPPHIRE - depends on ARCH_MSM7X00A + depends on ARCH_MSM7X00A && !MACH_TROUT && !MACH_HALIBUT default y bool "Sapphire" @@ -272,7 +274,7 @@ config HTC_35MM_JACK Provides support for 3.5mm headset jack devices, like wired headsets. config TROUT_BATTCHG - depends on MACH_TROUT && POWER_SUPPLY + depends on (MACH_TROUT || MACH_SAPPHIRE) && POWER_SUPPLY default y bool "Trout battery / charger driver" diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 31658aef2d735..60dcf904598c0 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-mmc.o board-sapphire-wifi.o obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-rfkill.o msm_vibrator.o obj-$(CONFIG_MACH_SAPPHIRE) += devices-msm7x00.o devices_htc.o +obj-$(CONFIG_MACH_SAPPHIRE) += htc_akm_cal.o htc_acoustic.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi.o board-mahimahi-panel.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-keypad.o board-mahimahi-mmc.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-rfkill.o htc_wifi_nvs.o From eea354470f4b36973d180801db2ade17d2cf1145 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 13 Jul 2010 12:18:24 -0700 Subject: [PATCH 0716/2556] [ARM] msm_defconfig: disable halibut and trout Change-Id: I82d8ae270b598d62839c16d59bd098221d139d6d Signed-off-by: Iliyan Malchev --- arch/arm/configs/msm_defconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm/configs/msm_defconfig b/arch/arm/configs/msm_defconfig index 9cef7e4a6c73a..6eb769622b6eb 100755 --- a/arch/arm/configs/msm_defconfig +++ b/arch/arm/configs/msm_defconfig @@ -22,8 +22,6 @@ CONFIG_MODULE_FORCE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_IOSCHED_DEADLINE is not set CONFIG_ARCH_MSM=y -CONFIG_MACH_HALIBUT=y -CONFIG_MACH_TROUT=y CONFIG_HTC_HEADSET=y CONFIG_MSM_SERIAL_DEBUGGER=y CONFIG_WIFI_CONTROL_FUNC=y From 6dd852dc1ef33f571a8fd944f9aaee0ef2911bb0 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 13 Jul 2010 12:27:33 -0700 Subject: [PATCH 0717/2556] [ARM] msm: sapphire: rearrange the kernel memory map for new radio -- new radio with 4M more memory -- reduce the sized of the pmem pools -- moves the kernel load address to 0x02000000 Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/board-sapphire.c | 24 ++++++++++++++++++------ arch/arm/mach-msm/board-sapphire.h | 15 ++++++++++----- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index 0117c503e6972..0d03d282e8bfa 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -1258,19 +1258,31 @@ static void __init sapphire_fixup(struct machine_desc *desc, struct tag *tags, engineerid = parse_tag_engineerid((const struct tag *)tags); printk("sapphire_fixup:engineerid=0x%x\n", engineerid); - mi->nr_banks = 1; - mi->bank[0].start = PHYS_OFFSET; - mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); if (smi_sz == 32) { + mi->nr_banks = 1; + mi->bank[0].start = PHYS_OFFSET; + mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); mi->bank[0].size = (84*1024*1024); } else if (smi_sz == 64) { - mi->bank[0].size = (104*1024*1024); + mi->nr_banks = 2; + mi->bank[0].start = SMI64_MSM_LINUX_BASE_1; + mi->bank[0].node = PHYS_TO_NID(SMI64_MSM_LINUX_BASE_1); + mi->bank[0].size = (32*1024*1024); + mi->bank[1].start = SMI64_MSM_LINUX_BASE_2; + mi->bank[1].node = PHYS_TO_NID(SMI64_MSM_LINUX_BASE_2); + mi->bank[1].size = (84*1024*1024); } else { printk(KERN_ERR "can not get smi size\n"); /*Give a default value when not get smi size*/ smi_sz = 64; - mi->bank[0].size = (104*1024*1024); + mi->nr_banks = 2; + mi->bank[0].start = SMI64_MSM_LINUX_BASE_1; + mi->bank[0].node = PHYS_TO_NID(SMI64_MSM_LINUX_BASE_1); + mi->bank[0].size = (32*1024*1024); + mi->bank[1].start = SMI64_MSM_LINUX_BASE_2; + mi->bank[1].node = PHYS_TO_NID(SMI64_MSM_LINUX_BASE_2); + mi->bank[1].size = (84*1024*1024); printk(KERN_ERR "use default : smisize=%d\n", smi_sz); } } @@ -1286,7 +1298,7 @@ MACHINE_START(SAPPHIRE, "sapphire") /* Maintainer: Brian Swetland */ #ifdef CONFIG_MSM_DEBUG_UART #endif - .boot_params = 0x10000100, + .boot_params = 0x02000100, .fixup = sapphire_fixup, .map_io = sapphire_map_io, .init_irq = sapphire_init_irq, diff --git a/arch/arm/mach-msm/board-sapphire.h b/arch/arm/mach-msm/board-sapphire.h index 2e6f6764b09ac..d96760a25ef8a 100644 --- a/arch/arm/mach-msm/board-sapphire.h +++ b/arch/arm/mach-msm/board-sapphire.h @@ -25,14 +25,14 @@ #define MSM_PMEM_GPU0_BASE 0x00000000 #define MSM_PMEM_GPU0_SIZE 0x00700000 -#define SMI64_MSM_PMEM_MDP_BASE 0x02000000 +#define SMI64_MSM_PMEM_MDP_BASE 0x15900000 #define SMI64_MSM_PMEM_MDP_SIZE 0x00800000 -#define SMI64_MSM_PMEM_ADSP_BASE 0x02800000 +#define SMI64_MSM_PMEM_ADSP_BASE 0x16100000 #define SMI64_MSM_PMEM_ADSP_SIZE 0x00800000 -#define SMI64_MSM_PMEM_CAMERA_BASE 0x03000000 -#define SMI64_MSM_PMEM_CAMERA_SIZE 0x01000000 +#define SMI64_MSM_PMEM_CAMERA_BASE 0x15400000 +#define SMI64_MSM_PMEM_CAMERA_SIZE 0x00500000 #define SMI64_MSM_FB_BASE 0x00700000 #define SMI64_MSM_FB_SIZE 0x00100000 @@ -40,6 +40,11 @@ #define SMI64_MSM_LINUX_BASE MSM_EBI_BASE #define SMI64_MSM_LINUX_SIZE 0x068e0000 +#define SMI64_MSM_LINUX_BASE_1 0x02000000 +#define SMI64_MSM_LINUX_SIZE_1 0x02000000 + +#define SMI64_MSM_LINUX_BASE_2 MSM_EBI_BASE +#define SMI64_MSM_LINUX_SIZE_2 0x05400000 #define SMI32_MSM_LINUX_BASE MSM_EBI_BASE #define SMI32_MSM_LINUX_SIZE 0x5400000 @@ -57,7 +62,7 @@ #define MSM_PMEM_GPU1_SIZE 0x800000 #define MSM_PMEM_GPU1_BASE (MSM_RAM_CONSOLE_BASE + MSM_RAM_CONSOLE_SIZE) -#define MSM_RAM_CONSOLE_BASE (MSM_EBI_BASE + SMI64_MSM_LINUX_SIZE) +#define MSM_RAM_CONSOLE_BASE 0x169E0000 #define MSM_RAM_CONSOLE_SIZE 128 * SZ_1K #if (SMI32_MSM_FB_BASE + SMI32_MSM_FB_SIZE) >= (MSM_PMEM_GPU1_BASE) From 58df6aef324a39c12c959c6ddceaeec30cc80efe Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Tue, 13 Jul 2010 12:29:56 -0700 Subject: [PATCH 0718/2556] [ARM] msm: sapphire: Adjust Makefile.boot and memory.h for new load address Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/Makefile.boot | 5 +++++ arch/arm/mach-msm/include/mach/memory.h | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot index 4f02ff6431cd5..a423a2ee969c2 100644 --- a/arch/arm/mach-msm/Makefile.boot +++ b/arch/arm/mach-msm/Makefile.boot @@ -2,6 +2,11 @@ params_phys-y := 0x10000100 initrd_phys-y := 0x10800000 +# override for Sapphire + zreladdr-$(CONFIG_MACH_SAPPHIRE) := 0x02008000 +params_phys-$(CONFIG_MACH_SAPPHIRE) := 0x02000100 +initrd_phys-$(CONFIG_MACH_SAPPHIRE) := 0x02800000 + # for now, override for QSD8x50 zreladdr-$(CONFIG_ARCH_QSD8X50) := 0x20008000 params_phys-$(CONFIG_ARCH_QSD8X50) := 0x20000100 diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h index d28b0531c2454..6e202fdfefd86 100644 --- a/arch/arm/mach-msm/include/mach/memory.h +++ b/arch/arm/mach-msm/include/mach/memory.h @@ -29,9 +29,14 @@ #define PHYS_OFFSET UL(0x40200000) #define RESET_VECTOR UL(0x00000000) #else +#ifdef CONFIG_MACH_SAPPHIRE +#define PHYS_OFFSET UL(0x02000000) +#define RESET_VECTOR UL(0x00000000) +#else #define PHYS_OFFSET UL(0x10000000) #define RESET_VECTOR UL(0x00000000) #endif +#endif #define HAS_ARCH_IO_REMAP_PFN_RANGE From ec9b3651b6e3c6f70ae356521196bb03279c11be Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Wed, 14 Jul 2010 12:32:34 -0700 Subject: [PATCH 0719/2556] [ARM] msm: sapphire: add missing file to Makefile Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 60dcf904598c0..4657a190dd1be 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -60,7 +60,7 @@ obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-mmc.o board-sapphire-wifi.o obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-rfkill.o msm_vibrator.o obj-$(CONFIG_MACH_SAPPHIRE) += devices-msm7x00.o devices_htc.o -obj-$(CONFIG_MACH_SAPPHIRE) += htc_akm_cal.o htc_acoustic.o +obj-$(CONFIG_MACH_SAPPHIRE) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi.o board-mahimahi-panel.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-keypad.o board-mahimahi-mmc.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-rfkill.o htc_wifi_nvs.o From 308686bfcfa97e37d09885bfd649dfc80c70a13e Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Thu, 15 Jul 2010 08:28:29 -0700 Subject: [PATCH 0720/2556] [ARM] msm: disable the allocator for pmem_camera Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/devices_htc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c index ae53e3c549a97..e5f65e720656d 100644 --- a/arch/arm/mach-msm/devices_htc.c +++ b/arch/arm/mach-msm/devices_htc.c @@ -221,7 +221,7 @@ static struct android_pmem_platform_data pmem_adsp_pdata = { static struct android_pmem_platform_data pmem_camera_pdata = { .name = "pmem_camera", - .no_allocator = 0, + .no_allocator = 1, .cached = 0, }; From 4cfb52659dfad68a3af03ff27aea137026e8a33d Mon Sep 17 00:00:00 2001 From: Ken Sumrall Date: Thu, 22 Jul 2010 17:36:52 -0700 Subject: [PATCH 0721/2556] [ARM] msm: kgsl: Remove support for mmap from the kgsl driver. The mmap(2) functionality of kgsl is no longer used, and it had a bug that would crash the kernel when mmap(2) was called with "odd" parameters. So we have removed the mmap functionality. If for some reason in the future you want to resurrect this, make sure memdesc is properly initialized in all paths through the kgsl_mmap() routine. Signed-off-by: Ken Sumrall --- drivers/video/msm/gpu/kgsl/kgsl.c | 42 ------------------------------- 1 file changed, 42 deletions(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c index 031e2285e7f71..a8d5e35f03321 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.c +++ b/drivers/video/msm/gpu/kgsl/kgsl.c @@ -1035,52 +1035,10 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) return result; } -static int kgsl_mmap(struct file *file, struct vm_area_struct *vma) -{ - int result; - struct kgsl_memdesc *memdesc = NULL; - unsigned long vma_size = vma->vm_end - vma->vm_start; - unsigned long vma_offset = vma->vm_pgoff << PAGE_SHIFT; - struct kgsl_device *device = NULL; - - mutex_lock(&kgsl_driver.mutex); - - device = &kgsl_driver.yamato_device; - - /*allow yamato memstore to be mapped read only */ - if (vma_offset == device->memstore.physaddr) { - if (vma->vm_flags & VM_WRITE) { - result = -EPERM; - goto done; - } - memdesc = &device->memstore; - } - - if (memdesc->size != vma_size) { - KGSL_MEM_ERR("file %p bad size %ld, should be %d\n", - file, vma_size, memdesc->size); - result = -EINVAL; - goto done; - } - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - - result = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, - vma_size, vma->vm_page_prot); - if (result != 0) { - KGSL_MEM_ERR("remap_pfn_range returned %d\n", - result); - goto done; - } -done: - mutex_unlock(&kgsl_driver.mutex); - return result; -} - static struct file_operations kgsl_fops = { .owner = THIS_MODULE, .release = kgsl_release, .open = kgsl_open, - .mmap = kgsl_mmap, .unlocked_ioctl = kgsl_ioctl, }; From 6ef65f57ce8f698fb6c39b5651932d7037fdaac6 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 23 Sep 2010 17:22:49 -0700 Subject: [PATCH 0722/2556] Revert "ARM: mahimahi: config: Enable L2TP" This reverts commit 248aae5cad75d343bf4196ac42f8407454b66b78. --- arch/arm/configs/mahimahi_defconfig | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/arm/configs/mahimahi_defconfig b/arch/arm/configs/mahimahi_defconfig index 22757338996e2..fcb9c10d516f0 100644 --- a/arch/arm/configs/mahimahi_defconfig +++ b/arch/arm/configs/mahimahi_defconfig @@ -131,10 +131,6 @@ CONFIG_IP_NF_TARGET_REDIRECT=y CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y -CONFIG_L2TP=y -CONFIG_L2TP_V3=y -CONFIG_L2TP_IP=y -CONFIG_L2TP_ETH=y CONFIG_NET_SCHED=y CONFIG_NET_SCH_HTB=y CONFIG_NET_SCH_INGRESS=y From eac21e0120e80241859e55228d5c15b7aabce95d Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 24 Sep 2010 00:16:19 -0700 Subject: [PATCH 0723/2556] [ARM] msm: mahimahi: Turn off arm unwind from defconfig Change-Id: I78c708aa3fa6d95ce227f07407b1dfa6a0fddf79 Signed-off-by: Dima Zavin --- arch/arm/configs/mahimahi_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/mahimahi_defconfig b/arch/arm/configs/mahimahi_defconfig index fcb9c10d516f0..944573507ba5f 100644 --- a/arch/arm/configs/mahimahi_defconfig +++ b/arch/arm/configs/mahimahi_defconfig @@ -284,6 +284,7 @@ CONFIG_DEBUG_MUTEXES=y CONFIG_DEBUG_SPINLOCK_SLEEP=y CONFIG_DEBUG_INFO=y # CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_ARM_UNWIND is not set CONFIG_CRYPTO_AES=y CONFIG_CRYPTO_TWOFISH=y # CONFIG_CRYPTO_ANSI_CPRNG is not set From fa5d3f2687f5fd49822021038e6032d904cd91f5 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 29 Sep 2010 10:16:55 -0700 Subject: [PATCH 0724/2556] mmc: msm_sdcc: Add MMC_PM_KEEP_POWER to pm_flags for built-in device Signed-off-by: Dmitry Shmidt --- drivers/mmc/host/msm_sdcc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 4290c1cd375c3..b29b6ddbcb76a 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1375,6 +1375,9 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state) if (host->stat_irq) disable_irq(host->stat_irq); + if (host->plat->built_in) + mmc->pm_flags |= MMC_PM_KEEP_POWER; + if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) rc = mmc_suspend_host(mmc); if (!rc) From 620111aaab0764d7fd291159368c1a179b4506f2 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 1 Oct 2010 11:09:48 -0700 Subject: [PATCH 0725/2556] ARM: msm: Fix CONFIG_VIRTUAL_KPANIC_PARTITION warning Signed-off-by: Dmitry Shmidt --- arch/arm/mach-msm/nand_partitions.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/nand_partitions.c b/arch/arm/mach-msm/nand_partitions.c index 72d3f8ba6bae0..4361e49fb4aa6 100644 --- a/arch/arm/mach-msm/nand_partitions.c +++ b/arch/arm/mach-msm/nand_partitions.c @@ -83,7 +83,7 @@ static int __init parse_tag_msm_partition(const struct tag *tag) ptn++; } -#if CONFIG_VIRTUAL_KPANIC_PARTITION +#ifdef CONFIG_VIRTUAL_KPANIC_PARTITION if (!have_kpanic) { int i; uint64_t kpanic_off = 0; @@ -117,8 +117,8 @@ static int __init parse_tag_msm_partition(const struct tag *tag) count++; } -#endif /* CONFIG_VIRTUAL_KPANIC_SRC */ out: +#endif /* CONFIG_VIRTUAL_KPANIC_SRC */ msm_nand_data.nr_parts = count; msm_nand_data.parts = msm_nand_partitions; From a511d0d31849f4c1da7f4aa6f11c76eef6bb75b6 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 8 Oct 2010 17:40:02 -0700 Subject: [PATCH 0726/2556] video: msm: kgsl: allocate uncached buffers as writecombined Change-Id: I6045660d4e374cc414c73eae466189bc338b3b42 Signed-off-by: Dima Zavin --- drivers/video/msm/gpu/kgsl/kgsl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c index a8d5e35f03321..4dde98e066421 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.c +++ b/drivers/video/msm/gpu/kgsl/kgsl.c @@ -712,7 +712,7 @@ static int kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_file_private *private, * overwrite this memory */ dmac_flush_range(vmalloc_area, vmalloc_area + len); KGSL_MEM_INFO("Caching for memory allocation turned off\n"); - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); } else { KGSL_MEM_INFO("Caching for memory allocation turned on\n"); } From 74eeaa3024a24ccb9ac65ca8b9fe3afd8d652e6e Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sun, 10 Oct 2010 15:34:25 -0700 Subject: [PATCH 0727/2556] ARM: msm: 8x50: request i2c gpios on first use Change-Id: I78120d4383aea5528f17de610e7643d0b98cba63 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/devices-qsd8x50.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm/mach-msm/devices-qsd8x50.c b/arch/arm/mach-msm/devices-qsd8x50.c index 313b366f63ea7..b4566eb18849c 100644 --- a/arch/arm/mach-msm/devices-qsd8x50.c +++ b/arch/arm/mach-msm/devices-qsd8x50.c @@ -13,6 +13,7 @@ * */ +#include #include #include #include @@ -205,9 +206,21 @@ struct platform_device msm_device_i2c = { #define GPIO_I2C_CLK 95 #define GPIO_I2C_DAT 96 +static int gpio_i2c_clk = -1; +static int gpio_i2c_dat = -1; void msm_set_i2c_mux(bool gpio, int *gpio_clk, int *gpio_dat) { unsigned id; + + if (gpio_i2c_clk < 0) { + gpio_request(GPIO_I2C_CLK, "i2c-clk"); + gpio_i2c_clk = GPIO_I2C_CLK; + } + if (gpio_i2c_dat < 0) { + gpio_request(GPIO_I2C_DAT, "i2c-dat"); + gpio_i2c_dat = GPIO_I2C_DAT; + } + if (gpio) { id = PCOM_GPIO_CFG(GPIO_I2C_CLK, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA); From 30d625656e04a0be3416f247fc7bcdaaab5d4808 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 8 Oct 2010 16:47:09 -0700 Subject: [PATCH 0728/2556] mmc: msm_sdcc: Add MMC_PM_IGNORE_PM_NOTIFY for builtin device Signed-off-by: Dmitry Shmidt --- drivers/mmc/host/msm_sdcc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index b29b6ddbcb76a..52438b8b28121 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1248,6 +1248,10 @@ msmsdcc_probe(struct platform_device *pdev) msmsdcc_writel(host, MCI_IRQENABLE, MMCIMASK0); host->saved_irq0mask = MCI_IRQENABLE; + mmc->pm_caps = MMC_PM_KEEP_POWER | MMC_PM_IGNORE_PM_NOTIFY; + if (plat->built_in) + mmc->pm_flags = MMC_PM_KEEP_POWER | MMC_PM_IGNORE_PM_NOTIFY; + /* * Setup card detect change */ From 06f74a71c35dbed5396d8886c7bc1738e34c71ed Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Thu, 30 Sep 2010 19:18:16 -0700 Subject: [PATCH 0729/2556] [ARM] msm8k: fixes for audio_ctl and pcm_out -- q6_ioctl was not doing proper checks on user data -- pcm_ioctl would crash if AUDIO_SET_VOUME got called before AUDIO_START Signed-off-by: Iliyan Malchev --- arch/arm/mach-msm/qdsp6/audio_ctl.c | 30 ++++++++++++++++++++++------- arch/arm/mach-msm/qdsp6/pcm_out.c | 6 ++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-msm/qdsp6/audio_ctl.c b/arch/arm/mach-msm/qdsp6/audio_ctl.c index 5f9f317babb9b..170b3eb10f6e6 100644 --- a/arch/arm/mach-msm/qdsp6/audio_ctl.c +++ b/arch/arm/mach-msm/qdsp6/audio_ctl.c @@ -90,28 +90,40 @@ static int q6_ioctl(struct inode *inode, struct file *file, switch (cmd) { case AUDIO_SWITCH_DEVICE: rc = copy_from_user(&id, (void *)arg, sizeof(id)); - if (!rc) + if (rc) { + pr_err("%s: bad user address\n", __func__); + rc = -EFAULT; + } else rc = q6audio_do_routing(id[0], id[1]); break; case AUDIO_SET_VOLUME: rc = copy_from_user(&n, (void *)arg, sizeof(n)); - if (!rc) + if (rc) { + pr_err("%s: bad user address\n", __func__); + rc = -EFAULT; + } else rc = q6audio_set_rx_volume(n); break; case AUDIO_SET_MUTE: rc = copy_from_user(&n, (void *)arg, sizeof(n)); - if (!rc) + if (rc) { + pr_err("%s: bad user address\n", __func__); + rc = -EFAULT; + } else rc = q6audio_set_tx_mute(n); break; case AUDIO_UPDATE_ACDB: rc = copy_from_user(&id, (void *)arg, sizeof(id)); - if (!rc) + if (rc) { + pr_err("%s: bad user address\n", __func__); + rc = -EFAULT; + } else rc = q6audio_update_acdb(id[0], id[1]); break; case AUDIO_START_VOICE: - if (arg == 0) { + if (arg == 0) id[0] = id[1] = 0; - } else if (copy_from_user(&id, (void*) arg, sizeof(id))) { + else if (copy_from_user(&id, (void *)arg, sizeof(id))) { pr_info("voice: copy acdb_id from user failed\n"); rc = -EFAULT; break; @@ -123,10 +135,14 @@ static int q6_ioctl(struct inode *inode, struct file *file, break; case AUDIO_REINIT_ACDB: rc = copy_from_user(&filename, (void *)arg, sizeof(filename)); - if (!rc) + if (rc) { + pr_err("%s: bad user address\n", __func__); + rc = -EFAULT; + } else rc = q6audio_reinit_acdb(filename); break; default: + pr_info("%s: unknown %d\n", __func__, cmd); rc = -EINVAL; } diff --git a/arch/arm/mach-msm/qdsp6/pcm_out.c b/arch/arm/mach-msm/qdsp6/pcm_out.c index 11221686566da..6d041d8c686ae 100644 --- a/arch/arm/mach-msm/qdsp6/pcm_out.c +++ b/arch/arm/mach-msm/qdsp6/pcm_out.c @@ -56,6 +56,12 @@ static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { case AUDIO_SET_VOLUME: { int vol; + if (!pcm->ac) { + pr_err("%s: cannot set volume before AUDIO_START!\n", + __func__); + rc = -EINVAL; + break; + } if (copy_from_user(&vol, (void*) arg, sizeof(vol))) { rc = -EFAULT; break; From ee7cc1cd0fe1a231b30f405e82ac5f84b869308b Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 14 Oct 2010 17:08:27 -0700 Subject: [PATCH 0730/2556] i2c: msm: provide more debugging info on xfer errors Change-Id: Iae5dd5df9d3b7592e27fb72c0ba8d36aecda3afc Signed-off-by: Dima Zavin --- drivers/i2c/busses/i2c-msm.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-msm.c b/drivers/i2c/busses/i2c-msm.c index 57a7999b4cb4f..0322262f88543 100644 --- a/drivers/i2c/busses/i2c-msm.c +++ b/drivers/i2c/busses/i2c-msm.c @@ -256,7 +256,7 @@ msm_i2c_poll_notbusy(struct msm_i2c_dev *dev, int warn) if (retries++ > 100) msleep(10); } - dev_err(dev->dev, "Error waiting for notbusy\n"); + dev_err(dev->dev, "Error waiting for notbusy (%d)\n", warn); return -ETIMEDOUT; } @@ -332,6 +332,9 @@ msm_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) long timeout; unsigned long flags; + if (WARN_ON(!num)) + return -EINVAL; + /* * If there is an i2c_xfer after driver has been suspended, * grab wakelock to abort suspend. @@ -394,7 +397,8 @@ msm_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) } if (ret < 0) { - dev_err(dev->dev, "Error during data xfer (%d)\n", ret); + dev_err(dev->dev, "Error during data xfer %x (%d)\n", + msgs[0].addr, ret); msm_i2c_recover_bus_busy(dev); } err: From 9ee6ddb31fd2d6f74c91e2a3cdfba3dff163d90a Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 19 Oct 2010 09:52:36 -0700 Subject: [PATCH 0731/2556] [ARM] msm: qsd8k: audio When muting mic input, always update saved mute state regardless of completion status returned by audio dsp. This ensures that saved mute state is coherent with mute state in phone application that has no feedback on completion status. Signed-off-by: Eric Laurent --- arch/arm/mach-msm/qdsp6/q6audio.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c index 26c202bc2bc61..1b053e7e9efe6 100644 --- a/arch/arm/mach-msm/qdsp6/q6audio.c +++ b/arch/arm/mach-msm/qdsp6/q6audio.c @@ -1253,7 +1253,6 @@ int q6audio_update_acdb(uint32_t id_src, uint32_t id_dst) int q6audio_set_tx_mute(int mute) { uint32_t adev; - int rc; if (q6audio_init()) return 0; @@ -1266,8 +1265,8 @@ int q6audio_set_tx_mute(int mute) } adev = audio_tx_device_id; - rc = audio_tx_mute(ac_control, adev, mute); - if (!rc) tx_mute_status = mute; + audio_tx_mute(ac_control, adev, mute); + tx_mute_status = mute; mutex_unlock(&audio_path_lock); return 0; } From 95c25d91c3dd9fd1cad29d336532a8074e5397cc Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 17 Dec 2010 18:29:42 -0800 Subject: [PATCH 0732/2556] ARM: msm: only create rpcrouter pdev once Change-Id: I1e3bebe50c93eb4c4874d35d1c87ebafa4f88af5 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/smd_rpcrouter.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c index 6e0cf27f2b57b..ed9745a9a93fb 100644 --- a/arch/arm/mach-msm/smd_rpcrouter.c +++ b/arch/arm/mach-msm/smd_rpcrouter.c @@ -111,6 +111,7 @@ static void do_create_rpcrouter_pdev(struct work_struct *work); static DECLARE_WORK(work_read_data, do_read_data); static DECLARE_WORK(work_create_pdevs, do_create_pdevs); static DECLARE_WORK(work_create_rpcrouter_pdev, do_create_rpcrouter_pdev); +static atomic_t rpcrouter_pdev_created = ATOMIC_INIT(0); #define RR_STATE_IDLE 0 #define RR_STATE_HEADER 1 @@ -517,7 +518,8 @@ static int process_control_msg(union rr_control_msg *msg, int len) static void do_create_rpcrouter_pdev(struct work_struct *work) { - platform_device_register(&rpcrouter_pdev); + if (atomic_cmpxchg(&rpcrouter_pdev_created, 0, 1) == 0) + platform_device_register(&rpcrouter_pdev); } static void do_create_pdevs(struct work_struct *work) From 8fe3d55f969a78a13bb3a2db946efb4d4c8b3d02 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 28 Dec 2010 10:03:12 -0800 Subject: [PATCH 0733/2556] ARM: msm: only build board-qsd8x50 for machs that need it Change-Id: I31217a31cfcdf9697aec819ca065c7019459f7dc Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 4657a190dd1be..5c925972cc16d 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -78,7 +78,10 @@ obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-panel.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-mmc.o obj-$(CONFIG_MACH_MSM7X30_SURF) += board-surf7x30.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o -obj-$(CONFIG_ARCH_QSD8X50) += board-qsd8x50.o devices-qsd8x50.o +obj-$(CONFIG_ARCH_QSD8X50) += devices-qsd8x50.o +obj-$(CONFIG_MACH_QSD8X50_SURF) += board-qsd8x50.o +obj-$(CONFIG_MACH_QSD8X50A_ST1_5) += board-qsd8x50.o +obj-$(CONFIG_MACH_QSD8X50_FFA) += board-qsd8x50.o obj-$(CONFIG_ARCH_MSM8X60) += board-msm8x60.o obj-$(CONFIG_ARCH_MSM7X30) += gpiomux-7x30.o gpiomux-v1.o gpiomux.o From 0da778d7a7aec129b0e722d3327b0c57ff0dec26 Mon Sep 17 00:00:00 2001 From: Gregory Bean Date: Thu, 19 Aug 2010 09:07:51 -0700 Subject: [PATCH 0734/2556] [ARM] msm: board-sapphire: Use gpiolib instead of generic_gpio. Use gpiolib instead of generic_gpio to control gpio lines, as generic_gpio will soon be retired. Change-Id: I02089df2322eb783d0f7a0efe081a72165c7da51 Signed-off-by: Gregory Bean --- arch/arm/mach-msm/board-sapphire.c | 14 ++++++++------ arch/arm/mach-msm/board-sapphire.h | 1 - 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index 0d03d282e8bfa..6986598cf0543 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -263,7 +263,7 @@ static int gpio_tp_ls_en = SAPPHIRE_TP_LS_EN; static int sapphire_ts_power(int on) { if (on) { - sapphire_gpio_write(NULL, SAPPHIRE_GPIO_TP_EN, 1); + gpio_set_value(SAPPHIRE_GPIO_TP_EN, 1); /* touchscreen must be powered before we enable i2c pullup */ msleep(2); /* enable touch panel level shift */ @@ -272,7 +272,7 @@ static int sapphire_ts_power(int on) } else { gpio_direction_output(gpio_tp_ls_en, 0); udelay(50); - sapphire_gpio_write(NULL, SAPPHIRE_GPIO_TP_EN, 0); + gpio_set_value(SAPPHIRE_GPIO_TP_EN, 0); } return 0; @@ -1128,6 +1128,10 @@ static void __init sapphire_init(void) * Setup common MSM GPIOS */ config_gpios(); + rc = gpio_request(SAPPHIRE_GPIO_TP_EN, NULL); + if (rc < 0) + pr_err("%s: gpio_request(%d) failure %d\n", + __func__, SAPPHIRE_GPIO_TP_EN, rc); msm_hw_reset_hook = sapphire_reset; @@ -1150,10 +1154,8 @@ static void __init sapphire_init(void) gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0); gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 1); /* put the AF VCM in powerdown mode to avoid noise */ - if (sapphire_is_5M_camera()) - sapphire_gpio_write(NULL, SAPPHIRE_GPIO_VCM_PWDN, 0); - else - sapphire_gpio_write(NULL, SAPPHIRE_GPIO_VCM_PWDN, 1); + gpio_set_value(SAPPHIRE_GPIO_VCM_PWDN, !sapphire_is_5M_camera()); + mdelay(100); printk(KERN_DEBUG "sapphire_is_5M_camera=%d\n", diff --git a/arch/arm/mach-msm/board-sapphire.h b/arch/arm/mach-msm/board-sapphire.h index d96760a25ef8a..fd4ccd8ec140f 100644 --- a/arch/arm/mach-msm/board-sapphire.h +++ b/arch/arm/mach-msm/board-sapphire.h @@ -219,6 +219,5 @@ unsigned int sapphire_get_hwid(void); unsigned int sapphire_get_skuid(void); unsigned int is_12pin_camera(void); int sapphire_is_5M_camera(void); -int sapphire_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on); #endif /* GUARD */ From 652b6371f872fd76389266d27a1e6a3204928e00 Mon Sep 17 00:00:00 2001 From: Gregory Bean Date: Sun, 15 Aug 2010 11:16:14 -0700 Subject: [PATCH 0735/2556] [ARM] msm: Convert sapphire gpio to gpiolib. Change-Id: I5a4c637639afdb1db125021f92ee17f09b538e4e Signed-off-by: Gregory Bean ARM: msm: sapphire: fix input board gpio chip Change-Id: I8de9bf60710016016e0f125c2c4c7e2f6bee7ee1 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-sapphire-gpio.c | 56 ++++++++++++------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire-gpio.c b/arch/arm/mach-msm/board-sapphire-gpio.c index 375440c5afb9d..90d9e837739ad 100644 --- a/arch/arm/mach-msm/board-sapphire-gpio.c +++ b/arch/arm/mach-msm/board-sapphire-gpio.c @@ -22,7 +22,6 @@ #include #include -#include "gpio_chip.h" #include "board-sapphire.h" #ifdef DEBUG_SAPPHIRE_GPIO @@ -65,8 +64,10 @@ static uint8_t sapphire_sleep_int_mask[] = { static int sapphire_suspended; -static int sapphire_gpio_read(struct gpio_chip *chip, unsigned n) +static int sapphire_gpio_get(struct gpio_chip *chip, unsigned offset) { + unsigned n = chip->base + offset; + if (n < SAPPHIRE_GPIO_INT_B0_BASE) /*MISCn*/ return !!(readb(CPLD_GPIO_REG(n)) & CPLD_GPIO_BIT_POS_MASK(n)); else if (n <= SAPPHIRE_GPIO_END) /*gpio n is INT pin*/ @@ -78,12 +79,13 @@ static int sapphire_gpio_read(struct gpio_chip *chip, unsigned n) /*CPLD Write only register :MISC2, MISC3, MISC4, MISC5 => reg=0,2,4,6 Reading from write-only registers is undefined, so the writing value should be kept in shadow for later usage.*/ -int sapphire_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on) +static void sapphire_gpio_set(struct gpio_chip *chip, unsigned offset, int on) { + unsigned n = chip->base + offset; unsigned long flags; uint8_t reg_val; if (n > SAPPHIRE_GPIO_END) - return -1; + return; local_irq_save(flags); reg_val = readb(CPLD_GPIO_REG(n)); @@ -96,35 +98,35 @@ int sapphire_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on) DBG("gpio=%d, l=0x%x\r\n", n, readb(SAPPHIRE_CPLD_INT_LEVEL)); local_irq_restore(flags); +} +static int sapphire_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + sapphire_gpio_set(chip, offset, value); return 0; } -static int sapphire_gpio_configure(struct gpio_chip *chip, unsigned int gpio, - unsigned long flags) +static int sapphire_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) { - if (flags & (GPIOF_OUTPUT_LOW | GPIOF_OUTPUT_HIGH)) - sapphire_gpio_write(chip, gpio, flags & GPIOF_OUTPUT_HIGH); - - DBG("gpio=%d, l=0x%x\r\n", gpio, readb(SAPPHIRE_CPLD_INT_LEVEL)); - return 0; } -static int sapphire_gpio_get_irq_num(struct gpio_chip *chip, unsigned int gpio, - unsigned int *irqp, unsigned long *irqnumflagsp) +static int sapphire_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { + unsigned gpio = chip->base + offset; + int irq; + DBG("gpio=%d, l=0x%x\r\n", gpio, readb(SAPPHIRE_CPLD_INT_LEVEL)); DBG("SAPPHIRE_GPIO_INT_B0_BASE=%d, SAPPHIRE_GPIO_LAST_INT=%d\r\n", SAPPHIRE_GPIO_INT_B0_BASE, SAPPHIRE_GPIO_LAST_INT); if ((gpio < SAPPHIRE_GPIO_INT_B0_BASE) || (gpio > SAPPHIRE_GPIO_LAST_INT)) return -ENOENT; - *irqp = SAPPHIRE_GPIO_TO_INT(gpio); - DBG("*irqp=%d\r\n", *irqp); - if (irqnumflagsp) - *irqnumflagsp = 0; - return 0; + irq = SAPPHIRE_GPIO_TO_INT(gpio); + DBG("irq=%d\r\n", irq); + return irq; } /*write 1 to clear INT status bit.*/ @@ -265,16 +267,14 @@ static struct irq_chip sapphire_gpio_irq_chip = { /*.set_type = sapphire_gpio_irq_set_type,*/ }; -/*Thomas:For CPLD*/ static struct gpio_chip sapphire_gpio_chip = { - .start = SAPPHIRE_GPIO_START, - .end = SAPPHIRE_GPIO_END, - .configure = sapphire_gpio_configure, - .get_irq_num = sapphire_gpio_get_irq_num, - .read = sapphire_gpio_read, - .write = sapphire_gpio_write, -/* .read_detect_status = sapphire_gpio_read_detect_status, - .clear_detect_status = sapphire_gpio_clear_detect_status */ + .base = SAPPHIRE_GPIO_START, + .ngpio = SAPPHIRE_GPIO_END - SAPPHIRE_GPIO_START + 1, + .direction_output = sapphire_gpio_direction_output, + .direction_input = sapphire_gpio_direction_input, + .get = sapphire_gpio_get, + .set = sapphire_gpio_set, + .to_irq = sapphire_gpio_to_irq, }; struct sysdev_class sapphire_sysdev_class = { @@ -301,7 +301,7 @@ int sapphire_init_gpio(void) set_irq_flags(i, IRQF_VALID); } - register_gpio_chip(&sapphire_gpio_chip); + gpiochip_add(&sapphire_gpio_chip); /*setup CPLD INT connecting to SOC's gpio 17 */ set_irq_type(MSM_GPIO_TO_INT(17), IRQF_TRIGGER_HIGH); From 7469e10bd5181ee99afb25e8995ff2fed14c9592 Mon Sep 17 00:00:00 2001 From: Gregory Bean Date: Wed, 18 Aug 2010 09:53:03 -0700 Subject: [PATCH 0736/2556] [ARM] msm: sapphire: remove uneccessary includes Remove includes of "gpio_chip.h" which provide no value. Change-Id: I5a138ecfe5ea9f368a0b11e9689f372c890cd0bf Signed-off-by: Gregory Bean --- arch/arm/mach-msm/board-sapphire-keypad.c | 1 - arch/arm/mach-msm/board-sapphire-mmc.c | 1 - arch/arm/mach-msm/board-sapphire-panel.c | 1 - arch/arm/mach-msm/board-sapphire-rfkill.c | 1 - arch/arm/mach-msm/board-sapphire.c | 2 -- 5 files changed, 6 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire-keypad.c b/arch/arm/mach-msm/board-sapphire-keypad.c index 5c8fc37649a3c..a48c2d3f375cd 100755 --- a/arch/arm/mach-msm/board-sapphire-keypad.c +++ b/arch/arm/mach-msm/board-sapphire-keypad.c @@ -17,7 +17,6 @@ #include #include #include -#include "gpio_chip.h" #include "board-sapphire.h" static char *keycaps = "--qwerty"; #undef MODULE_PARAM_PREFIX diff --git a/arch/arm/mach-msm/board-sapphire-mmc.c b/arch/arm/mach-msm/board-sapphire-mmc.c index a8a7963abe4b6..5219dd93d1998 100755 --- a/arch/arm/mach-msm/board-sapphire-mmc.c +++ b/arch/arm/mach-msm/board-sapphire-mmc.c @@ -31,7 +31,6 @@ #include #include "devices.h" -#include "gpio_chip.h" #include "board-sapphire.h" #include "proc_comm.h" diff --git a/arch/arm/mach-msm/board-sapphire-panel.c b/arch/arm/mach-msm/board-sapphire-panel.c index f71ca0113c5fe..775ff5b194346 100644 --- a/arch/arm/mach-msm/board-sapphire-panel.c +++ b/arch/arm/mach-msm/board-sapphire-panel.c @@ -28,7 +28,6 @@ #include #include -#include "gpio_chip.h" #include "board-sapphire.h" #include "proc_comm.h" #include "devices.h" diff --git a/arch/arm/mach-msm/board-sapphire-rfkill.c b/arch/arm/mach-msm/board-sapphire-rfkill.c index 2fd6ea198e3df..fba0a16618ebe 100644 --- a/arch/arm/mach-msm/board-sapphire-rfkill.c +++ b/arch/arm/mach-msm/board-sapphire-rfkill.c @@ -21,7 +21,6 @@ #include #include #include -#include "gpio_chip.h" #include "board-sapphire.h" static struct rfkill *bt_rfk; diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index 6986598cf0543..422a7b53db382 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -52,8 +52,6 @@ #include #include - -#include "gpio_chip.h" #include "board-sapphire.h" #include From 4dd45fe4f2eb2a160baad4158f0f934d00ebe98f Mon Sep 17 00:00:00 2001 From: Gregory Bean Date: Wed, 18 Aug 2010 09:53:03 -0700 Subject: [PATCH 0737/2556] [ARM] msm: troue: remove uneccessary includes Remove includes of "gpio_chip.h" which provide no value. Change-Id: I5a138ecfe5ea9f368a0b11e9689f372c890cd0bf Signed-off-by: Gregory Bean --- arch/arm/mach-msm/board-trout.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 1ce43e1c25ca2..86ce9905e1653 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -55,8 +55,6 @@ #include "board-trout.h" -#include "gpio_chip.h" - #include #include #include From 61d962a037363ee968f310c5921530e320d4b2e3 Mon Sep 17 00:00:00 2001 From: Gregory Bean Date: Wed, 18 Aug 2010 09:53:03 -0700 Subject: [PATCH 0738/2556] [ARM] msm: devices_htc: remove uneccessary includes Remove includes of "gpio_chip.h" which provide no value. Change-Id: I5a138ecfe5ea9f368a0b11e9689f372c890cd0bf Signed-off-by: Gregory Bean --- arch/arm/mach-msm/devices_htc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c index e5f65e720656d..f270df4054198 100644 --- a/arch/arm/mach-msm/devices_htc.c +++ b/arch/arm/mach-msm/devices_htc.c @@ -20,7 +20,6 @@ #include #include #include -#include "gpio_chip.h" #include "devices.h" #include #include From f4e1410dd9aed2ecf58ebbcf369333fe62b6fdf1 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 4 Jan 2011 18:34:46 -0800 Subject: [PATCH 0739/2556] ARM: configs: mahimahi: select misc_devices Change-Id: Ie6b6e9c6933d619eae403df759e81ce024608922 Signed-off-by: Dima Zavin --- arch/arm/configs/mahimahi_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/mahimahi_defconfig b/arch/arm/configs/mahimahi_defconfig index 944573507ba5f..942941adcbd32 100644 --- a/arch/arm/configs/mahimahi_defconfig +++ b/arch/arm/configs/mahimahi_defconfig @@ -160,6 +160,7 @@ CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y CONFIG_BLK_DEV_LOOP=y +CONFIG_MISC_DEVICES=y CONFIG_UID_STAT=y CONFIG_SENSORS_AKM8973=y CONFIG_VP_A1026=y From 7c2afa80aecb1a30f37418881e99508ec46fd638 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 5 Jan 2011 15:35:20 -0800 Subject: [PATCH 0740/2556] ARM: configs: mahimahi: use correct mmc_msm config option Change-Id: Iaeb772019d9018865364510bdf41701d7787a9a3 Signed-off-by: Dima Zavin --- arch/arm/configs/mahimahi_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/mahimahi_defconfig b/arch/arm/configs/mahimahi_defconfig index 942941adcbd32..8544abd4438f2 100644 --- a/arch/arm/configs/mahimahi_defconfig +++ b/arch/arm/configs/mahimahi_defconfig @@ -235,7 +235,7 @@ CONFIG_MMC_EMBEDDED_SDIO=y CONFIG_MMC_PARANOID_SD_INIT=y # CONFIG_MMC_BLOCK_BOUNCE is not set CONFIG_MMC_BLOCK_DEFERRED_RESUME=y -CONFIG_MMC_MSM7X00A=y +CONFIG_MMC_MSM=y CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y CONFIG_LEDS_GPIO=y From a59f52602c72666496344cf4160d59257652b159 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 5 Jan 2011 16:12:44 -0800 Subject: [PATCH 0741/2556] usb: gadget: msm: fix driver registration on 2.6.37 Change-Id: Iac47f5c689b3846ca6162ceb53ce51bd432c00a0 Signed-off-by: Dima Zavin --- drivers/usb/gadget/msm72k_udc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 9b0e697f972b3..3ae42edc037da 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -1821,14 +1821,15 @@ static int msm72k_probe(struct platform_device *pdev) return 0; } -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) { struct usb_info *ui = the_usb_info; int retval, n; if (!driver || driver->speed < USB_SPEED_FULL - || !driver->bind + || !bind || !driver->disconnect || !driver->setup) return -EINVAL; @@ -1861,7 +1862,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) if (retval) goto fail; - retval = driver->bind(&ui->gadget); + retval = bind(&ui->gadget); if (retval) { INFO("bind to driver %s --> error %d\n", driver->driver.name, retval); @@ -1885,7 +1886,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) ui->gadget.dev.driver = NULL; return retval; } -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { From 40185b798ef3017c84343c9ed7086202eb0c43b3 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 5 Jan 2011 16:14:28 -0800 Subject: [PATCH 0742/2556] ARM: msm: gpio: fix compilation, get rid of duplicate first_gpio_irq define Change-Id: Id2a9fad41acba22a443c548cade279a856541332 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/gpio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/gpio.c b/arch/arm/mach-msm/gpio.c index ba3e970aa52f5..2fe2e81fd79f8 100644 --- a/arch/arm/mach-msm/gpio.c +++ b/arch/arm/mach-msm/gpio.c @@ -20,11 +20,10 @@ #include #include #include +#include #include "gpio_hw.h" #include "gpiomux.h" -#define FIRST_GPIO_IRQ MSM_GPIO_TO_INT(0) - #include "proc_comm.h" #include "smd_private.h" From 6712c5d4be5c4eca45e986c04720ad1091990831 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 5 Jan 2011 16:58:04 -0800 Subject: [PATCH 0743/2556] ARM: msm: mahimahi: use the msm-specific msm_mmc_platform_data Change-Id: I5af885b947f983f7e15dea6b0e03a9a63e57872c Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-mahimahi-mmc.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi-mmc.c b/arch/arm/mach-msm/board-mahimahi-mmc.c index 78ed97fc49d39..2d339e25ced43 100644 --- a/arch/arm/mach-msm/board-mahimahi-mmc.c +++ b/arch/arm/mach-msm/board-mahimahi-mmc.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include @@ -175,7 +175,7 @@ static unsigned int mahimahi_sdslot_status_rev0(struct device *dev) int mahimahi_microp_sdslot_status_register(void (*cb)(int, void *), void *); unsigned int mahimahi_microp_sdslot_status(struct device *); -static struct mmc_platform_data mahimahi_sdslot_data = { +static struct msm_mmc_platform_data mahimahi_sdslot_data = { .ocr_mask = MAHIMAHI_MMC_VDD, .status = mahimahi_microp_sdslot_status, .register_status_notify = mahimahi_microp_sdslot_status_register, @@ -236,7 +236,7 @@ static unsigned int mahimahi_wifi_status(struct device *dev) return mahimahi_wifi_cd; } -static struct mmc_platform_data mahimahi_wifi_data = { +static struct msm_mmc_platform_data mahimahi_wifi_data = { .ocr_mask = MMC_VDD_28_29, .built_in = 1, .status = mahimahi_wifi_status, @@ -287,9 +287,6 @@ int mahimahi_wifi_reset(int on) return 0; } -int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, - unsigned int stat_irq, unsigned long stat_irq_flags); - int __init mahimahi_init_mmc(unsigned int sys_rev, unsigned debug_uart) { uint32_t id; From c0c2586db4422aae2b21034d4e457e09eeb78d48 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 5 Jan 2011 16:59:06 -0800 Subject: [PATCH 0744/2556] ARM: msm: mahimahi: remove membank .node member Change-Id: I736ab9f17789a16d9836d3749c566fcc0ac03049 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-mahimahi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 9219188402b7a..9a88f44633c93 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -1142,10 +1142,8 @@ static void __init mahimahi_fixup(struct machine_desc *desc, struct tag *tags, { mi->nr_banks = 2; mi->bank[0].start = PHYS_OFFSET; - mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); mi->bank[0].size = (219*1024*1024); mi->bank[1].start = MSM_HIGHMEM_BASE; - mi->bank[1].node = PHYS_TO_NID(MSM_HIGHMEM_BASE); mi->bank[1].size = MSM_HIGHMEM_SIZE; } From aa3b7c92f17271c9615345f1eb7f5addde10460b Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 5 Jan 2011 16:59:34 -0800 Subject: [PATCH 0745/2556] ARM: msm: swordfish: remove membank .node member, phys_io members from mach Change-Id: Ic4f112aed365beb04b5fa96afe31dfe476d6c47d Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-swordfish.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/arch/arm/mach-msm/board-swordfish.c b/arch/arm/mach-msm/board-swordfish.c index 70fac49f42f8f..12948eb9e1703 100644 --- a/arch/arm/mach-msm/board-swordfish.c +++ b/arch/arm/mach-msm/board-swordfish.c @@ -328,7 +328,6 @@ static void __init swordfish_fixup(struct machine_desc *desc, struct tag *tags, { mi->nr_banks = 1; mi->bank[0].start = PHYS_OFFSET; - mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); mi->bank[0].size = (101*1024*1024); } @@ -339,10 +338,6 @@ static void __init swordfish_map_io(void) } MACHINE_START(SWORDFISH, "Swordfish Board (QCT SURF8250)") -#ifdef CONFIG_MSM_DEBUG_UART - .phys_io = MSM_DEBUG_UART_PHYS, - .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, -#endif .boot_params = 0x20000100, .fixup = swordfish_fixup, .map_io = swordfish_map_io, @@ -352,10 +347,6 @@ MACHINE_START(SWORDFISH, "Swordfish Board (QCT SURF8250)") MACHINE_END MACHINE_START(QSD8X50_FFA, "qsd8x50 FFA Board (QCT FFA8250)") -#ifdef CONFIG_MSM_DEBUG_UART - .phys_io = MSM_DEBUG_UART_PHYS, - .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, -#endif .boot_params = 0x20000100, .fixup = swordfish_fixup, .map_io = swordfish_map_io, From 3706fc2f703a52c621154648f86f876f01422b17 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 5 Jan 2011 17:00:01 -0800 Subject: [PATCH 0746/2556] ARM: msm: fix devices-qsd8x50 from bad rebase Change-Id: Ic988c68d460f914d15647a36e371162d1d7f23ae Signed-off-by: Dima Zavin --- arch/arm/mach-msm/devices-qsd8x50.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/arch/arm/mach-msm/devices-qsd8x50.c b/arch/arm/mach-msm/devices-qsd8x50.c index b4566eb18849c..a38f05108adfa 100644 --- a/arch/arm/mach-msm/devices-qsd8x50.c +++ b/arch/arm/mach-msm/devices-qsd8x50.c @@ -263,11 +263,6 @@ struct platform_device msm_device_nand = { }, }; -struct platform_device msm_device_smd = { - .name = "msm_smd", - .id = -1, -}; - static struct resource resources_sdc1[] = { { .start = MSM_SDC1_PHYS, @@ -431,7 +426,7 @@ static struct platform_device *msm_sdcc_devices[] __initdata = { &msm_device_sdc4, }; -int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, +int __init msm_add_sdcc(unsigned int controller, struct msm_mmc_platform_data *plat, unsigned int stat_irq, unsigned long stat_irq_flags) { struct platform_device *pdev; From 62bee4c0fc9062a84b1fabae9f5119ed75f78894 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 5 Jan 2011 17:00:36 -0800 Subject: [PATCH 0747/2556] ARM: msm: htc_acoustic_qsd: fix compile errors Change-Id: I6e1bbcb98e5afef25f52382915f3bb4c85d9542a Signed-off-by: Dima Zavin --- arch/arm/mach-msm/htc_acoustic_qsd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/htc_acoustic_qsd.c b/arch/arm/mach-msm/htc_acoustic_qsd.c index ce3c3a0bbfe2c..390ab6d7ff2dd 100644 --- a/arch/arm/mach-msm/htc_acoustic_qsd.c +++ b/arch/arm/mach-msm/htc_acoustic_qsd.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include From 0830593a70f75553777ed9a7482bfe43769d7005 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 5 Jan 2011 17:00:54 -0800 Subject: [PATCH 0748/2556] ARM: msm: qdsp6: fix compile errors Change-Id: If292a820f3eed1843f55e3c38b4b559da4f5c46f Signed-off-by: Dima Zavin --- arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h index 54614a9877035..2b4df49ebfc52 100644 --- a/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h +++ b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h @@ -20,6 +20,8 @@ #define AUDIO_FLAG_READ 0 #define AUDIO_FLAG_WRITE 1 +#include + struct audio_buffer { dma_addr_t phys; void *data; From 1a4c6d888ddf6d803c00a20f1c8f920395df23b9 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 5 Jan 2011 17:01:30 -0800 Subject: [PATCH 0749/2556] ARM: msm: qdsp6: convert audio ioctl to unlocked_ioctl Change-Id: I29351cc0091405ebf8b370eda12310cb7a2fabd5 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/qdsp6/audio_ctl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/qdsp6/audio_ctl.c b/arch/arm/mach-msm/qdsp6/audio_ctl.c index 170b3eb10f6e6..c7d5bc703357e 100644 --- a/arch/arm/mach-msm/qdsp6/audio_ctl.c +++ b/arch/arm/mach-msm/qdsp6/audio_ctl.c @@ -79,8 +79,7 @@ static int q6_open(struct inode *inode, struct file *file) return 0; } -static int q6_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long q6_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int rc; uint32_t n; @@ -158,7 +157,7 @@ static int q6_release(struct inode *inode, struct file *file) static struct file_operations q6_dev_fops = { .owner = THIS_MODULE, .open = q6_open, - .ioctl = q6_ioctl, + .unlocked_ioctl = q6_ioctl, .release = q6_release, }; From e453f2f8558dfadf000c829ef46c5c517da88494 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 5 Jan 2011 17:03:07 -0800 Subject: [PATCH 0750/2556] input: misc: capella_cm3602: fix compile errors on 37 Change-Id: I33370cb23e78ea6b8782de9bf0d2d60777752223 Signed-off-by: Dima Zavin --- drivers/input/misc/capella_cm3602.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/misc/capella_cm3602.c b/drivers/input/misc/capella_cm3602.c index b03eb62482ef2..453a88c1bb2b0 100644 --- a/drivers/input/misc/capella_cm3602.c +++ b/drivers/input/misc/capella_cm3602.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include From f375dc95501d9bad72c076fa8b8284d34667dcc2 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 5 Jan 2011 17:03:41 -0800 Subject: [PATCH 0751/2556] leds: leds-cpld: fix compile errors on 37 Change-Id: I6f3482b226d416652540d06ed817f37db75288a1 Signed-off-by: Dima Zavin --- drivers/leds/leds-cpld.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/leds/leds-cpld.c b/drivers/leds/leds-cpld.c index eab004c3d75c5..2a45093f1aff1 100644 --- a/drivers/leds/leds-cpld.c +++ b/drivers/leds/leds-cpld.c @@ -180,7 +180,7 @@ static struct device_attribute dev_attr_blink_all = { .store = cpldled_blink_all_store, }; -static void led_brightness_set(struct led_classdev *led_cdev, +static void cpld_led_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct CPLD_LED_data *CPLD_LED; @@ -285,16 +285,16 @@ static int CPLD_LED_probe(struct platform_device *pdev) writeb(0x00, _g_cpld_led_addr); CPLD_LED->leds[0].name = "red"; - CPLD_LED->leds[0].brightness_set = led_brightness_set; + CPLD_LED->leds[0].brightness_set = cpld_led_brightness_set; CPLD_LED->leds[1].name = "green"; - CPLD_LED->leds[1].brightness_set = led_brightness_set; + CPLD_LED->leds[1].brightness_set = cpld_led_brightness_set; CPLD_LED->leds[2].name = "blue"; - CPLD_LED->leds[2].brightness_set = led_brightness_set; + CPLD_LED->leds[2].brightness_set = cpld_led_brightness_set; CPLD_LED->leds[3].name = "jogball-backlight"; - CPLD_LED->leds[3].brightness_set = led_brightness_set; + CPLD_LED->leds[3].brightness_set = cpld_led_brightness_set; spin_lock_init(&CPLD_LED->data_lock); From a5e8dd7009167fccd5175af0bde16d9ed24e3478 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 5 Jan 2011 17:10:18 -0800 Subject: [PATCH 0752/2556] misc: a1026: convert to unlocked_ioctl, take a1026_lock in ioctl Change-Id: Ia309ead93adec9f81ea7b39942959dcfc73b7d13 Signed-off-by: Dima Zavin --- drivers/misc/a1026.c | 76 ++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/drivers/misc/a1026.c b/drivers/misc/a1026.c index 7eaa3e1916303..1ef882be56ca4 100644 --- a/drivers/misc/a1026.c +++ b/drivers/misc/a1026.c @@ -871,9 +871,8 @@ static int exe_cmd_in_file(unsigned char *incmd) } #endif /* ENABLE_DIAG_IOCTLS */ -static int -a1026_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) +static long +a1026_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; struct a1026img img; @@ -886,28 +885,37 @@ a1026_ioctl(struct inode *inode, struct file *file, unsigned int cmd, int pathid = 0; unsigned int ns_state; + mutex_lock(&a1026_lock); switch (cmd) { case A1026_BOOTUP_INIT: img.buf = 0; img.img_size = 0; - if (copy_from_user(&img, argp, sizeof(img))) - return -EFAULT; + if (copy_from_user(&img, argp, sizeof(img))) { + rc = -EFAULT; + goto out; + } rc = a1026_bootup_init(file, &img); break; case A1026_SET_CONFIG: - if (copy_from_user(&pathid, argp, sizeof(pathid))) - return -EFAULT; + if (copy_from_user(&pathid, argp, sizeof(pathid))) { + rc = -EFAULT; + goto out; + } rc = a1026_set_config(pathid, A1026_CONFIG_FULL); if (rc < 0) pr_err("%s: A1026_SET_CONFIG (%d) error %d!\n", __func__, pathid, rc); break; case A1026_SET_NS_STATE: - if (copy_from_user(&ns_state, argp, sizeof(ns_state))) - return -EFAULT; + if (copy_from_user(&ns_state, argp, sizeof(ns_state))) { + rc = -EFAULT; + goto out; + } pr_info("%s: set noise suppression %d\n", __func__, ns_state); - if (ns_state < 0 || ns_state >= A1026_NS_NUM_STATES) - return -EINVAL; + if (ns_state < 0 || ns_state >= A1026_NS_NUM_STATES) { + rc = -EINVAL; + goto out; + } a1026_NS_state = ns_state; if (!a1026_suspended) a1026_set_config(a1026_current_config, @@ -917,9 +925,11 @@ a1026_ioctl(struct inode *inode, struct file *file, unsigned int cmd, case A1026_SET_MIC_ONOFF: rc = chk_wakeup_a1026(); if (rc < 0) - return rc; - if (copy_from_user(&mic_cases, argp, sizeof(mic_cases))) - return -EFAULT; + goto out; + if (copy_from_user(&mic_cases, argp, sizeof(mic_cases))) { + rc = -EFAULT; + goto out; + } rc = a1026_set_mic_state(mic_cases); if (rc < 0) pr_err("%s: A1026_SET_MIC_ONOFF %d error %d!\n", @@ -928,32 +938,38 @@ a1026_ioctl(struct inode *inode, struct file *file, unsigned int cmd, case A1026_SET_MICSEL_ONOFF: rc = chk_wakeup_a1026(); if (rc < 0) - return rc; - if (copy_from_user(&mic_sel, argp, sizeof(mic_sel))) - return -EFAULT; + goto out; + if (copy_from_user(&mic_sel, argp, sizeof(mic_sel))) { + rc = -EFAULT; + goto out; + } gpio_set_value(pdata->gpio_a1026_micsel, !!mic_sel); rc = 0; break; case A1026_READ_DATA: rc = chk_wakeup_a1026(); if (rc < 0) - return rc; + goto out; rc = a1026_i2c_read(msg, 4); - if (copy_to_user(argp, &msg, 4)) - return -EFAULT; + if (copy_to_user(argp, &msg, 4)) { + rc = -EFAULT; + goto out; + } break; case A1026_WRITE_MSG: rc = chk_wakeup_a1026(); if (rc < 0) - return rc; - if (copy_from_user(msg, argp, sizeof(msg))) - return -EFAULT; + goto out; + if (copy_from_user(msg, argp, sizeof(msg))) { + rc = -EFAULT; + goto out; + } rc = a1026_i2c_write(msg, 4); break; case A1026_SYNC_CMD: rc = chk_wakeup_a1026(); if (rc < 0) - return rc; + goto out; msg[0] = 0x80; msg[1] = 0x00; msg[2] = 0x00; @@ -963,9 +979,11 @@ a1026_ioctl(struct inode *inode, struct file *file, unsigned int cmd, case A1026_SET_CMD_FILE: rc = chk_wakeup_a1026(); if (rc < 0) - return rc; - if (copy_from_user(msg, argp, sizeof(msg))) - return -EFAULT; + goto out; + if (copy_from_user(msg, argp, sizeof(msg))) { + rc = -EFAULT; + goto out; + } rc = exe_cmd_in_file(msg); break; #endif /* ENABLE_DIAG_IOCTLS */ @@ -975,6 +993,8 @@ a1026_ioctl(struct inode *inode, struct file *file, unsigned int cmd, break; } +out: + mutex_unlock(&a1026_lock); return rc; } @@ -982,7 +1002,7 @@ static const struct file_operations a1026_fops = { .owner = THIS_MODULE, .open = a1026_open, .release = a1026_release, - .ioctl = a1026_ioctl, + .unlocked_ioctl = a1026_ioctl, }; static struct miscdevice a1026_device = { From ffd2d75f78d0c888d22f42d79d5e3a50c330648b Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 5 Jan 2011 18:38:14 -0800 Subject: [PATCH 0753/2556] ARM: msm: mahimahi: fix tpa2xxx ioctl to be unlocked Change-Id: I6e98dd2d27f7a75006f2f7b4d3bf72ba0c98b4e8 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-mahimahi-tpa2018d1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi-tpa2018d1.c b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.c index 78919b9b19543..7f02762ef50ee 100644 --- a/arch/arm/mach-msm/board-mahimahi-tpa2018d1.c +++ b/arch/arm/mach-msm/board-mahimahi-tpa2018d1.c @@ -149,7 +149,7 @@ static int tpa2018d1_read_config(void __user *argp) return rc; } -static int tpa2018d1_ioctl(struct inode *inode, struct file *file, +static long tpa2018d1_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; @@ -247,7 +247,7 @@ static struct file_operations tpa2018d1_fops = { .owner = THIS_MODULE, .open = tpa2018d1_open, .release = tpa2018d1_release, - .ioctl = tpa2018d1_ioctl, + .unlocked_ioctl = tpa2018d1_ioctl, }; static struct miscdevice tpa2018d1_device = { From 8965c411056bd1fcc6363902e528238a7751cf4a Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 5 Jan 2011 21:22:20 -0800 Subject: [PATCH 0754/2556] ARM: msm: mahimahi: fix microp bma150 ioctl to be unlocked_ioctl, add mutex Change-Id: I6f206204aecf720b6bb71aa47ae31e7f5dabf60e Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-mahimahi-microp.c | 38 +++++++++++++++-------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi-microp.c b/arch/arm/mach-msm/board-mahimahi-microp.c index da30672d26a16..aef72af95e1ce 100644 --- a/arch/arm/mach-msm/board-mahimahi-microp.c +++ b/arch/arm/mach-msm/board-mahimahi-microp.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include #include @@ -1560,6 +1560,8 @@ static int gsensor_write(uint8_t *data) return ret; } +static DEFINE_MUTEX(bma150_lock); + static int bma150_open(struct inode *inode, struct file *file) { pr_debug("%s\n", __func__); @@ -1571,8 +1573,7 @@ static int bma150_release(struct inode *inode, struct file *file) return 0; } -static int bma150_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long bma150_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; char rwbuf[8]; @@ -1594,31 +1595,36 @@ static int bma150_ioctl(struct inode *inode, struct file *file, break; } + mutex_lock(&bma150_lock); switch (cmd) { case BMA_IOCTL_INIT: ret = gsensor_init_hw(); if (ret < 0) - return ret; + goto err; break; case BMA_IOCTL_READ: - if (rwbuf[0] < 1) - return -EINVAL; + if (rwbuf[0] < 1) { + ret = -EINVAL; + goto err; + } ret = gsensor_read(rwbuf); if (ret < 0) - return ret; + goto err; break; case BMA_IOCTL_WRITE: - if (rwbuf[0] < 2) - return -EINVAL; + if (rwbuf[0] < 2) { + ret = -EINVAL; + goto err; + } ret = gsensor_write(rwbuf); if (ret < 0) - return ret; + goto err; break; case BMA_IOCTL_READ_ACCELERATION: ret = gsensor_read_acceleration(&buf[0]); if (ret < 0) - return ret; + goto err; break; case BMA_IOCTL_SET_MODE: bma150_set_mode(rwbuf[0]); @@ -1627,8 +1633,10 @@ static int bma150_ioctl(struct inode *inode, struct file *file, temp = 0; break; default: - return -ENOTTY; + ret = -ENOTTY; + goto err; } + mutex_unlock(&bma150_lock); switch (cmd) { case BMA_IOCTL_READ: @@ -1648,13 +1656,17 @@ static int bma150_ioctl(struct inode *inode, struct file *file, } return 0; + +err: + mutex_unlock(&bma150_lock); + return ret; } static struct file_operations bma_fops = { .owner = THIS_MODULE, .open = bma150_open, .release = bma150_release, - .ioctl = bma150_ioctl, + .unlocked_ioctl = bma150_ioctl, }; static struct miscdevice spi_bma_device = { From 80834c7a3d8c41a8a42974e9e506f17f830dc89f Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 7 Jan 2011 13:45:44 -0800 Subject: [PATCH 0755/2556] ARM: msm: hw3d: fix section mismatch warning Change-Id: I9a2fe46c1dcc91c50601e1a9f2e41b32f8f34b8e Signed-off-by: Dima Zavin --- arch/arm/mach-msm/hw3d.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/hw3d.c b/arch/arm/mach-msm/hw3d.c index aef7f37bf0164..05d562129e613 100644 --- a/arch/arm/mach-msm/hw3d.c +++ b/arch/arm/mach-msm/hw3d.c @@ -722,7 +722,7 @@ static int hw3d_resume(struct platform_device *pdev) return 0; } -static int __init hw3d_probe(struct platform_device *pdev) +static int __devinit hw3d_probe(struct platform_device *pdev) { struct hw3d_info *info; #define DEV_MASTER MKDEV(MAJOR(info->devno), MINOR_MASTER) From edd12663022a7969a16eb9e8bbc1461a3117a9ad Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 7 Jan 2011 13:46:04 -0800 Subject: [PATCH 0756/2556] ARM: msm: htc_pwrsink: fix section mismatch warning Change-Id: I9b953ddd95b1c58e8809bd6c003f931f5c819992 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/htc_pwrsink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/htc_pwrsink.c b/arch/arm/mach-msm/htc_pwrsink.c index 12d6182e70136..f4e8b3859422d 100644 --- a/arch/arm/mach-msm/htc_pwrsink.c +++ b/arch/arm/mach-msm/htc_pwrsink.c @@ -237,7 +237,7 @@ struct early_suspend htc_pwrsink_early_suspend = { }; #endif -static int __init htc_pwrsink_probe(struct platform_device *pdev) +static int __devinit htc_pwrsink_probe(struct platform_device *pdev) { struct pwr_sink_platform_data *pdata = pdev->dev.platform_data; int i; From 8392e4943b73428360bea417ddbdf89eb21835fb Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 7 Jan 2011 13:47:01 -0800 Subject: [PATCH 0757/2556] ARM: msm: htc_battery: fix compile error on 37 Change-Id: Ic600ac0dcf53f6cbae323ba5b93d9c251ce8e1f5 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/htc_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/htc_battery.c b/arch/arm/mach-msm/htc_battery.c index e1dbbf55675e3..d6b9e8317f766 100644 --- a/arch/arm/mach-msm/htc_battery.c +++ b/arch/arm/mach-msm/htc_battery.c @@ -501,7 +501,7 @@ static int htc_battery_get_property(struct power_supply *psy, #define HTC_BATTERY_ATTR(_name) \ { \ - .attr = { .name = #_name, .mode = S_IRUGO, .owner = THIS_MODULE }, \ + .attr = { .name = #_name, .mode = S_IRUGO }, \ .show = htc_battery_show_property, \ .store = NULL, \ } From 63ef698d1ea790f44122519d61f3c799bf8b6386 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 7 Jan 2011 13:47:28 -0800 Subject: [PATCH 0758/2556] video: msm: mddi: fix compile error in debugging code Change-Id: I43becdfa3b281ac3476791d00c66a011576f9f0e Signed-off-by: Dima Zavin --- drivers/video/msm/mddi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c index 4f136dfed8911..2f337da613c7e 100644 --- a/drivers/video/msm/mddi.c +++ b/drivers/video/msm/mddi.c @@ -243,6 +243,8 @@ static void mddi_handle_rev_data_avail(struct mddi_info *mddi) return; if (mddi_debug_flags & 1) { + int i; + union mddi_rev *rev = mddi->rev_data; printk(KERN_INFO "INT %x, STAT %x, CURR_REV_PTR %x\n", mddi_readl(INT), mddi_readl(STAT), mddi_readl(CURR_REV_PTR)); From dec9e0f89f0c578b9cab76f5e6cb4619b6b0723c Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 7 Jan 2011 13:47:47 -0800 Subject: [PATCH 0759/2556] ARM: msm: trout: fix building on 2.6.37 Change-Id: I95298854d0a35b55a6178e61f804ccfdcb07427e Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-trout-mmc.c | 7 ++----- arch/arm/mach-msm/board-trout.c | 3 +-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-msm/board-trout-mmc.c b/arch/arm/mach-msm/board-trout-mmc.c index 0b3da5a25fb81..3fe9c00ae0d10 100644 --- a/arch/arm/mach-msm/board-trout-mmc.c +++ b/arch/arm/mach-msm/board-trout-mmc.c @@ -27,9 +27,6 @@ #define DEBUG_SDSLOT_VDD 1 -extern int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, - unsigned int stat_irq, unsigned long stat_irq_flags); - /* ---- COMMON ---- */ static void config_gpio_table(uint32_t *table, int len) { @@ -164,7 +161,7 @@ static unsigned int trout_sdslot_status(struct device *dev) | MMC_VDD_25_26 | MMC_VDD_26_27 | MMC_VDD_27_28 \ | MMC_VDD_28_29 | MMC_VDD_29_30 -static struct mmc_platform_data trout_sdslot_data = { +static struct msm_mmc_platform_data trout_sdslot_data = { .ocr_mask = TROUT_MMC_VDD, .status = trout_sdslot_status, .translate_vdd = trout_sdslot_switchvdd, @@ -297,7 +294,7 @@ int trout_wifi_reset(int on) EXPORT_SYMBOL(trout_wifi_reset); #endif -static struct mmc_platform_data trout_wifi_data = { +static struct msm_mmc_platform_data trout_wifi_data = { .ocr_mask = MMC_VDD_28_29, .status = trout_wifi_status, .register_status_notify = trout_wifi_status_register, diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index 86ce9905e1653..d69cc21d59fdc 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -49,7 +49,7 @@ #include #include -#include +#include #include #include @@ -841,7 +841,6 @@ static void __init trout_fixup(struct machine_desc *desc, struct tag *tags, { mi->nr_banks=1; mi->bank[0].start = PHYS_OFFSET; - mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); mi->bank[0].size = (101*1024*1024); } From 1bd22b8118da76f56e0dbc573751f4b40ed74988 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 7 Jan 2011 13:48:03 -0800 Subject: [PATCH 0760/2556] ARM: msm: sapphire: fix build on 2.6.37 Change-Id: I27715a5fa570c570d8d6619add444a5d378a6f82 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-sapphire-mmc.c | 9 +++------ arch/arm/mach-msm/board-sapphire.c | 7 +------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire-mmc.c b/arch/arm/mach-msm/board-sapphire-mmc.c index 5219dd93d1998..252831d5f1e46 100755 --- a/arch/arm/mach-msm/board-sapphire-mmc.c +++ b/arch/arm/mach-msm/board-sapphire-mmc.c @@ -28,7 +28,7 @@ #include #include -#include +#include #include "devices.h" #include "board-sapphire.h" @@ -36,9 +36,6 @@ #define DEBUG_SDSLOT_VDD 0 -extern int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, - unsigned int stat_irq, unsigned long stat_irq_flags); - /* ---- COMMON ---- */ static void config_gpio_table(uint32_t *table, int len) { @@ -173,7 +170,7 @@ static unsigned int sapphire_sdslot_status(struct device *dev) | MMC_VDD_25_26 | MMC_VDD_26_27 | MMC_VDD_27_28 \ | MMC_VDD_28_29 | MMC_VDD_29_30) -static struct mmc_platform_data sapphire_sdslot_data = { +static struct msm_mmc_platform_data sapphire_sdslot_data = { .ocr_mask = SAPPHIRE_MMC_VDD, .status = sapphire_sdslot_status, .translate_vdd = sapphire_sdslot_switchvdd, @@ -341,7 +338,7 @@ void sapphire_wifi_reset(int on) EXPORT_SYMBOL(sapphire_wifi_reset); #endif -static struct mmc_platform_data sapphire_wifi_data = { +static struct msm_mmc_platform_data sapphire_wifi_data = { .ocr_mask = MMC_VDD_28_29, .status = sapphire_wifi_status, .register_status_notify = sapphire_wifi_status_register, diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index 422a7b53db382..0a636077ffb74 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -49,7 +49,7 @@ #include #include -#include +#include #include #include "board-sapphire.h" @@ -1261,15 +1261,12 @@ static void __init sapphire_fixup(struct machine_desc *desc, struct tag *tags, if (smi_sz == 32) { mi->nr_banks = 1; mi->bank[0].start = PHYS_OFFSET; - mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); mi->bank[0].size = (84*1024*1024); } else if (smi_sz == 64) { mi->nr_banks = 2; mi->bank[0].start = SMI64_MSM_LINUX_BASE_1; - mi->bank[0].node = PHYS_TO_NID(SMI64_MSM_LINUX_BASE_1); mi->bank[0].size = (32*1024*1024); mi->bank[1].start = SMI64_MSM_LINUX_BASE_2; - mi->bank[1].node = PHYS_TO_NID(SMI64_MSM_LINUX_BASE_2); mi->bank[1].size = (84*1024*1024); } else { printk(KERN_ERR "can not get smi size\n"); @@ -1278,10 +1275,8 @@ static void __init sapphire_fixup(struct machine_desc *desc, struct tag *tags, smi_sz = 64; mi->nr_banks = 2; mi->bank[0].start = SMI64_MSM_LINUX_BASE_1; - mi->bank[0].node = PHYS_TO_NID(SMI64_MSM_LINUX_BASE_1); mi->bank[0].size = (32*1024*1024); mi->bank[1].start = SMI64_MSM_LINUX_BASE_2; - mi->bank[1].node = PHYS_TO_NID(SMI64_MSM_LINUX_BASE_2); mi->bank[1].size = (84*1024*1024); printk(KERN_ERR "use default : smisize=%d\n", smi_sz); } From 05060387cb6a9cb603d34491563e567d69b1d380 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 7 Jan 2011 13:48:38 -0800 Subject: [PATCH 0761/2556] ARM: msm: don't force trout to be compiled for 7x00a Change-Id: I17c4d86cf49d712677594bd9e05db846e4f3dbc9 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 2deb0de34a71e..afb27da08a4fa 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -6,7 +6,6 @@ choice config ARCH_MSM7X00A bool "MSM7x00A / MSM7x01A" - select MACH_TROUT if !MACH_HALIBUT select ARCH_MSM_ARM11 select MSM_SMD select MSM_SMD_PKG3 From 3b210fe5c879f0f443f699a6308a5d0d536e9c38 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 7 Jan 2011 13:49:02 -0800 Subject: [PATCH 0762/2556] ARM: configs: fix msm_defconfig to select the right drivers Change-Id: I6f13f2565be355d7a10ab4ff2776b06ca91066bf Signed-off-by: Dima Zavin --- arch/arm/configs/msm_defconfig | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/arch/arm/configs/msm_defconfig b/arch/arm/configs/msm_defconfig index 6eb769622b6eb..0119068747cc5 100755 --- a/arch/arm/configs/msm_defconfig +++ b/arch/arm/configs/msm_defconfig @@ -22,6 +22,10 @@ CONFIG_MODULE_FORCE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_IOSCHED_DEADLINE is not set CONFIG_ARCH_MSM=y +CONFIG_ARCH_MSM7X00A=y +CONFIG_MSM_DEBUG_UART=3 +CONFIG_MSM_DEBUG_UART3=y +CONFIG_MACH_SAPPHIRE=y CONFIG_HTC_HEADSET=y CONFIG_MSM_SERIAL_DEBUGGER=y CONFIG_WIFI_CONTROL_FUNC=y @@ -149,6 +153,7 @@ CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y CONFIG_BLK_DEV_LOOP=y +CONFIG_MISC_DEVICES=y CONFIG_UID_STAT=y CONFIG_MD=y CONFIG_BLK_DEV_DM=y @@ -194,8 +199,7 @@ CONFIG_I2C=y CONFIG_POWER_SUPPLY=y # CONFIG_HWMON is not set CONFIG_MEDIA_SUPPORT=y -CONFIG_VIDEO_DEV=y -# CONFIG_VIDEO_ALLOW_V4L1 is not set +# CONFIG_IR_CORE is not set CONFIG_MSM_CAMERA=y CONFIG_MT9T013=y CONFIG_DAB=y @@ -215,7 +219,7 @@ CONFIG_MMC_EMBEDDED_SDIO=y CONFIG_MMC_PARANOID_SD_INIT=y # CONFIG_MMC_BLOCK_BOUNCE is not set CONFIG_MMC_BLOCK_DEFERRED_RESUME=y -CONFIG_MMC_MSM7X00A=y +CONFIG_MMC_MSM=y CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y CONFIG_LEDS_GPIO=y From c826ff8ccc15b58d56cd200dd3586b5ec002dc27 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 7 Jan 2011 18:52:09 -0800 Subject: [PATCH 0763/2556] ARM: msm: sapphire: don't install sapphire_gpio at postcore Rather, call cpld gpio init from the machine init function to ensure correct ordering. Otherwise, order of the files in the Makefile will control order of postcore execution and can cause sapphire_gpio_init to be called before init_msm_gpio. Change-Id: I8775dc87c663dd31a7a80676c3ace28447aabd97 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-sapphire-gpio.c | 4 +--- arch/arm/mach-msm/board-sapphire.c | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/board-sapphire-gpio.c b/arch/arm/mach-msm/board-sapphire-gpio.c index 90d9e837739ad..a75df356dd996 100644 --- a/arch/arm/mach-msm/board-sapphire-gpio.c +++ b/arch/arm/mach-msm/board-sapphire-gpio.c @@ -287,7 +287,7 @@ static struct sys_device sapphire_irq_device = { .cls = &sapphire_sysdev_class, }; -int sapphire_init_gpio(void) +int __init sapphire_init_gpio(void) { int i; if (!machine_is_sapphire()) @@ -322,5 +322,3 @@ int sapphire_init_cpld(unsigned int sys_rev) writeb(sapphire_cpld_initdata[i], SAPPHIRE_CPLD_BASE + i * 2); return 0; } - -postcore_initcall(sapphire_init_gpio); diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c index 0a636077ffb74..229d7284eb166 100644 --- a/arch/arm/mach-msm/board-sapphire.c +++ b/arch/arm/mach-msm/board-sapphire.c @@ -1117,11 +1117,14 @@ static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { }; #endif +extern int sapphire_init_gpio(void); + static void __init sapphire_init(void) { int rc; printk("sapphire_init() revision = 0x%X\n", system_rev); + sapphire_init_gpio(); /* * Setup common MSM GPIOS */ From 840da49176b4abc0a994f606b4550b4ea6809d5e Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 7 Jan 2011 18:53:58 -0800 Subject: [PATCH 0764/2556] ARM: configs: msm_defconfig: fix stacktracing Change-Id: I7002881a2dff3e2172f7e22ac01bbfeb584daea0 Signed-off-by: Dima Zavin --- arch/arm/configs/msm_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/configs/msm_defconfig b/arch/arm/configs/msm_defconfig index 0119068747cc5..4e1788a381586 100755 --- a/arch/arm/configs/msm_defconfig +++ b/arch/arm/configs/msm_defconfig @@ -261,10 +261,12 @@ CONFIG_PRINTK_TIME=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_FS=y CONFIG_DEBUG_KERNEL=y +# CONFIG_STACKTRACE is not set CONFIG_SCHEDSTATS=y CONFIG_TIMER_STATS=y # CONFIG_DEBUG_PREEMPT is not set CONFIG_DEBUG_INFO=y +# CONFIG_ARM_UNWIND is not set # CONFIG_RCU_CPU_STALL_DETECTOR is not set CONFIG_CRYPTO_AES=y CONFIG_CRYPTO_TWOFISH=y From 474a5f6a682fd69c749e307de6065a292c8b1ed1 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 7 Jan 2011 20:00:19 -0800 Subject: [PATCH 0765/2556] ARM: configs: update surf7x30_defconfig Change-Id: Ied0dcbc15eda25c0736c9463048d6b65e4f53bc4 Signed-off-by: Dima Zavin --- arch/arm/configs/surf7x30_defconfig | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/arm/configs/surf7x30_defconfig b/arch/arm/configs/surf7x30_defconfig index 4538e223214d1..26169f44b312f 100644 --- a/arch/arm/configs/surf7x30_defconfig +++ b/arch/arm/configs/surf7x30_defconfig @@ -86,6 +86,7 @@ CONFIG_MTD_CMDLINE_PARTS=y CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y CONFIG_BLK_DEV_LOOP=y +CONFIG_MISC_DEVICES=y CONFIG_KERNEL_DEBUGGER_CORE=y CONFIG_UID_STAT=y CONFIG_APANIC=y @@ -118,7 +119,6 @@ CONFIG_INPUT_MISC=y CONFIG_INPUT_KEYCHORD=y CONFIG_INPUT_UINPUT=y CONFIG_INPUT_GPIO=y -CONFIG_INPUT_CAPELLA_CM3602=y # CONFIG_SERIO is not set # CONFIG_VT is not set # CONFIG_DEVMEM is not set @@ -129,8 +129,6 @@ CONFIG_SERIAL_MSM_CONSOLE=y # CONFIG_LEGACY_PTYS is not set # CONFIG_HW_RANDOM is not set CONFIG_I2C=y -CONFIG_W1=y -CONFIG_W1_MASTER_DS2482=y CONFIG_POWER_SUPPLY=y # CONFIG_HWMON is not set CONFIG_REGULATOR=y @@ -138,6 +136,8 @@ CONFIG_REGULATOR_DEBUG=y CONFIG_MEDIA_SUPPORT=y CONFIG_VIDEO_DEV=y # CONFIG_VIDEO_ALLOW_V4L1 is not set +# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +CONFIG_VIDEO_HELPER_CHIPS_AUTO=y CONFIG_DAB=y CONFIG_VIDEO_OUTPUT_CONTROL=y CONFIG_USB_GADGET=y @@ -167,6 +167,7 @@ CONFIG_RTC_CLASS=y # CONFIG_RTC_INTF_SYSFS is not set # CONFIG_RTC_INTF_PROC is not set # CONFIG_RTC_INTF_DEV is not set +# CONFIG_RTC_DRV_MSM7X00A is not set CONFIG_STAGING=y # CONFIG_STAGING_EXCLUDE_BUILD is not set CONFIG_ANDROID=y @@ -181,10 +182,10 @@ CONFIG_EXT2_FS_XATTR=y CONFIG_EXT2_FS_POSIX_ACL=y CONFIG_EXT2_FS_SECURITY=y CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set CONFIG_EXT3_FS_POSIX_ACL=y CONFIG_EXT3_FS_SECURITY=y # CONFIG_DNOTIFY is not set -CONFIG_INOTIFY=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_YAFFS_FS=y @@ -194,6 +195,7 @@ CONFIG_PRINTK_TIME=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_FS=y CONFIG_DEBUG_KERNEL=y +CONFIG_DETECT_HUNG_TASK=y CONFIG_SCHEDSTATS=y CONFIG_TIMER_STATS=y CONFIG_DEBUG_SLAB=y @@ -205,6 +207,9 @@ CONFIG_DEBUG_INFO=y CONFIG_DEBUG_VM=y CONFIG_DEBUG_SG=y # CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_ARM_UNWIND is not set +CONFIG_DEBUG_LL=y +CONFIG_EARLY_PRINTK=y CONFIG_CRYPTO_AES=y CONFIG_CRYPTO_TWOFISH=y # CONFIG_CRYPTO_ANSI_CPRNG is not set From 81e3283a9e67d4c559420b2dda10eb1a315d3617 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 7 Jan 2011 20:00:55 -0800 Subject: [PATCH 0766/2556] ARM: msm: 7x30: fix devices file Change-Id: Id06b7501c29ff173550703fdc342fafe752b6d8b Signed-off-by: Dima Zavin --- arch/arm/mach-msm/devices-msm7x30.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c index d3902249f2fa3..56ba5d9f5fde5 100644 --- a/arch/arm/mach-msm/devices-msm7x30.c +++ b/arch/arm/mach-msm/devices-msm7x30.c @@ -473,7 +473,8 @@ static struct platform_device *msm_sdcc_devices[] __initdata = { &msm_device_sdc4, }; -int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, +int __init msm_add_sdcc(unsigned int controller, + struct msm_mmc_platform_data *plat, unsigned int stat_irq, unsigned long stat_irq_flags) { struct platform_device *pdev; From ee976da296db921a4978af6d7423a9b167e892f3 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 7 Jan 2011 20:01:21 -0800 Subject: [PATCH 0767/2556] ARM: msm: surf7x30: fix compile issues on 2.6.37 Change-Id: I66527c5da497a53d317afdf25d71d28bccf74e1e Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-surf7x30.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/arm/mach-msm/board-surf7x30.c b/arch/arm/mach-msm/board-surf7x30.c index fe52633f506a0..802739f234387 100644 --- a/arch/arm/mach-msm/board-surf7x30.c +++ b/arch/arm/mach-msm/board-surf7x30.c @@ -462,7 +462,6 @@ static void __init surf7x30_fixup(struct machine_desc *desc, struct tag *tags, { mi->nr_banks = 1; mi->bank[0].start = PHYS_OFFSET; - mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); mi->bank[0].size = (51*1024*1024); } @@ -473,10 +472,6 @@ static void __init surf7x30_map_io(void) } MACHINE_START(MSM7X30_SURF, "QCT SURF7X30 Development Board") -#ifdef CONFIG_MSM_DEBUG_UART - .phys_io = MSM_DEBUG_UART_PHYS, - .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, -#endif .boot_params = 0x00200100, .fixup = surf7x30_fixup, .map_io = surf7x30_map_io, From c0748e79eae640aba8e5bb8beb10cbd2b0ae8951 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sun, 9 Jan 2011 13:35:35 -0800 Subject: [PATCH 0768/2556] Revert "[ARM] msm: gpio: Add GPIO_CFG_ prefix to GPIO_CFG argument to avoid conflict with proc_comm.h" This reverts commit 5736cd87409d3ee32ba0d424fbfaf950c45f434b. --- arch/arm/mach-msm/include/mach/gpio.h | 51 --------------------------- 1 file changed, 51 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/gpio.h b/arch/arm/mach-msm/include/mach/gpio.h index 15feb02750a88..9e4345f5e4e90 100644 --- a/arch/arm/mach-msm/include/mach/gpio.h +++ b/arch/arm/mach-msm/include/mach/gpio.h @@ -21,57 +21,6 @@ #define FIRST_BOARD_GPIO NR_GPIO_IRQS -/* GPIO TLMM (Top Level Multiplexing) Definitions */ - -/* GPIO TLMM: Function -- GPIO specific */ - -/* GPIO TLMM: Direction */ -enum { - GPIO_CFG_INPUT, - GPIO_CFG_OUTPUT, -}; - -/* GPIO TLMM: Pullup/Pulldown */ -enum { - GPIO_CFG_NO_PULL, - GPIO_CFG_PULL_DOWN, - GPIO_CFG_KEEPER, - GPIO_CFG_PULL_UP, -}; - -/* GPIO TLMM: Drive Strength */ -enum { - GPIO_CFG_2MA, - GPIO_CFG_4MA, - GPIO_CFG_6MA, - GPIO_CFG_8MA, - GPIO_CFG_10MA, - GPIO_CFG_12MA, - GPIO_CFG_14MA, - GPIO_CFG_16MA, -}; - -enum { - GPIO_CFG_ENABLE, - GPIO_CFG_DISABLE, -}; - -#define GPIO_CFG(gpio, func, dir, pull, drvstr) \ - ((((gpio) & 0x3FF) << 4) | \ - ((func) & 0xf) | \ - (((dir) & 0x1) << 14) | \ - (((pull) & 0x3) << 15) | \ - (((drvstr) & 0xF) << 17)) - -/** - * extract GPIO pin from bit-field used for gpio_tlmm_config - */ -#define GPIO_PIN(gpio_cfg) (((gpio_cfg) >> 4) & 0x3ff) -#define GPIO_FUNC(gpio_cfg) (((gpio_cfg) >> 0) & 0xf) -#define GPIO_DIR(gpio_cfg) (((gpio_cfg) >> 14) & 0x1) -#define GPIO_PULL(gpio_cfg) (((gpio_cfg) >> 15) & 0x3) -#define GPIO_DRVSTR(gpio_cfg) (((gpio_cfg) >> 17) & 0xf) - #define gpio_get_value __gpio_get_value #define gpio_set_value __gpio_set_value #define gpio_cansleep __gpio_cansleep From 600483fb14c1fa95d8a8779441a4847e853a8fde Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sun, 9 Jan 2011 16:09:01 -0800 Subject: [PATCH 0769/2556] ARM: msm: board-msm7x30: add pm8058 support Change-Id: Id5fe0da3f14cd61f03c2be88ac5186fff9ebd510 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 46 +++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 6f3b9735e9708..01d58190d6b86 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include @@ -33,10 +33,18 @@ #include #include #include +#include #include #include "devices.h" #include "proc_comm.h" +#include "gpiomux.h" + +#define MSM7X30_PM8058_GPIO_BASE FIRST_BOARD_GPIO +#define MSM7X30_PM8058_GPIO(x) (MSM7X30_PM8058_GPIO_BASE + (x)) +#define MSM7X30_PM8058_IRQ_BASE FIRST_BOARD_IRQ + +#define MSM7X30_GPIO_PMIC_INT_N 27 extern struct sys_timer msm_timer; @@ -67,12 +75,40 @@ static void __init msm7x30_init_irq(void) msm_init_irq(); } -static void __init msm7x30_init(void) +static struct pm8058_platform_data msm7x30_pm8058_pdata = { + .irq_base = MSM7X30_PM8058_IRQ_BASE, + .gpio_base = MSM7X30_PM8058_GPIO_BASE, +}; + +static struct msm_ssbi_platform_data msm7x30_ssbi_pmic_pdata = { + .slave = { + .name = "pm8058-core", + .irq = MSM_GPIO_TO_INT(MSM7X30_GPIO_PMIC_INT_N), + .platform_data = &msm7x30_pm8058_pdata, + }, + .rspinlock_name = "D:PMIC_SSBI", +}; + +static int __init msm7x30_ssbi_pmic_init(void) { - msm_device_otg.dev.platform_data = &msm_otg_pdata; - msm_device_hsusb.dev.parent = &msm_device_otg.dev; - msm_device_hsusb_host.dev.parent = &msm_device_otg.dev; + int ret; + + pr_info("%s()\n", __func__); + msm_gpiomux_write(MSM7X30_GPIO_PMIC_INT_N, 0, + GPIOMUX_FUNC_GPIO | + GPIOMUX_PULL_NONE | + GPIOMUX_DIR_INPUT | + GPIOMUX_DRV_2MA | GPIOMUX_VALID); + ret = gpiochip_reserve(msm7x30_pm8058_pdata.gpio_base, + PM8058_NUM_GPIOS); + WARN(ret, "can't reserve pm8058 gpios. badness will ensue...\n"); + msm_device_ssbi_pmic.dev.platform_data = &msm7x30_ssbi_pmic_pdata; + return platform_device_register(&msm_device_ssbi_pmic); +} +static void __init msm7x30_init(void) +{ + msm7x30_ssbi_pmic_init(); platform_add_devices(devices, ARRAY_SIZE(devices)); } From 75cbd9b5141e75cac9ea3393bd35c206fc6a2253 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sun, 9 Jan 2011 16:11:29 -0800 Subject: [PATCH 0770/2556] ARM: msm: gpiomux-v1: add gpio direction defines Change-Id: I659a58e05b86a955f8c75a106a65da747b736c28 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/gpiomux-v1.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/mach-msm/gpiomux-v1.h b/arch/arm/mach-msm/gpiomux-v1.h index 71d86feba4509..a523ad25d0cbc 100644 --- a/arch/arm/mach-msm/gpiomux-v1.h +++ b/arch/arm/mach-msm/gpiomux-v1.h @@ -64,4 +64,8 @@ enum { GPIOMUX_PULL_UP = 3UL << 15, }; +enum { + GPIOMUX_DIR_INPUT = 0UL << 14, + GPIOMUX_DIR_OUTPUT = 1UL << 14, +}; #endif From fcc2249080425b064358ddd3029228b49e98817e Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sun, 9 Jan 2011 16:14:06 -0800 Subject: [PATCH 0771/2556] ARM: msm: board-msm7x30: add uart1 serial support Change-Id: I987ef99bd8100faf3fb53ac7432cc30e3f6ff1c1 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 01d58190d6b86..c11016e131107 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -61,6 +61,9 @@ static struct msm_otg_platform_data msm_otg_pdata = { }; static struct platform_device *devices[] __initdata = { +#if defined(CONFIG_SERIAL_MSM) && !defined(CONFIG_MSM_SERIAL_DEBUGGER) + &msm_device_uart1, +#endif #if defined(CONFIG_SERIAL_MSM) || defined(CONFIG_MSM_SERIAL_DEBUGGER) &msm_device_uart2, #endif @@ -106,8 +109,16 @@ static int __init msm7x30_ssbi_pmic_init(void) return platform_device_register(&msm_device_ssbi_pmic); } +extern void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq); + static void __init msm7x30_init(void) { +#if defined(CONFIG_MSM_SERIAL_DEBUGGER) + msm_serial_debug_init(MSM_UART1_PHYS, INT_UART1, + &msm_device_uart1.dev, 1); +#endif + msm7x30_ssbi_pmic_init(); platform_add_devices(devices, ARRAY_SIZE(devices)); } From 5f31e50561f0bfc1c33381ec0cb1470995f80457 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sun, 9 Jan 2011 16:14:46 -0800 Subject: [PATCH 0772/2556] ARM: msm: board-7x30: add nand support Change-Id: Ia6b3b2930d2176d5852d43e84680fc89d72c64b6 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index c11016e131107..55064ba49d468 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -68,9 +68,7 @@ static struct platform_device *devices[] __initdata = { &msm_device_uart2, #endif &msm_device_smd, - &msm_device_otg, - &msm_device_hsusb, - &msm_device_hsusb_host, + &msm_device_nand, }; static void __init msm7x30_init_irq(void) From 9c1eae7cfed240d052b7df46da9208cda812c8bc Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sun, 9 Jan 2011 16:15:48 -0800 Subject: [PATCH 0773/2556] ARM: msm: HACK: board-msm7x30: add a single 51mb bank for now Change-Id: I3f8ea5e1b57b8944c567a40d250939f2a030fea2 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 55064ba49d468..7c6441e7c5e7f 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -127,10 +127,19 @@ static void __init msm7x30_map_io(void) msm_clock_init(msm_clocks_7x30, msm_num_clocks_7x30); } +static void __init msm7x30_fixup(struct machine_desc *desc, struct tag *tags, + char **cmdline, struct meminfo *mi) +{ + mi->nr_banks = 1; + mi->bank[0].start = PHYS_OFFSET; + mi->bank[0].size = (51*1024*1024); +} + MACHINE_START(MSM7X30_SURF, "QCT MSM7X30 SURF") #ifdef CONFIG_MSM_DEBUG_UART #endif .boot_params = PHYS_OFFSET + 0x100, + .fixup = msm7x30_fixup, .map_io = msm7x30_map_io, .init_irq = msm7x30_init_irq, .init_machine = msm7x30_init, From f0f85eaaca80844b36a98bf04d9545793e815eac Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sun, 9 Jan 2011 16:16:21 -0800 Subject: [PATCH 0774/2556] ARM: msm: surf7x30_defconfig: update defconfig Change-Id: Ib8bafa25d4d5b8a557979a9a456fe037526d2c66 Signed-off-by: Dima Zavin --- arch/arm/configs/surf7x30_defconfig | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/arch/arm/configs/surf7x30_defconfig b/arch/arm/configs/surf7x30_defconfig index 26169f44b312f..9b66b9b4d146b 100644 --- a/arch/arm/configs/surf7x30_defconfig +++ b/arch/arm/configs/surf7x30_defconfig @@ -29,8 +29,8 @@ CONFIG_MSM_DEBUG_UART1=y CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT=y CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT=y CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000 -# CONFIG_MSM_FIQ_SUPPORT is not set # CONFIG_MSM_HW3D is not set +CONFIG_MSM_SSBI=y CONFIG_WIFI_CONTROL_FUNC=y CONFIG_ARM_THUMBEE=y CONFIG_NO_HZ=y @@ -111,6 +111,7 @@ CONFIG_PPPOPNS=y CONFIG_INPUT_EVDEV=y CONFIG_INPUT_KEYRESET=y # CONFIG_KEYBOARD_ATKBD is not set +CONFIG_KEYBOARD_PM8058=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_MSM=y @@ -136,7 +137,26 @@ CONFIG_REGULATOR_DEBUG=y CONFIG_MEDIA_SUPPORT=y CONFIG_VIDEO_DEV=y # CONFIG_VIDEO_ALLOW_V4L1 is not set -# CONFIG_MEDIA_TUNER_CUSTOMISE is not set +# CONFIG_IR_CORE is not set +# CONFIG_MEDIA_TUNER_SIMPLE is not set +# CONFIG_MEDIA_TUNER_TDA8290 is not set +# CONFIG_MEDIA_TUNER_TDA827X is not set +# CONFIG_MEDIA_TUNER_TDA18271 is not set +# CONFIG_MEDIA_TUNER_TDA9887 is not set +# CONFIG_MEDIA_TUNER_TEA5761 is not set +# CONFIG_MEDIA_TUNER_TEA5767 is not set +# CONFIG_MEDIA_TUNER_MT20XX is not set +# CONFIG_MEDIA_TUNER_MT2060 is not set +# CONFIG_MEDIA_TUNER_MT2266 is not set +# CONFIG_MEDIA_TUNER_MT2131 is not set +# CONFIG_MEDIA_TUNER_QT1010 is not set +# CONFIG_MEDIA_TUNER_XC2028 is not set +# CONFIG_MEDIA_TUNER_XC5000 is not set +# CONFIG_MEDIA_TUNER_MXL5005S is not set +# CONFIG_MEDIA_TUNER_MXL5007T is not set +# CONFIG_MEDIA_TUNER_MC44S803 is not set +# CONFIG_MEDIA_TUNER_MAX2165 is not set +# CONFIG_MEDIA_TUNER_TDA18218 is not set CONFIG_VIDEO_HELPER_CHIPS_AUTO=y CONFIG_DAB=y CONFIG_VIDEO_OUTPUT_CONTROL=y @@ -208,8 +228,6 @@ CONFIG_DEBUG_VM=y CONFIG_DEBUG_SG=y # CONFIG_RCU_CPU_STALL_DETECTOR is not set # CONFIG_ARM_UNWIND is not set -CONFIG_DEBUG_LL=y -CONFIG_EARLY_PRINTK=y CONFIG_CRYPTO_AES=y CONFIG_CRYPTO_TWOFISH=y # CONFIG_CRYPTO_ANSI_CPRNG is not set From add410e68866bf1efc8ee09fcd72f9ebd0471cee Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sun, 9 Jan 2011 16:20:39 -0800 Subject: [PATCH 0775/2556] ARM: msm: board-msm7x30: add marimba support Change-Id: Ibbe3233c7ac08eb6c5474cbfb50103078d8e4c3e Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 7c6441e7c5e7f..34465eecd0307 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -69,6 +70,7 @@ static struct platform_device *devices[] __initdata = { #endif &msm_device_smd, &msm_device_nand, + &msm_device_i2c2, }; static void __init msm7x30_init_irq(void) @@ -107,6 +109,13 @@ static int __init msm7x30_ssbi_pmic_init(void) return platform_device_register(&msm_device_ssbi_pmic); } +static struct i2c_board_info surf_i2c_devices[] = { + /* marimba master is implied at 0x0c */ + { + I2C_BOARD_INFO("marimba-codec", 0x77), + }, +}; + extern void msm_serial_debug_init(unsigned int base, int irq, struct device *clk_device, int signal_irq); @@ -119,6 +128,9 @@ static void __init msm7x30_init(void) msm7x30_ssbi_pmic_init(); platform_add_devices(devices, ARRAY_SIZE(devices)); + + i2c_register_board_info(1, surf_i2c_devices, + ARRAY_SIZE(surf_i2c_devices)); } static void __init msm7x30_map_io(void) From e5eaf8ca108a86b486d858f195ba070386abca79 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sun, 9 Jan 2011 16:35:07 -0800 Subject: [PATCH 0776/2556] ARM: msm: board-msm7x30: add usb support Change-Id: I185e5e148d6e09ce4efe1f5e9a567b82ba994696 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 128 ++++++++++++++++++++++++++++-- 1 file changed, 121 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 34465eecd0307..ff554a1fdbbd2 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -35,10 +36,12 @@ #include #include #include +#include #include #include "devices.h" #include "proc_comm.h" +#include "clock-pcom.h" #include "gpiomux.h" #define MSM7X30_PM8058_GPIO_BASE FIRST_BOARD_GPIO @@ -49,16 +52,119 @@ extern struct sys_timer msm_timer; -static int hsusb_phy_init_seq[] = { - 0x30, 0x32, /* Enable and set Pre-Emphasis Depth to 20% */ - 0x02, 0x36, /* Disable CDR Auto Reset feature */ +static int msm7x30_phy_init_seq[] = { + 0x0C, 0x31, + 0x31, 0x32, + 0x1D, 0x0D, + 0x1D, 0x10, -1 }; -static struct msm_otg_platform_data msm_otg_pdata = { - .phy_init_seq = hsusb_phy_init_seq, - .mode = USB_PERIPHERAL, - .otg_control = OTG_PHY_CONTROL, +static void msm7x30_usb_phy_reset(void) +{ + u32 id; + int ret; + + id = P_USB_PHY_CLK; + ret = msm_proc_comm(PCOM_CLKCTL_RPC_RESET_ASSERT, &id, NULL); + if (ret) { + pr_err("%s: Cannot assert (%d)\n", __func__, ret); + return; + } + + msleep(1); + + id = P_USB_PHY_CLK; + ret = msm_proc_comm(PCOM_CLKCTL_RPC_RESET_DEASSERT, &id, NULL); + if (ret) { + pr_err("%s: Cannot assert (%d)\n", __func__, ret); + return; + } +} + +static void msm7x30_usb_hw_reset(bool enable) +{ + u32 id; + int ret; + u32 func; + + id = P_USB_HS_CLK; + if (enable) + func = PCOM_CLKCTL_RPC_RESET_ASSERT; + else + func = PCOM_CLKCTL_RPC_RESET_DEASSERT; + ret = msm_proc_comm(func, &id, NULL); + if (ret) + pr_err("%s: Cannot set reset to %d (%d)\n", __func__, enable, + ret); +} + +static struct msm_hsusb_platform_data msm_hsusb_pdata = { + .phy_init_seq = msm7x30_phy_init_seq, + .phy_reset = msm7x30_usb_phy_reset, + .hw_reset = msm7x30_usb_hw_reset, +}; + +static char *usb_functions[] = { + "usb_mass_storage", +}; + +static char *usb_functions_adb[] = { + "usb_mass_storage", + "adb", +}; + +static char *usb_functions_all[] = { + "usb_mass_storage", + "adb", +}; + +static struct android_usb_product usb_products[] = { + { + .product_id = 0x4e11, + .num_functions = ARRAY_SIZE(usb_functions), + .functions = usb_functions, + }, + { + .product_id = 0x4e12, + .num_functions = ARRAY_SIZE(usb_functions_adb), + .functions = usb_functions_adb, + }, +}; + +static struct android_usb_platform_data android_usb_pdata = { + .vendor_id = 0x18d1, + .product_id = 0x4e11, + .version = 0x0100, + .product_name = "Surf7x30", + .manufacturer_name = "Qualcomm, Inc.", + .num_products = ARRAY_SIZE(usb_products), + .products = usb_products, + .num_functions = ARRAY_SIZE(usb_functions_all), + .functions = usb_functions_all, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; + +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .vendor = "Qualcomm, Inc.", + .product = "Surf7x30", + .release = 0x0100, +}; + +static struct platform_device usb_mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &mass_storage_pdata, + }, }; static struct platform_device *devices[] __initdata = { @@ -71,6 +177,9 @@ static struct platform_device *devices[] __initdata = { &msm_device_smd, &msm_device_nand, &msm_device_i2c2, + &msm_device_hsusb, + &usb_mass_storage_device, + &android_usb_device, }; static void __init msm7x30_init_irq(void) @@ -127,10 +236,15 @@ static void __init msm7x30_init(void) #endif msm7x30_ssbi_pmic_init(); + msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; platform_add_devices(devices, ARRAY_SIZE(devices)); i2c_register_board_info(1, surf_i2c_devices, ARRAY_SIZE(surf_i2c_devices)); + + msm_hsusb_set_vbus_state(1); + msm_hsusb_set_vbus_state(0); + msm_hsusb_set_vbus_state(1); } static void __init msm7x30_map_io(void) From 3d0ef5c5812cd8e66ad33b8f916b52edafd3f81e Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 10 Jan 2011 14:42:57 -0800 Subject: [PATCH 0777/2556] ARM: msm: board-msm7x30: add basic pm8058 keypad support Change-Id: Ie323b2e08375429206d482bc45906ef6de73a4d4 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 69 +++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index ff554a1fdbbd2..40661ae9fd45b 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -187,9 +188,77 @@ static void __init msm7x30_init_irq(void) msm_init_irq(); } +static struct pm8058_pin_config msm7x30_kpd_input_gpio_cfg = { + .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3, + .dir = PM8058_GPIO_INPUT, + .pull_up = PM8058_GPIO_PULL_UP_31P5, + .strength = PM8058_GPIO_STRENGTH_OFF, + .func = PM8058_GPIO_FUNC_NORMAL, + .flags = PM8058_GPIO_INV_IRQ_POL +}; + +static struct pm8058_pin_config msm7x30_kpd_output_gpio_cfg = { + .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3, + .dir = PM8058_GPIO_OUTPUT, + .pull_up = PM8058_GPIO_PULL_NONE, + .strength = PM8058_GPIO_STRENGTH_LOW, + .func = PM8058_GPIO_FUNC_1, + .flags = (PM8058_GPIO_OPEN_DRAIN | + PM8058_GPIO_INV_IRQ_POL), +}; + +static unsigned int msm7x30_pmic_col_gpios[] = { + MSM7X30_PM8058_GPIO(0), MSM7X30_PM8058_GPIO(1), + MSM7X30_PM8058_GPIO(2), MSM7X30_PM8058_GPIO(3), + MSM7X30_PM8058_GPIO(4), MSM7X30_PM8058_GPIO(5), + MSM7X30_PM8058_GPIO(6), MSM7X30_PM8058_GPIO(7), +}; +static unsigned int msm7x30_pmic_row_gpios[] = { + MSM7X30_PM8058_GPIO(8), MSM7X30_PM8058_GPIO(9), + MSM7X30_PM8058_GPIO(10), MSM7X30_PM8058_GPIO(11), + MSM7X30_PM8058_GPIO(12), MSM7X30_PM8058_GPIO(13), + MSM7X30_PM8058_GPIO(14), MSM7X30_PM8058_GPIO(15), + MSM7X30_PM8058_GPIO(16), MSM7X30_PM8058_GPIO(17), + MSM7X30_PM8058_GPIO(18), MSM7X30_PM8058_GPIO(19), +}; + +#define KEYMAP_NUM_ROWS ARRAY_SIZE(msm7x30_pmic_row_gpios) +#define KEYMAP_NUM_COLS ARRAY_SIZE(msm7x30_pmic_col_gpios) +#define KEYMAP_INDEX(row, col) (((row) * KEYMAP_NUM_COLS) + (col)) +#define KEYMAP_SIZE (KEYMAP_NUM_ROWS * KEYMAP_NUM_COLS) + +static int msm7x30_pmic_keypad_init(struct device *dev) +{ + int i; + + for (i = 0; i < KEYMAP_NUM_COLS; ++i) + pm8058_gpio_mux(msm7x30_pmic_col_gpios[i], + &msm7x30_kpd_input_gpio_cfg); + for (i = 0; i < KEYMAP_NUM_ROWS; ++i) + pm8058_gpio_mux(msm7x30_pmic_row_gpios[i], + &msm7x30_kpd_output_gpio_cfg); + return 0; +} + +static const unsigned short msm7x30_pmic_keymap[KEYMAP_SIZE] = { + [KEYMAP_INDEX(0, 6)] = KEY_BACK, +}; + +static struct pm8058_keypad_platform_data msm7x30_pmic_keypad_pdata = { + .name = "msm7x30-keypad", + .num_drv = KEYMAP_NUM_ROWS, + .num_sns = KEYMAP_NUM_COLS, + .scan_delay_shift = 5, + .drv_hold_clks = 4, + .debounce_ms = 10, + .keymap = msm7x30_pmic_keymap, + .init = msm7x30_pmic_keypad_init, +}; + static struct pm8058_platform_data msm7x30_pm8058_pdata = { .irq_base = MSM7X30_PM8058_IRQ_BASE, .gpio_base = MSM7X30_PM8058_GPIO_BASE, + .keypad_pdata = &msm7x30_pmic_keypad_pdata, }; static struct msm_ssbi_platform_data msm7x30_ssbi_pmic_pdata = { From 9cd310311fa7a55aa9d0c8ced7d6a4b04c9e3289 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 10 Jan 2011 10:50:40 -0800 Subject: [PATCH 0778/2556] ARM: msm: remove superfluous surf7x30 board file Change-Id: I9b041906093e50082ebec2f85da9b29cc1f98b4c Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Makefile | 1 - arch/arm/mach-msm/board-surf7x30.c | 481 ----------------------------- 2 files changed, 482 deletions(-) delete mode 100644 arch/arm/mach-msm/board-surf7x30.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 5c925972cc16d..2abdea9f547ea 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -76,7 +76,6 @@ obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-keypad.o fish_battery.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-panel.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-mmc.o -obj-$(CONFIG_MACH_MSM7X30_SURF) += board-surf7x30.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o obj-$(CONFIG_ARCH_QSD8X50) += devices-qsd8x50.o obj-$(CONFIG_MACH_QSD8X50_SURF) += board-qsd8x50.o diff --git a/arch/arm/mach-msm/board-surf7x30.c b/arch/arm/mach-msm/board-surf7x30.c deleted file mode 100644 index 802739f234387..0000000000000 --- a/arch/arm/mach-msm/board-surf7x30.c +++ /dev/null @@ -1,481 +0,0 @@ -/* linux/arch/arm/mach-msm/board-surf7x30.c - * - * Copyright (C) 2010 Google, Inc. - * Author: Dima Zavin - * - * 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 -#include -#include -#include -#include - -#include "devices.h" -#include "proc_comm.h" - -#define SURF7X30_PM8058_GPIO_BASE FIRST_BOARD_GPIO -#define SURF7X30_PM8058_GPIO(x) (SURF7X30_PM8058_GPIO_BASE + (x)) -#define SURF7X30_PM8058_IRQ_BASE FIRST_BOARD_IRQ - -#define SURF7X30_GPIO_PMIC_INT_N 27 - -#define SURF7X30_USE_PMIC_KEYPAD 1 - -static struct resource smc91x_resources[] = { - [0] = { - .start = 0x8A000300, - .end = 0x8A0003ff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = MSM_GPIO_TO_INT(156), - .end = MSM_GPIO_TO_INT(156), - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device smc91x_device = { - .name = "smc91x", - .id = 0, - .num_resources = ARRAY_SIZE(smc91x_resources), - .resource = smc91x_resources, -}; - -static int surf7x30_phy_init_seq[] = { - 0x0C, 0x31, - 0x31, 0x32, - 0x1D, 0x0D, - 0x1D, 0x10, - -1 }; - -static void surf7x30_usb_phy_reset(void) -{ - u32 id; - int ret; - - id = PCOM_CLKRGM_APPS_RESET_USB_PHY; - ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_ASSERT, &id, NULL); - if (ret) { - pr_err("%s: Cannot assert (%d)\n", __func__, ret); - return; - } - - msleep(1); - - id = PCOM_CLKRGM_APPS_RESET_USB_PHY; - ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_DEASSERT, &id, NULL); - if (ret) { - pr_err("%s: Cannot assert (%d)\n", __func__, ret); - return; - } -} - -static void surf7x30_usb_hw_reset(bool enable) -{ - u32 id; - int ret; - u32 func; - - id = PCOM_CLKRGM_APPS_RESET_USBH; - if (enable) - func = PCOM_CLK_REGIME_SEC_RESET_ASSERT; - else - func = PCOM_CLK_REGIME_SEC_RESET_DEASSERT; - ret = msm_proc_comm(func, &id, NULL); - if (ret) - pr_err("%s: Cannot set reset to %d (%d)\n", __func__, enable, - ret); -} - -static struct msm_hsusb_platform_data msm_hsusb_pdata = { - .phy_init_seq = surf7x30_phy_init_seq, - .phy_reset = surf7x30_usb_phy_reset, - .hw_reset = surf7x30_usb_hw_reset, -}; - -static char *usb_functions[] = { - "usb_mass_storage", -#ifdef CONFIG_USB_ANDROID_RNDIS - "rndis", -#endif -#ifdef CONFIG_USB_ANDROID_ACM - "acm", -#endif -}; - -static char *usb_functions_adb[] = { - "usb_mass_storage", - "adb", -#ifdef CONFIG_USB_ANDROID_RNDIS - "rndis", -#endif -#ifdef CONFIG_USB_ANDROID_ACM - "acm", -#endif -}; - -#ifdef CONFIG_USB_ANDROID_DIAG -static char *usb_functions_adb_diag[] = { - "usb_mass_storage", - "adb", - "diag", -}; -#endif - -static char *usb_functions_all[] = { - "usb_mass_storage", - "adb", -#ifdef CONFIG_USB_ANDROID_ACM - "acm", -#endif -#ifdef CONFIG_USB_ANDROID_RNDIS - "rndis", -#endif -#ifdef CONFIG_USB_ANDROID_DIAG - "diag", -#endif -}; - -static struct android_usb_product usb_products[] = { - { -#ifdef CONFIG_USB_ANDROID_ACM - .product_id = 0x4e21, -#else - .product_id = 0x4e11, -#endif - .num_functions = ARRAY_SIZE(usb_functions), - .functions = usb_functions, - }, - { -#ifdef CONFIG_USB_ANDROID_ACM - .product_id = 0x4e22, -#else - .product_id = 0x4e12, -#endif - .num_functions = ARRAY_SIZE(usb_functions_adb), - .functions = usb_functions_adb, - }, -#ifdef CONFIG_USB_ANDROID_DIAG - { - .product_id = 0x4e17, - .num_functions = ARRAY_SIZE(usb_functions_adb_diag), - .functions = usb_functions_adb_diag, - }, -#endif -}; - -static struct usb_mass_storage_platform_data mass_storage_pdata = { - .nluns = 1, - .vendor = "Qualcomm, Inc.", - .product = "Surf7x30", - .release = 0x0100, -}; - -static struct platform_device usb_mass_storage_device = { - .name = "usb_mass_storage", - .id = -1, - .dev = { - .platform_data = &mass_storage_pdata, - }, -}; - -static struct android_usb_platform_data android_usb_pdata = { - .vendor_id = 0x18d1, - .product_id = 0x4e11, - .version = 0x0100, - .product_name = "Surf7x30", - .manufacturer_name = "Qualcomm, Inc.", - .num_products = ARRAY_SIZE(usb_products), - .products = usb_products, - .num_functions = ARRAY_SIZE(usb_functions_all), - .functions = usb_functions_all, -}; - -static struct platform_device android_usb_device = { - .name = "android_usb", - .id = -1, - .dev = { - .platform_data = &android_usb_pdata, - }, -}; - -#if SURF7X30_USE_PMIC_KEYPAD -static struct pm8058_pin_config surf7x30_kpd_input_gpio_cfg = { - .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3, - .dir = PM8058_GPIO_INPUT, - .pull_up = PM8058_GPIO_PULL_UP_31P5, - .strength = PM8058_GPIO_STRENGTH_OFF, - .func = PM8058_GPIO_FUNC_NORMAL, - .flags = PM8058_GPIO_INV_IRQ_POL -}; - -static struct pm8058_pin_config surf7x30_kpd_output_gpio_cfg = { - .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3, - .dir = PM8058_GPIO_OUTPUT, - .pull_up = PM8058_GPIO_PULL_NONE, - .strength = PM8058_GPIO_STRENGTH_LOW, - .func = PM8058_GPIO_FUNC_1, - .flags = (PM8058_GPIO_OPEN_DRAIN | - PM8058_GPIO_INV_IRQ_POL), -}; - -#else - -static struct pm8058_pin_config surf7x30_kpd_input_gpio_cfg = { - .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3, - .dir = PM8058_GPIO_INPUT, - .pull_up = PM8058_GPIO_PULL_UP_31P5, - .strength = PM8058_GPIO_STRENGTH_OFF, - .func = PM8058_GPIO_FUNC_NORMAL, - .flags = PM8058_GPIO_INV_IRQ_POL -}; - -static struct pm8058_pin_config surf7x30_kpd_output_gpio_cfg = { - .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3, - .dir = PM8058_GPIO_OUTPUT, - .pull_up = PM8058_GPIO_PULL_NONE, - .strength = PM8058_GPIO_STRENGTH_LOW, - .func = PM8058_GPIO_FUNC_NORMAL, - .flags = (PM8058_GPIO_OPEN_DRAIN | - PM8058_GPIO_INV_IRQ_POL), -}; -#endif - -static unsigned int surf7x30_pmic_col_gpios[] = { - SURF7X30_PM8058_GPIO(0), SURF7X30_PM8058_GPIO(1), - SURF7X30_PM8058_GPIO(2), SURF7X30_PM8058_GPIO(3), - SURF7X30_PM8058_GPIO(4), SURF7X30_PM8058_GPIO(5), - SURF7X30_PM8058_GPIO(6), SURF7X30_PM8058_GPIO(7), -}; -static unsigned int surf7x30_pmic_row_gpios[] = { - SURF7X30_PM8058_GPIO(8), SURF7X30_PM8058_GPIO(9), - SURF7X30_PM8058_GPIO(10), SURF7X30_PM8058_GPIO(11), - SURF7X30_PM8058_GPIO(12), SURF7X30_PM8058_GPIO(13), - SURF7X30_PM8058_GPIO(14), SURF7X30_PM8058_GPIO(15), - SURF7X30_PM8058_GPIO(16), SURF7X30_PM8058_GPIO(17), - SURF7X30_PM8058_GPIO(18), SURF7X30_PM8058_GPIO(19), -}; - -#define KEYMAP_NUM_ROWS ARRAY_SIZE(surf7x30_pmic_row_gpios) -#define KEYMAP_NUM_COLS ARRAY_SIZE(surf7x30_pmic_col_gpios) -#define KEYMAP_INDEX(row, col) (((row) * KEYMAP_NUM_COLS) + (col)) -#define KEYMAP_SIZE (KEYMAP_NUM_ROWS * KEYMAP_NUM_COLS) - -static int mux_keypad_gpios(struct device *dev) -{ - int i; - - for (i = 0; i < KEYMAP_NUM_COLS; ++i) - pm8058_gpio_mux_cfg(dev, surf7x30_pmic_col_gpios[i], - &surf7x30_kpd_input_gpio_cfg); - for (i = 0; i < KEYMAP_NUM_ROWS; ++i) - pm8058_gpio_mux_cfg(dev, surf7x30_pmic_row_gpios[i], - &surf7x30_kpd_output_gpio_cfg); - return 0; -} - -/* if we are using the pmic matrix h/w, we need to do the muxing inside the - * keypad probe function once the keypad has been setup. */ -static int surf7x30_pmic_keypad_init(struct device *dev) -{ - return mux_keypad_gpios(dev->parent); -} - -static const unsigned short surf7x30_pmic_keymap[KEYMAP_SIZE] = { - [KEYMAP_INDEX(0, 6)] = KEY_BACK, -}; - -static struct pm8058_keypad_platform_data surf7x30_pmic_keypad_pdata = { - .name = "surf7x30-keypad", - .num_drv = KEYMAP_NUM_ROWS, - .num_sns = KEYMAP_NUM_COLS, - .scan_delay_shift = 5, - .drv_hold_clks = 4, - .debounce_ms = 10, - .keymap = surf7x30_pmic_keymap, - .init = surf7x30_pmic_keypad_init, -}; - -static struct gpio_event_matrix_info surf7x30_keypad_matrix_info = { - .info.func = gpio_event_matrix_func, - .keymap = surf7x30_pmic_keymap, - .output_gpios = surf7x30_pmic_row_gpios, - .input_gpios = surf7x30_pmic_col_gpios, - .noutputs = KEYMAP_NUM_ROWS, - .ninputs = KEYMAP_NUM_COLS, - .settle_time.tv.nsec = 40 * NSEC_PER_USEC, - .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, - .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_REMOVE_PHANTOM_KEYS |GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/ -}; - -static struct gpio_event_info *surf7x30_keypad_info[] = { - &surf7x30_keypad_matrix_info.info, -}; - -static struct gpio_event_platform_data surf7x30_keypad_data = { - .name = "surf7x30-keypad", - .info = surf7x30_keypad_info, - .info_count = ARRAY_SIZE(surf7x30_keypad_info) -}; - -static struct platform_device surf7x30_keypad_device = { - .name = GPIO_EVENT_DEV_NAME, - .id = 0, - .dev = { - .platform_data = &surf7x30_keypad_data, - }, -}; - -static int surf7x30_pmic_init(struct device *dev) -{ - int ret = 0; - -#if !SURF7X30_USE_PMIC_KEYPAD - /* if we are not using the keypad matrix h/w, but are using the - * gpio_matrix, do the pimuxing here, which is called from pmic's - * probe */ - ret = mux_keypad_gpios(dev); -#endif - return ret; -} - -static struct pm8058_platform_data surf7x30_pm8058_pdata = { - .irq_base = SURF7X30_PM8058_IRQ_BASE, - .gpio_base = SURF7X30_PM8058_GPIO_BASE, - .init = surf7x30_pmic_init, - -#if SURF7X30_USE_PMIC_KEYPAD - .keypad_pdata = &surf7x30_pmic_keypad_pdata, -#endif -}; - -static struct msm_ssbi_platform_data surf7x30_ssbi_pmic_pdata = { - .slave = { - .name = "pm8058-core", - .irq = MSM_GPIO_TO_INT(SURF7X30_GPIO_PMIC_INT_N), - .platform_data = &surf7x30_pm8058_pdata, - }, - .rspinlock_name = "D:PMIC_SSBI", -}; - -static int __init surf7x30_ssbi_pmic_init(void) -{ - int ret; - u32 id; - - pr_info("%s()\n", __func__); - id = PCOM_GPIO_CFG(SURF7X30_GPIO_PMIC_INT_N, 1, GPIO_INPUT, - GPIO_NO_PULL, GPIO_2MA); - ret = msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); - if (ret) - pr_err("%s: gpio %d cfg failed\n", __func__, - SURF7X30_GPIO_PMIC_INT_N); - - ret = gpiochip_reserve(surf7x30_pm8058_pdata.gpio_base, - PM8058_NUM_GPIOS); - WARN(ret, "can't reserve pm8058 gpios. badness will ensue...\n"); - msm_device_ssbi_pmic.dev.platform_data = &surf7x30_ssbi_pmic_pdata; - return platform_device_register(&msm_device_ssbi_pmic); -} - - -static struct i2c_board_info surf_i2c_devices[] = { - /* marimba master is implied at 0x0c */ - { - I2C_BOARD_INFO("marimba-codec", 0x77), - }, -}; - -extern struct sys_timer msm_timer; - -void msm_serial_debug_init(unsigned int base, int irq, - struct device *clk_device, int signal_irq); - -static struct platform_device *devices[] __initdata = { -#if !defined(CONFIG_MSM_SERIAL_DEBUGGER) - &msm_device_uart1, -#endif - &msm_device_smd, - &msm_device_nand, - &msm_device_hsusb, - &usb_mass_storage_device, - &android_usb_device, - &smc91x_device, - &msm_device_i2c2, -#if !SURF7X30_USE_PMIC_KEYPAD - &surf7x30_keypad_device, -#endif -}; - -static void __init surf7x30_init(void) -{ - printk("%s()\n", __func__); - -#if defined(CONFIG_MSM_SERIAL_DEBUGGER) - msm_serial_debug_init(MSM_UART1_PHYS, INT_UART1, - &msm_device_uart1.dev, 1); -#endif - /* do this as early as possible to use pmic gpio/mux facilities */ - surf7x30_ssbi_pmic_init(); - - msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; - platform_add_devices(devices, ARRAY_SIZE(devices)); - - i2c_register_board_info(1, surf_i2c_devices, - ARRAY_SIZE(surf_i2c_devices)); - msm_hsusb_set_vbus_state(1); - msm_hsusb_set_vbus_state(0); - msm_hsusb_set_vbus_state(1); -} - -static void __init surf7x30_fixup(struct machine_desc *desc, struct tag *tags, - char **cmdline, struct meminfo *mi) -{ - mi->nr_banks = 1; - mi->bank[0].start = PHYS_OFFSET; - mi->bank[0].size = (51*1024*1024); -} - -static void __init surf7x30_map_io(void) -{ - msm_map_msm7x30_io(); - msm_clock_init(msm_clocks_7x30, msm_num_clocks_7x30); -} - -MACHINE_START(MSM7X30_SURF, "QCT SURF7X30 Development Board") - .boot_params = 0x00200100, - .fixup = surf7x30_fixup, - .map_io = surf7x30_map_io, - .init_irq = msm_init_irq, - .init_machine = surf7x30_init, - .timer = &msm_timer, -MACHINE_END From 375e94b91e1553386fa6caadf49b19b6e2d1e0a9 Mon Sep 17 00:00:00 2001 From: Bikas Gurung Date: Thu, 20 May 2010 15:53:53 -0700 Subject: [PATCH 0779/2556] video: msm: lcdc: update lcdc output format to support RGB888 Change-Id: I703d8706497c69fdba8ac8d02417197c192e0910 Signed-off-by: Bikas Gurung --- arch/arm/mach-msm/include/mach/msm_fb.h | 1 + drivers/video/msm/mdp_lcdc.c | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/msm_fb.h b/arch/arm/mach-msm/include/mach/msm_fb.h index 339fa463a455e..052ba22289a29 100644 --- a/arch/arm/mach-msm/include/mach/msm_fb.h +++ b/arch/arm/mach-msm/include/mach/msm_fb.h @@ -24,6 +24,7 @@ struct mddi_info; /* output interface format */ #define MSM_MDP_OUT_IF_FMT_RGB565 0 #define MSM_MDP_OUT_IF_FMT_RGB666 1 +#define MSM_MDP_OUT_IF_FMT_RGB888 2 struct msm_fb_data { int xres; /* x resolution in pixels */ diff --git a/drivers/video/msm/mdp_lcdc.c b/drivers/video/msm/mdp_lcdc.c index a88840aa45a54..9590bc279b8ac 100644 --- a/drivers/video/msm/mdp_lcdc.c +++ b/drivers/video/msm/mdp_lcdc.c @@ -159,9 +159,17 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) dma_cfg &= ~DMA_DST_BITS_MASK; if (fb_panel->fb_data->output_format == MSM_MDP_OUT_IF_FMT_RGB666) - dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + dma_cfg |= DMA_DSTC0G_6BITS | + DMA_DSTC1B_6BITS | + DMA_DSTC2R_6BITS; + else if (fb_panel->fb_data->output_format == MSM_MDP_OUT_IF_FMT_RGB888) + dma_cfg |= DMA_DSTC0G_8BITS | + DMA_DSTC1B_8BITS | + DMA_DSTC2R_8BITS; else - dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + dma_cfg |= DMA_DSTC0G_6BITS | + DMA_DSTC1B_5BITS | + DMA_DSTC2R_5BITS; mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG); From 1f0a2f30371973793cce62f56615d07a7616bafb Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 10 Jan 2011 15:44:43 -0800 Subject: [PATCH 0780/2556] ARM: msm: 7x30: use MACH_xxx to chose which board to build Using ARCH_MSM7X30 is not correct since we don't always want to build the surf board files just because we have the arch selected. Change-Id: Ia02fb9ea83609cbce5603b7e9466b93766002020 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 2abdea9f547ea..f46584eb06b25 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -76,13 +76,15 @@ obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-keypad.o fish_battery.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-panel.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-mmc.o -obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o +obj-$(CONFIG_ARCH_MSM7X30) += devices-msm7x30.o obj-$(CONFIG_ARCH_QSD8X50) += devices-qsd8x50.o obj-$(CONFIG_MACH_QSD8X50_SURF) += board-qsd8x50.o obj-$(CONFIG_MACH_QSD8X50A_ST1_5) += board-qsd8x50.o obj-$(CONFIG_MACH_QSD8X50_FFA) += board-qsd8x50.o obj-$(CONFIG_ARCH_MSM8X60) += board-msm8x60.o +obj-$(CONFIG_MACH_MSM7X30_SURF) += board-msm7x30.o + obj-$(CONFIG_ARCH_MSM7X30) += gpiomux-7x30.o gpiomux-v1.o gpiomux.o obj-$(CONFIG_ARCH_QSD8X50) += gpiomux-8x50.o gpiomux-v1.o gpiomux.o obj-$(CONFIG_ARCH_MSM8X60) += gpiomux-8x60.o gpiomux-v2.o gpiomux.o From d98704863c9249e1adcdc3605153306bc97fb95a Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 10 Jan 2011 16:02:46 -0800 Subject: [PATCH 0781/2556] ARM: msm: board-msm7x30: move pmic gpio defs to header file Change-Id: I4ce2b285ab3a0e9ca47e266b2964496c38433fa8 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 7 +------ arch/arm/mach-msm/board-msm7x30.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 arch/arm/mach-msm/board-msm7x30.h diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 40661ae9fd45b..3eed8520e1d83 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -44,12 +44,7 @@ #include "proc_comm.h" #include "clock-pcom.h" #include "gpiomux.h" - -#define MSM7X30_PM8058_GPIO_BASE FIRST_BOARD_GPIO -#define MSM7X30_PM8058_GPIO(x) (MSM7X30_PM8058_GPIO_BASE + (x)) -#define MSM7X30_PM8058_IRQ_BASE FIRST_BOARD_IRQ - -#define MSM7X30_GPIO_PMIC_INT_N 27 +#include "board-msm7x30.h" extern struct sys_timer msm_timer; diff --git a/arch/arm/mach-msm/board-msm7x30.h b/arch/arm/mach-msm/board-msm7x30.h new file mode 100644 index 0000000000000..b5dc923ab1486 --- /dev/null +++ b/arch/arm/mach-msm/board-msm7x30.h @@ -0,0 +1,29 @@ +/* linux/arch/arm/mach-msm/board-msm7x30.h + * + * Copyright (C) 2011 Google, Inc. + * Author: Dima Zavin + * + * 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 __ARCH_ARM_MACH_MSM_BOARD_MSM7X30_H +#define __ARCH_ARM_MACH_MSM_BOARD_MSM7X30_H + +#include +#include + +#define MSM7X30_PM8058_GPIO_BASE FIRST_BOARD_GPIO +#define MSM7X30_PM8058_GPIO(x) (MSM7X30_PM8058_GPIO_BASE + (x)) +#define MSM7X30_PM8058_IRQ_BASE FIRST_BOARD_IRQ + +#define MSM7X30_GPIO_PMIC_INT_N 27 + +#endif From 769d1eea8d7515120574b77f48dc97c1bf94748d Mon Sep 17 00:00:00 2001 From: Michael Bohan Date: Mon, 21 Jun 2010 14:17:34 -0700 Subject: [PATCH 0782/2556] [ARM] msm: pmic: Add support for msm7x30 The modem used on msm7x30 only support the RPC version 0x30001. Change-Id: I96ef47e6ca1c8afa9b1891f1c42f8227e391b66d Signed-off-by: Michael Bohan --- arch/arm/mach-msm/pmic.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/mach-msm/pmic.c b/arch/arm/mach-msm/pmic.c index e6dc9fe50aaea..3dee193b47282 100644 --- a/arch/arm/mach-msm/pmic.c +++ b/arch/arm/mach-msm/pmic.c @@ -94,7 +94,11 @@ #define PMIC_RPC_TIMEOUT (5*HZ) #define PMIC_RPC_PROG 0x30000061 +#ifdef CONFIG_ARCH_MSM7X30 +#define PMIC_RPC_VER 0x00030001 +#else #define PMIC_RPC_VER 0x00010001 +#endif /* error bit flags defined by modem side */ #define PM_ERR_FLAG__PAR1_OUT_OF_RANGE (0x0001) From 1c5c9b844812910b65a9c522b037dae4b4c088ca Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 11 Jan 2011 14:00:56 -0800 Subject: [PATCH 0783/2556] ARM: msm: board-msm7x30: use uart2 for console Also grab an extra reference for the uart2 clock if debug_ll is enabled, otherwise we'll turn the clock off during clock_late_init Change-Id: I24ef65be0e083d9bbdda1b838316d0bd63d53895 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 3eed8520e1d83..9080f550ca004 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -165,9 +166,6 @@ static struct platform_device usb_mass_storage_device = { static struct platform_device *devices[] __initdata = { #if defined(CONFIG_SERIAL_MSM) && !defined(CONFIG_MSM_SERIAL_DEBUGGER) - &msm_device_uart1, -#endif -#if defined(CONFIG_SERIAL_MSM) || defined(CONFIG_MSM_SERIAL_DEBUGGER) &msm_device_uart2, #endif &msm_device_smd, @@ -294,9 +292,21 @@ extern void msm_serial_debug_init(unsigned int base, int irq, static void __init msm7x30_init(void) { +#ifdef CONFIG_DEBUG_LL + { + /* HACK: get a fake clock request for uart2 for debug_ll */ + struct clk *uart2_clk; + uart2_clk = clk_get(&msm_device_uart2.dev, "uart_clk"); + if (IS_ERR(uart2_clk)) + uart2_clk = NULL; + else + clk_enable(uart2_clk); + } +#endif + #if defined(CONFIG_MSM_SERIAL_DEBUGGER) - msm_serial_debug_init(MSM_UART1_PHYS, INT_UART1, - &msm_device_uart1.dev, 1); + msm_serial_debug_init(MSM_UART2_PHYS, INT_UART2, + &msm_device_uart2.dev, 1); #endif msm7x30_ssbi_pmic_init(); From a3d92f6ed663be648a762500b40fa6d7822ad181 Mon Sep 17 00:00:00 2001 From: Michael Bohan Date: Wed, 2 Jun 2010 12:19:40 -0700 Subject: [PATCH 0784/2556] ARM: msm: board-msm7x30: Add display support for SURF/FFA The board uses a Toshiba Wega MDDI client. Change-Id: Id30127f309432ab4204d02a13e6c36406bc063fd Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/board-msm7x30-panel.c | 730 ++++++++++++++++++++++++ arch/arm/mach-msm/board-msm7x30.c | 11 + 3 files changed, 742 insertions(+) create mode 100644 arch/arm/mach-msm/board-msm7x30-panel.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index f46584eb06b25..3790eeae2973d 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_MACH_QSD8X50_FFA) += board-qsd8x50.o obj-$(CONFIG_ARCH_MSM8X60) += board-msm8x60.o obj-$(CONFIG_MACH_MSM7X30_SURF) += board-msm7x30.o +obj-$(CONFIG_MACH_MSM7X30_SURF) += board-msm7x30-panel.o obj-$(CONFIG_ARCH_MSM7X30) += gpiomux-7x30.o gpiomux-v1.o gpiomux.o obj-$(CONFIG_ARCH_QSD8X50) += gpiomux-8x50.o gpiomux-v1.o gpiomux.o diff --git a/arch/arm/mach-msm/board-msm7x30-panel.c b/arch/arm/mach-msm/board-msm7x30-panel.c new file mode 100644 index 0000000000000..158c65cca509a --- /dev/null +++ b/arch/arm/mach-msm/board-msm7x30-panel.c @@ -0,0 +1,730 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "gpiomux.h" +#include "devices.h" +#include "pmic.h" +#include "board-msm7x30.h" + +#define MSM_FB_SIZE (0x00500000UL) + +#define CLK_NS_TO_RATE(ns) (1000000000UL / (ns)) + +#define MDDI_CLIENT_CORE_BASE (0x00108000UL) +#define LCD_CONTROL_BLOCK_BASE (0x00110000UL) +#define SPI_BLOCK_BASE (0x00120000UL) +#define PWM_BLOCK_BASE (0x00140000UL) +#define GPIO_BLOCK_BASE (0x00150000UL) +#define SYSTEM_BLOCK1_BASE (0x00160000UL) +#define SYSTEM_BLOCK2_BASE (0x00170000UL) + +#define TTBUSSEL (MDDI_CLIENT_CORE_BASE | 0x18) +#define DPSET0 (MDDI_CLIENT_CORE_BASE | 0x1C) +#define DPSET1 (MDDI_CLIENT_CORE_BASE | 0x20) +#define DPSUS (MDDI_CLIENT_CORE_BASE | 0x24) +#define DPRUN (MDDI_CLIENT_CORE_BASE | 0x28) +#define SYSCKENA (MDDI_CLIENT_CORE_BASE | 0x2C) + +#define BITMAP0 (MDDI_CLIENT_CORE_BASE|0x44) +#define BITMAP1 (MDDI_CLIENT_CORE_BASE|0x48) +#define BITMAP2 (MDDI_CLIENT_CORE_BASE|0x4C) +#define BITMAP3 (MDDI_CLIENT_CORE_BASE|0x50) +#define BITMAP4 (MDDI_CLIENT_CORE_BASE|0x54) + +#define SRST (LCD_CONTROL_BLOCK_BASE|0x00) +#define PORT_ENB (LCD_CONTROL_BLOCK_BASE|0x04) +#define START (LCD_CONTROL_BLOCK_BASE|0x08) +#define PORT (LCD_CONTROL_BLOCK_BASE|0x0C) + +#define INTFLG (LCD_CONTROL_BLOCK_BASE|0x18) +#define INTMSK (LCD_CONTROL_BLOCK_BASE|0x1C) +#define MPLFBUF (LCD_CONTROL_BLOCK_BASE|0x20) + +#define PXL (LCD_CONTROL_BLOCK_BASE|0x30) +#define HCYCLE (LCD_CONTROL_BLOCK_BASE|0x34) +#define HSW (LCD_CONTROL_BLOCK_BASE|0x38) +#define HDE_START (LCD_CONTROL_BLOCK_BASE|0x3C) +#define HDE_SIZE (LCD_CONTROL_BLOCK_BASE|0x40) +#define VCYCLE (LCD_CONTROL_BLOCK_BASE|0x44) +#define VSW (LCD_CONTROL_BLOCK_BASE|0x48) +#define VDE_START (LCD_CONTROL_BLOCK_BASE|0x4C) +#define VDE_SIZE (LCD_CONTROL_BLOCK_BASE|0x50) +#define WAKEUP (LCD_CONTROL_BLOCK_BASE|0x54) +#define REGENB (LCD_CONTROL_BLOCK_BASE|0x5C) +#define VSYNIF (LCD_CONTROL_BLOCK_BASE|0x60) +#define WRSTB (LCD_CONTROL_BLOCK_BASE|0x64) +#define RDSTB (LCD_CONTROL_BLOCK_BASE|0x68) +#define ASY_DATA (LCD_CONTROL_BLOCK_BASE|0x6C) +#define ASY_DATB (LCD_CONTROL_BLOCK_BASE|0x70) +#define ASY_DATC (LCD_CONTROL_BLOCK_BASE|0x74) +#define ASY_DATD (LCD_CONTROL_BLOCK_BASE|0x78) +#define ASY_DATE (LCD_CONTROL_BLOCK_BASE|0x7C) +#define ASY_DATF (LCD_CONTROL_BLOCK_BASE|0x80) +#define ASY_DATG (LCD_CONTROL_BLOCK_BASE|0x84) +#define ASY_DATH (LCD_CONTROL_BLOCK_BASE|0x88) +#define ASY_CMDSET (LCD_CONTROL_BLOCK_BASE|0x8C) +#define MONI (LCD_CONTROL_BLOCK_BASE|0xB0) +#define VPOS (LCD_CONTROL_BLOCK_BASE|0xC0) + +#define SSICTL (SPI_BLOCK_BASE|0x00) +#define SSITIME (SPI_BLOCK_BASE|0x04) +#define SSITX (SPI_BLOCK_BASE|0x08) +#define SSIINTS (SPI_BLOCK_BASE|0x14) + +#define TIMER0LOAD (PWM_BLOCK_BASE|0x00) +#define TIMER0CTRL (PWM_BLOCK_BASE|0x08) +#define PWM0OFF (PWM_BLOCK_BASE|0x1C) +#define TIMER1LOAD (PWM_BLOCK_BASE|0x20) +#define TIMER1CTRL (PWM_BLOCK_BASE|0x28) +#define PWM1OFF (PWM_BLOCK_BASE|0x3C) +#define TIMER2LOAD (PWM_BLOCK_BASE|0x40) +#define TIMER2CTRL (PWM_BLOCK_BASE|0x48) +#define PWM2OFF (PWM_BLOCK_BASE|0x5C) +#define PWMCR (PWM_BLOCK_BASE|0x68) + +#define GPIODATA (GPIO_BLOCK_BASE|0x00) +#define GPIODIR (GPIO_BLOCK_BASE|0x04) +#define GPIOIS (GPIO_BLOCK_BASE|0x08) +#define GPIOIEV (GPIO_BLOCK_BASE|0x10) +#define GPIOIC (GPIO_BLOCK_BASE|0x20) +#define GPIOPC (GPIO_BLOCK_BASE|0x28) + +#define WKREQ (SYSTEM_BLOCK1_BASE|0x00) +#define CLKENB (SYSTEM_BLOCK1_BASE|0x04) +#define DRAMPWR (SYSTEM_BLOCK1_BASE|0x08) +#define INTMASK (SYSTEM_BLOCK1_BASE|0x0C) +#define CNT_DIS (SYSTEM_BLOCK1_BASE|0x10) + +#define GPIOSEL (SYSTEM_BLOCK2_BASE|0x00) + +struct mddi_table { + uint32_t reg; + uint32_t value; +}; +static struct mddi_table mddi_toshiba_init_table[] = { + { DPSET0, 0x4bec0066 }, + { DPSET1, 0x00000113 }, + { DPSUS, 0x00000000 }, + { DPRUN, 0x00000001 }, + { 1, 5 }, + { SYSCKENA, 0x00000001 }, + { CLKENB, 0x0000A0E9 }, + { GPIODATA, 0x03FF0000 }, + { GPIODIR, 0x0000024D }, + { GPIOSEL, 0x00000173 }, + { GPIOPC, 0x03C300C0 }, + { WKREQ, 0x00000000 }, + + { GPIOIS, 0x00000000 }, + { GPIOIEV, 0x00000001 }, + { GPIOIC, 0x000003FF }, + { GPIODATA, 0x00040004 }, + + { GPIODATA, 0x00080008 }, + { DRAMPWR, 0x00000001 }, + { CLKENB, 0x0000A0EB }, + { PWMCR, 0x00000000 }, + { 1, 1 }, + { SSICTL, 0x00060399 }, + { SSITIME, 0x00000100 }, + { CNT_DIS, 0x00000002 }, + { SSICTL, 0x0006039b }, + { SSITX, 0x00000000 }, + { 1, 7 }, + { SSITX, 0x00000000 }, + { 1, 7 }, + { SSITX, 0x00000000 }, + { 1, 7 }, + { SSITX, 0x000800BA }, + { SSITX, 0x00000111 }, + { SSITX, 0x00080036 }, + { SSITX, 0x00000100 }, + { 1, 1 }, + { SSITX, 0x0008003A }, + { SSITX, 0x00000160 }, + { SSITX, 0x000800B1 }, + { SSITX, 0x0000015D }, + { 1, 1 }, + { SSITX, 0x000800B2 }, + { SSITX, 0x00000133 }, + { SSITX, 0x000800B3 }, + { SSITX, 0x00000122 }, + { 1, 1 }, + { SSITX, 0x000800B4 }, + { SSITX, 0x00000102 }, + { SSITX, 0x000800B5 }, + { SSITX, 0x0000011E }, + { 1, 1 }, + { SSITX, 0x000800B6 }, + { SSITX, 0x00000127 }, + { SSITX, 0x000800B7 }, + { SSITX, 0x00000103 }, + { 1, 1 }, + { SSITX, 0x000800B9 }, + { SSITX, 0x00000124 }, + { SSITX, 0x000800BD }, + { SSITX, 0x000001A1 }, + { 1, 1 }, + { SSITX, 0x000800BB }, + { SSITX, 0x00000100 }, + { SSITX, 0x000800BF }, + { SSITX, 0x00000101 }, + { 1, 1 }, + { SSITX, 0x000800BE }, + { SSITX, 0x00000100 }, + { SSITX, 0x000800C0 }, + { SSITX, 0x00000111 }, + { 1, 1 }, + { SSITX, 0x000800C1 }, + { SSITX, 0x00000111 }, + { SSITX, 0x000800C2 }, + { SSITX, 0x00000111 }, + { 1, 1 }, + { SSITX, 0x000800C3 }, + { SSITX, 0x00080132 }, + { SSITX, 0x00000132 }, + { 1, 1 }, + { SSITX, 0x000800C4 }, + { SSITX, 0x00080132 }, + { SSITX, 0x00000132 }, + { 1, 1 }, + { SSITX, 0x000800C5 }, + { SSITX, 0x00080132 }, + { SSITX, 0x00000132 }, + { 1, 1 }, + { SSITX, 0x000800C6 }, + { SSITX, 0x00080132 }, + { SSITX, 0x00000132 }, + { 1, 1 }, + { SSITX, 0x000800C7 }, + { SSITX, 0x00080164 }, + { SSITX, 0x00000145 }, + { 1, 1 }, + { SSITX, 0x000800C8 }, + { SSITX, 0x00000144 }, + { SSITX, 0x000800C9 }, + { SSITX, 0x00000152 }, + { 1, 1 }, + { SSITX, 0x000800CA }, + { SSITX, 0x00000100 }, + { 1, 1 }, + { SSITX, 0x000800EC }, + { SSITX, 0x00080101 }, + { SSITX, 0x000001FC }, + { 1, 1 }, + { SSITX, 0x000800CF }, + { SSITX, 0x00000101 }, + { 1, 1 }, + { SSITX, 0x000800D0 }, + { SSITX, 0x00080110 }, + { SSITX, 0x00000104 }, + { 1, 1 }, + { SSITX, 0x000800D1 }, + { SSITX, 0x00000101 }, + { 1, 1 }, + { SSITX, 0x000800D2 }, + { SSITX, 0x00080100 }, + { SSITX, 0x00000128 }, + { 1, 1 }, + { SSITX, 0x000800D3 }, + { SSITX, 0x00080100 }, + { SSITX, 0x00000128 }, + { 1, 1 }, + { SSITX, 0x000800D4 }, + { SSITX, 0x00080126 }, + { SSITX, 0x000001A4 }, + { 1, 1 }, + { SSITX, 0x000800D5 }, + { SSITX, 0x00000120 }, + { 1, 1 }, + { SSITX, 0x000800EF }, + { SSITX, 0x00080132 }, + { SSITX, 0x00000100 }, + { 1, 1 }, + { BITMAP0, 0x032001E0 }, + { BITMAP1, 0x032001E0 }, + { BITMAP2, 0x014000F0 }, + { BITMAP3, 0x014000F0 }, + { BITMAP4, 0x014000F0 }, + { CLKENB, 0x0000A1EB }, + { PORT_ENB, 0x00000001 }, + { PORT, 0x00000004 }, + { PXL, 0x00000002 }, + { MPLFBUF, 0x00000000 }, + { HCYCLE, 0x000000FD }, + { HSW, 0x00000003 }, + { HDE_START, 0x00000007 }, + { HDE_SIZE, 0x000000EF }, + { VCYCLE, 0x00000325 }, + { VSW, 0x00000001 }, + { VDE_START, 0x00000003 }, + { VDE_SIZE, 0x0000031F }, + { START, 0x00000001 }, + { 1, 32 }, + { SSITX, 0x000800BC }, + { SSITX, 0x00000180 }, + { SSITX, 0x0008003B }, + { SSITX, 0x00000100 }, + { 1, 1 }, + { SSITX, 0x000800B0 }, + { SSITX, 0x00000116 }, + { 1, 1 }, + { SSITX, 0x000800B8 }, + { SSITX, 0x000801FF }, + { SSITX, 0x000001F5 }, + { 1, 1 }, + { SSITX, 0x00000011 }, + { 1, 5 }, + { SSITX, 0x00000029 }, +}; + +#define MSM7X30_SURF_DEFAULT_BACKLIGHT_BRIGHTNESS 15 + +static int msm7x30_backlight_off; +static int msm7x30_backlight_brightness = + MSM7X30_SURF_DEFAULT_BACKLIGHT_BRIGHTNESS; + +static DEFINE_MUTEX(msm7x30_backlight_lock); + + +static void msm7x30_set_backlight_level(uint8_t level) +{ + pmic_set_led_intensity(LED_LCD, + MSM7X30_SURF_DEFAULT_BACKLIGHT_BRIGHTNESS * + level / LED_FULL); +} + +static void msm7x30_process_mddi_table(struct msm_mddi_client_data *client_data, + const struct mddi_table *table, + size_t count) +{ + int i; + for (i = 0; i < count; i++) { + uint32_t reg = table[i].reg; + uint32_t value = table[i].value; + + if (reg == 0) + udelay(value); + else if (reg == 1) + msleep(value); + else + client_data->remote_write(client_data, value, reg); + } +} + +static unsigned wega_reset_gpio = 180; +static unsigned fluid_vee_reset_gpio = 20; + +static struct pm8058_pin_config msm7x30_mddi_sleep_clk_cfg_on = { + .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3, + .dir = PM8058_GPIO_OUTPUT, + .pull_up = PM8058_GPIO_PULL_NONE, + .strength = PM8058_GPIO_STRENGTH_HIGH, + .func = PM8058_GPIO_FUNC_2, +}; + +static struct pm8058_pin_config msm7x30_mddi_sleep_clk_cfg_off = { + .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3, + .dir = PM8058_GPIO_OUTPUT, + .pull_up = PM8058_GPIO_PULL_NONE, + .strength = PM8058_GPIO_STRENGTH_HIGH, + .func = PM8058_GPIO_FUNC_NORMAL, +}; + +static void msm7x30_mddi_power_client(struct msm_mddi_client_data *mddi, int on) +{ + int rc = 0, flag_on = !!on; + static int display_common_power_save_on; + struct vreg *vreg_ldo12, *vreg_ldo15 = NULL; + struct vreg *vreg_ldo20, *vreg_ldo16, *vreg_ldo8 = NULL; + + if (display_common_power_save_on == flag_on) + return; + + display_common_power_save_on = flag_on; + + if (on) { + // XXX enable wega reset gpio + + /* reset Toshiba WeGA chip -- toggle reset pin -- gpio_180 */ + gpio_set_value(180, 0); /* bring reset line low to hold reset*/ + } + + /* Toshiba WeGA power -- has 3 power source */ + /* 1.5V -- LDO20*/ + vreg_ldo20 = vreg_get(NULL, "gp13"); + + if (IS_ERR(vreg_ldo20) || vreg_ldo20 == NULL) { + rc = PTR_ERR(vreg_ldo20); + pr_err("%s: gp13 vreg get failed (%d)\n", + __func__, rc); + return; + } + + /* 1.8V -- LDO12 */ + vreg_ldo12 = vreg_get(NULL, "gp9"); + + if (IS_ERR(vreg_ldo12) || vreg_ldo12 == NULL) { + rc = PTR_ERR(vreg_ldo12); + pr_err("%s: gp9 vreg get failed (%d)\n", + __func__, rc); + return; + } + + /* 2.6V -- LDO16 */ + vreg_ldo16 = vreg_get(NULL, "gp10"); + + if (IS_ERR(vreg_ldo16) || vreg_ldo16 == NULL) { + rc = PTR_ERR(vreg_ldo16); + pr_err("%s: gp10 vreg get failed (%d)\n", + __func__, rc); + return; + } + + if (machine_is_msm7x30_fluid()) { + /* 1.8V -- LDO8 */ + vreg_ldo8 = vreg_get(NULL, "gp7"); + + if (IS_ERR(vreg_ldo8) || vreg_ldo8 == NULL) { + rc = PTR_ERR(vreg_ldo8); + pr_err("%s: gp7 vreg get failed (%d)\n", + __func__, rc); + return; + } + } else { + /* lcd panel power */ + /* 3.1V -- LDO15 */ + vreg_ldo15 = vreg_get(NULL, "gp6"); + + if (IS_ERR(vreg_ldo15) || vreg_ldo15 == NULL) { + rc = PTR_ERR(vreg_ldo15); + pr_err("%s: gp6 vreg get failed (%d)\n", + __func__, rc); + return; + } + } + + rc = vreg_set_level(vreg_ldo20, 1500); + if (rc) { + pr_err("%s: vreg LDO20 set level failed (%d)\n", + __func__, rc); + return; + } + + rc = vreg_set_level(vreg_ldo12, 1800); + if (rc) { + pr_err("%s: vreg LDO12 set level failed (%d)\n", + __func__, rc); + return; + } + + rc = vreg_set_level(vreg_ldo16, 2600); + if (rc) { + pr_err("%s: vreg LDO16 set level failed (%d)\n", + __func__, rc); + return; + } + + if (machine_is_msm7x30_fluid()) { + rc = vreg_set_level(vreg_ldo8, 1800); + if (rc) { + pr_err("%s: vreg LDO8 set level failed (%d)\n", + __func__, rc); + return; + } + } else { + rc = vreg_set_level(vreg_ldo15, 3100); + if (rc) { + pr_err("%s: vreg LDO15 set level failed (%d)\n", + __func__, rc); + return; + } + } + + if (on) { + rc = vreg_enable(vreg_ldo20); + if (rc) { + pr_err("%s: LDO20 vreg enable failed (%d)\n", + __func__, rc); + return; + } + + rc = vreg_enable(vreg_ldo12); + if (rc) { + pr_err("%s: LDO12 vreg enable failed (%d)\n", + __func__, rc); + return; + } + + rc = vreg_enable(vreg_ldo16); + if (rc) { + pr_err("%s: LDO16 vreg enable failed (%d)\n", + __func__, rc); + return; + } + + if (machine_is_msm7x30_fluid()) { + rc = vreg_enable(vreg_ldo8); + if (rc) { + pr_err("%s: LDO8 vreg enable failed (%d)\n", + __func__, rc); + return; + } + } else { + rc = vreg_enable(vreg_ldo15); + if (rc) { + pr_err("%s: LDO15 vreg enable failed (%d)\n", + __func__, rc); + return; + } + } + + mdelay(5); /* ensure power is stable */ + + if (machine_is_msm7x30_fluid()) { + // XXX enable vee reset gpio + + /* assert vee reset_n */ + gpio_set_value(20, 1); + gpio_set_value(20, 0); + mdelay(1); + gpio_set_value(20, 1); + } + + gpio_set_value(180, 1); /* bring reset line high */ + mdelay(10); /* 10 msec before IO can be accessed */ + + rc = pm8058_gpio_mux(MSM7X30_PM8058_GPIO(37), + &msm7x30_mddi_sleep_clk_cfg_on); + if (rc) + pr_err("%s: pm8058_gpio_mux failure\n", __func__); + } else { + vreg_disable(vreg_ldo20); + vreg_disable(vreg_ldo16); + + gpio_set_value(180, 0); /* bring reset line low */ + + if (machine_is_msm7x30_fluid()) + vreg_disable(vreg_ldo8); + else + vreg_disable(vreg_ldo15); + + mdelay(5); /* ensure power is stable */ + + vreg_disable(vreg_ldo12); + + if (machine_is_msm7x30_fluid()) { + // XXX disable vee_reset_gpio + } + + rc = pm8058_gpio_mux(MSM7X30_PM8058_GPIO(37), + &msm7x30_mddi_sleep_clk_cfg_off); + if (rc) + pr_err("%s: pm8058_gpio_mux failure\n", __func__); + } +} + +static int msm7x30_mddi_toshiba_client_init( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + client_data->auto_hibernate(client_data, 0); + msm7x30_process_mddi_table(client_data, mddi_toshiba_init_table, + ARRAY_SIZE(mddi_toshiba_init_table)); + client_data->auto_hibernate(client_data, 1); + + return 0; +} + +static int msm7x30_mddi_toshiba_client_uninit( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + return 0; +} + +static int msm7x30_mddi_panel_unblank( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + mutex_lock(&msm7x30_backlight_lock); + msm7x30_set_backlight_level(msm7x30_backlight_brightness); + msm7x30_backlight_off = 0; + mutex_unlock(&msm7x30_backlight_lock); + + return 0; +} + +static int msm7x30_mddi_panel_blank( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + mutex_lock(&msm7x30_backlight_lock); + msm7x30_set_backlight_level(0); + msm7x30_backlight_off = 1; + mutex_unlock(&msm7x30_backlight_lock); + + return 0; +} + +static void msm7x30_brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + mutex_lock(&msm7x30_backlight_lock); + msm7x30_backlight_brightness = value; + if (!msm7x30_backlight_off) + msm7x30_set_backlight_level(msm7x30_backlight_brightness); + mutex_unlock(&msm7x30_backlight_lock); +} + +static struct led_classdev msm7x30_backlight_led = { + .name = "lcd-backlight", + .brightness = MSM7X30_SURF_DEFAULT_BACKLIGHT_BRIGHTNESS, + .brightness_set = msm7x30_brightness_set, +}; + +static int msm7x30_backlight_probe(struct platform_device *pdev) +{ + led_classdev_register(&pdev->dev, &msm7x30_backlight_led); + return 0; +} + +static int msm7x30_backlight_remove(struct platform_device *pdev) +{ + led_classdev_unregister(&msm7x30_backlight_led); + return 0; +} + +static struct platform_driver msm7x30_backlight_driver = { + .probe = msm7x30_backlight_probe, + .remove = msm7x30_backlight_remove, + .driver = { + .name = "msm7x30-backlight", + .owner = THIS_MODULE, + }, +}; + +static struct resource resources_msm_fb[] = { + { + .flags = IORESOURCE_MEM, + }, +}; + +void __init msm7x30_allocate_fbmem(void) +{ + unsigned long base; + unsigned long size; + + size = MSM_FB_SIZE; + base = memblock_alloc(size, SZ_1M); + memblock_free(base, size); + memblock_remove(base, size); + resources_msm_fb[0].start = base; + resources_msm_fb[0].end = resources_msm_fb[0].start + size - 1; + pr_info("%s: allocated %lu bytes at 0x%lx\n", __func__, size, base); +} + +#define TOSHIBAWEGA_MFR_NAME 0xd263 +#define TOSHIBAWEGA_PRODUCT_CODE 0x8722 + +static void toshibawega_fixup(uint16_t *mfr_name, uint16_t *product_code) +{ + *mfr_name = TOSHIBAWEGA_MFR_NAME ; + *product_code = TOSHIBAWEGA_PRODUCT_CODE ; +} + +static struct msm_mddi_bridge_platform_data toshiba_client_data = { + .init = msm7x30_mddi_toshiba_client_init, + .uninit = msm7x30_mddi_toshiba_client_uninit, + .blank = msm7x30_mddi_panel_blank, + .unblank = msm7x30_mddi_panel_unblank, + .fb_data = { + .xres = 480, + .yres = 800, + .width = 45, + .height = 67, + .output_format = MSM_MDP_OUT_IF_FMT_RGB888, + }, +}; + +static struct msm_mddi_platform_data mddi_pdata = { + .clk_rate = 445500000, + .fixup = toshibawega_fixup, + .power_client = msm7x30_mddi_power_client, + .fb_resource = resources_msm_fb, + .num_clients = 1, + .client_platform_data = { + { + .product_id = (0xd263 << 16 | 0x8722), + .name = "mddi_c_simple", + .id = 0, + .client_data = &toshiba_client_data, + .clk_rate = 0, + }, + }, +}; + +static struct platform_device msm7x30_backlight = { + .name = "msm7x30-backlight", +}; + +int __init msm7x30_init_panel(void) +{ + int rc; + + msm_gpiomux_write(180, 0, + GPIOMUX_FUNC_GPIO | + GPIOMUX_PULL_NONE | + GPIOMUX_DIR_OUTPUT | + GPIOMUX_DRV_2MA | GPIOMUX_VALID); + msm_gpiomux_write(20, 0, + GPIOMUX_FUNC_GPIO | + GPIOMUX_PULL_NONE | + GPIOMUX_DIR_OUTPUT | + GPIOMUX_DRV_2MA | GPIOMUX_VALID); + + rc = platform_device_register(&msm_device_mdp); + if (rc) + return rc; + + msm_device_mddi0.dev.platform_data = &mddi_pdata; + rc = platform_device_register(&msm_device_mddi0); + if (rc) + return rc; + + platform_device_register(&msm7x30_backlight); + return platform_driver_register(&msm7x30_backlight_driver); +} + +device_initcall(msm7x30_init_panel); diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 9080f550ca004..62edb6720b277 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -335,6 +335,14 @@ static void __init msm7x30_fixup(struct machine_desc *desc, struct tag *tags, mi->bank[0].size = (51*1024*1024); } + +extern void __init msm7x30_allocate_fbmem(void); + +static void __init msm7x30_reserve(void) +{ + msm7x30_allocate_fbmem(); +} + MACHINE_START(MSM7X30_SURF, "QCT MSM7X30 SURF") #ifdef CONFIG_MSM_DEBUG_UART #endif @@ -344,6 +352,7 @@ MACHINE_START(MSM7X30_SURF, "QCT MSM7X30 SURF") .init_irq = msm7x30_init_irq, .init_machine = msm7x30_init, .timer = &msm_timer, + .reserve = msm7x30_reserve, MACHINE_END MACHINE_START(MSM7X30_FFA, "QCT MSM7X30 FFA") @@ -354,6 +363,7 @@ MACHINE_START(MSM7X30_FFA, "QCT MSM7X30 FFA") .init_irq = msm7x30_init_irq, .init_machine = msm7x30_init, .timer = &msm_timer, + .reserve = msm7x30_reserve, MACHINE_END MACHINE_START(MSM7X30_FLUID, "QCT MSM7X30 FLUID") @@ -364,4 +374,5 @@ MACHINE_START(MSM7X30_FLUID, "QCT MSM7X30 FLUID") .init_irq = msm7x30_init_irq, .init_machine = msm7x30_init, .timer = &msm_timer, + .reserve = msm7x30_reserve, MACHINE_END From e2cfde510206a3b452f77f4e92e72fa523942932 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 11 Jan 2011 19:24:03 -0800 Subject: [PATCH 0785/2556] Revert "ARM: msm: HACK: board-msm7x30: add a single 51mb bank for now" This reverts commit 91649092086a46de9792e835aee1dff58f3102d4. Conflicts: arch/arm/mach-msm/board-msm7x30.c Change-Id: Ia3a2d2edce1f11ced12dc6e16153d96e826fbe62 --- arch/arm/mach-msm/board-msm7x30.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 62edb6720b277..9c6f740ba2cae 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -327,14 +327,6 @@ static void __init msm7x30_map_io(void) msm_clock_init(msm_clocks_7x30, msm_num_clocks_7x30); } -static void __init msm7x30_fixup(struct machine_desc *desc, struct tag *tags, - char **cmdline, struct meminfo *mi) -{ - mi->nr_banks = 1; - mi->bank[0].start = PHYS_OFFSET; - mi->bank[0].size = (51*1024*1024); -} - extern void __init msm7x30_allocate_fbmem(void); @@ -347,7 +339,6 @@ MACHINE_START(MSM7X30_SURF, "QCT MSM7X30 SURF") #ifdef CONFIG_MSM_DEBUG_UART #endif .boot_params = PHYS_OFFSET + 0x100, - .fixup = msm7x30_fixup, .map_io = msm7x30_map_io, .init_irq = msm7x30_init_irq, .init_machine = msm7x30_init, From 16c4e005db959d54777148083d2aed0e07d85645 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 13 Jan 2011 10:23:35 -0800 Subject: [PATCH 0786/2556] ARM: msm: board-msm7x30: add pmem region, reserve mem at boot Change-Id: If705890a70601aec543d553ea4a3d19ac7bc9bba Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 53 ++++++++++++++++++++++++++++++- arch/arm/mach-msm/board-msm7x30.h | 2 ++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 9c6f740ba2cae..698b2978b1c87 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include @@ -164,6 +166,36 @@ static struct platform_device usb_mass_storage_device = { }, }; +static struct android_pmem_platform_data pmem_pdata = { + .name = "pmem", + .size = MSM7X30_SURF_PMEM_SIZE, + .no_allocator = 0, + .cached = 1, +}; + +static struct android_pmem_platform_data pmem_adsp_pdata = { + .name = "pmem_adsp", + .size = MSM7X30_SURF_PMEM_ADSP_SIZE, + .no_allocator = 0, + .cached = 0, +}; + +static struct platform_device android_pmem_device = { + .name = "android_pmem", + .id = 0, + .dev = { + .platform_data = &pmem_pdata + }, +}; + +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 1, + .dev = { + .platform_data = &pmem_adsp_pdata, + }, +}; + static struct platform_device *devices[] __initdata = { #if defined(CONFIG_SERIAL_MSM) && !defined(CONFIG_MSM_SERIAL_DEBUGGER) &msm_device_uart2, @@ -174,6 +206,8 @@ static struct platform_device *devices[] __initdata = { &msm_device_hsusb, &usb_mass_storage_device, &android_usb_device, + &android_pmem_device, + &android_pmem_adsp_device, }; static void __init msm7x30_init_irq(void) @@ -327,12 +361,29 @@ static void __init msm7x30_map_io(void) msm_clock_init(msm_clocks_7x30, msm_num_clocks_7x30); } - extern void __init msm7x30_allocate_fbmem(void); +static phys_addr_t _reserve_mem(const char *name, unsigned long size, + unsigned long align) +{ + unsigned long base; + + size = ALIGN(size, align); + base = memblock_alloc(size, align); + memblock_free(base, size); + memblock_remove(base, size); + pr_info("msm7x30_surf: reserved memory for %s @ 0x%08lx (%lu bytes)\n", + name, base, size); + return base; +} + static void __init msm7x30_reserve(void) { msm7x30_allocate_fbmem(); + + pmem_pdata.start = _reserve_mem("pmem", pmem_pdata.size, SZ_1M); + pmem_adsp_pdata.start = _reserve_mem("pmem_adsp", pmem_adsp_pdata.size, + SZ_1M); } MACHINE_START(MSM7X30_SURF, "QCT MSM7X30 SURF") diff --git a/arch/arm/mach-msm/board-msm7x30.h b/arch/arm/mach-msm/board-msm7x30.h index b5dc923ab1486..d9abf345a1240 100644 --- a/arch/arm/mach-msm/board-msm7x30.h +++ b/arch/arm/mach-msm/board-msm7x30.h @@ -26,4 +26,6 @@ #define MSM7X30_GPIO_PMIC_INT_N 27 +#define MSM7X30_SURF_PMEM_SIZE 0x02000000 +#define MSM7X30_SURF_PMEM_ADSP_SIZE 0x01800000 #endif From 99327ef905f20b352d44b6f0bd242246f0437467 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 13 Jan 2011 10:23:55 -0800 Subject: [PATCH 0787/2556] ARM: msm: board-msm7x30: add kgsl device and resources Change-Id: I067dec617e707d77981476494aa47153c52a34f9 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 35 +++++++++++++++++++++++++++++++ arch/arm/mach-msm/board-msm7x30.h | 3 +++ 2 files changed, 38 insertions(+) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 698b2978b1c87..3e7c2a1d395af 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -196,6 +196,31 @@ static struct platform_device android_pmem_adsp_device = { }, }; +static struct resource msm_kgsl_resources[] = { + { + .name = "kgsl_phys_memory", + .flags = IORESOURCE_MEM, + }, + { + .name = "kgsl_reg_memory", + .start = MSM_GPU_REG_PHYS, + .end = MSM_GPU_REG_PHYS + MSM_GPU_REG_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_GRAPHICS, + .end = INT_GRAPHICS, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_kgsl_device = { + .name = "kgsl", + .id = -1, + .resource = msm_kgsl_resources, + .num_resources = ARRAY_SIZE(msm_kgsl_resources), +}; + static struct platform_device *devices[] __initdata = { #if defined(CONFIG_SERIAL_MSM) && !defined(CONFIG_MSM_SERIAL_DEBUGGER) &msm_device_uart2, @@ -208,6 +233,7 @@ static struct platform_device *devices[] __initdata = { &android_usb_device, &android_pmem_device, &android_pmem_adsp_device, + &msm_kgsl_device, }; static void __init msm7x30_init_irq(void) @@ -379,11 +405,20 @@ static phys_addr_t _reserve_mem(const char *name, unsigned long size, static void __init msm7x30_reserve(void) { + struct resource *mem_res; + msm7x30_allocate_fbmem(); pmem_pdata.start = _reserve_mem("pmem", pmem_pdata.size, SZ_1M); pmem_adsp_pdata.start = _reserve_mem("pmem_adsp", pmem_adsp_pdata.size, SZ_1M); + + mem_res = platform_get_resource_byname(&msm_kgsl_device, IORESOURCE_MEM, + "kgsl_phys_memory"); + BUG_ON(!mem_res); + mem_res->start = _reserve_mem("gpu_mem", MSM7X30_SURF_GPU_MEM_SIZE, + SZ_1M); + mem_res->end = mem_res->start + MSM7X30_SURF_GPU_MEM_SIZE - 1; } MACHINE_START(MSM7X30_SURF, "QCT MSM7X30 SURF") diff --git a/arch/arm/mach-msm/board-msm7x30.h b/arch/arm/mach-msm/board-msm7x30.h index d9abf345a1240..a313e15897838 100644 --- a/arch/arm/mach-msm/board-msm7x30.h +++ b/arch/arm/mach-msm/board-msm7x30.h @@ -28,4 +28,7 @@ #define MSM7X30_SURF_PMEM_SIZE 0x02000000 #define MSM7X30_SURF_PMEM_ADSP_SIZE 0x01800000 + +#define MSM7X30_SURF_GPU_MEM_SIZE 0x00500000 + #endif From 2f4ce0f15dd64452ca8a214513c52fb6e3173e6d Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 11 May 2010 23:05:03 -0700 Subject: [PATCH 0788/2556] ARM: video: msm: HACK: mdp4_mddi: use the overlays to support 32bit fb Change-Id: If3fb56ca9b969b1d5742993c3dc2e91aeb074f40 Signed-off-by: Dima Zavin --- drivers/video/msm/mdp_hw.h | 25 ++++++++++++-- drivers/video/msm/mdp_hw40.c | 64 +++++++++++++++++++++++++++--------- 2 files changed, 71 insertions(+), 18 deletions(-) diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h index ba29454e8b6a8..f2af51ae72241 100644 --- a/drivers/video/msm/mdp_hw.h +++ b/drivers/video/msm/mdp_hw.h @@ -139,9 +139,28 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #endif #if defined(CONFIG_MSM_MDP40) -#define MDP_LAYERMIXER_IN_CFG (0x10100) -#define MDP_OVERLAYPROC0_CFG (0x10004) -#define MDP_OVERLAYPROC1_CFG (0x18004) +#define MDP_LAYERMIXER_IN_CFG (0x10100) +#define MDP_OVERLAYPROC_START(x) (0x00004 + ((x) * 0x4)) +#define MDP_OVERLAYPROC_CFG(x) (0x10004 + ((x) * 0x8000)) +#define MDP_OVERLAYPROC_OUT_SIZE(x) (0x10008 + ((x) * 0x8000)) +#define MDP_OVERLAYPROC_FB_ADDR(x) (0x1000c + ((x) * 0x8000)) +#define MDP_OVERLAYPROC_FB_ADDR2(x) (0x1001c + ((x) * 0x8000)) +#define MDP_OVERLAYPROC_FB_Y_STRIDE(x) (0x10010 + ((x) * 0x8000)) +#define MDP_OVERLAYPROC_OPMODE(x) (0x10014 + ((x) * 0x8000)) + +#define MDP_PIPE_RGB_SRC_SIZE(x) (0x40000 + ((x) * 0x10000)) +#define MDP_PIPE_RGB_SRC_XY(x) (0x40004 + ((x) * 0x10000)) +#define MDP_PIPE_RGB_OUT_SIZE(x) (0x40008 + ((x) * 0x10000)) +#define MDP_PIPE_RGB_OUT_XY(x) (0x4000c + ((x) * 0x10000)) +#define MDP_PIPE_RGB_SRC_ADDR(x) (0x40010 + ((x) * 0x10000)) +#define MDP_PIPE_RGB_SRC_Y_STRIDE(x) (0x40040 + ((x) * 0x10000)) +#define MDP_PIPE_RGB_SRC_FORMAT(x) (0x40050 + ((x) * 0x10000)) +#define MDP_PIPE_RGB_SRC_UNPACK_PATTERN(x) (0x40054 + ((x) * 0x10000)) +#define MDP_PIPE_RGB_OP_MODE(x) (0x40058 + ((x) * 0x10000)) +#define MDP_PIPE_RGB_SCALE_PHASEX_STEP(x) (0x4005c + ((x) * 0x10000)) +#define MDP_PIPE_RGB_SCALE_PHASEY_STEP(x) (0x40060 + ((x) * 0x10000)) + +#define MDP_PIPE_RGB_FETCH_CFG(x) (0x41004 + ((x) * 0x10000)) #endif #define MDP_CGC_EN (0x00100) diff --git a/drivers/video/msm/mdp_hw40.c b/drivers/video/msm/mdp_hw40.c index a642c9bcf7188..a259728e3e460 100644 --- a/drivers/video/msm/mdp_hw40.c +++ b/drivers/video/msm/mdp_hw40.c @@ -27,7 +27,39 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, struct mdp_info *mdp = priv; uint32_t dma2_cfg; uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */ - + uint32_t fmt, pattern; + + /* configure source, base layer */ + mdp_writel(mdp, ((height << 16) | (width)), MDP_PIPE_RGB_SRC_SIZE(0)); + mdp_writel(mdp, 0, MDP_PIPE_RGB_SRC_XY(0)); + mdp_writel(mdp, addr, MDP_PIPE_RGB_SRC_ADDR(0)); + mdp_writel(mdp, stride, MDP_PIPE_RGB_SRC_Y_STRIDE(0)); + + switch (mdp->dma_format) { + case DMA_IBUF_FORMAT_XRGB8888: + fmt = PPP_CFG_MDP_XRGB_8888(SRC); + pattern = PPP_PACK_PATTERN_MDP_BGRA_8888; + break; + case DMA_IBUF_FORMAT_RGB565: + fmt = PPP_CFG_MDP_RGB_565(SRC); + pattern = PPP_PACK_PATTERN_MDP_RGB_565; + break; + default: + BUG(); + break; + } + + mdp_writel(mdp, fmt, MDP_PIPE_RGB_SRC_FORMAT(0)); + mdp_writel(mdp, pattern, MDP_PIPE_RGB_SRC_UNPACK_PATTERN(0)); + + /* configure destination */ + /* setup size, address, and stride in the overlay engine */ + mdp_writel(mdp, 1, MDP_OVERLAYPROC_CFG(0)); + mdp_writel(mdp, (height << 16) | (width), MDP_OVERLAYPROC_OUT_SIZE(0)); + mdp_writel(mdp, addr, MDP_OVERLAYPROC_FB_ADDR(0)); + mdp_writel(mdp, stride, MDP_OVERLAYPROC_FB_Y_STRIDE(0)); + + /* output i/f config is in dma_p */ dma2_cfg = DMA_PACK_TIGHT | DMA_PACK_ALIGN_LSB; @@ -38,11 +70,6 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, /* 666 18BPP */ dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; - /* setup size, address, and stride */ - mdp_writel(mdp, (height << 16) | (width), MDP_DMA_P_SIZE); - mdp_writel(mdp, addr, MDP_DMA_P_IBUF_ADDR); - mdp_writel(mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE); - /* set y & x offset and MDDI transaction parameters */ mdp_writel(mdp, (y << 16) | (x), MDP_DMA_P_OUT_XY); mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL); @@ -51,7 +78,9 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, mdp_writel(mdp, 0x1, MDP_MDDI_DATA_XFR); mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG); - mdp_writel(mdp, 0, MDP_DMA_P_START); + + /* start the overlay fetch */ + mdp_writel(mdp, 0, MDP_OVERLAYPROC_START(0)); } int mdp_hw_init(struct mdp_info *mdp) @@ -71,24 +100,29 @@ int mdp_hw_init(struct mdp_info *mdp) clk_set_rate(mdp->clk, 122880000); /* 122.88 Mhz */ pr_info("%s: mdp_clk=%lu\n", __func__, clk_get_rate(mdp->clk)); - /* TODO: Configure the VG/RGB pipes fetch data */ - /* this should work for any mdp_clk freq. * TODO: use different value for mdp_clk freqs >= 90Mhz */ - mdp_writel(mdp, 0x27, MDP_DMA_P_FETCH_CFG); /* 8 bytes-burst x 8 req */ + /* 8 bytes-burst x 8 req */ + mdp_writel(mdp, 0x27, MDP_DMA_P_FETCH_CFG); + /* 16 bytes-burst x 4 req */ + /* TODO: do same for vg pipes */ + mdp_writel(mdp, 0xc3, MDP_PIPE_RGB_FETCH_CFG(0)); + mdp_writel(mdp, 0xc3, MDP_PIPE_RGB_FETCH_CFG(1)); mdp_writel(mdp, 0x3, MDP_EBI2_PORTMAP_MODE); /* 3 pending requests */ mdp_writel(mdp, 0x02222, MDP_MAX_RD_PENDING_CMD_CONFIG); - /* no overlay processing, sw controls everything */ - mdp_writel(mdp, 0, MDP_LAYERMIXER_IN_CFG); - mdp_writel(mdp, 1 << 3, MDP_OVERLAYPROC0_CFG); - mdp_writel(mdp, 1 << 3, MDP_OVERLAYPROC1_CFG); - /* XXX: HACK! hardcode to do mddi on primary */ mdp_writel(mdp, 0x2, MDP_DISP_INTF_SEL); + + /* RGB1 -> Layer 0 base */ + mdp_writel(mdp, 1 << 8, MDP_LAYERMIXER_IN_CFG); + + mdp_writel(mdp, 1, MDP_OVERLAYPROC_CFG(0)); + mdp_writel(mdp, 0, MDP_OVERLAYPROC_CFG(1)); + return 0; } From cb36857ebbe359a49c2439610613a35386ded489 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 13 Jan 2011 11:43:58 -0800 Subject: [PATCH 0789/2556] ARM: msm: board-msm7x30: add the full surf keymap, taken from qct Change-Id: I6059b0dbd9c1ca3697aa1daabf5a6107e548d9a9 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 106 ++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 3e7c2a1d395af..70eb35912e515 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -294,7 +294,113 @@ static int msm7x30_pmic_keypad_init(struct device *dev) } static const unsigned short msm7x30_pmic_keymap[KEYMAP_SIZE] = { + [KEYMAP_INDEX(0, 0)] = KEY_7, + [KEYMAP_INDEX(0, 1)] = KEY_DOWN, + [KEYMAP_INDEX(0, 2)] = KEY_UP, + [KEYMAP_INDEX(0, 3)] = KEY_RIGHT, + [KEYMAP_INDEX(0, 4)] = KEY_ENTER, + [KEYMAP_INDEX(0, 5)] = KEY_L, [KEYMAP_INDEX(0, 6)] = KEY_BACK, + [KEYMAP_INDEX(0, 7)] = KEY_M, + + [KEYMAP_INDEX(1, 0)] = KEY_LEFT, + [KEYMAP_INDEX(1, 1)] = KEY_SEND, + [KEYMAP_INDEX(1, 2)] = KEY_1, + [KEYMAP_INDEX(1, 3)] = KEY_4, + [KEYMAP_INDEX(1, 4)] = KEY_CLEAR, + [KEYMAP_INDEX(1, 5)] = KEY_MSDOS, + [KEYMAP_INDEX(1, 6)] = KEY_SPACE, + [KEYMAP_INDEX(1, 7)] = KEY_COMMA, + + [KEYMAP_INDEX(2, 0)] = KEY_6, + [KEYMAP_INDEX(2, 1)] = KEY_5, + [KEYMAP_INDEX(2, 2)] = KEY_8, + [KEYMAP_INDEX(2, 3)] = KEY_3, + [KEYMAP_INDEX(2, 4)] = KEY_NUMERIC_STAR, + [KEYMAP_INDEX(2, 5)] = KEY_UP, + [KEYMAP_INDEX(2, 6)] = KEY_DOWN, + [KEYMAP_INDEX(2, 7)] = KEY_LEFTSHIFT, + + [KEYMAP_INDEX(3, 0)] = KEY_9, + [KEYMAP_INDEX(3, 1)] = KEY_NUMERIC_POUND, + [KEYMAP_INDEX(3, 2)] = KEY_0, + [KEYMAP_INDEX(3, 3)] = KEY_2, + [KEYMAP_INDEX(3, 4)] = KEY_SLEEP, + [KEYMAP_INDEX(3, 5)] = KEY_F1, + [KEYMAP_INDEX(3, 6)] = KEY_F2, + [KEYMAP_INDEX(3, 7)] = KEY_F3, + + [KEYMAP_INDEX(4, 0)] = KEY_BACK, + [KEYMAP_INDEX(4, 1)] = KEY_HOME, + [KEYMAP_INDEX(4, 2)] = KEY_MENU, + [KEYMAP_INDEX(4, 3)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(4, 4)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(4, 5)] = KEY_F4, + [KEYMAP_INDEX(4, 6)] = KEY_F5, + [KEYMAP_INDEX(4, 7)] = KEY_F6, + + [KEYMAP_INDEX(5, 0)] = KEY_R, + [KEYMAP_INDEX(5, 1)] = KEY_T, + [KEYMAP_INDEX(5, 2)] = KEY_Y, + [KEYMAP_INDEX(5, 3)] = KEY_LEFTALT, + [KEYMAP_INDEX(5, 4)] = KEY_KPENTER, + [KEYMAP_INDEX(5, 5)] = KEY_Q, + [KEYMAP_INDEX(5, 6)] = KEY_W, + [KEYMAP_INDEX(5, 7)] = KEY_E, + + [KEYMAP_INDEX(6, 0)] = KEY_F, + [KEYMAP_INDEX(6, 1)] = KEY_G, + [KEYMAP_INDEX(6, 2)] = KEY_H, + [KEYMAP_INDEX(6, 3)] = KEY_CAPSLOCK, + [KEYMAP_INDEX(6, 4)] = KEY_PAGEUP, + [KEYMAP_INDEX(6, 5)] = KEY_A, + [KEYMAP_INDEX(6, 6)] = KEY_S, + [KEYMAP_INDEX(6, 7)] = KEY_D, + + [KEYMAP_INDEX(7, 0)] = KEY_V, + [KEYMAP_INDEX(7, 1)] = KEY_B, + [KEYMAP_INDEX(7, 2)] = KEY_N, + [KEYMAP_INDEX(7, 3)] = KEY_MENU, + [KEYMAP_INDEX(7, 4)] = KEY_PAGEDOWN, + [KEYMAP_INDEX(7, 5)] = KEY_Z, + [KEYMAP_INDEX(7, 6)] = KEY_X, + [KEYMAP_INDEX(7, 7)] = KEY_C, + + [KEYMAP_INDEX(8, 0)] = KEY_P, + [KEYMAP_INDEX(8, 1)] = KEY_J, + [KEYMAP_INDEX(8, 2)] = KEY_K, + [KEYMAP_INDEX(8, 3)] = KEY_INSERT, + [KEYMAP_INDEX(8, 4)] = KEY_LINEFEED, + [KEYMAP_INDEX(8, 5)] = KEY_U, + [KEYMAP_INDEX(8, 6)] = KEY_I, + [KEYMAP_INDEX(8, 7)] = KEY_O, + + [KEYMAP_INDEX(9, 0)] = KEY_4, + [KEYMAP_INDEX(9, 1)] = KEY_5, + [KEYMAP_INDEX(9, 2)] = KEY_6, + [KEYMAP_INDEX(9, 3)] = KEY_7, + [KEYMAP_INDEX(9, 4)] = KEY_8, + [KEYMAP_INDEX(9, 5)] = KEY_1, + [KEYMAP_INDEX(9, 6)] = KEY_2, + [KEYMAP_INDEX(9, 7)] = KEY_3, + + [KEYMAP_INDEX(10, 0)] = KEY_F7, + [KEYMAP_INDEX(10, 1)] = KEY_F8, + [KEYMAP_INDEX(10, 2)] = KEY_F9, + [KEYMAP_INDEX(10, 3)] = KEY_F10, + [KEYMAP_INDEX(10, 4)] = KEY_FN, + [KEYMAP_INDEX(10, 5)] = KEY_9, + [KEYMAP_INDEX(10, 6)] = KEY_0, + [KEYMAP_INDEX(10, 7)] = KEY_DOT, + + [KEYMAP_INDEX(11, 0)] = KEY_LEFTCTRL, + [KEYMAP_INDEX(11, 1)] = KEY_F11, + [KEYMAP_INDEX(11, 2)] = KEY_ENTER, + [KEYMAP_INDEX(11, 3)] = KEY_SEARCH, + [KEYMAP_INDEX(11, 4)] = KEY_DELETE, + [KEYMAP_INDEX(11, 5)] = KEY_RIGHT, + [KEYMAP_INDEX(11, 6)] = KEY_LEFT, + [KEYMAP_INDEX(11, 7)] = KEY_RIGHTSHIFT, }; static struct pm8058_keypad_platform_data msm7x30_pmic_keypad_pdata = { From 8f20a5ec21654bb249bbbe6ceec56b111c8ab0c1 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 14 Jan 2011 13:29:27 -0800 Subject: [PATCH 0790/2556] ARM: msm: add fluid definition and force select surf Change-Id: I62df0cf08539cb9af574f4ac7653d7412442bec5 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Kconfig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index afb27da08a4fa..9088f294a3e09 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -150,6 +150,13 @@ config MACH_MSM7X30_SURF help Support for the Qualcomm MSM7x30 SURF eval board. +config MACH_MSM7X30_FLUID + depends on ARCH_MSM7X30 + select MACH_MSM7X30_SURF + bool "MSM7x30 FLUID" + help + Support for the Qualcomm MSM7x30 FLUID eval board. + config MACH_SWORDFISH depends on ARCH_QSD8X50 default y From 9392da8ad4d6615224e81d606fd122a8cd2f46cd Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 14 Jan 2011 17:52:01 -0800 Subject: [PATCH 0791/2556] spi: spi_qsd: provide mode_bits to have spi_core reject bad modes Change-Id: I78b45ad3be14bbb20bfdc7211bcda78eb2254a7d Signed-off-by: Dima Zavin --- drivers/spi/spi_qsd.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c index 1392d29841182..c6460a1205b11 100644 --- a/drivers/spi/spi_qsd.c +++ b/drivers/spi/spi_qsd.c @@ -953,20 +953,11 @@ static int msm_spi_setup(struct spi_device *spi) u32 spi_config; u32 mask; - if (!spi->bits_per_word) - spi->bits_per_word = 8; if (spi->bits_per_word < 4 || spi->bits_per_word > 32) { dev_err(&spi->dev, "%s: invalid bits_per_word %d\n", __func__, spi->bits_per_word); rc = -EINVAL; } - if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP)) { - dev_err(&spi->dev, "%s, unsupported mode bits %x\n", - __func__, - spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_CS_HIGH - | SPI_LOOP)); - rc = -EINVAL; - } if (spi->chip_select > SPI_NUM_CHIPSELECTS-1) { dev_err(&spi->dev, "%s, chip select %d exceeds max value %d\n", __func__, spi->chip_select, SPI_NUM_CHIPSELECTS - 1); @@ -1302,6 +1293,8 @@ static int __init msm_spi_probe(struct platform_device *pdev) goto err_probe_exit; } + /* the spi->mode bits understood by this driver: */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; master->bus_num = pdev->id; master->num_chipselect = SPI_NUM_CHIPSELECTS; master->setup = msm_spi_setup; From a4ce931f61110e2bb1ac0ce8212be9b17c91ef9b Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 19 Jan 2011 15:57:55 -0800 Subject: [PATCH 0792/2556] video: msm: lcdc: manage mdp_pclk if available Change-Id: I0b35a5bc9be4905229890b2b84c1f656ca82d6e4 Signed-off-by: Dima Zavin --- drivers/video/msm/mdp_lcdc.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/video/msm/mdp_lcdc.c b/drivers/video/msm/mdp_lcdc.c index 9590bc279b8ac..837681c1ccb64 100644 --- a/drivers/video/msm/mdp_lcdc.c +++ b/drivers/video/msm/mdp_lcdc.c @@ -35,6 +35,7 @@ struct mdp_lcdc_info { struct mdp_info *mdp; struct clk *mdp_clk; + struct clk *mdp_pclk; struct clk *pclk; struct clk *pad_pclk; struct msm_panel_data fb_panel_data; @@ -94,6 +95,8 @@ static int lcdc_suspend(struct msm_panel_data *fb_panel) mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); clk_disable(lcdc->pad_pclk); clk_disable(lcdc->pclk); + if (lcdc->mdp_pclk) + clk_disable(lcdc->mdp_pclk); clk_disable(lcdc->mdp_clk); return 0; @@ -106,6 +109,8 @@ static int lcdc_resume(struct msm_panel_data *fb_panel) pr_info("%s: resuming\n", __func__); clk_enable(lcdc->mdp_clk); + if (lcdc->mdp_pclk) + clk_enable(lcdc->mdp_pclk); clk_enable(lcdc->pclk); clk_enable(lcdc->pad_pclk); mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); @@ -119,6 +124,8 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) uint32_t dma_cfg; clk_enable(lcdc->mdp_clk); + if (lcdc->mdp_pclk) + clk_enable(lcdc->mdp_pclk); clk_enable(lcdc->pclk); clk_enable(lcdc->pad_pclk); @@ -308,6 +315,10 @@ static int mdp_lcdc_probe(struct platform_device *pdev) goto err_get_mdp_clk; } + lcdc->mdp_pclk = clk_get(mdp_dev->dev.parent, "mdp_pclk"); + if (IS_ERR(lcdc->mdp_pclk)) + lcdc->mdp_pclk = NULL; + lcdc->pclk = clk_get(mdp_dev->dev.parent, "lcdc_pclk_clk"); if (IS_ERR(lcdc->pclk)) { pr_err("%s: failed to get lcdc_pclk\n", __func__); @@ -378,6 +389,8 @@ static int mdp_lcdc_probe(struct platform_device *pdev) err_get_pad_pclk: clk_put(lcdc->pclk); err_get_pclk: + if (lcdc->mdp_pclk) + clk_put(lcdc->mdp_pclk); clk_put(lcdc->mdp_clk); err_get_mdp_clk: kfree(lcdc); From c937d4300455b9d0a6a49e6dd76ab4fdb8d0b9a6 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 19 Jan 2011 16:05:02 -0800 Subject: [PATCH 0793/2556] video: msm: fix LCDC support for MDP4.0 in msm7x30 Change-Id: If691dcf206a3579f24c1608710ca1532045091b3 Signed-off-by: Dima Zavin --- drivers/video/msm/Kconfig | 4 +- drivers/video/msm/mdp_hw.h | 20 +++++++++- drivers/video/msm/mdp_hw40.c | 14 ++++--- drivers/video/msm/mdp_lcdc.c | 74 ++++++++++++++++++++++++++++++++---- 4 files changed, 95 insertions(+), 17 deletions(-) diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig index fe5ee262d3a71..fefc8a9b5c38a 100644 --- a/drivers/video/msm/Kconfig +++ b/drivers/video/msm/Kconfig @@ -17,8 +17,8 @@ config FB_MSM_MDP_PPP default y config FB_MSM_LCDC - bool "Support for integrated LCD controller in qsd8x50" - depends on FB_MSM && MSM_MDP31 + bool "Support for integrated LCD controller in MDP3/4" + depends on FB_MSM && (MSM_MDP31 || MSM_MDP40) default y config FB_MSM_MDDI diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h index f2af51ae72241..1bd670b8691af 100644 --- a/drivers/video/msm/mdp_hw.h +++ b/drivers/video/msm/mdp_hw.h @@ -140,6 +140,7 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #if defined(CONFIG_MSM_MDP40) #define MDP_LAYERMIXER_IN_CFG (0x10100) +#define MDP_OVERLAY_REG_FLUSH (0x18000) #define MDP_OVERLAYPROC_START(x) (0x00004 + ((x) * 0x4)) #define MDP_OVERLAYPROC_CFG(x) (0x10004 + ((x) * 0x8000)) #define MDP_OVERLAYPROC_OUT_SIZE(x) (0x10008 + ((x) * 0x8000)) @@ -163,7 +164,12 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #define MDP_PIPE_RGB_FETCH_CFG(x) (0x41004 + ((x) * 0x10000)) #endif +#if defined(CONFIG_MSM_MDP40) +#define MDP_CGC_EN (0x00040) +#else #define MDP_CGC_EN (0x00100) +#endif + #define MDP_CMD_STATUS (0x10008) #define MDP_PROFILE_EN (0x10010) #define MDP_PROFILE_COUNT (0x10014) @@ -331,7 +337,10 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #define TV_ENC_UNDERRUN (1<<7) #if defined(CONFIG_MSM_MDP40) +#define MDP_OVERLAYPROC0_DONE (1 << 0) +#define MDP_OVERLAYPROC1_DONE (1 << 1) #define MDP_DMA_P_DONE (1 << 4) +#define MDP_LCDC_FRAME_START (1 << 7) #elif defined(CONFIG_MSM_MDP22) #define MDP_DMA_P_DONE (1 << 2) #elif defined(CONFIG_MSM_MDP31) @@ -790,6 +799,7 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #define DMA_PACK_LOOSE 0 #define DMA_PACK_ALIGN_LSB 0 #define DMA_PACK_ALIGN_MSB (1<<7) +#define DMA_PACK_ALIGN_MASK (1<<7) #define DMA_PACK_PATTERN_MASK (0x3f<<8) #define DMA_PACK_PATTERN_RGB \ (MDP_GET_PACK_PATTERN(0, CLR_R, CLR_G, CLR_B, 2)<<8) @@ -813,12 +823,20 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #define DMA_IBUF_FORMAT_MASK (1 << 20) #define DMA_IBUF_NONCONTIGUOUS (1<<21) -#else /* CONFIG_MSM_MDP31 */ +#else /* CONFIG_MSM_MDP31 || CONFIG_MSM_MDP40 */ +#ifdef CONFIG_MSM_MDP40 +#define DMA_OUT_SEL_AHB (0) +#define DMA_OUT_SEL_MDDI (0) +#define DMA_OUT_SEL_LCDC (0) +#define DMA_OUT_SEL_LCDC_MDDI (0) +#else #define DMA_OUT_SEL_AHB (0 << 19) #define DMA_OUT_SEL_MDDI (1 << 19) #define DMA_OUT_SEL_LCDC (2 << 19) #define DMA_OUT_SEL_LCDC_MDDI (3 << 19) +#endif + #define DMA_DITHER_EN (1 << 24) #define DMA_IBUF_FORMAT_RGB888 (0 << 25) #define DMA_IBUF_FORMAT_RGB565 (1 << 25) diff --git a/drivers/video/msm/mdp_hw40.c b/drivers/video/msm/mdp_hw40.c index a259728e3e460..769b8bc3d7ba8 100644 --- a/drivers/video/msm/mdp_hw40.c +++ b/drivers/video/msm/mdp_hw40.c @@ -29,6 +29,9 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */ uint32_t fmt, pattern; + /* XXX: HACK! hardcode to do mddi on primary */ + mdp_writel(mdp, 0x2, MDP_DISP_INTF_SEL); + /* configure source, base layer */ mdp_writel(mdp, ((height << 16) | (width)), MDP_PIPE_RGB_SRC_SIZE(0)); mdp_writel(mdp, 0, MDP_PIPE_RGB_SRC_XY(0)); @@ -54,14 +57,12 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, /* configure destination */ /* setup size, address, and stride in the overlay engine */ - mdp_writel(mdp, 1, MDP_OVERLAYPROC_CFG(0)); mdp_writel(mdp, (height << 16) | (width), MDP_OVERLAYPROC_OUT_SIZE(0)); mdp_writel(mdp, addr, MDP_OVERLAYPROC_FB_ADDR(0)); mdp_writel(mdp, stride, MDP_OVERLAYPROC_FB_Y_STRIDE(0)); /* output i/f config is in dma_p */ - dma2_cfg = DMA_PACK_TIGHT | - DMA_PACK_ALIGN_LSB; + dma2_cfg = DMA_PACK_ALIGN_LSB; dma2_cfg |= mdp->dma_format; dma2_cfg |= mdp->dma_pack_pattern; @@ -94,6 +95,8 @@ int mdp_hw_init(struct mdp_info *mdp) mdp_writel(mdp, 0, MDP_INTR_ENABLE); mdp_writel(mdp, 0, MDP_DMA_P_HIST_INTR_ENABLE); + mdp_writel(mdp, 0, MDP_LCDC_EN); + mdp_writel(mdp, 0xffffffff, MDP_CGC_EN); /* XXX: why set this? QCT says it should be > mdp_pclk, * but they never set the clkrate of pclk */ @@ -114,15 +117,14 @@ int mdp_hw_init(struct mdp_info *mdp) /* 3 pending requests */ mdp_writel(mdp, 0x02222, MDP_MAX_RD_PENDING_CMD_CONFIG); - /* XXX: HACK! hardcode to do mddi on primary */ - mdp_writel(mdp, 0x2, MDP_DISP_INTF_SEL); - /* RGB1 -> Layer 0 base */ mdp_writel(mdp, 1 << 8, MDP_LAYERMIXER_IN_CFG); mdp_writel(mdp, 1, MDP_OVERLAYPROC_CFG(0)); mdp_writel(mdp, 0, MDP_OVERLAYPROC_CFG(1)); + mdp_writel(mdp, 0, MDP_OVERLAYPROC_OPMODE(0)); + return 0; } diff --git a/drivers/video/msm/mdp_lcdc.c b/drivers/video/msm/mdp_lcdc.c index 837681c1ccb64..119ceb4f417cc 100644 --- a/drivers/video/msm/mdp_lcdc.c +++ b/drivers/video/msm/mdp_lcdc.c @@ -122,6 +122,7 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) { struct msm_panel_data *fb_panel = &lcdc->fb_panel_data; uint32_t dma_cfg; + uint32_t fb_size; clk_enable(lcdc->mdp_clk); if (lcdc->mdp_pclk) @@ -144,24 +145,31 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW); mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR); - mdp_writel(lcdc->mdp, 0xff, MDP_LCDC_UNDERFLOW_CTL); + mdp_writel(lcdc->mdp, 0x80000000 | 0xff, MDP_LCDC_UNDERFLOW_CTL); mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL); mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START); mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END); mdp_writel(lcdc->mdp, lcdc->parms.polarity, MDP_LCDC_CTL_POLARITY); + fb_size = ((fb_panel->fb_data->yres & 0x7ff) << 16) | + (fb_panel->fb_data->xres & 0x7ff); + /* config the dma_p block that drives the lcdc data */ mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_DMA_P_IBUF_ADDR); - mdp_writel(lcdc->mdp, (((fb_panel->fb_data->yres & 0x7ff) << 16) | - (fb_panel->fb_data->xres & 0x7ff)), - MDP_DMA_P_SIZE); - + mdp_writel(lcdc->mdp, fb_size, MDP_DMA_P_SIZE); mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY); +#ifdef CONFIG_MSM_MDP40 + mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_PIPE_RGB_SRC_ADDR(0)); + mdp_writel(lcdc->mdp, fb_size, MDP_PIPE_RGB_SRC_SIZE(0)); + mdp_writel(lcdc->mdp, 0, MDP_PIPE_RGB_SRC_XY(0)); +#endif + dma_cfg = mdp_readl(lcdc->mdp, MDP_DMA_P_CONFIG); - dma_cfg |= (DMA_PACK_ALIGN_LSB | - DMA_PACK_PATTERN_RGB | - DMA_DITHER_EN); + dma_cfg &= ~(DMA_PACK_PATTERN_MASK | DMA_PACK_ALIGN_MASK); + dma_cfg |= (DMA_PACK_ALIGN_MSB | + DMA_PACK_PATTERN_RGB | + DMA_DITHER_EN); dma_cfg |= DMA_OUT_SEL_LCDC; dma_cfg &= ~DMA_DST_BITS_MASK; @@ -180,6 +188,10 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG); +#ifdef CONFIG_MSM_MDP40 + mdp_writel(lcdc->mdp, 0, MDP_DISP_INTF_SEL); +#endif + /* enable the lcdc timing generation */ mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); @@ -234,15 +246,61 @@ static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride, uint32_t y) { struct mdp_lcdc_info *lcdc = priv; + struct mdp_info *mdp = lcdc->mdp; + uint32_t dma2_cfg; +#ifdef CONFIG_MSM_MDP31 if (lcdc->mdp->dma_format_dirty) { mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); mdelay(20); mdp_configure_dma_format(mdp_dev); mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); } + mdp_writel(lcdc->mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE); mdp_writel(lcdc->mdp, addr, MDP_DMA_P_IBUF_ADDR); +#else + if (lcdc->mdp->dma_format_dirty) { + uint32_t fmt; + uint32_t dma_fmt; + uint32_t dma_ptrn; + uint32_t pattern; + + switch (mdp->dma_format) { + case DMA_IBUF_FORMAT_XRGB8888: + dma_fmt = DMA_IBUF_FORMAT_RGB888; + dma_ptrn = DMA_PACK_PATTERN_BGR; + fmt = PPP_CFG_MDP_XRGB_8888(SRC); + pattern = PPP_PACK_PATTERN_MDP_BGRA_8888; + break; + case DMA_IBUF_FORMAT_RGB565: + dma_fmt = DMA_IBUF_FORMAT_RGB565; + dma_ptrn = DMA_PACK_PATTERN_RGB; + fmt = PPP_CFG_MDP_RGB_565(SRC); + pattern = PPP_PACK_PATTERN_MDP_RGB_565; + break; + default: + BUG(); + break; + } + + mdp_writel(mdp, fmt, MDP_PIPE_RGB_SRC_FORMAT(0)); + mdp_writel(mdp, pattern, MDP_PIPE_RGB_SRC_UNPACK_PATTERN(0)); + + dma2_cfg = mdp_readl(lcdc->mdp, MDP_DMA_P_CONFIG); + dma2_cfg &= ~(DMA_PACK_PATTERN_MASK | DMA_IBUF_FORMAT_MASK | + DMA_PACK_ALIGN_MASK); + dma2_cfg |= dma_ptrn | dma_fmt | DMA_PACK_ALIGN_MSB; + mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG); + lcdc->mdp->dma_format_dirty = false; + } + + mdp_writel(mdp, addr, MDP_PIPE_RGB_SRC_ADDR(0)); + mdp_writel(mdp, stride, MDP_PIPE_RGB_SRC_Y_STRIDE(0)); + + /* flush the new pipe config */ + mdp_writel(mdp, 0x11, MDP_OVERLAY_REG_FLUSH); +#endif } static void precompute_timing_parms(struct mdp_lcdc_info *lcdc) From dc69b3d4a1e3d4e19a7a44a2ff6a02c4c70acc99 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 19 Jan 2011 16:06:34 -0800 Subject: [PATCH 0794/2556] ARM: msm: board-msm7x30: add SPI device Change-Id: I4bb6e69608f3b32f2bffc709011a8fe1cd6d3b8b Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 70eb35912e515..77174ebd7a701 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include "devices.h" @@ -234,6 +235,7 @@ static struct platform_device *devices[] __initdata = { &android_pmem_device, &android_pmem_adsp_device, &msm_kgsl_device, + &msm_device_spi, }; static void __init msm7x30_init_irq(void) @@ -453,6 +455,34 @@ static struct i2c_board_info surf_i2c_devices[] = { }, }; +static int msm7x30_spi_init(void) +{ + msm_gpiomux_write(45, 0, + GPIOMUX_FUNC_1 | + GPIOMUX_PULL_NONE | + GPIOMUX_DIR_INPUT | + GPIOMUX_DRV_2MA | GPIOMUX_VALID); + msm_gpiomux_write(46, 0, + GPIOMUX_FUNC_1 | + GPIOMUX_PULL_NONE | + GPIOMUX_DIR_INPUT | + GPIOMUX_DRV_2MA | GPIOMUX_VALID); + msm_gpiomux_write(47, 0, + GPIOMUX_FUNC_1 | + GPIOMUX_PULL_NONE | + GPIOMUX_DIR_INPUT | + GPIOMUX_DRV_2MA | GPIOMUX_VALID); + msm_gpiomux_write(48, 0, + GPIOMUX_FUNC_1 | + GPIOMUX_PULL_NONE | + GPIOMUX_DIR_INPUT | + GPIOMUX_DRV_2MA | GPIOMUX_VALID); +} + +static struct msm_spi_platform_data msm7x30_spi_pdata = { + .max_clock_speed = 26331429, +}; + extern void msm_serial_debug_init(unsigned int base, int irq, struct device *clk_device, int signal_irq); @@ -476,7 +506,11 @@ static void __init msm7x30_init(void) #endif msm7x30_ssbi_pmic_init(); + msm7x30_spi_init(); + msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; + msm_device_spi.dev.platform_data = &msm7x30_spi_pdata; + platform_add_devices(devices, ARRAY_SIZE(devices)); i2c_register_board_info(1, surf_i2c_devices, From a6bab0ba3f44879e1c68b6f32a79a3a733f78b39 Mon Sep 17 00:00:00 2001 From: Neil Leeder Date: Wed, 19 Jan 2011 16:51:13 -0800 Subject: [PATCH 0795/2556] ARM: msm: board-msm7x30: add fluid panel support Adapted from CodeAuroraForum version Change-Id: Id51b5b26b9724e2601d9c0bccfc263674271412e Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30-panel.c | 269 ++++++++++++++++++++++-- 1 file changed, 254 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-msm/board-msm7x30-panel.c b/arch/arm/mach-msm/board-msm7x30-panel.c index 158c65cca509a..4f40834058152 100644 --- a/arch/arm/mach-msm/board-msm7x30-panel.c +++ b/arch/arm/mach-msm/board-msm7x30-panel.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -320,9 +321,13 @@ static DEFINE_MUTEX(msm7x30_backlight_lock); static void msm7x30_set_backlight_level(uint8_t level) { - pmic_set_led_intensity(LED_LCD, - MSM7X30_SURF_DEFAULT_BACKLIGHT_BRIGHTNESS * - level / LED_FULL); + if (machine_is_msm7x30_fluid()) { + gpio_set_value(MSM7X30_PM8058_GPIO(25), !!level); + } else { + pmic_set_led_intensity(LED_LCD, + MSM7X30_SURF_DEFAULT_BACKLIGHT_BRIGHTNESS * + level / LED_FULL); + } } static void msm7x30_process_mddi_table(struct msm_mddi_client_data *client_data, @@ -362,18 +367,20 @@ static struct pm8058_pin_config msm7x30_mddi_sleep_clk_cfg_off = { .func = PM8058_GPIO_FUNC_NORMAL, }; -static void msm7x30_mddi_power_client(struct msm_mddi_client_data *mddi, int on) +static struct pm8058_pin_config msm7x30_fluid_backlight = { + .vin_src = PM8058_GPIO_VIN_SRC_VREG_S3, + .dir = PM8058_GPIO_OUTPUT, + .pull_up = PM8058_GPIO_PULL_NONE, + .strength = PM8058_GPIO_STRENGTH_HIGH, + .func = PM8058_GPIO_FUNC_NORMAL, +}; + +static void msm7x30_power_panel(int on) { - int rc = 0, flag_on = !!on; - static int display_common_power_save_on; + int rc = 0; struct vreg *vreg_ldo12, *vreg_ldo15 = NULL; struct vreg *vreg_ldo20, *vreg_ldo16, *vreg_ldo8 = NULL; - if (display_common_power_save_on == flag_on) - return; - - display_common_power_save_on = flag_on; - if (on) { // XXX enable wega reset gpio @@ -555,6 +562,11 @@ static void msm7x30_mddi_power_client(struct msm_mddi_client_data *mddi, int on) } } +static void msm7x30_mddi_power_client(struct msm_mddi_client_data *mddi, int on) +{ + msm7x30_power_panel(on); +} + static int msm7x30_mddi_toshiba_client_init( struct msm_mddi_bridge_platform_data *bridge_data, struct msm_mddi_client_data *client_data) @@ -695,6 +707,209 @@ static struct msm_mddi_platform_data mddi_pdata = { }, }; +/********************** FLUID LCDC PANEL CODE */ +struct { + u8 addr; + u8 data; +} fluid_sharp_init_tbl[] = { + { 15, 0x01 }, + { 5, 0x01 }, + { 7, 0x10 }, + { 9, 0x1E }, + { 10, 0x04 }, + { 17, 0xFF }, + { 21, 0x8A }, + { 22, 0x00 }, + { 23, 0x82 }, + { 24, 0x24 }, + { 25, 0x22 }, + { 26, 0x6D }, + { 27, 0xEB }, + { 28, 0xB9 }, + { 29, 0x3A }, + { 49, 0x1A }, + { 50, 0x16 }, + { 51, 0x05 }, + { 55, 0x7F }, + { 56, 0x15 }, + { 57, 0x7B }, + { 60, 0x05 }, + { 61, 0x0C }, + { 62, 0x80 }, + { 63, 0x00 }, + { 92, 0x90 }, + { 97, 0x01 }, + { 98, 0xFF }, + { 113, 0x11 }, + { 114, 0x02 }, + { 115, 0x08 }, + { 123, 0xAB }, + { 124, 0x04 }, + { 6, 0x02 }, + { 133, 0x00 }, + { 134, 0xFE }, + { 135, 0x22 }, + { 136, 0x0B }, + { 137, 0xFF }, + { 138, 0x0F }, + { 139, 0x00 }, + { 140, 0xFE }, + { 141, 0x22 }, + { 142, 0x0B }, + { 143, 0xFF }, + { 144, 0x0F }, + { 145, 0x00 }, + { 146, 0xFE }, + { 147, 0x22 }, + { 148, 0x0B }, + { 149, 0xFF }, + { 150, 0x0F }, + { 202, 0x30 }, + { 30, 0x01 }, + { 4, 0x01 }, + { 31, 0x41 }, +}; + +static struct spi_device *lcdc_spi_client; + +static int fluid_spi_write(u8 reg, u8 data) +{ + u8 tx_buf[2]; + int rc; + struct spi_message m; + struct spi_transfer t = { + .tx_buf = tx_buf, + }; + + if (!lcdc_spi_client) { + pr_err("%s: lcdc_spi_client is NULL\n", __func__); + return -EINVAL; + } + + spi_setup(lcdc_spi_client); + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + tx_buf[0] = reg; + tx_buf[1] = data; + t.rx_buf = NULL; + t.len = 2; + rc = spi_sync(lcdc_spi_client, &m); + return rc; +} + +int fluid_panel_blank(struct msm_lcdc_panel_ops *ops) +{ + /* TODO: Turn backlight off? */ + return 0; +} + +int fluid_panel_unblank(struct msm_lcdc_panel_ops *ops) +{ + /* TODO: Turn backlight on? */ + return 0; +} + +int fluid_panel_init(struct msm_lcdc_panel_ops *ops) +{ + int i; + + msm7x30_power_panel(true); + + for (i = 0; i < ARRAY_SIZE(fluid_sharp_init_tbl); i++) + fluid_spi_write(fluid_sharp_init_tbl[i].addr, + fluid_sharp_init_tbl[i].data); + mdelay(10); + fluid_spi_write(31, 0xC1); + mdelay(10); + fluid_spi_write(31, 0xD9); + fluid_spi_write(31, 0xDF); + + return 0; +} + +static struct msm_lcdc_timing fluid_lcdc_timing = { + .clk_rate = 24576000, + .hsync_pulse_width = 10, + .hsync_back_porch = 20, + .hsync_front_porch = 10, + .hsync_skew = 0, + .vsync_pulse_width = 2, + .vsync_back_porch = 2, + .vsync_front_porch = 2, + .vsync_act_low = 1, + .hsync_act_low = 1, + .den_act_low = 0, +}; + +static struct msm_fb_data fluid_lcdc_fb_data = { + .xres = 480, + .yres = 800, + .width = 57, + .height = 94, + .output_format = MSM_MDP_OUT_IF_FMT_RGB666, +}; + +static struct msm_lcdc_panel_ops fluid_lcdc_panel_ops = { + .init = fluid_panel_init, + .blank = fluid_panel_blank, + .unblank = fluid_panel_unblank, +}; + +static struct msm_lcdc_platform_data fluid_lcdc_platform_data = { + .panel_ops = &fluid_lcdc_panel_ops, + .timing = &fluid_lcdc_timing, + .fb_id = 0, + .fb_data = &fluid_lcdc_fb_data, + .fb_resource = resources_msm_fb, +}; + +static struct platform_device fluid_lcdc_device = { + .name = "msm_mdp_lcdc", + .id = -1, + .dev = { + .platform_data = &fluid_lcdc_platform_data, + }, +}; + +static int __devinit fluid_lcdc_sharp_spi_probe(struct spi_device *spi) +{ + int rc; + + lcdc_spi_client = spi; + lcdc_spi_client->bits_per_word = 32; + + rc = platform_device_register(&fluid_lcdc_device); + if (rc) + return rc; + return 0; +} + +static int __devexit fluid_lcdc_sharp_spi_remove(struct spi_device *spi) +{ + lcdc_spi_client = NULL; + return 0; +} + +static struct spi_driver fluid_lcdc_sharp_spi_driver = { + .driver = { + .name = "fluid_lcdc_sharp", + .owner = THIS_MODULE, + }, + .probe = fluid_lcdc_sharp_spi_probe, + .remove = __devexit_p(fluid_lcdc_sharp_spi_remove), +}; + +static struct spi_board_info fluid_lcdc_sharp_spi_board_info[] __initdata = { + { + .modalias = "fluid_lcdc_sharp", + .mode = SPI_MODE_1, + .bus_num = 0, + .chip_select = 0, + .max_speed_hz = 26331429, + } +}; + static struct platform_device msm7x30_backlight = { .name = "msm7x30-backlight", }; @@ -714,14 +929,38 @@ int __init msm7x30_init_panel(void) GPIOMUX_DIR_OUTPUT | GPIOMUX_DRV_2MA | GPIOMUX_VALID); + if (machine_is_msm7x30_fluid()) { + int i; + int mux_val = GPIOMUX_FUNC_1 | GPIOMUX_PULL_NONE | + GPIOMUX_DIR_OUTPUT | + GPIOMUX_DRV_2MA | GPIOMUX_VALID; + + msm_gpiomux_write(22, 0, mux_val); + msm_gpiomux_write(25, 0, mux_val); + + for (i = 90; i <= 109; i++) + msm_gpiomux_write(i, 0, mux_val); + } + rc = platform_device_register(&msm_device_mdp); if (rc) return rc; - msm_device_mddi0.dev.platform_data = &mddi_pdata; - rc = platform_device_register(&msm_device_mddi0); - if (rc) - return rc; + if (machine_is_msm7x30_fluid()) { + rc = spi_register_driver(&fluid_lcdc_sharp_spi_driver); + if (rc) + return rc; + spi_register_board_info(fluid_lcdc_sharp_spi_board_info, + ARRAY_SIZE(fluid_lcdc_sharp_spi_board_info)); + pm8058_gpio_mux(MSM7X30_PM8058_GPIO(25), + &msm7x30_fluid_backlight); + gpio_request(MSM7X30_PM8058_GPIO(25), "lcd_backlight"); + } else { + msm_device_mddi0.dev.platform_data = &mddi_pdata; + rc = platform_device_register(&msm_device_mddi0); + if (rc) + return rc; + } platform_device_register(&msm7x30_backlight); return platform_driver_register(&msm7x30_backlight_driver); From 02849f2852227e098aaba46b027873a3d9df7ce2 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 20 Jan 2011 15:22:06 -0800 Subject: [PATCH 0796/2556] ARM: mm: enforce pageblock alignment when freeing memmap entries at init On some machines, the nodes do not always start on pageblock boundaries. In these cases it is possible for free_unused_memmap to free mappings for pages inside a pageblock with otherwise valid pages. This presents problems for page migration since it operates on whole pageblocks at a time. Round down bank_start to pageblock boundary so that whole pageblocks always have valid mappings. Change-Id: Ie6588a274fc2fe9832cf47da31549ae3d004c118 Signed-off-by: Dima Zavin --- arch/arm/mm/init.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index cddd684364dab..f34700d54e66e 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -424,7 +424,11 @@ static void __init free_unused_memmap(struct meminfo *mi) for_each_bank(i, mi) { struct membank *bank = &mi->bank[i]; - bank_start = bank_pfn_start(bank); + /* Round bank_start down to the start of a pageblock so that + * all pages in a pageblock always have a mapping. + */ + bank_start = round_down(bank_pfn_start(bank), + MAX_ORDER_NR_PAGES); /* * If we had a previous bank, and there is a space From 28960dc1352f4a528a0c8658ce1224a0195844ce Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 20 Jan 2011 16:09:12 -0800 Subject: [PATCH 0797/2556] ARM: msm: update surf7x30 defconfig Change-Id: Idf28eb700e7b9e6984914de50f32fe9baebe35c5 Signed-off-by: Dima Zavin --- arch/arm/configs/surf7x30_defconfig | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/arch/arm/configs/surf7x30_defconfig b/arch/arm/configs/surf7x30_defconfig index 9b66b9b4d146b..8d37223be61b0 100644 --- a/arch/arm/configs/surf7x30_defconfig +++ b/arch/arm/configs/surf7x30_defconfig @@ -14,7 +14,6 @@ CONFIG_EMBEDDED=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set CONFIG_ASHMEM=y -CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y @@ -23,7 +22,8 @@ CONFIG_MODULE_FORCE_UNLOAD=y # CONFIG_IOSCHED_CFQ is not set CONFIG_ARCH_MSM=y CONFIG_ARCH_MSM7X30=y -CONFIG_MSM_DEBUG_UART1=y +CONFIG_MACH_MSM7X30_FLUID=y +CONFIG_MSM_DEBUG_UART2=y # CONFIG_HTC_PWRSPLY is not set # CONFIG_HTC_PWRSINK is not set CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT=y @@ -130,7 +130,10 @@ CONFIG_SERIAL_MSM_CONSOLE=y # CONFIG_LEGACY_PTYS is not set # CONFIG_HW_RANDOM is not set CONFIG_I2C=y +CONFIG_SPI=y +CONFIG_SPI_QSD=y CONFIG_POWER_SUPPLY=y +CONFIG_CHARGER_PM8058=y # CONFIG_HWMON is not set CONFIG_REGULATOR=y CONFIG_REGULATOR_DEBUG=y @@ -160,6 +163,9 @@ CONFIG_VIDEO_DEV=y CONFIG_VIDEO_HELPER_CHIPS_AUTO=y CONFIG_DAB=y CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +CONFIG_GPU_MSM_KGSL=y +CONFIG_MSM_KGSL_MMU=y CONFIG_USB_GADGET=y CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_USB_GADGET_MSM_72K=y @@ -218,7 +224,7 @@ CONFIG_DEBUG_KERNEL=y CONFIG_DETECT_HUNG_TASK=y CONFIG_SCHEDSTATS=y CONFIG_TIMER_STATS=y -CONFIG_DEBUG_SLAB=y +CONFIG_SLUB_DEBUG_ON=y # CONFIG_DEBUG_PREEMPT is not set CONFIG_DEBUG_MUTEXES=y CONFIG_DEBUG_SPINLOCK_SLEEP=y From 98c65775a4f59721113939b30425146e81f31e01 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 20 Jan 2011 16:30:12 -0800 Subject: [PATCH 0798/2556] ARM: msm: board-msm7x30: add charger support Change-Id: I5de7c49b353e77c110c08198a9f0d07233f213b0 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 69 +++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 77174ebd7a701..a857280a29250 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -103,6 +104,72 @@ static struct msm_hsusb_platform_data msm_hsusb_pdata = { .phy_init_seq = msm7x30_phy_init_seq, .phy_reset = msm7x30_usb_phy_reset, .hw_reset = msm7x30_usb_hw_reset, + .usb_connected = pm8058_notify_charger_connected, +}; + +static struct wake_lock vbus_wake_lock; + +static void msm7x30_vbus_present(bool present) +{ + pr_info("usb_cable_status: %s\n", present ? "inserted" : "removed"); + if (present) + wake_lock(&vbus_wake_lock); + msm_hsusb_set_vbus_state(present); + if (!present) + wake_unlock(&vbus_wake_lock); +} + +static int msm7x30_pcom_charge(u32 max_current, bool is_ac) +{ + u32 status = 0; + u32 pc_ids[] = { + PCOM_CHG_USB_IS_PC_CONNECTED, + PCOM_CHG_USB_IS_CHARGER_CONNECTED, + }; + int ret = 0; + + pr_info("%s(%u,%d)\n", __func__, max_current, is_ac); + + if (max_current) { + /* enable charging */ + status = 0; + msm_proc_comm(pc_ids[!!is_ac], &status, 0); + if (!status) { + pr_err("%s: can't set chg type (ac=%d)\n", __func__, + is_ac); + ret = -EINVAL; + goto err; + } + msm_proc_comm(PCOM_CHG_USB_IS_AVAILABLE, &max_current, &status); + if (!status) { + pr_err("%s: set_i failed %u\n", __func__, max_current); + ret = -EINVAL; + goto err; + } + } else { + msm_proc_comm(PCOM_CHG_USB_IS_AVAILABLE, &max_current, &status); + if (!status) { + pr_err("%s: set_i failed %u\n", __func__, max_current); + ret = -EINVAL; + goto err; + } + msm_proc_comm(PCOM_CHG_USB_IS_DISCONNECTED, &status, 0); + if (!status) { + pr_err("%s: can't set disconnect\n", __func__); + ret = -EINVAL; + goto err; + } + } + +err: + return ret; +} + +static struct pm8058_charger_platform_data msm7x30_pmic_charger_pdata = { + .vbus_present = msm7x30_vbus_present, + .charge = msm7x30_pcom_charge, + .supplied_to = NULL, + .num_supplicants = 0, }; static char *usb_functions[] = { @@ -420,6 +487,7 @@ static struct pm8058_platform_data msm7x30_pm8058_pdata = { .irq_base = MSM7X30_PM8058_IRQ_BASE, .gpio_base = MSM7X30_PM8058_GPIO_BASE, .keypad_pdata = &msm7x30_pmic_keypad_pdata, + .charger_pdata = &msm7x30_pmic_charger_pdata, }; static struct msm_ssbi_platform_data msm7x30_ssbi_pmic_pdata = { @@ -488,6 +556,7 @@ extern void msm_serial_debug_init(unsigned int base, int irq, static void __init msm7x30_init(void) { + wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "board-vbus"); #ifdef CONFIG_DEBUG_LL { /* HACK: get a fake clock request for uart2 for debug_ll */ From d2a9a29429aeace5348467435e050e3165d9e00d Mon Sep 17 00:00:00 2001 From: Trilok Soni Date: Thu, 20 Jan 2011 18:37:33 -0800 Subject: [PATCH 0799/2556] ARM: msm: board-msm7x30: add fluid keymap Change-Id: I7564ed14fbad9cbae00fd161d63b825d0e70488f Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index a857280a29250..cf720a42f4e26 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -362,7 +362,20 @@ static int msm7x30_pmic_keypad_init(struct device *dev) return 0; } -static const unsigned short msm7x30_pmic_keymap[KEYMAP_SIZE] = { +static const unsigned short msm7x30_fluid_pmic_keymap[KEYMAP_SIZE] = { + [KEYMAP_INDEX(0, 0)] = KEY_7, + [KEYMAP_INDEX(0, 1)] = KEY_ENTER, + [KEYMAP_INDEX(0, 2)] = KEY_UP, + [KEYMAP_INDEX(0, 4)] = KEY_DOWN, + + [KEYMAP_INDEX(1, 0)] = KEY_POWER, + [KEYMAP_INDEX(1, 1)] = KEY_SELECT, + [KEYMAP_INDEX(1, 2)] = KEY_1, + [KEYMAP_INDEX(1, 3)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(1, 4)] = KEY_VOLUMEDOWN, +}; + +static const unsigned short msm7x30_surf_pmic_keymap[KEYMAP_SIZE] = { [KEYMAP_INDEX(0, 0)] = KEY_7, [KEYMAP_INDEX(0, 1)] = KEY_DOWN, [KEYMAP_INDEX(0, 2)] = KEY_UP, @@ -479,7 +492,6 @@ static struct pm8058_keypad_platform_data msm7x30_pmic_keypad_pdata = { .scan_delay_shift = 5, .drv_hold_clks = 4, .debounce_ms = 10, - .keymap = msm7x30_pmic_keymap, .init = msm7x30_pmic_keypad_init, }; @@ -574,6 +586,11 @@ static void __init msm7x30_init(void) &msm_device_uart2.dev, 1); #endif + if (machine_is_msm7x30_fluid()) + msm7x30_pmic_keypad_pdata.keymap = msm7x30_fluid_pmic_keymap; + else + msm7x30_pmic_keypad_pdata.keymap = msm7x30_surf_pmic_keymap; + msm7x30_ssbi_pmic_init(); msm7x30_spi_init(); From 2ab62bd489eddcba705dfb16c196a0d799916fba Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 21 Jan 2011 16:04:03 -0800 Subject: [PATCH 0800/2556] Revert "input: support for Cypress TMG i2c capacitive touchscreen" This reverts commit fc68a78eff3cfd7a31842e7f1e7de752143bc656. Change-Id: Ifc6d7308fa11f8d22092c8e24c6a582fd98bf32a --- drivers/input/touchscreen/Kconfig | 11 - drivers/input/touchscreen/Makefile | 1 - drivers/input/touchscreen/cy8c_tmg_ts.c | 467 ------------------------ include/linux/cy8c_tmg_ts.h | 37 -- 4 files changed, 516 deletions(-) delete mode 100644 drivers/input/touchscreen/cy8c_tmg_ts.c delete mode 100644 include/linux/cy8c_tmg_ts.h diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 5dc30b21927b8..3c9da231da1ed 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -124,17 +124,6 @@ config TOUCHSCREEN_CY8CTMG110 To compile this driver as a module, choose M here: the module will be called cy8ctmg110_ts. -config TOUCHSCREEN_CYPRESS_TMG - tristate "Support for Cypress TMC i2c touchscreen" - depends on I2C - help - Say Y here to enable support for cy8ctmg touchcreens. - - If unsure, say N - - To compile this driver as a module, choose M here: the - module will be called h3600_ts_input. - config TOUCHSCREEN_DA9034 tristate "Touchscreen support for Dialog Semiconductor DA9034" depends on PMIC_DA903X diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 4f2897627c660..be5078e463524 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -17,7 +17,6 @@ obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o -obj-$(CONFIG_TOUCHSCREEN_CYPRESS_TMG) += cy8c_tmg_ts.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o diff --git a/drivers/input/touchscreen/cy8c_tmg_ts.c b/drivers/input/touchscreen/cy8c_tmg_ts.c deleted file mode 100644 index f48374eb30e17..0000000000000 --- a/drivers/input/touchscreen/cy8c_tmg_ts.c +++ /dev/null @@ -1,467 +0,0 @@ -/* drivers/input/touchscreen/cy8c_tmg_ts.c - * - * Copyright (C) 2007-2008 HTC Corporation. - * - * 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 - -#define CY8C_REG_START_NEW_SCAN 0x0F -#define CY8C_REG_INTR_STATUS 0x3C -#define CY8C_REG_VERSION 0x3E - -struct cy8c_ts_data { - struct i2c_client *client; - struct input_dev *input_dev; - int use_irq; - struct hrtimer timer; - struct work_struct work; - uint16_t version; - int (*power) (int on); - struct early_suspend early_suspend; -}; - -struct workqueue_struct *cypress_touch_wq; - -#ifdef CONFIG_HAS_EARLYSUSPEND -static void cy8c_ts_early_suspend(struct early_suspend *h); -static void cy8c_ts_late_resume(struct early_suspend *h); -#endif - -uint16_t sample_count, X_mean, Y_mean, first_touch; - -static s32 cy8c_read_word_data(struct i2c_client *client, - u8 command, uint16_t * data) -{ - s32 ret = i2c_smbus_read_word_data(client, command); - if (ret != -1) { - *data = (u16) ((ret << 8) | (ret >> 8)); - } - return ret; -} - -static int cy8c_init_panel(struct cy8c_ts_data *ts) -{ - int ret; - sample_count = X_mean = Y_mean = first_touch = 0; - - /* clean intr busy */ - ret = i2c_smbus_write_byte_data(ts->client, CY8C_REG_INTR_STATUS, - 0x00); - if (ret < 0) { - dev_err(&ts->client->dev, - "cy8c_init_panel failed for clean intr busy\n"); - goto exit; - } - - /* start new scan */ - ret = i2c_smbus_write_byte_data(ts->client, CY8C_REG_START_NEW_SCAN, - 0x01); - if (ret < 0) { - dev_err(&ts->client->dev, - "cy8c_init_panel failed for start new scan\n"); - goto exit; - } - -exit: - return ret; -} - -static void cy8c_ts_reset(struct i2c_client *client) -{ - struct cy8c_ts_data *ts = i2c_get_clientdata(client); - - if (ts->power) { - ts->power(0); - msleep(10); - ts->power(1); - msleep(10); - } - - cy8c_init_panel(ts); -} - -static void cy8c_ts_work_func(struct work_struct *work) -{ - struct cy8c_ts_data *ts = container_of(work, struct cy8c_ts_data, work); - uint16_t x1, y1, x2, y2; - uint8_t is_touch, start_reg, force, area, finger2_pressed; - uint8_t buf[11]; - struct i2c_msg msg[2]; - int ret = 0; - - x2 = y2 = 0; - - /*printk("%s: enter\n",__func__);*/ - is_touch = i2c_smbus_read_byte_data(ts->client, 0x20); - dev_dbg(&ts->client->dev, "fIsTouch %d,\n", is_touch); - if (is_touch < 0 || is_touch > 3) { - pr_err("%s: invalid is_touch = %d\n", __func__, is_touch); - cy8c_ts_reset(ts->client); - msleep(10); - goto done; - } - - msg[0].addr = ts->client->addr; - msg[0].flags = 0; - msg[0].len = 1; - start_reg = 0x16; - msg[0].buf = &start_reg; - - msg[1].addr = ts->client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = sizeof(buf); - msg[1].buf = buf; - - ret = i2c_transfer(ts->client->adapter, msg, 2); - if (ret < 0) - goto done; - - /* parse data */ - force = buf[0]; - area = buf[1]; - x1 = (buf[2] << 8) | buf[3]; - y1 = (buf[6] << 8) | buf[7]; - is_touch = buf[10]; - - if (is_touch == 2) { - x2 = (buf[4] << 8) | buf[5]; - y2 = (buf[8] << 8) | buf[9]; - finger2_pressed = 1; - } - - dev_dbg(&ts->client->dev, - "bFingerForce %d, bFingerArea %d \n", force, area); - dev_dbg(&ts->client->dev, "x1: %d, y1: %d \n", x1, y1); - if (finger2_pressed) - dev_dbg(&ts->client->dev, "x2: %d, y2: %d \n", x2, y2); - - /* drop the first one? */ - if ((is_touch == 1) && (first_touch == 0)) { - first_touch = 1; - goto done; - } - - if (!first_touch) - goto done; - - if (is_touch == 2) - finger2_pressed = 1; - - input_report_abs(ts->input_dev, ABS_X, x1); - input_report_abs(ts->input_dev, ABS_Y, y1); - input_report_abs(ts->input_dev, ABS_PRESSURE, force); - input_report_abs(ts->input_dev, ABS_TOOL_WIDTH, area); - input_report_key(ts->input_dev, BTN_TOUCH, is_touch); - input_report_key(ts->input_dev, BTN_2, finger2_pressed); - - if (finger2_pressed) { - input_report_abs(ts->input_dev, ABS_HAT0X, x2); - input_report_abs(ts->input_dev, ABS_HAT0Y, y2); - } - input_sync(ts->input_dev); - -done: - if (is_touch == 0) - first_touch = sample_count = 0; - - /* prepare for next intr */ - i2c_smbus_write_byte_data(ts->client, CY8C_REG_INTR_STATUS, 0x00); - if (!ts->use_irq) - hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL); - else - enable_irq(ts->client->irq); -} - -static enum hrtimer_restart cy8c_ts_timer_func(struct hrtimer *timer) -{ - struct cy8c_ts_data *ts; - - ts = container_of(timer, struct cy8c_ts_data, timer); - queue_work(cypress_touch_wq, &ts->work); - return HRTIMER_NORESTART; -} - -static irqreturn_t cy8c_ts_irq_handler(int irq, void *dev_id) -{ - struct cy8c_ts_data *ts = dev_id; - - disable_irq_nosync(ts->client->irq); - queue_work(cypress_touch_wq, &ts->work); - return IRQ_HANDLED; -} - -static int cy8c_ts_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct cy8c_ts_data *ts; - struct cy8c_i2c_platform_data *pdata; - uint16_t panel_version; - int ret = 0; - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - dev_err(&client->dev, "need I2C_FUNC_I2C\n"); - ret = -ENODEV; - goto err_check_functionality_failed; - } - - ts = kzalloc(sizeof(struct cy8c_ts_data), GFP_KERNEL); - if (ts == NULL) { - dev_err(&client->dev, "allocate cy8c_ts_data failed\n"); - ret = -ENOMEM; - goto err_alloc_data_failed; - } - - INIT_WORK(&ts->work, cy8c_ts_work_func); - ts->client = client; - i2c_set_clientdata(client, ts); - - pdata = client->dev.platform_data; - if (pdata) { - ts->version = pdata->version; - ts->power = pdata->power; - } - - if (ts->power) { - ret = ts->power(1); - msleep(10); - if (ret < 0) { - dev_err(&client->dev, "power on failed\n"); - goto err_power_failed; - } - } - - ret = cy8c_read_word_data(ts->client, CY8C_REG_VERSION, &panel_version); - if (ret < 0) { - dev_err(&client->dev, "init panel failed\n"); - goto err_detect_failed; - } - dev_info(&client->dev, "Panel Version %04X\n", panel_version); - if (pdata) { - while (pdata->version > panel_version) { - dev_info(&client->dev, "old tp detected, " - "panel version = %x\n", panel_version); - pdata++; - } - } - - ret = cy8c_init_panel(ts); - if (ret < 0) { - dev_err(&client->dev, "init panel failed\n"); - goto err_detect_failed; - } - - ts->input_dev = input_allocate_device(); - if (ts->input_dev == NULL) { - ret = -ENOMEM; - dev_err(&client->dev, "Failed to allocate input device\n"); - goto err_input_dev_alloc_failed; - } - ts->input_dev->name = "cy8c-touchscreen"; - - set_bit(EV_SYN, ts->input_dev->evbit); - set_bit(EV_ABS, ts->input_dev->evbit); - set_bit(EV_KEY, ts->input_dev->evbit); - input_set_capability(ts->input_dev, EV_KEY, BTN_TOUCH); - input_set_capability(ts->input_dev, EV_KEY, BTN_2); - - input_set_abs_params(ts->input_dev, ABS_X, - pdata->abs_x_min, pdata->abs_x_max, 5, 0); - input_set_abs_params(ts->input_dev, ABS_Y, - pdata->abs_y_min, pdata->abs_y_max, 5, 0); - input_set_abs_params(ts->input_dev, ABS_HAT0X, - pdata->abs_x_min, pdata->abs_x_max, 0, 0); - input_set_abs_params(ts->input_dev, ABS_HAT0Y, - pdata->abs_y_min, pdata->abs_y_max, 0, 0); - input_set_abs_params(ts->input_dev, ABS_PRESSURE, - pdata->abs_pressure_min, pdata->abs_pressure_max, - 0, 0); - input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH, - pdata->abs_width_min, pdata->abs_width_max, 0, 0); - - ret = input_register_device(ts->input_dev); - if (ret) { - dev_err(&client->dev, - "cy8c_ts_probe: Unable to register %s input device\n", - ts->input_dev->name); - goto err_input_register_device_failed; - } - - if (client->irq) { - ret = request_irq(client->irq, cy8c_ts_irq_handler, - IRQF_TRIGGER_LOW, CYPRESS_TMG_NAME, ts); - if (ret == 0) - ts->use_irq = 1; - else - dev_err(&client->dev, "request_irq failed\n"); - } - - if (!ts->use_irq) { - hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - ts->timer.function = cy8c_ts_timer_func; - hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); - } - -#ifdef CONFIG_HAS_EARLYSUSPEND - ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; - ts->early_suspend.suspend = cy8c_ts_early_suspend; - ts->early_suspend.resume = cy8c_ts_late_resume; - register_early_suspend(&ts->early_suspend); -#endif - - dev_info(&client->dev, "Start touchscreen %s in %s mode\n", - ts->input_dev->name, (ts->use_irq ? "interrupt" : "polling")); - - return 0; - -err_input_register_device_failed: - input_free_device(ts->input_dev); - -err_input_dev_alloc_failed: - if (ts->power) - ts->power(0); - -err_detect_failed: -err_power_failed: - kfree(ts); - -err_alloc_data_failed: -err_check_functionality_failed: - return ret; -} - -static int cy8c_ts_remove(struct i2c_client *client) -{ - struct cy8c_ts_data *ts = i2c_get_clientdata(client); - - unregister_early_suspend(&ts->early_suspend); - - if (ts->use_irq) - free_irq(client->irq, ts); - else - hrtimer_cancel(&ts->timer); - - input_unregister_device(ts->input_dev); - kfree(ts); - - return 0; -} - -static int cy8c_ts_suspend(struct i2c_client *client, pm_message_t mesg) -{ - struct cy8c_ts_data *ts = i2c_get_clientdata(client); - int ret; - - if (ts->use_irq) - disable_irq_nosync(client->irq); - else - hrtimer_cancel(&ts->timer); - - ret = cancel_work_sync(&ts->work); - if (ret && ts->use_irq) - enable_irq(client->irq); - - if (ts->power) - ts->power(0); - - return 0; -} - -static int cy8c_ts_resume(struct i2c_client *client) -{ - int ret; - struct cy8c_ts_data *ts = i2c_get_clientdata(client); - - if (ts->power) { - ret = ts->power(1); - if (ret < 0) - dev_err(&client->dev, - "cy8c_ts_resume power on failed\n"); - msleep(10); - - cy8c_init_panel(ts); - } - - if (ts->use_irq) - enable_irq(client->irq); - else - hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); - - return 0; -} - -#ifdef CONFIG_HAS_EARLYSUSPEND -static void cy8c_ts_early_suspend(struct early_suspend *h) -{ - struct cy8c_ts_data *ts; - ts = container_of(h, struct cy8c_ts_data, early_suspend); - cy8c_ts_suspend(ts->client, PMSG_SUSPEND); -} - -static void cy8c_ts_late_resume(struct early_suspend *h) -{ - struct cy8c_ts_data *ts; - ts = container_of(h, struct cy8c_ts_data, early_suspend); - cy8c_ts_resume(ts->client); -} -#endif - -static const struct i2c_device_id cy8c_ts_i2c_id[] = { - {CYPRESS_TMG_NAME, 0}, - {} -}; - -static struct i2c_driver cy8c_ts_driver = { - .id_table = cy8c_ts_i2c_id, - .probe = cy8c_ts_probe, - .remove = cy8c_ts_remove, -#ifndef CONFIG_HAS_EARLYSUSPEND - .suspend = cy8c_ts_suspend, - .resume = cy8c_ts_resume, -#endif - .driver = { - .name = CYPRESS_TMG_NAME, - .owner = THIS_MODULE, - }, -}; - -static int __devinit cy8c_ts_init(void) -{ - cypress_touch_wq = create_singlethread_workqueue("cypress_touch_wq"); - if (!cypress_touch_wq) - return -ENOMEM; - - return i2c_add_driver(&cy8c_ts_driver); -} - -static void __exit cy8c_ts_exit(void) -{ - if (cypress_touch_wq) - destroy_workqueue(cypress_touch_wq); - - i2c_del_driver(&cy8c_ts_driver); -} - -module_init(cy8c_ts_init); -module_exit(cy8c_ts_exit); - -MODULE_DESCRIPTION("Cypress TMG Touchscreen Driver"); -MODULE_LICENSE("GPL"); - diff --git a/include/linux/cy8c_tmg_ts.h b/include/linux/cy8c_tmg_ts.h deleted file mode 100644 index f3cf17c4faee3..0000000000000 --- a/include/linux/cy8c_tmg_ts.h +++ /dev/null @@ -1,37 +0,0 @@ -/* include/linux/cy8c_tmg_ts.c - * - * Copyright (C) 2007-2008 HTC Corporation. - * - * 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 CY8C_I2C_H -#define CY8C_I2C_H - -#include - -#define CYPRESS_TMG_NAME "cy8c-tmg-ts" - -struct cy8c_i2c_platform_data { - uint16_t version; - int abs_x_min; - int abs_x_max; - int abs_y_min; - int abs_y_max; - int abs_pressure_min; - int abs_pressure_max; - int abs_width_min; - int abs_width_max; - int (*power)(int on); -}; - -#endif - From 8124707237a954f29d3c50b0e98e34af5e339224 Mon Sep 17 00:00:00 2001 From: Anirudh Ghayal Date: Sun, 18 Jul 2010 13:06:34 +0530 Subject: [PATCH 0801/2556] input: touchscreen: Add support for Cypress TTSP touchscreen This is a new touchscreen driver for the Cypress Semiconductor cyttsp family of devices. This driver is for the i2c version of cyttsp parts. Signed-off-by: Kevin McNeely Signed-off-by: Anirudh Ghayal input: cyttsp-i2c: Enable firmware loading feature from driver TTSP firmware can be loaded during driver's probe by updating the firmware header file. This header file contains the firmware data as part of an array and it's version number. Signed-off-by: Kevin McNeely Signed-off-by: Mohan Pallaka input: cyttsp-i2c: Reduce the time needed to update firmware Optimize the firmware loading procedure to load the firmware faster. Signed-off-by: Kevin McNeely Signed-off-by: Mohan Pallaka input: cyttsp-i2c: Move suspend, resume to dev_pm_ops Signed-off-by: Kevin McNeely Signed-off-by: Mohan Pallaka input: cyttsp-i2c: Add runtime PM annotations Signed-off-by: Kevin McNeely Signed-off-by: Mohan Pallaka input: cyttsp-i2c: Add firmware version in sysfs Expose ttsp firmware version to userspace via a sysfs entry. Signed-off-by: Kevin McNeely Signed-off-by: Mohan Pallaka input: cyttsp-i2c: Add sysfs entry to update the firmware Add sysfs entry to update firmware using request_firmware. The firmware image has to be available in /etc/firmware. If not, request_firmware will wait for 60secs and returns error. Signed-off-by: Kevin McNeely Signed-off-by: Mohan Pallaka input: cyttsp-i2c: Support system wakeup through touch Enable touch as a wakeup source based on platform data. Signed-off-by: Kevin McNeely Signed-off-by: Mohan Pallaka input: cyttsp-i2c: Add offset support for touchscreen Adjust the coordinates if the touch panel is longer than display. Add minx and miny for display to move the coordinates clipping to user space. Signed-off-by: Kevin McNeely Signed-off-by: Mohan Pallaka --- drivers/input/touchscreen/Kconfig | 14 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/cyttsp-i2c.c | 2135 ++++++++++++ drivers/input/touchscreen/cyttsp_fw.h | 4307 ++++++++++++++++++++++++ include/linux/cyttsp.h | 658 ++++ 5 files changed, 7115 insertions(+) mode change 100644 => 100755 drivers/input/touchscreen/Kconfig mode change 100644 => 100755 drivers/input/touchscreen/Makefile create mode 100644 drivers/input/touchscreen/cyttsp-i2c.c create mode 100755 drivers/input/touchscreen/cyttsp_fw.h create mode 100644 include/linux/cyttsp.h diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig old mode 100644 new mode 100755 index 3c9da231da1ed..fb301140ca3f9 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -124,6 +124,20 @@ config TOUCHSCREEN_CY8CTMG110 To compile this driver as a module, choose M here: the module will be called cy8ctmg110_ts. +config TOUCHSCREEN_CYTTSP_I2C + tristate "Cypress TTSP based touchscreens" + depends on I2C + default n + help + Say Y here if you have a Cypress TTSP based touchscreen. + TMA300 is a multi-touch screen which can report upto 10 + touches at a time. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called cyttsp-i2c. + config TOUCHSCREEN_DA9034 tristate "Touchscreen support for Dialog Semiconductor DA9034" depends on PMIC_DA903X diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile old mode 100644 new mode 100755 index be5078e463524..fb925eba66207 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp-i2c.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o diff --git a/drivers/input/touchscreen/cyttsp-i2c.c b/drivers/input/touchscreen/cyttsp-i2c.c new file mode 100644 index 0000000000000..dd59089d25daf --- /dev/null +++ b/drivers/input/touchscreen/cyttsp-i2c.c @@ -0,0 +1,2135 @@ +/* Source for: + * Cypress TrueTouch(TM) Standard Product I2C touchscreen driver. + * drivers/input/touchscreen/cyttsp-i2c.c + * + * Copyright (C) 2009, 2010 Cypress Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Cypress reserves the right to make changes without further notice + * to the materials described herein. Cypress does not assume any + * liability arising out of the application described herein. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +#define CY_DECLARE_GLOBALS + +#include + +uint32_t cyttsp_tsdebug1 = 0xff; +module_param_named(tsdebug1, cyttsp_tsdebug1, uint, 0664); + +/* CY TTSP I2C Driver private data */ +struct cyttsp { + struct i2c_client *client; + struct input_dev *input; + struct work_struct work; + struct timer_list timer; + struct mutex mutex; + char phys[32]; + struct cyttsp_platform_data *platform_data; + u8 num_prv_st_tch; + u16 act_trk[CY_NUM_TRK_ID]; + u16 prv_st_tch[CY_NUM_ST_TCH_ID]; + u16 prv_mt_tch[CY_NUM_MT_TCH_ID]; + u16 prv_mt_pos[CY_NUM_TRK_ID][2]; + atomic_t irq_enabled; + char cyttsp_fw_ver[10]; + bool cyttsp_update_fw; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif /* CONFIG_HAS_EARLYSUSPEND */ +}; +static u8 irq_cnt; /* comparison counter with register valuw */ +static u32 irq_cnt_total; /* total interrupts */ +static u32 irq_err_cnt; /* count number of touch interrupts with err */ +#define CY_IRQ_CNT_MASK 0x000000FF /* mapped for sizeof count in reg */ +#define CY_IRQ_CNT_REG 0x00 /* tt_undef[0]=reg 0x1B - Gen3 only */ + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void cyttsp_early_suspend(struct early_suspend *handler); +static void cyttsp_late_resume(struct early_suspend *handler); +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +static struct workqueue_struct *cyttsp_ts_wq; + + +/* **************************************************************************** + * Prototypes for static functions + * ************************************************************************** */ +static void cyttsp_xy_worker(struct work_struct *work); +static irqreturn_t cyttsp_irq(int irq, void *handle); +static int cyttsp_inlist(u16 prev_track[], + u8 cur_trk_id, u8 *prev_loc, u8 num_touches); +static int cyttsp_next_avail_inlist(u16 cur_trk[], + u8 *new_loc, u8 num_touches); +static int cyttsp_putbl(struct cyttsp *ts, int show, + int show_status, int show_version, int show_cid); +static int __devinit cyttsp_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int __devexit cyttsp_remove(struct i2c_client *client); +static int cyttsp_resume(struct device *dev); +static int cyttsp_suspend(struct device *dev); + +/* Static variables */ +static struct cyttsp_gen3_xydata_t g_xy_data; +static struct cyttsp_bootloader_data_t g_bl_data; +static struct cyttsp_sysinfo_data_t g_sysinfo_data; +static const struct i2c_device_id cyttsp_id[] = { + { CY_I2C_NAME, 0 }, { } +}; +static u8 bl_cmd[] = { + CY_BL_FILE0, CY_BL_CMD, CY_BL_EXIT, + CY_BL_KEY0, CY_BL_KEY1, CY_BL_KEY2, + CY_BL_KEY3, CY_BL_KEY4, CY_BL_KEY5, + CY_BL_KEY6, CY_BL_KEY7}; + +MODULE_DEVICE_TABLE(i2c, cyttsp_id); + +static const struct dev_pm_ops cyttsp_pm_ops = { + .suspend = cyttsp_suspend, + .resume = cyttsp_resume, +}; + +static struct i2c_driver cyttsp_driver = { + .driver = { + .name = CY_I2C_NAME, + .owner = THIS_MODULE, + .pm = &cyttsp_pm_ops, + }, + .probe = cyttsp_probe, + .remove = __devexit_p(cyttsp_remove), + .id_table = cyttsp_id, +}; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver"); +MODULE_AUTHOR("Cypress"); + +static ssize_t cyttsp_irq_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct cyttsp *ts = i2c_get_clientdata(client); + return sprintf(buf, "%u\n", atomic_read(&ts->irq_enabled)); +} + +static ssize_t cyttsp_irq_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct cyttsp *ts = i2c_get_clientdata(client); + int err = 0; + unsigned long value; + + if (size > 2) + return -EINVAL; + + err = strict_strtoul(buf, 10, &value); + if (err != 0) + return err; + + switch (value) { + case 0: + if (atomic_cmpxchg(&ts->irq_enabled, 1, 0)) { + pr_info("touch irq disabled!\n"); + disable_irq_nosync(ts->client->irq); + } + err = size; + break; + case 1: + if (!atomic_cmpxchg(&ts->irq_enabled, 0, 1)) { + pr_info("touch irq enabled!\n"); + enable_irq(ts->client->irq); + } + err = size; + break; + default: + pr_info("cyttsp_irq_enable failed -> irq_enabled = %d\n", + atomic_read(&ts->irq_enabled)); + err = -EINVAL; + break; + } + + return err; +} + +static DEVICE_ATTR(irq_enable, 0777, cyttsp_irq_status, cyttsp_irq_enable); + +static ssize_t cyttsp_fw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp *ts = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", ts->cyttsp_fw_ver); +} + +static DEVICE_ATTR(cyttsp_fw_ver, 0777, cyttsp_fw_show, NULL); + +static ssize_t cyttsp_update_fw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp *ts = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", ts->cyttsp_update_fw); +} + +static ssize_t cyttsp_update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct cyttsp *ts = dev_get_drvdata(dev); + const struct firmware *cyttsp_fw = NULL; + unsigned long val; + int i, data_len, rc; + const u8 *data = NULL; + + if (size > 2) + return -EINVAL; + + rc = strict_strtoul(buf, 10, &val); + if (rc != 0) + return rc; + + if ((ts->cyttsp_update_fw ^ val) && val) { + ts->cyttsp_update_fw = 1; + /* read fw file */ + if (request_firmware(&cyttsp_fw, "ttsp.fw", dev)) + pr_err("%s: ttsp.fw request failed\n", __func__); + else { + data = cyttsp_fw->data; + data_len = cyttsp_fw->size; + for (i = 0; i < data_len; i++) + pr_debug("%x ", data[i]); + } + ts->cyttsp_update_fw = 0; + } + return size; +} + +static DEVICE_ATTR(cyttsp_update_fw, 0777, cyttsp_update_fw_show, + cyttsp_update_fw_store); + +/* The cyttsp_xy_worker function reads the XY coordinates and sends them to + * the input layer. It is scheduled from the interrupt (or timer). + */ +void cyttsp_xy_worker(struct work_struct *work) +{ + struct cyttsp *ts = container_of(work, struct cyttsp, work); + u8 id, tilt, rev_x, rev_y; + u8 i, loc; + u8 prv_tch; /* number of previous touches */ + u8 cur_tch; /* number of current touches */ + u16 tmp_trk[CY_NUM_MT_TCH_ID]; + u16 snd_trk[CY_NUM_MT_TCH_ID]; + u16 cur_trk[CY_NUM_TRK_ID]; + u16 cur_st_tch[CY_NUM_ST_TCH_ID]; + u16 cur_mt_tch[CY_NUM_MT_TCH_ID]; + /* if NOT CY_USE_TRACKING_ID then + * only uses CY_NUM_MT_TCH_ID positions */ + u16 cur_mt_pos[CY_NUM_TRK_ID][2]; + /* if NOT CY_USE_TRACKING_ID then + * only uses CY_NUM_MT_TCH_ID positions */ + u8 cur_mt_z[CY_NUM_TRK_ID]; + u8 curr_tool_width; + u16 st_x1, st_y1; + u8 st_z1; + u16 st_x2, st_y2; + u8 st_z2; + s32 retval; + + cyttsp_xdebug("TTSP worker start 1:\n"); + + /* get event data from CYTTSP device */ + i = CY_NUM_RETRY; + do { + retval = i2c_smbus_read_i2c_block_data(ts->client, + CY_REG_BASE, + sizeof(struct cyttsp_gen3_xydata_t), (u8 *)&g_xy_data); + } while ((retval < CY_OK) && --i); + + if (retval < CY_OK) { + /* return immediately on + * failure to read device on the i2c bus */ + goto exit_xy_worker; + } + + cyttsp_xdebug("TTSP worker start 2:\n"); + + /* compare own irq counter with the device irq counter */ + if (ts->client->irq) { + u8 host_reg; + u8 cur_cnt; + if (ts->platform_data->use_hndshk) { + + host_reg = g_xy_data.hst_mode & CY_HNDSHK_BIT ? + g_xy_data.hst_mode & ~CY_HNDSHK_BIT : + g_xy_data.hst_mode | CY_HNDSHK_BIT; + retval = i2c_smbus_write_i2c_block_data(ts->client, + CY_REG_BASE, sizeof(host_reg), &host_reg); + } + cur_cnt = g_xy_data.tt_undef[CY_IRQ_CNT_REG]; + irq_cnt_total++; + irq_cnt++; + if (irq_cnt != cur_cnt) { + irq_err_cnt++; + cyttsp_debug("i_c_ER: dv=%d fw=%d hm=%02X t=%lu te=%lu\n", \ + irq_cnt, \ + cur_cnt, g_xy_data.hst_mode, \ + (unsigned long)irq_cnt_total, \ + (unsigned long)irq_err_cnt); + } else { + cyttsp_debug("i_c_ok: dv=%d fw=%d hm=%02X t=%lu te=%lu\n", \ + irq_cnt, \ + cur_cnt, g_xy_data.hst_mode, \ + (unsigned long)irq_cnt_total, \ + (unsigned long)irq_err_cnt); + } + irq_cnt = cur_cnt; + } + + /* Get the current num touches and return if there are no touches */ + if ((GET_BOOTLOADERMODE(g_xy_data.tt_mode) == 1) || + (GET_HSTMODE(g_xy_data.hst_mode) != CY_OK)) { + u8 host_reg, tries; + /* the TTSP device has suffered spurious reset or mode switch */ + cyttsp_debug( \ + "Spurious err opmode (tt_mode=%02X hst_mode=%02X)\n", \ + g_xy_data.tt_mode, g_xy_data.hst_mode); + cyttsp_debug("Reset TTSP Device; Terminating active tracks\n"); + /* terminate all active tracks */ + cur_tch = CY_NTCH; + /* reset TTSP part and take it back out of Bootloader mode */ + /* reset TTSP Device back to bootloader mode */ + host_reg = CY_SOFT_RESET_MODE; + retval = i2c_smbus_write_i2c_block_data(ts->client, CY_REG_BASE, + sizeof(host_reg), &host_reg); + /* wait for TTSP Device to complete reset back to bootloader */ + tries = 0; + do { + mdelay(1); + cyttsp_putbl(ts, 1, false, false, false); + } while (g_bl_data.bl_status != 0x10 && + g_bl_data.bl_status != 0x11 && + tries++ < 100); + retval = cyttsp_putbl(ts, 1, true, true, true); + /* switch back to operational mode */ + /* take TTSP device out of bootloader mode; + * switch back to TrueTouch operational mode */ + if (!(retval < CY_OK)) { + int tries; + retval = i2c_smbus_write_i2c_block_data(ts->client, + CY_REG_BASE, + sizeof(bl_cmd), bl_cmd); + /* wait for TTSP Device to complete + * switch to Operational mode */ + tries = 0; + do { + mdelay(100); + cyttsp_putbl(ts, 2, false, false, false); + } while (GET_BOOTLOADERMODE(g_bl_data.bl_status) && + tries++ < 100); + cyttsp_putbl(ts, 2, true, false, false); + } + goto exit_xy_worker; + } else { + cur_tch = GET_NUM_TOUCHES(g_xy_data.tt_stat); + if (IS_LARGE_AREA(g_xy_data.tt_stat)) { + /* terminate all active tracks */ + cur_tch = CY_NTCH; + cyttsp_debug("Large obj detect (tt_stat=0x%02X). Terminate act trks\n", \ + g_xy_data.tt_stat); + } else if (cur_tch > CY_NUM_MT_TCH_ID) { + /* if the number of fingers on the touch surface + * is more than the maximum then + * there will be no new track information + * even for the original touches. + * Therefore, terminate all active tracks. + */ + cur_tch = CY_NTCH; + cyttsp_debug("Num touch err (tt_stat=0x%02X). Terminate act trks\n", \ + g_xy_data.tt_stat); + } + } + + /* set tool size */ + curr_tool_width = CY_SMALL_TOOL_WIDTH; + + /* translate Gen2 interface data into comparable Gen3 data */ + if (ts->platform_data->gen == CY_GEN2) { + struct cyttsp_gen2_xydata_t *pxy_gen2_data; + pxy_gen2_data = (struct cyttsp_gen2_xydata_t *)(&g_xy_data); + + /* use test data? */ + cyttsp_testdat(&g_xy_data, &tt_gen2_testray, \ + sizeof(struct cyttsp_gen3_xydata_t)); + + if (pxy_gen2_data->evnt_idx == CY_GEN2_NOTOUCH) { + cur_tch = 0; + } else if (cur_tch == CY_GEN2_GHOST) { + cur_tch = 0; + } else if (cur_tch == CY_GEN2_2TOUCH) { + /* stuff artificial track ID1 and ID2 */ + g_xy_data.touch12_id = 0x12; + g_xy_data.z1 = CY_MAXZ; + g_xy_data.z2 = CY_MAXZ; + cur_tch--; /* 2 touches */ + } else if (cur_tch == CY_GEN2_1TOUCH) { + /* stuff artificial track ID1 and ID2 */ + g_xy_data.touch12_id = 0x12; + g_xy_data.z1 = CY_MAXZ; + g_xy_data.z2 = CY_NTCH; + if (pxy_gen2_data->evnt_idx == CY_GEN2_TOUCH2) { + /* push touch 2 data into touch1 + * (first finger up; second finger down) */ + /* stuff artificial track ID1 for touch2 info */ + g_xy_data.touch12_id = 0x20; + /* stuff touch 1 with touch 2 coordinate data */ + g_xy_data.x1 = g_xy_data.x2; + g_xy_data.y1 = g_xy_data.y2; + } + } else { + cur_tch = 0; + } + } else { + /* use test data? */ + cyttsp_testdat(&g_xy_data, &tt_gen3_testray, \ + sizeof(struct cyttsp_gen3_xydata_t)); + } + + + + /* clear current active track ID array and count previous touches */ + for (id = 0, prv_tch = CY_NTCH; + id < CY_NUM_TRK_ID; id++) { + cur_trk[id] = CY_NTCH; + prv_tch += ts->act_trk[id]; + } + + /* send no events if no previous touches and no new touches */ + if ((prv_tch == CY_NTCH) && + ((cur_tch == CY_NTCH) || + (cur_tch > CY_NUM_MT_TCH_ID))) { + goto exit_xy_worker; + } + + cyttsp_debug("prev=%d curr=%d\n", prv_tch, cur_tch); + + for (id = 0; id < CY_NUM_ST_TCH_ID; id++) { + /* clear current single touches array */ + cur_st_tch[id] = CY_IGNR_TCH; + } + + /* clear single touch positions */ + st_x1 = CY_NTCH; + st_y1 = CY_NTCH; + st_z1 = CY_NTCH; + st_x2 = CY_NTCH; + st_y2 = CY_NTCH; + st_z2 = CY_NTCH; + + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + /* clear current multi-touches array and + * multi-touch positions/z */ + cur_mt_tch[id] = CY_IGNR_TCH; + } + + if (ts->platform_data->use_trk_id) { + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + cur_mt_pos[id][CY_XPOS] = 0; + cur_mt_pos[id][CY_YPOS] = 0; + cur_mt_z[id] = 0; + } + } else { + for (id = 0; id < CY_NUM_TRK_ID; id++) { + cur_mt_pos[id][CY_XPOS] = 0; + cur_mt_pos[id][CY_YPOS] = 0; + cur_mt_z[id] = 0; + } + } + + /* Determine if display is tilted */ + if (FLIP_DATA(ts->platform_data->flags)) + tilt = true; + else + tilt = false; + + /* Check for switch in origin */ + if (REVERSE_X(ts->platform_data->flags)) + rev_x = true; + else + rev_x = false; + + if (REVERSE_Y(ts->platform_data->flags)) + rev_y = true; + else + rev_y = false; + + if (cur_tch) { + struct cyttsp_gen2_xydata_t *pxy_gen2_data; + struct cyttsp_gen3_xydata_t *pxy_gen3_data; + switch (ts->platform_data->gen) { + case CY_GEN2: { + pxy_gen2_data = + (struct cyttsp_gen2_xydata_t *)(&g_xy_data); + cyttsp_xdebug("TTSP Gen2 report:\n"); + cyttsp_xdebug("%02X %02X %02X\n", \ + pxy_gen2_data->hst_mode, \ + pxy_gen2_data->tt_mode, \ + pxy_gen2_data->tt_stat); + cyttsp_xdebug("%04X %04X %02X %02X\n", \ + pxy_gen2_data->x1, \ + pxy_gen2_data->y1, \ + pxy_gen2_data->z1, \ + pxy_gen2_data->evnt_idx); + cyttsp_xdebug("%04X %04X %02X\n", \ + pxy_gen2_data->x2, \ + pxy_gen2_data->y2, \ + pxy_gen2_data->tt_undef1); + cyttsp_xdebug("%02X %02X %02X\n", \ + pxy_gen2_data->gest_cnt, \ + pxy_gen2_data->gest_id, \ + pxy_gen2_data->gest_set); + break; + } + case CY_GEN3: + default: { + pxy_gen3_data = + (struct cyttsp_gen3_xydata_t *)(&g_xy_data); + cyttsp_xdebug("TTSP Gen3 report:\n"); + cyttsp_xdebug("%02X %02X %02X\n", \ + pxy_gen3_data->hst_mode, + pxy_gen3_data->tt_mode, + pxy_gen3_data->tt_stat); + cyttsp_xdebug("%04X %04X %02X %02X", \ + pxy_gen3_data->x1, + pxy_gen3_data->y1, + pxy_gen3_data->z1, \ + pxy_gen3_data->touch12_id); + cyttsp_xdebug("%04X %04X %02X\n", \ + pxy_gen3_data->x2, \ + pxy_gen3_data->y2, \ + pxy_gen3_data->z2); + cyttsp_xdebug("%02X %02X %02X\n", \ + pxy_gen3_data->gest_cnt, \ + pxy_gen3_data->gest_id, \ + pxy_gen3_data->gest_set); + cyttsp_xdebug("%04X %04X %02X %02X\n", \ + pxy_gen3_data->x3, \ + pxy_gen3_data->y3, \ + pxy_gen3_data->z3, \ + pxy_gen3_data->touch34_id); + cyttsp_xdebug("%04X %04X %02X\n", \ + pxy_gen3_data->x4, \ + pxy_gen3_data->y4, \ + pxy_gen3_data->z4); + break; + } + } + } + + /* process the touches */ + switch (cur_tch) { + case 4: { + g_xy_data.x4 = be16_to_cpu(g_xy_data.x4); + g_xy_data.y4 = be16_to_cpu(g_xy_data.y4); + if (tilt) + FLIP_XY(g_xy_data.x4, g_xy_data.y4); + + if (rev_x) { + g_xy_data.x4 = INVERT_X(g_xy_data.x4, + ts->platform_data->panel_maxx); + if (g_xy_data.x4 < 0) + pr_debug("X value is negative. Please configure" + " maxx in platform data structure\n"); + } + if (rev_y) { + g_xy_data.y4 = INVERT_X(g_xy_data.y4, + ts->platform_data->panel_maxy); + if (g_xy_data.y4 < 0) + pr_debug("Y value is negative. Please configure" + " maxy in platform data structure\n"); + + } + id = GET_TOUCH4_ID(g_xy_data.touch34_id); + if (ts->platform_data->use_trk_id) { + cur_mt_pos[CY_MT_TCH4_IDX][CY_XPOS] = + g_xy_data.x4; + cur_mt_pos[CY_MT_TCH4_IDX][CY_YPOS] = + g_xy_data.y4; + cur_mt_z[CY_MT_TCH4_IDX] = g_xy_data.z4; + } else { + cur_mt_pos[id][CY_XPOS] = g_xy_data.x4; + cur_mt_pos[id][CY_YPOS] = g_xy_data.y4; + cur_mt_z[id] = g_xy_data.z4; + } + cur_mt_tch[CY_MT_TCH4_IDX] = id; + cur_trk[id] = CY_TCH; + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] < + CY_NUM_TRK_ID) { + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) { + st_x1 = g_xy_data.x4; + st_y1 = g_xy_data.y4; + st_z1 = g_xy_data.z4; + cur_st_tch[CY_ST_FNGR1_IDX] = id; + } else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) { + st_x2 = g_xy_data.x4; + st_y2 = g_xy_data.y4; + st_z2 = g_xy_data.z4; + cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + } + cyttsp_xdebug("4th XYZ:% 3d,% 3d,% 3d ID:% 2d\n\n", \ + g_xy_data.x4, g_xy_data.y4, g_xy_data.z4, \ + (g_xy_data.touch34_id & 0x0F)); + /* do not break */ + } + case 3: { + g_xy_data.x3 = be16_to_cpu(g_xy_data.x3); + g_xy_data.y3 = be16_to_cpu(g_xy_data.y3); + if (tilt) + FLIP_XY(g_xy_data.x3, g_xy_data.y3); + + if (rev_x) { + g_xy_data.x3 = INVERT_X(g_xy_data.x3, + ts->platform_data->panel_maxx); + if (g_xy_data.x3 < 0) + pr_debug("X value is negative. Please configure" + " maxx in platform data structure\n"); + + } + if (rev_y) { + g_xy_data.y3 = INVERT_X(g_xy_data.y3, + ts->platform_data->panel_maxy); + if (g_xy_data.y3 < 0) + pr_debug("Y value is negative. Please configure" + " maxy in platform data structure\n"); + + } + id = GET_TOUCH3_ID(g_xy_data.touch34_id); + if (ts->platform_data->use_trk_id) { + cur_mt_pos[CY_MT_TCH3_IDX][CY_XPOS] = + g_xy_data.x3; + cur_mt_pos[CY_MT_TCH3_IDX][CY_YPOS] = + g_xy_data.y3; + cur_mt_z[CY_MT_TCH3_IDX] = g_xy_data.z3; + } else { + cur_mt_pos[id][CY_XPOS] = g_xy_data.x3; + cur_mt_pos[id][CY_YPOS] = g_xy_data.y3; + cur_mt_z[id] = g_xy_data.z3; + } + cur_mt_tch[CY_MT_TCH3_IDX] = id; + cur_trk[id] = CY_TCH; + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] < + CY_NUM_TRK_ID) { + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) { + st_x1 = g_xy_data.x3; + st_y1 = g_xy_data.y3; + st_z1 = g_xy_data.z3; + cur_st_tch[CY_ST_FNGR1_IDX] = id; + } else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) { + st_x2 = g_xy_data.x3; + st_y2 = g_xy_data.y3; + st_z2 = g_xy_data.z3; + cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + } + cyttsp_xdebug("3rd XYZ:% 3d,% 3d,% 3d ID:% 2d\n", \ + g_xy_data.x3, g_xy_data.y3, g_xy_data.z3, \ + ((g_xy_data.touch34_id >> 4) & 0x0F)); + /* do not break */ + } + case 2: { + g_xy_data.x2 = be16_to_cpu(g_xy_data.x2); + g_xy_data.y2 = be16_to_cpu(g_xy_data.y2); + if (tilt) + FLIP_XY(g_xy_data.x2, g_xy_data.y2); + + if (rev_x) { + g_xy_data.x2 = INVERT_X(g_xy_data.x2, + ts->platform_data->panel_maxx); + if (g_xy_data.x2 < 0) + pr_debug("X value is negative. Please configure" + " maxx in platform data structure\n"); + } + if (rev_y) { + g_xy_data.y2 = INVERT_X(g_xy_data.y2, + ts->platform_data->panel_maxy); + if (g_xy_data.y2 < 0) + pr_debug("Y value is negative. Please configure" + " maxy in platform data structure\n"); + } + id = GET_TOUCH2_ID(g_xy_data.touch12_id); + if (ts->platform_data->use_trk_id) { + cur_mt_pos[CY_MT_TCH2_IDX][CY_XPOS] = + g_xy_data.x2; + cur_mt_pos[CY_MT_TCH2_IDX][CY_YPOS] = + g_xy_data.y2; + cur_mt_z[CY_MT_TCH2_IDX] = g_xy_data.z2; + } else { + cur_mt_pos[id][CY_XPOS] = g_xy_data.x2; + cur_mt_pos[id][CY_YPOS] = g_xy_data.y2; + cur_mt_z[id] = g_xy_data.z2; + } + cur_mt_tch[CY_MT_TCH2_IDX] = id; + cur_trk[id] = CY_TCH; + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] < + CY_NUM_TRK_ID) { + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) { + st_x1 = g_xy_data.x2; + st_y1 = g_xy_data.y2; + st_z1 = g_xy_data.z2; + cur_st_tch[CY_ST_FNGR1_IDX] = id; + } else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) { + st_x2 = g_xy_data.x2; + st_y2 = g_xy_data.y2; + st_z2 = g_xy_data.z2; + cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + } + cyttsp_xdebug("2nd XYZ:% 3d,% 3d,% 3d ID:% 2d\n", \ + g_xy_data.x2, g_xy_data.y2, g_xy_data.z2, \ + (g_xy_data.touch12_id & 0x0F)); + /* do not break */ + } + case 1: { + g_xy_data.x1 = be16_to_cpu(g_xy_data.x1); + g_xy_data.y1 = be16_to_cpu(g_xy_data.y1); + if (tilt) + FLIP_XY(g_xy_data.x1, g_xy_data.y1); + + if (rev_x) { + g_xy_data.x1 = INVERT_X(g_xy_data.x1, + ts->platform_data->panel_maxx); + if (g_xy_data.x1 < 0) + pr_debug("X value is negative. Please configure" + " maxx in platform data structure\n"); + } + if (rev_y) { + g_xy_data.y1 = INVERT_X(g_xy_data.y1, + ts->platform_data->panel_maxy); + if (g_xy_data.y1 < 0) + pr_debug("Y value is negative. Please configure" + " maxy in platform data structure"); + } + id = GET_TOUCH1_ID(g_xy_data.touch12_id); + if (ts->platform_data->use_trk_id) { + cur_mt_pos[CY_MT_TCH1_IDX][CY_XPOS] = + g_xy_data.x1; + cur_mt_pos[CY_MT_TCH1_IDX][CY_YPOS] = + g_xy_data.y1; + cur_mt_z[CY_MT_TCH1_IDX] = g_xy_data.z1; + } else { + cur_mt_pos[id][CY_XPOS] = g_xy_data.x1; + cur_mt_pos[id][CY_YPOS] = g_xy_data.y1; + cur_mt_z[id] = g_xy_data.z1; + } + cur_mt_tch[CY_MT_TCH1_IDX] = id; + cur_trk[id] = CY_TCH; + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] < + CY_NUM_TRK_ID) { + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) { + st_x1 = g_xy_data.x1; + st_y1 = g_xy_data.y1; + st_z1 = g_xy_data.z1; + cur_st_tch[CY_ST_FNGR1_IDX] = id; + } else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) { + st_x2 = g_xy_data.x1; + st_y2 = g_xy_data.y1; + st_z2 = g_xy_data.z1; + cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + } + cyttsp_xdebug("1st XYZ:% 3d,% 3d,% 3d ID:% 2d\n", \ + g_xy_data.x1, g_xy_data.y1, g_xy_data.z1, \ + ((g_xy_data.touch12_id >> 4) & 0x0F)); + break; + } + case 0: + default:{ + break; + } + } + + /* handle Single Touch signals */ + if (ts->platform_data->use_st) { + cyttsp_xdebug("ST STEP 0 - ST1 ID=%d ST2 ID=%d\n", \ + cur_st_tch[CY_ST_FNGR1_IDX], \ + cur_st_tch[CY_ST_FNGR2_IDX]); + if (cur_st_tch[CY_ST_FNGR1_IDX] > CY_NUM_TRK_ID) { + /* reassign finger 1 and 2 positions to new tracks */ + if (cur_tch > 0) { + /* reassign st finger1 */ + if (ts->platform_data->use_trk_id) { + id = CY_MT_TCH1_IDX; + cur_st_tch[CY_ST_FNGR1_IDX] = cur_mt_tch[id]; + } else { + id = GET_TOUCH1_ID(g_xy_data.touch12_id); + cur_st_tch[CY_ST_FNGR1_IDX] = id; + } + st_x1 = cur_mt_pos[id][CY_XPOS]; + st_y1 = cur_mt_pos[id][CY_YPOS]; + st_z1 = cur_mt_z[id]; + cyttsp_xdebug("ST STEP 1 - ST1 ID=%3d\n", \ + cur_st_tch[CY_ST_FNGR1_IDX]); + if ((cur_tch > 1) && + (cur_st_tch[CY_ST_FNGR2_IDX] > + CY_NUM_TRK_ID)) { + /* reassign st finger2 */ + if (cur_tch > 1) { + if (ts->platform_data->use_trk_id) { + id = CY_MT_TCH2_IDX; + cur_st_tch[CY_ST_FNGR2_IDX] = cur_mt_tch[id]; + } else { + id = GET_TOUCH2_ID(g_xy_data.touch12_id); + cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + st_x2 = cur_mt_pos[id][CY_XPOS]; + st_y2 = cur_mt_pos[id][CY_YPOS]; + st_z2 = cur_mt_z[id]; + cyttsp_xdebug("ST STEP 2 - ST2 ID=%3d\n", \ + cur_st_tch[CY_ST_FNGR2_IDX]); + } + } + } + } else if (cur_st_tch[CY_ST_FNGR2_IDX] > CY_NUM_TRK_ID) { + if (cur_tch > 1) { + /* reassign st finger2 */ + if (ts->platform_data->use_trk_id) { + /* reassign st finger2 */ + id = CY_MT_TCH2_IDX; + cur_st_tch[CY_ST_FNGR2_IDX] = + cur_mt_tch[id]; + } else { + /* reassign st finger2 */ + id = GET_TOUCH2_ID(g_xy_data.touch12_id); + cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + st_x2 = cur_mt_pos[id][CY_XPOS]; + st_y2 = cur_mt_pos[id][CY_YPOS]; + st_z2 = cur_mt_z[id]; + cyttsp_xdebug("ST STEP 3 - ST2 ID=%3d\n", \ + cur_st_tch[CY_ST_FNGR2_IDX]); + } + } + /* if the 1st touch is missing and there is a 2nd touch, + * then set the 1st touch to 2nd touch and terminate 2nd touch + */ + if ((cur_st_tch[CY_ST_FNGR1_IDX] > CY_NUM_TRK_ID) && + (cur_st_tch[CY_ST_FNGR2_IDX] < CY_NUM_TRK_ID)) { + st_x1 = st_x2; + st_y1 = st_y2; + st_z1 = st_z2; + cur_st_tch[CY_ST_FNGR1_IDX] = + cur_st_tch[CY_ST_FNGR2_IDX]; + cur_st_tch[CY_ST_FNGR2_IDX] = + CY_IGNR_TCH; + } + /* if the 2nd touch ends up equal to the 1st touch, + * then just report a single touch */ + if (cur_st_tch[CY_ST_FNGR1_IDX] == + cur_st_tch[CY_ST_FNGR2_IDX]) { + cur_st_tch[CY_ST_FNGR2_IDX] = + CY_IGNR_TCH; + } + /* set Single Touch current event signals */ + if (cur_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) { + input_report_abs(ts->input, + ABS_X, st_x1); + input_report_abs(ts->input, + ABS_Y, st_y1); + input_report_abs(ts->input, + ABS_PRESSURE, st_z1); + input_report_key(ts->input, + BTN_TOUCH, + CY_TCH); + input_report_abs(ts->input, + ABS_TOOL_WIDTH, + curr_tool_width); + cyttsp_debug("ST->F1:%3d X:%3d Y:%3d Z:%3d\n", \ + cur_st_tch[CY_ST_FNGR1_IDX], \ + st_x1, st_y1, st_z1); + if (cur_st_tch[CY_ST_FNGR2_IDX] < CY_NUM_TRK_ID) { + input_report_key(ts->input, BTN_2, CY_TCH); + input_report_abs(ts->input, ABS_HAT0X, st_x2); + input_report_abs(ts->input, ABS_HAT0Y, st_y2); + cyttsp_debug("ST->F2:%3d X:%3d Y:%3d Z:%3d\n", \ + cur_st_tch[CY_ST_FNGR2_IDX], + st_x2, st_y2, st_z2); + } else { + input_report_key(ts->input, + BTN_2, + CY_NTCH); + } + } else { + input_report_abs(ts->input, ABS_PRESSURE, CY_NTCH); + input_report_key(ts->input, BTN_TOUCH, CY_NTCH); + input_report_key(ts->input, BTN_2, CY_NTCH); + } + /* update platform data for the current single touch info */ + ts->prv_st_tch[CY_ST_FNGR1_IDX] = cur_st_tch[CY_ST_FNGR1_IDX]; + ts->prv_st_tch[CY_ST_FNGR2_IDX] = cur_st_tch[CY_ST_FNGR2_IDX]; + + } + + /* handle Multi-touch signals */ + if (ts->platform_data->use_mt) { + if (ts->platform_data->use_trk_id) { + /* terminate any previous touch where the track + * is missing from the current event */ + for (id = 0; id < CY_NUM_TRK_ID; id++) { + if ((ts->act_trk[id] != CY_NTCH) && + (cur_trk[id] == CY_NTCH)) { + input_report_abs(ts->input, + ABS_MT_TRACKING_ID, + id); + input_report_abs(ts->input, + ABS_MT_TOUCH_MAJOR, + CY_NTCH); + input_report_abs(ts->input, + ABS_MT_WIDTH_MAJOR, + curr_tool_width); + input_report_abs(ts->input, + ABS_MT_POSITION_X, + ts->prv_mt_pos[id][CY_XPOS]); + input_report_abs(ts->input, + ABS_MT_POSITION_Y, + ts->prv_mt_pos[id][CY_YPOS]); + CY_MT_SYNC(ts->input); + ts->act_trk[id] = CY_NTCH; + ts->prv_mt_pos[id][CY_XPOS] = 0; + ts->prv_mt_pos[id][CY_YPOS] = 0; + } + } + /* set Multi-Touch current event signals */ + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + if (cur_mt_tch[id] < CY_NUM_TRK_ID) { + input_report_abs(ts->input, + ABS_MT_TRACKING_ID, + cur_mt_tch[id]); + input_report_abs(ts->input, + ABS_MT_TOUCH_MAJOR, + cur_mt_z[id]); + input_report_abs(ts->input, + ABS_MT_WIDTH_MAJOR, + curr_tool_width); + input_report_abs(ts->input, + ABS_MT_POSITION_X, + cur_mt_pos[id][CY_XPOS]); + input_report_abs(ts->input, + ABS_MT_POSITION_Y, + cur_mt_pos[id][CY_YPOS]); + CY_MT_SYNC(ts->input); + ts->act_trk[id] = CY_TCH; + ts->prv_mt_pos[id][CY_XPOS] = + cur_mt_pos[id][CY_XPOS]; + ts->prv_mt_pos[id][CY_YPOS] = + cur_mt_pos[id][CY_YPOS]; + } + } + } else { + /* set temporary track array elements to voids */ + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + tmp_trk[id] = CY_IGNR_TCH; + snd_trk[id] = CY_IGNR_TCH; + } + + /* get what is currently active */ + for (i = 0, id = 0; + id < CY_NUM_TRK_ID && i < CY_NUM_MT_TCH_ID; + id++) { + if (cur_trk[id] == CY_TCH) { + /* only incr counter if track found */ + tmp_trk[i] = id; + i++; + } + } + cyttsp_xdebug("T1: t0=%d, t1=%d, t2=%d, t3=%d\n", \ + tmp_trk[0], tmp_trk[1], tmp_trk[2], \ + tmp_trk[3]); + cyttsp_xdebug("T1: p0=%d, p1=%d, p2=%d, p3=%d\n", \ + ts->prv_mt_tch[0], ts->prv_mt_tch[1], \ + ts->prv_mt_tch[2], ts->prv_mt_tch[3]); + + /* pack in still active previous touches */ + for (id = 0, prv_tch = 0; + id < CY_NUM_MT_TCH_ID; id++) { + if (tmp_trk[id] < CY_NUM_TRK_ID) { + if (cyttsp_inlist(ts->prv_mt_tch, + tmp_trk[id], &loc, + CY_NUM_MT_TCH_ID)) { + loc &= CY_NUM_MT_TCH_ID - 1; + snd_trk[loc] = tmp_trk[id]; + prv_tch++; + cyttsp_xdebug("inlist s[%d]=%d t[%d]=%d l=%d p=%d\n", \ + loc, snd_trk[loc], \ + id, tmp_trk[id], \ + loc, prv_tch); + } else { + cyttsp_xdebug("not inlist s[%d]=%d t[%d]=%d l=%d \n", \ + id, snd_trk[id], \ + id, tmp_trk[id], \ + loc); + } + } + } + cyttsp_xdebug("S1: s0=%d, s1=%d, s2=%d, s3=%d p=%d\n", \ + snd_trk[0], snd_trk[1], snd_trk[2], \ + snd_trk[3], prv_tch); + + /* pack in new touches */ + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + if (tmp_trk[id] < CY_NUM_TRK_ID) { + if (!cyttsp_inlist(snd_trk, tmp_trk[id], &loc, CY_NUM_MT_TCH_ID)) { + cyttsp_xdebug("not inlist t[%d]=%d l=%d\n", \ + id, tmp_trk[id], loc); + if (cyttsp_next_avail_inlist(snd_trk, &loc, CY_NUM_MT_TCH_ID)) { + loc &= CY_NUM_MT_TCH_ID - 1; + snd_trk[loc] = tmp_trk[id]; + cyttsp_xdebug("put inlist s[%d]=%d t[%d]=%d\n", + loc, snd_trk[loc], id, tmp_trk[id]); + } + } else { + cyttsp_xdebug("is in list s[%d]=%d t[%d]=%d loc=%d\n", \ + id, snd_trk[id], id, tmp_trk[id], loc); + } + } + } + cyttsp_xdebug("S2: s0=%d, s1=%d, s2=%d, s3=%d\n", \ + snd_trk[0], snd_trk[1], + snd_trk[2], snd_trk[3]); + + /* sync motion event signals for each current touch */ + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + /* z will either be 0 (NOTOUCH) or + * some pressure (TOUCH) */ + cyttsp_xdebug("MT0 prev[%d]=%d temp[%d]=%d send[%d]=%d\n", \ + id, ts->prv_mt_tch[id], \ + id, tmp_trk[id], \ + id, snd_trk[id]); + if (snd_trk[id] < CY_NUM_TRK_ID) { + input_report_abs(ts->input, + ABS_MT_TOUCH_MAJOR, + cur_mt_z[snd_trk[id]]); + input_report_abs(ts->input, + ABS_MT_WIDTH_MAJOR, + curr_tool_width); + input_report_abs(ts->input, + ABS_MT_POSITION_X, + cur_mt_pos[snd_trk[id]][CY_XPOS]); + input_report_abs(ts->input, + ABS_MT_POSITION_Y, + cur_mt_pos[snd_trk[id]][CY_YPOS]); + CY_MT_SYNC(ts->input); + cyttsp_debug("MT1->TID:%2d X:%3d Y:%3d Z:%3d touch-sent\n", \ + snd_trk[id], \ + cur_mt_pos[snd_trk[id]][CY_XPOS], \ + cur_mt_pos[snd_trk[id]][CY_YPOS], \ + cur_mt_z[snd_trk[id]]); + } else if (ts->prv_mt_tch[id] < CY_NUM_TRK_ID) { + /* void out this touch */ + input_report_abs(ts->input, + ABS_MT_TOUCH_MAJOR, + CY_NTCH); + input_report_abs(ts->input, + ABS_MT_WIDTH_MAJOR, + curr_tool_width); + input_report_abs(ts->input, + ABS_MT_POSITION_X, + ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_XPOS]); + input_report_abs(ts->input, + ABS_MT_POSITION_Y, + ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_YPOS]); + CY_MT_SYNC(ts->input); + cyttsp_debug("MT2->TID:%2d X:%3d Y:%3d Z:%3d lift off-sent\n", \ + ts->prv_mt_tch[id], \ + ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_XPOS], \ + ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_YPOS], \ + CY_NTCH); + } else { + /* do not stuff any signals for this + * previously and currently + * void touches */ + cyttsp_xdebug("MT3->send[%d]=%d - No touch - NOT sent\n", \ + id, snd_trk[id]); + } + } + + /* save current posted tracks to + * previous track memory */ + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + ts->prv_mt_tch[id] = snd_trk[id]; + if (snd_trk[id] < CY_NUM_TRK_ID) { + ts->prv_mt_pos[snd_trk[id]][CY_XPOS] = + cur_mt_pos[snd_trk[id]][CY_XPOS]; + ts->prv_mt_pos[snd_trk[id]][CY_YPOS] = + cur_mt_pos[snd_trk[id]][CY_YPOS]; + cyttsp_xdebug("MT4->TID:%2d X:%3d Y:%3d Z:%3d save for previous\n", \ + snd_trk[id], \ + ts->prv_mt_pos[snd_trk[id]][CY_XPOS], \ + ts->prv_mt_pos[snd_trk[id]][CY_YPOS], \ + CY_NTCH); + } + } + for (id = 0; id < CY_NUM_TRK_ID; id++) + ts->act_trk[id] = CY_NTCH; + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + if (snd_trk[id] < CY_NUM_TRK_ID) + ts->act_trk[snd_trk[id]] = CY_TCH; + } + } + } + + /* handle gestures */ + if (ts->platform_data->use_gestures) { + if (g_xy_data.gest_id) { + input_report_key(ts->input, + BTN_3, CY_TCH); + input_report_abs(ts->input, + ABS_HAT1X, g_xy_data.gest_id); + input_report_abs(ts->input, + ABS_HAT2Y, g_xy_data.gest_cnt); + } + } + + /* signal the view motion event */ + input_sync(ts->input); + + for (id = 0; id < CY_NUM_TRK_ID; id++) { + /* update platform data for the current MT information */ + ts->act_trk[id] = cur_trk[id]; + } + +exit_xy_worker: + if (cyttsp_disable_touch) { + /* Turn off the touch interrupts */ + cyttsp_debug("Not enabling touch\n"); + } else { + if (ts->client->irq == 0) { + /* restart event timer */ + mod_timer(&ts->timer, jiffies + TOUCHSCREEN_TIMEOUT); + } else { + /* re-enable the interrupt after processing */ + enable_irq(ts->client->irq); + } + } + return; +} + +static int cyttsp_inlist(u16 prev_track[], u8 cur_trk_id, + u8 *prev_loc, u8 num_touches) +{ + u8 id = 0; + + *prev_loc = CY_IGNR_TCH; + + cyttsp_xdebug("IN p[%d]=%d c=%d n=%d loc=%d\n", \ + id, prev_track[id], cur_trk_id, \ + num_touches, *prev_loc); + for (id = 0, *prev_loc = CY_IGNR_TCH; + (id < num_touches); id++) { + cyttsp_xdebug("p[%d]=%d c=%d n=%d loc=%d\n", \ + id, prev_track[id], cur_trk_id, \ + num_touches, *prev_loc); + if (prev_track[id] == cur_trk_id) { + *prev_loc = id; + break; + } + } + cyttsp_xdebug("OUT p[%d]=%d c=%d n=%d loc=%d\n", \ + id, prev_track[id], cur_trk_id, num_touches, *prev_loc); + + return ((*prev_loc < CY_NUM_TRK_ID) ? true : false); +} + +static int cyttsp_next_avail_inlist(u16 cur_trk[], + u8 *new_loc, u8 num_touches) +{ + u8 id; + + for (id = 0, *new_loc = CY_IGNR_TCH; + (id < num_touches); id++) { + if (cur_trk[id] > CY_NUM_TRK_ID) { + *new_loc = id; + break; + } + } + + return ((*new_loc < CY_NUM_TRK_ID) ? true : false); +} + +/* Timer function used as dummy interrupt driver */ +static void cyttsp_timer(unsigned long handle) +{ + struct cyttsp *ts = (struct cyttsp *) handle; + + cyttsp_xdebug("TTSP Device timer event\n"); + + /* schedule motion signal handling */ + queue_work(cyttsp_ts_wq, &ts->work); + + return; +} + + + +/* ************************************************************************ + * ISR function. This function is general, initialized in drivers init + * function + * ************************************************************************ */ +static irqreturn_t cyttsp_irq(int irq, void *handle) +{ + struct cyttsp *ts = (struct cyttsp *) handle; + + cyttsp_xdebug("%s: Got IRQ\n", CY_I2C_NAME); + + /* disable further interrupts until this interrupt is processed */ + disable_irq_nosync(ts->client->irq); + + /* schedule motion signal handling */ + queue_work(cyttsp_ts_wq, &ts->work); + return IRQ_HANDLED; +} + +/* ************************************************************************ + * Probe initialization functions + * ************************************************************************ */ +static int cyttsp_putbl(struct cyttsp *ts, int show, + int show_status, int show_version, int show_cid) +{ + int retval = CY_OK; + + int num_bytes = (show_status * 3) + (show_version * 6) + (show_cid * 3); + + if (show_cid) + num_bytes = sizeof(struct cyttsp_bootloader_data_t); + else if (show_version) + num_bytes = sizeof(struct cyttsp_bootloader_data_t) - 3; + else + num_bytes = sizeof(struct cyttsp_bootloader_data_t) - 9; + + if (show) { + retval = i2c_smbus_read_i2c_block_data(ts->client, + CY_REG_BASE, num_bytes, (u8 *)&g_bl_data); + if (show_status) { + cyttsp_debug("BL%d: f=%02X s=%02X err=%02X bl=%02X%02X bld=%02X%02X\n", \ + show, \ + g_bl_data.bl_file, \ + g_bl_data.bl_status, \ + g_bl_data.bl_error, \ + g_bl_data.blver_hi, g_bl_data.blver_lo, \ + g_bl_data.bld_blver_hi, g_bl_data.bld_blver_lo); + } + if (show_version) { + cyttsp_debug("BL%d: ttspver=0x%02X%02X appid=0x%02X%02X appver=0x%02X%02X\n", \ + show, \ + g_bl_data.ttspver_hi, g_bl_data.ttspver_lo, \ + g_bl_data.appid_hi, g_bl_data.appid_lo, \ + g_bl_data.appver_hi, g_bl_data.appver_lo); + } + if (show_cid) { + cyttsp_debug("BL%d: cid=0x%02X%02X%02X\n", \ + show, \ + g_bl_data.cid_0, \ + g_bl_data.cid_1, \ + g_bl_data.cid_2); + } + } + + return retval; +} + +#ifdef CY_INCLUDE_LOAD_FILE +#define CY_MAX_I2C_LEN 256 +#define CY_MAX_TRY 10 +#define CY_BL_PAGE_SIZE 16 +#define CY_BL_NUM_PAGES 5 +static int cyttsp_i2c_wr_blk_chunks(struct cyttsp *ts, u8 command, + u8 length, const u8 *values) +{ + int retval = CY_OK; + int block = 1; + + u8 dataray[CY_MAX_I2C_LEN]; + + /* first page already includes the bl page offset */ + retval = i2c_smbus_write_i2c_block_data(ts->client, CY_REG_BASE, + CY_BL_PAGE_SIZE+1, values); + values += CY_BL_PAGE_SIZE+1; + length -= CY_BL_PAGE_SIZE+1; + + /* rem blocks require bl page offset stuffing */ + while (length && + (block < CY_BL_NUM_PAGES) && + !(retval < CY_OK)) { + udelay(43*2); /* TRM * 2 */ + dataray[0] = CY_BL_PAGE_SIZE*block; + memcpy(&dataray[1], values, + length >= CY_BL_PAGE_SIZE ? + CY_BL_PAGE_SIZE : length); + retval = i2c_smbus_write_i2c_block_data(ts->client, + CY_REG_BASE, + length >= CY_BL_PAGE_SIZE ? + CY_BL_PAGE_SIZE + 1 : length+1, dataray); + values += CY_BL_PAGE_SIZE; + length = length >= CY_BL_PAGE_SIZE ? + length - CY_BL_PAGE_SIZE : 0; + block++; + } + + return retval; +} + +static int cyttsp_bootload_app(struct cyttsp *ts) +{ + int retval = CY_OK; + int i, tries; + u8 host_reg; + + cyttsp_debug("load new firmware \n"); + /* reset TTSP Device back to bootloader mode */ + host_reg = CY_SOFT_RESET_MODE; + retval = i2c_smbus_write_i2c_block_data(ts->client, CY_REG_BASE, + sizeof(host_reg), &host_reg); + /* wait for TTSP Device to complete reset back to bootloader */ + tries = 0; + do { + mdelay(1); + cyttsp_putbl(ts, 3, false, false, false); + } while (g_bl_data.bl_status != 0x10 && + g_bl_data.bl_status != 0x11 && + tries++ < 100); + cyttsp_debug("load file - tver=0x%02X%02X a_id=0x%02X%02X aver=0x%02X%02X\n", \ + cyttsp_fw_tts_verh, cyttsp_fw_tts_verl, \ + cyttsp_fw_app_idh, cyttsp_fw_app_idl, \ + cyttsp_fw_app_verh, cyttsp_fw_app_verl); + + /* download new TTSP Application to the Bootloader */ + if (!(retval < CY_OK)) { + i = 0; + /* send bootload initiation command */ + if (cyttsp_fw[i].Command == CY_BL_INIT_LOAD) { + g_bl_data.bl_file = 0; + g_bl_data.bl_status = 0; + g_bl_data.bl_error = 0; + retval = i2c_smbus_write_i2c_block_data(ts->client, + CY_REG_BASE, + cyttsp_fw[i].Length, cyttsp_fw[i].Block); + /* delay to allow bl to get ready for block writes */ + i++; + tries = 0; + do { + mdelay(100); + cyttsp_putbl(ts, 4, false, false, false); + } while (g_bl_data.bl_status != 0x10 && + g_bl_data.bl_status != 0x11 && + tries++ < 100); + cyttsp_debug("wait init f=%02X, s=%02X, e=%02X t=%d\n", \ + g_bl_data.bl_file, g_bl_data.bl_status, \ + g_bl_data.bl_error, tries); + /* send bootload firmware load blocks */ + if (!(retval < CY_OK)) { + while (cyttsp_fw[i].Command == CY_BL_WRITE_BLK) { + retval = cyttsp_i2c_wr_blk_chunks(ts, + CY_REG_BASE, + cyttsp_fw[i].Length, + cyttsp_fw[i].Block); + cyttsp_xdebug("BL DNLD Rec=% 3d Len=% 3d Addr=%04X\n", \ + cyttsp_fw[i].Record, \ + cyttsp_fw[i].Length, \ + cyttsp_fw[i].Address); + i++; + if (retval < CY_OK) { + cyttsp_debug("BL fail Rec=%3d retval=%d\n", \ + cyttsp_fw[i-1].Record, \ + retval); + break; + } else { + tries = 0; + cyttsp_putbl(ts, 5, false, false, false); + while (!((g_bl_data.bl_status == 0x10) && + (g_bl_data.bl_error == 0x20)) && + !((g_bl_data.bl_status == 0x11) && + (g_bl_data.bl_error == 0x20)) && + (tries++ < 100)) { + mdelay(1); + cyttsp_putbl(ts, 5, false, false, false); + } + } + } + + if (!(retval < CY_OK)) { + while (i < cyttsp_fw_records) { + retval = i2c_smbus_write_i2c_block_data(ts->client, CY_REG_BASE, + cyttsp_fw[i].Length, + cyttsp_fw[i].Block); + i++; + tries = 0; + do { + mdelay(100); + cyttsp_putbl(ts, 6, true, false, false); + } while (g_bl_data.bl_status != 0x10 && + g_bl_data.bl_status != 0x11 && + tries++ < 100); + cyttsp_debug("wait term f=%02X, s=%02X, e=%02X t=%d\n", \ + g_bl_data.bl_file, \ + g_bl_data.bl_status, \ + g_bl_data.bl_error, \ + tries); + if (retval < CY_OK) + break; + } + } + } + } + } + + /* reset TTSP Device back to bootloader mode */ + host_reg = CY_SOFT_RESET_MODE; + retval = i2c_smbus_write_i2c_block_data(ts->client, CY_REG_BASE, + sizeof(host_reg), &host_reg); + /* wait for TTSP Device to complete reset back to bootloader */ + tries = 0; + do { + mdelay(1); + cyttsp_putbl(ts, 3, false, false, false); + } while (g_bl_data.bl_status != 0x10 && + g_bl_data.bl_status != 0x11 && + tries++ < 100); + + /* set arg2 to non-0 to activate */ + retval = cyttsp_putbl(ts, 8, true, true, true); + + return retval; +} +#else +static int cyttsp_bootload_app(struct cyttsp *ts) +{ + cyttsp_debug("no-load new firmware \n"); + return CY_OK; +} +#endif /* CY_INCLUDE_LOAD_FILE */ + + +static int cyttsp_power_on(struct cyttsp *ts) +{ + int retval = CY_OK; + u8 host_reg; + int tries; + + cyttsp_debug("Power up \n"); + + /* check if the TTSP device has a bootloader installed */ + host_reg = CY_SOFT_RESET_MODE; + retval = i2c_smbus_write_i2c_block_data(ts->client, CY_REG_BASE, + sizeof(host_reg), &host_reg); + tries = 0; + do { + mdelay(1); + + /* set arg2 to non-0 to activate */ + retval = cyttsp_putbl(ts, 1, true, true, true); + cyttsp_info("BL%d: f=%02X s=%02X err=%02X bl=%02X%02X bld=%02X%02X R=%d\n", \ + 101, \ + g_bl_data.bl_file, g_bl_data.bl_status, \ + g_bl_data.bl_error, \ + g_bl_data.blver_hi, g_bl_data.blver_lo, \ + g_bl_data.bld_blver_hi, g_bl_data.bld_blver_lo, + retval); + cyttsp_info("BL%d: tver=%02X%02X a_id=%02X%02X aver=%02X%02X\n", \ + 102, \ + g_bl_data.ttspver_hi, g_bl_data.ttspver_lo, \ + g_bl_data.appid_hi, g_bl_data.appid_lo, \ + g_bl_data.appver_hi, g_bl_data.appver_lo); + cyttsp_info("BL%d: c_id=%02X%02X%02X\n", \ + 103, \ + g_bl_data.cid_0, g_bl_data.cid_1, g_bl_data.cid_2); + } while (!(retval < CY_OK) && + !GET_BOOTLOADERMODE(g_bl_data.bl_status) && + !(g_bl_data.bl_file == CY_OP_MODE + CY_LOW_PWR_MODE) && + tries++ < 100); + + /* is bootloader missing? */ + if (!(retval < CY_OK)) { + cyttsp_xdebug("Ret=%d Check if bootloader is missing...\n", \ + retval); + if (!GET_BOOTLOADERMODE(g_bl_data.bl_status)) { + /* skip all bl and sys info and go to op mode */ + if (!(retval < CY_OK)) { + cyttsp_xdebug("Bl is missing (ret=%d)\n", \ + retval); + host_reg = CY_OP_MODE/* + CY_LOW_PWR_MODE*/; + retval = i2c_smbus_write_i2c_block_data(ts->client, CY_REG_BASE, + sizeof(host_reg), &host_reg); + /* wait for TTSP Device to complete switch to + * Operational mode */ + mdelay(1000); + goto bypass; + } + } + } + + + /* take TTSP out of bootloader mode; go to TrueTouch operational mode */ + if (!(retval < CY_OK)) { + cyttsp_xdebug1("exit bootloader; go operational\n"); + retval = i2c_smbus_write_i2c_block_data(ts->client, + CY_REG_BASE, sizeof(bl_cmd), bl_cmd); + tries = 0; + do { + mdelay(100); + cyttsp_putbl(ts, 4, true, false, false); + cyttsp_info("BL%d: f=%02X s=%02X err=%02X bl=%02X%02X bld=%02X%02X\n", \ + 104, \ + g_bl_data.bl_file, g_bl_data.bl_status, \ + g_bl_data.bl_error, \ + g_bl_data.blver_hi, g_bl_data.blver_lo, \ + g_bl_data.bld_blver_hi, g_bl_data.bld_blver_lo); + } while (GET_BOOTLOADERMODE(g_bl_data.bl_status) && + tries++ < 100); + } + + + + if (!(retval < CY_OK) && + cyttsp_app_load()) { + if (CY_DIFF(g_bl_data.ttspver_hi, cyttsp_tts_verh()) || + CY_DIFF(g_bl_data.ttspver_lo, cyttsp_tts_verl()) || + CY_DIFF(g_bl_data.appid_hi, cyttsp_app_idh()) || + CY_DIFF(g_bl_data.appid_lo, cyttsp_app_idl()) || + CY_DIFF(g_bl_data.appver_hi, cyttsp_app_verh()) || + CY_DIFF(g_bl_data.appver_lo, cyttsp_app_verl()) || + CY_DIFF(g_bl_data.cid_0, cyttsp_cid_0()) || + CY_DIFF(g_bl_data.cid_1, cyttsp_cid_1()) || + CY_DIFF(g_bl_data.cid_2, cyttsp_cid_2()) || + cyttsp_force_fw_load()) { + cyttsp_debug("blttsp=0x%02X%02X flttsp=0x%02X%02X force=%d\n", \ + g_bl_data.ttspver_hi, g_bl_data.ttspver_lo, \ + cyttsp_tts_verh(), cyttsp_tts_verl(), \ + cyttsp_force_fw_load()); + cyttsp_debug("blappid=0x%02X%02X flappid=0x%02X%02X\n", \ + g_bl_data.appid_hi, g_bl_data.appid_lo, \ + cyttsp_app_idh(), cyttsp_app_idl()); + cyttsp_debug("blappver=0x%02X%02X flappver=0x%02X%02X\n", \ + g_bl_data.appver_hi, g_bl_data.appver_lo, \ + cyttsp_app_verh(), cyttsp_app_verl()); + cyttsp_debug("blcid=0x%02X%02X%02X flcid=0x%02X%02X%02X\n", \ + g_bl_data.cid_0, \ + g_bl_data.cid_1, \ + g_bl_data.cid_2, \ + cyttsp_cid_0(), \ + cyttsp_cid_1(), \ + cyttsp_cid_2()); + /* enter bootloader to load new app into TTSP Device */ + retval = cyttsp_bootload_app(ts); + /* take TTSP device out of bootloader mode; + * switch back to TrueTouch operational mode */ + if (!(retval < CY_OK)) { + retval = i2c_smbus_write_i2c_block_data(ts->client, + CY_REG_BASE, + sizeof(bl_cmd), bl_cmd); + /* wait for TTSP Device to complete + * switch to Operational mode */ + tries = 0; + do { + mdelay(100); + cyttsp_putbl(ts, 9, false, false, false); + } while (GET_BOOTLOADERMODE(g_bl_data.bl_status) && + tries++ < 100); + cyttsp_putbl(ts, 9, true, false, false); + } + } + } + +bypass: + /* switch to System Information mode to read versions + * and set interval registers */ + if (!(retval < CY_OK)) { + cyttsp_debug("switch to sysinfo mode \n"); + host_reg = CY_SYSINFO_MODE; + retval = i2c_smbus_write_i2c_block_data(ts->client, + CY_REG_BASE, sizeof(host_reg), &host_reg); + /* wait for TTSP Device to complete switch to SysInfo mode */ + mdelay(100); + if (!(retval < CY_OK)) { + retval = i2c_smbus_read_i2c_block_data(ts->client, + CY_REG_BASE, + sizeof(struct cyttsp_sysinfo_data_t), + (u8 *)&g_sysinfo_data); + cyttsp_debug("SI2: hst_mode=0x%02X mfg_cmd=0x%02X mfg_stat=0x%02X\n", \ + g_sysinfo_data.hst_mode, \ + g_sysinfo_data.mfg_cmd, \ + g_sysinfo_data.mfg_stat); + cyttsp_debug("SI2: bl_ver=0x%02X%02X\n", \ + g_sysinfo_data.bl_verh, \ + g_sysinfo_data.bl_verl); + cyttsp_debug("SI2: sysinfo act_int=0x%02X tch_tmout=0x%02X lp_int=0x%02X\n", \ + g_sysinfo_data.act_intrvl, \ + g_sysinfo_data.tch_tmout, \ + g_sysinfo_data.lp_intrvl); + cyttsp_info("SI%d: tver=%02X%02X a_id=%02X%02X aver=%02X%02X\n", \ + 102, \ + g_sysinfo_data.tts_verh, \ + g_sysinfo_data.tts_verl, \ + g_sysinfo_data.app_idh, \ + g_sysinfo_data.app_idl, \ + g_sysinfo_data.app_verh, \ + g_sysinfo_data.app_verl); + cyttsp_info("SI%d: c_id=%02X%02X%02X\n", \ + 103, \ + g_sysinfo_data.cid[0], \ + g_sysinfo_data.cid[1], \ + g_sysinfo_data.cid[2]); + if (!(retval < CY_OK) && + (CY_DIFF(ts->platform_data->act_intrvl, + CY_ACT_INTRVL_DFLT) || + CY_DIFF(ts->platform_data->tch_tmout, + CY_TCH_TMOUT_DFLT) || + CY_DIFF(ts->platform_data->lp_intrvl, + CY_LP_INTRVL_DFLT))) { + if (!(retval < CY_OK)) { + u8 intrvl_ray[sizeof(ts->platform_data->act_intrvl) + + sizeof(ts->platform_data->tch_tmout) + + sizeof(ts->platform_data->lp_intrvl)]; + u8 i = 0; + + intrvl_ray[i++] = + ts->platform_data->act_intrvl; + intrvl_ray[i++] = + ts->platform_data->tch_tmout; + intrvl_ray[i++] = + ts->platform_data->lp_intrvl; + + cyttsp_debug("SI2: platinfo act_intrvl=0x%02X tch_tmout=0x%02X lp_intrvl=0x%02X\n", \ + ts->platform_data->act_intrvl, \ + ts->platform_data->tch_tmout, \ + ts->platform_data->lp_intrvl); + /* set intrvl registers */ + retval = i2c_smbus_write_i2c_block_data( + ts->client, + CY_REG_ACT_INTRVL, + sizeof(intrvl_ray), intrvl_ray); + mdelay(CY_DLY_SYSINFO); + } + } + } + /* switch back to Operational mode */ + cyttsp_debug("switch back to operational mode \n"); + if (!(retval < CY_OK)) { + host_reg = CY_OP_MODE/* + CY_LOW_PWR_MODE*/; + retval = i2c_smbus_write_i2c_block_data(ts->client, + CY_REG_BASE, + sizeof(host_reg), &host_reg); + /* wait for TTSP Device to complete + * switch to Operational mode */ + mdelay(100); + } + } + /* init gesture setup; + * this is required even if not using gestures + * in order to set the active distance */ + if (!(retval < CY_OK)) { + u8 gesture_setup; + cyttsp_debug("init gesture setup \n"); + gesture_setup = ts->platform_data->gest_set; + retval = i2c_smbus_write_i2c_block_data(ts->client, + CY_REG_GEST_SET, + sizeof(gesture_setup), &gesture_setup); + mdelay(CY_DLY_DFLT); + } + + if (!(retval < CY_OK)) + ts->platform_data->power_state = CY_ACTIVE_STATE; + else + ts->platform_data->power_state = CY_IDLE_STATE; + + cyttsp_debug("Retval=%d Power state is %s\n", \ + retval, \ + ts->platform_data->power_state == CY_ACTIVE_STATE ? \ + "ACTIVE" : "IDLE"); + + return retval; +} + +/* cyttsp_initialize: Driver Initialization. This function takes + * care of the following tasks: + * 1. Create and register an input device with input layer + * 2. Take CYTTSP device out of bootloader mode; go operational + * 3. Start any timers/Work queues. */ +static int cyttsp_initialize(struct i2c_client *client, struct cyttsp *ts) +{ + struct input_dev *input_device; + int error = 0; + int retval = CY_OK; + u8 id; + + /* Create the input device and register it. */ + input_device = input_allocate_device(); + if (!input_device) { + error = -ENOMEM; + cyttsp_xdebug1("err input allocate device\n"); + goto error_free_device; + } + + if (!client) { + error = ~ENODEV; + cyttsp_xdebug1("err client is Null\n"); + goto error_free_device; + } + + if (!ts) { + error = ~ENODEV; + cyttsp_xdebug1("err context is Null\n"); + goto error_free_device; + } + + ts->input = input_device; + input_device->name = CY_I2C_NAME; + input_device->phys = ts->phys; + input_device->dev.parent = &client->dev; + + /* init the touch structures */ + ts->num_prv_st_tch = CY_NTCH; + for (id = 0; id < CY_NUM_TRK_ID; id++) { + ts->act_trk[id] = CY_NTCH; + ts->prv_mt_pos[id][CY_XPOS] = 0; + ts->prv_mt_pos[id][CY_YPOS] = 0; + } + + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) + ts->prv_mt_tch[id] = CY_IGNR_TCH; + + for (id = 0; id < CY_NUM_ST_TCH_ID; id++) + ts->prv_st_tch[id] = CY_IGNR_TCH; + + set_bit(EV_SYN, input_device->evbit); + set_bit(EV_KEY, input_device->evbit); + set_bit(EV_ABS, input_device->evbit); + set_bit(BTN_TOUCH, input_device->keybit); + set_bit(BTN_2, input_device->keybit); + if (ts->platform_data->use_gestures) + set_bit(BTN_3, input_device->keybit); + + input_set_abs_params(input_device, ABS_X, ts->platform_data->disp_minx, + ts->platform_data->disp_maxx, 0, 0); + input_set_abs_params(input_device, ABS_Y, ts->platform_data->disp_miny, + ts->platform_data->disp_maxy, 0, 0); + input_set_abs_params(input_device, + ABS_TOOL_WIDTH, 0, CY_LARGE_TOOL_WIDTH, 0 , 0); + input_set_abs_params(input_device, + ABS_PRESSURE, 0, CY_MAXZ, 0, 0); + input_set_abs_params(input_device, + ABS_HAT0X, 0, ts->platform_data->panel_maxx, 0, 0); + input_set_abs_params(input_device, + ABS_HAT0Y, 0, ts->platform_data->panel_maxy, 0, 0); + if (ts->platform_data->use_gestures) { + input_set_abs_params(input_device, + ABS_HAT1X, 0, CY_MAXZ, 0, 0); + input_set_abs_params(input_device, + ABS_HAT1Y, 0, CY_MAXZ, 0, 0); + } + if (ts->platform_data->use_mt) { + input_set_abs_params(input_device, ABS_MT_POSITION_X, + ts->platform_data->disp_minx, + ts->platform_data->disp_maxx, 0, 0); + input_set_abs_params(input_device, ABS_MT_POSITION_Y, + ts->platform_data->disp_miny, + ts->platform_data->disp_maxy, 0, 0); + input_set_abs_params(input_device, + ABS_MT_TOUCH_MAJOR, 0, CY_MAXZ, 0, 0); + input_set_abs_params(input_device, + ABS_MT_WIDTH_MAJOR, 0, CY_LARGE_TOOL_WIDTH, 0, 0); + if (ts->platform_data->use_trk_id) { + input_set_abs_params(input_device, + ABS_MT_TRACKING_ID, 0, CY_NUM_TRK_ID, 0, 0); + } + } + + /* set dummy key to make driver work with virtual keys */ + input_set_capability(input_device, EV_KEY, KEY_PROG1); + + cyttsp_info("%s: Register input device\n", CY_I2C_NAME); + error = input_register_device(input_device); + if (error) { + cyttsp_alert("%s: Failed to register input device\n", \ + CY_I2C_NAME); + retval = error; + goto error_free_device; + } + + /* Prepare our worker structure prior to setting up the timer/ISR */ + INIT_WORK(&ts->work, cyttsp_xy_worker); + + /* Power on the chip and make sure that I/Os are set as specified + * in the platform */ + if (ts->platform_data->init) + retval = ts->platform_data->init(client); + + if (!(retval < CY_OK)) + retval = cyttsp_power_on(ts); + + if (retval < 0) + goto error_free_device; + + /* Timer or Interrupt setup */ + if (ts->client->irq == 0) { + cyttsp_info("Setting up timer\n"); + setup_timer(&ts->timer, cyttsp_timer, (unsigned long) ts); + mod_timer(&ts->timer, jiffies + TOUCHSCREEN_TIMEOUT); + } else { + cyttsp_info("Setting up interrupt\n"); + /* request_irq() will also call enable_irq() */ + error = request_irq(client->irq, cyttsp_irq, + IRQF_TRIGGER_FALLING, + client->dev.driver->name, ts); + if (error) { + cyttsp_alert("error: could not request irq\n"); + retval = error; + goto error_free_irq; + } + } + + irq_cnt = 0; + irq_cnt_total = 0; + irq_err_cnt = 0; + + atomic_set(&ts->irq_enabled, 1); + retval = device_create_file(&ts->client->dev, &dev_attr_irq_enable); + if (retval < CY_OK) { + cyttsp_alert("File device creation failed: %d\n", retval); + retval = -ENODEV; + goto error_free_irq; + } + + retval = device_create_file(&client->dev, &dev_attr_cyttsp_fw_ver); + if (retval) { + cyttsp_alert("sysfs entry for firmware version failed\n"); + goto error_rm_dev_file_irq_en; + } + + sprintf(ts->cyttsp_fw_ver, "%d.%d", g_bl_data.ttspver_hi, + g_bl_data.ttspver_lo); + + retval = device_create_file(&client->dev, &dev_attr_cyttsp_update_fw); + if (retval) { + cyttsp_alert("sysfs entry for firmware update failed\n"); + goto error_rm_dev_file_fw_ver; + } + + cyttsp_info("%s: Successful registration\n", CY_I2C_NAME); + goto success; + +error_rm_dev_file_fw_ver: + device_remove_file(&client->dev, &dev_attr_cyttsp_fw_ver); +error_rm_dev_file_irq_en: + device_remove_file(&client->dev, &dev_attr_irq_enable); +error_free_irq: + cyttsp_alert("Error: Failed to register IRQ handler\n"); + free_irq(client->irq, ts); + +error_free_device: + if (input_device) + input_free_device(input_device); + +success: + return retval; +} + +/* I2C driver probe function */ +static int __devinit cyttsp_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cyttsp *ts; + int error; + int retval = CY_OK; + + cyttsp_info("Start Probe 1.2\n"); + + /* allocate and clear memory */ + ts = kzalloc(sizeof(struct cyttsp), GFP_KERNEL); + if (ts == NULL) { + cyttsp_xdebug1("err kzalloc for cyttsp\n"); + retval = -ENOMEM; + } + + /* Enable runtime PM ops, start in ACTIVE mode */ + error = pm_runtime_set_active(&client->dev); + if (error < 0) + dev_dbg(&client->dev, "unable to set runtime pm state\n"); + pm_runtime_enable(&client->dev); + + if (!(retval < CY_OK)) { + /* register driver_data */ + ts->client = client; + ts->platform_data = client->dev.platform_data; + i2c_set_clientdata(client, ts); + + error = cyttsp_initialize(client, ts); + if (error) { + cyttsp_xdebug1("err cyttsp_initialize\n"); + if (ts != NULL) { + /* deallocate memory */ + kfree(ts); + } +/* + i2c_del_driver(&cyttsp_driver); +*/ + retval = -ENODEV; + } else + cyttsp_openlog(); + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + if (!(retval < CY_OK)) { + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend = cyttsp_early_suspend; + ts->early_suspend.resume = cyttsp_late_resume; + register_early_suspend(&ts->early_suspend); + } +#endif /* CONFIG_HAS_EARLYSUSPEND */ + device_init_wakeup(&client->dev, ts->platform_data->wakeup); + + cyttsp_info("Start Probe %s\n", \ + (retval < CY_OK) ? "FAIL" : "PASS"); + + return retval; +} + +/* Function to manage power-on resume */ +static int cyttsp_resume(struct device *dev) +{ + struct cyttsp *ts = dev_get_drvdata(dev); + int retval = CY_OK; + + cyttsp_debug("Wake Up\n"); + + if (device_may_wakeup(dev)) { + if (ts->client->irq) + disable_irq_wake(ts->client->irq); + return 0; + } + + /* re-enable the interrupt prior to wake device */ + if (ts->client->irq) + enable_irq(ts->client->irq); + + if (ts->platform_data->use_sleep && + (ts->platform_data->power_state != CY_ACTIVE_STATE)) { + if (ts->platform_data->resume) + retval = ts->platform_data->resume(ts->client); + if (!(retval < CY_OK)) { + /* take TTSP device out of bootloader mode; + * switch back to TrueTouch operational mode */ + if (!(retval < CY_OK)) { + int tries; + retval = i2c_smbus_write_i2c_block_data(ts->client, + CY_REG_BASE, + sizeof(bl_cmd), bl_cmd); + /* wait for TTSP Device to complete + * switch to Operational mode */ + tries = 0; + do { + mdelay(100); + cyttsp_putbl(ts, 16, false, false, false); + } while (GET_BOOTLOADERMODE(g_bl_data.bl_status) && + tries++ < 100); + cyttsp_putbl(ts, 16, true, false, false); + } + } + } + + if (!(retval < CY_OK) && + (GET_HSTMODE(g_bl_data.bl_file) == CY_OK)) { + ts->platform_data->power_state = CY_ACTIVE_STATE; + + /* re-enable the timer after resuming */ + if (ts->client->irq == 0) + mod_timer(&ts->timer, jiffies + TOUCHSCREEN_TIMEOUT); + } else + retval = -ENODEV; + + cyttsp_debug("Wake Up %s\n", \ + (retval < CY_OK) ? "FAIL" : "PASS"); + + return retval; +} + + +/* Function to manage low power suspend */ +static int cyttsp_suspend(struct device *dev) +{ + struct cyttsp *ts = dev_get_drvdata(dev); + u8 sleep_mode = CY_OK; + int retval = CY_OK; + + cyttsp_debug("Enter Sleep\n"); + + if (device_may_wakeup(dev)) { + if (ts->client->irq) + enable_irq_wake(ts->client->irq); + return 0; + } + + /* disable worker */ + if (ts->client->irq == 0) + del_timer(&ts->timer); + else + disable_irq_nosync(ts->client->irq); + retval = cancel_work_sync(&ts->work); + + if (retval) + enable_irq(ts->client->irq); + + if (!(retval < CY_OK)) { + if (ts->platform_data->use_sleep && + (ts->platform_data->power_state == CY_ACTIVE_STATE)) { + if (ts->platform_data->use_sleep & CY_USE_DEEP_SLEEP_SEL) + sleep_mode = CY_DEEP_SLEEP_MODE; + else + sleep_mode = CY_LOW_PWR_MODE; + + retval = i2c_smbus_write_i2c_block_data(ts->client, + CY_REG_BASE, + sizeof(sleep_mode), &sleep_mode); + } + } + + if (!(retval < CY_OK)) { + if (sleep_mode == CY_DEEP_SLEEP_MODE) + ts->platform_data->power_state = CY_SLEEP_STATE; + else if (sleep_mode == CY_LOW_PWR_MODE) + ts->platform_data->power_state = CY_LOW_PWR_STATE; + } + + cyttsp_debug("Sleep Power state is %s\n", \ + (ts->platform_data->power_state == CY_ACTIVE_STATE) ? \ + "ACTIVE" : \ + ((ts->platform_data->power_state == CY_SLEEP_STATE) ? \ + "SLEEP" : "LOW POWER")); + + return retval; +} + +/* registered in driver struct */ +static int __devexit cyttsp_remove(struct i2c_client *client) +{ + struct cyttsp *ts; + int err; + + cyttsp_alert("Unregister\n"); + + pm_runtime_set_suspended(&client->dev); + pm_runtime_disable(&client->dev); + + device_init_wakeup(&client->dev, 0); + /* clientdata registered on probe */ + ts = i2c_get_clientdata(client); + device_remove_file(&ts->client->dev, &dev_attr_irq_enable); + device_remove_file(&client->dev, &dev_attr_cyttsp_fw_ver); + device_remove_file(&client->dev, &dev_attr_cyttsp_update_fw); + + /* Start cleaning up by removing any delayed work and the timer */ + if (cancel_delayed_work((struct delayed_work *)&ts->work) < CY_OK) + cyttsp_alert("error: could not remove work from workqueue\n"); + + /* free up timer or irq */ + if (ts->client->irq == 0) { + err = del_timer(&ts->timer); + if (err < CY_OK) + cyttsp_alert("error: failed to delete timer\n"); + } else + free_irq(client->irq, ts); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ts->early_suspend); +#endif /* CONFIG_HAS_EARLYSUSPEND */ + + /* housekeeping */ + if (ts != NULL) + kfree(ts); + + cyttsp_alert("Leaving\n"); + + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void cyttsp_early_suspend(struct early_suspend *handler) +{ + struct cyttsp *ts; + + ts = container_of(handler, struct cyttsp, early_suspend); + cyttsp_suspend(&ts->client->dev); +} + +static void cyttsp_late_resume(struct early_suspend *handler) +{ + struct cyttsp *ts; + + ts = container_of(handler, struct cyttsp, early_suspend); + cyttsp_resume(&ts->client->dev); +} +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +static int cyttsp_init(void) +{ + int ret; + + cyttsp_info("Cypress TrueTouch(R) Standard Product\n"); + cyttsp_info("I2C Touchscreen Driver (Built %s @ %s)\n", \ + __DATE__, __TIME__); + + cyttsp_ts_wq = create_singlethread_workqueue("cyttsp_ts_wq"); + if (cyttsp_ts_wq == NULL) { + cyttsp_debug("No memory for cyttsp_ts_wq\n"); + return -ENOMEM; + } + + ret = i2c_add_driver(&cyttsp_driver); + + return ret; +} + +static void cyttsp_exit(void) +{ + if (cyttsp_ts_wq) + destroy_workqueue(cyttsp_ts_wq); + return i2c_del_driver(&cyttsp_driver); +} + +module_init(cyttsp_init); +module_exit(cyttsp_exit); +MODULE_FIRMWARE("ttsp.fw"); + diff --git a/drivers/input/touchscreen/cyttsp_fw.h b/drivers/input/touchscreen/cyttsp_fw.h new file mode 100755 index 0000000000000..f14153e0dec80 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_fw.h @@ -0,0 +1,4307 @@ +/* Header file for: + * Cypress TrueTouch(TM) Standard Product touchscreen drivers. + * drivers/input/touchscreen/cyttsp_fw.h + * + * Copyright (C) 2009, 2010 Cypress Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Cypress reserves the right to make changes without further notice + * to the materials described herein. Cypress does not assume any + * liability arising out of the application described herein. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#define CYTTSP_BL_OST_LEN 1 +#define CYTTSP_BL_CMD_LEN 2 +#define CYTTSP_BL_KEY_LEN 8 +#define CYTTSP_LD_ADR_LEN 2 +#define CYTTSP_LD_DAT_LEN 64 +#define CYTTSP_LD_CHK_LEN 2 +#define CYTTSP_LD_BLK_LEN (CYTTSP_BL_OST_LEN + CYTTSP_BL_CMD_LEN + CYTTSP_BL_KEY_LEN + \ + CYTTSP_LD_ADR_LEN + CYTTSP_LD_DAT_LEN + CYTTSP_LD_CHK_LEN) + +typedef struct cyttsp_ld_blk_ray_t { + unsigned short Record; + unsigned short Length; + unsigned char Command; + unsigned short Address; + unsigned char Block[CYTTSP_LD_BLK_LEN]; +} cyttsp_ld_blk_ray, *pcyttsp_ld_blk_ray; + +cyttsp_ld_blk_ray cyttsp_fw[] = { + { + 0, + 11, + 0x38, + -1, + { + 0x00, 0xFF, 0x38, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + } + }, + { + 1, + 79, + 0x39, + 0x002C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x2C, 0x40, 0x7D, 0x0B, 0x68, 0x30, 0x30, 0x30, 0x30, 0x7E, 0x30, 0x30, 0x30, 0x7D, 0x10, 0x12, 0x7E, 0x7D, 0x10, 0x36, 0x7E, 0x7E, 0x30, 0x30, 0x30, 0x7E, 0x30, 0x30, 0x30, 0x7D, 0x1F, 0x2A, 0x7E, 0x7E, 0x30, 0x30, 0x30, 0x7E, 0x30, 0x30, 0x30, 0x7E, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x7E, 0x30, 0x30, 0x30, 0x7E, 0x30, 0x30, 0x30, 0x7D, 0x20, 0x70, 0x7E, 0x7E, 0x30, 0x30, 0x30, 0x5B, 0x36 + } + }, + { + 2, + 79, + 0x39, + 0x002D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x2D, 0x7E, 0x30, 0x30, 0x30, 0x7E, 0x30, 0x30, 0x30, 0x7E, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x40, 0x43, 0xE6, 0x02, 0x40, 0x70, 0xCF, 0x71, 0x10, 0x62, 0xE3, 0x02, 0x70, 0xCF, 0x41, 0xFF, 0xEF, 0x50, 0x80, 0x4E, 0x5D, 0xD5, 0x08, 0x62, 0x44, 0x09 + } + }, + { + 3, + 79, + 0x39, + 0x002E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x2E, 0xD5, 0x00, 0x55, 0xFA, 0x01, 0x40, 0x50, 0x06, 0x55, 0xF8, 0x3A, 0x7C, 0x00, 0x60, 0x40, 0x40, 0x70, 0xCF, 0x71, 0x10, 0x51, 0xFA, 0x60, 0xE8, 0x70, 0xCF, 0x18, 0x60, 0xD5, 0x55, 0xF8, 0x00, 0x55, 0xF9, 0x00, 0x70, 0xCF, 0x71, 0x10, 0x41, 0x9F, 0xFE, 0x70, 0xCF, 0x62, 0xE3, 0x38, 0x62, 0xD1, 0x0F, 0x50, 0x00, 0x4E, 0x62, 0xD3, 0x0F, 0x62, 0xD0, 0x00, 0x62, 0xD5, 0x00, 0x62, 0xD4, 0x35, 0xEC + } + }, + { + 4, + 79, + 0x39, + 0x002F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x2F, 0x00, 0x71, 0xC0, 0x7C, 0x0F, 0x76, 0x62, 0xD0, 0x00, 0x50, 0x0F, 0x57, 0x74, 0x08, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x28, 0x4B, 0x51, 0xE9, 0x80, 0x04, 0x75, 0x09, 0x00, 0x62, 0xE3, 0x00, 0x08, 0x28, 0x60, 0xD5, 0x74, 0xA0, 0x4B, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0xA0, 0x1C, 0x53, 0xE8, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0xCD, 0x1D + } + }, + { + 5, + 79, + 0x39, + 0x0030, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x30, 0x3F, 0xE9, 0x47, 0xE9, 0xFF, 0xB0, 0x06, 0x5D, 0xD5, 0x74, 0x60, 0xD5, 0x18, 0x7A, 0xE8, 0xBF, 0xEB, 0x8F, 0xC9, 0x18, 0x75, 0x09, 0x00, 0x08, 0x28, 0x53, 0xE8, 0x50, 0x00, 0x3F, 0xE9, 0x47, 0xE9, 0xFF, 0xB0, 0x08, 0x5D, 0xD5, 0x74, 0x60, 0xD5, 0x50, 0x00, 0x7A, 0xE8, 0xBF, 0xEF, 0x18, 0x8F, 0xAA, 0x18, 0x70, 0xCF, 0x71, 0x10, 0x62, 0xEC, 0x10, 0x43, 0xE3, 0x00, 0x70, 0xCF, 0x62, 0x4D, 0x1E + } + }, + { + 6, + 79, + 0x39, + 0x0031, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x31, 0xE0, 0x00, 0x41, 0xFE, 0xE7, 0x43, 0xFE, 0x10, 0x70, 0xCF, 0x71, 0x10, 0x62, 0xE0, 0x53, 0x70, 0xCF, 0x62, 0xE2, 0x00, 0x7C, 0x3E, 0xD3, 0x8F, 0xFF, 0x7F, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xE9, 0x57 + } + }, + { + 7, + 79, + 0x39, + 0x0032, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x32, 0x5D, 0x04, 0x73, 0x21, 0xA0, 0xBF, 0xFA, 0x5D, 0x04, 0x73, 0x21, 0xA0, 0xBF, 0xF3, 0x5D, 0x04, 0x73, 0x21, 0xA0, 0xBF, 0xEC, 0x50, 0x18, 0x49, 0x04, 0x20, 0xAF, 0xE5, 0x60, 0xFF, 0x49, 0xC9, 0x01, 0xB0, 0x1A, 0x41, 0xD6, 0xFE, 0x70, 0xCF, 0x71, 0x10, 0x41, 0x04, 0x5F, 0x70, 0xCF, 0x43, 0xD6, 0x01, 0x40, 0x70, 0xCF, 0x71, 0x10, 0x43, 0x04, 0xA0, 0x70, 0xCF, 0x7F, 0x30, 0x30, 0x30, 0x81, 0x88 + } + }, + { + 8, + 79, + 0x39, + 0x0033, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x87 + } + }, + { + 9, + 79, + 0x39, + 0x0034, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x34, 0x0A, 0x20, 0x20, 0x51, 0x55, 0x41, 0x4C, 0x43, 0x4F, 0x4D, 0x4D, 0x20, 0x56, 0x50, 0x30, 0x34, 0x33, 0x2D, 0x48, 0x32, 0x20, 0x54, 0x4D, 0x41, 0x33, 0x30, 0x30, 0x45, 0x20, 0x46, 0x69, 0x72, 0x6D, 0x77, 0x61, 0x72, 0x65, 0x20, 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x42, 0x6C, 0x6F, 0x63, 0x6B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x56, 0x99, 0xBA + } + }, + { + 10, + 79, + 0x39, + 0x0035, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x35, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x20, 0x30, 0x32, 0x2E, 0x30, 0x34, 0x2E, 0x30, 0x30, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x69, 0x6C, 0x65, 0x64, 0x20, 0x4A, 0x75, 0x6C, 0x20, 0x31, 0x34, 0x20, 0x32, 0x30, 0x31, 0x30, 0x20, 0x31, 0x32, 0x3A, 0x35, 0x33, 0x3A, 0x31, 0x33, 0x0A, 0x20, 0x20, 0x45, 0x6E, 0x64, 0x20, 0x6F, 0x66, 0x20, 0x49, 0x44, 0x20, 0x42, 0x6C, 0x6F, 0x63, 0x6B, 0x0A, 0x0D, 0xA3 + } + }, + { + 11, + 79, + 0x39, + 0x0036, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x36, 0x00, 0x03, 0x09, 0x10, 0x16, 0x06, 0x02, 0x02, 0x02, 0x01, 0xF4, 0x00, 0x0A, 0x01, 0xF4, 0x00, 0x0A, 0x01, 0xF4, 0x00, 0x0A, 0x14, 0x19, 0x19, 0x00, 0x32, 0x02, 0x14, 0x01, 0x01, 0xE0, 0x03, 0x98, 0x0C, 0x0C, 0x00, 0x10, 0x10, 0x08, 0x00, 0x04, 0x08, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x00, 0x04, 0x08, 0x00, 0x00, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x01, 0x80, 0x10, 0x01, 0x80, 0x50, 0x2A + } + }, + { + 12, + 79, + 0x39, + 0x0037, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x37, 0x01, 0x40, 0x04, 0x02, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x40, 0x08, 0x80, 0x20, 0x80, 0x08, 0x04, 0x02, 0x40, 0x20, 0x23, 0x04, 0x21, 0x20, 0x22, 0x00, 0x61, 0x00, 0xFD, 0x00, 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA3, 0x00, 0xA8, 0x00, 0xA7, 0x00, 0x7C, 0x00, 0x7A, 0x00, 0x7B, 0x00, 0x79, 0x00, 0xCA, 0x24, 0xD6, 0x04, 0xCF, 0x00, 0xC8, 0x00, 0xA9, 0x00, 0xB7, 0x00, 0xB0, 0xB3, 0xF1 + } + }, + { + 13, + 79, + 0x39, + 0x0038, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x38, 0xCA, 0xB1, 0x0B, 0xB2, 0x00, 0xB3, 0x33, 0xB4, 0x33, 0xB5, 0x80, 0xB6, 0x00, 0x6C, 0x00, 0x6D, 0x00, 0x6E, 0x00, 0x6F, 0x00, 0xE6, 0x00, 0xE9, 0x00, 0xEC, 0x00, 0xE8, 0x20, 0xEB, 0x00, 0xEE, 0x00, 0xE7, 0x00, 0xEA, 0x00, 0xED, 0x00, 0xFF, 0x23, 0x00, 0x20, 0x20, 0x21, 0x07, 0x22, 0x40, 0x76, 0x00, 0xAF, 0x00, 0xD1, 0x00, 0xA1, 0x00, 0xD3, 0x00, 0xA3, 0x00, 0xD0, 0x00, 0xA0, 0x00, 0x69, 0x5E + } + }, + { + 14, + 79, + 0x39, + 0x0039, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x39, 0xD2, 0x00, 0xA2, 0x00, 0xDC, 0x08, 0xE1, 0xFF, 0xE2, 0x01, 0xDF, 0xFF, 0xDE, 0x02, 0xDD, 0x00, 0x99, 0x00, 0x9C, 0x00, 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0x9E, 0x00, 0xAC, 0x00, 0xFF, 0x70, 0xCF, 0x62, 0x00, 0x04, 0x70, 0xCF, 0x71, 0x10, 0x62, 0x00, 0xFF, 0x62, 0x01, 0xF6, 0x70, 0xCF, 0x62, 0x02, 0x00, 0x62, 0x01, 0x00, 0x62, 0x04, 0xAB, 0x70, 0xCF, 0x71, 0x10, 0x62, 0xF2, 0x71 + } + }, + { + 15, + 79, + 0x39, + 0x003A, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x3A, 0x04, 0xEF, 0x62, 0x05, 0xFC, 0x70, 0xCF, 0x62, 0x06, 0x00, 0x62, 0x05, 0x00, 0x62, 0x08, 0x04, 0x70, 0xCF, 0x71, 0x10, 0x62, 0x08, 0xFF, 0x62, 0x09, 0x8F, 0x70, 0xCF, 0x62, 0x0A, 0x00, 0x62, 0x09, 0x00, 0x62, 0x0C, 0x00, 0x70, 0xCF, 0x71, 0x10, 0x62, 0x0C, 0xFF, 0x62, 0x0D, 0xFF, 0x70, 0xCF, 0x62, 0x0E, 0x00, 0x62, 0x0D, 0x00, 0x62, 0x10, 0x00, 0x70, 0xCF, 0x71, 0x10, 0x62, 0x10, 0xD6, 0x3A + } + }, + { + 16, + 79, + 0x39, + 0x003B, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x3B, 0xFF, 0x62, 0x11, 0xEF, 0x70, 0xCF, 0x62, 0x12, 0x00, 0x62, 0x11, 0x00, 0x70, 0xCF, 0x7F, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0xA8, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00, 0xAB, 0x00, 0xAC, 0x00, 0xAD, 0x00, 0x88, 0x00, 0x89, 0x00, 0x8A, 0x00, 0x8B, 0x00, 0xAE, 0x00, 0xAF, 0x00, 0xB0, 0x00, 0x8C, 0x65, 0x59 + } + }, + { + 17, + 79, + 0x39, + 0x003C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x3C, 0x00, 0x8D, 0x00, 0x8E, 0x00, 0x8F, 0x00, 0xB1, 0x00, 0xB2, 0x00, 0xB3, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00, 0xB4, 0x00, 0xB5, 0x00, 0xB6, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0xB7, 0x00, 0xB8, 0x00, 0xB9, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9A, 0x00, 0x9B, 0x00, 0xBA, 0x00, 0xBB, 0x00, 0xBC, 0x00, 0x9C, 0x00, 0x9D, 0x00, 0x9E, 0x00, 0x9F, 0x00, 0xBD, 0x6D, 0x6A + } + }, + { + 18, + 79, + 0x39, + 0x003D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x3D, 0x00, 0xBE, 0x00, 0xBF, 0x00, 0xA4, 0x00, 0xC0, 0x00, 0xFF, 0x11, 0x06, 0x12, 0x02, 0x13, 0x87, 0x14, 0x03, 0x1B, 0x30, 0x1C, 0x00, 0x19, 0x24, 0x1A, 0x30, 0x0A, 0x3C, 0x0B, 0x3C, 0xFF, 0x01, 0x02, 0x06, 0x00, 0x01, 0x02, 0x01, 0x00, 0x02, 0x00, 0x02, 0x01, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x02, 0x01, 0x00, 0x73, 0x97, 0x55, 0xAE, 0x04, 0x55, 0xAF, 0xAB, 0x55, 0xB0, 0x04, 0x55, 0x6F, 0x6F + } + }, + { + 19, + 79, + 0x39, + 0x003E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x3E, 0xB1, 0x00, 0x55, 0xB2, 0x00, 0x7C, 0x0F, 0x8C, 0x7C, 0x0E, 0x61, 0x7F, 0x10, 0x70, 0xCF, 0x50, 0x00, 0x08, 0x50, 0x0D, 0x57, 0xD5, 0x7C, 0x0F, 0xBF, 0x18, 0x50, 0x01, 0x08, 0x50, 0x0E, 0x57, 0x28, 0x7C, 0x0F, 0xBF, 0x18, 0x50, 0x02, 0x08, 0x50, 0x0E, 0x57, 0xCF, 0x7C, 0x0F, 0xBF, 0x18, 0x50, 0x03, 0x08, 0x50, 0x0F, 0x57, 0x4A, 0x7C, 0x0F, 0xBF, 0x18, 0x70, 0xCF, 0x20, 0x7F, 0x38, 0x76, 0x7E + } + }, + { + 20, + 79, + 0x39, + 0x003F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x3F, 0x02, 0x10, 0x08, 0x4F, 0x52, 0xF9, 0x64, 0x08, 0x64, 0x03, 0x00, 0x54, 0xFC, 0x18, 0x18, 0x20, 0x70, 0xCF, 0x62, 0xE3, 0x00, 0x10, 0x08, 0x28, 0x39, 0xFF, 0xA0, 0x30, 0x4F, 0x54, 0xFD, 0x52, 0xFC, 0x39, 0x00, 0xA0, 0x13, 0x11, 0x06, 0xE0, 0x01, 0x70, 0xCF, 0x71, 0x10, 0x80, 0x09, 0x70, 0xCF, 0x71, 0x20, 0x80, 0x03, 0x71, 0x30, 0x18, 0x20, 0x75, 0x09, 0x00, 0x10, 0x08, 0x28, 0x4F, 0x47, 0x21 + } + }, + { + 21, + 79, + 0x39, + 0x0040, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x40, 0x59, 0xFD, 0x61, 0x00, 0x18, 0x20, 0x75, 0x09, 0x00, 0x8F, 0xC6, 0x38, 0xFC, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x08, 0x10, 0x5D, 0xD0, 0x08, 0x5D, 0xD3, 0x08, 0x5D, 0xD4, 0x08, 0x5D, 0xD5, 0x08, 0x70, 0x3F, 0x71, 0x80, 0x62, 0xD0, 0x00, 0x18, 0x60, 0xD5, 0x18, 0x60, 0xD4, 0x18, 0x60, 0xD3, 0x18, 0x60, 0xD0, 0x20, 0x18, 0x7E, 0x08, 0x51, 0x54, 0x04, 0x01, 0x51, 0x53, 0x0C, 0x00, 0x51, 0xB4, 0xFC + } + }, + { + 22, + 79, + 0x39, + 0x0041, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x41, 0x54, 0x04, 0x03, 0x51, 0x53, 0x0C, 0x02, 0x51, 0x54, 0x04, 0x05, 0x51, 0x53, 0x0C, 0x04, 0x51, 0x54, 0x04, 0x07, 0x51, 0x53, 0x0C, 0x06, 0x18, 0x08, 0x51, 0x0B, 0x04, 0x0D, 0x51, 0x0A, 0x0C, 0x0C, 0x41, 0x23, 0xFE, 0x55, 0xBB, 0x00, 0x51, 0x55, 0x60, 0x21, 0x62, 0xDB, 0xFE, 0x43, 0x23, 0x01, 0x18, 0x7E, 0x05, 0x62, 0xD0, 0x00, 0x51, 0x42, 0x08, 0x26, 0x42, 0xEF, 0x7C, 0x19, 0x73, 0xD7, 0x43 + } + }, + { + 23, + 79, + 0x39, + 0x0042, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x42, 0x7C, 0x19, 0x73, 0x62, 0xD0, 0x00, 0x18, 0x53, 0x42, 0x70, 0xBF, 0x57, 0x98, 0x62, 0xD3, 0x05, 0x52, 0x00, 0x73, 0x54, 0x00, 0x62, 0xD3, 0x05, 0x54, 0x00, 0x79, 0xDF, 0xF1, 0x7C, 0x19, 0x64, 0x7C, 0x19, 0x64, 0x70, 0xBF, 0x57, 0x98, 0x62, 0xD3, 0x05, 0x52, 0x00, 0x62, 0xD3, 0x08, 0x54, 0x00, 0x62, 0xD3, 0x07, 0x56, 0x00, 0x00, 0x79, 0xDF, 0xEE, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x7F, 0x12, 0xBA + } + }, + { + 24, + 79, + 0x39, + 0x0043, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x43, 0x5C, 0x51, 0x41, 0xE0, 0x01, 0x80, 0x13, 0x80, 0x08, 0x80, 0x01, 0x5B, 0x9F, 0xF1, 0x80, 0x77, 0x62, 0xD3, 0x05, 0x51, 0x57, 0x54, 0x00, 0x80, 0x6E, 0x62, 0xD3, 0x05, 0x51, 0x57, 0x73, 0x53, 0x46, 0x47, 0x42, 0x07, 0xB0, 0x05, 0x54, 0x00, 0x80, 0x15, 0x47, 0x42, 0x04, 0xA0, 0x10, 0x62, 0xD3, 0x05, 0x3B, 0x00, 0xA0, 0x09, 0xC0, 0x04, 0x78, 0x80, 0x02, 0x74, 0x54, 0x00, 0x62, 0xD3, 0xA3, 0xDD + } + }, + { + 25, + 79, + 0x39, + 0x0044, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x44, 0x08, 0x13, 0x00, 0xD0, 0x0E, 0x62, 0xD3, 0x02, 0x56, 0x00, 0x00, 0x3C, 0x0E, 0x00, 0xB0, 0x37, 0x80, 0x16, 0x62, 0xD3, 0x02, 0x08, 0x11, 0x05, 0xD0, 0x03, 0x50, 0x00, 0x54, 0x00, 0x18, 0x3A, 0x15, 0xD0, 0x24, 0x51, 0x0E, 0xB0, 0x20, 0x62, 0xD3, 0x08, 0x52, 0x00, 0x53, 0x45, 0x51, 0x47, 0x12, 0x45, 0x1E, 0x45, 0x00, 0x62, 0xD3, 0x07, 0x03, 0x00, 0x0E, 0x45, 0x00, 0x54, 0x00, 0x51, 0x53, 0x3E + } + }, + { + 26, + 79, + 0x39, + 0x0045, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x45, 0x45, 0x62, 0xD3, 0x08, 0x54, 0x00, 0x7F, 0x7C, 0x21, 0xD2, 0x08, 0x18, 0x7F, 0x50, 0xFF, 0x3C, 0x10, 0x80, 0xC0, 0x11, 0x34, 0x12, 0x76, 0x12, 0x34, 0x11, 0x0E, 0x11, 0x00, 0x34, 0x10, 0x0E, 0x10, 0x00, 0x53, 0x0F, 0x50, 0x00, 0x53, 0x48, 0x53, 0x49, 0x53, 0x4A, 0x53, 0x4B, 0x55, 0x46, 0x18, 0x65, 0x12, 0x6B, 0x11, 0x6B, 0x10, 0x6B, 0x4B, 0x6B, 0x4A, 0x6B, 0x49, 0x51, 0x4B, 0x1A, 0xFD, 0x93 + } + }, + { + 27, + 79, + 0x39, + 0x0046, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x46, 0x14, 0x51, 0x4A, 0x1A, 0x13, 0x51, 0x49, 0x19, 0x00, 0xC0, 0x0D, 0x53, 0x49, 0x51, 0x14, 0x14, 0x4B, 0x51, 0x13, 0x1C, 0x4A, 0x76, 0x12, 0x7A, 0x46, 0xBF, 0xD7, 0x50, 0xFF, 0x3C, 0x0F, 0x80, 0xC0, 0x11, 0x34, 0x12, 0x76, 0x12, 0x34, 0x11, 0x0E, 0x11, 0x00, 0x34, 0x10, 0x0E, 0x10, 0x00, 0x34, 0x0F, 0x7F, 0x50, 0x00, 0x53, 0x48, 0x53, 0x49, 0x53, 0x4A, 0x53, 0x4B, 0x51, 0x12, 0x04, 0xCE, 0x36 + } + }, + { + 28, + 79, + 0x39, + 0x0047, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x47, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x90, 0x54, 0x90, 0x52, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x90, 0x48, 0x90, 0x46, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x90, 0x3C, 0x90, 0x3A, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x90, 0x30, 0x90, 0x2E, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x90, 0x24, 0x90, 0x22, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0xA3, 0xE1 + } + }, + { + 29, + 79, + 0x39, + 0x0048, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x48, 0x48, 0x90, 0x18, 0x90, 0x16, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x90, 0x0C, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x90, 0x02, 0x90, 0x00, 0x70, 0xFB, 0x6E, 0x48, 0x6E, 0x49, 0x6E, 0x4A, 0x6E, 0x4B, 0x7F, 0x50, 0x00, 0x53, 0x48, 0x53, 0x49, 0x53, 0x4A, 0x53, 0x4B, 0x9F, 0xE9, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x9F, 0xDF, 0x51, 0x12, 0x04, 0x49, 0xC6, 0x28 + } + }, + { + 30, + 79, + 0x39, + 0x0049, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x9F, 0xD5, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x9F, 0xCB, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x9F, 0xC1, 0x9F, 0xBF, 0x9F, 0xBD, 0x9F, 0xBB, 0x9F, 0xB9, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x9F, 0xAF, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x9F, 0xA5, 0x9F, 0xA3, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x32, 0x01 + } + }, + { + 31, + 79, + 0x39, + 0x004A, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x4A, 0x9F, 0x99, 0x51, 0x12, 0x04, 0x49, 0x51, 0x11, 0x0C, 0x48, 0x9F, 0x8F, 0x9F, 0x8D, 0x8F, 0x8C, 0x70, 0xBF, 0x62, 0xD0, 0x00, 0x53, 0x44, 0x55, 0x0F, 0x80, 0x55, 0x10, 0x60, 0x55, 0x11, 0x00, 0x62, 0xD3, 0x02, 0x50, 0x10, 0x57, 0x98, 0x54, 0x00, 0x79, 0xDF, 0xFC, 0x62, 0xD3, 0x01, 0x51, 0x0F, 0x57, 0x1A, 0x54, 0xA0, 0x79, 0xDF, 0xFC, 0x55, 0x3D, 0x00, 0x7C, 0x17, 0x18, 0x55, 0x45, 0x6E, 0x7A + } + }, + { + 32, + 79, + 0x39, + 0x004B, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x4B, 0x00, 0x62, 0xD3, 0x01, 0x58, 0xA1, 0x51, 0x10, 0x54, 0xC5, 0x62, 0xD3, 0x01, 0x58, 0xA1, 0x51, 0x0F, 0x54, 0xA0, 0x55, 0x4A, 0x80, 0x52, 0xC5, 0x70, 0xCF, 0x71, 0x20, 0x60, 0xA5, 0x70, 0xCF, 0x62, 0xD3, 0x01, 0x58, 0xA1, 0x52, 0xA0, 0x60, 0xFD, 0x55, 0x4B, 0x10, 0x7C, 0x1B, 0x87, 0x51, 0xA0, 0x01, 0x00, 0x5C, 0x62, 0xD3, 0x02, 0x51, 0x45, 0x7C, 0x19, 0x8F, 0x43, 0xA4, 0x08, 0x47, 0x37, 0x0D + } + }, + { + 33, + 79, + 0x39, + 0x004C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x4C, 0x9F, 0x01, 0xA0, 0x03, 0x71, 0x01, 0x70, 0xCF, 0x7C, 0x1C, 0x8E, 0x7C, 0x1C, 0xE3, 0x55, 0x56, 0x00, 0x55, 0x57, 0xFF, 0x55, 0x48, 0x07, 0x62, 0xD3, 0x00, 0x58, 0x48, 0x3D, 0x70, 0x00, 0xA0, 0x2A, 0x52, 0x68, 0x08, 0x51, 0x48, 0x64, 0x5C, 0x52, 0x59, 0x20, 0x62, 0xD3, 0x02, 0x3A, 0x44, 0xD0, 0x06, 0x51, 0x4B, 0x73, 0x25, 0x00, 0x51, 0x4B, 0x67, 0x2D, 0x00, 0x52, 0x00, 0x3A, 0x56, 0x92, 0xC4 + } + }, + { + 34, + 79, + 0x39, + 0x004D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x4D, 0xC0, 0x03, 0x53, 0x56, 0x3A, 0x57, 0xD0, 0x03, 0x53, 0x57, 0x7A, 0x48, 0xDF, 0xCA, 0x68, 0x4B, 0xDF, 0x9B, 0x51, 0x4A, 0xA0, 0x42, 0x47, 0x11, 0x01, 0xB0, 0x3D, 0x58, 0xA1, 0x62, 0xD3, 0x01, 0x51, 0x57, 0x02, 0x56, 0x39, 0x1F, 0xA0, 0x30, 0xD0, 0x06, 0x51, 0x4A, 0x73, 0x25, 0xA0, 0x51, 0x4A, 0x67, 0x21, 0x7F, 0x2D, 0xA0, 0x68, 0x4A, 0x26, 0x4A, 0x7F, 0x55, 0x48, 0x07, 0x62, 0xD3, 0xBE, 0x1D + } + }, + { + 35, + 79, + 0x39, + 0x004E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x4E, 0x00, 0x58, 0x48, 0x3D, 0x70, 0x00, 0xA0, 0x0A, 0x52, 0x68, 0x5C, 0x62, 0xD3, 0x02, 0x56, 0x00, 0x10, 0x7A, 0x48, 0xDF, 0xEA, 0x8F, 0x4A, 0x47, 0x11, 0x02, 0xB0, 0x32, 0x3C, 0x56, 0x1F, 0xC0, 0x2D, 0x62, 0xD3, 0x01, 0x58, 0xA1, 0x3D, 0xC5, 0x40, 0xA0, 0x23, 0x17, 0xC5, 0x20, 0x55, 0x48, 0x07, 0x62, 0xD3, 0x00, 0x62, 0xD3, 0x00, 0x58, 0x48, 0x3D, 0x70, 0x00, 0xA0, 0x0A, 0x52, 0x68, 0xD7, 0x50 + } + }, + { + 36, + 79, + 0x39, + 0x004F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x4F, 0x5C, 0x62, 0xD3, 0x02, 0x56, 0x00, 0x10, 0x7A, 0x48, 0xDF, 0xEA, 0x8E, 0xFE, 0x76, 0xA1, 0x51, 0x45, 0x7C, 0x1A, 0xE9, 0x76, 0x45, 0x3C, 0x45, 0x03, 0xCE, 0xE7, 0x7C, 0x17, 0x55, 0x76, 0x3D, 0x3C, 0x3D, 0x09, 0xCE, 0xD7, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x70, 0xBF, 0x62, 0xD0, 0x00, 0x55, 0x44, 0x99, 0x53, 0x0F, 0x5A, 0x10, 0x55, 0x11, 0x03, 0x8E, 0xA6, 0x70, 0xBF, 0x62, 0xD0, 0x00, 0xAE, 0xFF + } + }, + { + 37, + 79, + 0x39, + 0x0050, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x50, 0x55, 0x44, 0x99, 0x53, 0x0F, 0x55, 0x10, 0x60, 0x55, 0x11, 0x01, 0x8E, 0x94, 0x70, 0xBF, 0x62, 0xD0, 0x00, 0x55, 0x44, 0x99, 0x55, 0x0F, 0x80, 0x53, 0x10, 0x55, 0x11, 0x02, 0x8E, 0x82, 0x90, 0x11, 0x55, 0x38, 0x03, 0x51, 0x38, 0x90, 0x1A, 0x76, 0x38, 0x3C, 0x38, 0x0A, 0xCF, 0xF6, 0x90, 0x66, 0x7F, 0x62, 0xD5, 0x02, 0x62, 0xD0, 0x00, 0x55, 0x34, 0x99, 0x55, 0x0E, 0x00, 0x55, 0x33, 0x76, 0x90 + } + }, + { + 38, + 79, + 0x39, + 0x0051, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x51, 0x01, 0x7F, 0x70, 0xBF, 0x62, 0xD3, 0x02, 0x62, 0xD5, 0x02, 0x62, 0xD0, 0x00, 0x11, 0x02, 0x53, 0x45, 0x51, 0x33, 0x02, 0x4C, 0x53, 0x33, 0x53, 0x32, 0x55, 0x44, 0x01, 0x58, 0x32, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x24, 0x3B, 0x12, 0xC0, 0x20, 0x3B, 0x11, 0xC0, 0x1C, 0x3B, 0x10, 0xC0, 0x18, 0x3B, 0x01, 0xC0, 0x14, 0x78, 0x3B, 0xFF, 0xC0, 0x0F, 0x3B, 0xF0, 0xC0, 0x0B, 0x3B, 0xEF, 0xC0, 0x6C, 0x7D + } + }, + { + 39, + 79, + 0x39, + 0x0052, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x52, 0x07, 0x3B, 0xEE, 0xC0, 0x03, 0x91, 0x84, 0x76, 0x32, 0x76, 0x44, 0x51, 0x4C, 0x78, 0x3A, 0x44, 0xBF, 0xCB, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x70, 0xBF, 0x62, 0xD3, 0x02, 0x62, 0xD5, 0x02, 0x62, 0xD0, 0x00, 0x55, 0x32, 0x01, 0x55, 0x44, 0x01, 0x55, 0x45, 0x00, 0x58, 0x32, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x18, 0x3B, 0x12, 0xC0, 0x14, 0x3B, 0x11, 0xC0, 0x10, 0x3B, 0x10, 0xC0, 0x0C, 0x3B, 0x06, 0xB2 + } + }, + { + 40, + 79, + 0x39, + 0x0053, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x53, 0x01, 0xC0, 0x08, 0x78, 0x3B, 0xFF, 0xC0, 0x03, 0x91, 0x41, 0x76, 0x32, 0x76, 0x44, 0x51, 0x4C, 0x78, 0x3A, 0x44, 0xBF, 0xD7, 0x55, 0x44, 0x01, 0x51, 0x4D, 0x78, 0x53, 0x45, 0x51, 0x4E, 0x12, 0x4C, 0x74, 0x74, 0x53, 0x32, 0x58, 0x32, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x18, 0x3B, 0x01, 0xC0, 0x14, 0x78, 0x3B, 0xFF, 0xC0, 0x0F, 0x3B, 0xF0, 0xC0, 0x0B, 0x3B, 0xEF, 0xC0, 0x07, 0x3B, 0xEE, 0xF2, 0x8B + } + }, + { + 41, + 79, + 0x39, + 0x0054, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x54, 0xC0, 0x03, 0x91, 0x07, 0x76, 0x32, 0x76, 0x44, 0x51, 0x4C, 0x78, 0x3A, 0x44, 0xBF, 0xD7, 0x51, 0x4C, 0x78, 0x53, 0x44, 0x55, 0x45, 0x01, 0x02, 0x4C, 0x53, 0x32, 0x58, 0x32, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x18, 0x3B, 0x11, 0xC0, 0x14, 0x3B, 0x10, 0xC0, 0x10, 0x78, 0x3B, 0xFF, 0xC0, 0x0B, 0x3B, 0xEF, 0xC0, 0x07, 0x3B, 0xEE, 0xC0, 0x03, 0x90, 0xD1, 0x51, 0x4C, 0x04, 0x32, 0x76, 0x45, 0x88, 0xB8 + } + }, + { + 42, + 79, + 0x39, + 0x0055, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x55, 0x51, 0x4D, 0x78, 0x3A, 0x45, 0xBF, 0xD5, 0x55, 0x44, 0x00, 0x55, 0x45, 0x01, 0x51, 0x4C, 0x53, 0x32, 0x58, 0x32, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x18, 0x3B, 0x12, 0xC0, 0x14, 0x3B, 0x11, 0xC0, 0x10, 0x3B, 0x01, 0xC0, 0x0C, 0x78, 0x3B, 0xF0, 0xC0, 0x07, 0x3B, 0xEF, 0xC0, 0x03, 0x90, 0x9B, 0x51, 0x4C, 0x04, 0x32, 0x76, 0x45, 0x51, 0x4D, 0x78, 0x3A, 0x45, 0xBF, 0xD5, 0x50, 0x00, 0x53, 0xA4, 0xF1 + } + }, + { + 43, + 79, + 0x39, + 0x0056, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x56, 0x44, 0x53, 0x45, 0x55, 0x32, 0x00, 0x5C, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x0F, 0x3B, 0x12, 0xC0, 0x0B, 0x3B, 0x11, 0xC0, 0x07, 0x3B, 0x01, 0xC0, 0x03, 0x90, 0x70, 0x55, 0x44, 0x00, 0x51, 0x4D, 0x78, 0x53, 0x45, 0x51, 0x4E, 0x12, 0x4C, 0x74, 0x53, 0x32, 0x5C, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x10, 0x3B, 0x01, 0xC0, 0x0C, 0x78, 0x3B, 0xF0, 0xC0, 0x07, 0x3B, 0xEF, 0xC0, 0x03, 0x90, 0x4B, 0x9F, 0xE8 + } + }, + { + 44, + 79, + 0x39, + 0x0057, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x57, 0x55, 0x45, 0x00, 0x51, 0x4C, 0x78, 0x53, 0x44, 0x53, 0x32, 0x5C, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x10, 0x3B, 0x11, 0xC0, 0x0C, 0x3B, 0x10, 0xC0, 0x08, 0x78, 0x3B, 0xFF, 0xC0, 0x03, 0x90, 0x2B, 0x51, 0x4D, 0x53, 0x45, 0x51, 0x4C, 0x53, 0x44, 0x51, 0x4E, 0x7A, 0x44, 0x7A, 0x45, 0x53, 0x32, 0x5C, 0x52, 0x00, 0x3A, 0x16, 0xC0, 0x10, 0x78, 0x3B, 0xFF, 0xC0, 0x0B, 0x3B, 0xEF, 0xC0, 0x07, 0x3B, 0x21 + } + }, + { + 45, + 79, + 0x39, + 0x0058, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x58, 0x3B, 0xEE, 0xC0, 0x03, 0x90, 0x05, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x51, 0x43, 0x01, 0x03, 0x3A, 0x0E, 0xC0, 0x0F, 0x51, 0x45, 0x3F, 0x34, 0x51, 0x44, 0x3F, 0x34, 0x51, 0x32, 0x3F, 0x34, 0x76, 0x0E, 0x7F, 0x84, 0x88, 0x8C, 0x90, 0x94, 0x98, 0x9C, 0x9C, 0x9C, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x20, 0x40, 0x62, 0xD0, 0x00, 0x55, 0x4C, 0x11, 0x55, 0x4D, 0x09, 0x55, 0x4E, 0x98, 0xAB, 0x02 + } + }, + { + 46, + 79, + 0x39, + 0x0059, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x59, 0x55, 0x4F, 0x03, 0x55, 0x50, 0x97, 0x55, 0x51, 0x01, 0x55, 0x52, 0xDF, 0x55, 0x15, 0x08, 0x55, 0x16, 0x08, 0x55, 0x17, 0x08, 0x55, 0x42, 0x1C, 0x55, 0x43, 0x04, 0x55, 0xA2, 0x00, 0x55, 0xA3, 0x00, 0x55, 0xA4, 0x48, 0x55, 0xA5, 0x04, 0x55, 0xA6, 0x08, 0x55, 0xA9, 0x01, 0x55, 0xA7, 0x0C, 0x55, 0xA8, 0x05, 0x55, 0x18, 0x04, 0x55, 0xAD, 0x02, 0x55, 0x40, 0x00, 0x55, 0x3F, 0x00, 0x51, 0xE1, 0x6F + } + }, + { + 47, + 79, + 0x39, + 0x005A, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x5A, 0xA9, 0xA0, 0x08, 0x51, 0xA7, 0x58, 0xA8, 0x7C, 0x18, 0xF9, 0x70, 0xBF, 0x62, 0xD3, 0x01, 0x57, 0x3F, 0x50, 0x09, 0x28, 0x54, 0xA0, 0x79, 0xDF, 0xF9, 0x70, 0x3F, 0x71, 0xC0, 0x5D, 0xFC, 0x70, 0xCF, 0x71, 0x10, 0x62, 0x76, 0x07, 0x43, 0xE2, 0x08, 0x70, 0xCF, 0x71, 0x20, 0x62, 0xA4, 0x01, 0x62, 0xC0, 0x00, 0x39, 0x04, 0xD0, 0x04, 0x43, 0xC8, 0x04, 0x7C, 0x19, 0x41, 0x70, 0xCF, 0x71, 0x3B, 0x24 + } + }, + { + 48, + 79, + 0x39, + 0x005B, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x5B, 0x20, 0x43, 0x81, 0x0E, 0x43, 0x85, 0x0E, 0x43, 0x89, 0x0E, 0x43, 0x8D, 0x0E, 0x43, 0x91, 0x0E, 0x43, 0x95, 0x0E, 0x43, 0x99, 0x0E, 0x43, 0x9D, 0x0E, 0x70, 0xCF, 0x55, 0x9A, 0x07, 0x55, 0x9C, 0x02, 0x55, 0x9E, 0x06, 0x55, 0x9D, 0x00, 0x50, 0x48, 0x57, 0x00, 0x7C, 0x18, 0xB7, 0x71, 0x30, 0x62, 0x1B, 0x40, 0x70, 0xCF, 0x62, 0xA2, 0x10, 0x7C, 0x2A, 0xD8, 0x50, 0x04, 0x7C, 0x18, 0xE7, 0x6B, 0x85 + } + }, + { + 49, + 79, + 0x39, + 0x005C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x5C, 0x70, 0xCF, 0x7C, 0x19, 0x4E, 0x7F, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x00, 0x03, 0x06, 0x09, 0x0C, 0x0F, 0x12, 0x15, 0x18, 0x62, 0xD0, 0x00, 0x70, 0xCF, 0x71, 0x10, 0x51, 0x3D, 0xF0, 0x60, 0x5C, 0x51, 0x3D, 0xF0, 0x75, 0x73, 0x53, 0x09, 0x5E, 0x00, 0x22, 0x09, 0x61, 0x00, 0x70, 0xCF, 0x71, 0x20, 0x51, 0x3D, 0xFE, 0xE9, 0x5C, 0x51, 0x3D, 0xFE, 0xED, 0x53, 0x09, 0xBF, 0x2E + } + }, + { + 50, + 79, + 0x39, + 0x005D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x5D, 0x5E, 0x00, 0x2A, 0x09, 0x61, 0x00, 0x70, 0xCF, 0x51, 0x3D, 0xFF, 0xBA, 0x53, 0xA0, 0x51, 0x3D, 0xFF, 0xBD, 0x53, 0xA1, 0x7F, 0x70, 0xCF, 0x71, 0x20, 0x51, 0x3D, 0xFE, 0xC5, 0x5C, 0x51, 0x3D, 0xFE, 0xC9, 0x73, 0x53, 0x09, 0x5E, 0x00, 0x22, 0x09, 0x61, 0x00, 0x70, 0xCF, 0x71, 0x10, 0x51, 0x3D, 0xF0, 0x10, 0x5C, 0x51, 0x3D, 0xF0, 0x25, 0x53, 0x09, 0x5E, 0x00, 0x2A, 0x09, 0x61, 0x00, 0x4E, 0x4D + } + }, + { + 51, + 79, + 0x39, + 0x005E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x5E, 0x70, 0xCF, 0x7F, 0x0C, 0x0C, 0x00, 0x10, 0x10, 0x08, 0x00, 0x04, 0x08, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x00, 0x04, 0x08, 0x00, 0x00, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x01, 0x80, 0x10, 0x01, 0x80, 0x01, 0x40, 0x04, 0x02, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x02, 0x40, 0x08, 0x80, 0x20, 0x80, 0x08, 0x04, 0x02, 0x40, 0x20, 0x62, 0xD0, 0x00, 0x55, 0x9D, 0x00, 0x51, 0xA6, 0x91, 0x11, 0xD4 + } + }, + { + 52, + 79, + 0x39, + 0x005F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x5F, 0x76, 0x51, 0xA4, 0x58, 0xA3, 0x7C, 0x18, 0xB7, 0x51, 0xA5, 0x7C, 0x18, 0xE7, 0x70, 0xCF, 0x71, 0x20, 0x50, 0x00, 0x60, 0x80, 0x60, 0x84, 0x60, 0x88, 0x60, 0x8C, 0x60, 0x90, 0x60, 0x94, 0x60, 0x98, 0x60, 0x9C, 0x60, 0x82, 0x60, 0x86, 0x60, 0x8A, 0x60, 0x8E, 0x60, 0x92, 0x60, 0x96, 0x60, 0x9A, 0x60, 0x9E, 0x60, 0xC0, 0x43, 0x81, 0x04, 0x43, 0x85, 0x04, 0x43, 0x89, 0x04, 0x43, 0x8D, 0x86, 0xBF + } + }, + { + 53, + 79, + 0x39, + 0x0060, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x60, 0x04, 0x43, 0x91, 0x04, 0x43, 0x95, 0x04, 0x43, 0x99, 0x04, 0x43, 0x9D, 0x04, 0x71, 0x30, 0x71, 0x30, 0x62, 0x1F, 0x00, 0x62, 0x1B, 0x70, 0x62, 0x13, 0x87, 0x70, 0xCF, 0x71, 0x10, 0x55, 0x09, 0x19, 0x51, 0x09, 0xFF, 0x5E, 0x5C, 0x51, 0x09, 0xFF, 0x73, 0x53, 0x45, 0x5E, 0x00, 0x2A, 0x45, 0x61, 0x00, 0x7A, 0x09, 0xDF, 0xEC, 0x70, 0xCF, 0x41, 0xA2, 0x3F, 0x55, 0x40, 0x00, 0x55, 0x3F, 0xDC, 0x6C + } + }, + { + 54, + 79, + 0x39, + 0x0061, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x61, 0x00, 0x51, 0xA9, 0xA0, 0x08, 0x51, 0xA7, 0x58, 0xA8, 0x7C, 0x18, 0xF9, 0x7F, 0x41, 0xE0, 0xFB, 0x71, 0x30, 0x41, 0x1B, 0xBF, 0x70, 0xCF, 0x71, 0x20, 0x41, 0x81, 0xFD, 0x41, 0x85, 0xFD, 0x41, 0x89, 0xFD, 0x41, 0x8D, 0xFD, 0x41, 0x91, 0xFD, 0x41, 0x95, 0xFD, 0x41, 0x99, 0xFD, 0x41, 0x9D, 0xFD, 0x70, 0xCF, 0x41, 0xA2, 0xEF, 0x7C, 0x19, 0x5A, 0x70, 0xCF, 0x71, 0x10, 0x41, 0xE2, 0xF7, 0x90, 0xD5 + } + }, + { + 55, + 79, + 0x39, + 0x0062, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x62, 0x70, 0xCF, 0x7F, 0x7C, 0x19, 0x41, 0x70, 0xCF, 0x71, 0x10, 0x43, 0xE2, 0x08, 0x71, 0x30, 0x43, 0x1B, 0x40, 0x70, 0xCF, 0x71, 0x20, 0x43, 0x81, 0x02, 0x43, 0x85, 0x02, 0x43, 0x89, 0x02, 0x43, 0x8D, 0x02, 0x43, 0x91, 0x02, 0x43, 0x95, 0x02, 0x43, 0x99, 0x02, 0x43, 0x9D, 0x02, 0x70, 0xCF, 0x43, 0xA2, 0x10, 0x7C, 0x19, 0x4E, 0x7F, 0x62, 0xD0, 0x00, 0x53, 0x49, 0x5A, 0x48, 0x53, 0x9B, 0x24, 0xFE + } + }, + { + 56, + 79, + 0x39, + 0x0063, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x63, 0x5B, 0x21, 0x01, 0xA0, 0x06, 0x2E, 0x9E, 0x01, 0x80, 0x04, 0x26, 0x9E, 0xFE, 0x68, 0x48, 0x6E, 0x49, 0x51, 0x49, 0x78, 0x70, 0xCF, 0x71, 0x20, 0x60, 0xC9, 0x70, 0xCF, 0x7C, 0x2A, 0xBA, 0x7F, 0x00, 0x04, 0x0C, 0x1C, 0x3C, 0x7C, 0xFC, 0x62, 0xD0, 0x00, 0x53, 0x3E, 0x76, 0x3E, 0xFF, 0xF0, 0x26, 0x9C, 0x03, 0x2C, 0x9C, 0x7C, 0x2A, 0xBA, 0x7F, 0x62, 0xD0, 0x00, 0x53, 0xA7, 0x5A, 0xA8, 0xA6, 0x03 + } + }, + { + 57, + 79, + 0x39, + 0x0064, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x64, 0x90, 0x01, 0x7F, 0x62, 0xD0, 0x00, 0x08, 0x5A, 0x3E, 0x55, 0x40, 0xFF, 0x55, 0x3F, 0x01, 0x78, 0xA0, 0x0A, 0x06, 0x40, 0xFF, 0x0E, 0x3F, 0x01, 0x78, 0xBF, 0xF8, 0x51, 0x3E, 0x68, 0x3F, 0x6E, 0x40, 0x78, 0xDF, 0xFA, 0x16, 0x40, 0x7F, 0x1E, 0x3F, 0x00, 0x18, 0x78, 0x64, 0x64, 0x26, 0x9C, 0x03, 0x2C, 0x9C, 0x7C, 0x2A, 0xBA, 0x7F, 0x62, 0xD0, 0x00, 0x78, 0x53, 0x9A, 0x7C, 0x2A, 0xBA, 0x11, 0xDA + } + }, + { + 58, + 79, + 0x39, + 0x0065, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x65, 0x7F, 0x70, 0xCF, 0x71, 0x20, 0x62, 0xA7, 0x89, 0x62, 0xA7, 0x49, 0x70, 0xCF, 0x7F, 0x70, 0xCF, 0x71, 0x20, 0x49, 0xC8, 0x08, 0xAF, 0xFC, 0x70, 0xCF, 0x7F, 0x70, 0xCF, 0x71, 0x20, 0x62, 0xA7, 0x09, 0x70, 0xCF, 0x7F, 0x70, 0xBF, 0x62, 0xD0, 0x00, 0x55, 0x41, 0x00, 0x93, 0xA8, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x70, 0xBF, 0x62, 0xD0, 0x00, 0x55, 0x41, 0x02, 0x93, 0x99, 0x70, 0x3F, 0x71, 0xB9, 0x2B + } + }, + { + 59, + 79, + 0x39, + 0x0066, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x66, 0xC0, 0x7F, 0x57, 0x98, 0x50, 0x0A, 0x28, 0x21, 0xE0, 0xB0, 0x04, 0x79, 0xDF, 0xF7, 0x7F, 0x70, 0xCF, 0x71, 0x10, 0x64, 0xE0, 0x01, 0x80, 0x09, 0x80, 0x75, 0x80, 0xE1, 0x81, 0x3D, 0x81, 0x49, 0x41, 0x00, 0xFD, 0x41, 0x0C, 0xF7, 0x41, 0x0C, 0xBF, 0x41, 0x00, 0x7F, 0x41, 0x10, 0xF7, 0x41, 0x10, 0xBF, 0x70, 0xCF, 0x71, 0x20, 0x5D, 0xF7, 0x53, 0x9F, 0x70, 0xFE, 0x43, 0x80, 0x04, 0x52, 0xE2, 0x7E + } + }, + { + 60, + 79, + 0x39, + 0x0067, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x67, 0x06, 0x60, 0x83, 0x06, 0x68, 0x06, 0x2E, 0x70, 0x01, 0x43, 0x84, 0x08, 0x52, 0x03, 0x60, 0x87, 0x06, 0x69, 0x03, 0x2E, 0x71, 0x01, 0x43, 0x88, 0x04, 0x52, 0x00, 0x60, 0x8B, 0x06, 0x6A, 0x00, 0x2E, 0x72, 0x01, 0x43, 0x8C, 0x02, 0x52, 0x09, 0x60, 0x8F, 0x06, 0x6B, 0x09, 0x2E, 0x73, 0x01, 0x43, 0x90, 0x01, 0x52, 0x0C, 0x60, 0x93, 0x06, 0x6C, 0x0C, 0x2E, 0x74, 0x01, 0x43, 0x94, 0x02, 0x8C, 0xD3 + } + }, + { + 61, + 79, + 0x39, + 0x0068, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x68, 0x52, 0x0F, 0x60, 0x97, 0x06, 0x6D, 0x0F, 0x2E, 0x75, 0x01, 0x43, 0xA3, 0x3F, 0x7F, 0x41, 0x04, 0xBF, 0x41, 0x0C, 0xFB, 0x41, 0x0C, 0xDF, 0x41, 0x00, 0xDF, 0x41, 0x10, 0xFB, 0x41, 0x10, 0xDF, 0x70, 0xCF, 0x71, 0x20, 0x5D, 0xF7, 0x53, 0x9F, 0x70, 0xFE, 0x43, 0x80, 0x02, 0x52, 0x07, 0x60, 0x83, 0x06, 0x68, 0x07, 0x2E, 0x70, 0x01, 0x43, 0x84, 0x04, 0x52, 0x04, 0x60, 0x87, 0x06, 0x69, 0x46, 0x48 + } + }, + { + 62, + 79, + 0x39, + 0x0069, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x69, 0x04, 0x2E, 0x71, 0x01, 0x43, 0x88, 0x02, 0x52, 0x01, 0x60, 0x8B, 0x06, 0x6A, 0x01, 0x2E, 0x72, 0x01, 0x43, 0x8C, 0x04, 0x52, 0x0A, 0x60, 0x8F, 0x06, 0x6B, 0x0A, 0x2E, 0x73, 0x01, 0x43, 0x90, 0x02, 0x52, 0x0D, 0x60, 0x93, 0x06, 0x6C, 0x0D, 0x2E, 0x74, 0x01, 0x43, 0x94, 0x04, 0x52, 0x10, 0x60, 0x97, 0x06, 0x6D, 0x10, 0x2E, 0x75, 0x01, 0x43, 0xA3, 0x3F, 0x7F, 0x41, 0x08, 0xF7, 0x41, 0xC5, 0x47 + } + }, + { + 63, + 79, + 0x39, + 0x006A, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x6A, 0x0C, 0xFD, 0x41, 0x0C, 0xEF, 0x41, 0x08, 0x7F, 0x41, 0x10, 0xFD, 0x70, 0xCF, 0x71, 0x20, 0x5D, 0xF7, 0x53, 0x9F, 0x70, 0xFE, 0x43, 0x80, 0x01, 0x52, 0x08, 0x60, 0x83, 0x06, 0x68, 0x08, 0x2E, 0x70, 0x01, 0x43, 0x84, 0x02, 0x52, 0x05, 0x60, 0x87, 0x06, 0x69, 0x05, 0x2E, 0x71, 0x01, 0x43, 0x88, 0x01, 0x52, 0x02, 0x60, 0x8B, 0x06, 0x6A, 0x02, 0x2E, 0x72, 0x01, 0x43, 0x8C, 0x08, 0x52, 0x57, 0x6C + } + }, + { + 64, + 79, + 0x39, + 0x006B, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x6B, 0x0B, 0x60, 0x8F, 0x06, 0x6B, 0x0B, 0x2E, 0x73, 0x01, 0x43, 0x90, 0x04, 0x52, 0x0E, 0x60, 0x93, 0x06, 0x6C, 0x0E, 0x2E, 0x74, 0x01, 0x43, 0xA3, 0x1F, 0x7F, 0x70, 0xCF, 0x71, 0x20, 0x5D, 0xF7, 0x53, 0x9F, 0x70, 0xFE, 0x43, 0xA3, 0x00, 0x7F, 0x7F, 0x70, 0xCF, 0x71, 0x10, 0x64, 0xE0, 0x01, 0x80, 0x09, 0x80, 0x34, 0x80, 0x5F, 0x80, 0x84, 0x80, 0x8B, 0x43, 0x00, 0x02, 0x43, 0x0C, 0x08, 0x1D, 0xF9 + } + }, + { + 65, + 79, + 0x39, + 0x006C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x6C, 0x43, 0x0C, 0x40, 0x43, 0x00, 0x80, 0x43, 0x10, 0x08, 0x43, 0x10, 0x40, 0x70, 0xCF, 0x71, 0x20, 0x41, 0x80, 0xFB, 0x41, 0x84, 0xF7, 0x41, 0x88, 0xFB, 0x41, 0x8C, 0xFD, 0x41, 0x90, 0xFE, 0x41, 0x94, 0xFD, 0x62, 0xA3, 0x00, 0x80, 0x5E, 0x43, 0x04, 0x40, 0x43, 0x0C, 0x04, 0x43, 0x0C, 0x20, 0x43, 0x00, 0x20, 0x43, 0x10, 0x04, 0x43, 0x10, 0x20, 0x70, 0xCF, 0x71, 0x20, 0x41, 0x80, 0xFD, 0x9E, 0xFC + } + }, + { + 66, + 79, + 0x39, + 0x006D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x6D, 0x41, 0x84, 0xFB, 0x41, 0x88, 0xFD, 0x41, 0x8C, 0xFB, 0x41, 0x90, 0xFD, 0x41, 0x94, 0xFB, 0x62, 0xA3, 0x00, 0x80, 0x31, 0x43, 0x08, 0x08, 0x43, 0x0C, 0x02, 0x43, 0x0C, 0x10, 0x43, 0x08, 0x80, 0x43, 0x10, 0x02, 0x70, 0xCF, 0x71, 0x20, 0x41, 0x80, 0xFE, 0x41, 0x84, 0xFD, 0x41, 0x88, 0xFE, 0x41, 0x8C, 0xF7, 0x41, 0x90, 0xFB, 0x62, 0xA3, 0x00, 0x80, 0x0A, 0x70, 0xCF, 0x71, 0x20, 0x62, 0x2E, 0x1D + } + }, + { + 67, + 79, + 0x39, + 0x006E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x6E, 0xA3, 0x00, 0x80, 0x01, 0x70, 0xCF, 0x7F, 0x62, 0xD3, 0x00, 0x57, 0x07, 0x52, 0x70, 0x54, 0x80, 0x52, 0x68, 0x54, 0x78, 0x51, 0xA0, 0x56, 0x70, 0x00, 0x54, 0x68, 0x79, 0xDF, 0xEF, 0x7F, 0x62, 0xD5, 0x00, 0x62, 0xD3, 0x00, 0x58, 0xA0, 0x55, 0x09, 0x88, 0x50, 0x0A, 0x28, 0x21, 0x1F, 0x3F, 0x09, 0x75, 0x3C, 0x09, 0x99, 0xCF, 0xF4, 0x7F, 0x62, 0xD0, 0x00, 0x55, 0xAA, 0x00, 0x55, 0xAC, 0xE5, 0x8C + } + }, + { + 68, + 79, + 0x39, + 0x006F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x6F, 0x00, 0x57, 0x07, 0x62, 0xD3, 0x00, 0x5B, 0x3D, 0x80, 0x00, 0xA0, 0x5A, 0x10, 0x64, 0x5C, 0x52, 0x58, 0x53, 0x56, 0x52, 0x59, 0x53, 0x57, 0x51, 0x3E, 0x6E, 0x56, 0x6E, 0x57, 0x78, 0xDF, 0xFA, 0x51, 0x40, 0x14, 0x57, 0x51, 0x3F, 0x1C, 0x56, 0xA0, 0x0E, 0xD0, 0x06, 0x55, 0x57, 0x00, 0x80, 0x04, 0x55, 0x57, 0xFF, 0x55, 0x56, 0x00, 0x20, 0x10, 0x52, 0x78, 0x5C, 0x62, 0xD3, 0x02, 0x51, 0x6C, 0x9B + } + }, + { + 69, + 79, + 0x39, + 0x0070, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x70, 0x57, 0x73, 0x54, 0x00, 0x39, 0xDC, 0xD0, 0x05, 0x39, 0x23, 0xD0, 0x04, 0x55, 0xAB, 0x01, 0x62, 0xD3, 0x08, 0x13, 0x00, 0xC0, 0x07, 0x39, 0x0F, 0xD0, 0x0B, 0x80, 0x05, 0x39, 0xF1, 0xC0, 0x05, 0x04, 0xAA, 0x76, 0xAC, 0x20, 0x79, 0xDF, 0x9C, 0x51, 0xAC, 0x47, 0xAA, 0x80, 0xA0, 0x03, 0x76, 0xAA, 0x68, 0xAA, 0x39, 0x02, 0xC0, 0x18, 0x47, 0xAA, 0x80, 0xA0, 0x03, 0x76, 0xAA, 0x68, 0xAA, 0x67, 0x92 + } + }, + { + 70, + 79, + 0x39, + 0x0071, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x71, 0x67, 0x39, 0x02, 0xC0, 0x0A, 0x47, 0xAA, 0x80, 0xA0, 0x03, 0x76, 0xAA, 0x68, 0xAA, 0x57, 0x07, 0x62, 0xD3, 0x00, 0x3D, 0x80, 0x00, 0xA0, 0x33, 0x10, 0x52, 0x78, 0x5C, 0x62, 0xD3, 0x02, 0x52, 0x00, 0x53, 0x47, 0x47, 0x42, 0x10, 0xA0, 0x1B, 0x51, 0xAA, 0x15, 0x00, 0xD0, 0x0B, 0x47, 0xAA, 0x80, 0xB0, 0x0E, 0x56, 0x00, 0x00, 0x80, 0x09, 0x47, 0xAA, 0x80, 0xA0, 0x04, 0x56, 0x00, 0xFF, 0xE5, 0x8F + } + }, + { + 71, + 79, + 0x39, + 0x0072, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x72, 0x52, 0x00, 0x73, 0x53, 0x57, 0x5B, 0x7C, 0x10, 0xC0, 0x20, 0x79, 0xDF, 0xC4, 0x7F, 0x62, 0xD0, 0x00, 0x70, 0xCF, 0x71, 0x20, 0x49, 0xC4, 0x01, 0xAF, 0xFC, 0x41, 0xA4, 0xF7, 0x41, 0xC4, 0xFE, 0x5D, 0xA8, 0x53, 0x59, 0x5D, 0xA9, 0x53, 0x58, 0x5D, 0xAB, 0x53, 0x5B, 0x5D, 0xAC, 0x53, 0x5A, 0x5D, 0xAE, 0x53, 0x5D, 0x5D, 0xAF, 0x53, 0x5C, 0x5D, 0xB1, 0x53, 0x5F, 0x5D, 0xB2, 0x53, 0x5E, 0x2F, 0x24 + } + }, + { + 72, + 79, + 0x39, + 0x0073, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x73, 0x5D, 0xB4, 0x53, 0x61, 0x5D, 0xB5, 0x53, 0x60, 0x5D, 0xB7, 0x53, 0x63, 0x5D, 0xB8, 0x53, 0x62, 0x5D, 0xBA, 0x53, 0x65, 0x5D, 0xBB, 0x53, 0x64, 0x5D, 0xBD, 0x53, 0x67, 0x5D, 0xBE, 0x53, 0x66, 0x70, 0xCF, 0x7F, 0x62, 0xD3, 0x00, 0x57, 0x07, 0x5B, 0x3D, 0x70, 0x00, 0xA0, 0x25, 0x10, 0x64, 0x5C, 0x51, 0x3E, 0x6F, 0x58, 0x6F, 0x59, 0x78, 0xDF, 0xFA, 0x51, 0x40, 0x15, 0x59, 0x51, 0x3F, 0x50, 0x67 + } + }, + { + 73, + 79, + 0x39, + 0x0074, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x74, 0x1D, 0x58, 0xA0, 0x0E, 0xD0, 0x06, 0x56, 0x59, 0x00, 0x80, 0x04, 0x56, 0x59, 0xFF, 0x56, 0x58, 0x00, 0x20, 0x79, 0xDF, 0xD4, 0x7F, 0x55, 0x3D, 0x00, 0x55, 0x37, 0x01, 0x7C, 0x17, 0x18, 0x9E, 0x7E, 0x58, 0xA1, 0x62, 0xD3, 0x01, 0x52, 0xA0, 0x60, 0xFD, 0x52, 0xC5, 0x70, 0xCF, 0x71, 0x20, 0x60, 0xA5, 0x70, 0xCF, 0x9E, 0x51, 0x50, 0x00, 0x57, 0x88, 0x9C, 0x53, 0x43, 0xA4, 0x08, 0x47, 0x25, 0x12 + } + }, + { + 74, + 79, + 0x39, + 0x0075, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x75, 0x9F, 0x01, 0xA0, 0x03, 0x71, 0x01, 0x70, 0xCF, 0x51, 0x3D, 0xA0, 0x05, 0x9E, 0x6A, 0x51, 0x3D, 0x9F, 0x3C, 0x50, 0x00, 0x9D, 0x93, 0x62, 0xD3, 0x01, 0x58, 0xA1, 0x52, 0xA1, 0x60, 0xFD, 0x52, 0xC6, 0x70, 0xCF, 0x71, 0x20, 0x60, 0xA5, 0x70, 0xCF, 0x9E, 0x1C, 0x50, 0x01, 0x57, 0x88, 0x9C, 0x1E, 0x43, 0xA4, 0x08, 0x47, 0x9F, 0x01, 0xA0, 0x03, 0x71, 0x01, 0x70, 0xCF, 0x9E, 0x39, 0x9F, 0x07, 0xD7 + } + }, + { + 75, + 79, + 0x39, + 0x0076, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x76, 0x0D, 0x50, 0x01, 0x9D, 0x64, 0x62, 0xD3, 0x01, 0x58, 0xA1, 0x52, 0xA2, 0x60, 0xFD, 0x52, 0xC7, 0x70, 0xCF, 0x71, 0x20, 0x60, 0xA5, 0x70, 0xCF, 0x9D, 0xED, 0x50, 0x02, 0x57, 0x88, 0x9B, 0xEF, 0x43, 0xA4, 0x08, 0x47, 0x9F, 0x01, 0xA0, 0x03, 0x71, 0x01, 0x70, 0xCF, 0x9E, 0x0A, 0x9E, 0xDE, 0x50, 0x02, 0x9D, 0x35, 0x7C, 0x17, 0x55, 0x76, 0x3D, 0x3C, 0x3D, 0x09, 0xCF, 0x5F, 0x62, 0xD3, 0x43, 0x50 + } + }, + { + 76, + 79, + 0x39, + 0x0077, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x77, 0x00, 0x9D, 0xC4, 0x9D, 0xF3, 0x7C, 0x14, 0x1F, 0x55, 0x37, 0x00, 0x7F, 0x43, 0xE0, 0x08, 0x7F, 0x41, 0xE0, 0xF7, 0x7F, 0x62, 0xE6, 0x04, 0x62, 0xD0, 0x00, 0x5A, 0x53, 0x53, 0x54, 0x10, 0x08, 0x51, 0x55, 0x08, 0x38, 0x03, 0x4F, 0x50, 0x00, 0x54, 0xFE, 0x54, 0xFD, 0x01, 0x08, 0x54, 0xFF, 0x48, 0xFC, 0x01, 0xA0, 0x09, 0x52, 0xFB, 0x05, 0xFE, 0x52, 0xFA, 0x0D, 0xFD, 0x6F, 0xFD, 0x6F, 0xCC, 0x63 + } + }, + { + 77, + 79, + 0x39, + 0x0078, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x78, 0xFE, 0x6F, 0xFC, 0x7B, 0xFF, 0xBF, 0xEA, 0x52, 0xFC, 0x60, 0xE8, 0x52, 0xFE, 0x60, 0xE7, 0x62, 0xE6, 0x00, 0x62, 0xE6, 0x01, 0x38, 0xFA, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x5D, 0xF7, 0x08, 0x70, 0xFE, 0x57, 0xF0, 0x50, 0x00, 0x62, 0xE6, 0x04, 0x62, 0xE8, 0x01, 0x62, 0xE7, 0x00, 0x62, 0xE6, 0x00, 0x62, 0xE6, 0x01, 0x62, 0xDA, 0xF7, 0x49, 0xDA, 0x08, 0xAF, 0xFC, 0x62, 0xDA, 0xF7, 0x08, 0xF1, 0xAE + } + }, + { + 78, + 79, + 0x39, + 0x0079, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x79, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x51, 0x00, 0x18, 0x49, 0xDA, 0x08, 0xB0, 0x04, 0x40, 0x80, 0x05, 0x74, 0x62, 0xDA, 0xF7, 0x79, 0xBF, 0xE0, 0x49, 0xDA, 0x08, 0xA0, 0x02, 0x74, 0x62, 0xE6, 0x04, 0x60, 0xE8, 0x62, 0xE7, 0x00, 0x62, 0xE6, 0x00, 0x62, 0xE6, 0x01, 0x62, 0xD0, 0x00, 0x53, 0x55, 0x55, 0x53, 0x00, 0x55, 0x54, 0x01, 0x7E, 0x55, 0x77 + } + }, + { + 79, + 79, + 0x39, + 0x007A, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x7A, 0x5D, 0xF7, 0x08, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x51, 0x05, 0x58, 0x04, 0x7E, 0x5D, 0xF7, 0x08, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x51, 0x01, 0x58, 0x00, 0x7E, 0x5D, 0xF7, 0x08, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x51, 0x03, 0x58, 0x02, 0x7E, 0x5D, 0xF7, 0x08, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x51, 0x07, 0x58, 0x06, 0x7E, 0x08, 0x08, 0x10, 0x4F, 0x5D, 0xF7, 0x54, 0xFD, 0x70, 0x3F, 0x71, 0xC0, 0x9C, 0x06 + } + }, + { + 80, + 79, + 0x39, + 0x007B, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x7B, 0x20, 0x18, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x53, 0x05, 0x5A, 0x04, 0x7E, 0x08, 0x08, 0x10, 0x4F, 0x5D, 0xF7, 0x54, 0xFD, 0x70, 0x3F, 0x71, 0xC0, 0x20, 0x18, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x53, 0x01, 0x5A, 0x00, 0x7E, 0x08, 0x08, 0x10, 0x4F, 0x5D, 0xF7, 0x54, 0xFD, 0x70, 0x3F, 0x71, 0xC0, 0x20, 0x18, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x53, 0x03, 0x5A, 0x02, 0x7E, 0x0E, 0x1E, 0x3D, 0x7A, 0xE3, 0x95 + } + }, + { + 81, + 79, + 0x39, + 0x007C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x7C, 0x07, 0x03, 0x00, 0x00, 0x07, 0x0E, 0x1E, 0x3D, 0x03, 0x01, 0x00, 0x00, 0x1E, 0x3D, 0x7A, 0xF6, 0x0E, 0x07, 0x01, 0x00, 0x58, 0x45, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x79, 0xDF, 0xF6, 0x7A, 0x44, 0xBF, 0xF0, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x08, 0x10, 0x70, 0x3F, 0x71, 0x80, 0x5D, 0xD3, 0x08, 0x5D, 0xD0, 0x08, 0x62, 0xD0, 0x00, 0x51, 0xB6, 0x60, 0xD3, 0x2E, 0xB3, 0x80, 0x48, 0x60 + } + }, + { + 82, + 79, + 0x39, + 0x007D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x7D, 0x49, 0xD7, 0x08, 0xA0, 0x09, 0x26, 0xB3, 0xF0, 0x2E, 0xB3, 0x00, 0x80, 0x08, 0x49, 0xD7, 0x20, 0xA0, 0x03, 0x80, 0xA6, 0x51, 0xB3, 0x21, 0x0E, 0xE0, 0x01, 0x80, 0x11, 0x80, 0x67, 0x80, 0x79, 0x80, 0x47, 0x80, 0x96, 0x80, 0x94, 0x80, 0x92, 0x80, 0x90, 0x80, 0x97, 0x5D, 0xD8, 0x21, 0xFE, 0x39, 0x48, 0xA0, 0x06, 0x62, 0xD7, 0x00, 0x80, 0x8A, 0x49, 0xD8, 0x01, 0xB0, 0x0F, 0x55, 0xBA, 0x69, 0xA3 + } + }, + { + 83, + 79, + 0x39, + 0x007E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x7E, 0x02, 0x26, 0xB3, 0xF0, 0x2E, 0xB3, 0x02, 0x62, 0xD7, 0x10, 0x80, 0x77, 0x55, 0xBA, 0x01, 0x26, 0xB3, 0xF0, 0x2E, 0xB3, 0x06, 0x5F, 0xB5, 0xB4, 0x51, 0xB7, 0x02, 0xB5, 0x5C, 0x52, 0x00, 0x60, 0xD8, 0x76, 0xB5, 0x62, 0xD7, 0x14, 0x80, 0x5B, 0x51, 0xB8, 0x78, 0x3A, 0xB5, 0xC0, 0x0F, 0x51, 0xB7, 0x02, 0xB5, 0x5C, 0x52, 0x00, 0x60, 0xD8, 0x76, 0xB5, 0x2E, 0xB3, 0x20, 0x60, 0xD8, 0x62, 0x18, 0x02 + } + }, + { + 84, + 79, + 0x39, + 0x007F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x7F, 0xD7, 0x04, 0x80, 0x3F, 0x5D, 0xD8, 0x3A, 0xB8, 0xD0, 0x2B, 0xA0, 0x29, 0x53, 0xB5, 0x53, 0xB4, 0x26, 0xB3, 0xF0, 0x2E, 0xB3, 0x04, 0x80, 0x18, 0x51, 0xB9, 0x78, 0x3A, 0xB5, 0xC0, 0x16, 0x51, 0xB7, 0x02, 0xB5, 0x5C, 0x5D, 0xD8, 0x54, 0x00, 0x2E, 0xB3, 0x10, 0x76, 0xB5, 0x80, 0x01, 0x62, 0xD7, 0x10, 0x80, 0x0F, 0x62, 0xD7, 0x00, 0x80, 0x0A, 0x26, 0xB3, 0xF0, 0x2E, 0xB3, 0x00, 0x55, 0xFC, 0xCB + } + }, + { + 85, + 79, + 0x39, + 0x0080, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x80, 0xBA, 0x00, 0x18, 0x60, 0xD0, 0x18, 0x60, 0xD3, 0x20, 0x18, 0x7E, 0x62, 0xD0, 0x00, 0x70, 0xCF, 0x71, 0x10, 0x41, 0x04, 0x5F, 0x43, 0x05, 0xA0, 0x70, 0xCF, 0x26, 0xAF, 0x5F, 0x51, 0xAF, 0x60, 0x04, 0x55, 0xBA, 0x00, 0x90, 0x1F, 0x90, 0x24, 0x40, 0x40, 0x40, 0x40, 0x40, 0x50, 0x00, 0x53, 0xB4, 0x70, 0xCF, 0x71, 0x10, 0x43, 0x04, 0xA0, 0x43, 0x05, 0xA0, 0x70, 0xCF, 0x2E, 0xAF, 0xA0, 0xAC, 0x2C + } + }, + { + 86, + 79, + 0x39, + 0x0081, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x81, 0x51, 0xAF, 0x60, 0x04, 0x7F, 0x41, 0xE0, 0x7F, 0x43, 0xE0, 0x80, 0x7F, 0x43, 0xD6, 0x31, 0x7F, 0x41, 0xE0, 0x7F, 0x41, 0xD6, 0xFE, 0x7F, 0x62, 0xD0, 0x00, 0x4F, 0x52, 0xFD, 0x53, 0xB8, 0x52, 0xFC, 0x53, 0xB9, 0x52, 0xFB, 0x53, 0xB7, 0x52, 0xFA, 0x53, 0xB6, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x76, 0xBB, 0xD0, 0x04, 0x55, 0xBB, 0xFF, 0x7E, 0x43, 0xE1, 0x01, 0x7F, 0x41, 0xE1, 0xFE, 0x7F, 0xB7, 0x43 + } + }, + { + 87, + 79, + 0x39, + 0x0082, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x82, 0x43, 0x23, 0x01, 0x7F, 0x54, 0x00, 0x70, 0xFE, 0x41, 0x23, 0xFE, 0x18, 0x60, 0x22, 0x18, 0x60, 0x23, 0x18, 0x70, 0x3F, 0x71, 0xC0, 0x7E, 0x30, 0x62, 0xD0, 0x00, 0x53, 0xF8, 0x5D, 0xF7, 0x08, 0x21, 0xC0, 0xB0, 0x07, 0x56, 0x01, 0x00, 0x55, 0xF8, 0x00, 0x51, 0xF8, 0x70, 0x3F, 0x71, 0x80, 0x60, 0xD3, 0x55, 0xFD, 0x01, 0x3C, 0xFD, 0x01, 0xB0, 0xAE, 0x70, 0xCF, 0x71, 0x10, 0x5D, 0xE0, 0xFE, 0xD2 + } + }, + { + 88, + 79, + 0x39, + 0x0083, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x83, 0x08, 0x21, 0xF8, 0x49, 0xFE, 0x08, 0xB0, 0x0A, 0x49, 0xFE, 0x10, 0xB0, 0x09, 0x29, 0x01, 0x80, 0x07, 0x29, 0x02, 0x80, 0x03, 0x29, 0x00, 0x60, 0xE0, 0x70, 0xCF, 0x80, 0x01, 0x65, 0xFD, 0x3C, 0xFD, 0x02, 0xB0, 0x84, 0x65, 0xFD, 0x70, 0xCF, 0x71, 0x10, 0x49, 0xE4, 0x08, 0xA0, 0x05, 0x70, 0xCF, 0x80, 0x20, 0x70, 0xCF, 0x52, 0x00, 0x53, 0xFA, 0x51, 0xFD, 0x39, 0x04, 0xB0, 0x69, 0x08, 0xF8, 0xC7 + } + }, + { + 89, + 79, + 0x39, + 0x0084, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x84, 0x10, 0x50, 0x03, 0x55, 0xF8, 0x3A, 0x7C, 0x00, 0x60, 0x20, 0x18, 0x53, 0xFD, 0x3C, 0xF8, 0x00, 0xA0, 0x09, 0x55, 0xFF, 0x00, 0x55, 0xFD, 0x10, 0x80, 0x37, 0x65, 0xFD, 0x52, 0x00, 0x53, 0xFA, 0x52, 0x02, 0x53, 0xFB, 0x52, 0x01, 0x60, 0xD4, 0x52, 0x05, 0x53, 0xFC, 0x55, 0xFE, 0x56, 0x51, 0xFD, 0x39, 0x08, 0xB0, 0x33, 0x08, 0x10, 0x50, 0x02, 0x55, 0xF8, 0x3A, 0x7C, 0x00, 0x60, 0x20, 0x70, 0xB8 + } + }, + { + 90, + 79, + 0x39, + 0x0085, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x85, 0x18, 0x53, 0xFD, 0x55, 0xFF, 0x01, 0x3C, 0xF8, 0x00, 0xA0, 0x04, 0x55, 0xFF, 0x00, 0x65, 0xFD, 0x3C, 0xFD, 0x10, 0xB0, 0x13, 0x18, 0x70, 0xCF, 0x71, 0x10, 0x60, 0xE0, 0x70, 0xCF, 0x65, 0xFD, 0x51, 0xFF, 0x3C, 0xFD, 0x20, 0xA0, 0x04, 0x30, 0x8F, 0xFE, 0x62, 0xD0, 0x00, 0x62, 0xD5, 0x00, 0x62, 0xD4, 0x00, 0x7E, 0x30, 0x30, 0x30, 0x51, 0xF8, 0x70, 0x3F, 0x71, 0x80, 0x60, 0xD3, 0x52, 0x35, 0x43 + } + }, + { + 91, + 79, + 0x39, + 0x0086, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x86, 0x02, 0x53, 0xFB, 0x52, 0x01, 0x60, 0xD5, 0x52, 0x03, 0x74, 0x53, 0xFD, 0x52, 0x04, 0x53, 0xFE, 0x50, 0x00, 0x6C, 0x00, 0x6A, 0x6C, 0x00, 0x6A, 0x6C, 0x00, 0x6A, 0x6C, 0x00, 0x6A, 0x6C, 0x00, 0x6A, 0x6C, 0x00, 0x6A, 0x6C, 0x00, 0x6A, 0x08, 0x52, 0x00, 0x5C, 0x18, 0x08, 0x28, 0x3F, 0xFB, 0x18, 0x75, 0xB0, 0x02, 0x74, 0x7A, 0xFE, 0xB0, 0x05, 0x7A, 0xFD, 0xA0, 0x0F, 0x3C, 0xFB, 0x00, 0x37, 0x48 + } + }, + { + 92, + 79, + 0x39, + 0x0087, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x87, 0xBF, 0xEB, 0x08, 0x5D, 0xD5, 0x74, 0x60, 0xD5, 0x18, 0x8F, 0xE2, 0x62, 0xD0, 0x00, 0x62, 0xD5, 0x00, 0x7E, 0x70, 0xBF, 0x62, 0xD0, 0x00, 0x47, 0x36, 0x40, 0xB0, 0x0F, 0x47, 0x36, 0x80, 0xA0, 0x0A, 0x26, 0x36, 0x3F, 0x51, 0x36, 0x3A, 0x0E, 0xA0, 0x01, 0x70, 0xBF, 0x51, 0x0E, 0xA1, 0x1A, 0x55, 0xBE, 0x00, 0x3C, 0x0E, 0x02, 0xC0, 0x04, 0x55, 0xBE, 0x01, 0x5F, 0x36, 0x0E, 0x62, 0xD4, 0xE5, 0xA5 + } + }, + { + 93, + 79, + 0x39, + 0x0088, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x88, 0x02, 0x62, 0xD5, 0x01, 0x55, 0x0E, 0x00, 0x55, 0x35, 0x99, 0x55, 0x34, 0xE0, 0x3E, 0x35, 0x53, 0x45, 0x3E, 0x35, 0x53, 0x44, 0x3E, 0x35, 0x53, 0x32, 0x3C, 0x45, 0x02, 0xC0, 0x94, 0x51, 0x4D, 0x11, 0x03, 0x3A, 0x45, 0xC0, 0x8C, 0x3C, 0x44, 0x02, 0xC0, 0x87, 0x51, 0x4C, 0x11, 0x03, 0x3A, 0x44, 0xC0, 0x7F, 0x62, 0xD3, 0x02, 0x58, 0x32, 0x52, 0xFE, 0x53, 0x23, 0x52, 0xFF, 0x53, 0x24, 0x10, 0xFC + } + }, + { + 94, + 79, + 0x39, + 0x0089, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x89, 0x52, 0x00, 0x53, 0x25, 0x52, 0x01, 0x53, 0x26, 0x52, 0x02, 0x53, 0x27, 0x5B, 0x12, 0x4C, 0x5C, 0x52, 0xFE, 0x53, 0x1E, 0x52, 0xFF, 0x53, 0x1F, 0x52, 0x00, 0x53, 0x20, 0x52, 0x01, 0x53, 0x21, 0x52, 0x02, 0x53, 0x22, 0x5B, 0x12, 0x4C, 0x5C, 0x52, 0xFE, 0x53, 0x19, 0x52, 0xFF, 0x53, 0x1A, 0x52, 0x00, 0x53, 0x1B, 0x52, 0x01, 0x53, 0x1C, 0x52, 0x02, 0x53, 0x1D, 0x51, 0x32, 0x02, 0x4C, 0xF8, 0xCD + } + }, + { + 95, + 79, + 0x39, + 0x008A, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x8A, 0x5C, 0x52, 0xFE, 0x53, 0x28, 0x52, 0xFF, 0x53, 0x29, 0x52, 0x00, 0x53, 0x2A, 0x52, 0x01, 0x53, 0x2B, 0x52, 0x02, 0x53, 0x2C, 0x5B, 0x02, 0x4C, 0x5C, 0x52, 0xFE, 0x53, 0x2D, 0x52, 0xFF, 0x53, 0x2E, 0x52, 0x00, 0x53, 0x2F, 0x52, 0x01, 0x53, 0x30, 0x52, 0x02, 0x53, 0x31, 0x90, 0x62, 0x80, 0x44, 0x7C, 0x25, 0x70, 0x90, 0x5B, 0x55, 0xBD, 0x00, 0x51, 0x45, 0xA0, 0x18, 0x51, 0x4D, 0x78, 0xB8, 0x4E + } + }, + { + 96, + 79, + 0x39, + 0x008B, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x8B, 0x3A, 0x45, 0xA0, 0x11, 0x51, 0x44, 0xA0, 0x1A, 0x51, 0x4C, 0x78, 0x3A, 0x44, 0xA0, 0x13, 0x7C, 0x26, 0x0E, 0x80, 0x21, 0x51, 0x44, 0xA0, 0x17, 0x51, 0x4C, 0x78, 0x3A, 0x44, 0xA0, 0x10, 0x80, 0x11, 0x51, 0x45, 0xA0, 0x0A, 0x51, 0x4D, 0x78, 0x3A, 0x45, 0xA0, 0x03, 0x80, 0x04, 0x55, 0xBD, 0x01, 0x7C, 0x26, 0x94, 0x51, 0x0E, 0x3A, 0x43, 0xC0, 0x05, 0x50, 0xFF, 0x80, 0x0C, 0x7C, 0x23, 0x96, 0x0B + } + }, + { + 97, + 79, + 0x39, + 0x008C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x8C, 0x48, 0x7A, 0x36, 0x51, 0x36, 0xBF, 0x07, 0x51, 0x0E, 0x55, 0x36, 0x00, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x55, 0x13, 0x00, 0x51, 0x1F, 0x02, 0x20, 0x0E, 0x13, 0x00, 0x02, 0x21, 0x0E, 0x13, 0x00, 0x02, 0x24, 0x0E, 0x13, 0x00, 0x02, 0x25, 0x0E, 0x13, 0x00, 0x02, 0x26, 0x0E, 0x13, 0x00, 0x02, 0x29, 0x0E, 0x13, 0x00, 0x02, 0x2A, 0x0E, 0x13, 0x00, 0x02, 0x2B, 0x0E, 0x13, 0x00, 0x3C, 0x13, 0xFB, 0xD6 + } + }, + { + 98, + 79, + 0x39, + 0x008D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x8D, 0x00, 0xA0, 0x03, 0x50, 0xFF, 0x53, 0xBC, 0x7F, 0x3C, 0xBE, 0x01, 0xB0, 0x23, 0x50, 0x00, 0x53, 0x19, 0x53, 0x1A, 0x53, 0x1B, 0x53, 0x1C, 0x53, 0x1D, 0x53, 0x1E, 0x53, 0x22, 0x53, 0x23, 0x53, 0x27, 0x53, 0x28, 0x53, 0x2C, 0x53, 0x2D, 0x53, 0x2E, 0x53, 0x2F, 0x53, 0x30, 0x53, 0x31, 0x62, 0xD5, 0x01, 0x06, 0x34, 0x03, 0x50, 0x00, 0x53, 0x0F, 0x53, 0x10, 0x53, 0x12, 0x53, 0x13, 0x62, 0xD5, 0x8B + } + }, + { + 99, + 79, + 0x39, + 0x008E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x8E, 0xD3, 0x00, 0x10, 0x51, 0x31, 0x57, 0x17, 0x03, 0x19, 0x0E, 0x13, 0x00, 0x79, 0xDF, 0xF9, 0x53, 0x14, 0x20, 0x51, 0xBC, 0x80, 0x08, 0x3C, 0x13, 0x00, 0xA0, 0x03, 0x50, 0xFF, 0x3F, 0x34, 0x51, 0x31, 0x02, 0x2C, 0x0E, 0x10, 0x00, 0x02, 0x27, 0x0E, 0x10, 0x00, 0x02, 0x22, 0x0E, 0x10, 0x00, 0x02, 0x1D, 0x0E, 0x10, 0x00, 0x12, 0x19, 0x1E, 0x10, 0x00, 0x12, 0x1E, 0x1E, 0x10, 0x00, 0x12, 0x8E, 0xFE + } + }, + { + 100, + 79, + 0x39, + 0x008F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x8F, 0x23, 0x1E, 0x10, 0x00, 0x12, 0x28, 0x1E, 0x10, 0x00, 0x12, 0x2D, 0x1E, 0x10, 0x00, 0x64, 0x6B, 0x10, 0x02, 0x30, 0x0E, 0x10, 0x00, 0x02, 0x2B, 0x0E, 0x10, 0x00, 0x02, 0x26, 0x0E, 0x10, 0x00, 0x02, 0x21, 0x0E, 0x10, 0x00, 0x02, 0x1C, 0x0E, 0x10, 0x00, 0x12, 0x1A, 0x1E, 0x10, 0x00, 0x12, 0x1F, 0x1E, 0x10, 0x00, 0x12, 0x24, 0x1E, 0x10, 0x00, 0x12, 0x29, 0x1E, 0x10, 0x00, 0x12, 0x2E, 0x29, 0x35 + } + }, + { + 101, + 79, + 0x39, + 0x0090, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x90, 0x1E, 0x10, 0x00, 0x53, 0x11, 0x7C, 0x11, 0x4D, 0x51, 0x44, 0x06, 0x12, 0x80, 0x0C, 0x11, 0x0E, 0x10, 0x00, 0x47, 0x10, 0x80, 0xA0, 0x0A, 0x55, 0x10, 0x00, 0x55, 0x11, 0x00, 0x55, 0x12, 0x00, 0x7C, 0x12, 0x26, 0x47, 0x42, 0x08, 0xA0, 0x36, 0x62, 0xD3, 0x01, 0x4D, 0x34, 0x51, 0x48, 0x3B, 0x00, 0xC0, 0x1E, 0xB0, 0x09, 0x51, 0x49, 0x3B, 0x01, 0xA0, 0x21, 0xC0, 0x14, 0x51, 0x48, 0x3A, 0x02, 0xE8 + } + }, + { + 102, + 79, + 0x39, + 0x0091, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x91, 0x4F, 0xB0, 0x07, 0x51, 0x49, 0x3A, 0x50, 0xA0, 0x13, 0x7A, 0x49, 0x1E, 0x48, 0x00, 0x80, 0x0C, 0x51, 0x48, 0x2A, 0x49, 0xA0, 0x06, 0x76, 0x49, 0x0E, 0x48, 0x00, 0x4D, 0x34, 0x51, 0x48, 0x3A, 0x4F, 0xC0, 0x0B, 0xB0, 0x13, 0x51, 0x49, 0x3A, 0x50, 0xC0, 0x03, 0xB0, 0x0B, 0x51, 0x48, 0x3F, 0x34, 0x51, 0x49, 0x3F, 0x34, 0x80, 0x09, 0x51, 0x4F, 0x3F, 0x34, 0x51, 0x50, 0x3F, 0x34, 0x50, 0x45, 0x6F + } + }, + { + 103, + 79, + 0x39, + 0x0092, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x92, 0x00, 0x53, 0x10, 0x53, 0x12, 0x51, 0x2D, 0x02, 0x2E, 0x0E, 0x10, 0x00, 0x02, 0x2F, 0x0E, 0x10, 0x00, 0x02, 0x30, 0x0E, 0x10, 0x00, 0x02, 0x31, 0x0E, 0x10, 0x00, 0x12, 0x19, 0x1E, 0x10, 0x00, 0x12, 0x1A, 0x1E, 0x10, 0x00, 0x12, 0x1B, 0x1E, 0x10, 0x00, 0x12, 0x1C, 0x1E, 0x10, 0x00, 0x12, 0x1D, 0x1E, 0x10, 0x00, 0x64, 0x6B, 0x10, 0x02, 0x28, 0x0E, 0x10, 0x00, 0x02, 0x29, 0x0E, 0x10, 0xBB, 0x5C + } + }, + { + 104, + 79, + 0x39, + 0x0093, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x93, 0x00, 0x02, 0x2A, 0x0E, 0x10, 0x00, 0x02, 0x2B, 0x0E, 0x10, 0x00, 0x02, 0x2C, 0x0E, 0x10, 0x00, 0x12, 0x1E, 0x1E, 0x10, 0x00, 0x12, 0x1F, 0x1E, 0x10, 0x00, 0x12, 0x20, 0x1E, 0x10, 0x00, 0x12, 0x21, 0x1E, 0x10, 0x00, 0x12, 0x22, 0x1E, 0x10, 0x00, 0x53, 0x11, 0x7C, 0x11, 0x4D, 0x51, 0x45, 0x06, 0x12, 0x80, 0x0C, 0x11, 0x0E, 0x10, 0x00, 0x47, 0x10, 0x80, 0xA0, 0x0A, 0x55, 0x10, 0x00, 0x4E, 0x83 + } + }, + { + 105, + 79, + 0x39, + 0x0094, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x94, 0x55, 0x11, 0x00, 0x55, 0x12, 0x00, 0x7C, 0x11, 0xB3, 0x47, 0x42, 0x08, 0xA0, 0x36, 0x62, 0xD3, 0x01, 0x4D, 0x34, 0x51, 0x48, 0x3B, 0x00, 0xC0, 0x1E, 0xB0, 0x09, 0x51, 0x49, 0x3B, 0x01, 0xA0, 0x21, 0xC0, 0x14, 0x51, 0x48, 0x3A, 0x51, 0xB0, 0x07, 0x51, 0x49, 0x3A, 0x52, 0xA0, 0x13, 0x7A, 0x49, 0x1E, 0x48, 0x00, 0x80, 0x0C, 0x51, 0x48, 0x2A, 0x49, 0xA0, 0x06, 0x76, 0x49, 0x0E, 0x48, 0x31, 0x4A + } + }, + { + 106, + 79, + 0x39, + 0x0095, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x95, 0x00, 0x4D, 0x34, 0x51, 0x48, 0x3A, 0x51, 0xC0, 0x0B, 0xB0, 0x13, 0x51, 0x49, 0x3A, 0x52, 0xC0, 0x03, 0xB0, 0x0B, 0x51, 0x48, 0x3F, 0x34, 0x51, 0x49, 0x3F, 0x34, 0x80, 0x09, 0x51, 0x51, 0x3F, 0x34, 0x51, 0x52, 0x3F, 0x34, 0x62, 0xD3, 0x02, 0x76, 0x0E, 0x51, 0x0E, 0x55, 0xBC, 0x00, 0x7F, 0x55, 0x12, 0x00, 0x5F, 0x11, 0x45, 0x06, 0x11, 0xFE, 0x5F, 0x10, 0x44, 0x06, 0x10, 0xFE, 0x51, 0x97, 0x17 + } + }, + { + 107, + 79, + 0x39, + 0x0096, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x96, 0x32, 0x08, 0x51, 0x4C, 0x14, 0x32, 0x14, 0x32, 0x16, 0x32, 0x02, 0x55, 0x0F, 0x06, 0x7A, 0x0F, 0x51, 0x0F, 0xA0, 0x74, 0x47, 0x11, 0x80, 0xB0, 0x44, 0x51, 0x4D, 0x78, 0x3A, 0x11, 0xC0, 0x3D, 0x55, 0x13, 0x06, 0x7A, 0x13, 0x51, 0x13, 0xA0, 0x4F, 0x47, 0x10, 0x80, 0xB0, 0x1E, 0x51, 0x4C, 0x78, 0x3A, 0x10, 0xC0, 0x17, 0x58, 0x32, 0x62, 0xD3, 0x02, 0x52, 0x00, 0x58, 0x12, 0x62, 0xD3, 0x19, 0x1C + } + }, + { + 108, + 79, + 0x39, + 0x0097, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x97, 0x00, 0x54, 0x19, 0x76, 0x12, 0x76, 0x10, 0x76, 0x32, 0x8F, 0xD9, 0x58, 0x12, 0x62, 0xD3, 0x00, 0x56, 0x19, 0x00, 0x75, 0x5A, 0x12, 0x76, 0x10, 0x76, 0x32, 0x8F, 0xC8, 0x58, 0x12, 0x62, 0xD3, 0x00, 0x50, 0x00, 0x54, 0x19, 0x75, 0x54, 0x19, 0x75, 0x54, 0x19, 0x75, 0x54, 0x19, 0x75, 0x54, 0x19, 0x75, 0x5A, 0x12, 0x06, 0x32, 0x05, 0x76, 0x11, 0x5F, 0x10, 0x44, 0x06, 0x10, 0xFE, 0x51, 0xA0, 0x2B + } + }, + { + 109, + 79, + 0x39, + 0x0098, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x98, 0x4C, 0x11, 0x05, 0x04, 0x32, 0x8F, 0x88, 0x62, 0xD3, 0x02, 0x18, 0x53, 0x32, 0x7F, 0x62, 0xD3, 0x00, 0x3C, 0x45, 0x01, 0xB0, 0x1B, 0x57, 0x05, 0x52, 0x19, 0x6D, 0x6D, 0x6D, 0x21, 0x1F, 0x53, 0x0F, 0x6D, 0x21, 0x7F, 0x02, 0x0F, 0x54, 0x14, 0x75, 0x5B, 0x39, 0x0A, 0xBF, 0xEB, 0x80, 0x21, 0x51, 0x4D, 0x11, 0x02, 0x3A, 0x45, 0xB0, 0x19, 0x57, 0x0F, 0x52, 0x19, 0x6D, 0x6D, 0x6D, 0x21, 0x4A, 0x80 + } + }, + { + 110, + 79, + 0x39, + 0x0099, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x99, 0x1F, 0x53, 0x0F, 0x6D, 0x21, 0x7F, 0x02, 0x0F, 0x54, 0x1E, 0x75, 0x5B, 0x39, 0x14, 0xBF, 0xEB, 0x3C, 0x44, 0x01, 0xB0, 0x1D, 0x57, 0x01, 0x52, 0x19, 0x6D, 0x6D, 0x6D, 0x21, 0x1F, 0x53, 0x0F, 0x6D, 0x21, 0x7F, 0x02, 0x0F, 0x54, 0x18, 0x5B, 0x01, 0x05, 0x5C, 0x39, 0x15, 0xBF, 0xE9, 0x80, 0x23, 0x51, 0x4C, 0x11, 0x02, 0x3A, 0x44, 0xB0, 0x1B, 0x57, 0x03, 0x52, 0x19, 0x6D, 0x6D, 0x6D, 0xB7, 0x5B + } + }, + { + 111, + 79, + 0x39, + 0x009A, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x9A, 0x21, 0x1F, 0x53, 0x0F, 0x6D, 0x21, 0x7F, 0x02, 0x0F, 0x54, 0x1A, 0x5B, 0x01, 0x05, 0x5C, 0x39, 0x17, 0xBF, 0xE9, 0x7F, 0x62, 0xD3, 0x00, 0x51, 0x45, 0xB0, 0x94, 0x55, 0x19, 0x04, 0x55, 0x1A, 0x10, 0x55, 0x1B, 0x10, 0x55, 0x1C, 0x10, 0x55, 0x1D, 0x04, 0x51, 0xBD, 0xB0, 0x23, 0x51, 0x25, 0xA0, 0x0D, 0x51, 0x24, 0x5F, 0x12, 0x26, 0x5F, 0x14, 0x25, 0x92, 0x47, 0x53, 0x1B, 0x51, 0x2A, 0x43, 0x74 + } + }, + { + 112, + 79, + 0x39, + 0x009B, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x9B, 0xA0, 0x0F, 0x51, 0x29, 0x5F, 0x12, 0x2B, 0x5F, 0x14, 0x2A, 0x92, 0x37, 0x53, 0x1A, 0x53, 0x1C, 0x57, 0x04, 0x52, 0x23, 0x53, 0x4A, 0x52, 0x19, 0x53, 0x4B, 0x7C, 0x29, 0x2D, 0x50, 0x04, 0x6E, 0x4A, 0x6E, 0x4B, 0x78, 0xBF, 0xFA, 0x52, 0x28, 0x14, 0x4B, 0x1E, 0x4A, 0x00, 0x47, 0x4A, 0x80, 0xA0, 0x07, 0x55, 0x4A, 0x00, 0x55, 0x4B, 0x00, 0x47, 0x4A, 0x7F, 0xA0, 0x04, 0x55, 0x4B, 0xFF, 0xD0, 0x8F + } + }, + { + 113, + 79, + 0x39, + 0x009C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x9C, 0x51, 0x4B, 0x3C, 0xAD, 0x02, 0xA0, 0x0D, 0x3C, 0xAD, 0x03, 0xA0, 0x03, 0x80, 0x10, 0x6D, 0x21, 0x7F, 0x80, 0x0B, 0x6D, 0x21, 0x7F, 0x53, 0x4B, 0x6D, 0x21, 0x7F, 0x02, 0x4B, 0x54, 0x1E, 0x6D, 0x6D, 0x6D, 0x21, 0x1F, 0x54, 0x19, 0x6D, 0x21, 0x7F, 0x05, 0x19, 0x79, 0xDF, 0xA5, 0x51, 0x4D, 0x11, 0x01, 0x3A, 0x45, 0xB0, 0x94, 0x55, 0x2D, 0x04, 0x55, 0x2E, 0x10, 0x55, 0x2F, 0x10, 0x55, 0xF3, 0xD6 + } + }, + { + 114, + 79, + 0x39, + 0x009D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x9D, 0x30, 0x10, 0x55, 0x31, 0x04, 0x51, 0xBD, 0xB0, 0x23, 0x51, 0x25, 0xA0, 0x0D, 0x51, 0x24, 0x5F, 0x12, 0x26, 0x5F, 0x14, 0x25, 0x91, 0xAC, 0x53, 0x2F, 0x51, 0x20, 0xA0, 0x0F, 0x51, 0x1F, 0x5F, 0x12, 0x21, 0x5F, 0x14, 0x20, 0x91, 0x9C, 0x53, 0x2E, 0x53, 0x30, 0x57, 0x04, 0x52, 0x23, 0x53, 0x4A, 0x52, 0x2D, 0x53, 0x4B, 0x7C, 0x29, 0x2D, 0x50, 0x04, 0x6E, 0x4A, 0x6E, 0x4B, 0x78, 0xBF, 0x6F, 0xCF + } + }, + { + 115, + 79, + 0x39, + 0x009E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x9E, 0xFA, 0x52, 0x1E, 0x14, 0x4B, 0x1E, 0x4A, 0x00, 0x47, 0x4A, 0x80, 0xA0, 0x07, 0x55, 0x4A, 0x00, 0x55, 0x4B, 0x00, 0x47, 0x4A, 0x7F, 0xA0, 0x04, 0x55, 0x4B, 0xFF, 0x51, 0x4B, 0x3C, 0xAD, 0x02, 0xA0, 0x0D, 0x3C, 0xAD, 0x03, 0xA0, 0x03, 0x80, 0x10, 0x6D, 0x21, 0x7F, 0x80, 0x0B, 0x6D, 0x21, 0x7F, 0x53, 0x4B, 0x6D, 0x21, 0x7F, 0x02, 0x4B, 0x54, 0x28, 0x6D, 0x6D, 0x6D, 0x21, 0x1F, 0x54, 0xC2, 0x76 + } + }, + { + 116, + 79, + 0x39, + 0x009F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x9F, 0x2D, 0x6D, 0x21, 0x7F, 0x05, 0x2D, 0x79, 0xDF, 0xA5, 0x3C, 0x44, 0x00, 0xB0, 0x97, 0x55, 0x19, 0x04, 0x55, 0x1E, 0x10, 0x55, 0x23, 0x10, 0x55, 0x28, 0x10, 0x55, 0x2D, 0x04, 0x51, 0xBD, 0xB0, 0x23, 0x51, 0x25, 0xA0, 0x0D, 0x51, 0x20, 0x5F, 0x12, 0x2A, 0x5F, 0x14, 0x25, 0x91, 0x14, 0x53, 0x23, 0x51, 0x26, 0xA0, 0x0F, 0x51, 0x21, 0x5F, 0x12, 0x2B, 0x5F, 0x14, 0x26, 0x91, 0x04, 0x53, 0x38, 0x63 + } + }, + { + 117, + 79, + 0x39, + 0x00A0, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA0, 0x1E, 0x53, 0x28, 0x57, 0x14, 0x52, 0x1B, 0x53, 0x4A, 0x52, 0x19, 0x53, 0x4B, 0x7C, 0x29, 0x2D, 0x50, 0x04, 0x6E, 0x4A, 0x6E, 0x4B, 0x78, 0xBF, 0xFA, 0x52, 0x1C, 0x14, 0x4B, 0x1E, 0x4A, 0x00, 0x47, 0x4A, 0x80, 0xA0, 0x07, 0x55, 0x4A, 0x00, 0x55, 0x4B, 0x00, 0x47, 0x4A, 0x7F, 0xA0, 0x04, 0x55, 0x4B, 0xFF, 0x51, 0x4B, 0x3C, 0xAD, 0x02, 0xA0, 0x0D, 0x3C, 0xAD, 0x03, 0xA0, 0x03, 0x80, 0xA1, 0x36 + } + }, + { + 118, + 79, + 0x39, + 0x00A1, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA1, 0x10, 0x6D, 0x21, 0x7F, 0x80, 0x0B, 0x6D, 0x21, 0x7F, 0x53, 0x4B, 0x6D, 0x21, 0x7F, 0x02, 0x4B, 0x54, 0x1A, 0x6D, 0x6D, 0x6D, 0x21, 0x1F, 0x54, 0x19, 0x6D, 0x21, 0x7F, 0x05, 0x19, 0x5B, 0x11, 0x05, 0x5C, 0xDF, 0xA2, 0x51, 0x4C, 0x11, 0x01, 0x3A, 0x44, 0xB0, 0x97, 0x55, 0x1D, 0x04, 0x55, 0x22, 0x10, 0x55, 0x27, 0x10, 0x55, 0x2C, 0x10, 0x55, 0x31, 0x04, 0x51, 0xBD, 0xB0, 0x23, 0x51, 0xD2, 0x99 + } + }, + { + 119, + 79, + 0x39, + 0x00A2, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA2, 0x25, 0xA0, 0x0D, 0x51, 0x20, 0x5F, 0x12, 0x2A, 0x5F, 0x14, 0x25, 0x90, 0x76, 0x53, 0x27, 0x51, 0x24, 0xA0, 0x0F, 0x51, 0x1F, 0x5F, 0x12, 0x29, 0x5F, 0x14, 0x24, 0x90, 0x66, 0x53, 0x22, 0x53, 0x2C, 0x57, 0x14, 0x52, 0x1B, 0x53, 0x4A, 0x52, 0x1D, 0x53, 0x4B, 0x7C, 0x29, 0x2D, 0x50, 0x04, 0x6E, 0x4A, 0x6E, 0x4B, 0x78, 0xBF, 0xFA, 0x52, 0x1A, 0x14, 0x4B, 0x1E, 0x4A, 0x00, 0x47, 0x4A, 0xB3, 0x5C + } + }, + { + 120, + 79, + 0x39, + 0x00A3, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA3, 0x80, 0xA0, 0x07, 0x55, 0x4A, 0x00, 0x55, 0x4B, 0x00, 0x47, 0x4A, 0x7F, 0xA0, 0x04, 0x55, 0x4B, 0xFF, 0x51, 0x4B, 0x3C, 0xAD, 0x02, 0xA0, 0x0D, 0x3C, 0xAD, 0x03, 0xA0, 0x03, 0x80, 0x10, 0x6D, 0x21, 0x7F, 0x80, 0x0B, 0x6D, 0x21, 0x7F, 0x53, 0x4B, 0x6D, 0x21, 0x7F, 0x02, 0x4B, 0x54, 0x1C, 0x6D, 0x6D, 0x6D, 0x21, 0x1F, 0x54, 0x1D, 0x6D, 0x21, 0x7F, 0x05, 0x1D, 0x5B, 0x11, 0x05, 0x5C, 0x0D, 0x11 + } + }, + { + 121, + 79, + 0x39, + 0x00A4, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA4, 0xDF, 0xA2, 0x7F, 0x55, 0x11, 0x00, 0x04, 0x12, 0x0E, 0x11, 0x00, 0x65, 0x12, 0x6B, 0x11, 0x65, 0x12, 0x6B, 0x11, 0x65, 0x12, 0x6B, 0x11, 0x65, 0x12, 0x6B, 0x11, 0x55, 0x10, 0x00, 0x55, 0x13, 0x00, 0x7C, 0x11, 0x4D, 0x51, 0x12, 0x39, 0x10, 0xD0, 0x03, 0x50, 0x10, 0x7F, 0x12, 0x4B, 0x55, 0x10, 0x08, 0x47, 0x4B, 0x01, 0xA0, 0x03, 0x02, 0x4A, 0x6D, 0x6E, 0x4B, 0x7A, 0x10, 0xBF, 0xF3, 0x1A, 0x2C + } + }, + { + 122, + 79, + 0x39, + 0x00A5, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA5, 0x53, 0x4A, 0x7F, 0x62, 0xD0, 0x00, 0x3C, 0x0E, 0x02, 0xC0, 0x0E, 0x55, 0x36, 0x00, 0x90, 0x09, 0x47, 0x36, 0x40, 0xA0, 0x04, 0x7C, 0x14, 0x1F, 0x7F, 0x70, 0xBF, 0x62, 0xD4, 0x02, 0x62, 0xD3, 0x02, 0x50, 0x00, 0x53, 0x32, 0x53, 0x35, 0x53, 0x44, 0x53, 0x45, 0x55, 0x34, 0x99, 0x3E, 0x34, 0x53, 0x19, 0x3E, 0x34, 0x53, 0x1A, 0x3E, 0x34, 0x53, 0x1B, 0x76, 0x45, 0x51, 0x45, 0x3A, 0x0E, 0x9D, 0x33 + } + }, + { + 123, + 79, + 0x39, + 0x00A6, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA6, 0xD1, 0x1F, 0x3E, 0x34, 0x53, 0x1C, 0x3E, 0x34, 0x53, 0x1D, 0x3E, 0x34, 0x53, 0x1E, 0x51, 0x19, 0x12, 0x1C, 0xD0, 0x03, 0x73, 0x74, 0x53, 0x1F, 0x51, 0x1A, 0x12, 0x1D, 0xD0, 0x03, 0x73, 0x74, 0x53, 0x20, 0x51, 0x1F, 0xA0, 0x07, 0x39, 0x02, 0xA0, 0x03, 0x80, 0x2D, 0x51, 0x20, 0xA0, 0x05, 0x39, 0x02, 0xB0, 0x25, 0x51, 0x19, 0x3A, 0x1C, 0xC0, 0x0B, 0xA0, 0x11, 0x51, 0x1B, 0x12, 0x4C, 0xEF, 0xD8 + } + }, + { + 124, + 79, + 0x39, + 0x00A7, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA7, 0x53, 0x21, 0x80, 0x0C, 0x51, 0x1B, 0x02, 0x4C, 0x53, 0x21, 0x80, 0x04, 0x5F, 0x21, 0x1B, 0x51, 0x1A, 0x12, 0x1D, 0x67, 0x14, 0x21, 0x80, 0x8D, 0x3C, 0x1F, 0x02, 0xB0, 0x41, 0x3C, 0x20, 0x01, 0xB0, 0x3C, 0x51, 0x19, 0x3A, 0x1C, 0xC0, 0x0B, 0xA0, 0x11, 0x51, 0x1B, 0x12, 0x4C, 0x53, 0x21, 0x80, 0x0C, 0x51, 0x1B, 0x02, 0x4C, 0x53, 0x21, 0x80, 0x04, 0x5F, 0x21, 0x1B, 0x51, 0x1A, 0x3A, 0x1F, 0x39 + } + }, + { + 125, + 79, + 0x39, + 0x00A8, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA8, 0x1D, 0xC0, 0x0E, 0x58, 0x21, 0x52, 0x00, 0x79, 0x3B, 0x00, 0xD0, 0x10, 0x7A, 0x21, 0x80, 0x0C, 0x58, 0x21, 0x52, 0x00, 0x75, 0x3B, 0x00, 0xD0, 0x03, 0x76, 0x21, 0x80, 0x48, 0x3C, 0x1F, 0x01, 0xB0, 0x41, 0x3C, 0x20, 0x02, 0xB0, 0x3C, 0x51, 0x1A, 0x3A, 0x1D, 0xC0, 0x08, 0x51, 0x1B, 0x78, 0x53, 0x21, 0x80, 0x06, 0x51, 0x1B, 0x74, 0x53, 0x21, 0x51, 0x19, 0x3A, 0x1C, 0xC0, 0x0B, 0xA0, 0x9C, 0x34 + } + }, + { + 126, + 79, + 0x39, + 0x00A9, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xA9, 0x11, 0x51, 0x21, 0x12, 0x4C, 0x53, 0x1F, 0x80, 0x0C, 0x51, 0x21, 0x02, 0x4C, 0x53, 0x1F, 0x80, 0x04, 0x55, 0x1F, 0x00, 0x58, 0x21, 0x52, 0x00, 0x58, 0x1F, 0x3B, 0x00, 0xD0, 0x03, 0x5A, 0x21, 0x80, 0x03, 0x8F, 0x17, 0x58, 0x1B, 0x52, 0x00, 0x58, 0x21, 0x13, 0x00, 0xCF, 0x0D, 0x3A, 0x18, 0xDF, 0x09, 0x58, 0x1E, 0x52, 0x00, 0x58, 0x21, 0x13, 0x00, 0xCE, 0xFF, 0x3A, 0x18, 0xDE, 0xFB, 0xB0, 0x5D + } + }, + { + 127, + 79, + 0x39, + 0x00AA, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xAA, 0x58, 0x1B, 0x52, 0x00, 0x58, 0x1E, 0x3B, 0x00, 0xD0, 0x0A, 0x52, 0x00, 0x01, 0x01, 0x55, 0x36, 0x40, 0x80, 0x06, 0x01, 0x01, 0x55, 0x36, 0x40, 0x58, 0x21, 0x54, 0x00, 0x76, 0x32, 0x8E, 0xDB, 0x76, 0x44, 0x5F, 0x45, 0x44, 0x06, 0x35, 0x03, 0x51, 0x35, 0x55, 0x34, 0x99, 0x04, 0x34, 0x51, 0x44, 0x3A, 0x0E, 0xCE, 0xBA, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x62, 0xD0, 0x00, 0x70, 0xCF, 0x71, 0x06, 0x0A + } + }, + { + 128, + 79, + 0x39, + 0x00AB, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xAB, 0x20, 0x51, 0x9A, 0x60, 0xA0, 0x51, 0x9C, 0x60, 0xA2, 0x51, 0x9B, 0x60, 0xA1, 0x51, 0x9E, 0x60, 0xC7, 0x51, 0x9D, 0x60, 0xA4, 0x70, 0xCF, 0x7F, 0x62, 0xD0, 0x00, 0x62, 0xD5, 0x00, 0x62, 0xD4, 0x00, 0x62, 0xD3, 0x00, 0x55, 0xFA, 0x00, 0x50, 0x06, 0x55, 0xF8, 0x3A, 0x7C, 0x00, 0x60, 0x3C, 0xF8, 0x05, 0xB0, 0x12, 0x70, 0xCF, 0x71, 0x20, 0x62, 0xA6, 0x00, 0x71, 0x30, 0x62, 0x1B, 0x30, 0xAA, 0x53 + } + }, + { + 129, + 79, + 0x39, + 0x00AC, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xAC, 0x43, 0x1B, 0x40, 0x70, 0xCF, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x62, 0xD0, 0x03, 0x51, 0xE1, 0x54, 0x01, 0x51, 0xE0, 0x54, 0x00, 0x38, 0xFE, 0x20, 0x7F, 0x7F, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x62, 0xD0, 0x00, 0x26, 0xAF, 0xFD, 0x7C, 0x72, 0x51, 0x26, 0xAE, 0xFB, 0x51, 0xAE, 0x60, 0x00, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0xEF, 0xDE + } + }, + { + 130, + 79, + 0x39, + 0x00AD, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xAD, 0x10, 0x4F, 0x20, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x10, 0x4F, 0x62, 0xD0, 0x00, 0x51, 0xAF, 0x29, 0x02, 0x7C, 0x6F, 0x20, 0x62, 0xD0, 0x00, 0x51, 0xAE, 0x29, 0x04, 0x53, 0xAE, 0x51, 0xAE, 0x60, 0x00, 0x20, 0x7F, 0x7F, 0x7F, 0x08, 0x62, 0xD0, 0x00, 0x55, 0xFA, 0x00, 0x62, 0xD5, 0x00, 0x62, 0xD4, 0x00, 0x4F, 0x5B, 0x01, 0x03, 0x53, 0xF9, 0x55, 0xF8, 0x3A, 0x50, 0xE3, 0xC7 + } + }, + { + 131, + 79, + 0x39, + 0x00AE, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xAE, 0x06, 0x00, 0x20, 0x70, 0xBF, 0x62, 0xD3, 0x00, 0x52, 0xF8, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x62, 0xD0, 0x00, 0x55, 0xFA, 0x00, 0x62, 0xD5, 0x00, 0x62, 0xD4, 0x00, 0x4F, 0x5B, 0x01, 0x03, 0x53, 0xF9, 0x55, 0xF8, 0x3A, 0x50, 0x06, 0x00, 0x7F, 0x11, 0x04, 0x4B, 0xD0, 0x04, 0x78, 0xC0, 0x09, 0x3A, 0x80, 0x40, 0x79, 0x19, 0x00, 0xDF, 0xF9, 0x7F, 0x71, 0x40, 0xA0, 0x05, 0x70, 0xCF, 0x71, 0xD5, 0xAC + } + }, + { + 132, + 79, + 0x39, + 0x00AF, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xAF, 0x10, 0x5E, 0x00, 0x70, 0xCF, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x56, 0x00, 0x00, 0x80, 0x13, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xC0, 0x7C, 0x6F, 0x54, 0x52, 0x00, 0x3F, 0xE8, 0x77, 0x00, 0x3D, 0x00, 0x04, 0xCF, 0xEA, 0x62, 0xD0, 0x04, 0x55, 0xB6, 0x00, 0x62, 0xD0, 0x04, 0x55, 0xB5, 0x00, 0x7C, 0x73, 0x74, 0x38, 0xFF, 0x20, 0x7F, 0x7F, 0x10, 0x4F, 0xBC, 0x7B + } + }, + { + 133, + 79, + 0x39, + 0x00B0, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB0, 0x38, 0x01, 0x10, 0x7C, 0x11, 0x47, 0x62, 0xD0, 0x00, 0x20, 0x54, 0x00, 0x50, 0x0F, 0x08, 0x10, 0x7C, 0x2B, 0x38, 0x38, 0xFE, 0x52, 0x00, 0x62, 0xD0, 0x00, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x56, 0x01, 0x00, 0x9F, 0xD7, 0x62, 0xD0, 0x00, 0x54, 0x00, 0x52, 0x00, 0x08, 0x7C, 0x47, 0x34, 0x38, 0xFF, 0x52, 0x00, 0x08, 0x90, 0x46, 0x52, 0x00, 0x08, 0x62, 0xD0, 0x04, 0x51, 0x2E, 0x60 + } + }, + { + 134, + 79, + 0x39, + 0x00B1, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB1, 0xB5, 0x08, 0x7C, 0x3A, 0x9B, 0x38, 0xFD, 0x62, 0xD0, 0x00, 0x54, 0x01, 0x5A, 0xE8, 0x06, 0xE8, 0x01, 0x50, 0x0F, 0x08, 0x51, 0xE8, 0x08, 0x52, 0x00, 0x08, 0x91, 0x47, 0x62, 0xD0, 0x00, 0x5A, 0xE8, 0x06, 0xE8, 0x01, 0x50, 0x0F, 0x08, 0x51, 0xE8, 0x08, 0x7C, 0x2B, 0x3C, 0x38, 0xFB, 0x52, 0x00, 0x62, 0xD0, 0x04, 0x53, 0xB5, 0x52, 0x01, 0x62, 0xD0, 0x00, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0xF0, 0xE5 + } + }, + { + 135, + 79, + 0x39, + 0x00B2, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB2, 0x4F, 0x38, 0x06, 0x50, 0x04, 0x3B, 0xFC, 0xD0, 0x04, 0x56, 0xFC, 0x04, 0x56, 0x05, 0x00, 0x56, 0x04, 0x00, 0x80, 0x67, 0x56, 0x02, 0xE0, 0x56, 0x01, 0x01, 0x56, 0x00, 0x00, 0x80, 0x23, 0x7C, 0x6F, 0x4C, 0x52, 0x01, 0x7C, 0x70, 0xCD, 0x7C, 0x6F, 0x44, 0x06, 0xE8, 0xC4, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x3B, 0x03, 0xB0, 0x03, 0x80, 0x0F, 0x07, 0x02, 0x08, 0x0F, 0x01, 0x00, 0x77, 0x09, 0x18 + } + }, + { + 136, + 79, + 0x39, + 0x00B3, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB3, 0x00, 0x52, 0x00, 0x3B, 0xFC, 0xCF, 0xD9, 0x52, 0x00, 0x3B, 0xFC, 0xA0, 0x2C, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x18, 0x06, 0xE8, 0xC0, 0x7C, 0x6F, 0x54, 0x52, 0x00, 0x3F, 0xE8, 0x7C, 0x6F, 0x18, 0x06, 0xE8, 0xC4, 0x7C, 0x6F, 0x54, 0x52, 0x03, 0x3F, 0xE8, 0x52, 0x02, 0x53, 0xE8, 0x52, 0x01, 0x60, 0xD5, 0x50, 0xFF, 0x3F, 0xE8, 0x77, 0x05, 0x77, 0x04, 0x62, 0xD0, 0x04, 0x52, 0x04, 0x3A, 0xDB, 0xBD + } + }, + { + 137, + 79, + 0x39, + 0x00B4, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB4, 0xB6, 0xCF, 0x92, 0x52, 0x05, 0x62, 0xD0, 0x04, 0x53, 0xB6, 0x3D, 0x05, 0x04, 0xD0, 0x55, 0x56, 0x02, 0xE0, 0x56, 0x01, 0x01, 0x56, 0x00, 0x00, 0x80, 0x44, 0x7C, 0x6F, 0x4C, 0x52, 0x01, 0x7C, 0x70, 0xCD, 0x3D, 0x03, 0xFF, 0xA0, 0x2F, 0x62, 0xD0, 0x04, 0x51, 0xB6, 0x7C, 0x70, 0x0E, 0x06, 0xE8, 0xC0, 0x7C, 0x6F, 0x54, 0x52, 0x00, 0x7C, 0x72, 0xE6, 0x7C, 0x70, 0x0E, 0x06, 0xE8, 0xC4, 0x09, 0x1A + } + }, + { + 138, + 79, + 0x39, + 0x00B5, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB5, 0x7C, 0x6F, 0x54, 0x52, 0x03, 0x7C, 0x72, 0xE6, 0x01, 0x01, 0x53, 0xB6, 0x62, 0xD0, 0x00, 0x39, 0x04, 0xC0, 0x03, 0x80, 0x0F, 0x07, 0x02, 0x08, 0x0F, 0x01, 0x00, 0x77, 0x00, 0x52, 0x00, 0x3B, 0xFC, 0xCF, 0xB8, 0x56, 0x04, 0x00, 0x80, 0x32, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x44, 0x06, 0xE8, 0xC4, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x7C, 0x72, 0x31, 0x06, 0xE6, 0xC0, 0x0E, 0x76, 0xF5 + } + }, + { + 139, + 79, + 0x39, + 0x00B6, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB6, 0xE7, 0x03, 0x51, 0xE7, 0x60, 0xD4, 0x3E, 0xE6, 0x7C, 0x6E, 0xB6, 0x7C, 0x70, 0x1E, 0x06, 0xE6, 0xE0, 0x0E, 0xE7, 0x01, 0x7C, 0x6D, 0xEA, 0x77, 0x04, 0x52, 0x04, 0x3B, 0x05, 0xCF, 0xCA, 0x38, 0xFA, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x3D, 0xFC, 0x00, 0xB0, 0x06, 0x7C, 0x73, 0x74, 0x80, 0x28, 0x90, 0x29, 0x54, 0x00, 0x3D, 0x00, 0x00, 0xA0, 0x1F, 0x62, 0xD0, 0x04, 0x3C, 0xB4, 0x00, 0xF5, 0xF4 + } + }, + { + 140, + 79, + 0x39, + 0x00B7, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB7, 0xB0, 0x10, 0x62, 0xD0, 0x00, 0x52, 0xFB, 0x53, 0xE8, 0x52, 0xFA, 0x60, 0xD5, 0x50, 0x01, 0x3F, 0xE8, 0x52, 0x00, 0x62, 0xD0, 0x04, 0x53, 0xB4, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x7C, 0x6F, 0xC9, 0x80, 0x22, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x62, 0xD0, 0x00, 0x51, 0x16, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0x04, 0x13 + } + }, + { + 141, + 79, + 0x39, + 0x00B8, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB8, 0xD0, 0x03, 0x77, 0x01, 0x77, 0x00, 0x3D, 0x00, 0x99, 0xD0, 0x07, 0x50, 0x28, 0x3B, 0x01, 0xDF, 0xD5, 0x50, 0x28, 0x3B, 0x01, 0xD0, 0x08, 0x62, 0xD0, 0x00, 0x50, 0x10, 0x80, 0x06, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x7C, 0x6F, 0x2F, 0xB0, 0x0A, 0x3D, 0xFC, 0x00, 0xA0, 0x4E, 0x91, 0x78, 0x80, 0x4A, 0x3D, 0x00, 0x10, 0xB0, 0x03, 0x80, 0x43, 0xFB, 0x02 + } + }, + { + 142, + 79, + 0x39, + 0x00B9, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xB9, 0x3D, 0x00, 0x20, 0xB0, 0x2B, 0x62, 0xD0, 0x04, 0x51, 0xB5, 0x21, 0x0F, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xB4, 0x21, 0xF0, 0x62, 0xD0, 0x00, 0x2A, 0xE9, 0x62, 0xD0, 0x03, 0x53, 0x9A, 0x51, 0x9A, 0x08, 0x50, 0x02, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x80, 0x14, 0x3D, 0x00, 0x30, 0xB0, 0x05, 0x90, 0x10, 0x80, 0x0B, 0x62, 0xD0, 0x03, 0x47, 0x99, 0x40, 0xA0, 0x03, 0xDB, 0xC3 + } + }, + { + 143, + 79, + 0x39, + 0x00BA, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xBA, 0x90, 0xE4, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x04, 0x62, 0xD0, 0x04, 0x51, 0xB5, 0x08, 0x50, 0x23, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x62, 0xD0, 0x04, 0x50, 0x04, 0x3A, 0xB5, 0xC0, 0xB7, 0x56, 0x03, 0x00, 0x80, 0xA9, 0x62, 0xD0, 0x00, 0x52, 0x03, 0x7C, 0x6D, 0x8A, 0x51, 0xE8, 0x01, 0xE0, 0x54, 0x02, 0x51, 0xE9, 0x09, 0x01, 0x54, 0x01, 0x52, 0x03, 0x64, 0x64, 0x64, 0x01, 0x8B, 0x24 + } + }, + { + 144, + 79, + 0x39, + 0x00BB, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xBB, 0x03, 0x54, 0x00, 0x7C, 0x6F, 0xF6, 0x08, 0x52, 0x00, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x01, 0x01, 0x53, 0xE8, 0x52, 0x01, 0x09, 0x00, 0x7C, 0x6D, 0xE3, 0x3E, 0xE8, 0x53, 0xE8, 0x51, 0xE9, 0x08, 0x51, 0xE8, 0x08, 0x52, 0x00, 0x01, 0x01, 0x08, 0x7C, 0x32, 0x52, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x01, 0x03, 0x7C, 0x6E, 0xA3, 0x08, 0x52, 0x00, 0x01, 0x03, 0x2A, 0x63 + } + }, + { + 145, + 79, + 0x39, + 0x00BC, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xBC, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFB, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x01, 0x04, 0x53, 0xE8, 0x52, 0x01, 0x09, 0x00, 0x7C, 0x6D, 0xE3, 0x3E, 0xE8, 0x53, 0xE8, 0x51, 0xE9, 0x08, 0x51, 0xE8, 0x08, 0x52, 0x00, 0x01, 0x04, 0x08, 0x7C, 0x32, 0x52, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x01, 0x06, 0x53, 0xE8, 0x52, 0x01, 0x09, 0x00, 0x7C, 0x6D, 0xE3, 0x3E, 0xE8, 0x53, 0xE8, 0x51, 0xE9, 0x08, 0x51, 0x0A, 0x24 + } + }, + { + 146, + 79, + 0x39, + 0x00BD, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xBD, 0xE8, 0x08, 0x52, 0x00, 0x01, 0x06, 0x08, 0x7C, 0x32, 0x52, 0x38, 0xFA, 0x77, 0x03, 0x62, 0xD0, 0x04, 0x52, 0x03, 0x3A, 0xB5, 0xCF, 0x50, 0x50, 0x00, 0x08, 0x50, 0x25, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x38, 0xFC, 0x20, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xB5, 0x21, 0x0F, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xB4, 0x21, 0xF0, 0x62, 0xD0, 0x00, 0x2A, 0xE9, 0x62, 0xD0, 0xCB, 0xA7 + } + }, + { + 147, + 79, + 0x39, + 0x00BE, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xBE, 0x03, 0x53, 0x9A, 0x51, 0x9A, 0x08, 0x50, 0x02, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x62, 0xD0, 0x01, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x50, 0x03, 0x08, 0x7C, 0x32, 0x52, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x50, 0x05, 0x08, 0x7C, 0x32, 0x52, 0x38, 0xFA, 0x7F, 0x10, 0x4F, 0x38, 0x07, 0x62, 0xD0, 0x04, 0x51, 0xB5, 0x21, 0x0F, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x62, 0xD0, 0xB2 + } + }, + { + 148, + 79, + 0x39, + 0x00BF, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xBF, 0xD0, 0x04, 0x51, 0xB4, 0x21, 0xF0, 0x62, 0xD0, 0x00, 0x2A, 0xE9, 0x62, 0xD0, 0x03, 0x53, 0x9A, 0x51, 0x9A, 0x08, 0x50, 0x02, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x56, 0x00, 0x00, 0x80, 0xCA, 0x56, 0x04, 0x00, 0x62, 0xD0, 0x04, 0x52, 0x00, 0x3A, 0xB5, 0xD0, 0x12, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xC0, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x54, 0x04, 0x62, 0xD0, 0x00, 0x3F, 0x91 + } + }, + { + 149, + 79, + 0x39, + 0x00C0, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC0, 0x52, 0x04, 0x7C, 0x6D, 0x8A, 0x51, 0xE8, 0x01, 0xE0, 0x54, 0x02, 0x51, 0xE9, 0x09, 0x01, 0x54, 0x01, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x81, 0x0E, 0xE9, 0x0D, 0x7C, 0x6F, 0x5C, 0x54, 0x03, 0x52, 0x02, 0x01, 0x06, 0x53, 0xE8, 0x52, 0x01, 0x09, 0x00, 0x7C, 0x6D, 0xE3, 0x3E, 0xE8, 0x53, 0xE8, 0x51, 0xE9, 0x08, 0x51, 0xE8, 0x08, 0x52, 0x03, 0x08, 0x7C, 0x32, 0x52, 0x38, 0xFD, 0x62, 0xD0, 0xD7, 0xC2 + } + }, + { + 150, + 79, + 0x39, + 0x00C1, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC1, 0x00, 0x52, 0x02, 0x01, 0x04, 0x53, 0xE8, 0x52, 0x01, 0x09, 0x00, 0x7C, 0x6D, 0xE3, 0x3E, 0xE8, 0x53, 0xE8, 0x51, 0xE9, 0x08, 0x51, 0xE8, 0x08, 0x52, 0x03, 0x01, 0x02, 0x08, 0x7C, 0x32, 0x52, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x01, 0x03, 0x7C, 0x6E, 0xA3, 0x08, 0x52, 0x03, 0x01, 0x04, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFB, 0x7C, 0x6F, 0x4C, 0x52, 0x01, 0x60, 0xD4, 0x3E, 0xE8, 0x54, 0x05, 0xAB, 0x6B + } + }, + { + 151, + 79, + 0x39, + 0x00C2, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC2, 0x48, 0x00, 0x01, 0xA0, 0x18, 0x52, 0x05, 0x21, 0x0F, 0x53, 0xE9, 0x52, 0x06, 0x2A, 0xE9, 0x08, 0x52, 0x03, 0x11, 0x01, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x80, 0x0C, 0x52, 0x05, 0x62, 0xD0, 0x00, 0x64, 0x64, 0x64, 0x64, 0x54, 0x06, 0x77, 0x00, 0x3D, 0x00, 0x04, 0xCF, 0x33, 0x38, 0xF9, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x71, 0x10, 0x41, 0x04, 0x5F, 0x70, 0xCF, 0x62, 0xD0, 0x00, 0x4D, 0xB0 + } + }, + { + 152, + 79, + 0x39, + 0x00C3, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC3, 0x51, 0xAF, 0x29, 0xA0, 0x7C, 0x6F, 0x20, 0x10, 0x7C, 0x20, 0x0B, 0x7C, 0x20, 0x50, 0x20, 0x10, 0x50, 0x01, 0x08, 0x50, 0x00, 0x08, 0x50, 0xA0, 0x08, 0x08, 0x7C, 0x20, 0x57, 0x38, 0xFC, 0x20, 0x62, 0xC8, 0x0B, 0x62, 0xCA, 0x24, 0x43, 0xD6, 0x01, 0x62, 0xCD, 0x00, 0x56, 0x00, 0x20, 0x80, 0x06, 0x62, 0xCF, 0x00, 0x7B, 0x00, 0x3D, 0x00, 0x00, 0xBF, 0xF7, 0x41, 0xD6, 0xFE, 0x38, 0xFF, 0x54, 0xBF + } + }, + { + 153, + 79, + 0x39, + 0x00C4, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC4, 0x20, 0x7F, 0x10, 0x4F, 0x3D, 0xFC, 0x21, 0xD0, 0x0C, 0x41, 0xD6, 0xEF, 0x41, 0xE0, 0x7F, 0x62, 0xC8, 0x0B, 0x80, 0x0A, 0x62, 0xC8, 0x00, 0x43, 0xD6, 0x10, 0x43, 0xE0, 0x80, 0x20, 0x7F, 0x43, 0xD6, 0x01, 0x40, 0x62, 0xD0, 0x00, 0x51, 0xAF, 0x29, 0xA0, 0x7C, 0x6F, 0x20, 0x71, 0x10, 0x43, 0x04, 0xA0, 0x70, 0xCF, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x5D, 0xF7, 0x54, 0x00, 0x70, 0xFE, 0x7C, 0xDE, 0xD4 + } + }, + { + 154, + 79, + 0x39, + 0x00C5, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC5, 0x70, 0xF8, 0xB0, 0x13, 0x7C, 0x73, 0x90, 0xBF, 0xFC, 0x71, 0x01, 0x40, 0x70, 0xFE, 0x62, 0xE3, 0x38, 0x41, 0xD6, 0xFE, 0x80, 0x06, 0x10, 0x7C, 0x33, 0x60, 0x20, 0x71, 0x10, 0x41, 0x04, 0x5F, 0x70, 0xCF, 0x62, 0xD0, 0x00, 0x51, 0xAF, 0x29, 0xA0, 0x7C, 0x6F, 0x20, 0x48, 0x00, 0x01, 0xA0, 0x03, 0x71, 0x01, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x7C, 0x33, 0x60, 0x20, 0x71, 0x10, 0x41, 0x04, 0x7F, 0x17 + } + }, + { + 155, + 79, + 0x39, + 0x00C6, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC6, 0x5F, 0x70, 0xCF, 0x62, 0xD0, 0x00, 0x51, 0xAF, 0x29, 0xA0, 0x7C, 0x6F, 0x20, 0x71, 0x10, 0x43, 0xEC, 0x02, 0x70, 0xCF, 0x62, 0xDA, 0x7F, 0x43, 0xE0, 0x80, 0x9F, 0x83, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x9F, 0x92, 0x71, 0x10, 0x43, 0xEC, 0x02, 0x70, 0xCF, 0x62, 0xDA, 0x7F, 0x43, 0xE0, 0x80, 0x9F, 0x6D, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x7C, 0x73, 0x89, 0x38, 0xFF, 0x20, 0xF5, 0x04 + } + }, + { + 156, + 79, + 0x39, + 0x00C7, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC7, 0x7F, 0x7C, 0x73, 0x89, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x5D, 0xC8, 0x39, 0x00, 0xB0, 0x18, 0x7C, 0x73, 0x90, 0xA0, 0x09, 0x56, 0x01, 0x01, 0x56, 0x00, 0x00, 0x80, 0x04, 0x7C, 0x6F, 0xC9, 0x62, 0xD0, 0x00, 0x52, 0x01, 0x80, 0x1D, 0x5D, 0xC9, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x47, 0xE9, 0x01, 0xA0, 0x09, 0x56, 0x01, 0x01, 0x56, 0x00, 0x00, 0x80, 0x04, 0x7C, 0x6F, 0xC9, 0x62, 0xD0, 0x00, 0xEB, 0xF1 + } + }, + { + 157, + 79, + 0x39, + 0x00C8, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC8, 0x52, 0x01, 0x38, 0xFE, 0x20, 0x7F, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x7C, 0x70, 0xF8, 0xA0, 0x25, 0x5D, 0xD6, 0x53, 0xE9, 0x2E, 0xE9, 0xFE, 0x51, 0xE9, 0x54, 0x00, 0x43, 0xD6, 0x01, 0x52, 0xFC, 0x60, 0xCD, 0x52, 0xFB, 0x60, 0xCF, 0x5D, 0xD6, 0x53, 0xE9, 0x52, 0x00, 0x24, 0xE9, 0x51, 0xE9, 0x60, 0xD6, 0x80, 0x16, 0x3D, 0xFC, 0xA0, 0xD0, 0x11, 0x7C, 0x6F, 0x06, 0x28 + } + }, + { + 158, + 79, + 0x39, + 0x00C9, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xC9, 0xB9, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0x00, 0x7C, 0x71, 0x08, 0x52, 0xFB, 0x3F, 0xE8, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x03, 0x52, 0xFB, 0x54, 0x01, 0x52, 0xFA, 0x54, 0x00, 0x7C, 0x70, 0xF8, 0xA0, 0x1C, 0x7C, 0x71, 0x65, 0x60, 0xCD, 0x52, 0x00, 0x60, 0xCF, 0x52, 0x01, 0x60, 0xCF, 0x5D, 0xD6, 0x53, 0xE9, 0x52, 0x02, 0x24, 0xE9, 0x51, 0xE9, 0x60, 0xD6, 0x80, 0x26, 0x3D, 0xFC, 0x41, 0x9F + } + }, + { + 159, + 79, + 0x39, + 0x00CA, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xCA, 0x9F, 0xD0, 0x21, 0x7C, 0x6F, 0xB9, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x01, 0x7C, 0x6F, 0xD9, 0x52, 0xFC, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0x01, 0x7C, 0x71, 0x08, 0x52, 0x01, 0x3F, 0xE8, 0x38, 0xFD, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x7C, 0x70, 0xF8, 0xA0, 0x29, 0x5D, 0xD6, 0x53, 0xE9, 0x2E, 0xE9, 0xFE, 0x51, 0xE9, 0x54, 0x01, 0x43, 0xD6, 0x01, 0x10, 0x52, 0xEA, 0xF2 + } + }, + { + 160, + 79, + 0x39, + 0x00CB, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xCB, 0xFC, 0x7C, 0x33, 0x49, 0x62, 0xD0, 0x00, 0x20, 0x54, 0x00, 0x5D, 0xD6, 0x53, 0xE9, 0x52, 0x01, 0x24, 0xE9, 0x51, 0xE9, 0x60, 0xD6, 0x80, 0x17, 0x3D, 0xFC, 0xA0, 0xD0, 0x12, 0x7C, 0x6F, 0xB9, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0x54, 0x00, 0x52, 0x00, 0x62, 0xD0, 0x00, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x03, 0x7C, 0x70, 0xF8, 0xA0, 0x1B, 0x45, 0xA9 + } + }, + { + 161, + 79, + 0x39, + 0x00CC, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xCC, 0x7C, 0x71, 0x65, 0x08, 0x7C, 0x33, 0x53, 0x38, 0xFF, 0x7C, 0x72, 0x25, 0x5D, 0xD6, 0x53, 0xE9, 0x52, 0x02, 0x24, 0xE9, 0x51, 0xE9, 0x60, 0xD6, 0x80, 0x29, 0x3D, 0xFC, 0x9F, 0xD0, 0x24, 0x7C, 0x6F, 0xB9, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0x54, 0x00, 0x52, 0xFC, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0x01, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0xFD, 0x1A + } + }, + { + 162, + 79, + 0x39, + 0x00CD, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xCD, 0x54, 0x01, 0x7C, 0x71, 0x2E, 0x38, 0xFD, 0x20, 0x7F, 0x60, 0xCD, 0x5D, 0xF7, 0x08, 0x70, 0xFE, 0x5D, 0xCF, 0x7E, 0x60, 0xCD, 0x5D, 0xF7, 0x08, 0x70, 0xFE, 0x5D, 0xCF, 0x5C, 0x5D, 0xCF, 0x7E, 0x49, 0xC9, 0x01, 0xBF, 0xFC, 0x41, 0xD6, 0xFE, 0x7F, 0x41, 0x05, 0xF7, 0x7C, 0x73, 0x82, 0x62, 0xD0, 0x00, 0x51, 0xAF, 0x29, 0x08, 0x7C, 0x6F, 0x20, 0x71, 0x10, 0x43, 0x05, 0x08, 0x43, 0x04, 0xA4, 0x69 + } + }, + { + 163, + 79, + 0x39, + 0x00CE, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xCE, 0x08, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x55, 0xB9, 0x00, 0x62, 0xD0, 0x03, 0x55, 0x99, 0x04, 0x55, 0x9A, 0x00, 0x55, 0x9B, 0xF8, 0x55, 0x9C, 0x00, 0x55, 0x9E, 0x64, 0x55, 0x9D, 0x32, 0x55, 0x9F, 0x00, 0x55, 0xA0, 0x00, 0x7C, 0x30, 0xB2, 0x90, 0x10, 0x7C, 0x6F, 0x64, 0x10, 0x57, 0x01, 0x50, 0xF4, 0x7C, 0x2B, 0xA8, 0x20, 0x7C, 0x6E, 0xE8, 0x7F, 0x10, 0x4F, 0x38, 0x03, 0x7C, 0x31, 0x35, 0x13, 0x48 + } + }, + { + 164, + 79, + 0x39, + 0x00CF, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xCF, 0x7C, 0x6F, 0x2F, 0xB0, 0x06, 0x56, 0x01, 0x20, 0x80, 0x04, 0x56, 0x01, 0xA0, 0x52, 0x01, 0x08, 0x7C, 0x31, 0x02, 0x38, 0xFF, 0x62, 0xD0, 0x03, 0x51, 0x99, 0x21, 0xFC, 0x62, 0xD0, 0x00, 0x08, 0x50, 0x00, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x20, 0x08, 0x50, 0x01, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x3D, 0x00, 0x00, 0xB0, 0x28, 0x62, 0xD0, 0x04, 0x51, 0xB9, 0x08, 0x50, 0x01, 0x08, 0x7C, 0x7C, 0x1B + } + }, + { + 165, + 79, + 0x39, + 0x00D0, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD0, 0x32, 0x0C, 0x38, 0xFE, 0x50, 0x00, 0x08, 0x50, 0x02, 0x08, 0x7C, 0x32, 0x0C, 0x62, 0xD0, 0x03, 0x51, 0x9B, 0x08, 0x50, 0x1E, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x82, 0x52, 0x3D, 0x00, 0x10, 0xB1, 0x87, 0x50, 0x00, 0x08, 0x50, 0x02, 0x08, 0x7C, 0x32, 0x0C, 0x7C, 0x40, 0x1F, 0x62, 0xD0, 0x00, 0x08, 0x50, 0x01, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0xC0, 0x08, 0x50, 0x03, 0x08, 0x01, 0x26 + } + }, + { + 166, + 79, + 0x39, + 0x00D1, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD1, 0x7C, 0x32, 0x0C, 0x50, 0xC1, 0x08, 0x50, 0x04, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0xC2, 0x08, 0x50, 0x05, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x00, 0x08, 0x50, 0x06, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x10, 0x50, 0x00, 0x7C, 0x2B, 0x69, 0x62, 0xD0, 0x00, 0x20, 0x08, 0x50, 0x07, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x10, 0x50, 0x01, 0x7C, 0x2B, 0x69, 0x62, 0xD0, 0x00, 0x20, 0x08, 0xD6, 0xD1 + } + }, + { + 167, + 79, + 0x39, + 0x00D2, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD2, 0x50, 0x08, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x10, 0x50, 0x02, 0x7C, 0x2B, 0x69, 0x62, 0xD0, 0x00, 0x20, 0x08, 0x50, 0x09, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x10, 0x50, 0x03, 0x7C, 0x2B, 0x69, 0x62, 0xD0, 0x00, 0x20, 0x08, 0x50, 0x0A, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x10, 0x50, 0x04, 0x7C, 0x2B, 0x69, 0x62, 0xD0, 0x00, 0x20, 0x08, 0x50, 0x0B, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xAF, 0x84 + } + }, + { + 168, + 79, + 0x39, + 0x00D3, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD3, 0xFE, 0x10, 0x50, 0x05, 0x7C, 0x2B, 0x69, 0x62, 0xD0, 0x00, 0x20, 0x08, 0x50, 0x0C, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x10, 0x50, 0x06, 0x7C, 0x2B, 0x69, 0x62, 0xD0, 0x00, 0x20, 0x08, 0x50, 0x0D, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x10, 0x50, 0x07, 0x7C, 0x2B, 0x69, 0x62, 0xD0, 0x00, 0x20, 0x08, 0x50, 0x0E, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x50, 0x07, 0x10, 0x06, 0x33 + } + }, + { + 169, + 79, + 0x39, + 0x00D4, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD4, 0x08, 0x57, 0xA0, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x28, 0x53, 0xE8, 0x20, 0x51, 0xE9, 0x08, 0x51, 0xE8, 0x08, 0x50, 0x0F, 0x08, 0x7C, 0x32, 0x52, 0x38, 0xFD, 0x50, 0x10, 0x08, 0x50, 0x12, 0x08, 0x50, 0x11, 0x08, 0x7C, 0x32, 0x52, 0x50, 0xA0, 0x08, 0x50, 0x02, 0x08, 0x50, 0x13, 0x08, 0x7C, 0x32, 0x52, 0x38, 0xFA, 0x50, 0x04, 0x08, 0x50, 0x00, 0x08, 0x50, 0x15, 0x08, 0x7C, 0x62, 0xEC + } + }, + { + 170, + 79, + 0x39, + 0x00D5, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD5, 0x32, 0x52, 0x50, 0x00, 0x08, 0x50, 0x17, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFB, 0x50, 0x00, 0x08, 0x50, 0x18, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x00, 0x08, 0x50, 0x19, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x00, 0x08, 0x50, 0x1A, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x00, 0x08, 0x50, 0x1B, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x00, 0x08, 0x50, 0x1C, 0x08, 0x7C, 0x32, 0x0C, 0x62, 0xD0, 0x66, 0xF5 + } + }, + { + 171, + 79, + 0x39, + 0x00D6, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD6, 0x03, 0x51, 0x9C, 0x08, 0x50, 0x1D, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x62, 0xD0, 0x03, 0x51, 0x9E, 0x08, 0x50, 0x1E, 0x08, 0x7C, 0x32, 0x0C, 0x62, 0xD0, 0x03, 0x51, 0x9D, 0x08, 0x50, 0x1F, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x80, 0xC7, 0x3D, 0x00, 0x20, 0xB0, 0x03, 0x80, 0xC0, 0x3D, 0x00, 0x30, 0xB0, 0xBB, 0x50, 0x01, 0x08, 0x50, 0x00, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x04, 0x08, 0x7D, 0x24 + } + }, + { + 172, + 79, + 0x39, + 0x00D7, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD7, 0x50, 0x01, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x01, 0x08, 0x50, 0x02, 0x08, 0x7C, 0x32, 0x0C, 0x62, 0xD0, 0x03, 0x51, 0x9F, 0x08, 0x50, 0x29, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x04, 0x08, 0x50, 0x2A, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x08, 0x08, 0x50, 0x2B, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x08, 0x08, 0x50, 0x2C, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x00, 0x08, 0x50, 0xFF, 0x29 + } + }, + { + 173, + 79, + 0x39, + 0x00D8, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD8, 0x48, 0x08, 0x50, 0x2D, 0x08, 0x7C, 0x32, 0x52, 0x38, 0xFB, 0x50, 0x1C, 0x08, 0x50, 0x2F, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x00, 0x08, 0x50, 0x30, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x08, 0x08, 0x50, 0x31, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x08, 0x08, 0x50, 0x32, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x5A, 0x08, 0x50, 0x33, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x00, 0x08, 0x50, 0x34, 0xD9, 0xDE + } + }, + { + 174, + 79, + 0x39, + 0x00D9, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xD9, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x04, 0x08, 0x50, 0x35, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x0C, 0x08, 0x50, 0x36, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x50, 0x05, 0x08, 0x50, 0x37, 0x08, 0x7C, 0x32, 0x0C, 0x50, 0x01, 0x08, 0x50, 0x38, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x7C, 0x2B, 0x1A, 0x7C, 0x31, 0x1F, 0x38, 0xFD, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x7C, 0x32, 0x06, 0x7C, 0x20, 0x6D + } + }, + { + 175, + 79, + 0x39, + 0x00DA, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xDA, 0x6F, 0x3C, 0x54, 0x00, 0x3D, 0x00, 0x30, 0xB0, 0x05, 0x90, 0xE8, 0x80, 0x03, 0x90, 0x4B, 0x62, 0xD0, 0x03, 0x51, 0x99, 0x21, 0x01, 0x62, 0xD0, 0x00, 0x39, 0x01, 0xB0, 0x19, 0x7C, 0x31, 0x35, 0x62, 0xD4, 0x00, 0x62, 0xD5, 0x00, 0x62, 0xD1, 0x00, 0x62, 0xD3, 0x00, 0x62, 0xD0, 0x00, 0x62, 0xE3, 0x38, 0x50, 0x00, 0x00, 0x7C, 0x6F, 0x3C, 0x54, 0x01, 0x52, 0x01, 0x3B, 0x00, 0xA0, 0x17, 0xE4, 0xF6 + } + }, + { + 176, + 79, + 0x39, + 0x00DB, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xDB, 0x52, 0x01, 0x08, 0x52, 0x00, 0x08, 0x7C, 0x2B, 0x1B, 0x9C, 0xEE, 0x52, 0x01, 0x08, 0x52, 0x00, 0x08, 0x7C, 0x2B, 0x1F, 0x38, 0xFC, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x7C, 0x6F, 0x2F, 0xB0, 0x30, 0x50, 0x00, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0xD0, 0x03, 0x53, 0x99, 0x50, 0x1E, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFE, 0x62, 0xD0, 0x03, 0x53, 0x9B, 0x47, 0x99, 0x02, 0xA0, 0x70, 0xFB, 0x25 + } + }, + { + 177, + 79, + 0x39, + 0x00DC, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xDC, 0x51, 0x99, 0x21, 0xFD, 0x62, 0xD0, 0x00, 0x08, 0x50, 0x00, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x80, 0x5E, 0x3D, 0x00, 0x10, 0xB0, 0x33, 0x50, 0x00, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0xD0, 0x03, 0x53, 0x99, 0x50, 0x1D, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFE, 0x62, 0xD0, 0x03, 0x53, 0x9C, 0x50, 0x1F, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0xD0, 0x03, 0x53, 0x9D, 0x50, 0x1E, 0x08, 0x7C, 0x32, 0xA7, 0x46, 0xBC + } + }, + { + 178, + 79, + 0x39, + 0x00DD, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xDD, 0x38, 0xFE, 0x62, 0xD0, 0x03, 0x53, 0x9E, 0x80, 0x27, 0x3D, 0x00, 0x20, 0xB0, 0x10, 0x50, 0x00, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFF, 0x62, 0xD0, 0x03, 0x53, 0x99, 0x80, 0x13, 0x48, 0x00, 0x40, 0xA0, 0x0E, 0x50, 0x00, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFF, 0x62, 0xD0, 0x03, 0x53, 0x99, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x04, 0x50, 0x00, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFF, 0x62, 0xAA, 0x85 + } + }, + { + 179, + 79, + 0x39, + 0x00DE, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xDE, 0xD0, 0x00, 0x54, 0x00, 0x3D, 0x00, 0x01, 0xA0, 0x1F, 0x52, 0x00, 0x21, 0x70, 0x39, 0x30, 0xB0, 0x0E, 0x50, 0x01, 0x08, 0x50, 0x00, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x80, 0xE7, 0x52, 0x00, 0x62, 0xD0, 0x03, 0x53, 0x99, 0x80, 0xDE, 0x50, 0x29, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0xD0, 0x03, 0x53, 0x9F, 0x50, 0x02, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x39, 0x81, 0xB0, 0x82, 0x36 + } + }, + { + 180, + 79, + 0x39, + 0x00DF, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xDF, 0xC4, 0x50, 0x2A, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0xD0, 0x00, 0x53, 0xA5, 0x50, 0x2B, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x53, 0x15, 0x50, 0x2C, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0xD0, 0x00, 0x53, 0x16, 0x50, 0x2D, 0x08, 0x7C, 0x32, 0xF7, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x00, 0x53, 0xA3, 0x18, 0x53, 0xA4, 0x50, 0x2F, 0x08, 0x7C, 0x28, 0x83 + } + }, + { + 181, + 79, + 0x39, + 0x00E0, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE0, 0x32, 0xA7, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x53, 0x42, 0x50, 0x30, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0xD0, 0x04, 0x53, 0xB7, 0x50, 0x31, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x53, 0xA6, 0x50, 0x32, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0xD0, 0x00, 0x53, 0x17, 0x50, 0x36, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x53, 0xA7, 0x50, 0x37, 0x08, 0x7C, 0x32, 0xA7, 0x62, 0x39, 0xA6 + } + }, + { + 182, + 79, + 0x39, + 0x00E1, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE1, 0xD0, 0x00, 0x53, 0xA8, 0x50, 0x38, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x53, 0xA9, 0x10, 0x7C, 0x18, 0x83, 0x7C, 0x17, 0xB7, 0x20, 0x80, 0x04, 0x62, 0xE3, 0x38, 0x7C, 0x31, 0xC5, 0x62, 0xD0, 0x00, 0x39, 0x00, 0xBF, 0xF4, 0x7C, 0x71, 0x10, 0x7C, 0x49, 0x07, 0x62, 0xE3, 0x38, 0x52, 0x01, 0x71, 0x10, 0x60, 0xE0, 0x50, 0x01, 0x08, 0x50, 0x02, 0x08, 0x70, 0xCF, 0x7C, 0xFE, 0x31 + } + }, + { + 183, + 79, + 0x39, + 0x00E2, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE2, 0x32, 0x0C, 0x38, 0xFE, 0x38, 0xFC, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x7C, 0x6F, 0x2F, 0xB0, 0x06, 0x3D, 0xFC, 0x00, 0xB0, 0x0B, 0x3D, 0x00, 0x20, 0xA0, 0x06, 0x48, 0x00, 0x40, 0xA0, 0x26, 0x62, 0xD0, 0x04, 0x06, 0xB9, 0x40, 0x51, 0xB9, 0x29, 0x20, 0x62, 0xD0, 0x00, 0x08, 0x50, 0x01, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x80, 0x04, 0x62, 0xE3, 0x38, 0x7C, 0x31, 0xC5, 0x62, 0xD0, 0xA4, 0x7E + } + }, + { + 184, + 79, + 0x39, + 0x00E3, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE3, 0x00, 0x39, 0x00, 0xBF, 0xF4, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x7C, 0x6F, 0x2F, 0xB0, 0x06, 0x3D, 0xFC, 0x00, 0xB0, 0x0B, 0x3D, 0x00, 0x20, 0xA0, 0x06, 0x48, 0x00, 0x40, 0xA0, 0x0F, 0x62, 0xD0, 0x04, 0x51, 0xB9, 0x08, 0x50, 0x01, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x50, 0x00, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFF, 0x21, 0x80, 0x62, 0xD0, 0x04, 0x53, 0xB8, 0x38, 0xFF, 0xE6, 0x03 + } + }, + { + 185, + 79, + 0x39, + 0x00E4, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE4, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x7C, 0x6F, 0x2F, 0xB0, 0x0A, 0x3D, 0xFC, 0x01, 0xB0, 0x19, 0x90, 0xC1, 0x80, 0x15, 0x3D, 0x00, 0x10, 0xB0, 0x05, 0x90, 0x63, 0x80, 0x0C, 0x3D, 0x00, 0x30, 0xB0, 0x05, 0x90, 0x21, 0x80, 0x03, 0x90, 0x56, 0x52, 0xFC, 0x08, 0x7C, 0x2B, 0x4C, 0x38, 0xFF, 0x38, 0xFF, 0x20, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xB9, 0x62, 0xD0, 0x00, 0x67, 0x67, 0x67, 0x67, 0x15, 0x62 + } + }, + { + 186, + 79, + 0x39, + 0x00E5, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE5, 0x67, 0x67, 0x21, 0x03, 0x7F, 0x50, 0x84, 0x08, 0x50, 0x01, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x7C, 0x6F, 0x64, 0x7C, 0x32, 0x06, 0x62, 0xD0, 0x00, 0x62, 0xE3, 0x38, 0x50, 0x01, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x39, 0x04, 0xA0, 0x10, 0x50, 0x00, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x39, 0x01, 0xAF, 0xDA, 0x7C, 0x6E, 0xE8, 0x7F, 0x10, 0x4F, 0xD5, 0xE3 + } + }, + { + 187, + 79, + 0x39, + 0x00E6, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE6, 0x38, 0x02, 0x7C, 0x6F, 0x64, 0x56, 0x01, 0xFA, 0x56, 0x00, 0x00, 0x80, 0x36, 0x7C, 0x32, 0x06, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x08, 0x7C, 0x32, 0xA7, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x26, 0xE9, 0x80, 0x62, 0xD0, 0x04, 0x51, 0xB8, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0xA0, 0x03, 0x80, 0x1C, 0x10, 0x57, 0x03, 0x50, 0xE3, 0x7C, 0x2B, 0xA8, 0x20, 0x62, 0xE3, 0x38, 0x7B, 0x01, 0x1F, 0xA4, 0x82 + } + }, + { + 188, + 79, + 0x39, + 0x00E7, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE7, 0x00, 0x00, 0x3D, 0x00, 0x00, 0xBF, 0xC7, 0x3D, 0x01, 0x00, 0xBF, 0xC2, 0x7C, 0x6E, 0xE8, 0x38, 0xFE, 0x20, 0x7F, 0x7C, 0x6F, 0x64, 0x10, 0x57, 0x01, 0x50, 0xF4, 0x7C, 0x2B, 0xA8, 0x20, 0x7C, 0x6E, 0xE8, 0x7C, 0x32, 0x06, 0x62, 0xD0, 0x00, 0x7F, 0x7C, 0x31, 0x77, 0x7F, 0x7C, 0x31, 0xC1, 0x7F, 0x43, 0x05, 0x08, 0x62, 0xD0, 0x00, 0x26, 0xB0, 0xFB, 0x51, 0xB0, 0x60, 0x00, 0x62, 0xDA, 0x4A, 0xCF + } + }, + { + 189, + 79, + 0x39, + 0x00E8, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE8, 0xEF, 0x43, 0xE0, 0x10, 0x7C, 0x31, 0x9D, 0x7F, 0x7C, 0x31, 0xB6, 0x7C, 0x73, 0x82, 0x41, 0x05, 0xF7, 0x62, 0xD0, 0x00, 0x51, 0xB0, 0x29, 0x04, 0x53, 0xB0, 0x51, 0xB0, 0x60, 0x00, 0x7F, 0x7F, 0x7F, 0x7F, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xC3, 0x00, 0x62, 0xD0, 0x04, 0x55, 0xC2, 0x00, 0x62, 0xD0, 0x04, 0x09, 0x4E + } + }, + { + 190, + 79, + 0x39, + 0x00E9, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xE9, 0x55, 0xC1, 0x00, 0x7C, 0x3A, 0x1F, 0x10, 0x7C, 0x49, 0x5B, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x62, 0xD0, 0x03, 0x51, 0x9B, 0x21, 0xF0, 0x62, 0xD0, 0x04, 0x53, 0xC2, 0x7C, 0x6F, 0x2F, 0xA0, 0x06, 0x3D, 0x00, 0x30, 0xB0, 0x0B, 0x7C, 0x73, 0x26, 0x62, 0xD0, 0x00, 0x53, 0x39, 0x80, 0x07, 0x62, 0xD0, 0x00, 0x55, 0x39, 0x00, 0x62, 0xD0, 0x00, 0x51, 0x39, 0x62, 0xD0, 0x00, 0x53, 0x3A, 0x4C, 0xD5 + } + }, + { + 191, + 79, + 0x39, + 0x00EA, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xEA, 0x62, 0xD0, 0x00, 0x51, 0x39, 0x62, 0xD0, 0x00, 0x53, 0x3B, 0x62, 0xD0, 0x00, 0x51, 0x39, 0x62, 0xD0, 0x00, 0x53, 0x3C, 0x7C, 0x3A, 0x21, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x62, 0xD0, 0x04, 0x51, 0xCF, 0x54, 0x01, 0x10, 0x52, 0xFB, 0x7C, 0x49, 0x92, 0x20, 0x62, 0xD0, 0x04, 0x53, 0xC3, 0x3C, 0xC3, 0x00, 0xB0, 0x4F, 0x52, 0xFB, 0x3B, 0xFC, 0xA0, 0x49, 0x52, 0xFC, 0x3B, 0xFE, 0x3A + } + }, + { + 192, + 79, + 0x39, + 0x00EB, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xEB, 0xFB, 0xD0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xC3, 0x2F, 0x80, 0x21, 0x62, 0xD0, 0x04, 0x55, 0xC3, 0x4F, 0x3D, 0xFB, 0x00, 0xB0, 0x16, 0x7C, 0x3A, 0x2C, 0x7C, 0x3A, 0x22, 0x62, 0xD0, 0x00, 0x39, 0x00, 0xA0, 0x09, 0x7C, 0x3A, 0x22, 0x62, 0xD0, 0x04, 0x53, 0xC3, 0x62, 0xD0, 0x04, 0x51, 0xC1, 0x62, 0xD0, 0x04, 0x3A, 0xC3, 0xB0, 0x0C, 0x62, 0xD0, 0x04, 0x52, 0x01, 0x01, 0x01, 0x53, 0xCF, 0x63, 0x05 + } + }, + { + 193, + 79, + 0x39, + 0x00EC, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xEC, 0x80, 0x04, 0x7C, 0x70, 0x5B, 0x62, 0xD0, 0x04, 0x3C, 0xC3, 0x00, 0xA0, 0x09, 0x52, 0xFB, 0x08, 0x7C, 0x3A, 0x28, 0x38, 0xFF, 0x62, 0xD0, 0x04, 0x51, 0xC3, 0x08, 0x52, 0xFB, 0x08, 0x91, 0x86, 0x38, 0xFE, 0x39, 0x00, 0xA0, 0x21, 0x62, 0xD0, 0x04, 0x55, 0xC3, 0xFF, 0x62, 0xD0, 0x04, 0x51, 0xC1, 0x62, 0xD0, 0x04, 0x3A, 0xC3, 0xB0, 0x0C, 0x62, 0xD0, 0x04, 0x52, 0x01, 0x01, 0x01, 0x53, 0x63, 0x06 + } + }, + { + 194, + 79, + 0x39, + 0x00ED, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xED, 0xCF, 0x80, 0x04, 0x7C, 0x70, 0x5B, 0x62, 0xD0, 0x04, 0x3C, 0xC3, 0x00, 0xA0, 0x06, 0x56, 0x00, 0x01, 0x80, 0x04, 0x56, 0x00, 0x00, 0x52, 0xFB, 0x08, 0x52, 0xFC, 0x08, 0x50, 0x04, 0x08, 0x50, 0xC3, 0x08, 0x62, 0xD0, 0x00, 0x50, 0x0F, 0x08, 0x10, 0x7C, 0x2B, 0x40, 0x38, 0xFA, 0x62, 0xD0, 0x04, 0x3C, 0xC3, 0x00, 0xA0, 0x2B, 0x90, 0x31, 0x62, 0xD0, 0x04, 0x3C, 0xC3, 0x00, 0xA0, 0x21, 0x41, 0xC3 + } + }, + { + 195, + 79, + 0x39, + 0x00EE, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xEE, 0x62, 0xD0, 0x04, 0x3C, 0xCF, 0x00, 0xB0, 0x0F, 0x62, 0xD0, 0x04, 0x3C, 0xC3, 0xFF, 0xA0, 0x07, 0x62, 0xD0, 0x04, 0x55, 0xCF, 0x80, 0x62, 0xD0, 0x04, 0x51, 0xC3, 0x62, 0xD0, 0x04, 0x53, 0xC1, 0x52, 0x00, 0x62, 0xD0, 0x00, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x62, 0xD0, 0x04, 0x51, 0xC3, 0x54, 0x00, 0x3C, 0xC3, 0x00, 0xA0, 0x0B, 0x7C, 0x6F, 0x3C, 0x62, 0xD0, 0x00, 0x39, 0x14, 0x6A + } + }, + { + 196, + 79, + 0x39, + 0x00EF, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xEF, 0x30, 0xB0, 0x03, 0x80, 0xB1, 0x50, 0x10, 0x08, 0x50, 0x29, 0x08, 0x50, 0x28, 0x08, 0x90, 0xA9, 0x50, 0x20, 0x08, 0x50, 0x3F, 0x08, 0x50, 0x30, 0x08, 0x90, 0x9E, 0x38, 0xFA, 0x50, 0x40, 0x08, 0x50, 0x49, 0x08, 0x50, 0x48, 0x08, 0x90, 0x91, 0x50, 0x80, 0x08, 0x50, 0x9F, 0x08, 0x50, 0x90, 0x08, 0x90, 0x86, 0x38, 0xFA, 0x62, 0xD0, 0x04, 0x51, 0xC3, 0x3B, 0x00, 0xA0, 0x6E, 0x3D, 0x00, 0x76, 0x2F + } + }, + { + 197, + 79, + 0x39, + 0x00F0, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF0, 0x28, 0xC0, 0x69, 0x50, 0x29, 0x3B, 0x00, 0xC0, 0x63, 0x62, 0xD0, 0x04, 0x51, 0xDF, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x65, 0xE9, 0x51, 0xE9, 0x01, 0x10, 0x62, 0xD0, 0x04, 0x53, 0xC3, 0x62, 0xD0, 0x04, 0x51, 0xC1, 0x62, 0xD0, 0x04, 0x3A, 0xC3, 0xB0, 0x0F, 0x62, 0xD0, 0x04, 0x51, 0xBF, 0x01, 0x01, 0x62, 0xD0, 0x04, 0x53, 0xCF, 0x80, 0x04, 0x7C, 0x70, 0x5B, 0x50, 0x10, 0x08, 0x50, 0x29, 0x67, 0x12 + } + }, + { + 198, + 79, + 0x39, + 0x00F1, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF1, 0x08, 0x50, 0x28, 0x08, 0x90, 0x33, 0x50, 0x20, 0x08, 0x50, 0x3F, 0x08, 0x50, 0x30, 0x08, 0x90, 0x28, 0x38, 0xFA, 0x50, 0x40, 0x08, 0x50, 0x49, 0x08, 0x50, 0x48, 0x08, 0x90, 0x1B, 0x50, 0x80, 0x08, 0x50, 0x9F, 0x08, 0x50, 0x90, 0x08, 0x90, 0x10, 0x38, 0xFA, 0x62, 0xD0, 0x04, 0x51, 0xCF, 0x62, 0xD0, 0x04, 0x53, 0xBF, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x62, 0xD0, 0x04, 0x51, 0xC3, 0xF6, 0x31 + } + }, + { + 199, + 79, + 0x39, + 0x00F2, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF2, 0x3B, 0xFC, 0xC0, 0x21, 0x62, 0xD0, 0x04, 0x52, 0xFB, 0x3A, 0xC3, 0xC0, 0x18, 0x62, 0xD0, 0x04, 0x51, 0xC2, 0x23, 0xFA, 0x39, 0x00, 0xB0, 0x0D, 0x62, 0xD0, 0x04, 0x55, 0xC3, 0x00, 0x62, 0xD0, 0x04, 0x55, 0xCF, 0x00, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x0A, 0x50, 0x02, 0x3B, 0xFC, 0xD1, 0xA6, 0x7C, 0x73, 0x26, 0x54, 0x03, 0x56, 0x00, 0x00, 0x80, 0xCA, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x80, 0x46 + } + }, + { + 200, + 79, + 0x39, + 0x00F3, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF3, 0x06, 0xE8, 0xC0, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x7C, 0x6D, 0x8A, 0x51, 0xE8, 0x01, 0xE0, 0x54, 0x02, 0x51, 0xE9, 0x09, 0x01, 0x54, 0x01, 0x7C, 0x6D, 0xA5, 0x65, 0xE8, 0x6B, 0xE9, 0x06, 0xE8, 0xDD, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x7C, 0x6E, 0xE1, 0x52, 0x02, 0x53, 0xE6, 0x52, 0x01, 0x60, 0xD4, 0x3E, 0xE6, 0x53, 0xE6, 0x50, 0x00, 0x3A, 0xE9, 0xB0, 0x07, 0x51, 0xE6, 0x3A, 0xC6, 0xD3 + } + }, + { + 201, + 79, + 0x39, + 0x00F4, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF4, 0xE8, 0xA0, 0x03, 0x80, 0x84, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x01, 0x06, 0x7C, 0x6E, 0xA3, 0x54, 0x04, 0x3E, 0xE8, 0x54, 0x05, 0x52, 0x02, 0x01, 0x04, 0x7C, 0x6E, 0xA3, 0x54, 0x06, 0x3E, 0xE8, 0x54, 0x07, 0x7C, 0x6D, 0xA5, 0x65, 0xE8, 0x6B, 0xE9, 0x06, 0xE8, 0xF5, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x7C, 0x6E, 0xE1, 0x51, 0xE9, 0x08, 0x51, 0xE8, 0x08, 0x52, 0x04, 0x08, 0x52, 0x05, 0xBB, 0xBE + } + }, + { + 202, + 79, + 0x39, + 0x00F5, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF5, 0x08, 0x91, 0x1B, 0x38, 0xFC, 0x62, 0xD0, 0x00, 0x52, 0x03, 0x7C, 0x71, 0xD1, 0xC0, 0x31, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x65, 0xE8, 0x6B, 0xE9, 0x06, 0xE8, 0xA1, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x7C, 0x6E, 0xE1, 0x51, 0xE9, 0x08, 0x51, 0xE8, 0x08, 0x52, 0x06, 0x08, 0x52, 0x07, 0x08, 0x90, 0xEB, 0x38, 0xFC, 0x62, 0xD0, 0x00, 0x52, 0x03, 0x7C, 0x71, 0xD1, 0xD0, 0x03, 0x80, 0xCA, 0xDD + } + }, + { + 203, + 79, + 0x39, + 0x00F6, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF6, 0x08, 0x77, 0x00, 0x7C, 0x72, 0x41, 0xCF, 0x33, 0x50, 0x04, 0x3B, 0xFC, 0xD0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xC0, 0x04, 0x80, 0x08, 0x52, 0xFC, 0x62, 0xD0, 0x04, 0x53, 0xC0, 0x7C, 0x72, 0x41, 0xA0, 0xAD, 0x56, 0x00, 0x00, 0x80, 0x89, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xC0, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x7C, 0x6D, 0x8A, 0x51, 0xE8, 0x01, 0xE0, 0x54, 0x02, 0x51, 0x65, 0x14 + } + }, + { + 204, + 79, + 0x39, + 0x00F7, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF7, 0xE9, 0x09, 0x01, 0x54, 0x01, 0x7C, 0x6F, 0xF6, 0x53, 0xE8, 0x7C, 0x6E, 0xAE, 0x65, 0xE6, 0x6B, 0xE7, 0x06, 0xE6, 0xDD, 0x0E, 0xE7, 0x02, 0x51, 0xE7, 0x60, 0xD5, 0x50, 0x00, 0x3F, 0xE6, 0x51, 0xE8, 0x3F, 0xE6, 0x52, 0x02, 0x01, 0x06, 0x53, 0xE8, 0x52, 0x01, 0x09, 0x00, 0x7C, 0x6D, 0xE3, 0x3E, 0xE8, 0x53, 0xE8, 0x7C, 0x6E, 0xAE, 0x65, 0xE6, 0x6B, 0xE7, 0x06, 0xE6, 0xF5, 0x0E, 0xE7, 0xC3, 0xD1 + } + }, + { + 205, + 79, + 0x39, + 0x00F8, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF8, 0x02, 0x7C, 0x6D, 0xEA, 0x51, 0xE8, 0x3F, 0xE6, 0x52, 0x02, 0x01, 0x04, 0x53, 0xE8, 0x52, 0x01, 0x09, 0x00, 0x7C, 0x6D, 0xE3, 0x3E, 0xE8, 0x53, 0xE8, 0x7C, 0x6E, 0xAE, 0x65, 0xE6, 0x6B, 0xE7, 0x06, 0xE6, 0xA1, 0x0E, 0xE7, 0x03, 0x7C, 0x6D, 0xEA, 0x51, 0xE8, 0x3F, 0xE6, 0x77, 0x00, 0x7C, 0x72, 0x41, 0xCF, 0x74, 0x3D, 0xFB, 0x00, 0xB0, 0x09, 0x56, 0x09, 0x01, 0x56, 0x08, 0x00, 0x80, 0x53, 0xF2 + } + }, + { + 206, + 79, + 0x39, + 0x00F9, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xF9, 0x07, 0x56, 0x09, 0x00, 0x56, 0x08, 0x00, 0x62, 0xD0, 0x00, 0x52, 0x09, 0x80, 0x0D, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x80, 0x06, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x38, 0xF6, 0x20, 0x7F, 0x10, 0x4F, 0x52, 0xFC, 0x13, 0xFA, 0x52, 0xFB, 0x1B, 0xF9, 0xC0, 0x12, 0x62, 0xD0, 0x00, 0x52, 0xFC, 0x13, 0xFA, 0x53, 0xE8, 0x52, 0xFB, 0x1B, 0xF9, 0x53, 0xE9, 0x80, 0x10, 0x62, 0xD0, 0x00, 0x52, 0xFA, 0x95, 0x77 + } + }, + { + 207, + 79, + 0x39, + 0x00FA, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xFA, 0x13, 0xFC, 0x53, 0xE8, 0x52, 0xF9, 0x1B, 0xFB, 0x53, 0xE9, 0x20, 0x7F, 0x10, 0x4F, 0x7C, 0x72, 0x93, 0xB0, 0x22, 0x3D, 0xFC, 0x01, 0xB0, 0x32, 0x62, 0xD0, 0x04, 0x51, 0xCF, 0x08, 0x50, 0x0E, 0x08, 0x7C, 0x32, 0x0C, 0x62, 0xD0, 0x04, 0x51, 0xC3, 0x08, 0x50, 0x0F, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x80, 0x16, 0x7C, 0x6F, 0x3C, 0x39, 0x30, 0xB0, 0x0F, 0x62, 0xD0, 0x04, 0x51, 0xC3, 0xA8, 0x9E + } + }, + { + 208, + 79, + 0x39, + 0x00FB, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xFB, 0x08, 0x50, 0x24, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x52, 0xFC, 0x08, 0x7C, 0x3A, 0x2D, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x62, 0xE3, 0x38, 0x7C, 0x2B, 0x06, 0x71, 0x01, 0x90, 0x67, 0x90, 0xC5, 0x80, 0x5D, 0x62, 0xD0, 0x00, 0x26, 0xAE, 0xFB, 0x51, 0xAE, 0x60, 0x00, 0x7C, 0x70, 0x4B, 0x51, 0xAE, 0x29, 0x04, 0x53, 0xAE, 0x51, 0xAE, 0x60, 0x00, 0x7C, 0x70, 0x4B, 0x26, 0x61, 0x11 + } + }, + { + 209, + 79, + 0x39, + 0x00FC, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xFC, 0xAF, 0xFD, 0x51, 0xAF, 0x60, 0x04, 0x7C, 0x70, 0x4B, 0x51, 0xAF, 0x29, 0x02, 0x7C, 0x6F, 0x20, 0x56, 0x01, 0x00, 0x80, 0x03, 0x77, 0x01, 0x3D, 0x01, 0x0A, 0xCF, 0xFA, 0x62, 0xE3, 0x38, 0x90, 0x9D, 0x62, 0xD0, 0x04, 0x3C, 0xC4, 0x00, 0xA0, 0x0C, 0x7C, 0x45, 0x53, 0x7C, 0x2C, 0x1E, 0x62, 0xD0, 0x00, 0x54, 0x00, 0x52, 0x00, 0x08, 0x90, 0x9B, 0x52, 0x00, 0x08, 0x7C, 0x39, 0x02, 0x38, 0x9A, 0x84 + } + }, + { + 210, + 79, + 0x39, + 0x00FD, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xFD, 0xFE, 0x8F, 0xA3, 0x38, 0xFE, 0x20, 0x8F, 0xFF, 0x10, 0x4F, 0x38, 0x01, 0x10, 0x7C, 0x16, 0x34, 0x20, 0x10, 0x57, 0x13, 0x50, 0x88, 0x7C, 0x2B, 0xA8, 0x20, 0x62, 0xD0, 0x04, 0x55, 0xC4, 0x01, 0x62, 0xD0, 0x00, 0x55, 0xA7, 0x0C, 0x62, 0xD0, 0x00, 0x55, 0xA8, 0x05, 0x62, 0xD0, 0x00, 0x55, 0xA9, 0x01, 0x10, 0x7C, 0x17, 0xB7, 0x7C, 0x19, 0x82, 0x62, 0xD0, 0x00, 0x20, 0x39, 0x00, 0xA0, 0xEE, 0x2D + } + }, + { + 211, + 79, + 0x39, + 0x00FE, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xFE, 0x1F, 0x71, 0x10, 0x5D, 0xE0, 0x54, 0x00, 0x41, 0xE0, 0xE7, 0x43, 0xE0, 0x18, 0x70, 0xCF, 0x62, 0xE3, 0x38, 0x7C, 0x49, 0x07, 0x62, 0xE3, 0x38, 0x52, 0x00, 0x7C, 0x72, 0xB2, 0x80, 0x06, 0x10, 0x7C, 0x10, 0x74, 0x20, 0x38, 0xFF, 0x20, 0x7F, 0x7C, 0x33, 0x69, 0x7C, 0x40, 0x59, 0x7C, 0x45, 0x51, 0x7C, 0x2B, 0xCA, 0x7C, 0x3A, 0x31, 0x7C, 0x40, 0x10, 0x7C, 0x2B, 0x19, 0x7F, 0x7C, 0x40, 0x55, 0xFC + } + }, + { + 212, + 79, + 0x39, + 0x00FF, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0xFF, 0x98, 0x7C, 0x36, 0x78, 0x7C, 0x45, 0x52, 0x7C, 0x2B, 0xFD, 0x7C, 0x3A, 0x4C, 0x7C, 0x40, 0x1E, 0x7C, 0x2B, 0x23, 0x7F, 0x10, 0x4F, 0x52, 0xFC, 0x08, 0x7C, 0x38, 0x88, 0x52, 0xFC, 0x08, 0x7C, 0x45, 0x4D, 0x38, 0xFE, 0x52, 0xFC, 0x08, 0x7C, 0x45, 0x76, 0x52, 0xFC, 0x08, 0x7C, 0x2E, 0x27, 0x38, 0xFE, 0x52, 0xFC, 0x08, 0x7C, 0x3E, 0x8C, 0x52, 0xFC, 0x08, 0x7C, 0x40, 0x28, 0x38, 0xFE, 0x1F, 0x91 + } + }, + { + 213, + 79, + 0x39, + 0x0100, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x00, 0x52, 0xFC, 0x08, 0x7C, 0x2B, 0x48, 0x52, 0xFC, 0x08, 0x7C, 0x38, 0xC9, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x62, 0xD0, 0x04, 0x55, 0xC5, 0x04, 0x38, 0xFF, 0x20, 0x7F, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xC5, 0x62, 0xD0, 0x00, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x62, 0xD0, 0x00, 0x51, 0x54, 0x54, 0x01, 0x51, 0x53, 0x54, 0x00, 0x70, 0xFE, 0x10, 0x7C, 0x1E, 0xFE, 0x51 + } + }, + { + 214, + 79, + 0x39, + 0x0101, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x01, 0x1C, 0x62, 0xD0, 0x00, 0x20, 0x10, 0x52, 0x00, 0x08, 0x52, 0x01, 0x20, 0x7C, 0x1D, 0xD4, 0x20, 0x62, 0xDA, 0xF7, 0x71, 0x01, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x7C, 0x1E, 0x1C, 0x20, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x55, 0x0D, 0x00, 0x55, 0x0C, 0x00, 0x7C, 0x72, 0x19, 0x92, 0x21, 0x7C, 0x73, 0x6D, 0x10, 0x7C, 0x1D, 0xCC, 0x7C, 0x20, 0x80, 0x7C, 0x20, 0x78, 0x20, 0x62, 0xD0, 0x00, 0x55, 0xFD, 0x50 + } + }, + { + 215, + 79, + 0x39, + 0x0102, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x02, 0xEF, 0x00, 0x55, 0xEE, 0x00, 0x62, 0xD0, 0x03, 0x55, 0xC8, 0x00, 0x55, 0xC9, 0x00, 0x55, 0xCA, 0x00, 0x55, 0xCB, 0x00, 0x7C, 0x2B, 0x67, 0x7F, 0x10, 0x4F, 0x38, 0x03, 0x7C, 0x72, 0x93, 0xB0, 0xF9, 0x7C, 0x70, 0xE8, 0x54, 0x01, 0x51, 0x0C, 0x54, 0x00, 0x71, 0x01, 0x62, 0xD0, 0x03, 0x47, 0x99, 0x02, 0xA0, 0x3F, 0x93, 0x58, 0x9F, 0x74, 0x7C, 0x70, 0xE8, 0x08, 0x51, 0x0C, 0x62, 0xD0, 0xDD, 0x11 + } + }, + { + 216, + 79, + 0x39, + 0x0103, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x03, 0x00, 0x53, 0xEE, 0x18, 0x53, 0xEF, 0x71, 0x01, 0x62, 0xD0, 0x03, 0x55, 0xC8, 0x00, 0x55, 0xC9, 0x00, 0x55, 0xCA, 0x00, 0x55, 0xCB, 0x00, 0x7C, 0x2B, 0x67, 0x10, 0x7C, 0x18, 0x83, 0x20, 0x10, 0x57, 0x13, 0x50, 0x88, 0x7C, 0x2B, 0xA8, 0x7C, 0x10, 0x74, 0x20, 0x91, 0x3B, 0x7C, 0x6F, 0xEA, 0x81, 0x33, 0x62, 0xD0, 0x03, 0x51, 0x9A, 0x21, 0x0F, 0x54, 0x02, 0x62, 0xD0, 0x04, 0x3C, 0xCB, 0x0A, 0x6C + } + }, + { + 217, + 79, + 0x39, + 0x0104, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x04, 0xF1, 0xB0, 0x45, 0x62, 0xD0, 0x03, 0x3C, 0x9C, 0x00, 0xA0, 0x03, 0x93, 0x67, 0x3D, 0x02, 0x00, 0xA0, 0x06, 0x7C, 0x6F, 0xEA, 0x80, 0x97, 0x7C, 0x73, 0x2E, 0xA0, 0x27, 0x62, 0xD0, 0x03, 0x52, 0x01, 0x12, 0xE5, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x52, 0x00, 0x62, 0xD0, 0x03, 0x1A, 0xE4, 0x7C, 0x72, 0x49, 0x62, 0xD0, 0x03, 0x12, 0xDB, 0x7C, 0x70, 0xA1, 0x1A, 0xDA, 0xC0, 0x70, 0x91, 0x2A, 0x7B, 0x4F + } + }, + { + 218, + 79, + 0x39, + 0x0105, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x05, 0x80, 0x6C, 0x7C, 0x6F, 0xEA, 0x80, 0x67, 0x62, 0xD0, 0x04, 0x3C, 0xCB, 0xF2, 0xB0, 0x1E, 0x3D, 0x02, 0x00, 0xB0, 0x06, 0x7C, 0x73, 0x2E, 0xB0, 0x08, 0x90, 0xCD, 0x7C, 0x6F, 0xEA, 0x80, 0x4E, 0x62, 0xD0, 0x03, 0x3C, 0x9D, 0x00, 0xA0, 0x46, 0x93, 0x0A, 0x80, 0x42, 0x9E, 0xBE, 0x7C, 0x70, 0xE8, 0x08, 0x51, 0x0C, 0x62, 0xD0, 0x00, 0x53, 0xEE, 0x18, 0x53, 0xEF, 0x71, 0x01, 0x62, 0xD0, 0xC8, 0xEA + } + }, + { + 219, + 79, + 0x39, + 0x0106, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x06, 0x03, 0x55, 0xC8, 0x00, 0x55, 0xC9, 0x00, 0x55, 0xCA, 0x00, 0x55, 0xCB, 0x00, 0x7C, 0x2B, 0x67, 0x90, 0xFD, 0x90, 0x94, 0x7C, 0x6F, 0xEA, 0x80, 0x15, 0x62, 0xD0, 0x04, 0x3C, 0xCB, 0xF4, 0xA0, 0x0D, 0x10, 0x57, 0x00, 0x50, 0x01, 0x7C, 0x1D, 0xD4, 0x20, 0x7C, 0x73, 0x6D, 0x7C, 0x72, 0x93, 0xB0, 0x70, 0x7C, 0x70, 0xE8, 0x54, 0x01, 0x51, 0x0C, 0x54, 0x00, 0x71, 0x01, 0x62, 0xD0, 0x00, 0x44, 0xE3 + } + }, + { + 220, + 79, + 0x39, + 0x0107, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x07, 0x52, 0x01, 0x12, 0xEF, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x52, 0x00, 0x62, 0xD0, 0x00, 0x1A, 0xEE, 0x7C, 0x72, 0x49, 0x53, 0xE6, 0x51, 0xE9, 0x53, 0xE7, 0x51, 0xE6, 0x62, 0xD0, 0x03, 0x04, 0xCB, 0x62, 0xD0, 0x00, 0x51, 0xE7, 0x62, 0xD0, 0x03, 0x0C, 0xCA, 0x0E, 0xC9, 0x00, 0x0E, 0xC8, 0x00, 0x62, 0xD0, 0x00, 0x52, 0x01, 0x53, 0xEF, 0x52, 0x00, 0x53, 0xEE, 0x62, 0xD0, 0x03, 0x51, 0xCB, 0x39, 0xCE + } + }, + { + 221, + 79, + 0x39, + 0x0108, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x08, 0x11, 0x30, 0x51, 0xCA, 0x19, 0x75, 0x51, 0xC9, 0x19, 0x00, 0x51, 0xC8, 0x19, 0x00, 0xC0, 0x12, 0x9E, 0x1A, 0x62, 0xD0, 0x03, 0x55, 0xC8, 0x00, 0x55, 0xC9, 0x00, 0x55, 0xCA, 0x00, 0x55, 0xCB, 0x00, 0x7C, 0x2B, 0x68, 0x38, 0xFD, 0x20, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xCB, 0xF1, 0x62, 0xD0, 0x03, 0x51, 0x9C, 0x08, 0x62, 0xD0, 0x03, 0x55, 0xD8, 0x00, 0x18, 0x53, 0xD9, 0x62, 0xD0, 0x03, 0xB7, 0xCB + } + }, + { + 222, + 79, + 0x39, + 0x0109, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x09, 0x3C, 0x9C, 0x00, 0xA0, 0x14, 0x10, 0x62, 0xD0, 0x03, 0x51, 0xD8, 0x08, 0x51, 0xD9, 0x20, 0x7C, 0x1D, 0xD4, 0x20, 0x7C, 0x70, 0xAA, 0x80, 0x0F, 0x10, 0x57, 0x00, 0x50, 0x01, 0x7C, 0x1D, 0xD4, 0x20, 0x70, 0xFE, 0x7C, 0x72, 0x19, 0x7C, 0x71, 0x54, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xCB, 0xF2, 0x62, 0xD0, 0x03, 0x51, 0xD7, 0x08, 0x51, 0xD6, 0x62, 0xD0, 0x03, 0x53, 0xD8, 0x18, 0x53, 0xD9, 0xF0, 0x3E + } + }, + { + 223, + 79, + 0x39, + 0x010A, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x0A, 0x10, 0x51, 0xD8, 0x08, 0x51, 0xD9, 0x20, 0x7C, 0x1D, 0xD4, 0x20, 0x7C, 0x70, 0xAA, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x62, 0xD0, 0x03, 0x50, 0x78, 0x3A, 0x9D, 0xD0, 0x07, 0x62, 0xD0, 0x03, 0x55, 0x9D, 0x78, 0x7C, 0x71, 0xD8, 0x53, 0xE9, 0x65, 0xE9, 0x7C, 0x71, 0xD8, 0x64, 0x64, 0x64, 0x02, 0xE9, 0x54, 0x00, 0x80, 0x09, 0x62, 0xD0, 0x03, 0x76, 0x9D, 0x07, 0x00, 0x0A, 0x62, 0xD0, 0x03, 0xA5, 0xA9 + } + }, + { + 224, + 79, + 0x39, + 0x010B, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x0B, 0x3C, 0x9D, 0x1A, 0xD0, 0x0A, 0x62, 0xD0, 0x03, 0x52, 0x00, 0x3A, 0x9C, 0xCF, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0x9D, 0x08, 0x62, 0xD0, 0x03, 0x55, 0xD6, 0x00, 0x18, 0x53, 0xD7, 0x62, 0xD0, 0x03, 0x51, 0x9D, 0x7C, 0x70, 0x0E, 0x7C, 0x6D, 0x9C, 0x62, 0xD0, 0x03, 0x51, 0xD7, 0x62, 0xD0, 0x00, 0x04, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0xD6, 0x62, 0xD0, 0x00, 0x0C, 0xE9, 0x7C, 0x70, 0x17, 0x08, 0x59, 0x12 + } + }, + { + 225, + 79, + 0x39, + 0x010C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x0C, 0x51, 0xE9, 0x62, 0xD0, 0x03, 0x53, 0xD6, 0x18, 0x53, 0xD7, 0x62, 0xD0, 0x03, 0x51, 0x9E, 0x08, 0x62, 0xD0, 0x03, 0x55, 0xDA, 0x00, 0x18, 0x53, 0xDB, 0x51, 0xDB, 0x08, 0x51, 0xDA, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x18, 0x53, 0xE8, 0x7C, 0x6D, 0x9C, 0x62, 0xD0, 0x03, 0x51, 0xDB, 0x62, 0xD0, 0x00, 0x04, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0xDA, 0x62, 0xD0, 0x00, 0x0C, 0xE9, 0x7C, 0x70, 0x17, 0x5E, 0x1D + } + }, + { + 226, + 79, + 0x39, + 0x010D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x0D, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x03, 0x53, 0xDA, 0x18, 0x53, 0xDB, 0x38, 0xFF, 0x20, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0xAE, 0x21, 0x0D, 0x60, 0x00, 0x62, 0xD0, 0x00, 0x51, 0xAF, 0x21, 0xBB, 0x60, 0x04, 0x62, 0xD0, 0x00, 0x51, 0xB0, 0x21, 0x74, 0x7C, 0x73, 0x1E, 0x21, 0x00, 0x7C, 0x73, 0x16, 0x21, 0x10, 0x60, 0x10, 0x71, 0x10, 0x5D, 0x00, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x53, 0xCA, 0x71, 0x6E, 0x3E + } + }, + { + 227, + 79, + 0x39, + 0x010E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x0E, 0x10, 0x5D, 0x04, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x53, 0xC9, 0x71, 0x10, 0x5D, 0x08, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x53, 0xC8, 0x71, 0x10, 0x5D, 0x0C, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x53, 0xC7, 0x71, 0x10, 0x5D, 0x10, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x53, 0xC6, 0x71, 0x10, 0x43, 0x00, 0xF2, 0x43, 0x04, 0x44, 0x43, 0x08, 0x8B, 0x43, 0x0C, 0xFF, 0x43, 0x10, 0xEF, 0x70, 0xCF, 0x7F, 0x62, 0x34, 0xCB + } + }, + { + 228, + 79, + 0x39, + 0x010F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x0F, 0xD0, 0x04, 0x51, 0xCA, 0x71, 0x10, 0x60, 0x00, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x51, 0xC9, 0x71, 0x10, 0x60, 0x04, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x51, 0xC8, 0x71, 0x10, 0x60, 0x08, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x51, 0xC7, 0x71, 0x10, 0x60, 0x0C, 0x70, 0xCF, 0x62, 0xD0, 0x04, 0x51, 0xC6, 0x71, 0x10, 0x60, 0x10, 0x70, 0xCF, 0x62, 0xD0, 0x00, 0x51, 0xAE, 0x60, 0x00, 0x62, 0xD0, 0x00, 0xB4, 0xCC + } + }, + { + 229, + 79, + 0x39, + 0x0110, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x10, 0x7C, 0x72, 0x51, 0x51, 0xB0, 0x7C, 0x73, 0x1E, 0x7C, 0x73, 0x16, 0x60, 0x10, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x71, 0x10, 0x43, 0xEC, 0x01, 0x70, 0xFE, 0x70, 0xCF, 0x9F, 0x32, 0x7C, 0x31, 0xC5, 0x62, 0xD0, 0x00, 0x39, 0x00, 0xB0, 0x3A, 0x7C, 0x72, 0xCB, 0x10, 0x70, 0xCF, 0x7C, 0x1D, 0xD0, 0x7C, 0x20, 0x7C, 0x20, 0x62, 0xDB, 0xFE, 0x7C, 0x39, 0xF1, 0x71, 0x10, 0x43, 0xD7, 0x20, 0x43, 0x58, 0x15 + } + }, + { + 230, + 79, + 0x39, + 0x0111, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x11, 0xC9, 0x80, 0x70, 0xCF, 0x43, 0xFF, 0x08, 0x71, 0x10, 0x41, 0xC9, 0x7F, 0x41, 0xD7, 0xDF, 0x40, 0x7C, 0x73, 0x7B, 0x70, 0xCF, 0x7C, 0x3A, 0x08, 0x10, 0x7C, 0x20, 0x78, 0x7C, 0x1D, 0xCC, 0x20, 0x62, 0xD0, 0x00, 0x55, 0xBB, 0xFF, 0x62, 0xD0, 0x03, 0x26, 0x99, 0xFD, 0x9F, 0x51, 0x71, 0x01, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x56, 0x00, 0x00, 0x71, 0x10, 0x41, 0xEC, 0xFE, 0x27, 0xB4 + } + }, + { + 231, + 79, + 0x39, + 0x0112, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x12, 0x70, 0xFE, 0x70, 0xCF, 0x9E, 0xC9, 0x7C, 0x71, 0x10, 0x80, 0x6B, 0x7C, 0x32, 0x06, 0x62, 0xD0, 0x00, 0x7C, 0x31, 0xC5, 0x39, 0x00, 0xB0, 0x5A, 0x3D, 0x00, 0x00, 0xB0, 0x55, 0x62, 0xD0, 0x00, 0x3C, 0xBB, 0xFF, 0xA0, 0x4D, 0x7C, 0x39, 0xE9, 0x62, 0xD0, 0x03, 0x51, 0xD9, 0x11, 0x02, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0xD8, 0x19, 0x00, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x50, 0x07 + } + }, + { + 232, + 79, + 0x39, + 0x0113, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x13, 0x62, 0xD0, 0x00, 0x51, 0xBB, 0x62, 0xD0, 0x00, 0x7C, 0x70, 0xBF, 0xC0, 0x22, 0x7C, 0x72, 0xCB, 0x56, 0x00, 0x01, 0x10, 0x70, 0xCF, 0x7C, 0x20, 0x7C, 0x20, 0x62, 0xDB, 0xFE, 0x10, 0x7C, 0x0C, 0x80, 0x20, 0x71, 0x10, 0x7C, 0x73, 0x7B, 0x10, 0x70, 0xCF, 0x7C, 0x20, 0x78, 0x20, 0x7C, 0x39, 0xED, 0x71, 0x01, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x51, 0x0D, 0x62, 0xD0, 0x03, 0x12, 0xD5, 0x62, 0x05, 0x72 + } + }, + { + 233, + 79, + 0x39, + 0x0114, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x14, 0xD0, 0x00, 0x53, 0xE8, 0x62, 0xD0, 0x00, 0x51, 0x0C, 0x62, 0xD0, 0x03, 0x1A, 0xD4, 0x7C, 0x72, 0x49, 0x62, 0xD0, 0x03, 0x12, 0xD9, 0x7C, 0x70, 0xA1, 0x1A, 0xD8, 0xCF, 0x6F, 0x52, 0x01, 0x7C, 0x72, 0xB2, 0x9E, 0x9B, 0x7C, 0x71, 0x54, 0x71, 0x01, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x62, 0xD0, 0x04, 0x3C, 0xCB, 0xF2, 0xB0, 0x09, 0x56, 0x01, 0x01, 0x56, 0x00, 0x00, 0x80, 0x34, 0xD1 + } + }, + { + 234, + 79, + 0x39, + 0x0115, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x15, 0x04, 0x7C, 0x6F, 0xC9, 0x62, 0xD0, 0x00, 0x52, 0x01, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x7F, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x56, 0x00, 0x00, 0x10, 0x7C, 0x18, 0x83, 0x7C, 0x19, 0x64, 0x20, 0x56, 0x00, 0x01, 0x52, 0x00, 0x08, 0x7C, 0x2B, 0x34, 0x38, 0xFF, 0x10, 0x7C, 0x18, 0x4D, 0x20, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x04, 0x7C, 0x6F, 0x3C, 0x54, 0x01, 0x3D, 0x3A, 0xDE + } + }, + { + 235, + 79, + 0x39, + 0x0116, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x16, 0x01, 0x50, 0xB0, 0x29, 0x56, 0x00, 0x00, 0x80, 0x1D, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x08, 0x52, 0x00, 0x01, 0x07, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x77, 0x00, 0x3D, 0x00, 0x99, 0xCF, 0xE0, 0x81, 0x85, 0x3D, 0x01, 0x40, 0xB0, 0x29, 0x56, 0x00, 0x00, 0x80, 0x1D, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x00, 0x0E, 0x92, 0x8F + } + }, + { + 236, + 79, + 0x39, + 0x0117, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x17, 0xE9, 0x05, 0x7C, 0x6D, 0x83, 0x08, 0x52, 0x00, 0x01, 0x07, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x77, 0x00, 0x3D, 0x00, 0x99, 0xCF, 0xE0, 0x81, 0x58, 0x3D, 0x01, 0x70, 0xB0, 0x61, 0x7C, 0x39, 0x34, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x26, 0xE9, 0x01, 0x3C, 0xE9, 0x01, 0xB0, 0x29, 0x56, 0x00, 0x00, 0x80, 0x1D, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x05, 0x7C, 0xC7, 0xFA + } + }, + { + 237, + 79, + 0x39, + 0x0118, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x18, 0x6D, 0x83, 0x08, 0x52, 0x00, 0x01, 0x07, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x77, 0x00, 0x3D, 0x00, 0x99, 0xCF, 0xE0, 0x81, 0x1B, 0x56, 0x00, 0x00, 0x80, 0x1D, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x08, 0x7C, 0x6D, 0x83, 0x08, 0x52, 0x00, 0x01, 0x07, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x77, 0x00, 0x3D, 0x00, 0x99, 0xCF, 0xE0, 0x80, 0xF3, 0x3D, 0x01, 0xF5, 0x57 + } + }, + { + 238, + 79, + 0x39, + 0x0119, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x19, 0x60, 0xB0, 0x95, 0x7C, 0x39, 0x34, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x26, 0xE9, 0x01, 0x3C, 0xE9, 0x01, 0xB0, 0x5D, 0x50, 0x1B, 0x08, 0x50, 0x07, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x56, 0x00, 0x00, 0x80, 0x1D, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x09, 0x7C, 0x6F, 0x5C, 0x08, 0x52, 0x00, 0x01, 0x08, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x77, 0x00, 0x3D, 0xF8, 0x5E + } + }, + { + 239, + 79, + 0x39, + 0x011A, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x1A, 0x00, 0x1B, 0xCF, 0xE0, 0x56, 0x02, 0x23, 0x56, 0x00, 0x00, 0x80, 0x1D, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x25, 0x0E, 0xE9, 0x09, 0x7C, 0x6F, 0x5C, 0x08, 0x52, 0x02, 0x03, 0x00, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x77, 0x00, 0x3D, 0x00, 0x1B, 0xCF, 0xE0, 0x80, 0x82, 0x56, 0x00, 0x00, 0x80, 0x1D, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x0A, 0xCB, 0x05 + } + }, + { + 240, + 79, + 0x39, + 0x011B, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x1B, 0x7C, 0x6F, 0x5C, 0x08, 0x52, 0x00, 0x01, 0x07, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x77, 0x00, 0x3D, 0x00, 0x99, 0xCF, 0xE0, 0x80, 0x5A, 0x3D, 0x01, 0x30, 0xB0, 0x55, 0x62, 0xD0, 0x03, 0x3C, 0x9F, 0x99, 0xD0, 0x4D, 0x62, 0xD0, 0x03, 0x51, 0x9F, 0x7C, 0x70, 0x0E, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x08, 0x7C, 0x6D, 0x83, 0x08, 0x50, 0x26, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFE, 0x62, 0xD0, 0xD0, 0x10 + } + }, + { + 241, + 79, + 0x39, + 0x011C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x1C, 0x03, 0x51, 0x9F, 0x7C, 0x70, 0x0E, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x08, 0x50, 0x27, 0x08, 0x7C, 0x32, 0x0C, 0x62, 0xD0, 0x03, 0x51, 0x9F, 0x7C, 0x70, 0x0E, 0x06, 0xE8, 0x00, 0x0E, 0xE9, 0x05, 0x7C, 0x6D, 0x83, 0x08, 0x50, 0x28, 0x08, 0x7C, 0x32, 0x0C, 0x38, 0xFC, 0x38, 0xFC, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x03, 0x56, 0x00, 0x00, 0x80, 0x5A, 0x62, 0xD0, 0x00, 0x3B, 0xE7 + } + }, + { + 242, + 79, + 0x39, + 0x011D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x1D, 0x52, 0x00, 0x7C, 0x6D, 0x8A, 0x06, 0xE8, 0xE6, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0x54, 0x01, 0x3E, 0xE8, 0x54, 0x02, 0x7C, 0x70, 0x01, 0x7C, 0x70, 0xD4, 0x52, 0x02, 0x53, 0xE9, 0x7C, 0x6E, 0xAE, 0x06, 0xE6, 0xB8, 0x0E, 0xE7, 0x03, 0x7C, 0x6D, 0xEA, 0x52, 0x00, 0x7C, 0x6D, 0x8A, 0x06, 0xE8, 0xE4, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0x54, 0x01, 0x3E, 0xE8, 0x54, 0x02, 0x7C, 0x70, 0xCA, 0x06 + } + }, + { + 243, + 79, + 0x39, + 0x011E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x1E, 0xD4, 0x7C, 0x70, 0x01, 0x52, 0x02, 0x53, 0xE9, 0x7C, 0x6E, 0xAE, 0x06, 0xE6, 0xB4, 0x0E, 0xE7, 0x03, 0x7C, 0x6D, 0xEA, 0x77, 0x00, 0x7C, 0x6F, 0xC1, 0xCF, 0xA3, 0x52, 0xFC, 0x08, 0x7C, 0x5F, 0x91, 0x38, 0xFF, 0x38, 0xFD, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x7C, 0x6F, 0xB9, 0x52, 0xFB, 0x60, 0xD5, 0x50, 0x04, 0x3F, 0xE8, 0x52, 0xFC, 0x01, 0x01, 0x7C, 0x71, 0x76, 0x50, 0x01, 0x3F, 0x32, 0xD7 + } + }, + { + 244, + 79, + 0x39, + 0x011F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x1F, 0xE8, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x03, 0x7C, 0x6F, 0xB9, 0x52, 0xFB, 0x7C, 0x6D, 0xE3, 0x47, 0xE9, 0x80, 0xBF, 0xF4, 0x52, 0xFC, 0x53, 0xE8, 0x52, 0xFB, 0x60, 0xD4, 0x3E, 0xE8, 0x39, 0x04, 0xB0, 0x73, 0x56, 0x00, 0x00, 0x80, 0x3F, 0x62, 0xD0, 0x00, 0x52, 0x00, 0x7C, 0x6D, 0x8A, 0x52, 0xFC, 0x01, 0x02, 0x53, 0xE6, 0x52, 0xFB, 0x09, 0x00, 0x53, 0xE7, 0x51, 0xE8, 0x02, 0x69, 0x46 + } + }, + { + 245, + 79, + 0x39, + 0x0120, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x20, 0xE6, 0x53, 0xE6, 0x51, 0xE9, 0x0A, 0xE7, 0x53, 0xE7, 0x06, 0xE8, 0xE0, 0x0E, 0xE9, 0x01, 0x51, 0xE9, 0x60, 0xD4, 0x51, 0xE7, 0x60, 0xD5, 0x10, 0x57, 0x08, 0x62, 0xD0, 0x00, 0x3E, 0xE8, 0x3F, 0xE6, 0x79, 0xBF, 0xF7, 0x20, 0x77, 0x00, 0x7C, 0x6F, 0xC1, 0xCF, 0xBE, 0x62, 0xD0, 0x00, 0x52, 0xFC, 0x01, 0x22, 0x7C, 0x71, 0x76, 0x52, 0xF9, 0x3F, 0xE8, 0x52, 0xFC, 0x01, 0x23, 0x7C, 0x71, 0xE7, 0x43 + } + }, + { + 246, + 79, + 0x39, + 0x0121, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x21, 0x76, 0x52, 0xFA, 0x3F, 0xE8, 0x52, 0xFC, 0x01, 0x24, 0x7C, 0x71, 0x76, 0x62, 0xD0, 0x00, 0x51, 0xA2, 0x62, 0xD0, 0x00, 0x3F, 0xE8, 0x7C, 0x6F, 0xB9, 0x52, 0xFB, 0x60, 0xD5, 0x50, 0x84, 0x3F, 0xE8, 0x38, 0xFD, 0x20, 0x7F, 0x10, 0x4F, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x62, 0xD0, 0x03, 0x55, 0xAC, 0x19, 0x55, 0xAB, 0x00, 0x55, 0xAA, 0x02, 0x62, 0xD0, 0x00, 0x55, 0xE8, 0x00, 0x55, 0xDD, 0x30 + } + }, + { + 247, + 79, + 0x39, + 0x0122, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x22, 0xE9, 0x0A, 0x7C, 0x6D, 0xAD, 0x7C, 0x6D, 0xC6, 0x51, 0xE8, 0x62, 0xD0, 0x03, 0x53, 0xA9, 0x10, 0x50, 0x03, 0x08, 0x50, 0xA9, 0x5C, 0x18, 0x7C, 0x20, 0x98, 0x20, 0x62, 0xD0, 0x03, 0x50, 0x00, 0x01, 0x80, 0x53, 0xAB, 0x50, 0x02, 0x09, 0x00, 0x53, 0xAA, 0x62, 0xD0, 0x00, 0x55, 0xE8, 0x00, 0x55, 0xE9, 0x0A, 0x7C, 0x6D, 0xC6, 0x7C, 0x6D, 0xAD, 0x7C, 0x73, 0x3C, 0x51, 0xE8, 0x62, 0xD0, 0xBC, 0xEF + } + }, + { + 248, + 79, + 0x39, + 0x0123, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x23, 0x03, 0x53, 0xA9, 0x10, 0x50, 0x03, 0x08, 0x50, 0xA9, 0x5C, 0x18, 0x7C, 0x20, 0x98, 0x20, 0x62, 0xD0, 0x03, 0x55, 0xAB, 0xA0, 0x55, 0xAA, 0x01, 0x62, 0xD0, 0x00, 0x55, 0xE8, 0x00, 0x55, 0xE9, 0x0A, 0x7C, 0x6D, 0xC6, 0x7C, 0x6D, 0xAD, 0x16, 0xE8, 0x02, 0x1E, 0xE9, 0x00, 0x51, 0xE8, 0x62, 0xD0, 0x03, 0x53, 0xA9, 0x10, 0x50, 0x03, 0x08, 0x50, 0xA9, 0x5C, 0x18, 0x7C, 0x20, 0x98, 0x62, 0xDB, 0x2E + } + }, + { + 249, + 79, + 0x39, + 0x0124, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x24, 0xD0, 0x00, 0x20, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x50, 0x99, 0x7C, 0x12, 0x90, 0x20, 0x9F, 0x59, 0x10, 0x7C, 0x10, 0x74, 0x20, 0x7F, 0x10, 0x4F, 0x10, 0x52, 0xFB, 0x08, 0x52, 0xFC, 0x20, 0x7C, 0x13, 0xEA, 0x20, 0x9F, 0x44, 0x10, 0x7C, 0x10, 0x74, 0x20, 0x20, 0x7F, 0x10, 0x4F, 0x10, 0x52, 0xFC, 0x7C, 0x13, 0xFB, 0x20, 0x9F, 0x32, 0x10, 0x7C, 0x10, 0x74, 0x20, 0x20, 0x7F, 0x10, 0x4F, 0x86, 0x85 + } + }, + { + 250, + 79, + 0x39, + 0x0125, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x25, 0x10, 0x52, 0xFC, 0x7C, 0x14, 0x0D, 0x20, 0x9F, 0x20, 0x10, 0x7C, 0x10, 0x74, 0x20, 0x20, 0x7F, 0x10, 0x4F, 0x52, 0xFC, 0x62, 0xD0, 0x00, 0x53, 0xA9, 0x20, 0x7F, 0x62, 0xD0, 0x00, 0x55, 0x39, 0x08, 0x55, 0x3A, 0x08, 0x55, 0x3B, 0x08, 0x55, 0x3C, 0x08, 0x55, 0x01, 0x00, 0x55, 0x00, 0x00, 0x55, 0x03, 0x00, 0x55, 0x02, 0x00, 0x55, 0x05, 0x00, 0x55, 0x04, 0x00, 0x55, 0x07, 0x00, 0x55, 0x71, 0x5C + } + }, + { + 251, + 79, + 0x39, + 0x0126, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x26, 0x06, 0x00, 0x55, 0x55, 0x20, 0x55, 0x54, 0x01, 0x55, 0x53, 0x00, 0x43, 0xE6, 0x01, 0x43, 0xE0, 0x08, 0x7F, 0x49, 0xE0, 0x08, 0xB0, 0x05, 0x50, 0x00, 0x80, 0x07, 0x08, 0x7C, 0x54, 0x59, 0x38, 0xFF, 0x7F, 0x08, 0x7C, 0x54, 0xA4, 0x38, 0xFF, 0x7F, 0x08, 0x7C, 0x54, 0xB5, 0x38, 0xFF, 0x7F, 0x08, 0x7C, 0x54, 0xE2, 0x38, 0xFF, 0x7F, 0x08, 0x7C, 0x54, 0xF3, 0x38, 0xFF, 0x7F, 0x08, 0x7C, 0x84, 0x83 + } + }, + { + 252, + 79, + 0x39, + 0x0127, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x27, 0x55, 0x04, 0x38, 0xFF, 0x7F, 0x08, 0x7C, 0x55, 0x15, 0x38, 0xFF, 0x7F, 0x08, 0x7C, 0x5B, 0xE0, 0x38, 0xFF, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x50, 0x00, 0x3D, 0xF9, 0x80, 0xC0, 0x06, 0x7C, 0x4A, 0x8B, 0x50, 0xC0, 0x3D, 0xF5, 0x80, 0xC0, 0x0C, 0x10, 0x4B, 0x11, 0x04, 0x4B, 0x7C, 0x4A, 0x8B, 0x31, 0x80, 0x20, 0x08, 0x7C, 0x4A, 0x28, 0x18, 0x6A, 0xD0, 0x04, 0x7C, 0x4A, 0x8B, 0x6A, 0xD0, 0x1F, 0xBA + } + }, + { + 253, + 79, + 0x39, + 0x0128, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x28, 0x08, 0x4B, 0x11, 0x04, 0x4B, 0x7C, 0x4A, 0x8B, 0x38, 0xFF, 0x20, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x10, 0x4F, 0x5D, 0xD0, 0x08, 0x62, 0xD0, 0x00, 0x7C, 0x4A, 0xA3, 0x51, 0xE1, 0x54, 0xFB, 0x18, 0x60, 0xD0, 0x20, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x5D, 0xD0, 0x08, 0x62, 0xD0, 0x00, 0x51, 0xE9, 0x08, 0x50, 0x00, 0x53, 0xE9, 0x53, 0xE1, 0x53, 0xE0, 0x53, 0xDF, 0x56, 0x00, 0x20, 0x66, 0xFC, 0xD7, 0x2B + } + }, + { + 254, + 79, + 0x39, + 0x0129, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x29, 0x6C, 0xFB, 0x6C, 0xFA, 0x6C, 0xF9, 0x6B, 0xDF, 0x6B, 0xE0, 0x6B, 0xE1, 0x6B, 0xE9, 0x51, 0xDF, 0x1B, 0xF8, 0x51, 0xE0, 0x1B, 0xF7, 0x51, 0xE1, 0x1B, 0xF6, 0x51, 0xE9, 0x1B, 0xF5, 0xC0, 0x11, 0x53, 0xE9, 0x52, 0xF8, 0x14, 0xDF, 0x52, 0xF7, 0x1C, 0xE0, 0x52, 0xF6, 0x1C, 0xE1, 0x77, 0xFC, 0x7B, 0x00, 0xBF, 0xCB, 0x51, 0xDF, 0x54, 0xF8, 0x51, 0xE0, 0x54, 0xF7, 0x51, 0xE1, 0x54, 0xF6, 0x3A, 0xF2 + } + }, + { + 255, + 79, + 0x39, + 0x012A, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x2A, 0x51, 0xE9, 0x54, 0xF5, 0x18, 0x53, 0xE9, 0x18, 0x60, 0xD0, 0x7F, 0x37, 0xFC, 0xFF, 0x77, 0xFC, 0x37, 0xFB, 0xFF, 0x0F, 0xFB, 0x00, 0x37, 0xFA, 0xFF, 0x0F, 0xFA, 0x00, 0x37, 0xF9, 0xFF, 0x0F, 0xF9, 0x00, 0x7F, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x08, 0x66, 0xFC, 0x6B, 0xE1, 0x51, 0xE1, 0x1B, 0xFB, 0xC0, 0x05, 0x53, 0xE1, 0x77, 0xFC, 0x7A, 0xE0, 0xBF, 0xEF, 0x7F, 0x08, 0x10, 0x4F, 0x50, 0x80, 0x7F + } + }, + { + 256, + 79, + 0x39, + 0x012B, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x2B, 0x00, 0x6F, 0xFF, 0xD0, 0x03, 0x03, 0xFE, 0x66, 0xFE, 0xBF, 0xF7, 0x38, 0xFE, 0x70, 0x3F, 0x71, 0xC0, 0x7F, 0x7C, 0x6E, 0x82, 0x7C, 0x6E, 0xC0, 0x7F, 0x62, 0xD0, 0x01, 0x51, 0xE7, 0x08, 0x51, 0xE6, 0x62, 0xD0, 0x03, 0x53, 0xFE, 0x18, 0x53, 0xFF, 0x62, 0xD0, 0x01, 0x51, 0xE5, 0x08, 0x51, 0xE4, 0x62, 0xD0, 0x03, 0x53, 0xFC, 0x18, 0x53, 0xFD, 0x62, 0xD0, 0x01, 0x51, 0xEF, 0x08, 0x51, 0x7E, 0x7C + } + }, + { + 257, + 79, + 0x39, + 0x012C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x2C, 0xEE, 0x62, 0xD0, 0x03, 0x53, 0xFA, 0x18, 0x53, 0xFB, 0x62, 0xD0, 0x01, 0x51, 0xED, 0x08, 0x51, 0xEC, 0x62, 0xD0, 0x03, 0x53, 0xF8, 0x18, 0x53, 0xF9, 0x7F, 0x62, 0xD0, 0x01, 0x51, 0xE7, 0x02, 0xEF, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x62, 0xD0, 0x01, 0x51, 0xE6, 0x0A, 0xEE, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x7C, 0x6D, 0xF3, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0xA3, 0x18, 0x23, 0xC7 + } + }, + { + 258, + 79, + 0x39, + 0x012D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x2D, 0x53, 0xA4, 0x62, 0xD0, 0x01, 0x51, 0xE5, 0x02, 0xED, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x0A, 0xEC, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x7C, 0x6D, 0xF3, 0x7C, 0x71, 0xC3, 0x7F, 0x10, 0x4F, 0x52, 0xFA, 0x13, 0xFC, 0x52, 0xF9, 0x1B, 0xFB, 0xD0, 0x12, 0x62, 0xD0, 0x00, 0x52, 0xFC, 0x13, 0xFA, 0x53, 0xE8, 0x52, 0xFB, 0x1B, 0xF9, 0x53, 0xE9, 0x80, 0x10, 0x62, 0xD0, 0xB7, 0xF0 + } + }, + { + 259, + 79, + 0x39, + 0x012E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x2E, 0x00, 0x52, 0xFA, 0x13, 0xFC, 0x53, 0xE8, 0x52, 0xF9, 0x1B, 0xFB, 0x53, 0xE9, 0x20, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xCD, 0x00, 0x7C, 0x70, 0x41, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x9F, 0xB4, 0x38, 0xFC, 0x7C, 0x6F, 0xA8, 0x62, 0xD0, 0x03, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xEE, 0x8C, 0x9B + } + }, + { + 260, + 79, + 0x39, + 0x012F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x2F, 0x08, 0x51, 0xEF, 0x08, 0x9F, 0x9B, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x62, 0xD0, 0x04, 0x04, 0xA0, 0x7C, 0x71, 0x7F, 0x0C, 0x9F, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x08, 0x51, 0xEB, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x9F, 0x78, 0x38, 0xF8, 0x7C, 0x6F, 0x76, 0x62, 0xD0, 0x03, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x6A, 0x58 + } + }, + { + 261, + 79, + 0x39, + 0x0130, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x30, 0x9F, 0x5F, 0x38, 0xFC, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x62, 0xD0, 0x04, 0x04, 0x9E, 0x7C, 0x71, 0x7F, 0x0C, 0x9D, 0x7F, 0x10, 0x7C, 0x1E, 0x80, 0x62, 0xD0, 0x00, 0x5A, 0xE9, 0x20, 0x7C, 0x71, 0x22, 0x51, 0xAE, 0x62, 0xD0, 0x03, 0x12, 0xDD, 0x62, 0xD0, 0x04, 0x53, 0xA6, 0x62, 0xD0, 0x04, 0x51, 0xAD, 0x62, 0xD0, 0x03, 0x1A, 0xDC, 0x62, 0xD0, 0x04, 0x53, 0xA5, 0x62, 0xD0, 0x04, 0x51, 0x3D, 0xFF + } + }, + { + 262, + 79, + 0x39, + 0x0131, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x31, 0xAE, 0x08, 0x51, 0xAD, 0x62, 0xD0, 0x03, 0x53, 0xDC, 0x18, 0x53, 0xDD, 0x62, 0xD0, 0x04, 0x51, 0xA6, 0x62, 0xD0, 0x00, 0x53, 0xE6, 0x62, 0xD0, 0x04, 0x51, 0xA5, 0x62, 0xD0, 0x00, 0x53, 0xE7, 0x62, 0xD0, 0x04, 0x51, 0xA0, 0x08, 0x62, 0xD0, 0x00, 0x18, 0x53, 0xE5, 0x55, 0xE4, 0x00, 0x65, 0xE5, 0x65, 0xE4, 0x6B, 0xE5, 0x51, 0xE4, 0x53, 0xE2, 0x51, 0xE5, 0x53, 0xE3, 0x50, 0x00, 0x08, 0x8B, 0x9C + } + }, + { + 263, + 79, + 0x39, + 0x0132, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x32, 0x08, 0x51, 0xE7, 0x08, 0x51, 0xE6, 0x08, 0x50, 0x00, 0x08, 0x08, 0x51, 0xE3, 0x08, 0x51, 0xE2, 0x08, 0x7C, 0x49, 0xD3, 0x18, 0x53, 0xE6, 0x18, 0x53, 0xE7, 0x18, 0x18, 0x38, 0xFC, 0x51, 0xE6, 0x53, 0xE8, 0x51, 0xE7, 0x53, 0xE9, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0x9B, 0x18, 0x53, 0x9C, 0x62, 0xD0, 0x04, 0x51, 0xA6, 0x62, 0xD0, 0x00, 0x53, 0xE6, 0x62, 0xD0, 0x04, 0x57, 0x35 + } + }, + { + 264, + 79, + 0x39, + 0x0133, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x33, 0x51, 0xA5, 0x62, 0xD0, 0x00, 0x53, 0xE7, 0x62, 0xD0, 0x04, 0x51, 0x9E, 0x08, 0x62, 0xD0, 0x00, 0x18, 0x53, 0xE5, 0x55, 0xE4, 0x00, 0x65, 0xE5, 0x65, 0xE4, 0x6B, 0xE5, 0x51, 0xE4, 0x53, 0xE2, 0x51, 0xE5, 0x53, 0xE3, 0x50, 0x00, 0x08, 0x08, 0x51, 0xE7, 0x08, 0x51, 0xE6, 0x08, 0x50, 0x00, 0x08, 0x08, 0x51, 0xE3, 0x08, 0x51, 0xE2, 0x08, 0x7C, 0x49, 0xD3, 0x18, 0x53, 0xE6, 0x18, 0x53, 0x0D, 0xA2 + } + }, + { + 265, + 79, + 0x39, + 0x0134, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x34, 0xE7, 0x18, 0x18, 0x38, 0xFC, 0x51, 0xE6, 0x53, 0xE8, 0x51, 0xE7, 0x53, 0xE9, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0x99, 0x18, 0x53, 0x9A, 0x7C, 0x6E, 0x82, 0x7F, 0x10, 0x4F, 0x38, 0x08, 0x62, 0xD0, 0x04, 0x55, 0xDE, 0x00, 0x62, 0xD0, 0x04, 0x55, 0xDD, 0x00, 0x52, 0xFC, 0x03, 0xFA, 0x54, 0x01, 0x52, 0xFB, 0x0B, 0xF9, 0x54, 0x00, 0x52, 0xF8, 0x03, 0xF6, 0x54, 0x03, 0x0D, 0xA3 + } + }, + { + 266, + 79, + 0x39, + 0x0135, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x35, 0x52, 0xF7, 0x0B, 0xF5, 0x54, 0x02, 0x52, 0xFC, 0x03, 0xF6, 0x54, 0x05, 0x52, 0xFB, 0x0B, 0xF5, 0x54, 0x04, 0x52, 0xFA, 0x03, 0xF8, 0x54, 0x07, 0x52, 0xF9, 0x0B, 0xF7, 0x54, 0x06, 0x52, 0xFC, 0x13, 0xF8, 0x52, 0xFB, 0x1B, 0xF7, 0xC0, 0x43, 0x7C, 0x72, 0xD4, 0xD0, 0x1E, 0x62, 0xD0, 0x04, 0x55, 0xDE, 0x01, 0x52, 0x01, 0x13, 0x03, 0x52, 0x00, 0x1B, 0x02, 0xD0, 0x06, 0x7C, 0x73, 0x66, 0x05, 0x94 + } + }, + { + 267, + 79, + 0x39, + 0x0136, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x36, 0x80, 0x69, 0x62, 0xD0, 0x04, 0x55, 0xDD, 0x02, 0x80, 0x61, 0x62, 0xD0, 0x04, 0x55, 0xDE, 0x02, 0x52, 0x07, 0x13, 0x05, 0x52, 0x06, 0x1B, 0x04, 0xD0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xDD, 0x02, 0x80, 0x49, 0x62, 0xD0, 0x04, 0x55, 0xDD, 0x03, 0x80, 0x41, 0x7C, 0x72, 0xD4, 0xC0, 0x21, 0x62, 0xD0, 0x04, 0x55, 0xDE, 0x03, 0x52, 0x03, 0x13, 0x01, 0x52, 0x02, 0x1B, 0x00, 0xD0, 0x09, 0x62, 0xE1, 0x4D + } + }, + { + 268, + 79, + 0x39, + 0x0137, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x37, 0xD0, 0x04, 0x55, 0xDD, 0x03, 0x80, 0x24, 0x62, 0xD0, 0x04, 0x55, 0xDD, 0x04, 0x80, 0x1C, 0x62, 0xD0, 0x04, 0x55, 0xDE, 0x04, 0x52, 0x05, 0x13, 0x07, 0x52, 0x04, 0x1B, 0x06, 0xD0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xDD, 0x04, 0x80, 0x04, 0x7C, 0x73, 0x66, 0x62, 0xD0, 0x04, 0x3C, 0xDD, 0x01, 0xB0, 0x29, 0x62, 0xD0, 0x00, 0x52, 0xF3, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x52, 0xFA, 0x02, 0xE8, 0x77, 0x7A + } + }, + { + 269, + 79, + 0x39, + 0x0138, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x38, 0x53, 0xE8, 0x52, 0xF9, 0x0A, 0xE9, 0x53, 0xE9, 0x51, 0xE8, 0x13, 0xF6, 0x51, 0xE9, 0x1B, 0xF5, 0xD0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xE0, 0x01, 0x80, 0x97, 0x62, 0xD0, 0x04, 0x3C, 0xDD, 0x02, 0xB0, 0x29, 0x62, 0xD0, 0x00, 0x52, 0xF4, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x52, 0xF8, 0x02, 0xE8, 0x53, 0xE8, 0x52, 0xF7, 0x0A, 0xE9, 0x53, 0xE9, 0x51, 0xE8, 0x13, 0xFC, 0x51, 0xE9, 0x1B, 0xFB, 0x37, 0xFB + } + }, + { + 270, + 79, + 0x39, + 0x0139, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x39, 0xD0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xE0, 0x02, 0x80, 0x67, 0x62, 0xD0, 0x04, 0x3C, 0xDD, 0x03, 0xB0, 0x29, 0x62, 0xD0, 0x00, 0x52, 0xF3, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x52, 0xF6, 0x02, 0xE8, 0x53, 0xE8, 0x52, 0xF5, 0x0A, 0xE9, 0x53, 0xE9, 0x51, 0xE8, 0x13, 0xFA, 0x51, 0xE9, 0x1B, 0xF9, 0xD0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xE0, 0x03, 0x80, 0x37, 0x62, 0xD0, 0x04, 0x3C, 0xDD, 0x04, 0x10, 0xAE + } + }, + { + 271, + 79, + 0x39, + 0x013A, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x3A, 0xB0, 0x29, 0x62, 0xD0, 0x00, 0x52, 0xF4, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x52, 0xFC, 0x02, 0xE8, 0x53, 0xE8, 0x52, 0xFB, 0x0A, 0xE9, 0x53, 0xE9, 0x51, 0xE8, 0x13, 0xF8, 0x51, 0xE9, 0x1B, 0xF7, 0xD0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xE0, 0x04, 0x80, 0x07, 0x62, 0xD0, 0x04, 0x55, 0xE0, 0x00, 0x62, 0xD0, 0x04, 0x3C, 0xDE, 0x04, 0xB0, 0x15, 0x62, 0xD0, 0x04, 0x3C, 0xDD, 0x01, 0xB0, 0x0D, 0xA9, 0xE1 + } + }, + { + 272, + 79, + 0x39, + 0x013B, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x3B, 0x62, 0xD0, 0x04, 0x51, 0xDE, 0x01, 0x03, 0x62, 0xD0, 0x00, 0x80, 0x15, 0x62, 0xD0, 0x04, 0x51, 0xDE, 0x62, 0xD0, 0x04, 0x02, 0xDD, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x16, 0xE9, 0x02, 0x51, 0xE9, 0x38, 0xF8, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x09, 0x52, 0xF7, 0x3B, 0xFB, 0xB0, 0x1B, 0x52, 0xF8, 0x3B, 0xFC, 0xB0, 0x15, 0x52, 0xF5, 0x3B, 0xF9, 0xB0, 0x0F, 0x52, 0xF6, 0x3B, 0xFA, 0xB0, 0x09, 0xC2, 0x14 + } + }, + { + 273, + 79, + 0x39, + 0x013C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x3C, 0x62, 0xD0, 0x04, 0x55, 0xDF, 0xFE, 0x81, 0x17, 0x52, 0xF7, 0x3B, 0xFB, 0xB0, 0x07, 0x52, 0xF8, 0x3B, 0xFC, 0xA0, 0x0D, 0x52, 0xF5, 0x3B, 0xF9, 0xB0, 0x4E, 0x52, 0xF6, 0x3B, 0xFA, 0xB0, 0x48, 0x62, 0xD0, 0x04, 0x3C, 0xCE, 0x00, 0xA0, 0x06, 0x3C, 0xCE, 0x07, 0xB0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xDF, 0x00, 0x80, 0xEA, 0x62, 0xD0, 0x04, 0x3C, 0xCE, 0x01, 0xA0, 0x06, 0x3C, 0xCE, 0x02, 0xC9, 0x23 + } + }, + { + 274, + 79, + 0x39, + 0x013D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x3D, 0xB0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xDF, 0x02, 0x80, 0xD5, 0x62, 0xD0, 0x04, 0x3C, 0xCE, 0x03, 0xA0, 0x06, 0x3C, 0xCE, 0x04, 0xB0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xDF, 0x04, 0x80, 0xC0, 0x62, 0xD0, 0x04, 0x55, 0xDF, 0x06, 0x80, 0xB8, 0x52, 0xFB, 0x08, 0x52, 0xFC, 0x08, 0x52, 0xF7, 0x08, 0x52, 0xF8, 0x08, 0x9B, 0xEC, 0x7C, 0x72, 0x25, 0x52, 0xF9, 0x08, 0x52, 0xFA, 0x08, 0x52, 0xF5, 0x56, 0x3E + } + }, + { + 275, + 79, + 0x39, + 0x013E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x3E, 0x08, 0x52, 0xF6, 0x08, 0x9B, 0xDB, 0x38, 0xF8, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x54, 0x03, 0x51, 0xE9, 0x54, 0x02, 0x52, 0x01, 0x13, 0x03, 0x52, 0x00, 0x1B, 0x02, 0xD0, 0x23, 0x7C, 0x71, 0x2E, 0x7C, 0x70, 0x17, 0x7C, 0x73, 0x4A, 0xD0, 0x09, 0x56, 0x06, 0x01, 0x56, 0x05, 0x00, 0x80, 0x07, 0x56, 0x06, 0x00, 0x56, 0x05, 0x00, 0x62, 0xD0, 0x00, 0x52, 0x06, 0x54, 0x04, 0x80, 0x2C, 0x62, 0x31, 0xF5 + } + }, + { + 276, + 79, + 0x39, + 0x013F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x3F, 0xD0, 0x00, 0x52, 0x03, 0x53, 0xE8, 0x52, 0x02, 0x53, 0xE9, 0x7C, 0x70, 0x17, 0x13, 0x01, 0x51, 0xE9, 0x1B, 0x00, 0xD0, 0x09, 0x56, 0x08, 0x01, 0x56, 0x07, 0x00, 0x80, 0x07, 0x56, 0x08, 0x00, 0x56, 0x07, 0x00, 0x62, 0xD0, 0x00, 0x52, 0x08, 0x54, 0x04, 0x62, 0xD0, 0x04, 0x47, 0xCE, 0x01, 0xB0, 0x1B, 0x62, 0xD0, 0x00, 0x52, 0x04, 0x31, 0x01, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xCE, 0x79, 0x86 + } + }, + { + 277, + 79, + 0x39, + 0x0140, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x40, 0x62, 0xD0, 0x00, 0x02, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0xDF, 0x80, 0x0D, 0x62, 0xD0, 0x04, 0x51, 0xCE, 0x03, 0x04, 0x62, 0xD0, 0x04, 0x53, 0xDF, 0x62, 0xD0, 0x04, 0x26, 0xDF, 0x07, 0x62, 0xD0, 0x04, 0x51, 0xDF, 0x01, 0x01, 0x62, 0xD0, 0x00, 0x38, 0xF7, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x04, 0x56, 0x00, 0x00, 0x7C, 0x72, 0xAB, 0x10, 0x7C, 0x1E, 0x80, 0x62, 0xD0, 0x00, 0x5A, 0xE9, 0x20, 0xC9, 0x27 + } + }, + { + 278, + 79, + 0x39, + 0x0141, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x41, 0x7C, 0x71, 0x22, 0x3D, 0xFC, 0x01, 0xA0, 0x62, 0x3D, 0xFC, 0x00, 0xB0, 0x41, 0x62, 0xD0, 0x04, 0x55, 0xD0, 0x00, 0x62, 0xD0, 0x04, 0x7C, 0x71, 0xEE, 0x3C, 0xAD, 0x00, 0xB0, 0x06, 0x3C, 0xAE, 0x00, 0xA0, 0x22, 0x62, 0xD0, 0x04, 0x51, 0xAE, 0x08, 0x51, 0xAD, 0x62, 0xD0, 0x04, 0x53, 0xAF, 0x18, 0x53, 0xB0, 0x10, 0x50, 0x00, 0x5C, 0x7C, 0x1E, 0xB4, 0x20, 0x62, 0xD0, 0x03, 0x55, 0xDD, 0x4B, 0x2C + } + }, + { + 279, + 79, + 0x39, + 0x0142, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x42, 0x00, 0x55, 0xDC, 0x00, 0x62, 0xD0, 0x04, 0x55, 0xA6, 0x00, 0x55, 0xA5, 0x00, 0x7C, 0x72, 0x85, 0x62, 0xD0, 0x03, 0x55, 0xE3, 0x00, 0x55, 0xE2, 0x00, 0x7C, 0x73, 0x5F, 0x62, 0xD0, 0x04, 0x55, 0xD4, 0x00, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x83, 0xAD, 0x62, 0xD0, 0x04, 0x3C, 0xDC, 0x01, 0xA0, 0x2A, 0x62, 0xD0, 0x01, 0x51, 0xE7, 0x08, 0x51, 0xE6, 0x62, 0xD0, 0x03, 0x53, 0xFE, 0x18, 0x53, 0x86, 0xA3 + } + }, + { + 280, + 79, + 0x39, + 0x0143, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x43, 0xFF, 0x62, 0xD0, 0x01, 0x51, 0xE5, 0x08, 0x51, 0xE4, 0x62, 0xD0, 0x03, 0x53, 0xFC, 0x18, 0x53, 0xFD, 0x7C, 0x6E, 0x82, 0x62, 0xD0, 0x04, 0x55, 0xDC, 0x01, 0x62, 0xD0, 0x01, 0x51, 0xE7, 0x08, 0x51, 0xE6, 0x62, 0xD0, 0x03, 0x53, 0xF6, 0x18, 0x53, 0xF7, 0x62, 0xD0, 0x01, 0x51, 0xE5, 0x08, 0x51, 0xE4, 0x62, 0xD0, 0x03, 0x53, 0xF4, 0x18, 0x53, 0xF5, 0x62, 0xD0, 0x00, 0x51, 0x3A, 0x08, 0x36, 0x04 + } + }, + { + 281, + 79, + 0x39, + 0x0144, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x44, 0x62, 0xD0, 0x00, 0x51, 0x39, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x08, 0x51, 0xEB, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x9B, 0xF5, 0x38, 0xF6, 0x7C, 0x71, 0x88, 0x54, 0x00, 0x3D, 0x00, 0x00, 0xA1, 0x23, 0x3D, 0xFB, 0x00, 0xA1, 0x1E, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0xAC, 0xF1 + } + }, + { + 282, + 79, + 0x39, + 0x0145, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x45, 0x62, 0xD0, 0x03, 0x51, 0xE3, 0x21, 0x0F, 0x62, 0xD0, 0x00, 0x53, 0xE6, 0x62, 0xD0, 0x03, 0x51, 0xE2, 0x21, 0x00, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0xB0, 0x07, 0x51, 0xE6, 0x3A, 0xE8, 0xA0, 0xA4, 0x62, 0xD0, 0x03, 0x65, 0xE3, 0x6B, 0xE2, 0x65, 0xE3, 0x6B, 0xE2, 0x65, 0xE3, 0x6B, 0xE2, 0x65, 0xE3, 0x6B, 0xE2, 0x52, 0x00, 0x2C, 0xE3, 0x3C, 0xE2, 0x12, 0xB0, 0x06, 0x3C, 0xE3, 0x34, 0xA0, 0xF9, 0x8C + } + }, + { + 283, + 79, + 0x39, + 0x0146, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x46, 0x28, 0x62, 0xD0, 0x03, 0x3C, 0xE2, 0x23, 0xB0, 0x06, 0x3C, 0xE3, 0x41, 0xA0, 0x1B, 0x62, 0xD0, 0x03, 0x3C, 0xE2, 0x34, 0xB0, 0x06, 0x3C, 0xE3, 0x12, 0xA0, 0x0E, 0x62, 0xD0, 0x03, 0x3C, 0xE2, 0x41, 0xB0, 0x14, 0x3C, 0xE3, 0x23, 0xB0, 0x0F, 0x62, 0xD0, 0x04, 0x55, 0xD2, 0x01, 0x62, 0xD0, 0x04, 0x55, 0xD3, 0x01, 0x80, 0x49, 0x62, 0xD0, 0x03, 0x3C, 0xE2, 0x43, 0xB0, 0x06, 0x3C, 0xE3, 0xD5, 0x45 + } + }, + { + 284, + 79, + 0x39, + 0x0147, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x47, 0x21, 0xA0, 0x28, 0x62, 0xD0, 0x03, 0x3C, 0xE2, 0x32, 0xB0, 0x06, 0x3C, 0xE3, 0x14, 0xA0, 0x1B, 0x62, 0xD0, 0x03, 0x3C, 0xE2, 0x21, 0xB0, 0x06, 0x3C, 0xE3, 0x43, 0xA0, 0x0E, 0x62, 0xD0, 0x03, 0x3C, 0xE2, 0x14, 0xB0, 0x14, 0x3C, 0xE3, 0x32, 0xB0, 0x0F, 0x62, 0xD0, 0x04, 0x55, 0xD2, 0x00, 0x62, 0xD0, 0x04, 0x55, 0xD3, 0x01, 0x80, 0x07, 0x62, 0xD0, 0x04, 0x55, 0xD3, 0x00, 0x7C, 0x72, 0x16, 0xC8 + } + }, + { + 285, + 79, + 0x39, + 0x0148, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x48, 0x85, 0x80, 0x3D, 0x7C, 0x73, 0x0E, 0x39, 0x00, 0xA0, 0x36, 0x62, 0xD0, 0x04, 0x3C, 0xD3, 0x01, 0xB0, 0x2E, 0x62, 0xD0, 0x00, 0x7C, 0x73, 0x0E, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xD1, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0xA0, 0x08, 0x62, 0xD0, 0x04, 0x76, 0xD1, 0x80, 0x13, 0x62, 0xD0, 0x03, 0x55, 0xE3, 0x00, 0x55, 0xE2, 0x00, 0x7C, 0x72, 0x85, 0x62, 0xD0, 0x04, 0x55, 0xD3, 0x00, 0x62, 0x19, 0xCF + } + }, + { + 286, + 79, + 0x39, + 0x0149, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x49, 0xD0, 0x04, 0x3C, 0xD3, 0x01, 0xB0, 0x11, 0x62, 0xD0, 0x04, 0x3C, 0xD2, 0x01, 0xB0, 0x06, 0x56, 0x00, 0x28, 0x80, 0x04, 0x56, 0x00, 0x29, 0x3D, 0x00, 0x00, 0xA0, 0x3E, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x08, 0x51, 0xEB, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x9C, 0x65, 0x38, 0x1E, 0xDA + } + }, + { + 287, + 79, + 0x39, + 0x014A, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x4A, 0xF8, 0x54, 0x03, 0x3D, 0xFB, 0x00, 0xA0, 0x09, 0x62, 0xD0, 0x04, 0x3C, 0xD3, 0x00, 0xB0, 0x0A, 0x52, 0x03, 0x54, 0x00, 0x66, 0x00, 0x07, 0x00, 0x0E, 0x7C, 0x72, 0xF6, 0x39, 0x00, 0xA1, 0x5B, 0x3D, 0x00, 0x00, 0xA1, 0x56, 0x3D, 0x00, 0x28, 0xA1, 0x51, 0x3D, 0x00, 0x29, 0xA1, 0x4C, 0x62, 0xD0, 0x03, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0xB4, 0x07 + } + }, + { + 288, + 79, + 0x39, + 0x014B, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x4B, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x7C, 0x6F, 0xA8, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x08, 0x51, 0xEB, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x7C, 0x6F, 0x76, 0x62, 0xD0, 0x04, 0x52, 0x00, 0x3A, 0xD4, 0xB0, 0x04, 0x56, 0x01, 0x01, 0x3D, 0x01, 0x00, 0xB0, 0xD4, 0x62, 0xD0, 0x00, 0x3C, 0x39, 0x00, 0xB0, 0x73, 0x62, 0xD0, 0x00, 0x3C, 0xBA, 0x14 + } + }, + { + 289, + 79, + 0x39, + 0x014C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x4C, 0x3A, 0x00, 0xB0, 0x6B, 0x62, 0xD0, 0x04, 0x3C, 0x9F, 0x00, 0xB0, 0x06, 0x3C, 0xA0, 0x00, 0xA0, 0x0E, 0x62, 0xD0, 0x04, 0x3C, 0x9D, 0x00, 0xB0, 0x56, 0x3C, 0x9E, 0x00, 0xB0, 0x51, 0x3D, 0x00, 0x10, 0xB0, 0x13, 0x62, 0xD0, 0x04, 0x3C, 0xD4, 0x14, 0xA0, 0x06, 0x3C, 0xD4, 0x1C, 0xB0, 0x3F, 0x56, 0x01, 0x01, 0x80, 0x3A, 0x3D, 0x00, 0x1C, 0xB0, 0x13, 0x62, 0xD0, 0x04, 0x3C, 0xD4, 0x18, 0x4D, 0x3B + } + }, + { + 290, + 79, + 0x39, + 0x014D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x4D, 0xA0, 0x06, 0x3C, 0xD4, 0x10, 0xB0, 0x28, 0x56, 0x01, 0x01, 0x80, 0x23, 0x3D, 0x00, 0x18, 0xA0, 0x06, 0x3D, 0x00, 0x14, 0xB0, 0x19, 0x62, 0xD0, 0x00, 0x52, 0x00, 0x11, 0x04, 0x7C, 0x70, 0x27, 0xA0, 0x0A, 0x52, 0x00, 0x01, 0x04, 0x7C, 0x70, 0x27, 0xB0, 0x04, 0x56, 0x01, 0x01, 0x3D, 0x00, 0x10, 0xB0, 0x18, 0x62, 0xD0, 0x04, 0x3C, 0xD4, 0x10, 0xA0, 0x0B, 0x3C, 0xD4, 0x1E, 0xA0, 0x06, 0x64, 0x6A + } + }, + { + 291, + 79, + 0x39, + 0x014E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x4E, 0x3C, 0xD4, 0x12, 0xB0, 0x43, 0x56, 0x01, 0x01, 0x80, 0x3E, 0x3D, 0x00, 0x1E, 0xB0, 0x18, 0x62, 0xD0, 0x04, 0x3C, 0xD4, 0x1E, 0xA0, 0x0B, 0x3C, 0xD4, 0x10, 0xA0, 0x06, 0x3C, 0xD4, 0x1C, 0xB0, 0x27, 0x56, 0x01, 0x01, 0x80, 0x22, 0x62, 0xD0, 0x04, 0x51, 0xD4, 0x3B, 0x00, 0xA0, 0x16, 0x62, 0xD0, 0x00, 0x52, 0x00, 0x11, 0x02, 0x7C, 0x70, 0x27, 0xA0, 0x0A, 0x52, 0x00, 0x01, 0x02, 0x7C, 0x91, 0xC5 + } + }, + { + 292, + 79, + 0x39, + 0x014F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x4F, 0x70, 0x27, 0xB0, 0x04, 0x56, 0x01, 0x01, 0x3D, 0x01, 0x01, 0xB0, 0x22, 0x62, 0xD0, 0x00, 0x7C, 0x72, 0xF6, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xD5, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0xA0, 0x1B, 0x7C, 0x6E, 0x82, 0x62, 0xD0, 0x04, 0x76, 0xD5, 0x56, 0x00, 0x00, 0x80, 0x0E, 0x7C, 0x73, 0x5F, 0x52, 0x00, 0x62, 0xD0, 0x04, 0x53, 0xD4, 0x56, 0x00, 0x00, 0x3D, 0x00, 0x00, 0xA0, 0x52, 0x62, 0x27, 0xF2 + } + }, + { + 293, + 79, + 0x39, + 0x0150, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x50, 0xD0, 0x03, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x7C, 0x6F, 0xA8, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x08, 0x51, 0xEB, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x7C, 0x6F, 0x76, 0x7C, 0x4C, 0x14, 0x62, 0xD0, 0x04, 0x52, 0x00, 0x3A, 0xD0, 0xB0, 0x08, 0x62, 0xAE, 0x01 + } + }, + { + 294, + 79, + 0x39, + 0x0151, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x51, 0xD0, 0x04, 0x76, 0xCF, 0x80, 0x04, 0x7C, 0x70, 0x5B, 0x52, 0x00, 0x62, 0xD0, 0x04, 0x53, 0xD0, 0x52, 0x00, 0x62, 0xD0, 0x00, 0x38, 0xFC, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x03, 0x7C, 0x72, 0xAB, 0x56, 0x00, 0x00, 0x52, 0xFC, 0x08, 0x7C, 0x5B, 0xE0, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x54, 0x00, 0x52, 0xFC, 0x08, 0x90, 0x6D, 0x62, 0xD0, 0x00, 0x54, 0x01, 0x52, 0xFC, 0x08, 0x90, 0x96, 0x38, 0xC7, 0x34 + } + }, + { + 295, + 79, + 0x39, + 0x0152, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x52, 0xFE, 0x62, 0xD0, 0x00, 0x54, 0x02, 0x3D, 0x00, 0x00, 0xA0, 0x05, 0x52, 0x00, 0x80, 0x12, 0x3D, 0x01, 0x00, 0xA0, 0x08, 0x52, 0x01, 0x62, 0xD0, 0x00, 0x80, 0x06, 0x52, 0x02, 0x62, 0xD0, 0x00, 0x38, 0xFD, 0x20, 0x7F, 0x10, 0x4F, 0x50, 0x00, 0x08, 0x52, 0xFC, 0x08, 0x9B, 0x7E, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x01, 0x56, 0x00, 0x00, 0x50, 0x01, 0x08, 0x52, 0x5D, 0x61 + } + }, + { + 296, + 79, + 0x39, + 0x0153, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x53, 0xFC, 0x08, 0x9B, 0x68, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x54, 0x00, 0x3D, 0x00, 0x28, 0xA0, 0x0A, 0x3D, 0x00, 0x29, 0xA0, 0x05, 0x50, 0x00, 0x80, 0x06, 0x52, 0x00, 0x62, 0xD0, 0x00, 0x38, 0xFF, 0x20, 0x7F, 0x10, 0x4F, 0x50, 0x01, 0x08, 0x52, 0xFC, 0x08, 0x9B, 0x40, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x20, 0x7F, 0x10, 0x4F, 0x50, 0x00, 0x08, 0x52, 0xFC, 0x08, 0x90, 0x29, 0x38, 0xFE, 0x62, 0x5B, 0x5E + } + }, + { + 297, + 79, + 0x39, + 0x0154, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x54, 0xD0, 0x00, 0x20, 0x7F, 0x10, 0x4F, 0x50, 0x01, 0x08, 0x52, 0xFC, 0x08, 0x90, 0x18, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x20, 0x7F, 0x10, 0x4F, 0x50, 0x02, 0x08, 0x52, 0xFC, 0x08, 0x90, 0x07, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x0F, 0x56, 0x00, 0x00, 0x56, 0x04, 0x00, 0x7C, 0x72, 0xAB, 0x56, 0x03, 0x00, 0x10, 0x7C, 0x1E, 0x80, 0x62, 0xD0, 0x00, 0x5A, 0xE9, 0x20, 0xE0, 0x69 + } + }, + { + 298, + 79, + 0x39, + 0x0155, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x55, 0x7C, 0x71, 0x22, 0x10, 0x7C, 0x1E, 0x8D, 0x62, 0xD0, 0x00, 0x5A, 0xE9, 0x20, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0xAB, 0x18, 0x53, 0xAC, 0x3D, 0xFC, 0x02, 0xD0, 0x61, 0x3D, 0xFC, 0x00, 0xB0, 0x41, 0x62, 0xD0, 0x04, 0x55, 0xD0, 0x00, 0x62, 0xD0, 0x04, 0x7C, 0x71, 0xEE, 0x3C, 0xAD, 0x00, 0xB0, 0x06, 0x3C, 0xAE, 0x00, 0xA0, 0x22, 0x62, 0xD0, 0x04, 0x51, 0xAE, 0x08, 0x51, 0xAD, 0xF0, 0x8A + } + }, + { + 299, + 79, + 0x39, + 0x0156, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x56, 0x62, 0xD0, 0x04, 0x53, 0xAF, 0x18, 0x53, 0xB0, 0x10, 0x50, 0x00, 0x5C, 0x7C, 0x1E, 0xB4, 0x20, 0x62, 0xD0, 0x03, 0x55, 0xDD, 0x00, 0x55, 0xDC, 0x00, 0x62, 0xD0, 0x04, 0x55, 0xA6, 0x00, 0x55, 0xA5, 0x00, 0x62, 0xD0, 0x04, 0x55, 0xDB, 0x00, 0x7C, 0x73, 0x58, 0x7C, 0x73, 0x51, 0x10, 0x50, 0x00, 0x5C, 0x7C, 0x1E, 0xCC, 0x20, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x83, 0x71, 0x3D, 0xFC, 0x02, 0x20, 0xEB + } + }, + { + 300, + 79, + 0x39, + 0x0157, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x57, 0xB3, 0x0F, 0x62, 0xD0, 0x04, 0x3C, 0xDC, 0x02, 0xD0, 0x55, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x7C, 0x72, 0x01, 0x20, 0x3C, 0xE9, 0x00, 0xB0, 0x05, 0x39, 0x00, 0xA0, 0x33, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x7C, 0x72, 0x01, 0x53, 0xE8, 0x20, 0x62, 0xD0, 0x04, 0x51, 0xAC, 0x62, 0xD0, 0x00, 0x12, 0xE8, 0x62, 0xD0, 0x04, 0x51, 0xAB, 0x62, 0xD0, 0x00, 0x1A, 0xE9, 0xD0, 0x1A, 0x7C, 0xE0, 0x6C + } + }, + { + 301, + 79, + 0x39, + 0x0158, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x58, 0x4A, 0xD9, 0x7C, 0x4A, 0xD2, 0x7C, 0x4B, 0x1A, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x83, 0x1F, 0x7C, 0x4A, 0xD9, 0x7C, 0x4A, 0xD2, 0x7C, 0x4B, 0x1A, 0x62, 0xD0, 0x04, 0x55, 0xDC, 0x02, 0x3D, 0xFB, 0x01, 0xA0, 0x06, 0x3D, 0xFB, 0x02, 0xB1, 0x49, 0x62, 0xD0, 0x01, 0x51, 0xEE, 0x08, 0x51, 0xEF, 0x08, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x7C, 0x4B, 0x61, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x54, 0x7C, 0xA5 + } + }, + { + 302, + 79, + 0x39, + 0x0159, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x59, 0x08, 0x51, 0xE9, 0x54, 0x07, 0x62, 0xD0, 0x01, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xF8, 0x7C, 0x71, 0xF5, 0x62, 0xD0, 0x03, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x7C, 0x4B, 0x61, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x54, 0x0C, 0x51, 0xE9, 0x54, 0x0B, 0x62, 0xD0, 0x03, 0x51, 0x9A, 0xE2 + } + }, + { + 303, + 79, + 0x39, + 0x015A, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x5A, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x08, 0x51, 0xEB, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xF8, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x54, 0x0E, 0x51, 0xE9, 0x54, 0x0D, 0x52, 0x08, 0x13, 0x0C, 0x54, 0x06, 0x52, 0x07, 0x1B, 0x0B, 0x54, 0x05, 0x62, 0xD0, 0x00, 0x51, 0x3B, 0x62, 0xD0, 0x00, 0x13, 0x06, 0x52, 0x05, 0x31, 0x80, 0x53, 0xE1, 0x50, 0x00, 0x31, 0x80, 0x1A, 0xE1, 0x96, 0xDB + } + }, + { + 304, + 79, + 0x39, + 0x015B, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x5B, 0xD0, 0x06, 0x56, 0x00, 0x48, 0x80, 0x41, 0x52, 0x06, 0x11, 0x00, 0x52, 0x05, 0x31, 0x80, 0x19, 0x80, 0xD0, 0x35, 0x62, 0xD0, 0x00, 0x52, 0x06, 0x73, 0x53, 0xE8, 0x52, 0x05, 0x73, 0x53, 0xE9, 0x51, 0xE8, 0x01, 0x01, 0x54, 0x06, 0x51, 0xE9, 0x09, 0x00, 0x54, 0x05, 0x62, 0xD0, 0x00, 0x51, 0x3B, 0x62, 0xD0, 0x00, 0x13, 0x06, 0x52, 0x05, 0x31, 0x80, 0x53, 0xE1, 0x50, 0x00, 0x31, 0x80, 0xF9, 0xA2 + } + }, + { + 305, + 79, + 0x39, + 0x015C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x5C, 0x1A, 0xE1, 0xD0, 0x04, 0x56, 0x00, 0x49, 0x52, 0x0A, 0x13, 0x0E, 0x54, 0x06, 0x52, 0x09, 0x1B, 0x0D, 0x54, 0x05, 0x62, 0xD0, 0x00, 0x51, 0x3C, 0x62, 0xD0, 0x00, 0x13, 0x06, 0x52, 0x05, 0x31, 0x80, 0x53, 0xE1, 0x50, 0x00, 0x31, 0x80, 0x1A, 0xE1, 0xD0, 0x06, 0x56, 0x00, 0x48, 0x80, 0x41, 0x52, 0x06, 0x11, 0x00, 0x52, 0x05, 0x31, 0x80, 0x19, 0x80, 0xD0, 0x35, 0x62, 0xD0, 0x00, 0x52, 0x2B, 0x07 + } + }, + { + 306, + 79, + 0x39, + 0x015D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x5D, 0x06, 0x73, 0x53, 0xE8, 0x52, 0x05, 0x73, 0x53, 0xE9, 0x51, 0xE8, 0x01, 0x01, 0x54, 0x06, 0x51, 0xE9, 0x09, 0x00, 0x54, 0x05, 0x62, 0xD0, 0x00, 0x51, 0x3C, 0x62, 0xD0, 0x00, 0x13, 0x06, 0x52, 0x05, 0x31, 0x80, 0x53, 0xE1, 0x50, 0x00, 0x31, 0x80, 0x1A, 0xE1, 0xD0, 0x04, 0x56, 0x00, 0x49, 0x3D, 0xFB, 0x00, 0xA0, 0x06, 0x3D, 0xFB, 0x02, 0xB1, 0x57, 0x62, 0xD0, 0x00, 0x51, 0x3A, 0x08, 0x85, 0xBC + } + }, + { + 307, + 79, + 0x39, + 0x015E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x5E, 0x62, 0xD0, 0x00, 0x51, 0x39, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x08, 0x51, 0xEB, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x7C, 0x4D, 0x1E, 0x7C, 0x71, 0x88, 0x54, 0x01, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x08, 0x51, 0xEB, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x92, 0xD7 + } + }, + { + 308, + 79, + 0x39, + 0x015F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x5F, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xE4, 0x08, 0x51, 0xE5, 0x08, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x7C, 0x4E, 0xE4, 0x38, 0xEE, 0x54, 0x02, 0x62, 0xD0, 0x00, 0x51, 0x3A, 0x08, 0x62, 0xD0, 0x00, 0x51, 0x39, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x51, 0xEE, 0x51, 0x56 + } + }, + { + 309, + 79, + 0x39, + 0x0160, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x60, 0x08, 0x51, 0xEF, 0x08, 0x7C, 0x4D, 0x1E, 0x7C, 0x71, 0x88, 0x05, 0x01, 0x62, 0xD0, 0x03, 0x51, 0xE6, 0x08, 0x51, 0xE7, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x08, 0x62, 0xD0, 0x01, 0x51, 0xEC, 0x08, 0x51, 0xED, 0x08, 0x51, 0xEE, 0x08, 0x51, 0xEF, 0x08, 0x7C, 0x4E, 0xE4, 0x38, 0xEE, 0x62, 0xD0, 0x00, 0x54, 0x03, 0x3D, 0x01, 0x00, 0xA0, 0x95, 0x3D, 0x02, 0xFF, 0xA0, 0x19, 0xE7 + } + }, + { + 310, + 79, + 0x39, + 0x0161, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x61, 0x90, 0x3D, 0x03, 0xFF, 0xA0, 0x8B, 0x52, 0x02, 0x3B, 0x03, 0xA0, 0x0B, 0x3D, 0x02, 0x08, 0xB0, 0x0B, 0x3D, 0x03, 0x01, 0xB0, 0x06, 0x7C, 0x71, 0xB9, 0x80, 0x76, 0x3D, 0x03, 0x08, 0xB0, 0x11, 0x3D, 0x02, 0x01, 0xB0, 0x0C, 0x52, 0x03, 0x03, 0x03, 0x54, 0x00, 0x07, 0x00, 0x2E, 0x80, 0x61, 0x52, 0x03, 0x3B, 0x02, 0xD0, 0x2C, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x13, 0x03, 0x39, 0x01, 0xB0, 0xAA, 0x0A + } + }, + { + 311, + 79, + 0x39, + 0x0162, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x62, 0x21, 0x50, 0x02, 0x08, 0x52, 0x02, 0x08, 0x7C, 0x4A, 0x10, 0x38, 0xFF, 0x18, 0x39, 0x00, 0xB0, 0x06, 0x7C, 0x71, 0xB9, 0x80, 0x3B, 0x52, 0x03, 0x03, 0x03, 0x54, 0x00, 0x07, 0x00, 0x2E, 0x80, 0x30, 0x52, 0x02, 0x3B, 0x03, 0xD0, 0x2A, 0x62, 0xD0, 0x00, 0x52, 0x03, 0x13, 0x02, 0x39, 0x01, 0xB0, 0x1F, 0x50, 0x02, 0x08, 0x52, 0x03, 0x08, 0x7C, 0x4A, 0x10, 0x38, 0xFF, 0x18, 0x39, 0x00, 0x26, 0x03 + } + }, + { + 312, + 79, + 0x39, + 0x0163, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x63, 0xB0, 0x0C, 0x52, 0x03, 0x03, 0x03, 0x54, 0x00, 0x07, 0x00, 0x2E, 0x80, 0x04, 0x7C, 0x71, 0xB9, 0x3D, 0x00, 0x00, 0xA0, 0x54, 0x3D, 0xFB, 0x01, 0xB0, 0x0D, 0x52, 0x00, 0x08, 0x90, 0x52, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x80, 0x48, 0x3D, 0xFB, 0x00, 0xB0, 0x12, 0x62, 0xD0, 0x00, 0x52, 0x00, 0x29, 0x30, 0x08, 0x91, 0x16, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x80, 0x32, 0x3D, 0xFB, 0x02, 0xB0, 0xE9, 0x8A + } + }, + { + 313, + 79, + 0x39, + 0x0164, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x64, 0x28, 0x3D, 0x00, 0x48, 0xA0, 0x06, 0x3D, 0x00, 0x49, 0xB0, 0x0D, 0x52, 0x00, 0x08, 0x90, 0x21, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x80, 0x17, 0x62, 0xD0, 0x00, 0x52, 0x00, 0x29, 0x30, 0x08, 0x90, 0xEA, 0x38, 0xFF, 0x62, 0xD0, 0x00, 0x80, 0x06, 0x62, 0xD0, 0x00, 0x50, 0x00, 0x38, 0xF1, 0x20, 0x7F, 0x10, 0x4F, 0x7C, 0x72, 0xEE, 0x39, 0x00, 0xA0, 0x43, 0x3D, 0xFC, 0x00, 0xA0, 0x3E, 0x62, 0x09, 0xCB + } + }, + { + 314, + 79, + 0x39, + 0x0165, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x65, 0xD0, 0x04, 0x52, 0xFC, 0x3A, 0xD7, 0xB0, 0x2C, 0x62, 0xD0, 0x00, 0x7C, 0x72, 0xEE, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xD8, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0xA0, 0x21, 0x7C, 0x72, 0x39, 0x39, 0x00, 0xA0, 0x04, 0x7C, 0x72, 0x8C, 0x7C, 0x4A, 0xD2, 0x62, 0xD0, 0x04, 0x76, 0xD8, 0x56, 0xFC, 0x00, 0x80, 0x0A, 0x62, 0xD0, 0x04, 0x55, 0xD8, 0x00, 0x7C, 0x72, 0x59, 0x7C, 0x72, 0x39, 0x39, 0x4E, 0x56 + } + }, + { + 315, + 79, + 0x39, + 0x0166, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x66, 0x00, 0xA0, 0x4D, 0x3D, 0xFC, 0x00, 0xA0, 0x48, 0x62, 0xD0, 0x04, 0x3C, 0xDB, 0x00, 0xA0, 0x3D, 0x3C, 0xDB, 0x48, 0xA0, 0x38, 0x3C, 0xDB, 0x49, 0xA0, 0x33, 0x62, 0xD0, 0x04, 0x52, 0xFC, 0x3A, 0xD7, 0xB0, 0x22, 0x62, 0xD0, 0x00, 0x7C, 0x72, 0x39, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xD6, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0xA0, 0x19, 0x7C, 0x4A, 0xD2, 0x62, 0xD0, 0x04, 0x76, 0xD6, 0x56, 0x79, 0xAD + } + }, + { + 316, + 79, + 0x39, + 0x0167, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x67, 0xFC, 0x00, 0x80, 0x0C, 0x7C, 0x72, 0x8C, 0x7C, 0x72, 0x59, 0x80, 0x04, 0x7C, 0x72, 0x8C, 0x3D, 0xFC, 0x00, 0xA0, 0x31, 0x7C, 0x4B, 0x1A, 0x7C, 0x4B, 0x99, 0x7C, 0x4C, 0x14, 0x7C, 0x6E, 0xC0, 0x62, 0xD0, 0x04, 0x52, 0xFC, 0x3A, 0xD0, 0xB0, 0x08, 0x62, 0xD0, 0x04, 0x76, 0xCF, 0x80, 0x04, 0x7C, 0x70, 0x5B, 0x52, 0xFC, 0x62, 0xD0, 0x04, 0x53, 0xD0, 0x52, 0xFC, 0x62, 0xD0, 0x04, 0x53, 0x22, 0x00 + } + }, + { + 317, + 79, + 0x39, + 0x0168, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x68, 0xDB, 0x7C, 0x73, 0x58, 0x52, 0xFC, 0x62, 0xD0, 0x00, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x05, 0x7C, 0x72, 0x64, 0x39, 0x00, 0xA1, 0x10, 0x56, 0x00, 0x00, 0x3D, 0xFC, 0x00, 0xA1, 0x08, 0x7C, 0x4B, 0x99, 0x62, 0xD0, 0x04, 0x52, 0xFC, 0x3A, 0xD9, 0xB0, 0x04, 0x56, 0x00, 0x01, 0x3D, 0x00, 0x00, 0xB0, 0xC1, 0x62, 0xD0, 0x00, 0x3C, 0x39, 0x00, 0xB0, 0x73, 0x62, 0xD0, 0x00, 0x3C, 0x3A, 0x00, 0x73, 0xA3 + } + }, + { + 318, + 79, + 0x39, + 0x0169, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x69, 0xB0, 0x6B, 0x62, 0xD0, 0x04, 0x3C, 0x9F, 0x00, 0xB0, 0x06, 0x3C, 0xA0, 0x00, 0xA0, 0x0E, 0x62, 0xD0, 0x04, 0x3C, 0x9D, 0x00, 0xB0, 0x56, 0x3C, 0x9E, 0x00, 0xB0, 0x51, 0x3D, 0xFC, 0x30, 0xB0, 0x13, 0x62, 0xD0, 0x04, 0x3C, 0xD9, 0x34, 0xA0, 0x06, 0x3C, 0xD9, 0x3C, 0xB0, 0x3F, 0x56, 0x00, 0x01, 0x80, 0x3A, 0x3D, 0xFC, 0x3C, 0xB0, 0x13, 0x62, 0xD0, 0x04, 0x3C, 0xD9, 0x38, 0xA0, 0x06, 0x5F, 0x7C + } + }, + { + 319, + 79, + 0x39, + 0x016A, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x6A, 0x3C, 0xD9, 0x30, 0xB0, 0x28, 0x56, 0x00, 0x01, 0x80, 0x23, 0x3D, 0xFC, 0x38, 0xA0, 0x06, 0x3D, 0xFC, 0x34, 0xB0, 0x19, 0x62, 0xD0, 0x00, 0x52, 0xFC, 0x11, 0x04, 0x7C, 0x70, 0x34, 0xA0, 0x0A, 0x52, 0xFC, 0x01, 0x04, 0x7C, 0x70, 0x34, 0xB0, 0x04, 0x56, 0x00, 0x01, 0x3D, 0xFC, 0x30, 0xB0, 0x13, 0x62, 0xD0, 0x04, 0x3C, 0xD9, 0x3E, 0xA0, 0x06, 0x3C, 0xD9, 0x32, 0xB0, 0x35, 0x56, 0x00, 0x1E, 0xFB + } + }, + { + 320, + 79, + 0x39, + 0x016B, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x6B, 0x01, 0x80, 0x30, 0x3D, 0xFC, 0x3E, 0xB0, 0x13, 0x62, 0xD0, 0x04, 0x3C, 0xD9, 0x30, 0xA0, 0x06, 0x3C, 0xD9, 0x3C, 0xB0, 0x1E, 0x56, 0x00, 0x01, 0x80, 0x19, 0x62, 0xD0, 0x00, 0x52, 0xFC, 0x11, 0x02, 0x7C, 0x70, 0x34, 0xA0, 0x0A, 0x52, 0xFC, 0x01, 0x02, 0x7C, 0x70, 0x34, 0xB0, 0x04, 0x56, 0x00, 0x01, 0x3D, 0x00, 0x01, 0xB0, 0x1F, 0x7C, 0x72, 0x64, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0x16, 0xEC + } + }, + { + 321, + 79, + 0x39, + 0x016C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x6C, 0xDA, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0xA0, 0x1E, 0x7C, 0x4A, 0xD2, 0x62, 0xD0, 0x04, 0x76, 0xDA, 0x56, 0xFC, 0x00, 0x80, 0x11, 0x62, 0xD0, 0x04, 0x55, 0xDA, 0x00, 0x52, 0xFC, 0x62, 0xD0, 0x04, 0x53, 0xD9, 0x56, 0xFC, 0x00, 0x3D, 0xFC, 0x00, 0xA0, 0xAE, 0x7C, 0x4B, 0x1A, 0x62, 0xD0, 0x03, 0x51, 0xED, 0x62, 0xD0, 0x03, 0x02, 0xE9, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0x43, 0x47 + } + }, + { + 322, + 79, + 0x39, + 0x016D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x6D, 0xEC, 0x62, 0xD0, 0x03, 0x0A, 0xE8, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x7C, 0x6D, 0xF3, 0x51, 0xE8, 0x54, 0x02, 0x51, 0xE9, 0x54, 0x01, 0x62, 0xD0, 0x03, 0x51, 0xEB, 0x62, 0xD0, 0x03, 0x02, 0xE7, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0xEA, 0x62, 0xD0, 0x03, 0x0A, 0xE6, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x7C, 0x6D, 0xF3, 0x51, 0xE8, 0x54, 0x04, 0x51, 0xE9, 0x54, 0x03, 0x52, 0xCA, 0x56 + } + }, + { + 323, + 79, + 0x39, + 0x016E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x6E, 0x01, 0x08, 0x52, 0x02, 0x08, 0x62, 0xD0, 0x04, 0x51, 0xA3, 0x08, 0x51, 0xA4, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x7C, 0x6F, 0xA8, 0x52, 0x03, 0x08, 0x52, 0x04, 0x08, 0x62, 0xD0, 0x04, 0x51, 0xA1, 0x08, 0x51, 0xA2, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x7C, 0x6F, 0x76, 0x7C, 0x4C, 0x14, 0x7C, 0x6E, 0xC0, 0x62, 0xD0, 0x04, 0x52, 0xFC, 0x3A, 0xD0, 0xB0, 0x08, 0x62, 0xD0, 0x04, 0x76, 0x9E, 0xFF + } + }, + { + 324, + 79, + 0x39, + 0x016F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x6F, 0xCF, 0x80, 0x04, 0x7C, 0x70, 0x5B, 0x52, 0xFC, 0x62, 0xD0, 0x04, 0x53, 0xD0, 0x52, 0xFC, 0x62, 0xD0, 0x04, 0x53, 0xDB, 0x7C, 0x73, 0x51, 0x52, 0xFC, 0x62, 0xD0, 0x00, 0x38, 0xFB, 0x20, 0x7F, 0x10, 0x4F, 0x50, 0x00, 0x08, 0x52, 0xFC, 0x08, 0x90, 0x07, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x04, 0x7C, 0x6F, 0xC9, 0x3D, 0xFB, 0x00, 0xA0, 0x37, 0x62, 0xD0, 0x04, 0xC4, 0x4C + } + }, + { + 325, + 79, + 0x39, + 0x0170, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x70, 0x55, 0xCD, 0x00, 0x7C, 0x70, 0x62, 0x50, 0x0D, 0x10, 0x08, 0x57, 0x8D, 0x28, 0x53, 0xE7, 0x18, 0x75, 0x09, 0x00, 0x28, 0x20, 0x02, 0xE8, 0x53, 0xE8, 0x51, 0xE7, 0x0A, 0xE9, 0x10, 0x08, 0x51, 0xE8, 0x20, 0x7C, 0x1E, 0xE4, 0x20, 0x7C, 0x73, 0x43, 0x7C, 0x70, 0x41, 0x52, 0xFB, 0x62, 0xD0, 0x00, 0x83, 0x5B, 0x3D, 0xFC, 0x00, 0xB2, 0xB8, 0x7C, 0x70, 0x5B, 0x10, 0x7C, 0x1E, 0x9A, 0x62, 0xB4, 0x2D + } + }, + { + 326, + 79, + 0x39, + 0x0171, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x71, 0xD0, 0x00, 0x5A, 0xE9, 0x20, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0xA9, 0x18, 0x53, 0xAA, 0x10, 0x7C, 0x1E, 0xA7, 0x62, 0xD0, 0x00, 0x5A, 0xE9, 0x20, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0xA7, 0x18, 0x53, 0xA8, 0x62, 0xD0, 0x04, 0x3C, 0xA9, 0x00, 0xB0, 0x06, 0x3C, 0xAA, 0x00, 0xA1, 0x37, 0x62, 0xD0, 0x04, 0x3C, 0xCC, 0x01, 0xB0, 0xFB, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x3F, 0x44 + } + }, + { + 327, + 79, + 0x39, + 0x0172, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x72, 0x08, 0x57, 0x8F, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x28, 0x20, 0x62, 0xD0, 0x04, 0x12, 0xAA, 0x7C, 0x71, 0x7F, 0x1A, 0xA9, 0xD0, 0xDD, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x08, 0x57, 0x8D, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x28, 0x53, 0xE8, 0x20, 0x7C, 0x70, 0x77, 0xD0, 0xC4, 0x7C, 0x72, 0xB9, 0x3A, 0xFE, 0xB0, 0x08, 0x7C, 0x72, 0xC2, 0x3A, 0xFF, 0xA0, 0x96, 0x62, 0x15, 0xF1 + } + }, + { + 328, + 79, + 0x39, + 0x0173, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x73, 0xD0, 0x03, 0x51, 0xF4, 0x62, 0xD0, 0x03, 0x3A, 0xFC, 0xB0, 0x0D, 0x62, 0xD0, 0x03, 0x51, 0xF5, 0x62, 0xD0, 0x03, 0x3A, 0xFD, 0xA0, 0x7E, 0x62, 0xD0, 0x03, 0x51, 0xF6, 0x08, 0x51, 0xF7, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xFE, 0x08, 0x51, 0xFF, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x57, 0x96, 0x28, 0x20, 0x7C, 0x71, 0xD1, 0xC0, 0x27, 0x62, 0xD0, 0x03, 0x07, 0xD6 + } + }, + { + 329, + 79, + 0x39, + 0x0174, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x74, 0x51, 0xF4, 0x08, 0x51, 0xF5, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xFC, 0x08, 0x51, 0xFD, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x57, 0x97, 0x28, 0x20, 0x7C, 0x71, 0xD1, 0xD0, 0x32, 0x56, 0x01, 0x01, 0x62, 0xD0, 0x04, 0x55, 0xCD, 0x00, 0x7C, 0x70, 0x41, 0x62, 0xD0, 0x03, 0x50, 0x0D, 0x10, 0x08, 0x57, 0x9D, 0x28, 0x53, 0xBC, 0x18, 0x75, 0x09, 0x00, 0x28, 0xDC, 0x81 + } + }, + { + 330, + 79, + 0x39, + 0x0175, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x75, 0x53, 0xBD, 0x20, 0x50, 0x0D, 0x10, 0x08, 0x57, 0x9F, 0x28, 0x53, 0xBE, 0x18, 0x75, 0x09, 0x00, 0x28, 0x53, 0xBF, 0x20, 0x3D, 0x01, 0x00, 0xB0, 0x1C, 0x62, 0xD0, 0x04, 0x3C, 0xCD, 0x00, 0xB0, 0x07, 0x7C, 0x6F, 0x87, 0x56, 0x00, 0x20, 0x62, 0xD0, 0x04, 0x76, 0xCD, 0x3C, 0xCD, 0x01, 0xB0, 0x04, 0x7C, 0x71, 0x3A, 0x62, 0xD0, 0x04, 0x3C, 0xCC, 0x02, 0xB0, 0x2D, 0x62, 0xD0, 0x00, 0x50, 0xA9, 0x1C + } + }, + { + 331, + 79, + 0x39, + 0x0176, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x76, 0x0D, 0x10, 0x08, 0x57, 0x8B, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x28, 0x20, 0x62, 0xD0, 0x04, 0x12, 0xAA, 0x7C, 0x71, 0x7F, 0x1A, 0xA9, 0xD0, 0x0F, 0x7C, 0x70, 0x62, 0x7C, 0x70, 0x77, 0xD0, 0x07, 0x56, 0x00, 0x40, 0x7C, 0x4B, 0x8F, 0x62, 0xD0, 0x03, 0x3C, 0xEE, 0x00, 0xB0, 0x06, 0x3C, 0xEF, 0x00, 0xA1, 0x26, 0x62, 0xD0, 0x04, 0x3C, 0xCC, 0x00, 0xB0, 0x42, 0x62, 0xD0, 0x04, 0x2A, 0x1F + } + }, + { + 332, + 79, + 0x39, + 0x0177, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x77, 0x3C, 0xCD, 0x01, 0xB0, 0x3A, 0x62, 0xD0, 0x03, 0x51, 0xEF, 0x62, 0xD0, 0x04, 0x12, 0xA8, 0x62, 0xD0, 0x03, 0x51, 0xEE, 0x62, 0xD0, 0x04, 0x1A, 0xA7, 0xD0, 0x0D, 0x7C, 0x72, 0x6F, 0x54, 0x03, 0x7C, 0x72, 0x7A, 0x54, 0x02, 0x80, 0x04, 0x7C, 0x70, 0x41, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x7C, 0x72, 0x0D, 0x20, 0x7C, 0x73, 0x4A, 0xD0, 0xEA, 0x7C, 0x4B, 0x8F, 0x80, 0xE5, 0x62, 0xD0, 0x5E, 0x88 + } + }, + { + 333, + 79, + 0x39, + 0x0178, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x78, 0x04, 0x3C, 0xCC, 0x01, 0xB0, 0xDD, 0x7C, 0x73, 0x35, 0xB0, 0xD8, 0x62, 0xD0, 0x03, 0x51, 0xEF, 0x62, 0xD0, 0x04, 0x12, 0xA8, 0x62, 0xD0, 0x03, 0x51, 0xEE, 0x62, 0xD0, 0x04, 0x1A, 0xA7, 0xD0, 0x0D, 0x7C, 0x72, 0x6F, 0x53, 0xEF, 0x7C, 0x72, 0x7A, 0x53, 0xEE, 0x80, 0x04, 0x7C, 0x70, 0x41, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x08, 0x57, 0x93, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x77, 0xBB + } + }, + { + 334, + 79, + 0x39, + 0x0179, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x79, 0x28, 0x20, 0x62, 0xD0, 0x03, 0x12, 0xEF, 0x7C, 0x70, 0xA1, 0x1A, 0xEE, 0xD0, 0x88, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x7C, 0x72, 0x0D, 0x53, 0xE8, 0x20, 0x62, 0xD0, 0x03, 0x51, 0xEF, 0x62, 0xD0, 0x00, 0x12, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0xEE, 0x62, 0xD0, 0x00, 0x1A, 0xE9, 0xD0, 0x66, 0x62, 0xD0, 0x03, 0x51, 0xF6, 0x08, 0x51, 0xF7, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xBC, 0x08, 0x51, 0x4F, 0x6C + } + }, + { + 335, + 79, + 0x39, + 0x017A, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x7A, 0xBD, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x57, 0x95, 0x28, 0x20, 0x7C, 0x70, 0xBF, 0xD0, 0x2F, 0x62, 0xD0, 0x03, 0x51, 0xF4, 0x08, 0x51, 0xF5, 0x08, 0x62, 0xD0, 0x03, 0x51, 0xBE, 0x08, 0x51, 0xBF, 0x08, 0x7C, 0x4B, 0x61, 0x38, 0xFC, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x57, 0x95, 0x28, 0x20, 0x7C, 0x70, 0xBF, 0xD0, 0x09, 0x56, 0x00, 0x22, 0x7C, 0x79, 0xC1 + } + }, + { + 336, + 79, + 0x39, + 0x017B, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x7B, 0x4B, 0x8F, 0x80, 0x1F, 0x7C, 0x6F, 0x87, 0x62, 0xD0, 0x04, 0x55, 0xCD, 0x01, 0x7C, 0x71, 0x3A, 0x56, 0x00, 0x20, 0x80, 0x0E, 0x7C, 0x4B, 0x8F, 0x80, 0x09, 0x7C, 0x73, 0x35, 0xB0, 0x04, 0x7C, 0x4B, 0x8F, 0x10, 0x50, 0x00, 0x5C, 0x7C, 0x1E, 0xE4, 0x20, 0x7C, 0x73, 0x43, 0x80, 0x53, 0x62, 0xD0, 0x04, 0x3C, 0xCC, 0x00, 0xB0, 0x04, 0x7C, 0x4A, 0xD9, 0x52, 0xFC, 0x62, 0xD0, 0x04, 0x53, 0x43, 0x56 + } + }, + { + 337, + 79, + 0x39, + 0x017C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x7C, 0xCC, 0x62, 0xD0, 0x01, 0x51, 0xE7, 0x08, 0x51, 0xE6, 0x62, 0xD0, 0x03, 0x53, 0xF6, 0x18, 0x53, 0xF7, 0x62, 0xD0, 0x01, 0x51, 0xE5, 0x08, 0x51, 0xE4, 0x62, 0xD0, 0x03, 0x53, 0xF4, 0x18, 0x53, 0xF5, 0x62, 0xD0, 0x01, 0x51, 0xEF, 0x08, 0x51, 0xEE, 0x62, 0xD0, 0x03, 0x53, 0xF2, 0x18, 0x53, 0xF3, 0x62, 0xD0, 0x01, 0x51, 0xED, 0x08, 0x51, 0xEC, 0x62, 0xD0, 0x03, 0x53, 0xF0, 0x18, 0x53, 0x03, 0xD7 + } + }, + { + 338, + 79, + 0x39, + 0x017D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x7D, 0xF1, 0x3D, 0x00, 0x40, 0xB0, 0x43, 0x7C, 0x72, 0xC2, 0x02, 0xF3, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x7C, 0x72, 0xB9, 0x0A, 0xF2, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x7C, 0x6D, 0xF3, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0xA3, 0x18, 0x53, 0xA4, 0x62, 0xD0, 0x03, 0x51, 0xF5, 0x02, 0xF5, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0xF4, 0x0A, 0xF4, 0x62, 0xD0, 0x00, 0x9F, 0x10 + } + }, + { + 339, + 79, + 0x39, + 0x017E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x7E, 0x53, 0xE9, 0x7C, 0x6D, 0xF3, 0x7C, 0x71, 0xC3, 0x52, 0x00, 0x62, 0xD0, 0x00, 0x38, 0xFC, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x06, 0x62, 0xD0, 0x00, 0x3C, 0x0E, 0x00, 0xA0, 0x06, 0x3D, 0xFC, 0xFF, 0xB0, 0x09, 0x62, 0xD0, 0x04, 0x55, 0xE2, 0x01, 0x85, 0xED, 0x62, 0xD0, 0x04, 0x3C, 0xE1, 0x00, 0xA0, 0x06, 0x3C, 0xE1, 0xFF, 0xB0, 0x74, 0x56, 0x00, 0x00, 0x56, 0x00, 0x00, 0x80, 0x65, 0x62, 0xDB, 0x89 + } + }, + { + 340, + 79, + 0x39, + 0x017F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x7F, 0xD0, 0x00, 0x52, 0x00, 0x7C, 0x6D, 0x8A, 0x06, 0xE8, 0xE0, 0x7C, 0x71, 0x08, 0x62, 0xD0, 0x04, 0x51, 0xE2, 0x62, 0xD0, 0x00, 0x3F, 0xE8, 0x52, 0x00, 0x7C, 0x6D, 0x8A, 0x06, 0xE8, 0xE1, 0x7C, 0x71, 0x08, 0x7C, 0x72, 0x9A, 0x62, 0xD0, 0x04, 0x76, 0xE2, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xB8, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x7C, 0x6E, 0xAE, 0x06, 0xE6, 0x45, 0x5E + } + }, + { + 341, + 79, + 0x39, + 0x0180, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x80, 0xB0, 0x0E, 0xE7, 0x03, 0x7C, 0x6D, 0xEA, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xB4, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x7C, 0x6E, 0xAE, 0x06, 0xE6, 0xEA, 0x0E, 0xE7, 0x00, 0x7C, 0x6D, 0xEA, 0x77, 0x00, 0x7C, 0x6F, 0xC1, 0xCF, 0x98, 0x85, 0x6D, 0x62, 0xD0, 0x04, 0x50, 0x03, 0x3A, 0xE1, 0xC0, 0x0A, 0x62, 0xD0, 0x00, 0x50, 0x03, 0x3A, 0x0E, 0xD4, 0xAD, 0x7C, 0x71, 0x93, 0xC2, 0xFD, 0xCF + } + }, + { + 342, + 79, + 0x39, + 0x0181, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x81, 0x0D, 0x7C, 0x6F, 0xC9, 0x80, 0x13, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xCC, 0x7C, 0x6F, 0x54, 0x50, 0xFF, 0x3F, 0xE8, 0x77, 0x00, 0x7C, 0x71, 0xB1, 0xCF, 0xEA, 0x56, 0x00, 0x00, 0x80, 0x51, 0x56, 0x02, 0x00, 0x80, 0x41, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x27, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x47, 0xE9, 0x0F, 0xA0, 0x29, 0x52, 0x02, 0x08, 0x95, 0x00 + } + }, + { + 343, + 79, + 0x39, + 0x0182, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x82, 0x52, 0x00, 0x08, 0x95, 0xA1, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x54, 0x03, 0x50, 0x0D, 0x10, 0x57, 0x85, 0x28, 0x20, 0x3B, 0x03, 0xC0, 0x0F, 0x52, 0x02, 0x08, 0x52, 0x00, 0x08, 0x95, 0x0D, 0x38, 0xFE, 0x77, 0x01, 0x80, 0x0C, 0x77, 0x02, 0x62, 0xD0, 0x04, 0x52, 0x02, 0x3A, 0xE1, 0xCF, 0xB8, 0x77, 0x00, 0x7C, 0x6F, 0xC1, 0xCF, 0xAC, 0x7C, 0x71, 0xE1, 0x3D, 0x00, 0x02, 0xA0, 0x06, 0x3D, 0xB2, 0x3B + } + }, + { + 344, + 79, + 0x39, + 0x0183, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x83, 0x00, 0x03, 0xB1, 0x09, 0x7C, 0x68, 0xBB, 0x52, 0x01, 0x08, 0x7C, 0x66, 0xDE, 0x52, 0x01, 0x08, 0x7C, 0x6A, 0x7F, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x51, 0x0E, 0x13, 0x01, 0x62, 0xD0, 0x00, 0x54, 0x00, 0x56, 0x02, 0x00, 0x80, 0xDF, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x27, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0x73, 0xBE + } + }, + { + 345, + 79, + 0x39, + 0x0184, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x84, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xCE, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x54, 0x04, 0x7C, 0x6F, 0x27, 0x06, 0xE8, 0xFD, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x54, 0x03, 0x7C, 0x71, 0x00, 0xCF, 0x77 + } + }, + { + 346, + 79, + 0x39, + 0x0185, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x85, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xC5, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x25, 0x24 + } + }, + { + 347, + 79, + 0x39, + 0x0186, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x86, 0x83, 0x54, 0x03, 0x52, 0x03, 0x7C, 0x6D, 0x8A, 0x06, 0xE8, 0xE0, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x7C, 0x72, 0x31, 0x06, 0xE6, 0xD0, 0x0E, 0xE7, 0x03, 0x7C, 0x6D, 0xEA, 0x52, 0x03, 0x7C, 0x6D, 0x8A, 0x06, 0xE8, 0xE1, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0x7C, 0x6E, 0xE1, 0x52, 0x04, 0x7C, 0x6E, 0xB6, 0x06, 0xE6, 0xD5, 0x0E, 0xE7, 0x02, 0x7C, 0x6D, 0xEA, 0x51, 0xE8, 0x36, 0x47 + } + }, + { + 348, + 79, + 0x39, + 0x0187, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x87, 0x3F, 0xE6, 0x77, 0x02, 0x52, 0x02, 0x3B, 0x00, 0xCF, 0x1D, 0x83, 0x3A, 0x56, 0x00, 0x00, 0x80, 0x76, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x47, 0xE9, 0xF0, 0xA0, 0x5E, 0x56, 0x03, 0x00, 0x56, 0x04, 0xFF, 0x56, 0x02, 0x00, 0x80, 0x3B, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x27, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x02, 0xE0 + } + }, + { + 349, + 79, + 0x39, + 0x0188, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x88, 0x83, 0x53, 0xE9, 0x47, 0xE9, 0x0F, 0xA0, 0x23, 0x52, 0x02, 0x08, 0x52, 0x00, 0x08, 0x94, 0x16, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x54, 0x05, 0x3D, 0x03, 0x00, 0xB0, 0x06, 0x7C, 0x71, 0x4B, 0x80, 0x0A, 0x52, 0x05, 0x3B, 0x03, 0xD0, 0x04, 0x7C, 0x71, 0x4B, 0x77, 0x02, 0x62, 0xD0, 0x04, 0x52, 0x02, 0x3A, 0xE1, 0xCF, 0xBE, 0x3D, 0x04, 0xFF, 0xA0, 0x0B, 0x52, 0x04, 0x08, 0x52, 0x00, 0x08, 0x89, 0xEF + } + }, + { + 350, + 79, + 0x39, + 0x0189, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x89, 0x93, 0x6A, 0x38, 0xFE, 0x77, 0x00, 0x7C, 0x6F, 0xC1, 0xCF, 0x87, 0x82, 0xB9, 0x7C, 0x6F, 0xC9, 0x80, 0x13, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xCC, 0x7C, 0x6F, 0x54, 0x50, 0xFF, 0x3F, 0xE8, 0x77, 0x00, 0x7C, 0x6F, 0xC1, 0xCF, 0xEA, 0x56, 0x00, 0x00, 0x80, 0x51, 0x56, 0x02, 0x00, 0x80, 0x41, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x27, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x7C, 0xED, 0xB8 + } + }, + { + 351, + 79, + 0x39, + 0x018A, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x8A, 0x6D, 0x83, 0x53, 0xE9, 0x47, 0xE9, 0xF0, 0xA0, 0x29, 0x52, 0x00, 0x08, 0x52, 0x02, 0x08, 0x93, 0x95, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x54, 0x03, 0x50, 0x0D, 0x10, 0x57, 0x85, 0x28, 0x20, 0x3B, 0x03, 0xC0, 0x0F, 0x52, 0x00, 0x08, 0x52, 0x02, 0x08, 0x93, 0x01, 0x38, 0xFE, 0x77, 0x01, 0x80, 0x0C, 0x77, 0x02, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x3A, 0x0E, 0xCF, 0xB8, 0x77, 0x00, 0x7C, 0x71, 0x06, 0xEB + } + }, + { + 352, + 79, + 0x39, + 0x018B, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x8B, 0xB1, 0xCF, 0xAC, 0x7C, 0x71, 0xE1, 0x50, 0x00, 0x3B, 0x00, 0xC0, 0x06, 0x3D, 0x00, 0x04, 0xD1, 0x24, 0x62, 0xD0, 0x00, 0x51, 0x0E, 0x13, 0x01, 0x62, 0xD0, 0x00, 0x39, 0x04, 0xD1, 0x16, 0x7C, 0x68, 0xBB, 0x52, 0x01, 0x08, 0x93, 0xF7, 0x52, 0x01, 0x08, 0x7C, 0x6B, 0x94, 0x38, 0xFE, 0x56, 0x02, 0x00, 0x80, 0xF9, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x27, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x9B, 0x16 + } + }, + { + 353, + 79, + 0x39, + 0x018C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x8C, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xC5, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x54, 0x04, 0x7C, 0x6F, 0x27, 0x98, 0x11 + } + }, + { + 354, + 79, + 0x39, + 0x018D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x8D, 0x06, 0xE8, 0xFD, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x54, 0x03, 0x7C, 0x6F, 0x44, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x7A, 0xE8, 0x53, 0xE7, 0x26, 0xE7, 0xF0, 0x7C, 0x6F, 0xD0, 0x7C, 0x71, 0x00, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xE5, 0xAC + } + }, + { + 355, + 79, + 0x39, + 0x018E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x8E, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xCE, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x54, 0x03, 0x52, 0x04, 0x7C, 0x6D, 0x8A, 0x06, 0xE8, 0xE0, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x52, 0x03, 0x53, 0xE6, 0x55, 0xE7, 0x00, 0x06, 0xE6, 0x47, 0x71 + } + }, + { + 356, + 79, + 0x39, + 0x018F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x8F, 0xD0, 0x0E, 0xE7, 0x03, 0x7C, 0x6D, 0xEA, 0x52, 0x04, 0x7C, 0x6D, 0x8A, 0x06, 0xE8, 0xE1, 0x0E, 0xE9, 0x01, 0x7C, 0x6D, 0x83, 0x7C, 0x6E, 0xE1, 0x52, 0x03, 0x7C, 0x6E, 0xB6, 0x06, 0xE6, 0xD5, 0x0E, 0xE7, 0x02, 0x7C, 0x6D, 0xEA, 0x51, 0xE8, 0x3F, 0xE6, 0x77, 0x02, 0x52, 0x02, 0x3B, 0x00, 0xCF, 0x03, 0x80, 0x80, 0x56, 0x00, 0x00, 0x80, 0x76, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0x2C, 0x3C + } + }, + { + 357, + 79, + 0x39, + 0x0190, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x90, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x47, 0xE9, 0x0F, 0xA0, 0x5E, 0x56, 0x03, 0x00, 0x56, 0x04, 0xFF, 0x56, 0x02, 0x00, 0x80, 0x3B, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x27, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x47, 0xE9, 0xF0, 0xA0, 0x23, 0x52, 0x00, 0x08, 0x52, 0x02, 0x08, 0x91, 0xEE, 0x38, 0xFE, 0x62, 0xD0, 0x00, 0x54, 0x05, 0x3D, 0x0E, 0x01 + } + }, + { + 358, + 79, + 0x39, + 0x0191, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x91, 0x03, 0x00, 0xB0, 0x06, 0x7C, 0x71, 0x4B, 0x80, 0x0A, 0x52, 0x05, 0x3B, 0x03, 0xD0, 0x04, 0x7C, 0x71, 0x4B, 0x77, 0x02, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x3A, 0x0E, 0xCF, 0xBE, 0x3D, 0x04, 0xFF, 0xA0, 0x0B, 0x52, 0x00, 0x08, 0x52, 0x04, 0x08, 0x91, 0x42, 0x38, 0xFE, 0x77, 0x00, 0x7C, 0x71, 0xB1, 0xCF, 0x87, 0x7C, 0x71, 0x93, 0xD0, 0x8E, 0x7C, 0x72, 0xDD, 0x12, 0xE1, 0x62, 0xD0, 0x00, 0xD5, 0x90 + } + }, + { + 359, + 79, + 0x39, + 0x0192, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x92, 0x54, 0x00, 0x56, 0x02, 0x00, 0x80, 0x57, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x27, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x26, 0xE9, 0xF0, 0x3C, 0xE9, 0xF0, 0xB0, 0x35, 0x7C, 0x6F, 0x27, 0x06, 0xE8, 0xD0, 0x7C, 0x6F, 0x54, 0x62, 0xD0, 0x04, 0x51, 0xE2, 0x62, 0xD0, 0x00, 0x3F, 0xE8, 0x52, 0x02, 0x7C, 0x6D, 0xD9, 0x06, 0xE8, 0xD5, 0x7C, 0x6F, 0xE2, 0x7C, 0x72, 0x81, 0xE9 + } + }, + { + 360, + 79, + 0x39, + 0x0193, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x93, 0x9A, 0x62, 0xD0, 0x04, 0x51, 0xE2, 0x08, 0x91, 0xCC, 0x38, 0xFF, 0x62, 0xD0, 0x04, 0x53, 0xE2, 0x7B, 0x00, 0x80, 0x08, 0x3D, 0x00, 0x00, 0xB0, 0x03, 0x80, 0x2B, 0x77, 0x02, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x3A, 0x0E, 0xCF, 0xA2, 0x80, 0x1E, 0x50, 0x00, 0x08, 0x91, 0xF1, 0x38, 0xFF, 0x7C, 0x71, 0x93, 0xC0, 0x0A, 0x50, 0x00, 0x08, 0x95, 0x86, 0x38, 0xFF, 0x80, 0x09, 0x50, 0x00, 0x08, 0xE4, 0xB0 + } + }, + { + 361, + 79, + 0x39, + 0x0194, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x94, 0x7C, 0x6B, 0x94, 0x38, 0xFF, 0x56, 0x00, 0x00, 0x80, 0x88, 0x62, 0xD0, 0x00, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xD0, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x7C, 0x6E, 0xAE, 0x65, 0xE6, 0x6B, 0xE7, 0x7C, 0x70, 0x1E, 0x06, 0xE6, 0xE0, 0x0E, 0xE7, 0x01, 0x7C, 0x6D, 0xEA, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xB8, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x7C, 0x6E, 0xAE, 0x06, 0x78, 0xD9 + } + }, + { + 362, + 79, + 0x39, + 0x0195, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x95, 0xE6, 0xB0, 0x0E, 0xE7, 0x03, 0x7C, 0x6D, 0xEA, 0x7C, 0x6D, 0xA5, 0x06, 0xE8, 0xB4, 0x0E, 0xE9, 0x03, 0x7C, 0x6D, 0x83, 0x53, 0xE9, 0x7C, 0x6E, 0xAE, 0x06, 0xE6, 0xEA, 0x0E, 0xE7, 0x00, 0x7C, 0x6D, 0xEA, 0x7C, 0x6D, 0xA5, 0x65, 0xE8, 0x6B, 0xE9, 0x06, 0xE8, 0xD5, 0x0E, 0xE9, 0x02, 0x7C, 0x6D, 0x83, 0x7C, 0x6E, 0xE1, 0x7C, 0x73, 0x3C, 0x7C, 0x6E, 0xAE, 0x65, 0xE6, 0x6B, 0xE7, 0x7C, 0x67, 0xB8 + } + }, + { + 363, + 79, + 0x39, + 0x0196, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x96, 0x70, 0x1E, 0x06, 0xE6, 0xE1, 0x0E, 0xE7, 0x01, 0x7C, 0x6D, 0xEA, 0x51, 0xE8, 0x3F, 0xE6, 0x77, 0x00, 0x7C, 0x6F, 0xC1, 0xCF, 0x75, 0x3D, 0xFC, 0xFF, 0xA0, 0x08, 0x7C, 0x72, 0xDD, 0x53, 0xE1, 0x80, 0x07, 0x62, 0xD0, 0x04, 0x55, 0xE1, 0xFF, 0x38, 0xFA, 0x20, 0x7F, 0x10, 0x4F, 0x62, 0xD0, 0x00, 0x52, 0xFB, 0x97, 0xD5, 0x40, 0x06, 0xE8, 0xE0, 0x0E, 0xE9, 0x01, 0x97, 0xC5, 0x40, 0x53, 0xCA, 0x7F + } + }, + { + 364, + 79, + 0x39, + 0x0197, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x97, 0xE9, 0x52, 0xFC, 0x53, 0xE6, 0x55, 0xE7, 0x00, 0x06, 0xE6, 0xD0, 0x0E, 0xE7, 0x03, 0x7C, 0x6D, 0xEA, 0x52, 0xFB, 0x97, 0xB5, 0x40, 0x06, 0xE8, 0xE1, 0x0E, 0xE9, 0x01, 0x97, 0xA5, 0x40, 0x7C, 0x6E, 0xE1, 0x52, 0xFC, 0x7C, 0x6E, 0xB6, 0x06, 0xE6, 0xD5, 0x0E, 0xE7, 0x02, 0x97, 0xFB, 0x40, 0x51, 0xE8, 0x3F, 0xE6, 0x7C, 0x70, 0xF0, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x97, 0x84, 0x40, 0xDB, 0xA2 + } + }, + { + 365, + 79, + 0x39, + 0x0198, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x98, 0x7A, 0xE8, 0x53, 0xE7, 0x26, 0xE7, 0xF0, 0x7C, 0x6F, 0xD0, 0x52, 0xFC, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x97, 0x6A, 0x40, 0x7A, 0xE8, 0x53, 0xE7, 0x26, 0xE7, 0x0F, 0x7C, 0x6F, 0xD0, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x03, 0x7C, 0x6F, 0xB9, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0xB8, 0x0E, 0xE9, 0x03, 0x97, 0x4B, 0x40, 0x54, 0x00, 0x7C, 0x70, 0xF0, 0x06, 0xE8, 0x7E, 0xE9 + } + }, + { + 366, + 79, + 0x39, + 0x0199, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x99, 0xB0, 0x0E, 0xE9, 0x03, 0x97, 0x3D, 0x40, 0x54, 0x01, 0x52, 0x00, 0x3B, 0x01, 0xD0, 0x08, 0x7C, 0x73, 0x06, 0x54, 0x02, 0x80, 0x06, 0x7C, 0x72, 0xFE, 0x54, 0x02, 0x7C, 0x6F, 0xB9, 0x55, 0xE9, 0x00, 0x06, 0xE8, 0xB4, 0x0E, 0xE9, 0x03, 0x97, 0x1A, 0x40, 0x54, 0x00, 0x7C, 0x70, 0xF0, 0x06, 0xE8, 0xEA, 0x0E, 0xE9, 0x00, 0x97, 0x0C, 0x40, 0x54, 0x01, 0x52, 0x00, 0x3B, 0x01, 0xD0, 0x08, 0x98, 0x1E + } + }, + { + 367, + 79, + 0x39, + 0x019A, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x9A, 0x7C, 0x73, 0x06, 0x05, 0x02, 0x80, 0x06, 0x7C, 0x72, 0xFE, 0x05, 0x02, 0x52, 0x02, 0x62, 0xD0, 0x00, 0x38, 0xFD, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x77, 0xFC, 0xB0, 0x03, 0x77, 0xFC, 0x50, 0x0F, 0x3B, 0xFC, 0xD0, 0x04, 0x56, 0xFC, 0x01, 0x52, 0xFC, 0x54, 0x01, 0x56, 0x00, 0x00, 0x80, 0x1A, 0x62, 0xD0, 0x00, 0x52, 0x00, 0x96, 0xD2, 0x40, 0x06, 0xE8, 0xE0, 0x0E, 0xE9, 0x01, 0x96, 0xD9, 0xA1 + } + }, + { + 368, + 79, + 0x39, + 0x019B, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x9B, 0xC2, 0x40, 0x3B, 0xFC, 0xB0, 0x03, 0x77, 0xFC, 0x77, 0x00, 0x7C, 0x6F, 0xC1, 0xCF, 0xE3, 0x52, 0xFC, 0x3B, 0x01, 0xBF, 0xD4, 0x52, 0xFC, 0x62, 0xD0, 0x00, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x0B, 0x3D, 0xFC, 0x00, 0xB0, 0x11, 0x62, 0xD0, 0x04, 0x51, 0xE1, 0x54, 0x03, 0x62, 0xD0, 0x00, 0x51, 0x0E, 0x54, 0x02, 0x80, 0x0D, 0x62, 0xD0, 0x04, 0x51, 0xE1, 0x13, 0xFC, 0x54, 0x03, 0x43, 0x76 + } + }, + { + 369, + 79, + 0x39, + 0x019C, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x9C, 0x7C, 0x70, 0xDB, 0x56, 0x00, 0x00, 0x56, 0x01, 0x00, 0x81, 0xA7, 0x56, 0x04, 0x00, 0x81, 0x97, 0x3D, 0xFC, 0x00, 0xB0, 0x22, 0x62, 0xD0, 0x00, 0x7C, 0x6F, 0x44, 0x06, 0xE8, 0xB8, 0x0E, 0xE9, 0x03, 0x96, 0x60, 0x40, 0x54, 0x05, 0x96, 0x7D, 0x40, 0x06, 0xE8, 0xB0, 0x0E, 0xE9, 0x03, 0x96, 0x52, 0x40, 0x54, 0x06, 0x80, 0x54, 0x62, 0xD0, 0x00, 0x52, 0x04, 0x96, 0xBD, 0x40, 0x96, 0x43, 0xDE, 0xAD + } + }, + { + 370, + 79, + 0x39, + 0x019D, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x9D, 0x40, 0x54, 0x05, 0x96, 0x60, 0x40, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0x4D, 0x8C + } + }, + { + 371, + 79, + 0x39, + 0x019E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x9E, 0xC3, 0x0E, 0xE9, 0x02, 0x95, 0xFD, 0x40, 0x54, 0x06, 0x52, 0x05, 0x3B, 0x06, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x52, 0x06, 0x13, 0x05, 0x54, 0x07, 0x80, 0x0A, 0x62, 0xD0, 0x00, 0x52, 0x05, 0x13, 0x06, 0x54, 0x07, 0x3D, 0xFC, 0x00, 0xB0, 0x22, 0x62, 0xD0, 0x00, 0x97, 0x97, 0x40, 0x06, 0xE8, 0xB4, 0x0E, 0xE9, 0x03, 0x95, 0xCD, 0x40, 0x54, 0x05, 0x95, 0xEA, 0x40, 0x06, 0xE8, 0xEA, 0x0E, 0xC7, 0x81 + } + }, + { + 372, + 79, + 0x39, + 0x019F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0x9F, 0xE9, 0x00, 0x95, 0xBF, 0x40, 0x54, 0x06, 0x80, 0x54, 0x62, 0xD0, 0x00, 0x52, 0x04, 0x96, 0x6E, 0x40, 0x95, 0xB0, 0x40, 0x54, 0x05, 0x95, 0xCD, 0x40, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0B, 0x0A + } + }, + { + 373, + 79, + 0x39, + 0x01A0, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA0, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xC4, 0x0E, 0xE9, 0x02, 0x95, 0x6A, 0x40, 0x54, 0x06, 0x52, 0x05, 0x3B, 0x06, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x52, 0x06, 0x13, 0x05, 0x54, 0x08, 0x80, 0x0A, 0x62, 0xD0, 0x00, 0x52, 0x05, 0x13, 0x06, 0x54, 0x08, 0x62, 0xD0, 0x00, 0x52, 0x07, 0x53, 0xE8, 0x50, 0x00, 0x08, 0xD8, 0xA5 + } + }, + { + 374, + 79, + 0x39, + 0x01A1, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA1, 0x51, 0xE8, 0x08, 0x52, 0x07, 0x08, 0x95, 0x13, 0x7C, 0x71, 0xF5, 0x52, 0x08, 0x53, 0xE6, 0x50, 0x00, 0x08, 0x51, 0xE6, 0x08, 0x52, 0x08, 0x08, 0x95, 0x01, 0x38, 0xFA, 0x62, 0xD0, 0x00, 0x52, 0x0A, 0x02, 0xE8, 0x62, 0xD0, 0x03, 0x53, 0xDF, 0x52, 0x09, 0x62, 0xD0, 0x00, 0x0A, 0xE9, 0x62, 0xD0, 0x03, 0x53, 0xDE, 0x62, 0xD0, 0x00, 0x96, 0xCB, 0x40, 0x52, 0x01, 0x02, 0xE8, 0x53, 0xE8, 0x90, 0x16 + } + }, + { + 375, + 79, + 0x39, + 0x01A2, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA2, 0x50, 0x00, 0x0A, 0xE9, 0x53, 0xE9, 0x65, 0xE8, 0x6B, 0xE9, 0x06, 0xE8, 0xB1, 0x97, 0x53, 0x40, 0x62, 0xD0, 0x03, 0x51, 0xDE, 0x62, 0xD0, 0x00, 0x3F, 0xE8, 0x62, 0xD0, 0x03, 0x51, 0xDF, 0x62, 0xD0, 0x00, 0x3F, 0xE8, 0x77, 0x04, 0x52, 0x04, 0x3B, 0x02, 0xCE, 0x65, 0x77, 0x00, 0x07, 0x01, 0x03, 0x52, 0x00, 0x3B, 0x03, 0xCE, 0x55, 0x38, 0xF5, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x97, 0xD6, 0xA3 + } + }, + { + 376, + 79, + 0x39, + 0x01A3, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA3, 0x08, 0x40, 0x80, 0x95, 0x62, 0xD0, 0x00, 0x94, 0xDC, 0x40, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x94, 0xB1, 0x40, 0x53, 0xE9, 0x47, 0xE9, 0xF0, 0xA0, 0x7D, 0x52, 0x01, 0x95, 0x1C, 0x40, 0x95, 0xCD, 0x40, 0x06, 0xE6, 0xB8, 0x0E, 0xE7, 0x03, 0x51, 0xE7, 0x60, 0xD4, 0x3E, 0xE6, 0x53, 0xE7, 0x96, 0xDE, 0x40, 0x52, 0x01, 0x95, 0x47, 0x40, 0x95, 0xB4, 0x40, 0x06, 0xE6, 0xB4, 0x0E, 0xE7, 0x7E, 0xF4 + } + }, + { + 377, + 79, + 0x39, + 0x01A4, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA4, 0x03, 0x51, 0xE7, 0x60, 0xD4, 0x3E, 0xE6, 0x53, 0xE7, 0x96, 0xC5, 0x40, 0x52, 0x01, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0xD5, 0xA3 + } + }, + { + 378, + 79, + 0x39, + 0x01A5, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA5, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xCE, 0x0E, 0xE9, 0x02, 0x96, 0x86, 0x40, 0x77, 0x01, 0x77, 0x00, 0x96, 0x67, 0x40, 0xCF, 0x68, 0x96, 0x6A, 0x40, 0x81, 0x15, 0x62, 0xD0, 0x00, 0x94, 0x3E, 0x40, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x03, 0x94, 0x13, 0x40, 0x53, 0xE9, 0x47, 0xE9, 0x0F, 0xA0, 0xFD, 0x52, 0x01, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x55, 0x51, 0x9C + } + }, + { + 379, + 79, + 0x39, + 0x01A6, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA6, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xC3, 0x0E, 0xE9, 0x02, 0x94, 0xEF, 0x40, 0xA8, 0x4B + } + }, + { + 380, + 79, + 0x39, + 0x01A7, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA7, 0x06, 0xE6, 0xB0, 0x0E, 0xE7, 0x03, 0x51, 0xE7, 0x60, 0xD4, 0x3E, 0xE6, 0x53, 0xE7, 0x96, 0x00, 0x40, 0x52, 0x01, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0x20, 0x3C + } + }, + { + 381, + 79, + 0x39, + 0x01A8, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA8, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xC4, 0x0E, 0xE9, 0x02, 0x94, 0x96, 0x40, 0x06, 0xE6, 0xEA, 0x0E, 0xE7, 0x00, 0x51, 0xE7, 0x60, 0xD4, 0x3E, 0xE6, 0x53, 0xE7, 0x95, 0xA7, 0x40, 0x52, 0x01, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0x6A, 0xD1 + } + }, + { + 382, + 79, + 0x39, + 0x01A9, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xA9, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xC5, 0x0E, 0xE9, 0x02, 0x95, 0x68, 0x40, 0x77, 0x01, 0x77, 0x00, 0x97, 0x39, 0x40, 0xCE, 0xE8, 0x38, 0xFE, 0x20, 0x7F, 0x10, 0x0B, 0x14 + } + }, + { + 383, + 79, + 0x39, + 0x01AA, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xAA, 0x4F, 0x38, 0x07, 0x3D, 0xFC, 0x00, 0xB0, 0x11, 0x62, 0xD0, 0x04, 0x51, 0xE1, 0x54, 0x05, 0x62, 0xD0, 0x00, 0x51, 0x0E, 0x54, 0x02, 0x80, 0x0D, 0x62, 0xD0, 0x04, 0x51, 0xE1, 0x13, 0xFC, 0x54, 0x05, 0x96, 0x38, 0x40, 0x62, 0xD0, 0x00, 0x94, 0x6F, 0x40, 0x06, 0xE8, 0x5E, 0x0E, 0xE9, 0x0F, 0x94, 0xAA, 0x40, 0x54, 0x04, 0x56, 0x03, 0x00, 0x56, 0x01, 0x00, 0x97, 0xE4, 0x40, 0x80, 0xCB, 0xEE, 0xDB + } + }, + { + 384, + 79, + 0x39, + 0x01AB, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xAB, 0x62, 0xD0, 0x03, 0x55, 0xDF, 0x00, 0x55, 0xDE, 0x00, 0x56, 0x06, 0x00, 0x80, 0x34, 0x96, 0xCE, 0x40, 0x52, 0x01, 0x94, 0x2B, 0x40, 0x10, 0x57, 0x03, 0x7C, 0x4A, 0xBC, 0x20, 0x03, 0x06, 0x54, 0x00, 0x92, 0xC2, 0x40, 0x65, 0xE8, 0x6B, 0xE9, 0x06, 0xE8, 0xB1, 0x0E, 0xE9, 0x02, 0x92, 0x93, 0x40, 0x53, 0xE9, 0x3E, 0xE8, 0x62, 0xD0, 0x03, 0x04, 0xDF, 0x95, 0xA5, 0x40, 0x0C, 0xDE, 0x77, 0x92, 0x24 + } + }, + { + 385, + 79, + 0x39, + 0x01AC, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xAC, 0x06, 0x52, 0x06, 0x3B, 0x02, 0xCF, 0xC8, 0x95, 0x83, 0x40, 0xD0, 0x7A, 0x62, 0xD0, 0x03, 0x51, 0xDF, 0x08, 0x51, 0xDE, 0x62, 0xD0, 0x04, 0x53, 0xB1, 0x18, 0x53, 0xB2, 0x56, 0x06, 0x00, 0x80, 0x5F, 0x96, 0x7B, 0x40, 0x52, 0x01, 0x93, 0xD8, 0x40, 0x54, 0x00, 0x3D, 0xFC, 0x00, 0xB0, 0x42, 0x52, 0x00, 0x92, 0x56, 0x40, 0x06, 0xE8, 0xE0, 0x0E, 0xE9, 0x01, 0x92, 0x46, 0x40, 0x53, 0xE9, 0x64, 0xC9 + } + }, + { + 386, + 79, + 0x39, + 0x01AD, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xAD, 0x96, 0x67, 0x40, 0x06, 0xE6, 0xD0, 0x0E, 0xE7, 0x03, 0x92, 0x9F, 0x40, 0x52, 0x00, 0x92, 0x3A, 0x40, 0x06, 0xE8, 0xE1, 0x0E, 0xE9, 0x01, 0x92, 0x2A, 0x40, 0x93, 0x85, 0x40, 0x52, 0x06, 0x93, 0x55, 0x40, 0x06, 0xE6, 0xD5, 0x0E, 0xE7, 0x02, 0x92, 0x80, 0x40, 0x51, 0xE8, 0x3F, 0xE6, 0x80, 0x0D, 0x96, 0x2B, 0x40, 0x06, 0xE8, 0xFD, 0x0E, 0xE9, 0x02, 0x94, 0x5D, 0x40, 0x77, 0x06, 0x52, 0x35, 0x6C + } + }, + { + 387, + 79, + 0x39, + 0x01AE, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xAE, 0x06, 0x3B, 0x02, 0xCF, 0x9D, 0x77, 0x03, 0x07, 0x01, 0x03, 0x52, 0x03, 0x3B, 0x04, 0xCF, 0x31, 0x38, 0xF9, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x07, 0x3D, 0xFC, 0x00, 0xB0, 0x11, 0x62, 0xD0, 0x04, 0x51, 0xE1, 0x54, 0x01, 0x62, 0xD0, 0x00, 0x51, 0x0E, 0x54, 0x02, 0x80, 0x0D, 0x62, 0xD0, 0x04, 0x51, 0xE1, 0x13, 0xFC, 0x54, 0x01, 0x95, 0x23, 0x40, 0x62, 0xD0, 0x00, 0x93, 0x69, 0x40, 0x06, 0x99, 0x35 + } + }, + { + 388, + 79, + 0x39, + 0x01AF, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xAF, 0xE8, 0x5E, 0x0E, 0xE9, 0x0F, 0x93, 0x95, 0x40, 0x54, 0x04, 0x56, 0x03, 0x00, 0x56, 0x00, 0x00, 0x96, 0xCF, 0x40, 0x81, 0x6E, 0x62, 0xD0, 0x03, 0x55, 0xDF, 0x00, 0x55, 0xDE, 0x00, 0x56, 0x05, 0x00, 0x80, 0x39, 0x62, 0xD0, 0x00, 0x93, 0x30, 0x40, 0x52, 0x00, 0x93, 0x13, 0x40, 0x53, 0xE9, 0x10, 0x52, 0x05, 0x57, 0x03, 0x7C, 0x4A, 0xBC, 0x20, 0x02, 0xE9, 0x54, 0x06, 0x52, 0x06, 0x91, 0x39, 0x76 + } + }, + { + 389, + 79, + 0x39, + 0x01B0, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB0, 0xD8, 0x40, 0x06, 0xE8, 0xB1, 0x0E, 0xE9, 0x02, 0x91, 0x79, 0x40, 0x53, 0xE9, 0x3E, 0xE8, 0x62, 0xD0, 0x03, 0x04, 0xDF, 0x94, 0x8B, 0x40, 0x0C, 0xDE, 0x77, 0x05, 0x52, 0x05, 0x3B, 0x01, 0xCF, 0xC3, 0x94, 0x69, 0x40, 0xD1, 0x18, 0x62, 0xD0, 0x03, 0x51, 0xDF, 0x08, 0x51, 0xDE, 0x62, 0xD0, 0x04, 0x53, 0xB1, 0x18, 0x53, 0xB2, 0x56, 0x05, 0x00, 0x80, 0x2A, 0x3D, 0xFC, 0x00, 0xB0, 0x13, 0x78, 0xF5 + } + }, + { + 390, + 79, + 0x39, + 0x01B1, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB1, 0x62, 0xD0, 0x00, 0x92, 0xD3, 0x40, 0x06, 0xE8, 0xD0, 0x93, 0x09, 0x40, 0x50, 0x00, 0x3F, 0xE8, 0x80, 0x11, 0x62, 0xD0, 0x00, 0x92, 0xC1, 0x40, 0x06, 0xE8, 0xFD, 0x93, 0x85, 0x40, 0x50, 0xFF, 0x3F, 0xE8, 0x77, 0x05, 0x52, 0x05, 0x3B, 0x02, 0xCF, 0xD2, 0x56, 0x05, 0x00, 0x80, 0x66, 0x62, 0xD0, 0x00, 0x92, 0xA4, 0x40, 0x52, 0x00, 0x92, 0x87, 0x40, 0x54, 0x06, 0x3D, 0xFC, 0x00, 0xB0, 0x7F, 0x04 + } + }, + { + 391, + 79, + 0x39, + 0x01B2, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB2, 0x42, 0x52, 0x05, 0x91, 0x05, 0x40, 0x06, 0xE8, 0xE0, 0x0E, 0xE9, 0x01, 0x90, 0xF5, 0x40, 0x53, 0xE9, 0x95, 0x16, 0x40, 0x06, 0xE6, 0xD0, 0x0E, 0xE7, 0x03, 0x91, 0x4E, 0x40, 0x52, 0x05, 0x90, 0xE9, 0x40, 0x06, 0xE8, 0xE1, 0x0E, 0xE9, 0x01, 0x90, 0xD9, 0x40, 0x92, 0x34, 0x40, 0x52, 0x06, 0x92, 0x04, 0x40, 0x06, 0xE6, 0xD5, 0x0E, 0xE7, 0x02, 0x91, 0x2F, 0x40, 0x51, 0xE8, 0x3F, 0xE6, 0xBE, 0x83 + } + }, + { + 392, + 79, + 0x39, + 0x01B3, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB3, 0x80, 0x11, 0x62, 0xD0, 0x00, 0x92, 0x51, 0x40, 0x06, 0xE8, 0xFD, 0x93, 0x15, 0x40, 0x52, 0x06, 0x3F, 0xE8, 0x77, 0x05, 0x52, 0x05, 0x3B, 0x01, 0xCF, 0x96, 0x3D, 0xFC, 0x00, 0xB0, 0x5F, 0x62, 0xD0, 0x04, 0x51, 0xE2, 0x62, 0xD0, 0x04, 0x53, 0xE3, 0x56, 0x05, 0x00, 0x80, 0x4A, 0x62, 0xD0, 0x00, 0x92, 0x25, 0x40, 0x06, 0xE8, 0xD0, 0x0E, 0xE9, 0x03, 0x90, 0x87, 0x40, 0x39, 0x00, 0xB0, 0x0F, 0x26 + } + }, + { + 393, + 79, + 0x39, + 0x01B4, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB4, 0x35, 0x92, 0x15, 0x40, 0x06, 0xE8, 0xD0, 0x92, 0x4B, 0x40, 0x62, 0xD0, 0x04, 0x51, 0xE3, 0x62, 0xD0, 0x00, 0x3F, 0xE8, 0x62, 0xD0, 0x04, 0x51, 0xE3, 0x08, 0x7C, 0x66, 0x95, 0x38, 0xFF, 0x62, 0xD0, 0x04, 0x53, 0xE3, 0x62, 0xD0, 0x00, 0x52, 0x05, 0x90, 0xAE, 0x40, 0x06, 0xE8, 0xD5, 0x92, 0xB1, 0x40, 0x95, 0x66, 0x40, 0x77, 0x05, 0x52, 0x05, 0x3B, 0x02, 0xCF, 0xB2, 0x77, 0x03, 0x07, 0xE0, 0xC9 + } + }, + { + 394, + 79, + 0x39, + 0x01B5, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB5, 0x00, 0x03, 0x52, 0x03, 0x3B, 0x04, 0xCE, 0x8E, 0x3D, 0xFC, 0x00, 0xB0, 0x0B, 0x62, 0xD0, 0x04, 0x51, 0xE3, 0x62, 0xD0, 0x04, 0x53, 0xE2, 0x38, 0xF9, 0x20, 0x7F, 0x10, 0x4F, 0x38, 0x02, 0x92, 0x68, 0x40, 0x48, 0xFC, 0x01, 0xA0, 0x09, 0x52, 0xFB, 0x05, 0x01, 0x52, 0xFA, 0x0D, 0x00, 0x66, 0xFB, 0x6C, 0xFA, 0x70, 0xFB, 0x6F, 0xFC, 0x3D, 0xFC, 0x00, 0xBF, 0xE7, 0x93, 0xB0, 0x40, 0x38, 0x30, 0x6A + } + }, + { + 395, + 79, + 0x39, + 0x01B6, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB6, 0xFE, 0x20, 0x7F, 0x51, 0xE9, 0x60, 0xD4, 0x3E, 0xE8, 0x7F, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x65, 0xE8, 0x6B, 0xE9, 0x65, 0xE8, 0x6B, 0xE9, 0x65, 0xE8, 0x6B, 0xE9, 0x7F, 0x65, 0xE8, 0x6B, 0xE9, 0x65, 0xE8, 0x6B, 0xE9, 0x7F, 0x52, 0x00, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x7F, 0x70, 0xFB, 0x6E, 0xE9, 0x6E, 0xE8, 0x70, 0xFB, 0x6E, 0xE9, 0x6E, 0xE8, 0x70, 0xFB, 0x6E, 0xE9, 0x6E, 0xE8, 0x70, 0x4E, 0xA7 + } + }, + { + 396, + 79, + 0x39, + 0x01B7, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB7, 0xFB, 0x6E, 0xE9, 0x6E, 0xE8, 0x7F, 0x70, 0xFB, 0x6E, 0xE9, 0x6E, 0xE8, 0x70, 0xFB, 0x6E, 0xE9, 0x6E, 0xE8, 0x70, 0xFB, 0x6E, 0xE9, 0x6E, 0xE8, 0x7F, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x65, 0xE8, 0x6B, 0xE9, 0x7F, 0x60, 0xD4, 0x3E, 0xE8, 0x53, 0xE9, 0x7F, 0x51, 0xE7, 0x60, 0xD5, 0x51, 0xE9, 0x3F, 0xE6, 0x7F, 0x70, 0xFB, 0x6E, 0xE9, 0x6E, 0xE8, 0x7F, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x55, 0x4C, 0xA4 + } + }, + { + 397, + 79, + 0x39, + 0x01B8, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB8, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xCC, 0x0E, 0xE9, 0x02, 0x7F, 0x53, 0xE8, 0xA8, 0x5D + } + }, + { + 398, + 79, + 0x39, + 0x01B9, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xB9, 0x55, 0xE9, 0x00, 0x55, 0xE6, 0x03, 0x55, 0xE7, 0x00, 0x55, 0xE1, 0x00, 0x55, 0xE0, 0x00, 0x3C, 0xE7, 0x00, 0xB0, 0x06, 0x3C, 0xE6, 0x00, 0xA0, 0x1A, 0x70, 0xFB, 0x6E, 0xE7, 0x6E, 0xE6, 0xD0, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x04, 0xE1, 0x51, 0xE9, 0x0C, 0xE0, 0x65, 0xE8, 0x6B, 0xE9, 0x8F, 0xDE, 0x5F, 0xE8, 0xE1, 0x5F, 0xE9, 0xE0, 0x62, 0xD0, 0x00, 0x06, 0xE8, 0xCD, 0x0E, 0xE9, 0x80, 0x0E + } + }, + { + 399, + 79, + 0x39, + 0x01BA, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xBA, 0x02, 0x7F, 0x62, 0xD0, 0x01, 0x51, 0xE7, 0x08, 0x51, 0xE6, 0x62, 0xD0, 0x03, 0x53, 0xEC, 0x18, 0x53, 0xED, 0x62, 0xD0, 0x01, 0x51, 0xE5, 0x08, 0x51, 0xE4, 0x62, 0xD0, 0x03, 0x53, 0xEA, 0x18, 0x53, 0xEB, 0x7F, 0x53, 0xE8, 0x52, 0x01, 0x09, 0x00, 0x60, 0xD4, 0x3E, 0xE8, 0x7F, 0x52, 0x00, 0x53, 0xE6, 0x55, 0xE7, 0x00, 0x7F, 0x53, 0xE6, 0x55, 0xE7, 0x00, 0x65, 0xE6, 0x6B, 0xE7, 0x7F, 0x2B, 0x65 + } + }, + { + 400, + 79, + 0x39, + 0x01BB, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xBB, 0x62, 0xD0, 0x01, 0x51, 0xEF, 0x08, 0x51, 0xEE, 0x62, 0xD0, 0x03, 0x53, 0xE8, 0x18, 0x53, 0xE9, 0x62, 0xD0, 0x01, 0x51, 0xED, 0x08, 0x51, 0xEC, 0x62, 0xD0, 0x03, 0x53, 0xE6, 0x18, 0x53, 0xE7, 0x7F, 0x53, 0xE9, 0x3E, 0xE8, 0x53, 0xE8, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0xAF, 0x29, 0x01, 0x53, 0xAF, 0x51, 0xAF, 0x60, 0x04, 0x51, 0xAF, 0x29, 0x08, 0x53, 0xAF, 0x51, 0xAF, 0x60, 0x04, 0x7F, 0x67, 0xDE + } + }, + { + 401, + 79, + 0x39, + 0x01BC, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xBC, 0x02, 0xE8, 0x53, 0xE8, 0x50, 0x00, 0x0A, 0xE9, 0x53, 0xE9, 0x06, 0xE8, 0x62, 0x0E, 0xE9, 0x0F, 0x51, 0xE9, 0x10, 0x58, 0xE8, 0x28, 0x20, 0x7F, 0x52, 0x05, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x7F, 0x53, 0xAF, 0x51, 0xAF, 0x60, 0x04, 0x7F, 0x52, 0x02, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0x99, 0x21, 0x70, 0x54, 0x00, 0x3D, 0x00, 0x00, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0x11, 0x33 + } + }, + { + 402, + 79, + 0x39, + 0x01BD, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xBD, 0x99, 0x21, 0x70, 0x7F, 0x52, 0x04, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x7F, 0x62, 0xD0, 0x00, 0x52, 0x02, 0x53, 0xE8, 0x7F, 0x0E, 0xE9, 0x03, 0x51, 0xE9, 0x60, 0xD5, 0x7F, 0x51, 0xE9, 0x10, 0x58, 0xE8, 0x28, 0x20, 0x7F, 0x62, 0xD0, 0x00, 0x26, 0xAF, 0xF7, 0x51, 0xAF, 0x60, 0x04, 0x26, 0xAF, 0xFE, 0x51, 0xAF, 0x60, 0x04, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x67, 0xE0 + } + }, + { + 403, + 79, + 0x39, + 0x01BE, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xBE, 0x04, 0x53, 0x9D, 0x18, 0x53, 0x9E, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0xF7, 0x08, 0x51, 0xF6, 0x62, 0xD0, 0x03, 0x53, 0xBC, 0x18, 0x53, 0xBD, 0x62, 0xD0, 0x03, 0x51, 0xF5, 0x08, 0x51, 0xF4, 0x62, 0xD0, 0x03, 0x53, 0xBE, 0x18, 0x53, 0xBF, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0x9F, 0x18, 0x53, 0xA0, 0x7F, 0x62, 0xD0, 0x00, 0x52, 0xFC, 0x53, 0xE8, 0x85, 0x1D + } + }, + { + 404, + 79, + 0x39, + 0x01BF, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xBF, 0x7F, 0x62, 0xD0, 0x00, 0x52, 0x00, 0x3A, 0x0E, 0x7F, 0x56, 0x01, 0x00, 0x56, 0x00, 0x00, 0x7F, 0x51, 0xE9, 0x60, 0xD5, 0x51, 0xE7, 0x3F, 0xE8, 0x7F, 0x51, 0xE9, 0x60, 0xD5, 0x52, 0x00, 0x3F, 0xE8, 0x7F, 0x0E, 0xE9, 0x02, 0x51, 0xE9, 0x60, 0xD5, 0x7F, 0x62, 0xD0, 0x03, 0x52, 0x01, 0x53, 0xE5, 0x52, 0x00, 0x53, 0xE4, 0x7F, 0x52, 0x02, 0x53, 0xE8, 0x52, 0x01, 0x60, 0xD4, 0x3E, 0xE8, 0x95, 0x3E + } + }, + { + 405, + 79, + 0x39, + 0x01C0, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC0, 0x7F, 0x70, 0xFB, 0x6F, 0x01, 0x6F, 0x02, 0x70, 0xFB, 0x6F, 0x01, 0x6F, 0x02, 0x7F, 0x62, 0xD0, 0x00, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x7F, 0x65, 0xE8, 0x6B, 0xE9, 0x51, 0xE8, 0x7F, 0x65, 0xE6, 0x6B, 0xE7, 0x65, 0xE6, 0x6B, 0xE7, 0x7F, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xD4, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0x7F, 0x53, 0xE9, 0x62, 0xD0, 0x04, 0x51, 0xD9, 0x62, 0xD0, 0x00, 0x3A, 0xE9, 0x2E, 0x71 + } + }, + { + 406, + 79, + 0x39, + 0x01C1, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC1, 0x7F, 0x62, 0xD0, 0x03, 0x55, 0xEF, 0x00, 0x55, 0xEE, 0x00, 0x7F, 0x56, 0x01, 0x00, 0x80, 0x03, 0x77, 0x01, 0x3D, 0x01, 0x0A, 0xCF, 0xFA, 0x62, 0xD0, 0x00, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xCF, 0x01, 0x7F, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x08, 0x57, 0x89, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x28, 0x53, 0xE8, 0x20, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xAA, 0x62, 0xD0, 0x00, 0x12, 0xA0, 0x56 + } + }, + { + 407, + 79, + 0x39, + 0x01C2, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC2, 0xE8, 0x62, 0xD0, 0x04, 0x51, 0xA9, 0x62, 0xD0, 0x00, 0x1A, 0xE9, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0xDF, 0x62, 0xD0, 0x04, 0x12, 0xB2, 0x62, 0xD0, 0x03, 0x51, 0xDE, 0x62, 0xD0, 0x04, 0x1A, 0xB1, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0xE9, 0x62, 0xD0, 0x03, 0x7F, 0x70, 0xFE, 0x62, 0xD0, 0x03, 0x51, 0xD9, 0x08, 0x51, 0xD8, 0x62, 0xD0, 0x00, 0x53, 0x0A, 0x18, 0x53, 0x0B, 0x71, 0x01, 0x7F, 0x53, 0x76, 0x03 + } + }, + { + 408, + 79, + 0x39, + 0x01C3, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC3, 0xE6, 0x55, 0xE7, 0x00, 0x51, 0xE8, 0x12, 0xE6, 0x51, 0xE9, 0x1A, 0xE7, 0x7F, 0x60, 0xD4, 0x3E, 0xE8, 0x54, 0x03, 0x7F, 0x70, 0xFB, 0x6F, 0x01, 0x6F, 0x02, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0x0E, 0x13, 0xFC, 0x62, 0xD0, 0x00, 0x54, 0x02, 0x7F, 0x70, 0xFE, 0x62, 0xD0, 0x00, 0x51, 0x0D, 0x7F, 0x52, 0xFB, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x7F, 0x5D, 0xC8, 0x62, 0xD0, 0x00, 0x39, 0x00, 0x7F, 0x80, 0x18 + } + }, + { + 409, + 79, + 0x39, + 0x01C4, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC4, 0x52, 0x03, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x7F, 0x0E, 0xE9, 0x01, 0x51, 0xE9, 0x60, 0xD5, 0x7F, 0x71, 0x10, 0x5D, 0xE0, 0x54, 0x01, 0x41, 0xE0, 0xE7, 0x43, 0xE0, 0x18, 0x70, 0xCF, 0x62, 0xE3, 0x38, 0x7F, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0xAD, 0x18, 0x53, 0xAE, 0x7F, 0x62, 0xD0, 0x00, 0x52, 0x01, 0x53, 0xE8, 0x52, 0x00, 0x53, 0xE9, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xA8, 0x08, 0xD8, 0xC9 + } + }, + { + 410, + 79, + 0x39, + 0x01C5, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC5, 0x51, 0xA7, 0x62, 0xD0, 0x03, 0x53, 0xEE, 0x18, 0x53, 0xEF, 0x7F, 0x52, 0x05, 0x54, 0x03, 0x52, 0x02, 0x54, 0x04, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0x0D, 0x08, 0x51, 0x0C, 0x62, 0xD0, 0x03, 0x53, 0xD4, 0x18, 0x53, 0xD5, 0x7F, 0x5D, 0xD6, 0x53, 0xE9, 0x2E, 0xE9, 0xFE, 0x51, 0xE9, 0x54, 0x02, 0x43, 0xD6, 0x01, 0x52, 0xFC, 0x7F, 0x53, 0xE8, 0x52, 0xFB, 0x09, 0x00, 0x60, 0xD5, 0x7F, 0x62, 0xD2, 0xBE + } + }, + { + 411, + 79, + 0x39, + 0x01C6, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC6, 0xD0, 0x00, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x7F, 0x62, 0xD0, 0x04, 0x53, 0xCE, 0x62, 0xD0, 0x04, 0x51, 0xE0, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xE1, 0x62, 0xD0, 0x00, 0x3A, 0x0E, 0x7F, 0x62, 0xD0, 0x00, 0x52, 0x06, 0x53, 0xE8, 0x55, 0xE9, 0x00, 0x7F, 0x52, 0x06, 0x53, 0xE6, 0x55, 0xE7, 0x00, 0x7F, 0x62, 0xD0, 0x04, 0x52, 0x00, 0x3A, 0xE1, 0x7F, 0x52, 0x02, 0x03, 0x02, 0x54, 0x00, 0x07, 0x01, 0x1D + } + }, + { + 412, + 79, + 0x39, + 0x01C7, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC7, 0x00, 0x2E, 0x7F, 0x51, 0xE8, 0x08, 0x51, 0xE9, 0x62, 0xD0, 0x04, 0x53, 0xA1, 0x18, 0x53, 0xA2, 0x7F, 0x12, 0xE8, 0x50, 0x00, 0x1A, 0xE9, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0x9D, 0x62, 0xD0, 0x00, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xE1, 0x13, 0x01, 0x62, 0xD0, 0x00, 0x54, 0x00, 0x7F, 0x55, 0xDC, 0x00, 0x62, 0xD0, 0x04, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x54, 0x0A, 0x51, 0xE9, 0x54, 0x09, 0x45, 0xA6 + } + }, + { + 413, + 79, + 0x39, + 0x01C8, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC8, 0x7F, 0x08, 0x57, 0x98, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x28, 0x7F, 0x08, 0x57, 0x91, 0x28, 0x53, 0xE9, 0x18, 0x75, 0x09, 0x00, 0x28, 0x7F, 0x62, 0xD0, 0x00, 0x55, 0x0B, 0x01, 0x55, 0x0A, 0x00, 0x71, 0x01, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0xE8, 0x54, 0x01, 0x51, 0xE9, 0x54, 0x00, 0x7F, 0x52, 0x04, 0x53, 0xE6, 0x55, 0xE7, 0x00, 0x7F, 0x50, 0x0D, 0x10, 0x57, 0x88, 0x28, 0x20, 0x36, 0x89 + } + }, + { + 414, + 79, + 0x39, + 0x01C9, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xC9, 0x7F, 0x62, 0xD0, 0x04, 0x52, 0x00, 0x3A, 0xC0, 0x7F, 0x62, 0xD0, 0x00, 0x53, 0xE9, 0x51, 0xE8, 0x7F, 0x51, 0xAF, 0x60, 0x04, 0x62, 0xD0, 0x00, 0x7F, 0x52, 0xFC, 0x62, 0xD0, 0x04, 0x53, 0xD7, 0x56, 0xFC, 0x00, 0x7F, 0x62, 0xD0, 0x00, 0x50, 0x0D, 0x10, 0x57, 0x87, 0x28, 0x20, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xA8, 0x62, 0xD0, 0x03, 0x12, 0xEF, 0x7F, 0x62, 0xD0, 0x04, 0x51, 0xA7, 0x62, 0xF2, 0x02 + } + }, + { + 415, + 79, + 0x39, + 0x01CA, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xCA, 0xD0, 0x03, 0x1A, 0xEE, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xD1, 0x00, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xD6, 0x00, 0x7F, 0x62, 0xD0, 0x03, 0x47, 0x99, 0x70, 0x7F, 0x50, 0x00, 0x3F, 0xE8, 0x3F, 0xE8, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xB2, 0xFF, 0x55, 0xB1, 0xFF, 0x7F, 0x56, 0x01, 0x00, 0x56, 0x02, 0x00, 0x7F, 0x71, 0x10, 0x60, 0xE0, 0x70, 0xCF, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0xF6, 0x62, 0xD0, 0x51, 0xC1 + } + }, + { + 416, + 79, + 0x39, + 0x01CB, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xCB, 0x03, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0xF7, 0x62, 0xD0, 0x03, 0x7F, 0x71, 0x10, 0x43, 0xD7, 0x20, 0x43, 0xE0, 0x40, 0x7F, 0x52, 0xFA, 0x13, 0xF6, 0x52, 0xF9, 0x1B, 0xF5, 0x7F, 0x62, 0xD0, 0x00, 0x51, 0x0E, 0x62, 0xD0, 0x04, 0x7F, 0x3F, 0xE8, 0x62, 0xD0, 0x04, 0x51, 0xB6, 0x7F, 0x50, 0x0D, 0x10, 0x57, 0x86, 0x28, 0x20, 0x7F, 0x50, 0x0D, 0x10, 0x57, 0x9A, 0x28, 0x20, 0x7F, 0x62, 0xD0, 0x10, 0x40 + } + }, + { + 417, + 79, + 0x39, + 0x01CC, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xCC, 0x00, 0x52, 0x00, 0x13, 0x01, 0x7F, 0x62, 0xD0, 0x00, 0x52, 0x01, 0x13, 0x00, 0x7F, 0x50, 0x0D, 0x10, 0x57, 0x9B, 0x28, 0x20, 0x7F, 0x60, 0x0C, 0x62, 0xD0, 0x00, 0x51, 0xB2, 0x7F, 0x60, 0x08, 0x62, 0xD0, 0x00, 0x51, 0xB1, 0x7F, 0x62, 0xD0, 0x03, 0x51, 0x9B, 0x21, 0x0F, 0x7F, 0x62, 0xD0, 0x03, 0x47, 0x99, 0x04, 0x7F, 0x62, 0xD0, 0x04, 0x3C, 0xCD, 0x02, 0x7F, 0x06, 0xE8, 0x01, 0x0E, 0x82, 0x25 + } + }, + { + 418, + 79, + 0x39, + 0x01CD, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xCD, 0xE9, 0x00, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xCC, 0x00, 0x7F, 0x13, 0x03, 0x51, 0xE9, 0x1B, 0x02, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xD7, 0x00, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xD9, 0x00, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xD5, 0x00, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xDD, 0x01, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xCB, 0xF4, 0x7F, 0x62, 0xD0, 0x04, 0x55, 0xB4, 0x00, 0x7F, 0x41, 0xD7, 0xDF, 0x41, 0xE0, 0x54, 0xCA + } + }, + { + 419, + 79, + 0x39, + 0x01CE, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xCE, 0xBF, 0x7F, 0x41, 0xE0, 0xEF, 0x62, 0xDA, 0xEF, 0x7F, 0x41, 0xE0, 0x7F, 0x62, 0xDA, 0x7F, 0x7F, 0x62, 0xD0, 0x00, 0x3C, 0xBA, 0x00, 0x7F, 0x00, 0xBF, 0x00, 0x20, 0x00, 0xEA, 0x00, 0x06, 0x01, 0x00, 0x00, 0xA0, 0x02, 0xB1, 0x00, 0x4F, 0x03, 0x99, 0x00, 0x47, 0x03, 0xE0, 0x01, 0x0D, 0x03, 0xE1, 0x00, 0x1F, 0x04, 0x99, 0x00, 0x49, 0x04, 0xE2, 0x02, 0x01, 0x00, 0xFF, 0x00, 0x30, 0x30, 0xF0, 0x03 + } + }, + { + 420, + 79, + 0x39, + 0x01CF, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xCF, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x24 + } + }, + { + 421, + 79, + 0x39, + 0x01D0, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x25 + } + }, + { + 422, + 79, + 0x39, + 0x01D1, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD1, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x26 + } + }, + { + 423, + 79, + 0x39, + 0x01D2, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD2, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x27 + } + }, + { + 424, + 79, + 0x39, + 0x01D3, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD3, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x28 + } + }, + { + 425, + 79, + 0x39, + 0x01D4, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD4, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x29 + } + }, + { + 426, + 79, + 0x39, + 0x01D5, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD5, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x2A + } + }, + { + 427, + 79, + 0x39, + 0x01D6, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD6, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x2B + } + }, + { + 428, + 79, + 0x39, + 0x01D7, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD7, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x2C + } + }, + { + 429, + 79, + 0x39, + 0x01D8, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD8, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x2D + } + }, + { + 430, + 79, + 0x39, + 0x01D9, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xD9, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x2E + } + }, + { + 431, + 79, + 0x39, + 0x01DA, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xDA, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x2F + } + }, + { + 432, + 79, + 0x39, + 0x01DB, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xDB, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30 + } + }, + { + 433, + 79, + 0x39, + 0x01DC, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xDC, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x31 + } + }, + { + 434, + 79, + 0x39, + 0x01DD, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xDD, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x32 + } + }, + { + 435, + 79, + 0x39, + 0x01DE, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xDE, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x33 + } + }, + { + 436, + 79, + 0x39, + 0x01DF, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xDF, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x34 + } + }, + { + 437, + 79, + 0x39, + 0x01E0, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x35 + } + }, + { + 438, + 79, + 0x39, + 0x01E1, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE1, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x36 + } + }, + { + 439, + 79, + 0x39, + 0x01E2, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE2, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x37 + } + }, + { + 440, + 79, + 0x39, + 0x01E3, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE3, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x38 + } + }, + { + 441, + 79, + 0x39, + 0x01E4, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE4, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x39 + } + }, + { + 442, + 79, + 0x39, + 0x01E5, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE5, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x3A + } + }, + { + 443, + 79, + 0x39, + 0x01E6, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE6, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x3B + } + }, + { + 444, + 79, + 0x39, + 0x01E7, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE7, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x3C + } + }, + { + 445, + 79, + 0x39, + 0x01E8, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE8, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x3D + } + }, + { + 446, + 79, + 0x39, + 0x01E9, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xE9, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x3E + } + }, + { + 447, + 79, + 0x39, + 0x01EA, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xEA, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x3F + } + }, + { + 448, + 79, + 0x39, + 0x01EB, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xEB, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x40 + } + }, + { + 449, + 79, + 0x39, + 0x01EC, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xEC, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x41 + } + }, + { + 450, + 79, + 0x39, + 0x01ED, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xED, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x42 + } + }, + { + 451, + 79, + 0x39, + 0x01EE, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xEE, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x43 + } + }, + { + 452, + 79, + 0x39, + 0x01EF, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xEF, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x44 + } + }, + { + 453, + 79, + 0x39, + 0x01F0, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x45 + } + }, + { + 454, + 79, + 0x39, + 0x01F1, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF1, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x46 + } + }, + { + 455, + 79, + 0x39, + 0x01F2, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF2, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x47 + } + }, + { + 456, + 79, + 0x39, + 0x01F3, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF3, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x48 + } + }, + { + 457, + 79, + 0x39, + 0x01F4, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF4, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x49 + } + }, + { + 458, + 79, + 0x39, + 0x01F5, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF5, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x4A + } + }, + { + 459, + 79, + 0x39, + 0x01F6, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF6, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x4B + } + }, + { + 460, + 79, + 0x39, + 0x01F7, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF7, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x4C + } + }, + { + 461, + 79, + 0x39, + 0x01F8, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF8, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x4D + } + }, + { + 462, + 79, + 0x39, + 0x01F9, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xF9, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x4E + } + }, + { + 463, + 79, + 0x39, + 0x01FA, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xFA, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x4F + } + }, + { + 464, + 79, + 0x39, + 0x01FB, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xFB, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x50 + } + }, + { + 465, + 79, + 0x39, + 0x01FC, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xFC, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x51 + } + }, + { + 466, + 79, + 0x39, + 0x01FD, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xFD, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x52 + } + }, + { + 467, + 79, + 0x39, + 0x01FE, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xFE, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x53 + } + }, + { + 468, + 79, + 0x39, + 0x01FF, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x01, 0xFF, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x54 + } + }, + { + 469, + 79, + 0x39, + 0x001E, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x1E, 0x19, 0xE5, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x01, 0x0B, 0x10, 0x12, 0xA0, 0x02, 0x04, 0x00, 0xC0, 0xC1, 0xC2, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xA5, 0xBC + } + }, + { + 470, + 79, + 0x39, + 0x001F, + { + 0x00, 0xFF, 0x39, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x1F, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xA0, 0x07, 0x5F, 0xF8, 0x3E, 0xEF + } + }, + { + 471, + 11, + 0x3B, + -1, + { + 0x00, 0xFF, 0x3B, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 + } + }, +}; + +unsigned short cyttsp_fw_records = 472; + +unsigned char cyttsp_fw_tts_verh = 0x10; +unsigned char cyttsp_fw_tts_verl = 0x12; +unsigned char cyttsp_fw_app_idh = 0xA0; +unsigned char cyttsp_fw_app_idl = 0x02; +unsigned char cyttsp_fw_app_verh = 0x04; +unsigned char cyttsp_fw_app_verl = 0x00; +unsigned char cyttsp_fw_cid_0 = 0xC0; +unsigned char cyttsp_fw_cid_1 = 0xC1; +unsigned char cyttsp_fw_cid_2 = 0xC2; diff --git a/include/linux/cyttsp.h b/include/linux/cyttsp.h new file mode 100644 index 0000000000000..a525a9f81812e --- /dev/null +++ b/include/linux/cyttsp.h @@ -0,0 +1,658 @@ +/* Header file for: + * Cypress TrueTouch(TM) Standard Product touchscreen drivers. + * include/linux/cyttsp.h + * + * Copyright (C) 2009, 2010 Cypress Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Cypress reserves the right to make changes without further notice + * to the materials described herein. Cypress does not assume any + * liability arising out of the application described herein. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + + +#ifndef __CYTTSP_H__ +#define __CYTTSP_H__ + +#include +#include +#include +#include +#include + +#include + +#define CYPRESS_TTSP_NAME "cyttsp" +#define CY_I2C_NAME "cyttsp-i2c" +#define CY_SPI_NAME "cyttsp-spi" + +#ifdef CY_DECLARE_GLOBALS + uint32_t cyttsp_tsdebug; + module_param_named(tsdebug, cyttsp_tsdebug, uint, 0664); + uint32_t cyttsp_tsxdebug; + module_param_named(tsxdebug, cyttsp_tsxdebug, uint, 0664); + + uint32_t cyttsp_disable_touch; + module_param_named(disable_touch, cyttsp_disable_touch, uint, 0664); +#else + extern uint32_t cyttsp_tsdebug; + extern uint32_t cyttsp_tsxdebug; + extern uint32_t cyttsp_disable_touch; +#endif + + + +/****************************************************************************** + * Global Control, Used to control the behavior of the driver + */ + +/* defines for Gen2 (Txx2xx); Gen3 (Txx3xx) + * use these defines to set cyttsp_platform_data.gen in board config file + */ +#define CY_GEN2 2 +#define CY_GEN3 3 + +/* define for using I2C driver + */ +#define CY_USE_I2C_DRIVER + +/* defines for using SPI driver */ +/* +#define CY_USE_SPI_DRIVER + */ +#define CY_SPI_DFLT_SPEED_HZ 1000000 +#define CY_SPI_MAX_SPEED_HZ 4000000 +#define CY_SPI_SPEED_HZ CY_SPI_DFLT_SPEED_HZ +#define CY_SPI_BITS_PER_WORD 8 +#define CY_SPI_DAV 139 /* set correct gpio id */ +#define CY_SPI_BUFSIZE 512 + + +/* define for inclusion of TTSP App Update Load File + * use this define if update to the TTSP Device is desired + */ +/* +#define CY_INCLUDE_LOAD_FILE +*/ + +/* define if force new load file for bootloader load */ +/* +#define CY_FORCE_FW_UPDATE +*/ + +/* undef for production use */ +/* +#define CY_USE_DEBUG +*/ + +/* undef for irq use; use this define in the board configuration file */ +/* +#define CY_USE_TIMER + */ + +/* undef to allow use of extra debug capability */ +/* +#define CY_ALLOW_EXTRA_DEBUG +*/ + +/* undef to remove additional debug prints */ +/* +#define CY_USE_EXTRA_DEBUG +*/ + +/* undef to remove additional debug prints */ +/* +#define CY_USE_EXTRA_DEBUG1 + */ + +/* undef to use operational touch timer jiffies; else use test jiffies */ +/* + */ +#define CY_USE_TIMER_DEBUG + +/* define to use canned test data */ +/* +#define CY_USE_TEST_DATA + */ + +/* define to activate power management */ +/* +#define CY_USE_LOW_POWER + */ + +/* define if wake on i2c addr is activated */ +/* +#define CY_USE_DEEP_SLEEP + */ + +/* define if gesture signaling is used + * and which gesture groups to use + */ +/* +#define CY_USE_GEST +#define CY_USE_GEST_GRP1 +#define CY_USE_GEST_GRP2 +#define CY_USE_GEST_GRP3 +#define CY_USE_GEST_GRP4 + */ +/* Active distance in pixels for a gesture to be reported + * if set to 0, then all gesture movements are reported + */ +#define CY_ACT_DIST_DFLT 8 +#define CY_ACT_DIST CY_ACT_DIST_DFLT + +/* define if MT signals are desired */ +/* +*/ +#define CY_USE_MT_SIGNALS + +/* define if MT tracking id signals are used */ +/* +#define CY_USE_MT_TRACK_ID + */ + +/* define if ST signals are required */ +/* +*/ +#define CY_USE_ST_SIGNALS + +/* define to send handshake to device */ +/* +*/ +#define CY_USE_HNDSHK + +/* define if log all raw motion signals to a sysfs file */ +/* +#define CY_LOG_TO_FILE +*/ + + +/* End of the Global Control section + ****************************************************************************** + */ +#define CY_DIFF(m, n) ((m) != (n)) + +#ifdef CY_LOG_TO_FILE + #define cyttsp_openlog() /* use sysfs */ +#else + #define cyttsp_openlog() +#endif /* CY_LOG_TO_FILE */ + +/* see kernel.h for pr_xxx def'ns */ +#define cyttsp_info(f, a...) pr_info("%s:" f, __func__ , ## a) +#define cyttsp_error(f, a...) pr_err("%s:" f, __func__ , ## a) +#define cyttsp_alert(f, a...) pr_alert("%s:" f, __func__ , ## a) + +#ifdef CY_USE_DEBUG + #define cyttsp_debug(f, a...) pr_alert("%s:" f, __func__ , ## a) +#else + #define cyttsp_debug(f, a...) {if (cyttsp_tsdebug) \ + pr_alert("%s:" f, __func__ , ## a); } +#endif /* CY_USE_DEBUG */ + +#ifdef CY_ALLOW_EXTRA_DEBUG +#ifdef CY_USE_EXTRA_DEBUG + #define cyttsp_xdebug(f, a...) pr_alert("%s:" f, __func__ , ## a) +#else + #define cyttsp_xdebug(f, a...) {if (cyttsp_tsxdebug) \ + pr_alert("%s:" f, __func__ , ## a); } +#endif /* CY_USE_EXTRA_DEBUG */ + +#ifdef CY_USE_EXTRA_DEBUG1 + #define cyttsp_xdebug1(f, a...) pr_alert("%s:" f, __func__ , ## a) +#else + #define cyttsp_xdebug1(f, a...) +#endif /* CY_USE_EXTRA_DEBUG1 */ +#else + #define cyttsp_xdebug(f, a...) + #define cyttsp_xdebug1(f, a...) +#endif /* CY_ALLOW_EXTRA_DEBUG */ + +#ifdef CY_USE_TIMER_DEBUG + #define TOUCHSCREEN_TIMEOUT (msecs_to_jiffies(1000)) +#else + #define TOUCHSCREEN_TIMEOUT (msecs_to_jiffies(28)) +#endif + +/* reduce extra signals in MT only build + * be careful not to lose backward compatibility for pre-MT apps + */ +#ifdef CY_USE_ST_SIGNALS + #define CY_USE_ST 1 +#else + #define CY_USE_ST 0 +#endif /* CY_USE_ST_SIGNALS */ + +/* rely on kernel input.h to define Multi-Touch capability */ +/* if input.h defines the Multi-Touch signals, then use MT */ +#if defined(ABS_MT_TOUCH_MAJOR) && defined(CY_USE_MT_SIGNALS) + #define CY_USE_MT 1 + #define CY_MT_SYNC(input) input_mt_sync(input) +#else + #define CY_USE_MT 0 + #define CY_MT_SYNC(input) + /* the following includes are provided to ensure a compile; + * the code that compiles with these defines will not be executed if + * the CY_USE_MT is properly used in the platform structure init + */ + #ifndef ABS_MT_TOUCH_MAJOR + #define ABS_MT_TOUCH_MAJOR 0x30 /* touching ellipse */ + #define ABS_MT_TOUCH_MINOR 0x31 /* (omit if circular) */ + #define ABS_MT_WIDTH_MAJOR 0x32 /* approaching ellipse */ + #define ABS_MT_WIDTH_MINOR 0x33 /* (omit if circular) */ + #define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ + #define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */ + #define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */ + #define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */ + #define ABS_MT_BLOB_ID 0x38 /* Group set of pkts as blob */ + #endif /* ABS_MT_TOUCH_MAJOR */ +#endif /* ABS_MT_TOUCH_MAJOR and CY_USE_MT_SIGNALS */ +#if defined(ABS_MT_TRACKING_ID) && defined(CY_USE_MT_TRACK_ID) + #define CY_USE_TRACKING_ID 1 +#else + #define CY_USE_TRACKING_ID 0 +/* define only if not defined already by system; + * value based on linux kernel 2.6.30.10 + */ +#ifndef ABS_MT_TRACKING_ID + #define ABS_MT_TRACKING_ID (ABS_MT_BLOB_ID+1) +#endif +#endif /* ABS_MT_TRACKING_ID */ + +#ifdef CY_USE_DEEP_SLEEP + #define CY_USE_DEEP_SLEEP_SEL 0x80 +#else + #define CY_USE_DEEP_SLEEP_SEL 0x00 +#endif +#ifdef CY_USE_LOW_POWER + #define CY_USE_SLEEP (CY_USE_DEEP_SLEEP_SEL | 0x01) +#else + #define CY_USE_SLEEP 0x00 +#endif /* CY_USE_LOW_POWER */ + +#ifdef CY_USE_TEST_DATA + #define cyttsp_testdat(ray1, ray2, sizeofray) \ + { \ + int i; \ + u8 *up1 = (u8 *)ray1; \ + u8 *up2 = (u8 *)ray2; \ + for (i = 0; i < sizeofray; i++) { \ + up1[i] = up2[i]; \ + } \ + } +#else + #define cyttsp_testdat(xy, test_xy, sizeofray) +#endif /* CY_USE_TEST_DATA */ + +/* helper macros */ +#define GET_NUM_TOUCHES(x) ((x) & 0x0F) +#define GET_TOUCH1_ID(x) (((x) & 0xF0) >> 4) +#define GET_TOUCH2_ID(x) ((x) & 0x0F) +#define GET_TOUCH3_ID(x) (((x) & 0xF0) >> 4) +#define GET_TOUCH4_ID(x) ((x) & 0x0F) +#define IS_LARGE_AREA(x) (((x) & 0x10) >> 4) +#define FLIP_DATA_FLAG 0x01 +#define REVERSE_X_FLAG 0x02 +#define REVERSE_Y_FLAG 0x04 +#define FLIP_DATA(flags) ((flags) & FLIP_DATA_FLAG) +#define REVERSE_X(flags) ((flags) & REVERSE_X_FLAG) +#define REVERSE_Y(flags) ((flags) & REVERSE_Y_FLAG) +#define FLIP_XY(x, y) { \ + u16 tmp; \ + tmp = (x); \ + (x) = (y); \ + (y) = tmp; \ + } +#define INVERT_X(x, xmax) ((xmax) - (x)) +#define INVERT_Y(y, maxy) ((maxy) - (y)) +#define SET_HSTMODE(reg, mode) ((reg) & (mode)) +#define GET_HSTMODE(reg) ((reg & 0x70) >> 4) +#define GET_BOOTLOADERMODE(reg) ((reg & 0x10) >> 4) + +/* constant definitions */ +/* maximum number of concurrent ST track IDs */ +#define CY_NUM_ST_TCH_ID 2 + +/* maximum number of concurrent MT track IDs */ +#define CY_NUM_MT_TCH_ID 4 + +/* maximum number of track IDs */ +#define CY_NUM_TRK_ID 16 + +#define CY_NTCH 0 /* no touch (lift off) */ +#define CY_TCH 1 /* active touch (touchdown) */ +#define CY_ST_FNGR1_IDX 0 +#define CY_ST_FNGR2_IDX 1 +#define CY_MT_TCH1_IDX 0 +#define CY_MT_TCH2_IDX 1 +#define CY_MT_TCH3_IDX 2 +#define CY_MT_TCH4_IDX 3 +#define CY_XPOS 0 +#define CY_YPOS 1 +#define CY_IGNR_TCH (-1) +#define CY_SMALL_TOOL_WIDTH 10 +#define CY_LARGE_TOOL_WIDTH 255 +#define CY_REG_BASE 0x00 +#define CY_REG_GEST_SET 0x1E +#define CY_REG_ACT_INTRVL 0x1D +#define CY_REG_TCH_TMOUT (CY_REG_ACT_INTRVL+1) +#define CY_REG_LP_INTRVL (CY_REG_TCH_TMOUT+1) +#define CY_SOFT_RESET ((1 << 0)) +#define CY_DEEP_SLEEP ((1 << 1)) +#define CY_LOW_POWER ((1 << 2)) +#define CY_MAXZ 255 +#define CY_OK 0 +#define CY_INIT 1 +#define CY_DLY_DFLT 10 /* ms */ +#define CY_DLY_SYSINFO 20 /* ms */ +#define CY_DLY_BL 300 +#define CY_DLY_DNLOAD 100 /* ms */ +#define CY_NUM_RETRY 4 /* max num touch data read */ + +/* handshake bit in the hst_mode reg */ +#define CY_HNDSHK_BIT 0x80 +#ifdef CY_USE_HNDSHK + #define CY_SEND_HNDSHK 1 +#else + #define CY_SEND_HNDSHK 0 +#endif + +/* Bootloader File 0 offset */ +#define CY_BL_FILE0 0x00 + +/* Bootloader command directive */ +#define CY_BL_CMD 0xFF + +/* Bootloader Initiate Bootload */ +#define CY_BL_INIT_LOAD 0x38 + +/* Bootloader Write a Block */ +#define CY_BL_WRITE_BLK 0x39 + +/* Bootloader Terminate Bootload */ +#define CY_BL_TERMINATE 0x3B + +/* Bootloader Exit and Verify Checksum command */ +#define CY_BL_EXIT 0xA5 + +/* Bootloader default keys */ +#define CY_BL_KEY0 0x00 +#define CY_BL_KEY1 0x01 +#define CY_BL_KEY2 0x02 +#define CY_BL_KEY3 0x03 +#define CY_BL_KEY4 0x04 +#define CY_BL_KEY5 0x05 +#define CY_BL_KEY6 0x06 +#define CY_BL_KEY7 0x07 + +/* Active Power state scanning/processing refresh interval */ +#define CY_ACT_INTRVL_DFLT 0x00 + +/* touch timeout for the Active power */ +#define CY_TCH_TMOUT_DFLT 0xFF + +/* Low Power state scanning/processing refresh interval */ +#define CY_LP_INTRVL_DFLT 0x0A + +#define CY_IDLE_STATE 0 +#define CY_ACTIVE_STATE 1 +#define CY_LOW_PWR_STATE 2 +#define CY_SLEEP_STATE 3 + +/* device mode bits */ +#define CY_OP_MODE 0x00 +#define CY_SYSINFO_MODE 0x10 + +/* power mode select bits */ +#define CY_SOFT_RESET_MODE 0x01 /* return to Bootloader mode */ +#define CY_DEEP_SLEEP_MODE 0x02 +#define CY_LOW_PWR_MODE 0x04 + +#define CY_NUM_KEY 8 + +#ifdef CY_USE_GEST + #define CY_USE_GESTURES 1 +#else + #define CY_USE_GESTURES 0 +#endif /* CY_USE_GESTURE_SIGNALS */ + +#ifdef CY_USE_GEST_GRP1 + #define CY_GEST_GRP1 0x10 +#else + #define CY_GEST_GRP1 0x00 +#endif /* CY_USE_GEST_GRP1 */ +#ifdef CY_USE_GEST_GRP2 + #define CY_GEST_GRP2 0x20 +#else + #define CY_GEST_GRP2 0x00 +#endif /* CY_USE_GEST_GRP2 */ +#ifdef CY_USE_GEST_GRP3 + #define CY_GEST_GRP3 0x40 +#else + #define CY_GEST_GRP3 0x00 +#endif /* CY_USE_GEST_GRP3 */ +#ifdef CY_USE_GEST_GRP4 + #define CY_GEST_GRP4 0x80 +#else + #define CY_GEST_GRP4 0x00 +#endif /* CY_USE_GEST_GRP4 */ + + +struct cyttsp_platform_data { + u32 panel_maxx; + u32 panel_maxy; + u32 disp_resx; + u32 disp_resy; + u32 disp_minx; + u32 disp_miny; + u32 disp_maxx; + u32 disp_maxy; + u32 flags; + u8 gen; + u8 use_st; + u8 use_mt; + u8 use_hndshk; + u8 use_trk_id; + u8 use_sleep; + u8 use_gestures; + u8 gest_set; + u8 act_intrvl; + u8 tch_tmout; + u8 lp_intrvl; + u8 power_state; + bool wakeup; +#ifdef CY_USE_I2C_DRIVER + s32 (*init)(struct i2c_client *client); + s32 (*resume)(struct i2c_client *client); +#endif +#ifdef CY_USE_SPI_DRIVER + s32 (*init)(struct spi_device *spi); + s32 (*resume)(struct spi_device *spi); +#endif +}; + +/* TrueTouch Standard Product Gen3 (Txx3xx) interface definition */ +struct cyttsp_gen3_xydata_t { + u8 hst_mode; + u8 tt_mode; + u8 tt_stat; + u16 x1 __attribute__ ((packed)); + u16 y1 __attribute__ ((packed)); + u8 z1; + u8 touch12_id; + u16 x2 __attribute__ ((packed)); + u16 y2 __attribute__ ((packed)); + u8 z2; + u8 gest_cnt; + u8 gest_id; + u16 x3 __attribute__ ((packed)); + u16 y3 __attribute__ ((packed)); + u8 z3; + u8 touch34_id; + u16 x4 __attribute__ ((packed)); + u16 y4 __attribute__ ((packed)); + u8 z4; + u8 tt_undef[3]; + u8 gest_set; + u8 tt_reserved; +}; + +/* TrueTouch Standard Product Gen2 (Txx2xx) interface definition */ +#define CY_GEN2_NOTOUCH 0x03 /* Both touches removed */ +#define CY_GEN2_GHOST 0x02 /* ghost */ +#define CY_GEN2_2TOUCH 0x03 /* 2 touch; no ghost */ +#define CY_GEN2_1TOUCH 0x01 /* 1 touch only */ +#define CY_GEN2_TOUCH2 0x01 /* 1st touch removed; + * 2nd touch remains */ +struct cyttsp_gen2_xydata_t { + u8 hst_mode; + u8 tt_mode; + u8 tt_stat; + u16 x1 __attribute__ ((packed)); + u16 y1 __attribute__ ((packed)); + u8 z1; + u8 evnt_idx; + u16 x2 __attribute__ ((packed)); + u16 y2 __attribute__ ((packed)); + u8 tt_undef1; + u8 gest_cnt; + u8 gest_id; + u8 tt_undef[14]; + u8 gest_set; + u8 tt_reserved; +}; + +/* TTSP System Information interface definition */ +struct cyttsp_sysinfo_data_t { + u8 hst_mode; + u8 mfg_cmd; + u8 mfg_stat; + u8 cid[3]; + u8 tt_undef1; + u8 uid[8]; + u8 bl_verh; + u8 bl_verl; + u8 tts_verh; + u8 tts_verl; + u8 app_idh; + u8 app_idl; + u8 app_verh; + u8 app_verl; + u8 tt_undef[6]; + u8 act_intrvl; + u8 tch_tmout; + u8 lp_intrvl; +}; + +/* TTSP Bootloader Register Map interface definition */ +#define CY_BL_CHKSUM_OK 0x01 +struct cyttsp_bootloader_data_t { + u8 bl_file; + u8 bl_status; + u8 bl_error; + u8 blver_hi; + u8 blver_lo; + u8 bld_blver_hi; + u8 bld_blver_lo; + u8 ttspver_hi; + u8 ttspver_lo; + u8 appid_hi; + u8 appid_lo; + u8 appver_hi; + u8 appver_lo; + u8 cid_0; + u8 cid_1; + u8 cid_2; +}; + +#define cyttsp_wake_data_t cyttsp_gen3_xydata_t +#ifdef CY_DECLARE_GLOBALS + #ifdef CY_INCLUDE_LOAD_FILE + /* this file declares: + * firmware download block array (cyttsp_fw[]), + * the number of command block records (cyttsp_fw_records), + * and the version variables + */ + #include "cyttsp_fw.h" /* imports cyttsp_fw[] array */ + #define cyttsp_app_load() 1 + #ifdef CY_FORCE_FW_UPDATE + #define cyttsp_force_fw_load() 1 + #else + #define cyttsp_force_fw_load() 0 + #endif + + #else + /* the following declarations are to allow + * some debugging capability + */ + unsigned char cyttsp_fw_tts_verh = 0x00; + unsigned char cyttsp_fw_tts_verl = 0x01; + unsigned char cyttsp_fw_app_idh = 0x02; + unsigned char cyttsp_fw_app_idl = 0x03; + unsigned char cyttsp_fw_app_verh = 0x04; + unsigned char cyttsp_fw_app_verl = 0x05; + unsigned char cyttsp_fw_cid_0 = 0x06; + unsigned char cyttsp_fw_cid_1 = 0x07; + unsigned char cyttsp_fw_cid_2 = 0x08; + #define cyttsp_app_load() 0 + #define cyttsp_force_fw_load() 0 + #endif + #define cyttsp_tts_verh() cyttsp_fw_tts_verh + #define cyttsp_tts_verl() cyttsp_fw_tts_verl + #define cyttsp_app_idh() cyttsp_fw_app_idh + #define cyttsp_app_idl() cyttsp_fw_app_idl + #define cyttsp_app_verh() cyttsp_fw_app_verh + #define cyttsp_app_verl() cyttsp_fw_app_verl + #define cyttsp_cid_0() cyttsp_fw_cid_0 + #define cyttsp_cid_1() cyttsp_fw_cid_1 + #define cyttsp_cid_2() cyttsp_fw_cid_2 + #ifdef CY_USE_TEST_DATA + static struct cyttsp_gen2_xydata_t tt_gen2_testray[] = { + {0x00}, {0x00}, {0x04}, + {0x4000}, {0x8000}, {0x80}, + {0x03}, + {0x2000}, {0x1000}, {0x00}, + {0x00}, + {0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00}, + {0x00} + }; + + static struct cyttsp_gen3_xydata_t tt_gen3_testray[] = { + {0x00}, {0x00}, {0x04}, + {0x4000}, {0x8000}, {0x80}, + {0x12}, + {0x2000}, {0x1000}, {0xA0}, + {0x00}, {0x00}, + {0x8000}, {0x4000}, {0xB0}, + {0x34}, + {0x4000}, {0x1000}, {0xC0}, + {0x00, 0x00, 0x00}, + {0x00}, + {0x00} + }; + #endif /* CY_USE_TEST_DATA */ + +#else + extern u8 g_appload_ray[]; +#endif + +#endif /* __CYTTSP_H__ */ From 711b8afefb516f1ed9547414871ed59740f15ddc Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 25 Jan 2011 12:45:21 -0800 Subject: [PATCH 0802/2556] input: cyttsp-i2c: unregister input device before freeing it on error Change-Id: Ide02efd37e8f87a4be58bf5aba5d029a27d48ac2 Signed-off-by: Dima Zavin --- drivers/input/touchscreen/cyttsp-i2c.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/cyttsp-i2c.c b/drivers/input/touchscreen/cyttsp-i2c.c index dd59089d25daf..b5908e4cd9f5a 100644 --- a/drivers/input/touchscreen/cyttsp-i2c.c +++ b/drivers/input/touchscreen/cyttsp-i2c.c @@ -1801,7 +1801,7 @@ static int cyttsp_initialize(struct i2c_client *client, struct cyttsp *ts) retval = cyttsp_power_on(ts); if (retval < 0) - goto error_free_device; + goto error_unreg_device; /* Timer or Interrupt setup */ if (ts->client->irq == 0) { @@ -1858,7 +1858,8 @@ static int cyttsp_initialize(struct i2c_client *client, struct cyttsp *ts) error_free_irq: cyttsp_alert("Error: Failed to register IRQ handler\n"); free_irq(client->irq, ts); - +error_unreg_device: + input_unregister_device(input_device); error_free_device: if (input_device) input_free_device(input_device); From 988de6c4771fb562a85b8712386c4168e9147950 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Mon, 24 Jan 2011 21:27:51 -0800 Subject: [PATCH 0803/2556] ARM: msm: board-msm7x30: add i2c-0 bus device Change-Id: I19a4abc0fc83f2a3e6ec609d298a5aad216852dd Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index cf720a42f4e26..fb4ffa84e46ad 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -295,6 +295,7 @@ static struct platform_device *devices[] __initdata = { #endif &msm_device_smd, &msm_device_nand, + &msm_device_i2c, &msm_device_i2c2, &msm_device_hsusb, &usb_mass_storage_device, @@ -535,6 +536,20 @@ static struct i2c_board_info surf_i2c_devices[] = { }, }; +static int msm7x30_i2c_0_init(void) +{ + msm_gpiomux_write(70, 0, + GPIOMUX_FUNC_1 | + GPIOMUX_PULL_NONE | + GPIOMUX_DIR_INPUT | + GPIOMUX_DRV_16MA | GPIOMUX_VALID); + msm_gpiomux_write(71, 0, + GPIOMUX_FUNC_1 | + GPIOMUX_PULL_NONE | + GPIOMUX_DIR_INPUT | + GPIOMUX_DRV_16MA | GPIOMUX_VALID); +} + static int msm7x30_spi_init(void) { msm_gpiomux_write(45, 0, @@ -592,6 +607,7 @@ static void __init msm7x30_init(void) msm7x30_pmic_keypad_pdata.keymap = msm7x30_surf_pmic_keymap; msm7x30_ssbi_pmic_init(); + msm7x30_i2c_0_init(); msm7x30_spi_init(); msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; From 1ed2a65ed601e806beff760fbbbd4f43659c26b4 Mon Sep 17 00:00:00 2001 From: Anirudh Ghayal Date: Mon, 24 Jan 2011 21:28:43 -0800 Subject: [PATCH 0804/2556] ARM: msm: board-msm7x30: add cypress ttsp touch device Change-Id: I101119b5a5172716878edfd7f72777aa24004586 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 93 +++++++++++++++++++++++++++++++ arch/arm/mach-msm/board-msm7x30.h | 1 + 2 files changed, 94 insertions(+) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index fb4ffa84e46ad..3c06734181ad1 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -529,6 +530,93 @@ static int __init msm7x30_ssbi_pmic_init(void) return platform_device_register(&msm_device_ssbi_pmic); } +static struct vreg *vreg_l15; /* gp6 */ +static struct vreg *vreg_l8; /* gp7 */ +static struct vreg *vreg_l16; /* gp10 */ + +static struct vreg *_get_vreg(char *name, u32 mv, bool en) +{ + struct vreg *vr; + + vr = vreg_get(NULL, name); + if (IS_ERR(vr)) + return NULL; + if (mv != 0) + vreg_set_level(vr, mv); + if (en) + vreg_enable(vr); + return vr; +} + +static void _put_vreg(struct vreg *vr) +{ + if (vr) { + vreg_disable(vr); + vreg_put(vr); + } +} + +static void fluid_cyttsp_init(void) +{ + vreg_l8 = _get_vreg("gp7", 1800, true); + vreg_l16 = _get_vreg("gp10", 2600, true); + vreg_l15 = _get_vreg("gp6", 3050, true); + + if (!vreg_l8 || !vreg_l16 || !vreg_l15) { + pr_err("%s: can't get vregs\n", __func__); + _put_vreg(vreg_l8); + _put_vreg(vreg_l15); + _put_vreg(vreg_l16); + return; + } + + /* enable interrupt gpio */ + msm_gpiomux_write(MSM7X30_FLUID_GPIO_TOUCH_INT_N, 0, + GPIOMUX_FUNC_GPIO | + GPIOMUX_PULL_UP | + GPIOMUX_DIR_INPUT | + GPIOMUX_DRV_6MA | GPIOMUX_VALID); +} + +static int fluid_cyttsp_resume(struct i2c_client *client) +{ + mdelay(10); + return CY_OK; +} + +static struct cyttsp_platform_data fluid_cyttsp_pdata = { + .panel_maxx = 479, + .panel_maxy = 799, + .disp_maxx = 469, + .disp_maxy = 799, + .disp_minx = 10, + .disp_miny = 0, + .flags = 0, + .gen = CY_GEN3, /* or */ + .use_st = CY_USE_ST, + .use_mt = CY_USE_MT, + .use_hndshk = CY_SEND_HNDSHK, + .use_trk_id = CY_USE_TRACKING_ID, + .use_sleep = CY_USE_SLEEP, + .use_gestures = CY_USE_GESTURES, + .gest_set = CY_GEST_GRP1 | CY_GEST_GRP2 | + CY_GEST_GRP3 | CY_GEST_GRP4 | + CY_ACT_DIST, + .act_intrvl = CY_ACT_INTRVL_DFLT, + .tch_tmout = CY_TCH_TMOUT_DFLT, + .lp_intrvl = CY_LP_INTRVL_DFLT, + .resume = fluid_cyttsp_resume, + .init = NULL, +}; + +static struct i2c_board_info fluid_i2c_0_board_info[] = { + { + I2C_BOARD_INFO("cyttsp-i2c", 0x24), + .platform_data = &fluid_cyttsp_pdata, + .irq = MSM_GPIO_TO_INT(MSM7X30_FLUID_GPIO_TOUCH_INT_N), + } +}; + static struct i2c_board_info surf_i2c_devices[] = { /* marimba master is implied at 0x0c */ { @@ -615,6 +703,11 @@ static void __init msm7x30_init(void) platform_add_devices(devices, ARRAY_SIZE(devices)); + if (machine_is_msm7x30_fluid()) { + fluid_cyttsp_init(); + i2c_register_board_info(0, fluid_i2c_0_board_info, + ARRAY_SIZE(fluid_i2c_0_board_info)); + } i2c_register_board_info(1, surf_i2c_devices, ARRAY_SIZE(surf_i2c_devices)); diff --git a/arch/arm/mach-msm/board-msm7x30.h b/arch/arm/mach-msm/board-msm7x30.h index a313e15897838..1da78f720fd9f 100644 --- a/arch/arm/mach-msm/board-msm7x30.h +++ b/arch/arm/mach-msm/board-msm7x30.h @@ -25,6 +25,7 @@ #define MSM7X30_PM8058_IRQ_BASE FIRST_BOARD_IRQ #define MSM7X30_GPIO_PMIC_INT_N 27 +#define MSM7X30_FLUID_GPIO_TOUCH_INT_N 150 #define MSM7X30_SURF_PMEM_SIZE 0x02000000 #define MSM7X30_SURF_PMEM_ADSP_SIZE 0x01800000 From 09c0bd922e3be0c5a0ffd6d9326cb0e445e4dc7a Mon Sep 17 00:00:00 2001 From: Anirudh Ghayal Date: Tue, 25 Jan 2011 11:23:09 -0800 Subject: [PATCH 0805/2556] ARM: msm: board-msm7x30: add cypress virtual keys Change-Id: I360eff30158decaa12c88afaed6513b89dd3beb3 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 56 +++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 3c06734181ad1..3d8212d39ecf3 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -666,6 +666,59 @@ static struct msm_spi_platform_data msm7x30_spi_pdata = { .max_clock_speed = 26331429, }; +static ssize_t fluid_virtual_keys_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, + __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":50:842:80:100" + ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":170:842:80:100" + ":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":290:842:80:100" + ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":410:842:80:100" + "\n"); +} + +static struct kobj_attribute fluid_virtual_keys_attr = { + .attr = { + .name = "virtualkeys.cyttsp-i2c", + .mode = S_IRUGO, + }, + .show = &fluid_virtual_keys_show, +}; + +static struct attribute *fluid_properties_attrs[] = { + &fluid_virtual_keys_attr.attr, + NULL +}; + +static struct attribute_group fluid_properties_attr_group = { + .attrs = fluid_properties_attrs, +}; + +static int fluid_board_props_init(void) +{ + int rc; + struct kobject *properties_kobj; + + properties_kobj = kobject_create_and_add("board_properties", NULL); + if (!properties_kobj) { + rc = -ENOMEM; + goto err_kobj_create; + } + + rc = sysfs_create_group(properties_kobj, &fluid_properties_attr_group); + if (rc) + goto err_sysfs_create; + + return 0; + +err_sysfs_create: + kobject_put(properties_kobj); +err_kobj_create: + pr_err("failed to create board_properties\n"); + return rc; +} + + extern void msm_serial_debug_init(unsigned int base, int irq, struct device *clk_device, int signal_irq); @@ -711,6 +764,9 @@ static void __init msm7x30_init(void) i2c_register_board_info(1, surf_i2c_devices, ARRAY_SIZE(surf_i2c_devices)); + if (machine_is_msm7x30_fluid()) + fluid_board_props_init(); + msm_hsusb_set_vbus_state(1); msm_hsusb_set_vbus_state(0); msm_hsusb_set_vbus_state(1); From 7116ebf79be0928af0827fe14ee4ef28192dbaa1 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 26 Jan 2011 18:08:58 -0800 Subject: [PATCH 0806/2556] ARM: msm: board-msm7x30: disable gpu powergating Change-Id: I12de7fd6b1863ebeea0002a810c6da9d107665d1 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 50 +++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 3d8212d39ecf3..afe83938fbba6 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -290,6 +290,51 @@ static struct platform_device msm_kgsl_device = { .num_resources = ARRAY_SIZE(msm_kgsl_resources), }; +#define PWR_RAIL_GRP_CLK 8 +static int msm7x30_kgsl_power_rail_mode(int follow_clk) +{ + int mode = follow_clk ? 0 : 1; + int rail_id = PWR_RAIL_GRP_CLK; + + return msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &rail_id, &mode); +} + +static int msm7x30_kgsl_power(bool on) +{ + int cmd; + int id; + + if (on) { + /* turn clock on, turn power on, turn clock off */ + cmd = PCOM_CLKCTL_RPC_RAIL_ENABLE; + id = P_GRP_3D_CLK; + msm_proc_comm(cmd, &id, NULL); + + cmd = PCOM_CLKCTL_RPC_ENABLE; + id = PWR_RAIL_GRP_CLK; + msm_proc_comm(cmd, &id, NULL); + + cmd = PCOM_CLKCTL_RPC_RAIL_DISABLE; + id = P_GRP_3D_CLK; + msm_proc_comm(cmd, &id, NULL); + } else { + /* turn clock on, turn power off, turn clock off */ + cmd = PCOM_CLKCTL_RPC_RAIL_ENABLE; + id = P_GRP_3D_CLK; + msm_proc_comm(cmd, &id, NULL); + + cmd = PCOM_CLKCTL_RPC_DISABLE; + id = PWR_RAIL_GRP_CLK; + msm_proc_comm(cmd, &id, NULL); + + cmd = PCOM_CLKCTL_RPC_RAIL_DISABLE; + id = P_GRP_3D_CLK; + msm_proc_comm(cmd, &id, NULL); + } + + return 0; +} + static struct platform_device *devices[] __initdata = { #if defined(CONFIG_SERIAL_MSM) && !defined(CONFIG_MSM_SERIAL_DEBUGGER) &msm_device_uart2, @@ -751,6 +796,11 @@ static void __init msm7x30_init(void) msm7x30_i2c_0_init(); msm7x30_spi_init(); + /* set the gpu power rail to manual mode so clk en/dis will not + * turn off gpu power, and hang it on resume */ + msm7x30_kgsl_power_rail_mode(0); + msm7x30_kgsl_power(true); + msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; msm_device_spi.dev.platform_data = &msm7x30_spi_pdata; From 09b5a3d97f9777b79c1798593aff8fefeda2e057 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 27 Jan 2011 10:20:12 -0800 Subject: [PATCH 0807/2556] ARM: msm: board-msm7x30: make fiq debugger work Change-Id: Iec331e60f9ac58338222aa200cd5cccf43e7af4c Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-msm7x30.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index afe83938fbba6..02bbbd737ae33 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -765,7 +765,8 @@ static int fluid_board_props_init(void) extern void msm_serial_debug_init(unsigned int base, int irq, - struct device *clk_device, int signal_irq); + struct device *clk_device, int signal_irq, + int wakeup_irq); static void __init msm7x30_init(void) { @@ -784,7 +785,7 @@ static void __init msm7x30_init(void) #if defined(CONFIG_MSM_SERIAL_DEBUGGER) msm_serial_debug_init(MSM_UART2_PHYS, INT_UART2, - &msm_device_uart2.dev, 1); + &msm_device_uart2.dev, 23, MSM_GPIO_TO_INT(51)); #endif if (machine_is_msm7x30_fluid()) From 74fe682f63b79ebf961b98dcecbe588a45f7c37a Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Sat, 10 Jul 2010 00:20:22 -0700 Subject: [PATCH 0808/2556] msm7x30: qdsp5v2: code to enable/disable i2s input path Signed-off-by: Brian Swetland --- arch/arm/mach-msm/qdsp5v2/adsp_audio.h | 1 + arch/arm/mach-msm/qdsp5v2/audio_glue.c | 47 ++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_audio.h b/arch/arm/mach-msm/qdsp5v2/adsp_audio.h index 4d52e7cfe0bcd..c176cd7019766 100644 --- a/arch/arm/mach-msm/qdsp5v2/adsp_audio.h +++ b/arch/arm/mach-msm/qdsp5v2/adsp_audio.h @@ -38,6 +38,7 @@ int afe_disable(unsigned device); int msm_codec_output(int enable); +int msm_codec_input(int enable); void adsp_audio_init(void); diff --git a/arch/arm/mach-msm/qdsp5v2/audio_glue.c b/arch/arm/mach-msm/qdsp5v2/audio_glue.c index 49b9841ce4d5c..5f7e703525b9f 100644 --- a/arch/arm/mach-msm/qdsp5v2/audio_glue.c +++ b/arch/arm/mach-msm/qdsp5v2/audio_glue.c @@ -474,6 +474,44 @@ int msm_codec_output_disable(struct msm_codec *mc) } +int msm_codec_input_enable(struct msm_codec *mc) +{ + unsigned rate, val; + + pr_info("msm_codec_input_enable()\n"); + + /* yes tx clks for rx codec -- the clocks + * are named from the opposite POV of the + * codec for some reason... + */ + + + /* bitrate * bits * channels * 8 */ + rate = 48000 * 16 * 2 * 8; + clk_set_rate(mc->tx_mclk, rate); + + printk("RATE %d\n", clk_get_rate(mc->tx_mclk)); + + clk_enable(mc->tx_mclk); + clk_enable(mc->tx_sclk); + + /* fire up mi2s transport */ + mi2s_set_input(mc, 2, 16); + + return 0; +} + +int msm_codec_input_disable(struct msm_codec *mc) +{ + pr_info("msm_codec_input_disable()\n"); + + clk_disable(mc->tx_sclk); + clk_disable(mc->tx_mclk); + + return 0; +} + + static struct msm_codec the_msm_codec; int msm_codec_output(int enable) @@ -485,6 +523,15 @@ int msm_codec_output(int enable) return msm_codec_output_disable(mc); } +int msm_codec_input(int enable) +{ + struct msm_codec *mc = &the_msm_codec; + if (enable) + return msm_codec_input_enable(mc); + else + return msm_codec_input_disable(mc); +} + /* 7x30 memory map */ #define PHYS_ADDR_LPA 0xA5000000 From dc60aed71265c667d513c4faf60308dbe2e92197 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Sat, 10 Jul 2010 00:25:09 -0700 Subject: [PATCH 0809/2556] msm7x30: qdsp5v2: test rig for voice support Signed-off-by: Brian Swetland --- arch/arm/mach-msm/qdsp5v2/Makefile | 1 + arch/arm/mach-msm/qdsp5v2/voice.c | 219 +++++++++++++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 arch/arm/mach-msm/qdsp5v2/voice.c diff --git a/arch/arm/mach-msm/qdsp5v2/Makefile b/arch/arm/mach-msm/qdsp5v2/Makefile index 75016db475e7c..4fb4ddaadc685 100644 --- a/arch/arm/mach-msm/qdsp5v2/Makefile +++ b/arch/arm/mach-msm/qdsp5v2/Makefile @@ -3,3 +3,4 @@ obj-y += adsp_audio.o obj-y += audio_glue.o obj-y += audio_out.o obj-y += marimba.o +obj-y += voice.o diff --git a/arch/arm/mach-msm/qdsp5v2/voice.c b/arch/arm/mach-msm/qdsp5v2/voice.c new file mode 100644 index 0000000000000..99e14df096b8a --- /dev/null +++ b/arch/arm/mach-msm/qdsp5v2/voice.c @@ -0,0 +1,219 @@ +/* arch/arm/mach-msm/qdsp5v2/voice.c + * + * Copyright (C) 2010 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 "../dal.h" + +#define VOICE_DAL_DEVICE 0x02000075 +#define VOICE_DAL_PORT "SMD_DAL00" + +/* Commands sent to Modem */ +#define CMD_VOICE_INIT 0x1 +#define CMD_ACQUIRE_DONE 0x2 +#define CMD_RELEASE_DONE 0x3 +#define CMD_DEVICE_INFO 0x4 +#define CMD_DEVICE_CHANGE 0x6 + +/* EVENTS received from MODEM */ +#define EVENT_ACQUIRE_START 0x51 +#define EVENT_RELEASE_START 0x52 +#define EVENT_CHANGE_START 0x54 +#define EVENT_NETWORK_RECONFIG 0x53 + +#define NETWORK_CDMA 0 +#define NETWORK_GSM 1 +#define NETWORK_WCDMA 2 +#define NETWORK_WCDMA_WB 3 + +#define VOICE_DALRPC_CMD DAL_OP_FIRST_DEVICE_API + +struct voice_header { + uint32_t id; + uint32_t data_len; +}; + +struct voice_init { + struct voice_header hdr; + void *cb_handle; +}; + +struct voice_device { + struct voice_header hdr; + uint32_t rx_device; + uint32_t tx_device; + uint32_t rx_volume; + uint32_t rx_mute; + uint32_t tx_mute; + uint32_t rx_sample; + uint32_t tx_sample; +}; + +struct voice_network { + struct voice_header hdr; + uint32_t network_info; +}; + +struct voice_event { + /* common DAL event header */ + uint32_t evt_handle; + uint32_t evt_cookie; + uint32_t evt_length; + + /* voice event header */ + uint32_t id; + uint32_t length; + + union { + uint32_t network; + } u; +}; + +struct msm_voice { + struct dal_client *client; + + uint32_t next; +}; + +static struct msm_voice the_voice; + +static int voice_cmd_acquire_done(struct msm_voice *voice) +{ + struct voice_header cmd; + int rc; + + cmd.id = CMD_ACQUIRE_DONE; + cmd.data_len = 0; + + return dal_call_f5(voice->client, VOICE_DALRPC_CMD, + &cmd, sizeof(cmd)); +} + +static void voice_work_func(struct work_struct *work) +{ + struct msm_voice *voice = &the_voice; + struct voice_device cmd; + int rc; + + pr_info("voice: doing work...\n"); + + if (voice->next == EVENT_ACQUIRE_START) { + cmd.hdr.id = CMD_DEVICE_INFO; + cmd.hdr.data_len = sizeof(cmd) - sizeof(cmd.hdr); + cmd.rx_device = 1; + cmd.tx_device = 2; + cmd.rx_volume = -500; /* millibels */ + cmd.tx_mute = 0; + cmd.rx_mute = 0; + cmd.rx_sample = 48000 / 1000; + cmd.tx_sample = 48000 / 1000; + + rc = dal_call_f5(voice->client, + VOICE_DALRPC_CMD, + &cmd, sizeof(cmd)); + if (rc < 0) { + pr_err("voice: device info failed\n"); + } + + rc = voice_cmd_acquire_done(voice); + } +} + +static DECLARE_WORK(voice_work, voice_work_func); + +static void voice_dal_callback(void *data, int len, void *cookie) +{ + struct msm_voice *voice = cookie; + struct voice_event *evt = data; + + voice->next = evt->id; + + switch (evt->id) { + case EVENT_ACQUIRE_START: + pr_info("voice: ACQUIRE_START (net %d)\n", + evt->u.network); + break; + case EVENT_RELEASE_START: + pr_info("voice: RELEASE_START\n"); + + break; + case EVENT_CHANGE_START: + pr_info("voice: CHANGE_START\n"); + break; + case EVENT_NETWORK_RECONFIG: + pr_info("voice: NETWORK_RECONFIG\n"); + break; + }; + + schedule_work(&voice_work); +} + +static int voice_open(struct inode *inode, struct file *file) +{ + struct msm_voice *voice = &the_voice; + struct voice_init cmd; + int rc; + + pr_info("!!! voice !!!\n"); + + voice->client = dal_attach(VOICE_DAL_DEVICE, + VOICE_DAL_PORT, + voice_dal_callback, + voice); + + if (!voice->client) { + pr_err("voice: cannot attach to service\n"); + return -ENODEV; + } + + cmd.hdr.id = CMD_VOICE_INIT; + cmd.hdr.data_len = sizeof(cmd) - sizeof(cmd.hdr); + cmd.cb_handle = 0x12345678; + rc = dal_call_f5(voice->client, VOICE_DALRPC_CMD, &cmd, sizeof(cmd)); + + if (rc < 0) { + pr_err("voice: init failed\n"); + return -ENODEV; + } + return 0; +} + +static ssize_t voice_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + return count; +} + +static const struct file_operations voice_fops = { + .owner = THIS_MODULE, + .open = voice_open, + .write = voice_write, +}; + +struct miscdevice voice_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "voice", + .fops = &voice_fops, +}; + +static int __init audio_init(void) +{ + return misc_register(&voice_misc); +} + +device_initcall(audio_init); From 79a4cd91f2e389047a6fd62317814c47ad8620ec Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 27 Jan 2011 10:52:37 -0800 Subject: [PATCH 0810/2556] [ARM] msm7x30: pmic support for hsed enable and new rpc version Change-Id: I6f7d956871608e927e14aa09a825b827ef2137a0 Signed-off-by: Brian Swetland --- arch/arm/mach-msm/pmic.c | 29 +++++++++++++++++++++-------- arch/arm/mach-msm/pmic.h | 17 +++++++++++++++++ 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-msm/pmic.c b/arch/arm/mach-msm/pmic.c index 3dee193b47282..0256308b10b9d 100644 --- a/arch/arm/mach-msm/pmic.c +++ b/arch/arm/mach-msm/pmic.c @@ -88,17 +88,17 @@ #define SPKR_ADD_RIGHT_LEFT_CHAN_PROC 60 #define SPKR_SET_GAIN_PROC 61 #define SPKR_EN_PROC 62 - +#define HSED_SET_PERIOD_PROC 63 +#define HSED_SET_HYSTERESIS_PROC 64 +#define HSED_SET_CURRENT_THRESHOLD_PROC 65 +#define HSED_ENABLE_PROC 66 /* rpc related */ #define PMIC_RPC_TIMEOUT (5*HZ) #define PMIC_RPC_PROG 0x30000061 -#ifdef CONFIG_ARCH_MSM7X30 -#define PMIC_RPC_VER 0x00030001 -#else -#define PMIC_RPC_VER 0x00010001 -#endif +#define PMIC_RPC_VER1 0x00010001 +#define PMIC_RPC_VER3 0x00030000 /* error bit flags defined by modem side */ #define PM_ERR_FLAG__PAR1_OUT_OF_RANGE (0x0001) @@ -160,8 +160,11 @@ static int pmic_rpc(int proc, void *msg, int msglen, void *rep, int replen) mutex_lock(&pmic_mutex); if (!pmic_ept) { - pmic_ept = msm_rpc_connect(PMIC_RPC_PROG, PMIC_RPC_VER, 0); - if (!pmic_ept) { + pmic_ept = msm_rpc_connect(PMIC_RPC_PROG, PMIC_RPC_VER3, 0); + if (IS_ERR(pmic_ept)) + pmic_ept = msm_rpc_connect(PMIC_RPC_PROG, PMIC_RPC_VER1, 0); + if (IS_ERR(pmic_ept)) { + pmic_ept = NULL; pr_err("pmic: cannot connect to rpc server\n"); r = -ENODEV; goto done; @@ -532,3 +535,13 @@ int pmic_spkr_en_stereo(uint enable) } EXPORT_SYMBOL(pmic_spkr_en_stereo); +int pmic_hsed_enable( + enum hsed_controller controller, + enum hsed_enable enable_hsed +) +{ + return pmic_rpc_set_only(controller, enable_hsed, 0, 0, + 2, + HSED_ENABLE_PROC); +} +EXPORT_SYMBOL(pmic_hsed_enable); diff --git a/arch/arm/mach-msm/pmic.h b/arch/arm/mach-msm/pmic.h index 14ad7897953d3..1778ec083163b 100644 --- a/arch/arm/mach-msm/pmic.h +++ b/arch/arm/mach-msm/pmic.h @@ -270,6 +270,19 @@ enum rtc_alarm { }; +enum hsed_controller { + PM_HSED_CONTROLLER_0, + PM_HSED_CONTROLLER_1, + PM_HSED_CONTROLLER_2, +}; + +enum hsed_enable { + PM_HSED_ENABLE_OFF, + PM_HSED_ENABLE_TCXO, + PM_HSED_ENABLE_PWM_TCXO, + PM_HSED_ENABLE_ALWAYS, +}; + int pmic_lp_mode_control(enum switch_cmd cmd, enum vreg_lp_id id); int pmic_secure_mpp_control_digital_output(enum mpp_which which, enum mpp_dlogic_level level, enum mpp_dlogic_out_ctrl out); @@ -306,5 +319,9 @@ int pmic_vib_mot_set_mode(enum pm_vib_mot_mode mode); int pmic_vib_mot_set_polarity(enum pm_vib_mot_pol pol); int pmic_vid_en(uint enable); int pmic_vid_load_detect_en(uint enable); +int pmic_hsed_enable( + enum hsed_controller controller, + enum hsed_enable enable +); #endif From 25fcbde2e53fd4007258e389e361cdb5ab010c7b Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Wed, 14 Jul 2010 23:22:43 -0700 Subject: [PATCH 0811/2556] [ARM] msm7x30: audio bringup - fire off higher level init from completion of dsp init (seems to be a reliable point when everything else is ready) - start sketching out a routing model - marimba slam tables for handset paths - request handset routing from voice proxy Signed-off-by: Brian Swetland --- arch/arm/mach-msm/qdsp5v2/adsp.c | 13 ++- arch/arm/mach-msm/qdsp5v2/adsp_audio.h | 8 +- arch/arm/mach-msm/qdsp5v2/audio_glue.c | 42 ++++++++-- arch/arm/mach-msm/qdsp5v2/audio_out.c | 18 +++- arch/arm/mach-msm/qdsp5v2/marimba.c | 112 +++++++++++++++++++++---- arch/arm/mach-msm/qdsp5v2/voice.c | 63 ++++++++------ 6 files changed, 202 insertions(+), 54 deletions(-) diff --git a/arch/arm/mach-msm/qdsp5v2/adsp.c b/arch/arm/mach-msm/qdsp5v2/adsp.c index 8d742412673aa..18886e321bc1c 100644 --- a/arch/arm/mach-msm/qdsp5v2/adsp.c +++ b/arch/arm/mach-msm/qdsp5v2/adsp.c @@ -29,6 +29,7 @@ #include "adsp.h" #include "adsp_private.h" +#include "adsp_audio.h" struct msm_adsp_queue { const char *name; @@ -660,6 +661,16 @@ static int adsp_probe(struct platform_device *pdev) { return ret; pr_info("*** adsp_probe() done ***\n"); + + pr_info("audio: codec init...\n"); + msm_codec_init(); + + pr_info("audio: adsp init...\n"); + adsp_audio_init(); + + pr_info("audio: voice init...\n"); + msm_voice_init(); + return 0; } @@ -684,8 +695,6 @@ static int __init adsp_init(void) spin_lock_init(&adsp->write_lock); spin_lock_init(&adsp->event_lock); - msm_codec_init(); - return platform_driver_register(&adsp_driver); } diff --git a/arch/arm/mach-msm/qdsp5v2/adsp_audio.h b/arch/arm/mach-msm/qdsp5v2/adsp_audio.h index c176cd7019766..2de216a4a23ee 100644 --- a/arch/arm/mach-msm/qdsp5v2/adsp_audio.h +++ b/arch/arm/mach-msm/qdsp5v2/adsp_audio.h @@ -37,9 +37,11 @@ int afe_enable(unsigned device, unsigned rate, unsigned channels); int afe_disable(unsigned device); -int msm_codec_output(int enable); -int msm_codec_input(int enable); - void adsp_audio_init(void); +int msm_voice_init(void); +int msm_codec_init(void); + +int audio_route_path(const char *path); +int codec_route_path(const char *path); #endif diff --git a/arch/arm/mach-msm/qdsp5v2/audio_glue.c b/arch/arm/mach-msm/qdsp5v2/audio_glue.c index 5f7e703525b9f..5364b788bd002 100644 --- a/arch/arm/mach-msm/qdsp5v2/audio_glue.c +++ b/arch/arm/mach-msm/qdsp5v2/audio_glue.c @@ -19,6 +19,11 @@ #include #include +#include "adsp_audio.h" +#include "adsp_module_afe.h" + +#include "../pmic.h" + /* the audio codec control consists of * - mi2s transports (x3) * - lpa (low power audio) frontend for mi2s tx @@ -425,8 +430,6 @@ int msm_codec_output_enable(struct msm_codec *mc) rate = 48000 * 16 * 2 * 8; clk_set_rate(mc->rx_mclk, rate); - printk("RATE %d\n", clk_get_rate(mc->rx_mclk)); - clk_enable(mc->rx_mclk); clk_enable(mc->rx_sclk); @@ -476,7 +479,7 @@ int msm_codec_output_disable(struct msm_codec *mc) int msm_codec_input_enable(struct msm_codec *mc) { - unsigned rate, val; + unsigned rate; pr_info("msm_codec_input_enable()\n"); @@ -490,8 +493,6 @@ int msm_codec_input_enable(struct msm_codec *mc) rate = 48000 * 16 * 2 * 8; clk_set_rate(mc->tx_mclk, rate); - printk("RATE %d\n", clk_get_rate(mc->tx_mclk)); - clk_enable(mc->tx_mclk); clk_enable(mc->tx_sclk); @@ -585,3 +586,34 @@ int msm_codec_init(void) return 0; } + +int audio_route_path(const char *path) +{ + static int need_init = 1; + + pr_info("audio_route_path: %s\n", path ? path : "default"); + + if (need_init) { + pr_info("audio_route_path: enable mi2s and lpa\n"); + + msm_codec_output(1); + afe_enable(AFE_DEVICE_MI2S_CODEC_RX, 48000, 2); + + msm_codec_input(1); + afe_enable(AFE_DEVICE_MI2S_CODEC_TX, 48000, 2); + + /* should be only for handset codec when required... */ + pmic_hsed_enable(PM_HSED_CONTROLLER_0, PM_HSED_ENABLE_PWM_TCXO); + + need_init = 0; + + /* make sure something sane happens if nobody has configured + * an audio route and the first user requests the default + * route + */ + if (path == NULL) + return codec_route_path("speaker"); + } + + return codec_route_path(path); +} diff --git a/arch/arm/mach-msm/qdsp5v2/audio_out.c b/arch/arm/mach-msm/qdsp5v2/audio_out.c index 4e63e601d6736..86fc61902145d 100644 --- a/arch/arm/mach-msm/qdsp5v2/audio_out.c +++ b/arch/arm/mach-msm/qdsp5v2/audio_out.c @@ -68,17 +68,20 @@ static void audio_send_data(void *cookie) } -static int need_init = 1; - static int audio_open(struct inode *inode, struct file *file) { int ret; struct audio *audio; +#if 0 + static int need_init = 1; if (need_init) { msm_codec_output(1); afe_enable(AFE_DEVICE_MI2S_CODEC_RX, 48000, 2); adie_enable(); + + msm_codec_input(1); + afe_enable(AFE_DEVICE_MI2S_CODEC_TX, 48000, 2); need_init = 0; } @@ -90,9 +93,14 @@ static int audio_open(struct inode *inode, struct file *file) msleep(5000); msm_codec_output(1); afe_enable(AFE_DEVICE_MI2S_CODEC_RX, 48000, 2); - return 0; + adie_enable(); +#endif #endif + ret = audio_route_path(NULL); + if (ret < 0) + return ret; + audio = kzalloc(sizeof(*audio), GFP_KERNEL); if (!audio) return -ENOMEM; @@ -175,6 +183,10 @@ static int audio_release(struct inode *inode, struct file *file) audplay_dsp_config(audio->audplay, 0); audplay_put(audio->audplay); kfree(audio); +#if 0 + afe_disable(AFE_DEVICE_MI2S_CODEC_RX); + msm_codec_output(0); +#endif return 0; } diff --git a/arch/arm/mach-msm/qdsp5v2/marimba.c b/arch/arm/mach-msm/qdsp5v2/marimba.c index fb1e18a3b583f..1173862089a29 100644 --- a/arch/arm/mach-msm/qdsp5v2/marimba.c +++ b/arch/arm/mach-msm/qdsp5v2/marimba.c @@ -17,6 +17,30 @@ struct codec_reg { unsigned char addr, mask, val; }; +static struct codec_reg init_tx[] = { + { 0x04, 0xc0, 0x8C }, + { 0x0D, 0xFF, 0x00 }, + { 0x0E, 0xFF, 0x00 }, + { 0x0F, 0xFF, 0x00 }, + { 0x10, 0xF8, 0x68 }, + { 0x11, 0xFE, 0x00 }, + { 0x12, 0xFE, 0x00 }, + { 0x13, 0xFF, 0x58 }, + { 0x14, 0xFF, 0x00 }, + { 0x15, 0xFE, 0x00 }, + { 0x16, 0xFF, 0x00 }, + { 0x1A, 0xFF, 0x00 }, + { 0x80, 0x01, 0x00 }, + { 0x82, 0x7F, 0x18 }, + { 0x83, 0x1C, 0x00 }, + { 0x86, 0xFF, 0xAC }, + { 0x87, 0xFF, 0xAC }, + { 0x89, 0xFF, 0xFF }, + { 0x8A, 0xF0, 0x30 }, + + { 0xFF, 0x00, 0x00 }, +}; + static struct codec_reg init_rx[] = { { 0x23, 0xF8, 0x00 }, { 0x24, 0x6F, 0x00 }, @@ -70,7 +94,7 @@ static struct codec_reg init_rx[] = { { 0xFF, 0x00, 0x00 }, }; -static struct codec_reg init_handset_48k_256_mono[] = { +static struct codec_reg init_handset_rx_48k_256_mono[] = { { 0x80, 0x02, 0x02 }, { 0x80, 0x02, 0x00 }, @@ -94,8 +118,32 @@ static struct codec_reg init_handset_48k_256_mono[] = { { 0xFF, 0x00, 0x00 }, }; - -static struct codec_reg init_speaker_48k_256_stereo[] = { + +static struct codec_reg init_handset_tx_48k_256_mono[] = { + { 0x80, 0x01, 0x01 }, + { 0x80, 0x01, 0x00 }, + { 0x8A, 0x30, 0x30 }, + { 0x11, 0xfc, 0xfc }, + { 0x13, 0xfc, 0x58 }, + { 0x14, 0xff, 0x65 }, + { 0x15, 0xff, 0x64 }, + { 0x82, 0xff, 0x5A }, + { 0x10, 0xFF, 0x68 }, + + { 0x0D, 0xF0, 0xd0 }, + + { 0xFF, 0x00, 3 }, + + { 0x83, 0x14, 0x14 }, + { 0x8b, 0xff, 0xE6 }, + { 0x8c, 0x03, 0x02 }, + { 0x86, 0xff, 0xFA }, + { 0x8A, 0x50, 0x40 }, + + { 0xFF, 0x00, 0x00 }, +}; + +static struct codec_reg init_speaker_rx_48k_256_stereo[] = { { 0x80, 0x02, 0x02 }, { 0x80, 0x02, 0x00 }, @@ -120,6 +168,7 @@ static struct codec_reg init_speaker_48k_256_stereo[] = { }; + #include #include #include @@ -252,22 +301,57 @@ void adie_load(struct i2c_client *client, struct codec_reg *regs) void adie_enable(void) { + static int need_init = 1; struct i2c_client *client = marimba_client; - marimba_vreg_enable(); + if (need_init) { + marimba_vreg_enable(); + + marimba_write(client, 0xff, 0x08); /* bring up codec */ + marimba_write(client, 0xff, 0x0a); /* GDFS_EN_FEW=1 */ + marimba_write(client, 0xff, 0x0e); /* GDFS_EN_REST=1 */ + marimba_write(client, 0xff, 0x07); /* RESET_N=1 */ + marimba_write(client, 0xff, 0x17); /* clock enable */ + marimba_write(client, 0x03, 0x04); /* enable band gap */ + marimba_write(client, 0x8F, 0x44); /* dither delay select, dmic gain bypass */ + + msleep(100); - marimba_write(client, 0xff, 0x08); /* bring up codec */ - marimba_write(client, 0xff, 0x0a); /* GDFS_EN_FEW=1 */ - marimba_write(client, 0xff, 0x0e); /* GDFS_EN_REST=1 */ - marimba_write(client, 0xff, 0x07); /* RESET_N=1 */ - marimba_write(client, 0xff, 0x17); /* clock enable */ - marimba_write(client, 0x03, 0x04); /* enable band gap */ - marimba_write(client, 0x8F, 0x44); /* dither delay select, dmic gain bypass */ + adie_load(client, init_tx); + adie_load(client, init_rx); - msleep(100); + need_init = 0; + } +} + +int codec_route_path(const char *path) +{ + struct i2c_client *client = marimba_client; + + pr_info("codec_route_path: %s\n", path ? path : "default"); + + if (!path) + return 0; + + if (!client) { + pr_err("codec_route_path: codec does not exist?!\n"); + return -ENODEV; + } + + if (!strcmp(path,"speaker")) { + adie_enable(); + adie_load(client, init_speaker_rx_48k_256_stereo); + return 0; + } + + if (!strcmp(path, "handset")) { + adie_enable(); + adie_load(client, init_handset_tx_48k_256_mono); + adie_load(client, init_handset_rx_48k_256_mono); + return 0; + } - adie_load(client, init_rx); - adie_load(client, init_speaker_48k_256_stereo); + return -ENODEV; } static int marimba_probe(struct i2c_client *client, diff --git a/arch/arm/mach-msm/qdsp5v2/voice.c b/arch/arm/mach-msm/qdsp5v2/voice.c index 99e14df096b8a..3c4e45c7a3f10 100644 --- a/arch/arm/mach-msm/qdsp5v2/voice.c +++ b/arch/arm/mach-msm/qdsp5v2/voice.c @@ -20,6 +20,8 @@ #include "../dal.h" +#include "adsp_audio.h" + #define VOICE_DAL_DEVICE 0x02000075 #define VOICE_DAL_PORT "SMD_DAL00" @@ -112,7 +114,10 @@ static void voice_work_func(struct work_struct *work) pr_info("voice: doing work...\n"); - if (voice->next == EVENT_ACQUIRE_START) { + switch (voice->next) { + case EVENT_ACQUIRE_START: + audio_route_path("handset"); + cmd.hdr.id = CMD_DEVICE_INFO; cmd.hdr.data_len = sizeof(cmd) - sizeof(cmd.hdr); cmd.rx_device = 1; @@ -131,6 +136,11 @@ static void voice_work_func(struct work_struct *work) } rc = voice_cmd_acquire_done(voice); + break; + + case EVENT_RELEASE_START: + audio_route_path("speaker"); + break; } } @@ -164,12 +174,35 @@ static void voice_dal_callback(void *data, int len, void *cookie) } static int voice_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t voice_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + return count; +} + +static const struct file_operations voice_fops = { + .owner = THIS_MODULE, + .open = voice_open, + .write = voice_write, +}; + +struct miscdevice voice_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "voice", + .fops = &voice_fops, +}; + +int msm_voice_init(void) { struct msm_voice *voice = &the_voice; struct voice_init cmd; int rc; - pr_info("!!! voice !!!\n"); + pr_info("voice: init()\n"); voice->client = dal_attach(VOICE_DAL_DEVICE, VOICE_DAL_PORT, @@ -183,37 +216,13 @@ static int voice_open(struct inode *inode, struct file *file) cmd.hdr.id = CMD_VOICE_INIT; cmd.hdr.data_len = sizeof(cmd) - sizeof(cmd.hdr); - cmd.cb_handle = 0x12345678; + cmd.cb_handle = NULL; rc = dal_call_f5(voice->client, VOICE_DALRPC_CMD, &cmd, sizeof(cmd)); if (rc < 0) { pr_err("voice: init failed\n"); return -ENODEV; } - return 0; -} -static ssize_t voice_write(struct file *file, const char __user *buf, - size_t count, loff_t *pos) -{ - return count; -} - -static const struct file_operations voice_fops = { - .owner = THIS_MODULE, - .open = voice_open, - .write = voice_write, -}; - -struct miscdevice voice_misc = { - .minor = MISC_DYNAMIC_MINOR, - .name = "voice", - .fops = &voice_fops, -}; - -static int __init audio_init(void) -{ return misc_register(&voice_misc); } - -device_initcall(audio_init); From 3d6b625340449ce9aa489c3aec1a1fc9a55a6bd2 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Thu, 27 Jan 2011 14:07:10 -0800 Subject: [PATCH 0812/2556] ARM: msm: board-msm7x30: add basic audio support Turns on poweramp and the needed regulators Change-Id: I1d12011e0e24a7c8f48db616a4c6e46ef34ce3ff Signed-off-by: Dima Zavin --- arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/board-msm7x30-audio.c | 59 +++++++++++++++++++++++++ arch/arm/mach-msm/board-msm7x30.c | 2 + arch/arm/mach-msm/board-msm7x30.h | 2 + 4 files changed, 64 insertions(+) create mode 100644 arch/arm/mach-msm/board-msm7x30-audio.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 3790eeae2973d..bca50dececbec 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -85,6 +85,7 @@ obj-$(CONFIG_ARCH_MSM8X60) += board-msm8x60.o obj-$(CONFIG_MACH_MSM7X30_SURF) += board-msm7x30.o obj-$(CONFIG_MACH_MSM7X30_SURF) += board-msm7x30-panel.o +obj-$(CONFIG_MACH_MSM7X30_SURF) += board-msm7x30-audio.o obj-$(CONFIG_ARCH_MSM7X30) += gpiomux-7x30.o gpiomux-v1.o gpiomux.o obj-$(CONFIG_ARCH_QSD8X50) += gpiomux-8x50.o gpiomux-v1.o gpiomux.o diff --git a/arch/arm/mach-msm/board-msm7x30-audio.c b/arch/arm/mach-msm/board-msm7x30-audio.c new file mode 100644 index 0000000000000..b83a66ea32682 --- /dev/null +++ b/arch/arm/mach-msm/board-msm7x30-audio.c @@ -0,0 +1,59 @@ +/* linux/arch/arm/mach-msm/board-msm7x30-audio.c + * + * Copyright (C) 2011 Google, Inc. + * + * Author: Dima Zavin + * + * 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 "gpiomux.h" +#include "board-msm7x30.h" + +static void msm7x30_speaker_amp_init(void) +{ + msm_gpiomux_write(82, 0, + GPIOMUX_FUNC_GPIO | + GPIOMUX_PULL_NONE | + GPIOMUX_DIR_OUTPUT | + GPIOMUX_DRV_2MA | GPIOMUX_VALID); + + gpio_request(82, "poweramp"); + gpio_direction_output(82, 1); +} + +static void msm7x30_marimba_init(void) +{ + struct vreg *vr; + + vr = vreg_get(NULL, "s3"); + vreg_set_level(vr, 1800); + vreg_enable(vr); + + vr = vreg_get(NULL, "gp16"); + vreg_set_level(vr, 1200); + vreg_enable(vr); + + vr = vreg_get(NULL, "usb2"); + vreg_set_level(vr, 1800); + vreg_enable(vr); +} + +void msm7x30_board_audio_init(void) +{ + msm7x30_marimba_init(); + msm7x30_speaker_amp_init(); +} diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index 02bbbd737ae33..6595eb6545c11 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -818,6 +818,8 @@ static void __init msm7x30_init(void) if (machine_is_msm7x30_fluid()) fluid_board_props_init(); + msm7x30_board_audio_init(); + msm_hsusb_set_vbus_state(1); msm_hsusb_set_vbus_state(0); msm_hsusb_set_vbus_state(1); diff --git a/arch/arm/mach-msm/board-msm7x30.h b/arch/arm/mach-msm/board-msm7x30.h index 1da78f720fd9f..976febe02ac42 100644 --- a/arch/arm/mach-msm/board-msm7x30.h +++ b/arch/arm/mach-msm/board-msm7x30.h @@ -32,4 +32,6 @@ #define MSM7X30_SURF_GPU_MEM_SIZE 0x00500000 +void msm7x30_board_audio_init(void); + #endif From 3d7338dd9928536a94875301e0d9686c95e2e813 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 28 Jan 2011 13:01:18 -0800 Subject: [PATCH 0813/2556] ARM: msm: update surf7x30_defconfig Change-Id: Ia9d3a624521ed9a96bdf7267074e1301a8059ac0 Signed-off-by: Dima Zavin --- arch/arm/configs/surf7x30_defconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/arm/configs/surf7x30_defconfig b/arch/arm/configs/surf7x30_defconfig index 8d37223be61b0..0e9df11ed7c0f 100644 --- a/arch/arm/configs/surf7x30_defconfig +++ b/arch/arm/configs/surf7x30_defconfig @@ -26,8 +26,6 @@ CONFIG_MACH_MSM7X30_FLUID=y CONFIG_MSM_DEBUG_UART2=y # CONFIG_HTC_PWRSPLY is not set # CONFIG_HTC_PWRSINK is not set -CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT=y -CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT=y CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000 # CONFIG_MSM_HW3D is not set CONFIG_MSM_SSBI=y @@ -114,6 +112,7 @@ CONFIG_INPUT_KEYRESET=y CONFIG_KEYBOARD_PM8058=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_CYTTSP_I2C=y CONFIG_TOUCHSCREEN_MSM=y CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y CONFIG_INPUT_MISC=y From b02f2ecfb5681e4a77720869ee3b8d131e70d3ed Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 28 Jan 2011 20:39:28 -0800 Subject: [PATCH 0814/2556] ARM: msm: mahimahi: remove obsoleted support for cypress touch Change-Id: Ie219a6e6afe3db0809cdb101ab82d6a0326e9d72 Signed-off-by: Dima Zavin --- arch/arm/mach-msm/board-mahimahi.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 9a88f44633c93..a1f32a8c571de 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -15,7 +15,6 @@ * */ -#include #include #include #include @@ -378,19 +377,6 @@ static int mahimahi_ts_power(int on) return 0; } -struct cy8c_i2c_platform_data mahimahi_cy8c_ts_data = { - .version = 0x0001, - .abs_x_min = 0, - .abs_x_max = 479, - .abs_y_min = 0, - .abs_y_max = 799, - .abs_pressure_min = 0, - .abs_pressure_max = 255, - .abs_width_min = 0, - .abs_width_max = 10, - .power = mahimahi_ts_power, -}; - static struct synaptics_i2c_rmi_platform_data mahimahi_synaptics_ts_data[] = { { .version = 0x105, @@ -490,11 +476,6 @@ static struct i2c_board_info base_i2c_devices[] = { I2C_BOARD_INFO("ds2482", 0x30 >> 1), .platform_data = ds2482_set_slp_n, }, - { - I2C_BOARD_INFO("cy8c-tmg-ts", 0x34), - .platform_data = &mahimahi_cy8c_ts_data, - .irq = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_TP_INT_N), - }, { I2C_BOARD_INFO(SYNAPTICS_I2C_RMI_NAME, 0x40), .platform_data = mahimahi_synaptics_ts_data, From 0d867738ce95d50f37529e18fdabc1f8e3c0c33c Mon Sep 17 00:00:00 2001 From: Lena Salman Date: Fri, 28 Jan 2011 15:39:05 -0800 Subject: [PATCH 0815/2556] spi_qsd: Move global input_fifo_size to device context. To improve SMP safety move global variable input_fifo_size to device context. Change-Id: I0a9c471722d2d8ec0cf181afee5246ee3da53e48 Signed-off-by: Lena Salman --- drivers/spi/spi_qsd.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c index c6460a1205b11..de77086cef476 100644 --- a/drivers/spi/spi_qsd.c +++ b/drivers/spi/spi_qsd.c @@ -237,6 +237,7 @@ struct msm_spi { int max_clock_speed; unsigned long mem_phys_addr; size_t mem_size; + int input_fifo_size; u32 rx_bytes_remaining; u32 tx_bytes_remaining; u32 clock_speed; @@ -280,8 +281,6 @@ struct msm_spi { #endif }; -static int input_fifo_size; - static void msm_spi_clock_set(struct msm_spi *dd, int speed) { int rc; @@ -317,20 +316,20 @@ static void __init msm_spi_calculate_fifo_size(struct msm_spi *dd) } switch (mult) { case 0: - input_fifo_size = words * 2; + dd->input_fifo_size = words * 2; break; case 1: - input_fifo_size = words * 4; + dd->input_fifo_size = words * 4; break; case 2: - input_fifo_size = words * 8; + dd->input_fifo_size = words * 8; break; default: goto fifo_size_err; } /* we can't use dma with 8 bytes of fifo since dm burst size is 16 */ - if (input_fifo_size*sizeof(u32) < DM_BURST_SIZE) + if (dd->input_fifo_size*sizeof(u32) < DM_BURST_SIZE) dd->use_dma = 0; if (dd->use_dma) { dd->block_size = words*sizeof(u32); /* in bytes */ @@ -585,7 +584,7 @@ static irqreturn_t msm_spi_output_irq(int irq, void *dev_id) /* There could be one word in input FIFO, so don't send more */ /* than input_fifo_size - 1 more words. */ while ((dd->tx_bytes_remaining > 0) && - (count < input_fifo_size - 1) && + (count < dd->input_fifo_size - 1) && !(readl(dd->base + SPI_OPERATIONAL) & SPI_OP_OUTPUT_FIFO_FULL)) { msm_spi_write_word_to_fifo(dd); count++; @@ -721,7 +720,7 @@ static void msm_spi_process_transfer(struct msm_spi *dd) larger than fifo size READ COUNT must be disabled. For those transactions we usually move to Data Mover mode. */ - if (read_count <= input_fifo_size) + if (read_count <= dd->input_fifo_size) writel(read_count, dd->base + SPI_MX_READ_COUNT); else writel(0, dd->base + SPI_MX_READ_COUNT); From d810cf3172b8a0aa804166923294bdde8277c71f Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 11 Feb 2011 10:56:58 -0800 Subject: [PATCH 0816/2556] mmc: msm_sdcc: Add CONFIG_MMC_EMBEDDED_SDIO support Signed-off-by: Dmitry Shmidt --- drivers/mmc/host/msm_sdcc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 52438b8b28121..b29e38197da95 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1185,7 +1185,14 @@ msmsdcc_probe(struct platform_device *pdev) host->memres = memres; host->dmares = dmares; spin_lock_init(&host->lock); - +#ifdef CONFIG_MMC_EMBEDDED_SDIO + if (plat->embedded_sdio) + mmc_set_embedded_sdio_data(mmc, + &plat->embedded_sdio->cis, + &plat->embedded_sdio->cccr, + plat->embedded_sdio->funcs, + plat->embedded_sdio->num_funcs); +#endif tasklet_init(&host->dma_tlet, msmsdcc_dma_complete_tlet, (unsigned long)host); From 6b97d1bac2f90ec53a46a0f4cdc87579efe1bbaf Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Fri, 4 Mar 2011 11:31:53 -0800 Subject: [PATCH 0817/2556] power: ds2784: fix build due to freezeable->freezable rename Change-Id: I3f20fc049a2801abc8d460314046c2dfb95ca49f Signed-off-by: Dima Zavin --- drivers/power/ds2784_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/ds2784_battery.c b/drivers/power/ds2784_battery.c index bc1b4d7c56252..f7145746d3755 100755 --- a/drivers/power/ds2784_battery.c +++ b/drivers/power/ds2784_battery.c @@ -663,7 +663,7 @@ static int ds2784_battery_probe(struct platform_device *pdev) goto fail_register; INIT_WORK(&di->monitor_work, ds2784_battery_work); - di->monitor_wqueue = create_freezeable_workqueue(dev_name(&pdev->dev)); + di->monitor_wqueue = create_freezable_workqueue(dev_name(&pdev->dev)); /* init to something sane */ di->last_poll = alarm_get_elapsed_realtime(); From d3d61f7d1426a6719055cde4facc729e8661d2c2 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 29 Mar 2011 14:35:01 -0700 Subject: [PATCH 0818/2556] ARM: configs: msm: update mahimahi defconfig Change-Id: I56c35070f06e6ee9759d5de4a3c9f1a581adeb7d Signed-off-by: Dima Zavin --- arch/arm/configs/mahimahi_defconfig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm/configs/mahimahi_defconfig b/arch/arm/configs/mahimahi_defconfig index 8544abd4438f2..47bba92c19218 100644 --- a/arch/arm/configs/mahimahi_defconfig +++ b/arch/arm/configs/mahimahi_defconfig @@ -161,9 +161,9 @@ CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y CONFIG_BLK_DEV_LOOP=y CONFIG_MISC_DEVICES=y -CONFIG_UID_STAT=y CONFIG_SENSORS_AKM8973=y CONFIG_VP_A1026=y +CONFIG_UID_STAT=y CONFIG_MD=y CONFIG_BLK_DEV_DM=y CONFIG_DM_DEBUG=y @@ -212,7 +212,7 @@ CONFIG_REGULATOR=y CONFIG_REGULATOR_DEBUG=y CONFIG_REGULATOR_TPS65023=y CONFIG_MEDIA_SUPPORT=y -# CONFIG_IR_CORE is not set +# CONFIG_RC_CORE is not set CONFIG_MSM_CAMERA=y CONFIG_S5K3E2FX=y CONFIG_VIDEO_OUTPUT_CONTROL=y @@ -266,7 +266,6 @@ CONFIG_EXT3_FS=y CONFIG_EXT3_FS_POSIX_ACL=y CONFIG_EXT3_FS_SECURITY=y # CONFIG_DNOTIFY is not set -CONFIG_INOTIFY=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_YAFFS_FS=y From 7e3bcb36de0243aa471dd9d5450b8b9ab004132a Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 15 Mar 2011 15:29:21 -0700 Subject: [PATCH 0819/2556] dcache.c: create helper function for duplicated functionality commit c826cb7dfce80512c26c984350077a25046bd215 upstream. This creates a helper function for he "try to ascend into the parent directory" case, which was written out in triplicate before. With all the locking and subtle sequence number stuff, we really don't want to duplicate that kind of code. Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/dcache.c | 88 ++++++++++++++++++++++------------------------------- 1 file changed, 37 insertions(+), 51 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index 611ffe928c03c..361882a14ccbc 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1011,6 +1011,34 @@ void shrink_dcache_for_umount(struct super_block *sb) } } +/* + * This tries to ascend one level of parenthood, but + * we can race with renaming, so we need to re-check + * the parenthood after dropping the lock and check + * that the sequence number still matches. + */ +static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq) +{ + struct dentry *new = old->d_parent; + + rcu_read_lock(); + spin_unlock(&old->d_lock); + spin_lock(&new->d_lock); + + /* + * might go back up the wrong parent if we have had a rename + * or deletion + */ + if (new != old->d_parent || + (!locked && read_seqretry(&rename_lock, seq))) { + spin_unlock(&new->d_lock); + new = NULL; + } + rcu_read_unlock(); + return new; +} + + /* * Search for at least 1 mount point in the dentry's subdirs. * We descend to the next level whenever the d_subdirs @@ -1066,24 +1094,10 @@ int have_submounts(struct dentry *parent) * All done at this level ... ascend and resume the search. */ if (this_parent != parent) { - struct dentry *tmp; - struct dentry *child; - - tmp = this_parent->d_parent; - rcu_read_lock(); - spin_unlock(&this_parent->d_lock); - child = this_parent; - this_parent = tmp; - spin_lock(&this_parent->d_lock); - /* might go back up the wrong parent if we have had a rename - * or deletion */ - if (this_parent != child->d_parent || - (!locked && read_seqretry(&rename_lock, seq))) { - spin_unlock(&this_parent->d_lock); - rcu_read_unlock(); + struct dentry *child = this_parent; + this_parent = try_to_ascend(this_parent, locked, seq); + if (!this_parent) goto rename_retry; - } - rcu_read_unlock(); next = child->d_u.d_child.next; goto resume; } @@ -1181,24 +1195,10 @@ static int select_parent(struct dentry * parent) * All done at this level ... ascend and resume the search. */ if (this_parent != parent) { - struct dentry *tmp; - struct dentry *child; - - tmp = this_parent->d_parent; - rcu_read_lock(); - spin_unlock(&this_parent->d_lock); - child = this_parent; - this_parent = tmp; - spin_lock(&this_parent->d_lock); - /* might go back up the wrong parent if we have had a rename - * or deletion */ - if (this_parent != child->d_parent || - (!locked && read_seqretry(&rename_lock, seq))) { - spin_unlock(&this_parent->d_lock); - rcu_read_unlock(); + struct dentry *child = this_parent; + this_parent = try_to_ascend(this_parent, locked, seq); + if (!this_parent) goto rename_retry; - } - rcu_read_unlock(); next = child->d_u.d_child.next; goto resume; } @@ -2942,28 +2942,14 @@ void d_genocide(struct dentry *root) spin_unlock(&dentry->d_lock); } if (this_parent != root) { - struct dentry *tmp; - struct dentry *child; - - tmp = this_parent->d_parent; + struct dentry *child = this_parent; if (!(this_parent->d_flags & DCACHE_GENOCIDE)) { this_parent->d_flags |= DCACHE_GENOCIDE; this_parent->d_count--; } - rcu_read_lock(); - spin_unlock(&this_parent->d_lock); - child = this_parent; - this_parent = tmp; - spin_lock(&this_parent->d_lock); - /* might go back up the wrong parent if we have had a rename - * or deletion */ - if (this_parent != child->d_parent || - (!locked && read_seqretry(&rename_lock, seq))) { - spin_unlock(&this_parent->d_lock); - rcu_read_unlock(); + this_parent = try_to_ascend(this_parent, locked, seq); + if (!this_parent) goto rename_retry; - } - rcu_read_unlock(); next = child->d_u.d_child.next; goto resume; } From 2b3bba6569c81482c29ab78427ad5f3a018b153f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 15 Mar 2011 13:36:43 -0400 Subject: [PATCH 0820/2556] VFS: Fix the nfs sillyrename regression in kernel 2.6.38 commit c83ce989cb5ff86575821992ea82c4df5c388ebc upstream. The new vfs locking scheme introduced in 2.6.38 breaks NFS sillyrename because the latter relies on being able to determine the parent directory of the dentry in the ->iput() callback in order to send the appropriate unlink rpc call. Looking at the code that cares about races with dput(), there doesn't seem to be anything that specifically uses d_parent as a test for whether or not there is a race: - __d_lookup_rcu(), __d_lookup() all test for d_hashed() after d_parent - shrink_dcache_for_umount() is safe since nothing else can rearrange the dentries in that super block. - have_submount(), select_parent() and d_genocide() can test for a deletion if we set the DCACHE_DISCONNECTED flag when the dentry is removed from the parent's d_subdirs list. Signed-off-by: Trond Myklebust Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/dcache.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/dcache.c b/fs/dcache.c index 361882a14ccbc..a39fe47c466f7 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -296,8 +296,12 @@ static struct dentry *d_kill(struct dentry *dentry, struct dentry *parent) __releases(parent->d_lock) __releases(dentry->d_inode->i_lock) { - dentry->d_parent = NULL; list_del(&dentry->d_u.d_child); + /* + * Inform try_to_ascend() that we are no longer attached to the + * dentry tree + */ + dentry->d_flags |= DCACHE_DISCONNECTED; if (parent) spin_unlock(&parent->d_lock); dentry_iput(dentry); @@ -1030,6 +1034,7 @@ static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq * or deletion */ if (new != old->d_parent || + (old->d_flags & DCACHE_DISCONNECTED) || (!locked && read_seqretry(&rename_lock, seq))) { spin_unlock(&new->d_lock); new = NULL; From d7de6e42b19accff2e84a612dd2cde6e7cbf4a6e Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 10 Feb 2011 21:26:13 -0500 Subject: [PATCH 0821/2556] ftrace: Fix memory leak with function graph and cpu hotplug commit 868baf07b1a259f5f3803c1dc2777b6c358f83cf upstream. When the fuction graph tracer starts, it needs to make a special stack for each task to save the real return values of the tasks. All running tasks have this stack created, as well as any new tasks. On CPU hot plug, the new idle task will allocate a stack as well when init_idle() is called. The problem is that cpu hotplug does not create a new idle_task. Instead it uses the idle task that existed when the cpu went down. ftrace_graph_init_task() will add a new ret_stack to the task that is given to it. Because a clone will make the task have a stack of its parent it does not check if the task's ret_stack is already NULL or not. When the CPU hotplug code starts a CPU up again, it will allocate a new stack even though one already existed for it. The solution is to treat the idle_task specially. In fact, the function_graph code already does, just not at init_idle(). Instead of using the ftrace_graph_init_task() for the idle task, which that function expects the task to be a clone, have a separate ftrace_graph_init_idle_task(). Also, we will create a per_cpu ret_stack that is used by the idle task. When we call ftrace_graph_init_idle_task() it will check if the idle task's ret_stack is NULL, if it is, then it will assign it the per_cpu ret_stack. Reported-by: Benjamin Herrenschmidt Suggested-by: Peter Zijlstra Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- include/linux/ftrace.h | 2 ++ kernel/sched.c | 2 +- kernel/trace/ftrace.c | 52 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index dcd6a7c3a4358..ca29e03c1fac0 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -428,6 +428,7 @@ extern void unregister_ftrace_graph(void); extern void ftrace_graph_init_task(struct task_struct *t); extern void ftrace_graph_exit_task(struct task_struct *t); +extern void ftrace_graph_init_idle_task(struct task_struct *t, int cpu); static inline int task_curr_ret_stack(struct task_struct *t) { @@ -451,6 +452,7 @@ static inline void unpause_graph_tracing(void) static inline void ftrace_graph_init_task(struct task_struct *t) { } static inline void ftrace_graph_exit_task(struct task_struct *t) { } +static inline void ftrace_graph_init_idle_task(struct task_struct *t, int cpu) { } static inline int register_ftrace_graph(trace_func_graph_ret_t retfunc, trace_func_graph_ent_t entryfunc) diff --git a/kernel/sched.c b/kernel/sched.c index b294a1882ffca..488bd59505be8 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -5573,7 +5573,7 @@ void __cpuinit init_idle(struct task_struct *idle, int cpu) * The idle tasks have their own, simple scheduling class: */ idle->sched_class = &idle_sched_class; - ftrace_graph_init_task(idle); + ftrace_graph_init_idle_task(idle, cpu); } /* diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index f3dadae83883e..888b611897d37 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -3328,7 +3328,7 @@ static int start_graph_tracing(void) /* The cpu_boot init_task->ret_stack will never be freed */ for_each_online_cpu(cpu) { if (!idle_task(cpu)->ret_stack) - ftrace_graph_init_task(idle_task(cpu)); + ftrace_graph_init_idle_task(idle_task(cpu), cpu); } do { @@ -3418,6 +3418,49 @@ void unregister_ftrace_graph(void) mutex_unlock(&ftrace_lock); } +static DEFINE_PER_CPU(struct ftrace_ret_stack *, idle_ret_stack); + +static void +graph_init_task(struct task_struct *t, struct ftrace_ret_stack *ret_stack) +{ + atomic_set(&t->tracing_graph_pause, 0); + atomic_set(&t->trace_overrun, 0); + t->ftrace_timestamp = 0; + /* make curr_ret_stack visable before we add the ret_stack */ + smp_wmb(); + t->ret_stack = ret_stack; +} + +/* + * Allocate a return stack for the idle task. May be the first + * time through, or it may be done by CPU hotplug online. + */ +void ftrace_graph_init_idle_task(struct task_struct *t, int cpu) +{ + t->curr_ret_stack = -1; + /* + * The idle task has no parent, it either has its own + * stack or no stack at all. + */ + if (t->ret_stack) + WARN_ON(t->ret_stack != per_cpu(idle_ret_stack, cpu)); + + if (ftrace_graph_active) { + struct ftrace_ret_stack *ret_stack; + + ret_stack = per_cpu(idle_ret_stack, cpu); + if (!ret_stack) { + ret_stack = kmalloc(FTRACE_RETFUNC_DEPTH + * sizeof(struct ftrace_ret_stack), + GFP_KERNEL); + if (!ret_stack) + return; + per_cpu(idle_ret_stack, cpu) = ret_stack; + } + graph_init_task(t, ret_stack); + } +} + /* Allocate a return stack for newly created task */ void ftrace_graph_init_task(struct task_struct *t) { @@ -3433,12 +3476,7 @@ void ftrace_graph_init_task(struct task_struct *t) GFP_KERNEL); if (!ret_stack) return; - atomic_set(&t->tracing_graph_pause, 0); - atomic_set(&t->trace_overrun, 0); - t->ftrace_timestamp = 0; - /* make curr_ret_stack visable before we add the ret_stack */ - smp_wmb(); - t->ret_stack = ret_stack; + graph_init_task(t, ret_stack); } } From e99476e4d03d35945fc42200611775f340d1daab Mon Sep 17 00:00:00 2001 From: Kamal Mostafa Date: Thu, 3 Feb 2011 17:38:04 -0800 Subject: [PATCH 0822/2556] x86: Fix panic when handling "mem={invalid}" param commit 77eed821accf5dd962b1f13bed0680e217e49112 upstream. Avoid removing all of memory and panicing when "mem={invalid}" is specified, e.g. mem=blahblah, mem=0, or mem=nopentium (on platforms other than x86_32). Signed-off-by: Kamal Mostafa BugLink: http://bugs.launchpad.net/bugs/553464 Cc: Yinghai Lu Cc: Len Brown Cc: Rafael J. Wysocki LKML-Reference: <1296783486-23033-1-git-send-email-kamal@canonical.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/e820.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index 294f26da0c0ce..55a59d889dbd4 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c @@ -856,6 +856,9 @@ static int __init parse_memopt(char *p) userdef = 1; mem_size = memparse(p, &p); + /* don't remove all of memory when handling "mem={invalid}" param */ + if (mem_size == 0) + return -EINVAL; e820_remove_range(mem_size, ULLONG_MAX - mem_size, E820_RAM, 1); return 0; From 6f699c3d51e85148fa0fe4248511c535fb8b0977 Mon Sep 17 00:00:00 2001 From: Kamal Mostafa Date: Thu, 3 Feb 2011 17:38:05 -0800 Subject: [PATCH 0823/2556] x86: Emit "mem=nopentium ignored" warning when not supported commit 9a6d44b9adb777ca9549e88cd55bd8f2673c52a2 upstream. Emit warning when "mem=nopentium" is specified on any arch other than x86_32 (the only that arch supports it). Signed-off-by: Kamal Mostafa BugLink: http://bugs.launchpad.net/bugs/553464 Cc: Yinghai Lu Cc: Len Brown Cc: Rafael J. Wysocki LKML-Reference: <1296783486-23033-2-git-send-email-kamal@canonical.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/e820.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index 55a59d889dbd4..0b5e2b546566f 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c @@ -847,12 +847,15 @@ static int __init parse_memopt(char *p) if (!p) return -EINVAL; -#ifdef CONFIG_X86_32 if (!strcmp(p, "nopentium")) { +#ifdef CONFIG_X86_32 setup_clear_cpu_cap(X86_FEATURE_PSE); return 0; - } +#else + printk(KERN_WARNING "mem=nopentium ignored! (only supported on x86_32)\n"); + return -EINVAL; #endif + } userdef = 1; mem_size = memparse(p, &p); From 0582f964131dc661641b2609af1daba2ee42af15 Mon Sep 17 00:00:00 2001 From: Seth Heasley Date: Fri, 11 Mar 2011 11:57:42 -0800 Subject: [PATCH 0824/2556] ahci: AHCI mode SATA patch for Intel Patsburg SATA RAID controller commit 64a3903d0885879ba8706a8bcf71c5e3e7664db2 upstream. This patch adds an updated SATA RAID DeviceID for the Intel Patsburg PCH. Signed-off-by: Seth Heasley Signed-off-by: Jeff Garzik Signed-off-by: Greg Kroah-Hartman --- drivers/ata/ahci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index b8d96ce37fc99..2010681896d8b 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -260,6 +260,7 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x1d02), board_ahci }, /* PBG AHCI */ { PCI_VDEVICE(INTEL, 0x1d04), board_ahci }, /* PBG RAID */ { PCI_VDEVICE(INTEL, 0x1d06), board_ahci }, /* PBG RAID */ + { PCI_VDEVICE(INTEL, 0x2826), board_ahci }, /* PBG RAID */ { PCI_VDEVICE(INTEL, 0x2323), board_ahci }, /* DH89xxCC AHCI */ /* JMicron 360/1/3/5/6, match class to avoid IDE function */ From 54a03e89438e98b66c13198d2df6f0500d03dc47 Mon Sep 17 00:00:00 2001 From: Per Jessen Date: Tue, 8 Feb 2011 13:54:32 +0100 Subject: [PATCH 0825/2556] ahci: recognize Marvell 88se9125 PCIe SATA 6.0 Gb/s controller commit 467b41c688c79d1b5e076fbdf082f9cd5d6a000c upstream. Recognize Marvell 88SE9125 PCIe SATA 6.0 Gb/s controller. Signed-off-by: Per Jessen Signed-off-by: Jeff Garzik Signed-off-by: Greg Kroah-Hartman --- drivers/ata/ahci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 2010681896d8b..34e08f63b6889 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -384,6 +384,8 @@ static const struct pci_device_id ahci_pci_tbl[] = { .class = PCI_CLASS_STORAGE_SATA_AHCI, .class_mask = 0xffffff, .driver_data = board_ahci_yes_fbs }, /* 88se9128 */ + { PCI_DEVICE(0x1b4b, 0x9125), + .driver_data = board_ahci_yes_fbs }, /* 88se9125 */ /* Promise */ { PCI_VDEVICE(PROMISE, 0x3f20), board_ahci }, /* PDC42819 */ From 0a0d8be5a7609457e61a52c46bd60d5e7f1c900d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 24 Feb 2011 19:30:37 +0100 Subject: [PATCH 0826/2556] libata: fix hotplug for drivers which don't implement LPM commit eb0e85e36b971ec31610eda7e3ff5c11c1c44785 upstream. ata_eh_analyze_serror() suppresses hotplug notifications if LPM is being used because LPM generates spurious hotplug events. It compared whether link->lpm_policy was different from ATA_LPM_MAX_POWER to determine whether LPM is enabled; however, this is incorrect as for drivers which don't implement LPM, lpm_policy is always ATA_LPM_UNKNOWN. This disabled hotplug detection for all drivers which don't implement LPM. Fix it by comparing whether lpm_policy is greater than ATA_LPM_MAX_POWER. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-eh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 17a637877d031..e16850e8d2f8a 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1618,7 +1618,7 @@ static void ata_eh_analyze_serror(struct ata_link *link) * host links. For disabled PMP links, only N bit is * considered as X bit is left at 1 for link plugging. */ - if (link->lpm_policy != ATA_LPM_MAX_POWER) + if (link->lpm_policy > ATA_LPM_MAX_POWER) hotplug_mask = 0; /* hotplug doesn't work w/ LPM */ else if (!(link->flags & ATA_LFLAG_DISABLED) || ata_is_host_link(link)) hotplug_mask = SERR_PHYRDY_CHG | SERR_DEV_XCHG; From ebab12c37fb62f74d348d2d4890598bac95c2e57 Mon Sep 17 00:00:00 2001 From: Sean Hefty Date: Wed, 23 Feb 2011 08:11:32 -0800 Subject: [PATCH 0827/2556] RDMA/cma: Fix crash in request handlers commit 25ae21a10112875763c18b385624df713a288a05 upstream. Doug Ledford and Red Hat reported a crash when running the rdma_cm on a real-time OS. The crash has the following call trace: cm_process_work cma_req_handler cma_disable_callback rdma_create_id kzalloc init_completion cma_get_net_info cma_save_net_info cma_any_addr cma_zero_addr rdma_translate_ip rdma_copy_addr cma_acquire_dev rdma_addr_get_sgid ib_find_cached_gid cma_attach_to_dev ucma_event_handler kzalloc ib_copy_ah_attr_to_user cma_comp [ preempted ] cma_write copy_from_user ucma_destroy_id copy_from_user _ucma_find_context ucma_put_ctx ucma_free_ctx rdma_destroy_id cma_exch cma_cancel_operation rdma_node_get_transport rt_mutex_slowunlock bad_area_nosemaphore oops_enter They were able to reproduce the crash multiple times with the following details: Crash seems to always happen on the: mutex_unlock(&conn_id->handler_mutex); as conn_id looks to have been freed during this code path. An examination of the code shows that a race exists in the request handlers. When a new connection request is received, the rdma_cm allocates a new connection identifier. This identifier has a single reference count on it. If a user calls rdma_destroy_id() from another thread after receiving a callback, rdma_destroy_id will proceed to destroy the id and free the associated memory. However, the request handlers may still be in the process of running. When control returns to the request handlers, they can attempt to access the newly created identifiers. Fix this by holding a reference on the newly created rdma_cm_id until the request handler is through accessing it. Signed-off-by: Sean Hefty Acked-by: Doug Ledford Signed-off-by: Roland Dreier Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/core/cma.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 6884da24fde1e..e450c5a877276 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -1210,6 +1210,11 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event) cm_id->context = conn_id; cm_id->cm_handler = cma_ib_handler; + /* + * Protect against the user destroying conn_id from another thread + * until we're done accessing it. + */ + atomic_inc(&conn_id->refcount); ret = conn_id->id.event_handler(&conn_id->id, &event); if (!ret) { /* @@ -1222,8 +1227,10 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event) ib_send_cm_mra(cm_id, CMA_CM_MRA_SETTING, NULL, 0); mutex_unlock(&lock); mutex_unlock(&conn_id->handler_mutex); + cma_deref_id(conn_id); goto out; } + cma_deref_id(conn_id); /* Destroy the CM ID by returning a non-zero value. */ conn_id->cm_id.ib = NULL; @@ -1425,17 +1432,25 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id, event.param.conn.private_data_len = iw_event->private_data_len; event.param.conn.initiator_depth = attr.max_qp_init_rd_atom; event.param.conn.responder_resources = attr.max_qp_rd_atom; + + /* + * Protect against the user destroying conn_id from another thread + * until we're done accessing it. + */ + atomic_inc(&conn_id->refcount); ret = conn_id->id.event_handler(&conn_id->id, &event); if (ret) { /* User wants to destroy the CM ID */ conn_id->cm_id.iw = NULL; cma_exch(conn_id, CMA_DESTROYING); mutex_unlock(&conn_id->handler_mutex); + cma_deref_id(conn_id); rdma_destroy_id(&conn_id->id); goto out; } mutex_unlock(&conn_id->handler_mutex); + cma_deref_id(conn_id); out: if (dev) From 8d6acb64cadd622d234bc235506dae4b1e74925d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 16 Mar 2011 08:04:07 -0700 Subject: [PATCH 0828/2556] Increase OSF partition limit from 8 to 18 commit 34d211a2d5df4984a35b18d8ccacbe1d10abb067 upstream. It turns out that while a maximum of 8 partitions may be what people "should" have had, you can actually fit up to 18 entries(*) in a sector. And some people clearly were taking advantage of that, like Michael Cree, who had ten partitions on one of his OSF disks. (*) The OSF partition data starts at byte offset 64 in the first sector, and the array of 16-byte partition entries start at offset 148 in the on-disk partition structure. Reported-by: Michael Cree Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/partitions/osf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/partitions/osf.c b/fs/partitions/osf.c index be03a0b08b47a..764b86a01965a 100644 --- a/fs/partitions/osf.c +++ b/fs/partitions/osf.c @@ -10,7 +10,7 @@ #include "check.h" #include "osf.h" -#define MAX_OSF_PARTITIONS 8 +#define MAX_OSF_PARTITIONS 18 int osf_partition(struct parsed_partitions *state) { From a9d5277ea3d315e1b909d0ab79990655933c5706 Mon Sep 17 00:00:00 2001 From: Sean Hefty Date: Wed, 23 Feb 2011 08:17:40 -0800 Subject: [PATCH 0829/2556] IB/cm: Bump reference count on cm_id before invoking callback commit 29963437a48475036353b95ab142bf199adb909e upstream. When processing a SIDR REQ, the ib_cm allocates a new cm_id. The refcount of the cm_id is initialized to 1. However, cm_process_work will decrement the refcount after invoking all callbacks. The result is that the cm_id will end up with refcount set to 0 by the end of the sidr req handler. If a user tries to destroy the cm_id, the destruction will proceed, under the incorrect assumption that no other threads are referencing the cm_id. This can lead to a crash when the cm callback thread tries to access the cm_id. This problem was noticed as part of a larger investigation with kernel crashes in the rdma_cm when running on a real time OS. Signed-off-by: Sean Hefty Acked-by: Doug Ledford Signed-off-by: Roland Dreier Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/core/cm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index 64e0903091a86..1d9616be41922 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -2989,6 +2989,7 @@ static int cm_sidr_req_handler(struct cm_work *work) goto out; /* No match. */ } atomic_inc(&cur_cm_id_priv->refcount); + atomic_inc(&cm_id_priv->refcount); spin_unlock_irq(&cm.lock); cm_id_priv->id.cm_handler = cur_cm_id_priv->id.cm_handler; From 2b9b79f6ecc13ff633f0c837f99b7f7e1524a44e Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Tue, 15 Mar 2011 15:31:37 +0100 Subject: [PATCH 0830/2556] x86, quirk: Fix SB600 revision check commit 1d3e09a304e6c4e004ca06356578b171e8735d3c upstream. Commit 7f74f8f28a2bd9db9404f7d364e2097a0c42cc12 (x86 quirk: Fix polarity for IRQ0 pin2 override on SB800 systems) introduced a regression. It removed some SB600 specific code to determine the revision ID without adapting a corresponding revision ID check for SB600. See this mail thread: http://marc.info/?l=linux-kernel&m=129980296006380&w=2 This patch adapts the corresponding check to cover all SB600 revisions. Tested-by: Wang Lei Signed-off-by: Andreas Herrmann Cc: Andrew Morton LKML-Reference: <20110315143137.GD29499@alberich.amd.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/early-quirks.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/early-quirks.c b/arch/x86/kernel/early-quirks.c index 9efbdcc56425b..3755ef4943905 100644 --- a/arch/x86/kernel/early-quirks.c +++ b/arch/x86/kernel/early-quirks.c @@ -159,7 +159,12 @@ static void __init ati_bugs_contd(int num, int slot, int func) if (rev >= 0x40) acpi_fix_pin2_polarity = 1; - if (rev > 0x13) + /* + * SB600: revisions 0x11, 0x12, 0x13, 0x14, ... + * SB700: revisions 0x39, 0x3a, ... + * SB800: revisions 0x40, 0x41, ... + */ + if (rev >= 0x39) return; if (acpi_use_timer_override) From 18ee4109afd98d2eaa4cfeaba13f5827cc1f139c Mon Sep 17 00:00:00 2001 From: "Steven J. Magnani" Date: Thu, 10 Feb 2011 12:12:13 -0600 Subject: [PATCH 0831/2556] microblaze: Fix /dev/zero corruption from __clear_user() commit 6f3946b421395ff853bc0bcdab9c26b50ebbba8f upstream. A userland read of more than PAGE_SIZE bytes from /dev/zero results in (a) not all of the bytes returned being zero, and (b) memory corruption due to zeroing of bytes beyond the user buffer. This is caused by improper constraints on the assembly __clear_user function. The constrints don't indicate to the compiler that the pointer argument is modified. Since the function is inline, this results in double-incrementing of the pointer when __clear_user() is invoked through a multi-page read() of /dev/zero. Signed-off-by: Steven J. Magnani Acked-by: Michal Simek Signed-off-by: Greg Kroah-Hartman --- arch/microblaze/include/asm/uaccess.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/microblaze/include/asm/uaccess.h b/arch/microblaze/include/asm/uaccess.h index d840f4a2d3c92..5bb95a11880d2 100644 --- a/arch/microblaze/include/asm/uaccess.h +++ b/arch/microblaze/include/asm/uaccess.h @@ -120,16 +120,16 @@ static inline unsigned long __must_check __clear_user(void __user *to, { /* normal memset with two words to __ex_table */ __asm__ __volatile__ ( \ - "1: sb r0, %2, r0;" \ + "1: sb r0, %1, r0;" \ " addik %0, %0, -1;" \ " bneid %0, 1b;" \ - " addik %2, %2, 1;" \ + " addik %1, %1, 1;" \ "2: " \ __EX_TABLE_SECTION \ ".word 1b,2b;" \ ".previous;" \ - : "=r"(n) \ - : "0"(n), "r"(to) + : "=r"(n), "=r"(to) \ + : "0"(n), "1"(to) ); return n; } From 0d589c024d98933c20c60e850d65005bdc2ff659 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Thu, 3 Mar 2011 11:01:37 -0500 Subject: [PATCH 0832/2556] x86: stop_machine_text_poke() should issue sync_core() commit 0e00f7aed6af21fc09b2a94d28bc34e449bd3a53 upstream. Intel Archiecture Software Developer's Manual section 7.1.3 specifies that a core serializing instruction such as "cpuid" should be executed on _each_ core before the new instruction is made visible. Failure to do so can lead to unspecified behavior (Intel XMC erratas include General Protection Fault in the list), so we should avoid this at all cost. This problem can affect modified code executed by interrupt handlers after interrupt are re-enabled at the end of stop_machine, because no core serializing instruction is executed between the code modification and the moment interrupts are reenabled. Because stop_machine_text_poke performs the text modification from the first CPU decrementing stop_machine_first, modified code executed in thread context is also affected by this problem. To explain why, we have to split the CPUs in two categories: the CPU that initiates the text modification (calls text_poke_smp) and all the others. The scheduler, executed on all other CPUs after stop_machine, issues an "iret" core serializing instruction, and therefore handles core serialization for all these CPUs. However, the text modification initiator can continue its execution on the same thread and access the modified text without any scheduler call. Given that the CPU that initiates the code modification is not guaranteed to be the one actually performing the code modification, it falls into the XMC errata. Q: Isn't this executed from an IPI handler, which will return with IRET (a serializing instruction) anyway? A: No, now stop_machine uses per-cpu workqueue, so that handler will be executed from worker threads. There is no iret anymore. Signed-off-by: Mathieu Desnoyers LKML-Reference: <20110303160137.GB1590@Krystal> Reviewed-by: Masami Hiramatsu Cc: Arjan van de Ven Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Andrew Morton Cc: Andi Kleen Cc: Frederic Weisbecker Signed-off-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/alternative.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index 7038b95d363f2..4db35544de738 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c @@ -620,7 +620,12 @@ static int __kprobes stop_machine_text_poke(void *data) flush_icache_range((unsigned long)p->addr, (unsigned long)p->addr + p->len); } - + /* + * Intel Archiecture Software Developer's Manual section 7.1.3 specifies + * that a core serializing instruction such as "cpuid" should be + * executed on _each_ core before the new instruction is made visible. + */ + sync_core(); return 0; } From ab8b5e5fb6360947b8c6b1c972f0a8713a55c95d Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 2 Mar 2011 16:54:24 +0900 Subject: [PATCH 0833/2556] TOMOYO: Fix memory leak upon file open. commit eae61f3c829439f8f9121b5cd48a14be04df451f upstream. In tomoyo_check_open_permission() since 2.6.36, TOMOYO was by error recalculating already calculated pathname when checking allow_rewrite permission. As a result, memory will leak whenever a file is opened for writing without O_APPEND flag. Also, performance will degrade because TOMOYO is calculating pathname regardless of profile configuration. This patch fixes the leak and performance degrade. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris Signed-off-by: Greg Kroah-Hartman --- security/tomoyo/file.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 9d32f182301ee..cb09f1fce9109 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -927,7 +927,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, struct path *path, const int flag) { const u8 acc_mode = ACC_MODE(flag); - int error = -ENOMEM; + int error = 0; struct tomoyo_path_info buf; struct tomoyo_request_info r; int idx; @@ -938,9 +938,6 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, buf.name = NULL; r.mode = TOMOYO_CONFIG_DISABLED; idx = tomoyo_read_lock(); - if (!tomoyo_get_realpath(&buf, path)) - goto out; - error = 0; /* * If the filename is specified by "deny_rewrite" keyword, * we need to check "allow_rewrite" permission when the filename is not From 6d03a1fa4651ab870dab1fb600931f234872144b Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 5 Feb 2011 10:08:21 +0000 Subject: [PATCH 0834/2556] drm/i915: Replace vblank PM QoS with "Interrupt-Based AGPBUSY#" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 8692d00e996ed2a6560702623e5cb646da0f9767 upstream. I stumbled over this magic bit in the gen3 INSTPM: Bit11 Interrupt-Based AGPBUSY# Enable: ‘0’ = Pending GMCH interrupts will not cause AGPBUSY# assertion. ‘1’ = Pending GMCH interrupts will cause AGPBUSY# assertion and hence can cause the CPU to exit C3. There is no suppression of cacheable writes. Note that in either case in C3 the interrupts are not lost. They will be forwarded to the ICH when the GMCH is out of C3. Signed-off-by: Chris Wilson Tested-by: Daniel Vetter Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/i915_irq.c | 9 +++++++++ drivers/gpu/drm/i915/i915_reg.h | 5 ++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 8a9e08bf1cf74..2347bc16d7fbe 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1377,7 +1377,12 @@ int i915_enable_vblank(struct drm_device *dev, int pipe) else i915_enable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE); + + /* maintain vblank delivery even in deep C-states */ + if (dev_priv->info->gen == 3) + I915_WRITE(INSTPM, INSTPM_AGPBUSY_DIS << 16); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); + return 0; } @@ -1390,6 +1395,10 @@ void i915_disable_vblank(struct drm_device *dev, int pipe) unsigned long irqflags; spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + if (dev_priv->info->gen == 3) + I915_WRITE(INSTPM, + INSTPM_AGPBUSY_DIS << 16 | INSTPM_AGPBUSY_DIS); + if (HAS_PCH_SPLIT(dev)) ironlake_disable_display_irq(dev_priv, (pipe == 0) ? DE_PIPEA_VBLANK: DE_PIPEB_VBLANK); diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 2abe240dae583..12c547a5ca13e 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -405,9 +405,12 @@ #define I915_ERROR_INSTRUCTION (1<<0) #define INSTPM 0x020c0 #define INSTPM_SELF_EN (1<<12) /* 915GM only */ +#define INSTPM_AGPBUSY_DIS (1<<11) /* gen3: when disabled, pending interrupts + will not assert AGPBUSY# and will only + be delivered when out of C3. */ #define ACTHD 0x020c8 #define FW_BLC 0x020d8 -#define FW_BLC2 0x020dc +#define FW_BLC2 0x020dc #define FW_BLC_SELF 0x020e0 /* 915+ only */ #define FW_BLC_SELF_EN_MASK (1<<31) #define FW_BLC_SELF_FIFO_MASK (1<<16) /* 945 only */ From f58dc85f4b44e22d087656a9d28f3a9fcf8ef3f6 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 14 Mar 2011 23:18:00 -0400 Subject: [PATCH 0835/2556] drm/radeon/kms: fix typo in atom overscan setup commit 942b0e95c34f1ba432d08e1c0288ed032d32c3b2 upstream. Typo in the aspect scale setup. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/atombios_crtc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index a4e5e53e0a627..4a5a73bcf460a 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -61,8 +61,8 @@ static void atombios_overscan_setup(struct drm_crtc *crtc, args.usOverscanLeft = cpu_to_le16((adjusted_mode->crtc_hdisplay - (a2 / mode->crtc_vdisplay)) / 2); args.usOverscanRight = cpu_to_le16((adjusted_mode->crtc_hdisplay - (a2 / mode->crtc_vdisplay)) / 2); } else if (a2 > a1) { - args.usOverscanLeft = cpu_to_le16((adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2); - args.usOverscanRight = cpu_to_le16((adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2); + args.usOverscanTop = cpu_to_le16((adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2); + args.usOverscanBottom = cpu_to_le16((adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2); } break; case RMX_FULL: From f20cbfafbdb84ca3ae06b6436e565271bdf76f11 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 15 Mar 2011 11:40:00 +0000 Subject: [PATCH 0836/2556] drm: Hold the mode mutex whilst probing for sysfs status commit 007c80a5497a3f9c8393960ec6e6efd30955dcb1 upstream. As detect will use hw registers and may modify structures, it needs to be serialised by use of the dev->mode_config.mutex. Make it so. Otherwise, we may cause random crashes as the sysfs file is queried whilst a concurrent hotplug poll is being run. For example: [ 1189.189626] BUG: unable to handle kernel NULL pointer dereference at 00000100 [ 1189.189821] IP: [] intel_tv_detect_type+0xa2/0x203 [i915] [ 1189.190020] *pde = 00000000 [ 1189.190104] Oops: 0000 [#1] SMP [ 1189.190209] last sysfs file: /sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-SVIDEO-1/status [ 1189.190412] Modules linked in: mperf cpufreq_conservative cpufreq_userspace cpufreq_powersave cpufreq_stats decnet uinput fuse loop joydev snd_hd a_codec_realtek snd_hda_intel snd_hda_codec snd_hwdep snd_pcm_oss snd_mixer_oss snd_pcm i915 snd_seq_midi snd_rawmidi snd_seq_midi_event snd_seq drm_kms_helper snd_timer uvcvideo d rm snd_seq_device eeepc_laptop tpm_tis usbhid videodev i2c_algo_bit v4l1_compat snd sparse_keymap i2c_core hid serio_raw tpm psmouse evdev tpm_bios rfkill shpchp ac processor rng_c ore battery video power_supply soundcore pci_hotplug button output snd_page_alloc usb_storage uas ext3 jbd mbcache sd_mod crc_t10dif ata_generic ahci libahci ata_piix libata uhci_h cd ehci_hcd scsi_mod usbcore thermal atl2 thermal_sys nls_base [last unloaded: scsi_wait_scan] [ 1189.192007] [ 1189.192007] Pid: 1464, comm: upowerd Not tainted 2.6.37-2-686 #1 ASUSTeK Computer INC. 701/701 [ 1189.192007] EIP: 0060:[] EFLAGS: 00010246 CPU: 0 [ 1189.192007] EIP is at intel_tv_detect_type+0xa2/0x203 [i915] [ 1189.192007] EAX: 00000000 EBX: dca74000 ECX: e0f68004 EDX: 00068004 [ 1189.192007] ESI: dd110c00 EDI: 400c0c37 EBP: dca7429c ESP: de365e2c [ 1189.192007] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 [ 1189.192007] Process upowerd (pid: 1464, ti=de364000 task=dcc8acb0 task.ti=de364000) [ 1189.192007] Stack: Mar 15 03:43:23 hostname kernel: [ 1189.192007] e0c2cda4 70000000 400c0c30 00000000 dd111000 de365e54 de365f24 dd110c00 [ 1189.192007] e0c22203 01000000 00000003 00000000 00000000 00000000 00000000 4353544e [ 1189.192007] 30383420 00000069 00000000 00000000 00000000 00000000 00000000 00000000 [ 1189.192007] Call Trace: Mar 15 03:43:23 hostname kernel: [ 1189.192007] [] ? intel_tv_detect+0x89/0x12d [i915] [ 1189.192007] [] ? status_show+0x0/0x2f [drm] [ 1189.192007] [] ? status_show+0x14/0x2f [drm] [Digression: what is upowerd doing reading those power hungry files?] Reported-by: Paul Menzel Signed-off-by: Chris Wilson Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/drm_sysfs.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 85da4c40694cc..2eee8e016b385 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -158,8 +158,15 @@ static ssize_t status_show(struct device *device, { struct drm_connector *connector = to_drm_connector(device); enum drm_connector_status status; + int ret; + + ret = mutex_lock_interruptible(&connector->dev->mode_config.mutex); + if (ret) + return ret; status = connector->funcs->detect(connector, true); + mutex_unlock(&connector->dev->mode_config.mutex); + return snprintf(buf, PAGE_SIZE, "%s\n", drm_get_connector_status_name(status)); } From e6bddf5b29360c7f97bb540dc3accffe79a420b5 Mon Sep 17 00:00:00 2001 From: Senthil Balasubramanian Date: Wed, 22 Dec 2010 19:17:18 +0530 Subject: [PATCH 0837/2556] ath9k_hw: read and backup AR_WA register value even before chip reset on. commit 0a8d7cb0c8182df7a28ad719780071178c386f0f upstream. We need to read and backup AR_WA register value permanently and reading this after the chip is awakened results in this register being zeroed out. This seems to fix the ASPM with L1 enabled issue that we have observed. The laptop becomes very slow and hangs mostly with ASPM L1 enabled without this fix. Signed-off-by: Senthil Balasubramanian Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/hw.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 9f01e50d5cda7..d3ea6dc094c63 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -495,6 +495,15 @@ static int __ath9k_hw_init(struct ath_hw *ah) if (ah->hw_version.devid == AR5416_AR9100_DEVID) ah->hw_version.macVersion = AR_SREV_VERSION_9100; + /* + * Read back AR_WA into a permanent copy and set bits 14 and 17. + * We need to do this to avoid RMW of this register. We cannot + * read the reg when chip is asleep. + */ + ah->WARegVal = REG_READ(ah, AR_WA); + ah->WARegVal |= (AR_WA_D3_L1_DISABLE | + AR_WA_ASPM_TIMER_BASED_DISABLE); + if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { ath_err(common, "Couldn't reset chip\n"); return -EIO; @@ -563,14 +572,6 @@ static int __ath9k_hw_init(struct ath_hw *ah) ath9k_hw_init_mode_regs(ah); - /* - * Read back AR_WA into a permanent copy and set bits 14 and 17. - * We need to do this to avoid RMW of this register. We cannot - * read the reg when chip is asleep. - */ - ah->WARegVal = REG_READ(ah, AR_WA); - ah->WARegVal |= (AR_WA_D3_L1_DISABLE | - AR_WA_ASPM_TIMER_BASED_DISABLE); if (ah->is_pciexpress) ath9k_hw_configpcipowersave(ah, 0, 0); From decf894ea3522897e1d2d22bef4ef0254b1c61f7 Mon Sep 17 00:00:00 2001 From: Senthil Balasubramanian Date: Wed, 22 Dec 2010 21:14:20 +0530 Subject: [PATCH 0838/2556] ath9k_hw: Fix incorrect macversion and macrev checks commit ac45c12dfb3f727a5a7a3332ed9c11b4a5ab287e upstream. There are few places where we are checking for macversion and revsions before RTC is powered ON. However we are reading the macversion and revisions only after RTC is powered ON and so both macversion and revisions are actully zero and this leads to incorrect srev checks Incorrect srev checks can cause registers to be configured wrongly and can cause unexpected behavior. Fixing this seems to address the ASPM issue that we have observed. The laptop becomes very slow and hangs mostly with ASPM L1 enabled without this fix. fix this by reading the macversion and revisisons even before we start using them. There is no reason why should we delay reading this info until RTC is powered on as this is just a register information. Signed-off-by: Senthil Balasubramanian Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/hw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index d3ea6dc094c63..7c0a7c48eea3b 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -504,6 +504,8 @@ static int __ath9k_hw_init(struct ath_hw *ah) ah->WARegVal |= (AR_WA_D3_L1_DISABLE | AR_WA_ASPM_TIMER_BASED_DISABLE); + ath9k_hw_read_revisions(ah); + if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { ath_err(common, "Couldn't reset chip\n"); return -EIO; @@ -1083,8 +1085,6 @@ static bool ath9k_hw_set_reset_power_on(struct ath_hw *ah) return false; } - ath9k_hw_read_revisions(ah); - return ath9k_hw_set_reset(ah, ATH9K_RESET_WARM); } From 150c321b1cd5aa981c6ac1de02eb1eb8d58b8f3e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 9 Mar 2011 01:48:12 +0100 Subject: [PATCH 0839/2556] ath9k: remove support for the FIF_PROMISC_IN_BSS filter flag commit 2e286947f1294239527c11f9f466ddce6466455b upstream. The hardware rx filter flag triggered by FIF_PROMISC_IN_BSS is overly broad and covers even frames with PHY errors. When this flag is enabled, this message shows up frequently during scanning or hardware resets: ath: Could not stop RX, we could be confusing the DMA engine when we start RX up Since promiscuous mode is usually not particularly useful, yet enabled by default by bridging (either used normally in 4-addr mode, or with hacks for various virtualization software), we should sacrifice it for better reliability during normal operation. This patch leaves it enabled if there are active monitor mode interfaces, since it's very useful for debugging. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/recv.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index b2497b8601e5b..3867a2e8de39c 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -439,9 +439,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc) * mode interface or when in monitor mode. AP mode does not need this * since it receives all in-BSS frames anyway. */ - if (((sc->sc_ah->opmode != NL80211_IFTYPE_AP) && - (sc->rx.rxfilter & FIF_PROMISC_IN_BSS)) || - (sc->sc_ah->is_monitoring)) + if (sc->sc_ah->is_monitoring) rfilt |= ATH9K_RX_FILTER_PROM; if (sc->rx.rxfilter & FIF_CONTROL) From c50c23da03636977c974577182300e2f15d0f19d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 25 Jan 2011 14:15:11 +0000 Subject: [PATCH 0840/2556] serial: mrst_max3110: make buffer larger commit d8653d305ef66861c91fa7455fb8038460a7274c upstream. This is used to store the spi_device ->modalias so they have to be the same size. SPI_NAME_SIZE is 32. Signed-off-by: Dan Carpenter Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mrst_max3110.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c index b62857bf2fdbd..37e13c3d91d99 100644 --- a/drivers/tty/serial/mrst_max3110.c +++ b/drivers/tty/serial/mrst_max3110.c @@ -51,7 +51,7 @@ struct uart_max3110 { struct uart_port port; struct spi_device *spi; - char name[24]; + char name[SPI_NAME_SIZE]; wait_queue_head_t wq; struct task_struct *main_thread; From 35033bf6e6885b3cd3daeb91830284e06ccc4ced Mon Sep 17 00:00:00 2001 From: Yin Kangkai Date: Wed, 9 Feb 2011 11:34:20 +0800 Subject: [PATCH 0841/2556] serial: also set the uartclk value in resume after goes to highspeed commit 95926d2db6256e08d06b753752a0d903a0580acc upstream. For any reason if the NS16550A was not work in high speed mode (e.g. we hold NS16550A from going to high speed mode in autoconfig_16550a()), now we are resume from suspend, we should also set the uartclk to the correct value. Otherwise it is still the old 1843200 and that will bring issues. CC: Greg Kroah-Hartman CC: David Woodhouse CC: linux-kernel@vger.kernel.org Signed-off-by: Yin Kangkai Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index 3975df6f7fdba..c10a6a909c764 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -3036,6 +3036,7 @@ void serial8250_resume_port(int line) serial_outp(up, 0x04, tmp); serial_outp(up, UART_LCR, 0); + up->port.uartclk = 921600*16; } uart_resume_port(&serial8250_reg, &up->port); } From 6bd1688ca02f8b5c4f3c56bd607a80817b300f23 Mon Sep 17 00:00:00 2001 From: Yin Kangkai Date: Wed, 9 Feb 2011 11:35:18 +0800 Subject: [PATCH 0842/2556] serial: change the divisor latch only when prescalar actually changed commit 0d0389e5414c8950b1613e8bdc74289cde3d6d98 upstream. In 8250.c original ns16550 autoconfig code, we change the divisor latch when we goto to high speed mode, we're assuming the previous speed is legacy. This some times is not true. For example in a system with both CONFIG_SERIAL_8250 and CONFIG_SERIAL_8250_PNP set, in this case, the code (autoconfig) will be called twice, one in serial8250_init/probe() and the other is from serial_pnp_probe. When serial_pnp_probe calls the autoconfig for NS16550A, it's already in high speed mode, change the divisor latch (quot << 3) in this case will make the UART console garbled. CC: Greg Kroah-Hartman CC: David Woodhouse CC: linux-kernel@vger.kernel.org Signed-off-by: Yin Kangkai Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index c10a6a909c764..b3b881bc4712f 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -954,6 +954,23 @@ static int broken_efr(struct uart_8250_port *up) return 0; } +static inline int ns16550a_goto_highspeed(struct uart_8250_port *up) +{ + unsigned char status; + + status = serial_in(up, 0x04); /* EXCR2 */ +#define PRESL(x) ((x) & 0x30) + if (PRESL(status) == 0x10) { + /* already in high speed mode */ + return 0; + } else { + status &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */ + status |= 0x10; /* 1.625 divisor for baud_base --> 921600 */ + serial_outp(up, 0x04, status); + } + return 1; +} + /* * We know that the chip has FIFOs. Does it have an EFR? The * EFR is located in the same register position as the IIR and @@ -1025,12 +1042,8 @@ static void autoconfig_16550a(struct uart_8250_port *up) quot = serial_dl_read(up); quot <<= 3; - status1 = serial_in(up, 0x04); /* EXCR2 */ - status1 &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */ - status1 |= 0x10; /* 1.625 divisor for baud_base --> 921600 */ - serial_outp(up, 0x04, status1); - - serial_dl_write(up, quot); + if (ns16550a_goto_highspeed(up)) + serial_dl_write(up, quot); serial_outp(up, UART_LCR, 0); @@ -3025,15 +3038,10 @@ void serial8250_resume_port(int line) struct uart_8250_port *up = &serial8250_ports[line]; if (up->capabilities & UART_NATSEMI) { - unsigned char tmp; - /* Ensure it's still in high speed mode */ serial_outp(up, UART_LCR, 0xE0); - tmp = serial_in(up, 0x04); /* EXCR2 */ - tmp &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */ - tmp |= 0x10; /* 1.625 divisor for baud_base --> 921600 */ - serial_outp(up, 0x04, tmp); + ns16550a_goto_highspeed(up); serial_outp(up, UART_LCR, 0); up->port.uartclk = 921600*16; From b2e7e40f9a2b6e630393ece5e88895935fc66ac8 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 23 Feb 2011 15:28:18 -0500 Subject: [PATCH 0843/2556] USB: serial drivers need to use larger bulk-in buffers commit 969e3033ae7733a0af8f7742ca74cd16c0857e71 upstream. When a driver doesn't know how much data a device is going to send, the buffer size should be at least as big as the endpoint's maxpacket value. The serial drivers don't follow this rule; many of them request only 256-byte bulk-in buffers. As a result, they suffer overflow errors if a high-speed device wants to send a lot of data, because high-speed bulk endpoints are required to have a maxpacket size of 512. This patch (as1450) fixes the problem by using the driver's bulk_in_size value as a minimum, always allocating buffers no smaller than the endpoint's maxpacket size. Signed-off-by: Alan Stern Tested-by: Flynn Marquardt Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/usb-serial.c | 5 ++--- include/linux/usb/serial.h | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 546a52179becb..2ff90a9c8f474 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -911,9 +911,8 @@ int usb_serial_probe(struct usb_interface *interface, dev_err(&interface->dev, "No free urbs available\n"); goto probe_error; } - buffer_size = serial->type->bulk_in_size; - if (!buffer_size) - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = max_t(int, serial->type->bulk_in_size, + le16_to_cpu(endpoint->wMaxPacketSize)); port->bulk_in_size = buffer_size; port->bulk_in_endpointAddress = endpoint->bEndpointAddress; port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index c9049139a7a5a..45f3b9db42582 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -191,7 +191,8 @@ static inline void usb_set_serial_data(struct usb_serial *serial, void *data) * @id_table: pointer to a list of usb_device_id structures that define all * of the devices this structure can support. * @num_ports: the number of different ports this device will have. - * @bulk_in_size: bytes to allocate for bulk-in buffer (0 = end-point size) + * @bulk_in_size: minimum number of bytes to allocate for bulk-in buffer + * (0 = end-point size) * @bulk_out_size: bytes to allocate for bulk-out buffer (0 = end-point size) * @calc_num_ports: pointer to a function to determine how many ports this * device has dynamically. It will be called after the probe() From 73c5812da0392d32ab6bf8c98e3068ca793f050f Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 28 Feb 2011 10:34:06 +0100 Subject: [PATCH 0844/2556] USB: serial/kobil_sct, fix potential tty NULL dereference commit 6960f40a954619857e7095a6179eef896f297077 upstream. Make sure that we check the return value of tty_port_tty_get. Sometimes it may return NULL and we later dereference that. The only place here is in kobil_read_int_callback, so fix it. Signed-off-by: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/kobil_sct.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index bd5bd8589e04c..b382d9a0274d5 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -372,7 +372,7 @@ static void kobil_read_int_callback(struct urb *urb) } tty = tty_port_tty_get(&port->port); - if (urb->actual_length) { + if (tty && urb->actual_length) { /* BEGIN DEBUG */ /* From d84a72f277e908c20b61937a091f1f7c81358ac6 Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Wed, 9 Mar 2011 09:19:48 +0000 Subject: [PATCH 0845/2556] USB: serial: option: Apply OPTION_BLACKLIST_SENDSETUP also for ZTE MF626 commit 7a89e4cb9cdaba92f5fbc509945cf4e3c48db4e2 upstream. On https://bugs.launchpad.net/ubuntu/+source/linux/+bug/636091, one of the cases reported is a big timeout on option_send_setup, which causes some side effects as tty_lock is held. Looks like some of ZTE MF626 devices also don't like the RTS/DTR setting in option_send_setup, like with 4G XS Stick W14. The reporter confirms which this it solves the long freezes in his system. Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 5f46838dfee5d..75c7f456eed52 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -652,7 +652,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0028, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0029, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0030, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff, + 0xff, 0xff), .driver_info = (kernel_ulong_t)&four_g_w14_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0032, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0033, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0034, 0xff, 0xff, 0xff) }, From 93e0a904c960dc86622c07e75a95edafe724abfe Mon Sep 17 00:00:00 2001 From: wangyanqing Date: Fri, 11 Mar 2011 06:24:38 -0800 Subject: [PATCH 0846/2556] USB: serial: ch341: add new id commit d0781383038e983a63843a9a6a067ed781db89c1 upstream. I picked up a new DAK-780EX(professional digitl reverb/mix system), which use CH341T chipset to communication with computer on 3/2011 and the CH341T's vendor code is 1a86 Looking up the CH341T's vendor and product id's I see: 1a86 QinHeng Electronics 5523 CH341 in serial mode, usb to serial port converter CH341T,CH341 are the products of the same company, maybe have some common hardware, and I test the ch341.c works well with CH341T Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ch341.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 7b8815ddf3688..14ac87ee9251b 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -75,6 +75,7 @@ static int debug; static const struct usb_device_id id_table[] = { { USB_DEVICE(0x4348, 0x5523) }, { USB_DEVICE(0x1a86, 0x7523) }, + { USB_DEVICE(0x1a86, 0x5523) }, { }, }; MODULE_DEVICE_TABLE(usb, id_table); From 2be8a03ff57cbec95f16ccd596a3e7d5ffba4167 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 24 Feb 2011 14:49:00 -0500 Subject: [PATCH 0847/2556] staging: winbond: needs for msleep and friends commit cecf826df8648c843ea8db63b1f82c154a74db36 upstream. linux/delay.h is pulled in somehow on x86 but not on ia64 or powerpc. This fixes a build failure on those arches since they use [mu]delay. Signed-off-by: Jeff Mahoney Signed-off-by: Greg Kroah-Hartman --- drivers/staging/winbond/core.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/winbond/core.h b/drivers/staging/winbond/core.h index d7b3aca5ddeba..6160b2fab833f 100644 --- a/drivers/staging/winbond/core.h +++ b/drivers/staging/winbond/core.h @@ -3,6 +3,7 @@ #include #include +#include #include "wbhal.h" #include "mto.h" From 316741107783d5c0a38d6a48f526904dff688d2a Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Fri, 11 Mar 2011 18:29:06 -0600 Subject: [PATCH 0848/2556] staging: tidspbridge: protect dmm_map properly commit ab42abf33a3efdf754710a0a513c00c40854cd61 upstream. We need to protect not only the dmm_map list, but the individual map_obj's, otherwise, we might be building the scatter-gather list with garbage. So, use the existing proc_lock for that. I observed race conditions which caused kernel panics while running stress tests, also, Tuomas Kulve found it happening quite often in Gumstix Over. This patch fixes those. Cc: Tuomas Kulve Signed-off-by: Felipe Contreras Signed-off-by: Omar Ramirez Luna Signed-off-by: Greg Kroah-Hartman --- drivers/staging/tidspbridge/rmgr/proc.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/staging/tidspbridge/rmgr/proc.c b/drivers/staging/tidspbridge/rmgr/proc.c index b47d7aa747b1d..e2fe165bd797b 100644 --- a/drivers/staging/tidspbridge/rmgr/proc.c +++ b/drivers/staging/tidspbridge/rmgr/proc.c @@ -781,12 +781,14 @@ int proc_begin_dma(void *hprocessor, void *pmpu_addr, u32 ul_size, (u32)pmpu_addr, ul_size, dir); + mutex_lock(&proc_lock); + /* find requested memory are in cached mapping information */ map_obj = find_containing_mapping(pr_ctxt, (u32) pmpu_addr, ul_size); if (!map_obj) { pr_err("%s: find_containing_mapping failed\n", __func__); status = -EFAULT; - goto err_out; + goto no_map; } if (memory_give_ownership(map_obj, (u32) pmpu_addr, ul_size, dir)) { @@ -795,6 +797,8 @@ int proc_begin_dma(void *hprocessor, void *pmpu_addr, u32 ul_size, status = -EFAULT; } +no_map: + mutex_unlock(&proc_lock); err_out: return status; @@ -819,21 +823,24 @@ int proc_end_dma(void *hprocessor, void *pmpu_addr, u32 ul_size, (u32)pmpu_addr, ul_size, dir); + mutex_lock(&proc_lock); + /* find requested memory are in cached mapping information */ map_obj = find_containing_mapping(pr_ctxt, (u32) pmpu_addr, ul_size); if (!map_obj) { pr_err("%s: find_containing_mapping failed\n", __func__); status = -EFAULT; - goto err_out; + goto no_map; } if (memory_regain_ownership(map_obj, (u32) pmpu_addr, ul_size, dir)) { pr_err("%s: InValid address parameters %p %x\n", __func__, pmpu_addr, ul_size); status = -EFAULT; - goto err_out; } +no_map: + mutex_unlock(&proc_lock); err_out: return status; } @@ -1726,9 +1733,8 @@ int proc_un_map(void *hprocessor, void *map_addr, (p_proc_object->hbridge_context, va_align, size_align); } - mutex_unlock(&proc_lock); if (status) - goto func_end; + goto unmap_failed; /* * A successful unmap should be followed by removal of map_obj @@ -1737,6 +1743,9 @@ int proc_un_map(void *hprocessor, void *map_addr, */ remove_mapping_information(pr_ctxt, (u32) map_addr, size_align); +unmap_failed: + mutex_unlock(&proc_lock); + func_end: dev_dbg(bridge, "%s: hprocessor: 0x%p map_addr: 0x%p status: 0x%x\n", __func__, hprocessor, map_addr, status); From c4b67cc74b7a251c3a9fba6ed6e390ada60b0590 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 28 Feb 2011 23:36:09 -0600 Subject: [PATCH 0849/2556] rtl8187: Change rate-control feedback commit 6410db593e8c1b2b79a2f18554310d6da9415584 upstream. The driver for the RTL8187L chips returns IEEE80211_TX_STAT_ACK for all packets, even if the maximum number of retries was exhausted. In addition it fails to setup max_rates in the ieee80211_hw struct, This behavior may be responsible for the problems noted in Bug 14168. As the bug is very old, testers have not been found, and I do not have the case where the indicated signal is less than -70 dBm. Signed-off-by: Larry Finger Acked-by: Hin-Tak Leung Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/rtl818x/rtl8187/dev.c | 25 +++++++++++++++---- .../net/wireless/rtl818x/rtl8187/rtl8187.h | 2 ++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c index 6b82cac37ee33..2bb5297655cee 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -871,23 +871,35 @@ static void rtl8187_work(struct work_struct *work) /* The RTL8187 returns the retry count through register 0xFFFA. In * addition, it appears to be a cumulative retry count, not the * value for the current TX packet. When multiple TX entries are - * queued, the retry count will be valid for the last one in the queue. - * The "error" should not matter for purposes of rate setting. */ + * waiting in the queue, the retry count will be the total for all. + * The "error" may matter for purposes of rate setting, but there is + * no other choice with this hardware. + */ struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, work.work); struct ieee80211_tx_info *info; struct ieee80211_hw *dev = priv->dev; static u16 retry; u16 tmp; + u16 avg_retry; + int length; mutex_lock(&priv->conf_mutex); tmp = rtl818x_ioread16(priv, (__le16 *)0xFFFA); + length = skb_queue_len(&priv->b_tx_status.queue); + if (unlikely(!length)) + length = 1; + if (unlikely(tmp < retry)) + tmp = retry; + avg_retry = (tmp - retry) / length; while (skb_queue_len(&priv->b_tx_status.queue) > 0) { struct sk_buff *old_skb; old_skb = skb_dequeue(&priv->b_tx_status.queue); info = IEEE80211_SKB_CB(old_skb); - info->status.rates[0].count = tmp - retry + 1; + info->status.rates[0].count = avg_retry + 1; + if (info->status.rates[0].count > RETRY_COUNT) + info->flags &= ~IEEE80211_TX_STAT_ACK; ieee80211_tx_status_irqsafe(dev, old_skb); } retry = tmp; @@ -933,8 +945,8 @@ static int rtl8187_start(struct ieee80211_hw *dev) rtl818x_iowrite32(priv, &priv->map->TX_CONF, RTL818X_TX_CONF_HW_SEQNUM | RTL818X_TX_CONF_DISREQQSIZE | - (7 << 8 /* short retry limit */) | - (7 << 0 /* long retry limit */) | + (RETRY_COUNT << 8 /* short retry limit */) | + (RETRY_COUNT << 0 /* long retry limit */) | (7 << 21 /* MAX TX DMA */)); rtl8187_init_urbs(dev); rtl8187b_init_status_urb(dev); @@ -1378,6 +1390,9 @@ static int __devinit rtl8187_probe(struct usb_interface *intf, dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_RX_INCLUDES_FCS; + /* Initialize rate-control variables */ + dev->max_rates = 1; + dev->max_rate_tries = RETRY_COUNT; eeprom.data = dev; eeprom.register_read = rtl8187_eeprom_register_read; diff --git a/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h index 0d7b1423f77b4..f1cc90751dbf5 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h +++ b/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h @@ -35,6 +35,8 @@ #define RFKILL_MASK_8187_89_97 0x2 #define RFKILL_MASK_8198 0x4 +#define RETRY_COUNT 7 + struct rtl8187_rx_info { struct urb *urb; struct ieee80211_hw *dev; From 52e8f2f1c214a5cb063d5de9a79be8d6bc53da79 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 8 Feb 2011 21:07:40 +0100 Subject: [PATCH 0850/2556] USB: isp1760: Implement solution for erratum 2 commit b14e840d04dba211fbdc930247e379085623eacd upstream. The document says: |2.1 Problem description | When at least two USB devices are simultaneously running, it is observed that | sometimes the INT corresponding to one of the USB devices stops occurring. This may | be observed sometimes with USB-to-serial or USB-to-network devices. | The problem is not noticed when only USB mass storage devices are running. |2.2 Implication | This issue is because of the clearing of the respective Done Map bit on reading the ATL | PTD Done Map register when an INT is generated by another PTD completion, but is not | found set on that read access. In this situation, the respective Done Map bit will remain | reset and no further INT will be asserted so the data transfer corresponding to that USB | device will stop. |2.3 Workaround | An SOF INT can be used instead of an ATL INT with polling on Done bits. A time-out can | be implemented and if a certain Done bit is never set, verification of the PTD completion | can be done by reading PTD contents (valid bit). | This is a proven workaround implemented in software. Russell King run into this with an USB-to-serial converter. This patch implements his suggestion to enable the high frequent SOF interrupt only at the time we have ATL packages queued. It goes even one step further and enables the SOF interrupt only if we have more than one ATL packet queued at the same time. Tested-by: Russell King Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1760-hcd.c | 22 ++++++++++++++++------ drivers/usb/host/isp1760-hcd.h | 1 + 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index bdba8c5d844aa..c470cc83dbb01 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -33,6 +33,7 @@ struct isp1760_hcd { struct inter_packet_info atl_ints[32]; struct inter_packet_info int_ints[32]; struct memory_chunk memory_pool[BLOCKS]; + u32 atl_queued; /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 @@ -850,6 +851,11 @@ static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, skip_map &= ~queue_entry; isp1760_writel(skip_map, hcd->regs + HC_ATL_PTD_SKIPMAP_REG); + priv->atl_queued++; + if (priv->atl_queued == 2) + isp1760_writel(INTERRUPT_ENABLE_SOT_MASK, + hcd->regs + HC_INTERRUPT_ENABLE); + buffstatus = isp1760_readl(hcd->regs + HC_BUFFER_STATUS_REG); buffstatus |= ATL_BUFFER; isp1760_writel(buffstatus, hcd->regs + HC_BUFFER_STATUS_REG); @@ -992,6 +998,7 @@ static void do_atl_int(struct usb_hcd *usb_hcd) u32 dw3; status = 0; + priv->atl_queued--; queue_entry = __ffs(done_map); done_map &= ~(1 << queue_entry); @@ -1054,11 +1061,6 @@ static void do_atl_int(struct usb_hcd *usb_hcd) * device is not able to send data fast enough. * This happens mostly on slower hardware. */ - printk(KERN_NOTICE "Reloading ptd %p/%p... qh %p read: " - "%d of %zu done: %08x cur: %08x\n", qtd, - urb, qh, PTD_XFERRED_LENGTH(dw3), - qtd->length, done_map, - (1 << queue_entry)); /* RL counter = ERR counter */ dw3 &= ~(0xf << 19); @@ -1086,6 +1088,11 @@ static void do_atl_int(struct usb_hcd *usb_hcd) priv_write_copy(priv, (u32 *)&ptd, usb_hcd->regs + atl_regs, sizeof(ptd)); + priv->atl_queued++; + if (priv->atl_queued == 2) + isp1760_writel(INTERRUPT_ENABLE_SOT_MASK, + usb_hcd->regs + HC_INTERRUPT_ENABLE); + buffstatus = isp1760_readl(usb_hcd->regs + HC_BUFFER_STATUS_REG); buffstatus |= ATL_BUFFER; @@ -1191,6 +1198,9 @@ static void do_atl_int(struct usb_hcd *usb_hcd) skip_map = isp1760_readl(usb_hcd->regs + HC_ATL_PTD_SKIPMAP_REG); } + if (priv->atl_queued <= 1) + isp1760_writel(INTERRUPT_ENABLE_MASK, + usb_hcd->regs + HC_INTERRUPT_ENABLE); } static void do_intl_int(struct usb_hcd *usb_hcd) @@ -1770,7 +1780,7 @@ static irqreturn_t isp1760_irq(struct usb_hcd *usb_hcd) goto leave; isp1760_writel(imask, usb_hcd->regs + HC_INTERRUPT_REG); - if (imask & HC_ATL_INT) + if (imask & (HC_ATL_INT | HC_SOT_INT)) do_atl_int(usb_hcd); if (imask & HC_INTL_INT) diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 6931ef5c96509..612bce5dce03c 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -69,6 +69,7 @@ void deinit_kmem_cache(void); #define HC_INTERRUPT_ENABLE 0x314 #define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT | HC_EOT_INT) +#define INTERRUPT_ENABLE_SOT_MASK (HC_INTL_INT | HC_SOT_INT | HC_EOT_INT) #define HC_ISO_INT (1 << 9) #define HC_ATL_INT (1 << 8) From aad009b17a4d039941c517bcda195daac1f2b90d Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 11 Jan 2011 12:26:48 -0500 Subject: [PATCH 0851/2556] ehci: Check individual port status registers on resume commit 294d95f2cbc2aef5346258f216cd9df570e271a5 upstream. If a device plug/unplug is detected on an ATI SB700 USB controller in D3, it appears to set the port status register but not the controller status register. As a result we'll fail to detect the plug event. Check the port status register on resume as well in order to catch this case. Signed-off-by: Matthew Garrett Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hub.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 8a515f0d59880..72ae77c7b7c08 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -106,6 +106,27 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci) ehci->owned_ports = 0; } +static int ehci_port_change(struct ehci_hcd *ehci) +{ + int i = HCS_N_PORTS(ehci->hcs_params); + + /* First check if the controller indicates a change event */ + + if (ehci_readl(ehci, &ehci->regs->status) & STS_PCD) + return 1; + + /* + * Not all controllers appear to update this while going from D3 to D0, + * so check the individual port status registers as well + */ + + while (i--) + if (ehci_readl(ehci, &ehci->regs->port_status[i]) & PORT_CSC) + return 1; + + return 0; +} + static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, bool suspending, bool do_wakeup) { @@ -173,7 +194,7 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, } /* Does the root hub have a port wakeup pending? */ - if (!suspending && (ehci_readl(ehci, &ehci->regs->status) & STS_PCD)) + if (!suspending && ehci_port_change(ehci)) usb_hcd_resume_root_hub(ehci_to_hcd(ehci)); spin_unlock_irqrestore(&ehci->lock, flags); From cfff86a94aa5efd6c87600d4da6aab2ed47218d1 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 7 Mar 2011 11:11:52 -0500 Subject: [PATCH 0852/2556] USB: move usbcore away from hcd->state commit 9b37596a2e860404503a3f2a6513db60c296bfdc upstream. The hcd->state variable is a disaster. It's not clearly owned by either usbcore or the host controller drivers, and they both change it from time to time, potentially stepping on each other's toes. It's not protected by any locks. And there's no mechanism to prevent it from going through an invalid transition. This patch (as1451) takes a first step toward fixing these problems. As it turns out, usbcore uses hcd->state for essentially only two things: checking whether the controller's root hub is running and checking whether the controller has died. Therefore the patch adds two new atomic bitflags to the hcd structure, to store these pieces of information. The new flags are used only by usbcore, and a private spinlock prevents invalid combinations (a dead controller's root hub cannot be running). The patch does not change the places where usbcore sets hcd->state, since HCDs may depend on them. Furthermore, there is one place in usb_hcd_irq() where usbcore still must use hcd->state: An HCD's interrupt handler can implicitly indicate that the controller died by setting hcd->state to HC_STATE_HALT. Nevertheless, the new code is a big improvement over the current code. The patch makes one other change. The hcd_bus_suspend() and hcd_bus_resume() routines now check first whether the host controller has died; if it has then they return immediately without calling the HCD's bus_suspend or bus_resume methods. This fixes the major problem reported in Bugzilla #29902: The system fails to suspend after a host controller dies during system resume. Signed-off-by: Alan Stern Tested-by: Alex Terekhov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 13 +++++---- drivers/usb/core/hcd.c | 55 ++++++++++++++++++++++++++++---------- include/linux/usb/hcd.h | 4 +++ 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index f71e8e307e0f0..d37088591d9af 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -363,8 +363,7 @@ static int check_root_hub_suspended(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); struct usb_hcd *hcd = pci_get_drvdata(pci_dev); - if (!(hcd->state == HC_STATE_SUSPENDED || - hcd->state == HC_STATE_HALT)) { + if (HCD_RH_RUNNING(hcd)) { dev_warn(dev, "Root hub is not suspended\n"); return -EBUSY; } @@ -386,7 +385,7 @@ static int suspend_common(struct device *dev, bool do_wakeup) if (retval) return retval; - if (hcd->driver->pci_suspend) { + if (hcd->driver->pci_suspend && !HCD_DEAD(hcd)) { /* Optimization: Don't suspend if a root-hub wakeup is * pending and it would cause the HCD to wake up anyway. */ @@ -427,7 +426,7 @@ static int resume_common(struct device *dev, int event) struct usb_hcd *hcd = pci_get_drvdata(pci_dev); int retval; - if (hcd->state != HC_STATE_SUSPENDED) { + if (HCD_RH_RUNNING(hcd)) { dev_dbg(dev, "can't resume, not suspended!\n"); return 0; } @@ -442,7 +441,7 @@ static int resume_common(struct device *dev, int event) clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); - if (hcd->driver->pci_resume) { + if (hcd->driver->pci_resume && !HCD_DEAD(hcd)) { if (event != PM_EVENT_AUTO_RESUME) wait_for_companions(pci_dev, hcd); @@ -475,10 +474,10 @@ static int hcd_pci_suspend_noirq(struct device *dev) pci_save_state(pci_dev); - /* If the root hub is HALTed rather than SUSPENDed, + /* If the root hub is dead rather than suspended, * disallow remote wakeup. */ - if (hcd->state == HC_STATE_HALT) + if (HCD_DEAD(hcd)) device_set_wakeup_enable(dev, 0); dev_dbg(dev, "wakeup: %d\n", device_may_wakeup(dev)); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index e935f71d7a346..c34a935c7a379 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -983,7 +983,7 @@ static int register_root_hub(struct usb_hcd *hcd) spin_unlock_irq (&hcd_root_hub_lock); /* Did the HC die before the root hub was registered? */ - if (hcd->state == HC_STATE_HALT) + if (HCD_DEAD(hcd) || hcd->state == HC_STATE_HALT) usb_hc_died (hcd); /* This time clean up */ } @@ -1089,13 +1089,10 @@ int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb) * Check the host controller's state and add the URB to the * endpoint's queue. */ - switch (hcd->state) { - case HC_STATE_RUNNING: - case HC_STATE_RESUMING: + if (HCD_RH_RUNNING(hcd)) { urb->unlinked = 0; list_add_tail(&urb->urb_list, &urb->ep->urb_list); - break; - default: + } else { rc = -ESHUTDOWN; goto done; } @@ -1913,7 +1910,7 @@ int usb_hcd_get_frame_number (struct usb_device *udev) { struct usb_hcd *hcd = bus_to_hcd(udev->bus); - if (!HC_IS_RUNNING (hcd->state)) + if (!HCD_RH_RUNNING(hcd)) return -ESHUTDOWN; return hcd->driver->get_frame_number (hcd); } @@ -1930,9 +1927,15 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) dev_dbg(&rhdev->dev, "bus %s%s\n", (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "suspend"); + if (HCD_DEAD(hcd)) { + dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "suspend"); + return 0; + } + if (!hcd->driver->bus_suspend) { status = -ENOENT; } else { + clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); hcd->state = HC_STATE_QUIESCING; status = hcd->driver->bus_suspend(hcd); } @@ -1940,7 +1943,12 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) usb_set_device_state(rhdev, USB_STATE_SUSPENDED); hcd->state = HC_STATE_SUSPENDED; } else { - hcd->state = old_state; + spin_lock_irq(&hcd_root_hub_lock); + if (!HCD_DEAD(hcd)) { + set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); + hcd->state = old_state; + } + spin_unlock_irq(&hcd_root_hub_lock); dev_dbg(&rhdev->dev, "bus %s fail, err %d\n", "suspend", status); } @@ -1955,9 +1963,13 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) dev_dbg(&rhdev->dev, "usb %s%s\n", (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume"); + if (HCD_DEAD(hcd)) { + dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "resume"); + return 0; + } if (!hcd->driver->bus_resume) return -ENOENT; - if (hcd->state == HC_STATE_RUNNING) + if (HCD_RH_RUNNING(hcd)) return 0; hcd->state = HC_STATE_RESUMING; @@ -1966,10 +1978,15 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) if (status == 0) { /* TRSMRCY = 10 msec */ msleep(10); - usb_set_device_state(rhdev, rhdev->actconfig - ? USB_STATE_CONFIGURED - : USB_STATE_ADDRESS); - hcd->state = HC_STATE_RUNNING; + spin_lock_irq(&hcd_root_hub_lock); + if (!HCD_DEAD(hcd)) { + usb_set_device_state(rhdev, rhdev->actconfig + ? USB_STATE_CONFIGURED + : USB_STATE_ADDRESS); + set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); + hcd->state = HC_STATE_RUNNING; + } + spin_unlock_irq(&hcd_root_hub_lock); } else { hcd->state = old_state; dev_dbg(&rhdev->dev, "bus %s fail, err %d\n", @@ -2080,7 +2097,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd) */ local_irq_save(flags); - if (unlikely(hcd->state == HC_STATE_HALT || !HCD_HW_ACCESSIBLE(hcd))) { + if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd))) { rc = IRQ_NONE; } else if (hcd->driver->irq(hcd) == IRQ_NONE) { rc = IRQ_NONE; @@ -2114,6 +2131,8 @@ void usb_hc_died (struct usb_hcd *hcd) dev_err (hcd->self.controller, "HC died; cleaning up\n"); spin_lock_irqsave (&hcd_root_hub_lock, flags); + clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); + set_bit(HCD_FLAG_DEAD, &hcd->flags); if (hcd->rh_registered) { clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); @@ -2256,6 +2275,12 @@ int usb_add_hcd(struct usb_hcd *hcd, */ device_init_wakeup(&rhdev->dev, 1); + /* HCD_FLAG_RH_RUNNING doesn't matter until the root hub is + * registered. But since the controller can die at any time, + * let's initialize the flag before touching the hardware. + */ + set_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); + /* "reset" is misnamed; its role is now one-time init. the controller * should already have been reset (and boot firmware kicked off etc). */ @@ -2323,6 +2348,7 @@ int usb_add_hcd(struct usb_hcd *hcd, return retval; error_create_attr_group: + clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); if (HC_IS_RUNNING(hcd->state)) hcd->state = HC_STATE_QUIESCING; spin_lock_irq(&hcd_root_hub_lock); @@ -2375,6 +2401,7 @@ void usb_remove_hcd(struct usb_hcd *hcd) usb_get_dev(rhdev); sysfs_remove_group(&rhdev->dev.kobj, &usb_bus_attr_group); + clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); if (HC_IS_RUNNING (hcd->state)) hcd->state = HC_STATE_QUIESCING; diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index a854fe89484e4..f21f5996fa61f 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -99,6 +99,8 @@ struct usb_hcd { #define HCD_FLAG_POLL_RH 2 /* poll for rh status? */ #define HCD_FLAG_POLL_PENDING 3 /* status has changed? */ #define HCD_FLAG_WAKEUP_PENDING 4 /* root hub is resuming? */ +#define HCD_FLAG_RH_RUNNING 5 /* root hub is running? */ +#define HCD_FLAG_DEAD 6 /* controller has died? */ /* The flags can be tested using these macros; they are likely to * be slightly faster than test_bit(). @@ -108,6 +110,8 @@ struct usb_hcd { #define HCD_POLL_RH(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_RH)) #define HCD_POLL_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_PENDING)) #define HCD_WAKEUP_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_WAKEUP_PENDING)) +#define HCD_RH_RUNNING(hcd) ((hcd)->flags & (1U << HCD_FLAG_RH_RUNNING)) +#define HCD_DEAD(hcd) ((hcd)->flags & (1U << HCD_FLAG_DEAD)) /* Flags that get set only during HCD registration or removal. */ unsigned rh_registered:1;/* is root hub registered? */ From ab82fc323a8712cd1dd001449f503188618879e1 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 23 Feb 2011 15:46:42 -0800 Subject: [PATCH 0853/2556] xhci: Update internal dequeue pointers after stalls. commit bf161e85fb153c0dd5a95faca73fd6a9d237c389 upstream. When an endpoint stalls, the xHCI driver must move the endpoint ring's dequeue pointer past the stalled transfer. To do that, the driver issues a Set TR Dequeue Pointer command, which will complete some time later. Takashi was having issues with USB 1.1 audio devices that stalled, and his analysis of the code was that the old code would not update the xHCI driver's ring dequeue pointer after the command completes. However, the dequeue pointer is set in xhci_find_new_dequeue_state(), just before the set command is issued to the hardware. Setting the dequeue pointer before the Set TR Dequeue Pointer command completes is a dangerous thing to do, since the xHCI hardware can fail the command. Instead, store the new dequeue pointer in the xhci_virt_ep structure, and update the ring's dequeue pointer when the Set TR dequeue pointer command completes. While we're at it, make sure we can't queue another Set TR Dequeue Command while the first one is still being processed. This just won't work with the internal xHCI state code. I'm still not sure if this is the right thing to do, since we might have a case where a driver queues multiple URBs to a control ring, one of the URBs Stalls, and then the driver tries to cancel the second URB. There may be a race condition there where the xHCI driver might try to issue multiple Set TR Dequeue Pointer commands, but I would have to think very hard about how the Stop Endpoint and cancellation code works. Keep the fix simple until when/if we run into that case. This patch should be queued to kernels all the way back to 2.6.31. Signed-off-by: Sarah Sharp Tested-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 29 ++++++++++++++++++++++++++--- drivers/usb/host/xhci.h | 9 +++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 3289bf4832c9a..699676d1df888 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -506,9 +506,6 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, addr = xhci_trb_virt_to_dma(state->new_deq_seg, state->new_deq_ptr); xhci_dbg(xhci, "New dequeue pointer = 0x%llx (DMA)\n", (unsigned long long) addr); - xhci_dbg(xhci, "Setting dequeue pointer in internal ring state.\n"); - ep_ring->dequeue = state->new_deq_ptr; - ep_ring->deq_seg = state->new_deq_seg; } static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, @@ -951,9 +948,26 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, } else { xhci_dbg(xhci, "Successful Set TR Deq Ptr cmd, deq = @%08llx\n", ep_ctx->deq); + if (xhci_trb_virt_to_dma(dev->eps[ep_index].queued_deq_seg, + dev->eps[ep_index].queued_deq_ptr) == + (ep_ctx->deq & ~(EP_CTX_CYCLE_MASK))) { + /* Update the ring's dequeue segment and dequeue pointer + * to reflect the new position. + */ + ep_ring->deq_seg = dev->eps[ep_index].queued_deq_seg; + ep_ring->dequeue = dev->eps[ep_index].queued_deq_ptr; + } else { + xhci_warn(xhci, "Mismatch between completed Set TR Deq " + "Ptr command & xHCI internal state.\n"); + xhci_warn(xhci, "ep deq seg = %p, deq ptr = %p\n", + dev->eps[ep_index].queued_deq_seg, + dev->eps[ep_index].queued_deq_ptr); + } } dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING; + dev->eps[ep_index].queued_deq_seg = NULL; + dev->eps[ep_index].queued_deq_ptr = NULL; /* Restart any rings with pending URBs */ ring_doorbell_for_active_rings(xhci, slot_id, ep_index); } @@ -3229,6 +3243,7 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id, u32 trb_ep_index = EP_ID_FOR_TRB(ep_index); u32 trb_stream_id = STREAM_ID_FOR_TRB(stream_id); u32 type = TRB_TYPE(TRB_SET_DEQ); + struct xhci_virt_ep *ep; addr = xhci_trb_virt_to_dma(deq_seg, deq_ptr); if (addr == 0) { @@ -3237,6 +3252,14 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id, deq_seg, deq_ptr); return 0; } + ep = &xhci->devs[slot_id]->eps[ep_index]; + if ((ep->ep_state & SET_DEQ_PENDING)) { + xhci_warn(xhci, "WARN Cannot submit Set TR Deq Ptr\n"); + xhci_warn(xhci, "A Set TR Deq Ptr command is pending.\n"); + return 0; + } + ep->queued_deq_seg = deq_seg; + ep->queued_deq_ptr = deq_ptr; return queue_command(xhci, lower_32_bits(addr) | cycle_state, upper_32_bits(addr), trb_stream_id, trb_slot_id | trb_ep_index | type, false); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 7f127df6dd553..62bc1bc2bd72e 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -644,6 +644,9 @@ struct xhci_ep_ctx { #define AVG_TRB_LENGTH_FOR_EP(p) ((p) & 0xffff) #define MAX_ESIT_PAYLOAD_FOR_EP(p) (((p) & 0xffff) << 16) +/* deq bitmasks */ +#define EP_CTX_CYCLE_MASK (1 << 0) + /** * struct xhci_input_control_context @@ -746,6 +749,12 @@ struct xhci_virt_ep { struct timer_list stop_cmd_timer; int stop_cmds_pending; struct xhci_hcd *xhci; + /* Dequeue pointer and dequeue segment for a submitted Set TR Dequeue + * command. We'll need to update the ring's dequeue segment and dequeue + * pointer after the command completes. + */ + struct xhci_segment *queued_deq_seg; + union xhci_trb *queued_deq_ptr; /* * Sometimes the xHC can not process isochronous endpoint ring quickly * enough, and it will miss some isoc tds on the ring and generate From be77840bdbc0ee51f78a564f91f58a036226e9c4 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 23 Feb 2011 18:12:29 -0800 Subject: [PATCH 0854/2556] xhci: Fix cycle bit calculation during stall handling. commit 01a1fdb9a7afa5e3c14c9316d6f380732750b4e4 upstream. When an endpoint stalls, we need to update the xHCI host's internal dequeue pointer to move it past the stalled transfer. This includes updating the cycle bit (TRB ownership bit) if we have moved the dequeue pointer past a link TRB with the toggle cycle bit set. When we're trying to find the new dequeue segment, find_trb_seg() is supposed to keep track of whether we've passed any link TRBs with the toggle cycle bit set. However, this while loop's body while (cur_seg->trbs > trb || &cur_seg->trbs[TRBS_PER_SEGMENT - 1] < trb) { Will never get executed if the ring only contains one segment. find_trb_seg() will return immediately, without updating the new cycle bit. Since find_trb_seg() has no idea where in the segment the TD that stalled was, make the caller, xhci_find_new_dequeue_state(), check for this special case and update the cycle bit accordingly. This patch should be queued to kernels all the way back to 2.6.31. Signed-off-by: Sarah Sharp Tested-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 699676d1df888..d3f0406f6f20a 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -500,6 +500,20 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, state->new_cycle_state = ~(state->new_cycle_state) & 0x1; next_trb(xhci, ep_ring, &state->new_deq_seg, &state->new_deq_ptr); + /* + * If there is only one segment in a ring, find_trb_seg()'s while loop + * will not run, and it will return before it has a chance to see if it + * needs to toggle the cycle bit. It can't tell if the stalled transfer + * ended just before the link TRB on a one-segment ring, or if the TD + * wrapped around the top of the ring, because it doesn't have the TD in + * question. Look for the one-segment case where stalled TRB's address + * is greater than the new dequeue pointer address. + */ + if (ep_ring->first_seg == ep_ring->first_seg->next && + state->new_deq_ptr < dev->eps[ep_index].stopped_trb) + state->new_cycle_state ^= 0x1; + xhci_dbg(xhci, "Cycle state = 0x%x\n", state->new_cycle_state); + /* Don't update the ring cycle state for the producer (us). */ xhci_dbg(xhci, "New dequeue segment = %p (virtual)\n", state->new_deq_seg); From 1e3e0193dda6e1b194ec0bae3d711896e5b05ee7 Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Mon, 28 Feb 2011 18:11:27 -0800 Subject: [PATCH 0855/2556] USB: Add support for SuperSpeed isoc endpoints commit 500132a0f26ad7d9916102193cbc6c1b1becb373 upstream. Use the Mult and bMaxBurst values from the endpoint companion descriptor to calculate the max length of an isoc transfer. Add USB_SS_MULT macro to access Mult field of bmAttributes, at Sarah's suggestion. This patch should be queued for the 2.6.36 and 2.6.37 stable trees, since those were the first kernels to have isochronous support for SuperSpeed devices. Signed-off-by: Paul Zimmerman Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/urb.c | 11 ++++++++++- include/linux/usb/ch9.h | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index c14fc082864f1..ae334b067c13e 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -366,7 +366,16 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) if (xfertype == USB_ENDPOINT_XFER_ISOC) { int n, len; - /* FIXME SuperSpeed isoc endpoints have up to 16 bursts */ + /* SuperSpeed isoc endpoints have up to 16 bursts of up to + * 3 packets each + */ + if (dev->speed == USB_SPEED_SUPER) { + int burst = 1 + ep->ss_ep_comp.bMaxBurst; + int mult = USB_SS_MULT(ep->ss_ep_comp.bmAttributes); + max *= burst; + max *= mult; + } + /* "high bandwidth" mode, 1-3 packets/uframe? */ if (dev->speed == USB_SPEED_HIGH) { int mult = 1 + ((max >> 11) & 0x03); diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index ab461948b579e..76d896cc842e9 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -585,6 +585,8 @@ struct usb_ss_ep_comp_descriptor { #define USB_DT_SS_EP_COMP_SIZE 6 /* Bits 4:0 of bmAttributes if this is a bulk endpoint */ #define USB_SS_MAX_STREAMS(p) (1 << (p & 0x1f)) +/* Bits 1:0 of bmAttributes if this is an isoc endpoint */ +#define USB_SS_MULT(p) (1 + ((p) & 0x3)) /*-------------------------------------------------------------------------*/ From b9b31ac4d31eb8d97f31954acc4e3ee8ecd5fab4 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 4 Mar 2011 13:37:50 +0100 Subject: [PATCH 0856/2556] ALSA: HDA: Enable surround and subwoofer on Lenovo Ideapad Y530 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 32eea3884debb65ec1da633bc5df5aee23879865 upstream. The pin config values would change the association instead of the sequence, this commit fixes that up. Tested-by: Bartłomiej Żogała Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4261bb8eec1d5..8caf593fcf20e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10748,6 +10748,7 @@ static struct alc_config_preset alc882_presets[] = { */ enum { PINFIX_ABIT_AW9D_MAX, + PINFIX_LENOVO_Y530, PINFIX_PB_M5210, PINFIX_ACER_ASPIRE_7736, }; @@ -10762,6 +10763,14 @@ static const struct alc_fixup alc882_fixups[] = { { } } }, + [PINFIX_LENOVO_Y530] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x15, 0x99130112 }, /* rear int speakers */ + { 0x16, 0x99130111 }, /* subwoofer */ + { } + } + }, [PINFIX_PB_M5210] = { .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { @@ -10777,6 +10786,7 @@ static const struct alc_fixup alc882_fixups[] = { static struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", PINFIX_PB_M5210), + SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", PINFIX_LENOVO_Y530), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX), SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736), {} From 9aa3c4d8ee7e7cc7d62ee0100799252aaad630f1 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 4 Mar 2011 14:08:30 +0100 Subject: [PATCH 0857/2556] ALSA: HDA: Fix volume control naming for surround speakers on Realtek auto-parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ebbeb3d6aa22433c218da6f29fd7b3ebc89b87ea upstream. When more than one pair of internal speakers is present, allow names according to their channels. Tested-by: Bartłomiej Żogała Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8caf593fcf20e..8c61566bdc583 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5151,7 +5151,9 @@ static const char *alc_get_line_out_pfx(const struct auto_pin_cfg *cfg, switch (cfg->line_out_type) { case AUTO_PIN_SPEAKER_OUT: - return "Speaker"; + if (cfg->line_outs == 1) + return "Speaker"; + break; case AUTO_PIN_HP_OUT: return "Headphone"; default: From 7f591c383883bb28cd6f767c4aa05f9aac29b045 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 4 Mar 2011 14:22:25 +0100 Subject: [PATCH 0858/2556] ALSA: HDA: Fixup unnecessary volume control index on Realtek ALC88x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 7e59e097c09b82760bb0fe08b0fa2b704d76c3f4 upstream. Without this change, a volume control named "Surround" or "Side" would get an unnecessary index, causing it to be ignored by the vmaster and PulseAudio. Tested-by: Bartłomiej Żogała Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8c61566bdc583..ffb05bd54badc 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5207,16 +5207,19 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, return err; } else { const char *name = pfx; - if (!name) + int index = i; + if (!name) { name = chname[i]; + index = 0; + } err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - name, i, + name, index, HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); if (err < 0) return err; err = __add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - name, i, + name, index, HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); if (err < 0) From bd5451ce449e022160d63f39cef0bd8e12cd4f0d Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 4 Mar 2011 16:54:52 +0100 Subject: [PATCH 0859/2556] ALSA: HDA: Realtek ALC88x: Do not over-initialize speakers and hp that are primary outputs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 0a3fabe30e1a3b2037a12b863b8c45fffce38ee9 upstream. Do not initialize again the what has already been initialized as multi outs, as this breaks surround speakers. Tested-by: Bartłomiej Żogała Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 37 ++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ffb05bd54badc..2a1c4124f300b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10844,23 +10844,28 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec) hda_nid_t pin, dac; int i; - for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) { - pin = spec->autocfg.hp_pins[i]; - if (!pin) - break; - dac = spec->multiout.hp_nid; - if (!dac) - dac = spec->multiout.dac_nids[0]; /* to front */ - alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); + if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) { + for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) { + pin = spec->autocfg.hp_pins[i]; + if (!pin) + break; + dac = spec->multiout.hp_nid; + if (!dac) + dac = spec->multiout.dac_nids[0]; /* to front */ + alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); + } } - for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) { - pin = spec->autocfg.speaker_pins[i]; - if (!pin) - break; - dac = spec->multiout.extra_out_nid[0]; - if (!dac) - dac = spec->multiout.dac_nids[0]; /* to front */ - alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); + + if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) { + for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) { + pin = spec->autocfg.speaker_pins[i]; + if (!pin) + break; + dac = spec->multiout.extra_out_nid[0]; + if (!dac) + dac = spec->multiout.dac_nids[0]; /* to front */ + alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); + } } } From b07d4ba06a60c7c3c44e93c9a9524c00c4767018 Mon Sep 17 00:00:00 2001 From: Vitaliy Kulikov Date: Wed, 9 Mar 2011 19:47:43 -0600 Subject: [PATCH 0860/2556] ALSA: hda - fix digital mic selection in mixer on 92HD8X codecs commit 094a42452abd5564429045e210281c6d22e67fca upstream. When the mux for digital mic is different from the mux for other mics, the current auto-parser doesn't handle them in a right way but provides only one mic. This patch fixes the issue. Signed-off-by: Vitaliy Kulikov Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_sigmatel.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index bd7b123f64407..052062dd523ba 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -757,7 +757,7 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e struct sigmatel_spec *spec = codec->spec; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); const struct hda_input_mux *imux = spec->input_mux; - unsigned int idx, prev_idx; + unsigned int idx, prev_idx, didx; idx = ucontrol->value.enumerated.item[0]; if (idx >= imux->num_items) @@ -769,7 +769,8 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e snd_hda_codec_write_cache(codec, spec->mux_nids[adc_idx], 0, AC_VERB_SET_CONNECT_SEL, imux->items[idx].index); - if (prev_idx >= spec->num_analog_muxes) { + if (prev_idx >= spec->num_analog_muxes && + spec->mux_nids[adc_idx] != spec->dmux_nids[adc_idx]) { imux = spec->dinput_mux; /* 0 = analog */ snd_hda_codec_write_cache(codec, @@ -779,9 +780,13 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e } } else { imux = spec->dinput_mux; + /* first dimux item is hardcoded to select analog imux, + * so lets skip it + */ + didx = idx - spec->num_analog_muxes + 1; snd_hda_codec_write_cache(codec, spec->dmux_nids[adc_idx], 0, AC_VERB_SET_CONNECT_SEL, - imux->items[idx - 1].index); + imux->items[didx].index); } spec->cur_mux[adc_idx] = idx; return 1; From aa491820d3b8cd004fb3a82fd3ad9b03aaa7ab46 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Mar 2011 12:51:11 +0100 Subject: [PATCH 0861/2556] ALSA: hda - Initialize special cases for input src in init phase commit 584c0c4c359bdac37d94157f8d7fc513d26c8328 upstream. Currently some special handling for the unusual case like dual-ADCs or a single-input-src is done in the tree-parse time in set_capture_mixer(). But this setup could be overwritten by static init verbs. This patch moves the initialization into the init phase so that such input-src setup won't be lost. Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2a1c4124f300b..acd209958ea24 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -394,6 +394,7 @@ struct alc_spec { /* other flags */ unsigned int no_analog :1; /* digital I/O only */ unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */ + unsigned int single_input_src:1; int init_amp; int codec_variant; /* flag for other variants */ @@ -3919,6 +3920,8 @@ static struct hda_amp_list alc880_lg_loopbacks[] = { * Common callbacks */ +static void alc_init_special_input_src(struct hda_codec *codec); + static int alc_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -3929,6 +3932,7 @@ static int alc_init(struct hda_codec *codec) for (i = 0; i < spec->num_init_verbs; i++) snd_hda_sequence_write(codec, spec->init_verbs[i]); + alc_init_special_input_src(codec); if (spec->init_hook) spec->init_hook(codec); @@ -5590,6 +5594,7 @@ static void fixup_single_adc(struct hda_codec *codec) spec->capsrc_nids += i; spec->adc_nids += i; spec->num_adc_nids = 1; + spec->single_input_src = 1; } } @@ -5601,6 +5606,16 @@ static void fixup_dual_adc_switch(struct hda_codec *codec) init_capsrc_for_pin(codec, spec->int_mic.pin); } +/* initialize some special cases for input sources */ +static void alc_init_special_input_src(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + if (spec->dual_adc_switch) + fixup_dual_adc_switch(codec); + else if (spec->single_input_src) + init_capsrc_for_pin(codec, spec->autocfg.inputs[0].pin); +} + static void set_capture_mixer(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -5616,7 +5631,7 @@ static void set_capture_mixer(struct hda_codec *codec) int mux = 0; int num_adcs = spec->num_adc_nids; if (spec->dual_adc_switch) - fixup_dual_adc_switch(codec); + num_adcs = 1; else if (spec->auto_mic) fixup_automic_adc(codec); else if (spec->input_mux) { @@ -5625,8 +5640,6 @@ static void set_capture_mixer(struct hda_codec *codec) else if (spec->input_mux->num_items == 1) fixup_single_adc(codec); } - if (spec->dual_adc_switch) - num_adcs = 1; spec->cap_mixer = caps[mux][num_adcs - 1]; } } From 1b088077781999b630a5bf136690d1de77b7e258 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Wed, 9 Mar 2011 18:38:57 +0100 Subject: [PATCH 0862/2556] HID: hid-magicmouse: Correct touch orientation direction commit 2d9ca4e9f393d81d8f37ed37505aecbf3a5e1bd6 upstream. The magic trackpad and mouse both report touch orientation in opposite direction to the bcm5974 driver and what is written in Documents/input/multi-touch-protocol.txt. This patch reverts the direction, so that all in-kernel devices with this feature behave the same way. Since no known application has been utilizing this information yet, it seems appropriate also for stable. Cc: Michael Poole Signed-off-by: Henrik Rydberg Acked-by: Chase Douglas Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-magicmouse.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 698e6459fd0b3..318cc40df92d6 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -258,7 +258,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda input_report_abs(input, ABS_MT_TRACKING_ID, id); input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major << 2); input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor << 2); - input_report_abs(input, ABS_MT_ORIENTATION, orientation); + input_report_abs(input, ABS_MT_ORIENTATION, -orientation); input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); @@ -397,7 +397,7 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, 15, 0, 0); input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 4, 0); input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 4, 0); - input_set_abs_params(input, ABS_MT_ORIENTATION, -32, 31, 1, 0); + input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0); /* Note: Touch Y position from the device is inverted relative * to how pointer motion is reported (and relative to how USB From 825ec8b79976710e20c69a92fb1203214f733413 Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Wed, 16 Mar 2011 14:13:53 -0300 Subject: [PATCH 0863/2556] HID: add support for Ortek PKB-1700 commit 270fdc0748bd3f7b625caff985f2fcf8e2185ec7 upstream. As reported on http://ubuntuforums.org/showthread.php?t=1594007 the PKB-1700 needs same special handling as WKB-2000. This change is originally based on patch posted by user asmoore82 on the Ubuntu forums. Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/Kconfig | 4 ++-- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-ortek.c | 3 ++- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 2560f01c1a632..3a07b4da4eca2 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -319,10 +319,10 @@ config HID_NTRIG Support for N-Trig touch screen. config HID_ORTEK - tristate "Ortek WKB-2000 wireless keyboard and mouse trackpad" + tristate "Ortek PKB-1700/WKB-2000 wireless keyboard and mouse trackpad" depends on USB_HID ---help--- - Support for Ortek WKB-2000 wireless keyboard + mouse trackpad. + Support for Ortek PKB-1700/WKB-2000 wireless keyboard + mouse trackpad. config HID_PANTHERLORD tristate "Pantherlord/GreenAsia game controller" diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index d678cf3d33d5e..295e2487102bf 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1400,6 +1400,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_16) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_17) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) }, { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 92a0d61a7379c..5f5cc9100aff9 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -466,6 +466,7 @@ #define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 #define USB_VENDOR_ID_ORTEK 0x05a4 +#define USB_DEVICE_ID_ORTEK_PKB1700 0x1700 #define USB_DEVICE_ID_ORTEK_WKB2000 0x2000 #define USB_VENDOR_ID_PANJIT 0x134c diff --git a/drivers/hid/hid-ortek.c b/drivers/hid/hid-ortek.c index e90edfc63051b..ad6faa6c0ceac 100644 --- a/drivers/hid/hid-ortek.c +++ b/drivers/hid/hid-ortek.c @@ -1,5 +1,5 @@ /* - * HID driver for Ortek WKB-2000 (wireless keyboard + mouse trackpad). + * HID driver for Ortek PKB-1700/WKB-2000 (wireless keyboard + mouse trackpad). * Fixes LogicalMaximum error in USB report description, see * http://bugzilla.kernel.org/show_bug.cgi?id=14787 * @@ -30,6 +30,7 @@ static __u8 *ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc, } static const struct hid_device_id ortek_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) }, { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, { } }; From d9a20cc85c97aea622a786ad2b3d77a010d041ec Mon Sep 17 00:00:00 2001 From: "Brandeburg, Jesse" Date: Mon, 14 Feb 2011 09:05:02 -0800 Subject: [PATCH 0864/2556] PCI: remove quirk for pre-production systems commit b99af4b002e4908d1a5cdaf424529bdf1dc69768 upstream. Revert commit 7eb93b175d4de9438a4b0af3a94a112cb5266944 Author: Yu Zhao Date: Fri Apr 3 15:18:11 2009 +0800 PCI: SR-IOV quirk for Intel 82576 NIC If BIOS doesn't allocate resources for the SR-IOV BARs, zero the Flash BAR and program the SR-IOV BARs to use the old Flash Memory Space. Please refer to Intel 82576 Gigabit Ethernet Controller Datasheet section 7.9.2.14.2 for details. http://download.intel.com/design/network/datashts/82576_Datasheet.pdf Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes This quirk was added before SR-IOV was in production and now all machines that originally had this issue alreayd have bios updates to correct the issue. The quirk itself is no longer needed and in fact causes bugs if run. Remove it. Signed-off-by: Jesse Brandeburg CC: Yu Zhao CC: Jesse Barnes Signed-off-by: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- drivers/pci/quirks.c | 52 -------------------------------------------- 1 file changed, 52 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 53a786fd0d40c..ff01bfb3cc291 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2618,58 +2618,6 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x4375, #endif /* CONFIG_PCI_MSI */ -#ifdef CONFIG_PCI_IOV - -/* - * For Intel 82576 SR-IOV NIC, if BIOS doesn't allocate resources for the - * SR-IOV BARs, zero the Flash BAR and program the SR-IOV BARs to use the - * old Flash Memory Space. - */ -static void __devinit quirk_i82576_sriov(struct pci_dev *dev) -{ - int pos, flags; - u32 bar, start, size; - - if (PAGE_SIZE > 0x10000) - return; - - flags = pci_resource_flags(dev, 0); - if ((flags & PCI_BASE_ADDRESS_SPACE) != - PCI_BASE_ADDRESS_SPACE_MEMORY || - (flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) != - PCI_BASE_ADDRESS_MEM_TYPE_32) - return; - - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); - if (!pos) - return; - - pci_read_config_dword(dev, pos + PCI_SRIOV_BAR, &bar); - if (bar & PCI_BASE_ADDRESS_MEM_MASK) - return; - - start = pci_resource_start(dev, 1); - size = pci_resource_len(dev, 1); - if (!start || size != 0x400000 || start & (size - 1)) - return; - - pci_resource_flags(dev, 1) = 0; - pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, 0); - pci_write_config_dword(dev, pos + PCI_SRIOV_BAR, start); - pci_write_config_dword(dev, pos + PCI_SRIOV_BAR + 12, start + size / 2); - - dev_info(&dev->dev, "use Flash Memory Space for SR-IOV BARs\n"); -} -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10c9, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10e6, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10e7, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10e8, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x150a, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x150d, quirk_i82576_sriov); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1518, quirk_i82576_sriov); - -#endif /* CONFIG_PCI_IOV */ - /* Allow manual resource allocation for PCI hotplug bridges * via pci=hpmemsize=nnM and pci=hpiosize=nnM parameters. For * some PCI-PCI hotplug bridges, like PLX 6254 (former HINT HB6), From 14e431382ea0365423ef90da6fbbd4eb2ceb3e6a Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 28 Feb 2011 10:45:09 +0100 Subject: [PATCH 0865/2556] PCI: add more checking to ICH region quirks commit cdb9755849fbaf2bb9c0a009ba5baa817a0f152d upstream. Per ICH4 and ICH6 specs, ACPI and GPIO regions are valid iff ACPI_EN and GPIO_EN bits are set to 1. Add checks for these bits into the quirks prior to the region creation. While at it, name the constants by macros. Signed-off-by: Jiri Slaby Cc: Bjorn Helgaas Cc: "David S. Miller" Cc: Thomas Renninger Signed-off-by: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- drivers/pci/quirks.c | 45 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index ff01bfb3cc291..9e23912c97ac9 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -533,6 +533,17 @@ static void __devinit quirk_piix4_acpi(struct pci_dev *dev) DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, quirk_piix4_acpi); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3, quirk_piix4_acpi); +#define ICH_PMBASE 0x40 +#define ICH_ACPI_CNTL 0x44 +#define ICH4_ACPI_EN 0x10 +#define ICH6_ACPI_EN 0x80 +#define ICH4_GPIOBASE 0x58 +#define ICH4_GPIO_CNTL 0x5c +#define ICH4_GPIO_EN 0x10 +#define ICH6_GPIOBASE 0x48 +#define ICH6_GPIO_CNTL 0x4c +#define ICH6_GPIO_EN 0x10 + /* * ICH4, ICH4-M, ICH5, ICH5-M ACPI: Three IO regions pointed to by longwords at * 0x40 (128 bytes of ACPI, GPIO & TCO registers) @@ -541,12 +552,21 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3, qui static void __devinit quirk_ich4_lpc_acpi(struct pci_dev *dev) { u32 region; + u8 enable; - pci_read_config_dword(dev, 0x40, ®ion); - quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, "ICH4 ACPI/GPIO/TCO"); + pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable); + if (enable & ICH4_ACPI_EN) { + pci_read_config_dword(dev, ICH_PMBASE, ®ion); + quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, + "ICH4 ACPI/GPIO/TCO"); + } - pci_read_config_dword(dev, 0x58, ®ion); - quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES+1, "ICH4 GPIO"); + pci_read_config_byte(dev, ICH4_GPIO_CNTL, &enable); + if (enable & ICH4_GPIO_EN) { + pci_read_config_dword(dev, ICH4_GPIOBASE, ®ion); + quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES + 1, + "ICH4 GPIO"); + } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, quirk_ich4_lpc_acpi); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0, quirk_ich4_lpc_acpi); @@ -562,12 +582,21 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, qui static void __devinit ich6_lpc_acpi_gpio(struct pci_dev *dev) { u32 region; + u8 enable; - pci_read_config_dword(dev, 0x40, ®ion); - quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, "ICH6 ACPI/GPIO/TCO"); + pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable); + if (enable & ICH6_ACPI_EN) { + pci_read_config_dword(dev, ICH_PMBASE, ®ion); + quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, + "ICH6 ACPI/GPIO/TCO"); + } - pci_read_config_dword(dev, 0x48, ®ion); - quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES+1, "ICH6 GPIO"); + pci_read_config_byte(dev, ICH6_GPIO_CNTL, &enable); + if (enable & ICH4_GPIO_EN) { + pci_read_config_dword(dev, ICH6_GPIOBASE, ®ion); + quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES + 1, + "ICH6 GPIO"); + } } static void __devinit ich6_lpc_generic_decode(struct pci_dev *dev, unsigned reg, const char *name, int dynsize) From a9de7fa274d3723175b8b018e26dbecfa2bbb978 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 28 Feb 2011 10:45:10 +0100 Subject: [PATCH 0866/2556] PCI: do not create quirk I/O regions below PCIBIOS_MIN_IO for ICH commit 87e3dc3855430bd254370afc79f2ed92250f5b7c upstream. Some broken BIOSes on ICH4 chipset report an ACPI region which is in conflict with legacy IDE ports when ACPI is disabled. Even though the regions overlap, IDE ports are working correctly (we cannot find out the decoding rules on chipsets). So the only problem is the reported region itself, if we don't reserve the region in the quirk everything works as expected. This patch avoids reserving any quirk regions below PCIBIOS_MIN_IO which is 0x1000. Some regions might be (and are by a fast google query) below this border, but the only difference is that they won't be reserved anymore. They should still work though the same as before. The conflicts look like (1f.0 is bridge, 1f.1 is IDE ctrl): pci 0000:00:1f.1: address space collision: [io 0x0170-0x0177] conflicts with 0000:00:1f.0 [io 0x0100-0x017f] At 0x0100 a 128 bytes long ACPI region is reported in the quirk for ICH4. ata_piix then fails to find disks because the IDE legacy ports are zeroed: ata_piix 0000:00:1f.1: device not available (can't reserve [io 0x0000-0x0007]) References: https://bugzilla.novell.com/show_bug.cgi?id=558740 Signed-off-by: Jiri Slaby Cc: Bjorn Helgaas Cc: "David S. Miller" Cc: Thomas Renninger Signed-off-by: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- drivers/pci/quirks.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 9e23912c97ac9..bd80f63784635 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -554,18 +554,30 @@ static void __devinit quirk_ich4_lpc_acpi(struct pci_dev *dev) u32 region; u8 enable; + /* + * The check for PCIBIOS_MIN_IO is to ensure we won't create a conflict + * with low legacy (and fixed) ports. We don't know the decoding + * priority and can't tell whether the legacy device or the one created + * here is really at that address. This happens on boards with broken + * BIOSes. + */ + pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable); if (enable & ICH4_ACPI_EN) { pci_read_config_dword(dev, ICH_PMBASE, ®ion); - quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, - "ICH4 ACPI/GPIO/TCO"); + region &= PCI_BASE_ADDRESS_IO_MASK; + if (region >= PCIBIOS_MIN_IO) + quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, + "ICH4 ACPI/GPIO/TCO"); } pci_read_config_byte(dev, ICH4_GPIO_CNTL, &enable); if (enable & ICH4_GPIO_EN) { pci_read_config_dword(dev, ICH4_GPIOBASE, ®ion); - quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES + 1, - "ICH4 GPIO"); + region &= PCI_BASE_ADDRESS_IO_MASK; + if (region >= PCIBIOS_MIN_IO) + quirk_io_region(dev, region, 64, + PCI_BRIDGE_RESOURCES + 1, "ICH4 GPIO"); } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, quirk_ich4_lpc_acpi); @@ -587,15 +599,19 @@ static void __devinit ich6_lpc_acpi_gpio(struct pci_dev *dev) pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable); if (enable & ICH6_ACPI_EN) { pci_read_config_dword(dev, ICH_PMBASE, ®ion); - quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, - "ICH6 ACPI/GPIO/TCO"); + region &= PCI_BASE_ADDRESS_IO_MASK; + if (region >= PCIBIOS_MIN_IO) + quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, + "ICH6 ACPI/GPIO/TCO"); } pci_read_config_byte(dev, ICH6_GPIO_CNTL, &enable); if (enable & ICH4_GPIO_EN) { pci_read_config_dword(dev, ICH6_GPIOBASE, ®ion); - quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES + 1, - "ICH6 GPIO"); + region &= PCI_BASE_ADDRESS_IO_MASK; + if (region >= PCIBIOS_MIN_IO) + quirk_io_region(dev, region, 64, + PCI_BRIDGE_RESOURCES + 1, "ICH6 GPIO"); } } From 21a07cfa00f677c1113db2235b2d372c22f02161 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 13 Jan 2011 19:47:56 +0000 Subject: [PATCH 0867/2556] PCI: sysfs: Fix failure path for addition of "vpd" attribute commit 0f12a4e29368a9476076515881d9ef4e5876c6e2 upstream. Commit 280c73d ("PCI: centralize the capabilities code in pci-sysfs.c") changed the initialisation of the "rom" and "vpd" attributes, and made the failure path for the "vpd" attribute incorrect. We must free the new attribute structure (attr), but instead we currently free dev->vpd->attr. That will normally be NULL, resulting in a memory leak, but it might be a stale pointer, resulting in a double-free. Found by inspection; compile-tested only. Signed-off-by: Ben Hutchings Signed-off-by: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci-sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index ea25e5bfcf238..c85438a367d53 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1088,7 +1088,7 @@ static int pci_create_capabilities_sysfs(struct pci_dev *dev) attr->write = write_vpd_attr; retval = sysfs_create_bin_file(&dev->dev.kobj, attr); if (retval) { - kfree(dev->vpd->attr); + kfree(attr); return retval; } dev->vpd->attr = attr; From c00ba60ea2b58503f304f4fb6bf5fc19c4959061 Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Thu, 17 Mar 2011 18:32:24 -0400 Subject: [PATCH 0868/2556] ALSA: sound/pci/asihpi: check adapter index in hpi_ioctl commit 4a122c10fbfe9020df469f0f669da129c5757671 upstream. The user-supplied index into the adapters array needs to be checked, or an out-of-bounds kernel pointer could be accessed and used, leading to potentially exploitable memory corruption. Signed-off-by: Dan Rosenberg Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/asihpi/hpioctl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index 22dbd91811a4e..448dd01943f50 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -155,6 +155,11 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) goto out; } + if (hm->h.adapter_index >= HPI_MAX_ADAPTERS) { + err = -EINVAL; + goto out; + } + pa = &adapters[hm->h.adapter_index]; hr->h.size = 0; if (hm->h.object == HPI_OBJ_SUBSYSTEM) { From 0a1f8751a2f5f5ebf8ca0dea0ace16e414fb83e1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Mar 2011 07:31:53 +0100 Subject: [PATCH 0869/2556] ALSA: aloop - Fix possible IRQ lock inversion commit 98d21df431ad55281e1abf780f8d51e3391900b2 upstream. loopback_pos_update() can be called in the timer callback, thus the lock held should be irq-safe. Otherwise you'll get AB/BA deadlock together with substream->self_group.lock. Reported-and-tested-by: Knut Petersen Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/drivers/aloop.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 12b44b0b67771..a0da7755fceaa 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -482,8 +482,9 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable) cable->streams[SNDRV_PCM_STREAM_CAPTURE]; unsigned long delta_play = 0, delta_capt = 0; unsigned int running; + unsigned long flags; - spin_lock(&cable->lock); + spin_lock_irqsave(&cable->lock, flags); running = cable->running ^ cable->pause; if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { delta_play = jiffies - dpcm_play->last_jiffies; @@ -495,10 +496,8 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable) dpcm_capt->last_jiffies += delta_capt; } - if (delta_play == 0 && delta_capt == 0) { - spin_unlock(&cable->lock); - return running; - } + if (delta_play == 0 && delta_capt == 0) + goto unlock; if (delta_play > delta_capt) { loopback_bytepos_update(dpcm_play, delta_play - delta_capt, @@ -510,14 +509,14 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable) delta_capt = delta_play; } - if (delta_play == 0 && delta_capt == 0) { - spin_unlock(&cable->lock); - return running; - } + if (delta_play == 0 && delta_capt == 0) + goto unlock; + /* note delta_capt == delta_play at this moment */ loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY); loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY); - spin_unlock(&cable->lock); + unlock: + spin_unlock_irqrestore(&cable->lock, flags); return running; } From 4fced4e86917193756967aeb27673f9e490a5afb Mon Sep 17 00:00:00 2001 From: Przemyslaw Bruski Date: Sun, 13 Mar 2011 16:18:56 +0100 Subject: [PATCH 0870/2556] ALSA: ctxfi - Fix incorrect SPDIF status bit mask commit 4c1847e884efddcc3ede371f7839e5e65b25c34d upstream. SPDIF status mask creation was incorrect. Signed-off-by: Przemyslaw Bruski Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/ctxfi/ctatc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 1bff80cde0a2f..b9321544c31c4 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -869,7 +869,7 @@ spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm) mutex_lock(&atc->atc_mutex); dao->ops->get_spos(dao, &status); if (((status >> 24) & IEC958_AES3_CON_FS) != iec958_con_fs) { - status &= ((~IEC958_AES3_CON_FS) << 24); + status &= ~(IEC958_AES3_CON_FS << 24); status |= (iec958_con_fs << 24); dao->ops->set_spos(dao, status); dao->ops->commit_write(dao); From d25bd8fa21b0272d6fecf32ecfb9ae13f6db3ecd Mon Sep 17 00:00:00 2001 From: Przemyslaw Bruski Date: Sun, 13 Mar 2011 16:18:57 +0100 Subject: [PATCH 0871/2556] ALSA: ctxfi - Fix SPDIF status retrieval commit f164753a263bfd2daaf3e0273b179de7e099c57d upstream. SDPIF status retrieval always returned the default settings instead of the actual ones. Signed-off-by: Przemyslaw Bruski Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/ctxfi/ctmixer.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index 15c1e7271ea88..c3519ff42fbbf 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -566,19 +566,6 @@ static int ct_spdif_get_mask(struct snd_kcontrol *kcontrol, return 0; } -static int ct_spdif_default_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - unsigned int status = SNDRV_PCM_DEFAULT_CON_SPDIF; - - ucontrol->value.iec958.status[0] = (status >> 0) & 0xff; - ucontrol->value.iec958.status[1] = (status >> 8) & 0xff; - ucontrol->value.iec958.status[2] = (status >> 16) & 0xff; - ucontrol->value.iec958.status[3] = (status >> 24) & 0xff; - - return 0; -} - static int ct_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -586,6 +573,10 @@ static int ct_spdif_get(struct snd_kcontrol *kcontrol, unsigned int status; atc->spdif_out_get_status(atc, &status); + + if (status == 0) + status = SNDRV_PCM_DEFAULT_CON_SPDIF; + ucontrol->value.iec958.status[0] = (status >> 0) & 0xff; ucontrol->value.iec958.status[1] = (status >> 8) & 0xff; ucontrol->value.iec958.status[2] = (status >> 16) & 0xff; @@ -629,7 +620,7 @@ static struct snd_kcontrol_new iec958_default_ctl = { .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), .count = 1, .info = ct_spdif_info, - .get = ct_spdif_default_get, + .get = ct_spdif_get, .put = ct_spdif_put, .private_value = MIXER_IEC958_DEFAULT }; From cd0f8c1141e60e49721cb077616c7532b79b166b Mon Sep 17 00:00:00 2001 From: Przemyslaw Bruski Date: Sun, 13 Mar 2011 16:18:58 +0100 Subject: [PATCH 0872/2556] ALSA: ctxfi - Clear input settings before initialization commit efed5f26664f93991c929d5bb343e65f900d72bc upstream. Clear input settings before initialization. Signed-off-by: Przemyslaw Bruski Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/ctxfi/ctdaio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index af56eb949bded..47d9ea97de02e 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -176,6 +176,7 @@ static int dao_set_left_input(struct dao *dao, struct rsc *input) if (!entry) return -ENOMEM; + dao->ops->clear_left_input(dao); /* Program master and conjugate resources */ input->ops->master(input); daio->rscl.ops->master(&daio->rscl); @@ -204,6 +205,7 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input) if (!entry) return -ENOMEM; + dao->ops->clear_right_input(dao); /* Program master and conjugate resources */ input->ops->master(input); daio->rscr.ops->master(&daio->rscr); From 815cd06b8a79f491f14315c4e4a05285c7b6519e Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Fri, 11 Mar 2011 11:05:38 +0200 Subject: [PATCH 0873/2556] ASoC: PXA: Z2: Fix codec pin name commit 64c25a92e865f06ad8782fbdaa1e2a97d50acf73 upstream. MONO was renamed to MONO1. Signed-off-by: Vasily Khoruzhick Acked-by: Liam Girdwood Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/pxa/z2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c index 3ceaef68e01de..838a0d540c5a3 100644 --- a/sound/soc/pxa/z2.c +++ b/sound/soc/pxa/z2.c @@ -147,7 +147,7 @@ static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_disable_pin(dapm, "LINPUT3"); snd_soc_dapm_disable_pin(dapm, "RINPUT3"); snd_soc_dapm_disable_pin(dapm, "OUT3"); - snd_soc_dapm_disable_pin(dapm, "MONO"); + snd_soc_dapm_disable_pin(dapm, "MONO1"); /* Add z2 specific widgets */ snd_soc_dapm_new_controls(dapm, wm8750_dapm_widgets, From 1e0ae50a204440434970b0e5118b959a3dc5406e Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 2 Mar 2011 15:52:51 -0800 Subject: [PATCH 0874/2556] target: Fix volume size misreporting for volumes > 2TB commit 904f0bc482201fa86e75c330d79dfd11be494cf8 upstream. the target infrastructure fails to send the correct conventional size to READ_CAPACITY that force a retry with READ_CAPACITY_16, which reads the capacity for devices > 2TB. Fix by adding the correct return to trigger RC(16). Reported-by: Ben Jarvis Signed-off-by: Signed-off-by: Nicholas A. Bellinger Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_cdb.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c index 366080baf4743..7f19c8b7b84c7 100644 --- a/drivers/target/target_core_cdb.c +++ b/drivers/target/target_core_cdb.c @@ -667,7 +667,13 @@ target_emulate_readcapacity(struct se_cmd *cmd) { struct se_device *dev = SE_DEV(cmd); unsigned char *buf = cmd->t_task->t_task_buf; - u32 blocks = dev->transport->get_blocks(dev); + unsigned long long blocks_long = dev->transport->get_blocks(dev); + u32 blocks; + + if (blocks_long >= 0x00000000ffffffff) + blocks = 0xffffffff; + else + blocks = (u32)blocks_long; buf[0] = (blocks >> 24) & 0xff; buf[1] = (blocks >> 16) & 0xff; From c56f41bb40358ff8f5fda706bb9d48bcbf157180 Mon Sep 17 00:00:00 2001 From: Joseph Gruher Date: Wed, 5 Jan 2011 16:00:21 -0500 Subject: [PATCH 0875/2556] scsi_dh_alua: fix deadlock in stpg_endio commit ed0f36bc5719b25659b637f80ceea85494b84502 upstream. The use of blk_execute_rq_nowait() implies __blk_put_request() is needed in stpg_endio() rather than blk_put_request() -- blk_finish_request() is called with queue lock already held. Signed-off-by: Joseph Gruher Signed-off-by: Ilgu Hong Signed-off-by: Mike Snitzer Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/device_handler/scsi_dh_alua.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 6b729324b8d37..30f2b333ccbdb 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -285,7 +285,8 @@ static void stpg_endio(struct request *req, int error) print_alua_state(h->state)); } done: - blk_put_request(req); + req->end_io_data = NULL; + __blk_put_request(req->q, req); if (h->callback_fn) { h->callback_fn(h->callback_data, err); h->callback_fn = h->callback_data = NULL; From ae4e880485b117c83d509e5324b9b16118cc26ca Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 15 Mar 2011 19:56:30 -0400 Subject: [PATCH 0876/2556] SUNRPC: Ensure we always run the tk_callback before tk_action commit e020c6800c9621a77223bf2c1ff68180e41e8ebf upstream. This fixes a race in which the task->tk_callback() puts the rpc_task to sleep, setting a new callback. Under certain circumstances, the current code may end up executing the task->tk_action before it gets round to the callback. Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/sched.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 59e599498e37f..17c3e3aade292 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -637,14 +637,12 @@ static void __rpc_execute(struct rpc_task *task) save_callback = task->tk_callback; task->tk_callback = NULL; save_callback(task); - } - - /* - * Perform the next FSM step. - * tk_action may be NULL when the task has been killed - * by someone else. - */ - if (!RPC_IS_QUEUED(task)) { + } else { + /* + * Perform the next FSM step. + * tk_action may be NULL when the task has been killed + * by someone else. + */ if (task->tk_action == NULL) break; task->tk_action(task); From b2e485383201e8f7db6fef75f63b8e590257b36f Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Thu, 17 Mar 2011 18:54:23 +0300 Subject: [PATCH 0877/2556] RPC: killing RPC tasks races fixed commit 8e26de238fd794c8ea56a5c98bf67c40cfeb051d upstream. RPC task RPC_TASK_QUEUED bit is set must be checked before trying to wake up task rpc_killall_tasks() because task->tk_waitqueue can not be set (equal to NULL). Also, as Trond Myklebust mentioned, such approach (instead of checking tk_waitqueue to NULL) allows us to "optimise away the call to rpc_wake_up_queued_task() altogether for those tasks that aren't queued". Here is an example of dereferencing of tk_waitqueue equal to NULL: CPU 0 CPU 1 CPU 2 -------------------- --------------------- -------------------------- nfs4_run_open_task rpc_run_task rpc_execute rpc_set_active rpc_make_runnable (waiting) rpc_async_schedule nfs4_open_prepare nfs_wait_on_sequence nfs_umount_begin rpc_killall_tasks rpc_wake_up_task rpc_wake_up_queued_task spin_lock(tk_waitqueue == NULL) BUG() rpc_sleep_on spin_lock(&q->lock) __rpc_sleep_on task->tk_waitqueue = q Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/clnt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 57d344cf2256b..35d046b588f3f 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -436,7 +436,9 @@ void rpc_killall_tasks(struct rpc_clnt *clnt) if (!(rovr->tk_flags & RPC_TASK_KILLED)) { rovr->tk_flags |= RPC_TASK_KILLED; rpc_exit(rovr, -EIO); - rpc_wake_up_queued_task(rovr->tk_waitqueue, rovr); + if (RPC_IS_QUEUED(rovr)) + rpc_wake_up_queued_task(rovr->tk_waitqueue, + rovr); } } spin_unlock(&clnt->cl_lock); From 78bd43a244bc4e07148203736ffba6e7dba06f15 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 7 Mar 2011 21:27:08 +0100 Subject: [PATCH 0878/2556] perf: Fix the software events state check commit 91b2f482e62ad0d444222253026a5cbca28c4ab9 upstream. Fix the mistakenly inverted check of events state. Signed-off-by: Frederic Weisbecker Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Stephane Eranian LKML-Reference: <1299529629-18280-1-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/perf_event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 656222fcf767e..853705f6d2943 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -4567,7 +4567,7 @@ static int perf_exclude_event(struct perf_event *event, struct pt_regs *regs) { if (event->hw.state & PERF_HES_STOPPED) - return 0; + return 1; if (regs) { if (event->attr.exclude_user && user_mode(regs)) From d424325648f5debcb6bf9bf3c31cb9822381d4f8 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 7 Mar 2011 21:27:09 +0100 Subject: [PATCH 0879/2556] perf: Handle stopped state with tracepoints commit a0f7d0f7fc02465bb9758501f611f63381792996 upstream. We toggle the state from start and stop callbacks but actually don't check it when the event triggers. Do it so that these callbacks actually work. Signed-off-by: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Stephane Eranian Signed-off-by: Peter Zijlstra LKML-Reference: <1299529629-18280-2-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/perf_event.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 853705f6d2943..b22a2ef75c86f 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -4923,6 +4923,8 @@ static int perf_tp_event_match(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) { + if (event->hw.state & PERF_HES_STOPPED) + return 0; /* * All tracepoints are from kernel-space. */ From b7a5e056c07dbbef332f6833f70c03200e627535 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 9 Mar 2011 14:38:42 +1100 Subject: [PATCH 0880/2556] perf, powerpc: Handle events that raise an exception without overflowing commit 0837e3242c73566fc1c0196b4ec61779c25ffc93 upstream. Events on POWER7 can roll back if a speculative event doesn't eventually complete. Unfortunately in some rare cases they will raise a performance monitor exception. We need to catch this to ensure we reset the PMC. In all cases the PMC will be 256 or less cycles from overflow. Signed-off-by: Anton Blanchard Signed-off-by: Peter Zijlstra LKML-Reference: <20110309143842.6c22845e@kryten> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/include/asm/reg.h | 1 + arch/powerpc/kernel/perf_event.c | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 125fc1ad665d0..7626fa78e1f8b 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -880,6 +880,7 @@ #define PV_970 0x0039 #define PV_POWER5 0x003A #define PV_POWER5p 0x003B +#define PV_POWER7 0x003F #define PV_970FX 0x003C #define PV_630 0x0040 #define PV_630p 0x0041 diff --git a/arch/powerpc/kernel/perf_event.c b/arch/powerpc/kernel/perf_event.c index ab6f6beadb572..97e0ae414940e 100644 --- a/arch/powerpc/kernel/perf_event.c +++ b/arch/powerpc/kernel/perf_event.c @@ -1269,6 +1269,28 @@ unsigned long perf_instruction_pointer(struct pt_regs *regs) return ip; } +static bool pmc_overflow(unsigned long val) +{ + if ((int)val < 0) + return true; + + /* + * Events on POWER7 can roll back if a speculative event doesn't + * eventually complete. Unfortunately in some rare cases they will + * raise a performance monitor exception. We need to catch this to + * ensure we reset the PMC. In all cases the PMC will be 256 or less + * cycles from overflow. + * + * We only do this if the first pass fails to find any overflowing + * PMCs because a user might set a period of less than 256 and we + * don't want to mistakenly reset them. + */ + if (__is_processor(PV_POWER7) && ((0x80000000 - val) <= 256)) + return true; + + return false; +} + /* * Performance monitor interrupt stuff */ @@ -1316,7 +1338,7 @@ static void perf_event_interrupt(struct pt_regs *regs) if (is_limited_pmc(i + 1)) continue; val = read_pmc(i + 1); - if ((int)val < 0) + if (pmc_overflow(val)) write_pmc(i + 1, 0); } } From 98d56e6785b71961d3210d53f1b95f7566d220a6 Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Tue, 15 Mar 2011 19:16:40 -0700 Subject: [PATCH 0881/2556] perf tools: Version incorrect with some versions of grep commit 58d406ed6a5f1ca4bc1dba5390b718c67847fa5f upstream. Some versions of grep don't treat '\s' properly. When building perf on such systems and using a kernel tarball the perf version is unable to be determined from the main kernel Makefile and the user is left with a version of '..'. Replacing the use of '\s' with '[[:space:]]', which should work in all grep versions, gives a usable version number. Reported-by: Tapan Dhimant Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Tapan Dhimant Cc: linux-kernel@vger.kernel.org LKML-Reference: <1300241800-30281-1-git-send-email-johunt@akamai.com> Signed-off-by: Josh Hunt Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Greg Kroah-Hartman --- tools/perf/util/PERF-VERSION-GEN | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index 97d76562a1a09..26d4d3fd6deb2 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN @@ -23,10 +23,10 @@ if test -d ../../.git -o -f ../../.git && then VN=$(echo "$VN" | sed -e 's/-/./g'); else - eval `grep '^VERSION\s*=' ../../Makefile|tr -d ' '` - eval `grep '^PATCHLEVEL\s*=' ../../Makefile|tr -d ' '` - eval `grep '^SUBLEVEL\s*=' ../../Makefile|tr -d ' '` - eval `grep '^EXTRAVERSION\s*=' ../../Makefile|tr -d ' '` + eval $(grep '^VERSION[[:space:]]*=' ../../Makefile|tr -d ' ') + eval $(grep '^PATCHLEVEL[[:space:]]*=' ../../Makefile|tr -d ' ') + eval $(grep '^SUBLEVEL[[:space:]]*=' ../../Makefile|tr -d ' ') + eval $(grep '^EXTRAVERSION[[:space:]]*=' ../../Makefile|tr -d ' ') VN="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}${EXTRAVERSION}" fi From dae65240407e55c12a2e2cbbee7bf1e99674fadf Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Fri, 4 Mar 2011 16:04:08 -0600 Subject: [PATCH 0882/2556] ext3: Always set dx_node's fake_dirent explicitly. commit d7433142b63d727b5a217c37b1a1468b116a9771 upstream. (crossport of 1f7bebb9e911d870fa8f997ddff838e82b5715ea by Andreas Schlick ) When ext3_dx_add_entry() has to split an index node, it has to ensure that name_len of dx_node's fake_dirent is also zero, because otherwise e2fsck won't recognise it as an intermediate htree node and consider the htree to be corrupted. Signed-off-by: Eric Sandeen Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/ext3/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index b27ba71810ecd..75c968eaf90d2 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -1540,8 +1540,8 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry, goto cleanup; node2 = (struct dx_node *)(bh2->b_data); entries2 = node2->entries; + memset(&node2->fake, 0, sizeof(struct fake_dirent)); node2->fake.rec_len = ext3_rec_len_to_disk(sb->s_blocksize); - node2->fake.inode = 0; BUFFER_TRACE(frame->bh, "get_write_access"); err = ext3_journal_get_write_access(handle, frame->bh); if (err) From 423d508f7b5489ab83fabd4ed36a606825da921b Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Tue, 15 Mar 2011 13:27:16 -0600 Subject: [PATCH 0883/2556] call_function_many: fix list delete vs add race commit e6cd1e07a185d5f9b0aa75e020df02d3c1c44940 upstream. Peter pointed out there was nothing preventing the list_del_rcu in smp_call_function_interrupt from running before the list_add_rcu in smp_call_function_many. Fix this by not setting refs until we have gotten the lock for the list. Take advantage of the wmb in list_add_rcu to save an explicit additional one. I tried to force this race with a udelay before the lock & list_add and by mixing all 64 online cpus with just 3 random cpus in the mask, but was unsuccessful. Still, inspection shows a valid race, and the fix is a extension of the existing protection window in the current code. Reported-by: Peter Zijlstra Signed-off-by: Milton Miller Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/smp.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/kernel/smp.c b/kernel/smp.c index 9910744f0856c..aaeee20c56344 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -491,14 +491,15 @@ void smp_call_function_many(const struct cpumask *mask, cpumask_clear_cpu(this_cpu, data->cpumask); /* - * To ensure the interrupt handler gets an complete view - * we order the cpumask and refs writes and order the read - * of them in the interrupt handler. In addition we may - * only clear our own cpu bit from the mask. + * We reuse the call function data without waiting for any grace + * period after some other cpu removes it from the global queue. + * This means a cpu might find our data block as it is writen. + * The interrupt handler waits until it sees refs filled out + * while its cpu mask bit is set; here we may only clear our + * own cpu mask bit, and must wait to set refs until we are sure + * previous writes are complete and we have obtained the lock to + * add the element to the queue. */ - smp_wmb(); - - atomic_set(&data->refs, cpumask_weight(data->cpumask)); raw_spin_lock_irqsave(&call_function.lock, flags); /* @@ -507,6 +508,11 @@ void smp_call_function_many(const struct cpumask *mask, * will not miss any other list entries: */ list_add_rcu(&data->csd.list, &call_function.queue); + /* + * We rely on the wmb() in list_add_rcu to order the writes + * to func, data, and cpumask before this write to refs. + */ + atomic_set(&data->refs, cpumask_weight(data->cpumask)); raw_spin_unlock_irqrestore(&call_function.lock, flags); /* From 8cce06d885fad20bd42e69a4e55490f4e4f3b302 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Tue, 15 Mar 2011 13:27:16 -0600 Subject: [PATCH 0884/2556] call_function_many: add missing ordering commit 45a5791920ae643eafc02e2eedef1a58e341b736 upstream. Paul McKenney's review pointed out two problems with the barriers in the 2.6.38 update to the smp call function many code. First, a barrier that would force the func and info members of data to be visible before their consumption in the interrupt handler was missing. This can be solved by adding a smp_wmb between setting the func and info members and setting setting the cpumask; this will pair with the existing and required smp_rmb ordering the cpumask read before the read of refs. This placement avoids the need a second smp_rmb in the interrupt handler which would be executed on each of the N cpus executing the call request. (I was thinking this barrier was present but was not). Second, the previous write to refs (establishing the zero that we the interrupt handler was testing from all cpus) was performed by a third party cpu. This would invoke transitivity which, as a recient or concurrent addition to memory-barriers.txt now explicitly states, would require a full smp_mb(). However, we know the cpumask will only be set by one cpu (the data owner) and any preivous iteration of the mask would have cleared by the reading cpu. By redundantly writing refs to 0 on the owning cpu before the smp_wmb, the write to refs will follow the same path as the writes that set the cpumask, which in turn allows us to keep the barrier in the interrupt handler a smp_rmb instead of promoting it to a smp_mb (which will be be executed by N cpus for each of the possible M elements on the list). I moved and expanded the comment about our (ab)use of the rcu list primitives for the concurrent walk earlier into this function. I considered moving the first two paragraphs to the queue list head and lock, but felt it would have been too disconected from the code. Cc: Paul McKinney Signed-off-by: Milton Miller Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/smp.c | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/kernel/smp.c b/kernel/smp.c index aaeee20c56344..7c6ded5effd96 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -483,23 +483,42 @@ void smp_call_function_many(const struct cpumask *mask, data = &__get_cpu_var(cfd_data); csd_lock(&data->csd); - BUG_ON(atomic_read(&data->refs) || !cpumask_empty(data->cpumask)); - data->csd.func = func; - data->csd.info = info; - cpumask_and(data->cpumask, mask, cpu_online_mask); - cpumask_clear_cpu(this_cpu, data->cpumask); + /* This BUG_ON verifies our reuse assertions and can be removed */ + BUG_ON(atomic_read(&data->refs) || !cpumask_empty(data->cpumask)); /* + * The global call function queue list add and delete are protected + * by a lock, but the list is traversed without any lock, relying + * on the rcu list add and delete to allow safe concurrent traversal. * We reuse the call function data without waiting for any grace * period after some other cpu removes it from the global queue. - * This means a cpu might find our data block as it is writen. - * The interrupt handler waits until it sees refs filled out - * while its cpu mask bit is set; here we may only clear our - * own cpu mask bit, and must wait to set refs until we are sure - * previous writes are complete and we have obtained the lock to - * add the element to the queue. + * This means a cpu might find our data block as it is being + * filled out. + * + * We hold off the interrupt handler on the other cpu by + * ordering our writes to the cpu mask vs our setting of the + * refs counter. We assert only the cpu owning the data block + * will set a bit in cpumask, and each bit will only be cleared + * by the subject cpu. Each cpu must first find its bit is + * set and then check that refs is set indicating the element is + * ready to be processed, otherwise it must skip the entry. + * + * On the previous iteration refs was set to 0 by another cpu. + * To avoid the use of transitivity, set the counter to 0 here + * so the wmb will pair with the rmb in the interrupt handler. */ + atomic_set(&data->refs, 0); /* convert 3rd to 1st party write */ + + data->csd.func = func; + data->csd.info = info; + + /* Ensure 0 refs is visible before mask. Also orders func and info */ + smp_wmb(); + + /* We rely on the "and" being processed before the store */ + cpumask_and(data->cpumask, mask, cpu_online_mask); + cpumask_clear_cpu(this_cpu, data->cpumask); raw_spin_lock_irqsave(&call_function.lock, flags); /* @@ -509,8 +528,9 @@ void smp_call_function_many(const struct cpumask *mask, */ list_add_rcu(&data->csd.list, &call_function.queue); /* - * We rely on the wmb() in list_add_rcu to order the writes - * to func, data, and cpumask before this write to refs. + * We rely on the wmb() in list_add_rcu to complete our writes + * to the cpumask before this write to refs, which indicates + * data is on the list and is ready to be processed. */ atomic_set(&data->refs, cpumask_weight(data->cpumask)); raw_spin_unlock_irqrestore(&call_function.lock, flags); From 2aebcc99779340d5aa6cb70136bad6fc170f2e72 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Tue, 15 Mar 2011 13:27:17 -0600 Subject: [PATCH 0885/2556] smp_call_function_many: handle concurrent clearing of mask commit 723aae25d5cdb09962901d36d526b44d4be1051c upstream. Mike Galbraith reported finding a lockup ("perma-spin bug") where the cpumask passed to smp_call_function_many was cleared by other cpu(s) while a cpu was preparing its call_data block, resulting in no cpu to clear the last ref and unlock the block. Having cpus clear their bit asynchronously could be useful on a mask of cpus that might have a translation context, or cpus that need a push to complete an rcu window. Instead of adding a BUG_ON and requiring yet another cpumask copy, just detect the race and handle it. Note: arch_send_call_function_ipi_mask must still handle an empty cpumask because the data block is globally visible before the that arch callback is made. And (obviously) there are no guarantees to which cpus are notified if the mask is changed during the call; only cpus that were online and had their mask bit set during the whole call are guaranteed to be called. Reported-by: Mike Galbraith Reported-by: Jan Beulich Acked-by: Jan Beulich Signed-off-by: Milton Miller Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/smp.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/kernel/smp.c b/kernel/smp.c index 7c6ded5effd96..954548906afbe 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -450,7 +450,7 @@ void smp_call_function_many(const struct cpumask *mask, { struct call_function_data *data; unsigned long flags; - int cpu, next_cpu, this_cpu = smp_processor_id(); + int refs, cpu, next_cpu, this_cpu = smp_processor_id(); /* * Can deadlock when called with interrupts disabled. @@ -461,7 +461,7 @@ void smp_call_function_many(const struct cpumask *mask, WARN_ON_ONCE(cpu_online(this_cpu) && irqs_disabled() && !oops_in_progress && !early_boot_irqs_disabled); - /* So, what's a CPU they want? Ignoring this one. */ + /* Try to fastpath. So, what's a CPU they want? Ignoring this one. */ cpu = cpumask_first_and(mask, cpu_online_mask); if (cpu == this_cpu) cpu = cpumask_next_and(cpu, mask, cpu_online_mask); @@ -519,6 +519,13 @@ void smp_call_function_many(const struct cpumask *mask, /* We rely on the "and" being processed before the store */ cpumask_and(data->cpumask, mask, cpu_online_mask); cpumask_clear_cpu(this_cpu, data->cpumask); + refs = cpumask_weight(data->cpumask); + + /* Some callers race with other cpus changing the passed mask */ + if (unlikely(!refs)) { + csd_unlock(&data->csd); + return; + } raw_spin_lock_irqsave(&call_function.lock, flags); /* @@ -532,7 +539,7 @@ void smp_call_function_many(const struct cpumask *mask, * to the cpumask before this write to refs, which indicates * data is on the list and is ready to be processed. */ - atomic_set(&data->refs, cpumask_weight(data->cpumask)); + atomic_set(&data->refs, refs); raw_spin_unlock_irqrestore(&call_function.lock, flags); /* From e05f640c68c7982e799a7a2aec118ab52001cd21 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Wed, 16 Mar 2011 11:37:29 +0800 Subject: [PATCH 0886/2556] x86: Flush TLB if PGD entry is changed in i386 PAE mode commit 4981d01eada5354d81c8929d5b2836829ba3df7b upstream. According to intel CPU manual, every time PGD entry is changed in i386 PAE mode, we need do a full TLB flush. Current code follows this and there is comment for this too in the code. But current code misses the multi-threaded case. A changed page table might be used by several CPUs, every such CPU should flush TLB. Usually this isn't a problem, because we prepopulate all PGD entries at process fork. But when the process does munmap and follows new mmap, this issue will be triggered. When it happens, some CPUs keep doing page faults: http://marc.info/?l=linux-kernel&m=129915020508238&w=2 Reported-by: Yasunori Goto Tested-by: Yasunori Goto Reviewed-by: Rik van Riel Signed-off-by: Shaohua Li Cc: Mallick Asit K Cc: Linus Torvalds Cc: Andrew Morton Cc: linux-mm LKML-Reference: <1300246649.2337.95.camel@sli10-conroe> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/pgtable-3level.h | 11 +++-------- arch/x86/mm/pgtable.c | 3 +-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/arch/x86/include/asm/pgtable-3level.h b/arch/x86/include/asm/pgtable-3level.h index 94b979d1b58db..effff47a3c828 100644 --- a/arch/x86/include/asm/pgtable-3level.h +++ b/arch/x86/include/asm/pgtable-3level.h @@ -69,8 +69,6 @@ static inline void native_pmd_clear(pmd_t *pmd) static inline void pud_clear(pud_t *pudp) { - unsigned long pgd; - set_pud(pudp, __pud(0)); /* @@ -79,13 +77,10 @@ static inline void pud_clear(pud_t *pudp) * section 8.1: in PAE mode we explicitly have to flush the * TLB via cr3 if the top-level pgd is changed... * - * Make sure the pud entry we're updating is within the - * current pgd to avoid unnecessary TLB flushes. + * Currently all places where pud_clear() is called either have + * flush_tlb_mm() followed or don't need TLB flush (x86_64 code or + * pud_clear_bad()), so we don't need TLB flush here. */ - pgd = read_cr3(); - if (__pa(pudp) >= pgd && __pa(pudp) < - (pgd + sizeof(pgd_t)*PTRS_PER_PGD)) - write_cr3(pgd); } #ifdef CONFIG_SMP diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c index 0113d19c8aa60..8573b83a63d03 100644 --- a/arch/x86/mm/pgtable.c +++ b/arch/x86/mm/pgtable.c @@ -168,8 +168,7 @@ void pud_populate(struct mm_struct *mm, pud_t *pudp, pmd_t *pmd) * section 8.1: in PAE mode we explicitly have to flush the * TLB via cr3 if the top-level pgd is changed... */ - if (mm == current->active_mm) - write_cr3(read_cr3()); + flush_tlb_mm(mm); } #else /* !CONFIG_X86_PAE */ From 81eb08593f7d2846a7527bfcc3ae6366f5d53c37 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 11 Mar 2011 00:27:34 -0800 Subject: [PATCH 0887/2556] HID: ACRUX - activate the device immediately after binding commit 0ae43810976bc969ee158510c4acbe70ed136e61 upstream. This device does not tolerate delayed opening and goes into a coma if we try to that. Ubuntu even has a crutch for udev that opened the device upon seeing it for the first time, but it did not work if we happened to boot with the device attached, since by the time userspace got around opening the device it was too late. Let's start the device immediately to deal with this issue. Reported-by: Sergei Kolzun Signed-off-by: Dmitry Torokhov Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/Kconfig | 10 ++++++++-- drivers/hid/Makefile | 2 +- drivers/hid/hid-axff.c | 31 ++++++++++++++++++++++++++++--- drivers/hid/hid-core.c | 2 -- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 3a07b4da4eca2..ae445b1dffaa2 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -68,9 +68,15 @@ config HID_A4TECH ---help--- Support for A4 tech X5 and WOP-35 / Trust 450L mice. -config HID_ACRUX_FF - tristate "ACRUX force feedback" +config HID_ACRUX + tristate "ACRUX game controller support" depends on USB_HID + ---help--- + Say Y here if you want to enable support for ACRUX game controllers. + +config HID_ACRUX_FF + tristate "ACRUX force feedback support" + depends on HID_ACRUX select INPUT_FF_MEMLESS ---help--- Say Y here if you want to enable force feedback support for ACRUX diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 6efc2a0370ad1..13e624867d193 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -27,7 +27,7 @@ endif obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o -obj-$(CONFIG_HID_ACRUX_FF) += hid-axff.o +obj-$(CONFIG_HID_ACRUX) += hid-axff.o obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_BELKIN) += hid-belkin.o obj-$(CONFIG_HID_CANDO) += hid-cando.o diff --git a/drivers/hid/hid-axff.c b/drivers/hid/hid-axff.c index e5b961d6ff220..b4554288de00b 100644 --- a/drivers/hid/hid-axff.c +++ b/drivers/hid/hid-axff.c @@ -33,6 +33,8 @@ #include #include "hid-ids.h" + +#ifdef CONFIG_HID_ACRUX_FF #include "usbhid/usbhid.h" struct axff_device { @@ -109,6 +111,12 @@ static int axff_init(struct hid_device *hid) kfree(axff); return error; } +#else +static inline int axff_init(struct hid_device *hid) +{ + return 0; +} +#endif static int ax_probe(struct hid_device *hdev, const struct hid_device_id *id) { @@ -139,9 +147,25 @@ static int ax_probe(struct hid_device *hdev, const struct hid_device_id *id) error); } + /* + * We need to start polling device right away, otherwise + * it will go into a coma. + */ + error = hid_hw_open(hdev); + if (error) { + dev_err(&hdev->dev, "hw open failed\n"); + return error; + } + return 0; } +static void ax_remove(struct hid_device *hdev) +{ + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + static const struct hid_device_id ax_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802), }, { } @@ -149,9 +173,10 @@ static const struct hid_device_id ax_devices[] = { MODULE_DEVICE_TABLE(hid, ax_devices); static struct hid_driver ax_driver = { - .name = "acrux", - .id_table = ax_devices, - .probe = ax_probe, + .name = "acrux", + .id_table = ax_devices, + .probe = ax_probe, + .remove = ax_remove, }; static int __init ax_init(void) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 295e2487102bf..ee27b108c4c78 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1256,9 +1256,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) }, { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) }, { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) }, -#if defined(CONFIG_HID_ACRUX_FF) || defined(CONFIG_HID_ACRUX_FF_MODULE) { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) }, -#endif { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, From ed3983bffc5775e4b3a08a72e55530158eee0e00 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Thu, 24 Feb 2011 19:30:59 +0100 Subject: [PATCH 0888/2556] HID: Do not create input devices for feature reports commit f635bd11c8d332d917fb9a4cad3071b2357d5b2a upstream. When the multi input quirk is set, there is a new input device created for every feature report. Since the idea is to present features per hid device, not per input device, revert back to the original report loop and change the feature_mapping() callback to not take the input device as argument. Signed-off-by: Henrik Rydberg Tested-by: Benjamin Tissoires Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-input.c | 30 +++++++++++++++++++++--------- drivers/hid/hid-multitouch.c | 2 +- include/linux/hid.h | 2 +- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 7f552bfad32c0..ebcc02a1c1c94 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -290,14 +290,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel goto ignore; } - if (field->report_type == HID_FEATURE_REPORT) { - if (device->driver->feature_mapping) { - device->driver->feature_mapping(device, hidinput, field, - usage); - } - goto ignore; - } - if (device->driver->input_mapping) { int ret = device->driver->input_mapping(device, hidinput, field, usage, &bit, &max); @@ -835,6 +827,24 @@ static void hidinput_close(struct input_dev *dev) hid_hw_close(hid); } +static void report_features(struct hid_device *hid) +{ + struct hid_driver *drv = hid->driver; + struct hid_report_enum *rep_enum; + struct hid_report *rep; + int i, j; + + if (!drv->feature_mapping) + return; + + rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) + for (i = 0; i < rep->maxfield; i++) + for (j = 0; j < rep->field[i]->maxusage; j++) + drv->feature_mapping(hid, rep->field[i], + rep->field[i]->usage + j); +} + /* * Register the input device; print a message. * Configure the input layer interface @@ -863,7 +873,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) return -1; } - for (k = HID_INPUT_REPORT; k <= HID_FEATURE_REPORT; k++) { + report_features(hid); + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { if (k == HID_OUTPUT_REPORT && hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) continue; diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 07d3183fdde50..2bbc9545f5ccd 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -122,7 +122,7 @@ struct mt_class mt_classes[] = { { } }; -static void mt_feature_mapping(struct hid_device *hdev, struct hid_input *hi, +static void mt_feature_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { if (usage->hid == HID_DG_INPUTMODE) { diff --git a/include/linux/hid.h b/include/linux/hid.h index d91c25e253c87..fc5faf60f6df4 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -638,7 +638,7 @@ struct hid_driver { struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max); void (*feature_mapping)(struct hid_device *hdev, - struct hid_input *hidinput, struct hid_field *field, + struct hid_field *field, struct hid_usage *usage); #ifdef CONFIG_PM int (*suspend)(struct hid_device *hdev, pm_message_t message); From 2968dd7b1f81282018195b031b52839b86592957 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 7 Feb 2011 19:28:01 +0100 Subject: [PATCH 0889/2556] fix per-cpu flag problem in the cpu affinity checkers commit 9804c9eaeacfe78651052c5ddff31099f60ef78c upstream. The CHECK_IRQ_PER_CPU is wrong, it should be checking irq_to_desc(irq)->status not just irq. Signed-off-by: Thomas Gleixner Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- arch/parisc/kernel/irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/parisc/kernel/irq.c b/arch/parisc/kernel/irq.c index d7d94b845dc2c..3948f1dd455aa 100644 --- a/arch/parisc/kernel/irq.c +++ b/arch/parisc/kernel/irq.c @@ -108,7 +108,7 @@ int cpu_check_affinity(unsigned int irq, const struct cpumask *dest) int cpu_dest; /* timer and ipi have to always be received on all CPUs */ - if (CHECK_IRQ_PER_CPU(irq)) { + if (CHECK_IRQ_PER_CPU(irq_to_desc(irq)->status)) { /* Bad linux design decision. The mask has already * been set; we must reset it */ cpumask_setall(irq_desc[irq].affinity); From 87fade73525e6d261aebbc538af1bb6496c56d03 Mon Sep 17 00:00:00 2001 From: Andy Botting Date: Sat, 12 Mar 2011 20:27:22 -0800 Subject: [PATCH 0890/2556] Input: bcm5974 - add support for MacBookPro8 commit 47340bd9fefb571888836da942b5aee0e85e959c upstream. This patch add multitouch support for the MacBookPro8,1 and MacBookPro8,2 models. Signed-off-by: Andy Botting Signed-off-by: Henrik Rydberg Acked-by: Jiri Kosina Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-apple.c | 6 ++++++ drivers/hid/hid-core.c | 6 ++++++ drivers/hid/hid-ids.h | 3 +++ drivers/input/mouse/bcm5974.c | 20 ++++++++++++++++++++ 4 files changed, 35 insertions(+) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 61aa712333927..b85744fe84647 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -481,6 +481,12 @@ static const struct hid_device_id apple_devices[] = { .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI), + .driver_data = APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ISO), + .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_JIS), + .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ee27b108c4c78..9477b2a46f494 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1300,6 +1300,9 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_JIS) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) }, @@ -1800,6 +1803,9 @@ static const struct hid_device_id hid_mouse_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING5_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, { } diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 5f5cc9100aff9..090bf488196e1 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -103,6 +103,9 @@ #define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI 0x0242 #define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO 0x0243 #define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS 0x0244 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI 0x0245 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO 0x0246 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS 0x0247 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI 0x0239 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO 0x023a #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index ee82851afe3ee..318531424848f 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -63,6 +63,10 @@ #define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI 0x0242 #define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO 0x0243 #define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS 0x0244 +/* Macbook8 (unibody, March 2011) */ +#define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI 0x0245 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO 0x0246 +#define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS 0x0247 #define BCM5974_DEVICE(prod) { \ .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \ @@ -96,6 +100,10 @@ static const struct usb_device_id bcm5974_table[] = { BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI), BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO), BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS), + /* MacbookPro8 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_JIS), /* Terminating entry */ {} }; @@ -274,6 +282,18 @@ static const struct bcm5974_config bcm5974_config_table[] = { { DIM_X, DIM_X / SN_COORD, -4616, 5112 }, { DIM_Y, DIM_Y / SN_COORD, -142, 5234 } }, + { + USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING5_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING5_JIS, + HAS_INTEGRATED_BUTTON, + 0x84, sizeof(struct bt_data), + 0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS, + { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 }, + { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 }, + { DIM_X, DIM_X / SN_COORD, -4415, 5050 }, + { DIM_Y, DIM_Y / SN_COORD, -55, 6680 } + }, {} }; From 61b153bc9980673656c1e03b3dda634dd4520018 Mon Sep 17 00:00:00 2001 From: Roman Fietze Date: Sun, 20 Mar 2011 14:50:52 +0100 Subject: [PATCH 0891/2556] i2c: Fix typo in instantiating-devices document commit 6ced9e6b3901af4ab6ac0a11231402c888286ea6 upstream. The struct i2c_board_info member holding the name is "type", not "name". Signed-off-by: Roman Fietze Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- Documentation/i2c/instantiating-devices | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/i2c/instantiating-devices b/Documentation/i2c/instantiating-devices index 87da405a85979..9edb75d8c9b94 100644 --- a/Documentation/i2c/instantiating-devices +++ b/Documentation/i2c/instantiating-devices @@ -100,7 +100,7 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev) (...) i2c_adap = i2c_get_adapter(2); memset(&i2c_info, 0, sizeof(struct i2c_board_info)); - strlcpy(i2c_info.name, "isp1301_pnx", I2C_NAME_SIZE); + strlcpy(i2c_info.type, "isp1301_pnx", I2C_NAME_SIZE); isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info, normal_i2c, NULL); i2c_put_adapter(i2c_adap); From b5fd12dd68ac8065701aa7a8da034de4a8cb2f7b Mon Sep 17 00:00:00 2001 From: Manoj Iyer Date: Fri, 11 Feb 2011 16:25:31 -0600 Subject: [PATCH 0892/2556] mmc: sdhci: Add Ricoh e823 PCI ID commit 5fd11c0754fa069b6aba64b65734aa2fb193552d upstream. Signed-off-by: Manoj Iyer Signed-off-by: Chris Ball Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-pci.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 0dc905b20eee1..f7e622c539baa 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -546,6 +546,14 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc, }, + { + .vendor = PCI_VENDOR_ID_RICOH, + .device = 0xe823, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_ricoh_mmc, + }, + { .vendor = PCI_VENDOR_ID_ENE, .device = PCI_DEVICE_ID_ENE_CB712_SD, From 8e9f5c745c93a536f020dbaeb86022f63e50b072 Mon Sep 17 00:00:00 2001 From: Stefan Nilsson XK Date: Tue, 1 Mar 2011 14:41:04 +0100 Subject: [PATCH 0893/2556] mmc: sdio: remember new card RCA when redetecting card commit 0aab3995485b8a994bf29a995a008c9ea4a28054 upstream. During redetection of a SDIO card, a request for a new card RCA was submitted to the card, but was then overwritten by the old RCA. This caused the card to be deselected instead of selected when using the incorrect RCA. This bug's been present since the "oldcard" handling was introduced in 2.6.32. Signed-off-by: Stefan Nilsson XK Reviewed-by: Ulf Hansson Reviewed-by: Pawel Wieczorkiewicz Signed-off-by: Linus Walleij Signed-off-by: Chris Ball Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/core/sdio.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index d7892386c9405..6779207bbd108 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -399,6 +399,14 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, if (err) goto remove; + /* + * Update oldcard with the new RCA received from the SDIO + * device -- we're doing this so that it's updated in the + * "card" struct when oldcard overwrites that later. + */ + if (oldcard) + oldcard->rca = card->rca; + mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); } From 3d72f459bf5e2d5ce5e40e2f5abb31c84747719c Mon Sep 17 00:00:00 2001 From: Alexander van Heukelum Date: Fri, 11 Mar 2011 21:59:38 +0100 Subject: [PATCH 0894/2556] x86, binutils, xen: Fix another wrong size directive commit 371c394af27ab7d1e58a66bc19d9f1f3ac1f67b4 upstream. The latest binutils (2.21.0.20110302/Ubuntu) breaks the build yet another time, under CONFIG_XEN=y due to a .size directive that refers to a slightly differently named (hence, to the now very strict and unforgiving assembler, non-existent) symbol. [ mingo: This unnecessary build breakage caused by new binutils version 2.21 gets escallated back several kernel releases spanning several years of Linux history, affecting over 130,000 upstream kernel commits (!), on CONFIG_XEN=y 64-bit kernels (i.e. essentially affecting all major Linux distro kernel configs). Git annotate tells us that this slight debug symbol code mismatch bug has been introduced in 2008 in commit 3d75e1b8: 3d75e1b8 (Jeremy Fitzhardinge 2008-07-08 15:06:49 -0700 1231) ENTRY(xen_do_hypervisor_callback) # do_hypervisor_callback(struct *pt_regs) The 'bug' is just a slight assymetry in ENTRY()/END() debug-symbols sequences, with lots of assembly code between the ENTRY() and the END(): ENTRY(xen_do_hypervisor_callback) # do_hypervisor_callback(struct *pt_regs) ... END(do_hypervisor_callback) Human reviewers almost never catch such small mismatches, and binutils never even warned about it either. This new binutils version thus breaks the Xen build on all upstream kernels since v2.6.27, out of the blue. This makes a straightforward Git bisection of all 64-bit Xen-enabled kernels impossible on such binutils, for a bisection window of over hundred thousand historic commits. (!) This is a major fail on the side of binutils and binutils needs to turn this show-stopper build failure into a warning ASAP. ] Signed-off-by: Alexander van Heukelum Cc: Jeremy Fitzhardinge Cc: Jan Beulich Cc: H.J. Lu Cc: Linus Torvalds Cc: Andrew Morton Cc: "H. Peter Anvin" Cc: Kees Cook LKML-Reference: <1299877178-26063-1-git-send-email-heukelum@fastmail.fm> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/entry_64.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index aed1ffbeb0c9b..bbd5c80cb09b0 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -1248,7 +1248,7 @@ ENTRY(xen_do_hypervisor_callback) # do_hypervisor_callback(struct *pt_regs) decl PER_CPU_VAR(irq_count) jmp error_exit CFI_ENDPROC -END(do_hypervisor_callback) +END(xen_do_hypervisor_callback) /* * Hypervisor uses this for application faults while it executes. From f9c46b05f630f2807f8e94f090bb747f4e56d4d6 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sun, 13 Mar 2011 20:06:59 +0000 Subject: [PATCH 0895/2556] davinci: DM644x EVM: register MUSB device earlier commit 60d97a840175d3becb2e6de36537a5cdfc0ec3a9 upstream. The MUSB driver doesn't see its platform device on DM644x EVM board anymore since commit 73b089b052a69020b953312a624a6e1eb5b81fab (usb: musb: split davinci to its own platform_driver) because the new probe is called as subsys_initcall() now, and the device is registered later than that by the board code. Move the registration to davinci_evm_init() -- it's safe to do so because the MUSB core device still gets initialized as fs_initcall() -- which is late enough for the I2C GPIO expander (which controls VBUS) to be initialized. Signed-off-by: Sergei Shtylyov Acked-by: Felipe Balbi Tested-by: Sekhar Nori Signed-off-by: Kevin Hilman Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-davinci/board-dm644x-evm.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 0ca90b834586d..556bbd468db3d 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -440,11 +440,6 @@ evm_u35_setup(struct i2c_client *client, int gpio, unsigned ngpio, void *c) gpio_request(gpio + 7, "nCF_SEL"); gpio_direction_output(gpio + 7, 1); - /* irlml6401 switches over 1A, in under 8 msec; - * now it can be managed by nDRV_VBUS ... - */ - davinci_setup_usb(1000, 8); - return 0; } @@ -705,6 +700,9 @@ static __init void davinci_evm_init(void) davinci_serial_init(&uart_config); dm644x_init_asp(&dm644x_evm_snd_data); + /* irlml6401 switches over 1A, in under 8 msec */ + davinci_setup_usb(1000, 8); + soc_info->emac_pdata->phy_id = DM644X_EVM_PHY_ID; /* Register the fixup for PHY on DaVinci */ phy_register_fixup_for_uid(LXT971_PHY_ID, LXT971_PHY_MASK, From 8eb3530b26d32930bad1205d6380e48cd021ac44 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 21 Mar 2011 17:59:35 +0100 Subject: [PATCH 0896/2556] hwmon: (sht15) Fix integer overflow in humidity calculation commit ccd32e735de7a941906e093f8dca924bb05c5794 upstream. An integer overflow occurs in the calculation of RHlinear when the relative humidity is greater than around 30%. The consequence is a subtle (but noticeable) error in the resulting humidity measurement. Signed-off-by: Vivien Didelot Signed-off-by: Jean Delvare Cc: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/sht15.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index a610e7880fb3e..38a41d27f3da1 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -333,11 +333,11 @@ static inline int sht15_calc_humid(struct sht15_data *data) const int c1 = -4; const int c2 = 40500; /* x 10 ^ -6 */ - const int c3 = -2800; /* x10 ^ -9 */ + const int c3 = -28; /* x 10 ^ -7 */ RHlinear = c1*1000 + c2 * data->val_humid/1000 - + (data->val_humid * data->val_humid * c3)/1000000; + + (data->val_humid * data->val_humid * c3) / 10000; return (temp - 25000) * (10000 + 80 * data->val_humid) / 1000000 + RHlinear; } From 9c72c10f80df539ce6589a4feb961e78eb42fde3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 23 Mar 2011 13:04:47 -0700 Subject: [PATCH 0897/2556] Linux 2.6.38.1 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d6592b63c8cb6..167ef453a8cd5 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 38 -EXTRAVERSION = +EXTRAVERSION = .1 NAME = Flesh-Eating Bats with Fangs # *DOCUMENTATION* From a8dda38e220fc5bc48726807d1e57777c7faf8fa Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 23 Mar 2011 08:35:07 +0100 Subject: [PATCH 0898/2556] ALSA: HDA: Realtek: Avoid unnecessary volume control index on Surround/Side commit 5a8826463c19b0d1a2fc60b2adac0ec318047844 upstream. Similar to commit 7e59e097c09b82760bb0fe08b0fa2b704d76c3f4, this patch avoids unnecessary volume control indices for more Realtek auto-parsers, e g the ALC66x family, on the "Surround" and "Side" controls. These indices cause these volume controls to be ignored by PulseAudio and vmaster and should be removed whenever possible. Reported-by: Jan Losinski Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index acd209958ea24..c2eb6a7c2b3c8 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -16085,9 +16085,12 @@ static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec, return err; } else { const char *name = pfx; - if (!name) + int index = i; + if (!name) { name = chname[i]; - err = __alc861_create_out_sw(codec, name, nid, i, 3); + index = 0; + } + err = __alc861_create_out_sw(codec, name, nid, index, 3); if (err < 0) return err; } @@ -17238,16 +17241,19 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, return err; } else { const char *name = pfx; - if (!name) + int index = i; + if (!name) { name = chname[i]; + index = 0; + } err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - name, i, + name, index, HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT)); if (err < 0) return err; err = __add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - name, i, + name, index, HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT)); if (err < 0) @@ -19296,12 +19302,15 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, return err; } else { const char *name = pfx; - if (!name) + int index = i; + if (!name) { name = chname[i]; - err = __alc662_add_vol_ctl(spec, name, nid, i, 3); + index = 0; + } + err = __alc662_add_vol_ctl(spec, name, nid, index, 3); if (err < 0) return err; - err = __alc662_add_sw_ctl(spec, name, mix, i, 3); + err = __alc662_add_sw_ctl(spec, name, mix, index, 3); if (err < 0) return err; } From 0255353e9525d22629bb49dfb96cd1e078179e79 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Tue, 22 Mar 2011 16:21:38 +0800 Subject: [PATCH 0899/2556] ALSA: hda - VIA: Fix stereo mixer recording no sound issue commit bff5fbf50bd498c217994bd2d41a53ac3141185a upstream. Modify function via_mux_enum_put() to fix stereo mixer recording no sound issue. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_via.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 63b0054200a87..5bc9bd983e865 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1101,6 +1101,7 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + int ret; if (!spec->mux_nids[adc_idx]) return -EINVAL; @@ -1109,12 +1110,14 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol, AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, AC_VERB_SET_POWER_STATE, AC_PWRST_D0); - /* update jack power state */ - set_jack_power_state(codec); - return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, + ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]); + /* update jack power state */ + set_jack_power_state(codec); + + return ret; } static int via_independent_hp_info(struct snd_kcontrol *kcontrol, From e599e5b1c18b00b2ef36e8917b20e1dd3352592d Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Tue, 22 Mar 2011 16:22:37 +0800 Subject: [PATCH 0900/2556] ALSA: hda - VIA: Fix independent headphone no sound issue commit ce0e5a9e81fbb153ee15ca60246c6722f07fc546 upstream. Modify via_independent_hp_put() function to support VT1718S and VT1812 codecs, and fix independent headphone no sound issue. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_via.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 5bc9bd983e865..2f605e593957f 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1191,8 +1191,16 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, /* Get Independent Mode index of headphone pin widget */ spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel ? 1 : 0; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); + if (spec->codec_type == VT1718S) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0); + else + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, pinsel); + if (spec->codec_type == VT1812) + snd_hda_codec_write(codec, 0x35, 0, + AC_VERB_SET_CONNECT_SEL, pinsel); if (spec->multiout.hp_nid && spec->multiout.hp_nid != spec->multiout.dac_nids[HDA_FRONT]) snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, @@ -1211,6 +1219,8 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, activate_ctl(codec, "Headphone Playback Switch", spec->hp_independent_mode); } + /* update jack power state */ + set_jack_power_state(codec); return 0; } From 9808f5a4e0d0fdc36c789c8331d338dce0f09efc Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Tue, 22 Mar 2011 16:23:23 +0800 Subject: [PATCH 0901/2556] ALSA: hda - VIA: Add missing support for VT1718S in A-A path commit ab657e0cacc39d88145871c6a3c844597c02d406 upstream. Modify mute_aa_path() function to support VT1718S codec. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_via.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 2f605e593957f..23e58f0904695 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1323,6 +1323,11 @@ static void mute_aa_path(struct hda_codec *codec, int mute) start_idx = 2; end_idx = 4; break; + case VT1718S: + nid_mixer = 0x21; + start_idx = 1; + end_idx = 3; + break; default: return; } From 42b04e70f314762c44beacf40349be5684d0de55 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Tue, 22 Mar 2011 16:24:10 +0800 Subject: [PATCH 0902/2556] ALSA: hda - VIA: Fix invalid A-A path volume adjust issue commit 169222813eec8403c76394fb7b35ecab98e3c607 upstream. Modify vt_auto_create_analog_input_ctls() function to fix invalid a-a path volume adjust issue for VT1708S, VT1702 and VT1716S codecs. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_via.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 23e58f0904695..299a18b735a3e 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -2456,7 +2456,14 @@ static int vt_auto_create_analog_input_ctls(struct hda_codec *codec, else type_idx = 0; label = hda_get_autocfg_input_label(codec, cfg, i); - err = via_new_analog_input(spec, label, type_idx, idx, cap_nid); + if (spec->codec_type == VT1708S || + spec->codec_type == VT1702 || + spec->codec_type == VT1716S) + err = via_new_analog_input(spec, label, type_idx, + idx+1, cap_nid); + else + err = via_new_analog_input(spec, label, type_idx, + idx, cap_nid); if (err < 0) return err; snd_hda_add_imux_item(imux, label, idx, NULL); From 1e5d0ebe2c91d22445536a0d3f9ed3ae1330efdc Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Tue, 22 Mar 2011 16:25:03 +0800 Subject: [PATCH 0903/2556] ALSA: hda - VIA: Fix codec type for VT1708BCE at the right timing commit 0341ccd7557fecafe6a79c55158670cf629d269e upstream. Add get_codec_type() in via_new_spec() function to make sure getting correct codec type before building mixer controls. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_via.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 299a18b735a3e..269bb365752b4 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -159,6 +159,7 @@ struct via_spec { #endif }; +static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); static struct via_spec * via_new_spec(struct hda_codec *codec) { struct via_spec *spec; @@ -169,6 +170,10 @@ static struct via_spec * via_new_spec(struct hda_codec *codec) codec->spec = spec; spec->codec = codec; + spec->codec_type = get_codec_type(codec); + /* VT1708BCE & VT1708S are almost same */ + if (spec->codec_type == VT1708BCE) + spec->codec_type = VT1708S; return spec; } @@ -2203,10 +2208,6 @@ static int via_init(struct hda_codec *codec) for (i = 0; i < spec->num_iverbs; i++) snd_hda_sequence_write(codec, spec->init_verbs[i]); - spec->codec_type = get_codec_type(codec); - if (spec->codec_type == VT1708BCE) - spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost - same */ /* Lydia Add for EAPD enable */ if (!spec->dig_in_nid) { /* No Digital In connection */ if (spec->dig_in_pin) { From 0dda7a321e280b9ab00db471439e1b048b32f2b3 Mon Sep 17 00:00:00 2001 From: Lydia Wang Date: Tue, 22 Mar 2011 16:26:36 +0800 Subject: [PATCH 0904/2556] ALSA: hda - VIA: Fix VT1708 can't build up Headphone control issue commit ee3c35c0827de02de414d08b2ddcbb910c2263ab upstream. Since VT1708 didn't support the control of getting connection number, building of headphone control will fail in via_hp_build() function. Signed-off-by: Lydia Wang Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_via.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 269bb365752b4..acc45798eeb43 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1266,9 +1266,12 @@ static int via_hp_build(struct hda_codec *codec) break; } - nums = snd_hda_get_connections(codec, nid, conn, HDA_MAX_CONNECTIONS); - if (nums <= 1) - return 0; + if (spec->codec_type != VT1708) { + nums = snd_hda_get_connections(codec, nid, + conn, HDA_MAX_CONNECTIONS); + if (nums <= 1) + return 0; + } knew = via_clone_control(spec, &via_hp_mixer[0]); if (knew == NULL) From 9c9b01bea036a0ba4f833d2053129695959a870c Mon Sep 17 00:00:00 2001 From: Marek Belisko Date: Wed, 9 Mar 2011 21:46:20 +0100 Subject: [PATCH 0905/2556] ASoC: mini2440: Fix uda134x codec problem. commit a110f4ef810ee29d810876df725f41d66629733e upstream. ASoC audio for mini2440 platform in current kenrel doesn't work. First problem is samsung_asoc_dma device is missing in initialization. Next problem is with codec. Codec is initialized but never probed because no platform_device exist for codec driver. It leads to errors during codec binding to asoc dai. Next problem was platform data which was passed from board to asoc main driver but not passed to codec when called codec_soc_probe(). Following patch should fix issues. But not sure if in correct way. Please review. Signed-off-by: Marek Belisko Acked-by: Liam Girdwood Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- arch/arm/mach-s3c2440/mach-mini2440.c | 7 +++++++ sound/soc/codecs/uda134x.c | 3 ++- sound/soc/samsung/s3c24xx_uda134x.c | 3 ++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-s3c2440/mach-mini2440.c b/arch/arm/mach-s3c2440/mach-mini2440.c index f62bb4c793bdd..7c3fb071ddd69 100644 --- a/arch/arm/mach-s3c2440/mach-mini2440.c +++ b/arch/arm/mach-s3c2440/mach-mini2440.c @@ -506,6 +506,11 @@ static struct i2c_board_info mini2440_i2c_devs[] __initdata = { }, }; +static struct platform_device uda1340_codec = { + .name = "uda134x-codec", + .id = -1, +}; + static struct platform_device *mini2440_devices[] __initdata = { &s3c_device_ohci, &s3c_device_wdt, @@ -521,7 +526,9 @@ static struct platform_device *mini2440_devices[] __initdata = { &s3c_device_nand, &s3c_device_sdi, &s3c_device_iis, + &uda1340_codec, &mini2440_audio, + &samsung_asoc_dma, }; static void __init mini2440_map_io(void) diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index e76847a9438b0..48ffd406a71d1 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -486,7 +486,8 @@ static struct snd_soc_dai_driver uda134x_dai = { static int uda134x_soc_probe(struct snd_soc_codec *codec) { struct uda134x_priv *uda134x; - struct uda134x_platform_data *pd = dev_get_drvdata(codec->card->dev); + struct uda134x_platform_data *pd = codec->card->dev->platform_data; + int ret; printk(KERN_INFO "UDA134X SoC Audio Codec\n"); diff --git a/sound/soc/samsung/s3c24xx_uda134x.c b/sound/soc/samsung/s3c24xx_uda134x.c index 2c09e93dd566f..86f1dc434e9f0 100644 --- a/sound/soc/samsung/s3c24xx_uda134x.c +++ b/sound/soc/samsung/s3c24xx_uda134x.c @@ -226,7 +226,7 @@ static struct snd_soc_ops s3c24xx_uda134x_ops = { static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = { .name = "UDA134X", .stream_name = "UDA134X", - .codec_name = "uda134x-hifi", + .codec_name = "uda134x-codec", .codec_dai_name = "uda134x-hifi", .cpu_dai_name = "s3c24xx-iis", .ops = &s3c24xx_uda134x_ops, @@ -321,6 +321,7 @@ static int s3c24xx_uda134x_probe(struct platform_device *pdev) platform_set_drvdata(s3c24xx_uda134x_snd_device, &snd_soc_s3c24xx_uda134x); + platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x)); ret = platform_device_add(s3c24xx_uda134x_snd_device); if (ret) { printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n"); From b77e02c177b61a6b883751015029cbfa6f3afe44 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 17 Mar 2011 07:34:32 +0000 Subject: [PATCH 0906/2556] ethtool: Compat handling for struct ethtool_rxnfc commit 3a7da39d165e0c363c294feec119db1427032afd upstream. This structure was accidentally defined such that its layout can differ between 32-bit and 64-bit processes. Add compat structure definitions and an ioctl wrapper function. Signed-off-by: Ben Hutchings Acked-by: Alexander Duyck Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- include/linux/ethtool.h | 34 ++++++++++++ net/socket.c | 114 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 141 insertions(+), 7 deletions(-) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 1908929204a9e..a3c1874171c1c 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -13,6 +13,9 @@ #ifndef _LINUX_ETHTOOL_H #define _LINUX_ETHTOOL_H +#ifdef __KERNEL__ +#include +#endif #include #include @@ -449,6 +452,37 @@ struct ethtool_rxnfc { __u32 rule_locs[0]; }; +#ifdef __KERNEL__ +#ifdef CONFIG_COMPAT + +struct compat_ethtool_rx_flow_spec { + u32 flow_type; + union { + struct ethtool_tcpip4_spec tcp_ip4_spec; + struct ethtool_tcpip4_spec udp_ip4_spec; + struct ethtool_tcpip4_spec sctp_ip4_spec; + struct ethtool_ah_espip4_spec ah_ip4_spec; + struct ethtool_ah_espip4_spec esp_ip4_spec; + struct ethtool_usrip4_spec usr_ip4_spec; + struct ethhdr ether_spec; + u8 hdata[72]; + } h_u, m_u; + compat_u64 ring_cookie; + u32 location; +}; + +struct compat_ethtool_rxnfc { + u32 cmd; + u32 flow_type; + compat_u64 data; + struct compat_ethtool_rx_flow_spec fs; + u32 rule_cnt; + u32 rule_locs[0]; +}; + +#endif /* CONFIG_COMPAT */ +#endif /* __KERNEL__ */ + /** * struct ethtool_rxfh_indir - command to get or set RX flow hash indirection * @cmd: Specific command number - %ETHTOOL_GRXFHINDIR or %ETHTOOL_SRXFHINDIR diff --git a/net/socket.c b/net/socket.c index 06d835d3dab9e..b0b8bdaa079f5 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2586,23 +2586,123 @@ static int dev_ifconf(struct net *net, struct compat_ifconf __user *uifc32) static int ethtool_ioctl(struct net *net, struct compat_ifreq __user *ifr32) { + struct compat_ethtool_rxnfc __user *compat_rxnfc; + bool convert_in = false, convert_out = false; + size_t buf_size = ALIGN(sizeof(struct ifreq), 8); + struct ethtool_rxnfc __user *rxnfc; struct ifreq __user *ifr; + u32 rule_cnt = 0, actual_rule_cnt; + u32 ethcmd; u32 data; - void __user *datap; + int ret; + + if (get_user(data, &ifr32->ifr_ifru.ifru_data)) + return -EFAULT; - ifr = compat_alloc_user_space(sizeof(*ifr)); + compat_rxnfc = compat_ptr(data); - if (copy_in_user(&ifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ)) + if (get_user(ethcmd, &compat_rxnfc->cmd)) return -EFAULT; - if (get_user(data, &ifr32->ifr_ifru.ifru_data)) + /* Most ethtool structures are defined without padding. + * Unfortunately struct ethtool_rxnfc is an exception. + */ + switch (ethcmd) { + default: + break; + case ETHTOOL_GRXCLSRLALL: + /* Buffer size is variable */ + if (get_user(rule_cnt, &compat_rxnfc->rule_cnt)) + return -EFAULT; + if (rule_cnt > KMALLOC_MAX_SIZE / sizeof(u32)) + return -ENOMEM; + buf_size += rule_cnt * sizeof(u32); + /* fall through */ + case ETHTOOL_GRXRINGS: + case ETHTOOL_GRXCLSRLCNT: + case ETHTOOL_GRXCLSRULE: + convert_out = true; + /* fall through */ + case ETHTOOL_SRXCLSRLDEL: + case ETHTOOL_SRXCLSRLINS: + buf_size += sizeof(struct ethtool_rxnfc); + convert_in = true; + break; + } + + ifr = compat_alloc_user_space(buf_size); + rxnfc = (void *)ifr + ALIGN(sizeof(struct ifreq), 8); + + if (copy_in_user(&ifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ)) return -EFAULT; - datap = compat_ptr(data); - if (put_user(datap, &ifr->ifr_ifru.ifru_data)) + if (put_user(convert_in ? rxnfc : compat_ptr(data), + &ifr->ifr_ifru.ifru_data)) return -EFAULT; - return dev_ioctl(net, SIOCETHTOOL, ifr); + if (convert_in) { + /* We expect there to be holes between fs.m_u and + * fs.ring_cookie and at the end of fs, but nowhere else. + */ + BUILD_BUG_ON(offsetof(struct compat_ethtool_rxnfc, fs.m_u) + + sizeof(compat_rxnfc->fs.m_u) != + offsetof(struct ethtool_rxnfc, fs.m_u) + + sizeof(rxnfc->fs.m_u)); + BUILD_BUG_ON( + offsetof(struct compat_ethtool_rxnfc, fs.location) - + offsetof(struct compat_ethtool_rxnfc, fs.ring_cookie) != + offsetof(struct ethtool_rxnfc, fs.location) - + offsetof(struct ethtool_rxnfc, fs.ring_cookie)); + + if (copy_in_user(rxnfc, compat_rxnfc, + (void *)(&rxnfc->fs.m_u + 1) - + (void *)rxnfc) || + copy_in_user(&rxnfc->fs.ring_cookie, + &compat_rxnfc->fs.ring_cookie, + (void *)(&rxnfc->fs.location + 1) - + (void *)&rxnfc->fs.ring_cookie) || + copy_in_user(&rxnfc->rule_cnt, &compat_rxnfc->rule_cnt, + sizeof(rxnfc->rule_cnt))) + return -EFAULT; + } + + ret = dev_ioctl(net, SIOCETHTOOL, ifr); + if (ret) + return ret; + + if (convert_out) { + if (copy_in_user(compat_rxnfc, rxnfc, + (const void *)(&rxnfc->fs.m_u + 1) - + (const void *)rxnfc) || + copy_in_user(&compat_rxnfc->fs.ring_cookie, + &rxnfc->fs.ring_cookie, + (const void *)(&rxnfc->fs.location + 1) - + (const void *)&rxnfc->fs.ring_cookie) || + copy_in_user(&compat_rxnfc->rule_cnt, &rxnfc->rule_cnt, + sizeof(rxnfc->rule_cnt))) + return -EFAULT; + + if (ethcmd == ETHTOOL_GRXCLSRLALL) { + /* As an optimisation, we only copy the actual + * number of rules that the underlying + * function returned. Since Mallory might + * change the rule count in user memory, we + * check that it is less than the rule count + * originally given (as the user buffer size), + * which has been range-checked. + */ + if (get_user(actual_rule_cnt, &rxnfc->rule_cnt)) + return -EFAULT; + if (actual_rule_cnt < rule_cnt) + rule_cnt = actual_rule_cnt; + if (copy_in_user(&compat_rxnfc->rule_locs[0], + &rxnfc->rule_locs[0], + rule_cnt * sizeof(u32))) + return -EFAULT; + } + } + + return 0; } static int compat_siocwandev(struct net *net, struct compat_ifreq __user *uifr32) From ab624cc85512a62436b4e6ade14e91e157464c1c Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Mon, 14 Feb 2011 17:46:21 +0200 Subject: [PATCH 0907/2556] Revert "slab: Fix missing DEBUG_SLAB last user" commit 3ff84a7f36554b257cd57325b1a7c1fa4b49fbe3 upstream. This reverts commit 5c5e3b33b7cb959a401f823707bee006caadd76e. The commit breaks ARM thusly: | Mount-cache hash table entries: 512 | slab error in verify_redzone_free(): cache `idr_layer_cache': memory outside object was overwritten | Backtrace: | [] (dump_backtrace+0x0/0x110) from [] (dump_stack+0x18/0x1c) | [] (dump_stack+0x0/0x1c) from [] (__slab_error+0x28/0x30) | [] (__slab_error+0x0/0x30) from [] (cache_free_debugcheck+0x1c0/0x2b8) | [] (cache_free_debugcheck+0x0/0x2b8) from [] (kmem_cache_free+0x3c/0xc0) | [] (kmem_cache_free+0x0/0xc0) from [] (ida_get_new_above+0x19c/0x1c0) | [] (ida_get_new_above+0x0/0x1c0) from [] (alloc_vfsmnt+0x54/0x144) | [] (alloc_vfsmnt+0x0/0x144) from [] (vfs_kern_mount+0x30/0xec) | [] (vfs_kern_mount+0x0/0xec) from [] (kern_mount_data+0x1c/0x20) | [] (kern_mount_data+0x0/0x20) from [] (sysfs_init+0x68/0xc8) | [] (sysfs_init+0x0/0xc8) from [] (mnt_init+0x90/0x1b0) | [] (mnt_init+0x0/0x1b0) from [] (vfs_caches_init+0x100/0x140) | [] (vfs_caches_init+0x0/0x140) from [] (start_kernel+0x2e8/0x368) | [] (start_kernel+0x0/0x368) from [] (__enable_mmu+0x0/0x2c) | c0113268: redzone 1:0xd84156c5c032b3ac, redzone 2:0xd84156c5635688c0. | slab error in cache_alloc_debugcheck_after(): cache `idr_layer_cache': double free, or memory outside object was overwritten | ... | c011307c: redzone 1:0x9f91102ffffffff, redzone 2:0x9f911029d74e35b | slab: Internal list corruption detected in cache 'idr_layer_cache'(24), slabp c0113000(16). Hexdump: | | 000: 20 4f 10 c0 20 4f 10 c0 7c 00 00 00 7c 30 11 c0 | 010: 10 00 00 00 10 00 00 00 00 00 c9 17 fe ff ff ff | 020: fe ff ff ff fe ff ff ff fe ff ff ff fe ff ff ff | 030: fe ff ff ff fe ff ff ff fe ff ff ff fe ff ff ff | 040: fe ff ff ff fe ff ff ff fe ff ff ff fe ff ff ff | 050: fe ff ff ff fe ff ff ff fe ff ff ff 11 00 00 00 | 060: 12 00 00 00 13 00 00 00 14 00 00 00 15 00 00 00 | 070: 16 00 00 00 17 00 00 00 c0 88 56 63 | kernel BUG at /home/rmk/git/linux-2.6-rmk/mm/slab.c:2928! Reference: https://lkml.org/lkml/2011/2/7/238 Reported-and-analyzed-by: Russell King Signed-off-by: Pekka Enberg Signed-off-by: Greg Kroah-Hartman --- mm/slab.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mm/slab.c b/mm/slab.c index 37961d1f584fe..4c6e2e31ced05 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -2288,8 +2288,8 @@ kmem_cache_create (const char *name, size_t size, size_t align, if (ralign < align) { ralign = align; } - /* disable debug if not aligning with REDZONE_ALIGN */ - if (ralign & (__alignof__(unsigned long long) - 1)) + /* disable debug if necessary */ + if (ralign > __alignof__(unsigned long long)) flags &= ~(SLAB_RED_ZONE | SLAB_STORE_USER); /* * 4) Store it. @@ -2315,8 +2315,8 @@ kmem_cache_create (const char *name, size_t size, size_t align, */ if (flags & SLAB_RED_ZONE) { /* add space for red zone words */ - cachep->obj_offset += align; - size += align + sizeof(unsigned long long); + cachep->obj_offset += sizeof(unsigned long long); + size += 2 * sizeof(unsigned long long); } if (flags & SLAB_STORE_USER) { /* user store requires one word storage behind the end of From 1d4fbc27bd44084b583be988f5c8c6193d87d89a Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Tue, 22 Mar 2011 16:35:10 -0700 Subject: [PATCH 0908/2556] aio: wake all waiters when destroying ctx commit e91f90bb0bb10be9cc8efd09a3cf4ecffcad0db1 upstream. The test program below will hang because io_getevents() uses add_wait_queue_exclusive(), which means the wake_up() in io_destroy() only wakes up one of the threads. Fix this by using wake_up_all() in the aio code paths where we want to make sure no one gets stuck. // t.c -- compile with gcc -lpthread -laio t.c #include #include #include #include static const int nthr = 2; void *getev(void *ctx) { struct io_event ev; io_getevents(ctx, 1, 1, &ev, NULL); printf("io_getevents returned\n"); return NULL; } int main(int argc, char *argv[]) { io_context_t ctx = 0; pthread_t thread[nthr]; int i; io_setup(1024, &ctx); for (i = 0; i < nthr; ++i) pthread_create(&thread[i], NULL, getev, ctx); sleep(1); io_destroy(ctx); for (i = 0; i < nthr; ++i) pthread_join(thread[i], NULL); return 0; } Signed-off-by: Roland Dreier Reviewed-by: Jeff Moyer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/aio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/aio.c b/fs/aio.c index 26869cde39535..88f0ed5144252 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -520,7 +520,7 @@ static inline void really_put_req(struct kioctx *ctx, struct kiocb *req) ctx->reqs_active--; if (unlikely(!ctx->reqs_active && ctx->dead)) - wake_up(&ctx->wait); + wake_up_all(&ctx->wait); } static void aio_fput_routine(struct work_struct *data) @@ -1229,7 +1229,7 @@ static void io_destroy(struct kioctx *ioctx) * by other CPUs at this point. Right now, we rely on the * locking done by the above calls to ensure this consistency. */ - wake_up(&ioctx->wait); + wake_up_all(&ioctx->wait); put_ioctx(ioctx); /* once for the lookup */ } From 90d3c6025ad404c03190d95d3aee1b76dcc753d1 Mon Sep 17 00:00:00 2001 From: Phil Carmody Date: Tue, 22 Mar 2011 16:30:13 -0700 Subject: [PATCH 0909/2556] cgroups: if you list_empty() a head then don't list_del() it commit 8d2587970b8bdf7c8d9208e3f4bb93182aef1a0f upstream. list_del() leaves poison in the prev and next pointers. The next list_empty() will compare those poisons, and say the list isn't empty. Any list operations that assume the node is on a list because of such a check will be fooled into dereferencing poison. One needs to INIT the node after the del, and fortunately there's already a wrapper for that - list_del_init(). Some of the dels are followed by deallocations, so can be ignored, and one can be merged with an add to make a move. Apart from that, I erred on the side of caution in making nodes list_empty()-queriable. Signed-off-by: Phil Carmody Reviewed-by: Paul Menage Cc: Li Zefan Acked-by: Kirill A. Shutemov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/cgroup.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/kernel/cgroup.c b/kernel/cgroup.c index d96bd1eb5627d..d83723e7ee05c 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1820,10 +1820,8 @@ int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk) /* Update the css_set linked lists if we're using them */ write_lock(&css_set_lock); - if (!list_empty(&tsk->cg_list)) { - list_del(&tsk->cg_list); - list_add(&tsk->cg_list, &newcg->tasks); - } + if (!list_empty(&tsk->cg_list)) + list_move(&tsk->cg_list, &newcg->tasks); write_unlock(&css_set_lock); for_each_subsys(root, ss) { @@ -3670,12 +3668,12 @@ static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry) spin_lock(&release_list_lock); set_bit(CGRP_REMOVED, &cgrp->flags); if (!list_empty(&cgrp->release_list)) - list_del(&cgrp->release_list); + list_del_init(&cgrp->release_list); spin_unlock(&release_list_lock); cgroup_lock_hierarchy(cgrp->root); /* delete this cgroup from parent->children */ - list_del(&cgrp->sibling); + list_del_init(&cgrp->sibling); cgroup_unlock_hierarchy(cgrp->root); d = dget(cgrp->dentry); @@ -3893,7 +3891,7 @@ void cgroup_unload_subsys(struct cgroup_subsys *ss) subsys[ss->subsys_id] = NULL; /* remove subsystem from rootnode's list of subsystems */ - list_del(&ss->sibling); + list_del_init(&ss->sibling); /* * disentangle the css from all css_sets attached to the dummytop. as From 1452b40dc0feef7e81f69c78fd769ddf0dd18415 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Tue, 22 Mar 2011 16:30:08 -0700 Subject: [PATCH 0910/2556] mm: swap: unlock swapfile inode mutex before closing file on bad swapfiles commit 52c50567d8ab0a0a87f12cceaa4194967854f0bd upstream. If an administrator tries to swapon a file backed by NFS, the inode mutex is taken (as it is for any swapfile) but later identified to be a bad swapfile due to the lack of bmap and tries to cleanup. During cleanup, an attempt is made to close the file but with inode->i_mutex still held. Closing an NFS file syncs it which tries to acquire the inode mutex leading to deadlock. If lockdep is enabled the following appears on the console; ============================================= [ INFO: possible recursive locking detected ] 2.6.38-rc8-autobuild #1 --------------------------------------------- swapon/2192 is trying to acquire lock: (&sb->s_type->i_mutex_key#13){+.+.+.}, at: vfs_fsync_range+0x47/0x7c but task is already holding lock: (&sb->s_type->i_mutex_key#13){+.+.+.}, at: sys_swapon+0x28d/0xae7 other info that might help us debug this: 1 lock held by swapon/2192: #0: (&sb->s_type->i_mutex_key#13){+.+.+.}, at: sys_swapon+0x28d/0xae7 stack backtrace: Pid: 2192, comm: swapon Not tainted 2.6.38-rc8-autobuild #1 Call Trace: __lock_acquire+0x2eb/0x1623 find_get_pages_tag+0x14a/0x174 pagevec_lookup_tag+0x25/0x2e vfs_fsync_range+0x47/0x7c lock_acquire+0xd3/0x100 vfs_fsync_range+0x47/0x7c nfs_flush_one+0x0/0xdf [nfs] mutex_lock_nested+0x40/0x2b1 vfs_fsync_range+0x47/0x7c vfs_fsync_range+0x47/0x7c vfs_fsync+0x1c/0x1e nfs_file_flush+0x64/0x69 [nfs] filp_close+0x43/0x72 sys_swapon+0xa39/0xae7 sysret_check+0x2e/0x69 system_call_fastpath+0x16/0x1b This patch releases the mutex if its held before calling filep_close() so swapon fails as expected without deadlock when the swapfile is backed by NFS. If accepted for 2.6.39, it should also be considered a -stable candidate for 2.6.38 and 2.6.37. Signed-off-by: Mel Gorman Acked-by: Hugh Dickins Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/swapfile.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mm/swapfile.c b/mm/swapfile.c index 0341c5700e346..6d6d28c0a72f6 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -2149,8 +2149,13 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) p->flags = 0; spin_unlock(&swap_lock); vfree(swap_map); - if (swap_file) + if (swap_file) { + if (did_down) { + mutex_unlock(&inode->i_mutex); + did_down = 0; + } filp_close(swap_file, NULL); + } out: if (page && !IS_ERR(page)) { kunmap(page); From 3161a55f0b8f04371a402324236c32452aacf443 Mon Sep 17 00:00:00 2001 From: Andrea Arcangeli Date: Tue, 22 Mar 2011 16:30:38 -0700 Subject: [PATCH 0911/2556] mm: compaction: prevent kswapd compacting memory to reduce CPU usage commit d527caf22e48480b102c7c6ee5b9ba12170148f7 upstream. This patch reverts 5a03b051 ("thp: use compaction in kswapd for GFP_ATOMIC order > 0") due to reports stating that kswapd CPU usage was higher and IRQs were being disabled more frequently. This was reported at http://www.spinics.net/linux/fedora/alsa-user/msg09885.html. Without this patch applied, CPU usage by kswapd hovers around the 20% mark according to the tester (Arthur Marsh: http://www.spinics.net/linux/fedora/alsa-user/msg09899.html). With this patch applied, it's around 2%. The problem is not related to THP which specifies __GFP_NO_KSWAPD but is triggered by high-order allocations hitting the low watermark for their order and waking kswapd on kernels with CONFIG_COMPACTION set. The most common trigger for this is network cards configured for jumbo frames but it's also possible it'll be triggered by fork-heavy workloads (order-1) and some wireless cards which depend on order-1 allocations. The symptoms for the user will be high CPU usage by kswapd in low-memory situations which could be confused with another writeback problem. While a patch like 5a03b051 may be reintroduced in the future, this patch plays it safe for now and reverts it. [mel@csn.ul.ie: Beefed up the changelog] Signed-off-by: Andrea Arcangeli Signed-off-by: Mel Gorman Reported-by: Arthur Marsh Tested-by: Arthur Marsh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- include/linux/compaction.h | 9 ++------- mm/compaction.c | 24 +++--------------------- mm/vmscan.c | 18 +----------------- 3 files changed, 6 insertions(+), 45 deletions(-) diff --git a/include/linux/compaction.h b/include/linux/compaction.h index dfa2ed4c0d26a..cc9f7a4286490 100644 --- a/include/linux/compaction.h +++ b/include/linux/compaction.h @@ -11,9 +11,6 @@ /* The full zone was compacted */ #define COMPACT_COMPLETE 3 -#define COMPACT_MODE_DIRECT_RECLAIM 0 -#define COMPACT_MODE_KSWAPD 1 - #ifdef CONFIG_COMPACTION extern int sysctl_compact_memory; extern int sysctl_compaction_handler(struct ctl_table *table, int write, @@ -28,8 +25,7 @@ extern unsigned long try_to_compact_pages(struct zonelist *zonelist, bool sync); extern unsigned long compaction_suitable(struct zone *zone, int order); extern unsigned long compact_zone_order(struct zone *zone, int order, - gfp_t gfp_mask, bool sync, - int compact_mode); + gfp_t gfp_mask, bool sync); /* Do not skip compaction more than 64 times */ #define COMPACT_MAX_DEFER_SHIFT 6 @@ -74,8 +70,7 @@ static inline unsigned long compaction_suitable(struct zone *zone, int order) } static inline unsigned long compact_zone_order(struct zone *zone, int order, - gfp_t gfp_mask, bool sync, - int compact_mode) + gfp_t gfp_mask, bool sync) { return COMPACT_CONTINUE; } diff --git a/mm/compaction.c b/mm/compaction.c index 8be430b812def..dcb058bd76c42 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -42,8 +42,6 @@ struct compact_control { unsigned int order; /* order a direct compactor needs */ int migratetype; /* MOVABLE, RECLAIMABLE etc */ struct zone *zone; - - int compact_mode; }; static unsigned long release_freepages(struct list_head *freelist) @@ -397,10 +395,7 @@ static int compact_finished(struct zone *zone, return COMPACT_COMPLETE; /* Compaction run is not finished if the watermark is not met */ - if (cc->compact_mode != COMPACT_MODE_KSWAPD) - watermark = low_wmark_pages(zone); - else - watermark = high_wmark_pages(zone); + watermark = low_wmark_pages(zone); watermark += (1 << cc->order); if (!zone_watermark_ok(zone, cc->order, watermark, 0, 0)) @@ -413,15 +408,6 @@ static int compact_finished(struct zone *zone, if (cc->order == -1) return COMPACT_CONTINUE; - /* - * Generating only one page of the right order is not enough - * for kswapd, we must continue until we're above the high - * watermark as a pool for high order GFP_ATOMIC allocations - * too. - */ - if (cc->compact_mode == COMPACT_MODE_KSWAPD) - return COMPACT_CONTINUE; - /* Direct compactor: Is a suitable page free? */ for (order = cc->order; order < MAX_ORDER; order++) { /* Job done if page is free of the right migratetype */ @@ -543,8 +529,7 @@ static int compact_zone(struct zone *zone, struct compact_control *cc) unsigned long compact_zone_order(struct zone *zone, int order, gfp_t gfp_mask, - bool sync, - int compact_mode) + bool sync) { struct compact_control cc = { .nr_freepages = 0, @@ -553,7 +538,6 @@ unsigned long compact_zone_order(struct zone *zone, .migratetype = allocflags_to_migratetype(gfp_mask), .zone = zone, .sync = sync, - .compact_mode = compact_mode, }; INIT_LIST_HEAD(&cc.freepages); INIT_LIST_HEAD(&cc.migratepages); @@ -599,8 +583,7 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist, nodemask) { int status; - status = compact_zone_order(zone, order, gfp_mask, sync, - COMPACT_MODE_DIRECT_RECLAIM); + status = compact_zone_order(zone, order, gfp_mask, sync); rc = max(status, rc); /* If a normal allocation would succeed, stop compacting */ @@ -631,7 +614,6 @@ static int compact_node(int nid) .nr_freepages = 0, .nr_migratepages = 0, .order = -1, - .compact_mode = COMPACT_MODE_DIRECT_RECLAIM, }; zone = &pgdat->node_zones[zoneid]; diff --git a/mm/vmscan.c b/mm/vmscan.c index 6771ea70bfe7e..3b4a41d724894 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2397,7 +2397,6 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, * cause too much scanning of the lower zones. */ for (i = 0; i <= end_zone; i++) { - int compaction; struct zone *zone = pgdat->node_zones + i; int nr_slab; @@ -2428,24 +2427,9 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, sc.nr_reclaimed += reclaim_state->reclaimed_slab; total_scanned += sc.nr_scanned; - compaction = 0; - if (order && - zone_watermark_ok(zone, 0, - high_wmark_pages(zone), - end_zone, 0) && - !zone_watermark_ok(zone, order, - high_wmark_pages(zone), - end_zone, 0)) { - compact_zone_order(zone, - order, - sc.gfp_mask, false, - COMPACT_MODE_KSWAPD); - compaction = 1; - } - if (zone->all_unreclaimable) continue; - if (!compaction && nr_slab == 0 && + if (nr_slab == 0 && !zone_reclaimable(zone)) zone->all_unreclaimable = 1; /* From dafac92013aec4842d48b85f3b00b23c9a3712d2 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 22 Mar 2011 16:30:09 -0700 Subject: [PATCH 0912/2556] oom: prevent unnecessary oom kills or kernel panics commit 3a5dda7a17cf3706f79b86293f29db02d61e0d48 upstream. This patch prevents unnecessary oom kills or kernel panics by reverting two commits: 495789a5 (oom: make oom_score to per-process value) cef1d352 (oom: multi threaded process coredump don't make deadlock) First, 495789a5 (oom: make oom_score to per-process value) ignores the fact that all threads in a thread group do not necessarily exit at the same time. It is imperative that select_bad_process() detect threads that are in the exit path, specifically those with PF_EXITING set, to prevent needlessly killing additional tasks. If a process is oom killed and the thread group leader exits, select_bad_process() cannot detect the other threads that are PF_EXITING by iterating over only processes. Thus, it currently chooses another task unnecessarily for oom kill or panics the machine when nothing else is eligible. By iterating over threads instead, it is possible to detect threads that are exiting and nominate them for oom kill so they get access to memory reserves. Second, cef1d352 (oom: multi threaded process coredump don't make deadlock) erroneously avoids making the oom killer a no-op when an eligible thread other than current isfound to be exiting. We want to detect this situation so that we may allow that exiting thread time to exit and free its memory; if it is able to exit on its own, that should free memory so current is no loner oom. If it is not able to exit on its own, the oom killer will nominate it for oom kill which, in this case, only means it will get access to memory reserves. Without this change, it is easy for the oom killer to unnecessarily target tasks when all threads of a victim don't exit before the thread group leader or, in the worst case, panic the machine. Signed-off-by: David Rientjes Cc: KOSAKI Motohiro Cc: KAMEZAWA Hiroyuki Cc: Oleg Nesterov Cc: Hugh Dickins Cc: Andrey Vagin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/oom_kill.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mm/oom_kill.c b/mm/oom_kill.c index 7dcca55ede7ca..b5a7b5f46e7ac 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -292,11 +292,11 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, unsigned long totalpages, struct mem_cgroup *mem, const nodemask_t *nodemask) { - struct task_struct *p; + struct task_struct *g, *p; struct task_struct *chosen = NULL; *ppoints = 0; - for_each_process(p) { + do_each_thread(g, p) { unsigned int points; if (oom_unkillable_task(p, mem, nodemask)) @@ -324,7 +324,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, * the process of exiting and releasing its resources. * Otherwise we could get an easy OOM deadlock. */ - if (thread_group_empty(p) && (p->flags & PF_EXITING) && p->mm) { + if ((p->flags & PF_EXITING) && p->mm) { if (p != current) return ERR_PTR(-1UL); @@ -337,7 +337,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, chosen = p; *ppoints = points; } - } + } while_each_thread(g, p); return chosen; } From 61dc6727042603518fc54cab824ae6ba846ef969 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Tue, 22 Mar 2011 16:30:11 -0700 Subject: [PATCH 0913/2556] oom: skip zombies when iterating tasklist commit 30e2b41f20b6238f51e7cffb879c7a0f0073f5fe upstream. We shouldn't defer oom killing if a thread has already detached its ->mm and still has TIF_MEMDIE set. Memory needs to be freed, so find kill other threads that pin the same ->mm or find another task to kill. Signed-off-by: Andrey Vagin Signed-off-by: David Rientjes Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/oom_kill.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mm/oom_kill.c b/mm/oom_kill.c index b5a7b5f46e7ac..d7f345e47e73a 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -299,6 +299,8 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, do_each_thread(g, p) { unsigned int points; + if (!p->mm) + continue; if (oom_unkillable_task(p, mem, nodemask)) continue; @@ -324,7 +326,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, * the process of exiting and releasing its resources. * Otherwise we could get an easy OOM deadlock. */ - if ((p->flags & PF_EXITING) && p->mm) { + if (p->flags & PF_EXITING) { if (p != current) return ERR_PTR(-1UL); From aa21f1ad91cd7f0931a284aa460e59ec48bce93f Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 22 Mar 2011 16:30:12 -0700 Subject: [PATCH 0914/2556] oom: avoid deferring oom killer if exiting task is being traced commit edd45544c6f09550df0a5491aa8a07af24767e73 upstream. The oom killer naturally defers killing anything if it finds an eligible task that is already exiting and has yet to detach its ->mm. This avoids unnecessarily killing tasks when one is already in the exit path and may free enough memory that the oom killer is no longer needed. This is detected by PF_EXITING since threads that have already detached its ->mm are no longer considered at all. The problem with always deferring when a thread is PF_EXITING, however, is that it may never actually exit when being traced, specifically if another task is tracing it with PTRACE_O_TRACEEXIT. The oom killer does not want to defer in this case since there is no guarantee that thread will ever exit without intervention. This patch will now only defer the oom killer when a thread is PF_EXITING and no ptracer has stopped its progress in the exit path. It also ensures that a child is sacrificed for the chosen parent only if it has a different ->mm as the comment implies: this ensures that the thread group leader is always targeted appropriately. Signed-off-by: David Rientjes Reported-by: Oleg Nesterov Cc: KOSAKI Motohiro Cc: KAMEZAWA Hiroyuki Cc: Hugh Dickins Cc: Andrey Vagin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/oom_kill.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/mm/oom_kill.c b/mm/oom_kill.c index d7f345e47e73a..33b58615072cc 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -31,6 +31,7 @@ #include #include #include +#include int sysctl_panic_on_oom; int sysctl_oom_kill_allocating_task; @@ -316,22 +317,29 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, if (test_tsk_thread_flag(p, TIF_MEMDIE)) return ERR_PTR(-1UL); - /* - * This is in the process of releasing memory so wait for it - * to finish before killing some other task by mistake. - * - * However, if p is the current task, we allow the 'kill' to - * go ahead if it is exiting: this will simply set TIF_MEMDIE, - * which will allow it to gain access to memory reserves in - * the process of exiting and releasing its resources. - * Otherwise we could get an easy OOM deadlock. - */ if (p->flags & PF_EXITING) { - if (p != current) - return ERR_PTR(-1UL); - - chosen = p; - *ppoints = 1000; + /* + * If p is the current task and is in the process of + * releasing memory, we allow the "kill" to set + * TIF_MEMDIE, which will allow it to gain access to + * memory reserves. Otherwise, it may stall forever. + * + * The loop isn't broken here, however, in case other + * threads are found to have already been oom killed. + */ + if (p == current) { + chosen = p; + *ppoints = 1000; + } else { + /* + * If this task is not being ptraced on exit, + * then wait for it to finish before killing + * some other task unnecessarily. + */ + if (!(task_ptrace(p->group_leader) & + PT_TRACE_EXIT)) + return ERR_PTR(-1UL); + } } points = oom_badness(p, mem, nodemask, totalpages); @@ -493,6 +501,8 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, list_for_each_entry(child, &t->children, sibling) { unsigned int child_points; + if (child->mm == p->mm) + continue; /* * oom_badness() returns 0 if the thread is unkillable */ From b7e9c49620471eb16eacf76fd4083202ab668779 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Mon, 28 Feb 2011 16:20:11 +0000 Subject: [PATCH 0915/2556] PCI hotplug: acpiphp: set current_state to D0 in register_slot commit 47e9037ac16637cd7f12b8790ea7ce6680e42168 upstream. If a device doesn't support power management (pm_cap == 0) but it is acpi_pci_power_manageable() because there is a _PS0 method declared for it and _EJ0 is also declared for the slot then nobody is going to set current_state = PCI_D0 for this device. This is what I think it is happening: pci_enable_device | __pci_enable_device_flags /* here we do not set current_state because !pm_cap */ | do_pci_enable_device | pci_set_power_state | __pci_start_power_transition | pci_platform_power_transition /* platform_pci_power_manageable() calls acpi_pci_power_manageable that * returns true */ | platform_pci_set_power_state /* acpi_pci_set_power_state gets called and does nothing because the * acpi device has _EJ0, see the comment "If the ACPI device has _EJ0, * ignore the device" */ at this point if we refer to the commit message that introduced the comment above (10b3dcae0f275e2546e55303d64ddbb58cec7599), it is up to the hotplug driver to set the state to D0. However AFAICT the pci hotplug driver never does, in fact drivers/pci/hotplug/acpiphp_glue.c:register_slot sets the slot flags to (SLOT_ENABLED | SLOT_POWEREDON) but it does not set the pci device current state to PCI_D0. So my proposed fix is also to set current_state = PCI_D0 in register_slot. Comments are very welcome. Signed-off-by: Stefano Stabellini Signed-off-by: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- drivers/pci/hotplug/acpiphp_glue.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index cb23aa2ebf96b..e610cfe4f07bc 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -212,6 +212,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) pdev = pci_get_slot(pbus, PCI_DEVFN(device, function)); if (pdev) { + pdev->current_state = PCI_D0; slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON); pci_dev_put(pdev); } From 0cc128624b4cb884d99220f23c529d8d8dd35d18 Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Wed, 16 Mar 2011 22:11:46 -0700 Subject: [PATCH 0916/2556] Input: xen-kbdfront - advertise either absolute or relative coordinates commit 8c3c283e6bf463ab498d6e7823aff6c4762314b6 upstream. A virtualized display device is usually viewed with the vncviewer application, either by 'xm vnc domU' or with vncviewer localhost:port. vncviewer and the RFB protocol provides absolute coordinates to the virtual display. These coordinates are either passed through to a PV guest or converted to relative coordinates for a HVM guest. A PV guest receives these coordinates and passes them to the kernels evdev driver. There it can be picked up by applications such as the xorg-input drivers. Using absolute coordinates avoids issues such as guest mouse pointer not tracking host mouse pointer due to wrong mouse acceleration settings in the guests X display. Advertise either absolute or relative coordinates to the input system and the evdev driver, depending on what dom0 provides. The xorg-input driver prefers relative coordinates even if a devices provides both. Signed-off-by: Olaf Hering Signed-off-by: Stefano Stabellini Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/xen-kbdfront.c | 44 ++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c index 7f85a862ad116..53e62732ee96f 100644 --- a/drivers/input/xen-kbdfront.c +++ b/drivers/input/xen-kbdfront.c @@ -110,7 +110,7 @@ static irqreturn_t input_handler(int rq, void *dev_id) static int __devinit xenkbd_probe(struct xenbus_device *dev, const struct xenbus_device_id *id) { - int ret, i; + int ret, i, abs; struct xenkbd_info *info; struct input_dev *kbd, *ptr; @@ -128,6 +128,11 @@ static int __devinit xenkbd_probe(struct xenbus_device *dev, if (!info->page) goto error_nomem; + if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-abs-pointer", "%d", &abs) < 0) + abs = 0; + if (abs) + xenbus_printf(XBT_NIL, dev->nodename, "request-abs-pointer", "1"); + /* keyboard */ kbd = input_allocate_device(); if (!kbd) @@ -137,11 +142,12 @@ static int __devinit xenkbd_probe(struct xenbus_device *dev, kbd->id.bustype = BUS_PCI; kbd->id.vendor = 0x5853; kbd->id.product = 0xffff; - kbd->evbit[0] = BIT(EV_KEY); + + __set_bit(EV_KEY, kbd->evbit); for (i = KEY_ESC; i < KEY_UNKNOWN; i++) - set_bit(i, kbd->keybit); + __set_bit(i, kbd->keybit); for (i = KEY_OK; i < KEY_MAX; i++) - set_bit(i, kbd->keybit); + __set_bit(i, kbd->keybit); ret = input_register_device(kbd); if (ret) { @@ -160,12 +166,20 @@ static int __devinit xenkbd_probe(struct xenbus_device *dev, ptr->id.bustype = BUS_PCI; ptr->id.vendor = 0x5853; ptr->id.product = 0xfffe; - ptr->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS); + + if (abs) { + __set_bit(EV_ABS, ptr->evbit); + input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0); + input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0); + } else { + input_set_capability(ptr, EV_REL, REL_X); + input_set_capability(ptr, EV_REL, REL_Y); + } + input_set_capability(ptr, EV_REL, REL_WHEEL); + + __set_bit(EV_KEY, ptr->evbit); for (i = BTN_LEFT; i <= BTN_TASK; i++) - set_bit(i, ptr->keybit); - ptr->relbit[0] = BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL); - input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0); - input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0); + __set_bit(i, ptr->keybit); ret = input_register_device(ptr); if (ret) { @@ -272,7 +286,7 @@ static void xenkbd_backend_changed(struct xenbus_device *dev, enum xenbus_state backend_state) { struct xenkbd_info *info = dev_get_drvdata(&dev->dev); - int ret, val; + int val; switch (backend_state) { case XenbusStateInitialising: @@ -285,16 +299,6 @@ static void xenkbd_backend_changed(struct xenbus_device *dev, case XenbusStateInitWait: InitWait: - ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend, - "feature-abs-pointer", "%d", &val); - if (ret < 0) - val = 0; - if (val) { - ret = xenbus_printf(XBT_NIL, info->xbdev->nodename, - "request-abs-pointer", "1"); - if (ret) - pr_warning("can't request abs-pointer\n"); - } xenbus_switch_state(dev, XenbusStateConnected); break; From 105e068cce2b421cc21cff09d6a92fb96d09d79c Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 18 Feb 2011 11:32:40 +0000 Subject: [PATCH 0917/2556] xen: set max_pfn_mapped to the last pfn mapped commit 14988a4d350ce3b41ecad4f63c4f44c56f5ae34d upstream. Do not set max_pfn_mapped to the end of the initial memory mappings, that also contain pages that don't belong in pfn space (like the mfn list). Set max_pfn_mapped to the last real pfn mapped in the initial memory mappings that is the pfn backing _end. Signed-off-by: Stefano Stabellini Acked-by: Konrad Rzeszutek Wilk LKML-Reference: Signed-off-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/xen/mmu.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index f6089421147a0..60205625e47e0 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -1651,9 +1651,6 @@ static __init void xen_map_identity_early(pmd_t *pmd, unsigned long max_pfn) for (pteidx = 0; pteidx < PTRS_PER_PTE; pteidx++, pfn++) { pte_t pte; - if (pfn > max_pfn_mapped) - max_pfn_mapped = pfn; - if (!pte_none(pte_page[pteidx])) continue; @@ -1711,6 +1708,12 @@ __init pgd_t *xen_setup_kernel_pagetable(pgd_t *pgd, pud_t *l3; pmd_t *l2; + /* max_pfn_mapped is the last pfn mapped in the initial memory + * mappings. Considering that on Xen after the kernel mappings we + * have the mappings of some pages that don't exist in pfn space, we + * set max_pfn_mapped to the last real pfn mapped. */ + max_pfn_mapped = PFN_DOWN(__pa(xen_start_info->mfn_list)); + /* Zap identity mapping */ init_level4_pgt[0] = __pgd(0); @@ -1815,9 +1818,7 @@ __init pgd_t *xen_setup_kernel_pagetable(pgd_t *pgd, initial_kernel_pmd = extend_brk(sizeof(pmd_t) * PTRS_PER_PMD, PAGE_SIZE); - max_pfn_mapped = PFN_DOWN(__pa(xen_start_info->pt_base) + - xen_start_info->nr_pt_frames * PAGE_SIZE + - 512*1024); + max_pfn_mapped = PFN_DOWN(__pa(xen_start_info->mfn_list)); kernel_pmd = m2v(pgd[KERNEL_PGD_BOUNDARY].pgd); memcpy(initial_kernel_pmd, kernel_pmd, sizeof(pmd_t) * PTRS_PER_PMD); From 9c3e0c3631fb3ba65275be84b19c71be21ce217b Mon Sep 17 00:00:00 2001 From: Julien Tinnes Date: Fri, 18 Mar 2011 15:05:21 -0700 Subject: [PATCH 0918/2556] Prevent rt_sigqueueinfo and rt_tgsigqueueinfo from spoofing the signal code commit da48524eb20662618854bb3df2db01fc65f3070c upstream. Userland should be able to trust the pid and uid of the sender of a signal if the si_code is SI_TKILL. Unfortunately, the kernel has historically allowed sigqueueinfo() to send any si_code at all (as long as it was negative - to distinguish it from kernel-generated signals like SIGILL etc), so it could spoof a SI_TKILL with incorrect siginfo values. Happily, it looks like glibc has always set si_code to the appropriate SI_QUEUE, so there are probably no actual user code that ever uses anything but the appropriate SI_QUEUE flag. So just tighten the check for si_code (we used to allow any negative value), and add a (one-time) warning in case there are binaries out there that might depend on using other si_code values. Signed-off-by: Julien Tinnes Acked-by: Oleg Nesterov Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/signal.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/kernel/signal.c b/kernel/signal.c index 4e3cff10fdced..31751868de885 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2421,9 +2421,13 @@ SYSCALL_DEFINE3(rt_sigqueueinfo, pid_t, pid, int, sig, return -EFAULT; /* Not even root can pretend to send signals from the kernel. - Nor can they impersonate a kill(), which adds source info. */ - if (info.si_code >= 0) + * Nor can they impersonate a kill()/tgkill(), which adds source info. + */ + if (info.si_code != SI_QUEUE) { + /* We used to allow any < 0 si_code */ + WARN_ON_ONCE(info.si_code < 0); return -EPERM; + } info.si_signo = sig; /* POSIX.1b doesn't mention process groups. */ @@ -2437,9 +2441,13 @@ long do_rt_tgsigqueueinfo(pid_t tgid, pid_t pid, int sig, siginfo_t *info) return -EINVAL; /* Not even root can pretend to send signals from the kernel. - Nor can they impersonate a kill(), which adds source info. */ - if (info->si_code >= 0) + * Nor can they impersonate a kill()/tgkill(), which adds source info. + */ + if (info->si_code != SI_QUEUE) { + /* We used to allow any < 0 si_code */ + WARN_ON_ONCE(info->si_code < 0); return -EPERM; + } info->si_signo = sig; return do_send_specific(tgid, pid, sig, info); From ba5fe4034786b89056cd045cab13c826abce5642 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 22 Mar 2011 10:23:28 +0100 Subject: [PATCH 0919/2556] ALSA: HDA: Fix internal mic on Dell E5420/E5520 This is a fixup for the 2.6.38 kernel, as the issue is being resolved by upstream commits 699d899560cd7e72da39231e584412e7ac8114a4 and 094a42452abd5564429045e210281c6d22e67fca - which are too invasive to reach 2.6.38. Instead make pin fixes as a workaround. BugLink: http://bugs.launchpad.net/bugs/740055 Tested-by: Kent Baxley Signed-off-by: David Henningsson Acked-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 052062dd523ba..856611909c413 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -94,6 +94,7 @@ enum { STAC_92HD83XXX_REF, STAC_92HD83XXX_PWR_REF, STAC_DELL_S14, + STAC_DELL_E5520M, STAC_92HD83XXX_HP, STAC_HP_DV7_4000, STAC_92HD83XXX_MODELS @@ -1657,6 +1658,13 @@ static unsigned int dell_s14_pin_configs[10] = { 0x40f000f0, 0x40f000f0, }; +/* Switch int mic from 0x20 to 0x11 */ +static unsigned int dell_e5520m_pin_configs[10] = { + 0x04a11020, 0x0421101f, 0x400000f0, 0x90170110, + 0x23011050, 0x23a1102e, 0x400000f3, 0xd5a30130, + 0x400000f0, 0x40f000f0, +}; + static unsigned int hp_dv7_4000_pin_configs[10] = { 0x03a12050, 0x0321201f, 0x40f000f0, 0x90170110, 0x40f000f0, 0x40f000f0, 0x90170110, 0xd5a30140, @@ -1667,6 +1675,7 @@ static unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = { [STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs, [STAC_92HD83XXX_PWR_REF] = ref92hd83xxx_pin_configs, [STAC_DELL_S14] = dell_s14_pin_configs, + [STAC_DELL_E5520M] = dell_e5520m_pin_configs, [STAC_HP_DV7_4000] = hp_dv7_4000_pin_configs, }; @@ -1675,6 +1684,7 @@ static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { [STAC_92HD83XXX_REF] = "ref", [STAC_92HD83XXX_PWR_REF] = "mic-ref", [STAC_DELL_S14] = "dell-s14", + [STAC_DELL_E5520M] = "dell-e5520m", [STAC_92HD83XXX_HP] = "hp", [STAC_HP_DV7_4000] = "hp-dv7-4000", }; @@ -1687,6 +1697,14 @@ static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { "DFI LanParty", STAC_92HD83XXX_REF), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba, "unknown Dell", STAC_DELL_S14), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x049a, + "Dell E5520", STAC_DELL_E5520M), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x049b, + "Dell E5420", STAC_DELL_E5520M), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x04eb, + "Dell E5420m", STAC_DELL_E5520M), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x04ec, + "Dell E5520m", STAC_DELL_E5520M), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x3600, "HP", STAC_92HD83XXX_HP), {} /* terminator */ From c55022da99894057a1e20c6cd530737ab672f6fc Mon Sep 17 00:00:00 2001 From: Andrea Arcangeli Date: Fri, 18 Mar 2011 00:16:35 +0100 Subject: [PATCH 0920/2556] mm: PageBuddy and mapcount robustness commit ef2b4b95a63a1d23958dcb99eb2c6898eddc87d0 upstream. Change the _mapcount value indicating PageBuddy from -2 to -128 for more robusteness against page_mapcount() undeflows. Use reset_page_mapcount instead of __ClearPageBuddy in bad_page to ignore the previous retval of PageBuddy(). Signed-off-by: Andrea Arcangeli Reported-by: Hugh Dickins Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- include/linux/mm.h | 11 +++++++++-- mm/page_alloc.c | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index cdd44722cbc21..25b6461ecaa5a 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -402,16 +402,23 @@ static inline void init_page_count(struct page *page) /* * PageBuddy() indicate that the page is free and in the buddy system * (see mm/page_alloc.c). + * + * PAGE_BUDDY_MAPCOUNT_VALUE must be <= -2 but better not too close to + * -2 so that an underflow of the page_mapcount() won't be mistaken + * for a genuine PAGE_BUDDY_MAPCOUNT_VALUE. -128 can be created very + * efficiently by most CPU architectures. */ +#define PAGE_BUDDY_MAPCOUNT_VALUE (-128) + static inline int PageBuddy(struct page *page) { - return atomic_read(&page->_mapcount) == -2; + return atomic_read(&page->_mapcount) == PAGE_BUDDY_MAPCOUNT_VALUE; } static inline void __SetPageBuddy(struct page *page) { VM_BUG_ON(atomic_read(&page->_mapcount) != -1); - atomic_set(&page->_mapcount, -2); + atomic_set(&page->_mapcount, PAGE_BUDDY_MAPCOUNT_VALUE); } static inline void __ClearPageBuddy(struct page *page) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index c9e283d3ac86c..eabfeeba7912a 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -287,7 +287,7 @@ static void bad_page(struct page *page) /* Don't complain about poisoned pages */ if (PageHWPoison(page)) { - __ClearPageBuddy(page); + reset_page_mapcount(page); /* remove PageBuddy */ return; } @@ -318,7 +318,7 @@ static void bad_page(struct page *page) dump_stack(); out: /* Leave bad fields for debug, except PageBuddy could make trouble */ - __ClearPageBuddy(page); + reset_page_mapcount(page); /* remove PageBuddy */ add_taint(TAINT_BAD_PAGE); } From 93f49de503462b2e7b24bcac6da64a991b383cc9 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Sat, 26 Feb 2011 22:40:19 +0200 Subject: [PATCH 0921/2556] ext3: skip orphan cleanup on rocompat fs commit ce654b37f87980d95f339080e4c3bdb2370bdf22 upstream. Orphan cleanup is currently executed even if the file system has some number of unknown ROCOMPAT features, which deletes inodes and frees blocks, which could be very bad for some RO_COMPAT features. This patch skips the orphan cleanup if it contains readonly compatible features not known by this ext3 implementation, which would prevent the fs from being mounted (or remounted) readwrite. Signed-off-by: Amir Goldstein Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/ext3/super.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 85c8cc8f24732..0d62f29f213a1 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -1464,6 +1464,13 @@ static void ext3_orphan_cleanup (struct super_block * sb, return; } + /* Check if feature set allows readwrite operations */ + if (EXT3_HAS_RO_COMPAT_FEATURE(sb, ~EXT3_FEATURE_RO_COMPAT_SUPP)) { + ext3_msg(sb, KERN_INFO, "Skipping orphan cleanup due to " + "unknown ROCOMPAT features"); + return; + } + if (EXT3_SB(sb)->s_mount_state & EXT3_ERROR_FS) { if (es->s_last_orphan) jbd_debug(1, "Errors on filesystem, " From b691482c6b72f071673b14a3ca34759f9905428c Mon Sep 17 00:00:00 2001 From: Sedat Dilek Date: Tue, 8 Mar 2011 22:39:24 +0100 Subject: [PATCH 0922/2556] x86: Fix binutils-2.21 symbol related build failures commit 2ae9d293b14d17f35eff624272cfecac7979a2ee upstream. [only 1/2 of the upstream commit was needed for stable - gkh] New binutils version 2.21.0.20110302-1 started checking that the symbol parameter to the .size directive matches the entry name's symbol parameter, unearthing two mismatches: AS arch/x86/kernel/acpi/wakeup_rm.o arch/x86/kernel/acpi/wakeup_rm.S: Assembler messages: arch/x86/kernel/acpi/wakeup_rm.S:12: Error: .size expression with symbol `wakeup_code_start' does not evaluate to a constant arch/x86/kernel/entry_32.S: Assembler messages: arch/x86/kernel/entry_32.S:1421: Error: .size expression with symbol `apf_page_fault' does not evaluate to a constant The problem was discovered while using Debian's binutils (2.21.0.20110302-1) and experimenting with binutils from upstream. Thanks Alexander and H.J. for the vital help. Signed-off-by: Sedat Dilek Cc: Alexander van Heukelum Cc: H.J. Lu Cc: Len Brown Cc: Pavel Machek Cc: Rafael J. Wysocki LKML-Reference: <1299620364-21644-1-git-send-email-sedat.dilek@gmail.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/entry_32.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index c8b4efad7ebb0..9ca3b0e343e5d 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S @@ -1413,7 +1413,7 @@ ENTRY(async_page_fault) CFI_ADJUST_CFA_OFFSET 4 jmp error_code CFI_ENDPROC -END(apf_page_fault) +END(async_page_fault) #endif /* From 0fd9211a57925c828d414287964cd21fcdb8ddfc Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Wed, 23 Mar 2011 16:43:11 -0700 Subject: [PATCH 0923/2556] sysctl: restrict write access to dmesg_restrict commit bfdc0b497faa82a0ba2f9dddcf109231dd519fcc upstream. When dmesg_restrict is set to 1 CAP_SYS_ADMIN is needed to read the kernel ring buffer. But a root user without CAP_SYS_ADMIN is able to reset dmesg_restrict to 0. This is an issue when e.g. LXC (Linux Containers) are used and complete user space is running without CAP_SYS_ADMIN. A unprivileged and jailed root user can bypass the dmesg_restrict protection. With this patch writing to dmesg_restrict is only allowed when root has CAP_SYS_ADMIN. Signed-off-by: Richard Weinberger Acked-by: Dan Rosenberg Acked-by: Serge E. Hallyn Cc: Eric Paris Cc: Kees Cook Cc: James Morris Cc: Eugene Teo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/sysctl.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/kernel/sysctl.c b/kernel/sysctl.c index a26c37df1b19f..4bc1435706b0d 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -170,6 +170,11 @@ static int proc_taint(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); #endif +#ifdef CONFIG_PRINTK +static int proc_dmesg_restrict(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); +#endif + #ifdef CONFIG_MAGIC_SYSRQ /* Note: sysrq code uses it's own private copy */ static int __sysrq_enabled = SYSRQ_DEFAULT_ENABLE; @@ -714,7 +719,7 @@ static struct ctl_table kern_table[] = { .data = &kptr_restrict, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec_minmax, + .proc_handler = proc_dmesg_restrict, .extra1 = &zero, .extra2 = &two, }, @@ -2405,6 +2410,17 @@ static int proc_taint(struct ctl_table *table, int write, return err; } +#ifdef CONFIG_PRINTK +static int proc_dmesg_restrict(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + if (write && !capable(CAP_SYS_ADMIN)) + return -EPERM; + + return proc_dointvec_minmax(table, write, buffer, lenp, ppos); +} +#endif + struct do_proc_dointvec_minmax_conv_param { int *min; int *max; From 8562d2e5e78b816c60518ad3bf2171ad5ae8498c Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Wed, 23 Mar 2011 16:42:50 -0700 Subject: [PATCH 0924/2556] procfs: fix /proc//maps heap check commit 0db0c01b53a1a421513f91573241aabafb87802a upstream. The current code fails to print the "[heap]" marking if the heap is split into multiple mappings. Fix the check so that the marking is displayed in all possible cases: 1. vma matches exactly the heap 2. the heap vma is merged e.g. with bss 3. the heap vma is splitted e.g. due to locked pages Test cases. In all cases, the process should have mapping(s) with [heap] marking: (1) vma matches exactly the heap #include #include #include int main (void) { if (sbrk(4096) != (void *)-1) { printf("check /proc/%d/maps\n", (int)getpid()); while (1) sleep(1); } return 0; } # ./test1 check /proc/553/maps [1] + Stopped ./test1 # cat /proc/553/maps | head -4 00008000-00009000 r-xp 00000000 01:00 3113640 /test1 00010000-00011000 rw-p 00000000 01:00 3113640 /test1 00011000-00012000 rw-p 00000000 00:00 0 [heap] 4006f000-40070000 rw-p 00000000 00:00 0 (2) the heap vma is merged #include #include #include char foo[4096] = "foo"; char bar[4096]; int main (void) { if (sbrk(4096) != (void *)-1) { printf("check /proc/%d/maps\n", (int)getpid()); while (1) sleep(1); } return 0; } # ./test2 check /proc/556/maps [2] + Stopped ./test2 # cat /proc/556/maps | head -4 00008000-00009000 r-xp 00000000 01:00 3116312 /test2 00010000-00012000 rw-p 00000000 01:00 3116312 /test2 00012000-00014000 rw-p 00000000 00:00 0 [heap] 4004a000-4004b000 rw-p 00000000 00:00 0 (3) the heap vma is splitted (this fails without the patch) #include #include #include #include int main (void) { if ((sbrk(4096) != (void *)-1) && !mlockall(MCL_FUTURE) && (sbrk(4096) != (void *)-1)) { printf("check /proc/%d/maps\n", (int)getpid()); while (1) sleep(1); } return 0; } # ./test3 check /proc/559/maps [1] + Stopped ./test3 # cat /proc/559/maps|head -4 00008000-00009000 r-xp 00000000 01:00 3119108 /test3 00010000-00011000 rw-p 00000000 01:00 3119108 /test3 00011000-00012000 rw-p 00000000 00:00 0 [heap] 00012000-00013000 rw-p 00000000 00:00 0 [heap] It looks like the bug has been there forever, and since it only results in some information missing from a procfile, it does not fulfil the -stable "critical issue" criteria. Signed-off-by: Aaro Koskinen Reviewed-by: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/proc/task_mmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 60b914860f815..f269ee673c8bf 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -249,8 +249,8 @@ static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma) const char *name = arch_vma_name(vma); if (!name) { if (mm) { - if (vma->vm_start <= mm->start_brk && - vma->vm_end >= mm->brk) { + if (vma->vm_start <= mm->brk && + vma->vm_end >= mm->start_brk) { name = "[heap]"; } else if (vma->vm_start <= mm->start_stack && vma->vm_end >= mm->start_stack) { From 3ce51c9a071f9fc633d7dbb7c5eb04cd3b7059c3 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 23 Mar 2011 16:42:53 -0700 Subject: [PATCH 0925/2556] proc: protect mm start_code/end_code in /proc/pid/stat commit 5883f57ca0008ffc93e09cbb9847a1928e50c6f3 upstream. While mm->start_stack was protected from cross-uid viewing (commit f83ce3e6b02d5 ("proc: avoid information leaks to non-privileged processes")), the start_code and end_code values were not. This would allow the text location of a PIE binary to leak, defeating ASLR. Note that the value "1" is used instead of "0" for a protected value since "ps", "killall", and likely other readers of /proc/pid/stat, take start_code of "0" to mean a kernel thread and will misbehave. Thanks to Brad Spengler for pointing this out. Addresses CVE-2011-0726 Signed-off-by: Kees Cook Cc: Alexey Dobriyan Cc: David Howells Cc: Eugene Teo Cc: Martin Schwidefsky Cc: Brad Spengler Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/proc/array.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/proc/array.c b/fs/proc/array.c index 7c99c1cf7e5c4..5e4f776b0917a 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -489,8 +489,8 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, vsize, mm ? get_mm_rss(mm) : 0, rsslim, - mm ? mm->start_code : 0, - mm ? mm->end_code : 0, + mm ? (permitted ? mm->start_code : 1) : 0, + mm ? (permitted ? mm->end_code : 1) : 0, (permitted && mm) ? mm->start_stack : 0, esp, eip, From 36fd36e47592521bb865417b118a96367a2674dc Mon Sep 17 00:00:00 2001 From: Henry Nestler Date: Sun, 20 Feb 2011 20:50:56 +0000 Subject: [PATCH 0926/2556] fbcon: Bugfix soft cursor detection in Tile Blitting commit d6244bc0ed0c52a795e6f4dcab3886daf3e74fac upstream. Use mask 0x10 for "soft cursor" detection on in function tile_cursor. (Tile Blitting Operation in framebuffer console). The old mask 0x01 for vc_cursor_type detects CUR_NONE, CUR_LOWER_THIRD and every second mode value as "software cursor". This hides the cursor for these modes (cursor.mode = 0). But, only CUR_NONE or "software cursor" should hide the cursor. See also 0x10 in functions add_softcursor, bit_cursor and cw_cursor. Signed-off-by: Henry Nestler Signed-off-by: Paul Mundt Signed-off-by: Greg Kroah-Hartman --- drivers/video/console/tileblit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/console/tileblit.c b/drivers/video/console/tileblit.c index 0056a41e5c35c..15e8e1a89c45d 100644 --- a/drivers/video/console/tileblit.c +++ b/drivers/video/console/tileblit.c @@ -83,7 +83,7 @@ static void tile_cursor(struct vc_data *vc, struct fb_info *info, int mode, int softback_lines, int fg, int bg) { struct fb_tilecursor cursor; - int use_sw = (vc->vc_cursor_type & 0x01); + int use_sw = (vc->vc_cursor_type & 0x10); cursor.sx = vc->vc_x; cursor.sy = vc->vc_y; From 20ca17a721c6b9a90b97719c59672988d817e1a9 Mon Sep 17 00:00:00 2001 From: Mi Jinlong Date: Fri, 18 Feb 2011 09:08:31 +0800 Subject: [PATCH 0927/2556] nfsd41: modify the members value of nfsd4_op_flags commit 5ece3cafbd88d4da5c734e1810c4a2e6474b57b2 upstream. The members of nfsd4_op_flags, (ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS) equals to ALLOWED_AS_FIRST_OP, maybe that's not what we want. OP_PUTROOTFH with op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS, can't appears as the first operation with out SEQUENCE ops. This patch modify the wrong value of ALLOWED_WITHOUT_FH etc which was introduced by f9bb94c4. Reviewed-by: Benny Halevy Signed-off-by: Mi Jinlong Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4proc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index db52546143d12..5fcb1396a7e32 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -984,8 +984,8 @@ typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *, void *); enum nfsd4_op_flags { ALLOWED_WITHOUT_FH = 1 << 0, /* No current filehandle required */ - ALLOWED_ON_ABSENT_FS = 2 << 0, /* ops processed on absent fs */ - ALLOWED_AS_FIRST_OP = 3 << 0, /* ops reqired first in compound */ + ALLOWED_ON_ABSENT_FS = 1 << 1, /* ops processed on absent fs */ + ALLOWED_AS_FIRST_OP = 1 << 2, /* ops reqired first in compound */ }; struct nfsd4_operation { From a9eb0c437873a68b55c4c8f507e3aac608ab20aa Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 2 Mar 2011 23:48:33 -0500 Subject: [PATCH 0928/2556] nfsd4: minor nfs4state.c reshuffling commit 529d7b2a7fa31e9f7d08bc790d232c3cbe64fa24 upstream. Minor cleanup in preparation for a bugfix--moving some code to avoid forward references, etc. No change in functionality. Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4state.c | 125 +++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 64 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 7b566ec14e183..50c157cfa8eac 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -316,64 +316,6 @@ static struct list_head unconf_id_hashtbl[CLIENT_HASH_SIZE]; static struct list_head client_lru; static struct list_head close_lru; -static void unhash_generic_stateid(struct nfs4_stateid *stp) -{ - list_del(&stp->st_hash); - list_del(&stp->st_perfile); - list_del(&stp->st_perstateowner); -} - -static void free_generic_stateid(struct nfs4_stateid *stp) -{ - put_nfs4_file(stp->st_file); - kmem_cache_free(stateid_slab, stp); -} - -static void release_lock_stateid(struct nfs4_stateid *stp) -{ - struct file *file; - - unhash_generic_stateid(stp); - file = find_any_file(stp->st_file); - if (file) - locks_remove_posix(file, (fl_owner_t)stp->st_stateowner); - free_generic_stateid(stp); -} - -static void unhash_lockowner(struct nfs4_stateowner *sop) -{ - struct nfs4_stateid *stp; - - list_del(&sop->so_idhash); - list_del(&sop->so_strhash); - list_del(&sop->so_perstateid); - while (!list_empty(&sop->so_stateids)) { - stp = list_first_entry(&sop->so_stateids, - struct nfs4_stateid, st_perstateowner); - release_lock_stateid(stp); - } -} - -static void release_lockowner(struct nfs4_stateowner *sop) -{ - unhash_lockowner(sop); - nfs4_put_stateowner(sop); -} - -static void -release_stateid_lockowners(struct nfs4_stateid *open_stp) -{ - struct nfs4_stateowner *lock_sop; - - while (!list_empty(&open_stp->st_lockowners)) { - lock_sop = list_entry(open_stp->st_lockowners.next, - struct nfs4_stateowner, so_perstateid); - /* list_del(&open_stp->st_lockowners); */ - BUG_ON(lock_sop->so_is_open_owner); - release_lockowner(lock_sop); - } -} - /* * We store the NONE, READ, WRITE, and BOTH bits separately in the * st_{access,deny}_bmap field of the stateid, in order to track not @@ -446,6 +388,64 @@ static int nfs4_access_bmap_to_omode(struct nfs4_stateid *stp) return nfs4_access_to_omode(access); } +static void unhash_generic_stateid(struct nfs4_stateid *stp) +{ + list_del(&stp->st_hash); + list_del(&stp->st_perfile); + list_del(&stp->st_perstateowner); +} + +static void free_generic_stateid(struct nfs4_stateid *stp) +{ + put_nfs4_file(stp->st_file); + kmem_cache_free(stateid_slab, stp); +} + +static void release_lock_stateid(struct nfs4_stateid *stp) +{ + struct file *file; + + unhash_generic_stateid(stp); + file = find_any_file(stp->st_file); + if (file) + locks_remove_posix(file, (fl_owner_t)stp->st_stateowner); + free_generic_stateid(stp); +} + +static void unhash_lockowner(struct nfs4_stateowner *sop) +{ + struct nfs4_stateid *stp; + + list_del(&sop->so_idhash); + list_del(&sop->so_strhash); + list_del(&sop->so_perstateid); + while (!list_empty(&sop->so_stateids)) { + stp = list_first_entry(&sop->so_stateids, + struct nfs4_stateid, st_perstateowner); + release_lock_stateid(stp); + } +} + +static void release_lockowner(struct nfs4_stateowner *sop) +{ + unhash_lockowner(sop); + nfs4_put_stateowner(sop); +} + +static void +release_stateid_lockowners(struct nfs4_stateid *open_stp) +{ + struct nfs4_stateowner *lock_sop; + + while (!list_empty(&open_stp->st_lockowners)) { + lock_sop = list_entry(open_stp->st_lockowners.next, + struct nfs4_stateowner, so_perstateid); + /* list_del(&open_stp->st_lockowners); */ + BUG_ON(lock_sop->so_is_open_owner); + release_lockowner(lock_sop); + } +} + static void release_open_stateid(struct nfs4_stateid *stp) { int oflag = nfs4_access_bmap_to_omode(stp); @@ -3765,7 +3765,6 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct file_lock conflock; __be32 status = 0; unsigned int strhashval; - unsigned int cmd; int err; dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n", @@ -3852,8 +3851,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, filp = find_readable_file(lock_stp->st_file); } file_lock.fl_type = F_RDLCK; - cmd = F_SETLK; - break; + break; case NFS4_WRITE_LT: case NFS4_WRITEW_LT: if (find_writeable_file(lock_stp->st_file)) { @@ -3861,8 +3859,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, filp = find_writeable_file(lock_stp->st_file); } file_lock.fl_type = F_WRLCK; - cmd = F_SETLK; - break; + break; default: status = nfserr_inval; goto out; @@ -3886,7 +3883,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, * Note: locks.c uses the BKL to protect the inode's lock list. */ - err = vfs_lock_file(filp, cmd, &file_lock, &conflock); + err = vfs_lock_file(filp, F_SETLK, &file_lock, &conflock); switch (-err) { case 0: /* success! */ update_stateid(&lock_stp->st_stateid); From 097d5cc33e8111a343d6cf0656cc810bbcc699a5 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 2 Mar 2011 18:01:35 -0500 Subject: [PATCH 0929/2556] nfsd4: fix struct file leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 0997b173609b9229ece28941c118a2a9b278796e upstream. Make sure we properly reference count the struct files that a lock depends on, and release them when the lock stateid is released. This fixes a major leak of struct files when using locking over nfsv4. Reported-by: Rick Koshi Tested-by: Ivo Přikryl Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4state.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 50c157cfa8eac..f0e448a512c63 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -397,6 +397,9 @@ static void unhash_generic_stateid(struct nfs4_stateid *stp) static void free_generic_stateid(struct nfs4_stateid *stp) { + int oflag = nfs4_access_bmap_to_omode(stp); + + nfs4_file_put_access(stp->st_file, oflag); put_nfs4_file(stp->st_file); kmem_cache_free(stateid_slab, stp); } @@ -448,11 +451,8 @@ release_stateid_lockowners(struct nfs4_stateid *open_stp) static void release_open_stateid(struct nfs4_stateid *stp) { - int oflag = nfs4_access_bmap_to_omode(stp); - unhash_generic_stateid(stp); release_stateid_lockowners(stp); - nfs4_file_put_access(stp->st_file, oflag); free_generic_stateid(stp); } @@ -3735,6 +3735,7 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc stp->st_stateid.si_stateownerid = sop->so_id; stp->st_stateid.si_fileid = fp->fi_id; stp->st_stateid.si_generation = 0; + stp->st_access_bmap = 0; stp->st_deny_bmap = open_stp->st_deny_bmap; stp->st_openstp = open_stp; @@ -3749,6 +3750,17 @@ check_lock_length(u64 offset, u64 length) LOFF_OVERFLOW(offset, length))); } +static void get_lock_access(struct nfs4_stateid *lock_stp, u32 access) +{ + struct nfs4_file *fp = lock_stp->st_file; + int oflag = nfs4_access_to_omode(access); + + if (test_bit(access, &lock_stp->st_access_bmap)) + return; + nfs4_file_get_access(fp, oflag); + __set_bit(access, &lock_stp->st_access_bmap); +} + /* * LOCK operation */ @@ -3846,18 +3858,16 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, switch (lock->lk_type) { case NFS4_READ_LT: case NFS4_READW_LT: - if (find_readable_file(lock_stp->st_file)) { - nfs4_get_vfs_file(rqstp, fp, &cstate->current_fh, NFS4_SHARE_ACCESS_READ); - filp = find_readable_file(lock_stp->st_file); - } + filp = find_readable_file(lock_stp->st_file); + if (filp) + get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ); file_lock.fl_type = F_RDLCK; break; case NFS4_WRITE_LT: case NFS4_WRITEW_LT: - if (find_writeable_file(lock_stp->st_file)) { - nfs4_get_vfs_file(rqstp, fp, &cstate->current_fh, NFS4_SHARE_ACCESS_WRITE); - filp = find_writeable_file(lock_stp->st_file); - } + filp = find_writeable_file(lock_stp->st_file); + if (filp) + get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE); file_lock.fl_type = F_WRLCK; break; default: From 0edbda4d8fcc879a2dce0c0f945a32e2b2339282 Mon Sep 17 00:00:00 2001 From: Mi Jinlong Date: Fri, 11 Mar 2011 12:13:55 +0800 Subject: [PATCH 0930/2556] nfsd: wrong index used in inner loop commit 5a02ab7c3c4580f94d13c683721039855b67cda6 upstream. We must not use dummy for index. After the first index, READ32(dummy) will change dummy!!!! Signed-off-by: Mi Jinlong [bfields@redhat.com: Trond points out READ_BUF alone is sufficient.] Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4xdr.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 615f0a9f06008..c6766af00d983 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1142,7 +1142,7 @@ nfsd4_decode_create_session(struct nfsd4_compoundargs *argp, u32 dummy; char *machine_name; - int i, j; + int i; int nr_secflavs; READ_BUF(16); @@ -1215,8 +1215,6 @@ nfsd4_decode_create_session(struct nfsd4_compoundargs *argp, READ_BUF(4); READ32(dummy); READ_BUF(dummy * 4); - for (j = 0; j < dummy; ++j) - READ32(dummy); break; case RPC_AUTH_GSS: dprintk("RPC_AUTH_GSS callback secflavor " @@ -1232,7 +1230,6 @@ nfsd4_decode_create_session(struct nfsd4_compoundargs *argp, READ_BUF(4); READ32(dummy); READ_BUF(dummy); - p += XDR_QUADLEN(dummy); break; default: dprintk("Illegal callback secflavor\n"); From 36d9ebdeb9c8686611db7e064008d6b4fbdce067 Mon Sep 17 00:00:00 2001 From: Stephan Lachowsky Date: Thu, 27 Jan 2011 23:04:33 -0300 Subject: [PATCH 0931/2556] uvcvideo: Fix uvc_fixup_video_ctrl() format search commit 38a66824d96de8aeeb915e6f46f0d3fe55828eb1 upstream. The scheme used to index format in uvc_fixup_video_ctrl() is not robust: format index is based on descriptor ordering, which does not necessarily match bFormatIndex ordering. Searching for first matching format will prevent uvc_fixup_video_ctrl() from using the wrong format/frame to make adjustments. Signed-off-by: Stephan Lachowsky Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/video/uvc/uvc_video.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index 5673d673504b8..545c0294813d9 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -89,15 +89,19 @@ int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl) { - struct uvc_format *format; + struct uvc_format *format = NULL; struct uvc_frame *frame = NULL; unsigned int i; - if (ctrl->bFormatIndex <= 0 || - ctrl->bFormatIndex > stream->nformats) - return; + for (i = 0; i < stream->nformats; ++i) { + if (stream->format[i].index == ctrl->bFormatIndex) { + format = &stream->format[i]; + break; + } + } - format = &stream->format[ctrl->bFormatIndex - 1]; + if (format == NULL) + return; for (i = 0; i < format->nframes; ++i) { if (format->frame[i].bFrameIndex == ctrl->bFrameIndex) { From e4f5f321fe6c48bd99e411e256846d448ffb607b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 23 Feb 2011 11:19:17 -0300 Subject: [PATCH 0932/2556] uvcvideo: Fix descriptor parsing for video output devices commit 4093a5c4a3f59cba1a085bbf87b6ffdddc5a443d upstream. Commit 4057ac6ca9a77c4275b34b5925ab5c99557913b1 V4L/DVB (13505): uvcvideo: Refactor chain scan broke output terminals parsing. Fix it. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/video/uvc/uvc_driver.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index a1e9dfb52f698..6459b8cba2238 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -1264,6 +1264,14 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, break; + case UVC_OTT_VENDOR_SPECIFIC: + case UVC_OTT_DISPLAY: + case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" OT %d", entity->id); + + break; + case UVC_TT_STREAMING: if (UVC_ENTITY_IS_ITERM(entity)) { if (uvc_trace_param & UVC_TRACE_PROBE) From e4a0498b48a58241ae143e0b9715e0e99329186c Mon Sep 17 00:00:00 2001 From: Phil Edworthy Date: Fri, 18 Mar 2011 14:16:31 +0000 Subject: [PATCH 0933/2556] sh: Fix ptrace fpu state initialisation commit c49b6ecf0870e78fa40497cd8b142915c1d5c7c9 upstream. Commit 0ea820cf introduced the PTRACE_GETFPREGS/SETFPREGS cmds, but gdb-server still accesses the FPU state using the PTRACE_PEEKUSR/POKEUSR commands. In this case, xstate was not initialised. Signed-off-by: Phil Edworthy Signed-off-by: Paul Mundt Signed-off-by: Greg Kroah-Hartman --- arch/sh/kernel/ptrace_32.c | 6 ++++++ arch/sh/kernel/ptrace_64.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index 90a15d29feebd..b53664ef53e84 100644 --- a/arch/sh/kernel/ptrace_32.c +++ b/arch/sh/kernel/ptrace_32.c @@ -392,6 +392,9 @@ long arch_ptrace(struct task_struct *child, long request, tmp = 0; } else { unsigned long index; + ret = init_fpu(child); + if (ret) + break; index = addr - offsetof(struct user, fpu); tmp = ((unsigned long *)child->thread.xstate) [index >> 2]; @@ -423,6 +426,9 @@ long arch_ptrace(struct task_struct *child, long request, else if (addr >= offsetof(struct user, fpu) && addr < offsetof(struct user, u_fpvalid)) { unsigned long index; + ret = init_fpu(child); + if (ret) + break; index = addr - offsetof(struct user, fpu); set_stopped_child_used_math(child); ((unsigned long *)child->thread.xstate) diff --git a/arch/sh/kernel/ptrace_64.c b/arch/sh/kernel/ptrace_64.c index 4436eacddb153..c8f97649f354b 100644 --- a/arch/sh/kernel/ptrace_64.c +++ b/arch/sh/kernel/ptrace_64.c @@ -403,6 +403,9 @@ long arch_ptrace(struct task_struct *child, long request, else if ((addr >= offsetof(struct user, fpu)) && (addr < offsetof(struct user, u_fpvalid))) { unsigned long index; + ret = init_fpu(child); + if (ret) + break; index = addr - offsetof(struct user, fpu); tmp = get_fpu_long(child, index); } else if (addr == offsetof(struct user, u_fpvalid)) { @@ -442,6 +445,9 @@ long arch_ptrace(struct task_struct *child, long request, else if ((addr >= offsetof(struct user, fpu)) && (addr < offsetof(struct user, u_fpvalid))) { unsigned long index; + ret = init_fpu(child); + if (ret) + break; index = addr - offsetof(struct user, fpu); ret = put_fpu_long(child, index, data); } From 52541b5104beda8058232ddd459677549adecc6c Mon Sep 17 00:00:00 2001 From: David Engraf Date: Wed, 23 Mar 2011 11:35:42 +0000 Subject: [PATCH 0934/2556] sh: Fix ptrace hw_breakpoint handling commit fb7f045ace0624f1e59a7db8497e460bd54b1cbc upstream. Since commit 34d0b5af50a063cded842716633501b38ff815fb it is no longer possible to debug an application using singlestep. The old commit converted singlestep handling via ptrace to hw_breakpoints. The hw_breakpoint is disabled when an event is triggered and not re-enabled again. This patch re-enables the existing hw_breakpoint before the existing breakpoint is reused. Signed-off-by: David Engraf Signed-off-by: Paul Mundt Signed-off-by: Greg Kroah-Hartman --- arch/sh/kernel/ptrace_32.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index b53664ef53e84..2130ca674e9bd 100644 --- a/arch/sh/kernel/ptrace_32.c +++ b/arch/sh/kernel/ptrace_32.c @@ -101,6 +101,8 @@ static int set_single_step(struct task_struct *tsk, unsigned long addr) attr = bp->attr; attr.bp_addr = addr; + /* reenable breakpoint */ + attr.disabled = false; err = modify_user_hw_breakpoint(bp, &attr); if (unlikely(err)) return err; From 289c0fa9992855798b1900fba0f738a17772304f Mon Sep 17 00:00:00 2001 From: Michal Sojka Date: Tue, 15 Mar 2011 16:41:47 +0100 Subject: [PATCH 0935/2556] USB: Do not pass negative length to snoop_urb() commit 9d02b42614149ebccf12c9c580601ed01bd83070 upstream. When `echo Y > /sys/module/usbcore/parameters/usbfs_snoop` and usb_control_msg() returns error, a lot of kernel memory is dumped to dmesg until unhandled kernel paging request occurs. Signed-off-by: Michal Sojka Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index a7131ad630f9a..37518dfdeb987 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -802,7 +802,7 @@ static int proc_control(struct dev_state *ps, void __user *arg) tbuf, ctrl.wLength, tmo); usb_lock_device(dev); snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE, - tbuf, i); + tbuf, max(i, 0)); if ((i > 0) && ctrl.wLength) { if (copy_to_user(ctrl.data, tbuf, i)) { free_page((unsigned long)tbuf); From be56377ef87e7f1de92842cac335c9ecd1f6c478 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 16 Mar 2011 10:57:15 -0400 Subject: [PATCH 0936/2556] ehci-hcd: Bug fix: don't set a QH's Halt bit commit b5a3b3d985493c173925907adfebf3edab236fe7 upstream. This patch (as1453) fixes a long-standing bug in the ehci-hcd driver. There is no need to set the Halt bit in the overlay region for an unlinked or blocked QH. Contrary to what the comment says, setting the Halt bit does not cause the QH to be patched later; that decision (made in qh_refresh()) depends only on whether the QH is currently pointing to a valid qTD. Likewise, setting the Halt bit does not prevent completions from activating the QH while it is "stopped"; they are prevented by the fact that qh_completions() temporarily changes qh->qh_state to QH_STATE_COMPLETING. On the other hand, there are circumstances in which the QH will be reactivated _without_ being patched; this happens after an URB beyond the head of the queue is unlinked. Setting the Halt bit will then cause the hardware to see the QH with both the Active and Halt bits set, an invalid combination that will prevent the queue from advancing and may even crash some controllers. Apparently the only reason this hasn't been reported before is that unlinking URBs from the middle of a running queue is quite uncommon. However Test 17, recently added to the usbtest driver, does exactly this, and it confirms the presence of the bug. In short, there is no reason to set the Halt bit for an unlinked or blocked QH, and there is a very good reason not to set it. Therefore the code that sets it is removed. Signed-off-by: Alan Stern Tested-by: Andiry Xu CC: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-q.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 233c288e3f931..5add8b5ddda88 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -315,7 +315,6 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) int stopped; unsigned count = 0; u8 state; - const __le32 halt = HALT_BIT(ehci); struct ehci_qh_hw *hw = qh->hw; if (unlikely (list_empty (&qh->qtd_list))) @@ -422,7 +421,6 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) && !(qtd->hw_alt_next & EHCI_LIST_END(ehci))) { stopped = 1; - goto halt; } /* stop scanning when we reach qtds the hc is using */ @@ -456,16 +454,6 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) */ ehci_clear_tt_buffer(ehci, qh, urb, token); } - - /* force halt for unlinked or blocked qh, so we'll - * patch the qh later and so that completions can't - * activate it while we "know" it's stopped. - */ - if ((halt & hw->hw_token) == 0) { -halt: - hw->hw_token |= halt; - wmb (); - } } /* unless we already know the urb's status, collect qtd status From ed04bcf36c1280d8163c543110a063570140c88f Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 22 Mar 2011 14:43:37 -0400 Subject: [PATCH 0937/2556] usb: musb: blackfin: fix typo in platform driver name commit 417ddf86c8c499fada439b8ee89bb4c6f282ed6c upstream. The modularization of the Blackfin driver set the name to "musb-blackfin" in all the boards, but "musb-bfin" in the driver itself. Since the driver file name uses "blackfin", change the driver to "musb-blackfin". This is also easier as it's only one file to change. Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/blackfin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 9d49d1cd7ce23..5da9ef8ff3113 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -548,7 +548,7 @@ static struct dev_pm_ops bfin_pm_ops = { static struct platform_driver bfin_driver = { .remove = __exit_p(bfin_remove), .driver = { - .name = "musb-bfin", + .name = "musb-blackfin", .pm = DEV_PM_OPS, }, }; From 19c771515efddefe577f5cead08afd7922e2e8b2 Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Mon, 21 Mar 2011 14:06:31 -0400 Subject: [PATCH 0938/2556] usb: musb: blackfin: fix typo in new dev_pm_ops struct commit 8f7e7b87ec7c3202941ef2770bacd353ab93368b upstream. Signed-off-by: Bob Liu Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/blackfin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 5da9ef8ff3113..7843333b61993 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -540,7 +540,7 @@ static struct dev_pm_ops bfin_pm_ops = { .resume = bfin_resume, }; -#define DEV_PM_OPS &bfin_pm_op, +#define DEV_PM_OPS &bfin_pm_ops #else #define DEV_PM_OPS NULL #endif From d1a86ee18695de5b44b2d9febea66ac47da7bc80 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Mon, 21 Mar 2011 14:06:32 -0400 Subject: [PATCH 0939/2556] usb: musb: blackfin: fix typo in new bfin_musb_vbus_status func commit 45567c28d29a8766a67c53f898d502aef71b7ef0 upstream. The common code has a "get" in the middle, but each implementation does not have it. Signed-off-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/blackfin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 7843333b61993..52312e8af213a 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -322,7 +322,7 @@ static void bfin_musb_try_idle(struct musb *musb, unsigned long timeout) mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); } -static int bfin_musb_get_vbus_status(struct musb *musb) +static int bfin_musb_vbus_status(struct musb *musb) { return 0; } From 88bc8459e8972a869349a1ded5260a58f9406709 Mon Sep 17 00:00:00 2001 From: Peter Holik Date: Fri, 18 Mar 2011 18:47:44 +0100 Subject: [PATCH 0940/2556] USB: uss720 fixup refcount position commit adaa3c6342b249548ea830fe8e02aa5b45be8688 upstream. My testprog do a lot of bitbang - after hours i got following warning and my machine lockups: WARNING: at /build/buildd/linux-2.6.38/lib/kref.c:34 After debugging uss720 driver i discovered that the completion callback was called before usb_submit_urb returns. The callback frees the request structure that is krefed on return by usb_submit_urb. Signed-off-by: Peter Holik Acked-by: Thomas Sailer Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/uss720.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index f7a2057380321..8b1d94a769140 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -177,12 +177,11 @@ static struct uss720_async_request *submit_async_request(struct parport_uss720_p spin_lock_irqsave(&priv->asynclock, flags); list_add_tail(&rq->asynclist, &priv->asynclist); spin_unlock_irqrestore(&priv->asynclock, flags); + kref_get(&rq->ref_count); ret = usb_submit_urb(rq->urb, mem_flags); - if (!ret) { - kref_get(&rq->ref_count); + if (!ret) return rq; - } - kref_put(&rq->ref_count, destroy_async); + destroy_async(&rq->ref_count); err("submit_async_request submit_urb failed with %d", ret); return NULL; } From 5993c3a0d146200b4ad01f2e4534c1e41178da20 Mon Sep 17 00:00:00 2001 From: Robert Lukassen Date: Wed, 16 Mar 2011 12:13:34 +0100 Subject: [PATCH 0941/2556] USB: Fix 'bad dma' problem on WDM device disconnect commit 878b753e32ca765cd346a5d3038d630178ec78ff upstream. In the WDM class driver a disconnect event leads to calls to usb_free_coherent to put back two USB DMA buffers allocated earlier. The call to usb_free_coherent uses a different size parameter (desc->wMaxCommand) than the corresponding call to usb_alloc_coherent (desc->bMaxPacketSize0). When a disconnect event occurs, this leads to 'bad dma' complaints from usb core because the USB DMA buffer is being pushed back to the 'buffer-2048' pool from which it has not been allocated. This patch against the most recent linux-2.6 kernel ensures that the parameters used by usb_alloc_coherent & usb_free_coherent calls in cdc-wdm.c match. Signed-off-by: Robert Lukassen Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-wdm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 47085e5879abb..a97c018dd4198 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -281,7 +281,7 @@ static void cleanup(struct wdm_device *desc) desc->sbuf, desc->validity->transfer_dma); usb_free_coherent(interface_to_usbdev(desc->intf), - desc->wMaxCommand, + desc->bMaxPacketSize0, desc->inbuf, desc->response->transfer_dma); kfree(desc->orq); From f2898a0f735ab96c6dd5f57cb68775e98851779c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:09 +0100 Subject: [PATCH 0942/2556] USB: cdc-acm: fix memory corruption / panic commit 23b80550e2aa61d0ba3af98b831b9195be0db9ee upstream. Prevent read urbs from being resubmitted from tasklet after port close. The receive tasklet was not disabled on port close, which could lead to corruption of receive lists on consecutive port open. In particular, read urbs could be re-submitted before port open, added to free list in open, and then added a second time to the free list in the completion handler. cdc-acm.c: Entering acm_tty_open. cdc-acm.c: acm_control_msg: rq: 0x22 val: 0x3 len: 0x0 result: 0 cdc-acm.c: Entering acm_rx_tasklet cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da280, rcv 0xf57fbc24, buf 0xf57fbd64 cdc-acm.c: set line: 115200 0 0 8 cdc-acm.c: acm_control_msg: rq: 0x20 val: 0x0 len: 0x7 result: 7 cdc-acm.c: acm_tty_close cdc-acm.c: acm_port_down cdc-acm.c: acm_control_msg: rq: 0x22 val: 0x0 len: 0x0 result: 0 cdc-acm.c: acm_ctrl_irq - urb shutting down with status: -2 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da300, rcv 0xf57fbc10, buf 0xf57fbd50 cdc-acm.c: Entering acm_read_bulk with status -2 cdc_acm 4-1:1.1: Aborting, acm not ready cdc-acm.c: Entering acm_read_bulk with status -2 cdc_acm 4-1:1.1: Aborting, acm not ready cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da380, rcv 0xf57fbbfc, buf 0xf57fbd3c cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da400, rcv 0xf57fbbe8, buf 0xf57fbd28 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da480, rcv 0xf57fbbd4, buf 0xf57fbd14 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da900, rcv 0xf57fbbc0, buf 0xf57fbd00 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da980, rcv 0xf57fbbac, buf 0xf57fbcec cdc-acm.c: acm_rx_tasklet: sending urb 0xf50daa00, rcv 0xf57fbb98, buf 0xf57fbcd8 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50daa80, rcv 0xf57fbb84, buf 0xf57fbcc4 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50dab00, rcv 0xf57fbb70, buf 0xf57fbcb0 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50dab80, rcv 0xf57fbb5c, buf 0xf57fbc9c cdc-acm.c: acm_rx_tasklet: sending urb 0xf50dac00, rcv 0xf57fbb48, buf 0xf57fbc88 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50dac80, rcv 0xf57fbb34, buf 0xf57fbc74 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50dad00, rcv 0xf57fbb20, buf 0xf57fbc60 cdc-acm.c: acm_rx_tasklet: sending urb 0xf50dad80, rcv 0xf57fbb0c, buf 0xf57fbc4c cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da880, rcv 0xf57fbaf8, buf 0xf57fbc38 cdc-acm.c: Entering acm_tty_open. cdc-acm.c: acm_control_msg: rq: 0x22 val: 0x3 len: 0x0 result: 0 cdc-acm.c: Entering acm_rx_tasklet cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da280, rcv 0xf57fbc24, buf 0xf57fbd64 cdc-acm.c: Entering acm_tty_write to write 3 bytes, cdc-acm.c: Get 3 bytes... cdc-acm.c: acm_write_start susp_count: 0 cdc-acm.c: Entering acm_read_bulk with status 0 ------------[ cut here ]------------ WARNING: at /home/johan/src/linux/linux-2.6/lib/list_debug.c:57 list_del+0x10c/0x120() Hardware name: Vostro 1520 list_del corruption. next->prev should be f57fbc10, but was f57fbaf8 Modules linked in: cdc_acm Pid: 3, comm: ksoftirqd/0 Not tainted 2.6.37+ #39 Call Trace: [] warn_slowpath_common+0x72/0xa0 [] ? list_del+0x10c/0x120 [] ? list_del+0x10c/0x120 [] warn_slowpath_fmt+0x33/0x40 [] list_del+0x10c/0x120 [] acm_rx_tasklet+0xef/0x3e0 [cdc_acm] [] ? net_rps_action_and_irq_enable+0x6d/0x80 [] tasklet_action+0xe6/0x140 [] __do_softirq+0xaf/0x210 [] ? __do_softirq+0x0/0x210 [] ? run_ksoftirqd+0x8a/0x1c0 [] ? run_ksoftirqd+0x0/0x1c0 [] ? kthread+0x74/0x80 [] ? kthread+0x0/0x80 [] ? kernel_thread_helper+0x6/0x10 ---[ end trace efd9a11434f0082e ]--- ------------[ cut here ]------------ WARNING: at /home/johan/src/linux/linux-2.6/lib/list_debug.c:57 list_del+0x10c/0x120() Hardware name: Vostro 1520 list_del corruption. next->prev should be f57fbd50, but was f57fbdb0 Modules linked in: cdc_acm Pid: 3, comm: ksoftirqd/0 Tainted: G W 2.6.37+ #39 Call Trace: [] warn_slowpath_common+0x72/0xa0 [] ? list_del+0x10c/0x120 [] ? list_del+0x10c/0x120 [] warn_slowpath_fmt+0x33/0x40 [] list_del+0x10c/0x120 [] acm_rx_tasklet+0x106/0x3e0 [cdc_acm] [] ? net_rps_action_and_irq_enable+0x6d/0x80 [] tasklet_action+0xe6/0x140 [] __do_softirq+0xaf/0x210 [] ? __do_softirq+0x0/0x210 [] ? run_ksoftirqd+0x8a/0x1c0 [] ? run_ksoftirqd+0x0/0x1c0 [] ? kthread+0x74/0x80 [] ? kthread+0x0/0x80 [] ? kernel_thread_helper+0x6/0x10 ---[ end trace efd9a11434f0082f ]--- cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da300, rcv 0xf57fbc10, buf 0xf57fbd50 cdc-acm.c: disconnected from network cdc-acm.c: acm_rx_tasklet: sending urb 0xf50da380, rcv 0xf57fbbfc, buf 0xf57fbd3c cdc-acm.c: Entering acm_rx_tasklet ------------[ cut here ]------------ WARNING: at /home/johan/src/linux/linux-2.6/lib/list_debug.c:48 list_del+0xd5/0x120() Hardware name: Vostro 1520 list_del corruption, next is LIST_POISON1 (00100100) Modules linked in: cdc_acm Pid: 3, comm: ksoftirqd/0 Tainted: G W 2.6.37+ #39 Call Trace: [] warn_slowpath_common+0x72/0xa0 [] ? list_del+0xd5/0x120 [] ? list_del+0xd5/0x120 [] warn_slowpath_fmt+0x33/0x40 [] list_del+0xd5/0x120 [] acm_rx_tasklet+0x2dc/0x3e0 [cdc_acm] [] ? trace_hardirqs_on+0xb/0x10 [] ? tasklet_action+0x60/0x140 [] tasklet_action+0xe6/0x140 [] __do_softirq+0xaf/0x210 [] ? __do_softirq+0x0/0x210 [] ? run_ksoftirqd+0x8a/0x1c0 [] ? run_ksoftirqd+0x0/0x1c0 [] ? kthread+0x74/0x80 [] ? kthread+0x0/0x80 [] ? kernel_thread_helper+0x6/0x10 ---[ end trace efd9a11434f00830 ]--- BUG: unable to handle kernel paging request at 00200200 IP: [] list_del+0x1d/0x120 *pde = 00000000 Oops: 0000 [#1] PREEMPT SMP last sysfs file: /sys/devices/pci0000:00/0000:00:1a.1/usb4/4-1/4-1:1.0/tty/ttyACM0/uevent Modules linked in: cdc_acm Pid: 3, comm: ksoftirqd/0 Tainted: G W 2.6.37+ #39 0T816J/Vostro 1520 EIP: 0060:[] EFLAGS: 00010046 CPU: 0 EIP is at list_del+0x1d/0x120 EAX: f57fbd3c EBX: f57fb800 ECX: ffff8000 EDX: 00200200 ESI: f57fbe90 EDI: f57fbd3c EBP: f600bf54 ESP: f600bf3c DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 Process ksoftirqd/0 (pid: 3, ti=f600a000 task=f60791c0 task.ti=f6082000) Stack: c1527e84 00000030 c1527e54 00100100 f57fb800 f57fbd3c f600bf98 f8051fac f8053104 f8052b94 f600bf6c c106dbab f600bf80 00000286 f60791c0 c1042b30 f57fbda8 f57f5800 f57fbdb0 f57fbd80 f57fbe7c c1656b04 00000000 f600bfb0 Call Trace: [] ? acm_rx_tasklet+0x2dc/0x3e0 [cdc_acm] [] ? trace_hardirqs_on+0xb/0x10 [] ? tasklet_action+0x60/0x140 [] ? tasklet_action+0xe6/0x140 [] ? __do_softirq+0xaf/0x210 [] ? __do_softirq+0x0/0x210 [] ? run_ksoftirqd+0x8a/0x1c0 [] ? run_ksoftirqd+0x0/0x1c0 [] ? kthread+0x74/0x80 [] ? kthread+0x0/0x80 [] ? kernel_thread_helper+0x6/0x10 Code: ff 48 14 e9 57 ff ff ff 90 90 90 90 90 90 55 89 e5 83 ec 18 81 38 00 01 10 00 0f 84 9c 00 00 00 8b 50 04 81 fa 00 02 20 00 74 33 <8b> 12 39 d0 75 5c 8b 10 8b 4a 04 39 c8 0f 85 b5 00 00 00 8b 48 EIP: [] list_del+0x1d/0x120 SS:ESP 0068:f600bf3c CR2: 0000000000200200 ---[ end trace efd9a11434f00831 ]--- Kernel panic - not syncing: Fatal exception in interrupt Pid: 3, comm: ksoftirqd/0 Tainted: G D W 2.6.37+ #39 Call Trace: [] ? printk+0x1d/0x24 [] panic+0x66/0x15c [] oops_end+0x8f/0x90 [] no_context+0xc6/0x160 [] __bad_area_nosemaphore+0x98/0x140 [] ? release_console_sem+0x1d8/0x210 [] bad_area_nosemaphore+0x17/0x20 [] do_page_fault+0x279/0x420 [] ? show_trace+0x1f/0x30 [] ? printk+0x1d/0x24 [] ? do_page_fault+0x0/0x420 [] error_code+0x5f/0x64 [] ? select_task_rq_fair+0x37b/0x6a0 [] ? do_page_fault+0x0/0x420 [] ? list_del+0x1d/0x120 [] acm_rx_tasklet+0x2dc/0x3e0 [cdc_acm] [] ? trace_hardirqs_on+0xb/0x10 [] ? tasklet_action+0x60/0x140 [] tasklet_action+0xe6/0x140 [] __do_softirq+0xaf/0x210 [] ? __do_softirq+0x0/0x210 [] ? run_ksoftirqd+0x8a/0x1c0 [] ? run_ksoftirqd+0x0/0x1c0 [] ? kthread+0x74/0x80 [] ? kthread+0x0/0x80 [] ? kernel_thread_helper+0x6/0x10 panic occurred, switching back to text console ------------[ cut here ]------------ Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 4ab49d4eebf4a..805916675fbc5 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -646,8 +646,10 @@ static void acm_port_down(struct acm *acm) usb_kill_urb(acm->ctrlurb); for (i = 0; i < ACM_NW; i++) usb_kill_urb(acm->wb[i].urb); + tasklet_disable(&acm->urb_task); for (i = 0; i < nr; i++) usb_kill_urb(acm->ru[i].urb); + tasklet_enable(&acm->urb_task); acm->control->needs_remote_wakeup = 0; usb_autopm_put_interface(acm->control); } From 82f174f8986b65bc59911b7a33864f7aa674fa1d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:10 +0100 Subject: [PATCH 0943/2556] USB: cdc-acm: fix potential null-pointer dereference commit 15e5bee33ffc11d0e5c6f819a65e7881c5c407be upstream. Must check return value of tty_port_tty_get. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 805916675fbc5..fcdd41e0429bb 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -533,6 +533,8 @@ static void acm_softint(struct work_struct *work) if (!ACM_READY(acm)) return; tty = tty_port_tty_get(&acm->port); + if (!tty) + return; tty_wakeup(tty); tty_kref_put(tty); } From 35358c06bcbd6af2074ff148659753c72a8f13de Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 22 Mar 2011 11:12:11 +0100 Subject: [PATCH 0944/2556] USB: cdc-acm: fix potential null-pointer dereference on disconnect commit 7e7797e7f6f7bfab73fca02c65e40eaa5bb9000c upstream. Fix potential null-pointer exception on disconnect introduced by commit 11ea859d64b69a747d6b060b9ed1520eab1161fe (USB: additional power savings for cdc-acm devices that support remote wakeup). Only access acm->dev after making sure it is non-null in control urb completion handler. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index fcdd41e0429bb..30bb8d053c2ee 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -297,6 +297,8 @@ static void acm_ctrl_irq(struct urb *urb) if (!ACM_READY(acm)) goto exit; + usb_mark_last_busy(acm->dev); + data = (unsigned char *)(dr + 1); switch (dr->bNotificationType) { case USB_CDC_NOTIFY_NETWORK_CONNECTION: @@ -336,7 +338,6 @@ static void acm_ctrl_irq(struct urb *urb) break; } exit: - usb_mark_last_busy(acm->dev); retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with " From 11f5a220889aeeb1fc95685a711ffd7bac6d21e4 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 18 Mar 2011 08:29:36 -0400 Subject: [PATCH 0945/2556] fix deadlock in pivot_root() commit 27cb1572e3e6bb1f8cf6bb3d74c914a87b131792 upstream. Don't hold vfsmount_lock over the loop traversing ->mnt_parent; do check_mnt(new.mnt) under namespace_sem instead; combined with namespace_sem held over all that code it'll guarantee the stability of ->mnt_parent chain all the way to the root. Doing check_mnt() outside of namespace_sem in case of pivot_root() is wrong anyway. Signed-off-by: Al Viro Signed-off-by: Greg Kroah-Hartman --- fs/namespace.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index d1edf26025dcb..445534be02432 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2469,9 +2469,6 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, error = user_path_dir(new_root, &new); if (error) goto out0; - error = -EINVAL; - if (!check_mnt(new.mnt)) - goto out1; error = user_path_dir(put_old, &old); if (error) @@ -2491,7 +2488,7 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, IS_MNT_SHARED(new.mnt->mnt_parent) || IS_MNT_SHARED(root.mnt->mnt_parent)) goto out2; - if (!check_mnt(root.mnt)) + if (!check_mnt(root.mnt) || !check_mnt(new.mnt)) goto out2; error = -ENOENT; if (cant_mount(old.dentry)) @@ -2515,19 +2512,19 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, goto out2; /* not attached */ /* make sure we can reach put_old from new_root */ tmp = old.mnt; - br_write_lock(vfsmount_lock); if (tmp != new.mnt) { for (;;) { if (tmp->mnt_parent == tmp) - goto out3; /* already mounted on put_old */ + goto out2; /* already mounted on put_old */ if (tmp->mnt_parent == new.mnt) break; tmp = tmp->mnt_parent; } if (!is_subdir(tmp->mnt_mountpoint, new.dentry)) - goto out3; + goto out2; } else if (!is_subdir(old.dentry, new.dentry)) - goto out3; + goto out2; + br_write_lock(vfsmount_lock); detach_mnt(new.mnt, &parent_path); detach_mnt(root.mnt, &root_parent); /* mount old root on put_old */ @@ -2550,9 +2547,6 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, path_put(&new); out0: return error; -out3: - br_write_unlock(vfsmount_lock); - goto out2; } static void __init init_mount_tree(void) From 952be7db1782b711cfbcd1a7fd8b94106b2d4e6f Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 17 Mar 2011 11:13:12 +0100 Subject: [PATCH 0946/2556] fs: assign sb->s_bdi to default_backing_dev_info if the bdi is going away commit 95f28604a65b1c40b6c6cd95e58439cd7ded3add upstream. We don't have proper reference counting for this yet, so we run into cases where the device is pulled and we OOPS on flushing the fs data. This happens even though the dirty inodes have already been migrated to the default_backing_dev_info. Reported-by: Torsten Hilbrich Tested-by: Torsten Hilbrich Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- fs/super.c | 2 ++ fs/sync.c | 4 ++-- mm/backing-dev.c | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/super.c b/fs/super.c index 7e9dd4cc2c011..0d89e93f654e1 100644 --- a/fs/super.c +++ b/fs/super.c @@ -71,6 +71,7 @@ static struct super_block *alloc_super(struct file_system_type *type) #else INIT_LIST_HEAD(&s->s_files); #endif + s->s_bdi = &default_backing_dev_info; INIT_LIST_HEAD(&s->s_instances); INIT_HLIST_BL_HEAD(&s->s_anon); INIT_LIST_HEAD(&s->s_inodes); @@ -1003,6 +1004,7 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void } BUG_ON(!mnt->mnt_sb); WARN_ON(!mnt->mnt_sb->s_bdi); + WARN_ON(mnt->mnt_sb->s_bdi == &default_backing_dev_info); mnt->mnt_sb->s_flags |= MS_BORN; error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata); diff --git a/fs/sync.c b/fs/sync.c index ba76b9623e7e8..412dc89163d31 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -33,7 +33,7 @@ static int __sync_filesystem(struct super_block *sb, int wait) * This should be safe, as we require bdi backing to actually * write out data in the first place */ - if (!sb->s_bdi || sb->s_bdi == &noop_backing_dev_info) + if (sb->s_bdi == &noop_backing_dev_info) return 0; if (sb->s_qcop && sb->s_qcop->quota_sync) @@ -79,7 +79,7 @@ EXPORT_SYMBOL_GPL(sync_filesystem); static void sync_one_sb(struct super_block *sb, void *arg) { - if (!(sb->s_flags & MS_RDONLY) && sb->s_bdi) + if (!(sb->s_flags & MS_RDONLY)) __sync_filesystem(sb, *(int *)arg); } /* diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 027100d30227f..8e4ed884f198b 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -604,7 +604,7 @@ static void bdi_prune_sb(struct backing_dev_info *bdi) spin_lock(&sb_lock); list_for_each_entry(sb, &super_blocks, s_list) { if (sb->s_bdi == bdi) - sb->s_bdi = NULL; + sb->s_bdi = &default_backing_dev_info; } spin_unlock(&sb_lock); } From 7dc06099bec855136a3be5f5fa829197bd085929 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 18 Feb 2011 11:30:30 +0000 Subject: [PATCH 0947/2556] x86: Cleanup highmap after brk is concluded commit e5f15b45ddf3afa2bbbb10c7ea34fb32b6de0a0e upstream. Now cleanup_highmap actually is in two steps: one is early in head64.c and only clears above _end; a second one is in init_memory_mapping() and tries to clean from _brk_end to _end. It should check if those boundaries are PMD_SIZE aligned but currently does not. Also init_memory_mapping() is called several times for numa or memory hotplug, so we really should not handle initial kernel mappings there. This patch moves cleanup_highmap() down after _brk_end is settled so we can do everything in one step. Also we honor max_pfn_mapped in the implementation of cleanup_highmap. Signed-off-by: Yinghai Lu Signed-off-by: Stefano Stabellini LKML-Reference: Signed-off-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/head64.c | 3 --- arch/x86/kernel/setup.c | 5 +++++ arch/x86/mm/init.c | 19 ------------------- arch/x86/mm/init_64.c | 11 ++++++----- 4 files changed, 11 insertions(+), 27 deletions(-) diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index 2d2673c28aff2..5655c2272adb8 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c @@ -77,9 +77,6 @@ void __init x86_64_start_kernel(char * real_mode_data) /* Make NULL pointers segfault */ zap_identity_mappings(); - /* Cleanup the over mapped high alias */ - cleanup_highmap(); - max_pfn_mapped = KERNEL_IMAGE_SIZE >> PAGE_SHIFT; for (i = 0; i < NUM_EXCEPTION_VECTORS; i++) { diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index d3cfe26c0252a..e543fe9311e4a 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -297,6 +297,9 @@ static void __init init_gbpages(void) static inline void init_gbpages(void) { } +static void __init cleanup_highmap(void) +{ +} #endif static void __init reserve_brk(void) @@ -922,6 +925,8 @@ void __init setup_arch(char **cmdline_p) */ reserve_brk(); + cleanup_highmap(); + memblock.current_limit = get_max_mapped(); memblock_x86_fill(); diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 947f42abe820e..f13ff3a226736 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -279,25 +279,6 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, load_cr3(swapper_pg_dir); #endif -#ifdef CONFIG_X86_64 - if (!after_bootmem && !start) { - pud_t *pud; - pmd_t *pmd; - - mmu_cr4_features = read_cr4(); - - /* - * _brk_end cannot change anymore, but it and _end may be - * located on different 2M pages. cleanup_highmap(), however, - * can only consider _end when it runs, so destroy any - * mappings beyond _brk_end here. - */ - pud = pud_offset(pgd_offset_k(_brk_end), _brk_end); - pmd = pmd_offset(pud, _brk_end - 1); - while (++pmd <= pmd_offset(pud, (unsigned long)_end - 1)) - pmd_clear(pmd); - } -#endif __flush_tlb_all(); if (!after_bootmem && e820_table_end > e820_table_start) diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index c14a5422e1522..68f9921ae9c16 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -51,6 +51,7 @@ #include #include #include +#include static int __init parse_direct_gbpages_off(char *arg) { @@ -293,18 +294,18 @@ void __init init_extra_mapping_uc(unsigned long phys, unsigned long size) * to the compile time generated pmds. This results in invalid pmds up * to the point where we hit the physaddr 0 mapping. * - * We limit the mappings to the region from _text to _end. _end is - * rounded up to the 2MB boundary. This catches the invalid pmds as + * We limit the mappings to the region from _text to _brk_end. _brk_end + * is rounded up to the 2MB boundary. This catches the invalid pmds as * well, as they are located before _text: */ void __init cleanup_highmap(void) { unsigned long vaddr = __START_KERNEL_map; - unsigned long end = roundup((unsigned long)_end, PMD_SIZE) - 1; + unsigned long vaddr_end = __START_KERNEL_map + (max_pfn_mapped << PAGE_SHIFT); + unsigned long end = roundup((unsigned long)_brk_end, PMD_SIZE) - 1; pmd_t *pmd = level2_kernel_pgt; - pmd_t *last_pmd = pmd + PTRS_PER_PMD; - for (; pmd < last_pmd; pmd++, vaddr += PMD_SIZE) { + for (; vaddr + PMD_SIZE - 1 < vaddr_end; pmd++, vaddr += PMD_SIZE) { if (pmd_none(*pmd)) continue; if (vaddr < (unsigned long) _text || vaddr > end) From 6ac48803ff56cea62c3715e1f672f1e486f21c09 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 8 Feb 2011 13:55:21 +1000 Subject: [PATCH 0948/2556] drm: check for modesetting on modeset ioctls commit fb3b06c8a1fd1a80298f13b738ab38ef8c73baff upstream. Noticed this while working on some other things, helps if we check for modeset enabled on modesetting ioctls. Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/drm_crtc.c | 51 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 654faa803dcbc..6a5371b8fe9af 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1073,6 +1073,9 @@ int drm_mode_getresources(struct drm_device *dev, void *data, uint32_t __user *encoder_id; struct drm_mode_group *mode_group; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); /* @@ -1244,6 +1247,9 @@ int drm_mode_getcrtc(struct drm_device *dev, struct drm_mode_object *obj; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, crtc_resp->crtc_id, @@ -1312,6 +1318,9 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, uint64_t __user *prop_values; uint32_t __user *encoder_ptr; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id); @@ -1431,6 +1440,9 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, struct drm_encoder *encoder; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, enc_resp->encoder_id, DRM_MODE_OBJECT_ENCODER); @@ -1486,6 +1498,9 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, int ret = 0; int i; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, crtc_req->crtc_id, DRM_MODE_OBJECT_CRTC); @@ -1603,6 +1618,9 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, struct drm_crtc *crtc; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + if (!req->flags) { DRM_ERROR("no operation set\n"); return -EINVAL; @@ -1667,6 +1685,9 @@ int drm_mode_addfb(struct drm_device *dev, struct drm_framebuffer *fb; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + if ((config->min_width > r->width) || (r->width > config->max_width)) { DRM_ERROR("mode new framebuffer width not within limits\n"); return -EINVAL; @@ -1724,6 +1745,9 @@ int drm_mode_rmfb(struct drm_device *dev, int ret = 0; int found = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); /* TODO check that we realy get a framebuffer back. */ @@ -1780,6 +1804,9 @@ int drm_mode_getfb(struct drm_device *dev, struct drm_framebuffer *fb; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { @@ -1813,6 +1840,9 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, int num_clips; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { @@ -1996,6 +2026,9 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev, struct drm_mode_modeinfo *umode = &mode_cmd->mode; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); @@ -2042,6 +2075,9 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev, struct drm_mode_modeinfo *umode = &mode_cmd->mode; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); @@ -2211,6 +2247,9 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, uint64_t __user *values_ptr; uint32_t __user *blob_length_ptr; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); if (!obj) { @@ -2333,6 +2372,9 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, int ret = 0; void *blob_ptr; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB); if (!obj) { @@ -2393,6 +2435,9 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev, int ret = -EINVAL; int i; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); @@ -2509,6 +2554,9 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, int size; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { @@ -2560,6 +2608,9 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, int size; int ret = 0; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { From 8ec7efc24bd60ca875418eee8a6e1f42b5b978e7 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 14 Mar 2011 15:11:24 +0000 Subject: [PATCH 0949/2556] drm/i915: Disable pagefaults along execbuffer relocation fast path commit d4aeee776017b6da6dcd12f453cd82a3c951a0dc upstream. Along the fast path for relocation handling, we attempt to copy directly from the user data structures whilst holding our mutex. This causes lockdep to warn about circular lock dependencies if we need to pagefault the user pages. [Since when handling a page fault on a mmapped bo, we need to acquire the struct mutex whilst already holding the mm semaphore, it is then verboten to acquire the mm semaphore when already holding the struct mutex. The likelihood of the user passing in the relocations contained in a GTT mmaped bo is low, but conceivable for extreme pathology.] In order to force the mm to return EFAULT rather than handle the pagefault, we therefore need to disable pagefaults across the relocation fast path. Signed-off-by: Chris Wilson Reviewed-by: Daniel Vetter Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 50ab1614571c7..ded73a6007a8f 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -388,6 +388,10 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, uint32_t __iomem *reloc_entry; void __iomem *reloc_page; + /* We can't wait for rendering with pagefaults disabled */ + if (obj->active && in_atomic()) + return -EFAULT; + ret = i915_gem_object_set_to_gtt_domain(obj, 1); if (ret) return ret; @@ -461,15 +465,24 @@ i915_gem_execbuffer_relocate(struct drm_device *dev, struct list_head *objects) { struct drm_i915_gem_object *obj; - int ret; - + int ret = 0; + + /* This is the fast path and we cannot handle a pagefault whilst + * holding the struct mutex lest the user pass in the relocations + * contained within a mmaped bo. For in such a case we, the page + * fault handler would call i915_gem_fault() and we would try to + * acquire the struct mutex again. Obviously this is bad and so + * lockdep complains vehemently. + */ + pagefault_disable(); list_for_each_entry(obj, objects, exec_list) { ret = i915_gem_execbuffer_relocate_object(obj, eb); if (ret) - return ret; + break; } + pagefault_enable(); - return 0; + return ret; } static int From 814aa2c9b2e2531afbedaa8d8eefaf492d9076f9 Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Thu, 17 Mar 2011 13:45:12 +0000 Subject: [PATCH 0950/2556] drm/i915: Prevent racy removal of request from client list commit 09bfa51773c1e90f13000dc2fc0c4b84047009bc upstream. When i915_gem_retire_requests_ring calls i915_gem_request_remove_from_client, the client_list for that request may already be removed in i915_gem_release. So we may call twice list_del(&request->client_list), resulting in an oops like this report: [126167.230394] BUG: unable to handle kernel paging request at 00100104 [126167.230699] IP: [] i915_gem_retire_requests_ring+0xd4/0x240 [i915] [126167.231042] *pdpt = 00000000314c1001 *pde = 0000000000000000 [126167.231314] Oops: 0002 [#1] SMP [126167.231471] last sysfs file: /sys/devices/LNXSYSTM:00/device:00/PNP0C0A:00/power_supply/BAT1/current_now [126167.231901] Modules linked in: snd_seq_dummy nls_utf8 isofs btrfs zlib_deflate libcrc32c ufs qnx4 hfsplus hfs minix ntfs vfat msdos fat jfs xfs exportfs reiserfs cryptd aes_i586 aes_generic binfmt_misc vboxnetadp vboxnetflt vboxdrv parport_pc ppdev snd_hda_codec_hdmi snd_hda_codec_conexant snd_hda_intel snd_hda_codec snd_hwdep arc4 snd_pcm snd_seq_midi snd_rawmidi snd_seq_midi_event snd_seq uvcvideo videodev snd_timer snd_seq_device joydev iwlagn iwlcore mac80211 snd cfg80211 soundcore i915 drm_kms_helper snd_page_alloc psmouse drm serio_raw i2c_algo_bit video lp parport usbhid hid sky2 sdhci_pci ahci sdhci libahci [126167.232018] [126167.232018] Pid: 1101, comm: Xorg Not tainted 2.6.38-6-generic-pae #34-Ubuntu Gateway MC7833U / [126167.232018] EIP: 0060:[] EFLAGS: 00213246 CPU: 0 [126167.232018] EIP is at i915_gem_retire_requests_ring+0xd4/0x240 [i915] [126167.232018] EAX: 00200200 EBX: f1ac25b0 ECX: 00000040 EDX: 00100100 [126167.232018] ESI: f1a2801c EDI: e87fc060 EBP: ef4d7dd8 ESP: ef4d7db0 [126167.232018] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 [126167.232018] Process Xorg (pid: 1101, ti=ef4d6000 task=f1ba6500 task.ti=ef4d6000) [126167.232018] Stack: [126167.232018] f1a28000 f1a2809c f1a28094 0058bd97 f1aa2400 f1a2801c 0058bd7b 0058bd85 [126167.232018] f1a2801c f1a28000 ef4d7e38 f8c2e995 ef4d7e30 ef4d7e60 c14d1ebc f6b3a040 [126167.232018] f1522cc0 000000db 00000000 f1ba6500 ffffffa1 00000000 00000001 f1a29214 [126167.232018] Call Trace: Unfortunately the call trace reported was cut, but looking at debug symbols the crash is at __list_del, when probably list_del is called twice on the same request->client_list, as the dereferenced value is LIST_POISON1 + 4, and by looking more at the debug symbols before list_del call it should have being called by i915_gem_request_remove_from_client And as I can see in the code, it seems we indeed have the possibility to remove a request->client_list twice, which would cause the above, because we do list_del(&request->client_list) on both i915_gem_request_remove_from_client and i915_gem_release As Chris Wilson pointed out, it's indeed the case: "(...) I had thought that the actual insertion/deletion was serialised under the struct mutex and the intention of the spinlock was to protect the unlocked list traversal during throttling. However, I missed that i915_gem_release() is also called without struct mutex and so we do need the double check for i915_gem_request_remove_from_client()." This change does the required check to avoid the duplicate remove of request->client_list. Bugzilla: http://bugs.launchpad.net/bugs/733780 Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Chris Wilson Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/i915_gem.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 36e66cc5225eb..729c95a8fe6ea 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1749,8 +1749,10 @@ i915_gem_request_remove_from_client(struct drm_i915_gem_request *request) return; spin_lock(&file_priv->mm.lock); - list_del(&request->client_list); - request->file_priv = NULL; + if (request->file_priv) { + list_del(&request->client_list); + request->file_priv = NULL; + } spin_unlock(&file_priv->mm.lock); } From d81619c7c5e9bfcf5971a624c3a7aaf20c57cfde Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 17 Mar 2011 22:33:33 +0000 Subject: [PATCH 0951/2556] drm: Fix use-after-free in drm_gem_vm_close() commit b74ad5ae14def5e81ad0be3dddb96e485b861b1b upstream. As we may release the last reference, we need to store the device in a local variable in order to unlock afterwards. [ 60.140768] BUG: unable to handle kernel paging request at 6b6b6b9f [ 60.140973] IP: [] __mutex_unlock_slowpath+0x5a/0x111 [ 60.141014] *pdpt = 0000000024a54001 *pde = 0000000000000000 [ 60.141014] Oops: 0002 [#1] PREEMPT SMP [ 60.141014] last sysfs file: /sys/devices/LNXSYSTM:00/device:00/PNP0A08:00/PNP0C0A:00/power_supply/BAT0/voltage_now [ 60.141014] Modules linked in: uvcvideo ath9k pegasus ath9k_common ath9k_hw hid_egalax ath3k joydev asus_laptop sparse_keymap battery input_polldev [ 60.141014] [ 60.141014] Pid: 771, comm: meego-ux-daemon Not tainted 2.6.37.2-7.1 #1 EXOPC EXOPG06411/EXOPG06411 [ 60.141014] EIP: 0060:[] EFLAGS: 00010046 CPU: 0 [ 60.141014] EIP is at __mutex_unlock_slowpath+0x5a/0x111 [ 60.141014] EAX: 00000100 EBX: 6b6b6b9b ECX: e9b4a1b0 EDX: e4a4e580 [ 60.141014] ESI: db162558 EDI: 00000246 EBP: e480be50 ESP: e480be44 [ 60.141014] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 [ 60.141014] Process meego-ux-daemon (pid: 771, ti=e480a000 task=e9b4a1b0 task.ti=e480a000) [ 60.141014] Stack: [ 60.141014] e4a4e580 db162558 f5a2f838 e480be58 c1536dd0 e480be68 c125ab1b db162558 [ 60.141014] db1624e0 e480be78 c10ba071 db162558 f760241c e480be94 c10bb0bc 000155fe [ 60.141014] f760241c f5a2f838 f5a2f8c8 00000000 e480bea4 c1037c24 00000000 f5a2f838 [ 60.141014] Call Trace: [ 60.141014] [] ? mutex_unlock+0x8/0xa [ 60.141014] [] ? drm_gem_vm_close+0x39/0x3d [ 60.141014] [] ? remove_vma+0x2d/0x58 [ 60.141014] [] ? exit_mmap+0x126/0x13f [ 60.141014] [] ? mmput+0x37/0x9a [ 60.141014] [] ? exec_mmap+0x178/0x19c [ 60.141014] [] ? _raw_spin_unlock+0x1d/0x36 [ 60.141014] [] ? flush_old_exec+0x42/0x75 [ 60.141014] [] ? load_elf_binary+0x32a/0x922 [ 60.141014] [] ? search_binary_handler+0x200/0x2ea [ 60.141014] [] ? search_binary_handler+0x159/0x2ea [ 60.141014] [] ? load_elf_binary+0x0/0x922 [ 60.141014] [] ? do_execve+0x1ff/0x2e6 [ 60.141014] [] ? sys_execve+0x2d/0x55 [ 60.141014] [] ? ptregs_execve+0x12/0x18 [ 60.141014] [] ? sysenter_do_call+0x12/0x3c [ 60.141014] [] ? init_centaur+0x9c/0x1ba [ 60.141014] Code: c1 00 75 0f ba 38 01 00 00 b8 8c 3a 6c c1 e8 cc 2e b0 ff 9c 58 8d 74 26 00 89 c7 fa 90 8d 74 26 00 e8 d2 b4 b2 ff b8 00 01 00 00 66 0f c1 43 04 38 e0 74 07 f3 90 8a 43 04 eb f5 83 3d 64 ef [ 60.141014] EIP: [] __mutex_unlock_slowpath+0x5a/0x111 SS:ESP 0068:e480be44 [ 60.141014] CR2: 000000006b6b6b9f Reported-by: Rusty Lynch Signed-off-by: Chris Wilson Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/drm_gem.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index ea1c4b019ebf9..c3c78eefc538d 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -498,11 +498,12 @@ EXPORT_SYMBOL(drm_gem_vm_open); void drm_gem_vm_close(struct vm_area_struct *vma) { struct drm_gem_object *obj = vma->vm_private_data; + struct drm_device *dev = obj->dev; - mutex_lock(&obj->dev->struct_mutex); + mutex_lock(&dev->struct_mutex); drm_vm_close_locked(vma); drm_gem_object_unreference(obj); - mutex_unlock(&obj->dev->struct_mutex); + mutex_unlock(&dev->struct_mutex); } EXPORT_SYMBOL(drm_gem_vm_close); From b62dd4442df4703e0388f20cd838fb218dc8906f Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 22 Mar 2011 01:46:12 -0400 Subject: [PATCH 0952/2556] drm/radeon/kms: prefer legacy pll algo for tv-out commit 64146f8b2af1ba77fe3c21d9d6d7213b9bb72b40 upstream. ntsc seems to work fine with either algo, some pal TVs seem pickier. Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=30832 Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/atombios_crtc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 4a5a73bcf460a..e967cc869b3b2 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -957,7 +957,11 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode /* adjust pixel clock as needed */ adjusted_clock = atombios_adjust_pll(crtc, mode, pll, ss_enabled, &ss); - if (ASIC_IS_AVIVO(rdev)) + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) + /* TV seems to prefer the legacy algo on some boards */ + radeon_compute_pll_legacy(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div, + &ref_div, &post_div); + else if (ASIC_IS_AVIVO(rdev)) radeon_compute_pll_avivo(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div, &ref_div, &post_div); else From 1a7fb011eeafb9feaa4a80beae055dc61fe4ea37 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 23 Mar 2011 08:10:10 +0000 Subject: [PATCH 0953/2556] drm/radeon/kms: fix hardcoded EDID handling commit fafcf94e2b5732d1e13b440291c53115d2b172e9 upstream. On some servers there is a hardcoded EDID provided in the vbios so that the driver will always see a display connected even if something like a KVM prevents traditional means like DDC or load detection from working properly. Also most server boards with DVI are not actually DVI, but DVO connected to a virtual KVM service processor. If we fail to detect a monitor via DDC or load detection and a hardcoded EDID is available, use it. Additionally, when using the hardcoded EDID, use a copy of it rather than the actual one stored in the driver as the detect() and get_modes() functions may free it if DDC is successful. This fixes the virtual KVM on several internal servers. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_combios.c | 21 +++++++++++---- drivers/gpu/drm/radeon/radeon_connectors.c | 30 ++++++++++++++++++++-- drivers/gpu/drm/radeon/radeon_mode.h | 1 + 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index cf7c8d5b4ec24..cf602e2d0718e 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -448,7 +448,7 @@ static uint16_t combios_get_table_offset(struct drm_device *dev, bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) { - int edid_info; + int edid_info, size; struct edid *edid; unsigned char *raw; edid_info = combios_get_table_offset(rdev->ddev, COMBIOS_HARDCODED_EDID_TABLE); @@ -456,11 +456,12 @@ bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) return false; raw = rdev->bios + edid_info; - edid = kmalloc(EDID_LENGTH * (raw[0x7e] + 1), GFP_KERNEL); + size = EDID_LENGTH * (raw[0x7e] + 1); + edid = kmalloc(size, GFP_KERNEL); if (edid == NULL) return false; - memcpy((unsigned char *)edid, raw, EDID_LENGTH * (raw[0x7e] + 1)); + memcpy((unsigned char *)edid, raw, size); if (!drm_edid_is_valid(edid)) { kfree(edid); @@ -468,6 +469,7 @@ bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) } rdev->mode_info.bios_hardcoded_edid = edid; + rdev->mode_info.bios_hardcoded_edid_size = size; return true; } @@ -475,8 +477,17 @@ bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) struct edid * radeon_bios_get_hardcoded_edid(struct radeon_device *rdev) { - if (rdev->mode_info.bios_hardcoded_edid) - return rdev->mode_info.bios_hardcoded_edid; + struct edid *edid; + + if (rdev->mode_info.bios_hardcoded_edid) { + edid = kmalloc(rdev->mode_info.bios_hardcoded_edid_size, GFP_KERNEL); + if (edid) { + memcpy((unsigned char *)edid, + (unsigned char *)rdev->mode_info.bios_hardcoded_edid, + rdev->mode_info.bios_hardcoded_edid_size); + return edid; + } + } return NULL; } diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 22b7e3dc0eca4..d83338b11cdb0 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -629,6 +629,8 @@ static int radeon_vga_mode_valid(struct drm_connector *connector, static enum drm_connector_status radeon_vga_detect(struct drm_connector *connector, bool force) { + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct drm_encoder *encoder; struct drm_encoder_helper_funcs *encoder_funcs; @@ -679,6 +681,17 @@ radeon_vga_detect(struct drm_connector *connector, bool force) if (ret == connector_status_connected) ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true); + + /* RN50 and some RV100 asics in servers often have a hardcoded EDID in the + * vbios to deal with KVMs. If we have one and are not able to detect a monitor + * by other means, assume the CRT is connected and use that EDID. + */ + if ((!rdev->is_atom_bios) && + (ret == connector_status_disconnected) && + rdev->mode_info.bios_hardcoded_edid_size) { + ret = connector_status_connected; + } + radeon_connector_update_scratch_regs(connector, ret); return ret; } @@ -790,6 +803,8 @@ static int radeon_dvi_get_modes(struct drm_connector *connector) static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connector, bool force) { + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct drm_encoder *encoder = NULL; struct drm_encoder_helper_funcs *encoder_funcs; @@ -829,8 +844,6 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) * you don't really know what's connected to which port as both are digital. */ if (radeon_connector->shared_ddc && (ret == connector_status_connected)) { - struct drm_device *dev = connector->dev; - struct radeon_device *rdev = dev->dev_private; struct drm_connector *list_connector; struct radeon_connector *list_radeon_connector; list_for_each_entry(list_connector, &dev->mode_config.connector_list, head) { @@ -895,6 +908,19 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true); } + /* RN50 and some RV100 asics in servers often have a hardcoded EDID in the + * vbios to deal with KVMs. If we have one and are not able to detect a monitor + * by other means, assume the DFP is connected and use that EDID. In most + * cases the DVI port is actually a virtual KVM port connected to the service + * processor. + */ + if ((!rdev->is_atom_bios) && + (ret == connector_status_disconnected) && + rdev->mode_info.bios_hardcoded_edid_size) { + radeon_connector->use_digital = true; + ret = connector_status_connected; + } + out: /* updated in get modes as well since we need to know if it's analog or digital */ radeon_connector_update_scratch_regs(connector, ret); diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index a670caaee29e5..8c134db007d9b 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -239,6 +239,7 @@ struct radeon_mode_info { struct drm_property *underscan_vborder_property; /* hardcoded DFP edid from BIOS */ struct edid *bios_hardcoded_edid; + int bios_hardcoded_edid_size; /* pointer to fbdev info structure */ struct radeon_fbdev *rfbdev; From 85dcda94e03ebe54e3edb66aae9598a6bde3ddd2 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 15 Mar 2011 14:37:10 +0100 Subject: [PATCH 0954/2556] perf: Fix tear-down of inherited group events commit 38b435b16c36b0d863efcf3f07b34a6fac9873fd upstream. When destroying inherited events, we need to destroy groups too, otherwise the event iteration in perf_event_exit_task_context() will miss group siblings and we leak events with all the consequences. Reported-and-tested-by: Vince Weaver Signed-off-by: Peter Zijlstra LKML-Reference: <1300196470.2203.61.camel@twins> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/perf_event.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/kernel/perf_event.c b/kernel/perf_event.c index b22a2ef75c86f..ad02feadb6b02 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -6115,17 +6115,20 @@ __perf_event_exit_task(struct perf_event *child_event, struct perf_event_context *child_ctx, struct task_struct *child) { - struct perf_event *parent_event; + if (child_event->parent) { + raw_spin_lock_irq(&child_ctx->lock); + perf_group_detach(child_event); + raw_spin_unlock_irq(&child_ctx->lock); + } perf_event_remove_from_context(child_event); - parent_event = child_event->parent; /* - * It can happen that parent exits first, and has events + * It can happen that the parent exits first, and has events * that are still around due to the child reference. These - * events need to be zapped - but otherwise linger. + * events need to be zapped. */ - if (parent_event) { + if (child_event->parent) { sync_child_event(child_event, child); free_event(child_event); } From 68589dac59ea15e5879529d6882ab348360cfd09 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 21 Mar 2011 15:37:01 -0400 Subject: [PATCH 0955/2556] NFS: Fix a hang/infinite loop in nfs_wb_page() commit b8413f98f997bb3ed7327e6d7117e7e91ce010c3 upstream. When one of the two waits in nfs_commit_inode() is interrupted, it returns a non-negative value, which causes nfs_wb_page() to think that the operation was successful causing it to busy-loop rather than exiting. It also causes nfs_file_fsync() to incorrectly report the file as being successfully committed to disk. This patch fixes both problems by ensuring that we return an error if the attempts to wait fail. Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- fs/nfs/write.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 42b92d7a9cc4e..b5fcbf7da6fc3 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1214,13 +1214,17 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) static int nfs_commit_set_lock(struct nfs_inode *nfsi, int may_wait) { + int ret; + if (!test_and_set_bit(NFS_INO_COMMIT, &nfsi->flags)) return 1; - if (may_wait && !out_of_line_wait_on_bit_lock(&nfsi->flags, - NFS_INO_COMMIT, nfs_wait_bit_killable, - TASK_KILLABLE)) - return 1; - return 0; + if (!may_wait) + return 0; + ret = out_of_line_wait_on_bit_lock(&nfsi->flags, + NFS_INO_COMMIT, + nfs_wait_bit_killable, + TASK_KILLABLE); + return (ret < 0) ? ret : 1; } static void nfs_commit_clear_lock(struct nfs_inode *nfsi) @@ -1396,9 +1400,10 @@ int nfs_commit_inode(struct inode *inode, int how) { LIST_HEAD(head); int may_wait = how & FLUSH_SYNC; - int res = 0; + int res; - if (!nfs_commit_set_lock(NFS_I(inode), may_wait)) + res = nfs_commit_set_lock(NFS_I(inode), may_wait); + if (res <= 0) goto out_mark_dirty; spin_lock(&inode->i_lock); res = nfs_scan_commit(inode, &head, 0, 0); @@ -1407,12 +1412,14 @@ int nfs_commit_inode(struct inode *inode, int how) int error = nfs_commit_list(inode, &head, how); if (error < 0) return error; - if (may_wait) - wait_on_bit(&NFS_I(inode)->flags, NFS_INO_COMMIT, - nfs_wait_bit_killable, - TASK_KILLABLE); - else + if (!may_wait) goto out_mark_dirty; + error = wait_on_bit(&NFS_I(inode)->flags, + NFS_INO_COMMIT, + nfs_wait_bit_killable, + TASK_KILLABLE); + if (error < 0) + return error; } else nfs_commit_clear_lock(NFS_I(inode)); return res; From 9f63a51f6d0a67790388d2710611b71b5ee48bf8 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 22 Mar 2011 18:40:10 -0400 Subject: [PATCH 0956/2556] SUNRPC: Never reuse the socket port after an xs_close() commit 246408dcd5dfeef2df437ccb0ef4d6ee87805f58 upstream. If we call xs_close(), we're in one of two situations: - Autoclose, which means we don't expect to resend a request - bind+connect failed, which probably means the port is in use Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/xprtsock.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index be96d429b475f..1e336a06d3e63 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -710,6 +710,8 @@ static void xs_reset_transport(struct sock_xprt *transport) if (sk == NULL) return; + transport->srcport = 0; + write_lock_bh(&sk->sk_callback_lock); transport->inet = NULL; transport->sock = NULL; From f1bc19369772f1c221a59e043b67ef2eee582427 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 18 Nov 2010 20:52:55 -0500 Subject: [PATCH 0957/2556] fs: call security_d_instantiate in d_obtain_alias V2 commit 24ff6663ccfdaf088dfa7acae489cb11ed4f43c4 upstream. While trying to track down some NFS problems with BTRFS, I kept noticing I was getting -EACCESS for no apparent reason. Eric Paris and printk() helped me figure out that it was SELinux that was giving me grief, with the following denial type=AVC msg=audit(1290013638.413:95): avc: denied { 0x800000 } for pid=1772 comm="nfsd" name="" dev=sda1 ino=256 scontext=system_u:system_r:kernel_t:s0 tcontext=system_u:object_r:unlabeled_t:s0 tclass=file Turns out this is because in d_obtain_alias if we can't find an alias we create one and do all the normal instantiation stuff, but we don't do the security_d_instantiate. Usually we are protected from getting a hashed dentry that hasn't yet run security_d_instantiate() by the parent's i_mutex, but obviously this isn't an option there, so in order to deal with the case that a second thread comes in and finds our new dentry before we get to run security_d_instantiate(), we go ahead and call it if we find a dentry already. Eric assures me that this is ok as the code checks to see if the dentry has been initialized already so calling security_d_instantiate() against the same dentry multiple times is ok. With this patch I'm no longer getting errant -EACCESS values. Signed-off-by: Josef Bacik Signed-off-by: Al Viro Cc: Chuck Ebbert Signed-off-by: Greg Kroah-Hartman --- fs/dcache.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/dcache.c b/fs/dcache.c index a39fe47c466f7..1baddc1cec48f 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1612,10 +1612,13 @@ struct dentry *d_obtain_alias(struct inode *inode) __bit_spin_unlock(0, (unsigned long *)&tmp->d_sb->s_anon.first); spin_unlock(&tmp->d_lock); spin_unlock(&inode->i_lock); + security_d_instantiate(tmp, inode); return tmp; out_iput: + if (res && !IS_ERR(res)) + security_d_instantiate(res, inode); iput(inode); return res; } From 5e52b12c5efae3ae2731ad103e6fbe6fd24a9bc0 Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Wed, 2 Mar 2011 13:42:05 +0100 Subject: [PATCH 0958/2556] dcdbas: force SMI to happen when expected commit dd65c736d1b5312c80c88a64bf521db4959eded5 upstream. The dcdbas driver can do an I/O write to cause a SMI to occur. The SMI handler looks at certain registers and memory locations, so the SMI needs to happen immediately. On some systems I/O writes are posted, though, causing the SMI to happen well after the "outb" occurred, which causes random failures. Following the "outb" with an "inb" forces the write to go through even if it is posted. Signed-off-by: Stuart Hayes Acked-by: Doug Warzecha Cc: Chuck Ebbert Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/dcdbas.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c index 69ad529d92fbb..ea5ac2dc12337 100644 --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c @@ -268,8 +268,10 @@ int dcdbas_smi_request(struct smi_cmd *smi_cmd) } /* generate SMI */ + /* inb to force posted write through and make SMI happen now */ asm volatile ( - "outb %b0,%w1" + "outb %b0,%w1\n" + "inb %w1" : /* no output args */ : "a" (smi_cmd->command_code), "d" (smi_cmd->command_address), From 60f939f9463bb75dd23f90659490eca27ab76b0a Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Mon, 28 Feb 2011 00:53:45 -0500 Subject: [PATCH 0959/2556] ext4: skip orphan cleanup if fs has unknown ROCOMPAT features commit d39195c33bb1b5fdcb0f416e8a0b34bfdb07a027 upstream. Orphan cleanup is currently executed even if the file system has some number of unknown ROCOMPAT features, which deletes inodes and frees blocks, which could be very bad for some RO_COMPAT features, especially the SNAPSHOT feature. This patch skips the orphan cleanup if it contains readonly compatible features not known by this ext4 implementation, which would prevent the fs from being mounted (or remounted) readwrite. Signed-off-by: Amir Goldstein Signed-off-by: "Theodore Ts'o" Signed-off-by: Greg Kroah-Hartman --- fs/ext4/super.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index f6a318f836b2c..4381efee3dbc1 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -75,6 +75,7 @@ static void ext4_write_super(struct super_block *sb); static int ext4_freeze(struct super_block *sb); static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data); +static int ext4_feature_set_ok(struct super_block *sb, int readonly); static void ext4_destroy_lazyinit_thread(void); static void ext4_unregister_li_request(struct super_block *sb); static void ext4_clear_request_list(void); @@ -2120,6 +2121,13 @@ static void ext4_orphan_cleanup(struct super_block *sb, return; } + /* Check if feature set would not allow a r/w mount */ + if (!ext4_feature_set_ok(sb, 0)) { + ext4_msg(sb, KERN_INFO, "Skipping orphan cleanup due to " + "unknown ROCOMPAT features"); + return; + } + if (EXT4_SB(sb)->s_mount_state & EXT4_ERROR_FS) { if (es->s_last_orphan) jbd_debug(1, "Errors on filesystem, " From fe2e477d14653aff6cee232b1dd500e42889db06 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 27 Mar 2011 11:37:20 -0700 Subject: [PATCH 0960/2556] Linux 2.6.38.2 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 167ef453a8cd5..6c155251f7228 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 38 -EXTRAVERSION = .1 +EXTRAVERSION = .2 NAME = Flesh-Eating Bats with Fangs # *DOCUMENTATION* From a436b50a32801e6c3fa647e499fa6a0177e51d75 Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Tue, 29 Mar 2011 18:23:08 -0400 Subject: [PATCH 0961/2556] add Makefile FLAGS --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 6c155251f7228..2372fed455d86 100644 --- a/Makefile +++ b/Makefile @@ -338,8 +338,8 @@ CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ CFLAGS_MODULE = AFLAGS_MODULE = LDFLAGS_MODULE = -CFLAGS_KERNEL = -AFLAGS_KERNEL = +CFLAGS_KERNEL = -O1 -mtune=cortex-a8 -ftree-vectorize -ffast-math -fsingle-precision-constant +AFLAGS_KERNEL = -O1 -mtune=cortex-a8 -ftree-vectorize -ffast-math -fsingle-precision-constant CFLAGS_GCOV = -fprofile-arcs -ftest-coverage From 4a8ec2d0dc95833cd4502d76d540a364b1edb11e Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Tue, 29 Mar 2011 18:23:29 -0400 Subject: [PATCH 0962/2556] vfp: set mfpu=neon --- arch/arm/vfp/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/vfp/Makefile b/arch/arm/vfp/Makefile index 39f6d8e1af730..e8cede3368e03 100644 --- a/arch/arm/vfp/Makefile +++ b/arch/arm/vfp/Makefile @@ -7,7 +7,7 @@ # EXTRA_CFLAGS := -DDEBUG # EXTRA_AFLAGS := -DDEBUG -KBUILD_AFLAGS :=$(KBUILD_AFLAGS:-msoft-float=-Wa,-mfpu=softvfp+vfp) +KBUILD_AFLAGS :=$(KBUILD_AFLAGS:-msoft-float=-Wa,-mfpu=neon) LDFLAGS +=--no-warn-mismatch obj-y += vfp.o From 4770004fbef2dac17c1ce5dbc1f02549f56628cb Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Tue, 29 Mar 2011 18:24:02 -0400 Subject: [PATCH 0963/2556] msm: add .fpu neon to VFP --- arch/arm/mach-msm/idle-v7.S | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/idle-v7.S b/arch/arm/mach-msm/idle-v7.S index 967db0b4ac510..3fd510f9450a5 100644 --- a/arch/arm/mach-msm/idle-v7.S +++ b/arch/arm/mach-msm/idle-v7.S @@ -43,6 +43,7 @@ ENTRY(msm_pm_collapse) stmia r0!, {r1-r9, ip} #ifdef CONFIG_VFP + .fpu neon VFPFSTMIA r0, r1 /* Save VFP working registers */ fmrx r1, fpexc fmrx r2, fpscr From a1384591511ec78ff5c08c8ff2e31f1bd6b209ab Mon Sep 17 00:00:00 2001 From: Jonas Larsson Date: Sat, 14 Aug 2010 20:30:36 +0200 Subject: [PATCH 0964/2556] mahimahi: Enable full light sensor range --- arch/arm/mach-msm/board-mahimahi-microp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-mahimahi-microp.c b/arch/arm/mach-msm/board-mahimahi-microp.c index aef72af95e1ce..8161517089c9a 100644 --- a/arch/arm/mach-msm/board-mahimahi-microp.c +++ b/arch/arm/mach-msm/board-mahimahi-microp.c @@ -212,7 +212,7 @@ enum led_type { }; static uint16_t lsensor_adc_table[10] = { - 0x000, 0x001, 0x00F, 0x01E, 0x03C, 0x121, 0x190, 0x2BA, 0x26E, 0x3FF + 0x000, 0x001, 0x00F, 0x01E, 0x03C, 0x121, 0x190, 0x2BA, 0x35C, 0x3FF }; static uint16_t remote_key_adc_table[6] = { From f037a1799671819073ccd9222690c85bc58952a8 Mon Sep 17 00:00:00 2001 From: xinyu Date: Tue, 12 Oct 2010 18:59:10 +0800 Subject: [PATCH 0965/2556] support FM Radio in kernel --- .../mach-msm/include/mach/msm_qdsp6_audio.h | 3 ++ arch/arm/mach-msm/qdsp6/audio_ctl.c | 45 +++++++++++++++++++ arch/arm/mach-msm/qdsp6/q6audio.c | 32 +++++++++++++ include/linux/msm_audio.h | 2 + 4 files changed, 82 insertions(+) diff --git a/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h index 2b4df49ebfc52..acc9ce308fe90 100644 --- a/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h +++ b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h @@ -73,6 +73,9 @@ int q6audio_close(struct audio_client *ac); int q6voice_close(struct audio_client *ac); int q6audio_mp3_close(struct audio_client *ac); +struct audio_client *q6fm_open(void); +int q6fm_close(struct audio_client *ac); + int q6audio_read(struct audio_client *ac, struct audio_buffer *ab); int q6audio_write(struct audio_client *ac, struct audio_buffer *ab); int q6audio_async(struct audio_client *ac); diff --git a/arch/arm/mach-msm/qdsp6/audio_ctl.c b/arch/arm/mach-msm/qdsp6/audio_ctl.c index c7d5bc703357e..23d9eabbdd138 100644 --- a/arch/arm/mach-msm/qdsp6/audio_ctl.c +++ b/arch/arm/mach-msm/qdsp6/audio_ctl.c @@ -25,10 +25,13 @@ #define BUFSZ (0) static DEFINE_MUTEX(voice_lock); +static DEFINE_MUTEX(fm_lock); static int voice_started; +static int fm_started; static struct audio_client *voc_tx_clnt; static struct audio_client *voc_rx_clnt; +static struct audio_client *fm_clnt; static int q6_voice_start(uint32_t rx_acdb_id, uint32_t tx_acdb_id) { @@ -74,6 +77,42 @@ static int q6_voice_stop(void) return 0; } +int q6_fm_start(void) +{ + int rc = 0; + + mutex_lock(&fm_lock); + + if (fm_started) { + pr_err("fm: busy\n"); + rc = -EBUSY; + goto done; + } + + fm_clnt = q6fm_open(); + if (!fm_clnt) { + pr_err("fm: open failed.\n"); + rc = -ENOMEM; + goto done; + } + + fm_started = 1; +done: + mutex_unlock(&fm_lock); + return rc; +} + +int q6_fm_stop(void) +{ + mutex_lock(&fm_lock); + if (fm_started) { + q6fm_close(fm_clnt); + fm_started = 0; + } + mutex_unlock(&fm_lock); + return 0; +} + static int q6_open(struct inode *inode, struct file *file) { return 0; @@ -132,6 +171,12 @@ static long q6_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case AUDIO_STOP_VOICE: rc = q6_voice_stop(); break; + case AUDIO_START_FM: + rc = q6_fm_start(); + break; + case AUDIO_STOP_FM: + rc = q6_fm_stop(); + break; case AUDIO_REINIT_ACDB: rc = copy_from_user(&filename, (void *)arg, sizeof(filename)); if (rc) { diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c index 1b053e7e9efe6..9b62bb6d1ae86 100644 --- a/arch/arm/mach-msm/qdsp6/q6audio.c +++ b/arch/arm/mach-msm/qdsp6/q6audio.c @@ -32,6 +32,7 @@ #include "dal_acdb.h" #include "dal_adie.h" #include +#include #include @@ -1572,3 +1573,34 @@ int q6audio_async(struct audio_client *ac) rpc.response_type = ADSP_AUDIO_RESPONSE_ASYNC; return audio_ioctl(ac, &rpc, sizeof(rpc)); } + +struct audio_client *q6fm_open(void) +{ + struct audio_client *ac; + + if (q6audio_init()) + return 0; + +/* if (audio_rx_device_id != ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO && + audio_rx_device_id != ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO) + return 0; +*/ + ac = audio_client_alloc(0); + if (!ac) + return 0; + + ac->flags = AUDIO_FLAG_WRITE; + audio_rx_path_enable(1, 0); + enable_aux_loopback(1); + + return ac; +} + +int q6fm_close(struct audio_client *ac) +{ + audio_rx_path_enable(0, 0); + enable_aux_loopback(0); + audio_client_free(ac); + return 0; +} + diff --git a/include/linux/msm_audio.h b/include/linux/msm_audio.h index 5875c9950fea9..8c1fb13937259 100644 --- a/include/linux/msm_audio.h +++ b/include/linux/msm_audio.h @@ -47,6 +47,8 @@ #define AUDIO_UPDATE_ACDB _IOW(AUDIO_IOCTL_MAGIC, 34, unsigned) #define AUDIO_START_VOICE _IOW(AUDIO_IOCTL_MAGIC, 35, unsigned) #define AUDIO_STOP_VOICE _IOW(AUDIO_IOCTL_MAGIC, 36, unsigned) +#define AUDIO_START_FM _IOW(AUDIO_IOCTL_MAGIC, 37, unsigned) +#define AUDIO_STOP_FM _IOW(AUDIO_IOCTL_MAGIC, 38, unsigned) #define AUDIO_REINIT_ACDB _IOW(AUDIO_IOCTL_MAGIC, 39, unsigned) #define AUDIO_MAX_COMMON_IOCTL_NUM 100 From 6438de114c2954a7dced6455d067c5f4686aac3f Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Mon, 1 Nov 2010 03:00:59 -0400 Subject: [PATCH 0966/2556] msm_kgsl: Move kgsl_sharedmem_init to module load kgsl_sharedmem_init/kgsl_sharedmem_close only need to be called once, so move them to the the module load and close functions so they can be available through the life of the module. --- drivers/video/msm/gpu/kgsl/kgsl.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c index 4dde98e066421..edf3b90b282df 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.c +++ b/drivers/video/msm/gpu/kgsl/kgsl.c @@ -190,11 +190,6 @@ static int kgsl_first_open_locked(void) kgsl_clk_enable(); - /* init memory apertures */ - result = kgsl_sharedmem_init(&kgsl_driver.shmem); - if (result != 0) - goto done; - /* init devices */ result = kgsl_yamato_init(&kgsl_driver.yamato_device, &kgsl_driver.yamato_config); @@ -221,9 +216,6 @@ static int kgsl_last_release_locked(void) /* close devices */ kgsl_yamato_close(&kgsl_driver.yamato_device); - /* shutdown memory apertures */ - kgsl_sharedmem_close(&kgsl_driver.shmem); - kgsl_clk_disable(); kgsl_driver.active = false; wake_unlock(&kgsl_driver.wake_lock); @@ -1066,6 +1058,9 @@ static void kgsl_driver_cleanup(void) kgsl_driver.interrupt_num = 0; } + /* shutdown memory apertures */ + kgsl_sharedmem_close(&kgsl_driver.shmem); + if (kgsl_driver.grp_clk) { clk_put(kgsl_driver.grp_clk); kgsl_driver.grp_clk = NULL; @@ -1170,6 +1165,9 @@ static int __devinit kgsl_platform_probe(struct platform_device *pdev) kgsl_driver.shmem.physbase = res->start; kgsl_driver.shmem.size = resource_size(res); + /* init memory apertures */ + result = kgsl_sharedmem_init(&kgsl_driver.shmem); + done: if (result) kgsl_driver_cleanup(); From 05d36a17ed50d1aa74e744d5b1c53f970b507f2f Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Mon, 1 Nov 2010 03:38:55 -0400 Subject: [PATCH 0967/2556] msm_kgsl: Optimize TLB flushing Only flush TLB when it is absolutely nessasary as opposed to flushing it on every map and unmap --- drivers/video/msm/gpu/kgsl/kgsl_mmu.c | 70 +++++++++++++++------------ drivers/video/msm/gpu/kgsl/kgsl_mmu.h | 22 +++++++++ 2 files changed, 61 insertions(+), 31 deletions(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c index c32558ffe5849..ebab5416d4e5c 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c @@ -396,6 +396,16 @@ int kgsl_mmu_init(struct kgsl_device *device) return -ENOMEM; } mmu->hwpagetable = mmu->defaultpagetable; + mmu->tlbflushfilter.size = (mmu->va_range / + (PAGE_SIZE * GSL_PT_SUPER_PTE * 8)) + 1; + mmu->tlbflushfilter.base = (unsigned int *) + kzalloc(mmu->tlbflushfilter.size, GFP_KERNEL); + if (!mmu->tlbflushfilter.base) { + KGSL_MEM_ERR("Failed to create tlbflushfilter\n"); + kgsl_mmu_close(device); + return -ENOMEM; + } + GSL_TLBFLUSH_FILTER_RESET(); kgsl_yamato_regwrite(device, REG_MH_MMU_PT_BASE, mmu->hwpagetable->base.gpuaddr); kgsl_yamato_regwrite(device, REG_MH_MMU_VA_RANGE, @@ -456,7 +466,7 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, unsigned int flags) { int numpages; - unsigned int pte, superpte, ptefirst, ptelast, physaddr; + unsigned int pte, ptefirst, ptelast, physaddr; int flushtlb, alloc_size; struct kgsl_mmu *mmu = NULL; int phys_contiguous = flags & KGSL_MEMFLAGS_CONPHYS; @@ -514,15 +524,11 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, pte = ptefirst; flushtlb = 0; - superpte = ptefirst & (GSL_PT_SUPER_PTE - 1); - for (pte = superpte; pte < ptefirst; pte++) { - /* tlb needs to be flushed only when a dirty superPTE - gets backed */ - if (kgsl_pt_map_isdirty(pagetable, pte)) { - flushtlb = 1; - break; - } - } + /* tlb needs to be flushed when the first and last pte are not at + * superpte boundaries */ + if ((ptefirst & (GSL_PT_SUPER_PTE - 1)) != 0 || + ((ptelast + 1) & (GSL_PT_SUPER_PTE-1)) != 0) + flushtlb = 1; for (pte = ptefirst; pte < ptelast; pte++) { #ifdef VERBOSE_DEBUG @@ -530,8 +536,10 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, uint32_t val = kgsl_pt_map_getaddr(pagetable, pte); BUG_ON(val != 0 && val != GSL_PT_PAGE_DIRTY); #endif - if (kgsl_pt_map_isdirty(pagetable, pte)) - flushtlb = 1; + if ((pte & (GSL_PT_SUPER_PTE-1)) == 0) + if (GSL_TLBFLUSH_FILTER_ISDIRTY(pte / GSL_PT_SUPER_PTE)) + flushtlb = 1; + /* mark pte as in use */ if (phys_contiguous) physaddr = address; @@ -552,17 +560,6 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, address += KGSL_PAGESIZE; } - /* set superpte to end of next superpte */ - superpte = (ptelast + (GSL_PT_SUPER_PTE - 1)) - & (GSL_PT_SUPER_PTE - 1); - for (pte = ptelast; pte < superpte; pte++) { - /* tlb needs to be flushed only when a dirty superPTE - gets backed */ - if (kgsl_pt_map_isdirty(pagetable, pte)) { - flushtlb = 1; - break; - } - } KGSL_MEM_INFO("pt %p p %08x g %08x pte f %d l %d n %d f %d\n", pagetable, address, *gpuaddr, ptefirst, ptelast, numpages, flushtlb); @@ -571,8 +568,10 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable, /* Invalidate tlb only if current page table used by GPU is the * pagetable that we used to allocate */ - if (pagetable == mmu->hwpagetable) + if (flushtlb && (pagetable == mmu->hwpagetable)) { kgsl_yamato_setstate(mmu->device, KGSL_MMUFLAGS_TLBFLUSH); + GSL_TLBFLUSH_FILTER_RESET(); + } KGSL_MEM_VDBG("return %d\n", 0); @@ -585,7 +584,8 @@ kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, unsigned int gpuaddr, int range) { unsigned int numpages; - unsigned int pte, ptefirst, ptelast; + unsigned int pte, ptefirst, ptelast, superpte; + struct kgsl_mmu *mmu = NULL; KGSL_MEM_VDBG("enter (pt=%p, gpuaddr=0x%08x, range=%d)\n", pagetable, gpuaddr, range); @@ -602,22 +602,24 @@ kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, unsigned int gpuaddr, KGSL_MEM_INFO("pt %p gpu %08x pte first %d last %d numpages %d\n", pagetable, gpuaddr, ptefirst, ptelast, numpages); + mmu = pagetable->mmu; + + superpte = ptefirst - (ptefirst & (GSL_PT_SUPER_PTE-1)); + GSL_TLBFLUSH_FILTER_SETDIRTY(superpte / GSL_PT_SUPER_PTE); for (pte = ptefirst; pte < ptelast; pte++) { #ifdef VERBOSE_DEBUG /* check if PTE exists */ BUG_ON(!kgsl_pt_map_getaddr(pagetable, pte)); #endif kgsl_pt_map_set(pagetable, pte, GSL_PT_PAGE_DIRTY); + superpte = pte - (pte & (GSL_PT_SUPER_PTE - 1)); + if (pte == superpte) + GSL_TLBFLUSH_FILTER_SETDIRTY(superpte / + GSL_PT_SUPER_PTE); } dmb(); - /* Invalidate tlb only if current page table used by GPU is the - * pagetable that we used to allocate */ - if (pagetable == pagetable->mmu->hwpagetable) - kgsl_yamato_setstate(pagetable->mmu->device, - KGSL_MMUFLAGS_TLBFLUSH); - gen_pool_free(pagetable->pool, gpuaddr, range); KGSL_MEM_VDBG("return %d\n", 0); @@ -651,6 +653,12 @@ int kgsl_mmu_close(struct kgsl_device *device) if (mmu->dummyspace.gpuaddr) kgsl_sharedmem_free(&mmu->dummyspace); + if (mmu->tlbflushfilter.base) { + mmu->tlbflushfilter.size = 0; + kfree(mmu->tlbflushfilter.base); + mmu->tlbflushfilter.base = NULL; + } + mmu->flags &= ~KGSL_FLAGS_STARTED; mmu->flags &= ~KGSL_FLAGS_INITIALIZED; mmu->flags &= ~KGSL_FLAGS_INITIALIZED0; diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.h b/drivers/video/msm/gpu/kgsl/kgsl_mmu.h index d70f24a1e514a..6074518756ffe 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_mmu.h +++ b/drivers/video/msm/gpu/kgsl/kgsl_mmu.h @@ -31,6 +31,21 @@ #define KGSL_MMUFLAGS_TLBFLUSH 0x10000000 #define KGSL_MMUFLAGS_PTUPDATE 0x20000000 +/* Macros to manage TLB flushing */ +#define GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS (sizeof(unsigned char) * 8) +#define GSL_TLBFLUSH_FILTER_GET(superpte) \ + (*((unsigned char *) \ + (((unsigned int)mmu->tlbflushfilter.base) \ + + (superpte / GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS)))) +#define GSL_TLBFLUSH_FILTER_SETDIRTY(superpte) \ + (GSL_TLBFLUSH_FILTER_GET((superpte)) |= 1 << \ + (superpte % GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS)) +#define GSL_TLBFLUSH_FILTER_ISDIRTY(superpte) \ + (GSL_TLBFLUSH_FILTER_GET((superpte)) & \ + (1 << (superpte % GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS))) +#define GSL_TLBFLUSH_FILTER_RESET() memset(mmu->tlbflushfilter.base,\ + 0, mmu->tlbflushfilter.size) + extern unsigned int kgsl_cache_enable; struct kgsl_device; @@ -68,6 +83,11 @@ struct kgsl_pagetable { struct gen_pool *pool; }; +struct kgsl_tlbflushfilter { + unsigned int *base; + unsigned int size; +}; + struct kgsl_mmu { unsigned int refcnt; uint32_t flags; @@ -81,6 +101,8 @@ struct kgsl_mmu { /* current page table object being used by device mmu */ struct kgsl_pagetable *defaultpagetable; struct kgsl_pagetable *hwpagetable; + /* Maintain filter to manage tlb flushing */ + struct kgsl_tlbflushfilter tlbflushfilter; }; From 4f1d29020452a08370ddaefe1dfa332c500fb55e Mon Sep 17 00:00:00 2001 From: Wei Zou Date: Fri, 5 Nov 2010 15:46:11 -0700 Subject: [PATCH 0968/2556] msm: kgsl: Fix a potential subtraction overflow issue CRs-fixed: 262785 Change-Id: Ic0de06f59fe4b37056d89e3672a8320ec814a418 Signed-off-by: Wei Zou --- drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c index 3e5262e8f00fa..a057c8c94079b 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c @@ -175,7 +175,8 @@ unsigned int uint2float(unsigned int uintval) } /* Calculate fraction */ - frac = (uintval & (~(1 << exp))) << (23 - exp); + if (23 >= exp) + frac = (uintval & (~(1 << exp))) << (23 - exp); /* Exp is biased by 127 and shifted 23 bits */ exp = (exp + 127) << 23; From dfc75d089ae1284340be16614fbc7879a8fa1572 Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Tue, 2 Mar 2010 21:52:47 -0700 Subject: [PATCH 0969/2556] msm_kgsl: Reuse vmalloced memory buffers Memory buffers that are vmalloced and can be freed are put into a free list and reused later if an allocation of same size is required. Change-Id: Ia7387ee8235cc192d20646cfef2510a9820d464b Signed-off-by: Shubhraprakash Das --- drivers/video/msm/gpu/kgsl/kgsl.c | 216 ++++++++++++-------- drivers/video/msm/gpu/kgsl/kgsl.h | 10 +- drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c | 10 +- 3 files changed, 144 insertions(+), 92 deletions(-) diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c index edf3b90b282df..884eea74c5599 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.c +++ b/drivers/video/msm/gpu/kgsl/kgsl.c @@ -1,17 +1,17 @@ /* * Copyright (c) 2008-2009 QUALCOMM USA, INC. -* +* * All source code in this file is licensed under the following license -* +* * 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, you can find it at http://www.fsf.org */ @@ -41,12 +41,17 @@ #include "kgsl_cmdstream.h" #include "kgsl_log.h" +#define KGSL_MAX_PRESERVED_BUFFERS 10 +#define KGSL_MAX_SIZE_OF_PRESERVED_BUFFER 0x10000 + struct kgsl_file_private { struct list_head list; struct list_head mem_list; uint32_t ctxt_id_mask; struct kgsl_pagetable *pagetable; unsigned long vmalloc_size; + struct list_head preserve_entry_list; + int preserve_list_size; }; static void kgsl_put_phys_file(struct file *file); @@ -86,7 +91,7 @@ static long flush_l1_cache_all(struct kgsl_file_private *private) list_for_each_entry(entry, &private->mem_list, list) { if (KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH & entry->memdesc.priv) { result = - flush_l1_cache_range((unsigned long)entry-> + flush_l1_cache_range((unsigned long)entry-> memdesc.hostptr, entry->memdesc.size); if (result) @@ -244,7 +249,13 @@ static int kgsl_release(struct inode *inodep, struct file *filep) kgsl_drawctxt_destroy(&kgsl_driver.yamato_device, i); list_for_each_entry_safe(entry, entry_tmp, &private->mem_list, list) - kgsl_remove_mem_entry(entry); + kgsl_remove_mem_entry(entry, false); + + entry = NULL; + entry_tmp = NULL; + list_for_each_entry_safe(entry, entry_tmp, + &private->preserve_entry_list, list) + kgsl_remove_mem_entry(entry, false); if (private->pagetable != NULL) { kgsl_yamato_cleanup_pt(&kgsl_driver.yamato_device, @@ -290,6 +301,8 @@ static int kgsl_open(struct inode *inodep, struct file *filep) private->ctxt_id_mask = 0; INIT_LIST_HEAD(&private->mem_list); + INIT_LIST_HEAD(&private->preserve_entry_list); + private->preserve_list_size = 0; filep->private_data = private; @@ -356,7 +369,7 @@ kgsl_sharedmem_find_region(struct kgsl_file_private *private, list_for_each_entry(entry, &private->mem_list, list) { if (gpuaddr >= entry->memdesc.gpuaddr && - ((gpuaddr + size) <= + ((gpuaddr + size) <= (entry->memdesc.gpuaddr + entry->memdesc.size))) { result = entry; break; @@ -386,7 +399,7 @@ static long kgsl_ioctl_device_getproperty(struct kgsl_file_private *private, } static long kgsl_ioctl_device_regread(struct kgsl_file_private *private, - void __user *arg) + void __user *arg) { int result = 0; struct kgsl_device_regread param; @@ -396,7 +409,7 @@ static long kgsl_ioctl_device_regread(struct kgsl_file_private *private, goto done; } result = kgsl_yamato_regread(&kgsl_driver.yamato_device, - param.offsetwords, ¶m.value); + param.offsetwords, ¶m.value); if (result != 0) goto done; @@ -410,7 +423,7 @@ static long kgsl_ioctl_device_regread(struct kgsl_file_private *private, static long kgsl_ioctl_device_waittimestamp(struct kgsl_file_private *private, - void __user *arg) + void __user *arg) { int result = 0; struct kgsl_device_waittimestamp param; @@ -424,8 +437,8 @@ static long kgsl_ioctl_device_waittimestamp(struct kgsl_file_private *private, if (param.timeout == -1) param.timeout = 10 * MSEC_PER_SEC; result = kgsl_yamato_waittimestamp(&kgsl_driver.yamato_device, - param.timestamp, - param.timeout); + param.timestamp, + param.timeout); kgsl_yamato_runpending(&kgsl_driver.yamato_device); done: @@ -433,7 +446,7 @@ static long kgsl_ioctl_device_waittimestamp(struct kgsl_file_private *private, } static long kgsl_ioctl_rb_issueibcmds(struct kgsl_file_private *private, - void __user *arg) + void __user *arg) { int result = 0; struct kgsl_ringbuffer_issueibcmds param; @@ -447,7 +460,7 @@ static long kgsl_ioctl_rb_issueibcmds(struct kgsl_file_private *private, || (private->ctxt_id_mask & 1 << param.drawctxt_id) == 0) { result = -EINVAL; KGSL_DRV_ERR("invalid drawctxt drawctxt_id %d\n", - param.drawctxt_id); + param.drawctxt_id); result = -EINVAL; goto done; } @@ -455,18 +468,18 @@ static long kgsl_ioctl_rb_issueibcmds(struct kgsl_file_private *private, if (kgsl_sharedmem_find_region(private, param.ibaddr, param.sizedwords*sizeof(uint32_t)) == NULL) { KGSL_DRV_ERR("invalid cmd buffer ibaddr %08x sizedwords %d\n", - param.ibaddr, param.sizedwords); + param.ibaddr, param.sizedwords); result = -EINVAL; goto done; } result = kgsl_ringbuffer_issueibcmds(&kgsl_driver.yamato_device, - param.drawctxt_id, - param.ibaddr, - param.sizedwords, - ¶m.timestamp, - param.flags); + param.drawctxt_id, + param.ibaddr, + param.sizedwords, + ¶m.timestamp, + param.flags); if (result != 0) goto done; @@ -537,7 +550,7 @@ static long kgsl_ioctl_cmdstream_freememontimestamp(struct kgsl_file_private } static long kgsl_ioctl_drawctxt_create(struct kgsl_file_private *private, - void __user *arg) + void __user *arg) { int result = 0; struct kgsl_drawctxt_create param; @@ -566,7 +579,7 @@ static long kgsl_ioctl_drawctxt_create(struct kgsl_file_private *private, } static long kgsl_ioctl_drawctxt_destroy(struct kgsl_file_private *private, - void __user *arg) + void __user *arg) { int result = 0; struct kgsl_drawctxt_destroy param; @@ -591,11 +604,31 @@ static long kgsl_ioctl_drawctxt_destroy(struct kgsl_file_private *private, return result; } -void kgsl_remove_mem_entry(struct kgsl_mem_entry *entry) +void kgsl_remove_mem_entry(struct kgsl_mem_entry *entry, bool preserve) { + /* If allocation is vmalloc and preserve is requested then save + * the allocation in a free list to be used later instead of + * freeing it here */ + if (KGSL_MEMFLAGS_VMALLOC_MEM & entry->memdesc.priv && + preserve && + entry->priv->preserve_list_size < KGSL_MAX_PRESERVED_BUFFERS && + entry->memdesc.size <= KGSL_MAX_SIZE_OF_PRESERVED_BUFFER) { + if (entry->free_list.prev) { + list_del(&entry->free_list); + entry->free_list.prev = NULL; + } + if (entry->list.prev) { + list_del(&entry->list); + entry->list.prev = NULL; + } + list_add(&entry->list, &entry->priv->preserve_entry_list); + entry->priv->preserve_list_size++; + return; + } + kgsl_mmu_unmap(entry->memdesc.pagetable, - entry->memdesc.gpuaddr & KGSL_PAGEMASK, - entry->memdesc.size); + entry->memdesc.gpuaddr & KGSL_PAGEMASK, + entry->memdesc.size); if (KGSL_MEMFLAGS_VMALLOC_MEM & entry->memdesc.priv) { vfree((void *)entry->memdesc.physaddr); entry->priv->vmalloc_size -= entry->memdesc.size; @@ -611,7 +644,7 @@ void kgsl_remove_mem_entry(struct kgsl_mem_entry *entry) } static long kgsl_ioctl_sharedmem_free(struct kgsl_file_private *private, - void __user *arg) + void __user *arg) { int result = 0; struct kgsl_sharedmem_free param; @@ -629,18 +662,18 @@ static long kgsl_ioctl_sharedmem_free(struct kgsl_file_private *private, goto done; } - kgsl_remove_mem_entry(entry); + kgsl_remove_mem_entry(entry, false); done: return result; } #ifdef CONFIG_MSM_KGSL_MMU static int kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_file_private *private, - void __user *arg) + void __user *arg) { - int result = 0, len; + int result = 0, len, found = 0; struct kgsl_sharedmem_from_vmalloc param; - struct kgsl_mem_entry *entry = NULL; + struct kgsl_mem_entry *entry = NULL, *entry_tmp = NULL; void *vmalloc_area; struct vm_area_struct *vma; @@ -651,8 +684,8 @@ static int kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_file_private *private, if (!param.hostptr) { KGSL_DRV_ERR - ("Invalid host pointer of malloc passed: param.hostptr " - "%08x\n", param.hostptr); + ("Invalid host pointer of malloc passed: param.hostptr " + "%08x\n", param.hostptr); result = -EINVAL; goto error; } @@ -660,13 +693,13 @@ static int kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_file_private *private, vma = find_vma(current->mm, param.hostptr); if (!vma) { KGSL_MEM_ERR("Could not find vma for address %x\n", - param.hostptr); + param.hostptr); result = -EINVAL; goto error; } len = vma->vm_end - vma->vm_start; if (vma->vm_pgoff || !IS_ALIGNED(len, PAGE_SIZE) - || !IS_ALIGNED(vma->vm_start, PAGE_SIZE)) { + || !IS_ALIGNED(vma->vm_start, PAGE_SIZE)) { KGSL_MEM_ERR ("kgsl vmalloc mapping must be at offset 0 and page aligned\n"); result = -EINVAL; @@ -674,35 +707,69 @@ static int kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_file_private *private, } if (vma->vm_start != param.hostptr) { KGSL_MEM_ERR - ("vma start address is not equal to mmap address\n"); + ("vma start address is not equal to mmap address\n"); result = -EINVAL; goto error; } if ((private->vmalloc_size + len) > KGSL_GRAPHICS_MEMORY_LOW_WATERMARK - && !param.force_no_low_watermark) { + && !param.force_no_low_watermark) { result = -ENOMEM; goto error; } - entry = kzalloc(sizeof(struct kgsl_mem_entry), GFP_KERNEL); - if (entry == NULL) { - result = -ENOMEM; - goto error; + list_for_each_entry_safe(entry, entry_tmp, + &private->preserve_entry_list, list) { + if (entry->memdesc.size == len) { + list_del(&entry->list); + found = 1; + break; + } } + if (!found) { + entry = kzalloc(sizeof(struct kgsl_mem_entry), GFP_KERNEL); + if (entry == NULL) { + result = -ENOMEM; + goto error; + } + + /* allocate memory and map it to user space */ + vmalloc_area = vmalloc_user(len); + if (!vmalloc_area) { + KGSL_MEM_ERR("vmalloc failed\n"); + result = -ENOMEM; + goto error_free_entry; + } + dmac_flush_range(vmalloc_area, vmalloc_area + len); + + result = + kgsl_mmu_map(private->pagetable, + (unsigned long)vmalloc_area, len, + GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, + &entry->memdesc.gpuaddr, KGSL_MEMFLAGS_ALIGN4K); + if (result != 0) + goto error_free_vmalloc; + + entry->memdesc.pagetable = private->pagetable; + entry->memdesc.size = len; + entry->memdesc.priv = KGSL_MEMFLAGS_VMALLOC_MEM | + KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH; + entry->memdesc.physaddr = (unsigned long)vmalloc_area; + entry->priv = private; + private->vmalloc_size += len; + + } else { + KGSL_MEM_INFO("Reusing memory entry: %x, size: %x\n", + (unsigned int)entry, entry->memdesc.size); + entry->priv->preserve_list_size--; + vmalloc_area = (void *)entry->memdesc.physaddr; + } - /* allocate memory and map it to user space */ - vmalloc_area = vmalloc_user(len); - if (!vmalloc_area) { - KGSL_MEM_ERR("vmalloc failed\n"); - result = -ENOMEM; - goto error_free_entry; - } if (!kgsl_cache_enable) { /* If we are going to map non-cached, make sure to flush the * cache to ensure that previously cached data does not * overwrite this memory */ - dmac_flush_range(vmalloc_area, vmalloc_area + len); +// dmac_flush_range(vmalloc_area, vmalloc_area + len); KGSL_MEM_INFO("Caching for memory allocation turned off\n"); vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); } else { @@ -712,24 +779,10 @@ static int kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_file_private *private, result = remap_vmalloc_range(vma, vmalloc_area, 0); if (result) { KGSL_MEM_ERR("remap_vmalloc_range returned %d\n", result); - goto error_free_vmalloc; + goto error_unmap_entry; } - result = - kgsl_mmu_map(private->pagetable, (unsigned long)vmalloc_area, len, - GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, - &entry->memdesc.gpuaddr, KGSL_MEMFLAGS_ALIGN4K); - - if (result != 0) - goto error_free_vmalloc; - - entry->memdesc.pagetable = private->pagetable; - entry->memdesc.size = len; entry->memdesc.hostptr = (void *)param.hostptr; - entry->memdesc.priv = KGSL_MEMFLAGS_VMALLOC_MEM | - KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH; - entry->memdesc.physaddr = (unsigned long)vmalloc_area; - entry->priv = private; param.gpuaddr = entry->memdesc.gpuaddr; @@ -737,14 +790,13 @@ static int kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_file_private *private, result = -EFAULT; goto error_unmap_entry; } - private->vmalloc_size += len; list_add(&entry->list, &private->mem_list); return 0; error_unmap_entry: kgsl_mmu_unmap(private->pagetable, entry->memdesc.gpuaddr, - entry->memdesc.size); + entry->memdesc.size); error_free_vmalloc: vfree(vmalloc_area); @@ -764,7 +816,7 @@ static inline int kgsl_ioctl_sharedmem_from_vmalloc( #endif static int kgsl_get_phys_file(int fd, unsigned long *start, unsigned long *len, - struct file **filep) + struct file **filep) { struct file *fbfile; int put_needed; @@ -820,13 +872,13 @@ static int kgsl_ioctl_sharedmem_from_pmem(struct kgsl_file_private *private, goto error; } else if (param.offset + param.len > len) { KGSL_DRV_ERR("%s: region too large 0x%x + 0x%x >= 0x%lx\n", - __func__, param.offset, param.len, len); + __func__, param.offset, param.len, len); result = -EINVAL; goto error_put_pmem; } KGSL_MEM_INFO("get phys file %p start 0x%lx len 0x%lx\n", - pmem_file, start, len); + pmem_file, start, len); KGSL_DRV_DBG("locked phys file %p\n", pmem_file); entry = kzalloc(sizeof(*entry), GFP_KERNEL); @@ -868,8 +920,8 @@ static int kgsl_ioctl_sharedmem_from_pmem(struct kgsl_file_private *private, error_unmap_entry: kgsl_mmu_unmap(entry->memdesc.pagetable, - entry->memdesc.gpuaddr & KGSL_PAGEMASK, - entry->memdesc.size); + entry->memdesc.gpuaddr & KGSL_PAGEMASK, + entry->memdesc.size); error_free_entry: kfree(entry); @@ -884,7 +936,7 @@ static int kgsl_ioctl_sharedmem_from_pmem(struct kgsl_file_private *private, /*This function flushes a graphics memory allocation from CPU cache *when caching is enabled with MMU*/ static int kgsl_ioctl_sharedmem_flush_cache(struct kgsl_file_private *private, - void __user *arg) + void __user *arg) { int result = 0; struct kgsl_mem_entry *entry; @@ -902,7 +954,7 @@ static int kgsl_ioctl_sharedmem_flush_cache(struct kgsl_file_private *private, goto done; } result = flush_l1_cache_range((unsigned long)entry->memdesc.hostptr, - entry->memdesc.size); + entry->memdesc.size); /* Mark memory as being flushed so we don't flush it again */ entry->memdesc.priv &= ~KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH; done: @@ -910,7 +962,7 @@ static int kgsl_ioctl_sharedmem_flush_cache(struct kgsl_file_private *private, } #else static int kgsl_ioctl_sharedmem_flush_cache(struct kgsl_file_private *private, - void __user *arg) + void __user *arg) { return -ENOSYS; } @@ -935,7 +987,7 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) case IOCTL_KGSL_DEVICE_GETPROPERTY: result = - kgsl_ioctl_device_getproperty(private, (void __user *)arg); + kgsl_ioctl_device_getproperty(private, (void __user *)arg); break; case IOCTL_KGSL_DEVICE_REGREAD: @@ -955,14 +1007,14 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) case IOCTL_KGSL_CMDSTREAM_READTIMESTAMP: result = - kgsl_ioctl_cmdstream_readtimestamp(private, + kgsl_ioctl_cmdstream_readtimestamp(private, (void __user *)arg); break; case IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP: result = - kgsl_ioctl_cmdstream_freememontimestamp(private, - (void __user *)arg); + kgsl_ioctl_cmdstream_freememontimestamp(private, + (void __user *)arg); break; case IOCTL_KGSL_DRAWCTXT_CREATE: @@ -972,7 +1024,7 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) case IOCTL_KGSL_DRAWCTXT_DESTROY: result = - kgsl_ioctl_drawctxt_destroy(private, (void __user *)arg); + kgsl_ioctl_drawctxt_destroy(private, (void __user *)arg); break; case IOCTL_KGSL_SHAREDMEM_FREE: @@ -988,7 +1040,7 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) case IOCTL_KGSL_SHAREDMEM_FLUSH_CACHE: if (kgsl_cache_enable) result = kgsl_ioctl_sharedmem_flush_cache(private, - (void __user *)arg); + (void __user *)arg); break; case IOCTL_KGSL_SHAREDMEM_FROM_PMEM: kgsl_yamato_runpending(&kgsl_driver.yamato_device); @@ -1011,7 +1063,7 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) } else { result = -EINVAL; KGSL_DRV_ERR("invalid drawctxt drawctxt_id %d\n", - binbase.drawctxt_id); + binbase.drawctxt_id); } break; @@ -1136,7 +1188,7 @@ static int __devinit kgsl_platform_probe(struct platform_device *pdev) kgsl_driver.interrupt_num = platform_get_irq(pdev, 0); if (kgsl_driver.interrupt_num <= 0) { KGSL_DRV_ERR("platform_get_irq() returned %d\n", - kgsl_driver.interrupt_num); + kgsl_driver.interrupt_num); result = -EINVAL; goto done; } @@ -1145,7 +1197,7 @@ static int __devinit kgsl_platform_probe(struct platform_device *pdev) IRQF_TRIGGER_HIGH, DRIVER_NAME, NULL); if (result) { KGSL_DRV_ERR("request_irq(%d) returned %d\n", - kgsl_driver.interrupt_num, result); + kgsl_driver.interrupt_num, result); goto done; } kgsl_driver.have_irq = 1; diff --git a/drivers/video/msm/gpu/kgsl/kgsl.h b/drivers/video/msm/gpu/kgsl/kgsl.h index e9f0d7ccb0679..dece225fd9d28 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl.h +++ b/drivers/video/msm/gpu/kgsl/kgsl.h @@ -1,17 +1,17 @@ /* * Copyright (c) 2008-2009 QUALCOMM USA, INC. -* +* * All source code in this file is licensed under the following license -* +* * 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, you can find it at http://www.fsf.org */ @@ -79,6 +79,6 @@ struct kgsl_mem_entry { struct kgsl_file_private *priv; }; -void kgsl_remove_mem_entry(struct kgsl_mem_entry *entry); +void kgsl_remove_mem_entry(struct kgsl_mem_entry *entry, bool preserve); #endif /* _GSL_DRIVER_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c index 9b7183cbd596c..a6ff8d9d73c66 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c @@ -1,17 +1,17 @@ /* * Copyright (c) 2008-2009 QUALCOMM USA, INC. -* +* * All source code in this file is licensed under the following license -* +* * 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, you can find it at http://www.fsf.org */ @@ -81,7 +81,7 @@ void kgsl_cmdstream_memqueue_drain(struct kgsl_device *device) KGSL_MEM_DBG("ts_processed %d ts_free %d gpuaddr %x)\n", ts_processed, entry->free_timestamp, entry->memdesc.gpuaddr); - kgsl_remove_mem_entry(entry); + kgsl_remove_mem_entry(entry, true); } } From 4f70c6d0beb3b5ac9dc057c11dd4f42ea3e3b222 Mon Sep 17 00:00:00 2001 From: Ashwin Chaugule Date: Thu, 21 Oct 2010 10:56:33 -0400 Subject: [PATCH 0970/2556] Optimized ARM RWSEM algorithm RWSEM implementation for ARM using atomic functions. Heavily based on arch/sh/include/asm/rwsem.h Signed-off-by: Ashwin Chaugule --- arch/arm/Kconfig | 3 +- arch/arm/include/asm/rwsem.h | 185 +++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 arch/arm/include/asm/rwsem.h diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index c979ec6c326ff..c8ab1ac752775 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -131,10 +131,9 @@ config GENERIC_LOCKBREAK config RWSEM_GENERIC_SPINLOCK bool - default y config RWSEM_XCHGADD_ALGORITHM - bool + def_bool y config ARCH_HAS_ILOG2_U32 bool diff --git a/arch/arm/include/asm/rwsem.h b/arch/arm/include/asm/rwsem.h new file mode 100644 index 0000000000000..694cf4005afc5 --- /dev/null +++ b/arch/arm/include/asm/rwsem.h @@ -0,0 +1,185 @@ +/* rwsem.h: R/W semaphores implemented using ARM atomic functions. + * + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef _ASM_ARM_RWSEM_H +#define _ASM_ARM_RWSEM_H + +#ifndef _LINUX_RWSEM_H +#error "please don't include asm/rwsem.h directly, use linux/rwsem.h instead" +#endif + +#ifdef __KERNEL__ +#include +#include +#include +#include + +/* + * the semaphore definition + */ +struct rw_semaphore { + long count; +#define RWSEM_UNLOCKED_VALUE 0x00000000 +#define RWSEM_ACTIVE_BIAS 0x00000001 +#define RWSEM_ACTIVE_MASK 0x0000ffff +#define RWSEM_WAITING_BIAS (-0x00010000) +#define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS +#define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) + spinlock_t wait_lock; + struct list_head wait_list; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; +#endif +}; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname } +#else +# define __RWSEM_DEP_MAP_INIT(lockname) +#endif + +#define __RWSEM_INITIALIZER(name) \ + { RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED((name).wait_lock), \ + LIST_HEAD_INIT((name).wait_list) \ + __RWSEM_DEP_MAP_INIT(name) } + +#define DECLARE_RWSEM(name) \ + struct rw_semaphore name = __RWSEM_INITIALIZER(name) + +extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem); +extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem); +extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem); +extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem); + +extern void __init_rwsem(struct rw_semaphore *sem, const char *name, + struct lock_class_key *key); + +#define init_rwsem(sem) \ +do { \ + static struct lock_class_key __key; \ + \ + __init_rwsem((sem), #sem, &__key); \ +} while (0) + +/* + * lock for reading + */ +static inline void __down_read(struct rw_semaphore *sem) +{ + if (atomic_inc_return((atomic_t *)(&sem->count)) < 0) + rwsem_down_read_failed(sem); +} + +static inline int __down_read_trylock(struct rw_semaphore *sem) +{ + int tmp; + + while ((tmp = sem->count) >= 0) { + if (tmp == cmpxchg(&sem->count, tmp, + tmp + RWSEM_ACTIVE_READ_BIAS)) { + return 1; + } + } + return 0; +} + +/* + * lock for writing + */ +static inline void __down_write(struct rw_semaphore *sem) +{ + int tmp; + + tmp = atomic_add_return(RWSEM_ACTIVE_WRITE_BIAS, + (atomic_t *)(&sem->count)); + if (tmp != RWSEM_ACTIVE_WRITE_BIAS) + rwsem_down_write_failed(sem); +} + +static inline int __down_write_trylock(struct rw_semaphore *sem) +{ + int tmp; + + tmp = cmpxchg(&sem->count, RWSEM_UNLOCKED_VALUE, + RWSEM_ACTIVE_WRITE_BIAS); + return tmp == RWSEM_UNLOCKED_VALUE; +} + +/* + * unlock after reading + */ +static inline void __up_read(struct rw_semaphore *sem) +{ + int tmp; + + tmp = atomic_dec_return((atomic_t *)(&sem->count)); + if (tmp < -1 && (tmp & RWSEM_ACTIVE_MASK) == 0) + rwsem_wake(sem); +} + +/* + * unlock after writing + */ +static inline void __up_write(struct rw_semaphore *sem) +{ + if (atomic_sub_return(RWSEM_ACTIVE_WRITE_BIAS, + (atomic_t *)(&sem->count)) < 0) + rwsem_wake(sem); +} + +/* + * implement atomic add functionality + */ +static inline void rwsem_atomic_add(int delta, struct rw_semaphore *sem) +{ + atomic_add(delta, (atomic_t *)(&sem->count)); +} + +/* + * downgrade write lock to read lock + */ +static inline void __downgrade_write(struct rw_semaphore *sem) +{ + int tmp; + + tmp = atomic_add_return(-RWSEM_WAITING_BIAS, (atomic_t *)(&sem->count)); + if (tmp < 0) + rwsem_downgrade_wake(sem); +} + +static inline void __down_write_nested(struct rw_semaphore *sem, int subclass) +{ + __down_write(sem); +} + +/* + * implement exchange and add functionality + */ +static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem) +{ + return atomic_add_return(delta, (atomic_t *)(&sem->count)); +} + +static inline int rwsem_is_locked(struct rw_semaphore *sem) +{ + return (sem->count != 0); +} + +#endif /* __KERNEL__ */ +#endif /* _ASM_ARM_RWSEM_H */ From a0d680c391963df4b2add9a68410655dc167164d Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Mon, 18 Oct 2010 04:21:29 -0400 Subject: [PATCH 0971/2556] msm: Support for configurable ondemand frequency range Change-Id: I1a13b16929b92873589ab6e02054b869cf78c9df --- arch/arm/Kconfig | 8 ++++++++ arch/arm/mach-msm/Kconfig | 38 +++++++++++++++++++------------------ arch/arm/mach-msm/cpufreq.c | 28 ++++----------------------- 3 files changed, 32 insertions(+), 42 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index c8ab1ac752775..1d26df86a6059 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1840,6 +1840,14 @@ endif source "drivers/cpuidle/Kconfig" +config CPU_FREQ_MSM + bool + depends on CPU_FREQ && ARCH_MSM + default y + help + This enables the CPUFreq driver for Qualcomm CPUs. + If in doubt, say Y. + endmenu menu "Floating point emulation" diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 9088f294a3e09..5d3b1b1c0eea0 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -504,24 +504,26 @@ config MSM_RPCSERVERS help none -config MSM_CPU_FREQ_SCREEN - bool - default n - depends on HAS_EARLYSUSPEND - help - Simple cpufreq scaling based on screen ON/OFF. - -if MSM_CPU_FREQ_SCREEN - -config MSM_CPU_FREQ_SCREEN_OFF - int "Screen off cpu frequency" - default 245760 - -config MSM_CPU_FREQ_SCREEN_ON - int "Screen on cpu frequency" - default 384000 - -endif # MSM_CPU_FREQ_SCREEN +if CPU_FREQ_MSM + +config MSM_CPU_FREQ_SET_MIN_MAX + bool "Set Min/Max CPU frequencies." + default n + help + Allow setting min and max CPU frequencies. Sysfs can be used + to override these values. + +config MSM_CPU_FREQ_MAX + int "Max CPU Frequency" + depends on MSM_CPU_FREQ_SET_MIN_MAX + default 384000 + +config MSM_CPU_FREQ_MIN + int "Min CPU Frequency" + depends on MSM_CPU_FREQ_SET_MIN_MAX + default 245760 + +endif # CPU_FREQ_MSM config MSM_HW3D tristate "MSM Hardware 3D Register Driver" diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c index aaa30bb3df778..48b6630b05091 100644 --- a/arch/arm/mach-msm/cpufreq.c +++ b/arch/arm/mach-msm/cpufreq.c @@ -22,29 +22,6 @@ #include #include "acpuclock.h" -#ifdef CONFIG_MSM_CPU_FREQ_SCREEN -static void msm_early_suspend(struct early_suspend *handler) { - acpuclk_set_rate(CONFIG_MSM_CPU_FREQ_SCREEN_OFF * 1000, 0); -} - -static void msm_late_resume(struct early_suspend *handler) { - acpuclk_set_rate(CONFIG_MSM_CPU_FREQ_SCREEN_ON * 1000, 0); -} - -static struct early_suspend msm_power_suspend = { - .suspend = msm_early_suspend, - .resume = msm_late_resume, -}; - -static int __init clock_late_init(void) -{ - register_early_suspend(&msm_power_suspend); - return 0; -} - -late_initcall(clock_late_init); -#else - static int msm_cpufreq_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) @@ -90,6 +67,10 @@ static int msm_cpufreq_init(struct cpufreq_policy *policy) BUG_ON(cpufreq_frequency_table_cpuinfo(policy, table)); policy->cur = acpuclk_get_rate(); +#ifdef CONFIG_MSM_CPU_FREQ_SET_MIN_MAX + policy->min = CONFIG_MSM_CPU_FREQ_MIN; + policy->max = CONFIG_MSM_CPU_FREQ_MAX; +#endif policy->cpuinfo.transition_latency = acpuclk_get_switch_time() * NSEC_PER_USEC; return 0; @@ -116,4 +97,3 @@ static int __init msm_cpufreq_register(void) } device_initcall(msm_cpufreq_register); -#endif From 4dcb6c22ded662d682718f20ecdab1681a42e93a Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 2 Nov 2010 01:15:28 -0400 Subject: [PATCH 0972/2556] msm: Add the generic MSM flashlight driver Includes DEATH_RAY function. Change-Id: I9d1b1231b7e21884bdb8d8b33263e162bc345fe0 --- arch/arm/mach-msm/Kconfig | 6 + arch/arm/mach-msm/Makefile | 3 + .../mach-msm/include/mach/msm_flashlight.h | 55 ++ arch/arm/mach-msm/msm_flashlight.c | 490 ++++++++++++++++++ 4 files changed, 554 insertions(+) create mode 100644 arch/arm/mach-msm/include/mach/msm_flashlight.h create mode 100644 arch/arm/mach-msm/msm_flashlight.c diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 5d3b1b1c0eea0..e2a22eca02400 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -581,6 +581,12 @@ config WIFI_MEM_PREALLOC help Preallocates memory buffers for WiFi driver +config ARCH_MSM_FLASHLIGHT + bool "Flashlight Driver" + depends on ARCH_MSM + help + The flashlight driver is for MSM series. + config VIRTUAL_KPANIC_PARTITION bool "Create virtual kpanic partition" default n diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index bca50dececbec..38f7209d56442 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -103,3 +103,6 @@ obj-$(CONFIG_HTC_35MM_JACK) += htc_35mm_jack.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-tpa2018d1.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-smb329.o + +obj-$(CONFIG_ARCH_MSM_FLASHLIGHT) += msm_flashlight.o + diff --git a/arch/arm/mach-msm/include/mach/msm_flashlight.h b/arch/arm/mach-msm/include/mach/msm_flashlight.h new file mode 100644 index 0000000000000..bababa09c4c8c --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_flashlight.h @@ -0,0 +1,55 @@ +/* + * arch/arm/mach-msm/include/mach/msm_flashlight.h - The flashlight header + * Copyright (C) 2009 HTC Corporation + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ASM_ARCH_MSM8X50_FLASHLIGHT_H +#define __ASM_ARCH_MSM8X50_FLASHLIGHT_H +#include + +#define FLASHLIGHT_NAME "flashlight" + +#define FLASHLIGHT_OFF 0 +#define FLASHLIGHT_TORCH 1 +#define FLASHLIGHT_FLASH 2 +#define FLASHLIGHT_NUM 3 + +enum flashlight_mode_flags { + FL_MODE_OFF = 0, + FL_MODE_TORCH, + FL_MODE_FLASH, + FL_MODE_PRE_FLASH, + FL_MODE_TORCH_LED_A, + FL_MODE_TORCH_LED_B, + FL_MODE_TORCH_LEVEL_1, + FL_MODE_TORCH_LEVEL_2, + FL_MODE_DEATH_RAY, +}; + +struct flashlight_platform_data { + void (*gpio_init) (void); + uint32_t torch; + uint32_t flash; + uint32_t flash_adj; + uint32_t flash_duration_ms; + uint8_t led_count; /* 0: 1 LED, 1: 2 LED */ +}; + +int flashlight_control(int mode); + +#undef __ASM_ARCH_MSM8X50_FLASHLIGHT_H +#endif diff --git a/arch/arm/mach-msm/msm_flashlight.c b/arch/arm/mach-msm/msm_flashlight.c new file mode 100644 index 0000000000000..4b6e2cce2b972 --- /dev/null +++ b/arch/arm/mach-msm/msm_flashlight.c @@ -0,0 +1,490 @@ +/* + * arch/arm/mach-msm/msm_flashlight.c - The flashlight driver + * Copyright (C) 2009 HTC Corporation + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +/*#include */ +#include +#include + +struct flashlight_struct { + struct led_classdev fl_lcdev; + struct early_suspend early_suspend_flashlight; + struct hrtimer timer; + struct wake_lock wake_lock; + spinlock_t spin_lock; + uint32_t gpio_torch; + uint32_t gpio_flash; + uint32_t gpio_flash_adj; + uint32_t flash_sw_timeout_ms; + enum flashlight_mode_flags mode_status; + unsigned long spinlock_flags; + unsigned flash_adj_gpio_status; + /* inactive: 0x0 + * active: 0x1 + * force disable flashlight function: 0x2 */ + uint8_t flash_adj_value; + uint8_t led_count; +}; + +/* disable it, we didn't need to adjust GPIO */ +/* #define FLASHLIGHT_ADJ_FUNC */ + +static struct flashlight_struct *this_fl_str; + +static void flashlight_hw_command(uint8_t addr, uint8_t data) +{ + uint8_t loop_i, loop_j; + const uint8_t fl_addr_to_rising_count[4] = { 17, 18, 19, 20 }; + uint8_t loop_tmp; + if (!this_fl_str->gpio_torch && !this_fl_str->gpio_torch) { + printk(KERN_ERR "%s: not setup GPIO??? torch: %d, flash: %d\n", + __func__, this_fl_str->gpio_torch, + this_fl_str->gpio_flash); + return; + } + for (loop_j = 0; loop_j < 2; loop_j++) { + if (!loop_j) + loop_tmp = fl_addr_to_rising_count[addr]; + else + loop_tmp = data; + for (loop_i = 0; loop_i < loop_tmp; loop_i++) { + gpio_direction_output(this_fl_str->gpio_torch, 0); + udelay(2); + gpio_direction_output(this_fl_str->gpio_torch, 1); + udelay(2); + } + udelay(500); + } +} + +static void flashlight_turn_off(void) +{ + gpio_direction_output(this_fl_str->gpio_flash, 0); + gpio_direction_output(this_fl_str->gpio_torch, 0); + this_fl_str->mode_status = FL_MODE_OFF; + this_fl_str->fl_lcdev.brightness = LED_OFF; + wake_unlock(&this_fl_str->wake_lock); +} + +static enum hrtimer_restart flashlight_hrtimer_func(struct hrtimer *timer) +{ + struct flashlight_struct *fl_str = container_of(timer, + struct flashlight_struct, timer); + wake_unlock(&fl_str->wake_lock); + spin_lock_irqsave(&fl_str->spin_lock, fl_str->spinlock_flags); + flashlight_turn_off(); + spin_unlock_irqrestore(&fl_str->spin_lock, fl_str->spinlock_flags); + printk(KERN_INFO "%s: turn off flash mode\n", __func__); + return HRTIMER_NORESTART; +} + +int flashlight_control(int mode) +{ + int ret = 0; + uint32_t flash_ns = ktime_to_ns(ktime_get()); + +#if 0 /* disable flash_adj_value check now */ + if (this_fl_str->flash_adj_value == 2) { + printk(KERN_WARNING "%s: force disable function!\n", __func__); + return -EIO; + } +#endif + spin_lock_irqsave(&this_fl_str->spin_lock, + this_fl_str->spinlock_flags); + if (this_fl_str->mode_status == FL_MODE_FLASH) { + hrtimer_cancel(&this_fl_str->timer); + wake_unlock(&this_fl_str->wake_lock); + flashlight_turn_off(); + } + switch (mode) { + case FL_MODE_OFF: + flashlight_turn_off(); + break; + case FL_MODE_TORCH: + flashlight_hw_command(3, 1); + flashlight_hw_command(0, 15); + flashlight_hw_command(2, 4); + this_fl_str->mode_status = FL_MODE_TORCH; + this_fl_str->fl_lcdev.brightness = LED_HALF; + break; + case FL_MODE_TORCH_LED_A: + flashlight_hw_command(3, 1); + flashlight_hw_command(0, 15); + flashlight_hw_command(2, 3); + this_fl_str->mode_status = FL_MODE_TORCH_LED_A; + this_fl_str->fl_lcdev.brightness = 1; + break; + case FL_MODE_TORCH_LED_B: + flashlight_hw_command(3, 1); + flashlight_hw_command(0, 15); + flashlight_hw_command(2, 2); + this_fl_str->mode_status = FL_MODE_TORCH_LED_B; + this_fl_str->fl_lcdev.brightness = 2; + break; + case FL_MODE_FLASH: + flashlight_hw_command(2, 4); + gpio_direction_output(this_fl_str->gpio_flash, 1); + this_fl_str->mode_status = FL_MODE_FLASH; + this_fl_str->fl_lcdev.brightness = LED_FULL; + hrtimer_start(&this_fl_str->timer, + ktime_set(this_fl_str->flash_sw_timeout_ms / 1000, + (this_fl_str->flash_sw_timeout_ms % 1000) * + NSEC_PER_MSEC), HRTIMER_MODE_REL); + wake_lock(&this_fl_str->wake_lock); + break; + case FL_MODE_PRE_FLASH: + flashlight_hw_command(3, 1); + flashlight_hw_command(0, 9); + flashlight_hw_command(2, 4); + this_fl_str->mode_status = FL_MODE_PRE_FLASH; + this_fl_str->fl_lcdev.brightness = LED_HALF + 1; + break; + case FL_MODE_TORCH_LEVEL_1: + flashlight_hw_command(3, 8); + flashlight_hw_command(0, 15); + flashlight_hw_command(2, 4); + this_fl_str->mode_status = FL_MODE_TORCH_LEVEL_1; + this_fl_str->fl_lcdev.brightness = LED_HALF - 2; + break; + case FL_MODE_TORCH_LEVEL_2: + flashlight_hw_command(3, 4); + flashlight_hw_command(0, 15); + flashlight_hw_command(2, 4); + this_fl_str->mode_status = FL_MODE_TORCH_LEVEL_2; + this_fl_str->fl_lcdev.brightness = LED_HALF - 1; + break; + case FL_MODE_DEATH_RAY: + pr_info("%s: death ray\n", __func__); + hrtimer_cancel(&this_fl_str->timer); + gpio_direction_output(this_fl_str->gpio_flash, 0); + udelay(40); + gpio_direction_output(this_fl_str->gpio_flash, 1); + this_fl_str->mode_status = 0; + this_fl_str->fl_lcdev.brightness = 3; + wake_lock(&this_fl_str->wake_lock); + break; + default: + printk(KERN_ERR "%s: unknown flash_light flags: %d\n", + __func__, mode); + ret = -EINVAL; + break; + } + + printk(KERN_DEBUG "%s: mode: %d, %u\n", FLASHLIGHT_NAME, mode, + flash_ns/(1000*1000)); + + spin_unlock_irqrestore(&this_fl_str->spin_lock, + this_fl_str->spinlock_flags); + return ret; +} + +static void fl_lcdev_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct flashlight_struct *fl_str; + enum flashlight_mode_flags mode; + + fl_str = container_of(led_cdev, struct flashlight_struct, fl_lcdev); + if (brightness > 0 && brightness <= LED_HALF) { + /* Torch mode */ + if (brightness == (LED_HALF - 2)) + mode = FL_MODE_TORCH_LEVEL_1; + else if (brightness == (LED_HALF - 1)) + mode = FL_MODE_TORCH_LEVEL_2; + else if (brightness == 1 && fl_str->led_count) + mode = FL_MODE_TORCH_LED_A; + else if (brightness == 2 && fl_str->led_count) + mode = FL_MODE_TORCH_LED_B; + else if (brightness == 3) + mode = FL_MODE_DEATH_RAY; + else + mode = FL_MODE_TORCH; + } else if (brightness > LED_HALF && brightness <= LED_FULL) { + /* Flashlight mode */ + if (brightness == (LED_HALF + 1)) + mode = FL_MODE_PRE_FLASH; /* pre-flash mode */ + else + mode = FL_MODE_FLASH; + } else + /* off and else */ + mode = FL_MODE_OFF; + flashlight_control(mode); + + return; +} + +static void flashlight_early_suspend(struct early_suspend *handler) +{ + struct flashlight_struct *fl_str = container_of(handler, + struct flashlight_struct, early_suspend_flashlight); + if (fl_str != NULL && fl_str->mode_status) { + spin_lock_irqsave(&fl_str->spin_lock, fl_str->spinlock_flags); + flashlight_turn_off(); + spin_unlock_irqrestore(&fl_str->spin_lock, + fl_str->spinlock_flags); + } +} + +static void flashlight_late_resume(struct early_suspend *handler) +{ + /* + struct flashlight_struct *fl_str = container_of(handler, + struct flashlight_struct, early_suspend_flashlight); + */ +} + +static int flashlight_setup_gpio(struct flashlight_platform_data *flashlight, + struct flashlight_struct *fl_str) +{ + int ret = 0; + if (flashlight->gpio_init) + flashlight->gpio_init(); + if (flashlight->torch) { + ret = gpio_request(flashlight->torch, "fl_torch"); + if (ret < 0) { + printk(KERN_ERR "%s: gpio_request(torch) failed\n", + __func__); + return ret; + } + fl_str->gpio_torch = flashlight->torch; + } + + if (flashlight->flash) { + ret = gpio_request(flashlight->flash, "fl_flash"); + if (ret < 0) { + printk(KERN_ERR "%s: gpio_request(flash) failed\n", + __func__); + return ret; + } + fl_str->gpio_flash = flashlight->flash; + } + + if (flashlight->flash_adj) { + ret = gpio_request(flashlight->flash_adj, "fl_flash_adj"); + if (ret < 0) { + printk(KERN_ERR "%s: gpio_request(flash_adj) failed\n", + __func__); + return ret; + } + fl_str->gpio_flash_adj = flashlight->flash_adj; + gpio_set_value(fl_str->gpio_flash_adj, 0); + fl_str->flash_adj_gpio_status = 0; + printk(KERN_DEBUG "%s: enable flash_adj function\n", + FLASHLIGHT_NAME); + } + if (flashlight->flash_duration_ms) + fl_str->flash_sw_timeout_ms = flashlight->flash_duration_ms; + else /* load default value */ + fl_str->flash_sw_timeout_ms = 600; + return ret; +} + +static int flashlight_free_gpio(struct flashlight_platform_data *flashlight, + struct flashlight_struct *fl_str) +{ + int ret = 0; + if (fl_str->gpio_torch) { + gpio_free(flashlight->torch); + fl_str->gpio_torch = 0; + } + + if (fl_str->gpio_flash) { + gpio_free(flashlight->flash); + fl_str->gpio_flash = 0; + } + + if (fl_str->gpio_flash_adj) { + gpio_free(flashlight->flash_adj); + fl_str->gpio_flash_adj = 0; + } + + return ret; +} + +#ifdef FLASHLIGHT_ADJ_FUNC +static ssize_t show_flash_adj(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + length = sprintf(buf, "%d\n", this_fl_str->flash_adj_value); + return length; +} + +static ssize_t store_flash_adj(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + static int tmp, adj_tmp; + if ((buf[0] == '0' || buf[0] == '1' || buf[0] == '2') + && buf[1] == '\n') { + spin_lock_irqsave(&this_fl_str->spin_lock, + this_fl_str->spinlock_flags); + tmp = buf[0] - 0x30; + if (tmp == this_fl_str->flash_adj_value) { + spin_unlock_irqrestore(&this_fl_str->spin_lock, + this_fl_str->spinlock_flags); + printk(KERN_NOTICE "%s: status is same(%d)\n", + __func__, this_fl_str->flash_adj_value); + return count; + } + adj_tmp = this_fl_str->gpio_flash_adj; + switch (tmp) { + case 2: + flashlight_turn_off(); + break; + case 1: + /* + if (this_fl_str->flash_adj_gpio_status) { + gpio_set_value(adj_tmp, 0); + this_fl_str->flash_adj_gpio_status = 0; + } + */ + break; + case 0: + /* + if (!this_fl_str->flash_adj_gpio_status) { + gpio_set_value(adj_tmp, 1); + this_fl_str->flash_adj_gpio_status = 1; + } + */ + break; + } + this_fl_str->flash_adj_value = tmp; + spin_unlock_irqrestore(&this_fl_str->spin_lock, + this_fl_str->spinlock_flags); + } + return count; +} + +static DEVICE_ATTR(flash_adj, 0666, show_flash_adj, store_flash_adj); +#endif + +static int flashlight_probe(struct platform_device *pdev) +{ + + struct flashlight_platform_data *flashlight = pdev->dev.platform_data; + struct flashlight_struct *fl_str; + int err = 0; + + fl_str = kzalloc(sizeof(struct flashlight_struct), GFP_KERNEL); + if (!fl_str) { + printk(KERN_ERR "%s: kzalloc fail !!!\n", __func__); + return -ENOMEM; + } + + err = flashlight_setup_gpio(flashlight, fl_str); + if (err < 0) { + printk(KERN_ERR "%s: setup GPIO fail !!!\n", __func__); + goto fail_free_mem; + } + spin_lock_init(&fl_str->spin_lock); + wake_lock_init(&fl_str->wake_lock, WAKE_LOCK_SUSPEND, pdev->name); + fl_str->fl_lcdev.name = pdev->name; + fl_str->fl_lcdev.brightness_set = fl_lcdev_brightness_set; + fl_str->fl_lcdev.brightness = 0; + err = led_classdev_register(&pdev->dev, &fl_str->fl_lcdev); + if (err < 0) { + printk(KERN_ERR "failed on led_classdev_register\n"); + goto fail_free_gpio; + } +#ifdef FLASHLIGHT_ADJ_FUNC + if (fl_str->gpio_flash_adj) { + printk(KERN_DEBUG "%s: flash_adj exist, create attr file\n", + __func__); + err = device_create_file(fl_str->fl_lcdev.dev, + &dev_attr_flash_adj); + if (err != 0) + printk(KERN_WARNING "dev_attr_flash_adj failed\n"); + } +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + fl_str->early_suspend_flashlight.suspend = flashlight_early_suspend; + fl_str->early_suspend_flashlight.resume = flashlight_late_resume; + register_early_suspend(&fl_str->early_suspend_flashlight); +#endif + hrtimer_init(&fl_str->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + fl_str->timer.function = flashlight_hrtimer_func; + fl_str->led_count = flashlight->led_count; + + this_fl_str = fl_str; + printk(KERN_INFO "%s: The Flashlight Driver is ready\n", __func__); + return 0; + +fail_free_gpio: + wake_lock_destroy(&fl_str->wake_lock); + flashlight_free_gpio(flashlight, fl_str); +fail_free_mem: + kfree(fl_str); + printk(KERN_ERR "%s: The Flashlight driver is Failure\n", __func__); + return err; +} + +static int flashlight_remove(struct platform_device *pdev) +{ + struct flashlight_platform_data *flashlight = pdev->dev.platform_data; + + flashlight_turn_off(); + hrtimer_cancel(&this_fl_str->timer); + unregister_early_suspend(&this_fl_str->early_suspend_flashlight); +#ifdef FLASHLIGHT_ADJ_FUNC + if (this_fl_str->gpio_flash_adj) { + device_remove_file(this_fl_str->fl_lcdev.dev, + &dev_attr_flash_adj); + } +#endif + led_classdev_unregister(&this_fl_str->fl_lcdev); + wake_lock_destroy(&this_fl_str->wake_lock); + flashlight_free_gpio(flashlight, this_fl_str); + + kfree(this_fl_str); + return 0; +} + +static struct platform_driver flashlight_driver = { + .probe = flashlight_probe, + .remove = flashlight_remove, + .driver = { + .name = FLASHLIGHT_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init flashlight_init(void) +{ + return platform_driver_register(&flashlight_driver); +} + +static void __exit flashlight_exit(void) +{ + platform_driver_unregister(&flashlight_driver); +} + +module_init(flashlight_init); +module_exit(flashlight_exit); + +MODULE_DESCRIPTION("flash light driver"); +MODULE_LICENSE("GPL"); From e86feb56d7fd622e2bad7c2970b8f820cca9eaeb Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 2 Nov 2010 01:29:19 -0400 Subject: [PATCH 0973/2556] msm: Add HTC auto white balance calibration driver Change-Id: If9bfa45f14ebdc9e887e730df54ba33e4b340ba7 --- arch/arm/mach-msm/Makefile | 2 +- arch/arm/mach-msm/htc_awb_cal.c | 146 ++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-msm/htc_awb_cal.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 38f7209d56442..7f3e3c43ca241 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -63,7 +63,7 @@ obj-$(CONFIG_MACH_SAPPHIRE) += devices-msm7x00.o devices_htc.o obj-$(CONFIG_MACH_SAPPHIRE) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi.o board-mahimahi-panel.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-keypad.o board-mahimahi-mmc.o -obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-rfkill.o htc_wifi_nvs.o +obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-rfkill.o htc_wifi_nvs.o htc_awb_cal.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-wifi.o board-mahimahi-audio.o obj-$(CONFIG_MACH_MAHIMAHI) += msm_vibrator.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-microp.o diff --git a/arch/arm/mach-msm/htc_awb_cal.c b/arch/arm/mach-msm/htc_awb_cal.c new file mode 100644 index 0000000000000..012f063ada5c1 --- /dev/null +++ b/arch/arm/mach-msm/htc_awb_cal.c @@ -0,0 +1,146 @@ +/* arch/arm/mach-msm/htc_awb_cal.c */ +/* Code to extract Camera AWB calibration information from ATAG +set up by the bootloader. + +Copyright (C) 2008 Google, Inc. +Author: Dmitry Shmidt + +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 + +/* for outputing file to filesystem : /data/awb_calibration_data_hboot.txt */ +#include +#include + +/* configuration tags specific to msm */ +#define ATAG_MSM_AWB_CAL 0x59504550 /* MSM CAMERA AWB Calibration */ + +#ifdef CONFIG_ARCH_MSM7X30 +#define AWB_CAL_MAX_SIZE 0x1000U /* 0x1000 = 4096 bytes */ +#else +#define AWB_CAL_MAX_SIZE 0x800U /* 0x800 = 2048 bytes */ +#endif + +struct qct_lsc_struct{ + unsigned long int lsc_verify; + unsigned long int lsc_fuseid[4]; + float pCalcParam[17*13*4]; + unsigned long int lsc_checksum; +}; + +struct qct_awb_lsc_struct{ + unsigned long int caBuff[8];/* AWB Calibartion */ + struct qct_lsc_struct qct_lsc_data;/* LSC Calibration */ +}; + +static unsigned char cam_awb_ram[AWB_CAL_MAX_SIZE]; + +int gCAM_AWB_CAL_LEN; + +unsigned char *get_cam_awb_cal(void) +{ + return cam_awb_ram; +} + +EXPORT_SYMBOL(get_cam_awb_cal); + +static int __init parse_tag_cam_awb_cal(const struct tag *tag) +{ + unsigned char *dptr = (unsigned char *)(&tag->u); + unsigned size; + + size = min((tag->hdr.size - 2) * sizeof(__u32), AWB_CAL_MAX_SIZE); + + printk(KERN_INFO "CAM_AWB_CAL Data size = %d , 0x%x, size = %d\n", + tag->hdr.size, tag->hdr.tag, size); + + gCAM_AWB_CAL_LEN = size; + memcpy(cam_awb_ram, dptr, size); + + +#ifdef ATAG_CAM_AWB_CAL_DEBUG + { + int *pint, i; + + printk(KERN_INFO "parse_tag_cam_awb_cal():\n"); + + pint = (int *)cam_awb_ram; + + for (i = 0; i < 1024; i++) + printk(KERN_INFO "%x\n", pint[i]); + + } +#endif + + return 0; +} + +__tagtable(ATAG_MSM_AWB_CAL, parse_tag_cam_awb_cal); + + +static ssize_t awb_calibration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + unsigned char *ptr; + + ptr = get_cam_awb_cal(); + /* fixed : workaround because of defined 8 parameters now */ + + ret = sizeof(struct qct_awb_lsc_struct);/* 8*4; */ + memcpy(buf, ptr, ret); + + +#ifdef ATAG_CAM_AWB_CAL_DEBUG + { + int i, *pint; + printk(KERN_INFO "awb_calibration_show():\n"); + pint = (int *)buf; + for (i = 0; i < 898; i++) + printk(KERN_INFO "%x\n", pint[i]); + + } +#endif + + return ret; +} + +static DEVICE_ATTR(awb_cal, 0444, awb_calibration_show, NULL); + +static struct kobject *cam_awb_cal; + +static int cam_get_awb_cal(void) +{ + int ret ; + + /* Create /sys/android_camera_awb_cal/awb_cal */ + cam_awb_cal = kobject_create_and_add("android_camera_awb_cal", NULL); + if (cam_awb_cal == NULL) { + pr_info("cam_get_awb_cal: subsystem_register failed\n"); + ret = -ENOMEM; + return ret ; + } + + /* dev_attr_[register_name]<== DEVICE_ATTR(awb_cal, 0444, + awb_calibration_show, NULL); */ + ret = sysfs_create_file(cam_awb_cal, &dev_attr_awb_cal.attr); + if (ret) { + pr_info("cam_get_awb_cal:: sysfs_create_file failed\n"); + kobject_del(cam_awb_cal); + } + return 0 ; +} + +late_initcall(cam_get_awb_cal); From c5644f78192028ac9aa715b9780b6dac4410b5ea Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 2 Nov 2010 00:07:10 -0400 Subject: [PATCH 0974/2556] msm: camera: Import latest drivers from HTC Includes updated drivers for 7x/8x, additional sensors, decoupling from V4L, DEATH_RAY, and 720P support. Change-Id: I31ff8457d5364b1e26304349a605490374ba0cb2 --- arch/arm/mach-msm/include/mach/board.h | 100 +- arch/arm/mach-msm/include/mach/camera.h | 89 +- .../mach-msm/include/mach/msm_flashlight.h | 2 + drivers/media/video/msm/Kconfig | 42 +- drivers/media/video/msm/Makefile | 7 +- drivers/media/video/msm/msm_camera.c | 749 +++-- drivers/media/video/msm/msm_io7x.c | 32 +- drivers/media/video/msm/msm_io8x.c | 4 +- drivers/media/video/msm/msm_io_vfe31.c | 285 -- drivers/media/video/msm/msm_v4l2.c | 1 + drivers/media/video/msm/msm_vfe31.c | 2314 -------------- drivers/media/video/msm/msm_vfe31.h | 1052 ------- drivers/media/video/msm/msm_vfe7x.c | 253 +- drivers/media/video/msm/msm_vfe8x.c | 133 +- drivers/media/video/msm/msm_vfe8x.h | 16 +- drivers/media/video/msm/msm_vfe8x_proc.c | 294 +- drivers/media/video/msm/msm_vfe8x_proc.h | 6 +- drivers/media/video/msm/mt9d112.c | 1 + drivers/media/video/msm/mt9p012_fox.c | 1 + drivers/media/video/msm/ov8810.c | 2749 +++++++++++++++++ drivers/media/video/msm/ov8810.h | 61 + drivers/media/video/msm/ov9665.c | 1168 +++++++ drivers/media/video/msm/ov9665.h | 54 + drivers/media/video/msm/ov9665_reg.c | 210 ++ drivers/media/video/msm/s5k3e2fx.c | 2301 ++++++++------ drivers/media/video/msm/s5k3h1gx.c | 1756 +++++++++++ drivers/media/video/msm/s5k3h1gx.h | 93 + drivers/media/video/msm/s5k3h1gx_reg.c | 420 +++ drivers/media/video/msm/s5k6aafx.c | 1022 ++++++ drivers/media/video/msm/s5k6aafx.h | 80 + drivers/media/video/msm/s5k6aafx_reg_mecha.c | 2647 ++++++++++++++++ include/media/msm_camera.h | 281 +- 32 files changed, 13094 insertions(+), 5129 deletions(-) mode change 100755 => 100644 drivers/media/video/msm/Makefile delete mode 100644 drivers/media/video/msm/msm_io_vfe31.c delete mode 100644 drivers/media/video/msm/msm_vfe31.c delete mode 100644 drivers/media/video/msm/msm_vfe31.h create mode 100644 drivers/media/video/msm/ov8810.c create mode 100644 drivers/media/video/msm/ov8810.h create mode 100644 drivers/media/video/msm/ov9665.c create mode 100644 drivers/media/video/msm/ov9665.h create mode 100644 drivers/media/video/msm/ov9665_reg.c create mode 100644 drivers/media/video/msm/s5k3h1gx.c create mode 100644 drivers/media/video/msm/s5k3h1gx.h create mode 100644 drivers/media/video/msm/s5k3h1gx_reg.c create mode 100644 drivers/media/video/msm/s5k6aafx.c create mode 100644 drivers/media/video/msm/s5k6aafx.h create mode 100644 drivers/media/video/msm/s5k6aafx_reg_mecha.c diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index f1558704b1088..913d1b6ee7072 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -19,11 +19,10 @@ #include #include +#include /* platform device data structures */ - -struct msm_acpu_clock_platform_data -{ +struct msm_acpu_clock_platform_data { uint32_t acpu_switch_time_us; uint32_t max_speed_delta_khz; uint32_t vdd_switch_time_us; @@ -37,8 +36,11 @@ struct msm_camera_io_ext { uint32_t mdcsz; uint32_t appphy; uint32_t appsz; - unsigned long camifpadphy; - unsigned long camifpadsz; + uint32_t camifpadphy; + uint32_t camifpadsz; + uint32_t csiphy; + uint32_t csisz; + uint32_t csiirq; }; struct msm_camera_device_platform_data { @@ -46,6 +48,18 @@ struct msm_camera_device_platform_data { void (*camera_gpio_off)(void); struct msm_camera_io_ext ioext; }; +enum msm_camera_csi_data_format { + CSI_8BIT, + CSI_10BIT, + CSI_12BIT, +}; +struct msm_camera_csi_params { + enum msm_camera_csi_data_format data_format; + uint8_t lane_cnt; + uint8_t lane_assign; + uint8_t settle_cnt; + uint8_t dpcm_scheme; +}; struct msm_camera_legacy_device_platform_data { int sensor_reset; @@ -53,6 +67,52 @@ struct msm_camera_legacy_device_platform_data { int vcm_pwd; void (*config_gpio_on) (void); void (*config_gpio_off)(void); + struct msm_camsensor_device_platform_data *sensor_info; +}; + +#define MSM_CAMERA_FLASH_NONE 0 +#define MSM_CAMERA_FLASH_LED 1 +#define MSM_CAMERA_FLASH_SRC_PMIC (0x00000001<<0) +#define MSM_CAMERA_FLASH_SRC_PWM (0x00000001<<1) + +struct msm_camera_sensor_flash_pmic { + uint32_t low_current; + uint32_t high_current; +}; + +struct msm_camera_sensor_flash_pwm { + uint32_t freq; + uint32_t max_load; + uint32_t low_load; + uint32_t high_load; + uint32_t channel; +}; + +struct msm_camera_sensor_flash_src { + int flash_sr_type; + + union { + struct msm_camera_sensor_flash_pmic pmic_src; + struct msm_camera_sensor_flash_pwm pwm_src; + } _fsrc; +}; + +struct msm_camera_sensor_flash_data { + int flash_type; + struct msm_camera_sensor_flash_src *flash_src; +}; + +struct camera_flash_cfg { + int num_flash_levels; + int (*camera_flash)(int level); + uint16_t low_temp_limit; + uint16_t low_cap_limit; + uint8_t postpone_led_mode; +}; + +enum msm_camera_source{ + MAIN_SOURCE, + SECOND_SOURCE, }; struct msm_camera_sensor_info { @@ -60,14 +120,37 @@ struct msm_camera_sensor_info { int sensor_reset; int sensor_pwd; int vcm_pwd; + void(*camera_clk_switch)(void); + /*power*/ + char *camera_analog_pwd; + char *camera_io_pwd; + char *camera_vcm_pwd; + char *camera_digital_pwd; + int analog_pwd1_gpio; + int (*camera_power_on)(void); + int (*camera_power_off)(void); + void(*camera_set_source)(enum msm_camera_source); + enum msm_camera_source(*camera_get_source)(void); + int (*camera_main_get_probe)(void); + void (*camera_main_set_probe)(int); int mclk; - int num_flash_levels; - int (*camera_flash)(int level); + int flash_type; /* for back support */ + uint8_t led_high_enabled; int need_suspend; struct msm_camera_device_platform_data *pdata; struct resource *resource; uint8_t num_resources; + uint32_t waked_up; + wait_queue_head_t event_wait; + uint32_t kpi_sensor_start; + uint32_t kpi_sensor_end; + struct camera_flash_cfg* flash_cfg; + int csi_if; + struct msm_camera_csi_params csi_params; + int sensor_lc_disable; /* for sensor lens correction support */ + uint8_t (*preview_skip_frame)(void); }; +struct clk; struct snd_endpoint { int id; @@ -79,12 +162,9 @@ struct msm_snd_endpoints { unsigned num; }; -struct clk; - extern struct sys_timer msm_timer; /* common init routines for use by arch/arm/mach-msm/board-*.c */ - void __init msm_add_devices(void); void __init msm_map_common_io(void); void __init msm_init_irq(void); diff --git a/arch/arm/mach-msm/include/mach/camera.h b/arch/arm/mach-msm/include/mach/camera.h index 1da814f3d3fbf..a517d29077c09 100644 --- a/arch/arm/mach-msm/include/mach/camera.h +++ b/arch/arm/mach-msm/include/mach/camera.h @@ -1,19 +1,5 @@ -/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. */ #ifndef __ASM__ARCH_CAMERA_H @@ -54,27 +40,28 @@ enum vfe_resp_msg { VFE_EVENT, VFE_MSG_GENERAL, VFE_MSG_SNAPSHOT, +#ifndef CONFIG_720P_CAMERA VFE_MSG_OUTPUT1, VFE_MSG_OUTPUT2, - VFE_MSG_STATS_AF, - VFE_MSG_STATS_WE, /* AEC + AWB */ +#else VFE_MSG_OUTPUT_P, /* preview (continuous mode ) */ VFE_MSG_OUTPUT_T, /* thumbnail (snapshot mode )*/ VFE_MSG_OUTPUT_S, /* main image (snapshot mode )*/ VFE_MSG_OUTPUT_V, /* video (continuous mode ) */ - VFE_MSG_STATS_AEC, - VFE_MSG_STATS_AWB, - VFE_MSG_STATS_RS, - VFE_MSG_STATS_CS, - VFE_MSG_STATS_IHIST, - VFE_MSG_STATS_SKIN, +#endif + VFE_MSG_STATS_AF, + VFE_MSG_STATS_WE, }; +#define VFE31_OUTPUT_MODE_PT (0x1 << 0) +#define VFE31_OUTPUT_MODE_S (0x1 << 1) +#define VFE31_OUTPUT_MODE_V (0x1 << 2) + struct msm_vfe_phy_info { uint32_t sbuf_phy; uint32_t y_phy; uint32_t cbcr_phy; - uint8_t output_id; /* OUTPUT_MODE_P/T/S/V */ + uint8_t output_id; /* VFE31_OUTPUT_MODE_PT/S/V */ }; struct msm_vfe_resp { @@ -91,7 +78,6 @@ struct msm_vfe_callback { gfp_t gfp); void* (*vfe_alloc)(int, void *syncdata, gfp_t gfp); void (*vfe_free)(void *ptr); - int (*flash_ctrl)(void *syncdata, int level); }; struct msm_camvfe_fn { @@ -104,9 +90,10 @@ struct msm_camvfe_fn { }; struct msm_sensor_ctrl { - int (*s_init)(const struct msm_camera_sensor_info *); + int (*s_init)(struct msm_camera_sensor_info *); int (*s_release)(void); int (*s_config)(void __user *); + int node; }; /* this structure is used in kernel */ @@ -118,6 +105,7 @@ struct msm_queue_cmd { enum msm_queue type; void *command; int on_heap; + struct timespec ts; }; struct msm_device_queue { @@ -154,10 +142,12 @@ struct msm_sync { * interrupt context, and by the control thread. */ struct msm_device_queue pict_q; + int get_pic_abort; struct msm_camera_sensor_info *sdata; struct msm_camvfe_fn vfefn; struct msm_sensor_ctrl sctrl; + struct wake_lock wake_suspend_lock; struct wake_lock wake_lock; struct platform_device *pdev; uint8_t opencnt; @@ -177,7 +167,6 @@ struct msm_sync { struct mutex lock; struct list_head list; - int get_pic_abort; }; #define MSM_APPS_ID_V4L2 "msm_v4l2" @@ -215,7 +204,9 @@ struct register_address_value_pair { struct msm_pmem_region { struct hlist_node list; unsigned long paddr; +//#ifdef CONFIG_MSM_CAMERA_LEGACY unsigned long kvaddr; +//#endif unsigned long len; struct file *file; struct msm_pmem_info info; @@ -224,10 +215,25 @@ struct msm_pmem_region { struct axidata { uint32_t bufnum1; uint32_t bufnum2; - struct msm_pmem_region *region; +//#ifdef CONFIG_720P_CAMERA uint32_t bufnum3; +//#endif + struct msm_pmem_region *region; }; +#ifdef CONFIG_MSM_CAMERA_FLASH + int msm_camera_flash_set_led_state( + struct msm_camera_sensor_flash_data *fdata, + unsigned led_state); +#else + static inline int msm_camera_flash_set_led_state( + struct msm_camera_sensor_flash_data *fdata, + unsigned led_state) + { + return -ENOTSUPP; + } +#endif + #ifdef CONFIG_MSM_CAMERA_V4L2 /* Below functions are added for V4L2 kernel APIs */ struct msm_v4l2_driver { @@ -251,7 +257,7 @@ void msm_camvfe_init(void); int msm_camvfe_check(void *); void msm_camvfe_fn_init(struct msm_camvfe_fn *, void *); int msm_camera_drv_start(struct platform_device *dev, - int (*sensor_probe)(const struct msm_camera_sensor_info *, + int (*sensor_probe)(struct msm_camera_sensor_info *, struct msm_sensor_ctrl *)); enum msm_camio_clk_type { @@ -259,7 +265,8 @@ enum msm_camio_clk_type { CAMIO_MDC_CLK, CAMIO_VFE_CLK, CAMIO_VFE_AXI_CLK, - +//#ifdef CONFIG_MSM_CAMERA_7X30 + CAMIO_VFE_CLK_FOR_MIPI_2_LANE, CAMIO_VFE_CAMIF_CLK, CAMIO_VFE_PBDG_CLK, CAMIO_CAM_MCLK_CLK, @@ -267,6 +274,7 @@ enum msm_camio_clk_type { CAMIO_CSI_CLK, CAMIO_CSI_VFE_CLK, CAMIO_CSI_PCLK, +//#endif CAMIO_MAX_CLK }; @@ -312,7 +320,6 @@ int msm_camio_clk_disable(enum msm_camio_clk_type clk); int msm_camio_clk_config(uint32_t freq); void msm_camio_clk_rate_set(int rate); void msm_camio_clk_axi_rate_set(int rate); -void msm_disable_io_gpio_clk(struct platform_device *); void msm_camio_camif_pad_reg_reset(void); void msm_camio_camif_pad_reg_reset_2(void); @@ -323,4 +330,22 @@ void msm_camio_clk_sel(enum msm_camio_clk_src_type); void msm_camio_disable(struct platform_device *); int msm_camio_probe_on(struct platform_device *); int msm_camio_probe_off(struct platform_device *); + +#ifdef CONFIG_MSM_CAMERA_7X30 +void msm_camio_clk_rate_set_2(struct clk *clk, int rate); +void msm_disable_io_gpio_clk(struct platform_device *); +int msm_camio_csi_config(struct msm_camera_csi_params *csi_params); +int request_axi_qos(uint32_t freq); +int update_axi_qos(uint32_t freq); +void release_axi_qos(void); +int msm_camio_read_camif_status(void); + +void msm_io_w(u32 data, void __iomem *addr); +void msm_io_w_mb(u32 data, void __iomem *addr); +u32 msm_io_r(void __iomem *addr); +u32 msm_io_r_mb(void __iomem *addr); +void msm_io_dump(void __iomem *addr, int size); +void msm_io_memcpy(void __iomem *dest_addr, void __iomem *src_addr, u32 len); +#endif + #endif diff --git a/arch/arm/mach-msm/include/mach/msm_flashlight.h b/arch/arm/mach-msm/include/mach/msm_flashlight.h index bababa09c4c8c..08efd10298f50 100644 --- a/arch/arm/mach-msm/include/mach/msm_flashlight.h +++ b/arch/arm/mach-msm/include/mach/msm_flashlight.h @@ -50,6 +50,8 @@ struct flashlight_platform_data { }; int flashlight_control(int mode); +int aat1271_flashlight_control(int mode); +int adp1650_flashlight_control(int mode); #undef __ASM_ARCH_MSM8X50_FLASHLIGHT_H #endif diff --git a/drivers/media/video/msm/Kconfig b/drivers/media/video/msm/Kconfig index b4796d33ccb13..142c28641ea75 100644 --- a/drivers/media/video/msm/Kconfig +++ b/drivers/media/video/msm/Kconfig @@ -7,10 +7,15 @@ menuconfig MSM_CAMERA Say Y here to enable selecting the video adapters for Qualcomm msm camera and video encoding +config 720P_CAMERA + bool "Qualcomm MSM camera with 720P video support" + depends on MSM_CAMERA + help + 720P video support + config MSM_CAMERA_V4L2 bool "Video For Linux interface to MSM camera" depends on MSM_CAMERA && VIDEO_V4L2 && EXPERIMENTAL - default y help Say Y here to enable the V4L2 interface for the MSM camera. Not everything works through this interface and it has not @@ -41,8 +46,43 @@ config MT9P012 ---help--- MICRON 5M Bayer Sensor with Autofocus +config MSM_CAMERA_AF_FOXCONN + bool "FOXCONN Module" + depends on MT9P012 + ---help--- + This driver supports FOXCONN AF module for 5M Bayer sensor + config S5K3E2FX bool "Sensor s5k3e2fx (Samsung 5M)" depends on MSM_CAMERA ---help--- Samsung 5M with Autofocus + +config S5K6AAFX + bool "Sensor s5k6aafx (Samsung 1.3M)" + depends on MSM_CAMERA + default n + ---help--- + Samsung 1.3M without Autofocus + +config OV8810 + bool "Sensor ov8810" + depends on MSM_CAMERA + default n + ---help--- + OmniVision 8M Bayer Sensor + +config OV9665 + bool "Sensor ov9665" + depends on MSM_CAMERA + default n + ---help--- + OmniVision 3M YUV Sensor + +config S5K3H1GX + bool "Sensor s5k3h1gx" + depends on MSM_CAMERA + default n + ---help--- + Samsung 8M 3H1 MIPI AF Sensor + diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile old mode 100755 new mode 100644 index 61e370c921d91..26d74658d0a1b --- a/drivers/media/video/msm/Makefile +++ b/drivers/media/video/msm/Makefile @@ -1,9 +1,12 @@ obj-$(CONFIG_MT9T013) += mt9t013.o mt9t013_reg.o obj-$(CONFIG_MT9D112) += mt9d112.o mt9d112_reg.o +obj-$(CONFIG_OV9665) += ov9665.o ov9665_reg.o obj-$(CONFIG_MT9P012) += mt9p012_fox.o mt9p012_reg.o obj-$(CONFIG_MSM_CAMERA) += msm_camera.o obj-$(CONFIG_MSM_CAMERA_V4L2) += msm_v4l2.o obj-$(CONFIG_S5K3E2FX) += s5k3e2fx.o -obj-$(CONFIG_ARCH_MSM7X00A) += msm_vfe7x.o msm_io7x.o +obj-$(CONFIG_S5K6AAFX) += s5k6aafx.o s5k6aafx_reg_mecha.o +obj-$(CONFIG_ARCH_MSM_ARM11) += msm_vfe7x.o msm_io7x.o obj-$(CONFIG_ARCH_QSD8X50) += msm_vfe8x.o msm_vfe8x_proc.o msm_io8x.o -obj-$(CONFIG_ARCH_MSM7X30) += msm_vfe31.o msm_io_vfe31.o +obj-$(CONFIG_OV8810) += ov8810.o +obj-$(CONFIG_S5K3H1GX) += s5k3h1gx.o s5k3h1gx_reg.o diff --git a/drivers/media/video/msm/msm_camera.c b/drivers/media/video/msm/msm_camera.c index 42cd3adb75054..aace48743b006 100644 --- a/drivers/media/video/msm/msm_camera.c +++ b/drivers/media/video/msm/msm_camera.c @@ -21,8 +21,6 @@ #include #include #include -#include -#include #include #include @@ -31,14 +29,15 @@ #include #include #include -#include #include #include - #include +#include +#include +#include +DEFINE_MUTEX(hlist_mut); #define MSM_MAX_CAMERA_SENSORS 5 -#define CAMERA_STOP_SNAPSHOT 42 #define ERR_USER_COPY(to) pr_err("%s(%d): copy %s user\n", \ __func__, __LINE__, ((to) ? "to" : "from")) @@ -85,6 +84,7 @@ static inline void free_qcmd(struct msm_queue_cmd *qcmd) { if (!qcmd || !qcmd->on_heap) return; + CDBG("%s qcmd->on_heap:%d\n",__func__,qcmd->on_heap); if (!--qcmd->on_heap) kfree(qcmd); } @@ -105,10 +105,17 @@ static void msm_enqueue(struct msm_device_queue *queue, unsigned long flags; spin_lock_irqsave(&queue->lock, flags); queue->len++; + if (queue->len > queue->max) { queue->max = queue->len; +#if 0 pr_info("%s: queue %s new max is %d\n", __func__, queue->name, queue->max); +#else + if(queue->max < 1024) + pr_info("%s: queue %s new max is %d\n", __func__, + queue->name, queue->max); +#endif } list_add_tail(entry, &queue->list); wake_up(&queue->wait); @@ -117,33 +124,36 @@ static void msm_enqueue(struct msm_device_queue *queue, } #define msm_dequeue(queue, member) ({ \ - unsigned long flags; \ + unsigned long flags; \ struct msm_device_queue *__q = (queue); \ - struct msm_queue_cmd *qcmd = 0; \ + struct msm_queue_cmd *qcmd = 0; \ spin_lock_irqsave(&__q->lock, flags); \ - if (!list_empty(&__q->list)) { \ - __q->len--; \ - qcmd = list_first_entry(&__q->list, \ - struct msm_queue_cmd, member); \ - list_del_init(&qcmd->member); \ - } \ + if (!list_empty(&__q->list)) { \ + __q->len--; \ + qcmd = list_first_entry(&__q->list, \ + struct msm_queue_cmd, member); \ + if (qcmd) { \ + list_del_init(&qcmd->member); \ + } \ + } \ spin_unlock_irqrestore(&__q->lock, flags); \ - qcmd; \ + qcmd; \ }) + #define msm_queue_drain(queue, member) do { \ - unsigned long flags; \ + unsigned long flags; \ struct msm_device_queue *__q = (queue); \ - struct msm_queue_cmd *qcmd; \ + struct msm_queue_cmd *qcmd; \ spin_lock_irqsave(&__q->lock, flags); \ CDBG("%s: draining queue %s\n", __func__, __q->name); \ - while (!list_empty(&__q->list)) { \ - qcmd = list_first_entry(&__q->list, \ - struct msm_queue_cmd, member); \ - list_del_init(&qcmd->member); \ - free_qcmd(qcmd); \ - }; \ - __q->len = 0; \ + while (!list_empty(&__q->list)) { \ + qcmd = list_first_entry(&__q->list, \ + struct msm_queue_cmd, member); \ + list_del_init(&qcmd->member); \ + free_qcmd(qcmd); \ + }; \ + __q->len = 0; \ spin_unlock_irqrestore(&__q->lock, flags); \ } while(0) @@ -232,7 +242,7 @@ static int msm_pmem_table_add(struct hlist_head *ptype, __func__, info->type, paddr, (unsigned long)info->vaddr); - region = kmalloc(sizeof(struct msm_pmem_region), GFP_KERNEL); + region = kzalloc(sizeof(struct msm_pmem_region), GFP_KERNEL); if (!region) return -ENOMEM; @@ -244,10 +254,6 @@ static int msm_pmem_table_add(struct hlist_head *ptype, region->file = file; memcpy(®ion->info, info, sizeof(region->info)); - if (info->vfe_can_write) { - dmac_map_area((void*)region->kvaddr, region->len, DMA_FROM_DEVICE); - } - hlist_add_head(&(region->list), ptype); return 0; @@ -264,7 +270,7 @@ static uint8_t msm_pmem_region_lookup(struct hlist_head *ptype, uint8_t rc = 0; regptr = reg; - + mutex_lock(&hlist_mut); hlist_for_each_entry_safe(region, node, n, ptype, list) { if (region->info.type == pmem_type && region->info.vfe_can_write) { @@ -275,7 +281,7 @@ static uint8_t msm_pmem_region_lookup(struct hlist_head *ptype, regptr++; } } - + mutex_unlock(&hlist_mut); return rc; } @@ -289,13 +295,19 @@ static int msm_pmem_frame_ptov_lookup(struct msm_sync *sync, hlist_for_each_entry_safe(region, node, n, &sync->pmem_frames, list) { if (pyaddr == (region->paddr + region->info.y_off) && +#ifndef CONFIG_ARCH_MSM7225 pcbcraddr == (region->paddr + region->info.cbcr_off) && +#endif region->info.vfe_can_write) { *pmem_region = region; - dmac_unmap_area((void*)region->kvaddr, region->len, - DMA_FROM_DEVICE); region->info.vfe_can_write = !take_from_vfe; +#ifdef CONFIG_ARCH_MSM7225 + if (pcbcraddr != (region->paddr + region->info.cbcr_off)) { + pr_err("%s cbcr addr = %lx, NOT EQUAL to region->paddr + region->info.cbcr_off = %lx\n", + __func__, pcbcraddr, region->paddr + region->info.cbcr_off); + } +#endif return 0; } } @@ -314,13 +326,22 @@ static unsigned long msm_pmem_stats_ptov_lookup(struct msm_sync *sync, /* offset since we could pass vaddr inside a * registered pmem buffer */ *fd = region->info.fd; - dmac_unmap_area((void*)region->kvaddr, region->len, - DMA_FROM_DEVICE); region->info.vfe_can_write = 0; return (unsigned long)(region->info.vaddr); } } - +#if 1 + printk("msm_pmem_stats_ptov_lookup: lookup vaddr..\n"); + hlist_for_each_entry_safe(region, node, n, &sync->pmem_stats, list) { + if (addr == (unsigned long)(region->info.vaddr)) { + /* offset since we could pass vaddr inside a + * registered pmem buffer */ + *fd = region->info.fd; + region->info.vfe_can_write = 0; + return (unsigned long)(region->info.vaddr); + } + } +#endif return 0; } @@ -338,7 +359,6 @@ static unsigned long msm_pmem_frame_vtop_lookup(struct msm_sync *sync, (region->info.cbcr_off == cbcroff) && (region->info.fd == fd) && (region->info.vfe_can_write == 0)) { - dmac_map_area((void*)region->kvaddr, region->len, DMA_FROM_DEVICE); region->info.vfe_can_write = 1; return region->paddr; } @@ -359,7 +379,6 @@ static unsigned long msm_pmem_stats_vtop_lookup( if (((unsigned long)(region->info.vaddr) == buffer) && (region->info.fd == fd) && region->info.vfe_can_write == 0) { - dmac_map_area((void*)region->kvaddr, region->len, DMA_FROM_DEVICE); region->info.vfe_can_write = 1; return region->paddr; } @@ -376,10 +395,13 @@ static int __msm_pmem_table_del(struct msm_sync *sync, struct hlist_node *node, *n; switch (pinfo->type) { +#ifndef CONFIG_720P_CAMERA case MSM_PMEM_OUTPUT1: case MSM_PMEM_OUTPUT2: +#else case MSM_PMEM_VIDEO: case MSM_PMEM_PREVIEW: +#endif case MSM_PMEM_THUMBNAIL: case MSM_PMEM_MAINIMG: case MSM_PMEM_RAW_MAINIMG: @@ -391,10 +413,6 @@ static int __msm_pmem_table_del(struct msm_sync *sync, pinfo->fd == region->info.fd) { hlist_del(node); put_pmem_file(region->file); - if (region->info.vfe_can_write) { - dmac_unmap_area((void*)region->kvaddr, region->len, - DMA_FROM_DEVICE); - } kfree(region); } } @@ -410,10 +428,6 @@ static int __msm_pmem_table_del(struct msm_sync *sync, pinfo->fd == region->info.fd) { hlist_del(node); put_pmem_file(region->file); - if (region->info.vfe_can_write) { - dmac_unmap_area((void*)region->kvaddr, region->len, - DMA_FROM_DEVICE); - } kfree(region); } } @@ -449,7 +463,9 @@ static int __msm_get_frame(struct msm_sync *sync, struct msm_vfe_resp *vdata; struct msm_vfe_phy_info *pphy; - qcmd = msm_dequeue(&sync->frame_q, list_frame); + if (&sync->frame_q) { + qcmd = msm_dequeue(&sync->frame_q, list_frame); + } if (!qcmd) { pr_err("%s: no preview frame.\n", __func__); @@ -479,7 +495,6 @@ static int __msm_get_frame(struct msm_sync *sync, frame->cbcr_off = region->info.cbcr_off; frame->fd = region->info.fd; frame->path = vdata->phy.output_id; - CDBG("%s: y %x, cbcr %x, qcmd %x, virt_addr %x\n", __func__, pphy->y_phy, pphy->cbcr_phy, (int) qcmd, (int) frame->buffer); @@ -598,7 +613,7 @@ static struct msm_queue_cmd *__msm_control(struct msm_sync *sync, * need to remove it. Alternatively, qcmd may have * been dequeued and processed already, in which case * the list removal will be a no-op. - */ + */ list_del_init(&qcmd->list_config); return ERR_PTR(rc); } @@ -623,7 +638,7 @@ static struct msm_queue_cmd *__msm_control_nb(struct msm_sync *sync, struct msm_ctrl_cmd *udata_to_copy = qcmd_to_copy->command; struct msm_queue_cmd *qcmd = - kmalloc(sizeof(*qcmd_to_copy) + + kzalloc(sizeof(*qcmd_to_copy) + sizeof(*udata_to_copy) + udata_to_copy->length, GFP_KERNEL); @@ -665,8 +680,7 @@ static int msm_control(struct msm_control_device *ctrl_pmsm, uptr = udata.value; udata.value = data; - if (udata.type == CAMERA_STOP_SNAPSHOT) - sync->get_pic_abort = 1; + qcmd.on_heap = 0; qcmd.type = MSM_CAM_Q_CTRL; qcmd.command = &udata; @@ -864,7 +878,7 @@ static int msm_get_stats(struct msm_sync *sync, void __user *arg) if (rc == 0) rc = -ETIMEDOUT; if (rc < 0) { - pr_err("%s: error %d\n", __func__, rc); + pr_err("\n%s: error %d\n", __func__, rc); return rc; } } @@ -875,6 +889,13 @@ static int msm_get_stats(struct msm_sync *sync, void __user *arg) qcmd = msm_dequeue(&sync->event_q, list_config); BUG_ON(!qcmd); + /* HTC: check qcmd */ + if (!qcmd) { + rc = -EFAULT; + pr_err("%s: qcmd is NULL, rc %d\n", __func__, rc); + return rc; + } + CDBG("%s: received from DSP %d\n", __func__, qcmd->type); switch (qcmd->type) { @@ -895,20 +916,22 @@ static int msm_get_stats(struct msm_sync *sync, void __user *arg) se.stats_event.len, se.stats_event.msg_id); - if (data->type == VFE_MSG_STATS_AF || - data->type == VFE_MSG_STATS_WE || - (data->type >= VFE_MSG_STATS_AEC && - data->type <= VFE_MSG_STATS_SKIN)) { - /* the check above includes all stats type. */ + if ((data->type == VFE_MSG_STATS_AF) || + (data->type == VFE_MSG_STATS_WE)) { + stats.buffer = msm_pmem_stats_ptov_lookup(sync, data->phy.sbuf_phy, &(stats.fd)); if (!stats.buffer) { - pr_err("%s: msm_pmem_stats_ptov_lookup error\n", - __func__); + pr_err("%s: msm_pmem_stats_ptov_lookup error, addr = %x\n", + __func__,data->phy.sbuf_phy); +#if 1 + se.resptype = MSM_CAM_RESP_MAX; +#else rc = -EINVAL; goto failure; +#endif } if (copy_to_user((void *)(se.stats_event.data), @@ -928,16 +951,26 @@ static int msm_get_stats(struct msm_sync *sync, void __user *arg) goto failure; } } else { +#ifndef CONFIG_720P_CAMERA if ((sync->pp_mask & PP_PREV) && - (data->type == VFE_MSG_OUTPUT1 || - data->type == VFE_MSG_OUTPUT2 || - data->type == VFE_MSG_OUTPUT_P)) + (data->type == VFE_MSG_OUTPUT1 || + data->type == VFE_MSG_OUTPUT2)) + rc = msm_divert_frame(sync, data, &se); + else if ((sync->pp_mask & (PP_SNAP|PP_RAW_SNAP)) && + data->type == VFE_MSG_SNAPSHOT) + rc = msm_divert_snapshot(sync, + data, &se); +#else + if ((sync->pp_mask & PP_PREV) && + (data->type == VFE_MSG_OUTPUT_P)) rc = msm_divert_frame(sync, data, &se); else if ((sync->pp_mask & (PP_SNAP|PP_RAW_SNAP)) && (data->type == VFE_MSG_SNAPSHOT || + data->type == VFE_MSG_OUTPUT_T || data->type == VFE_MSG_OUTPUT_S)) rc = msm_divert_snapshot(sync, data, &se); +#endif } break; @@ -1075,11 +1108,20 @@ static int msm_config_vfe(struct msm_sync *sync, void __user *arg) axi_data.bufnum1 = msm_pmem_region_lookup(&sync->pmem_stats, MSM_PMEM_AEC_AWB, ®ion[0], - NUM_STAT_OUTPUT_BUFFERS); + NUM_WB_EXP_STAT_OUTPUT_BUFFERS); + + /* HTC: check axi_data.bufnum1 if out of bound of "region" array */ + if (!axi_data.bufnum1 || axi_data.bufnum1 >= + (sizeof(region)/sizeof(struct msm_pmem_region))) { + pr_err("%s %d: pmem region lookup error or out of bound\n", + __func__, __LINE__); + return -EINVAL; + } + axi_data.bufnum2 = msm_pmem_region_lookup(&sync->pmem_stats, MSM_PMEM_AF, ®ion[axi_data.bufnum1], - NUM_STAT_OUTPUT_BUFFERS); + NUM_AF_STAT_OUTPUT_BUFFERS); if (!axi_data.bufnum1 || !axi_data.bufnum2) { pr_err("%s: pmem region lookup error\n", __func__); return -EINVAL; @@ -1090,7 +1132,7 @@ static int msm_config_vfe(struct msm_sync *sync, void __user *arg) axi_data.bufnum1 = msm_pmem_region_lookup(&sync->pmem_stats, MSM_PMEM_AF, ®ion[0], - NUM_STAT_OUTPUT_BUFFERS); + NUM_AF_STAT_OUTPUT_BUFFERS); if (!axi_data.bufnum1) { pr_err("%s %d: pmem region lookup error\n", __func__, __LINE__); @@ -1098,35 +1140,12 @@ static int msm_config_vfe(struct msm_sync *sync, void __user *arg) } axi_data.region = ®ion[0]; return sync->vfefn.vfe_config(&cfgcmd, &axi_data); + case CMD_STATS_AEC_AWB_ENABLE: axi_data.bufnum1 = msm_pmem_region_lookup(&sync->pmem_stats, MSM_PMEM_AEC_AWB, ®ion[0], - NUM_STAT_OUTPUT_BUFFERS); - if (!axi_data.bufnum1) { - pr_err("%s %d: pmem region lookup error\n", - __func__, __LINE__); - return -EINVAL; - } - axi_data.region = ®ion[0]; - return sync->vfefn.vfe_config(&cfgcmd, &axi_data); - case CMD_STATS_AEC_ENABLE: - axi_data.bufnum1 = - msm_pmem_region_lookup(&sync->pmem_stats, - MSM_PMEM_AEC, ®ion[0], - NUM_STAT_OUTPUT_BUFFERS); - if (!axi_data.bufnum1) { - pr_err("%s %d: pmem region lookup error\n", - __func__, __LINE__); - return -EINVAL; - } - axi_data.region = ®ion[0]; - return sync->vfefn.vfe_config(&cfgcmd, &axi_data); - case CMD_STATS_AWB_ENABLE: - axi_data.bufnum1 = - msm_pmem_region_lookup(&sync->pmem_stats, - MSM_PMEM_AWB, ®ion[0], - NUM_STAT_OUTPUT_BUFFERS); + NUM_WB_EXP_STAT_OUTPUT_BUFFERS); if (!axi_data.bufnum1) { pr_err("%s %d: pmem region lookup error\n", __func__, __LINE__); @@ -1134,47 +1153,6 @@ static int msm_config_vfe(struct msm_sync *sync, void __user *arg) } axi_data.region = ®ion[0]; return sync->vfefn.vfe_config(&cfgcmd, &axi_data); - - - case CMD_STATS_IHIST_ENABLE: - axi_data.bufnum1 = - msm_pmem_region_lookup(&sync->pmem_stats, - MSM_PMEM_IHIST, ®ion[0], - NUM_STAT_OUTPUT_BUFFERS); - if (!axi_data.bufnum1) { - pr_err("%s %d: pmem region lookup error\n", - __func__, __LINE__); - return -EINVAL; - } - axi_data.region = ®ion[0]; - return sync->vfefn.vfe_config(&cfgcmd, &axi_data); - - case CMD_STATS_RS_ENABLE: - axi_data.bufnum1 = - msm_pmem_region_lookup(&sync->pmem_stats, - MSM_PMEM_RS, ®ion[0], - NUM_STAT_OUTPUT_BUFFERS); - if (!axi_data.bufnum1) { - pr_err("%s %d: pmem region lookup error\n", - __func__, __LINE__); - return -EINVAL; - } - axi_data.region = ®ion[0]; - return sync->vfefn.vfe_config(&cfgcmd, &axi_data); - - case CMD_STATS_CS_ENABLE: - axi_data.bufnum1 = - msm_pmem_region_lookup(&sync->pmem_stats, - MSM_PMEM_CS, ®ion[0], - NUM_STAT_OUTPUT_BUFFERS); - if (!axi_data.bufnum1) { - pr_err("%s %d: pmem region lookup error\n", - __func__, __LINE__); - return -EINVAL; - } - axi_data.region = ®ion[0]; - return sync->vfefn.vfe_config(&cfgcmd, &axi_data); - case CMD_GENERAL: case CMD_STATS_DISABLE: return sync->vfefn.vfe_config(&cfgcmd, NULL); @@ -1198,6 +1176,8 @@ static int msm_frame_axi_cfg(struct msm_sync *sync, memset(&axi_data, 0, sizeof(axi_data)); switch (cfgcmd->cmd_type) { + +#ifndef CONFIG_720P_CAMERA case CMD_AXI_CFG_OUT1: pmem_type = MSM_PMEM_OUTPUT1; axi_data.bufnum1 = @@ -1223,6 +1203,31 @@ static int msm_frame_axi_cfg(struct msm_sync *sync, } break; + case CMD_AXI_CFG_SNAP_O1_AND_O2: + pmem_type = MSM_PMEM_THUMBNAIL; + axi_data.bufnum1 = + msm_pmem_region_lookup(&sync->pmem_frames, pmem_type, + ®ion[0], 8); + + /* HTC: check axi_data.bufnum1 if out of bound of "region" array */ + if (!axi_data.bufnum1 || axi_data.bufnum1 >= + (sizeof(region)/sizeof(struct msm_pmem_region))) { + pr_err("%s %d: pmem region lookup error or out of bound\n", + __func__, __LINE__); + return -EINVAL; + } + + pmem_type = MSM_PMEM_MAINIMG; + axi_data.bufnum2 = + msm_pmem_region_lookup(&sync->pmem_frames, pmem_type, + ®ion[axi_data.bufnum1], 8); + if (!axi_data.bufnum2) { + pr_err("%s %d: pmem region lookup error\n", + __func__, __LINE__); + return -EINVAL; + } + break; +#else case CMD_AXI_CFG_PREVIEW: pmem_type = MSM_PMEM_PREVIEW; axi_data.bufnum2 = @@ -1259,31 +1264,8 @@ static int msm_frame_axi_cfg(struct msm_sync *sync, } break; - case CMD_AXI_CFG_O1_AND_O2: - pmem_type = MSM_PMEM_OUTPUT1; - axi_data.bufnum1 = - msm_pmem_region_lookup(&sync->pmem_frames, pmem_type, - ®ion[0], 8); - if (!axi_data.bufnum1) { - pr_err("%s %d: pmem region lookup error\n", - __func__, __LINE__); - return -EINVAL; - } - - pmem_type = MSM_PMEM_OUTPUT2; - axi_data.bufnum2 = - msm_pmem_region_lookup(&sync->pmem_frames, pmem_type, - ®ion[axi_data.bufnum1], - (8-(axi_data.bufnum1))); - if (!axi_data.bufnum2) { - pr_err("%s %d: pmem region lookup error\n", - __func__, __LINE__); - return -EINVAL; - } - break; case CMD_AXI_CFG_SNAP: - case CMD_AXI_CFG_SNAP_O1_AND_O2: pmem_type = MSM_PMEM_THUMBNAIL; axi_data.bufnum1 = msm_pmem_region_lookup(&sync->pmem_frames, pmem_type, @@ -1305,7 +1287,7 @@ static int msm_frame_axi_cfg(struct msm_sync *sync, return -EINVAL; } break; - +#endif case CMD_RAW_PICT_AXI_CFG: pmem_type = MSM_PMEM_RAW_MAINIMG; axi_data.bufnum2 = @@ -1331,6 +1313,7 @@ static int msm_frame_axi_cfg(struct msm_sync *sync, axi_data.region = ®ion[0]; /* send the AXI configuration command to driver */ + if (sync->vfefn.vfe_config) rc = sync->vfefn.vfe_config(cfgcmd, data); @@ -1356,7 +1339,7 @@ static int msm_get_sensor_info(struct msm_sync *sync, void __user *arg) memcpy(&info.name[0], sdata->sensor_name, MAX_SENSOR_NAME); - info.flash_enabled = !!sdata->camera_flash; + info.flash_enabled = !!sdata->flash_cfg; /* copy back to user space */ if (copy_to_user((void *)arg, @@ -1387,12 +1370,12 @@ static int __msm_put_frame_buf(struct msm_sync *sync, pb->buffer, pphy); cfgcmd.cmd_type = CMD_FRAME_BUF_RELEASE; cfgcmd.value = (void *)pb; - if (sync->vfefn.vfe_config) + if (sync->vfefn.vfe_config) { rc = sync->vfefn.vfe_config(&cfgcmd, &pphy); + } } else { - pr_err("%s: msm_pmem_frame_vtop_lookup failed. " - "buffer=0x%lx, y_off=%d, cbcr_off=%d, fd=%d\n", - __func__, pb->buffer, pb->y_off, pb->cbcr_off, pb->fd); + pr_err("%s: msm_pmem_frame_vtop_lookup failed\n", + __func__); rc = -EINVAL; } @@ -1419,10 +1402,13 @@ static int __msm_register_pmem(struct msm_sync *sync, int rc = 0; switch (pinfo->type) { +#ifndef CONFIG_720P_CAMERA case MSM_PMEM_OUTPUT1: case MSM_PMEM_OUTPUT2: +#else case MSM_PMEM_VIDEO: case MSM_PMEM_PREVIEW: +#endif case MSM_PMEM_THUMBNAIL: case MSM_PMEM_MAINIMG: case MSM_PMEM_RAW_MAINIMG: @@ -1431,13 +1417,6 @@ static int __msm_register_pmem(struct msm_sync *sync, case MSM_PMEM_AEC_AWB: case MSM_PMEM_AF: - case MSM_PMEM_AEC: - case MSM_PMEM_AWB: - case MSM_PMEM_RS: - case MSM_PMEM_CS: - case MSM_PMEM_IHIST: - case MSM_PMEM_SKIN: - rc = msm_pmem_table_add(&sync->pmem_stats, pinfo); break; @@ -1492,7 +1471,7 @@ static int msm_stats_axi_cfg(struct msm_sync *sync, if (cfgcmd->cmd_type != CMD_GENERAL) { axi_data.bufnum1 = msm_pmem_region_lookup(&sync->pmem_stats, pmem_type, - ®ion[0], NUM_STAT_OUTPUT_BUFFERS); + ®ion[0], NUM_WB_EXP_STAT_OUTPUT_BUFFERS); if (!axi_data.bufnum1) { pr_err("%s %d: pmem region lookup error\n", __func__, __LINE__); @@ -1502,6 +1481,7 @@ static int msm_stats_axi_cfg(struct msm_sync *sync, } /* send the AEC/AWB STATS configuration command to driver */ + if (sync->vfefn.vfe_config) rc = sync->vfefn.vfe_config(cfgcmd, &axi_data); @@ -1530,17 +1510,6 @@ static int msm_put_stats_buffer(struct msm_sync *sync, void __user *arg) cfgcmd.cmd_type = CMD_STATS_BUF_RELEASE; else if (buf.type == STAT_AF) cfgcmd.cmd_type = CMD_STATS_AF_BUF_RELEASE; - else if (buf.type == STAT_AEC) - cfgcmd.cmd_type = CMD_STATS_AEC_BUF_RELEASE; - else if (buf.type == STAT_AWB) - cfgcmd.cmd_type = CMD_STATS_AWB_BUF_RELEASE; - else if (buf.type == STAT_IHIST) - cfgcmd.cmd_type = CMD_STATS_IHIST_BUF_RELEASE; - else if (buf.type == STAT_RS) - cfgcmd.cmd_type = CMD_STATS_RS_BUF_RELEASE; - else if (buf.type == STAT_CS) - cfgcmd.cmd_type = CMD_STATS_CS_BUF_RELEASE; - else { pr_err("%s: invalid buf type %d\n", __func__, @@ -1550,7 +1519,6 @@ static int msm_put_stats_buffer(struct msm_sync *sync, void __user *arg) } cfgcmd.value = (void *)&buf; - if (sync->vfefn.vfe_config) { rc = sync->vfefn.vfe_config(&cfgcmd, &pphy); if (rc < 0) @@ -1577,13 +1545,15 @@ static int msm_axi_config(struct msm_sync *sync, void __user *arg) } switch (cfgcmd.cmd_type) { +#ifndef CONFIG_720P_CAMERA case CMD_AXI_CFG_OUT1: case CMD_AXI_CFG_OUT2: - case CMD_AXI_CFG_O1_AND_O2: case CMD_AXI_CFG_SNAP_O1_AND_O2: +#else case CMD_AXI_CFG_VIDEO: case CMD_AXI_CFG_PREVIEW: case CMD_AXI_CFG_SNAP: +#endif case CMD_RAW_PICT_AXI_CFG: return msm_frame_axi_cfg(sync, &cfgcmd); @@ -1612,14 +1582,8 @@ static int __msm_get_pic(struct msm_sync *sync, struct msm_ctrl_cmd *ctrl) rc = wait_event_interruptible_timeout( sync->pict_q.wait, - !list_empty_careful( - &sync->pict_q.list) || sync->get_pic_abort, + !list_empty_careful(&sync->pict_q.list), msecs_to_jiffies(tm)); - - if (sync->get_pic_abort == 1) { - sync->get_pic_abort = 0; - return -ENODATA; - } if (list_empty_careful(&sync->pict_q.list)) { if (rc == 0) return -ETIMEDOUT; @@ -1634,6 +1598,13 @@ static int __msm_get_pic(struct msm_sync *sync, struct msm_ctrl_cmd *ctrl) qcmd = msm_dequeue(&sync->pict_q, list_pict); BUG_ON(!qcmd); + /* HTC: check qcmd */ + if (!qcmd) { + rc = -EFAULT; + pr_err("%s: qcmd is NULL, rc %d\n", __func__, rc); + return rc; + } + if (qcmd->command != NULL) { struct msm_ctrl_cmd *q = (struct msm_ctrl_cmd *)qcmd->command; @@ -1652,8 +1623,7 @@ static int __msm_get_pic(struct msm_sync *sync, struct msm_ctrl_cmd *ctrl) static int msm_get_pic(struct msm_sync *sync, void __user *arg) { struct msm_ctrl_cmd ctrlcmd; - struct msm_pmem_region *pic_pmem_region = NULL, *region; - struct hlist_node *node, *n; + struct msm_pmem_region pic_pmem_region; int rc; unsigned long end; int cline_mask; @@ -1685,31 +1655,31 @@ static int msm_get_pic(struct msm_sync *sync, void __user *arg) } } - hlist_for_each_entry_safe(region, node, n, &sync->pmem_frames, list) { - if (region->info.vfe_can_write && - (region->info.type == MSM_PMEM_MAINIMG || - region->info.type == MSM_PMEM_RAW_MAINIMG)) { - pic_pmem_region = region; - break; + if (msm_pmem_region_lookup(&sync->pmem_frames, + MSM_PMEM_MAINIMG, + &pic_pmem_region, 1) == 0) { + pr_err("%s pmem region lookup error\n", __func__); + pr_info("%s probably getting RAW\n", __func__); + if (msm_pmem_region_lookup(&sync->pmem_frames, + MSM_PMEM_RAW_MAINIMG, + &pic_pmem_region, 1) == 0) { + pr_err("%s RAW pmem region lookup error\n", __func__); + return -EIO; } } - if (!pic_pmem_region) { - pr_err("%s pmem region lookup error\n", __func__); - return -EIO; - } cline_mask = cache_line_size() - 1; - end = pic_pmem_region->kvaddr + pic_pmem_region->len; + end = pic_pmem_region.kvaddr + pic_pmem_region.len; end = (end + cline_mask) & ~cline_mask; pr_info("%s: flushing cache for [%08lx, %08lx)\n", __func__, - pic_pmem_region->kvaddr, end); + pic_pmem_region.kvaddr, end); - /* HACK: Invalidate buffer */ - dmac_unmap_area((void*)pic_pmem_region->kvaddr, pic_pmem_region->len, - DMA_FROM_DEVICE); - pic_pmem_region->info.vfe_can_write = 0; + /* HACK: Invalidate buffer */ + dmac_unmap_area((void*)pic_pmem_region.kvaddr, pic_pmem_region.len, + DMA_FROM_DEVICE); + pic_pmem_region.info.vfe_can_write = 0; CDBG("%s: copy snapshot frame to user\n", __func__); if (copy_to_user((void *)arg, @@ -1733,7 +1703,7 @@ static int msm_set_crop(struct msm_sync *sync, void __user *arg) } if (!sync->croplen) { - sync->cropinfo = kmalloc(crop.len, GFP_KERNEL); + sync->cropinfo = kzalloc(crop.len, GFP_KERNEL); if (!sync->cropinfo) return -ENOMEM; } else if (sync->croplen < crop.len) @@ -1800,7 +1770,6 @@ static int msm_pp_release(struct msm_sync *sync, void __user *arg) return -EINVAL; } pr_info("%s: delivering pp_prev\n", __func__); - msm_enqueue(&sync->frame_q, &sync->pp_prev->list_frame); sync->pp_prev = NULL; goto done; @@ -1840,34 +1809,54 @@ static long msm_ioctl_common(struct msm_device *pmsm, int msm_camera_flash(struct msm_sync *sync, int level) { - int flash_level; + int flash_level = 0; + uint8_t phy_flash = 0; + int ret = 0; - if (!sync->sdata->camera_flash) { + if (!sync->sdata->flash_cfg) { pr_err("%s: camera flash is not supported.\n", __func__); return -EINVAL; } - if (!sync->sdata->num_flash_levels) { + if (!sync->sdata->flash_cfg->num_flash_levels) { pr_err("%s: no flash levels.\n", __func__); return -EINVAL; } + sync->sdata->flash_cfg->postpone_led_mode = MSM_CAMERA_LED_OFF; + switch (level) { + case MSM_CAMERA_LED_DEATH_RAY: + flash_level = FL_MODE_DEATH_RAY; + phy_flash = 1; + break; + case MSM_CAMERA_LED_LOW_FOR_SNAPSHOT: + /* postpone set led low*/ + sync->sdata->flash_cfg->postpone_led_mode = MSM_CAMERA_LED_LOW; + phy_flash = 0; + break; case MSM_CAMERA_LED_HIGH: - flash_level = sync->sdata->num_flash_levels - 1; + /* postpone set led high*/ + sync->sdata->flash_cfg->postpone_led_mode = MSM_CAMERA_LED_HIGH; + phy_flash = 0; break; case MSM_CAMERA_LED_LOW: - flash_level = sync->sdata->num_flash_levels / 2; + flash_level = sync->sdata->flash_cfg->num_flash_levels / 2; + phy_flash = 1; break; case MSM_CAMERA_LED_OFF: flash_level = 0; + phy_flash = 1; break; default: pr_err("%s: invalid flash level %d.\n", __func__, level); return -EINVAL; } - return sync->sdata->camera_flash(level); + if (phy_flash) + ret = sync->sdata->flash_cfg->camera_flash(flash_level); + + return ret; } static long msm_ioctl_config(struct file *filep, unsigned int cmd, @@ -1950,8 +1939,7 @@ static long msm_ioctl_config(struct file *filep, unsigned int cmd, } else rc = msm_camera_flash(pmsm->sync, led_state); break; - } - + } case MSM_CAM_IOCTL_ENABLE_OUTPUT_IND: { uint32_t enable; if (copy_from_user(&enable, argp, sizeof(enable))) { @@ -1959,11 +1947,7 @@ static long msm_ioctl_config(struct file *filep, unsigned int cmd, rc = -EFAULT; break; } - pr_info("%s: copying all preview frames to config: %d\n", - __func__, enable); pmsm->sync->report_preview_to_config = enable; - rc = 0; - break; } default: @@ -1984,7 +1968,6 @@ static long msm_ioctl_frame(struct file *filep, unsigned int cmd, void __user *argp = (void __user *)arg; struct msm_device *pmsm = filep->private_data; - switch (cmd) { case MSM_CAM_IOCTL_GETFRAME: /* Coming from frame thread to get frame @@ -2043,17 +2026,33 @@ static long msm_ioctl_control(struct file *filep, unsigned int cmd, return rc; } + +static void msm_show_time(void){ + struct timespec ts; + struct rtc_time tm; + getnstimeofday(&ts); + rtc_time_to_tm(ts.tv_sec, &tm); + pr_info(">>>>>>>>(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)<<<<<<<\n", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); + return; +} + + static int __msm_release(struct msm_sync *sync) { struct msm_pmem_region *region; struct hlist_node *hnode; struct hlist_node *n; - + pr_info("%s:sync->opencnt:%d \n", __func__, sync->opencnt); mutex_lock(&sync->lock); if (sync->opencnt) sync->opencnt--; if (!sync->opencnt) { + + msm_show_time(); + /* need to clean up system resource */ if (sync->vfefn.vfe_release) sync->vfefn.vfe_release(sync->pdev); @@ -2062,6 +2061,8 @@ static int __msm_release(struct msm_sync *sync) sync->cropinfo = NULL; sync->croplen = 0; + /*sensor release moved to vfe_release*/ + hlist_for_each_entry_safe(region, hnode, n, &sync->pmem_frames, list) { hlist_del(hnode); @@ -2075,8 +2076,12 @@ static int __msm_release(struct msm_sync *sync) put_pmem_file(region->file); kfree(region); } + + msm_queue_drain(&sync->event_q, list_config); + msm_queue_drain(&sync->frame_q, list_frame); msm_queue_drain(&sync->pict_q, list_pict); + wake_unlock(&sync->wake_suspend_lock); wake_unlock(&sync->wake_lock); sync->apps_id = NULL; @@ -2091,10 +2096,10 @@ static int msm_release_config(struct inode *node, struct file *filep) { int rc; struct msm_device *pmsm = filep->private_data; - CDBG("%s: %s\n", __func__, filep->f_path.dentry->d_name.name); + pr_info("%s: %s\n", __func__, filep->f_path.dentry->d_name.name); rc = __msm_release(pmsm->sync); if (!rc) { - msm_queue_drain(&pmsm->sync->event_q, list_config); + pr_info("release config atomic_set pmsm->opened as 0\n"); atomic_set(&pmsm->opened, 0); } return rc; @@ -2105,7 +2110,7 @@ static int msm_release_control(struct inode *node, struct file *filep) int rc; struct msm_control_device *ctrl_pmsm = filep->private_data; struct msm_device *pmsm = ctrl_pmsm->pmsm; - CDBG("%s: %s\n", __func__, filep->f_path.dentry->d_name.name); + pr_info("%s: %s\n", __func__, filep->f_path.dentry->d_name.name); rc = __msm_release(pmsm->sync); if (!rc) { msm_queue_drain(&ctrl_pmsm->ctrl_q, list_control); @@ -2118,10 +2123,10 @@ static int msm_release_frame(struct inode *node, struct file *filep) { int rc; struct msm_device *pmsm = filep->private_data; - CDBG("%s: %s\n", __func__, filep->f_path.dentry->d_name.name); + pr_info("%s: %s\n", __func__, filep->f_path.dentry->d_name.name); rc = __msm_release(pmsm->sync); if (!rc) { - msm_queue_drain(&pmsm->sync->frame_q, list_frame); + pr_info("release frame atomic_set pmsm->opened as 0\n"); atomic_set(&pmsm->opened, 0); } return rc; @@ -2177,8 +2182,11 @@ static void *msm_vfe_sync_alloc(int size, gfp_t gfp) { struct msm_queue_cmd *qcmd = - kmalloc(sizeof(struct msm_queue_cmd) + size, gfp); + kzalloc(sizeof(struct msm_queue_cmd) + size, gfp); if (qcmd) { + /* Becker and kant */ + memset(qcmd, 0x0, sizeof(struct msm_queue_cmd) + size); + qcmd->on_heap = 1; return qcmd + 1; } @@ -2221,14 +2229,40 @@ static void msm_vfe_sync(struct msm_vfe_resp *vdata, qcmd->type = qtype; qcmd->command = vdata; + CDBG("%s: qtype %d \n", __func__, qtype); + CDBG("%s: evt_msg.msg_id %d\n", __func__, vdata->evt_msg.msg_id); + CDBG("%s: evt_msg.exttype %d\n", __func__, vdata->evt_msg.exttype); + if (sync->sdata->flash_cfg) { + if (qtype == MSM_CAM_Q_VFE_MSG && + vdata->evt_msg.exttype == VFE_MSG_SNAPSHOT) { +#if defined(CONFIG_ARCH_MSM_ARM11) + if (vdata->evt_msg.msg_id == 4) + /* QDSP_VFETASK_MSG_VFE_START_ACK */ +#elif defined(CONFIG_ARCH_QSD8X50) + if (vdata->evt_msg.msg_id == 1) + /* VFE_MSG_ID_START_ACK */ +#endif + { + pr_info("flashlight: postpone_led_mode %d\n", + sync->sdata->flash_cfg->postpone_led_mode); + sync->sdata->flash_cfg->camera_flash( + sync->sdata->flash_cfg->postpone_led_mode); + } + } + } + if (qtype != MSM_CAM_Q_VFE_MSG) goto for_config; - + CDBG("%s: vdata->type %d\n", __func__, vdata->type); switch (vdata->type) { +#ifndef CONFIG_720P_CAMERA case VFE_MSG_OUTPUT1: case VFE_MSG_OUTPUT2: +#else case VFE_MSG_OUTPUT_P: +#endif + if (sync->pp_mask & PP_PREV) { CDBG("%s: PP_PREV in progress: phy_y %x phy_cbcr %x\n", __func__, @@ -2239,24 +2273,24 @@ static void msm_vfe_sync(struct msm_vfe_resp *vdata, __func__); pr_info("%s: sending preview to config\n", __func__); sync->pp_prev = qcmd; - break; - } - - if (sync->report_preview_to_config) { if (qcmd->on_heap) qcmd->on_heap++; - msm_enqueue(&sync->frame_q, &qcmd->list_frame); - break; - } + break; + } + CDBG("%s: msm_enqueue frame_q\n", __func__); + if (qcmd->on_heap) + qcmd->on_heap++; msm_enqueue(&sync->frame_q, &qcmd->list_frame); - return; + break; - case VFE_MSG_OUTPUT_V: +#ifdef CONFIG_720P_CAMERA + case VFE_MSG_OUTPUT_V: + if (qcmd->on_heap) + qcmd->on_heap++; CDBG("%s: msm_enqueue video frame_q\n", __func__); - if (qcmd->on_heap) - qcmd->on_heap++; msm_enqueue(&sync->frame_q, &qcmd->list_frame); break; +#endif case VFE_MSG_SNAPSHOT: if (sync->pp_mask & (PP_SNAP | PP_RAW_SNAP)) { @@ -2267,46 +2301,16 @@ static void msm_vfe_sync(struct msm_vfe_resp *vdata, __func__); pr_info("%s: sending snapshot to config\n", __func__); sync->pp_snap = qcmd; - break; + if (qcmd->on_heap) + qcmd->on_heap++; + break; } - if (qcmd->on_heap) qcmd->on_heap++; msm_enqueue(&sync->pict_q, &qcmd->list_pict); break; - case VFE_MSG_STATS_AWB: - CDBG("%s: qtype %d, AWB stats, enqueue event_q.\n", - __func__, vdata->type); - break; - - case VFE_MSG_STATS_AEC: - CDBG("%s: qtype %d, AEC stats, enqueue event_q.\n", - __func__, vdata->type); - break; - - case VFE_MSG_STATS_IHIST: - CDBG("%s: qtype %d, ihist stats, enqueue event_q.\n", - __func__, vdata->type); - break; - - case VFE_MSG_STATS_RS: - CDBG("%s: qtype %d, rs stats, enqueue event_q.\n", - __func__, vdata->type); - break; - - case VFE_MSG_STATS_CS: - CDBG("%s: qtype %d, cs stats, enqueue event_q.\n", - __func__, vdata->type); - break; - - - case VFE_MSG_GENERAL: - CDBG("%s: qtype %d, general msg, enqueue event_q.\n", - __func__, vdata->type); - break; - - default: + default: CDBG("%s: qtype %d not handled\n", __func__, vdata->type); /* fall through, send to config. */ } @@ -2319,7 +2323,6 @@ static struct msm_vfe_callback msm_vfe_s = { .vfe_resp = msm_vfe_sync, .vfe_alloc = msm_vfe_sync_alloc, .vfe_free = msm_vfe_sync_free, - .flash_ctrl = msm_camera_flash, }; static int __msm_open(struct msm_sync *sync, const char *const apps_id) @@ -2340,11 +2343,11 @@ static int __msm_open(struct msm_sync *sync, const char *const apps_id) sync->apps_id = apps_id; if (!sync->opencnt) { + wake_lock(&sync->wake_suspend_lock); wake_lock(&sync->wake_lock); msm_camvfe_fn_init(&sync->vfefn, sync); if (sync->vfefn.vfe_init) { - sync->get_pic_abort = 0; rc = sync->vfefn.vfe_init(&msm_vfe_s, sync->pdev); if (rc < 0) { @@ -2356,6 +2359,7 @@ static int __msm_open(struct msm_sync *sync, const char *const apps_id) if (rc < 0) { pr_err("%s: sensor init failed: %d\n", __func__, rc); + sync->vfefn.vfe_release(sync->pdev); goto msm_open_done; } } else { @@ -2384,7 +2388,7 @@ static int msm_open_common(struct inode *inode, struct file *filep, struct msm_device *pmsm = container_of(inode->i_cdev, struct msm_device, cdev); - CDBG("%s: open %s\n", __func__, filep->f_path.dentry->d_name.name); + pr_info("%s: open %s\n", __func__, filep->f_path.dentry->d_name.name); if (atomic_cmpxchg(&pmsm->opened, 0, 1) && once) { pr_err("%s: %s is already opened.\n", @@ -2409,8 +2413,11 @@ static int msm_open_common(struct inode *inode, struct file *filep, return rc; } + + static int msm_open(struct inode *inode, struct file *filep) { + msm_show_time(); return msm_open_common(inode, filep, 1); } @@ -2419,7 +2426,7 @@ static int msm_open_control(struct inode *inode, struct file *filep) int rc; struct msm_control_device *ctrl_pmsm = - kmalloc(sizeof(struct msm_control_device), GFP_KERNEL); + kzalloc(sizeof(struct msm_control_device), GFP_KERNEL); if (!ctrl_pmsm) return -ENOMEM; @@ -2431,7 +2438,6 @@ static int msm_open_control(struct inode *inode, struct file *filep) ctrl_pmsm->pmsm = filep->private_data; filep->private_data = ctrl_pmsm; - msm_queue_init(&ctrl_pmsm->ctrl_q, "control"); CDBG("%s: rc %d\n", __func__, rc); @@ -2449,7 +2455,7 @@ static int __msm_v4l2_control(struct msm_sync *sync, struct msm_device_queue FIXME; /* wake up config thread, 4 is for V4L2 application */ - qcmd = kmalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL); + qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL); if (!qcmd) { pr_err("%s: cannot allocate buffer\n", __func__); rc = -ENOMEM; @@ -2538,6 +2544,169 @@ static int msm_tear_down_cdev(struct msm_device *msm, dev_t devno) return 0; } +static uint32_t led_ril_status_value; +static uint32_t led_wimax_status_value; +static uint32_t led_hotspot_status_value; +static uint16_t led_low_temp_limit; +static uint16_t led_low_cap_limit; +static struct kobject *led_status_obj; + +static ssize_t led_ril_status_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + length = sprintf(buf, "%d\n", led_ril_status_value); + return length; +} + +static ssize_t led_ril_status_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + uint32_t tmp = 0; + + tmp = buf[0] - 0x30; /* only get the first char */ + + led_ril_status_value = tmp; + pr_info("led_ril_status_value = %d\n", led_ril_status_value); + return count; +} + +static ssize_t led_wimax_status_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + length = sprintf(buf, "%d\n", led_wimax_status_value); + return length; +} + +static ssize_t led_wimax_status_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + uint32_t tmp = 0; + + tmp = buf[0] - 0x30; /* only get the first char */ + + led_wimax_status_value = tmp; + pr_info("led_wimax_status_value = %d\n", led_wimax_status_value); + return count; +} + +static ssize_t led_hotspot_status_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + length = sprintf(buf, "%d\n", led_hotspot_status_value); + return length; +} + +static ssize_t led_hotspot_status_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + uint32_t tmp = 0; + + tmp = buf[0] - 0x30; /* only get the first char */ + + led_hotspot_status_value = tmp; + pr_info("led_hotspot_status_value = %d\n", led_hotspot_status_value); + return count; +} + +static ssize_t low_temp_limit_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + length = sprintf(buf, "%d\n", led_low_temp_limit); + return length; +} + +static ssize_t low_cap_limit_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + length = sprintf(buf, "%d\n", led_low_cap_limit); + return length; +} + +static DEVICE_ATTR(led_ril_status, 0644, + led_ril_status_get, + led_ril_status_set); + +static DEVICE_ATTR(led_wimax_status, 0644, + led_wimax_status_get, + led_wimax_status_set); + +static DEVICE_ATTR(led_hotspot_status, 0644, + led_hotspot_status_get, + led_hotspot_status_set); + +static DEVICE_ATTR(low_temp_limit, 0444, + low_temp_limit_get, + NULL); + +static DEVICE_ATTR(low_cap_limit, 0444, + low_cap_limit_get, + NULL); + +static int msm_camera_sysfs_init(struct msm_sync* sync) +{ + int ret = 0; + CDBG("msm_camera:kobject creat and add\n"); + led_status_obj = kobject_create_and_add("camera_led_status", NULL); + if (led_status_obj == NULL) { + pr_info("msm_camera: subsystem_register failed\n"); + ret = -ENOMEM; + goto error; + } + + ret = sysfs_create_file(led_status_obj, + &dev_attr_led_ril_status.attr); + if (ret) { + pr_info("msm_camera: sysfs_create_file ril failed\n"); + ret = -EFAULT; + goto error; + } + + ret = sysfs_create_file(led_status_obj, + &dev_attr_led_wimax_status.attr); + if (ret) { + pr_info("msm_camera: sysfs_create_file wimax failed\n"); + ret = -EFAULT; + goto error; + } + + ret = sysfs_create_file(led_status_obj, + &dev_attr_led_hotspot_status.attr); + if (ret) { + pr_info("msm_camera: sysfs_create_file hotspot failed\n"); + ret = -EFAULT; + goto error; + } + + ret = sysfs_create_file(led_status_obj, + &dev_attr_low_temp_limit.attr); + if (ret) { + pr_info("msm_camera: sysfs_create_file low_temp_limit failed\n"); + ret = -EFAULT; + goto error; + } + + ret = sysfs_create_file(led_status_obj, + &dev_attr_low_cap_limit.attr); + if (ret) { + pr_info("msm_camera: sysfs_create_file low_cap_limit failed\n"); + ret = -EFAULT; + goto error; + } + + led_low_temp_limit = sync->sdata->flash_cfg->low_temp_limit; + led_low_cap_limit = sync->sdata->flash_cfg->low_cap_limit; + + return ret; +error: + kobject_del(led_status_obj); + return ret; +} + #ifdef CONFIG_MSM_CAMERA_V4L2 int msm_v4l2_register(struct msm_v4l2_driver *drv) { @@ -2569,8 +2738,8 @@ EXPORT_SYMBOL(msm_v4l2_unregister); static int msm_sync_init(struct msm_sync *sync, struct platform_device *pdev, - int (*sensor_probe)(const struct msm_camera_sensor_info *, - struct msm_sensor_ctrl *)) + int (*sensor_probe)(struct msm_camera_sensor_info *, + struct msm_sensor_ctrl *), int camera_node) { int rc = 0; struct msm_sensor_ctrl sctrl; @@ -2580,11 +2749,14 @@ static int msm_sync_init(struct msm_sync *sync, msm_queue_init(&sync->frame_q, "frame"); msm_queue_init(&sync->pict_q, "pict"); - wake_lock_init(&sync->wake_lock, WAKE_LOCK_SUSPEND, "msm_camera"); + wake_lock_init(&sync->wake_suspend_lock, WAKE_LOCK_SUSPEND, "msm_camera_wake"); + wake_lock_init(&sync->wake_lock, WAKE_LOCK_IDLE, "msm_camera"); rc = msm_camio_probe_on(pdev); if (rc < 0) return rc; + sctrl.node = camera_node; + pr_info("sctrl.node %d\n", sctrl.node); rc = sensor_probe(sync->sdata, &sctrl); if (rc >= 0) { sync->pdev = pdev; @@ -2595,6 +2767,7 @@ static int msm_sync_init(struct msm_sync *sync, pr_err("%s: failed to initialize %s\n", __func__, sync->sdata->sensor_name); + wake_lock_destroy(&sync->wake_suspend_lock); wake_lock_destroy(&sync->wake_lock); return rc; } @@ -2607,6 +2780,7 @@ static int msm_sync_init(struct msm_sync *sync, static int msm_sync_destroy(struct msm_sync *sync) { + wake_lock_destroy(&sync->wake_suspend_lock); wake_lock_destroy(&sync->wake_lock); return 0; } @@ -2658,13 +2832,13 @@ static int msm_device_init(struct msm_device *pmsm, } int msm_camera_drv_start(struct platform_device *dev, - int (*sensor_probe)(const struct msm_camera_sensor_info *, + int (*sensor_probe)(struct msm_camera_sensor_info *, struct msm_sensor_ctrl *)) { struct msm_device *pmsm = NULL; struct msm_sync *sync; int rc = -ENODEV; - static int camera_node; + static int camera_node = 0; if (camera_node >= MSM_MAX_CAMERA_SENSORS) { pr_err("%s: too many camera sensors\n", __func__); @@ -2697,7 +2871,7 @@ int msm_camera_drv_start(struct platform_device *dev, return -ENOMEM; sync = (struct msm_sync *)(pmsm + 3); - rc = msm_sync_init(sync, dev, sensor_probe); + rc = msm_sync_init(sync, dev, sensor_probe, camera_node); if (rc < 0) { kfree(pmsm); return rc; @@ -2711,6 +2885,9 @@ int msm_camera_drv_start(struct platform_device *dev, return rc; } + if (!!sync->sdata->flash_cfg) + msm_camera_sysfs_init(sync); + camera_node++; list_add(&sync->list, &msm_sensors); return rc; diff --git a/drivers/media/video/msm/msm_io7x.c b/drivers/media/video/msm/msm_io7x.c index 63c9b64fa865c..4a33d2e5727de 100644 --- a/drivers/media/video/msm/msm_io7x.c +++ b/drivers/media/video/msm/msm_io7x.c @@ -55,7 +55,7 @@ static struct msm_camera_io_ext camio_ext; static struct resource *appio, *mdcio; void __iomem *appbase, *mdcbase; -int clk_set_flags(struct clk *clk, unsigned long flags); +extern int clk_set_flags(struct clk *clk, unsigned long flags); int msm_camio_clk_enable(enum msm_camio_clk_type clktype) { @@ -79,7 +79,8 @@ int msm_camio_clk_enable(enum msm_camio_clk_type clktype) break; } - if (!IS_ERR(clk)) { + /* HTC: check clk */ + if (!IS_ERR(clk) && clk) { clk_enable(clk); rc = 0; } @@ -108,8 +109,8 @@ int msm_camio_clk_disable(enum msm_camio_clk_type clktype) default: break; } - - if (!IS_ERR(clk)) { + /* HTC: check clk */ + if (!IS_ERR(clk) && clk) { clk_disable(clk); clk_put(clk); rc = 0; @@ -141,7 +142,8 @@ int msm_camio_enable(struct platform_device *pdev) goto enable_fail; } - appbase = ioremap(camio_ext.appphy, camio_ext.appsz); + appbase = ioremap(camio_ext.appphy, + camio_ext.appsz); if (!appbase) { rc = -ENOMEM; goto apps_no_mem; @@ -154,17 +156,21 @@ int msm_camio_enable(struct platform_device *pdev) goto mdc_busy; } - mdcbase = ioremap(camio_ext.mdcphy, camio_ext.mdcsz); + mdcbase = ioremap(camio_ext.mdcphy, + camio_ext.mdcsz); if (!mdcbase) { rc = -ENOMEM; goto mdc_no_mem; } - camdev->camera_gpio_on(); - msm_camio_clk_enable(CAMIO_VFE_CLK); msm_camio_clk_enable(CAMIO_MDC_CLK); msm_camio_clk_enable(CAMIO_VFE_MDC_CLK); + + mdelay(2); + camdev->camera_gpio_on(); + mdelay(2); + return 0; mdc_no_mem: @@ -187,11 +193,12 @@ void msm_camio_disable(struct platform_device *pdev) iounmap(appbase); release_mem_region(camio_ext.appphy, camio_ext.appsz); - camdev->camera_gpio_off(); msm_camio_clk_disable(CAMIO_VFE_CLK); msm_camio_clk_disable(CAMIO_MDC_CLK); msm_camio_clk_disable(CAMIO_VFE_MDC_CLK); + + camdev->camera_gpio_off(); } void msm_camio_camif_pad_reg_reset(void) @@ -204,10 +211,13 @@ void msm_camio_camif_pad_reg_reset(void) reg = (readl(mdcbase)) & CAMIF_CFG_RMSK; - mask = CAM_SEL_BMSK | CAM_PCLK_SRC_SEL_BMSK | CAM_PCLK_INVERT_BMSK; + mask = CAM_SEL_BMSK | + CAM_PCLK_SRC_SEL_BMSK | + CAM_PCLK_INVERT_BMSK; value = 1 << CAM_SEL_SHFT | - 3 << CAM_PCLK_SRC_SEL_SHFT | 0 << CAM_PCLK_INVERT_SHFT; + 3 << CAM_PCLK_SRC_SEL_SHFT | + 0 << CAM_PCLK_INVERT_SHFT; writel((reg & (~mask)) | (value & mask), mdcbase); mdelay(10); diff --git a/drivers/media/video/msm/msm_io8x.c b/drivers/media/video/msm/msm_io8x.c index ae4fe44c6a41a..13364d91b48a7 100644 --- a/drivers/media/video/msm/msm_io8x.c +++ b/drivers/media/video/msm/msm_io8x.c @@ -79,7 +79,7 @@ int msm_camio_clk_enable(enum msm_camio_clk_type clktype) break; } - if (!IS_ERR(clk)) { + if (!IS_ERR(clk) && clk != NULL) { /* Set rate here *before* enabling the block to prevent * unstable clock from source. */ @@ -120,7 +120,7 @@ int msm_camio_clk_disable(enum msm_camio_clk_type clktype) break; } - if (!IS_ERR(clk)) { + if (!IS_ERR(clk) && clk != NULL) { clk_disable(clk); clk_put(clk); } else diff --git a/drivers/media/video/msm/msm_io_vfe31.c b/drivers/media/video/msm/msm_io_vfe31.c deleted file mode 100644 index 523c7f1a0d8fd..0000000000000 --- a/drivers/media/video/msm/msm_io_vfe31.c +++ /dev/null @@ -1,285 +0,0 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#define CAMIF_CFG_RMSK 0x1fffff -#define CAM_SEL_BMSK 0x2 -#define CAM_PCLK_SRC_SEL_BMSK 0x60000 -#define CAM_PCLK_INVERT_BMSK 0x80000 -#define CAM_PAD_REG_SW_RESET_BMSK 0x100000 - -#define EXT_CAM_HSYNC_POL_SEL_BMSK 0x10000 -#define EXT_CAM_VSYNC_POL_SEL_BMSK 0x8000 -#define MDDI_CLK_CHICKEN_BIT_BMSK 0x80 - -#define CAM_SEL_SHFT 0x1 -#define CAM_PCLK_SRC_SEL_SHFT 0x11 -#define CAM_PCLK_INVERT_SHFT 0x13 -#define CAM_PAD_REG_SW_RESET_SHFT 0x14 - -#define EXT_CAM_HSYNC_POL_SEL_SHFT 0x10 -#define EXT_CAM_VSYNC_POL_SEL_SHFT 0xF -#define MDDI_CLK_CHICKEN_BIT_SHFT 0x7 - -static struct clk *camio_vfe_mdc_clk; -static struct clk *camio_mdc_clk; -static struct clk *camio_vfe_clk; -static struct clk *camio_vfe_camif_clk; -static struct clk *camio_vfe_pbdg_clk; -static struct clk *camio_cam_m_clk; -static struct clk *camio_camif_pad_pbdg_clk; -static struct msm_camera_io_ext camio_ext; -static struct resource *camifpadio; -void __iomem *camifpadbase; - -static void camif_io_w(u32 data) -{ - writel(data, camifpadbase); - wmb(); -} - -static u32 camif_io_r(void) -{ - uint32_t data = readl(camifpadbase); - rmb(); - return data; -} - -int msm_camio_clk_enable(enum msm_camio_clk_type clktype) -{ - int rc = 0; - struct clk *clk = NULL; - - switch (clktype) { - case CAMIO_VFE_MDC_CLK: - camio_vfe_mdc_clk = clk = clk_get(NULL, "vfe_mdc_clk"); - break; - - case CAMIO_MDC_CLK: - camio_mdc_clk = clk = clk_get(NULL, "mdc_clk"); - break; - - case CAMIO_VFE_CLK: - camio_vfe_clk = clk = clk_get(NULL, "vfe_clk"); - clk_set_rate(clk, 122880000); - break; - - case CAMIO_VFE_CAMIF_CLK: - camio_vfe_camif_clk = clk = clk_get(NULL, "vfe_camif_clk"); - break; - - case CAMIO_VFE_PBDG_CLK: - camio_vfe_pbdg_clk = clk = clk_get(NULL, "vfe_pclk"); - break; - - case CAMIO_CAM_MCLK_CLK: - camio_cam_m_clk = clk = clk_get(NULL, "cam_m_clk"); - clk_set_rate(clk, 24000000); - break; - - case CAMIO_CAMIF_PAD_PBDG_CLK: - camio_camif_pad_pbdg_clk = clk = clk_get(NULL, "camif_pad_pclk"); - break; - - default: - break; - } - - if (!IS_ERR(clk)) - clk_enable(clk); - else - rc = -1; - return rc; -} - -int msm_camio_clk_disable(enum msm_camio_clk_type clktype) -{ - int rc = 0; - struct clk *clk = NULL; - - switch (clktype) { - case CAMIO_VFE_MDC_CLK: - clk = camio_vfe_mdc_clk; - break; - - case CAMIO_MDC_CLK: - clk = camio_mdc_clk; - break; - - case CAMIO_VFE_CLK: - clk = camio_vfe_clk; - break; - - case CAMIO_VFE_CAMIF_CLK: - clk = camio_vfe_camif_clk; - break; - - case CAMIO_VFE_PBDG_CLK: - clk = camio_vfe_pbdg_clk; - break; - - case CAMIO_CAM_MCLK_CLK: - clk = camio_cam_m_clk; - break; - - case CAMIO_CAMIF_PAD_PBDG_CLK: - clk = camio_camif_pad_pbdg_clk; - break; - - default: - break; - } - - if (!IS_ERR(clk)) { - clk_disable(clk); - clk_put(clk); - } else - rc = -1; - - return rc; -} - -void msm_camio_clk_rate_set(int rate) -{ - struct clk *clk = camio_cam_m_clk; - clk_set_rate(clk, rate); -} - -int msm_camio_enable(struct platform_device *pdev) -{ - int rc = 0; - struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; - struct msm_camera_device_platform_data *camdev = sinfo->pdata; - - camio_ext = camdev->ioext; - - camdev->camera_gpio_on(); - msm_camio_clk_enable(CAMIO_VFE_PBDG_CLK); - msm_camio_clk_enable(CAMIO_CAMIF_PAD_PBDG_CLK); - msm_camio_clk_enable(CAMIO_CAM_MCLK_CLK); - msm_camio_clk_enable(CAMIO_VFE_CLK); - camifpadio = request_mem_region(camio_ext.camifpadphy, - camio_ext.camifpadsz, pdev->name); - if (!camifpadio) { - rc = -EBUSY; - goto common_fail; - } - camifpadbase = ioremap(camio_ext.camifpadphy, camio_ext.camifpadsz); - if (!camifpadbase) { - rc = -ENOMEM; - goto parallel_busy; - } - msm_camio_clk_enable(CAMIO_VFE_CAMIF_CLK); - return 0; - -parallel_busy: - release_mem_region(camio_ext.camifpadphy, camio_ext.camifpadsz); -common_fail: - msm_camio_clk_disable(CAMIO_VFE_CLK); - msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK); - msm_camio_clk_disable(CAMIO_CAMIF_PAD_PBDG_CLK); - msm_camio_clk_disable(CAMIO_VFE_PBDG_CLK); - camdev->camera_gpio_off(); - return rc; -} - -void msm_camio_disable(struct platform_device *pdev) -{ - struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; - struct msm_camera_device_platform_data *camdev = sinfo->pdata; - - msm_camio_clk_disable(CAMIO_VFE_CAMIF_CLK); - iounmap(camifpadbase); - release_mem_region(camio_ext.camifpadphy, camio_ext.camifpadsz); - CDBG("disable clocks\n"); - - msm_camio_clk_disable(CAMIO_VFE_CLK); - msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK); - msm_camio_clk_disable(CAMIO_CAMIF_PAD_PBDG_CLK); - msm_camio_clk_disable(CAMIO_VFE_PBDG_CLK); - camdev->camera_gpio_off(); -} - -void msm_camio_camif_pad_reg_reset(void) -{ - uint32_t reg; - - msm_camio_clk_sel(MSM_CAMIO_CLK_SRC_INTERNAL); - msleep(10); - - reg = camif_io_r() & CAMIF_CFG_RMSK; - reg |= 0x3; - camif_io_w(reg); - msleep(10); - - reg = camif_io_r() & CAMIF_CFG_RMSK; - reg |= 0x10; - camif_io_w(reg); - msleep(10); - - reg = camif_io_r() & CAMIF_CFG_RMSK; - /* Need to be uninverted*/ - reg &= 0x03; - camif_io_w(reg); - msleep(10); -} - -void msm_camio_vfe_blk_reset(void) -{ - return; -} - -void msm_camio_clk_sel(enum msm_camio_clk_src_type srctype) -{ - if (camio_vfe_clk != NULL) { - switch (srctype) { - case MSM_CAMIO_CLK_SRC_INTERNAL: - clk_set_flags(camio_vfe_clk, 0x00000100 << 1); - break; - - case MSM_CAMIO_CLK_SRC_EXTERNAL: - clk_set_flags(camio_vfe_clk, 0x00000100); - break; - - default: - break; - } - } -} -int msm_camio_probe_on(struct platform_device *pdev) -{ - struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; - struct msm_camera_device_platform_data *camdev = sinfo->pdata; - camdev->camera_gpio_on(); - return msm_camio_clk_enable(CAMIO_CAM_MCLK_CLK); -} - -int msm_camio_probe_off(struct platform_device *pdev) -{ - struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; - struct msm_camera_device_platform_data *camdev = sinfo->pdata; - camdev->camera_gpio_off(); - return msm_camio_clk_disable(CAMIO_CAM_MCLK_CLK); -} diff --git a/drivers/media/video/msm/msm_v4l2.c b/drivers/media/video/msm/msm_v4l2.c index 6abbdb957cb5a..138d7d4960d4c 100644 --- a/drivers/media/video/msm/msm_v4l2.c +++ b/drivers/media/video/msm/msm_v4l2.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/media/video/msm/msm_vfe31.c b/drivers/media/video/msm/msm_vfe31.c deleted file mode 100644 index 71602293e3356..0000000000000 --- a/drivers/media/video/msm/msm_vfe31.c +++ /dev/null @@ -1,2314 +0,0 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include "msm_vfe31.h" -#include -#include - -#define CHECKED_COPY_FROM_USER(in) { \ - if (copy_from_user((in), (void __user *)cmd->value, \ - cmd->length)) { \ - rc = -EFAULT; \ - break; \ - } \ -} - -#define vfe31_get_ch_ping_addr(chn) \ - (vfe_io_r(VFE_AXI_OFFSET + 0x18 * (chn))) -#define vfe31_get_ch_pong_addr(chn) \ - (vfe_io_r(VFE_AXI_OFFSET + 0x18 * (chn) + 4)) -#define vfe31_get_ch_addr(ping_pong, chn) \ - (((ping_pong) & (1 << (chn))) == 0 ? \ - vfe31_get_ch_pong_addr(chn) : vfe31_get_ch_ping_addr(chn)) - -#define vfe31_put_ch_ping_addr(chn, addr) \ - (vfe_io_w((addr), VFE_AXI_OFFSET + 0x18 * (chn))) -#define vfe31_put_ch_pong_addr(chn, addr) \ - (vfe_io_w((addr), VFE_AXI_OFFSET + 0x18 * (chn) + 4)) -#define vfe31_put_ch_addr(ping_pong, chn, addr) \ - (((ping_pong) & (1 << (chn))) == 0 ? \ - vfe31_put_ch_pong_addr((chn), (addr)) : \ - vfe31_put_ch_ping_addr((chn), (addr))) - -static struct vfe31_ctrl_type *vfe31_ctrl; -static void *vfe_syncdata; - -struct vfe31_isr_queue_cmd { - struct list_head list; - uint32_t vfeInterruptStatus0; - uint32_t vfeInterruptStatus1; - struct vfe_frame_asf_info vfeAsfFrameInfo; - struct vfe_frame_bpc_info vfeBpcFrameInfo; - struct vfe_msg_camif_status vfeCamifStatusLocal; -}; - -static struct vfe31_cmd_type vfe31_cmd[] = { -/* 0*/ {V31_DUMMY_0}, - {V31_SET_CLK}, - {V31_RESET}, - {V31_START}, - {V31_TEST_GEN_START}, -/* 5*/ {V31_OPERATION_CFG, V31_OPERATION_CFG_LEN}, - {V31_AXI_OUT_CFG, V31_AXI_OUT_LEN, V31_AXI_OUT_OFF, 0xFF}, - {V31_CAMIF_CFG, V31_CAMIF_LEN, V31_CAMIF_OFF, 0xFF}, - {V31_AXI_INPUT_CFG}, - {V31_BLACK_LEVEL_CFG, V31_BLACK_LEVEL_LEN, V31_BLACK_LEVEL_OFF, - 0xFF}, -/*10*/ {V31_ROLL_OFF_CFG, V31_ROLL_OFF_CFG_LEN, V31_ROLL_OFF_CFG_OFF, - 0xFF}, - {V31_DEMUX_CFG, V31_DEMUX_LEN, V31_DEMUX_OFF, 0xFF}, - {V31_DEMOSAIC_0_CFG, V31_DEMOSAIC_0_LEN, V31_DEMOSAIC_0_OFF, - 0xFF}, - {V31_DEMOSAIC_1_CFG, V31_DEMOSAIC_1_LEN, V31_DEMOSAIC_1_OFF, - 0xFF}, - {V31_DEMOSAIC_2_CFG, V31_DEMOSAIC_2_LEN, V31_DEMOSAIC_2_OFF, - 0xFF}, -/*15*/ {V31_FOV_CFG, V31_FOV_LEN, V31_FOV_OFF, 0xFF}, - {V31_MAIN_SCALER_CFG, V31_MAIN_SCALER_LEN, V31_MAIN_SCALER_OFF, - 0xFF}, - {V31_WB_CFG, V31_WB_LEN, V31_WB_OFF, 0xFF}, - {V31_COLOR_COR_CFG, V31_COLOR_COR_LEN, V31_COLOR_COR_OFF, 0xFF}, - {V31_RGB_G_CFG, V31_RGB_G_LEN, V31_RGB_G_OFF, 0xFF}, -/*20*/ {V31_LA_CFG, V31_LA_LEN, V31_LA_OFF, 0xFF }, - {V31_CHROMA_EN_CFG, V31_CHROMA_EN_LEN, V31_CHROMA_EN_OFF, 0xFF}, - {V31_CHROMA_SUP_CFG, V31_CHROMA_SUP_LEN, V31_CHROMA_SUP_OFF, - 0xFF}, - {V31_MCE_CFG, V31_MCE_LEN, V31_MCE_OFF, 0xFF}, - {V31_SK_ENHAN_CFG}, -/*25*/ {V31_ASF_CFG, V31_ASF_LEN, V31_ASF_OFF, 0xFF}, - {V31_S2Y_CFG, V31_S2Y_LEN, V31_S2Y_OFF, 0xFF}, - {V31_S2CbCr_CFG, V31_S2CbCr_LEN, V31_S2CbCr_OFF, 0xFF}, - {V31_CHROMA_SUBS_CFG, V31_CHROMA_SUBS_LEN, V31_CHROMA_SUBS_OFF, - 0xFF}, - {V31_OUT_CLAMP_CFG, V31_OUT_CLAMP_LEN, V31_OUT_CLAMP_OFF, 0xFF}, -/*30*/ {V31_FRAME_SKIP_CFG, V31_FRAME_SKIP_LEN, V31_FRAME_SKIP_OFF, 0xFF}, - {V31_DUMMY_1}, - {V31_DUMMY_2}, - {V31_DUMMY_3}, - {V31_UPDATE}, -/*35*/ {V31_BL_LVL_UPDATE, V31_BLACK_LEVEL_LEN, V31_BLACK_LEVEL_OFF, - 0xFF}, - {V31_DEMUX_UPDATE, V31_DEMUX_LEN, V31_DEMUX_OFF, 0xFF}, - {V31_DEMOSAIC_1_UPDATE, V31_DEMOSAIC_1_LEN, V31_DEMOSAIC_1_OFF, - 0xFF}, - {V31_DEMOSAIC_2_UPDATE, V31_DEMOSAIC_2_LEN, V31_DEMOSAIC_2_OFF, - 0xFF}, - {V31_FOV_UPDATE, V31_FOV_LEN, V31_FOV_OFF, 0xFF}, -/*40*/ {V31_MAIN_SCALER_UPDATE, V31_MAIN_SCALER_LEN, V31_MAIN_SCALER_OFF, - 0xFF}, - {V31_WB_UPDATE, V31_WB_LEN, V31_WB_OFF, 0xFF}, - {V31_COLOR_COR_UPDATE, V31_COLOR_COR_LEN, V31_COLOR_COR_OFF, - 0xFF}, - {V31_RGB_G_UPDATE, V31_RGB_G_LEN, V31_CHROMA_EN_OFF, 0xFF}, - {V31_LA_UPDATE, V31_LA_LEN, V31_LA_OFF, 0xFF }, -/*45*/ {V31_CHROMA_EN_UPDATE, V31_CHROMA_EN_LEN, V31_CHROMA_EN_OFF, 0xFF}, - {V31_CHROMA_SUP_UPDATE, V31_CHROMA_SUP_LEN, V31_CHROMA_SUP_OFF, - 0xFF}, - {V31_MCE_UPDATE, V31_MCE_LEN, V31_MCE_OFF, 0xFF}, - {V31_SK_ENHAN_UPDATE}, - {V31_S2CbCr_UPDATE, V31_S2CbCr_LEN, V31_S2CbCr_OFF, 0xFF}, -/*50*/ {V31_S2Y_UPDATE, V31_S2Y_LEN, V31_S2Y_OFF, 0xFF}, - {V31_ASF_UPDATE, V31_ASF_UPDATE_LEN, V31_ASF_OFF, 0xFF}, - {V31_FRAME_SKIP_UPDATE}, - {V31_CAMIF_FRAME_UPDATE}, - {V31_STATS_AF_UPDATE, V31_STATS_AF_LEN, V31_STATS_AF_OFF}, -/*55*/ {V31_STATS_AE_UPDATE, V31_STATS_AE_LEN, V31_STATS_AE_OFF}, - {V31_STATS_AWB_UPDATE, V31_STATS_AWB_LEN, V31_STATS_AWB_OFF}, - {V31_STATS_RS_UPDATE, V31_STATS_RS_LEN, V31_STATS_RS_OFF}, - {V31_STATS_CS_UPDATE, V31_STATS_CS_LEN, V31_STATS_CS_OFF}, - {V31_STATS_SKIN_UPDATE}, -/*60*/ {V31_STATS_IHIST_UPDATE, V31_STATS_IHIST_LEN, V31_STATS_IHIST_OFF}, - {V31_DUMMY_4}, - {V31_EPOCH1_ACK}, - {V31_EPOCH2_ACK}, - {V31_START_RECORDING}, -/*65*/ {V31_STOP_RECORDING}, - {V31_DUMMY_5}, - {V31_DUMMY_6}, - {V31_CAPTURE, V31_CAPTURE_LEN, 0xFF}, - {V31_DUMMY_7}, -/*70*/ {V31_STOP}, - {V31_GET_HW_VERSION}, - {V31_GET_FRAME_SKIP_COUNTS}, - {V31_OUTPUT1_BUFFER_ENQ}, - {V31_OUTPUT2_BUFFER_ENQ}, -/*75*/ {V31_OUTPUT3_BUFFER_ENQ}, - {V31_JPEG_OUT_BUF_ENQ}, - {V31_RAW_OUT_BUF_ENQ}, - {V31_RAW_IN_BUF_ENQ}, - {V31_STATS_AF_ENQ}, -/*80*/ {V31_STATS_AE_ENQ}, - {V31_STATS_AWB_ENQ}, - {V31_STATS_RS_ENQ}, - {V31_STATS_CS_ENQ}, - {V31_STATS_SKIN_ENQ}, -/*85*/ {V31_STATS_IHIST_ENQ}, - {V31_DUMMY_8}, - {V31_JPEG_ENC_CFG}, - {V31_DUMMY_9}, - {V31_STATS_AF_START, V31_STATS_AF_LEN, V31_STATS_AF_OFF}, -/*90*/ {V31_STATS_AF_STOP}, - {V31_STATS_AE_START, V31_STATS_AE_LEN, V31_STATS_AE_OFF}, - {V31_STATS_AE_STOP}, - {V31_STATS_AWB_START, V31_STATS_AWB_LEN, V31_STATS_AWB_OFF}, - {V31_STATS_AWB_STOP}, -/*95*/ {V31_STATS_RS_START, V31_STATS_RS_LEN, V31_STATS_RS_OFF}, - {V31_STATS_RS_STOP}, - {V31_STATS_CS_START, V31_STATS_CS_LEN, V31_STATS_CS_OFF}, - {V31_STATS_CS_STOP}, - {V31_STATS_SKIN_START, V31_STATS_IHIST_LEN, - V31_STATS_IHIST_OFF}, -/*100*/ {V31_STATS_SKIN_STOP}, - {V31_STATS_IHIST_START}, - {V31_STATS_IHIST_STOP}, - {V31_DUMMY_10}, - {V31_SYNC_TIMER_SETTING}, -/*105*/ {V31_ASYNC_TIMER_SETTING}, -}; - -static void vfe_io_w(u32 data, unsigned long offset) -{ - writel(data, vfe31_ctrl->vfebase + offset); - wmb(); -} - -static u32 vfe_io_r(unsigned long offset) -{ - uint32_t data = readl(vfe31_ctrl->vfebase + offset); - rmb(); - return data; -} - -static void msm_io_memcpy(void __iomem *dest_addr, - void __iomem *src_addr, u32 len) -{ - int i; - u32 *d = (u32 *) dest_addr; - u32 *s = (u32 *) src_addr; - /* memcpy_toio does not work. Use writel for now */ - for (i = 0; i < len / 4; i++) { - writel(*s++, d++); - wmb(); - } -} - -static void vfe_io_w_axi_out_chan(u32 data, int8_t chan) -{ - uint32_t temp; - vfe_io_w(data, V31_AXI_OUT_OFF + 20 + 24 * chan); - temp = vfe_io_r(V31_AXI_OUT_OFF + 20 + 24 * chan); -} - -static void vfe_msg_get_phy_addr(struct msm_vfe_phy_info *pinfo, - struct vfe_message *data) -{ - pinfo->y_phy = data->_u.msgOut.yBuffer; - pinfo->cbcr_phy = data->_u.msgOut.cbcrBuffer; -} - -static void vfe31_proc_ops(enum VFE31_MESSAGE_ID id, void *msg, size_t len) -{ - struct msm_vfe_resp *rp; - struct vfe_message *vfe_msg_data = NULL; - - rp = vfe31_ctrl->resp->vfe_alloc(sizeof(struct msm_vfe_resp), - vfe31_ctrl->syncdata, GFP_ATOMIC); - if (!rp) { - pr_err("rp: cannot allocate buffer\n"); - return; - } - CDBG("vfe31_proc_ops, msgId = %d\n", id); - rp->evt_msg.type = MSM_CAMERA_MSG; - rp->evt_msg.msg_id = id; - rp->evt_msg.len = len; - rp->evt_msg.data = msg; - vfe_msg_data = (struct vfe_message *)rp->evt_msg.data; - - switch (rp->evt_msg.msg_id) { - case MSG_ID_SNAPSHOT_DONE: - rp->type = VFE_MSG_SNAPSHOT; - break; - - case MSG_ID_OUTPUT_P: - rp->type = VFE_MSG_OUTPUT_P; - rp->phy.output_id = OUTPUT_TYPE_P; - vfe_msg_get_phy_addr(&rp->phy, vfe_msg_data); - break; - - case MSG_ID_OUTPUT_T: - rp->type = VFE_MSG_OUTPUT_T; - rp->phy.output_id = OUTPUT_TYPE_T; - vfe_msg_get_phy_addr(&rp->phy, vfe_msg_data); - break; - - case MSG_ID_OUTPUT_S: - rp->type = VFE_MSG_OUTPUT_S; - rp->phy.output_id = OUTPUT_TYPE_S; - vfe_msg_get_phy_addr(&rp->phy, vfe_msg_data); - break; - - case MSG_ID_OUTPUT_V: - rp->type = VFE_MSG_OUTPUT_V; - rp->phy.output_id = OUTPUT_TYPE_V; - vfe_msg_get_phy_addr(&rp->phy, vfe_msg_data); - break; - - case MSG_ID_STATS_AF: - rp->type = VFE_MSG_STATS_AF; - rp->phy.sbuf_phy = vfe_msg_data->_u.msgStats.buffer; - break; - - case MSG_ID_STATS_AWB: - rp->type = VFE_MSG_STATS_AWB; - rp->phy.sbuf_phy = vfe_msg_data->_u.msgStats.buffer; - break; - - case MSG_ID_STATS_AEC: - rp->type = VFE_MSG_STATS_AEC; - rp->phy.sbuf_phy = vfe_msg_data->_u.msgStats.buffer; - break; - - default: - rp->type = VFE_MSG_GENERAL; - break; - } - vfe31_ctrl->resp->vfe_resp(rp, MSM_CAM_Q_VFE_MSG, vfe31_ctrl->syncdata, - GFP_ATOMIC); -} - -static void vfe_send_outmsg(uint8_t msgid, uint32_t pyaddr, uint32_t pcbcraddr) -{ - struct vfe_message msg; - msg._d = msgid; - msg._u.msgOut.yBuffer = pyaddr; - msg._u.msgOut.cbcrBuffer = pcbcraddr; - vfe31_proc_ops(msgid, &msg, sizeof(struct vfe_message)); -} - -static int vfe31_enable(struct camera_enable_cmd *enable) -{ - return 0; -} - -void vfe_stop(void) -{ - uint8_t axiBusyFlag = true; - unsigned long flags; - - /* for reset hw modules, and send msg when reset_irq comes. */ - spin_lock_irqsave(&vfe31_ctrl->stop_flag_lock, flags); - vfe31_ctrl->stop_ack_pending = TRUE; - spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags); - - /* disable all interrupts. */ - vfe_io_w(VFE_DISABLE_ALL_IRQS, VFE_IRQ_MASK_0); - vfe_io_w(VFE_DISABLE_ALL_IRQS, VFE_IRQ_MASK_1); - - /* clear all pending interrupts*/ - vfe_io_w(VFE_CLEAR_ALL_IRQS, VFE_IRQ_CLEAR_0); - vfe_io_w(VFE_CLEAR_ALL_IRQS, VFE_IRQ_CLEAR_1); - vfe_io_w(1, VFE_IRQ_CMD); - - /* in either continuous or snapshot mode, stop command can be issued - * at any time. stop camif immediately. */ - vfe_io_w(CAMIF_COMMAND_STOP_IMMEDIATELY, VFE_CAMIF_COMMAND); - - /* axi halt command. */ - vfe_io_w(AXI_HALT, VFE_AXI_CMD); - - while (axiBusyFlag) { - if (vfe_io_r(VFE_AXI_STATUS) & 0x1) { - axiBusyFlag = false; - } - } - vfe_io_w(AXI_HALT_CLEAR, VFE_AXI_CMD); - - /* after axi halt, then ok to apply global reset. */ - /* enable reset_ack and async timer interrupt only while - stopping the pipeline.*/ - vfe_io_w(0xf0000000, VFE_IRQ_MASK_0); - vfe_io_w(VFE_IMASK_WHILE_STOPPING_1, VFE_IRQ_MASK_1); - vfe_io_w(VFE_RESET_UPON_STOP_CMD, VFE_GLOBAL_RESET); -} - -static int vfe31_disable(struct camera_enable_cmd *enable, - struct platform_device *dev) -{ - vfe_stop(); - msm_camio_disable(dev); - return 0; -} - -static void vfe31_release(struct platform_device *pdev) -{ - struct msm_sensor_ctrl *sctrl = - &((struct msm_sync *)vfe_syncdata)->sctrl; - struct resource *vfemem, *vfeio; - - if (sctrl) - sctrl->s_release(); - - vfemem = vfe31_ctrl->vfemem; - vfeio = vfe31_ctrl->vfeio; - - kfree(vfe31_ctrl->extdata); - free_irq(vfe31_ctrl->vfeirq, 0); - iounmap(vfe31_ctrl->vfebase); - kfree(vfe31_ctrl); - vfe31_ctrl = NULL; - release_mem_region(vfemem->start, (vfemem->end - vfemem->start) + 1); - msm_camio_disable(pdev); - vfe_syncdata = NULL; -} - -static int vfe31_config_axi(int mode, struct axidata *ad, uint32_t *ao) -{ - int i; - uint32_t *p, *p1, *p2; - struct vfe31_output_ch *outp1 = NULL, *outp2 = NULL; - struct msm_pmem_region *regp1 = NULL, *regp2 = NULL; - - p = ao + 2; - - CDBG("vfe31_config_axi: mode = %d, bufnum1 = %d, bufnum2 = %d\n", - mode, ad->bufnum1, ad->bufnum2); - - switch (mode) { - case OUTPUT_2: - if (ad->bufnum2 != 3) - return -EINVAL; - *p = 0x200; /* preview with wm0 & wm1 */ - - vfe31_ctrl->outpath.out0.ch0 = 0; /* luma */ - vfe31_ctrl->outpath.out0.ch1 = 1; /* chroma */ - regp1 = &(ad->region[ad->bufnum1]); - outp1 = &(vfe31_ctrl->outpath.out0); - vfe31_ctrl->outpath.output_mode |= VFE31_OUTPUT_MODE_PT; - - for (i = 0; i < 2; i++) { - p1 = ao + 6 + i; /* wm0 for y */ - *p1 = (regp1->paddr + regp1->info.y_off); - - p1 = ao + 12 + i; /* wm1 for cbcr */ - *p1 = (regp1->paddr + regp1->info.cbcr_off); - regp1++; - } - outp1->free_buf.available = 1; - outp1->free_buf.paddr = regp1->paddr; - outp1->free_buf.y_off = regp1->info.y_off; - outp1->free_buf.cbcr_off = regp1->info.cbcr_off; - - CDBG("vfe31_config_axi: free_buf paddr = 0x%x, y_off = %d," - " cbcr_off = %d\n", - outp1->free_buf.paddr, outp1->free_buf.y_off, - outp1->free_buf.cbcr_off); - break; - - case OUTPUT_1_AND_2: - /* use wm0& 4 for thumbnail, wm1&5 for main image.*/ - if ((ad->bufnum1 < 1) || (ad->bufnum2 < 1)) - return -EINVAL; - /* at least one frame for snapshot. */ - *p++ = 0x1; /* xbar cfg0 */ - *p = 0x203; /* xbar cfg1 */ - vfe31_ctrl->outpath.out0.ch0 = 0; /* thumbnail luma */ - vfe31_ctrl->outpath.out0.ch1 = 4; /* thumbnail chroma */ - vfe31_ctrl->outpath.out1.ch0 = 1; /* main image luma */ - vfe31_ctrl->outpath.out1.ch1 = 5; /* main image chroma */ - vfe31_ctrl->outpath.output_mode |= - VFE31_OUTPUT_MODE_S; /* main image.*/ - vfe31_ctrl->outpath.output_mode |= - VFE31_OUTPUT_MODE_PT; /* thumbnail. */ - - regp1 = &(ad->region[0]); /* this is thumbnail buffer. */ - /* this is main image buffer. */ - regp2 = &(ad->region[ad->bufnum1]); - outp1 = &(vfe31_ctrl->outpath.out0); - outp2 = &(vfe31_ctrl->outpath.out1); /* snapshot */ - - p1 = ao + 6; /* wm0 ping */ - *p1++ = (regp1->paddr + regp1->info.y_off); - /* this is to duplicate ping address to pong.*/ - *p1 = (regp1->paddr + regp1->info.y_off); - p1 = ao + 30; /* wm4 ping */ - *p1++ = (regp1->paddr + regp1->info.cbcr_off); - /* this is to duplicate ping address to pong.*/ - *p1 = (regp1->paddr + regp1->info.cbcr_off); - p1 = ao + 12; /* wm1 ping */ - *p1++ = (regp2->paddr + regp2->info.y_off); - /* pong = ping,*/ - *p1 = (regp2->paddr + regp2->info.y_off); - p1 = ao + 36; /* wm5 */ - *p1++ = (regp2->paddr + regp2->info.cbcr_off); - *p1 = (regp2->paddr + regp2->info.cbcr_off); - break; - - case OUTPUT_1_AND_3: - /* use wm0& 4 for preview, wm1&5 for video.*/ - if ((ad->bufnum1 < 2) || (ad->bufnum2 < 2)) - return -EINVAL; - *p++ = 0x1; /* xbar cfg0 */ - *p = 0x1a03; /* xbar cfg1 */ - vfe31_ctrl->outpath.out0.ch0 = 0; /* preview luma */ - vfe31_ctrl->outpath.out0.ch1 = 4; /* preview chroma */ - vfe31_ctrl->outpath.out2.ch0 = 1; /* video luma */ - vfe31_ctrl->outpath.out2.ch1 = 5; /* video chroma */ - vfe31_ctrl->outpath.output_mode |= - VFE31_OUTPUT_MODE_V; /* video*/ - vfe31_ctrl->outpath.output_mode |= - VFE31_OUTPUT_MODE_PT; /* preview */ - - regp1 = &(ad->region[0]); /* this is preview buffer. */ - regp2 = &(ad->region[ad->bufnum1]);/* this is video buffer. */ - outp1 = &(vfe31_ctrl->outpath.out0); /* preview */ - outp2 = &(vfe31_ctrl->outpath.out2); /* video */ - - - for (i = 0; i < 2; i++) { - p1 = ao + 6 + i; /* wm0 for y */ - *p1 = (regp1->paddr + regp1->info.y_off); - - p1 = ao + 30 + i; /* wm1 for cbcr */ - *p1 = (regp1->paddr + regp1->info.cbcr_off); - regp1++; - } - - for (i = 0; i < 2; i++) { - p2 = ao + 12 + i; /* wm0 for y */ - *p2 = (regp2->paddr + regp2->info.y_off); - - p2 = ao + 36 + i; /* wm1 for cbcr */ - *p2 = (regp2->paddr + regp2->info.cbcr_off); - regp2++; - } - outp1->free_buf.available = 1; - outp1->free_buf.paddr = regp1->paddr; - outp1->free_buf.y_off = regp1->info.y_off; - outp1->free_buf.cbcr_off = regp1->info.cbcr_off; - - outp2->free_buf.available = 1; - outp2->free_buf.paddr = regp2->paddr; - outp2->free_buf.y_off = regp2->info.y_off; - outp2->free_buf.cbcr_off = regp2->info.cbcr_off; - CDBG("vfe31_config_axi: preview free_buf" - "paddr = 0x%x, y_off = %d, cbcr_off = %d\n", - outp1->free_buf.paddr, outp1->free_buf.y_off, - outp1->free_buf.cbcr_off); - CDBG("vfe31_config_axi: video free_buf" - "paddr = 0x%x,y_off = %d, cbcr_off = %d\n", - outp2->free_buf.paddr, outp2->free_buf.y_off, - outp2->free_buf.cbcr_off); - break; - - case CAMIF_TO_AXI_VIA_OUTPUT_2: /* use wm0 only */ - if (ad->bufnum2 < 1) - return -EINVAL; - CDBG("config axi for raw snapshot.\n"); - *p = 0x60; /* raw snapshot with wm0 */ - vfe31_ctrl->outpath.out1.ch0 = 0; /* raw */ - regp1 = &(ad->region[ad->bufnum1]); - vfe31_ctrl->outpath.output_mode |= VFE31_OUTPUT_MODE_S; - p1 = ao + 6; /* wm0 for y */ - *p1 = (regp1->paddr + regp1->info.y_off); - break; - - default: - break; - } - msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[V31_AXI_OUT_CFG].offset, - ao, vfe31_cmd[V31_AXI_OUT_CFG].length); - return 0; -} - -static void vfe31_reset_internal_variables(void) -{ - unsigned long flags; - - vfe31_ctrl->vfeImaskCompositePacked = 0; - /* state control variables */ - vfe31_ctrl->start_ack_pending = FALSE; - - spin_lock_irqsave(&vfe31_ctrl->stop_flag_lock, flags); - vfe31_ctrl->stop_ack_pending = FALSE; - spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags); - - vfe31_ctrl->reset_ack_pending = FALSE; - - spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags); - vfe31_ctrl->update_ack_pending = FALSE; - spin_unlock_irqrestore(&vfe31_ctrl->update_ack_lock, flags); - - vfe31_ctrl->req_stop_video_rec = FALSE; - vfe31_ctrl->req_start_video_rec = FALSE; - - spin_lock_irqsave(&vfe31_ctrl->state_lock, flags); - vfe31_ctrl->vstate = VFE_STATE_IDLE; - spin_unlock_irqrestore(&vfe31_ctrl->state_lock, flags); - - /* 0 for continuous mode, 1 for snapshot mode */ - vfe31_ctrl->operation_mode = 0; - vfe31_ctrl->outpath.output_mode = 0; - vfe31_ctrl->vfe_capture_count = 0; - - /* this is unsigned 32 bit integer. */ - vfe31_ctrl->vfeFrameId = 0; - vfe31_ctrl->output1Pattern = 0xffffffff; - vfe31_ctrl->output1Period = 31; - vfe31_ctrl->output2Pattern = 0xffffffff; - vfe31_ctrl->output2Period = 31; - vfe31_ctrl->vfeFrameSkipCount = 0; - vfe31_ctrl->vfeFrameSkipPeriod = 31; - - /* Stats control variables. */ - memset(&vfe31_ctrl->afStatsControl, 0, - sizeof(struct vfe_stats_control)); - memset(&vfe31_ctrl->awbStatsControl, 0, - sizeof(struct vfe_stats_control)); - memset(&vfe31_ctrl->aecStatsControl, 0, - sizeof(struct vfe_stats_control)); - memset(&vfe31_ctrl->ihistStatsControl, 0, - sizeof(struct vfe_stats_control)); - memset(&vfe31_ctrl->rsStatsControl, 0, - sizeof(struct vfe_stats_control)); - memset(&vfe31_ctrl->csStatsControl, 0, - sizeof(struct vfe_stats_control)); -} - -static void vfe31_reset(void) -{ - uint32_t vfe_version; - - vfe31_reset_internal_variables(); - vfe_version = vfe_io_r(VFE_VERSION); - CDBG("vfe_version = 0x%x\n", vfe_version); - /* disable all interrupts. vfeImaskLocal is also reset to 0 - * to begin with. */ - vfe_io_w(VFE_DISABLE_ALL_IRQS, VFE_IRQ_MASK_0); - vfe_io_w(VFE_DISABLE_ALL_IRQS, VFE_IRQ_MASK_1); - - /* clear all pending interrupts*/ - vfe_io_w(VFE_CLEAR_ALL_IRQS, VFE_IRQ_CLEAR_0); - vfe_io_w(VFE_CLEAR_ALL_IRQS, VFE_IRQ_CLEAR_1); - vfe_io_w(1, VFE_IRQ_CMD); - - /* enable reset_ack interrupt. */ - vfe_io_w(VFE_IMASK_WHILE_STOPPING_1, VFE_IRQ_MASK_1); - - /* Write to VFE_GLOBAL_RESET_CMD to reset the vfe hardware. Once reset - * is done, hardware interrupt will be generated. VFE ist processes - * the interrupt to complete the function call. Note that the reset - * function is synchronous. */ - vfe_io_w(VFE_RESET_UPON_RESET_CMD, VFE_GLOBAL_RESET); -} - -static int vfe31_operation_config(uint32_t *cmd) -{ - uint32_t *p = cmd; - - vfe31_ctrl->operation_mode = *p; - vfe31_ctrl->stats_comp = *(++p); - - vfe_io_w(*(++p), VFE_CFG_OFF); - vfe_io_w(*(++p), VFE_MODULE_CFG); - vfe_io_w(*(++p), VFE_REALIGN_BUF); - vfe_io_w(*(++p), VFE_CHROMA_UP); - vfe_io_w(*(++p), VFE_STATS_CFG); - return 0; -} - -static uint32_t vfe_stats_awb_buf_init(struct vfe_cmd_stats_buf *in) -{ - uint32_t *ptr = in->statsBuf; - uint32_t addr; - - addr = ptr[0]; - vfe_io_w(addr, VFE_BUS_STATS_AWB_WR_PING_ADDR); - addr = ptr[1]; - vfe_io_w(addr, VFE_BUS_STATS_AWB_WR_PONG_ADDR); - vfe31_ctrl->awbStatsControl.nextFrameAddrBuf = in->statsBuf[2]; - return 0; -} - -static uint32_t vfe_stats_aec_buf_init(struct vfe_cmd_stats_buf *in) -{ - uint32_t *ptr = in->statsBuf; - uint32_t addr; - - addr = ptr[0]; - vfe_io_w(addr, VFE_BUS_STATS_AEC_WR_PING_ADDR); - addr = ptr[1]; - vfe_io_w(addr, VFE_BUS_STATS_AEC_WR_PONG_ADDR); - vfe31_ctrl->aecStatsControl.nextFrameAddrBuf = in->statsBuf[2]; - return 0; -} - -static uint32_t vfe_stats_af_buf_init(struct vfe_cmd_stats_buf *in) -{ - uint32_t *ptr = in->statsBuf; - uint32_t addr; - - addr = ptr[0]; - vfe_io_w(addr, VFE_BUS_STATS_AF_WR_PING_ADDR); - addr = ptr[1]; - vfe_io_w(addr, VFE_BUS_STATS_AF_WR_PONG_ADDR); - vfe31_ctrl->afStatsControl.nextFrameAddrBuf = in->statsBuf[2]; - return 0; -} - -static uint32_t vfe_stats_ihist_buf_init(struct vfe_cmd_stats_buf *in) -{ - uint32_t *ptr = in->statsBuf; - uint32_t addr; - - addr = ptr[0]; - vfe_io_w(addr, VFE_BUS_STATS_HIST_WR_PING_ADDR); - addr = ptr[1]; - vfe_io_w(addr, VFE_BUS_STATS_HIST_WR_PONG_ADDR); - vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf = in->statsBuf[2]; - return 0; -} - -static uint32_t vfe_stats_rs_buf_init(struct vfe_cmd_stats_buf *in) -{ - uint32_t *ptr = in->statsBuf; - uint32_t addr; - - addr = ptr[0]; - vfe_io_w(addr, VFE_BUS_STATS_RS_WR_PING_ADDR); - addr = ptr[1]; - vfe_io_w(addr, VFE_BUS_STATS_RS_WR_PONG_ADDR); - vfe31_ctrl->rsStatsControl.nextFrameAddrBuf = in->statsBuf[2]; - return 0; -} - -static uint32_t vfe_stats_cs_buf_init(struct vfe_cmd_stats_buf *in) -{ - uint32_t *ptr = in->statsBuf; - uint32_t addr; - - addr = ptr[0]; - vfe_io_w(addr, VFE_BUS_STATS_CS_WR_PING_ADDR); - addr = ptr[1]; - vfe_io_w(addr, VFE_BUS_STATS_CS_WR_PONG_ADDR); - vfe31_ctrl->csStatsControl.nextFrameAddrBuf = in->statsBuf[2]; - return 0; -} - -static void vfe31_start_common(void) -{ - unsigned long flags; - - vfe31_ctrl->start_ack_pending = TRUE; - CDBG("VFE opertaion mode = 0x%x.\n", vfe31_ctrl->operation_mode); - CDBG("VFE output path out mode = 0x%x.\n", - vfe31_ctrl->outpath.output_mode); - vfe_io_w(0x00EFE021, VFE_IRQ_MASK_0); - vfe_io_w(VFE_IMASK_WHILE_STOPPING_1, VFE_IRQ_MASK_1); - - vfe_io_w(1, VFE_REG_UPDATE_CMD); - vfe_io_w(1, VFE_CAMIF_COMMAND); - - spin_lock_irqsave(&vfe31_ctrl->state_lock, flags); - vfe31_ctrl->vstate = VFE_STATE_ACTIVE; - spin_unlock_irqrestore(&vfe31_ctrl->state_lock, flags); -} - -static int vfe31_start_recording(void) -{ - vfe31_ctrl->req_start_video_rec = TRUE; - return 0; -} - -static int vfe31_stop_recording(void) -{ - vfe31_ctrl->req_stop_video_rec = TRUE; - return 0; -} - -static int vfe31_capture(uint32_t num_frames_capture) -{ - uint32_t irq_comp_mask = 0; - - /* capture command is valid for both idle and active state. */ - vfe31_ctrl->outpath.out1.capture_cnt = num_frames_capture; - if (vfe31_ctrl->operation_mode == 1) { - vfe31_ctrl->outpath.out0.capture_cnt = num_frames_capture; - } - vfe31_ctrl->vfe_capture_count = num_frames_capture; - irq_comp_mask = vfe_io_r(VFE_IRQ_COMP_MASK); - - if (vfe31_ctrl->operation_mode == 1) { - if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) { - irq_comp_mask |= (0x1 << vfe31_ctrl->outpath.out0.ch0 | - 0x1 << vfe31_ctrl->outpath.out0.ch1); - } - if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) { - irq_comp_mask |= - (0x1 << (vfe31_ctrl->outpath.out1.ch0 + 8) | - 0x1 << (vfe31_ctrl->outpath.out1.ch1 + 8)); - } - if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) { - vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out0.ch0); - vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out0.ch1); - } - if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) { - vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out1.ch0); - vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out1.ch1); - } - } else { /* this is raw snapshot mode. */ - CDBG("config the comp imask for raw snapshot mode.\n"); - if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) { - irq_comp_mask |= - (0x1 << (vfe31_ctrl->outpath.out1.ch0 + 8)); - vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out1.ch0); - } - } - vfe_io_w(irq_comp_mask, VFE_IRQ_COMP_MASK); - vfe_io_r(VFE_IRQ_COMP_MASK); - vfe31_start_common(); - vfe_io_r(VFE_IRQ_COMP_MASK); - /* for debug */ - vfe_io_w(1, 0x18C); - vfe_io_w(1, 0x188); - return 0; -} - -static int vfe31_start(void) -{ - uint32_t irq_comp_mask = 0; - - /* start command now is only good for continuous mode. */ - if (vfe31_ctrl->operation_mode & 1) - return 0; - irq_comp_mask = vfe_io_r(VFE_IRQ_COMP_MASK); - - if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) { - irq_comp_mask |= (0x1 << vfe31_ctrl->outpath.out0.ch0 | - 0x1 << vfe31_ctrl->outpath.out0.ch1); - } - - if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_V) { - irq_comp_mask |= (0x1 << (vfe31_ctrl->outpath.out2.ch0 + 16) | - 0x1 << (vfe31_ctrl->outpath.out2.ch1 + 16)); - } - - vfe_io_w(irq_comp_mask, VFE_IRQ_COMP_MASK); - - if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) { - vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out0.ch0); - vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out0.ch1); - } - vfe31_start_common(); - return 0; -} - -static void vfe31_update(void) -{ - unsigned long flags; - - spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags); - vfe31_ctrl->update_ack_pending = TRUE; - spin_unlock_irqrestore(&vfe31_ctrl->update_ack_lock, flags); - vfe_io_w(1, VFE_REG_UPDATE_CMD); -} - -void vfe31_program_dmi_cfg(enum VFE31_DMI_RAM_SEL bankSel) -{ - /* set bit 8 for auto increment. */ - uint32_t value = VFE_DMI_CFG_DEFAULT; - value += (uint32_t)bankSel; - - vfe_io_w(value, VFE_DMI_CFG); - /* by default, always starts with offset 0. */ - vfe_io_w(0, VFE_DMI_ADDR); -} - -void vfe31_write_gamma_cfg(enum VFE31_DMI_RAM_SEL channel_sel, - const uint32_t *tbl) -{ - int i; - uint32_t value, value1, value2; - - vfe31_program_dmi_cfg(channel_sel); - /* for loop for extracting init table. */ - for (i = 0 ; i < (VFE31_GAMMA_NUM_ENTRIES/2) ; i++) { - value = *tbl++; - value1 = value & 0x0000FFFF; - value2 = (value & 0xFFFF0000)>>16; - vfe_io_w(value1, VFE_DMI_DATA_LO); - vfe_io_w(value2, VFE_DMI_DATA_LO); - } - vfe31_program_dmi_cfg(NO_MEM_SELECTED); -} - -static int vfe31_proc_general(struct msm_vfe31_cmd *cmd) -{ - int i, rc = 0; - uint32_t old_val = 0, new_val = 0; - uint32_t *cmdp = NULL; - uint32_t *cmdp_local = NULL; - - CDBG("vfe31_proc_general: cmdID = %d, length = %d\n", - cmd->id, cmd->length); - switch (cmd->id) { - case V31_RESET: - vfe31_reset(); - break; - case V31_START: - rc = vfe31_start(); - break; - case V31_UPDATE: - vfe31_update(); - break; - case V31_CAPTURE: - rc = vfe31_capture(1); - break; - case V31_START_RECORDING: - rc = vfe31_start_recording(); - break; - case V31_STOP_RECORDING: - rc = vfe31_stop_recording(); - break; - case V31_OPERATION_CFG: - if (cmd->length != V31_OPERATION_CFG_LEN) { - rc = -EINVAL; - goto proc_general_done; - } - cmdp = kmalloc(V31_OPERATION_CFG_LEN, GFP_KERNEL); - if (copy_from_user(cmdp, (void __user *)cmd->value, - V31_OPERATION_CFG_LEN)) { - rc = -EFAULT; - goto proc_general_done; - } - rc = vfe31_operation_config(cmdp); - break; - - case V31_STATS_AE_START: - cmdp = kmalloc(cmd->length, GFP_KERNEL); - if (!cmdp) { - rc = -ENOMEM; - goto proc_general_done; - } - if (copy_from_user(cmdp, (void __user *)cmd->value, - cmd->length)) { - rc = -EFAULT; - goto proc_general_done; - } - old_val = vfe_io_r(VFE_MODULE_CFG); - old_val |= AE_ENABLE_MASK; - vfe_io_w(old_val, VFE_MODULE_CFG); - msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, - cmdp, vfe31_cmd[cmd->id].length); - break; - - case V31_STATS_AF_START: - cmdp = kmalloc(cmd->length, GFP_KERNEL); - if (!cmdp) { - rc = -ENOMEM; - goto proc_general_done; - } - if (copy_from_user(cmdp, (void __user *)cmd->value, - cmd->length)) { - rc = -EFAULT; - goto proc_general_done; - } - old_val = vfe_io_r(VFE_MODULE_CFG); - old_val |= AF_ENABLE_MASK; - vfe_io_w(old_val, VFE_MODULE_CFG); - msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, - cmdp, vfe31_cmd[cmd->id].length); - break; - - case V31_STATS_AWB_START: - cmdp = kmalloc(cmd->length, GFP_KERNEL); - if (!cmdp) { - rc = -ENOMEM; - goto proc_general_done; - } - if (copy_from_user(cmdp, (void __user *)cmd->value, - cmd->length)) { - rc = -EFAULT; - goto proc_general_done; - } - old_val = vfe_io_r(VFE_MODULE_CFG); - old_val |= AWB_ENABLE_MASK; - vfe_io_w(old_val, VFE_MODULE_CFG); - msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, - cmdp, vfe31_cmd[cmd->id].length); - break; - - case V31_STATS_IHIST_START: - cmdp = kmalloc(cmd->length, GFP_KERNEL); - if (!cmdp) { - rc = -ENOMEM; - goto proc_general_done; - } - if (copy_from_user(cmdp, (void __user *)cmd->value, - cmd->length)) { - rc = -EFAULT; - goto proc_general_done; - } - old_val = vfe_io_r(VFE_MODULE_CFG); - old_val |= IHIST_ENABLE_MASK; - vfe_io_w(old_val, VFE_MODULE_CFG); - msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, - cmdp, vfe31_cmd[cmd->id].length); - break; - - - case V31_STATS_RS_START: - cmdp = kmalloc(cmd->length, GFP_KERNEL); - if (!cmdp) { - rc = -ENOMEM; - goto proc_general_done; - } - if (copy_from_user(cmdp, (void __user *)cmd->value, - cmd->length)) { - rc = -EFAULT; - goto proc_general_done; - } - old_val = vfe_io_r(VFE_MODULE_CFG); - old_val |= RS_ENABLE_MASK; - vfe_io_w(old_val, VFE_MODULE_CFG); - msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, - cmdp, vfe31_cmd[cmd->id].length); - break; - - case V31_STATS_CS_START: - cmdp = kmalloc(cmd->length, GFP_KERNEL); - if (!cmdp) { - rc = -ENOMEM; - goto proc_general_done; - } - if (copy_from_user(cmdp, (void __user *)cmd->value, - cmd->length)) { - rc = -EFAULT; - goto proc_general_done; - } - old_val = vfe_io_r(VFE_MODULE_CFG); - old_val |= CS_ENABLE_MASK; - vfe_io_w(old_val, VFE_MODULE_CFG); - msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, - cmdp, vfe31_cmd[cmd->id].length); - break; - - case V31_MCE_UPDATE: - case V31_MCE_CFG: - cmdp = kmalloc(cmd->length, GFP_KERNEL); - if (!cmdp) { - rc = -ENOMEM; - goto proc_general_done; - } - /* Incrementing with 4 so as to point to the 2nd Register as - the 2nd register has the mce_enable bit */ - old_val = vfe_io_r(V31_CHROMA_EN_OFF + 4); - if (copy_from_user(cmdp, (void __user *)cmd->value, - cmd->length)) { - rc = -EFAULT; - goto proc_general_done; - } - cmdp_local = cmdp; - new_val = *cmdp_local; - old_val &= MCE_EN_MASK; - new_val = new_val | old_val; - msm_io_memcpy(vfe31_ctrl->vfebase + V31_CHROMA_EN_OFF + 4, - &new_val, 4); - cmdp_local += 1; - - old_val = vfe_io_r(V31_CHROMA_EN_OFF + 8); - new_val = *cmdp_local; - old_val &= MCE_Q_K_MASK; - new_val = new_val | old_val; - msm_io_memcpy(vfe31_ctrl->vfebase + V31_CHROMA_EN_OFF + 8, - &new_val, 4); - cmdp_local += 1; - msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, - cmdp_local, vfe31_cmd[cmd->id].length); - break; - - case V31_DEMOSAIC_2_UPDATE: /* 38 BPC update */ - case V31_DEMOSAIC_2_CFG: /* 14 BPC config */ - cmdp = kmalloc(cmd->length, GFP_KERNEL); - if (!cmdp) { - rc = -ENOMEM; - goto proc_general_done; - } - if (copy_from_user(cmdp, (void __user *)cmd->value, - cmd->length)) { - rc = -EFAULT; - goto proc_general_done; - } - cmdp_local = cmdp; - new_val = *cmdp_local; - - old_val = vfe_io_r(V31_DEMOSAIC_0_OFF); - old_val &= BPC_MASK; - - new_val = new_val | old_val; - *cmdp_local = new_val; - msm_io_memcpy(vfe31_ctrl->vfebase + V31_DEMOSAIC_0_OFF, - cmdp_local, 4); - cmdp_local += 1; - msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, - cmdp_local, vfe31_cmd[cmd->id].length); - break; - - case V31_DEMOSAIC_1_UPDATE:/* 37 ABF update */ - case V31_DEMOSAIC_1_CFG: /* 13 ABF config */ - cmdp = kmalloc(cmd->length, GFP_KERNEL); - if (!cmdp) { - rc = -ENOMEM; - goto proc_general_done; - } - if (copy_from_user(cmdp, (void __user *)cmd->value, - cmd->length)) { - rc = -EFAULT; - goto proc_general_done; - } - cmdp_local = cmdp; - new_val = *cmdp_local; - - old_val = vfe_io_r(V31_DEMOSAIC_0_OFF); - old_val &= ABF_MASK; - new_val = new_val | old_val; - *cmdp_local = new_val; - - msm_io_memcpy(vfe31_ctrl->vfebase + V31_DEMOSAIC_0_OFF, - cmdp_local, 4); - - cmdp_local += 1; - msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, - cmdp_local, vfe31_cmd[cmd->id].length); - break; - - case V31_ROLL_OFF_CFG: - cmdp = kmalloc(cmd->length, GFP_KERNEL); - if (!cmdp) { - rc = -ENOMEM; - goto proc_general_done; - } - if (copy_from_user(cmdp, (void __user *)cmd->value, - cmd->length)) { - rc = -EFAULT; - goto proc_general_done; - } - cmdp_local = cmdp; - msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, - cmdp_local, 16); - cmdp_local += 4; - vfe31_program_dmi_cfg(ROLLOFF_RAM); - /* for loop for extrcting init table. */ - for (i = 0 ; i < (VFE31_ROLL_OFF_INIT_TABLE_SIZE * 2) ; i++) { - vfe_io_w(*cmdp_local, VFE_DMI_DATA_LO); - cmdp_local++; - } - CDBG("done writing init table\n"); - /* by default, always starts with offset 0. */ - vfe_io_w(LENS_ROLL_OFF_DELTA_TABLE_OFFSET, VFE_DMI_ADDR); - /* for loop for extracting delta table. */ - for (i = 0 ; i < (VFE31_ROLL_OFF_DELTA_TABLE_SIZE * 2) ; i++) { - vfe_io_w(*cmdp_local, VFE_DMI_DATA_LO); - cmdp_local++; - } - vfe31_program_dmi_cfg(NO_MEM_SELECTED); - break; - - case V31_LA_CFG: - case V31_LA_UPDATE: - cmdp = kmalloc(cmd->length, GFP_KERNEL); - if (!cmdp) { - rc = -ENOMEM; - goto proc_general_done; - } - if (copy_from_user(cmdp, (void __user *)cmd->value, - cmd->length)) { - rc = -EFAULT; - goto proc_general_done; - } - msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, - cmdp, vfe31_cmd[cmd->id].length); - - /* If the value is 0x00 then write in to LUT bank0 - if the value is 0x01 then write in to LUT bank1 */ - if (*cmdp == 0x0) - vfe31_program_dmi_cfg(LUMA_ADAPT_LUT_RAM_BANK0); - else - vfe31_program_dmi_cfg(LUMA_ADAPT_LUT_RAM_BANK1); - cmdp += 1; - /* for loop for extracting init table. */ - for (i = 0 ; i < VFE31_LA_TABLE_LENGTH ; i++) { - vfe_io_w(*cmdp, VFE_DMI_DATA_LO); - cmdp++; - } - vfe31_program_dmi_cfg(NO_MEM_SELECTED); - cmdp -= 1; - break; - - case V31_RGB_G_CFG: - cmdp = kmalloc(cmd->length, GFP_KERNEL); - if (!cmdp) { - rc = -ENOMEM; - goto proc_general_done; - } - if (copy_from_user(cmdp, (void __user *)cmd->value, - cmd->length)) { - rc = -EFAULT; - goto proc_general_done; - } - msm_io_memcpy(vfe31_ctrl->vfebase + V31_RGB_G_OFF, cmdp, 4); - cmdp += 1; - vfe31_write_gamma_cfg(RGBLUT_RAM_CH0_BANK0 , cmdp); - vfe31_write_gamma_cfg(RGBLUT_RAM_CH1_BANK0 , cmdp); - vfe31_write_gamma_cfg(RGBLUT_RAM_CH2_BANK0 , cmdp); - cmdp -= 1; - break; - - case V31_RGB_G_UPDATE: - cmdp = kmalloc(cmd->length, GFP_KERNEL); - if (!cmdp) { - rc = -ENOMEM; - goto proc_general_done; - } - if (copy_from_user(cmdp, (void __user *)cmd->value, - cmd->length)) { - rc = -EFAULT; - goto proc_general_done; - } - - msm_io_memcpy(vfe31_ctrl->vfebase + V31_RGB_G_OFF, cmdp, 4); - old_val = *cmdp; - cmdp += 1; - - if (old_val) { - vfe31_write_gamma_cfg(RGBLUT_RAM_CH0_BANK1, cmdp); - vfe31_write_gamma_cfg(RGBLUT_RAM_CH1_BANK1, cmdp); - vfe31_write_gamma_cfg(RGBLUT_RAM_CH2_BANK1, cmdp); - } else { - vfe31_write_gamma_cfg(RGBLUT_RAM_CH0_BANK0, cmdp); - vfe31_write_gamma_cfg(RGBLUT_RAM_CH1_BANK0, cmdp); - vfe31_write_gamma_cfg(RGBLUT_RAM_CH2_BANK0, cmdp); - } - cmdp -= 1; - break; - - case V31_STATS_AWB_STOP: - old_val = vfe_io_r(VFE_MODULE_CFG); - old_val &= ~AWB_ENABLE_MASK; - vfe_io_w(old_val, VFE_MODULE_CFG); - break; - - case V31_STATS_AE_STOP: - old_val = vfe_io_r(VFE_MODULE_CFG); - old_val &= ~AE_ENABLE_MASK; - vfe_io_w(old_val, VFE_MODULE_CFG); - break; - - case V31_STATS_AF_STOP: - old_val = vfe_io_r(VFE_MODULE_CFG); - old_val &= ~AF_ENABLE_MASK; - vfe_io_w(old_val, VFE_MODULE_CFG); - break; - - case V31_STATS_IHIST_STOP: - old_val = vfe_io_r(VFE_MODULE_CFG); - old_val &= ~IHIST_ENABLE_MASK; - vfe_io_w(old_val, VFE_MODULE_CFG); - break; - - case V31_STATS_RS_STOP: - old_val = vfe_io_r(VFE_MODULE_CFG); - old_val &= ~RS_ENABLE_MASK; - vfe_io_w(old_val, VFE_MODULE_CFG); - break; - - case V31_STATS_CS_STOP: - old_val = vfe_io_r(VFE_MODULE_CFG); - old_val &= ~CS_ENABLE_MASK; - vfe_io_w(old_val, VFE_MODULE_CFG); - break; - - case V31_STOP: - vfe_stop(); - break; - - default: - if (cmd->length != vfe31_cmd[cmd->id].length) - return -EINVAL; - - cmdp = kmalloc(vfe31_cmd[cmd->id].length, GFP_KERNEL); - if (!cmdp) { - rc = -ENOMEM; - goto proc_general_done; - } - - CHECKED_COPY_FROM_USER(cmdp); - msm_io_memcpy(vfe31_ctrl->vfebase + vfe31_cmd[cmd->id].offset, - cmdp, vfe31_cmd[cmd->id].length); - break; - } - -proc_general_done: - kfree(cmdp); - - return rc; -} - -static void vfe31_stats_af_ack(struct vfe_cmd_stats_ack *pAck) -{ - unsigned long flags; - spin_lock_irqsave(&vfe31_ctrl->af_ack_lock, flags); - vfe31_ctrl->afStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf; - vfe31_ctrl->afStatsControl.ackPending = FALSE; - spin_unlock_irqrestore(&vfe31_ctrl->af_ack_lock, flags); -} - -static void vfe31_stats_awb_ack(struct vfe_cmd_stats_ack *pAck) -{ - unsigned long flags; - spin_lock_irqsave(&vfe31_ctrl->awb_ack_lock, flags); - vfe31_ctrl->awbStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf; - vfe31_ctrl->awbStatsControl.ackPending = FALSE; - spin_unlock_irqrestore(&vfe31_ctrl->awb_ack_lock, flags); -} - -static void vfe31_stats_aec_ack(struct vfe_cmd_stats_ack *pAck) -{ - unsigned long flags; - spin_lock_irqsave(&vfe31_ctrl->aec_ack_lock, flags); - vfe31_ctrl->aecStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf; - vfe31_ctrl->aecStatsControl.ackPending = FALSE; - spin_unlock_irqrestore(&vfe31_ctrl->aec_ack_lock, flags); -} - -static void vfe31_stats_ihist_ack(struct vfe_cmd_stats_ack *pAck) -{ - unsigned long flags; - spin_lock_irqsave(&vfe31_ctrl->ihist_ack_lock, flags); - vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf; - vfe31_ctrl->ihistStatsControl.ackPending = FALSE; - spin_unlock_irqrestore(&vfe31_ctrl->ihist_ack_lock, flags); -} - -static void vfe31_stats_rs_ack(struct vfe_cmd_stats_ack *pAck) -{ - unsigned long flags; - spin_lock_irqsave(&vfe31_ctrl->rs_ack_lock, flags); - vfe31_ctrl->rsStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf; - vfe31_ctrl->rsStatsControl.ackPending = FALSE; - spin_unlock_irqrestore(&vfe31_ctrl->rs_ack_lock, flags); -} - -static void vfe31_stats_cs_ack(struct vfe_cmd_stats_ack *pAck) -{ - unsigned long flags; - spin_lock_irqsave(&vfe31_ctrl->cs_ack_lock, flags); - vfe31_ctrl->csStatsControl.nextFrameAddrBuf = pAck->nextStatsBuf; - vfe31_ctrl->csStatsControl.ackPending = FALSE; - spin_unlock_irqrestore(&vfe31_ctrl->cs_ack_lock, flags); -} - -static int vfe31_config(struct msm_vfe_cfg_cmd *cmd, void *data) -{ - struct msm_vfe31_cmd vfecmd; - long rc = 0; - uint32_t i = 0; - struct vfe_cmd_stats_buf *scfg = NULL; - struct msm_pmem_region *regptr = NULL; - struct vfe_cmd_stats_ack *sack = NULL; - - if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE && - cmd->cmd_type != CMD_STATS_AEC_BUF_RELEASE && - cmd->cmd_type != CMD_STATS_AWB_BUF_RELEASE && - cmd->cmd_type != CMD_STATS_IHIST_BUF_RELEASE && - cmd->cmd_type != CMD_STATS_RS_BUF_RELEASE && - cmd->cmd_type != CMD_STATS_CS_BUF_RELEASE && - cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) { - if (copy_from_user(&vfecmd, (void __user *)cmd->value, - sizeof(vfecmd))) { - pr_err("%s %d: copy_from_user failed\n", __func__, - __LINE__); - return -EFAULT; - } - } else if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE) { - /* This must be stats release. */ - if (!data) - return -EFAULT; - sack = kmalloc(sizeof(struct vfe_cmd_stats_ack), GFP_KERNEL); - if (!sack) - return -ENOMEM; - sack->nextStatsBuf = *(uint32_t *)data; - } - - CDBG("%s: cmdType = %d\n", __func__, cmd->cmd_type); - - switch (cmd->cmd_type) { - case CMD_GENERAL: - rc = vfe31_proc_general(&vfecmd); - break; - case CMD_FRAME_BUF_RELEASE: { - struct msm_frame *b; - unsigned long p; - struct vfe31_free_buf *fbuf = NULL; - if (!data) - return -EFAULT; - - b = (struct msm_frame *)(cmd->value); - p = *(unsigned long *)data; - - CDBG("CMD_FRAME_BUF_RELEASE b->path = %d\n", b->path); - - if (b->path & OUTPUT_TYPE_P) { - CDBG("CMD_FRAME_BUF_RELEASE got free buffer\n"); - fbuf = &vfe31_ctrl->outpath.out0.free_buf; - } else if (b->path & OUTPUT_TYPE_S) { - fbuf = &vfe31_ctrl->outpath.out1.free_buf; - } else if (b->path & OUTPUT_TYPE_V) { - fbuf = &vfe31_ctrl->outpath.out2.free_buf; - } else - return -EFAULT; - - fbuf->paddr = p; - fbuf->y_off = b->y_off; - fbuf->cbcr_off = b->cbcr_off; - fbuf->available = 1; - } - break; - - case CMD_SNAP_BUF_RELEASE: - break; - case CMD_STATS_AEC_BUF_RELEASE: - vfe31_stats_aec_ack(sack); - break; - case CMD_STATS_AF_BUF_RELEASE: - vfe31_stats_af_ack(sack); - break; - case CMD_STATS_AWB_BUF_RELEASE: - vfe31_stats_awb_ack(sack); - break; - case CMD_STATS_IHIST_BUF_RELEASE: - vfe31_stats_ihist_ack(sack); - break; - case CMD_STATS_RS_BUF_RELEASE: - vfe31_stats_rs_ack(sack); - break; - case CMD_STATS_CS_BUF_RELEASE: - vfe31_stats_cs_ack(sack); - break; - - case CMD_AXI_CFG_PREVIEW: { - struct axidata *axid; - uint32_t *axio = NULL; - axid = data; - if (!axid) - return -EFAULT; - axio = kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length, GFP_KERNEL); - if (!axio) - return -ENOMEM; - - if (copy_from_user(axio, (void __user *)vfecmd.value, - vfe31_cmd[V31_AXI_OUT_CFG].length)) { - kfree(axio); - return -EFAULT; - } - vfe31_config_axi(OUTPUT_2, axid, axio); - kfree(axio); - } - break; - - case CMD_RAW_PICT_AXI_CFG: { - struct axidata *axid; - uint32_t *axio = NULL; - axid = data; - if (!axid) - return -EFAULT; - axio = kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length, GFP_KERNEL); - if (!axio) - return -ENOMEM; - - if (copy_from_user(axio, (void __user *)vfecmd.value, - vfe31_cmd[V31_AXI_OUT_CFG].length)) { - kfree(axio); - return -EFAULT; - } - vfe31_config_axi(CAMIF_TO_AXI_VIA_OUTPUT_2, axid, axio); - kfree(axio); - } - break; - - case CMD_AXI_CFG_SNAP: { - struct axidata *axid; - uint32_t *axio = NULL; - axid = data; - if (!axid) - return -EFAULT; - axio = kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length, GFP_KERNEL); - if (!axio) - return -ENOMEM; - - if (copy_from_user(axio, (void __user *)vfecmd.value, - vfe31_cmd[V31_AXI_OUT_CFG].length)) { - kfree(axio); - return -EFAULT; - } - vfe31_config_axi(OUTPUT_1_AND_2, axid, axio); - kfree(axio); - } - break; - - case CMD_AXI_CFG_VIDEO: { - struct axidata *axid; - uint32_t *axio = NULL; - axid = data; - if (!axid) - return -EFAULT; - axio = kmalloc(vfe31_cmd[V31_AXI_OUT_CFG].length, GFP_KERNEL); - if (!axio) - return -ENOMEM; - - if (copy_from_user(axio, (void __user *)vfecmd.value, - vfe31_cmd[V31_AXI_OUT_CFG].length)) { - kfree(axio); - return -EFAULT; - } - vfe31_config_axi(OUTPUT_1_AND_3, axid, axio); - kfree(axio); - } - break; - - case CMD_STATS_AF_ENABLE: - case CMD_STATS_AWB_ENABLE: - case CMD_STATS_IHIST_ENABLE: - case CMD_STATS_RS_ENABLE: - case CMD_STATS_CS_ENABLE: - case CMD_STATS_AEC_ENABLE: { - struct axidata *axid; - axid = (struct axidata *)data; - if (!axid) - return -EFAULT; - - scfg = kmalloc(sizeof(struct vfe_cmd_stats_buf), GFP_KERNEL); - if (!scfg) - return -ENOMEM; - regptr = axid->region; - if (axid->bufnum1 > 0) { - for (i = 0; i < axid->bufnum1; i++) { - scfg->statsBuf[i] = (uint32_t)(regptr->paddr); - regptr++; - } - } - /* individual */ - switch (cmd->cmd_type) { - case CMD_STATS_AEC_ENABLE: - rc = vfe_stats_aec_buf_init(scfg); - break; - case CMD_STATS_AF_ENABLE: - rc = vfe_stats_af_buf_init(scfg); - break; - case CMD_STATS_AWB_ENABLE: - rc = vfe_stats_awb_buf_init(scfg); - break; - case CMD_STATS_IHIST_ENABLE: - rc = vfe_stats_ihist_buf_init(scfg); - break; - case CMD_STATS_RS_ENABLE: - rc = vfe_stats_rs_buf_init(scfg); - break; - case CMD_STATS_CS_ENABLE: - rc = vfe_stats_cs_buf_init(scfg); - break; - } - } - - default: - rc = -EINVAL; - break; - } - kfree(scfg); - kfree(sack); - CDBG("%s done: rc = %d\n", __func__, (int) rc); - return rc; -} - -static inline void vfe31_read_irq_status(struct vfe31_irq_status *out) -{ - memset(out, 0, sizeof(struct vfe31_irq_status)); - out->vfeIrqStatus0 = vfe_io_r(VFE_IRQ_STATUS_0); - out->vfeIrqStatus1 = vfe_io_r(VFE_IRQ_STATUS_1); - out->camifStatus = vfe_io_r(VFE_CAMIF_STATUS); - CDBG("camifStatus = 0x%x\n", out->camifStatus); -} - -static void vfe31_send_msg_no_payload(enum VFE31_MESSAGE_ID id) -{ - struct vfe_message msg; - - CDBG("vfe31_send_msg_no_payload\n"); - msg._d = id; - vfe31_proc_ops(id, &msg, 0); -} - -static void vfe31_process_reg_update_irq(void) -{ - unsigned long flags; - uint32_t temp; - - if (vfe31_ctrl->req_start_video_rec) { - if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_V) { - vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out2.ch0); - vfe_io_w_axi_out_chan(1, vfe31_ctrl->outpath.out2.ch1); - } - vfe31_ctrl->req_start_video_rec = FALSE; - CDBG("start video triggered .\n"); - } else if (vfe31_ctrl->req_stop_video_rec) { - if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_V) { - vfe_io_w_axi_out_chan(0, vfe31_ctrl->outpath.out2.ch0); - vfe_io_w_axi_out_chan(0, vfe31_ctrl->outpath.out2.ch1); - } - vfe31_ctrl->req_stop_video_rec = FALSE; - CDBG("stop video triggered .\n"); - } - if (vfe31_ctrl->start_ack_pending == TRUE) { - vfe31_send_msg_no_payload(MSG_ID_START_ACK); - vfe31_ctrl->start_ack_pending = FALSE; - } else { - spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags); - if (vfe31_ctrl->update_ack_pending == TRUE) { - spin_unlock_irqrestore( - &vfe31_ctrl->update_ack_lock, flags); - vfe31_send_msg_no_payload(MSG_ID_UPDATE_ACK); - spin_lock_irqsave(&vfe31_ctrl->update_ack_lock, flags); - vfe31_ctrl->update_ack_pending = FALSE; - spin_unlock_irqrestore( - &vfe31_ctrl->update_ack_lock, flags); - } else { - spin_unlock_irqrestore( - &vfe31_ctrl->update_ack_lock, flags); - } - } - if (vfe31_ctrl->operation_mode & 1) { /* in snapshot mode */ - /* later we need to add check for live snapshot mode. */ - vfe31_ctrl->vfe_capture_count--; - /* if last frame to be captured: */ - if (vfe31_ctrl->vfe_capture_count == 0) { - /* stop the bus output: write master enable = 0*/ - if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_PT) { - vfe_io_w_axi_out_chan(0, - vfe31_ctrl->outpath.out0.ch0); - vfe_io_w_axi_out_chan(0, - vfe31_ctrl->outpath.out0.ch1); - } - if (vfe31_ctrl->outpath.output_mode & VFE31_OUTPUT_MODE_S) { - vfe_io_w_axi_out_chan(0, - vfe31_ctrl->outpath.out1.ch0); - vfe_io_w_axi_out_chan(0, - vfe31_ctrl->outpath.out1.ch1); - } - vfe_io_w(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY, - VFE_CAMIF_COMMAND); - - temp = vfe_io_r(VFE_CAMIF_COMMAND); - /* then do reg_update. */ - vfe_io_w(1, VFE_REG_UPDATE_CMD); - } - } /* if snapshot mode. */ -} - -static void vfe31_set_default_reg_values(void) -{ - vfe_io_w(0x800080, VFE_DEMUX_GAIN_0); - vfe_io_w(0x800080, VFE_DEMUX_GAIN_1); - vfe_io_w(0xFFFFF, VFE_CGC_OVERRIDE); - - /* default frame drop period and pattern */ - vfe_io_w(0x1f, VFE_FRAMEDROP_ENC_Y_CFG); - vfe_io_w(0x1f, VFE_FRAMEDROP_ENC_CBCR_CFG); - vfe_io_w(0xFFFFFFFF, VFE_FRAMEDROP_ENC_Y_PATTERN); - vfe_io_w(0xFFFFFFFF, VFE_FRAMEDROP_ENC_CBCR_PATTERN); - vfe_io_w(0x1f, VFE_FRAMEDROP_VIEW_Y); - vfe_io_w(0x1f, VFE_FRAMEDROP_VIEW_CBCR); - vfe_io_w(0xFFFFFFFF, VFE_FRAMEDROP_VIEW_Y_PATTERN); - vfe_io_w(0xFFFFFFFF, VFE_FRAMEDROP_VIEW_CBCR_PATTERN); - vfe_io_w(0, VFE_CLAMP_MIN); - vfe_io_w(0xFFFFFF, VFE_CLAMP_MAX); - - /* stats UB config */ - vfe_io_w(0x3900007, VFE_BUS_STATS_AEC_UB_CFG); - vfe_io_w(0x3980007, VFE_BUS_STATS_AF_UB_CFG); - vfe_io_w(0x3A0000F, VFE_BUS_STATS_AWB_UB_CFG); - vfe_io_w(0x3B00007, VFE_BUS_STATS_RS_UB_CFG); - vfe_io_w(0x3B8001F, VFE_BUS_STATS_CS_UB_CFG); - vfe_io_w(0x3D8001F, VFE_BUS_STATS_HIST_UB_CFG); - vfe_io_w(0x3F80007, VFE_BUS_STATS_SKIN_UB_CFG); -} - -static void vfe31_process_reset_irq(void) -{ - unsigned long flags; - - spin_lock_irqsave(&vfe31_ctrl->state_lock, flags); - vfe31_ctrl->vstate = VFE_STATE_IDLE; - spin_unlock_irqrestore(&vfe31_ctrl->state_lock, flags); - - spin_lock_irqsave(&vfe31_ctrl->stop_flag_lock, flags); - if (vfe31_ctrl->stop_ack_pending) { - vfe31_ctrl->stop_ack_pending = FALSE; - spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags); - vfe31_send_msg_no_payload(MSG_ID_STOP_ACK); - } else { - spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags); - /* this is from reset command. */ - vfe31_set_default_reg_values(); - - /* reload all write masters. (frame & line)*/ - vfe_io_w(0x7FFF, VFE_BUS_CMD); - vfe31_send_msg_no_payload(MSG_ID_RESET_ACK); - } -} - -static void vfe31_process_camif_sof_irq(void) -{ - uint32_t temp; - - if (vfe31_ctrl->operation_mode == 3) { /* in raw snapshot mode */ - if (vfe31_ctrl->start_ack_pending) { - vfe31_send_msg_no_payload(MSG_ID_START_ACK); - vfe31_ctrl->start_ack_pending = FALSE; - } - vfe31_ctrl->vfe_capture_count--; - /* if last frame to be captured: */ - if (vfe31_ctrl->vfe_capture_count == 0) { - vfe_io_w(CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY, VFE_CAMIF_COMMAND); - temp = vfe_io_r(VFE_CAMIF_COMMAND); - } - } /* if raw snapshot mode. */ - - vfe31_ctrl->vfeFrameId++; - CDBG("camif_sof_irq, frameId = %d\n", vfe31_ctrl->vfeFrameId); -} - -static void vfe31_process_output_path_irq_0(void) -{ - uint32_t ping_pong; - uint32_t pyaddr, pcbcraddr; - uint8_t out_bool = 0; - - /* we render frames in the following conditions: - 1. Continuous mode and the free buffer is avaialable. - 2. In snapshot shot mode, free buffer is not always available. - when pending snapshot count is <=1, then no need to use - free buffer. - */ - out_bool = - ((vfe31_ctrl->operation_mode & 1) && - (vfe31_ctrl->vfe_capture_count <= 1)) || - (vfe31_ctrl->outpath.out0.free_buf.available); - if (out_bool) { - ping_pong = vfe_io_r(VFE_BUS_PING_PONG_STATUS); - - /* Y channel */ - pyaddr = vfe31_get_ch_addr(ping_pong, - vfe31_ctrl->outpath.out0.ch0); - /* Chroma channel */ - pcbcraddr = vfe31_get_ch_addr(ping_pong, - vfe31_ctrl->outpath.out0.ch1); - - CDBG("output path 0, pyaddr = 0x%x, pcbcraddr = 0x%x\n", - pyaddr, pcbcraddr); - if (vfe31_ctrl->outpath.out0.free_buf.available) { - /* Y channel */ - vfe31_put_ch_addr(ping_pong, - vfe31_ctrl->outpath.out0.ch0, - vfe31_ctrl->outpath.out0.free_buf.paddr + - vfe31_ctrl->outpath.out0.free_buf.y_off); - /* Chroma channel */ - vfe31_put_ch_addr(ping_pong, - vfe31_ctrl->outpath.out0.ch1, - vfe31_ctrl->outpath.out0.free_buf.paddr + - vfe31_ctrl->outpath.out0.free_buf.cbcr_off); - vfe31_ctrl->outpath.out0.free_buf.available = 0; - } - if (vfe31_ctrl->operation_mode & 1) { - /* will add message for multi-shot. */ - vfe31_ctrl->outpath.out0.capture_cnt--; - } else { - /* always send message for continous mode. */ - /* if continuous mode, this is for display. (preview) */ - vfe_send_outmsg(MSG_ID_OUTPUT_P, pyaddr, pcbcraddr); - } - } else { - vfe31_ctrl->outpath.out0.frame_drop_cnt++; - CDBG("path_irq_0 - no free buffer!\n"); - } -} - -static void vfe31_process_output_path_irq_1(void) -{ - uint32_t ping_pong; - uint32_t pyaddr, pcbcraddr; - /* this must be snapshot main image output. */ - uint8_t out_bool; - - /* we render frames in the following conditions: - 1. Continuous mode and the free buffer is avaialable. - 2. In snapshot shot mode, free buffer is not always available. - -- when pending snapshot count is <=1, then no need to use - free buffer. - */ - out_bool = - ((vfe31_ctrl->operation_mode & 1) && - (vfe31_ctrl->vfe_capture_count <= 1)) || - (vfe31_ctrl->outpath.out1.free_buf.available); - if (out_bool) { - ping_pong = vfe_io_r(VFE_BUS_PING_PONG_STATUS); - - /* Y channel */ - pyaddr = vfe31_get_ch_addr(ping_pong, - vfe31_ctrl->outpath.out1.ch0); - /* Chroma channel */ - pcbcraddr = vfe31_get_ch_addr(ping_pong, - vfe31_ctrl->outpath.out1.ch1); - - CDBG("snapshot main, pyaddr = 0x%x, pcbcraddr = 0x%x\n", - pyaddr, pcbcraddr); - if (vfe31_ctrl->outpath.out1.free_buf.available) { - /* Y channel */ - vfe31_put_ch_addr(ping_pong, - vfe31_ctrl->outpath.out1.ch0, - vfe31_ctrl->outpath.out1.free_buf.paddr + - vfe31_ctrl->outpath.out1.free_buf.y_off); - /* Chroma channel */ - vfe31_put_ch_addr(ping_pong, - vfe31_ctrl->outpath.out1.ch1, - vfe31_ctrl->outpath.out1.free_buf.paddr + - vfe31_ctrl->outpath.out1.free_buf.cbcr_off); - vfe31_ctrl->outpath.out1.free_buf.available = 0; - } - - vfe31_ctrl->outpath.out1.capture_cnt--; - } else { - vfe31_ctrl->outpath.out1.frame_drop_cnt++; - CDBG("path_irq_1 - no free buffer!\n"); - } -} - -static void vfe31_process_output_path_irq_2(void) -{ - uint32_t ping_pong; - uint32_t pyaddr, pcbcraddr; - uint8_t out_bool; - - /* we render frames in the following conditions: - 1. Continuous mode and the free buffer is avaialable. - 2. In snapshot shot mode, free buffer is not always available. - -- when pending snapshot count is <=1, then no need to use - free buffer. - */ - out_bool = - ((vfe31_ctrl->operation_mode & 1) && - (vfe31_ctrl->vfe_capture_count <= 1)) || - (vfe31_ctrl->outpath.out2.free_buf.available); - if (out_bool) { - ping_pong = vfe_io_r(VFE_BUS_PING_PONG_STATUS); - - /* Y channel */ - pyaddr = vfe31_get_ch_addr(ping_pong, - vfe31_ctrl->outpath.out2.ch0); - /* Chroma channel */ - pcbcraddr = vfe31_get_ch_addr(ping_pong, - vfe31_ctrl->outpath.out2.ch1); - - CDBG("video output, pyaddr = 0x%x, pcbcraddr = 0x%x\n", - pyaddr, pcbcraddr); - - if (vfe31_ctrl->outpath.out2.free_buf.available) { - /* Y channel */ - vfe31_put_ch_addr(ping_pong, - vfe31_ctrl->outpath.out2.ch0, - vfe31_ctrl->outpath.out2.free_buf.paddr + - vfe31_ctrl->outpath.out2.free_buf.y_off); - /* Chroma channel */ - vfe31_put_ch_addr(ping_pong, - vfe31_ctrl->outpath.out2.ch1, - vfe31_ctrl->outpath.out2.free_buf.paddr + - vfe31_ctrl->outpath.out2.free_buf.cbcr_off); - vfe31_ctrl->outpath.out2.free_buf.available = 0; - } - vfe_send_outmsg(MSG_ID_OUTPUT_V, pyaddr, pcbcraddr); - } else { - vfe31_ctrl->outpath.out2.frame_drop_cnt++; - CDBG("path_irq_2 - no free buffer!\n"); - } -} - -static void vfe31_process_stats_comb_irq(uint32_t *irqstatus) -{ - return; -} - -static uint32_t vfe31_process_stats_irq_common(uint32_t statsNum, - uint32_t newAddr) { - uint32_t pingpongStatus; - uint32_t returnAddr; - uint32_t pingpongAddrOffset; - - /* must be 0=ping, 1=pong */ - pingpongStatus = - (vfe_io_r(VFE_BUS_PING_PONG_STATUS) - & ((uint32_t)(1<<(statsNum + 7)))) >> (statsNum + 7); - /* stats bits starts at 7 */ - CDBG("statsNum %d, pingpongStatus %d\n", statsNum, pingpongStatus); - pingpongAddrOffset = - ((uint32_t)(VFE_BUS_STATS_PING_PONG_BASE)) + - (3*statsNum)*4 + (1-pingpongStatus)*4; - returnAddr = vfe_io_r(pingpongAddrOffset); - vfe_io_w(newAddr, pingpongAddrOffset); - return returnAddr; -} - -static void vfe_send_stats_msg(uint32_t bufAddress, uint32_t statsNum) -{ - struct vfe_message msg; - - /* fill message with right content. */ - msg._u.msgStats.frameCounter = vfe31_ctrl->vfeFrameId; - msg._u.msgStats.buffer = bufAddress; - - switch (statsNum) { - case statsAeNum: - msg._d = MSG_ID_STATS_AEC; - break; - case statsAfNum: - msg._d = MSG_ID_STATS_AF; - break; - case statsAwbNum: - msg._d = MSG_ID_STATS_AWB; - break; - case statsIhistNum: - msg._d = MSG_ID_STATS_IHIST; - break; - case statsRsNum: - msg._d = MSG_ID_STATS_RS; - break; - case statsCsNum: - msg._d = MSG_ID_STATS_CS; - break; - default: - goto stats_done; - } - - vfe31_proc_ops(msg._d, &msg, sizeof(struct vfe_message)); -stats_done: - return; -} - -static void vfe31_process_stats_ae_irq(void) -{ - unsigned long flags; - - spin_lock_irqsave(&vfe31_ctrl->aec_ack_lock, flags); - if (!vfe31_ctrl->aecStatsControl.ackPending) { - vfe31_ctrl->aecStatsControl.ackPending = TRUE; - spin_unlock_irqrestore(&vfe31_ctrl->aec_ack_lock, flags); - vfe31_ctrl->aecStatsControl.bufToRender = - vfe31_process_stats_irq_common(statsAeNum, - vfe31_ctrl->aecStatsControl.nextFrameAddrBuf); - - vfe_send_stats_msg(vfe31_ctrl->aecStatsControl.bufToRender, - statsAeNum); - } else { - spin_unlock_irqrestore(&vfe31_ctrl->aec_ack_lock, flags); - vfe31_ctrl->aecStatsControl.droppedStatsFrameCount++; - } - -} - -static void vfe31_process_stats_af_irq(void) -{ - unsigned long flags; - - spin_lock_irqsave(&vfe31_ctrl->af_ack_lock, flags); - if (!vfe31_ctrl->afStatsControl.ackPending) { - vfe31_ctrl->afStatsControl.ackPending = TRUE; - spin_unlock_irqrestore(&vfe31_ctrl->af_ack_lock, flags); - vfe31_ctrl->afStatsControl.bufToRender = - vfe31_process_stats_irq_common(statsAfNum, - vfe31_ctrl->afStatsControl.nextFrameAddrBuf); - - vfe_send_stats_msg(vfe31_ctrl->afStatsControl.bufToRender, - statsAfNum); - } else { - spin_unlock_irqrestore(&vfe31_ctrl->af_ack_lock, flags); - vfe31_ctrl->afStatsControl.droppedStatsFrameCount++; - } - -} - -static void vfe31_process_stats_awb_irq(void) -{ - unsigned long flags; - - spin_lock_irqsave(&vfe31_ctrl->awb_ack_lock, flags); - if (!vfe31_ctrl->awbStatsControl.ackPending) { - vfe31_ctrl->awbStatsControl.ackPending = TRUE; - spin_unlock_irqrestore(&vfe31_ctrl->awb_ack_lock, flags); - vfe31_ctrl->awbStatsControl.bufToRender = - vfe31_process_stats_irq_common(statsAwbNum, - vfe31_ctrl->awbStatsControl.nextFrameAddrBuf); - - vfe_send_stats_msg(vfe31_ctrl->awbStatsControl.bufToRender, - statsAwbNum); - } else { - spin_unlock_irqrestore(&vfe31_ctrl->awb_ack_lock, flags); - vfe31_ctrl->awbStatsControl.droppedStatsFrameCount++; - } - -} - -static void vfe31_process_stats_ihist_irq(void) -{ - unsigned long flags; - - spin_lock_irqsave(&vfe31_ctrl->ihist_ack_lock, flags); - if (!vfe31_ctrl->ihistStatsControl.ackPending) { - vfe31_ctrl->ihistStatsControl.ackPending = TRUE; - spin_unlock_irqrestore(&vfe31_ctrl->ihist_ack_lock, flags); - vfe31_ctrl->ihistStatsControl.bufToRender = - vfe31_process_stats_irq_common(statsIhistNum, - vfe31_ctrl->ihistStatsControl.nextFrameAddrBuf); - - vfe_send_stats_msg(vfe31_ctrl->ihistStatsControl.bufToRender, - statsIhistNum); - } else { - spin_unlock_irqrestore(&vfe31_ctrl->ihist_ack_lock, flags); - vfe31_ctrl->ihistStatsControl.droppedStatsFrameCount++; - } -} - - -static void vfe31_process_stats_rs_irq(void) -{ - unsigned long flags; - - spin_lock_irqsave(&vfe31_ctrl->rs_ack_lock, flags); - if (!vfe31_ctrl->rsStatsControl.ackPending) { - vfe31_ctrl->rsStatsControl.ackPending = TRUE; - spin_unlock_irqrestore(&vfe31_ctrl->rs_ack_lock, flags); - vfe31_ctrl->rsStatsControl.bufToRender = - vfe31_process_stats_irq_common(statsRsNum, - vfe31_ctrl->rsStatsControl.nextFrameAddrBuf); - - vfe_send_stats_msg(vfe31_ctrl->rsStatsControl.bufToRender, - statsRsNum); - } else { - spin_unlock_irqrestore(&vfe31_ctrl->rs_ack_lock, flags); - vfe31_ctrl->rsStatsControl.droppedStatsFrameCount++; - } -} - -static void vfe31_process_stats_cs_irq(void) -{ - unsigned long flags; - - spin_lock_irqsave(&vfe31_ctrl->cs_ack_lock, flags); - if (!vfe31_ctrl->csStatsControl.ackPending) { - vfe31_ctrl->csStatsControl.ackPending = TRUE; - spin_unlock_irqrestore(&vfe31_ctrl->cs_ack_lock, flags); - vfe31_ctrl->csStatsControl.bufToRender = - vfe31_process_stats_irq_common(statsCsNum, - vfe31_ctrl->csStatsControl.nextFrameAddrBuf); - - vfe_send_stats_msg(vfe31_ctrl->csStatsControl.bufToRender, - statsCsNum); - } else { - spin_unlock_irqrestore(&vfe31_ctrl->cs_ack_lock, flags); - vfe31_ctrl->csStatsControl.droppedStatsFrameCount++; - } -} - -static void vfe31_do_tasklet(unsigned long data) -{ - unsigned long flags; - struct vfe31_isr_queue_cmd *qcmd = NULL; - - CDBG("=== vfe31_do_tasklet start ===\n"); - - spin_lock_irqsave(&vfe31_ctrl->tasklet_lock, flags); - qcmd = list_first_entry(&vfe31_ctrl->tasklet_q, - struct vfe31_isr_queue_cmd, list); - - if (!qcmd) { - spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock, flags); - return; - } - - list_del(&qcmd->list); - spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock, flags); - - /* interrupt to be processed, *qcmd has the payload. */ - if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_REG_UPDATE_MASK) { - CDBG("irq regUpdateIrq\n"); - vfe31_process_reg_update_irq(); - } - - if (qcmd->vfeInterruptStatus1 & VFE_IMASK_WHILE_STOPPING_1) { - CDBG("irq resetAckIrq\n"); - vfe31_process_reset_irq(); - } - - spin_lock_irqsave(&vfe31_ctrl->state_lock, flags); - if (vfe31_ctrl->vstate == VFE_STATE_ACTIVE) { - /* irqs below are only valid when in active state. */ - spin_unlock_irqrestore(&vfe31_ctrl->state_lock, flags); - /* next, check output path related interrupts. */ - if (qcmd->vfeInterruptStatus0 & - VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE0_MASK) { - vfe31_process_output_path_irq_0(); - } - if (qcmd->vfeInterruptStatus0 & - VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE1_MASK) { - vfe31_process_output_path_irq_1(); - } - if (qcmd->vfeInterruptStatus0 & - VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE2_MASK) { - vfe31_process_output_path_irq_2(); - } - /* in snapshot mode if done then send snapshot done message */ - if (vfe31_ctrl->operation_mode & 1) { - if ((vfe31_ctrl->outpath.out0.capture_cnt == 0) && - (vfe31_ctrl->outpath.out1.capture_cnt == 0)) { - vfe31_send_msg_no_payload(MSG_ID_SNAPSHOT_DONE); - vfe_io_w(CAMIF_COMMAND_STOP_IMMEDIATELY, VFE_CAMIF_COMMAND); - } - } - /* then process stats irq. */ - if (vfe31_ctrl->stats_comp) { - /* process stats comb interrupt. */ - if (qcmd->vfeInterruptStatus0 & - VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK) { - CDBG("Stats composite irq occured.\n"); - vfe31_process_stats_comb_irq( - &qcmd->vfeInterruptStatus0); - } - } else { - /* process individual stats interrupt. */ - if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_AEC) { - CDBG("Stats AEC irq occured.\n"); - vfe31_process_stats_ae_irq(); - } - if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_AWB) { - CDBG("Stats AWB irq occured.\n"); - vfe31_process_stats_awb_irq(); - } - if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_AF) { - CDBG("Stats AF irq occured.\n"); - vfe31_process_stats_af_irq(); - } - if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_IHIST) { - CDBG("Stats IHIST irq occured.\n"); - vfe31_process_stats_ihist_irq(); - } - if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_RS) { - CDBG("Stats RS irq occured.\n"); - vfe31_process_stats_rs_irq(); - } - if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_STATS_CS) { - CDBG("Stats CS irq occured.\n"); - vfe31_process_stats_cs_irq(); - } - } - } else { - /* do we really need spin lock for state? */ - spin_unlock_irqrestore(&vfe31_ctrl->state_lock, flags); - } - if (qcmd->vfeInterruptStatus0 & VFE_IRQ_STATUS0_CAMIF_SOF_MASK) { - vfe31_process_camif_sof_irq(); - } - kfree(qcmd); - CDBG("=== vfe31_do_tasklet end ===\n"); -} - -DECLARE_TASKLET(vfe31_tasklet, vfe31_do_tasklet, 0); - -static irqreturn_t vfe31_parse_irq(int irq_num, void *data) -{ - unsigned long flags; - struct vfe31_irq_status irq; - struct vfe31_isr_queue_cmd *qcmd; - - CDBG("vfe_parse_irq\n"); - vfe31_read_irq_status(&irq); - - if ((irq.vfeIrqStatus0 == 0) && (irq.vfeIrqStatus1 == 0)) { - CDBG("vfe_parse_irq: vfeIrqStatus0 & 1 are both 0!\n"); - return IRQ_HANDLED; - } - - qcmd = kzalloc(sizeof(struct vfe31_isr_queue_cmd), GFP_ATOMIC); - if (!qcmd) { - pr_err("vfe_parse_irq: qcmd malloc failed!\n"); - return IRQ_HANDLED; - } - - spin_lock_irqsave(&vfe31_ctrl->stop_flag_lock, flags); - if (vfe31_ctrl->stop_ack_pending) { - irq.vfeIrqStatus0 &= VFE_IMASK_WHILE_STOPPING_0; - irq.vfeIrqStatus1 &= VFE_IMASK_WHILE_STOPPING_1; - } - spin_unlock_irqrestore(&vfe31_ctrl->stop_flag_lock, flags); - - CDBG("vfe_parse_irq: Irq_status0 = 0x%x, Irq_status1 = 0x%x.\n", - irq.vfeIrqStatus0, irq.vfeIrqStatus1); - - qcmd->vfeInterruptStatus0 = irq.vfeIrqStatus0; - qcmd->vfeInterruptStatus1 = irq.vfeIrqStatus1; - - spin_lock_irqsave(&vfe31_ctrl->tasklet_lock, flags); - list_add_tail(&qcmd->list, &vfe31_ctrl->tasklet_q); - spin_unlock_irqrestore(&vfe31_ctrl->tasklet_lock, flags); - tasklet_schedule(&vfe31_tasklet); - - /* clear the pending interrupt of the same kind.*/ - vfe_io_w(irq.vfeIrqStatus0, VFE_IRQ_CLEAR_0); - vfe_io_w(irq.vfeIrqStatus1, VFE_IRQ_CLEAR_1); - - vfe_io_w(1, VFE_IRQ_CMD); - - return IRQ_HANDLED; -} - -static int vfe31_resource_init(struct msm_vfe_callback *presp, - struct platform_device *pdev, void *sdata) -{ - struct resource *vfemem, *vfeirq, *vfeio; - int rc; - struct msm_camera_sensor_info *s_info; - s_info = pdev->dev.platform_data; - - pdev->resource = s_info->resource; - pdev->num_resources = s_info->num_resources; - - vfemem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!vfemem) { - pr_err("%s: no mem resource?\n", __func__); - return -ENODEV; - } - - vfeirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!vfeirq) { - pr_err("%s: no irq resource?\n", __func__); - return -ENODEV; - } - - vfeio = request_mem_region(vfemem->start, - resource_size(vfemem), pdev->name); - if (!vfeio) { - pr_err("%s: VFE region already claimed\n", __func__); - return -EBUSY; - } - - vfe31_ctrl = kzalloc(sizeof(struct vfe31_ctrl_type), GFP_KERNEL); - if (!vfe31_ctrl) { - rc = -ENOMEM; - goto cmd_init_failed1; - } - - vfe31_ctrl->vfeirq = vfeirq->start; - - vfe31_ctrl->vfebase = - ioremap(vfemem->start, (vfemem->end - vfemem->start) + 1); - if (!vfe31_ctrl->vfebase) { - rc = -ENOMEM; - pr_err("%s: vfe ioremap failed\n", __func__); - goto cmd_init_failed2; - } - - rc = request_irq(vfe31_ctrl->vfeirq, vfe31_parse_irq, - IRQF_TRIGGER_RISING, "vfe", 0); - if (rc < 0) - goto cmd_init_failed2; - - if (presp && presp->vfe_resp) - vfe31_ctrl->resp = presp; - else { - rc = -EINVAL; - goto cmd_init_failed3; - } - - vfe31_ctrl->extdata = kmalloc(sizeof(struct vfe31_frame_extra), - GFP_KERNEL); - if (!vfe31_ctrl->extdata) { - rc = -ENOMEM; - goto cmd_init_failed3; - } - - vfe31_ctrl->extlen = sizeof(struct vfe31_frame_extra); - - spin_lock_init(&vfe31_ctrl->stop_flag_lock); - spin_lock_init(&vfe31_ctrl->state_lock); - spin_lock_init(&vfe31_ctrl->update_ack_lock); - spin_lock_init(&vfe31_ctrl->tasklet_lock); - - spin_lock_init(&vfe31_ctrl->aec_ack_lock); - spin_lock_init(&vfe31_ctrl->awb_ack_lock); - spin_lock_init(&vfe31_ctrl->af_ack_lock); - spin_lock_init(&vfe31_ctrl->ihist_ack_lock); - spin_lock_init(&vfe31_ctrl->rs_ack_lock); - spin_lock_init(&vfe31_ctrl->cs_ack_lock); - INIT_LIST_HEAD(&vfe31_ctrl->tasklet_q); - - vfe31_ctrl->syncdata = sdata; - vfe31_ctrl->vfemem = vfemem; - vfe31_ctrl->vfeio = vfeio; - return 0; - -cmd_init_failed3: - free_irq(vfe31_ctrl->vfeirq, 0); - iounmap(vfe31_ctrl->vfebase); -cmd_init_failed2: - kfree(vfe31_ctrl); -cmd_init_failed1: - release_mem_region(vfemem->start, (vfemem->end - vfemem->start) + 1); - return rc; -} - -static int vfe31_init(struct msm_vfe_callback *presp, - struct platform_device *dev) -{ - int rc = 0; - rc = vfe31_resource_init(presp, dev, vfe_syncdata); - if (rc < 0) - return rc; - - /* Bring up all the required GPIOs and Clocks */ - rc = msm_camio_enable(dev); - return rc; -} - -void msm_camvfe_fn_init(struct msm_camvfe_fn *fptr, void *data) -{ - fptr->vfe_init = vfe31_init; - fptr->vfe_enable = vfe31_enable; - fptr->vfe_config = vfe31_config; - fptr->vfe_disable = vfe31_disable; - fptr->vfe_release = vfe31_release; - vfe_syncdata = data; -} diff --git a/drivers/media/video/msm/msm_vfe31.h b/drivers/media/video/msm/msm_vfe31.h deleted file mode 100644 index 2a12ac443eb13..0000000000000 --- a/drivers/media/video/msm/msm_vfe31.h +++ /dev/null @@ -1,1052 +0,0 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef __MSM_VFE31_H__ -#define __MSM_VFE31_H__ - -#define TRUE 1 -#define FALSE 0 - -/* at start of camif, bit 1:0 = 0x01:enable - * image data capture at frame boundary. */ -#define CAMIF_COMMAND_START 0x00000005 - -/* bit 2= 0x1:clear the CAMIF_STATUS register - * value. */ -#define CAMIF_COMMAND_CLEAR 0x00000004 - -/* at stop of vfe pipeline, for now it is assumed - * that camif will stop at any time. Bit 1:0 = 0x10: - * disable image data capture immediately. */ -#define CAMIF_COMMAND_STOP_IMMEDIATELY 0x00000002 - -/* at stop of vfe pipeline, for now it is assumed - * that camif will stop at any time. Bit 1:0 = 0x00: - * disable image data capture at frame boundary */ -#define CAMIF_COMMAND_STOP_AT_FRAME_BOUNDARY 0x00000000 - -/* to halt axi bridge */ -#define AXI_HALT 0x00000001 - -/* clear the halt bit. */ -#define AXI_HALT_CLEAR 0x00000000 - -/* reset the pipeline when stop command is issued. - * (without reset the register.) bit 26-31 = 0, - * domain reset, bit 0-9 = 1 for module reset, except - * register module. */ -#define VFE_RESET_UPON_STOP_CMD 0x000003ef - -/* reset the pipeline when reset command. - * bit 26-31 = 0, domain reset, bit 0-9 = 1 for module reset. */ -#define VFE_RESET_UPON_RESET_CMD 0x000003ff - -/* bit 5 is for axi status idle or busy. - * 1 = halted, 0 = busy */ -#define AXI_STATUS_BUSY_MASK 0x00000020 - -/* bit 0 & bit 1 = 1, both y and cbcr irqs need to be present - * for frame done interrupt */ -#define VFE_COMP_IRQ_BOTH_Y_CBCR 3 - -/* bit 1 = 1, only cbcr irq triggers frame done interrupt */ -#define VFE_COMP_IRQ_CBCR_ONLY 2 - -/* bit 0 = 1, only y irq triggers frame done interrupt */ -#define VFE_COMP_IRQ_Y_ONLY 1 - -/* bit 0 = 1, PM go; bit1 = 1, PM stop */ -#define VFE_PERFORMANCE_MONITOR_GO 0x00000001 -#define VFE_PERFORMANCE_MONITOR_STOP 0x00000002 - -/* bit 0 = 1, test gen go; bit1 = 1, test gen stop */ -#define VFE_TEST_GEN_GO 0x00000001 -#define VFE_TEST_GEN_STOP 0x00000002 - -/* the chroma is assumed to be interpolated between - * the luma samples. JPEG 4:2:2 */ -#define VFE_CHROMA_UPSAMPLE_INTERPOLATED 0 - -/* constants for irq registers */ -#define VFE_DISABLE_ALL_IRQS 0 -/* bit =1 is to clear the corresponding bit in VFE_IRQ_STATUS. */ -#define VFE_CLEAR_ALL_IRQS 0xffffffff - -#define VFE_IRQ_STATUS0_CAMIF_SOF_MASK 0x00000001 -#define VFE_IRQ_STATUS0_REG_UPDATE_MASK 0x00000020 -#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE0_MASK 0x00200000 -#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE1_MASK 0x00400000 -#define VFE_IRQ_STATUS0_IMAGE_COMPOSIT_DONE2_MASK 0x00800000 -#define VFE_IRQ_STATUS1_RESET_AXI_HALT_ACK_MASK 0x00800000 -#define VFE_IRQ_STATUS0_STATS_COMPOSIT_MASK 0x01000000 - -#define VFE_IRQ_STATUS0_STATS_AEC 0x2000 /* bit 13 */ -#define VFE_IRQ_STATUS0_STATS_AF 0x4000 /* bit 14 */ -#define VFE_IRQ_STATUS0_STATS_AWB 0x8000 /* bit 15 */ -#define VFE_IRQ_STATUS0_STATS_RS 0x10000 /* bit 16 */ -#define VFE_IRQ_STATUS0_STATS_CS 0x20000 /* bit 17 */ -#define VFE_IRQ_STATUS0_STATS_IHIST 0x40000 /* bit 18 */ - -/* imask for while waiting for stop ack, driver has already - * requested stop, waiting for reset irq, and async timer irq. - * For irq_status_0, bit 28-31 are for async timer. For - * irq_status_1, bit 22 for reset irq, bit 23 for axi_halt_ack - irq */ -#define VFE_IMASK_WHILE_STOPPING_0 0xF0000000 -#define VFE_IMASK_WHILE_STOPPING_1 0x00400000 - -/* no error irq in mask 0 */ -#define VFE_IMASK_ERROR_ONLY_0 0x0 -/* when normal case, don't want to block error status. */ -/* bit 0-21 are error irq bits */ -#define VFE_IMASK_ERROR_ONLY_1 0x003fffff - -/* For BPC bit 0,bit 12-17 and bit 26 -20 are set to zero and other's 1 */ -#define BPC_MASK 0xF80C0FFE - -/* For BPC bit 1 and 2 are set to zero and other's 1 */ -#define ABF_MASK 0xFFFFFFF9 - -/* For MCE enable bit 28 set to zero and other's 1 */ -#define MCE_EN_MASK 0xEFFFFFFF - -/* For MCE Q_K bit 28 to 31 set to zero and other's 1 */ -#define MCE_Q_K_MASK 0x0FFFFFFF - -#define AWB_ENABLE_MASK 0x00000080 /* bit 7 */ -#define AF_ENABLE_MASK 0x00000040 /* bit 6 */ -#define AE_ENABLE_MASK 0x00000020 /* bit 5 */ -#define IHIST_ENABLE_MASK 0x00008000 /* bit 15 */ -#define RS_ENABLE_MASK 0x00000100 /* bit 8 */ -#define CS_ENABLE_MASK 0x00000200 /* bit 9 */ - - -#define VFE_REG_UPDATE_TRIGGER 1 -#define VFE_PM_BUF_MAX_CNT_MASK 0xFF -#define VFE_DMI_CFG_DEFAULT 0x00000100 -#define LENS_ROLL_OFF_DELTA_TABLE_OFFSET 32 -#define VFE_AE_PINGPONG_STATUS_BIT 0x80 -#define VFE_AF_PINGPONG_STATUS_BIT 0x100 -#define VFE_AWB_PINGPONG_STATUS_BIT 0x200 - - -enum VFE31_DMI_RAM_SEL { - NO_MEM_SELECTED = 0, - ROLLOFF_RAM = 0x1, - RGBLUT_RAM_CH0_BANK0 = 0x2, - RGBLUT_RAM_CH0_BANK1 = 0x3, - RGBLUT_RAM_CH1_BANK0 = 0x4, - RGBLUT_RAM_CH1_BANK1 = 0x5, - RGBLUT_RAM_CH2_BANK0 = 0x6, - RGBLUT_RAM_CH2_BANK1 = 0x7, - STATS_HIST_CB_EVEN_RAM = 0x8, - STATS_HIST_CB_ODD_RAM = 0x9, - STATS_HIST_CR_EVEN_RAM = 0xa, - STATS_HIST_CR_ODD_RAM = 0xb, - RGBLUT_CHX_BANK0 = 0xc, - RGBLUT_CHX_BANK1 = 0xd, - LUMA_ADAPT_LUT_RAM_BANK0 = 0xe, - LUMA_ADAPT_LUT_RAM_BANK1 = 0xf - -}; - -enum VFE_STATE { - VFE_STATE_IDLE, - VFE_STATE_ACTIVE -}; - -#define V31_DUMMY_0 0 -#define V31_SET_CLK 1 -#define V31_RESET 2 -#define V31_START 3 -#define V31_TEST_GEN_START 4 -#define V31_OPERATION_CFG 5 -#define V31_AXI_OUT_CFG 6 -#define V31_CAMIF_CFG 7 -#define V31_AXI_INPUT_CFG 8 -#define V31_BLACK_LEVEL_CFG 9 -#define V31_ROLL_OFF_CFG 10 -#define V31_DEMUX_CFG 11 -#define V31_DEMOSAIC_0_CFG 12 /* general */ -#define V31_DEMOSAIC_1_CFG 13 /* ABF */ -#define V31_DEMOSAIC_2_CFG 14 /* BPC */ -#define V31_FOV_CFG 15 -#define V31_MAIN_SCALER_CFG 16 -#define V31_WB_CFG 17 -#define V31_COLOR_COR_CFG 18 -#define V31_RGB_G_CFG 19 -#define V31_LA_CFG 20 -#define V31_CHROMA_EN_CFG 21 -#define V31_CHROMA_SUP_CFG 22 -#define V31_MCE_CFG 23 -#define V31_SK_ENHAN_CFG 24 -#define V31_ASF_CFG 25 -#define V31_S2Y_CFG 26 -#define V31_S2CbCr_CFG 27 -#define V31_CHROMA_SUBS_CFG 28 -#define V31_OUT_CLAMP_CFG 29 -#define V31_FRAME_SKIP_CFG 30 -#define V31_DUMMY_1 31 -#define V31_DUMMY_2 32 -#define V31_DUMMY_3 33 -#define V31_UPDATE 34 -#define V31_BL_LVL_UPDATE 35 -#define V31_DEMUX_UPDATE 36 -#define V31_DEMOSAIC_1_UPDATE 37 /* BPC */ -#define V31_DEMOSAIC_2_UPDATE 38 /* ABF */ -#define V31_FOV_UPDATE 39 -#define V31_MAIN_SCALER_UPDATE 40 -#define V31_WB_UPDATE 41 -#define V31_COLOR_COR_UPDATE 42 -#define V31_RGB_G_UPDATE 43 -#define V31_LA_UPDATE 44 -#define V31_CHROMA_EN_UPDATE 45 -#define V31_CHROMA_SUP_UPDATE 46 -#define V31_MCE_UPDATE 47 -#define V31_SK_ENHAN_UPDATE 48 -#define V31_S2CbCr_UPDATE 49 -#define V31_S2Y_UPDATE 50 -#define V31_ASF_UPDATE 51 -#define V31_FRAME_SKIP_UPDATE 52 -#define V31_CAMIF_FRAME_UPDATE 53 -#define V31_STATS_AF_UPDATE 54 -#define V31_STATS_AE_UPDATE 55 -#define V31_STATS_AWB_UPDATE 56 -#define V31_STATS_RS_UPDATE 57 -#define V31_STATS_CS_UPDATE 58 -#define V31_STATS_SKIN_UPDATE 59 -#define V31_STATS_IHIST_UPDATE 60 -#define V31_DUMMY_4 61 -#define V31_EPOCH1_ACK 62 -#define V31_EPOCH2_ACK 63 -#define V31_START_RECORDING 64 -#define V31_STOP_RECORDING 65 -#define V31_DUMMY_5 66 -#define V31_DUMMY_6 67 -#define V31_CAPTURE 68 -#define V31_DUMMY_7 69 -#define V31_STOP 70 -#define V31_GET_HW_VERSION 71 -#define V31_GET_FRAME_SKIP_COUNTS 72 -#define V31_OUTPUT1_BUFFER_ENQ 73 -#define V31_OUTPUT2_BUFFER_ENQ 74 -#define V31_OUTPUT3_BUFFER_ENQ 75 -#define V31_JPEG_OUT_BUF_ENQ 76 -#define V31_RAW_OUT_BUF_ENQ 77 -#define V31_RAW_IN_BUF_ENQ 78 -#define V31_STATS_AF_ENQ 79 -#define V31_STATS_AE_ENQ 80 -#define V31_STATS_AWB_ENQ 81 -#define V31_STATS_RS_ENQ 82 -#define V31_STATS_CS_ENQ 83 -#define V31_STATS_SKIN_ENQ 84 -#define V31_STATS_IHIST_ENQ 85 -#define V31_DUMMY_8 86 -#define V31_JPEG_ENC_CFG 87 -#define V31_DUMMY_9 88 -#define V31_STATS_AF_START 89 -#define V31_STATS_AF_STOP 90 -#define V31_STATS_AE_START 91 -#define V31_STATS_AE_STOP 92 -#define V31_STATS_AWB_START 93 -#define V31_STATS_AWB_STOP 94 -#define V31_STATS_RS_START 95 -#define V31_STATS_RS_STOP 96 -#define V31_STATS_CS_START 97 -#define V31_STATS_CS_STOP 98 -#define V31_STATS_SKIN_START 99 -#define V31_STATS_SKIN_STOP 100 -#define V31_STATS_IHIST_START 101 -#define V31_STATS_IHIST_STOP 102 -#define V31_DUMMY_10 103 -#define V31_SYNC_TIMER_SETTING 104 -#define V31_ASYNC_TIMER_SETTING 105 -#define V31_CAMIF_OFF 0x000001E4 -#define V31_CAMIF_LEN 32 - -#define V31_DEMUX_OFF 0x00000284 -#define V31_DEMUX_LEN 20 - -#define V31_DEMOSAIC_0_OFF 0x00000298 -#define V31_DEMOSAIC_0_LEN 4 -/* ABF */ -#define V31_DEMOSAIC_1_OFF 0x000002A4 -#define V31_DEMOSAIC_1_LEN 180 -/* BPC */ -#define V31_DEMOSAIC_2_OFF 0x0000029C -#define V31_DEMOSAIC_2_LEN 8 - -#define V31_OUT_CLAMP_OFF 0x00000524 -#define V31_OUT_CLAMP_LEN 8 - -#define V31_OPERATION_CFG_LEN 28 - -#define V31_AXI_OUT_OFF 0x00000038 -#define V31_AXI_OUT_LEN 188 - -#define V31_FRAME_SKIP_OFF 0x00000504 -#define V31_FRAME_SKIP_LEN 32 - -#define V31_CHROMA_SUBS_OFF 0x000004F8 -#define V31_CHROMA_SUBS_LEN 12 - -#define V31_FOV_OFF 0x00000360 -#define V31_FOV_LEN 8 - -#define V31_MAIN_SCALER_OFF 0x00000368 -#define V31_MAIN_SCALER_LEN 28 - -#define V31_S2Y_OFF 0x000004D0 -#define V31_S2Y_LEN 20 - -#define V31_S2CbCr_OFF 0x000004E4 -#define V31_S2CbCr_LEN 20 - -#define V31_CHROMA_EN_OFF 0x000003C4 -#define V31_CHROMA_EN_LEN 36 - -#define V31_BLACK_LEVEL_OFF 0x00000264 -#define V31_BLACK_LEVEL_LEN 16 - -#define V31_ROLL_OFF_CFG_OFF 0x00000274 -#define V31_ROLL_OFF_CFG_LEN 16 - -#define V31_COLOR_COR_OFF 0x00000388 -#define V31_COLOR_COR_LEN 52 - -#define V31_WB_OFF 0x00000384 -#define V31_WB_LEN 4 - -#define V31_RGB_G_OFF 0x000003BC -#define V31_RGB_G_LEN 4 - -#define V31_LA_OFF 0x000003C0 -#define V31_LA_LEN 4 - -#define V31_CHROMA_SUP_OFF 0x000003E8 -#define V31_CHROMA_SUP_LEN 12 - -#define V31_MCE_OFF 0x000003E8 -#define V31_MCE_LEN 36 -#define V31_STATS_AF_OFF 0x0000053c -#define V31_STATS_AF_LEN 16 - -#define V31_STATS_AE_OFF 0x00000534 -#define V31_STATS_AE_LEN 8 - -#define V31_STATS_AWB_OFF 0x0000054c -#define V31_STATS_AWB_LEN 32 - -#define V31_STATS_IHIST_OFF 0x0000057c -#define V31_STATS_IHIST_LEN 8 - -#define V31_STATS_RS_OFF 0x0000056c -#define V31_STATS_RS_LEN 8 - -#define V31_STATS_CS_OFF 0x00000574 -#define V31_STATS_CS_LEN 8 - - -#define V31_ASF_OFF 0x000004A0 -#define V31_ASF_LEN 48 -#define V31_ASF_UPDATE_LEN 36 - -#define V31_CAPTURE_LEN 4 - -struct vfe_cmd_hw_version { - uint32_t minorVersion; - uint32_t majorVersion; - uint32_t coreVersion; -}; - -enum VFE_AXI_OUTPUT_MODE { - VFE_AXI_OUTPUT_MODE_Output1, - VFE_AXI_OUTPUT_MODE_Output2, - VFE_AXI_OUTPUT_MODE_Output1AndOutput2, - VFE_AXI_OUTPUT_MODE_CAMIFToAXIViaOutput2, - VFE_AXI_OUTPUT_MODE_Output2AndCAMIFToAXIViaOutput1, - VFE_AXI_OUTPUT_MODE_Output1AndCAMIFToAXIViaOutput2, - VFE_AXI_LAST_OUTPUT_MODE_ENUM -}; - -enum VFE_RAW_WR_PATH_SEL { - VFE_RAW_OUTPUT_DISABLED, - VFE_RAW_OUTPUT_ENC_CBCR_PATH, - VFE_RAW_OUTPUT_VIEW_CBCR_PATH, - VFE_RAW_OUTPUT_PATH_INVALID -}; - - -#define VFE_AXI_OUTPUT_BURST_LENGTH 4 -#define VFE_MAX_NUM_FRAGMENTS_PER_FRAME 4 -#define VFE_AXI_OUTPUT_CFG_FRAME_COUNT 3 - -struct vfe_cmds_per_write_master { - uint16_t imageWidth; - uint16_t imageHeight; - uint16_t outRowCount; - uint16_t outRowIncrement; - uint32_t outFragments[VFE_AXI_OUTPUT_CFG_FRAME_COUNT] - [VFE_MAX_NUM_FRAGMENTS_PER_FRAME]; -}; - -struct vfe_cmds_axi_per_output_path { - uint8_t fragmentCount; - struct vfe_cmds_per_write_master firstWM; - struct vfe_cmds_per_write_master secondWM; -}; - -enum VFE_AXI_BURST_LENGTH { - VFE_AXI_BURST_LENGTH_IS_2 = 2, - VFE_AXI_BURST_LENGTH_IS_4 = 4, - VFE_AXI_BURST_LENGTH_IS_8 = 8, - VFE_AXI_BURST_LENGTH_IS_16 = 16 -}; - - -struct vfe_cmd_fov_crop_config { - uint8_t enable; - uint16_t firstPixel; - uint16_t lastPixel; - uint16_t firstLine; - uint16_t lastLine; -}; - -struct vfe_cmds_main_scaler_stripe_init { - uint16_t MNCounterInit; - uint16_t phaseInit; -}; - -struct vfe_cmds_scaler_one_dimension { - uint8_t enable; - uint16_t inputSize; - uint16_t outputSize; - uint32_t phaseMultiplicationFactor; - uint8_t interpolationResolution; -}; - -struct vfe_cmd_main_scaler_config { - uint8_t enable; - struct vfe_cmds_scaler_one_dimension hconfig; - struct vfe_cmds_scaler_one_dimension vconfig; - struct vfe_cmds_main_scaler_stripe_init MNInitH; - struct vfe_cmds_main_scaler_stripe_init MNInitV; -}; - -struct vfe_cmd_scaler2_config { - uint8_t enable; - struct vfe_cmds_scaler_one_dimension hconfig; - struct vfe_cmds_scaler_one_dimension vconfig; -}; - - -struct vfe_cmd_frame_skip_update { - uint32_t output1Pattern; - uint32_t output2Pattern; -}; - -struct vfe_cmd_output_clamp_config { - uint8_t minCh0; - uint8_t minCh1; - uint8_t minCh2; - uint8_t maxCh0; - uint8_t maxCh1; - uint8_t maxCh2; -}; - -struct vfe_cmd_chroma_subsample_config { - uint8_t enable; - uint8_t cropEnable; - uint8_t vsubSampleEnable; - uint8_t hsubSampleEnable; - uint8_t vCosited; - uint8_t hCosited; - uint8_t vCositedPhase; - uint8_t hCositedPhase; - uint16_t cropWidthFirstPixel; - uint16_t cropWidthLastPixel; - uint16_t cropHeightFirstLine; - uint16_t cropHeightLastLine; -}; - -enum VFE_START_INPUT_SOURCE { - VFE_START_INPUT_SOURCE_CAMIF, - VFE_START_INPUT_SOURCE_TESTGEN, - VFE_START_INPUT_SOURCE_AXI, - VFE_START_INPUT_SOURCE_INVALID -}; - -enum VFE_START_OPERATION_MODE { - VFE_START_OPERATION_MODE_CONTINUOUS, - VFE_START_OPERATION_MODE_SNAPSHOT -}; - -enum VFE_START_PIXEL_PATTERN { - VFE_BAYER_RGRGRG, - VFE_BAYER_GRGRGR, - VFE_BAYER_BGBGBG, - VFE_BAYER_GBGBGB, - VFE_YUV_YCbYCr, - VFE_YUV_YCrYCb, - VFE_YUV_CbYCrY, - VFE_YUV_CrYCbY -}; - -enum VFE_BUS_RD_INPUT_PIXEL_PATTERN { - VFE_BAYER_RAW, - VFE_YUV_INTERLEAVED, - VFE_YUV_PSEUDO_PLANAR_Y, - VFE_YUV_PSEUDO_PLANAR_CBCR -}; - -enum VFE_YUV_INPUT_COSITING_MODE { - VFE_YUV_COSITED, - VFE_YUV_INTERPOLATED -}; - - -/* 13*1 */ -#define VFE31_ROLL_OFF_INIT_TABLE_SIZE 13 -/* 13*16 */ -#define VFE31_ROLL_OFF_DELTA_TABLE_SIZE 208 - -#define VFE31_GAMMA_NUM_ENTRIES 64 - -#define VFE31_LA_TABLE_LENGTH 64 - -struct vfe_cmds_demosaic_abf { - uint8_t enable; - uint8_t forceOn; - uint8_t shift; - uint16_t lpThreshold; - uint16_t max; - uint16_t min; - uint8_t ratio; -}; - -struct vfe_cmds_demosaic_bpc { - uint8_t enable; - uint16_t fmaxThreshold; - uint16_t fminThreshold; - uint16_t redDiffThreshold; - uint16_t blueDiffThreshold; - uint16_t greenDiffThreshold; -}; - -struct vfe_cmd_demosaic_config { - uint8_t enable; - uint8_t slopeShift; - struct vfe_cmds_demosaic_abf abfConfig; - struct vfe_cmds_demosaic_bpc bpcConfig; -}; - -struct vfe_cmd_demosaic_bpc_update { - struct vfe_cmds_demosaic_bpc bpcUpdate; -}; - -struct vfe_cmd_demosaic_abf_update { - struct vfe_cmds_demosaic_abf abfUpdate; -}; - -struct vfe_cmd_white_balance_config { - uint8_t enable; - uint16_t ch2Gain; - uint16_t ch1Gain; - uint16_t ch0Gain; -}; - -enum VFE_COLOR_CORRECTION_COEF_QFACTOR { - COEF_IS_Q7_SIGNED, - COEF_IS_Q8_SIGNED, - COEF_IS_Q9_SIGNED, - COEF_IS_Q10_SIGNED -}; - -struct vfe_cmd_color_correction_config { - uint8_t enable; - enum VFE_COLOR_CORRECTION_COEF_QFACTOR coefQFactor; - int16_t C0; - int16_t C1; - int16_t C2; - int16_t C3; - int16_t C4; - int16_t C5; - int16_t C6; - int16_t C7; - int16_t C8; - int16_t K0; - int16_t K1; - int16_t K2; -}; - -#define VFE_LA_TABLE_LENGTH 256 -struct vfe_cmd_la_config { - uint8_t enable; - int16_t table[VFE_LA_TABLE_LENGTH]; -}; - -#define VFE_GAMMA_TABLE_LENGTH 256 -enum VFE_RGB_GAMMA_TABLE_SELECT { - RGB_GAMMA_CH0_SELECTED, - RGB_GAMMA_CH1_SELECTED, - RGB_GAMMA_CH2_SELECTED, - RGB_GAMMA_CH0_CH1_SELECTED, - RGB_GAMMA_CH0_CH2_SELECTED, - RGB_GAMMA_CH1_CH2_SELECTED, - RGB_GAMMA_CH0_CH1_CH2_SELECTED -}; - -struct vfe_cmd_rgb_gamma_config { - uint8_t enable; - enum VFE_RGB_GAMMA_TABLE_SELECT channelSelect; - int16_t table[VFE_GAMMA_TABLE_LENGTH]; -}; - -struct vfe_cmd_chroma_enhan_config { - uint8_t enable; - int16_t am; - int16_t ap; - int16_t bm; - int16_t bp; - int16_t cm; - int16_t cp; - int16_t dm; - int16_t dp; - int16_t kcr; - int16_t kcb; - int16_t RGBtoYConversionV0; - int16_t RGBtoYConversionV1; - int16_t RGBtoYConversionV2; - uint8_t RGBtoYConversionOffset; -}; - -struct vfe_cmd_chroma_suppression_config { - uint8_t enable; - uint8_t m1; - uint8_t m3; - uint8_t n1; - uint8_t n3; - uint8_t nn1; - uint8_t mm1; -}; - -struct vfe_cmd_asf_config { - uint8_t enable; - uint8_t smoothFilterEnabled; - uint8_t sharpMode; - uint8_t smoothCoefCenter; - uint8_t smoothCoefSurr; - uint8_t normalizeFactor; - uint8_t sharpK1; - uint8_t sharpK2; - uint8_t sharpThreshE1; - int8_t sharpThreshE2; - int8_t sharpThreshE3; - int8_t sharpThreshE4; - int8_t sharpThreshE5; - int8_t filter1Coefficients[9]; - int8_t filter2Coefficients[9]; - uint8_t cropEnable; - uint16_t cropFirstPixel; - uint16_t cropLastPixel; - uint16_t cropFirstLine; - uint16_t cropLastLine; -}; - -struct vfe_cmd_asf_update { - uint8_t enable; - uint8_t smoothFilterEnabled; - uint8_t sharpMode; - uint8_t smoothCoefCenter; - uint8_t smoothCoefSurr; - uint8_t normalizeFactor; - uint8_t sharpK1; - uint8_t sharpK2; - uint8_t sharpThreshE1; - int8_t sharpThreshE2; - int8_t sharpThreshE3; - int8_t sharpThreshE4; - int8_t sharpThreshE5; - int8_t filter1Coefficients[9]; - int8_t filter2Coefficients[9]; - uint8_t cropEnable; -}; - -enum VFE_TEST_GEN_SYNC_EDGE { - VFE_TEST_GEN_SYNC_EDGE_ActiveHigh, - VFE_TEST_GEN_SYNC_EDGE_ActiveLow -}; - - -struct vfe_cmd_bus_pm_start { - uint8_t output2YWrPmEnable; - uint8_t output2CbcrWrPmEnable; - uint8_t output1YWrPmEnable; - uint8_t output1CbcrWrPmEnable; -}; - -struct vfe_cmd_sync_timer_setting { - uint8_t whichSyncTimer; - uint8_t operation; - uint8_t polarity; - uint16_t repeatCount; - uint16_t hsyncCount; - uint32_t pclkCount; - uint32_t outputDuration; -}; - -struct vfe_cmd_async_timer_setting { - uint8_t whichAsyncTimer; - uint8_t operation; - uint8_t polarity; - uint16_t repeatCount; - uint16_t inactiveCount; - uint32_t activeCount; -}; - -struct vfe_frame_skip_counts { - uint32_t totalFrameCount; - uint32_t output1Count; - uint32_t output2Count; -}; - -enum VFE_AXI_RD_UNPACK_HBI_SEL { - VFE_AXI_RD_HBI_32_CLOCK_CYCLES, - VFE_AXI_RD_HBI_64_CLOCK_CYCLES, - VFE_AXI_RD_HBI_128_CLOCK_CYCLES, - VFE_AXI_RD_HBI_256_CLOCK_CYCLES, - VFE_AXI_RD_HBI_512_CLOCK_CYCLES, - VFE_AXI_RD_HBI_1024_CLOCK_CYCLES, - VFE_AXI_RD_HBI_2048_CLOCK_CYCLES, - VFE_AXI_RD_HBI_4096_CLOCK_CYCLES -}; - -enum VFE31_MESSAGE_ID { - MSG_ID_RESET_ACK, - MSG_ID_START_ACK, - MSG_ID_STOP_ACK, - MSG_ID_UPDATE_ACK, - MSG_ID_OUTPUT_P, - MSG_ID_OUTPUT_T, - MSG_ID_OUTPUT_S, - MSG_ID_OUTPUT_V, - MSG_ID_SNAPSHOT_DONE, - MSG_ID_STATS_AEC, - MSG_ID_STATS_AF, - MSG_ID_STATS_AWB, /* 8 */ - MSG_ID_STATS_RS, - MSG_ID_STATS_CS, - MSG_ID_STATS_IHIST, - MSG_ID_STATS_SKIN, - MSG_ID_EPOCH1, - MSG_ID_EPOCH2, - MSG_ID_SYNC_TIMER0_DONE, - MSG_ID_SYNC_TIMER1_DONE, - MSG_ID_SYNC_TIMER2_DONE, - MSG_ID_ASYNC_TIMER0_DONE, - MSG_ID_ASYNC_TIMER1_DONE, - MSG_ID_ASYNC_TIMER2_DONE, - MSG_ID_ASYNC_TIMER3_DONE, - MSG_ID_AE_OVERFLOW, - MSG_ID_AF_OVERFLOW, - MSG_ID_AWB_OVERFLOW, - MSG_ID_RS_OVERFLOW, - MSG_ID_CS_OVERFLOW, - MSG_ID_IHIST_OVERFLOW, - MSG_ID_SKIN_OVERFLOW, - MSG_ID_AXI_ERROR, - MSG_ID_CAMIF_OVERFLOW, - MSG_ID_VIOLATION, - MSG_ID_CAMIF_ERROR, - MSG_ID_BUS_OVERFLOW, -}; - -struct vfe_msg_stats{ - uint32_t buffer; - uint32_t frameCounter; -}; - - -struct vfe_frame_bpc_info { - uint32_t greenDefectPixelCount; - uint32_t redBlueDefectPixelCount; -}; - -struct vfe_frame_asf_info { - uint32_t asfMaxEdge; - uint32_t asfHbiCount; -}; - -struct vfe_msg_camif_status { - uint8_t camifState; - uint32_t pixelCount; - uint32_t lineCount; -}; - - -struct vfe31_irq_status { - uint32_t vfeIrqStatus0; - uint32_t vfeIrqStatus1; - uint32_t camifStatus; - uint32_t demosaicStatus; - uint32_t asfMaxEdge; -}; - -struct vfe_msg_output { - uint8_t output_id; - uint32_t yBuffer; - uint32_t cbcrBuffer; - struct vfe_frame_bpc_info bpcInfo; - struct vfe_frame_asf_info asfInfo; - uint32_t frameCounter; -}; - -struct vfe_message { - enum VFE31_MESSAGE_ID _d; - union { - struct vfe_msg_output msgOut; - struct vfe_msg_stats msgStats; - struct vfe_msg_camif_status msgCamifError; - } _u; -}; - -/* New one for 7x30 */ -struct msm_vfe31_cmd { - int32_t id; - uint16_t length; - void *value; -}; - -#define V31_PREVIEW_AXI_FLAG 0x00000001 -#define V31_SNAPSHOT_AXI_FLAG (0x00000001<<1) - -struct vfe31_cmd_type { - uint16_t id; - uint32_t length; - uint32_t offset; - uint32_t flag; -}; - -struct vfe31_free_buf { - spinlock_t f_lock; - uint8_t available; - uint32_t paddr; - uint32_t y_off; - uint32_t cbcr_off; -}; - -struct vfe31_output_ch { - struct vfe31_free_buf free_buf; - uint16_t output_fmt; - int8_t ch0; - int8_t ch1; - int8_t ch2; - uint32_t capture_cnt; - uint32_t frame_drop_cnt; -}; - -/* no error irq in mask 0 */ -#define VFE31_IMASK_ERROR_ONLY_0 0x0 -/* when normal case, don't want to block error status. */ -/* bit 0-21 are error irq bits */ -#define VFE31_IMASK_ERROR_ONLY_1 0x003fffff - -struct vfe31_output_path { - uint16_t output_mode; /* bitmask */ - - struct vfe31_output_ch out0; /* preview and thumbnail */ - struct vfe31_output_ch out1; /* snapshot */ - struct vfe31_output_ch out2; /* video */ -}; - -struct vfe31_frame_extra { - uint32_t greenDefectPixelCount; - uint32_t redBlueDefectPixelCount; - - uint32_t asfMaxEdge; - uint32_t asfHbiCount; - - uint32_t yWrPmStats0; - uint32_t yWrPmStats1; - uint32_t cbcrWrPmStats0; - uint32_t cbcrWrPmStats1; - - uint32_t frameCounter; -}; - -#define VFE_DISABLE_ALL_IRQS 0 -#define VFE_CLEAR_ALL_IRQS 0xffffffff - -#define VFE_VERSION 0x00000000 -#define VFE_GLOBAL_RESET 0x00000004 -#define VFE_CGC_OVERRIDE 0x0000000C -#define VFE_MODULE_CFG 0x00000010 -#define VFE_CFG_OFF 0x00000014 -#define VFE_IRQ_CMD 0x00000018 -#define VFE_IRQ_MASK_0 0x0000001C -#define VFE_IRQ_MASK_1 0x00000020 -#define VFE_IRQ_CLEAR_0 0x00000024 -#define VFE_IRQ_CLEAR_1 0x00000028 -#define VFE_IRQ_STATUS_0 0x0000002C -#define VFE_IRQ_STATUS_1 0x00000030 -#define VFE_IRQ_COMP_MASK 0x00000034 -#define VFE_BUS_CMD 0x00000038 -#define VFE_AXI_OFFSET 0x00000050 -#define VFE_BUS_STATS_PING_PONG_BASE 0x000000F4 -#define VFE_BUS_STATS_AEC_WR_PING_ADDR 0x000000F4 -#define VFE_BUS_STATS_AEC_WR_PONG_ADDR 0x000000F8 -#define VFE_BUS_STATS_AEC_UB_CFG 0x000000FC -#define VFE_BUS_STATS_AF_WR_PING_ADDR 0x00000100 -#define VFE_BUS_STATS_AF_WR_PONG_ADDR 0x00000104 -#define VFE_BUS_STATS_AF_UB_CFG 0x00000108 -#define VFE_BUS_STATS_AWB_WR_PING_ADDR 0x0000010C -#define VFE_BUS_STATS_AWB_WR_PONG_ADDR 0x00000110 -#define VFE_BUS_STATS_AWB_UB_CFG 0x00000114 -#define VFE_BUS_STATS_RS_WR_PING_ADDR 0x00000118 -#define VFE_BUS_STATS_RS_WR_PONG_ADDR 0x0000011C -#define VFE_BUS_STATS_RS_UB_CFG 0x00000120 -#define VFE_BUS_STATS_CS_WR_PING_ADDR 0x00000124 -#define VFE_BUS_STATS_CS_WR_PONG_ADDR 0x00000128 -#define VFE_BUS_STATS_CS_UB_CFG 0x0000012C -#define VFE_BUS_STATS_HIST_WR_PING_ADDR 0x00000130 -#define VFE_BUS_STATS_HIST_WR_PONG_ADDR 0x00000134 -#define VFE_BUS_STATS_HIST_UB_CFG 0x00000138 -#define VFE_BUS_STATS_SKIN_WR_PING_ADDR 0x0000013C -#define VFE_BUS_STATS_SKIN_WR_PONG_ADDR 0x00000140 -#define VFE_BUS_STATS_SKIN_UB_CFG 0x00000144 -#define VFE_BUS_PING_PONG_STATUS 0x00000180 -#define VFE_AXI_CMD 0x000001D8 -#define VFE_AXI_STATUS 0x000001DC -#define VFE_CAMIF_COMMAND 0x000001E0 -#define VFE_CAMIF_STATUS 0x00000204 -#define VFE_REG_UPDATE_CMD 0x00000260 -#define VFE_DEMUX_GAIN_0 0x00000288 -#define VFE_DEMUX_GAIN_1 0x0000028C -#define VFE_CHROMA_UP 0x0000035C -#define VFE_FRAMEDROP_ENC_Y_CFG 0x00000504 -#define VFE_FRAMEDROP_ENC_CBCR_CFG 0x00000508 -#define VFE_FRAMEDROP_ENC_Y_PATTERN 0x0000050C -#define VFE_FRAMEDROP_ENC_CBCR_PATTERN 0x00000510 -#define VFE_FRAMEDROP_VIEW_Y 0x00000514 -#define VFE_FRAMEDROP_VIEW_CBCR 0x00000518 -#define VFE_FRAMEDROP_VIEW_Y_PATTERN 0x0000051C -#define VFE_FRAMEDROP_VIEW_CBCR_PATTERN 0x00000520 -#define VFE_CLAMP_MAX 0x00000524 -#define VFE_CLAMP_MIN 0x00000528 -#define VFE_REALIGN_BUF 0x0000052C -#define VFE_STATS_CFG 0x00000530 -#define VFE_DMI_CFG 0x00000598 -#define VFE_DMI_ADDR 0x0000059C -#define VFE_DMI_DATA_LO 0x000005A4 - -struct vfe_stats_control { - uint8_t ackPending; - uint32_t nextFrameAddrBuf; - uint32_t droppedStatsFrameCount; - uint32_t bufToRender; -}; - -struct vfe31_ctrl_type { - uint16_t operation_mode; /* streaming or snapshot */ - struct vfe31_output_path outpath; - - uint32_t vfeImaskCompositePacked; - - spinlock_t stop_flag_lock; /* protects stop_ack_pending */ - spinlock_t update_ack_lock; /* protects update_ack_pending */ - spinlock_t state_lock; /* protects vstate */ - spinlock_t aec_ack_lock; /* protects aecStatsControl.ackPending */ - spinlock_t awb_ack_lock; /* protects awbStatsControl.ackPending */ - spinlock_t af_ack_lock; /* protects afStatsControl.ackPending */ - spinlock_t ihist_ack_lock; /* protects ihistStatsControl.ackPending */ - spinlock_t rs_ack_lock; /* protects rsStatsControl.ackPending */ - spinlock_t cs_ack_lock; /* protects csStatsControl.ackPending */ - - struct msm_vfe_callback *resp; - uint32_t extlen; - void *extdata; - - int8_t start_ack_pending; - int8_t stop_ack_pending; - int8_t reset_ack_pending; - int8_t update_ack_pending; - int8_t req_start_video_rec; - int8_t req_stop_video_rec; - - spinlock_t tasklet_lock; - struct list_head tasklet_q; - int vfeirq; - void __iomem *vfebase; - void *syncdata; - - struct resource *vfemem; - struct resource *vfeio; - - uint32_t stats_comp; - uint8_t vstate; - uint32_t vfe_capture_count; - - uint32_t vfeFrameId; - uint32_t output1Pattern; - uint32_t output1Period; - uint32_t output2Pattern; - uint32_t output2Period; - uint32_t vfeFrameSkipCount; - uint32_t vfeFrameSkipPeriod; - struct vfe_stats_control afStatsControl; - struct vfe_stats_control awbStatsControl; - struct vfe_stats_control aecStatsControl; - struct vfe_stats_control ihistStatsControl; - struct vfe_stats_control rsStatsControl; - struct vfe_stats_control csStatsControl; -}; - -#define statsAeNum 0 -#define statsAfNum 1 -#define statsAwbNum 2 -#define statsRsNum 3 -#define statsCsNum 4 -#define statsIhistNum 5 -#define statsSkinNum 6 - -struct vfe_cmd_stats_ack{ - uint32_t nextStatsBuf; -}; - -#define VFE_STATS_BUFFER_COUNT 3 - -struct vfe_cmd_stats_buf{ - uint32_t statsBuf[VFE_STATS_BUFFER_COUNT]; -}; - -#define VFE31_OUTPUT_MODE_PT (0x1 << 0) -#define VFE31_OUTPUT_MODE_S (0x1 << 1) -#define VFE31_OUTPUT_MODE_V (0x1 << 2) - -#endif /* __MSM_VFE31_H__ */ diff --git a/drivers/media/video/msm/msm_vfe7x.c b/drivers/media/video/msm/msm_vfe7x.c index 0812bf58cef9a..c7c75f68cacbd 100644 --- a/drivers/media/video/msm/msm_vfe7x.c +++ b/drivers/media/video/msm/msm_vfe7x.c @@ -21,11 +21,11 @@ #include #include #include -#include +#include #include #include +#include #include -#include #include "msm_vfe7x.h" #define QDSP_CMDQUEUE QDSP_vfeCommandQueue @@ -55,9 +55,6 @@ static uint8_t vfestopped; static struct stop_event stopevent; -static struct clk *ebi1_clk; -static const char *const ebi1_clk_name = "ebi1_clk"; - static void vfe_7x_convert(struct msm_vfe_phy_info *pinfo, enum vfe_resp_msg type, void *data, void **ext, int *elen) @@ -234,7 +231,6 @@ static void vfe_7x_release(struct platform_device *pdev) { struct msm_sensor_ctrl *sctrl = &((struct msm_sync *)vfe_syncdata)->sctrl; - mutex_lock(&vfe_lock); vfe_syncdata = NULL; mutex_unlock(&vfe_lock); @@ -256,12 +252,6 @@ static void vfe_7x_release(struct platform_device *pdev) msm_camio_disable(pdev); - if (ebi1_clk) { - clk_set_rate(ebi1_clk, 0); - clk_put(ebi1_clk); - ebi1_clk = 0; - } - kfree(extdata); extdata = 0; } @@ -278,20 +268,7 @@ static int vfe_7x_init(struct msm_vfe_callback *presp, if (presp && presp->vfe_resp) resp = presp; else - return -EIO; - - ebi1_clk = clk_get(NULL, ebi1_clk_name); - if (!ebi1_clk) { - pr_err("%s: could not get %s\n", __func__, ebi1_clk_name); - return -EIO; - } - - rc = clk_set_rate(ebi1_clk, 128000000); - if (rc < 0) { - pr_err("%s: clk_set_rate(%s) failed: %d\n", __func__, - ebi1_clk_name, rc); - return rc; - } + return -EFAULT; /* Bring up all the required GPIOs and Clocks */ rc = msm_camio_enable(dev); @@ -393,60 +370,77 @@ static int vfe_7x_config_axi(int mode, struct axidata *ad, struct axiout *ao) static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) { - int rc = 0; - int i; - - struct msm_pmem_region *regptr = NULL; + struct msm_pmem_region *regptr; + unsigned char buf[256]; struct vfe_stats_ack sack; - struct axidata *axid = NULL; + struct axidata *axid; + uint32_t i; + + struct vfe_stats_we_cfg *scfg = NULL; + struct vfe_stats_af_cfg *sfcfg = NULL; - struct axiout axio; + struct axiout *axio = NULL; + void *cmd_data = NULL; void *cmd_data_alloc = NULL; - struct msm_vfe_command_7k vfecmd; + long rc = 0; + struct msm_vfe_command_7k *vfecmd; + + vfecmd = kmalloc(sizeof(struct msm_vfe_command_7k), GFP_ATOMIC); + if (!vfecmd) { + pr_err("vfecmd alloc failed!\n"); + return -ENOMEM; + } if (cmd->cmd_type != CMD_FRAME_BUF_RELEASE && cmd->cmd_type != CMD_STATS_BUF_RELEASE && cmd->cmd_type != CMD_STATS_AF_BUF_RELEASE) { - if (copy_from_user(&vfecmd, + if (copy_from_user(vfecmd, (void __user *)(cmd->value), sizeof(struct msm_vfe_command_7k))) { rc = -EFAULT; - goto config_error; + goto config_failure; } } switch (cmd->cmd_type) { case CMD_STATS_AEC_AWB_ENABLE: case CMD_STATS_AXI_CFG:{ - struct vfe_stats_we_cfg scfg; axid = data; if (!axid) { rc = -EFAULT; - goto config_error; + goto config_failure; } - if (vfecmd.length != sizeof(typeof(scfg))) { + scfg = + kmalloc(sizeof(struct vfe_stats_we_cfg), + GFP_ATOMIC); + if (!scfg) { + rc = -ENOMEM; + goto config_failure; + } + + if (vfecmd->length != sizeof(typeof(*scfg))) { rc = -EIO; pr_err ("msm_camera: %s: cmd %d: "\ "user-space data size %d "\ "!= kernel data size %d\n", __func__, - cmd->cmd_type, vfecmd.length, - sizeof(typeof(scfg))); - goto config_error; + cmd->cmd_type, vfecmd->length, + sizeof(typeof(*scfg))); + goto config_failure; } - if (copy_from_user(&scfg, - (void __user *)(vfecmd.value), - vfecmd.length)) { + if (copy_from_user(scfg, + (void __user *)(vfecmd->value), + vfecmd->length)) { rc = -EFAULT; - goto config_error; + goto config_done; } CDBG("STATS_ENABLE: bufnum = %d, enabling = %d\n", - axid->bufnum1, scfg.wb_expstatsenable); + axid->bufnum1, scfg->wb_expstatsenable); if (axid->bufnum1 > 0) { regptr = axid->region; @@ -456,48 +450,57 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) CDBG("STATS_ENABLE, phy = 0x%lx\n", regptr->paddr); - scfg.wb_expstatoutputbuffer[i] = + scfg->wb_expstatoutputbuffer[i] = (void *)regptr->paddr; regptr++; } - vfecmd.value = &scfg; + cmd_data = scfg; } else { rc = -EINVAL; - goto config_error; + goto config_done; } } break; case CMD_STATS_AF_ENABLE: case CMD_STATS_AF_AXI_CFG:{ - struct vfe_stats_af_cfg sfcfg; axid = data; if (!axid) { rc = -EFAULT; - goto config_error; + goto config_failure; } - if (vfecmd.length > sizeof(typeof(sfcfg))) { + sfcfg = + kmalloc(sizeof(struct vfe_stats_af_cfg), + GFP_ATOMIC); + + if (!sfcfg) { + rc = -ENOMEM; + goto config_failure; + } + + if (vfecmd->length > sizeof(typeof(*sfcfg))) { pr_err ("msm_camera: %s: cmd %d: user-space "\ "data %d exceeds kernel buffer %d\n", - __func__, cmd->cmd_type, vfecmd.length, - sizeof(typeof(sfcfg))); + __func__, cmd->cmd_type, vfecmd->length, + sizeof(typeof(*sfcfg))); rc = -EIO; - goto config_error; + goto config_failure; } - if (copy_from_user(&sfcfg, - (void __user *)(vfecmd.value), - vfecmd.length)) { + if (copy_from_user(sfcfg, + (void __user *)(vfecmd->value), + vfecmd->length)) { + rc = -EFAULT; - goto config_error; + goto config_done; } CDBG("AF_ENABLE: bufnum = %d, enabling = %d\n", - axid->bufnum1, sfcfg.af_enable); + axid->bufnum1, sfcfg->af_enable); if (axid->bufnum1 > 0) { regptr = axid->region; @@ -507,17 +510,17 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) CDBG("STATS_ENABLE, phy = 0x%lx\n", regptr->paddr); - sfcfg.af_outbuf[i] = + sfcfg->af_outbuf[i] = (void *)regptr->paddr; regptr++; } - vfecmd.value = &sfcfg; + cmd_data = sfcfg; } else { rc = -EINVAL; - goto config_error; + goto config_done; } } break; @@ -528,7 +531,7 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) struct vfe_outputack fack; if (!data) { rc = -EFAULT; - goto config_error; + goto config_failure; } b = (struct msm_frame *)(cmd->value); @@ -541,9 +544,9 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) fack.output2newcbcrbufferaddress = (void *)(p + b->cbcr_off); - vfecmd.queue = QDSP_CMDQUEUE; - vfecmd.length = sizeof(struct vfe_outputack); - vfecmd.value = &fack; + vfecmd->queue = QDSP_CMDQUEUE; + vfecmd->length = sizeof(struct vfe_outputack); + cmd_data = &fack; } break; @@ -554,15 +557,15 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) CDBG("vfe_7x_config: CMD_STATS_BUF_RELEASE\n"); if (!data) { rc = -EFAULT; - goto config_error; + goto config_failure; } sack.header = STATS_WE_ACK; sack.bufaddr = (void *)*(uint32_t *) data; - vfecmd.queue = QDSP_CMDQUEUE; - vfecmd.length = sizeof(struct vfe_stats_ack); - vfecmd.value = &sack; + vfecmd->queue = QDSP_CMDQUEUE; + vfecmd->length = sizeof(struct vfe_stats_ack); + cmd_data = &sack; } break; @@ -570,41 +573,41 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) CDBG("vfe_7x_config: CMD_STATS_AF_BUF_RELEASE\n"); if (!data) { rc = -EFAULT; - goto config_error; + goto config_failure; } sack.header = STATS_AF_ACK; sack.bufaddr = (void *)*(uint32_t *) data; - vfecmd.queue = QDSP_CMDQUEUE; - vfecmd.length = sizeof(struct vfe_stats_ack); - vfecmd.value = &sack; + vfecmd->queue = QDSP_CMDQUEUE; + vfecmd->length = sizeof(struct vfe_stats_ack); + cmd_data = &sack; } break; case CMD_GENERAL: case CMD_STATS_DISABLE:{ - uint8_t buf[256]; - void *tmp = buf; - if (vfecmd.length > sizeof(buf)) { - cmd_data_alloc = tmp = - kmalloc(vfecmd.length, GFP_ATOMIC); - if (!cmd_data_alloc) { + if (vfecmd->length > sizeof(buf)) { + cmd_data_alloc = + cmd_data = + kmalloc(vfecmd->length, GFP_ATOMIC); + if (!cmd_data) { rc = -ENOMEM; - goto config_error; + goto config_failure; } - } + } else + cmd_data = buf; + + if (copy_from_user(cmd_data, + (void __user *)(vfecmd->value), + vfecmd->length)) { - if (copy_from_user(tmp, - (void __user *)(vfecmd.value), - vfecmd.length)) { rc = -EFAULT; - goto config_error; + goto config_done; } - vfecmd.value = tmp; - if (vfecmd.queue == QDSP_CMDQUEUE) { - switch (*(uint32_t *) vfecmd.value) { + if (vfecmd->queue == QDSP_CMDQUEUE) { + switch (*(uint32_t *) cmd_data) { case VFE_RESET_CMD: msm_camio_vfe_blk_reset(); msm_camio_camif_pad_reg_reset_2(); @@ -631,18 +634,24 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) axid = data; if (!axid) { rc = -EFAULT; - goto config_error; + goto config_failure; + } + + axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); + if (!axio) { + rc = -ENOMEM; + goto config_failure; } - if (copy_from_user(&axio, (void *)(vfecmd.value), - sizeof(axio))) { + if (copy_from_user(axio, (void *)(vfecmd->value), + sizeof(struct axiout))) { rc = -EFAULT; - goto config_error; + goto config_done; } - vfe_7x_config_axi(OUTPUT_1, axid, &axio); + vfe_7x_config_axi(OUTPUT_1, axid, axio); - vfecmd.value = &axio; + cmd_data = axio; } break; @@ -651,17 +660,23 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) axid = data; if (!axid) { rc = -EFAULT; - goto config_error; + goto config_failure; + } + + axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); + if (!axio) { + rc = -ENOMEM; + goto config_failure; } - if (copy_from_user(&axio, (void __user *)(vfecmd.value), - sizeof(axio))) { + if (copy_from_user(axio, (void __user *)(vfecmd->value), + sizeof(struct axiout))) { rc = -EFAULT; - goto config_error; + goto config_done; } - vfe_7x_config_axi(OUTPUT_2, axid, &axio); - vfecmd.value = &axio; + vfe_7x_config_axi(OUTPUT_2, axid, axio); + cmd_data = axio; } break; @@ -669,18 +684,24 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) axid = data; if (!axid) { rc = -EFAULT; - goto config_error; + goto config_failure; + } + + axio = kmalloc(sizeof(struct axiout), GFP_ATOMIC); + if (!axio) { + rc = -ENOMEM; + goto config_failure; } - if (copy_from_user(&axio, (void __user *)(vfecmd.value), - sizeof(axio))) { + if (copy_from_user(axio, (void __user *)(vfecmd->value), + sizeof(struct axiout))) { rc = -EFAULT; - goto config_error; + goto config_done; } - vfe_7x_config_axi(OUTPUT_1_AND_2, axid, &axio); + vfe_7x_config_axi(OUTPUT_1_AND_2, axid, axio); - vfecmd.value = &axio; + cmd_data = axio; } break; @@ -689,14 +710,20 @@ static int vfe_7x_config(struct msm_vfe_cfg_cmd *cmd, void *data) } /* switch */ if (vfestopped) - goto config_error; + goto config_done; config_send: - CDBG("send adsp command = %d\n", *(uint32_t *)vfecmd.value); - rc = msm_adsp_write(vfe_mod, vfecmd.queue, vfecmd.value, vfecmd.length); + CDBG("send adsp command = %d\n", *(uint32_t *) cmd_data); + rc = msm_adsp_write(vfe_mod, vfecmd->queue, cmd_data, vfecmd->length); + +config_done: + if (cmd_data_alloc != NULL) + kfree(cmd_data_alloc); -config_error: - kfree(cmd_data_alloc); +config_failure: + kfree(scfg); + kfree(axio); + kfree(vfecmd); return rc; } diff --git a/drivers/media/video/msm/msm_vfe8x.c b/drivers/media/video/msm/msm_vfe8x.c index b4943bec4bebe..1fedb2e36c90f 100644 --- a/drivers/media/video/msm/msm_vfe8x.c +++ b/drivers/media/video/msm/msm_vfe8x.c @@ -22,6 +22,7 @@ #include #include #include "msm_vfe8x_proc.h" +#include #define ON 1 #define OFF 0 @@ -91,9 +92,10 @@ static void vfe_config_axi(int mode, struct axidata *ad, struct vfe_cmd_axi_output_config *ao) { - struct msm_pmem_region *regptr; + struct msm_pmem_region *regptr, *regptr1; int i, j; uint32_t *p1, *p2; + regptr1 = NULL; if (mode == OUTPUT_1 || mode == OUTPUT_1_AND_2) { regptr = ad->region; @@ -141,6 +143,59 @@ static void vfe_config_axi(int mode, regptr++; } } +#ifdef CONFIG_720P_CAMERA + /* For video configuration */ + if (mode == OUTPUT_1_AND_3) { + /* this is preview buffer. */ + regptr = &(ad->region[0]); + /* this is video buffer. */ + regptr1 = &(ad->region[ad->bufnum1]); + CDBG("bufnum1 = %d\n", ad->bufnum1); + CDBG("bufnum2 = %d\n", ad->bufnum2); + + for (i = 0; i < ad->bufnum1; i++) { + p1 = &(ao->output1.outputY.outFragments[i][0]); + p2 = &(ao->output1.outputCbcr.outFragments[i][0]); + + CDBG("config_axi: O1, phy = 0x%lx, y_off = %d, "\ + "cbcr_off = %d\n", regptr->paddr, + regptr->info.y_off, regptr->info.cbcr_off); + + for (j = 0; j < ao->output1.fragmentCount; j++) { + + *p1 = regptr->paddr + regptr->info.y_off; + CDBG("vfe_config_axi: p1 = 0x%x\n", *p1); + p1++; + + *p2 = regptr->paddr + regptr->info.cbcr_off; + CDBG("vfe_config_axi: p2 = 0x%x\n", *p2); + p2++; + } + regptr++; + } + for (i = 0; i < ad->bufnum2; i++) { + p1 = &(ao->output2.outputY.outFragments[i][0]); + p2 = &(ao->output2.outputCbcr.outFragments[i][0]); + + CDBG("config_axi: O2, phy = 0x%lx, y_off = %d, "\ + "cbcr_off = %d\n", regptr1->paddr, + regptr1->info.y_off, regptr1->info.cbcr_off); + + for (j = 0; j < ao->output2.fragmentCount; j++) { + + *p1 = regptr1->paddr + regptr1->info.y_off; + CDBG("vfe_config_axi: p1 = 0x%x\n", *p1); + p1++; + + *p2 = regptr1->paddr + regptr1->info.cbcr_off; + CDBG("vfe_config_axi: p2 = 0x%x\n", *p2); + p2++; + } + regptr1++; + } + } +#endif + } #define ERR_COPY_FROM_USER() \ @@ -503,13 +558,6 @@ static int vfe_proc_general(struct msm_vfe_command_8k *cmd) case VFE_CMD_ID_TEST_GEN_START: break; - case VFE_CMD_ID_EPOCH1_CONFIG:{ - struct vfe_cmds_camif_epoch epoch1; - CHECKED_COPY_FROM_USER(&epoch1); - vfe_epoch1_config(&epoch1); - } - break; - /* acknowledge from upper layer these are not in general command. @@ -622,6 +670,9 @@ static int vfe_config(struct msm_vfe_cfg_cmd *cmd, void *data) b = (struct msm_frame *)(cmd->value); p = *(unsigned long *)data; +#ifndef CONFIG_720P_CAMERA + b->path = MSM_FRAME_ENC; + fack.ybufaddr[0] = (uint32_t) (p + b->y_off); fack.chromabufaddr[0] = (uint32_t) (p + b->cbcr_off); @@ -632,8 +683,19 @@ static int vfe_config(struct msm_vfe_cfg_cmd *cmd, void *data) if (b->path == MSM_FRAME_ENC || b->path == MSM_FRAME_PREV_2) vfe_output2_ack(&fack); +#else + + fack.ybufaddr[0] = (uint32_t) (p + b->y_off); + + fack.chromabufaddr[0] = (uint32_t) (p + b->cbcr_off); + if (b->path == OUTPUT_TYPE_P) + vfe_output_p_ack(&fack); + if ((b->path == OUTPUT_TYPE_V) + || (b->path == OUTPUT_TYPE_S)) + vfe_output_v_ack(&fack); +#endif } break; @@ -659,7 +721,7 @@ static int vfe_config(struct msm_vfe_cfg_cmd *cmd, void *data) vfe_stats_af_ack(&ack); } break; - +#ifndef CONFIG_720P_CAMERA case CMD_AXI_CFG_OUT1: { BUG_ON(!axid); @@ -693,8 +755,7 @@ static int vfe_config(struct msm_vfe_cfg_cmd *cmd, void *data) } break; - case CMD_AXI_CFG_O1_AND_O2: - case CMD_AXI_CFG_SNAP_O1_AND_O2: { + case CMD_AXI_CFG_SNAP_O1_AND_O2:{ BUG_ON(!axid); @@ -708,7 +769,57 @@ static int vfe_config(struct msm_vfe_cfg_cmd *cmd, void *data) vfe_axi_output_config(&axio); } break; +#else + case CMD_AXI_CFG_PREVIEW: + case CMD_RAW_PICT_AXI_CFG: { + + BUG_ON(!axid); + + if (copy_from_user(&axio, (void __user *)(vfecmd.value), + sizeof(axio))) { + pr_err("%s %d: copy_from_user failed\n", + __func__, __LINE__); + return -EFAULT; + } + + vfe_config_axi(OUTPUT_2, axid, &axio); + + axio.outputDataSize = 0; + vfe_axi_output_config(&axio); + } + break; + + case CMD_AXI_CFG_SNAP: { + + BUG_ON(!axid); + if (copy_from_user(&axio, (void __user *)(vfecmd.value), + sizeof(axio))) { + pr_err("%s %d: copy_from_user failed\n", + __func__, __LINE__); + return -EFAULT; + } + + vfe_config_axi(OUTPUT_1_AND_2, axid, &axio); + vfe_axi_output_config(&axio); + } + break; + + case CMD_AXI_CFG_VIDEO: { + BUG_ON(!axid); + + if (copy_from_user(&axio, (void __user *)(vfecmd.value), + sizeof(axio))) { + pr_err("%s %d: copy_from_user failed\n", + __func__, __LINE__); + return -EFAULT; + } + vfe_config_axi(OUTPUT_1_AND_3, axid, &axio); + axio.outputDataSize = 0; + vfe_axi_output_config(&axio); + } + break; +#endif default: break; } /* switch */ diff --git a/drivers/media/video/msm/msm_vfe8x.h b/drivers/media/video/msm/msm_vfe8x.h index 09d5875d4d1f5..dc59b0fdd39bf 100644 --- a/drivers/media/video/msm/msm_vfe8x.h +++ b/drivers/media/video/msm/msm_vfe8x.h @@ -108,10 +108,11 @@ enum vfe_cmd_id { VFE_CMD_ID_STATS_WB_EXP_STOP, VFE_CMD_ID_ASYNC_TIMER_SETTING, - - /* epoch1 */ - VFE_CMD_ID_EPOCH1_CONFIG, - +#ifdef CONFIG_720P_CAMERA + /* Video recording */ + VFE_CMD_ID_START_RECORDING, + VFE_CMD_ID_STOP_RECORDING, +#endif /* max id */ VFE_CMD_ID_MAX }; @@ -807,8 +808,15 @@ enum VFE_MESSAGE_ID { VFE_MSG_ID_START_ACK, VFE_MSG_ID_STOP_ACK, VFE_MSG_ID_UPDATE_ACK, +#ifndef CONFIG_720P_CAMERA VFE_MSG_ID_OUTPUT1, VFE_MSG_ID_OUTPUT2, +#else + VFE_MSG_ID_OUTPUT_P, + VFE_MSG_ID_OUTPUT_V, + VFE_MSG_ID_OUTPUT_S, + VFE_MSG_ID_OUTPUT_T, +#endif VFE_MSG_ID_SNAPSHOT_DONE, VFE_MSG_ID_STATS_AUTOFOCUS, VFE_MSG_ID_STATS_WB_EXP, diff --git a/drivers/media/video/msm/msm_vfe8x_proc.c b/drivers/media/video/msm/msm_vfe8x_proc.c index 23ea197785b13..7d6f83d95e99a 100644 --- a/drivers/media/video/msm/msm_vfe8x_proc.c +++ b/drivers/media/video/msm/msm_vfe8x_proc.c @@ -99,6 +99,7 @@ struct msm_vfe8x_ctrl { void __iomem *vfebase; void *syncdata; + struct msm_camera_sensor_info *s_info; }; static struct msm_vfe8x_ctrl *ctrl; @@ -365,7 +366,8 @@ static void vfe_write_lens_roll_off_table(struct vfe_cmd_roll_off_config *in) writel(data, ctrl->vfebase + VFE_DMI_DATA_LO); data = (((uint32_t) (*initB)) & 0x0000FFFF) | - (((uint32_t) (*initGr)) << 16); + /* 20101011: fix mesh LSC */ + (((uint32_t) (*initGb)) << 16); initB++; initGb++; @@ -378,13 +380,17 @@ static void vfe_write_lens_roll_off_table(struct vfe_cmd_roll_off_config *in) /* pack and write delta table */ for (i = 0; i < VFE_ROLL_OFF_DELTA_TABLE_SIZE; i++) { - data = *pDeltaR | (*pDeltaGr << 16); + /* 20101011: fix mesh LSC */ + data = (((int32_t)(*pDeltaR)) & 0x0000FFFF) | + (((int32_t)(*pDeltaGr))<<16); pDeltaR++; pDeltaGr++; writel(data, ctrl->vfebase + VFE_DMI_DATA_LO); + /* 20101011: fix mesh LSC */ + data = (((int32_t)(*pDeltaB)) & 0x0000FFFF) | + (((int32_t)(*pDeltaGb))<<16); - data = *pDeltaB | (*pDeltaGb << 16); pDeltaB++; pDeltaGb++; @@ -602,6 +608,7 @@ static void vfe_addr_convert(struct msm_vfe_phy_info *pinfo, int *elen) { switch (type) { +#ifndef CONFIG_720P_CAMERA case VFE_MSG_OUTPUT1:{ pinfo->y_phy = ((struct vfe_message *)data)->_u.msgOutput1.yBuffer; @@ -656,6 +663,34 @@ static void vfe_addr_convert(struct msm_vfe_phy_info *pinfo, *elen = sizeof(ctrl->extdata); } break; +#else + case VFE_MSG_OUTPUT_P: + case VFE_MSG_OUTPUT_V:{ + pinfo->y_phy = + ((struct vfe_message *)data)->_u.msgOutput2.yBuffer; + pinfo->cbcr_phy = + ((struct vfe_message *)data)->_u.msgOutput2. + cbcrBuffer; + + CDBG("vfe_addr_convert, pinfo->y_phy = 0x%x\n", + pinfo->y_phy); + CDBG("vfe_addr_convert, pinfo->cbcr_phy = 0x%x\n", + pinfo->cbcr_phy); + /*pinfo->output_id = OUTPUT_TYPE_P;*/ + ctrl->extdata.bpcInfo = + ((struct vfe_message *)data)->_u.msgOutput2.bpcInfo; + ctrl->extdata.asfInfo = + ((struct vfe_message *)data)->_u.msgOutput2.asfInfo; + ctrl->extdata.frameCounter = + ((struct vfe_message *)data)->_u.msgOutput2. + frameCounter; + ctrl->extdata.pmData = + ((struct vfe_message *)data)->_u.msgOutput2.pmData; + *ext = &ctrl->extdata; + *elen = sizeof(ctrl->extdata); + } + break; +#endif case VFE_MSG_STATS_AF: pinfo->sbuf_phy = @@ -672,10 +707,17 @@ static void vfe_addr_convert(struct msm_vfe_phy_info *pinfo, } /* switch */ } -static boolean vfe_send_output1_msg(struct msm_vfe_resp *rp, +static boolean vfe_send_preview_msg(struct msm_vfe_resp *rp, + struct vfe_message *msg, void *data); +static boolean vfe_send_video_msg(struct msm_vfe_resp *rp, struct vfe_message *msg, void *data); -static boolean vfe_send_output2_msg(struct msm_vfe_resp *rp, +#ifdef CONFIG_720P_CAMERA +static boolean vfe_send_mainimage_msg(struct msm_vfe_resp *rp, struct vfe_message *msg, void *data); +static boolean vfe_send_thumbnail_msg(struct msm_vfe_resp *rp, + struct vfe_message *msg, void *data); +#endif + static boolean vfe_send_af_stats_msg(struct msm_vfe_resp *rp, struct vfe_message *msg, void *data); static boolean vfe_send_awb_stats_msg(struct msm_vfe_resp *rp, @@ -700,8 +742,15 @@ static struct { [VFE_MSG_ID_START_ACK] = { NULL, VFE_MSG_GENERAL }, [VFE_MSG_ID_STOP_ACK] = { NULL, VFE_MSG_GENERAL }, [VFE_MSG_ID_UPDATE_ACK] = { NULL, VFE_MSG_GENERAL }, - [VFE_MSG_ID_OUTPUT1] = { vfe_send_output1_msg, VFE_MSG_OUTPUT1 }, - [VFE_MSG_ID_OUTPUT2] = { vfe_send_output2_msg, VFE_MSG_OUTPUT2 }, +#ifndef CONFIG_720P_CAMERA + [VFE_MSG_ID_OUTPUT1] = { vfe_send_preview_msg, VFE_MSG_OUTPUT1 }, + [VFE_MSG_ID_OUTPUT2] = { vfe_send_video_msg, VFE_MSG_OUTPUT2 }, +#else + [VFE_MSG_ID_OUTPUT_P] = { vfe_send_preview_msg, VFE_MSG_OUTPUT_P }, + [VFE_MSG_ID_OUTPUT_V] = { vfe_send_video_msg, VFE_MSG_OUTPUT_V }, + [VFE_MSG_ID_OUTPUT_S] = { vfe_send_mainimage_msg, VFE_MSG_OUTPUT_S }, + [VFE_MSG_ID_OUTPUT_T] = { vfe_send_thumbnail_msg, VFE_MSG_OUTPUT_T }, +#endif [VFE_MSG_ID_SNAPSHOT_DONE] = { NULL, VFE_MSG_SNAPSHOT }, [VFE_MSG_ID_STATS_AUTOFOCUS] = { vfe_send_af_stats_msg, VFE_MSG_STATS_AF }, [VFE_MSG_ID_STATS_WB_EXP] = { vfe_send_awb_stats_msg, VFE_MSG_STATS_WE }, @@ -727,7 +776,6 @@ static void vfe_proc_ops(enum VFE_MESSAGE_ID id, void *data) { struct msm_vfe_resp *rp; struct vfe_message *msg; - struct msm_sync *sync = (struct msm_sync *)ctrl->syncdata; CDBG("ctrl->vfeOperationMode = %d, msgId = %d\n", ctrl->vfeOperationMode, id); @@ -743,11 +791,17 @@ static void vfe_proc_ops(enum VFE_MESSAGE_ID id, void *data) * allocate and then immediately free the msm_vfe_resp structure, * which is wasteful. */ +#ifndef CONFIG_720P_CAMERA if ((ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT) && (id == VFE_MSG_ID_OUTPUT1 || id == VFE_MSG_ID_OUTPUT2)) return; - +#else + if ((ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT) && + (id == VFE_MSG_ID_OUTPUT_T || + id == VFE_MSG_ID_OUTPUT_S)) + return; +#endif rp = ctrl->resp->vfe_alloc(sizeof(*rp) + (vfe_funcs[id].fn ? sizeof(*msg) : 0), ctrl->syncdata, @@ -760,7 +814,12 @@ static void vfe_proc_ops(enum VFE_MESSAGE_ID id, void *data) rp->type = vfe_funcs[id].rt; rp->evt_msg.type = MSM_CAMERA_MSG; rp->evt_msg.msg_id = id; + rp->evt_msg.exttype = 0; + + if (ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT) { + rp->evt_msg.exttype = VFE_MSG_SNAPSHOT; +#if 0 /* google flashlight */ /* Turn off the flash if epoch1 is enabled and snapshot is done. */ if (ctrl->vfeCamifEpoch1Local.enable && ctrl->vfeOperationMode == @@ -768,6 +827,7 @@ static void vfe_proc_ops(enum VFE_MESSAGE_ID id, void *data) id == VFE_MSG_ID_SNAPSHOT_DONE) { ctrl->resp->flash_ctrl(sync, MSM_CAMERA_LED_OFF); ctrl->vfeCamifEpoch1Local.enable = 0; +#endif } if (!vfe_funcs[id].fn) { @@ -781,6 +841,12 @@ static void vfe_proc_ops(enum VFE_MESSAGE_ID id, void *data) else rp->evt_msg.data = msg = 0; rp->evt_msg.len = sizeof(*msg); + + if (msg == NULL) { + pr_err("%s dsp send msg with NULL pointer\n", + __func__); + return ; + } msg->_d = id; if (vfe_funcs[id].fn(rp, msg, data) == FALSE) { pr_info("%s: freeing memory: handler for %d " @@ -853,8 +919,8 @@ static void vfe_process_error_irq(struct isr_queue_cmd *qcmd) static void vfe_process_camif_epoch1_irq(void) { /* Turn on the flash. */ - struct msm_sync *sync = (struct msm_sync *)ctrl->syncdata; - ctrl->resp->flash_ctrl(sync, MSM_CAMERA_LED_HIGH); + /*remove google flashlight*/ + /*ctrl->resp->flash_ctrl(sync, MSM_CAMERA_LED_HIGH);*/ /* Disable the epoch1 interrupt. */ ctrl->vfeImaskLocal.camifEpoch1Irq = FALSE; @@ -1325,9 +1391,25 @@ static void vfe_process_pingpong_irq(struct vfe_output_path *in, } } -static boolean vfe_send_output2_msg(struct msm_vfe_resp *rp, +static boolean vfe_send_video_msg(struct msm_vfe_resp *rp, struct vfe_message *msg, void *data) { +#ifdef CONFIG_720P_CAMERA + struct vfe_msg_output *pPayload = data; + + if (ctrl->vstate != VFE_STATE_ACTIVE) + return FALSE; + memcpy(&(msg->_u), + (void *)pPayload, sizeof(struct vfe_msg_output)); + + rp->phy.output_id = OUTPUT_TYPE_V; + CDBG("vfe_send_video_msg rp->type= %d\n",rp->type); + + vfe_addr_convert(&(rp->phy), + rp->type, msg, + &(rp->extdata), &(rp->extlen)); + return TRUE; +#else struct vfe_msg_output *pPayload = data; if (ctrl->vstate != VFE_STATE_ACTIVE) @@ -1337,7 +1419,6 @@ static boolean vfe_send_output2_msg(struct msm_vfe_resp *rp, (void *)pPayload, sizeof(struct vfe_msg_output)); ctrl->encPath.ackPending = TRUE; - rp->phy.output_id = MSM_FRAME_PREV_2; if (!(ctrl->vfeRequestedSnapShotCount <= 3) && (ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT)) @@ -1347,11 +1428,29 @@ static boolean vfe_send_output2_msg(struct msm_vfe_resp *rp, rp->type, msg, &(rp->extdata), &(rp->extlen)); return TRUE; +#endif } -static boolean vfe_send_output1_msg(struct msm_vfe_resp *rp, +static boolean vfe_send_preview_msg(struct msm_vfe_resp *rp, struct vfe_message *msg, void *data) { +#ifdef CONFIG_720P_CAMERA + struct vfe_msg_output *pPayload = data; + + if (ctrl->vstate != VFE_STATE_ACTIVE) + return FALSE; + + memcpy(&(msg->_u), (void *)pPayload, sizeof(struct vfe_msg_output)); + + rp->phy.output_id = OUTPUT_TYPE_P; + CDBG("vfe_send_preview_msg rp->type= %d\n",rp->type); + + vfe_addr_convert(&(rp->phy), + rp->type, msg, + &(rp->extdata), &(rp->extlen)); + + return TRUE; +#else struct vfe_msg_output *pPayload = data; if (ctrl->vstate != VFE_STATE_ACTIVE) @@ -1360,7 +1459,7 @@ static boolean vfe_send_output1_msg(struct msm_vfe_resp *rp, memcpy(&(msg->_u), (void *)pPayload, sizeof(struct vfe_msg_output)); ctrl->viewPath.ackPending = TRUE; - rp->phy.output_id = MSM_FRAME_PREV_1; + if (!(ctrl->vfeRequestedSnapShotCount <= 3) && (ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT)) ctrl->viewPath.ackPending = TRUE; @@ -1369,9 +1468,60 @@ static boolean vfe_send_output1_msg(struct msm_vfe_resp *rp, rp->type, msg, &(rp->extdata), &(rp->extlen)); + return TRUE; +#endif +} + +#ifdef CONFIG_720P_CAMERA + +static boolean vfe_send_thumbnail_msg(struct msm_vfe_resp *rp, + struct vfe_message *msg, void *data) +{ + struct vfe_msg_output *pPayload = data; + + if (ctrl->vstate != VFE_STATE_ACTIVE) + return FALSE; + + memcpy(&(msg->_u), (void *)pPayload, sizeof(struct vfe_msg_output)); + + rp->phy.output_id = OUTPUT_TYPE_T; + CDBG("vfe_send_thumbnail_msg rp->type= %d\n",rp->type); + + if (ctrl->viewPath.snapshotPendingCount <= 1) + ctrl->viewPath.ackPending = FALSE; + + vfe_addr_convert(&(rp->phy), + rp->type, msg, + &(rp->extdata), &(rp->extlen)); + return TRUE; } +static boolean vfe_send_mainimage_msg(struct msm_vfe_resp *rp, + struct vfe_message *msg, void *data) +{ + struct vfe_msg_output *pPayload = data; + + if (ctrl->vstate != VFE_STATE_ACTIVE) + return FALSE; + + memcpy(&(msg->_u), (void *)pPayload, sizeof(struct vfe_msg_output)); + + rp->phy.output_id = OUTPUT_TYPE_S; + CDBG("vfe_send_mainimage_msg rp->type= %d\n",rp->type); + + if (ctrl->encPath.snapshotPendingCount <=1 ) { + ctrl->encPath.ackPending = FALSE; + } + + vfe_addr_convert(&(rp->phy), + rp->type, msg, + &(rp->extdata), &(rp->extlen)); + + return TRUE; +} +#endif + static void vfe_send_output_msg(boolean whichOutputPath, uint32_t yPathAddr, uint32_t cbcrPathAddr) { @@ -1395,6 +1545,7 @@ static void vfe_send_output_msg(boolean whichOutputPath, /* frame ID is common for both paths. */ msgPayload.frameCounter = ctrl->vfeFrameId; +#ifndef CONFIG_720P_CAMERA if (whichOutputPath) { /* msgPayload.pmData = ctrl->vfePmData.encPathPmInfo; */ vfe_proc_ops(VFE_MSG_ID_OUTPUT2, &msgPayload); @@ -1402,6 +1553,35 @@ static void vfe_send_output_msg(boolean whichOutputPath, /* msgPayload.pmData = ctrl->vfePmData.viewPathPmInfo; */ vfe_proc_ops(VFE_MSG_ID_OUTPUT1, &msgPayload); } +#else + if (whichOutputPath) {/* vfe output2 physical path */ + /* msgPayload.pmData = ctrl->vfePmData.encPathPmInfo; */ + ctrl->encPath.ackPending = TRUE; + + if (ctrl->vfeOperationMode == 0) { + if (ctrl->axiOutputMode == VFE_AXI_OUTPUT_MODE_Output1AndOutput2) { + /* video mode */ + vfe_proc_ops(VFE_MSG_ID_OUTPUT_V, &msgPayload); + } else { /* preview mode */ + vfe_proc_ops(VFE_MSG_ID_OUTPUT_P, &msgPayload); + } + } else { + vfe_proc_ops(VFE_MSG_ID_OUTPUT_S, &msgPayload); + } + + } else { /* physical output1 path from vfe */ + ctrl->viewPath.ackPending = TRUE; + + if (ctrl->vfeOperationMode == 0) { + vfe_proc_ops(VFE_MSG_ID_OUTPUT_P, &msgPayload); + CDBG(" ==== check output ==== video mode display output.\n"); + + } else { + vfe_proc_ops(VFE_MSG_ID_OUTPUT_T, &msgPayload); + CDBG(" ==== check output ==== snapshot mode thumbnail output.\n"); + } + } +#endif } static void vfe_process_frame_done_irq_multi_frag(struct vfe_output_path_combo @@ -1595,6 +1775,7 @@ static void vfe_process_output_path_irq(struct vfe_interrupt_status *irqstatus) encPath); } else { + CDBG("horng irqstatus->encIrq = %x\n", irqstatus->encIrq); if (irqstatus->encIrq) vfe_process_frame_done_irq_no_frag(&ctrl-> encPath); @@ -1614,6 +1795,8 @@ static void vfe_process_output_path_irq(struct vfe_interrupt_status *irqstatus) } } +static int preview_skipframe; +#define FRAME_SKIP 2 static void __vfe_do_tasklet(struct isr_queue_cmd *qcmd) { if (qcmd->vfeInterruptStatus.regUpdateIrq) { @@ -1623,6 +1806,7 @@ static void __vfe_do_tasklet(struct isr_queue_cmd *qcmd) if (qcmd->vfeInterruptStatus.resetAckIrq) { CDBG("%s: process resetAckIrq\n", __func__); + preview_skipframe = 0; vfe_process_reset_irq(); } @@ -1641,7 +1825,11 @@ static void __vfe_do_tasklet(struct isr_queue_cmd *qcmd) /* next, check output path related interrupts. */ if (qcmd->vfeInterruptStatus.anyOutputPathIrqs) { CDBG("irq: anyOutputPathIrqs\n"); - vfe_process_output_path_irq(&qcmd->vfeInterruptStatus); + if(preview_skipframe > FRAME_SKIP || + ctrl->vfeOperationMode == VFE_START_OPERATION_MODE_SNAPSHOT) + vfe_process_output_path_irq(&qcmd->vfeInterruptStatus); + else + preview_skipframe ++; } if (qcmd->vfeInterruptStatus.afPingpongIrq) @@ -1713,6 +1901,9 @@ static void vfe_do_tasklet(unsigned long data) int cnt = 0; struct isr_queue_cmd *qcmd = NULL; + if (!ctrl) + return; + CDBG("%s\n", __func__); while ((qcmd = next_irq_cmd())) { @@ -1836,6 +2027,7 @@ int vfe_cmd_init(struct msm_vfe_callback *presp, } ctrl->syncdata = sdata; + ctrl->s_info = s_info; return 0; cmd_init_failed3: @@ -1858,7 +2050,11 @@ void vfe_cmd_release(struct platform_device *dev) iounmap(ctrl->vfebase); mem = platform_get_resource(dev, IORESOURCE_MEM, 0); - release_mem_region(mem->start, (mem->end - mem->start) + 1); + if (mem == NULL) { + pr_err("%s : platform get resource is NULL pointer\n", + __func__); + } else + release_mem_region(mem->start, (mem->end - mem->start) + 1); kfree(ctrl); ctrl = 0; @@ -2074,6 +2270,7 @@ void vfe_stats_wb_exp_ack(struct vfe_cmd_stats_wb_exp_ack *in) ctrl->awbStatsControl.ackPending = FALSE; } +#ifndef CONFIG_720P_CAMERA void vfe_output2_ack(struct vfe_cmd_output_ack *in) { const uint32_t *psrc; @@ -2114,6 +2311,69 @@ void vfe_output1_ack(struct vfe_cmd_output_ack *in) ctrl->viewPath.ackPending = FALSE; } +#else + +void vfe_output_v_ack(struct vfe_cmd_output_ack *in) +{ + const uint32_t *psrc; + uint32_t *pdest; + uint8_t i; + + pdest = ctrl->encPath.nextFrameAddrBuf; + +// CDBG("output2_ack: ack addr = 0x%x\n", in->ybufaddr[0]); + CDBG("video_frame_ack: ack addr = 0x%x\n", in->ybufaddr[0]); + + psrc = in->ybufaddr; + for (i = 0; i < ctrl->encPath.fragCount; i++) + *pdest++ = *psrc++; + + psrc = in->chromabufaddr; + for (i = 0; i < ctrl->encPath.fragCount; i++) + *pdest++ = *psrc++; + + ctrl->encPath.ackPending = FALSE; +} + +void vfe_output_p_ack(struct vfe_cmd_output_ack *in) +{ + const uint32_t *psrc; + uint32_t *pdest; + uint8_t i; + + if (ctrl->axiOutputMode == VFE_AXI_OUTPUT_MODE_Output1AndOutput2 ) { + /* video mode, preview comes from output1 path */ + + pdest = ctrl->viewPath.nextFrameAddrBuf; + + psrc = in->ybufaddr; + for (i = 0; i < ctrl->viewPath.fragCount; i++) + *pdest++ = *psrc++; + + psrc = in->chromabufaddr; + for (i = 0; i < ctrl->viewPath.fragCount; i++) + *pdest++ = *psrc++; + + ctrl->viewPath.ackPending = FALSE; + + } else { /* preview mode, preview comes from output2 path. */ + pdest = ctrl->encPath.nextFrameAddrBuf; + + psrc = in->ybufaddr; + for (i = 0; i < ctrl->encPath.fragCount; i++) + *pdest++ = *psrc++; + + psrc = in->chromabufaddr; + for (i = 0; i < ctrl->encPath.fragCount; i++) + *pdest++ = *psrc++; + + ctrl->encPath.ackPending = FALSE; + + } +} + +#endif + void vfe_start(struct vfe_cmd_start *in) { uint32_t pmstatus = 0; diff --git a/drivers/media/video/msm/msm_vfe8x_proc.h b/drivers/media/video/msm/msm_vfe8x_proc.h index aef7ecac22221..27db7df28e194 100644 --- a/drivers/media/video/msm/msm_vfe8x_proc.h +++ b/drivers/media/video/msm/msm_vfe8x_proc.h @@ -1564,7 +1564,11 @@ void vfe_fov_crop_config(struct vfe_cmd_fov_crop_config *); void vfe_get_hw_version(struct vfe_cmd_hw_version *); void vfe_reset(void); void vfe_cmd_release(struct platform_device *); +#ifndef CONFIG_720P_CAMERA void vfe_output1_ack(struct vfe_cmd_output_ack *); void vfe_output2_ack(struct vfe_cmd_output_ack *); -void vfe_epoch1_config(struct vfe_cmds_camif_epoch *); +#else +void vfe_output_p_ack(struct vfe_cmd_output_ack *); +void vfe_output_v_ack(struct vfe_cmd_output_ack *); +#endif #endif /* __MSM_VFE8X_REG_H__ */ diff --git a/drivers/media/video/msm/mt9d112.c b/drivers/media/video/msm/mt9d112.c index 5a6a8dbdd96c7..868445f1e04c9 100644 --- a/drivers/media/video/msm/mt9d112.c +++ b/drivers/media/video/msm/mt9d112.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include "mt9d112.h" diff --git a/drivers/media/video/msm/mt9p012_fox.c b/drivers/media/video/msm/mt9p012_fox.c index f2ff40a6614f5..299c59bae0a5e 100644 --- a/drivers/media/video/msm/mt9p012_fox.c +++ b/drivers/media/video/msm/mt9p012_fox.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/media/video/msm/ov8810.c b/drivers/media/video/msm/ov8810.c new file mode 100644 index 0000000000000..8e29135d7e42b --- /dev/null +++ b/drivers/media/video/msm/ov8810.c @@ -0,0 +1,2749 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ov8810.h" + + +/* CAMIF output resolutions */ +/* 816x612, 24MHz MCLK 96MHz PCLK */ +#define OV8810_FULL_SIZE_DUMMY_PIXELS 0 +#define OV8810_FULL_SIZE_DUMMY_LINES 0 +#define OV8810_FULL_SIZE_WIDTH 3280 +#define OV8810_FULL_SIZE_HEIGHT 2456 + +#define OV8810_QTR_SIZE_DUMMY_PIXELS 0 +#define OV8810_QTR_SIZE_DUMMY_LINES 0 +#define OV8810_QTR_SIZE_WIDTH 1632 +#define OV8810_QTR_SIZE_HEIGHT 1224 + +#define OV8810_HRZ_FULL_BLK_PIXELS 696 /*stella 1203*/ +#define OV8810_VER_FULL_BLK_LINES 44 +#define OV8810_HRZ_QTR_BLK_PIXELS 890 +#define OV8810_VER_QTR_BLK_LINES 44 + +static int cam_mode_sel = 0; /* 0: photo, 1: video@30fps, 2: video@24fps */ +/* 240: 26, 365: 24, 589: 21 */ +const int ov8810_ver_qtr_blk_lines_array[] = {44, 44, 365}; + +/*============================================================= + SENSOR REGISTER DEFINES +==============================================================*/ +#define Q8 0x00000100 + +/* Omnivision8810 product ID register address */ +#define OV8810_PIDH_REG 0x300A +#define OV8810_PIDL_REG 0x300B + +/* Omnivision8810 product ID */ +#define OV8810_PID 0x88 +/* Omnivision8810 version */ +#define OV8810_VER 0x10 + +/* Time in milisecs for waiting for the sensor to reset */ +#define OV8810_RESET_DELAY_MSECS 66 + +#define OV8810_DEFAULT_CLOCK_RATE 24000000 + +/* Registers*/ +/* PLL Registers */ +#define REG_PRE_PLL_CLK_DIV 0x3011 /*0x0305*/ +#define REG_PLL_MULTIPLIER 0x3010 +#define REG_VT_CLK_DIV 0x300E /*[7:4]VT_SYS_DIV, [3-0]VT_PIX_DIV*/ +#define REG_OP_CLK_DIV 0x300F /*[7:4]OP_SYS_DIV, [3-0]OP_PIX_DIV*/ + +/* ISP Enable Control */ +#define REG_ISP_ENABLE_CONTROL_00 0x3302 +#define REG_ISP_ENABLE_CONTROL_01 0x3301 + +/* AWB Control */ +#define REG_AWB_CTRL_0 0x3320 +#define REG_AWB_CTRL_1 0x3321 +#define REG_AWB_CTRL_2 0x3322 +#define REG_AWB_CTRL_8 0x3328 + +/* Output Size */ +#define REG_X_OUTPUT_SIZE_MSB 0x302C +#define REG_X_OUTPUT_SIZE_LSB 0x302D +#define REG_Y_OUTPUT_SIZE_MSB 0x302E +#define REG_Y_OUTPUT_SIZE_LSB 0x302F + +/*Reserved register */ +#define REG_BINNING_CONTROL 0x3091 + +/* Frame Fotmat */ +#define REG_FRAME_LENGTH_LINES_MSB 0x3020 +#define REG_FRAME_LENGTH_LINES_LSB 0x3021 +#define REG_LINE_LENGTH_PCK_MSB 0x3022 +#define REG_LINE_LENGTH_PCK_LSB 0x3023 +#define REG_EXTRA_VSYNC_WIDTH_MSB 0x301E +#define REG_EXTRA_VSYNC_WIDTH_LSB 0x301F + +#define REG_X_ADDR_START_HIGH 0x3024 +#define REG_X_ADDR_START_LOW 0x3025 +#define REG_Y_ADDR_START_HIGH 0x3026 +#define REG_Y_ADDR_START_LOW 0x3027 +#define REG_X_ADDR_END_HIGH 0x3028 +#define REG_X_ADDR_END_LOW 0x3029 +#define REG_Y_ADDR_END_HIGH 0x302A +#define REG_Y_ADDR_END_LOW 0x302B + +/* Gain setting register */ +#define OV8810_GAIN 0x3000 +#define OV8810_AEC_MSB 0x3002 +#define OV8810_AEC_LSB 0x3003 + +/* additional gain function provided by OV8810, + * original gain can changed to 1x, 2x or 4x + * to increase the gain that OV8810 can provide */ +#define OV8810_REG_MUL_GAIN 0x3006 +#define MUL_GAIN_INIT_VALUE 0x00 + +#define OV8810_MAX_EXPOSURE_GAIN 0x1FF + +/* Mode select register */ +#define OV8810_REG_MODE_SELECT 0x30FA /* image system */ +#define OV8810_MODE_SELECT_STREAM 0x01 /* start streaming */ +#define OV8810_MODE_SELECT_SW_STANDBY 0x00 /* software standby */ +#define OV8810_REG_SOFTWARE_RESET 0x3012 /* 0x0103 */ +#define OV8810_SOFTWARE_RESET 0x80 /* 0x01 */ + +/* AF Total steps parameters */ +#define OV8810_AF_MSB 0x30EC +#define OV8810_AF_LSB 0x30ED + +#define OV8810_STEPS_NEAR_TO_CLOSEST_INF 42 /*43 stella0122 */ +#define OV8810_TOTAL_STEPS_NEAR_TO_FAR 42 /*43 stella0122 */ + +/*Test pattern*/ +/* Color bar pattern selection */ +#define OV8810_COLOR_BAR_PATTERN_SEL_REG 0x307B + +/* Color bar enabling control */ +#define OV8810_COLOR_BAR_ENABLE_REG 0x307D + +/* I2C Address of the Sensor */ +#define OV8810_I2C_SLAVE_ID 0x6C + +/*LSC table length*/ +#define LSC_table_length 144 +/*============================================================================ +TYPE DECLARATIONS +============================================================================*/ + +/* 16bit address - 8 bit context register structure */ +#if 0 +typedef struct reg_addr_val_pair_struct { + uint16_t reg_addr; + uint8_t reg_val; +} reg_struct_type; +#endif + +struct awb_lsc_struct_type { + unsigned int caBuff[8]; /*awb_calibartion*/ + struct reg_addr_val_pair_struct LSC_table[150]; /*lsc_calibration*/ + uint32_t LSC_table_CRC; +}; + +enum ov8810_test_mode_t { + TEST_OFF, + TEST_1, + TEST_2, + TEST_3 +}; + +enum ov8810_resolution_t { + QTR_SIZE, + FULL_SIZE, + INVALID_SIZE +}; + +/*LSC calibration*/ +int global_mode; +/*TODO: should be use a header file to reference this function*/ +extern unsigned char *get_cam_awb_cal(void); + +static int sensor_probe_node = 0; + +static struct wake_lock ov8810_wake_lock; + +static inline void init_suspend(void) +{ + wake_lock_init(&ov8810_wake_lock, WAKE_LOCK_IDLE, "ov8810"); +} + +static inline void deinit_suspend(void) +{ + wake_lock_destroy(&ov8810_wake_lock); +} + +static inline void prevent_suspend(void) +{ + wake_lock(&ov8810_wake_lock); +} + +static inline void allow_suspend(void) +{ + wake_unlock(&ov8810_wake_lock); +} + +/*============================================================================ +DATA DECLARATIONS +============================================================================*/ + +/* 96MHz PCLK @ 24MHz MCLK inc*/ + /*stella1223 start*/ +static struct reg_addr_val_pair_struct ov8810_init_settings_array[] = +{ + /* Sensor clk setup */ + {REG_OP_CLK_DIV, 0x04}, + {REG_VT_CLK_DIV, 0x05}, +#if 1 /* weiting0414 prevent capture hang restore CLK */ + {REG_PLL_MULTIPLIER, 0x28}, /*0x28 96MHz PCLK 0x18 64MHz PCLK*/ + {REG_PRE_PLL_CLK_DIV, 0x22}, +#else + {REG_PLL_MULTIPLIER, 0x14}, /*Reduce internal clock to prevent hang Weiting0331*/ + {REG_PRE_PLL_CLK_DIV, 0x21}, +#endif + {OV8810_GAIN, 8}, /*0x30},*/ + {OV8810_AEC_MSB, 0x04}, + {OV8810_AEC_LSB, 0xc4}, /*stella 1203*/ + {REG_ISP_ENABLE_CONTROL_00, 0x20}, + {0x30b2, 0x13}, /*driving strength*/ + {0x30a0, 0x40}, + {0x3098, 0x24}, + {0x3099, 0x81}, + {0x309a, 0x64}, + {0x309b, 0x00}, + {0x309d, 0x64}, + {0x309e, 0x2d}, + {REG_AWB_CTRL_0, 0xc2}, /*set wb manual*/ + {REG_AWB_CTRL_1, 0x02}, + {REG_AWB_CTRL_2, 0x04}, + {REG_AWB_CTRL_8, 0x40}, + {0x3329, 0xe3}, /*00},*/ /*stella 1203*/ + {0x3306, 0x00}, + {0x3316, 0x03}, + {0x3079, 0x0a}, + /*stella 1203*/ + {0x3058, 0x01}, + {0x3059, 0xa0}, + {0x306b, 0x00}, + {0x3065, 0x50}, + {0x3067, 0x40}, + {0x3069, 0x80}, + {0x3071, 0x40},/*50 BLC trigger by gain 40 BLC every frame */ + {0x3300, 0xef}, + {0x3334, 0x02}, + {0x3331, 0x08}, /*BLC level 8813*/ /*stella 1203*/ + {0x3332, 0x08}, /*8813*/ + {0x3333, 0x41}, + /*Stella1221 for adding init size */ + {0x30f8, 0x45}, + {REG_FRAME_LENGTH_LINES_MSB, + ((OV8810_QTR_SIZE_HEIGHT + OV8810_VER_QTR_BLK_LINES) & 0xFF00) >> 8}, + {REG_FRAME_LENGTH_LINES_LSB, + ((OV8810_QTR_SIZE_HEIGHT + OV8810_VER_QTR_BLK_LINES) & 0x00FF)}, + {REG_LINE_LENGTH_PCK_MSB, + ((OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS) & 0xFF00) >> 8}, + {REG_LINE_LENGTH_PCK_LSB, + ((OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS) & 0x00FF)}, + {REG_X_ADDR_START_HIGH, 0x00}, + {REG_X_ADDR_START_LOW, 0x04}, /*stella 1203*/ + {REG_Y_ADDR_START_HIGH, 0x00}, + {REG_Y_ADDR_START_LOW, 0x00}, + {REG_X_ADDR_END_HIGH, 0x0c}, + {REG_X_ADDR_END_LOW, 0xdb}, /*stella 1203*/ + {REG_Y_ADDR_END_HIGH, 0x09}, + {REG_Y_ADDR_END_LOW, 0x9f}, + {REG_X_OUTPUT_SIZE_MSB, (OV8810_QTR_SIZE_WIDTH & 0xFF00) >> 8}, + {REG_X_OUTPUT_SIZE_LSB, (OV8810_QTR_SIZE_WIDTH & 0x00FF)}, + {REG_Y_OUTPUT_SIZE_MSB, (OV8810_QTR_SIZE_HEIGHT & 0xFF00) >> 8}, + {REG_Y_OUTPUT_SIZE_LSB, (OV8810_QTR_SIZE_HEIGHT & 0x00FF)}, + /*Stella1221 for adding init size */ + /* {REG_BINNING_CONTROL, 0x00},*/ /*stella 1203*/ + {OV8810_REG_MUL_GAIN, MUL_GAIN_INIT_VALUE}, + {0x3082, 0x80}, + {0x331e, 0x94}, + {0x331f, 0x6e}, + {0x3092, 0x00}, + {0x3094, 0x01}, + {0x3090, 0x2b}, /* for AN version 8a */ /*changed by Stella for 8813*/ + {0x30ab, 0x44}, + {0x3095, 0x0a}, + {0x308d, 0x00}, + {0x3082, 0x00}, + {0x3080, 0x40}, + {0x30aa, 0x59}, + {0x30a9, 0x00}, + {0x30be, 0x08}, + {0x309f, 0x23}, + {0x3065, 0x40}, + {0x3068, 0x00}, + {0x30bf, 0x80}, + {0x309c, 0x00}, + {0x3084, 0x44}, /*added by stella for 8813*/ + {0x3016, 0x03}, /*added by stella for 8813*/ + {0x30e9, 0x09}, /*changed by stella for 8813*/ + {0x3075, 0x29}, + {0x3076, 0x29}, + {0x3077, 0x29}, + {0x3078, 0x29}, + {0x306a, 0x05}, + {0x3015, 0x33}, /*changed by stella for 8813*/ + /*stella 1203 start*/ + {0x3090, 0x36}, + {0x333e, 0x00}, + {0x306a, 0x05}, + /*stella 1203 end*/ + {0x3087, 0x41}, + {0x3090, 0x97}, /*99, QCT=97*/ + {0x309e, 0x1b}, + {0x30e3, 0x0e}, + {0x30f0, 0x00}, + {0x30f2, 0x00}, + {0x30f4, 0x90}, + /*stella 1203 start*/ + {0x3347, 0x00}, + {0x3347, 0x00}, +#if 0 + {0x3092, 0x00}, //marked by QCT + {0x30f0, 0x10}, //marked by QCT + {0x30f1, 0x56}, //marked by QCT + {0x30fb, 0x8e}, //marked by QCT + {0x30f3, 0xa7}, //marked by QCT +#endif + {0x3091, 0x08}, /*QCT for 8813*/ + {0x3090, 0x97}, /*QCT for 8813*/ + {0x30fb, 0xc9}, /*QCT for 8813*/ + {0x308d, 0x02}, + {0x30e7, 0x41}, + {0x30b3, 0x08}, + {0x33e5, 0x00}, /*30e5*/ + {0x350e, 0x40}, /*305e*/ + {0x301f, 0x00}, + {0x309f, 0x23}, + {0x3013, 0xc0}, + {0x30e1, 0x90}, + {0x3058, 0x01}, + {0x3500, 0x40}, /* vsync_new */ + {REG_BINNING_CONTROL, 0x00}, /*stella 0126*/ + /*stella 1203 end*/ +}; + +/*Vincent for LSC calibration*/ +static struct reg_addr_val_pair_struct lsc_table_array[] = +{ + {0x3358, 0x1f },//{0x3358, 0x18}, + {0x3359, 0x14 },//{0x3359, 0x0f}, + {0x335a, 0x0f },//{0x335a, 0x0c}, + {0x335b, 0x0d },//{0x335b, 0x0a}, + {0x335c, 0x0d },//{0x335c, 0x0a}, + {0x335d, 0x0f },//{0x335d, 0x0b}, + {0x335e, 0x14 },//{0x335e, 0x0d}, + {0x335f, 0x1d },//{0x335f, 0x15}, + {0x3360, 0x0f },//{0x3360, 0x0b}, + {0x3361, 0x0a },//{0x3361, 0x09}, + {0x3362, 0x07 },//{0x3362, 0x06}, + {0x3363, 0x06 },//{0x3363, 0x05}, + {0x3364, 0x06 },//{0x3364, 0x05}, + {0x3365, 0x07 },//{0x3365, 0x06}, + {0x3366, 0x09 },//{0x3366, 0x08}, + {0x3367, 0x0d },//{0x3367, 0x0b}, + {0x3368, 0x09 },//{0x3368, 0x07}, + {0x3369, 0x06 },//{0x3369, 0x05}, + {0x336a, 0x04 },//{0x336a, 0x03}, + {0x336b, 0x03 },//{0x336b, 0x02}, + {0x336c, 0x03 },//{0x336c, 0x02}, + {0x336d, 0x04 },//{0x336d, 0x03}, + {0x336e, 0x06 },//{0x336e, 0x04}, + {0x336f, 0x09 },//{0x336f, 0x06}, + {0x3370, 0x07 },//{0x3370, 0x05}, + {0x3371, 0x04 },//{0x3371, 0x04}, + {0x3372, 0x01 },//{0x3372, 0x01}, + {0x3373, 0x00 },//{0x3373, 0x00}, + {0x3374, 0x00 },//{0x3374, 0x00}, + {0x3375, 0x01 },//{0x3375, 0x01}, + {0x3376, 0x04 },//{0x3376, 0x03}, + {0x3377, 0x07 },//{0x3377, 0x05}, + {0x3378, 0x08 },//{0x3378, 0x05}, + {0x3379, 0x04 },//{0x3379, 0x03}, + {0x337a, 0x01 },//{0x337a, 0x01}, + {0x337b, 0x00 },//{0x337b, 0x00}, + {0x337c, 0x00 },//{0x337c, 0x00}, + {0x337d, 0x01 },//{0x337d, 0x00}, + {0x337e, 0x04 },//{0x337e, 0x02}, + {0x337f, 0x07 },//{0x337f, 0x05}, + {0x3380, 0x09 },//{0x3380, 0x06}, + {0x3381, 0x06 },//{0x3381, 0x04}, + {0x3382, 0x04 },//{0x3382, 0x03}, + {0x3383, 0x02 },//{0x3383, 0x02}, + {0x3384, 0x02 },//{0x3384, 0x01}, + {0x3385, 0x04 },//{0x3385, 0x02}, + {0x3386, 0x06 },//{0x3386, 0x03}, + {0x3387, 0x09 },//{0x3387, 0x05}, + {0x3388, 0x0f },//{0x3388, 0x0a}, + {0x3389, 0x0a },//{0x3389, 0x07}, + {0x338a, 0x07 },//{0x338a, 0x05}, + {0x338b, 0x07 },//{0x338b, 0x04}, + {0x338c, 0x07 },//{0x338c, 0x04}, + {0x338d, 0x07 },//{0x338d, 0x05}, + {0x338e, 0x0a },//{0x338e, 0x06}, + {0x338f, 0x0f },//{0x338f, 0x09}, + {0x3390, 0x1d },//{0x3390, 0x12}, + {0x3391, 0x12 },//{0x3391, 0x0d}, + {0x3392, 0x0d },//{0x3392, 0x09}, + {0x3393, 0x0b },//{0x3393, 0x08}, + {0x3394, 0x0b },//{0x3394, 0x08}, + {0x3395, 0x0d },//{0x3395, 0x09}, + {0x3396, 0x12 },//{0x3396, 0x0c}, + {0x3397, 0x1a },//{0x3397, 0x11}, + {0x3398, 0x0f },//{0x3398, 0x10}, + {0x3399, 0x0d },//{0x3399, 0x10}, + {0x339a, 0x0e },//{0x339a, 0x10}, + {0x339b, 0x0f },//{0x339b, 0x0e}, + {0x339c, 0x11 },//{0x339c, 0x0e}, + {0x339d, 0x0d },//{0x339d, 0x0f}, + {0x339e, 0x12 },//{0x339e, 0x0e}, + {0x339f, 0x0e },//{0x339f, 0x0f}, + {0x33a0, 0x0f },//{0x33a0, 0x0f}, + {0x33a1, 0x0f },//{0x33a1, 0x0f}, + {0x33a2, 0x10 },//{0x33a2, 0x0f}, + {0x33a3, 0x10 },//{0x33a3, 0x10}, + {0x33a4, 0x0f },//{0x33a4, 0x0e}, + {0x33a5, 0x0d },//{0x33a5, 0x10}, + {0x33a6, 0x0f },//{0x33a6, 0x11}, + {0x33a7, 0x10 },//{0x33a7, 0x10}, + {0x33a8, 0x10 },//{0x33a8, 0x10}, + {0x33a9, 0x0f },//{0x33a9, 0x0f}, + {0x33aa, 0x10 },//{0x33aa, 0x0e}, + {0x33ab, 0x0e },//{0x33ab, 0x0f}, + {0x33ac, 0x10 },//{0x33ac, 0x10}, + {0x33ad, 0x11 },//{0x33ad, 0x10}, + {0x33ae, 0x11 },//{0x33ae, 0x10}, + {0x33af, 0x0f },//{0x33af, 0x0f}, + {0x33b0, 0x0f },//{0x33b0, 0x0e}, + {0x33b1, 0x0d },//{0x33b1, 0x0f}, + {0x33b2, 0x0d },//{0x33b2, 0x0f}, + {0x33b3, 0x0e },//{0x33b3, 0x0f}, + {0x33b4, 0x0f },//{0x33b4, 0x0f}, + {0x33b5, 0x10 },//{0x33b5, 0x0f}, + {0x33b6, 0x12 },//{0x33b6, 0x0e}, + {0x33b7, 0x0d },//{0x33b7, 0x0d}, + {0x33b8, 0x0c },//{0x33b8, 0x0c}, + {0x33b9, 0x0c },//{0x33b9, 0x0c}, + {0x33ba, 0x0c },//{0x33ba, 0x0d}, + {0x33bb, 0x0b },//{0x33bb, 0x0f}, + {0x33bc, 0x1b },//{0x33bc, 0x16}, + {0x33bd, 0x1b },//{0x33bd, 0x17}, + {0x33be, 0x1d },//{0x33be, 0x17}, + {0x33bf, 0x1d },//{0x33bf, 0x17}, + {0x33c0, 0x1e },//{0x33c0, 0x17}, + {0x33c1, 0x1c },//{0x33c1, 0x14}, + {0x33c2, 0x1a },//{0x33c2, 0x17}, + {0x33c3, 0x17 },//{0x33c3, 0x14}, + {0x33c4, 0x15 },//{0x33c4, 0x13}, + {0x33c5, 0x16 },//{0x33c5, 0x13}, + {0x33c6, 0x19 },//{0x33c6, 0x14}, + {0x33c7, 0x1e },//{0x33c7, 0x15}, + {0x33c8, 0x16 },//{0x33c8, 0x15}, + {0x33c9, 0x12 },//{0x33c9, 0x12}, + {0x33ca, 0x10 },//{0x33ca, 0x10}, + {0x33cb, 0x10 },//{0x33cb, 0x10}, + {0x33cc, 0x14 },//{0x33cc, 0x12}, + {0x33cd, 0x19 },//{0x33cd, 0x14}, + {0x33ce, 0x16 },//{0x33ce, 0x15}, + {0x33cf, 0x12 },//{0x33cf, 0x12}, + {0x33d0, 0x10 },//{0x33d0, 0x10}, + {0x33d1, 0x11 },//{0x33d1, 0x10}, + {0x33d2, 0x14 },//{0x33d2, 0x12}, + {0x33d3, 0x1a },//{0x33d3, 0x14}, + {0x33d4, 0x18 },//{0x33d4, 0x16}, + {0x33d5, 0x15 },//{0x33d5, 0x13}, + {0x33d6, 0x13 },//{0x33d6, 0x12}, + {0x33d7, 0x14 },//{0x33d7, 0x12}, + {0x33d8, 0x17 },//{0x33d8, 0x13}, + {0x33d9, 0x1b },//{0x33d9, 0x15}, + {0x33da, 0x18 },//{0x33da, 0x18}, + {0x33db, 0x1a },//{0x33db, 0x15}, + {0x33dc, 0x1b },//{0x33dc, 0x15}, + {0x33dd, 0x1b },//{0x33dd, 0x15}, + {0x33de, 0x1b },//{0x33de, 0x15}, + {0x33df, 0x1c },//{0x33df, 0x14}, + {0x3350, 0x06 },//{0x3350, 0x06}, + {0x3351, 0xab },//{0x3351, 0xab}, + {0x3352, 0x05 },//{0x3352, 0x05}, + {0x3353, 0x00 },//{0x3353, 0x00}, + {0x3354, 0x04 },//{0x3354, 0x04}, + {0x3355, 0xf8 },//{0x3355, 0xf8}, + {0x3356, 0x07 },//{0x3356, 0x07}, + {0x3357, 0x74 },//{0x3357, 0x74}, + /* lsc setting on sensor*/ + {0x3300, 0xff}, /*enable lsc on sensor*/ + /*move to the last*/ + {OV8810_REG_MODE_SELECT, OV8810_MODE_SELECT_STREAM}, +}; + +/*1632x1224; 24MHz MCLK 96MHz PCLK*/ +static struct reg_addr_val_pair_struct ov8810_qtr_settings_array[] = +{ + {0x30f8, 0x45}, + {REG_FRAME_LENGTH_LINES_MSB, + ((OV8810_QTR_SIZE_HEIGHT + OV8810_VER_QTR_BLK_LINES) & 0xFF00) >> 8}, + {REG_FRAME_LENGTH_LINES_LSB, + ((OV8810_QTR_SIZE_HEIGHT + OV8810_VER_QTR_BLK_LINES) & 0x00FF)}, + {REG_LINE_LENGTH_PCK_MSB, + ((OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS) & 0xFF00) >> 8}, + {REG_LINE_LENGTH_PCK_LSB, + ((OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS) & 0x00FF)}, + {REG_X_ADDR_START_HIGH, 0x00}, + {REG_X_ADDR_START_LOW, 0x04}, /*stella 1203*/ + {REG_Y_ADDR_START_HIGH, 0x00}, + {REG_Y_ADDR_START_LOW, 0x00}, + {REG_X_ADDR_END_HIGH, 0x0c}, + {REG_X_ADDR_END_LOW, 0xd8}, /*stella 1203=db*/ /*QCT:d8*/ + {REG_Y_ADDR_END_HIGH, 0x09}, + {REG_Y_ADDR_END_LOW, 0x9f}, + {REG_X_OUTPUT_SIZE_MSB, (OV8810_QTR_SIZE_WIDTH & 0xFF00) >> 8}, + {REG_X_OUTPUT_SIZE_LSB, (OV8810_QTR_SIZE_WIDTH & 0x00FF)}, + {REG_Y_OUTPUT_SIZE_MSB, (OV8810_QTR_SIZE_HEIGHT & 0xFF00) >> 8}, + {REG_Y_OUTPUT_SIZE_LSB, (OV8810_QTR_SIZE_HEIGHT & 0x00FF)}, + /*stella1202 for capture over exposure issue due to user space use 2X line count*/ + {0x3068, 0x00}, /*changed for color edge, stella 1203*/ + {0x307e, 0x00}, + {0x3071, 0x40},/*50 BLC trigger by gain 40 BLC every frame */ + {REG_ISP_ENABLE_CONTROL_01, 0x0B}, + {REG_BINNING_CONTROL, 0x00}, //stella0127 + {0x331c, 0x00}, + {0x331d, 0x00}, + {0x308a, 0x02}, + {0x3072, 0x0d}, + {0x3319, 0x04}, + {0x309e, 0x09}, + {0x300e, 0x05}, + {0x300f, 0x04}, + {0x33e4, 0x07}, /*lsc for 2:1 down sampling*/ +}; + + /*stella1223 end*/ + +/* 3280x2456 Sensor Raw; 24MHz MCLK 96MHz PCLK*/ +static struct reg_addr_val_pair_struct ov8810_full_settings_array[] = +{ + {0x30f8, 0x40}, + {REG_FRAME_LENGTH_LINES_MSB, + ((OV8810_FULL_SIZE_HEIGHT + OV8810_VER_FULL_BLK_LINES) & 0xFF00) >> 8}, + {REG_FRAME_LENGTH_LINES_LSB, + ((OV8810_FULL_SIZE_HEIGHT + OV8810_VER_FULL_BLK_LINES) & 0x00FF)}, + {REG_LINE_LENGTH_PCK_MSB, + ((OV8810_FULL_SIZE_WIDTH + OV8810_HRZ_FULL_BLK_PIXELS) & 0xFF00) >> 8}, + {REG_LINE_LENGTH_PCK_LSB, + ((OV8810_FULL_SIZE_WIDTH + OV8810_HRZ_FULL_BLK_PIXELS) & 0x00FF)}, + {REG_X_ADDR_START_HIGH, 0x00}, + {REG_X_ADDR_START_LOW, 0x02}, /*stella 1203*/ + {REG_Y_ADDR_START_HIGH, 0x00}, + {REG_Y_ADDR_START_LOW, 0x00}, + {REG_X_ADDR_END_HIGH, 0x0c}, + {REG_X_ADDR_END_LOW, 0xdd}, /*stella 1203*/ + {REG_Y_ADDR_END_HIGH, 0x09}, + {REG_Y_ADDR_END_LOW, 0x9f}, + {REG_X_OUTPUT_SIZE_MSB, (OV8810_FULL_SIZE_WIDTH & 0xFF00) >> 8}, + {REG_X_OUTPUT_SIZE_LSB, (OV8810_FULL_SIZE_WIDTH & 0x00FF)}, + {REG_Y_OUTPUT_SIZE_MSB, (OV8810_FULL_SIZE_HEIGHT & 0xFF00) >> 8}, + {REG_Y_OUTPUT_SIZE_LSB, (OV8810_FULL_SIZE_HEIGHT & 0x00FF)}, + /*stella1202 for capture over exposure issue + due to user space use 2X line count */ + {0x3068, 0x00}, /* changed for color edge stella 1203*/ + {0x307e, 0x00}, + {REG_ISP_ENABLE_CONTROL_01, 0x0B}, + {REG_BINNING_CONTROL, 0x00}, //stella0127 + {0x331c, 0x28}, + {0x331d, 0x21}, + {0x308a, 0x01}, + {0x3072, 0x01}, + {0x3319, 0x06}, + {0x309e, 0x1b}, + {0x300e, 0x05}, + {0x300f, 0x04}, + {0x33e4, 0x02}, /*lsc for full resolution*/ +}; + +/* AF Tuning Parameters */ + +static uint16_t ov8810_step_position_table[OV8810_TOTAL_STEPS_NEAR_TO_FAR+1]; + +static uint8_t ov8810_damping_threshold = 10; +static uint8_t ov8810_damping_course_step = 4; +static uint8_t ov8810_damping_fine_step = 10; +static uint8_t ov8810_damping_time_wait; +static uint16_t ov8810_focus_debug; /*don't init to 0*/ +static uint16_t ov8810_use_default_damping = 1; +static uint16_t ov8810_use_threshold_damping = 1; /*set to FALSE if too slow*/ +/*static uint32_t stored_line_length_ratio = 1 * Q8;*/ + +/*Andy1217 write Line 1 frame ealier before Gain*/ +struct backup_line_gain_struct { + uint32_t line; + uint8_t mul; + uint16_t gain; + uint32_t extra_line_length; +}; + +static struct backup_line_gain_struct backup_line_gain[2]; + +static uint16_t write_cnt; +static uint16_t updated_BLC; /* only set to 0x50 after 1st update again*/ + +uint8_t S3_to_0 = 0x1; /* 0x9 */ + +/* static Variables*/ +static uint16_t step_position_table[OV8810_TOTAL_STEPS_NEAR_TO_FAR+1]; + + +/* FIXME: Changes from here */ +struct ov8810_work { + struct work_struct work; +}; + +static struct ov8810_work *ov8810_sensorw; +static struct i2c_client *ov8810_client; + +static struct vreg *vreg_af_actuator; + +struct ov8810_ctrl { + const struct msm_camera_sensor_info *sensordata; + + uint32_t sensormode; + uint32_t fps_divider; /* init to 1 * 0x00000400 */ + uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */ + uint16_t fps; + + int16_t curr_lens_pos; + uint16_t curr_step_pos; + uint16_t my_reg_gain; + uint32_t my_reg_line_count; + uint16_t total_lines_per_frame; + + enum ov8810_resolution_t prev_res; + enum ov8810_resolution_t pict_res; + enum ov8810_resolution_t curr_res; + enum ov8810_test_mode_t set_test; + + unsigned short imgaddr; +}; + + +static struct ov8810_ctrl *ov8810_ctrl; +static struct platform_device *ov8810_pdev; + +struct ov8810_waitevent{ + uint32_t waked_up; + wait_queue_head_t event_wait; +}; +static struct ov8810_waitevent ov8810_event; + +static DECLARE_WAIT_QUEUE_HEAD(ov8810_wait_queue); +DEFINE_SEMAPHORE(ov8810_sem); + + +/*=============================================================*/ + +static int ov8810_i2c_rxdata(unsigned short saddr, + unsigned char *rxdata, int length) +{ + struct i2c_msg msgs[] = { + { + .addr = saddr, + .flags = 0, + .len = 2, + .buf = rxdata, + }, + { + .addr = saddr, + .flags = I2C_M_RD, + .len = length, + .buf = rxdata, + }, + }; + CDBG("%s: saddr=0x%X\n", __func__, saddr); + CDBG("%s: raddr=0x%X\n", __func__, *rxdata); + + if (i2c_transfer(ov8810_client->adapter, msgs, 2) < 0) { + pr_err("ov8810_i2c_rxdata failed!\n"); + return -EIO; + } + CDBG("%s: rxdata=0x%X\n", __func__, *rxdata); + + return 0; +} +static int32_t ov8810_i2c_txdata(unsigned short saddr, + unsigned char *txdata, int length) +{ + struct i2c_msg msg[] = { + { + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, + }, + }; + if (i2c_transfer(ov8810_client->adapter, msg, 1) < 0) { + pr_err("ov8810_i2c_txdata faild 0x%x\n", ov8810_client->addr); + return -EIO; + } + + return 0; +} + + +static int32_t ov8810_i2c_read(unsigned short raddr, + unsigned short *rdata, int rlen) +{ + int32_t rc = 0; + unsigned char buf[2]; + int count = 0; + if (!rdata) + return -EIO; + + memset(buf, 0, sizeof(buf)); + + buf[0] = (raddr & 0xFF00) >> 8; + buf[1] = (raddr & 0x00FF); +retry: + rc = ov8810_i2c_rxdata(ov8810_client->addr, buf, rlen); + + if (rc < 0) { + pr_err("ov8810_i2c_read 0x%x failed!\n", raddr); + printk(KERN_ERR "starting read retry policy count:%d\n", count); + udelay(10); + count++; + if (count < 20) { + if (count > 10) + udelay(100); + } else + return rc; + goto retry; + } + + *rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]); + return rc; +} + + +static int32_t ov8810_i2c_write_b(unsigned short saddr, + unsigned short waddr, uint8_t bdata) +{ + int32_t rc = -EFAULT; + unsigned char buf[3]; + int count = 0; + CDBG("i2c_write_w_b, addr = 0x%x, val = 0x%x!\n", waddr, bdata); + + memset(buf, 0, sizeof(buf)); + buf[0] = (waddr & 0xFF00) >> 8; + buf[1] = (waddr & 0x00FF); + buf[2] = bdata; + +retry: + CDBG("i2c_write_b addr = %d, val = %d\n", waddr, bdata); + rc = ov8810_i2c_txdata(saddr, buf, 3); + + if (rc < 0) { + pr_err("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n", + waddr, bdata); + pr_err(KERN_ERR "starting read retry policy count:%d\n", count); + udelay(10); + count++; + if (count < 20) { + if (count > 10) + udelay(100); + } else + return rc; + goto retry; + } + + return rc; +} + + +/*for LSC calibration*/ +static int ov8810_update_lsc_table(struct sensor_cfg_data *cdata) +{ + int i = 0; + pr_info("[LSC calibration]ov8810_update_lsc_table\n"); + for (i = 0; i < 144; i++) { + ov8810_i2c_write_b( + ov8810_client->addr, + cdata->cfg.lsctable.lsc_table[i].reg_addr, + cdata->cfg.lsctable.lsc_table[i].reg_val); + pr_info("[LSC calibration]update_lsc_table: 0x%x, 0x%x\n", + cdata->cfg.lsctable.lsc_table[i].reg_addr, + cdata->cfg.lsctable.lsc_table[i].reg_val); + } + /*enable lsc on sensor*/ + ov8810_i2c_write_b(ov8810_client->addr, 0x3300, 0xff); + /*mirror on*/ + ov8810_i2c_write_b(ov8810_client->addr, 0x30f8, 0x45); + /*mirror on*/ + ov8810_i2c_write_b(ov8810_client->addr, 0x3316, 0x03); + return 1; + +} + +/*20100330 vincent for LSC calibration*/ +static int ov8810_LSC_calibration_set_rawflag(struct sensor_cfg_data *cdata) +{ + global_mode = 1; + return 1; +} + +#define MAX_FUSE_ID_INFO 11 +static int ov8810_i2c_read_fuseid(struct sensor_cfg_data *cdata) +{ + unsigned short fuse_id[MAX_FUSE_ID_INFO]; + int count = 0; + + ov8810_i2c_write_b(ov8810_client->addr, 0x30d5, 0xff); + ov8810_i2c_write_b(ov8810_client->addr, 0x30d6, 0xff); + ov8810_i2c_write_b(ov8810_client->addr, 0x30d7, 0xff); + ov8810_i2c_write_b(ov8810_client->addr, 0x30d8, 0xff); + ov8810_i2c_write_b(ov8810_client->addr, 0x30d9, 0xff); + ov8810_i2c_write_b(ov8810_client->addr, 0x30da, 0xff); + ov8810_i2c_write_b(ov8810_client->addr, 0x30db, 0xff); + ov8810_i2c_write_b(ov8810_client->addr, 0x30dc, 0xff); + ov8810_i2c_write_b(ov8810_client->addr, 0x30dd, 0xff); + ov8810_i2c_write_b(ov8810_client->addr, 0x30de, 0xff); + ov8810_i2c_write_b(ov8810_client->addr, 0x30df, 0xff); + ov8810_i2c_write_b(ov8810_client->addr, 0x303e, 0x55); + ov8810_i2c_read(0x30d5, &fuse_id[0], 2); + ov8810_i2c_read(0x30d6, &fuse_id[1], 2); + ov8810_i2c_read(0x30d7, &fuse_id[2], 2); + ov8810_i2c_read(0x30d8, &fuse_id[3], 2); + ov8810_i2c_read(0x30d9, &fuse_id[4], 2); + ov8810_i2c_read(0x30da, &fuse_id[5], 2); + ov8810_i2c_read(0x30db, &fuse_id[6], 2); + ov8810_i2c_read(0x30dc, &fuse_id[7], 2); + ov8810_i2c_read(0x30dd, &fuse_id[8], 2); + ov8810_i2c_read(0x30de, &fuse_id[9], 2); + ov8810_i2c_read(0x30df, &fuse_id[10], 2); + cdata->cfg.fuse.fuse_id_word1 = (uint32_t) fuse_id[0]; + cdata->cfg.fuse.fuse_id_word2 = (uint32_t) fuse_id[1]; + cdata->cfg.fuse.fuse_id_word3 = 0; + cdata->cfg.fuse.fuse_id_word4 = 0; + for (count = 0; count < MAX_FUSE_ID_INFO; count++) + pr_info("Ov8810 Get fuse: fuse_id[%d]: %x\n", + count, fuse_id[count]); + return 0; +} + + +static int32_t ov8810_af_i2c_write(uint16_t data) +{ + uint8_t code_val_msb, code_val_lsb; /* S3_to_0; */ + uint32_t rc = 0; + /* S3_to_0 = 0x9; S[3:0] */ + code_val_msb = data >> 4; /* D[9:4] */ + code_val_lsb = ((data & 0x000F) << 4) | S3_to_0; + + CDBG("code value = %d ,D[9:4] = %d ,D[3:0] = %d\n", + data, code_val_msb, code_val_lsb); + rc = ov8810_i2c_write_b(ov8810_client->addr, + OV8810_AF_MSB, code_val_msb); + + if (rc < 0) { + pr_err("Unable to write code_val_msb = %d\n", code_val_msb); + return rc; + } + + rc = ov8810_i2c_write_b(ov8810_client->addr, + OV8810_AF_LSB, code_val_lsb); + if (rc < 0) { + pr_err("Unable to write code_val_lsb = %disclaimer\n", + code_val_lsb); + return rc; + } + + return rc; +} /* ov8810_af_i2c_write */ + +static int32_t ov8810_move_focus(int direction, int32_t num_steps) +{ + + int8_t step_direction; + int8_t dest_step_position; + uint16_t dest_lens_position, target_dist, small_step; + int16_t next_lens_position; + int32_t rc = 0; + + if (num_steps == 0) { + return rc; + } + + if (direction == MOVE_NEAR) { + step_direction = 1; + } else if (direction == MOVE_FAR) { + step_direction = -1; + } else { + pr_err("Illegal focus direction\n"); + return -EINVAL;; /* CAMERA_INVALID_PARM; */ + } + + CDBG("%s, interpolate\n", __func__); + dest_step_position = + ov8810_ctrl->curr_step_pos + (step_direction * num_steps); + + if (dest_step_position < 0) + dest_step_position = 0; + else if (dest_step_position > OV8810_TOTAL_STEPS_NEAR_TO_FAR) + dest_step_position = OV8810_TOTAL_STEPS_NEAR_TO_FAR; + + dest_lens_position = ov8810_step_position_table[dest_step_position]; + + /* Taking small damping steps */ + target_dist = step_direction * + (dest_lens_position - ov8810_ctrl->curr_lens_pos); + + if (target_dist == 0) { + return rc; + } + + if (ov8810_use_threshold_damping && + (step_direction < 0) && + (target_dist >= + ov8810_step_position_table[ov8810_damping_threshold])) { + + /* change to variable */ + small_step = (uint16_t)(target_dist/ov8810_damping_fine_step); + ov8810_damping_time_wait = 1; + } else { + small_step = (uint16_t)(target_dist/ov8810_damping_course_step); + ov8810_damping_time_wait = 4; + } + + for (next_lens_position = + ov8810_ctrl->curr_lens_pos + (step_direction * small_step); + (step_direction * next_lens_position) <= + (step_direction * dest_lens_position); + next_lens_position += (step_direction * small_step)) { + + if (ov8810_af_i2c_write(next_lens_position) < 0) + return -EBUSY; + + ov8810_ctrl->curr_lens_pos = next_lens_position; + + if (ov8810_ctrl->curr_lens_pos != dest_lens_position) { + mdelay(ov8810_damping_time_wait); + } + } + + if (ov8810_ctrl->curr_lens_pos != dest_lens_position) { + + if (ov8810_af_i2c_write(dest_lens_position) < 0) { + return -EBUSY; + } + } + + /* Storing the current lens Position */ + ov8810_ctrl->curr_lens_pos = dest_lens_position; + ov8810_ctrl->curr_step_pos = dest_step_position; + + CDBG("done\n"); + return rc; +} + +static int32_t ov8810_set_default_focus(uint8_t af_step) +{ + int16_t position; + int32_t rc = 0; + ov8810_damping_time_wait = 4; + + if (ov8810_use_default_damping) { + + /* when lens is uninitialized */ + if (ov8810_ctrl->curr_lens_pos == -1 + || (ov8810_focus_debug == 1)) { + + position = ov8810_step_position_table[ov8810_damping_threshold]; + rc = ov8810_af_i2c_write(position); + + if (rc < 0) { + return rc; + } + + ov8810_ctrl->curr_step_pos = ov8810_damping_threshold; + ov8810_ctrl->curr_lens_pos = position; + mdelay(ov8810_damping_time_wait); + } + + rc = ov8810_move_focus(MOVE_FAR, ov8810_ctrl->curr_step_pos); + if (rc < 0) + return rc; + } else { + rc = ov8810_af_i2c_write(ov8810_step_position_table[0]); + if (rc < 0) + return rc; + + ov8810_ctrl->curr_step_pos = 0; + ov8810_ctrl->curr_lens_pos = ov8810_step_position_table[0]; + } + + return rc; +} + + +static void ov8810_get_pict_fps(uint16_t fps, uint16_t *pfps) +{ + /* input fps is preview fps in Q8 format */ + + uint32_t divider, d1, d2; + + uint16_t snapshot_height, preview_height, preview_width, snapshot_width; + + if (ov8810_ctrl->prev_res == QTR_SIZE) { + preview_width = + OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS ; + preview_height = + OV8810_QTR_SIZE_HEIGHT + ov8810_ver_qtr_blk_lines_array[cam_mode_sel] ; + } else { + /* full size resolution used for preview. */ + preview_width = + OV8810_FULL_SIZE_WIDTH + OV8810_HRZ_FULL_BLK_PIXELS ; + preview_height = + OV8810_FULL_SIZE_HEIGHT + OV8810_VER_FULL_BLK_LINES ; + } + + if (ov8810_ctrl->pict_res == QTR_SIZE) { + snapshot_width = + OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS ; + snapshot_height = + OV8810_QTR_SIZE_HEIGHT + ov8810_ver_qtr_blk_lines_array[cam_mode_sel] ; + + } else { + snapshot_width = + OV8810_FULL_SIZE_WIDTH + OV8810_HRZ_FULL_BLK_PIXELS; + snapshot_height = + OV8810_FULL_SIZE_HEIGHT + OV8810_VER_FULL_BLK_LINES; + } + + d1 = preview_height * 0x00000400 / snapshot_height; + d2 = preview_width * 0x00000400 / snapshot_width; + + divider = (uint32_t) (d1 * d2) / 0x00000400; + *pfps = (uint16_t)(fps * divider / 0x00000400); + +} /* endof ov8810_get_pict_fps */ + +static uint16_t ov8810_get_prev_lines_pf(void) +{ + if (ov8810_ctrl->prev_res == QTR_SIZE) { + return (OV8810_QTR_SIZE_HEIGHT + ov8810_ver_qtr_blk_lines_array[cam_mode_sel]); + } else { + return (OV8810_FULL_SIZE_HEIGHT + OV8810_VER_FULL_BLK_LINES); + } +} + +static uint16_t ov8810_get_prev_pixels_pl(void) +{ + if (ov8810_ctrl->prev_res == QTR_SIZE) { + return (OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS); + } else { + return (OV8810_FULL_SIZE_WIDTH + OV8810_HRZ_FULL_BLK_PIXELS); +} +} + +static uint16_t ov8810_get_pict_lines_pf(void) +{ + if (ov8810_ctrl->pict_res == QTR_SIZE) { + return (OV8810_QTR_SIZE_HEIGHT + ov8810_ver_qtr_blk_lines_array[cam_mode_sel]); + } else { + return (OV8810_FULL_SIZE_HEIGHT + OV8810_VER_FULL_BLK_LINES); + } +} + +static uint16_t ov8810_get_pict_pixels_pl(void) +{ + if (ov8810_ctrl->pict_res == QTR_SIZE) { + return (OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS); + } else { + return (OV8810_FULL_SIZE_WIDTH + OV8810_HRZ_FULL_BLK_PIXELS); + } +} + +static uint32_t ov8810_get_pict_max_exp_lc(void) +{ + if (ov8810_ctrl->pict_res == QTR_SIZE) { + return (OV8810_QTR_SIZE_HEIGHT + ov8810_ver_qtr_blk_lines_array[cam_mode_sel]); + } else { + return (OV8810_FULL_SIZE_HEIGHT + OV8810_VER_FULL_BLK_LINES); + } +} + +static int32_t ov8810_set_fps(struct fps_cfg *fps) +{ + int32_t rc = 0; + ov8810_ctrl->fps_divider = fps->fps_div; + ov8810_ctrl->pict_fps_divider = fps->pict_fps_div; + ov8810_ctrl->fps = fps->f_mult; + return rc; +} + + +static int32_t ov8810_write_exp_gain + (uint16_t mul, uint16_t gain, uint32_t line) +{ + uint16_t aec_msb; + uint16_t aec_lsb; + int32_t rc = 0; + uint32_t total_lines_per_frame; + uint32_t total_pixels_per_line; + /*uint32_t line_length_ratio = 1 * Q8;*/ + /**uint8_t ov8810_offset = 2; */ + uint32_t extra_line_length = 0; + uint16_t extra_line_msb = 0; + uint16_t extra_line_lsb = 0; + uint32_t phy_line = 0; + uint8_t phy_mul = MUL_GAIN_INIT_VALUE; + uint16_t phy_gain = 0; + uint32_t phy_extra_line_length = 0; + const uint16_t postpone_frames = 4; + uint16_t do_write = 1; /* assume do things */ + uint16_t ori_reg_mul_gain; + uint8_t ori_reg_mul_gain_8bit; + + CDBG("%s start, mul = %d gain = %d line = %d\n", __func__, + mul, gain, line); + + if (ov8810_ctrl->curr_res == QTR_SIZE) { + total_lines_per_frame = + (OV8810_QTR_SIZE_HEIGHT + ov8810_ver_qtr_blk_lines_array[cam_mode_sel]); + total_pixels_per_line = + OV8810_QTR_SIZE_WIDTH + OV8810_HRZ_QTR_BLK_PIXELS; + } else { + total_lines_per_frame = + (OV8810_FULL_SIZE_HEIGHT + OV8810_VER_FULL_BLK_LINES); + total_pixels_per_line = + OV8810_FULL_SIZE_WIDTH + OV8810_HRZ_FULL_BLK_PIXELS; + } + + if (line > total_lines_per_frame - 4) { + extra_line_length = + (uint32_t)(line - (total_lines_per_frame-4)); + line = total_lines_per_frame - 4; + } else { + extra_line_length = (uint16_t)0; + } + + phy_line = line; + phy_mul = mul; + phy_gain = gain; + phy_extra_line_length = extra_line_length; + + /* postpone writing gain only apply to preview */ + if (ov8810_ctrl->sensormode == SENSOR_PREVIEW_MODE) { + + /* need time to wait for aec stable (prevent black preview) */ + mdelay(6); + + CDBG("Stella: write_cnt=%d, pre_line = %d, line = %d," \ + "pre_mul = %d mul = %d," \ + "pre_gain = %d gain = %d," \ + "pre_extra_line_length =%d extra_line_length = %d\n", + write_cnt, + backup_line_gain[1].line, line, + backup_line_gain[1].mul, mul, + backup_line_gain[1].gain, gain, + backup_line_gain[1].extra_line_length, extra_line_length); + + if (write_cnt == 0 && ( + backup_line_gain[1].line != line || + backup_line_gain[1].mul != mul || + backup_line_gain[1].gain != gain || + backup_line_gain[1].extra_line_length != extra_line_length)) { + + backup_line_gain[1].line = line; + backup_line_gain[1].mul = mul; + backup_line_gain[1].gain = gain; + backup_line_gain[1].extra_line_length = extra_line_length; + phy_line = backup_line_gain[1].line; + phy_mul = backup_line_gain[0].mul; + phy_gain = backup_line_gain[0].gain; + phy_extra_line_length = backup_line_gain[0].extra_line_length; + write_cnt++; + } else if (write_cnt >= 1 && write_cnt < postpone_frames) { + phy_line = backup_line_gain[1].line; + phy_mul = backup_line_gain[1].mul; + phy_gain = backup_line_gain[1].gain; + phy_extra_line_length = backup_line_gain[1].extra_line_length; + + CDBG("updated_BLC = %d\n", updated_BLC); + if (updated_BLC == 5) { + /*50 BLC trigger by gain 40 BLC every frame */ + pr_info("### BLC to 0x50 ###\n"); +#if 0 + ov8810_i2c_write_b(ov8810_client->addr, 0x3071, 0x50); +#endif + } + if (updated_BLC <= 5) + updated_BLC++; + + if (write_cnt > 1) + do_write = 0; + write_cnt++; + } else { + write_cnt = 0; + do_write = 0; + } + + if (do_write) { + backup_line_gain[0].line = phy_line; + backup_line_gain[0].mul = phy_mul; + backup_line_gain[0].gain = phy_gain; + backup_line_gain[0].extra_line_length = phy_extra_line_length; + } + + } +#if 0 + pr_info("Stella: backup_line_gain[0].line = %d\n", + backup_line_gain[0].line); + pr_info("Stella: backup_line_gain[0].mul = %d\n", + backup_line_gain[0].mul); + pr_info("Stella: backup_line_gain[0].gain = %d\n", + backup_line_gain[0].gain); + pr_info("Stella: backup_line_gain[0].extra_line_length = %d\n", + backup_line_gain[0].extra_line_length); + pr_info("Stella: backup_line_gain[1].line = %d\n", + backup_line_gain[1].line); + pr_info("Stella: backup_line_gain[1].mul = %d\n", + backup_line_gain[1].mul); + pr_info("Stella: backup_line_gain[1].gain = %d\n", + backup_line_gain[1].gain); + pr_info("Stella: backup_line_gain[1].extra_line_length = %d\n", + backup_line_gain[1].extra_line_length); + + pr_info("Stella: phy_line=%d\n", phy_line); + pr_info("Stella: phy_gain=%d\n", phy_gain); + pr_info("Stella: phy_extra_line_length=%d\n", phy_extra_line_length); +#endif + + extra_line_msb = (uint16_t)(phy_extra_line_length & 0xFF00) >> 8; + extra_line_lsb = (uint16_t)(phy_extra_line_length & 0x00FF); + + aec_msb = (uint16_t)(phy_line & 0xFF00) >> 8; + aec_lsb = (uint16_t)(phy_line & 0x00FF); + + if (!do_write) + return rc; + +/*Move the read function out of group update to prevent hang Weiting0331*/ + rc = ov8810_i2c_read(OV8810_REG_MUL_GAIN, + &ori_reg_mul_gain, 2); + if (rc < 0) { + pr_err("read OV8810_REG_MUL_GAIN fail\n"); + return rc; + } + + + /* since we do STREAM ON here, don't do group update for snapshot */ + if (ov8810_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) { + /*for group update top*/ + /* weiting0414 prevent capture hang, enable 0x30b7[2] */ + rc = ov8810_i2c_write_b(ov8810_client->addr, 0x30b7, 0x8c); + if (rc < 0) + return rc; + } + + /* FIXME: prevent black preview by restoring 0x30bf -> 0x80 */ + rc = ov8810_i2c_write_b(ov8810_client->addr, 0x30bf, 0x80); + if (rc < 0) + return rc; + + rc = ov8810_i2c_write_b(ov8810_client->addr, + OV8810_AEC_MSB, (uint8_t)aec_msb); + if (rc < 0) + return rc; + + rc = ov8810_i2c_write_b(ov8810_client->addr, + OV8810_AEC_LSB, (uint8_t)aec_lsb); + if (rc < 0) + return rc; + + ori_reg_mul_gain_8bit = + (uint8_t)((ori_reg_mul_gain & 0xFF00) >> 8); + CDBG("%s, read OV8810_REG_MUL_GAIN ori_reg_mul_gain = %x\n", + __func__, ori_reg_mul_gain_8bit); + ori_reg_mul_gain_8bit = + (ori_reg_mul_gain_8bit & 0xFC) | (phy_mul & 0x03); + CDBG("%s, read OV8810_REG_MUL_GAIN ori_reg_mul_gain = %x\n", + __func__, ori_reg_mul_gain_8bit); + rc = ov8810_i2c_write_b(ov8810_client->addr, + OV8810_REG_MUL_GAIN, ori_reg_mul_gain_8bit); + if (rc < 0) + return rc; + + rc = ov8810_i2c_write_b(ov8810_client->addr, + OV8810_GAIN, (uint8_t)phy_gain); + if (rc < 0) + return rc; + + rc = ov8810_i2c_write_b(ov8810_client->addr, + REG_EXTRA_VSYNC_WIDTH_MSB, (uint8_t)extra_line_msb); + if (rc < 0) + return rc; + + rc = ov8810_i2c_write_b(ov8810_client->addr, + REG_EXTRA_VSYNC_WIDTH_LSB, (uint8_t)extra_line_lsb); + if (rc < 0) + return rc; + + if (ov8810_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) { + /* for group update bottom */ + /* weiting0414 prevent capture hang , enable 0x30b7[2] */ + rc = ov8810_i2c_write_b(ov8810_client->addr, 0x30b7, 0x84); + if (rc < 0) + return rc; + + /* for group update enable */ + rc = ov8810_i2c_write_b(ov8810_client->addr, 0x30ff, 0xff); + if (rc < 0) + return rc; + /* weiting0414 prevent capture hang , + retry I2C write to make sure enable */ + rc = ov8810_i2c_write_b(ov8810_client->addr, 0x30ff, 0xff); + if (rc < 0) + return rc; + } + + if (ov8810_ctrl->sensormode == SENSOR_RAW_SNAPSHOT_MODE) { + pr_info("sleep 500 ms for safety raw snapshot"); + msleep(500); + } + + /* STREAM ON for SNAPSHOT */ + if (ov8810_ctrl->sensormode == SENSOR_SNAPSHOT_MODE) { + pr_info("ov8810_ctrl: STREAM ON for SNAPSHOT\n"); + rc = ov8810_i2c_write_b(ov8810_client->addr, + OV8810_REG_MODE_SELECT, + OV8810_MODE_SELECT_STREAM); + if (rc < 0) + return rc; + msleep(50); + } + + /*stored_line_length_ratio = line_length_ratio;*/ + return rc; + +} /* endof ov8810_write_exp_gain*/ + +/* ### this function is not called for userspace ### */ +static int32_t ov8810_set_pict_exp_gain + (uint16_t mul, uint16_t gain, uint32_t line) +{ + int32_t rc = 0; + rc = ov8810_write_exp_gain(mul, gain, line); + return rc; +} /* endof ov8810_set_pict_exp_gain*/ + +/* remove test code */ +#if 0 +static int32_t ov8810_test(enum ov8810_test_mode_t mo) +{ + int32_t rc = 0; + if (mo == TEST_OFF) { + return rc; + } + + /* Activate the Color bar test pattern */ + if (mo == TEST_1) { + rc = ov8810_i2c_write_b(ov8810_client->addr, + OV8810_COLOR_BAR_ENABLE_REG, 0xa0); + if (rc < 0) { + return rc; + } + rc = ov8810_i2c_write_b(ov8810_client->addr, + 0x3085, 0x20); + if (rc < 0) { + return rc; + } + rc = ov8810_i2c_write_b(ov8810_client->addr, + 0x306c, 0x00); + if (rc < 0) { + return rc; + } + rc = ov8810_i2c_write_b(ov8810_client->addr, + OV8810_COLOR_BAR_PATTERN_SEL_REG, 0x02); + if (rc < 0) { + return rc; + } + } + + return rc; + +} +#endif + + +uint32_t Crc32CheckSumByte(uint8_t *pData, uint32_t uiLen, uint32_t preValue) +{ + const uint32_t crc32table[256] = { + /* 0x00 */ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + /* 0x04 */ 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + /* 0x08 */ 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + /* 0x0C */ 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + /* 0x10 */ 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + /* 0x14 */ 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + /* 0x18 */ 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + /* 0x1C */ 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + /* 0x20 */ 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + /* 0x24 */ 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + /* 0x28 */ 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + /* 0x2C */ 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + /* 0x30 */ 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + /* 0x34 */ 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + /* 0x38 */ 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + /* 0x3C */ 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + /* 0x40 */ 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + /* 0x44 */ 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + /* 0x48 */ 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + /* 0x4C */ 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + /* 0x50 */ 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + /* 0x54 */ 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + /* 0x58 */ 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + /* 0x5C */ 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + /* 0x60 */ 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + /* 0x64 */ 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + /* 0x68 */ 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + /* 0x6C */ 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + /* 0x70 */ 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + /* 0x74 */ 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + /* 0x78 */ 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + /* 0x7C */ 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + /* 0x80 */ 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + /* 0x84 */ 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + /* 0x88 */ 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + /* 0x8C */ 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + /* 0x90 */ 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + /* 0x94 */ 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + /* 0x98 */ 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + /* 0x9C */ 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + /* 0xA0 */ 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + /* 0xA4 */ 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + /* 0xA8 */ 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + /* 0xAC */ 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + /* 0xB0 */ 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + /* 0xB4 */ 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + /* 0xB8 */ 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + /* 0xBC */ 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + /* 0xC0 */ 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + /* 0xC4 */ 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + /* 0xC8 */ 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + /* 0xCC */ 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + /* 0xD0 */ 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + /* 0xD4 */ 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + /* 0xD8 */ 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + /* 0xDC */ 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + /* 0xE0 */ 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + /* 0xE4 */ 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + /* 0xE8 */ 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + /* 0xEC */ 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + /* 0xF0 */ 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + /* 0xF4 */ 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + /* 0xF8 */ 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + /* 0xFC */ 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,}; + uint32_t i, CheckSum, cvalue; + + CheckSum = preValue; + for (i = 0; i < uiLen; i++) { + cvalue = *pData; + CheckSum = + (CheckSum>>8) ^ + crc32table[(CheckSum & 0xFF) ^ + (cvalue & 0xFF)]; + pData++; + } + return CheckSum; +} + +static int32_t HTC_update_ov8810_lsc_registers(void) +{ + int i; + struct awb_lsc_struct_type *awb_lsc_data_ptr; + awb_lsc_data_ptr = (struct awb_lsc_struct_type *)get_cam_awb_cal(); + + for (i = 0; i < 8; i++) { + pr_info(KERN_INFO"[LSC calibration] read AWB table 0x%x\n", + awb_lsc_data_ptr->caBuff[i]); + } + + for (i = 0; i < LSC_table_length; i++) { + pr_info("[LSC calibration] read LSC table 0x%x, 0x%x\n", + awb_lsc_data_ptr->LSC_table[i].reg_addr, + awb_lsc_data_ptr->LSC_table[i].reg_val); + } + + if (awb_lsc_data_ptr->LSC_table_CRC == + Crc32CheckSumByte( + (uint8_t *) awb_lsc_data_ptr->LSC_table, + 150 * sizeof(struct reg_addr_val_pair_struct), 0) && + awb_lsc_data_ptr->LSC_table_CRC != 0) { + + pr_info("[LSC calibration]checksum pass,use calibrated LSC\n"); + + for (i = 0; i < LSC_table_length; i++) { + ov8810_i2c_write_b(ov8810_client->addr, + awb_lsc_data_ptr->LSC_table[i].reg_addr, + awb_lsc_data_ptr->LSC_table[i].reg_val); + } + /*enable lsc on sensor*/ + ov8810_i2c_write_b(ov8810_client->addr, 0x3300, 0xff); + /*move to the last*/ + ov8810_i2c_write_b(ov8810_client->addr, + OV8810_REG_MODE_SELECT, OV8810_MODE_SELECT_STREAM); + + } else {/*use default LSC table*/ + pr_info("[LSC calibration]checksum fail\n"); + return false; + } + return true; +} + +static int32_t initialize_ov8810_registers(void) +{ + int32_t i, array_length; + int32_t rc = 0; + + mdelay(5); + ov8810_i2c_write_b( + ov8810_client->addr, + OV8810_REG_SOFTWARE_RESET, + OV8810_SOFTWARE_RESET); + mdelay(5); + ov8810_i2c_write_b( + ov8810_client->addr, + OV8810_REG_MODE_SELECT, + OV8810_MODE_SELECT_SW_STANDBY); + mdelay(1); + array_length = sizeof(ov8810_init_settings_array) / + sizeof(ov8810_init_settings_array[0]); + + /* Configure sensor for Preview mode and Snapshot mode */ + for (i = 0; i < array_length; i++) { + rc = ov8810_i2c_write_b(ov8810_client->addr, + ov8810_init_settings_array[i].reg_addr, + ov8810_init_settings_array[i].reg_val); + if (rc < 0) + return rc; + } + + /*use calibrated LSC table*/ + if (HTC_update_ov8810_lsc_registers()) { + pr_info("[LSC calibration] use calibrated LSC table done!\n"); + } else {/*use default LSC table*/ + array_length = + sizeof(lsc_table_array) / sizeof(lsc_table_array[0]); + + for (i = 0; i < array_length; i++) { + rc = ov8810_i2c_write_b(ov8810_client->addr, + lsc_table_array[i].reg_addr, + lsc_table_array[i].reg_val); + } + pr_info("[LSC calibration] use default LSC table done\n"); + } + return rc; +} /* end of initialize_ov8810_ov8m0vc_registers. */ + +static int32_t ov8810_setting(int rt) +{ + int32_t rc = 0; + int32_t i, array_length; + static int16_t did_snapshot; + uint16_t ori_reg_mul_gain; + uint8_t ori_reg_mul_gain_8bit; + + uint16_t i2c_ret = 0; + + write_cnt = 0; + + pr_info("ov8810_setting rt = %d\n", rt); + + if (rt == FULL_SIZE) { + + ov8810_i2c_read(0x30b7, &i2c_ret, 1); + pr_info("0x30b7, i2c_ret = 0x%X\n", i2c_ret); + /*Retry writing group update bottom to ensure capture settings can be updated Weiting0331*/ + while (i2c_ret != 0x84) { + + /* for group update bottom */ + rc = ov8810_i2c_write_b(ov8810_client->addr, 0x30b7, 0x84); + if (rc < 0) + return rc; + + /* for group update enable */ + rc = ov8810_i2c_write_b(ov8810_client->addr, 0x30ff, 0xff); + if (rc < 0) + return rc; + + msleep(50); + ov8810_i2c_read(0x30b7, &i2c_ret, 1); + pr_info("retry 0x30b7, i2c_ret = 0x%X\n", i2c_ret); + }; + } + + rc = ov8810_i2c_write_b(ov8810_client->addr, + OV8810_REG_MODE_SELECT, + OV8810_MODE_SELECT_SW_STANDBY); + if (rc < 0) { + return rc; + } + + ov8810_i2c_read(OV8810_REG_MODE_SELECT, &i2c_ret, 1); + pr_info("OV8810_REG_MODE_SELECT, i2c_ret = 0x%X\n", i2c_ret); + + switch (rt) { + + case QTR_SIZE: + + array_length = sizeof(ov8810_qtr_settings_array) / + sizeof(ov8810_qtr_settings_array[0]); + + /* Configure sensor for XGA preview mode */ + for (i = 0; i < array_length; i++) { + rc = ov8810_i2c_write_b(ov8810_client->addr, + ov8810_qtr_settings_array[i].reg_addr, + ov8810_qtr_settings_array[i].reg_val); + + if (rc < 0) { + return rc; + } + } + +/* reconfigure the qtr height to adjust frame rate */ +{ + uint16_t fl_line = 0; + fl_line = OV8810_QTR_SIZE_HEIGHT + + ov8810_ver_qtr_blk_lines_array[cam_mode_sel]; + rc = ov8810_i2c_write_b(ov8810_client->addr, + REG_FRAME_LENGTH_LINES_MSB, + (fl_line & 0xFF00) >> 8); + if (rc < 0) + return rc; + rc = ov8810_i2c_write_b(ov8810_client->addr, + REG_FRAME_LENGTH_LINES_LSB, + fl_line & 0x00FF); + if (rc < 0) + return rc; +#if 0 + if (cam_mode_sel > 0) { + pr_info("andy write binning ctrl 0x00, cam_mode_sel %d\n", cam_mode_sel); + rc = ov8810_i2c_write_b(ov8810_client->addr, //weiting ori c0 + REG_BINNING_CONTROL, 0x00); + if (rc < 0) + return rc; + + } +#endif +} + + +#if 1 /* this is supposed to prevent abnormal color when restart preview */ + + if (!did_snapshot) + { + memset(&backup_line_gain, 0, + sizeof(struct backup_line_gain_struct)); + backup_line_gain[0].line = 0x4c4; + backup_line_gain[0].mul = MUL_GAIN_INIT_VALUE; + backup_line_gain[0].gain = 8; /*0x30;*/ + backup_line_gain[0].extra_line_length = 0; + } + + CDBG("backup_line_gain[0].line = %d" \ + "backup_line_gain[0].mul = %d" \ + "backup_line_gain[0].gain = %d" \ + "backup_line_gain[0].extra_line_length = %d", + backup_line_gain[0].line, + backup_line_gain[0].mul, + backup_line_gain[0].gain, + backup_line_gain[0].extra_line_length); + + rc = ov8810_i2c_write_b(ov8810_client->addr, + OV8810_AEC_MSB, + (uint8_t)((backup_line_gain[0].line & 0xFF00) >> 8)); + if (rc < 0) + return rc; + + rc = ov8810_i2c_write_b(ov8810_client->addr, + OV8810_AEC_LSB, + (uint8_t)(backup_line_gain[0].line & 0x00FF)); + if (rc < 0) + return rc; + + rc = ov8810_i2c_read(OV8810_REG_MUL_GAIN, &ori_reg_mul_gain, 2); + if (rc < 0) { + pr_err("read OV8810_REG_MUL_GAIN fail\n"); + return rc; + } + ori_reg_mul_gain_8bit = + (uint8_t)((ori_reg_mul_gain & 0xFF00)>>8); + CDBG("%s, read OV8810_REG_MUL_GAIN ori_reg_mul_gain = %x\n", + __func__, ori_reg_mul_gain_8bit); + ori_reg_mul_gain_8bit = + (ori_reg_mul_gain_8bit & 0xFC) | + (backup_line_gain[0].mul & 0x03); + CDBG("%s, read OV8810_REG_MUL_GAIN ori_reg_mul_gain = %x\n", + __func__, ori_reg_mul_gain_8bit); + rc = ov8810_i2c_write_b(ov8810_client->addr, + OV8810_REG_MUL_GAIN, ori_reg_mul_gain_8bit); + if (rc < 0) + return rc; + + rc = ov8810_i2c_write_b(ov8810_client->addr, + OV8810_GAIN, + (uint8_t)(backup_line_gain[0].gain & 0x00FF)); + if (rc < 0) + return rc; + + rc = ov8810_i2c_write_b(ov8810_client->addr, + REG_EXTRA_VSYNC_WIDTH_MSB, + (uint8_t)((backup_line_gain[0].extra_line_length + & 0xFF00) >> 8)); + if (rc < 0) + return rc; + + rc = ov8810_i2c_write_b(ov8810_client->addr, + REG_EXTRA_VSYNC_WIDTH_LSB, + (uint8_t)(backup_line_gain[0].extra_line_length + & 0x00FF)); + if (rc < 0) + return rc; + +#endif + did_snapshot = 0; + + ov8810_ctrl->curr_res = QTR_SIZE; + + break; + + case FULL_SIZE: + if (rc < 0) + return rc; + array_length = sizeof(ov8810_full_settings_array) / + sizeof(ov8810_full_settings_array[0]); + /* Configure sensor for QXGA capture mode */ + for (i = 0; i < array_length; i++) { + rc = ov8810_i2c_write_b(ov8810_client->addr, + ov8810_full_settings_array[i].reg_addr, + ov8810_full_settings_array[i].reg_val); + if (rc < 0) + return rc; + } + did_snapshot = 1; + ov8810_ctrl->curr_res = FULL_SIZE; + break; + + default: + rc = -EFAULT; + return rc; + } + + /*disablt LSC for calibration*/ + pr_info("[LSC calibration] global_mode=%d!!!!\n", global_mode); + /*take raw picture for LSC calibration*/ + if (global_mode) { + /*disable sensor LSC*/ + rc = ov8810_i2c_write_b(ov8810_client->addr, 0x3300, 0xef); + /*mirror off*/ + rc = ov8810_i2c_write_b(ov8810_client->addr, 0x30f8, 0x00); + /*mirror off*/ + rc = ov8810_i2c_write_b(ov8810_client->addr, 0x3316, 0x02); + pr_info("[LSC calibration]turn off LSC!Mirror On\n"); + + /*fix gain & linecount*/ + /*Gain=0x9,exp=008d*/ + /*so luma taget = 100 to mfg light source*/ + rc = ov8810_i2c_write_b(ov8810_client->addr, 0x3000, 0x9); + /*AEC_MSB*/ + rc = ov8810_i2c_write_b(ov8810_client->addr, 0x3002, 0x00); + /*AEC_LSB*/ + rc = ov8810_i2c_write_b(ov8810_client->addr, 0x3003, 0x8d); + pr_info("[LSC calibration]fix gain & linecount\n"); + global_mode = 0; + } + + if (ov8810_ctrl->sensormode != SENSOR_SNAPSHOT_MODE) { + msleep(50); + rc = ov8810_i2c_write_b(ov8810_client->addr, + OV8810_REG_MODE_SELECT, + OV8810_MODE_SELECT_STREAM); + if (rc < 0) + return rc; + + updated_BLC = 0; + } + +/* remove test code + rc = ov8810_test(ov8810_ctrl->set_test); + if (rc < 0) + return rc; +*/ + + return rc; +} /*endof ov8810_setting*/ + +static int32_t ov8810_video_config(int mode) +{ + int32_t rc = 0; + static int pre_sel = 0; + int cur_sel = (cam_mode_sel > 1)?1:0; + + ov8810_ctrl->sensormode = mode; + + pr_info("%s cam_mode_sel %d cur_sel %d \n", __func__, cam_mode_sel, cur_sel); + + if (ov8810_ctrl->curr_res != ov8810_ctrl->prev_res + || pre_sel != cur_sel + ) { + rc = ov8810_setting(ov8810_ctrl->prev_res); + if (rc < 0) + return rc; + + } else { + ov8810_ctrl->curr_res = ov8810_ctrl->prev_res; + } + + pre_sel = cur_sel; + + ov8810_ctrl->sensormode = mode; + + return rc; + +} /*end of ov354_video_config*/ + +static int32_t ov8810_snapshot_config(int mode) +{ + int32_t rc = 0; + ov8810_ctrl->sensormode = mode; + + if (ov8810_ctrl->curr_res != ov8810_ctrl->pict_res) { + rc = ov8810_setting(ov8810_ctrl->pict_res); + if (rc < 0) + return rc; + } else { + ov8810_ctrl->curr_res = ov8810_ctrl->pict_res; + } + ov8810_ctrl->sensormode = mode; + + return rc; + +} /*end of ov8810_snapshot_config*/ + +static int32_t ov8810_raw_snapshot_config(int mode) +{ + int32_t rc = 0; + ov8810_ctrl->sensormode = mode; + if (ov8810_ctrl->curr_res != ov8810_ctrl->pict_res) { + rc = ov8810_setting(ov8810_ctrl->pict_res); + if (rc < 0) + return rc; + } else { + ov8810_ctrl->curr_res = ov8810_ctrl->pict_res; + } /* Update sensor resolution */ + + ov8810_ctrl->sensormode = mode; + + return rc; + +} /*end of ov8810_raw_snapshot_config*/ + +static int32_t ov8810_set_sensor_mode(int mode, + int res) +{ + int32_t rc = 0; + struct msm_camera_sensor_info *sinfo = ov8810_pdev->dev.platform_data; + + switch (mode) { + case SENSOR_PREVIEW_MODE: + rc = ov8810_video_config(mode); + break; + + case SENSOR_SNAPSHOT_MODE: + pr_info("KPI PA: start sensor snapshot config: %d\n", __LINE__); + sinfo->kpi_sensor_start = ktime_to_ns(ktime_get()); + rc = ov8810_snapshot_config(mode); + break; + + case SENSOR_RAW_SNAPSHOT_MODE: + /*global_mode = 1; //20100330 vincent lsc calibration*/ + pr_info("KPI PA: start sensor snapshot config: %d\n", __LINE__); + sinfo->kpi_sensor_start = ktime_to_ns(ktime_get()); + rc = ov8810_raw_snapshot_config(mode); + break; + + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static int32_t ov8810_power_down(void) +{ + return 0; +} + +static int ov8810_probe_read_id(const struct msm_camera_sensor_info *data) +{ + int32_t rc = 0; + uint16_t chipidh = 0; /*, chipidl;*/ + uint16_t def_chipid = 0; + msleep(20); + pr_info("%s, ov8810_probe_init_sensor 1\n", __func__); + /* 3. Read sensor Model ID: */ + if (ov8810_i2c_read(OV8810_PIDH_REG, &chipidh, 2) < 0) { + rc = -1; + pr_err("read sensor id fail\n"); + } + + pr_info("ov8810 model_id + ver = 0x%x\n", chipidh); + + /* 4. Compare sensor ID to OV8810 ID: */ + def_chipid = (((OV8810_PID << 8) & 0xFF00) + (OV8810_VER & 0x00FF)); + pr_info("%s, Expected id=0x%x\n", __func__, def_chipid); + + if (chipidh < def_chipid) { + rc = -ENODEV; + pr_err("read sensor id incorrect\n"); + } + + pr_info("%s, vreg_get vreg_af_actuator\n", __func__); + vreg_af_actuator = vreg_get(0, "gp5"); + if (IS_ERR(vreg_af_actuator)) + return PTR_ERR(vreg_af_actuator); + + data->camera_set_source(MAIN_SOURCE); + pr_info(" ov8810_probe_init_sensor finishes\n"); + return rc; +} + +static int ov8810_sensor_open_init(struct msm_camera_sensor_info *data) +{ + + int i; + int32_t rc = 0; + /*stella0122*/ + uint16_t ov8810_nl_region_boundary = 5; /*3;*/ + uint16_t ov8810_nl_region_code_per_step = 35; /*101;*/ + uint16_t ov8810_l_region_code_per_step = 20; /*18;*/ + int timeout; + pr_info("Calling ov8810_sensor_open_init\n"); + + down(&ov8810_sem); + + if (data == NULL) { + pr_info("data is a NULL pointer\n"); + return -EINVAL; + } + /*check whether resume done*/ + timeout = wait_event_interruptible_timeout( + ov8810_event.event_wait, + ov8810_event.waked_up, + 30*HZ); + pr_info("wait event : %d timeout:%d\n", ov8810_event.waked_up, timeout); + if (timeout == 0) { + up(&ov8810_sem); + return rc; + } + msm_camio_probe_on(ov8810_pdev); + ov8810_ctrl = kzalloc(sizeof(struct ov8810_ctrl), GFP_KERNEL); + if (!ov8810_ctrl) { + pr_err("ov8810_init failed!\n"); + rc = -ENOMEM; + goto init_done; + } + ov8810_ctrl->curr_lens_pos = -1; + ov8810_ctrl->fps_divider = 1 * 0x00000400; + ov8810_ctrl->pict_fps_divider = 1 * 0x00000400; + ov8810_ctrl->set_test = TEST_OFF; + ov8810_ctrl->prev_res = QTR_SIZE; + ov8810_ctrl->pict_res = FULL_SIZE; + ov8810_ctrl->curr_res = INVALID_SIZE; + if (data) + ov8810_ctrl->sensordata = data; + + /*switch pclk and mclk between main cam and 2nd cam*/ + /*only for supersonic*/ + pr_info("doing clk switch (ov8810)\n"); + if(data->camera_clk_switch != NULL) + data->camera_clk_switch(); + + /*PWD config*/ + pr_info("%s, GPIO(%d) sensor_pwd 0\n", __func__, data->sensor_pwd); + rc = gpio_request(data->sensor_pwd, "ov8810"); + if (!rc) + gpio_direction_output(data->sensor_pwd, 0); + else + pr_err("GPIO (%d) request faile\n", data->sensor_pwd); + gpio_free(data->sensor_pwd); + mdelay(3); + + /* enable mclk first */ + msm_camio_clk_rate_set(OV8810_DEFAULT_CLOCK_RATE); + msm_camio_camif_pad_reg_reset(); + msleep(3); + /*Pull reset*/ + rc = gpio_request(data->sensor_reset, "ov8810"); + if (!rc) + gpio_direction_output(data->sensor_reset, 1); + else + pr_err("GPIO (%d) request faile\n", data->sensor_reset); + gpio_free(data->sensor_reset); + msleep(20); + /*read sensor id*/ + rc = ov8810_probe_read_id(data); + + ov8810_ctrl->sensormode = SENSOR_PREVIEW_MODE ; + + pr_info("%s, initialize_ov8810_registers: %d\n", __func__, __LINE__); + if (rc < 0) + goto init_fail; + #if 1 /*move to probe up sensor*/ + /* Initialize Sensor registers */ + rc = initialize_ov8810_registers(); + if (rc < 0) + return rc; + pr_info("%s, ov8810_setting preview %d\n", __func__, __LINE__); + + if (ov8810_ctrl->curr_res != ov8810_ctrl->prev_res) { + rc = ov8810_setting(ov8810_ctrl->prev_res); + if (rc < 0) + goto init_fail; + } else { + ov8810_ctrl->curr_res = ov8810_ctrl->prev_res; + } + #endif + + pr_info("%s, enable AF actuator %d\n", __func__, __LINE__); + + /* enable AF actuator */ + rc = vreg_enable(vreg_af_actuator); + if (!rc) { + + rc = vreg_set_level(vreg_af_actuator, 2800); /*2v8*/ + if (rc) + { + pr_err("vreg_af_actuator vreg_set_level 2v8 failed!\n"); + goto init_fail; + } + } + else { + pr_err("vreg_af_actuator vreg_enable failed!\n"); + goto init_fail; + } + + msleep(20); + + pr_info("%s, set step_position_table %d\n", __func__, __LINE__); + + ov8810_ctrl->fps = 30*Q8; + + step_position_table[0] = 0; + + for (i = 1; i <= OV8810_TOTAL_STEPS_NEAR_TO_FAR; i++) { + if (i <= ov8810_nl_region_boundary) { + ov8810_step_position_table[i] = + ov8810_step_position_table[i-1] + + ov8810_nl_region_code_per_step; + } else { + ov8810_step_position_table[i] = + ov8810_step_position_table[i-1] + + ov8810_l_region_code_per_step; + } + } + + /* generate test pattern */ + pr_info("%s, generate test pattern, %d, rc=%d\n", + __func__, __LINE__, rc); + + if (rc < 0) + goto init_fail; + else + goto init_done; + /* reset the driver state */ +init_fail: + pr_err("%s: init_fail\n", __func__); + vreg_disable(vreg_af_actuator); + if (ov8810_ctrl) { + kfree(ov8810_ctrl); + ov8810_ctrl = NULL; + } +init_done: + up(&ov8810_sem); + pr_info("%s: init_done\n", __func__); + return rc; + +} /*endof ov8810_sensor_open_init*/ + +static int ov8810_init_client(struct i2c_client *client) +{ + /* Initialize the MSM_CAMI2C Chip */ + init_waitqueue_head(&ov8810_wait_queue); + return 0; +} + +static const struct i2c_device_id ov8810_i2c_id[] = { + { "ov8810", 0}, + { } +}; + +static int ov8810_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + pr_info("ov8810_probe called!\n"); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("i2c_check_functionality failed\n"); + goto probe_failure; + } + + ov8810_sensorw = kzalloc(sizeof(struct ov8810_work), GFP_KERNEL); + if (!ov8810_sensorw) { + pr_err("kzalloc failed.\n"); + rc = -ENOMEM; + goto probe_failure; + } + + i2c_set_clientdata(client, ov8810_sensorw); + ov8810_init_client(client); + ov8810_client = client; + + msleep(50); + + pr_info("ov8810_probe successed! rc = %d\n", rc); + return 0; + +probe_failure: + pr_err("ov8810_probe failed! rc = %d\n", rc); + return rc; +} + +static int ov8810_probe_init_done(const struct msm_camera_sensor_info *data) +{ + gpio_request(data->sensor_pwd, "ov8810"); + gpio_direction_output(data->sensor_pwd, 1); + gpio_free(data->sensor_pwd); + mdelay(1); + gpio_request(data->sensor_reset, "ov8810"); + gpio_direction_output(data->sensor_reset, 0); + gpio_free(data->sensor_reset); + return 0; +} + +static int ov8810_suspend(struct platform_device *pdev, pm_message_t state) +{ + int rc; + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + + if (!sinfo->need_suspend) + return 0; + ov8810_event.waked_up = 0; + + pr_info("ov8810: camera suspend\n"); + + pr_info("%s, vreg_af_actuator vreg_disable\n", __func__); + vreg_disable(vreg_af_actuator); + + rc = gpio_request(sinfo->sensor_reset, "ov8810"); + if (!rc) + gpio_direction_output(sinfo->sensor_reset, 0); + else + pr_info("ov8810: request GPIO(sensor_reset) :%d faile\n", + sinfo->sensor_reset); + + gpio_free(sinfo->sensor_reset); + msleep(10); + rc = gpio_request(sinfo->sensor_pwd, "ov8810"); + if (!rc) + gpio_direction_output(sinfo->sensor_pwd, 0); + else + pr_info("ov8810: request GPIO(sensor_reset) :%d faile\n", + sinfo->sensor_pwd); + + gpio_free(sinfo->sensor_pwd); + + pr_info("ov8810:suspend done\n"); + return rc; +} + +static void ov8810_resume(struct early_suspend *handler) +{ + int rc = 0; + struct msm_camera_sensor_info *sinfo = ov8810_pdev->dev.platform_data; + pr_info("ov8810_resume\n"); + + /*check whether need resume*/ + if (!sinfo->need_suspend) + return; + + /*check whether already suspend*/ + if (ov8810_event.waked_up == 1) { + pr_info("Ov8810: No nesesary to do Resume\n"); + return; + } + + mdelay(5); + /*power down setup*/ + pr_info("%s, sensor_pwd 0\n", __func__); + rc = gpio_request(sinfo->sensor_pwd, "ov8810"); + if (!rc) + gpio_direction_output(sinfo->sensor_pwd, 0); + else + pr_err("GPIO (%d) request faile\n", sinfo->sensor_pwd); + gpio_free(sinfo->sensor_pwd); + mdelay(5); + /*reset setup */ + rc = gpio_request(sinfo->sensor_reset, "ov8810"); + if (!rc) + gpio_direction_output(sinfo->sensor_reset, 1); + else + pr_err("GPIO (%d) request faile\n", sinfo->sensor_reset); + gpio_free(sinfo->sensor_reset); + + /*init msm,clk ,GPIO,enable*/ + pr_info("%s, msm_camio_probe_on\n", __func__); + msm_camio_probe_on(ov8810_pdev); + msm_camio_clk_enable(CAMIO_MDC_CLK); + + /*set MCLK*/ + pr_info("%s, msm_camio_clk_rate_set = %d\n", + __func__, OV8810_DEFAULT_CLOCK_RATE); + msm_camio_clk_rate_set(OV8810_DEFAULT_CLOCK_RATE); + msleep(100); + + /*read sensor id*/ + rc = ov8810_probe_read_id(sinfo); + if (rc < 0) + pr_err("OV8810 resume faile :can not read sensor ID\n"); + + /* Initialize Sensor registers */ + rc = initialize_ov8810_registers(); + if (rc < 0) + return; + msleep(20); + /*resume done*/ + ov8810_probe_init_done(sinfo); + /*turn off MCLK*/ + msm_camio_probe_off(ov8810_pdev); + msm_camio_clk_disable(CAMIO_MDC_CLK); + + ov8810_event.waked_up = 1; + pr_info("ov8810:resume done\n"); + wake_up(&ov8810_event.event_wait); + return; +} + + +static int __exit ov8810_i2c_remove(struct i2c_client *client) +{ + struct ov8810_work_t *sensorw = i2c_get_clientdata(client); + free_irq(client->irq, sensorw); + deinit_suspend(); + ov8810_client = NULL; + kfree(sensorw); + return 0; +} + +static struct i2c_driver ov8810_i2c_driver = { + .id_table = ov8810_i2c_id, + .probe = ov8810_i2c_probe, + .remove = __exit_p(ov8810_i2c_remove), + .driver = { + .name = "ov8810", + }, +}; + + +static struct early_suspend early_suspend_ov8810 = { + .level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN+1, + .resume = ov8810_resume, + .suspend = NULL, +}; + +static const char *Ov8810Vendor = "OmniVision"; +static const char *Ov8810NAME = "ov8810"; +static const char *Ov8810Size = "8M"; + +static ssize_t sensor_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + + sprintf(buf, "%s %s %s\n", Ov8810Vendor, Ov8810NAME, Ov8810Size); + ret = strlen(buf) + 1; + + return ret; +} + +DEFINE_MUTEX(cam_mode_lock); + +static ssize_t sensor_read_cam_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + mutex_lock(&cam_mode_lock); + length = sprintf(buf, "%d\n", cam_mode_sel); + mutex_unlock(&cam_mode_lock); + return length; +} + +static ssize_t sensor_set_cam_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + uint32_t tmp = 0; + mutex_lock(&cam_mode_lock); + tmp = buf[0] - 0x30; /* only get the first char */ + cam_mode_sel = tmp; + mutex_unlock(&cam_mode_lock); + return count; +} + +static ssize_t sensor_read_node(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + length = sprintf(buf, "%d\n", sensor_probe_node); + return length; +} + +static DEVICE_ATTR(sensor, 0444, sensor_vendor_show, NULL); +static DEVICE_ATTR(cam_mode, 0644, sensor_read_cam_mode, sensor_set_cam_mode); +static DEVICE_ATTR(node, 0444, sensor_read_node, NULL); + +static struct kobject *android_ov8810; + +static int ov8810_sysfs_init(void) +{ + int ret = 0; + pr_info("ov8810:kobject creat and add\n"); + android_ov8810 = kobject_create_and_add("android_camera", NULL); + if (android_ov8810 == NULL) { + pr_info("ov8810_sysfs_init: subsystem_register failed\n"); + ret = -ENOMEM; + return ret ; + } + pr_info("Ov8810:sysfs_create_file\n"); + ret = sysfs_create_file(android_ov8810, &dev_attr_sensor.attr); + if (ret) { + pr_info("ov8810_sysfs_init: sysfs_create_file failed\n"); + ret = -EFAULT; + goto error; + } + + ret = sysfs_create_file(android_ov8810, &dev_attr_cam_mode.attr); + if (ret) { + pr_info("ov8810_sysfs_init: dev_attr_cam_mode failed\n"); + ret = -EFAULT; + goto error; + } + + ret = sysfs_create_file(android_ov8810, &dev_attr_node.attr); + if (ret) { + pr_info("ov8810_sysfs_init: dev_attr_node failed\n"); + ret = -EFAULT; + goto error; + } + + return ret; + +error: + kobject_del(android_ov8810); + return ret; +} + + +int ov8810_sensor_config(void __user *argp) +{ + struct sensor_cfg_data cdata; + long rc = 0; + + if (copy_from_user(&cdata, + (void *)argp, + sizeof(struct sensor_cfg_data))) + return -EFAULT; + + down(&ov8810_sem); + + CDBG("ov8810_sensor_config: cfgtype = %d\n", + cdata.cfgtype); + switch (cdata.cfgtype) { + case CFG_GET_PICT_FPS: + ov8810_get_pict_fps( + cdata.cfg.gfps.prevfps, + &(cdata.cfg.gfps.pictfps)); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_L_PF: + cdata.cfg.prevl_pf = + ov8810_get_prev_lines_pf(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_P_PL: + cdata.cfg.prevp_pl = + ov8810_get_prev_pixels_pl(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_L_PF: + cdata.cfg.pictl_pf = + ov8810_get_pict_lines_pf(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_P_PL: + cdata.cfg.pictp_pl = + ov8810_get_pict_pixels_pl(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_MAX_EXP_LC: + cdata.cfg.pict_max_exp_lc = + ov8810_get_pict_max_exp_lc(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_SET_FPS: + case CFG_SET_PICT_FPS: + rc = ov8810_set_fps(&(cdata.cfg.fps)); + break; + + case CFG_SET_EXP_GAIN: + rc = + ov8810_write_exp_gain( + cdata.cfg.exp_gain.mul, + cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_PICT_EXP_GAIN: + rc = + ov8810_set_pict_exp_gain( + cdata.cfg.exp_gain.mul, + cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_MODE: + rc = ov8810_set_sensor_mode(cdata.mode, + cdata.rs); + break; + + case CFG_PWR_DOWN: + rc = ov8810_power_down(); + break; + + case CFG_MOVE_FOCUS: + rc = + ov8810_move_focus( + cdata.cfg.focus.dir, + cdata.cfg.focus.steps); + break; + + case CFG_SET_DEFAULT_FOCUS: + rc = + ov8810_set_default_focus( + cdata.cfg.focus.steps); + break; + + case CFG_SET_EFFECT: + rc = ov8810_set_default_focus( + cdata.cfg.effect); + break; + + case CFG_I2C_IOCTL_R_OTP:{ + rc = ov8810_i2c_read_fuseid(&cdata); + if (copy_to_user + (argp, &cdata, sizeof(struct sensor_cfg_data)) + ) + rc = -EFAULT; + } + break; + + case CFG_SET_OV_LSC: + rc = ov8810_update_lsc_table(&cdata); + break; + + /*20100330 vincent for lsc calibration*/ + case CFG_SET_OV_LSC_RAW_CAPTURE: + rc = ov8810_LSC_calibration_set_rawflag(&cdata); + break; + + default: + rc = -EFAULT; + break; + } + + prevent_suspend(); + up(&ov8810_sem); + + return rc; +} + + + + +static int ov8810_sensor_release(void) +{ + int rc = -EBADF; + + down(&ov8810_sem); + /*Pull hi PWD*/ + gpio_request(ov8810_ctrl->sensordata->sensor_pwd, "ov8810"); + gpio_direction_output(ov8810_ctrl->sensordata->sensor_pwd, 1); + gpio_free(ov8810_ctrl->sensordata->sensor_pwd); + /*Pull low RST*/ + gpio_request(ov8810_ctrl->sensordata->sensor_reset, "ov8810"); + gpio_direction_output(ov8810_ctrl->sensordata->sensor_reset, 0); + gpio_free(ov8810_ctrl->sensordata->sensor_reset); + + pr_info("vreg_af_actuator vreg_disable\n"); + vreg_disable(vreg_af_actuator); + msleep(20); + pr_info("%s, %d\n", __func__, __LINE__); + /*MCLK off*/ + msm_camio_probe_off(ov8810_pdev); + if (ov8810_ctrl) { + kfree(ov8810_ctrl); + ov8810_ctrl = NULL; + } + mdelay(3); + allow_suspend(); + pr_info("ov8810_release completed\n"); + up(&ov8810_sem); + + return rc; +} + +static int ov8810_sensor_probe(struct msm_camera_sensor_info *info, + struct msm_sensor_ctrl *s) +{ + int rc = 0; + rc = i2c_add_driver(&ov8810_i2c_driver); + if (rc < 0 || ov8810_client == NULL) { + rc = -ENOTSUPP; + goto probe_fail; + } + + pr_info("ov8810 s->node %d\n", s->node); + sensor_probe_node = s->node; + /*switch pclk and mclk between main cam and 2nd cam*/ + /*only for supersonic*/ + pr_info("Ov8810: doing clk switch (ov8810)\n"); + if(info->camera_clk_switch != NULL) + info->camera_clk_switch(); + mdelay(5); + /*power down setup*/ + rc = gpio_request(info->sensor_pwd, "ov8810"); + if (!rc) + gpio_direction_output(info->sensor_pwd, 0); + else + pr_err("GPIO (%d) request faile\n", info->sensor_pwd); + gpio_free(info->sensor_pwd); + mdelay(5); + /*reset setup */ + rc = gpio_request(info->sensor_reset, "ov8810"); + if (!rc) + gpio_direction_output(info->sensor_reset, 1); + else + pr_err("GPIO (%d) request faile\n", info->sensor_reset); + gpio_free(info->sensor_reset); + + /*set MCLK*/ + pr_info("%s, msm_camio_clk_rate_set %d\n", + __func__, OV8810_DEFAULT_CLOCK_RATE); + msm_camio_clk_rate_set(OV8810_DEFAULT_CLOCK_RATE); + msleep(100); + /*read sensor id*/ + rc = ov8810_probe_read_id(info); + if (rc < 0) + goto probe_fail; + + /* Initialize Sensor registers */ + rc = initialize_ov8810_registers(); + if (rc < 0) + return rc; + + if (info->camera_main_set_probe != NULL) + info->camera_main_set_probe(true); + init_suspend(); + s->s_init = ov8810_sensor_open_init; + s->s_release = ov8810_sensor_release; + s->s_config = ov8810_sensor_config; + msleep(20); + ov8810_probe_init_done(info); + /*register late resuem*/ + register_early_suspend(&early_suspend_ov8810); + /*init wait event*/ + init_waitqueue_head(&ov8810_event.event_wait); + /*init waked_up value*/ + ov8810_event.waked_up = 1; + /*write sysfs*/ + ov8810_sysfs_init(); + pr_info("%s: ov8810_probe_init_done %d\n", __func__, __LINE__); + goto probe_done; + +probe_fail: + pr_err("SENSOR PROBE FAILS!\n"); +probe_done: + return rc; + +} + +static int ov8810_vreg_enable(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sdata = pdev->dev.platform_data; + int rc; + pr_info("%s camera vreg on\n", __func__); + + if (sdata->camera_power_on == NULL) { + pr_err("sensor platform_data didnt register\n"); + return -EIO; + } + rc = sdata->camera_power_on(); + return rc; +} + + +static int ov8810_vreg_disable(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sdata = pdev->dev.platform_data; + int rc; + printk(KERN_INFO "%s camera vreg off\n", __func__); + if (sdata->camera_power_off == NULL) { + pr_err("sensor platform_data didnt register\n"); + return -EIO; + } + rc = sdata->camera_power_off(); + return rc; +} + + +static int __ov8810_probe(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sdata = pdev->dev.platform_data; + printk("__ov8810_probe\n"); + ov8810_pdev = pdev; + if (sdata->camera_main_get_probe != NULL) { + if (sdata->camera_main_get_probe()) { + pr_info("__ov8810_probe camera main get probed already.\n"); + return 0; + } + } + return msm_camera_drv_start(pdev, ov8810_sensor_probe); +} + +static struct platform_driver msm_camera_driver = { + .probe = __ov8810_probe, + .driver = { + .name = "msm_camera_ov8810", + .owner = THIS_MODULE, + }, + .suspend = ov8810_suspend, +}; + +static int __init ov8810_init(void) +{ + return platform_driver_register(&msm_camera_driver); +} + +module_init(ov8810_init); diff --git a/drivers/media/video/msm/ov8810.h b/drivers/media/video/msm/ov8810.h new file mode 100644 index 0000000000000..48219d2db63d9 --- /dev/null +++ b/drivers/media/video/msm/ov8810.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef OV8810_H +#define OV8810_H + +#endif + diff --git a/drivers/media/video/msm/ov9665.c b/drivers/media/video/msm/ov9665.c new file mode 100644 index 0000000000000..6d1ab50ab9268 --- /dev/null +++ b/drivers/media/video/msm/ov9665.c @@ -0,0 +1,1168 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ov9665.h" + +/* OV9665 Registers and their values */ +/* Sensor Core Registers */ +#define REG_OV9665_MODEL_ID_H 0x0A +#define REG_OV9665_MODEL_ID_L 0x0B +#define OV9665_MODEL_ID 0x9663 + +/* SOC Registers Page 1 */ +#define REG_OV9665_SENSOR_RESET 0x301A +#define REG_OV9665_STANDBY_CONTROL 0x3202 +#define REG_OV9665_MCU_BOOT 0x3386 + +struct ov9665_work { + struct work_struct work; +}; + +static struct ov9665_work *ov9665_sensorw; +static struct i2c_client *ov9665_client; + +struct ov9665_ctrl { + const struct msm_camera_sensor_info *sensordata; +}; + +static struct ov9665_ctrl *ov9665_ctrl; +static int op_mode; +static DECLARE_WAIT_QUEUE_HEAD(ov9665_wait_queue); +DEFINE_SEMAPHORE(ov9665_sem); + +static int sensor_probe_node = 0; + +static enum wb_mode current_wb = CAMERA_AWB_AUTO; +static int ov9665_set_wb(enum wb_mode wb_value); + +#define MAX_I2C_RETRIES 20 +static int i2c_transfer_retry(struct i2c_adapter *adap, + struct i2c_msg *msgs, + int len) +{ + int i2c_retry = 0; + int ns; /* number sent */ + + while (i2c_retry++ < MAX_I2C_RETRIES) { + ns = i2c_transfer(adap, msgs, len); + if (ns == len) + break; + pr_err("%s: try %d/%d: i2c_transfer sent: %d, len %d\n", + __func__, + i2c_retry, MAX_I2C_RETRIES, ns, len); + msleep(10); + } + + return ns == len ? 0 : -EIO; +} + + +static int ov9665_i2c_txdata(unsigned short saddr, + unsigned char *txdata, int length) +{ + struct i2c_msg msg[] = { + { + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, + }, + }; + + if (i2c_transfer_retry(ov9665_client->adapter, msg, 1) < 0) { + pr_info("ov9665_i2c_txdata failed\n"); + return -EIO; + } + + return 0; +} + +static int ov9665_i2c_write(unsigned short saddr, + unsigned char waddr, unsigned char wdata, + enum ov9665_width width) +{ + int rc = -EIO; + unsigned char buf[4]; + memset(buf, 0, sizeof(buf)); + switch (width) { + case WORD_LEN:{ + buf[0] = (waddr & 0xFF00) >> 8; + buf[1] = (waddr & 0x00FF); + buf[2] = (wdata & 0xFF00) >> 8; + buf[3] = (wdata & 0x00FF); + + rc = ov9665_i2c_txdata(saddr, buf, 4); + } + break; + + case BYTE_LEN:{ + buf[0] = waddr; + buf[1] = wdata; + rc = ov9665_i2c_txdata(saddr, buf, 2); + } + break; + + default: + break; + } + + if (rc < 0) + pr_info("i2c_write failed, addr = 0x%x, val = 0x%x!\n", + waddr, wdata); + + return rc; +} + +static int ov9665_i2c_write_table(struct ov9665_i2c_reg_conf const + *reg_conf_tbl, int num_of_items_in_table) +{ + int i; + int rc = -EIO; + + for (i = 0; i < num_of_items_in_table; i++) { + rc = ov9665_i2c_write(ov9665_client->addr, + reg_conf_tbl->waddr, reg_conf_tbl->wdata, + reg_conf_tbl->width); + if (rc < 0) + break; + if (reg_conf_tbl->mdelay_time != 0) + mdelay(reg_conf_tbl->mdelay_time); + reg_conf_tbl++; + } + + return rc; +} + +static int ov9665_i2c_rxdata(unsigned short saddr, + unsigned char *rxdata, int length) +{ + struct i2c_msg msgs[] = { + { + .addr = saddr, + .flags = 0, + .len = 1, + .buf = rxdata, + }, + { + .addr = saddr, + .flags = I2C_M_RD, + .len = length, + .buf = rxdata, + }, + }; + + if (i2c_transfer_retry(ov9665_client->adapter, msgs, 2) < 0) { + pr_info("ov9665_i2c_rxdata failed!\n"); + return -EIO; + } + + return 0; +} + + +static int ov9665_i2c_read(unsigned short saddr, + unsigned short raddr, unsigned char *rdata) +{ + int rc = 0; + unsigned char buf[1]; + if (!rdata) + return -EIO; + + memset(buf, 0, sizeof(buf)); + buf[0] = raddr; + rc = ov9665_i2c_rxdata(saddr, buf,1); + if (rc < 0) + return rc; + *rdata = buf[0]; + if (rc < 0) + pr_info("ov9665_i2c_read failed!\n"); + + return rc; +} + + +static int ov9665_i2c_write_mask( + unsigned char addr,unsigned char Data, unsigned char Mask) +{ + int rc = 0; + unsigned char temp; + rc = ov9665_i2c_read(ov9665_client->addr, addr, &temp); + if(rc < 0){ + pr_err("ov9665 error : read i2c error\n"); + return rc; + } + temp = (temp&(~Mask))|Data; + rc = ov9665_i2c_write(ov9665_client->addr, addr, temp, BYTE_LEN); + if(rc < 0){ + pr_err("ov9665 error : write i2c error\n"); + return rc; + } + return rc; +} + + +static int ov9665_pwd(const struct msm_camera_sensor_info *info){ + int rc=0; + rc = gpio_request(info->sensor_pwd, "ov9665"); + if (!rc) + gpio_direction_output(info->sensor_pwd, 0); + else + pr_err("GPIO(%d) request faile",info->sensor_pwd); + gpio_free(info->sensor_pwd); + /*for 2nd camera 2nd source*/ + /*main camera pwd pull down*/ + rc = gpio_request(105, "ov9665"); + if (!rc) + gpio_direction_output(105, 0); + else + pr_err("GPIO(105) request faile"); + gpio_free(105); + return rc; +} + +static int ov9665_reset(const struct msm_camera_sensor_info *dev) +{ + int rc = 0; + rc = gpio_request(dev->sensor_reset, "ov9665"); + + if (!rc) + rc = gpio_direction_output(dev->sensor_reset, 1); + else + pr_err("GPIO(%d) request faile",dev->sensor_reset); + + gpio_free(dev->sensor_reset); + return rc; +} + +static int ov9665_reg_init(void) +{ + int rc = 0; + rc = ov9665_i2c_write_table(&ov9665_regs.register_init[0], + ov9665_regs.register_init_size); + return rc; +} + +static int ov9665_init_done(const struct msm_camera_sensor_info *info) +{ + int rc = 0; + rc = gpio_request(info->sensor_pwd, "ov9665"); + if (!rc) + gpio_direction_output(info->sensor_pwd, 1); + else + pr_err("GPIO(%d) request faile",info->sensor_pwd); + gpio_free(info->sensor_pwd); + return rc; +} + + +static int ov9665_set_sensor_mode(int mode) +{ + unsigned char shading, gain; + switch (mode) { + case SENSOR_PREVIEW_MODE: + op_mode = SENSOR_PREVIEW_MODE; + pr_info("ov9665:sensor set mode: preview\n"); + ov9665_i2c_write(ov9665_client->addr, 0x63, 0x00, BYTE_LEN); + /*Windowing*/ + ov9665_i2c_write(ov9665_client->addr, 0x12, 0x40, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x4d, 0x09, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x17, 0x0c, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x18, 0x5d, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x19, 0x02, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x1a, 0x3f, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x03, 0x83, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x32, 0xad, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x2b, 0x00, BYTE_LEN); + /*scaling*/ + ov9665_i2c_write(ov9665_client->addr, 0x64, 0xa4, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xab, 0xe7, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xb9, 0x50, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xba, 0x3c, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xbb, 0x50, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xbc, 0x3c, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x85, 0xe7, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x0d, 0x92, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x0d, 0x90, BYTE_LEN); + /*enable 3A*/ + ov9665_i2c_write(ov9665_client->addr, 0x13, 0xe7, BYTE_LEN); + ov9665_set_wb(current_wb); + /*VGA 30fps*/ + ov9665_i2c_write(ov9665_client->addr, 0x11, 0x80, BYTE_LEN); + //ov9665_i2c_write(ov9665_client->addr, 0x09, 0x01, BYTE_LEN); + mdelay(400);/*skip 2 break frame */ + break; + + case SENSOR_SNAPSHOT_MODE: + pr_info("ov9665:sensor set mode: snapshot\n"); + op_mode = SENSOR_SNAPSHOT_MODE; + /*disable 3A*/ + ov9665_i2c_write(ov9665_client->addr, 0x13, 0xe0, BYTE_LEN); + /*SXGA 7.5fps*/ + ov9665_i2c_write(ov9665_client->addr, 0x11, 0x81, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x63, 0x01, BYTE_LEN); + /*Windowing*/ + ov9665_i2c_write(ov9665_client->addr, 0x12, 0x00, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x4d, 0x11, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x17, 0x0c, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x18, 0x5d, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x19, 0x01, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x1a, 0x82, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x03, 0x83, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x32, 0x24, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x2b, 0x00, BYTE_LEN); + /*scaling*/ + ov9665_i2c_write(ov9665_client->addr, 0x64, 0x24, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xab, 0xe7, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xb9, 0xa0, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xba, 0x80, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xbb, 0xa0, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xbc, 0x80, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x85, 0xe7, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x0d, 0x82, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0x0d, 0x80, BYTE_LEN); + mdelay(400); /*wait for AE stable*/ + break; + case SENSOR_GET_EXP: + ov9665_i2c_read(ov9665_client->addr, 0x83, &shading); + ov9665_i2c_read(ov9665_client->addr, 0x00, &gain); + if (gain >= 0x36) //5.5xgain + ov9665_i2c_write + (ov9665_client->addr, 0x83, 0x06, BYTE_LEN); + else if (gain < 0x34) //5x gain + ov9665_i2c_write + (ov9665_client->addr, 0x83, 0x07, BYTE_LEN); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ov9665_set_effect(int effect) +{ + if (op_mode == SENSOR_SNAPSHOT_MODE) + return 0; + switch (effect) { + case CAMERA_EFFECT_OFF: + #if 1/*color matrix*/ + ov9665_i2c_write(ov9665_client->addr, 0xbd, 0x04, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xbe, 0x1f, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xbf, 0x03, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc0, 0x0d, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc1, 0x24, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc2, 0x30, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc3, 0x34, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc4, 0x34, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc5, 0x01, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc6, 0x9c, BYTE_LEN); + #endif + #if 0/*control by SDE*/ + ov9665_i2c_write(ov9665_client->addr, 0xc7, 0x80, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc8, 0x00, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xcd, 0x80, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xce, 0x80, BYTE_LEN); + #endif + break; + + case CAMERA_EFFECT_MONO: + #if 1/*color matrix*/ + ov9665_i2c_write(ov9665_client->addr, 0xbd, 0x09, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xbe, 0x12, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xbf, 0x03, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc0, 0x00, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc1, 0x00, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc2, 0x00, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc3, 0x00, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc4, 0x00, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc5, 0x00, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc6, 0x00, BYTE_LEN); + #endif + #if 0/*control by SDE*/ + ov9665_i2c_write_mask(0xc7, 0x10, 0x10); + ov9665_i2c_write_mask(0xc8, 0x18, 0x18); + ov9665_i2c_write(ov9665_client->addr, 0xCD, 0x80, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xCE, 0x80, BYTE_LEN); + #endif + break; + + case CAMERA_EFFECT_NEGATIVE: + #if 0/*control by SDE*/ + ov9665_i2c_write_mask(0xc7, 0x10, 0x10); + ov9665_i2c_write_mask(0xc8, 0x40, 0x58); + /*ov9665_i2c_write(ov9665_client->addr, 0xC7, 0x90, BYTE_LEN);*/ + #endif + break; + + case CAMERA_EFFECT_SEPIA: + #if 1/*user color matrix*/ + ov9665_i2c_write(ov9665_client->addr, 0xbd, 0x09, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xbe, 0x09, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xbf, 0x00, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc0, 0x06, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc1, 0x08, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc2, 0x07, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc3, 0x0a, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc4, 0x04, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc5, 0x00, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc6, 0x98, BYTE_LEN); + #endif + #if 0/*control by SDE*/ + ov9665_i2c_write(ov9665_client->addr, 0xc7, 0x90, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xc8, 0x18, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xCD, 0x40, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, 0xCE, 0xa0, BYTE_LEN); + #endif + break; + default: + return -EINVAL; + } + return 0; +} + + +static int ov9665_set_antibanding(enum antibanding_mode antibanding_value) +{ + if (op_mode == SENSOR_SNAPSHOT_MODE) + return 0; + switch (antibanding_value) { + case CAMERA_ANTI_BANDING_50HZ: + ov9665_i2c_write_mask(0x0c, 0x00, 0x02); + ov9665_i2c_write_mask(0x0c, 0x04, 0x04); + break; + case CAMERA_ANTI_BANDING_60HZ: + ov9665_i2c_write_mask(0x0c, 0x00, 0x02); + ov9665_i2c_write_mask(0x0c, 0x00, 0x04); + break; + case CAMERA_ANTI_BANDING_AUTO: + ov9665_i2c_write_mask(0x0c, 0x02, 0x02); + break; + } + return 0; +} + + +static int ov9665_set_brightness(enum brightness_t brightness_value) +{ + if (op_mode == SENSOR_SNAPSHOT_MODE) + return 0; + ov9665_i2c_write_mask(0xc8, 0x04, 0x04); + switch (brightness_value) { + case CAMERA_BRIGHTNESS_N4: + ov9665_i2c_write_mask(0xc7, 0x18, 0x18); + ov9665_i2c_write(ov9665_client->addr, + 0xd1, 0x40, BYTE_LEN); + break; + case CAMERA_BRIGHTNESS_N3: + ov9665_i2c_write_mask(0xc7, 0x18, 0x18); + ov9665_i2c_write(ov9665_client->addr, + 0xd1, 0x30, BYTE_LEN); + break; + case CAMERA_BRIGHTNESS_N2: + ov9665_i2c_write_mask(0xc7, 0x18, 0x18); + ov9665_i2c_write(ov9665_client->addr, + 0xd1, 0x20, BYTE_LEN); + break; + case CAMERA_BRIGHTNESS_N1: + ov9665_i2c_write_mask(0xc7, 0x18, 0x18); + ov9665_i2c_write(ov9665_client->addr, + 0xd1, 0x10, BYTE_LEN); + break; + case CAMERA_BRIGHTNESS_D: + ov9665_i2c_write_mask(0xc7, 0x10, 0x18); + ov9665_i2c_write(ov9665_client->addr, + 0xd1, 0x00, BYTE_LEN); + break; + case CAMERA_BRIGHTNESS_P1: + ov9665_i2c_write_mask(0xc7, 0x10, 0x18); + ov9665_i2c_write(ov9665_client->addr, + 0xd1, 0x10, BYTE_LEN); + break; + case CAMERA_BRIGHTNESS_P2: + ov9665_i2c_write_mask(0xc7, 0x10, 0x18); + ov9665_i2c_write(ov9665_client->addr, + 0xd1, 0x20, BYTE_LEN); + break; + case CAMERA_BRIGHTNESS_P3: + ov9665_i2c_write_mask(0xc7, 0x10, 0x18); + ov9665_i2c_write(ov9665_client->addr, + 0xd1, 0x30, BYTE_LEN); + break; + case CAMERA_BRIGHTNESS_P4: + ov9665_i2c_write_mask(0xc7, 0x10, 0x18); + ov9665_i2c_write(ov9665_client->addr, + 0xd1, 0x40, BYTE_LEN); + break; + default: + break; + } + return 0; +} + + +static int ov9665_set_wb(enum wb_mode wb_value) +{ + if (op_mode == SENSOR_SNAPSHOT_MODE) + return 0; + switch (wb_value) { + case CAMERA_AWB_AUTO: + ov9665_i2c_write(ov9665_client->addr, + 0x13, 0xe7, BYTE_LEN); + break; + case CAMERA_AWB_CLOUDY: + ov9665_i2c_write(ov9665_client->addr, + 0x13, 0xe5, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0x01, 0x40, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0x02, 0x68, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0x16, 0x46, BYTE_LEN); + break; + case CAMERA_AWB_INDOOR_HOME: + ov9665_i2c_write(ov9665_client->addr, + 0x13, 0xe5, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0x01, 0x62, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0x02, 0x3c, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0x16, 0x41, BYTE_LEN); + break; + case CAMERA_AWB_INDOOR_OFFICE: + ov9665_i2c_write(ov9665_client->addr, + 0x13, 0xe5, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0x01, 0x50, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0x02, 0x40, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0x16, 0x40, BYTE_LEN); + break; + case CAMERA_AWB_SUNNY: + ov9665_i2c_write(ov9665_client->addr, + 0x13, 0xe5, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0x01, 0x35, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0x02, 0x52, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0x16, 0x40, BYTE_LEN); + break; + default: + break; + } + current_wb = wb_value; + return 0; +} + + +static int ov9665_set_sharpness(enum sharpness_mode sharpness_value) +{ + if (op_mode == SENSOR_SNAPSHOT_MODE) + return 0; + ov9665_i2c_write_mask(0xab, 0x04, 0x04); + switch (sharpness_value) { + case CAMERA_SHARPNESS_X0: + ov9665_i2c_write(ov9665_client->addr, + 0xad, 0x20, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0xd9, 0x00, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0xda, 0x00, BYTE_LEN); + break; + case CAMERA_SHARPNESS_X1: + ov9665_i2c_write(ov9665_client->addr, + 0xad, 0x22, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0xd9, 0x01, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0xda, 0x00, BYTE_LEN); + break; + case CAMERA_SHARPNESS_X2: + ov9665_i2c_write(ov9665_client->addr, + 0xad, 0x24, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0xd9, 0x13, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0xda, 0x00, BYTE_LEN); + break; + case CAMERA_SHARPNESS_X3: + ov9665_i2c_write(ov9665_client->addr, + 0xad, 0x28, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0xd9, 0x26, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0xda, 0x22, BYTE_LEN); + break; + case CAMERA_SHARPNESS_X4: + ov9665_i2c_write(ov9665_client->addr, + 0xad, 0x2c, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0xd9, 0x6a, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0xda, 0x66, BYTE_LEN); + break; + default: + break; + } + return 0; +} + + +static int ov9665_set_saturation(enum saturation_mode saturation_value) +{ + if (op_mode == SENSOR_SNAPSHOT_MODE) + return 0; + switch (saturation_value) { + case CAMERA_SATURATION_X0: + ov9665_i2c_write_mask(0xc7, 0x10, 0x10); + ov9665_i2c_write_mask(0xc8, 0x02, 0x02); + ov9665_i2c_write(ov9665_client->addr, + 0xcb, 0x00, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0xcc, 0x00, BYTE_LEN); + break; + case CAMERA_SATURATION_X05: + ov9665_i2c_write_mask(0xc7, 0x10, 0x10); + ov9665_i2c_write_mask(0xc8, 0x02, 0x02); + ov9665_i2c_write(ov9665_client->addr, + 0xcb, 0x20, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0xcc, 0x20, BYTE_LEN); + break; + case CAMERA_SATURATION_X1: + ov9665_i2c_write_mask(0xc7, 0x10, 0x10); + ov9665_i2c_write_mask(0xc8, 0x02, 0x02); + ov9665_i2c_write(ov9665_client->addr, + 0xcb, 0x40, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0xcc, 0x40, BYTE_LEN); + break; + case CAMERA_SATURATION_X15: + ov9665_i2c_write_mask(0xc7, 0x10, 0x10); + ov9665_i2c_write_mask(0xc8, 0x02, 0x02); + ov9665_i2c_write(ov9665_client->addr, + 0xcb, 0x60, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0xcc, 0x60, BYTE_LEN); + break; + case CAMERA_SATURATION_X2: + ov9665_i2c_write_mask(0xc7, 0x10, 0x10); + ov9665_i2c_write_mask(0xc8, 0x02, 0x02); + ov9665_i2c_write(ov9665_client->addr, + 0xcb, 0x80, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0xcc, 0x80, BYTE_LEN); + break; + default: + break; + } + return 0; +} + +static int ov9665_set_contrast(enum contrast_mode contrast_value) +{ + if (op_mode == SENSOR_SNAPSHOT_MODE) + return 0; + ov9665_i2c_write_mask(0xc7, 0x34, 0x34); + ov9665_i2c_write_mask(0xc8, 0x04, 0x04); + ov9665_i2c_write_mask(0x64, 0x02, 0x02); + switch (contrast_value) { + case CAMERA_CONTRAST_N2: + ov9665_i2c_write(ov9665_client->addr, + 0xd0, 0x08, BYTE_LEN); + break; + case CAMERA_CONTRAST_N1: + ov9665_i2c_write(ov9665_client->addr, + 0xd0, 0x10, BYTE_LEN); + break; + case CAMERA_CONTRAST_D: + ov9665_i2c_write(ov9665_client->addr, + 0xd0, 0x20, BYTE_LEN); + break; + case CAMERA_CONTRAST_P1: + ov9665_i2c_write(ov9665_client->addr, + 0xd0, 0x30, BYTE_LEN); + break; + case CAMERA_CONTRAST_P2: + ov9665_i2c_write(ov9665_client->addr, + 0xd0, 0x40, BYTE_LEN); + break; + default: + break; + } + return 0; +} + +static int ov9665_set_front_camera_mode(enum frontcam_t frontcam_value) +{ + if (op_mode == SENSOR_SNAPSHOT_MODE) + return 0; + + switch (frontcam_value) { + case CAMERA_MIRROR: + /*mirror and flip*/ + ov9665_i2c_write(ov9665_client->addr, + 0x04, 0xa8, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0x33, 0xc8, BYTE_LEN); + break; + case CAMERA_REVERSE: + /*reverse mode*/ + ov9665_i2c_write(ov9665_client->addr, + 0x04, 0x28, BYTE_LEN); + ov9665_i2c_write(ov9665_client->addr, + 0x33, 0xc0, BYTE_LEN); + break; + default: + break; + } + return 0; +} + +static int ov9665_sensor_init(const struct msm_camera_sensor_info *data) +{ + uint8_t model_id_h = 0,model_id_l = 0; + uint16_t model_id; + int rc = 0; + pr_info("ov9665_sensor_init_probe \n"); + /* Read the Model ID of the sensor */ + rc = ov9665_i2c_read(ov9665_client->addr, + REG_OV9665_MODEL_ID_H, &model_id_h); + if (rc < 0) + goto init_probe_fail; + + rc = ov9665_i2c_read(ov9665_client->addr, + REG_OV9665_MODEL_ID_L, &model_id_l); + if (rc < 0) + goto init_probe_fail; + model_id = (((model_id_h << 8) & 0xFF00) +(model_id_l)); + pr_info("ov9665: model_id = 0x%x\n", model_id); + /* Check if it matches it with the value in Datasheet */ + if (model_id != OV9665_MODEL_ID) { + rc = -EINVAL; + goto init_probe_fail; + } + + + return rc; +init_probe_fail: + return rc; +} + +int ov9665_sensor_open_init(struct msm_camera_sensor_info *data) +{ + int rc = 0; + ov9665_ctrl = kzalloc(sizeof(struct ov9665_ctrl), GFP_KERNEL); + if (!ov9665_ctrl) { + pr_info("ov9665_init failed!\n"); + rc = -ENOMEM; + goto init_done; + } + + if (data == NULL) { + pr_err("%s sensor data is NULL\n", __func__); + return -EINVAL; + } + ov9665_ctrl->sensordata = data; + /*switch PCLK and MCLK to 2nd cam*/ + pr_info("ov9665: ov9665_sensor_probe switch clk\n"); + if(data->camera_clk_switch != NULL) + data->camera_clk_switch(); + + /* Config power down */ + rc = gpio_request(data->sensor_pwd, "ov9665"); + if (!rc) + gpio_direction_output(data->sensor_pwd, 0); + else + pr_info("GPIO(%d) request faile",data->sensor_pwd); + gpio_free(data->sensor_pwd); + mdelay(3); + /* Input MCLK = 24MHz */ + msm_camio_clk_rate_set(24000000); + mdelay(5); + + /* Config reset */ + rc = gpio_request(data->sensor_reset, "ov9665"); + if (!rc) + gpio_direction_output(data->sensor_reset, 1); + else + pr_info("GPIO(%d) request faile", data->sensor_reset); + gpio_free(data->sensor_reset); + mdelay(20); + msm_camio_camif_pad_reg_reset(); + + rc = ov9665_i2c_write_table(&ov9665_regs.plltbl[0], + ov9665_regs.plltbl_size); + + /*read ID*/ + rc = ov9665_sensor_init(data); + if (rc < 0) { + pr_info("ov9665_sensor_init failed!\n"); + goto init_fail; + } + /*set initial register*/ + rc = ov9665_reg_init(); + if (rc < 0) { + pr_info("ov9665_sensor_reg_init failed!\n"); + goto init_fail; + } +init_done: + return rc; + +init_fail: + /* remove free ov9665_ctrl to prevent kernel panic in sensor release */ + pr_info("ov9665_sensor_open_init failed\n"); + return rc; +} + +static int ov9665_init_client(struct i2c_client *client) +{ + /* Initialize the MSM_CAMI2C Chip */ + init_waitqueue_head(&ov9665_wait_queue); + return 0; +} + +int ov9665_sensor_config(void __user *argp) +{ + struct sensor_cfg_data cfg_data; + long rc = 0; + if (copy_from_user(&cfg_data, + (void *)argp, sizeof(struct sensor_cfg_data))) + return -EFAULT; + + switch (cfg_data.cfgtype) { + case CFG_SET_MODE: + rc = ov9665_set_sensor_mode(cfg_data.mode); + break; + + case CFG_SET_EFFECT: + rc = ov9665_set_effect(cfg_data.cfg.effect); + if(rc < 0) + return rc; + break; + + case CFG_SET_ANTIBANDING: + rc = ov9665_set_antibanding + (cfg_data.cfg.antibanding_value); + break; + case CFG_SET_BRIGHTNESS: + rc = ov9665_set_brightness + (cfg_data.cfg.brightness_value); + break; + case CFG_SET_WB: + rc = ov9665_set_wb(cfg_data.cfg.wb_value); + break; + case CFG_SET_SHARPNESS: + rc = ov9665_set_sharpness + (cfg_data.cfg.sharpness_value); + break; + case CFG_SET_SATURATION: + rc = ov9665_set_saturation + (cfg_data.cfg.saturation_value); + break; + case CFG_SET_CONTRAST: + rc = ov9665_set_contrast(cfg_data.cfg.contrast_value); + break; + case CFG_SET_FRONT_CAMERA_MODE: + rc = ov9665_set_front_camera_mode(cfg_data.cfg.frontcam_value); + break; + default: + rc = -EINVAL; + break; + } + + return rc; +} + +int ov9665_sensor_release(void) +{ + int rc = 0; + down(&ov9665_sem); + + rc = gpio_request(ov9665_ctrl->sensordata->sensor_pwd, "ov9665"); + if (!rc) + gpio_direction_output(ov9665_ctrl->sensordata->sensor_pwd, 1); + else + pr_info("GPIO(%d) request faile", + ov9665_ctrl->sensordata->sensor_pwd); + gpio_free(ov9665_ctrl->sensordata->sensor_pwd); + + if (ov9665_ctrl->sensordata->camera_get_source() == SECOND_SOURCE) { + rc = gpio_request( + ov9665_ctrl->sensordata->sensor_reset, "ov9665"); + if (!rc) + gpio_direction_output( + ov9665_ctrl->sensordata->sensor_reset, 0); + else + pr_info("GPIO(%d) request faile", + ov9665_ctrl->sensordata->sensor_reset); + gpio_free(ov9665_ctrl->sensordata->sensor_reset); + } + + + if (ov9665_ctrl) { + kfree(ov9665_ctrl); + ov9665_ctrl = NULL; + } + + up(&ov9665_sem); + + return rc; +} + +static const char *Ov9665Vendor = "OmniVision"; +static const char *Ov9665NAME = "ov9665"; +static const char *Ov9665Size = "1M"; +static uint32_t htcwc_value; + +static ssize_t sensor_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + + sprintf(buf, "%s %s %s\n", Ov9665Vendor, Ov9665NAME, Ov9665Size); + ret = strlen(buf) + 1; + + return ret; +} + +static ssize_t htcwc_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + length = sprintf(buf, "%d\n", htcwc_value); + return length; +} + +static ssize_t htcwc_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + uint32_t tmp = 0; + + tmp = buf[0] - 0x30; /* only get the first char */ + +#if 0 + if (strcmp(current->comm,"com.android.camera")!=0){ + pr_info("No permission : not camera ap\n"); + return -EINVAL; + } +#endif + + htcwc_value = tmp; + //pr_info("current_comm = %s\n", current->comm); + pr_info("htcwc_value = %d\n", htcwc_value); + return count; +} + +static ssize_t sensor_read_node(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + length = sprintf(buf, "%d\n", sensor_probe_node); + return length; +} + +static DEVICE_ATTR(sensor, 0444, sensor_vendor_show, NULL); +static DEVICE_ATTR(htcwc, 0777, htcwc_get, htcwc_set); +static DEVICE_ATTR(node, 0444, sensor_read_node, NULL); + +static struct kobject *android_ov9665; + +static int ov9665_sysfs_init(void) +{ + int ret ; + pr_info("ov9665:kobject creat and add\n"); + android_ov9665 = kobject_create_and_add("android_camera2", NULL); + if (android_ov9665 == NULL) { + pr_info("ov9665_sysfs_init: subsystem_register " \ + "failed\n"); + ret = -ENOMEM; + return ret ; + } + pr_info("ov9665:sysfs_create_file\n"); + ret = sysfs_create_file(android_ov9665, &dev_attr_sensor.attr); + if (ret) { + pr_info("ov9665_sysfs_init: sysfs_create_file " \ + "failed\n"); + kobject_del(android_ov9665); + } + + ret = sysfs_create_file(android_ov9665, &dev_attr_htcwc.attr); + if (ret) { + pr_info("ov9665_sysfs_init: sysfs_create_file htcwc failed\n"); + kobject_del(android_ov9665); + } + + ret = sysfs_create_file(android_ov9665, &dev_attr_node.attr); + if (ret) { + pr_info("ov9665_sysfs_init: dev_attr_node failed\n"); + kobject_del(android_ov9665); + } + + return 0 ; +} + + +static int ov9665_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + rc = -ENOTSUPP; + goto probe_failure; + } + + ov9665_sensorw = kzalloc(sizeof(struct ov9665_work), GFP_KERNEL); + + if (!ov9665_sensorw) { + rc = -ENOMEM; + goto probe_failure; + } + + i2c_set_clientdata(client, ov9665_sensorw); + ov9665_init_client(client); + ov9665_client = client; + + pr_info("ov9665_probe succeeded!\n"); + + return 0; + +probe_failure: + kfree(ov9665_sensorw); + ov9665_sensorw = NULL; + pr_info("ov9665_probe failed!\n"); + return rc; +} + +static const struct i2c_device_id ov9665_i2c_id[] = { + {"ov9665", 0}, + {}, +}; + +static struct i2c_driver ov9665_i2c_driver = { + .id_table = ov9665_i2c_id, + .probe = ov9665_i2c_probe, + .remove = __exit_p(ov9665_i2c_remove), + .driver = { + .name = "ov9665", + }, +}; + +static int ov9665_sensor_probe(struct msm_camera_sensor_info *info, + struct msm_sensor_ctrl *s) +{ + int rc = i2c_add_driver(&ov9665_i2c_driver); + if (rc < 0 || ov9665_client == NULL) { + rc = -ENOTSUPP; + goto probe_done; + } + + pr_info("ov9665 s->node %d\n", s->node); + sensor_probe_node = s->node; + + /*switch clk source*/ + pr_info("ov9665: ov9665_sensor_probe switch clk\n"); + if(info->camera_clk_switch != NULL) + info->camera_clk_switch(); + + + /* Config power down */ + if(ov9665_pwd(info)<0) + goto probe_fail; + mdelay(3); + + /*Config reset */ + if(ov9665_reset(info)<0) + goto probe_fail; + mdelay(5); + + /*MCLK enable*/ + pr_info("ov9665: MCLK enable clk\n"); + msm_camio_clk_rate_set(24000000); + mdelay(100); + + /* PLL Setup Start */ + rc = ov9665_i2c_write_table(&ov9665_regs.plltbl[0], + ov9665_regs.plltbl_size); + + rc = ov9665_sensor_init(info); + if (rc < 0) + goto probe_fail; + /*set initial register*/ + rc = ov9665_reg_init(); + if (rc < 0) + goto probe_fail; + if (info->camera_main_set_probe != NULL) + info->camera_main_set_probe(true); + + s->s_init = ov9665_sensor_open_init; + s->s_release = ov9665_sensor_release; + s->s_config = ov9665_sensor_config; + + /*init done*/ + mdelay(800); + ov9665_init_done(info); + ov9665_sysfs_init(); + +probe_done: + pr_info("%s %s:%d\n", __FILE__, __func__, __LINE__); + return rc; +probe_fail: + pr_err("OV9665 probe faile\n"); + return rc; + +} + +static int __ov9665_probe(struct platform_device *pdev) +{ + int rc; + struct msm_camera_sensor_info *sdata = pdev->dev.platform_data; + + if (sdata->camera_main_get_probe != NULL) { + if (sdata->camera_main_get_probe()) { + pr_info("__s5k6aafx_probe camera main get probed already.\n"); + return 0; + } + } + return msm_camera_drv_start(pdev, ov9665_sensor_probe); +} + +static struct platform_driver msm_camera_driver = { + .probe = __ov9665_probe, + .driver = { + .name = "msm_camera_ov9665", + .owner = THIS_MODULE, + }, +}; + +static int __init ov9665_init(void) +{ + return platform_driver_register(&msm_camera_driver); +} + +module_init(ov9665_init); diff --git a/drivers/media/video/msm/ov9665.h b/drivers/media/video/msm/ov9665.h new file mode 100644 index 0000000000000..e399b86b6bbbf --- /dev/null +++ b/drivers/media/video/msm/ov9665.h @@ -0,0 +1,54 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#ifndef MT9D112_H +#define MT9D112_H + +#include +#include + +extern struct ov9665_reg ov9665_regs; + +enum ov9665_width { + WORD_LEN, + BYTE_LEN +}; + +struct ov9665_i2c_reg_conf { + unsigned short waddr; + unsigned short wdata; + enum ov9665_width width; + unsigned short mdelay_time; +}; + +struct ov9665_reg { + const struct ov9665_i2c_reg_conf *register_init; + uint16_t register_init_size; + const struct register_address_value_pair *prev_snap_reg_settings; + uint16_t prev_snap_reg_settings_size; + const struct register_address_value_pair *noise_reduction_reg_settings; + uint16_t noise_reduction_reg_settings_size; + const struct ov9665_i2c_reg_conf *plltbl; + uint16_t plltbl_size; + const struct ov9665_i2c_reg_conf *stbl; + uint16_t stbl_size; + const struct ov9665_i2c_reg_conf *rftbl; + uint16_t rftbl_size; +}; + +#endif /* MT9D112_H */ diff --git a/drivers/media/video/msm/ov9665_reg.c b/drivers/media/video/msm/ov9665_reg.c new file mode 100644 index 0000000000000..46cd4357717b5 --- /dev/null +++ b/drivers/media/video/msm/ov9665_reg.c @@ -0,0 +1,210 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "ov9665.h" + + +static const struct ov9665_i2c_reg_conf const pll_setup_tbl[] = { + {0x3E, 0xD0, BYTE_LEN, 5}, + {0x3E, 0xD0, BYTE_LEN, 5}, + {0x12, 0x80, BYTE_LEN, 5} +}; + +/* sensor register init*/ +static const struct ov9665_i2c_reg_conf const register_init_tbl[] = { + /*sensor reset*/ + {0x12, 0x80, BYTE_LEN, 1}, + /*sensor IO output*/ + {0xd5, 0xff, BYTE_LEN, 1}, + {0xd6, 0x3f, BYTE_LEN, 1}, + /*Clock 24MHz 10FPS*/ + {0x3d, 0x3c, BYTE_LEN, 1}, + {0x11, 0x80, BYTE_LEN, 1},/*0x81 24MHz,0x80 48MHz*/ + {0x2a, 0x00, BYTE_LEN, 1}, + {0x2b, 0x00, BYTE_LEN, 1}, + /*power control*/ + {0x3a, 0xd9, BYTE_LEN, 1}, + {0x3b, 0x00, BYTE_LEN, 1}, + {0x3c, 0x58, BYTE_LEN, 1}, + {0x3e, 0x50, BYTE_LEN, 1}, + {0x71, 0x00, BYTE_LEN, 1}, + /*driving strengh*/ + {0x09, 0x03, BYTE_LEN, 1}, + /*Data Format YUV*/ + {0xD7, 0x10, BYTE_LEN, 1}, + {0x6a, 0x24, BYTE_LEN, 1}, + {0x85, 0xe7, BYTE_LEN, 1}, + /*sample option*/ + {0x63, 0x01, BYTE_LEN, 1}, + /*Windowing*/ + {0x17, 0x0c, BYTE_LEN, 1}, + {0x18, 0x5c, BYTE_LEN, 1}, + {0x19, 0x01, BYTE_LEN, 1}, + {0x1a, 0x82, BYTE_LEN, 1}, + {0x03, 0x0f, BYTE_LEN, 1}, + {0x2b, 0x00, BYTE_LEN, 1}, + {0x32, 0x34, BYTE_LEN, 1}, + /*BLC*/ + {0x36, 0xb4, BYTE_LEN, 1}, + {0x65, 0x10, BYTE_LEN, 1}, + {0x70, 0x02, BYTE_LEN, 1}, + {0x71, 0x9c, BYTE_LEN, 1}, + {0x72, 0xc0, BYTE_LEN, 1}, /*For preview greenish in lowlight Weiting*/ + {0x64, 0x24, BYTE_LEN, 1}, + /*AEC ,Average ,9 zone*/ + {0x43, 0x00, BYTE_LEN, 1}, + {0x5d, 0x55, BYTE_LEN, 1}, + {0x5e, 0x57, BYTE_LEN, 1}, + {0x5f, 0x21, BYTE_LEN, 1}, + /*Brightness*/ + {0x24, 0x40, BYTE_LEN, 1}, /*upper bc 35ori 39*/ + {0x25, 0x35, BYTE_LEN, 1}, /*lower bc 2aori 2e*/ + {0x26, 0x82, BYTE_LEN, 1}, + /*BF 60Hz*/ + /*0x48 for 8xgain 28 for 4xgain 68for 16xgain*/ + {0x14, 0x48, BYTE_LEN, 1}, + {0x0c, 0x38, BYTE_LEN, 1}, + {0x4f, 0x9e, BYTE_LEN, 1}, + {0x50, 0x84, BYTE_LEN, 1}, + {0x5a, 0x67, BYTE_LEN, 1}, + /*LC enable*/ + {0x7d, 0x00, BYTE_LEN, 1}, + {0x7e, 0xa0, BYTE_LEN, 1}, + {0x7f, 0x00, BYTE_LEN, 1}, + {0x80, 0x09, BYTE_LEN, 1}, + {0x81, 0x0a, BYTE_LEN, 1}, + {0x82, 0x09, BYTE_LEN, 1}, + {0x83, 0x07, BYTE_LEN, 1}, /*07 enable LC 06 disable*/ + /*AWB advance*/ + {0x96, 0xf0, BYTE_LEN, 1}, + {0x97, 0x0a, BYTE_LEN, 1}, + {0x92, 0x17, BYTE_LEN, 1}, + {0x94, 0x38, BYTE_LEN, 1}, + {0x93, 0x33, BYTE_LEN, 1}, + {0x95, 0x49, BYTE_LEN, 1}, + {0x91, 0xd8, BYTE_LEN, 1}, + {0x90, 0xdf, BYTE_LEN, 1}, + {0x8e, 0x4a, BYTE_LEN, 1}, + {0x8f, 0x59, BYTE_LEN, 1}, + {0x8d, 0x12, BYTE_LEN, 1}, + {0x8c, 0x11, BYTE_LEN, 1}, + {0x8b, 0x0c, BYTE_LEN, 1}, + {0x86, 0x9e, BYTE_LEN, 1}, + {0x87, 0x11, BYTE_LEN, 1}, + {0x88, 0x22, BYTE_LEN, 1}, + {0x89, 0x05, BYTE_LEN, 1}, + {0x8a, 0x03, BYTE_LEN, 1}, + /*Gamma enable for outdoor 1228*/ + {0x9b, 0x05, BYTE_LEN, 1}, /*ori0x08 htc0d*/ + {0x9c, 0x10, BYTE_LEN, 1}, /*ori0x16 htc19*/ + {0x9d, 0x28, BYTE_LEN, 1}, /*ori0x2f htc2e*/ + {0x9e, 0x51, BYTE_LEN, 1}, /*ori0x56 htc51*/ + {0x9f, 0x60, BYTE_LEN, 1}, /*ori0x66 htc60*/ + {0xa0, 0x6c, BYTE_LEN, 1}, /*ori0x75 htc6c*/ + {0xa1, 0x77, BYTE_LEN, 1}, /*ori0x80 htc77*/ + {0xa2, 0x81, BYTE_LEN, 1}, /*ori0x88 htc81*/ + {0xa3, 0x8a, BYTE_LEN, 1}, /*ori0x8f htc8a*/ + {0xa4, 0x93, BYTE_LEN, 1}, /*ori0x96 htc93*/ + {0xa5, 0xa1, BYTE_LEN, 1}, /*ori0xa3 htca1*/ + {0xa6, 0xae, BYTE_LEN, 1}, /*ori0xaf htcae*/ + {0xa7, 0xc4, BYTE_LEN, 1}, /*ori0xc4 htcc4*/ + {0xa8, 0xd6, BYTE_LEN, 1}, /*ori0xd7 htcd6*/ + {0xa9, 0xe7, BYTE_LEN, 1}, /*ori0xe8 htce7*/ + {0xaa, 0x21, BYTE_LEN, 1}, /*ori0x20 htc21*/ + /*De-noise enable auto*/ + {0xab, 0xe7, BYTE_LEN, 1}, + {0xb0, 0x43, BYTE_LEN, 1}, + {0xac, 0x04, BYTE_LEN, 1}, + {0x84, 0x80, BYTE_LEN, 1}, // For stronger de-noise ori0x50 + /*Sharpness*/ + {0xad, 0x24, BYTE_LEN, 1}, //Sharpness of 0-2xgain ori0x22 + {0xd9, 0x13, BYTE_LEN, 1}, //Sharpness of 2-4,4-8xgain ori0x64 + {0xda, 0x00, BYTE_LEN, 1}, //Sharpness of >8xgain ori0xa8 + {0xae, 0x10, BYTE_LEN, 1}, + /*Scaling*/ + {0xab, 0xe7, BYTE_LEN, 1}, + {0xb9, 0xa0, BYTE_LEN, 1}, + {0xba, 0x80, BYTE_LEN, 1}, + {0xbb, 0xa0, BYTE_LEN, 1}, + {0xbc, 0x80, BYTE_LEN, 1}, + /*CMX*/ + {0xbd, 0x04, BYTE_LEN, 1}, /*0x08 unit0a*/ + {0xbe, 0x1f, BYTE_LEN, 1}, /*0x19 unit12*/ + {0xbf, 0x03, BYTE_LEN, 1}, /*0x02 unit03*/ + {0xc0, 0x0d, BYTE_LEN, 1}, /*0x05 unit05 06*/ + {0xc1, 0x24, BYTE_LEN, 1}, /*0x28 unit0b 2c*/ + {0xc2, 0x30, BYTE_LEN, 1}, /*0x2e unit10 33*/ + {0xc3, 0x34, BYTE_LEN, 1}, /*0x27 unit10 2b*/ + {0xc4, 0x34, BYTE_LEN, 1}, /*0x26 unit0d 2a*/ + {0xc5, 0x01, BYTE_LEN, 1}, /*0x00 unit03*/ + {0xc6, 0x9c, BYTE_LEN, 1}, /*0x98 unit98*/ + {0xc7, 0x18, BYTE_LEN, 1}, /*0x18 unit98*/ + {0x69, 0x48, BYTE_LEN, 1}, + /*UV ave*/ + {0x74, 0xc0, BYTE_LEN, 1}, + /*SAT & Brightness*/ + {0xc7, 0x18, BYTE_LEN, 1}, + {0xc8, 0x06, BYTE_LEN, 1}, + {0xcb, 0x40, BYTE_LEN, 1}, + {0xcc, 0x40, BYTE_LEN, 1}, + {0xcf, 0x00, BYTE_LEN, 1}, + {0xd0, 0x20, BYTE_LEN, 1}, + {0xd1, 0x00, BYTE_LEN, 1}, + /*BLC*/ + {0x0d, 0x82, BYTE_LEN, 1}, + {0x0d, 0x80, BYTE_LEN, 1}, + #if 1 + /*UV adjustment*/ + {0xd2, 0x80, BYTE_LEN, 1}, + {0x7c, 0x18, BYTE_LEN, 1}, + {0x65, 0x01, BYTE_LEN, 1}, + {0x66, 0x00, BYTE_LEN, 1}, + {0x41, 0xa0, BYTE_LEN, 1}, + {0x5b, 0x08, BYTE_LEN, 1}, + {0x60, 0x05, BYTE_LEN, 1}, + {0x05, 0x06, BYTE_LEN, 1}, + {0x03, 0x4f, BYTE_LEN, 1}, + {0x72, 0xc0, BYTE_LEN, 1}, + #endif + /*output driving current*/ + {0x09, 0x03, BYTE_LEN, 1}, + /*H/V sync signal control*/ + {0xd8, 0xc4, BYTE_LEN, 1}, + {0x15, 0x02, BYTE_LEN, 1}, + /*night mode*/ + {0x03, 0x8f, BYTE_LEN, 1}, //Control the min fps 4f ->1/2 8f ->1/4 cf->1/8 + {0x0f, 0x4e, BYTE_LEN, 1}, + {0x06, 0x50, BYTE_LEN, 1}, //keep fps 30 when gain<4 +#if 1 + /*mirror and flip*/ + {0x04, 0xa8, BYTE_LEN, 1}, + {0x33, 0xc8, BYTE_LEN, 1} +#else + /*reverse mode*/ + {0x04, 0x28, BYTE_LEN, 1}, + {0x33, 0xc0, BYTE_LEN, 1} +#endif +}; + + +struct ov9665_reg ov9665_regs = { + .register_init = ®ister_init_tbl, + .register_init_size = ARRAY_SIZE(register_init_tbl), + .plltbl = pll_setup_tbl, + .plltbl_size = ARRAY_SIZE(pll_setup_tbl), +}; diff --git a/drivers/media/video/msm/s5k3e2fx.c b/drivers/media/video/msm/s5k3e2fx.c index a2ab9ba179dcd..4cffae710dcf4 100644 --- a/drivers/media/video/msm/s5k3e2fx.c +++ b/drivers/media/video/msm/s5k3e2fx.c @@ -14,11 +14,17 @@ #include #include #include +#include + static uint16_t g_usModuleVersion; /*0: rev.4, 1: rev.5 */ /* prepare for modify PCLK*/ +#ifdef CONFIG_MACH_PASSIONC +#define REG_PLL_MULTIPLIER_LSB_VALUE 0xA0 +#else #define REG_PLL_MULTIPLIER_LSB_VALUE 0x90 +#endif /* 0xA0 for PCLK=80MHz */ /* 0x90 for PCLK=72MHz */ @@ -43,11 +49,6 @@ static uint16_t g_usModuleVersion; /*0: rev.4, 1: rev.5 */ #define GROUPED_PARAMETER_HOLD 0x01 #define GROUPED_PARAMETER_UPDATE 0x00 -/* Greenish in low light */ -#define REG_MASK_CORRUPTED_FRAMES 0x0105 -#define MASK 0x01 -#define NO_MASK 0x00 - /* PLL Registers */ #define REG_PRE_PLL_CLK_DIV 0x0305 #define REG_PLL_MULTIPLIER_MSB 0x0306 @@ -138,7 +139,7 @@ static uint16_t g_usModuleVersion; /*0: rev.4, 1: rev.5 */ #define REG_301B_RESERVED 0x301B #define REG_30BD_RESERVED 0x30BD #define REG_30C2_RESERVED 0x30C2 -#define REG_3151_RESERVED 0x3151 +#define REG_3151_RESERVED 0x3151 /* 100202 the right address is 0x3151 */ #define REG_3029_RESERVED 0x3029 #define REG_30BF_RESERVED 0x30BF #define REG_3022_RESERVED 0x3022 @@ -170,8 +171,6 @@ static uint16_t g_usModuleVersion; /*0: rev.4, 1: rev.5 */ #define REG_Y_ADDR_END_MSB 0x034A #define REG_Y_ADDR_END_LSB 0x034B -#define NUM_INIT_REG 94 -#define NUM_LC_REG 434 struct s5k3e2fx_i2c_reg_conf { unsigned short waddr; @@ -179,32 +178,21 @@ struct s5k3e2fx_i2c_reg_conf { }; /* Separate the EVT4/EVT5 sensor init and LC setting start */ -struct s5k3e2fx_i2c_reg_conf Init_setting[2][NUM_INIT_REG] = { + +struct s5k3e2fx_i2c_reg_conf Init_setting_evt4[] = { /*EVT4 */ - { {REG_PRE_PLL_CLK_DIV, 0x06}, /* PLL setting */ {REG_PLL_MULTIPLIER_MSB, 0x00}, - {REG_PLL_MULTIPLIER_LSB, REG_PLL_MULTIPLIER_LSB_VALUE}, + {REG_PLL_MULTIPLIER_LSB, 0x83}, {REG_VT_PIX_CLK_DIV, 0x08}, {REG_VT_SYS_CLK_DIV, 0x01}, {REG_OP_PIX_CLK_DIV, 0x08}, {REG_OP_SYS_CLK_DIV, 0x01}, -/* Data Format */ - {REG_CCP_DATA_FORMAT_MSB, 0x0a}, - {REG_CCP_DATA_FORMAT_LSB, 0x0a}, /* Preview Output Size */ {REG_X_OUTPUT_SIZE_MSB, 0x05}, {REG_X_OUTPUT_SIZE_LSB, 0x10}, {REG_Y_OUTPUT_SIZE_MSB, 0x03}, {REG_Y_OUTPUT_SIZE_LSB, 0xcc}, - {REG_X_ADDR_START_MSB, 0x00}, - {REG_X_ADDR_START_LSB, 0x08}, - {REG_Y_ADDR_START_MSB, 0x00}, - {REG_Y_ADDR_START_LSB, 0x08}, - {REG_X_ADDR_END_MSB, 0x0a}, - {REG_X_ADDR_END_LSB, 0x27}, - {REG_Y_ADDR_END_MSB, 0x07}, - {REG_Y_ADDR_END_LSB, 0x9f}, /* Frame format */ {REG_FRAME_LENGTH_LINES_MSB, 0x03}, {REG_FRAME_LENGTH_LINES_LSB, 0xe2}, @@ -230,72 +218,44 @@ struct s5k3e2fx_i2c_reg_conf Init_setting[2][NUM_INIT_REG] = { {REG_S1S_END, 0x93}, {REG_S3_START, 0x05}, {REG_S3_END, 0x3a}, - {REG_CMP_EN_START, 0x10}, {REG_CLP_SL_START, 0x02}, {REG_CLP_SL_END, 0x3e}, - {REG_OFF_START, 0x02}, {REG_RMP_EN_START, 0x0e}, {REG_TX_START, 0x46}, {REG_TX_END, 0x64}, {REG_STX_WIDTH, 0x1e}, - {REG_CLAMP_ON, 0x00}, {REG_301D_RESERVED, 0x3f}, {REG_VPIX, 0x04}, {REG_3028_RESERVED, 0x40}, {REG_3070_RESERVED, 0xdf}, - {REG_3072_RESERVED, 0x20}, {REG_301B_RESERVED, 0x73}, {REG_OFFSET, 0x02}, {REG_30BD_RESERVED, 0x06}, {REG_30C2_RESERVED, 0x0b}, {REG_SHADE_CLK_ENABLE, 0x81}, - {REG_3151_RESERVED, 0xe6}, + {0x3151, 0xe6}, {REG_3029_RESERVED, 0x02}, {REG_30BF_RESERVED, 0x00}, {REG_3022_RESERVED, 0x87}, {REG_3019_RESERVED, 0x60}, - {REG_3019_RESERVED, 0x60}, - {REG_3019_RESERVED, 0x60}, - {REG_3019_RESERVED, 0x60}, - {REG_3019_RESERVED, 0x60}, - {REG_3019_RESERVED, 0x60}, + {0x3146, 0x3c}, {REG_3152_RESERVED, 0x08}, - {REG_3150_RESERVED, 0x50}, /* from 0x40 to 0x50 for PCLK=80MHz */ -/* Inverse PCLK = 0x50 */ - {REG_3157_RESERVED, 0x04}, /* from 0x00 to 0x04 for PCLK=80MHz */ -/* PCLK Delay offset; 0x0a will delay around 4ns at 80MHz */ - {REG_3159_RESERVED, 0x0f}, /* from 0x00 to 0x0f for PCLK=80MHz */ + {REG_3159_RESERVED, 0x0A}, /* HS, VS driving strength [3:2]=>VS, [1:0]=>HS 00:2mA, 01:4mA, 10:6mA, * 11:8mA */ - {REG_315A_RESERVED, 0xf0}, /* from 0x10 to 0xf0 for PCLK=80MHz */ + {REG_315A_RESERVED, 0xAA}, /* PCLK, DATA driving strength [7:6]=>data, [5:4]=>PCLK 00:2mA, 01:4mA * 10:6mA, 11:8mA */ /* AEC Setting */ - {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, 0x00}, {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB_VALUE}, {REG_FINE_INTEGRATION_TIME, 0x02}, {REG_COARSE_INTEGRATION_TIME, 0x03}, -/* Preview LC config Setting */ - {REG_SH4CH_BLK_WIDTH_R, 0x52}, - {REG_SH4CH_BLK_HEIGHT_R, 0x3e}, - {REG_SH4CH_STEP_X_R_MSB, 0x03}, - {REG_SH4CH_STEP_X_R_LSB, 0x1f}, - {REG_SH4CH_STEP_Y_R_MSB, 0x04}, - {REG_SH4CH_STEP_Y_R_LSB, 0x21}, - {REG_SH4CH_START_BLK_CNT_X_R, 0x04}, - {REG_SH4CH_START_BLK_INT_X_R, 0x00}, - {REG_SH4CH_START_FRAC_X_R_MSB, 0x0c}, - {REG_SH4CH_START_FRAC_X_R_LSB, 0x7c}, - {REG_SH4CH_START_BLK_CNT_Y_R, 0x04}, - {REG_SH4CH_START_BLK_INT_Y_R, 0x00}, - {REG_SH4CH_START_FRAC_Y_R_MSB, 0x10}, - {REG_SH4CH_START_FRAC_Y_R_LSB, 0x84}, - }, +}; +struct s5k3e2fx_i2c_reg_conf Init_setting_evt5[] = { /* EVT5 */ - { {REG_PRE_PLL_CLK_DIV, 0x06}, /* PLL setting */ {REG_PLL_MULTIPLIER_MSB, 0x00}, {REG_PLL_MULTIPLIER_LSB, REG_PLL_MULTIPLIER_LSB_VALUE}, @@ -312,13 +272,13 @@ struct s5k3e2fx_i2c_reg_conf Init_setting[2][NUM_INIT_REG] = { {REG_Y_OUTPUT_SIZE_MSB, 0x03}, {REG_Y_OUTPUT_SIZE_LSB, 0xcc}, {REG_X_ADDR_START_MSB, 0x00}, - {REG_X_ADDR_START_LSB, 0x08}, + {REG_X_ADDR_START_LSB, 0x00}, /* 100202 Change to 00 to the same as DesireC */ {REG_Y_ADDR_START_MSB, 0x00}, - {REG_Y_ADDR_START_LSB, 0x08}, + {REG_Y_ADDR_START_LSB, 0x00}, /* 100202 Change to 00 to the same as DesireC */ {REG_X_ADDR_END_MSB, 0x0a}, - {REG_X_ADDR_END_LSB, 0x27}, + {REG_X_ADDR_END_LSB, 0x2F}, /* 100202 Change to 2F to the same as DesireC */ {REG_Y_ADDR_END_MSB, 0x07}, - {REG_Y_ADDR_END_LSB, 0x9f}, + {REG_Y_ADDR_END_LSB, 0xA7}, /* 100202 Change to A7 to the same as DesireC */ /* Frame format */ {REG_FRAME_LENGTH_LINES_MSB, 0x03}, {REG_FRAME_LENGTH_LINES_LSB, 0xe2}, @@ -363,7 +323,7 @@ struct s5k3e2fx_i2c_reg_conf Init_setting[2][NUM_INIT_REG] = { {REG_30BD_RESERVED, 0x06}, {REG_30C2_RESERVED, 0x0b}, {REG_SHADE_CLK_ENABLE, 0x81}, - {REG_3151_RESERVED, 0xe6}, + {REG_3151_RESERVED, 0xe6}, /* 100202 the right address is 0x3151 */ {REG_3029_RESERVED, 0x02}, {REG_30BF_RESERVED, 0x00}, {REG_3022_RESERVED, 0x87}, @@ -387,7 +347,6 @@ struct s5k3e2fx_i2c_reg_conf Init_setting[2][NUM_INIT_REG] = { * 10:6mA, 11:8mA */ /* AEC Setting */ - {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, 0x00}, {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB_VALUE}, {REG_FINE_INTEGRATION_TIME, 0x02}, {REG_COARSE_INTEGRATION_TIME, 0x03}, @@ -406,454 +365,463 @@ struct s5k3e2fx_i2c_reg_conf Init_setting[2][NUM_INIT_REG] = { {REG_SH4CH_START_BLK_INT_Y_R, 0x00}, {REG_SH4CH_START_FRAC_Y_R_MSB, 0x10}, {REG_SH4CH_START_FRAC_Y_R_LSB, 0x84}, - } }; -struct s5k3e2fx_i2c_reg_conf lc_setting[2][NUM_LC_REG] = { -/*EVT4 */ - { - /*EVT4 */ /* 100108 Modify LC setting DNP light source t75-r73*/ +struct s5k3e2fx_i2c_reg_conf lc_setting_evt4[] = { + /*EVT4 */ {0x3200, 0x00}, - {0x3201, 0x99}, - {0x3202, 0xc1}, + {0x3201, 0xbe}, + {0x3202, 0x4e}, {0x3203, 0x0f}, - {0x3204, 0xd0}, - {0x3205, 0x1b}, + {0x3204, 0xb9}, + {0x3205, 0x07}, {0x3206, 0x00}, - {0x3207, 0x24}, - {0x3208, 0x8d}, + {0x3207, 0x4b}, + {0x3208, 0xdf}, {0x3209, 0x0f}, - {0x320a, 0xee}, - {0x320b, 0x0f}, + {0x320a, 0xc6}, + {0x320b, 0x39}, {0x320c, 0x00}, - {0x320d, 0x04}, - {0x320e, 0x5c}, + {0x320d, 0x13}, + {0x320e, 0xee}, {0x320f, 0x00}, - {0x3210, 0x07}, - {0x3211, 0x68}, + {0x3210, 0x14}, + {0x3211, 0x79}, {0x3212, 0x0f}, - {0x3213, 0xc2}, - {0x3214, 0x82}, + {0x3213, 0x9d}, + {0x3214, 0xed}, {0x3215, 0x00}, - {0x3216, 0x29}, - {0x3217, 0x3e}, + {0x3216, 0x3d}, + {0x3217, 0x02}, {0x3218, 0x0f}, - {0x3219, 0xd3}, - {0x321a, 0x63}, + {0x3219, 0xa8}, + {0x321a, 0x6a}, {0x321b, 0x00}, - {0x321c, 0x22}, - {0x321d, 0x6c}, + {0x321c, 0x4c}, + {0x321d, 0x9a}, {0x321e, 0x0f}, - {0x321f, 0xf8}, - {0x3220, 0xce}, + {0x321f, 0xfb}, + {0x3220, 0xdb}, {0x3221, 0x0f}, - {0x3222, 0xed}, - {0x3223, 0x30}, + {0x3222, 0xc8}, + {0x3223, 0x1a}, {0x3224, 0x00}, - {0x3225, 0x37}, - {0x3226, 0x87}, + {0x3225, 0x5b}, + {0x3226, 0xf3}, {0x3227, 0x0f}, - {0x3228, 0xc2}, - {0x3229, 0x87}, + {0x3228, 0xae}, + {0x3229, 0xe3}, {0x322a, 0x00}, - {0x322b, 0x2a}, - {0x322c, 0xc6}, + {0x322b, 0x5b}, + {0x322c, 0xc8}, {0x322d, 0x0f}, - {0x322e, 0xf3}, - {0x322f, 0xd9}, + {0x322e, 0xc3}, + {0x322f, 0xf6}, {0x3230, 0x0f}, - {0x3231, 0xea}, - {0x3232, 0x1a}, + {0x3231, 0xe4}, + {0x3232, 0xb3}, {0x3233, 0x00}, - {0x3234, 0x2d}, - {0x3235, 0x9f}, + {0x3234, 0x58}, + {0x3235, 0xdf}, {0x3236, 0x0f}, - {0x3237, 0xde}, - {0x3238, 0x7d}, + {0x3237, 0xbf}, + {0x3238, 0x67}, {0x3239, 0x00}, - {0x323a, 0x37}, - {0x323b, 0x1e}, + {0x323a, 0x3c}, + {0x323b, 0x8e}, {0x323c, 0x0f}, - {0x323d, 0xed}, - {0x323e, 0x9c}, - {0x323f, 0x0f}, - {0x3240, 0xf6}, + {0x323d, 0xd0}, + {0x323e, 0x3d}, + {0x323f, 0x00}, + {0x3240, 0x11}, {0x3241, 0xfd}, {0x3242, 0x00}, - {0x3243, 0x15}, - {0x3244, 0xeb}, + {0x3243, 0x1a}, + {0x3244, 0xf0}, {0x3245, 0x0f}, - {0x3246, 0xd3}, - {0x3247, 0xca}, + {0x3246, 0xbd}, + {0x3247, 0x5d}, {0x3248, 0x00}, - {0x3249, 0x08}, - {0x324a, 0xe6}, + {0x3249, 0x22}, + {0x324a, 0x32}, {0x324b, 0x0f}, - {0x324c, 0xf4}, - {0x324d, 0x7a}, + {0x324c, 0xff}, + {0x324d, 0x2e}, {0x324e, 0x0f}, - {0x324f, 0xed}, - {0x3250, 0x1e}, + {0x324f, 0xeb}, + {0x3250, 0x0c}, {0x3251, 0x00}, - {0x3252, 0x0d}, - {0x3253, 0x46}, + {0x3252, 0x11}, + {0x3253, 0xbd}, {0x3254, 0x00}, - {0x3255, 0x0c}, - {0x3256, 0x3e}, - {0x3257, 0x00}, - {0x3258, 0x09}, - {0x3259, 0xcf}, + {0x3255, 0x17}, + {0x3256, 0xda}, + {0x3257, 0x0f}, + {0x3258, 0xeb}, + {0x3259, 0xf9}, {0x325a, 0x00}, - {0x325b, 0x09}, - {0x325c, 0xb5}, + {0x325b, 0x00}, + {0x325c, 0x81}, {0x325d, 0x0f}, - {0x325e, 0xec}, - {0x325f, 0x47}, + {0x325e, 0xdf}, + {0x325f, 0x3e}, {0x3260, 0x00}, - {0x3261, 0x1d}, - {0x3262, 0xd8}, + {0x3261, 0x2c}, + {0x3262, 0x9f}, {0x3263, 0x0f}, - {0x3264, 0xf7}, - {0x3265, 0x11}, + {0x3264, 0xe9}, + {0x3265, 0xd7}, {0x3266, 0x0f}, - {0x3267, 0xea}, - {0x3268, 0x3d}, + {0x3267, 0xd1}, + {0x3268, 0x83}, {0x3269, 0x00}, - {0x326a, 0x09}, - {0x326b, 0xcc}, + {0x326a, 0x3e}, + {0x326b, 0x18}, {0x326c, 0x00}, - {0x326d, 0x9b}, - {0x326e, 0x73}, + {0x326d, 0xcb}, + {0x326e, 0x32}, {0x326f, 0x0f}, - {0x3270, 0xd4}, - {0x3271, 0x9e}, + {0x3270, 0xaf}, + {0x3271, 0xe3}, {0x3272, 0x00}, - {0x3273, 0x1a}, - {0x3274, 0x87}, + {0x3273, 0x51}, + {0x3274, 0xc8}, {0x3275, 0x0f}, - {0x3276, 0xfd}, - {0x3277, 0xeb}, - {0x3278, 0x0f}, - {0x3279, 0xf5}, - {0x327a, 0xb4}, + {0x3276, 0xc5}, + {0x3277, 0x4c}, + {0x3278, 0x00}, + {0x3279, 0x13}, + {0x327a, 0x30}, {0x327b, 0x00}, - {0x327c, 0x0d}, - {0x327d, 0x8c}, + {0x327c, 0x15}, + {0x327d, 0x7b}, {0x327e, 0x0f}, - {0x327f, 0xc9}, - {0x3280, 0x4d}, + {0x327f, 0x97}, + {0x3280, 0x3f}, {0x3281, 0x00}, - {0x3282, 0x1d}, - {0x3283, 0x2d}, + {0x3282, 0x3e}, + {0x3283, 0x26}, {0x3284, 0x0f}, - {0x3285, 0xea}, - {0x3286, 0x5b}, + {0x3285, 0xb3}, + {0x3286, 0x02}, {0x3287, 0x00}, - {0x3288, 0x04}, - {0x3289, 0x76}, + {0x3288, 0x37}, + {0x3289, 0x73}, {0x328a, 0x00}, - {0x328b, 0x10}, - {0x328c, 0x2d}, + {0x328b, 0x0f}, + {0x328c, 0xd7}, {0x328d, 0x0f}, - {0x328e, 0xe6}, - {0x328f, 0xde}, + {0x328e, 0xbf}, + {0x328f, 0xdc}, {0x3290, 0x00}, - {0x3291, 0x26}, - {0x3292, 0x85}, + {0x3291, 0x5a}, + {0x3292, 0x9b}, {0x3293, 0x0f}, - {0x3294, 0xcf}, - {0x3295, 0x12}, + {0x3294, 0xaf}, + {0x3295, 0x68}, {0x3296, 0x00}, - {0x3297, 0x14}, - {0x3298, 0x0f}, - {0x3299, 0x00}, - {0x329a, 0x0b}, - {0x329b, 0x36}, + {0x3297, 0x4c}, + {0x3298, 0xdb}, + {0x3299, 0x0f}, + {0x329a, 0xdc}, + {0x329b, 0xb5}, {0x329c, 0x0f}, - {0x329d, 0xe4}, - {0x329e, 0xa4}, + {0x329d, 0xca}, + {0x329e, 0x69}, {0x329f, 0x00}, - {0x32a0, 0x21}, - {0x32a1, 0x1f}, + {0x32a0, 0x68}, + {0x32a1, 0x0a}, {0x32a2, 0x0f}, - {0x32a3, 0xf3}, - {0x32a4, 0x99}, + {0x32a3, 0xc9}, + {0x32a4, 0x6c}, {0x32a5, 0x00}, - {0x32a6, 0x30}, - {0x32a7, 0x8f}, + {0x32a6, 0x37}, + {0x32a7, 0x6e}, {0x32a8, 0x0f}, - {0x32a9, 0xf9}, - {0x32aa, 0x35}, + {0x32a9, 0xe2}, + {0x32aa, 0x22}, {0x32ab, 0x0f}, - {0x32ac, 0xee}, - {0x32ad, 0x6e}, + {0x32ac, 0xfd}, + {0x32ad, 0x8b}, {0x32ae, 0x00}, - {0x32af, 0x09}, - {0x32b0, 0x19}, + {0x32af, 0x36}, + {0x32b0, 0x33}, {0x32b1, 0x0f}, - {0x32b2, 0xf0}, - {0x32b3, 0x57}, + {0x32b2, 0xa3}, + {0x32b3, 0xf7}, {0x32b4, 0x00}, - {0x32b5, 0x01}, - {0x32b6, 0xcc}, - {0x32b7, 0x0f}, - {0x32b8, 0xf1}, - {0x32b9, 0x0b}, + {0x32b5, 0x1b}, + {0x32b6, 0xd5}, + {0x32b7, 0x00}, + {0x32b8, 0x0a}, + {0x32b9, 0x4f}, {0x32ba, 0x0f}, - {0x32bb, 0xee}, - {0x32bc, 0x99}, + {0x32bb, 0xd6}, + {0x32bc, 0x4d}, {0x32bd, 0x00}, - {0x32be, 0x11}, - {0x32bf, 0x3d}, - {0x32c0, 0x00}, - {0x32c1, 0x10}, - {0x32c2, 0x64}, - {0x32c3, 0x0f}, - {0x32c4, 0xf6}, - {0x32c5, 0xab}, + {0x32be, 0x21}, + {0x32bf, 0x85}, + {0x32c0, 0x0f}, + {0x32c1, 0xfc}, + {0x32c2, 0x04}, + {0x32c3, 0x00}, + {0x32c4, 0x10}, + {0x32c5, 0x8c}, {0x32c6, 0x00}, - {0x32c7, 0x03}, - {0x32c8, 0x19}, + {0x32c7, 0x00}, + {0x32c8, 0xf5}, {0x32c9, 0x0f}, - {0x32ca, 0xf3}, - {0x32cb, 0xc9}, + {0x32ca, 0xd4}, + {0x32cb, 0xf3}, {0x32cc, 0x00}, - {0x32cd, 0x17}, - {0x32ce, 0xb3}, + {0x32cd, 0x3b}, + {0x32ce, 0x31}, {0x32cf, 0x0f}, - {0x32d0, 0xf2}, - {0x32d1, 0x3d}, + {0x32d0, 0xe0}, + {0x32d1, 0xb3}, {0x32d2, 0x0f}, - {0x32d3, 0xf4}, - {0x32d4, 0x7e}, + {0x32d3, 0xe4}, + {0x32d4, 0xa1}, {0x32d5, 0x00}, - {0x32d6, 0x09}, - {0x32d7, 0x46}, + {0x32d6, 0x22}, + {0x32d7, 0x10}, {0x32d8, 0x00}, - {0x32d9, 0x7c}, - {0x32da, 0x79}, + {0x32d9, 0xa7}, + {0x32da, 0x91}, {0x32db, 0x0f}, - {0x32dc, 0xde}, - {0x32dd, 0x19}, + {0x32dc, 0xc6}, + {0x32dd, 0xd2}, {0x32de, 0x00}, - {0x32df, 0x19}, - {0x32e0, 0xe8}, + {0x32df, 0x3a}, + {0x32e0, 0x5e}, {0x32e1, 0x0f}, - {0x32e2, 0xf3}, - {0x32e3, 0x41}, + {0x32e2, 0xd6}, + {0x32e3, 0xe0}, {0x32e4, 0x00}, - {0x32e5, 0x03}, - {0x32e6, 0x4c}, + {0x32e5, 0x0f}, + {0x32e6, 0xa2}, {0x32e7, 0x00}, - {0x32e8, 0x05}, - {0x32e9, 0x73}, + {0x32e8, 0x0b}, + {0x32e9, 0x02}, {0x32ea, 0x0f}, - {0x32eb, 0xd6}, - {0x32ec, 0xa5}, + {0x32eb, 0xb3}, + {0x32ec, 0xdd}, {0x32ed, 0x00}, - {0x32ee, 0x1f}, - {0x32ef, 0x81}, + {0x32ee, 0x2f}, + {0x32ef, 0xa2}, {0x32f0, 0x0f}, - {0x32f1, 0xdc}, - {0x32f2, 0xe6}, + {0x32f1, 0xbb}, + {0x32f2, 0x1f}, {0x32f3, 0x00}, - {0x32f4, 0x18}, - {0x32f5, 0x65}, - {0x32f6, 0x00}, - {0x32f7, 0x00}, - {0x32f8, 0x11}, + {0x32f4, 0x38}, + {0x32f5, 0x09}, + {0x32f6, 0x0f}, + {0x32f7, 0xfc}, + {0x32f8, 0xc4}, {0x32f9, 0x0f}, - {0x32fa, 0xed}, - {0x32fb, 0x65}, + {0x32fa, 0xde}, + {0x32fb, 0x51}, {0x32fc, 0x00}, - {0x32fd, 0x23}, - {0x32fe, 0x12}, + {0x32fd, 0x3c}, + {0x32fe, 0xdb}, {0x32ff, 0x0f}, - {0x3300, 0xcf}, - {0x3301, 0x28}, + {0x3300, 0xc3}, + {0x3301, 0x2e}, {0x3302, 0x00}, - {0x3303, 0x2b}, - {0x3304, 0xda}, + {0x3303, 0x4a}, + {0x3304, 0x96}, {0x3305, 0x0f}, - {0x3306, 0xef}, - {0x3307, 0xae}, + {0x3306, 0xd7}, + {0x3307, 0x20}, {0x3308, 0x0f}, - {0x3309, 0xeb}, - {0x330a, 0x13}, + {0x3309, 0xe3}, + {0x330a, 0x64}, {0x330b, 0x00}, - {0x330c, 0x27}, - {0x330d, 0xb8}, + {0x330c, 0x3b}, + {0x330d, 0xde}, {0x330e, 0x0f}, - {0x330f, 0xec}, - {0x3310, 0x69}, + {0x330f, 0xe2}, + {0x3310, 0xb6}, {0x3311, 0x00}, - {0x3312, 0x2f}, - {0x3313, 0x5f}, + {0x3312, 0x29}, + {0x3313, 0xfd}, {0x3314, 0x0f}, - {0x3315, 0xdf}, - {0x3316, 0x4f}, + {0x3315, 0xd3}, + {0x3316, 0xee}, {0x3317, 0x00}, - {0x3318, 0x05}, - {0x3319, 0x70}, + {0x3318, 0x0c}, + {0x3319, 0x40}, {0x331a, 0x00}, - {0x331b, 0x0f}, - {0x331c, 0xd2}, + {0x331b, 0x1d}, + {0x331c, 0x96}, {0x331d, 0x0f}, - {0x331e, 0xe1}, - {0x331f, 0xd8}, + {0x331e, 0xd4}, + {0x331f, 0xd9}, {0x3320, 0x00}, - {0x3321, 0x09}, - {0x3322, 0xcf}, - {0x3323, 0x0f}, - {0x3324, 0xf2}, - {0x3325, 0x6e}, + {0x3321, 0x0e}, + {0x3322, 0xa8}, + {0x3323, 0x00}, + {0x3324, 0x02}, + {0x3325, 0xc6}, {0x3326, 0x0f}, - {0x3327, 0xf6}, - {0x3328, 0xb4}, + {0x3327, 0xf3}, + {0x3328, 0xc1}, {0x3329, 0x00}, - {0x332a, 0x0d}, - {0x332b, 0x87}, + {0x332a, 0x0f}, + {0x332b, 0xe2}, {0x332c, 0x00}, - {0x332d, 0x08}, - {0x332e, 0x1e}, + {0x332d, 0x03}, + {0x332e, 0x56}, {0x332f, 0x0f}, - {0x3330, 0xfa}, - {0x3331, 0x6e}, + {0x3330, 0xf4}, + {0x3331, 0xc0}, {0x3332, 0x0f}, - {0x3333, 0xff}, - {0x3334, 0xaa}, + {0x3333, 0xfe}, + {0x3334, 0xc5}, {0x3335, 0x0f}, - {0x3336, 0xf2}, - {0x3337, 0xc0}, + {0x3336, 0xe8}, + {0x3337, 0xb8}, {0x3338, 0x00}, - {0x3339, 0x1d}, - {0x333a, 0x18}, + {0x3339, 0x1e}, + {0x333a, 0xb0}, {0x333b, 0x0f}, - {0x333c, 0xef}, - {0x333d, 0xed}, + {0x333c, 0xf2}, + {0x333d, 0x01}, {0x333e, 0x0f}, - {0x333f, 0xec}, - {0x3340, 0xf6}, + {0x333f, 0xe4}, + {0x3340, 0x68}, {0x3341, 0x00}, - {0x3342, 0x16}, - {0x3343, 0x8e}, + {0x3342, 0x27}, + {0x3343, 0x00}, {0x3344, 0x00}, - {0x3345, 0x9c}, - {0x3346, 0x52}, + {0x3345, 0xc0}, + {0x3346, 0x46}, {0x3347, 0x0f}, - {0x3348, 0xcf}, - {0x3349, 0xb9}, + {0x3348, 0xbb}, + {0x3349, 0x8b}, {0x334a, 0x00}, - {0x334b, 0x29}, - {0x334c, 0xe9}, + {0x334b, 0x46}, + {0x334c, 0xea}, {0x334d, 0x0f}, - {0x334e, 0xe2}, - {0x334f, 0x83}, + {0x334e, 0xcc}, + {0x334f, 0xb7}, {0x3350, 0x00}, - {0x3351, 0x11}, - {0x3352, 0xcc}, - {0x3353, 0x0f}, - {0x3354, 0xff}, - {0x3355, 0xf4}, + {0x3351, 0x10}, + {0x3352, 0x01}, + {0x3353, 0x00}, + {0x3354, 0x13}, + {0x3355, 0xe1}, {0x3356, 0x0f}, - {0x3357, 0xc1}, - {0x3358, 0xa4}, + {0x3357, 0x9f}, + {0x3358, 0xff}, {0x3359, 0x00}, - {0x335a, 0x2f}, - {0x335b, 0xce}, + {0x335a, 0x3d}, + {0x335b, 0x6c}, {0x335c, 0x0f}, - {0x335d, 0xc5}, - {0x335e, 0xbb}, + {0x335d, 0xa7}, + {0x335e, 0x7b}, {0x335f, 0x00}, - {0x3360, 0x35}, - {0x3361, 0x2a}, + {0x3360, 0x4b}, + {0x3361, 0x91}, {0x3362, 0x0f}, - {0x3363, 0xe6}, - {0x3364, 0x2a}, + {0x3363, 0xfb}, + {0x3364, 0x99}, {0x3365, 0x0f}, - {0x3366, 0xf7}, - {0x3367, 0x44}, + {0x3366, 0xcc}, + {0x3367, 0x52}, {0x3368, 0x00}, - {0x3369, 0x31}, - {0x336a, 0xfe}, + {0x3369, 0x53}, + {0x336a, 0x00}, {0x336b, 0x0f}, - {0x336c, 0xb6}, - {0x336d, 0x84}, + {0x336c, 0xaa}, + {0x336d, 0xa2}, {0x336e, 0x00}, - {0x336f, 0x3c}, - {0x3370, 0x71}, + {0x336f, 0x64}, + {0x3370, 0xa2}, {0x3371, 0x0f}, - {0x3372, 0xe5}, - {0x3373, 0xfe}, + {0x3372, 0xbe}, + {0x3373, 0xc4}, {0x3374, 0x0f}, - {0x3375, 0xf2}, - {0x3376, 0x87}, + {0x3375, 0xe4}, + {0x3376, 0xbb}, {0x3377, 0x00}, - {0x3378, 0x29}, - {0x3379, 0x2b}, + {0x3378, 0x56}, + {0x3379, 0xd8}, {0x337a, 0x0f}, - {0x337b, 0xe5}, - {0x337c, 0x3f}, + {0x337b, 0xc8}, + {0x337c, 0xdc}, {0x337d, 0x00}, - {0x337e, 0x45}, - {0x337f, 0xc6}, + {0x337e, 0x44}, + {0x337f, 0xa7}, {0x3380, 0x0f}, - {0x3381, 0xdf}, - {0x3382, 0xe6}, - {0x3383, 0x0f}, - {0x3384, 0xfb}, - {0x3385, 0x0f}, + {0x3381, 0xbd}, + {0x3382, 0xca}, + {0x3383, 0x00}, + {0x3384, 0x29}, + {0x3385, 0xf7}, {0x3386, 0x00}, - {0x3387, 0x0f}, - {0x3388, 0xf4}, + {0x3387, 0x08}, + {0x3388, 0xf2}, {0x3389, 0x0f}, - {0x338a, 0xdf}, - {0x338b, 0x72}, + {0x338a, 0xc6}, + {0x338b, 0x1c}, {0x338c, 0x00}, - {0x338d, 0x0e}, - {0x338e, 0xaf}, + {0x338d, 0x28}, + {0x338e, 0x3b}, {0x338f, 0x0f}, - {0x3390, 0xed}, - {0x3391, 0x7a}, + {0x3390, 0xfc}, + {0x3391, 0x30}, {0x3392, 0x0f}, - {0x3393, 0xe5}, - {0x3394, 0xab}, + {0x3393, 0xee}, + {0x3394, 0x3e}, {0x3395, 0x00}, - {0x3396, 0x18}, - {0x3397, 0x43}, + {0x3396, 0x02}, + {0x3397, 0x32}, {0x3398, 0x00}, - {0x3399, 0x1b}, - {0x339a, 0x41}, + {0x3399, 0x25}, + {0x339a, 0xb6}, {0x339b, 0x0f}, - {0x339c, 0xea}, - {0x339d, 0x84}, + {0x339c, 0xe9}, + {0x339d, 0xd5}, {0x339e, 0x0f}, - {0x339f, 0xfd}, - {0x33a0, 0xdb}, + {0x339f, 0xf3}, + {0x33a0, 0x80}, {0x33a1, 0x0f}, - {0x33a2, 0xe9}, - {0x33a3, 0xbd}, + {0x33a2, 0xda}, + {0x33a3, 0x56}, {0x33a4, 0x00}, - {0x33a5, 0x30}, - {0x33a6, 0x77}, + {0x33a5, 0x3c}, + {0x33a6, 0x4a}, {0x33a7, 0x0f}, - {0x33a8, 0xe9}, - {0x33a9, 0x93}, + {0x33a8, 0xe0}, + {0x33a9, 0x9d}, {0x33aa, 0x0f}, - {0x33ab, 0xd7}, - {0x33ac, 0xde}, + {0x33ab, 0xd9}, + {0x33ac, 0x7d}, {0x33ad, 0x00}, - {0x33ae, 0x2a}, - {0x33af, 0x14}, + {0x33ae, 0x34}, + {0x33af, 0x54}, {0x309D, 0x62}, {0x309d, 0x22}, - -/* LC setting End */ - }, + {0x309e, 0x52}, + {0x309f, 0x3e}, + {0x30a0, 0x03}, + {0x30a1, 0x1f}, + {0x30a2, 0x04}, + {0x30a3, 0x21}, + {0x30a4, 0x04}, + {0x30a5, 0x00}, + {0x30a6, 0x0c}, + {0x30a7, 0x7c}, + {0x30a8, 0x04}, + {0x30a9, 0x00}, + {0x30aa, 0x10}, + {0x30ab, 0x84}, +}; +struct s5k3e2fx_i2c_reg_conf lc_setting_evt5[] = { /*EVT5 */ - { /* LC setting Start */ - {0x3200, 0x00}, /* 100108 Modify LC setting DNP light source t75-r73*/ + {0x3200, 0x00}, /* 100304 Modify LC setting DNP light source t75-r70 to improve reddish issue*/ {0x3201, 0x99}, {0x3202, 0xc1}, {0x3203, 0x0f}, @@ -1288,7 +1256,6 @@ struct s5k3e2fx_i2c_reg_conf lc_setting[2][NUM_LC_REG] = { {0x309D, 0x62}, {0x309d, 0x22}, /* shading enable */ /*LC setting End */ - } }; /* lc_setting} */ static struct wake_lock s5k3e2fx_wake_lock; @@ -1384,7 +1351,7 @@ struct reg_struct { uint8_t reg_30bd_reserved; /* 0x30BD */ uint8_t reg_30c2_reserved; /* 0x30C2 */ uint8_t shade_clk_enable; /* 0x30AC */ - uint8_t reg_3051_reserved; /* 0x3051 */ + uint8_t reg_3151_reserved; /* 0x3151 */ /* 100202 the right address is 0x3151 */ uint8_t reg_3029_reserved; /* 0x3029 */ uint8_t reg_30bf_reserved; /* 0x30BF */ uint8_t reg_3022_reserved; /* 0x3022 */ @@ -1401,7 +1368,8 @@ struct reg_struct { uint8_t analogue_gain_code_global_msb; /* 0x0204 */ uint8_t analogue_gain_code_global_lsb; /* 0x0205 */ uint8_t fine_integration_time; /* 0x0200 */ - uint8_t coarse_integration_time; /* 0x0202 */ + uint8_t coarse_integration_time_msb; /* 0x0202 */ + uint8_t coarse_integration_time_lsb; /* 0x0203 */ /* 100202 Add coarse_integration_time_lsb */ /* LC Preview/Snapshot difference register */ /* Preview LC Setting */ uint8_t sh4ch_blk_width_r; /* 0x309E */ @@ -1446,13 +1414,13 @@ struct reg_struct s5k3e2fx_reg_pat[2] = { 0xcc, /* y_output_size_lsb REG=0x034F */ /* X-Y addr setting position. Start */ 0x00, /* x_addr_start_MSB REG=0x0344 */ - 0x08, /* x_addr_start_LSB REG=0x0345 */ + 0x00, /* x_addr_start_LSB REG=0x0345 */ /* 100202 Change to 00 to the same as DesireC */ 0x00, /* y_addr_start_MSB REG=0x0346 */ - 0x08, /* y_addr_start_LSB REG=0x0347 */ + 0x00, /* y_addr_start_LSB REG=0x0347 */ /* 100202 Change to 00 to the same as DesireC */ 0x0a, /* x_addr_end_MSB REG=0x0348 */ - 0x27, /* x_addr_end_LSB REG=0x0349 */ + 0x2F, /* x_addr_end_LSB REG=0x0349 */ /* 100202 Change to 2F to the same as DesireC */ 0x07, /* y_addr_end_MSB REG=0x034A */ - 0x9f, /* y_addr_end_LSB REG=0x034B */ + 0xA7, /* y_addr_end_LSB REG=0x034B */ /* 100202 Change to A7 to the same as DesireC */ /* change the setting position */ /* Frame format */ 0x03, /* frame_length_lines_msb REG=0x0340 */ @@ -1498,7 +1466,7 @@ struct reg_struct s5k3e2fx_reg_pat[2] = { 0x06, /* reg_30bd_reserved REG=0x30BD */ 0x0b, /* reg_30c2_reserved REG=0x30C2 */ 0x81, /* shade_clk_enable REG=0x30AC */ - 0xe6, /* reg_3051_reserved REG=0x3051 */ + 0xe6, /* reg_3151_reserved REG=0x3151 */ /* 100202 the right address is 0x3151 */ 0x02, /* reg_3029_reserved REG=0x3029 */ 0x00, /* reg_30bf_reserved REG=0x30BF */ 0x87, /* reg_3022_reserved REG=0x3022 */ @@ -1522,7 +1490,8 @@ struct reg_struct s5k3e2fx_reg_pat[2] = { REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB_VALUE, /* analogue_gain_code_global_lsb REG=0x0205 */ 0x02, /* fine_integration_time REG=0x0200 */ - 0x03, /* coarse_integration_time REG=0x0202 */ + 0x03, /* coarse_integration_time_msb REG=0x0202 */ + 0x00, /* coarse_integration_time_lsb REG=0x0203 */ /* 100202 Add coarse_integration_time_lsb */ /* LC Preview/Snapshot difference register. */ /* Preview LC config Setting */ 0x52, /* sh4ch_blk_width_r REG=0x309E */ @@ -1616,7 +1585,7 @@ struct reg_struct s5k3e2fx_reg_pat[2] = { 0x06, /* reg_30bd_reserved REG=0x30BD */ 0x0b, /* reg_30c2_reserved REG=0x30C2 */ 0x81, /* shade_clk_enable REG=0x30AC */ - 0xe6, /* reg_3051_reserved REG=0x3051 */ + 0xe6, /* reg_3151_reserved REG=0x3151 */ /* 100202 the right address is 0x3151 */ 0x02, /* reg_3029_reserved REG=0x3029 */ 0x00, /* reg_30bf_reserved REG=0x30BF */ 0x87, /* reg_3022_reserved REG=0x3022 */ @@ -1641,7 +1610,8 @@ struct reg_struct s5k3e2fx_reg_pat[2] = { REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB_VALUE, /* analogue_gain_code_global_lsb REG=0x0205 */ 0x02, /* fine_integration_time REG=0x0200 */ - 0x03, /* coarse_integration_time REG=0x0202 */ + 0x03, /* coarse_integration_time_msb REG=0x0202 */ + 0x00, /* coarse_integration_time_lsb REG=0x0203 */ /* 100202 Add coarse_integration_time_lsb */ /* Add LC Preview/Snapshot diff register. */ /* Snapshot LC config Setting */ 0x52, /* sh4ch_blk_width_r REG=0x309E */ @@ -1690,7 +1660,17 @@ struct s5k3e2fx_ctrl { }; static struct s5k3e2fx_ctrl *s5k3e2fx_ctrl; +static struct platform_device *s5k3e2fx_pdev; + +struct s5k3e2fx_waitevent{ + uint32_t waked_up; + wait_queue_head_t event_wait; +}; +static struct s5k3e2fx_waitevent s5k3e2fx_event; + + static DECLARE_WAIT_QUEUE_HEAD(s5k3e2fx_wait_queue); +DEFINE_SEMAPHORE(s5k3e2fx_sem); #define MAX_I2C_RETRIES 20 static int i2c_transfer_retry(struct i2c_adapter *adap, @@ -1946,9 +1926,20 @@ static int s5k3e2fx_i2c_probe(struct i2c_client *client, return rc; } +static int __exit s5k3e2fx_i2c_remove(struct i2c_client *client) +{ + struct s5k3e2fx_work_t *sensorw = i2c_get_clientdata(client); + free_irq(client->irq, sensorw); + deinit_suspend(); + s5k3e2fx_client = NULL; + kfree(sensorw); + return 0; +} + static struct i2c_driver s5k3e2fx_i2c_driver = { .id_table = s5k3e2fx_i2c_id, .probe = s5k3e2fx_i2c_probe, + .remove = __exit_p(s5k3e2fx_i2c_remove), .driver = { .name = "s5k3e2fx", }, @@ -1968,159 +1959,422 @@ static int s5k3e2fx_test(enum msm_s_test_mode mo) return rc; } #endif -static int s5k3e2fx_setting(enum msm_s_reg_update rupdate, - enum msm_s_setting rt) + +static int s5k3e2fx_setting_INIT_EVT4(void) { int rc = 0; - uint16_t num_lperf; + struct s5k3e2fx_i2c_reg_conf EVT4_INIT[] = { + /*pclk setting*/ + {0x0305, 0x06}, + {0x0306, 0x00}, + {0x0307, 0x83}, + {0x0301, 0x08}, + {0x0303, 0x01}, + {0x0309, 0x08}, + {0x030b, 0x01}, + /*output size*/ + {0x034c, 0x05}, + {0x034d, 0x10}, + {0x034e, 0x03}, + {0x034f, 0xcc}, + /*frame format (min blanking)*/ + {0x0340, 0x03}, + {0x0341, 0xe2}, + {0x0342, 0x0a}, + {0x0343, 0xac}, + /*Binning */ + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x03}, + {0x3014, 0x06}, + /*MSR setting*/ + {0x30c4, 0x01}, + {0x3000, 0x03}, + {0x3001, 0x94}, + {0x3002, 0x02}, + {0x3003, 0x95}, + {0x3004, 0x0f}, + {0x3005, 0x05}, + {0x3006, 0x3c}, + {0x3007, 0x8c}, + {0x3008, 0x93}, + {0x3009, 0x05}, + {0x300a, 0x3a}, + {0x300c, 0x02}, + {0x300d, 0x3e}, + {0x300f, 0x0e}, + {0x3010, 0x46}, + {0x3011, 0x64}, + {0x3012, 0x1e}, + {0x301d, 0x3f}, + {0x3024, 0x04}, + {0x3028, 0x40}, + {0x3070, 0xdf}, + {0x301b, 0x73}, + {0x307e, 0x02}, + {0x30bd, 0x06}, + {0x30c2, 0x0b}, + {0x30ac, 0x81}, + {0x3151, 0xe6}, + {0x3029, 0x02}, + {0x30bf, 0x00}, + {0x3022, 0x87}, + /*tune ADC to got batter yield rate in EDS*/ + {0x3019, 0x60}, + /*AF driving strength*/ + {0x3146, 0x3c}, + {0x3152, 0x08}, + /*data pclk driving strength*/ + {0x315a, 0x7f}, + /*h sync v sync driving strength*/ + {0x3159, 0x0f}, + {0x3157, 0x03}, + {0x0204, 0x00}, + {0x0205, 0x80}, + {0x0202, 0x03}, + {0x0203, 0xd9}, + {0x0200, 0x02}, + {0x3130, 0x03}, + {0x0100, 0x01}, + }; - switch (rupdate) { - case S_UPDATE_PERIODIC:{ - if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) { - struct s5k3e2fx_i2c_reg_conf tbl_1[] = { - {REG_X_OUTPUT_SIZE_MSB, - s5k3e2fx_reg_pat[rt]. - x_output_size_msb}, - {REG_X_OUTPUT_SIZE_LSB, - s5k3e2fx_reg_pat[rt]. - x_output_size_lsb}, - {REG_Y_OUTPUT_SIZE_MSB, - s5k3e2fx_reg_pat[rt]. - y_output_size_msb}, - {REG_Y_OUTPUT_SIZE_LSB, - s5k3e2fx_reg_pat[rt]. - y_output_size_lsb}, - /* Start-End address */ - {REG_X_ADDR_START_MSB, - s5k3e2fx_reg_pat[rt].x_addr_start_MSB}, - {REG_X_ADDR_START_LSB, - s5k3e2fx_reg_pat[rt].x_addr_start_LSB}, - {REG_Y_ADDR_START_MSB, - s5k3e2fx_reg_pat[rt].y_addr_start_MSB}, - {REG_Y_ADDR_START_LSB, - s5k3e2fx_reg_pat[rt].y_addr_start_LSB}, - {REG_X_ADDR_END_MSB, - s5k3e2fx_reg_pat[rt].x_addr_end_MSB}, - {REG_X_ADDR_END_LSB, - s5k3e2fx_reg_pat[rt].x_addr_end_LSB}, - {REG_Y_ADDR_END_MSB, - s5k3e2fx_reg_pat[rt].y_addr_end_MSB}, - {REG_Y_ADDR_END_LSB, - s5k3e2fx_reg_pat[rt].y_addr_end_LSB}, - /* Binning */ - {REG_X_EVEN_INC, - s5k3e2fx_reg_pat[rt].x_even_inc}, - {REG_X_ODD_INC, - s5k3e2fx_reg_pat[rt].x_odd_inc}, - {REG_Y_EVEN_INC, - s5k3e2fx_reg_pat[rt].y_even_inc}, - {REG_Y_ODD_INC, - s5k3e2fx_reg_pat[rt].y_odd_inc}, - {REG_BINNING_ENABLE, - s5k3e2fx_reg_pat[rt].binning_enable}, - }; - struct s5k3e2fx_i2c_reg_conf tbl_2[] = { - {REG_FRAME_LENGTH_LINES_MSB, 0}, - {REG_FRAME_LENGTH_LINES_LSB, 0}, - {REG_LINE_LENGTH_PCK_MSB, - s5k3e2fx_reg_pat[rt]. - line_length_pck_msb}, - {REG_LINE_LENGTH_PCK_LSB, - s5k3e2fx_reg_pat[rt]. - line_length_pck_lsb}, - {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, - s5k3e2fx_reg_pat[rt]. - analogue_gain_code_global_msb}, - {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, - s5k3e2fx_reg_pat[rt]. - analogue_gain_code_global_lsb}, - {REG_FINE_INTEGRATION_TIME, - s5k3e2fx_reg_pat[rt]. - fine_integration_time}, - {REG_COARSE_INTEGRATION_TIME, - s5k3e2fx_reg_pat[rt]. - coarse_integration_time}, - /* LC Preview/Snapshot difference - * register + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0100, 0x00); + msleep(30); + /* write REG_INIT registers */ + rc = s5k3e2fx_i2c_write_table(&EVT4_INIT[0], + ARRAY_SIZE(EVT4_INIT)); + return rc; +} + +static int s5k3e2fx_setting_INIT_EVT5(enum msm_s_setting rt) +{ + int rc = 0; + struct s5k3e2fx_i2c_reg_conf EVT5_INIT[] = { + {S5K3E2FX_REG_MODE_SELECT, + S5K3E2FX_MODE_SELECT_SW_STANDBY}, + /*Output Size */ + {REG_X_OUTPUT_SIZE_MSB, + s5k3e2fx_reg_pat[rt].x_output_size_msb}, + {REG_X_OUTPUT_SIZE_LSB, + s5k3e2fx_reg_pat[rt].x_output_size_lsb}, + {REG_Y_OUTPUT_SIZE_MSB, + s5k3e2fx_reg_pat[rt].y_output_size_msb}, + {REG_Y_OUTPUT_SIZE_LSB, + s5k3e2fx_reg_pat[rt].y_output_size_lsb}, + /* Start-End address */ +/* 100202 Modify X_ADDR and Y_ADDR Start-end is the same between preview and snapshot like DesireC + {REG_X_ADDR_START_MSB, + s5k3e2fx_reg_pat[rt].x_addr_start_MSB}, + {REG_X_ADDR_START_LSB, + s5k3e2fx_reg_pat[rt].x_addr_start_LSB}, + {REG_Y_ADDR_START_MSB, + s5k3e2fx_reg_pat[rt].y_addr_start_MSB}, + {REG_Y_ADDR_START_LSB, + s5k3e2fx_reg_pat[rt].y_addr_start_LSB}, + {REG_X_ADDR_END_MSB, + s5k3e2fx_reg_pat[rt].x_addr_end_MSB}, + {REG_X_ADDR_END_LSB, + s5k3e2fx_reg_pat[rt].x_addr_end_LSB}, + {REG_Y_ADDR_END_MSB, + s5k3e2fx_reg_pat[rt].y_addr_end_MSB}, + {REG_Y_ADDR_END_LSB, + s5k3e2fx_reg_pat[rt].y_addr_end_LSB}, +*/ + /* Binning */ + {REG_X_EVEN_INC, + s5k3e2fx_reg_pat[rt].x_even_inc}, + {REG_X_ODD_INC, + s5k3e2fx_reg_pat[rt].x_odd_inc}, + {REG_Y_EVEN_INC, + s5k3e2fx_reg_pat[rt].y_even_inc}, + {REG_Y_ODD_INC, + s5k3e2fx_reg_pat[rt].y_odd_inc}, + {REG_BINNING_ENABLE, + s5k3e2fx_reg_pat[rt].binning_enable}, + /* Frame format */ + {REG_FRAME_LENGTH_LINES_MSB, + s5k3e2fx_reg_pat[rt].frame_length_lines_msb}, + {REG_FRAME_LENGTH_LINES_LSB, + s5k3e2fx_reg_pat[rt].frame_length_lines_lsb}, +/* 100202 Remove the AEC setting in EVT5_INIT + {REG_LINE_LENGTH_PCK_MSB, + s5k3e2fx_reg_pat[rt].line_length_pck_msb}, + {REG_LINE_LENGTH_PCK_LSB, + s5k3e2fx_reg_pat[rt].line_length_pck_lsb}, +*/ + /* MSR setting */ +/* 100202 Remove the AEC and the same value setting in EVT5_INIT + {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, + s5k3e2fx_reg_pat[rt].analogue_gain_code_global_msb}, + {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, + s5k3e2fx_reg_pat[rt].analogue_gain_code_global_lsb}, + {REG_FINE_INTEGRATION_TIME, + s5k3e2fx_reg_pat[rt].fine_integration_time}, + {REG_COARSE_INTEGRATION_TIME, + s5k3e2fx_reg_pat[rt].coarse_integration_time_msb}, + {REG_COARSE_INTEGRATION_TIME_LSB, + s5k3e2fx_reg_pat[rt].coarse_integration_time_lsb}, +*/ + {S5K3E2FX_REG_MODE_SELECT, + S5K3E2FX_MODE_SELECT_STREAM}, + }; + /*write table*/ + rc = s5k3e2fx_i2c_write_table(&EVT5_INIT[0], ARRAY_SIZE(EVT5_INIT)); + if (rc < 0) { + pr_err("REG_INIT failed, rc=%d\n", rc); + return rc; + } + return rc; +} + + +static int s5k3e2fx_setting_PREIODIC_EVT4(enum msm_s_setting rt) +{ + int rc = 0; + struct s5k3e2fx_i2c_reg_conf EVT4_1[] = { + /*output size*/ + {0x034c, 0x05}, + {0x034d, 0x10}, + {0x034e, 0x03}, + {0x034f, 0xcc}, + /*frame format (min blanking)*/ + {0x0340, 0x03}, + {0x0341, 0xe2}, + {0x0342, 0x0a}, + {0x0343, 0xac}, + /*Binning*/ + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x03}, + {0x3014, 0x06}, + {0x30bf, 0x00}, + {0x3022, 0x87}, + /*tune ADC to got batter yield rate in EDS*/ + {0x3019, 0x60}, + /*AF driving strength*/ + {0x3146, 0x3c}, + {0x3152, 0x08}, + /*data pclk driving strength*/ + {0x315a, 0x7f}, + {0x3159, 0x0f}, + /*h sync v sync driving strength*/ + {0x3157, 0x03}, + {0x0204, 0x00}, + {0x0205, 0x80}, + {0x0202, 0x03}, + {0x0203, 0xd9}, + {0x0200, 0x02}, + {0x3130, 0x03}, + /*lens shading setting for preview*/ + {0x309e, 0x52}, + {0x309f, 0x3e}, + {0x30a0, 0x03}, + {0x30a1, 0x1f}, + {0x30a2, 0x04}, + {0x30a3, 0x21}, + {0x30a4, 0x04}, + {0x30a5, 0x00}, + {0x30a6, 0x0c}, + {0x30a7, 0x7c}, + {0x30a8, 0x04}, + {0x30a9, 0x00}, + {0x30aa, 0x10}, + {0x30ab, 0x84}, + /*streaimg on*/ + {0x0100, 0x01}, + }; + struct s5k3e2fx_i2c_reg_conf EVT4_2[] = { + /*output size*/ + {0x034c, 0x0a}, + {0x034d, 0x30}, + {0x034e, 0x07}, + {0x034f, 0xa8}, + /*frame format (min blanking)*/ + {0x0340, 0x07}, + {0x0341, 0xb6}, + {0x0342, 0x0a}, + {0x0343, 0xac}, + /*Binning*/ + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x01}, + {0x3014, 0x00}, + {0x30bf, 0x00}, + {0x3022, 0x87}, + /*tune ADC to got batter yield rate in EDS*/ + {0x3019, 0x60}, + /*AF driving strength*/ + {0x3146, 0x3c}, + {0x3152, 0x08}, + /*data pclk driving strength*/ + {0x315a, 0x7f}, + /*h sync v sync driving strength*/ + {0x3159, 0x0f}, + {0x3157, 0x03}, + {0x0204, 0x00}, + {0x0205, 0x80}, + {0x0202, 0x03}, + {0x0203, 0xd9}, + {0x0200, 0x02}, + {0x3130, 0x03}, + /*lens shading setting for snapshot*/ + {0x309e, 0x52}, + {0x309f, 0x7b}, + {0x30a0, 0x03}, + {0x30a1, 0x1f}, + {0x30a2, 0x02}, + {0x30a3, 0x15}, + {0x30a4, 0x00}, + {0x30a5, 0x00}, + {0x30a6, 0x00}, + {0x30a7, 0x00}, + {0x30a8, 0x00}, + {0x30a9, 0x00}, + {0x30aa, 0x00}, + {0x30ab, 0x00}, + /*streaming on*/ + {0x0100, 0x01}, + }; + struct s5k3e2fx_i2c_reg_conf EVT4_PCLK[] = { + /*pclk setting*/ + {0x0305, 0x06}, + {0x0306, 0x00}, + {0x0307, 0x83}, + {0x0301, 0x08}, + {0x0303, 0x01}, + {0x0309, 0x08}, + {0x030b, 0x01}, + }; + /*streaming off*/ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0100, 0x00); + msleep(30); + /*pclk setting*/ + rc = s5k3e2fx_i2c_write_table(&EVT4_PCLK[0], ARRAY_SIZE(EVT4_PCLK)); + /*write table*/ + if (rt == 0) + rc = s5k3e2fx_i2c_write_table(&EVT4_1[0], ARRAY_SIZE(EVT4_1)); + else + rc = s5k3e2fx_i2c_write_table(&EVT4_2[0], ARRAY_SIZE(EVT4_2)); + return rc; +} + + +static int s5k3e2fc_setting_PREIODIC_EVT5(enum msm_s_setting rt) +{ + int rc = 0; + uint16_t num_lperf; + struct s5k3e2fx_i2c_reg_conf tbl_1[] = { + /* skip doing streaming off + {S5K3E2FX_REG_MODE_SELECT, + S5K3E2FX_MODE_SELECT_SW_STANDBY}, */ - {REG_SH4CH_BLK_WIDTH_R, - s5k3e2fx_reg_pat[rt]. - sh4ch_blk_width_r}, - {REG_SH4CH_BLK_HEIGHT_R, - s5k3e2fx_reg_pat[rt]. - sh4ch_blk_height_r}, - {REG_SH4CH_STEP_X_R_MSB, - s5k3e2fx_reg_pat[rt]. - sh4ch_step_x_r_MSB}, - {REG_SH4CH_STEP_X_R_LSB, - s5k3e2fx_reg_pat[rt]. - sh4ch_step_x_r_LSB}, - {REG_SH4CH_STEP_Y_R_MSB, - s5k3e2fx_reg_pat[rt]. - sh4ch_step_y_r_MSB}, - {REG_SH4CH_STEP_Y_R_LSB, - s5k3e2fx_reg_pat[rt]. - sh4ch_step_y_r_LSB}, - {REG_SH4CH_START_BLK_CNT_X_R, - s5k3e2fx_reg_pat[rt]. - sh4ch_start_blk_cnt_x_r}, - {REG_SH4CH_START_BLK_INT_X_R, - s5k3e2fx_reg_pat[rt]. - sh4ch_start_blk_int_x_r}, - {REG_SH4CH_START_FRAC_X_R_MSB, - s5k3e2fx_reg_pat[rt]. - sh4ch_start_frac_x_r_MSB}, - {REG_SH4CH_START_FRAC_X_R_LSB, - s5k3e2fx_reg_pat[rt]. - sh4ch_start_frac_x_r_LSB}, - {REG_SH4CH_START_BLK_CNT_Y_R, - s5k3e2fx_reg_pat[rt]. - sh4ch_start_blk_cnt_y_r}, - {REG_SH4CH_START_BLK_INT_Y_R, - s5k3e2fx_reg_pat[rt]. - sh4ch_start_blk_int_y_r}, - {REG_SH4CH_START_FRAC_Y_R_MSB, - s5k3e2fx_reg_pat[rt]. - sh4ch_start_frac_y_r_MSB}, - {REG_SH4CH_START_FRAC_Y_R_LSB, - s5k3e2fx_reg_pat[rt]. - sh4ch_start_frac_y_r_LSB}, - }; - -/* add EVT5 sensor Samsung difference MSR setting between Preview and Capture */ - - struct s5k3e2fx_i2c_reg_conf - tbl_only_for_EVT5[2][2] = { - { /* S_RES_PREVIEW */ - {0x3062, 0x00}, - {0x3063, 0xD6}, - }, - { /* S_RES_CAPTURE */ - {0x3062, 0x01}, - {0x3063, 0x16}, - } - }; - - /* Most registers are directly applied at next frame after - writing except shutter and analog gain. Shutter and gain are - applied at 2nd or 1st frame later depending on register - writing time. When the camera is switched from preview to - snapshot, the first frame may have wrong shutter/gain and - should be discarded. The register REG_MASK_CORRUPTED_FRAMES - can discard the frame that has wrong shutter/gain. But in - preview mode, the frames should not be dropped. Otherwise - the preview will not be smooth. */ - if (rt == S_RES_PREVIEW) { - /* Frames will be not discarded after exposure and gain are - written. */ - s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, - REG_MASK_CORRUPTED_FRAMES, NO_MASK); - } else { - /* Solve greenish in lowlight. Prevent corrupted frame */ - s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, - REG_MASK_CORRUPTED_FRAMES, MASK); - } + {REG_X_OUTPUT_SIZE_MSB, + s5k3e2fx_reg_pat[rt].x_output_size_msb}, + {REG_X_OUTPUT_SIZE_LSB, + s5k3e2fx_reg_pat[rt].x_output_size_lsb}, + {REG_Y_OUTPUT_SIZE_MSB, + s5k3e2fx_reg_pat[rt].y_output_size_msb}, + {REG_Y_OUTPUT_SIZE_LSB, + s5k3e2fx_reg_pat[rt].y_output_size_lsb}, + /* Start-End address */ +/* 100202 Modify X_ADDR and Y_ADDR Start-end is the same between preview and snapshot like DesireC + {REG_X_ADDR_START_MSB, + s5k3e2fx_reg_pat[rt].x_addr_start_MSB}, + {REG_X_ADDR_START_LSB, + s5k3e2fx_reg_pat[rt].x_addr_start_LSB}, + {REG_Y_ADDR_START_MSB, + s5k3e2fx_reg_pat[rt].y_addr_start_MSB}, + {REG_Y_ADDR_START_LSB, + s5k3e2fx_reg_pat[rt].y_addr_start_LSB}, + {REG_X_ADDR_END_MSB, + s5k3e2fx_reg_pat[rt].x_addr_end_MSB}, + {REG_X_ADDR_END_LSB, + s5k3e2fx_reg_pat[rt].x_addr_end_LSB}, + {REG_Y_ADDR_END_MSB, + s5k3e2fx_reg_pat[rt].y_addr_end_MSB}, + {REG_Y_ADDR_END_LSB, + s5k3e2fx_reg_pat[rt].y_addr_end_LSB}, +*/ + /* Binning */ + {REG_X_EVEN_INC, + s5k3e2fx_reg_pat[rt].x_even_inc}, + {REG_X_ODD_INC, + s5k3e2fx_reg_pat[rt].x_odd_inc}, + {REG_Y_EVEN_INC, + s5k3e2fx_reg_pat[rt].y_even_inc}, + {REG_Y_ODD_INC, + s5k3e2fx_reg_pat[rt].y_odd_inc}, + {REG_BINNING_ENABLE, + s5k3e2fx_reg_pat[rt].binning_enable}, + }; + struct s5k3e2fx_i2c_reg_conf tbl_2[] = { + {REG_FRAME_LENGTH_LINES_MSB, 0}, + {REG_FRAME_LENGTH_LINES_LSB, 0}, +/* 100202 Remove the AEC setting in s5k3e2fc_setting_PREIODIC_EVT5 + {REG_LINE_LENGTH_PCK_MSB, + s5k3e2fx_reg_pat[rt].line_length_pck_msb}, + {REG_LINE_LENGTH_PCK_LSB, + s5k3e2fx_reg_pat[rt].line_length_pck_lsb}, + {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, + s5k3e2fx_reg_pat[rt].analogue_gain_code_global_msb}, + {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, + s5k3e2fx_reg_pat[rt].analogue_gain_code_global_lsb}, + {REG_FINE_INTEGRATION_TIME, + s5k3e2fx_reg_pat[rt].fine_integration_time}, + {REG_COARSE_INTEGRATION_TIME, + s5k3e2fx_reg_pat[rt].coarse_integration_time_msb}, + {REG_COARSE_INTEGRATION_TIME_LSB, + s5k3e2fx_reg_pat[rt].coarse_integration_time_lsb}, +*/ + /* LC Preview/Snapshot difference register*/ + {REG_SH4CH_BLK_WIDTH_R, + s5k3e2fx_reg_pat[rt].sh4ch_blk_width_r}, + {REG_SH4CH_BLK_HEIGHT_R, + s5k3e2fx_reg_pat[rt].sh4ch_blk_height_r}, + {REG_SH4CH_STEP_X_R_MSB, + s5k3e2fx_reg_pat[rt].sh4ch_step_x_r_MSB}, + {REG_SH4CH_STEP_X_R_LSB, + s5k3e2fx_reg_pat[rt].sh4ch_step_x_r_LSB}, + {REG_SH4CH_STEP_Y_R_MSB, + s5k3e2fx_reg_pat[rt].sh4ch_step_y_r_MSB}, + {REG_SH4CH_STEP_Y_R_LSB, + s5k3e2fx_reg_pat[rt].sh4ch_step_y_r_LSB}, + {REG_SH4CH_START_BLK_CNT_X_R, + s5k3e2fx_reg_pat[rt].sh4ch_start_blk_cnt_x_r}, + {REG_SH4CH_START_BLK_INT_X_R, + s5k3e2fx_reg_pat[rt].sh4ch_start_blk_int_x_r}, + {REG_SH4CH_START_FRAC_X_R_MSB, + s5k3e2fx_reg_pat[rt].sh4ch_start_frac_x_r_MSB}, + {REG_SH4CH_START_FRAC_X_R_LSB, + s5k3e2fx_reg_pat[rt].sh4ch_start_frac_x_r_LSB}, + {REG_SH4CH_START_BLK_CNT_Y_R, + s5k3e2fx_reg_pat[rt].sh4ch_start_blk_cnt_y_r}, + {REG_SH4CH_START_BLK_INT_Y_R, + s5k3e2fx_reg_pat[rt].sh4ch_start_blk_int_y_r}, + {REG_SH4CH_START_FRAC_Y_R_MSB, + s5k3e2fx_reg_pat[rt].sh4ch_start_frac_y_r_MSB}, + {REG_SH4CH_START_FRAC_Y_R_LSB, + s5k3e2fx_reg_pat[rt].sh4ch_start_frac_y_r_LSB}, + }; + /* add EVT5 sensor Samsung difference + * MSR setting between Preview and Capture + */ + + struct s5k3e2fx_i2c_reg_conf + tbl_only_for_EVT5[2][2] = { + { /* S_RES_PREVIEW */ + {0x3062, 0x00}, + {0x3063, 0xD6}, + }, + { /* S_RES_CAPTURE */ + {0x3062, 0x01}, + {0x3063, 0x16}, + } + }; /* solve greenish: hold for both */ rc = s5k3e2fx_i2c_write_b( s5k3e2fx_client->addr, @@ -2129,183 +2383,102 @@ static int s5k3e2fx_setting(enum msm_s_reg_update rupdate, if (rc < 0) return rc; - CDBG("Binning_enable = 0x %2x" - "[s5k3e2fx.c s5k3e2fx_setting]\r\n", - s5k3e2fx_reg_pat[rt].binning_enable); + CDBG("Binning_enable = 0x %2x" + "[s5k3e2fx.c s5k3e2fx_setting]\r\n", + s5k3e2fx_reg_pat[rt].binning_enable); + rc = s5k3e2fx_i2c_write_table(&tbl_1[0], ARRAY_SIZE(tbl_1)); + if (rc < 0) { + pr_err("UPDATE_PERIODIC, tb1_1 failed"); + return rc; + } - rc = s5k3e2fx_i2c_write_table(&tbl_1[0], - ARRAY_SIZE - (tbl_1)); - if (rc < 0) { - pr_err("UPDATE_PERIODIC, tb1_1 failed"); - return rc; - } - - num_lperf = - (uint16_t) ((s5k3e2fx_reg_pat[rt]. - frame_length_lines_msb - << 8) & 0xFF00) + - s5k3e2fx_reg_pat[rt]. - frame_length_lines_lsb; - - num_lperf = - num_lperf * - s5k3e2fx_ctrl->fps_divider / 0x0400; - - tbl_2[0] = - (struct s5k3e2fx_i2c_reg_conf) { - REG_FRAME_LENGTH_LINES_MSB, - (num_lperf & 0xFF00) >> 8}; - tbl_2[1] = - (struct s5k3e2fx_i2c_reg_conf) { - REG_FRAME_LENGTH_LINES_LSB, - (num_lperf & 0x00FF)}; - - rc = s5k3e2fx_i2c_write_table(&tbl_2[0], - ARRAY_SIZE - (tbl_2)); - if (rc < 0) { - pr_err("UPDATE_PERIODIC, tb1_2 failed"); - return rc; - } - - /* only for evt5 */ - if (g_usModuleVersion == 1) { - rc = s5k3e2fx_i2c_write_table - (&tbl_only_for_EVT5[rt][0], - 2); - if (rc < 0) - return rc; - } - - /* solve greenish: only release for preview */ - if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) - { - rc = s5k3e2fx_i2c_write_b( - s5k3e2fx_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_UPDATE); - if (rc < 0) - return rc; - } - - rc = s5k3e2fx_i2c_write_b - (s5k3e2fx_client->addr, - S5K3E2FX_REG_MODE_SELECT, - S5K3E2FX_MODE_SELECT_STREAM); - if (rc < 0) - return rc; - } - break; /* UPDATE_PERIODIC */ + /* only for evt5 */ + if (g_usModuleVersion == 1) { + rc = s5k3e2fx_i2c_write_table(&tbl_only_for_EVT5[rt][0], 2); + if (rc < 0) + return rc; + } + + num_lperf = (uint16_t) ((s5k3e2fx_reg_pat[rt].frame_length_lines_msb + << 8) & 0xFF00) + s5k3e2fx_reg_pat[rt].frame_length_lines_lsb; + + num_lperf = num_lperf * s5k3e2fx_ctrl->fps_divider / 0x0400; + + tbl_2[0] = (struct s5k3e2fx_i2c_reg_conf) {REG_FRAME_LENGTH_LINES_MSB, + (num_lperf & 0xFF00) >> 8}; + tbl_2[1] = (struct s5k3e2fx_i2c_reg_conf) {REG_FRAME_LENGTH_LINES_LSB, + (num_lperf & 0x00FF)}; + + rc = s5k3e2fx_i2c_write_table(&tbl_2[0], ARRAY_SIZE(tbl_2)); + if (rc < 0) { + pr_err("UPDATE_PERIODIC, tb1_2 failed"); + return rc; + } + +/* solve greenish: only release for preview */ + if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) + { + rc = s5k3e2fx_i2c_write_b( + s5k3e2fx_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) + return rc; + } + + + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + S5K3E2FX_REG_MODE_SELECT, + S5K3E2FX_MODE_SELECT_STREAM); + if (rc < 0) + return rc; + + return 0; +} + + +static int s5k3e2fx_setting(enum msm_s_reg_update rupdate, + enum msm_s_setting rt) +{ + int rc = 0; + pr_info("s5k3e2fx_setting rupdate:%d g_usModuleVersion:%d\n", + rupdate, g_usModuleVersion); + switch (rupdate) { + case S_UPDATE_PERIODIC:{ + if (g_usModuleVersion == 1) + rc = s5k3e2fc_setting_PREIODIC_EVT5(rt); + else + rc = s5k3e2fx_setting_PREIODIC_EVT4(rt); } + break; /* UPDATE_PERIODIC */ + case S_REG_INIT:{ + if (g_usModuleVersion == 1) { + /*EVT5*/ if (rt == S_RES_PREVIEW || rt == S_RES_CAPTURE) { - struct s5k3e2fx_i2c_reg_conf tbl_3[] = { -/* {S5K3E2FX_REG_SOFTWARE_RESET, S5K3E2FX_SOFTWARE_RESET},*/ - {S5K3E2FX_REG_MODE_SELECT, - S5K3E2FX_MODE_SELECT_SW_STANDBY}, - /*Output Size */ - {REG_X_OUTPUT_SIZE_MSB, - s5k3e2fx_reg_pat[rt]. - x_output_size_msb}, - {REG_X_OUTPUT_SIZE_LSB, - s5k3e2fx_reg_pat[rt]. - x_output_size_lsb}, - {REG_Y_OUTPUT_SIZE_MSB, - s5k3e2fx_reg_pat[rt]. - y_output_size_msb}, - {REG_Y_OUTPUT_SIZE_LSB, - s5k3e2fx_reg_pat[rt]. - y_output_size_lsb}, - /* Start-End address */ - {REG_X_ADDR_START_MSB, - s5k3e2fx_reg_pat[rt].x_addr_start_MSB}, - {REG_X_ADDR_START_LSB, - s5k3e2fx_reg_pat[rt].x_addr_start_LSB}, - {REG_Y_ADDR_START_MSB, - s5k3e2fx_reg_pat[rt].y_addr_start_MSB}, - {REG_Y_ADDR_START_LSB, - s5k3e2fx_reg_pat[rt].y_addr_start_LSB}, - {REG_X_ADDR_END_MSB, - s5k3e2fx_reg_pat[rt].x_addr_end_MSB}, - {REG_X_ADDR_END_LSB, - s5k3e2fx_reg_pat[rt].x_addr_end_LSB}, - {REG_Y_ADDR_END_MSB, - s5k3e2fx_reg_pat[rt].y_addr_end_MSB}, - {REG_Y_ADDR_END_LSB, - s5k3e2fx_reg_pat[rt].y_addr_end_LSB}, - /* Binning */ - {REG_X_EVEN_INC, - s5k3e2fx_reg_pat[rt].x_even_inc}, - {REG_X_ODD_INC, - s5k3e2fx_reg_pat[rt].x_odd_inc}, - {REG_Y_EVEN_INC, - s5k3e2fx_reg_pat[rt].y_even_inc}, - {REG_Y_ODD_INC, - s5k3e2fx_reg_pat[rt].y_odd_inc}, - {REG_BINNING_ENABLE, - s5k3e2fx_reg_pat[rt].binning_enable}, - /* Frame format */ - {REG_FRAME_LENGTH_LINES_MSB, - s5k3e2fx_reg_pat[rt]. - frame_length_lines_msb}, - {REG_FRAME_LENGTH_LINES_LSB, - s5k3e2fx_reg_pat[rt]. - frame_length_lines_lsb}, - {REG_LINE_LENGTH_PCK_MSB, - s5k3e2fx_reg_pat[rt]. - line_length_pck_msb}, - {REG_LINE_LENGTH_PCK_LSB, - s5k3e2fx_reg_pat[rt]. - line_length_pck_lsb}, - /* MSR setting */ - {REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB, - s5k3e2fx_reg_pat[rt]. - analogue_gain_code_global_msb}, - {REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB, - s5k3e2fx_reg_pat[rt]. - analogue_gain_code_global_lsb}, - {REG_FINE_INTEGRATION_TIME, - s5k3e2fx_reg_pat[rt]. - fine_integration_time}, - {REG_COARSE_INTEGRATION_TIME, - s5k3e2fx_reg_pat[rt]. - coarse_integration_time}, - {S5K3E2FX_REG_MODE_SELECT, - S5K3E2FX_MODE_SELECT_STREAM}, - }; unsigned short rData = 0; mdelay(1); - s5k3e2fx_i2c_read_b(s5k3e2fx_client-> - addr, - REG_3150_RESERVED, - &rData); - s5k3e2fx_i2c_write_b(s5k3e2fx_client-> - addr, - REG_3150_RESERVED, - (rData & 0xFFFE)); + s5k3e2fx_i2c_read_b(s5k3e2fx_client->addr, + REG_3150_RESERVED, &rData); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_3150_RESERVED, (rData & 0xFFFE)); mdelay(1); - s5k3e2fx_i2c_read_b(s5k3e2fx_client-> - addr, - REG_TYPE1_AF_ENABLE, - &rData); - s5k3e2fx_i2c_write_b(s5k3e2fx_client-> - addr, - REG_TYPE1_AF_ENABLE, - (rData | 0x0001)); + s5k3e2fx_i2c_read_b(s5k3e2fx_client->addr, + REG_TYPE1_AF_ENABLE, &rData); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_TYPE1_AF_ENABLE, (rData | 0x0001)); mdelay(1); - /* reset fps_divider */ s5k3e2fx_ctrl->fps_divider = 1 * 0x0400; /* write REG_INIT registers */ - rc = s5k3e2fx_i2c_write_table(&tbl_3[0], - ARRAY_SIZE - (tbl_3)); - if (rc < 0) { - pr_err("REG_INIT failed, rc=%d\n", rc); - return rc; - } + s5k3e2fx_setting_INIT_EVT5(rt); } + } else{ + /*EVT4*/ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_3150_RESERVED, 0x50); + s5k3e2fx_setting_INIT_EVT4(); + } } break; /* REG_INIT */ @@ -2317,10 +2490,127 @@ static int s5k3e2fx_setting(enum msm_s_reg_update rupdate, return rc; } -static int s5k3e2fx_sensor_open_init(const struct msm_camera_sensor_info *data) +#define MAX_LAYER_NUM 5 +#define FIRST_LAYER 9 +#define FUSE_ID_FIRST_ADDR 14 + +static int s5k3e2fx_i2c_read_fuseid(struct sensor_cfg_data *cdata) { - int rc; + uint32_t otp_vendorid_index = 0; + uint32_t otp_fuseid_index = 0; + unsigned short otp_vendorid1[MAX_LAYER_NUM]; + unsigned short otp_vendorid2[MAX_LAYER_NUM]; + unsigned short otp_vendorid3[MAX_LAYER_NUM]; + unsigned short otp_fuseid1[MAX_LAYER_NUM]; + unsigned short otp_fuseid2[MAX_LAYER_NUM]; + unsigned short otp_fuseid3[MAX_LAYER_NUM]; + for (otp_vendorid_index = 0; + otp_vendorid_index < MAX_LAYER_NUM; + otp_vendorid_index++) { + + s5k3e2fx_i2c_write_b( + s5k3e2fx_client->addr, + 0x311A, FIRST_LAYER+otp_vendorid_index); + s5k3e2fx_i2c_read_b( + s5k3e2fx_client->addr, + 0x311B, &otp_vendorid1[otp_vendorid_index]); + s5k3e2fx_i2c_read_b( + s5k3e2fx_client->addr, + 0x311C, &otp_vendorid2[otp_vendorid_index]); + s5k3e2fx_i2c_read_b( + s5k3e2fx_client->addr, + 0x311D, &otp_vendorid3[otp_vendorid_index]); + pr_info("s5k3e2fx: otp_vendorid1[%d]:0x%4x\n", + otp_vendorid_index, otp_vendorid1[otp_vendorid_index]); + pr_info("s5k3e2fx: otp_vendorid2[%d]:0x%4x\n", + otp_vendorid_index, otp_vendorid2[otp_vendorid_index]); + pr_info("s5k3e2fx: otp_vendorid3[%d]:0x%4x\n", + otp_vendorid_index, otp_vendorid3[otp_vendorid_index]); + if ((otp_vendorid1[otp_vendorid_index] == 0) && + (otp_vendorid2[otp_vendorid_index] == 0) && + (otp_vendorid3[otp_vendorid_index] == 0) && + (otp_vendorid_index != 0)) { + break; + } + } + otp_vendorid_index = otp_vendorid_index-1; + /*read fuse id from layer14~layer18. + *The last non-all-zero layer contains + *correct fuse id */ + + for (otp_fuseid_index = 0; + otp_fuseid_index < MAX_LAYER_NUM; + otp_fuseid_index++) { + /*give OTP the address you want to read*/ + s5k3e2fx_i2c_write_b( + s5k3e2fx_client->addr, + 0x311A, FUSE_ID_FIRST_ADDR+otp_fuseid_index); + s5k3e2fx_i2c_read_b( + s5k3e2fx_client->addr, + 0x311B, &otp_fuseid1[otp_fuseid_index]); + s5k3e2fx_i2c_read_b( + s5k3e2fx_client->addr, + 0x311C, &otp_fuseid2[otp_fuseid_index]); + s5k3e2fx_i2c_read_b( + s5k3e2fx_client->addr, + 0x311D, &otp_fuseid3[otp_fuseid_index]); + pr_info("s5k3e2fx: otp_fuseid1[%d]:0x%4x\n", + otp_fuseid_index, otp_fuseid1[otp_fuseid_index]); + pr_info("s5k3e2fx: otp_fuseid2[%d]:0x%4x\n", + otp_fuseid_index, otp_fuseid2[otp_fuseid_index]); + pr_info("s5k3e2fx: otp_fuseid3[%d]:0x%4x\n", + otp_fuseid_index, otp_fuseid3[otp_fuseid_index]); + if ((otp_fuseid1[otp_fuseid_index] == 0) && + (otp_fuseid2[otp_fuseid_index] == 0) && + (otp_fuseid3[otp_fuseid_index] == 0) && + (otp_fuseid_index != 0)) { + break; + } + } + otp_fuseid_index = otp_fuseid_index-1; + cdata->cfg.fuse.fuse_id_word1 = otp_vendorid_index; + cdata->cfg.fuse.fuse_id_word2 = otp_fuseid_index; + cdata->cfg.fuse.fuse_id_word3 = + (((uint32_t)otp_vendorid1[otp_vendorid_index])<<16) | + (((uint32_t)otp_vendorid2[otp_vendorid_index])<<8) | + ((uint32_t)otp_vendorid3[otp_vendorid_index]); + cdata->cfg.fuse.fuse_id_word4 = + (((uint32_t)otp_fuseid1[otp_fuseid_index])<<16) | + (((uint32_t)otp_fuseid2[otp_fuseid_index])<<8) | + ((uint32_t)otp_fuseid3[otp_fuseid_index]); + pr_info("s5k3e2fx: fuse->fuse_id_word1:%d\n", + cdata->cfg.fuse.fuse_id_word1); + pr_info("s5k3e2fx: fuse->fuse_id_word2:%d\n", + cdata->cfg.fuse.fuse_id_word2); + pr_info("s5k3e2fx: fuse->fuse_id_word3:0x%08x\n", + cdata->cfg.fuse.fuse_id_word3); + pr_info("s5k3e2fx: fuse->fuse_id_word4:0x%08x\n", + cdata->cfg.fuse.fuse_id_word4); + return 0; +} + +static int s5k3e2fx_sensor_open_init(struct msm_camera_sensor_info *data) +{ + int rc = 0; + int timeout; + + down(&s5k3e2fx_sem); + + /*check whether resume done*/ + timeout = wait_event_interruptible_timeout( + s5k3e2fx_event.event_wait, + s5k3e2fx_event.waked_up, + 30*HZ); + + pr_info("wait event : %d timeout:%d\n", + s5k3e2fx_event.waked_up, timeout); + if (timeout == 0) { + up(&s5k3e2fx_sem); + return rc; + } + + msm_camio_probe_on(s5k3e2fx_pdev); CDBG("%s %s:%d\n", __FILE__, __func__, __LINE__); s5k3e2fx_ctrl = kzalloc(sizeof(struct s5k3e2fx_ctrl), GFP_KERNEL); if (!s5k3e2fx_ctrl) { @@ -2349,6 +2639,7 @@ static int s5k3e2fx_sensor_open_init(const struct msm_camera_sensor_info *data) if (rc < 0) goto init_fail1; + pr_info("s5k3e2fx_ctrl->prev_res:%d\n",s5k3e2fx_ctrl->prev_res); if (s5k3e2fx_ctrl->prev_res == S_QTR_SIZE) rc = s5k3e2fx_setting(S_REG_INIT, S_RES_PREVIEW); else @@ -2367,14 +2658,19 @@ static int s5k3e2fx_sensor_open_init(const struct msm_camera_sensor_info *data) goto init_done; init_fail1: - kfree(s5k3e2fx_ctrl); + if (s5k3e2fx_ctrl) { + kfree(s5k3e2fx_ctrl); + s5k3e2fx_ctrl = NULL; + } init_done: + up(&s5k3e2fx_sem); return rc; } static void s5k3e2fx_suspend_sensor(void) { unsigned short rData = 0; + /*AF*/ s5k3e2fx_i2c_read_b(s5k3e2fx_client->addr, REG_TYPE1_AF_ENABLE, &rData); @@ -2387,10 +2683,13 @@ static void s5k3e2fx_suspend_sensor(void) msleep(210); /*for 5FPS */ /* hi z */ s5k3e2fx_i2c_read_b(s5k3e2fx_client->addr, REG_3150_RESERVED, &rData); - s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + if (g_usModuleVersion == 1) + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, REG_3150_RESERVED, (rData | 0x0001)); + else + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_3150_RESERVED, 0x51); mdelay(1); - } static int s5k3e2fx_power_down(void) @@ -2406,8 +2705,11 @@ static int s5k3e2fx_sensor_release(void) s5k3e2fx_suspend_sensor(); - kfree(s5k3e2fx_ctrl); - s5k3e2fx_ctrl = NULL; + msm_camio_probe_off(s5k3e2fx_pdev); + if (s5k3e2fx_ctrl) { + kfree(s5k3e2fx_ctrl); + s5k3e2fx_ctrl = NULL; + } allow_suspend(); @@ -2420,7 +2722,6 @@ static int s5k3e2fx_probe_init_lens_correction( const struct msm_camera_sensor_info *data) { int rc = 0; - /* LC setting */ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, S5K3E2FX_REG_SOFTWARE_RESET, @@ -2429,19 +2730,46 @@ static int s5k3e2fx_probe_init_lens_correction( s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_SW_STANDBY); + /*20090811 separates the EVT4/EVT5 sensor init and LC setting start */ - s5k3e2fx_i2c_write_table(&Init_setting[g_usModuleVersion][0], - NUM_INIT_REG); + if (g_usModuleVersion == 0) + s5k3e2fx_i2c_write_table( + &Init_setting_evt4[0], + ARRAY_SIZE(Init_setting_evt4)); + else + s5k3e2fx_i2c_write_table( + &Init_setting_evt5[0], + ARRAY_SIZE(Init_setting_evt5)); + + if (g_usModuleVersion == 1) { + /*Only for EVT5*/ + /* 090911 Add for Samsung VCM calibration current Start */ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3112, 0x0A); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3112, 0x09); + mdelay(5); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3145, 0x04); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3146, 0x80); + /* 090911 Add for Samsung VCM calibration current End */ + } else{ + /*for AWB auto calibration*/ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3110, 0x03); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3112, 0x0A); + msleep(5); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3110, 0x03); + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3112, 0x09); + } - /* 090911 Add for Samsung VCM calibration current Start */ - s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3112, 0x0A); - s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3112, 0x09); - mdelay(5); - s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3145, 0x04); - s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3146, 0x80); - /* 090911 Add for Samsung VCM calibration current End */ + if (g_usModuleVersion == 0) + s5k3e2fx_i2c_write_table( + &lc_setting_evt4[0], + ARRAY_SIZE(lc_setting_evt4)); + else + s5k3e2fx_i2c_write_table( + &lc_setting_evt5[0], + ARRAY_SIZE(lc_setting_evt5)); - s5k3e2fx_i2c_write_table(&lc_setting[g_usModuleVersion][0], NUM_LC_REG); + /* Solve EVT5 greenish in lowlight, prevent corrupted frame*/ + s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0105,0x1); /*20090811 separates the EVT4/EVT5 sensor init and LC setting end */ s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, @@ -2504,8 +2832,12 @@ static uint32_t s5k3e2fx_get_pict_max_exp_lc(void) snapshot_lines_per_frame = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l; - else - snapshot_lines_per_frame = 3961 * 3; + else { + /* snapshot max linecount + (should be larger than transmitted preview max linecount of + preview ISO and chromatix gain-line table) */ + snapshot_lines_per_frame = 3000; //3961 * 3; + } return snapshot_lines_per_frame; } @@ -2519,7 +2851,7 @@ static int s5k3e2fx_set_fps(struct fps_cfg *fps) CDBG("s5k3e2fx_ctrl->fps_divider = %d\n", s5k3e2fx_ctrl->fps_divider); - +#if 0 rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, REG_FRAME_LENGTH_LINES_MSB, (((s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + @@ -2535,9 +2867,9 @@ static int s5k3e2fx_set_fps(struct fps_cfg *fps) s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l) * s5k3e2fx_ctrl->fps_divider / 0x400) & 0xFF00)); - set_fps_done: return rc; +#endif } static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) @@ -2545,7 +2877,7 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) int rc = 0; uint16_t max_legal_gain = 0x0200; - uint32_t ll_ratio; /* Q10 */ + //uint32_t ll_ratio; /* Q10 */ uint32_t ll_pck, fl_lines; uint16_t offset = 4; uint32_t gain_msb, gain_lsb; @@ -2554,8 +2886,10 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) struct s5k3e2fx_i2c_reg_conf tbl[2]; - CDBG("Line:%d s5k3e2fx_write_exp_gain gain %d line %d\n", - __LINE__, gain, line); + CDBG("Line:%d s5k3e2fx_write_exp_gain \n", __LINE__); +//printk("Steven Enter write_exp_gain User Space Gain and Line:gain = %4d, line = %6d \n", gain, line); +// if ((gain == 0) || (line == 0)) /* 100223 Mask this for Bright Flash In Beginning */ +// return rc; if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) { @@ -2563,23 +2897,24 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) s5k3e2fx_ctrl->my_reg_line_count = (uint16_t) line; fl_lines = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_h + - s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l; + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_l; /* 972 + 18 = 990 */ ll_pck = s5k3e2fx_reg_pat[S_RES_PREVIEW].size_w + - s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p; + s5k3e2fx_reg_pat[S_RES_PREVIEW].blk_p; /* 1296 + 1436 = 2732 */ } else { fl_lines = s5k3e2fx_reg_pat[S_RES_CAPTURE].size_h + - s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l; + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_l; /* 1960 + 14 = 1974 */ ll_pck = s5k3e2fx_reg_pat[S_RES_CAPTURE].size_w + - s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p; + s5k3e2fx_reg_pat[S_RES_CAPTURE].blk_p; /* 2608 + 124 = 2732 */ } if (gain > max_legal_gain) gain = max_legal_gain; +#if 0 /* in Q10 */ line = (line * s5k3e2fx_ctrl->fps_divider); @@ -2588,6 +2923,29 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) else ll_ratio = 0x400; + ll_pck = ll_pck * ll_ratio / 0x400; + line = line / ll_ratio; +#else + // solving accuracy lossing by calculating separately + if (fl_lines < (line * s5k3e2fx_ctrl->fps_divider / 0x400)){ + /* ll_ratio = + (line * s5k3e2fx_ctrl->fps_divider / (fl_lines - offset)); */ + + ll_pck = ll_pck * + (line * s5k3e2fx_ctrl->fps_divider / (fl_lines - offset)) / + 0x400; + + /* line = line * s5k3e2fx_ctrl->fps_divider / + (line * s5k3e2fx_ctrl->fps_divider / (fl_lines - offset)); */ + line = fl_lines - offset; + } + else{ + /* ll_ratio = 0x400; */ + /* ll_pck = ll_pck * 0x400 / 0x400; */ + line = line * s5k3e2fx_ctrl->fps_divider / 0x400; + } +#endif + /* solve greenish: only release for preview */ if (s5k3e2fx_ctrl->sensormode == SENSOR_PREVIEW_MODE) { rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, @@ -2611,9 +2969,8 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) if (rc < 0) goto write_gain_done; #if 1 /* Solve EVT5 greenish in lowlight*/ - ll_pck = ll_pck * ll_ratio; - ll_pck_msb = ((ll_pck / 0x400) & 0xFF00) >> 8; - ll_pck_lsb = (ll_pck / 0x400) & 0x00FF; + ll_pck_msb = (ll_pck & 0xFF00) >> 8; + ll_pck_lsb = ll_pck & 0x00FF; tbl[0].waddr = REG_LINE_LENGTH_PCK_MSB; tbl[0].bdata = ll_pck_msb; tbl[1].waddr = REG_LINE_LENGTH_PCK_LSB; @@ -2622,6 +2979,7 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); if (rc < 0) goto write_gain_done; + #else if (line / 0x400 + offset > fl_lines) ll_pck = line / 0x400 + offset; @@ -2639,8 +2997,7 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) if (rc < 0) goto write_gain_done; #endif - - line = line / 0x400; + CDBG("line %d, fl_lines %d\n", line, fl_lines); intg_t_msb = (line & 0xFF00) >> 8; intg_t_lsb = (line & 0x00FF); tbl[0].waddr = REG_COARSE_INTEGRATION_TIME; @@ -2650,15 +3007,13 @@ static int s5k3e2fx_write_exp_gain(uint16_t gain, uint32_t line) rc = s5k3e2fx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); /* solve greenish: release for both */ - rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, - REG_GROUPED_PARAMETER_HOLD, - GROUPED_PARAMETER_UPDATE); - if (rc < 0) { - pr_err("s5k3e2fx_i2c_write_b failed on line %d\n", - __LINE__); - return rc; - } - + rc = s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, + REG_GROUPED_PARAMETER_HOLD, + GROUPED_PARAMETER_UPDATE); + if (rc < 0) { + pr_err("s5k3e2fx_i2c_write_b failed on line %d\n", __LINE__); + return rc; + } write_gain_done: return rc; } @@ -2674,19 +3029,23 @@ static int s5k3e2fx_set_pict_exp_gain(uint16_t gain, uint32_t line) static int s5k3e2fx_video_config(int mode, int res) { int rc; + struct msm_camera_sensor_info *sinfo = s5k3e2fx_pdev->dev.platform_data; + CDBG("s5k3e2fx_video_config res:%d\n", res); +/* 100202 Move these setting from down */ + s5k3e2fx_ctrl->prev_res = res; + s5k3e2fx_ctrl->curr_res = res; + s5k3e2fx_ctrl->sensormode = mode; switch (res) { case S_QTR_SIZE: - pr_info("start sensor S_RES_PREVIEW config: %d\n", __LINE__); rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_PREVIEW); if (rc < 0) return rc; - /* only apply my_reg for returning preview*/ - rc = s5k3e2fx_write_exp_gain(s5k3e2fx_ctrl->my_reg_gain, - s5k3e2fx_ctrl->my_reg_line_count); break; case S_FULL_SIZE: + pr_info("KPI PA: start sensor snapshot config: %d\n", __LINE__); + sinfo->kpi_sensor_start = ktime_to_ns(ktime_get()); rc = s5k3e2fx_setting(S_UPDATE_PERIODIC, S_RES_CAPTURE); if (rc < 0) return rc; @@ -2695,10 +3054,14 @@ static int s5k3e2fx_video_config(int mode, int res) default: return 0; } - +/* 100202 Move these setting up s5k3e2fx_ctrl->prev_res = res; s5k3e2fx_ctrl->curr_res = res; s5k3e2fx_ctrl->sensormode = mode; +*/ + + rc = s5k3e2fx_write_exp_gain(s5k3e2fx_ctrl->my_reg_gain, + s5k3e2fx_ctrl->my_reg_line_count); return rc; } @@ -2720,7 +3083,9 @@ static int s5k3e2fx_set_default_focus(void) return rc; } -static int s5k3e2fx_move_focus(int direction, int num_steps) +static int s5k3e2fx_move_focus( + int direction, int num_steps,int coarse_delay,int fine_delay, + int step_dir, int init_code_offset_max) { int rc = 0; int i; @@ -2731,11 +3096,30 @@ static int s5k3e2fx_move_focus(int direction, int num_steps) uint8_t next_pos_msb, next_pos_lsb; int16_t s_move[5]; uint32_t gain; /* Q10 format */ + int16_t step_direction_pre_define; + int16_t init_code_offset_pre_define; + int16_t coarse_search_delay; + int16_t fine_search_delay; + + if (g_usModuleVersion == 1) { /* EVT5 */ + step_direction_pre_define = step_dir; + init_code_offset_pre_define = init_code_offset_max; + /*fine search delay time is turnable*/ + coarse_search_delay = coarse_delay; + fine_search_delay = fine_delay; + } else { + step_direction_pre_define = 20; + init_code_offset_pre_define = 738; + coarse_search_delay = 6; + fine_search_delay = 4; + } + +pr_info("%s step_direction_pre_define %d\n", __func__, step_direction_pre_define); if (direction == MOVE_NEAR) - step_direction = 20; + step_direction = step_direction_pre_define; else if (direction == MOVE_FAR) - step_direction = -20; + step_direction = 0 - step_direction_pre_define; else { pr_err("s5k3e2fx_move_focus failed at line %d ...\n", __LINE__); return -EINVAL; @@ -2760,8 +3144,8 @@ static int s5k3e2fx_move_focus(int direction, int num_steps) for (i = 0; i <= 4; i++) { next_pos = (int16_t) (pos_offset + s_move[i]); - if (next_pos > (738 + init_code)) - next_pos = 738 + init_code; + if (next_pos > (init_code_offset_max + init_code)) + next_pos = init_code_offset_pre_define + init_code; else if (next_pos < 0) next_pos = 0; @@ -2785,176 +3169,39 @@ static int s5k3e2fx_move_focus(int direction, int num_steps) pos_offset = next_pos; s5k3e2fx_ctrl->curr_lens_pos = pos_offset - init_code; - if (num_steps > 1) - mdelay(6); + + if(num_steps > 1) + mdelay(coarse_search_delay); else - mdelay(4); + mdelay(fine_search_delay); } return rc; } -static int s5k3e2fx_sensor_config(void __user *argp) -{ - struct sensor_cfg_data cdata; - long rc = 0; - if (copy_from_user(&cdata, - (void *)argp, sizeof(struct sensor_cfg_data))) - return -EFAULT; +static int s5k3e2fx_suspend(struct platform_device *pdev, pm_message_t state) +{ + int rc; + struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; - CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype); - switch (cdata.cfgtype) { - case CFG_GET_PICT_FPS: - s5k3e2fx_get_pict_fps(cdata.cfg.gfps.prevfps, - &(cdata.cfg.gfps.pictfps)); + if (!sinfo->need_suspend) + return 0; + s5k3e2fx_event.waked_up = 0; - if (copy_to_user((void *)argp, &cdata, - sizeof(struct sensor_cfg_data))) - rc = -EFAULT; - break; - case CFG_GET_PREV_L_PF: - cdata.cfg.prevl_pf = s5k3e2fx_get_prev_lines_pf(); - - if (copy_to_user((void *)argp, - &cdata, sizeof(struct sensor_cfg_data))) - rc = -EFAULT; - break; - - case CFG_GET_PREV_P_PL: - cdata.cfg.prevp_pl = s5k3e2fx_get_prev_pixels_pl(); - - if (copy_to_user((void *)argp, - &cdata, sizeof(struct sensor_cfg_data))) - rc = -EFAULT; - break; - - case CFG_GET_PICT_L_PF: - cdata.cfg.pictl_pf = s5k3e2fx_get_pict_lines_pf(); - - if (copy_to_user((void *)argp, - &cdata, sizeof(struct sensor_cfg_data))) - rc = -EFAULT; - break; - - case CFG_GET_PICT_P_PL: - cdata.cfg.pictp_pl = s5k3e2fx_get_pict_pixels_pl(); - - if (copy_to_user((void *)argp, - &cdata, sizeof(struct sensor_cfg_data))) - rc = -EFAULT; - break; - - case CFG_GET_PICT_MAX_EXP_LC: - cdata.cfg.pict_max_exp_lc = s5k3e2fx_get_pict_max_exp_lc(); - - if (copy_to_user((void *)argp, - &cdata, sizeof(struct sensor_cfg_data))) - rc = -EFAULT; - break; - - case CFG_SET_FPS: - case CFG_SET_PICT_FPS: - rc = s5k3e2fx_set_fps(&(cdata.cfg.fps)); - break; - - case CFG_SET_EXP_GAIN: - rc = s5k3e2fx_write_exp_gain(cdata.cfg.exp_gain.gain, - cdata.cfg.exp_gain.line); - break; - - case CFG_SET_PICT_EXP_GAIN: - rc = s5k3e2fx_set_pict_exp_gain(cdata.cfg.exp_gain.gain, - cdata.cfg.exp_gain.line); - break; - - case CFG_SET_MODE: - rc = s5k3e2fx_video_config(cdata.mode, cdata.rs); - break; - - case CFG_PWR_DOWN: - rc = s5k3e2fx_power_down(); - break; - - case CFG_MOVE_FOCUS: - rc = s5k3e2fx_move_focus(cdata.cfg.focus.dir, - cdata.cfg.focus.steps); - break; - - case CFG_SET_DEFAULT_FOCUS: - rc = s5k3e2fx_set_default_focus(); - break; - -/* case CFG_GET_AF_MAX_STEPS: */ - case CFG_SET_EFFECT: - rc = s5k3e2fx_set_default_focus(); - break; - - case CFG_SET_LENS_SHADING: - default: - rc = -EFAULT; - break; - } - - prevent_suspend(); - return rc; -} - -static int s5k3e2fx_sensor_probe(const struct msm_camera_sensor_info *info, - struct msm_sensor_ctrl *s) -{ - int rc = 0; - pr_info("%s\n", __func__); - - rc = i2c_add_driver(&s5k3e2fx_i2c_driver); - if (rc < 0 || s5k3e2fx_client == NULL) { - rc = -ENOTSUPP; - goto probe_fail; - } - - msm_camio_clk_rate_set(S5K3E2FX_DEF_MCLK); - msleep(20); - - rc = s5k3e2fx_probe_init_sensor(info); - if (rc < 0) - goto probe_fail; - - /* lens correction */ - s5k3e2fx_probe_init_lens_correction(info); - init_suspend(); - - s->s_init = s5k3e2fx_sensor_open_init; - s->s_release = s5k3e2fx_sensor_release; - s->s_config = s5k3e2fx_sensor_config; - - return rc; - -probe_fail: - pr_err("SENSOR PROBE FAILS!\n"); - return rc; -} - -static int s5k3e2fx_suspend(struct platform_device *pdev, pm_message_t state) -{ - int rc; - struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; - - if (!sinfo->need_suspend) - return 0; - - CDBG("s5k3e2fx: camera suspend\n"); - rc = gpio_request(sinfo->sensor_reset, "s5k3e2fx"); - if (!rc) - gpio_direction_output(sinfo->sensor_reset, 0); - else { - pr_err("s5k3e2fx: request GPIO(sensor_reset) :%d faile\n", - sinfo->sensor_reset); - goto suspend_fail; - } - CDBG("s5k3e2fx: gpio_free:%d line:%d\n", sinfo->sensor_reset, - __LINE__); - gpio_free(sinfo->sensor_reset); + pr_info("s5k3e2fx: camera suspend\n"); + rc = gpio_request(sinfo->sensor_reset, "s5k3e2fx"); + if (!rc) + gpio_direction_output(sinfo->sensor_reset, 0); + else { + pr_err("s5k3e2fx: request GPIO(sensor_reset) :%d faile\n", + sinfo->sensor_reset); + goto suspend_fail; + } + CDBG("s5k3e2fx: gpio_free:%d line:%d\n", sinfo->sensor_reset, + __LINE__); + gpio_free(sinfo->sensor_reset); suspend_fail: return rc; @@ -3035,23 +3282,29 @@ static void s5k3e2fx_sensor_resume_setting(void) s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0202, 0x03); s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x0200, 0x02); } -static int s5k3e2fx_resume(struct platform_device *pdev) + +static void s5k3e2fx_resume(struct early_suspend *handler) { - int rc = 0; - struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data; + struct msm_camera_sensor_info *sinfo = s5k3e2fx_pdev->dev.platform_data; if (!sinfo->need_suspend) - return 0; + return; - CDBG("s5k3e2fx_resume\n"); + /*check whether already suspend*/ + if (s5k3e2fx_event.waked_up == 1) { + pr_info("S5k3e2fx: No nesesary to do Resume\n"); + return; + } + + pr_info("s5k3e2fx_resume\n"); /*init msm,clk ,GPIO,enable */ - msm_camio_probe_on(pdev); + msm_camio_probe_on(s5k3e2fx_pdev); msm_camio_clk_enable(CAMIO_MDC_CLK); - CDBG("msm_camio_probe_on\n"); + pr_info("msm_camio_probe_on\n"); /*read sensor ID and pull down reset */ msm_camio_clk_rate_set(S5K3E2FX_DEF_MCLK); - CDBG("msm_camio_clk_rate_set\n"); + pr_info("msm_camio_clk_rate_set\n"); msleep(20); s5k3e2fx_probe_init_sensor(sinfo); CDBG("s5k3e2fx_probe_init_sensor\n"); @@ -3063,6 +3316,7 @@ static int s5k3e2fx_resume(struct platform_device *pdev) s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, S5K3E2FX_REG_MODE_SELECT, S5K3E2FX_MODE_SELECT_STREAM); + /*software standby */ msleep(25); s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3130, 0x00); @@ -3075,14 +3329,226 @@ static int s5k3e2fx_resume(struct platform_device *pdev) s5k3e2fx_i2c_write_b(s5k3e2fx_client->addr, 0x3150, 0x51); msleep(240); /*set RST to low */ - msm_camio_probe_off(pdev); + msm_camio_probe_off(s5k3e2fx_pdev); msm_camio_clk_disable(CAMIO_MDC_CLK); - CDBG("s5k3e2fx:resume done\n"); + s5k3e2fx_event.waked_up = 1; + wake_up(&s5k3e2fx_event.event_wait); + pr_info("s5k3e2fx:resume done\n"); + return; +} + + +static struct early_suspend early_suspend_s5k3e2fx = { + .level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN+1, + .resume = s5k3e2fx_resume, + .suspend = NULL, +}; + +static const char *s5k3e2fxVendor = "Samsung"; +static const char *s5k3e2fxNAME = "s5k3e2fx"; +static const char *s5k3e2fxSize = "5M"; + +static ssize_t sensor_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + + sprintf(buf, "%s %s %s\n", s5k3e2fxVendor, s5k3e2fxNAME, s5k3e2fxSize); + ret = strlen(buf) + 1; + + return ret; +} + +static DEVICE_ATTR(sensor, 0444, sensor_vendor_show, NULL); + +static struct kobject *android_s5k3e2fx; + +static int s5k3e2fx_sysfs_init(void) +{ + int ret ; + pr_info("s5k3e2fx:kobject creat and add\n"); + android_s5k3e2fx = kobject_create_and_add("android_camera", NULL); + if (android_s5k3e2fx == NULL) { + pr_info("s5k3e2fx_sysfs_init: subsystem_register " \ + "failed\n"); + ret = -ENOMEM; + return ret ; + } + pr_info("s5k3e2fx:sysfs_create_file\n"); + ret = sysfs_create_file(android_s5k3e2fx, &dev_attr_sensor.attr); + if (ret) { + pr_info("s5k3e2fx_sysfs_init: sysfs_create_file " \ + "failed\n"); + kobject_del(android_s5k3e2fx); + } + return 0 ; +} + + +static int s5k3e2fx_sensor_config(void __user *argp) +{ + struct sensor_cfg_data cdata; + long rc = 0; + + if (copy_from_user(&cdata, + (void *)argp, sizeof(struct sensor_cfg_data))) + return -EFAULT; + + CDBG("%s: cfgtype = %d\n", __func__, cdata.cfgtype); + switch (cdata.cfgtype) { + case CFG_GET_PICT_FPS: + s5k3e2fx_get_pict_fps(cdata.cfg.gfps.prevfps, + &(cdata.cfg.gfps.pictfps)); + + if (copy_to_user((void *)argp, &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_L_PF: + cdata.cfg.prevl_pf = s5k3e2fx_get_prev_lines_pf(); + + if (copy_to_user((void *)argp, + &cdata, sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_P_PL: + cdata.cfg.prevp_pl = s5k3e2fx_get_prev_pixels_pl(); + + if (copy_to_user((void *)argp, + &cdata, sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_L_PF: + cdata.cfg.pictl_pf = s5k3e2fx_get_pict_lines_pf(); + + if (copy_to_user((void *)argp, + &cdata, sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_P_PL: + cdata.cfg.pictp_pl = s5k3e2fx_get_pict_pixels_pl(); + + if (copy_to_user((void *)argp, + &cdata, sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_MAX_EXP_LC: + cdata.cfg.pict_max_exp_lc = s5k3e2fx_get_pict_max_exp_lc(); + + if (copy_to_user((void *)argp, + &cdata, sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_SET_FPS: + case CFG_SET_PICT_FPS: + rc = s5k3e2fx_set_fps(&(cdata.cfg.fps)); + break; + + case CFG_SET_EXP_GAIN: + rc = s5k3e2fx_write_exp_gain(cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_PICT_EXP_GAIN: + rc = s5k3e2fx_set_pict_exp_gain(cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_MODE: + pr_info("CFG_SET_MODE\n"); + rc = s5k3e2fx_video_config(cdata.mode, cdata.rs); + break; + + case CFG_PWR_DOWN: + rc = s5k3e2fx_power_down(); + break; + + case CFG_MOVE_FOCUS: + rc = s5k3e2fx_move_focus(cdata.cfg.focus.dir, + cdata.cfg.focus.steps, + cdata.cfg.focus.coarse_delay, + cdata.cfg.focus.fine_delay, + cdata.cfg.focus.step_dir, + cdata.cfg.focus.init_code_offset_max); + break; + + case CFG_SET_DEFAULT_FOCUS: + rc = s5k3e2fx_set_default_focus(); + break; + + /*case CFG_GET_AF_MAX_STEPS: */ + case CFG_SET_EFFECT: + rc = s5k3e2fx_set_default_focus(); + break; + + case CFG_I2C_IOCTL_R_OTP:{ + rc = s5k3e2fx_i2c_read_fuseid(&cdata); + if (copy_to_user(argp, &cdata, sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + } + break; + case CFG_SET_LENS_SHADING: + default: + rc = -EFAULT; + break; + } + + prevent_suspend(); + return rc; +} + +static int s5k3e2fx_sensor_probe(struct msm_camera_sensor_info *info, + struct msm_sensor_ctrl *s) +{ + int rc = 0; + pr_info("%s\n", __func__); + + rc = i2c_add_driver(&s5k3e2fx_i2c_driver); + if (rc < 0 || s5k3e2fx_client == NULL) { + rc = -ENOTSUPP; + goto probe_fail; + } + + msm_camio_clk_rate_set(S5K3E2FX_DEF_MCLK); + msleep(20); + + rc = s5k3e2fx_probe_init_sensor(info); + if (rc < 0) + goto probe_fail; + + /* lens correction */ + s5k3e2fx_probe_init_lens_correction(info); + init_suspend(); + + s->s_init = s5k3e2fx_sensor_open_init; + s->s_release = s5k3e2fx_sensor_release; + s->s_config = s5k3e2fx_sensor_config; + + /*register late resuem*/ + register_early_suspend(&early_suspend_s5k3e2fx); + /*init wait event*/ + init_waitqueue_head(&s5k3e2fx_event.event_wait); + /*init waked_up value*/ + s5k3e2fx_event.waked_up = 1; + /*write sysfs*/ + s5k3e2fx_sysfs_init(); + + return rc; + +probe_fail: + pr_err("SENSOR PROBE FAILS!\n"); return rc; } static int __s5k3e2fx_probe(struct platform_device *pdev) { + s5k3e2fx_pdev = pdev; return msm_camera_drv_start(pdev, s5k3e2fx_sensor_probe); } @@ -3093,7 +3559,6 @@ static struct platform_driver msm_camera_driver = { .owner = THIS_MODULE, }, .suspend = s5k3e2fx_suspend, - .resume = s5k3e2fx_resume, }; static int __init s5k3e2fx_init(void) diff --git a/drivers/media/video/msm/s5k3h1gx.c b/drivers/media/video/msm/s5k3h1gx.c new file mode 100644 index 0000000000000..c7e417c8573e8 --- /dev/null +++ b/drivers/media/video/msm/s5k3h1gx.c @@ -0,0 +1,1756 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "s5k3h1gx.h" + +/* CAMIF output resolutions */ +/* 816x612, 24MHz MCLK 96MHz PCLK */ +#define SENSOR_FULL_SIZE_WIDTH 3280 +#define SENSOR_FULL_SIZE_HEIGHT 2464 + +#define SENSOR_QTR_SIZE_WIDTH 1640 +#define SENSOR_QTR_SIZE_HEIGHT 1232 + +#define SENSOR_HRZ_FULL_BLK_PIXELS 190 +#define SENSOR_VER_FULL_BLK_LINES 16 /* 0 */ +#define SENSOR_HRZ_QTR_BLK_PIXELS 1830 +#define SENSOR_VER_QTR_BLK_LINES 16 /* 8 */ +#define SENSOR_VER_QTR_BLK_LINES_PARALLEL 611 /* 16 */ /* 8 */ + +static int cam_mode_sel = 0; /* 0: photo, 1: video@30fps, 2: video@24fps */ +/* 611: 30fps, 1073: 24fps */ +const int s5k3h1gx_ver_qtr_blk_lines_array[] = {611,611,1073}; + +#define S5K3H1GX_AF_I2C_ADDR 0x18 +#define S5K3H1GX_TOTAL_STEPS_NEAR_TO_FAR 42 /* 36 */ +#define S5K3H1GX_SW_DAMPING_STEP 10 +#define S5K3H1GX_MAX_FPS 30 +#define S5K3H1GX_MAX_FPS_PARALLEL 30 /* 22 */ + +/*============================================================= + SENSOR REGISTER DEFINES +==============================================================*/ + +#define S5K3H1GX_REG_MODEL_ID 0x0000 +#define S5K3H1GX_MODEL_ID 0x3810 + +/* Color bar pattern selection */ +#define S5K3H1GX_COLOR_BAR_PATTERN_SEL_REG 0x0601 + +#define REG_FRAME_LENGTH_LINES_MSB 0x0340 +#define REG_FRAME_LENGTH_LINES_LSB 0x0341 +#define REG_LINE_LENGTH_PCK_MSB 0x0342 +#define REG_LINE_LENGTH_PCK_LSB 0x0343 +#define REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB 0x0204 +#define REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB 0x0205 +#define REG_COARSE_INTEGRATION_TIME_MSB 0x0202 +#define REG_COARSE_INTEGRATION_TIME_LSB 0x0203 + +#define S5K3H1GX_REG_GROUP_PARAMETER_HOLD 0x0104 +#define S5K3H1GX_GROUP_PARAMETER_HOLD 0x01 +#define S5K3H1GX_GROUP_PARAMETER_UNHOLD 0x00 + +//////////////////////////// + +#define Q8 0x00000100 +#define SENSOR_DEFAULT_CLOCK_RATE 24000000 + +//////////////////////////////////////////////////////////// + +/*============================================================================ + TYPE DECLARATIONS +============================================================================*/ + +/* 16bit address - 8 bit context register structure */ +#if 0 +typedef struct reg_addr_val_pair_struct { + uint16_t reg_addr; + uint8_t reg_val; +} reg_struct_type; +#endif + +struct awb_lsc_struct_type { + unsigned int caBuff[8]; /*awb_calibartion*/ + struct reg_addr_val_pair_struct LSC_table[150]; /*lsc_calibration*/ + uint32_t LSC_table_CRC; +}; + +enum s5k3h1gx_test_mode_t { + TEST_OFF, + TEST_1, + TEST_2, + TEST_3 +}; + +enum s5k3h1gx_resolution_t { + QTR_SIZE, + FULL_SIZE, + INVALID_SIZE +}; + +enum s5k3h1gx_reg_update_t{ + REG_INIT, + REG_PERIODIC +}; + +/*20101011 QCT mesh LSC calibration*/ +static int global_mode = 0; + +static int sensor_probe_node; +static int preview_frame_count; + +static struct wake_lock s5k3h1gx_wake_lock; + +static inline void init_suspend(void) +{ + wake_lock_init(&s5k3h1gx_wake_lock, WAKE_LOCK_IDLE, "s5k3h1gx"); +} + +static inline void deinit_suspend(void) +{ + wake_lock_destroy(&s5k3h1gx_wake_lock); +} + +static inline void prevent_suspend(void) +{ + wake_lock(&s5k3h1gx_wake_lock); +} + +static inline void allow_suspend(void) +{ + wake_unlock(&s5k3h1gx_wake_lock); +} + +/*============================================================================ +DATA DECLARATIONS +============================================================================*/ + +/* 96MHz PCLK @ 24MHz MCLK inc*/ + + +/* FIXME: Changes from here */ +struct s5k3h1gx_work { + struct work_struct work; +}; + +static struct s5k3h1gx_work *s5k3h1gx_sensorw; +static struct i2c_client *s5k3h1gx_client; +static uint16_t s5k3h1gx_pos_tbl[S5K3H1GX_TOTAL_STEPS_NEAR_TO_FAR + 1]; + +struct s5k3h1gx_ctrl { + const struct msm_camera_sensor_info *sensordata; + + uint32_t sensormode; + uint32_t fps_divider; /* init to 1 * 0x00000400 */ + uint32_t pict_fps_divider; /* init to 1 * 0x00000400 */ + uint16_t fps; + + int16_t curr_lens_pos; + uint16_t curr_step_pos; + uint16_t init_curr_lens_pos; + uint16_t my_reg_gain; + uint32_t my_reg_line_count; + uint16_t total_lines_per_frame; + + enum s5k3h1gx_resolution_t prev_res; + enum s5k3h1gx_resolution_t pict_res; + enum s5k3h1gx_resolution_t curr_res; + enum s5k3h1gx_test_mode_t set_test; + enum s5k3h1gx_reg_update_t reg_update; + + unsigned short imgaddr; +}; + +static struct s5k3h1gx_ctrl *s5k3h1gx_ctrl; +static struct platform_device *s5k3h1gx_pdev; + +struct s5k3h1gx_waitevent{ + uint32_t waked_up; + wait_queue_head_t event_wait; +}; + +static DECLARE_WAIT_QUEUE_HEAD(s5k3h1gx_wait_queue); +DEFINE_SEMAPHORE(s5k3h1gx_sem); + + +/*=============================================================*/ + +static int s5k3h1gx_i2c_rxdata(unsigned short saddr, + unsigned char *rxdata, int length) +{ + struct i2c_msg msgs[] = { + { + .addr = saddr, + .flags = 0, + .len = 2, + .buf = rxdata, + }, + { + .addr = saddr, + .flags = I2C_M_RD, + .len = length, + .buf = rxdata, + }, + }; + + if (i2c_transfer(s5k3h1gx_client->adapter, msgs, 2) < 0) { + pr_err("s5k3h1gx_i2c_rxdata failed!\n"); + return -EIO; + } + pr_info("%s: rxdata=0x%X\n", __func__, *rxdata); + + return 0; +} +static int32_t s5k3h1gx_i2c_txdata(unsigned short saddr, + unsigned char *txdata, int length) +{ + struct i2c_msg msg[] = { + { + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, + }, + }; + if (i2c_transfer(s5k3h1gx_client->adapter, msg, 1) < 0) { + pr_err("s5k3h1gx_i2c_txdata failed 0x%x\n", saddr); + return -EIO; + } + + return 0; +} + +static int32_t s5k3h1gx_i2c_read_b(unsigned short saddr, unsigned short raddr, + unsigned short *rdata) +{ + int32_t rc = 0; + unsigned char buf[4]; + + if (!rdata) + return -EIO; + + memset(buf, 0, sizeof(buf)); + + buf[0] = (raddr & 0xFF00)>>8; + buf[1] = (raddr & 0x00FF); + + rc = s5k3h1gx_i2c_rxdata(saddr, buf, 1); + if (rc < 0) + return rc; + + *rdata = buf[0]; + + if (rc < 0) + pr_info("s5k3h1gx_i2c_read failed!\n"); + + return rc; +} + + +static int32_t s5k3h1gx_i2c_read(unsigned short raddr, + unsigned short *rdata, int rlen) +{ + int32_t rc = 0; + unsigned char buf[2]; + int count = 0; + if (!rdata) + return -EIO; + + memset(buf, 0, sizeof(buf)); + + buf[0] = (raddr & 0xFF00) >> 8; + buf[1] = (raddr & 0x00FF); +retry: + rc = s5k3h1gx_i2c_rxdata(s5k3h1gx_client->addr, buf, rlen); + + if (rc < 0) { + pr_err("s5k3h1gx_i2c_read 0x%x failed!\n", raddr); + printk(KERN_ERR "starting read retry policy count:%d\n", count); + udelay(10); + count++; + if (count < 20) { + if (count > 10) + udelay(100); + } else + return rc; + goto retry; + } + + *rdata = (rlen == 2 ? buf[0] << 8 | buf[1] : buf[0]); + return rc; +} + + +static int32_t s5k3h1gx_i2c_write_b(unsigned short saddr, + unsigned short waddr, uint8_t bdata) +{ + int32_t rc = -EFAULT; + unsigned char buf[3]; + int count = 0; + + memset(buf, 0, sizeof(buf)); + buf[0] = (waddr & 0xFF00) >> 8; + buf[1] = (waddr & 0x00FF); + buf[2] = bdata; + +retry: + rc = s5k3h1gx_i2c_txdata(saddr, buf, 3); + + if (rc < 0) { + pr_err("i2c_write_b failed, addr = 0x%x, val = 0x%x\n", + waddr, bdata); + pr_err(KERN_ERR "starting read retry policy count:%d\n", count); + udelay(10); + count++; + if (count < 20) { + if (count > 10) + udelay(100); + } else + return rc; + goto retry; + } + + return rc; +} + +static void s5k3h1gx_get_pict_fps(uint16_t fps, uint16_t *pfps) +{ + /* input fps is preview fps in Q8 format */ + uint32_t divider, d1, d2; + uint16_t snapshot_height, preview_height, preview_width, snapshot_width; + struct msm_camera_sensor_info *sinfo = s5k3h1gx_pdev->dev.platform_data; + + if (s5k3h1gx_ctrl->prev_res == QTR_SIZE) { + preview_width = + SENSOR_QTR_SIZE_WIDTH + SENSOR_HRZ_QTR_BLK_PIXELS; + + if (sinfo->csi_if) + preview_height = + SENSOR_QTR_SIZE_HEIGHT + + SENSOR_VER_QTR_BLK_LINES; + else + preview_height = + SENSOR_QTR_SIZE_HEIGHT + + s5k3h1gx_ver_qtr_blk_lines_array[cam_mode_sel] ; + } else { + /* full size resolution used for preview. */ + preview_width = + SENSOR_FULL_SIZE_WIDTH + SENSOR_HRZ_FULL_BLK_PIXELS; + preview_height = + SENSOR_FULL_SIZE_HEIGHT + SENSOR_VER_FULL_BLK_LINES; + } + + if (s5k3h1gx_ctrl->pict_res == QTR_SIZE) { + snapshot_width = + SENSOR_QTR_SIZE_WIDTH + SENSOR_HRZ_QTR_BLK_PIXELS; + + if (sinfo->csi_if) + snapshot_height = + SENSOR_QTR_SIZE_HEIGHT + + SENSOR_VER_QTR_BLK_LINES; + else + snapshot_height = + SENSOR_QTR_SIZE_HEIGHT + + s5k3h1gx_ver_qtr_blk_lines_array[cam_mode_sel] ; + } else { + snapshot_width = + SENSOR_FULL_SIZE_WIDTH + + SENSOR_HRZ_FULL_BLK_PIXELS; + snapshot_height = + SENSOR_FULL_SIZE_HEIGHT + + SENSOR_VER_FULL_BLK_LINES; + } + + d1 = preview_height * 0x00000400 / snapshot_height; + d2 = preview_width * 0x00000400 / snapshot_width; + + divider = (uint32_t) (d1 * d2) / 0x00000400; + *pfps = (uint16_t)(fps * divider / 0x00000400); + +} /* endof s5k3h1gx_get_pict_fps */ + +static uint16_t s5k3h1gx_get_prev_lines_pf(void) +{ + struct msm_camera_sensor_info *sinfo = s5k3h1gx_pdev->dev.platform_data; + + if (s5k3h1gx_ctrl->prev_res == QTR_SIZE) { + if (sinfo->csi_if) + return (SENSOR_QTR_SIZE_HEIGHT + + SENSOR_VER_QTR_BLK_LINES); + else + return (SENSOR_QTR_SIZE_HEIGHT + + s5k3h1gx_ver_qtr_blk_lines_array[cam_mode_sel]); + } else { + return (SENSOR_FULL_SIZE_HEIGHT + + SENSOR_VER_FULL_BLK_LINES); + } +} + +static uint16_t s5k3h1gx_get_prev_pixels_pl(void) +{ + if (s5k3h1gx_ctrl->prev_res == QTR_SIZE) { + return (SENSOR_QTR_SIZE_WIDTH + + SENSOR_HRZ_QTR_BLK_PIXELS); + } else { + return (SENSOR_FULL_SIZE_WIDTH + + SENSOR_HRZ_FULL_BLK_PIXELS); +} +} + +static uint16_t s5k3h1gx_get_pict_lines_pf(void) +{ + struct msm_camera_sensor_info *sinfo = s5k3h1gx_pdev->dev.platform_data; + + if (s5k3h1gx_ctrl->pict_res == QTR_SIZE) { + if (sinfo->csi_if) + return (SENSOR_QTR_SIZE_HEIGHT + + SENSOR_VER_QTR_BLK_LINES); + else + return (SENSOR_QTR_SIZE_HEIGHT + + s5k3h1gx_ver_qtr_blk_lines_array[cam_mode_sel]); + } else { + return (SENSOR_FULL_SIZE_HEIGHT + + SENSOR_VER_FULL_BLK_LINES); + } +} + +static uint16_t s5k3h1gx_get_pict_pixels_pl(void) +{ + if (s5k3h1gx_ctrl->pict_res == QTR_SIZE) { + return (SENSOR_QTR_SIZE_WIDTH + + SENSOR_HRZ_QTR_BLK_PIXELS); + } else { + return (SENSOR_FULL_SIZE_WIDTH + + SENSOR_HRZ_FULL_BLK_PIXELS); + } +} + +static uint32_t s5k3h1gx_get_pict_max_exp_lc(void) +{ + struct msm_camera_sensor_info *sinfo = s5k3h1gx_pdev->dev.platform_data; + + if (s5k3h1gx_ctrl->pict_res == QTR_SIZE) { + if (sinfo->csi_if) + return (SENSOR_QTR_SIZE_HEIGHT + + SENSOR_VER_QTR_BLK_LINES); + else + return (SENSOR_QTR_SIZE_HEIGHT + + s5k3h1gx_ver_qtr_blk_lines_array[cam_mode_sel]); + } else { + return (SENSOR_FULL_SIZE_HEIGHT + + SENSOR_VER_FULL_BLK_LINES); + } +} + +static int32_t s5k3h1gx_set_fps(struct fps_cfg *fps) +{ + int32_t rc = 0; + s5k3h1gx_ctrl->fps_divider = fps->fps_div; + s5k3h1gx_ctrl->pict_fps_divider = fps->pict_fps_div; + s5k3h1gx_ctrl->fps = fps->f_mult; + return rc; +} + +static int32_t s5k3h1gx_i2c_write_table( + struct s5k3h1gx_i2c_reg_conf *reg_cfg_tbl, int num) +{ + int i; + int32_t rc = -EIO; + for (i = 0; i < num; i++) { + rc = s5k3h1gx_i2c_write_b(s5k3h1gx_client->addr, + reg_cfg_tbl->waddr, reg_cfg_tbl->bdata); + if (rc < 0) + break; + reg_cfg_tbl++; + } + + return rc; +} + +static int32_t s5k3h1gx_write_exp_gain + (uint16_t gain, uint32_t line) +{ + int32_t rc = 0; + + uint16_t max_legal_gain = 0x0200; + uint32_t ll_ratio; /* Q10 */ + uint32_t ll_pck, fl_lines; + uint16_t offset = 8; /* 4; */ /* kipper */ + uint32_t gain_msb, gain_lsb; + uint32_t intg_t_msb, intg_t_lsb; + uint32_t ll_pck_msb, ll_pck_lsb; + struct s5k3h1gx_i2c_reg_conf tbl[3]; + struct msm_camera_sensor_info *sinfo = s5k3h1gx_pdev->dev.platform_data; + + CDBG("Line:%d s5k3h1gx_write_exp_gain , gain=%d, line=%d\n", + __LINE__, gain, line); + + if (s5k3h1gx_ctrl->sensormode == SENSOR_PREVIEW_MODE) { + + s5k3h1gx_ctrl->my_reg_gain = gain; + s5k3h1gx_ctrl->my_reg_line_count = (uint16_t)line; + + if (sinfo->csi_if) + fl_lines = SENSOR_QTR_SIZE_HEIGHT + + SENSOR_VER_QTR_BLK_LINES; + else + fl_lines = SENSOR_QTR_SIZE_HEIGHT + + s5k3h1gx_ver_qtr_blk_lines_array[cam_mode_sel] ; + + ll_pck = SENSOR_QTR_SIZE_WIDTH + + SENSOR_HRZ_QTR_BLK_PIXELS; + + } else { + + fl_lines = SENSOR_FULL_SIZE_HEIGHT + + SENSOR_VER_FULL_BLK_LINES; + + ll_pck = SENSOR_FULL_SIZE_WIDTH + + SENSOR_HRZ_FULL_BLK_PIXELS; + } + + if (gain > max_legal_gain) + gain = max_legal_gain; + + /* in Q10 */ + line = (line * s5k3h1gx_ctrl->fps_divider); + + CDBG("s5k3h1gx_ctrl->fps_divider = %d\n", + s5k3h1gx_ctrl->fps_divider); + CDBG("fl_lines = %d\n", fl_lines); + CDBG("line = %d\n", line); + if ((fl_lines-offset) < (line / 0x400)) + ll_ratio = (line / (fl_lines - offset)); + else + ll_ratio = 0x400; + CDBG("ll_ratio = %d\n", ll_ratio); + + /* update gain registers */ + CDBG("gain = %d\n", gain); + gain_msb = (gain & 0xFF00) >> 8; + gain_lsb = gain & 0x00FF; + tbl[0].waddr = S5K3H1GX_REG_GROUP_PARAMETER_HOLD; + tbl[0].bdata = S5K3H1GX_GROUP_PARAMETER_HOLD; + tbl[1].waddr = REG_ANALOGUE_GAIN_CODE_GLOBAL_MSB; + tbl[1].bdata = gain_msb; + tbl[2].waddr = REG_ANALOGUE_GAIN_CODE_GLOBAL_LSB; + tbl[2].bdata = gain_lsb; + rc = s5k3h1gx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); + if (rc < 0) + goto write_gain_done; + + ll_pck = ll_pck * ll_ratio; + CDBG("ll_pck/0x400 = %d\n", ll_pck / 0x400); + ll_pck_msb = ((ll_pck / 0x400) & 0xFF00) >> 8; + ll_pck_lsb = (ll_pck / 0x400) & 0x00FF; + tbl[0].waddr = REG_LINE_LENGTH_PCK_MSB; + tbl[0].bdata = ll_pck_msb; + tbl[1].waddr = REG_LINE_LENGTH_PCK_LSB; + tbl[1].bdata = ll_pck_lsb; + rc = s5k3h1gx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)-1); + if (rc < 0) + goto write_gain_done; + + line = line / ll_ratio; + CDBG("line = %d\n", line); + intg_t_msb = (line & 0xFF00) >> 8; + intg_t_lsb = (line & 0x00FF); + tbl[0].waddr = REG_COARSE_INTEGRATION_TIME_MSB; + tbl[0].bdata = intg_t_msb; + tbl[1].waddr = REG_COARSE_INTEGRATION_TIME_LSB; + tbl[1].bdata = intg_t_lsb; + tbl[2].waddr = S5K3H1GX_REG_GROUP_PARAMETER_HOLD; + tbl[2].bdata = S5K3H1GX_GROUP_PARAMETER_UNHOLD; + rc = s5k3h1gx_i2c_write_table(&tbl[0], ARRAY_SIZE(tbl)); + +write_gain_done: + return rc; +} + +/* ### this function is not called for userspace ### */ +static int32_t s5k3h1gx_set_pict_exp_gain(uint16_t gain, uint32_t line) +{ + int32_t rc = 0; + rc = s5k3h1gx_write_exp_gain(gain, line); + + return rc; +} /* endof s5k3h1gx_set_pict_exp_gain*/ + +static int32_t initialize_s5k3h1gx_registers(void) +{ + int32_t rc = 0; + struct msm_camera_sensor_info *sinfo = + s5k3h1gx_pdev->dev.platform_data; + + mdelay(5); + + if (sinfo->csi_if) { + if (s5k3h1gx_regs.init_mipi_size > 0) + rc = s5k3h1gx_i2c_write_table( + s5k3h1gx_regs.init_mipi, + s5k3h1gx_regs.init_mipi_size); + } else { + if (s5k3h1gx_regs.init_parallel_size > 0) + rc = s5k3h1gx_i2c_write_table( + s5k3h1gx_regs.init_parallel, + s5k3h1gx_regs.init_parallel_size); + } + + return rc; +} /* end of initialize_s5k3h1gx_ov8m0vc_registers. */ + +static int32_t s5k3h1gx_setting(int rt) +{ + int32_t rc = 0; + /* reconfigure the qtr height to adjust frame rate */ + uint16_t fl_line = 0; + struct msm_camera_sensor_info *sinfo = + s5k3h1gx_pdev->dev.platform_data; + + switch (rt) { + case QTR_SIZE: + pr_err("s5k3h1gx_setting(QTR_SIZE)\n"); + rc = s5k3h1gx_i2c_write_b( + s5k3h1gx_client->addr, 0x0100, 0x00); + + if (sinfo->csi_if) { + rc = s5k3h1gx_i2c_write_table( + s5k3h1gx_regs.qtr_mipi, + s5k3h1gx_regs.qtr_mipi_size); + } else { + rc = s5k3h1gx_i2c_write_table( + s5k3h1gx_regs.qtr_parallel, + s5k3h1gx_regs.qtr_parallel_size); + if (rc < 0) + return rc; + fl_line = SENSOR_QTR_SIZE_HEIGHT + + s5k3h1gx_ver_qtr_blk_lines_array[cam_mode_sel]; + rc = s5k3h1gx_i2c_write_b(s5k3h1gx_client->addr, + REG_FRAME_LENGTH_LINES_MSB, + (fl_line & 0xFF00) >> 8); + if (rc < 0) + return rc; + rc = s5k3h1gx_i2c_write_b(s5k3h1gx_client->addr, + REG_FRAME_LENGTH_LINES_LSB, + fl_line & 0x00FF); + if (rc < 0) + return rc; + } + msleep(200); + rc = s5k3h1gx_i2c_write_b( + s5k3h1gx_client->addr, 0x0100, 0x01); + if (rc < 0) + return rc; + + s5k3h1gx_ctrl->curr_res = QTR_SIZE; + break; + + case FULL_SIZE: + pr_err("s5k3h1gx_setting(FULL_SIZE)\n"); + rc = s5k3h1gx_i2c_write_b( + s5k3h1gx_client->addr, 0x0100, 0x00); + + if (sinfo->csi_if) { + rc = s5k3h1gx_i2c_write_table( + s5k3h1gx_regs.full_mipi, + s5k3h1gx_regs.full_mipi_size); + } else { + rc = s5k3h1gx_i2c_write_table( + s5k3h1gx_regs.full_parallel, + s5k3h1gx_regs.full_parallel_size); + } + if (rc < 0) + return rc; + + msleep(100); + rc = s5k3h1gx_i2c_write_b( + s5k3h1gx_client->addr, 0x0100, 0x01); + + if (rc < 0) + return rc; + + s5k3h1gx_ctrl->curr_res = FULL_SIZE; + break; + + default: + rc = -EFAULT; + return rc; + } + + /* sync OV8810 method to set gain and line */ +#if 1 + /*take raw picture for LSC calibration*/ + if (global_mode) { + pr_info("[LSC calibration] set gain and line !!!!\n"); + s5k3h1gx_write_exp_gain(32, 300); + global_mode = 0; + } +#endif + + return rc; +} /* end of s5k3h1gx_setting */ + +static int32_t s5k3h1gx_video_config(int mode) +{ + int32_t rc = 0; + static int pre_sel = 0; + int cur_sel = (cam_mode_sel > 1)?1:0; + s5k3h1gx_ctrl->sensormode = mode; + + CDBG("cam_mode_sel %d cur_sel = %d, pre_sel = %d\n", + cam_mode_sel, cur_sel, pre_sel); + + if (s5k3h1gx_ctrl->curr_res != s5k3h1gx_ctrl->prev_res + || pre_sel != cur_sel + ) { + rc = s5k3h1gx_setting(s5k3h1gx_ctrl->prev_res); + if (rc < 0) + return rc; + } else { + s5k3h1gx_ctrl->curr_res = s5k3h1gx_ctrl->prev_res; + } + s5k3h1gx_ctrl->sensormode = mode; + + pre_sel = cur_sel; + + preview_frame_count = 0; + rc = + s5k3h1gx_write_exp_gain(s5k3h1gx_ctrl->my_reg_gain, + s5k3h1gx_ctrl->my_reg_line_count); + + return rc; + +} /* end of s5k3h1gx_video_config */ + +static int32_t s5k3h1gx_snapshot_config(int mode) +{ + int32_t rc = 0; + s5k3h1gx_ctrl->sensormode = mode; + + if (s5k3h1gx_ctrl->curr_res != s5k3h1gx_ctrl->pict_res) { + rc = s5k3h1gx_setting(s5k3h1gx_ctrl->pict_res); + if (rc < 0) + return rc; + } else { + s5k3h1gx_ctrl->curr_res = s5k3h1gx_ctrl->pict_res; + } + s5k3h1gx_ctrl->sensormode = mode; + + return rc; + +} /*end of s5k3h1gx_snapshot_config*/ + +static int32_t s5k3h1gx_raw_snapshot_config(int mode) +{ + int32_t rc = 0; + s5k3h1gx_ctrl->sensormode = mode; + if (s5k3h1gx_ctrl->curr_res != s5k3h1gx_ctrl->pict_res) { + rc = s5k3h1gx_setting(s5k3h1gx_ctrl->pict_res); + if (rc < 0) + return rc; + } else { + s5k3h1gx_ctrl->curr_res = s5k3h1gx_ctrl->pict_res; + } /* Update sensor resolution */ + + s5k3h1gx_ctrl->sensormode = mode; + + return rc; + +} /*end of s5k3h1gx_raw_snapshot_config*/ + +static int32_t s5k3h1gx_set_sensor_mode(int mode, + int res) +{ + int32_t rc = 0; + + switch (mode) { + case SENSOR_PREVIEW_MODE: + rc = s5k3h1gx_video_config(mode); + break; + + case SENSOR_SNAPSHOT_MODE: + rc = s5k3h1gx_snapshot_config(mode); + break; + + case SENSOR_RAW_SNAPSHOT_MODE: + rc = s5k3h1gx_raw_snapshot_config(mode); + break; + + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static int s5k3h1gx_probe_init_done(const struct msm_camera_sensor_info *data) +{ + int32_t rc = 0; + + pr_info("[Camera] gpio_request(%d, \"s5k3h1gx\")\n", data->sensor_pwd); + rc = gpio_request(data->sensor_pwd, "s5k3h1gx"); + if (!rc) + gpio_direction_output(data->sensor_pwd, 0); + else + pr_err("GPIO (%d) request failed\n", data->sensor_pwd); + + gpio_free(data->sensor_pwd); + + if (data->sensor_reset) { + msleep(5); + rc = gpio_request(data->sensor_reset, "s5k3h1gx"); + if (!rc) + gpio_direction_output(data->sensor_reset, 0); + else + pr_err("GPIO (%d) request faile\n", data->sensor_reset); + gpio_free(data->sensor_reset); + } + + + return 0; +} + +static int32_t s5k3h1gx_power_down(void) +{ + return 0; +} + +static int s5k3h1gx_probe_init_sensor(const struct msm_camera_sensor_info *data) +{ + int32_t rc = 0; + uint16_t chipid = 0; + + pr_info("%s\n", __func__); + + /* Read sensor Model ID: */ + rc = s5k3h1gx_i2c_read(S5K3H1GX_REG_MODEL_ID, &chipid, 2); + if (rc < 0) { + pr_err("read sensor id fail\n"); + goto init_probe_fail; + } else + data->camera_set_source(SECOND_SOURCE); + + /* Compare sensor ID to S5K3H1GX ID: */ + pr_info("%s, Expected id=0x%x\n", __func__, S5K3H1GX_MODEL_ID); + pr_info("%s, Read id=0x%x\n", __func__, chipid); + + if (chipid != S5K3H1GX_MODEL_ID) { + pr_err("sensor model id is incorrect\n"); + rc = -ENODEV; + goto init_probe_fail; + } + + pr_info("s5k3h1gx_probe_init_sensor finishes\n"); + goto init_probe_done; + +init_probe_fail: + s5k3h1gx_probe_init_done(data); +init_probe_done: + return rc; +} + +static void s5k3h1gx_setup_af_tbl(void) +{ + int i; + uint16_t s5k3h1gx_nl_region_boundary1 = 3; + uint16_t s5k3h1gx_nl_region_boundary2 = 5; + uint16_t s5k3h1gx_nl_region_code_per_step1 = 40; + uint16_t s5k3h1gx_nl_region_code_per_step2 = 20; + uint16_t s5k3h1gx_l_region_code_per_step = 16; + + s5k3h1gx_pos_tbl[0] = 0; + + for (i = 1; i <= S5K3H1GX_TOTAL_STEPS_NEAR_TO_FAR; i++) { + if (i <= s5k3h1gx_nl_region_boundary1) + s5k3h1gx_pos_tbl[i] = s5k3h1gx_pos_tbl[i-1] + + s5k3h1gx_nl_region_code_per_step1; + else if (i <= s5k3h1gx_nl_region_boundary2) + s5k3h1gx_pos_tbl[i] = s5k3h1gx_pos_tbl[i-1] + + s5k3h1gx_nl_region_code_per_step2; + else + s5k3h1gx_pos_tbl[i] = s5k3h1gx_pos_tbl[i-1] + + s5k3h1gx_l_region_code_per_step; + } +} + +static int32_t +s5k3h1gx_go_to_position(uint32_t lens_pos, uint8_t mask) +{ + int32_t rc = 0; + unsigned char buf[2]; + uint8_t code_val_msb, code_val_lsb; + + code_val_msb = lens_pos >> 4; + code_val_lsb = (lens_pos & 0x000F) << 4; + code_val_lsb |= mask; + + buf[0] = code_val_msb; + buf[1] = code_val_lsb; + rc = s5k3h1gx_i2c_txdata(S5K3H1GX_AF_I2C_ADDR >> 1, buf, 2); + if (rc < 0) + pr_err("i2c_write failed, saddr = 0x%x addr = 0x%x, val =0x%x!\n", + S5K3H1GX_AF_I2C_ADDR >> 1, buf[0], buf[1]); + + return rc; +} + +/*20101011 for LSC calibration*/ +static int s5k3h1gx_QCT_LSC_calibration_set_rawflag(void) +{ + global_mode = 1; + return 1; +} + +static int s5k3h1gx_i2c_read_fuseid(struct sensor_cfg_data *cdata) +{ + + int32_t rc; + unsigned short i, R1, R2, R3; + unsigned short OTP[10]; + + pr_info("%s: sensor OTP information:\n", __func__); + + for (i = 0; i < 10; i++) + OTP[i] = 5; + + rc = s5k3h1gx_i2c_write_b(s5k3h1gx_client->addr, 0x3124, 0x10); + if (rc < 0) + pr_info("%s: i2c_write_b 0x30FB fail\n", __func__); + + rc = s5k3h1gx_i2c_write_b(s5k3h1gx_client->addr, 0x3127, 0xF1); + if (rc < 0) + pr_info("%s: i2c_write_b 0x30FB fail\n", __func__); + + mdelay(4); + + for (i = 0; i < 10; i++) { + rc = s5k3h1gx_i2c_write_b(s5k3h1gx_client->addr, 0x312B, i); + if (rc < 0) + pr_info("%s: i2c_write_b 0x310C fail\n", __func__); + rc = s5k3h1gx_i2c_read_b(s5k3h1gx_client->addr, 0x312C, &R1); + if (rc < 0) + pr_info("%s: i2c_read_b 0x310F fail\n", __func__); + rc = s5k3h1gx_i2c_read_b(s5k3h1gx_client->addr, 0x312D, &R2); + if (rc < 0) + pr_info("%s: i2c_read_b 0x310E fail\n", __func__); + rc = s5k3h1gx_i2c_read_b(s5k3h1gx_client->addr, 0x312E, &R3); + if (rc < 0) + pr_info("%s: i2c_read_b 0x310D fail\n", __func__); + + if ((R3&0x0F) != 0) + OTP[i] = (short)(R3&0x0F); + else if ((R2&0x0F) != 0) + OTP[i] = (short)(R2&0x0F); + else if ((R2>>4) != 0) + OTP[i] = (short)(R2>>4); + else if ((R1&0x0F) != 0) + OTP[i] = (short)(R1&0x0F); + else + OTP[i] = (short)(R1>>4); + + } + pr_info("%s: VenderID=%x,LensID=%x,SensorID=%x%x\n", __func__, + OTP[0], OTP[1], OTP[2], OTP[3]); + pr_info("%s: ModuleFuseID= %x%x%x%x%x%x\n", __func__, + OTP[4], OTP[5], OTP[6], OTP[7], OTP[8], OTP[9]); + + cdata->cfg.fuse.fuse_id_word1 = 0; + cdata->cfg.fuse.fuse_id_word2 = 0; + cdata->cfg.fuse.fuse_id_word3 = (OTP[0]); + cdata->cfg.fuse.fuse_id_word4 = + (OTP[4]<<20) | + (OTP[5]<<16) | + (OTP[6]<<12) | + (OTP[7]<<8) | + (OTP[8]<<4) | + (OTP[9]); + + pr_info("s5k3h1gx: fuse->fuse_id_word1:%d\n", + cdata->cfg.fuse.fuse_id_word1); + pr_info("s5k3h1gx: fuse->fuse_id_word2:%d\n", + cdata->cfg.fuse.fuse_id_word2); + pr_info("s5k3h1gx: fuse->fuse_id_word3:0x%08x\n", + cdata->cfg.fuse.fuse_id_word3); + pr_info("s5k3h1gx: fuse->fuse_id_word4:0x%08x\n", + cdata->cfg.fuse.fuse_id_word4); + return 0; +} + +static int s5k3h1gx_sensor_open_init(struct msm_camera_sensor_info *data) +{ + int32_t rc = 0; + struct msm_camera_sensor_info *sinfo = s5k3h1gx_pdev->dev.platform_data; + + pr_info("Calling s5k3h1gx_sensor_open_init\n"); + + down(&s5k3h1gx_sem); + + if (data == NULL) { + pr_info("data is a NULL pointer\n"); + goto init_fail; + } + + s5k3h1gx_ctrl = kzalloc(sizeof(struct s5k3h1gx_ctrl), GFP_KERNEL); + + if (!s5k3h1gx_ctrl) { + rc = -ENOMEM; + goto init_fail; + } + + s5k3h1gx_ctrl->curr_lens_pos = -1; + s5k3h1gx_ctrl->fps_divider = 1 * 0x00000400; + s5k3h1gx_ctrl->pict_fps_divider = 1 * 0x00000400; + s5k3h1gx_ctrl->set_test = TEST_OFF; + s5k3h1gx_ctrl->prev_res = QTR_SIZE; + s5k3h1gx_ctrl->pict_res = FULL_SIZE; + s5k3h1gx_ctrl->curr_res = INVALID_SIZE; + s5k3h1gx_ctrl->reg_update = REG_INIT; + + if (data) + s5k3h1gx_ctrl->sensordata = data; + + /* switch pclk and mclk between main cam and 2nd cam */ + pr_info("doing clk switch (s5k3h1gx)\n"); + + if (data->camera_clk_switch != NULL) + data->camera_clk_switch(); + + msm_camio_probe_on(s5k3h1gx_pdev); + + /* for parallel interface */ + if (!sinfo->csi_if) { + mdelay(20); + msm_camio_camif_pad_reg_reset(); + mdelay(20); + } + pr_info("[Camera] gpio_request(%d, \"s5k3h1gx\")\n", data->sensor_pwd); + rc = gpio_request(data->sensor_pwd, "s5k3h1gx"); + if (!rc) { + gpio_direction_output(data->sensor_pwd, 1); + } else { + pr_err("GPIO (%d) request failed\n", data->sensor_pwd); + goto init_fail; + } + gpio_free(data->sensor_pwd); + msleep(5); + rc = gpio_request(data->sensor_reset, "s5k3h1gx"); + if (!rc) { + gpio_direction_output(data->sensor_reset, 1); + } else { + pr_err("GPIO (%d) request failed\n", data->sensor_reset); + goto init_fail; + } + gpio_free(data->sensor_reset); + msleep(1); + + /* read sensor id */ + rc = s5k3h1gx_probe_init_sensor(data); + + if (rc < 0) + goto init_fail; + + if (!sinfo->csi_if) { + rc = s5k3h1gx_i2c_write_table( + s5k3h1gx_regs.common_parallel, + s5k3h1gx_regs.common_parallel_size); + } + else + { + rc = s5k3h1gx_i2c_write_table( + s5k3h1gx_regs.common_mipi, + s5k3h1gx_regs.common_mipi_size); + } + + if (rc < 0) + goto init_fail; + + rc = s5k3h1gx_i2c_write_b( + s5k3h1gx_client->addr, 0x0100, 0x01); + + /* set up lens position table */ + s5k3h1gx_setup_af_tbl(); + s5k3h1gx_go_to_position(0, 0); + s5k3h1gx_ctrl->curr_lens_pos = 0; + s5k3h1gx_ctrl->curr_step_pos = 0; + + goto init_done; + +init_fail: + pr_err("%s: init_fail\n", __func__); + +init_done: + up(&s5k3h1gx_sem); + pr_info("%s: init_done\n", __func__); + return rc; + +} /* end of s5k3h1gx_sensor_open_init */ + +static int s5k3h1gx_init_client(struct i2c_client *client) +{ + /* Initialize the MSM_CAMI2C Chip */ + init_waitqueue_head(&s5k3h1gx_wait_queue); + return 0; +} + +static const struct i2c_device_id s5k3h1gx_i2c_id[] = { + { "s5k3h1gx", 0}, + { } +}; + +static int s5k3h1gx_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + pr_info("s5k3h1gx_probe called!\n"); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("i2c_check_functionality failed\n"); + goto probe_failure; + } + + s5k3h1gx_sensorw = kzalloc(sizeof(struct s5k3h1gx_work), GFP_KERNEL); + if (!s5k3h1gx_sensorw) { + pr_err("kzalloc failed.\n"); + rc = -ENOMEM; + goto probe_failure; + } + + i2c_set_clientdata(client, s5k3h1gx_sensorw); + s5k3h1gx_init_client(client); + s5k3h1gx_client = client; + + msleep(50); + + pr_info("s5k3h1gx_probe successed! rc = %d\n", rc); + return 0; + +probe_failure: + pr_err("s5k3h1gx_probe failed! rc = %d\n", rc); + return rc; +} + +static int __exit s5k3h1gx_i2c_remove(struct i2c_client *client) +{ + struct s5k3h1gx_work_t *sensorw = i2c_get_clientdata(client); + free_irq(client->irq, sensorw); + deinit_suspend(); + s5k3h1gx_client = NULL; + kfree(sensorw); + return 0; +} + +static struct i2c_driver s5k3h1gx_i2c_driver = { + .id_table = s5k3h1gx_i2c_id, + .probe = s5k3h1gx_i2c_probe, + .remove = __exit_p(s5k3h1gx_i2c_remove), + .driver = { + .name = "s5k3h1gx", + }, +}; + +static const char *S5K3H1GXVendor = "samsung"; +static const char *S5K3H1GXNAME = "S5K3H1GX"; +static const char *S5K3H1GXSize = "8M"; + +static ssize_t sensor_vendor_show( struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + + sprintf(buf, "%s %s %s\n", S5K3H1GXVendor, S5K3H1GXNAME, S5K3H1GXSize); + ret = strlen(buf) + 1; + + return ret; +} + +DEFINE_MUTEX(s5k3h1gx_cam_mode_lock); + +static ssize_t sensor_read_cam_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + mutex_lock(&s5k3h1gx_cam_mode_lock); + length = sprintf(buf, "%d\n", cam_mode_sel); + mutex_unlock(&s5k3h1gx_cam_mode_lock); + return length; +} + +static ssize_t sensor_set_cam_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + uint32_t tmp = 0; + mutex_lock(&s5k3h1gx_cam_mode_lock); + tmp = buf[0] - 0x30; /* only get the first char */ + cam_mode_sel = tmp; + mutex_unlock(&s5k3h1gx_cam_mode_lock); + return count; +} + +static ssize_t sensor_read_node( struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + length = sprintf(buf, "%d\n", sensor_probe_node); + return length; +} + +static DEVICE_ATTR(sensor, 0444, sensor_vendor_show, NULL); +static DEVICE_ATTR(cam_mode, 0644, sensor_read_cam_mode, sensor_set_cam_mode); +static DEVICE_ATTR(node, 0444, sensor_read_node, NULL); + +static struct kobject *android_s5k3h1gx = NULL; + +static int s5k3h1gx_sysfs_init(void) +{ + int ret = 0; + pr_info("s5k3h1gx:kobject creat and add\n"); + android_s5k3h1gx = kobject_create_and_add("android_camera", NULL); + if (android_s5k3h1gx == NULL) { + pr_info("s5k3h1gx_sysfs_init: subsystem_register failed\n"); + ret = -ENOMEM; + return ret ; + } + pr_info("s5k3h1gx:sysfs_create_file\n"); + ret = sysfs_create_file(android_s5k3h1gx, &dev_attr_sensor.attr); + if (ret) { + pr_info("s5k3h1gx_sysfs_init: sysfs_create_file failed\n"); + ret = -EFAULT; + goto error; + } + + ret = sysfs_create_file(android_s5k3h1gx, &dev_attr_cam_mode.attr); + if (ret) { + pr_info("s5k3h1gx_sysfs_init: dev_attr_cam_mode failed\n"); + ret = -EFAULT; + goto error; + } + ret = sysfs_create_file(android_s5k3h1gx, &dev_attr_node.attr); + if (ret) { + pr_info("s5k3h1gx_sysfs_init: dev_attr_node failed\n"); + ret = -EFAULT; + goto error; + } + + return ret; + +error: + kobject_del(android_s5k3h1gx); + return ret; +} + +static int32_t +s5k3h1gx_move_focus(int direction, int32_t num_steps) +{ + uint16_t s5k3h1gx_sw_damping_time_wait = 1; + uint16_t s5k3h1gx_damping_threshold = 10; + uint8_t s5k3h1gx_mode_mask = 0x02; + int16_t step_direction; + int16_t curr_lens_pos; + int16_t curr_step_pos; + int16_t dest_lens_pos; + int16_t dest_step_pos; + int16_t target_dist; + int16_t small_step; + int16_t next_lens_pos; + int16_t time_wait_per_step; + int32_t rc = 0, time_wait; + int8_t s5k3h1gx_sw_damping_required = 0; + uint16_t s5k3h1gx_max_fps_val; + struct msm_camera_sensor_info *sinfo = s5k3h1gx_pdev->dev.platform_data; + + if (num_steps > S5K3H1GX_TOTAL_STEPS_NEAR_TO_FAR) + num_steps = S5K3H1GX_TOTAL_STEPS_NEAR_TO_FAR; + else if (num_steps == 0) + return -EINVAL; + + if (direction == MOVE_NEAR) + step_direction = 1; + else if (direction == MOVE_FAR) + step_direction = -1; + else + return -EINVAL; + + /* need to decide about default position and power supplied + * at start up and reset */ + curr_lens_pos = s5k3h1gx_ctrl->curr_lens_pos; + curr_step_pos = s5k3h1gx_ctrl->curr_step_pos; + + if (curr_lens_pos < s5k3h1gx_ctrl->init_curr_lens_pos) + curr_lens_pos = s5k3h1gx_ctrl->init_curr_lens_pos; + + dest_step_pos = curr_step_pos + (step_direction * num_steps); + + if (dest_step_pos < 0) + dest_step_pos = 0; + else if (dest_step_pos > S5K3H1GX_TOTAL_STEPS_NEAR_TO_FAR) + dest_step_pos = S5K3H1GX_TOTAL_STEPS_NEAR_TO_FAR; + + if (dest_step_pos == s5k3h1gx_ctrl->curr_step_pos) + return rc; + + dest_lens_pos = s5k3h1gx_pos_tbl[dest_step_pos]; + target_dist = step_direction * (dest_lens_pos - curr_lens_pos); + + if (sinfo->csi_if) + s5k3h1gx_max_fps_val = S5K3H1GX_MAX_FPS; + else + s5k3h1gx_max_fps_val = S5K3H1GX_MAX_FPS_PARALLEL; + + + /* HW damping */ + if (step_direction < 0 + && target_dist >= s5k3h1gx_pos_tbl[s5k3h1gx_damping_threshold]) { + s5k3h1gx_sw_damping_required = 1; + time_wait = 1000000 + / s5k3h1gx_max_fps_val + - S5K3H1GX_SW_DAMPING_STEP * s5k3h1gx_sw_damping_time_wait * 1000; + } else + time_wait = 1000000 / s5k3h1gx_max_fps_val; + + time_wait_per_step = (int16_t) (time_wait / target_dist); + + if (time_wait_per_step >= 800) + /* ~800 */ + s5k3h1gx_mode_mask = 0x5; + else if (time_wait_per_step >= 400) + /* ~400 */ + s5k3h1gx_mode_mask = 0x4; + else if (time_wait_per_step >= 200) + /* 200~400 */ + s5k3h1gx_mode_mask = 0x3; + else if (time_wait_per_step >= 100) + /* 100~200 */ + s5k3h1gx_mode_mask = 0x2; + else if (time_wait_per_step >= 50) + /* 50~100 */ + s5k3h1gx_mode_mask = 0x1; + else { + if (time_wait >= 17600) + s5k3h1gx_mode_mask = 0x0D; + else if (time_wait >= 8800) + s5k3h1gx_mode_mask = 0x0C; + else if (time_wait >= 4400) + s5k3h1gx_mode_mask = 0x0B; + else if (time_wait >= 2200) + s5k3h1gx_mode_mask = 0x0A; + else + s5k3h1gx_mode_mask = 0x09; + } + + if (s5k3h1gx_sw_damping_required) { + small_step = (uint16_t) target_dist / S5K3H1GX_SW_DAMPING_STEP; + if ((target_dist % S5K3H1GX_SW_DAMPING_STEP) != 0) + small_step = small_step + 1; + + for (next_lens_pos = curr_lens_pos + (step_direction*small_step); + (step_direction*next_lens_pos) <= (step_direction*dest_lens_pos); + next_lens_pos += (step_direction*small_step)) { + rc = s5k3h1gx_go_to_position(next_lens_pos, s5k3h1gx_mode_mask); + if (rc < 0) { + CDBG("s5k3h1gx_go_to_position Failed in Move Focus!!!\n"); + return rc; + } + curr_lens_pos = next_lens_pos; + mdelay(s5k3h1gx_sw_damping_time_wait); + } + + if (curr_lens_pos != dest_lens_pos) { + rc = s5k3h1gx_go_to_position(dest_lens_pos, s5k3h1gx_mode_mask); + if (rc < 0) { + pr_err("s5k3h1gx_go_to_position Failed in Move Focus!!!\n"); + return rc; + } + mdelay(s5k3h1gx_sw_damping_time_wait); + } + } else { + rc = s5k3h1gx_go_to_position(dest_lens_pos, s5k3h1gx_mode_mask); + if (rc < 0) { + pr_err("s5k3h1gx_go_to_position Failed in Move Focus!!!\n"); + return rc; + } + } + + s5k3h1gx_ctrl->curr_lens_pos = dest_lens_pos; + s5k3h1gx_ctrl->curr_step_pos = dest_step_pos; + + return rc; +} + +static int32_t +s5k3h1gx_set_default_focus(void) +{ + int32_t rc = 0; + if (s5k3h1gx_ctrl->curr_step_pos != 0) { + rc = s5k3h1gx_move_focus(MOVE_FAR, s5k3h1gx_ctrl->curr_step_pos); + if (rc < 0) { + pr_err("s5k3h1gx_set_default_focus Failed!!!\n"); + return rc; + } + } else { + rc = s5k3h1gx_go_to_position(0, 0x02); + if (rc < 0) { + pr_err("s5k3h1gx_go_to_position Failed!!!\n"); + return rc; + } + } + + s5k3h1gx_ctrl->curr_lens_pos = 0; + s5k3h1gx_ctrl->init_curr_lens_pos = 0; + s5k3h1gx_ctrl->curr_step_pos = 0; + + return rc; +} + +uint8_t s5k3h1gx_preview_skip_frame(void) +{ + if (s5k3h1gx_ctrl->sensormode == SENSOR_PREVIEW_MODE + && preview_frame_count < 2) { + preview_frame_count++; + return 1; + } + return 0; +} + +int s5k3h1gx_sensor_config(void __user *argp) +{ + struct sensor_cfg_data cdata; + long rc = 0; + + if (copy_from_user(&cdata, + (void *)argp, + sizeof(struct sensor_cfg_data))) + return -EFAULT; + + down(&s5k3h1gx_sem); + + CDBG("s5k3h1gx_sensor_config: cfgtype = %d\n", + cdata.cfgtype); + switch (cdata.cfgtype) { + case CFG_GET_PICT_FPS: + s5k3h1gx_get_pict_fps( + cdata.cfg.gfps.prevfps, + &(cdata.cfg.gfps.pictfps)); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_L_PF: + cdata.cfg.prevl_pf = + s5k3h1gx_get_prev_lines_pf(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PREV_P_PL: + cdata.cfg.prevp_pl = + s5k3h1gx_get_prev_pixels_pl(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_L_PF: + cdata.cfg.pictl_pf = + s5k3h1gx_get_pict_lines_pf(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_P_PL: + cdata.cfg.pictp_pl = + s5k3h1gx_get_pict_pixels_pl(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_GET_PICT_MAX_EXP_LC: + cdata.cfg.pict_max_exp_lc = + s5k3h1gx_get_pict_max_exp_lc(); + + if (copy_to_user((void *)argp, + &cdata, + sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + break; + + case CFG_SET_FPS: + case CFG_SET_PICT_FPS: + rc = s5k3h1gx_set_fps(&(cdata.cfg.fps)); + break; + + case CFG_SET_EXP_GAIN: + rc = s5k3h1gx_write_exp_gain( + cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_PICT_EXP_GAIN: + rc = s5k3h1gx_set_pict_exp_gain( + cdata.cfg.exp_gain.gain, + cdata.cfg.exp_gain.line); + break; + + case CFG_SET_MODE: + rc = s5k3h1gx_set_sensor_mode(cdata.mode, + cdata.rs); + break; + + case CFG_PWR_DOWN: + rc = s5k3h1gx_power_down(); + break; + + case CFG_MOVE_FOCUS: + rc = + s5k3h1gx_move_focus( + cdata.cfg.focus.dir, + cdata.cfg.focus.steps); + break; + + case CFG_SET_DEFAULT_FOCUS: + rc = + s5k3h1gx_set_default_focus(); + break; + + case CFG_I2C_IOCTL_R_OTP:{ + pr_info("Line:%d CFG_I2C_IOCTL_R_OTP \n", __LINE__); + rc = s5k3h1gx_i2c_read_fuseid(&cdata); + if (copy_to_user(argp, &cdata, sizeof(struct sensor_cfg_data))) + rc = -EFAULT; + } + break; + + /*20101011 for QCT mesh LSC calibration*/ + case CFG_SET_QCT_LSC_RAW_CAPTURE: + pr_info("Line:%d : CFG_SET_QCT_LSC_RAW_CAPTURE \n", __LINE__); + rc = s5k3h1gx_QCT_LSC_calibration_set_rawflag(); + break; + + default: + rc = -EFAULT; + break; + } + + prevent_suspend(); + up(&s5k3h1gx_sem); + + return rc; +} + +static int s5k3h1gx_sensor_release(void) +{ + int rc = -EBADF; + + down(&s5k3h1gx_sem); + pr_info("%s, %d\n", __func__, __LINE__); + + if (s5k3h1gx_ctrl) { + rc = gpio_request( + s5k3h1gx_ctrl->sensordata->sensor_pwd, "s5k3h1gx"); + if (!rc) + gpio_direction_output( + s5k3h1gx_ctrl->sensordata->sensor_pwd, 0); + else + pr_err("GPIO (%d) request failed\n", + s5k3h1gx_ctrl->sensordata->sensor_pwd); + gpio_free(s5k3h1gx_ctrl->sensordata->sensor_pwd); + } + + msleep(5); + rc = gpio_request( + s5k3h1gx_ctrl->sensordata->sensor_reset, "s5k3h1gx"); + if (!rc) + gpio_direction_output( + s5k3h1gx_ctrl->sensordata->sensor_reset, 0); + else + pr_err("GPIO (%d) request faile\n", + s5k3h1gx_ctrl->sensordata->sensor_reset); + gpio_free(s5k3h1gx_ctrl->sensordata->sensor_reset); + + + msm_camio_probe_off(s5k3h1gx_pdev); + + if (s5k3h1gx_ctrl != NULL) { + kfree(s5k3h1gx_ctrl); + s5k3h1gx_ctrl = NULL; + } + + allow_suspend(); + pr_info("s5k3h1gx_release completed\n"); + up(&s5k3h1gx_sem); + + return rc; +} + +static int s5k3h1gx_sensor_probe(struct msm_camera_sensor_info *info, + struct msm_sensor_ctrl *s) +{ + int rc = 0; + pr_info("s5k3h1gx_sensor_probe()\n"); + + rc = i2c_add_driver(&s5k3h1gx_i2c_driver); + if (rc < 0 || s5k3h1gx_client == NULL) { + rc = -ENOTSUPP; + goto probe_fail; + } + + pr_info("s5k3h1 s->node %d\n", s->node); + sensor_probe_node = s->node; + + /* switch PCLK and MCLK to Main cam */ + pr_info("s5k3h1gx: s5k3h1gx_sensor_probe: switch clk\n"); + if (info->camera_clk_switch != NULL) + info->camera_clk_switch(); + + mdelay(20); + + pr_info("[Camera] gpio_request(%d, \"s5k3h1gx\")\n", info->sensor_pwd); + rc = gpio_request(info->sensor_pwd, "s5k3h1gx"); + if (!rc) + gpio_direction_output(info->sensor_pwd, 1); + else + pr_err("GPIO (%d) request failed\n", info->sensor_pwd); + gpio_free(info->sensor_pwd); + + msleep(100); + + /* read sensor id */ + rc = s5k3h1gx_probe_init_sensor(info); + + if (rc < 0) + goto probe_fail; + + /* Initialize Sensor registers */ + rc = initialize_s5k3h1gx_registers(); + if (rc < 0) + return rc; + + if (info->camera_main_set_probe != NULL) + info->camera_main_set_probe(true); + + init_suspend(); + s->s_init = s5k3h1gx_sensor_open_init; + s->s_release = s5k3h1gx_sensor_release; + s->s_config = s5k3h1gx_sensor_config; + s5k3h1gx_probe_init_done(info); + s5k3h1gx_sysfs_init(); + + pr_info("%s: s5k3h1gx_probe_init_done %d\n", __func__, __LINE__); + goto probe_done; + +probe_fail: + pr_err("SENSOR PROBE FAILS!\n"); +probe_done: + return rc; + +} + +static int __s5k3h1gx_probe(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sdata = pdev->dev.platform_data; + pr_info("s5k3h1gx_probe\n"); + s5k3h1gx_pdev = pdev; + if (sdata->camera_main_get_probe != NULL) { + if (sdata->camera_main_get_probe()) { + pr_info("__s5k3h1gx_probe camera main get probed already.\n"); + return 0; + } + } + return msm_camera_drv_start(pdev, s5k3h1gx_sensor_probe); +} + +static struct platform_driver msm_camera_driver = { + .probe = __s5k3h1gx_probe, + .driver = { + .name = "msm_camera_s5k3h1gx", + .owner = THIS_MODULE, + }, +}; + +static int __init s5k3h1gx_init(void) +{ + return platform_driver_register(&msm_camera_driver); +} + +module_init(s5k3h1gx_init); diff --git a/drivers/media/video/msm/s5k3h1gx.h b/drivers/media/video/msm/s5k3h1gx.h new file mode 100644 index 0000000000000..cfd75832684e4 --- /dev/null +++ b/drivers/media/video/msm/s5k3h1gx.h @@ -0,0 +1,93 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef S5K3H1GX_H +#define S5K3H1GX_H + +#include +#include + +struct s5k3h1gx_i2c_reg_conf { + unsigned short waddr; + unsigned char bdata; +}; + +struct s5k3h1gx_reg_t { + struct s5k3h1gx_i2c_reg_conf *init_mipi; + uint16_t init_mipi_size; + struct s5k3h1gx_i2c_reg_conf *init_parallel; + uint16_t init_parallel_size; + + struct s5k3h1gx_i2c_reg_conf *common_mipi; + uint16_t common_mipi_size; + struct s5k3h1gx_i2c_reg_conf *common_parallel; + uint16_t common_parallel_size; + + struct s5k3h1gx_i2c_reg_conf *qtr_mipi; + uint16_t qtr_mipi_size; + struct s5k3h1gx_i2c_reg_conf *qtr_parallel; + uint16_t qtr_parallel_size; + + struct s5k3h1gx_i2c_reg_conf *full_mipi; + uint16_t full_mipi_size; + struct s5k3h1gx_i2c_reg_conf *full_parallel; + uint16_t full_parallel_size; +}; + +extern struct s5k3h1gx_reg_t s5k3h1gx_regs; + +#endif + diff --git a/drivers/media/video/msm/s5k3h1gx_reg.c b/drivers/media/video/msm/s5k3h1gx_reg.c new file mode 100644 index 0000000000000..3854828c6ba8e --- /dev/null +++ b/drivers/media/video/msm/s5k3h1gx_reg.c @@ -0,0 +1,420 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "s5k3h1gx.h" + + +struct s5k3h1gx_i2c_reg_conf s5k3h1gx_init_settings_array_mipi[] = +{ + /* Because we will turn off power source after probe, there is no need to set register here */ +}; + +struct s5k3h1gx_i2c_reg_conf s5k3h1gx_init_settings_array_parallel[] = +{ + /* Because we will turn off power source after probe, there is no need to set register here */ +}; + +struct s5k3h1gx_i2c_reg_conf s5k3h1gx_common_settings_array_mipi[] = +{ + { 0x0100 , 0x00 }, + { 0x3091 , 0x00 }, + { 0x310E , 0x00 }, + { 0x0111 , 0x01 }, + { 0x3098 , 0xAB }, + { 0x309A , 0x01 }, + { 0x310D , 0xC6 }, + { 0x30C3 , 0x40 }, + { 0x308E , 0x05 }, + { 0x308F , 0x88 }, + { 0x30BB , 0x02 }, + { 0x30C7 , 0x1A }, + { 0x30BC , 0x38 }, + { 0x30BD , 0x40 }, + { 0x3110 , 0x70 }, + { 0x3111 , 0x80 }, + { 0x3112 , 0x7B }, + { 0x3113 , 0xC0 }, + { 0x3000 , 0x08 }, + { 0x3001 , 0x05 }, + { 0x3002 , 0x0D }, + { 0x3003 , 0x21 }, + { 0x3004 , 0x62 }, + { 0x3005 , 0x0B }, + { 0x3006 , 0x6D }, + { 0x3007 , 0x02 }, + { 0x3008 , 0x62 }, + { 0x3009 , 0x62 }, + { 0x300A , 0x41 }, + { 0x300B , 0x10 }, + { 0x300C , 0x21 }, + { 0x300D , 0x04 }, + { 0x307E , 0x03 }, + { 0x307F , 0xA5 }, + { 0x3080 , 0x04 }, + { 0x3081 , 0x29 }, + { 0x3082 , 0x03 }, + { 0x3083 , 0x21 }, + { 0x3011 , 0x5F }, + { 0x3156 , 0xE2 }, + { 0x3027 , 0x0E }, + { 0x300f , 0x02 }, + { 0x3072 , 0x13 }, + { 0x3073 , 0x61 }, + { 0x3074 , 0x92 }, + { 0x3075 , 0x10 }, + { 0x3076 , 0xA2 }, + { 0x3077 , 0x02 }, + { 0x3078 , 0x91 }, + { 0x3079 , 0x91 }, + { 0x307A , 0x61 }, + { 0x307B , 0x18 }, + { 0x307C , 0x61 }, + { 0x3010 , 0x10 }, + { 0x3017 , 0x74 }, + { 0x3018 , 0x00 }, + { 0x3020 , 0x02 }, + { 0x3021 , 0x24 }, + { 0x3023 , 0x40 }, + { 0x3024 , 0x08 }, + { 0x3025 , 0x08 }, + { 0x301C , 0xD4 }, + { 0x315D , 0x00 }, + { 0x3053 , 0xCF }, + { 0x3054 , 0x00 }, + { 0x3055 , 0x35 }, + { 0x3062 , 0x04 }, + { 0x3063 , 0x38 }, + { 0x3016 , 0x2c }, + { 0x3157 , 0x02 }, + { 0x3158 , 0x00 }, + { 0x315B , 0x02 }, + { 0x315C , 0x00 }, + { 0x301B , 0x04 }, + { 0x301A , 0xC4 }, + { 0x302d , 0x19 }, + { 0x302b , 0x04 }, + { 0x0305 , 0x04 }, /* pre_pll_clk_div = 4 */ + { 0x0306 , 0x00 }, /* pll_multiplier */ + { 0x0307 , 0x66 }, /* pll_multiplier = 102 */ + { 0x0303 , 0x01 }, /* vt_sys_clk_div = 1 */ + { 0x0301 , 0x05 }, /* vt_pix_clk_div = 5 */ + { 0x030B , 0x01 }, /* op_sys_clk_div = 1 */ + { 0x0309 , 0x05 }, /* op_pix_clk_div = 5 */ +}; + +struct s5k3h1gx_i2c_reg_conf s5k3h1gx_common_settings_array_parallel[] = +{ + { 0x0100 , 0x00 }, + /* MIPI/CCP/Parallel Setting */ + { 0x3091 , 0x00 }, + { 0x3065 , 0x15 }, /* sync mode */ + { 0x310E , 0x08 }, /* reg_sel 08h:parallel / 04h: CCP / 00h : MIPI */ + { 0x0111 , 0x01 }, /* CCP2_signalling_mode */ + { 0x308E , 0x05 }, + { 0x308F , 0x88 }, + /* Manufacture Setting */ + { 0x3000 , 0x08 }, + { 0x3001 , 0x05 }, + { 0x3002 , 0x0D }, + { 0x3003 , 0x21 }, + { 0x3004 , 0x62 }, + { 0x3005 , 0x0B }, + { 0x3006 , 0x6D }, + { 0x3007 , 0x02 }, + { 0x3008 , 0x62 }, + { 0x3009 , 0x62 }, + { 0x300A , 0x41 }, + { 0x300B , 0x10 }, + { 0x300C , 0x21 }, + { 0x300D , 0x04 }, + { 0x307E , 0x03 }, + { 0x307F , 0xA5 }, + { 0x3080 , 0x04 }, + { 0x3081 , 0x29 }, + { 0x3082 , 0x03 }, + { 0x3083 , 0x21 }, + { 0x3011 , 0x5F }, + { 0x3156 , 0xE2 }, + /* { 0x3027 , 0x0E }, */ + { 0x300f , 0x02 }, + { 0x3072 , 0x13 }, + { 0x3073 , 0x61 }, + { 0x3074 , 0x92 }, + { 0x3075 , 0x10 }, + { 0x3076 , 0xA2 }, + { 0x3077 , 0x02 }, + { 0x3078 , 0x91 }, + { 0x3079 , 0x91 }, + { 0x307A , 0x61 }, + { 0x307B , 0x18 }, + { 0x307C , 0x61 }, + { 0x3010 , 0x10 }, + { 0x3017 , 0x74 }, + { 0x3018 , 0x00 }, + { 0x3020 , 0x02 }, + { 0x3021 , 0x24 }, + { 0x3023 , 0x40 }, + { 0x3024 , 0x08 }, + { 0x3025 , 0x08 }, + { 0x301C , 0xD4 }, + { 0x315D , 0x00 }, + { 0x3053 , 0xCF }, + { 0x3054 , 0x00 }, + { 0x3055 , 0x35 }, + { 0x3062 , 0x04 }, + { 0x3063 , 0x38 }, + { 0x3016 , 0x2c }, + { 0x3157 , 0x02 }, + { 0x3158 , 0x00 }, + { 0x315B , 0x02 }, + { 0x315C , 0x00 }, + { 0x301A , 0xC4 }, + { 0x301B , 0x04 }, + { 0x302d , 0x19 }, + { 0x302b , 0x04 }, + { 0x310d , 0xe6 }, +}; + +struct s5k3h1gx_i2c_reg_conf s5k3h1gx_qtr_settings_array_mipi[] = +{ + { 0x30CC , 0xA0 }, /* DPHY_band_ctrl 560 ~ 640Mbps */ + { 0x0344 , 0x00 }, /* Xaddrstart 0d */ + { 0x0345 , 0x00 }, + { 0x0346 , 0x00 }, /* Yaddrstart 0d */ + { 0x0347 , 0x00 }, + { 0x0348 , 0x0C }, /* Xaddrend 3279d */ + { 0x0349 , 0xCf }, + { 0x034A , 0x09 }, /* Yaddrend 2463d */ + { 0x034B , 0x9F }, + { 0x0381 , 0x01 }, /* x_even_inc = 1 */ + { 0x0383 , 0x01 }, /* x_odd_inc = 1 */ + { 0x0385 , 0x01 }, /* y_even_inc = 1 */ + { 0x0387 , 0x03 }, /* y_odd_inc = 3 */ + { 0x034C , 0x06 }, /* x_output_size = 1640 */ + { 0x034D , 0x68 }, + { 0x034E , 0x04 }, /* y_output_size = 1232 */ + { 0x034F , 0xD0 }, + { 0x0200 , 0x02 }, /* fine integration time */ + { 0x0201 , 0x50 }, + { 0x0202 , 0x03 }, /* Coarse integration time */ + { 0x0203 , 0xA0 }, + { 0x0204 , 0x00 }, /* Analog gain */ + { 0x0205 , 0x20 }, + { 0x0342 , 0x0D }, /* Line_length_pck 3470d */ + { 0x0343 , 0x8E }, + { 0x0340 , 0x04 }, /* Frame_length_lines 1248d */ + { 0x0341 , 0xE0 }, + { 0x300E , 0xED }, /* Reserved */ + { 0x3085 , 0x00 }, /* Reserved */ + { 0x301D , 0x81 }, /* Reserved */ + { 0x3086 , 0x03 }, /* Reserved */ + { 0x3087 , 0x34 }, /* Reserved */ + { 0x3065 , 0x15 }, /* Reserved */ + { 0x3028 , 0x40 }, /* Reserved */ + { 0x0100 , 0x01 }, +}; + +struct s5k3h1gx_i2c_reg_conf s5k3h1gx_qtr_settings_array_parallel[] = +{ + /* PLL settings MCLK:24MHz,vt_pix_clk_freq_mhz=130.2MHz,op_sys_clk_freq_mhz=65.1MHz */ + { 0x0305 , 0x08 }, /* pre_pll_clk_div = 8 */ + { 0x0306 , 0x01 }, /* pll_multiplier */ + { 0x0307 , 0x40 }, /* pll_multiplier = 320 */ + { 0x0303 , 0x01 }, /* vt_sys_clk_div = 1 */ + { 0x0301 , 0x05 }, /* vt_pix_clk_div = 5 */ + { 0x030B , 0x01 }, /* op_sys_clk_div = 1 */ + { 0x0309 , 0x0A }, /* op_pix_clk_div = 10 */ + /* DBLR Clock setting = 96Mhz = vt_pix_clk_freq_mhz/2 */ + { 0x3027 , 0x7E }, + + /* Readout H:1/2 SubSampling binning, V:1/2 SubSampling binning */ + { 0x0344 , 0x00 }, /* X addr start 0d */ + { 0x0345 , 0x00 }, + { 0x0346 , 0x00 }, /* Y addr start 0d */ + { 0x0347 , 0x00 }, + { 0x0348 , 0x0C }, /* X addr end 3279d */ + { 0x0349 , 0xCF }, + { 0x034A , 0x09 }, /* Y addr end 2463d */ + { 0x034B , 0x9F }, + { 0x0381 , 0x01 }, /* x_even_inc = 1 */ + { 0x0383 , 0x01 }, /* x_odd_inc = 1 */ + { 0x0385 , 0x01 }, /* y_even_inc = 1 */ + { 0x0387 , 0x03 }, /* y_odd_inc = 3 */ + /* ------------- */ + { 0x0401 , 0x01 }, /* Derating_en = 1 (disable) */ + { 0x0405 , 0x10 }, + { 0x0700 , 0x05 }, /* fifo_threshold = 1328 */ + { 0x0701 , 0x30 }, + /* ------------- */ + { 0x034C , 0x06 }, /* x_output_size = 1640 */ + { 0x034D , 0x68 }, + { 0x034E , 0x04 }, /* y_output_size = 1232 */ + { 0x034F , 0xD0 }, + { 0x0200 , 0x03 }, /* fine integration time */ + { 0x0201 , 0x50 }, + /* ------------- */ + { 0x0202 , 0x03 }, /* Coarse integration time */ + { 0x0203 , 0xA0 }, /* DB */ + /* ------------- */ + { 0x0204 , 0x00 }, /* Analog gain */ + { 0x0205 , 0x20 }, + { 0x0342 , 0x0D }, /* Line_length_pck 3470d */ + { 0x0343 , 0x8E }, + { 0x0340 , 0x07 }, /* Frame_length_lines 1843d */ + { 0x0341 , 0x33 }, + /* Manufacture Setting */ + { 0x300E , 0xED }, + { 0x3085 , 0x00 }, + { 0x301D , 0x81 }, + { 0x3028 , 0x40 }, + { 0x3086 , 0x03 }, + { 0x3087 , 0x34 }, + { 0x3065 , 0x15 }, + /* ------------- */ + { 0x310C , 0x50 }, /* pclk invert */ + { 0x3117 , 0x0F }, /* H/V sync driving strength 6mA */ + { 0x3118 , 0xF0 }, /* parallel data driving strength 6mA */ + /* ------------- */ + + /*{ 0x0100 , 0x01 },*/ +}; + +struct s5k3h1gx_i2c_reg_conf s5k3h1gx_full_settings_array_mipi[] = +{ + { 0x30CC , 0xA0 }, /* DPHY_band_ctrl 560 ~ 640Mbps */ + { 0x0344 , 0x00 }, /* X addr start 0d */ + { 0x0345 , 0x00 }, + { 0x0346 , 0x00 }, /* Y addr start 0d */ + { 0x0347 , 0x00 }, + { 0x0348 , 0x0C }, /* X addr end 3279d */ + { 0x0349 , 0xCf }, + { 0x034A , 0x09 }, /* Y addr end 2463d */ + { 0x034B , 0x9F }, + { 0x0381 , 0x01 }, /* x_even_inc = 1 */ + { 0x0383 , 0x01 }, /* x_odd_inc = 1 */ + { 0x0385 , 0x01 }, /* y_even_inc = 1 */ + { 0x0387 , 0x01 }, /* y_odd_inc = 1 */ + { 0x034C , 0x0C }, /* x_output_size = 3280 */ + { 0x034D , 0xD0 }, + { 0x034E , 0x09 }, /* y_output_size = 2464 */ + { 0x034F , 0xA0 }, + { 0x0200 , 0x02 }, /* fine integration time */ + { 0x0201 , 0x50 }, + { 0x0202 , 0x04 }, /* Coarse integration time */ + { 0x0203 , 0xE7 }, + { 0x0204 , 0x00 }, /* Analog gain */ + { 0x0205 , 0x20 }, + { 0x0342 , 0x0D }, /* Line_length_pck */ + { 0x0343 , 0x8E }, + { 0x0340 , 0x09 }, /* Frame_length_lines */ + { 0x0341 , 0xB0 }, + { 0x300E , 0xE9 }, /* Reserved */ + { 0x3085 , 0x01 }, /* Reserved */ + { 0x301D , 0x01 }, /* Reserved */ + { 0x3086 , 0x03 }, /* Reserved */ + { 0x3087 , 0x34 }, /* Reserved */ + { 0x3065 , 0x15 }, /* Reserved */ + { 0x3028 , 0x41 }, /* Reserved */ + { 0x0100 , 0x01 }, +}; + +struct s5k3h1gx_i2c_reg_conf s5k3h1gx_full_settings_array_parallel[] = +{ + /* PLL settings MCLK:24MHz,vt_pix_clk_freq_mhz=96MHz,op_sys_clk_freq_mhz=96MHz */ + { 0x0305 , 0x04 }, /* pre_pll_clk_div = 4 */ + { 0x0306 , 0x00 }, /* pll_multiplier */ + { 0x0307 , 0xA0 }, /* pll_multiplier = 160 */ + { 0x0303 , 0x01 }, /* vt_sys_clk_div = 1 */ + { 0x0301 , 0x0A }, /* vt_pix_clk_div = 10 */ + { 0x030B , 0x01 }, /* op_sys_clk_div = 1 */ + { 0x0309 , 0x0A }, /* op_pix_clk_div = 10 */ + /* DBLR Clock setting = 96Mhz = vt_pix_clk_freq_mhz */ + { 0x3027 , 0x3E }, + /* Readout Full */ + { 0x0344 , 0x00 }, /* X addr start 0d */ + { 0x0345 , 0x00 }, + { 0x0346 , 0x00 }, /* Y addr start 0d */ + { 0x0347 , 0x00 }, + { 0x0348 , 0x0C }, /* X addr end 3279d */ + { 0x0349 , 0xCF }, + { 0x034A , 0x09 }, /* Y addr end 2463d */ + { 0x034B , 0x9F }, + { 0x0381 , 0x01 }, /* x_even_inc = 1 */ + { 0x0383 , 0x01 }, /* x_odd_inc = 1 */ + { 0x0385 , 0x01 }, /* y_even_inc = 1 */ + { 0x0387 , 0x01 }, /* y_odd_inc = 1 */ + /* ------------- */ + { 0x0401 , 0x00 }, /* Scaler OFF */ + { 0x0405 , 0x10 }, /* Scaling ratio 16/16 */ + { 0x0700 , 0x03 }, /* fifo_threshold = 818d */ + { 0x0701 , 0x32 }, + /* ------------- */ + { 0x034C , 0x0C }, /* x_output_size = 3280 */ + { 0x034D , 0xD0 }, + { 0x034E , 0x09 }, /* y_output_size = 2464 */ + { 0x034F , 0xA0 }, + { 0x0200 , 0x03 }, /* fine integration time */ + { 0x0201 , 0x50 }, + { 0x0202 , 0x04 }, /* Coarse integration time */ + { 0x0203 , 0xE7 }, + { 0x0204 , 0x00 }, /* Analog gain */ + { 0x0205 , 0x20 }, + { 0x0342 , 0x0D }, /* Line_length_pck 3470d */ + { 0x0343 , 0x8E }, + { 0x0340 , 0x09 }, /* Frame_length_lines 2480d */ + { 0x0341 , 0xB0 }, + /* Manufacture Setting */ + { 0x300E , 0xE9 }, + { 0x3085 , 0x01 }, + { 0x301D , 0x01 }, + { 0x3086 , 0x03 }, + { 0x3087 , 0x34 }, + { 0x3028 , 0x41 }, + { 0x3065 , 0x15 }, + /* ------------- */ + { 0x310C , 0x50 }, /* pclk invert */ + { 0x3117 , 0x0F }, /* H/V sync driving strength 6mA */ + { 0x3118 , 0xF0 }, /* parallel data driving strength 6mA */ + /* ------------- */ + + /*{ 0x0100 , 0x01 },*/ +}; + + + +struct s5k3h1gx_reg_t s5k3h1gx_regs = { + .init_mipi = &s5k3h1gx_init_settings_array_mipi[0], + .init_mipi_size = ARRAY_SIZE(s5k3h1gx_init_settings_array_mipi), + .init_parallel = &s5k3h1gx_init_settings_array_parallel[0], + .init_parallel_size = ARRAY_SIZE(s5k3h1gx_init_settings_array_parallel), + + .common_mipi = &s5k3h1gx_common_settings_array_mipi[0], + .common_mipi_size = ARRAY_SIZE(s5k3h1gx_common_settings_array_mipi), + .common_parallel = &s5k3h1gx_common_settings_array_parallel[0], + .common_parallel_size = ARRAY_SIZE(s5k3h1gx_common_settings_array_parallel), + + .qtr_mipi = &s5k3h1gx_qtr_settings_array_mipi[0], + .qtr_mipi_size = ARRAY_SIZE(s5k3h1gx_qtr_settings_array_mipi), + .qtr_parallel = &s5k3h1gx_qtr_settings_array_parallel[0], + .qtr_parallel_size = ARRAY_SIZE(s5k3h1gx_qtr_settings_array_parallel), + + .full_mipi = &s5k3h1gx_full_settings_array_mipi[0], + .full_mipi_size = ARRAY_SIZE(s5k3h1gx_full_settings_array_mipi), + .full_parallel = &s5k3h1gx_full_settings_array_parallel[0], + .full_parallel_size = ARRAY_SIZE(s5k3h1gx_full_settings_array_parallel), +}; diff --git a/drivers/media/video/msm/s5k6aafx.c b/drivers/media/video/msm/s5k6aafx.c new file mode 100644 index 0000000000000..b7b84b4d45d85 --- /dev/null +++ b/drivers/media/video/msm/s5k6aafx.c @@ -0,0 +1,1022 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "s5k6aafx.h" + +struct s5k6aafx_work { + struct work_struct work; +}; + +static struct s5k6aafx_work *s5k6aafx_sensorw; +static struct i2c_client *s5k6aafx_client; + +struct s5k6aafx_ctrl { + const struct msm_camera_sensor_info *sensordata; +}; + +static struct s5k6aafx_ctrl *s5k6aafx_ctrl; +static struct platform_device *s5k6aafx_pdev; + +static int op_mode; +static DECLARE_WAIT_QUEUE_HEAD(s5k6aafx_wait_queue); +DEFINE_SEMAPHORE(s5k6aafx_sem); + +static int sensor_probe_node = 0; +static enum frontcam_t previous_mirror_mode; + +static enum wb_mode current_wb = CAMERA_AWB_AUTO; +static int s5k6aafx_set_wb(enum wb_mode wb_value); + +#define MAX_I2C_RETRIES 20 +static int i2c_transfer_retry(struct i2c_adapter *adap, + struct i2c_msg *msgs, + int len) +{ + int i2c_retry = 0; + int ns; /* number sent */ + + while (i2c_retry++ < MAX_I2C_RETRIES) { + ns = i2c_transfer(adap, msgs, len); + if (ns == len) + break; + pr_err("%s: try %d/%d: i2c_transfer sent: %d, len %d\n", + __func__, + i2c_retry, MAX_I2C_RETRIES, ns, len); + msleep(10); + } + + return ns == len ? 0 : -EIO; +} + + +static int s5k6aafx_i2c_txdata(unsigned short saddr, + unsigned char *txdata, int length) +{ + struct i2c_msg msg[] = { + { + .addr = saddr, + .flags = 0, + .len = length, + .buf = txdata, + }, + }; + + if (i2c_transfer_retry(s5k6aafx_client->adapter, msg, 1) < 0) { + pr_info("s5k6aafx_i2c_txdata failed\n"); + return -EIO; + } + + return 0; +} + +static int s5k6aafx_i2c_write(unsigned short saddr, + unsigned short waddr, unsigned short wdata) +{ + int rc = -EIO; + unsigned char buf[4]; + memset(buf, 0, sizeof(buf)); + + buf[0] = (waddr & 0xFF00) >> 8; + buf[1] = (waddr & 0x00FF); + buf[2] = (wdata & 0xFF00) >> 8; + buf[3] = (wdata & 0x00FF); + rc = s5k6aafx_i2c_txdata(saddr, buf, 4); + if (rc < 0) + pr_info("i2c_write failed, addr = 0x%x, val = 0x%x!\n", + waddr, wdata); + + return rc; +} + +static int s5k6aafx_i2c_write_table(struct s5k6aafx_i2c_reg_conf const + *reg_conf_tbl, int num_of_items_in_table) +{ + int i; + int rc = -EIO; + + for (i = 0; i < num_of_items_in_table; i++) { + rc = s5k6aafx_i2c_write(s5k6aafx_client->addr, + reg_conf_tbl->waddr, reg_conf_tbl->wdata); + if (rc < 0) + break; + reg_conf_tbl++; + } + return rc; +} + +static int s5k6aafx_i2c_rxdata(unsigned short saddr, + unsigned char *rxdata, int length) +{ + struct i2c_msg msgs[] = { + { + .addr = saddr, + .flags = 0, + .len = 2, + .buf = rxdata, + }, + { + .addr = saddr, + .flags = I2C_M_RD, + .len = length, + .buf = rxdata, + }, + }; + + if (i2c_transfer_retry(s5k6aafx_client->adapter, msgs, 2) < 0) { + pr_info("s5k6aafx_i2c_rxdata failed!\n"); + return -EIO; + } + + return 0; +} + +static int s5k6aafx_i2c_read(unsigned short saddr, + unsigned short raddr, unsigned short *rdata) +{ + int32_t rc = 0; + unsigned char buf[4]; + + if (!rdata) + return -EIO; + + memset(buf, 0, sizeof(buf)); + + buf[0] = (raddr & 0xFF00)>>8; + buf[1] = (raddr & 0x00FF); + + rc = s5k6aafx_i2c_rxdata(saddr, buf, 2); + if (rc < 0){ + printk(KERN_ERR "s5k6aafx_i2c_read failed!\n"); + return rc; + } + + *rdata = buf[0] << 8 | buf[1]; + + return rc; +} + +static int s5k6aafx_gpio_pull(int gpio_pin, int pull_mode) +{ + int rc = 0; + rc = gpio_request(gpio_pin, "s5k6aafx"); + if (!rc) + gpio_direction_output(gpio_pin, pull_mode); + else + pr_err("GPIO(%d) request failed\n", gpio_pin); + gpio_free(gpio_pin); + return rc; +} + +static int s5k6aafx_set_sensor_mode(int mode) +{ + switch (mode) { + case SENSOR_PREVIEW_MODE: + pr_info("s5k6aafx:sensor set mode: preview\n"); + op_mode = SENSOR_PREVIEW_MODE; + + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_I2C_MODE, S5K6AAFX_I2C_MODE_GENERAL); + + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_W_ADDH, S5K6AAFX_ADDH_SW_REG_INT); + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_W_ADDL, 0x01F4); + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_WR, 0x0000); /* REG_TC_GP_EnableCapture */ + /* REG_TC_GP_EnableCaptureChanged */ + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_WR, 0x0001); + + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_W_ADDL, 0x0400); + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_WR, 0x007F); + s5k6aafx_set_wb(current_wb); + + break; + + case SENSOR_SNAPSHOT_MODE: + pr_info("s5k6aafx:sensor set mode: snapshot\n"); + op_mode = SENSOR_SNAPSHOT_MODE; + + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_I2C_MODE, S5K6AAFX_I2C_MODE_GENERAL); + + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_W_ADDH, S5K6AAFX_ADDH_SW_REG_INT); + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_W_ADDL, 0x01F4); + /* REG_TC_GP_EnableCapture */ + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_WR, 0x0001); + /* REG_TC_GP_EnableCaptureChanged */ + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_WR, 0x0001); + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int s5k6aafx_set_effect(int effect) +{ + if (op_mode == SENSOR_SNAPSHOT_MODE) + return 0; + + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_I2C_MODE, S5K6AAFX_ADDH_SW_REG_INT); + + switch (effect) { + case CAMERA_EFFECT_OFF: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01EE, 0x0000); + break; + case CAMERA_EFFECT_MONO: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01EE, 0x0001); + break; + case CAMERA_EFFECT_NEGATIVE: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01EE, 0x0002); + break; + case CAMERA_EFFECT_SEPIA: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01EE, 0x0003); + break; + case CAMERA_EFFECT_AQUA: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01EE, 0x0004); + break; + default: + return -EINVAL; + } + return 0; +} + + +static int s5k6aafx_set_antibanding(enum antibanding_mode antibanding_value) +{ + if (op_mode == SENSOR_SNAPSHOT_MODE) + return 0; + + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_I2C_MODE, S5K6AAFX_ADDH_SW_REG_INT); + + switch (antibanding_value) { + case CAMERA_ANTI_BANDING_50HZ: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03DC, 0x0001); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03DE, 0x0001); + break; + case CAMERA_ANTI_BANDING_60HZ: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03DC, 0x0002); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03DE, 0x0001); + break; + case CAMERA_ANTI_BANDING_AUTO: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03DC, 0x0002); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03DE, 0x0001); + break; + } + return 0; +} + + +static int s5k6aafx_set_brightness(enum brightness_t brightness_value) +{ + if (op_mode == SENSOR_SNAPSHOT_MODE) + return 0; + + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_I2C_MODE, S5K6AAFX_ADDH_SW_REG_INT); + + switch (brightness_value) { + case CAMERA_BRIGHTNESS_N4: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E4, 0xFF81); + break; + case CAMERA_BRIGHTNESS_N3: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E4, 0xFFA1); + break; + case CAMERA_BRIGHTNESS_N2: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E4, 0xFFC1); + break; + case CAMERA_BRIGHTNESS_N1: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E4, 0xFFE1); + break; + case CAMERA_BRIGHTNESS_D: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E4, 0x0000); + break; + case CAMERA_BRIGHTNESS_P1: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E4, 0x001F); + break; + case CAMERA_BRIGHTNESS_P2: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E4, 0x003F); + break; + case CAMERA_BRIGHTNESS_P3: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E4, 0x005F); + break; + case CAMERA_BRIGHTNESS_P4: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E4, 0x007F); + break; + default: + break; + } + return 0; +} + +static int s5k6aafx_set_wb(enum wb_mode wb_value) +{ + if (op_mode == SENSOR_SNAPSHOT_MODE) + return 0; + + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_I2C_MODE, S5K6AAFX_ADDH_SW_REG_INT); + + switch (wb_value) { + case CAMERA_AWB_AUTO: /*auto*/ + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x0400, 0x007F); + break; + case CAMERA_AWB_CLOUDY: /*cloudy*/ + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x0400, 0x0077); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D0, 0x0147); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D2, 0x0001); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D4, 0x0100); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D6, 0x0001); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D8, 0x0138); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03DA, 0x0001); + break; + case CAMERA_AWB_INDOOR_HOME: /*Fluorescent*/ + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x0400, 0x0077); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D0, 0x00D9); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D2, 0x0001); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D4, 0x0100); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D6, 0x0001); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D8, 0x0200); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03DA, 0x0001); + break; + case CAMERA_AWB_INDOOR_OFFICE: /*Incandescent*/ + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x0400, 0x0077); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D0, 0x00D9); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D2, 0x0001); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D4, 0x0100); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D6, 0x0001); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D8, 0x0219); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03DA, 0x0001); + break; + case CAMERA_AWB_SUNNY: /*outdoor*/ + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x0400, 0x0077); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D0, 0x0133); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D2, 0x0001); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D4, 0x0100); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D6, 0x0001); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03D8, 0x0119); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x03DA, 0x0001); + break; + default: + break; + } + current_wb = wb_value; + return 0; +} + + +static int s5k6aafx_set_sharpness(enum sharpness_mode sharpness_value) +{ + if (op_mode == SENSOR_SNAPSHOT_MODE) + return 0; + + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_I2C_MODE, S5K6AAFX_ADDH_SW_REG_INT); + + switch (sharpness_value) { + case CAMERA_SHARPNESS_X0: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01EA, 0xFF81); + break; + case CAMERA_SHARPNESS_X1: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01EA, 0xFFC1); + break; + case CAMERA_SHARPNESS_X2: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01EA, 0x0000); + break; + case CAMERA_SHARPNESS_X3: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01EA, 0x003F); + break; + case CAMERA_SHARPNESS_X4: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01EA, 0x007F); + break; + default: + break; + } + return 0; +} + + +static int s5k6aafx_set_saturation(enum saturation_mode saturation_value) +{ + if (op_mode == SENSOR_SNAPSHOT_MODE) + return 0; + + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_I2C_MODE, S5K6AAFX_ADDH_SW_REG_INT); + + switch (saturation_value) { + case CAMERA_SATURATION_X0: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E8, 0xFF81); + break; + case CAMERA_SATURATION_X05: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E8, 0xFFC1); + break; + case CAMERA_SATURATION_X1: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E8, 0x0000); + break; + case CAMERA_SATURATION_X15: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E8, 0x003F); + break; + case CAMERA_SATURATION_X2: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E8, 0x007F); + break; + default: + break; + } + return 0; +} + +static int s5k6aafx_set_contrast(enum contrast_mode contrast_value) +{ + if (op_mode == SENSOR_SNAPSHOT_MODE) + return 0; + + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_I2C_MODE, S5K6AAFX_ADDH_SW_REG_INT); + + switch (contrast_value) { + case CAMERA_CONTRAST_N2: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E6, 0xFF81); + break; + case CAMERA_CONTRAST_N1: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E6, 0xFFC1); + break; + case CAMERA_CONTRAST_D: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E6, 0x0000); + break; + case CAMERA_CONTRAST_P1: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E6, 0x003F); + break; + case CAMERA_CONTRAST_P2: + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x01E6, 0x007F); + break; + default: + break; + } + return 0; +} + +static int s5k6aafx_set_front_camera_mode(enum frontcam_t frontcam_value) +{ + if (op_mode == SENSOR_SNAPSHOT_MODE || previous_mirror_mode == frontcam_value) + return 0; + + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_I2C_MODE, S5K6AAFX_ADDH_SW_REG_INT); + + switch (frontcam_value) { + case CAMERA_MIRROR: + /*mirror and flip*/ + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x02D4, 0x0002); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x02D6, 0x0002); + + break; + case CAMERA_REVERSE: + /*reverse mode*/ + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x02D4, 0x0003); + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x02D6, 0x0003); + + break; + + default: + break; + } + + s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x021E, 0x0001); + + previous_mirror_mode = frontcam_value; + + return 0; +} + +#if 0 +static int s5k6aafx_set_metering_mode(enum aec_metering_mode metering_value) +{ + uint16_t weight_table[32]; + uint8_t i; + + if (op_mode == SENSOR_SNAPSHOT_MODE) + return 0; + + for (i = 0; i < 32; i++) + weight_table[i] = 0x0101; + + if (metering_value == CAMERA_METERING_CENTERING) { + weight_table[9] = 0x0303; + weight_table[10] = 0x0303; + weight_table[13] = 0x0303; /* 0x0305 */ + weight_table[14] = 0x0303; /* 0x0503 */ + weight_table[17] = 0x0303; /* 0x0305 */ + weight_table[18] = 0x0303; /* 0x0503 */ + weight_table[21] = 0x0303; + weight_table[22] = 0x0303; + } else if (metering_value == CAMERA_METERING_SPOT) { + weight_table[13] = 0x0501; + weight_table[14] = 0x0105; + weight_table[17] = 0x0501; + weight_table[18] = 0x0105; + } else if (metering_value >= CAMERA_METERING_ZONE1 && + metering_value <= CAMERA_METERING_ZONE16) { + i = metering_value - CAMERA_METERING_ZONE1; + i += (i & 0xFC); /* i=i+((int)(i/4))*4 */ + weight_table[i] = 0x0505; + weight_table[i+4] = 0x0505; + } + + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_I2C_MODE, S5K6AAFX_I2C_MODE_GENERAL); + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_W_ADDH, S5K6AAFX_ADDH_SW_REG_INT); + s5k6aafx_i2c_write(s5k6aafx_client->addr, S5K6AAFX_REG_W_ADDL, 0x100E); + + for (i = 0; i < 32; i++) { + CDBG("write table[%d]=%x\n", i, weight_table[i]); + s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_WR, weight_table[i]); + } + + return 0; +} +#endif + +static int s5k6aafx_sensor_read_id(const struct msm_camera_sensor_info *data) +{ + uint16_t model_id; + int rc = 0; + + pr_info("s5k6aafx_sensor_read_id\n"); + /* Read the Model ID of the sensor */ + rc = s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_I2C_MODE, S5K6AAFX_I2C_MODE_GENERAL); + if (rc < 0) + goto init_probe_fail; + rc = s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_R_ADDH, S5K6AAFX_ADDH_SW_REG_INT); + if (rc < 0) + goto init_probe_fail; + rc = s5k6aafx_i2c_write(s5k6aafx_client->addr, + S5K6AAFX_REG_R_ADDL, S5K6AAFX_REG_MODEL_ID); + if (rc < 0) + goto init_probe_fail; + rc = s5k6aafx_i2c_read(s5k6aafx_client->addr, + S5K6AAFX_REG_WR, &model_id); + if (rc < 0) + goto init_probe_fail; + + pr_info("s5k6aafx: model_id = 0x%x\n", model_id); + /* Check if it matches it with the value in Datasheet */ + if (model_id != S5K6AAFX_MODEL_ID) { + pr_info("invalid model id\n"); + rc = -EINVAL; + goto init_probe_fail; + } + +init_probe_fail: + return rc; + +} + +int s5k6aafx_sensor_open_init(struct msm_camera_sensor_info *data) +{ + int rc = 0; + s5k6aafx_ctrl = kzalloc(sizeof(struct s5k6aafx_ctrl), GFP_KERNEL); + if (!s5k6aafx_ctrl) { + pr_info("s5k6aafx_init failed!\n"); + rc = -ENOMEM; + goto init_done; + } + + if (data == NULL) { + pr_err("%s sensor data is NULL\n", __func__); + return -EINVAL; + } + s5k6aafx_ctrl->sensordata = data; + + + if (s5k6aafx_gpio_pull(data->sensor_pwd, 1) < 0) + goto init_fail; + mdelay(1); + + /*switch PCLK and MCLK to 2nd cam*/ + pr_info("s5k6aafx: s5k6aafx_sensor_probe switch clk\n"); + if(data->camera_clk_switch != NULL) + data->camera_clk_switch(); + + /*MCLK enable*/ + pr_info("s5k6aafx: MCLK enable clk\n"); + msm_camio_probe_on(s5k6aafx_pdev); + mdelay(3); + + if (s5k6aafx_gpio_pull(data->sensor_reset, 1) < 0) + goto init_fail; + mdelay(10); + msm_camio_camif_pad_reg_reset(); + /*reset sensor*/ + rc = s5k6aafx_i2c_write_table(&s5k6aafx_regs.reset_init[0], + s5k6aafx_regs.reset_init_size); + if (rc < 0) + goto init_fail; + mdelay(100); + + /*T&P setting*/ + rc = s5k6aafx_i2c_write_table(&s5k6aafx_regs.TP_init[0], + s5k6aafx_regs.TP_init_size); + if (rc < 0) + goto init_fail; + + /*analog setting*/ + rc = s5k6aafx_i2c_write_table(&s5k6aafx_regs.analog_setting_init[0], + s5k6aafx_regs.analog_setting_init_size); + if (rc < 0) + goto init_fail; + mdelay(10); + + /*set initial register*/ + rc = s5k6aafx_i2c_write_table(&s5k6aafx_regs.register_init[0], + s5k6aafx_regs.register_init_size); + if (rc < 0) + goto init_fail; + + /*set clock*/ + rc = s5k6aafx_i2c_write_table(&s5k6aafx_regs.clk_init[0], + s5k6aafx_regs.clk_init_size); + + if (rc < 0) + goto init_fail; + mdelay(100); + + /* preview configuration */ + rc = s5k6aafx_i2c_write_table(&s5k6aafx_regs.prev_snap_conf_init[0], + s5k6aafx_regs.prev_snap_conf_init_size); + + if (rc < 0) + goto init_fail; + + rc = s5k6aafx_sensor_read_id(data); + if (rc < 0) + goto init_fail; + + previous_mirror_mode = -1; +init_done: + return rc; + +init_fail: + kfree(s5k6aafx_ctrl); + return rc; +} + +static int s5k6aafx_init_client(struct i2c_client *client) +{ + /* Initialize the MSM_CAMI2C Chip */ + init_waitqueue_head(&s5k6aafx_wait_queue); + return 0; +} + +int s5k6aafx_sensor_config(void __user *argp) +{ + struct sensor_cfg_data cfg_data; + long rc = 0; + if (copy_from_user(&cfg_data, + (void *)argp, sizeof(struct sensor_cfg_data))) + return -EFAULT; + + switch (cfg_data.cfgtype) { + case CFG_SET_MODE: + rc = s5k6aafx_set_sensor_mode(cfg_data.mode); + break; + case CFG_SET_EFFECT: + rc = s5k6aafx_set_effect(cfg_data.cfg.effect); + break; + case CFG_SET_ANTIBANDING: + rc = s5k6aafx_set_antibanding + (cfg_data.cfg.antibanding_value); + break; + case CFG_SET_BRIGHTNESS: + rc = s5k6aafx_set_brightness + (cfg_data.cfg.brightness_value); + break; + case CFG_SET_WB: + rc = s5k6aafx_set_wb(cfg_data.cfg.wb_value); + break; + case CFG_SET_SHARPNESS: + rc = s5k6aafx_set_sharpness + (cfg_data.cfg.sharpness_value); + break; + case CFG_SET_SATURATION: + rc = s5k6aafx_set_saturation + (cfg_data.cfg.saturation_value); + break; + case CFG_SET_CONTRAST: + rc = s5k6aafx_set_contrast(cfg_data.cfg.contrast_value); + break; + case CFG_SET_FRONT_CAMERA_MODE: + rc = s5k6aafx_set_front_camera_mode(cfg_data.cfg.frontcam_value); + break; +#if 0 + case CFG_SET_EXPOSURE_MODE: + rc = s5k6aafx_set_metering_mode + (cfg_data.cfg.metering_value); + break; +#endif + default: + rc = -EINVAL; + break; + } + + return rc; +} + +int s5k6aafx_sensor_release(void) +{ + int rc = 0; + down(&s5k6aafx_sem); + rc = s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x022e, 0x01); + rc = s5k6aafx_i2c_write(s5k6aafx_client->addr, 0x0230, 0x01); + mdelay(133); + + if (s5k6aafx_ctrl) + s5k6aafx_gpio_pull(s5k6aafx_ctrl->sensordata->sensor_reset, 0); + mdelay(1); + msm_camio_probe_off(s5k6aafx_pdev); + mdelay(1); + if (s5k6aafx_ctrl) + s5k6aafx_gpio_pull(s5k6aafx_ctrl->sensordata->sensor_pwd, 0); + + if (s5k6aafx_ctrl) { + kfree(s5k6aafx_ctrl); + s5k6aafx_ctrl = NULL; + } + + up(&s5k6aafx_sem); + + return rc; +} + +static const char *S5K6AAFXVendor = "Samsung"; +static const char *S5K6AAFXNAME = "s5k6aafx"; +static const char *S5K6AAFXSize = "1M"; +static uint32_t htcwc_value; + +static ssize_t sensor_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + + sprintf(buf, "%s %s %s\n", S5K6AAFXVendor, S5K6AAFXNAME, S5K6AAFXSize); + ret = strlen(buf) + 1; + + return ret; +} + +static ssize_t htcwc_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + length = sprintf(buf, "%d\n", htcwc_value); + return length; +} + +static ssize_t htcwc_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + uint32_t tmp = 0; + + tmp = buf[0] - 0x30; /* only get the first char */ + +#if 0 + if (strcmp(current->comm,"com.android.camera")!=0){ + pr_info("No permission : not camera ap\n"); + return -EINVAL; + } +#endif + + htcwc_value = tmp; + //pr_info("current_comm = %s\n", current->comm); + pr_info("htcwc_value = %d\n", htcwc_value); + return count; +} + +static ssize_t sensor_read_node(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + length = sprintf(buf, "%d\n", sensor_probe_node); + return length; +} + +static DEVICE_ATTR(sensor, 0444, sensor_vendor_show, NULL); +static DEVICE_ATTR(htcwc, 0777, htcwc_get, htcwc_set); +static DEVICE_ATTR(node, 0444, sensor_read_node, NULL); + +static struct kobject *android_s5k6aafx; + +static int s5k6aafx_sysfs_init(void) +{ + int ret ; + pr_info("s5k6aafx:kobject creat and add\n"); + android_s5k6aafx = kobject_create_and_add("android_camera2", NULL); + if (android_s5k6aafx == NULL) { + pr_info("s5k6aafx_sysfs_init: subsystem_register " \ + "failed\n"); + ret = -ENOMEM; + return ret ; + } + pr_info("s5k6aafx:sysfs_create_file\n"); + ret = sysfs_create_file(android_s5k6aafx, &dev_attr_sensor.attr); + if (ret) { + pr_info("s5k6aafx_sysfs_init: sysfs_create_file " \ + "failed\n"); + kobject_del(android_s5k6aafx); + } + + ret = sysfs_create_file(android_s5k6aafx, &dev_attr_htcwc.attr); + if (ret) { + pr_info("s5k6aafx_sysfs_init: sysfs_create_file htcwc failed\n"); + kobject_del(android_s5k6aafx); + } + + ret = sysfs_create_file(android_s5k6aafx, &dev_attr_node.attr); + if (ret) { + pr_info("s5k6aafx_sysfs_init: dev_attr_node failed\n"); + kobject_del(android_s5k6aafx); + } + + return 0 ; +} + + +static int s5k6aafx_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + rc = -ENOTSUPP; + goto probe_failure; + } + + s5k6aafx_sensorw = kzalloc(sizeof(struct s5k6aafx_work), GFP_KERNEL); + + if (!s5k6aafx_sensorw) { + rc = -ENOMEM; + goto probe_failure; + } + + i2c_set_clientdata(client, s5k6aafx_sensorw); + s5k6aafx_init_client(client); + s5k6aafx_client = client; + + pr_info("s5k6aafx_probe succeeded!\n"); + + return 0; + +probe_failure: + kfree(s5k6aafx_sensorw); + s5k6aafx_sensorw = NULL; + pr_info("s5k6aafx_probe failed!\n"); + return rc; +} + +static const struct i2c_device_id s5k6aafx_i2c_id[] = { + {"s5k6aafx", 0}, + {}, +}; + +static struct i2c_driver s5k6aafx_i2c_driver = { + .id_table = s5k6aafx_i2c_id, + .probe = s5k6aafx_i2c_probe, + .remove = __exit_p(s5k6aafx_i2c_remove), + .driver = { + .name = "s5k6aafx", + }, +}; + +static int s5k6aafx_sensor_probe(struct msm_camera_sensor_info *info, + struct msm_sensor_ctrl *s) +{ + int rc = i2c_add_driver(&s5k6aafx_i2c_driver); + if (rc < 0 || s5k6aafx_client == NULL) { + rc = -ENOTSUPP; + goto probe_done; + } + + pr_info("s5k6aafx s->node %d\n", s->node); + sensor_probe_node = s->node; + /*2nd camera pwd*/ + if (s5k6aafx_gpio_pull(info->sensor_pwd, 1) < 0) + goto probe_fail; + /*main camera pwd*/ + if (s5k6aafx_gpio_pull(105, 0) < 0) + goto probe_fail; + mdelay(5); + + /*switch clk source*/ + pr_info("s5k6aafx: s5k6aafx_sensor_probe switch clk\n"); + if(info->camera_clk_switch != NULL) + info->camera_clk_switch(); + + /*MCLK enable*/ + pr_info("s5k6aafx: MCLK enable clk\n"); + msm_camio_probe_on(s5k6aafx_pdev); + mdelay(10); + + if (s5k6aafx_gpio_pull(info->sensor_reset, 1) < 0) + goto probe_fail; + mdelay(10); + + rc = s5k6aafx_sensor_read_id(info); + if (rc < 0) + goto probe_fail; + if (info->camera_main_set_probe != NULL) + info->camera_main_set_probe(true); + s->s_init = s5k6aafx_sensor_open_init; + s->s_release = s5k6aafx_sensor_release; + s->s_config = s5k6aafx_sensor_config; + + /*init done*/ + msm_camio_probe_off(s5k6aafx_pdev); + + s5k6aafx_gpio_pull(info->sensor_pwd, 0); + mdelay(5); + + s5k6aafx_sysfs_init(); + + mdelay(5); + +probe_done: + pr_info("%s %s:%d\n", __FILE__, __func__, __LINE__); + return rc; +probe_fail: + msm_camio_probe_off(s5k6aafx_pdev); + pr_err("S5K6AAFX probe failed\n"); + return rc; + +} + +static int __s5k6aafx_probe(struct platform_device *pdev) +{ + struct msm_camera_sensor_info *sdata = pdev->dev.platform_data; + + s5k6aafx_pdev = pdev; + if (sdata->camera_main_get_probe != NULL) { + if (sdata->camera_main_get_probe()) { + pr_info("__s5k6aafx_probe camera main get probed already.\n"); + return 0; + } + } + /*init reset /1 pwd/2pwd*/ + s5k6aafx_gpio_pull(sdata->sensor_pwd, 0); + s5k6aafx_gpio_pull(105, 1); + mdelay(10); + s5k6aafx_gpio_pull(sdata->sensor_reset, 0); + return msm_camera_drv_start(pdev, s5k6aafx_sensor_probe); +} + +static struct platform_driver msm_camera_driver = { + .probe = __s5k6aafx_probe, + .driver = { + .name = "msm_camera_s5k6aafx", + .owner = THIS_MODULE, + }, +}; + +static int __init s5k6aafx_init(void) +{ + + return platform_driver_register(&msm_camera_driver); +} + +module_init(s5k6aafx_init); diff --git a/drivers/media/video/msm/s5k6aafx.h b/drivers/media/video/msm/s5k6aafx.h new file mode 100644 index 0000000000000..43d20b1d791b0 --- /dev/null +++ b/drivers/media/video/msm/s5k6aafx.h @@ -0,0 +1,80 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#ifndef S5K6AAFX_H +#define S5K6AAFX_H + +#include +#include + +/* S5K6AAFX Registers and their values */ +/* Sensor Core Registers */ +#define S5K6AAFX_REG_I2C_MODE 0xFCFC +#define S5K6AAFX_I2C_MODE_SENSOR 0x0000 +#define S5K6AAFX_I2C_MODE_GENERAL 0xD000 + +#define S5K6AAFX_REG_MODEL_ID 0x0152 +#define S5K6AAFX_MODEL_ID 0x06AA + +/* Mode select register */ +#define S5K6AAFX_REG_MODE_SELECT 0x107E +#define S5K6AAFX_MODE_SELECT_STREAM 0x0000 +#define S5K6AAFX_MODE_SELECT_SW_STANDBY 0x0001 + +#define S5K6AAFX_ADDH_SW_REG_INT 0x7000 +#define S5K6AAFX_REG_W_ADDH 0x0028 +#define S5K6AAFX_REG_W_ADDL 0x002A +#define S5K6AAFX_REG_R_ADDH 0x002C +#define S5K6AAFX_REG_R_ADDL 0x002E +#define S5K6AAFX_REG_WR 0x0F12 + +#define S5K6AAFX_QTR_SIZE_WIDTH 0x0280 +#define S5K6AAFX_QTR_SIZE_HEIGHT 0x01E0 +#define S5K6AAFX_FULL_SIZE_WIDTH 0x0500 +#define S5K6AAFX_FULL_SIZE_HEIGHT 0x0400 +#define S5K6AAFX_ADJ_FULL_SIZE_WIDTH S5K6AAFX_QTR_SIZE_WIDTH*2 +#define S5K6AAFX_ADJ_FULL_SIZE_HEIGHT S5K6AAFX_QTR_SIZE_HEIGHT*2 + +extern struct s5k6aafx_reg s5k6aafx_regs; + +struct s5k6aafx_i2c_reg_conf { + unsigned short waddr; + unsigned short wdata; +}; + +struct s5k6aafx_reg { + const struct s5k6aafx_i2c_reg_conf *reset_init; + uint16_t reset_init_size; + const struct s5k6aafx_i2c_reg_conf *TP_init; + uint16_t TP_init_size; + const struct s5k6aafx_i2c_reg_conf *analog_setting_init; + uint16_t analog_setting_init_size; + const struct s5k6aafx_i2c_reg_conf *register_init; + uint16_t register_init_size; + const struct s5k6aafx_i2c_reg_conf *clk_init; + uint16_t clk_init_size; + const struct s5k6aafx_i2c_reg_conf *prev_snap_conf_init; + uint16_t prev_snap_conf_init_size; + /* for full-size preview */ + const struct s5k6aafx_i2c_reg_conf *clk_init_tb2; + uint16_t clk_init_tb2_size; + const struct s5k6aafx_i2c_reg_conf *prev_snap_conf_init_tb2; + uint16_t prev_snap_conf_init_tb2_size; +}; + +#endif /* S5K6AAFX_H */ diff --git a/drivers/media/video/msm/s5k6aafx_reg_mecha.c b/drivers/media/video/msm/s5k6aafx_reg_mecha.c new file mode 100644 index 0000000000000..fc54aa2035c62 --- /dev/null +++ b/drivers/media/video/msm/s5k6aafx_reg_mecha.c @@ -0,0 +1,2647 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ + +#include "s5k6aafx.h" + +static const struct s5k6aafx_i2c_reg_conf const reset_init_tbl[] = { + /* change to general mode */ + {S5K6AAFX_REG_I2C_MODE, S5K6AAFX_I2C_MODE_GENERAL}, + {0x0010, 0x0001}, /* Reset */ + {0x1030, 0x0000}, /* Clear host interrupt so main will wait */ + {0x0014, 0x0001}, /* ARM go */ + /* delay 100ms */ +}; + +static const struct s5k6aafx_i2c_reg_conf const TP_init_tbl[] = { + + /* Start T&P part */ + /* DO NOT DELETE T&P SECTION COMMENTS! They are required to debug T&P related issues. */ + /* svn://transrdsrv/svn/svnroot/System/Software/tcevb/SDK+FW/ISP_Oscar/Firmware */ + /* Rev: 33110-33110 */ + /* Signature: */ + /* md5 f0ba942df15b96de5c09e6cf13fed9c9 .btp */ + /* md5 8bc59f72129cb36e6f6db4be5ddca1f6 .htp */ + /* md5 954ec97efcabad291d89f63e29f32490 .RegsMap.h */ + /* md5 5c29fe50b51e7e860313f5b3b6452bfd .RegsMap.bin */ + /* md5 6211407baaa234b753431cde4ba32402 .base.RegsMap.h */ + /* md5 90cc21d42cc5f02eb80b2586e5c46d9b .base.RegsMap.bin */ + + {S5K6AAFX_REG_W_ADDH, S5K6AAFX_ADDH_SW_REG_INT}, + {S5K6AAFX_REG_W_ADDL, 0x1D60}, + {S5K6AAFX_REG_WR, 0xB570}, + {S5K6AAFX_REG_WR, 0x4936}, + {S5K6AAFX_REG_WR, 0x4836}, + {S5K6AAFX_REG_WR, 0x2205}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xFA4E}, + {S5K6AAFX_REG_WR, 0x4935}, + {S5K6AAFX_REG_WR, 0x2002}, + {S5K6AAFX_REG_WR, 0x83C8}, + {S5K6AAFX_REG_WR, 0x2001}, + {S5K6AAFX_REG_WR, 0x3120}, + {S5K6AAFX_REG_WR, 0x8088}, + {S5K6AAFX_REG_WR, 0x4933}, + {S5K6AAFX_REG_WR, 0x0200}, + {S5K6AAFX_REG_WR, 0x8008}, + {S5K6AAFX_REG_WR, 0x4933}, + {S5K6AAFX_REG_WR, 0x8048}, + {S5K6AAFX_REG_WR, 0x4933}, + {S5K6AAFX_REG_WR, 0x4833}, + {S5K6AAFX_REG_WR, 0x2204}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xFA3E}, + {S5K6AAFX_REG_WR, 0x4932}, + {S5K6AAFX_REG_WR, 0x4833}, + {S5K6AAFX_REG_WR, 0x2206}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xFA39}, + {S5K6AAFX_REG_WR, 0x4932}, + {S5K6AAFX_REG_WR, 0x4832}, + {S5K6AAFX_REG_WR, 0x2207}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xFA34}, + {S5K6AAFX_REG_WR, 0x4931}, + {S5K6AAFX_REG_WR, 0x4832}, + {S5K6AAFX_REG_WR, 0x2208}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xFA2F}, + {S5K6AAFX_REG_WR, 0x4931}, + {S5K6AAFX_REG_WR, 0x4831}, + {S5K6AAFX_REG_WR, 0x2209}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xFA2A}, + {S5K6AAFX_REG_WR, 0x4930}, + {S5K6AAFX_REG_WR, 0x4831}, + {S5K6AAFX_REG_WR, 0x220A}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xFA25}, + {S5K6AAFX_REG_WR, 0x4930}, + {S5K6AAFX_REG_WR, 0x4830}, + {S5K6AAFX_REG_WR, 0x220B}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xFA20}, + {S5K6AAFX_REG_WR, 0x482F}, + {S5K6AAFX_REG_WR, 0x4930}, + {S5K6AAFX_REG_WR, 0x6108}, + {S5K6AAFX_REG_WR, 0x4830}, + {S5K6AAFX_REG_WR, 0x39FF}, + {S5K6AAFX_REG_WR, 0x3901}, + {S5K6AAFX_REG_WR, 0x6748}, + {S5K6AAFX_REG_WR, 0x482F}, + {S5K6AAFX_REG_WR, 0x1C0A}, + {S5K6AAFX_REG_WR, 0x32C0}, + {S5K6AAFX_REG_WR, 0x6390}, + {S5K6AAFX_REG_WR, 0x482E}, + {S5K6AAFX_REG_WR, 0x6708}, + {S5K6AAFX_REG_WR, 0x491A}, + {S5K6AAFX_REG_WR, 0x482D}, + {S5K6AAFX_REG_WR, 0x3108}, + {S5K6AAFX_REG_WR, 0x60C1}, + {S5K6AAFX_REG_WR, 0x6882}, + {S5K6AAFX_REG_WR, 0x1A51}, + {S5K6AAFX_REG_WR, 0x8201}, + {S5K6AAFX_REG_WR, 0x4C2B}, + {S5K6AAFX_REG_WR, 0x2607}, + {S5K6AAFX_REG_WR, 0x6821}, + {S5K6AAFX_REG_WR, 0x0736}, + {S5K6AAFX_REG_WR, 0x42B1}, + {S5K6AAFX_REG_WR, 0xDA05}, + {S5K6AAFX_REG_WR, 0x4829}, + {S5K6AAFX_REG_WR, 0x22D8}, + {S5K6AAFX_REG_WR, 0x1C05}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xFA09}, + {S5K6AAFX_REG_WR, 0x6025}, + {S5K6AAFX_REG_WR, 0x68A1}, + {S5K6AAFX_REG_WR, 0x42B1}, + {S5K6AAFX_REG_WR, 0xDA07}, + {S5K6AAFX_REG_WR, 0x4825}, + {S5K6AAFX_REG_WR, 0x2224}, + {S5K6AAFX_REG_WR, 0x3824}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xFA00}, + {S5K6AAFX_REG_WR, 0x4822}, + {S5K6AAFX_REG_WR, 0x3824}, + {S5K6AAFX_REG_WR, 0x60A0}, + {S5K6AAFX_REG_WR, 0x4D22}, + {S5K6AAFX_REG_WR, 0x6D29}, + {S5K6AAFX_REG_WR, 0x42B1}, + {S5K6AAFX_REG_WR, 0xDA07}, + {S5K6AAFX_REG_WR, 0x481F}, + {S5K6AAFX_REG_WR, 0x228F}, + {S5K6AAFX_REG_WR, 0x00D2}, + {S5K6AAFX_REG_WR, 0x30D8}, + {S5K6AAFX_REG_WR, 0x1C04}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF9F2}, + {S5K6AAFX_REG_WR, 0x652C}, + {S5K6AAFX_REG_WR, 0xBC70}, + {S5K6AAFX_REG_WR, 0xBC08}, + {S5K6AAFX_REG_WR, 0x4718}, + {S5K6AAFX_REG_WR, 0x218B}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x127B}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0398}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x1376}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x2370}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x1F0D}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x890D}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x1F2F}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x27A9}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x1FE1}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x27C5}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x2043}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x285F}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x2003}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x28FF}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x20CD}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x6181}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x20EF}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x6663}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x2123}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x0100}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x1EC1}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x1EAD}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x1F79}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x04AC}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x06CC}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x23A4}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x0704}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0xB510}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF9B9}, + {S5K6AAFX_REG_WR, 0x48C3}, + {S5K6AAFX_REG_WR, 0x49C3}, + {S5K6AAFX_REG_WR, 0x8800}, + {S5K6AAFX_REG_WR, 0x8048}, + {S5K6AAFX_REG_WR, 0xBC10}, + {S5K6AAFX_REG_WR, 0xBC08}, + {S5K6AAFX_REG_WR, 0x4718}, + {S5K6AAFX_REG_WR, 0xB5F8}, + {S5K6AAFX_REG_WR, 0x1C06}, + {S5K6AAFX_REG_WR, 0x4DC0}, + {S5K6AAFX_REG_WR, 0x68AC}, + {S5K6AAFX_REG_WR, 0x1C30}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF9B3}, + {S5K6AAFX_REG_WR, 0x68A9}, + {S5K6AAFX_REG_WR, 0x4ABC}, + {S5K6AAFX_REG_WR, 0x42A1}, + {S5K6AAFX_REG_WR, 0xD003}, + {S5K6AAFX_REG_WR, 0x4BBD}, + {S5K6AAFX_REG_WR, 0x8A1B}, + {S5K6AAFX_REG_WR, 0x3301}, + {S5K6AAFX_REG_WR, 0x8013}, + {S5K6AAFX_REG_WR, 0x8813}, + {S5K6AAFX_REG_WR, 0x1C14}, + {S5K6AAFX_REG_WR, 0x2B00}, + {S5K6AAFX_REG_WR, 0xD00F}, + {S5K6AAFX_REG_WR, 0x2201}, + {S5K6AAFX_REG_WR, 0x4281}, + {S5K6AAFX_REG_WR, 0xD003}, + {S5K6AAFX_REG_WR, 0x8C2F}, + {S5K6AAFX_REG_WR, 0x42B9}, + {S5K6AAFX_REG_WR, 0xD300}, + {S5K6AAFX_REG_WR, 0x2200}, + {S5K6AAFX_REG_WR, 0x60AE}, + {S5K6AAFX_REG_WR, 0x2A00}, + {S5K6AAFX_REG_WR, 0xD003}, + {S5K6AAFX_REG_WR, 0x8C28}, + {S5K6AAFX_REG_WR, 0x42B0}, + {S5K6AAFX_REG_WR, 0xD800}, + {S5K6AAFX_REG_WR, 0x1C30}, + {S5K6AAFX_REG_WR, 0x1E59}, + {S5K6AAFX_REG_WR, 0x8021}, + {S5K6AAFX_REG_WR, 0xBCF8}, + {S5K6AAFX_REG_WR, 0xBC08}, + {S5K6AAFX_REG_WR, 0x4718}, + {S5K6AAFX_REG_WR, 0xB510}, + {S5K6AAFX_REG_WR, 0x1C04}, + {S5K6AAFX_REG_WR, 0x48AF}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF997}, + {S5K6AAFX_REG_WR, 0x4AAD}, + {S5K6AAFX_REG_WR, 0x4BAE}, + {S5K6AAFX_REG_WR, 0x8811}, + {S5K6AAFX_REG_WR, 0x885B}, + {S5K6AAFX_REG_WR, 0x8852}, + {S5K6AAFX_REG_WR, 0x4359}, + {S5K6AAFX_REG_WR, 0x1889}, + {S5K6AAFX_REG_WR, 0x4288}, + {S5K6AAFX_REG_WR, 0xD800}, + {S5K6AAFX_REG_WR, 0x1C08}, + {S5K6AAFX_REG_WR, 0x6020}, + {S5K6AAFX_REG_WR, 0xE7C5}, + {S5K6AAFX_REG_WR, 0xB570}, + {S5K6AAFX_REG_WR, 0x1C05}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF98F}, + {S5K6AAFX_REG_WR, 0x49A5}, + {S5K6AAFX_REG_WR, 0x8989}, + {S5K6AAFX_REG_WR, 0x4348}, + {S5K6AAFX_REG_WR, 0x0200}, + {S5K6AAFX_REG_WR, 0x0C00}, + {S5K6AAFX_REG_WR, 0x2101}, + {S5K6AAFX_REG_WR, 0x0349}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF98E}, + {S5K6AAFX_REG_WR, 0x1C04}, + {S5K6AAFX_REG_WR, 0x489F}, + {S5K6AAFX_REG_WR, 0x8F80}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF991}, + {S5K6AAFX_REG_WR, 0x1C01}, + {S5K6AAFX_REG_WR, 0x20FF}, + {S5K6AAFX_REG_WR, 0x43C0}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF994}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF998}, + {S5K6AAFX_REG_WR, 0x1C01}, + {S5K6AAFX_REG_WR, 0x4898}, + {S5K6AAFX_REG_WR, 0x8840}, + {S5K6AAFX_REG_WR, 0x4360}, + {S5K6AAFX_REG_WR, 0x0200}, + {S5K6AAFX_REG_WR, 0x0C00}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF97A}, + {S5K6AAFX_REG_WR, 0x6028}, + {S5K6AAFX_REG_WR, 0xBC70}, + {S5K6AAFX_REG_WR, 0xBC08}, + {S5K6AAFX_REG_WR, 0x4718}, + {S5K6AAFX_REG_WR, 0xB5F1}, + {S5K6AAFX_REG_WR, 0xB082}, + {S5K6AAFX_REG_WR, 0x4D96}, + {S5K6AAFX_REG_WR, 0x4E91}, + {S5K6AAFX_REG_WR, 0x88A8}, + {S5K6AAFX_REG_WR, 0x1C2C}, + {S5K6AAFX_REG_WR, 0x3420}, + {S5K6AAFX_REG_WR, 0x4F90}, + {S5K6AAFX_REG_WR, 0x2800}, + {S5K6AAFX_REG_WR, 0xD018}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF988}, + {S5K6AAFX_REG_WR, 0x9001}, + {S5K6AAFX_REG_WR, 0x9802}, + {S5K6AAFX_REG_WR, 0x6B39}, + {S5K6AAFX_REG_WR, 0x0200}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF974}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF978}, + {S5K6AAFX_REG_WR, 0x9901}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF95F}, + {S5K6AAFX_REG_WR, 0x8020}, + {S5K6AAFX_REG_WR, 0x8871}, + {S5K6AAFX_REG_WR, 0x0200}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF96A}, + {S5K6AAFX_REG_WR, 0x0400}, + {S5K6AAFX_REG_WR, 0x0C00}, + {S5K6AAFX_REG_WR, 0x21FF}, + {S5K6AAFX_REG_WR, 0x3101}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF97A}, + {S5K6AAFX_REG_WR, 0x8020}, + {S5K6AAFX_REG_WR, 0x88E8}, + {S5K6AAFX_REG_WR, 0x2800}, + {S5K6AAFX_REG_WR, 0xD00A}, + {S5K6AAFX_REG_WR, 0x4980}, + {S5K6AAFX_REG_WR, 0x8820}, + {S5K6AAFX_REG_WR, 0x3128}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF979}, + {S5K6AAFX_REG_WR, 0x8D38}, + {S5K6AAFX_REG_WR, 0x8871}, + {S5K6AAFX_REG_WR, 0x4348}, + {S5K6AAFX_REG_WR, 0x0200}, + {S5K6AAFX_REG_WR, 0x0C00}, + {S5K6AAFX_REG_WR, 0x8538}, + {S5K6AAFX_REG_WR, 0xBCFE}, + {S5K6AAFX_REG_WR, 0xBC08}, + {S5K6AAFX_REG_WR, 0x4718}, + {S5K6AAFX_REG_WR, 0xB510}, + {S5K6AAFX_REG_WR, 0x1C04}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF974}, + {S5K6AAFX_REG_WR, 0x6821}, + {S5K6AAFX_REG_WR, 0x0409}, + {S5K6AAFX_REG_WR, 0x0C09}, + {S5K6AAFX_REG_WR, 0x1A40}, + {S5K6AAFX_REG_WR, 0x4976}, + {S5K6AAFX_REG_WR, 0x6849}, + {S5K6AAFX_REG_WR, 0x4281}, + {S5K6AAFX_REG_WR, 0xD800}, + {S5K6AAFX_REG_WR, 0x1C08}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF971}, + {S5K6AAFX_REG_WR, 0x6020}, + {S5K6AAFX_REG_WR, 0xE75B}, + {S5K6AAFX_REG_WR, 0xB570}, + {S5K6AAFX_REG_WR, 0x6801}, + {S5K6AAFX_REG_WR, 0x040D}, + {S5K6AAFX_REG_WR, 0x0C2D}, + {S5K6AAFX_REG_WR, 0x6844}, + {S5K6AAFX_REG_WR, 0x486F}, + {S5K6AAFX_REG_WR, 0x8981}, + {S5K6AAFX_REG_WR, 0x1C28}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF927}, + {S5K6AAFX_REG_WR, 0x8060}, + {S5K6AAFX_REG_WR, 0x4970}, + {S5K6AAFX_REG_WR, 0x69C9}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF968}, + {S5K6AAFX_REG_WR, 0x1C01}, + {S5K6AAFX_REG_WR, 0x80A0}, + {S5K6AAFX_REG_WR, 0x0228}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF92D}, + {S5K6AAFX_REG_WR, 0x0400}, + {S5K6AAFX_REG_WR, 0x0C00}, + {S5K6AAFX_REG_WR, 0x8020}, + {S5K6AAFX_REG_WR, 0x496B}, + {S5K6AAFX_REG_WR, 0x2300}, + {S5K6AAFX_REG_WR, 0x5EC9}, + {S5K6AAFX_REG_WR, 0x4288}, + {S5K6AAFX_REG_WR, 0xDA02}, + {S5K6AAFX_REG_WR, 0x20FF}, + {S5K6AAFX_REG_WR, 0x3001}, + {S5K6AAFX_REG_WR, 0x8020}, + {S5K6AAFX_REG_WR, 0xE797}, + {S5K6AAFX_REG_WR, 0xB5F8}, + {S5K6AAFX_REG_WR, 0x1C04}, + {S5K6AAFX_REG_WR, 0x4867}, + {S5K6AAFX_REG_WR, 0x4E65}, + {S5K6AAFX_REG_WR, 0x7800}, + {S5K6AAFX_REG_WR, 0x6AB7}, + {S5K6AAFX_REG_WR, 0x2800}, + {S5K6AAFX_REG_WR, 0xD100}, + {S5K6AAFX_REG_WR, 0x6A37}, + {S5K6AAFX_REG_WR, 0x495D}, + {S5K6AAFX_REG_WR, 0x2800}, + {S5K6AAFX_REG_WR, 0x688D}, + {S5K6AAFX_REG_WR, 0xD100}, + {S5K6AAFX_REG_WR, 0x684D}, + {S5K6AAFX_REG_WR, 0x4859}, + {S5K6AAFX_REG_WR, 0x8841}, + {S5K6AAFX_REG_WR, 0x6820}, + {S5K6AAFX_REG_WR, 0x0200}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF94B}, + {S5K6AAFX_REG_WR, 0x8DF1}, + {S5K6AAFX_REG_WR, 0x434F}, + {S5K6AAFX_REG_WR, 0x0A3A}, + {S5K6AAFX_REG_WR, 0x4282}, + {S5K6AAFX_REG_WR, 0xD30C}, + {S5K6AAFX_REG_WR, 0x4D5C}, + {S5K6AAFX_REG_WR, 0x26FF}, + {S5K6AAFX_REG_WR, 0x8829}, + {S5K6AAFX_REG_WR, 0x3601}, + {S5K6AAFX_REG_WR, 0x43B1}, + {S5K6AAFX_REG_WR, 0x8029}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF944}, + {S5K6AAFX_REG_WR, 0x6020}, + {S5K6AAFX_REG_WR, 0x8828}, + {S5K6AAFX_REG_WR, 0x4330}, + {S5K6AAFX_REG_WR, 0x8028}, + {S5K6AAFX_REG_WR, 0xE73B}, + {S5K6AAFX_REG_WR, 0x1C0A}, + {S5K6AAFX_REG_WR, 0x436A}, + {S5K6AAFX_REG_WR, 0x0A12}, + {S5K6AAFX_REG_WR, 0x4282}, + {S5K6AAFX_REG_WR, 0xD304}, + {S5K6AAFX_REG_WR, 0x0200}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF8F3}, + {S5K6AAFX_REG_WR, 0x6020}, + {S5K6AAFX_REG_WR, 0xE7F4}, + {S5K6AAFX_REG_WR, 0x6025}, + {S5K6AAFX_REG_WR, 0xE7F2}, + {S5K6AAFX_REG_WR, 0xB410}, + {S5K6AAFX_REG_WR, 0x4848}, + {S5K6AAFX_REG_WR, 0x4950}, + {S5K6AAFX_REG_WR, 0x89C0}, + {S5K6AAFX_REG_WR, 0x2316}, + {S5K6AAFX_REG_WR, 0x5ECC}, + {S5K6AAFX_REG_WR, 0x1C02}, + {S5K6AAFX_REG_WR, 0x42A0}, + {S5K6AAFX_REG_WR, 0xDC00}, + {S5K6AAFX_REG_WR, 0x1C22}, + {S5K6AAFX_REG_WR, 0x82CA}, + {S5K6AAFX_REG_WR, 0x2318}, + {S5K6AAFX_REG_WR, 0x5ECA}, + {S5K6AAFX_REG_WR, 0x4290}, + {S5K6AAFX_REG_WR, 0xDC00}, + {S5K6AAFX_REG_WR, 0x1C10}, + {S5K6AAFX_REG_WR, 0x8308}, + {S5K6AAFX_REG_WR, 0xBC10}, + {S5K6AAFX_REG_WR, 0x4770}, + {S5K6AAFX_REG_WR, 0xB570}, + {S5K6AAFX_REG_WR, 0x1C06}, + {S5K6AAFX_REG_WR, 0x4C45}, + {S5K6AAFX_REG_WR, 0x2501}, + {S5K6AAFX_REG_WR, 0x8820}, + {S5K6AAFX_REG_WR, 0x02AD}, + {S5K6AAFX_REG_WR, 0x43A8}, + {S5K6AAFX_REG_WR, 0x8020}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF91E}, + {S5K6AAFX_REG_WR, 0x6030}, + {S5K6AAFX_REG_WR, 0xF7FF}, + {S5K6AAFX_REG_WR, 0xFFE0}, + {S5K6AAFX_REG_WR, 0x8820}, + {S5K6AAFX_REG_WR, 0x4328}, + {S5K6AAFX_REG_WR, 0x8020}, + {S5K6AAFX_REG_WR, 0xE741}, + {S5K6AAFX_REG_WR, 0xB570}, + {S5K6AAFX_REG_WR, 0x4C3D}, + {S5K6AAFX_REG_WR, 0x2501}, + {S5K6AAFX_REG_WR, 0x8820}, + {S5K6AAFX_REG_WR, 0x02ED}, + {S5K6AAFX_REG_WR, 0x43A8}, + {S5K6AAFX_REG_WR, 0x8020}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF916}, + {S5K6AAFX_REG_WR, 0xF7FF}, + {S5K6AAFX_REG_WR, 0xFFD1}, + {S5K6AAFX_REG_WR, 0x8820}, + {S5K6AAFX_REG_WR, 0x4328}, + {S5K6AAFX_REG_WR, 0x8020}, + {S5K6AAFX_REG_WR, 0xE732}, + {S5K6AAFX_REG_WR, 0x230D}, + {S5K6AAFX_REG_WR, 0x071B}, + {S5K6AAFX_REG_WR, 0x18C3}, + {S5K6AAFX_REG_WR, 0x8818}, + {S5K6AAFX_REG_WR, 0x2A00}, + {S5K6AAFX_REG_WR, 0xD001}, + {S5K6AAFX_REG_WR, 0x4308}, + {S5K6AAFX_REG_WR, 0xE000}, + {S5K6AAFX_REG_WR, 0x4388}, + {S5K6AAFX_REG_WR, 0x8018}, + {S5K6AAFX_REG_WR, 0x4770}, + {S5K6AAFX_REG_WR, 0xB570}, + {S5K6AAFX_REG_WR, 0x2402}, + {S5K6AAFX_REG_WR, 0x4932}, + {S5K6AAFX_REG_WR, 0x8809}, + {S5K6AAFX_REG_WR, 0x078A}, + {S5K6AAFX_REG_WR, 0xD500}, + {S5K6AAFX_REG_WR, 0x2406}, + {S5K6AAFX_REG_WR, 0x2900}, + {S5K6AAFX_REG_WR, 0xD01F}, + {S5K6AAFX_REG_WR, 0x1C02}, + {S5K6AAFX_REG_WR, 0x207D}, + {S5K6AAFX_REG_WR, 0x00C0}, + {S5K6AAFX_REG_WR, 0x2600}, + {S5K6AAFX_REG_WR, 0x4D2D}, + {S5K6AAFX_REG_WR, 0x2A00}, + {S5K6AAFX_REG_WR, 0xD019}, + {S5K6AAFX_REG_WR, 0x2101}, + {S5K6AAFX_REG_WR, 0x8229}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF8F9}, + {S5K6AAFX_REG_WR, 0x2200}, + {S5K6AAFX_REG_WR, 0x2101}, + {S5K6AAFX_REG_WR, 0x482A}, + {S5K6AAFX_REG_WR, 0x0309}, + {S5K6AAFX_REG_WR, 0xF7FF}, + {S5K6AAFX_REG_WR, 0xFFDB}, + {S5K6AAFX_REG_WR, 0x2008}, + {S5K6AAFX_REG_WR, 0x4304}, + {S5K6AAFX_REG_WR, 0x1C21}, + {S5K6AAFX_REG_WR, 0x4C26}, + {S5K6AAFX_REG_WR, 0x2200}, + {S5K6AAFX_REG_WR, 0x3C14}, + {S5K6AAFX_REG_WR, 0x1C20}, + {S5K6AAFX_REG_WR, 0xF7FF}, + {S5K6AAFX_REG_WR, 0xFFD2}, + {S5K6AAFX_REG_WR, 0x2200}, + {S5K6AAFX_REG_WR, 0x2121}, + {S5K6AAFX_REG_WR, 0x1C20}, + {S5K6AAFX_REG_WR, 0xF7FF}, + {S5K6AAFX_REG_WR, 0xFFCD}, + {S5K6AAFX_REG_WR, 0x802E}, + {S5K6AAFX_REG_WR, 0xE6FD}, + {S5K6AAFX_REG_WR, 0x822E}, + {S5K6AAFX_REG_WR, 0x0789}, + {S5K6AAFX_REG_WR, 0x0FC9}, + {S5K6AAFX_REG_WR, 0x0089}, + {S5K6AAFX_REG_WR, 0x223B}, + {S5K6AAFX_REG_WR, 0x4311}, + {S5K6AAFX_REG_WR, 0x8029}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF8DA}, + {S5K6AAFX_REG_WR, 0xE7F4}, + {S5K6AAFX_REG_WR, 0xB510}, + {S5K6AAFX_REG_WR, 0x491B}, + {S5K6AAFX_REG_WR, 0x8FC8}, + {S5K6AAFX_REG_WR, 0x2800}, + {S5K6AAFX_REG_WR, 0xD007}, + {S5K6AAFX_REG_WR, 0x2000}, + {S5K6AAFX_REG_WR, 0x87C8}, + {S5K6AAFX_REG_WR, 0x8F88}, + {S5K6AAFX_REG_WR, 0x4C19}, + {S5K6AAFX_REG_WR, 0x2800}, + {S5K6AAFX_REG_WR, 0xD002}, + {S5K6AAFX_REG_WR, 0x2008}, + {S5K6AAFX_REG_WR, 0x8020}, + {S5K6AAFX_REG_WR, 0xE689}, + {S5K6AAFX_REG_WR, 0x4815}, + {S5K6AAFX_REG_WR, 0x3060}, + {S5K6AAFX_REG_WR, 0x8900}, + {S5K6AAFX_REG_WR, 0x2800}, + {S5K6AAFX_REG_WR, 0xD103}, + {S5K6AAFX_REG_WR, 0x4814}, + {S5K6AAFX_REG_WR, 0x2101}, + {S5K6AAFX_REG_WR, 0xF000}, + {S5K6AAFX_REG_WR, 0xF8CA}, + {S5K6AAFX_REG_WR, 0x2010}, + {S5K6AAFX_REG_WR, 0x8020}, + {S5K6AAFX_REG_WR, 0xE7F2}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x1376}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x2370}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x14D8}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x235C}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0xF4B0}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x1554}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x1AB8}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x0080}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x046C}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x0468}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x1100}, + {S5K6AAFX_REG_WR, 0xD000}, + {S5K6AAFX_REG_WR, 0x198C}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x0AC4}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0xB0A0}, + {S5K6AAFX_REG_WR, 0xD000}, + {S5K6AAFX_REG_WR, 0xB0B4}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x01B8}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x044E}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x0450}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x9CE7}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xF004}, + {S5K6AAFX_REG_WR, 0xE51F}, + {S5K6AAFX_REG_WR, 0x9FB8}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x14C1}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x27E1}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x88DF}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x275D}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x1ED3}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x27C5}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xF004}, + {S5K6AAFX_REG_WR, 0xE51F}, + {S5K6AAFX_REG_WR, 0xA144}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x1F87}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x27A9}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x1ECB}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x28FF}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x26F9}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x4027}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x9F03}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xF004}, + {S5K6AAFX_REG_WR, 0xE51F}, + {S5K6AAFX_REG_WR, 0x9D9C}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x285F}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x6181}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x6663}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x85D9}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x4778}, + {S5K6AAFX_REG_WR, 0x46C0}, + {S5K6AAFX_REG_WR, 0xC000}, + {S5K6AAFX_REG_WR, 0xE59F}, + {S5K6AAFX_REG_WR, 0xFF1C}, + {S5K6AAFX_REG_WR, 0xE12F}, + {S5K6AAFX_REG_WR, 0x2001}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0xE848}, + {S5K6AAFX_REG_WR, 0x0001}, + {S5K6AAFX_REG_WR, 0xE848}, + {S5K6AAFX_REG_WR, 0x0001}, + {S5K6AAFX_REG_WR, 0x0500}, + {S5K6AAFX_REG_WR, 0x0064}, + {S5K6AAFX_REG_WR, 0x0002}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + /* End T&P part */ +}; + +static const struct s5k6aafx_i2c_reg_conf const analog_setting_init_tbl[] = { + /* Start tuning part */ + + /* Analog Settings */ + {S5K6AAFX_REG_W_ADDL, 0x1102}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_W_ADDL, 0x1108}, + {S5K6AAFX_REG_WR, 0x0090}, + + {0xF40C, 0x0060}, + + {S5K6AAFX_REG_W_ADDL, 0x11B6}, + {S5K6AAFX_REG_WR, 0x0020}, + {S5K6AAFX_REG_WR, 0x0010}, + {S5K6AAFX_REG_WR, 0x0008}, + {S5K6AAFX_REG_WR, 0x0004}, + + {S5K6AAFX_REG_W_ADDL, 0x119C}, + {S5K6AAFX_REG_WR, 0x0040}, + + {S5K6AAFX_REG_W_ADDL, 0x07B6}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0002}, + {S5K6AAFX_REG_WR, 0x0003}, + {S5K6AAFX_REG_WR, 0x0006}, + {S5K6AAFX_REG_WR, 0x000C}, + {S5K6AAFX_REG_WR, 0x0018}, + + {0x1000, 0x0001}, /* Set host interrupt so main start run */ + + /* delay 10ms */ + +}; + +static const struct s5k6aafx_i2c_reg_conf const register_init_tbl[] = { +// Start user init script + +// End user init script + + /* param_start - TVAR_ash_GASAlpha */ + /* parawrite _start - TVAR_ash_GASAlpha */ + {S5K6AAFX_REG_W_ADDL, 0x0712}, + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[0] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[1] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[2] */ + {S5K6AAFX_REG_WR, 0x00B0}, /* TVAR_ash_GASAlpha[3] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[4] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[5] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[6] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[7] */ + {S5K6AAFX_REG_WR, 0x00C8}, /* TVAR_ash_GASAlpha[8] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[9] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[10] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[11] */ + {S5K6AAFX_REG_WR, 0x00D8}, /* TVAR_ash_GASAlpha[12] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[13] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[14] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[15] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[16] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[17] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[18] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[19] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[20] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[21] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[22] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[23] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[24] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[25] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[26] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_GASAlpha[27] */ + /* parawrite _end - TVAR_ash_GASAlpha */ + + /* parawrite _start - TVAR_ash_GASOutdoorAlpha */ + {S5K6AAFX_REG_W_ADDL, 0x074A}, + {S5K6AAFX_REG_WR, 0x00FB}, //TVAR_ash_GASOutdoorAlpha[0] + {S5K6AAFX_REG_WR, 0x00FF}, //TVAR_ash_GASOutdoorAlpha[1] + {S5K6AAFX_REG_WR, 0x00F9}, //TVAR_ash_GASOutdoorAlpha[2] + {S5K6AAFX_REG_WR, 0x0104}, //TVAR_ash_GASOutdoorAlpha[3] + /* parawrite _end - TVAR_ash_GASOutdoorAlpha */ + + {S5K6AAFX_REG_W_ADDL, 0x075A}, + {S5K6AAFX_REG_WR, 0x0001}, /* ash_bParabolicEstiwrite ation */ + {S5K6AAFX_REG_WR, 0x0282}, /* ash_uParabolicCenterX */ + {S5K6AAFX_REG_WR, 0x0216}, /* ash_uParabolicCenterY */ + {S5K6AAFX_REG_WR, 0x000B}, /* ash_uParabolicScalingA */ + {S5K6AAFX_REG_WR, 0x000E}, /* ash_uParabolicScalingB */ + + /* parawrite _start - TVAR_ash_pGAS */ + {S5K6AAFX_REG_W_ADDL, 0x247C}, + {S5K6AAFX_REG_WR, 0x02B2}, /* TVAR_ash_pGAS[0] */ + {S5K6AAFX_REG_WR, 0x01C3}, /* TVAR_ash_pGAS[1] */ + {S5K6AAFX_REG_WR, 0x0138}, /* TVAR_ash_pGAS[2] */ + {S5K6AAFX_REG_WR, 0x0100}, /* TVAR_ash_pGAS[3] */ + {S5K6AAFX_REG_WR, 0x00E5}, /* TVAR_ash_pGAS[4] */ + {S5K6AAFX_REG_WR, 0x00D1}, /* TVAR_ash_pGAS[5] */ + {S5K6AAFX_REG_WR, 0x00C6}, /* TVAR_ash_pGAS[6] */ + {S5K6AAFX_REG_WR, 0x00CA}, /* TVAR_ash_pGAS[7] */ + {S5K6AAFX_REG_WR, 0x00DA}, /* TVAR_ash_pGAS[8] */ + {S5K6AAFX_REG_WR, 0x00EE}, /* TVAR_ash_pGAS[9] */ + {S5K6AAFX_REG_WR, 0x0110}, /* TVAR_ash_pGAS[10] */ + {S5K6AAFX_REG_WR, 0x0179}, /* TVAR_ash_pGAS[11] */ + {S5K6AAFX_REG_WR, 0x0232}, /* TVAR_ash_pGAS[12] */ + {S5K6AAFX_REG_WR, 0x01EC}, /* TVAR_ash_pGAS[13] */ + {S5K6AAFX_REG_WR, 0x0148}, /* TVAR_ash_pGAS[14] */ + {S5K6AAFX_REG_WR, 0x00F3}, /* TVAR_ash_pGAS[15] */ + {S5K6AAFX_REG_WR, 0x00C7}, /* TVAR_ash_pGAS[16] */ + {S5K6AAFX_REG_WR, 0x00A3}, /* TVAR_ash_pGAS[17] */ + {S5K6AAFX_REG_WR, 0x0089}, /* TVAR_ash_pGAS[18] */ + {S5K6AAFX_REG_WR, 0x007A}, /* TVAR_ash_pGAS[19] */ + {S5K6AAFX_REG_WR, 0x0081}, /* TVAR_ash_pGAS[20] */ + {S5K6AAFX_REG_WR, 0x0093}, /* TVAR_ash_pGAS[21] */ + {S5K6AAFX_REG_WR, 0x00AF}, /* TVAR_ash_pGAS[22] */ + {S5K6AAFX_REG_WR, 0x00CF}, /* TVAR_ash_pGAS[23] */ + {S5K6AAFX_REG_WR, 0x010A}, /* TVAR_ash_pGAS[24] */ + {S5K6AAFX_REG_WR, 0x0181}, /* TVAR_ash_pGAS[25] */ + {S5K6AAFX_REG_WR, 0x015D}, /* TVAR_ash_pGAS[26] */ + {S5K6AAFX_REG_WR, 0x00F2}, /* TVAR_ash_pGAS[27] */ + {S5K6AAFX_REG_WR, 0x00B5}, /* TVAR_ash_pGAS[28] */ + {S5K6AAFX_REG_WR, 0x0083}, /* TVAR_ash_pGAS[29] */ + {S5K6AAFX_REG_WR, 0x0058}, /* TVAR_ash_pGAS[30] */ + {S5K6AAFX_REG_WR, 0x003F}, /* TVAR_ash_pGAS[31] */ + {S5K6AAFX_REG_WR, 0x0036}, /* TVAR_ash_pGAS[32] */ + {S5K6AAFX_REG_WR, 0x0038}, /* TVAR_ash_pGAS[33] */ + {S5K6AAFX_REG_WR, 0x0048}, /* TVAR_ash_pGAS[34] */ + {S5K6AAFX_REG_WR, 0x0065}, /* TVAR_ash_pGAS[35] */ + {S5K6AAFX_REG_WR, 0x008E}, /* TVAR_ash_pGAS[36] */ + {S5K6AAFX_REG_WR, 0x00C0}, /* TVAR_ash_pGAS[37] */ + {S5K6AAFX_REG_WR, 0x010A}, /* TVAR_ash_pGAS[38] */ + {S5K6AAFX_REG_WR, 0x0119}, /* TVAR_ash_pGAS[39] */ + {S5K6AAFX_REG_WR, 0x00C7}, /* TVAR_ash_pGAS[40] */ + {S5K6AAFX_REG_WR, 0x008A}, /* TVAR_ash_pGAS[41] */ + {S5K6AAFX_REG_WR, 0x0056}, /* TVAR_ash_pGAS[42] */ + {S5K6AAFX_REG_WR, 0x0030}, /* TVAR_ash_pGAS[43] */ + {S5K6AAFX_REG_WR, 0x001B}, /* TVAR_ash_pGAS[44] */ + {S5K6AAFX_REG_WR, 0x0012}, /* TVAR_ash_pGAS[45] */ + {S5K6AAFX_REG_WR, 0x0011}, /* TVAR_ash_pGAS[46] */ + {S5K6AAFX_REG_WR, 0x001C}, /* TVAR_ash_pGAS[47] */ + {S5K6AAFX_REG_WR, 0x0036}, /* TVAR_ash_pGAS[48] */ + {S5K6AAFX_REG_WR, 0x005F}, /* TVAR_ash_pGAS[49] */ + {S5K6AAFX_REG_WR, 0x0096}, /* TVAR_ash_pGAS[50] */ + {S5K6AAFX_REG_WR, 0x00D2}, /* TVAR_ash_pGAS[51] */ + {S5K6AAFX_REG_WR, 0x00FA}, /* TVAR_ash_pGAS[52] */ + {S5K6AAFX_REG_WR, 0x00B7}, /* TVAR_ash_pGAS[53] */ + {S5K6AAFX_REG_WR, 0x0073}, /* TVAR_ash_pGAS[54] */ + {S5K6AAFX_REG_WR, 0x003D}, /* TVAR_ash_pGAS[55] */ + {S5K6AAFX_REG_WR, 0x001B}, /* TVAR_ash_pGAS[56] */ + {S5K6AAFX_REG_WR, 0x000C}, /* TVAR_ash_pGAS[57] */ + {S5K6AAFX_REG_WR, 0x0004}, /* TVAR_ash_pGAS[58] */ + {S5K6AAFX_REG_WR, 0x0004}, /* TVAR_ash_pGAS[59] */ + {S5K6AAFX_REG_WR, 0x0008}, /* TVAR_ash_pGAS[60] */ + {S5K6AAFX_REG_WR, 0x001C}, /* TVAR_ash_pGAS[61] */ + {S5K6AAFX_REG_WR, 0x0045}, /* TVAR_ash_pGAS[62] */ + {S5K6AAFX_REG_WR, 0x0083}, /* TVAR_ash_pGAS[63] */ + {S5K6AAFX_REG_WR, 0x00BA}, /* TVAR_ash_pGAS[64] */ + {S5K6AAFX_REG_WR, 0x00F2}, /* TVAR_ash_pGAS[65] */ + {S5K6AAFX_REG_WR, 0x00B2}, /* TVAR_ash_pGAS[66] */ + {S5K6AAFX_REG_WR, 0x006B}, /* TVAR_ash_pGAS[67] */ + {S5K6AAFX_REG_WR, 0x0034}, /* TVAR_ash_pGAS[68] */ + {S5K6AAFX_REG_WR, 0x0016}, /* TVAR_ash_pGAS[69] */ + {S5K6AAFX_REG_WR, 0x0008}, /* TVAR_ash_pGAS[70] */ + {S5K6AAFX_REG_WR, 0x0000}, /* TVAR_ash_pGAS[71] */ + {S5K6AAFX_REG_WR, 0x0000}, /* TVAR_ash_pGAS[72] */ + {S5K6AAFX_REG_WR, 0x0006}, /* TVAR_ash_pGAS[73] */ + {S5K6AAFX_REG_WR, 0x0018}, /* TVAR_ash_pGAS[74] */ + {S5K6AAFX_REG_WR, 0x003F}, /* TVAR_ash_pGAS[75] */ + {S5K6AAFX_REG_WR, 0x0080}, /* TVAR_ash_pGAS[76] */ + {S5K6AAFX_REG_WR, 0x00BA}, /* TVAR_ash_pGAS[77] */ + {S5K6AAFX_REG_WR, 0x00FD}, /* TVAR_ash_pGAS[78] */ + {S5K6AAFX_REG_WR, 0x00BE}, /* TVAR_ash_pGAS[79] */ + {S5K6AAFX_REG_WR, 0x0075}, /* TVAR_ash_pGAS[80] */ + {S5K6AAFX_REG_WR, 0x003D}, /* TVAR_ash_pGAS[81] */ + {S5K6AAFX_REG_WR, 0x001A}, /* TVAR_ash_pGAS[82] */ + {S5K6AAFX_REG_WR, 0x000B}, /* TVAR_ash_pGAS[83] */ + {S5K6AAFX_REG_WR, 0x0003}, /* TVAR_ash_pGAS[84] */ + {S5K6AAFX_REG_WR, 0x0005}, /* TVAR_ash_pGAS[85] */ + {S5K6AAFX_REG_WR, 0x000D}, /* TVAR_ash_pGAS[86] */ + {S5K6AAFX_REG_WR, 0x0022}, /* TVAR_ash_pGAS[87] */ + {S5K6AAFX_REG_WR, 0x004C}, /* TVAR_ash_pGAS[88] */ + {S5K6AAFX_REG_WR, 0x008E}, /* TVAR_ash_pGAS[89] */ + {S5K6AAFX_REG_WR, 0x00CB}, /* TVAR_ash_pGAS[90] */ + {S5K6AAFX_REG_WR, 0x0121}, /* TVAR_ash_pGAS[91] */ + {S5K6AAFX_REG_WR, 0x00DB}, /* TVAR_ash_pGAS[92] */ + {S5K6AAFX_REG_WR, 0x0096}, /* TVAR_ash_pGAS[93] */ + {S5K6AAFX_REG_WR, 0x0057}, /* TVAR_ash_pGAS[94] */ + {S5K6AAFX_REG_WR, 0x002C}, /* TVAR_ash_pGAS[95] */ + {S5K6AAFX_REG_WR, 0x0016}, /* TVAR_ash_pGAS[96] */ + {S5K6AAFX_REG_WR, 0x000F}, /* TVAR_ash_pGAS[97] */ + {S5K6AAFX_REG_WR, 0x0011}, /* TVAR_ash_pGAS[98] */ + {S5K6AAFX_REG_WR, 0x001E}, /* TVAR_ash_pGAS[99] */ + {S5K6AAFX_REG_WR, 0x003B}, /* TVAR_ash_pGAS[100] */ + {S5K6AAFX_REG_WR, 0x006D}, /* TVAR_ash_pGAS[101] */ + {S5K6AAFX_REG_WR, 0x00AE}, /* TVAR_ash_pGAS[102] */ + {S5K6AAFX_REG_WR, 0x00F0}, /* TVAR_ash_pGAS[103] */ + {S5K6AAFX_REG_WR, 0x0163}, /* TVAR_ash_pGAS[104] */ + {S5K6AAFX_REG_WR, 0x0107}, /* TVAR_ash_pGAS[105] */ + {S5K6AAFX_REG_WR, 0x00C6}, /* TVAR_ash_pGAS[106] */ + {S5K6AAFX_REG_WR, 0x0085}, /* TVAR_ash_pGAS[107] */ + {S5K6AAFX_REG_WR, 0x0053}, /* TVAR_ash_pGAS[108] */ + {S5K6AAFX_REG_WR, 0x0034}, /* TVAR_ash_pGAS[109] */ + {S5K6AAFX_REG_WR, 0x0029}, /* TVAR_ash_pGAS[110] */ + {S5K6AAFX_REG_WR, 0x002F}, /* TVAR_ash_pGAS[111] */ + {S5K6AAFX_REG_WR, 0x0042}, /* TVAR_ash_pGAS[112] */ + {S5K6AAFX_REG_WR, 0x0066}, /* TVAR_ash_pGAS[113] */ + {S5K6AAFX_REG_WR, 0x009E}, /* TVAR_ash_pGAS[114] */ + {S5K6AAFX_REG_WR, 0x00DC}, /* TVAR_ash_pGAS[115] */ + {S5K6AAFX_REG_WR, 0x012D}, /* TVAR_ash_pGAS[116] */ + {S5K6AAFX_REG_WR, 0x01E1}, /* TVAR_ash_pGAS[117] */ + {S5K6AAFX_REG_WR, 0x014C}, /* TVAR_ash_pGAS[118] */ + {S5K6AAFX_REG_WR, 0x0102}, /* TVAR_ash_pGAS[119] */ + {S5K6AAFX_REG_WR, 0x00CA}, /* TVAR_ash_pGAS[120] */ + {S5K6AAFX_REG_WR, 0x0096}, /* TVAR_ash_pGAS[121] */ + {S5K6AAFX_REG_WR, 0x0072}, /* TVAR_ash_pGAS[122] */ + {S5K6AAFX_REG_WR, 0x0062}, /* TVAR_ash_pGAS[123] */ + {S5K6AAFX_REG_WR, 0x0068}, /* TVAR_ash_pGAS[124] */ + {S5K6AAFX_REG_WR, 0x007F}, /* TVAR_ash_pGAS[125] */ + {S5K6AAFX_REG_WR, 0x00A9}, /* TVAR_ash_pGAS[126] */ + {S5K6AAFX_REG_WR, 0x00D7}, /* TVAR_ash_pGAS[127] */ + {S5K6AAFX_REG_WR, 0x011B}, /* TVAR_ash_pGAS[128] */ + {S5K6AAFX_REG_WR, 0x0196}, /* TVAR_ash_pGAS[129] */ + {S5K6AAFX_REG_WR, 0x029C}, /* TVAR_ash_pGAS[130] */ + {S5K6AAFX_REG_WR, 0x01C0}, /* TVAR_ash_pGAS[131] */ + {S5K6AAFX_REG_WR, 0x0144}, /* TVAR_ash_pGAS[132] */ + {S5K6AAFX_REG_WR, 0x0108}, /* TVAR_ash_pGAS[133] */ + {S5K6AAFX_REG_WR, 0x00DE}, /* TVAR_ash_pGAS[134] */ + {S5K6AAFX_REG_WR, 0x00BB}, /* TVAR_ash_pGAS[135] */ + {S5K6AAFX_REG_WR, 0x00AB}, /* TVAR_ash_pGAS[136] */ + {S5K6AAFX_REG_WR, 0x00AC}, /* TVAR_ash_pGAS[137] */ + {S5K6AAFX_REG_WR, 0x00C7}, /* TVAR_ash_pGAS[138] */ + {S5K6AAFX_REG_WR, 0x00E8}, /* TVAR_ash_pGAS[139] */ + {S5K6AAFX_REG_WR, 0x011A}, /* TVAR_ash_pGAS[140] */ + {S5K6AAFX_REG_WR, 0x017B}, /* TVAR_ash_pGAS[141] */ + {S5K6AAFX_REG_WR, 0x0222}, /* TVAR_ash_pGAS[142] */ + {S5K6AAFX_REG_WR, 0x0281}, /* TVAR_ash_pGAS[143] */ + {S5K6AAFX_REG_WR, 0x019C}, /* TVAR_ash_pGAS[144] */ + {S5K6AAFX_REG_WR, 0x011A}, /* TVAR_ash_pGAS[145] */ + {S5K6AAFX_REG_WR, 0x00E7}, /* TVAR_ash_pGAS[146] */ + {S5K6AAFX_REG_WR, 0x00CF}, /* TVAR_ash_pGAS[147] */ + {S5K6AAFX_REG_WR, 0x00BE}, /* TVAR_ash_pGAS[148] */ + {S5K6AAFX_REG_WR, 0x00B3}, /* TVAR_ash_pGAS[149] */ + {S5K6AAFX_REG_WR, 0x00B2}, /* TVAR_ash_pGAS[150] */ + {S5K6AAFX_REG_WR, 0x00BA}, /* TVAR_ash_pGAS[151] */ + {S5K6AAFX_REG_WR, 0x00C7}, /* TVAR_ash_pGAS[152] */ + {S5K6AAFX_REG_WR, 0x00E0}, /* TVAR_ash_pGAS[153] */ + {S5K6AAFX_REG_WR, 0x0139}, /* TVAR_ash_pGAS[154] */ + {S5K6AAFX_REG_WR, 0x01E4}, /* TVAR_ash_pGAS[155] */ + {S5K6AAFX_REG_WR, 0x01B4}, /* TVAR_ash_pGAS[156] */ + {S5K6AAFX_REG_WR, 0x011D}, /* TVAR_ash_pGAS[157] */ + {S5K6AAFX_REG_WR, 0x00D8}, /* TVAR_ash_pGAS[158] */ + {S5K6AAFX_REG_WR, 0x00B4}, /* TVAR_ash_pGAS[159] */ + {S5K6AAFX_REG_WR, 0x0093}, /* TVAR_ash_pGAS[160] */ + {S5K6AAFX_REG_WR, 0x007B}, /* TVAR_ash_pGAS[161] */ + {S5K6AAFX_REG_WR, 0x0070}, /* TVAR_ash_pGAS[162] */ + {S5K6AAFX_REG_WR, 0x0072}, /* TVAR_ash_pGAS[163] */ + {S5K6AAFX_REG_WR, 0x007F}, /* TVAR_ash_pGAS[164] */ + {S5K6AAFX_REG_WR, 0x0091}, /* TVAR_ash_pGAS[165] */ + {S5K6AAFX_REG_WR, 0x00A9}, /* TVAR_ash_pGAS[166] */ + {S5K6AAFX_REG_WR, 0x00D6}, /* TVAR_ash_pGAS[167] */ + {S5K6AAFX_REG_WR, 0x0142}, /* TVAR_ash_pGAS[168] */ + {S5K6AAFX_REG_WR, 0x013A}, /* TVAR_ash_pGAS[169] */ + {S5K6AAFX_REG_WR, 0x00D3}, /* TVAR_ash_pGAS[170] */ + {S5K6AAFX_REG_WR, 0x00AA}, /* TVAR_ash_pGAS[171] */ + {S5K6AAFX_REG_WR, 0x007C}, /* TVAR_ash_pGAS[172] */ + {S5K6AAFX_REG_WR, 0x0055}, /* TVAR_ash_pGAS[173] */ + {S5K6AAFX_REG_WR, 0x003D}, /* TVAR_ash_pGAS[174] */ + {S5K6AAFX_REG_WR, 0x0035}, /* TVAR_ash_pGAS[175] */ + {S5K6AAFX_REG_WR, 0x0036}, /* TVAR_ash_pGAS[176] */ + {S5K6AAFX_REG_WR, 0x0044}, /* TVAR_ash_pGAS[177] */ + {S5K6AAFX_REG_WR, 0x005B}, /* TVAR_ash_pGAS[178] */ + {S5K6AAFX_REG_WR, 0x007A}, /* TVAR_ash_pGAS[179] */ + {S5K6AAFX_REG_WR, 0x009E}, /* TVAR_ash_pGAS[180] */ + {S5K6AAFX_REG_WR, 0x00DF}, /* TVAR_ash_pGAS[181] */ + {S5K6AAFX_REG_WR, 0x00F9}, /* TVAR_ash_pGAS[182] */ + {S5K6AAFX_REG_WR, 0x00B5}, /* TVAR_ash_pGAS[183] */ + {S5K6AAFX_REG_WR, 0x0083}, /* TVAR_ash_pGAS[184] */ + {S5K6AAFX_REG_WR, 0x0052}, /* TVAR_ash_pGAS[185] */ + {S5K6AAFX_REG_WR, 0x002D}, /* TVAR_ash_pGAS[186] */ + {S5K6AAFX_REG_WR, 0x001B}, /* TVAR_ash_pGAS[187] */ + {S5K6AAFX_REG_WR, 0x0013}, /* TVAR_ash_pGAS[188] */ + {S5K6AAFX_REG_WR, 0x0012}, /* TVAR_ash_pGAS[189] */ + {S5K6AAFX_REG_WR, 0x001B}, /* TVAR_ash_pGAS[190] */ + {S5K6AAFX_REG_WR, 0x0031}, /* TVAR_ash_pGAS[191] */ + {S5K6AAFX_REG_WR, 0x0055}, /* TVAR_ash_pGAS[192] */ + {S5K6AAFX_REG_WR, 0x007F}, /* TVAR_ash_pGAS[193] */ + {S5K6AAFX_REG_WR, 0x00AF}, /* TVAR_ash_pGAS[194] */ + {S5K6AAFX_REG_WR, 0x00E0}, /* TVAR_ash_pGAS[195] */ + {S5K6AAFX_REG_WR, 0x00A6}, /* TVAR_ash_pGAS[196] */ + {S5K6AAFX_REG_WR, 0x006C}, /* TVAR_ash_pGAS[197] */ + {S5K6AAFX_REG_WR, 0x0039}, /* TVAR_ash_pGAS[198] */ + {S5K6AAFX_REG_WR, 0x001A}, /* TVAR_ash_pGAS[199] */ + {S5K6AAFX_REG_WR, 0x000D}, /* TVAR_ash_pGAS[200] */ + {S5K6AAFX_REG_WR, 0x0007}, /* TVAR_ash_pGAS[201] */ + {S5K6AAFX_REG_WR, 0x0005}, /* TVAR_ash_pGAS[202] */ + {S5K6AAFX_REG_WR, 0x0008}, /* TVAR_ash_pGAS[203] */ + {S5K6AAFX_REG_WR, 0x0018}, /* TVAR_ash_pGAS[204] */ + {S5K6AAFX_REG_WR, 0x003D}, /* TVAR_ash_pGAS[205] */ + {S5K6AAFX_REG_WR, 0x0070}, /* TVAR_ash_pGAS[206] */ + {S5K6AAFX_REG_WR, 0x009C}, /* TVAR_ash_pGAS[207] */ + {S5K6AAFX_REG_WR, 0x00DA}, /* TVAR_ash_pGAS[208] */ + {S5K6AAFX_REG_WR, 0x00A2}, /* TVAR_ash_pGAS[209] */ + {S5K6AAFX_REG_WR, 0x0065}, /* TVAR_ash_pGAS[210] */ + {S5K6AAFX_REG_WR, 0x0031}, /* TVAR_ash_pGAS[211] */ + {S5K6AAFX_REG_WR, 0x0015}, /* TVAR_ash_pGAS[212] */ + {S5K6AAFX_REG_WR, 0x0009}, /* TVAR_ash_pGAS[213] */ + {S5K6AAFX_REG_WR, 0x0003}, /* TVAR_ash_pGAS[214] */ + {S5K6AAFX_REG_WR, 0x0002}, /* TVAR_ash_pGAS[215] */ + {S5K6AAFX_REG_WR, 0x0005}, /* TVAR_ash_pGAS[216] */ + {S5K6AAFX_REG_WR, 0x0014}, /* TVAR_ash_pGAS[217] */ + {S5K6AAFX_REG_WR, 0x0038}, /* TVAR_ash_pGAS[218] */ + {S5K6AAFX_REG_WR, 0x006D}, /* TVAR_ash_pGAS[219] */ + {S5K6AAFX_REG_WR, 0x009C}, /* TVAR_ash_pGAS[220] */ + {S5K6AAFX_REG_WR, 0x00DF}, /* TVAR_ash_pGAS[221] */ + {S5K6AAFX_REG_WR, 0x00A8}, /* TVAR_ash_pGAS[222] */ + {S5K6AAFX_REG_WR, 0x006B}, /* TVAR_ash_pGAS[223] */ + {S5K6AAFX_REG_WR, 0x0038}, /* TVAR_ash_pGAS[224] */ + {S5K6AAFX_REG_WR, 0x0019}, /* TVAR_ash_pGAS[225] */ + {S5K6AAFX_REG_WR, 0x000C}, /* TVAR_ash_pGAS[226] */ + {S5K6AAFX_REG_WR, 0x0005}, /* TVAR_ash_pGAS[227] */ + {S5K6AAFX_REG_WR, 0x0006}, /* TVAR_ash_pGAS[228] */ + {S5K6AAFX_REG_WR, 0x000B}, /* TVAR_ash_pGAS[229] */ + {S5K6AAFX_REG_WR, 0x001D}, /* TVAR_ash_pGAS[230] */ + {S5K6AAFX_REG_WR, 0x0043}, /* TVAR_ash_pGAS[231] */ + {S5K6AAFX_REG_WR, 0x0075}, /* TVAR_ash_pGAS[232] */ + {S5K6AAFX_REG_WR, 0x00A6}, /* TVAR_ash_pGAS[233] */ + {S5K6AAFX_REG_WR, 0x00FA}, /* TVAR_ash_pGAS[234] */ + {S5K6AAFX_REG_WR, 0x00BE}, /* TVAR_ash_pGAS[235] */ + {S5K6AAFX_REG_WR, 0x0087}, /* TVAR_ash_pGAS[236] */ + {S5K6AAFX_REG_WR, 0x004F}, /* TVAR_ash_pGAS[237] */ + {S5K6AAFX_REG_WR, 0x0028}, /* TVAR_ash_pGAS[238] */ + {S5K6AAFX_REG_WR, 0x0016}, /* TVAR_ash_pGAS[239] */ + {S5K6AAFX_REG_WR, 0x000F}, /* TVAR_ash_pGAS[240] */ + {S5K6AAFX_REG_WR, 0x0010}, /* TVAR_ash_pGAS[241] */ + {S5K6AAFX_REG_WR, 0x001A}, /* TVAR_ash_pGAS[242] */ + {S5K6AAFX_REG_WR, 0x0033}, /* TVAR_ash_pGAS[243] */ + {S5K6AAFX_REG_WR, 0x005D}, /* TVAR_ash_pGAS[244] */ + {S5K6AAFX_REG_WR, 0x008F}, /* TVAR_ash_pGAS[245] */ + {S5K6AAFX_REG_WR, 0x00C2}, /* TVAR_ash_pGAS[246] */ + {S5K6AAFX_REG_WR, 0x0132}, /* TVAR_ash_pGAS[247] */ + {S5K6AAFX_REG_WR, 0x00DF}, /* TVAR_ash_pGAS[248] */ + {S5K6AAFX_REG_WR, 0x00B0}, /* TVAR_ash_pGAS[249] */ + {S5K6AAFX_REG_WR, 0x0077}, /* TVAR_ash_pGAS[250] */ + {S5K6AAFX_REG_WR, 0x004A}, /* TVAR_ash_pGAS[251] */ + {S5K6AAFX_REG_WR, 0x0031}, /* TVAR_ash_pGAS[252] */ + {S5K6AAFX_REG_WR, 0x0027}, /* TVAR_ash_pGAS[253] */ + {S5K6AAFX_REG_WR, 0x002B}, /* TVAR_ash_pGAS[254] */ + {S5K6AAFX_REG_WR, 0x003A}, /* TVAR_ash_pGAS[255] */ + {S5K6AAFX_REG_WR, 0x0057}, /* TVAR_ash_pGAS[256] */ + {S5K6AAFX_REG_WR, 0x0083}, /* TVAR_ash_pGAS[257] */ + {S5K6AAFX_REG_WR, 0x00B0}, /* TVAR_ash_pGAS[258] */ + {S5K6AAFX_REG_WR, 0x00F2}, /* TVAR_ash_pGAS[259] */ + {S5K6AAFX_REG_WR, 0x019B}, /* TVAR_ash_pGAS[260] */ + {S5K6AAFX_REG_WR, 0x0117}, /* TVAR_ash_pGAS[261] */ + {S5K6AAFX_REG_WR, 0x00D9}, /* TVAR_ash_pGAS[262] */ + {S5K6AAFX_REG_WR, 0x00B0}, /* TVAR_ash_pGAS[263] */ + {S5K6AAFX_REG_WR, 0x0085}, /* TVAR_ash_pGAS[264] */ + {S5K6AAFX_REG_WR, 0x0067}, /* TVAR_ash_pGAS[265] */ + {S5K6AAFX_REG_WR, 0x0059}, /* TVAR_ash_pGAS[266] */ + {S5K6AAFX_REG_WR, 0x005C}, /* TVAR_ash_pGAS[267] */ + {S5K6AAFX_REG_WR, 0x006F}, /* TVAR_ash_pGAS[268] */ + {S5K6AAFX_REG_WR, 0x008D}, /* TVAR_ash_pGAS[269] */ + {S5K6AAFX_REG_WR, 0x00AE}, /* TVAR_ash_pGAS[270] */ + {S5K6AAFX_REG_WR, 0x00DE}, /* TVAR_ash_pGAS[271] */ + {S5K6AAFX_REG_WR, 0x0146}, /* TVAR_ash_pGAS[272] */ + {S5K6AAFX_REG_WR, 0x0249}, /* TVAR_ash_pGAS[273] */ + {S5K6AAFX_REG_WR, 0x017C}, /* TVAR_ash_pGAS[274] */ + {S5K6AAFX_REG_WR, 0x010F}, /* TVAR_ash_pGAS[275] */ + {S5K6AAFX_REG_WR, 0x00DF}, /* TVAR_ash_pGAS[276] */ + {S5K6AAFX_REG_WR, 0x00C0}, /* TVAR_ash_pGAS[277] */ + {S5K6AAFX_REG_WR, 0x00A6}, /* TVAR_ash_pGAS[278] */ + {S5K6AAFX_REG_WR, 0x0095}, /* TVAR_ash_pGAS[279] */ + {S5K6AAFX_REG_WR, 0x0096}, /* TVAR_ash_pGAS[280] */ + {S5K6AAFX_REG_WR, 0x00A8}, /* TVAR_ash_pGAS[281] */ + {S5K6AAFX_REG_WR, 0x00C0}, /* TVAR_ash_pGAS[282] */ + {S5K6AAFX_REG_WR, 0x00E3}, /* TVAR_ash_pGAS[283] */ + {S5K6AAFX_REG_WR, 0x012E}, /* TVAR_ash_pGAS[284] */ + {S5K6AAFX_REG_WR, 0x01BF}, /* TVAR_ash_pGAS[285] */ + {S5K6AAFX_REG_WR, 0x0289}, /* TVAR_ash_pGAS[286] */ + {S5K6AAFX_REG_WR, 0x019B}, /* TVAR_ash_pGAS[287] */ + {S5K6AAFX_REG_WR, 0x0116}, /* TVAR_ash_pGAS[288] */ + {S5K6AAFX_REG_WR, 0x00DE}, /* TVAR_ash_pGAS[289] */ + {S5K6AAFX_REG_WR, 0x00C0}, /* TVAR_ash_pGAS[290] */ + {S5K6AAFX_REG_WR, 0x00A9}, /* TVAR_ash_pGAS[291] */ + {S5K6AAFX_REG_WR, 0x009D}, /* TVAR_ash_pGAS[292] */ + {S5K6AAFX_REG_WR, 0x00A4}, /* TVAR_ash_pGAS[293] */ + {S5K6AAFX_REG_WR, 0x00B8}, /* TVAR_ash_pGAS[294] */ + {S5K6AAFX_REG_WR, 0x00D8}, /* TVAR_ash_pGAS[295] */ + {S5K6AAFX_REG_WR, 0x0106}, /* TVAR_ash_pGAS[296] */ + {S5K6AAFX_REG_WR, 0x0175}, /* TVAR_ash_pGAS[297] */ + {S5K6AAFX_REG_WR, 0x0239}, /* TVAR_ash_pGAS[298] */ + {S5K6AAFX_REG_WR, 0x01C5}, /* TVAR_ash_pGAS[299] */ + {S5K6AAFX_REG_WR, 0x0125}, /* TVAR_ash_pGAS[300] */ + {S5K6AAFX_REG_WR, 0x00D9}, /* TVAR_ash_pGAS[301] */ + {S5K6AAFX_REG_WR, 0x00B2}, /* TVAR_ash_pGAS[302] */ + {S5K6AAFX_REG_WR, 0x008D}, /* TVAR_ash_pGAS[303] */ + {S5K6AAFX_REG_WR, 0x006F}, /* TVAR_ash_pGAS[304] */ + {S5K6AAFX_REG_WR, 0x0062}, /* TVAR_ash_pGAS[305] */ + {S5K6AAFX_REG_WR, 0x006A}, /* TVAR_ash_pGAS[306] */ + {S5K6AAFX_REG_WR, 0x0084}, /* TVAR_ash_pGAS[307] */ + {S5K6AAFX_REG_WR, 0x00A8}, /* TVAR_ash_pGAS[308] */ + {S5K6AAFX_REG_WR, 0x00CD}, /* TVAR_ash_pGAS[309] */ + {S5K6AAFX_REG_WR, 0x010D}, /* TVAR_ash_pGAS[310] */ + {S5K6AAFX_REG_WR, 0x0189}, /* TVAR_ash_pGAS[311] */ + {S5K6AAFX_REG_WR, 0x0143}, /* TVAR_ash_pGAS[312] */ + {S5K6AAFX_REG_WR, 0x00DF}, /* TVAR_ash_pGAS[313] */ + {S5K6AAFX_REG_WR, 0x00AF}, /* TVAR_ash_pGAS[314] */ + {S5K6AAFX_REG_WR, 0x007D}, /* TVAR_ash_pGAS[315] */ + {S5K6AAFX_REG_WR, 0x0050}, /* TVAR_ash_pGAS[316] */ + {S5K6AAFX_REG_WR, 0x0036}, /* TVAR_ash_pGAS[317] */ + {S5K6AAFX_REG_WR, 0x002D}, /* TVAR_ash_pGAS[318] */ + {S5K6AAFX_REG_WR, 0x0032}, /* TVAR_ash_pGAS[319] */ + {S5K6AAFX_REG_WR, 0x0048}, /* TVAR_ash_pGAS[320] */ + {S5K6AAFX_REG_WR, 0x006F}, /* TVAR_ash_pGAS[321] */ + {S5K6AAFX_REG_WR, 0x009F}, /* TVAR_ash_pGAS[322] */ + {S5K6AAFX_REG_WR, 0x00CF}, /* TVAR_ash_pGAS[323] */ + {S5K6AAFX_REG_WR, 0x0118}, /* TVAR_ash_pGAS[324] */ + {S5K6AAFX_REG_WR, 0x010C}, /* TVAR_ash_pGAS[325] */ + {S5K6AAFX_REG_WR, 0x00C3}, /* TVAR_ash_pGAS[326] */ + {S5K6AAFX_REG_WR, 0x008C}, /* TVAR_ash_pGAS[327] */ + {S5K6AAFX_REG_WR, 0x0056}, /* TVAR_ash_pGAS[328] */ + {S5K6AAFX_REG_WR, 0x002D}, /* TVAR_ash_pGAS[329] */ + {S5K6AAFX_REG_WR, 0x0017}, /* TVAR_ash_pGAS[330] */ + {S5K6AAFX_REG_WR, 0x000D}, /* TVAR_ash_pGAS[331] */ + {S5K6AAFX_REG_WR, 0x0010}, /* TVAR_ash_pGAS[332] */ + {S5K6AAFX_REG_WR, 0x001F}, /* TVAR_ash_pGAS[333] */ + {S5K6AAFX_REG_WR, 0x0040}, /* TVAR_ash_pGAS[334] */ + {S5K6AAFX_REG_WR, 0x0070}, /* TVAR_ash_pGAS[335] */ + {S5K6AAFX_REG_WR, 0x00A6}, /* TVAR_ash_pGAS[336] */ + {S5K6AAFX_REG_WR, 0x00DB}, /* TVAR_ash_pGAS[337] */ + {S5K6AAFX_REG_WR, 0x00F2}, /* TVAR_ash_pGAS[338] */ + {S5K6AAFX_REG_WR, 0x00B6}, /* TVAR_ash_pGAS[339] */ + {S5K6AAFX_REG_WR, 0x0078}, /* TVAR_ash_pGAS[340] */ + {S5K6AAFX_REG_WR, 0x003E}, /* TVAR_ash_pGAS[341] */ + {S5K6AAFX_REG_WR, 0x001B}, /* TVAR_ash_pGAS[342] */ + {S5K6AAFX_REG_WR, 0x000B}, /* TVAR_ash_pGAS[343] */ + {S5K6AAFX_REG_WR, 0x0004}, /* TVAR_ash_pGAS[344] */ + {S5K6AAFX_REG_WR, 0x0003}, /* TVAR_ash_pGAS[345] */ + {S5K6AAFX_REG_WR, 0x0009}, /* TVAR_ash_pGAS[346] */ + {S5K6AAFX_REG_WR, 0x001F}, /* TVAR_ash_pGAS[347] */ + {S5K6AAFX_REG_WR, 0x004B}, /* TVAR_ash_pGAS[348] */ + {S5K6AAFX_REG_WR, 0x0088}, /* TVAR_ash_pGAS[349] */ + {S5K6AAFX_REG_WR, 0x00B6}, /* TVAR_ash_pGAS[350] */ + {S5K6AAFX_REG_WR, 0x00EA}, /* TVAR_ash_pGAS[351] */ + {S5K6AAFX_REG_WR, 0x00B4}, /* TVAR_ash_pGAS[352] */ + {S5K6AAFX_REG_WR, 0x0070}, /* TVAR_ash_pGAS[353] */ + {S5K6AAFX_REG_WR, 0x0037}, /* TVAR_ash_pGAS[354] */ + {S5K6AAFX_REG_WR, 0x0016}, /* TVAR_ash_pGAS[355] */ + {S5K6AAFX_REG_WR, 0x0008}, /* TVAR_ash_pGAS[356] */ + {S5K6AAFX_REG_WR, 0x0000}, /* TVAR_ash_pGAS[357] */ + {S5K6AAFX_REG_WR, 0x0000}, /* TVAR_ash_pGAS[358] */ + {S5K6AAFX_REG_WR, 0x0002}, /* TVAR_ash_pGAS[359] */ + {S5K6AAFX_REG_WR, 0x0013}, /* TVAR_ash_pGAS[360] */ + {S5K6AAFX_REG_WR, 0x0038}, /* TVAR_ash_pGAS[361] */ + {S5K6AAFX_REG_WR, 0x0071}, /* TVAR_ash_pGAS[362] */ + {S5K6AAFX_REG_WR, 0x00A0}, /* TVAR_ash_pGAS[363] */ + {S5K6AAFX_REG_WR, 0x00F1}, /* TVAR_ash_pGAS[364] */ + {S5K6AAFX_REG_WR, 0x00B8}, /* TVAR_ash_pGAS[365] */ + {S5K6AAFX_REG_WR, 0x0076}, /* TVAR_ash_pGAS[366] */ + {S5K6AAFX_REG_WR, 0x003E}, /* TVAR_ash_pGAS[367] */ + {S5K6AAFX_REG_WR, 0x001C}, /* TVAR_ash_pGAS[368] */ + {S5K6AAFX_REG_WR, 0x000B}, /* TVAR_ash_pGAS[369] */ + {S5K6AAFX_REG_WR, 0x0002}, /* TVAR_ash_pGAS[370] */ + {S5K6AAFX_REG_WR, 0x0000}, /* TVAR_ash_pGAS[371] */ + {S5K6AAFX_REG_WR, 0x0004}, /* TVAR_ash_pGAS[372] */ + {S5K6AAFX_REG_WR, 0x0014}, /* TVAR_ash_pGAS[373] */ + {S5K6AAFX_REG_WR, 0x0037}, /* TVAR_ash_pGAS[374] */ + {S5K6AAFX_REG_WR, 0x0068}, /* TVAR_ash_pGAS[375] */ + {S5K6AAFX_REG_WR, 0x0095}, /* TVAR_ash_pGAS[376] */ + {S5K6AAFX_REG_WR, 0x010B}, /* TVAR_ash_pGAS[377] */ + {S5K6AAFX_REG_WR, 0x00CC}, /* TVAR_ash_pGAS[378] */ + {S5K6AAFX_REG_WR, 0x0093}, /* TVAR_ash_pGAS[379] */ + {S5K6AAFX_REG_WR, 0x0056}, /* TVAR_ash_pGAS[380] */ + {S5K6AAFX_REG_WR, 0x002B}, /* TVAR_ash_pGAS[381] */ + {S5K6AAFX_REG_WR, 0x0015}, /* TVAR_ash_pGAS[382] */ + {S5K6AAFX_REG_WR, 0x000B}, /* TVAR_ash_pGAS[383] */ + {S5K6AAFX_REG_WR, 0x0009}, /* TVAR_ash_pGAS[384] */ + {S5K6AAFX_REG_WR, 0x000E}, /* TVAR_ash_pGAS[385] */ + {S5K6AAFX_REG_WR, 0x0021}, /* TVAR_ash_pGAS[386] */ + {S5K6AAFX_REG_WR, 0x0043}, /* TVAR_ash_pGAS[387] */ + {S5K6AAFX_REG_WR, 0x0070}, /* TVAR_ash_pGAS[388] */ + {S5K6AAFX_REG_WR, 0x00A0}, /* TVAR_ash_pGAS[389] */ + {S5K6AAFX_REG_WR, 0x0143}, /* TVAR_ash_pGAS[390] */ + {S5K6AAFX_REG_WR, 0x00EB}, /* TVAR_ash_pGAS[391] */ + {S5K6AAFX_REG_WR, 0x00B8}, /* TVAR_ash_pGAS[392] */ + {S5K6AAFX_REG_WR, 0x007E}, /* TVAR_ash_pGAS[393] */ + {S5K6AAFX_REG_WR, 0x004E}, /* TVAR_ash_pGAS[394] */ + {S5K6AAFX_REG_WR, 0x002F}, /* TVAR_ash_pGAS[395] */ + {S5K6AAFX_REG_WR, 0x0021}, /* TVAR_ash_pGAS[396] */ + {S5K6AAFX_REG_WR, 0x0020}, /* TVAR_ash_pGAS[397] */ + {S5K6AAFX_REG_WR, 0x0027}, /* TVAR_ash_pGAS[398] */ + {S5K6AAFX_REG_WR, 0x003D}, /* TVAR_ash_pGAS[399] */ + {S5K6AAFX_REG_WR, 0x005D}, /* TVAR_ash_pGAS[400] */ + {S5K6AAFX_REG_WR, 0x0084}, /* TVAR_ash_pGAS[401] */ + {S5K6AAFX_REG_WR, 0x00BD}, /* TVAR_ash_pGAS[402] */ + {S5K6AAFX_REG_WR, 0x01AD}, /* TVAR_ash_pGAS[403] */ + {S5K6AAFX_REG_WR, 0x0122}, /* TVAR_ash_pGAS[404] */ + {S5K6AAFX_REG_WR, 0x00E3}, /* TVAR_ash_pGAS[405] */ + {S5K6AAFX_REG_WR, 0x00B5}, /* TVAR_ash_pGAS[406] */ + {S5K6AAFX_REG_WR, 0x0087}, /* TVAR_ash_pGAS[407] */ + {S5K6AAFX_REG_WR, 0x0064}, /* TVAR_ash_pGAS[408] */ + {S5K6AAFX_REG_WR, 0x0051}, /* TVAR_ash_pGAS[409] */ + {S5K6AAFX_REG_WR, 0x004E}, /* TVAR_ash_pGAS[410] */ + {S5K6AAFX_REG_WR, 0x0057}, /* TVAR_ash_pGAS[411] */ + {S5K6AAFX_REG_WR, 0x006A}, /* TVAR_ash_pGAS[412] */ + {S5K6AAFX_REG_WR, 0x007F}, /* TVAR_ash_pGAS[413] */ + {S5K6AAFX_REG_WR, 0x00A8}, /* TVAR_ash_pGAS[414] */ + {S5K6AAFX_REG_WR, 0x0101}, /* TVAR_ash_pGAS[415] */ + {S5K6AAFX_REG_WR, 0x0267}, /* TVAR_ash_pGAS[416] */ + {S5K6AAFX_REG_WR, 0x018C}, /* TVAR_ash_pGAS[417] */ + {S5K6AAFX_REG_WR, 0x0119}, /* TVAR_ash_pGAS[418] */ + {S5K6AAFX_REG_WR, 0x00E5}, /* TVAR_ash_pGAS[419] */ + {S5K6AAFX_REG_WR, 0x00C2}, /* TVAR_ash_pGAS[420] */ + {S5K6AAFX_REG_WR, 0x00A2}, /* TVAR_ash_pGAS[421] */ + {S5K6AAFX_REG_WR, 0x008D}, /* TVAR_ash_pGAS[422] */ + {S5K6AAFX_REG_WR, 0x0086}, /* TVAR_ash_pGAS[423] */ + {S5K6AAFX_REG_WR, 0x008C}, /* TVAR_ash_pGAS[424] */ + {S5K6AAFX_REG_WR, 0x0099}, /* TVAR_ash_pGAS[425] */ + {S5K6AAFX_REG_WR, 0x00B0}, /* TVAR_ash_pGAS[426] */ + {S5K6AAFX_REG_WR, 0x00F2}, /* TVAR_ash_pGAS[427] */ + {S5K6AAFX_REG_WR, 0x016C}, /* TVAR_ash_pGAS[428] */ + {S5K6AAFX_REG_WR, 0x01F3}, /* TVAR_ash_pGAS[429] */ + {S5K6AAFX_REG_WR, 0x0136}, /* TVAR_ash_pGAS[430] */ + {S5K6AAFX_REG_WR, 0x00D6}, /* TVAR_ash_pGAS[431] */ + {S5K6AAFX_REG_WR, 0x00B3}, /* TVAR_ash_pGAS[432] */ + {S5K6AAFX_REG_WR, 0x00A1}, /* TVAR_ash_pGAS[433] */ + {S5K6AAFX_REG_WR, 0x0095}, /* TVAR_ash_pGAS[434] */ + {S5K6AAFX_REG_WR, 0x008E}, /* TVAR_ash_pGAS[435] */ + {S5K6AAFX_REG_WR, 0x0098}, /* TVAR_ash_pGAS[436] */ + {S5K6AAFX_REG_WR, 0x00AD}, /* TVAR_ash_pGAS[437] */ + {S5K6AAFX_REG_WR, 0x00C5}, /* TVAR_ash_pGAS[438] */ + {S5K6AAFX_REG_WR, 0x00ED}, /* TVAR_ash_pGAS[439] */ + {S5K6AAFX_REG_WR, 0x014D}, /* TVAR_ash_pGAS[440] */ + {S5K6AAFX_REG_WR, 0x0207}, /* TVAR_ash_pGAS[441] */ + {S5K6AAFX_REG_WR, 0x014C}, /* TVAR_ash_pGAS[442] */ + {S5K6AAFX_REG_WR, 0x00D1}, /* TVAR_ash_pGAS[443] */ + {S5K6AAFX_REG_WR, 0x00A4}, /* TVAR_ash_pGAS[444] */ + {S5K6AAFX_REG_WR, 0x0091}, /* TVAR_ash_pGAS[445] */ + {S5K6AAFX_REG_WR, 0x0077}, /* TVAR_ash_pGAS[446] */ + {S5K6AAFX_REG_WR, 0x0062}, /* TVAR_ash_pGAS[447] */ + {S5K6AAFX_REG_WR, 0x005E}, /* TVAR_ash_pGAS[448] */ + {S5K6AAFX_REG_WR, 0x006A}, /* TVAR_ash_pGAS[449] */ + {S5K6AAFX_REG_WR, 0x0081}, /* TVAR_ash_pGAS[450] */ + {S5K6AAFX_REG_WR, 0x009F}, /* TVAR_ash_pGAS[451] */ + {S5K6AAFX_REG_WR, 0x00BE}, /* TVAR_ash_pGAS[452] */ + {S5K6AAFX_REG_WR, 0x00F2}, /* TVAR_ash_pGAS[453] */ + {S5K6AAFX_REG_WR, 0x0162}, /* TVAR_ash_pGAS[454] */ + {S5K6AAFX_REG_WR, 0x00DB}, /* TVAR_ash_pGAS[455] */ + {S5K6AAFX_REG_WR, 0x008C}, /* TVAR_ash_pGAS[456] */ + {S5K6AAFX_REG_WR, 0x0079}, /* TVAR_ash_pGAS[457] */ + {S5K6AAFX_REG_WR, 0x005D}, /* TVAR_ash_pGAS[458] */ + {S5K6AAFX_REG_WR, 0x003D}, /* TVAR_ash_pGAS[459] */ + {S5K6AAFX_REG_WR, 0x002B}, /* TVAR_ash_pGAS[460] */ + {S5K6AAFX_REG_WR, 0x002B}, /* TVAR_ash_pGAS[461] */ + {S5K6AAFX_REG_WR, 0x0033}, /* TVAR_ash_pGAS[462] */ + {S5K6AAFX_REG_WR, 0x004A}, /* TVAR_ash_pGAS[463] */ + {S5K6AAFX_REG_WR, 0x006A}, /* TVAR_ash_pGAS[464] */ + {S5K6AAFX_REG_WR, 0x0092}, /* TVAR_ash_pGAS[465] */ + {S5K6AAFX_REG_WR, 0x00B2}, /* TVAR_ash_pGAS[466] */ + {S5K6AAFX_REG_WR, 0x00F2}, /* TVAR_ash_pGAS[467] */ + {S5K6AAFX_REG_WR, 0x00A2}, /* TVAR_ash_pGAS[468] */ + {S5K6AAFX_REG_WR, 0x0072}, /* TVAR_ash_pGAS[469] */ + {S5K6AAFX_REG_WR, 0x0059}, /* TVAR_ash_pGAS[470] */ + {S5K6AAFX_REG_WR, 0x003A}, /* TVAR_ash_pGAS[471] */ + {S5K6AAFX_REG_WR, 0x001E}, /* TVAR_ash_pGAS[472] */ + {S5K6AAFX_REG_WR, 0x0011}, /* TVAR_ash_pGAS[473] */ + {S5K6AAFX_REG_WR, 0x000F}, /* TVAR_ash_pGAS[474] */ + {S5K6AAFX_REG_WR, 0x0012}, /* TVAR_ash_pGAS[475] */ + {S5K6AAFX_REG_WR, 0x0020}, /* TVAR_ash_pGAS[476] */ + {S5K6AAFX_REG_WR, 0x003B}, /* TVAR_ash_pGAS[477] */ + {S5K6AAFX_REG_WR, 0x005E}, /* TVAR_ash_pGAS[478] */ + {S5K6AAFX_REG_WR, 0x0084}, /* TVAR_ash_pGAS[479] */ + {S5K6AAFX_REG_WR, 0x00AD}, /* TVAR_ash_pGAS[480] */ + {S5K6AAFX_REG_WR, 0x008B}, /* TVAR_ash_pGAS[481] */ + {S5K6AAFX_REG_WR, 0x0065}, /* TVAR_ash_pGAS[482] */ + {S5K6AAFX_REG_WR, 0x0045}, /* TVAR_ash_pGAS[483] */ + {S5K6AAFX_REG_WR, 0x0024}, /* TVAR_ash_pGAS[484] */ + {S5K6AAFX_REG_WR, 0x000F}, /* TVAR_ash_pGAS[485] */ + {S5K6AAFX_REG_WR, 0x0007}, /* TVAR_ash_pGAS[486] */ + {S5K6AAFX_REG_WR, 0x0005}, /* TVAR_ash_pGAS[487] */ + {S5K6AAFX_REG_WR, 0x0005}, /* TVAR_ash_pGAS[488] */ + {S5K6AAFX_REG_WR, 0x0008}, /* TVAR_ash_pGAS[489] */ + {S5K6AAFX_REG_WR, 0x0017}, /* TVAR_ash_pGAS[490] */ + {S5K6AAFX_REG_WR, 0x0037}, /* TVAR_ash_pGAS[491] */ + {S5K6AAFX_REG_WR, 0x0060}, /* TVAR_ash_pGAS[492] */ + {S5K6AAFX_REG_WR, 0x0083}, /* TVAR_ash_pGAS[493] */ + {S5K6AAFX_REG_WR, 0x0081}, /* TVAR_ash_pGAS[494] */ + {S5K6AAFX_REG_WR, 0x0060}, /* TVAR_ash_pGAS[495] */ + {S5K6AAFX_REG_WR, 0x003D}, /* TVAR_ash_pGAS[496] */ + {S5K6AAFX_REG_WR, 0x001D}, /* TVAR_ash_pGAS[497] */ + {S5K6AAFX_REG_WR, 0x000C}, /* TVAR_ash_pGAS[498] */ + {S5K6AAFX_REG_WR, 0x0006}, /* TVAR_ash_pGAS[499] */ + {S5K6AAFX_REG_WR, 0x0001}, /* TVAR_ash_pGAS[500] */ + {S5K6AAFX_REG_WR, 0x0001}, /* TVAR_ash_pGAS[501] */ + {S5K6AAFX_REG_WR, 0x0002}, /* TVAR_ash_pGAS[502] */ + {S5K6AAFX_REG_WR, 0x0009}, /* TVAR_ash_pGAS[503] */ + {S5K6AAFX_REG_WR, 0x0022}, /* TVAR_ash_pGAS[504] */ + {S5K6AAFX_REG_WR, 0x0047}, /* TVAR_ash_pGAS[505] */ + {S5K6AAFX_REG_WR, 0x0068}, /* TVAR_ash_pGAS[506] */ + {S5K6AAFX_REG_WR, 0x0084}, /* TVAR_ash_pGAS[507] */ + {S5K6AAFX_REG_WR, 0x0064}, /* TVAR_ash_pGAS[508] */ + {S5K6AAFX_REG_WR, 0x0042}, /* TVAR_ash_pGAS[509] */ + {S5K6AAFX_REG_WR, 0x0023}, /* TVAR_ash_pGAS[510] */ + {S5K6AAFX_REG_WR, 0x0010}, /* TVAR_ash_pGAS[511] */ + {S5K6AAFX_REG_WR, 0x0007}, /* TVAR_ash_pGAS[512] */ + {S5K6AAFX_REG_WR, 0x0002}, /* TVAR_ash_pGAS[513] */ + {S5K6AAFX_REG_WR, 0x0001}, /* TVAR_ash_pGAS[514] */ + {S5K6AAFX_REG_WR, 0x0001}, /* TVAR_ash_pGAS[515] */ + {S5K6AAFX_REG_WR, 0x0008}, /* TVAR_ash_pGAS[516] */ + {S5K6AAFX_REG_WR, 0x001C}, /* TVAR_ash_pGAS[517] */ + {S5K6AAFX_REG_WR, 0x0039}, /* TVAR_ash_pGAS[518] */ + {S5K6AAFX_REG_WR, 0x005B}, /* TVAR_ash_pGAS[519] */ + {S5K6AAFX_REG_WR, 0x009C}, /* TVAR_ash_pGAS[520] */ + {S5K6AAFX_REG_WR, 0x0076}, /* TVAR_ash_pGAS[521] */ + {S5K6AAFX_REG_WR, 0x005B}, /* TVAR_ash_pGAS[522] */ + {S5K6AAFX_REG_WR, 0x0037}, /* TVAR_ash_pGAS[523] */ + {S5K6AAFX_REG_WR, 0x001B}, /* TVAR_ash_pGAS[524] */ + {S5K6AAFX_REG_WR, 0x000F}, /* TVAR_ash_pGAS[525] */ + {S5K6AAFX_REG_WR, 0x0009}, /* TVAR_ash_pGAS[526] */ + {S5K6AAFX_REG_WR, 0x0008}, /* TVAR_ash_pGAS[527] */ + {S5K6AAFX_REG_WR, 0x0009}, /* TVAR_ash_pGAS[528] */ + {S5K6AAFX_REG_WR, 0x0011}, /* TVAR_ash_pGAS[529] */ + {S5K6AAFX_REG_WR, 0x0025}, /* TVAR_ash_pGAS[530] */ + {S5K6AAFX_REG_WR, 0x003E}, /* TVAR_ash_pGAS[531] */ + {S5K6AAFX_REG_WR, 0x005F}, /* TVAR_ash_pGAS[532] */ + {S5K6AAFX_REG_WR, 0x00D0}, /* TVAR_ash_pGAS[533] */ + {S5K6AAFX_REG_WR, 0x0095}, /* TVAR_ash_pGAS[534] */ + {S5K6AAFX_REG_WR, 0x007E}, /* TVAR_ash_pGAS[535] */ + {S5K6AAFX_REG_WR, 0x005C}, /* TVAR_ash_pGAS[536] */ + {S5K6AAFX_REG_WR, 0x003A}, /* TVAR_ash_pGAS[537] */ + {S5K6AAFX_REG_WR, 0x0025}, /* TVAR_ash_pGAS[538] */ + {S5K6AAFX_REG_WR, 0x001B}, /* TVAR_ash_pGAS[539] */ + {S5K6AAFX_REG_WR, 0x001B}, /* TVAR_ash_pGAS[540] */ + {S5K6AAFX_REG_WR, 0x001E}, /* TVAR_ash_pGAS[541] */ + {S5K6AAFX_REG_WR, 0x0027}, /* TVAR_ash_pGAS[542] */ + {S5K6AAFX_REG_WR, 0x003A}, /* TVAR_ash_pGAS[543] */ + {S5K6AAFX_REG_WR, 0x004F}, /* TVAR_ash_pGAS[544] */ + {S5K6AAFX_REG_WR, 0x007B}, /* TVAR_ash_pGAS[545] */ + {S5K6AAFX_REG_WR, 0x012F}, /* TVAR_ash_pGAS[546] */ + {S5K6AAFX_REG_WR, 0x00C8}, /* TVAR_ash_pGAS[547] */ + {S5K6AAFX_REG_WR, 0x00A7}, /* TVAR_ash_pGAS[548] */ + {S5K6AAFX_REG_WR, 0x008E}, /* TVAR_ash_pGAS[549] */ + {S5K6AAFX_REG_WR, 0x006F}, /* TVAR_ash_pGAS[550] */ + {S5K6AAFX_REG_WR, 0x0057}, /* TVAR_ash_pGAS[551] */ + {S5K6AAFX_REG_WR, 0x0048}, /* TVAR_ash_pGAS[552] */ + {S5K6AAFX_REG_WR, 0x0047}, /* TVAR_ash_pGAS[553] */ + {S5K6AAFX_REG_WR, 0x0049}, /* TVAR_ash_pGAS[554] */ + {S5K6AAFX_REG_WR, 0x004F}, /* TVAR_ash_pGAS[555] */ + {S5K6AAFX_REG_WR, 0x0058}, /* TVAR_ash_pGAS[556] */ + {S5K6AAFX_REG_WR, 0x006E}, /* TVAR_ash_pGAS[557] */ + {S5K6AAFX_REG_WR, 0x00B9}, /* TVAR_ash_pGAS[558] */ + {S5K6AAFX_REG_WR, 0x01CB}, /* TVAR_ash_pGAS[559] */ + {S5K6AAFX_REG_WR, 0x0123}, /* TVAR_ash_pGAS[560] */ + {S5K6AAFX_REG_WR, 0x00D5}, /* TVAR_ash_pGAS[561] */ + {S5K6AAFX_REG_WR, 0x00B9}, /* TVAR_ash_pGAS[562] */ + {S5K6AAFX_REG_WR, 0x00A2}, /* TVAR_ash_pGAS[563] */ + {S5K6AAFX_REG_WR, 0x008E}, /* TVAR_ash_pGAS[564] */ + {S5K6AAFX_REG_WR, 0x0080}, /* TVAR_ash_pGAS[565] */ + {S5K6AAFX_REG_WR, 0x007B}, /* TVAR_ash_pGAS[566] */ + {S5K6AAFX_REG_WR, 0x0079}, /* TVAR_ash_pGAS[567] */ + {S5K6AAFX_REG_WR, 0x0078}, /* TVAR_ash_pGAS[568] */ + {S5K6AAFX_REG_WR, 0x0081}, /* TVAR_ash_pGAS[569] */ + {S5K6AAFX_REG_WR, 0x00A9}, /* TVAR_ash_pGAS[570] */ + {S5K6AAFX_REG_WR, 0x0108}, /* TVAR_ash_pGAS[571] */ + /* parawrite _end - TVAR_ash_pGAS */ + + {S5K6AAFX_REG_W_ADDL, 0x0C48}, + {S5K6AAFX_REG_WR, 0x0550}, /* R*/ + {S5K6AAFX_REG_WR, 0x0400}, /* G*/ + {S5K6AAFX_REG_WR, 0x0600}, /*B */ + +#if 0 + {S5K6AAFX_REG_W_ADDL, 0x0F12}, + {S5K6AAFX_REG_WR, 0x02C9}, /* awbb_GLocusR */ + {S5K6AAFX_REG_WR, 0x033F}, /* awbb_GLocusB */ +#endif + + /* param_start - TVAR_ash_AwbAshCord */ + {S5K6AAFX_REG_W_ADDL, 0x0704}, + {S5K6AAFX_REG_WR, 0x00ED/*0x00C7*/}, /* TVAR_ash_AwbAshCord[0] */ + {S5K6AAFX_REG_WR, 0x0124/*0x00F7*/}, /* TVAR_ash_AwbAshCord[1] */ + {S5K6AAFX_REG_WR, 0x012B/*0x0107*/}, /* TVAR_ash_AwbAshCord[2] */ + {S5K6AAFX_REG_WR, 0x014A/*0x0142*/}, /* TVAR_ash_AwbAshCord[3] */ + {S5K6AAFX_REG_WR, 0x0190/*0x017A*/}, /* TVAR_ash_AwbAshCord[4] */ + {S5K6AAFX_REG_WR, 0x01B2/*0x01A0*/}, /* TVAR_ash_AwbAshCord[5] */ + {S5K6AAFX_REG_WR, 0x01C4/*0x01B6*/}, /* TVAR_ash_AwbAshCord[6] */ + /* param_end - TVAR_ash_AwbAshCord */ + + {S5K6AAFX_REG_W_ADDL, 0x0754}, + {S5K6AAFX_REG_WR, 0x247C}, + {S5K6AAFX_REG_WR, 0x7000}, + + {S5K6AAFX_REG_W_ADDL, 0x0E1A}, + {S5K6AAFX_REG_WR, 0x0138}, + + /* AWB Speed */ + {S5K6AAFX_REG_W_ADDL, 0x0E7C}, + {S5K6AAFX_REG_WR, 0x0010}, + {S5K6AAFX_REG_WR, 0x0003}, + + /* AWB grid */ + {S5K6AAFX_REG_W_ADDL, 0x0E42}, + {S5K6AAFX_REG_WR, 0x0001}, + + {S5K6AAFX_REG_W_ADDL, 0x0E82}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0xFFC4}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0xFFDA}, + {S5K6AAFX_REG_WR, 0xFFF2}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0xFFC4}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0xFFC4}, + {S5K6AAFX_REG_WR, 0x0000}, + + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0064}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0064}, + {S5K6AAFX_REG_WR, 0x004B}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0064}, + //{S5K6AAFX_REG_WR, 0x0030}, + //{S5K6AAFX_REG_WR, 0x0040}, + //{S5K6AAFX_REG_WR, 0x0000}, + //{S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0064}, + {S5K6AAFX_REG_WR, 0x0000}, + + /* param_start - awbb_IndoorGrZones_m_BGrid */ + {S5K6AAFX_REG_W_ADDL, 0x0C50}, + {S5K6AAFX_REG_WR, 0x0389}, + {S5K6AAFX_REG_WR, 0x0396}, + {S5K6AAFX_REG_WR, 0x0365}, + {S5K6AAFX_REG_WR, 0x03A4}, + {S5K6AAFX_REG_WR, 0x0343}, + {S5K6AAFX_REG_WR, 0x03A4}, + {S5K6AAFX_REG_WR, 0x0323}, + {S5K6AAFX_REG_WR, 0x0390}, + {S5K6AAFX_REG_WR, 0x0300}, + {S5K6AAFX_REG_WR, 0x036D}, + {S5K6AAFX_REG_WR, 0x02E0}, + {S5K6AAFX_REG_WR, 0x0356}, + {S5K6AAFX_REG_WR, 0x02CC}, + {S5K6AAFX_REG_WR, 0x033C}, + {S5K6AAFX_REG_WR, 0x02C0}, + {S5K6AAFX_REG_WR, 0x0325}, + {S5K6AAFX_REG_WR, 0x02B4}, + {S5K6AAFX_REG_WR, 0x0303}, + {S5K6AAFX_REG_WR, 0x02AA}, + {S5K6AAFX_REG_WR, 0x02E7}, + {S5K6AAFX_REG_WR, 0x02A1}, + {S5K6AAFX_REG_WR, 0x02D5}, + {S5K6AAFX_REG_WR, 0x0298}, + {S5K6AAFX_REG_WR, 0x02C9}, + {S5K6AAFX_REG_WR, 0x028D}, + {S5K6AAFX_REG_WR, 0x02BF}, + {S5K6AAFX_REG_WR, 0x0284}, + {S5K6AAFX_REG_WR, 0x02B4}, + {S5K6AAFX_REG_WR, 0x0279}, + {S5K6AAFX_REG_WR, 0x02A9}, + {S5K6AAFX_REG_WR, 0x026D}, + {S5K6AAFX_REG_WR, 0x02A1}, + {S5K6AAFX_REG_WR, 0x0260}, + {S5K6AAFX_REG_WR, 0x029B}, + {S5K6AAFX_REG_WR, 0x025A}, + {S5K6AAFX_REG_WR, 0x0291}, + {S5K6AAFX_REG_WR, 0x0252}, + {S5K6AAFX_REG_WR, 0x028A}, + {S5K6AAFX_REG_WR, 0x024F}, + {S5K6AAFX_REG_WR, 0x0284}, + {S5K6AAFX_REG_WR, 0x024C}, + {S5K6AAFX_REG_WR, 0x0279}, + {S5K6AAFX_REG_WR, 0x0259}, + {S5K6AAFX_REG_WR, 0x0265}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x0000}, + /* param_end - awbb_IndoorGrZones_m_BGrid */ + + {S5K6AAFX_REG_WR, 0x0004}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_W_ADDL, 0x0CF8}, + {S5K6AAFX_REG_WR, 0x0115}, + {S5K6AAFX_REG_WR, 0x0000}, + + /* param_end - awbb_OutdoorGrZones_m_BGrid */ + {S5K6AAFX_REG_W_ADDL, 0x0d08}, + {S5K6AAFX_REG_WR, 0X026D}, + {S5K6AAFX_REG_WR, 0X029D}, + {S5K6AAFX_REG_WR, 0X025B}, + {S5K6AAFX_REG_WR, 0X029D}, + {S5K6AAFX_REG_WR, 0X024D}, + {S5K6AAFX_REG_WR, 0X0293}, + {S5K6AAFX_REG_WR, 0X0240}, + {S5K6AAFX_REG_WR, 0X0286}, + {S5K6AAFX_REG_WR, 0X0236}, + {S5K6AAFX_REG_WR, 0X0279}, + {S5K6AAFX_REG_WR, 0X022D}, + {S5K6AAFX_REG_WR, 0X026C}, + {S5K6AAFX_REG_WR, 0X0225}, + {S5K6AAFX_REG_WR, 0X0260}, + {S5K6AAFX_REG_WR, 0X0225}, + {S5K6AAFX_REG_WR, 0X0254}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_WR, 0X0000}, + + {S5K6AAFX_REG_WR, 0X0004}, + {S5K6AAFX_REG_WR, 0X0000}, + {S5K6AAFX_REG_W_ADDL, 0x0D74}, + {S5K6AAFX_REG_WR, 0X0222}, + {S5K6AAFX_REG_WR, 0X0000}, + + /* param_start - SARR_usGammaLutRGBIndoor */ + {S5K6AAFX_REG_W_ADDL, 0x04C8}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x000A}, + {S5K6AAFX_REG_WR, 0x0028}, + {S5K6AAFX_REG_WR, 0x003E}, + {S5K6AAFX_REG_WR, 0x0072}, + {S5K6AAFX_REG_WR, 0x00DA}, + {S5K6AAFX_REG_WR, 0x0129}, + {S5K6AAFX_REG_WR, 0x0165}, + {S5K6AAFX_REG_WR, 0x01C5}, + {S5K6AAFX_REG_WR, 0x0224}, + {S5K6AAFX_REG_WR, 0x028F}, + {S5K6AAFX_REG_WR, 0x02EE}, + {S5K6AAFX_REG_WR, 0x033C}, + {S5K6AAFX_REG_WR, 0x0380}, + {S5K6AAFX_REG_WR, 0x03C3}, + {S5K6AAFX_REG_WR, 0x03FF}, + + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x000A}, + {S5K6AAFX_REG_WR, 0x0028}, + {S5K6AAFX_REG_WR, 0x003E}, + {S5K6AAFX_REG_WR, 0x0072}, + {S5K6AAFX_REG_WR, 0x00DA}, + {S5K6AAFX_REG_WR, 0x0129}, + {S5K6AAFX_REG_WR, 0x0165}, + {S5K6AAFX_REG_WR, 0x01C5}, + {S5K6AAFX_REG_WR, 0x0224}, + {S5K6AAFX_REG_WR, 0x028F}, + {S5K6AAFX_REG_WR, 0x02EE}, + {S5K6AAFX_REG_WR, 0x033C}, + {S5K6AAFX_REG_WR, 0x0380}, + {S5K6AAFX_REG_WR, 0x03C3}, + {S5K6AAFX_REG_WR, 0x03FF}, + + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0x000A}, + {S5K6AAFX_REG_WR, 0x0028}, + {S5K6AAFX_REG_WR, 0x003E}, + {S5K6AAFX_REG_WR, 0x0072}, + {S5K6AAFX_REG_WR, 0x00DA}, + {S5K6AAFX_REG_WR, 0x0129}, + {S5K6AAFX_REG_WR, 0x0165}, + {S5K6AAFX_REG_WR, 0x01C5}, + {S5K6AAFX_REG_WR, 0x0224}, + {S5K6AAFX_REG_WR, 0x028F}, + {S5K6AAFX_REG_WR, 0x02EE}, + {S5K6AAFX_REG_WR, 0x033C}, + {S5K6AAFX_REG_WR, 0x0380}, + {S5K6AAFX_REG_WR, 0x03C3}, + {S5K6AAFX_REG_WR, 0x03FF}, + /* param_end - SARR_usGammaLutRGBIndoor */ + + + {S5K6AAFX_REG_W_ADDL, 0x1000}, + {S5K6AAFX_REG_WR, 0x003F}, + + {S5K6AAFX_REG_W_ADDL, 0x0474}, + {S5K6AAFX_REG_WR, 0x0112/*0x010F*//*0x0114*/}, + {S5K6AAFX_REG_WR, 0x00EF/*0x00F1*//*0x00F9*/}, + + {S5K6AAFX_REG_W_ADDL, 0x2180}, + {S5K6AAFX_REG_WR, 0x0000}, + + {S5K6AAFX_REG_W_ADDL, 0x1006}, + {S5K6AAFX_REG_WR, 0x001F}, + + {S5K6AAFX_REG_W_ADDL, 0x108E}, + {S5K6AAFX_REG_WR, 0x00C7}, + {S5K6AAFX_REG_WR, 0x00F7}, + {S5K6AAFX_REG_WR, 0x0107}, + {S5K6AAFX_REG_WR, 0x0142}, + {S5K6AAFX_REG_WR, 0x017A}, + {S5K6AAFX_REG_WR, 0x01A0}, + {S5K6AAFX_REG_WR, 0x01B6}, + + {S5K6AAFX_REG_WR, 0x0100/*0x0112*/}, + {S5K6AAFX_REG_WR, 0x0100/*0x0122*/}, + {S5K6AAFX_REG_WR, 0x0100/*0x0136*/}, + {S5K6AAFX_REG_WR, 0x0100/*0x00F6*/}, + {S5K6AAFX_REG_WR, 0x0100/*0x0100*/}, + {S5K6AAFX_REG_WR, 0x0100/*0x00FE*/}, + {S5K6AAFX_REG_WR, 0x0100/*0x0100*/}, + + /* param_start - TVAR_wbt_pBaseCcms */ + {S5K6AAFX_REG_W_ADDL, 0x23A4}, + {S5K6AAFX_REG_WR, 0x01FA}, /* H */ + {S5K6AAFX_REG_WR, 0xFFB9}, + {S5K6AAFX_REG_WR, 0xFFF8}, + {S5K6AAFX_REG_WR, 0x0116}, + {S5K6AAFX_REG_WR, 0x00BD}, + {S5K6AAFX_REG_WR, 0xFF38}, + {S5K6AAFX_REG_WR, 0xFF23}, + {S5K6AAFX_REG_WR, 0x01AB}, + {S5K6AAFX_REG_WR, 0xFF81}, + {S5K6AAFX_REG_WR, 0xFF0D}, + {S5K6AAFX_REG_WR, 0x0169}, + {S5K6AAFX_REG_WR, 0x00DE}, + {S5K6AAFX_REG_WR, 0xFFEF}, + {S5K6AAFX_REG_WR, 0xFFCA}, + {S5K6AAFX_REG_WR, 0x014D}, + {S5K6AAFX_REG_WR, 0x01C3}, + {S5K6AAFX_REG_WR, 0xFF7E}, + {S5K6AAFX_REG_WR, 0x016F}, + + {S5K6AAFX_REG_WR, 0x01FA}, /* A */ + {S5K6AAFX_REG_WR, 0xFFB9}, + {S5K6AAFX_REG_WR, 0xFFF8}, + {S5K6AAFX_REG_WR, 0x0116}, + {S5K6AAFX_REG_WR, 0x00BD}, + {S5K6AAFX_REG_WR, 0xFF38}, + {S5K6AAFX_REG_WR, 0xFF23}, + {S5K6AAFX_REG_WR, 0x01AB}, + {S5K6AAFX_REG_WR, 0xFF81}, + {S5K6AAFX_REG_WR, 0xFF0D}, + {S5K6AAFX_REG_WR, 0x0169}, + {S5K6AAFX_REG_WR, 0x00DE}, + {S5K6AAFX_REG_WR, 0xFFEF}, + {S5K6AAFX_REG_WR, 0xFFCA}, + {S5K6AAFX_REG_WR, 0x014D}, + {S5K6AAFX_REG_WR, 0x01C3}, + {S5K6AAFX_REG_WR, 0xFF7E}, + {S5K6AAFX_REG_WR, 0x016F}, + + {S5K6AAFX_REG_WR, 0x01FA}, /* WW */ + {S5K6AAFX_REG_WR, 0xFFB9}, + {S5K6AAFX_REG_WR, 0xFFF8}, + {S5K6AAFX_REG_WR, 0x0116}, + {S5K6AAFX_REG_WR, 0x00BD}, + {S5K6AAFX_REG_WR, 0xFF38}, + {S5K6AAFX_REG_WR, 0xFF23}, + {S5K6AAFX_REG_WR, 0x01AB}, + {S5K6AAFX_REG_WR, 0xFF81}, + {S5K6AAFX_REG_WR, 0xFF0D}, + {S5K6AAFX_REG_WR, 0x0169}, + {S5K6AAFX_REG_WR, 0x00DE}, + {S5K6AAFX_REG_WR, 0xFFEF}, + {S5K6AAFX_REG_WR, 0xFFCA}, + {S5K6AAFX_REG_WR, 0x014D}, + {S5K6AAFX_REG_WR, 0x01C3}, + {S5K6AAFX_REG_WR, 0xFF7E}, + {S5K6AAFX_REG_WR, 0x016F}, + + {S5K6AAFX_REG_WR, 0x01FA}, /* CW */ + {S5K6AAFX_REG_WR, 0xFFB9}, + {S5K6AAFX_REG_WR, 0xFFF8}, + {S5K6AAFX_REG_WR, 0x0116}, + {S5K6AAFX_REG_WR, 0x00BD}, + {S5K6AAFX_REG_WR, 0xFF38}, + {S5K6AAFX_REG_WR, 0xFF23}, + {S5K6AAFX_REG_WR, 0x01AB}, + {S5K6AAFX_REG_WR, 0xFF81}, + {S5K6AAFX_REG_WR, 0xFF0D}, + {S5K6AAFX_REG_WR, 0x0169}, + {S5K6AAFX_REG_WR, 0x00DE}, + {S5K6AAFX_REG_WR, 0xFFEF}, + {S5K6AAFX_REG_WR, 0xFFCA}, + {S5K6AAFX_REG_WR, 0x014D}, + {S5K6AAFX_REG_WR, 0x01C3}, + {S5K6AAFX_REG_WR, 0xFF7E}, + {S5K6AAFX_REG_WR, 0x016F}, + + {S5K6AAFX_REG_WR, 0x0276/*0x028D*/}, /* D50 */ + {S5K6AAFX_REG_WR, 0xFFCA/*0xFFBC*/}, + {S5K6AAFX_REG_WR, 0xFFCF/*0xFFA9*/}, + {S5K6AAFX_REG_WR, 0x010D/*0x0124*/}, + {S5K6AAFX_REG_WR, 0x010E/*0x0115*/}, + {S5K6AAFX_REG_WR, 0xFF6D/*0xFF1B*/}, + {S5K6AAFX_REG_WR, 0xFE97/*0xFEC5*/}, + {S5K6AAFX_REG_WR, 0x0144/*0x01F8*/}, + {S5K6AAFX_REG_WR, 0xFF9E/*0xFF4B*/}, + {S5K6AAFX_REG_WR, 0xFE8A/*0xFEA4*/}, + {S5K6AAFX_REG_WR, 0x0137/*0x0198*/}, + {S5K6AAFX_REG_WR, 0x0131/*0x00D0*/}, + {S5K6AAFX_REG_WR, 0xFFF3/*0xFFE7*/}, + {S5K6AAFX_REG_WR, 0xFFF3/*0xFFB1*/}, + {S5K6AAFX_REG_WR, 0x0193/*0x016E*/}, + {S5K6AAFX_REG_WR, 0x0269/*0x0217*/}, + {S5K6AAFX_REG_WR, 0xFFBD/*0xFF43*/}, + {S5K6AAFX_REG_WR, 0x0162/*0x019E*/}, + + + {S5K6AAFX_REG_WR, 0x01A0}, /* D65 */ + {S5K6AAFX_REG_WR, 0xFFC4}, + {S5K6AAFX_REG_WR, 0xFF8F}, + {S5K6AAFX_REG_WR, 0x00FC}, + {S5K6AAFX_REG_WR, 0x012D}, + {S5K6AAFX_REG_WR, 0xFF21}, + {S5K6AAFX_REG_WR, 0xFF5C}, + {S5K6AAFX_REG_WR, 0x0169}, + {S5K6AAFX_REG_WR, 0xFF92}, + {S5K6AAFX_REG_WR, 0xFF60}, + {S5K6AAFX_REG_WR, 0x013C}, + {S5K6AAFX_REG_WR, 0x0171}, + {S5K6AAFX_REG_WR, 0x0004}, + {S5K6AAFX_REG_WR, 0xFFD3}, + {S5K6AAFX_REG_WR, 0x01DF}, + {S5K6AAFX_REG_WR, 0x01A4}, + {S5K6AAFX_REG_WR, 0xFF97}, + {S5K6AAFX_REG_WR, 0x016E}, + /* param_end - TVAR_wbt_pBaseCcms */ + + /* param_start - TVAR_wbt_pOutdoorCcm */ + {S5K6AAFX_REG_W_ADDL, 0x2380}, + {S5K6AAFX_REG_WR, 0x01F2/*0x01F2*//*0x019D*/}, /* TVAR_wbt_pOutdoorCcm[0] */ + {S5K6AAFX_REG_WR, 0xFFC3/*0xFFc3*//*0xFFC8*/}, /* TVAR_wbt_pOutdoorCcm[1] */ + {S5K6AAFX_REG_WR, 0xFFE3/*0xFFe3*//*0x000C*/}, /* TVAR_wbt_pOutdoorCcm[2] */ + {S5K6AAFX_REG_WR, 0x00F9/*0x00F9*//*0x008D*/}, /* TVAR_wbt_pOutdoorCcm[3] */ + {S5K6AAFX_REG_WR, 0x013F/*0x013F*//*0x00CD*/}, /* TVAR_wbt_pOutdoorCcm[4] */ + {S5K6AAFX_REG_WR, 0xFF6E/*0xFF6E*//*0xFF3F*/}, /* TVAR_wbt_pOutdoorCcm[5] */ + {S5K6AAFX_REG_WR, 0xFEBB/*0xFEbb*//*0xFEDD*/}, /* TVAR_wbt_pOutdoorCcm[6] */ + {S5K6AAFX_REG_WR, 0x01F2/*0x01F2*//*0x01C6*/}, /* TVAR_wbt_pOutdoorCcm[7] */ + {S5K6AAFX_REG_WR, 0xFEFA/*0xFEFA*//*0xFF77*/}, /* TVAR_wbt_pOutdoorCcm[8] */ + {S5K6AAFX_REG_WR, 0xFF37/*0xFF37*//*0xFEAB*/}, /* TVAR_wbt_pOutdoorCcm[9] */ + {S5K6AAFX_REG_WR, 0x01A2/*0x01A2*//*0x015D*/}, /* TVAR_wbt_pOutdoorCcm[10] */ + {S5K6AAFX_REG_WR, 0x0126/*0x0126*//*0x0082*/}, /* TVAR_wbt_pOutdoorCcm[11] */ + {S5K6AAFX_REG_WR, 0xFFE0/*0xFFE0*//*0xFFCA*/}, /* TVAR_wbt_pOutdoorCcm[12] */ + {S5K6AAFX_REG_WR, 0xFFBF/*0xFFBF*//*0xFFA2*/}, /* TVAR_wbt_pOutdoorCcm[13] */ + {S5K6AAFX_REG_WR, 0x01E6/*0x01E6*//*0x016F*/}, /* TVAR_wbt_pOutdoorCcm[14] */ + {S5K6AAFX_REG_WR, 0x0186/*0x0186*//*0x0171*/}, /* TVAR_wbt_pOutdoorCcm[15] */ + {S5K6AAFX_REG_WR, 0xFF4B/*0xFF4B*//*0xFF35*/}, /* TVAR_wbt_pOutdoorCcm[16] */ + {S5K6AAFX_REG_WR, 0x01B1/*0x01B1*//*0x013E*/}, /* TVAR_wbt_pOutdoorCcm[17] */ + /* param_end - TVAR_wbt_pOutdoorCcm */ + + {S5K6AAFX_REG_W_ADDL, 0x06D4}, + {S5K6AAFX_REG_WR, 0x2380}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_W_ADDL, 0x06CC}, + {S5K6AAFX_REG_WR, 0x23A4}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_W_ADDL, 0x06E8}, + {S5K6AAFX_REG_WR, 0x23A4}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x23C8}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x23EC}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x2410}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x2434}, + {S5K6AAFX_REG_WR, 0x7000}, + {S5K6AAFX_REG_WR, 0x2458}, + {S5K6AAFX_REG_WR, 0x7000}, + + {S5K6AAFX_REG_W_ADDL, 0x06DA}, + {S5K6AAFX_REG_WR, 0x00BF}, /* SARR_AwbCcmCord[0] */ + {S5K6AAFX_REG_WR, 0x00E6}, /* SARR_AwbCcmCord[1] */ + {S5K6AAFX_REG_WR, 0x00F2}, /* SARR_AwbCcmCord[2] */ + {S5K6AAFX_REG_WR, 0x0143}, /* SARR_AwbCcmCord[3] */ + {S5K6AAFX_REG_WR, 0x0178}, /* SARR_AwbCcmCord[4] */ + {S5K6AAFX_REG_WR, 0x01A3}, /* SARR_AwbCcmCord[5] */ + + /* param_start - SARR_uNormBrInDoor */ + {S5K6AAFX_REG_W_ADDL, 0x07E8}, + {S5K6AAFX_REG_WR, 0x0016/*0x000A*/}, /* SARR_uNormBrInDoor[0] */ + {S5K6AAFX_REG_WR, 0x0028/*0x0019*/}, /* SARR_uNormBrInDoor[1] */ + {S5K6AAFX_REG_WR, 0x0096/*0x0096*/}, /* SARR_uNormBrInDoor[2] */ + {S5K6AAFX_REG_WR, 0x01F4/*0x01F4*/}, /* SARR_uNormBrInDoor[3] */ + {S5K6AAFX_REG_WR, 0x07D0/*0x07D0*/}, /* SARR_uNormBrInDoor[4] */ + /* param_end - SARR_uNormBrInDoor */ + + /* param_start - afit_uNoiseIndInDoor */ + {S5K6AAFX_REG_W_ADDL, 0x07D0}, + {S5K6AAFX_REG_WR, 0x0030}, /* afit_uNoiseIndInDoor[0] */ + {S5K6AAFX_REG_WR, 0x0046}, /* afit_uNoiseIndInDoor[1] */ + {S5K6AAFX_REG_WR, 0x0088}, /* afit_uNoiseIndInDoor[2] */ + {S5K6AAFX_REG_WR, 0x0205}, /* afit_uNoiseIndInDoor[3] */ + {S5K6AAFX_REG_WR, 0x02BC}, /* afit_uNoiseIndInDoor[4] */ + /* param_end - afit_uNoiseIndInDoor */ + + {S5K6AAFX_REG_W_ADDL, 0x07E6}, + {S5K6AAFX_REG_WR, 0x0000}, /* afit_bUseNoiseInd */ + + /* param_start - TVAR_afit_pBaseVals */ + {S5K6AAFX_REG_W_ADDL, 0x0828}, + {S5K6AAFX_REG_WR, 0x0010}, /*TVAR_afit_pBaseVals[0] 70000828 */ + {S5K6AAFX_REG_WR, 0x0031}, /*TVAR_afit_pBaseVals[1] 7000082A */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[2] 7000082C */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[3] 7000082E */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[4] 70000830 */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[5] 70000832 */ + {S5K6AAFX_REG_WR, 0x0021}, /*TVAR_afit_pBaseVals[6] 70000834 */ + {S5K6AAFX_REG_WR, 0x0028}, /*TVAR_afit_pBaseVals[7] 70000836 */ + {S5K6AAFX_REG_WR, 0x0050}, /*TVAR_afit_pBaseVals[8] 70000838 */ + {S5K6AAFX_REG_WR, 0x00FF}, /*TVAR_afit_pBaseVals[9] 7000083A */ + {S5K6AAFX_REG_WR, 0x0129}, /*TVAR_afit_pBaseVals[10] 7000083C */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[11] 7000083E */ + {S5K6AAFX_REG_WR, 0x0028}, /*TVAR_afit_pBaseVals[12] 70000840 */ + {S5K6AAFX_REG_WR, 0x0028}, /*TVAR_afit_pBaseVals[13] 70000842 */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[14] 70000844 */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[15] 70000846 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[16] 70000848 */ + {S5K6AAFX_REG_WR, 0x0344}, /*TVAR_afit_pBaseVals[17] 7000084A */ + {S5K6AAFX_REG_WR, 0x033A}, /*TVAR_afit_pBaseVals[18] 7000084C */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[19] 7000084E */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[20] 70000850 */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[21] 70000852 */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[22] 70000854 */ + {S5K6AAFX_REG_WR, 0x001E}, /*TVAR_afit_pBaseVals[23] 70000856 */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[24] 70000858 */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[25] 7000085A */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[26] 7000085C */ + {S5K6AAFX_REG_WR, 0x0010}, /*TVAR_afit_pBaseVals[27] 7000085E */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[28] 70000860 */ + {S5K6AAFX_REG_WR, 0x0106}, /*TVAR_afit_pBaseVals[29] 70000862 */ + {S5K6AAFX_REG_WR, 0x006F}, /*TVAR_afit_pBaseVals[30] 70000864 */ + {S5K6AAFX_REG_WR, 0x0C0F}, /*TVAR_afit_pBaseVals[31] 70000866 */ + {S5K6AAFX_REG_WR, 0x0C0F}, /*TVAR_afit_pBaseVals[32] 70000868 */ + {S5K6AAFX_REG_WR, 0x0303}, /*TVAR_afit_pBaseVals[33] 7000086A */ + {S5K6AAFX_REG_WR, 0x0303}, /*TVAR_afit_pBaseVals[34] 7000086C */ + {S5K6AAFX_REG_WR, 0x140A}, /*TVAR_afit_pBaseVals[35] 7000086E */ + {S5K6AAFX_REG_WR, 0x140A}, /*TVAR_afit_pBaseVals[36] 70000870 */ + {S5K6AAFX_REG_WR, 0x2828}, /*TVAR_afit_pBaseVals[37] 70000872 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[38] 70000874 */ + {S5K6AAFX_REG_WR, 0x020A}, /*TVAR_afit_pBaseVals[39] 70000876 */ + {S5K6AAFX_REG_WR, 0x0480}, /*TVAR_afit_pBaseVals[40] 70000878 */ + {S5K6AAFX_REG_WR, 0x0E08}, /*TVAR_afit_pBaseVals[41] 7000087A */ + {S5K6AAFX_REG_WR, 0x030A}, /*TVAR_afit_pBaseVals[42] 7000087C */ + {S5K6AAFX_REG_WR, 0x0A03}, /*TVAR_afit_pBaseVals[43] 7000087E */ + {S5K6AAFX_REG_WR, 0x0A11}, /*TVAR_afit_pBaseVals[44] 70000880 */ + {S5K6AAFX_REG_WR, 0x000F}, /*TVAR_afit_pBaseVals[45] 70000882 */ + {S5K6AAFX_REG_WR, 0x0500}, /*TVAR_afit_pBaseVals[46] 70000884 */ + {S5K6AAFX_REG_WR, 0x0914}, /*TVAR_afit_pBaseVals[47] 70000886 */ + {S5K6AAFX_REG_WR, 0x0012}, /*TVAR_afit_pBaseVals[48] 70000888 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[49] 7000088A */ + {S5K6AAFX_REG_WR, 0x0005}, /*TVAR_afit_pBaseVals[50] 7000088C */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[51] 7000088E */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[52] 70000890 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[53] 70000892 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[54] 70000894 */ + {S5K6AAFX_REG_WR, 0x0A00}, /*TVAR_afit_pBaseVals[55] 70000896 */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[56] 70000898 */ + {S5K6AAFX_REG_WR, 0x014C}, /*TVAR_afit_pBaseVals[57] 7000089A */ + {S5K6AAFX_REG_WR, 0x014D}, /*TVAR_afit_pBaseVals[58] 7000089C */ + {S5K6AAFX_REG_WR, 0x0100}, /*TVAR_afit_pBaseVals[59] 7000089E */ + {S5K6AAFX_REG_WR, 0xA020/*0x8020*/}, /*TVAR_afit_pBaseVals[60] 700008A0 */ //CKLIN_20100908 White level low brightness 2.5 pass + {S5K6AAFX_REG_WR, 0x0180}, /*TVAR_afit_pBaseVals[61] 700008A2 */ + {S5K6AAFX_REG_WR, 0x0001/*0x000A*/}, /*TVAR_afit_pBaseVals[62] 700008A4 */ //CKLIN_20100908 Black Noise pass + + {S5K6AAFX_REG_WR, 0xFFFE}, /*TVAR_afit_pBaseVals[63] 700008A6 */ + {S5K6AAFX_REG_WR, 0x0031}, /*TVAR_afit_pBaseVals[64] 700008A8 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[65] 700008AA */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[66] 700008AC */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[67] 700008AE */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[68] 700008B0 */ + {S5K6AAFX_REG_WR, 0x000C}, /*TVAR_afit_pBaseVals[69] 700008B2 */ + {S5K6AAFX_REG_WR, 0x000E}, /*TVAR_afit_pBaseVals[70] 700008B4 */ + {S5K6AAFX_REG_WR, 0x0050}, /*TVAR_afit_pBaseVals[71] 700008B6 */ + {S5K6AAFX_REG_WR, 0x00FF}, /*TVAR_afit_pBaseVals[72] 700008B8 */ + {S5K6AAFX_REG_WR, 0x0129}, /*TVAR_afit_pBaseVals[73] 700008BA */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[74] 700008BC */ + {S5K6AAFX_REG_WR, 0x0028}, /*TVAR_afit_pBaseVals[75] 700008BE */ + {S5K6AAFX_REG_WR, 0x0028}, /*TVAR_afit_pBaseVals[76] 700008C0 */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[77] 700008C2 */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[78] 700008C4 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[79] 700008C6 */ + {S5K6AAFX_REG_WR, 0x0114}, /*TVAR_afit_pBaseVals[80] 700008C8 */ + {S5K6AAFX_REG_WR, 0x020A}, /*TVAR_afit_pBaseVals[81] 700008CA */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[82] 700008CC */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[83] 700008CE */ + {S5K6AAFX_REG_WR, 0x0018}, /*TVAR_afit_pBaseVals[84] 700008D0 */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[85] 700008D2 */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[86] 700008D4 */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[87] 700008D6 */ + {S5K6AAFX_REG_WR, 0x0028}, /*TVAR_afit_pBaseVals[88] 700008D8 */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[89] 700008DA */ + {S5K6AAFX_REG_WR, 0x0010}, /*TVAR_afit_pBaseVals[90] 700008DC */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[91] 700008DE */ + {S5K6AAFX_REG_WR, 0x0106}, /*TVAR_afit_pBaseVals[92] 700008E0 */ + {S5K6AAFX_REG_WR, 0x006F}, /*TVAR_afit_pBaseVals[93] 700008E2 */ + {S5K6AAFX_REG_WR, 0x050F}, /*TVAR_afit_pBaseVals[94] 700008E4 */ + {S5K6AAFX_REG_WR, 0x0A1F}, /*TVAR_afit_pBaseVals[95] 700008E6 */ + {S5K6AAFX_REG_WR, 0x0203}, /*TVAR_afit_pBaseVals[96] 700008E8 */ + {S5K6AAFX_REG_WR, 0x0303}, /*TVAR_afit_pBaseVals[97] 700008EA */ + {S5K6AAFX_REG_WR, 0x140A}, /*TVAR_afit_pBaseVals[98] 700008EC */ + {S5K6AAFX_REG_WR, 0x140A}, /*TVAR_afit_pBaseVals[99] 700008EE */ + {S5K6AAFX_REG_WR, 0x2828}, /*TVAR_afit_pBaseVals[100] 700008F0 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[101] 700008F2 */ + {S5K6AAFX_REG_WR, 0x020A}, /*TVAR_afit_pBaseVals[102] 700008F4 */ + {S5K6AAFX_REG_WR, 0x0480}, /*TVAR_afit_pBaseVals[103] 700008F6 */ + {S5K6AAFX_REG_WR, 0x0E08}, /*TVAR_afit_pBaseVals[104] 700008F8 */ + {S5K6AAFX_REG_WR, 0x030A}, /*TVAR_afit_pBaseVals[105] 700008FA */ + {S5K6AAFX_REG_WR, 0x1403}, /*TVAR_afit_pBaseVals[106] 700008FC */ + {S5K6AAFX_REG_WR, 0x0A11}, /*TVAR_afit_pBaseVals[107] 700008FE */ + {S5K6AAFX_REG_WR, 0x0A0F}, /*TVAR_afit_pBaseVals[108] 70000900 */ + {S5K6AAFX_REG_WR, 0x050A}, /*TVAR_afit_pBaseVals[109] 70000902 */ + {S5K6AAFX_REG_WR, 0x101E}, /*TVAR_afit_pBaseVals[110] 70000904 */ + {S5K6AAFX_REG_WR, 0x101E}, /*TVAR_afit_pBaseVals[111] 70000906 */ + {S5K6AAFX_REG_WR, 0x0A08}, /*TVAR_afit_pBaseVals[112] 70000908 */ + {S5K6AAFX_REG_WR, 0x0005}, /*TVAR_afit_pBaseVals[113] 7000090A */ + {S5K6AAFX_REG_WR, 0x0400}, /*TVAR_afit_pBaseVals[114] 7000090C */ + {S5K6AAFX_REG_WR, 0x0400}, /*TVAR_afit_pBaseVals[115] 7000090E */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[116] 70000910 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[117] 70000912 */ + {S5K6AAFX_REG_WR, 0x0A00}, /*TVAR_afit_pBaseVals[118] 70000914 */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[119] 70000916 */ + {S5K6AAFX_REG_WR, 0x0180}, /*TVAR_afit_pBaseVals[120] 70000918 */ + {S5K6AAFX_REG_WR, 0x0151}, /*TVAR_afit_pBaseVals[121] 7000091A */ + {S5K6AAFX_REG_WR, 0x0100}, /*TVAR_afit_pBaseVals[122] 7000091C */ + {S5K6AAFX_REG_WR, 0x9820}, /*TVAR_afit_pBaseVals[123] 7000091E */ + {S5K6AAFX_REG_WR, 0x0180}, /*TVAR_afit_pBaseVals[124] 70000920 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[125] 70000922 */ + + {S5K6AAFX_REG_WR, 0xFFFB}, /*TVAR_afit_pBaseVals[126] 70000924 */ + {S5K6AAFX_REG_WR, 0x0031}, /*TVAR_afit_pBaseVals[127] 70000926 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[128] 70000928 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[129] 7000092A */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[130] 7000092C */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[131] 7000092E */ + {S5K6AAFX_REG_WR, 0x0008}, /*TVAR_afit_pBaseVals[132] 70000930 */ + {S5K6AAFX_REG_WR, 0x0006}, /*TVAR_afit_pBaseVals[133] 70000932 */ + {S5K6AAFX_REG_WR, 0x0050}, /*TVAR_afit_pBaseVals[134] 70000934 */ + {S5K6AAFX_REG_WR, 0x0050}, /*TVAR_afit_pBaseVals[135] 70000936 */ + {S5K6AAFX_REG_WR, 0x0002}, /*TVAR_afit_pBaseVals[136] 70000938 */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[137] 7000093A */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[138] 7000093C */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[139] 7000093E */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[140] 70000940 */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[141] 70000942 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[142] 70000944 */ + {S5K6AAFX_REG_WR, 0x0014}, /*TVAR_afit_pBaseVals[143] 70000946 */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[144] 70000948 */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[145] 7000094A */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[146] 7000094C */ + {S5K6AAFX_REG_WR, 0x001C}, /*TVAR_afit_pBaseVals[147] 7000094E */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[148] 70000950 */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[149] 70000952 */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[150] 70000954 */ + {S5K6AAFX_REG_WR, 0x0028}, /*TVAR_afit_pBaseVals[151] 70000956 */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[152] 70000958 */ + {S5K6AAFX_REG_WR, 0x0010}, /*TVAR_afit_pBaseVals[153] 7000095A */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[154] 7000095C */ + {S5K6AAFX_REG_WR, 0x0106}, /*TVAR_afit_pBaseVals[155] 7000095E */ + {S5K6AAFX_REG_WR, 0x006F}, /*TVAR_afit_pBaseVals[156] 70000960 */ + {S5K6AAFX_REG_WR, 0x0205}, /*TVAR_afit_pBaseVals[157] 70000962 */ + {S5K6AAFX_REG_WR, 0x051E}, /*TVAR_afit_pBaseVals[158] 70000964 */ + {S5K6AAFX_REG_WR, 0x0101}, /*TVAR_afit_pBaseVals[159] 70000966 */ + {S5K6AAFX_REG_WR, 0x0202}, /*TVAR_afit_pBaseVals[160] 70000968 */ + {S5K6AAFX_REG_WR, 0x140A}, /*TVAR_afit_pBaseVals[161] 7000096A */ + {S5K6AAFX_REG_WR, 0x140A}, /*TVAR_afit_pBaseVals[162] 7000096C */ + {S5K6AAFX_REG_WR, 0x2828}, /*TVAR_afit_pBaseVals[163] 7000096E */ + {S5K6AAFX_REG_WR, 0x0606}, /*TVAR_afit_pBaseVals[164] 70000970 */ + {S5K6AAFX_REG_WR, 0x0205}, /*TVAR_afit_pBaseVals[165] 70000972 */ + {S5K6AAFX_REG_WR, 0x0480}, /*TVAR_afit_pBaseVals[166] 70000974 */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[167] 70000976 */ + {S5K6AAFX_REG_WR, 0x0005}, /*TVAR_afit_pBaseVals[168] 70000978 */ + {S5K6AAFX_REG_WR, 0x1903}, /*TVAR_afit_pBaseVals[169] 7000097A */ + {S5K6AAFX_REG_WR, 0x1911}, /*TVAR_afit_pBaseVals[170] 7000097C */ + {S5K6AAFX_REG_WR, 0x0A0F}, /*TVAR_afit_pBaseVals[171] 7000097E */ + {S5K6AAFX_REG_WR, 0x050A}, /*TVAR_afit_pBaseVals[172] 70000980 */ + {S5K6AAFX_REG_WR, 0x2028}, /*TVAR_afit_pBaseVals[173] 70000982 */ + {S5K6AAFX_REG_WR, 0x2028}, /*TVAR_afit_pBaseVals[174] 70000984 */ + {S5K6AAFX_REG_WR, 0x0A08}, /*TVAR_afit_pBaseVals[175] 70000986 */ + {S5K6AAFX_REG_WR, 0x0007}, /*TVAR_afit_pBaseVals[176] 70000988 */ + {S5K6AAFX_REG_WR, 0x0403}, /*TVAR_afit_pBaseVals[177] 7000098A */ + {S5K6AAFX_REG_WR, 0x0402}, /*TVAR_afit_pBaseVals[178] 7000098C */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[179] 7000098E */ + {S5K6AAFX_REG_WR, 0x0203}, /*TVAR_afit_pBaseVals[180] 70000990 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[181] 70000992 */ + {S5K6AAFX_REG_WR, 0x0006}, /*TVAR_afit_pBaseVals[182] 70000994 */ + {S5K6AAFX_REG_WR, 0x0180}, /*TVAR_afit_pBaseVals[183] 70000996 */ + {S5K6AAFX_REG_WR, 0x0170}, /*TVAR_afit_pBaseVals[184] 70000998 */ + {S5K6AAFX_REG_WR, 0x0100}, /*TVAR_afit_pBaseVals[185] 7000099A */ + {S5K6AAFX_REG_WR, 0x8050/*0x8030*/}, /*TVAR_afit_pBaseVals[186] 7000099C */ //CKLIN_20100908 Shading pass + {S5K6AAFX_REG_WR, 0x0180}, /*TVAR_afit_pBaseVals[187] 7000099E */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[188] 700009A0 */ + + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[189] 700009A2 */ + {S5K6AAFX_REG_WR, 0x0031}, /*TVAR_afit_pBaseVals[190] 700009A4 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[191] 700009A6 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[192] 700009A8 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[193] 700009AA */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[194] 700009AC */ + {S5K6AAFX_REG_WR, 0x0008}, /*TVAR_afit_pBaseVals[195] 700009AE */ + {S5K6AAFX_REG_WR, 0x0006}, /*TVAR_afit_pBaseVals[196] 700009B0 */ + {S5K6AAFX_REG_WR, 0x0050}, /*TVAR_afit_pBaseVals[197] 700009B2 */ + {S5K6AAFX_REG_WR, 0x0050}, /*TVAR_afit_pBaseVals[198] 700009B4 */ + {S5K6AAFX_REG_WR, 0x0002}, /*TVAR_afit_pBaseVals[199] 700009B6 */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[200] 700009B8 */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[201] 700009BA */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[202] 700009BC */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[203] 700009BE */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[204] 700009C0 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[205] 700009C2 */ + {S5K6AAFX_REG_WR, 0x0014}, /*TVAR_afit_pBaseVals[206] 700009C4 */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[207] 700009C6 */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[208] 700009C8 */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[209] 700009CA */ + {S5K6AAFX_REG_WR, 0x001C}, /*TVAR_afit_pBaseVals[210] 700009CC */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[211] 700009CE */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[212] 700009D0 */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[213] 700009D2 */ + {S5K6AAFX_REG_WR, 0x0028}, /*TVAR_afit_pBaseVals[214] 700009D4 */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[215] 700009D6 */ + {S5K6AAFX_REG_WR, 0x0010}, /*TVAR_afit_pBaseVals[216] 700009D8 */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[217] 700009DA */ + {S5K6AAFX_REG_WR, 0x0106}, /*TVAR_afit_pBaseVals[218] 700009DC */ + {S5K6AAFX_REG_WR, 0x006F}, /*TVAR_afit_pBaseVals[219] 700009DE */ + {S5K6AAFX_REG_WR, 0x0205}, /*TVAR_afit_pBaseVals[220] 700009E0 */ + {S5K6AAFX_REG_WR, 0x051E}, /*TVAR_afit_pBaseVals[221] 700009E2 */ + {S5K6AAFX_REG_WR, 0x0101}, /*TVAR_afit_pBaseVals[222] 700009E4 */ + {S5K6AAFX_REG_WR, 0x0202}, /*TVAR_afit_pBaseVals[223] 700009E6 */ + {S5K6AAFX_REG_WR, 0x140A}, /*TVAR_afit_pBaseVals[224] 700009E8 */ + {S5K6AAFX_REG_WR, 0x140A}, /*TVAR_afit_pBaseVals[225] 700009EA */ + {S5K6AAFX_REG_WR, 0x2828}, /*TVAR_afit_pBaseVals[226] 700009EC */ + {S5K6AAFX_REG_WR, 0x0606}, /*TVAR_afit_pBaseVals[227] 700009EE */ + {S5K6AAFX_REG_WR, 0x0205}, /*TVAR_afit_pBaseVals[228] 700009F0 */ + {S5K6AAFX_REG_WR, 0x0480}, /*TVAR_afit_pBaseVals[229] 700009F2 */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[230] 700009F4 */ + {S5K6AAFX_REG_WR, 0x0005}, /*TVAR_afit_pBaseVals[231] 700009F6 */ + {S5K6AAFX_REG_WR, 0x1903}, /*TVAR_afit_pBaseVals[232] 700009F8 */ + {S5K6AAFX_REG_WR, 0x1911}, /*TVAR_afit_pBaseVals[233] 700009FA */ + {S5K6AAFX_REG_WR, 0x0A0F}, /*TVAR_afit_pBaseVals[234] 700009FC */ + {S5K6AAFX_REG_WR, 0x050A}, /*TVAR_afit_pBaseVals[235] 700009FE */ + {S5K6AAFX_REG_WR, 0x2028}, /*TVAR_afit_pBaseVals[236] 70000A00 */ + {S5K6AAFX_REG_WR, 0x2028}, /*TVAR_afit_pBaseVals[237] 70000A02 */ + {S5K6AAFX_REG_WR, 0x0A08}, /*TVAR_afit_pBaseVals[238] 70000A04 */ + {S5K6AAFX_REG_WR, 0x0007}, /*TVAR_afit_pBaseVals[239] 70000A06 */ + {S5K6AAFX_REG_WR, 0x0403}, /*TVAR_afit_pBaseVals[240] 70000A08 */ + {S5K6AAFX_REG_WR, 0x0402}, /*TVAR_afit_pBaseVals[241] 70000A0A */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[242] 70000A0C */ + {S5K6AAFX_REG_WR, 0x0203}, /*TVAR_afit_pBaseVals[243] 70000A0E */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[244] 70000A10 */ + {S5K6AAFX_REG_WR, 0x0006}, /*TVAR_afit_pBaseVals[245] 70000A12 */ + {S5K6AAFX_REG_WR, 0x0170}, /*TVAR_afit_pBaseVals[246] 70000A14 */ + {S5K6AAFX_REG_WR, 0x0175}, /*TVAR_afit_pBaseVals[247] 70000A16 */ + {S5K6AAFX_REG_WR, 0x0100}, /*TVAR_afit_pBaseVals[248] 70000A18 */ + {S5K6AAFX_REG_WR, 0x8070/*0x8068*/}, /*TVAR_afit_pBaseVals[249] 70000A1A */ //CKLIN_20100908 Shading pass + {S5K6AAFX_REG_WR, 0x0180}, /*TVAR_afit_pBaseVals[250] 70000A1C */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[251] 70000A1E */ + + {S5K6AAFX_REG_WR, 0x0032/*0x0000*/}, /*TVAR_afit_pBaseVals[252] 70000A20 */ //CKLIN_20100908 White level high brightness 286~2292 pass + {S5K6AAFX_REG_WR, 0x0031}, /*TVAR_afit_pBaseVals[253] 70000A22 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[254] 70000A24 */ + {S5K6AAFX_REG_WR, 0x0014}, /*TVAR_afit_pBaseVals[255] 70000A26 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[256] 70000A28 */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[257] 70000A2A */ + {S5K6AAFX_REG_WR, 0x000E}, /*TVAR_afit_pBaseVals[258] 70000A2C */ + {S5K6AAFX_REG_WR, 0x0006}, /*TVAR_afit_pBaseVals[259] 70000A2E */ + {S5K6AAFX_REG_WR, 0x0020}, /*TVAR_afit_pBaseVals[260] 70000A30 */ + {S5K6AAFX_REG_WR, 0x0050}, /*TVAR_afit_pBaseVals[261] 70000A32 */ + {S5K6AAFX_REG_WR, 0x0002}, /*TVAR_afit_pBaseVals[262] 70000A34 */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[263] 70000A36 */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[264] 70000A38 */ + {S5K6AAFX_REG_WR, 0x000A}, /*TVAR_afit_pBaseVals[265] 70000A3A */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[266] 70000A3C */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[267] 70000A3E */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[268] 70000A40 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[269] 70000A42 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[270] 70000A44 */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[271] 70000A46 */ + {S5K6AAFX_REG_WR, 0x03FF}, /*TVAR_afit_pBaseVals[272] 70000A48 */ + {S5K6AAFX_REG_WR, 0x0014}, /*TVAR_afit_pBaseVals[273] 70000A4A */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[274] 70000A4C */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[275] 70000A4E */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[276] 70000A50 */ + {S5K6AAFX_REG_WR, 0x0020}, /*TVAR_afit_pBaseVals[277] 70000A52 */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[278] 70000A54 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[279] 70000A56 */ + {S5K6AAFX_REG_WR, 0x0032}, /*TVAR_afit_pBaseVals[280] 70000A58 */ + {S5K6AAFX_REG_WR, 0x0106}, /*TVAR_afit_pBaseVals[281] 70000A5A */ + {S5K6AAFX_REG_WR, 0x006F}, /*TVAR_afit_pBaseVals[282] 70000A5C */ + {S5K6AAFX_REG_WR, 0x0202}, /*TVAR_afit_pBaseVals[283] 70000A5E */ + {S5K6AAFX_REG_WR, 0x051E}, /*TVAR_afit_pBaseVals[284] 70000A60 */ + {S5K6AAFX_REG_WR, 0x0101}, /*TVAR_afit_pBaseVals[285] 70000A62 */ + {S5K6AAFX_REG_WR, 0x0202}, /*TVAR_afit_pBaseVals[286] 70000A64 */ + {S5K6AAFX_REG_WR, 0x140A}, /*TVAR_afit_pBaseVals[287] 70000A66 */ + {S5K6AAFX_REG_WR, 0x140A}, /*TVAR_afit_pBaseVals[288] 70000A68 */ + {S5K6AAFX_REG_WR, 0x2828}, /*TVAR_afit_pBaseVals[289] 70000A6A */ + {S5K6AAFX_REG_WR, 0x0606}, /*TVAR_afit_pBaseVals[290] 70000A6C */ + {S5K6AAFX_REG_WR, 0x0205}, /*TVAR_afit_pBaseVals[291] 70000A6E */ + {S5K6AAFX_REG_WR, 0x0880}, /*TVAR_afit_pBaseVals[292] 70000A70 */ + {S5K6AAFX_REG_WR, 0x000F}, /*TVAR_afit_pBaseVals[293] 70000A72 */ + {S5K6AAFX_REG_WR, 0x0005}, /*TVAR_afit_pBaseVals[294] 70000A74 */ + {S5K6AAFX_REG_WR, 0x1903}, /*TVAR_afit_pBaseVals[295] 70000A76 */ + {S5K6AAFX_REG_WR, 0x1911}, /*TVAR_afit_pBaseVals[296] 70000A78 */ + {S5K6AAFX_REG_WR, 0x0A0F}, /*TVAR_afit_pBaseVals[297] 70000A7A */ + {S5K6AAFX_REG_WR, 0x050A}, /*TVAR_afit_pBaseVals[298] 70000A7C */ + {S5K6AAFX_REG_WR, 0x2020}, /*TVAR_afit_pBaseVals[299] 70000A7E */ + {S5K6AAFX_REG_WR, 0x2020}, /*TVAR_afit_pBaseVals[300] 70000A80 */ + {S5K6AAFX_REG_WR, 0x0A08}, /*TVAR_afit_pBaseVals[301] 70000A82 */ + {S5K6AAFX_REG_WR, 0x0007}, /*TVAR_afit_pBaseVals[302] 70000A84 */ + {S5K6AAFX_REG_WR, 0x0408}, /*TVAR_afit_pBaseVals[303] 70000A86 */ + {S5K6AAFX_REG_WR, 0x0406}, /*TVAR_afit_pBaseVals[304] 70000A88 */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[305] 70000A8A */ + {S5K6AAFX_REG_WR, 0x0608}, /*TVAR_afit_pBaseVals[306] 70000A8C */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[307] 70000A8E */ + {S5K6AAFX_REG_WR, 0x0006}, /*TVAR_afit_pBaseVals[308] 70000A90 */ + {S5K6AAFX_REG_WR, 0x0170}, /*TVAR_afit_pBaseVals[309] 70000A92 */ + {S5K6AAFX_REG_WR, 0x0175}, /*TVAR_afit_pBaseVals[310] 70000A94 */ + {S5K6AAFX_REG_WR, 0x0100}, /*TVAR_afit_pBaseVals[311] 70000A96 */ + {S5K6AAFX_REG_WR, 0x7058/*0x7050*/}, /*TVAR_afit_pBaseVals[312] 70000A98 */ //CKLIN_20100908 Shading pass + {S5K6AAFX_REG_WR, 0x0180}, /*TVAR_afit_pBaseVals[313] 70000A9A */ + {S5K6AAFX_REG_WR, 0x0000}, /*TVAR_afit_pBaseVals[314] 70000A9C */ + /* param_end - TVAR_afit_pBaseVals */ + + /* param_start - afit_pConstBaseVals */ + {S5K6AAFX_REG_WR, 0x00FF}, /* afit_pConstBaseVals[0] */ + {S5K6AAFX_REG_WR, 0x00FF}, /* afit_pConstBaseVals[1] */ + {S5K6AAFX_REG_WR, 0x0800}, /* afit_pConstBaseVals[2] */ + {S5K6AAFX_REG_WR, 0x0600}, /* afit_pConstBaseVals[3] */ + {S5K6AAFX_REG_WR, 0x0000}, /* afit_pConstBaseVals[4] */ + {S5K6AAFX_REG_WR, 0x0000}, /* afit_pConstBaseVals[5] */ + {S5K6AAFX_REG_WR, 0x0000}, /* afit_pConstBaseVals[6] */ + {S5K6AAFX_REG_WR, 0x0300}, /* afit_pConstBaseVals[7] */ + {S5K6AAFX_REG_WR, 0x0002}, /* afit_pConstBaseVals[8] */ + {S5K6AAFX_REG_WR, 0x0400}, /* afit_pConstBaseVals[9] */ + {S5K6AAFX_REG_WR, 0x0106}, /* afit_pConstBaseVals[10] */ + {S5K6AAFX_REG_WR, 0x0005}, /* afit_pConstBaseVals[11] */ + {S5K6AAFX_REG_WR, 0x0000}, /* afit_pConstBaseVals[12] */ + {S5K6AAFX_REG_WR, 0x0703}, /* afit_pConstBaseVals[13] */ + {S5K6AAFX_REG_WR, 0x0000}, /* afit_pConstBaseVals[14] */ + {S5K6AAFX_REG_WR, 0xFFD6}, /* afit_pConstBaseVals[15] */ + {S5K6AAFX_REG_WR, 0x53C1}, /* afit_pConstBaseVals[16] */ + {S5K6AAFX_REG_WR, 0xE1FE}, /* afit_pConstBaseVals[17] */ + {S5K6AAFX_REG_WR, 0x0001}, /* afit_pConstBaseVals[18] */ + /* param_end - afit_pConstBaseVals */ + + + {S5K6AAFX_REG_W_ADDL, 0x0488}, + {S5K6AAFX_REG_WR, 0x416E}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0xA316}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_W_ADDL, 0x2174}, + {S5K6AAFX_REG_WR, 0xF424}, + {S5K6AAFX_REG_WR, 0x0000}, + + {S5K6AAFX_REG_W_ADDL, 0x0490}, + {S5K6AAFX_REG_WR, 0x416E}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_WR, 0xA316}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_W_ADDL, 0x2178}, + {S5K6AAFX_REG_WR, 0xF424}, + {S5K6AAFX_REG_WR, 0x0000}, + + {S5K6AAFX_REG_W_ADDL, 0x0498}, + {S5K6AAFX_REG_WR, 0x01E8}, + {S5K6AAFX_REG_WR, 0x0310/*0x0270*/}, + {S5K6AAFX_REG_W_ADDL, 0x217C}, + {S5K6AAFX_REG_WR, 0x0580}, + {S5K6AAFX_REG_W_ADDL, 0x049C}, + {S5K6AAFX_REG_WR, 0x0160}, + + + /* WRITE #AWBBTune_EVT4_uMaxExp3 0000 */ + /* WRITE #AWBBTune_EVT4_uCapMaxExp3 0000 */ + /* WRITE #AWBBTune_EVT4_uMaxAnGain3 0000 */ + + {S5K6AAFX_REG_W_ADDL, 0x2170}, + {S5K6AAFX_REG_WR, 0x0001}, + {S5K6AAFX_REG_WR, 0x0090}, + + /* AE Weight */ + {S5K6AAFX_REG_W_ADDL, 0x100E}, + {S5K6AAFX_REG_WR, 0x0101}, + {S5K6AAFX_REG_WR, 0x0101}, + {S5K6AAFX_REG_WR, 0x0101}, + {S5K6AAFX_REG_WR, 0x0101}, + {S5K6AAFX_REG_WR, 0x0101}, + {S5K6AAFX_REG_WR, 0x0101}, + {S5K6AAFX_REG_WR, 0x0101}, + {S5K6AAFX_REG_WR, 0x0101}, + {S5K6AAFX_REG_WR, 0x0201}, + {S5K6AAFX_REG_WR, 0x0202}, + {S5K6AAFX_REG_WR, 0x0202}, + {S5K6AAFX_REG_WR, 0x0102}, + {S5K6AAFX_REG_WR, 0x0201}, + {S5K6AAFX_REG_WR, 0x0302}, + {S5K6AAFX_REG_WR, 0x0203}, + {S5K6AAFX_REG_WR, 0x0102}, + {S5K6AAFX_REG_WR, 0x0201}, + {S5K6AAFX_REG_WR, 0x0302}, + {S5K6AAFX_REG_WR, 0x0203}, + {S5K6AAFX_REG_WR, 0x0102}, + {S5K6AAFX_REG_WR, 0x0201}, + {S5K6AAFX_REG_WR, 0x0302}, + {S5K6AAFX_REG_WR, 0x0203}, + {S5K6AAFX_REG_WR, 0x0102}, + {S5K6AAFX_REG_WR, 0x0101}, + {S5K6AAFX_REG_WR, 0x0202}, + {S5K6AAFX_REG_WR, 0x0202}, + {S5K6AAFX_REG_WR, 0x0101}, + {S5K6AAFX_REG_WR, 0x0101}, + {S5K6AAFX_REG_WR, 0x0101}, + {S5K6AAFX_REG_WR, 0x0101}, + {S5K6AAFX_REG_WR, 0x0101}, +}; + +static const struct s5k6aafx_i2c_reg_conf const clk_init_tbl[] = { + /* clk Settings */ + {S5K6AAFX_REG_W_ADDL, 0x01B8}, + {S5K6AAFX_REG_WR, 0x5DC0}, /* 24MHz input clock */ + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_W_ADDL, 0x01C6}, + {S5K6AAFX_REG_WR, 0x0002}, /* PLL configurations */ + {S5K6AAFX_REG_W_ADDL, 0x01CC}, + {S5K6AAFX_REG_WR, 0x1770}, /* 1st system CLK 24MHz */ + {S5K6AAFX_REG_WR, 0x1770}, /* 24MHz output clock */ + {S5K6AAFX_REG_WR, 0x1770}, /* 2nd system CLK */ + {S5K6AAFX_REG_WR, 0x1B58}, + {S5K6AAFX_REG_WR, 0x36B0}, + {S5K6AAFX_REG_WR, 0x36B0}, + {S5K6AAFX_REG_W_ADDL, 0x01E0}, + {S5K6AAFX_REG_WR, 0x0001}, + + /* delay 100ms */ +}; + +static const struct s5k6aafx_i2c_reg_conf const clk_init_tb2[] = { + /* clk Settings */ + {S5K6AAFX_REG_W_ADDL, 0x01B8}, + {S5K6AAFX_REG_WR, 0x5DC0}, /* 24MHz input clock */ + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_W_ADDL, 0x01C6}, + {S5K6AAFX_REG_WR, 0x0002}, /* PLL configurations */ + {S5K6AAFX_REG_W_ADDL, 0x01CC}, + {S5K6AAFX_REG_WR, 0x1770}, /* 1st system CLK 24MHz */ + {S5K6AAFX_REG_WR, 0x1770}, /* 24MHz output clock */ + {S5K6AAFX_REG_WR, 0x1770}, /* 2nd system CLK */ + {S5K6AAFX_REG_WR, 0x1770}, + {S5K6AAFX_REG_WR, 0x2EE0}, + {S5K6AAFX_REG_WR, 0x2EE0}, + {S5K6AAFX_REG_W_ADDL, 0x01E0}, + {S5K6AAFX_REG_WR, 0x0001}, + + /* delay 100ms */ +}; + +static const struct s5k6aafx_i2c_reg_conf const prev_snap_conf_init_tbl[] = { + /* PREVIEW CONFIGURATION 3 (VGA, YUV) */ + {S5K6AAFX_REG_W_ADDL, 0x02B4}, + {S5K6AAFX_REG_WR, S5K6AAFX_QTR_SIZE_WIDTH}, + {S5K6AAFX_REG_WR, S5K6AAFX_QTR_SIZE_HEIGHT}, + {S5K6AAFX_REG_WR, 0x0005}, /* YUV */ + {S5K6AAFX_REG_W_ADDL, 0x02C0}, + {S5K6AAFX_REG_WR, 0x0000}, /* PLL config */ + {S5K6AAFX_REG_W_ADDL, 0x02BA}, + {S5K6AAFX_REG_WR, 0x1770}, + {S5K6AAFX_REG_WR, 0x1700}, + {S5K6AAFX_REG_WR, 0x0042}, + {S5K6AAFX_REG_W_ADDL, 0x02C4}, + {S5K6AAFX_REG_WR, 0x0001}, /* 1b: Avg S.S 2b: SXGA */ + {S5K6AAFX_REG_W_ADDL, 0x02C2}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_W_ADDL, 0x02C6}, + {S5K6AAFX_REG_WR, 0x03E8}, + {S5K6AAFX_REG_WR, 0x0168}, + {S5K6AAFX_REG_W_ADDL, 0x02D4},/* flip and mirror */ + {S5K6AAFX_REG_WR, 0x0002},/* 0x02D4 value */ + {S5K6AAFX_REG_WR, 0x0002},/* 0x02D4 value */ + + /*********** APPLY PREVIEW CONFIGURATION & RUN PREVIEW ***********/ + /* REG_TC_GP_ActivePrevConfig-Select preview configuration_3 */ + {S5K6AAFX_REG_W_ADDL, 0x021C}, + {S5K6AAFX_REG_WR, 0x0003}, + /* REG_TC_GP_PrevOpenAfterChange */ + {S5K6AAFX_REG_W_ADDL, 0x0220}, + {S5K6AAFX_REG_WR, 0x0001}, + /* REG_TC_GP_NewConfigSync-Update preview configuration */ + {S5K6AAFX_REG_W_ADDL, 0x01F8}, + {S5K6AAFX_REG_WR, 0x0001}, + /* REG_TC_GP_PrevConfigChanged */ + {S5K6AAFX_REG_W_ADDL, 0x021E}, + {S5K6AAFX_REG_WR, 0x0001}, /* Enable output after config change */ + {S5K6AAFX_REG_W_ADDL, 0x01F0}, + {S5K6AAFX_REG_WR, 0x0001}, /* REG_TC_GP_EnablePreview - Start preview */ + {S5K6AAFX_REG_WR, 0x0001}, /* REG_TC_GP_EnablePreviewChanged */ + + + /* CAPTURE CONFIGURATION 0 (SXGA, YUV) */ + {S5K6AAFX_REG_W_ADDL, 0x030E}, + {S5K6AAFX_REG_WR, S5K6AAFX_FULL_SIZE_WIDTH}, /* 1280 */ + {S5K6AAFX_REG_WR, S5K6AAFX_FULL_SIZE_HEIGHT}, /* 1024 */ + {S5K6AAFX_REG_WR, 0x0005}, /* YUV */ + {S5K6AAFX_REG_W_ADDL, 0x031A}, + {S5K6AAFX_REG_WR, 0x0000}, /* PLL config */ + {S5K6AAFX_REG_W_ADDL, 0x0314}, + {S5K6AAFX_REG_WR, 0x1770}, + {S5K6AAFX_REG_WR, 0x1770}, + {S5K6AAFX_REG_WR, 0x0042}, + {S5K6AAFX_REG_W_ADDL, 0x031E}, + {S5K6AAFX_REG_WR, 0x0002}, /* 1b: Avg S.S 2b: SXGA */ + {S5K6AAFX_REG_W_ADDL, 0x031C}, + {S5K6AAFX_REG_WR, 0x0002}, + {S5K6AAFX_REG_W_ADDL, 0x0320}, + {S5K6AAFX_REG_WR, 0x0535}, + {S5K6AAFX_REG_WR, 0x0000}, + + /* REG_TC_GP_CapConfigChanged */ + {S5K6AAFX_REG_W_ADDL, 0x0226}, + {S5K6AAFX_REG_WR, 0x0001}, /* Enable output after config change */ + + {S5K6AAFX_REG_W_ADDL, 0x01FA}, + /* REG_TC_GP_PrevReqInputWidth */ + {S5K6AAFX_REG_WR, S5K6AAFX_ADJ_FULL_SIZE_WIDTH}, + /* REG_TC_GP_PrevReqInputHeight */ + {S5K6AAFX_REG_WR, S5K6AAFX_ADJ_FULL_SIZE_HEIGHT}, + /* REG_TC_GP_PrevInputWidthOfs */ + {S5K6AAFX_REG_WR, (S5K6AAFX_FULL_SIZE_WIDTH-S5K6AAFX_ADJ_FULL_SIZE_WIDTH)/2}, + /* REG_TC_GP_PrevInputHeightOfs */ + {S5K6AAFX_REG_WR, (S5K6AAFX_FULL_SIZE_HEIGHT-S5K6AAFX_ADJ_FULL_SIZE_HEIGHT)/2}, + {S5K6AAFX_REG_W_ADDL, 0x020A}, + /* REG_TC_GP_PrevZoomReqInputWidth */ + {S5K6AAFX_REG_WR, S5K6AAFX_ADJ_FULL_SIZE_WIDTH}, + /* REG_TC_GP_PrevZoomReqInputHeight */ + {S5K6AAFX_REG_WR, S5K6AAFX_ADJ_FULL_SIZE_HEIGHT}, + /* REG_TC_GP_PrevZoomReqInputWidthOfs */ + {S5K6AAFX_REG_WR, 0x0000}, + /* REG_TC_GP_PrevZoomReqInputHeightOfs */ + {S5K6AAFX_REG_WR, 0x0000}, + + /* REG_TC_GEP_InputsChangeRequest */ + {S5K6AAFX_REG_W_ADDL, 0x021A}, + {S5K6AAFX_REG_WR, 0x0001}, + +}; + +static const struct s5k6aafx_i2c_reg_conf const prev_snap_conf_init_tb2[] = { + /* PREVIEW CONFIGURATION 3 (VGA, YUV) */ + {S5K6AAFX_REG_W_ADDL, 0x02B4}, + {S5K6AAFX_REG_WR, S5K6AAFX_QTR_SIZE_WIDTH}, + {S5K6AAFX_REG_WR, S5K6AAFX_QTR_SIZE_HEIGHT}, + {S5K6AAFX_REG_WR, 0x0005}, /* YUV */ + {S5K6AAFX_REG_W_ADDL, 0x02C0}, + {S5K6AAFX_REG_WR, 0x0001}, /* PLL config */ + {S5K6AAFX_REG_W_ADDL, 0x02BA}, + {S5K6AAFX_REG_WR, 0x1770}, + {S5K6AAFX_REG_WR, 0x1700}, + {S5K6AAFX_REG_WR, 0x0042}, + {S5K6AAFX_REG_W_ADDL, 0x02C4}, + {S5K6AAFX_REG_WR, 0x0001}, /* 1b: Avg S.S 2b: SXGA */ + {S5K6AAFX_REG_W_ADDL, 0x0250}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_W_ADDL, 0x02C6}, + {S5K6AAFX_REG_WR, 0x0535}, + {S5K6AAFX_REG_WR, 0x0168}, + {S5K6AAFX_REG_W_ADDL, 0x02D4},/* flip and mirror */ + {S5K6AAFX_REG_WR, 0x0001},/* 0x0288 value */ + {S5K6AAFX_REG_WR, 0x0001}, + + /* PREVIEW CONFIGURATION 1 (SXGA, YUV) */ + {S5K6AAFX_REG_W_ADDL, 0x0268}, + {S5K6AAFX_REG_WR, S5K6AAFX_FULL_SIZE_WIDTH}, + {S5K6AAFX_REG_WR, S5K6AAFX_FULL_SIZE_HEIGHT}, + {S5K6AAFX_REG_WR, 0x0005}, /* YUV */ + {S5K6AAFX_REG_W_ADDL, 0x0274}, + {S5K6AAFX_REG_WR, 0x0001}, /* PLL config */ + {S5K6AAFX_REG_W_ADDL, 0x026E}, + {S5K6AAFX_REG_WR, 0x2EE0}, + {S5K6AAFX_REG_WR, 0x2EE0}, + {S5K6AAFX_REG_WR, 0x0042}, + {S5K6AAFX_REG_W_ADDL, 0x0278}, + {S5K6AAFX_REG_WR, 0x0002}, /* 1b: Avg S.S 2b: SXGA */ + {S5K6AAFX_REG_W_ADDL, 0x0276}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_W_ADDL, 0x027A}, + {S5K6AAFX_REG_WR, 0x0535}, + {S5K6AAFX_REG_WR, 0x029A}, + {S5K6AAFX_REG_W_ADDL, 0x0288}, /* flip and mirror */ + {S5K6AAFX_REG_WR, 0x0001}, /* 0x0288 value */ + {S5K6AAFX_REG_WR, 0x0001}, /* 0x0288 value */ + + /*********** APPLY PREVIEW CONFIGURATION & RUN PREVIEW ***********/ + /* REG_TC_GP_ActivePrevConfig-Select preview configuration_3 */ + {S5K6AAFX_REG_W_ADDL, 0x021C}, + {S5K6AAFX_REG_WR, 0x0001},/*Preview: 3 : VGA 30fps. 1:Full size 15fps*/ + /* REG_TC_GP_PrevOpenAfterChange */ + {S5K6AAFX_REG_W_ADDL, 0x0220}, + {S5K6AAFX_REG_WR, 0x0001}, + /* REG_TC_GP_NewConfigSync-Update preview configuration */ + {S5K6AAFX_REG_W_ADDL, 0x01F8}, + {S5K6AAFX_REG_WR, 0x0001}, + /* REG_TC_GP_PrevConfigChanged */ + {S5K6AAFX_REG_W_ADDL, 0x021E}, + {S5K6AAFX_REG_WR, 0x0001}, /* Enable output after config change */ + {S5K6AAFX_REG_W_ADDL, 0x01F0}, + {S5K6AAFX_REG_WR, 0x0001}, /* REG_TC_GP_EnablePreview - Start preview */ + {S5K6AAFX_REG_WR, 0x0001}, /* REG_TC_GP_EnablePreviewChanged */ + + + /* CAPTURE CONFIGURATION 0 (SXGA, YUV) */ + {S5K6AAFX_REG_W_ADDL, 0x030E}, + {S5K6AAFX_REG_WR, S5K6AAFX_FULL_SIZE_WIDTH}, /* 1280 */ + {S5K6AAFX_REG_WR, S5K6AAFX_FULL_SIZE_HEIGHT}, /* 1024 */ + {S5K6AAFX_REG_WR, 0x0005}, /* YUV */ + {S5K6AAFX_REG_W_ADDL, 0x031A}, + {S5K6AAFX_REG_WR, 0x0001}, /* PLL config */ + {S5K6AAFX_REG_W_ADDL, 0x0314}, + {S5K6AAFX_REG_WR, 0x2EE0}, + {S5K6AAFX_REG_WR, 0x2EE0}, + {S5K6AAFX_REG_WR, 0x0042}, + {S5K6AAFX_REG_W_ADDL, 0x031E}, + {S5K6AAFX_REG_WR, 0x0002}, /* 1b: Avg S.S 2b: SXGA */ + {S5K6AAFX_REG_W_ADDL, 0x031C}, + {S5K6AAFX_REG_WR, 0x0000}, + {S5K6AAFX_REG_W_ADDL, 0x0320}, + {S5K6AAFX_REG_WR, 0x0535}, + {S5K6AAFX_REG_WR, 0x029A}, + + /* REG_TC_GP_CapConfigChanged */ + {S5K6AAFX_REG_W_ADDL, 0x0226}, + {S5K6AAFX_REG_WR, 0x0001}, /* Enable output after config change */ +}; + + +struct s5k6aafx_reg s5k6aafx_regs = { + .reset_init = &reset_init_tbl[0], + .reset_init_size = ARRAY_SIZE(reset_init_tbl), + .TP_init = &TP_init_tbl[0], + .TP_init_size = ARRAY_SIZE(TP_init_tbl), + .analog_setting_init = &analog_setting_init_tbl[0], + .analog_setting_init_size = ARRAY_SIZE(analog_setting_init_tbl), + .register_init = ®ister_init_tbl[0], + .register_init_size = ARRAY_SIZE(register_init_tbl), + .clk_init = &clk_init_tbl[0], + .clk_init_size = ARRAY_SIZE(clk_init_tbl), + .prev_snap_conf_init = &prev_snap_conf_init_tbl[0], + .prev_snap_conf_init_size = ARRAY_SIZE(prev_snap_conf_init_tbl), + /* for full-size preview */ + .clk_init_tb2 = &clk_init_tb2[0], + .clk_init_tb2_size = ARRAY_SIZE(clk_init_tb2), + .prev_snap_conf_init_tb2 = &prev_snap_conf_init_tb2[0], + .prev_snap_conf_init_tb2_size = ARRAY_SIZE(prev_snap_conf_init_tb2), +}; diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h index c0165405e3c99..db7172eda5f6b 100644 --- a/include/media/msm_camera.h +++ b/include/media/msm_camera.h @@ -1,19 +1,5 @@ -/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * +/* + * Copyright (C) 2008-2009 QUALCOMM Incorporated. */ #ifndef __LINUX_MSM_CAMERA_H @@ -91,6 +77,8 @@ #define MSM_CAMERA_LED_OFF 0 #define MSM_CAMERA_LED_LOW 1 #define MSM_CAMERA_LED_HIGH 2 +#define MSM_CAMERA_LED_LOW_FOR_SNAPSHOT 3 +#define MSM_CAMERA_LED_DEATH_RAY 4 #define MSM_CAM_IOCTL_FLASH_LED_CFG \ _IOW(MSM_CAM_IOCTL_MAGIC, 22, unsigned *) @@ -104,11 +92,6 @@ #define MSM_CAM_IOCTL_ENABLE_OUTPUT_IND \ _IOW(MSM_CAM_IOCTL_MAGIC, 25, uint32_t *) -#define MSM_CAM_IOCTL_AF_CTRL \ - _IOR(MSM_CAM_IOCTL_MAGIC, 26, struct msm_ctrl_cmt_t *) -#define MSM_CAM_IOCTL_AF_CTRL_DONE \ - _IOW(MSM_CAM_IOCTL_MAGIC, 27, struct msm_ctrl_cmt_t *) - #define MAX_SENSOR_NUM 3 #define MAX_SENSOR_NAME 32 @@ -146,6 +129,7 @@ struct msm_vfe_evt_msg { unsigned short msg_id; unsigned int len; /* size in, number of bytes out */ void *data; + unsigned short exttype; }; #define MSM_CAM_RESP_CTRL 0 @@ -178,6 +162,52 @@ struct msm_camera_cfg_cmd { void *value; }; +#ifdef CONFIG_720P_CAMERA +#define CMD_GENERAL 0 +#define CMD_AXI_CFG_SNAP 1 +#define CMD_AXI_CFG_PREVIEW 2 +#define CMD_AXI_CFG_VIDEO 3 +#define CMD_RAW_PICT_AXI_CFG 4 + +#define CMD_FRAME_BUF_RELEASE 5 +#define CMD_PREV_BUF_CFG 6 +#define CMD_SNAP_BUF_RELEASE 7 +#define CMD_SNAP_BUF_CFG 8 +#define CMD_STATS_DISABLE 9 +#define CMD_STATS_AEC_AWB_ENABLE 10 +#define CMD_STATS_AF_ENABLE 11 +#define CMD_STATS_AEC_ENABLE 12 +#define CMD_STATS_AWB_ENABLE 13 +#define CMD_STATS_ENABLE 14 + +#define CMD_STATS_AXI_CFG 15 +#define CMD_STATS_AEC_AXI_CFG 16 +#define CMD_STATS_AF_AXI_CFG 17 +#define CMD_STATS_AWB_AXI_CFG 18 +#define CMD_STATS_RS_AXI_CFG 19 +#define CMD_STATS_CS_AXI_CFG 20 +#define CMD_STATS_IHIST_AXI_CFG 21 +#define CMD_STATS_SKIN_AXI_CFG 22 + +#define CMD_STATS_BUF_RELEASE 23 +#define CMD_STATS_AEC_BUF_RELEASE 24 +#define CMD_STATS_AF_BUF_RELEASE 25 +#define CMD_STATS_AWB_BUF_RELEASE 26 +#define CMD_STATS_RS_BUF_RELEASE 27 +#define CMD_STATS_CS_BUF_RELEASE 28 +#define CMD_STATS_IHIST_BUF_RELEASE 29 +#define CMD_STATS_SKIN_BUF_RELEASE 30 + +#define UPDATE_STATS_INVALID 31 +#else + +//Just for build pass (Horng test) +//------------------------------------ +#define CMD_AXI_CFG_SNAP 1 +#define CMD_AXI_CFG_PREVIEW 2 +#define CMD_AXI_CFG_VIDEO 3 +//------------------------------------ + #define CMD_GENERAL 0 #define CMD_AXI_CFG_OUT1 1 #define CMD_AXI_CFG_SNAP_O1_AND_O2 2 @@ -199,30 +229,7 @@ struct msm_camera_cfg_cmd { #define CMD_STATS_ENABLE 18 #define UPDATE_STATS_INVALID 19 -#define CMD_STATS_AEC_ENABLE 20 -#define CMD_STATS_AWB_ENABLE 21 -#define CMD_STATS_AEC_AXI_CFG 22 -#define CMD_STATS_AWB_AXI_CFG 23 -#define CMD_STATS_RS_AXI_CFG 24 -#define CMD_STATS_CS_AXI_CFG 25 -#define CMD_STATS_IHIST_AXI_CFG 26 -#define CMD_STATS_SKIN_AXI_CFG 27 -#define CMD_STATS_AEC_BUF_RELEASE 28 -#define CMD_STATS_AWB_BUF_RELEASE 29 -#define CMD_STATS_RS_BUF_RELEASE 30 -#define CMD_STATS_CS_BUF_RELEASE 31 -#define CMD_STATS_IHIST_BUF_RELEASE 32 -#define CMD_STATS_SKIN_BUF_RELEASE 33 - -#define CMD_AXI_CFG_SNAP_GEMINI 34 -#define CMD_AXI_CFG_SNAP 35 -#define CMD_AXI_CFG_PREVIEW 36 -#define CMD_AXI_CFG_VIDEO 37 - -#define CMD_STATS_IHIST_ENABLE 38 -#define CMD_STATS_RS_ENABLE 39 -#define CMD_STATS_CS_ENABLE 40 -#define CMD_AXI_CFG_O1_AND_O2 41 /* output1 and output2 */ +#endif /* vfe config command: config command(from config thread)*/ struct msm_vfe_cfg_cmd { @@ -236,6 +243,31 @@ struct camera_enable_cmd { char name[MAX_CAMERA_ENABLE_NAME_LEN]; }; +#ifdef CONFIG_720P_CAMERA + +#define MSM_PMEM_VIDEO 0 +#define MSM_PMEM_PREVIEW 1 +#define MSM_PMEM_THUMBNAIL 2 +#define MSM_PMEM_MAINIMG 3 +#define MSM_PMEM_RAW_MAINIMG 4 +#define MSM_PMEM_AEC_AWB 5 +#define MSM_PMEM_AF 6 +#define MSM_PMEM_AEC 7 +#define MSM_PMEM_AWB 8 +#define MSM_PMEM_RS 9 +#define MSM_PMEM_CS 10 +#define MSM_PMEM_IHIST 11 +#define MSM_PMEM_SKIN 12 +#define MSM_PMEM_MAX 13 + +#else + +//Just for build pass (Horng test) +//------------------------------------ +#define MSM_PMEM_VIDEO 0 +#define MSM_PMEM_PREVIEW 1 +//------------------------------------ + #define MSM_PMEM_OUTPUT1 0 #define MSM_PMEM_OUTPUT2 1 #define MSM_PMEM_OUTPUT1_OUTPUT2 2 @@ -244,20 +276,14 @@ struct camera_enable_cmd { #define MSM_PMEM_RAW_MAINIMG 5 #define MSM_PMEM_AEC_AWB 6 #define MSM_PMEM_AF 7 -#define MSM_PMEM_AEC 8 -#define MSM_PMEM_AWB 9 -#define MSM_PMEM_RS 10 -#define MSM_PMEM_CS 11 -#define MSM_PMEM_IHIST 12 -#define MSM_PMEM_SKIN 13 -#define MSM_PMEM_VIDEO 14 -#define MSM_PMEM_PREVIEW 15 -#define MSM_PMEM_MAX 16 +#define MSM_PMEM_MAX 8 + +#endif #define FRAME_PREVIEW_OUTPUT1 0 #define FRAME_PREVIEW_OUTPUT2 1 #define FRAME_SNAPSHOT 2 -#define FRAME_THUMBNAIL 3 +#define FRAME_THUMBAIL 3 #define FRAME_RAW_SNAPSHOT 4 #define FRAME_MAX 5 @@ -280,24 +306,41 @@ struct outputCfg { uint32_t window_height_lastline; }; +#ifndef CONFIG_720P_CAMERA + #define OUTPUT_1 0 #define OUTPUT_2 1 #define OUTPUT_1_AND_2 2 #define CAMIF_TO_AXI_VIA_OUTPUT_2 3 #define OUTPUT_1_AND_CAMIF_TO_AXI_VIA_OUTPUT_2 4 #define OUTPUT_2_AND_CAMIF_TO_AXI_VIA_OUTPUT_1 5 -#define OUTPUT_1_AND_3 6 -#define LAST_AXI_OUTPUT_MODE_ENUM = OUTPUT_1_AND_3 7 /* video */ +#define LAST_AXI_OUTPUT_MODE_ENUM = OUTPUT_2_AND_CAMIF_TO_AXI_VIA_OUTPUT_1 6 +#define MSM_FRAME_PREV_1 0 +#define MSM_FRAME_PREV_2 1 +#define MSM_FRAME_ENC 2 + +#else + +#define OUTPUT_1 0 +#define OUTPUT_2 1 +#define OUTPUT_1_AND_2 2 +#define OUTPUT_1_AND_3 3 +#define CAMIF_TO_AXI_VIA_OUTPUT_2 4 +#define OUTPUT_1_AND_CAMIF_TO_AXI_VIA_OUTPUT_2 5 +#define OUTPUT_2_AND_CAMIF_TO_AXI_VIA_OUTPUT_1 6 +#define LAST_AXI_OUTPUT_MODE_ENUM = OUTPUT_2_AND_CAMIF_TO_AXI_VIA_OUTPUT_1 7 #define MSM_FRAME_PREV_1 0 #define MSM_FRAME_PREV_2 1 #define MSM_FRAME_ENC 2 -#define OUTPUT_TYPE_P 1 -#define OUTPUT_TYPE_T 2 -#define OUTPUT_TYPE_S 3 -#define OUTPUT_TYPE_V 4 +#define OUTPUT_TYPE_P 1 +#define OUTPUT_TYPE_T 2 +#define OUTPUT_TYPE_S 3 +#define OUTPUT_TYPE_V 4 + +#endif struct msm_frame { int path; @@ -312,13 +355,7 @@ struct msm_frame { #define STAT_AEAW 0 #define STAT_AF 1 -#define STAT_AEC 2 -#define STAT_AWB 3 -#define STAT_RS 4 -#define STAT_CS 5 -#define STAT_IHIST 6 -#define STAT_SKIN 7 -#define STAT_MAX 8 +#define STAT_MAX 2 struct msm_stats_buf { int type; @@ -334,8 +371,11 @@ struct msm_stats_buf { #define MSM_V4L2_GET_CTRL 5 #define MSM_V4L2_SET_CTRL 6 #define MSM_V4L2_QUERY 7 -#define MSM_V4L2_MAX 8 +#define MSM_V4L2_GET_CROP 8 +#define MSM_V4L2_SET_CROP 9 +#define MSM_V4L2_MAX 10 +#define V4L2_CAMERA_EXIT 43 struct crop_info { void *info; int len; @@ -380,7 +420,20 @@ struct msm_snapshot_pp_status { #define CFG_GET_PICT_P_PL 25 #define CFG_GET_AF_MAX_STEPS 26 #define CFG_GET_PICT_MAX_EXP_LC 27 -#define CFG_MAX 28 +#define CFG_I2C_IOCTL_R_OTP 28 +#define CFG_SET_OV_LSC 29 /*vincent for LSC calibration*/ +#define CFG_SET_SHARPNESS 30 +#define CFG_SET_SATURATION 31 +#define CFG_SET_OV_LSC_RAW_CAPTURE 32/*20100330 vincent for LSC calibration*/ +#define CFG_SET_ISO 33 +#define CFG_SET_COORDINATE 34 +#define CFG_RUN_AUTO_FOCUS 35 +#define CFG_CANCEL_AUTO_FOCUS 36 +#define CFG_GET_EXP_FOR_LED 37 +#define CFG_UPDATE_AEC_FOR_LED 38 +#define CFG_SET_FRONT_CAMERA_MODE 39 +#define CFG_SET_QCT_LSC_RAW_CAPTURE 40 /* 20101011 QCT mesh LSC Calibration */ +#define CFG_MAX 41 #define MOVE_NEAR 0 #define MOVE_FAR 1 @@ -388,6 +441,7 @@ struct msm_snapshot_pp_status { #define SENSOR_PREVIEW_MODE 0 #define SENSOR_SNAPSHOT_MODE 1 #define SENSOR_RAW_SNAPSHOT_MODE 2 +#define SENSOR_GET_EXP 3 #define SENSOR_QTR_SIZE 0 #define SENSOR_FULL_SIZE 1 @@ -415,11 +469,16 @@ struct sensor_pict_fps { struct exp_gain_cfg { uint16_t gain; uint32_t line; + uint16_t mul; }; struct focus_cfg { int32_t steps; int dir; + int coarse_delay; + int fine_delay; + int step_dir; + int init_code_offset_max; }; struct fps_cfg { @@ -428,6 +487,79 @@ struct fps_cfg { uint32_t pict_fps_div; }; +/*Becker for AWB calibration*/ +struct fuse_id{ + uint32_t fuse_id_word1; + uint32_t fuse_id_word2; + uint32_t fuse_id_word3; + uint32_t fuse_id_word4; +}; + +/*Vincent for LSC calibration*/ +struct reg_addr_val_pair_struct { + uint16_t reg_addr; + uint8_t reg_val; +}; + +struct lsc_cfg{ + struct reg_addr_val_pair_struct lsc_table[144]; /*OV LSC table*/ +}; + +enum antibanding_mode{ + CAMERA_ANTI_BANDING_50HZ, + CAMERA_ANTI_BANDING_60HZ, + CAMERA_ANTI_BANDING_AUTO, +}; + +enum brightness_t{ + CAMERA_BRIGHTNESS_N3, + CAMERA_BRIGHTNESS_N2, + CAMERA_BRIGHTNESS_N1, + CAMERA_BRIGHTNESS_D, + CAMERA_BRIGHTNESS_P1, + CAMERA_BRIGHTNESS_P2, + CAMERA_BRIGHTNESS_P3, + CAMERA_BRIGHTNESS_P4, + CAMERA_BRIGHTNESS_N4, +}; + +enum frontcam_t{ + CAMERA_MIRROR, + CAMERA_REVERSE, +}; + +enum wb_mode{ + CAMERA_AWB_AUTO,/*auto*/ + CAMERA_AWB_CLOUDY,/*Cloudy*/ + CAMERA_AWB_INDOOR_HOME,/*Fluorescent*/ + CAMERA_AWB_INDOOR_OFFICE,/*Incandescent*/ + CAMERA_AWB_SUNNY,/*daylight*/ +}; + +enum sharpness_mode{ + CAMERA_SHARPNESS_X0, + CAMERA_SHARPNESS_X1, + CAMERA_SHARPNESS_X2, + CAMERA_SHARPNESS_X3, + CAMERA_SHARPNESS_X4, +}; + +enum saturation_mode{ + CAMERA_SATURATION_X0, + CAMERA_SATURATION_X05, + CAMERA_SATURATION_X1, + CAMERA_SATURATION_X15, + CAMERA_SATURATION_X2, +}; + +enum contrast_mode{ + CAMERA_CONTRAST_P2, + CAMERA_CONTRAST_P1, + CAMERA_CONTRAST_D, + CAMERA_CONTRAST_N1, + CAMERA_CONTRAST_N2, +}; + struct sensor_cfg_data { int cfgtype; int mode; @@ -447,6 +579,15 @@ struct sensor_cfg_data { struct exp_gain_cfg exp_gain; struct focus_cfg focus; struct fps_cfg fps; + struct fuse_id fuse; + struct lsc_cfg lsctable;/*Vincent for LSC calibration*/ + enum antibanding_mode antibanding_value; + enum brightness_t brightness_value; + enum frontcam_t frontcam_value; + enum wb_mode wb_value; + enum sharpness_mode sharpness_value; + enum saturation_mode saturation_value; + enum contrast_mode contrast_value; } cfg; }; From 57a87fbe19acd2878d4862b883a2ad781b5785f3 Mon Sep 17 00:00:00 2001 From: Charan Singh Date: Wed, 3 Nov 2010 19:48:21 -0400 Subject: [PATCH 0975/2556] camera: s5k3e2fx: Increase clock speed to 83MHz --- drivers/media/video/msm/s5k3e2fx.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/msm/s5k3e2fx.c b/drivers/media/video/msm/s5k3e2fx.c index 4cffae710dcf4..e548d5855f21c 100644 --- a/drivers/media/video/msm/s5k3e2fx.c +++ b/drivers/media/video/msm/s5k3e2fx.c @@ -20,11 +20,8 @@ static uint16_t g_usModuleVersion; /*0: rev.4, 1: rev.5 */ /* prepare for modify PCLK*/ -#ifdef CONFIG_MACH_PASSIONC -#define REG_PLL_MULTIPLIER_LSB_VALUE 0xA0 -#else -#define REG_PLL_MULTIPLIER_LSB_VALUE 0x90 -#endif +#define REG_PLL_MULTIPLIER_LSB_VALUE 0xA6 +/* 0xA6 for PCLK=83MHz */ /* 0xA0 for PCLK=80MHz */ /* 0x90 for PCLK=72MHz */ From 6e3321c6d85cfb0219667ac224d0d034fa2140a2 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 2 Nov 2010 13:52:32 -0400 Subject: [PATCH 0976/2556] mahimahi: Memory layout changes and enhancements * Replace the pmem_camera device with pmem_venc for 720P * Rearrange memory map to avoid HIGHMEM and reclaim 24MB * Fine-tune the softkey positions (kmobs) * Add perflock platform data * Add msm_flashlight platform data, remove old driver --- arch/arm/mach-msm/Makefile | 1 - arch/arm/mach-msm/board-mahimahi-flashlight.c | 279 ------------------ arch/arm/mach-msm/board-mahimahi-flashlight.h | 20 -- arch/arm/mach-msm/board-mahimahi.c | 79 +++-- arch/arm/mach-msm/board-mahimahi.h | 27 +- 5 files changed, 66 insertions(+), 340 deletions(-) delete mode 100644 arch/arm/mach-msm/board-mahimahi-flashlight.c delete mode 100644 arch/arm/mach-msm/board-mahimahi-flashlight.h diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 7f3e3c43ca241..35afe55921efe 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -68,7 +68,6 @@ obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-wifi.o board-mahimahi-audio.o obj-$(CONFIG_MACH_MAHIMAHI) += msm_vibrator.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-microp.o obj-$(CONFIG_MACH_MAHIMAHI) += htc_acoustic_qsd.o -obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-flashlight.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-panel.o fish_battery.o diff --git a/arch/arm/mach-msm/board-mahimahi-flashlight.c b/arch/arm/mach-msm/board-mahimahi-flashlight.c deleted file mode 100644 index 829b1f11cf218..0000000000000 --- a/arch/arm/mach-msm/board-mahimahi-flashlight.c +++ /dev/null @@ -1,279 +0,0 @@ -/* - * arch/arm/mach-msm/flashlight.c - flashlight driver - * - * Copyright (C) 2009 zion huang - * - * 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; version 2 of the License. - */ - -#define DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "board-mahimahi-flashlight.h" - -struct flashlight_struct { - struct led_classdev fl_lcdev; - struct early_suspend early_suspend_flashlight; - spinlock_t spin_lock; - struct hrtimer timer; - int brightness; - int gpio_torch; - int gpio_flash; - int flash_duration_ms; -}; - -static struct flashlight_struct the_fl; - -static inline void toggle(void) -{ - gpio_direction_output(the_fl.gpio_torch, 0); - udelay(2); - gpio_direction_output(the_fl.gpio_torch, 1); - udelay(2); -} - -static void flashlight_hw_command(uint8_t addr, uint8_t data) -{ - int i; - - for (i = 0; i < addr + 17; i++) - toggle(); - udelay(500); - - for (i = 0; i < data; i++) - toggle(); - udelay(500); -} - -static enum hrtimer_restart flashlight_timeout(struct hrtimer *timer) -{ - unsigned long flags; - - pr_debug("%s\n", __func__); - - spin_lock_irqsave(&the_fl.spin_lock, flags); - gpio_direction_output(the_fl.gpio_flash, 0); - the_fl.brightness = LED_OFF; - spin_unlock_irqrestore(&the_fl.spin_lock, flags); - - return HRTIMER_NORESTART; -} - -int flashlight_control(int mode) -{ - int ret = 0; - unsigned long flags; - - pr_debug("%s: mode %d -> %d\n", __func__, - the_fl.brightness, mode); - - spin_lock_irqsave(&the_fl.spin_lock, flags); - - the_fl.brightness = mode; - - switch (mode) { - case FLASHLIGHT_TORCH: - pr_info("%s: half\n", __func__); - /* If we are transitioning from flash to torch, make sure to - * cancel the flash timeout timer, otherwise when it expires, - * the torch will go off as well. - */ - hrtimer_cancel(&the_fl.timer); - flashlight_hw_command(2, 4); - break; - - case FLASHLIGHT_FLASH: - pr_info("%s: full\n", __func__); - hrtimer_cancel(&the_fl.timer); - gpio_direction_output(the_fl.gpio_flash, 0); - udelay(40); - gpio_direction_output(the_fl.gpio_flash, 1); - hrtimer_start(&the_fl.timer, - ktime_set(the_fl.flash_duration_ms / 1000, - (the_fl.flash_duration_ms % 1000) * - NSEC_PER_MSEC), - HRTIMER_MODE_REL); - /* Flash overrides torch mode, and after the flash period, the - * flash LED will turn off. - */ - mode = LED_OFF; - break; - - case FLASHLIGHT_OFF: - pr_info("%s: off\n", __func__); - gpio_direction_output(the_fl.gpio_flash, 0); - gpio_direction_output(the_fl.gpio_torch, 0); - break; - - default: - pr_err("%s: unknown flash_light flags: %d\n", __func__, mode); - ret = -EINVAL; - goto done; - } - -done: - spin_unlock_irqrestore(&the_fl.spin_lock, flags); - return ret; -} -EXPORT_SYMBOL(flashlight_control); - -static void fl_lcdev_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - int level; - switch (brightness) { - case LED_HALF: - level = FLASHLIGHT_TORCH; - break; - case LED_FULL: - level = FLASHLIGHT_FLASH; - break; - case LED_OFF: - default: - level = FLASHLIGHT_OFF; - }; - - flashlight_control(level); -} - -static void flashlight_early_suspend(struct early_suspend *handler) -{ - flashlight_control(FLASHLIGHT_OFF); -} - -static int flashlight_setup_gpio(struct flashlight_platform_data *fl_pdata) -{ - int ret; - - pr_debug("%s\n", __func__); - - if (fl_pdata->gpio_init) { - ret = fl_pdata->gpio_init(); - if (ret < 0) { - pr_err("%s: gpio init failed: %d\n", __func__, - ret); - return ret; - } - } - - if (fl_pdata->torch) { - ret = gpio_request(fl_pdata->torch, "flashlight_torch"); - if (ret < 0) { - pr_err("%s: gpio_request failed\n", __func__); - return ret; - } - } - - if (fl_pdata->flash) { - ret = gpio_request(fl_pdata->flash, "flashlight_flash"); - if (ret < 0) { - pr_err("%s: gpio_request failed\n", __func__); - gpio_free(fl_pdata->torch); - return ret; - } - } - - the_fl.gpio_torch = fl_pdata->torch; - the_fl.gpio_flash = fl_pdata->flash; - the_fl.flash_duration_ms = fl_pdata->flash_duration_ms; - return 0; -} - -static int flashlight_probe(struct platform_device *pdev) -{ - struct flashlight_platform_data *fl_pdata = pdev->dev.platform_data; - int err = 0; - - pr_debug("%s\n", __func__); - - err = flashlight_setup_gpio(fl_pdata); - if (err < 0) { - pr_err("%s: setup GPIO failed\n", __func__); - goto fail_free_mem; - } - - spin_lock_init(&the_fl.spin_lock); - the_fl.fl_lcdev.name = pdev->name; - the_fl.fl_lcdev.brightness_set = fl_lcdev_brightness_set; - the_fl.fl_lcdev.brightness = LED_OFF; - err = led_classdev_register(&pdev->dev, &the_fl.fl_lcdev); - if (err < 0) { - pr_err("failed on led_classdev_register\n"); - goto fail_free_gpio; - } - - hrtimer_init(&the_fl.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - the_fl.timer.function = flashlight_timeout; - -#ifdef CONFIG_HAS_EARLYSUSPEND - the_fl.early_suspend_flashlight.suspend = flashlight_early_suspend; - the_fl.early_suspend_flashlight.resume = NULL; - register_early_suspend(&the_fl.early_suspend_flashlight); -#endif - - return 0; - -fail_free_gpio: - if (fl_pdata->torch) - gpio_free(fl_pdata->torch); - if (fl_pdata->flash) - gpio_free(fl_pdata->flash); -fail_free_mem: - return err; -} - -static int flashlight_remove(struct platform_device *pdev) -{ - struct flashlight_platform_data *fl_pdata = pdev->dev.platform_data; - - pr_debug("%s\n", __func__); - - hrtimer_cancel(&the_fl.timer); - unregister_early_suspend(&the_fl.early_suspend_flashlight); - flashlight_control(FLASHLIGHT_OFF); - led_classdev_unregister(&the_fl.fl_lcdev); - if (fl_pdata->torch) - gpio_free(fl_pdata->torch); - if (fl_pdata->flash) - gpio_free(fl_pdata->flash); - return 0; -} - -static struct platform_driver flashlight_driver = { - .probe = flashlight_probe, - .remove = flashlight_remove, - .driver = { - .name = FLASHLIGHT_NAME, - .owner = THIS_MODULE, - }, -}; - -static int __init flashlight_init(void) -{ - pr_debug("%s\n", __func__); - return platform_driver_register(&flashlight_driver); -} - -static void __exit flashlight_exit(void) -{ - pr_debug("%s\n", __func__); - platform_driver_unregister(&flashlight_driver); -} - -module_init(flashlight_init); -module_exit(flashlight_exit); - -MODULE_AUTHOR("Zion Huang "); -MODULE_DESCRIPTION("flash light driver"); -MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-mahimahi-flashlight.h b/arch/arm/mach-msm/board-mahimahi-flashlight.h deleted file mode 100644 index 93b4095edaab1..0000000000000 --- a/arch/arm/mach-msm/board-mahimahi-flashlight.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __ASM_ARM_ARCH_FLASHLIGHT_H -#define __ASM_ARM_ARCH_FLASHLIGHT_H - -#define FLASHLIGHT_NAME "flashlight" - -#define FLASHLIGHT_OFF 0 -#define FLASHLIGHT_TORCH 1 -#define FLASHLIGHT_FLASH 2 -#define FLASHLIGHT_NUM 3 - -struct flashlight_platform_data { - int (*gpio_init) (void); - int torch; - int flash; - int flash_duration_ms; -}; - -int flashlight_control(int level); - -#endif /*__ASM_ARM_ARCH_FLASHLIGHT_H*/ diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index a1f32a8c571de..90b3b8367a53d 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -49,11 +49,13 @@ #include #include #include - +#include +#ifdef CONFIG_PERFLOCK +#include +#endif #include "board-mahimahi.h" #include "devices.h" #include "proc_comm.h" -#include "board-mahimahi-flashlight.h" #include "board-mahimahi-tpa2018d1.h" #include "board-mahimahi-smb329.h" @@ -310,12 +312,12 @@ static struct android_pmem_platform_data android_pmem_adsp_pdata = { .cached = 1, }; -static struct android_pmem_platform_data android_pmem_camera_pdata = { - .name = "pmem_camera", - .start = MSM_PMEM_CAMERA_BASE, - .size = MSM_PMEM_CAMERA_SIZE, - .no_allocator = 1, - .cached = 1, +static struct android_pmem_platform_data android_pmem_venc_pdata = { + .name = "pmem_venc", + .start = MSM_PMEM_VENC_BASE, + .size = MSM_PMEM_VENC_SIZE, + .no_allocator = 0, + .cached = 1, }; static struct platform_device android_pmem_mdp_device = { @@ -334,12 +336,12 @@ static struct platform_device android_pmem_adsp_device = { }, }; -static struct platform_device android_pmem_camera_device = { - .name = "android_pmem", - .id = 2, - .dev = { - .platform_data = &android_pmem_camera_pdata, - }, +static struct platform_device android_pmem_venc_device = { + .name = "android_pmem", + .id = 3, + .dev = { + .platform_data = &android_pmem_venc_pdata, + }, }; static struct resource ram_console_resources[] = { @@ -601,6 +603,14 @@ static struct msm_camera_device_platform_data msm_camera_device_data = { .ioext.appsz = MSM_CLK_CTL_SIZE, }; +static struct camera_flash_cfg msm_camera_sensor_flash_cfg = { + .camera_flash = flashlight_control, + .num_flash_levels = FLASHLIGHT_NUM, + .low_temp_limit = 5, + .low_cap_limit = 15, + +}; + static struct msm_camera_sensor_info msm_camera_sensor_s5k3e2fx_data = { .sensor_name = "s5k3e2fx", .sensor_reset = 144, /* CAM1_RST */ @@ -609,8 +619,7 @@ static struct msm_camera_sensor_info msm_camera_sensor_s5k3e2fx_data = { .pdata = &msm_camera_device_data, .resource = msm_camera_resources, .num_resources = ARRAY_SIZE(msm_camera_resources), - .camera_flash = flashlight_control, - .num_flash_levels = FLASHLIGHT_NUM, + .flash_cfg = &msm_camera_sensor_flash_cfg, }; static struct platform_device msm_camera_sensor_s5k3e2fx = { @@ -686,6 +695,7 @@ static struct platform_device mahimahi_flashlight_device = { .platform_data = &mahimahi_flashlight_data, }, }; + static struct timed_gpio timed_gpios[] = { { .name = "vibrator", @@ -819,7 +829,9 @@ static struct platform_device *devices[] __initdata = { &android_usb_device, &android_pmem_mdp_device, &android_pmem_adsp_device, - &android_pmem_camera_device, +#ifdef CONFIG_720P_CAMERA + &android_pmem_venc_device, +#endif &msm_kgsl_device, &msm_device_i2c, &capella_cm3602, @@ -962,16 +974,29 @@ static struct msm_acpu_clock_platform_data mahimahi_cdma_clock_data = { .mpll_khz = 235930 }; +#ifdef CONFIG_PERFLOCK +static unsigned mahimahi_perf_acpu_table[] = { + 245000000, + 576000000, + 998400000, +}; + +static struct perflock_platform_data mahimahi_perflock_data = { + .perf_acpu_table = mahimahi_perf_acpu_table, + .table_size = ARRAY_SIZE(mahimahi_perf_acpu_table), +}; +#endif + static ssize_t mahimahi_virtual_keys_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { if (system_rev > 2 && system_rev != 0xC0) { - /* center: x: back: 55, menu: 172, home: 298, search 412, y: 835 */ + /* center: x: back: 60, menu: 172, home: 298, search 412, y: 840 */ return sprintf(buf, - __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":55:835:90:55" - ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":172:835:125:55" - ":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":298:835:115:55" - ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":412:835:95:55" + __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":55:840:90:60" + ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":172:840:125:60" + ":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":298:840:115:60" + ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":412:840:95:60" "\n"); } else { /* center: x: home: 55, menu: 185, back: 305, search 425, y: 835 */ @@ -1032,6 +1057,10 @@ static void __init mahimahi_init(void) else msm_acpu_clock_init(&mahimahi_clock_data); +#ifdef CONFIG_PERFLOCK + perflock_init(&mahimahi_perflock_data); +#endif + msm_serial_debug_init(MSM_UART1_PHYS, INT_UART1, &msm_device_uart1.dev, 1, MSM_GPIO_TO_INT(139)); @@ -1123,9 +1152,9 @@ static void __init mahimahi_fixup(struct machine_desc *desc, struct tag *tags, { mi->nr_banks = 2; mi->bank[0].start = PHYS_OFFSET; - mi->bank[0].size = (219*1024*1024); - mi->bank[1].start = MSM_HIGHMEM_BASE; - mi->bank[1].size = MSM_HIGHMEM_SIZE; + mi->bank[0].size = MSM_EBI1_BANK0_SIZE; + mi->bank[1].start = MSM_EBI1_BANK1_BASE; + mi->bank[1].size = MSM_EBI1_BANK1_SIZE; } static void __init mahimahi_map_io(void) diff --git a/arch/arm/mach-msm/board-mahimahi.h b/arch/arm/mach-msm/board-mahimahi.h index 9696a47c4006a..3d8a364d8492a 100644 --- a/arch/arm/mach-msm/board-mahimahi.h +++ b/arch/arm/mach-msm/board-mahimahi.h @@ -21,33 +21,30 @@ #define MSM_SMI_BASE 0x02B00000 #define MSM_SMI_SIZE 0x01500000 +#define MSM_PMEM_VENC_BASE 0x02B00000 +#define MSM_PMEM_VENC_SIZE 0x00800000 + +#define MSM_GPU_MEM_BASE 0x03300000 +#define MSM_GPU_MEM_SIZE 0x00500000 + #define MSM_RAM_CONSOLE_BASE 0x03A00000 #define MSM_RAM_CONSOLE_SIZE 0x00040000 #define MSM_FB_BASE 0x03B00000 -#define MSM_FB_SIZE 0x00465000 - -#define MSM_EBI1_BANK0_BASE 0x20000000 -#define MSM_EBI1_BANK0_SIZE 0x0E000000 +#define MSM_FB_SIZE 0x00300000 -#define MSM_GPU_MEM_BASE 0x2DB00000 -#define MSM_GPU_MEM_SIZE 0x00500000 +#define MSM_EBI1_BANK0_BASE 0x20000000 +#define MSM_EBI1_BANK0_SIZE 0x0E800000 #define MSM_EBI1_BANK1_BASE 0x30000000 -#define MSM_EBI1_BANK1_SIZE 0x10000000 +#define MSM_EBI1_BANK1_SIZE 0x0B700000 -#define MSM_PMEM_MDP_BASE 0x30000000 +#define MSM_PMEM_MDP_BASE 0x3B700000 #define MSM_PMEM_MDP_SIZE 0x02000000 -#define MSM_PMEM_ADSP_BASE 0x32000000 +#define MSM_PMEM_ADSP_BASE 0x3D700000 #define MSM_PMEM_ADSP_SIZE 0x02900000 -#define MSM_PMEM_CAMERA_BASE 0x34900000 -#define MSM_PMEM_CAMERA_SIZE 0x00800000 - -#define MSM_HIGHMEM_BASE 0x35100000 -#define MSM_HIGHMEM_SIZE 0x0AF00000 - #define MAHIMAHI_GPIO_PS_HOLD 25 #define MAHIMAHI_GPIO_UP_INT_N 35 From 6b7ca0d01dbd8be25d8de0963f356caeff35c951 Mon Sep 17 00:00:00 2001 From: Christopher Lais Date: Thu, 18 Nov 2010 13:36:17 -0600 Subject: [PATCH 0977/2556] ganeth: better gannet device Change-Id: Id657f0fbf9fbddb55c2fb404cb95f9d31fb995ef --- drivers/net/Kconfig | 35 +++-- drivers/net/Makefile | 1 + drivers/net/gan-eth.c | 321 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 344 insertions(+), 13 deletions(-) create mode 100644 drivers/net/gan-eth.c diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 6c5a9ee224311..deb945784da62 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1045,12 +1045,12 @@ config SMC911X help This is a driver for SMSC's LAN911x series of Ethernet chipsets including the new LAN9115, LAN9116, LAN9117, and LAN9118. - Say Y if you want it compiled into the kernel, + Say Y if you want it compiled into the kernel, and read the Ethernet-HOWTO, available from . - This driver is also available as a module. The module will be - called smc911x. If you want to compile it as a module, say M + This driver is also available as a module. The module will be + called smc911x. If you want to compile it as a module, say M here and read config SMSC911X @@ -1525,9 +1525,9 @@ config E100 select MII ---help--- This driver supports Intel(R) PRO/100 family of adapters. - To verify that your adapter is supported, find the board ID number - on the adapter. Look for a label that has a barcode and a number - in the format 123456-001 (six digits hyphen three digits). + To verify that your adapter is supported, find the board ID number + on the adapter. Look for a label that has a barcode and a number + in the format 123456-001 (six digits hyphen three digits). Use the above information and the Adapter & Driver ID Guide at: @@ -1539,7 +1539,7 @@ config E100 - More specific information on configuring the driver is in + More specific information on configuring the driver is in . To compile this driver as a module, choose M here. The module @@ -2080,7 +2080,7 @@ config E1000 depends on PCI ---help--- This driver supports Intel(R) PRO/1000 gigabit ethernet family of - adapters. For more information on how to identify your adapter, go + adapters. For more information on how to identify your adapter, go to the Adapter & Driver ID Guide at: @@ -2090,7 +2090,7 @@ config E1000 - More specific information on configuring the driver is in + More specific information on configuring the driver is in . To compile this driver as a module, choose M here. The module @@ -2280,7 +2280,7 @@ config SKGE and related Gigabit Ethernet adapters. It is a new smaller driver with better performance and more complete ethtool support. - It does not support the link failover and network management + It does not support the link failover and network management features that "portable" vendor supplied sk98lin driver does. This driver supports adapters based on the original Yukon chipset: @@ -2759,7 +2759,7 @@ config IXGB - More specific information on configuring the driver is in + More specific information on configuring the driver is in . To compile this driver as a module, choose M here. The module @@ -3263,9 +3263,9 @@ config PPPOE Support for PPP over Ethernet. This driver requires the latest version of pppd from the CVS - repository at cvs.samba.org. Alternatively, see the + repository at cvs.samba.org. Alternatively, see the RoaringPenguin package () - which contains instruction on how to use this driver (under + which contains instruction on how to use this driver (under the heading "Kernel mode PPPoE"). config PPTP @@ -3454,4 +3454,13 @@ config VMXNET3 To compile this driver as a module, choose M here: the module will be called vmxnet3. +config GAN_ETH + tristate "Kineto GAN compatibility" + depends on INET + default n + help + A virutual ethernet device that adds/removes IP/UDP headers, + as required by Kineto GAN. + + endif # NETDEVICES diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 97f6a28eb8dd3..e2a98b16556a1 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -107,6 +107,7 @@ obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o obj-$(CONFIG_RIONET) += rionet.o obj-$(CONFIG_SH_ETH) += sh_eth.o obj-$(CONFIG_STMMAC_ETH) += stmmac/ +obj-$(CONFIG_GAN_ETH) += gan-eth.o # # end link order section diff --git a/drivers/net/gan-eth.c b/drivers/net/gan-eth.c new file mode 100644 index 0000000000000..84ad68b007c5c --- /dev/null +++ b/drivers/net/gan-eth.c @@ -0,0 +1,321 @@ +/* + * gan-eth.c: "gannet" compatibility for Kineto GAN + * + * Packets received on UDP PORT_RX: + * strip headers, add ETH header and reintroduce + * Packets sent via interface: + * strip ETH header, send to UDP localhost:PORT_TX + * + * And yes, this is a very hackish way to add/strip IP/UDP headers. + * Blame Kineto. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#define MODULE_NAME "ganeth" + +#define PORT_TX 13001 +#define PORT_RX 13010 + +#define IP_DEST INADDR_LOOPBACK + +struct ganeth_priv { + struct socket *tx, *rx; + struct sockaddr_in tx_saddr, rx_saddr; + + struct workqueue_struct *workqueue; + struct work_struct tx_work; + struct sk_buff_head queue; +}; + +static struct net_device *netdev; + +static int ganeth_open(struct net_device *dev) +{ + netif_start_queue(dev); + return 0; +} + +static int ganeth_stop(struct net_device *dev) +{ + netif_stop_queue(dev); + return 0; +} + +static int ganeth_sendmsg(struct net_device *dev, void *buf, int len) { + struct ganeth_priv *priv = netdev_priv(dev); + struct msghdr msg = { + .msg_name = &priv->tx_saddr, + .msg_namelen = sizeof(priv->tx_saddr), + .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT, + }; + struct kvec iov = { + .iov_base = buf, + .iov_len = len, + }; + int err; + + err = kernel_sendmsg(priv->tx, &msg, &iov, 1, len); + if (err >= 0) { + dev->stats.tx_packets++; + dev->stats.tx_bytes += len; + } else { + dev->stats.tx_errors++; + } + + return err; +} + +static void ganeth_tx_work(struct work_struct *work) +{ + struct ganeth_priv *priv = netdev_priv(netdev); + struct sk_buff *skb; + + while ((skb = skb_dequeue(&priv->queue))) { + ganeth_sendmsg(netdev, skb->data + ETH_HLEN, skb->len - ETH_HLEN); + dev_kfree_skb(skb); + } +} + +static netdev_tx_t ganeth_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ganeth_priv *priv = netdev_priv(dev); + + if (skb->protocol != htons(ETH_P_IP)) { + if (skb->protocol == htons(ETH_P_IPV6)) { + /* Silently drop IPV6 */ + goto out_drop; + } + + pr_warning("%s: dropping packet with protocol %d\n", dev->name, ntohs(skb->protocol)); + goto out_tx_err; + } + + if (skb->len < ETH_HLEN) { + pr_err("%s: short packet\n", dev->name); + goto out_tx_err; + } + + skb_queue_tail(&priv->queue, skb); + queue_work(priv->workqueue, &priv->tx_work); + return NETDEV_TX_OK; + +out_tx_err: + dev->stats.tx_errors++; +out_drop: + dev_kfree_skb(skb); + return NETDEV_TX_OK; +} + +static void ganeth_tx_timeout(struct net_device *dev) +{ + netif_wake_queue(dev); +} + +static void ganeth_rx_data_ready(struct sock *sk, int len) +{ + struct sk_buff *skb, *rxskb; + struct ethhdr *eth; + void *buf; + unsigned int ulen; + int err; + + skb = skb_recv_datagram(sk, 0, 1, &err); + + if (!skb) + goto out_err; + + ulen = skb->len - sizeof(struct udphdr); + + if (!skb_csum_unnecessary(skb)) { + if (udp_lib_checksum_complete(skb)) { + pr_err("%s: checksum error\n", netdev->name); + goto out_err; + } + } + + dst_confirm(skb_dst(skb)); + + rxskb = dev_alloc_skb(ulen + ETH_HLEN + NET_IP_ALIGN); + if (!rxskb) { + pr_err("%s: failed to allocate skb\n", netdev->name); + goto out_err; + } + + skb_reserve(rxskb, NET_IP_ALIGN); + + /* Ethernet header */ + eth = (struct ethhdr *)skb_put(rxskb, ETH_HLEN); + memset(eth->h_dest, 0, ETH_ALEN); + memcpy(eth->h_source, netdev->dev_addr, ETH_ALEN); + eth->h_proto = htons(ETH_P_IP); + + /* data */ + buf = skb_put(rxskb, ulen); + memcpy(buf, skb->data + sizeof(struct udphdr), ulen); + + rxskb->dev = netdev; + + skb_reset_mac_header(rxskb); + rxskb->protocol = eth->h_proto; + skb_pull(rxskb, ETH_HLEN); /* Eat ethernet header */ + + rxskb->ip_summed = CHECKSUM_NONE; + rxskb->pkt_type = PACKET_HOST; + + if (netif_rx(rxskb) == NET_RX_DROP) + goto out_err; + + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += rxskb->len; + skb_free_datagram(sk, skb); + return; + +out_err: + netdev->stats.rx_errors++; + netdev->stats.rx_dropped++; + skb_free_datagram(sk, skb); + return; + +} + +static const struct net_device_ops ganeth_netdev_ops = { + .ndo_open = ganeth_open, + .ndo_stop = ganeth_stop, + .ndo_start_xmit = ganeth_start_xmit, + .ndo_tx_timeout = ganeth_tx_timeout, +}; + +static void __init ganeth_setup(struct net_device *dev) +{ + ether_setup(dev); + + dev->netdev_ops = &ganeth_netdev_ops; + + dev->flags |= IFF_NOARP; + dev->features |= NETIF_F_NETNS_LOCAL; + + dev->mtu = 1320; + + dev->watchdog_timeo = msecs_to_jiffies(2000); + + random_ether_addr(dev->dev_addr); +} + +static int __init ganeth_init(void) +{ + struct net_device *dev; + struct ganeth_priv *priv; + int err; + + netdev = dev = alloc_netdev(sizeof(*priv), "gannet%d", ganeth_setup); + + if (!dev) + return -ENOMEM; + + err = register_netdev(dev); + if (err) { + pr_err("%s: error registering netdev\n", __func__); + goto out_free; + } + + priv = netdev_priv(dev); + + priv->workqueue = create_workqueue("gannet"); + INIT_WORK(&priv->tx_work, ganeth_tx_work); + skb_queue_head_init(&priv->queue); + + /* tx */ + err = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &priv->tx); + if (err < 0) { + pr_err("%s: error creating tx socket\n", dev->name); + goto out_unregister; + } + + memset(&priv->tx_saddr, 0, sizeof(priv->tx_saddr)); + priv->tx_saddr.sin_family = AF_INET; + priv->tx_saddr.sin_addr.s_addr = htonl(IP_DEST); + priv->tx_saddr.sin_port = htons(PORT_TX); + + /* rx */ + err = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &priv->rx); + if (err < 0) { + pr_err("%s: error creating rx socket\n", dev->name); + goto out_release_tx; + } + priv->rx->sk->sk_data_ready = ganeth_rx_data_ready; + + memset(&priv->rx_saddr, 0, sizeof(priv->rx_saddr)); + priv->rx_saddr.sin_family = AF_INET; + priv->rx_saddr.sin_addr.s_addr = htonl(INADDR_ANY); + priv->rx_saddr.sin_port = htons(PORT_RX); + + err = priv->rx->ops->bind( + priv->rx, + (struct sockaddr *)&priv->rx_saddr, + sizeof(priv->rx_saddr) + ); + if (err < 0) { + pr_err("%s: error binding rx socket\n", dev->name); + goto out_release_rx; + } + + return 0; + +out_release_rx: + sock_release(priv->rx); +out_release_tx: + sock_release(priv->tx); +out_unregister: + unregister_netdev(dev); +out_free: + free_netdev(dev); + return err; +} + +static void __exit ganeth_exit(void) +{ + struct ganeth_priv *priv; + + if (!netdev) + return; + + priv = netdev_priv(netdev); + sock_release(priv->rx); + sock_release(priv->tx); + unregister_netdev(netdev); + destroy_workqueue(priv->workqueue); + free_netdev(netdev); + + netdev = NULL; +} + +module_init(ganeth_init); +module_exit(ganeth_exit); + +MODULE_AUTHOR("Christopher Lais "); +MODULE_DESCRIPTION("Virtual IP over UDP Ethernet Device"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("gannet"); From 227f5b2538219a4516ee22122f20f24b64856cd6 Mon Sep 17 00:00:00 2001 From: Prashant SOmashekar Date: Tue, 24 Aug 2010 22:22:16 -0400 Subject: [PATCH 0978/2556] charge battery to 100%; merged from od and increased 95 to 99 for percentage - commit: ffb0ef63ab15c0a03df6ab25dd7325d06f199e79 (git://github.com/xdabravoteam/cm-kernel.git) --- drivers/power/ds2784_battery.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/drivers/power/ds2784_battery.c b/drivers/power/ds2784_battery.c index f7145746d3755..ae4f68c36f20d 100755 --- a/drivers/power/ds2784_battery.c +++ b/drivers/power/ds2784_battery.c @@ -445,11 +445,31 @@ static int battery_adjust_charge_state(struct ds2784_device_info *di) /* shut off charger when full: * - CHGTF flag is set */ - if (di->status.status_reg & 0x80) { + /* We don't move from full to not-full until + * we drop below 95%, to avoid confusing the + * user while we're maintaining a full charge + * (slowly draining to 95 and charging back + * to 100) + * Oddly, only Passion is for 99% cycles, HTC + * set the Bravo to 95%. + * + * Set 99 for Passion - pershoot + */ + + if (di->status.percentage < 99) { + di->status.battery_full = 0; + } + + /* Modified check function, based on original HTC Source. + * Added current_uA check (relates to 1400mAh capacity check) + */ + + if ((di->status.status_reg & 0x80) && + ((di->status.current_uA/1000) <= 80) && + (di->status.percentage == 100)) { di->status.battery_full = 1; charge_mode = CHARGE_BATT_DISABLE; - } else - di->status.battery_full = 0; + } if (temp >= TEMP_HOT) { if (temp >= TEMP_CRITICAL) @@ -473,7 +493,8 @@ static int battery_adjust_charge_state(struct ds2784_device_info *di) charge_mode = CHARGE_BATT_DISABLE; } - if (di->status.current_uA > 1024) +// if (di->status.current_uA > 1024) + if (di->status.battery_full == 1) di->last_charge_seen = di->last_poll; else if (di->last_charge_mode != CHARGE_OFF && check_timeout(di->last_poll, di->last_charge_seen, 60 * 60)) { From 35ff50806071142346dd070c2ec2a043487b9087 Mon Sep 17 00:00:00 2001 From: Prashant SOmashekar Date: Fri, 27 Aug 2010 07:28:16 -0400 Subject: [PATCH 0979/2556] ds2784: set current_avg_ua <=40 for 2nd block check --- drivers/power/ds2784_battery.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/power/ds2784_battery.c b/drivers/power/ds2784_battery.c index ae4f68c36f20d..cd1af79fe9816 100755 --- a/drivers/power/ds2784_battery.c +++ b/drivers/power/ds2784_battery.c @@ -452,6 +452,7 @@ static int battery_adjust_charge_state(struct ds2784_device_info *di) * to 100) * Oddly, only Passion is for 99% cycles, HTC * set the Bravo to 95%. + * -od of xbravoteam * * Set 99 for Passion - pershoot */ @@ -462,10 +463,14 @@ static int battery_adjust_charge_state(struct ds2784_device_info *di) /* Modified check function, based on original HTC Source. * Added current_uA check (relates to 1400mAh capacity check) + * -od of xbravoteam + * + * replace current_uA <= 80 with current_avg_uA <= 40 + * - pershoot,dvgrhl,rogerpodacter */ if ((di->status.status_reg & 0x80) && - ((di->status.current_uA/1000) <= 80) && + ((di->status.current_avg_uA/1000) <= 40) && (di->status.percentage == 100)) { di->status.battery_full = 1; charge_mode = CHARGE_BATT_DISABLE; @@ -493,7 +498,6 @@ static int battery_adjust_charge_state(struct ds2784_device_info *di) charge_mode = CHARGE_BATT_DISABLE; } -// if (di->status.current_uA > 1024) if (di->status.battery_full == 1) di->last_charge_seen = di->last_poll; else if (di->last_charge_mode != CHARGE_OFF && From 60b133103a39d4cdc15a1b9812c1b9dc4728b37d Mon Sep 17 00:00:00 2001 From: Roger Podacter Date: Fri, 19 Nov 2010 11:32:14 -0500 Subject: [PATCH 0980/2556] power: Enhanced ds2784 battery driver Change-Id: Iea4191e5654e1bf5254576e814b0f3c127f40cc1 --- drivers/power/ds2784_battery.c | 409 ++++++++++++++++++++++++++++++++- 1 file changed, 399 insertions(+), 10 deletions(-) mode change 100755 => 100644 drivers/power/ds2784_battery.c diff --git a/drivers/power/ds2784_battery.c b/drivers/power/ds2784_battery.c old mode 100755 new mode 100644 index cd1af79fe9816..b1ef247328442 --- a/drivers/power/ds2784_battery.c +++ b/drivers/power/ds2784_battery.c @@ -403,6 +403,350 @@ static int battery_get_property(struct power_supply *psy, return 0; } +/* Here we begin to add the functions for our device attribute + * files which act as our link from kernel space to user space. + * + * --RogerPodacter, theloginwithnoname + */ + +static ssize_t store_set_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct ds2784_device_info *di = dev_get_drvdata(dev); + int reg; + int val; + int check; + + sscanf(buf, "%x %x", ®, &val); + + if (reg < 0 || reg > 255) + return -EINVAL; + if (val < 0 || val > 255) + return -EINVAL; + + di->raw[reg] = val; + check = w1_ds2784_write(di->w1_slave, di->raw + reg, reg, 1); + + if (check != 1) { + dev_warn(di->dev, "w1_ds2784_write register failed (ox%p)\n", di->w1_slave); + } + + pr_info("batt: register 0x%02x changed to 0x%02x by user\n", reg, val); + + return count; +} +/*This is the actual device attr macro call-out for "setreg" file*/ +static DEVICE_ATTR(setreg, 0644, NULL, store_set_reg); + +static ssize_t show_dump_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ds2784_device_info *di = dev_get_drvdata(dev); + + int addr; + int ret = 0; + int val; + int printbytes = 0; + + for (addr = 0; addr <= 0xb1; addr++, printbytes++) { + if (addr == 0 || addr == 0x30 || addr == 0x80) { + if (addr == 0x30) { + addr = 0x60; + } + if (addr == 0x80) { + addr = 0xb0; + } + + if (PAGE_SIZE-ret > 2) { + ret+= snprintf(&buf[ret], PAGE_SIZE-ret, "\n%02x:", addr); + } + + w1_ds2784_read(di->w1_slave, di->raw + addr, addr, 1); + val = di->raw[addr]; + printbytes = 0; + } + + if (printbytes >= 16) { + if (PAGE_SIZE-ret > 2) { + ret+= snprintf(&buf[ret], PAGE_SIZE-ret, "\n%02x:", addr); + } + printbytes = 0; + } + + if (PAGE_SIZE-ret > 2) { + val = di->raw[addr]; + ret+= snprintf(&buf[ret], PAGE_SIZE-ret, " %02x", val); + } + else + { + break; + } + } + if (PAGE_SIZE-ret > 2) { + ret+= snprintf(&buf[ret], PAGE_SIZE-ret, "\n"); + } + + return ret; +} + +static DEVICE_ATTR(dumpreg, 0644, show_dump_reg, NULL); + +static ssize_t show_status_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ds2784_device_info *di = dev_get_drvdata(dev); + int ret; + int statusreg; + int check; + + check = w1_ds2784_read(di->w1_slave, di->raw + DS2784_REG_STS, DS2784_REG_STS, 1); + + if (check != 1) { + dev_warn(di->dev, "w1_ds2784_read Status Register failed (ox%p)\n", di->w1_slave); + } + + statusreg = di->raw[DS2784_REG_STS]; + + ret = sprintf(buf, "0x%02x\n", statusreg); + return ret; +} + +static ssize_t store_status_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct ds2784_device_info *di = dev_get_drvdata(dev); + int val; + int check; + + sscanf(buf, "%x", &val); + + if (val < 0 || val > 255) + return -EINVAL; + + di->raw[DS2784_REG_STS] = val; + check = w1_ds2784_write(di->w1_slave, di->raw + DS2784_REG_STS, DS2784_REG_STS, 1); + + if (check != 1) { + dev_warn(di->dev, "w1_ds2784_write Status Register failed (ox%p)\n", di->w1_slave); + } + + pr_info("batt: Status Register set to: 0x%02x \n", val); + + return count; +} + +static DEVICE_ATTR(statusreg, 0644, show_status_reg, store_status_reg); + +static ssize_t show_getvoltage(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ds2784_device_info *di = dev_get_drvdata(dev); + int ret; + int getvoltage; + int check; + + check = w1_ds2784_read(di->w1_slave, di->raw + DS2784_REG_VOLT_MSB, DS2784_REG_VOLT_MSB, 2); + + getvoltage = (((di->raw[DS2784_REG_VOLT_MSB]<<8) | (di->raw[DS2784_REG_VOLT_LSB]))>>5)*4886; + + if (check != 2) { + dev_warn(di->dev, "w1_ds2784_read Voltage failed (ox%p)\n", di->w1_slave); + } + + ret = sprintf(buf, "%d\n", getvoltage); + return ret; +} + +static DEVICE_ATTR(getvoltage, 0644, show_getvoltage, NULL); + +static ssize_t show_getcurrent(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ds2784_device_info *di = dev_get_drvdata(dev); + short n; + int ret; + int getcurrent; + int check; + + check = w1_ds2784_read(di->w1_slave, di->raw + DS2784_REG_CURR_MSB, DS2784_REG_CURR_MSB, 2); + + if (check != 2) { + dev_warn(di->dev, "w1_ds2784_read Current failed (ox%p)\n", di->w1_slave); + } + + n = ((di->raw[DS2784_REG_CURR_MSB]) << 8) | di->raw[DS2784_REG_CURR_LSB]; + getcurrent = ((n * 15625) / 10000) * 67; + + ret = sprintf(buf, "%d\n", getcurrent); + return ret; +} + +static DEVICE_ATTR(getcurrent, 0644, show_getcurrent, NULL); + +static ssize_t show_getavgcurrent(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ds2784_device_info *di = dev_get_drvdata(dev); + short n; + int ret; + int getavgcurrent; + int check; + + check = w1_ds2784_read(di->w1_slave, di->raw + DS2784_REG_AVG_CURR_MSB, DS2784_REG_AVG_CURR_MSB, 2); + + if (check != 2) { + dev_warn(di->dev, "w1_ds2784_read Avg Current failed (ox%p)\n", di->w1_slave); + } + + n = ((di->raw[DS2784_REG_AVG_CURR_MSB]) << 8) | di->raw[DS2784_REG_AVG_CURR_LSB]; + getavgcurrent = ((n * 15625) / 10000) * 67; + + ret = sprintf(buf, "%d\n", getavgcurrent); + return ret; +} +static DEVICE_ATTR(getavgcurrent, 0644, show_getavgcurrent, NULL); + +static ssize_t show_set_age(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ds2784_device_info *di = dev_get_drvdata(dev); + int ret; + int check; + int age; + int ageraw; + + check = w1_ds2784_read(di->w1_slave, di->raw + DS2784_REG_AGE_SCALAR, DS2784_REG_AGE_SCALAR, 1); + + if (check != 1) { + dev_warn(di->dev, "w1_ds2784_read age_scalar failed (ox%p)\n", di->w1_slave); + } + + ageraw = di->raw[DS2784_REG_AGE_SCALAR]; + age = (ageraw * 100) / 128; + + pr_info("batt: age_scalar life left is: %d\n", age); + + ret = sprintf(buf, "Battery's age: %d", age); + return ret; + +} + +static ssize_t store_set_age(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct ds2784_device_info *di = dev_get_drvdata(dev); + int age; + int check; + + sscanf(buf, "%d", &age); + + di->raw[DS2784_REG_AGE_SCALAR] = ((age * 128) / 100); + + check = w1_ds2784_write(di->w1_slave, di->raw + DS2784_REG_AGE_SCALAR, DS2784_REG_AGE_SCALAR, 1); + + if (check != 1) { + dev_warn(di->dev, "w1_ds2784_write age_scalar failed (ox%p)\n", di->w1_slave); + } + + pr_info("batt: age_scalar set to: %d percent\n", age); + + return count; +} + +static DEVICE_ATTR(setage, 0644, show_set_age, store_set_age); + +static ssize_t show_set_AEvolt(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ds2784_device_info *di = dev_get_drvdata(dev); + int ret; + int check; + int rawaevolt; + int aevolt; + + check = w1_ds2784_read(di->w1_slave, di->raw + DS2784_REG_ACTIVE_EMPTY_VOLT, DS2784_REG_ACTIVE_EMPTY_VOLT, 1); + + if (check != 1) { + dev_warn(di->dev, "w1_ds2784_read Active Empty Voltage failed (ox%p)\n", di->w1_slave); + } + rawaevolt = di->raw[DS2784_REG_ACTIVE_EMPTY_VOLT]; + aevolt = (rawaevolt * 1952) / 100; + + pr_info("batt: Active Empty Voltage is: %d volts\n", aevolt); + + ret = sprintf(buf, "AEvolt: %d\n", aevolt); + + return ret; +} + +static ssize_t store_set_AEvolt (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct ds2784_device_info *di = dev_get_drvdata(dev); + int val; + int check; + int temp; + + sscanf(buf, "%d", &val); + + di->raw[DS2784_REG_ACTIVE_EMPTY_VOLT] = ( ( val * 100 ) / 1952 ) ; + + check = w1_ds2784_write(di->w1_slave, di->raw + DS2784_REG_ACTIVE_EMPTY_VOLT, DS2784_REG_ACTIVE_EMPTY_VOLT, 1); + + if (check != 1) { + dev_warn(di->dev, "w1_ds2784_write Active Empty Voltage failed (ox%p)\n", di->w1_slave); + } + + temp = ( ( val * 100 ) / 1952 ) ; + + pr_info("batt: Active Empty Voltage set to: %d percent\n", temp); + + return count; +} + +static DEVICE_ATTR(setAEvolt, 0644, show_set_AEvolt, store_set_AEvolt); + +static ssize_t show_get_full40(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ds2784_device_info *di = dev_get_drvdata(dev); + int ret; + int check; + int full40mAh, full40raw; + + check = w1_ds2784_read(di->w1_slave, di->raw + DS2784_REG_FULL_40_MSB, DS2784_REG_FULL_40_MSB, 2); + + if (check != 2) { + dev_warn(di->dev, "w1_ds2784_read Full40 mAh failed (ox%p)\n", di->w1_slave); + } + + full40raw = ((di->raw[DS2784_REG_FULL_40_MSB]) << 8) | di->raw[DS2784_REG_FULL_40_LSB]; + + full40mAh = ((full40raw * 625) / 100) / 15; + + pr_info("batt: Full40 mAh capacity is: %d mAh\n", full40mAh); + + ret = sprintf(buf, "Full40: %d mAh\n", full40mAh); + + return ret; +} + +static DEVICE_ATTR(getFull40, 0644, show_get_full40, NULL); + +static ssize_t show_get_mAh(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ds2784_device_info *di = dev_get_drvdata(dev); + int ret; + int check; + int mAh; + + check = w1_ds2784_read(di->w1_slave, di->raw + DS2784_REG_RAAC_MSB, DS2784_REG_RAAC_MSB, 2); + + if (check != 2) { + dev_warn(di->dev, "w1_ds2784_read mAh failed (ox%p)\n", di->w1_slave); + } + + mAh = ((di->raw[DS2784_REG_RAAC_MSB] << 8) | di->raw[DS2784_REG_RAAC_LSB]) * 1600; + + ret = sprintf(buf, "%d\n", mAh); + + return ret; +} + +static DEVICE_ATTR(getmAh, 0644, show_get_mAh, NULL); + +/*End of file functions edits + *--RP + */ + static void ds2784_battery_update_status(struct ds2784_device_info *di) { u8 last_level; @@ -449,6 +793,7 @@ static int battery_adjust_charge_state(struct ds2784_device_info *di) * we drop below 95%, to avoid confusing the * user while we're maintaining a full charge * (slowly draining to 95 and charging back + * to 100) * Oddly, only Passion is for 99% cycles, HTC * set the Bravo to 95%. @@ -461,20 +806,18 @@ static int battery_adjust_charge_state(struct ds2784_device_info *di) di->status.battery_full = 0; } - /* Modified check function, based on original HTC Source. - * Added current_uA check (relates to 1400mAh capacity check) - * -od of xbravoteam - * - * replace current_uA <= 80 with current_avg_uA <= 40 - * - pershoot,dvgrhl,rogerpodacter + /* Changed this code if-statement back to stock because this + * parameter is now user settable by changing the actual + * register value inside the battery chip EEPROM. + * --RP */ - if ((di->status.status_reg & 0x80) && - ((di->status.current_avg_uA/1000) <= 40) && - (di->status.percentage == 100)) { + if (di->status.status_reg & 0x80) { di->status.battery_full = 1; charge_mode = CHARGE_BATT_DISABLE; } + else + di->status.battery_full = 0; if (temp >= TEMP_HOT) { if (temp >= TEMP_CRITICAL) @@ -653,7 +996,7 @@ static void battery_ext_power_changed(struct power_supply *psy) static int ds2784_battery_probe(struct platform_device *pdev) { - int rc; + int rc, ret; /*Added "ret" as a check for creating the sysfs files*/ struct ds2784_device_info *di; struct ds2784_platform_data *pdata; @@ -687,6 +1030,52 @@ static int ds2784_battery_probe(struct platform_device *pdev) if (rc) goto fail_register; + /* Here we call out all of the new file creations in the + * probe function. + * --RP + */ + ret = device_create_file(&pdev->dev, &dev_attr_setreg); + if(ret < 0) + pr_err("%s: Failed to create sysfs entry for setreg\n", __func__); + + ret = device_create_file(&pdev->dev, &dev_attr_dumpreg); + if(ret < 0) + pr_err("%s: Failed to create sysfs entry for dumpreg\n", __func__); + + ret = device_create_file(&pdev->dev, &dev_attr_statusreg); + if(ret < 0) + pr_err("%s: Failed to create sysfs entry for statusreg\n", __func__); + + ret = device_create_file(&pdev->dev, &dev_attr_getvoltage); + if(ret < 0) + pr_err("%s: Failed to create sysfs entry for voltage\n", __func__); + + ret = device_create_file(&pdev->dev, &dev_attr_getcurrent); + if(ret < 0) + pr_err("%s: Failed to create sysfs entry for current\n", __func__); + + ret = device_create_file(&pdev->dev, &dev_attr_getavgcurrent); + if(ret < 0) + pr_err("%s: Failed to create sysfs entry for avg current\n", __func__); + + ret = device_create_file(&pdev->dev, &dev_attr_setage); + if(ret < 0) + pr_err("%s: Failed to create sysfs entry for setage\n", __func__); + + ret = device_create_file(&pdev->dev, &dev_attr_setAEvolt); + if(ret < 0) + pr_err("%s: Failed to create sysfs entry for setAEvolt\n", __func__); + + ret = device_create_file(&pdev->dev, &dev_attr_getFull40); + if(ret < 0) + pr_err("%s: Failed to create sysfs entry for getFull40\n", __func__); + + ret = device_create_file(&pdev->dev, &dev_attr_getmAh); + if(ret < 0) + pr_err("%s: Failed to create sysfs entry for mAh\n", __func__); + + /* End of file call-outs --RP*/ + INIT_WORK(&di->monitor_work, ds2784_battery_work); di->monitor_wqueue = create_freezable_workqueue(dev_name(&pdev->dev)); From 9102ddd7e0b2252d0294a376ac044b0391f7aeed Mon Sep 17 00:00:00 2001 From: thelogin Date: Fri, 26 Nov 2010 05:16:28 -0500 Subject: [PATCH 0981/2556] power: ds2784: Updates for battery calibration Change-Id: I42e00d2c0c97bcc0d269c0e9366fe1f6c2acd873 --- drivers/power/ds2784_battery.c | 105 ++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 40 deletions(-) diff --git a/drivers/power/ds2784_battery.c b/drivers/power/ds2784_battery.c index b1ef247328442..3990488913734 100644 --- a/drivers/power/ds2784_battery.c +++ b/drivers/power/ds2784_battery.c @@ -403,12 +403,26 @@ static int battery_get_property(struct power_supply *psy, return 0; } -/* Here we begin to add the functions for our device attribute - * files which act as our link from kernel space to user space. +/* Here we begin to add our device attribute files which is our link from + * kernel space to user space. These files appear under + * /sys/devices/platform/ds2784-battery/ and are named setreg, dumpreg, + * statusreg, getvoltage and getcurrent. These files/functions allow us to + * interact with the battery's read and write functions which is the + * foundation for a re-calibration mode app along with a number of other + * features/advantages. * + * Here we call out the functions and the device attribute macros, and in the + * probe function (end of this driver) we call out the actual file creations. * --RogerPodacter, theloginwithnoname */ +/* setreg - this creates a file called "setreg" and allows us to + * echo 2 values with a space between them, register and value, which + * calls the battery write function. For example, "echo 0x14 0x80" + * writes register 0x14 with a value of 0x80. + * + * --RogerPodacter + */ static ssize_t store_set_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ds2784_device_info *di = dev_get_drvdata(dev); @@ -437,6 +451,11 @@ static ssize_t store_set_reg(struct device *dev, struct device_attribute *attr, /*This is the actual device attr macro call-out for "setreg" file*/ static DEVICE_ATTR(setreg, 0644, NULL, store_set_reg); +/* dumpreg - this creates a file called "dumpreg" and this is a read-only + * file. When this file is read, it returns all values from the battery + * EEPROM chip. + * --RP + */ static ssize_t show_dump_reg(struct device *dev, struct device_attribute *attr, char *buf) { struct ds2784_device_info *di = dev_get_drvdata(dev); @@ -486,9 +505,16 @@ static ssize_t show_dump_reg(struct device *dev, struct device_attribute *attr, return ret; } - +/*This is the actual device attr macro call-out for "dumpreg" file*/ static DEVICE_ATTR(dumpreg, 0644, show_dump_reg, NULL); +/* statusreg - this creates a file called "statusreg" which is needed. + * This allows us to read the status register more frequently than + * the driver's 1-minute intervals. We often only get a 3-5 second + * window from the status reg updating to shutdown to hit learn mode, + * so this gives us more accurate polling of the status register. + * --RP + */ static ssize_t show_status_reg(struct device *dev, struct device_attribute *attr, char *buf) { struct ds2784_device_info *di = dev_get_drvdata(dev); @@ -530,9 +556,16 @@ static ssize_t store_status_reg(struct device *dev, struct device_attribute *att return count; } - +/*This is the actual device attr macro call-out for "statusreg" file*/ static DEVICE_ATTR(statusreg, 0644, show_status_reg, store_status_reg); +/* getvoltage - this creates a file called "getvoltage". This allows us + * to poll the battery voltage more frequently than the driver's 1-minute + * intervals should we want that option for the graphing portion of the + * calibration app. + * + * --RP + */ static ssize_t show_getvoltage(struct device *dev, struct device_attribute *attr, char *buf) { struct ds2784_device_info *di = dev_get_drvdata(dev); @@ -551,9 +584,16 @@ static ssize_t show_getvoltage(struct device *dev, struct device_attribute *attr ret = sprintf(buf, "%d\n", getvoltage); return ret; } - +/*This is the actual device attr macro call-out for "getvoltage" file*/ static DEVICE_ATTR(getvoltage, 0644, show_getvoltage, NULL); +/* getcurrent - this creates a file called "getcurrent". This allows us + * to poll the battery voltage more frequently than the driver's 1-minute + * intervals should we want that option for the graphing portion of the + * calibration app. + * + * --RP + */ static ssize_t show_getcurrent(struct device *dev, struct device_attribute *attr, char *buf) { struct ds2784_device_info *di = dev_get_drvdata(dev); @@ -574,9 +614,16 @@ static ssize_t show_getcurrent(struct device *dev, struct device_attribute *attr ret = sprintf(buf, "%d\n", getcurrent); return ret; } - +/*This is the actual device attr macro call-out for "getcurrent" file*/ static DEVICE_ATTR(getcurrent, 0644, show_getcurrent, NULL); +/* getavgcurrent - this creates a file called "getavgcurrent". This allows us + * to poll the battery voltage more frequently than the driver's 1-minute + * intervals should we want that option for the graphing portion of the + * calibration app. + * + * --RP + */ static ssize_t show_getavgcurrent(struct device *dev, struct device_attribute *attr, char *buf) { struct ds2784_device_info *di = dev_get_drvdata(dev); @@ -597,6 +644,7 @@ static ssize_t show_getavgcurrent(struct device *dev, struct device_attribute *a ret = sprintf(buf, "%d\n", getavgcurrent); return ret; } +/*This is the actual device attr macro call-out for "getavgcurrent" file*/ static DEVICE_ATTR(getavgcurrent, 0644, show_getavgcurrent, NULL); static ssize_t show_set_age(struct device *dev, struct device_attribute *attr, char *buf) @@ -618,7 +666,7 @@ static ssize_t show_set_age(struct device *dev, struct device_attribute *attr, c pr_info("batt: age_scalar life left is: %d\n", age); - ret = sprintf(buf, "Battery's age: %d", age); + ret = sprintf(buf, "%d\n", age); return ret; } @@ -644,7 +692,7 @@ static ssize_t store_set_age(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR(setage, 0644, show_set_age, store_set_age); +static DEVICE_ATTR(age, 0644, show_set_age, store_set_age); static ssize_t show_set_AEvolt(struct device *dev, struct device_attribute *attr, char *buf) { @@ -688,12 +736,12 @@ static ssize_t store_set_AEvolt (struct device *dev, struct device_attribute *at temp = ( ( val * 100 ) / 1952 ) ; - pr_info("batt: Active Empty Voltage set to: %d percent\n", temp); + pr_info("batt: Active Empty Voltage set to: %d volts\n", temp); return count; } -static DEVICE_ATTR(setAEvolt, 0644, show_set_AEvolt, store_set_AEvolt); +static DEVICE_ATTR(voltAE, 0644, show_set_AEvolt, store_set_AEvolt); static ssize_t show_get_full40(struct device *dev, struct device_attribute *attr, char *buf) { @@ -743,9 +791,7 @@ static ssize_t show_get_mAh(struct device *dev, struct device_attribute *attr, c static DEVICE_ATTR(getmAh, 0644, show_get_mAh, NULL); -/*End of file functions edits - *--RP - */ +/*End of file functions edits --RP*/ static void ds2784_battery_update_status(struct ds2784_device_info *di) { @@ -789,28 +835,6 @@ static int battery_adjust_charge_state(struct ds2784_device_info *di) /* shut off charger when full: * - CHGTF flag is set */ - /* We don't move from full to not-full until - * we drop below 95%, to avoid confusing the - * user while we're maintaining a full charge - * (slowly draining to 95 and charging back - - * to 100) - * Oddly, only Passion is for 99% cycles, HTC - * set the Bravo to 95%. - * -od of xbravoteam - * - * Set 99 for Passion - pershoot - */ - - if (di->status.percentage < 99) { - di->status.battery_full = 0; - } - - /* Changed this code if-statement back to stock because this - * parameter is now user settable by changing the actual - * register value inside the battery chip EEPROM. - * --RP - */ if (di->status.status_reg & 0x80) { di->status.battery_full = 1; @@ -819,6 +843,7 @@ static int battery_adjust_charge_state(struct ds2784_device_info *di) else di->status.battery_full = 0; + if (temp >= TEMP_HOT) { if (temp >= TEMP_CRITICAL) charge_mode = CHARGE_BATT_DISABLE; @@ -841,7 +866,7 @@ static int battery_adjust_charge_state(struct ds2784_device_info *di) charge_mode = CHARGE_BATT_DISABLE; } - if (di->status.battery_full == 1) + if (di->status.current_uA > 1024) di->last_charge_seen = di->last_poll; else if (di->last_charge_mode != CHARGE_OFF && check_timeout(di->last_poll, di->last_charge_seen, 60 * 60)) { @@ -1058,13 +1083,13 @@ static int ds2784_battery_probe(struct platform_device *pdev) if(ret < 0) pr_err("%s: Failed to create sysfs entry for avg current\n", __func__); - ret = device_create_file(&pdev->dev, &dev_attr_setage); + ret = device_create_file(&pdev->dev, &dev_attr_age); if(ret < 0) - pr_err("%s: Failed to create sysfs entry for setage\n", __func__); + pr_err("%s: Failed to create sysfs entry for age\n", __func__); - ret = device_create_file(&pdev->dev, &dev_attr_setAEvolt); + ret = device_create_file(&pdev->dev, &dev_attr_voltAE); if(ret < 0) - pr_err("%s: Failed to create sysfs entry for setAEvolt\n", __func__); + pr_err("%s: Failed to create sysfs entry for voltAE\n", __func__); ret = device_create_file(&pdev->dev, &dev_attr_getFull40); if(ret < 0) From 3199406f01009062287adbe26e99e8b330a94a8c Mon Sep 17 00:00:00 2001 From: thelogin Date: Tue, 29 Mar 2011 22:31:59 -0400 Subject: [PATCH 0982/2556] *finally* brought my github up to date with changes submitted to cm-kernel! --- drivers/power/ds2784_battery.c | 133 ++++++++++----------------------- 1 file changed, 39 insertions(+), 94 deletions(-) diff --git a/drivers/power/ds2784_battery.c b/drivers/power/ds2784_battery.c index 3990488913734..5becf4e15d37d 100644 --- a/drivers/power/ds2784_battery.c +++ b/drivers/power/ds2784_battery.c @@ -12,6 +12,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * + * additional code by XDA members RogerPodacter and theloginwithnoname, 2010 */ #include #include @@ -403,27 +404,7 @@ static int battery_get_property(struct power_supply *psy, return 0; } -/* Here we begin to add our device attribute files which is our link from - * kernel space to user space. These files appear under - * /sys/devices/platform/ds2784-battery/ and are named setreg, dumpreg, - * statusreg, getvoltage and getcurrent. These files/functions allow us to - * interact with the battery's read and write functions which is the - * foundation for a re-calibration mode app along with a number of other - * features/advantages. - * - * Here we call out the functions and the device attribute macros, and in the - * probe function (end of this driver) we call out the actual file creations. - * --RogerPodacter, theloginwithnoname - */ - -/* setreg - this creates a file called "setreg" and allows us to - * echo 2 values with a space between them, register and value, which - * calls the battery write function. For example, "echo 0x14 0x80" - * writes register 0x14 with a value of 0x80. - * - * --RogerPodacter - */ -static ssize_t store_set_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t set_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ds2784_device_info *di = dev_get_drvdata(dev); int reg; @@ -448,15 +429,10 @@ static ssize_t store_set_reg(struct device *dev, struct device_attribute *attr, return count; } -/*This is the actual device attr macro call-out for "setreg" file*/ -static DEVICE_ATTR(setreg, 0644, NULL, store_set_reg); -/* dumpreg - this creates a file called "dumpreg" and this is a read-only - * file. When this file is read, it returns all values from the battery - * EEPROM chip. - * --RP - */ -static ssize_t show_dump_reg(struct device *dev, struct device_attribute *attr, char *buf) +static DEVICE_ATTR(setreg, 0644, NULL, set_reg); + +static ssize_t dump_regs(struct device *dev, struct device_attribute *attr, char *buf) { struct ds2784_device_info *di = dev_get_drvdata(dev); @@ -505,16 +481,9 @@ static ssize_t show_dump_reg(struct device *dev, struct device_attribute *attr, return ret; } -/*This is the actual device attr macro call-out for "dumpreg" file*/ -static DEVICE_ATTR(dumpreg, 0644, show_dump_reg, NULL); - -/* statusreg - this creates a file called "statusreg" which is needed. - * This allows us to read the status register more frequently than - * the driver's 1-minute intervals. We often only get a 3-5 second - * window from the status reg updating to shutdown to hit learn mode, - * so this gives us more accurate polling of the status register. - * --RP - */ + +static DEVICE_ATTR(dumpreg, 0644, dump_regs, NULL); + static ssize_t show_status_reg(struct device *dev, struct device_attribute *attr, char *buf) { struct ds2784_device_info *di = dev_get_drvdata(dev); @@ -556,17 +525,10 @@ static ssize_t store_status_reg(struct device *dev, struct device_attribute *att return count; } -/*This is the actual device attr macro call-out for "statusreg" file*/ + static DEVICE_ATTR(statusreg, 0644, show_status_reg, store_status_reg); -/* getvoltage - this creates a file called "getvoltage". This allows us - * to poll the battery voltage more frequently than the driver's 1-minute - * intervals should we want that option for the graphing portion of the - * calibration app. - * - * --RP - */ -static ssize_t show_getvoltage(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_voltage(struct device *dev, struct device_attribute *attr, char *buf) { struct ds2784_device_info *di = dev_get_drvdata(dev); int ret; @@ -584,17 +546,10 @@ static ssize_t show_getvoltage(struct device *dev, struct device_attribute *attr ret = sprintf(buf, "%d\n", getvoltage); return ret; } -/*This is the actual device attr macro call-out for "getvoltage" file*/ -static DEVICE_ATTR(getvoltage, 0644, show_getvoltage, NULL); -/* getcurrent - this creates a file called "getcurrent". This allows us - * to poll the battery voltage more frequently than the driver's 1-minute - * intervals should we want that option for the graphing portion of the - * calibration app. - * - * --RP - */ -static ssize_t show_getcurrent(struct device *dev, struct device_attribute *attr, char *buf) +static DEVICE_ATTR(getvoltage, 0644, show_voltage, NULL); + +static ssize_t show_current(struct device *dev, struct device_attribute *attr, char *buf) { struct ds2784_device_info *di = dev_get_drvdata(dev); short n; @@ -614,17 +569,10 @@ static ssize_t show_getcurrent(struct device *dev, struct device_attribute *attr ret = sprintf(buf, "%d\n", getcurrent); return ret; } -/*This is the actual device attr macro call-out for "getcurrent" file*/ -static DEVICE_ATTR(getcurrent, 0644, show_getcurrent, NULL); -/* getavgcurrent - this creates a file called "getavgcurrent". This allows us - * to poll the battery voltage more frequently than the driver's 1-minute - * intervals should we want that option for the graphing portion of the - * calibration app. - * - * --RP - */ -static ssize_t show_getavgcurrent(struct device *dev, struct device_attribute *attr, char *buf) +static DEVICE_ATTR(getcurrent, 0644, show_current, NULL); + +static ssize_t show_avgcurrent(struct device *dev, struct device_attribute *attr, char *buf) { struct ds2784_device_info *di = dev_get_drvdata(dev); short n; @@ -644,10 +592,10 @@ static ssize_t show_getavgcurrent(struct device *dev, struct device_attribute *a ret = sprintf(buf, "%d\n", getavgcurrent); return ret; } -/*This is the actual device attr macro call-out for "getavgcurrent" file*/ -static DEVICE_ATTR(getavgcurrent, 0644, show_getavgcurrent, NULL); -static ssize_t show_set_age(struct device *dev, struct device_attribute *attr, char *buf) +static DEVICE_ATTR(getavgcurrent, 0644, show_avgcurrent, NULL); + +static ssize_t show_age(struct device *dev, struct device_attribute *attr, char *buf) { struct ds2784_device_info *di = dev_get_drvdata(dev); int ret; @@ -664,14 +612,14 @@ static ssize_t show_set_age(struct device *dev, struct device_attribute *attr, c ageraw = di->raw[DS2784_REG_AGE_SCALAR]; age = (ageraw * 100) / 128; - pr_info("batt: age_scalar life left is: %d\n", age); + pr_info("%d\n", age); ret = sprintf(buf, "%d\n", age); - return ret; + return ret; } -static ssize_t store_set_age(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t set_age(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ds2784_device_info *di = dev_get_drvdata(dev); int age; @@ -692,9 +640,9 @@ static ssize_t store_set_age(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR(age, 0644, show_set_age, store_set_age); +static DEVICE_ATTR(age, 0644, show_age, set_age); -static ssize_t show_set_AEvolt(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_AEvolt(struct device *dev, struct device_attribute *attr, char *buf) { struct ds2784_device_info *di = dev_get_drvdata(dev); int ret; @@ -712,12 +660,12 @@ static ssize_t show_set_AEvolt(struct device *dev, struct device_attribute *attr pr_info("batt: Active Empty Voltage is: %d volts\n", aevolt); - ret = sprintf(buf, "AEvolt: %d\n", aevolt); + ret = sprintf(buf, "%d\n", aevolt); return ret; } -static ssize_t store_set_AEvolt (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t set_AEvolt (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ds2784_device_info *di = dev_get_drvdata(dev); int val; @@ -741,9 +689,9 @@ static ssize_t store_set_AEvolt (struct device *dev, struct device_attribute *at return count; } -static DEVICE_ATTR(voltAE, 0644, show_set_AEvolt, store_set_AEvolt); +static DEVICE_ATTR(voltAE, 0644, show_AEvolt, set_AEvolt); -static ssize_t show_get_full40(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_full40(struct device *dev, struct device_attribute *attr, char *buf) { struct ds2784_device_info *di = dev_get_drvdata(dev); int ret; @@ -762,14 +710,16 @@ static ssize_t show_get_full40(struct device *dev, struct device_attribute *attr pr_info("batt: Full40 mAh capacity is: %d mAh\n", full40mAh); - ret = sprintf(buf, "Full40: %d mAh\n", full40mAh); + ret = sprintf(buf, "%dmAh\n", full40mAh); return ret; } -static DEVICE_ATTR(getFull40, 0644, show_get_full40, NULL); +// backwards compatibility for app until it is updated +static DEVICE_ATTR(getFull40, 0644, show_full40, NULL); +static DEVICE_ATTR(getfull40, 0644, show_full40, NULL); -static ssize_t show_get_mAh(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_mAh(struct device *dev, struct device_attribute *attr, char *buf) { struct ds2784_device_info *di = dev_get_drvdata(dev); int ret; @@ -789,9 +739,7 @@ static ssize_t show_get_mAh(struct device *dev, struct device_attribute *attr, c return ret; } -static DEVICE_ATTR(getmAh, 0644, show_get_mAh, NULL); - -/*End of file functions edits --RP*/ +static DEVICE_ATTR(getmAh, 0644, show_mAh, NULL); static void ds2784_battery_update_status(struct ds2784_device_info *di) { @@ -1055,10 +1003,6 @@ static int ds2784_battery_probe(struct platform_device *pdev) if (rc) goto fail_register; - /* Here we call out all of the new file creations in the - * probe function. - * --RP - */ ret = device_create_file(&pdev->dev, &dev_attr_setreg); if(ret < 0) pr_err("%s: Failed to create sysfs entry for setreg\n", __func__); @@ -1091,16 +1035,17 @@ static int ds2784_battery_probe(struct platform_device *pdev) if(ret < 0) pr_err("%s: Failed to create sysfs entry for voltAE\n", __func__); + // backwards compatibility for app until it is updated ret = device_create_file(&pdev->dev, &dev_attr_getFull40); - if(ret < 0) - pr_err("%s: Failed to create sysfs entry for getFull40\n", __func__); + + ret = device_create_file(&pdev->dev, &dev_attr_getfull40); + if (ret < 0) + pr_err("%s: Failed to create sysfs entry for getfull40\n", __func__); ret = device_create_file(&pdev->dev, &dev_attr_getmAh); if(ret < 0) pr_err("%s: Failed to create sysfs entry for mAh\n", __func__); - /* End of file call-outs --RP*/ - INIT_WORK(&di->monitor_work, ds2784_battery_work); di->monitor_wqueue = create_freezable_workqueue(dev_name(&pdev->dev)); From 2384b244e534a57ebda1e01b0d813cefa399c2a8 Mon Sep 17 00:00:00 2001 From: thelogin Date: Tue, 29 Mar 2011 22:44:28 -0400 Subject: [PATCH 0983/2556] updated filenames, removing verb-like prefixes (e.g. getfull40 -> full40) corrected the permissions of those files to reflect the way they can be used (e.g. r--r--r-- for read-only, rw-rw-rw- for read and write) nb: this will allow access to the files when device rooted but current user not authenticated as root - useful for some apps (e.g. Astro file manager) removed extraneous logging when reading values --- drivers/power/ds2784_battery.c | 66 ++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/drivers/power/ds2784_battery.c b/drivers/power/ds2784_battery.c index 5becf4e15d37d..533552cc4aa52 100644 --- a/drivers/power/ds2784_battery.c +++ b/drivers/power/ds2784_battery.c @@ -430,7 +430,7 @@ static ssize_t set_reg(struct device *dev, struct device_attribute *attr, const return count; } -static DEVICE_ATTR(setreg, 0644, NULL, set_reg); +static DEVICE_ATTR(setreg, 0666, NULL, set_reg); static ssize_t dump_regs(struct device *dev, struct device_attribute *attr, char *buf) { @@ -482,7 +482,7 @@ static ssize_t dump_regs(struct device *dev, struct device_attribute *attr, char return ret; } -static DEVICE_ATTR(dumpreg, 0644, dump_regs, NULL); +static DEVICE_ATTR(dumpreg, 0444, dump_regs, NULL); static ssize_t show_status_reg(struct device *dev, struct device_attribute *attr, char *buf) { @@ -526,7 +526,7 @@ static ssize_t store_status_reg(struct device *dev, struct device_attribute *att return count; } -static DEVICE_ATTR(statusreg, 0644, show_status_reg, store_status_reg); +static DEVICE_ATTR(statusreg, 0666, show_status_reg, store_status_reg); static ssize_t show_voltage(struct device *dev, struct device_attribute *attr, char *buf) { @@ -547,7 +547,8 @@ static ssize_t show_voltage(struct device *dev, struct device_attribute *attr, c return ret; } -static DEVICE_ATTR(getvoltage, 0644, show_voltage, NULL); +static DEVICE_ATTR(getvoltage, 0644, show_voltage, NULL); // deprecated +static DEVICE_ATTR(voltageNow, 0444, show_voltage, NULL); static ssize_t show_current(struct device *dev, struct device_attribute *attr, char *buf) { @@ -570,7 +571,8 @@ static ssize_t show_current(struct device *dev, struct device_attribute *attr, c return ret; } -static DEVICE_ATTR(getcurrent, 0644, show_current, NULL); +static DEVICE_ATTR(getcurrent, 0644, show_current, NULL); // deprecated +static DEVICE_ATTR(currentNow, 0444, show_current, NULL); static ssize_t show_avgcurrent(struct device *dev, struct device_attribute *attr, char *buf) { @@ -593,7 +595,9 @@ static ssize_t show_avgcurrent(struct device *dev, struct device_attribute *attr return ret; } -static DEVICE_ATTR(getavgcurrent, 0644, show_avgcurrent, NULL); +// compatibility til app update +static DEVICE_ATTR(getavgcurrent, 0644, show_avgcurrent, NULL); // deprecated +static DEVICE_ATTR(currentAverage, 0444, show_avgcurrent, NULL); static ssize_t show_age(struct device *dev, struct device_attribute *attr, char *buf) { @@ -640,7 +644,7 @@ static ssize_t set_age(struct device *dev, struct device_attribute *attr, const return count; } -static DEVICE_ATTR(age, 0644, show_age, set_age); +static DEVICE_ATTR(age, 0666, show_age, set_age); static ssize_t show_AEvolt(struct device *dev, struct device_attribute *attr, char *buf) { @@ -658,7 +662,7 @@ static ssize_t show_AEvolt(struct device *dev, struct device_attribute *attr, ch rawaevolt = di->raw[DS2784_REG_ACTIVE_EMPTY_VOLT]; aevolt = (rawaevolt * 1952) / 100; - pr_info("batt: Active Empty Voltage is: %d volts\n", aevolt); + // pr_info("batt: Active Empty Voltage is: %d volts\n", aevolt); ret = sprintf(buf, "%d\n", aevolt); @@ -689,7 +693,8 @@ static ssize_t set_AEvolt (struct device *dev, struct device_attribute *attr, co return count; } -static DEVICE_ATTR(voltAE, 0644, show_AEvolt, set_AEvolt); +static DEVICE_ATTR(voltAE, 0644, show_AEvolt, set_AEvolt); // deprecated +static DEVICE_ATTR(voltageActiveEmpty, 0666, show_AEvolt, set_AEvolt); static ssize_t show_full40(struct device *dev, struct device_attribute *attr, char *buf) { @@ -708,16 +713,23 @@ static ssize_t show_full40(struct device *dev, struct device_attribute *attr, ch full40mAh = ((full40raw * 625) / 100) / 15; - pr_info("batt: Full40 mAh capacity is: %d mAh\n", full40mAh); + // no need to put this in log, only when writing + // pr_info("batt: Full40 mAh capacity is: %d mAh\n", full40mAh); ret = sprintf(buf, "%dmAh\n", full40mAh); return ret; } -// backwards compatibility for app until it is updated -static DEVICE_ATTR(getFull40, 0644, show_full40, NULL); -static DEVICE_ATTR(getfull40, 0644, show_full40, NULL); +// backwards compatibility removed +// static DEVICE_ATTR(getFull40, 0644, show_full40, NULL); + +// again, backwards compatibility until app updated +static DEVICE_ATTR(getfull40, 0644, show_full40, NULL); // deprecated + +// use "proper" permissions, appropriate to read/write nature of file +// correct the filename - permissions indicate whether value changeable from now on +static DEVICE_ATTR(full40, 0444, show_full40, NULL); static ssize_t show_mAh(struct device *dev, struct device_attribute *attr, char *buf) { @@ -739,7 +751,8 @@ static ssize_t show_mAh(struct device *dev, struct device_attribute *attr, char return ret; } -static DEVICE_ATTR(getmAh, 0644, show_mAh, NULL); +static DEVICE_ATTR(getmAh, 0644, show_mAh, NULL); // deprecated +static DEVICE_ATTR(mAh, 0444, show_mAh, NULL); static void ds2784_battery_update_status(struct ds2784_device_info *di) { @@ -1015,15 +1028,21 @@ static int ds2784_battery_probe(struct platform_device *pdev) if(ret < 0) pr_err("%s: Failed to create sysfs entry for statusreg\n", __func__); - ret = device_create_file(&pdev->dev, &dev_attr_getvoltage); + ret = device_create_file(&pdev->dev, &dev_attr_getvoltage); // deprecated + + ret = device_create_file(&pdev->dev, &dev_attr_voltageNow); if(ret < 0) pr_err("%s: Failed to create sysfs entry for voltage\n", __func__); - ret = device_create_file(&pdev->dev, &dev_attr_getcurrent); + ret = device_create_file(&pdev->dev, &dev_attr_getcurrent); // deprecated + + ret = device_create_file(&pdev->dev, &dev_attr_currentNow); if(ret < 0) pr_err("%s: Failed to create sysfs entry for current\n", __func__); - ret = device_create_file(&pdev->dev, &dev_attr_getavgcurrent); + ret = device_create_file(&pdev->dev, &dev_attr_getavgcurrent); // deprecated + + ret = device_create_file(&pdev->dev, &dev_attr_currentAverage); if(ret < 0) pr_err("%s: Failed to create sysfs entry for avg current\n", __func__); @@ -1031,18 +1050,21 @@ static int ds2784_battery_probe(struct platform_device *pdev) if(ret < 0) pr_err("%s: Failed to create sysfs entry for age\n", __func__); - ret = device_create_file(&pdev->dev, &dev_attr_voltAE); + ret = device_create_file(&pdev->dev, &dev_attr_voltAE); // deprecated + + ret = device_create_file(&pdev->dev, &dev_attr_voltageActiveEmpty); if(ret < 0) pr_err("%s: Failed to create sysfs entry for voltAE\n", __func__); - // backwards compatibility for app until it is updated - ret = device_create_file(&pdev->dev, &dev_attr_getFull40); + ret = device_create_file(&pdev->dev, &dev_attr_getfull40); // deprecated - ret = device_create_file(&pdev->dev, &dev_attr_getfull40); + ret = device_create_file(&pdev->dev, &dev_attr_full40); if (ret < 0) pr_err("%s: Failed to create sysfs entry for getfull40\n", __func__); - ret = device_create_file(&pdev->dev, &dev_attr_getmAh); + ret = device_create_file(&pdev->dev, &dev_attr_getmAh); // deprecated + + ret = device_create_file(&pdev->dev, &dev_attr_mAh); if(ret < 0) pr_err("%s: Failed to create sysfs entry for mAh\n", __func__); From 14ba008001325bd83babbd583f24ff2dbf8deb4c Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Sat, 8 Jan 2011 05:04:21 -0500 Subject: [PATCH 0984/2556] msm: qsdp6: Updated encoder/decoder from HTC --- arch/arm/mach-msm/dal.c | 24 + arch/arm/mach-msm/dal.h | 8 + arch/arm/mach-msm/qdsp6/msm_q6vdec.c | 113 +++-- arch/arm/mach-msm/qdsp6/msm_q6venc.c | 654 ++++++++++++++++----------- include/linux/msm_q6vdec.h | 15 +- include/linux/msm_q6venc.h | 10 + 6 files changed, 533 insertions(+), 291 deletions(-) diff --git a/arch/arm/mach-msm/dal.c b/arch/arm/mach-msm/dal.c index 7d37efc2a3c28..971abbbcefc3a 100644 --- a/arch/arm/mach-msm/dal.c +++ b/arch/arm/mach-msm/dal.c @@ -567,6 +567,30 @@ int dal_call_f5(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t il return res; } +int dal_call_f9(struct dal_client *client, uint32_t ddi, void *obuf, + uint32_t olen) +{ + uint32_t tmp[128]; + int res; + + if (olen > sizeof(tmp) - 8) + return -EINVAL; + tmp[0] = olen; + + res = dal_call(client, ddi, 9, tmp, sizeof(uint32_t), tmp, + sizeof(tmp)); + + if (res >= 4) + res = (int)tmp[0]; + + if (!res) { + if (tmp[1] > olen) + return -EIO; + memcpy(obuf, &tmp[2], tmp[1]); + } + return res; +} + int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1, uint32_t ilen1, void *ibuf2, uint32_t ilen2, void *obuf, uint32_t olen) diff --git a/arch/arm/mach-msm/dal.h b/arch/arm/mach-msm/dal.h index c02f5c7843b0e..6e4368295be31 100644 --- a/arch/arm/mach-msm/dal.h +++ b/arch/arm/mach-msm/dal.h @@ -19,6 +19,12 @@ struct dal_client; +struct dal_info { + uint32_t size; + uint32_t version; + char name[32]; +}; + typedef void (*dal_event_func_t)(void *data, int len, void *cookie); struct dal_client *dal_attach(uint32_t device_id, const char *name, @@ -44,6 +50,8 @@ int dal_call_f1(struct dal_client *client, uint32_t ddi, uint32_t arg1, uint32_t arg2); int dal_call_f5(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t ilen); +int dal_call_f9(struct dal_client *client, uint32_t ddi, + void *obuf, uint32_t olen); int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1, uint32_t ilen1, void *ibuf2, uint32_t ilen2, void *obuf, uint32_t olen); diff --git a/arch/arm/mach-msm/qdsp6/msm_q6vdec.c b/arch/arm/mach-msm/qdsp6/msm_q6vdec.c index a719dbdbcd912..d6106e0463b7d 100644 --- a/arch/arm/mach-msm/qdsp6/msm_q6vdec.c +++ b/arch/arm/mach-msm/qdsp6/msm_q6vdec.c @@ -41,10 +41,10 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -67,35 +67,10 @@ #define TRACE(fmt,x...) \ do { pr_debug("%s:%d " fmt, __func__, __LINE__, ##x); } while (0) #else -#define TRACE(fmt,x...) do { } while (0) +#define TRACE(fmt, x...) do { } while (0) #endif - -static DEFINE_MUTEX(idlecount_lock); -static int idlecount; -static struct wake_lock wakelock; -static struct wake_lock idlelock; - -static void prevent_sleep(void) -{ - mutex_lock(&idlecount_lock); - if (++idlecount == 1) { - wake_lock(&idlelock); - wake_lock(&wakelock); - } - mutex_unlock(&idlecount_lock); -} - -static void allow_sleep(void) -{ - mutex_lock(&idlecount_lock); - if (--idlecount == 0) { - wake_unlock(&idlelock); - wake_unlock(&wakelock); - } - mutex_unlock(&idlecount_lock); -} - +#define MAX_SUPPORTED_INSTANCES 2 enum { VDEC_DALRPC_INITIALIZE = DAL_OP_FIRST_DEVICE_API, @@ -163,6 +138,31 @@ static struct cdev vdec_cdev; static int ref_cnt; static DEFINE_MUTEX(vdec_ref_lock); +static DEFINE_MUTEX(idlecount_lock); +static int idlecount; +static struct wake_lock wakelock; +static struct wake_lock idlelock; + +static void prevent_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (++idlecount == 1) { + wake_lock(&idlelock); + wake_lock(&wakelock); + } + mutex_unlock(&idlecount_lock); +} + +static void allow_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (--idlecount == 0) { + wake_unlock(&idlelock); + wake_unlock(&wakelock); + } + mutex_unlock(&idlecount_lock); +} + static inline int vdec_check_version(u32 client, u32 server) { int ret = -EINVAL; @@ -448,6 +448,8 @@ static int vdec_queue(struct vdec_data *vd, void *argp) rpc.size = sizeof(struct vdec_input_buf_info); rpc.osize = sizeof(struct vdec_queue_status); + /* complete the writes to the buffer */ + wmb(); ret = dal_call(vd->vdec_handle, VDEC_DALRPC_QUEUE, 8, &rpc, sizeof(rpc), &rpc_res, sizeof(rpc_res)); if (ret < 4) { @@ -588,6 +590,22 @@ static int vdec_freebuffers(struct vdec_data *vd, void *argp) return ret; } + +static int vdec_getversion(struct vdec_data *vd, void *argp) +{ + struct vdec_version ver_info; + int ret = 0; + + ver_info.major = VDEC_GET_MAJOR_VERSION(VDEC_INTERFACE_VERSION); + ver_info.minor = VDEC_GET_MINOR_VERSION(VDEC_INTERFACE_VERSION); + + ret = copy_to_user(((struct vdec_version *)argp), + &ver_info, sizeof(ver_info)); + + return ret; + +} + static long vdec_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct vdec_data *vd = file->private_data; @@ -639,6 +657,9 @@ static long vdec_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (vd->close_decode) ret = -EINTR; + else + /* order the reads from the buffer */ + rmb(); break; case VDEC_IOCTL_CLOSE: @@ -664,7 +685,15 @@ static long vdec_ioctl(struct file *file, unsigned int cmd, unsigned long arg) pr_err("%s: remote function failed (%d)\n", __func__, ret); break; + case VDEC_IOCTL_GETVERSION: + TRACE("VDEC_IOCTL_GETVERSION (pid=%d tid=%d)\n", + current->group_leader->pid, current->pid); + ret = vdec_getversion(vd, argp); + if (ret) + pr_err("%s: remote function failed (%d)\n", + __func__, ret); + break; default: pr_err("%s: invalid ioctl!\n", __func__); ret = -EINVAL; @@ -685,6 +714,7 @@ static void vdec_dcdone_handler(struct vdec_data *vd, void *frame, unsigned long flags; int found = 0; +/*if (frame_size != sizeof(struct vdec_frame_info)) {*/ if (frame_size < sizeof(struct vdec_frame_info)) { pr_warning("%s: msg size mismatch %d != %d\n", __func__, frame_size, sizeof(struct vdec_frame_info)); @@ -769,13 +799,14 @@ static int vdec_open(struct inode *inode, struct file *file) int i; struct vdec_msg_list *l; struct vdec_data *vd; + struct dal_info version_info; pr_info("q6vdec_open()\n"); mutex_lock(&vdec_ref_lock); - if (ref_cnt > 0) { - pr_err("%s: Instance alredy running\n", __func__); + if (ref_cnt >= MAX_SUPPORTED_INSTANCES) { + pr_err("%s: Max allowed instances exceeded \n", __func__); mutex_unlock(&vdec_ref_lock); - return -ENOMEM; + return -EBUSY; } ref_cnt++; mutex_unlock(&vdec_ref_lock); @@ -814,11 +845,26 @@ static int vdec_open(struct inode *inode, struct file *file) ret = -EIO; goto vdec_open_err_handle_list; } + ret = dal_call_f9(vd->vdec_handle, DAL_OP_INFO, + &version_info, sizeof(struct dal_info)); + + if (ret) { + pr_err("%s: failed to get version \n", __func__); + goto vdec_open_err_handle_version; + } + + TRACE("q6vdec_open() interface version 0x%x\n", version_info.version); + if (vdec_check_version(VDEC_INTERFACE_VERSION, + version_info.version)) { + pr_err("%s: driver version mismatch !\n", __func__); + goto vdec_open_err_handle_version; + } vd->running = 1; prevent_sleep(); return 0; - +vdec_open_err_handle_version: + dal_detach(vd->vdec_handle); vdec_open_err_handle_list: { struct vdec_msg_list *l, *n; @@ -828,6 +874,9 @@ static int vdec_open(struct inode *inode, struct file *file) } } vdec_open_err_handle_vd: + mutex_lock(&vdec_ref_lock); + ref_cnt--; + mutex_unlock(&vdec_ref_lock); kfree(vd); return ret; } diff --git a/arch/arm/mach-msm/qdsp6/msm_q6venc.c b/arch/arm/mach-msm/qdsp6/msm_q6venc.c index 7b4eb85d8b6d6..7b889767476fa 100644 --- a/arch/arm/mach-msm/qdsp6/msm_q6venc.c +++ b/arch/arm/mach-msm/qdsp6/msm_q6venc.c @@ -1,23 +1,57 @@ /* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. - * Copyright (c) 2009-2010, Google Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. * - * 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. + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. + * START * - * Original driver and v2 protocol changes from Code Aurora. - * Heavily modified by Dima Zavin - * Further cleanup by Brian Swetland + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. * */ @@ -28,54 +62,30 @@ #include #include #include -#include #include #include +#include +#include #include #include -#include - -//#include #include "../dal.h" -#define DALDEVICEID_VENC_DEVICE 0x0200002D -#define DALDEVICEID_VENC_PORTNAME "DSP_DAL_AQ_VID" +#define DALDEVICEID_VENC_DEVICE 0x0200002D +/*#define DALDEVICEID_VENC_PORTNAME "DAL_AQ_VID"*/ +#define DALDEVICEID_VENC_PORTNAME "DSP_DAL_AQ_VID" -#define VENC_NAME "q6venc" -#define VENC_MSG_MAX 128 +#define VENC_NAME "q6venc" +#define VENC_MSG_MAX 128 #define VENC_INTERFACE_VERSION 0x00020000 #define MAJOR_MASK 0xFFFF0000 #define MINOR_MASK 0x0000FFFF -#define VENC_GET_MAJOR_VERSION(version) ((version & MAJOR_MASK)>>16) -#define VENC_GET_MINOR_VERSION(version) (version & MINOR_MASK) - -#define VERSION_CHECK 0 - -static DEFINE_MUTEX(idlecount_lock); -static int idlecount; -static struct wake_lock wakelock; -static struct wake_lock idlelock; - -static void prevent_sleep(void) -{ - mutex_lock(&idlecount_lock); - if (++idlecount == 1) { - wake_lock(&idlelock); - wake_lock(&wakelock); - } - mutex_unlock(&idlecount_lock); -} +#define VENC_GET_MAJOR_VERSION(version) ((version & MAJOR_MASK)>>16) +#define VENC_GET_MINOR_VERSION(version) (version & MINOR_MASK) -static void allow_sleep(void) -{ - mutex_lock(&idlecount_lock); - if (--idlecount == 0) { - wake_unlock(&idlelock); - wake_unlock(&wakelock); - } - mutex_unlock(&idlecount_lock); -} +uint32_t kpi_start[5]; +uint32_t kpi_end; +static uint32_t cnt = 0; enum { VENC_BUFFER_TYPE_INPUT, @@ -132,6 +142,11 @@ struct venc_output_buf { struct venc_buf_type bit_stream_buf; u32 client_data; }; + +struct venc_msg_list { + struct list_head list; + struct venc_msg msg_data; +}; struct venc_buf { int fd; u32 offset; @@ -144,18 +159,13 @@ struct venc_pmem_list { struct list_head list; struct venc_buf buf; }; -struct venc_qmsg { - struct list_head list; - struct venc_msg msg; -}; struct venc_dev { - bool stop_called; + bool is_active; + bool pmem_freed; enum venc_state_type state; - - struct list_head msg_pool; - struct list_head msg_queue; - spinlock_t msg_lock; - + struct list_head venc_msg_list_head; + struct list_head venc_msg_list_free; + spinlock_t venc_msg_list_lock; struct list_head venc_pmem_list_head; spinlock_t venc_pmem_list_lock; struct dal_client *q6_handle; @@ -177,6 +187,31 @@ static struct class *venc_class; static struct venc_dev *venc_device_p; static int venc_ref; +static DEFINE_MUTEX(idlecount_lock); +static int idlecount; +static struct wake_lock wakelock; +static struct wake_lock idlelock; + +static void prevent_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (++idlecount == 1) { + wake_lock(&idlelock); + wake_lock(&wakelock); + } + mutex_unlock(&idlecount_lock); +} + +static void allow_sleep(void) +{ + mutex_lock(&idlecount_lock); + if (--idlecount == 0) { + wake_unlock(&idlelock); + wake_unlock(&wakelock); + } + mutex_unlock(&idlecount_lock); +} + static inline int venc_check_version(u32 client, u32 server) { int ret = -EINVAL; @@ -189,62 +224,49 @@ static inline int venc_check_version(u32 client, u32 server) return ret; } -static struct venc_qmsg *__dequeue(spinlock_t *lock, struct list_head *queue) +static int venc_get_msg(struct venc_dev *dvenc, void *msg) { + struct venc_msg_list *l; unsigned long flags; - struct venc_qmsg *msg; - spin_lock_irqsave(lock, flags); - if (list_empty(queue)) { - msg = NULL; - } else { - msg = list_first_entry(queue, struct venc_qmsg, list); - list_del(&msg->list); - } - spin_unlock_irqrestore(lock, flags); - return msg; -} + int ret = 0; + struct venc_msg qdsp_msg; -static inline struct venc_qmsg *venc_alloc_msg(struct venc_dev *dvenc) -{ - return __dequeue(&dvenc->msg_lock, &dvenc->msg_pool); -} -static inline struct venc_qmsg *venc_recv_msg(struct venc_dev *dvenc) -{ - return __dequeue(&dvenc->msg_lock, &dvenc->msg_queue); + if (!dvenc->is_active) + return -EPERM; + spin_lock_irqsave(&dvenc->venc_msg_list_lock, flags); + list_for_each_entry_reverse(l, &dvenc->venc_msg_list_head, list) { + memcpy(&qdsp_msg, &l->msg_data, sizeof(struct venc_msg)); + list_del(&l->list); + list_add(&l->list, &dvenc->venc_msg_list_free); + ret = 1; + break; + } + spin_unlock_irqrestore(&dvenc->venc_msg_list_lock, flags); + if (copy_to_user(msg, &qdsp_msg, sizeof(struct venc_msg))) + pr_err("%s failed to copy_to_user\n", __func__); + return ret; } -static void venc_free_msg(struct venc_dev *dvenc, struct venc_qmsg *msg) +static void venc_put_msg(struct venc_dev *dvenc, struct venc_msg *msg) { + struct venc_msg_list *l; unsigned long flags; - spin_lock_irqsave(&dvenc->msg_lock, flags); - list_add_tail(&msg->list, &dvenc->msg_pool); - spin_unlock_irqrestore(&dvenc->msg_lock, flags); -} + int found = 0; -static void venc_post(struct venc_dev *dvenc, - unsigned code, unsigned status, - union venc_msg_data *data) -{ - unsigned long flags; - struct venc_qmsg *msg; - msg = venc_alloc_msg(dvenc); - if (msg == NULL) { - pr_err("%s cannot alloc message\n", __func__); - return; - } - msg->msg.msg_code = code; - msg->msg.status_code = status; - if (data) { - msg->msg.msg_data_size = sizeof(union venc_msg_data); - memcpy(&msg->msg.msg_data, data, sizeof(union venc_msg_data)); - } else { - msg->msg.msg_data_size = 0; + spin_lock_irqsave(&dvenc->venc_msg_list_lock, flags); + list_for_each_entry(l, &dvenc->venc_msg_list_free, list) { + memcpy(&l->msg_data, msg, sizeof(struct venc_msg)); + list_del(&l->list); + list_add(&l->list, &dvenc->venc_msg_list_head); + found = 1; + break; } + spin_unlock_irqrestore(&dvenc->venc_msg_list_lock, flags); + if (found) + wake_up(&dvenc->venc_msg_evt); + else + pr_err("%s: failed to find a free node\n", __func__); - spin_lock_irqsave(&dvenc->msg_lock, flags); - list_add_tail(&msg->list, &dvenc->msg_queue); - spin_unlock_irqrestore(&dvenc->msg_lock, flags); - wake_up(&dvenc->venc_msg_evt); } static struct venc_pmem_list *venc_add_pmem_to_list(struct venc_dev *dvenc, @@ -265,8 +287,6 @@ static struct venc_pmem_list *venc_add_pmem_to_list(struct venc_dev *dvenc, ret = get_pmem_file(mptr->fd, &(plist->buf.paddr), &vaddr, &len, &(plist->buf.file)); - - /* xxx bounds checking insufficient here */ if (ret) { pr_err("%s: get_pmem_file failed for fd=%d offset=%d\n", __func__, mptr->fd, mptr->offset); @@ -363,7 +383,7 @@ static int venc_assign_q6_buffers(struct venc_dev *dvenc, __func__); return -EPERM; } - pcfg->recon_buf1.region = 0; + pcfg->recon_buf1.region = pbufs->recon_buf[0].src; pcfg->recon_buf1.phys = plist->buf.paddr; pcfg->recon_buf1.size = plist->buf.size; pcfg->recon_buf1.offset = 0; @@ -375,7 +395,7 @@ static int venc_assign_q6_buffers(struct venc_dev *dvenc, __func__); return -EPERM; } - pcfg->recon_buf2.region = 0; + pcfg->recon_buf2.region = pbufs->recon_buf[1].src; pcfg->recon_buf2.phys = plist->buf.paddr; pcfg->recon_buf2.size = plist->buf.size; pcfg->recon_buf2.offset = 0; @@ -387,7 +407,7 @@ static int venc_assign_q6_buffers(struct venc_dev *dvenc, __func__); return -EPERM; } - pcfg->wb_buf.region = 0; + pcfg->wb_buf.region = pbufs->wb_buf.src; pcfg->wb_buf.phys = plist->buf.paddr; pcfg->wb_buf.size = plist->buf.size; pcfg->wb_buf.offset = 0; @@ -399,7 +419,7 @@ static int venc_assign_q6_buffers(struct venc_dev *dvenc, __func__); return -EPERM; } - pcfg->cmd_buf.region = 0; + pcfg->cmd_buf.region = pbufs->cmd_buf.src; pcfg->cmd_buf.phys = plist->buf.paddr; pcfg->cmd_buf.size = plist->buf.size; pcfg->cmd_buf.offset = 0; @@ -411,7 +431,7 @@ static int venc_assign_q6_buffers(struct venc_dev *dvenc, " failed\n", __func__); return -EPERM; } - pcfg->vlc_buf.region = 0; + pcfg->vlc_buf.region = pbufs->vlc_buf.src; pcfg->vlc_buf.phys = plist->buf.paddr; pcfg->vlc_buf.size = plist->buf.size; pcfg->vlc_buf.offset = 0; @@ -505,6 +525,11 @@ static int venc_encode_frame(struct venc_dev *dvenc, void *argp) q6_input.dvs_offsetx = 0; q6_input.dvs_offsety = 0; + +kpi_start[cnt] = ktime_to_ns(ktime_get()); +TRACE("kpi_start %d, %u \n", cnt, kpi_start[cnt]); +cnt++; + TRACE("Pushing down input phys=0x%x fd= %d, client_data: 0x%x," " time_stamp:%lld \n", q6_input.yuv_buf.phys, plist->buf.fd, input.client_data, input.time_stamp); @@ -564,12 +589,15 @@ static int venc_fill_output(struct venc_dev *dvenc, void *argp) static int venc_stop(struct venc_dev *dvenc) { int ret = 0; + struct venc_msg msg; - dvenc->stop_called = 1; ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_STOP, 1); if (ret) { pr_err("%s: remote runction failed (%d)\n", __func__, ret); - venc_post(dvenc, VENC_MSG_STOP, VENC_S_EFAIL, NULL); + msg.msg_code = VENC_MSG_STOP; + msg.msg_data_size = 0; + msg.status_code = VENC_S_EFAIL; + venc_put_msg(dvenc, &msg); } return ret; } @@ -577,11 +605,15 @@ static int venc_stop(struct venc_dev *dvenc) static int venc_pause(struct venc_dev *dvenc) { int ret = 0; + struct venc_msg msg; ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_SUSPEND, 1); if (ret) { pr_err("%s: remote function failed (%d)\n", __func__, ret); - venc_post(dvenc, VENC_MSG_PAUSE, VENC_S_EFAIL, NULL); + msg.msg_code = VENC_MSG_PAUSE; + msg.status_code = VENC_S_EFAIL; + msg.msg_data_size = 0; + venc_put_msg(dvenc, &msg); } return ret; } @@ -589,11 +621,15 @@ static int venc_pause(struct venc_dev *dvenc) static int venc_resume(struct venc_dev *dvenc) { int ret = 0; + struct venc_msg msg; ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_RESUME, 1); if (ret) { pr_err("%s: remote function failed (%d)\n", __func__, ret); - venc_post(dvenc, VENC_MSG_RESUME, VENC_S_EFAIL, NULL); + msg.msg_code = VENC_MSG_RESUME; + msg.msg_data_size = 0; + msg.status_code = VENC_S_EFAIL; + venc_put_msg(dvenc, &msg); } return ret; } @@ -601,24 +637,42 @@ static int venc_resume(struct venc_dev *dvenc) static int venc_flush(struct venc_dev *dvenc, void *argp) { int ret = 0; + struct venc_msg msg; + union venc_msg_data smsg; int status = VENC_S_SUCCESS; - union venc_msg_data data; + struct venc_buffer_flush flush; - if (copy_from_user(&data.flush_ret, argp, sizeof(struct venc_buffer_flush))) + if (copy_from_user(&flush, argp, sizeof(struct venc_buffer_flush))) return -EFAULT; - - if (data.flush_ret.flush_mode == VENC_FLUSH_ALL) { + if (flush.flush_mode == VENC_FLUSH_ALL) { ret = dal_call_f0(dvenc->q6_handle, VENC_DALRPC_FLUSH, 1); if (ret) status = VENC_S_EFAIL; } else status = VENC_S_ENOTSUPP; - if (status == VENC_S_SUCCESS) - return ret; - - venc_post(dvenc, VENC_MSG_FLUSH, status, &data); - return -EIO; + if (status != VENC_S_SUCCESS) { + if ((flush.flush_mode == VENC_FLUSH_INPUT) || + (flush.flush_mode == VENC_FLUSH_ALL)) { + smsg.flush_ret.flush_mode = VENC_FLUSH_INPUT; + msg.msg_data = smsg; + msg.status_code = status; + msg.msg_code = VENC_MSG_FLUSH; + msg.msg_data_size = sizeof(union venc_msg_data); + venc_put_msg(dvenc, &msg); + } + if (flush.flush_mode == VENC_FLUSH_OUTPUT || + (flush.flush_mode == VENC_FLUSH_ALL)) { + smsg.flush_ret.flush_mode = VENC_FLUSH_OUTPUT; + msg.msg_data = smsg; + msg.status_code = status; + msg.msg_code = VENC_MSG_FLUSH; + msg.msg_data_size = sizeof(union venc_msg_data); + venc_put_msg(dvenc, &msg); + } + return -EIO; + } + return ret; } static int venc_get_sequence_hdr(struct venc_dev *dvenc, void *argp) @@ -752,46 +806,84 @@ static int venc_request_iframe(struct venc_dev *dvenc) static int venc_stop_read_msg(struct venc_dev *dvenc) { - venc_post(dvenc, VENC_MSG_STOP_READING_MSG, 0, NULL); - return 0; + struct venc_msg msg; + int ret = 0; + + msg.status_code = 0; + msg.msg_code = VENC_MSG_STOP_READING_MSG; + msg.msg_data_size = 0; + venc_put_msg(dvenc, &msg); + return ret; +} + +static int venc_q6_stop(struct venc_dev *dvenc) +{ + int ret = 0; + struct venc_pmem_list *plist; + unsigned long flags; + + wake_up(&dvenc->venc_msg_evt); + spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags); + if (!dvenc->pmem_freed) { + list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) + put_pmem_file(plist->buf.file); + dvenc->pmem_freed = 1; + } + spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags); + + dvenc->state = VENC_STATE_STOP; + return ret; } static int venc_translate_error(enum venc_status_code q6_status) { + int ret = 0; + switch (q6_status) { case VENC_STATUS_SUCCESS: - return VENC_S_SUCCESS; + ret = VENC_S_SUCCESS; + break; case VENC_STATUS_ERROR: - return VENC_S_EFAIL; + ret = VENC_S_EFAIL; + break; case VENC_STATUS_INVALID_STATE: - return VENC_S_EINVALSTATE; + ret = VENC_S_EINVALSTATE; + break; case VENC_STATUS_FLUSHING: - return VENC_S_EFLUSHED; + ret = VENC_S_EFLUSHED; + break; case VENC_STATUS_INVALID_PARAM: - return VENC_S_EBADPARAM; + ret = VENC_S_EBADPARAM; + break; case VENC_STATUS_CMD_QUEUE_FULL: - return VENC_S_ECMDQFULL; + ret = VENC_S_ECMDQFULL; + break; case VENC_STATUS_CRITICAL: - return VENC_S_EFATAL; + ret = VENC_S_EFATAL; + break; case VENC_STATUS_INSUFFICIENT_RESOURCES: - return VENC_S_ENOHWRES; + ret = VENC_S_ENOHWRES; + break; case VENC_STATUS_TIMEOUT: - return VENC_S_ETIMEOUT; - default: - /* xxx probably shouldn't assume success */ - return 0; + ret = VENC_S_ETIMEOUT; + break; } + if (q6_status != VENC_STATUS_SUCCESS) + pr_err("%s: Q6 failed (%d)", __func__, (int)q6_status); + return ret; } -static void venc_q6_callback(void *_data, int len, void *cookie) +static void venc_q6_callback(void *data, int len, void *cookie) { int status = 0; struct venc_dev *dvenc = (struct venc_dev *)cookie; struct venc_msg_type *q6_msg = NULL; + struct venc_msg msg, msg1; + union venc_msg_data smsg1, smsg2; + unsigned long msg_code; struct venc_input_payload *pload1; struct venc_output_payload *pload2; - union venc_msg_data data; - uint32_t *tmp = (uint32_t *) _data; + uint32_t *tmp = (uint32_t *) data; if (dvenc == NULL) { pr_err("%s: empty driver parameter\n", __func__); @@ -804,135 +896,178 @@ static void venc_q6_callback(void *_data, int len, void *cookie) __func__, tmp[2], sizeof(struct venc_msg_type)); return; } - + msg.msg_data_size = 0; status = venc_translate_error(q6_msg->status); - if (status != VENC_STATUS_SUCCESS) - pr_err("%s: Q6 failed (%d)", __func__, (int)status); - switch ((enum venc_event_type_enum)q6_msg->event) { case VENC_EVENT_START_STATUS: dvenc->state = VENC_STATE_START; - venc_post(dvenc, VENC_MSG_START, status, NULL); + msg_code = VENC_MSG_START; break; case VENC_EVENT_STOP_STATUS: - dvenc->state = VENC_STATE_STOP; - venc_post(dvenc, VENC_MSG_STOP, status, NULL); + venc_q6_stop(dvenc); + msg_code = VENC_MSG_STOP; break; case VENC_EVENT_SUSPEND_STATUS: dvenc->state = VENC_STATE_PAUSE; - venc_post(dvenc, VENC_MSG_PAUSE, status, NULL); + msg_code = VENC_MSG_PAUSE; break; case VENC_EVENT_RESUME_STATUS: dvenc->state = VENC_STATE_START; - venc_post(dvenc, VENC_MSG_RESUME, status, NULL); + msg_code = VENC_MSG_RESUME; break; case VENC_EVENT_FLUSH_STATUS: - data.flush_ret.flush_mode = VENC_FLUSH_INPUT; - venc_post(dvenc, VENC_MSG_FLUSH, status, &data); - data.flush_ret.flush_mode = VENC_FLUSH_OUTPUT; - venc_post(dvenc, VENC_MSG_FLUSH, status, &data); + smsg1.flush_ret.flush_mode = VENC_FLUSH_INPUT; + msg1.status_code = status; + msg1.msg_code = VENC_MSG_FLUSH; + msg1.msg_data = smsg1; + msg1.msg_data_size = sizeof(union venc_msg_data); + venc_put_msg(dvenc, &msg1); + smsg2.flush_ret.flush_mode = VENC_FLUSH_OUTPUT; + msg_code = VENC_MSG_FLUSH; + msg.msg_data = smsg2; + msg.msg_data_size = sizeof(union venc_msg_data); break; case VENC_EVENT_RELEASE_INPUT: + +kpi_end = ktime_to_ns(ktime_get()); +TRACE("KPI : encode a frame, %u ms\n", (kpi_end - kpi_start[0])/(1000*1000)); +if (cnt > 0) { + int i = 0; + for (i = 0; i < cnt; i++) + kpi_start[i] = kpi_start[i+1]; +} +cnt--; pload1 = &((q6_msg->payload).input_payload); TRACE("Release_input: data: 0x%x \n", pload1->data); if (pload1 != NULL) { - /* xxx should we zero? */ - data.buf.client_data = pload1->data; - venc_post(dvenc, VENC_MSG_INPUT_BUFFER_DONE, status, &data); - } else { - pr_err("%s no payload on buffer done?\n", __func__); + msg.msg_data.buf.client_data = pload1->data; + msg_code = VENC_MSG_INPUT_BUFFER_DONE; + msg.msg_data_size = sizeof(union venc_msg_data); } break; case VENC_EVENT_DELIVER_OUTPUT: pload2 = &((q6_msg->payload).output_payload); - data.buf.flags = 0; + smsg1.buf.flags = 0; if (pload2->flags & VENC_FLAG_SYNC_FRAME) - data.buf.flags |= VENC_FLAG_SYNC_FRAME; + smsg1.buf.flags |= VENC_FLAG_SYNC_FRAME; if (pload2->flags & VENC_FLAG_CODEC_CONFIG) - data.buf.flags |= VENC_FLAG_CODEC_CONFIG; + smsg1.buf.flags |= VENC_FLAG_CODEC_CONFIG; if (pload2->flags & VENC_FLAG_END_OF_FRAME) - data.buf.flags |= VENC_FLAG_END_OF_FRAME; + smsg1.buf.flags |= VENC_FLAG_END_OF_FRAME; if (pload2->flags & VENC_FLAG_EOS) - data.buf.flags |= VENC_FLAG_EOS; - data.buf.len = pload2->size; - data.buf.offset = 0; - data.buf.time_stamp = pload2->time_stamp; - data.buf.client_data = pload2->data; - venc_post(dvenc, VENC_MSG_OUTPUT_BUFFER_DONE, status, &data); + smsg1.buf.flags |= VENC_FLAG_EOS; + smsg1.buf.len = pload2->size; + smsg1.buf.offset = 0; + smsg1.buf.time_stamp = pload2->time_stamp; + smsg1.buf.client_data = pload2->data; + msg_code = VENC_MSG_OUTPUT_BUFFER_DONE; + msg.msg_data = smsg1; + msg.msg_data_size = sizeof(union venc_msg_data); break; default: pr_err("%s: invalid response from Q6 (%d)\n", __func__, (int)q6_msg->event); - break; + return; } + msg.status_code = status; + msg.msg_code = msg_code; + venc_put_msg(dvenc, &msg); + return; } -static int venc_read_next_msg(struct venc_dev *dvenc, void __user *argp) +static int venc_get_version(struct venc_dev *dvenc, void *argp) { - int res; - struct venc_qmsg *msg; - - res = wait_event_interruptible(dvenc->venc_msg_evt, - (msg = venc_recv_msg(dvenc)) != NULL); - if (res < 0) - return res; - res = copy_to_user(argp, &msg->msg, sizeof(struct venc_msg)); - venc_free_msg(dvenc, msg); - if (res) - return -EFAULT; - return 0; + struct venc_version ver_info; + int ret = 0; + + ver_info.major = VENC_GET_MAJOR_VERSION(VENC_INTERFACE_VERSION); + ver_info.minor = VENC_GET_MINOR_VERSION(VENC_INTERFACE_VERSION); + + ret = copy_to_user(((struct venc_version *)argp), + &ver_info, sizeof(ver_info)); + if (ret) + pr_err("%s failed to copy_to_user\n", __func__); + + return ret; + } static long q6venc_ioctl(struct file *file, u32 cmd, unsigned long arg) { + long ret = 0; void __user *argp = (void __user *)arg; struct venc_dev *dvenc = file->private_data; + if (!dvenc || !dvenc->is_active) + return -EPERM; + switch (cmd) { case VENC_IOCTL_SET_INPUT_BUFFER: - return venc_set_buffer(dvenc, argp, VENC_BUFFER_TYPE_INPUT); + ret = venc_set_buffer(dvenc, argp, VENC_BUFFER_TYPE_INPUT); + break; case VENC_IOCTL_SET_OUTPUT_BUFFER: - return venc_set_buffer(dvenc, argp, VENC_BUFFER_TYPE_OUTPUT); + ret = venc_set_buffer(dvenc, argp, VENC_BUFFER_TYPE_OUTPUT); + break; case VENC_IOCTL_GET_SEQUENCE_HDR: - return venc_get_sequence_hdr(dvenc, argp); + ret = venc_get_sequence_hdr(dvenc, argp); + break; case VENC_IOCTL_SET_QP_RANGE: - return venc_set_qp_range(dvenc, argp); + ret = venc_set_qp_range(dvenc, argp); + break; case VENC_IOCTL_SET_INTRA_PERIOD: - return venc_set_intra_period(dvenc, argp); + ret = venc_set_intra_period(dvenc, argp); + break; case VENC_IOCTL_SET_INTRA_REFRESH: - return venc_set_intra_refresh(dvenc, argp); + ret = venc_set_intra_refresh(dvenc, argp); + break; case VENC_IOCTL_SET_FRAME_RATE: - return venc_set_frame_rate(dvenc, argp); + ret = venc_set_frame_rate(dvenc, argp); + break; case VENC_IOCTL_SET_TARGET_BITRATE: - return venc_set_target_bitrate(dvenc, argp); + ret = venc_set_target_bitrate(dvenc, argp); + break; case VENC_IOCTL_CMD_REQUEST_IFRAME: if (dvenc->state == VENC_STATE_START) - return venc_request_iframe(dvenc); - else - return 0; + ret = venc_request_iframe(dvenc); + break; case VENC_IOCTL_CMD_START: - return venc_start(dvenc, argp); + ret = venc_start(dvenc, argp); + break; case VENC_IOCTL_CMD_STOP: - return venc_stop(dvenc); + ret = venc_stop(dvenc); + break; case VENC_IOCTL_CMD_PAUSE: - return venc_pause(dvenc); + ret = venc_pause(dvenc); + break; case VENC_IOCTL_CMD_RESUME: - return venc_resume(dvenc); + ret = venc_resume(dvenc); + break; case VENC_IOCTL_CMD_ENCODE_FRAME: - return venc_encode_frame(dvenc, argp); + ret = venc_encode_frame(dvenc, argp); + break; case VENC_IOCTL_CMD_FILL_OUTPUT_BUFFER: - return venc_fill_output(dvenc, argp); + ret = venc_fill_output(dvenc, argp); + break; case VENC_IOCTL_CMD_FLUSH: - return venc_flush(dvenc, argp); + ret = venc_flush(dvenc, argp); + break; case VENC_IOCTL_CMD_READ_NEXT_MSG: - return venc_read_next_msg(dvenc, argp); + wait_event_interruptible(dvenc->venc_msg_evt, + venc_get_msg(dvenc, argp)); + break; case VENC_IOCTL_CMD_STOP_READ_MSG: - return venc_stop_read_msg(dvenc); + ret = venc_stop_read_msg(dvenc); + break; + case VENC_IOCTL_GET_VERSION: + ret = venc_get_version(dvenc, argp); + break; default: pr_err("%s: invalid ioctl code (%d)\n", __func__, cmd); - return -EINVAL; + ret = -ENOTTY; + break; } + return ret; } static int q6venc_open(struct inode *inode, struct file *file) @@ -940,72 +1075,71 @@ static int q6venc_open(struct inode *inode, struct file *file) int i; int ret = 0; struct venc_dev *dvenc; - struct venc_qmsg *msg; -#if VERSION_CHECK + struct venc_msg_list *plist; struct dal_info version_info; -#endif dvenc = kzalloc(sizeof(struct venc_dev), GFP_KERNEL); - if (!dvenc) + if (!dvenc) { + pr_err("%s: unable to allocate memory for struct venc_dev\n", + __func__); return -ENOMEM; - + } file->private_data = dvenc; - INIT_LIST_HEAD(&dvenc->msg_pool); - INIT_LIST_HEAD(&dvenc->msg_queue); + INIT_LIST_HEAD(&dvenc->venc_msg_list_head); + INIT_LIST_HEAD(&dvenc->venc_msg_list_free); INIT_LIST_HEAD(&dvenc->venc_pmem_list_head); init_waitqueue_head(&dvenc->venc_msg_evt); - spin_lock_init(&dvenc->msg_lock); + spin_lock_init(&dvenc->venc_msg_list_lock); spin_lock_init(&dvenc->venc_pmem_list_lock); venc_ref++; - for (i = 0; i < VENC_MSG_MAX; i++) { - msg = kzalloc(sizeof(struct venc_qmsg), GFP_KERNEL); - if (msg == NULL) { + plist = kzalloc(sizeof(struct venc_msg_list), GFP_KERNEL); + if (!plist) { + pr_err("%s: kzalloc failed\n", __func__); ret = -ENOMEM; - goto fail_list_alloc; + goto err_venc_create_msg_list; } - venc_free_msg(dvenc, msg); + list_add(&plist->list, &dvenc->venc_msg_list_free); } - - dvenc->q6_handle = dal_attach(DALDEVICEID_VENC_DEVICE, - DALDEVICEID_VENC_PORTNAME, - venc_q6_callback, (void *)dvenc); - + dvenc->q6_handle = + dal_attach(DALDEVICEID_VENC_DEVICE, DALDEVICEID_VENC_PORTNAME, + venc_q6_callback, (void *)dvenc); if (!(dvenc->q6_handle)) { pr_err("%s: daldevice_attach failed (%d)\n", __func__, ret); - goto fail_list_alloc; + goto err_venc_dal_attach; } - -#if VERSION_CHECK ret = dal_call_f9(dvenc->q6_handle, DAL_OP_INFO, &version_info, sizeof(struct dal_info)); if (ret) { pr_err("%s: failed to get version\n", __func__); - ret = -EINVAL; - goto fail_open; + goto err_venc_dal_open; } + + pr_info("VENC_INTERFACE_VERSION %X, version_info.version %X\n", + VENC_INTERFACE_VERSION, version_info.version); +#if 0 if (venc_check_version(VENC_INTERFACE_VERSION, version_info.version)) { pr_err("%s: driver version mismatch\n", __func__); - ret = -EINVAL; - goto fail_open; + goto err_venc_dal_open; } #endif ret = dal_call_f0(dvenc->q6_handle, DAL_OP_OPEN, 1); if (ret) { pr_err("%s: dal_call_open failed (%d)\n", __func__, ret); - goto fail_open; + goto err_venc_dal_open; } dvenc->state = VENC_STATE_STOP; + dvenc->is_active = 1; prevent_sleep(); return ret; - -fail_open: +err_venc_dal_open: dal_detach(dvenc->q6_handle); - -fail_list_alloc: - while ((msg = venc_alloc_msg(dvenc))) - kfree(msg); - +err_venc_dal_attach: + list_for_each_entry(plist, &dvenc->venc_msg_list_free, list) { + list_del(&plist->list); + kfree(plist); + } +err_venc_create_msg_list: kfree(dvenc); venc_ref--; return ret; @@ -1014,30 +1148,35 @@ static int q6venc_open(struct inode *inode, struct file *file) static int q6venc_release(struct inode *inode, struct file *file) { int ret = 0; + struct venc_msg_list *l, *n; struct venc_pmem_list *plist, *m; struct venc_dev *dvenc; - struct venc_qmsg *msg; + unsigned long flags; venc_ref--; - dvenc = file->private_data; + dvenc->is_active = 0; wake_up_all(&dvenc->venc_msg_evt); - if (!dvenc->stop_called) - dal_call_f0(dvenc->q6_handle, VENC_DALRPC_STOP, 1); + dal_call_f0(dvenc->q6_handle, VENC_DALRPC_STOP, 1); dal_call_f0(dvenc->q6_handle, DAL_OP_CLOSE, 1); dal_detach(dvenc->q6_handle); - - - /* free all messages in the pool */ - while ((msg = venc_alloc_msg(dvenc))) - kfree(msg); - - /* free all messages sitting in the queue */ - while ((msg = venc_recv_msg(dvenc))) - kfree(msg); + list_for_each_entry_safe(l, n, &dvenc->venc_msg_list_free, list) { + list_del(&l->list); + kfree(l); + } + list_for_each_entry_safe(l, n, &dvenc->venc_msg_list_head, list) { + list_del(&l->list); + kfree(l); + } + spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags); + if (!dvenc->pmem_freed) { + list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) + put_pmem_file(plist->buf.file); + dvenc->pmem_freed = 1; + } + spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags); list_for_each_entry_safe(plist, m, &dvenc->venc_pmem_list_head, list) { - put_pmem_file(plist->buf.file); list_del(&plist->list); kfree(plist); } @@ -1057,15 +1196,15 @@ static int __init q6venc_init(void) { int ret = 0; + wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "venc_idle"); + wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "venc_suspend"); + venc_device_p = kzalloc(sizeof(struct venc_dev), GFP_KERNEL); if (!venc_device_p) { pr_err("%s: unable to allocate memory for venc_device_p\n", __func__); return -ENOMEM; } - wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "venc_idle"); - wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "venc_suspend"); - ret = alloc_chrdev_region(&venc_dev_num, 0, 1, VENC_NAME); if (ret < 0) { pr_err("%s: alloc_chrdev_region failed (%d)\n", __func__, @@ -1115,6 +1254,7 @@ static void __exit q6venc_exit(void) unregister_chrdev_region(venc_dev_num, 1); } +MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("Video encoder driver for QDSP6"); MODULE_VERSION("2.0"); module_init(q6venc_init); diff --git a/include/linux/msm_q6vdec.h b/include/linux/msm_q6vdec.h index 1dca80307033e..ed7cf78241927 100644 --- a/include/linux/msm_q6vdec.h +++ b/include/linux/msm_q6vdec.h @@ -45,6 +45,7 @@ #define VDEC_IOCTL_FREEBUFFERS _IOW(VDEC_IOCTL_MAGIC, 9, struct vdec_buf_info) #define VDEC_IOCTL_GETDECATTRIBUTES _IOR(VDEC_IOCTL_MAGIC, 10, \ struct vdec_dec_attributes) +#define VDEC_IOCTL_GETVERSION _IOR(VDEC_IOCTL_MAGIC, 11, struct vdec_version) enum { VDEC_FRAME_DECODE_OK, @@ -77,6 +78,11 @@ enum { VDEC_QUEUE_BADSTATE, }; +enum { + VDEC_COLOR_FORMAT_NV21 = 0x01, + VDEC_COLOR_FORMAT_NV21_YAMOTO = 0x02 + }; + struct vdec_input_buf_info { u32 offset; u32 data; @@ -118,7 +124,7 @@ struct vdec_config { u32 h264_nal_len_size; u32 postproc_flag; u32 fruc_enable; - u32 reserved; + u32 color_format; /* used to set YUV color format */ }; struct vdec_vc1_panscan_regions { @@ -161,7 +167,7 @@ struct vdec_frame_info { u32 concealed_macblk_num; /* number of concealed macro blk */ u32 flags; /* input flags */ u32 performance_stats; /* performance statistics returned by decoder */ - u32 data3; /* user data field 3 */ + u32 data3; /* user data field 3 */ }; struct vdec_buf_info { @@ -227,4 +233,9 @@ struct vdec_dec_attributes { struct vdec_buf_desc dec_req2; }; +struct vdec_version { + u32 major; + u32 minor; +}; + #endif /* _MSM_VDEC_H_ */ diff --git a/include/linux/msm_q6venc.h b/include/linux/msm_q6venc.h index db31332fecbea..097e2db869c36 100755 --- a/include/linux/msm_q6venc.h +++ b/include/linux/msm_q6venc.h @@ -214,6 +214,7 @@ struct venc_buffer { long long time_stamp; unsigned int flags; unsigned int client_data; + }; struct venc_buffers { @@ -230,6 +231,7 @@ struct venc_buffer_flush { union venc_msg_data { struct venc_buffer buf; struct venc_buffer_flush flush_ret; + }; struct venc_msg { @@ -268,6 +270,11 @@ struct venc_seq_config { struct venc_q6_config q6_config; }; +struct venc_version { + u32 major; + u32 minor; +}; + #define VENC_IOCTL_MAGIC 'V' #define VENC_IOCTL_CMD_READ_NEXT_MSG \ @@ -318,4 +325,7 @@ struct venc_seq_config { #define VENC_IOCTL_SET_QP_RANGE \ _IOW(VENC_IOCTL_MAGIC, 18, struct venc_qp_range) +#define VENC_IOCTL_GET_VERSION \ + _IOR(VENC_IOCTL_MAGIC, 19, struct venc_version) + #endif From 16dba22ad04f094e089c7cc527093d0b704f1e64 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Wed, 9 Feb 2011 09:38:26 -0500 Subject: [PATCH 0985/2556] USB: gadget: android: Support switching vendor ID when configuration changes Based on the list of enabled USB functions, we can now switch the vendor ID as well as the product ID. Signed-off-by: Mike Lockwood --- drivers/usb/gadget/android.c | 32 +++++++++++++++++++++------ include/linux/usb/android_composite.h | 7 +++++- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index 3d3fc795c4c72..e49ef2c470d66 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -65,6 +65,7 @@ struct android_dev { int num_functions; char **functions; + int vendor_id; int product_id; int version; }; @@ -254,6 +255,22 @@ static int product_matches_functions(struct android_usb_product *p) return 1; } +static int get_vendor_id(struct android_dev *dev) +{ + struct android_usb_product *p = dev->products; + int count = dev->num_products; + int i; + + if (p) { + for (i = 0; i < count; i++, p++) { + if (p->vendor_id && product_matches_functions(p)) + return p->vendor_id; + } + } + /* use default vendor ID */ + return dev->vendor_id; +} + static int get_product_id(struct android_dev *dev) { struct android_usb_product *p = dev->products; @@ -274,7 +291,7 @@ static int android_bind(struct usb_composite_dev *cdev) { struct android_dev *dev = _android_dev; struct usb_gadget *gadget = cdev->gadget; - int gcnum, id, product_id, ret; + int gcnum, id, ret; printk(KERN_INFO "android_bind\n"); @@ -324,8 +341,8 @@ static int android_bind(struct usb_composite_dev *cdev) usb_gadget_set_selfpowered(gadget); dev->cdev = cdev; - product_id = get_product_id(dev); - device_desc.idProduct = __constant_cpu_to_le16(product_id); + device_desc.idVendor = __constant_cpu_to_le16(get_vendor_id(dev)); + device_desc.idProduct = __constant_cpu_to_le16(get_product_id(dev)); cdev->desc.idProduct = device_desc.idProduct; return 0; @@ -394,7 +411,6 @@ void android_enable_function(struct usb_function *f, int enable) { struct android_dev *dev = _android_dev; int disable = !enable; - int product_id; if (!!f->disabled != disable) { usb_function_set_enabled(f, !disable); @@ -416,8 +432,8 @@ void android_enable_function(struct usb_function *f, int enable) update_dev_desc(dev); - product_id = get_product_id(dev); - device_desc.idProduct = __constant_cpu_to_le16(product_id); + device_desc.idVendor = __constant_cpu_to_le16(get_vendor_id(dev)); + device_desc.idProduct = __constant_cpu_to_le16(get_product_id(dev)); if (dev->cdev) dev->cdev->desc.idProduct = device_desc.idProduct; usb_composite_force_reset(dev->cdev); @@ -436,9 +452,11 @@ static int android_probe(struct platform_device *pdev) dev->num_products = pdata->num_products; dev->functions = pdata->functions; dev->num_functions = pdata->num_functions; - if (pdata->vendor_id) + if (pdata->vendor_id) { + dev->vendor_id = pdata->vendor_id; device_desc.idVendor = __constant_cpu_to_le16(pdata->vendor_id); + } if (pdata->product_id) { dev->product_id = pdata->product_id; device_desc.idProduct = diff --git a/include/linux/usb/android_composite.h b/include/linux/usb/android_composite.h index 62e72e3bd2b6e..7f9000711f4e3 100644 --- a/include/linux/usb/android_composite.h +++ b/include/linux/usb/android_composite.h @@ -27,7 +27,12 @@ struct android_usb_function { }; struct android_usb_product { - /* Default product ID. */ + /* Vendor ID for this set of functions. + * Default vendor_id in platform data will be used if this is zero. + */ + __u16 vendor_id; + + /* Product ID for this set of functions. */ __u16 product_id; /* List of function names associated with this product. From cedf3e907c4ba7fac23a0ddcf2802d69b1d7569c Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Thu, 10 Feb 2011 11:54:53 -0500 Subject: [PATCH 0986/2556] USB: gadget: f_mtp: Don't block in mtp_send_event We used to wait for the previous interrupt packet to complete before sending the next packet. But unfortunately the previous packet will not complete until USB is disconnected if the host is not listening on the interrupt endpoint (which is the case with libmtp on Linux and Mac). To avoid hanging indefinitely in this case, we now simply return -EBUSY if the previous interrupt packet has not completed yet. Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_mtp.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index d560fbcf4f58b..8128b203e76f4 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -92,7 +92,6 @@ struct mtp_dev { wait_queue_head_t read_wq; wait_queue_head_t write_wq; - wait_queue_head_t intr_wq; struct usb_request *rx_req[RX_REQ_MAX]; struct usb_request *intr_req; int rx_done; @@ -373,12 +372,11 @@ static void mtp_complete_intr(struct usb_ep *ep, struct usb_request *req) { struct mtp_dev *dev = _mtp_dev; - DBG(dev->cdev, "mtp_complete_intr status: %d actual: %d\n", req->status, req->actual); + DBG(dev->cdev, "mtp_complete_intr status: %d actual: %d\n", + req->status, req->actual); dev->intr_busy = 0; if (req->status != 0) dev->state = STATE_ERROR; - - wake_up(&dev->intr_wq); } static int __init create_bulk_endpoints(struct mtp_dev *dev, @@ -798,13 +796,15 @@ static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event) if (length < 0 || length > INTR_BUFFER_SIZE) return -EINVAL; - - /* wait for a request to complete */ - ret = wait_event_interruptible(dev->intr_wq, !dev->intr_busy || dev->state == STATE_OFFLINE); - if (ret < 0) - return ret; if (dev->state == STATE_OFFLINE) return -ENODEV; + /* unfortunately an interrupt request might hang indefinitely if the host + * is not listening on the interrupt endpoint, so instead of waiting, + * we just fail if the endpoint is busy. + */ + if (dev->intr_busy) + return -EBUSY; + req = dev->intr_req; if (copy_from_user(req->buf, (void __user *)event->data, length)) return -EFAULT; @@ -1016,7 +1016,6 @@ mtp_function_unbind(struct usb_configuration *c, struct usb_function *f) mtp_request_free(dev->intr_req, dev->ep_intr); dev->state = STATE_OFFLINE; spin_unlock_irq(&dev->lock); - wake_up(&dev->intr_wq); misc_deregister(&mtp_device); kfree(_mtp_dev); @@ -1180,7 +1179,6 @@ static void mtp_function_disable(struct usb_function *f) /* readers may be blocked waiting for us to go online */ wake_up(&dev->read_wq); - wake_up(&dev->intr_wq); VDBG(cdev, "%s disabled\n", dev->function.name); } @@ -1208,7 +1206,6 @@ static int mtp_bind_config(struct usb_configuration *c) spin_lock_init(&dev->lock); init_waitqueue_head(&dev->read_wq); init_waitqueue_head(&dev->write_wq); - init_waitqueue_head(&dev->intr_wq); atomic_set(&dev->open_excl, 0); atomic_set(&dev->ioctl_excl, 0); INIT_LIST_HEAD(&dev->tx_idle); From 048d6886301bab1928cd16f12629d24e6c20cc51 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 11 Feb 2011 16:54:39 -0800 Subject: [PATCH 0987/2556] net: wireless: bcm4329: Add sdlock to firmware loading Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/dhd_linux.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index f14f5e81b3213..72b5fda183a64 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -2186,12 +2186,15 @@ dhd_bus_start(dhd_pub_t *dhdp) DHD_TRACE(("%s: \n", __FUNCTION__)); + dhd_os_sdlock(dhdp); + /* try to download image and nvram to the dongle */ if (dhd->pub.busstate == DHD_BUS_DOWN) { if (!(dhd_bus_download_firmware(dhd->pub.bus, dhd->pub.osh, fw_path, nv_path))) { DHD_ERROR(("%s: dhdsdio_probe_download failed. firmware = %s nvram = %s\n", __FUNCTION__, fw_path, nv_path)); + dhd_os_sdunlock(dhdp); return -1; } } @@ -2201,8 +2204,9 @@ dhd_bus_start(dhd_pub_t *dhdp) dhd_os_wd_timer(&dhd->pub, dhd_watchdog_ms); /* Bring up the bus */ - if ((ret = dhd_bus_init(&dhd->pub, TRUE)) != 0) { + if ((ret = dhd_bus_init(&dhd->pub, FALSE)) != 0) { DHD_ERROR(("%s, dhd_bus_init failed %d\n", __FUNCTION__, ret)); + dhd_os_sdunlock(dhdp); return ret; } #if defined(OOB_INTR_ONLY) @@ -2211,6 +2215,7 @@ dhd_bus_start(dhd_pub_t *dhdp) dhd->wd_timer_valid = FALSE; del_timer_sync(&dhd->timer); DHD_ERROR(("%s Host failed to resgister for OOB\n", __FUNCTION__)); + dhd_os_sdunlock(dhdp); return -ENODEV; } @@ -2223,9 +2228,12 @@ dhd_bus_start(dhd_pub_t *dhdp) dhd->wd_timer_valid = FALSE; del_timer_sync(&dhd->timer); DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__)); + dhd_os_sdunlock(dhdp); return -ENODEV; } + dhd_os_sdunlock(dhdp); + #ifdef EMBEDDED_PLATFORM bcm_mkiovar("event_msgs", dhdp->eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); dhdcdc_query_ioctl(dhdp, 0, WLC_GET_VAR, iovbuf, sizeof(iovbuf)); From 9b929ad70abc4ea5aa3244433331ff68b46fce70 Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Fri, 11 Feb 2011 17:01:28 -0800 Subject: [PATCH 0988/2556] net: wireless: bcm4329: Fix memleak in dev->p Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/bcmsdh_linux.c | 12 +++++++----- drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c | 10 +++------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/bcm4329/bcmsdh_linux.c b/drivers/net/wireless/bcm4329/bcmsdh_linux.c index 3b7da4263fd8e..1e33555b0531e 100644 --- a/drivers/net/wireless/bcm4329/bcmsdh_linux.c +++ b/drivers/net/wireless/bcm4329/bcmsdh_linux.c @@ -301,7 +301,7 @@ int bcmsdh_remove(struct device *dev) MFREE(osh, sdhc, sizeof(bcmsdh_hc_t)); osl_detach(osh); -#if !defined(BCMLXSDMMC) +#if !defined(BCMLXSDMMC) || defined(OOB_INTR_ONLY) dev_set_drvdata(dev, NULL); #endif /* !defined(BCMLXSDMMC) */ @@ -647,10 +647,12 @@ void bcmsdh_unregister_oob_intr(void) { SDLX_MSG(("%s: Enter\n", __FUNCTION__)); - set_irq_wake(sdhcinfo->oob_irq, 0); - disable_irq(sdhcinfo->oob_irq); /* just in case.. */ - free_irq(sdhcinfo->oob_irq, NULL); - sdhcinfo->oob_irq_registered = FALSE; + if (sdhcinfo->oob_irq_registered) { + set_irq_wake(sdhcinfo->oob_irq, 0); + disable_irq(sdhcinfo->oob_irq); /* just in case.. */ + free_irq(sdhcinfo->oob_irq, NULL); + sdhcinfo->oob_irq_registered = FALSE; + } } #endif /* defined(OOB_INTR_ONLY) */ /* Module parameters specific to each host-controller driver */ diff --git a/drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c index 8992a4267f9f7..5a1a46c93571c 100644 --- a/drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c +++ b/drivers/net/wireless/bcm4329/bcmsdh_sdmmc_linux.c @@ -82,7 +82,6 @@ PBCMSDH_SDMMC_INSTANCE gInstance; extern int bcmsdh_probe(struct device *dev); extern int bcmsdh_remove(struct device *dev); -struct device sdmmc_dev; static int bcmsdh_sdmmc_probe(struct sdio_func *func, const struct sdio_device_id *id) @@ -102,7 +101,7 @@ static int bcmsdh_sdmmc_probe(struct sdio_func *func, if(func->device == 0x4) { /* 4318 */ gInstance->func[2] = NULL; sd_trace(("NIC found, calling bcmsdh_probe...\n")); - ret = bcmsdh_probe(&sdmmc_dev); + ret = bcmsdh_probe(&func->dev); } } @@ -110,7 +109,7 @@ static int bcmsdh_sdmmc_probe(struct sdio_func *func, if (func->num == 2) { sd_trace(("F2 found, calling bcmsdh_probe...\n")); - ret = bcmsdh_probe(&sdmmc_dev); + ret = bcmsdh_probe(&func->dev); } return ret; @@ -126,7 +125,7 @@ static void bcmsdh_sdmmc_remove(struct sdio_func *func) if (func->num == 2) { sd_trace(("F2 found, calling bcmsdh_remove...\n")); - bcmsdh_remove(&sdmmc_dev); + bcmsdh_remove(&func->dev); } } @@ -250,10 +249,8 @@ int sdio_function_init(void) if (!gInstance) return -ENOMEM; - bzero(&sdmmc_dev, sizeof(sdmmc_dev)); error = sdio_register_driver(&bcmsdh_sdmmc_driver); - return error; } @@ -265,7 +262,6 @@ void sdio_function_cleanup(void) { sd_trace(("%s Enter\n", __FUNCTION__)); - sdio_unregister_driver(&bcmsdh_sdmmc_driver); if (gInstance) From 22504241171df09696144401a38baff9ad8ffff4 Mon Sep 17 00:00:00 2001 From: Mike Corrigan Date: Fri, 11 Feb 2011 17:05:54 -0800 Subject: [PATCH 0989/2556] net: wireless: bcm4329: Fix mem leak in wl_iw_handle_scanresults_ies Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/wl_iw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/bcm4329/wl_iw.c b/drivers/net/wireless/bcm4329/wl_iw.c index 8460804c945ad..00222196f6e4c 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.c +++ b/drivers/net/wireless/bcm4329/wl_iw.c @@ -3686,6 +3686,7 @@ wl_iw_handle_scanresults_ies(char **event_p, char *end, wpa_snprintf_hex(buf + 10, 2+1, &(ie->len), 1); wpa_snprintf_hex(buf + 12, 2*ie->len+1, ie->data, ie->len); event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, buf); + kfree(buf); #endif break; } From bb18dfc1099fc5210152f6d595b2ab1d0dfb564d Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Sat, 12 Feb 2011 13:21:32 -0800 Subject: [PATCH 0990/2556] Revert "mmc: subtract boot sectors from disk size for eMMC 4.3+ devices" This reverts commit f0b0e4bec1e89014f3dcef4da8bcf95428cc771c. The reverted commit incorrectly calculates the size of eMMC devices in some (all?) cases. This revert may cause problems in cases where the bootloader was bug-compatible and puts a GPT partition at the incorrect end of the eMMC device. Change-Id: Ib006acf9e517b3b8f7570220c28e19c91e7b5f25 Signed-off-by: Colin Cross --- drivers/mmc/core/mmc.c | 7 +------ include/linux/mmc/mmc.h | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 83ceeec404f2e..16006ef153fe0 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -253,13 +253,8 @@ static int mmc_read_ext_csd(struct mmc_card *card) ext_csd[EXT_CSD_SEC_CNT + 3] << 24; /* Cards with density > 2GiB are sector addressed */ - if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512) { - unsigned boot_sectors; - /* size is in 256K chunks, i.e. 512 sectors each */ - boot_sectors = ext_csd[EXT_CSD_BOOT_SIZE_MULTI] * 512; - card->ext_csd.sectors -= boot_sectors; + if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512) mmc_card_set_blockaddr(card); - } } switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) { diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 0a99c8936a055..612301f85d144 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -268,7 +268,6 @@ struct _mmc_csd { #define EXT_CSD_SEC_ERASE_MULT 230 /* RO */ #define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */ #define EXT_CSD_TRIM_MULT 232 /* RO */ -#define EXT_CSD_BOOT_SIZE_MULTI 226 /* RO */ /* * EXT_CSD field definitions From 10149a342a78e2d499dc1a4759480836e2d1ad44 Mon Sep 17 00:00:00 2001 From: Greg Goldman Date: Mon, 14 Feb 2011 15:54:42 -0800 Subject: [PATCH 0991/2556] net: wireless: bcm4329: Update to version 4.218.248-23 - Add support to PNO with adaptive scan time - Remove logic to generate Link Down based on Roaming Event - Improve sco/esco detection method for BT COEX - Add new function TXPOWER to reduce power if needed Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/Makefile | 2 +- drivers/net/wireless/bcm4329/dhd.h | 18 +- drivers/net/wireless/bcm4329/dhd_common.c | 49 ++- .../net/wireless/bcm4329/dhd_custom_gpio.c | 49 ++- drivers/net/wireless/bcm4329/dhd_linux.c | 25 +- .../net/wireless/bcm4329/include/epivers.h | 10 +- .../net/wireless/bcm4329/include/wlioctl.h | 23 +- drivers/net/wireless/bcm4329/wl_iw.c | 295 ++++++++++++------ drivers/net/wireless/bcm4329/wl_iw.h | 56 +++- 9 files changed, 384 insertions(+), 143 deletions(-) diff --git a/drivers/net/wireless/bcm4329/Makefile b/drivers/net/wireless/bcm4329/Makefile index 3f49a643e8ff3..dd0452d129eed 100644 --- a/drivers/net/wireless/bcm4329/Makefile +++ b/drivers/net/wireless/bcm4329/Makefile @@ -7,7 +7,7 @@ DHDCFLAGS = -DLINUX -DBCMDRIVER -DBCMDONGLEHOST -DDHDTHREAD -DBCMWPA2 \ -DDHD_USE_STATIC_BUF -DMMC_SDIO_ABORT -DDHD_DEBUG_TRAP -DSOFTAP \ -DEMBEDDED_PLATFORM -DARP_OFFLOAD_SUPPORT -DPKT_FILTER_SUPPORT \ -DGET_CUSTOM_MAC_ENABLE -DSET_RANDOM_MAC_SOFTAP -DCSCAN -DHW_OOB \ - -DKEEP_ALIVE \ + -DKEEP_ALIVE -DPNO_SUPPORT \ -Idrivers/net/wireless/bcm4329 -Idrivers/net/wireless/bcm4329/include DHDOFILES = dhd_linux.o linux_osl.o bcmutils.o dhd_common.o dhd_custom_gpio.o \ diff --git a/drivers/net/wireless/bcm4329/dhd.h b/drivers/net/wireless/bcm4329/dhd.h index 1ddf1ff61e705..6c2998806e4d7 100644 --- a/drivers/net/wireless/bcm4329/dhd.h +++ b/drivers/net/wireless/bcm4329/dhd.h @@ -24,7 +24,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd.h,v 1.32.4.7.2.4.14.49.4.7 2010/11/12 22:48:36 Exp $ + * $Id: dhd.h,v 1.32.4.7.2.4.14.49.4.9 2011/01/14 22:40:45 Exp $ */ /**************** @@ -164,7 +164,7 @@ typedef struct dhd_pub { char * pktfilter[100]; int pktfilter_count; - uint8 country_code[WLC_CNTRY_BUF_SZ]; + wl_country_t dhd_cspec; /* Current Locale info */ char eventmask[WL_EVENTING_MASK_LEN]; } dhd_pub_t; @@ -214,6 +214,20 @@ typedef struct dhd_pub { #define DHD_IF_VIF 0x01 /* Virtual IF (Hidden from user) */ +inline static void NETIF_ADDR_LOCK(struct net_device *dev) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)) + netif_addr_lock_bh(dev); +#endif +} + +inline static void NETIF_ADDR_UNLOCK(struct net_device *dev) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)) + netif_addr_unlock_bh(dev); +#endif +} + /* Wakelock Functions */ extern int dhd_os_wake_lock(dhd_pub_t *pub); extern int dhd_os_wake_unlock(dhd_pub_t *pub); diff --git a/drivers/net/wireless/bcm4329/dhd_common.c b/drivers/net/wireless/bcm4329/dhd_common.c index 4331d39a68f11..9a345fb430760 100644 --- a/drivers/net/wireless/bcm4329/dhd_common.c +++ b/drivers/net/wireless/bcm4329/dhd_common.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_common.c,v 1.5.6.8.2.6.6.69.4.20 2010/12/20 23:37:28 Exp $ + * $Id: dhd_common.c,v 1.5.6.8.2.6.6.69.4.25 2011/02/11 21:16:02 Exp $ */ #include #include @@ -1287,9 +1287,10 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif /* SET_RANDOM_MAC_SOFTAP */ /* Set Country code */ - if (dhd->country_code[0] != 0) { - if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_COUNTRY, - dhd->country_code, sizeof(dhd->country_code)) < 0) { + if (dhd->dhd_cspec.ccode[0] != 0) { + bcm_mkiovar("country", (char *)&dhd->dhd_cspec, \ + sizeof(wl_country_t), iovbuf, sizeof(iovbuf)); + if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf))) < 0) { DHD_ERROR(("%s: country code setting failed\n", __FUNCTION__)); } } @@ -1896,6 +1897,7 @@ int dhd_pno_clean(dhd_pub_t *dhd) int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled) { char iovbuf[128]; + uint8 bssid[6]; int ret = -1; if ((!dhd) && ((pfn_enabled != 0) || (pfn_enabled != 1))) { @@ -1903,6 +1905,20 @@ int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled) return ret; } + memset(iovbuf, 0, sizeof(iovbuf)); + + /* Check if disassoc to enable pno */ + if ((pfn_enabled) && \ + ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_GET_BSSID, \ + (char *)&bssid, ETHER_ADDR_LEN)) == BCME_NOTASSOCIATED)) { + DHD_TRACE(("%s pno enable called in disassoc mode\n", __FUNCTION__)); + } + else { + DHD_ERROR(("%s pno enable called in assoc mode ret=%d\n", \ + __FUNCTION__, ret)); + return ret; + } + /* Enable/disable PNO */ if ((ret = bcm_mkiovar("pfn", (char *)&pfn_enabled, 4, iovbuf, sizeof(iovbuf))) > 0) { if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf))) < 0) { @@ -1921,7 +1937,8 @@ int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled) /* Function to execute combined scan */ int -dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr) +dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr, \ + int pno_repeat, int pno_freq_expo_max) { int err = -1; char iovbuf[128]; @@ -1966,12 +1983,23 @@ dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr) pfn_param.version = htod32(PFN_VERSION); pfn_param.flags = htod16((PFN_LIST_ORDER << SORT_CRITERIA_BIT)); + /* check and set extra pno params */ + if ((pno_repeat != 0) || (pno_freq_expo_max != 0)) { + pfn_param.flags |= htod16(ENABLE << ENABLE_ADAPTSCAN_BIT); + pfn_param.repeat_scan = htod32(pno_repeat); + pfn_param.max_freq_adjust = htod32(pno_freq_expo_max); + } + /* set up pno scan fr */ if (scan_fr != 0) pfn_param.scan_freq = htod32(scan_fr); - if (pfn_param.scan_freq > PNO_SCAN_MAX_FW) { - DHD_ERROR(("%s pno freq above %d sec\n", __FUNCTION__, PNO_SCAN_MAX_FW)); + if (pfn_param.scan_freq > PNO_SCAN_MAX_FW_SEC) { + DHD_ERROR(("%s pno freq above %d sec\n", __FUNCTION__, PNO_SCAN_MAX_FW_SEC)); + return err; + } + if (pfn_param.scan_freq < PNO_SCAN_MIN_FW_SEC) { + DHD_ERROR(("%s pno freq less %d sec\n", __FUNCTION__, PNO_SCAN_MIN_FW_SEC)); return err; } @@ -1983,8 +2011,6 @@ dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr) pfn_element.bss_type = htod32(DOT11_BSSTYPE_INFRASTRUCTURE); pfn_element.auth = (DOT11_OPEN_SYSTEM); - pfn_element.wpa_auth = htod32(WPA_AUTH_PFN_ANY); - pfn_element.wsec = htod32(0); pfn_element.infra = htod32(1); memcpy((char *)pfn_element.ssid.SSID, ssids_local[i].SSID, ssids_local[i].SSID_len); @@ -2000,8 +2026,9 @@ dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr) return err; } else - DHD_ERROR(("%s set OK with PNO time=%d\n", __FUNCTION__, \ - pfn_param.scan_freq)); + DHD_ERROR(("%s set OK with PNO time=%d repeat=%d max_adjust=%d\n", \ + __FUNCTION__, pfn_param.scan_freq, \ + pfn_param.repeat_scan, pfn_param.max_freq_adjust)); } else DHD_ERROR(("%s failed err=%d\n", __FUNCTION__, err)); } diff --git a/drivers/net/wireless/bcm4329/dhd_custom_gpio.c b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c index 8c6ec470b8bd2..4739b97b48bcf 100644 --- a/drivers/net/wireless/bcm4329/dhd_custom_gpio.c +++ b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c @@ -20,7 +20,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * -* $Id: dhd_custom_gpio.c,v 1.1.4.8.4.1 2010/09/02 23:13:16 Exp $ +* $Id: dhd_custom_gpio.c,v 1.1.4.8.4.4 2011/01/20 20:23:09 Exp $ */ @@ -177,3 +177,50 @@ dhd_custom_get_mac_address(unsigned char *buf) return ret; } #endif /* GET_CUSTOM_MAC_ENABLE */ + +#define EXAMPLE_TABLE +/* Customized Locale table : OPTIONAL feature */ +const struct cntry_locales_custom translate_custom_table[] = { +/* Table should be filled out based on custom platform regulatory requirement */ +#ifdef EXAMPLE_TABLE + {"US", "US", 69}, /* input ISO "US" to : US regrev 69 */ + {"CA", "US", 69}, /* input ISO "CA" to : US regrev 69 */ + {"EU", "EU", 05}, /* input ISO "EU" to : EU regrev 05 */ + {"FR", "EU", 05}, + {"DE", "EU", 05}, + {"IR", "EU", 05}, + {"UK", "EU", 05}, /* input ISO "UK" to : EU regrev 05 */ + {"KR", "XY", 03}, + {"AU", "XY", 03}, + {"CN", "XY", 03}, /* input ISO "CN" to : XY regrev 03 */ + {"TW", "XY", 03}, + {"AR", "XY", 03} +#endif /* EXAMPLE_TABLE */ +}; + + +/* Customized Locale convertor +* input : ISO 3166-1 country abbreviation +* output: customized cspec +*/ +void get_customized_country_code(char *country_iso_code, wl_country_t *cspec) +{ + int size, i; + + size = ARRAYSIZE(translate_custom_table); + + if (cspec == 0) + return; + + if (size == 0) + return; + + for (i = 0; i < size; i++) { + if (strcmp(country_iso_code, translate_custom_table[i].iso_abbrev) == 0) { + memcpy(cspec->ccode, translate_custom_table[i].custom_locale, WLC_CNTRY_BUF_SZ); + cspec->rev = translate_custom_table[i].custom_locale_rev; + break; + } + } + return; +} diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index 72b5fda183a64..26cf90c6a4470 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_linux.c,v 1.65.4.9.2.12.2.104.4.35 2010/11/17 03:13:21 Exp $ + * $Id: dhd_linux.c,v 1.65.4.9.2.12.2.104.4.40 2011/02/03 19:55:18 Exp $ */ #ifdef CONFIG_WIFI_CONTROL_FUNC @@ -764,13 +764,13 @@ _dhd_set_multicast_list(dhd_info_t *dhd, int ifidx) ASSERT(dhd && dhd->iflist[ifidx]); dev = dhd->iflist[ifidx]->net; - netif_addr_lock_bh(dev); + NETIF_ADDR_LOCK(dev); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) cnt = netdev_mc_count(dev); #else cnt = dev->mc_count; #endif - netif_addr_unlock_bh(dev); + NETIF_ADDR_UNLOCK(dev); /* Determine initial value of allmulti flag */ allmulti = (dev->flags & IFF_ALLMULTI) ? TRUE : FALSE; @@ -790,7 +790,7 @@ _dhd_set_multicast_list(dhd_info_t *dhd, int ifidx) memcpy(bufp, &cnt, sizeof(cnt)); bufp += sizeof(cnt); - netif_addr_lock_bh(dev); + NETIF_ADDR_LOCK(dev); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) netdev_for_each_mc_addr(ha, dev) { if (!cnt) @@ -800,12 +800,12 @@ _dhd_set_multicast_list(dhd_info_t *dhd, int ifidx) cnt--; } #else - for (mclist = dev->mc_list;(mclist && (cnt > 0)); cnt--, mclist = mclist->next) { + for (mclist = dev->mc_list; (mclist && (cnt > 0)); cnt--, mclist = mclist->next) { memcpy(bufp, (void *)mclist->dmi_addr, ETHER_ADDR_LEN); bufp += ETHER_ADDR_LEN; } #endif - netif_addr_unlock_bh(dev); + NETIF_ADDR_UNLOCK(dev); memset(&ioc, 0, sizeof(ioc)); ioc.cmd = WLC_SET_VAR; @@ -3046,11 +3046,12 @@ dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled) /* Linux wrapper to call common dhd_pno_set */ int -dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr) +dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, + ushort scan_fr, int pno_repeat, int pno_freq_expo_max) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); - return (dhd_pno_set(&dhd->pub, ssids_local, nssid, scan_fr)); + return (dhd_pno_set(&dhd->pub, ssids_local, nssid, scan_fr, pno_repeat, pno_freq_expo_max)); } /* Linux wrapper to get pno status */ @@ -3078,20 +3079,20 @@ int net_os_send_hang_message(struct net_device *dev) return ret; } -void dhd_bus_country_set(struct net_device *dev, char *country_code) +void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); if (dhd && dhd->pub.up) - strncpy(dhd->pub.country_code, country_code, WLC_CNTRY_BUF_SZ); + memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); } char *dhd_bus_country_get(struct net_device *dev) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); - if (dhd && (dhd->pub.country_code[0] != 0)) - return dhd->pub.country_code; + if (dhd && (dhd->pub.dhd_cspec.ccode[0] != 0)) + return dhd->pub.dhd_cspec.ccode; return NULL; } diff --git a/drivers/net/wireless/bcm4329/include/epivers.h b/drivers/net/wireless/bcm4329/include/epivers.h index 00e3cac14dc5d..cd66a9501cb61 100644 --- a/drivers/net/wireless/bcm4329/include/epivers.h +++ b/drivers/net/wireless/bcm4329/include/epivers.h @@ -33,16 +33,16 @@ #define EPI_RC_NUMBER 248 -#define EPI_INCREMENTAL_NUMBER 20 +#define EPI_INCREMENTAL_NUMBER 23 #define EPI_BUILD_NUMBER 0 -#define EPI_VERSION 4, 218, 248, 20 +#define EPI_VERSION 4, 218, 248, 23 -#define EPI_VERSION_NUM 0x04daf814 +#define EPI_VERSION_NUM 0x04daf817 -#define EPI_VERSION_STR "4.218.248.20" -#define EPI_ROUTER_VERSION_STR "4.219.248.20" +#define EPI_VERSION_STR "4.218.248.23" +#define EPI_ROUTER_VERSION_STR "4.219.248.23" #endif diff --git a/drivers/net/wireless/bcm4329/include/wlioctl.h b/drivers/net/wireless/bcm4329/include/wlioctl.h index cd7725a70db41..94518205d3d8a 100644 --- a/drivers/net/wireless/bcm4329/include/wlioctl.h +++ b/drivers/net/wireless/bcm4329/include/wlioctl.h @@ -24,7 +24,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wlioctl.h,v 1.601.4.15.2.14.2.62.4.1 2010/11/17 03:09:28 Exp $ + * $Id: wlioctl.h,v 1.601.4.15.2.14.2.62.4.3 2011/02/09 23:31:02 Exp $ */ @@ -254,6 +254,11 @@ typedef struct wl_join_params { #define WLC_CNTRY_BUF_SZ 4 +typedef struct wl_country { + char country_abbrev[WLC_CNTRY_BUF_SZ]; + int32 rev; + char ccode[WLC_CNTRY_BUF_SZ]; +} wl_country_t; typedef enum sup_auth_status { @@ -1309,12 +1314,16 @@ enum { #define ENABLE_BKGRD_SCAN_BIT 2 #define IMMEDIATE_SCAN_BIT 3 #define AUTO_CONNECT_BIT 4 +#define ENABLE_BD_SCAN_BIT 5 +#define ENABLE_ADAPTSCAN_BIT 6 #define SORT_CRITERIA_MASK 0x01 #define AUTO_NET_SWITCH_MASK 0x02 #define ENABLE_BKGRD_SCAN_MASK 0x04 #define IMMEDIATE_SCAN_MASK 0x08 #define AUTO_CONNECT_MASK 0x10 +#define ENABLE_BD_SCAN_MASK 0x20 +#define ENABLE_ADAPTSCAN_MASK 0x40 #define PFN_VERSION 1 @@ -1327,6 +1336,8 @@ typedef struct wl_pfn_param { int32 lost_network_timeout; int16 flags; int16 rssi_margin; + int32 repeat_scan; + int32 max_freq_adjust; } wl_pfn_param_t; typedef struct wl_pfn { @@ -1336,14 +1347,12 @@ typedef struct wl_pfn { int32 auth; uint32 wpa_auth; int32 wsec; -#ifdef WLPFN_AUTO_CONNECT - union { - wl_wsec_key_t sec_key; - wsec_pmk_t wpa_sec_key; - } pfn_security; -#endif } wl_pfn_t; +#define PNO_SCAN_MAX_FW 508*1000 +#define PNO_SCAN_MAX_FW_SEC PNO_SCAN_MAX_FW/1000 +#define PNO_SCAN_MIN_FW_SEC 10 + #define TOE_TX_CSUM_OL 0x00000001 #define TOE_RX_CSUM_OL 0x00000002 diff --git a/drivers/net/wireless/bcm4329/wl_iw.c b/drivers/net/wireless/bcm4329/wl_iw.c index 00222196f6e4c..230619d901ec1 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.c +++ b/drivers/net/wireless/bcm4329/wl_iw.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wl_iw.c,v 1.51.4.9.2.6.4.142.4.69 2010/12/21 03:00:08 Exp $ + * $Id: wl_iw.c,v 1.51.4.9.2.6.4.142.4.78 2011/02/11 21:27:52 Exp $ */ @@ -54,6 +54,7 @@ typedef const struct si_pub si_t; #define WL_INFORM(x) #define WL_WSEC(x) #define WL_SCAN(x) +#define WL_PNO(x) #define WL_TRACE_COEX(x) #include @@ -115,10 +116,6 @@ static int g_onoff = G_WLAN_SET_ON; wl_iw_extra_params_t g_wl_iw_params; static struct mutex wl_cache_lock; -#ifdef CONFIG_US_NON_DFS_CHANNELS_ONLY -static bool use_non_dfs_channels = true; -#endif - extern bool wl_iw_conn_status_str(uint32 event_type, uint32 status, uint32 reason, char* stringBuf, uint buflen); #include @@ -161,12 +158,13 @@ extern int dhd_wait_pend8021x(struct net_device *dev); #endif static void *g_scan = NULL; -static volatile uint g_scan_specified_ssid; -static wlc_ssid_t g_specific_ssid; +static volatile uint g_scan_specified_ssid; +static wlc_ssid_t g_specific_ssid; static wlc_ssid_t g_ssid; -static wl_iw_ss_cache_ctrl_t g_ss_cache_ctrl; +bool btcoex_is_sco_active(struct net_device *dev); +static wl_iw_ss_cache_ctrl_t g_ss_cache_ctrl; #if defined(CONFIG_FIRST_SCAN) static volatile uint g_first_broadcast_scan; static volatile uint g_first_counter_scans; @@ -594,6 +592,36 @@ wl_iw_set_passive_scan( return error; } + +static int +wl_iw_set_txpower( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = 0; + char *p = extra; + int txpower = -1; + + txpower = bcm_atoi(extra + strlen(TXPOWER_SET_CMD) + 1); + if ((txpower >= 0) && (txpower <= 127)) { + txpower |= WL_TXPWR_OVERRIDE; + txpower = htod32(txpower); + + error = dev_wlc_intvar_set(dev, "qtxpower", txpower); + p += snprintf(p, MAX_WX_STRING, "OK"); + WL_TRACE(("%s: set TXpower 0x%X is OK\n", __FUNCTION__, txpower)); + } else { + WL_ERROR(("%s: set tx power failed\n", __FUNCTION__)); + p += snprintf(p, MAX_WX_STRING, "FAIL"); + } + + wrqu->data.length = p - extra + 1; + return error; +} + static int wl_iw_get_macaddr( struct net_device *dev, @@ -619,31 +647,6 @@ wl_iw_get_macaddr( return error; } -static int -wl_iw_set_country_code(struct net_device *dev, char *ccode) -{ - char country_code[WLC_CNTRY_BUF_SZ]; - int ret = -1; - - WL_TRACE(("%s\n", __FUNCTION__)); - if (!ccode) - ccode = dhd_bus_country_get(dev); - strncpy(country_code, ccode, sizeof(country_code)); - if (ccode && (country_code[0] != 0)) { -#ifdef CONFIG_US_NON_DFS_CHANNELS_ONLY - if (use_non_dfs_channels && !strncmp(country_code, "US", 2)) - strncpy(country_code, "Q2", WLC_CNTRY_BUF_SZ); - if (!use_non_dfs_channels && !strncmp(country_code, "Q2", 2)) - strncpy(country_code, "US", WLC_CNTRY_BUF_SZ); -#endif - ret = dev_wlc_ioctl(dev, WLC_SET_COUNTRY, &country_code, sizeof(country_code)); - if (ret >= 0) { - WL_TRACE(("%s: set country %s OK\n", __FUNCTION__, country_code)); - dhd_bus_country_set(dev, &country_code[0]); - } - } - return ret; -} static int wl_iw_set_country( @@ -658,25 +661,39 @@ wl_iw_set_country( char *p = extra; int country_offset; int country_code_size; + wl_country_t cspec = {{0}, 0, {0}}; + char smbuf[WLC_IOCTL_SMLEN]; - WL_TRACE(("%s\n", __FUNCTION__)); + cspec.rev = -1; memset(country_code, 0, sizeof(country_code)); + memset(smbuf, 0, sizeof(smbuf)); country_offset = strcspn(extra, " "); country_code_size = strlen(extra) - country_offset; if (country_offset != 0) { - strncpy(country_code, extra + country_offset + 1, + strncpy(country_code, extra + country_offset +1, MIN(country_code_size, sizeof(country_code))); - error = wl_iw_set_country_code(dev, country_code); - if (error >= 0) { + + + memcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ); + memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ); + + get_customized_country_code((char *)&cspec.country_abbrev, &cspec); + + if ((error = dev_iw_iovar_setbuf(dev, "country", &cspec, \ + sizeof(cspec), smbuf, sizeof(smbuf))) >= 0) { p += snprintf(p, MAX_WX_STRING, "OK"); - WL_TRACE(("%s: set country %s OK\n", __FUNCTION__, country_code)); + WL_ERROR(("%s: set country for %s as %s rev %d is OK\n", \ + __FUNCTION__, country_code, cspec.ccode, cspec.rev)); + dhd_bus_country_set(dev, &cspec); goto exit; } } - WL_ERROR(("%s: set country %s failed code %d\n", __FUNCTION__, country_code, error)); + WL_ERROR(("%s: set country for %s as %s rev %d failed\n", \ + __FUNCTION__, country_code, cspec.ccode, cspec.rev)); + p += snprintf(p, MAX_WX_STRING, "FAIL"); exit: @@ -738,26 +755,40 @@ wl_iw_set_power_mode( #endif -static bool btcoex_is_sco_active(struct net_device *dev) +bool btcoex_is_sco_active(struct net_device *dev) { int ioc_res = 0; bool res = false; - int temp = 0; + int sco_id_cnt = 0; + int param27; + int i; - ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 4, &temp); + for (i = 0; i < 12; i++) { - if (ioc_res == 0) { - WL_TRACE_COEX(("%s: read btc_params[4] = %x\n", __FUNCTION__, temp)); + ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 27, ¶m27); - if ((temp > 0xea0) && (temp < 0xed8)) { - WL_TRACE_COEX(("%s: BT SCO/eSCO is ACTIVE\n", __FUNCTION__)); + WL_TRACE_COEX(("%s, sample[%d], btc params: 27:%x\n", + __FUNCTION__, i, param27)); + + if (ioc_res < 0) { + WL_ERROR(("%s ioc read btc params error\n", __FUNCTION__)); + break; + } + + if ((param27 & 0x6) == 2) { + sco_id_cnt++; + } + + if (sco_id_cnt > 2) { + WL_TRACE_COEX(("%s, sco/esco detected, pkt id_cnt:%d samples:%d\n", + __FUNCTION__, sco_id_cnt, i)); res = true; - } else { - WL_TRACE_COEX(("%s: BT SCO/eSCO is NOT detected\n", __FUNCTION__)); + break; } - } else { - WL_ERROR(("%s ioc read btc params error\n", __FUNCTION__)); + + msleep(5); } + return res; } @@ -1043,21 +1074,6 @@ wl_iw_set_suspend( return ret; } -#ifdef CONFIG_US_NON_DFS_CHANNELS_ONLY -static int -wl_iw_set_dfs_channels( - struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra -) -{ - use_non_dfs_channels = *(extra + strlen(SETDFSCHANNELS_CMD) + 1) - '0'; - use_non_dfs_channels = (use_non_dfs_channels != 0) ? false : true; - wl_iw_set_country_code(dev, NULL); - return 0; -} -#endif int wl_format_ssid(char* ssid_buf, uint8* ssid, int ssid_len) @@ -1343,9 +1359,10 @@ wl_iw_set_pno_set( int nssid = 0; cmd_tlv_t *cmd_tlv_temp; char *str_ptr; - char *str_ptr_end; int tlv_size_left; int pno_time; + int pno_repeat; + int pno_freq_expo_max; #ifdef PNO_SET_DEBUG int i; @@ -1359,6 +1376,10 @@ wl_iw_set_pno_set( 'G', 'O', 'O', 'G', 'T', '1','E', + 'R', + '2', + 'M', + '2', 0x00 }; #endif @@ -1402,6 +1423,7 @@ wl_iw_set_pno_set( cmd_tlv_temp = (cmd_tlv_t *)str_ptr; memset(ssids_local, 0, sizeof(ssids_local)); + pno_repeat = pno_freq_expo_max = 0; if ((cmd_tlv_temp->prefix == PNO_TLV_PREFIX) && \ (cmd_tlv_temp->version == PNO_TLV_VERSION) && \ @@ -1422,9 +1444,28 @@ wl_iw_set_pno_set( goto exit_proc; } str_ptr++; - pno_time = simple_strtoul(str_ptr, &str_ptr_end, 16); - WL_ERROR((" got %d bytes left pno_time %d or %#x\n", \ - tlv_size_left, pno_time, pno_time)); + pno_time = simple_strtoul(str_ptr, &str_ptr, 16); + WL_PNO(("%s: pno_time=%d\n", __FUNCTION__, pno_time)); + + if (str_ptr[0] != 0) { + if ((str_ptr[0] != PNO_TLV_FREQ_REPEAT)) { + WL_ERROR(("%s pno repeat : corrupted field\n", \ + __FUNCTION__)); + goto exit_proc; + } + str_ptr++; + pno_repeat = simple_strtoul(str_ptr, &str_ptr, 16); + WL_PNO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat)); + if (str_ptr[0] != PNO_TLV_FREQ_EXPO_MAX) { + WL_ERROR(("%s FREQ_EXPO_MAX corrupted field size\n", \ + __FUNCTION__)); + goto exit_proc; + } + str_ptr++; + pno_freq_expo_max = simple_strtoul(str_ptr, &str_ptr, 16); + WL_PNO(("%s: pno_freq_expo_max=%d\n", \ + __FUNCTION__, pno_freq_expo_max)); + } } } else { @@ -1432,7 +1473,7 @@ wl_iw_set_pno_set( goto exit_proc; } - res = dhd_dev_pno_set(dev, ssids_local, nssid, pno_time); + res = dhd_dev_pno_set(dev, ssids_local, nssid, pno_time, pno_repeat, pno_freq_expo_max); exit_proc: net_os_wake_unlock(dev); @@ -1719,6 +1760,79 @@ int hstr_2_buf(const char *txt, u8 *buf, int len) return 0; } +#if defined(SOFTAP) && defined(SOFTAP_TLV_CFG) + +static int wl_iw_softap_cfg_tlv( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int res = -1; + char *str_ptr; + int tlv_size_left; + + +#define SOFTAP_TLV_DEBUG 1 +#ifdef SOFTAP_TLV_DEBUG +char softap_cmd_example[] = { + + 'S', 'O', 'F', 'T', 'A', 'P', 'S', 'E', 'T', ' ', + + SOFTAP_TLV_PREFIX, SOFTAP_TLV_VERSION, + SOFTAP_TLV_SUBVERSION, SOFTAP_TLV_RESERVED, + + TLV_TYPE_SSID, 9, 'B', 'R', 'C', 'M', ',', 'G', 'O', 'O', 'G', + + TLV_TYPE_SECUR, 4, 'O', 'P', 'E', 'N', + + TLV_TYPE_KEY, 4, 0x31, 0x32, 0x33, 0x34, + + TLV_TYPE_CHANNEL, 4, 0x06, 0x00, 0x00, 0x00 +}; +#endif + + +#ifdef SOFTAP_TLV_DEBUG + { + int i; + if (!(extra = kmalloc(sizeof(softap_cmd_example) +10, GFP_KERNEL))) + return -ENOMEM; + memcpy(extra, softap_cmd_example, sizeof(softap_cmd_example)); + wrqu->data.length = sizeof(softap_cmd_example); + print_buf(extra, wrqu->data.length, 16); + for (i = 0; i < wrqu->data.length; i++) + printf("%c ", extra[i]); + printf("\n"); + } +#endif + + WL_ERROR(("\n### %s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n", + __FUNCTION__, info->cmd, info->flags, + wrqu->data.pointer, wrqu->data.length)); + + if (g_onoff == G_WLAN_SET_OFF) { + WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__)); + return -1; + } + + if (wrqu->data.length < (strlen(SOFTAP_SET_CMD) + sizeof(cmd_tlv_t))) { + WL_ERROR(("%s argument=%d less %d\n", __FUNCTION__, + wrqu->data.length, strlen(SOFTAP_SET_CMD) + sizeof(cmd_tlv_t))); + return -1; + } + + str_ptr = extra + strlen(SOFTAP_SET_CMD)+1; + tlv_size_left = wrqu->data.length - (strlen(SOFTAP_SET_CMD)+1); + + memset(&my_ap, 0, sizeof(my_ap)); + + return res; +} +#endif + + #ifdef SOFTAP int init_ap_profile_from_string(char *param_str, struct ap_profile *ap_cfg) { @@ -5728,7 +5842,7 @@ wl_iw_combined_scan_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nss WL_SCAN(("scan_type=%d\n", iscan->iscan_ex_params_p->params.scan_type)); WL_SCAN(("\n###################\n")); } -#endif +#endif if (params_size > WLC_IOCTL_MEDLEN) { WL_ERROR(("Set ISCAN for %s due to params_size=%d \n", \ @@ -5768,6 +5882,11 @@ static int iwpriv_set_cscan(struct net_device *dev, struct iw_request_info *info return -1; } +#ifdef PNO_SET_DEBUG + wl_iw_set_pno_set(dev, info, wrqu, extra); + return 0; +#endif + if (wrqu->data.length != 0) { char *str_ptr; @@ -6295,16 +6414,8 @@ static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap) } if (strlen(ap->country_code)) { - int error = 0; - if ((error = dev_wlc_ioctl(dev, WLC_SET_COUNTRY, - ap->country_code, sizeof(ap->country_code))) >= 0) { - WL_SOFTAP(("%s: set country %s OK\n", - __FUNCTION__, ap->country_code)); - dhd_bus_country_set(dev, &ap->country_code[0]); - } else { - WL_ERROR(("%s: ERROR:%d setting country %s\n", - __FUNCTION__, error, ap->country_code)); - } + WL_ERROR(("%s: Igonored: Country MUST be specified \ + COUNTRY command with \n", __FUNCTION__)); } else { WL_SOFTAP(("%s: Country code is not specified," " will use Radio's default\n", @@ -7114,10 +7225,8 @@ static int wl_iw_set_priv( ret = wl_iw_set_dtim_skip(dev, info, (union iwreq_data *)dwrq, extra); else if (strnicmp(extra, SETSUSPEND_CMD, strlen(SETSUSPEND_CMD)) == 0) ret = wl_iw_set_suspend(dev, info, (union iwreq_data *)dwrq, extra); -#ifdef CONFIG_US_NON_DFS_CHANNELS_ONLY - else if (strnicmp(extra, SETDFSCHANNELS_CMD, strlen(SETDFSCHANNELS_CMD)) == 0) - ret = wl_iw_set_dfs_channels(dev, info, (union iwreq_data *)dwrq, extra); -#endif + else if (strnicmp(extra, TXPOWER_SET_CMD, strlen(TXPOWER_SET_CMD)) == 0) + ret = wl_iw_set_txpower(dev, info, (union iwreq_data *)dwrq, extra); #if defined(PNO_SUPPORT) else if (strnicmp(extra, PNOSSIDCLR_SET_CMD, strlen(PNOSSIDCLR_SET_CMD)) == 0) ret = wl_iw_set_pno_reset(dev, info, (union iwreq_data *)dwrq, extra); @@ -7133,8 +7242,10 @@ static int wl_iw_set_priv( #ifdef CUSTOMER_HW2 else if (strnicmp(extra, "POWERMODE", strlen("POWERMODE")) == 0) ret = wl_iw_set_power_mode(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, "BTCOEXMODE", strlen("BTCOEXMODE")) == 0) + else if (strnicmp(extra, "BTCOEXMODE", strlen("BTCOEXMODE")) == 0) { + WL_TRACE_COEX(("%s:got Framwrork cmd: 'BTCOEXMODE'\n", __FUNCTION__)); ret = wl_iw_set_btcoex_dhcp(dev, info, (union iwreq_data *)dwrq, extra); + } #else else if (strnicmp(extra, "POWERMODE", strlen("POWERMODE")) == 0) ret = wl_iw_set_btcoex_dhcp(dev, info, (union iwreq_data *)dwrq, extra); @@ -7142,6 +7253,11 @@ static int wl_iw_set_priv( else if (strnicmp(extra, "GETPOWER", strlen("GETPOWER")) == 0) ret = wl_iw_get_power_mode(dev, info, (union iwreq_data *)dwrq, extra); #ifdef SOFTAP +#ifdef SOFTAP_TLV_CFG + else if (strnicmp(extra, SOFTAP_SET_CMD, strlen(SOFTAP_SET_CMD)) == 0) { + wl_iw_softap_cfg_tlv(dev, info, (union iwreq_data *)dwrq, extra); + } +#endif else if (strnicmp(extra, "ASCII_CMD", strlen("ASCII_CMD")) == 0) { wl_iw_process_private_ascii_cmd(dev, info, (union iwreq_data *)dwrq, extra); } else if (strnicmp(extra, "AP_MAC_LIST_SET", strlen("AP_MAC_LIST_SET")) == 0) { @@ -7662,9 +7778,10 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) uint32 datalen = ntoh32(e->datalen); uint32 status = ntoh32(e->status); uint32 toto; +#if defined(ROAM_NOT_USED) static uint32 roam_no_success = 0; static bool roam_no_success_send = FALSE; - +#endif memset(&wrqu, 0, sizeof(wrqu)); memset(extra, 0, sizeof(extra)); @@ -7739,6 +7856,7 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) wrqu.addr.sa_family = ARPHRD_ETHER; cmd = SIOCGIWAP; } +#if defined(ROAM_NOT_USED) else if (status == WLC_E_STATUS_NO_NETWORKS) { roam_no_success++; if ((roam_no_success == 5) && (roam_no_success_send == FALSE)) { @@ -7753,6 +7871,7 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) goto wl_iw_event_end; } } +#endif break; case WLC_E_DEAUTH_IND: case WLC_E_DISASSOC_IND: @@ -7808,8 +7927,10 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) wl_iw_send_priv_event(priv_dev, "AP_UP"); } else { WL_TRACE(("STA_LINK_UP\n")); +#if defined(ROAM_NOT_USED) roam_no_success_send = FALSE; roam_no_success = 0; +#endif } #endif WL_TRACE(("Link UP\n")); diff --git a/drivers/net/wireless/bcm4329/wl_iw.h b/drivers/net/wireless/bcm4329/wl_iw.h index 928291fe589a5..d94bdb4327233 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.h +++ b/drivers/net/wireless/bcm4329/wl_iw.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wl_iw.h,v 1.5.34.1.6.36.4.15 2010/11/17 03:13:51 Exp $ + * $Id: wl_iw.h,v 1.5.34.1.6.36.4.18 2011/02/10 19:33:12 Exp $ */ @@ -52,7 +52,7 @@ #define PNOSETUP_SET_CMD "PNOSETUP " #define PNOENABLE_SET_CMD "PNOFORCE" #define PNODEBUG_SET_CMD "PNODEBUG" -#define SETDFSCHANNELS_CMD "SETDFSCHANNELS" +#define TXPOWER_SET_CMD "TXPOWER" #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" @@ -62,6 +62,12 @@ typedef struct wl_iw_extra_params { int target_channel; } wl_iw_extra_params_t; +struct cntry_locales_custom { + char iso_abbrev[WLC_CNTRY_BUF_SZ]; + char custom_locale[WLC_CNTRY_BUF_SZ]; + int32 custom_locale_rev; +}; + #define WL_IW_RSSI_MINVAL -200 #define WL_IW_RSSI_NO_SIGNAL -91 #define WL_IW_RSSI_VERY_LOW -80 @@ -133,13 +139,13 @@ typedef struct wl_iw_ss_cache { } wl_iw_ss_cache_t; typedef struct wl_iw_ss_cache_ctrl { - wl_iw_ss_cache_t *m_cache_head; - int m_link_down; - int m_timer_expired; - char m_active_bssid[ETHER_ADDR_LEN]; - uint m_prev_scan_mode; - uint m_cons_br_scan_cnt; - struct timer_list *m_timer; + wl_iw_ss_cache_t *m_cache_head; + int m_link_down; + int m_timer_expired; + char m_active_bssid[ETHER_ADDR_LEN]; + uint m_prev_scan_mode; + uint m_cons_br_scan_cnt; + struct timer_list *m_timer; } wl_iw_ss_cache_ctrl_t; typedef enum broadcast_first_scan { @@ -165,7 +171,7 @@ struct ap_profile { }; -#define MACLIST_MODE_DISABLED 0 +#define MACLIST_MODE_DISABLED 0 #define MACLIST_MODE_DENY 1 #define MACLIST_MODE_ALLOW 2 struct mflist { @@ -199,7 +205,7 @@ extern int net_os_set_suspend_disable(struct net_device *dev, int val); extern int net_os_set_suspend(struct net_device *dev, int val); extern int net_os_set_dtim_skip(struct net_device *dev, int val); extern int net_os_set_packet_filter(struct net_device *dev, int val); -extern void dhd_bus_country_set(struct net_device *dev, char *country_code); +extern void get_customized_country_code(char *country_iso_code, wl_country_t *cspec); extern char *dhd_bus_country_get(struct net_device *dev); extern int dhd_get_dtim_skip(dhd_pub_t *dhd); @@ -221,13 +227,15 @@ extern int dhd_get_dtim_skip(dhd_pub_t *dhd); extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); extern int dhd_pno_clean(dhd_pub_t *dhd); -extern int dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr); +extern int dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, \ + ushort scan_fr, int pno_repeat, int pno_freq_expo_max); extern int dhd_pno_get_status(dhd_pub_t *dhd); extern int dhd_dev_pno_reset(struct net_device *dev); extern int dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, \ - int nssid, ushort scan_fr); + int nssid, ushort scan_fr, int pno_repeat, int pno_freq_expo_max); extern int dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled); extern int dhd_dev_get_pno_status(struct net_device *dev); +extern void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec); #define PNO_TLV_PREFIX 'S' #define PNO_TLV_VERSION '1' @@ -235,8 +243,9 @@ extern int dhd_dev_get_pno_status(struct net_device *dev); #define PNO_TLV_RESERVED '0' #define PNO_TLV_TYPE_SSID_IE 'S' #define PNO_TLV_TYPE_TIME 'T' -#define PNO_EVENT_UP "PNO_EVENT" -#define PNO_SCAN_MAX_FW 508 +#define PNO_TLV_FREQ_REPEAT 'R' +#define PNO_TLV_FREQ_EXPO_MAX 'M' +#define PNO_EVENT_UP "PNO_EVENT" typedef struct cmd_tlv { char prefix; @@ -245,6 +254,19 @@ typedef struct cmd_tlv { char reserved; } cmd_tlv_t; +#ifdef SOFTAP_TLV_CFG +#define SOFTAP_SET_CMD "SOFTAPSET " +#define SOFTAP_TLV_PREFIX 'A' +#define SOFTAP_TLV_VERSION '1' +#define SOFTAP_TLV_SUBVERSION '0' +#define SOFTAP_TLV_RESERVED '0' + +#define TLV_TYPE_SSID 'S' +#define TLV_TYPE_SECUR 'E' +#define TLV_TYPE_KEY 'K' +#define TLV_TYPE_CHANNEL 'C' +#endif + #if defined(CSCAN) typedef struct cscan_tlv { @@ -279,6 +301,6 @@ extern int wl_iw_parse_ssid_list(char** list_str, wlc_ssid_t* ssid, int idx, int extern int wl_iw_parse_channel_list(char** list_str, uint16* channel_list, int channel_num); -#endif +#endif -#endif +#endif From 6ed2fdb6b7ca6b06a9b52149a5281046d5a8db7e Mon Sep 17 00:00:00 2001 From: Greg Goldman Date: Fri, 18 Feb 2011 17:12:19 -0800 Subject: [PATCH 0992/2556] net: wireless: bcm4329: Fix disable pno call Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/Makefile | 2 +- drivers/net/wireless/bcm4329/dhd_common.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/bcm4329/Makefile b/drivers/net/wireless/bcm4329/Makefile index dd0452d129eed..5a662be7fc532 100644 --- a/drivers/net/wireless/bcm4329/Makefile +++ b/drivers/net/wireless/bcm4329/Makefile @@ -7,7 +7,7 @@ DHDCFLAGS = -DLINUX -DBCMDRIVER -DBCMDONGLEHOST -DDHDTHREAD -DBCMWPA2 \ -DDHD_USE_STATIC_BUF -DMMC_SDIO_ABORT -DDHD_DEBUG_TRAP -DSOFTAP \ -DEMBEDDED_PLATFORM -DARP_OFFLOAD_SUPPORT -DPKT_FILTER_SUPPORT \ -DGET_CUSTOM_MAC_ENABLE -DSET_RANDOM_MAC_SOFTAP -DCSCAN -DHW_OOB \ - -DKEEP_ALIVE -DPNO_SUPPORT \ + -DKEEP_ALIVE -DPNO_SUPPORT \ -Idrivers/net/wireless/bcm4329 -Idrivers/net/wireless/bcm4329/include DHDOFILES = dhd_linux.o linux_osl.o bcmutils.o dhd_common.o dhd_custom_gpio.o \ diff --git a/drivers/net/wireless/bcm4329/dhd_common.c b/drivers/net/wireless/bcm4329/dhd_common.c index 9a345fb430760..8fcb95fde827d 100644 --- a/drivers/net/wireless/bcm4329/dhd_common.c +++ b/drivers/net/wireless/bcm4329/dhd_common.c @@ -1911,12 +1911,12 @@ int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled) if ((pfn_enabled) && \ ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_GET_BSSID, \ (char *)&bssid, ETHER_ADDR_LEN)) == BCME_NOTASSOCIATED)) { - DHD_TRACE(("%s pno enable called in disassoc mode\n", __FUNCTION__)); + DHD_TRACE(("%s pno enable called in disassoc mode\n", __FUNCTION__)); } - else { - DHD_ERROR(("%s pno enable called in assoc mode ret=%d\n", \ - __FUNCTION__, ret)); - return ret; + else if (pfn_enabled) { + DHD_ERROR(("%s pno enable called in assoc mode ret=%d\n", \ + __FUNCTION__, ret)); + return ret; } /* Enable/disable PNO */ From 095dafcdff2c80934d3f85c9b9bcd86429dd0408 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 14 Feb 2011 13:32:05 -0500 Subject: [PATCH 0993/2556] USB: gadget: android: Fix problem switching vendor IDs We need to update the vendor ID in the composite driver as well Signed-off-by: Mike Lockwood --- drivers/usb/gadget/android.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index e49ef2c470d66..eb6af6d538d6c 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -343,6 +343,7 @@ static int android_bind(struct usb_composite_dev *cdev) dev->cdev = cdev; device_desc.idVendor = __constant_cpu_to_le16(get_vendor_id(dev)); device_desc.idProduct = __constant_cpu_to_le16(get_product_id(dev)); + cdev->desc.idVendor = device_desc.idVendor; cdev->desc.idProduct = device_desc.idProduct; return 0; @@ -434,8 +435,10 @@ void android_enable_function(struct usb_function *f, int enable) device_desc.idVendor = __constant_cpu_to_le16(get_vendor_id(dev)); device_desc.idProduct = __constant_cpu_to_le16(get_product_id(dev)); - if (dev->cdev) + if (dev->cdev) { + dev->cdev->desc.idVendor = device_desc.idVendor; dev->cdev->desc.idProduct = device_desc.idProduct; + } usb_composite_force_reset(dev->cdev); } } From 0a8e9af1f6011b54680106c229dcbc6a0f1b3438 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Wed, 9 Mar 2011 02:24:53 -0500 Subject: [PATCH 0994/2556] drivers: a1026: Check for invalid paths Check for invalid path id in ioctl so we are sure to drop the lock. --- drivers/misc/a1026.c | 4 ++++ include/linux/a1026.h | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/misc/a1026.c b/drivers/misc/a1026.c index 1ef882be56ca4..b3ee9d2c9474e 100644 --- a/drivers/misc/a1026.c +++ b/drivers/misc/a1026.c @@ -901,6 +901,10 @@ a1026_ioctl(struct file *file, unsigned int cmd, unsigned long arg) rc = -EFAULT; goto out; } + if (pathid < 0 || pathid >= A1026_PATH_MAX) { + rc = -EINVAL; + goto out; + } rc = a1026_set_config(pathid, A1026_CONFIG_FULL); if (rc < 0) pr_err("%s: A1026_SET_CONFIG (%d) error %d!\n", diff --git a/include/linux/a1026.h b/include/linux/a1026.h index 44cdf4d623964..474a971942c3f 100644 --- a/include/linux/a1026.h +++ b/include/linux/a1026.h @@ -43,7 +43,8 @@ enum A1026_PathID { A1026_PATH_RECORD_SPEAKER, A1026_PATH_RECORD_BT, A1026_PATH_CAMCORDER, - A1026_PATH_INCALL_TTY + A1026_PATH_INCALL_TTY, + A1026_PATH_MAX }; /* noise suppression states */ From 142934d3ccc8ca6721ef642b7bf9cc9a361567fe Mon Sep 17 00:00:00 2001 From: Phani Kumar Allada Date: Wed, 8 Dec 2010 14:43:06 +0530 Subject: [PATCH 0995/2556] msm: audio: 8k: Send ACDB data after the PREPARE stage of device switch As per the Q6 API, the ACDB configuration need to be sent to Q6 after the PREPARE stage of the device switch. But in the current code it is in the reverse order. Hence correcting the code as per the Q6 API. Change-Id: I9f947d44f73b1fc71ddea8259ca54402edc895c2 Signed-off-by: Phani Kumar Allada --- arch/arm/mach-msm/qdsp6/q6audio.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c index 9b62bb6d1ae86..3d38da5d07f29 100644 --- a/arch/arm/mach-msm/qdsp6/q6audio.c +++ b/arch/arm/mach-msm/qdsp6/q6audio.c @@ -951,9 +951,9 @@ static void _audio_rx_path_enable(int reconf, uint32_t acdb_id) adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_READY); adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_ANALOG_READY); - audio_update_acdb(audio_rx_device_id, acdb_id); if (!reconf) qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, audio_rx_device_id); + audio_update_acdb(audio_rx_device_id, acdb_id); qdsp6_standby(ac_control); qdsp6_start(ac_control); @@ -975,10 +975,9 @@ static void _audio_tx_path_enable(int reconf, uint32_t acdb_id) adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_DIGITAL_READY); adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_DIGITAL_ANALOG_READY); - audio_update_acdb(audio_tx_device_id, acdb_id); - if (!reconf) qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, audio_tx_device_id); + audio_update_acdb(audio_tx_device_id, acdb_id); qdsp6_standby(ac_control); qdsp6_start(ac_control); @@ -1236,14 +1235,15 @@ int q6audio_update_acdb(uint32_t id_src, uint32_t id_dst) return 0; mutex_lock(&audio_path_lock); - res = audio_update_acdb(id_dst, id_src); - if (res) - goto done; if (q6_device_to_dir(id_dst) == Q6_RX) qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, id_dst); else qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, id_dst); + res = audio_update_acdb(id_dst, id_src); + if (res) + goto done; + qdsp6_standby(ac_control); qdsp6_start(ac_control); done: @@ -1310,8 +1310,8 @@ static void do_rx_routing(uint32_t device_id, uint32_t acdb_id) { if (device_id == audio_rx_device_id) { if (acdb_id != rx_acdb) { - audio_update_acdb(device_id, acdb_id); qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, device_id); + audio_update_acdb(device_id, acdb_id); qdsp6_standby(ac_control); qdsp6_start(ac_control); } @@ -1333,8 +1333,8 @@ static void do_tx_routing(uint32_t device_id, uint32_t acdb_id) { if (device_id == audio_tx_device_id) { if (acdb_id != tx_acdb) { - audio_update_acdb(device_id, acdb_id); qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, device_id); + audio_update_acdb(device_id, acdb_id); qdsp6_standby(ac_control); qdsp6_start(ac_control); } From 937f0a8dd3a5ee13e3feacf282d715d49a1c3b0c Mon Sep 17 00:00:00 2001 From: Phani Kumar Allada Date: Mon, 3 Jan 2011 13:14:24 +0530 Subject: [PATCH 0996/2556] msm: audio: 8k: Send ACDB configuration during device switch In the current code, for a device switch command, ACDB configuration is sent to Q6 only if there is any active tx/rx session. As per Q6 API, changing the code to send ACDB configuration after the PREPARE stage of the device switch even if there's no active tx/rx session. Change-Id: I895f5da5d23f586ae829c4856a5b39b4399422ee CRs-Fixed: 267703 Signed-off-by: Phani Kumar Allada --- arch/arm/mach-msm/qdsp6/q6audio.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c index 3d38da5d07f29..71b2ca9d1bab6 100644 --- a/arch/arm/mach-msm/qdsp6/q6audio.c +++ b/arch/arm/mach-msm/qdsp6/q6audio.c @@ -1324,6 +1324,11 @@ static void do_rx_routing(uint32_t device_id, uint32_t acdb_id) _audio_rx_clk_reinit(device_id); _audio_rx_path_enable(1, acdb_id); } else { + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, + device_id); + audio_update_acdb(device_id, acdb_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); audio_rx_device_id = device_id; audio_rx_path_id = q6_device_to_path(device_id); } @@ -1333,7 +1338,8 @@ static void do_tx_routing(uint32_t device_id, uint32_t acdb_id) { if (device_id == audio_tx_device_id) { if (acdb_id != tx_acdb) { - qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, device_id); + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, + device_id); audio_update_acdb(device_id, acdb_id); qdsp6_standby(ac_control); qdsp6_start(ac_control); @@ -1347,6 +1353,11 @@ static void do_tx_routing(uint32_t device_id, uint32_t acdb_id) _audio_tx_clk_reinit(device_id); _audio_tx_path_enable(1, acdb_id); } else { + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, + device_id); + audio_update_acdb(device_id, acdb_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); audio_tx_device_id = device_id; audio_tx_path_id = q6_device_to_path(device_id); } From 0f5fe5d0661e57e4ac140b03a15ed5b4e0f36378 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Wed, 9 Mar 2011 23:41:24 -0500 Subject: [PATCH 0997/2556] Add sched.h includes to enable build without BKL --- drivers/net/wireless/bcm4329/dhd.h | 1 + drivers/net/wireless/bcm4329/dhd_linux.c | 1 + drivers/usb/gadget/f_adb.c | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/net/wireless/bcm4329/dhd.h b/drivers/net/wireless/bcm4329/dhd.h index 6c2998806e4d7..3fb5c6bf38e38 100644 --- a/drivers/net/wireless/bcm4329/dhd.h +++ b/drivers/net/wireless/bcm4329/dhd.h @@ -44,6 +44,7 @@ #include #include #include +#include #include #include diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index 26cf90c6a4470..a4c9df52d9818 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c index 15a5387dd3478..105399f917714 100644 --- a/drivers/usb/gadget/f_adb.c +++ b/drivers/usb/gadget/f_adb.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include From e316f1df9040b762cb2a8a6c17234293689bc638 Mon Sep 17 00:00:00 2001 From: Abhijeet Dharmapurikar Date: Sun, 19 Dec 2010 16:23:48 -0800 Subject: [PATCH 0998/2556] genirq: pm: Fix the enable ordering in resume In suspend interrupts are disabled from 0 to NR_IRQ, in resume interrupts should be enabled in reverse order. Enabling parent or summary interrupts before enabling child interrupts causes the handler of the child interrupt to run even before it is enabled. Usually the genirq handler does the correct thing of masking the interrupt and additionally marking the interrupt IRQ_PENDING if its an edge triggered interrupt. However the nested handler (handle_nested_irq()) simply ignores the interrupt causing a loss of it. Not calling the action of an interrupt, especially if it marked wakeup, causes the system to incorrectly go back to suspend immediately. Change-Id: Ica30c10a975a4a7b41b97b4f21250dac80335b2b Signed-off-by: Abhijeet Dharmapurikar --- kernel/irq/pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c index 1df62ef4713bf..bc56498e7b90d 100644 --- a/kernel/irq/pm.c +++ b/kernel/irq/pm.c @@ -50,7 +50,7 @@ void resume_device_irqs(void) struct irq_desc *desc; int irq; - for_each_irq_desc(irq, desc) { + for_each_irq_desc_reverse(irq, desc) { unsigned long flags; if (!(desc->status & IRQ_SUSPENDED)) From 291aded157a8f8516a81e7af9aec8851fa49ff88 Mon Sep 17 00:00:00 2001 From: Larry Bassel Date: Fri, 7 May 2010 14:59:30 -0700 Subject: [PATCH 0999/2556] arm: cache page tables and set ACTLR on Scorpion These were set in .29, porting to .32 Change-Id: If49b277e7275b3a9bf1a575ca3c75118e431c18e Signed-off-by: Larry Bassel --- arch/arm/mm/proc-v7.S | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index 8e3356239136a..9f863e3085a05 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -282,6 +282,21 @@ __v7_setup: ALT_SMP(orr r4, r4, #TTB_FLAGS_SMP) ALT_UP(orr r4, r4, #TTB_FLAGS_UP) mcr p15, 0, r4, c2, c0, 1 @ load TTB1 +#ifdef CONFIG_ARCH_MSM_SCORPION +#ifdef CONFIG_ARCH_QSD8X50 + mov r0, #0x77 +#else + mov r0, #0x33 +#endif + mcr p15, 3, r0, c15, c0, 3 @ set L2CR1 + mrc p15, 0, r0, c1, c0, 1 @ read ACTLR +#ifdef CONFIG_CPU_CACHE_ERR_REPORT + orr r0, r0, #0x37 @ turn on L1/L2 error reporting +#else + bic r0, r0, #0x37 +#endif + mcr p15, 0, r0, c1, c0, 1 @ write ACTLR +#endif /* * Memory region attributes with SCTLR.TRE=1 * From 8cf16f2fcd5862d95040946792d70ec4b46aa9b1 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 9 Mar 2011 16:52:08 -0800 Subject: [PATCH 1000/2556] net: wireless: bcm4329: Disable wake irq at driver stop Previously at driver stop interrupts were not disabled and might cause never-ending waking up Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/bcmsdh_linux.c | 14 ++++++++++++++ drivers/net/wireless/bcm4329/dhd_sdio.c | 7 ++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcm4329/bcmsdh_linux.c b/drivers/net/wireless/bcm4329/bcmsdh_linux.c index 1e33555b0531e..94f19a1c46a4f 100644 --- a/drivers/net/wireless/bcm4329/bcmsdh_linux.c +++ b/drivers/net/wireless/bcm4329/bcmsdh_linux.c @@ -643,6 +643,20 @@ int bcmsdh_register_oob_intr(void * dhdp) return 0; } +void bcmsdh_set_irq(int flag) +{ + if (sdhcinfo->oob_irq_registered) { + SDLX_MSG(("%s Flag = %d", __FUNCTION__, flag)); + if (flag) { + enable_irq(sdhcinfo->oob_irq); + enable_irq_wake(sdhcinfo->oob_irq); + } else { + disable_irq_wake(sdhcinfo->oob_irq); + disable_irq(sdhcinfo->oob_irq); + } + } +} + void bcmsdh_unregister_oob_intr(void) { SDLX_MSG(("%s: Enter\n", __FUNCTION__)); diff --git a/drivers/net/wireless/bcm4329/dhd_sdio.c b/drivers/net/wireless/bcm4329/dhd_sdio.c index f9b9eceb91c7a..1380dd389cf62 100644 --- a/drivers/net/wireless/bcm4329/dhd_sdio.c +++ b/drivers/net/wireless/bcm4329/dhd_sdio.c @@ -146,6 +146,8 @@ DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep); extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len); +extern void bcmsdh_set_irq(int flag); + #ifdef DHD_DEBUG /* Device console log buffer state */ typedef struct dhd_console { @@ -5749,7 +5751,9 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) /* Expect app to have torn down any connection before calling */ /* Stop the bus, disable F2 */ dhd_bus_stop(bus, FALSE); - +#if defined(OOB_INTR_ONLY) + bcmsdh_set_irq(FALSE); +#endif /* defined(OOB_INTR_ONLY) */ /* Clean tx/rx buffer pointers, detach from the dongle */ dhdsdio_release_dongle(bus, bus->dhd->osh, TRUE); @@ -5785,6 +5789,7 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) bcmerror = dhd_bus_init((dhd_pub_t *) bus->dhd, FALSE); if (bcmerror == BCME_OK) { #if defined(OOB_INTR_ONLY) + bcmsdh_set_irq(TRUE); dhd_enable_oob_intr(bus, TRUE); #endif /* defined(OOB_INTR_ONLY) */ bus->dhd->dongle_reset = FALSE; From 4fc7853e53252caea491dd9a80f1de077f930573 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 10 Mar 2011 10:18:39 -0800 Subject: [PATCH 1001/2556] net: wireless: bcm4329: Allocate skb with GFP_KERNEL flag if possible Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/linux_osl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcm4329/linux_osl.c b/drivers/net/wireless/bcm4329/linux_osl.c index 980416baf918c..cf72a077bd908 100644 --- a/drivers/net/wireless/bcm4329/linux_osl.c +++ b/drivers/net/wireless/bcm4329/linux_osl.c @@ -247,8 +247,10 @@ void* osl_pktget(osl_t *osh, uint len) { struct sk_buff *skb; + gfp_t flags; - if ((skb = dev_alloc_skb(len))) { + flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; + if ((skb = __dev_alloc_skb(len, flags))) { skb_put(skb, len); skb->priority = 0; From 360a66e7289baa8193d9f381e75420665a3aebe8 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 29 Mar 2011 23:15:52 -0400 Subject: [PATCH 1002/2556] msm: i2c: Use usleep_range --- drivers/i2c/busses/i2c-msm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-msm.c b/drivers/i2c/busses/i2c-msm.c index 0322262f88543..ad76d794d6e81 100644 --- a/drivers/i2c/busses/i2c-msm.c +++ b/drivers/i2c/busses/i2c-msm.c @@ -254,7 +254,7 @@ msm_i2c_poll_notbusy(struct msm_i2c_dev *dev, int warn) return 0; } if (retries++ > 100) - msleep(10); + usleep_range(100, 200); } dev_err(dev->dev, "Error waiting for notbusy (%d)\n", warn); return -ETIMEDOUT; @@ -299,7 +299,7 @@ msm_i2c_recover_bus_busy(struct msm_i2c_dev *dev) gpio_direction_input(gpio_clk); udelay(5); if (!gpio_get_value(gpio_clk)) - udelay(20); + usleep_range(20, 30); if (!gpio_get_value(gpio_clk)) msleep(10); gpio_clk_status = gpio_get_value(gpio_clk); From 06927fbfd1a7aed1bb9712ddf74b0891b35b57ad Mon Sep 17 00:00:00 2001 From: Patrick Jacques Date: Tue, 17 Aug 2010 00:41:24 -0700 Subject: [PATCH 1003/2556] input: synaptics_i2c_rmi: duplicated_filter to throttle touch events Create multitouch threshold to prevent event hub from being flooded. This attempts to (partially) address Android issue #7836: http://code.google.com/p/android/issues/detail?id=7836 Depends on CONFIG_TOUCHSCREEN_DUPLICATED_FILTER=y Originally authored by HTC for their Hero 2.6.29 kernel, ported to kernel-hero by netarchy/ninpo, adapted to .34 by kernelzilla. --- drivers/input/touchscreen/Kconfig | 8 +++ drivers/input/touchscreen/synaptics_i2c_rmi.c | 58 +++++++++++++++++++ include/linux/synaptics_i2c_rmi.h | 2 + 3 files changed, 68 insertions(+) diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index fb301140ca3f9..b4921be9d2493 100755 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -404,6 +404,14 @@ config TOUCHSCREEN_SYNAPTICS_I2C_RMI help This enables support for Synaptics RMI over I2C based touchscreens. +config TOUCHSCREEN_DUPLICATED_FILTER + bool "Touchscreen duplicated report filter" + depends on TOUCHSCREEN_SYNAPTICS_I2C_RMI + default y + help + This enables filter for duplicated report in touchscreen driver + This support will discard small movement report in touchscreen driver + config TOUCHSCREEN_TOUCHRIGHT tristate "Touchright serial touchscreen" select SERIO diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi.c b/drivers/input/touchscreen/synaptics_i2c_rmi.c index 5729602cbb63a..c4d967c312d3d 100644 --- a/drivers/input/touchscreen/synaptics_i2c_rmi.c +++ b/drivers/input/touchscreen/synaptics_i2c_rmi.c @@ -13,6 +13,12 @@ * */ +/* Ported HTC's filtering code from Hero kernel sources to prevent + * the event hub being spammed with unnecessary events causing + * massive cpu usage. + * netarchy / Ninpo + */ + #include #include #include @@ -46,6 +52,7 @@ struct synaptics_ts_data { uint32_t flags; int reported_finger_count; int8_t sensitivity_adjust; + uint32_t dup_threshold; int (*power)(int on); struct early_suspend early_suspend; }; @@ -55,6 +62,48 @@ static void synaptics_ts_early_suspend(struct early_suspend *h); static void synaptics_ts_late_resume(struct early_suspend *h); #endif +#ifdef CONFIG_TOUCHSCREEN_DUPLICATED_FILTER +static int duplicated_filter(struct synaptics_ts_data *ts, int pos[2][2], + const int finger2_pressed, const int z) +{ + int drift_x[2]; + int drift_y[2]; + static int ref_x[2], ref_y[2]; + uint8_t discard[2] = {0, 0}; + + drift_x[0] = abs(ref_x[0] - pos[0][0]); + drift_y[0] = abs(ref_y[0] - pos[0][1]); + if (finger2_pressed) { + drift_x[1] = abs(ref_x[1] - pos[1][0]); + drift_y[1] = abs(ref_y[1] - pos[1][1]); + } + + if (drift_x[0] < ts->dup_threshold && drift_y[0] < ts->dup_threshold && z != 0) { + + discard[0] = 1; + } + if (!finger2_pressed || (drift_x[1] < ts->dup_threshold && drift_y[1] < ts->dup_threshold)) { + discard[1] = 1; + } + if (discard[0] && discard[1]) { +// if finger 0 and finger 1's movement < threshold , discard it. + return 1; + } + ref_x[0] = pos[0][0]; + ref_y[0] = pos[0][1]; + if (finger2_pressed) { + ref_x[1] = pos[1][0]; + ref_y[1] = pos[1][1]; + } + if (z == 0) { + ref_x[0] = ref_y[0] = 0; + ref_x[1] = ref_y[1] = 0; + } + + return 0; +} +#endif + static int synaptics_init_panel(struct synaptics_ts_data *ts) { int ret; @@ -209,6 +258,14 @@ static void synaptics_ts_work_func(struct work_struct *work) if (!finger) z = 0; +#ifdef CONFIG_TOUCHSCREEN_DUPLICATED_FILTER + // discard duplicate events + ret = duplicated_filter(ts, pos, finger2_pressed, z); + if (ret == 1) { + /* printk("%s: duplicated_filter\n", __func__); */ + break; + } +#endif input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, z); input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); input_report_abs(ts->input_dev, ABS_MT_POSITION_X, pos[0][0]); @@ -363,6 +420,7 @@ static int synaptics_ts_probe( fuzz_y = pdata->fuzz_y; fuzz_p = pdata->fuzz_p; fuzz_w = pdata->fuzz_w; + ts->dup_threshold = pdata->dup_threshold; // adapting filtering } else { irqflags = 0; inactive_area_left = 0; diff --git a/include/linux/synaptics_i2c_rmi.h b/include/linux/synaptics_i2c_rmi.h index 5539cc5207796..206ba75669810 100644 --- a/include/linux/synaptics_i2c_rmi.h +++ b/include/linux/synaptics_i2c_rmi.h @@ -50,6 +50,8 @@ struct synaptics_i2c_rmi_platform_data { int fuzz_p; int fuzz_w; int8_t sensitivity_adjust; + uint32_t dup_threshold; + uint32_t margin_inactive_pixel[4]; }; #endif /* _LINUX_SYNAPTICS_I2C_RMI_H */ From 239d6da4d7277eec4c2f101315f4c07a734e1e22 Mon Sep 17 00:00:00 2001 From: Abaakouk Mehdi Date: Mon, 7 Feb 2011 21:45:03 +0100 Subject: [PATCH 1004/2556] Add bfq iosched Change-Id: Iea63ed97743c4eda9bd924b5e0cd437070314283 --- block/Kconfig.iosched | 26 + block/Makefile | 1 + block/bfq-cgroup.c | 769 +++++++++++ block/bfq-ioc.c | 374 ++++++ block/bfq-iosched.c | 2317 +++++++++++++++++++++++++++++++++ block/bfq-sched.c | 1010 ++++++++++++++ block/bfq.h | 555 ++++++++ block/blk-ioc.c | 30 +- block/cfq-iosched.c | 10 +- fs/ioprio.c | 9 +- include/linux/cgroup_subsys.h | 6 + include/linux/iocontext.h | 18 +- 12 files changed, 5104 insertions(+), 21 deletions(-) create mode 100644 block/bfq-cgroup.c create mode 100644 block/bfq-ioc.c create mode 100644 block/bfq-iosched.c create mode 100644 block/bfq-sched.c create mode 100644 block/bfq.h diff --git a/block/Kconfig.iosched b/block/Kconfig.iosched index 3199b76f795de..59054520e5615 100644 --- a/block/Kconfig.iosched +++ b/block/Kconfig.iosched @@ -43,6 +43,28 @@ config CFQ_GROUP_IOSCHED ---help--- Enable group IO scheduling in CFQ. +config IOSCHED_BFQ + tristate "BFQ I/O scheduler" + depends on EXPERIMENTAL + default n + ---help--- + The BFQ I/O scheduler tries to distribute bandwidth among + all processes according to their weights. + It aims at distributing the bandwidth as desired, independently of + the disk parameters and with any workload. It also tries to + guarantee low latency to interactive and soft real-time + applications. If compiled built-in (saying Y here), BFQ can + be configured to support hierarchical scheduling. + +config CGROUP_BFQIO + bool "BFQ hierarchical scheduling support" + depends on CGROUPS && IOSCHED_BFQ=y + default n + ---help--- + Enable hierarchical scheduling in BFQ, using the cgroups + filesystem interface. The name of the subsystem will be + bfqio. + choice prompt "Default I/O scheduler" default DEFAULT_CFQ @@ -56,6 +78,9 @@ choice config DEFAULT_CFQ bool "CFQ" if IOSCHED_CFQ=y + config DEFAULT_BFQ + bool "BFQ" if IOSCHED_BFQ=y + config DEFAULT_NOOP bool "No-op" @@ -65,6 +90,7 @@ config DEFAULT_IOSCHED string default "deadline" if DEFAULT_DEADLINE default "cfq" if DEFAULT_CFQ + default "bfq" if DEFAULT_BFQ default "noop" if DEFAULT_NOOP endmenu diff --git a/block/Makefile b/block/Makefile index 0fec4b3fab511..22d86087f7f56 100644 --- a/block/Makefile +++ b/block/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_BLK_DEV_THROTTLING) += blk-throttle.o obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o obj-$(CONFIG_IOSCHED_DEADLINE) += deadline-iosched.o obj-$(CONFIG_IOSCHED_CFQ) += cfq-iosched.o +obj-$(CONFIG_IOSCHED_BFQ) += bfq-iosched.o obj-$(CONFIG_BLOCK_COMPAT) += compat_ioctl.o obj-$(CONFIG_BLK_DEV_INTEGRITY) += blk-integrity.o diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c new file mode 100644 index 0000000000000..cc7be6ba4034d --- /dev/null +++ b/block/bfq-cgroup.c @@ -0,0 +1,769 @@ +/* + * BFQ: CGROUPS support. + * + * Based on ideas and code from CFQ: + * Copyright (C) 2003 Jens Axboe + * + * Copyright (C) 2008 Fabio Checconi + * Paolo Valente + * + * Licensed under the GPL-2 as detailed in the accompanying COPYING.BFQ file. + */ + +#ifdef CONFIG_CGROUP_BFQIO +static struct bfqio_cgroup bfqio_root_cgroup = { + .weight = BFQ_DEFAULT_GRP_WEIGHT, + .ioprio = BFQ_DEFAULT_GRP_IOPRIO, + .ioprio_class = BFQ_DEFAULT_GRP_CLASS, +}; + +static inline void bfq_init_entity(struct bfq_entity *entity, + struct bfq_group *bfqg) +{ + entity->weight = entity->new_weight; + entity->orig_weight = entity->new_weight; + entity->ioprio = entity->new_ioprio; + entity->ioprio_class = entity->new_ioprio_class; + entity->parent = bfqg->my_entity; + entity->sched_data = &bfqg->sched_data; +} + +static struct bfqio_cgroup *cgroup_to_bfqio(struct cgroup *cgroup) +{ + return container_of(cgroup_subsys_state(cgroup, bfqio_subsys_id), + struct bfqio_cgroup, css); +} + +/* + * Search the bfq_group for bfqd into the hash table (by now only a list) + * of bgrp. Must be called under rcu_read_lock(). + */ +static struct bfq_group *bfqio_lookup_group(struct bfqio_cgroup *bgrp, + struct bfq_data *bfqd) +{ + struct bfq_group *bfqg; + struct hlist_node *n; + void *key; + + hlist_for_each_entry_rcu(bfqg, n, &bgrp->group_data, group_node) { + key = rcu_dereference(bfqg->bfqd); + if (key == bfqd) + return bfqg; + } + + return NULL; +} + +static inline void bfq_group_init_entity(struct bfqio_cgroup *bgrp, + struct bfq_group *bfqg) +{ + struct bfq_entity *entity = &bfqg->entity; + + entity->weight = entity->new_weight = bgrp->weight; + entity->orig_weight = entity->new_weight; + entity->ioprio = entity->new_ioprio = bgrp->ioprio; + entity->ioprio_class = entity->new_ioprio_class = bgrp->ioprio_class; + entity->ioprio_changed = 1; + entity->my_sched_data = &bfqg->sched_data; +} + +static inline void bfq_group_set_parent(struct bfq_group *bfqg, + struct bfq_group *parent) +{ + struct bfq_entity *entity; + + BUG_ON(parent == NULL); + BUG_ON(bfqg == NULL); + + entity = &bfqg->entity; + entity->parent = parent->my_entity; + entity->sched_data = &parent->sched_data; +} + +/** + * bfq_group_chain_alloc - allocate a chain of groups. + * @bfqd: queue descriptor. + * @cgroup: the leaf cgroup this chain starts from. + * + * Allocate a chain of groups starting from the one belonging to + * @cgroup up to the root cgroup. Stop if a cgroup on the chain + * to the root has already an allocated group on @bfqd. + */ +static struct bfq_group *bfq_group_chain_alloc(struct bfq_data *bfqd, + struct cgroup *cgroup) +{ + struct bfqio_cgroup *bgrp; + struct bfq_group *bfqg, *prev = NULL, *leaf = NULL; + + for (; cgroup != NULL; cgroup = cgroup->parent) { + bgrp = cgroup_to_bfqio(cgroup); + + bfqg = bfqio_lookup_group(bgrp, bfqd); + if (bfqg != NULL) { + /* + * All the cgroups in the path from there to the + * root must have a bfq_group for bfqd, so we don't + * need any more allocations. + */ + break; + } + + bfqg = kzalloc(sizeof(*bfqg), GFP_ATOMIC); + if (bfqg == NULL) + goto cleanup; + + bfq_group_init_entity(bgrp, bfqg); + bfqg->my_entity = &bfqg->entity; + + if (leaf == NULL) { + leaf = bfqg; + prev = leaf; + } else { + bfq_group_set_parent(prev, bfqg); + /* + * Build a list of allocated nodes using the bfqd + * filed, that is still unused and will be initialized + * only after the node will be connected. + */ + prev->bfqd = bfqg; + prev = bfqg; + } + } + + return leaf; + +cleanup: + while (leaf != NULL) { + prev = leaf; + leaf = leaf->bfqd; + kfree(prev); + } + + return NULL; +} + +/** + * bfq_group_chain_link - link an allocatd group chain to a cgroup hierarchy. + * @bfqd: the queue descriptor. + * @cgroup: the leaf cgroup to start from. + * @leaf: the leaf group (to be associated to @cgroup). + * + * Try to link a chain of groups to a cgroup hierarchy, connecting the + * nodes bottom-up, so we can be sure that when we find a cgroup in the + * hierarchy that already as a group associated to @bfqd all the nodes + * in the path to the root cgroup have one too. + * + * On locking: the queue lock protects the hierarchy (there is a hierarchy + * per device) while the bfqio_cgroup lock protects the list of groups + * belonging to the same cgroup. + */ +static void bfq_group_chain_link(struct bfq_data *bfqd, struct cgroup *cgroup, + struct bfq_group *leaf) +{ + struct bfqio_cgroup *bgrp; + struct bfq_group *bfqg, *next, *prev = NULL; + unsigned long flags; + + assert_spin_locked(bfqd->queue->queue_lock); + + for (; cgroup != NULL && leaf != NULL; cgroup = cgroup->parent) { + bgrp = cgroup_to_bfqio(cgroup); + next = leaf->bfqd; + + bfqg = bfqio_lookup_group(bgrp, bfqd); + BUG_ON(bfqg != NULL); + + spin_lock_irqsave(&bgrp->lock, flags); + + rcu_assign_pointer(leaf->bfqd, bfqd); + hlist_add_head_rcu(&leaf->group_node, &bgrp->group_data); + hlist_add_head(&leaf->bfqd_node, &bfqd->group_list); + + spin_unlock_irqrestore(&bgrp->lock, flags); + + prev = leaf; + leaf = next; + } + + BUG_ON(cgroup == NULL && leaf != NULL); + if (cgroup != NULL && prev != NULL) { + bgrp = cgroup_to_bfqio(cgroup); + bfqg = bfqio_lookup_group(bgrp, bfqd); + bfq_group_set_parent(prev, bfqg); + } +} + +/** + * bfq_find_alloc_group - return the group associated to @bfqd in @cgroup. + * @bfqd: queue descriptor. + * @cgroup: cgroup being searched for. + * + * Return a group associated to @bfqd in @cgroup, allocating one if + * necessary. When a group is returned all the cgroups in the path + * to the root have a group associated to @bfqd. + * + * If the allocation fails, return the root group: this breaks guarantees + * but is a safe fallbak. If this loss becames a problem it can be + * mitigated using the equivalent weight (given by the product of the + * weights of the groups in the path from @group to the root) in the + * root scheduler. + * + * We allocate all the missing nodes in the path from the leaf cgroup + * to the root and we connect the nodes only after all the allocations + * have been successful. + */ +static struct bfq_group *bfq_find_alloc_group(struct bfq_data *bfqd, + struct cgroup *cgroup) +{ + struct bfqio_cgroup *bgrp = cgroup_to_bfqio(cgroup); + struct bfq_group *bfqg; + + bfqg = bfqio_lookup_group(bgrp, bfqd); + if (bfqg != NULL) + return bfqg; + + bfqg = bfq_group_chain_alloc(bfqd, cgroup); + if (bfqg != NULL) + bfq_group_chain_link(bfqd, cgroup, bfqg); + else + bfqg = bfqd->root_group; + + return bfqg; +} + +/** + * bfq_bfqq_move - migrate @bfqq to @bfqg. + * @bfqd: queue descriptor. + * @bfqq: the queue to move. + * @entity: @bfqq's entity. + * @bfqg: the group to move to. + * + * Move @bfqq to @bfqg, deactivating it from its old group and reactivating + * it on the new one. Avoid putting the entity on the old group idle tree. + * + * Must be called under the queue lock; the cgroup owning @bfqg must + * not disappear (by now this just means that we are called under + * rcu_read_lock()). + */ +static void bfq_bfqq_move(struct bfq_data *bfqd, struct bfq_queue *bfqq, + struct bfq_entity *entity, struct bfq_group *bfqg) +{ + int busy, resume; + + busy = bfq_bfqq_busy(bfqq); + resume = !RB_EMPTY_ROOT(&bfqq->sort_list); + + BUG_ON(resume && !entity->on_st); + BUG_ON(busy && !resume && entity->on_st && bfqq != bfqd->active_queue); + + if (busy) { + BUG_ON(atomic_read(&bfqq->ref) < 2); + + if (!resume) + bfq_del_bfqq_busy(bfqd, bfqq, 0); + else + bfq_deactivate_bfqq(bfqd, bfqq, 0); + } + + /* + * Here we use a reference to bfqg. We don't need a refcounter + * as the cgroup reference will not be dropped, so that its + * destroy() callback will not be invoked. + */ + entity->parent = bfqg->my_entity; + entity->sched_data = &bfqg->sched_data; + + if (busy && resume) + bfq_activate_bfqq(bfqd, bfqq); +} + +/** + * __bfq_cic_change_cgroup - move @cic to @cgroup. + * @bfqd: the queue descriptor. + * @cic: the cic to move. + * @cgroup: the cgroup to move to. + * + * Move cic to cgroup, assuming that bfqd->queue is locked; the caller + * has to make sure that the reference to cgroup is valid across the call. + * + * NOTE: an alternative approach might have been to store the current + * cgroup in bfqq and getting a reference to it, reducing the lookup + * time here, at the price of slightly more complex code. + */ +static struct bfq_group *__bfq_cic_change_cgroup(struct bfq_data *bfqd, + struct cfq_io_context *cic, + struct cgroup *cgroup) +{ + struct bfq_queue *async_bfqq = cic_to_bfqq(cic, 0); + struct bfq_queue *sync_bfqq = cic_to_bfqq(cic, 1); + struct bfq_entity *entity; + struct bfq_group *bfqg; + struct bfqio_cgroup *bgrp; + + bgrp = cgroup_to_bfqio(cgroup); + + bfqg = bfq_find_alloc_group(bfqd, cgroup); + if (async_bfqq != NULL) { + entity = &async_bfqq->entity; + + if (entity->sched_data != &bfqg->sched_data) { + cic_set_bfqq(cic, NULL, 0); + bfq_log_bfqq(bfqd, async_bfqq, + "cic_change_group: %p %d", + async_bfqq, async_bfqq->ref); + bfq_put_queue(async_bfqq); + } + } + + if (sync_bfqq != NULL) { + entity = &sync_bfqq->entity; + if (entity->sched_data != &bfqg->sched_data) + bfq_bfqq_move(bfqd, sync_bfqq, entity, bfqg); + } + + return bfqg; +} + +/** + * bfq_cic_change_cgroup - move @cic to @cgroup. + * @cic: the cic being migrated. + * @cgroup: the destination cgroup. + * + * When the task owning @cic is moved to @cgroup, @cic is immediately + * moved into its new parent group. + */ +static void bfq_cic_change_cgroup(struct cfq_io_context *cic, + struct cgroup *cgroup) +{ + struct bfq_data *bfqd; + unsigned long uninitialized_var(flags); + + bfqd = bfq_get_bfqd_locked(&cic->key, &flags); + if (bfqd != NULL) { + __bfq_cic_change_cgroup(bfqd, cic, cgroup); + bfq_put_bfqd_unlock(bfqd, &flags); + } +} + +/** + * bfq_cic_update_cgroup - update the cgroup of @cic. + * @cic: the @cic to update. + * + * Make sure that @cic is enqueued in the cgroup of the current task. + * We need this in addition to moving cics during the cgroup attach + * phase because the task owning @cic could be at its first disk + * access or we may end up in the root cgroup as the result of a + * memory allocation failure and here we try to move to the right + * group. + * + * Must be called under the queue lock. It is safe to use the returned + * value even after the rcu_read_unlock() as the migration/destruction + * paths act under the queue lock too. IOW it is impossible to race with + * group migration/destruction and end up with an invalid group as: + * a) here cgroup has not yet been destroyed, nor its destroy callback + * has started execution, as current holds a reference to it, + * b) if it is destroyed after rcu_read_unlock() [after current is + * migrated to a different cgroup] its attach() callback will have + * taken care of remove all the references to the old cgroup data. + */ +static struct bfq_group *bfq_cic_update_cgroup(struct cfq_io_context *cic) +{ + struct bfq_data *bfqd = cic->key; + struct bfq_group *bfqg; + struct cgroup *cgroup; + + BUG_ON(bfqd == NULL); + + rcu_read_lock(); + cgroup = task_cgroup(current, bfqio_subsys_id); + bfqg = __bfq_cic_change_cgroup(bfqd, cic, cgroup); + rcu_read_unlock(); + + return bfqg; +} + +/** + * bfq_flush_idle_tree - deactivate any entity on the idle tree of @st. + * @st: the service tree being flushed. + */ +static inline void bfq_flush_idle_tree(struct bfq_service_tree *st) +{ + struct bfq_entity *entity = st->first_idle; + + for (; entity != NULL; entity = st->first_idle) + __bfq_deactivate_entity(entity, 0); +} + +/** + * bfq_destroy_group - destroy @bfqg. + * @bgrp: the bfqio_cgroup containing @bfqg. + * @bfqg: the group being destroyed. + * + * Destroy @bfqg, making sure that it is not referenced from its parent. + */ +static void bfq_destroy_group(struct bfqio_cgroup *bgrp, struct bfq_group *bfqg) +{ + struct bfq_data *bfqd; + struct bfq_service_tree *st; + struct bfq_entity *entity = bfqg->my_entity; + unsigned long uninitialized_var(flags); + int i; + + hlist_del(&bfqg->group_node); + + /* + * We may race with device destruction, take extra care when + * dereferencing bfqg->bfqd. + */ + bfqd = bfq_get_bfqd_locked(&bfqg->bfqd, &flags); + if (bfqd != NULL) { + hlist_del(&bfqg->bfqd_node); + __bfq_deactivate_entity(entity, 0); + bfq_put_async_queues(bfqd, bfqg); + bfq_put_bfqd_unlock(bfqd, &flags); + } + + for (i = 0; i < BFQ_IOPRIO_CLASSES; i++) { + st = bfqg->sched_data.service_tree + i; + + /* + * The idle tree may still contain bfq_queues belonging + * to exited task because they never migrated to a different + * cgroup from the one being destroyed now. Noone else + * can access them so it's safe to act without any lock. + */ + bfq_flush_idle_tree(st); + + BUG_ON(!RB_EMPTY_ROOT(&st->active)); + BUG_ON(!RB_EMPTY_ROOT(&st->idle)); + } + BUG_ON(bfqg->sched_data.next_active != NULL); + BUG_ON(bfqg->sched_data.active_entity != NULL); + BUG_ON(entity->tree != NULL); + + /* + * No need to defer the kfree() to the end of the RCU grace + * period: we are called from the destroy() callback of our + * cgroup, so we can be sure that noone is a) still using + * this cgroup or b) doing lookups in it. + */ + kfree(bfqg); +} + +/** + * bfq_disconnect_groups - diconnect @bfqd from all its groups. + * @bfqd: the device descriptor being exited. + * + * When the device exits we just make sure that no lookup can return + * the now unused group structures. They will be deallocated on cgroup + * destruction. + */ +static void bfq_disconnect_groups(struct bfq_data *bfqd) +{ + struct hlist_node *pos, *n; + struct bfq_group *bfqg; + + bfq_log(bfqd, "disconnect_groups beginning") ; + hlist_for_each_entry_safe(bfqg, pos, n, &bfqd->group_list, bfqd_node) { + hlist_del(&bfqg->bfqd_node); + + __bfq_deactivate_entity(bfqg->my_entity, 0); + + /* + * Don't remove from the group hash, just set an + * invalid key. No lookups can race with the + * assignment as bfqd is being destroyed; this + * implies also that new elements cannot be added + * to the list. + */ + rcu_assign_pointer(bfqg->bfqd, NULL); + + bfq_log(bfqd, "disconnect_groups: put async for group %p", + bfqg) ; + bfq_put_async_queues(bfqd, bfqg); + } +} + +static inline void bfq_free_root_group(struct bfq_data *bfqd) +{ + struct bfqio_cgroup *bgrp = &bfqio_root_cgroup; + struct bfq_group *bfqg = bfqd->root_group; + + bfq_put_async_queues(bfqd, bfqg); + + spin_lock_irq(&bgrp->lock); + hlist_del_rcu(&bfqg->group_node); + spin_unlock_irq(&bgrp->lock); + + /* + * No need to synchronize_rcu() here: since the device is gone + * there cannot be any read-side access to its root_group. + */ + kfree(bfqg); +} + +static struct bfq_group *bfq_alloc_root_group(struct bfq_data *bfqd, int node) +{ + struct bfq_group *bfqg; + struct bfqio_cgroup *bgrp; + int i; + + bfqg = kmalloc_node(sizeof(*bfqg), GFP_KERNEL | __GFP_ZERO, node); + if (bfqg == NULL) + return NULL; + + bfqg->entity.parent = NULL; + for (i = 0; i < BFQ_IOPRIO_CLASSES; i++) + bfqg->sched_data.service_tree[i] = BFQ_SERVICE_TREE_INIT; + + bgrp = &bfqio_root_cgroup; + spin_lock_irq(&bgrp->lock); + rcu_assign_pointer(bfqg->bfqd, bfqd); + hlist_add_head_rcu(&bfqg->group_node, &bgrp->group_data); + spin_unlock_irq(&bgrp->lock); + + return bfqg; +} + +#define SHOW_FUNCTION(__VAR) \ +static u64 bfqio_cgroup_##__VAR##_read(struct cgroup *cgroup, \ + struct cftype *cftype) \ +{ \ + struct bfqio_cgroup *bgrp; \ + u64 ret; \ + \ + if (!cgroup_lock_live_group(cgroup)) \ + return -ENODEV; \ + \ + bgrp = cgroup_to_bfqio(cgroup); \ + spin_lock_irq(&bgrp->lock); \ + ret = bgrp->__VAR; \ + spin_unlock_irq(&bgrp->lock); \ + \ + cgroup_unlock(); \ + \ + return ret; \ +} + +SHOW_FUNCTION(weight); +SHOW_FUNCTION(ioprio); +SHOW_FUNCTION(ioprio_class); +#undef SHOW_FUNCTION + +#define STORE_FUNCTION(__VAR, __MIN, __MAX) \ +static int bfqio_cgroup_##__VAR##_write(struct cgroup *cgroup, \ + struct cftype *cftype, \ + u64 val) \ +{ \ + struct bfqio_cgroup *bgrp; \ + struct bfq_group *bfqg; \ + struct hlist_node *n; \ + \ + if (val < (__MIN) || val > (__MAX)) \ + return -EINVAL; \ + \ + if (!cgroup_lock_live_group(cgroup)) \ + return -ENODEV; \ + \ + bgrp = cgroup_to_bfqio(cgroup); \ + \ + spin_lock_irq(&bgrp->lock); \ + bgrp->__VAR = (unsigned short)val; \ + hlist_for_each_entry(bfqg, n, &bgrp->group_data, group_node) { \ + bfqg->entity.new_##__VAR = (unsigned short)val; \ + smp_wmb(); \ + bfqg->entity.ioprio_changed = 1; \ + } \ + spin_unlock_irq(&bgrp->lock); \ + \ + cgroup_unlock(); \ + \ + return 0; \ +} + +STORE_FUNCTION(weight, BFQ_MIN_WEIGHT, BFQ_MAX_WEIGHT); +STORE_FUNCTION(ioprio, 0, IOPRIO_BE_NR - 1); +STORE_FUNCTION(ioprio_class, IOPRIO_CLASS_RT, IOPRIO_CLASS_IDLE); +#undef STORE_FUNCTION + +static struct cftype bfqio_files[] = { + { + .name = "weight", + .read_u64 = bfqio_cgroup_weight_read, + .write_u64 = bfqio_cgroup_weight_write, + }, + { + .name = "ioprio", + .read_u64 = bfqio_cgroup_ioprio_read, + .write_u64 = bfqio_cgroup_ioprio_write, + }, + { + .name = "ioprio_class", + .read_u64 = bfqio_cgroup_ioprio_class_read, + .write_u64 = bfqio_cgroup_ioprio_class_write, + }, +}; + +static int bfqio_populate(struct cgroup_subsys *subsys, struct cgroup *cgroup) +{ + return cgroup_add_files(cgroup, subsys, bfqio_files, + ARRAY_SIZE(bfqio_files)); +} + +static struct cgroup_subsys_state *bfqio_create(struct cgroup_subsys *subsys, + struct cgroup *cgroup) +{ + struct bfqio_cgroup *bgrp; + + if (cgroup->parent != NULL) { + bgrp = kzalloc(sizeof(*bgrp), GFP_KERNEL); + if (bgrp == NULL) + return ERR_PTR(-ENOMEM); + } else + bgrp = &bfqio_root_cgroup; + + spin_lock_init(&bgrp->lock); + INIT_HLIST_HEAD(&bgrp->group_data); + bgrp->ioprio = BFQ_DEFAULT_GRP_IOPRIO; + bgrp->ioprio_class = BFQ_DEFAULT_GRP_CLASS; + + return &bgrp->css; +} + +/* + * We cannot support shared io contexts, as we have no mean to support + * two tasks with the same ioc in two different groups without major rework + * of the main cic/bfqq data structures. By now we allow a task to change + * its cgroup only if it's the only owner of its ioc; the drawback of this + * behavior is that a group containing a task that forked using CLONE_IO + * will not be destroyed until the tasks sharing the ioc die. + */ +static int bfqio_can_attach(struct cgroup_subsys *subsys, struct cgroup *cgroup, + struct task_struct *tsk, bool threadgroup) +{ + struct io_context *ioc; + int ret = 0; + + /* task_lock() is needed to avoid races with exit_io_context() */ + task_lock(tsk); + ioc = tsk->io_context; + if (ioc != NULL && atomic_read(&ioc->nr_tasks) > 1) + /* + * ioc == NULL means that the task is either too young or + * exiting: if it has still no ioc the ioc can't be shared, + * if the task is exiting the attach will fail anyway, no + * matter what we return here. + */ + ret = -EINVAL; + task_unlock(tsk); + + return ret; +} + +static void bfqio_attach(struct cgroup_subsys *subsys, struct cgroup *cgroup, + struct cgroup *prev, struct task_struct *tsk, + bool threadgroup) +{ + struct io_context *ioc; + struct cfq_io_context *cic; + struct hlist_node *n; + + task_lock(tsk); + ioc = tsk->io_context; + if (ioc != NULL) { + BUG_ON(atomic_long_read(&ioc->refcount) == 0); + atomic_long_inc(&ioc->refcount); + } + task_unlock(tsk); + + if (ioc == NULL) + return; + + rcu_read_lock(); + hlist_for_each_entry_rcu(cic, n, &ioc->bfq_cic_list, cic_list) + bfq_cic_change_cgroup(cic, cgroup); + rcu_read_unlock(); + + put_io_context(ioc); +} + +static void bfqio_destroy(struct cgroup_subsys *subsys, struct cgroup *cgroup) +{ + struct bfqio_cgroup *bgrp = cgroup_to_bfqio(cgroup); + struct hlist_node *n, *tmp; + struct bfq_group *bfqg; + + /* + * Since we are destroying the cgroup, there are no more tasks + * referencing it, and all the RCU grace periods that may have + * referenced it are ended (as the destruction of the parent + * cgroup is RCU-safe); bgrp->group_data will not be accessed by + * anything else and we don't need any synchronization. + */ + hlist_for_each_entry_safe(bfqg, n, tmp, &bgrp->group_data, group_node) + bfq_destroy_group(bgrp, bfqg); + + BUG_ON(!hlist_empty(&bgrp->group_data)); + + kfree(bgrp); +} + +struct cgroup_subsys bfqio_subsys = { + .name = "bfqio", + .create = bfqio_create, + .can_attach = bfqio_can_attach, + .attach = bfqio_attach, + .destroy = bfqio_destroy, + .populate = bfqio_populate, + .subsys_id = bfqio_subsys_id, +}; +#else +static inline void bfq_init_entity(struct bfq_entity *entity, + struct bfq_group *bfqg) +{ + entity->weight = entity->new_weight; + entity->orig_weight = entity->new_weight; + entity->ioprio = entity->new_ioprio; + entity->ioprio_class = entity->new_ioprio_class; + entity->sched_data = &bfqg->sched_data; +} + +static inline struct bfq_group * +bfq_cic_update_cgroup(struct cfq_io_context *cic) +{ + struct bfq_data *bfqd = cic->key; + return bfqd->root_group; +} + +static inline void bfq_bfqq_move(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + struct bfq_entity *entity, + struct bfq_group *bfqg) +{ +} + +static inline void bfq_disconnect_groups(struct bfq_data *bfqd) +{ + bfq_put_async_queues(bfqd, bfqd->root_group); +} + +static inline void bfq_free_root_group(struct bfq_data *bfqd) +{ + kfree(bfqd->root_group); +} + +static struct bfq_group *bfq_alloc_root_group(struct bfq_data *bfqd, int node) +{ + struct bfq_group *bfqg; + int i; + + bfqg = kmalloc_node(sizeof(*bfqg), GFP_KERNEL | __GFP_ZERO, node); + if (bfqg == NULL) + return NULL; + + for (i = 0; i < BFQ_IOPRIO_CLASSES; i++) + bfqg->sched_data.service_tree[i] = BFQ_SERVICE_TREE_INIT; + + return bfqg; +} +#endif diff --git a/block/bfq-ioc.c b/block/bfq-ioc.c new file mode 100644 index 0000000000000..9d06cebde960e --- /dev/null +++ b/block/bfq-ioc.c @@ -0,0 +1,374 @@ +/* + * BFQ: I/O context handling. + * + * Based on ideas and code from CFQ: + * Copyright (C) 2003 Jens Axboe + * + * Copyright (C) 2008 Fabio Checconi + * Paolo Valente + */ + +/** + * bfq_cic_free_rcu - deferred cic freeing. + * @head: RCU head of the cic to free. + * + * Free the cic containing @head and, if it was the last one and + * the module is exiting wake up anyone waiting for its deallocation + * (see bfq_exit()). + */ +static void bfq_cic_free_rcu(struct rcu_head *head) +{ + struct cfq_io_context *cic; + + cic = container_of(head, struct cfq_io_context, rcu_head); + + kmem_cache_free(bfq_ioc_pool, cic); + elv_ioc_count_dec(bfq_ioc_count); + + if (bfq_ioc_gone != NULL) { + spin_lock(&bfq_ioc_gone_lock); + if (bfq_ioc_gone != NULL && + !elv_ioc_count_read(bfq_ioc_count)) { + complete(bfq_ioc_gone); + bfq_ioc_gone = NULL; + } + spin_unlock(&bfq_ioc_gone_lock); + } +} + +static void bfq_cic_free(struct cfq_io_context *cic) +{ + call_rcu(&cic->rcu_head, bfq_cic_free_rcu); +} + +/** + * cic_free_func - disconnect a cic ready to be freed. + * @ioc: the io_context @cic belongs to. + * @cic: the cic to be freed. + * + * Remove @cic from the @ioc radix tree hash and from its cic list, + * deferring the deallocation of @cic to the end of the current RCU + * grace period. This assumes that __bfq_exit_single_io_context() + * has already been called for @cic. + */ +static void cic_free_func(struct io_context *ioc, struct cfq_io_context *cic) +{ + unsigned long flags; + unsigned long dead_key = (unsigned long) cic->key; + + BUG_ON(!(dead_key & CIC_DEAD_KEY)); + + spin_lock_irqsave(&ioc->lock, flags); + radix_tree_delete(&ioc->bfq_radix_root, + dead_key >> CIC_DEAD_INDEX_SHIFT); + hlist_del_init_rcu(&cic->cic_list); + spin_unlock_irqrestore(&ioc->lock, flags); + + bfq_cic_free(cic); +} + +static void bfq_free_io_context(struct io_context *ioc) +{ + /* + * ioc->refcount is zero here, or we are called from elv_unregister(), + * so no more cic's are allowed to be linked into this ioc. So it + * should be ok to iterate over the known list, we will see all cic's + * since no new ones are added. + */ + call_for_each_cic(ioc, cic_free_func); +} + +/** + * __bfq_exit_single_io_context - deassociate @cic from any running task. + * @bfqd: bfq_data on which @cic is valid. + * @cic: the cic being exited. + * + * Whenever no more tasks are using @cic or @bfqd is deallocated we + * need to invalidate its entry in the radix tree hash table and to + * release the queues it refers to. + * + * Called under the queue lock. + */ +static void __bfq_exit_single_io_context(struct bfq_data *bfqd, + struct cfq_io_context *cic) +{ + struct io_context *ioc = cic->ioc; + + list_del_init(&cic->queue_list); + + /* + * Make sure dead mark is seen for dead queues + */ + smp_wmb(); + rcu_assign_pointer(cic->key, bfqd_dead_key(bfqd)); + + /* + * No write-side locking as no task is using @ioc (they're exited + * or bfqd is being deallocated. + */ + if (ioc->ioc_data == cic) + rcu_assign_pointer(ioc->ioc_data, NULL); + + if (cic->cfqq[BLK_RW_ASYNC] != NULL) { + bfq_exit_bfqq(bfqd, cic->cfqq[BLK_RW_ASYNC]); + cic->cfqq[BLK_RW_ASYNC] = NULL; + } + + if (cic->cfqq[BLK_RW_SYNC] != NULL) { + bfq_exit_bfqq(bfqd, cic->cfqq[BLK_RW_SYNC]); + cic->cfqq[BLK_RW_SYNC] = NULL; + } +} + +/** + * bfq_exit_single_io_context - deassociate @cic from @ioc (unlocked version). + * @ioc: the io_context @cic belongs to. + * @cic: the cic being exited. + * + * Take the queue lock and call __bfq_exit_single_io_context() to do the + * rest of the work. We take care of possible races with bfq_exit_queue() + * using bfq_get_bfqd_locked() (and abusing a little bit the RCU mechanism). + */ +static void bfq_exit_single_io_context(struct io_context *ioc, + struct cfq_io_context *cic) +{ + struct bfq_data *bfqd; + unsigned long uninitialized_var(flags); + + bfqd = bfq_get_bfqd_locked(&cic->key, &flags); + if (bfqd != NULL) { + __bfq_exit_single_io_context(bfqd, cic); + bfq_put_bfqd_unlock(bfqd, &flags); + } +} + +/** + * bfq_exit_io_context - deassociate @ioc from all cics it owns. + * @ioc: the @ioc being exited. + * + * No more processes are using @ioc we need to clean up and put the + * internal structures we have that belongs to that process. Loop + * through all its cics, locking their queues and exiting them. + */ +static void bfq_exit_io_context(struct io_context *ioc) +{ + call_for_each_cic(ioc, bfq_exit_single_io_context); +} + +static struct cfq_io_context *bfq_alloc_io_context(struct bfq_data *bfqd, + gfp_t gfp_mask) +{ + struct cfq_io_context *cic; + + cic = kmem_cache_alloc_node(bfq_ioc_pool, gfp_mask | __GFP_ZERO, + bfqd->queue->node); + if (cic != NULL) { + cic->last_end_request = jiffies; + INIT_LIST_HEAD(&cic->queue_list); + INIT_HLIST_NODE(&cic->cic_list); + cic->dtor = bfq_free_io_context; + cic->exit = bfq_exit_io_context; + elv_ioc_count_inc(bfq_ioc_count); + } + + return cic; +} + +/** + * bfq_drop_dead_cic - free an exited cic. + * @bfqd: bfq data for the device in use. + * @ioc: io_context owning @cic. + * @cic: the @cic to free. + * + * We drop cfq io contexts lazily, so we may find a dead one. + */ +static void bfq_drop_dead_cic(struct bfq_data *bfqd, struct io_context *ioc, + struct cfq_io_context *cic) +{ + unsigned long flags; + + WARN_ON(!list_empty(&cic->queue_list)); + BUG_ON(cic->key != bfqd_dead_key(bfqd)); + + spin_lock_irqsave(&ioc->lock, flags); + + BUG_ON(ioc->ioc_data == cic); + + /* + * With shared I/O contexts two lookups may race and drop the + * same cic more than one time: RCU guarantees that the storage + * will not be freed too early, here we make sure that we do + * not try to remove the cic from the hashing structures multiple + * times. + */ + if (!hlist_unhashed(&cic->cic_list)) { + radix_tree_delete(&ioc->bfq_radix_root, bfqd->cic_index); + hlist_del_init_rcu(&cic->cic_list); + bfq_cic_free(cic); + } + + spin_unlock_irqrestore(&ioc->lock, flags); +} + +/** + * bfq_cic_lookup - search into @ioc a cic associated to @bfqd. + * @bfqd: the lookup key. + * @ioc: the io_context of the process doing I/O. + * + * If @ioc already has a cic associated to @bfqd return it, return %NULL + * otherwise. + */ +static struct cfq_io_context *bfq_cic_lookup(struct bfq_data *bfqd, + struct io_context *ioc) +{ + struct cfq_io_context *cic; + unsigned long flags; + void *k; + + if (unlikely(ioc == NULL)) + return NULL; + + rcu_read_lock(); + + /* We maintain a last-hit cache, to avoid browsing over the tree. */ + cic = rcu_dereference(ioc->ioc_data); + if (cic != NULL) { + k = rcu_dereference(cic->key); + if (k == bfqd) + goto out; + } + + do { + cic = radix_tree_lookup(&ioc->bfq_radix_root, + bfqd->cic_index); + if (cic == NULL) + goto out; + + k = rcu_dereference(cic->key); + if (unlikely(k != bfqd)) { + rcu_read_unlock(); + bfq_drop_dead_cic(bfqd, ioc, cic); + rcu_read_lock(); + continue; + } + + spin_lock_irqsave(&ioc->lock, flags); + rcu_assign_pointer(ioc->ioc_data, cic); + spin_unlock_irqrestore(&ioc->lock, flags); + break; + } while (1); + +out: + rcu_read_unlock(); + + return cic; +} + +/** + * bfq_cic_link - add @cic to @ioc. + * @bfqd: bfq_data @cic refers to. + * @ioc: io_context @cic belongs to. + * @cic: the cic to link. + * @gfp_mask: the mask to use for radix tree preallocations. + * + * Add @cic to @ioc, using @bfqd as the search key. This enables us to + * lookup the process specific cfq io context when entered from the block + * layer. Also adds @cic to a per-bfqd list, used when this queue is + * removed. + */ +static int bfq_cic_link(struct bfq_data *bfqd, struct io_context *ioc, + struct cfq_io_context *cic, gfp_t gfp_mask) +{ + unsigned long flags; + int ret; + + ret = radix_tree_preload(gfp_mask); + if (ret == 0) { + cic->ioc = ioc; + + /* No write-side locking, cic is not published yet. */ + rcu_assign_pointer(cic->key, bfqd); + + spin_lock_irqsave(&ioc->lock, flags); + ret = radix_tree_insert(&ioc->bfq_radix_root, + bfqd->cic_index, cic); + if (ret == 0) + hlist_add_head_rcu(&cic->cic_list, &ioc->bfq_cic_list); + spin_unlock_irqrestore(&ioc->lock, flags); + + radix_tree_preload_end(); + + if (ret == 0) { + spin_lock_irqsave(bfqd->queue->queue_lock, flags); + list_add(&cic->queue_list, &bfqd->cic_list); + spin_unlock_irqrestore(bfqd->queue->queue_lock, flags); + } + } + + if (ret != 0) + printk(KERN_ERR "bfq: cic link failed!\n"); + + return ret; +} + +/** + * bfq_ioc_set_ioprio - signal a priority change to the cics belonging to @ioc. + * @ioc: the io_context changing its priority. + */ +static inline void bfq_ioc_set_ioprio(struct io_context *ioc) +{ + call_for_each_cic(ioc, bfq_changed_ioprio); +} + +/** + * bfq_get_io_context - return the @cic associated to @bfqd in @ioc. + * @bfqd: the search key. + * @gfp_mask: the mask to use for cic allocation. + * + * Setup general io context and cfq io context. There can be several cfq + * io contexts per general io context, if this process is doing io to more + * than one device managed by cfq. + */ +static struct cfq_io_context *bfq_get_io_context(struct bfq_data *bfqd, + gfp_t gfp_mask) +{ + struct io_context *ioc = NULL; + struct cfq_io_context *cic; + + might_sleep_if(gfp_mask & __GFP_WAIT); + + ioc = get_io_context(gfp_mask, bfqd->queue->node); + if (ioc == NULL) + return NULL; + + /* Lookup for an existing cic. */ + cic = bfq_cic_lookup(bfqd, ioc); + if (cic != NULL) + goto out; + + /* Alloc one if needed. */ + cic = bfq_alloc_io_context(bfqd, gfp_mask); + if (cic == NULL) + goto err; + + /* Link it into the ioc's radix tree and cic list. */ + if (bfq_cic_link(bfqd, ioc, cic, gfp_mask) != 0) + goto err_free; + +out: + /* + * test_and_clear_bit() implies a memory barrier, paired with + * the wmb() in fs/ioprio.c, so the value seen for ioprio is the + * new one. + */ + if (unlikely(test_and_clear_bit(IOC_BFQ_IOPRIO_CHANGED, + ioc->ioprio_changed))) + bfq_ioc_set_ioprio(ioc); + + return cic; +err_free: + bfq_cic_free(cic); +err: + put_io_context(ioc); + return NULL; +} diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c new file mode 100644 index 0000000000000..6a0875b734a96 --- /dev/null +++ b/block/bfq-iosched.c @@ -0,0 +1,2317 @@ +/* + * BFQ, or Budget Fair Queueing, disk scheduler. + * + * Based on ideas and code from CFQ: + * Copyright (C) 2003 Jens Axboe + * + * Copyright (C) 2008 Fabio Checconi + * Paolo Valente + * + * Licensed under the GPL-2 as detailed in the accompanying COPYING.BFQ file. + * + * BFQ is a proportional share disk scheduling algorithm based on the + * slice-by-slice service scheme of CFQ. But BFQ assigns budgets, + * measured in number of sectors, to tasks instead of time slices. + * The disk is not granted to the active task for a given time slice, + * but until it has exahusted its assigned budget. This change from + * the time to the service domain allows BFQ to distribute the disk + * bandwidth among tasks as desired, without any distortion due to + * ZBR, workload fluctuations or other factors. BFQ uses an ad hoc + * internal scheduler, called B-WF2Q+, to schedule tasks according to + * their budgets. Thanks to this accurate scheduler, BFQ can afford + * to assign high budgets to disk-bound non-seeky tasks (to boost the + * throughput), and yet guarantee low latencies to interactive and + * soft real-time applications. + * + * BFQ has been introduced in [1], where the interested reader can + * find an accurate description of the algorithm, the bandwidth + * distribution and latency guarantees it provides, plus formal proofs + * of all the properties. With respect to the algorithm presented in + * the paper, this implementation adds several little heuristics, and + * a hierarchical extension, based on H-WF2Q+. + * + * B-WF2Q+ is based on WF2Q+, that is described in [2], together with + * H-WF2Q+, while the augmented tree used to implement B-WF2Q+ with O(log N) + * complexity derives from the one introduced with EEVDF in [3]. + * + * [1] P. Valente and F. Checconi, ``High Throughput Disk Scheduling + * with Deterministic Guarantees on Bandwidth Distribution,'' to appear + * on IEEE Transactions on Computer. + * + * http://algo.ing.unimo.it/people/paolo/disk_sched/bfq.pdf + * + * [2] Jon C.R. Bennett and H. Zhang, ``Hierarchical Packet Fair Queueing + * Algorithms,'' IEEE/ACM Transactions on Networking, 5(5):675-689, + * Oct 1997. + * + * http://www.cs.cmu.edu/~hzhang/papers/TON-97-Oct.ps.gz + * + * [3] I. Stoica and H. Abdel-Wahab, ``Earliest Eligible Virtual Deadline + * First: A Flexible and Accurate Mechanism for Proportional Share + * Resource Allocation,'' technical report. + * + * http://www.cs.berkeley.edu/~istoica/papers/eevdf-tr-95.pdf + */ +#include +#include +#include +#include +#include +#include +#include +#include "bfq.h" + +/* Max number of dispatches in one round of service. */ +static const int bfq_quantum = 4; + +/* Expiration time of sync (0) and async (1) requests, in jiffies. */ +static const int bfq_fifo_expire[2] = { HZ / 4, HZ / 8 }; + +/* Maximum backwards seek, in KiB. */ +static const int bfq_back_max = 16 * 1024; + +/* Penalty of a backwards seek, in number of sectors. */ +static const int bfq_back_penalty = 2; + +/* Idling period duration, in jiffies. */ +static int bfq_slice_idle = HZ / 125; + +/* Default maximum budget values, in sectors and number of requests. */ +static const int bfq_default_max_budget = 16 * 1024; +static const int bfq_max_budget_async_rq = 4; + +/* + * Async to sync throughput distribution is controlled as follows: + * when an async request is served, the entity is charged the number + * of sectors of the request, multipled by the factor below + */ +static const int bfq_async_charge_factor = 10; + +/* Default timeout values, in jiffies, approximating CFQ defaults. */ +static const int bfq_timeout_sync = HZ / 8; +static int bfq_timeout_async = HZ / 25; + +struct kmem_cache *bfq_pool; +struct kmem_cache *bfq_ioc_pool; + +static DEFINE_PER_CPU(unsigned long, bfq_ioc_count); +static struct completion *bfq_ioc_gone; +static DEFINE_SPINLOCK(bfq_ioc_gone_lock); + +static DEFINE_SPINLOCK(cic_index_lock); +static DEFINE_IDA(cic_index_ida); + +/* Below this threshold (in ms), we consider thinktime immediate. */ +#define BFQ_MIN_TT 2 + +/* hw_tag detection: parallel requests threshold and min samples needed. */ +#define BFQ_HW_QUEUE_THRESHOLD 4 +#define BFQ_HW_QUEUE_SAMPLES 32 + +#define BFQQ_SEEKY(bfqq) ((bfqq)->seek_mean > (8 * 1024)) + +/* Min samples used for peak rate estimation (for autotuning). */ +#define BFQ_PEAK_RATE_SAMPLES 32 + +/* Shift used for peak rate fixed precision calculations. */ +#define BFQ_RATE_SHIFT 16 + +#define BFQ_SERVICE_TREE_INIT ((struct bfq_service_tree) \ + { RB_ROOT, RB_ROOT, NULL, NULL, 0, 0 }) + +#define RQ_CIC(rq) \ + ((struct cfq_io_context *) (rq)->elevator_private) +#define RQ_BFQQ(rq) ((rq)->elevator_private2) + +#include "bfq-ioc.c" +#include "bfq-sched.c" +#include "bfq-cgroup.c" + +#define bfq_class_idle(cfqq) ((bfqq)->entity.ioprio_class ==\ + IOPRIO_CLASS_IDLE) + +#define bfq_sample_valid(samples) ((samples) > 80) + +/* + * We regard a request as SYNC, if either it's a read or has the SYNC bit + * set (in which case it could also be a direct WRITE). + */ +static inline int bfq_bio_sync(struct bio *bio) +{ + if (bio_data_dir(bio) == READ || (bio->bi_rw & REQ_SYNC)) + return 1; + + return 0; +} + +/* + * Scheduler run of queue, if there are requests pending and no one in the + * driver that will restart queueing. + */ +static inline void bfq_schedule_dispatch(struct bfq_data *bfqd) +{ + if (bfqd->queued != 0) { + bfq_log(bfqd, "schedule dispatch"); + kblockd_schedule_work(bfqd->queue, &bfqd->unplug_work); + } +} + +static inline int bfq_queue_empty(struct request_queue *q) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + + return bfqd->queued == 0; +} + +/* + * Lifted from AS - choose which of rq1 and rq2 that is best served now. + * We choose the request that is closesr to the head right now. Distance + * behind the head is penalized and only allowed to a certain extent. + */ +static struct request *bfq_choose_req(struct bfq_data *bfqd, + struct request *rq1, + struct request *rq2) +{ + sector_t last, s1, s2, d1 = 0, d2 = 0; + unsigned long back_max; +#define BFQ_RQ1_WRAP 0x01 /* request 1 wraps */ +#define BFQ_RQ2_WRAP 0x02 /* request 2 wraps */ + unsigned wrap = 0; /* bit mask: requests behind the disk head? */ + + if (rq1 == NULL || rq1 == rq2) + return rq2; + if (rq2 == NULL) + return rq1; + + if (rq_is_sync(rq1) && !rq_is_sync(rq2)) + return rq1; + else if (rq_is_sync(rq2) && !rq_is_sync(rq1)) + return rq2; + if ((rq1->cmd_flags & REQ_META) && !(rq2->cmd_flags & REQ_META)) + return rq1; + else if ((rq2->cmd_flags & REQ_META) && !(rq1->cmd_flags & REQ_META)) + return rq2; + + s1 = blk_rq_pos(rq1); + s2 = blk_rq_pos(rq2); + + last = bfqd->last_position; + + /* + * By definition, 1KiB is 2 sectors. + */ + back_max = bfqd->bfq_back_max * 2; + + /* + * Strict one way elevator _except_ in the case where we allow + * short backward seeks which are biased as twice the cost of a + * similar forward seek. + */ + if (s1 >= last) + d1 = s1 - last; + else if (s1 + back_max >= last) + d1 = (last - s1) * bfqd->bfq_back_penalty; + else + wrap |= BFQ_RQ1_WRAP; + + if (s2 >= last) + d2 = s2 - last; + else if (s2 + back_max >= last) + d2 = (last - s2) * bfqd->bfq_back_penalty; + else + wrap |= BFQ_RQ2_WRAP; + + /* Found required data */ + + /* + * By doing switch() on the bit mask "wrap" we avoid having to + * check two variables for all permutations: --> faster! + */ + switch (wrap) { + case 0: /* common case for CFQ: rq1 and rq2 not wrapped */ + if (d1 < d2) + return rq1; + else if (d2 < d1) + return rq2; + else { + if (s1 >= s2) + return rq1; + else + return rq2; + } + + case BFQ_RQ2_WRAP: + return rq1; + case BFQ_RQ1_WRAP: + return rq2; + case (BFQ_RQ1_WRAP|BFQ_RQ2_WRAP): /* both rqs wrapped */ + default: + /* + * Since both rqs are wrapped, + * start with the one that's further behind head + * (--> only *one* back seek required), + * since back seek takes more time than forward. + */ + if (s1 <= s2) + return rq1; + else + return rq2; + } +} + +static struct request *bfq_find_next_rq(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + struct request *last) +{ + struct rb_node *rbnext = rb_next(&last->rb_node); + struct rb_node *rbprev = rb_prev(&last->rb_node); + struct request *next = NULL, *prev = NULL; + + BUG_ON(RB_EMPTY_NODE(&last->rb_node)); + + if (rbprev != NULL) + prev = rb_entry_rq(rbprev); + + if (rbnext != NULL) + next = rb_entry_rq(rbnext); + else { + rbnext = rb_first(&bfqq->sort_list); + if (rbnext && rbnext != &last->rb_node) + next = rb_entry_rq(rbnext); + } + + return bfq_choose_req(bfqd, next, prev); +} + +static void bfq_del_rq_rb(struct request *rq) +{ + struct bfq_queue *bfqq = RQ_BFQQ(rq); + struct bfq_data *bfqd = bfqq->bfqd; + const int sync = rq_is_sync(rq); + + BUG_ON(bfqq->queued[sync] == 0); + bfqq->queued[sync]--; + bfqd->queued--; + + elv_rb_del(&bfqq->sort_list, rq); + + if (bfq_bfqq_busy(bfqq) && bfqq != bfqd->active_queue && + RB_EMPTY_ROOT(&bfqq->sort_list)) + bfq_del_bfqq_busy(bfqd, bfqq, 1); +} + +/* see the definition of bfq_async_charge_factor for details */ +static inline unsigned long bfq_serv_to_charge(struct request *rq, + struct bfq_queue *bfqq) +{ + return blk_rq_sectors(rq) * + (1 + ((!bfq_bfqq_sync(bfqq)) * bfq_async_charge_factor)); +} + +/** + * bfq_updated_next_req - update the queue after a new next_rq selection. + * @bfqd: the device data the queue belongs to. + * @bfqq: the queue to update. + * + * If the first request of a queue changes we make sure that the queue + * has enough budget to serve at least its first request (if the + * request has grown). We do this because if the queue has not enough + * budget for its first request, it has to go through two dispatch + * rounds to actually get it dispatched. + */ +static void bfq_updated_next_req(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + struct bfq_entity *entity = &bfqq->entity; + struct bfq_service_tree *st = bfq_entity_service_tree(entity); + struct request *next_rq = bfqq->next_rq; + unsigned long new_budget; + + if (next_rq == NULL) + return; + + if (bfqq == bfqd->active_queue) + /* + * In order not to break guarantees, budgets cannot be + * changed after an entity has been selected. + */ + return; + + BUG_ON(entity->tree != &st->active); + BUG_ON(entity == entity->sched_data->active_entity); + + new_budget = max_t(unsigned long, bfqq->max_budget, + bfq_serv_to_charge(next_rq, bfqq)); + entity->budget = new_budget; + bfq_log_bfqq(bfqd, bfqq, "updated next rq: new budget %lu", new_budget); + bfq_activate_bfqq(bfqd, bfqq); +} + +static void bfq_add_rq_rb(struct request *rq) +{ + struct bfq_queue *bfqq = RQ_BFQQ(rq); + struct bfq_entity *entity = &bfqq->entity; + struct bfq_data *bfqd = bfqq->bfqd; + struct request *__alias, *next_rq; + + bfq_log_bfqq(bfqd, bfqq, "add_rq_rb %d", rq_is_sync(rq)); + bfqq->queued[rq_is_sync(rq)]++; + bfqd->queued++; + + /* + * Looks a little odd, but the first insert might return an alias, + * if that happens, put the alias on the dispatch list. + */ + while ((__alias = elv_rb_add(&bfqq->sort_list, rq)) != NULL) + bfq_dispatch_insert(bfqd->queue, __alias); + + /* + * Check if this request is a better next-serve candidate. + */ + next_rq = bfq_choose_req(bfqd, bfqq->next_rq, rq); + BUG_ON(next_rq == NULL); + bfqq->next_rq = next_rq; + + if (!bfq_bfqq_busy(bfqq)) { + entity->budget = max_t(unsigned long, bfqq->max_budget, + bfq_serv_to_charge(next_rq, bfqq)); + + /* + * If the queue is not being boosted and has been idle + * for enough time, start a boosting period + */ + if (bfqd->low_latency && bfqq->high_weight_budget == 0) { + if (bfqq->last_activation_time + BFQ_MIN_ACT_INTERVAL < + jiffies_to_msecs(jiffies)) { + bfqq->high_weight_budget = BFQ_BOOST_BUDGET; + entity->ioprio_changed = 1; + bfq_log_bfqq(bfqd, bfqq, + "wboost starting at %lu msec", + bfqq->last_activation_time); + } + bfqq->last_activation_time = + jiffies_to_msecs(jiffies); + } + + bfq_add_bfqq_busy(bfqd, bfqq); + } else + bfq_updated_next_req(bfqd, bfqq); +} + +static void bfq_reposition_rq_rb(struct bfq_queue *bfqq, struct request *rq) +{ + elv_rb_del(&bfqq->sort_list, rq); + bfqq->queued[rq_is_sync(rq)]--; + bfqq->bfqd->queued--; + bfq_add_rq_rb(rq); +} + +static struct request *bfq_find_rq_fmerge(struct bfq_data *bfqd, + struct bio *bio) +{ + struct task_struct *tsk = current; + struct cfq_io_context *cic; + struct bfq_queue *bfqq; + + cic = bfq_cic_lookup(bfqd, tsk->io_context); + if (cic == NULL) + return NULL; + + bfqq = cic_to_bfqq(cic, bfq_bio_sync(bio)); + if (bfqq != NULL) { + sector_t sector = bio->bi_sector + bio_sectors(bio); + + return elv_rb_find(&bfqq->sort_list, sector); + } + + return NULL; +} + +static void bfq_activate_request(struct request_queue *q, struct request *rq) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + + bfqd->rq_in_driver++; + bfqd->last_position = blk_rq_pos(rq) + blk_rq_sectors(rq); +} + +static void bfq_deactivate_request(struct request_queue *q, struct request *rq) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + + WARN_ON(bfqd->rq_in_driver == 0); + bfqd->rq_in_driver--; +} + +static void bfq_remove_request(struct request *rq) +{ + struct bfq_queue *bfqq = RQ_BFQQ(rq); + struct bfq_data *bfqd = bfqq->bfqd; + + if (bfqq->next_rq == rq) { + bfqq->next_rq = bfq_find_next_rq(bfqd, bfqq, rq); + bfq_updated_next_req(bfqd, bfqq); + } + + list_del_init(&rq->queuelist); + bfq_del_rq_rb(rq); + + if (rq->cmd_flags & REQ_META) { + WARN_ON(bfqq->meta_pending == 0); + bfqq->meta_pending--; + } +} + +static int bfq_merge(struct request_queue *q, struct request **req, + struct bio *bio) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct request *__rq; + + __rq = bfq_find_rq_fmerge(bfqd, bio); + if (__rq != NULL && elv_rq_merge_ok(__rq, bio)) { + *req = __rq; + return ELEVATOR_FRONT_MERGE; + } + + return ELEVATOR_NO_MERGE; +} + +static void bfq_merged_request(struct request_queue *q, struct request *req, + int type) +{ + if (type == ELEVATOR_FRONT_MERGE) { + struct bfq_queue *bfqq = RQ_BFQQ(req); + + bfq_reposition_rq_rb(bfqq, req); + } +} + +static void bfq_merged_requests(struct request_queue *q, struct request *rq, + struct request *next) +{ + /* + * Reposition in fifo if next is older than rq. + */ + if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist) && + time_before(next->start_time, rq->start_time)) + list_move(&rq->queuelist, &next->queuelist); + + bfq_remove_request(next); +} + +static int bfq_allow_merge(struct request_queue *q, struct request *rq, + struct bio *bio) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct cfq_io_context *cic; + struct bfq_queue *bfqq; + + /* Disallow merge of a sync bio into an async request. */ + if (bfq_bio_sync(bio) && !rq_is_sync(rq)) + return 0; + + /* + * Lookup the bfqq that this bio will be queued with. Allow + * merge only if rq is queued there. + */ + cic = bfq_cic_lookup(bfqd, current->io_context); + if (cic == NULL) + return 0; + + bfqq = cic_to_bfqq(cic, bfq_bio_sync(bio)); + return bfqq == RQ_BFQQ(rq); +} + +static void __bfq_set_active_queue(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + if (bfqq != NULL) { + bfq_mark_bfqq_must_alloc(bfqq); + bfq_mark_bfqq_budget_new(bfqq); + bfq_clear_bfqq_fifo_expire(bfqq); + + bfqd->budgets_assigned = (bfqd->budgets_assigned*7 + 256) / 8; + + bfq_log_bfqq(bfqd, bfqq, "set_active_queue, cur-budget = %lu", + bfqq->entity.budget); + } + + bfqd->active_queue = bfqq; +} + +/* + * Get and set a new active queue for service. + */ +static struct bfq_queue *bfq_set_active_queue(struct bfq_data *bfqd) +{ + struct bfq_queue *bfqq; + + bfqq = bfq_get_next_queue(bfqd); + __bfq_set_active_queue(bfqd, bfqq); + return bfqq; +} + +/* + * If enough samples have been computed, return the current max budget + * stored in bfqd, which is dynamically updated according to the + * estimated disk peak rate; otherwise return the default max budget + */ +static inline unsigned long bfq_max_budget(struct bfq_data *bfqd) +{ + return bfqd->budgets_assigned < 194 ? bfq_default_max_budget : + bfqd->bfq_max_budget; +} + +/* + * Return min budget, which is a fraction of the current or default + * max budget (trying with 1/32) + */ +static inline unsigned long bfq_min_budget(struct bfq_data *bfqd) +{ + return bfqd->budgets_assigned < 194 ? bfq_default_max_budget / 32 : + bfqd->bfq_max_budget / 32; +} + +static void bfq_arm_slice_timer(struct bfq_data *bfqd) +{ + struct bfq_queue *bfqq = bfqd->active_queue; + struct cfq_io_context *cic; + unsigned long sl; + + WARN_ON(!RB_EMPTY_ROOT(&bfqq->sort_list)); + + /* Idling is disabled, either manually or by past process history. */ + if (bfqd->bfq_slice_idle == 0 || !bfq_bfqq_idle_window(bfqq)) + return; + + /* Tasks have exited, don't wait. */ + cic = bfqd->active_cic; + if (cic == NULL || atomic_read(&cic->ioc->nr_tasks) == 0) + return; + + bfq_mark_bfqq_wait_request(bfqq); + + /* + * We don't want to idle for seeks, but we do want to allow + * fair distribution of slice time for a process doing back-to-back + * seeks. So allow a little bit of time for him to submit a new rq. + * + * To prevent processes with (partly) seeky workloads from + * being too ill-treated, grant them a small fraction of the + * assigned budget before reducing the waiting time to + * BFQ_MIN_TT. This happened to help reduce latency. + */ + sl = bfqd->bfq_slice_idle; + if (bfq_sample_valid(bfqq->seek_samples) && BFQQ_SEEKY(bfqq) && + bfqq->entity.service > bfq_max_budget(bfqd) / 8) + sl = min(sl, msecs_to_jiffies(BFQ_MIN_TT)); + + bfqd->last_idling_start = ktime_get(); + mod_timer(&bfqd->idle_slice_timer, jiffies + sl); + bfq_log(bfqd, "arm idle: %lu ms", sl); +} + +/* + * Set the maximum time for the active queue to consume its + * budget. This prevents seeky processes from lowering the disk + * throughput (always guaranteed with a time slice scheme as in CFQ). + */ +static void bfq_set_budget_timeout(struct bfq_data *bfqd) +{ + struct bfq_queue *bfqq = bfqd->active_queue; + + bfqd->last_budget_start = ktime_get(); + + bfq_clear_bfqq_budget_new(bfqq); + bfqq->budget_timeout = jiffies + + bfqd->bfq_timeout[!!bfq_bfqq_sync(bfqq)] * + (bfqq->entity.weight / bfqq->entity.orig_weight); +} + +/* + * Move request from internal lists to the request queue dispatch list. + */ +static void bfq_dispatch_insert(struct request_queue *q, struct request *rq) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct bfq_queue *bfqq = RQ_BFQQ(rq); + + bfq_remove_request(rq); + bfqq->dispatched++; + elv_dispatch_sort(q, rq); + + if (bfq_bfqq_sync(bfqq)) + bfqd->sync_flight++; +} + +/* + * Return expired entry, or NULL to just start from scratch in rbtree. + */ +static struct request *bfq_check_fifo(struct bfq_queue *bfqq) +{ + struct bfq_data *bfqd = bfqq->bfqd; + struct request *rq; + int fifo; + + if (bfq_bfqq_fifo_expire(bfqq)) + return NULL; + + bfq_mark_bfqq_fifo_expire(bfqq); + + if (list_empty(&bfqq->fifo)) + return NULL; + + fifo = bfq_bfqq_sync(bfqq); + rq = rq_entry_fifo(bfqq->fifo.next); + + if (time_before(jiffies, rq->start_time + bfqd->bfq_fifo_expire[fifo])) + return NULL; + + return rq; +} + +static inline unsigned long bfq_bfqq_budget_left(struct bfq_queue *bfqq) +{ + struct bfq_entity *entity = &bfqq->entity; + return entity->budget - entity->service; +} + +static void __bfq_bfqq_expire(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + BUG_ON(bfqq != bfqd->active_queue); + + __bfq_bfqd_reset_active(bfqd); + + if (RB_EMPTY_ROOT(&bfqq->sort_list)) + bfq_del_bfqq_busy(bfqd, bfqq, 1); + else + bfq_activate_bfqq(bfqd, bfqq); +} + +/** + * __bfq_bfqq_recalc_budget - try to adapt the budget to the @bfqq behavior. + * @bfqd: device data. + * @bfqq: queue to update. + * @reason: reason for expiration. + * + * Handle the feedback on @bfqq budget. See the body for detailed + * comments. + */ +static void __bfq_bfqq_recalc_budget(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + enum bfqq_expiration reason) +{ + struct request *next_rq; + unsigned long budget, min_budget; + + budget = bfqq->max_budget; + min_budget = bfq_min_budget(bfqd); + + BUG_ON(bfqq != bfqd->active_queue); + + bfq_log_bfqq(bfqd, bfqq, "recalc_budg: last budg %lu, budg left %lu", + bfqq->entity.budget, bfq_bfqq_budget_left(bfqq)); + bfq_log_bfqq(bfqd, bfqq, "recalc_budg: last max_budg %lu, min budg %lu", + budget, bfq_min_budget(bfqd)); + bfq_log_bfqq(bfqd, bfqq, "recalc_budg: sync %d, seeky %d", + bfq_bfqq_sync(bfqq), BFQQ_SEEKY(bfqd->active_queue)); + + if (bfq_bfqq_sync(bfqq)) { + switch (reason) { + /* + * Caveat: in all the following cases we trade latency + * for throughput. + */ + case BFQ_BFQQ_TOO_IDLE: + /* + * This is the only case where we may reduce + * the budget: if there is no requets of the + * process still waiting for completion, then + * we assume (tentatively) that the timer has + * expired because the batch of requests of + * the process could have been served with a + * smaller budget. Hence, betting that + * process will behave in the same way when it + * becomes backlogged again, we reduce its + * next budget. As long as we guess right, + * this budget cut reduces the latency + * experienced by the process. + * + * However, if there are still outstanding + * requests, then the process may have not yet + * issued its next request just because it is + * still waiting for the completion of some of + * the still oustanding ones. So in this + * subcase we do not reduce its budget, on the + * contrary we increase it to possibly boost + * the throughput, as discussed in the + * comments to the BUDGET_TIMEOUT case. + */ + if (bfqq->dispatched > 0) /* still oustanding reqs */ + budget = min(budget * 2, bfqd->bfq_max_budget); + else { + if (budget > 5 * min_budget) + budget -= 4 * min_budget; + else + budget = min_budget; + } + break; + case BFQ_BFQQ_BUDGET_TIMEOUT: + /* + * We double the budget here because: 1) it + * gives the chance to boost the throughput if + * this is not a seeky process (which may have + * bumped into this timeout because of, e.g., + * ZBR), 2) together with charge_full_budget + * it helps give seeky processes higher + * timestamps, and hence be served less + * frequently. + */ + budget = min(budget * 2, bfqd->bfq_max_budget); + break; + case BFQ_BFQQ_BUDGET_EXHAUSTED: + /* + * The process still has backlog, and did not + * let either the budget timeout or the disk + * idling timeout expire. Hence it is not + * seeky, has a short thinktime and may be + * happy with a higher budget too. So + * definitely increase the budget of this good + * candidate to boost the disk throughput. + */ + budget = min(budget * 4, bfqd->bfq_max_budget); + break; + case BFQ_BFQQ_NO_MORE_REQUESTS: + /* + * Leave the budget unchanged. + */ + default: + return; + } + } else /* async queue */ + /* async queues get always the maximum possible budget + * (their ability to dispatch is limited by + * @bfqd->bfq_max_budget_async_rq). + */ + budget = bfqd->bfq_max_budget; + + bfqq->max_budget = budget; + + if (bfqd->budgets_assigned >= 194 && bfqd->bfq_user_max_budget == 0 && + bfqq->max_budget > bfqd->bfq_max_budget) + bfqq->max_budget = bfqd->bfq_max_budget; + + /* + * Make sure that we have enough budget for the next request. + * Since the finish time of the bfqq must be kept in sync with + * the budget, be sure to call __bfq_bfqq_expire() after the + * update. + */ + next_rq = bfqq->next_rq; + if (next_rq != NULL) + bfqq->entity.budget = max_t(unsigned long, bfqq->max_budget, + bfq_serv_to_charge(next_rq, bfqq)); + else + bfqq->entity.budget = bfqq->max_budget; + + bfq_log_bfqq(bfqd, bfqq, "head sect: %lu, new budget %lu", + next_rq != NULL ? blk_rq_sectors(next_rq) : 0, + bfqq->entity.budget); +} + +static unsigned long bfq_calc_max_budget(u64 peak_rate, u64 timeout) +{ + unsigned long max_budget; + + /* + * The max_budget calculated when autotuning is equal to the + * amount of sectors transfered in timeout_sync at the + * estimated peak rate. + */ + max_budget = (unsigned long)(peak_rate * 1000 * + timeout >> BFQ_RATE_SHIFT); + + return max_budget; +} + +/* + * In addition to updating the peak rate, checks whether the process + * is "slow", and returns 1 if so. This slow flag is used, in addition + * to the budget timeout, to reduce the amount of service provided to + * seeky processes, and hence reduce their chances to lower the + * throughput. See the code for more details. + */ +static int bfq_update_peak_rate(struct bfq_data *bfqd, struct bfq_queue *bfqq, + int compensate, enum bfqq_expiration reason) +{ + u64 bw, usecs, expected, timeout; + ktime_t delta; + int update = 0; + + if (!bfq_bfqq_sync(bfqq) || bfq_bfqq_budget_new(bfqq)) + return 0; + + delta = compensate ? bfqd->last_idling_start : ktime_get(); + delta = ktime_sub(delta, bfqd->last_budget_start); + usecs = ktime_to_us(delta); + + /* Don't trust short/unrealistic values. */ + if (usecs < 100 || usecs >= LONG_MAX) + return 0; + + /* + * Calculate the bandwidth for the last slice. We use a 64 bit + * value to store the peak rate, in sectors per usec in fixed + * point math. We do so to have enough precision in the estimate + * and to avoid overflows. + */ + bw = (u64)bfqq->entity.service << BFQ_RATE_SHIFT; + do_div(bw, (unsigned long)usecs); + + timeout = jiffies_to_msecs(bfqd->bfq_timeout[BLK_RW_SYNC]); + + /* + * Use only long (> 20ms) intervals to filter out spikes for + * the peak rate estimation. + */ + if (usecs > 20000) { + if (bw > bfqd->peak_rate || + (!BFQQ_SEEKY(bfqq) && + reason == BFQ_BFQQ_BUDGET_TIMEOUT)) { + bfq_log(bfqd, "measured bw =%llu", bw); + /* + * To smooth oscillations use a low-pass filter with + * alpha=7/8, i.e., + * new_rate = (7/8) * old_rate + (1/8) * bw + */ + do_div(bw, 8); + bfqd->peak_rate *= 7; + do_div(bfqd->peak_rate, 8); + bfqd->peak_rate += bw; + update = 1; + bfq_log(bfqd, "new peak_rate=%llu", bfqd->peak_rate); + } + + update |= bfqd->peak_rate_samples == BFQ_PEAK_RATE_SAMPLES - 1; + + if (bfqd->peak_rate_samples < BFQ_PEAK_RATE_SAMPLES) + bfqd->peak_rate_samples++; + + if (bfqd->peak_rate_samples == BFQ_PEAK_RATE_SAMPLES && + update && bfqd->bfq_user_max_budget == 0) { + bfqd->bfq_max_budget = + bfq_calc_max_budget(bfqd->peak_rate, timeout); + bfq_log(bfqd, "new max_budget=%lu", + bfqd->bfq_max_budget); + } + } + + /* + * If the process has been served for a too short time + * interval to let its possible sequential accesses prevail on + * the initial seek time needed to move the disk head on the + * first sector it requested, then give the process a chance + * and for the moment return false. + */ + if (bfqq->entity.budget <= bfq_max_budget(bfqd) / 8) + return 0; + + /* + * A process is considered ``slow'' (i.e., seeky, so that we + * cannot treat it fairly in the service domain, as it would + * slow down too much the other processes) if, when a slice + * ends for whatever reason, it has received service at a + * rate that would not be high enough to complete the budget + * before the budget timeout expiration. + */ + expected = bw * 1000 * timeout >> BFQ_RATE_SHIFT; + + /* + * Caveat: processes doing IO in the slower disk zones will + * tend to be slow(er) even if not seeky. And the estimated + * peak rate will actually be an average over the disk + * surface. Hence, to not be too harsh with unlucky processes, + * we keep a budget/3 margin of safety before declaring a + * process slow. + */ + return expected > (4 * bfqq->entity.budget) / 3; +} + +/** + * bfq_bfqq_expire - expire a queue. + * @bfqd: device owning the queue. + * @bfqq: the queue to expire. + * @compensate: if true, compensate for the time spent idling. + * @reason: the reason causing the expiration. + * + * + * If the process associated to the queue is slow (i.e., seeky), or in + * case of budget timeout, or, finally, if it is async, we + * artificially charge it an entire budget (independently of the + * actual service it received). As a consequence, the queue will get + * higher timestamps than the correct ones upon reactivation, and + * hence it will be rescheduled as if it had received more service + * than what it actually received. In the end, this class of processes + * will receive less service in proportion to how slowly they consume + * their budgets (and hence how seriously they tend to lower the + * throughput). + * + * In contrast, when a queue expires because it has been idling for + * too much or because it exhausted its budget, we do not touch the + * amount of service it has received. Hence when the queue will be + * reactivated and its timestamps updated, the latter will be in sync + * with the actual service received by the queue until expiration. + * + * Charging a full budget to the first type of queues and the exact + * service to the others has the effect of using the WF2Q+ policy to + * schedule the former on a timeslice basis, without violating the + * service domain guarantees of the latter. + */ +static void bfq_bfqq_expire(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + int compensate, + enum bfqq_expiration reason) +{ + int slow; + BUG_ON(bfqq != bfqd->active_queue); + + /* Update disk peak rate for autotuning and check whether the + * process is slow (see bfq_update_peak_rate). + */ + slow = bfq_update_peak_rate(bfqd, bfqq, compensate, reason); + + /* + * As above explained, 'punish' slow (i.e., seeky), timed-out + * and async queues, to favor sequential sync workloads. + * + * Processes doing IO in the slower disk zones will tend to be + * slow(er) even if not seeky. Hence, since the estimated peak + * rate is actually an average over the disk surface, these + * processes may timeout just for bad luck. To avoid punishing + * them we do not charge a full budget to a process that + * succeeded in consuming at least 2/3 of its budget. + */ + if (slow || (reason == BFQ_BFQQ_BUDGET_TIMEOUT && + bfq_bfqq_budget_left(bfqq) >= bfqq->entity.budget / 3)) + bfq_bfqq_charge_full_budget(bfqq); + + bfq_log_bfqq(bfqd, bfqq, + "expire (%d, slow %d, num_disp %d, idle_win %d)", reason, slow, + bfqq->dispatched, bfq_bfqq_idle_window(bfqq)); + + /* Increase, decrease or leave budget unchanged according to reason */ + __bfq_bfqq_recalc_budget(bfqd, bfqq, reason); + __bfq_bfqq_expire(bfqd, bfqq); +} + +/* + * Budget timeout is not implemented through a dedicated timer, but + * just checked on request arrivals and completions, as well as on + * idle timer expirations. + */ +static int bfq_bfqq_budget_timeout(struct bfq_queue *bfqq) +{ + if (bfq_bfqq_budget_new(bfqq)) + return 0; + + if (time_before(jiffies, bfqq->budget_timeout)) + return 0; + + return 1; +} + +/* + * If we expire a queue that is waiting for the arrival of a new + * request, we may prevent the fictitious timestamp backshifting that + * allows the guarantees of the queue to be preserved (see [1] for + * this tricky aspect). Hence we return true only if this condition + * does not hold, or if the queue is slow enough to deserve only to be + * kicked off for preserving a high throughput. +*/ +static inline int bfq_may_expire_for_budg_timeout(struct bfq_queue *bfqq) +{ + return (!bfq_bfqq_wait_request(bfqq) || + bfq_bfqq_budget_left(bfqq) >= bfqq->entity.budget / 3) + && + bfq_bfqq_budget_timeout(bfqq); +} + +/* + * Select a queue for service. If we have a current active queue, + * check whether to continue servicing it, or retrieve and set a new one. + */ +static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd) +{ + struct bfq_queue *bfqq; + struct request *next_rq; + enum bfqq_expiration reason = BFQ_BFQQ_BUDGET_TIMEOUT; + + bfqq = bfqd->active_queue; + if (bfqq == NULL) + goto new_queue; + + bfq_log_bfqq(bfqd, bfqq, "select_queue: already active queue"); + + if (bfq_may_expire_for_budg_timeout(bfqq)) + goto expire; + + next_rq = bfqq->next_rq; + /* + * If bfqq has requests queued and it has enough budget left to + * serve them, keep the queue, otherwise expire it. + */ + if (next_rq != NULL) { + if (bfq_serv_to_charge(next_rq, bfqq) > + bfq_bfqq_budget_left(bfqq)) { + reason = BFQ_BFQQ_BUDGET_EXHAUSTED; + goto expire; + } else + goto keep_queue; + } + + /* + * No requests pending. If the active queue still has + * requests in flight or is idling for a new request, then keep it. + */ + if (timer_pending(&bfqd->idle_slice_timer) || + (bfqq->dispatched != 0 && bfq_bfqq_idle_window(bfqq))) { + bfqq = NULL; + goto keep_queue; + } + + reason = BFQ_BFQQ_NO_MORE_REQUESTS; +expire: + bfq_bfqq_expire(bfqd, bfqq, 0, reason); +new_queue: + bfqq = bfq_set_active_queue(bfqd); + bfq_log(bfqd, "select_queue: new queue returned (possibly NULL)"); +keep_queue: + return bfqq; +} + +/* + * Dispatch some requests from bfqq, moving them to the request queue + * dispatch list. + */ +static int __bfq_dispatch_requests(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + int max_dispatch) +{ + int dispatched = 0; + + BUG_ON(RB_EMPTY_ROOT(&bfqq->sort_list)); + + do { + struct request *rq; + unsigned long service_to_charge; + + /* Follow expired path, else get first next available. */ + rq = bfq_check_fifo(bfqq); + if (rq == NULL) + rq = bfqq->next_rq; + service_to_charge = bfq_serv_to_charge(rq, bfqq); + + if (service_to_charge > bfq_bfqq_budget_left(bfqq)) { + /* + * Expire the queue for budget exhaustion, and + * make sure that the next act_budget is enough + * to serve the next request, even if it comes + * from the fifo expired path. + */ + bfqq->next_rq = rq; + goto expire; + } + + /* Finally, insert request into driver dispatch list. */ + bfq_bfqq_served(bfqq, service_to_charge); + bfq_dispatch_insert(bfqd->queue, rq); + + if (bfqq->high_weight_budget > 0) { /* queue is being boosted */ + struct bfq_entity *entity = &bfqq->entity; + + bfq_log_bfqq(bfqd, bfqq, "busy period dur %llu msec, " + "old highwbudg %lu", + jiffies_to_msecs(jiffies) - + bfqq->last_activation_time, + bfqq->high_weight_budget); + /* + * Decrease the budget for weight boosting by + * the just received service, or, if too much + * time has elapsed from the beginning of this + * boosting period, stop it + */ + if (jiffies_to_msecs(jiffies) - + bfqq->last_activation_time <= BFQ_BOOST_TIMEOUT + && + bfqq->high_weight_budget > service_to_charge) + bfqq->high_weight_budget -= service_to_charge; + else + bfqq->high_weight_budget = 0; + entity->ioprio_changed = 1; + __bfq_entity_update_weight_prio( + bfq_entity_service_tree(entity), + entity); + } + + bfq_log_bfqq(bfqd, bfqq, "dispatched %lu sec req (%llu), " + "budg left %lu", + blk_rq_sectors(rq), blk_rq_pos(rq), + bfq_bfqq_budget_left(bfqq)); + + dispatched++; + + if (bfqd->active_cic == NULL) { + atomic_long_inc(&RQ_CIC(rq)->ioc->refcount); + bfqd->active_cic = RQ_CIC(rq); + } + + if (RB_EMPTY_ROOT(&bfqq->sort_list)) + break; + } while (dispatched < max_dispatch); + + bfq_log_bfqq(bfqd, bfqq, "dispatched %d reqs", dispatched); + + if (bfqd->busy_queues > 1 && ((!bfq_bfqq_sync(bfqq) && + dispatched >= bfqd->bfq_max_budget_async_rq) || + bfq_class_idle(bfqq))) + goto expire; + + return dispatched; + +expire: + bfq_bfqq_expire(bfqd, bfqq, 0, BFQ_BFQQ_BUDGET_EXHAUSTED); + return dispatched; +} + +static int __bfq_forced_dispatch_bfqq(struct bfq_queue *bfqq) +{ + int dispatched = 0; + + while (bfqq->next_rq != NULL) { + bfq_dispatch_insert(bfqq->bfqd->queue, bfqq->next_rq); + dispatched++; + } + + BUG_ON(!list_empty(&bfqq->fifo)); + return dispatched; +} + +/* + * Drain our current requests. Used for barriers and when switching + * io schedulers on-the-fly. + */ +static int bfq_forced_dispatch(struct bfq_data *bfqd) +{ + struct bfq_queue *bfqq, *n; + struct bfq_service_tree *st; + int dispatched = 0; + + bfqq = bfqd->active_queue; + if (bfqq != NULL) + __bfq_bfqq_expire(bfqd, bfqq); + + /* + * Loop through classes, and be careful to leave the scheduler + * in a consistent state, as feedback mechanisms and vtime + * updates cannot be disabled during the process. + */ + list_for_each_entry_safe(bfqq, n, &bfqd->active_list, bfqq_list) { + st = bfq_entity_service_tree(&bfqq->entity); + + dispatched += __bfq_forced_dispatch_bfqq(bfqq); + bfqq->max_budget = bfq_max_budget(bfqd); + + bfq_forget_idle(st); + } + + BUG_ON(bfqd->busy_queues != 0); + + return dispatched; +} + +static int bfq_dispatch_requests(struct request_queue *q, int force) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct bfq_queue *bfqq; + int dispatched; + + bfq_log(bfqd, "dispatch requests: %d busy queues", bfqd->busy_queues); + if (bfqd->busy_queues == 0) + return 0; + + if (unlikely(force)) + return bfq_forced_dispatch(bfqd); + + dispatched = 0; + while ((bfqq = bfq_select_queue(bfqd)) != NULL) { + int max_dispatch; + + max_dispatch = bfqd->bfq_quantum; + if (bfq_class_idle(bfqq)) + max_dispatch = 1; + + if (!bfq_bfqq_sync(bfqq)) + max_dispatch = bfqd->bfq_max_budget_async_rq; + + if (bfqq->dispatched >= max_dispatch) { + if (bfqd->busy_queues > 1) + break; + if (bfqq->dispatched >= 4 * max_dispatch) + break; + } + + if (bfqd->sync_flight != 0 && !bfq_bfqq_sync(bfqq)) + break; + + bfq_clear_bfqq_wait_request(bfqq); + BUG_ON(timer_pending(&bfqd->idle_slice_timer)); + + dispatched += __bfq_dispatch_requests(bfqd, bfqq, max_dispatch); + bfq_log_bfqq(bfqd, bfqq, "total dispatched increased to %d " + "(max_disp %d)", dispatched, max_dispatch); + } + + bfq_log(bfqd, "final total dispatched=%d", dispatched); + return dispatched; +} + +/* + * Task holds one reference to the queue, dropped when task exits. Each rq + * in-flight on this queue also holds a reference, dropped when rq is freed. + * + * Queue lock must be held here. + */ +static void bfq_put_queue(struct bfq_queue *bfqq) +{ + struct bfq_data *bfqd = bfqq->bfqd; + + BUG_ON(atomic_read(&bfqq->ref) <= 0); + + bfq_log_bfqq(bfqd, bfqq, "put_queue: %p %d", bfqq, bfqq->ref); + if (!atomic_dec_and_test(&bfqq->ref)) + return; + + BUG_ON(rb_first(&bfqq->sort_list) != NULL); + BUG_ON(bfqq->allocated[READ] + bfqq->allocated[WRITE] != 0); + BUG_ON(bfqq->entity.tree != NULL); + BUG_ON(bfq_bfqq_busy(bfqq)); + BUG_ON(bfqd->active_queue == bfqq); + + bfq_log_bfqq(bfqd, bfqq, "put_queue: %p freed", bfqq); + + kmem_cache_free(bfq_pool, bfqq); +} + +static void bfq_exit_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + if (bfqq == bfqd->active_queue) { + __bfq_bfqq_expire(bfqd, bfqq); + bfq_schedule_dispatch(bfqd); + } + + bfq_log_bfqq(bfqd, bfqq, "exit_bfqq: %p, %d", bfqq, bfqq->ref); + bfq_put_queue(bfqq); +} + +/* + * Update the entity prio values; note that the new values will not + * be used until the next (re)activation. + */ +static void bfq_init_prio_data(struct bfq_queue *bfqq, struct io_context *ioc) +{ + struct task_struct *tsk = current; + int ioprio_class; + + if (!bfq_bfqq_prio_changed(bfqq)) + return; + + ioprio_class = IOPRIO_PRIO_CLASS(ioc->ioprio); + switch (ioprio_class) { + default: + printk(KERN_ERR "bfq: bad prio %x\n", ioprio_class); + case IOPRIO_CLASS_NONE: + /* + * No prio set, inherit CPU scheduling settings. + */ + bfqq->entity.new_ioprio = task_nice_ioprio(tsk); + bfqq->entity.new_ioprio_class = task_nice_ioclass(tsk); + break; + case IOPRIO_CLASS_RT: + bfqq->entity.new_ioprio = task_ioprio(ioc); + bfqq->entity.new_ioprio_class = IOPRIO_CLASS_RT; + break; + case IOPRIO_CLASS_BE: + bfqq->entity.new_ioprio = task_ioprio(ioc); + bfqq->entity.new_ioprio_class = IOPRIO_CLASS_BE; + break; + case IOPRIO_CLASS_IDLE: + bfqq->entity.new_ioprio_class = IOPRIO_CLASS_IDLE; + bfqq->entity.new_ioprio = 7; + bfq_clear_bfqq_idle_window(bfqq); + break; + } + + bfqq->entity.ioprio_changed = 1; + + /* + * Keep track of original prio settings in case we have to temporarily + * elevate the priority of this queue. + */ + bfqq->org_ioprio = bfqq->entity.new_ioprio; + bfqq->org_ioprio_class = bfqq->entity.new_ioprio_class; + bfq_clear_bfqq_prio_changed(bfqq); +} + +static void bfq_changed_ioprio(struct io_context *ioc, + struct cfq_io_context *cic) +{ + struct bfq_data *bfqd; + struct bfq_queue *bfqq, *new_bfqq; + struct bfq_group *bfqg; + unsigned long uninitialized_var(flags); + + bfqd = bfq_get_bfqd_locked(&cic->key, &flags); + if (unlikely(bfqd == NULL)) + return; + + bfqq = cic->cfqq[BLK_RW_ASYNC]; + if (bfqq != NULL) { + bfqg = container_of(bfqq->entity.sched_data, struct bfq_group, + sched_data); + new_bfqq = bfq_get_queue(bfqd, bfqg, BLK_RW_ASYNC, cic->ioc, + GFP_ATOMIC); + if (new_bfqq != NULL) { + cic->cfqq[BLK_RW_ASYNC] = new_bfqq; + bfq_log_bfqq(bfqd, bfqq, + "changed_ioprio: bfqq %p %d", + bfqq, bfqq->ref); + bfq_put_queue(bfqq); + } + } + + bfqq = cic->cfqq[BLK_RW_SYNC]; + if (bfqq != NULL) + bfq_mark_bfqq_prio_changed(bfqq); + + bfq_put_bfqd_unlock(bfqd, &flags); +} + +static struct bfq_queue *bfq_find_alloc_queue(struct bfq_data *bfqd, + struct bfq_group *bfqg, + int is_sync, + struct io_context *ioc, + gfp_t gfp_mask) +{ + struct bfq_queue *bfqq, *new_bfqq = NULL; + struct cfq_io_context *cic; + +retry: + cic = bfq_cic_lookup(bfqd, ioc); + /* cic always exists here */ + bfqq = cic_to_bfqq(cic, is_sync); + + if (bfqq == NULL) { + if (new_bfqq != NULL) { + bfqq = new_bfqq; + new_bfqq = NULL; + } else if (gfp_mask & __GFP_WAIT) { + /* + * Inform the allocator of the fact that we will + * just repeat this allocation if it fails, to allow + * the allocator to do whatever it needs to attempt to + * free memory. + */ + spin_unlock_irq(bfqd->queue->queue_lock); + new_bfqq = kmem_cache_alloc_node(bfq_pool, + gfp_mask | __GFP_NOFAIL | __GFP_ZERO, + bfqd->queue->node); + spin_lock_irq(bfqd->queue->queue_lock); + goto retry; + } else { + bfqq = kmem_cache_alloc_node(bfq_pool, + gfp_mask | __GFP_ZERO, + bfqd->queue->node); + if (bfqq == NULL) + goto out; + } + + RB_CLEAR_NODE(&bfqq->entity.rb_node); + INIT_LIST_HEAD(&bfqq->fifo); + + atomic_set(&bfqq->ref, 0); + bfqq->bfqd = bfqd; + + bfq_mark_bfqq_prio_changed(bfqq); + + bfq_init_prio_data(bfqq, ioc); + bfq_init_entity(&bfqq->entity, bfqg); + + if (is_sync) { + if (!bfq_class_idle(bfqq)) + bfq_mark_bfqq_idle_window(bfqq); + bfq_mark_bfqq_sync(bfqq); + } + /* Tentative initial value to trade off between thr and lat */ + bfqq->max_budget = (2 * bfq_max_budget(bfqd)) / 3; + bfqq->pid = current->pid; + + bfqq->last_activation_time = 0; + bfqq->high_weight_budget = 0; + + bfq_log_bfqq(bfqd, bfqq, "allocated"); + } + + if (new_bfqq != NULL) + kmem_cache_free(bfq_pool, new_bfqq); + +out: + WARN_ON((gfp_mask & __GFP_WAIT) && bfqq == NULL); + return bfqq; +} + +static struct bfq_queue **bfq_async_queue_prio(struct bfq_data *bfqd, + struct bfq_group *bfqg, + int ioprio_class, int ioprio) +{ + switch (ioprio_class) { + case IOPRIO_CLASS_RT: + return &bfqg->async_bfqq[0][ioprio]; + case IOPRIO_CLASS_BE: + return &bfqg->async_bfqq[1][ioprio]; + case IOPRIO_CLASS_IDLE: + return &bfqg->async_idle_bfqq; + default: + BUG(); + } +} + +static struct bfq_queue *bfq_get_queue(struct bfq_data *bfqd, + struct bfq_group *bfqg, int is_sync, + struct io_context *ioc, gfp_t gfp_mask) +{ + const int ioprio = task_ioprio(ioc); + const int ioprio_class = task_ioprio_class(ioc); + struct bfq_queue **async_bfqq = NULL; + struct bfq_queue *bfqq = NULL; + + if (!is_sync) { + async_bfqq = bfq_async_queue_prio(bfqd, bfqg, ioprio_class, + ioprio); + bfqq = *async_bfqq; + } + + if (bfqq == NULL) { + bfqq = bfq_find_alloc_queue(bfqd, bfqg, is_sync, ioc, gfp_mask); + if (bfqq == NULL) + return NULL; + } + + /* + * Pin the queue now that it's allocated, scheduler exit will prune it. + */ + if (!is_sync && *async_bfqq == NULL) { + atomic_inc(&bfqq->ref); + bfq_log_bfqq(bfqd, bfqq, "get_queue, bfqq not in async: %p, %d", + bfqq, bfqq->ref); + *async_bfqq = bfqq; + } + + atomic_inc(&bfqq->ref); + bfq_log_bfqq(bfqd, bfqq, "get_queue, at end: %p, %d", bfqq, bfqq->ref); + return bfqq; +} + +static void bfq_update_io_thinktime(struct bfq_data *bfqd, + struct cfq_io_context *cic) +{ + unsigned long elapsed = jiffies - cic->last_end_request; + unsigned long ttime = min(elapsed, 2UL * bfqd->bfq_slice_idle); + + cic->ttime_samples = (7*cic->ttime_samples + 256) / 8; + cic->ttime_total = (7*cic->ttime_total + 256*ttime) / 8; + cic->ttime_mean = (cic->ttime_total + 128) / cic->ttime_samples; +} + +static void bfq_update_io_seektime(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + struct request *rq) +{ + sector_t sdist; + u64 total; + + if (bfqq->last_request_pos < blk_rq_pos(rq)) + sdist = blk_rq_pos(rq) - bfqq->last_request_pos; + else + sdist = bfqq->last_request_pos - blk_rq_pos(rq); + + /* + * Don't allow the seek distance to get too large from the + * odd fragment, pagein, etc. + */ + if (bfqq->seek_samples == 0) /* first request, not really a seek */ + sdist = 0; + else if (bfqq->seek_samples <= 60) /* second & third seek */ + sdist = min(sdist, (bfqq->seek_mean * 4) + 2*1024*1024); + else + sdist = min(sdist, (bfqq->seek_mean * 4) + 2*1024*64); + + bfqq->seek_samples = (7*bfqq->seek_samples + 256) / 8; + bfqq->seek_total = (7*bfqq->seek_total + (u64)256*sdist) / 8; + total = bfqq->seek_total + (bfqq->seek_samples/2); + do_div(total, bfqq->seek_samples); + bfqq->seek_mean = (sector_t)total; + + bfq_log_bfqq(bfqd, bfqq, "dist=%llu mean=%llu", (u64)sdist, + (u64)bfqq->seek_mean); +} + +/* + * Disable idle window if the process thinks too long or seeks so much that + * it doesn't matter. + */ +static void bfq_update_idle_window(struct bfq_data *bfqd, + struct bfq_queue *bfqq, + struct cfq_io_context *cic) +{ + int enable_idle; + + /* Don't idle for async or idle io prio class. */ + if (!bfq_bfqq_sync(bfqq) || bfq_class_idle(bfqq)) + return; + + enable_idle = bfq_bfqq_idle_window(bfqq); + + if (atomic_read(&cic->ioc->nr_tasks) == 0 || + bfqd->bfq_slice_idle == 0 || (bfqd->hw_tag && BFQQ_SEEKY(bfqq))) + enable_idle = 0; + else if (bfq_sample_valid(cic->ttime_samples)) { + if (cic->ttime_mean > bfqd->bfq_slice_idle) + enable_idle = 0; + else + enable_idle = 1; + } + bfq_log_bfqq(bfqd, bfqq, "update_idle_window: enable_idle %d", + enable_idle); + + if (enable_idle) + bfq_mark_bfqq_idle_window(bfqq); + else + bfq_clear_bfqq_idle_window(bfqq); +} + +/* + * Called when a new fs request (rq) is added to bfqq. Check if there's + * something we should do about it. + */ +static void bfq_rq_enqueued(struct bfq_data *bfqd, struct bfq_queue *bfqq, + struct request *rq) +{ + struct cfq_io_context *cic = RQ_CIC(rq); + + if (rq->cmd_flags & REQ_META) + bfqq->meta_pending++; + + bfq_update_io_thinktime(bfqd, cic); + bfq_update_io_seektime(bfqd, bfqq, rq); + if (bfqq->entity.service > bfq_max_budget(bfqd) / 8 || + !BFQQ_SEEKY(bfqq)) + bfq_update_idle_window(bfqd, bfqq, cic); + + bfq_log_bfqq(bfqd, bfqq, + "rq_enqueued: idle_window=%d (seeky %d, mean %llu)", + bfq_bfqq_idle_window(bfqq), BFQQ_SEEKY(bfqq), + bfqq->seek_mean); + + bfqq->last_request_pos = blk_rq_pos(rq) + blk_rq_sectors(rq); + + if (bfqq == bfqd->active_queue) { + if (bfq_bfqq_wait_request(bfqq)) { + /* + * If we are waiting for a request for this queue, let + * it rip immediately and flag that we must not expire + * this queue just now. + */ + bfq_clear_bfqq_wait_request(bfqq); + del_timer(&bfqd->idle_slice_timer); + /* + * Here we can safely expire the queue, in + * case of budget timeout, without wasting + * guarantees + */ + if (bfq_bfqq_budget_timeout(bfqq)) + bfq_bfqq_expire(bfqd, bfqq, 0, + BFQ_BFQQ_BUDGET_TIMEOUT); + __blk_run_queue(bfqd->queue); + } + } +} + +static void bfq_insert_request(struct request_queue *q, struct request *rq) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct bfq_queue *bfqq = RQ_BFQQ(rq); + + assert_spin_locked(bfqd->queue->queue_lock); + bfq_init_prio_data(bfqq, RQ_CIC(rq)->ioc); + + bfq_add_rq_rb(rq); + + list_add_tail(&rq->queuelist, &bfqq->fifo); + + bfq_rq_enqueued(bfqd, bfqq, rq); +} + +static void bfq_update_hw_tag(struct bfq_data *bfqd) +{ + bfqd->max_rq_in_driver = max(bfqd->max_rq_in_driver, + bfqd->rq_in_driver); + + /* + * This sample is valid if the number of outstanding requests + * is large enough to allow a queueing behavior. Note that the + * sum is not exact, as it's not taking into account deactivated + * requests. + */ + if (bfqd->rq_in_driver + bfqd->queued < BFQ_HW_QUEUE_THRESHOLD) + return; + + if (bfqd->hw_tag_samples++ < BFQ_HW_QUEUE_SAMPLES) + return; + + bfqd->hw_tag = bfqd->max_rq_in_driver > BFQ_HW_QUEUE_THRESHOLD; + bfqd->max_rq_in_driver = 0; + bfqd->hw_tag_samples = 0; +} + +static void bfq_completed_request(struct request_queue *q, struct request *rq) +{ + struct bfq_queue *bfqq = RQ_BFQQ(rq); + struct bfq_data *bfqd = bfqq->bfqd; + const int sync = rq_is_sync(rq); + + bfq_log_bfqq(bfqd, bfqq, "completed %lu sects req (%d)", + blk_rq_sectors(rq), sync); + + bfq_update_hw_tag(bfqd); + + WARN_ON(!bfqd->rq_in_driver); + WARN_ON(!bfqq->dispatched); + bfqd->rq_in_driver--; + bfqq->dispatched--; + + if (bfq_bfqq_sync(bfqq)) + bfqd->sync_flight--; + + if (sync) + RQ_CIC(rq)->last_end_request = jiffies; + + /* + * If this is the active queue, check if it needs to be expired, + * or if we want to idle in case it has no pending requests. + */ + if (bfqd->active_queue == bfqq) { + if (bfq_bfqq_budget_new(bfqq)) + bfq_set_budget_timeout(bfqd); + + if (bfq_may_expire_for_budg_timeout(bfqq)) + bfq_bfqq_expire(bfqd, bfqq, 0, BFQ_BFQQ_BUDGET_TIMEOUT); + else if (sync && bfqd->rq_in_driver == 0 && + RB_EMPTY_ROOT(&bfqq->sort_list)) + bfq_arm_slice_timer(bfqd); + } + + if (!bfqd->rq_in_driver) + bfq_schedule_dispatch(bfqd); +} + +/* + * We temporarily boost lower priority queues if they are holding fs exclusive + * resources. They are boosted to normal prio (CLASS_BE/4). + */ +static void bfq_prio_boost(struct bfq_queue *bfqq) +{ + if (has_fs_excl()) { + /* + * Boost idle prio on transactions that would lock out other + * users of the filesystem + */ + if (bfq_class_idle(bfqq)) + bfqq->entity.new_ioprio_class = IOPRIO_CLASS_BE; + if (bfqq->entity.new_ioprio > IOPRIO_NORM) + bfqq->entity.new_ioprio = IOPRIO_NORM; + } else { + /* + * Check if we need to unboost the queue + */ + if (bfqq->entity.new_ioprio_class != bfqq->org_ioprio_class) + bfqq->entity.new_ioprio_class = bfqq->org_ioprio_class; + if (bfqq->entity.new_ioprio != bfqq->org_ioprio) + bfqq->entity.new_ioprio = bfqq->org_ioprio; + } +} + +static inline int __bfq_may_queue(struct bfq_queue *bfqq) +{ + if (bfq_bfqq_wait_request(bfqq) && bfq_bfqq_must_alloc(bfqq)) { + bfq_clear_bfqq_must_alloc(bfqq); + return ELV_MQUEUE_MUST; + } + + return ELV_MQUEUE_MAY; +} + +static int bfq_may_queue(struct request_queue *q, int rw) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct task_struct *tsk = current; + struct cfq_io_context *cic; + struct bfq_queue *bfqq; + + /* + * Don't force setup of a queue from here, as a call to may_queue + * does not necessarily imply that a request actually will be queued. + * So just lookup a possibly existing queue, or return 'may queue' + * if that fails. + */ + cic = bfq_cic_lookup(bfqd, tsk->io_context); + if (cic == NULL) + return ELV_MQUEUE_MAY; + + bfqq = cic_to_bfqq(cic, rw & REQ_SYNC); + if (bfqq != NULL) { + bfq_init_prio_data(bfqq, cic->ioc); + bfq_prio_boost(bfqq); + + return __bfq_may_queue(bfqq); + } + + return ELV_MQUEUE_MAY; +} + +/* + * Queue lock held here. + */ +static void bfq_put_request(struct request *rq) +{ + struct bfq_queue *bfqq = RQ_BFQQ(rq); + + if (bfqq != NULL) { + const int rw = rq_data_dir(rq); + + BUG_ON(!bfqq->allocated[rw]); + bfqq->allocated[rw]--; + + put_io_context(RQ_CIC(rq)->ioc); + + rq->elevator_private = NULL; + rq->elevator_private2 = NULL; + + bfq_log_bfqq(bfqq->bfqd, bfqq, "put_request %p, %d", + bfqq, bfqq->ref); + bfq_put_queue(bfqq); + } +} + +/* + * Allocate bfq data structures associated with this request. + */ +static int bfq_set_request(struct request_queue *q, struct request *rq, + gfp_t gfp_mask) +{ + struct bfq_data *bfqd = q->elevator->elevator_data; + struct cfq_io_context *cic; + const int rw = rq_data_dir(rq); + const int is_sync = rq_is_sync(rq); + struct bfq_queue *bfqq; + struct bfq_group *bfqg; + unsigned long flags; + + might_sleep_if(gfp_mask & __GFP_WAIT); + + cic = bfq_get_io_context(bfqd, gfp_mask); + + spin_lock_irqsave(q->queue_lock, flags); + + if (cic == NULL) + goto queue_fail; + + bfqg = bfq_cic_update_cgroup(cic); + + bfqq = cic_to_bfqq(cic, is_sync); + if (bfqq == NULL) { + bfqq = bfq_get_queue(bfqd, bfqg, is_sync, cic->ioc, gfp_mask); + if (bfqq == NULL) + goto queue_fail; + + cic_set_bfqq(cic, bfqq, is_sync); + } + + bfqq->allocated[rw]++; + atomic_inc(&bfqq->ref); + bfq_log_bfqq(bfqd, bfqq, "set_request: bfqq %p, %d", bfqq, bfqq->ref); + + spin_unlock_irqrestore(q->queue_lock, flags); + + rq->elevator_private = cic; + rq->elevator_private2 = bfqq; + + return 0; + +queue_fail: + if (cic != NULL) + put_io_context(cic->ioc); + + bfq_schedule_dispatch(bfqd); + spin_unlock_irqrestore(q->queue_lock, flags); + + return 1; +} + +static void bfq_kick_queue(struct work_struct *work) +{ + struct bfq_data *bfqd = + container_of(work, struct bfq_data, unplug_work); + struct request_queue *q = bfqd->queue; + unsigned long flags; + + spin_lock_irqsave(q->queue_lock, flags); + __blk_run_queue(q); + spin_unlock_irqrestore(q->queue_lock, flags); +} + +/* + * Handler of the expiration of the timer running if the active_queue + * is idling inside its time slice. + */ +static void bfq_idle_slice_timer(unsigned long data) +{ + struct bfq_data *bfqd = (struct bfq_data *)data; + struct bfq_queue *bfqq; + unsigned long flags; + enum bfqq_expiration reason; + + spin_lock_irqsave(bfqd->queue->queue_lock, flags); + + bfqq = bfqd->active_queue; + /* + * Theoretical race here: active_queue can be NULL or different + * from the queue that was idling if the timer handler spins on + * the queue_lock and a new request arrives for the current + * queue and there is a full dispatch cycle that changes the + * active_queue. This can hardly happen, but in the worst case + * we just expire a queue too early. + */ + if (bfqq != NULL) { + bfq_log_bfqq(bfqd, bfqq, "slice_timer expired"); + reason = BFQ_BFQQ_TOO_IDLE; + if (bfq_bfqq_budget_timeout(bfqq)) + /* + * Also here the queue can be safely expired + * for budget timeout without wasting + * guarantees + */ + reason = BFQ_BFQQ_BUDGET_TIMEOUT; + + bfq_bfqq_expire(bfqd, bfqq, 1, reason); + } + + bfq_schedule_dispatch(bfqd); + + spin_unlock_irqrestore(bfqd->queue->queue_lock, flags); +} + +static void bfq_shutdown_timer_wq(struct bfq_data *bfqd) +{ + del_timer_sync(&bfqd->idle_slice_timer); + cancel_work_sync(&bfqd->unplug_work); +} + +static inline void __bfq_put_async_bfqq(struct bfq_data *bfqd, + struct bfq_queue **bfqq_ptr) +{ + struct bfq_group *root_group = bfqd->root_group; + struct bfq_queue *bfqq = *bfqq_ptr; + + bfq_log(bfqd, "put_async_bfqq: %p", bfqq); + if (bfqq != NULL) { + bfq_bfqq_move(bfqd, bfqq, &bfqq->entity, root_group); + bfq_log_bfqq(bfqd, bfqq, "put_async_bfqq: putting %p, %d", + bfqq, bfqq->ref); + bfq_put_queue(bfqq); + *bfqq_ptr = NULL; + } +} + +/* + * Release all the bfqg references to its async queues. If we are + * deallocating the group these queues may still contain requests, so + * we reparent them to the root cgroup (i.e., the only one that will + * exist for sure untill all the requests on a device are gone). + */ +static void bfq_put_async_queues(struct bfq_data *bfqd, struct bfq_group *bfqg) +{ + int i, j; + + for (i = 0; i < 2; i++) + for (j = 0; j < IOPRIO_BE_NR; j++) + __bfq_put_async_bfqq(bfqd, &bfqg->async_bfqq[i][j]); + + __bfq_put_async_bfqq(bfqd, &bfqg->async_idle_bfqq); +} + +static void bfq_exit_queue(struct elevator_queue *e) +{ + struct bfq_data *bfqd = e->elevator_data; + struct request_queue *q = bfqd->queue; + struct bfq_queue *bfqq, *n; + struct cfq_io_context *cic; + + bfq_shutdown_timer_wq(bfqd); + + spin_lock_irq(q->queue_lock); + + while (!list_empty(&bfqd->cic_list)) { + cic = list_entry(bfqd->cic_list.next, struct cfq_io_context, + queue_list); + __bfq_exit_single_io_context(bfqd, cic); + } + + BUG_ON(bfqd->active_queue != NULL); + list_for_each_entry_safe(bfqq, n, &bfqd->idle_list, bfqq_list) + bfq_deactivate_bfqq(bfqd, bfqq, 0); + + bfq_disconnect_groups(bfqd); + spin_unlock_irq(q->queue_lock); + + bfq_shutdown_timer_wq(bfqd); + + spin_lock(&cic_index_lock); + ida_remove(&cic_index_ida, bfqd->cic_index); + spin_unlock(&cic_index_lock); + + /* Wait for cic->key accessors to exit their grace periods. */ + synchronize_rcu(); + + BUG_ON(timer_pending(&bfqd->idle_slice_timer)); + + bfq_free_root_group(bfqd); + kfree(bfqd); +} + +static int bfq_alloc_cic_index(void) +{ + int index, error; + + do { + if (!ida_pre_get(&cic_index_ida, GFP_KERNEL)) + return -ENOMEM; + + spin_lock(&cic_index_lock); + error = ida_get_new(&cic_index_ida, &index); + spin_unlock(&cic_index_lock); + if (error && error != -EAGAIN) + return error; + } while (error); + + return index; +} + +static void *bfq_init_queue(struct request_queue *q) +{ + struct bfq_group *bfqg; + struct bfq_data *bfqd; + int i; + + i = bfq_alloc_cic_index(); + if (i < 0) + return NULL; + + bfqd = kmalloc_node(sizeof(*bfqd), GFP_KERNEL | __GFP_ZERO, q->node); + if (bfqd == NULL) + return NULL; + + bfqd->cic_index = i; + + INIT_LIST_HEAD(&bfqd->cic_list); + + bfqd->queue = q; + + bfqg = bfq_alloc_root_group(bfqd, q->node); + if (bfqg == NULL) { + kfree(bfqd); + return NULL; + } + + bfqd->root_group = bfqg; + + init_timer(&bfqd->idle_slice_timer); + bfqd->idle_slice_timer.function = bfq_idle_slice_timer; + bfqd->idle_slice_timer.data = (unsigned long)bfqd; + + INIT_WORK(&bfqd->unplug_work, bfq_kick_queue); + + INIT_LIST_HEAD(&bfqd->active_list); + INIT_LIST_HEAD(&bfqd->idle_list); + + bfqd->hw_tag = 1; + + bfqd->bfq_max_budget = bfq_default_max_budget; + + bfqd->bfq_quantum = bfq_quantum; + bfqd->bfq_fifo_expire[0] = bfq_fifo_expire[0]; + bfqd->bfq_fifo_expire[1] = bfq_fifo_expire[1]; + bfqd->bfq_back_max = bfq_back_max; + bfqd->bfq_back_penalty = bfq_back_penalty; + bfqd->bfq_slice_idle = bfq_slice_idle; + bfqd->bfq_max_budget_async_rq = bfq_max_budget_async_rq; + bfqd->bfq_timeout[BLK_RW_ASYNC] = bfq_timeout_async; + bfqd->bfq_timeout[BLK_RW_SYNC] = bfq_timeout_sync; + + bfqd->low_latency = true; + + return bfqd; +} + +static void bfq_slab_kill(void) +{ + if (bfq_pool != NULL) + kmem_cache_destroy(bfq_pool); + if (bfq_ioc_pool != NULL) + kmem_cache_destroy(bfq_ioc_pool); +} + +static int __init bfq_slab_setup(void) +{ + bfq_pool = KMEM_CACHE(bfq_queue, 0); + if (bfq_pool == NULL) + goto fail; + + bfq_ioc_pool = kmem_cache_create("bfq_io_context", + sizeof(struct cfq_io_context), + __alignof__(struct cfq_io_context), + 0, NULL); + if (bfq_ioc_pool == NULL) + goto fail; + + return 0; +fail: + bfq_slab_kill(); + return -ENOMEM; +} + +static ssize_t bfq_var_show(unsigned int var, char *page) +{ + return sprintf(page, "%d\n", var); +} + +static ssize_t bfq_var_store(unsigned long *var, const char *page, size_t count) +{ + unsigned long new_val; + int ret = strict_strtoul(page, 10, &new_val); + + if (ret == 0) + *var = new_val; + + return count; +} + +#define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ +static ssize_t __FUNC(struct elevator_queue *e, char *page) \ +{ \ + struct bfq_data *bfqd = e->elevator_data; \ + unsigned int __data = __VAR; \ + if (__CONV) \ + __data = jiffies_to_msecs(__data); \ + return bfq_var_show(__data, (page)); \ +} +SHOW_FUNCTION(bfq_quantum_show, bfqd->bfq_quantum, 0); +SHOW_FUNCTION(bfq_fifo_expire_sync_show, bfqd->bfq_fifo_expire[1], 1); +SHOW_FUNCTION(bfq_fifo_expire_async_show, bfqd->bfq_fifo_expire[0], 1); +SHOW_FUNCTION(bfq_back_seek_max_show, bfqd->bfq_back_max, 0); +SHOW_FUNCTION(bfq_back_seek_penalty_show, bfqd->bfq_back_penalty, 0); +SHOW_FUNCTION(bfq_slice_idle_show, bfqd->bfq_slice_idle, 1); +SHOW_FUNCTION(bfq_max_budget_show, bfqd->bfq_user_max_budget, 0); +SHOW_FUNCTION(bfq_max_budget_async_rq_show, bfqd->bfq_max_budget_async_rq, 0); +SHOW_FUNCTION(bfq_timeout_sync_show, bfqd->bfq_timeout[BLK_RW_SYNC], 1); +SHOW_FUNCTION(bfq_timeout_async_show, bfqd->bfq_timeout[BLK_RW_ASYNC], 1); +SHOW_FUNCTION(bfq_low_latency_show, bfqd->low_latency, 0); +#undef SHOW_FUNCTION + +#define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ +static ssize_t \ +__FUNC(struct elevator_queue *e, const char *page, size_t count) \ +{ \ + struct bfq_data *bfqd = e->elevator_data; \ + unsigned long __data; \ + int ret = bfq_var_store(&__data, (page), count); \ + if (__data < (MIN)) \ + __data = (MIN); \ + else if (__data > (MAX)) \ + __data = (MAX); \ + if (__CONV) \ + *(__PTR) = msecs_to_jiffies(__data); \ + else \ + *(__PTR) = __data; \ + return ret; \ +} +STORE_FUNCTION(bfq_quantum_store, &bfqd->bfq_quantum, 1, INT_MAX, 0); +STORE_FUNCTION(bfq_fifo_expire_sync_store, &bfqd->bfq_fifo_expire[1], 1, + INT_MAX, 1); +STORE_FUNCTION(bfq_fifo_expire_async_store, &bfqd->bfq_fifo_expire[0], 1, + INT_MAX, 1); +STORE_FUNCTION(bfq_back_seek_max_store, &bfqd->bfq_back_max, 0, INT_MAX, 0); +STORE_FUNCTION(bfq_back_seek_penalty_store, &bfqd->bfq_back_penalty, 1, + INT_MAX, 0); +STORE_FUNCTION(bfq_slice_idle_store, &bfqd->bfq_slice_idle, 0, INT_MAX, 1); +STORE_FUNCTION(bfq_max_budget_async_rq_store, &bfqd->bfq_max_budget_async_rq, + 1, INT_MAX, 0); +STORE_FUNCTION(bfq_timeout_async_store, &bfqd->bfq_timeout[BLK_RW_ASYNC], 0, + INT_MAX, 1); +#undef STORE_FUNCTION + +static inline unsigned long bfq_estimated_max_budget(struct bfq_data *bfqd) +{ + u64 timeout = jiffies_to_msecs(bfqd->bfq_timeout[BLK_RW_SYNC]); + + if (bfqd->peak_rate_samples >= BFQ_PEAK_RATE_SAMPLES) + return bfq_calc_max_budget(bfqd->peak_rate, timeout); + else + return bfq_default_max_budget; +} + +static ssize_t bfq_max_budget_store(struct elevator_queue *e, + const char *page, size_t count) +{ + struct bfq_data *bfqd = e->elevator_data; + unsigned long __data; + int ret = bfq_var_store(&__data, (page), count); + + if (__data == 0) + bfqd->bfq_max_budget = bfq_estimated_max_budget(bfqd); + else { + if (__data > INT_MAX) + __data = INT_MAX; + bfqd->bfq_max_budget = __data; + } + + bfqd->bfq_user_max_budget = __data; + + return ret; +} + +static ssize_t bfq_timeout_sync_store(struct elevator_queue *e, + const char *page, size_t count) +{ + struct bfq_data *bfqd = e->elevator_data; + unsigned long __data; + int ret = bfq_var_store(&__data, (page), count); + + if (__data < 1) + __data = 1; + else if (__data > INT_MAX) + __data = INT_MAX; + + bfqd->bfq_timeout[BLK_RW_SYNC] = msecs_to_jiffies(__data); + if (bfqd->bfq_user_max_budget == 0) + bfqd->bfq_max_budget = bfq_estimated_max_budget(bfqd); + + return ret; +} + +static ssize_t bfq_low_latency_store(struct elevator_queue *e, + const char *page, size_t count) +{ + struct bfq_data *bfqd = e->elevator_data; + unsigned long __data; + int ret = bfq_var_store(&__data, (page), count); + + if (__data > 1) + __data = 1; + bfqd->low_latency = __data; + + return ret; +} + +#define BFQ_ATTR(name) \ + __ATTR(name, S_IRUGO|S_IWUSR, bfq_##name##_show, bfq_##name##_store) + +static struct elv_fs_entry bfq_attrs[] = { + BFQ_ATTR(quantum), + BFQ_ATTR(fifo_expire_sync), + BFQ_ATTR(fifo_expire_async), + BFQ_ATTR(back_seek_max), + BFQ_ATTR(back_seek_penalty), + BFQ_ATTR(slice_idle), + BFQ_ATTR(max_budget), + BFQ_ATTR(max_budget_async_rq), + BFQ_ATTR(timeout_sync), + BFQ_ATTR(timeout_async), + BFQ_ATTR(low_latency), + __ATTR_NULL +}; + +static struct elevator_type iosched_bfq = { + .ops = { + .elevator_merge_fn = bfq_merge, + .elevator_merged_fn = bfq_merged_request, + .elevator_merge_req_fn = bfq_merged_requests, + .elevator_allow_merge_fn = bfq_allow_merge, + .elevator_dispatch_fn = bfq_dispatch_requests, + .elevator_add_req_fn = bfq_insert_request, + .elevator_activate_req_fn = bfq_activate_request, + .elevator_deactivate_req_fn = bfq_deactivate_request, + .elevator_queue_empty_fn = bfq_queue_empty, + .elevator_completed_req_fn = bfq_completed_request, + .elevator_former_req_fn = elv_rb_former_request, + .elevator_latter_req_fn = elv_rb_latter_request, + .elevator_set_req_fn = bfq_set_request, + .elevator_put_req_fn = bfq_put_request, + .elevator_may_queue_fn = bfq_may_queue, + .elevator_init_fn = bfq_init_queue, + .elevator_exit_fn = bfq_exit_queue, + .trim = bfq_free_io_context, + }, + .elevator_attrs = bfq_attrs, + .elevator_name = "bfq", + .elevator_owner = THIS_MODULE, +}; + +static int __init bfq_init(void) +{ + /* + * Can be 0 on HZ < 1000 setups. + */ + if (bfq_slice_idle == 0) + bfq_slice_idle = 1; + + if (bfq_timeout_async == 0) + bfq_timeout_async = 1; + + if (bfq_slab_setup()) + return -ENOMEM; + + elv_register(&iosched_bfq); + + return 0; +} + +static void __exit bfq_exit(void) +{ + DECLARE_COMPLETION_ONSTACK(all_gone); + elv_unregister(&iosched_bfq); + bfq_ioc_gone = &all_gone; + /* bfq_ioc_gone's update must be visible before reading bfq_ioc_count */ + smp_wmb(); + if (elv_ioc_count_read(bfq_ioc_count) != 0) + wait_for_completion(&all_gone); + ida_destroy(&cic_index_ida); + bfq_slab_kill(); +} + +module_init(bfq_init); +module_exit(bfq_exit); + +MODULE_AUTHOR("Fabio Checconi, Paolo Valente"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Budget Fair Queueing IO scheduler"); diff --git a/block/bfq-sched.c b/block/bfq-sched.c new file mode 100644 index 0000000000000..6ef14cc681e5e --- /dev/null +++ b/block/bfq-sched.c @@ -0,0 +1,1010 @@ +/* + * BFQ: Hierarchical B-WF2Q+ scheduler. + * + * Based on ideas and code from CFQ: + * Copyright (C) 2003 Jens Axboe + * + * Copyright (C) 2008 Fabio Checconi + * Paolo Valente + */ + +#ifdef CONFIG_CGROUP_BFQIO +#define for_each_entity(entity) \ + for (; entity != NULL; entity = entity->parent) + +#define for_each_entity_safe(entity, parent) \ + for (; entity && ({ parent = entity->parent; 1; }); entity = parent) + +static struct bfq_entity *bfq_lookup_next_entity(struct bfq_sched_data *sd, + int extract); + +static int bfq_update_next_active(struct bfq_sched_data *sd) +{ + struct bfq_group *bfqg; + struct bfq_entity *entity, *next_active; + + if (sd->active_entity != NULL) + /* will update/requeue at the end of service */ + return 0; + + /* + * NOTE: this can be improved in many ways, such as returning + * 1 (and thus propagating upwards the update) only when the + * budget changes, or caching the bfqq that will be scheduled + * next from this subtree. By now we worry more about + * correctness than about performance... + */ + next_active = bfq_lookup_next_entity(sd, 0); + sd->next_active = next_active; + + if (next_active != NULL) { + bfqg = container_of(sd, struct bfq_group, sched_data); + entity = bfqg->my_entity; + if (entity != NULL) + entity->budget = next_active->budget; + } + + return 1; +} + +static inline void bfq_check_next_active(struct bfq_sched_data *sd, + struct bfq_entity *entity) +{ + BUG_ON(sd->next_active != entity); +} +#else +#define for_each_entity(entity) \ + for (; entity != NULL; entity = NULL) + +#define for_each_entity_safe(entity, parent) \ + for (parent = NULL; entity != NULL; entity = parent) + +static inline int bfq_update_next_active(struct bfq_sched_data *sd) +{ + return 0; +} + +static inline void bfq_check_next_active(struct bfq_sched_data *sd, + struct bfq_entity *entity) +{ +} +#endif + +/* + * Shift for timestamp calculations. This actually limits the maximum + * service allowed in one timestamp delta (small shift values increase it), + * the maximum total weight that can be used for the queues in the system + * (big shift values increase it), and the period of virtual time wraparounds. + */ +#define WFQ_SERVICE_SHIFT 22 + +/** + * bfq_gt - compare two timestamps. + * @a: first ts. + * @b: second ts. + * + * Return @a > @b, dealing with wrapping correctly. + */ +static inline int bfq_gt(u64 a, u64 b) +{ + return (s64)(a - b) > 0; +} + +static inline struct bfq_queue *bfq_entity_to_bfqq(struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = NULL; + + BUG_ON(entity == NULL); + + if (entity->my_sched_data == NULL) + bfqq = container_of(entity, struct bfq_queue, entity); + + return bfqq; +} + + +/** + * bfq_delta - map service into the virtual time domain. + * @service: amount of service. + * @weight: scale factor (weight of an entity or weight sum). + */ +static inline u64 bfq_delta(unsigned long service, + unsigned long weight) +{ + u64 d = (u64)service << WFQ_SERVICE_SHIFT; + + do_div(d, weight); + return d; +} + +/** + * bfq_calc_finish - assign the finish time to an entity. + * @entity: the entity to act upon. + * @service: the service to be charged to the entity. + */ +static inline void bfq_calc_finish(struct bfq_entity *entity, + unsigned long service) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + + BUG_ON(entity->weight == 0); + + entity->finish = entity->start + + bfq_delta(service, entity->weight); + + if (bfqq != NULL) { + bfq_log_bfqq(bfqq->bfqd, bfqq, + "calc_finish: serv %lu, w %lu, hi-budg %lu", + service, entity->weight, + bfqq->high_weight_budget); + bfq_log_bfqq(bfqq->bfqd, bfqq, + "calc_finish: start %llu, finish %llu, delta %llu", + entity->start, entity->finish, + bfq_delta(service, entity->weight)); + } +} + +/** + * bfq_entity_of - get an entity from a node. + * @node: the node field of the entity. + * + * Convert a node pointer to the relative entity. This is used only + * to simplify the logic of some functions and not as the generic + * conversion mechanism because, e.g., in the tree walking functions, + * the check for a %NULL value would be redundant. + */ +static inline struct bfq_entity *bfq_entity_of(struct rb_node *node) +{ + struct bfq_entity *entity = NULL; + + if (node != NULL) + entity = rb_entry(node, struct bfq_entity, rb_node); + + return entity; +} + +/** + * bfq_extract - remove an entity from a tree. + * @root: the tree root. + * @entity: the entity to remove. + */ +static inline void bfq_extract(struct rb_root *root, + struct bfq_entity *entity) +{ + BUG_ON(entity->tree != root); + + entity->tree = NULL; + rb_erase(&entity->rb_node, root); +} + +/** + * bfq_idle_extract - extract an entity from the idle tree. + * @st: the service tree of the owning @entity. + * @entity: the entity being removed. + */ +static void bfq_idle_extract(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + struct rb_node *next; + + BUG_ON(entity->tree != &st->idle); + + if (entity == st->first_idle) { + next = rb_next(&entity->rb_node); + st->first_idle = bfq_entity_of(next); + } + + if (entity == st->last_idle) { + next = rb_prev(&entity->rb_node); + st->last_idle = bfq_entity_of(next); + } + + bfq_extract(&st->idle, entity); + + if (bfqq != NULL) + list_del(&bfqq->bfqq_list); +} + +/** + * bfq_insert - generic tree insertion. + * @root: tree root. + * @entity: entity to insert. + * + * This is used for the idle and the active tree, since they are both + * ordered by finish time. + */ +static void bfq_insert(struct rb_root *root, struct bfq_entity *entity) +{ + struct bfq_entity *entry; + struct rb_node **node = &root->rb_node; + struct rb_node *parent = NULL; + + BUG_ON(entity->tree != NULL); + + while (*node != NULL) { + parent = *node; + entry = rb_entry(parent, struct bfq_entity, rb_node); + + if (bfq_gt(entry->finish, entity->finish)) + node = &parent->rb_left; + else + node = &parent->rb_right; + } + + rb_link_node(&entity->rb_node, parent, node); + rb_insert_color(&entity->rb_node, root); + + entity->tree = root; +} + +/** + * bfq_update_min - update the min_start field of a entity. + * @entity: the entity to update. + * @node: one of its children. + * + * This function is called when @entity may store an invalid value for + * min_start due to updates to the active tree. The function assumes + * that the subtree rooted at @node (which may be its left or its right + * child) has a valid min_start value. + */ +static inline void bfq_update_min(struct bfq_entity *entity, + struct rb_node *node) +{ + struct bfq_entity *child; + + if (node != NULL) { + child = rb_entry(node, struct bfq_entity, rb_node); + if (bfq_gt(entity->min_start, child->min_start)) + entity->min_start = child->min_start; + } +} + +/** + * bfq_update_active_node - recalculate min_start. + * @node: the node to update. + * + * @node may have changed position or one of its children may have moved, + * this function updates its min_start value. The left and right subtrees + * are assumed to hold a correct min_start value. + */ +static inline void bfq_update_active_node(struct rb_node *node) +{ + struct bfq_entity *entity = rb_entry(node, struct bfq_entity, rb_node); + + entity->min_start = entity->start; + bfq_update_min(entity, node->rb_right); + bfq_update_min(entity, node->rb_left); +} + +/** + * bfq_update_active_tree - update min_start for the whole active tree. + * @node: the starting node. + * + * @node must be the deepest modified node after an update. This function + * updates its min_start using the values held by its children, assuming + * that they did not change, and then updates all the nodes that may have + * changed in the path to the root. The only nodes that may have changed + * are the ones in the path or their siblings. + */ +static void bfq_update_active_tree(struct rb_node *node) +{ + struct rb_node *parent; + +up: + bfq_update_active_node(node); + + parent = rb_parent(node); + if (parent == NULL) + return; + + if (node == parent->rb_left && parent->rb_right != NULL) + bfq_update_active_node(parent->rb_right); + else if (parent->rb_left != NULL) + bfq_update_active_node(parent->rb_left); + + node = parent; + goto up; +} + +/** + * bfq_active_insert - insert an entity in the active tree of its group/device. + * @st: the service tree of the entity. + * @entity: the entity being inserted. + * + * The active tree is ordered by finish time, but an extra key is kept + * per each node, containing the minimum value for the start times of + * its children (and the node itself), so it's possible to search for + * the eligible node with the lowest finish time in logarithmic time. + */ +static void bfq_active_insert(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + struct rb_node *node = &entity->rb_node; + + bfq_insert(&st->active, entity); + + if (node->rb_left != NULL) + node = node->rb_left; + else if (node->rb_right != NULL) + node = node->rb_right; + + bfq_update_active_tree(node); + + if (bfqq != NULL) + list_add(&bfqq->bfqq_list, &bfqq->bfqd->active_list); +} + +/** + * bfq_ioprio_to_weight - calc a weight from an ioprio. + * @ioprio: the ioprio value to convert. + */ +static unsigned short bfq_ioprio_to_weight(int ioprio) +{ + WARN_ON(ioprio < 0 || ioprio >= IOPRIO_BE_NR); + return IOPRIO_BE_NR - ioprio; +} + +/** + * bfq_weight_to_ioprio - calc an ioprio from a weight. + * @weight: the weight value to convert. + * + * To preserve as mush as possible the old only-ioprio user interface, + * 0 is used as an escape ioprio value for weights (numerically) equal or + * larger than IOPRIO_BE_NR + */ +static unsigned short bfq_weight_to_ioprio(int weight) +{ + WARN_ON(weight < BFQ_MIN_WEIGHT || weight > BFQ_MAX_WEIGHT); + return IOPRIO_BE_NR - weight < 0 ? 0 : IOPRIO_BE_NR - weight; +} + +static inline void bfq_get_entity(struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + struct bfq_sched_data *sd; + + if (bfqq != NULL) { + sd = entity->sched_data; + atomic_inc(&bfqq->ref); + bfq_log_bfqq(bfqq->bfqd, bfqq, "get_entity: %p %d", + bfqq, bfqq->ref); + } +} + +/** + * bfq_find_deepest - find the deepest node that an extraction can modify. + * @node: the node being removed. + * + * Do the first step of an extraction in an rb tree, looking for the + * node that will replace @node, and returning the deepest node that + * the following modifications to the tree can touch. If @node is the + * last node in the tree return %NULL. + */ +static struct rb_node *bfq_find_deepest(struct rb_node *node) +{ + struct rb_node *deepest; + + if (node->rb_right == NULL && node->rb_left == NULL) + deepest = rb_parent(node); + else if (node->rb_right == NULL) + deepest = node->rb_left; + else if (node->rb_left == NULL) + deepest = node->rb_right; + else { + deepest = rb_next(node); + if (deepest->rb_right != NULL) + deepest = deepest->rb_right; + else if (rb_parent(deepest) != node) + deepest = rb_parent(deepest); + } + + return deepest; +} + +/** + * bfq_active_extract - remove an entity from the active tree. + * @st: the service_tree containing the tree. + * @entity: the entity being removed. + */ +static void bfq_active_extract(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + struct rb_node *node; + + node = bfq_find_deepest(&entity->rb_node); + bfq_extract(&st->active, entity); + + if (node != NULL) + bfq_update_active_tree(node); + + if (bfqq != NULL) + list_del(&bfqq->bfqq_list); +} + +/** + * bfq_idle_insert - insert an entity into the idle tree. + * @st: the service tree containing the tree. + * @entity: the entity to insert. + */ +static void bfq_idle_insert(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + struct bfq_entity *first_idle = st->first_idle; + struct bfq_entity *last_idle = st->last_idle; + + if (first_idle == NULL || bfq_gt(first_idle->finish, entity->finish)) + st->first_idle = entity; + if (last_idle == NULL || bfq_gt(entity->finish, last_idle->finish)) + st->last_idle = entity; + + bfq_insert(&st->idle, entity); + + if (bfqq != NULL) + list_add(&bfqq->bfqq_list, &bfqq->bfqd->idle_list); +} + +/** + * bfq_forget_entity - remove an entity from the wfq trees. + * @st: the service tree. + * @entity: the entity being removed. + * + * Update the device status and forget everything about @entity, putting + * the device reference to it, if it is a queue. Entities belonging to + * groups are not refcounted. + */ +static void bfq_forget_entity(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + struct bfq_sched_data *sd; + + BUG_ON(!entity->on_st); + + entity->on_st = 0; + st->wsum -= entity->weight; + if (bfqq != NULL) { + sd = entity->sched_data; + bfq_log_bfqq(bfqq->bfqd, bfqq, "forget_entity: %p %d", + bfqq, bfqq->ref); + bfq_put_queue(bfqq); + } +} + +/** + * bfq_put_idle_entity - release the idle tree ref of an entity. + * @st: service tree for the entity. + * @entity: the entity being released. + */ +static void bfq_put_idle_entity(struct bfq_service_tree *st, + struct bfq_entity *entity) +{ + bfq_idle_extract(st, entity); + bfq_forget_entity(st, entity); +} + +/** + * bfq_forget_idle - update the idle tree if necessary. + * @st: the service tree to act upon. + * + * To preserve the global O(log N) complexity we only remove one entry here; + * as the idle tree will not grow indefinitely this can be done safely. + */ +static void bfq_forget_idle(struct bfq_service_tree *st) +{ + struct bfq_entity *first_idle = st->first_idle; + struct bfq_entity *last_idle = st->last_idle; + + if (RB_EMPTY_ROOT(&st->active) && last_idle != NULL && + !bfq_gt(last_idle->finish, st->vtime)) { + /* + * Forget the whole idle tree, increasing the vtime past + * the last finish time of idle entities. + */ + st->vtime = last_idle->finish; + } + + if (first_idle != NULL && !bfq_gt(first_idle->finish, st->vtime)) + bfq_put_idle_entity(st, first_idle); +} + +static struct bfq_service_tree * +__bfq_entity_update_weight_prio(struct bfq_service_tree *old_st, + struct bfq_entity *entity) +{ + struct bfq_service_tree *new_st = old_st; + + if (entity->ioprio_changed) { + int new_boost_coeff = 1; + struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); + + if (bfqq != NULL) { + new_boost_coeff += + bfqq->high_weight_budget * BFQ_BOOST_COEFF / + BFQ_BOOST_BUDGET; + bfq_log_bfqq(bfqq->bfqd, bfqq, + "update_w_prio: wght %lu, hi-budg %lu, coef %d", + entity->weight, bfqq->high_weight_budget, + new_boost_coeff); + } + + BUG_ON(old_st->wsum < entity->weight); + old_st->wsum -= entity->weight; + + if (entity->new_weight != entity->orig_weight) { + entity->orig_weight = entity->new_weight; + entity->ioprio = + bfq_weight_to_ioprio(entity->orig_weight); + } else if (entity->new_ioprio != entity->ioprio) { + entity->ioprio = entity->new_ioprio; + entity->orig_weight = + bfq_ioprio_to_weight(entity->ioprio); + } else + entity->new_weight = entity->orig_weight = + bfq_ioprio_to_weight(entity->ioprio); + + entity->ioprio_class = entity->new_ioprio_class; + entity->ioprio_changed = 0; + + /* + * NOTE: here we may be changing the weight too early, + * this will cause unfairness. The correct approach + * would have required additional complexity to defer + * weight changes to the proper time instants (i.e., + * when entity->finish <= old_st->vtime). + */ + new_st = bfq_entity_service_tree(entity); + entity->weight = entity->orig_weight * new_boost_coeff; + new_st->wsum += entity->weight; + + if (new_st != old_st) + entity->start = new_st->vtime; + } + + return new_st; +} + +/** + * bfq_bfqq_served - update the scheduler status after selection for service. + * @bfqq: the queue being served. + * @served: bytes to transfer. + * + * NOTE: this can be optimized, as the timestamps of upper level entities + * are synchronized every time a new bfqq is selected for service. By now, + * we keep it to better check consistency. + */ +static void bfq_bfqq_served(struct bfq_queue *bfqq, unsigned long served) +{ + struct bfq_entity *entity = &bfqq->entity; + struct bfq_service_tree *st; + + for_each_entity(entity) { + st = bfq_entity_service_tree(entity); + + entity->service += served; + WARN_ON_ONCE(entity->service > entity->budget); + BUG_ON(st->wsum == 0); + + st->vtime += bfq_delta(served, st->wsum); + bfq_forget_idle(st); + } + bfq_log_bfqq(bfqq->bfqd, bfqq, "bfqq_served %lu secs", served); +} + +/** + * bfq_bfqq_charge_full_budget - set the service to the entity budget. + * @bfqq: the queue that needs a service update. + * + * When it's not possible to be fair in the service domain, because + * a queue is not consuming its budget fast enough (the meaning of + * fast depends on the timeout parameter), we charge it a full + * budget. In this way we should obtain a sort of time-domain + * fairness among all the seeky/slow queues. + */ +static inline void bfq_bfqq_charge_full_budget(struct bfq_queue *bfqq) +{ + struct bfq_entity *entity = &bfqq->entity; + + bfq_log_bfqq(bfqq->bfqd, bfqq, "charge_full_budget"); + + bfq_bfqq_served(bfqq, entity->budget - entity->service); +} + +/** + * __bfq_activate_entity - activate an entity. + * @entity: the entity being activated. + * + * Called whenever an entity is activated, i.e., it is not active and one + * of its children receives a new request, or has to be reactivated due to + * budget exhaustion. It uses the current budget of the entity (and the + * service received if @entity is active) of the queue to calculate its + * timestamps. + */ +static void __bfq_activate_entity(struct bfq_entity *entity) +{ + struct bfq_sched_data *sd = entity->sched_data; + struct bfq_service_tree *st = bfq_entity_service_tree(entity); + + if (entity == sd->active_entity) { + BUG_ON(entity->tree != NULL); + /* + * If we are requeueing the current entity we have + * to take care of not charging to it service it has + * not received. + */ + bfq_calc_finish(entity, entity->service); + entity->start = entity->finish; + sd->active_entity = NULL; + } else if (entity->tree == &st->active) { + /* + * Requeueing an entity due to a change of some + * next_active entity below it. We reuse the old + * start time. + */ + bfq_active_extract(st, entity); + } else if (entity->tree == &st->idle) { + /* + * Must be on the idle tree, bfq_idle_extract() will + * check for that. + */ + bfq_idle_extract(st, entity); + entity->start = bfq_gt(st->vtime, entity->finish) ? + st->vtime : entity->finish; + } else { + /* + * The finish time of the entity may be invalid, and + * it is in the past for sure, otherwise the queue + * would have been on the idle tree. + */ + entity->start = st->vtime; + st->wsum += entity->weight; + bfq_get_entity(entity); + + BUG_ON(entity->on_st); + entity->on_st = 1; + } + + st = __bfq_entity_update_weight_prio(st, entity); + bfq_calc_finish(entity, entity->budget); + bfq_active_insert(st, entity); +} + +/** + * bfq_activate_entity - activate an entity and its ancestors if necessary. + * @entity: the entity to activate. + * + * Activate @entity and all the entities on the path from it to the root. + */ +static void bfq_activate_entity(struct bfq_entity *entity) +{ + struct bfq_sched_data *sd; + + for_each_entity(entity) { + __bfq_activate_entity(entity); + + sd = entity->sched_data; + if (!bfq_update_next_active(sd)) + /* + * No need to propagate the activation to the + * upper entities, as they will be updated when + * the active entity is rescheduled. + */ + break; + } +} + +/** + * __bfq_deactivate_entity - deactivate an entity from its service tree. + * @entity: the entity to deactivate. + * @requeue: if false, the entity will not be put into the idle tree. + * + * Deactivate an entity, independently from its previous state. If the + * entity was not on a service tree just return, otherwise if it is on + * any scheduler tree, extract it from that tree, and if necessary + * and if the caller did not specify @requeue, put it on the idle tree. + * + * Return %1 if the caller should update the entity hierarchy, i.e., + * if the entity was under service or if it was the next_active for + * its sched_data; return %0 otherwise. + */ +static int __bfq_deactivate_entity(struct bfq_entity *entity, int requeue) +{ + struct bfq_sched_data *sd = entity->sched_data; + struct bfq_service_tree *st = bfq_entity_service_tree(entity); + int was_active = entity == sd->active_entity; + int ret = 0; + + if (!entity->on_st) + return 0; + + BUG_ON(was_active && entity->tree != NULL); + + if (was_active) { + bfq_calc_finish(entity, entity->service); + sd->active_entity = NULL; + } else if (entity->tree == &st->active) + bfq_active_extract(st, entity); + else if (entity->tree == &st->idle) + bfq_idle_extract(st, entity); + else if (entity->tree != NULL) + BUG(); + + if (was_active || sd->next_active == entity) + ret = bfq_update_next_active(sd); + + if (!requeue || !bfq_gt(entity->finish, st->vtime)) + bfq_forget_entity(st, entity); + else + bfq_idle_insert(st, entity); + + BUG_ON(sd->active_entity == entity); + BUG_ON(sd->next_active == entity); + + return ret; +} + +/** + * bfq_deactivate_entity - deactivate an entity. + * @entity: the entity to deactivate. + * @requeue: true if the entity can be put on the idle tree + */ +static void bfq_deactivate_entity(struct bfq_entity *entity, int requeue) +{ + struct bfq_sched_data *sd; + struct bfq_entity *parent; + + for_each_entity_safe(entity, parent) { + sd = entity->sched_data; + + if (!__bfq_deactivate_entity(entity, requeue)) + /* + * The parent entity is still backlogged, and + * we don't need to update it as it is still + * under service. + */ + break; + + if (sd->next_active != NULL) + /* + * The parent entity is still backlogged and + * the budgets on the path towards the root + * need to be updated. + */ + goto update; + + /* + * If we reach there the parent is no more backlogged and + * we want to propagate the dequeue upwards. + */ + requeue = 1; + } + + return; + +update: + entity = parent; + for_each_entity(entity) { + __bfq_activate_entity(entity); + + sd = entity->sched_data; + if (!bfq_update_next_active(sd)) + break; + } +} + +/** + * bfq_update_vtime - update vtime if necessary. + * @st: the service tree to act upon. + * + * If necessary update the service tree vtime to have at least one + * eligible entity, skipping to its start time. Assumes that the + * active tree of the device is not empty. + * + * NOTE: this hierarchical implementation updates vtimes quite often, + * we may end up with reactivated tasks getting timestamps after a + * vtime skip done because we needed a ->first_active entity on some + * intermediate node. + */ +static void bfq_update_vtime(struct bfq_service_tree *st) +{ + struct bfq_entity *entry; + struct rb_node *node = st->active.rb_node; + + entry = rb_entry(node, struct bfq_entity, rb_node); + if (bfq_gt(entry->min_start, st->vtime)) { + st->vtime = entry->min_start; + bfq_forget_idle(st); + } +} + +/** + * bfq_first_active - find the eligible entity with the smallest finish time + * @st: the service tree to select from. + * + * This function searches the first schedulable entity, starting from the + * root of the tree and going on the left every time on this side there is + * a subtree with at least one eligible (start >= vtime) entity. The path + * on the right is followed only if a) the left subtree contains no eligible + * entities and b) no eligible entity has been found yet. + */ +static struct bfq_entity *bfq_first_active_entity(struct bfq_service_tree *st) +{ + struct bfq_entity *entry, *first = NULL; + struct rb_node *node = st->active.rb_node; + + while (node != NULL) { + entry = rb_entry(node, struct bfq_entity, rb_node); +left: + if (!bfq_gt(entry->start, st->vtime)) + first = entry; + + BUG_ON(bfq_gt(entry->min_start, st->vtime)); + + if (node->rb_left != NULL) { + entry = rb_entry(node->rb_left, + struct bfq_entity, rb_node); + if (!bfq_gt(entry->min_start, st->vtime)) { + node = node->rb_left; + goto left; + } + } + if (first != NULL) + break; + node = node->rb_right; + } + + BUG_ON(first == NULL && !RB_EMPTY_ROOT(&st->active)); + return first; +} + +/** + * __bfq_lookup_next_entity - return the first eligible entity in @st. + * @st: the service tree. + * + * Update the virtual time in @st and return the first eligible entity + * it contains. + */ +static struct bfq_entity *__bfq_lookup_next_entity(struct bfq_service_tree *st) +{ + struct bfq_entity *entity; + + if (RB_EMPTY_ROOT(&st->active)) + return NULL; + + bfq_update_vtime(st); + entity = bfq_first_active_entity(st); + BUG_ON(bfq_gt(entity->start, st->vtime)); + + return entity; +} + +/** + * bfq_lookup_next_entity - return the first eligible entity in @sd. + * @sd: the sched_data. + * @extract: if true the returned entity will be also extracted from @sd. + * + * NOTE: since we cache the next_active entity at each level of the + * hierarchy, the complexity of the lookup can be decreased with + * absolutely no effort just returning the cached next_active value; + * we prefer to do full lookups to test the consistency of * the data + * structures. + */ +static struct bfq_entity *bfq_lookup_next_entity(struct bfq_sched_data *sd, + int extract) +{ + struct bfq_service_tree *st = sd->service_tree; + struct bfq_entity *entity; + int i; + + BUG_ON(sd->active_entity != NULL); + + for (i = 0; i < BFQ_IOPRIO_CLASSES; i++, st++) { + entity = __bfq_lookup_next_entity(st); + if (entity != NULL) { + if (extract) { + bfq_check_next_active(sd, entity); + bfq_active_extract(st, entity); + sd->active_entity = entity; + sd->next_active = NULL; + } + break; + } + } + + return entity; +} + +/* + * Get next queue for service. + */ +static struct bfq_queue *bfq_get_next_queue(struct bfq_data *bfqd) +{ + struct bfq_entity *entity = NULL; + struct bfq_sched_data *sd; + struct bfq_queue *bfqq; + + BUG_ON(bfqd->active_queue != NULL); + + if (bfqd->busy_queues == 0) + return NULL; + + sd = &bfqd->root_group->sched_data; + for (; sd != NULL; sd = entity->my_sched_data) { + entity = bfq_lookup_next_entity(sd, 1); + BUG_ON(entity == NULL); + entity->service = 0; + } + + bfqq = bfq_entity_to_bfqq(entity); + BUG_ON(bfqq == NULL); + + return bfqq; +} + +static void __bfq_bfqd_reset_active(struct bfq_data *bfqd) +{ + if (bfqd->active_cic != NULL) { + put_io_context(bfqd->active_cic->ioc); + bfqd->active_cic = NULL; + } + + bfqd->active_queue = NULL; + del_timer(&bfqd->idle_slice_timer); +} + +static void bfq_deactivate_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq, + int requeue) +{ + struct bfq_entity *entity = &bfqq->entity; + + if (bfqq == bfqd->active_queue) + __bfq_bfqd_reset_active(bfqd); + + bfq_deactivate_entity(entity, requeue); +} + +static void bfq_activate_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + struct bfq_entity *entity = &bfqq->entity; + + bfq_activate_entity(entity); +} + +/* + * Called when the bfqq no longer has requests pending, remove it from + * the service tree. + */ +static void bfq_del_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq, + int requeue) +{ + BUG_ON(!bfq_bfqq_busy(bfqq)); + BUG_ON(!RB_EMPTY_ROOT(&bfqq->sort_list)); + + bfq_log_bfqq(bfqd, bfqq, "del from busy"); + + bfq_clear_bfqq_busy(bfqq); + + BUG_ON(bfqd->busy_queues == 0); + bfqd->busy_queues--; + + bfq_deactivate_bfqq(bfqd, bfqq, requeue); +} + +/* + * Called when an inactive queue receives a new request. + */ +static void bfq_add_bfqq_busy(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + BUG_ON(bfq_bfqq_busy(bfqq)); + BUG_ON(bfqq == bfqd->active_queue); + + bfq_log_bfqq(bfqd, bfqq, "add to busy"); + + bfq_activate_bfqq(bfqd, bfqq); + + bfq_mark_bfqq_busy(bfqq); + bfqd->busy_queues++; +} diff --git a/block/bfq.h b/block/bfq.h new file mode 100644 index 0000000000000..07977ad938ea5 --- /dev/null +++ b/block/bfq.h @@ -0,0 +1,555 @@ +/* + * BFQ-v1-r1 for 2.6.35: data structures and common functions prototypes. + * + * Based on ideas and code from CFQ: + * Copyright (C) 2003 Jens Axboe + * + * Copyright (C) 2008 Fabio Checconi + * Paolo Valente + */ + +#ifndef _BFQ_H +#define _BFQ_H + +#include +#include +#include +#include + +#define BFQ_IOPRIO_CLASSES 3 + +#define BFQ_MIN_WEIGHT 1 +#define BFQ_MAX_WEIGHT 1000 + +#define BFQ_DEFAULT_GRP_WEIGHT 10 +#define BFQ_DEFAULT_GRP_IOPRIO 0 +#define BFQ_DEFAULT_GRP_CLASS IOPRIO_CLASS_BE + +/* Constants used in weight boosting (in its turn used to reduce latencies): */ +/* max factor by which the weight of a boosted queue is multiplied */ +#define BFQ_BOOST_COEFF 10 +/* max number of sectors that can be served during a boosting period */ +#define BFQ_BOOST_BUDGET 49152 +/* max duration of a boosting period, msec */ +#define BFQ_BOOST_TIMEOUT 6000 +/* min idle period after which boosting may be reactivated for a queue, msec */ +#define BFQ_MIN_ACT_INTERVAL 20000 + +struct bfq_entity; + +/** + * struct bfq_service_tree - per ioprio_class service tree. + * @active: tree for active entities (i.e., those backlogged). + * @idle: tree for idle entities (i.e., those not backlogged, with V <= F_i). + * @first_idle: idle entity with minimum F_i. + * @last_idle: idle entity with maximum F_i. + * @vtime: scheduler virtual time. + * @wsum: scheduler weight sum; active and idle entities contribute to it. + * + * Each service tree represents a B-WF2Q+ scheduler on its own. Each + * ioprio_class has its own independent scheduler, and so its own + * bfq_service_tree. All the fields are protected by the queue lock + * of the containing bfqd. + */ +struct bfq_service_tree { + struct rb_root active; + struct rb_root idle; + + struct bfq_entity *first_idle; + struct bfq_entity *last_idle; + + u64 vtime; + unsigned long wsum; +}; + +/** + * struct bfq_sched_data - multi-class scheduler. + * @active_entity: entity under service. + * @next_active: head-of-the-line entity in the scheduler. + * @service_tree: array of service trees, one per ioprio_class. + * + * bfq_sched_data is the basic scheduler queue. It supports three + * ioprio_classes, and can be used either as a toplevel queue or as + * an intermediate queue on a hierarchical setup. + * @next_active points to the active entity of the sched_data service + * trees that will be scheduled next. + * + * The supported ioprio_classes are the same as in CFQ, in descending + * priority order, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE. + * Requests from higher priority queues are served before all the + * requests from lower priority queues; among requests of the same + * queue requests are served according to B-WF2Q+. + * All the fields are protected by the queue lock of the containing bfqd. + */ +struct bfq_sched_data { + struct bfq_entity *active_entity; + struct bfq_entity *next_active; + struct bfq_service_tree service_tree[BFQ_IOPRIO_CLASSES]; +}; + +/** + * struct bfq_entity - schedulable entity. + * @rb_node: service_tree member. + * @on_st: flag, true if the entity is on a tree (either the active or + * the idle one of its service_tree). + * @finish: B-WF2Q+ finish timestamp (aka F_i). + * @start: B-WF2Q+ start timestamp (aka S_i). + * @tree: tree the entity is enqueued into; %NULL if not on a tree. + * @min_start: minimum start time of the (active) subtree rooted at + * this entity; used for O(log N) lookups into active trees. + * @service: service received during the last round of service. + * @budget: budget used to calculate F_i; F_i = S_i + @budget / @weight. + * @weight: weight of the queue + * @parent: parent entity, for hierarchical scheduling. + * @my_sched_data: for non-leaf nodes in the cgroup hierarchy, the + * associated scheduler queue, %NULL on leaf nodes. + * @sched_data: the scheduler queue this entity belongs to. + * @ioprio: the ioprio in use. + * @new_weight: when a weight change is requested, the new weight value. + * @orig_weight: original weight, used to implement weight boosting + * @new_ioprio: when an ioprio change is requested, the new ioprio value. + * @ioprio_class: the ioprio_class in use. + * @new_ioprio_class: when an ioprio_class change is requested, the new + * ioprio_class value. + * @ioprio_changed: flag, true when the user requested a weight, ioprio or + * ioprio_class change. + * + * A bfq_entity is used to represent either a bfq_queue (leaf node in the + * cgroup hierarchy) or a bfq_group into the upper level scheduler. Each + * entity belongs to the sched_data of the parent group in the cgroup + * hierarchy. Non-leaf entities have also their own sched_data, stored + * in @my_sched_data. + * + * Each entity stores independently its priority values; this would + * allow different weights on different devices, but this + * functionality is not exported to userspace by now. Priorities and + * weights are updated lazily, first storing the new values into the + * new_* fields, then setting the @ioprio_changed flag. As soon as + * there is a transition in the entity state that allows the priority + * update to take place the effective and the requested priority + * values are synchronized. + * + * Unless cgroups are used, the weight value is calculated from the + * ioprio to export the same interface as CFQ. When dealing with + * ``well-behaved'' queues (i.e., queues that do not spend too much + * time to consume their budget and have true sequential behavior, and + * when there are no external factors breaking anticipation) the + * relative weights at each level of the cgroups hierarchy should be + * guaranteed. All the fields are protected by the queue lock of the + * containing bfqd. + */ +struct bfq_entity { + struct rb_node rb_node; + + int on_st; + + u64 finish; + u64 start; + + struct rb_root *tree; + + u64 min_start; + + unsigned long service, budget; + unsigned short weight, new_weight; + unsigned short orig_weight; + + struct bfq_entity *parent; + + struct bfq_sched_data *my_sched_data; + struct bfq_sched_data *sched_data; + + unsigned short ioprio, new_ioprio; + unsigned short ioprio_class, new_ioprio_class; + + int ioprio_changed; +}; + +struct bfq_group; + +/** + * struct bfq_data - per device data structure. + * @queue: request queue for the managed device. + * @root_group: root bfq_group for the device. + * @busy_queues: number of bfq_queues containing requests (including the + * queue under service, even if it is idling). + * @queued: number of queued requests. + * @rq_in_driver: number of requests dispatched and waiting for completion. + * @sync_flight: number of sync requests in the driver. + * @max_rq_in_driver: max number of reqs in driver in the last @hw_tag_samples + * completed requests . + * @hw_tag_samples: nr of samples used to calculate hw_tag. + * @hw_tag: flag set to one if the driver is showing a queueing behavior. + * @budgets_assigned: number of budgets assigned. + * @idle_slice_timer: timer set when idling for the next sequential request + * from the queue under service. + * @unplug_work: delayed work to restart dispatching on the request queue. + * @active_queue: bfq_queue under service. + * @active_cic: cfq_io_context (cic) associated with the @active_queue. + * @last_position: on-disk position of the last served request. + * @last_budget_start: beginning of the last budget. + * @last_idling_start: beginning of the last idle slice. + * @peak_rate: peak transfer rate observed for a budget. + * @peak_rate_samples: number of samples used to calculate @peak_rate. + * @bfq_max_budget: maximum budget allotted to a bfq_queue before rescheduling. + * @cic_index: use small consequent indexes as radix tree keys to reduce depth + * @cic_list: list of all the cics active on the bfq_data device. + * @group_list: list of all the bfq_groups active on the device. + * @active_list: list of all the bfq_queues active on the device. + * @idle_list: list of all the bfq_queues idle on the device. + * @bfq_quantum: max number of requests dispatched per dispatch round. + * @bfq_fifo_expire: timeout for async/sync requests; when it expires + * requests are served in fifo order. + * @bfq_back_penalty: weight of backward seeks wrt forward ones. + * @bfq_back_max: maximum allowed backward seek. + * @bfq_slice_idle: maximum idling time. + * @bfq_user_max_budget: user-configured max budget value (0 for auto-tuning). + * @bfq_max_budget_async_rq: maximum budget (in nr of requests) allotted to + * async queues. + * @bfq_timeout: timeout for bfq_queues to consume their budget; used to + * to prevent seeky queues to impose long latencies to well + * behaved ones (this also implies that seeky queues cannot + * receive guarantees in the service domain; after a timeout + * they are charged for the whole allocated budget, to try + * to preserve a behavior reasonably fair among them, but + * without service-domain guarantees). + * + * All the fields are protected by the @queue lock. + */ +struct bfq_data { + struct request_queue *queue; + + struct bfq_group *root_group; + + int busy_queues; + int queued; + int rq_in_driver; + int sync_flight; + + int max_rq_in_driver; + int hw_tag_samples; + int hw_tag; + + int budgets_assigned; + + struct timer_list idle_slice_timer; + struct work_struct unplug_work; + + struct bfq_queue *active_queue; + struct cfq_io_context *active_cic; + + sector_t last_position; + + ktime_t last_budget_start; + ktime_t last_idling_start; + int peak_rate_samples; + u64 peak_rate; + unsigned long bfq_max_budget; + + unsigned int cic_index; + struct list_head cic_list; + struct hlist_head group_list; + struct list_head active_list; + struct list_head idle_list; + + unsigned int bfq_quantum; + unsigned int bfq_fifo_expire[2]; + unsigned int bfq_back_penalty; + unsigned int bfq_back_max; + unsigned int bfq_slice_idle; + + unsigned int bfq_user_max_budget; + unsigned int bfq_max_budget_async_rq; + unsigned int bfq_timeout[2]; + + bool low_latency; +}; + +/** + * struct bfq_queue - leaf schedulable entity. + * @ref: reference counter. + * @bfqd: parent bfq_data. + * @sort_list: sorted list of pending requests. + * @next_rq: if fifo isn't expired, next request to serve. + * @queued: nr of requests queued in @sort_list. + * @allocated: currently allocated requests. + * @meta_pending: pending metadata requests. + * @fifo: fifo list of requests in sort_list. + * @entity: entity representing this queue in the scheduler. + * @max_budget: maximum budget allowed from the feedback mechanism. + * @budget_timeout: budget expiration (in jiffies). + * @dispatched: number of requests on the dispatch list or inside driver. + * @org_ioprio: saved ioprio during boosted periods. + * @org_ioprio_class: saved ioprio_class during boosted periods. + * @flags: status flags. + * @bfqq_list: node for active/idle bfqq list inside our bfqd. + * @seek_samples: number of seeks sampled + * @seek_total: sum of the distances of the seeks sampled + * @seek_mean: mean seek distance + * @last_request_pos: position of the last request enqueued + * @pid: pid of the process owning the queue, used for logging purposes. + * @last_activation_time: time of the last (idle -> backlogged) transition + * @high_weight_budget: number of sectors left to serve with boosted weight + * + * A bfq_queue is a leaf request queue; it can be associated to an io_context + * or more (if it is an async one). @cgroup holds a reference to the + * cgroup, to be sure that it does not disappear while a bfqq still + * references it (mostly to avoid races between request issuing and task + * migration followed by cgroup distruction). + * All the fields are protected by the queue lock of the containing bfqd. + */ +struct bfq_queue { + atomic_t ref; + struct bfq_data *bfqd; + + struct rb_root sort_list; + struct request *next_rq; + int queued[2]; + int allocated[2]; + int meta_pending; + struct list_head fifo; + + struct bfq_entity entity; + + unsigned long max_budget; + unsigned long budget_timeout; + + int dispatched; + + unsigned short org_ioprio; + unsigned short org_ioprio_class; + + unsigned int flags; + + struct list_head bfqq_list; + + unsigned int seek_samples; + u64 seek_total; + sector_t seek_mean; + sector_t last_request_pos; + + pid_t pid; + + u64 last_activation_time; + unsigned long high_weight_budget; +}; + +enum bfqq_state_flags { + BFQ_BFQQ_FLAG_busy = 0, /* has requests or is under service */ + BFQ_BFQQ_FLAG_wait_request, /* waiting for a request */ + BFQ_BFQQ_FLAG_must_alloc, /* must be allowed rq alloc */ + BFQ_BFQQ_FLAG_fifo_expire, /* FIFO checked in this slice */ + BFQ_BFQQ_FLAG_idle_window, /* slice idling enabled */ + BFQ_BFQQ_FLAG_prio_changed, /* task priority has changed */ + BFQ_BFQQ_FLAG_sync, /* synchronous queue */ + BFQ_BFQQ_FLAG_budget_new, /* no completion with this budget */ +}; + +#define BFQ_BFQQ_FNS(name) \ +static inline void bfq_mark_bfqq_##name(struct bfq_queue *bfqq) \ +{ \ + (bfqq)->flags |= (1 << BFQ_BFQQ_FLAG_##name); \ +} \ +static inline void bfq_clear_bfqq_##name(struct bfq_queue *bfqq) \ +{ \ + (bfqq)->flags &= ~(1 << BFQ_BFQQ_FLAG_##name); \ +} \ +static inline int bfq_bfqq_##name(const struct bfq_queue *bfqq) \ +{ \ + return ((bfqq)->flags & (1 << BFQ_BFQQ_FLAG_##name)) != 0; \ +} + +BFQ_BFQQ_FNS(busy); +BFQ_BFQQ_FNS(wait_request); +BFQ_BFQQ_FNS(must_alloc); +BFQ_BFQQ_FNS(fifo_expire); +BFQ_BFQQ_FNS(idle_window); +BFQ_BFQQ_FNS(prio_changed); +BFQ_BFQQ_FNS(sync); +BFQ_BFQQ_FNS(budget_new); +#undef BFQ_BFQQ_FNS + +/* Logging facilities. */ +#define bfq_log_bfqq(bfqd, bfqq, fmt, args...) \ + blk_add_trace_msg((bfqd)->queue, "bfq%d " fmt, (bfqq)->pid, ##args) + +#define bfq_log(bfqd, fmt, args...) \ + blk_add_trace_msg((bfqd)->queue, "bfq " fmt, ##args) + +/* Expiration reasons. */ +enum bfqq_expiration { + BFQ_BFQQ_TOO_IDLE = 0, /* queue has been idling for too long */ + BFQ_BFQQ_BUDGET_TIMEOUT, /* budget took too long to be used */ + BFQ_BFQQ_BUDGET_EXHAUSTED, /* budget consumed */ + BFQ_BFQQ_NO_MORE_REQUESTS, /* the queue has no more requests */ +}; + +#ifdef CONFIG_CGROUP_BFQIO +/** + * struct bfq_group - per (device, cgroup) data structure. + * @entity: schedulable entity to insert into the parent group sched_data. + * @sched_data: own sched_data, to contain child entities (they may be + * both bfq_queues and bfq_groups). + * @group_node: node to be inserted into the bfqio_cgroup->group_data + * list of the containing cgroup's bfqio_cgroup. + * @bfqd_node: node to be inserted into the @bfqd->group_list list + * of the groups active on the same device; used for cleanup. + * @bfqd: the bfq_data for the device this group acts upon. + * @async_bfqq: array of async queues for all the tasks belonging to + * the group, one queue per ioprio value per ioprio_class, + * except for the idle class that has only one queue. + * @async_idle_bfqq: async queue for the idle class (ioprio is ignored). + * @my_entity: pointer to @entity, %NULL for the toplevel group; used + * to avoid too many special cases during group creation/migration. + * + * Each (device, cgroup) pair has its own bfq_group, i.e., for each cgroup + * there is a set of bfq_groups, each one collecting the lower-level + * entities belonging to the group that are acting on the same device. + * + * Locking works as follows: + * o @group_node is protected by the bfqio_cgroup lock, and is accessed + * via RCU from its readers. + * o @bfqd is protected by the queue lock, RCU is used to access it + * from the readers. + * o All the other fields are protected by the @bfqd queue lock. + */ +struct bfq_group { + struct bfq_entity entity; + struct bfq_sched_data sched_data; + + struct hlist_node group_node; + struct hlist_node bfqd_node; + + void *bfqd; + + struct bfq_queue *async_bfqq[2][IOPRIO_BE_NR]; + struct bfq_queue *async_idle_bfqq; + + struct bfq_entity *my_entity; +}; + +/** + * struct bfqio_cgroup - bfq cgroup data structure. + * @css: subsystem state for bfq in the containing cgroup. + * @weight: cgroup weight. + * @ioprio: cgroup ioprio. + * @ioprio_class: cgroup ioprio_class. + * @lock: spinlock that protects @ioprio, @ioprio_class and @group_data. + * @group_data: list containing the bfq_group belonging to this cgroup. + * + * @group_data is accessed using RCU, with @lock protecting the updates, + * @ioprio and @ioprio_class are protected by @lock. + */ +struct bfqio_cgroup { + struct cgroup_subsys_state css; + + unsigned short weight, ioprio, ioprio_class; + + spinlock_t lock; + struct hlist_head group_data; +}; +#else +struct bfq_group { + struct bfq_sched_data sched_data; + + struct bfq_queue *async_bfqq[2][IOPRIO_BE_NR]; + struct bfq_queue *async_idle_bfqq; +}; +#endif + +static inline struct bfq_service_tree * +bfq_entity_service_tree(struct bfq_entity *entity) +{ + struct bfq_sched_data *sched_data = entity->sched_data; + unsigned int idx = entity->ioprio_class - 1; + + BUG_ON(idx >= BFQ_IOPRIO_CLASSES); + BUG_ON(sched_data == NULL); + + return sched_data->service_tree + idx; +} + +static inline struct bfq_queue *cic_to_bfqq(struct cfq_io_context *cic, + int is_sync) +{ + return cic->cfqq[!!is_sync]; +} + +static inline void cic_set_bfqq(struct cfq_io_context *cic, + struct bfq_queue *bfqq, int is_sync) +{ + cic->cfqq[!!is_sync] = bfqq; +} + +static inline void call_for_each_cic(struct io_context *ioc, + void (*func)(struct io_context *, + struct cfq_io_context *)) +{ + struct cfq_io_context *cic; + struct hlist_node *n; + + rcu_read_lock(); + hlist_for_each_entry_rcu(cic, n, &ioc->bfq_cic_list, cic_list) + func(ioc, cic); + rcu_read_unlock(); +} + +#define CIC_DEAD_KEY 1ul +#define CIC_DEAD_INDEX_SHIFT 1 + +static inline void *bfqd_dead_key(struct bfq_data *bfqd) +{ + return (void *)(bfqd->cic_index << CIC_DEAD_INDEX_SHIFT | CIC_DEAD_KEY); +} + +/** + * bfq_get_bfqd_locked - get a lock to a bfqd using a RCU protected pointer. + * @ptr: a pointer to a bfqd. + * @flags: storage for the flags to be saved. + * + * This function allows cic->key and bfqg->bfqd to be protected by the + * queue lock of the bfqd they reference; the pointer is dereferenced + * under RCU, so the storage for bfqd is assured to be safe as long + * as the RCU read side critical section does not end. After the + * bfqd->queue->queue_lock is taken the pointer is rechecked, to be + * sure that no other writer accessed it. If we raced with a writer, + * the function returns NULL, with the queue unlocked, otherwise it + * returns the dereferenced pointer, with the queue locked. + */ +static inline struct bfq_data *bfq_get_bfqd_locked(void **ptr, + unsigned long *flags) +{ + struct bfq_data *bfqd; + + rcu_read_lock(); + bfqd = rcu_dereference(*(struct bfq_data **)ptr); + + if (bfqd != NULL && !((unsigned long) bfqd & CIC_DEAD_KEY)) { + spin_lock_irqsave(bfqd->queue->queue_lock, *flags); + if (*ptr == bfqd) + goto out; + spin_unlock_irqrestore(bfqd->queue->queue_lock, *flags); + } + + bfqd = NULL; +out: + rcu_read_unlock(); + return bfqd; +} + +static inline void bfq_put_bfqd_unlock(struct bfq_data *bfqd, + unsigned long *flags) +{ + spin_unlock_irqrestore(bfqd->queue->queue_lock, *flags); +} + +static void bfq_changed_ioprio(struct io_context *ioc, + struct cfq_io_context *cic); +static void bfq_put_queue(struct bfq_queue *bfqq); +static void bfq_dispatch_insert(struct request_queue *q, struct request *rq); +static struct bfq_queue *bfq_get_queue(struct bfq_data *bfqd, + struct bfq_group *bfqg, int is_sync, + struct io_context *ioc, gfp_t gfp_mask); +static void bfq_put_async_queues(struct bfq_data *bfqd, struct bfq_group *bfqg); +static void bfq_exit_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq); +#endif diff --git a/block/blk-ioc.c b/block/blk-ioc.c index b791022beef31..ced5f8c9fd631 100644 --- a/block/blk-ioc.c +++ b/block/blk-ioc.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include /* for max_pfn/max_low_pfn */ #include @@ -16,13 +17,12 @@ */ static struct kmem_cache *iocontext_cachep; -static void cfq_dtor(struct io_context *ioc) +static void hlist_sched_dtor(struct io_context *ioc, struct hlist_head *list) { - if (!hlist_empty(&ioc->cic_list)) { + if (!hlist_empty(list)) { struct cfq_io_context *cic; - cic = list_entry(ioc->cic_list.first, struct cfq_io_context, - cic_list); + cic = list_entry(list->first, struct cfq_io_context, cic_list); cic->dtor(ioc); } } @@ -40,7 +40,9 @@ int put_io_context(struct io_context *ioc) if (atomic_long_dec_and_test(&ioc->refcount)) { rcu_read_lock(); - cfq_dtor(ioc); + + hlist_sched_dtor(ioc, &ioc->cic_list); + hlist_sched_dtor(ioc, &ioc->bfq_cic_list); rcu_read_unlock(); kmem_cache_free(iocontext_cachep, ioc); @@ -50,15 +52,14 @@ int put_io_context(struct io_context *ioc) } EXPORT_SYMBOL(put_io_context); -static void cfq_exit(struct io_context *ioc) +static void hlist_sched_exit(struct io_context *ioc, struct hlist_head *list) { rcu_read_lock(); - if (!hlist_empty(&ioc->cic_list)) { + if (!hlist_empty(list)) { struct cfq_io_context *cic; - cic = list_entry(ioc->cic_list.first, struct cfq_io_context, - cic_list); + cic = list_entry(list->first, struct cfq_io_context, cic_list); cic->exit(ioc); } rcu_read_unlock(); @@ -74,9 +75,10 @@ void exit_io_context(struct task_struct *task) task->io_context = NULL; task_unlock(task); - if (atomic_dec_and_test(&ioc->nr_tasks)) - cfq_exit(ioc); - + if (atomic_dec_and_test(&ioc->nr_tasks)) { + hlist_sched_exit(ioc, &ioc->cic_list); + hlist_sched_exit(ioc, &ioc->bfq_cic_list); + } put_io_context(ioc); } @@ -89,12 +91,14 @@ struct io_context *alloc_io_context(gfp_t gfp_flags, int node) atomic_long_set(&ret->refcount, 1); atomic_set(&ret->nr_tasks, 1); spin_lock_init(&ret->lock); - ret->ioprio_changed = 0; + bitmap_zero(ret->ioprio_changed, IOC_IOPRIO_CHANGED_BITS); ret->ioprio = 0; ret->last_waited = 0; /* doesn't matter... */ ret->nr_batch_requests = 0; /* because this is 0 */ INIT_RADIX_TREE(&ret->radix_root, GFP_ATOMIC | __GFP_HIGH); INIT_HLIST_HEAD(&ret->cic_list); + INIT_RADIX_TREE(&ret->bfq_radix_root, GFP_ATOMIC | __GFP_HIGH); + INIT_HLIST_HEAD(&ret->bfq_cic_list); ret->ioc_data = NULL; } diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index ea83a4f0c27df..7b2163e141870 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -2840,7 +2840,6 @@ static void changed_ioprio(struct io_context *ioc, struct cfq_io_context *cic) static void cfq_ioc_set_ioprio(struct io_context *ioc) { call_for_each_cic(ioc, changed_ioprio); - ioc->ioprio_changed = 0; } static void cfq_init_cfqq(struct cfq_data *cfqd, struct cfq_queue *cfqq, @@ -3124,8 +3123,13 @@ cfq_get_io_context(struct cfq_data *cfqd, gfp_t gfp_mask) goto err_free; out: - smp_read_barrier_depends(); - if (unlikely(ioc->ioprio_changed)) + /* + * test_and_clear_bit() implies a memory barrier, paired with + * the wmb() in fs/ioprio.c, so the value seen for ioprio is the + * new one. + */ + if (unlikely(test_and_clear_bit(IOC_CFQ_IOPRIO_CHANGED, + ioc->ioprio_changed))) cfq_ioc_set_ioprio(ioc); #ifdef CONFIG_CFQ_GROUP_IOSCHED diff --git a/fs/ioprio.c b/fs/ioprio.c index 7da2a06508e54..95a6c2b04e0db 100644 --- a/fs/ioprio.c +++ b/fs/ioprio.c @@ -30,7 +30,7 @@ int set_task_ioprio(struct task_struct *task, int ioprio) { - int err; + int err, i; struct io_context *ioc; const struct cred *cred = current_cred(), *tcred; @@ -60,12 +60,17 @@ int set_task_ioprio(struct task_struct *task, int ioprio) err = -ENOMEM; break; } + /* let other ioc users see the new values */ + smp_wmb(); task->io_context = ioc; } while (1); if (!err) { ioc->ioprio = ioprio; - ioc->ioprio_changed = 1; + /* make sure schedulers see the new ioprio value */ + wmb(); + for (i = 0; i < IOC_IOPRIO_CHANGED_BITS; i++) + set_bit(i, ioc->ioprio_changed); } task_unlock(task); diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h index ccefff02b6cb0..37f523b85dd6d 100644 --- a/include/linux/cgroup_subsys.h +++ b/include/linux/cgroup_subsys.h @@ -66,3 +66,9 @@ SUBSYS(blkio) #endif /* */ + +#ifdef CONFIG_CGROUP_BFQIO +SUBSYS(bfqio) +#endif + +/* */ diff --git a/include/linux/iocontext.h b/include/linux/iocontext.h index b2eee896dcbc7..7cbace3e14f7b 100644 --- a/include/linux/iocontext.h +++ b/include/linux/iocontext.h @@ -1,14 +1,14 @@ #ifndef IOCONTEXT_H #define IOCONTEXT_H +#include #include #include -struct cfq_queue; struct cfq_io_context { void *key; - struct cfq_queue *cfqq[2]; + void *cfqq[2]; struct io_context *ioc; @@ -27,6 +27,16 @@ struct cfq_io_context { struct rcu_head rcu_head; }; +/* + * Indexes into the ioprio_changed bitmap. A bit set indicates that + * the corresponding I/O scheduler needs to see a ioprio update. + */ +enum { + IOC_CFQ_IOPRIO_CHANGED, + IOC_BFQ_IOPRIO_CHANGED, + IOC_IOPRIO_CHANGED_BITS +}; + /* * I/O subsystem state of the associated processes. It is refcounted * and kmalloc'ed. These could be shared between processes. @@ -39,7 +49,7 @@ struct io_context { spinlock_t lock; unsigned short ioprio; - unsigned short ioprio_changed; + DECLARE_BITMAP(ioprio_changed, IOC_IOPRIO_CHANGED_BITS); #if defined(CONFIG_BLK_CGROUP) || defined(CONFIG_BLK_CGROUP_MODULE) unsigned short cgroup_changed; @@ -53,6 +63,8 @@ struct io_context { struct radix_tree_root radix_root; struct hlist_head cic_list; + struct radix_tree_root bfq_radix_root; + struct hlist_head bfq_cic_list; void __rcu *ioc_data; }; From e3a63102a92029791090fe22a0001246cb1bfaef Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Sat, 12 Feb 2011 23:37:11 +0100 Subject: [PATCH 1005/2556] Switch from BFQ-v1 to BFQ-v2 for 2.6.37. BFQ-v2 provides the following four new features (apparently none of them is paid with a loss of aggregate throughput with respect to BFQ-v1). - Same low-latency guarantees with and without NCQ. - Latency for interactive applications about halved with respect to BFQ-v1. - When the low_latency tunable is set, also soft real-time applications now enjoy reduced latency. - A very little minimum bandwidth is now guaranteed to the Idle IO-scheduling class also when the other classes are backlogged, just to prevent them from starving. Signed-off-by: Paolo Valente --- block/bfq-iosched.c | 179 ++++++++++++++++++++++++++++++++------------ block/bfq-sched.c | 46 ++++++------ block/bfq.h | 34 +++++---- 3 files changed, 176 insertions(+), 83 deletions(-) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 6a0875b734a96..7257521181f2d 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -35,10 +35,10 @@ * complexity derives from the one introduced with EEVDF in [3]. * * [1] P. Valente and F. Checconi, ``High Throughput Disk Scheduling - * with Deterministic Guarantees on Bandwidth Distribution,'' to appear - * on IEEE Transactions on Computer. + * with Deterministic Guarantees on Bandwidth Distribution,'', + * IEEE Transactions on Computer, May 2010. * - * http://algo.ing.unimo.it/people/paolo/disk_sched/bfq.pdf + * http://algo.ing.unimo.it/people/paolo/disk_sched/bfq-techreport.pdf * * [2] Jon C.R. Bennett and H. Zhang, ``Hierarchical Packet Fair Queueing * Algorithms,'' IEEE/ACM Transactions on Networking, 5(5):675-689, @@ -353,6 +353,7 @@ static void bfq_add_rq_rb(struct request *rq) struct bfq_entity *entity = &bfqq->entity; struct bfq_data *bfqd = bfqq->bfqd; struct request *__alias, *next_rq; + unsigned long old_raising_coeff = bfqq->raising_coeff; bfq_log_bfqq(bfqd, bfqq, "add_rq_rb %d", rq_is_sync(rq)); bfqq->queued[rq_is_sync(rq)]++; @@ -376,26 +377,34 @@ static void bfq_add_rq_rb(struct request *rq) entity->budget = max_t(unsigned long, bfqq->max_budget, bfq_serv_to_charge(next_rq, bfqq)); + if (! bfqd->low_latency) + goto add_bfqq_busy; + /* * If the queue is not being boosted and has been idle - * for enough time, start a boosting period + * for enough time, start a weight-raising period */ - if (bfqd->low_latency && bfqq->high_weight_budget == 0) { - if (bfqq->last_activation_time + BFQ_MIN_ACT_INTERVAL < - jiffies_to_msecs(jiffies)) { - bfqq->high_weight_budget = BFQ_BOOST_BUDGET; - entity->ioprio_changed = 1; - bfq_log_bfqq(bfqd, bfqq, - "wboost starting at %lu msec", - bfqq->last_activation_time); - } - bfqq->last_activation_time = - jiffies_to_msecs(jiffies); + if(old_raising_coeff == 1 && + bfqq->last_rais_start_finish + + bfqd->bfq_raising_min_idle_time < jiffies) { + bfqq->raising_coeff = bfqd->bfq_raising_coeff; + entity->ioprio_changed = 1; + bfq_log_bfqq(bfqd, bfqq, + "wrais starting at %lu msec", + bfqq->last_rais_start_finish); } - +add_bfqq_busy: bfq_add_bfqq_busy(bfqd, bfqq); } else bfq_updated_next_req(bfqd, bfqq); + + if (! bfqd->low_latency) + return; + + if(old_raising_coeff == 1 || + (bfqd->bfq_raising_max_softrt_rate > 0 && + bfqq->soft_rt_next_start < jiffies)) + bfqq->last_rais_start_finish = jiffies; } static void bfq_reposition_rq_rb(struct bfq_queue *bfqq, struct request *rq) @@ -604,12 +613,15 @@ static void bfq_arm_slice_timer(struct bfq_data *bfqd) */ sl = bfqd->bfq_slice_idle; if (bfq_sample_valid(bfqq->seek_samples) && BFQQ_SEEKY(bfqq) && - bfqq->entity.service > bfq_max_budget(bfqd) / 8) + bfqq->entity.service > bfq_max_budget(bfqd) / 8 && + bfqq->raising_coeff == 1) sl = min(sl, msecs_to_jiffies(BFQ_MIN_TT)); - + else if (bfqq->raising_coeff > 1) + sl = sl * 4; bfqd->last_idling_start = ktime_get(); mod_timer(&bfqd->idle_slice_timer, jiffies + sl); - bfq_log(bfqd, "arm idle: %lu ms", sl); + bfq_log(bfqd, "arm idle: %lu/%lu ms", + jiffies_to_msecs(sl), jiffies_to_msecs(bfqd->bfq_slice_idle)); } /* @@ -625,7 +637,7 @@ static void bfq_set_budget_timeout(struct bfq_data *bfqd) bfq_clear_bfqq_budget_new(bfqq); bfqq->budget_timeout = jiffies + - bfqd->bfq_timeout[!!bfq_bfqq_sync(bfqq)] * + bfqd->bfq_timeout[bfq_bfqq_sync(bfqq)] * (bfqq->entity.weight / bfqq->entity.orig_weight); } @@ -996,6 +1008,18 @@ static void bfq_bfqq_expire(struct bfq_data *bfqd, bfq_bfqq_budget_left(bfqq) >= bfqq->entity.budget / 3)) bfq_bfqq_charge_full_budget(bfqq); + if (bfqd->low_latency && bfqq->raising_coeff == 1) + bfqq->last_rais_start_finish = jiffies; + + if (bfqd->low_latency && bfqd->bfq_raising_max_softrt_rate > 0) { + if(reason != BFQ_BFQQ_BUDGET_TIMEOUT) + bfqq->soft_rt_next_start = + jiffies + + HZ * bfqq->entity.service / + bfqd->bfq_raising_max_softrt_rate; + else + bfqq->soft_rt_next_start = -1; /* infinity */ + } bfq_log_bfqq(bfqd, bfqq, "expire (%d, slow %d, num_disp %d, idle_win %d)", reason, slow, bfqq->dispatched, bfq_bfqq_idle_window(bfqq)); @@ -1127,31 +1151,36 @@ static int __bfq_dispatch_requests(struct bfq_data *bfqd, bfq_bfqq_served(bfqq, service_to_charge); bfq_dispatch_insert(bfqd->queue, rq); - if (bfqq->high_weight_budget > 0) { /* queue is being boosted */ + if (bfqq->raising_coeff > 1) { /* queue is being boosted */ struct bfq_entity *entity = &bfqq->entity; - bfq_log_bfqq(bfqd, bfqq, "busy period dur %llu msec, " - "old highwbudg %lu", - jiffies_to_msecs(jiffies) - - bfqq->last_activation_time, - bfqq->high_weight_budget); + bfq_log_bfqq(bfqd, bfqq, + "raising period dur %llu/%lu msec, " + "old raising coeff %lu, w %lu(%lu)", + jiffies - bfqq->last_rais_start_finish, + bfqd->bfq_raising_max_time, + bfqq->raising_coeff, + bfqq->entity.weight, bfqq->entity.orig_weight); + + BUG_ON(entity->weight != + entity->orig_weight * bfqq->raising_coeff); + if(entity->ioprio_changed) + bfq_log_bfqq(bfqd, bfqq, + "WARN: pending prio change"); /* - * Decrease the budget for weight boosting by - * the just received service, or, if too much - * time has elapsed from the beginning of this - * boosting period, stop it + * If too much time has elapsed from the beginning + * of this weight-raising period, stop it */ - if (jiffies_to_msecs(jiffies) - - bfqq->last_activation_time <= BFQ_BOOST_TIMEOUT - && - bfqq->high_weight_budget > service_to_charge) - bfqq->high_weight_budget -= service_to_charge; - else - bfqq->high_weight_budget = 0; - entity->ioprio_changed = 1; - __bfq_entity_update_weight_prio( - bfq_entity_service_tree(entity), - entity); + if (jiffies - bfqq->last_rais_start_finish > + bfqd->bfq_raising_max_time) { + bfqq->raising_coeff = 1; + bfqq->last_rais_start_finish = jiffies; + + entity->ioprio_changed = 1; + __bfq_entity_update_weight_prio( + bfq_entity_service_tree(entity), + entity); + } } bfq_log_bfqq(bfqd, bfqq, "dispatched %lu sec req (%llu), " @@ -1456,8 +1485,9 @@ static struct bfq_queue *bfq_find_alloc_queue(struct bfq_data *bfqd, bfqq->max_budget = (2 * bfq_max_budget(bfqd)) / 3; bfqq->pid = current->pid; - bfqq->last_activation_time = 0; - bfqq->high_weight_budget = 0; + bfqq->raising_coeff = 1; + bfqq->last_rais_start_finish = 0; + bfqq->soft_rt_next_start = -1; bfq_log_bfqq(bfqd, bfqq, "allocated"); } @@ -1583,7 +1613,9 @@ static void bfq_update_idle_window(struct bfq_data *bfqd, enable_idle = bfq_bfqq_idle_window(bfqq); if (atomic_read(&cic->ioc->nr_tasks) == 0 || - bfqd->bfq_slice_idle == 0 || (bfqd->hw_tag && BFQQ_SEEKY(bfqq))) + bfqd->bfq_slice_idle == 0 || + (bfqd->hw_tag && BFQQ_SEEKY(bfqq) && + bfqq->raising_coeff == 1)) enable_idle = 0; else if (bfq_sample_valid(cic->ttime_samples)) { if (cic->ttime_mean > bfqd->bfq_slice_idle) @@ -1716,8 +1748,10 @@ static void bfq_completed_request(struct request_queue *q, struct request *rq) if (bfq_may_expire_for_budg_timeout(bfqq)) bfq_bfqq_expire(bfqd, bfqq, 0, BFQ_BFQQ_BUDGET_TIMEOUT); - else if (sync && bfqd->rq_in_driver == 0 && - RB_EMPTY_ROOT(&bfqq->sort_list)) + else if (sync && + (bfqd->rq_in_driver == 0 || + bfqq->raising_coeff > 1) + && RB_EMPTY_ROOT(&bfqq->sort_list)) bfq_arm_slice_timer(bfqd); } @@ -2064,12 +2098,18 @@ static void *bfq_init_queue(struct request_queue *q) bfqd->bfq_back_max = bfq_back_max; bfqd->bfq_back_penalty = bfq_back_penalty; bfqd->bfq_slice_idle = bfq_slice_idle; + bfqd->bfq_class_idle_last_service = 0; bfqd->bfq_max_budget_async_rq = bfq_max_budget_async_rq; bfqd->bfq_timeout[BLK_RW_ASYNC] = bfq_timeout_async; bfqd->bfq_timeout[BLK_RW_SYNC] = bfq_timeout_sync; bfqd->low_latency = true; + bfqd->bfq_raising_coeff = 20; + bfqd->bfq_raising_max_time = msecs_to_jiffies(8000); + bfqd->bfq_raising_min_idle_time = msecs_to_jiffies(2000); + bfqd->bfq_raising_max_softrt_rate = 7000; + return bfqd; } @@ -2116,6 +2156,27 @@ static ssize_t bfq_var_store(unsigned long *var, const char *page, size_t count) return count; } +static ssize_t bfq_weights_show(struct elevator_queue *e, char *page) +{ + struct bfq_queue *bfqq; + struct bfq_data *bfqd = e->elevator_data; + ssize_t num_char = 0; + + num_char += sprintf(page + num_char, "Active:\n"); + list_for_each_entry(bfqq, &bfqd->active_list, bfqq_list) { + num_char += sprintf(page + num_char, "pid%d: weight %hu\n", + bfqq->pid, + bfqq->entity.weight); + } + num_char += sprintf(page + num_char, "Idle:\n"); + list_for_each_entry(bfqq, &bfqd->idle_list, bfqq_list) { + num_char += sprintf(page + num_char, "pid%d: weight %hu\n", + bfqq->pid, + bfqq->entity.weight); + } + return num_char; +} + #define SHOW_FUNCTION(__FUNC, __VAR, __CONV) \ static ssize_t __FUNC(struct elevator_queue *e, char *page) \ { \ @@ -2136,6 +2197,12 @@ SHOW_FUNCTION(bfq_max_budget_async_rq_show, bfqd->bfq_max_budget_async_rq, 0); SHOW_FUNCTION(bfq_timeout_sync_show, bfqd->bfq_timeout[BLK_RW_SYNC], 1); SHOW_FUNCTION(bfq_timeout_async_show, bfqd->bfq_timeout[BLK_RW_ASYNC], 1); SHOW_FUNCTION(bfq_low_latency_show, bfqd->low_latency, 0); +SHOW_FUNCTION(bfq_raising_coeff_show, bfqd->bfq_raising_coeff, 0); +SHOW_FUNCTION(bfq_raising_max_time_show, bfqd->bfq_raising_max_time, 1); +SHOW_FUNCTION(bfq_raising_min_idle_time_show, bfqd->bfq_raising_min_idle_time, + 1); +SHOW_FUNCTION(bfq_raising_max_softrt_rate_show, + bfqd->bfq_raising_max_softrt_rate, 0); #undef SHOW_FUNCTION #define STORE_FUNCTION(__FUNC, __PTR, MIN, MAX, __CONV) \ @@ -2168,8 +2235,23 @@ STORE_FUNCTION(bfq_max_budget_async_rq_store, &bfqd->bfq_max_budget_async_rq, 1, INT_MAX, 0); STORE_FUNCTION(bfq_timeout_async_store, &bfqd->bfq_timeout[BLK_RW_ASYNC], 0, INT_MAX, 1); +STORE_FUNCTION(bfq_raising_coeff_store, &bfqd->bfq_raising_coeff, 1, + INT_MAX, 0); +STORE_FUNCTION(bfq_raising_max_time_store, &bfqd->bfq_raising_max_time, 0, + INT_MAX, 1); +STORE_FUNCTION(bfq_raising_min_idle_time_store, + &bfqd->bfq_raising_min_idle_time, 0, INT_MAX, 1); +STORE_FUNCTION(bfq_raising_max_softrt_rate_store, + &bfqd->bfq_raising_max_softrt_rate, 0, INT_MAX, 0); #undef STORE_FUNCTION +/* do nothing for the moment */ +static ssize_t bfq_weights_store(struct elevator_queue *e, + const char *page, size_t count) +{ + return count; +} + static inline unsigned long bfq_estimated_max_budget(struct bfq_data *bfqd) { u64 timeout = jiffies_to_msecs(bfqd->bfq_timeout[BLK_RW_SYNC]); @@ -2248,6 +2330,11 @@ static struct elv_fs_entry bfq_attrs[] = { BFQ_ATTR(timeout_sync), BFQ_ATTR(timeout_async), BFQ_ATTR(low_latency), + BFQ_ATTR(raising_coeff), + BFQ_ATTR(raising_max_time), + BFQ_ATTR(raising_min_idle_time), + BFQ_ATTR(raising_max_softrt_rate), + BFQ_ATTR(weights), __ATTR_NULL }; diff --git a/block/bfq-sched.c b/block/bfq-sched.c index 6ef14cc681e5e..64b23ef4caed6 100644 --- a/block/bfq-sched.c +++ b/block/bfq-sched.c @@ -16,7 +16,8 @@ for (; entity && ({ parent = entity->parent; 1; }); entity = parent) static struct bfq_entity *bfq_lookup_next_entity(struct bfq_sched_data *sd, - int extract); + int extract, + struct bfq_data *bfqd); static int bfq_update_next_active(struct bfq_sched_data *sd) { @@ -34,7 +35,7 @@ static int bfq_update_next_active(struct bfq_sched_data *sd) * next from this subtree. By now we worry more about * correctness than about performance... */ - next_active = bfq_lookup_next_entity(sd, 0); + next_active = bfq_lookup_next_entity(sd, 0, NULL); sd->next_active = next_active; if (next_active != NULL) { @@ -134,9 +135,8 @@ static inline void bfq_calc_finish(struct bfq_entity *entity, if (bfqq != NULL) { bfq_log_bfqq(bfqq->bfqd, bfqq, - "calc_finish: serv %lu, w %lu, hi-budg %lu", - service, entity->weight, - bfqq->high_weight_budget); + "calc_finish: serv %lu, w %lu", + service, entity->weight); bfq_log_bfqq(bfqq->bfqd, bfqq, "calc_finish: start %llu, finish %llu, delta %llu", entity->start, entity->finish, @@ -518,19 +518,8 @@ __bfq_entity_update_weight_prio(struct bfq_service_tree *old_st, struct bfq_service_tree *new_st = old_st; if (entity->ioprio_changed) { - int new_boost_coeff = 1; struct bfq_queue *bfqq = bfq_entity_to_bfqq(entity); - if (bfqq != NULL) { - new_boost_coeff += - bfqq->high_weight_budget * BFQ_BOOST_COEFF / - BFQ_BOOST_BUDGET; - bfq_log_bfqq(bfqq->bfqd, bfqq, - "update_w_prio: wght %lu, hi-budg %lu, coef %d", - entity->weight, bfqq->high_weight_budget, - new_boost_coeff); - } - BUG_ON(old_st->wsum < entity->weight); old_st->wsum -= entity->weight; @@ -557,7 +546,8 @@ __bfq_entity_update_weight_prio(struct bfq_service_tree *old_st, * when entity->finish <= old_st->vtime). */ new_st = bfq_entity_service_tree(entity); - entity->weight = entity->orig_weight * new_boost_coeff; + entity->weight = entity->orig_weight * + (bfqq != NULL ? bfqq->raising_coeff : 1); new_st->wsum += entity->weight; if (new_st != old_st) @@ -893,20 +883,30 @@ static struct bfq_entity *__bfq_lookup_next_entity(struct bfq_service_tree *st) * structures. */ static struct bfq_entity *bfq_lookup_next_entity(struct bfq_sched_data *sd, - int extract) + int extract, + struct bfq_data *bfqd) { struct bfq_service_tree *st = sd->service_tree; struct bfq_entity *entity; - int i; + int i=0; BUG_ON(sd->active_entity != NULL); - for (i = 0; i < BFQ_IOPRIO_CLASSES; i++, st++) { - entity = __bfq_lookup_next_entity(st); + if (bfqd != NULL && + jiffies - bfqd->bfq_class_idle_last_service > BFQ_CL_IDLE_TIMEOUT) { + entity = __bfq_lookup_next_entity(st + BFQ_IOPRIO_CLASSES - 1); + if (entity != NULL) { + i = BFQ_IOPRIO_CLASSES - 1; + bfqd->bfq_class_idle_last_service = jiffies; + sd->next_active = entity; + } + } + for (; i < BFQ_IOPRIO_CLASSES; i++) { + entity = __bfq_lookup_next_entity(st + i); if (entity != NULL) { if (extract) { bfq_check_next_active(sd, entity); - bfq_active_extract(st, entity); + bfq_active_extract(st + i, entity); sd->active_entity = entity; sd->next_active = NULL; } @@ -933,7 +933,7 @@ static struct bfq_queue *bfq_get_next_queue(struct bfq_data *bfqd) sd = &bfqd->root_group->sched_data; for (; sd != NULL; sd = entity->my_sched_data) { - entity = bfq_lookup_next_entity(sd, 1); + entity = bfq_lookup_next_entity(sd, 1, bfqd); BUG_ON(entity == NULL); entity->service = 0; } diff --git a/block/bfq.h b/block/bfq.h index 07977ad938ea5..8435821a666e1 100644 --- a/block/bfq.h +++ b/block/bfq.h @@ -1,5 +1,5 @@ /* - * BFQ-v1-r1 for 2.6.35: data structures and common functions prototypes. + * BFQ-v2 for 2.6.37: data structures and common functions prototypes. * * Based on ideas and code from CFQ: * Copyright (C) 2003 Jens Axboe @@ -17,6 +17,7 @@ #include #define BFQ_IOPRIO_CLASSES 3 +#define BFQ_CL_IDLE_TIMEOUT HZ/5 #define BFQ_MIN_WEIGHT 1 #define BFQ_MAX_WEIGHT 1000 @@ -25,16 +26,6 @@ #define BFQ_DEFAULT_GRP_IOPRIO 0 #define BFQ_DEFAULT_GRP_CLASS IOPRIO_CLASS_BE -/* Constants used in weight boosting (in its turn used to reduce latencies): */ -/* max factor by which the weight of a boosted queue is multiplied */ -#define BFQ_BOOST_COEFF 10 -/* max number of sectors that can be served during a boosting period */ -#define BFQ_BOOST_BUDGET 49152 -/* max duration of a boosting period, msec */ -#define BFQ_BOOST_TIMEOUT 6000 -/* min idle period after which boosting may be reactivated for a queue, msec */ -#define BFQ_MIN_ACT_INTERVAL 20000 - struct bfq_entity; /** @@ -213,6 +204,13 @@ struct bfq_group; * they are charged for the whole allocated budget, to try * to preserve a behavior reasonably fair among them, but * without service-domain guarantees). + * @bfq_raising_coeff: Maximum factor by which the weight of a boosted + * queue is multiplied + * @bfq_raising_max_time: maximum duration of a weight-raising period (jiffies) + * @bfq_raising_min_idle_time: minimum idle period after which weight-raising + * may be reactivated for a queue (in jiffies) + * @bfq_raising_max_softrt_rate: max service-rate for a soft real-time queue, + * sectors per seconds * * All the fields are protected by the @queue lock. */ @@ -257,12 +255,19 @@ struct bfq_data { unsigned int bfq_back_penalty; unsigned int bfq_back_max; unsigned int bfq_slice_idle; + u64 bfq_class_idle_last_service; unsigned int bfq_user_max_budget; unsigned int bfq_max_budget_async_rq; unsigned int bfq_timeout[2]; bool low_latency; + + /* parameters of the low_latency heuristics */ + unsigned int bfq_raising_coeff; + unsigned int bfq_raising_max_time; + unsigned int bfq_raising_min_idle_time; + unsigned int bfq_raising_max_softrt_rate; }; /** @@ -288,7 +293,7 @@ struct bfq_data { * @seek_mean: mean seek distance * @last_request_pos: position of the last request enqueued * @pid: pid of the process owning the queue, used for logging purposes. - * @last_activation_time: time of the last (idle -> backlogged) transition + * @last_rais_start_time: last (idle -> weight-raised) transition attempt * @high_weight_budget: number of sectors left to serve with boosted weight * * A bfq_queue is a leaf request queue; it can be associated to an io_context @@ -330,8 +335,9 @@ struct bfq_queue { pid_t pid; - u64 last_activation_time; - unsigned long high_weight_budget; + /* weight-raising fileds */ + u64 last_rais_start_finish, soft_rt_next_start; + unsigned int raising_coeff; }; enum bfqq_state_flags { From 384531df4e0691b6026f9404b695ba415a6ae370 Mon Sep 17 00:00:00 2001 From: Jan Steffens Date: Tue, 8 Mar 2011 02:46:17 +0100 Subject: [PATCH 1006/2556] Fix BFQ after __blk_run_queue was changed See commit 02d63a7d. --- block/bfq-iosched.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 7257521181f2d..7ec2b12db7a3d 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -1674,7 +1674,7 @@ static void bfq_rq_enqueued(struct bfq_data *bfqd, struct bfq_queue *bfqq, if (bfq_bfqq_budget_timeout(bfqq)) bfq_bfqq_expire(bfqd, bfqq, 0, BFQ_BFQQ_BUDGET_TIMEOUT); - __blk_run_queue(bfqd->queue); + __blk_run_queue(bfqd->queue, false); } } } @@ -1910,7 +1910,7 @@ static void bfq_kick_queue(struct work_struct *work) unsigned long flags; spin_lock_irqsave(q->queue_lock, flags); - __blk_run_queue(q); + __blk_run_queue(q, false); spin_unlock_irqrestore(q->queue_lock, flags); } From 23f357c0f78e0b92744889f3c1c20b200393dbf7 Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Tue, 29 Mar 2011 23:56:33 -0400 Subject: [PATCH 1007/2556] mahimahi: adjust in-call audio --- arch/arm/mach-msm/board-mahimahi-audio.c | 48 ++++++++++++------------ arch/arm/mach-msm/qdsp6/q6audio.c | 20 +++++----- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi-audio.c b/arch/arm/mach-msm/board-mahimahi-audio.c index 074d84e4d131d..88c9d475ee744 100644 --- a/arch/arm/mach-msm/board-mahimahi-audio.c +++ b/arch/arm/mach-msm/board-mahimahi-audio.c @@ -34,30 +34,30 @@ static struct mutex mic_lock; static struct mutex bt_sco_lock; static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { - [Q6_HW_HANDSET] = { - .min_gain = -2000, - .max_gain = 0, - }, - [Q6_HW_HEADSET] = { - .min_gain = -2000, - .max_gain = 0, - }, - [Q6_HW_SPEAKER] = { - .min_gain = -1500, - .max_gain = 0, - }, - [Q6_HW_TTY] = { - .min_gain = -2000, - .max_gain = 0, - }, - [Q6_HW_BT_SCO] = { - .min_gain = -2000, - .max_gain = 0, - }, - [Q6_HW_BT_A2DP] = { - .min_gain = -2000, - .max_gain = 0, - }, + [Q6_HW_HANDSET] = { + .min_gain = -1500, + .max_gain = 1100, + }, + [Q6_HW_HEADSET] = { + .min_gain = -1500, + .max_gain = 1100, + }, + [Q6_HW_SPEAKER] = { + .min_gain = -2000, + .max_gain = 800, + }, + [Q6_HW_TTY] = { + .min_gain = -1500, + .max_gain = 1100, + }, + [Q6_HW_BT_SCO] = { + .min_gain = -2000, + .max_gain = 800, + }, + [Q6_HW_BT_A2DP] = { + .min_gain = -2000, + .max_gain = 800, + }, }; void mahimahi_headset_enable(int en) diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c index 71b2ca9d1bab6..352c94011b7e5 100644 --- a/arch/arm/mach-msm/qdsp6/q6audio.c +++ b/arch/arm/mach-msm/qdsp6/q6audio.c @@ -46,28 +46,28 @@ static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { [Q6_HW_HANDSET] = { - .min_gain = -2000, - .max_gain = 0, + .min_gain = -1500, + .max_gain = 1100, }, [Q6_HW_HEADSET] = { - .min_gain = -2000, - .max_gain = 0, + .min_gain = -1500, + .max_gain = 1100, }, [Q6_HW_SPEAKER] = { - .min_gain = -1500, - .max_gain = 0, + .min_gain = -2000, + .max_gain = 800, }, [Q6_HW_TTY] = { - .min_gain = -2000, - .max_gain = 0, + .min_gain = -1500, + .max_gain = 1100, }, [Q6_HW_BT_SCO] = { .min_gain = -2000, - .max_gain = 0, + .max_gain = 800, }, [Q6_HW_BT_A2DP] = { .min_gain = -2000, - .max_gain = 0, + .max_gain = 800, }, }; From 9cca0f9699d8e03d79715c91fab7a89d443977d9 Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Wed, 30 Mar 2011 01:28:24 -0400 Subject: [PATCH 1008/2556] mahimahi: set 975 min_uV and 1275 max_uV --- arch/arm/mach-msm/board-mahimahi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 90b3b8367a53d..81d0f7051fc7d 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -425,8 +425,8 @@ static struct regulator_init_data tps65023_data[5] = { { .constraints = { .name = "dcdc1", /* VREG_MSMC2_1V29 */ - .min_uV = 1000000, - .max_uV = 1300000, + .min_uV = 975000, + .max_uV = 1275000, .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, }, .consumer_supplies = tps65023_dcdc1_supplies, From ca543f79df95f7ce1f878cc4943551eb36ce03d8 Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Wed, 30 Mar 2011 01:45:15 -0400 Subject: [PATCH 1009/2556] scorpion: cpufreq: adjust thresholds for ondemand and interactive --- drivers/cpufreq/cpufreq_interactive.c | 8 ++++++++ drivers/cpufreq/cpufreq_ondemand.c | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 81783286cad2f..111d96041789d 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -64,7 +64,11 @@ static unsigned long go_maxspeed_load; /* * The minimum amount of time to spend at a frequency before we can ramp down. */ +#if defined(CONFIG_ARCH_MSM_SCORPION) +#define DEFAULT_MIN_SAMPLE_TIME 75000; +#else #define DEFAULT_MIN_SAMPLE_TIME 80000; +#endif static unsigned long min_sample_time; #define DEBUG 0 @@ -163,7 +167,11 @@ static struct cpufreq_governor cpufreq_gov_interactive = { .name = "interactive", .governor = cpufreq_governor_interactive, +#if defined(CONFIG_ARCH_MSM_SCORPION) + .max_transition_latency = 8000000, +#else .max_transition_latency = 10000000, +#endif .owner = THIS_MODULE, }; diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index c631f27a3dcc7..5c8d6d1501555 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -28,6 +28,17 @@ * It helps to keep variable names smaller, simpler */ +#if defined(CONFIG_ARCH_MSM_SCORPION) +#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10) +#define DEF_FREQUENCY_UP_THRESHOLD (80) +#define DEF_SAMPLING_DOWN_FACTOR (1) +#define MAX_SAMPLING_DOWN_FACTOR (95000) +#define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3) +#define MICRO_FREQUENCY_UP_THRESHOLD (85) +#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (9500) +#define MIN_FREQUENCY_UP_THRESHOLD (11) +#define MAX_FREQUENCY_UP_THRESHOLD (100) +#else #define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10) #define DEF_FREQUENCY_UP_THRESHOLD (80) #define DEF_SAMPLING_DOWN_FACTOR (1) @@ -37,6 +48,7 @@ #define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000) #define MIN_FREQUENCY_UP_THRESHOLD (11) #define MAX_FREQUENCY_UP_THRESHOLD (100) +#endif /* * The polling frequency of this governor depends on the capability of From accc134a845c89ec3817373f55030003400096bf Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Wed, 30 Mar 2011 01:52:48 -0400 Subject: [PATCH 1010/2556] regulator: core: remove unset_supplies and add further logic from 2.6.34. -additions within consumer devices, to unset the device_supply/supplies and scrub. --- drivers/regulator/core.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 9fa20957847db..0cf7ce50bde61 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1002,6 +1002,29 @@ static int set_consumer_device_supply(struct regulator_dev *rdev, return 0; } +static void unset_consumer_device_supply(struct regulator_dev *rdev, + const char *consumer_dev_name, struct device *consumer_dev) +{ + struct regulator_map *node, *n; + + if (consumer_dev && !consumer_dev_name) + consumer_dev_name = dev_name(consumer_dev); + + list_for_each_entry_safe(node, n, ®ulator_map_list, list) { + if (rdev != node->regulator) + continue; + + if (consumer_dev_name && node->dev_name && + strcmp(consumer_dev_name, node->dev_name)) + continue; + + list_del(&node->list); + kfree(node->dev_name); + kfree(node); + return; + } +} + static void unset_regulator_supplies(struct regulator_dev *rdev) { struct regulator_map *node, *n; @@ -2565,9 +2588,14 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, init_data->consumer_supplies[i].dev, init_data->consumer_supplies[i].dev_name, init_data->consumer_supplies[i].supply); - if (ret < 0) - goto unset_supplies; + if (ret < 0) { + for (--i; i >= 0; i--) + unset_consumer_device_supply(rdev, + init_data->consumer_supplies[i].dev_name, + init_data->consumer_supplies[i].dev); + goto scrub; } +} list_add(&rdev->list, ®ulator_list); @@ -2576,9 +2604,6 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, mutex_unlock(®ulator_list_mutex); return rdev; -unset_supplies: - unset_regulator_supplies(rdev); - scrub: device_unregister(&rdev->dev); /* device core frees rdev */ From 4a327f091402e01c971a1ef61c546a3b8a5dd417 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Wed, 30 Mar 2011 04:23:56 -0400 Subject: [PATCH 1011/2556] msm: Add AXI frequency scaling This makes the screen on/off policy an option, replacing it with scaling. --- arch/arm/mach-msm/Kconfig | 8 +++ arch/arm/mach-msm/acpuclock-arm11.c | 21 ++++---- arch/arm/mach-msm/acpuclock-qsd8x50.c | 75 +++++++++++++++++---------- arch/arm/mach-msm/acpuclock.h | 11 +++- arch/arm/mach-msm/pm.c | 29 +++++++++-- 5 files changed, 101 insertions(+), 43 deletions(-) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index e2a22eca02400..7a972d85b2611 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -525,6 +525,14 @@ config MSM_CPU_FREQ_MIN endif # CPU_FREQ_MSM +config AXI_SCREEN_POLICY + depends on ARCH_QSD8X50 || ARCH_MSM7X00A || ARCH_MSM7227 || ARCH_MSM7225 + depends on HAS_EARLYSUSPEND + bool "Use higher AXI bus while screen ON" + default y + help + Simple AXI scaling based on screen ON/OFF and PWRC. + config MSM_HW3D tristate "MSM Hardware 3D Register Driver" depends on EARLYSUSPEND diff --git a/arch/arm/mach-msm/acpuclock-arm11.c b/arch/arm/mach-msm/acpuclock-arm11.c index 7ffbd987eb5d8..1e599a782088b 100644 --- a/arch/arm/mach-msm/acpuclock-arm11.c +++ b/arch/arm/mach-msm/acpuclock-arm11.c @@ -176,11 +176,12 @@ static int pc_pll_request(unsigned id, unsigned on) * ARM11 'owned' clock control *---------------------------------------------------------------------------*/ -unsigned long acpuclk_power_collapse(void) { +unsigned long acpuclk_power_collapse(int from_idle) { int ret = acpuclk_get_rate(); ret *= 1000; if (ret > drv_state.power_collapse_khz) - acpuclk_set_rate(drv_state.power_collapse_khz, 1); + acpuclk_set_rate(drv_state.power_collapse_khz, + (from_idle ? SETRATE_PC_IDLE : SETRATE_PC)); return ret; } @@ -193,7 +194,7 @@ unsigned long acpuclk_wait_for_irq(void) { int ret = acpuclk_get_rate(); ret *= 1000; if (ret > drv_state.wait_for_irq_khz) - acpuclk_set_rate(drv_state.wait_for_irq_khz, 1); + acpuclk_set_rate(drv_state.wait_for_irq_khz, SETRATE_SWFI); return ret; } @@ -290,7 +291,7 @@ static void acpuclk_set_div(const struct clkctl_acpu_speed *hunt_s) { } } -int acpuclk_set_rate(unsigned long rate, int for_power_collapse) +int acpuclk_set_rate(unsigned long rate, enum setrate_reason reason) { uint32_t reg_clkctl; struct clkctl_acpu_speed *cur_s, *tgt_s, *strt_s; @@ -315,7 +316,7 @@ int acpuclk_set_rate(unsigned long rate, int for_power_collapse) return -EINVAL; /* Choose the highest speed speed at or below 'rate' with same PLL. */ - if (for_power_collapse && tgt_s->a11clk_khz < cur_s->a11clk_khz) { + if (reason != SETRATE_CPUFREQ && tgt_s->a11clk_khz < cur_s->a11clk_khz) { while (tgt_s->pll != ACPU_PLL_TCXO && tgt_s->pll != cur_s->pll) tgt_s--; } @@ -323,7 +324,7 @@ int acpuclk_set_rate(unsigned long rate, int for_power_collapse) if (strt_s->pll != ACPU_PLL_TCXO) plls_enabled |= 1 << strt_s->pll; - if (!for_power_collapse) { + if (reason == SETRATE_CPUFREQ) { mutex_lock(&drv_state.lock); if (strt_s->pll != tgt_s->pll && tgt_s->pll != ACPU_PLL_TCXO) { rc = pc_pll_request(tgt_s->pll, 1); @@ -343,7 +344,7 @@ int acpuclk_set_rate(unsigned long rate, int for_power_collapse) } } - /* Set wait states for CPU inbetween frequency changes */ + /* Set wait states for CPU in/between frequency changes */ reg_clkctl = readl(A11S_CLK_CNTL_ADDR); reg_clkctl |= (100 << 16); /* set WT_ST_CNT */ writel(reg_clkctl, A11S_CLK_CNTL_ADDR); @@ -378,7 +379,7 @@ int acpuclk_set_rate(unsigned long rate, int for_power_collapse) printk(KERN_DEBUG "%s: STEP khz = %u, pll = %d\n", __FUNCTION__, cur_s->a11clk_khz, cur_s->pll); #endif - if (!for_power_collapse&& cur_s->pll != ACPU_PLL_TCXO + if (reason == SETRATE_CPUFREQ && cur_s->pll != ACPU_PLL_TCXO && !(plls_enabled & (1 << cur_s->pll))) { rc = pc_pll_request(cur_s->pll, 1); if (rc < 0) { @@ -397,7 +398,7 @@ int acpuclk_set_rate(unsigned long rate, int for_power_collapse) } /* Nothing else to do for power collapse. */ - if (for_power_collapse) + if (reason != SETRATE_CPUFREQ) return 0; /* Disable PLLs we are not using anymore. */ @@ -428,7 +429,7 @@ int acpuclk_set_rate(unsigned long rate, int for_power_collapse) printk(KERN_DEBUG "%s: ACPU speed change complete\n", __FUNCTION__); #endif out: - if (!for_power_collapse) + if (reason == SETRATE_CPUFREQ) mutex_unlock(&drv_state.lock); return rc; } diff --git a/arch/arm/mach-msm/acpuclock-qsd8x50.c b/arch/arm/mach-msm/acpuclock-qsd8x50.c index 691acdeaad74c..62f09b4cdcbf8 100644 --- a/arch/arm/mach-msm/acpuclock-qsd8x50.c +++ b/arch/arm/mach-msm/acpuclock-qsd8x50.c @@ -55,6 +55,7 @@ struct clkctl_acpu_speed { unsigned sc_l_value; unsigned lpj; int vdd; + unsigned axiclk_khz; }; /* clock sources */ @@ -71,27 +72,28 @@ struct clkctl_acpu_speed { #define SRC_PLL1 3 /* 768 MHz */ struct clkctl_acpu_speed acpu_freq_tbl[] = { - { 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 1050 }, - { 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 1050 }, - { 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 1050 }, - { 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 1050 }, - { 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 1050 }, - { 422400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0B, 0, 1050 }, - { 460800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0C, 0, 1050 }, - { 499200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0D, 0, 1075 }, - { 537600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0E, 0, 1100 }, - { 576000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0F, 0, 1100 }, - { 614400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x10, 0, 1125 }, - { 652800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x11, 0, 1150 }, - { 691200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x12, 0, 1175 }, - { 729600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x13, 0, 1200 }, - { 768000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x14, 0, 1200 }, - { 806400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x15, 0, 1225 }, - { 844800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x16, 0, 1250 }, - { 883200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x17, 0, 1275 }, - { 921600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x18, 0, 1275 }, - { 960000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x19, 0, 1275 }, - { 998400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1A, 0, 1275 }, + { 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 1050, 14000 }, + { 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 1050, 14000 }, + { 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 1050, 29000 }, + /* Work around for acpu resume hung, GPLL is turn off by arm9 */ + /*{ 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 1050, 29000 },*/ + { 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 1050, 58000 }, + { 422400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0B, 0, 1050, 117000 }, + { 460800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0C, 0, 1050, 117000 }, + { 499200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0D, 0, 1075, 117000 }, + { 537600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0E, 0, 1100, 117000 }, + { 576000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0F, 0, 1100, 117000 }, + { 614400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x10, 0, 1125, 117000 }, + { 652800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x11, 0, 1150, 117000 }, + { 691200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x12, 0, 1175, 117000 }, + { 729600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x13, 0, 1200, 117000 }, + { 768000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x14, 0, 1200, 128000 }, + { 806400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x15, 0, 1225, 128000 }, + { 844800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x16, 0, 1250, 128000 }, + { 883200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x17, 0, 1275, 128000 }, + { 921600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x18, 0, 1275, 128000 }, + { 960000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x19, 0, 1275, 128000 }, + { 998400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1A, 0, 1275, 128000 }, { 0 }, }; @@ -152,11 +154,16 @@ struct clock_state { uint32_t vdd_switch_time_us; unsigned long power_collapse_khz; unsigned long wait_for_irq_khz; + struct clk* clk_ebi1; struct regulator *regulator; }; static struct clock_state drv_state = { 0 }; +struct clk *clk_get(struct device *dev, const char *id); +unsigned long clk_get_rate(struct clk *clk); +int clk_set_rate(struct clk *clk, unsigned long rate); + static DEFINE_SPINLOCK(acpu_lock); #define PLLMODE_POWERDOWN 0 @@ -288,7 +295,7 @@ static int acpuclk_set_vdd_level(int vdd) return regulator_set_voltage(drv_state.regulator, vdd, vdd); } -int acpuclk_set_rate(unsigned long rate, int for_power_collapse) +int acpuclk_set_rate(unsigned long rate, enum setrate_reason reason) { struct clkctl_acpu_speed *cur, *next; unsigned long flags; @@ -298,7 +305,7 @@ int acpuclk_set_rate(unsigned long rate, int for_power_collapse) /* convert to KHz */ rate /= 1000; - DEBUG("acpuclk_set_rate(%d,%d)\n", (int) rate, for_power_collapse); + DEBUG("acpuclk_set_rate(%d,%d)\n", (int) rate, reason); if (rate == 0 || rate == cur->acpu_khz) return 0; @@ -312,7 +319,7 @@ int acpuclk_set_rate(unsigned long rate, int for_power_collapse) next++; } - if (!for_power_collapse) { + if (reason == SETRATE_CPUFREQ) { mutex_lock(&drv_state.lock); /* Increase VDD if needed. */ if (next->vdd > cur->vdd) { @@ -350,7 +357,16 @@ int acpuclk_set_rate(unsigned long rate, int for_power_collapse) drv_state.current_speed = next; spin_unlock_irqrestore(&acpu_lock, flags); - if (!for_power_collapse) { + +#ifndef CONFIG_AXI_SCREEN_POLICY + if (reason == SETRATE_CPUFREQ || reason == SETRATE_PC) { + if (cur->axiclk_khz != next->axiclk_khz) + clk_set_rate(drv_state.clk_ebi1, next->axiclk_khz * 1000); + DEBUG("acpuclk_set_rate switch axi to %d\n", + clk_get_rate(drv_state.clk_ebi1)); + } +#endif + if (reason == SETRATE_CPUFREQ) { /* Drop VDD level if we can. */ if (next->vdd < cur->vdd) { if (acpuclk_set_vdd_level(next->vdd)) @@ -454,11 +470,12 @@ uint32_t acpuclk_get_switch_time(void) return drv_state.acpu_switch_time_us; } -unsigned long acpuclk_power_collapse(void) +unsigned long acpuclk_power_collapse(int from_idle) { int ret = acpuclk_get_rate(); + enum setrate_reason reason = (from_idle) ? SETRATE_PC_IDLE : SETRATE_PC; if (ret > drv_state.power_collapse_khz) - acpuclk_set_rate(drv_state.power_collapse_khz * 1000, 1); + acpuclk_set_rate(drv_state.power_collapse_khz * 1000, reason); return ret * 1000; } @@ -491,4 +508,8 @@ void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *clkdata) acpuclk_init(); acpuclk_init_cpufreq_table(); + drv_state.clk_ebi1 = clk_get(NULL,"ebi1_clk"); +#ifndef CONFIG_AXI_SCREEN_POLICY + clk_set_rate(drv_state.clk_ebi1, drv_state.current_speed->axiclk_khz * 1000); +#endif } diff --git a/arch/arm/mach-msm/acpuclock.h b/arch/arm/mach-msm/acpuclock.h index 415de2eb9a5ee..921b6c5a08bc5 100644 --- a/arch/arm/mach-msm/acpuclock.h +++ b/arch/arm/mach-msm/acpuclock.h @@ -20,11 +20,18 @@ #ifndef __ARCH_ARM_MACH_MSM_ACPUCLOCK_H #define __ARCH_ARM_MACH_MSM_ACPUCLOCK_H -int acpuclk_set_rate(unsigned long rate, int for_power_collapse); +enum setrate_reason { + SETRATE_CPUFREQ = 0, + SETRATE_SWFI, + SETRATE_PC, + SETRATE_PC_IDLE, +}; + +int acpuclk_set_rate(unsigned long rate, enum setrate_reason reason); +unsigned long acpuclk_power_collapse(int from_idle); unsigned long acpuclk_get_rate(void); uint32_t acpuclk_get_switch_time(void); unsigned long acpuclk_wait_for_irq(void); -unsigned long acpuclk_power_collapse(void); unsigned long acpuclk_get_wfi_rate(void); diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index 6f2bfcdee7ab5..0997341981c2f 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -131,9 +131,11 @@ int msm_irq_idle_sleep_allowed(void); int msm_irq_pending(void); int clks_print_running(void); +#ifdef CONFIG_AXI_SCREEN_POLICY static int axi_rate; static int sleep_axi_rate; static struct clk *axi_clk; +#endif static uint32_t *msm_pm_reset_vector; static uint32_t msm_pm_max_sleep_time; @@ -374,16 +376,18 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) } if (sleep_mode <= MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT) { - pm_saved_acpu_clk_rate = acpuclk_power_collapse(); + pm_saved_acpu_clk_rate = acpuclk_power_collapse(from_idle); if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK) printk(KERN_INFO "msm_sleep(): %ld enter power collapse" "\n", pm_saved_acpu_clk_rate); if (pm_saved_acpu_clk_rate == 0) goto ramp_down_failed; +#ifdef CONFIG_AXI_SCREEN_POLICY /* Drop AXI request when the screen is on */ if (axi_rate) clk_set_rate(axi_clk, sleep_axi_rate); +#endif } if (sleep_mode < MSM_PM_SLEEP_MODE_APPS_SLEEP) { if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) @@ -420,13 +424,20 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK) printk(KERN_INFO "msm_sleep(): exit power collapse %ld" "\n", pm_saved_acpu_clk_rate); - if (acpuclk_set_rate(pm_saved_acpu_clk_rate, 1) < 0) +#if defined(CONFIG_ARCH_QSD8X50) + if (acpuclk_set_rate(pm_saved_acpu_clk_rate, 1) < 0) +#else + if (acpuclk_set_rate(pm_saved_acpu_clk_rate, + from_idle ? SETRATE_PC_IDLE : SETRATE_PC) < 0) +#endif printk(KERN_ERR "msm_sleep(): clk_set_rate %ld " "failed\n", pm_saved_acpu_clk_rate); +#ifdef CONFIG_AXI_SCREEN_POLICY /* Restore axi rate if needed */ if (axi_rate) clk_set_rate(axi_clk, axi_rate); +#endif } if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) printk(KERN_INFO "msm_sleep(): exit A11S_CLK_SLEEP_EN %x, " @@ -535,7 +546,12 @@ void arch_idle(void) if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK) printk(KERN_DEBUG "msm_sleep: clk swfi -> %ld\n", saved_rate); - if (acpuclk_set_rate(saved_rate, 1) < 0) +#if defined(CONFIG_ARCH_QSD8X50) + if (saved_rate && acpuclk_set_rate(saved_rate, 1) < 0) +#else + if (saved_rate + && acpuclk_set_rate(saved_rate, SETRATE_SWFI) < 0) +#endif printk(KERN_ERR "msm_sleep(): clk_set_rate %ld " "failed\n", saved_rate); #ifdef CONFIG_MSM_IDLE_STATS @@ -731,6 +747,7 @@ void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns) EXPORT_SYMBOL(msm_pm_set_max_sleep_time); #if defined(CONFIG_EARLYSUSPEND) && defined(CONFIG_ARCH_MSM_SCORPION) +#ifdef CONFIG_AXI_SCREEN_POLICY /* axi 128 screen on, 61mhz screen off */ static void axi_early_suspend(struct early_suspend *handler) { axi_rate = 0; @@ -748,7 +765,9 @@ static struct early_suspend axi_screen_suspend = { .resume = axi_late_resume, }; #endif +#endif +#ifdef CONFIG_AXI_SCREEN_POLICY static void __init msm_pm_axi_init(void) { #if defined(CONFIG_EARLYSUSPEND) && defined(CONFIG_ARCH_MSM_SCORPION) @@ -766,14 +785,16 @@ static void __init msm_pm_axi_init(void) axi_rate = 0; #endif } +#endif static int __init msm_pm_init(void) { pm_power_off = msm_pm_power_off; arm_pm_restart = msm_pm_restart; msm_pm_max_sleep_time = 0; +#ifdef CONFIG_AXI_SCREEN_POLICY msm_pm_axi_init(); - +#endif register_reboot_notifier(&msm_reboot_notifier); msm_pm_reset_vector = ioremap(RESET_VECTOR, PAGE_SIZE); From 1e9a83aaa95e3780f01945b89399104bced42982 Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Wed, 30 Mar 2011 05:33:38 -0400 Subject: [PATCH 1012/2556] mahimahi: Implement overclock, adjust voltages and misc changes. Overclock logic (coolbho3k) Voltages (pershoot) Misc changes from ARM11 (acpuclk_power_collapse, acpuclk_get_wfi_rate and acpuclk_wait_for_irq). Bootup clock to 806MHZ (pershoot) Overclock is to 1113. --- arch/arm/mach-msm/acpuclock-qsd8x50.c | 57 +++++++++++++++------------ 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/arch/arm/mach-msm/acpuclock-qsd8x50.c b/arch/arm/mach-msm/acpuclock-qsd8x50.c index 62f09b4cdcbf8..7100d7f5258c9 100644 --- a/arch/arm/mach-msm/acpuclock-qsd8x50.c +++ b/arch/arm/mach-msm/acpuclock-qsd8x50.c @@ -67,26 +67,26 @@ struct clkctl_acpu_speed { /* core sources */ #define SRC_RAW 0 /* clock from SPSS_CLK_CNTL */ -#define SRC_SCPLL 1 /* output of scpll 128-998 MHZ */ +#define SRC_SCPLL 1 /* output of scpll 128-1113 MHZ */ #define SRC_AXI 2 /* 128 MHz */ #define SRC_PLL1 3 /* 768 MHz */ struct clkctl_acpu_speed acpu_freq_tbl[] = { - { 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 1050, 14000 }, - { 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 1050, 14000 }, - { 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 1050, 29000 }, + { 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 975, 14000 }, + { 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 975, 14000 }, + { 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 1000, 29000 }, /* Work around for acpu resume hung, GPLL is turn off by arm9 */ - /*{ 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 1050, 29000 },*/ - { 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 1050, 58000 }, + /*{ 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 1000, 29000 },*/ + { 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 1025, 58000 }, { 422400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0B, 0, 1050, 117000 }, { 460800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0C, 0, 1050, 117000 }, { 499200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0D, 0, 1075, 117000 }, - { 537600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0E, 0, 1100, 117000 }, + { 537600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0E, 0, 1075, 117000 }, { 576000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0F, 0, 1100, 117000 }, - { 614400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x10, 0, 1125, 117000 }, - { 652800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x11, 0, 1150, 117000 }, - { 691200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x12, 0, 1175, 117000 }, - { 729600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x13, 0, 1200, 117000 }, + { 614400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x10, 0, 1100, 117000 }, + { 652800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x11, 0, 1125, 117000 }, + { 691200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x12, 0, 1150, 117000 }, + { 729600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x13, 0, 1175, 117000 }, { 768000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x14, 0, 1200, 128000 }, { 806400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x15, 0, 1225, 128000 }, { 844800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x16, 0, 1250, 128000 }, @@ -94,6 +94,9 @@ struct clkctl_acpu_speed acpu_freq_tbl[] = { { 921600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x18, 0, 1275, 128000 }, { 960000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x19, 0, 1275, 128000 }, { 998400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1A, 0, 1275, 128000 }, + { 1036800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1B, 0, 1275, 128000 }, + { 1075200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1C, 0, 1275, 128000 }, + { 1113600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1D, 0, 1275, 128000 }, { 0 }, }; @@ -119,9 +122,10 @@ static void __init acpuclk_init_cpufreq_table(void) freq_table[i].index = i; freq_table[i].frequency = CPUFREQ_ENTRY_INVALID; - /* Skip speeds using the global pll */ - if (acpu_freq_tbl[i].acpu_khz == 256000 || - acpu_freq_tbl[i].acpu_khz == 19200) + /* Define speeds that we want to skip */ + if (acpu_freq_tbl[i].acpu_khz == 19200 || + acpu_freq_tbl[i].acpu_khz == 128000 || + acpu_freq_tbl[i].acpu_khz == 256000) continue; vdd = acpu_freq_tbl[i].vdd; @@ -132,9 +136,8 @@ static void __init acpuclk_init_cpufreq_table(void) continue; } - /* Take the fastest speed available at the specified VDD level */ - if (vdd != acpu_freq_tbl[i + 1].vdd) - freq_table[i].frequency = acpu_freq_tbl[i].acpu_khz; + /* Add to the table */ + freq_table[i].frequency = acpu_freq_tbl[i].acpu_khz; } freq_table[i].index = i; @@ -324,7 +327,7 @@ int acpuclk_set_rate(unsigned long rate, enum setrate_reason reason) /* Increase VDD if needed. */ if (next->vdd > cur->vdd) { if (acpuclk_set_vdd_level(next->vdd)) { - pr_err("acpuclock: Unable to increase ACPU VDD.\n"); + pr_err("acpuclock: Unable to increase ACPU VDD from %d to %d setting rate to %d.\n", cur->vdd, next->vdd, (int) rate); mutex_unlock(&drv_state.lock); return -EINVAL; } @@ -370,7 +373,7 @@ int acpuclk_set_rate(unsigned long rate, enum setrate_reason reason) /* Drop VDD level if we can. */ if (next->vdd < cur->vdd) { if (acpuclk_set_vdd_level(next->vdd)) - pr_err("acpuclock: Unable to drop ACPU VDD.\n"); + pr_err("acpuclock: Unable to drop ACPU VDD from%d to %d setting rate to %d.\n", cur->vdd, next->vdd, (int) rate); } mutex_unlock(&drv_state.lock); } @@ -431,10 +434,10 @@ static void __init acpuclk_init(void) */ speed = acpu_freq_tbl; for (;;) { - if (speed->acpu_khz == 768000) + if (speed->acpu_khz == 806400) break; if (speed->acpu_khz == 0) { - pr_err("acpuclk_init: cannot find 768MHz\n"); + pr_err("acpuclk_init: cannot find 806MHz\n"); BUG(); } speed++; @@ -474,22 +477,24 @@ unsigned long acpuclk_power_collapse(int from_idle) { int ret = acpuclk_get_rate(); enum setrate_reason reason = (from_idle) ? SETRATE_PC_IDLE : SETRATE_PC; + ret *= 1000; if (ret > drv_state.power_collapse_khz) - acpuclk_set_rate(drv_state.power_collapse_khz * 1000, reason); - return ret * 1000; + acpuclk_set_rate(drv_state.power_collapse_khz, reason); + return ret; } unsigned long acpuclk_get_wfi_rate(void) { - return drv_state.wait_for_irq_khz * 1000; + return drv_state.wait_for_irq_khz; } unsigned long acpuclk_wait_for_irq(void) { int ret = acpuclk_get_rate(); + ret *= 1000; if (ret > drv_state.wait_for_irq_khz) - acpuclk_set_rate(drv_state.wait_for_irq_khz * 1000, 1); - return ret * 1000; + acpuclk_set_rate(drv_state.wait_for_irq_khz, 1); + return ret; } void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *clkdata) From e9453818394b18b8528a3ada3567c9a13e8a6ea7 Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Wed, 30 Mar 2011 09:14:53 -0400 Subject: [PATCH 1013/2556] regulator: tps65023: clear the client data in i2c (from 2.6.34) --- drivers/regulator/tps65023-regulator.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index 7698b603bba48..368c1dc08d077 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -590,6 +590,9 @@ static int __devexit tps_65023_remove(struct i2c_client *client) struct tps_pmic *tps = i2c_get_clientdata(client); int i; + /* clear the client data in i2c */ + i2c_set_clientdata(client, NULL); + for (i = 0; i < TPS65023_NUM_REGULATOR; i++) regulator_unregister(tps->rdev[i]); From afbee9fa8ac66de0af2f75236c57cc4019b3f576 Mon Sep 17 00:00:00 2001 From: Myshkinbob Date: Tue, 23 Nov 2010 05:01:34 -0500 Subject: [PATCH 1014/2556] net: wireless: bcm4329: switch to PM_FAST --- drivers/net/wireless/bcm4329/dhd_linux.c | 2 +- drivers/net/wireless/bcm4329/wl_iw.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index a4c9df52d9818..7b779d5ff9009 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -530,7 +530,7 @@ static void dhd_set_packet_filter(int value, dhd_pub_t *dhd) #if defined(CONFIG_HAS_EARLYSUSPEND) static int dhd_set_suspend(int value, dhd_pub_t *dhd) { - int power_mode = PM_MAX; + int power_mode = PM_FAST; /* wl_pkt_filter_enable_t enable_parm; */ char iovbuf[32]; int bcn_li_dtim = 3; diff --git a/drivers/net/wireless/bcm4329/wl_iw.c b/drivers/net/wireless/bcm4329/wl_iw.c index 230619d901ec1..cc860e3a03a1e 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.c +++ b/drivers/net/wireless/bcm4329/wl_iw.c @@ -4899,7 +4899,7 @@ wl_iw_set_power( WL_TRACE(("%s: SIOCSIWPOWER\n", dev->name)); - pm = vwrq->disabled ? PM_OFF : PM_MAX; + pm = vwrq->disabled ? PM_OFF : PM_FAST; pm = htod32(pm); if ((error = dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm)))) From 49be149790a956ac056aee328ab76e0612291ca9 Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Wed, 30 Mar 2011 19:58:08 -0400 Subject: [PATCH 1015/2556] defconfig: add pershoot_mahimahi_defconfig --- arch/arm/configs/pershoot_mahimahi_defconfig | 2097 ++++++++++++++++++ 1 file changed, 2097 insertions(+) create mode 100644 arch/arm/configs/pershoot_mahimahi_defconfig diff --git a/arch/arm/configs/pershoot_mahimahi_defconfig b/arch/arm/configs/pershoot_mahimahi_defconfig new file mode 100644 index 0000000000000..27852108fce1b --- /dev/null +++ b/arch/arm/configs/pershoot_mahimahi_defconfig @@ -0,0 +1,2097 @@ +# +# Automatically generated make config: don't edit +# Linux/arm 2.6.38.2 Kernel Configuration +# Wed Mar 30 05:42:44 2011 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="-cyanogenmod" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +CONFIG_KERNEL_LZMA=y +# CONFIG_KERNEL_LZO is not set +# CONFIG_SWAP is not set +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED is not set +CONFIG_HAVE_SPARSE_IRQ=y +# CONFIG_GENERIC_PENDING_IRQ is not set +# CONFIG_AUTO_IRQ_AFFINITY is not set +# CONFIG_IRQ_PER_CPU is not set +# CONFIG_SPARSE_IRQ is not set + +# +# RCU Subsystem +# +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +CONFIG_TINY_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_TRACE is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_RCU_BOOST=y +CONFIG_RCU_BOOST_PRIO=1 +CONFIG_RCU_BOOST_DELAY=500 +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_NS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +# CONFIG_CGROUP_MEM_RES_CTLR is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_CGROUP=y +# CONFIG_DEBUG_BLK_CGROUP is not set +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_USER_NS=y +CONFIG_PID_NS=y +# CONFIG_NET_NS is not set +CONFIG_SCHED_AUTOGROUP=y +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_ASHMEM=y +CONFIG_AIO=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +CONFIG_BLK_DEV_BSG=y +CONFIG_BLK_DEV_INTEGRITY=y +CONFIG_BLK_DEV_THROTTLING=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_IOSCHED_BFQ=y +CONFIG_CGROUP_BFQIO=y +CONFIG_DEFAULT_BFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="bfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +CONFIG_ARCH_MSM=y +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_S5PV310 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_TCC_926 is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_ARCH_MSM7X00A is not set +# CONFIG_ARCH_MSM7X30 is not set +CONFIG_ARCH_QSD8X50=y +# CONFIG_ARCH_MSM8X60 is not set +CONFIG_ARCH_MSM_SCORPION=y +CONFIG_HAS_MSM_DEBUG_UART_PHYS=y +CONFIG_MSM_MDP31=y + +# +# Qualcomm MSM Board Type +# +CONFIG_MACH_SWORDFISH=y +CONFIG_MACH_QSD8X50_SURF=y +# CONFIG_MACH_QSD8X50A_ST1_5 is not set +CONFIG_MSM_DEBUG_UART=1 +# CONFIG_MSM_DEBUG_UART_NONE is not set +CONFIG_MSM_DEBUG_UART1=y +# CONFIG_MSM_DEBUG_UART2 is not set +# CONFIG_MSM_DEBUG_UART3 is not set +CONFIG_MSM_PROC_COMM=y +CONFIG_MACH_MAHIMAHI=y +CONFIG_MACH_QSD8X50_FFA=y +# CONFIG_HTC_HEADSET is not set +CONFIG_HTC_35MM_JACK=y +CONFIG_HTC_PWRSPLY=y +# CONFIG_HTC_PWRSINK is not set +CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_MSM7X00A_USE_GP_TIMER=y +# CONFIG_MSM7X00A_USE_DG_TIMER is not set +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=1 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_FIQ_SUPPORT=y +CONFIG_MSM_SERIAL_DEBUGGER=y +CONFIG_MSM_SERIAL_DEBUGGER_NO_SLEEP=y +# CONFIG_MSM_SERIAL_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON is not set +# CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_DAL=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y +CONFIG_MSM_CPU_FREQ_MAX=998400 +CONFIG_MSM_CPU_FREQ_MIN=245000 +# CONFIG_AXI_SCREEN_POLICY is not set +CONFIG_MSM_HW3D=y +CONFIG_MSM_QDSP6=y +CONFIG_WIFI_CONTROL_FUNC=y +CONFIG_WIFI_MEM_PREALLOC=y +CONFIG_ARCH_MSM_FLASHLIGHT=y +# CONFIG_VIRTUAL_KPANIC_PARTITION is not set + +# +# System MMU +# + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_VERIFY_PERMISSION_FAULT=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_CPU_HAS_PMU=y +CONFIG_ARM_ERRATA_430973=y +CONFIG_ARM_ERRATA_458693=y +CONFIG_ARM_ERRATA_460075=y +CONFIG_ARM_ERRATA_743622=y +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_VMALLOC_RESERVE=0x08000000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_NEED_PER_CPU_KM=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +# CONFIG_CPU_FREQ_DEBUG is not set +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM_OPS=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +CONFIG_INET_ESP=y +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_LED=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +CONFIG_NETFILTER_XT_TARGET_TRACE=y +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +CONFIG_NETFILTER_XT_MATCH_OWNER=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +# CONFIG_NF_NAT_SNMP_BASIC is not set +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +CONFIG_NF_NAT_H323=y +CONFIG_NF_NAT_SIP=y +# CONFIG_IP_NF_MANGLE is not set +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV6 is not set +# CONFIG_NF_CONNTRACK_IPV6 is not set +# CONFIG_IP6_NF_QUEUE is not set +# CONFIG_IP6_NF_IPTABLES is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +CONFIG_NET_SCH_INGRESS=y + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +# CONFIG_GACT_PROB is not set +CONFIG_NET_ACT_MIRRED=y +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +CONFIG_DNS_RESOLVER=y +# CONFIG_BATMAN_ADV is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_PRIV=y +# CONFIG_CFG80211 is not set +CONFIG_WIRELESS_EXT_SYSFS=y +# CONFIG_LIB80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# + +# +# Some wireless drivers require a rate control algorithm +# +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_MSM_NAND=y +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_CRYPTOLOOP=y + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set +CONFIG_MISC_DEVICES=y +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_KERNEL_DEBUGGER_CORE=y +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +CONFIG_SENSORS_AKM8973=y +# CONFIG_SENSORS_AKM8976 is not set +CONFIG_VP_A1026=y +# CONFIG_DS1682 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_WL127X_RFKILL is not set +# CONFIG_APANIC is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_IFB=y +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=m +# CONFIG_VETH is not set +CONFIG_MII=y +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set +# CONFIG_ETHOC is not set +CONFIG_SMC911X=y +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8851_MLL is not set +CONFIG_NETDEV_1000=y +# CONFIG_STMMAC_ETH is not set +CONFIG_NETDEV_10000=y +CONFIG_WLAN=y +CONFIG_BCM4329=m +CONFIG_BCM4329_FW_PATH="/system/etc/firmware/fw_bcm4329.bin" +CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" +# CONFIG_HOSTAP is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set + +# +# CAIF transport drivers +# +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +# CONFIG_PPPOE is not set +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_GAN_ETH=y +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELAN_I2C_8232 is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_QT602240 is not set +# CONFIG_TOUCHSCREEN_MSM is not set +CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y +CONFIG_TOUCHSCREEN_DUPLICATED_FILTER=y +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set +CONFIG_INPUT_CAPELLA_CM3602=y + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_BCM_BT_LPM=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_MSM=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_SPI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_BASIC_MMIO is not set +# CONFIG_GPIO_IT8761E is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +CONFIG_W1=y + +# +# 1-wire Bus Masters +# +CONFIG_W1_MASTER_DS2482=y +# CONFIG_W1_MASTER_DS1WM is not set +# CONFIG_W1_MASTER_GPIO is not set + +# +# 1-wire Slaves +# +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_DS2423 is not set +# CONFIG_W1_SLAVE_DS2431 is not set +# CONFIG_W1_SLAVE_DS2433 is not set +# CONFIG_W1_SLAVE_DS2760 is not set +# CONFIG_W1_SLAVE_BQ27000 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2782 is not set +CONFIG_BATTERY_DS2784=y +# CONFIG_BATTERY_BQ20Z75 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_MFD_SUPPORT=y +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_WL1273_CORE is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +CONFIG_REGULATOR_TPS65023=y +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_AD5398 is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +CONFIG_720P_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set + +# +# Camera Sensor Selection +# +# CONFIG_MT9T013 is not set +# CONFIG_MT9D112 is not set +# CONFIG_MT9P012 is not set +CONFIG_S5K3E2FX=y +# CONFIG_S5K6AAFX is not set +# CONFIG_OV8810 is not set +# CONFIG_OV9665 is not set +# CONFIG_S5K3H1GX is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_LEGACY_MDP=y +CONFIG_FB_MSM_MDP_PPP=y +CONFIG_FB_MSM_LCDC=y +# CONFIG_FB_MSM_MDDI is not set +CONFIG_GPU_MSM_KGSL=y +CONFIG_MSM_KGSL_MMU=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set +# CONFIG_HID_PID is not set + +# +# Special HID drivers +# +# CONFIG_HID_APPLE is not set +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_WACOM is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +# CONFIG_USB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA_U2O is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_CI13XXX_MSM is not set +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_MSM_72K=y +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_ANDROID=y +# CONFIG_USB_ANDROID_ACM is not set +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_DIAG=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +# CONFIG_USB_ANDROID_MTP is not set +CONFIG_USB_ANDROID_RNDIS=y +CONFIG_USB_ANDROID_RNDIS_WCEIS=y +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG_72K is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_CLKGATE is not set +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_MSM=y +# CONFIG_MMC_DW is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +CONFIG_LEDS_CPLD=y +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +CONFIG_LEDS_TRIGGER_SLEEP=y + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_NFC_DEVICES is not set +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_MSM7X00A=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_ECHO is not set +# CONFIG_BRCM80211 is not set +# CONFIG_COMEDI is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE=128 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE=16 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE=8 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_POHMELFS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_IIO is not set +# CONFIG_ZRAM is not set +# CONFIG_FB_SM7XX is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_ST_BT is not set +# CONFIG_SMB_FS is not set +CONFIG_MACH_NO_WESTBRIDGE=y +# CONFIG_ATH6K_LEGACY is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +# CONFIG_DNOTIFY is not set +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=m +# CONFIG_CUSE is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_ECRYPT_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=m +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +# CONFIG_NFS_V4_1 is not set +# CONFIG_NFS_USE_LEGACY_DNS is not set +CONFIG_NFS_USE_KERNEL_DNS=y +# CONFIG_NFS_USE_NEW_IDMAPPER is not set +# CONFIG_NFSD is not set +CONFIG_LOCKD=m +CONFIG_LOCKD_V4=y +CONFIG_NFS_ACL_SUPPORT=m +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=m +CONFIG_SUNRPC_GSS=m +CONFIG_RPCSEC_GSS_KRB5=m +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=m +CONFIG_CIFS_STATS=y +CONFIG_CIFS_STATS2=y +CONFIG_CIFS_WEAK_PW_HASH=y +# CONFIG_CIFS_UPCALL is not set +CONFIG_CIFS_XATTR=y +# CONFIG_CIFS_POSIX is not set +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_DFS_UPCALL is not set +# CONFIG_CIFS_ACL is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +CONFIG_DEBUG_MUTEXES=y +CONFIG_BKL=y +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +CONFIG_DEBUG_SPINLOCK_SLEEP=y +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_ENABLE_DEFAULT_TRACERS is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_ARM_UNWIND is not set +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +CONFIG_KEYS=y +# CONFIG_KEYS_DEBUG_PROC_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y From 74a1f67a886cc9e282b24c7aa5f646a3511d96f6 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sun, 7 Nov 2010 23:23:56 -0500 Subject: [PATCH 1016/2556] msm: video: Add Silicon Image HDMI driver Change-Id: I2535712ba24986bf6347aed5e403d5218af614b9 --- arch/arm/mach-msm/include/mach/msm_fb.h | 125 +- arch/arm/mach-msm/include/mach/msm_hdmi.h | 25 + drivers/video/msm/Kconfig | 6 + drivers/video/msm/Makefile | 2 + drivers/video/msm/hdmi/Makefile | 9 + drivers/video/msm/hdmi/edid.c | 695 +++++++++++ drivers/video/msm/hdmi/fb-hdmi.c | 716 ++++++++++++ drivers/video/msm/hdmi/hdmi_lcdc.c | 634 ++++++++++ drivers/video/msm/hdmi/include/edid.h | 82 ++ drivers/video/msm/hdmi/include/fb-hdmi.h | 27 + drivers/video/msm/hdmi/include/sil902x.h | 400 +++++++ drivers/video/msm/hdmi/include/tpi.h | 411 +++++++ drivers/video/msm/hdmi/silicon-image/Makefile | 7 + .../video/msm/hdmi/silicon-image/av_config.c | 365 ++++++ .../msm/hdmi/silicon-image/debug-sil902x.c | 109 ++ drivers/video/msm/hdmi/silicon-image/hdcp.c | 354 ++++++ drivers/video/msm/hdmi/silicon-image/tpi.c | 960 ++++++++++++++++ drivers/video/msm/hdmi/transmitter.c | 1020 +++++++++++++++++ include/linux/htc_hdmi.h | 56 + 19 files changed, 5987 insertions(+), 16 deletions(-) create mode 100644 arch/arm/mach-msm/include/mach/msm_hdmi.h create mode 100644 drivers/video/msm/hdmi/Makefile create mode 100644 drivers/video/msm/hdmi/edid.c create mode 100644 drivers/video/msm/hdmi/fb-hdmi.c create mode 100644 drivers/video/msm/hdmi/hdmi_lcdc.c create mode 100644 drivers/video/msm/hdmi/include/edid.h create mode 100644 drivers/video/msm/hdmi/include/fb-hdmi.h create mode 100644 drivers/video/msm/hdmi/include/sil902x.h create mode 100644 drivers/video/msm/hdmi/include/tpi.h create mode 100644 drivers/video/msm/hdmi/silicon-image/Makefile create mode 100644 drivers/video/msm/hdmi/silicon-image/av_config.c create mode 100644 drivers/video/msm/hdmi/silicon-image/debug-sil902x.c create mode 100644 drivers/video/msm/hdmi/silicon-image/hdcp.c create mode 100644 drivers/video/msm/hdmi/silicon-image/tpi.c create mode 100644 drivers/video/msm/hdmi/transmitter.c create mode 100644 include/linux/htc_hdmi.h diff --git a/arch/arm/mach-msm/include/mach/msm_fb.h b/arch/arm/mach-msm/include/mach/msm_fb.h index 052ba22289a29..ac18c5e64e90e 100644 --- a/arch/arm/mach-msm/include/mach/msm_fb.h +++ b/arch/arm/mach-msm/include/mach/msm_fb.h @@ -26,6 +26,19 @@ struct mddi_info; #define MSM_MDP_OUT_IF_FMT_RGB666 1 #define MSM_MDP_OUT_IF_FMT_RGB888 2 +/* mdp override operations */ +#define MSM_MDP_PANEL_IGNORE_PIXEL_DATA (1 << 0) +#define MSM_MDP_PANEL_FLIP_UD (1 << 1) +#define MSM_MDP_PANEL_FLIP_LR (1 << 2) +#define MSM_MDP4_MDDI_DMA_SWITCH (1 << 3) + +/* mddi type */ +#define MSM_MDP_MDDI_TYPE_I 0 +#define MSM_MDP_MDDI_TYPE_II 1 + +/* lcdc override operations */ +#define MSM_MDP_LCDC_DMA_PACK_ALIGN_LSB (1 << 0) + struct msm_fb_data { int xres; /* x resolution in pixels */ int yres; /* y resolution in pixels */ @@ -48,6 +61,23 @@ enum { }; #define MSMFB_CAP_PARTIAL_UPDATES (1 << 0) +#define MSMFB_CAP_CABC (1 << 1) + +struct msm_lcdc_timing { + unsigned int clk_rate; /* dclk freq */ + unsigned int hsync_pulse_width; /* in dclks */ + unsigned int hsync_back_porch; /* in dclks */ + unsigned int hsync_front_porch; /* in dclks */ + unsigned int hsync_skew; /* in dclks */ + unsigned int vsync_pulse_width; /* in lines */ + unsigned int vsync_back_porch; /* in lines */ + unsigned int vsync_front_porch; /* in lines */ + + /* control signal polarity */ + unsigned int vsync_act_low:1; + unsigned int hsync_act_low:1; + unsigned int den_act_low:1; +}; struct msm_panel_data { /* turns off the fb memory */ @@ -58,9 +88,15 @@ struct msm_panel_data { int (*blank)(struct msm_panel_data *); /* turns on the panel */ int (*unblank)(struct msm_panel_data *); + /* for msmfb shutdown() */ + int (*shutdown)(struct msm_panel_data *); void (*wait_vsync)(struct msm_panel_data *); void (*request_vsync)(struct msm_panel_data *, struct msmfb_callback *); void (*clear_vsync)(struct msm_panel_data *); + void (*dump_vsync)(void); + /* change timing on the fly */ + int (*adjust_timing)(struct msm_panel_data *, struct msm_lcdc_timing *, + u32 xres, u32 yres); /* from the enum above */ unsigned interface_type; /* data to be passed to the fb driver */ @@ -68,6 +104,34 @@ struct msm_panel_data { /* capabilities supported by the panel */ uint32_t caps; + /* + * For samsung driver IC, we always need to indicate where + * to draw. So we pass update_into to mddi client. + * + */ + struct { + int left; + int top; + int eright; /* exclusive */ + int ebottom; /* exclusive */ + } update_info; + +}; + +enum { + MDP_DMA_P = 0, + MDP_DMA_S, +}; + +struct msm_mdp_platform_data { + /* from the enum above */ + int dma_channel; + unsigned overrides; + unsigned color_format; + int tearing_check; + unsigned sync_config; + unsigned sync_thresh; + unsigned sync_start_pos; }; struct msm_mddi_client_data { @@ -76,6 +140,8 @@ struct msm_mddi_client_data { void (*activate_link)(struct msm_mddi_client_data *); void (*remote_write)(struct msm_mddi_client_data *, uint32_t val, uint32_t reg); + void (*remote_write_vals)(struct msm_mddi_client_data *, uint8_t * val, + uint32_t reg, unsigned int nr_bytes); uint32_t (*remote_read)(struct msm_mddi_client_data *, uint32_t reg); void (*auto_hibernate)(struct msm_mddi_client_data *, int); /* custom data that needs to be passed from the board file to a @@ -98,6 +164,7 @@ struct msm_mddi_platform_data { struct resource *fb_resource; /*optional*/ /* number of clients in the list that follows */ int num_clients; + unsigned type; /* array of client information of clients */ struct { unsigned product_id; /* mfr id in top 16 bits, product id @@ -120,22 +187,6 @@ struct msm_mddi_platform_data { } client_platform_data[]; }; -struct msm_lcdc_timing { - unsigned int clk_rate; /* dclk freq */ - unsigned int hsync_pulse_width; /* in dclks */ - unsigned int hsync_back_porch; /* in dclks */ - unsigned int hsync_front_porch; /* in dclks */ - unsigned int hsync_skew; /* in dclks */ - unsigned int vsync_pulse_width; /* in lines */ - unsigned int vsync_back_porch; /* in lines */ - unsigned int vsync_front_porch; /* in lines */ - - /* control signal polarity */ - unsigned int vsync_act_low:1; - unsigned int hsync_act_low:1; - unsigned int den_act_low:1; -}; - struct msm_lcdc_panel_ops { int (*init)(struct msm_lcdc_panel_ops *); int (*uninit)(struct msm_lcdc_panel_ops *); @@ -149,8 +200,17 @@ struct msm_lcdc_platform_data { int fb_id; struct msm_fb_data *fb_data; struct resource *fb_resource; + unsigned overrides; }; +struct msm_tvenc_platform_data { + struct msm_tvenc_panel_ops *panel_ops; + int fb_id; + struct msm_fb_data *fb_data; + struct resource *fb_resource; + int (*video_relay)(int on_off); + }; + struct mdp_blit_req; struct fb_info; struct mdp_device { @@ -162,8 +222,11 @@ struct mdp_device { int (*blit)(struct mdp_device *mdp, struct fb_info *fb, struct mdp_blit_req *req); void (*set_grp_disp)(struct mdp_device *mdp, uint32_t disp_id); + void (*configure_dma)(struct mdp_device *mdp); int (*check_output_format)(struct mdp_device *mdp, int bpp); int (*set_output_format)(struct mdp_device *mdp, int bpp); + unsigned overrides; + unsigned color_format; }; struct class_interface; @@ -171,6 +234,20 @@ int register_mdp_client(struct class_interface *class_intf); /**** private client data structs go below this line ***/ +struct panel_data { + int panel_id; + u32 caps; + int shrink; + /* backlight data */ + u8 *pwm; + int min_level; + /* default_br used in turn on backlight, must sync with setting in user space */ + int default_br; + int (*shrink_br)(int brightness); + int (*change_cabcmode)(struct msm_mddi_client_data *client_data, + int mode, u8 dimming); +}; + struct msm_mddi_bridge_platform_data { /* from board file */ int (*init)(struct msm_mddi_bridge_platform_data *, @@ -182,10 +259,26 @@ struct msm_mddi_bridge_platform_data { struct msm_mddi_client_data *); int (*unblank)(struct msm_mddi_bridge_platform_data *, struct msm_mddi_client_data *); + int (*shutdown)(struct msm_mddi_bridge_platform_data *, + struct msm_mddi_client_data *); struct msm_fb_data fb_data; + struct panel_data panel_conf; + /* for those MDDI client which need to re-position display region + after each update or static electricity strike. It should be + implemented in board-xxx-panel due to the function itself need to + send the screen dimensional info of screen to MDDI client. + */ + void (*adjust)(struct msm_mddi_client_data *); +#define SAMSUNG_D 0 +#define SAMSUNG_S6 1 + int bridge_type; + int panel_type; + /* board file will identify what capabilities the panel supports */ uint32_t panel_caps; + /* backlight data */ + u8 *pwm; }; diff --git a/arch/arm/mach-msm/include/mach/msm_hdmi.h b/arch/arm/mach-msm/include/mach/msm_hdmi.h new file mode 100644 index 0000000000000..74b45a1b57c61 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_hdmi.h @@ -0,0 +1,25 @@ +/* arch/arm/mach-msm/include/mach/msm_hdmi.h + * Copyright (C) 2009 HTC Corporation. + * + * 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 _MSM_HDMI_H_ +#define _MSM_HDMI_H_ + +struct hdmi_platform_data { + struct resource hdmi_res; + /* power on hdmi chip */ + int (*power)(int on); /* mandatory */ + void (*hdmi_gpio_on)(void); /* optional */ + void (*hdmi_gpio_off)(void); /* optional */ +}; +#endif diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig index fefc8a9b5c38a..c505af0e71d5e 100644 --- a/drivers/video/msm/Kconfig +++ b/drivers/video/msm/Kconfig @@ -44,3 +44,9 @@ config MSM_KGSL_MMU help If enabled, the GPU driver will allocate memory from vmalloc and enable the use of GPU MMU, instead of using pmem. + +config MSM_HDMI + bool "Support for HDMI in QCT platform" + depends on MSM_MDP31 + default n + diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile index 3fcfbd88b0480..1b7803a7bbf07 100644 --- a/drivers/video/msm/Makefile +++ b/drivers/video/msm/Makefile @@ -32,3 +32,5 @@ obj-$(CONFIG_FB_MSM_LCDC) += mdp_lcdc.o # Yamato GL driver obj-$(CONFIG_GPU_MSM_KGSL) += gpu/kgsl/ + +obj-$(CONFIG_MSM_HDMI) += hdmi/ diff --git a/drivers/video/msm/hdmi/Makefile b/drivers/video/msm/hdmi/Makefile new file mode 100644 index 0000000000000..a6734d5dbb19f --- /dev/null +++ b/drivers/video/msm/hdmi/Makefile @@ -0,0 +1,9 @@ +msm_hdmi-objs = \ + transmitter.o \ + hdmi_lcdc.o \ + fb-hdmi.o \ + edid.o + +obj-$(CONFIG_MSM_HDMI) += msm_hdmi.o + +obj-$(CONFIG_MSM_HDMI) += silicon-image/ diff --git a/drivers/video/msm/hdmi/edid.c b/drivers/video/msm/hdmi/edid.c new file mode 100644 index 0000000000000..101afa1d95eaa --- /dev/null +++ b/drivers/video/msm/hdmi/edid.c @@ -0,0 +1,695 @@ +/* + * Copyright (C) 2009 HTC + * + * 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. + + * Common function for accessing/debugging EDID data. + * Reference: + http://en.wikipedia.org/wiki/Extended_display_identification_data +*/ + +#include +#include +#include + +#include "include/fb-hdmi.h" +#include "include/sil902x.h" + +#if 1 +#define EDID_DBG(s...) printk("[hdmi/edid]" s) +#else +#define EDID_DBG(s...) do {} while (0) +#endif + +static struct video_mode established_timing_db[] = { + {800, 600, 60, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 60 Hz"}, + {800, 600, 56, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 56 Hz"}, + {640, 480, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 75 Hz"}, + {640, 480, 72, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 72 Hz"}, + {640, 480, 67, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 67 Hz"}, + {640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 60 Hz"}, + {720, 400, 88, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 88 Hz"}, + {720, 400, 70, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 70 Hz"}, + + {1280, 1024, 75, ASPECT(4, 3), PROGRESSIVE, false, "1280x1024@75 Hz"}, + {1024, 768, 75, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@75 Hz"}, + {1024, 768, 70, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@70 Hz"}, + {1024, 768, 60, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@60 Hz"}, + {1024, 768, 87, ASPECT(4, 3), INTERLACE, false, + "1024x768@87 Hz (Interlaced)"}, + {832, 624, 75, ASPECT(4, 3), PROGRESSIVE, false, "832x624@75 Hz"}, + {800, 600, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600@75 Hz"}, + {800, 600, 72, ASPECT(4, 3), PROGRESSIVE, false, "800x600@72 Hz"}, + + {1152, 870, 75, ASPECT(4, 3), PROGRESSIVE, false, "1152x870 @ 75 Hz"}, +}; + +static struct video_mode standard_timing_db[8]; + +static struct video_mode additional_timing_db[] = { + {640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, + " 1 DMT0659 4:3 640x480p @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, + " 2 480p 4:3 720x480p @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, + " 3 480pH 16:9 720x480p @ 59.94/60Hz"}, + {1280, 720, 60, ASPECT(16, 9), PROGRESSIVE, false, + " 4 720p 16:9 1280x720p @ 59.94/60Hz"}, + {1920, 1080, 60, ASPECT(4, 3), INTERLACE, false, + " 5 1080i 16:9 1920x1080i @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(4, 3), INTERLACE, false, + " 6 480i 4:3 720(1440)x480i @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(16, 9), INTERLACE, false, + " 7 480iH 16:9 720(1440)x480i @ 59.94/60Hz"}, + {720, 240, 60, ASPECT(4, 3), PROGRESSIVE, false, + " 8 240p 4:3 720(1440)x240p @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, + " 9 240pH 16:9 720(1440)x240p @ 59.94/60Hz"}, + {2880, 480, 60, ASPECT(4, 3), INTERLACE, false, + "10 480i4x 4:3 (2880)x480i @ 59.94/60Hz"}, + {2880, 480, 60, ASPECT(16, 9), INTERLACE, false, + "11 480i4xH 16:9 (2880)x480i @ 59.94/60Hz"}, + {2880, 240, 60, ASPECT(4, 3), PROGRESSIVE, false, + "12 240p4x 4:3 (2880)x240p @ 59.94/60Hz"}, + {2880, 240, 60, ASPECT(16, 9), PROGRESSIVE, false, + "13 240p4xH 16:9 (2880)x240p @ 59.94/60Hz"}, + {1440, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, + "14 480p2x 4:3 1440x480p @ 59.94/60Hz"}, + {1440, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, + "15 480p2xH 16:9 1440x480p @ 59.94/60Hz"}, + {1920, 1080, 60, ASPECT(16, 9), PROGRESSIVE, false, + "16 1080p 16:9 1920x1080p @ 59.94/60Hz"}, + {720, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, + "17 576p 4:3 720x576p @ 50Hz"}, + {720, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, + "18 576pH 16:9 720x576p @ 50Hz"}, + {1280, 720, 50, ASPECT(16, 9), PROGRESSIVE, false, + "19 720p50 16:9 1280x720p @ 50Hz"}, + {1920, 1080, 50, ASPECT(16, 9), INTERLACE, false, + "20 1080i25 16:9 1920x1080i @ 50Hz*"}, + {1440, 576, 50, ASPECT(4, 3), INTERLACE, false, + "21 576i 4:3 720(1440)x576i @ 50Hz"}, + {1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, + "22 576iH 16:9 720(1440)x576i @ 50Hz"}, + {720, 288, 50, ASPECT(4, 3), PROGRESSIVE, false, + "23 288p 4:3 720(1440)x288p @ 50Hz"}, + {720, 288, 50, ASPECT(16, 9), PROGRESSIVE, false, + "24 288pH 16:9 720(1440)x288p @ 50Hz"}, + {2880, 576, 50, ASPECT(4, 3), INTERLACE, false, + "25 576i4x 4:3 (2880)x576i @ 50Hz"}, + {2880, 576, 50, ASPECT(16, 9), INTERLACE, false, + "26 576i4xH 16:9 (2880)x576i @ 50Hz"}, + {2880, 288, 50, ASPECT(4, 3), PROGRESSIVE, false, + "27 288p4x 4:3 (2880)x288p @ 50Hz"}, + {2880, 288, 50, ASPECT(16, 9), PROGRESSIVE, false, + "28 288p4xH 16:9 (2880)x288p @ 50Hz"}, + {1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, + "29 576p2x 4:3 1440x576p @ 50Hz"}, + {1440, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, + "30 576p2xH 16:9 1440x576p @ 50Hz"}, + {1920, 1080, 50, ASPECT(16, 9), PROGRESSIVE, false, + "31 1080p50 16:9 1920x1080p @ 50Hz"}, + {1920, 1080, 24, ASPECT(16, 9), PROGRESSIVE, false, + "32 1080p24 16:9 1920x1080p @ 23.98/24Hz"}, + {1920, 1080, 25, ASPECT(16, 9), PROGRESSIVE, false, + "33 1080p25 16:9 1920x1080p @ 25Hz"}, + {1920, 1080, 30, ASPECT(16, 9), PROGRESSIVE, false, + "34 1080p30 16:9 1920x1080p @ 29.97/30Hz"}, + {2880, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, + "35 480p4x 4:3 (2880)x480p @ 59.94/60Hz"}, + {2880, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, + "36 480p4xH 16:9 (2880)x480p @ 59.94/60Hz"}, + {2880, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, + "37 576p4x 4:3 (2880)x576p @ 50Hz"}, + {2880, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, + "38 576p4xH 16:9 (2880)x576p @ 50Hz"}, + {1920, 1080, 50, ASPECT(16, 9), INTERLACE, false, + "39 108Oi25 16:9 1920x1080i(1250 Total) @ 50Hz*"}, + {1920, 1080, 100, ASPECT(16, 9), INTERLACE, false, + "40 1080i50 16:9 1920x1080i @ 100Hz"}, + {1280, 720, 100, ASPECT(16, 9), PROGRESSIVE, false, + "41 720p100 16:9 1280x720p @ 100Hz"}, + {720, 576, 100, ASPECT(4, 3), PROGRESSIVE, false, + "42 576p100 4:3 720x576p @ 100Hz"}, + {720, 576, 100, ASPECT(16, 9), PROGRESSIVE, false, + "43 576p100H 16:9 720x576p @ 100Hz"}, + {720, 576, 100, ASPECT(4, 3), INTERLACE, false, + "44 576i50 4:3 720(1440)x576i @ 100Hz"}, + {720, 576, 100, ASPECT(16, 9), INTERLACE, false, + "45 576i50H 16:9 720(1440)x576i @ 100Hz"}, + {1920, 1080, 120, ASPECT(16, 9), INTERLACE, false, + "46 1080i60 16:9 1920x1080i @ 119.88/120Hz"}, + {1280, 720, 120, ASPECT(16, 9), PROGRESSIVE, false, + "47 720p120 16:9 1280x720p @ 119.88/120Hz"}, + {720, 480, 120, ASPECT(4, 3), PROGRESSIVE, false, + "48 480p119 4:3 720x480p @ 119.88/120Hz"}, + {720, 480, 120, ASPECT(16, 9), PROGRESSIVE, false, + "49 480p119H 16:9 720x480p @ 119.88/120Hz"}, + {720, 480, 120, ASPECT(4, 3), INTERLACE, false, + "50 480i59 4:3 720(1440)x480i @ 119.88/120Hz"}, + {720, 480, 120, ASPECT(16, 9), INTERLACE, false, + "51 480i59H 16:9 720(1440)x480i @ 119.88/120Hz"}, + {720, 576, 200, ASPECT(4, 3), PROGRESSIVE, false, + "52 576p200 4:3 720x576p @ 200Hz"}, + {720, 576, 200, ASPECT(16, 9), PROGRESSIVE, false, + "53 576p200H 16:9 720x576p @ 200Hz"}, + {720, 576, 200, ASPECT(4, 3), INTERLACE, false, + "54 576i100 4:3 720(1440)x576i @ 200Hz"}, + {720, 576, 200, ASPECT(16, 9), INTERLACE, false, + "55 576i100H 16:9 720(1440)x576i @ 200Hz"}, + {720, 480, 240, ASPECT(4, 3), PROGRESSIVE, false, + "56 480p239 4:3 720x480p @ 239.76/240Hz"}, + {720, 480, 240, ASPECT(16, 9), PROGRESSIVE, false, + "57 480p239H 16:9 720x480p @ 239.76/240Hz"}, + {720, 480, 240, ASPECT(4, 3), INTERLACE, false, + "58 480i119 4:3 720(1440)x480i @ 239.76/240Hz"}, + {720, 480, 240, ASPECT(16, 9), INTERLACE, false, + "59 480i119H 16:9 720(1440)x480i @ 239.76/240Hz"}, + {1280, 720, 24, ASPECT(16, 9), PROGRESSIVE, false, + "60 720p24 16:9 1280x720p @ 23.98/24Hz"}, + {1280, 720, 25, ASPECT(16, 9), PROGRESSIVE, false, + "61 720p25 16:9 1280x720p @ 25Hz"}, + {1280, 720, 30, ASPECT(16, 9), PROGRESSIVE, false, + "62 720p30 16:9 1280x720p @ 29.97/30Hz"}, + {1920, 1080, 120, ASPECT(16, 9), PROGRESSIVE, false, + "63 1080p120 16:9 1920x1080 @ 119.88/120Hz"}, +}; + +/* device supported modes in CEA */ +enum { + CEA_MODE_640X480P_60HZ_4_3 = 0, + CEA_MODE_720X480P_60HZ_4_3 = 1, + CEA_MODE_720X480P_60HZ_16_9 = 2, + CEA_MODE_1280X720P_60HZ_16_9 = 3, + CEA_MODE_720X576P_50HZ_4_3 = 16, + CEA_MODE_720X576P_50HZ_16_9 = 17, +}; + +/* device supported modes in established timing */ +enum { + ESTABLISHED_MODE_800X600_60HZ = 0, + ESTABLISHED_MODE_640X480_60HZ = 5, +}; + +int init_edid_info(struct edid_info_struct *edid_info) +{ + edid_info->is_valid = false; + mutex_init(&edid_info->access_lock); + + return 0; +} + +/* Byte 35-37 of block-0 */ +static char *established_timing_str[] = { + "800x600 @ 60 Hz", + "800x600 @ 56 Hz", + "640x480 @ 75 Hz", + "640x480 @ 72 Hz", + "640x480 @ 67 Hz", + "640x480 @ 60 Hz", + "720x400 @ 88 Hz", + "720x400 @ 70 Hz", + + "1280x1024@75 Hz", + "1024x768@75 Hz", + "1024x768@70 Hz", + "1024x768@60 Hz", + "1024x768@87 Hz (Interlaced)", + "832x624@75 Hz", + "800x600@75 Hz", + "800x600@72 Hz", + + "", + "", + "", + "", + "", + "", + "", + "1152x870 @ 75 Hz", +}; + +/* E-EDID Video data block: */ +static char *vdb_modes_str[] = { + " 1 DMT0659 4:3 640x480p @ 59.94/60Hz", + " 2 480p 4:3 720x480p @ 59.94/60Hz", + " 3 480pH 16:9 720x480p @ 59.94/60Hz", + " 4 720p 16:9 1280x720p @ 59.94/60Hz", + " 5 1080i 16:9 1920x1080i @ 59.94/60Hz", + " 6 480i 4:3 720(1440)x480i @ 59.94/60Hz", + " 7 480iH 16:9 720(1440)x480i @ 59.94/60Hz", + " 8 240p 4:3 720(1440)x240p @ 59.94/60Hz", + " 9 240pH 16:9 720(1440)x240p @ 59.94/60Hz", + "10 480i4x 4:3 (2880)x480i @ 59.94/60Hz", + "11 480i4xH 16:9 (2880)x480i @ 59.94/60Hz", + "12 240p4x 4:3 (2880)x240p @ 59.94/60Hz", + "13 240p4xH 16:9 (2880)x240p @ 59.94/60Hz", + "14 480p2x 4:3 1440x480p @ 59.94/60Hz", + "15 480p2xH 16:9 1440x480p @ 59.94/60Hz", + "16 1080p 16:9 1920x1080p @ 59.94/60Hz", + "17 576p 4:3 720x576p @ 50Hz", + "18 576pH 16:9 720x576p @ 50Hz", + "19 720p50 16:9 1280x720p @ 50Hz", + "20 1080i25 16:9 1920x1080i @ 50Hz*", + "21 576i 4:3 720(1440)x576i @ 50Hz", + "22 576iH 16:9 720(1440)x576i @ 50Hz", + "23 288p 4:3 720(1440)x288p @ 50Hz", + "24 288pH 16:9 720(1440)x288p @ 50Hz", + "25 576i4x 4:3 (2880)x576i @ 50Hz", + "26 576i4xH 16:9 (2880)x576i @ 50Hz", + "27 288p4x 4:3 (2880)x288p @ 50Hz", + "28 288p4xH 16:9 (2880)x288p @ 50Hz", + "29 576p2x 4:3 1440x576p @ 50Hz", + "30 576p2xH 16:9 1440x576p @ 50Hz", + "31 1080p50 16:9 1920x1080p @ 50Hz", + "32 1080p24 16:9 1920x1080p @ 23.98/24Hz", + "33 1080p25 16:9 1920x1080p @ 25Hz", + "34 1080p30 16:9 1920x1080p @ 29.97/30Hz", + "35 480p4x 4:3 (2880)x480p @ 59.94/60Hz", + "36 480p4xH 16:9 (2880)x480p @ 59.94/60Hz", + "37 576p4x 4:3 (2880)x576p @ 50Hz", + "38 576p4xH 16:9 (2880)x576p @ 50Hz", + "39 108Oi25 16:9 1920x1080i(1250 Total) @ 50Hz*", + "40 1080i50 16:9 1920x1080i @ 100Hz", + "41 720p100 16:9 1280x720p @ 100Hz", + "42 576p100 4:3 720x576p @ 100Hz", + "43 576p100H 16:9 720x576p @ 100Hz", + "44 576i50 4:3 720(1440)x576i @ 100Hz", + "45 576i50H 16:9 720(1440)x576i @ 100Hz", + "46 1080i60 16:9 1920x1080i @ 119.88/120Hz", + "47 720p120 16:9 1280x720p @ 119.88/120Hz", + "48 480p119 4:3 720x480p @ 119.88/120Hz", + "49 480p119H 16:9 720x480p @ 119.88/120Hz", + "50 480i59 4:3 720(1440)x480i @ 119.88/120Hz", + "51 480i59H 16:9 720(1440)x480i @ 119.88/120Hz", + "52 576p200 4:3 720x576p @ 200Hz", + "53 576p200H 16:9 720x576p @ 200Hz", + "54 576i100 4:3 720(1440)x576i @ 200Hz", + "55 576i100H 16:9 720(1440)x576i @ 200Hz", + "56 480p239 4:3 720x480p @ 239.76/240Hz", + "57 480p239H 16:9 720x480p @ 239.76/240Hz", + "58 480i119 4:3 720(1440)x480i @ 239.76/240Hz", + "59 480i119H 16:9 720(1440)x480i @ 239.76/240Hz", + "60 720p24 16:9 1280x720p @ 23.98/24Hz", + "61 720p25 16:9 1280x720p @ 25Hz", + "62 720p30 16:9 1280x720p @ 29.97/30Hz", + "63 1080p120 16:9 1920x1080 @ 119.88/120Hz", +}; + +int edid_dump_video_modes(u8 *edid_buf) +{ + int i, v1, v2, width, height, ret, aspect; + char *str_aspect[] = { "16:10", "4:3", "5:4", "16:9" }; + + switch (edid_buf[0]) { + case 0: + pr_info("block type 0: supported mode:\n"); + v1 = edid_buf[35] | edid_buf[36] << 8 | edid_buf[37] << 16; + /* Established timing */ + pr_info("established timing: {%02x, %02x, %02x}\n", + edid_buf[35], edid_buf[36], edid_buf[37]); + for (i = 0 ; i < 18; i++ ) { + v1 >>= 1; + if (v1 & 1) pr_info("%s\n", established_timing_str[i]); + }; + + pr_info("Standard timing identification:\n"); + /* Standard timing identification */ + for (i = 0; i < 8; i++) { + v1 = edid_buf[38+i*2]; + v2 = edid_buf[38+i*2+1]; + width = v1 * 8 + 248; + aspect = v2 >> 6; + switch (aspect) { + case 0: height = width * 10 / 16; break; + case 1: height = width * 3 / 4; break; + case 2: height = width * 4 / 5; break; + case 3: height = width * 9 / 16; break; + } + pr_info("%dx%d, %s, %d Hz\n", width, height, + str_aspect[aspect], (v2 & ~(3 << 6)) + 60); + } + ret = 0; + break; + case 2: + pr_info("block type 2: supported mode:\n"); + pr_info("edid_buf[4]=%x\n", edid_buf[4]); + for( i = 0; i < (edid_buf[4] & 0x1f); i++) { + pr_info("%s\n", vdb_modes_str[edid_buf[5+i] & 0x7f]); + } + ret = 0; + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} + +bool edid_do_checksum(u8 *data) +{ + int i; + u8 sum = 0; + + for (i = 0; i < EDID_BLOCK_SIZE; i++) + sum += data[i]; + EDID_DBG("%s: result=%s\n", __func__, sum ? "fail" : "pass"); + return sum ? false : true; +} + +static bool edid_check_header(u8 *data) +{ + int ret, i = 0; + /* EDID 8 bytes header */ + static u8 header[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}; + + for (i = 0; i < ARRAY_SIZE(header); i++) + if (data[i] != header[i]) + break; + ret = (i == ARRAY_SIZE(header)) ? true : false; + EDID_DBG("%s: result=%s\n", __func__, ret ? "pass" : "fail"); + + return ret; +} + +bool edid_check_hdmi_sink(struct hdmi_info *hdmi, int block) +{ + int ret = false, index = 4; + u8 *data = &hdmi->edid_buf[block * 128]; + /* block offset where long descriptors start */ + int long_desc_offset = data[LONG_DESCR_PTR_IDX]; + int tag_code, data_block_len; + + while (index < long_desc_offset) { + tag_code = (data[index] >> 5) & 0x7; + data_block_len = data[index++] & 0x1f; + if (tag_code == VENDOR_SPEC_D_BLOCK && + data[index] == 0x03 && + data[index + 1] == 0x0c && + data[index + 2] == 0x00) { + ret = true; + break; + } else + index += data_block_len; + } + hdmi->edid_info.hdmi_sink = ret; + EDID_DBG("%s: ret=%s\n", __func__, ret ? "yes" : "no"); + return ret; +} + +struct edid_black_list_info { + u8 mfr_model[4]; + u8 prefer_modes[3]; +}; + +struct edid_black_list_info edid_black_list[] = { + { {0x4c, 0x2d, 0xa5, 0x02}, {0, 0, 0x40} }, // 720p only + { {0x4c, 0x2d, 0x0d, 0x05}, {0, 0, 0x40} }, // 720p only + + //{ {0x5a, 0x63, 0x20, 0x2b}, {0, 0, 0x40} }, // Viewsonic test +}; + +/* By comparing the Manufacture(0x8, 0x9) and Model field(0xa, 0xb) of EDID, + * to check if the attached TV is a known less-compatibile one. + */ +int edid_fixup_compatibility_list(struct hdmi_info *hdmi) +{ + int i, ret = -1; + + /* FIXME: magic numbers...*/ + for (i = 0; i < ARRAY_SIZE(edid_black_list); i++) { + if (!memcmp(hdmi->edid_buf + 8, edid_black_list[i].mfr_model, 4)){ +#if 0 + EDID_DBG("%s: found in blacklist %d\n", __func__, i); + EDID_DBG("%s: old timing = {%02x, %02x, %02x}\n", + __func__, + hdmi->edid_buf[35], hdmi->edid_buf[36], + hdmi->edid_buf[37]); + memcpy(hdmi->edid_buf + 35, + edid_black_list[i].prefer_modes, 3); + EDID_DBG("%s: new timing = {%02x, %02x, %02x}\n", + __func__, + hdmi->edid_buf[35], hdmi->edid_buf[36], + hdmi->edid_buf[37]); +#else + EDID_DBG("%s: found in compatibility %d\n", __func__, i); + memcpy(hdmi->edid_buf + 35, + edid_black_list[i].prefer_modes, 3); +#endif + ret = i; + break; + } + } + + return ret; +} + +u8 edid_simple_parsing(struct hdmi_info *hdmi) +{ + u8 *edid_buf = hdmi->edid_buf; + int i, index, ret = -EINVAL; + struct edid_info_struct *edid_info = &hdmi->edid_info; + unsigned v1, width, height, aspect; + unsigned extensions; + + EDID_DBG("%s\n", __func__); + // FIXME: integrate with edid_check() + if (!edid_do_checksum(edid_buf)) { + pr_err("%s: checksum error\n", __func__); + //return EDID_CHECKSUM_ERROR; + } + if (!edid_check_header(edid_buf)) { + pr_err("%s: incorrect header\n", __func__); + return INCORRECT_EDID_HEADER; + } + edid_info->under_scan = ((edid_buf[MISC_SUPPORT_IDX]) >> 7) & 0x1; + edid_info->basic_audio = ((edid_buf[MISC_SUPPORT_IDX]) >> 6) & 0x1; + edid_info->ycbcr_4_4_4 = ((edid_buf[MISC_SUPPORT_IDX]) >> 5) & 0x1; + edid_info->ycbcr_4_2_2 = ((edid_buf[MISC_SUPPORT_IDX]) >> 4) & 0x1; + + // FIXME: 0x7e + extensions = edid_buf[0x7e]; + EDID_DBG("%s: extensions=%d\n", __func__, extensions); + if (!extensions) { + hdmi->edid_info.hdmi_sink = false; + return NO_861_EXTENSIONS; + } + //return; + + /* reset all supported */ + for (i = 0 ; i < ARRAY_SIZE(additional_timing_db); i++) + additional_timing_db[i].supported = false; + for (i = 0 ; i < ARRAY_SIZE(established_timing_db); i++) + established_timing_db[i].supported = false; + + /* Block 0: established timing */ + pr_info("established timing: {%02x, %02x, %02x}\n", + edid_buf[35], edid_buf[36], edid_buf[37]); + + v1 = edid_buf[35] | edid_buf[36] << 8 | (!!edid_buf[37]) << 16; + + for (i = 0 ; i < 17; i++ ) // 17 bits defined in established timing + established_timing_db[i].supported = ((v1 >>= 1) & 1) ; + +#if 0 + /* standard timing identification */ + for (i = 0; i < 8; i++) { + width = edid_buf[38 + i * 2] * 8 + 248; + v1 = edid_buf[38 + i * 2 + 1]; + switch (v1 >> 6) { + case 0: height = width * 10 / 16; aspect = ASPECT(16, 10); break; + case 1: height = width * 3 / 4; aspect = ASPECT(4, 3); break; + case 2: height = width * 4 / 5; aspect = ASPECT(5, 4); break; + case 3: height = width * 9 / 16; aspect = ASPECT(16, 9); break; + } + standard_timing_db[i].width = width; + standard_timing_db[i].height = height; + standard_timing_db[i].aspect = aspect; + standard_timing_db[i].refresh_rate = (v1 & ~(3 << 6)) + 60; + standard_timing_db[i].supported = true; + } +#endif + + if (extensions == 1) { + EDID_DBG("Block-1: additional timing\n"); + /* Block-1: additional timing */ + for( i = 0; i < (edid_buf[128 + 4] & 0x1f); i++) { + index = edid_buf[128 + 5 + i] & 0x7f; + additional_timing_db[index-1].supported = true; + EDID_DBG("%s\n", additional_timing_db[index-1].descrption); + } + edid_check_hdmi_sink(hdmi, 1); + } else { + EDID_DBG("Block-2: additional timing\n"); + for( i = 0; i < (edid_buf[384 + 4] & 0x1f); i++) { + index = edid_buf[384 + 5 + i] & 0x7f; + additional_timing_db[index-1].supported = true; + EDID_DBG("%s\n", additional_timing_db[index-1].descrption); + } + edid_check_hdmi_sink(hdmi, 3); + } + + edid_buf[35] = 0; + edid_buf[36] = 0; + edid_buf[37] = 0; + + /* edid_buf[37] bit4: 480p, bit5: 576p, bit6: 720p */ + if (additional_timing_db[CEA_MODE_720X480P_60HZ_4_3].supported || + additional_timing_db[CEA_MODE_720X480P_60HZ_16_9].supported) { + EDID_DBG("decide to support 480P\n"); + edid_buf[37] |= (1<<4); + } + + if (additional_timing_db[CEA_MODE_720X576P_50HZ_4_3].supported || + additional_timing_db[CEA_MODE_720X576P_50HZ_16_9].supported) { + EDID_DBG("decide to support 576P\n"); + edid_buf[37] |= (1<<5); + } + + if (additional_timing_db[CEA_MODE_1280X720P_60HZ_16_9].supported) { + EDID_DBG("decide to support 720P\n"); + edid_buf[37] |= (1<<6); + } + + if (established_timing_db[ESTABLISHED_MODE_800X600_60HZ].supported) { + EDID_DBG("decide to support 800x600\n"); + edid_buf[36] |= (1<<6); + } + + if (established_timing_db[ESTABLISHED_MODE_640X480_60HZ].supported) { + EDID_DBG("decide to support 640x480\n"); + edid_buf[35] |= (1<<5); + } + edid_fixup_compatibility_list(hdmi); + + return ret; +} + +// FIXME: modify the checking routines into inline function. +bool edid_is_video_mode_supported(struct video_mode *vmode) +{ + int i; + struct video_mode *vmode_db; + + vmode_db = established_timing_db; + for (i = 0, vmode_db = established_timing_db; + i < ARRAY_SIZE(established_timing_db); i++) + if ( (vmode->width == vmode_db[i].width) && + (vmode->height == vmode_db[i].height) && + (vmode->refresh_rate == vmode_db[i].refresh_rate ) && + (vmode_db[i].interlaced == PROGRESSIVE) && + (vmode_db[i].supported == true )) + return true; + for (i = 0, vmode_db = standard_timing_db; + i < ARRAY_SIZE(standard_timing_db); i++) + if ( (vmode->width == vmode_db[i].width) && + (vmode->height == vmode_db[i].height) && + (vmode->refresh_rate == vmode_db[i].refresh_rate ) && + (vmode_db[i].interlaced == PROGRESSIVE) && + (vmode_db[i].supported == true )) + return true; + for (i = 0, vmode_db = additional_timing_db; + i < ARRAY_SIZE(additional_timing_db); i++) + if ( (vmode->width == vmode_db[i].width) && + (vmode->height == vmode_db[i].height) && + (vmode->refresh_rate == vmode_db[i].refresh_rate ) && + (vmode_db[i].interlaced == PROGRESSIVE) && + (vmode_db[i].supported == true )) + return true; + return false; +} + +bool edid_check_sink_type(struct hdmi_info *hdmi) +{ + EDID_DBG("%s: ret=%d\n", __func__, hdmi->edid_info.hdmi_sink); + return hdmi->edid_info.hdmi_sink; +} + +int edid_dump_hex(u8 *src, int src_size, char *output, int output_size) +{ + char line[80]; + static char hextab[] = "0123456789abcdef"; + int i, j, n = 0, v, len, offset, line_size; + + len = src_size; + memset(line, ' ', 79); + line[79] = '\0'; + offset = strlen("0000 | "); + line_size = offset + 3 * 16 + 1; + for (i = 0; i < len / 16 ; i++) { + scnprintf(line, offset + 1, "%04x | ", (i << 4)); + for (j = 0; j < 16 ; j++) { + v = src[i * 16 + j]; + line[offset + j * 3] = hextab[v / 16]; + line[offset + j * 3 + 1] = hextab[v % 16]; + } + line[line_size - 1] = '\n'; + strncpy(output+ n, line, line_size); + if ((n + line_size) > output_size) + break; + else + n += line_size; + } + + return n; +} + +/*============================================================================*/ +static ssize_t edid_dbg_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static char hex_buff[2048]; +static ssize_t edid_buffered_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + int n; + int extensions; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + extensions = hdmi->edid_buf[0x7e] + 1; + edid_dump_video_modes(hdmi->edid_buf);// fixme: crashed + if (extensions == 1) + edid_dump_video_modes(hdmi->edid_buf + 128); + else + edid_dump_video_modes(hdmi->edid_buf + 384); + //edid_simple_parsing(hdmi); // FIXME: crashed... + n = edid_dump_hex(hdmi->edid_buf, (hdmi->edid_buf[0x7e] + 1) * 128, + hex_buff, 2048); + return simple_read_from_buffer(buf, count, ppos, hex_buff, n); +} + +static struct file_operations edid_debugfs_fops[] = { + { + .open = edid_dbg_open, + .read = edid_buffered_read, + .write = NULL, + } +}; + +// TODO: error handling +int edid_debugfs_init(struct hdmi_info *hdmi) +{ + //int ret; + struct dentry *edid_dent; + + edid_dent = debugfs_create_dir("edid", hdmi->debug_dir); + if (IS_ERR(edid_dent)) + return PTR_ERR(edid_dent); + + debugfs_create_file("hex_dump", 0444, edid_dent, hdmi, + &edid_debugfs_fops[0]); + + return 0; +} + diff --git a/drivers/video/msm/hdmi/fb-hdmi.c b/drivers/video/msm/hdmi/fb-hdmi.c new file mode 100644 index 0000000000000..7daf6d1abb9cc --- /dev/null +++ b/drivers/video/msm/hdmi/fb-hdmi.c @@ -0,0 +1,716 @@ +/* + * Copyright (C) 2009 HTC + * + * 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. + * + * Referenced from drivers/video/msm/msm_fb.c, Google Incorporated. + * + */ +#define DEBUG +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HTC_HEADSET_MGR +#include +#endif + +#include "include/fb-hdmi.h" +#include "include/sil902x.h" + +#if 1 +#define HDMI_DBG(s...) printk("[hdmi/fb]" s) +#else +#define HDMI_DBG(s...) do {} while (0) +#endif + +struct update_info_t { + int left; + int top; + int eright; /* exclusive */ + int ebottom; /* exclusive */ + unsigned yoffset; +}; + +struct hdmifb_info { + struct fb_info *fb; + struct msm_panel_data *panel; + struct notifier_block fb_hdmi_event; + struct msmfb_callback dma_callback; + struct msmfb_callback vsync_callback; + struct update_info_t update_info; + struct early_suspend earlier_suspend; + struct early_suspend early_suspend; + spinlock_t update_lock; + int xres; + int yres; + unsigned long state; + atomic_t use_count; +}; + +static struct mdp_device *mdp; +static struct hdmi_device *hdmi; + +static unsigned PP[16]; + +void hdmi_pre_change(struct hdmi_info *hdmi); +void hdmi_post_change(struct hdmi_info *info, struct fb_var_screeninfo *var); + +static int hdmifb_open(struct fb_info *info, int user) +{ + return 0; +} + +static int hdmifb_release(struct fb_info *info, int user) +{ + return 0; +} + +static int hdmifb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + u32 size; + + if (mdp->check_output_format(mdp, var->bits_per_pixel)) + return -EINVAL; + if (hdmi->check_res(hdmi, var)) + return -EINVAL; + + size = var->xres_virtual * var->yres_virtual * + (var->bits_per_pixel >> 3); + if (size > info->fix.smem_len) + return -EINVAL; + return 0; +} + +static int hdmifb_set_par(struct fb_info *info) +{ + struct fb_var_screeninfo *var = &info->var; + struct fb_fix_screeninfo *fix = &info->fix; + struct hdmifb_info *hdmi_fb = info->par; + struct msm_panel_data *panel = hdmi_fb->panel; + struct msm_lcdc_timing *timing; + struct hdmi_info *hinfo = container_of(hdmi, struct hdmi_info, + hdmi_dev); + + HDMI_DBG("%s\n", __func__); + /* we only support RGB ordering for now */ + if (var->bits_per_pixel == 32 || var->bits_per_pixel == 24) { + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 16; + var->blue.length = 8; + } else if (var->bits_per_pixel == 16) { + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + } else + return -1; + + HDMI_DBG("set res (%d, %d)\n", var->xres, var->yres); + timing = hdmi->set_res(hdmi, var); + panel->adjust_timing(panel, timing, var->xres, var->yres); + hdmi_post_change(hinfo, var); + + mdp->set_output_format(mdp, var->bits_per_pixel); + + hdmi_fb->xres = var->xres; + hdmi_fb->yres = var->yres; + fix->line_length = var->xres * var->bits_per_pixel / 8; + return 0; +} + +/* core update function */ +static void +hdmifb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, + uint32_t eright, uint32_t ebottom, uint32_t yoffset) +{ + struct hdmifb_info *hdmi_fb = info->par; + struct msm_panel_data *panel = hdmi_fb->panel; + unsigned long irq_flags; + + /* printk(KERN_DEBUG "%s\n", __func__); */ + if ((test_bit(fb_enabled, &hdmi_fb->state) == 0) || + (test_bit(hdmi_enabled, &hdmi_fb->state) == 0)) + return; + + spin_lock_irqsave(&hdmi_fb->update_lock, irq_flags); + hdmi_fb->update_info.left = left; + hdmi_fb->update_info.top = top; + hdmi_fb->update_info.eright = eright; + hdmi_fb->update_info.ebottom = ebottom; + hdmi_fb->update_info.yoffset = yoffset; + spin_unlock_irqrestore(&hdmi_fb->update_lock, irq_flags); + panel->request_vsync(panel, &hdmi_fb->vsync_callback); +} + +/* fb ops, fb_pan_display */ +static int +hdmifb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + /* full update */ + hdmifb_pan_update(info, 0, 0, info->var.xres, info->var.yres, + var->yoffset); + return 0; +} + +static void hdmifb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) +{ + cfb_fillrect(p, rect); + hdmifb_pan_update(p, rect->dx, rect->dy, rect->dx + rect->width, + rect->dy + rect->height, 0); +} + +static void hdmifb_copyarea(struct fb_info *p, const struct fb_copyarea *area) +{ + cfb_copyarea(p, area); + hdmifb_pan_update(p, area->dx, area->dy, area->dx + area->width, + area->dy + area->height, 0); +} + +static void hdmifb_imageblit(struct fb_info *p, const struct fb_image *image) +{ + cfb_imageblit(p, image); + hdmifb_pan_update(p, image->dx, image->dy, image->dx + image->width, + image->dy + image->height, 0); +} + +static int hdmifb_change_mode(struct fb_info *info, unsigned int mode) +{ + struct hdmifb_info *hdmi_fb = info->par; + + /* printk(KERN_DEBUG "%s mode = %d\n", __func__, mode); */ + + if (mode) + set_bit(hdmi_mode, &hdmi_fb->state); + else + clear_bit(hdmi_mode, &hdmi_fb->state); + return 0; +} + +struct hdmifb_info *_hdmi_fb; +int hdmifb_get_mode(void) +{ + return test_bit(hdmi_mode, &_hdmi_fb->state); +} + +bool hdmifb_suspending = false; + +static int hdmifb_pause(struct fb_info *fb, unsigned int mode) +{ + int ret = 0; + struct hdmifb_info *hdmi_fb = fb->par; + struct msm_panel_data *panel = hdmi_fb->panel; + struct hdmi_info *info = container_of(hdmi, struct hdmi_info, + hdmi_dev); + + pr_info("%s: %d %s\n", __func__, atomic_read(&hdmi_fb->use_count), + mode == 1 ? "pause" : "resume"); + + if (mode == 1) { + hdmifb_suspending = false; + HDMI_DBG("%s: hdmifb_suspending = false\n", __func__); + /* pause */ + if (atomic_read(&hdmi_fb->use_count) == 0) + goto done; + if (atomic_dec_return(&hdmi_fb->use_count) == 0) { + hdmi_pre_change(info); + ret = panel->blank(panel); + clear_bit(hdmi_enabled, &hdmi_fb->state); +#ifdef CONFIG_HTC_HEADSET_MGR + switch_send_event(BIT_HDMI_AUDIO, 0); +#endif + } + } else if (mode == 0) { + /* resume */ + if (atomic_inc_return(&hdmi_fb->use_count) == 1) { + hdmi_pre_change(info); + ret = panel->unblank(panel); +/* + // set timing again to prevent TV been out of range + var = &fb->var; + timing = hdmi->set_res(hdmi, var); + panel->adjust_timing(panel, timing, var->xres, var->yres); + hdmi_post_change(info, var); +*/ + set_bit(hdmi_enabled, &hdmi_fb->state); +#ifdef CONFIG_HTC_HEADSET_MGR + switch_send_event(BIT_HDMI_AUDIO, 1); +#endif + } + } else + ret = -EINVAL; +done: + return ret; +} + +static int hdmifb_blit(struct fb_info *info, void __user *p) +{ + struct mdp_blit_req req; + struct mdp_blit_req_list req_list; + int i; + int ret; + + if (copy_from_user(&req_list, p, sizeof(req_list))) + return -EFAULT; + + for (i = 0; i < req_list.count; i++) { + struct mdp_blit_req_list *list = + (struct mdp_blit_req_list *)p; + if (copy_from_user(&req, &list->req[i], sizeof(req))) + return -EFAULT; + req.flags |= MDP_DITHER; + ret = mdp->blit(mdp, info, &req); + if (ret) + return ret; + } + return 0; +} + +enum ioctl_cmd_index { + CMD_SET_MODE, + CMD_GET_MODE, + CMD_DISABLE, + CMD_ENABLE, + CMD_GET_STATE, + CMD_BLIT, + CMD_CABLE_STAT, + CMD_ESTABLISH_TIMING, +}; + +static char *cmd_str[] = { + "HDMI_SET_MODE", + "HDMI_GET_MODE", + "HDMI_DISABLE", + "HDMI_ENABLE", + "HDMI_GET_STATE", + "HDMI_BLIT", + "HDMI_CABLE_STAT", + "HDMI_ESTABLISH_TIMING", +}; + +static int hdmifb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg) +{ + struct hdmifb_info *hdmi_fb = p->par; + void __user *argp = (void __user *)arg; + unsigned int val; + int ret = -EINVAL; + struct hdmi_info *hinfo = container_of(hdmi, struct hdmi_info, + hdmi_dev); + +/* + if (cmd != HDMI_BLIT) + HDMI_DBG("%s, cmd=%d=%s\n", __func__, cmd - HDMI_SET_MODE, + cmd_str[cmd-HDMI_SET_MODE]); +*/ + + switch (cmd) { + case HDMI_SET_MODE: + get_user(val, (unsigned __user *) arg); + //pr_info("[hdmi] SET_MODE: %d\n", val); + ret = hdmifb_change_mode(p, val); + break; + case HDMI_GET_MODE: +/* + pr_info("[hdmi] GET_MODE: %d\n", + test_bit(hdmi_mode, &hdmi_fb->state)); +*/ + ret = put_user(test_bit(hdmi_mode, &hdmi_fb->state), + (unsigned __user *) arg); + break; + case HDMI_DISABLE: + get_user(val, (unsigned __user *) arg); + ret = hdmifb_pause(p, 1); + break; + case HDMI_ENABLE: + get_user(val, (unsigned __user *) arg); + ret = hdmifb_pause(p, 0); + break; + case HDMI_GET_STATE: + ret = put_user(test_bit(hdmi_enabled, &hdmi_fb->state), + (unsigned __user *) arg); + break; + case HDMI_BLIT: + if (test_bit(hdmi_enabled, &hdmi_fb->state)) + ret = hdmifb_blit(p, argp); + else + ret = -EPERM; + break; + case HDMI_CABLE_STAT: { + int connect; + ret = hdmi->get_cable_state(hdmi, &connect); + ret = put_user(connect, (unsigned __user *) arg); + break; + } + case HDMI_ESTABLISH_TIMING: { + u8 tmp[3]; + hdmi->get_establish_timing(hdmi, tmp); + ret = copy_to_user((unsigned __user *) arg, tmp, 3); + if (ret) + ret = -EFAULT; + break; + } + case HDMI_GET_EDID: + ret = copy_to_user((unsigned __user *) arg, + hinfo->edid_buf, 512); + break; + case HDMI_GET_DISPLAY_INFO: { + struct display_info dinfo; + u8 *ptr = hinfo->edid_buf; + dinfo.visible_width = + (((u32)ptr[68] & 0xf0) << 4) | ptr[66]; + dinfo.visible_height = + (((u32)ptr[68] & 0x0f) << 8) | ptr[67]; + dinfo.resolution_width = + (((u32)ptr[58] & 0xf0) << 4) | ptr[56]; + dinfo.resolution_height = + (((u32)ptr[61] & 0xf0) << 4) | ptr[59]; + ret = copy_to_user((unsigned __user *) arg, + &dinfo, sizeof(dinfo)); + break; + } + default: + printk(KERN_ERR "hdmi: unknown cmd, cmd = %d\n", cmd); + } + return ret; +} + +static struct fb_ops hdmi_fb_ops = { + .owner = THIS_MODULE, + .fb_open = hdmifb_open, + .fb_release = hdmifb_release, + .fb_check_var = hdmifb_check_var, + .fb_set_par = hdmifb_set_par, + .fb_pan_display = hdmifb_pan_display, + .fb_fillrect = hdmifb_fillrect, + .fb_copyarea = hdmifb_copyarea, + .fb_imageblit = hdmifb_imageblit, + .fb_ioctl = hdmifb_ioctl, +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void hdmifb_suspend(struct early_suspend *h) +{ + struct hdmifb_info *hdmi_fb = container_of(h, struct hdmifb_info, + early_suspend); + struct msm_panel_data *panel = hdmi_fb->panel; + + HDMI_DBG("%s, use_count=%d\n", __func__, + atomic_read(&hdmi_fb->use_count)); + hdmifb_suspending = true; + HDMI_DBG("%s: hdmifb_suspending = true\n", __func__); + if (atomic_read(&hdmi_fb->use_count) && + false == test_bit(hdmi_enabled, &hdmi_fb->state) + ) { + if (panel->blank) + panel->blank(panel); + } + + if (panel->suspend) + panel->suspend(panel); + + clear_bit(hdmi_enabled, &hdmi_fb->state); + clear_bit(fb_enabled, &hdmi_fb->state); +} + +static void hdmifb_resume(struct early_suspend *h) +{ + struct hdmifb_info *hdmi_fb = container_of(h, struct hdmifb_info, + early_suspend); + struct msm_panel_data *panel = hdmi_fb->panel; + + HDMI_DBG("%s\n", __func__); + if (panel->resume) + panel->resume(panel); + + atomic_set(&hdmi_fb->use_count, 0); + set_bit(fb_enabled, &hdmi_fb->state); +} +#endif + +#define BITS_PER_PIXEL 16 + +static void setup_fb_info(struct hdmifb_info *hdmi_fb) +{ + struct fb_info *fb_info = hdmi_fb->fb; + int r; + + /* finish setting up the fb_info struct */ + strncpy(fb_info->fix.id, "hdmi_fb", 16); + fb_info->fix.ypanstep = 1; + + fb_info->fbops = &hdmi_fb_ops; + fb_info->flags = FBINFO_DEFAULT; + + fb_info->fix.type = FB_TYPE_PACKED_PIXELS; + fb_info->fix.visual = FB_VISUAL_TRUECOLOR; + fb_info->fix.line_length = hdmi_fb->xres * 2; + + fb_info->var.xres = hdmi_fb->xres; + fb_info->var.yres = hdmi_fb->yres; + fb_info->var.width = hdmi_fb->panel->fb_data->width; + fb_info->var.height = hdmi_fb->panel->fb_data->height; + fb_info->var.xres_virtual = hdmi_fb->xres; + fb_info->var.yres_virtual = hdmi_fb->yres * 2; + fb_info->var.bits_per_pixel = BITS_PER_PIXEL; + fb_info->var.accel_flags = 0; + fb_info->var.yoffset = 0; + + fb_info->var.red.offset = 11; + fb_info->var.red.length = 5; + fb_info->var.red.msb_right = 0; + fb_info->var.green.offset = 5; + fb_info->var.green.length = 6; + fb_info->var.green.msb_right = 0; + fb_info->var.blue.offset = 0; + fb_info->var.blue.length = 5; + fb_info->var.blue.msb_right = 0; + + r = fb_alloc_cmap(&fb_info->cmap, 16, 0); + fb_info->pseudo_palette = PP; + + PP[0] = 0; + for (r = 1; r < 16; r++) + PP[r] = 0xffffffff; +} + +static int +setup_fbmem(struct hdmifb_info *hdmi_fb, struct platform_device *pdev) +{ + struct fb_info *fb = hdmi_fb->fb; + struct resource *res; + unsigned long size = hdmi_fb->xres * hdmi_fb->yres * + (BITS_PER_PIXEL >> 3) * 2; + unsigned char *fbram; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + /* check the resource is large enough to fit the fb */ + if (resource_size(res) < size) { + printk(KERN_ERR "allocated resource(%d) is too small(%lu)" + "for fb\n", resource_size(res), size); + return -ENOMEM; + } + + fb->fix.smem_start = res->start; + fb->fix.smem_len = resource_size(res); + + fbram = ioremap(res->start, resource_size(res)); + if (fbram == 0) { + printk(KERN_ERR "hdmi_fb: cannot allocate fbram!\n"); + return -ENOMEM; + } + + fb->screen_base = fbram; + memset(fbram, 0, resource_size(res)); + + printk(KERN_DEBUG "HDMI FB: 0x%x 0x%x\n", res->start, res->end); + return 0; +} + +/* Called from dma interrupt handler, must not sleep */ +static void hdmi_handle_dma(struct msmfb_callback *callback) +{ + /* printk(KERN_DEBUG "%s\n", __func__); */ +} + +/* Called from vsync interrupt handler, must not sleep */ +static void hdmi_handle_vsync(struct msmfb_callback *callback) +{ + uint32_t x, y, w, h; + unsigned yoffset; + unsigned addr; + unsigned long irq_flags; + struct fb_info *mirror_fb = registered_fb[0], *fb_hdmi; + struct hdmifb_info *hdmi = container_of(callback, struct hdmifb_info, + vsync_callback); + struct msm_panel_data *panel = hdmi->panel; + + spin_lock_irqsave(&hdmi->update_lock, irq_flags); + x = hdmi->update_info.left; + y = hdmi->update_info.top; + w = hdmi->update_info.eright - x; + h = hdmi->update_info.ebottom - y; + yoffset = hdmi->update_info.yoffset; + hdmi->update_info.left = hdmi->xres + 1; + hdmi->update_info.top = hdmi->yres + 1; + hdmi->update_info.eright = 0; + hdmi->update_info.ebottom = 0; + if (unlikely(w > hdmi->xres || h > hdmi->yres || + w == 0 || h == 0)) { + printk(KERN_INFO "invalid update: %d %d %d " + "%d\n", x, y, w, h); + goto error; + } + spin_unlock_irqrestore(&hdmi->update_lock, irq_flags); + + addr = ((hdmi->xres * (yoffset + y) + x) * 2); + if (test_bit(hdmi_mode, &hdmi->state) == 0) { + mdp->dma(mdp, addr + mirror_fb->fix.smem_start, + hdmi->xres * 2, w, h, x, y, &hdmi->dma_callback, + panel->interface_type); + } else { + fb_hdmi = hdmi->fb; + mdp->dma(mdp, addr + fb_hdmi->fix.smem_start, + hdmi->xres * 2, w, h, x, y, &hdmi->dma_callback, + panel->interface_type); + } + return; +error: + spin_unlock_irqrestore(&hdmi->update_lock, irq_flags); +} + +static int hdmifb_probe(struct platform_device *pdev) +{ + struct fb_info *info; + struct hdmifb_info *hdmi_fb; + struct msm_panel_data *panel = pdev->dev.platform_data; + int ret; + + printk(KERN_DEBUG "%s\n", __func__); + + if (!panel) { + pr_err("hdmi_fb_probe: no platform data\n"); + return -EINVAL; + } + + if (!panel->fb_data) { + pr_err("hdmi_fb_probe: no fb_data\n"); + return -EINVAL; + } + + info = framebuffer_alloc(sizeof(struct hdmifb_info), &pdev->dev); + if (!info) + return -ENOMEM; + + hdmi_fb = info->par; + _hdmi_fb = hdmi_fb; + hdmi_fb->fb = info; + hdmi_fb->panel = panel; + set_bit(hdmi_mode, &hdmi_fb->state); + hdmi_fb->dma_callback.func = hdmi_handle_dma; + hdmi_fb->vsync_callback.func = hdmi_handle_vsync; + hdmi_fb->xres = panel->fb_data->xres; + hdmi_fb->yres = panel->fb_data->yres; + spin_lock_init(&hdmi_fb->update_lock); + + ret = setup_fbmem(hdmi_fb, pdev); + if (ret) + goto error_setup_fbmem; + + setup_fb_info(hdmi_fb); + + ret = register_framebuffer(info); + if (ret) + goto error_register_fb; + + printk(KERN_INFO "hdmi_fb %d * %d initialed\n", + hdmi_fb->xres, hdmi_fb->yres); + +#ifdef CONFIG_HAS_EARLYSUSPEND + hdmi_fb->early_suspend.suspend = hdmifb_suspend; + hdmi_fb->early_suspend.resume = hdmifb_resume; + hdmi_fb->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + register_early_suspend(&hdmi_fb->early_suspend); +#endif + /* blank panel explicitly because we turn on clk on initial */ + if (panel->blank) + panel->blank(panel); + set_bit(fb_enabled, &hdmi_fb->state); + return 0; + +error_register_fb: +error_setup_fbmem: + framebuffer_release(hdmi_fb->fb); + printk(KERN_ERR "msm probe fail with %d\n", ret); + return ret; +} + +static struct platform_driver hdmi_frame_buffer = { + .probe = hdmifb_probe, + .driver = {.name = "msm_hdmi"}, +}; + +static int hdmifb_add_mdp_device(struct device *dev, + struct class_interface *class_intf) +{ + /* might need locking if mulitple mdp devices */ + if (mdp) + return 0; + mdp = container_of(dev, struct mdp_device, dev); + return platform_driver_register(&hdmi_frame_buffer); +} + +static void hdmifb_remove_mdp_device(struct device *dev, + struct class_interface *class_intf) +{ + /* might need locking if mulitple mdp devices */ + if (dev != &mdp->dev) + return; + platform_driver_unregister(&hdmi_frame_buffer); + mdp = NULL; +} + +static struct class_interface hdmi_fb_interface = { + .add_dev = &hdmifb_add_mdp_device, + .remove_dev = &hdmifb_remove_mdp_device, +}; + +static int hdmifb_add_hdmi_device(struct device *dev, + struct class_interface *class_intf) +{ + dev_dbg(dev, "%s\n", __func__); + + if (hdmi) + return 0; + hdmi = container_of(dev, struct hdmi_device, dev); + return 0; +} + +static struct class_interface hdmi_interface = { + .add_dev = hdmifb_add_hdmi_device, +}; + +static int __init hdmifb_init(void) +{ + int rc; + + rc = register_mdp_client(&hdmi_fb_interface); + if (rc) + return rc; + + rc = register_hdmi_client(&hdmi_interface); + if (rc) + return rc; + return 0; +} + +module_init(hdmifb_init); diff --git a/drivers/video/msm/hdmi/hdmi_lcdc.c b/drivers/video/msm/hdmi/hdmi_lcdc.c new file mode 100644 index 0000000000000..45bbbb35a34b2 --- /dev/null +++ b/drivers/video/msm/hdmi/hdmi_lcdc.c @@ -0,0 +1,634 @@ +/* drivers/video/msm/hdmi_lcdc.c + * + * Copyright (c) 2009 Google Inc. + * Copyright (c) 2009 HTC + * + * 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 "../mdp_hw.h" +#include "../../../../arch/arm/mach-msm/proc_comm.h" +#include "../../../../arch/arm/mach-msm/clock-pcom.h" + + +#if 1 +#define HDMI_DBG(s...) printk("[hdmi/lcdc]" s) +#else +#define HDMI_DBG(s...) do {} while (0) +#endif + +struct mdp_lcdc_info { + struct mdp_info *mdp; + struct clk *mdp_clk; + struct clk *pclk; + struct clk *pad_pclk; + struct msm_panel_data fb_panel_data; + struct platform_device fb_pdev; + struct msm_lcdc_platform_data *pdata; + uint32_t fb_start; + + struct msmfb_callback frame_start_cb; + wait_queue_head_t vsync_waitq; + int got_vsync; + + struct { + uint32_t clk_rate; + uint32_t hsync_ctl; + uint32_t vsync_period; + uint32_t vsync_pulse_width; + uint32_t display_hctl; + uint32_t display_vstart; + uint32_t display_vend; + uint32_t hsync_skew; + uint32_t polarity; + } parms; + atomic_t blank_count; + struct mutex blank_lock; +}; +struct mdp_lcdc_info *_lcdc; + +static struct mdp_device *mdp_dev; + +#define panel_to_lcdc(p) container_of((p), struct mdp_lcdc_info, fb_panel_data) + +/* FIXME: arrange the clock manipulating to proper place, + integrate with the counter of fb_hdmi +*/ +int lcdc_enable_video(void) +{ + //struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + struct mdp_lcdc_info *lcdc = _lcdc; + struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; + + mutex_lock(&lcdc->blank_lock); + if (atomic_read(&lcdc->blank_count)) + goto end_enable_video; + HDMI_DBG("%s: enable clocks\n", __func__); + clk_enable(lcdc->mdp_clk); + clk_enable(lcdc->pclk); + clk_enable(lcdc->pad_pclk); + + /* TODO: need pre-test to see if it make any influence to HDCP, + * if ebi1_clk doesn't enabled here. + */ + //panel_ops->unblank(panel_ops); + + mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); + atomic_inc(&lcdc->blank_count); + HDMI_DBG("%s, blank_count=%d\n", __func__, + atomic_read(&lcdc->blank_count)); +end_enable_video: + mutex_unlock(&lcdc->blank_lock); + + return 0; +} + +int lcdc_disable_video(void) +{ + struct mdp_lcdc_info *lcdc = _lcdc; + struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; + + mutex_lock(&lcdc->blank_lock); + if (atomic_read(&lcdc->blank_count) == 0) + goto disable_video_done; + if (atomic_dec_return(&lcdc->blank_count) == 0) { + HDMI_DBG("%s: disable clocks\n", __func__); + panel_ops->blank(panel_ops); + mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); + clk_disable(lcdc->pclk); + clk_disable(lcdc->pad_pclk); + clk_disable(lcdc->mdp_clk); + } +disable_video_done: + mutex_unlock(&lcdc->blank_lock); + HDMI_DBG("%s, blank_count=%d\n", __func__, + atomic_read(&lcdc->blank_count)); + return 0; +} + +static int lcdc_unblank(struct msm_panel_data *fb_panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; + + HDMI_DBG("%s\n", __func__); + +#if 0 + HDMI_DBG("%s: enable clocks\n", __func__); + clk_enable(lcdc->mdp_clk); + clk_enable(lcdc->pclk); + clk_enable(lcdc->pad_pclk); + + panel_ops->unblank(panel_ops); + + mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); + atomic_set(&lcdc->blank_count, 1); +#else + lcdc_enable_video(); + /* TODO: need pre-test to see if it make any influence to HDCP, + * if ebi1_clk enabled here. + */ + panel_ops->unblank(panel_ops); +#endif + return 0; +} + +static int lcdc_blank(struct msm_panel_data *fb_panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + //struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; + +#if 0 + mutex_lock(&lcdc->blank_lock); + if (atomic_read(&lcdc->blank_count) == 0) + goto blank_done; + if (atomic_dec_return(&lcdc->blank_count) == 0) { + HDMI_DBG("%s: disable clocks\n", __func__); + panel_ops->blank(panel_ops); + mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); + clk_disable(lcdc->pclk); + clk_disable(lcdc->pad_pclk); + clk_disable(lcdc->mdp_clk); + } +blank_done: + mutex_unlock(&lcdc->blank_lock); + HDMI_DBG("%s, blank_count=%d\n", __func__, + atomic_read(&lcdc->blank_count)); +#else + lcdc_disable_video(); +#endif + return 0; +} + +static int lcdc_suspend(struct msm_panel_data *fb_panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; + + //pr_info("%s: suspending\n", __func__); + HDMI_DBG("%s\n", __func__); + + if (panel_ops->uninit) + panel_ops->uninit(panel_ops); + lcdc_disable_video(); + + return 0; +} + +static int lcdc_resume(struct msm_panel_data *fb_panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; + + //pr_info("%s: resuming\n", __func__); + HDMI_DBG("%s\n", __func__); + + if (panel_ops->init) { + if (panel_ops->init(panel_ops) < 0) + printk(KERN_ERR "LCD init fail!\n"); + } + + return 0; +} + +static int +lcdc_adjust_timing(struct msm_panel_data *fb_panel, + struct msm_lcdc_timing *timing, u32 xres, u32 yres) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + unsigned int hsync_period; + unsigned int hsync_start_x; + unsigned int hsync_end_x; + unsigned int vsync_period; + unsigned int display_vstart; + unsigned int display_vend; + uint32_t dma_cfg; + + clk_set_rate(lcdc->pclk, timing->clk_rate); + clk_set_rate(lcdc->pad_pclk, timing->clk_rate); + HDMI_DBG("%s, clk=%d, xres=%d, yres=%d,\n", __func__, + clk_get_rate(lcdc->pclk), xres, yres); + + hsync_period = (timing->hsync_pulse_width + timing->hsync_back_porch + + xres + timing->hsync_front_porch); + hsync_start_x = (timing->hsync_pulse_width + timing->hsync_back_porch); + hsync_end_x = hsync_period - timing->hsync_front_porch - 1; + + vsync_period = (timing->vsync_pulse_width + timing->vsync_back_porch + + yres + timing->vsync_front_porch); + vsync_period *= hsync_period; + + display_vstart = timing->vsync_pulse_width + timing->vsync_back_porch; + display_vstart *= hsync_period; + display_vstart += timing->hsync_skew; + + display_vend = timing->vsync_front_porch * hsync_period; + display_vend = vsync_period - display_vend + timing->hsync_skew - 1; + + /* register values we pre-compute at init time from the timing + * information in the panel info */ + lcdc->parms.hsync_ctl = (((hsync_period & 0xfff) << 16) | + (timing->hsync_pulse_width & 0xfff)); + lcdc->parms.vsync_period = vsync_period & 0xffffff; + lcdc->parms.vsync_pulse_width = (timing->vsync_pulse_width * + hsync_period) & 0xffffff; + + lcdc->parms.display_hctl = (((hsync_end_x & 0xfff) << 16) | + (hsync_start_x & 0xfff)); + lcdc->parms.display_vstart = display_vstart & 0xffffff; + lcdc->parms.display_vend = display_vend & 0xffffff; + lcdc->parms.hsync_skew = timing->hsync_skew & 0xfff; + lcdc->parms.polarity = ((timing->hsync_act_low << 0) | + (timing->vsync_act_low << 1) | + (timing->den_act_low << 2)); + lcdc->parms.clk_rate = timing->clk_rate; + + mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL); + mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD); + mdp_writel(lcdc->mdp, lcdc->parms.vsync_pulse_width, + MDP_LCDC_VSYNC_PULSE_WIDTH); + mdp_writel(lcdc->mdp, lcdc->parms.display_hctl, MDP_LCDC_DISPLAY_HCTL); + mdp_writel(lcdc->mdp, lcdc->parms.display_vstart, + MDP_LCDC_DISPLAY_V_START); + mdp_writel(lcdc->mdp, lcdc->parms.display_vend, MDP_LCDC_DISPLAY_V_END); + mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW); + + mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR); + mdp_writel(lcdc->mdp, 0x0, MDP_LCDC_UNDERFLOW_CTL); + mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL); + mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START); + mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END); + mdp_writel(lcdc->mdp, lcdc->parms.polarity, MDP_LCDC_CTL_POLARITY); + printk("solomon: polarity=%04x\n", mdp_readl(lcdc->mdp, MDP_LCDC_CTL_POLARITY)); + + /* config the dma_p block that drives the lcdc data */ + mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_DMA_P_IBUF_ADDR); + mdp_writel(lcdc->mdp, (((yres & 0x7ff) << 16) | + (xres & 0x7ff)), + MDP_DMA_P_SIZE); + /* TODO: pull in the bpp info from somewhere else? */ + mdp_writel(lcdc->mdp, xres * 2, + MDP_DMA_P_IBUF_Y_STRIDE); + mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY); + + dma_cfg = (DMA_PACK_ALIGN_LSB | + DMA_PACK_PATTERN_RGB | + DMA_DITHER_EN); + dma_cfg |= DMA_OUT_SEL_LCDC; + dma_cfg |= DMA_IBUF_FORMAT_RGB565; + dma_cfg |= DMA_DSTC0G_8BITS | DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; + + mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG); + return 0; +} + +static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) +{ + struct msm_panel_data *fb_panel = &lcdc->fb_panel_data; + uint32_t dma_cfg; + unsigned int clk_id, clk_rate; + + clk_enable(lcdc->mdp_clk); + clk_enable(lcdc->pclk); + clk_enable(lcdc->pad_pclk); + + clk_set_rate(lcdc->pclk, lcdc->parms.clk_rate); + clk_set_rate(lcdc->pad_pclk, lcdc->parms.clk_rate); + printk(KERN_DEBUG "pclk = %ld, pad_pclk = %ld\n", + clk_get_rate(lcdc->pclk), + clk_get_rate(lcdc->pad_pclk)); + + /* write the lcdc params */ + mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL); + mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD); + mdp_writel(lcdc->mdp, lcdc->parms.vsync_pulse_width, + MDP_LCDC_VSYNC_PULSE_WIDTH); + mdp_writel(lcdc->mdp, lcdc->parms.display_hctl, MDP_LCDC_DISPLAY_HCTL); + mdp_writel(lcdc->mdp, lcdc->parms.display_vstart, + MDP_LCDC_DISPLAY_V_START); + mdp_writel(lcdc->mdp, lcdc->parms.display_vend, MDP_LCDC_DISPLAY_V_END); + mdp_writel(lcdc->mdp, lcdc->parms.hsync_skew, MDP_LCDC_HSYNC_SKEW); + + mdp_writel(lcdc->mdp, 0, MDP_LCDC_BORDER_CLR); + mdp_writel(lcdc->mdp, 0, MDP_LCDC_UNDERFLOW_CTL); + mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_HCTL); + mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START); + mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END); + mdp_writel(lcdc->mdp, lcdc->parms.polarity, MDP_LCDC_CTL_POLARITY); + printk("solomon: polarity=%04x\n", mdp_readl(lcdc->mdp, MDP_LCDC_CTL_POLARITY)); + + /* config the dma_p block that drives the lcdc data */ + mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_DMA_P_IBUF_ADDR); + mdp_writel(lcdc->mdp, (((fb_panel->fb_data->yres & 0x7ff) << 16) | + (fb_panel->fb_data->xres & 0x7ff)), + MDP_DMA_P_SIZE); + /* TODO: pull in the bpp info from somewhere else? */ + mdp_writel(lcdc->mdp, fb_panel->fb_data->xres * 2, + MDP_DMA_P_IBUF_Y_STRIDE); + mdp_writel(lcdc->mdp, 0, MDP_DMA_P_OUT_XY); + + dma_cfg = (DMA_PACK_ALIGN_LSB | + DMA_PACK_PATTERN_RGB | + DMA_DITHER_EN); + dma_cfg |= DMA_OUT_SEL_LCDC; + dma_cfg |= DMA_IBUF_FORMAT_RGB565; + dma_cfg |= DMA_DSTC0G_8BITS | DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; + + mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG); + + /* Send customized command to ARM9 for escalating DMA_P as tier-1 + * of AXI bus. + * Ref: SR#272509 + */ + clk_id = P_USB_PHY_CLK; + clk_rate = 0x1; + msm_proc_comm(PCOM_CLKCTL_RPC_MIN_RATE, &clk_id, &clk_rate); + + return 0; +} + +static void lcdc_wait_vsync(struct msm_panel_data *panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(panel); + int ret; + + ret = wait_event_timeout(lcdc->vsync_waitq, lcdc->got_vsync, HZ / 2); + if (ret == 0) + pr_err("%s: timeout waiting for VSYNC\n", __func__); + lcdc->got_vsync = 0; +} + +static void lcdc_request_vsync(struct msm_panel_data *fb_panel, + struct msmfb_callback *vsync_cb) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + + /* the vsync callback will start the dma */ + vsync_cb->func(vsync_cb); + lcdc->got_vsync = 0; + mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, MDP_LCDC_FRAME_START, + &lcdc->frame_start_cb); + lcdc_wait_vsync(fb_panel); +} + +static void lcdc_clear_vsync(struct msm_panel_data *fb_panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + lcdc->got_vsync = 0; + mdp_out_if_req_irq(mdp_dev, MSM_LCDC_INTERFACE, 0, NULL); +} + +/* called in irq context with mdp lock held, when mdp gets the + * MDP_LCDC_FRAME_START interrupt */ +static void lcdc_frame_start(struct msmfb_callback *cb) +{ + struct mdp_lcdc_info *lcdc; + + lcdc = container_of(cb, struct mdp_lcdc_info, frame_start_cb); + + lcdc->got_vsync = 1; + wake_up(&lcdc->vsync_waitq); +} + +static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride, + uint32_t width, uint32_t height, uint32_t x, + uint32_t y) +{ + struct mdp_lcdc_info *lcdc = priv; + struct mdp_info *mdp = lcdc->mdp; + +#if 0 + if (mdp->dma_config_dirty) { + mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); + mdelay(20); + mdp_dev->configure_dma(mdp_dev); + mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); + } +#endif + mdp_writel(lcdc->mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE); + mdp_writel(lcdc->mdp, addr, MDP_DMA_P_IBUF_ADDR); +} + +static void precompute_timing_parms(struct mdp_lcdc_info *lcdc) +{ + struct msm_lcdc_timing *timing = lcdc->pdata->timing; + struct msm_fb_data *fb_data = lcdc->pdata->fb_data; + unsigned int hsync_period; + unsigned int hsync_start_x; + unsigned int hsync_end_x; + unsigned int vsync_period; + unsigned int display_vstart; + unsigned int display_vend; + + hsync_period = (timing->hsync_pulse_width + timing->hsync_back_porch + + fb_data->xres + timing->hsync_front_porch); + hsync_start_x = (timing->hsync_pulse_width + timing->hsync_back_porch); + hsync_end_x = hsync_period - timing->hsync_front_porch - 1; + + vsync_period = (timing->vsync_pulse_width + timing->vsync_back_porch + + fb_data->yres + timing->vsync_front_porch); + vsync_period *= hsync_period; + + display_vstart = timing->vsync_pulse_width + timing->vsync_back_porch; + display_vstart *= hsync_period; + display_vstart += timing->hsync_skew; + + display_vend = timing->vsync_front_porch * hsync_period; + display_vend = vsync_period - display_vend + timing->hsync_skew - 1; + + /* register values we pre-compute at init time from the timing + * information in the panel info */ + lcdc->parms.hsync_ctl = (((hsync_period & 0xfff) << 16) | + (timing->hsync_pulse_width & 0xfff)); + lcdc->parms.vsync_period = vsync_period & 0xffffff; + lcdc->parms.vsync_pulse_width = (timing->vsync_pulse_width * + hsync_period) & 0xffffff; + + lcdc->parms.display_hctl = (((hsync_end_x & 0xfff) << 16) | + (hsync_start_x & 0xfff)); + lcdc->parms.display_vstart = display_vstart & 0xffffff; + lcdc->parms.display_vend = display_vend & 0xffffff; + lcdc->parms.hsync_skew = timing->hsync_skew & 0xfff; + lcdc->parms.polarity = ((timing->hsync_act_low << 0) | + (timing->vsync_act_low << 1) | + (timing->den_act_low << 2)); + lcdc->parms.clk_rate = timing->clk_rate; +} + +static int hdmi_lcdc_probe(struct platform_device *pdev) +{ + struct msm_lcdc_platform_data *pdata = pdev->dev.platform_data; + struct mdp_lcdc_info *lcdc; + int ret = 0; + + printk(KERN_DEBUG "%s\n", __func__); + + if (!pdata) { + pr_err("%s: no LCDC platform data found\n", __func__); + return -EINVAL; + } + + _lcdc = lcdc = kzalloc(sizeof(struct mdp_lcdc_info), GFP_KERNEL); + if (!lcdc) + return -ENOMEM; + + /* We don't actually own the clocks, the mdp does. */ + lcdc->mdp_clk = clk_get(mdp_dev->dev.parent, "mdp_clk"); + if (IS_ERR(lcdc->mdp_clk)) { + pr_err("%s: failed to get mdp_clk\n", __func__); + ret = PTR_ERR(lcdc->mdp_clk); + goto err_get_mdp_clk; + } + + lcdc->pclk = clk_get(mdp_dev->dev.parent, "lcdc_pclk_clk"); + if (IS_ERR(lcdc->pclk)) { + pr_err("%s: failed to get lcdc_pclk\n", __func__); + ret = PTR_ERR(lcdc->pclk); + goto err_get_pclk; + } + + lcdc->pad_pclk = clk_get(mdp_dev->dev.parent, "lcdc_pad_pclk_clk"); + if (IS_ERR(lcdc->pad_pclk)) { + pr_err("%s: failed to get lcdc_pad_pclk\n", __func__); + ret = PTR_ERR(lcdc->pad_pclk); + goto err_get_pad_pclk; + } + + init_waitqueue_head(&lcdc->vsync_waitq); + mutex_init(&lcdc->blank_lock); + lcdc->pdata = pdata; + lcdc->frame_start_cb.func = lcdc_frame_start; + + platform_set_drvdata(pdev, lcdc); + + mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, MDP_DMA_P_DONE, + lcdc_dma_start); + + precompute_timing_parms(lcdc); + + lcdc->fb_start = pdata->fb_resource->start; + lcdc->mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + + lcdc->fb_panel_data.suspend = lcdc_suspend; + lcdc->fb_panel_data.resume = lcdc_resume; + lcdc->fb_panel_data.wait_vsync = lcdc_wait_vsync; + lcdc->fb_panel_data.request_vsync = lcdc_request_vsync; + lcdc->fb_panel_data.clear_vsync = lcdc_clear_vsync; + lcdc->fb_panel_data.blank = lcdc_blank; + lcdc->fb_panel_data.unblank = lcdc_unblank; + lcdc->fb_panel_data.adjust_timing = lcdc_adjust_timing; + lcdc->fb_panel_data.fb_data = pdata->fb_data; + lcdc->fb_panel_data.interface_type = MSM_LCDC_INTERFACE; + + ret = lcdc_hw_init(lcdc); + atomic_set(&lcdc->blank_count, 1); + if (ret) { + pr_err("%s: Cannot initialize the mdp_lcdc\n", __func__); + goto err_hw_init; + } + + lcdc->fb_pdev.name = "msm_hdmi"; + lcdc->fb_pdev.id = pdata->fb_id; + lcdc->fb_pdev.resource = pdata->fb_resource; + lcdc->fb_pdev.num_resources = 1; + lcdc->fb_pdev.dev.platform_data = &lcdc->fb_panel_data; + + ret = platform_device_register(&lcdc->fb_pdev); + if (ret) { + pr_err("%s: Cannot register msm_panel pdev\n", __func__); + goto err_plat_dev_reg; + } + + pr_info("%s: initialized\n", __func__); + + return 0; + +err_plat_dev_reg: +err_hw_init: + platform_set_drvdata(pdev, NULL); + clk_put(lcdc->pad_pclk); +err_get_pad_pclk: + clk_put(lcdc->pclk); +err_get_pclk: + clk_put(lcdc->mdp_clk); +err_get_mdp_clk: + kfree(lcdc); + return ret; +} + +static int hdmi_lcdc_remove(struct platform_device *pdev) +{ + struct mdp_lcdc_info *lcdc = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + clk_put(lcdc->pclk); + clk_put(lcdc->pad_pclk); + kfree(lcdc); + + return 0; +} + +static struct platform_driver mdp_lcdc_driver = { + .probe = hdmi_lcdc_probe, + .remove = hdmi_lcdc_remove, + .driver = { + .name = "msm_mdp_hdmi", + .owner = THIS_MODULE, + }, +}; + +static int mdp_lcdc_add_mdp_device(struct device *dev, + struct class_interface *class_intf) +{ + /* might need locking if mulitple mdp devices */ + if (mdp_dev) + return 0; + mdp_dev = container_of(dev, struct mdp_device, dev); + return platform_driver_register(&mdp_lcdc_driver); +} + +static void mdp_lcdc_remove_mdp_device(struct device *dev, + struct class_interface *class_intf) +{ + /* might need locking if mulitple mdp devices */ + if (dev != &mdp_dev->dev) + return; + platform_driver_unregister(&mdp_lcdc_driver); + mdp_dev = NULL; +} + +static struct class_interface mdp_lcdc_interface = { + .add_dev = &mdp_lcdc_add_mdp_device, + .remove_dev = &mdp_lcdc_remove_mdp_device, +}; + +static int __init mdp_lcdc_init(void) +{ + return register_mdp_client(&mdp_lcdc_interface); +} + +module_init(mdp_lcdc_init); diff --git a/drivers/video/msm/hdmi/include/edid.h b/drivers/video/msm/hdmi/include/edid.h new file mode 100644 index 0000000000000..1b7e4587e5c6e --- /dev/null +++ b/drivers/video/msm/hdmi/include/edid.h @@ -0,0 +1,82 @@ +#ifndef _EDID_H_ +#define _EDID_H_ + +#define MAX_VIDEO_MODES 32 +struct edid_info_struct { + bool is_valid; + struct mutex access_lock; + bool under_scan; + bool basic_audio; + bool ycbcr_4_4_4; + bool ycbcr_4_2_2; + bool hdmi_sink; +}; + +#define EDID_BLOCK_SIZE 128 +#define EDID_HDR_NO_OF_FF 0x06 + +#define EDID_BLOCK_0_OFFSET 0x00 +#define EDID_BLOCK_1_OFFSET 0x80 +#define EDID_BLOCK_SIZE 128 +#define EDID_HDR_NO_OF_FF 0x06 +#define NUM_OF_EXTEN_ADDR 0x7E + +#define EDID_TAG_ADDR 0x00 +#define EDID_REV_ADDR 0x01 +#define EDID_TAG_IDX 0x02 +#define LONG_DESCR_PTR_IDX 0x02 +#define MISC_SUPPORT_IDX 0x03 + +#define ESTABLISHED_TIMING_INDEX 35 +#define NUM_OF_STANDARD_TIMINGS 8 +#define STANDARD_TIMING_OFFSET 38 +#define LONG_DESCR_LENi 18 +#define NUM_OF_DETAILED_DESCRIPTORS 4 + +#define DETAILED_TIMING_OFFSET 0x36 + +/* Offsets within a Long Descriptors Block */ +#define PIX_CLK_OFFSET 0 +#define H_ACTIVE_OFFSET 2 +#define H_BLANKING_OFFSET 3 +#define V_ACTIVE_OFFSET 5 +#define V_BLANKING_OFFSET 6 +#define H_SYNC_OFFSET 8 +#define H_SYNC_PW_OFFSET 9 +#define V_SYNC_OFFSET 10 +#define V_SYNC_PW_OFFSET 10 +#define H_IMAGE_SIZE_OFFSET 12 +#define V_IMAGE_SIZE_OFFSET 13 +#define H_BORDER_OFFSET 15 +#define V_BORDER_OFFSET 16 +#define FLAGS_OFFSET 17 + +#define AR16_10 0 +#define AR4_3 1 +#define AR5_4 2 +#define AR16_9 3 + +#define EDID_EXTENSION_TAG 0x02 +#define EDID_REV_THREE 0x03 +#define EDID_DATA_START 0x04 + +#define EDID_BLOCK_0 0x00 +#define EDID_BLOCK_2_3 0x01 + +#define AUDIO_DESCR_SIZE 3 + +/* Data Block Tag Codes */ +#define AUDIO_D_BLOCK 0x01 +#define VIDEO_D_BLOCK 0x02 +#define VENDOR_SPEC_D_BLOCK 0x03 +#define SPKR_ALLOC_D_BLOCK 0x04 +#define USE_EXTENDED_TAG 0x07 + +/* Extended Data Block Tag Codes */ +#define COLORIMETRY_D_BLOCK 0x05 + +#define VIDEO_CAPABILITY_D_BLOCK 0x00 +#define HDMI_SIGNATURE_LEN 0x03 + +#define CEC_PHYS_ADDR_LEN 0x02 +#endif diff --git a/drivers/video/msm/hdmi/include/fb-hdmi.h b/drivers/video/msm/hdmi/include/fb-hdmi.h new file mode 100644 index 0000000000000..3252ec125f337 --- /dev/null +++ b/drivers/video/msm/hdmi/include/fb-hdmi.h @@ -0,0 +1,27 @@ +#ifndef _FB_HDMI_H_ +#define _FB_HDMI_H_ + +#include +#include + +enum hd_res { + hd_720p = 0, /* 1280 * 720 */ + svga, /* 800 * 600 */ + pal, /* 720 * 576 */ + edtv, /* 720 * 480 */ + vga, /* 640 * 480 */ +}; + +struct msm_lcdc_timing; +struct hdmi_device { + struct device dev; + int (*check_res)(struct hdmi_device *, struct fb_var_screeninfo *); + struct msm_lcdc_timing *(*set_res)(struct hdmi_device *, + struct fb_var_screeninfo *); + int (*get_cable_state)(struct hdmi_device *, int *); + int (*get_establish_timing)(struct hdmi_device *, u8 *); +}; + +struct class_interface; +int register_hdmi_client(struct class_interface *class_intf); +#endif diff --git a/drivers/video/msm/hdmi/include/sil902x.h b/drivers/video/msm/hdmi/include/sil902x.h new file mode 100644 index 0000000000000..9f27a147580a1 --- /dev/null +++ b/drivers/video/msm/hdmi/include/sil902x.h @@ -0,0 +1,400 @@ +#ifndef __SIL902X_H_ +#define __SIL902X_H_ + +#include +#include +#include "edid.h" + +struct hdmi_info { + struct hdmi_device hdmi_dev; + struct i2c_client *client; + struct msm_lcdc_panel_ops hdmi_lcdc_ops; + struct work_struct work; + struct delayed_work hdmi_delay_work; + struct mutex lock; + struct mutex lock2; + struct clk *ebi1_clk; + int (*power)(int on); + void (*hdmi_gpio_on)(void); + void (*hdmi_gpio_off)(void); + enum hd_res res; + // FIXME: move to edid_info_struct + u8 edid_buf[128 * 4]; + enum { + SLEEP, + AWAKE, + } sleeping; + bool polling; + bool cable_connected; + bool isr_enabled; + bool first; + struct completion hotplug_completion; + struct timer_list timer; + struct work_struct polling_work; + struct dentry *debug_dir; + struct edid_info_struct edid_info; + struct mutex polling_lock; + bool suspending; + bool user_playing; + bool video_streaming; +}; + +enum { + HDMI_PIXEL_DATA = 0x08, + HDMI_AVI_INFO_FRAME = 0x0c, + HDMI_AUDIO_INFO_FRAME = 0xbf, + HDMI_SYS_CTL = 0x1a, + HDMI_POWER = 0x1e, + HDMI_IDENTIFY = 0x1b, + HDMI_INT_EN = 0x3c, + HDMI_INT_STAT = 0x3d, + HDMI_EN_REG = 0xc7, +}; + +/* flag bitmap for register HDMI_INT_STAT */ +enum { + HOT_PLUG_PENDING = (1U << 0), + RX_PENDING = (1U << 1), + HOT_PLUG_STATE = (1U << 2), + RX_STATE = (1U << 3), + AUDIO_ERR = (1U << 4), + SECURITY_STATE = (1U << 5), + HDCP_VALUE = (1U << 6), + HDCP_AUTH = (1U << 7), +}; + +enum ErrorMessages { + INIT_SYSTEM_SUCCESSFUL, // 0 + + BLACK_BOX_OPEN_FAILURE, + BLACK_BOX_OPENED_SUCCESSFULLY, + HW_RESET_FAILURE, + TPI_ENABLE_FAILURE, + INTERRUPT_EN_FAILURE, + INTERRUPT_POLLING_FAILED, + + NO_SINK_CONNECTED, + DDC_BUS_REQ_FAILURE, + HDCP_FAILURE, + HDCP_OK, // 10 + RX_AUTHENTICATED, + SINK_DOES_NOT_SUPPORT_HDCP, + TX_DOES_NOT_SUPPORT_HDCP, + + ILLEGAL_AKSV, + SET_PROTECTION_FAILURE, + REVOKED_KEYS_FOUND, + REPEATER_AUTHENTICATED, + INT_STATUS_READ_FAILURE, + + PROTECTION_OFF_FAILED, + PROTECTION_ON_FAILED, // 20 + INTERRUPT_POLLING_OK, + + EDID_PARSING_FAILURE, + VIDEO_SETUP_FAILURE, + TPI_READ_FAILURE, + TPI_WRITE_FAILURE, + + INIT_VIDEO_FAILURE, + DE_CANNOT_BE_SET_WITH_EMBEDDED_SYNC, + SET_EMBEDDED_SYC_FAILURE, + V_MODE_NOT_SUPPORTED, + + AUD_MODE_NOT_SUPPORTED, // 30 + I2S_NOT_SET, + + EDID_READ_FAILURE, + EDID_CHECKSUM_ERROR, + INCORRECT_EDID_HEADER, + EDID_EXT_TAG_ERROR, + + EDID_REV_ADDR_ERROR, + EDID_V_DESCR_OVERFLOW, + INCORRECT_EDID_FILE, + UNKNOWN_EDID_TAG_CODE, + NO_DETAILED_DESCRIPTORS_IN_THIS_EDID_BLOCK, // 40 + CONFIG_DATA_VALID, + CONFIG_DATA_INVALID, + + GPIO_ACCESS_FAILED, + GPIO_CONFIG_ERROR, + + HP_EVENT_GOING_TO_SERVICE_LOOP, + EDID_PARSED_OK, + VIDEO_MODE_SET_OK, + AUDIO_MODE_SET_OK, + + I2S_MAPPING_SUCCESSFUL, + I2S_INPUT_CONFIG_SUCCESSFUL, // 50 + I2S_HEADER_SET_SUCCESSFUL, + INTERRUPT_POLLING_SUCCESSFUL, + + HPD_LOOP_EXITED_SUCCESSFULY, + HPD_LOOP_FAILED, + SINK_CONNECTED, + HP_EVENT_RETURNING_FROM_SERVICE_LOOP, + AVI_INFOFRAMES_SETTING_FAILED, + + TMDS_ENABLING_FAILED, + DE_SET_OK, + DE_SET_FAILED, + NO_861_EXTENSIONS, + + GO_OK, + IMAGE_PKTS_UPDATED_OK, + MONITORING_BLOCKED, + LINK_NORMAL, + LINK_LOST, + RENEGOTIATION_REQUIRED, + + LINK_SUSPENDED, + EDID_SHORT_DESCRIPTORS_PARSED_OK, + EDID_LONG_DESCRIPTORS_PARSED_OK, + DDC_BUS_RELEASE_FAILURE, + FAILED_GETTING_BKSV, + + PLL_SETUP_FAILUE, + ERR_RX_QUEUE_FULL, + ERR_TX_QUEUE_FULL, + GBD_SET_SUCCESSFULLY, + BACKDOOR_SETTING_FAILED, + ERR_TX_QUEUE_EMPTY +}; + +#define BIT_0 0x01 +#define BIT_1 0x02 +#define BIT_2 0x04 +#define BIT_3 0x08 +#define BIT_4 0x10 +#define BIT_5 0x20 +#define BIT_6 0x40 +#define BIT_7 0x80 + +#define INTERVAL_HDCP_POLLING (HZ / 25) + +#define REQUEST_RELEASE_DDC_BEFORE_HDCP +#define T_HDCP_ACTIVATION 500 +#define T_HDCP_DEACTIVATION 200 + +#define T_HPD_DELAY 10 +#define TPI_INTERRUPT_EN 0x3c +#define ALL 0xff +#define DUMMY 0xFD + +#define SiI_DEVICE_ID 0xB0 + +#define T_DDC_ACCESS 50 +// TPI Control Masks +// ================= +#define BIT_OUTPUT_MODE 0x01 +#define BIT_DDC_BUS_GRANT 0x02 +#define BIT_DDC_BUS_REQ 0x04 +#define BIT_TMDS_OUTPUT 0x10 + +#define TPI_INTERNAL_PAGE_REG 0xBC +#define TPI_REGISTER_OFFSET_REG 0xBD +#define TPI_REGISTER_VALUE_REG 0xBE + +/* HDCP Control Masks */ +#define BIT_PROTECT_LEVEL 0x01 +#define BIT_PROTECT_TYPE 0x02 +#define BIT_REPEATER 0x08 +#define BIT_LOCAL_PROTECT 0x40 +#define BIT_EXT_PROTECT 0x80 + +#define BITS_LINK_LOST 0x10 +#define BITS_RENEGOTIATION 0x20 + +#define BIT_TMDS_OUTPUT 0x10 + +#define BIT_AUDIO_MUTE 0x10 + +#define TPI_HDCP_REVISION_DATA_REG (0x30) +#define HDCP_MAJOR_REVISION_MASK (BIT_7 | BIT_6 | BIT_5 | BIT_4) +#define HDCP_MAJOR_REVISION_VALUE (0x10) + +#define HDCP_MINOR_REVISION_MASK (BIT_3 | BIT_2 | BIT_1 | BIT_0) +#define HDCP_MINOR_REVISION_VALUE (0x02) + +#define HDCP_REVISION 0x12 +#define SET_PROT_ATTEMPTS 0x05 + +#define AKSV_SIZE 5 +#define BYTE_SIZE 8 +#define NUM_OF_ONES_IN_KSV 20 + +// Interrupt Masks +//================ +#define HOT_PLUG_EVENT 0x01 +#define RX_SENSE_EVENT 0x02 +#define TPI_HOT_PLUG_STATE 0x04 +#define RX_SENSE_STATE 0x08 + +#define AUDIO_ERROR_EVENT 0x10 +#define SECURITY_CHANGE_EVENT 0x20 +#define V_READY_EVENT 0x40 +#define HDCP_CHANGE_EVENT 0x80 + +#define NON_MASKABLE_INT 0xFF + +/* Protection Levels */ +#define NO_PROTECTION 0x00 +#define LOCAL_PROTECTION 0x01 +#define EXTENDED_PROTECTION 0x03 + +#define LINK_NORMAL 0 +#define MAX_V_DESCRIPTORS 20 +#define MAX_A_DESCRIPTORS 10 +#define MAX_SPEAKER_CONFIGURATIONS 4 + +#define HDMI_DEBUGFS_ROOT "hdmi" + +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// +/*\ +| | HDCP Implementation +| | +| | HDCP link security logic is implemented in certain transmitters; unique +| | keys are embedded in each chip as part of the solution. The security +| | scheme is fully automatic and handled completely by the hardware. +\*/ +/// HDCP Query Data Register ============================================== /// + +#define TPI_HDCP_QUERY_DATA_REG (0x29) + +#define EXTENDED_LINK_PROTECTION_MASK (BIT_7) +#define EXTENDED_LINK_PROTECTION_NONE (0x00) +#define EXTENDED_LINK_PROTECTION_SECURE (0x80) + +#define LOCAL_LINK_PROTECTION_MASK (BIT_6) +#define LOCAL_LINK_PROTECTION_NONE (0x00) +#define LOCAL_LINK_PROTECTION_SECURE (0x40) + +#define LINK_STATUS_MASK (BIT_5 | BIT_4) +#define LINK_STATUS_NORMAL (0x00) +#define LINK_STATUS_LINK_LOST (0x10) +#define LINK_STATUS_RENEGOTIATION_REQ (0x20) +#define LINK_STATUS_LINK_SUSPENDED (0x30) + +#define HDCP_REPEATER_MASK (BIT_3) +#define HDCP_REPEATER_NO (0x00) +#define HDCP_REPEATER_YES (0x08) + +#define CONNECTOR_TYPE_MASK (BIT_2 | BIT_0) +#define CONNECTOR_TYPE_DVI (0x00) +#define CONNECTOR_TYPE_RSVD (0x01) +#define CONNECTOR_TYPE_HDMI (0x04) +#define CONNECTOR_TYPE_FUTURE (0x05) + +#define PROTECTION_TYPE_MASK (BIT_1) +#define PROTECTION_TYPE_NONE (0x00) +#define PROTECTION_TYPE_HDCP (0x02) + +/// HDCP Control Data Register ============================================ /// + +#define TPI_HDCP_CONTROL_DATA_REG (0x2A) + +#define PROTECTION_LEVEL_MASK (BIT_0) +#define PROTECTION_LEVEL_MIN (0x00) +#define PROTECTION_LEVEL_MAX (0x01) + +/*---------------------------------------------------------------------------*/ + +#if 0 +/* Caller: ChangeVideoMode(), HDCP_Poll(), HotPlugServiceLoop(), RestartHDCP() + */ +#define EnableTMDS(hdmi) ReadClearWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT) // 0x1A[4] = 0 + +/* Caller: ChangeVideoMode(), HDCP_Poll(), TPI_Poll(), RestartHDCP(), + * OnHdmiCableDisconnected() + */ +#define DisableTMDS(hdmi) ReadSetWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT) // 0x1A[4] = 1 +#else + +void EnableTMDS(struct hdmi_info *hdmi); +void DisableTMDS(struct hdmi_info *hdmi); + +#endif + +// FIXME: fix the global variables +extern u8 pvid_mode, vid_mode; +extern u8 LinkProtectionLevel; +extern u8 systemInitialized; +/*---------------------------------------------------------------------------*/ +int hdmi_read(struct i2c_client *client, u8 cmd); +int hdmi_write_byte(struct i2c_client *client, u8 reg, u8 val); +int hdmi_enable_int(struct i2c_client *client); +int hdmi_disable_int(struct i2c_client *client); +int hdmi_read_edid(struct hdmi_info *info, struct i2c_client *client); +int hdmi_standby(struct hdmi_info *hdmi); +int hdmi_wakeup(struct hdmi_info *hdmi); +int read_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset); +void ReadSetWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Pattern); +void ReadModifyWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Mask, u8 Value); +void tpi_clear_interrupt(struct hdmi_info *hdmi, u8 pattern); +bool tpi_init(struct hdmi_info *hdmi); +void ReadClearWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Pattern); +s32 ReadBlockTPI(struct hdmi_info *hdmi, u8 TPI_Offset, u16 NBytes, u8 *pData); +bool IsRepeater(struct hdmi_info *hdmi); +bool GetDDC_Access(struct hdmi_info *hdmi, u8* SysCtrlRegVal); +bool ReleaseDDC(struct hdmi_info *hdmi, u8 SysCtrlRegVal); +int tpi_read_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset) +; +void tpi_write_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset, u8 RegValue); + +int HotPlugServiceLoop(struct hdmi_info *hdmi); + +int hdmi_active9022(struct i2c_client *client); +int hdmi_active9022_dup(struct i2c_client *client); +bool avc_send_avi_info_frames(struct hdmi_info *hdmi); +bool avc_init_video(struct hdmi_info *hdmi, u8 mode, u8 TclkSel, bool Init); +//void hdcp_on(struct hdmi_info *hdmi); +void hdcp_off(struct hdmi_info *hdmi); +void hdcp_check_status(struct hdmi_info *hdmi, u8 InterruptStatusImage); +void hdcp_init(struct hdmi_info *hdmi); +int hdcp_debugfs_init(struct hdmi_info *hdmi); + +extern u8 EDID_TempData[]; +u8 edid_simple_parsing(struct hdmi_info *hdmi); + +int edid_dump_hex(u8 *src, int src_size, char *output, int output_size); +bool edid_is_video_mode_supported(struct video_mode *vmode); +int edid_debugfs_init(struct hdmi_info *hdmi); +bool edid_check_sink_type(struct hdmi_info *hdmi); +int HotPlugServiceLoop(struct hdmi_info *hdmi); +int tpi_prepare(struct hdmi_info *hdmi); + +//bool InitVideo(struct hdmi_info *hdmi, u8 Mode, u8 TclkSel, bool Init); +void avc_set_basic_audio(struct hdmi_info *hdmi); + +int hdmi_debugfs_init(struct hdmi_info *hdmi); +int tpi_debugfs_init(struct hdmi_info *hdmi); +ssize_t hdmi_dbgfs_open(struct inode *inode, struct file *file); + +void SetAudioMute(struct hdmi_info *hdmi, u8 audioMute); +void SetInputColorSpace(struct hdmi_info *hdmi, u8 inputColorSpace); + +void WriteBackDoorRegister(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset, u8 RegValue); + +#define TPI_INPUT_FORMAT 0x09 +#define TPI_OUTPUT_FORMAT 0x0A + +#define TPI_SYSTEM_CONTROL 0x1A + +#define TPI_DEVICE_POWER_STATE_CTRL_REG (0x1E) + +#define CTRL_PIN_CONTROL_MASK (BIT_4) +#define CTRL_PIN_TRISTATE (0x00) +#define CTRL_PIN_DRIVEN_TX_BRIDGE (0x10) + +#define TX_POWER_STATE_D0 (0x00) +#define TX_POWER_STATE_D1 (0x01) +#define TX_POWER_STATE_D2 (0x02) +#define TX_POWER_STATE_D3 (0x03) + +#define TPI_AUDIO_INTERFACE 0x26 + +#define TPI_HDCP_QUERY_DATA 0x29 +#define TPI_HDCP_CTRL 0x2A + +#endif diff --git a/drivers/video/msm/hdmi/include/tpi.h b/drivers/video/msm/hdmi/include/tpi.h new file mode 100644 index 0000000000000..db0271fd5ed1b --- /dev/null +++ b/drivers/video/msm/hdmi/include/tpi.h @@ -0,0 +1,411 @@ +#ifndef _TPI_H_ +#define _TPI_H_ + +#define TPI_PIX_CLK_LSB (0x00) +#define TPI_PIX_CLK_MSB (0x01) + +#define TPI_VERT_FREQ_LSB (0x02) +#define TPI_VERT_FREQ_MSB (0x03) + +#define TPI_TOTAL_PIX_LSB (0x04) +#define TPI_TOTAL_PIX_MSB (0x05) + +#define TPI_TOTAL_LINES_LSB (0x06) +#define TPI_TOTAL_LINES_MSB (0x07) + +// Pixel Repetition Data +//====================== + +#define TPI_PIX_REPETITION (0x08) + +// TPI AVI Input and Output Format Data +//===================================== + +/// AVI Input Format Data ================================================= /// + +#define TPI_INPUT_FORMAT_REG (0x09) + +//Finish this... + +#define INPUT_COLOR_SPACE_MASK (BIT_1 | BIT_0) +#define INPUT_COLOR_SPACE_RGB (0x00) +#define INPUT_COLOR_SPACE_YCBCR444 (0x01) +#define INPUT_COLOR_SPACE_YCBCR422 (0x02) +#define INPUT_COLOR_SPACE_BLACK_MODE (0x03) + +/// AVI Output Format Data ================================================ /// + +#define TPI_OUTPUT_FORMAT_REG (0x0A) + +#define TPI_YC_Input_Mode (0x0B) + +// TPI AVI InfoFrame Data +//======================= + +#define TPI_AVI_BYTE_0 (0x0C) +#define TPI_AVI_BYTE_1 (0x0D) +#define TPI_AVI_BYTE_2 (0x0E) +#define TPI_AVI_BYTE_3 (0x0F) +#define TPI_AVI_BYTE_4 (0x10) +#define TPI_AVI_BYTE_5 (0x11) + +#define TPI_AUDIO_BYTE_0 (0xBF) + +#define TPI_INFO_FRM_DBYTE5 0xC8 +#define TPI_INFO_FRM_DBYTE6 0xC9 + +#define TPI_END_TOP_BAR_LSB (0x12) +#define TPI_END_TOP_BAR_MSB (0x13) + +#define TPI_START_BTM_BAR_LSB (0x14) +#define TPI_START_BTM_BAR_MSB (0x15) + +#define TPI_END_LEFT_BAR_LSB (0x16) +#define TPI_END_LEFT_BAR_MSB (0x17) + +#define TPI_END_RIGHT_BAR_LSB (0x18) +#define TPI_END_RIGHT_BAR_MSB (0x19) + +// Colorimetry +//============ +#define SET_EX_COLORIMETRY 0x0C // Set TPI_AVI_BYTE_2 to extended colorimetry and use + //TPI_AVI_BYTE_3 + +// ===================================================== // + +#define TPI_SYSTEM_CONTROL_DATA_REG (0x1A) + +#define LINK_INTEGRITY_MODE_MASK (BIT_6) +#define LINK_INTEGRITY_STATIC (0x00) +#define LINK_INTEGRITY_DYNAMIC (0x40) + +#define TMDS_OUTPUT_CONTROL_MASK (BIT_4) +#define TMDS_OUTPUT_CONTROL_ACTIVE (0x00) +#define TMDS_OUTPUT_CONTROL_POWER_DOWN (0x10) + +#define AV_MUTE_MASK (BIT_3) +#define AV_MUTE_NORMAL (0x00) +#define AV_MUTE_MUTED (0x08) + +#define DDC_BUS_REQUEST_MASK (BIT_2) +#define DDC_BUS_REQUEST_NOT_USING (0x00) +#define DDC_BUS_REQUEST_REQUESTED (0x04) + +#define DDC_BUS_GRANT_MASK (BIT_1) +#define DDC_BUS_GRANT_NOT_AVAILABLE (0x00) +#define DDC_BUS_GRANT_GRANTED (0x02) + +#define OUTPUT_MODE_MASK (BIT_0) +#define OUTPUT_MODE_DVI (0x00) +#define OUTPUT_MODE_HDMI (0x01) + + +// TPI Identification Registers +//============================= + +#define TPI_DEVICE_ID (0x1B) +#define TPI_DEVICE_REV_ID (0x1C) + +#define TPI_RESERVED2 (0x1D) + +// ===================================================== // + +#define TPI_DEVICE_POWER_STATE_CTRL_REG (0x1E) + +#define CTRL_PIN_CONTROL_MASK (BIT_4) +#define CTRL_PIN_TRISTATE (0x00) +#define CTRL_PIN_DRIVEN_TX_BRIDGE (0x10) + +#define TX_POWER_STATE_MASK (BIT_1 | BIT_0) +#define TX_POWER_STATE_D0 (0x00) +#define TX_POWER_STATE_D1 (0x01) +#define TX_POWER_STATE_D2 (0x02) +#define TX_POWER_STATE_D3 (0x03) + +// Configuration of I2S Interface +//=============================== + +#define TPI_I2S_EN (0x1F) +#define TPI_I2S_IN_CFG (0x20) + +// Available only when TPI 0x26[7:6]=10 to select I2S input +//========================================================= +#define TPI_I2S_CHST_0 (0x21) +#define TPI_I2S_CHST_1 (0x22) +#define TPI_I2S_CHST_2 (0x23) +#define TPI_I2S_CHST_3 (0x24) +#define TPI_I2S_CHST_4 (0x25) + + +// Available only when 0x26[7:6]=01 +//================================= +#define TPI_SPDIF_HEADER (0x24) +#define TPI_AUDIO_HANDLING (0x25) + + +// Audio Configuration Regiaters +//============================== +#define TPI_AUDIO_INTERFACE_REG (0x26) + +// Finish this... + +#define AUDIO_MUTE_MASK (BIT_4) +#define AUDIO_MUTE_NORMAL (0x00) +#define AUDIO_MUTE_MUTED (0x10) + + + + + + +#define TPI_AUDIO_SAMPLE_CTRL (0x27) + +#define TPI_SPEAKER_CFG (0xC7) +#define TPI_CHANNEL_COUNT (0xC4) + +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// + +/*\ +| | HDCP Implementation +| | +| | HDCP link security logic is implemented in certain transmitters; unique +| | keys are embedded in each chip as part of the solution. The security +| | scheme is fully automatic and handled completely by the hardware. +\*/ + +/// HDCP Query Data Register ============================================== /// + +#define TPI_HDCP_QUERY_DATA_REG (0x29) + +#define EXTENDED_LINK_PROTECTION_MASK (BIT_7) +#define EXTENDED_LINK_PROTECTION_NONE (0x00) +#define EXTENDED_LINK_PROTECTION_SECURE (0x80) + +#define LOCAL_LINK_PROTECTION_MASK (BIT_6) +#define LOCAL_LINK_PROTECTION_NONE (0x00) +#define LOCAL_LINK_PROTECTION_SECURE (0x40) + +#define LINK_STATUS_MASK (BIT_5 | BIT_4) +#define LINK_STATUS_NORMAL (0x00) +#define LINK_STATUS_LINK_LOST (0x10) +#define LINK_STATUS_RENEGOTIATION_REQ (0x20) +#define LINK_STATUS_LINK_SUSPENDED (0x30) + +#define HDCP_REPEATER_MASK (BIT_3) +#define HDCP_REPEATER_NO (0x00) +#define HDCP_REPEATER_YES (0x08) + +#define CONNECTOR_TYPE_MASK (BIT_2 | BIT_0) +#define CONNECTOR_TYPE_DVI (0x00) +#define CONNECTOR_TYPE_RSVD (0x01) +#define CONNECTOR_TYPE_HDMI (0x04) +#define CONNECTOR_TYPE_FUTURE (0x05) + +#define PROTECTION_TYPE_MASK (BIT_1) +#define PROTECTION_TYPE_NONE (0x00) +#define PROTECTION_TYPE_HDCP (0x02) + +/// HDCP Control Data Register ============================================ /// + +#define TPI_HDCP_CONTROL_DATA_REG (0x2A) + +#define PROTECTION_LEVEL_MASK (BIT_0) +#define PROTECTION_LEVEL_MIN (0x00) +#define PROTECTION_LEVEL_MAX (0x01) + +/// HDCP BKSV Registers =================================================== /// + +#define TPI_BKSV_1_REG (0x2B) +#define TPI_BKSV_2_REG (0x2C) +#define TPI_BKSV_3_REG (0x2D) +#define TPI_BKSV_4_REG (0x2E) +#define TPI_BKSV_5_REG (0x2F) + +/// HDCP Revision Data Register =========================================== /// + +#define TPI_HDCP_REVISION_DATA_REG (0x30) + +#define HDCP_MAJOR_REVISION_MASK (BIT_7 | BIT_6 | BIT_5 | BIT_4) +#define HDCP_MAJOR_REVISION_VALUE (0x10) + +#define HDCP_MINOR_REVISION_MASK (BIT_3 | BIT_2 | BIT_1 | BIT_0) +#define HDCP_MINOR_REVISION_VALUE (0x02) + +/// HDCP KSV and V' Value Data Register =================================== /// + +#define TPI_V_PRIME_SELECTOR_REG (0x31) + +/// V' Value Readback Registers =========================================== /// + +#define TPI_V_PRIME_7_0_REG (0x32) +#define TPI_V_PRIME_15_9_REG (0x33) +#define TPI_V_PRIME_23_16_REG (0x34) +#define TPI_V_PRIME_31_24_REG (0x35) + +/// HDCP AKSV Registers =================================================== /// + +#define TPI_AKSV_1_REG (0x36) +#define TPI_AKSV_2_REG (0x37) +#define TPI_AKSV_3_REG (0x38) +#define TPI_AKSV_4_REG (0x39) +#define TPI_AKSV_5_REG (0x3A) + +/*\ +| | Interrupt Service +| | +| | TPI can be configured to generate an interrupt to the host to notify it of +| | various events. The host can either poll for activity or use an interrupt +| | handler routine. TPI generates on a single interrupt (INT) to the host. +\*/ + +/// Interrupt Enable Register ============================================= /// + +#define TPI_INTERRUPT_ENABLE_REG (0x3C) + +#define HDCP_AUTH_STATUS_CHANGE_EN_MASK (BIT_7) +#define HDCP_AUTH_STATUS_CHANGE_DISABLE (0x00) +#define HDCP_AUTH_STATUS_CHANGE_ENABLE (0x80) + +#define HDCP_VPRIME_VALUE_READY_EN_MASK (BIT_6) +#define HDCP_VPRIME_VALUE_READY_DISABLE (0x00) +#define HDCP_VPRIME_VALUE_READY_ENABLE (0x40) + +#define HDCP_SECURITY_CHANGE_EN_MASK (BIT_5) +#define HDCP_SECURITY_CHANGE_DISABLE (0x00) +#define HDCP_SECURITY_CHANGE_ENABLE (0x20) + +#define AUDIO_ERROR_EVENT_EN_MASK (BIT_4) +#define AUDIO_ERROR_EVENT_DISABLE (0x00) +#define AUDIO_ERROR_EVENT_ENABLE (0x10) + +#define CPI_EVENT_NO_RX_SENSE_MASK (BIT_3) +#define CPI_EVENT_NO_RX_SENSE_DISABLE (0x00) +#define CPI_EVENT_NO_RX_SENSE_ENABLE (0x08) + +#define RECEIVER_SENSE_EVENT_EN_MASK (BIT_1) +#define RECEIVER_SENSE_EVENT_DISABLE (0x00) +#define RECEIVER_SENSE_EVENT_ENABLE (0x02) + +#define HOT_PLUG_EVENT_EN_MASK (BIT_0) +#define HOT_PLUG_EVENT_DISABLE (0x00) +#define HOT_PLUG_EVENT_ENABLE (0x01) + +/// Interrupt Status Register ============================================= /// + +#define TPI_INTERRUPT_STATUS_REG (0x3D) + +#define HDCP_AUTH_STATUS_CHANGE_EVENT_MASK (BIT_7) +#define HDCP_AUTH_STATUS_CHANGE_EVENT_NO (0x00) +#define HDCP_AUTH_STATUS_CHANGE_EVENT_YES (0x80) + +#define HDCP_VPRIME_VALUE_READY_EVENT_MASK (BIT_6) +#define HDCP_VPRIME_VALUE_READY_EVENT_NO (0x00) +#define HDCP_VPRIME_VALUE_READY_EVENT_YES (0x40) + +#define HDCP_SECURITY_CHANGE_EVENT_MASK (BIT_5) +#define HDCP_SECURITY_CHANGE_EVENT_NO (0x00) +#define HDCP_SECURITY_CHANGE_EVENT_YES (0x20) + +#define AUDIO_ERROR_EVENT_MASK (BIT_4) +#define AUDIO_ERROR_EVENT_NO (0x00) +#define AUDIO_ERROR_EVENT_YES (0x10) + +#define CPI_EVENT_MASK (BIT_3) +#define CPI_EVENT_NO (0x00) +#define CPI_EVENT_YES (0x08) +#define RX_SENSE_MASK (BIT_3) // This bit is dual purpose depending on the value of 0x3C[3] +#define RX_SENSE_NOT_ATTACHED (0x00) +#define RX_SENSE_ATTACHED (0x08) + +#define HOT_PLUG_PIN_STATE_MASK (BIT_2) +#define HOT_PLUG_PIN_STATE_LOW (0x00) +#define HOT_PLUG_PIN_STATE_HIGH (0x04) + +#define RECEIVER_SENSE_EVENT_MASK (BIT_1) +#define RECEIVER_SENSE_EVENT_NO (0x00) +#define RECEIVER_SENSE_EVENT_YES (0x02) + +#define HOT_PLUG_EVENT_MASK (BIT_0) +#define HOT_PLUG_EVENT_NO (0x00) +#define HOT_PLUG_EVENT_YES (0x01) + +/// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// + +// Sync Register Configuration and Sync Monitoring Registers +//========================================================== + +#define TPI_SYNC_GEN_CTRL (0x60) +#define TPI_SYNC_POLAR_DETECT (0x61) + +// Explicit Sync DE Generator Registers (TPI 0x60[7]=0) +//===================================================== + +#define TPI_DE_DLY (0x62) +#define TPI_DE_CTRL (0x63) +#define TPI_DE_TOP (0x64) + +#define TPI_RESERVED4 (0x65) + +#define TPI_DE_CNT_7_0 (0x66) +#define TPI_DE_CNT_11_8 (0x67) + +#define TPI_DE_LIN_7_0 (0x68) +#define TPI_DE_LIN_10_8 (0x69) + +#define TPI_DE_H_RES_7_0 (0x6A) +#define TPI_DE_H_RES_10_8 (0x6B) + +#define TPI_DE_V_RES_7_0 (0x6C) +#define TPI_DE_V_RES_10_8 (0x6D) + +// Embedded Sync Register Set (TPI 0x60[7]=1) +//=========================================== + +#define TPI_HBIT_TO_HSYNC_7_0 (0x62) +#define TPI_HBIT_TO_HSYNC_9_8 (0x63) +#define TPI_FIELD_2_OFFSET_7_0 (0x64) +#define TPI_FIELD_2_OFFSET_11_8 (0x65) +#define TPI_HWIDTH_7_0 (0x66) +#define TPI_HWIDTH_8_9 (0x67) +#define TPI_VBIT_TO_VSYNC (0x68) +#define TPI_VWIDTH (0x69) + +// TPI Enable Register +//==================== + +#define TPI_ENABLE (0xC7) + +// Misc InfoFrames +//================ +#define MISC_INFO_FRAMES_CTRL (0xBF) +#define MISC_INFO_FRAMES_TYPE (0xC0) +#define MISC_INFO_FRAMES_VER (0xC1) +#define MISC_INFO_FRAMES_LEN (0xC2) +#define MISC_INFO_FRAMES_CHKSUM (0xC3) + +// Backdoor Register Offsets +//========================== +#define INTERNAL_PAGE_0 0x00 +#define INTERNAL_PAGE_1 0x01 +#define DEVICE_ID_LOW_BYTE 0x02 +#define DEVICE_ID_HI_BYTE 0x03 +#define AUDIO_INPUT_LENGTH 0x24 + + +#define SW_RESET 0x05 +#define POWER_DOWN 0x6F + + +// Backdoor constants +//=================== + +#define DIV_BY_2 0x00 +#define MULT_BY_1 0x01 +#define MULT_BY_2 0x02 +#define MULT_BY_4 0x03 + +#define INTERNAL_PAGE_1 0x01 +#define INTERNAL_PAGE_2 0x02 +#define TMDS_CONT_REG 0x82 + +#endif diff --git a/drivers/video/msm/hdmi/silicon-image/Makefile b/drivers/video/msm/hdmi/silicon-image/Makefile new file mode 100644 index 0000000000000..37cda94e17518 --- /dev/null +++ b/drivers/video/msm/hdmi/silicon-image/Makefile @@ -0,0 +1,7 @@ +sil-objs = \ + tpi.o \ + hdcp.o \ + av_config.o \ + debug-sil902x.o + +obj-$(CONFIG_MSM_HDMI) += sil.o diff --git a/drivers/video/msm/hdmi/silicon-image/av_config.c b/drivers/video/msm/hdmi/silicon-image/av_config.c new file mode 100644 index 0000000000000..2be7d9500a727 --- /dev/null +++ b/drivers/video/msm/hdmi/silicon-image/av_config.c @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2009 HTC + * + * 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. + */ + +/* TODO: + * 1. Mutex while active + */ +#include +#include +#include "../include/fb-hdmi.h" +#include "../include/sil902x.h" +#include "../include/tpi.h" + +#if 1 +#define HDMI_DBG(s...) printk("[hdmi/avc]" s) +#else +#define HDMI_DBG(s...) do {} while (0) +#endif + +static u8 video_param[][8] = { + [hd_720p] = {0x01, 0x1d, 0x70, 0x17, 0x72, 0x06, 0xee, 0x02}, + [svga] = {0xa0, 0x0f, 0x70, 0x17, 0x20, 0x04, 0x74, 0x02}, + [pal] = {0x8c, 0x0a, 0x88, 0x13, 0x60, 0x03, 0x71, 0x02}, /* 576p50 */ + [edtv] = {0x8c, 0x0a, 0x70, 0x17, 0x5a, 0x03, 0x0d, 0x02},/* 480p60 */ + [vga] = {0x8c, 0x0a, 0x70, 0x17, 0x20, 0x03, 0x0d, 0x02}, +}; + +#if 0 +static u8 avi_info_frame[][14] = { + [hd_720p] = {0x32, 0x0d, 0xa8, 0x84, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00}, + [svga] = {0xd1, 0x0e, 0x08, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}, + [pal] = {0x64, 0x0d, 0x68, 0x84, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}, /* 576p50 */ + [edtv] = {0x73, 0x0d, 0x68, 0x84, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}, + [vga] = {0x5E, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}, +}; +#else +static u8 avi_info_frame[][14] = { + [hd_720p] = {0x43, 0x00, 0x28, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00}, + [svga] = {0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}, + [pal] = {0x46, 0x00, 0x18, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}, /* 576p50 */ + [edtv] = {0x55, 0x00, 0x18, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}, + [vga] = {0x5E, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}, +}; +#endif + +static u8 audio_info_frame[] = + { 0xc2, 0x84, 0x01, 0x0a, 0x71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +#if 0 +int hdmi_active9022(struct i2c_client *client) +{ + int i, ret = -EIO; + struct hdmi_info *info = i2c_get_clientdata(client); + u8 *video_parm = &video_param[info->res][0]; + + HDMI_DBG("%s+\n", __func__); + mutex_lock(&info->polling_lock); + mutex_lock(&info->lock); + + //hdcp_off(info); + DisableTMDS(info); + msleep(128); + + /* choose video mode */ + ret = i2c_smbus_write_i2c_block_data(client, 0, 8, video_parm); + + /* wakeup Sil9022 to D0 state */ + ret = hdmi_write_byte(client, HDMI_POWER, 0); + + if (edid_check_sink_type(info)) { + /* HDMI Output */ + ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL, + OUTPUT_MODE_MASK, OUTPUT_MODE_HDMI); + /* audio configuration */ + ret = hdmi_write_byte(client, 0x26, 0x91); + ret = hdmi_write_byte(client, 0x25, 0x03); + ret = hdmi_write_byte(client, 0x27, 0x59); + ret = hdmi_write_byte(client, 0x28, 0x00); + ret = hdmi_write_byte(client, 0x1f, 0x80); + ret = hdmi_write_byte(client, 0x20, 0x90); + ret = hdmi_write_byte(client, 0x21, 0x00); + //ret = hdmi_write_byte(client, 0x24, 0x00);//0x00 for 44.1k + ret = hdmi_write_byte(client, 0x24, 0x02);//0x02 for 48k + ret = hdmi_write_byte(client, 0x25, 0x00); + ret = hdmi_write_byte(client, 0xbc, 0x02); + ret = hdmi_write_byte(client, 0xbd, 0x24); + ret = hdmi_write_byte(client, 0xbe, 0x92); + ret = hdmi_write_byte(client, 0xbc, 0x02); + ret = hdmi_write_byte(client, 0xbd, 0x2f); + ret = hdmi_write_byte(client, 0xbe, 0); + ret = hdmi_write_byte(client, 0x26, 0x81); + } else { + ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL, + OUTPUT_MODE_MASK, OUTPUT_MODE_DVI); + SetAudioMute(info, AUDIO_MUTE_MUTED); + } + ret = hdmi_write_byte(client, HDMI_PIXEL_DATA, 0x60); + + ret = i2c_smbus_write_i2c_block_data(client, HDMI_AUDIO_INFO_FRAME, + 15, audio_info_frame); + hdmi_write_byte(client, 0x09, 0); + hdmi_write_byte(client, 0x0a, 0); + for (i = 0; i < 14 ;i++) + hdmi_write_byte(client, 0xc + i, avi_info_frame[info->res][i]); + + EnableTMDS(info); + HDMI_DBG("%s-\n", __func__); + mutex_unlock(&info->lock); + mutex_unlock(&info->polling_lock); + + return ret; +} +#endif + +int avc_set_video_parm(struct hdmi_info *hdmi) +{ + int ret; + u8 *video_parm = video_param[hdmi->res]; + + ret = i2c_smbus_write_i2c_block_data(hdmi->client, 0, 8, video_parm); + + return ret; +} + +int avc_set_blank_screen(struct hdmi_info *hdmi) +{ + HDMI_DBG("%s+\n", __func__); + hdmi_write_byte(hdmi->client, 0x09, 0x03); + hdmi_write_byte(hdmi->client, 0x19, 0x00); + hdmi_write_byte(hdmi->client, 0x26, + hdmi_read(hdmi->client, 0x26) | 0x10); +} + +int hdmi_active9022_dup(struct i2c_client *client) +{ + int i, ret = -EIO; + struct hdmi_info *info = i2c_get_clientdata(client); + u8 *video_parm = &video_param[info->res][0]; + + HDMI_DBG("%s+\n", __func__); + + //hdcp_off(info); + DisableTMDS(info); + msleep(128); + + /* choose video mode */ + ret = i2c_smbus_write_i2c_block_data(client, 0, 8, video_parm); + + /* wakeup Sil9022 to D0 state */ + ret = hdmi_write_byte(client, HDMI_POWER, 0); + + if (edid_check_sink_type(info)) { + /* HDMI Output */ + ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL, + OUTPUT_MODE_MASK, OUTPUT_MODE_HDMI); + /* audio configuration */ + ret = hdmi_write_byte(client, 0x26, 0x91); + ret = hdmi_write_byte(client, 0x25, 0x03); + ret = hdmi_write_byte(client, 0x27, 0x59); + ret = hdmi_write_byte(client, 0x28, 0x00); + ret = hdmi_write_byte(client, 0x1f, 0x80); + ret = hdmi_write_byte(client, 0x20, 0x90); + ret = hdmi_write_byte(client, 0x21, 0x00); + //ret = hdmi_write_byte(client, 0x24, 0x00);//0x00 for 44.1k + ret = hdmi_write_byte(client, 0x24, 0x02);//0x02 for 48k + ret = hdmi_write_byte(client, 0x25, 0x00); + ret = hdmi_write_byte(client, 0xbc, 0x02); + ret = hdmi_write_byte(client, 0xbd, 0x24); + ret = hdmi_write_byte(client, 0xbe, 0x92); + ret = hdmi_write_byte(client, 0xbc, 0x02); + ret = hdmi_write_byte(client, 0xbd, 0x2f); + ret = hdmi_write_byte(client, 0xbe, 0); + ret = hdmi_write_byte(client, 0x26, 0x81); + } else { + ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL, + OUTPUT_MODE_MASK, OUTPUT_MODE_DVI); + SetAudioMute(info, AUDIO_MUTE_MUTED); + } + ret = hdmi_write_byte(client, HDMI_PIXEL_DATA, 0x60); + + ret = i2c_smbus_write_i2c_block_data(client, HDMI_AUDIO_INFO_FRAME, + 15, audio_info_frame); + hdmi_write_byte(client, 0x09, 0x03); + hdmi_write_byte(client, 0x0a, 0); + for (i = 0; i < 14 ;i++) + hdmi_write_byte(client, 0xc + i, avi_info_frame[info->res][i]); + + EnableTMDS(info); + + HDMI_DBG("%s-\n", __func__); + return ret; +} + +bool avc_send_avi_info_frames(struct hdmi_info *hdmi) +{ + int i; + + HDMI_DBG("%s res=%d\n", __func__, hdmi->res); + for (i = 0; i < 14 ;i++) + hdmi_write_byte(hdmi->client, 0xc + i, + avi_info_frame[hdmi->res][i]); + + return true; +} + +#if 0 +/* FIXME: intergrate with active9022 */ +bool InitVideo(struct hdmi_info *hdmi, u8 Mode, u8 TclkSel, bool Init) +{ + int Pattern, ret; + u8 *video_parm = &video_param[hdmi->res][0]; + + /* Use TPI 0x08[7:6] for 9022A/24A video clock multiplier */ + Pattern = (1 << 6) & 0xc0; + ReadSetWriteTPI(hdmi, TPI_PIX_REPETITION, Pattern); + + ret = i2c_smbus_write_block_data(hdmi->client, 0, 8, video_parm); + + /* input format */ + hdmi_write_byte(hdmi->client, 0x09, 0); + hdmi_write_byte(hdmi->client, 0x0a, 0); + if(Init) { + hdmi_write_byte(hdmi->client, 0x08, 0x60); + hdmi_write_byte(hdmi->client, 0x09, 0); + hdmi_write_byte(hdmi->client, 0x0a, 0); + hdmi_write_byte(hdmi->client, 0x60, 0x4); + } + + // termination ??? + //ret = (read_back_door_register(0x1, 0x82) & 0x3f ) | 0x25; + //write_back_door_register(); +#if 0 + for (i = 0; i < 14 ;i++) { + hdmi_write_byte(hdmi->client, 0xc + i, avi_info_frame[info->res][i]); + } +#endif + + return true; +} +#endif + +bool avc_init_video(struct hdmi_info *hdmi, u8 mode, u8 TclkSel, bool Init) +{ + int ret; + u8 *video_parm = &video_param[hdmi->res][0]; + + HDMI_DBG("%s\n", __func__); + /* Use TPI 0x08[7:6] for 9022A/24A video clock multiplier */ + hdmi_write_byte(hdmi->client, HDMI_PIXEL_DATA, 0x60); + + ret = i2c_smbus_write_i2c_block_data(hdmi->client, 0, 8, video_parm); + + /* input format */ + hdmi_write_byte(hdmi->client, TPI_INPUT_FORMAT_REG, 0x00); + hdmi_write_byte(hdmi->client, TPI_OUTPUT_FORMAT_REG, 0x00); +#if 0 + if (Init) { + hdmi_write_byte(hdmi->client, 0x08, 0x60); + hdmi_write_byte(hdmi->client, 0x09, 0); + hdmi_write_byte(hdmi->client, 0x0a, 0); + /* Default to External Sync mode + disable VSync adjustment */ + hdmi_write_byte(hdmi->client, 0x60, 0x4); + /* Disable DE generator by default */ + hdmi_write_byte(hdmi->client, 0x63, 0x0); + } + +#endif + ret = (tpi_read_backdoor_register(hdmi, INTERNAL_PAGE_1, TMDS_CONT_REG) + & 0x3f ) | 0x25; + tpi_write_backdoor_register(hdmi, INTERNAL_PAGE_1, TMDS_CONT_REG, ret); + + return true; +} + +void avc_set_basic_audio(struct hdmi_info *hdmi) +{ + int ret; + struct i2c_client *client = hdmi->client; + HDMI_DBG("%s\n", __func__); + + ret = hdmi_write_byte(client, 0x26, 0x91); + ret = hdmi_write_byte(client, 0x25, 0x03); + ret = hdmi_write_byte(client, 0x27, 0x59); + ret = hdmi_write_byte(client, 0x28, 0x00); + ret = hdmi_write_byte(client, 0x1f, 0x80); + ret = hdmi_write_byte(client, 0x20, 0x90); + ret = hdmi_write_byte(client, 0x21, 0x00); + //ret = hdmi_write_byte(client, 0x24, 0x00);//0x00 for 44.1k + ret = hdmi_write_byte(client, 0x24, 0x02);//0x02 for 48k + ret = hdmi_write_byte(client, 0x25, 0x00); + ret = hdmi_write_byte(client, 0xbc, 0x02); + ret = hdmi_write_byte(client, 0xbd, 0x24); + ret = hdmi_write_byte(client, 0xbe, 0x92); + ret = hdmi_write_byte(client, 0xbc, 0x02); + ret = hdmi_write_byte(client, 0xbd, 0x2f); + ret = hdmi_write_byte(client, 0xbe, 0); + ret = hdmi_write_byte(client, 0x26, 0x81); + + ret = i2c_smbus_write_i2c_block_data(client, HDMI_AUDIO_INFO_FRAME, + 15, audio_info_frame); +} + +/* simplifier version of ChangeVideoMode() */ +u8 avc_change_video_mode(struct hdmi_info *hdmi, int *resolution) +{ + HDMI_DBG("%s\n", __func__); + + hdcp_off(hdmi); + DisableTMDS(hdmi); + /* allow control InfoFrames to pass through to the sink device. */ + mdelay(128); + + // FIXME: video mode + avc_init_video(hdmi, vid_mode, 0, 0); + + hdmi_write_byte(hdmi->client, TPI_PIX_REPETITION, 0x60); + hdmi_write_byte(hdmi->client, TPI_INPUT_FORMAT, 0); + + if (edid_check_sink_type(hdmi)) + ReadSetWriteTPI(hdmi, 0x1a, 0x01); + else + ReadSetWriteTPI(hdmi, 0x1a, 0x00); + + /* FIXME: 720p/480p ?*/ + hdmi_write_byte(hdmi->client, TPI_OUTPUT_FORMAT, 0); + + /* set 0x60[7] = 0 for External Sync */ + ReadClearWriteTPI(hdmi, TPI_SYNC_GEN_CTRL, 0x80); + /* clear 0x63[6] = 0 to disable internal DE */ + ReadClearWriteTPI(hdmi, 0x63, 1 << 6); + + /* InfoFrames - only if output mode is HDMI */ + if (edid_check_sink_type(hdmi)) + avc_send_avi_info_frames(hdmi); + + /* SETTING UP AVI InfoFrames CLEARS 0x63 and 0x60[5] */ + hdmi_write_byte(hdmi->client, TPI_SYNC_GEN_CTRL, 1 << 2); + /* SETTING UP AVI InfoFrames CLEARS 0x63 */ + hdmi_write_byte(hdmi->client, 0x63, 0); + + /* YC Input Mode Select */ + hdmi_write_byte(hdmi->client, 0x0b, 0); // 0x0b + EnableTMDS(hdmi); + + return VIDEO_MODE_SET_OK; +} diff --git a/drivers/video/msm/hdmi/silicon-image/debug-sil902x.c b/drivers/video/msm/hdmi/silicon-image/debug-sil902x.c new file mode 100644 index 0000000000000..1c61b30546993 --- /dev/null +++ b/drivers/video/msm/hdmi/silicon-image/debug-sil902x.c @@ -0,0 +1,109 @@ +#include +#include + +#include +#include + +#include "../include/fb-hdmi.h" +#include "../include/sil902x.h" + +//#define HDMI_DEBUGFS + +#if defined(HDMI_DEBUGFS) +static spinlock_t hdmi_dbgfs_lock; +ssize_t hdmi_dbgfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t hdmi_dbgfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + static char line[80], buffer[80*8*4]; + static char hextab[] = "0123456789abcdefg"; + int i, j, n = 0, v, len, offset, line_size; + unsigned long irq_flags; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + len = ((int)hdmi->edid_buf[0x7e]+1) * 128; + spin_lock_irqsave(&hdmi_dbgfs_lock, irq_flags); + memset(line, ' ', 79); + line[79] = '\0'; + offset = strlen("0000 | "); + line_size = offset + 3 * 16 + 1; + + for (i = 0; i < len / 16 ; i++) { + scnprintf(line, offset + 1, "%04x | ", (i << 4)); + for (j = 0; j < 16 ; j++) { + v = hdmi->edid_buf[i * 16 + j]; + line[offset + j * 3] = hextab[v / 16]; + line[offset + j * 3 + 1] = hextab[v % 16]; + } + line[line_size - 1] = '\n'; + strncpy(buffer + i * line_size, line, line_size); + n += line_size; + } + spin_unlock_irqrestore(&hdmi_dbgfs_lock, irq_flags); + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +#if 0 +static ssize_t hdmi_dbgfs_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned long v; + unsigned long irq_flags; + char buff[80]; + struct tv_reg_data *trd = (struct tv_reg_data *)filp->private_data; + + if (count >= sizeof(buff)) + return -EINVAL; + if (copy_from_user(&buff, buf, 80)) + return -EFAULT; + buff[count] = 0; + +#if 0 + spin_lock_irqsave(&hdmi_dbgfs_lock, irq_flags); + strict_strtoul(buff, 16, &v); + buff[strlen(buff)]=0; + writel(v, tvenc_base+trd->offset); + spin_unlock_irqrestore(&hdmi_dbgfs_lock, irq_flags); +#endif + + return count; +} +#endif + +static struct file_operations hdmi_fops[] = { + { + .open = hdmi_dbgfs_open, + .read = hdmi_dbgfs_read, + } +}; + +int hdmi_debugfs_init(struct hdmi_info *hdmi) +{ + HDMI_DBG("%s\n", __func__); + spin_lock_init(&hdmi_dbgfs_lock); + hdmi->debug_dir = debugfs_create_dir(HDMI_DEBUGFS_ROOT, 0); + if (IS_ERR(hdmi->debug_dir)) + return PTR_ERR(hdmi->debug_dir); + + // FIXME: error handling + debugfs_create_file("dummy", 0644, hdmi->debug_dir, hdmi, + &hdmi_fops[0]); + edid_debugfs_init(hdmi); + tpi_debugfs_init(hdmi); + hdcp_debugfs_init(hdmi); +/* + int ret; + if (!ret) { + pr_err("%s: failure on debugfs_create_file()\n", __func__); + return -1; + } +*/ + + return 0; +} +#endif diff --git a/drivers/video/msm/hdmi/silicon-image/hdcp.c b/drivers/video/msm/hdmi/silicon-image/hdcp.c new file mode 100644 index 0000000000000..474a84c56a332 --- /dev/null +++ b/drivers/video/msm/hdmi/silicon-image/hdcp.c @@ -0,0 +1,354 @@ +#include +#include +#include +#include + +#include "../include/fb-hdmi.h" +#include "../include/sil902x.h" +#include "../include/tpi.h" + +#if 1 +#define HDCP_DBG(fmt, arg...) printk( "[hdmi/hdcp]%s: " fmt, __func__, ##arg) +#else +#define HDCP_DBG(fmt...) do {} while (0) +#endif + +#define hdcp_err(fmt, arg...) pr_err( "[hdmi/hdcp]%s: " fmt, __func__, ##arg) + +//#define HDCP_DEBUG /* Remove this definition when releasing!!! */ +#if defined(HDCP_DEBUG) +#define HDCP_OVERWRITE_CONTROL 0x1 +#define HDCP_SUPPORT 0x2 +#define HDCP_AVALABLE 0x4 +static int hdcp_control = 0x7; +module_param_named(hdcp_control, hdcp_control, int, + S_IRUGO | S_IWUSR | S_IWGRP); +#endif + +bool HDCP_TxSupports; +bool HDCP_Started; +u8 HDCP_LinkProtectionLevel; + +////////////////////////////////////////////////////////////////////////////// +// FUNCTION : IsHDCP_Supported() +// PURPOSE : Check Tx revision number to find if this Tx supports HDCP +// by reading the HDCP revision number from TPI register 0x30. +// RETURNS : true if Tx supports HDCP. false if not. +////////////////////////////////////////////////////////////////////////////// +bool hdcp_check_support(struct hdmi_info *hdmi) +{ + u8 HDCP_Rev; + bool HDCP_Supported; + + HDCP_Supported = true; + /* Check Device ID */ + HDCP_Rev = hdmi_read(hdmi->client, TPI_HDCP_REVISION_DATA_REG); + if (HDCP_Rev != + (HDCP_MAJOR_REVISION_VALUE | HDCP_MINOR_REVISION_VALUE)) + HDCP_Supported = false; + + HDCP_DBG("ret=%d\n", HDCP_Supported); + return HDCP_Supported; +} + +////////////////////////////////////////////////////////////////////////////// +// FUNCTION : AreAKSV_OK() +// PURPOSE : Check if AKSVs contain 20 '0' and 20 '1' +// INPUT PARAMS : None +// OUTPUT PARAMS : None +// GLOBALS USED : TBD +// RETURNS : true if 20 zeros and 20 ones found in AKSV. false OTHERWISE +////////////////////////////////////////////////////////////////////////////// +static bool hdcp_check_aksv(struct hdmi_info *hdmi) +{ + int ret; + u8 B_Data[AKSV_SIZE]; + u8 i, j, NumOfOnes = 0; + + memset(B_Data, 0, AKSV_SIZE); +#if 0 + ReadBlockTPI(hdmi, TPI_AKSV_1_REG, AKSV_SIZE, B_Data); +#else + for (i = 0; i < 5; i++) { + B_Data[i] = hdmi_read(hdmi->client, TPI_AKSV_1_REG+i); + } +#endif + HDCP_DBG(" askv={%02x, %02x, %02x, %02x, %02x}\n", + B_Data[0], B_Data[1], B_Data[2], B_Data[3], B_Data[4]); + for (i=0; i < AKSV_SIZE; i++) + for (j=0; j < BYTE_SIZE; j++) { + if (B_Data[i] & 0x01) + NumOfOnes++; + B_Data[i] >>= 1; + } + if (NumOfOnes != NUM_OF_ONES_IN_KSV) + ret = false; + else ret = true; + + HDCP_DBG(":ret=%s\n", ret ? "true" : "false"); + + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// FUNCTION : HDCP_On() +// PURPOSE : Switch hdcp on. +// INPUT PARAMS : None +// OUTPUT PARAMS: None +// GLOBALS USED : HDCP_Started set to true +// RETURNS : None +////////////////////////////////////////////////////////////////////////////// +//void hdcp_on(struct hdmi_info *hdmi) +void hdcp_on(struct hdmi_info *hdmi, const char *caller) +{ + HDCP_DBG(", caller=%s\n", caller); + hdmi_write_byte(hdmi->client, TPI_HDCP_CONTROL_DATA_REG, PROTECTION_LEVEL_MAX); + HDCP_Started = true; +} + +////////////////////////////////////////////////////////////////////////////// +// FUNCTION : HDCP_Off() +// PURPOSE : Switch hdcp off. +// GLOBALS USED : HDCP_Started set to false +// RETURNS : None +////////////////////////////////////////////////////////////////////////////// +void hdcp_off(struct hdmi_info *hdmi) +{ + HDCP_DBG("\n"); + + SetInputColorSpace(hdmi, INPUT_COLOR_SPACE_BLACK_MODE); + SetAudioMute(hdmi, AUDIO_MUTE_MUTED); + hdmi_write_byte(hdmi->client, TPI_HDCP_CONTROL_DATA_REG, PROTECTION_LEVEL_MIN); + HDCP_Started = false; + HDCP_LinkProtectionLevel = EXTENDED_LINK_PROTECTION_NONE | LOCAL_LINK_PROTECTION_NONE; +} + +void hdcp_init(struct hdmi_info *hdmi) +{ + HDCP_DBG("\n"); + + HDCP_TxSupports = false; + HDCP_Started = false; + HDCP_LinkProtectionLevel = EXTENDED_LINK_PROTECTION_NONE | LOCAL_LINK_PROTECTION_NONE; + + /* TX-related... need only be done once. */ + if (!hdcp_check_support(hdmi)) { + hdcp_err("TX does not support HDCP\n"); + return; + } + if (!hdcp_check_aksv(hdmi)) { + hdcp_err("Illegal AKSV\n"); + return; + } + HDCP_TxSupports = true; +} + +void hdcp_restart(struct hdmi_info *hdmi) +{ + HDCP_DBG("\n"); + DisableTMDS(hdmi); + hdcp_off(hdmi); + EnableTMDS(hdmi); +} + +void hdcp_check_status(struct hdmi_info *hdmi, u8 InterruptStatusImage) +{ + u8 QueryData, LinkStatus, RegImage, NewLinkProtectionLevel; + + if (HDCP_TxSupports == false) + return; + + if ((HDCP_LinkProtectionLevel == + (EXTENDED_LINK_PROTECTION_NONE | LOCAL_LINK_PROTECTION_NONE)) && + (HDCP_Started == false)) { + QueryData = hdmi_read(hdmi->client, TPI_HDCP_QUERY_DATA_REG); + /* Is HDCP avaialable */ + if (QueryData & PROTECTION_TYPE_MASK) { + hdcp_on(hdmi, __func__); + } + } + /* Check if Link Status has changed: */ + if (InterruptStatusImage & SECURITY_CHANGE_EVENT) { + HDCP_DBG("SECURITY_CHANGE_EVENT\n"); + LinkStatus = hdmi_read(hdmi->client, TPI_HDCP_QUERY_DATA_REG); + LinkStatus &= LINK_STATUS_MASK; + tpi_clear_interrupt(hdmi, SECURITY_CHANGE_EVENT); + switch (LinkStatus) { + case LINK_STATUS_NORMAL: + HDCP_DBG("Link = Normal\n"); + break; + case LINK_STATUS_LINK_LOST: + HDCP_DBG("Link = Lost\n"); + hdcp_restart(hdmi); + break; + case LINK_STATUS_RENEGOTIATION_REQ: + HDCP_DBG("Link = Renegotiation Required\n"); + hdcp_off(hdmi); + hdcp_on(hdmi, __func__); + break; + case LINK_STATUS_LINK_SUSPENDED: + HDCP_DBG("Link = Suspended\n"); + hdcp_on(hdmi, __func__); + break; + } + } + /* Check if HDCP state has changed: */ + if (InterruptStatusImage & HDCP_CHANGE_EVENT) { + HDCP_DBG("HDCP_CHANGE_EVENT\n"); + RegImage = hdmi_read(hdmi->client, TPI_HDCP_QUERY_DATA_REG); + NewLinkProtectionLevel = RegImage & + (EXTENDED_LINK_PROTECTION_MASK | LOCAL_LINK_PROTECTION_MASK); + if (NewLinkProtectionLevel != HDCP_LinkProtectionLevel) { + HDCP_LinkProtectionLevel = NewLinkProtectionLevel; + switch (HDCP_LinkProtectionLevel) { + case (EXTENDED_LINK_PROTECTION_NONE | LOCAL_LINK_PROTECTION_NONE): + HDCP_DBG("Protection = None\n"); + hdcp_restart(hdmi); + break; + case LOCAL_LINK_PROTECTION_SECURE: + SetAudioMute(hdmi, AUDIO_MUTE_NORMAL); + //SetInputColorSpace (hdmi, INPUT_COLOR_SPACE_YCBCR422); + SetInputColorSpace (hdmi, INPUT_COLOR_SPACE_RGB); + HDCP_DBG("Protection = Local, Video Unmuted\n"); + break; + case (EXTENDED_LINK_PROTECTION_SECURE | LOCAL_LINK_PROTECTION_SECURE): + HDCP_DBG("Protection = Extended\n"); + break; + default: + HDCP_DBG("Protection = Extended but not Local?\n"); + hdcp_restart(hdmi); + break; + } + } + tpi_clear_interrupt(hdmi, HDCP_CHANGE_EVENT); + } +} + +/*----------------------------------------------------------------------------*/ +#if defined(HDMI_DEBUGFS) +static ssize_t hdcp_supported_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + int n=0; + char buffer[80]; + //struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + HDMI_DBG("%s\n", __func__); + //n = scnprintf(buffer, 80, "%d\n", is_hdcp_supported(hdmi)); + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static ssize_t hdcp_available_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + int n=0; + char buffer[80]; + //struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + HDMI_DBG("%s\n", __func__); + //n = scnprintf(buffer, 80, "%d\n", is_hdcp_available(hdmi)); + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static ssize_t hdcp_on_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + hdcp_on(hdmi, __func__); + return 0; +} + +static ssize_t hdcp_off_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + hdcp_off(hdmi); + return 0; +} + +static ssize_t hdcp_restart_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + hdcp_restart(hdmi); + return 0; +} + +static ssize_t hdcp_handle_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + //struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + //handle_hdcp(hdmi); + return 0; +} + +static ssize_t hdcp_poll_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + //struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + //hdcp_poll(hdmi); + return 0; +} + +static ssize_t hdcp_aksv_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + hdcp_check_aksv(hdmi); + return 0; +} + +static ssize_t hdcp_bksv_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + //struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + //hdcp_check_bksv(hdmi); + return 0; +} + +static struct file_operations hdcp_debugfs_fops[] = { + { .open = hdmi_dbgfs_open, .read = hdcp_supported_read, }, + { .open = hdmi_dbgfs_open, .read = hdcp_available_read, }, + { .open = hdmi_dbgfs_open, .read = hdcp_on_read, }, + { .open = hdmi_dbgfs_open, .read = hdcp_off_read, }, + { .open = hdmi_dbgfs_open, .read = hdcp_restart_read, }, + { .open = hdmi_dbgfs_open, .read = hdcp_poll_read, }, + { .open = hdmi_dbgfs_open, .read = hdcp_aksv_read, }, + { .open = hdmi_dbgfs_open, .read = hdcp_bksv_read, }, +}; + +// mutex ? +int hdcp_debugfs_init(struct hdmi_info *hdmi) +{ + struct dentry *hdcp_dent; + + hdcp_dent = debugfs_create_dir("hdcp", hdmi->debug_dir); + if (IS_ERR(hdcp_dent)) + return PTR_ERR(hdcp_dent); + + //FIXME: error handling + debugfs_create_file("supported", 0444, hdcp_dent, hdmi, + &hdcp_debugfs_fops[0]); + debugfs_create_file("available", 0444, hdcp_dent, hdmi, + &hdcp_debugfs_fops[1]); + debugfs_create_file("on", 0444, hdcp_dent, hdmi, + &hdcp_debugfs_fops[2]); + debugfs_create_file("off", 0444, hdcp_dent, hdmi, + &hdcp_debugfs_fops[3]); + debugfs_create_file("restart", 0444, hdcp_dent, hdmi, + &hdcp_debugfs_fops[4]); + debugfs_create_file("poll", 0444, hdcp_dent, hdmi, + &hdcp_debugfs_fops[5]); + debugfs_create_file("aksv", 0444, hdcp_dent, hdmi, + &hdcp_debugfs_fops[6]); + debugfs_create_file("bksv", 0444, hdcp_dent, hdmi, + &hdcp_debugfs_fops[7]); + + return 0; +} +#endif + diff --git a/drivers/video/msm/hdmi/silicon-image/tpi.c b/drivers/video/msm/hdmi/silicon-image/tpi.c new file mode 100644 index 0000000000000..c8d3cf3f982c3 --- /dev/null +++ b/drivers/video/msm/hdmi/silicon-image/tpi.c @@ -0,0 +1,960 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// FIXME: remove this if unnecessary in the future +#ifdef CONFIG_HTC_HEADSET_MGR +#include +#endif + +#if 1 +#define HDMI_DBG(s...) printk("[hdmi/tpi]" s) +#else +#define HDMI_DBG(s...) do {} while (0) +#endif + +#include "../include/fb-hdmi.h" +#include "../include/sil902x.h" +#include "../include/tpi.h" + +#define NEW_INTEGRATE +#define DBG_POLLING 0x1 +static int debug_mask; +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define DLOG(mask, fmt, args...) \ +do { \ + if (debug_mask & mask) \ + printk(KERN_INFO "[hdmi/sil]: "fmt, ##args); \ +} while (0) + +#define X1 0x01 +#define AFTER_INIT 1 + +void HotPlugService (struct hdmi_info *hdmi); +// FIXME: should be decide by detection +static bool dsRxPoweredUp; +static bool edidDataValid; +static bool tmdsPoweredUp; +u8 pvid_mode, vid_mode = 16; +u8 systemInitialized; + +void tpi_clear_interrupt(struct hdmi_info *hdmi, u8 pattern) +{ + /* write "1" to clear interrupt bit, and 0 won't effect origin value. */ + hdmi_write_byte(hdmi->client, TPI_INTERRUPT_STATUS_REG, pattern); +} + +////////////////////////////////////////////////////////////////////////////// +// FUNCTION : EnableInterrupts() +// PURPOSE : Enable the interrupts specified in the input parameter +// INPUT PARAMS : A bit pattern with "1" for each interrupt that needs to be +// set in the Interrupt Enable Register (TPI offset 0x3C) +// OUTPUT PARAMS : void +// GLOBALS USED : None +// RETURNS : TRUE +////////////////////////////////////////////////////////////////////////////// +bool tpi_enable_interrupts(struct hdmi_info *hdmi, u8 Interrupt_Pattern) +{ + HDMI_DBG("%s, reg=%02x, pat=%02x\n", __func__, TPI_INTERRUPT_EN, Interrupt_Pattern); + ReadSetWriteTPI(hdmi, TPI_INTERRUPT_EN, Interrupt_Pattern); + return true; +} + +static void tpi_disable_interrupts(struct hdmi_info *hdmi, u8 pattern) +{ +/* + HDMI_DBG("%s, reg=%02x, pat=%02x\n", __func__, + TPI_INTERRUPT_EN, pattern); +*/ + ReadClearWriteTPI(hdmi, TPI_INTERRUPT_EN, pattern); +} + +static void tpi_clear_pending_event(struct hdmi_info *hdmi) +{ + int retry = 100; + + if (hdmi->sleeping == SLEEP) return; + while (retry--) { + hdmi_write_byte(hdmi->client, 0x3c, 1); + hdmi_write_byte(hdmi->client, 0x3d, 1); + if (hdmi_read(hdmi->client, 0x3d) & 0x01) + msleep(1); + else + break; + } + if (retry < 19) HDMI_DBG("%s: retry=%d\n", __func__, 19 - retry); +} + +////////////////////////////////////////////////////////////////////////////// +// FUNCTION : ReadBackDoorRegister() +// PURPOSE : Read a 922x register value from a backdoor register +// Write: +// 1. 0xBC => Internal page num +// 2. 0xBD => Backdoor register offset +// Read: +// 3. 0xBE => Returns the backdoor register value +// INPUT PARAMS : Internal page number, backdoor register offset, pointer to +// buffer to store read value +// OUTPUT PARAMS: Buffer that stores the read value +// RETURNS : TRUE +// NOTE : This workaround is needed for the 9220/2 only. +////////////////////////////////////////////////////////////////////////////// +int tpi_read_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset) +{ + // FIXME: error handling + struct i2c_client *client = hdmi->client; + + /* Internal page */ + hdmi_write_byte(client, TPI_INTERNAL_PAGE_REG, PageNum); + /* Indexed register */ + hdmi_write_byte(client, TPI_REGISTER_OFFSET_REG, RegOffset); + /* Read value into buffer */ + return hdmi_read(client, TPI_REGISTER_VALUE_REG); +} + +void tpi_write_backdoor_register(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset, u8 RegValue) { + /* Internal page */ + hdmi_write_byte(hdmi->client, TPI_INTERNAL_PAGE_REG, PageNum); + /* Indexed register */ + hdmi_write_byte(hdmi->client, TPI_REGISTER_OFFSET_REG, RegOffset); + /* Read value into buffer */ + hdmi_write_byte(hdmi->client, TPI_REGISTER_VALUE_REG, RegValue); +} + +#define TPI_INTERNAL_PAGE_REG 0xBC +#define TPI_INDEXED_OFFSET_REG 0xBD +#define TPI_INDEXED_VALUE_REG 0xBE +#define INDEXED_PAGE_0 0x01 +#define INDEXED_PAGE_1 0x02 +#define INDEXED_PAGE_2 0x03 + +void ReadModifyWriteIndexedRegister(struct hdmi_info *hdmi, u8 PageNum, u8 RegOffset, u8 Mask, u8 Value) +{ + u8 Tmp; + + hdmi_write_byte(hdmi->client, TPI_INTERNAL_PAGE_REG, PageNum); + hdmi_write_byte(hdmi->client, TPI_INDEXED_OFFSET_REG, RegOffset); + Tmp = hdmi_read(hdmi->client, TPI_INDEXED_VALUE_REG); + + Tmp &= ~Mask; + Tmp |= (Value & Mask); + + hdmi_write_byte(hdmi->client, TPI_INDEXED_VALUE_REG, Tmp); +} + +void ReadSetWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Pattern) +{ + u8 Tmp; + struct i2c_client *client = hdmi->client; + + Tmp = hdmi_read(client, Offset); + Tmp |= Pattern; + hdmi_write_byte(client, Offset, Tmp); +} + +int tpi_set_bit(struct hdmi_info *hdmi, u8 reg, u8 pattern) +{ + return hdmi_write_byte(hdmi->client, reg, + hdmi_read(hdmi->client, reg) | pattern); +} +//// +void ReadModifyWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Mask, u8 Value) +{ + u8 Tmp; + struct i2c_client *client = hdmi->client; + + Tmp = hdmi_read(client, Offset); + Tmp &= ~Mask; + Tmp |= (Value & Mask); + hdmi_write_byte(client, Offset, Tmp); +} +//// +void ReadClearWriteTPI(struct hdmi_info *hdmi, u8 Offset, u8 Pattern) +{ + u8 Tmp; + + Tmp = hdmi_read(hdmi->client, Offset); + Tmp &= ~Pattern; + hdmi_write_byte(hdmi->client, Offset, Tmp); +} +void tpi_clear_bit(struct hdmi_info *hdmi, u8 reg, u8 pattern) +{ + hdmi_write_byte(hdmi->client, reg, + hdmi_read(hdmi->client, reg) & pattern); +} +//// + +/* Caller: ChangeVideoMode(), HDCP_Poll(), HotPlugServiceLoop(), RestartHDCP() + */ + +void EnableTMDS(struct hdmi_info *hdmi) +{ + u8 val; +#if 1 + /* 0x1A[4] = 0 */ + ReadClearWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT); + + if (edid_check_sink_type(hdmi)) + hdmi_write_byte(hdmi->client, 0x26, + hdmi_read(hdmi->client, 0x26) & ~0x10); + +#else + struct i2c_client *client = hdmi->i2c_client; + + val = hdmi_read(client, TPI_SYSTEM_CONTROL); + hdmi_write_byte(client, TPI_SYSTEM_CONTROL, val & ~BIT_TMDS_OUTPUT); + HDMI_DBG("%s, reg 0x1a: %02x->%02x\n", __func__, + val, val & ~BIT_TMDS_OUTPUT); +#endif + +} + +/* Caller: ChangeVideoMode(), HDCP_Poll(), TPI_Poll(), RestartHDCP(), + * OnHdmiCableDisconnected() + */ + +void DisableTMDS(struct hdmi_info *hdmi) +{ + /* 0x1A[4] = 1 */ + //ReadClearWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT); + ReadSetWriteTPI(hdmi, TPI_SYSTEM_CONTROL, BIT_TMDS_OUTPUT); +} +static void OnDownstreamRxPoweredDown(struct hdmi_info *hdmi) +{ + HDMI_DBG("%s\n", __func__); + dsRxPoweredUp = false; + hdcp_off(hdmi); +} + +static void OnDownstreamRxPoweredUp(struct hdmi_info *hdmi) +{ + HDMI_DBG("%s\n", __func__); + dsRxPoweredUp = true; + HotPlugService(hdmi); +#ifdef CONFIG_HTC_HEADSET_MGR + /* send cable in event */ + switch_send_event(BIT_HDMI_CABLE, 1); + HDMI_DBG("Cable inserted.\n"); +#endif + pvid_mode = vid_mode; + hdmi_active9022_dup(hdmi->client); +} + +bool GetDDC_Access(struct hdmi_info *hdmi, u8* SysCtrlRegVal) +{ + u8 sysCtrl, TPI_ControlImage, DDCReqTimeout = T_DDC_ACCESS; + + HDMI_DBG("%s\n", __func__); + /* Read and store original value. Will be passed into ReleaseDDC() */ + sysCtrl = hdmi_read(hdmi->client, TPI_SYSTEM_CONTROL); + *SysCtrlRegVal = sysCtrl; + + sysCtrl |= BIT_DDC_BUS_REQ; + hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, sysCtrl); + + /* Loop till 0x1A[1] reads "1" */ + while (DDCReqTimeout--) { + TPI_ControlImage = hdmi_read(hdmi->client, TPI_SYSTEM_CONTROL); + + /* When 0x1A[1] reads "1" */ + if (TPI_ControlImage & BIT_DDC_BUS_GRANT) { + sysCtrl |= BIT_DDC_BUS_GRANT; + /* lock host DDC bus access (0x1A[2:1] = 11) */ + hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, sysCtrl); + return true; + } + /* 0x1A[2] = "1" - Requst the DDC bus */ + hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, sysCtrl); + mdelay(200); + } + + /* Failure... restore original value. */ + hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, sysCtrl); + return false; +} + +bool ReleaseDDC(struct hdmi_info *hdmi, u8 SysCtrlRegVal) +{ + u8 DDCReqTimeout = T_DDC_ACCESS, TPI_ControlImage; + + HDMI_DBG("%s\n", __func__); + /* Just to be sure bits [2:1] are 0 before it is written */ + SysCtrlRegVal &= ~(0x6); + /* Loop till 0x1A[1] reads "0" */ + while (DDCReqTimeout--) { + /* Cannot use ReadClearWriteTPI() here. A read of + * TPI_SYSTEM_CONTROL is invalid while DDC is granted. + * Doing so will return 0xFF, and cause an invalid value to be + * written back. + */ + /* 0x1A[2:1] = "0" - release the DDC bus */ + //ReadClearWriteTPI(TPI_SYSTEM_CONTROL,BITS_2_1); + + hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, SysCtrlRegVal); + TPI_ControlImage = hdmi_read(hdmi->client, TPI_SYSTEM_CONTROL); + /* When 0x1A[2:1] read "0" */ + if (!(TPI_ControlImage & 0x6)) + return true; + } + + /* Failed to release DDC bus control */ + return false; +} + +int tpi_read_edid(struct hdmi_info *hdmi) +{ + u8 SysCtrlReg; + int ret, edid_blocks = 0; + struct i2c_msg msg; + u8 i2c_buff[2]; + u8 pbuf[] = {1, 0, 1, 128} ; + + struct i2c_msg paging_msg[] = { + { + .addr = 0x30, .flags = 0, .len = 1, .buf = &pbuf[0], + }, + { + .addr = 0x50, .flags = 0, .len = 1, .buf = &pbuf[1], + }, + { //Block-2 + .addr = 0x50, .flags = I2C_M_RD, .len = 128, .buf = &hdmi->edid_buf[256], + }, + { + .addr = 0x30, .flags = 0, .len = 1, .buf = &pbuf[2], + }, + { + .addr = 0x50, .flags = 0, .len = 1, .buf = &pbuf[3], + }, + { //Block-3 + .addr = 0x50, .flags = I2C_M_RD, .len = 128, .buf = &hdmi->edid_buf[384], + }, + }; + + HDMI_DBG("%s\n", __func__); +#if 0 + DisableTMDS(hdmi); +#else + u8 val; + val = hdmi_read(hdmi->client, TPI_SYSTEM_CONTROL); + //hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, val|BIT_4|BIT_6); + hdmi_write_byte(hdmi->client, TPI_SYSTEM_CONTROL, val|BIT_4); +#endif + + if (!GetDDC_Access(hdmi, &SysCtrlReg)) { + pr_err("%s: DDC bus request failed\n", __func__); + return DDC_BUS_REQ_FAILURE; + } + + // Block-0 + memset(hdmi->edid_buf, 0, 512); + + msg.addr = 0x50; + msg.flags = 0; + msg.len = 1; + msg.buf = hdmi->edid_buf; + ret = i2c_transfer(hdmi->client->adapter, &msg, 1); + if (ret < 0) + dev_err(&hdmi->client->dev, "%s: i2c transfer error\n", __func__); + + msg.addr = 0x50; + msg.flags = I2C_M_RD; + msg.len = 128; + msg.buf = hdmi->edid_buf; + ret = i2c_transfer(hdmi->client->adapter, &msg, 1); + if (ret < 0) { + dev_err(&hdmi->client->dev, "%s: i2c transfer error\n", __func__); + goto end_read_edid; + } else { + if (hdmi->edid_buf[0x7e] <= 3) + edid_blocks = hdmi->edid_buf[0x7e] ; + + dev_info(&hdmi->client->dev, "EDID blocks = %d\n", edid_blocks); + + if (edid_blocks == 0 ) { + goto end_read_edid; + } + // Block-1 + msg.addr = 0x50; + msg.flags = 0; + msg.len = 1; + i2c_buff[0] = 128; + msg.buf = i2c_buff; + ret = i2c_transfer(hdmi->client->adapter, &msg, 1); + + msg.addr = 0x50; + msg.flags = I2C_M_RD; + msg.len = 128; + msg.buf = &hdmi->edid_buf[128]; + ret = i2c_transfer(hdmi->client->adapter, &msg, 1); + } + + if (edid_blocks > 1) { + // block 2/3 + i2c_transfer(hdmi->client->adapter, paging_msg, 3); + i2c_transfer(hdmi->client->adapter, &paging_msg[3], 3); + } + +end_read_edid: + if (!ReleaseDDC(hdmi, SysCtrlReg)) { + pr_err("%s: DDC bus release failed\n", __func__); + return DDC_BUS_REQ_FAILURE; + } + + edid_simple_parsing(hdmi); + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// +// FUNCTION : HotPlugService() +// PURPOSE : Implement Hot Plug Service Loop activities +// INPUT PARAMS : None +// OUTPUT PARAMS: void +// GLOBALS USED : LinkProtectionLevel +// RETURNS : An error code that indicates success or cause of failure +////////////////////////////////////////////////////////////////////////////// + +extern bool HDCP_TxSupports; +static bool tmdsPoweredUp; +void HotPlugService (struct hdmi_info *hdmi) +{ + HDMI_DBG("%s\n", __func__); + + mutex_lock(&hdmi->lock); + tpi_disable_interrupts(hdmi, 0xFF); + +/* + // use 1st mode supported by sink + //vid_mode = EDID_Data.VideoDescriptor[0]; + vid_mode = 0; +*/ + avc_init_video(hdmi, vid_mode, X1, AFTER_INIT); + + hdmi_write_byte(hdmi->client, HDMI_POWER, 0); + if (edid_check_sink_type(hdmi)) + avc_send_avi_info_frames(hdmi); + + /* This check needs to be changed to if HDCP is required by the content + once support has been added by RX-side library. */ + if (HDCP_TxSupports == true) { + HDMI_DBG("TMDS -> Enabled\n"); + /* turn on black mode will lost around 3 secs frames thus remove it */ + //SetInputColorSpace(hdmi, INPUT_COLOR_SPACE_BLACK_MODE); +#if 1 + ReadModifyWriteTPI(hdmi, TPI_SYSTEM_CONTROL, + LINK_INTEGRITY_MODE_MASK | TMDS_OUTPUT_CONTROL_MASK, + LINK_INTEGRITY_DYNAMIC | TMDS_OUTPUT_CONTROL_ACTIVE); +#else + ReadModifyWriteTPI(hdmi, TPI_SYSTEM_CONTROL, + LINK_INTEGRITY_MODE_MASK | TMDS_OUTPUT_CONTROL_MASK, + LINK_INTEGRITY_DYNAMIC); +#endif + tmdsPoweredUp = true; + } else { + EnableTMDS(hdmi); + } + + if (edid_check_sink_type(hdmi)) + avc_set_basic_audio(hdmi); + else + SetAudioMute(hdmi, AUDIO_MUTE_MUTED); + + tpi_enable_interrupts(hdmi, HOT_PLUG_EVENT | RX_SENSE_EVENT | + AUDIO_ERROR_EVENT | SECURITY_CHANGE_EVENT | + V_READY_EVENT | HDCP_CHANGE_EVENT); + + //complete(&hdmi->hotplug_completion); + mutex_unlock(&hdmi->lock); +} + +static bool tpi_start(struct hdmi_info *hdmi) +{ + u8 devID = 0x00; + u16 wID = 0x0000; + + hdmi_write_byte(hdmi->client, TPI_ENABLE, 0x00); // Write "0" to 72:C7 to start HW TPI mode + mdelay(100); + + devID = tpi_read_backdoor_register(hdmi, 0x00, 0x03); + wID = devID; + wID <<= 8; + devID = tpi_read_backdoor_register(hdmi, 0x00, 0x02); + wID |= devID; + devID = hdmi_read(hdmi->client, TPI_DEVICE_ID); + HDMI_DBG("%s, ID=%04X\n", __func__, (u32)wID); + + if (devID == SiI_DEVICE_ID) { + return true; + } + + pr_err("%s: Unsupported TX\n", __func__); + return false; +} + +bool tpi_init(struct hdmi_info *hdmi) +{ + tmdsPoweredUp = false; + hdmi->cable_connected = false; + dsRxPoweredUp = false; + edidDataValid = false; + + /* Enable HW TPI mode, check device ID */ + if (tpi_start(hdmi)) { + hdcp_init(hdmi); + return true; + } + return 0; +} + +void SetAudioMute(struct hdmi_info *hdmi, u8 audioMute) +{ + ReadModifyWriteTPI(hdmi, TPI_AUDIO_INTERFACE_REG, AUDIO_MUTE_MASK, audioMute); +} + +void SetInputColorSpace(struct hdmi_info *hdmi, u8 inputColorSpace) +{ + ReadModifyWriteTPI(hdmi, TPI_INPUT_FORMAT_REG, INPUT_COLOR_SPACE_MASK, inputColorSpace); + /* Must be written for previous write to take effect. Just write read value unmodified. */ + ReadModifyWriteTPI(hdmi, TPI_END_RIGHT_BAR_MSB, 0x00, 0x00); +} + +static char edid_hex_buff[2048]; +int lcdc_enable_video(void); +int lcdc_disable_video(void); +void tpi_cable_conn(struct hdmi_info *hdmi) +{ + HDMI_DBG("%s\n", __func__); + + hdmi->cable_connected = true; + tpi_write_backdoor_register(hdmi, INTERNAL_PAGE_0, 0xCE, 0x00); // Clear BStatus + tpi_write_backdoor_register(hdmi, INTERNAL_PAGE_0, 0xCF, 0x00); + +//----------------------------------------------- + hdmi_write_byte(hdmi->client, 0x09, 0x03); + hdmi_write_byte(hdmi->client, 0x19, 0x00); // go to blank mode, avoid screen noise + +/* + HDMI_DBG("solomon: H/V total=%02x, %02x, %02x, %02x\n", + hdmi_read(hdmi->client, 0x6a), + hdmi_read(hdmi->client, 0x6b), + hdmi_read(hdmi->client, 0x6c), + hdmi_read(hdmi->client, 0x6d) + ); +*/ + + lcdc_enable_video(); + msleep(160); +/* + //clk_set_rate(hdmi->ebi1_clk, 120000000); + HDMI_DBG("solomon: H/V total=%02x, %02x, %02x, %02x\n", + hdmi_read(hdmi->client, 0x6a), + hdmi_read(hdmi->client, 0x6b), + hdmi_read(hdmi->client, 0x6c), + hdmi_read(hdmi->client, 0x6d) + ); +*/ + EnableTMDS(hdmi); + +//----------------------------------------------- + + tpi_read_edid(hdmi); + memset(edid_hex_buff, 0, 2048); + edid_dump_hex(hdmi->edid_buf, 256, edid_hex_buff, 2048); + printk("EDID data:\n%s\n=====", edid_hex_buff); + /* select output mode (HDMI/DVI) according to sink capabilty */ + if (edid_check_sink_type(hdmi)) + ReadModifyWriteTPI(hdmi, TPI_SYSTEM_CONTROL, OUTPUT_MODE_MASK, OUTPUT_MODE_HDMI); + else + ReadModifyWriteTPI(hdmi, TPI_SYSTEM_CONTROL, OUTPUT_MODE_MASK, OUTPUT_MODE_DVI); + + hdmi->first = false; +#if 0 +#ifdef CONFIG_HTC_HEADSET_MGR + /* send cable in event */ + switch_send_event(BIT_HDMI_CABLE, 1); + HDMI_DBG("Cable inserted.\n"); +#endif +#endif +} + +void tpi_cable_disconn(struct hdmi_info *hdmi, bool into_d3) +{ + HDMI_DBG("%s, into_d3=%d\n", __func__, into_d3); + + hdmi->cable_connected = false; + dsRxPoweredUp = false; + edidDataValid = false; + hdcp_off(hdmi); + DisableTMDS(hdmi); +#if 1 + /* wait for debounce */ + msleep(20); + tpi_clear_pending_event(hdmi); +#else + reg = hdmi_read(hdmi->client, 0x3d); + if (!(reg & 0x0c)) + tpi_clear_pending_event(hdmi); +#endif + if (into_d3) { + mutex_lock(&hdmi->lock); + HDMI_DBG("%s, playing=%d\n", __func__, hdmi->user_playing); + if (false == hdmi->user_playing) + lcdc_disable_video(); + clk_set_rate(hdmi->ebi1_clk, 0); + hdmi_standby(hdmi); + hdmi->power(2); + memset(hdmi->edid_buf, 0, 512); + mutex_unlock(&hdmi->lock); + } +#ifdef CONFIG_HTC_HEADSET_MGR + HDMI_DBG("Cable unplugged.\n"); + switch_send_event(BIT_HDMI_CABLE, 0); +#endif +} + +static char *str_debug_interrupt[] = { + "HOT_PLUG_EVENT\t\t\t", + "RECEIVER_SENSE_EVENT\t\t", + "HOT_PLUG_PIN_STATE\t\t", + "RX_SENSE_MASK\t\t\t", + "AUDIO_ERROR_EVENT\t\t", + "HDCP_SECURITY_CHANGE_EVENT\t", + "HDCP_VPRIME_VALUE_READY_EVENT\t", + "HDCP_AUTH_STATUS_CHANGE_EVENT\t", +}; + +void tpi_debug_interrupt(struct hdmi_info *hdmi, u8 old_status, u8 new_status) +{ + int i, diff, on_off; + HDMI_DBG("%s: status changed, %02x to %02x\n", __func__, + old_status, new_status); + for (i = 7; i >= 0; i--) { + diff = (old_status ^ new_status) & (1 << i); + if (!diff) + continue; + on_off = new_status & (1 << i); + HDMI_DBG("%d-%s->%s\n", i, str_debug_interrupt[i], + on_off ? "on" : "off"); + } +} +////////////////////////////////////////////////////////////////////////////// +// FUNCTION : TPI_Poll () +// PURPOSE : Poll Interrupt Status register for new interrupts +// INPUT PARAMS : None +// OUTPUT PARAMS: None +// GLOBALS USED : LinkProtectionLevel +// RETURNS : None +////////////////////////////////////////////////////////////////////////////// +static u8 last_status = 0; +static void tpi_poll(struct hdmi_info *hdmi) +{ + u8 status, orig_status; + int retry = 20; + + mutex_lock(&hdmi->polling_lock); + orig_status = status = hdmi_read(hdmi->client, TPI_INTERRUPT_STATUS_REG); + if (last_status != status) { + tpi_debug_interrupt(hdmi, last_status, status); + } + last_status = status; + DLOG(DBG_POLLING, "%s, INT_STAT=%02x\n", __func__, status); +#if 0 + if (status & HOT_PLUG_EVENT) { +#else + if (hdmi->first || status & HOT_PLUG_EVENT) { + if (hdmi->first) hdmi->first = false; +#endif + // Enable HPD interrupt bit + ReadSetWriteTPI(hdmi, TPI_INTERRUPT_ENABLE_REG, HOT_PLUG_EVENT); + // Repeat this loop while cable is bouncing: + do { + //DLOG(DBG_POLLING, "TPI: Interrupt status image - 2= %02x\n", status); + hdmi_write_byte(hdmi->client, TPI_INTERRUPT_STATUS_REG, HOT_PLUG_EVENT); + // Delay for metastability protection and to help filter out connection bouncing + mdelay(T_HPD_DELAY); + // Read Interrupt status register + status = hdmi_read(hdmi->client, TPI_INTERRUPT_STATUS_REG); + //DLOG(DBG_POLLING, "TPI: Interrupt status image - 3= %02x\n", status); + if (!retry--) { + HDMI_DBG("%s: retry failed\n", __func__); + break; + } + + } while (status & HOT_PLUG_EVENT);// loop as long as HP interrupts recur + DLOG(DBG_POLLING, "int status: %02x, after debouncing: %02x\n", + orig_status, status); + + //DLOG(DBG_POLLING, "TPI->hdmiCableConnected = %d\n", hdmi->cable_connected); + if (((status & HOT_PLUG_STATE) >> 2) != hdmi->cable_connected) { + DLOG(DBG_POLLING, "cable status changed: from %d to %d\n", + hdmi->cable_connected, !!(status & HOT_PLUG_STATE)); + //DLOG(DBG_POLLING, "TPI-> CONDITION\n"); + if (hdmi->cable_connected == true) + tpi_cable_disconn(hdmi, status & 0x8 ? false : true); + else { + tpi_cable_conn(hdmi); + ReadModifyWriteIndexedRegister(hdmi, INDEXED_PAGE_0, 0x0A, 0x08, 0x08); + } + if (hdmi->cable_connected == false) { + mutex_unlock(&hdmi->polling_lock); + return; + } + } else if ( false == hdmi->cable_connected) + /* only occur while booting without cable attached. */ + tpi_cable_disconn(hdmi, true); + } + + // Check rx power + if (((status & RX_SENSE_STATE) >> 3) != dsRxPoweredUp) + { + if (hdmi->cable_connected == true) { + if (dsRxPoweredUp == true) + OnDownstreamRxPoweredDown(hdmi); + else + OnDownstreamRxPoweredUp(hdmi); + } + tpi_clear_interrupt(hdmi, RX_SENSE_EVENT); + } + + // Check if Audio Error event has occurred: + if (status & AUDIO_ERROR_EVENT) + // The hardware handles the event without need for host intervention (PR, p. 31) + tpi_clear_interrupt(hdmi, AUDIO_ERROR_EVENT); + + if (hdmi->video_streaming) { + if ((hdmi->cable_connected == true) && (dsRxPoweredUp == true)) + hdcp_check_status(hdmi, status); + } + mutex_unlock(&hdmi->polling_lock); +} + +static void tpi_work_func(struct work_struct *work) +{ + u8 reg = 0; + struct hdmi_info *hdmi = + container_of(work, struct hdmi_info, polling_work); + + if (hdmi->sleeping == SLEEP) { + mutex_lock(&hdmi->lock); + hdmi->power(3); + hdmi_wakeup(hdmi); + tpi_init(hdmi); + hdcp_off(hdmi); + mutex_unlock(&hdmi->lock); + } + + tpi_poll(hdmi); +#if 1 + mutex_lock(&hdmi->lock); + if (hdmi->sleeping == AWAKE) + reg = hdmi_read(hdmi->client, 0x3d) & 0x0c; + if (hdmi->cable_connected || reg) { + hdmi->polling = true; + mod_timer(&hdmi->timer, jiffies + INTERVAL_HDCP_POLLING); + } else { + enable_irq(hdmi->client->irq); + hdmi->isr_enabled = true; + hdmi->polling = false; + } + mutex_unlock(&hdmi->lock); +#else + if (hdmi->sleeping == AWAKE) { + reg = hdmi_read(hdmi->client, 0x3d); + if (reg & 0x0c) { + hdmi->polling = true; + mod_timer(&hdmi->timer, jiffies + INTERVAL_HDCP_POLLING); + } else { + tpi_clear_pending_event(hdmi); + } + } + + if (hdmi->cable_connected ) { + hdmi->polling = true; + mod_timer(&hdmi->timer, jiffies + INTERVAL_HDCP_POLLING); + } else { + enable_irq(hdmi->client->irq); + hdmi->isr_enabled = true; + hdmi->polling = false; + } +#endif +/* + HDMI_DBG("after polling: reg=%02x, conn=%d, isr=%d, polling=%d\n", + reg, hdmi->cable_connected, hdmi->isr_enabled, hdmi->polling); +*/ +} + +static void tpi_timer_func(unsigned long arg) +{ + struct hdmi_info *hdmi = (struct hdmi_info *) arg; + + schedule_work(&hdmi->polling_work); +} + +int tpi_prepare(struct hdmi_info *hdmi) +{ + HDMI_DBG("%s\n", __func__); + init_timer(&hdmi->timer); + hdmi->timer.data = (unsigned long)hdmi; + hdmi->timer.function = tpi_timer_func; + hdmi->cable_connected = false; + + init_completion(&hdmi->hotplug_completion); + INIT_WORK(&hdmi->polling_work, tpi_work_func); + + return 0; +} + +/*============================================================================*/ +#if defined(HDMI_DEBUGFS) +static ssize_t tpi_dbg_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t tpi_ddc_request_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + //struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + return 0; +} + +static ssize_t tpi_ddc_request_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + return 0; +} + +static ssize_t tpi_isr_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + int n = 0; + char buffer[4]; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + HDMI_DBG("%s\n", __func__); + n = scnprintf(buffer, 4, "%d\n", hdmi->isr_enabled); + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static ssize_t tpi_polling_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + int n = 0; + char buffer[4]; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + HDMI_DBG("%s\n", __func__); + n = scnprintf(buffer, 4, "%d\n", hdmi->polling); + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static ssize_t tpi_int_status_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + int n = 0; + char buffer[8]; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + HDMI_DBG("%s\n", __func__); + n = scnprintf(buffer, 8, "%02x\n", hdmi_read(hdmi->client, 0x3d)); + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static ssize_t tpi_int_enable_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + int n = 0; + char buffer[8]; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + HDMI_DBG("%s\n", __func__); + n = scnprintf(buffer, 8, "%02x\n", hdmi_read(hdmi->client, 0x3c)); + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static ssize_t tpi_avc_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + int n = 0; + char buffer[8]; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + HDMI_DBG("%s\n", __func__); +/* + n = scnprintf(buffer, 8, "%02x\n", hdmi_read(hdmi->client, 0x3c)); + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +*/ + hdmi_active9022(hdmi->client); + return 0; +} + +static struct file_operations tpi_debugfs_fops[] = { + { + .open = tpi_dbg_open, + .read = tpi_ddc_request_read, + .write = tpi_ddc_request_write, + }, + { + .open = tpi_dbg_open, + .read = tpi_isr_read, + }, + { + .open = tpi_dbg_open, + .read = tpi_polling_read, + }, + { + .open = tpi_dbg_open, + .read = tpi_int_status_read, + }, + { + .open = tpi_dbg_open, + .read = tpi_int_enable_read, + }, + { + .open = tpi_dbg_open, + .read = tpi_avc_read, + }, +}; + +int tpi_debugfs_init(struct hdmi_info *hdmi) +{ + struct dentry *tpi_dent; + + tpi_dent = debugfs_create_dir("tpi", hdmi->debug_dir); + if (IS_ERR(tpi_dent)) + return PTR_ERR(tpi_dent); + + //FIXME: error handling + debugfs_create_file("ddc_request", 0644, tpi_dent, hdmi, + &tpi_debugfs_fops[0]); + debugfs_create_file("isr_enabled", 0444, tpi_dent, hdmi, + &tpi_debugfs_fops[1]); + debugfs_create_file("polling", 0444, tpi_dent, hdmi, + &tpi_debugfs_fops[2]); + debugfs_create_file("int_stat", 0444, tpi_dent, hdmi, + &tpi_debugfs_fops[3]); + debugfs_create_file("int_ena", 0444, tpi_dent, hdmi, + &tpi_debugfs_fops[4]); + debugfs_create_file("avc", 0444, tpi_dent, hdmi, + &tpi_debugfs_fops[5]); + + return 0; +} +#endif diff --git a/drivers/video/msm/hdmi/transmitter.c b/drivers/video/msm/hdmi/transmitter.c new file mode 100644 index 0000000000000..f0c56b85a85b8 --- /dev/null +++ b/drivers/video/msm/hdmi/transmitter.c @@ -0,0 +1,1020 @@ +/* + * Copyright (C) 2009 HTC Corporation. + * + * 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/fb-hdmi.h" +#include "include/sil902x.h" + +#ifdef CONFIG_HTC_HEADSET_MGR +#include +#endif + +#if 1 +#define HDMI_DBG(s...) printk("[hdmi/tx]" s) +#else +#define HDMI_DBG(s...) do {} while (0) +#endif + +#define HDMI_NAME "SiL902x-hdmi" +//#define HDMI_DEBUGFS + +static struct class *hdmi_class; + +enum { + ESTABLISHED_TIMING_OFFSET = 35, + LONG_DESCR_LEN = 18, + NUM_DETAILED_DESC = 4, + NUM_STANDARD_TIMING = 8, +}; + +#if 1 +int hdmi_read(struct i2c_client *client, u8 cmd) +#else +#define hdmi_read(client, cmd) _hdmi_read(client, cmd, __func__) +int _hdmi_read(struct i2c_client *client, u8 cmd, const char *caller) +#endif +{ + int ret = -EIO, retry = 10; + + while (retry--) { + ret = i2c_smbus_read_byte_data(client, cmd); + if (ret >= 0) + break; + msleep(1); + } +/* + if (retry!=9) + HDMI_DBG("%s, retry=%d, caller=%s\n", __func__, 10-retry, + caller); +*/ + + return ret; +} + +int tpi_readb(struct hdmi_info *hdmi, u8 reg) +{ + int i, ret = -EIO, retrial = 10, timeout = 1; + struct i2c_client *client = hdmi->client; + + for (i = 1 ; i < retrial ; i++) { + ret = i2c_smbus_read_byte_data(client, reg); + if (ret >= 0) + break; + msleep(timeout++); + } + return ret; +} + +int tpi_readb_oneshoot(struct hdmi_info *hdmi, u8 reg) +{ + return i2c_smbus_read_byte_data(hdmi->client, reg); +} + +#if 1 +int hdmi_write_byte(struct i2c_client *client, u8 reg, u8 val) +#else +#define hdmi_write_byte(client, reg, val) \ + _hdmi_write_byte(client, reg, val, __func__) +int _hdmi_write_byte(struct i2c_client *client, u8 reg, u8 val, const char *caller) +#endif +{ + int ret = -EIO, retry = 10; + + while (retry--) { + ret = i2c_smbus_write_byte_data(client, reg, val); + if (ret == 0) + break; + msleep(1); + } +/* + if (retry!=9) HDMI_DBG("%s, retry=%d, caller=%s\n", __func__, + 10 - retry, caller); +*/ + + return ret; +} + +int tpi_writeb(struct hdmi_info *hdmi, u8 reg, u8 val) +{ + int i, ret = -EIO, retrial = 10, timeout = 1; + struct i2c_client *client = hdmi->client; + + for (i = 1 ; i < retrial ; i++) { + ret = i2c_smbus_write_byte_data(client, reg, val); + if (ret == 0) + break; + msleep(timeout); + } + return ret; +} + +int tpi_writeb_oneshot(struct hdmi_info *hdmi, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(hdmi->client, reg, val); +} + +int hdmi_enable_int(struct i2c_client *client) +{ + u8 data; + + HDMI_DBG("%s\n", __func__); + data = hdmi_read(client, HDMI_INT_EN); + return hdmi_write_byte(client, HDMI_INT_EN, data | 0x01); +} + +int hdmi_disable_int(struct i2c_client *client) +{ + u8 data; + + HDMI_DBG("%s\n", __func__); + data = hdmi_read(client, HDMI_INT_EN); + return hdmi_write_byte(client, HDMI_INT_EN, data & 0xfe); +} + +/* + * Tx is brought to low-power state, off audio codec. + * i2c alive. Still be able to response to INT. + */ +int hdmi_standby(struct hdmi_info *hdmi) +{ + u8 data; + int ret; + struct i2c_client *client = hdmi->client; + + HDMI_DBG("%s\n", __func__); +#if 0 + /* D2 sleep mode */ + data = hdmi_read(client, HDMI_POWER); + return hdmi_write_byte(client, HDMI_POWER, (data & 0xfc) | 0x02); +#else + if (SLEEP == hdmi->sleeping) + return 0; + /* D3 sleep mode */ + hdmi->sleeping = SLEEP; + hdmi->cable_connected = false; + data = hdmi_write_byte(client, 0x3c, hdmi_read(client, 0x3c) | 1); + data = hdmi_write_byte(client, 0x3c, hdmi_read(client, 0x3c) & ~2); + HDMI_DBG("%s: INT_EN=%02x\n", __func__, hdmi_read(client, 0x3c)); + data = hdmi_read(client, HDMI_POWER); + data |= 4; + ret = hdmi_write_byte(client, HDMI_POWER, data ); + if (ret) + dev_err(&client->dev, + "error on entering D3 sleep mode: into cold mode\n"); +#if 0 + ret = hdmi_write_byte(client, HDMI_POWER, 7); +#else + tpi_writeb_oneshot(hdmi, HDMI_POWER, 7); +#endif +/* + if (ret) + dev_err(&client->dev, + "error on entering D3 sleep mode: set D3 mode\n"); +*/ +#endif + return ret; +} + +int hdmi_wakeup(struct hdmi_info *hdmi) +{ + int err = -EIO; + int ret; + u8 data; + struct i2c_client *client = hdmi->client; + + HDMI_DBG("%s\n", __func__); +#if 0 + data = hdmi_read(client, HDMI_POWER); + err = hdmi_write_byte(client, HDMI_POWER, data & 0xfc); + if (err) + goto exit; +#else + /* Exiting D3 sleep mode */ + ret = hdmi_write_byte(client, 0xc7, 0); + if (ret) + dev_err(&client->dev, + "error on exiting D3 sleep mode: 0xc7=0\n"); + + data = hdmi_read(client, HDMI_POWER); + data = ( data & 0xfc ) ; + ret = hdmi_write_byte(client, HDMI_POWER, data ); + if (ret) + dev_err(&client->dev, + "error on exiting D3 sleep mode: 0x1e=0\n"); + /* Enable insternal TMDS source termination */ + hdmi_write_byte(client, 0xbc, 0x01); + hdmi_write_byte(client, 0xbd, 0x82); + data = hdmi_read(client, 0xbe); + hdmi_write_byte(client, 0xbe, data | 0x01); + + hdmi->sleeping = AWAKE; +#endif + +/* + data = hdmi_read(client, HDMI_SYS_CTL); + dev_info(&client->dev, "%s, HDMI_SYS_CTL=0x%x\n", __func__, data); + err = hdmi_write_byte(client, HDMI_SYS_CTL, 0x01); + if (err) + goto exit; +*/ + return 0; +exit: + dev_err(&client->dev, "%s: fail, err = %d\n", __func__, err); + return err; +} + +static int +hdmi_check_res(struct hdmi_device *hdmi_device, struct fb_var_screeninfo *var) +{ + if (((var->xres == 1280) && (var->yres == 720)) || + ((var->xres == 800) && (var->yres == 600)) || + ((var->xres == 720) && (var->yres == 576)) || + ((var->xres == 720) && (var->yres == 480)) || + ((var->xres == 640) && (var->yres == 480))) { + dev_info(&hdmi_device->dev, "resolution check successfully\n"); + /* check pixel clock also */ + return 0; + } + + return -EINVAL; +} + +static struct msm_lcdc_timing hdmi_lcdc_timing[] = { + [hd_720p] = { + .clk_rate = 74250000, + .hsync_pulse_width = 40, + .hsync_back_porch = 220, + .hsync_front_porch = 110, + .hsync_skew = 0, + .vsync_pulse_width = 5, + .vsync_back_porch = 20, + .vsync_front_porch = 5, + .vsync_act_low = 0, + .hsync_act_low = 0, + .den_act_low = 0, + }, + [svga] = { + .clk_rate = 40000000, + .hsync_pulse_width = 128, + .hsync_back_porch = 88, + .hsync_front_porch = 40, + .hsync_skew = 0, + .vsync_pulse_width = 4, + .vsync_back_porch = 23, + .vsync_front_porch = 1, + .vsync_act_low = 0, + .hsync_act_low = 0, + .den_act_low = 0, + }, + [pal] = { + .clk_rate = 27027000, + .hsync_pulse_width = 64, + .hsync_back_porch = 68, + .hsync_front_porch = 12, + .hsync_skew = 0, + .vsync_pulse_width = 5, + .vsync_back_porch = 39, + .vsync_front_porch = 5, + .vsync_act_low = 1, + .hsync_act_low = 1, + .den_act_low = 0, + }, + [edtv] = { + .clk_rate = 27027000, + .hsync_pulse_width = 62, + .hsync_back_porch = 60, + .hsync_front_porch = 16, + .hsync_skew = 0, + .vsync_pulse_width = 6, + .vsync_back_porch = 30, + .vsync_front_porch = 9, +#if 1 + .vsync_act_low = 1, + .hsync_act_low = 1, +#else + .vsync_act_low = 0, + .hsync_act_low = 0, +#endif + + .den_act_low = 0, + }, + [vga] = { + .clk_rate = 25175000, + .hsync_pulse_width = 96, + .hsync_back_porch = 48, + .hsync_front_porch = 16, + .hsync_skew = 0, + .vsync_pulse_width = 2, + //.vsync_pulse_width = 3, + .vsync_back_porch = 33, + .vsync_front_porch = 10, + .vsync_act_low = 1, + .hsync_act_low = 1, + .den_act_low = 0, + }, +}; + +static struct msm_lcdc_timing * +hdmi_set_res(struct hdmi_device *hdmi_device, struct fb_var_screeninfo *var) +{ + struct hdmi_info *info = container_of(hdmi_device, struct hdmi_info, + hdmi_dev); + + printk(KERN_DEBUG "%s, info->res=%d=(%d x %d)\n", + __func__, info->res, var->xres, var->yres); + if ((var->xres == 1280) && (var->yres == 720)) + info->res = hd_720p; + else if ((var->xres == 800) && (var->yres == 600)) + info->res = svga; + else if ((var->xres == 720) && (var->yres == 576)) + info->res = pal; + else if ((var->xres == 720) && (var->yres == 480)) + info->res = edtv; + else if ((var->xres == 640) && (var->yres == 480)) + info->res = vga; + else + return ERR_PTR(-EINVAL); +/* + if (info->user_playing) + avc_send_avi_info_frames(info); +*/ + return &hdmi_lcdc_timing[info->res]; +} + +static int hdmi_get_cable_state(struct hdmi_device *hdmi_device, int *connect) +{ +#if 0 + struct hdmi_info *info = container_of(hdmi_device, struct hdmi_info, + hdmi_dev); + struct i2c_client *client = info->client; + u8 status; + + *connect = 0; + status = hdmi_read(client, HDMI_INT_STAT); + if (status & HOT_PLUG_STATE) + *connect = 1; +#else + struct hdmi_info *hdmi = + container_of(hdmi_device, struct hdmi_info, hdmi_dev); + *connect = hdmi->cable_connected; +#endif + HDMI_DBG("%s, state=%s\n", __func__, *connect ? "on" : "off" ); + return 0; +} + +static int +hdmi_get_established_timing(struct hdmi_device *hdmi_device, u8 *byte) +{ + struct hdmi_info *info = container_of(hdmi_device, struct hdmi_info, + hdmi_dev); + + HDMI_DBG("%s\n", __func__); + memcpy(byte, &info->edid_buf[ESTABLISHED_TIMING_OFFSET], 3); + return 0; +} + +#if 0 +// FIXME: remove the parameter: data +static u8 hdmi_request_ddc(struct i2c_client *client, int request, u8 data) +{ + int retry = 10; + + HDMI_DBG("%s, request=%d\n", __func__, request); + if (request) { + data = hdmi_read(client, HDMI_SYS_CTL); + hdmi_write_byte(client, HDMI_SYS_CTL, (data | 0x04)); + msleep(1); + hdmi_write_byte(client, HDMI_SYS_CTL, (data | 0x06)); + msleep(1); + } else { + hdmi_write_byte(client, HDMI_SYS_CTL, (data & 0xf9)); + hdmi_write_byte(client, HDMI_SYS_CTL, (data & 0xf9)); + /* make sure bit [2:1] = 00 */ + data = hdmi_read(client, HDMI_SYS_CTL); + while ((data & 0x03) & retry--) + msleep(1); + } + return data; +} + +#else +// FIXME: remove the static varible. if caller need to presev the reg, +// it should use hdmi_read() first. +static u8 hdmi_request_ddc(struct i2c_client *client, int request) +{ + int retry = 10; + static u8 val = 0; + u8 tmp; + + HDMI_DBG("%s, request=%d\n", __func__, request); + + if (request) { + val = hdmi_read(client, HDMI_SYS_CTL); + hdmi_write_byte(client, HDMI_SYS_CTL, (val | 0x04)); + msleep(1); + hdmi_write_byte(client, HDMI_SYS_CTL, (val | 0x06)); + msleep(1); + + } else { + do { + hdmi_write_byte(client, HDMI_SYS_CTL, (val & 0xf9)); + tmp = hdmi_read(client, HDMI_SYS_CTL); + msleep(1); + /* make sure bit [2:1] = 00 */ + } while ((tmp & 0x06) & retry--) ; + } + + return 0; +} +#endif + +static uint8_t timing_id[][3] = { + { 0x81, 0xc0, 1 << 6 }, /* 1280x720 */ + { 0x3b, 0x80, 1 << 5 }, /* 720x576 */ + { 0x3b, 0x00, 1 << 4 }, /* 720x480 */ +}; + +//---------------------------------------------------------------------- +static irqreturn_t hdmi_irq_handler(int irq, void *data) +{ + struct hdmi_info *hdmi = (struct hdmi_info *) data; + HDMI_DBG("%s\n", __func__); + + disable_irq_nosync(hdmi->client->irq); + hdmi->isr_enabled = false; + hdmi->first = true; + if (!hdmi->cable_connected) { + hdmi->timer.expires = jiffies + INTERVAL_HDCP_POLLING; + add_timer(&hdmi->timer); + } + + return IRQ_HANDLED; +} +/* ---------------------------------------------------------------- */ +extern bool hdmifb_suspending; +static int hdmi_panel_blank(struct msm_lcdc_panel_ops *ops) +{ + struct hdmi_info *info = container_of(ops, struct hdmi_info, + hdmi_lcdc_ops); + + HDMI_DBG("%s\n", __func__); + + info->user_playing = false; + info->video_streaming= false; +#if 0 + /* if called from suspending */ + if (hdmifb_suspending) { + /* to avoid timer been revoked after standby */ + HDMI_DBG("suspending=true, disable timer\n"); + cancel_work_sync(&info->polling_work); + del_timer(&info->timer); + + HDMI_DBG("%s\n", __func__); + mutex_lock(&info->lock); + hdmi_standby(info); + info->power(2); + mutex_unlock(&info->lock); + } +#endif + return 0; +} + +static int hdmi_panel_unblank(struct msm_lcdc_panel_ops *ops) +{ + struct hdmi_info *info = container_of(ops, struct hdmi_info, + hdmi_lcdc_ops); + struct i2c_client *client = info->client; + + HDMI_DBG("%s\n", __func__); + clk_set_rate(info->ebi1_clk, 120000000); + if (info->suspending == true) { + HDMI_DBG("%s :actived before panel_init\n", __func__); + msleep(500); + } + + info->user_playing = true; + + return 0; +} + +static int hdmi_panel_init(struct msm_lcdc_panel_ops *ops) +{ + u8 conn; + struct hdmi_info *hd = container_of(ops, struct hdmi_info, + hdmi_lcdc_ops); + struct i2c_client *client = hd->client; + + HDMI_DBG("%s\n", __func__); + + if (hd->hdmi_gpio_on) + hd->hdmi_gpio_on(); + /* Turn-on 5V to ensure hot-plug detection */ + hd->power(5); + +#if 0 + /* For D2 sleep mode */ + hd->power(1); + + ret = hdmi_write_byte(client, HDMI_EN_REG, 0x00); + if (ret < 0) + goto fail; + + hdmi_disable_int(client); + + data = hdmi_read(client, HDMI_POWER); + if (data & 0xfc) { + dev_info(&client->dev, "power state = %d\n", data & 0xfc); + } else { + dev_info(&client->dev, "bring HDMI back\n"); + hdmi_enable_int(client); + HDMI_DBG("hotplug state=%d\n", hd->cable_connected); + } +#else + if (hd->polling) { + mutex_lock(&hd->lock); + hd->power(3); + hdmi_wakeup(hd); + hd->first = true; + mod_timer(&hd->timer, jiffies + INTERVAL_HDCP_POLLING); + conn = hdmi_read(client, HDMI_INT_STAT) & HOT_PLUG_STATE; + tpi_init(hd); +#ifdef CONFIG_HTC_HEADSET_MGR + switch_send_event(BIT_HDMI_AUDIO, conn); +#endif + mutex_unlock(&hd->lock); + } + hd->suspending = false; +#endif + return 0; +/* +fail: + return ret; +*/ +} + +static int hdmi_panel_uninit(struct msm_lcdc_panel_ops *ops) +{ + struct hdmi_info *info = container_of(ops, struct hdmi_info, + hdmi_lcdc_ops); + HDMI_DBG("%s\n", __func__); + + if (info->hdmi_gpio_off) + info->hdmi_gpio_off(); +#if 0 + /* For D2 sleep mode */ + info->power(0); +#endif + + if (hdmifb_suspending) { + /* to avoid timer been revoked after standby */ + HDMI_DBG("suspending=true, disable timer\n"); + cancel_work_sync(&info->polling_work); + del_timer(&info->timer); + flush_scheduled_work(); + + HDMI_DBG("%s\n", __func__); + mutex_lock(&info->lock); + hdmi_standby(info); + info->power(4); + mutex_unlock(&info->lock); + } + info->suspending = true; + + return 0; +} + +int avc_set_video_parm(struct hdmi_info *hdmi); +int avc_set_blank_screen(struct hdmi_info *hdmi); +void hdmi_pre_change(struct hdmi_info *hdmi) { + if (hdmi->sleeping == SLEEP) + return; + mutex_lock(&hdmi->polling_lock); + HDMI_DBG("%s\n", __func__); + avc_set_blank_screen(hdmi); + hdcp_off(hdmi); + mutex_unlock(&hdmi->polling_lock); +} + +void hdmi_post_change(struct hdmi_info *info, struct fb_var_screeninfo *var) +{ + u8 data[4]; + int i, ret, retry = 10; + struct msm_lcdc_timing *timing; + unsigned h_total, v_total, h_curr, v_curr; + + if (info->sleeping == SLEEP) + return; + + mutex_lock(&info->polling_lock); + HDMI_DBG("%s\n", __func__); + timing = &hdmi_lcdc_timing[info->res]; + + h_total = var->xres + timing->hsync_pulse_width + + timing->hsync_back_porch + timing->hsync_front_porch; + v_total = var->yres + timing->vsync_pulse_width + + timing->vsync_back_porch + timing->vsync_front_porch; + /* Waiting for video stream until steady */ + for (i = 0; i < retry ; i++) { + /* TODO: error handling. */ + /* Read current horizontal/vertical info of video */ + data[0] = hdmi_read(info->client, 0x6a); + data[1] = hdmi_read(info->client, 0x6b); + data[2] = hdmi_read(info->client, 0x6c); + data[3] = hdmi_read(info->client, 0x6d); + h_curr = ((int)data[1]) << 8 | data[0]; + v_curr = ((int)data[3]) << 8 | data[2]; + if (h_curr == h_total && v_curr == v_total) + break; + msleep(17); + } + + avc_set_video_parm(info); + avc_send_avi_info_frames(info); + info->video_streaming = true; + + mutex_unlock(&info->polling_lock); +} + +static struct msm_fb_data hdmi_lcdc_fb_data = { +#if 1 + .xres = 1280, + .yres = 720, +#else + .xres = 720, + .yres = 480, +#endif + .width = 94, + .height = 57, + .output_format = 0, +}; + +static struct msm_lcdc_platform_data hdmi_lcdc_platform_data = { + .timing = &hdmi_lcdc_timing[hd_720p], + .fb_id = 0, + .fb_data = &hdmi_lcdc_fb_data, +}; + +static struct platform_device hdmi_lcdc_device = { + .name = "msm_mdp_hdmi", + .id = -1, +}; + +int register_hdmi_client(struct class_interface *interface) +{ + if (!hdmi_class) { + pr_err("mdp: no hdmi_class when register hdmi client\n"); + return -ENODEV; + } + interface->class = hdmi_class; + return class_interface_register(interface); +} + +#if defined(OLD_DEBUGFS) +static spinlock_t hdmi_dbgfs_lock; +ssize_t hdmi_dbgfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t hdmi_edid_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + static char line[80], buffer[80*8*4]; + static char hextab[] = "0123456789abcdef"; + int i, j, n = 0, v, len, offset, line_size; + unsigned long irq_flags; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + len = ((int)hdmi->edid_buf[0x7e]+1) * 128; + spin_lock_irqsave(&hdmi_dbgfs_lock, irq_flags); + memset(line, ' ', 79); + line[79] = '\0'; + offset = strlen("0000 | "); + line_size = offset + 3 * 16 + 1; + + for (i = 0; i < len / 16 ; i++) { + scnprintf(line, offset + 1, "%04x | ", (i << 4)); + for (j = 0; j < 16 ; j++) { + v = hdmi->edid_buf[i * 16 + j]; + line[offset + j * 3] = hextab[v / 16]; + line[offset + j * 3 + 1] = hextab[v % 16]; + } + line[line_size - 1] = '\n'; + strncpy(buffer + i * line_size, line, line_size); + n += line_size; + } + spin_unlock_irqrestore(&hdmi_dbgfs_lock, irq_flags); + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +#if 0 +static ssize_t hdmi_dbgfs_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned long v; + unsigned long irq_flags; + char buff[80]; + struct tv_reg_data *trd = (struct tv_reg_data *)filp->private_data; + + if (count >= sizeof(buff)) + return -EINVAL; + if (copy_from_user(&buff, buf, 80)) + return -EFAULT; + buff[count] = 0; + + spin_lock_irqsave(&hdmi_dbgfs_lock, irq_flags); + strict_strtoul(buff, 16, &v); + buff[strlen(buff)]=0; + writel(v, tvenc_base+trd->offset); + spin_unlock_irqrestore(&hdmi_dbgfs_lock, irq_flags); + + return count; +} +#endif + +static ssize_t hdmi_cable_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + int n; + char buffer[80]; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + n = scnprintf(buffer, 80, "%d\n", hdmi->cable_connected); + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static ssize_t hdmi_sleeping_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + int n; + char buffer[80]; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + n = scnprintf(buffer, 80, "%d\n", hdmi->sleeping); + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +int hdmifb_get_mode(void); +static ssize_t hdmi_fb_mode_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + int n; + char buffer[80]; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + n = scnprintf(buffer, 80, "%d\n", hdmifb_get_mode()); + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static ssize_t hdmi_isr_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + int n; + char buffer[80]; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + n = scnprintf(buffer, 80, "%d\n", hdmi->isr_enabled); + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static struct file_operations hdmi_fops[] = { + { + .open = hdmi_dbgfs_open, + .read = hdmi_edid_read, + }, + { /* cable*/ + .open = hdmi_dbgfs_open, + .read = hdmi_cable_read, + }, + { /* sleeping */ + .open = hdmi_dbgfs_open, + .read = hdmi_sleeping_read, + }, + { /* fb_mode */ + .open = hdmi_dbgfs_open, + .read = hdmi_fb_mode_read, + }, + { /* isr_enabled */ + .open = hdmi_dbgfs_open, + .read = hdmi_isr_read, + }, + +}; + +static int hdmi_debugfs_init(struct hdmi_info *hdmi) +{ + struct dentry *dent_hdmi; + int ret; + + spin_lock_init(&hdmi_dbgfs_lock); + dent_hdmi = debugfs_create_dir("hdmi", 0); + if (IS_ERR(dent_hdmi)) + return PTR_ERR(dent_hdmi); + debugfs_create_file("edid", 0644, dent_hdmi, hdmi, &hdmi_fops[0]); + debugfs_create_file("cable", 0444, dent_hdmi, hdmi, &hdmi_fops[1]); + debugfs_create_file("sleeping", 0444, dent_hdmi, hdmi, &hdmi_fops[2]); + debugfs_create_file("fb_mode", 0444, dent_hdmi, hdmi, &hdmi_fops[3]); + debugfs_create_file("isr", 0444, dent_hdmi, hdmi, &hdmi_fops[4]); + + return 0; +} +#endif + +static int __init hdmi_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct hdmi_info *hd; + struct hdmi_platform_data *pdata; + int ret = -EIO; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "No supported I2C func\n"); + ret = -ENOTSUPP; + goto exit; + } + + hd = kzalloc(sizeof(*hd), GFP_KERNEL); + if (hd == NULL) { + ret = -ENOMEM; + goto exit; + } + + hd->client = client; + i2c_set_clientdata(client, hd); + mutex_init(&hd->lock); + mutex_init(&hd->lock2); + mutex_init(&hd->polling_lock); + + hd->ebi1_clk = clk_get(NULL, "ebi1_clk"); + if (IS_ERR(hd->ebi1_clk)) { + dev_err(&client->dev, "get ebi1 clk fail\n"); + goto fail_get_ebi1; + } + + pdata = client->dev.platform_data; + if (unlikely(!pdata) || unlikely(!pdata->power)) { + dev_err(&client->dev, "No platform data\n"); + ret = -ENXIO; + goto fail_power; + } else { + if (pdata->hdmi_gpio_on) + pdata->hdmi_gpio_on(); + hd->power = pdata->power; + ret = hd->power(1); + if (ret) { + dev_err(&client->dev, "hdmi power on failed\n"); + ret = -EIO; + goto fail_power; + } + } + + ret = hdmi_write_byte(client, HDMI_EN_REG, 0x00); + if (ret < 0) { + ret = -EIO; + goto fail_hdmi_init; + } + + ret = hdmi_read(client, HDMI_IDENTIFY); + if (ret < 0) { + ret = -EIO; + goto fail_hdmi_init; + } else if (ret != 0xb0) { + dev_err(&client->dev, "can not recognize, 0x%x\n", ret); + ret = -ENXIO; + goto fail_hdmi_init; + } + + hdmi_disable_int(client); + + hd->user_playing = false; + tpi_prepare(hd); + ret = request_irq(client->irq, hdmi_irq_handler, IRQF_TRIGGER_LOW, + client->name, hd); + if (ret) { + /* HDMI did not care if interrupt fail */ + dev_err(&client->dev, "request irq fail, err = %d\n", ret); + } else { + ret = hdmi_enable_int(client); + if (ret) { + free_irq(client->irq, hd); + ret = -ENOTSUPP; + } + } + + dev_info(&client->dev, "hdmi is on line with irq %s\n", + ret ? "Disabled" : "Enabled"); + + /* set up "panel" */ + hd->hdmi_lcdc_ops.init = hdmi_panel_init; + hd->hdmi_lcdc_ops.uninit = hdmi_panel_uninit; + hd->hdmi_lcdc_ops.blank = hdmi_panel_blank; + hd->hdmi_lcdc_ops.unblank = hdmi_panel_unblank; + hd->hdmi_gpio_on = pdata->hdmi_gpio_on; + hd->hdmi_gpio_off = pdata->hdmi_gpio_off; + + hdmi_lcdc_platform_data.panel_ops = &hd->hdmi_lcdc_ops; + hdmi_lcdc_platform_data.fb_resource = &pdata->hdmi_res; + hdmi_lcdc_device.dev.platform_data = &hdmi_lcdc_platform_data; + ret = platform_device_register(&hdmi_lcdc_device); + if (ret) + goto fail_hdmi_init; + + hd->hdmi_dev.check_res = hdmi_check_res; + hd->hdmi_dev.set_res = hdmi_set_res; + hd->hdmi_dev.get_cable_state = hdmi_get_cable_state; + hd->hdmi_dev.get_establish_timing = hdmi_get_established_timing; + + hd->hdmi_dev.dev.parent = &client->dev; + hd->hdmi_dev.dev.class = hdmi_class; + //snprintf(hd->hdmi_dev.dev.bus_id, BUS_ID_SIZE, "hdmi%d", 0); + dev_set_name(&hd->hdmi_dev.dev, "hdmi%d", 0); + ret = device_register(&hd->hdmi_dev.dev); + if (ret) + dev_err(&client->dev, "device register fail\n"); + +#if defined(HDMI_DEBUGFS) + hdmi_debugfs_init(hd); +#endif + /* check any pending interrupt */ + hdmi_irq_handler(client->irq, hd); + return 0; + +fail_hdmi_init: +fail_get_ebi1: + clk_put(hd->ebi1_clk); +fail_power: + kfree(hd); +exit: + dev_err(&client->dev, "%s fail, err = %d\n", __func__, ret); + return ret; +} + +/* -------------------------------------------------------------------- */ + +static const struct i2c_device_id hdmi_id[] = { + {HDMI_NAME, 0}, + { } +}; + +static struct i2c_driver hdmi_driver = { + .probe = hdmi_probe, + /*.remove = hdmi_remove,*/ + .id_table = hdmi_id, + .driver = { + .name = HDMI_NAME, + }, +}; + +static int __init hdmi_init(void) +{ + hdmi_class = class_create(THIS_MODULE, "msm_hdmi"); + if (IS_ERR(hdmi_class)) { + printk(KERN_ERR "Error creating hdmi class\n"); + return PTR_ERR(hdmi_class); + } + return i2c_add_driver(&hdmi_driver); +} + +static void __exit hdmi_exit(void) +{ + i2c_del_driver(&hdmi_driver); +} + +module_init(hdmi_init); +module_exit(hdmi_exit); + +MODULE_DESCRIPTION("Sil902x hdmi driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/htc_hdmi.h b/include/linux/htc_hdmi.h new file mode 100644 index 0000000000000..1ad580ff6d127 --- /dev/null +++ b/include/linux/htc_hdmi.h @@ -0,0 +1,56 @@ +/* include/linux/htc_hdmi.h + * + * Copyright (c) 2010 HTC + * + * 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 _HTC_HDMI_H_ +#define _HTC_HDMI_H_ + +enum { + hdmi_mode = 0, /* 0: mirror, 1: presentation */ + hdmi_enabled, + fb_enabled, +}; + +#define HDMI_IOCTL_MAGIC 'h' +#define HDMI_SET_MODE _IOW(HDMI_IOCTL_MAGIC, 1, unsigned) +#define HDMI_GET_MODE _IOR(HDMI_IOCTL_MAGIC, 2, unsigned) +#define HDMI_DISABLE _IOW(HDMI_IOCTL_MAGIC, 3, unsigned) +#define HDMI_ENABLE _IOW(HDMI_IOCTL_MAGIC, 4, unsigned) +#define HDMI_GET_STATE _IOR(HDMI_IOCTL_MAGIC, 5, unsigned) +#define HDMI_BLIT _IOW(HDMI_IOCTL_MAGIC, 6, unsigned) +#define HDMI_CABLE_STAT _IOR(HDMI_IOCTL_MAGIC, 7, unsigned) +#define HDMI_ESTABLISH_TIMING _IOR(HDMI_IOCTL_MAGIC, 8, unsigned) +#define HDMI_GET_EDID _IOR(HDMI_IOCTL_MAGIC, 9, unsigned) +#define HDMI_GET_DISPLAY_INFO _IOR(HDMI_IOCTL_MAGIC, 10, unsigned) + +#define ASPECT(w, h) (w << 8 | h) +struct video_mode { + unsigned short width, height, refresh_rate, aspect; + bool interlaced, supported; + char *descrption; +}; + +enum { + PROGRESSIVE, + INTERLACE, +}; + +struct display_info { + unsigned int visible_width; /* in mm */ + unsigned int visible_height; + unsigned int resolution_width; /* in pixel */ + unsigned int resolution_height; +}; + +#endif From 9f3ea3fc912bee9af710c696abc69c66b7d903dc Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sun, 7 Nov 2010 23:49:32 -0500 Subject: [PATCH 1017/2556] msm: video: Add Epson and Novtec panel support Support for MDDI panels found on the HTC Supersonic. Change-Id: I3d28ee63d41b76a040a543d4e66000caca053ad9 --- drivers/video/msm/Kconfig | 10 + drivers/video/msm/Makefile | 2 + drivers/video/msm/mddi_client_epson.c | 268 ++++++++++++++++ drivers/video/msm/mddi_client_novb9f6_5582.c | 311 +++++++++++++++++++ 4 files changed, 591 insertions(+) create mode 100644 drivers/video/msm/mddi_client_epson.c create mode 100644 drivers/video/msm/mddi_client_novb9f6_5582.c diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig index c505af0e71d5e..8b4c1423749ad 100644 --- a/drivers/video/msm/Kconfig +++ b/drivers/video/msm/Kconfig @@ -26,6 +26,16 @@ config FB_MSM_MDDI depends on FB_MSM default y +config FB_MSM_MDDI_EPSON + bool "Support for Epson MDDI panels" + depends on FB_MSM_MDDI + default n + +config FB_MSM_MDDI_NOVTEC + bool "Support for Novtec MDDI panels" + depends on FB_MSM_MDDI + default n + config GPU_MSM_KGSL tristate "MSM 3D Graphics driver for Adreno class GPUs" default n diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile index 1b7803a7bbf07..1b307877e7f37 100644 --- a/drivers/video/msm/Makefile +++ b/drivers/video/msm/Makefile @@ -26,6 +26,8 @@ obj-$(CONFIG_FB_MSM_MDDI) += mddi.o # obj-$(CONFIG_FB_MSM_MDDI) += mddi_client_simple.o obj-$(CONFIG_FB_MSM_MDDI) += mddi_client_toshiba.o +obj-$(CONFIG_FB_MSM_MDDI_NOVTEC) += mddi_client_novb9f6_5582.o +obj-$(CONFIG_FB_MSM_MDDI_EPSON) += mddi_client_epson.o # MDP LCD controller driver obj-$(CONFIG_FB_MSM_LCDC) += mdp_lcdc.o diff --git a/drivers/video/msm/mddi_client_epson.c b/drivers/video/msm/mddi_client_epson.c new file mode 100644 index 0000000000000..7517acf10f9d7 --- /dev/null +++ b/drivers/video/msm/mddi_client_epson.c @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2008 HTC Corporation. + * + * 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 + +static DECLARE_WAIT_QUEUE_HEAD(epson_vsync_wait); + +struct panel_info { + struct msm_mddi_client_data *client_data; + struct platform_device pdev; + struct msm_panel_data panel_data; + struct msmfb_callback *epson_callback; + struct wake_lock idle_lock; + int epson_got_int; +}; + +static struct platform_device mddi_eps_cabc = { + .name = "eps_cabc", + .id = 0, +}; + +static void epson_request_vsync(struct msm_panel_data *panel_data, + struct msmfb_callback *callback) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + panel->epson_callback = callback; + if (panel->epson_got_int) { + panel->epson_got_int = 0; + client_data->activate_link(client_data); + } +} + +static void epson_clear_vsync(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + client_data->activate_link(client_data); +} + +static void epson_wait_vsync(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + if (panel->epson_got_int) { + panel->epson_got_int = 0; + client_data->activate_link(client_data); /* clears interrupt */ + } + if (wait_event_timeout(epson_vsync_wait, panel->epson_got_int, + HZ/2) == 0) + printk(KERN_ERR "timeout waiting for VSYNC\n"); + panel->epson_got_int = 0; + /* interrupt clears when screen dma starts */ +} + +static int epson_suspend(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + int ret; + + wake_lock(&panel->idle_lock); + ret = bridge_data->uninit(bridge_data, client_data); + wake_unlock(&panel->idle_lock); + if (ret) { + printk(KERN_INFO "mddi epson client: non zero return from " + "uninit\n"); + return ret; + } + client_data->suspend(client_data); + return 0; +} + +static int epson_resume(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + int ret; + + wake_lock(&panel->idle_lock); + client_data->resume(client_data); + wake_unlock(&panel->idle_lock); + ret = bridge_data->init(bridge_data, client_data); + if (ret) + return ret; + return 0; +} + +static int epson_blank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + return bridge_data->blank(bridge_data, client_data); +} + +static int epson_unblank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + return bridge_data->unblank(bridge_data, client_data); +} + +static irqreturn_t epson_vsync_interrupt(int irq, void *data) +{ + struct panel_info *panel = data; + + panel->epson_got_int = 1; + if (panel->epson_callback) { + panel->epson_callback->func(panel->epson_callback); + panel->epson_callback = 0; + } + wake_up(&epson_vsync_wait); + return IRQ_HANDLED; +} + +static int setup_vsync(struct panel_info *panel, + int init) +{ + int ret; + int gpio = 98; + unsigned int irq; + + if (!init) { + ret = 0; + goto uninit; + } + ret = gpio_request(gpio, "vsync"); + if (ret) + goto err_request_gpio_failed; + + ret = gpio_direction_input(gpio); + if (ret) + goto err_gpio_direction_input_failed; + + ret = irq = gpio_to_irq(gpio); + if (ret < 0) + goto err_get_irq_num_failed; + + ret = request_irq(irq, epson_vsync_interrupt, IRQF_TRIGGER_FALLING, + "vsync", panel); + if (ret) + goto err_request_irq_failed; + printk(KERN_INFO "vsync on gpio %d now %d\n", + gpio, gpio_get_value(gpio)); + return 0; + +uninit: + free_irq(gpio_to_irq(gpio), panel); +err_request_irq_failed: +err_get_irq_num_failed: +err_gpio_direction_input_failed: + gpio_free(gpio); +err_request_gpio_failed: + return ret; +} + +static int mddi_epson_probe(struct platform_device *pdev) +{ + int ret; + struct msm_mddi_client_data *client_data = pdev->dev.platform_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + struct panel_data *panel_data = &bridge_data->panel_conf; + struct panel_info *panel = + kzalloc(sizeof(struct panel_info), GFP_KERNEL); + if (!panel) + return -ENOMEM; + platform_set_drvdata(pdev, panel); + + printk(KERN_DEBUG "%s\n", __func__); + + if (panel_data->caps & MSMFB_CAP_CABC) { + printk(KERN_INFO "CABC enabled\n"); + mddi_eps_cabc.dev.platform_data = client_data; + platform_device_register(&mddi_eps_cabc); + } + + ret = setup_vsync(panel, 1); + if (ret) { + dev_err(&pdev->dev, "mddi_bridge_setup_vsync failed\n"); + return ret; + } + + panel->client_data = client_data; + panel->panel_data.suspend = epson_suspend; + panel->panel_data.resume = epson_resume; + panel->panel_data.wait_vsync = epson_wait_vsync; + panel->panel_data.request_vsync = epson_request_vsync; + panel->panel_data.clear_vsync = epson_clear_vsync; + panel->panel_data.blank = epson_blank; + panel->panel_data.unblank = epson_unblank; + panel->panel_data.fb_data = &bridge_data->fb_data; + panel->panel_data.caps = ~MSMFB_CAP_PARTIAL_UPDATES; + + panel->pdev.name = "msm_panel"; + panel->pdev.id = pdev->id; + panel->pdev.resource = client_data->fb_resource; + panel->pdev.num_resources = 1; + panel->pdev.dev.platform_data = &panel->panel_data; + platform_device_register(&panel->pdev); + wake_lock_init(&panel->idle_lock, WAKE_LOCK_IDLE, "eps_idle_lock"); + + return 0; +} + +static int mddi_epson_remove(struct platform_device *pdev) +{ + struct panel_info *panel = platform_get_drvdata(pdev); + + setup_vsync(panel, 0); + kfree(panel); + return 0; +} + +static struct platform_driver mddi_client_d263_0000 = { + .probe = mddi_epson_probe, + .remove = mddi_epson_remove, + .driver = { .name = "mddi_c_4ca3_0000" }, +}; + +static int __init mddi_client_epson_init(void) +{ + platform_driver_register(&mddi_client_d263_0000); + return 0; +} + +module_init(mddi_client_epson_init); + diff --git a/drivers/video/msm/mddi_client_novb9f6_5582.c b/drivers/video/msm/mddi_client_novb9f6_5582.c new file mode 100644 index 0000000000000..47575ce7b8106 --- /dev/null +++ b/drivers/video/msm/mddi_client_novb9f6_5582.c @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2008 HTC Corporation. + * + * 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 + +static DECLARE_WAIT_QUEUE_HEAD(novtec_vsync_wait); + +struct panel_info { + struct msm_mddi_client_data *client_data; + struct platform_device pdev; + struct msm_panel_data panel_data; + struct msmfb_callback *novtec_callback; + struct wake_lock idle_lock; + int novtec_got_int; +}; + +static struct platform_device mddi_nov_cabc = { + .name = "nov_cabc", + .id = 0, +}; + +static void novtec_request_vsync(struct msm_panel_data *panel_data, + struct msmfb_callback *callback) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + panel->novtec_callback = callback; + if (panel->novtec_got_int) { + panel->novtec_got_int = 0; + client_data->activate_link(client_data); + } +} + +static void novtec_clear_vsync(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + client_data->activate_link(client_data); +} + +static void novtec_wait_vsync(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + if (panel->novtec_got_int) { + panel->novtec_got_int = 0; + client_data->activate_link(client_data); /* clears interrupt */ + } + if (wait_event_timeout(novtec_vsync_wait, panel->novtec_got_int, + HZ/2) == 0) + printk(KERN_ERR "timeout waiting for VSYNC\n"); + panel->novtec_got_int = 0; + /* interrupt clears when screen dma starts */ +} + +static int novtec_suspend(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + int ret; + + wake_lock(&panel->idle_lock); + ret = bridge_data->uninit(bridge_data, client_data); + wake_unlock(&panel->idle_lock); + if (ret) { + printk(KERN_INFO "mddi novtec client: non zero return from " + "uninit\n"); + return ret; + } + client_data->suspend(client_data); + return 0; +} + +static int novtec_resume(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + int ret; + + wake_lock(&panel->idle_lock); + client_data->resume(client_data); + wake_unlock(&panel->idle_lock); + ret = bridge_data->init(bridge_data, client_data); + if (ret) + return ret; + return 0; +} + +static int novtec_blank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + return bridge_data->blank(bridge_data, client_data); +} + +static int novtec_unblank(struct msm_panel_data *panel_data) +{ + struct panel_info *panel = container_of(panel_data, struct panel_info, + panel_data); + struct msm_mddi_client_data *client_data = panel->client_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + + return bridge_data->unblank(bridge_data, client_data); +} + +static irqreturn_t novtec_vsync_interrupt(int irq, void *data) +{ + struct panel_info *panel = data; + + panel->novtec_got_int = 1; + if (panel->novtec_callback) { +//XXX T2 Fix For Supersonic +// mdelay(3); + panel->novtec_callback->func(panel->novtec_callback); + panel->novtec_callback = 0; + } + wake_up(&novtec_vsync_wait); + return IRQ_HANDLED; +} + +static int setup_vsync(struct panel_info *panel, + int init) +{ + int ret; + int gpio = 98; + unsigned int irq; + + if (!init) { + ret = 0; + goto uninit; + } + ret = gpio_request(gpio, "vsync"); + if (ret) + goto err_request_gpio_failed; + + ret = gpio_direction_input(gpio); + if (ret) + goto err_gpio_direction_input_failed; + + ret = irq = gpio_to_irq(gpio); + if (ret < 0) + goto err_get_irq_num_failed; + + ret = request_irq(irq, novtec_vsync_interrupt, IRQF_TRIGGER_FALLING, + "vsync", panel); + if (ret) + goto err_request_irq_failed; + printk(KERN_INFO "vsync on gpio %d now %d\n", + gpio, gpio_get_value(gpio)); + return 0; + +uninit: + free_irq(gpio_to_irq(gpio), panel); +err_request_irq_failed: +err_get_irq_num_failed: +err_gpio_direction_input_failed: + gpio_free(gpio); +err_request_gpio_failed: + return ret; +} + +/* maejrep's T2 interface - start */ +/* Allows for changing of the T2 register on the fly */ +static ssize_t nov_t2_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct msm_mddi_client_data *client_data = dev->platform_data; + int ret; + unsigned val; + + val = 0; + val |= client_data->remote_read(client_data, 0xb101) << 8; + val |= client_data->remote_read(client_data, 0xb102); + + ret = sprintf(buf, "T2: d%u, 0x%04xh\n", val, val); + + return ret; +} + +static ssize_t nov_t2_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct msm_mddi_client_data *client_data = dev->platform_data; + unsigned val; + + sscanf(buf, "%u", &val); + + if (val <= 245 || val > 999) { + printk(KERN_WARNING "%s: invalid value for t2: %u\n", __func__, val); + return -EINVAL; + } + + client_data->remote_write(client_data, (0xff00 & val) >> 8, 0xb101); + client_data->remote_write(client_data, (0x00ff & val), 0xb102); + + return count; +} + +DEVICE_ATTR(t2, 0644, nov_t2_show, nov_t2_store); +/* maejrep's T2 interface - end */ + +static int mddi_novtec_probe(struct platform_device *pdev) +{ + int ret; + struct msm_mddi_client_data *client_data = pdev->dev.platform_data; + struct msm_mddi_bridge_platform_data *bridge_data = + client_data->private_client_data; + struct panel_data *panel_data = &bridge_data->panel_conf; + struct panel_info *panel = + kzalloc(sizeof(struct panel_info), GFP_KERNEL); + + if (!panel) + return -ENOMEM; + platform_set_drvdata(pdev, panel); + + printk(KERN_DEBUG "%s\n", __func__); + + if (panel_data->caps & MSMFB_CAP_CABC) { + printk(KERN_INFO "CABC enabled\n"); + mddi_nov_cabc.dev.platform_data = client_data; + platform_device_register(&mddi_nov_cabc); + } + + ret = setup_vsync(panel, 1); + if (ret) { + dev_err(&pdev->dev, "mddi_bridge_setup_vsync failed\n"); + return ret; + } + + panel->client_data = client_data; + panel->panel_data.suspend = novtec_suspend; + panel->panel_data.resume = novtec_resume; + panel->panel_data.wait_vsync = novtec_wait_vsync; + panel->panel_data.request_vsync = novtec_request_vsync; + panel->panel_data.clear_vsync = novtec_clear_vsync; + panel->panel_data.blank = novtec_blank; + panel->panel_data.unblank = novtec_unblank; + panel->panel_data.fb_data = &bridge_data->fb_data; + panel->panel_data.caps = MSMFB_CAP_PARTIAL_UPDATES; + + panel->pdev.name = "msm_panel"; + panel->pdev.id = pdev->id; + panel->pdev.resource = client_data->fb_resource; + panel->pdev.num_resources = 1; + panel->pdev.dev.platform_data = &panel->panel_data; + platform_device_register(&panel->pdev); + wake_lock_init(&panel->idle_lock, WAKE_LOCK_IDLE, "nov_idle_lock"); + + return 0; +} + +static int mddi_novtec_remove(struct platform_device *pdev) +{ + struct panel_info *panel = platform_get_drvdata(pdev); + + setup_vsync(panel, 0); + kfree(panel); + return 0; +} + +static struct platform_driver mddi_client_d263_0000 = { + .probe = mddi_novtec_probe, + .remove = mddi_novtec_remove, + .driver = { .name = "mddi_c_b9f6_5582" }, +}; + +static int __init mddi_client_novtec_init(void) +{ + platform_driver_register(&mddi_client_d263_0000); + return 0; +} + +module_init(mddi_client_novtec_init); + From 2020cb650bf10e3b8cdda9ab466ffc1673119fda Mon Sep 17 00:00:00 2001 From: toastcfh Date: Wed, 20 Oct 2010 17:12:21 -0400 Subject: [PATCH 1018/2556] mach-msm: AMSS 3200 and Supersonic related make changes Change-Id: I1cd1802b674079862ff11d38e44e977be3f801cd --- arch/arm/mach-msm/Kconfig | 21 ++++++++++++++++++++ arch/arm/mach-msm/Makefile | 14 +++++++++++++ arch/arm/mach-msm/rpc_server_dog_keepalive.c | 3 +++ arch/arm/mach-msm/rpc_server_time_remote.c | 2 ++ 4 files changed, 40 insertions(+) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 7a972d85b2611..bbb5e09004d1c 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -103,6 +103,7 @@ config MSM_AMSS_VERSION default 6220 if MSM_AMSS_VERSION_6220 default 6225 if MSM_AMSS_VERSION_6225 default 6350 if MSM_AMSS_VERSION_6350 + default 3200 if MSM_AMSS_VERSION_3200 choice prompt "AMSS modem firmware version" @@ -124,6 +125,9 @@ choice config MSM_AMSS_VERSION_6350 bool "6.3.50" + + config MSM_AMSS_VERSION_3200 + bool "3.2.00" endchoice menu "Qualcomm MSM Board Type" @@ -251,6 +255,13 @@ config MACH_MAHIMAHI help Select this to support the Mahi-Mahi device +config MACH_SUPERSONIC + depends on ARCH_QSD8X50 + default y + bool "Supersonic (HTC EVO 4G)" + help + Select this to support the Supersonic device + config MACH_QSD8X50_FFA depends on ARCH_QSD8X50 default y @@ -284,6 +295,16 @@ config TROUT_BATTCHG default y bool "Trout battery / charger driver" +config HTC_BATTCHG + depends on MSM_ONCRPCROUTER && POWER_SUPPLY + default n + bool "HTC battery / charger driver" + +config HTC_BATTCHG_SMEM + depends on HTC_BATTCHG + default n + bool "Read Battery Info via SMEM" + config HTC_PWRSPLY depends on MSM_ONCRPCROUTER && POWER_SUPPLY && !TROUT_BATTCHG default y diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 35afe55921efe..66d65be87bc8a 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -75,6 +75,7 @@ obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-keypad.o fish_battery.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-panel.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-mmc.o + obj-$(CONFIG_ARCH_MSM7X30) += devices-msm7x30.o obj-$(CONFIG_ARCH_QSD8X50) += devices-qsd8x50.o obj-$(CONFIG_MACH_QSD8X50_SURF) += board-qsd8x50.o @@ -95,6 +96,19 @@ else obj-y += gpio.o endif +obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic.o board-supersonic-panel.o +obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-keypad.o board-supersonic-mmc.o +obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-rfkill.o board-supersonic-audio.o +obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-wifi.o htc_awb_cal.o +obj-$(CONFIG_MACH_SUPERSONIC) += msm_vibrator.o +obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-microp.o +obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-tpa2018d1.o +obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-smb329.o +obj-$(CONFIG_MACH_SUPERSONIC) += drv_callback.o +obj-$(CONFIG_MACH_SUPERSONIC) += htc_wifi_nvs.o htc_bluetooth.o +obj-$(CONFIG_MACH_SUPERSONIC) += htc_acoustic_qsd.o +obj-$(CONFIG_MACH_SUPERSONIC) += devices_htc.o + obj-$(CONFIG_HTC_PWRSINK) += htc_pwrsink.o obj-$(CONFIG_HTC_PWRSPLY) += htc_power_supply.o obj-$(CONFIG_HTC_HEADSET) += htc_headset.o diff --git a/arch/arm/mach-msm/rpc_server_dog_keepalive.c b/arch/arm/mach-msm/rpc_server_dog_keepalive.c index b23fccfa87e29..908f1e1bae255 100644 --- a/arch/arm/mach-msm/rpc_server_dog_keepalive.c +++ b/arch/arm/mach-msm/rpc_server_dog_keepalive.c @@ -30,6 +30,9 @@ #elif CONFIG_MSM_AMSS_VERSION==6350 #define DOG_KEEPALIVE_VERS 0x00010000 #define RPC_DOG_KEEPALIVE_BEACON 2 +#elif CONFIG_MSM_AMSS_VERSION==3200 +#define DOG_KEEPALIVE_VERS 0x731fa727 /* 1931454247 */ +#define RPC_DOG_KEEPALIVE_BEACON 2 #else #error "Unsupported AMSS version" #endif diff --git a/arch/arm/mach-msm/rpc_server_time_remote.c b/arch/arm/mach-msm/rpc_server_time_remote.c index 2f90fc88c3850..c738090797e67 100644 --- a/arch/arm/mach-msm/rpc_server_time_remote.c +++ b/arch/arm/mach-msm/rpc_server_time_remote.c @@ -27,6 +27,8 @@ #define TIME_REMOTE_MTOA_VERS 0x9202a8e4 #elif CONFIG_MSM_AMSS_VERSION==6350 #define TIME_REMOTE_MTOA_VERS 0x00010000 +#elif CONFIG_MSM_AMSS_VERSION==3200 +#define TIME_REMOTE_MTOA_VERS 0x9202a8e4 /* 2449647844 */ #else #error "Unknown AMSS version" #endif From 2eea97b310523fd709478ed4bd568f050b1f60c7 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Wed, 20 Oct 2010 17:24:23 -0400 Subject: [PATCH 1019/2556] mach-msm: add Supersonic board files and defconfig --- .../arm/configs/cyanogen_supersonic_defconfig | 2023 +++++++++++++++++ arch/arm/mach-msm/board-supersonic-audio.c | 268 +++ arch/arm/mach-msm/board-supersonic-keypad.c | 96 + arch/arm/mach-msm/board-supersonic-microp.c | 437 ++++ arch/arm/mach-msm/board-supersonic-mmc.c | 453 ++++ arch/arm/mach-msm/board-supersonic-panel.c | 970 ++++++++ arch/arm/mach-msm/board-supersonic-rfkill.c | 122 + arch/arm/mach-msm/board-supersonic-smb329.c | 178 ++ arch/arm/mach-msm/board-supersonic-smb329.h | 32 + .../arm/mach-msm/board-supersonic-tpa2018d1.c | 368 +++ .../arm/mach-msm/board-supersonic-tpa2018d1.h | 35 + arch/arm/mach-msm/board-supersonic-wifi.c | 140 ++ arch/arm/mach-msm/board-supersonic.c | 1344 +++++++++++ arch/arm/mach-msm/board-supersonic.h | 195 ++ 14 files changed, 6661 insertions(+) create mode 100644 arch/arm/configs/cyanogen_supersonic_defconfig create mode 100644 arch/arm/mach-msm/board-supersonic-audio.c create mode 100644 arch/arm/mach-msm/board-supersonic-keypad.c create mode 100644 arch/arm/mach-msm/board-supersonic-microp.c create mode 100644 arch/arm/mach-msm/board-supersonic-mmc.c create mode 100644 arch/arm/mach-msm/board-supersonic-panel.c create mode 100644 arch/arm/mach-msm/board-supersonic-rfkill.c create mode 100755 arch/arm/mach-msm/board-supersonic-smb329.c create mode 100644 arch/arm/mach-msm/board-supersonic-smb329.h create mode 100644 arch/arm/mach-msm/board-supersonic-tpa2018d1.c create mode 100644 arch/arm/mach-msm/board-supersonic-tpa2018d1.h create mode 100644 arch/arm/mach-msm/board-supersonic-wifi.c create mode 100644 arch/arm/mach-msm/board-supersonic.c create mode 100644 arch/arm/mach-msm/board-supersonic.h diff --git a/arch/arm/configs/cyanogen_supersonic_defconfig b/arch/arm/configs/cyanogen_supersonic_defconfig new file mode 100644 index 0000000000000..b64e42e1e6a46 --- /dev/null +++ b/arch/arm/configs/cyanogen_supersonic_defconfig @@ -0,0 +1,2023 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.34.5 +# Mon Sep 6 22:59:00 2010 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="-cyanogenmod" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_BZIP2 is not set +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_LZO is not set +# CONFIG_SWAP is not set +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +# CONFIG_RCU_TRACE is not set +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_CGROUPS=y +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CGROUP_NS is not set +# CONFIG_CGROUP_FREEZER is not set +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +# CONFIG_RESOURCE_COUNTERS is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +# CONFIG_SYSFS_DEPRECATED_V2 is not set +# CONFIG_RELAY is not set +# CONFIG_NAMESPACES is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_ASHMEM=y +CONFIG_AIO=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_SLOW_WORK=y +# CONFIG_SLOW_WORK_DEBUG is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set +CONFIG_BLK_CGROUP=y +# CONFIG_DEBUG_BLK_CGROUP is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CFQ_GROUP_IOSCHED=y +# CONFIG_DEBUG_CFQ_IOSCHED is not set +CONFIG_IOSCHED_BFQ=y +CONFIG_CGROUP_BFQIO=y +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +CONFIG_DEFAULT_BFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="bfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +CONFIG_ARCH_MSM=y +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P6440 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC1XX is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_ARCH_MSM7X00A is not set +CONFIG_ARCH_QSD8X50=y +CONFIG_ARCH_MSM_SCORPION=y +CONFIG_MSM_MDP31=y +CONFIG_MSM_AMSS_VERSION=3200 +# CONFIG_MSM_AMSS_VERSION_6210 is not set +# CONFIG_MSM_AMSS_VERSION_6220 is not set +# CONFIG_MSM_AMSS_VERSION_6225 is not set +# CONFIG_MSM_AMSS_VERSION_6350 is not set +CONFIG_MSM_AMSS_VERSION_3200=y +# CONFIG_MSM_AMSS_SUPPORT_256MB_EBI1 is not set +CONFIG_MSM_DEBUG_UART_NONE=y +# CONFIG_MSM_DEBUG_UART1 is not set +# CONFIG_MSM_DEBUG_UART2 is not set +# CONFIG_MSM_DEBUG_UART3 is not set + +# +# MSM Board Type +# +# CONFIG_HOLES_IN_ZONE is not set +# CONFIG_MACH_SWORDFISH is not set +# CONFIG_MACH_MAHIMAHI is not set +CONFIG_MACH_SUPERSONIC=y +# CONFIG_MACH_QSD8X50_FFA is not set +CONFIG_HTC_BATTCHG=y +CONFIG_HTC_BATTCHG_SMEM=y +# CONFIG_HTC_PWRSPLY is not set +# CONFIG_HTC_PWRSINK is not set +CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_MSM7X00A_USE_GP_TIMER=y +# CONFIG_MSM7X00A_USE_DG_TIMER is not set +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=1 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_FIQ_SUPPORT=y +CONFIG_MSM_SERIAL_DEBUGGER=y +CONFIG_MSM_SERIAL_DEBUGGER_NO_SLEEP=y +# CONFIG_MSM_SERIAL_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON is not set +# CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_RPCSERVERS=y +# CONFIG_MSM_CPU_FREQ_SCREEN is not set +# CONFIG_MSM_HW3D is not set +CONFIG_MSM_QDSP6=y +CONFIG_HTC_ACOUSTIC_QSD=y +# CONFIG_MSM_CLOCK_CTRL_DEBUG is not set +CONFIG_WIFI_CONTROL_FUNC=y +# CONFIG_WIFI_MEM_PREALLOC is not set +CONFIG_ARCH_MSM_FLASHLIGHT=y +CONFIG_MICROP_COMMON=y +CONFIG_HTC_HEADSET_MGR=y +# CONFIG_HTC_HEADSET_H2W is not set +CONFIG_HTC_HEADSET_GPIO=y +CONFIG_HTC_HEADSET_MICROP=y +# CONFIG_HTC_HEADSET_PMIC is not set +# CONFIG_VIRTUAL_KPANIC_PARTITION is not set + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_VERIFY_PERMISSION_FAULT=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_HAS_TLS_REG=y +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_CPU_HAS_PMU=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_VMALLOC_RESERVE=0x08000000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +# CONFIG_CPU_FREQ_DEBUG is not set +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +# CONFIG_CONSOLE_EARLYSUSPEND is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM_OPS=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_ASK_IP_FIB_HASH=y +# CONFIG_IP_FIB_TRIE is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_IP_ROUTE_MULTIPATH is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_XFRM_TUNNEL=y +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CT_ACCT=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +# CONFIG_NF_CONNTRACK_H323 is not set +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XTABLES=y +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_LED=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +CONFIG_NETFILTER_XT_MATCH_OWNER=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +CONFIG_NETFILTER_XT_MATCH_RECENT=y +# CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +# CONFIG_NF_NAT_SNMP_BASIC is not set +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +# CONFIG_NF_NAT_H323 is not set +CONFIG_NF_NAT_SIP=y +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_TARGET_TTL is not set +# CONFIG_IP_NF_RAW is not set +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +# CONFIG_NF_CONNTRACK_IPV6 is not set +# CONFIG_IP6_NF_QUEUE is not set +# CONFIG_IP6_NF_IPTABLES is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +CONFIG_NET_SCH_INGRESS=y + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +# CONFIG_GACT_PROB is not set +CONFIG_NET_ACT_MIRRED=y +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_PRIV=y +# CONFIG_CFG80211 is not set +CONFIG_WIRELESS_EXT_SYSFS=y +# CONFIG_LIB80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# + +# +# Some wireless drivers require a rate control algorithm +# +CONFIG_WIMAX=y +CONFIG_WIMAX_DEBUG_LEVEL=8 +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_LAZYECCSTATS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_MSM_NAND=y +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set + +# +# UBI - Unsorted block images +# +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +CONFIG_MISC_DEVICES=y +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_KERNEL_DEBUGGER_CORE=y +# CONFIG_ISL29003 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +CONFIG_UID_STAT=y +# CONFIG_WL127X_RFKILL is not set +CONFIG_SENSORS_BMA150_SPI=y +# CONFIG_APANIC is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +CONFIG_DM_CRYPT_GLOBAL_WORKQUEUES=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +# CONFIG_IFB is not set +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set +# CONFIG_ENC28J60 is not set +# CONFIG_ETHOC is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8842 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +CONFIG_NETDEV_1000=y +CONFIG_NETDEV_10000=y +CONFIG_WLAN=y +# CONFIG_HOSTAP is not set +# CONFIG_TIWLAN1251 is not set +CONFIG_BCM4329=m +CONFIG_BCM4329_FW_PATH="/system/etc/firmware/fw_bcm4329.bin" +CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" + +# +# WiMAX Wireless Broadband devices +# + +# +# Enable USB support to see WiMAX USB drivers +# +# CONFIG_WIMAX_I2400M_SDIO is not set +CONFIG_WIMAX_SQN=m +# CONFIG_WAN is not set +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +# CONFIG_PPPOE is not set +# CONFIG_PPPOL2TP is not set +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL=y +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879_I2C is not set +# CONFIG_TOUCHSCREEN_AD7879_SPI is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_CYPRESS_TMG is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELAN_I2C_8232 is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_MSM is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set +CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT=y +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +CONFIG_LIGHTSENSOR_MICROP=y +# CONFIG_INPUT_CAPELLA_CM3602 is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_BCM_BT_LPM=y +# CONFIG_SERIAL_TIMBERDALE is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_MSM=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_STUB is not set + +# +# Miscellaneous I2C Chip support +# +CONFIG_SENSORS_AKM8973=y +# CONFIG_SENSORS_AKM8976 is not set +CONFIG_TPS65200=y +# CONFIG_SENSORS_MT9T013 is not set +CONFIG_VP_A1026=y +# CONFIG_SENSORS_PCA963X is not set +CONFIG_AMP_TPA6130A=y +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +CONFIG_SPI_DEBUG=y +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +CONFIG_SPI_QSD=y +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set + +# +# PPS support +# +# CONFIG_PPS is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_IT8761E is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set + +# +# AC97 GPIO expanders: +# +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_BATTERY_DS2760 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_DS2784 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13783 is not set +# CONFIG_AB3100_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_AB4500_CORE is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_LP3971 is not set +CONFIG_REGULATOR_TPS65023=y +# CONFIG_REGULATOR_TPS6507X is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +# CONFIG_IR_CORE is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +# CONFIG_MSM_CAMERA_OLD is not set +CONFIG_MSM_CAMERA_LEGACY=y +# CONFIG_MSM_CAMERA_7X30 is not set +CONFIG_720P_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set + +# +# Camera Sensor Selection +# +# CONFIG_MT9T013 is not set +# CONFIG_MT9D112 is not set +# CONFIG_MT9P012 is not set +# CONFIG_S5K3E2FX is not set +# CONFIG_S5K4E1GX is not set +CONFIG_OV8810=y +CONFIG_OV9665=y +# CONFIG_S5K3H1GX is not set +# CONFIG_MT9V113 is not set +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_LCDC=y +CONFIG_GPU_MSM_KGSL=y +CONFIG_MSM_KGSL_MMU=y +CONFIG_MSM_HDMI=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set +# CONFIG_HID_PID is not set + +# +# Special HID drivers +# +CONFIG_HID_APPLE=y +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_WACOM is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_GADGET_MUSB_HDRC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_AT91 is not set +# CONFIG_USB_GADGET_ATMEL_USBA is not set +# CONFIG_USB_GADGET_FSL_USB2 is not set +# CONFIG_USB_GADGET_LH7A40X is not set +# CONFIG_USB_GADGET_OMAP is not set +# CONFIG_USB_GADGET_PXA25X is not set +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA27X is not set +# CONFIG_USB_GADGET_S3C_HSOTG is not set +# CONFIG_USB_GADGET_IMX is not set +# CONFIG_USB_GADGET_S3C2410 is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_FSL_QE is not set +# CONFIG_USB_GADGET_CI13XXX is not set +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_LANGWELL is not set +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_MSM_72K=y +# CONFIG_USB_GADGET_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_ANDROID=y +# CONFIG_USB_ANDROID_ACM is not set +CONFIG_USB_ANDROID_ADB=y +# CONFIG_USB_ANDROID_DIAG is not set +CONFIG_USB_ANDROID_MASS_STORAGE=y +CONFIG_USB_ANDROID_RNDIS=y +CONFIG_USB_ANDROID_RNDIS_WCEIS=y +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_NOKIA is not set +# CONFIG_USB_G_MULTI is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_MSM7X00A=y +# CONFIG_MMC_SPI is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +CONFIG_LEDS_CPLD=y +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +CONFIG_LEDS_TRIGGER_SLEEP=y + +# +# iptables trigger is under Netfilter config (LED target) +# +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_MSM7X00A=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set + +# +# TI VLYNQ +# +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_ECHO is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE=128 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE=16 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE=8 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y + +# +# Qualcomm MSM Camera And Video +# + +# +# Camera Sensor Selection +# +# CONFIG_POHMELFS is not set + +# +# RAR Register Driver +# +# CONFIG_RAR_REGISTER is not set +# CONFIG_IIO is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_STRIP is not set +# CONFIG_FB_SM7XX is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +CONFIG_FS_POSIX_ACL=y +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +# CONFIG_DNOTIFY is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=m +# CONFIG_CUSE is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_EMBEDDED=y +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_AUFS_FS=y +CONFIG_AUFS_BRANCH_MAX_127=y +# CONFIG_AUFS_BRANCH_MAX_511 is not set +# CONFIG_AUFS_BRANCH_MAX_1023 is not set +# CONFIG_AUFS_BRANCH_MAX_32767 is not set +# CONFIG_AUFS_HNOTIFY is not set +# CONFIG_AUFS_RDU is not set +# CONFIG_AUFS_SP_IATTR is not set +# CONFIG_AUFS_SHWH is not set +# CONFIG_AUFS_BR_RAMFS is not set +# CONFIG_AUFS_BR_FUSE is not set +CONFIG_AUFS_BDEV_LOOP=y +# CONFIG_AUFS_DEBUG is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=m +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +# CONFIG_NFS_V4_1 is not set +CONFIG_NFSD=m +CONFIG_NFSD_V2_ACL=y +CONFIG_NFSD_V3=y +CONFIG_NFSD_V3_ACL=y +CONFIG_NFSD_V4=y +CONFIG_LOCKD=m +CONFIG_LOCKD_V4=y +CONFIG_EXPORTFS=m +CONFIG_NFS_ACL_SUPPORT=m +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=m +CONFIG_SUNRPC_GSS=m +CONFIG_RPCSEC_GSS_KRB5=m +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=m +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_WEAK_PW_HASH=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +CONFIG_ARM_UNWIND=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_DEFAULT_SECURITY_SELINUX is not set +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_HW is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/arch/arm/mach-msm/board-supersonic-audio.c b/arch/arm/mach-msm/board-supersonic-audio.c new file mode 100644 index 0000000000000..d8f25dd33a182 --- /dev/null +++ b/arch/arm/mach-msm/board-supersonic-audio.c @@ -0,0 +1,268 @@ +/* arch/arm/mach-msm/board-supersonic-audio.c + * + * Copyright (C) 2009 HTC Corporation + * Copyright (C) 2009 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 +#include +#include + +#include "board-supersonic.h" +#include "proc_comm.h" +#include "pmic.h" +#include "board-supersonic-tpa2018d1.h" + +#if 0 +#define D(fmt, args...) printk(KERN_INFO "Audio: "fmt, ##args) +#else +#define D(fmt, args...) do {} while (0) +#endif + +static struct mutex mic_lock; +static struct mutex bt_sco_lock; +static int headset_status = 0; + +static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { + [Q6_HW_HANDSET] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_HEADSET] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_SPEAKER] = { + .min_gain = -1500, + .max_gain = 0, + }, + [Q6_HW_TTY] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_BT_SCO] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_BT_A2DP] = { + .min_gain = -2000, + .max_gain = 0, + }, +}; + +void supersonic_headset_enable(int en) +{ + D("%s %d\n", __func__, en); + /* enable audio amp */ + if (en) mdelay(15); + gpio_set_value(SUPERSONIC_AUD_JACKHP_EN, !!en); +} + +void supersonic_speaker_enable(int en) +{ + struct spkr_config_mode scm; + memset(&scm, 0, sizeof(scm)); + + D("%s %d\n", __func__, en); + if (en) { + scm.is_right_chan_en = 0; + scm.is_left_chan_en = 1; + scm.is_stereo_en = 0; + scm.is_hpf_en = 1; + pmic_spkr_en_mute(LEFT_SPKR, 0); + pmic_set_spkr_configuration(&scm); + pmic_spkr_en(LEFT_SPKR, 1); + + /* unmute */ + pmic_spkr_en_mute(LEFT_SPKR, 1); + } else { + pmic_spkr_en_mute(LEFT_SPKR, 0); + + pmic_spkr_en(LEFT_SPKR, 0); + + pmic_set_spkr_configuration(&scm); + } + tpa2018d1_set_speaker_amp(en); +} + +void supersonic_receiver_enable(int en) +{ + /* After XB*/ + if (system_rev >= 1) { + struct spkr_config_mode scm; + memset(&scm, 0, sizeof(scm)); + + D("%s %d\n", __func__, en); + if (en) { + scm.is_right_chan_en = 1; + scm.is_left_chan_en = 0; + scm.is_stereo_en = 0; + scm.is_hpf_en = 1; + pmic_spkr_en_mute(RIGHT_SPKR, 0); + pmic_set_spkr_configuration(&scm); + pmic_spkr_en(RIGHT_SPKR, 1); + + /* unmute */ + pmic_spkr_en_mute(RIGHT_SPKR, 1); + } else { + pmic_spkr_en_mute(RIGHT_SPKR, 0); + + pmic_spkr_en(RIGHT_SPKR, 0); + + pmic_set_spkr_configuration(&scm); + } + } +} + +static uint32_t bt_sco_enable[] = { + PCOM_GPIO_CFG(SUPERSONIC_BT_PCM_OUT, 1, GPIO_INPUT, + GPIO_PULL_DOWN, GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_BT_PCM_IN, 1, GPIO_INPUT, + GPIO_PULL_DOWN, GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_BT_PCM_SYNC, 2, GPIO_INPUT, + GPIO_PULL_DOWN, GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_BT_PCM_CLK, 2, GPIO_INPUT, + GPIO_PULL_DOWN, GPIO_2MA), +}; + +static uint32_t bt_sco_disable[] = { + PCOM_GPIO_CFG(SUPERSONIC_BT_PCM_OUT, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_BT_PCM_IN, 0, GPIO_INPUT, + GPIO_PULL_UP, GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_BT_PCM_SYNC, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_BT_PCM_CLK, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), +}; + +void supersonic_bt_sco_enable(int en) +{ + static int bt_sco_refcount; + D("%s %d\n", __func__, en); + mutex_lock(&bt_sco_lock); + if (en) { + if (++bt_sco_refcount == 1) + config_gpio_table(bt_sco_enable, + ARRAY_SIZE(bt_sco_enable)); + } else { + if (--bt_sco_refcount == 0) { + config_gpio_table(bt_sco_disable, ARRAY_SIZE(bt_sco_disable)); + gpio_set_value(SUPERSONIC_BT_PCM_OUT, 0); + gpio_set_value(SUPERSONIC_BT_PCM_SYNC,0); + gpio_set_value(SUPERSONIC_BT_PCM_CLK,0); + } + } + mutex_unlock(&bt_sco_lock); +} + +void supersonic_int_mic_enable(int en) +{ + D("%s %d\n", __func__, en); + if (en) + pmic_mic_en(ON_CMD); + else + pmic_mic_en(OFF_CMD); +} + +void supersonic_ext_mic_enable(int en) +{ + static int old_state = 0, new_state = 0; + + D("%s %d\n", __func__, en); + + mutex_lock(&mic_lock); + if (!!en) + new_state++; + else + new_state--; + + if (new_state == 1 && old_state == 0) { + gpio_set_value(SUPERSONIC_AUD_2V5_EN, 1); + } else if (new_state == 0 && old_state == 1) + gpio_set_value(SUPERSONIC_AUD_2V5_EN, 0); + else + D("%s: do nothing %d %d\n", __func__, old_state, new_state); + + old_state = new_state; + mutex_unlock(&mic_lock); +} + +void supersonic_analog_init(void) +{ + D("%s\n", __func__); + /* stereo pmic init */ + pmic_spkr_set_gain(LEFT_SPKR, SPKR_GAIN_PLUS12DB); + pmic_spkr_set_gain(RIGHT_SPKR, SPKR_GAIN_00DB); + pmic_spkr_en_right_chan(OFF_CMD); + pmic_spkr_en_left_chan(OFF_CMD); + pmic_spkr_add_right_left_chan(OFF_CMD); + pmic_spkr_en_stereo(OFF_CMD); + pmic_spkr_select_usb_with_hpf_20hz(OFF_CMD); + pmic_spkr_bypass_mux(OFF_CMD); + pmic_spkr_en_hpf(ON_CMD); + pmic_spkr_en_sink_curr_from_ref_volt_cir(OFF_CMD); + pmic_spkr_set_mux_hpf_corner_freq(SPKR_FREQ_0_73KHZ); + pmic_mic_set_volt(MIC_VOLT_1_80V); + pmic_set_speaker_delay(SPKR_DLY_100MS); + + gpio_request(SUPERSONIC_AUD_JACKHP_EN, "aud_jackhp_en"); + gpio_direction_output(SUPERSONIC_AUD_JACKHP_EN, 0); + gpio_set_value(SUPERSONIC_AUD_JACKHP_EN, 0); + + mutex_lock(&bt_sco_lock); + config_gpio_table(bt_sco_disable, ARRAY_SIZE(bt_sco_disable)); + gpio_set_value(SUPERSONIC_BT_PCM_OUT, 0); + gpio_set_value(SUPERSONIC_BT_PCM_SYNC,0); + gpio_set_value(SUPERSONIC_BT_PCM_CLK,0); + mutex_unlock(&bt_sco_lock); +} + +int supersonic_get_rx_vol(uint8_t hw, int level) +{ + struct q6_hw_info *info; + int vol; + + info = &q6_audio_hw[hw]; + vol = info->min_gain + ((info->max_gain - info->min_gain) * level) / 100; + D("%s %d\n", __func__, vol); + return vol; +} + +static struct qsd_acoustic_ops acoustic = { + .enable_mic_bias = supersonic_ext_mic_enable, +}; + +static struct q6audio_analog_ops ops = { + .init = supersonic_analog_init, + .speaker_enable = supersonic_speaker_enable, + .headset_enable = supersonic_headset_enable, + .receiver_enable = supersonic_receiver_enable, + .bt_sco_enable = supersonic_bt_sco_enable, + .int_mic_enable = supersonic_int_mic_enable, + .ext_mic_enable = supersonic_ext_mic_enable, + .get_rx_vol = supersonic_get_rx_vol, +}; + +void __init supersonic_audio_init(void) +{ + mutex_init(&mic_lock); + mutex_init(&bt_sco_lock); + q6audio_register_analog_ops(&ops); + acoustic_register_ops(&acoustic); +} + diff --git a/arch/arm/mach-msm/board-supersonic-keypad.c b/arch/arm/mach-msm/board-supersonic-keypad.c new file mode 100644 index 0000000000000..3132719c41d2c --- /dev/null +++ b/arch/arm/mach-msm/board-supersonic-keypad.c @@ -0,0 +1,96 @@ +/* arch/arm/mach-msm/board-supersonic-keypad.c + * + * Copyright (C) 2009 Google, Inc + * Copyright (C) 2009 HTC Corporation. + * + * Author: Dima Zavin + * + * 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 "board-supersonic.h" + + +static struct gpio_event_direct_entry supersonic_keypad_nav_map[] = { + { SUPERSONIC_POWER_KEY, KEY_POWER }, + { SUPERSONIC_VOLUME_UP, KEY_VOLUMEUP }, + { SUPERSONIC_VOLUME_DOWN, KEY_VOLUMEDOWN }, +}; + +static struct gpio_event_input_info supersonic_keypad_nav_info = { + .info.func = gpio_event_input_func, + .flags = GPIOEDF_PRINT_KEYS, + .type = EV_KEY, + .keymap = supersonic_keypad_nav_map, + .debounce_time.tv.nsec = 20 * NSEC_PER_MSEC, + .keymap_size = ARRAY_SIZE(supersonic_keypad_nav_map) +}; + +static struct gpio_event_info *supersonic_keypad_info[] = { + &supersonic_keypad_nav_info.info, +}; + +static struct gpio_event_platform_data supersonic_keypad_data = { + .name = "supersonic-keypad", + .info = supersonic_keypad_info, + .info_count = ARRAY_SIZE(supersonic_keypad_info) +}; + +static struct platform_device supersonic_keypad_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = 0, + .dev = { + .platform_data = &supersonic_keypad_data, + }, +}; + +static struct keyreset_platform_data supersonic_reset_keys_pdata = { + .keys_down = { + KEY_POWER, + KEY_VOLUMEUP, + KEY_VOLUMEDOWN, + 0 + }, +}; + +static struct platform_device supersonic_reset_keys_device = { + .name = KEYRESET_NAME, + .dev.platform_data = &supersonic_reset_keys_pdata, +}; + +static int __init supersonic_init_keypad(void) +{ + int ret; + + if (!machine_is_supersonic()) + return 0; + + if (platform_device_register(&supersonic_reset_keys_device)) + printk(KERN_WARNING "%s: register reset key fail\n", __func__); + + ret = platform_device_register(&supersonic_keypad_device); + if (ret != 0) + return ret; + + return 0; +} + +device_initcall(supersonic_init_keypad); + + diff --git a/arch/arm/mach-msm/board-supersonic-microp.c b/arch/arm/mach-msm/board-supersonic-microp.c new file mode 100644 index 0000000000000..8b89bd3b34c86 --- /dev/null +++ b/arch/arm/mach-msm/board-supersonic-microp.c @@ -0,0 +1,437 @@ +/* arch/arm/mach-msm/board-supersonic-microp.c + * Copyright (C) 2009 HTC Corporation. + * + * 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. +*/ +#ifdef CONFIG_MICROP_COMMON +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-supersonic.h" + +#define INT_PSENSOR (1<<4) + +static int misc_opened; +static unsigned int als_power_control; +static struct mutex als_power_lock; + +static void p_sensor_do_work(struct work_struct *w); +static DECLARE_WORK(p_sensor_work, p_sensor_do_work); + +struct wake_lock proximity_wake_lock; + +static struct capella_cm3602_data { + struct input_dev *input_dev; + struct capella_cm3602_platform_data *pdata; + int enabled; + struct workqueue_struct *p_sensor_wq; +} the_data; + +static int psensor_intr_enable(uint8_t enable) +{ + int ret; + uint8_t addr, data[2]; + + if (enable) + addr = MICROP_I2C_WCMD_GPI_INT_CTL_EN; + else + addr = MICROP_I2C_WCMD_GPI_INT_CTL_DIS; + + data[0] = INT_PSENSOR >> 8; + data[1] = INT_PSENSOR & 0xFF; + ret = microp_i2c_write(addr, data, 2); + if (ret < 0) + pr_err("%s: %s p-sensor interrupt failed\n", + __func__, (enable ? "enable" : "disable")); + + return ret; +} + +static int supersonic_microp_function_init(struct i2c_client *client) +{ + struct microp_i2c_platform_data *pdata; + struct microp_i2c_client_data *cdata; + uint8_t data[20]; + int i, j; + int ret; + + pdata = client->dev.platform_data; + cdata = i2c_get_clientdata(client); + + /* Headset remote key */ + ret = microp_function_check(client, MICROP_FUNCTION_REMOTEKEY); + if (ret >= 0) { + i = ret; + pdata->function_node[MICROP_FUNCTION_REMOTEKEY] = i; + cdata->int_pin.int_remotekey = + pdata->microp_function[i].int_pin; + + for (j = 0; j < 6; j++) { + data[j] = (uint8_t)(pdata->microp_function[i].levels[j] >> 8); + data[j + 6] = (uint8_t)(pdata->microp_function[i].levels[j]); + } + ret = microp_i2c_write(MICROP_I2C_WCMD_REMOTEKEY_TABLE, + data, 12); + if (ret) + goto exit; + } + + /* Reset button interrupt */ + ret = microp_write_interrupt(client, (1<<8), 1); + if (ret) + goto exit; + + /* HDMI interrupt */ + ret = microp_write_interrupt(client, (1 << 1), 1); + if (ret) + goto exit; + + return 0; + +exit: + return ret; +} + +static int report_psensor_data(void) +{ + int ret, ps_data = 0; + uint8_t data[3] = {0, 0, 0}; + + ret = microp_i2c_read(MICROP_I2C_RCMD_GPIO_STATUS, data, 3); + if (ret < 0) + pr_err("%s: read data failed\n", __func__); + else { + ps_data = (data[2] & 0x10) ? 1 : 0; + pr_info("proximity %s\n", ps_data ? "FAR" : "NEAR"); + + /* 0 is close, 1 is far */ + input_report_abs(the_data.input_dev, ABS_DISTANCE, ps_data); + input_sync(the_data.input_dev); + + wake_lock_timeout(&proximity_wake_lock, 2*HZ); + } + + return ret; +} + +static int capella_cm3602_enable(struct capella_cm3602_data *data) +{ + int rc; + pr_info("%s\n", __func__); + if (data->enabled) { + pr_info("%s: already enabled\n", __func__); + return 0; + } + + /* dummy report */ + input_report_abs(data->input_dev, ABS_DISTANCE, -1); + input_sync(data->input_dev); + + rc = data->pdata->power(PS_PWR_ON, 1); + if (rc < 0) + return -EIO; + + rc = gpio_direction_output(data->pdata->p_en, 0); + if (rc < 0) { + pr_err("%s: set psesnor enable failed!!", + __func__); + return -EIO; + } + msleep(220); + rc = psensor_intr_enable(1); + if (rc < 0) + return -EIO; + + data->enabled = 1; + report_psensor_data(); + + return rc; +} + +static int capella_cm3602_disable(struct capella_cm3602_data *data) +{ + int rc = -EIO; + pr_info("%s\n", __func__); + if (!data->enabled) { + pr_info("%s: already disabled\n", __func__); + return 0; + } + + rc = psensor_intr_enable(0); + if (rc < 0) + return -EIO; + + rc = gpio_direction_output(data->pdata->p_en, 1); + if (rc < 0) { + pr_err("%s: set GPIO failed!!", __func__); + return -EIO; + } + + rc = data->pdata->power(PS_PWR_ON, 0); + if (rc < 0) + return -EIO; + + data->enabled = 0; + return rc; +} + +static ssize_t capella_cm3602_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + + ret = sprintf(buf, "proximity enabled = %d\n", the_data.enabled); + + return ret; +} + +static ssize_t capella_cm3602_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count + ) +{ + ssize_t val; + + val = -1; + sscanf(buf, "%u", &val); + if (val < 0 || val > 1) + return -EINVAL; + + /* Enable capella_cm3602*/ + if (val == 1) + capella_cm3602_enable(&the_data); + + /* Disable capella_cm3602*/ + if (val == 0) + capella_cm3602_disable(&the_data); + + return count; +} + +static DEVICE_ATTR(proximity, 0644, capella_cm3602_show, capella_cm3602_store); + +static int capella_cm3602_open(struct inode *inode, struct file *file) +{ + pr_info("%s\n", __func__); + if (misc_opened) + return -EBUSY; + misc_opened = 1; + return 0; +} + +static int capella_cm3602_release(struct inode *inode, struct file *file) +{ + pr_info("%s\n", __func__); + misc_opened = 0; + return capella_cm3602_disable(&the_data); +} + +static long capella_cm3602_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int val; + pr_info("%s cmd %d\n", __func__, _IOC_NR(cmd)); + switch (cmd) { + case CAPELLA_CM3602_IOCTL_ENABLE: + if (get_user(val, (unsigned long __user *)arg)) + return -EFAULT; + if (val) + return capella_cm3602_enable(&the_data); + else + return capella_cm3602_disable(&the_data); + break; + case CAPELLA_CM3602_IOCTL_GET_ENABLED: + return put_user(the_data.enabled, (unsigned long __user *)arg); + break; + default: + pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd)); + return -EINVAL; + } +} + +static void p_sensor_do_work(struct work_struct *w) +{ + report_psensor_data(); +} + +static irqreturn_t p_sensor_irq_handler(int irq, void *data) +{ + struct capella_cm3602_data *ip = data; + queue_work(ip->p_sensor_wq, &p_sensor_work); + + return IRQ_HANDLED; +} + +static struct file_operations capella_cm3602_fops = { + .owner = THIS_MODULE, + .open = capella_cm3602_open, + .release = capella_cm3602_release, + .unlocked_ioctl = capella_cm3602_ioctl +}; + +static struct miscdevice capella_cm3602_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "cm3602", + .fops = &capella_cm3602_fops +}; + +static int capella_cm3602_probe(struct platform_device *pdev) +{ + int rc = -1; + struct input_dev *input_dev; + struct capella_cm3602_data *ip; + struct capella_cm3602_platform_data *pdata; + + struct class *proximity_attr_class; + struct device *proximity_attr_dev; + + pr_info("%s: probe\n", __func__); + + pdata = pdev->dev.platform_data; + + ip = &the_data; + platform_set_drvdata(pdev, ip); + + input_dev = input_allocate_device(); + if (!input_dev) { + pr_err("%s: could not allocate input device\n", __func__); + rc = -ENOMEM; + goto done; + } + ip->input_dev = input_dev; + ip->pdata = pdata; + input_set_drvdata(input_dev, ip); + + input_dev->name = "proximity"; + + set_bit(EV_ABS, input_dev->evbit); + input_set_abs_params(input_dev, ABS_DISTANCE, 0, 1, 0, 0); + + rc = input_register_device(input_dev); + if (rc < 0) { + pr_err("%s: could not register input device\n", __func__); + goto err_free_input_device; + } + + rc = misc_register(&capella_cm3602_misc); + if (rc < 0) { + pr_err("%s: could not register misc device\n", __func__); + goto err_unregister_input_device; + } + + wake_lock_init(&proximity_wake_lock, WAKE_LOCK_SUSPEND, "proximity"); + + proximity_attr_class = class_create(THIS_MODULE, "sensors"); + if (IS_ERR(proximity_attr_class)) { + pr_err("%s: class_create failed\n", __func__); + rc = PTR_ERR(proximity_attr_class); + proximity_attr_class = NULL; + goto err_create_class; + } + + proximity_attr_dev = device_create(proximity_attr_class, + NULL, 0, "%s", "proximity_sensor"); + if (unlikely(IS_ERR(proximity_attr_dev))) { + pr_err("%s: device create failed\n", __func__); + rc = PTR_ERR(proximity_attr_dev); + proximity_attr_dev = NULL; + goto err_create_proximity_attr_device; + } + + rc = device_create_file(proximity_attr_dev, &dev_attr_proximity); + if (rc) { + pr_err("%s: device_create_file failed\n", __func__); + goto err_create_proximity_device_file; + } + + ip->p_sensor_wq = create_workqueue("p-sensor_microp_wq"); + if (ip->p_sensor_wq == NULL) { + pr_err("%s: create_workqueue failed\n", __func__); + goto err_create_workqueue; + } + + rc = gpio_request(pdata->p_en, "gpio_proximity_en"); + if (rc < 0) { + pr_err("%s: gpio %d request failed (%d)\n", + __func__, pdata->p_en, rc); + goto err_request_proximity_en; + } + + rc = request_irq(pdata->p_out, p_sensor_irq_handler, + IRQF_TRIGGER_NONE, "p-sensor_microp", ip); + if (rc < 0) { + pr_err("%s: request_irq(%d) failed for (%d)\n", + __func__, pdata->p_out, rc); + goto err_request_proximity_irq; + } + + goto done; + +err_request_proximity_irq: + gpio_free(pdata->p_en); +err_request_proximity_en: + destroy_workqueue(ip->p_sensor_wq); +err_create_workqueue: + device_remove_file(proximity_attr_dev, &dev_attr_proximity); +err_create_proximity_device_file: + device_unregister(proximity_attr_dev); +err_create_proximity_attr_device: + class_destroy(proximity_attr_class); +err_create_class: + misc_deregister(&capella_cm3602_misc); +err_unregister_input_device: + input_unregister_device(input_dev); +err_free_input_device: + input_free_device(input_dev); +done: + return rc; +} + +static struct microp_ops ops = { + .init_microp_func = supersonic_microp_function_init, +}; + +void __init supersonic_microp_init(void) +{ + microp_register_ops(&ops); +} + +static struct platform_driver capella_cm3602_driver = { + .probe = capella_cm3602_probe, + .driver = { + .name = "supersonic_proximity", + .owner = THIS_MODULE + }, +}; + +static int __init supersonic_capella_cm3602_init(void) +{ + if (!machine_is_supersonic()) + return 0; + + return platform_driver_register(&capella_cm3602_driver); +} + +device_initcall(supersonic_capella_cm3602_init); + +#endif diff --git a/arch/arm/mach-msm/board-supersonic-mmc.c b/arch/arm/mach-msm/board-supersonic-mmc.c new file mode 100644 index 0000000000000..fb58d9a5b100b --- /dev/null +++ b/arch/arm/mach-msm/board-supersonic-mmc.c @@ -0,0 +1,453 @@ +/* linux/arch/arm/mach-msm/board-supersonic-mmc.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * + * 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 "board-supersonic.h" +#include "devices.h" +#include "proc_comm.h" + +#define DEBUG_SDSLOT_VDD 1 + +static bool opt_disable_sdcard; +static int __init supersonic_disablesdcard_setup(char *str) +{ + opt_disable_sdcard = (bool)simple_strtol(str, NULL, 0); + return 1; +} + +__setup("board_supersonic.disable_sdcard=", supersonic_disablesdcard_setup); + +static uint32_t sdcard_on_gpio_table[] = { + PCOM_GPIO_CFG(62, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(63, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(64, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ +}; + +static uint32_t sdcard_off_gpio_table[] = { + PCOM_GPIO_CFG(62, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(63, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(64, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ +}; + +static struct vreg *sdslot_vreg; +static uint32_t sdslot_vdd = 0xffffffff; +static uint32_t sdslot_vreg_enabled; + +static struct { + int mask; + int level; +} mmc_vdd_table[] = { + { MMC_VDD_28_29, 2850 }, + { MMC_VDD_29_30, 2900 }, +}; + +static uint32_t supersonic_sdslot_switchvdd(struct device *dev, unsigned int vdd) +{ + int i; + int ret; + + if (vdd == sdslot_vdd) + return 0; + + sdslot_vdd = vdd; + + if (vdd == 0) { +#if DEBUG_SDSLOT_VDD + printk(KERN_INFO "%s: Disabling SD slot power\n", __func__); +#endif + config_gpio_table(sdcard_off_gpio_table, + ARRAY_SIZE(sdcard_off_gpio_table)); + vreg_disable(sdslot_vreg); + sdslot_vreg_enabled = 0; + return 0; + } + + if (!sdslot_vreg_enabled) { + mdelay(5); + ret = vreg_enable(sdslot_vreg); + if (ret) + pr_err("%s: Error enabling vreg (%d)\n", __func__, ret); + udelay(500); + config_gpio_table(sdcard_on_gpio_table, + ARRAY_SIZE(sdcard_on_gpio_table)); + sdslot_vreg_enabled = 1; + } + + for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) { + if (mmc_vdd_table[i].mask != (1 << vdd)) + continue; + ret = vreg_set_level(sdslot_vreg, mmc_vdd_table[i].level); + if (ret) + pr_err("%s: Error setting level (%d)\n", __func__, ret); +#if DEBUG_SDSLOT_VDD + printk(KERN_INFO "%s: Setting level to %u (%s)\n", + __func__, mmc_vdd_table[i].level, + ret?"Failed":"Success"); +#endif + return 0; + } + + pr_err("%s: Invalid VDD (%d) specified\n", __func__, vdd); + return 0; +} + +static unsigned int supersonic_sdslot_status(struct device *dev) +{ + return (system_rev > 0)?1:!gpio_get_value(SUPERSONIC_GPIO_SDMC_CD_N); +} + +#define SUPERSONIC_MMC_VDD (MMC_VDD_28_29 | MMC_VDD_29_30) + +static unsigned int supersonic_sdslot_type = MMC_TYPE_SD; + +static struct mmc_platform_data supersonic_sdslot_data = { + .ocr_mask = SUPERSONIC_MMC_VDD, + .status = supersonic_sdslot_status, + .translate_vdd = supersonic_sdslot_switchvdd, +// .slot_type = &supersonic_sdslot_type, +}; + +int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, + unsigned int stat_irq, unsigned long stat_irq_flags); + + + +/* ---- WIFI ---- */ + +static uint32_t wifi_on_gpio_table[] = { + PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(152, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +static uint32_t wifi_off_gpio_table[] = { + PCOM_GPIO_CFG(51, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(56, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(152, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* WLAN IRQ */ +}; + +/* BCM4329 returns wrong sdio_vsn(1) when we read cccr, + * we use predefined value (sdio_vsn=2) here to initial sdio driver well + */ +static struct embedded_sdio_data supersonic_wifi_emb_data = { + .cccr = { + .sdio_vsn = 2, + .multi_block = 1, + .low_speed = 0, + .wide_bus = 0, + .high_power = 1, + .high_speed = 1, + }, +}; + +static void (*wifi_status_cb)(int card_present, void *dev_id); +static void *wifi_status_cb_devid; + +static int +supersonic_wifi_status_register(void (*callback)(int card_present, void *dev_id), + void *dev_id) +{ + if (wifi_status_cb) + return -EAGAIN; + wifi_status_cb = callback; + wifi_status_cb_devid = dev_id; + return 0; +} + +static int supersonic_wifi_cd; /* WiFi virtual 'card detect' status */ + +static unsigned int supersonic_wifi_status(struct device *dev) +{ + return supersonic_wifi_cd; +} + +static struct mmc_platform_data supersonic_wifi_data = { + .ocr_mask = MMC_VDD_28_29, + .status = supersonic_wifi_status, + .register_status_notify = supersonic_wifi_status_register, + .embedded_sdio = &supersonic_wifi_emb_data, +}; + +int supersonic_wifi_set_carddetect(int val) +{ + printk(KERN_INFO "%s: %d\n", __func__, val); + supersonic_wifi_cd = val; + if (wifi_status_cb) + wifi_status_cb(val, wifi_status_cb_devid); + else + printk(KERN_WARNING "%s: Nobody to notify\n", __func__); + return 0; +} +EXPORT_SYMBOL(supersonic_wifi_set_carddetect); + +int supersonic_wifi_power(int on) +{ + int rc = 0; + + printk(KERN_INFO "%s: %d\n", __func__, on); + + if (on) { + config_gpio_table(wifi_on_gpio_table, + ARRAY_SIZE(wifi_on_gpio_table)); + mdelay(50); + if (rc) + return rc; + } else { + config_gpio_table(wifi_off_gpio_table, + ARRAY_SIZE(wifi_off_gpio_table)); + } + + mdelay(100); + gpio_set_value(SUPERSONIC_GPIO_WIFI_SHUTDOWN_N, on); /* WIFI_SHUTDOWN */ + mdelay(100); + return 0; +} +EXPORT_SYMBOL(supersonic_wifi_power); + +int supersonic_wifi_reset(int on) +{ + printk(KERN_INFO "%s: do nothing\n", __func__); + return 0; +} + + +/* ---------------- WiMAX GPIO Settings --------------- */ +static uint32_t wimax_power_pin_gpio_table[] = { + PCOM_GPIO_CFG(48, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + PCOM_GPIO_CFG(106, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + PCOM_GPIO_CFG(154, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + PCOM_GPIO_CFG(155, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + PCOM_GPIO_CFG(156, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA) +}; + +static uint32_t wimax_on_gpio_table[] = { + PCOM_GPIO_CFG(88, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(89, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(90, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT3 */ + PCOM_GPIO_CFG(91, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT2 */ + PCOM_GPIO_CFG(92, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT1 */ + PCOM_GPIO_CFG(93, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT0 */ + /*WiMax_Host_2*/ + PCOM_GPIO_CFG(159, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), +}; + +static uint32_t wimax_off_gpio_table[] = { + PCOM_GPIO_CFG(88, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(89, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(90, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT3 */ + PCOM_GPIO_CFG(91, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT2 */ + PCOM_GPIO_CFG(92, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT1 */ + PCOM_GPIO_CFG(93, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT0 */ + /*WiMax_Host_2*/ + PCOM_GPIO_CFG(159, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), +}; + + +static void (*wimax_status_cb)(int card_present, void *dev_id); +static void *wimax_status_cb_devid; +static int supersonic_wimax_cd = 0; +static int supersonic_wimax_sdio_status = 0; + +static int supersonic_wimax_status_register(void (*callback)(int card_present, void *dev_id), void *dev_id) +{ + if (wimax_status_cb) + return -EAGAIN; + printk("%s\n", __func__); + wimax_status_cb = callback; + wimax_status_cb_devid = dev_id; + return 0; +} + +static unsigned int supersonic_wimax_status(struct device *dev) +{ + printk("%s\n", __func__); + return supersonic_wimax_cd; +} + +void supersonic_wimax_set_carddetect(int val) +{ + printk("%s: %d\n", __func__, val); + supersonic_wimax_cd = val; + if (wimax_status_cb) { + wimax_status_cb(val, wimax_status_cb_devid); + } else + printk(KERN_WARNING "%s: Nobody to notify\n", __func__); +} +EXPORT_SYMBOL(supersonic_wimax_set_carddetect); + +static struct mmc_platform_data supersonic_wimax_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30, + .status = supersonic_wimax_status, + .register_status_notify = supersonic_wimax_status_register, + .embedded_sdio = NULL, +}; + +struct _vreg +{ + const char *name; + unsigned id; +}; + + +/* 2 : wimax UART, 1 : CPU uart, 0 : usb +CPU_WIMAX_SW -> GPIO160 (SUPERSONIC_WIMAX_CPU_UARTz_SW) +USB_UART#_SW -> GPIO33 (SUPERSONIC_USB_UARTz_SW) + +XA : GPIO33 = 0 -> USB + GPIO33 = 1 -> CPU UART + +XB : GPIO33 = 0 -> USB + GPIO33 = 1 , GPIO160 = 0 -> CPU UART + GPIO33 = 1 , GPIO160 = 1 -> Wimax UART +*/ +int supersonic_wimax_uart_switch(int uart) +{ + printk("%s uart:%d\n", __func__, uart); + + gpio_set_value(SUPERSONIC_USB_UARTz_SW, uart?1:0); + if(system_rev && uart) + gpio_set_value(SUPERSONIC_WIMAX_CPU_UARTz_SW, uart==2?1:0); + return uart?1:0; +} +EXPORT_SYMBOL(supersonic_wimax_uart_switch); + +int supersonic_wimax_power(int on) +{ + printk("%s\n", __func__); + + if (on) { + /*Power ON sequence*/ + gpio_set_value(154, 1); + gpio_set_value(48, 1); + mdelay(5); + gpio_set_value(106, 0); + gpio_set_value(156, 1); + gpio_set_value(155, 1); + mdelay(5); + gpio_set_value(106, 1); + mdelay(1150); + + config_gpio_table(wimax_on_gpio_table, + ARRAY_SIZE(wimax_on_gpio_table)); + } else { + /*Power OFF sequence*/ + config_gpio_table(wimax_off_gpio_table, + ARRAY_SIZE(wimax_off_gpio_table)); + gpio_set_value(88, 0); /*WiMax_SDIO_CLK_1 OL*/ + gpio_set_value(159, 0); /*WiMax_Host_2 OL*/ + + gpio_set_value(106, 1); + mdelay(5); + gpio_set_value(156, 0); + gpio_set_value(155, 0); + gpio_set_value(106, 0); + mdelay(5); + gpio_set_value(154, 0); + gpio_set_value(48, 0); + mdelay(5); + } + return 0; +} +EXPORT_SYMBOL(supersonic_wimax_power); + +int supersonic_wimax_set_status(int on) +{ + printk(KERN_INFO "%s on:%d\n", __func__, on); + supersonic_wimax_sdio_status = on; + return 0; +} +EXPORT_SYMBOL(supersonic_wimax_set_status); + +int supersonic_wimax_get_status() +{ + //printk(KERN_INFO "%s status:%d\n", __func__, supersonic_wimax_sdio_status); + return supersonic_wimax_sdio_status; +} +EXPORT_SYMBOL(supersonic_wimax_get_status); + +int __init supersonic_init_mmc(unsigned int sys_rev) +{ + uint32_t id; + + wifi_status_cb = NULL; + + printk(KERN_INFO "%s()+\n", __func__); + + /* initial WIFI_SHUTDOWN# */ + id = PCOM_GPIO_CFG(SUPERSONIC_GPIO_WIFI_SHUTDOWN_N, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + + msm_add_sdcc(1, &supersonic_wifi_data, 0, 0); + + /* Initial WiMAX */ + printk("config wimax power gpio table\n"); + config_gpio_table(wimax_power_pin_gpio_table, + ARRAY_SIZE(wimax_power_pin_gpio_table)); + + msm_add_sdcc(3, &supersonic_wimax_data,0,0); + + if (opt_disable_sdcard) { + pr_info("%s: sdcard disabled on cmdline\n", __func__); + goto done; + } + + sdslot_vreg_enabled = 0; + + sdslot_vreg = vreg_get(0, "gp6"); + if (IS_ERR(sdslot_vreg)) + return PTR_ERR(sdslot_vreg); + + if (system_rev == 0) { /* XA board */ + set_irq_wake(MSM_GPIO_TO_INT(SUPERSONIC_GPIO_SDMC_CD_N), 1); + + msm_add_sdcc(2, &supersonic_sdslot_data, + MSM_GPIO_TO_INT(SUPERSONIC_GPIO_SDMC_CD_N), + IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE); + } else + msm_add_sdcc(2, &supersonic_sdslot_data, 0, 0); + +done: + printk(KERN_INFO "%s()-\n", __func__); + return 0; +} diff --git a/arch/arm/mach-msm/board-supersonic-panel.c b/arch/arm/mach-msm/board-supersonic-panel.c new file mode 100644 index 0000000000000..da9e6772973bb --- /dev/null +++ b/arch/arm/mach-msm/board-supersonic-panel.c @@ -0,0 +1,970 @@ +/* linux/arch/arm/mach-msm/board-supersonic-panel.c + * + * Copyright (C) 2008 HTC Corporation. + * Author: Jay Tu + * + * 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 "board-supersonic.h" +#include "devices.h" +#include "proc_comm.h" + +/* +** Innitial T2 Settings +** Novatek Panels are picky, most start 50fps @ 372, the minority starts @ 378 +** So defaulting at 380, should support all. keyword: *should* +*/ +int savedT2 = 380; // 380 default, should support around 53+fps for all Novatek Panels + +#if 1 +#define B(s...) printk(s) +#else +#define B(s...) do {} while(0) +#endif +extern int panel_type; +enum { + PANEL_SHARP, + PANEL_AUO, +}; + +static struct cabc_t { + struct led_classdev lcd_backlight; + struct msm_mddi_client_data *client_data; + struct mutex lock; + unsigned long status; +} cabc; + +enum { + GATE_ON = 1 << 0, +}; +static struct vreg *vreg_lcd_2v8; +static struct vreg *vreg_lcd_1v8; + +#define REG_WAIT (0xffff) +struct nov_regs { + unsigned reg; + unsigned val; +} nov_init_seq[] = { + {0xc000, 0x86}, + {0xc001, 0x00}, + {0xc002, 0x86}, + {0xc003, 0x00}, + {0xc100, 0x40}, + {0xc200, 0x02}, + {0xc202, 0x32}, + {0xe000, 0x0e}, + {0xe001, 0x34}, + {0xe002, 0x3F}, + {0xe003, 0x49}, + {0xe004, 0x1D}, + {0xe005, 0x2C}, + {0xe006, 0x5F}, + {0xe007, 0x3A}, + {0xe008, 0x20}, + {0xe009, 0x28}, + {0xe00a, 0x80}, + {0xe00b, 0x13}, + {0xe00c, 0x32}, + {0xe00d, 0x56}, + {0xe00e, 0x79}, + {0xe00f, 0xB8}, + {0xe010, 0x55}, + {0xe011, 0x57}, + {0xe100, 0x0e}, + {0xe101, 0x34}, + {0xe102, 0x3F}, + {0xe103, 0x49}, + {0xe104, 0x1D}, + {0xe105, 0x2C}, + {0xe106, 0x5F}, + {0xe107, 0x3A}, + {0xe108, 0x20}, + {0xe109, 0x28}, + {0xe10a, 0x80}, + {0xe10b, 0x13}, + {0xe10c, 0x32}, + {0xe10d, 0x56}, + {0xe10e, 0x79}, + {0xe10f, 0xB8}, + {0xe110, 0x55}, + {0xe111, 0x57}, + + {0xe200, 0x0E}, + {0xe201, 0x34}, + {0xe202, 0x3F}, + {0xe203, 0x49}, + {0xe204, 0x1D}, + {0xe205, 0x2C}, + {0xe206, 0x5F}, + {0xe207, 0x3A}, + {0xe208, 0x20}, + {0xe209, 0x28}, + {0xe20A, 0x80}, + {0xe20B, 0x13}, + {0xe20C, 0x32}, + {0xe20D, 0x56}, + {0xe20E, 0x79}, + {0xe20F, 0xB8}, + {0xe210, 0x55}, + {0xe211, 0x57}, + + {0xe300, 0x0E}, + {0xe301, 0x34}, + {0xe302, 0x3F}, + {0xe303, 0x49}, + {0xe304, 0x1D}, + {0xe305, 0x2C}, + {0xe306, 0x5F}, + {0xe307, 0x3A}, + {0xe308, 0x20}, + {0xe309, 0x28}, + {0xe30A, 0x80}, + {0xe30B, 0x13}, + {0xe30C, 0x32}, + {0xe30D, 0x56}, + {0xe30E, 0x79}, + {0xe30F, 0xB8}, + {0xe310, 0x55}, + {0xe311, 0x57}, + {0xe400, 0x0E}, + {0xe401, 0x34}, + {0xe402, 0x3F}, + {0xe403, 0x49}, + {0xe404, 0x1D}, + {0xe405, 0x2C}, + {0xe406, 0x5F}, + {0xe407, 0x3A}, + {0xe408, 0x20}, + {0xe409, 0x28}, + {0xe40A, 0x80}, + {0xe40B, 0x13}, + {0xe40C, 0x32}, + {0xe40D, 0x56}, + {0xe40E, 0x79}, + {0xe40F, 0xB8}, + {0xe410, 0x55}, + {0xe411, 0x57}, + {0xe500, 0x0E}, + {0xe501, 0x34}, + {0xe502, 0x3F}, + {0xe503, 0x49}, + {0xe504, 0x1D}, + {0xe505, 0x2C}, + {0xe506, 0x5F}, + {0xe507, 0x3A}, + {0xe508, 0x20}, + {0xe509, 0x28}, + {0xe50A, 0x80}, + {0xe50B, 0x13}, + {0xe50C, 0x32}, + {0xe50D, 0x56}, + {0xe50E, 0x79}, + {0xe50F, 0xB8}, + {0xe510, 0x55}, + {0xe511, 0x57}, + + {0x3a00, 0x05}, + + /* cabc */ + {0x4e00, 0x00}, + {0x5e00, 0x00}, + {0x6a01, 0x00}, + {0x6a02, 0x03}, + {0x5100, 0xff}, + {0x5301, 0x10}, + {0x6A18, 0xff}, + {0x6A17, 0x01}, + {0xF402, 0x14}, + + {0xb101, 0x01}, // T2 init - Put back in, just in case if T2 is defaulted, + {0xb102, 0x7c}, // it gets defaulted to 380, and not 340. For Novatek Panels. + + {0x3500, 0x00}, + {0x1100, 0x0}, + {REG_WAIT, 120}, +}; + +struct s1d_regs { + unsigned reg; + unsigned val; +} s1d13775_init_seq[] = { + {0x001C, 0x1500}, + {0x0020, 0x3047}, + {0x0024, 0x4014}, // original value 401A, last 5 bits set PLL clock div, 1A=27 div, 14 = ~53 fps, and is the sweet spot for a pure register epson fix - netarchy + {0x0028, 0x031A}, + {0x002C, 0x0001}, + {REG_WAIT, 0x0004}, /* increase delay 1ms -> 4ms */ + {0x0084, 0x0215}, + {0x0088, 0x0038}, + {0x008C, 0x2113}, + {0x002C, 0x0002}, + {REG_WAIT, 0x0004}, /* increase delay 1ms -> 4ms */ + {0x002C, 0x0003}, + {0x0100, 0x3703}, // last 5 bits change pixel clock divider, 00011 (3) sets divider to 4, we like 4 combined with modded PLL clock div - netarchy + {0x0104, 0x0180}, + {0x0140, 0x003F}, + {0x0144, 0x00EF}, + {0x0148, 0x0016}, + {0x014C, 0x0005}, + {0x0150, 0x0006}, + {0x0154, 0x032B}, + {0x0158, 0x031F}, + {0x015C, 0x0009}, + {0x0160, 0x0002}, + {0x0164, 0x0003}, + {0x0168, 0x00A2}, + {0x0180, 0x0057}, + {0x0184, 0x00DB}, + {0x0188, 0x00E3}, + {0x018C, 0x0000}, + {0x0190, 0x0000}, + {0x0280, 0x0000}, + {0x0284, 0x0002}, + {0x0288, 0x0000}, + {0x028C, 0x0001}, + {0x0294, 0x0000}, + {0x0400, 0x8000}, + {0x0404, 0x100F}, // original value 1001, Bits 0-9 set the Tearing Effect Delay (gets rid of tearing) - netarchy + {0x0480, 0x0001}, + {0x0500, 0x0000}, + {0x0504, 0x0011}, + {0x0508, 0x0000}, + {0x0510, 0x0000}, + {0x0518, 0x002E}, + {0x051C, 0x00c7}, + {0x0520, 0x01DF}, + {0x0524, 0x031f}, + {0x0528, 0x0000}, + {0x052C, 0x0000}, + {0x0530, 0x0000}, + {0x0534, 0x0000}, + + {0x0604, 0x0108}, + {0x060C, 0x0000}, + {0x0610, 0x00ff}, + + {0x0648, 0x0020}, + {0x0800, 0x0000}, + {0x0804, 0x000A}, + {0x0808, 0x0400}, + {0x080C, 0x0400}, + {0x0814, 0x0000}, + {0x081C, 0x0000}, + {0x0824, 0x002E}, + {0x0828, 0x00C7}, + {0x082C, 0x01DF}, + {0x0830, 0x031F}, + {0x0834, 0x0000}, + {0x0838, 0x0000}, + {0x083C, 0x0000}, + {0x0840, 0x0000}, + {0x0844, 0x01DF}, + {0x0848, 0x031F}, + {0x0870, 0x0064}, + {0x0874, 0x0064}, + {0x0878, 0x00C7}, + {0x087C, 0x00C7}, + {0x1410, 0x0004}, + {0x1414, 0x00FF}, + {0x1420, 0x0000}, + {0x1424, 0x0000}, + {0x1428, 0x01DF}, + {0x142C, 0x031F}, + {0x1430, 0xDC00}, + {0x1434, 0x0005}, + {0x1440, 0x0000}, + {0x1444, 0x0000}, + {0x1448, 0x01DF}, + {0x144C, 0x031F}, + {0x1450, 0x0000}, + {0x1454, 0x0000}, + {0x1458, 0x01DF}, + {0x145C, 0x031F}, + {0x1460, 0x0000}, + {0x1464, 0x0000}, + {0x1468, 0x01DF}, + {0x146C, 0x031F}, + {0x1470, 0x0000}, + {0x1474, 0x0000}, + {0x1478, 0x01DF}, + {0x147C, 0x031F}, + {0x14A4, 0x0110}, + {0x14A8, 0xAFC8}, + {0x14AC, 0x0FF0}, + {0x14B0, 0x0202}, + {0x14B4, 0x0080}, + {0x14A0, 0x0002}, + {0x1508, 0x0000}, + {0x150C, 0x0000}, + {0x1510, 0x0000}, + {0x1514, 0x0000}, + {0x1520, 0x0000}, + {0x1524, 0x0000}, + {0x1528, 0x0000}, + {0x152C, 0x0000}, + {0x1530, 0x0000}, + {0x1534, 0x0000}, + {0x1538, 0x0000}, + {0x153C, 0x0000}, + {0x1540, 0x0000}, + {0x1544, 0x0000}, + {0x1548, 0x0000}, + {0x154C, 0x0000}, + {0x1550, 0x0000}, + {0x1554, 0x0000}, + {0x1558, 0x0000}, + {0x1600, 0x0000}, + {0x1604, 0x0020}, + {0x1608, 0x0040}, + {0x160C, 0x0060}, + {0x1610, 0x0080}, + {0x1614, 0x00A0}, + {0x1618, 0x00C0}, + {0x161C, 0x00E0}, + {0x1620, 0x0100}, + {0x1624, 0x0000}, + {0x1628, 0x0020}, + {0x162C, 0x0040}, + {0x1630, 0x0060}, + {0x1634, 0x0080}, + {0x1638, 0x00A0}, + {0x163C, 0x00C0}, + {0x1640, 0x00E0}, + {0x1644, 0x0100}, + {0x1648, 0x0000}, + {0x164C, 0x0020}, + {0x1650, 0x0040}, + {0x1654, 0x0060}, + {0x1658, 0x0080}, + {0x165C, 0x00A0}, + {0x1660, 0x00C0}, + {0x1664, 0x00E0}, + {0x1668, 0x0100}, + {0x1680, 0x0000}, + {0x1684, 0x0000}, + {0x1688, 0x0000}, + {0x168C, 0x0000}, + {0x1694, 0x0000}, + {0x16A0, 0x0000}, + {0x16A4, 0x0000}, + {0x16A8, 0x0000}, + {0x16AC, 0x0000}, + {0x16B4, 0x0000}, + {0x16C0, 0x0000}, + {0x16C4, 0x0000}, + {0x16C8, 0x0000}, + {0x16CC, 0x0000}, + {0x16D4, 0x0000}, + {0x16E0, 0x0000}, + {0x16E4, 0x0000}, + {0x16E8, 0x0000}, + {0x16EC, 0x0000}, + {0x16F4, 0x0000}, + {0x1700, 0x0000}, + {0x1704, 0x0000}, + {0x1708, 0x0000}, + {0x170C, 0x0000}, + {0x1714, 0x0000}, + {0x1720, 0x0000}, + {0x1724, 0x0000}, + {0x1728, 0x0000}, + {0x172C, 0x0000}, + {0x1734, 0x0000}, + {0x1740, 0x0000}, + {0x1744, 0x0000}, + {0x1748, 0x0000}, + {0x174C, 0x0000}, + {0x1754, 0x0000}, + {0x1760, 0x0000}, + {0x1764, 0x0000}, + {0x1768, 0x0000}, + {0x176C, 0x0000}, + {0x1774, 0x0000}, + {0x0300, 0x7000}, + {0x0304, 0x0000}, + {0x0308, 0x0000}, + {0x030C, 0x0000}, + {0x0310, 0x0000}, + {0x0314, 0x0000}, + {0x0318, 0xF7FF}, + {0x031C, 0xFFFF}, + {0x0320, 0x000F}, + {0x0324, 0x0000}, + {0x0328, 0x0000}, + {0x032C, 0x0000}, +}; + +struct s1d_regs pwm_seq[] = { + {0x001C, 0x0010}, + {0x14A0, 0x0001}, + {0x14A4, 0x0110}, + {0x14B0, 0x3030}, + {0x14A8, 0x09C4}, + {0x14AC, 0x0FF0}, +}; +extern int qspi_send_9bit(unsigned char id, unsigned data); +extern int qspi_send_16bit(unsigned char id, unsigned data); + +static void suc_set_brightness(struct led_classdev *led_cdev, + enum led_brightness val) +{ + struct msm_mddi_client_data *client = cabc.client_data; + unsigned int shrink_br = val; + + printk(KERN_DEBUG "set brightness = %d\n", val); + if (test_bit(GATE_ON, &cabc.status) == 0) + return; + + if (val < 30) + shrink_br = 5; + else if ((val >= 30) && (val <= 143)) + shrink_br = 104 * (val - 30) / 113 + 5; + else + shrink_br = 145 * (val - 144) / 111 + 110; + mutex_lock(&cabc.lock); + if (panel_type == PANEL_SHARP) { + int i, reg, val; + for (i = 0; i < ARRAY_SIZE(pwm_seq); i++) { + reg = pwm_seq[i].reg; + val = pwm_seq[i].val; + if (reg == REG_WAIT) + msleep(val); + else + client->remote_write(client, cpu_to_le32(val), reg); + } + client->remote_write(client, shrink_br, 0x14B4); + } else { + qspi_send_16bit(0x1, 0x55); + qspi_send_16bit(0x0, 0x00); + qspi_send_16bit(0x2, 0x00); + + qspi_send_16bit(0x1, 0x51); + qspi_send_16bit(0x0, 0x00); + qspi_send_16bit(0x2, shrink_br); + } + mutex_unlock(&cabc.lock); +} + +static enum led_brightness +suc_get_brightness(struct led_classdev *led_cdev) +{ + struct msm_mddi_client_data *client = cabc.client_data; + if (panel_type == PANEL_SHARP) + return client->remote_read(client, 0x14B4); + else + return client->remote_read(client, 0x5100); +} + +#define DEFAULT_BRIGHTNESS 100 +static void suc_backlight_switch(int on) +{ + enum led_brightness val; + + if (on) { + printk(KERN_DEBUG "turn on backlight\n"); + set_bit(GATE_ON, &cabc.status); + val = cabc.lcd_backlight.brightness; + + /* LED core uses get_brightness for default value + * If the physical layer is not ready, we should + * not count on it */ + if (val == 0) + val = DEFAULT_BRIGHTNESS; + suc_set_brightness(&cabc.lcd_backlight, val); + } else { + clear_bit(GATE_ON, &cabc.status); + suc_set_brightness(&cabc.lcd_backlight, 0); + } +} + +static int suc_backlight_probe(struct platform_device *pdev) +{ + int err = -EIO; + + mutex_init(&cabc.lock); + cabc.client_data = pdev->dev.platform_data; + cabc.lcd_backlight.name = "lcd-backlight"; + cabc.lcd_backlight.brightness_set = suc_set_brightness; + cabc.lcd_backlight.brightness_get = suc_get_brightness; + err = led_classdev_register(&pdev->dev, &cabc.lcd_backlight); + if (err) + goto err_register_lcd_bl; + return 0; + +err_register_lcd_bl: + led_classdev_unregister(&cabc.lcd_backlight); + return err; +} + +/* ------------------------------------------------------------------- */ + +static struct resource resources_msm_fb[] = { + { + .start = MSM_FB_BASE, + .end = MSM_FB_BASE + MSM_FB_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static int +supersonic_mddi_init(struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + int i = 0, ret; + unsigned reg, val; + + if (panel_type == PANEL_SHARP) { + client_data->auto_hibernate(client_data, 0); + for (i = 0; i < ARRAY_SIZE(s1d13775_init_seq); i++) { + reg = s1d13775_init_seq[i].reg; + val = s1d13775_init_seq[i].val; + if (reg == REG_WAIT) + msleep(val); + else + client_data->remote_write(client_data, cpu_to_le32(val), reg); + } + client_data->auto_hibernate(client_data, 1); + + struct spi_cmd { + unsigned char reg; + unsigned char val; + unsigned int delay; + } sharp_spi[] = { + {0x0, 0x11, 100}, + + {0x0, 0xB9, 0}, + {0x1, 0xFF, 0}, + {0x1, 0x83, 0}, + {0x1, 0x63, 0}, + + {0x0, 0x3A, 0}, + {0x1, 0x50, 0}, + }; + + /* FIXME */ + + for (i = 0; i < ARRAY_SIZE(sharp_spi); i++) { + ret = qspi_send_9bit(sharp_spi[i].reg, sharp_spi[i].val); + if (ret < 0) + printk("%s: spi_write fail!\n", __func__); + else if (sharp_spi[i].delay) + msleep(sharp_spi[i].delay); + } + } + else { + client_data->auto_hibernate(client_data, 0); + for (i = 0; i < ARRAY_SIZE(nov_init_seq); i++) { + reg = cpu_to_le32(nov_init_seq[i].reg); + val = cpu_to_le32(nov_init_seq[i].val); + if (reg == REG_WAIT) + msleep(val); + else + client_data->remote_write(client_data, val, reg); + } + client_data->auto_hibernate(client_data, 1); + } + return 0; +} + +static int +supersonic_mddi_uninit(struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + if (panel_type == PANEL_SHARP) { + int i, ret; + struct spi_cmd { + unsigned char reg; + unsigned char val; + unsigned int delay; + } sharp_spi[] = { + {0x0, 0x28, 0}, + {0x0, 0x10, 100}, + }; + + /* FIXME */ + + for (i = 0; i < ARRAY_SIZE(sharp_spi); i++) { + ret = qspi_send_9bit(sharp_spi[i].reg, sharp_spi[i].val); + if (ret < 0) + printk("%s: spi_write fail!\n", __func__); + else if (sharp_spi[i].delay) + msleep(sharp_spi[i].delay); + } + } + else + client_data->remote_write(client_data, 0, 0x2800); + + return 0; +} + +/* FIXME: remove after XA03 */ +static int backlight_control(int on) +{ + struct i2c_adapter *adap = i2c_get_adapter(0); + struct i2c_msg msg; + u8 buf[] = {0x90, 0x00, 0x00, 0x08}; + int ret = -EIO, max_retry = 3; + + msg.addr = 0xcc >> 1; + msg.flags = 0; + msg.len = sizeof(buf); + msg.buf = buf; + + if (on == 0) + buf[0] = 0x91; + + while (max_retry--) { + ret = i2c_transfer(adap, &msg, 1); + if (ret != 1) + msleep(1); + else { + ret = 0; + break; + } + ret = -EIO; + } + + if (ret) + printk(KERN_ERR "backlight control fail\n"); + return 0; +} + +static int +supersonic_panel_blank(struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + /* For Novatek Panels Only */ + if (panel_type == 1) + { + /* + ** Get the T2 value into readT2 + */ + unsigned readT2; + readT2 = 0; + readT2 |= client_data->remote_read(client_data, 0xb101) << 8; + readT2 |= client_data->remote_read(client_data, 0xb102); + /* readT2 value safety check */ + if (readT2 < 245 || readT2 > 999) + { + readT2 = 380; + } + savedT2 = readT2; + } + /* Done */ + + B(KERN_DEBUG "%s\n", __func__); + suc_backlight_switch(LED_OFF); + backlight_control(0); + return 0; +} + +static int +supersonic_panel_unblank(struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + B(KERN_DEBUG "%s\n", __func__); + if (panel_type == PANEL_AUO) { + suc_backlight_switch(LED_FULL); + client_data->remote_write(client_data, 0x00, 0x2900); + msleep(100); + client_data->remote_write(client_data, 0x24, 0x5300); + } else { + suc_backlight_switch(LED_FULL); + client_data->remote_write(client_data, 0x4000, 0x0600); + msleep(10); + qspi_send_9bit(0x0, 0x29); + client_data->remote_write(client_data, 0x7000, 0x0324); + client_data->remote_write(client_data, 0x4000, 0x0600); + client_data->remote_write(client_data, 0x100F, 0x0404); // Set anti-tearing on unblank for epson fix (including the intitial unblank at boot time) - netarchy + client_data->remote_write(client_data, 0x3703, 0x0100); // Set Pixel Clock Divider to 4 on unblank + client_data->remote_write(client_data, 0x4014, 0x0024); // Set PLL clock divider to 15 on unblank + } + + backlight_control(1); + + /* For Novatek Panels Only */ + if (panel_type == 1) + { + /* + ** Check saved value just to be safe + */ + if (savedT2 < 245 || savedT2 > 999) + { + savedT2 = 380; + } + /* Write savedT2 to T2 */ + client_data->remote_write(client_data, (0xff00 & savedT2) >> 8, 0xb101); + client_data->remote_write(client_data, (0x00ff & savedT2), 0xb102); + } + /* Done */ + + return 0; +} + +static struct msm_mddi_bridge_platform_data novatec_client_data = { + .init = supersonic_mddi_init, + .uninit = supersonic_mddi_uninit, + .blank = supersonic_panel_blank, + .unblank = supersonic_panel_unblank, + .fb_data = { + .xres = 480, + .yres = 800, + .width = 48, + .height = 80, + .output_format = 0, + }, + .panel_conf = { + .caps = MSMFB_CAP_CABC, + }, +}; + +static struct msm_mddi_bridge_platform_data epson_client_data = { + .init = supersonic_mddi_init, + .uninit = supersonic_mddi_uninit, + .blank = supersonic_panel_blank, + .unblank = supersonic_panel_unblank, + .fb_data = { + .xres = 480, + .yres = 800, + .width = 48, + .height = 80, + .output_format = 0, + }, + .panel_conf = { + .caps = MSMFB_CAP_CABC, + }, +}; + + +#define SPI_CLK 17 +#define SPI_DO 18 +#define SPI_DI 19 +#define SPI_CS 20 + +#define LCM_GPIO_CFG(gpio, func) \ +PCOM_GPIO_CFG(gpio, func, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_16MA) +static uint32_t spi_on_gpio_table[] = { + LCM_GPIO_CFG(SPI_CLK, 1), + LCM_GPIO_CFG(SPI_CS, 1), + LCM_GPIO_CFG(SPI_DO, 1), + PCOM_GPIO_CFG(SPI_DI, 1, GPIO_INPUT, GPIO_NO_PULL, GPIO_16MA), +}; + +static uint32_t spi_off_gpio_table[] = { + LCM_GPIO_CFG(SPI_CLK, 0), + LCM_GPIO_CFG(SPI_CS, 0), + LCM_GPIO_CFG(SPI_DO, 0), + PCOM_GPIO_CFG(SPI_DI, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_16MA), +}; + +static int spi_gpio_switch(int on) +{ + config_gpio_table( + !!on ? spi_on_gpio_table : spi_off_gpio_table, + ARRAY_SIZE(spi_on_gpio_table)); + + return 0; +} + +static void +mddi_novatec_power(struct msm_mddi_client_data *client_data, int on) +{ + unsigned id, on_off = 1; + + B(KERN_DEBUG "%s: power %s.\n", __func__, on ? "on" : "off"); + + if (on) { + on_off = 1; + /* 2V8 */ + id = PM_VREG_PDOWN_SYNT_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + vreg_enable(vreg_lcd_2v8); + + /* 1V8 */ + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + vreg_enable(vreg_lcd_1v8); + msleep(15); + + gpio_set_value(SUPERSONIC_LCD_RST, 1); + msleep(1); + gpio_set_value(SUPERSONIC_LCD_RST, 0); + msleep(5); + gpio_set_value(SUPERSONIC_LCD_RST, 1); + msleep(50); + spi_gpio_switch(1); + } else { + on_off = 0; + gpio_set_value(SUPERSONIC_LCD_RST, 0); + msleep(120); + + /* 1V8 */ + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + vreg_disable(vreg_lcd_1v8); + + /* 2V8 */ + id = PM_VREG_PDOWN_SYNT_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + vreg_disable(vreg_lcd_2v8); + spi_gpio_switch(0); + } +} + +static void +mddi_epson_power(struct msm_mddi_client_data *client_data, int on) +{ + unsigned id, on_off = 1; + + B(KERN_DEBUG "%s: power %s.\n", __func__, on ? "on" : "off"); + + if (on) { + on_off = 1; + /* 2V8 */ + gpio_set_value(149, 1); + id = PM_VREG_PDOWN_SYNT_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + vreg_enable(vreg_lcd_2v8); + msleep(5); + /* 1V8 */ + gpio_set_value(16, 1); + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + vreg_enable(vreg_lcd_1v8); + msleep(10); + + gpio_set_value(151, 1); + msleep(2); + + gpio_set_value(SUPERSONIC_LCD_RST, 1); + msleep(1); + gpio_set_value(SUPERSONIC_LCD_RST, 0); + msleep(5); + gpio_set_value(SUPERSONIC_LCD_RST, 1); + msleep(50); + spi_gpio_switch(1); + } else { + on_off = 0; + gpio_set_value(SUPERSONIC_LCD_RST, 0); + msleep(2); + gpio_set_value(151, 0); + msleep(120); + + /* 1V8 */ + gpio_set_value(16, 0); + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + vreg_disable(vreg_lcd_1v8); + msleep(5); + /* 2V8 */ + gpio_set_value(149, 0); + id = PM_VREG_PDOWN_SYNT_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + vreg_disable(vreg_lcd_2v8); + spi_gpio_switch(0); + } +} + +static struct msm_mddi_platform_data mddi_pdata = { + .clk_rate = 384000000, + .fb_resource = resources_msm_fb, + .num_clients = 2, + .client_platform_data = { + { + .product_id = (0xb9f6 << 16 | 0x5582), + .name = "mddi_c_b9f6_5582", + .id = 1, + .client_data = &novatec_client_data, + .clk_rate = 0, + }, + { + .product_id = (0x4ca3 << 16 | 0x0000), + .name = "mddi_c_4ca3_0000", + .id = 0, + .client_data = &epson_client_data, + .clk_rate = 0, + }, + }, +}; + +static struct platform_driver suc_backlight_driver = { + .probe = suc_backlight_probe, + .driver = { + .owner = THIS_MODULE, + }, +}; + +static struct msm_mdp_platform_data mdp_pdata = { + .dma_channel = MDP_DMA_S, +}; + +int __init supersonic_init_panel(void) +{ + int rc; + + B(KERN_INFO "%s: enter.\n", __func__); + + vreg_lcd_1v8 = vreg_get(0, "gp4"); + if (IS_ERR(vreg_lcd_1v8)) + return PTR_ERR(vreg_lcd_1v8); + + vreg_lcd_2v8 = vreg_get(0, "synt"); + if (IS_ERR(vreg_lcd_2v8)) + return PTR_ERR(vreg_lcd_2v8); + + if (panel_type == PANEL_SHARP) + mdp_pdata.overrides |= MSM_MDP_PANEL_IGNORE_PIXEL_DATA; + else + mdp_pdata.overrides &= ~MSM_MDP_PANEL_IGNORE_PIXEL_DATA; + + msm_device_mdp.dev.platform_data = &mdp_pdata; + rc = platform_device_register(&msm_device_mdp); + if (rc) + return rc; + + if (panel_type) + mddi_pdata.power_client = mddi_novatec_power; + else + mddi_pdata.power_client = mddi_epson_power; + + msm_device_mddi0.dev.platform_data = &mddi_pdata; + rc = platform_device_register(&msm_device_mddi0); + if (rc) + return rc; + + if (panel_type) + suc_backlight_driver.driver.name = "nov_cabc"; + else + suc_backlight_driver.driver.name = "eps_cabc"; + rc = platform_driver_register(&suc_backlight_driver); + if (rc) + return rc; + + return 0; +} diff --git a/arch/arm/mach-msm/board-supersonic-rfkill.c b/arch/arm/mach-msm/board-supersonic-rfkill.c new file mode 100644 index 0000000000000..990a011060fd7 --- /dev/null +++ b/arch/arm/mach-msm/board-supersonic-rfkill.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation. + * + * 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 "board-supersonic.h" + +static struct rfkill *bt_rfk; +static const char bt_name[] = "bcm4329"; + +static int bluetooth_set_power(void *data, bool blocked) +{ + if (!blocked) { + gpio_direction_output(SUPERSONIC_GPIO_BT_RESET_N, 1); + gpio_direction_output(SUPERSONIC_GPIO_BT_SHUTDOWN_N, 1); + } else { + gpio_direction_output(SUPERSONIC_GPIO_BT_SHUTDOWN_N, 0); + gpio_direction_output(SUPERSONIC_GPIO_BT_RESET_N, 0); + } + return 0; +} + +static struct rfkill_ops supersonic_rfkill_ops = { + .set_block = bluetooth_set_power, +}; + +static int supersonic_rfkill_probe(struct platform_device *pdev) +{ + int rc = 0; + bool default_state = true; /* off */ + + rc = gpio_request(SUPERSONIC_GPIO_BT_RESET_N, "bt_reset"); + if (rc) + goto err_gpio_reset; + rc = gpio_request(SUPERSONIC_GPIO_BT_SHUTDOWN_N, "bt_shutdown"); + if (rc) + goto err_gpio_shutdown; + + bluetooth_set_power(NULL, default_state); + + bt_rfk = rfkill_alloc(bt_name, &pdev->dev, RFKILL_TYPE_BLUETOOTH, + &supersonic_rfkill_ops, NULL); + if (!bt_rfk) { + rc = -ENOMEM; + goto err_rfkill_alloc; + } + + rfkill_set_states(bt_rfk, default_state, false); + + /* userspace cannot take exclusive control */ + + rc = rfkill_register(bt_rfk); + if (rc) + goto err_rfkill_reg; + + return 0; + +err_rfkill_reg: + rfkill_destroy(bt_rfk); +err_rfkill_alloc: + gpio_free(SUPERSONIC_GPIO_BT_SHUTDOWN_N); +err_gpio_shutdown: + gpio_free(SUPERSONIC_GPIO_BT_RESET_N); +err_gpio_reset: + return rc; +} + +static int supersonic_rfkill_remove(struct platform_device *dev) +{ + rfkill_unregister(bt_rfk); + rfkill_destroy(bt_rfk); + gpio_free(SUPERSONIC_GPIO_BT_SHUTDOWN_N); + gpio_free(SUPERSONIC_GPIO_BT_RESET_N); + + return 0; +} + +static struct platform_driver supersonic_rfkill_driver = { + .probe = supersonic_rfkill_probe, + .remove = supersonic_rfkill_remove, + .driver = { + .name = "supersonic_rfkill", + .owner = THIS_MODULE, + }, +}; + +static int __init supersonic_rfkill_init(void) +{ + if (!machine_is_supersonic()) + return 0; + + return platform_driver_register(&supersonic_rfkill_driver); +} + +static void __exit supersonic_rfkill_exit(void) +{ + platform_driver_unregister(&supersonic_rfkill_driver); +} + +module_init(supersonic_rfkill_init); +module_exit(supersonic_rfkill_exit); +MODULE_DESCRIPTION("supersonic rfkill"); +MODULE_AUTHOR("Nick Pelly "); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-supersonic-smb329.c b/arch/arm/mach-msm/board-supersonic-smb329.c new file mode 100755 index 0000000000000..5167ba93c6b92 --- /dev/null +++ b/arch/arm/mach-msm/board-supersonic-smb329.c @@ -0,0 +1,178 @@ +/* drivers/i2c/chips/smb329.c + * + * SMB329B Switch Charger (SUMMIT Microelectronics) + * + * Copyright (C) 2009 HTC Corporation + * Author: Justin Lin + * + * 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 "board-supersonic-smb329.h" + +static struct smb329_data { + struct i2c_client *client; + uint8_t version; + struct work_struct work; + struct mutex state_lock; + int chg_state; +} smb329; + +static int smb329_i2c_write(uint8_t *value, uint8_t reg, uint8_t num_bytes) +{ + int ret; + struct i2c_msg msg; + + /* write the first byte of buffer as the register address */ + value[0] = reg; + msg.addr = smb329.client->addr; + msg.len = num_bytes + 1; + msg.flags = 0; + msg.buf = value; + + ret = i2c_transfer(smb329.client->adapter, &msg, 1); + + return (ret >= 0) ? 0 : ret; +} + +static int smb329_i2c_read(uint8_t *value, uint8_t reg, uint8_t num_bytes) +{ + int ret; + struct i2c_msg msg[2]; + + /* setup the address to read */ + msg[0].addr = smb329.client->addr; + msg[0].len = 1; + msg[0].flags = 0; + msg[0].buf = ® + + /* setup the read buffer */ + msg[1].addr = smb329.client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = num_bytes; + msg[1].buf = value; + + ret = i2c_transfer(smb329.client->adapter, msg, 2); + + return (ret >= 0) ? 0 : ret; +} + +static int smb329_i2c_write_byte(uint8_t value, uint8_t reg) +{ + int ret; + uint8_t buf[2] = { 0 }; + + buf[1] = value; + ret = smb329_i2c_write(buf, reg, 1); + if (ret) + pr_err("smb329: write byte error (%d)\n", ret); + + return ret; +} + +static int smb329_i2c_read_byte(uint8_t *value, uint8_t reg) +{ + int ret = smb329_i2c_read(value, reg, 1); + if (ret) + pr_err("smb329: read byte error (%d)\n", ret); + + return ret; +} + +int smb329_set_charger_ctrl(uint32_t ctl) +{ + mutex_lock(&smb329.state_lock); + smb329.chg_state = ctl; + schedule_work(&smb329.work); + mutex_unlock(&smb329.state_lock); + return 0; +} + +static void smb329_work_func(struct work_struct *work) +{ + mutex_lock(&smb329.state_lock); + + switch (smb329.chg_state) { + case SMB329_ENABLE_FAST_CHG: + pr_info("smb329: charger on (fast)\n"); + smb329_i2c_write_byte(0x84, 0x31); + smb329_i2c_write_byte(0xD0, 0x01); + smb329_i2c_write_byte(0x08, 0x05); + if ((smb329.version & 0x18) == 0x0) + smb329_i2c_write_byte(0xA9, 0x00); + break; + + case SMB329_DISABLE_CHG: + case SMB329_ENABLE_SLOW_CHG: + pr_info("smb329: charger off/slow\n"); + smb329_i2c_write_byte(0x88, 0x31); + smb329_i2c_write_byte(0x08, 0x05); + break; + default: + pr_err("smb329: unknown charger state %d\n", + smb329.chg_state); + } + + mutex_unlock(&smb329.state_lock); +} + +static int smb329_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { + dev_dbg(&client->dev, "[SMB329]:I2C fail\n"); + return -EIO; + } + + smb329.client = client; + mutex_init(&smb329.state_lock); + INIT_WORK(&smb329.work, smb329_work_func); + + smb329_i2c_read_byte(&smb329.version, 0x3B); + pr_info("smb329 version: 0x%02x\n", smb329.version); + + return 0; +} + +static const struct i2c_device_id smb329_id[] = { + { "smb329", 0 }, + { }, +}; + +static struct i2c_driver smb329_driver = { + .driver.name = "smb329", + .id_table = smb329_id, + .probe = smb329_probe, +}; + +static int __init smb329_init(void) +{ + int ret = i2c_add_driver(&smb329_driver); + if (ret) + pr_err("smb329_init: failed\n"); + + return ret; +} + +module_init(smb329_init); + +MODULE_AUTHOR("Justin Lin "); +MODULE_DESCRIPTION("SUMMIT Microelectronics SMB329B switch charger"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-supersonic-smb329.h b/arch/arm/mach-msm/board-supersonic-smb329.h new file mode 100644 index 0000000000000..13b326fa71dfa --- /dev/null +++ b/arch/arm/mach-msm/board-supersonic-smb329.h @@ -0,0 +1,32 @@ +/* include/linux/smb329.h - smb329 switch charger driver + * + * Copyright (C) 2009 HTC Corporation. + * + * 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_SMB329_H +#define _LINUX_SMB329_H + +#ifdef __KERNEL__ + +enum { + SMB329_DISABLE_CHG, + SMB329_ENABLE_SLOW_CHG, + SMB329_ENABLE_FAST_CHG, +}; + +extern int smb329_set_charger_ctrl(uint32_t ctl); + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_SMB329_H */ + diff --git a/arch/arm/mach-msm/board-supersonic-tpa2018d1.c b/arch/arm/mach-msm/board-supersonic-tpa2018d1.c new file mode 100644 index 0000000000000..4cb4c83029be1 --- /dev/null +++ b/arch/arm/mach-msm/board-supersonic-tpa2018d1.c @@ -0,0 +1,368 @@ +/* drivers/i2c/chips/tpa2018d1.c + * + * TI TPA2018D1 Speaker Amplifier + * + * Copyright (C) 2009 HTC Corporation + * + * 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. + * + */ + +/* TODO: content validation in TPA2018_SET_CONFIG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-supersonic-tpa2018d1.h" + +static struct i2c_client *this_client; +static struct tpa2018d1_platform_data *pdata; +static int is_on; +static char spk_amp_cfg[8]; +static const char spk_amp_on[8] = { /* same length as spk_amp_cfg */ + 0x01, 0xc3, 0x20, 0x01, 0x00, 0x08, 0x1a, 0x21 +}; +static const char spk_amp_off[] = {0x01, 0xa2}; + +static DEFINE_MUTEX(spk_amp_lock); +static int tpa2018d1_opened; +static char *config_data; +static int tpa2018d1_num_modes; + +#define DEBUG 0 + +static int tpa2018_i2c_write(const char *txData, int length) +{ + struct i2c_msg msg[] = { + { + .addr = this_client->addr, + .flags = 0, + .len = length, + .buf = txData, + }, + }; + + if (i2c_transfer(this_client->adapter, msg, 1) < 0) { + pr_err("%s: I2C transfer error\n", __func__); + return -EIO; + } else + return 0; +} + +static int tpa2018_i2c_read(char *rxData, int length) +{ + struct i2c_msg msgs[] = { + { + .addr = this_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = rxData, + }, + }; + + if (i2c_transfer(this_client->adapter, msgs, 1) < 0) { + pr_err("%s: I2C transfer error\n", __func__); + return -EIO; + } + +#if DEBUG + do { + int i = 0; + for (i = 0; i < length; i++) + pr_info("%s: rx[%d] = %2x\n", + __func__, i, rxData[i]); + } while(0); +#endif + + return 0; +} + +static int tpa2018d1_open(struct inode *inode, struct file *file) +{ + int rc = 0; + + mutex_lock(&spk_amp_lock); + + if (tpa2018d1_opened) { + pr_err("%s: busy\n", __func__); + rc = -EBUSY; + goto done; + } + + tpa2018d1_opened = 1; +done: + mutex_unlock(&spk_amp_lock); + return rc; +} + +static int tpa2018d1_release(struct inode *inode, struct file *file) +{ + mutex_lock(&spk_amp_lock); + tpa2018d1_opened = 0; + mutex_unlock(&spk_amp_lock); + + return 0; +} + +static int tpa2018d1_read_config(void __user *argp) +{ + int rc = 0; + unsigned char reg_idx = 0x01; + unsigned char tmp[7]; + + if (!is_on) { + gpio_set_value(pdata->gpio_tpa2018_spk_en, 1); + msleep(5); /* According to TPA2018D1 Spec */ + } + + rc = tpa2018_i2c_write(®_idx, sizeof(reg_idx)); + if (rc < 0) + goto err; + + rc = tpa2018_i2c_read(tmp, sizeof(tmp)); + if (rc < 0) + goto err; + + if (copy_to_user(argp, &tmp, sizeof(tmp))) + rc = -EFAULT; + +err: + if (!is_on) + gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); + return rc; +} + +static int tpa2018d1_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int rc = 0; + int mode = -1; + int offset = 0; + struct tpa2018d1_config_data cfg; + + mutex_lock(&spk_amp_lock); + + switch (cmd) { + case TPA2018_SET_CONFIG: + if (copy_from_user(spk_amp_cfg, argp, sizeof(spk_amp_cfg))) + rc = -EFAULT; + break; + + case TPA2018_READ_CONFIG: + rc = tpa2018d1_read_config(argp); + break; + + case TPA2018_SET_MODE: + if (copy_from_user(&mode, argp, sizeof(mode))) { + rc = -EFAULT; + break; + } + if (mode >= tpa2018d1_num_modes || mode < 0) { + pr_err("%s: unsupported tpa2018d1 mode %d\n", + __func__, mode); + rc = -EINVAL; + break; + } + if (!config_data) { + pr_err("%s: no config data!\n", __func__); + rc = -EIO; + break; + } + memcpy(spk_amp_cfg, config_data + mode * TPA2018D1_CMD_LEN, + TPA2018D1_CMD_LEN); + break; + + case TPA2018_SET_PARAM: + if (copy_from_user(&cfg, argp, sizeof(cfg))) { + pr_err("%s: copy from user failed.\n", __func__); + rc = -EFAULT; + break; + } + tpa2018d1_num_modes = cfg.mode_num; + if (tpa2018d1_num_modes > TPA2018_NUM_MODES) { + pr_err("%s: invalid number of modes %d\n", __func__, + tpa2018d1_num_modes); + rc = -EINVAL; + break; + } + if (cfg.data_len != tpa2018d1_num_modes*TPA2018D1_CMD_LEN) { + pr_err("%s: invalid data length %d, expecting %d\n", + __func__, cfg.data_len, + tpa2018d1_num_modes * TPA2018D1_CMD_LEN); + rc = -EINVAL; + break; + } + /* Free the old data */ + if (config_data) + kfree(config_data); + config_data = kmalloc(cfg.data_len, GFP_KERNEL); + if (!config_data) { + pr_err("%s: out of memory\n", __func__); + rc = -ENOMEM; + break; + } + if (copy_from_user(config_data, cfg.cmd_data, cfg.data_len)) { + pr_err("%s: copy data from user failed.\n", __func__); + kfree(config_data); + config_data = NULL; + rc = -EFAULT; + break; + } + /* replace default setting with playback setting */ + if (tpa2018d1_num_modes >= TPA2018_MODE_PLAYBACK) { + offset = TPA2018_MODE_PLAYBACK * TPA2018D1_CMD_LEN; + memcpy(spk_amp_cfg, config_data + offset, + TPA2018D1_CMD_LEN); + } + break; + + default: + pr_err("%s: invalid command %d\n", __func__, _IOC_NR(cmd)); + rc = -EINVAL; + break; + } + mutex_unlock(&spk_amp_lock); + return rc; +} + +static struct file_operations tpa2018d1_fops = { + .owner = THIS_MODULE, + .open = tpa2018d1_open, + .release = tpa2018d1_release, + .ioctl = tpa2018d1_ioctl, +}; + +static struct miscdevice tpa2018d1_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "tpa2018d1", + .fops = &tpa2018d1_fops, +}; + +void tpa2018d1_set_speaker_amp(int on) +{ + if (!pdata) { + pr_err("%s: no platform data!\n", __func__); + return; + } + mutex_lock(&spk_amp_lock); + if (on && !is_on) { + gpio_set_value(pdata->gpio_tpa2018_spk_en, 1); + msleep(5); /* According to TPA2018D1 Spec */ + + if (tpa2018_i2c_write(spk_amp_cfg, sizeof(spk_amp_cfg)) == 0) { + is_on = 1; + pr_info("%s: ON\n", __func__); + } + } else if (!on && is_on) { + if (tpa2018_i2c_write(spk_amp_off, sizeof(spk_amp_off)) == 0) { + is_on = 0; + msleep(2); + gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); + pr_info("%s: OFF\n", __func__); + } + } + mutex_unlock(&spk_amp_lock); +} + +static int tpa2018d1_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + + pdata = client->dev.platform_data; + + if (!pdata) { + ret = -EINVAL; + pr_err("%s: platform data is NULL\n", __func__); + goto err_no_pdata; + } + + this_client = client; + + ret = gpio_request(pdata->gpio_tpa2018_spk_en, "tpa2018"); + if (ret < 0) { + pr_err("%s: gpio request aud_spk_en pin failed\n", __func__); + goto err_free_gpio; + } + + ret = gpio_direction_output(pdata->gpio_tpa2018_spk_en, 1); + if (ret < 0) { + pr_err("%s: request aud_spk_en gpio direction failed\n", + __func__); + goto err_free_gpio; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s: i2c check functionality error\n", __func__); + ret = -ENODEV; + goto err_free_gpio; + } + + gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); /* Default Low */ + + ret = misc_register(&tpa2018d1_device); + if (ret) { + pr_err("%s: tpa2018d1_device register failed\n", __func__); + goto err_free_gpio; + } + memcpy(spk_amp_cfg, spk_amp_on, sizeof(spk_amp_on)); + return 0; + +err_free_gpio: + gpio_free(pdata->gpio_tpa2018_spk_en); +err_no_pdata: + return ret; +} + +static int tpa2018d1_suspend(struct i2c_client *client, pm_message_t mesg) +{ + return 0; +} + +static int tpa2018d1_resume(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id tpa2018d1_id[] = { + { TPA2018D1_I2C_NAME, 0 }, + { } +}; + +static struct i2c_driver tpa2018d1_driver = { + .probe = tpa2018d1_probe, + .suspend = tpa2018d1_suspend, + .resume = tpa2018d1_resume, + .id_table = tpa2018d1_id, + .driver = { + .name = TPA2018D1_I2C_NAME, + }, +}; + +static int __init tpa2018d1_init(void) +{ + pr_info("%s\n", __func__); + return i2c_add_driver(&tpa2018d1_driver); +} + +module_init(tpa2018d1_init); + +MODULE_DESCRIPTION("tpa2018d1 speaker amp driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-supersonic-tpa2018d1.h b/arch/arm/mach-msm/board-supersonic-tpa2018d1.h new file mode 100644 index 0000000000000..dc11012209454 --- /dev/null +++ b/arch/arm/mach-msm/board-supersonic-tpa2018d1.h @@ -0,0 +1,35 @@ +/* include/linux/tpa2018d1.h - tpa2018d1 speaker amplifier driver + * + * Copyright (C) 2009 HTC Corporation. + * + * 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 __ASM_ARM_ARCH_TPA2018D1_H +#define __ASM_ARM_ARCH_TPA2018D1_H + +#define TPA2018D1_I2C_NAME "tpa2018d1" +#define TPA2018D1_CMD_LEN 8 + +struct tpa2018d1_platform_data { + uint32_t gpio_tpa2018_spk_en; +}; + +struct tpa2018d1_config_data { + unsigned char *cmd_data; /* [mode][cmd_len][cmds..] */ + unsigned int mode_num; + unsigned int data_len; +}; + +extern void tpa2018d1_set_speaker_amp(int on); + +#endif /* __ASM_ARM_ARCH_TPA2018D1_H */ diff --git a/arch/arm/mach-msm/board-supersonic-wifi.c b/arch/arm/mach-msm/board-supersonic-wifi.c new file mode 100644 index 0000000000000..21801a708fad1 --- /dev/null +++ b/arch/arm/mach-msm/board-supersonic-wifi.c @@ -0,0 +1,140 @@ +/* linux/arch/arm/mach-msm/board-supersonic-wifi.c +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-supersonic.h" + +int supersonic_wifi_power(int on); +int supersonic_wifi_reset(int on); +int supersonic_wifi_set_carddetect(int on); + +#define PREALLOC_WLAN_NUMBER_OF_SECTIONS 4 +#define PREALLOC_WLAN_NUMBER_OF_BUFFERS 160 +#define PREALLOC_WLAN_SECTION_HEADER 24 + +#define WLAN_SECTION_SIZE_0 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128) +#define WLAN_SECTION_SIZE_1 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128) +#define WLAN_SECTION_SIZE_2 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 512) +#define WLAN_SECTION_SIZE_3 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 1024) + +#define WLAN_SKB_BUF_NUM 16 + +static struct sk_buff *wlan_static_skb[WLAN_SKB_BUF_NUM]; + +typedef struct wifi_mem_prealloc_struct { + void *mem_ptr; + unsigned long size; +} wifi_mem_prealloc_t; + +static wifi_mem_prealloc_t wifi_mem_array[PREALLOC_WLAN_NUMBER_OF_SECTIONS] = { + { NULL, (WLAN_SECTION_SIZE_0 + PREALLOC_WLAN_SECTION_HEADER) }, + { NULL, (WLAN_SECTION_SIZE_1 + PREALLOC_WLAN_SECTION_HEADER) }, + { NULL, (WLAN_SECTION_SIZE_2 + PREALLOC_WLAN_SECTION_HEADER) }, + { NULL, (WLAN_SECTION_SIZE_3 + PREALLOC_WLAN_SECTION_HEADER) } +}; + +static void *supersonic_wifi_mem_prealloc(int section, unsigned long size) +{ + if (section == PREALLOC_WLAN_NUMBER_OF_SECTIONS) + return wlan_static_skb; + if ((section < 0) || (section > PREALLOC_WLAN_NUMBER_OF_SECTIONS)) + return NULL; + if (wifi_mem_array[section].size < size) + return NULL; + return wifi_mem_array[section].mem_ptr; +} + +int __init supersonic_init_wifi_mem(void) +{ + int i; + + for(i=0;( i < WLAN_SKB_BUF_NUM );i++) { + if (i < (WLAN_SKB_BUF_NUM/2)) + wlan_static_skb[i] = dev_alloc_skb(4096); + else + wlan_static_skb[i] = dev_alloc_skb(8192); + } + for(i=0;( i < PREALLOC_WLAN_NUMBER_OF_SECTIONS );i++) { + wifi_mem_array[i].mem_ptr = kmalloc(wifi_mem_array[i].size, + GFP_KERNEL); + if (wifi_mem_array[i].mem_ptr == NULL) + return -ENOMEM; + } + return 0; +} + +static struct resource supersonic_wifi_resources[] = { + [0] = { + .name = "bcm4329_wlan_irq", + .start = MSM_GPIO_TO_INT(SUPERSONIC_GPIO_WIFI_IRQ), + .end = MSM_GPIO_TO_INT(SUPERSONIC_GPIO_WIFI_IRQ), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL, + }, +}; + +static struct wifi_platform_data supersonic_wifi_control = { + .set_power = supersonic_wifi_power, + .set_reset = supersonic_wifi_reset, + .set_carddetect = supersonic_wifi_set_carddetect, + .mem_prealloc = supersonic_wifi_mem_prealloc, +}; + +static struct platform_device supersonic_wifi_device = { + .name = "bcm4329_wlan", + .id = 1, + .num_resources = ARRAY_SIZE(supersonic_wifi_resources), + .resource = supersonic_wifi_resources, + .dev = { + .platform_data = &supersonic_wifi_control, + }, +}; + +extern unsigned char *get_wifi_nvs_ram(void); + +static unsigned supersonic_wifi_update_nvs(char *str) +{ +#define NVS_LEN_OFFSET 0x0C +#define NVS_DATA_OFFSET 0x40 + unsigned char *ptr; + unsigned len; + + if (!str) + return -EINVAL; + ptr = get_wifi_nvs_ram(); + /* Size in format LE assumed */ + memcpy(&len, ptr + NVS_LEN_OFFSET, sizeof(len)); + + /* the last bye in NVRAM is 0, trim it */ + if (ptr[NVS_DATA_OFFSET + len -1] == 0) + len -= 1; + + strcpy(ptr + NVS_DATA_OFFSET + len, str); + len += strlen(str); + memcpy(ptr + NVS_LEN_OFFSET, &len, sizeof(len)); + return 0; +} + +static int __init supersonic_wifi_init(void) +{ + int ret; + + if (!machine_is_supersonic()) + return 0; + + printk("%s: start\n", __func__); + supersonic_wifi_update_nvs("sd_oobonly=1\r\n"); + supersonic_init_wifi_mem(); + ret = platform_device_register(&supersonic_wifi_device); + return ret; +} + +late_initcall(supersonic_wifi_init); diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c new file mode 100644 index 0000000000000..18678c00a0bf7 --- /dev/null +++ b/arch/arm/mach-msm/board-supersonic.c @@ -0,0 +1,1344 @@ +/* linux/arch/arm/mach-msm/board-supersonic.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation. + * Author: Dima Zavin + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-supersonic.h" +#include "devices.h" +#include "proc_comm.h" +#include "smd_private.h" +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_MICROP_COMMON +#include +#endif + +#include + +#include "board-supersonic-tpa2018d1.h" + +#define SMEM_SPINLOCK_I2C 6 + +#ifdef CONFIG_ARCH_QSD8X50 +extern unsigned char *get_bt_bd_ram(void); +#endif + +static unsigned skuid; + +static uint opt_usb_h2w_sw; +module_param_named(usb_h2w_sw, opt_usb_h2w_sw, uint, 0); + +void msm_init_pmic_vibrator(void); +static void config_supersonic_usb_id_gpios(bool output); +extern void __init supersonic_audio_init(void); +extern void __init supersonic_init_panel(void); +#ifdef CONFIG_MICROP_COMMON +void __init supersonic_microp_init(void); +#endif +static struct htc_battery_platform_data htc_battery_pdev_data = { + .gpio_mbat_in = SUPERSONIC_GPIO_MBAT_IN, + .gpio_mchg_en_n = SUPERSONIC_GPIO_MCHG_EN_N, + .gpio_iset = SUPERSONIC_GPIO_ISET, + .guage_driver = GUAGE_MODEM, + .m2a_cable_detect = 1, + .charger = SWITCH_CHARGER, +}; + +static struct platform_device htc_battery_pdev = { + .name = "htc_battery", + .id = -1, + .dev = { + .platform_data = &htc_battery_pdev_data, + }, +}; + +#ifdef CONFIG_MICROP_COMMON +static int capella_cm3602_power(int pwr_device, uint8_t enable); +static struct microp_function_config microp_functions[] = { + { + .name = "remote-key", + .category = MICROP_FUNCTION_REMOTEKEY, + .levels = {0, 33, 50, 110, 160, 220}, + .channel = 1, + .int_pin = 1 << 7, + }, + { + .name = "reset-int", + .category = MICROP_FUNCTION_RESET_INT, + .int_pin = 1 << 8, + }, +}; + +static struct microp_function_config microp_lightsensor = { + .name = "light_sensor", + .category = MICROP_FUNCTION_LSENSOR, + .levels = { 3, 7, 12, 57, 114, 279, 366, 453, 540, 0x3FF }, + .channel = 3, + .int_pin = 1 << 9, + .golden_adc = 0x118, + .ls_power = capella_cm3602_power, +}; + +static struct lightsensor_platform_data lightsensor_data = { + .config = µp_lightsensor, + .irq = MSM_uP_TO_INT(9), +}; + +static struct microp_led_config led_config[] = { + { + .name = "amber", + .type = LED_RGB, + }, + { + .name = "green", + .type = LED_RGB, + }, + { + .name = "wimax", + .type = LED_WIMAX, + }, +}; + +static struct microp_led_platform_data microp_leds_data = { + .num_leds = ARRAY_SIZE(led_config), + .led_config = led_config, +}; + +static struct bma150_platform_data supersonic_g_sensor_pdata = { + .microp_new_cmd = 1, +}; + +/* Proximity Sensor (Capella_CM3602)*/ +static int __capella_cm3602_power(int on) +{ + int ret; + struct vreg *vreg = vreg_get(0, "gp1");; + if (!vreg) { + printk(KERN_ERR "%s: vreg error\n", __func__); + return -EIO; + } + ret = vreg_set_level(vreg, 2800); + + printk(KERN_DEBUG "%s: Turn the capella_cm3602 power %s\n", + __func__, (on) ? "on" : "off"); + if (on) { + gpio_direction_output(SUPERSONIC_GPIO_PROXIMITY_EN_N, 1); + ret = vreg_enable(vreg); + if (ret < 0) + printk(KERN_ERR "%s: vreg enable failed\n", __func__); + } else { + vreg_disable(vreg); + gpio_direction_output(SUPERSONIC_GPIO_PROXIMITY_EN_N, 0); + } + + return ret; +} + +static DEFINE_MUTEX(capella_cm3602_lock); +static unsigned int als_power_control; + +static int capella_cm3602_power(int pwr_device, uint8_t enable) +{ + unsigned int old_status = 0; + int ret = 0, on = 0; + mutex_lock(&capella_cm3602_lock); + + old_status = als_power_control; + if (enable) + als_power_control |= pwr_device; + else + als_power_control &= ~pwr_device; + + on = als_power_control ? 1 : 0; + if (old_status == 0 && on) + ret = __capella_cm3602_power(1); + else if (!on) + ret = __capella_cm3602_power(0); + + mutex_unlock(&capella_cm3602_lock); + return ret; +} + +static struct capella_cm3602_platform_data capella_cm3602_pdata = { + .power = capella_cm3602_power, + .p_en = SUPERSONIC_GPIO_PROXIMITY_EN_N, + .p_out = MSM_uP_TO_INT(4), +}; +/* End Proximity Sensor (Capella_CM3602)*/ + + +static struct platform_device microp_devices[] = { + { + .name = "lightsensor_microp", + .dev = { + .platform_data = &lightsensor_data, + }, + }, + { + .name = "leds-microp", + .id = -1, + .dev = { + .platform_data = µp_leds_data, + }, + }, + { + .name = BMA150_G_SENSOR_NAME, + .dev = { + .platform_data = &supersonic_g_sensor_pdata, + }, + }, + { + .name = "supersonic_proximity", + .id = -1, + .dev = { + .platform_data = &capella_cm3602_pdata, + }, + }, +}; + +static struct microp_i2c_platform_data microp_data = { + .num_functions = ARRAY_SIZE(microp_functions), + .microp_function = microp_functions, + .num_devices = ARRAY_SIZE(microp_devices), + .microp_devices = microp_devices, + .gpio_reset = SUPERSONIC_GPIO_UP_RESET_N, + .microp_ls_on = LS_PWR_ON | PS_PWR_ON, + .spi_devices = SPI_GSENSOR, +}; +#endif + +static struct gpio_led supersonic_led_list[] = { + { + .name = "button-backlight", + .gpio = SUPERSONIC_AP_KEY_LED_EN, + .active_low = 0, + }, +}; + +static struct gpio_led_platform_data supersonic_leds_data = { + .num_leds = ARRAY_SIZE(supersonic_led_list), + .leds = supersonic_led_list, +}; + +static struct platform_device supersonic_leds = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &supersonic_leds_data, + }, +}; + +extern void msm_hsusb_8x50_phy_reset(void); +static int supersonic_phy_init_seq[] = { 0xC, 0x31, 0x30, 0x32, 0x1D, 0x0D, 0x1D, 0x10, -1 }; + +static struct msm_hsusb_platform_data msm_hsusb_pdata = { + .phy_init_seq = supersonic_phy_init_seq, + .usb_connected = notify_usb_connected, +// XXX: Car kit +// .usb_id_pin_gpio = SUPERSONIC_GPIO_USB_ID_PIN, +// .config_usb_id_gpios = config_supersonic_usb_id_gpios, +}; + + +static char *usb_functions_ums[] = { + "usb_mass_storage", +}; + +static char *usb_functions_ums_adb[] = { + "usb_mass_storage", + "adb", +}; + +static char *usb_functions_rndis[] = { + "rndis", +}; + +static char *usb_functions_rndis_adb[] = { + "rndis", + "adb", +}; + +#ifdef CONFIG_USB_ANDROID_DIAG +static char *usb_functions_adb_diag[] = { + "usb_mass_storage", + "adb", + "diag", +}; +#endif + +static char *usb_functions_all[] = { +#ifdef CONFIG_USB_ANDROID_RNDIS + "rndis", +#endif + "usb_mass_storage", + "adb", +#ifdef CONFIG_USB_ANDROID_ACM + "acm", +#endif +#ifdef CONFIG_USB_ANDROID_DIAG + "diag", +#endif +}; + +static struct android_usb_product usb_products[] = { + { + .product_id = 0x0ff9, + .num_functions = ARRAY_SIZE(usb_functions_ums), + .functions = usb_functions_ums, + }, + { + .product_id = 0x0c8d, + .num_functions = ARRAY_SIZE(usb_functions_ums_adb), + .functions = usb_functions_ums_adb, + }, + { + .product_id = 0x0c03, + .num_functions = ARRAY_SIZE(usb_functions_rndis), + .functions = usb_functions_rndis, + }, + { + .product_id = 0x0c04, + .num_functions = ARRAY_SIZE(usb_functions_rndis_adb), + .functions = usb_functions_rndis_adb, + }, +#ifdef CONFIG_USB_ANDROID_DIAG + { + .product_id = 0x0c07, + .num_functions = ARRAY_SIZE(usb_functions_adb_diag), + .functions = usb_functions_adb_diag, + }, +#endif +}; + +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .vendor = "HTC", + .product = "Supersonic", + .release = 0x0100, +}; + +static struct platform_device usb_mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &mass_storage_pdata, + }, +}; + +#ifdef CONFIG_USB_ANDROID_RNDIS +static struct usb_ether_platform_data rndis_pdata = { + /* ethaddr is filled by board_serialno_setup */ + .vendorID = 0x18d1, + .vendorDescr = "Google, Inc.", +}; + +static struct platform_device rndis_device = { + .name = "rndis", + .id = -1, + .dev = { + .platform_data = &rndis_pdata, + }, +}; +#endif + +static struct android_usb_platform_data android_usb_pdata = { + .vendor_id = 0x0bb4, + .product_id = 0x0c8d, + .version = 0x0100, + .product_name = "Android Phone", + .manufacturer_name = "HTC", + .num_products = ARRAY_SIZE(usb_products), + .products = usb_products, + .num_functions = ARRAY_SIZE(usb_functions_all), + .functions = usb_functions_all, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; + + +/* 2 : wimax UART, 1 : CPU uart, 0 : usb +CPU_WIMAX_SW -> GPIO160 +USB_UART#_SW -> GPIO33 + +XA : GPIO33 = 0 -> USB + GPIO33 = 1 -> CPU UART + +XB : GPIO33 = 0 -> USB + GPIO33 = 1 , GPIO160 = 0 -> CPU UART // SUPERSONIC_WIMAX_CPU_UARTz_SW (GPIO160) + GPIO33 = 1 , GPIO160 = 1 -> Wimax UART // SUPERSONIC_USB_UARTz_SW (GPIO33) +*/ + + +// USB cable out: supersonic_uart_usb_switch(1) +// USB cable in: supersonic_uart_usb_switch(0) +static void supersonic_uart_usb_switch(int uart) +{ + printk(KERN_INFO "%s:uart:%d\n", __func__, uart); + gpio_set_value(SUPERSONIC_USB_UARTz_SW, uart?1:0); // XA and for USB cable in to reset wimax UART + + if(system_rev && uart) // XB + { + if (gpio_get_value(SUPERSONIC_WIMAX_CPU_UARTz_SW)) // Wimax UART + { + printk(KERN_INFO "%s:Wimax UART\n", __func__); + gpio_set_value(SUPERSONIC_USB_UARTz_SW,1); + gpio_set_value(SUPERSONIC_WIMAX_CPU_UARTz_SW,1); + } + else // USB, CPU UART + { + printk(KERN_INFO "%s:Non wimax UART\n", __func__); + gpio_set_value(SUPERSONIC_WIMAX_CPU_UARTz_SW, uart==2?1:0); + } + } +} + +static struct platform_device supersonic_rfkill = { + .name = "supersonic_rfkill", + .id = -1, +}; + +static struct spi_platform_data supersonic_spi_pdata = { + .clk_rate = 1200000, +}; + +static struct resource msm_kgsl_resources[] = { + { + .name = "kgsl_reg_memory", + .start = MSM_GPU_REG_PHYS, + .end = MSM_GPU_REG_PHYS + MSM_GPU_REG_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "kgsl_phys_memory", + .start = MSM_GPU_MEM_BASE, + .end = MSM_GPU_MEM_BASE + MSM_GPU_MEM_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_GRAPHICS, + .end = INT_GRAPHICS, + .flags = IORESOURCE_IRQ, + }, +}; + +#define PWR_RAIL_GRP_CLK 8 +static int supersonic_kgsl_power_rail_mode(int follow_clk) +{ + int mode = follow_clk ? 0 : 1; + int rail_id = PWR_RAIL_GRP_CLK; + + return msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &rail_id, &mode); +} + +static int supersonic_kgsl_power(bool on) +{ + int cmd; + int rail_id = PWR_RAIL_GRP_CLK; + + cmd = on ? PCOM_CLKCTL_RPC_RAIL_ENABLE : PCOM_CLKCTL_RPC_RAIL_DISABLE; + return msm_proc_comm(cmd, &rail_id, NULL); +} + +static struct platform_device msm_kgsl_device = { + .name = "kgsl", + .id = -1, + .resource = msm_kgsl_resources, + .num_resources = ARRAY_SIZE(msm_kgsl_resources), +}; + +static struct android_pmem_platform_data mdp_pmem_pdata = { + .name = "pmem", + .start = MSM_PMEM_MDP_BASE, + .size = MSM_PMEM_MDP_SIZE, + .no_allocator = 0, + .cached = 1, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .start = MSM_PMEM_ADSP_BASE, + .size = MSM_PMEM_ADSP_SIZE, + .no_allocator = 0, + .cached = 1, +}; + +static struct android_pmem_platform_data android_pmem_venc_pdata = { + .name = "pmem_venc", + .start = MSM_PMEM_VENC_BASE, + .size = MSM_PMEM_VENC_SIZE, + .no_allocator = 0, + .cached = 1, +}; + + +#ifdef CONFIG_BUILD_CIQ +static struct android_pmem_platform_data android_pmem_ciq_pdata = { + .name = "pmem_ciq", + .start = MSM_PMEM_CIQ_BASE, + .size = MSM_PMEM_CIQ_SIZE, + .no_allocator = 0, + .cached = 0, +}; + +static struct android_pmem_platform_data android_pmem_ciq1_pdata = { + .name = "pmem_ciq1", + .start = MSM_PMEM_CIQ1_BASE, + .size = MSM_PMEM_CIQ1_SIZE, + .no_allocator = 0, + .cached = 0, +}; + +static struct android_pmem_platform_data android_pmem_ciq2_pdata = { + .name = "pmem_ciq2", + .start = MSM_PMEM_CIQ2_BASE, + .size = MSM_PMEM_CIQ2_SIZE, + .no_allocator = 0, + .cached = 0, +}; + +static struct android_pmem_platform_data android_pmem_ciq3_pdata = { + .name = "pmem_ciq3", + .start = MSM_PMEM_CIQ3_BASE, + .size = MSM_PMEM_CIQ3_SIZE, + .no_allocator = 0, + .cached = 0, +}; +#endif + +static struct platform_device android_pmem_mdp_device = { + .name = "android_pmem", + .id = 0, + .dev = { + .platform_data = &mdp_pmem_pdata + }, +}; + +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 4, + .dev = { + .platform_data = &android_pmem_adsp_pdata, + }, +}; + +static struct platform_device android_pmem_venc_device = { + .name = "android_pmem", + .id = 6, + .dev = { + .platform_data = &android_pmem_venc_pdata, + }, +}; + +#ifdef CONFIG_BUILD_CIQ +static struct platform_device android_pmem_ciq_device = { + .name = "android_pmem", + .id = 7, + .dev = { .platform_data = &android_pmem_ciq_pdata }, +}; + +static struct platform_device android_pmem_ciq1_device = { + .name = "android_pmem", + .id = 8, + .dev = { .platform_data = &android_pmem_ciq1_pdata }, +}; + +static struct platform_device android_pmem_ciq2_device = { + .name = "android_pmem", + .id = 9, + .dev = { .platform_data = &android_pmem_ciq2_pdata }, +}; + +static struct platform_device android_pmem_ciq3_device = { + .name = "android_pmem", + .id = 10, + .dev = { .platform_data = &android_pmem_ciq3_pdata }, +}; +#endif + + + +static struct resource ram_console_resources[] = { + { + .start = MSM_RAM_CONSOLE_BASE, + .end = MSM_RAM_CONSOLE_BASE + MSM_RAM_CONSOLE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device ram_console_device = { + .name = "ram_console", + .id = -1, + .num_resources = ARRAY_SIZE(ram_console_resources), + .resource = ram_console_resources, +}; + +static int supersonic_atmel_ts_power(int on) +{ + printk(KERN_INFO "supersonic_atmel_ts_power(%d)\n", on); + if (on) { + gpio_set_value(SUPERSONIC_GPIO_TP_RST, 0); + msleep(5); + gpio_set_value(SUPERSONIC_GPIO_TP_EN, 1); + msleep(5); + gpio_set_value(SUPERSONIC_GPIO_TP_RST, 1); + msleep(40); + } else { + gpio_set_value(SUPERSONIC_GPIO_TP_EN, 0); + msleep(2); + } + return 0; +} + +struct atmel_i2c_platform_data supersonic_atmel_ts_data[] = { + { + .version = 0x016, + .display_width = 480, + .display_height = 800, + .abs_x_min = 34, + .abs_x_max = 990, + .abs_y_min = 15, + .abs_y_max = 950, + .abs_pressure_min = 0, + .abs_pressure_max = 255, + .abs_width_min = 0, + .abs_width_max = 20, + .gpio_irq = SUPERSONIC_GPIO_TP_INT_N, + .power = supersonic_atmel_ts_power, + .config_T6 = {0, 0, 0, 0, 0, 0}, + .config_T7 = {50, 15, 50}, + .config_T8 = {10, 0, 20, 10, 0, 0, 5, 0}, + .config_T9 = {139, 0, 0, 18, 12, 0, 16, 32, 3, 5, 0, 5, 2, 14, 2, 10, 25, 10, 0, 0, 0, 0, 0, 0, 0, 0, 143, 25, 146, 10, 40}, + .config_T15 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T19 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T20 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T22 = {15, 0, 0, 0, 0, 0, 0, 0, 14, 0, 1, 8, 12, 16, 30, 40, 0}, + .config_T23 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T24 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T25 = {3, 0, 200, 50, 64, 31, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T27 = {0, 0, 0, 0, 0, 0, 0}, + .config_T28 = {0, 0, 2, 4, 8, 60}, + .object_crc = {0x63, 0x27, 0x8E}, + .cable_config = {30, 30, 8, 16}, + .GCAF_level = {20, 24, 28, 40, 63}, + .filter_level = {46, 100, 923, 978}, + }, + { + .version = 0x015, + .abs_x_min = 10, + .abs_x_max = 1012, + .abs_y_min = 15, + .abs_y_max = 960, + .abs_pressure_min = 0, + .abs_pressure_max = 255, + .abs_width_min = 0, + .abs_width_max = 20, + .gpio_irq = SUPERSONIC_GPIO_TP_INT_N, + .power = supersonic_atmel_ts_power, + .config_T6 = {0, 0, 0, 0, 0, 0}, + .config_T7 = {100, 10, 50}, + .config_T8 = {8, 0, 50, 50, 0, 0, 50, 0}, + .config_T9 = {3, 0, 0, 18, 12, 0, 32, 40, 2, 5, 0, 0, 0, 0, 2, 10, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 143, 47, 145, 81}, + .config_T15 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T19 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T20 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T22 = {7, 0, 0, 25, 0, -25, 255, 4, 50, 0, 1, 10, 15, 20, 25, 30, 4}, + .config_T23 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T24 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T25 = {3, 0, 200, 50, 64, 31, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T27 = {0, 0, 0, 0, 0, 0, 0}, + .config_T28 = {0, 0, 2, 4, 8, 60}, + .object_crc = {0x87, 0xAD, 0xF5}, + }, + { + .version = 0x014, + .abs_x_min = 10, + .abs_x_max = 1012, + .abs_y_min = 15, + .abs_y_max = 960, + .abs_pressure_min = 0, + .abs_pressure_max = 255, + .abs_width_min = 0, + .abs_width_max = 20, + .gpio_irq = SUPERSONIC_GPIO_TP_INT_N, + .power = supersonic_atmel_ts_power, + .config_T6 = {0, 0, 0, 0, 0, 0}, + .config_T7 = {100, 10, 50}, + .config_T8 = {8, 0, 50, 50, 0, 0, 50, 0}, + .config_T9 = {3, 0, 0, 18, 12, 0, 64, 45, 3, 5, 0, 0, 0, 0, 2, 10, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 47, 143, 81}, + .config_T15 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T19 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T20 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T22 = {7, 0, 0, 25, 0, -25, 255, 4, 50, 0, 1, 10, 15, 20, 25, 30, 4}, + .config_T23 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T24 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T25 = {3, 0, 200, 50, 64, 31, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T27 = {0, 0, 0, 0, 0, 0, 0}, + .config_T28 = {0, 0, 2, 4, 8}, + } +}; + +static struct regulator_consumer_supply tps65023_dcdc1_supplies[] = { + { + .supply = "acpu_vcore", + }, +}; + +static struct regulator_init_data tps65023_data[5] = { + { + .constraints = { + .name = "dcdc1", /* VREG_MSMC2_1V29 */ + .min_uV = 1000000, + .max_uV = 1300000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + }, + .consumer_supplies = tps65023_dcdc1_supplies, + .num_consumer_supplies = ARRAY_SIZE(tps65023_dcdc1_supplies), + }, + /* dummy values for unused regulators to not crash driver: */ + { + .constraints = { + .name = "dcdc2", /* VREG_MSMC1_1V26 */ + .min_uV = 1260000, + .max_uV = 1260000, + }, + }, + { + .constraints = { + .name = "dcdc3", /* unused */ + .min_uV = 800000, + .max_uV = 3300000, + }, + }, + { + .constraints = { + .name = "ldo1", /* unused */ + .min_uV = 1000000, + .max_uV = 3150000, + }, + }, + { + .constraints = { + .name = "ldo2", /* V_USBPHY_3V3 */ + .min_uV = 3300000, + .max_uV = 3300000, + }, + }, +}; + +static struct h2w_platform_data supersonic_h2w_data = { +}; + +static struct platform_device supersonic_h2w = { + .name = "htc_headset", + .id = -1, + .dev = { + .platform_data = &supersonic_h2w_data, + }, +}; + +static struct audio_jack_platform_data supersonic_jack_data = { + .gpio = SUPERSONIC_GPIO_35MM_HEADSET_DET, +}; + +static struct platform_device supersonic_audio_jack = { + .name = "audio-jack", + .id = -1, + .dev = { + .platform_data = &supersonic_jack_data, + }, +}; + +static struct akm8973_platform_data compass_platform_data = { + .layouts = SUPERSONIC_LAYOUTS, + .project_name = SUPERSONIC_PROJECT_NAME, + .reset = SUPERSONIC_GPIO_COMPASS_RST_N, + .intr = SUPERSONIC_GPIO_COMPASS_INT_N, +}; + +static struct tpa2018d1_platform_data tpa2018_data = { + .gpio_tpa2018_spk_en = SUPERSONIC_AUD_SPK_EN, +}; + +static struct tpa6130_platform_data headset_amp_platform_data = { + .enable_rpc_server = 0, +}; + +static struct i2c_board_info i2c_devices[] = { + { + I2C_BOARD_INFO(ATMEL_QT602240_NAME, 0x94 >> 1), + .platform_data = &supersonic_atmel_ts_data, + .irq = MSM_GPIO_TO_INT(SUPERSONIC_GPIO_TP_INT_N) + }, +#ifdef CONFIG_MICROP_COMMON + { + I2C_BOARD_INFO(MICROP_I2C_NAME, 0xCC >> 1), + .platform_data = µp_data, + .irq = MSM_GPIO_TO_INT(SUPERSONIC_GPIO_UP_INT_N) + }, +#endif + { + I2C_BOARD_INFO("smb329", 0x6E >> 1), + }, + { + I2C_BOARD_INFO("tps65200", 0xD4 >> 1), + }, + { + I2C_BOARD_INFO("akm8973", 0x1C), + .platform_data = &compass_platform_data, + .irq = MSM_GPIO_TO_INT(SUPERSONIC_GPIO_COMPASS_INT_N), + }, + + { + I2C_BOARD_INFO("ov8810", 0x6C >> 1), + }, + { + I2C_BOARD_INFO("ov9665", 0x60 >> 1), + }, + { + I2C_BOARD_INFO(TPA6130_I2C_NAME, 0xC0 >> 1), + .platform_data = &headset_amp_platform_data, + }, + { + I2C_BOARD_INFO("tps65023", 0x48), + .platform_data = tps65023_data, + }, + { + I2C_BOARD_INFO("tpa2018d1", 0x58), + .platform_data = &tpa2018_data, + }, +}; + +#ifdef CONFIG_ARCH_QSD8X50 +static char bdaddress[20]; + +static void bt_export_bd_address(void) + { + unsigned char cTemp[6]; + + memcpy(cTemp, get_bt_bd_ram(), 6); + sprintf(bdaddress, "%02x:%02x:%02x:%02x:%02x:%02x", cTemp[0], cTemp[1], cTemp[2], cTemp[3], cTemp[4], cTemp[5]); + printk(KERN_INFO "YoYo--BD_ADDRESS=%s\n", bdaddress); +} + +module_param_string(bdaddress, bdaddress, sizeof(bdaddress), S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(bdaddress, "BT MAC ADDRESS"); +#endif + +static uint32_t camera_off_gpio_table[] = { + /* CAMERA SUSPEND*/ + PCOM_GPIO_CFG(0, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(1, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(2, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* PCLK */ + PCOM_GPIO_CFG(13, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* HSYNC */ + PCOM_GPIO_CFG(14, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* VSYNC */ + PCOM_GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* MCLK */ + PCOM_GPIO_CFG(99, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), /* CAM1_RST */ + PCOM_GPIO_CFG(100, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), /* CAM1_PWD */ +}; + +static uint32_t camera_on_gpio_table[] = { + /* CAMERA */ + PCOM_GPIO_CFG(0, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT0 */ + PCOM_GPIO_CFG(1, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT1 */ + PCOM_GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_16MA), /* PCLK */ + PCOM_GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* HSYNC */ + PCOM_GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* VSYNC */ + PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_16MA), /* MCLK */ +}; + +static void config_camera_on_gpios(void) +{ + config_gpio_table(camera_on_gpio_table, + ARRAY_SIZE(camera_on_gpio_table)); +} + +static void config_camera_off_gpios(void) +{ + config_gpio_table(camera_off_gpio_table, + ARRAY_SIZE(camera_off_gpio_table)); +} + + +static struct resource msm_camera_resources[] = { + { + .start = MSM_VFE_PHYS, + .end = MSM_VFE_PHYS + MSM_VFE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_VFE, + INT_VFE, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct msm_camera_device_platform_data msm_camera_device_data = { + .camera_gpio_on = config_camera_on_gpios, + .camera_gpio_off = config_camera_off_gpios, + .ioext.mdcphy = MSM_MDC_PHYS, + .ioext.mdcsz = MSM_MDC_SIZE, + .ioext.appphy = MSM_CLK_CTL_PHYS, + .ioext.appsz = MSM_CLK_CTL_SIZE, +}; + +static void supersonic_ov8810_clk_switch(void){ + int rc = 0; + pr_info("SuperSoinc: clk switch (supersonic)(ov8810)\n"); + rc = gpio_request(SUPERSONIC_CLK_SWITCH, "ov8810"); + if (rc < 0) + pr_err("GPIO (%d) request fail\n", SUPERSONIC_CLK_SWITCH); + else + gpio_direction_output(SUPERSONIC_CLK_SWITCH, 0); + gpio_free(SUPERSONIC_CLK_SWITCH); + + return; +} + +static void supersonic_ov9665_clk_switch(void){ + int rc = 0; + pr_info("SuperSoinc: Doing clk switch (supersonic)(ov9665)\n"); + rc = gpio_request(SUPERSONIC_CLK_SWITCH, "ov9665"); + if (rc < 0) + pr_err("GPIO (%d) request fail\n", SUPERSONIC_CLK_SWITCH); + else + gpio_direction_output(SUPERSONIC_CLK_SWITCH, 1); + gpio_free(SUPERSONIC_CLK_SWITCH); + + return; +} + +static struct camera_flash_cfg msm_camera_sensor_flash_cfg = { +// .camera_flash = flashlight_control, + .num_flash_levels = FLASHLIGHT_NUM, + .low_temp_limit = 10, + .low_cap_limit = 15, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_ov8810_data = { + .sensor_name = "ov8810", + .sensor_reset = SUPERSONIC_MAINCAM_RST, /* CAM1_RST */ + .sensor_pwd = SUPERSONIC_MAINCAM_PWD, /* CAM1_PWDN, enabled in a9 */ + .camera_clk_switch = supersonic_ov8810_clk_switch, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .waked_up = 0, + .need_suspend = 0, + .flash_cfg = &msm_camera_sensor_flash_cfg, +}; + +static struct platform_device msm_camera_sensor_ov8810 = { + .name = "msm_camera_ov8810", + .dev = { + .platform_data = &msm_camera_sensor_ov8810_data, + }, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_ov9665_data = { + .sensor_name = "ov9665", + .sensor_reset = SUPERSONIC_MAINCAM_RST, + .sensor_pwd = SUPERSONIC_2NDCAM_PWD, + .camera_clk_switch = supersonic_ov9665_clk_switch, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .waked_up = 0, + .need_suspend = 0, +}; + +static struct platform_device msm_camera_sensor_ov9665 = { + .name = "msm_camera_ov9665", + .dev = { + .platform_data = &msm_camera_sensor_ov9665_data, + }, +}; + +static void config_supersonic_flashlight_gpios(void) +{ + static uint32_t flashlight_gpio_table[] = { + PCOM_GPIO_CFG(SUPERSONIC_GPIO_FLASHLIGHT_TORCH, 0, + GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_GPIO_FLASHLIGHT_FLASH, 0, + GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_GPIO_FLASHLIGHT_FLASH_ADJ, 0, + GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + }; + config_gpio_table(flashlight_gpio_table, + ARRAY_SIZE(flashlight_gpio_table)); +} + +static struct flashlight_platform_data supersonic_flashlight_data = { + .gpio_init = config_supersonic_flashlight_gpios, + .torch = SUPERSONIC_GPIO_FLASHLIGHT_TORCH, + .flash = SUPERSONIC_GPIO_FLASHLIGHT_FLASH, + .flash_adj = SUPERSONIC_GPIO_FLASHLIGHT_FLASH_ADJ, + .flash_duration_ms = 600, + .led_count = 1, +}; + +static struct platform_device supersonic_flashlight_device = { + .name = FLASHLIGHT_NAME, + .dev = { + .platform_data = &supersonic_flashlight_data, + }, +}; + +static struct platform_device *devices[] __initdata = { +#ifndef CONFIG_MSM_SERIAL_DEBUGGER + &msm_device_uart1, +#endif +#ifdef CONFIG_SERIAL_MSM_HS + &msm_device_uart_dm1, +#endif + &supersonic_h2w, + &htc_battery_pdev, + &supersonic_audio_jack, + &ram_console_device, + &supersonic_rfkill, + &msm_device_smd, + &msm_device_nand, + &msm_device_hsusb, + &usb_mass_storage_device, +#ifdef CONFIG_USB_ANDROID_RNDIS + &rndis_device, +#endif + &android_usb_device, + &android_pmem_mdp_device, + &android_pmem_adsp_device, +// &android_pmem_camera_device, +#ifdef CONFIG_720P_CAMERA + &android_pmem_venc_device, +#endif +#ifdef CONFIG_BUILD_CIQ + &android_pmem_ciq_device, + &android_pmem_ciq1_device, + &android_pmem_ciq2_device, + &android_pmem_ciq3_device, +#endif + &msm_camera_sensor_ov8810, + &msm_kgsl_device, + &msm_device_i2c, + &msm_camera_sensor_ov9665, + &supersonic_flashlight_device, + &supersonic_leds, +#if defined(CONFIG_SPI_QSD) + &msm_device_spi, +#endif +}; + +static uint32_t usb_phy_3v3_table[] = { + PCOM_GPIO_CFG(SUPERSONIC_USB_PHY_3V3_ENABLE, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA) +}; + +static uint32_t usb_ID_PIN_input_table[] = { + PCOM_GPIO_CFG(SUPERSONIC_GPIO_USB_ID_PIN, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), +}; + +static uint32_t usb_ID_PIN_ouput_table[] = { + PCOM_GPIO_CFG(SUPERSONIC_GPIO_USB_ID_PIN, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), +}; + +static void config_supersonic_usb_id_gpios(bool output) +{ + if (output){ + config_gpio_table(usb_ID_PIN_ouput_table, ARRAY_SIZE(usb_ID_PIN_ouput_table)); + gpio_set_value(SUPERSONIC_GPIO_USB_ID_PIN, 1); + printk(KERN_INFO "%s %d output high\n", __func__, SUPERSONIC_GPIO_USB_ID_PIN); + }else{ + config_gpio_table(usb_ID_PIN_input_table, ARRAY_SIZE(usb_ID_PIN_input_table)); + printk(KERN_INFO "%s %d input none pull\n", __func__, SUPERSONIC_GPIO_USB_ID_PIN); + } +} + +static struct msm_acpu_clock_platform_data supersonic_clock_data = { + .acpu_switch_time_us = 20, + .max_speed_delta_khz = 256000, + .vdd_switch_time_us = 62, + .power_collapse_khz = 245000, + .wait_for_irq_khz = 245000, +}; + +int supersonic_init_mmc(int sysrev); + +#ifdef CONFIG_SERIAL_MSM_HS +extern void supersonic_config_bt_disable_active(void); +extern void supersonic_config_bt_disable_sleep(void); +extern int supersonic_is_bluetooth_off(void); + +static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { + .rx_wakeup_irq = MSM_GPIO_TO_INT(SUPERSONIC_GPIO_BT_HOST_WAKE), /*Chip to Device*/ + .inject_rx_on_wakeup = 0, + .exit_lpm_cb = bcm_bt_lpm_exit_lpm_locked, + +#if 0 + .config_as_uart = supersonic_config_bt_disable_active, + .config_as_gpio = supersonic_config_bt_disable_sleep, + .need_config = supersonic_is_bluetooth_off, +#endif +}; + +static struct bcm_bt_lpm_platform_data bcm_bt_lpm_pdata = { + .gpio_wake = SUPERSONIC_GPIO_BT_CHIP_WAKE, + .gpio_host_wake = SUPERSONIC_GPIO_BT_HOST_WAKE, + .request_clock_off_locked = msm_hs_request_clock_off_locked, + .request_clock_on_locked = msm_hs_request_clock_on_locked, +}; + +struct platform_device bcm_bt_lpm_device = { + .name = "bcm_bt_lpm", + .id = 0, + .dev = { + .platform_data = &bcm_bt_lpm_pdata, + }, +}; +#endif + + +static int OJ_BMA_power(void) +{ + int ret; + struct vreg *vreg = vreg_get(0, "synt"); + + if (!vreg) { + printk(KERN_ERR "%s: vreg error\n", __func__); + return -EIO; + } + ret = vreg_set_level(vreg, 2850); + + ret = vreg_enable(vreg); + if (ret < 0) + printk(KERN_ERR "%s: vreg enable failed\n", __func__); + + return 0; +} + +unsigned supersonic_get_skuid(void) +{ + return skuid; +} + +static ssize_t supersonic_virtual_keys_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, + __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":43:835:86:50" + ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":165:835:100:50" + ":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":300:835:110:50" + ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":425:835:90:50" + "\n"); +} + +static struct kobj_attribute supersonic_virtual_keys_attr = { + .attr = { + .name = "virtualkeys.atmel-touchscreen", + .mode = S_IRUGO, + }, + .show = &supersonic_virtual_keys_show, +}; + +static struct attribute *supersonic_properties_attrs[] = { + &supersonic_virtual_keys_attr.attr, + NULL +}; + +static struct attribute_group supersonic_properties_attr_group = { + .attrs = supersonic_properties_attrs, +}; + +static void supersonic_reset(void) +{ + gpio_set_value(SUPERSONIC_GPIO_PS_HOLD, 0); +} + +/* system_rev == higher 16bits of PCBID +XA -> 0000FFFF -> 0x0000 +XB -> 0101FFFF -> 0x0101 +XC -> 0202FFFF -> 0x0202 +*/ +static void __init supersonic_init(void) +{ + int ret; + struct kobject *properties_kobj; + + printk("supersonic_init() revision=%d\n", system_rev); + + msm_hw_reset_hook = supersonic_reset; + + OJ_BMA_power(); + + gpio_request(SUPERSONIC_GPIO_PROXIMITY_EN_N, "proximity_en"); + gpio_direction_output(SUPERSONIC_GPIO_PROXIMITY_EN_N, 0); + + msm_acpu_clock_init(&supersonic_clock_data); + +#if defined(CONFIG_MSM_SERIAL_DEBUGGER) + msm_serial_debug_init(MSM_UART1_PHYS, INT_UART1, + &msm_device_uart1.dev, 1, MSM_GPIO_TO_INT(SUPERSONIC_GPIO_UART1_RX)); +#endif + +#ifdef CONFIG_ARCH_QSD8X50 + bt_export_bd_address(); +#endif + + /* set the gpu power rail to manual mode so clk en/dis will not + * turn off gpu power, and hang it on resume */ + supersonic_kgsl_power_rail_mode(0); + supersonic_kgsl_power(true); + +#ifdef CONFIG_SPI_QSD + msm_device_spi.dev.platform_data = &supersonic_spi_pdata; +#endif + + #ifdef CONFIG_SERIAL_MSM_HS + msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; + msm_device_uart_dm1.name = "msm_serial_hs_bcm"; /* for bcm */ + #endif + + config_gpio_table(camera_off_gpio_table, + ARRAY_SIZE(camera_off_gpio_table)); + gpio_request(SUPERSONIC_GPIO_TP_EN, "tp_en"); + gpio_direction_output(SUPERSONIC_GPIO_TP_EN, 0); + + supersonic_audio_init(); + supersonic_init_panel(); +#ifdef CONFIG_MICROP_COMMON + supersonic_microp_init(); +#endif + + platform_add_devices(devices, ARRAY_SIZE(devices)); + if (!opt_usb_h2w_sw) { + msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; + config_supersonic_usb_id_gpios(0); + } + i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); + + ret = supersonic_init_mmc(system_rev); + if (ret != 0) + pr_crit("%s: Unable to initialize MMC\n", __func__); + + properties_kobj = kobject_create_and_add("board_properties", NULL); + if (properties_kobj) + ret = sysfs_create_group(properties_kobj, + &supersonic_properties_attr_group); + if (!properties_kobj || ret) + pr_err("failed to create board_properties\n"); + + msm_init_pmic_vibrator(); + config_gpio_table(usb_phy_3v3_table, ARRAY_SIZE(usb_phy_3v3_table)); + gpio_set_value(SUPERSONIC_USB_PHY_3V3_ENABLE, 1); + +} + +static void __init supersonic_fixup(struct machine_desc *desc, struct tag *tags, + char **cmdline, struct meminfo *mi) +{ + skuid = parse_tag_skuid((const struct tag *)tags); + printk(KERN_INFO "supersonic_fixup:skuid=0x%x\n", skuid); + /* First Bank 256MB */ + mi->nr_banks = 1; + mi->bank[0].start = PHYS_OFFSET; + mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); + mi->bank[0].size = MSM_EBI1_BANK0_SIZE; /*(219*1024*1024);*/ + + /* Second Bank 128MB */ + mi->nr_banks++; + mi->bank[1].start = MSM_EBI1_BANK1_BASE; + mi->bank[1].node = PHYS_TO_NID(MSM_EBI1_BANK1_BASE); + mi->bank[1].size = MSM_EBI1_BANK1_SIZE; +} + +static void __init supersonic_map_io(void) +{ + msm_map_qsd8x50_io(); + msm_clock_init(msm_clocks_8x50, msm_num_clocks_8x50); +} + +extern struct sys_timer msm_timer; + +MACHINE_START(SUPERSONIC, "supersonic") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x20000100, + .fixup = supersonic_fixup, + .map_io = supersonic_map_io, + .init_irq = msm_init_irq, + .init_machine = supersonic_init, + .timer = &msm_timer, +MACHINE_END diff --git a/arch/arm/mach-msm/board-supersonic.h b/arch/arm/mach-msm/board-supersonic.h new file mode 100644 index 0000000000000..d71d6c7eb1264 --- /dev/null +++ b/arch/arm/mach-msm/board-supersonic.h @@ -0,0 +1,195 @@ +/* arch/arm/mach-msm/board-supersonic.h + * + * Copyright (C) 2009 HTC Corporation. + * + * 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 __ARCH_ARM_MACH_MSM_BOARD_SUPERSONIC_H +#define __ARCH_ARM_MACH_MSM_BOARD_SUPERSONIC_H + +#include + +#define MSM_SMI_BASE 0x02B00000 +#define MSM_SMI_SIZE 0x01500000 + +#define MSM_HDMI_FB_BASE 0x02B00000 +#define MSM_HDMI_FB_SIZE 0x00400000 + +#define MSM_PMEM_VENC_BASE 0x02F00000 +#define MSM_PMEM_VENC_SIZE 0x00800000 + +#define MSM_GPU_MEM_BASE 0x03700000 +#define MSM_GPU_MEM_SIZE 0x00500000 + +#define MSM_RAM_CONSOLE_BASE 0x03C00000 +#define MSM_RAM_CONSOLE_SIZE 0x00040000 + +#ifdef CONFIG_BUILD_CIQ +#define MSM_PMEM_CIQ_BASE MSM_RAM_CONSOLE_BASE + MSM_RAM_CONSOLE_SIZE +#define MSM_PMEM_CIQ_SIZE SZ_64K +#define MSM_PMEM_CIQ1_BASE MSM_PMEM_CIQ_BASE +#define MSM_PMEM_CIQ1_SIZE MSM_PMEM_CIQ_SIZE +#define MSM_PMEM_CIQ2_BASE MSM_PMEM_CIQ_BASE +#define MSM_PMEM_CIQ2_SIZE MSM_PMEM_CIQ_SIZE +#define MSM_PMEM_CIQ3_BASE MSM_PMEM_CIQ_BASE +#define MSM_PMEM_CIQ3_SIZE MSM_PMEM_CIQ_SIZE +#endif + +#define MSM_FB_BASE 0x03D00000 +#define MSM_FB_SIZE 0x00300000 + +#define MSM_EBI1_BANK0_BASE 0x20000000 +//#define MSM_EBI1_BANK0_SIZE 0x0E000000 /* radio < 3210 */ +#define MSM_EBI1_BANK0_SIZE 0x0E800000 /*for radio >=3210 */ + +/* 4Gb/512MB DRAM */ +#define MSM_EBI1_BANK1_BASE 0x30000000 +#define MSM_EBI1_BANK1_SIZE 0x0C000000 + +#define MSM_PMEM_MDP_BASE 0x3C000000 +#define MSM_PMEM_MDP_SIZE 0x02000000 + +#define MSM_PMEM_ADSP_BASE 0x3E000000 +#define MSM_PMEM_ADSP_SIZE 0x02000000 + +#define SUPERSONIC_GPIO_UP_INT_N 35 +#define SUPERSONIC_GPIO_UP_RESET_N 108 + +#define SUPERSONIC_GPIO_TP_RST 34 +#define SUPERSONIC_GPIO_TP_INT_N 38 +#define SUPERSONIC_GPIO_TP_EN 100 /* V_TP3V3_EN */ + +//#define SUPERSONIC_GPIO_POWER_KEY 94 +#define SUPERSONIC_GPIO_SDMC_CD_N 28 + +/* BT */ +#define SUPERSONIC_GPIO_BT_UART1_RTS (43) +#define SUPERSONIC_GPIO_BT_UART1_CTS (44) +#define SUPERSONIC_GPIO_BT_UART1_RX (45) +#define SUPERSONIC_GPIO_BT_UART1_TX (46) +#define SUPERSONIC_GPIO_BT_RESET_N (27) +#define SUPERSONIC_GPIO_BT_SHUTDOWN_N (146) +#define SUPERSONIC_GPIO_BT_HOST_WAKE (86) +#define SUPERSONIC_GPIO_BT_CHIP_WAKE (87) + +#define SUPERSONIC_GPIO_COMPASS_RST_N 107 +#define SUPERSONIC_GPIO_COMPASS_INT_N 36 +#define SUPERSONIC_PROJECT_NAME "supersonic" +#define SUPERSONIC_LAYOUTS { \ + { { 0, 1, 0}, { -1, 0, 0}, {0, 0, 1} }, \ + { { 0, -1, 0}, { -1, 0, 0}, {0, 0, 1} }, \ + { { -1, 0, 0}, { 0, -1, 0}, {0, 0, 1} }, \ + { { 1, 0, 0}, { 0, 0, 1}, {0, 1, 0} } \ + } + +/* Proximity */ +#define SUPERSONIC_GPIO_PROXIMITY_EN_N 109 + +/* Battery */ +#define SUPERSONIC_GPIO_MBAT_IN 39 +#define SUPERSONIC_GPIO_MCHG_EN_N 22 +#define SUPERSONIC_GPIO_ISET 16 + +/*Audio */ +#define SUPERSONIC_AUD_JACKHP_EN 157 +#define SUPERSONIC_AUD_2V5_EN 26 +#define SUPERSONIC_AUD_SPK_EN 129 + +/* Bluetooth PCM */ +#define SUPERSONIC_BT_PCM_OUT 68 +#define SUPERSONIC_BT_PCM_IN 69 +#define SUPERSONIC_BT_PCM_SYNC 70 +#define SUPERSONIC_BT_PCM_CLK 71 + +//#define SUPERSONIC_MENU_KEY 40 +#define SUPERSONIC_VOLUME_UP 41 +#define SUPERSONIC_VOLUME_DOWN 42 +#define SUPERSONIC_POWER_KEY 94 + +/* flash light */ +#define SUPERSONIC_GPIO_FLASHLIGHT_FLASH (84) +#define SUPERSONIC_GPIO_FLASHLIGHT_TORCH (85) +#define SUPERSONIC_GPIO_FLASHLIGHT_FLASH_ADJ (31) + +/* AP Key Led turn on*/ +#define SUPERSONIC_AP_KEY_LED_EN (32) + +/* UART/USB switch : high -> UART, low -> HSUSB */ +#define SUPERSONIC_USB_UARTz_SW 33 +#define SUPERSONIC_WIMAX_CPU_UARTz_SW 160 + +/* USB PHY 3V3 enable*/ +#define SUPERSONIC_USB_PHY_3V3_ENABLE (104) +#define SUPERSONIC_GPIO_USB_CABLE_IN_PIN (82) +#define SUPERSONIC_GPIO_USB_ID_PIN (37) +/* 35mm headset */ +#define SUPERSONIC_GPIO_35MM_HEADSET_DET (153) +#if 0 /* TODO */ +//#define SUPERSONIC_GPIO_H2W_POWER (27) +//#define SUPERSONIC_GPIO_CABLE_IN1 (38) +#define SUPERSONIC_GPIO_CABLE_IN (37) +//#define SUPERSONIC_GPIO_H2W_DATA (139) +//#define SUPERSONIC_GPIO_H2W_CLK (140) +#endif + +/* UART1*/ +#define SUPERSONIC_GPIO_UART1_RX (139) +#define SUPERSONIC_GPIO_UART1_TX (140) + +/* Wifi */ +#define SUPERSONIC_GPIO_WIFI_SHUTDOWN_N 147 +#define SUPERSONIC_GPIO_WIFI_IRQ 152 +/*camera*/ +#define SUPERSONIC_MAINCAM_PWD 105 +#define SUPERSONIC_MAINCAM_RST 99 +#define SUPERSONIC_2NDCAM_PWD 120 +#define SUPERSONIC_CLK_SWITCH 102 + +#define SUPERSONIC_LCD_RST (113) +unsigned supersonic_get_skuid(void); + +/* HDMI */ +#define HDMI_RST (111) +#define V_HDMI_1V2_EN (119) +#define V_VGA_5V_SIL9022A_EN (127) +#define V_HDMI_3V3_EN (128) +#define SUPERSONIC_I2S_CLK (142) +#define SUPERSONIC_I2S_WS (143) +#define SUPERSONIC_I2S_DOUT (145) + +/* LCD RGB */ +#define SUPERSONIC_LCD_R0 (114) +#define SUPERSONIC_LCD_R1 (115) +#define SUPERSONIC_LCD_R2 (116) +#define SUPERSONIC_LCD_R3 (117) +#define SUPERSONIC_LCD_R4 (118) + +#define SUPERSONIC_LCD_G0 (121) +#define SUPERSONIC_LCD_G1 (122) +#define SUPERSONIC_LCD_G2 (123) +#define SUPERSONIC_LCD_G3 (124) +#define SUPERSONIC_LCD_G4 (125) +#define SUPERSONIC_LCD_G5 (126) + +#define SUPERSONIC_LCD_B0 (130) +#define SUPERSONIC_LCD_B1 (131) +#define SUPERSONIC_LCD_B2 (132) +#define SUPERSONIC_LCD_B3 (133) +#define SUPERSONIC_LCD_B4 (134) + +#define SUPERSONIC_LCD_PCLK (135) +#define SUPERSONIC_LCD_VSYNC (136) +#define SUPERSONIC_LCD_HSYNC (137) +#define SUPERSONIC_LCD_DE (138) + +#define SUPERSONIC_GPIO_PS_HOLD (25) + +#endif /* __ARCH_ARM_MACH_MSM_BOARD_SUPERSONIC_H */ From 606ad1e73a2704573047d7c84f865ec3f01e339c Mon Sep 17 00:00:00 2001 From: toastcfh Date: Mon, 8 Nov 2010 00:23:15 -0500 Subject: [PATCH 1020/2556] input: Atmel QT602240 touchscreen driver Support for the Atmel touchscreen found on the HTC Supersonic. Change-Id: Idec97ec6da7cd5e5ad95b3ba309c60c59f4c4df1 --- drivers/input/touchscreen/Kconfig | 13 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/atmel.c | 1359 ++++++++++++++++++++++++++++ include/linux/atmel_qt602240.h | 132 +++ include/linux/input.h | 2 + 5 files changed, 1507 insertions(+) create mode 100644 drivers/input/touchscreen/atmel.c create mode 100644 include/linux/atmel_qt602240.h diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index b4921be9d2493..a142ee9dfeff2 100755 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -11,6 +11,19 @@ menuconfig INPUT_TOUCHSCREEN if INPUT_TOUCHSCREEN +config TOUCHSCREEN_ATMEL + tristate "Atmel i2c touchscreen" + depends on I2C + help + This enables support for Atmel over I2C based touchscreens. + +config TOUCHSCREEN_COMPATIBLE_REPORT + bool "Touchscreen compatible report" + depends on I2C + default n + help + This enables support for old report style in touchscreen driver + config TOUCHSCREEN_88PM860X tristate "Marvell 88PM860x touchscreen" depends on MFD_88PM860X diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index fb925eba66207..71f04007865cd 100755 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -61,3 +61,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o +obj-$(CONFIG_TOUCHSCREEN_ATMEL) += atmel.o diff --git a/drivers/input/touchscreen/atmel.c b/drivers/input/touchscreen/atmel.c new file mode 100644 index 0000000000000..46c38780134b6 --- /dev/null +++ b/drivers/input/touchscreen/atmel.c @@ -0,0 +1,1359 @@ +/* drivers/input/touchscreen/atmel.c - ATMEL Touch driver + * + * Copyright (C) 2009 HTC Corporation. + * + * 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 + +#define ATMEL_EN_SYSFS +#define ATMEL_I2C_RETRY_TIMES 10 + +struct atmel_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct workqueue_struct *atmel_wq; + struct work_struct work; + int (*power) (int on); + struct early_suspend early_suspend; + struct info_id_t *id; + struct object_t *object_table; + uint8_t finger_count; + uint16_t abs_x_min; + uint16_t abs_x_max; + uint16_t abs_y_min; + uint16_t abs_y_max; + uint8_t abs_pressure_min; + uint8_t abs_pressure_max; + uint8_t abs_width_min; + uint8_t abs_width_max; + uint8_t first_pressed; + uint8_t debug_log_level; + struct atmel_finger_data finger_data[10]; + uint8_t finger_type; + uint8_t finger_support; + uint16_t finger_pressed; + uint8_t face_suppression; + uint8_t grip_suppression; + uint8_t noise_status[2]; + uint16_t *filter_level; + uint8_t calibration_confirm; + uint64_t timestamp; + struct atmel_config_data config_setting[2]; + uint8_t status; + uint8_t GCAF_sample; + uint8_t *GCAF_level; + uint8_t noisethr; +#ifdef ATMEL_EN_SYSFS + struct device dev; +#endif + +}; + +static struct atmel_ts_data *private_ts; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void atmel_ts_early_suspend(struct early_suspend *h); +static void atmel_ts_late_resume(struct early_suspend *h); +#endif + +int i2c_atmel_read(struct i2c_client *client, uint16_t address, uint8_t *data, uint8_t length) +{ + int retry; + uint8_t addr[2]; + + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + } + }; + addr[0] = address & 0xFF; + addr[1] = (address >> 8) & 0xFF; + + for (retry = 0; retry < ATMEL_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(client->adapter, msg, 2) == 2) + break; + mdelay(10); + } + if (retry == ATMEL_I2C_RETRY_TIMES) { + printk(KERN_ERR "i2c_read_block retry over %d\n", + ATMEL_I2C_RETRY_TIMES); + return -EIO; + } + return 0; + +} + +int i2c_atmel_write(struct i2c_client *client, uint16_t address, uint8_t *data, uint8_t length) +{ + int retry, loop_i; + uint8_t buf[length + 2]; + + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length + 2, + .buf = buf, + } + }; + + buf[0] = address & 0xFF; + buf[1] = (address >> 8) & 0xFF; + + for (loop_i = 0; loop_i < length; loop_i++) + buf[loop_i + 2] = data[loop_i]; + + for (retry = 0; retry < ATMEL_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) + break; + mdelay(10); + } + + if (retry == ATMEL_I2C_RETRY_TIMES) { + printk(KERN_ERR "i2c_write_block retry over %d\n", + ATMEL_I2C_RETRY_TIMES); + return -EIO; + } + return 0; + +} + +int i2c_atmel_write_byte_data(struct i2c_client *client, uint16_t address, uint8_t value) +{ + i2c_atmel_write(client, address, &value, 1); + return 0; +} + +uint16_t get_object_address(struct atmel_ts_data *ts, uint8_t object_type) +{ + uint8_t loop_i; + for (loop_i = 0; loop_i < ts->id->num_declared_objects; loop_i++) { + if (ts->object_table[loop_i].object_type == object_type) + return ts->object_table[loop_i].i2c_address; + } + return 0; +} +uint8_t get_object_size(struct atmel_ts_data *ts, uint8_t object_type) +{ + uint8_t loop_i; + for (loop_i = 0; loop_i < ts->id->num_declared_objects; loop_i++) { + if (ts->object_table[loop_i].object_type == object_type) + return ts->object_table[loop_i].size; + } + return 0; +} + +uint8_t get_report_ids_size(struct atmel_ts_data *ts, uint8_t object_type) +{ + uint8_t loop_i; + for (loop_i = 0; loop_i < ts->id->num_declared_objects; loop_i++) { + if (ts->object_table[loop_i].object_type == object_type) + return ts->object_table[loop_i].report_ids; + } + return 0; +} + +#ifdef ATMEL_EN_SYSFS +static ssize_t atmel_gpio_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + struct atmel_ts_data *ts_data; + struct atmel_i2c_platform_data *pdata; + + ts_data = private_ts; + pdata = ts_data->client->dev.platform_data; + + ret = gpio_get_value(pdata->gpio_irq); + printk(KERN_DEBUG "GPIO_TP_INT_N=%d\n", pdata->gpio_irq); + sprintf(buf, "GPIO_TP_INT_N=%d\n", ret); + ret = strlen(buf) + 1; + return ret; +} +static DEVICE_ATTR(gpio, 0444, atmel_gpio_show, NULL); +static ssize_t atmel_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + struct atmel_ts_data *ts_data; + ts_data = private_ts; + sprintf(buf, "%s_x%4.4X_x%4.4X\n", "ATMEL", + ts_data->id->family_id, ts_data->id->version); + ret = strlen(buf) + 1; + return ret; +} + +static DEVICE_ATTR(vendor, 0444, atmel_vendor_show, NULL); + +static uint16_t atmel_reg_addr; + +static ssize_t atmel_register_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + uint8_t ptr[1]; + struct atmel_ts_data *ts_data; + ts_data = private_ts; + if (i2c_atmel_read(ts_data->client, atmel_reg_addr, ptr, 1) < 0) { + printk(KERN_WARNING "%s: read fail\n", __func__); + return ret; + } + ret += sprintf(buf, "addr: %d, data: %d\n", atmel_reg_addr, ptr[0]); + return ret; +} + +static ssize_t atmel_register_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + struct atmel_ts_data *ts_data; + char buf_tmp[4], buf_zero[200]; + uint8_t write_da; + + ts_data = private_ts; + memset(buf_tmp, 0x0, sizeof(buf_tmp)); + if ((buf[0] == 'r' || buf[0] == 'w') && buf[1] == ':' && + (buf[5] == ':' || buf[5] == '\n')) { + memcpy(buf_tmp, buf + 2, 3); + atmel_reg_addr = simple_strtol(buf_tmp, NULL, 10); + printk(KERN_DEBUG "read addr: 0x%X\n", atmel_reg_addr); + if (!atmel_reg_addr) { + printk(KERN_WARNING "%s: string to number fail\n", + __func__); + return count; + } + printk(KERN_DEBUG "%s: set atmel_reg_addr is: %d\n", + __func__, atmel_reg_addr); + if (buf[0] == 'w' && buf[5] == ':' && buf[9] == '\n') { + memcpy(buf_tmp, buf + 6, 3); + write_da = simple_strtol(buf_tmp, NULL, 10); + printk(KERN_DEBUG "write addr: 0x%X, data: 0x%X\n", + atmel_reg_addr, write_da); + ret = i2c_atmel_write_byte_data(ts_data->client, + atmel_reg_addr, write_da); + if (ret < 0) { + printk(KERN_ERR "%s: write fail(%d)\n", + __func__, ret); + } + } + } + if ((buf[0] == '0') && (buf[1] == ':') && (buf[5] == ':')) { + memcpy(buf_tmp, buf + 2, 3); + atmel_reg_addr = simple_strtol(buf_tmp, NULL, 10); + memcpy(buf_tmp, buf + 6, 3); + memset(buf_zero, 0x0, sizeof(buf_zero)); + ret = i2c_atmel_write(ts_data->client, atmel_reg_addr, + buf_zero, simple_strtol(buf_tmp, NULL, 10) - atmel_reg_addr + 1); + if (buf[9] == 'r') { + i2c_atmel_write_byte_data(ts_data->client, + get_object_address(ts_data, GEN_COMMANDPROCESSOR_T6) + 1, 0x55); + i2c_atmel_write_byte_data(ts_data->client, + get_object_address(ts_data, GEN_COMMANDPROCESSOR_T6), 0x11); + } + } + + return count; +} + +static DEVICE_ATTR(register, 0644, atmel_register_show, atmel_register_store); + +static ssize_t atmel_regdump_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int count = 0, ret_t = 0; + struct atmel_ts_data *ts_data; + uint16_t loop_i; + uint8_t ptr[1]; + ts_data = private_ts; + if (ts_data->id->version >= 0x14) { + for (loop_i = 230; loop_i <= 425; loop_i++) { + ret_t = i2c_atmel_read(ts_data->client, loop_i, ptr, 1); + if (ret_t < 0) { + printk(KERN_WARNING "dump fail, addr: %d\n", + loop_i); + } + count += sprintf(buf + count, "addr[%3d]: %3d, ", + loop_i , *ptr); + if (((loop_i - 230) % 4) == 3) + count += sprintf(buf + count, "\n"); + } + count += sprintf(buf + count, "\n"); + } + return count; +} + +static ssize_t atmel_regdump_dump(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct atmel_ts_data *ts_data; + ts_data = private_ts; + if (buf[0] >= '0' && buf[0] <= '9' && buf[1] == '\n') + ts_data->debug_log_level = buf[0] - 0x30; + + return count; + +} + +static DEVICE_ATTR(regdump, 0644, atmel_regdump_show, atmel_regdump_dump); + +static ssize_t atmel_debug_level_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct atmel_ts_data *ts_data; + size_t count = 0; + ts_data = private_ts; + + count += sprintf(buf, "%d\n", ts_data->debug_log_level); + + return count; +} + +static ssize_t atmel_debug_level_dump(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct atmel_ts_data *ts_data; + ts_data = private_ts; + if (buf[0] >= '0' && buf[0] <= '9' && buf[1] == '\n') + ts_data->debug_log_level = buf[0] - 0x30; + + return count; +} + +static DEVICE_ATTR(debug_level, 0644, atmel_debug_level_show, atmel_debug_level_dump); + +static struct kobject *android_touch_kobj; + +static int atmel_touch_sysfs_init(void) +{ + int ret; + android_touch_kobj = kobject_create_and_add("android_touch", NULL); + if (android_touch_kobj == NULL) { + printk(KERN_ERR "%s: subsystem_register failed\n", __func__); + ret = -ENOMEM; + return ret; + } + ret = sysfs_create_file(android_touch_kobj, &dev_attr_gpio.attr); + if (ret) { + printk(KERN_ERR "%s: sysfs_create_file failed\n", __func__); + return ret; + } + ret = sysfs_create_file(android_touch_kobj, &dev_attr_vendor.attr); + if (ret) { + printk(KERN_ERR "%s: sysfs_create_file failed\n", __func__); + return ret; + } + atmel_reg_addr = 0; + ret = sysfs_create_file(android_touch_kobj, &dev_attr_register.attr); + if (ret) { + printk(KERN_ERR "%s: sysfs_create_file failed\n", __func__); + return ret; + } + ret = sysfs_create_file(android_touch_kobj, &dev_attr_regdump.attr); + if (ret) { + printk(KERN_ERR "%s: sysfs_create_file failed\n", __func__); + return ret; + } + ret = sysfs_create_file(android_touch_kobj, &dev_attr_debug_level.attr); + if (ret) { + printk(KERN_ERR "%s: sysfs_create_file failed\n", __func__); + return ret; + } + return 0; +} + +static void atmel_touch_sysfs_deinit(void) +{ + sysfs_remove_file(android_touch_kobj, &dev_attr_regdump.attr); + sysfs_remove_file(android_touch_kobj, &dev_attr_register.attr); + sysfs_remove_file(android_touch_kobj, &dev_attr_vendor.attr); + sysfs_remove_file(android_touch_kobj, &dev_attr_gpio.attr); + kobject_del(android_touch_kobj); +} + +#endif +static int check_delta(struct atmel_ts_data*ts) +{ + int8_t data[128]; + uint8_t loop_i; + int16_t rawdata, count = 0; + + memset(data, 0xFF, sizeof(data)); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + 5, 0x10); + + for (loop_i = 0; !(data[0] == 0x10 && data[1] == 0x00) && loop_i < 10; loop_i++) { + msleep(5); + i2c_atmel_read(ts->client, get_object_address(ts, DIAGNOSTIC_T37), data, 2); + } + if (loop_i == 10) + printk(KERN_ERR "%s: Diag data not ready\n", __func__); + + i2c_atmel_read(ts->client, get_object_address(ts, DIAGNOSTIC_T37), data, 128); + if (data[0] == 0x10 && data[1] == 0x00) { + for (loop_i = 2; loop_i < 127; loop_i += 2) { + rawdata = data[loop_i+1] << 8 | data[loop_i]; + if (abs(rawdata) > 50) + count++; + } + if (count > 32) + return 1; + } + return 0; +} + +static void check_calibration(struct atmel_ts_data*ts) +{ + uint8_t data[82]; + uint8_t loop_i, loop_j, x_limit = 0, check_mask, tch_ch = 0, atch_ch = 0; + + memset(data, 0xFF, sizeof(data)); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + 5, 0xF3); + + for (loop_i = 0; !(data[0] == 0xF3 && data[1] == 0x00) && loop_i < 10; loop_i++) { + msleep(5); + i2c_atmel_read(ts->client, get_object_address(ts, DIAGNOSTIC_T37), data, 2); + } + + if (loop_i == 10) + printk(KERN_ERR "%s: Diag data not ready\n", __func__); + + i2c_atmel_read(ts->client, get_object_address(ts, DIAGNOSTIC_T37), data, 82); + if (data[0] == 0xF3 && data[1] == 0x00) { + x_limit = 16 + ts->config_setting[0].config_T28[2]; + x_limit = x_limit << 1; + + for (loop_i = 0; loop_i < x_limit; loop_i += 2) { + for (loop_j = 0; loop_j < 8; loop_j++) { + check_mask = 1 << loop_j; + if (data[2 + loop_i] & check_mask) + tch_ch++; + if (data[3 + loop_i] & check_mask) + tch_ch++; + if (data[42 + loop_i] & check_mask) + atch_ch++; + if (data[43 + loop_i] & check_mask) + atch_ch++; + } + } + } + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + 5, 0x01); + + if (tch_ch && (atch_ch == 0)) { + if (jiffies > (ts->timestamp + HZ/2) && (ts->calibration_confirm == 1)) { + ts->calibration_confirm = 2; + printk(KERN_INFO "%s: calibration confirm\n", __func__); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 6, + ts->config_setting[ts->status].config_T8[6]); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 7, + ts->config_setting[ts->status].config_T8[7]); + } + if (ts->calibration_confirm < 2) + ts->calibration_confirm = 1; + ts->timestamp = jiffies; + } else if ((tch_ch - 25) <= atch_ch && (tch_ch || atch_ch)) { + ts->calibration_confirm = 0; + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + 2, 0x55); + } +} + +static void atmel_ts_work_func(struct work_struct *work) +{ + int ret; + struct atmel_ts_data *ts = container_of(work, struct atmel_ts_data, work); + uint8_t data[ts->finger_support * 9]; + + uint8_t loop_i, loop_j, report_type, msg_num, msg_byte_num = 8, finger_report; + msg_num = (ts->finger_count && ts->id->version >= 0x15) + ? ts->finger_count : 1; + + ret = i2c_atmel_read(ts->client, get_object_address(ts, + GEN_MESSAGEPROCESSOR_T5), data, msg_num * 9 - 2); + + if (ts->debug_log_level & 0x1) { + for (loop_i = 0; loop_i < msg_num * 9 - 2; loop_i++) + printk("0x%2.2X ", data[loop_i]); + printk("\n"); + } + + if (ts->id->version >= 0x15) { + for (loop_i = 0; loop_i < msg_num; loop_i++) { + report_type = data[loop_i * 9] - ts->finger_type; + if (report_type >= 0 && report_type < ts->finger_support) { + if (ts->calibration_confirm < 2 && ts->id->version >= 0x16) + check_calibration(ts); + ts->finger_data[report_type].x = data[loop_i * 9 + 2] << 2 | data[loop_i * 9 + 4] >> 6; + ts->finger_data[report_type].y = data[loop_i * 9 + 3] << 2 | (data[loop_i * 9 + 4] & 0x0C) >> 2; + ts->finger_data[report_type].w = data[loop_i * 9 + 5]; + ts->finger_data[report_type].z = data[loop_i * 9 + 6]; + if (data[loop_i * 9 + 1] & 0x20) { + if ((ts->grip_suppression >> report_type) & 1) + ts->grip_suppression &= ~(1 << report_type); + if (((ts->finger_pressed >> report_type) & 1) == 1) { + ts->finger_count--; + ts->finger_pressed &= ~(1 << report_type); + if (!ts->first_pressed) { + if (!ts->finger_count) + ts->first_pressed = 1; + printk(KERN_INFO "E%d@%d,%d\n", report_type + 1, + ts->finger_data[report_type].x, ts->finger_data[report_type].y); + } + } + } else if ((data[loop_i * 9 + 1] & 0xC0) && (((ts->finger_pressed >> report_type) & 1) == 0)) { + if (ts->filter_level[0]) { + if (ts->finger_data[report_type].x < ts->filter_level[0] || ts->finger_data[report_type].x > ts->filter_level[3]) + ts->grip_suppression |= 1 << report_type; + else if ((ts->finger_data[report_type].x < ts->filter_level[1] || ts->finger_data[report_type].x > ts->filter_level[2]) + && ((ts->grip_suppression >> report_type) & 1)) + ts->grip_suppression |= 1 << report_type; + else if (ts->finger_data[report_type].x > ts->filter_level[1] && ts->finger_data[report_type].x < ts->filter_level[2]) + ts->grip_suppression &= ~(1 << report_type); + } + if (((ts->grip_suppression >> report_type) & 1) == 0) { + if (!ts->first_pressed) + printk(KERN_INFO "S%d@%d,%d\n", report_type + 1, + ts->finger_data[report_type].x, ts->finger_data[report_type].y); + ts->finger_count++; + ts->finger_pressed |= 1 << report_type; + } + } + } else { + if (data[loop_i * 9] == get_report_ids_size(ts, GEN_COMMANDPROCESSOR_T6)) { + printk(KERN_INFO "Touch Status: "); + msg_byte_num = 5; + } else if (data[loop_i * 9] == get_report_ids_size(ts, PROCI_GRIPFACESUPPRESSION_T20)) { + if (ts->calibration_confirm < 2 && ts->id->version >= 0x16) + check_calibration(ts); + ts->face_suppression = data[loop_i * 9 + 1]; + printk(KERN_INFO "Touch Face suppression %s: ", + ts->face_suppression ? "Active" : "Inactive"); + msg_byte_num = 2; + } else if (data[loop_i * 9] == get_report_ids_size(ts, PROCG_NOISESUPPRESSION_T22)) { + if (data[loop_i * 9 + 1] == 0x10) /* reduce message print */ + msg_byte_num = 0; + else { + printk(KERN_INFO "Touch Noise suppression: "); + msg_byte_num = 4; + if (ts->status && data[loop_i * 9 + 2] >= ts->GCAF_sample) { + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_POWERCONFIG_T7), 0x08); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_POWERCONFIG_T7) + 1, 0x08); + for (loop_j = 0; loop_j < 5; loop_j++) { + if (ts->GCAF_sample < ts->GCAF_level[loop_j]) { + ts->GCAF_sample = ts->GCAF_level[loop_j]; + break; + } + } + if (loop_j == 5) + ts->GCAF_sample += 24; + if (ts->GCAF_sample >= 63) { + ts->GCAF_sample = 63; + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, PROCG_NOISESUPPRESSION_T22) + 8, + ts->config_setting[1].config[1]); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 7, 0x1); + } + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, SPT_CTECONFIG_T28) + 4, ts->GCAF_sample); + } + if (data[loop_i * 9 + 1] & 0x0C && ts->GCAF_sample == 63) { + ts->noisethr += 30; + if (ts->noisethr > 255) + ts->noisethr = 255; + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, PROCG_NOISESUPPRESSION_T22) + 8, + ts->noisethr); + } + } + } + if (data[loop_i * 9] != 0xFF) { + for (loop_j = 0; loop_j < msg_byte_num; loop_j++) + printk("0x%2.2X ", data[loop_i * 9 + loop_j]); + if (msg_byte_num) + printk("\n"); + } + } + if (loop_i == msg_num - 1) { + if (!ts->finger_count || ts->face_suppression) { + ts->finger_pressed = 0; + ts->finger_count = 0; +#ifdef CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); +#else + input_report_abs(ts->input_dev, ABS_MT_AMPLITUDE, 0); + input_report_abs(ts->input_dev, ABS_MT_POSITION, 1 << 31); +#endif + if (ts->debug_log_level & 0x2) + printk(KERN_INFO "Finger leave\n"); + } else { + for (loop_i = 0, finger_report = 0; loop_i < ts->finger_support; loop_i++) { + if (((ts->finger_pressed >> loop_i) & 1) == 1) { + finger_report++; +#ifdef CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, + ts->finger_data[loop_i].z); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, + ts->finger_data[loop_i].w); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, + ts->finger_data[loop_i].x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, + ts->finger_data[loop_i].y); + input_mt_sync(ts->input_dev); +#else + input_report_abs(ts->input_dev, ABS_MT_AMPLITUDE, + ts->finger_data[loop_i].z << 16 | ts->finger_data[loop_i].w); + input_report_abs(ts->input_dev, ABS_MT_POSITION, + (ts->finger_count == finger_report) << 31 | + ts->finger_data[loop_i].x << 16 | ts->finger_data[loop_i].y); +#endif + if (ts->debug_log_level & 0x2) + printk(KERN_INFO "Finger %d=> X:%d, Y:%d w:%d, z:%d, F:%d\n", + loop_i + 1, ts->finger_data[loop_i].x, + ts->finger_data[loop_i].y, ts->finger_data[loop_i].w, + ts->finger_data[loop_i].z, ts->finger_count); + } + } + } +#ifdef CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT + input_sync(ts->input_dev); +#endif + } + } + } else { /*read one message one time */ + report_type = data[0] - ts->finger_type; + if (report_type >= 0 && report_type < ts->finger_support) { + /* for issue debug only */ + if ((data[1] & 0x60) == 0x60) + printk(KERN_INFO"x60 ISSUE happened: %x, %x, %x, %x, %x, %x, %x, %x\n", + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); + if ((data[1] & 0x20) && (((ts->finger_pressed >> report_type) & 1) == 1)) { + ts->finger_count--; + ts->finger_pressed &= ~(1 << report_type); + } else if ((data[1] & 0xC0) && (((ts->finger_pressed >> report_type) & 1) == 0)) { + ts->finger_count++; + ts->finger_pressed |= 1 << report_type; + } + ts->finger_data[report_type].x = data[2] << 2 | data[4] >> 6; + ts->finger_data[report_type].y = data[3] << 2 | (data[4] & 0x0C) >> 2; + ts->finger_data[report_type].w = data[5]; + ts->finger_data[report_type].z = data[6]; + if (!ts->finger_count) { + ts->finger_pressed = 0; + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + if (ts->debug_log_level & 0x2) + printk(KERN_INFO "Finger leave\n"); + } else { + for (loop_i = 0; loop_i < ts->finger_support; loop_i++) { + if (((ts->finger_pressed >> loop_i) & 1) == 1) { + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, + ts->finger_data[loop_i].z); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, + ts->finger_data[loop_i].w); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, + ts->finger_data[loop_i].x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, + ts->finger_data[loop_i].y); + input_mt_sync(ts->input_dev); + if (ts->debug_log_level & 0x2) + printk(KERN_INFO "Finger %d=> X:%d, Y:%d w:%d, z:%d, F:%d\n", + loop_i + 1, ts->finger_data[loop_i].x, + ts->finger_data[loop_i].y, ts->finger_data[loop_i].w, + ts->finger_data[loop_i].z, ts->finger_count); + } + } + } + input_sync(ts->input_dev); + } else + printk(KERN_INFO"RAW data: %x, %x, %x, %x, %x, %x, %x, %x\n", + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); + } + enable_irq(ts->client->irq); +} + +static irqreturn_t atmel_ts_irq_handler(int irq, void *dev_id) +{ + struct atmel_ts_data *ts = dev_id; + + disable_irq_nosync(ts->client->irq); + queue_work(ts->atmel_wq, &ts->work); + return IRQ_HANDLED; +} + +static void cable_tp_status_handler_func(int connected) +{ + struct atmel_ts_data *ts; + ts = private_ts; + printk(KERN_INFO "Touch: cable change to %d\n", connected); + if (connected != ts->status) { + if (connected) + ts->status = 1; + else + ts->status = 0; + + if (ts->config_setting[1].config[0]) { + if (ts->status) { + ts->calibration_confirm = 2; + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 6, + ts->config_setting[ts->status].config_T8[6]); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 7, + ts->config_setting[ts->status].config_T8[7]); + } + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9) + 7, + ts->config_setting[ts->status].config[0]); + if (!ts->status) + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, PROCG_NOISESUPPRESSION_T22) + 8, + ts->config_setting[ts->status].config[1]); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, SPT_CTECONFIG_T28) + 3, + ts->config_setting[ts->status].config[2]); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, SPT_CTECONFIG_T28) + 4, + ts->config_setting[ts->status].config[3]); + ts->GCAF_sample = ts->config_setting[1].config[3]; + } else { + if (ts->config_setting[1].config_T7 != NULL) + i2c_atmel_write(ts->client, + get_object_address(ts, GEN_POWERCONFIG_T7), + ts->config_setting[ts->status].config_T7, + get_object_size(ts, GEN_POWERCONFIG_T7)); + if (ts->config_setting[1].config_T8 != NULL) + i2c_atmel_write(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8), + ts->config_setting[1].config_T8, + get_object_size(ts, GEN_ACQUISITIONCONFIG_T8)); + if (ts->config_setting[ts->status].config_T9 != NULL) + i2c_atmel_write(ts->client, + get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9), + ts->config_setting[ts->status].config_T9, + get_object_size(ts, TOUCH_MULTITOUCHSCREEN_T9)); + if (ts->config_setting[ts->status].config_T22 != NULL) + i2c_atmel_write(ts->client, + get_object_address(ts, PROCG_NOISESUPPRESSION_T22), + ts->config_setting[ts->status].config_T22, + get_object_size(ts, PROCG_NOISESUPPRESSION_T22)); + if (ts->config_setting[ts->status].config_T28 != NULL) { + i2c_atmel_write(ts->client, + get_object_address(ts, SPT_CTECONFIG_T28), + ts->config_setting[ts->status].config_T28, + get_object_size(ts, SPT_CTECONFIG_T28)); + ts->GCAF_sample = ts->config_setting[ts->status].config_T28[4]; + } + } + } +} + +static struct t_usb_status_notifier cable_status_handler = { + .name = "usb_tp_connected", + .func = cable_tp_status_handler_func, +}; + +static int atmel_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct atmel_ts_data *ts; + struct atmel_i2c_platform_data *pdata; + int ret = 0, i = 0, intr = 0; + uint8_t loop_i; + struct i2c_msg msg[2]; + uint8_t data[16]; + uint8_t type_count = 0, CRC_check = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + printk(KERN_ERR"%s: need I2C_FUNC_I2C\n", __func__); + ret = -ENODEV; + goto err_check_functionality_failed; + } + + ts = kzalloc(sizeof(struct atmel_ts_data), GFP_KERNEL); + if (ts == NULL) { + printk(KERN_ERR"%s: allocate atmel_ts_data failed\n", __func__); + ret = -ENOMEM; + goto err_alloc_data_failed; + } + + ts->atmel_wq = create_singlethread_workqueue("atmel_wq"); + if (!ts->atmel_wq) { + printk(KERN_ERR"%s: create workqueue failed\n", __func__); + ret = -ENOMEM; + goto err_cread_wq_failed; + } + + INIT_WORK(&ts->work, atmel_ts_work_func); + ts->client = client; + i2c_set_clientdata(client, ts); + pdata = client->dev.platform_data; + + if (pdata) { + ts->power = pdata->power; + intr = pdata->gpio_irq; + } + if (ts->power) { + ret = ts->power(1); + msleep(2); + if (ret < 0) { + printk(KERN_ERR "%s:power on failed\n", __func__); + goto err_power_failed; + } + } + + for (loop_i = 0; loop_i < 10; loop_i++) { + if (!gpio_get_value(intr)) + break; + msleep(10); + } + + if (loop_i == 10) + printk(KERN_ERR "No Messages\n"); + + /* read message*/ + msg[0].addr = ts->client->addr; + msg[0].flags = I2C_M_RD; + msg[0].len = 7; + msg[0].buf = data; + ret = i2c_transfer(client->adapter, msg, 1); + + if (ret < 0) { + printk(KERN_INFO "No Atmel chip inside\n"); + goto err_detect_failed; + } + printk(KERN_INFO "Touch: 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X\n", + data[0], data[1], data[2], data[3], data[4], data[5], data[6]); + + if (data[1] & 0x24) { + printk(KERN_INFO "atmel_ts_probe(): init err: %x\n", data[1]); + goto err_detect_failed; + } else { + for (loop_i = 0; loop_i < 10; loop_i++) { + if (gpio_get_value(intr)) { + printk(KERN_INFO "Touch: No more message\n"); + break; + } + ret = i2c_transfer(client->adapter, msg, 1); + printk(KERN_INFO "Touch: 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X\n", + data[0], data[1], data[2], data[3], data[4], data[5], data[6]); + msleep(10); + } + } + + /* Read the info block data. */ + ts->id = kzalloc(sizeof(struct info_id_t), GFP_KERNEL); + if (ts->id == NULL) { + printk(KERN_ERR"%s: allocate info_id_t failed\n", __func__); + goto err_alloc_failed; + } + ret = i2c_atmel_read(client, 0x00, data, 7); + + ts->id->family_id = data[0]; + ts->id->variant_id = data[1]; + if (ts->id->family_id == 0x80 && ts->id->variant_id == 0x10) + ts->id->version = data[2] + 6; + else + ts->id->version = data[2]; + ts->id->build = data[3]; + ts->id->matrix_x_size = data[4]; + ts->id->matrix_y_size = data[5]; + ts->id->num_declared_objects = data[6]; + + printk(KERN_INFO "info block: 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X\n", + ts->id->family_id, ts->id->variant_id, + ts->id->version, ts->id->build, + ts->id->matrix_x_size, ts->id->matrix_y_size, + ts->id->num_declared_objects); + + /* + Begin ffolkes multitouch selection + Purpose: Some hardware can't support more than 3 multitouch points, so revert back to 3 if we find a 004F touch sensor + BEGIN + */ + if(ts->id->family_id == 0x4F && pdata->config_T9[14] != 3) { + printk(KERN_INFO "%d point multitouch disabled due to possible hardware conflict, reverting to 3 point\n", pdata->config_T9[14]); + pdata->config_T9[14] = 3; + } else { + printk(KERN_INFO "%d point multitouch enabled\n", pdata->config_T9[14]); + } + /* + END + */ + + /* Read object table. */ + ts->object_table = kzalloc(sizeof(struct object_t)*ts->id->num_declared_objects, GFP_KERNEL); + if (ts->object_table == NULL) { + printk(KERN_ERR"%s: allocate object_table failed\n", __func__); + goto err_alloc_failed; + } + for (i = 0; i < ts->id->num_declared_objects; i++) { + ret = i2c_atmel_read(client, i * 6 + 0x07, data, 6); + ts->object_table[i].object_type = data[0]; + ts->object_table[i].i2c_address = data[1] | data[2] << 8; + ts->object_table[i].size = data[3] + 1; + ts->object_table[i].instances = data[4]; + ts->object_table[i].num_report_ids = data[5]; + if (data[5]) { + ts->object_table[i].report_ids = type_count + 1; + type_count += data[5]; + } + if (data[0] == 9) + ts->finger_type = ts->object_table[i].report_ids; + printk(KERN_INFO "Type: %2.2X, Start: %4.4X, Size: %2X, Instance: %2X, RD#: %2X, %2X\n", + ts->object_table[i].object_type , ts->object_table[i].i2c_address, + ts->object_table[i].size, ts->object_table[i].instances, + ts->object_table[i].num_report_ids, ts->object_table[i].report_ids); + } + + if (pdata) { + while (pdata->version > ts->id->version) + pdata++; + if (pdata->source) { + i2c_atmel_write_byte_data(client, + get_object_address(ts, SPT_GPIOPWM_T19), 0x7); + for (loop_i = 0; loop_i < 10; loop_i++) { + if (!gpio_get_value(intr)) + break; + msleep(10); + } + if (loop_i == 10) + printk(KERN_ERR "No Messages when check source\n"); + for (loop_i = 0; loop_i < 10; loop_i++) { + i2c_atmel_read(ts->client, get_object_address(ts, + GEN_MESSAGEPROCESSOR_T5), data, 2); + if (data[0] == get_report_ids_size(ts, SPT_GPIOPWM_T19)) { + while ((data[1] >> 3) != pdata->source) + pdata++; + break; + } + } + } + + ts->finger_support = pdata->config_T9[14]; + printk(KERN_INFO"finger_type: %d, max finger: %d\n", ts->finger_type, ts->finger_support); + + /* infoamtion block CRC check */ + if (pdata->object_crc[0]) { + ret = i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + 2, 0x55); + for (loop_i = 0; loop_i < 10; loop_i++) { + if (!gpio_get_value(intr)) { + ret = i2c_atmel_read(ts->client, get_object_address(ts, + GEN_MESSAGEPROCESSOR_T5), data, 5); + if (data[0] == get_report_ids_size(ts, GEN_COMMANDPROCESSOR_T6)) + break; + } + msleep(10); + } + if (loop_i == 10) + printk(KERN_INFO "Touch: No checksum read\n"); + else { + for (loop_i = 0; loop_i < 3; loop_i++) { + if (pdata->object_crc[loop_i] != data[loop_i + 2]) { + printk(KERN_ERR"CRC Error: %x, %x\n", pdata->object_crc[loop_i], data[loop_i + 2]); + break; + } + } + if (loop_i == 3) { + printk(KERN_INFO "CRC passed: "); + for (loop_i = 0; loop_i < 3; loop_i++) + printk("0x%2.2X ", pdata->object_crc[loop_i]); + printk("\n"); + CRC_check = 1; + } + } + } + ts->abs_x_min = pdata->abs_x_min; + ts->abs_x_max = pdata->abs_x_max; + ts->abs_y_min = pdata->abs_y_min; + ts->abs_y_max = pdata->abs_y_max; + ts->abs_pressure_min = pdata->abs_pressure_min; + ts->abs_pressure_max = pdata->abs_pressure_max; + ts->abs_width_min = pdata->abs_width_min; + ts->abs_width_max = pdata->abs_width_max; + ts->GCAF_level = pdata->GCAF_level; + printk(KERN_INFO "GCAF_level: %d, %d, %d, %d, %d\n", + ts->GCAF_level[0], ts->GCAF_level[1], ts->GCAF_level[2], + ts->GCAF_level[3], ts->GCAF_level[4]); + ts->filter_level = pdata->filter_level; + printk(KERN_INFO "filter_level: %d, %d, %d, %d\n", + ts->filter_level[0], ts->filter_level[1], ts->filter_level[2], ts->filter_level[3]); + + ts->config_setting[0].config_T7 + = ts->config_setting[1].config_T7 + = pdata->config_T7; + ts->config_setting[0].config_T8 + = ts->config_setting[1].config_T8 + = pdata->config_T8; + ts->config_setting[0].config_T9 = pdata->config_T9; + ts->config_setting[0].config_T22 = pdata->config_T22; + ts->config_setting[0].config_T28 = pdata->config_T28; + + if (pdata->cable_config[0]) { + ts->config_setting[0].config[0] = pdata->config_T9[7]; + ts->config_setting[0].config[1] = pdata->config_T22[8]; + ts->config_setting[0].config[2] = pdata->config_T28[3]; + ts->config_setting[0].config[3] = pdata->config_T28[4]; + for (loop_i = 0; loop_i < 4; loop_i++) + ts->config_setting[1].config[loop_i] = pdata->cable_config[loop_i]; + ts->GCAF_sample = ts->config_setting[1].config[3]; + ts->noisethr = pdata->cable_config[1]; + } else { + if (pdata->cable_config_T7[0]) + ts->config_setting[1].config_T7 = pdata->cable_config_T7; + if (pdata->cable_config_T8[0]) + ts->config_setting[1].config_T8 = pdata->cable_config_T8; + if (pdata->cable_config_T9[0]) { + ts->config_setting[1].config_T9 = pdata->cable_config_T9; + ts->config_setting[1].config_T22 = pdata->cable_config_T22; + ts->config_setting[1].config_T28 = pdata->cable_config_T28; + ts->GCAF_sample = ts->config_setting[ts->status].config_T28[4]; + } + } +#if 0 +///fix me!! TODO if (usb_get_connect_type()) + ts->status = 1; +#endif + if (!CRC_check) { + printk(KERN_INFO "Touch: Config reload\n"); + + i2c_atmel_write(ts->client, get_object_address(ts, SPT_CTECONFIG_T28), + pdata->config_T28, get_object_size(ts, SPT_CTECONFIG_T28)); + + ret = i2c_atmel_write_byte_data(client, get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + 1, 0x55); + msleep(10); + + ret = i2c_atmel_write_byte_data(client, get_object_address(ts, GEN_COMMANDPROCESSOR_T6), 0x11); + msleep(64); + + i2c_atmel_write(ts->client, get_object_address(ts, GEN_COMMANDPROCESSOR_T6), + pdata->config_T6, get_object_size(ts, GEN_COMMANDPROCESSOR_T6)); + i2c_atmel_write(ts->client, get_object_address(ts, GEN_POWERCONFIG_T7), + pdata->config_T7, get_object_size(ts, GEN_POWERCONFIG_T7)); + i2c_atmel_write(ts->client, get_object_address(ts, GEN_ACQUISITIONCONFIG_T8), + pdata->config_T8, get_object_size(ts, GEN_ACQUISITIONCONFIG_T8)); + i2c_atmel_write(ts->client, get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9), + pdata->config_T9, get_object_size(ts, TOUCH_MULTITOUCHSCREEN_T9)); + i2c_atmel_write(ts->client, get_object_address(ts, TOUCH_KEYARRAY_T15), + pdata->config_T15, get_object_size(ts, TOUCH_KEYARRAY_T15)); + i2c_atmel_write(ts->client, get_object_address(ts, SPT_GPIOPWM_T19), + pdata->config_T19, get_object_size(ts, SPT_GPIOPWM_T19)); + i2c_atmel_write(ts->client, get_object_address(ts, PROCI_GRIPFACESUPPRESSION_T20), + pdata->config_T20, get_object_size(ts, PROCI_GRIPFACESUPPRESSION_T20)); + i2c_atmel_write(ts->client, get_object_address(ts, PROCG_NOISESUPPRESSION_T22), + pdata->config_T22, get_object_size(ts, PROCG_NOISESUPPRESSION_T22)); + i2c_atmel_write(ts->client, get_object_address(ts, TOUCH_PROXIMITY_T23), + pdata->config_T23, get_object_size(ts, TOUCH_PROXIMITY_T23)); + i2c_atmel_write(ts->client, get_object_address(ts, PROCI_ONETOUCHGESTUREPROCESSOR_T24), + pdata->config_T24, get_object_size(ts, PROCI_ONETOUCHGESTUREPROCESSOR_T24)); + i2c_atmel_write(ts->client, get_object_address(ts, SPT_SELFTEST_T25), + pdata->config_T25, get_object_size(ts, SPT_SELFTEST_T25)); + i2c_atmel_write(ts->client, get_object_address(ts, PROCI_TWOTOUCHGESTUREPROCESSOR_T27), + pdata->config_T27, get_object_size(ts, PROCI_TWOTOUCHGESTUREPROCESSOR_T27)); + i2c_atmel_write(ts->client, get_object_address(ts, SPT_CTECONFIG_T28), + pdata->config_T28, get_object_size(ts, SPT_CTECONFIG_T28)); + + ret = i2c_atmel_write_byte_data(client, get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + 1, 0x55); + + for (loop_i = 0; loop_i < 10; loop_i++) { + if (!gpio_get_value(intr)) + break; + printk(KERN_INFO "Touch: wait for Message(%d)\n", loop_i + 1); + msleep(10); + } + + i2c_atmel_read(client, get_object_address(ts, GEN_MESSAGEPROCESSOR_T5), data, 7); + printk(KERN_INFO "Touch: 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X\n", + data[0], data[1], data[2], data[3], data[4], data[5], data[6]); + + ret = i2c_atmel_write_byte_data(client, get_object_address(ts, GEN_COMMANDPROCESSOR_T6), 0x11); + msleep(64); + + i2c_atmel_read(client, get_object_address(ts, GEN_MESSAGEPROCESSOR_T5), data, 7); + printk(KERN_INFO "Touch: 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X\n", + data[0], data[1], data[2], data[3], data[4], data[5], data[6]); + /* For Ace */ + } + + if (ts->status) { + printk(KERN_INFO "Touch: set cable config\n"); + if (ts->config_setting[1].config[0]) { + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9) + 7, + ts->config_setting[1].config[0]); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, SPT_CTECONFIG_T28) + 3, + ts->config_setting[1].config[2]); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, SPT_CTECONFIG_T28) + 4, + ts->config_setting[1].config[3]); + } else { + if (ts->config_setting[1].config_T7 != NULL) + i2c_atmel_write(ts->client, + get_object_address(ts, GEN_POWERCONFIG_T7), + ts->config_setting[1].config_T7, + get_object_size(ts, GEN_POWERCONFIG_T7)); + if (ts->config_setting[1].config_T8 != NULL) + i2c_atmel_write(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8), + ts->config_setting[1].config_T8, + get_object_size(ts, GEN_ACQUISITIONCONFIG_T8)); + if (ts->config_setting[1].config_T9 != NULL) + i2c_atmel_write(ts->client, + get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9), + ts->config_setting[1].config_T9, + get_object_size(ts, TOUCH_MULTITOUCHSCREEN_T9)); + if (ts->config_setting[1].config_T22 != NULL) + i2c_atmel_write(ts->client, + get_object_address(ts, PROCG_NOISESUPPRESSION_T22), + ts->config_setting[1].config_T22, + get_object_size(ts, PROCG_NOISESUPPRESSION_T22)); + if (ts->config_setting[1].config_T28 != NULL) { + i2c_atmel_write(ts->client, + get_object_address(ts, SPT_CTECONFIG_T28), + ts->config_setting[1].config_T28, + get_object_size(ts, SPT_CTECONFIG_T28)); + } + } + } + } + ts->input_dev = input_allocate_device(); + if (ts->input_dev == NULL) { + ret = -ENOMEM; + dev_err(&client->dev, "Failed to allocate input device\n"); + goto err_input_dev_alloc_failed; + } + ts->input_dev->name = "atmel-touchscreen"; + set_bit(EV_SYN, ts->input_dev->evbit); + set_bit(EV_KEY, ts->input_dev->evbit); + set_bit(BTN_TOUCH, ts->input_dev->keybit); + set_bit(BTN_2, ts->input_dev->keybit); + set_bit(EV_ABS, ts->input_dev->evbit); + + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, + ts->abs_x_min, ts->abs_x_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, + ts->abs_y_min, ts->abs_y_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, + ts->abs_pressure_min, ts->abs_pressure_max, + 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, + ts->abs_width_min, ts->abs_width_max, 0, 0); +#ifndef CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT + input_set_abs_params(ts->input_dev, ABS_MT_AMPLITUDE, + 0, ((ts->abs_pressure_max << 16) | ts->abs_width_max), 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION, + 0, ((1 << 31) | (ts->abs_x_max << 16) | ts->abs_y_max), 0, 0); +#endif + + + ret = input_register_device(ts->input_dev); + if (ret) { + dev_err(&client->dev, + "atmel_ts_probe: Unable to register %s input device\n", + ts->input_dev->name); + goto err_input_register_device_failed; + } + + ret = request_irq(client->irq, atmel_ts_irq_handler, IRQF_TRIGGER_LOW, + client->name, ts); + if (ret) + dev_err(&client->dev, "request_irq failed\n"); + +#ifdef CONFIG_HAS_EARLYSUSPEND + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_STOP_DRAWING - 1; + ts->early_suspend.suspend = atmel_ts_early_suspend; + ts->early_suspend.resume = atmel_ts_late_resume; + register_early_suspend(&ts->early_suspend); +#endif + + private_ts = ts; +#ifdef ATMEL_EN_SYSFS + atmel_touch_sysfs_init(); +#endif + + dev_info(&client->dev, "Start touchscreen %s in interrupt mode\n", + ts->input_dev->name); + +//fix me TODO usb_register_notifier(&cable_status_handler); + + return 0; + +err_input_register_device_failed: + input_free_device(ts->input_dev); + +err_input_dev_alloc_failed: +err_alloc_failed: +err_detect_failed: +err_power_failed: + destroy_workqueue(ts->atmel_wq); + +err_cread_wq_failed: + kfree(ts); + +err_alloc_data_failed: +err_check_functionality_failed: + + return ret; +} + +static int atmel_ts_remove(struct i2c_client *client) +{ + struct atmel_ts_data *ts = i2c_get_clientdata(client); + +#ifdef ATMEL_EN_SYSFS + atmel_touch_sysfs_deinit(); +#endif + + unregister_early_suspend(&ts->early_suspend); + free_irq(client->irq, ts); + + destroy_workqueue(ts->atmel_wq); + input_unregister_device(ts->input_dev); + kfree(ts); + + return 0; +} + +static int atmel_ts_suspend(struct i2c_client *client, pm_message_t mesg) +{ + int ret; + struct atmel_ts_data *ts = i2c_get_clientdata(client); + + printk(KERN_INFO "%s: enter\n", __func__); + + disable_irq(client->irq); + + ret = cancel_work_sync(&ts->work); + if (ret) + enable_irq(client->irq); + + ts->finger_pressed = 0; + ts->finger_count = 0; + ts->first_pressed = 0; + + i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_POWERCONFIG_T7), 0x0); + i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_POWERCONFIG_T7) + 1, 0x0); + + return 0; +} + +static int atmel_ts_resume(struct i2c_client *client) +{ + struct atmel_ts_data *ts = i2c_get_clientdata(client); + + i2c_atmel_write(ts->client, get_object_address(ts, GEN_POWERCONFIG_T7), + ts->config_setting[ts->status].config_T7, get_object_size(ts, GEN_POWERCONFIG_T7)); + if (ts->config_setting[1].config[0] && ts->status && !check_delta(ts)) { + ts->calibration_confirm = 2; + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 6, + ts->config_setting[ts->status].config_T8[6]); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 7, + ts->config_setting[ts->status].config_T8[7]); + } else { + ts->calibration_confirm = 0; + i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + 2, 0x55); + i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 6, 0x0); + i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 7, 0x0); + } + enable_irq(client->irq); + + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void atmel_ts_early_suspend(struct early_suspend *h) +{ + struct atmel_ts_data *ts; + ts = container_of(h, struct atmel_ts_data, early_suspend); + atmel_ts_suspend(ts->client, PMSG_SUSPEND); +} + +static void atmel_ts_late_resume(struct early_suspend *h) +{ + struct atmel_ts_data *ts; + ts = container_of(h, struct atmel_ts_data, early_suspend); + atmel_ts_resume(ts->client); +} +#endif + +static const struct i2c_device_id atml_ts_i2c_id[] = { + { ATMEL_QT602240_NAME, 0 }, + { } +}; + +static struct i2c_driver atmel_ts_driver = { + .id_table = atml_ts_i2c_id, + .probe = atmel_ts_probe, + .remove = atmel_ts_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = atmel_ts_suspend, + .resume = atmel_ts_resume, +#endif + .driver = { + .name = ATMEL_QT602240_NAME, + }, +}; + +static int __devinit atmel_ts_init(void) +{ + printk(KERN_INFO "atmel_ts_init():\n"); + return i2c_add_driver(&atmel_ts_driver); +} + +static void __exit atmel_ts_exit(void) +{ + i2c_del_driver(&atmel_ts_driver); +} + +module_init(atmel_ts_init); +module_exit(atmel_ts_exit); + +MODULE_DESCRIPTION("ATMEL Touch driver"); +MODULE_LICENSE("GPL"); + diff --git a/include/linux/atmel_qt602240.h b/include/linux/atmel_qt602240.h new file mode 100644 index 0000000000000..6c4b9d2e3f7e1 --- /dev/null +++ b/include/linux/atmel_qt602240.h @@ -0,0 +1,132 @@ +#ifndef _LINUX_ATMEL_H +#define _LINUX_ATMEL_H + +#define ATMEL_QT602240_NAME "atmel_qt602240" + +#define RESERVED_T0 0u +#define RESERVED_T1 1u +#define DEBUG_DELTAS_T2 2u +#define DEBUG_REFERENCES_T3 3u +#define DEBUG_SIGNALS_T4 4u +#define GEN_MESSAGEPROCESSOR_T5 5u +#define GEN_COMMANDPROCESSOR_T6 6u +#define GEN_POWERCONFIG_T7 7u +#define GEN_ACQUISITIONCONFIG_T8 8u +#define TOUCH_MULTITOUCHSCREEN_T9 9u +#define TOUCH_SINGLETOUCHSCREEN_T10 10u +#define TOUCH_XSLIDER_T11 11u +#define TOUCH_YSLIDER_T12 12u +#define TOUCH_XWHEEL_T13 13u +#define TOUCH_YWHEEL_T14 14u +#define TOUCH_KEYARRAY_T15 15u +#define PROCG_SIGNALFILTER_T16 16u +#define PROCI_LINEARIZATIONTABLE_T17 17u +#define SPT_COMCONFIG_T18 18u +#define SPT_GPIOPWM_T19 19u +#define PROCI_GRIPFACESUPPRESSION_T20 20u +#define RESERVED_T21 21u +#define PROCG_NOISESUPPRESSION_T22 22u +#define TOUCH_PROXIMITY_T23 23u +#define PROCI_ONETOUCHGESTUREPROCESSOR_T24 24u +#define SPT_SELFTEST_T25 25u +#define DEBUG_CTERANGE_T26 26u +#define PROCI_TWOTOUCHGESTUREPROCESSOR_T27 27u +#define SPT_CTECONFIG_T28 28u +#define SPT_GPI_T29 29u +#define SPT_GATE_T30 30u +#define TOUCH_KEYSET_T31 31u +#define TOUCH_XSLIDERSET_T32 32u +#define DIAGNOSTIC_T37 37u +struct info_id_t { + uint8_t family_id; + uint8_t variant_id; + uint8_t version; + uint8_t build; + uint8_t matrix_x_size; + uint8_t matrix_y_size; + uint8_t num_declared_objects; +}; + +struct object_t { + uint8_t object_type; + uint16_t i2c_address; + uint8_t size; + uint8_t instances; + uint8_t num_report_ids; + uint8_t report_ids; +}; + +struct atmel_virtual_key { + int keycode; + int range_min; + int range_max; +}; + +struct atmel_finger_data { + int x; + int y; + int w; + int z; +}; + +struct atmel_i2c_platform_data { + uint16_t version; + uint16_t source; + uint16_t abs_x_min; + uint16_t abs_x_max; + uint16_t abs_y_min; + uint16_t abs_y_max; + uint8_t abs_pressure_min; + uint8_t abs_pressure_max; + uint8_t abs_width_min; + uint8_t abs_width_max; + int gpio_irq; + int mach_type; + int (*power)(int on); + const char *input_name; + uint16_t key_type; + struct atmel_virtual_key *virtual_key; + uint8_t virtual_key_num; + uint8_t inactive_left; + uint8_t inactive_right; + uint8_t inactive_top; + uint8_t inactive_bottom; + uint16_t gap_area; + uint16_t key_area; + int8_t config_T6[6]; + int8_t config_T7[3]; + int8_t config_T8[8]; + int8_t config_T9[31]; + int8_t config_T15[11]; + int8_t config_T19[12]; + int8_t config_T20[12]; + int8_t config_T22[17]; + int8_t config_T23[13]; + int8_t config_T24[19]; + int8_t config_T25[14]; + int8_t config_T27[7]; + int8_t config_T28[6]; + uint8_t object_crc[3]; + int8_t cable_config[4]; + int8_t cable_config_T7[3]; + int8_t cable_config_T8[8]; + int8_t cable_config_T9[31]; + int8_t cable_config_T22[17]; + int8_t cable_config_T28[6]; + uint16_t filter_level[4]; + uint8_t GCAF_level[5]; + int display_width; /* display width in pixel */ + int display_height; /* display height in pixel */ +}; + +struct atmel_config_data { + int8_t config[4]; + int8_t *config_T7; + int8_t *config_T8; + int8_t *config_T9; + int8_t *config_T22; + int8_t *config_T28; +}; + +#endif + diff --git a/include/linux/input.h b/include/linux/input.h index e428382ca28a5..c49e04de0df05 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -760,6 +760,8 @@ struct input_keymap_entry { #define ABS_VOLUME 0x20 #define ABS_MISC 0x28 +#define ABS_MT_POSITION 0x2a /* Group a set of X and Y */ +#define ABS_MT_AMPLITUDE 0x2b /* Group a set of Z and W */ #define ABS_MT_SLOT 0x2f /* MT slot being modified */ #define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ From 109eb06c5df9b5c9c56acf2ec502260c9bdb4db3 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Mon, 8 Nov 2010 00:25:04 -0500 Subject: [PATCH 1021/2556] supersonic: TPA6130 amplifier driver Change-Id: Ibf6b5d46144586c581b938dcd155605ecb3b5e90 --- arch/arm/mach-msm/Makefile | 2 +- arch/arm/mach-msm/board-supersonic-tpa6130.c | 185 +++++++++++++++++++ arch/arm/mach-msm/include/mach/tpa6130.h | 21 +++ 3 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-msm/board-supersonic-tpa6130.c create mode 100644 arch/arm/mach-msm/include/mach/tpa6130.h diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 66d65be87bc8a..46bf53ca74e69 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -104,7 +104,7 @@ obj-$(CONFIG_MACH_SUPERSONIC) += msm_vibrator.o obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-microp.o obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-tpa2018d1.o obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-smb329.o -obj-$(CONFIG_MACH_SUPERSONIC) += drv_callback.o +obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-tpa6130.o obj-$(CONFIG_MACH_SUPERSONIC) += htc_wifi_nvs.o htc_bluetooth.o obj-$(CONFIG_MACH_SUPERSONIC) += htc_acoustic_qsd.o obj-$(CONFIG_MACH_SUPERSONIC) += devices_htc.o diff --git a/arch/arm/mach-msm/board-supersonic-tpa6130.c b/arch/arm/mach-msm/board-supersonic-tpa6130.c new file mode 100644 index 0000000000000..bd91aeae5f587 --- /dev/null +++ b/arch/arm/mach-msm/board-supersonic-tpa6130.c @@ -0,0 +1,185 @@ +/* driver/i2c/chip/tap6130.c + * + * TI TPA6130 Headset Amp + * + * Copyright (C) 2009 HTC Corporation + * + * 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 + +#define HEADSET_MTOA_PROG 0x30100003 +#define HEADSET_MTOA_VERS 0 +#define HTC_HEADSET_NULL_PROC 0 +#define HTC_HEADSET_CTL_PROC 1 + +static struct i2c_client *this_client; +struct mutex amp_mutex; +static struct tpa6130_platform_data *pdata; + +static int i2c_on; +char buffer[2]; + +static int I2C_TxData(char *txData, int length) +{ + struct i2c_msg msg[] = { + { + .addr = this_client->addr, + .flags = 0, + .len = length, + .buf = txData, + }, + }; + + if (i2c_transfer(this_client->adapter, msg, 1) < 0) { + pr_err("tpa6130 :I2C transfer error\n"); + return -EIO; + } else + return 0; +} + +void set_headset_amp(int on) +{ + mutex_lock(&_mutex); + if (on && !i2c_on) { + buffer[0] = 0x01; + buffer[1] = 0xC0; + buffer[2] = 0x3E; + if (I2C_TxData(buffer, 3) == 0) { + i2c_on = 1; + pr_err("tpa6130: turn on headset amp !\n"); + } + } else if (!on && i2c_on) { + buffer[0] = 0x01; + buffer[1] = 0xC1; + if (I2C_TxData(buffer, 2) == 0) { + i2c_on = 0; + pr_err("tpa6130: turn off headset amp !\n"); + } + } + mutex_unlock(&_mutex); +} + +static int handle_headset_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + struct rpc_headset_amp_ctl_args *args; + + if (!pdata->enable_rpc_server) + return 0; + + switch (req->procedure) { + case HTC_HEADSET_NULL_PROC: + return 0; + case HTC_HEADSET_CTL_PROC: + args = (struct rpc_headset_amp_ctl_args *)(req + 1); + args->on = be32_to_cpu(args->on); + if (args->on) { + gpio_set_value(pdata->gpio_hp_sd, 1); + msleep(10); + set_headset_amp(args->on); + } else if (!args->on) { + set_headset_amp(args->on); + gpio_set_value(pdata->gpio_hp_sd, 0); + } + return 0; + default: + pr_err("tpa6130a: the wrong proc for headset server\n"); + } + return -ENODEV; +} + +static struct msm_rpc_server headset_server = { + .prog = HEADSET_MTOA_PROG, + .vers = HEADSET_MTOA_VERS, + .rpc_call = handle_headset_call +}; + +int tpa6130_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + + pdata = client->dev.platform_data; + if (pdata == NULL) { + pr_err("tpa6130: platform data is NULL\n"); + goto fault; + } + + if (pdata->enable_rpc_server) { + msm_rpc_create_server(&headset_server); + + ret = gpio_request(pdata->gpio_hp_sd, "tpa6130"); + if (ret < 0) { + pr_err("tap6130a : gpio request failed\n"); + goto fault; + } + + ret = gpio_direction_output(pdata->gpio_hp_sd, 1); + if (ret < 0) { + pr_err("tap6130a: request reset gpio failed\n"); + goto fault; + } + gpio_set_value(pdata->gpio_hp_sd, 0); + } + + this_client = client; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("tpa6130a: i2c check functionality error\n"); + goto fault; + } + + return 0; +fault: + return -ENODEV; +} + +static int tpa6130_remove(struct i2c_client *client) +{ + return 0; +} +static const struct i2c_device_id tpa6130_id[] = { + { TPA6130_I2C_NAME, 0 }, + { } +}; + +static struct i2c_driver tpa6130_driver = { + .probe = tpa6130_probe, + .remove = tpa6130_remove, + .id_table = tpa6130_id, + .driver = { + .name = TPA6130_I2C_NAME, + }, +}; + +static int __init tpa6130_init(void) +{ + pr_err("tpa6130 HP AMP: init\n"); + mutex_init(&_mutex); + return i2c_add_driver(&tpa6130_driver); +} + +static void __exit tpa6130_exit(void) +{ + i2c_del_driver(&tpa6130_driver); +} + +module_init(tpa6130_init); +module_exit(tpa6130_exit); diff --git a/arch/arm/mach-msm/include/mach/tpa6130.h b/arch/arm/mach-msm/include/mach/tpa6130.h new file mode 100644 index 0000000000000..42124366081c1 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/tpa6130.h @@ -0,0 +1,21 @@ +/* + * Definitions for tpa6130a headset amp chip. + */ +#ifndef TPA6130_H +#define TPA6130_H + +#include + +#define TPA6130_I2C_NAME "tpa6130" +void set_headset_amp(int on); + +struct tpa6130_platform_data { + int gpio_hp_sd; + int enable_rpc_server; +}; + +struct rpc_headset_amp_ctl_args { + int on; +}; +#endif + From de97e84e24d08cb3112b674343186faf8df1cbe3 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Wed, 20 Oct 2010 17:46:12 -0400 Subject: [PATCH 1022/2556] mach-msm: add htc_bluetooth for using BT ATAG --- arch/arm/mach-msm/htc_bluetooth.c | 61 +++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 arch/arm/mach-msm/htc_bluetooth.c diff --git a/arch/arm/mach-msm/htc_bluetooth.c b/arch/arm/mach-msm/htc_bluetooth.c new file mode 100644 index 0000000000000..edb3b5205b39a --- /dev/null +++ b/arch/arm/mach-msm/htc_bluetooth.c @@ -0,0 +1,61 @@ +/* arch/arm/mach-msm/htc_bluetooth.c + * + * Code to extract Bluetooth bd_address information + * from ATAG set up by the bootloader. + * + * Copyright (C) 2009 HTC Corporation + * Author:Yomin Lin + * + */ + +#include +#include +#include + +#include + +/* configuration tags specific to Bluetooth*/ +#define ATAG_BLUETOOTH 0x43294329 + +#define ATAG_BT_DEBUG +#define MAX_BT_SIZE 0x8U +static unsigned char bt_bd_ram[MAX_BT_SIZE]; + +unsigned char *get_bt_bd_ram(void) +{ + return (bt_bd_ram); +} + +EXPORT_SYMBOL(get_bt_bd_ram); +#ifdef ATAG_BT_DEBUG +static int __init parse_tag_bt(const struct tag *tag) +{ + unsigned char *dptr=(unsigned char *)(&tag->u); + unsigned size; + unsigned i; + unsigned char *ptr; + + size=min((tag->hdr.size-2)*sizeof(__u32),MAX_BT_SIZE); + + ptr=dptr; + printk("BT Data size= %d, 0x%x,", tag->hdr.size, tag->hdr.tag); + for(i=0;iu); + unsigned size; + + size=min((tag->hdr.size-2)*sizeof(__u32),MAX_BT_SIZE); + memcpy((void *)bt_bd_ram ,(void *)dptr, size); + return 0; +} +#endif + __tagtable(ATAG_BLUETOOTH, parse_tag_bt); From e004b9a6cedf1f436727f544d42f1d60cfda9e7d Mon Sep 17 00:00:00 2001 From: jhansche Date: Mon, 8 Nov 2010 00:39:38 -0500 Subject: [PATCH 1023/2556] msm: HTC battery charger / driver Change-Id: I3e996138ad222bf0ebc646664892553470425d61 --- arch/arm/mach-msm/Makefile | 2 +- arch/arm/mach-msm/htc_battery.c | 1200 +++++++++++++++--- arch/arm/mach-msm/htc_battery_trout.c | 796 ++++++++++++ arch/arm/mach-msm/include/mach/htc_battery.h | 116 ++ include/linux/smb329.h | 23 + 5 files changed, 1961 insertions(+), 176 deletions(-) create mode 100644 arch/arm/mach-msm/htc_battery_trout.c create mode 100644 arch/arm/mach-msm/include/mach/htc_battery.h create mode 100644 include/linux/smb329.h diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 46bf53ca74e69..782bb8914ca32 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -54,7 +54,7 @@ obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o obj-$(CONFIG_MACH_TROUT) += board-trout-rfkill.o obj-$(CONFIG_MACH_TROUT) += board-trout-wifi.o obj-$(CONFIG_MACH_TROUT) += devices_htc.o -obj-$(CONFIG_TROUT_BATTCHG) += htc_battery.o +obj-$(CONFIG_TROUT_BATTCHG) += htc_battery_trout.o obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire.o board-sapphire-gpio.o obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-mmc.o board-sapphire-wifi.o diff --git a/arch/arm/mach-msm/htc_battery.c b/arch/arm/mach-msm/htc_battery.c index d6b9e8317f766..2426d1b6cd0a4 100644 --- a/arch/arm/mach-msm/htc_battery.c +++ b/arch/arm/mach-msm/htc_battery.c @@ -27,27 +27,59 @@ #include #include #include +#include /* Jay, to register display notifier */ +#include +#include +#ifdef CONFIG_HTC_BATTCHG_SMEM +#include "smd_private.h" +#endif + +#if defined(CONFIG_TROUT_BATTCHG_DOCK) +#include +#endif +#ifdef CONFIG_BATTERY_DS2784 +#include +#elif defined(CONFIG_BATTERY_DS2746) +#include +#endif + +#include static struct wake_lock vbus_wake_lock; -#define TRACE_BATT 0 +enum { + HTC_BATT_DEBUG_M2A_RPC = 1U << 0, + HTC_BATT_DEBUG_A2M_RPC = 1U << 1, + HTC_BATT_DEBUG_UEVT = 1U << 2, + HTC_BATT_DEBUG_USER_QUERY = 1U << 3, + HTC_BATT_DEBUG_USB_NOTIFY = 1U << 4, + HTC_BATT_DEBUG_SMEM = 1U << 5, +}; +static int htc_batt_debug_mask = HTC_BATT_DEBUG_M2A_RPC | HTC_BATT_DEBUG_A2M_RPC + | HTC_BATT_DEBUG_UEVT | HTC_BATT_DEBUG_USB_NOTIFY | HTC_BATT_DEBUG_SMEM; +module_param_named(debug_mask, htc_batt_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); -#if TRACE_BATT -#include +#define BATT_LOG(x...) do { \ +struct timespec ts; \ +struct rtc_time tm; \ +getnstimeofday(&ts); \ +rtc_time_to_tm(ts.tv_sec, &tm); \ +printk(KERN_INFO "batt: " x); \ +printk(" at %lld (%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", \ +ktime_to_ns(ktime_get()), tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \ +tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \ +} while (0) -#define BATT(x...) do { \ +#define BATT_ERR(x...) do { \ struct timespec ts; \ struct rtc_time tm; \ getnstimeofday(&ts); \ rtc_time_to_tm(ts.tv_sec, &tm); \ -printk(KERN_INFO "[BATT] " x); \ +printk(KERN_ERR "batt: err:" x); \ printk(" at %lld (%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", \ ktime_to_ns(ktime_get()), tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \ } while (0) -#else -#define BATT(x...) do {} while (0) -#endif /* rpc related */ #define APP_BATT_PDEV_NAME "rs30100001:00000000" @@ -58,54 +90,14 @@ tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \ #define HTC_PROCEDURE_GET_BATT_INFO 2 #define HTC_PROCEDURE_GET_CABLE_STATUS 3 #define HTC_PROCEDURE_SET_BATT_DELTA 4 - -/* module debugger */ -#define HTC_BATTERY_DEBUG 1 -#define BATTERY_PREVENTION 1 - -/* Enable this will shut down if no battery */ -#define ENABLE_BATTERY_DETECTION 0 -/* Sapphire pin changes: - * USB_ID (GPIO 90) is renamed to AC_IN (GPIO 30) - * CHARGER_EN (CPLD MISC2 bit[0]) is move to PMIC (MPP_14). - * ISET (CPLD MISC2 bit[1]) is move to PMIC (MPP_13). */ -#define GPIO_SAPPHIRE_USB_ID 30 - -#define GPIO_BATTERY_DETECTION 21 -#define GPIO_BATTERY_CHARGER_EN 128 - -/* Charge current selection */ -#define GPIO_BATTERY_CHARGER_CURRENT 129 - -typedef enum { - DISABLE = 0, - ENABLE_SLOW_CHG, - ENABLE_FAST_CHG -} batt_ctl_t; - -/* This order is the same as htc_power_supplies[] - * And it's also the same as htc_cable_status_update() - */ -typedef enum { - CHARGER_BATTERY = 0, - CHARGER_USB, - CHARGER_AC -} charger_type_t; +#define HTC_PROCEDURE_CHARGER_SWITCH 6 +#define HTC_PROCEDURE_SET_FULL_LEVEL 7 +#define HTC_PROCEDURE_GET_USB_ACCESSORY_ADC_LEVEL 10 const char *charger_tags[] = {"none", "USB", "AC"}; -struct battery_info_reply { - u32 batt_id; /* Battery ID from ADC */ - u32 batt_vol; /* Battery voltage from ADC */ - u32 batt_temp; /* Battery Temperature (C) from formula and ADC */ - u32 batt_current; /* Battery current from ADC */ - u32 level; /* formula */ - u32 charging_source; /* 0: no cable, 1:usb, 2:AC */ - u32 charging_enabled; /* 0: Disable, 1: Enable */ - u32 full_bat; /* Full capacity of battery (mAh) */ -}; - struct htc_battery_info { + int device_id; int present; unsigned long update_time; @@ -115,15 +107,25 @@ struct htc_battery_info { /* lock held while calling the arm9 to query the battery info */ struct mutex rpc_lock; struct battery_info_reply rep; + int (*func_show_batt_attr)(struct device_attribute *attr, char *buf); + int gpio_mbat_in; + int gpio_usb_id; + int gpio_mchg_en_n; + int gpio_iset; + int guage_driver; + int m2a_cable_detect; + int charger; }; static struct msm_rpc_endpoint *endpoint; static struct htc_battery_info htc_batt_info; -static unsigned int cache_time = 1000; +/* Remove cache mechanism to prevent cable status not sync. */ +static unsigned int cache_time; static int htc_battery_initial = 0; +static int htc_full_level_flag = 0; static enum power_supply_property htc_battery_properties[] = { POWER_SUPPLY_PROP_STATUS, @@ -146,11 +148,11 @@ static ssize_t htc_battery_show_property(struct device *dev, struct device_attribute *attr, char *buf); -static int htc_power_get_property(struct power_supply *psy, +static int htc_power_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val); -static int htc_battery_get_property(struct power_supply *psy, +static int htc_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val); @@ -182,15 +184,124 @@ static struct power_supply htc_power_supplies[] = { }, }; -static int g_usb_online; +static int update_batt_info(void); +extern void notify_usb_connected(int online); +//static int g_usb_online; +static struct t_usb_status_notifier usb_status_notifier = { + .name = "htc_battery", + .func = notify_usb_connected, +}; + +/* Move cable detection/notification to standard PMIC RPC. */ +static BLOCKING_NOTIFIER_HEAD(cable_status_notifier_list); +int register_notifier_cable_status(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&cable_status_notifier_list, nb); +} + +int unregister_notifier_cable_status(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&cable_status_notifier_list, nb); +} /* -------------------------------------------------------------------------- */ +/* For sleep charging screen. */ +static int zcharge_enabled; +static int htc_is_zcharge_enabled(void) +{ + return zcharge_enabled; +} +static int __init enable_zcharge_setup(char *str) +{ + int cal = simple_strtol(str, NULL, 0); + + zcharge_enabled = cal; + return 1; +} +__setup("enable_zcharge=", enable_zcharge_setup); + +static int htc_is_cable_in(void) +{ + if (!htc_batt_info.update_time) { + BATT_ERR("%s: battery driver hasn't been initialized yet.", __func__); + return -EINVAL; + } + return (htc_batt_info.rep.charging_source != CHARGER_BATTERY) ? 1 : 0; +} + +/** + * htc_power_policy - check if it obeys our policy + * return 0 for no errors, to indicate it follows policy. + * non zero otherwise. + **/ +static int __htc_power_policy(void) +{ + if (!zcharge_enabled) + return 0; + + if (htc_is_cable_in()) + return 1; + + return 0; +} + +/* + * Jay, 7/1/09' + */ +static int htc_power_policy(struct notifier_block *nfb, + unsigned long action, void *ignored) +{ + int rc; + switch (action) { + case NOTIFY_POWER: + pr_info("%s: enter.\n", __func__); + rc = __htc_power_policy(); + if (rc) + return NOTIFY_STOP; + else + return NOTIFY_OK; + } + return NOTIFY_DONE; /* we did not care other action here */ +} + +unsigned int batt_get_status(enum power_supply_property psp) +{ + union power_supply_propval val; + + if (update_batt_info()) + return -EINVAL; + + switch (psp) { + case POWER_SUPPLY_PROP_CAPACITY: + mutex_lock(&htc_batt_info.lock); + val.intval = htc_batt_info.rep.level; + mutex_unlock(&htc_batt_info.lock); + /* prevent shutdown before battery driver ready. */ + if (htc_batt_info.device_id == 0) + val.intval = 55; /* 55 == ?? */ + break; + case POWER_SUPPLY_PROP_TEMP: + mutex_lock(&htc_batt_info.lock); + val.intval = htc_batt_info.rep.batt_temp; + mutex_unlock(&htc_batt_info.lock); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + mutex_lock(&htc_batt_info.lock); + val.intval = htc_batt_info.rep.batt_vol; + mutex_unlock(&htc_batt_info.lock); + break; + default: + return -EINVAL; + } + + return val.intval; +} #if defined(CONFIG_DEBUG_FS) -int htc_battery_set_charging(batt_ctl_t ctl); +static int htc_battery_set_charging(enum batt_ctl_t ctl); static int batt_debug_set(void *data, u64 val) { - return htc_battery_set_charging((batt_ctl_t) val); + return htc_battery_set_charging((enum batt_ctl_t) val); } static int batt_debug_get(void *data, u64 *val) @@ -217,68 +328,65 @@ device_initcall(batt_debug_init); static int init_batt_gpio(void) { - if (!machine_is_trout()) - return 0; - if (gpio_request(GPIO_BATTERY_DETECTION, "batt_detect") < 0) + if (htc_batt_info.gpio_mbat_in > 0 && + gpio_request(htc_batt_info.gpio_mbat_in, "batt_detect") < 0) goto gpio_failed; - if (gpio_request(GPIO_BATTERY_CHARGER_EN, "charger_en") < 0) + if (htc_batt_info.gpio_mchg_en_n > 0 && + gpio_request(htc_batt_info.gpio_mchg_en_n, "charger_en") < 0) goto gpio_failed; - if (gpio_request(GPIO_BATTERY_CHARGER_CURRENT, "charge_current") < 0) + if (htc_batt_info.gpio_iset > 0 && + gpio_request(htc_batt_info.gpio_iset, "charge_current") < 0) goto gpio_failed; return 0; -gpio_failed: +gpio_failed: return -EINVAL; - + } -/* +/* * battery_charging_ctrl - battery charing control. * @ctl: battery control command * */ -static int battery_charging_ctrl(batt_ctl_t ctl) +int battery_charging_ctrl(enum batt_ctl_t ctl) { int result = 0; - /* The charing operations are move to A9 in Sapphire. */ - if (!machine_is_trout()) - return result; - switch (ctl) { case DISABLE: - BATT("charger OFF"); + BATT_LOG("charger OFF"); /* 0 for enable; 1 disable */ - result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 1); + result = gpio_direction_output(htc_batt_info.gpio_mchg_en_n, 1); break; case ENABLE_SLOW_CHG: - BATT("charger ON (SLOW)"); - result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 0); - result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0); + BATT_LOG("charger ON (SLOW)"); + result = gpio_direction_output(htc_batt_info.gpio_iset, 0); + result = gpio_direction_output(htc_batt_info.gpio_mchg_en_n, 0); break; case ENABLE_FAST_CHG: - BATT("charger ON (FAST)"); - result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 1); - result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0); + BATT_LOG("charger ON (FAST)"); + result = gpio_direction_output(htc_batt_info.gpio_iset, 1); + result = gpio_direction_output(htc_batt_info.gpio_mchg_en_n, 0); break; default: - printk(KERN_ERR "Not supported battery ctr called.!\n"); + BATT_ERR("%s: Not supported battery ctr called.!", __func__); result = -EINVAL; break; } - + return result; } -int htc_battery_set_charging(batt_ctl_t ctl) +static int htc_battery_set_charging(enum batt_ctl_t ctl) { int rc; - + if ((rc = battery_charging_ctrl(ctl)) < 0) goto result; - + if (!htc_battery_initial) { htc_batt_info.rep.charging_enabled = ctl & 0x3; } else { @@ -286,11 +394,11 @@ int htc_battery_set_charging(batt_ctl_t ctl) htc_batt_info.rep.charging_enabled = ctl & 0x3; mutex_unlock(&htc_batt_info.lock); } -result: +result: return rc; } -int htc_battery_status_update(u32 curr_level) +static int htc_battery_status_update(u32 curr_level) { int notify; if (!htc_battery_initial) @@ -300,106 +408,250 @@ int htc_battery_status_update(u32 curr_level) notify = (htc_batt_info.rep.level != curr_level); htc_batt_info.rep.level = curr_level; mutex_unlock(&htc_batt_info.lock); - - if (notify) +#if 0 + if (notify) { power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + if (htc_batt_debug_mask & HTC_BATT_DEBUG_UEVT) + BATT_LOG("power_supply_changed: battery"); + } +#else + /* we don't check level here for charging over temp RPC call */ + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + if (htc_batt_debug_mask & HTC_BATT_DEBUG_UEVT) + BATT_LOG("batt:power_supply_changed: battery"); +#endif return 0; } -int htc_cable_status_update(int status) +static void update_wake_lock(int status) +{ + if (status == CHARGER_USB) { + wake_lock(&vbus_wake_lock); + } else if (__htc_power_policy()) { + /* Lock suspend for DOPOD charging animation */ + wake_lock(&vbus_wake_lock); + } else { + /* give userspace some time to see the uevent and update + * LED state or whatnot... + */ + wake_lock_timeout(&vbus_wake_lock, HZ * 5); + } +} + +#ifdef CONFIG_HTC_BATTCHG_SMEM +static int htc_set_smem_cable_type(u32 cable_type); +#else +static int htc_set_smem_cable_type(u32 cable_type) { return -1; } +#endif +#if 1 //JH //this is for packet filter (notify port list while USB in/out) +int update_port_list_charging_state(int enable); +#endif +static int htc_cable_status_update(int status) { int rc = 0; - unsigned last_source; +// unsigned last_source; if (!htc_battery_initial) return 0; - + if (status < CHARGER_BATTERY || status > CHARGER_AC) { - BATT("%s: Not supported cable status received!", __func__); + BATT_ERR("%s: Not supported cable status received!", __func__); return -EINVAL; } + mutex_lock(&htc_batt_info.lock); +#if 1 + pr_info("batt: %s: %d -> %d\n", __func__, htc_batt_info.rep.charging_source, status); + if (status == htc_batt_info.rep.charging_source) { + /* When cable overvoltage(5V => 7V) A9 will report the same source, so only sent the uevent */ + if (status == CHARGER_USB) { + power_supply_changed(&htc_power_supplies[CHARGER_USB]); + if (htc_batt_debug_mask & HTC_BATT_DEBUG_UEVT) + BATT_LOG("batt:(htc_cable_status_update)power_supply_changed: OverVoltage"); + } + mutex_unlock(&htc_batt_info.lock); + return 0; + } + + /* TODO: replace charging_source to vbus_present */ + htc_batt_info.rep.charging_source = status; + /* ARM9 should know the status it notifies, + * keep this code for old projects. */ + /* htc_set_smem_cable_type(status); */ + + update_wake_lock(status); + /*ARM9 report CHARGER_AC while plug in htc_adaptor which is identify by usbid*/ + /*don't need to notify usb driver*/ + if ((htc_batt_info.guage_driver == GUAGE_MODEM) && (status == CHARGER_AC)) { + htc_set_smem_cable_type(CHARGER_AC); + power_supply_changed(&htc_power_supplies[CHARGER_AC]); + } else + msm_hsusb_set_vbus_state(!!htc_batt_info.rep.charging_source); + + /* TODO: use power_supply_change to notify battery drivers. */ + if (htc_batt_info.guage_driver == GUAGE_DS2784 || + htc_batt_info.guage_driver == GUAGE_DS2746) + blocking_notifier_call_chain(&cable_status_notifier_list, + status, NULL); + + if (status == CHARGER_BATTERY) { + htc_set_smem_cable_type(CHARGER_BATTERY); + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + if (htc_batt_debug_mask & HTC_BATT_DEBUG_UEVT) + BATT_LOG("batt:(htc_cable_status_update)power_supply_changed: battery"); + } + +#else /* A9 reports USB charging when helf AC cable in and China AC charger. */ - /* Work arround: notify userspace AC charging first, - and notify USB charging again when receiving usb connected notificaiton from usb driver. */ + /* notify userspace USB charging first, + and then usb driver will notify AC while D+/D- Line short. */ + /* China AC detection: + * Write SMEM as USB first, and update SMEM to AC + * if receives AC notification */ last_source = htc_batt_info.rep.charging_source; - if (status == CHARGER_USB && g_usb_online == 0) - htc_batt_info.rep.charging_source = CHARGER_AC; - else { + if (status == CHARGER_USB && g_usb_online == 0) { + htc_set_smem_cable_type(CHARGER_USB); + htc_batt_info.rep.charging_source = CHARGER_USB; + } else { + htc_set_smem_cable_type(status); htc_batt_info.rep.charging_source = status; /* usb driver will not notify usb offline. */ - if (status == CHARGER_BATTERY && g_usb_online == 1) + if (status == CHARGER_BATTERY && g_usb_online != 0) g_usb_online = 0; } - /* TODO: Don't call usb driver again with the same cable status. */ msm_hsusb_set_vbus_state(status == CHARGER_USB); + if (htc_batt_info.guage_driver == GUAGE_DS2784 || + htc_batt_info.guage_driver == GUAGE_DS2746) + blocking_notifier_call_chain(&cable_status_notifier_list, + htc_batt_info.rep.charging_source, NULL); if (htc_batt_info.rep.charging_source != last_source) { - if (htc_batt_info.rep.charging_source == CHARGER_USB || - htc_batt_info.rep.charging_source == CHARGER_AC) { - wake_lock(&vbus_wake_lock); - } else { - /* give userspace some time to see the uevent and update - * LED state or whatnot... - */ - wake_lock_timeout(&vbus_wake_lock, HZ / 2); - } +#if 1 //JH //this is for packet filter (notify port list while USB in/out) + update_port_list_charging_state(!(htc_batt_info.rep.charging_source == CHARGER_BATTERY)); +#endif + /* Lock suspend only when USB in for ADB or other USB functions. */ + if (htc_batt_info.rep.charging_source == CHARGER_USB) { + wake_lock(&vbus_wake_lock); + } else if (__htc_power_policy()) { + /* Lock suspend for DOPOD charging animation */ + wake_lock(&vbus_wake_lock); + } else { + if (htc_batt_info.rep.charging_source == CHARGER_AC + && last_source == CHARGER_USB) + BATT_ERR("%s: USB->AC\n", __func__); + /* give userspace some time to see the uevent and update + * LED state or whatnot... + */ + wake_lock_timeout(&vbus_wake_lock, HZ * 5); + } if (htc_batt_info.rep.charging_source == CHARGER_BATTERY || last_source == CHARGER_BATTERY) - power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); if (htc_batt_info.rep.charging_source == CHARGER_USB || last_source == CHARGER_USB) - power_supply_changed(&htc_power_supplies[CHARGER_USB]); + power_supply_changed(&htc_power_supplies[CHARGER_USB]); if (htc_batt_info.rep.charging_source == CHARGER_AC || last_source == CHARGER_AC) - power_supply_changed(&htc_power_supplies[CHARGER_AC]); + power_supply_changed(&htc_power_supplies[CHARGER_AC]); + if (htc_batt_debug_mask & HTC_BATT_DEBUG_UEVT) + BATT_LOG("power_supply_changed: %s -> %s", + charger_tags[last_source], charger_tags[htc_batt_info.rep.charging_source]); } +#endif mutex_unlock(&htc_batt_info.lock); return rc; } +#ifdef CONFIG_USB_ACCESSORY_DETECT_BY_ADC +int htc_get_usb_accessory_adc_level(uint32_t *buffer) +{ + struct rpc_request_hdr req; + + struct htc_get_usb_adc_value_rep { + struct rpc_reply_hdr hdr; + uint32_t adc_value; + } rep; + + int rc; + printk(KERN_INFO "%s\n", __func__); + + if (buffer == NULL) { + printk(KERN_INFO "%s: buffer null\n", __func__); + return -EINVAL; + } + + rc = msm_rpc_call_reply(endpoint, HTC_PROCEDURE_GET_USB_ACCESSORY_ADC_LEVEL, + &req, sizeof(req), + &rep, sizeof(rep), + 5 * HZ); + if (rc < 0) { + printk(KERN_INFO "%s: msm_rpc_call_reply fail\n", __func__); + return rc; + } + *buffer = be32_to_cpu(rep.adc_value); + + printk(KERN_INFO "%s: adc = %d\n", __func__, *buffer); + return 0; +} +EXPORT_SYMBOL(htc_get_usb_accessory_adc_level); +#endif + /* A9 reports USB charging when helf AC cable in and China AC charger. */ -/* Work arround: notify userspace AC charging first, -and notify USB charging again when receiving usb connected notification from usb driver. */ +/* notify userspace USB charging first, +and then usb driver will notify AC while D+/D- Line short. */ void notify_usb_connected(int online) { - mutex_lock(&htc_batt_info.lock); +#if 1 + pr_info("batt:online=%d",online); + /* TODO: replace charging_source to usb_status */ + htc_batt_info.rep.charging_source = online; + htc_set_smem_cable_type(htc_batt_info.rep.charging_source); - BATT("%s: online=%d, g_usb_online=%d", __func__, online, g_usb_online); + /* TODO: use power_supply_change to notify battery drivers. */ + if (htc_batt_info.guage_driver == GUAGE_DS2784 || htc_batt_info.guage_driver == GUAGE_DS2746) + blocking_notifier_call_chain(&cable_status_notifier_list, + htc_batt_info.rep.charging_source, NULL); + power_supply_changed(&htc_power_supplies[CHARGER_AC]); + power_supply_changed(&htc_power_supplies[CHARGER_USB]); + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + update_wake_lock(htc_batt_info.rep.charging_source); +#else + mutex_lock(&htc_batt_info.lock); + if (htc_batt_debug_mask & HTC_BATT_DEBUG_USB_NOTIFY) + BATT_LOG("%s: online=%d, g_usb_online=%d", __func__, online, g_usb_online); if (g_usb_online != online) { g_usb_online = online; - if (online && htc_batt_info.rep.charging_source == CHARGER_AC) { + if (online == CHARGER_AC && htc_batt_info.rep.charging_source == CHARGER_USB) { mutex_unlock(&htc_batt_info.lock); - htc_cable_status_update(CHARGER_USB); + htc_cable_status_update(CHARGER_AC); mutex_lock(&htc_batt_info.lock); - } else if (online) { - BATT("warning: usb connected but charging source=%d", htc_batt_info.rep.charging_source); } } mutex_unlock(&htc_batt_info.lock); +#endif } static int htc_get_batt_info(struct battery_info_reply *buffer) { struct rpc_request_hdr req; - + struct htc_get_batt_info_rep { struct rpc_reply_hdr hdr; struct battery_info_reply info; } rep; - + int rc; - if (buffer == NULL) + if (buffer == NULL) return -EINVAL; rc = msm_rpc_call_reply(endpoint, HTC_PROCEDURE_GET_BATT_INFO, &req, sizeof(req), &rep, sizeof(rep), 5 * HZ); - if ( rc < 0 ) + if ( rc < 0 ) return rc; - + mutex_lock(&htc_batt_info.lock); buffer->batt_id = be32_to_cpu(rep.info.batt_id); buffer->batt_vol = be32_to_cpu(rep.info.batt_vol); @@ -410,58 +662,338 @@ static int htc_get_batt_info(struct battery_info_reply *buffer) /* buffer->charging_source = be32_to_cpu(rep.info.charging_source); */ buffer->charging_enabled = be32_to_cpu(rep.info.charging_enabled); buffer->full_bat = be32_to_cpu(rep.info.full_bat); + /* Over_vchg only update in SMEM from A9 */ + /* buffer->over_vchg = be32_to_cpu(rep.info.over_vchg); */ mutex_unlock(&htc_batt_info.lock); + if (htc_batt_debug_mask & HTC_BATT_DEBUG_A2M_RPC) + BATT_LOG("A2M_RPC: get_batt_info: batt_id=%d, batt_vol=%d, batt_temp=%d, " + "batt_current=%d, level=%d, charging_source=%d, " + "charging_enabled=%d, full_bat=%d, over_vchg=%d", + buffer->batt_id, buffer->batt_vol, buffer->batt_temp, + buffer->batt_current, buffer->level, buffer->charging_source, + buffer->charging_enabled, buffer->full_bat, buffer->over_vchg); + + return 0; +} + +#ifdef CONFIG_HTC_BATTCHG_SMEM +struct htc_batt_info_full { + u32 batt_id; + u32 batt_vol; + u32 batt_vol_last; + u32 batt_temp; + s32 batt_current; + s32 batt_current_last; + u32 batt_discharge_current; + + u32 VREF_2; + u32 VREF; + u32 ADC4096_VREF; + + u32 Rtemp; + s32 Temp; + s32 Temp_last; + + u32 pd_M; + u32 MBAT_pd; + s32 I_MBAT; + + u32 pd_temp; + u32 percent_last; + u32 percent_update; + u32 dis_percent; + + u32 vbus; + u32 usbid; + u32 charging_source; + + u32 MBAT_IN; + u32 full_bat; + + u32 eval_current; + u32 eval_current_last; + u32 charging_enabled; + + u32 timeout; + u32 fullcharge; + u32 level; + u32 delta; + + u32 chg_time; + s32 level_change; + u32 sleep_timer_count; + u32 OT_led_on; + u32 overloading_charge; + + u32 a2m_cable_type; + u32 vchg; // VCHG => 0: Not, 1: In + u32 over_vchg; /*over voltage charger detection, 0:VCHG normal(below 6V) 1:VCHG over(upper 6V)*/ + u32 reserve4; + u32 reserve5; +}; + +/* SMEM_BATT_INFO is allocated by A9 after first A2M RPC is sent. */ +static struct htc_batt_info_full *smem_batt_info; + +static int htc_get_batt_info_smem(struct battery_info_reply *buffer) +{ + if (!smem_batt_info) { + smem_batt_info = smem_alloc(SMEM_BATT_INFO, + sizeof(struct htc_batt_info_full)); + if (!smem_batt_info) { + BATT_ERR("battery SMEM allocate fail, " + "use RPC instead of"); + return htc_get_batt_info(buffer); + } + } + + if (!buffer) + return -EINVAL; + + mutex_lock(&htc_batt_info.lock); + buffer->batt_id = smem_batt_info->batt_id; + buffer->batt_vol = smem_batt_info->batt_vol; + buffer->batt_temp = smem_batt_info->Temp; + buffer->batt_current = smem_batt_info->batt_current; + buffer->eval_current = smem_batt_info->eval_current; + /* Fix issue that recharging percent drop to 99%. */ + /* The level in SMEM is for A9 internal use, + * always use value reported by M2A level update RPC. */ +#if 0 + buffer->level = smem_batt_info->percent_update; +#endif + /* Move the rules of charging_source to cable_status_update. */ + /* buffer->charging_source = be32_to_cpu(smem_batt_info->charging_source); */ + buffer->charging_enabled = smem_batt_info->charging_enabled; + buffer->full_bat = smem_batt_info->full_bat; + buffer->over_vchg = smem_batt_info->over_vchg; + mutex_unlock(&htc_batt_info.lock); + + if (htc_batt_debug_mask & HTC_BATT_DEBUG_SMEM) + BATT_LOG("SMEM_BATT: get_batt_info: batt_id=%d, batt_vol=%d, batt_temp=%d, " + "batt_current=%d, eval_current=%d, level=%d, charging_source=%d, " + "charging_enabled=%d, full_bat=%d, over_vchg=%d", + buffer->batt_id, buffer->batt_vol, buffer->batt_temp, + buffer->batt_current, buffer->eval_current, buffer->level, buffer->charging_source, + buffer->charging_enabled, buffer->full_bat, buffer->over_vchg); + + return 0; +} + +static ssize_t htc_battery_show_smem(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int len = 0; + + if (!smem_batt_info) { + smem_batt_info = smem_alloc(SMEM_BATT_INFO, + sizeof(struct htc_batt_info_full)); + if (!smem_batt_info) { + BATT_ERR("Show SMEM: allocate fail"); + return 0; + } + } + + if (!strcmp(attr->attr.name, "smem_raw")) { + len = sizeof(struct htc_batt_info_full); + memcpy(buf, smem_batt_info, len); + } else if (!strcmp(attr->attr.name, "smem_text")) { + len += scnprintf(buf + len, PAGE_SIZE - len, + "batt_id: %d\n" + "batt_vol: %d\n" + "batt_vol_last: %d\n" + "batt_temp: %d\n" + "batt_current: %d\n" + "batt_current_last: %d\n" + "batt_discharge_current: %d\n" + "VREF_2: %d\n" + "VREF: %d\n" + "ADC4096_VREF: %d\n" + "Rtemp: %d\n" + "Temp: %d\n" + "Temp_last: %d\n" + "pd_M: %d\n" + "MBAT_pd: %d\n" + "I_MBAT: %d\n" + "pd_temp: %d\n" + "percent_last: %d\n" + "percent_update: %d\n" + "dis_percent: %d\n" + "vbus: %d\n" + "usbid: %d\n" + "charging_source: %d\n" + "MBAT_IN: %d\n" + "full_bat: %d\n" + "eval_current: %d\n" + "eval_current_last: %d\n" + "charging_enabled: %d\n" + "timeout: %d\n" + "fullcharge: %d\n" + "level: %d\n" + "delta: %d\n" + "chg_time: %d\n" + "level_change: %d\n" + "sleep_timer_count: %d\n" + "OT_led_on: %d\n" + "overloading_charge: %d\n" + "a2m_cable_type: %d\n" + "vchg: %d\n" + "over_vchg: %d\n", + smem_batt_info->batt_id, + smem_batt_info->batt_vol, + smem_batt_info->batt_vol_last, + smem_batt_info->batt_temp, + smem_batt_info->batt_current, + smem_batt_info->batt_current_last, + smem_batt_info->batt_discharge_current, + smem_batt_info->VREF_2, + smem_batt_info->VREF, + smem_batt_info->ADC4096_VREF, + smem_batt_info->Rtemp, + smem_batt_info->Temp, + smem_batt_info->Temp_last, + smem_batt_info->pd_M, + smem_batt_info->MBAT_pd, + smem_batt_info->I_MBAT, + smem_batt_info->pd_temp, + smem_batt_info->percent_last, + smem_batt_info->percent_update, + smem_batt_info->dis_percent, + smem_batt_info->vbus, + smem_batt_info->usbid, + smem_batt_info->charging_source, + smem_batt_info->MBAT_IN, + smem_batt_info->full_bat, + smem_batt_info->eval_current, + smem_batt_info->eval_current_last, + smem_batt_info->charging_enabled, + smem_batt_info->timeout, + smem_batt_info->fullcharge, + smem_batt_info->level, + smem_batt_info->delta, + smem_batt_info->chg_time, + smem_batt_info->level_change, + smem_batt_info->sleep_timer_count, + smem_batt_info->OT_led_on, + smem_batt_info->overloading_charge, + smem_batt_info->a2m_cable_type, + smem_batt_info->vchg, + smem_batt_info->over_vchg); + } + + return len; +} + +static int htc_set_smem_cable_type(u32 cable_type) +{ + if (!smem_batt_info) { + smem_batt_info = smem_alloc(SMEM_BATT_INFO, + sizeof(struct htc_batt_info_full)); + if (!smem_batt_info) { + BATT_ERR("Update SMEM: allocate fail"); + return -EINVAL; + } + } + + smem_batt_info->a2m_cable_type = cable_type; + BATT_LOG("Update SMEM: cable type %d", cable_type); + + return 0; +} +#endif +static ssize_t htc_battery_show_batt_attr(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + switch (htc_batt_info.guage_driver) { + case GUAGE_MODEM: +#ifdef CONFIG_HTC_BATTCHG_SMEM + return htc_battery_show_smem(dev, attr, buf); +#endif + break; + case GUAGE_DS2784: + case GUAGE_DS2746: + return htc_batt_info.func_show_batt_attr(attr, buf); + break; + } return 0; } /* -------------------------------------------------------------------------- */ -static int htc_power_get_property(struct power_supply *psy, +static int htc_power_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - charger_type_t charger; - + enum charger_type_t charger; + mutex_lock(&htc_batt_info.lock); + charger = htc_batt_info.rep.charging_source; + /* ARM9 decides charging_enabled value by battery id */ + if (htc_batt_info.rep.batt_id == 255) + charger = CHARGER_BATTERY; + mutex_unlock(&htc_batt_info.lock); switch (psp) { case POWER_SUPPLY_PROP_ONLINE: - if (psy->type == POWER_SUPPLY_TYPE_MAINS) + if (psy->type == POWER_SUPPLY_TYPE_MAINS) { val->intval = (charger == CHARGER_AC ? 1 : 0); - else if (psy->type == POWER_SUPPLY_TYPE_USB) + if (htc_batt_debug_mask & HTC_BATT_DEBUG_USER_QUERY) + BATT_LOG("%s: %s: online=%d", __func__, psy->name, val->intval); + } else if (psy->type == POWER_SUPPLY_TYPE_USB) { val->intval = (charger == CHARGER_USB ? 1 : 0); - else + if (htc_batt_debug_mask & HTC_BATT_DEBUG_USER_QUERY) + BATT_LOG("%s: %s: online=%d", __func__, psy->name, val->intval); + } else val->intval = 0; break; default: return -EINVAL; } - + return 0; } +/* Once charge full, set this flag */ +static int htc_charge_full = 0; + static int htc_battery_get_charging_status(void) { u32 level; - charger_type_t charger; + enum charger_type_t charger; int ret; - + mutex_lock(&htc_batt_info.lock); + charger = htc_batt_info.rep.charging_source; - + + /* ARM9 decides charging_enabled value by battery id */ + if (htc_batt_info.rep.batt_id == 255) + charger = CHARGER_UNKNOWN; + switch (charger) { case CHARGER_BATTERY: + htc_charge_full = 0; ret = POWER_SUPPLY_STATUS_NOT_CHARGING; break; case CHARGER_USB: case CHARGER_AC: + if ((htc_charge_full) && (htc_batt_info.rep.full_level == 100)) { + htc_batt_info.rep.level = 100; + } + level = htc_batt_info.rep.level; - if (level == 100) + if (level == 100){ + htc_charge_full = 1;} + if (htc_charge_full) ret = POWER_SUPPLY_STATUS_FULL; - else + else if (htc_batt_info.rep.charging_enabled != 0) ret = POWER_SUPPLY_STATUS_CHARGING; + else + ret = POWER_SUPPLY_STATUS_DISCHARGING; break; default: ret = POWER_SUPPLY_STATUS_UNKNOWN; @@ -470,32 +1002,51 @@ static int htc_battery_get_charging_status(void) return ret; } -static int htc_battery_get_property(struct power_supply *psy, +static int htc_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { switch (psp) { case POWER_SUPPLY_PROP_STATUS: val->intval = htc_battery_get_charging_status(); + if (htc_batt_debug_mask & HTC_BATT_DEBUG_USER_QUERY) + BATT_LOG("%s: %s: status=%d", __func__, psy->name, val->intval); break; case POWER_SUPPLY_PROP_HEALTH: val->intval = POWER_SUPPLY_HEALTH_GOOD; + if (machine_is_paradise() && (htc_batt_info.rep.batt_temp >= 500 || + htc_batt_info.rep.batt_temp <= 0)) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + else if (!machine_is_paradise() && (htc_batt_info.rep.batt_temp >= 480 || + htc_batt_info.rep.batt_temp <= 0)) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + if (htc_batt_debug_mask & HTC_BATT_DEBUG_USER_QUERY) + BATT_LOG("%s: %s: health=%d", __func__, psy->name, val->intval); break; case POWER_SUPPLY_PROP_PRESENT: val->intval = htc_batt_info.present; + if (htc_batt_debug_mask & HTC_BATT_DEBUG_USER_QUERY) + BATT_LOG("%s: %s: present=%d", __func__, psy->name, val->intval); break; case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + if (htc_batt_debug_mask & HTC_BATT_DEBUG_USER_QUERY) + BATT_LOG("%s: %s: technology=%d", __func__, psy->name, val->intval); break; case POWER_SUPPLY_PROP_CAPACITY: mutex_lock(&htc_batt_info.lock); val->intval = htc_batt_info.rep.level; + /* prevent shutdown before battery driver ready. */ + if (htc_batt_info.device_id == 0) + val->intval = 55; /* 55 == ?? */ mutex_unlock(&htc_batt_info.lock); + if (htc_batt_debug_mask & HTC_BATT_DEBUG_USER_QUERY) + BATT_LOG("%s: %s: capacity=%d", __func__, psy->name, val->intval); break; - default: + default: return -EINVAL; } - + return 0; } @@ -514,6 +1065,14 @@ static struct device_attribute htc_battery_attrs[] = { HTC_BATTERY_ATTR(charging_source), HTC_BATTERY_ATTR(charging_enabled), HTC_BATTERY_ATTR(full_bat), + HTC_BATTERY_ATTR(over_vchg), +/*[FIXME]__ATTR(batt_attr_raw, S_IRUGO, htc_battery_show_batt_attr, NULL),*/ +#ifdef CONFIG_HTC_BATTCHG_SMEM + __ATTR(smem_raw, S_IRUGO, htc_battery_show_smem, NULL), + __ATTR(smem_text, S_IRUGO, htc_battery_show_smem, NULL), +#else + __ATTR(batt_attr_text, S_IRUGO, htc_battery_show_batt_attr, NULL), +#endif }; enum { @@ -524,6 +1083,7 @@ enum { CHARGING_SOURCE, CHARGING_ENABLED, FULL_BAT, + OVER_VCHG, }; static int htc_rpc_set_delta(unsigned delta) @@ -538,6 +1098,29 @@ static int htc_rpc_set_delta(unsigned delta) &req, sizeof(req), 5 * HZ); } +static int htc_rpc_charger_switch(unsigned enable) +{ + struct charger_switch_req { + struct rpc_request_hdr hdr; + uint32_t data; + } req; + + req.data = cpu_to_be32(enable); + return msm_rpc_call(endpoint, HTC_PROCEDURE_CHARGER_SWITCH, + &req, sizeof(req), 5 * HZ); +} + +static int htc_rpc_set_full_level(unsigned level) +{ + struct set_batt_full_level_req { + struct rpc_request_hdr hdr; + uint32_t data; + } req; + + req.data = cpu_to_be32(level); + return msm_rpc_call(endpoint, HTC_PROCEDURE_SET_FULL_LEVEL, + &req, sizeof(req), 5 * HZ); +} static ssize_t htc_battery_set_delta(struct device *dev, struct device_attribute *attr, @@ -545,7 +1128,7 @@ static ssize_t htc_battery_set_delta(struct device *dev, { int rc; unsigned long delta = 0; - + delta = simple_strtoul(buf, NULL, 10); if (delta > 100) @@ -559,14 +1142,102 @@ static ssize_t htc_battery_set_delta(struct device *dev, return count; } +/* +* For PA and QA test +* 0x10-> fake temp to 250 +* 0x11->TBD if needed +* 0x12->TBD if needed +* .... +*/ +static ssize_t htc_battery_debug_flag(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long debug_flag; + debug_flag = simple_strtoul(buf, NULL, 10); + + if (debug_flag > 100 || debug_flag == 0) + return -EINVAL; + + mutex_lock(&htc_batt_info.lock); + blocking_notifier_call_chain(&cable_status_notifier_list, + debug_flag, 0); + mutex_unlock(&htc_batt_info.lock); + return 0; + +} + +static ssize_t htc_battery_charger_switch(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + unsigned long enable = 0; + + enable = simple_strtoul(buf, NULL, 10); + + if (enable > 1 || enable < 0) + return -EINVAL; + + mutex_lock(&htc_batt_info.rpc_lock); + rc = htc_rpc_charger_switch(enable); + mutex_unlock(&htc_batt_info.rpc_lock); + if (rc < 0) + return rc; + return count; +} + + +static ssize_t htc_battery_set_full_level(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc = 0; + unsigned long percent = 100; + unsigned long param = 0; + + percent = simple_strtoul(buf, NULL, 10); + + if (percent > 100 || percent == 0) + return -EINVAL; + + switch (htc_batt_info.guage_driver) { + case GUAGE_MODEM: + mutex_lock(&htc_batt_info.rpc_lock); + htc_batt_info.rep.full_level = percent; + rc = htc_rpc_set_full_level(percent); + mutex_unlock(&htc_batt_info.rpc_lock); + break; + case GUAGE_DS2784: + case GUAGE_DS2746: + if (htc_full_level_flag == 0) { + mutex_lock(&htc_batt_info.lock); + htc_full_level_flag = 1; + htc_batt_info.rep.full_level = percent; + param = percent; + blocking_notifier_call_chain(&cable_status_notifier_list, + 0xff, (void *) ¶m); + mutex_unlock(&htc_batt_info.lock); + } + rc = 0; + break; + } + if (rc < 0) + return rc; + return rc; +} + static struct device_attribute htc_set_delta_attrs[] = { __ATTR(delta, S_IWUSR | S_IWGRP, NULL, htc_battery_set_delta), + __ATTR(full_level, S_IWUSR | S_IWGRP, NULL, htc_battery_set_full_level), + __ATTR(batt_debug_flag,S_IWUSR | S_IWGRP, NULL, htc_battery_debug_flag), + __ATTR(charger_control, S_IWUSR | S_IWGRP, NULL, htc_battery_charger_switch), }; static int htc_battery_create_attrs(struct device * dev) { - int i, j, rc; - + int i = 0, j = 0, rc = 0; + for (i = 0; i < ARRAY_SIZE(htc_battery_attrs); i++) { rc = device_create_file(dev, &htc_battery_attrs[i]); if (rc) @@ -578,26 +1249,68 @@ static int htc_battery_create_attrs(struct device * dev) if (rc) goto htc_delta_attrs_failed; } - + goto succeed; - + htc_attrs_failed: while (i--) device_remove_file(dev, &htc_battery_attrs[i]); htc_delta_attrs_failed: while (j--) device_remove_file(dev, &htc_set_delta_attrs[i]); -succeed: +succeed: return rc; } +static int update_batt_info(void) +{ + int ret = 0; + + /* FIXME */ + switch (htc_batt_info.guage_driver) { + case GUAGE_MODEM: +#ifdef CONFIG_HTC_BATTCHG_SMEM + if (htc_get_batt_info_smem(&htc_batt_info.rep) < 0) { + BATT_ERR("%s: smem read failed!!!", __func__); + ret = -1; + } +#else + if (htc_get_batt_info(&htc_batt_info.rep) < 0) { + BATT_ERR("%s: rpc failed!!!", __func__); + ret = -1; + } +#endif + break; +#ifdef CONFIG_BATTERY_DS2784 + case GUAGE_DS2784: + if (ds2784_get_battery_info(&htc_batt_info.rep)) { + BATT_ERR("%s: ds2784 read failed!!!", __func__); + ret = -1; + } + break; +#elif CONFIG_BATTERY_DS2746 + case GUAGE_DS2746: + if (ds2746_get_battery_info(&htc_batt_info.rep)) { + BATT_ERR("%s: ds2746 read failed!!!", __func__); + ret = -1; + } + break; +#endif + + default: + return -EINVAL; + } + + return ret; +} + static ssize_t htc_battery_show_property(struct device *dev, struct device_attribute *attr, char *buf) { int i = 0; const ptrdiff_t off = attr - htc_battery_attrs; - + /* rpc lock is used to prevent two threads from calling * into the get info rpc at the same time */ @@ -606,14 +1319,14 @@ static ssize_t htc_battery_show_property(struct device *dev, /* check cache time to decide if we need to update */ if (htc_batt_info.update_time && time_before(jiffies, htc_batt_info.update_time + - msecs_to_jiffies(cache_time))) + msecs_to_jiffies(cache_time))) { + BATT_LOG("%s: use cached values", __func__); goto dont_need_update; - - if (htc_get_batt_info(&htc_batt_info.rep) < 0) { - printk(KERN_ERR "%s: rpc failed!!!\n", __FUNCTION__); - } else { - htc_batt_info.update_time = jiffies; } + + if (!update_batt_info()) + htc_batt_info.update_time = jiffies; + dont_need_update: mutex_unlock(&htc_batt_info.rpc_lock); @@ -642,28 +1355,40 @@ static ssize_t htc_battery_show_property(struct device *dev, case CHARGING_ENABLED: i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", htc_batt_info.rep.charging_enabled); - break; + break; case FULL_BAT: i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", htc_batt_info.rep.full_bat); break; + case OVER_VCHG: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.over_vchg); + break; default: i = -EINVAL; - } + } mutex_unlock(&htc_batt_info.lock); - + + if (htc_batt_debug_mask & HTC_BATT_DEBUG_USER_QUERY) { + if (i < 0) + BATT_LOG("%s: battery: attribute is not supported: %d", __func__, off); + else + BATT_LOG("%s: battery: %s=%s", __func__, attr->attr.name, buf); + } return i; } -static int htc_battery_probe(struct platform_device *pdev) +static int htc_battery_core_probe(struct platform_device *pdev) { int i, rc; /* init battery gpio */ + if (htc_batt_info.charger == LINEAR_CHARGER) { if ((rc = init_batt_gpio()) < 0) { - printk(KERN_ERR "%s: init battery gpio failed!\n", __FUNCTION__); + BATT_ERR("%s: init battery gpio failed!", __func__); return rc; } + } /* init structure data member */ htc_batt_info.update_time = jiffies; @@ -675,16 +1400,16 @@ static int htc_battery_probe(struct platform_device *pdev) /* init rpc */ endpoint = msm_rpc_connect(APP_BATT_PROG, APP_BATT_VER, 0); if (IS_ERR(endpoint)) { - printk(KERN_ERR "%s: init rpc failed! rc = %ld\n", - __FUNCTION__, PTR_ERR(endpoint)); - return rc; + BATT_ERR("%s: init rpc failed! rc = %ld", + __func__, PTR_ERR(endpoint)); + return -EINVAL; } /* init power supplier framework */ for (i = 0; i < ARRAY_SIZE(htc_power_supplies); i++) { rc = power_supply_register(&pdev->dev, &htc_power_supplies[i]); if (rc) - printk(KERN_ERR "Failed to register power supply (%d)\n", rc); + BATT_ERR("%s: Failed to register power supply (%d)", __func__, rc); } /* create htc detail attributes */ @@ -698,18 +1423,18 @@ static int htc_battery_probe(struct platform_device *pdev) mutex_lock(&htc_batt_info.rpc_lock); htc_batt_info.rep.charging_source = CHARGER_BATTERY; if (htc_get_batt_info(&htc_batt_info.rep) < 0) - printk(KERN_ERR "%s: get info failed\n", __FUNCTION__); + BATT_ERR("%s: get info failed", __func__); if (htc_rpc_set_delta(1) < 0) - printk(KERN_ERR "%s: set delta failed\n", __FUNCTION__); + BATT_ERR("%s: set delta failed", __func__); htc_batt_info.update_time = jiffies; mutex_unlock(&htc_batt_info.rpc_lock); return 0; } -static struct platform_driver htc_battery_driver = { - .probe = htc_battery_probe, +static struct platform_driver htc_battery_core_driver = { + .probe = htc_battery_core_probe, .driver = { .name = APP_BATT_PDEV_NAME, .owner = THIS_MODULE, @@ -721,7 +1446,7 @@ static struct platform_driver htc_battery_driver = { #define BATT_MTOA_VERS 0 #define RPC_BATT_MTOA_NULL 0 #define RPC_BATT_MTOA_SET_CHARGING_PROC 1 -#define RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC 2 +#define RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC 2 #define RPC_BATT_MTOA_LEVEL_UPDATE_PROC 3 struct rpc_batt_mtoa_set_charging_args { @@ -738,7 +1463,7 @@ struct rpc_dem_battery_update_args { static int handle_battery_call(struct msm_rpc_server *server, struct rpc_request_hdr *req, unsigned len) -{ +{ switch (req->procedure) { case RPC_BATT_MTOA_NULL: return 0; @@ -747,29 +1472,45 @@ static int handle_battery_call(struct msm_rpc_server *server, struct rpc_batt_mtoa_set_charging_args *args; args = (struct rpc_batt_mtoa_set_charging_args *)(req + 1); args->enable = be32_to_cpu(args->enable); - BATT("set_charging: enable=%d",args->enable); - htc_battery_set_charging(args->enable); + if (htc_batt_debug_mask & HTC_BATT_DEBUG_M2A_RPC) + BATT_LOG("M2A_RPC: set_charging: %d", args->enable); + if (htc_batt_info.charger == SWITCH_CHARGER) + blocking_notifier_call_chain(&cable_status_notifier_list, + args->enable, NULL); + else { + htc_battery_set_charging(args->enable); + } return 0; } case RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC: { struct rpc_batt_mtoa_cable_status_update_args *args; args = (struct rpc_batt_mtoa_cable_status_update_args *)(req + 1); args->status = be32_to_cpu(args->status); - BATT("cable_status_update: status=%d",args->status); + if (htc_batt_debug_mask & HTC_BATT_DEBUG_M2A_RPC) + BATT_LOG("M2A_RPC: cable_update: %s", charger_tags[args->status]); +#if 0 + /* FIXME: work arround for usb function, remove it after battery driver ready */ + if (machine_is_incrediblec() && args->status == CHARGER_AC) + args->status = CHARGER_USB; +#endif htc_cable_status_update(args->status); + #if defined(CONFIG_TROUT_BATTCHG_DOCK) + dock_detect_start(args->status); + #endif return 0; } case RPC_BATT_MTOA_LEVEL_UPDATE_PROC: { struct rpc_dem_battery_update_args *args; args = (struct rpc_dem_battery_update_args *)(req + 1); args->level = be32_to_cpu(args->level); - BATT("dem_battery_update: level=%d",args->level); + if (htc_batt_debug_mask & HTC_BATT_DEBUG_M2A_RPC) + BATT_LOG("M2A_RPC: level_update: %d", args->level); htc_battery_status_update(args->level); return 0; } default: - printk(KERN_ERR "%s: program 0x%08x:%d: unknown procedure %d\n", - __FUNCTION__, req->prog, req->vers, req->procedure); + BATT_ERR("%s: program 0x%08x:%d: unknown procedure %d", + __func__, req->prog, req->vers, req->procedure); return -ENODEV; } } @@ -780,17 +1521,126 @@ static struct msm_rpc_server battery_server = { .rpc_call = handle_battery_call, }; +#if defined(CONFIG_BATTERY_DS2784) || defined(CONFIG_BATTERY_DS2746) +static int ds2784_notifier_func(struct notifier_block *nfb, + unsigned long action, void *param) +{ + u8 arg = 0; + + if (param) + arg = *(u8 *)param; + + BATT_LOG("ds2784_notify: %ld %d", action, arg); + switch (action) { + case DS2784_CHARGING_CONTROL: + if (htc_batt_info.charger == LINEAR_CHARGER) + battery_charging_ctrl(arg); + else if(htc_batt_info.charger == SWITCH_CHARGER) + set_charger_ctrl(arg); + break; + case DS2784_LEVEL_UPDATE: + htc_battery_status_update(arg); + break; + case DS2784_BATTERY_FAULT: + case DS2784_OVER_TEMP: + htc_battery_status_update(htc_batt_info.rep.level); + break; + default: + return NOTIFY_BAD; + } + + return NOTIFY_OK; /* we did not care other action here */ +} + +static struct notifier_block ds2784_notifier = { + .notifier_call = ds2784_notifier_func, +}; + +#endif + +static int htc_battery_probe(struct platform_device *pdev) +{ + struct htc_battery_platform_data *pdata = pdev->dev.platform_data; + + htc_batt_info.device_id = pdev->id; + htc_batt_info.gpio_usb_id = pdata->gpio_usb_id; + htc_batt_info.guage_driver = pdata->guage_driver; + htc_batt_info.m2a_cable_detect = pdata->m2a_cable_detect; + htc_batt_info.func_show_batt_attr = pdata->func_show_batt_attr; + htc_batt_info.charger = pdata->charger; + htc_batt_info.rep.full_level = 100; + + if (htc_batt_info.charger == LINEAR_CHARGER) { + htc_batt_info.gpio_mbat_in = pdata->gpio_mbat_in; + htc_batt_info.gpio_mchg_en_n = pdata->gpio_mchg_en_n; + htc_batt_info.gpio_iset = pdata->gpio_iset; + } + + if (pdata->guage_driver == GUAGE_MODEM || + pdata->m2a_cable_detect) + msm_rpc_create_server(&battery_server); +#ifdef CONFIG_BATTERY_DS2784 + if (pdata->guage_driver == GUAGE_DS2784) + ds2784_register_notifier(&ds2784_notifier); +#elif CONFIG_BATTERY_DS2746 + if (pdata->guage_driver == GUAGE_DS2746) + ds2746_register_notifier(&ds2784_notifier); +#endif + + return 0; +} + +int get_cable_status(void) +{ +// if(htc_batt_info.rep.charging_source == CHARGER_AC || htc_batt_info.rep.charging_source == CHARGER_USB) +// htc_cable_status_update(htc_batt_info.rep.charging_source); + return htc_batt_info.rep.charging_source; +} + +static struct platform_driver htc_battery_driver = { + .probe = htc_battery_probe, + .driver = { + .name = "htc_battery", + .owner = THIS_MODULE, + }, +}; + +static struct notifier_block batt_notify = { + .notifier_call = htc_power_policy, +}; + +static BLOCKING_NOTIFIER_HEAD(battery_notifier_list); +int batt_register_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&battery_notifier_list, nb); +} + +int batt_unregister_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&battery_notifier_list, nb); +} + +int batt_notifier_call_chain(unsigned long val, void *v) +{ + return blocking_notifier_call_chain(&battery_notifier_list, val, v); +} + static int __init htc_battery_init(void) { wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present"); mutex_init(&htc_batt_info.lock); mutex_init(&htc_batt_info.rpc_lock); - msm_rpc_create_server(&battery_server); + usb_register_notifier(&usb_status_notifier); platform_driver_register(&htc_battery_driver); + platform_driver_register(&htc_battery_core_driver); + batt_register_client(&batt_notify); + /* Jay, The msm_fb need to consult htc_battery for power policy */ + display_notifier(htc_power_policy, NOTIFY_POWER); return 0; } module_init(htc_battery_init); MODULE_DESCRIPTION("HTC Battery Driver"); MODULE_LICENSE("GPL"); - +EXPORT_SYMBOL(htc_is_cable_in); +EXPORT_SYMBOL(htc_is_zcharge_enabled); diff --git a/arch/arm/mach-msm/htc_battery_trout.c b/arch/arm/mach-msm/htc_battery_trout.c new file mode 100644 index 0000000000000..e1dbbf55675e3 --- /dev/null +++ b/arch/arm/mach-msm/htc_battery_trout.c @@ -0,0 +1,796 @@ +/* arch/arm/mach-msm/htc_battery.c + * + * Copyright (C) 2008 HTC Corporation. + * Copyright (C) 2008 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 +#include +#include +#include +#include +#include +#include +#include +#include + +static struct wake_lock vbus_wake_lock; + +#define TRACE_BATT 0 + +#if TRACE_BATT +#include + +#define BATT(x...) do { \ +struct timespec ts; \ +struct rtc_time tm; \ +getnstimeofday(&ts); \ +rtc_time_to_tm(ts.tv_sec, &tm); \ +printk(KERN_INFO "[BATT] " x); \ +printk(" at %lld (%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", \ +ktime_to_ns(ktime_get()), tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \ +tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); \ +} while (0) +#else +#define BATT(x...) do {} while (0) +#endif + +/* rpc related */ +#define APP_BATT_PDEV_NAME "rs30100001:00000000" +#define APP_BATT_PROG 0x30100001 +#define APP_BATT_VER MSM_RPC_VERS(0,0) +#define HTC_PROCEDURE_BATTERY_NULL 0 +#define HTC_PROCEDURE_GET_BATT_LEVEL 1 +#define HTC_PROCEDURE_GET_BATT_INFO 2 +#define HTC_PROCEDURE_GET_CABLE_STATUS 3 +#define HTC_PROCEDURE_SET_BATT_DELTA 4 + +/* module debugger */ +#define HTC_BATTERY_DEBUG 1 +#define BATTERY_PREVENTION 1 + +/* Enable this will shut down if no battery */ +#define ENABLE_BATTERY_DETECTION 0 +/* Sapphire pin changes: + * USB_ID (GPIO 90) is renamed to AC_IN (GPIO 30) + * CHARGER_EN (CPLD MISC2 bit[0]) is move to PMIC (MPP_14). + * ISET (CPLD MISC2 bit[1]) is move to PMIC (MPP_13). */ +#define GPIO_SAPPHIRE_USB_ID 30 + +#define GPIO_BATTERY_DETECTION 21 +#define GPIO_BATTERY_CHARGER_EN 128 + +/* Charge current selection */ +#define GPIO_BATTERY_CHARGER_CURRENT 129 + +typedef enum { + DISABLE = 0, + ENABLE_SLOW_CHG, + ENABLE_FAST_CHG +} batt_ctl_t; + +/* This order is the same as htc_power_supplies[] + * And it's also the same as htc_cable_status_update() + */ +typedef enum { + CHARGER_BATTERY = 0, + CHARGER_USB, + CHARGER_AC +} charger_type_t; + +const char *charger_tags[] = {"none", "USB", "AC"}; + +struct battery_info_reply { + u32 batt_id; /* Battery ID from ADC */ + u32 batt_vol; /* Battery voltage from ADC */ + u32 batt_temp; /* Battery Temperature (C) from formula and ADC */ + u32 batt_current; /* Battery current from ADC */ + u32 level; /* formula */ + u32 charging_source; /* 0: no cable, 1:usb, 2:AC */ + u32 charging_enabled; /* 0: Disable, 1: Enable */ + u32 full_bat; /* Full capacity of battery (mAh) */ +}; + +struct htc_battery_info { + int present; + unsigned long update_time; + + /* lock to protect the battery info */ + struct mutex lock; + + /* lock held while calling the arm9 to query the battery info */ + struct mutex rpc_lock; + struct battery_info_reply rep; +}; + +static struct msm_rpc_endpoint *endpoint; + +static struct htc_battery_info htc_batt_info; + +static unsigned int cache_time = 1000; + +static int htc_battery_initial = 0; + +static enum power_supply_property htc_battery_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CAPACITY, +}; + +static enum power_supply_property htc_power_properties[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static char *supply_list[] = { + "battery", +}; + +/* HTC dedicated attributes */ +static ssize_t htc_battery_show_property(struct device *dev, + struct device_attribute *attr, + char *buf); + +static int htc_power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val); + +static int htc_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val); + +static struct power_supply htc_power_supplies[] = { + { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = htc_battery_properties, + .num_properties = ARRAY_SIZE(htc_battery_properties), + .get_property = htc_battery_get_property, + }, + { + .name = "usb", + .type = POWER_SUPPLY_TYPE_USB, + .supplied_to = supply_list, + .num_supplicants = ARRAY_SIZE(supply_list), + .properties = htc_power_properties, + .num_properties = ARRAY_SIZE(htc_power_properties), + .get_property = htc_power_get_property, + }, + { + .name = "ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .supplied_to = supply_list, + .num_supplicants = ARRAY_SIZE(supply_list), + .properties = htc_power_properties, + .num_properties = ARRAY_SIZE(htc_power_properties), + .get_property = htc_power_get_property, + }, +}; + +static int g_usb_online; + +/* -------------------------------------------------------------------------- */ + +#if defined(CONFIG_DEBUG_FS) +int htc_battery_set_charging(batt_ctl_t ctl); +static int batt_debug_set(void *data, u64 val) +{ + return htc_battery_set_charging((batt_ctl_t) val); +} + +static int batt_debug_get(void *data, u64 *val) +{ + return -ENOSYS; +} + +DEFINE_SIMPLE_ATTRIBUTE(batt_debug_fops, batt_debug_get, batt_debug_set, "%llu\n"); +static int __init batt_debug_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("htc_battery", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("charger_state", 0644, dent, NULL, &batt_debug_fops); + + return 0; +} + +device_initcall(batt_debug_init); +#endif + +static int init_batt_gpio(void) +{ + if (!machine_is_trout()) + return 0; + + if (gpio_request(GPIO_BATTERY_DETECTION, "batt_detect") < 0) + goto gpio_failed; + if (gpio_request(GPIO_BATTERY_CHARGER_EN, "charger_en") < 0) + goto gpio_failed; + if (gpio_request(GPIO_BATTERY_CHARGER_CURRENT, "charge_current") < 0) + goto gpio_failed; + + return 0; + +gpio_failed: + return -EINVAL; + +} + +/* + * battery_charging_ctrl - battery charing control. + * @ctl: battery control command + * + */ +static int battery_charging_ctrl(batt_ctl_t ctl) +{ + int result = 0; + + /* The charing operations are move to A9 in Sapphire. */ + if (!machine_is_trout()) + return result; + + switch (ctl) { + case DISABLE: + BATT("charger OFF"); + /* 0 for enable; 1 disable */ + result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 1); + break; + case ENABLE_SLOW_CHG: + BATT("charger ON (SLOW)"); + result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 0); + result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0); + break; + case ENABLE_FAST_CHG: + BATT("charger ON (FAST)"); + result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 1); + result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0); + break; + default: + printk(KERN_ERR "Not supported battery ctr called.!\n"); + result = -EINVAL; + break; + } + + return result; +} + +int htc_battery_set_charging(batt_ctl_t ctl) +{ + int rc; + + if ((rc = battery_charging_ctrl(ctl)) < 0) + goto result; + + if (!htc_battery_initial) { + htc_batt_info.rep.charging_enabled = ctl & 0x3; + } else { + mutex_lock(&htc_batt_info.lock); + htc_batt_info.rep.charging_enabled = ctl & 0x3; + mutex_unlock(&htc_batt_info.lock); + } +result: + return rc; +} + +int htc_battery_status_update(u32 curr_level) +{ + int notify; + if (!htc_battery_initial) + return 0; + + mutex_lock(&htc_batt_info.lock); + notify = (htc_batt_info.rep.level != curr_level); + htc_batt_info.rep.level = curr_level; + mutex_unlock(&htc_batt_info.lock); + + if (notify) + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + return 0; +} + +int htc_cable_status_update(int status) +{ + int rc = 0; + unsigned last_source; + + if (!htc_battery_initial) + return 0; + + if (status < CHARGER_BATTERY || status > CHARGER_AC) { + BATT("%s: Not supported cable status received!", __func__); + return -EINVAL; + } + mutex_lock(&htc_batt_info.lock); + /* A9 reports USB charging when helf AC cable in and China AC charger. */ + /* Work arround: notify userspace AC charging first, + and notify USB charging again when receiving usb connected notificaiton from usb driver. */ + last_source = htc_batt_info.rep.charging_source; + if (status == CHARGER_USB && g_usb_online == 0) + htc_batt_info.rep.charging_source = CHARGER_AC; + else { + htc_batt_info.rep.charging_source = status; + /* usb driver will not notify usb offline. */ + if (status == CHARGER_BATTERY && g_usb_online == 1) + g_usb_online = 0; + } + + /* TODO: Don't call usb driver again with the same cable status. */ + msm_hsusb_set_vbus_state(status == CHARGER_USB); + + if (htc_batt_info.rep.charging_source != last_source) { + if (htc_batt_info.rep.charging_source == CHARGER_USB || + htc_batt_info.rep.charging_source == CHARGER_AC) { + wake_lock(&vbus_wake_lock); + } else { + /* give userspace some time to see the uevent and update + * LED state or whatnot... + */ + wake_lock_timeout(&vbus_wake_lock, HZ / 2); + } + if (htc_batt_info.rep.charging_source == CHARGER_BATTERY || last_source == CHARGER_BATTERY) + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + if (htc_batt_info.rep.charging_source == CHARGER_USB || last_source == CHARGER_USB) + power_supply_changed(&htc_power_supplies[CHARGER_USB]); + if (htc_batt_info.rep.charging_source == CHARGER_AC || last_source == CHARGER_AC) + power_supply_changed(&htc_power_supplies[CHARGER_AC]); + } + mutex_unlock(&htc_batt_info.lock); + + return rc; +} + +/* A9 reports USB charging when helf AC cable in and China AC charger. */ +/* Work arround: notify userspace AC charging first, +and notify USB charging again when receiving usb connected notification from usb driver. */ +void notify_usb_connected(int online) +{ + mutex_lock(&htc_batt_info.lock); + + BATT("%s: online=%d, g_usb_online=%d", __func__, online, g_usb_online); + + if (g_usb_online != online) { + g_usb_online = online; + if (online && htc_batt_info.rep.charging_source == CHARGER_AC) { + mutex_unlock(&htc_batt_info.lock); + htc_cable_status_update(CHARGER_USB); + mutex_lock(&htc_batt_info.lock); + } else if (online) { + BATT("warning: usb connected but charging source=%d", htc_batt_info.rep.charging_source); + } + } + mutex_unlock(&htc_batt_info.lock); +} + +static int htc_get_batt_info(struct battery_info_reply *buffer) +{ + struct rpc_request_hdr req; + + struct htc_get_batt_info_rep { + struct rpc_reply_hdr hdr; + struct battery_info_reply info; + } rep; + + int rc; + + if (buffer == NULL) + return -EINVAL; + + rc = msm_rpc_call_reply(endpoint, HTC_PROCEDURE_GET_BATT_INFO, + &req, sizeof(req), + &rep, sizeof(rep), + 5 * HZ); + if ( rc < 0 ) + return rc; + + mutex_lock(&htc_batt_info.lock); + buffer->batt_id = be32_to_cpu(rep.info.batt_id); + buffer->batt_vol = be32_to_cpu(rep.info.batt_vol); + buffer->batt_temp = be32_to_cpu(rep.info.batt_temp); + buffer->batt_current = be32_to_cpu(rep.info.batt_current); + buffer->level = be32_to_cpu(rep.info.level); + /* Move the rules of charging_source to cable_status_update. */ + /* buffer->charging_source = be32_to_cpu(rep.info.charging_source); */ + buffer->charging_enabled = be32_to_cpu(rep.info.charging_enabled); + buffer->full_bat = be32_to_cpu(rep.info.full_bat); + mutex_unlock(&htc_batt_info.lock); + + return 0; +} + +/* -------------------------------------------------------------------------- */ +static int htc_power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + charger_type_t charger; + + mutex_lock(&htc_batt_info.lock); + charger = htc_batt_info.rep.charging_source; + mutex_unlock(&htc_batt_info.lock); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if (psy->type == POWER_SUPPLY_TYPE_MAINS) + val->intval = (charger == CHARGER_AC ? 1 : 0); + else if (psy->type == POWER_SUPPLY_TYPE_USB) + val->intval = (charger == CHARGER_USB ? 1 : 0); + else + val->intval = 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int htc_battery_get_charging_status(void) +{ + u32 level; + charger_type_t charger; + int ret; + + mutex_lock(&htc_batt_info.lock); + charger = htc_batt_info.rep.charging_source; + + switch (charger) { + case CHARGER_BATTERY: + ret = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case CHARGER_USB: + case CHARGER_AC: + level = htc_batt_info.rep.level; + if (level == 100) + ret = POWER_SUPPLY_STATUS_FULL; + else + ret = POWER_SUPPLY_STATUS_CHARGING; + break; + default: + ret = POWER_SUPPLY_STATUS_UNKNOWN; + } + mutex_unlock(&htc_batt_info.lock); + return ret; +} + +static int htc_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = htc_battery_get_charging_status(); + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = htc_batt_info.present; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_CAPACITY: + mutex_lock(&htc_batt_info.lock); + val->intval = htc_batt_info.rep.level; + mutex_unlock(&htc_batt_info.lock); + break; + default: + return -EINVAL; + } + + return 0; +} + +#define HTC_BATTERY_ATTR(_name) \ +{ \ + .attr = { .name = #_name, .mode = S_IRUGO, .owner = THIS_MODULE }, \ + .show = htc_battery_show_property, \ + .store = NULL, \ +} + +static struct device_attribute htc_battery_attrs[] = { + HTC_BATTERY_ATTR(batt_id), + HTC_BATTERY_ATTR(batt_vol), + HTC_BATTERY_ATTR(batt_temp), + HTC_BATTERY_ATTR(batt_current), + HTC_BATTERY_ATTR(charging_source), + HTC_BATTERY_ATTR(charging_enabled), + HTC_BATTERY_ATTR(full_bat), +}; + +enum { + BATT_ID = 0, + BATT_VOL, + BATT_TEMP, + BATT_CURRENT, + CHARGING_SOURCE, + CHARGING_ENABLED, + FULL_BAT, +}; + +static int htc_rpc_set_delta(unsigned delta) +{ + struct set_batt_delta_req { + struct rpc_request_hdr hdr; + uint32_t data; + } req; + + req.data = cpu_to_be32(delta); + return msm_rpc_call(endpoint, HTC_PROCEDURE_SET_BATT_DELTA, + &req, sizeof(req), 5 * HZ); +} + + +static ssize_t htc_battery_set_delta(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + unsigned long delta = 0; + + delta = simple_strtoul(buf, NULL, 10); + + if (delta > 100) + return -EINVAL; + + mutex_lock(&htc_batt_info.rpc_lock); + rc = htc_rpc_set_delta(delta); + mutex_unlock(&htc_batt_info.rpc_lock); + if (rc < 0) + return rc; + return count; +} + +static struct device_attribute htc_set_delta_attrs[] = { + __ATTR(delta, S_IWUSR | S_IWGRP, NULL, htc_battery_set_delta), +}; + +static int htc_battery_create_attrs(struct device * dev) +{ + int i, j, rc; + + for (i = 0; i < ARRAY_SIZE(htc_battery_attrs); i++) { + rc = device_create_file(dev, &htc_battery_attrs[i]); + if (rc) + goto htc_attrs_failed; + } + + for (j = 0; j < ARRAY_SIZE(htc_set_delta_attrs); j++) { + rc = device_create_file(dev, &htc_set_delta_attrs[j]); + if (rc) + goto htc_delta_attrs_failed; + } + + goto succeed; + +htc_attrs_failed: + while (i--) + device_remove_file(dev, &htc_battery_attrs[i]); +htc_delta_attrs_failed: + while (j--) + device_remove_file(dev, &htc_set_delta_attrs[i]); +succeed: + return rc; +} + +static ssize_t htc_battery_show_property(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int i = 0; + const ptrdiff_t off = attr - htc_battery_attrs; + + /* rpc lock is used to prevent two threads from calling + * into the get info rpc at the same time + */ + + mutex_lock(&htc_batt_info.rpc_lock); + /* check cache time to decide if we need to update */ + if (htc_batt_info.update_time && + time_before(jiffies, htc_batt_info.update_time + + msecs_to_jiffies(cache_time))) + goto dont_need_update; + + if (htc_get_batt_info(&htc_batt_info.rep) < 0) { + printk(KERN_ERR "%s: rpc failed!!!\n", __FUNCTION__); + } else { + htc_batt_info.update_time = jiffies; + } +dont_need_update: + mutex_unlock(&htc_batt_info.rpc_lock); + + mutex_lock(&htc_batt_info.lock); + switch (off) { + case BATT_ID: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.batt_id); + break; + case BATT_VOL: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.batt_vol); + break; + case BATT_TEMP: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.batt_temp); + break; + case BATT_CURRENT: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.batt_current); + break; + case CHARGING_SOURCE: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.charging_source); + break; + case CHARGING_ENABLED: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.charging_enabled); + break; + case FULL_BAT: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.full_bat); + break; + default: + i = -EINVAL; + } + mutex_unlock(&htc_batt_info.lock); + + return i; +} + +static int htc_battery_probe(struct platform_device *pdev) +{ + int i, rc; + + /* init battery gpio */ + if ((rc = init_batt_gpio()) < 0) { + printk(KERN_ERR "%s: init battery gpio failed!\n", __FUNCTION__); + return rc; + } + + /* init structure data member */ + htc_batt_info.update_time = jiffies; + /* A9 will shutdown the phone if battery is pluged out, so this value is always 1. + htc_batt_info.present = gpio_get_value(GPIO_TROUT_MBAT_IN); + */ + htc_batt_info.present = 1; + + /* init rpc */ + endpoint = msm_rpc_connect(APP_BATT_PROG, APP_BATT_VER, 0); + if (IS_ERR(endpoint)) { + printk(KERN_ERR "%s: init rpc failed! rc = %ld\n", + __FUNCTION__, PTR_ERR(endpoint)); + return rc; + } + + /* init power supplier framework */ + for (i = 0; i < ARRAY_SIZE(htc_power_supplies); i++) { + rc = power_supply_register(&pdev->dev, &htc_power_supplies[i]); + if (rc) + printk(KERN_ERR "Failed to register power supply (%d)\n", rc); + } + + /* create htc detail attributes */ + htc_battery_create_attrs(htc_power_supplies[CHARGER_BATTERY].dev); + + /* After battery driver gets initialized, send rpc request to inquiry + * the battery status in case of we lost some info + */ + htc_battery_initial = 1; + + mutex_lock(&htc_batt_info.rpc_lock); + htc_batt_info.rep.charging_source = CHARGER_BATTERY; + if (htc_get_batt_info(&htc_batt_info.rep) < 0) + printk(KERN_ERR "%s: get info failed\n", __FUNCTION__); + + if (htc_rpc_set_delta(1) < 0) + printk(KERN_ERR "%s: set delta failed\n", __FUNCTION__); + htc_batt_info.update_time = jiffies; + mutex_unlock(&htc_batt_info.rpc_lock); + + return 0; +} + +static struct platform_driver htc_battery_driver = { + .probe = htc_battery_probe, + .driver = { + .name = APP_BATT_PDEV_NAME, + .owner = THIS_MODULE, + }, +}; + +/* batt_mtoa server definitions */ +#define BATT_MTOA_PROG 0x30100000 +#define BATT_MTOA_VERS 0 +#define RPC_BATT_MTOA_NULL 0 +#define RPC_BATT_MTOA_SET_CHARGING_PROC 1 +#define RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC 2 +#define RPC_BATT_MTOA_LEVEL_UPDATE_PROC 3 + +struct rpc_batt_mtoa_set_charging_args { + int enable; +}; + +struct rpc_batt_mtoa_cable_status_update_args { + int status; +}; + +struct rpc_dem_battery_update_args { + uint32_t level; +}; + +static int handle_battery_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + switch (req->procedure) { + case RPC_BATT_MTOA_NULL: + return 0; + + case RPC_BATT_MTOA_SET_CHARGING_PROC: { + struct rpc_batt_mtoa_set_charging_args *args; + args = (struct rpc_batt_mtoa_set_charging_args *)(req + 1); + args->enable = be32_to_cpu(args->enable); + BATT("set_charging: enable=%d",args->enable); + htc_battery_set_charging(args->enable); + return 0; + } + case RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC: { + struct rpc_batt_mtoa_cable_status_update_args *args; + args = (struct rpc_batt_mtoa_cable_status_update_args *)(req + 1); + args->status = be32_to_cpu(args->status); + BATT("cable_status_update: status=%d",args->status); + htc_cable_status_update(args->status); + return 0; + } + case RPC_BATT_MTOA_LEVEL_UPDATE_PROC: { + struct rpc_dem_battery_update_args *args; + args = (struct rpc_dem_battery_update_args *)(req + 1); + args->level = be32_to_cpu(args->level); + BATT("dem_battery_update: level=%d",args->level); + htc_battery_status_update(args->level); + return 0; + } + default: + printk(KERN_ERR "%s: program 0x%08x:%d: unknown procedure %d\n", + __FUNCTION__, req->prog, req->vers, req->procedure); + return -ENODEV; + } +} + +static struct msm_rpc_server battery_server = { + .prog = BATT_MTOA_PROG, + .vers = BATT_MTOA_VERS, + .rpc_call = handle_battery_call, +}; + +static int __init htc_battery_init(void) +{ + wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present"); + mutex_init(&htc_batt_info.lock); + mutex_init(&htc_batt_info.rpc_lock); + msm_rpc_create_server(&battery_server); + platform_driver_register(&htc_battery_driver); + return 0; +} + +module_init(htc_battery_init); +MODULE_DESCRIPTION("HTC Battery Driver"); +MODULE_LICENSE("GPL"); + diff --git a/arch/arm/mach-msm/include/mach/htc_battery.h b/arch/arm/mach-msm/include/mach/htc_battery.h new file mode 100644 index 0000000000000..9b1b4d0ea73bc --- /dev/null +++ b/arch/arm/mach-msm/include/mach/htc_battery.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2007 HTC Incorporated + * Author: Jay Tu (jay_tu@htc.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. + */ +#ifndef _HTC_BATTERY_H_ +#define _HTC_BATTERY_H_ +#include +#include + +#define BATT_EVENT_SUSPEND 0x01 + +#define CHECK_CHG 0X64 +#define SET_ICL500 0X65 +#define SET_ICL100 0X66 +#define CHECK_INT2 0X67 + +/* information about the system we're running on */ +extern unsigned int system_rev; + +enum batt_ctl_t { + DISABLE = 0, + ENABLE_SLOW_CHG, + ENABLE_FAST_CHG +}; + +/* This order is the same as htc_power_supplies[] + * And it's also the same as htc_cable_status_update() + */ +enum charger_type_t { + CHARGER_UNKNOWN = -1, + CHARGER_BATTERY = 0, + CHARGER_USB, + CHARGER_AC +}; + +enum { + GUAGE_NONE, + GUAGE_MODEM, + GUAGE_DS2784, + GUAGE_DS2746, +}; + +enum { + LINEAR_CHARGER, + SWITCH_CHARGER, +}; + +struct battery_info_reply { + u32 batt_id; /* Battery ID from ADC */ + u32 batt_vol; /* Battery voltage from ADC */ + s32 batt_temp; /* Battery Temperature (C) from formula and ADC */ + s32 batt_current; /* Battery current from ADC */ + u32 level; /* formula */ + u32 charging_source; /* 0: no cable, 1:usb, 2:AC */ + u32 charging_enabled; /* 0: Disable, 1: Enable */ + u32 full_bat; /* Full capacity of battery (mAh) */ + u32 full_level; /* Full Level */ + u32 over_vchg; /* 0:normal, 1:over voltage charger */ + s32 eval_current; /* System loading current from ADC */ +}; +struct htc_battery_platform_data { + int (*func_show_batt_attr)(struct device_attribute *attr, + char *buf); + int gpio_mbat_in; + int gpio_usb_id; + int gpio_mchg_en_n; + int gpio_iset; + int guage_driver; + int m2a_cable_detect; + int charger; +}; + +#if CONFIG_HTC_BATTCHG +extern int register_notifier_cable_status(struct notifier_block *nb); +extern int unregister_notifier_cable_status(struct notifier_block *nb); +#else +static int register_notifier_cable_status(struct notifier_block *nb) { return 0; } +static int unregister_notifier_cable_status(struct notifier_block *nb) { return 0; } +#endif + +#ifdef CONFIG_BATTERY_DS2784 +extern int battery_charging_ctrl(enum batt_ctl_t ctl); +#endif +extern int get_cable_status(void); +#ifdef CONFIG_HTC_BATTCHG +extern int batt_register_client(struct notifier_block *nb); +extern int batt_unregister_client(struct notifier_block *nb); +extern int batt_notifier_call_chain(unsigned long val, void *v); +#else +static int batt_register_client(struct notifier_block *nb) +{ + return 0; +} + +static int batt_unregister_client(struct notifier_block *nb) +{ + return 0; +} + +static int batt_notifier_call_chain(unsigned long val, void *v) +{ + return 0; +} +#endif + +extern unsigned int batt_get_status(enum power_supply_property psp); +#endif diff --git a/include/linux/smb329.h b/include/linux/smb329.h new file mode 100644 index 0000000000000..8ce3440e0bc0e --- /dev/null +++ b/include/linux/smb329.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2007 HTC Incorporated + * + * 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 _SMB329_H_ +#define _SMB329_H_ +#include +#include + +#ifdef CONFIG_SMB329 +extern int set_charger_ctrl(u32 ctl); +#else +static int set_charger_ctrl(u32 ctl) {return 0 ; } +#endif +#endif From 250832305d568ddc46c7cb8a36c43ecd8c62758b Mon Sep 17 00:00:00 2001 From: toastcfh Date: Wed, 20 Oct 2010 17:41:45 -0400 Subject: [PATCH 1024/2556] msm: spi: SPI driver for QSD Replace QCT SPI driver with HTC version and update clock names. This should eventually be replaced with the QCT version. Change-Id: Ic1b3c05cb68643c927fc7fb057fc02d7e88d84a9 --- arch/arm/mach-msm/clock.h | 6 +- arch/arm/mach-msm/devices-qsd8x50.c | 88 +- drivers/spi/spi.c | 30 +- drivers/spi/spi_qsd.c | 1660 +++------------------------ include/linux/spi/spi.h | 22 +- 5 files changed, 295 insertions(+), 1511 deletions(-) diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h index 10d9458aa8ceb..b06b9aa7382c8 100644 --- a/arch/arm/mach-msm/clock.h +++ b/arch/arm/mach-msm/clock.h @@ -34,6 +34,8 @@ #define CLKFLAG_MIN 0x00000400 #define CLKFLAG_MAX 0x00000800 #define CLKFLAG_SHARED 0x00001000 +#define CLKFLAG_ARCH_QSD8X50 (0x00020000) +#define CLKFLAG_ARCH_ALL (0xffff0000) struct clk_ops { int (*enable)(unsigned id); @@ -78,10 +80,10 @@ struct clk_handle { #define CLOCK_DBG_NAME(x) #endif -#define CLOCK(clk_name, clk_id, clk_dev, clk_flags) { \ +#define CLOCK(clk_name, clk_id, clk_dev, clk_flags, clk_arch) { \ .name = clk_name, \ .id = clk_id, \ - .flags = clk_flags, \ + .flags = (clk_flags) | ((clk_arch) & CLKFLAG_ARCH_ALL), \ .dev = clk_dev, \ CLOCK_DBG_NAME(#clk_id) \ } diff --git a/arch/arm/mach-msm/devices-qsd8x50.c b/arch/arm/mach-msm/devices-qsd8x50.c index a38f05108adfa..078de83d7fd0d 100644 --- a/arch/arm/mach-msm/devices-qsd8x50.c +++ b/arch/arm/mach-msm/devices-qsd8x50.c @@ -545,38 +545,94 @@ struct platform_device msm_device_touchscreen = { .resource = resources_tssc, }; +#if defined(CONFIG_ARCH_QSD8X50) static struct resource resources_spi[] = { { - .start = MSM_SPI_PHYS, - .end = MSM_SPI_PHYS + MSM_SPI_SIZE - 1, - .flags = IORESOURCE_MEM, + .name = "spi_base", + .start = MSM_SPI_PHYS, + .end = MSM_SPI_PHYS + MSM_SPI_SIZE - 1, + .flags = IORESOURCE_MEM, }, { - .start = INT_SPI_INPUT, - .end = INT_SPI_INPUT, - .name = "irq_in", - .flags = IORESOURCE_IRQ, + .name = "spi_irq_in", + .start = INT_SPI_INPUT, + .end = INT_SPI_INPUT, + .flags = IORESOURCE_IRQ, }, { - .start = INT_SPI_OUTPUT, - .end = INT_SPI_OUTPUT, - .name = "irq_out", - .flags = IORESOURCE_IRQ, + .name = "spi_irq_out", + .start = INT_SPI_OUTPUT, + .end = INT_SPI_OUTPUT, + .flags = IORESOURCE_IRQ, }, { - .start = INT_SPI_ERROR, - .end = INT_SPI_ERROR, - .name = "irq_err", - .flags = IORESOURCE_IRQ, + .name = "spi_irq_err", + .start = INT_SPI_ERROR, + .end = INT_SPI_ERROR, + .flags = IORESOURCE_IRQ, + }, +#if defined(CONFIG_SPI_QSD) + { + .name = "spi_clk", + .start = 17, + .end = 1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spi_mosi", + .start = 18, + .end = 1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spi_miso", + .start = 19, + .end = 1, + .flags = IORESOURCE_IRQ, }, + { + .name = "spi_cs0", + .start = 20, + .end = 1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spi_pwr", + .start = 21, + .end = 0, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spi_irq_cs0", + .start = 22, + .end = 0, + .flags = IORESOURCE_IRQ, + }, +#endif }; struct platform_device msm_device_spi = { +#if defined(CONFIG_SPI_QSD) + .name = "spi_qsd", +#else .name = "msm_spi", +#endif .id = 0, .num_resources = ARRAY_SIZE(resources_spi), .resource = resources_spi, }; +#endif + +#define CLK_ALL(name, id, dev, flags) \ + CLOCK(name, id, dev, flags, CLKFLAG_ARCH_ALL) +#define CLK_7X00A(name, id, dev, flags) \ + CLOCK(name, id, dev, flags, CLKFLAG_ARCH_MSM7X00A) +#define CLK_8X50(name, id, dev, flags) \ + CLOCK(name, id, dev, flags, CLKFLAG_ARCH_QSD8X50) + +#define OFF CLKFLAG_AUTO_OFF +#define MINMAX (CLKFLAG_USE_MIN_TO_SET | CLKFLAG_USE_MAX_TO_SET) +#define USE_MIN (CLKFLAG_USE_MIN_TO_SET | CLKFLAG_SHARED) static struct resource resources_otg[] = { { @@ -700,7 +756,9 @@ struct clk msm_clocks_8x50[] = { CLK_PCOM("usb_hs3_clk", USB_HS3_CLK, NULL, OFF), CLK_PCOM("usb_hs3_pclk", USB_HS3_P_CLK, NULL, OFF), CLK_PCOM("usb_phy_clk", USB_PHY_CLK, NULL, 0), + }; + unsigned msm_num_clocks_8x50 = ARRAY_SIZE(msm_clocks_8x50); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 34bb17f030197..cfc1e33be29b4 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -25,6 +25,7 @@ #include #include #include + #include #include #include @@ -39,7 +40,7 @@ static void spidev_release(struct device *dev) spi->master->cleanup(spi); spi_master_put(spi->master); - kfree(spi); + kfree(dev); } static ssize_t @@ -339,7 +340,6 @@ int spi_add_device(struct spi_device *spi) { static DEFINE_MUTEX(spi_add_lock); struct device *dev = spi->master->dev.parent; - struct device *d; int status; /* Chipselects are numbered 0..max; validate. */ @@ -361,11 +361,10 @@ int spi_add_device(struct spi_device *spi) */ mutex_lock(&spi_add_lock); - d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev)); - if (d != NULL) { + if (bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev)) + != NULL) { dev_err(dev, "chipselect %d already in use\n", spi->chip_select); - put_device(d); status = -EBUSY; goto done; } @@ -1101,6 +1100,27 @@ int spi_write_then_read(struct spi_device *spi, } EXPORT_SYMBOL_GPL(spi_write_then_read); +static DEFINE_MUTEX(spi_lock); +int +spi_read_write_lock(struct spi_device *spidev, struct spi_msg *msg, char *buf, int size, int func) +{ + int i = 0, err = 0; + mutex_lock(&spi_lock); + if(func) { + if(!msg) return -EINVAL; + + for(i = 0; i < msg->len + 1; i++) { + err = spi_write(spidev, &msg->buffer[size * i], size); + } + } else { + if(!buf) return -EINVAL; + + err = spi_read(spidev, buf, size); + } + mutex_unlock(&spi_lock); + return err; +} + /*-------------------------------------------------------------------------*/ static int __init spi_init(void) diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c index de77086cef476..adce009b8ae78 100644 --- a/drivers/spi/spi_qsd.c +++ b/drivers/spi/spi_qsd.c @@ -1,63 +1,11 @@ -/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Code Aurora Forum nor - * the names of its contributors may be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * Alternatively, provided that this notice is retained in full, this software - * may be relicensed by the recipient under the terms of the GNU General Public - * License version 2 ("GPL") and only version 2, in which case the provisions of - * the GPL apply INSTEAD OF those given above. If the recipient relicenses the - * software under the GPL, then the identification text in the MODULE_LICENSE - * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a - * recipient changes the license terms to the GPL, subsequent recipients shall - * not relicense under alternate licensing terms, including the BSD or dual - * BSD/GPL terms. In addition, the following license statement immediately - * below and between the words START and END shall also then apply when this - * software is relicensed under the GPL: - * - * START - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License version 2 and only version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * END - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ -/* - * SPI driver for Qualcomm QSD platforms +/* linux/driver/spi/spi_qsd.c + * + * Copyright (C) 2009 Solomon Chiu * + * This is a temporary solution to substitute Qualcomm's SPI. + * Should be replaced by formal SPI driver in the future. */ + #include #include #include @@ -72,1515 +20,255 @@ #include #include #include -#include -#include -#include -#include -#include -#include - -#define SPI_CONFIG 0x0000 -#define SPI_IO_CONTROL 0x0004 -#define SPI_IO_MODES 0x0008 -#define SPI_SW_RESET 0x000C -#define SPI_TIME_OUT 0x0010 -#define SPI_TIME_OUT_CURRENT 0x0014 -#define SPI_MX_OUTPUT_COUNT 0x0018 -#define SPI_MX_OUTPUT_CNT_CURRENT 0x001C -#define SPI_MX_INPUT_COUNT 0x0020 -#define SPI_MX_INPUT_CNT_CURRENT 0x0024 -#define SPI_MX_READ_COUNT 0x0028 -#define SPI_MX_READ_CNT_CURRENT 0x002C -#define SPI_OPERATIONAL 0x0030 -#define SPI_ERROR_FLAGS 0x0034 -#define SPI_ERROR_FLAGS_EN 0x0038 -#define SPI_DEASSERT_WAIT 0x003C -#define SPI_OUTPUT_DEBUG 0x0040 -#define SPI_INPUT_DEBUG 0x0044 -#define SPI_FIFO_WORD_CNT 0x0048 -#define SPI_TEST_CTRL 0x004C -#define SPI_OUTPUT_FIFO 0x0100 -#define SPI_INPUT_FIFO 0x0200 - -/* SPI_CONFIG fields */ -#define SPI_CFG_INPUT_FIRST 0x00000200 -#define SPI_NO_INPUT 0x00000080 -#define SPI_NO_OUTPUT 0x00000040 -#define SPI_CFG_LOOPBACK 0x00000100 -#define SPI_CFG_N 0x0000001F - -/* SPI_IO_CONTROL fields */ -#define SPI_IO_C_CLK_IDLE_HIGH 0x00000400 -#define SPI_IO_C_MX_CS_MODE 0x00000100 -#define SPI_IO_C_CS_N_POLARITY 0x000000F0 -#define SPI_IO_C_CS_N_POLARITY_0 0x00000010 -#define SPI_IO_C_CS_SELECT 0x0000000C -#define SPI_IO_C_TRISTATE_CS 0x00000002 -#define SPI_IO_C_NO_TRI_STATE 0x00000001 - -/* SPI_IO_MODES fields */ -#define SPI_IO_M_OUTPUT_BIT_SHIFT_EN 0x00004000 -#define SPI_IO_M_PACK_EN 0x00002000 -#define SPI_IO_M_UNPACK_EN 0x00001000 -#define SPI_IO_M_INPUT_MODE 0x00000C00 -#define SPI_IO_M_OUTPUT_MODE 0x00000300 -#define SPI_IO_M_INPUT_FIFO_SIZE 0x000000C0 -#define SPI_IO_M_INPUT_BLOCK_SIZE 0x00000030 -#define SPI_IO_M_OUTPUT_FIFO_SIZE 0x0000000C -#define SPI_IO_M_OUTPUT_BLOCK_SIZE 0x00000003 - -/* SPI_OPERATIONAL fields */ -#define SPI_OP_MAX_INPUT_DONE_FLAG 0x00000800 -#define SPI_OP_MAX_OUTPUT_DONE_FLAG 0x00000400 -#define SPI_OP_INPUT_SERVICE_FLAG 0x00000200 -#define SPI_OP_OUTPUT_SERVICE_FLAG 0x00000100 -#define SPI_OP_INPUT_FIFO_FULL 0x00000080 -#define SPI_OP_OUTPUT_FIFO_FULL 0x00000040 -#define SPI_OP_IP_FIFO_NOT_EMPTY 0x00000020 -#define SPI_OP_OP_FIFO_NOT_EMPTY 0x00000010 -#define SPI_OP_STATE_VALID 0x00000004 -#define SPI_OP_STATE 0x00000003 -#define SPI_OP_STATE_RESET 0x00000000 -#define SPI_OP_STATE_RUN 0x00000001 -#define SPI_OP_STATE_PAUSE 0x00000003 - -/* SPI_ERROR_FLAGS fields */ -#define SPI_ERR_TIME_OUT_ERR 0x00000040 -#define SPI_ERR_OUTPUT_OVER_RUN_ERR 0x00000020 -#define SPI_ERR_INPUT_UNDER_RUN_ERR 0x00000010 -#define SPI_ERR_OUTPUT_UNDER_RUN_ERR 0x00000008 -#define SPI_ERR_INPUT_OVER_RUN_ERR 0x00000004 -#define SPI_ERR_CLK_OVER_RUN_ERR 0x00000002 -#define SPI_ERR_CLK_UNDER_RUN_ERR 0x00000001 -#define SPI_ERR_MASK 0x0000007F - -/* We don't allow transactions larger than 4K-64 */ -#define SPI_MAX_TRANSFERS 0x000FC0 -#define SPI_MAX_LEN (SPI_MAX_TRANSFERS * dd->bytes_per_word) -#define SPI_MAX_TIMEOUT 0x00010000 -#define SPI_MIN_TRANS_TIME 50 +#include +#include -#define SPI_NUM_CHIPSELECTS 4 -#define SPI_QSD_NAME "spi_qsd" +#define SPI_CONFIG (0x00000000) +#define SPI_IO_CONTROL (0x00000004) +#define SPI_OPERATIONAL (0x00000030) +#define SPI_ERROR_FLAGS_EN (0x00000038) +#define SPI_ERROR_FLAGS (0x00000034) +#define SPI_OUTPUT_FIFO (0x00000100) -/* Data Mover burst size */ -#define DM_BURST_SIZE 16 -/* Data Mover commands should be aligned to 64 bit(8 bytes) */ -#define DM_BYTE_ALIGN 8 +#ifdef CONFIG_ARCH_MSM7X30 -enum msm_spi_mode { - SPI_FIFO_MODE = 0x0, /* 00 */ - SPI_BLOCK_MODE = 0x1, /* 01 */ - SPI_DMOV_MODE = 0x2, /* 10 */ - SPI_MODE_NONE = 0xFF, /* invalid value */ -}; - -/* Structures for Data Mover */ -struct spi_dmov_cmd { - dmov_box box; /* data aligned to max(dm_burst_size, block_size) - (<= fifo_size) */ - dmov_s single_pad; /* data unaligned to max(dm_burst_size, block_size) - padded to fit */ - dma_addr_t cmd_ptr; -}; - -MODULE_LICENSE("GPL v2"); -MODULE_VERSION("0.2"); -MODULE_ALIAS("platform:spi_qsd"); - -#ifdef CONFIG_DEBUG_FS -/* Used to create debugfs entries */ -static const struct { - const char *name; - mode_t mode; - int offset; -} debugfs_spi_regs[] = { - {"config", S_IRUGO | S_IWUSR, SPI_CONFIG}, - {"io_control", S_IRUGO | S_IWUSR, SPI_IO_CONTROL}, - {"io_modes", S_IRUGO | S_IWUSR, SPI_IO_MODES}, - {"sw_reset", S_IWUSR, SPI_SW_RESET}, - {"time_out", S_IRUGO | S_IWUSR, SPI_TIME_OUT}, - {"time_out_current", S_IRUGO, SPI_TIME_OUT_CURRENT}, - {"mx_output_count", S_IRUGO | S_IWUSR, SPI_MX_OUTPUT_COUNT}, - {"mx_output_cnt_current", S_IRUGO, SPI_MX_OUTPUT_CNT_CURRENT}, - {"mx_input_count", S_IRUGO | S_IWUSR, SPI_MX_INPUT_COUNT}, - {"mx_input_cnt_current", S_IRUGO, SPI_MX_INPUT_CNT_CURRENT}, - {"mx_read_count", S_IRUGO | S_IWUSR, SPI_MX_READ_COUNT}, - {"mx_read_cnt_current", S_IRUGO, SPI_MX_READ_CNT_CURRENT}, - {"operational", S_IRUGO | S_IWUSR, SPI_OPERATIONAL}, - {"error_flags", S_IRUGO | S_IWUSR, SPI_ERROR_FLAGS}, - {"error_flags_en", S_IRUGO | S_IWUSR, SPI_ERROR_FLAGS_EN}, - {"deassert_wait", S_IRUGO | S_IWUSR, SPI_DEASSERT_WAIT}, - {"output_debug", S_IRUGO, SPI_OUTPUT_DEBUG}, - {"input_debug", S_IRUGO, SPI_INPUT_DEBUG}, - {"fifo_word_cnt", S_IRUGO, SPI_FIFO_WORD_CNT}, - {"test_ctrl", S_IRUGO | S_IWUSR, SPI_TEST_CTRL}, - {"output_fifo", S_IWUSR, SPI_OUTPUT_FIFO}, - {"input_fifo" , S_IRUSR, SPI_INPUT_FIFO}, -}; -#endif - -struct msm_spi { - u8 *read_buf; - const u8 *write_buf; - void __iomem *base; - struct device *dev; - spinlock_t queue_lock; - struct list_head queue; - struct workqueue_struct *workqueue; - struct work_struct work_data; - struct spi_message *cur_msg; - struct spi_transfer *cur_transfer; - struct completion transfer_complete; - struct clk *clk; - struct clk *pclk; - int max_clock_speed; - unsigned long mem_phys_addr; - size_t mem_size; - int input_fifo_size; - u32 rx_bytes_remaining; - u32 tx_bytes_remaining; - u32 clock_speed; - u32 irq_in; - u32 irq_out; - u32 irq_err; - int bytes_per_word; - bool suspended; - bool transfer_in_progress; - /* DMA data */ - enum msm_spi_mode mode; - bool use_dma; - int tx_dma_chan; - int tx_dma_crci; - int rx_dma_chan; - int rx_dma_crci; - /* Data Mover Commands */ - struct spi_dmov_cmd *tx_dmov_cmd; - struct spi_dmov_cmd *rx_dmov_cmd; - /* Physical address of the tx dmov box command */ - dma_addr_t tx_dmov_cmd_dma; - dma_addr_t rx_dmov_cmd_dma; - struct msm_dmov_cmd tx_hdr; - struct msm_dmov_cmd rx_hdr; - int block_size; - int burst_size; - atomic_t rx_irq_called; - /* Used to pad messages unaligned to block size */ - u8 *tx_padding; - dma_addr_t tx_padding_dma; - u8 *rx_padding; - dma_addr_t rx_padding_dma; - u32 unaligned_len; - /* DMA statistics */ - u32 stat_dmov_err; - u32 stat_rx; - u32 stat_tx; -#ifdef CONFIG_DEBUG_FS - struct dentry *dent_spi; - struct dentry *debugfs_spi_regs[ARRAY_SIZE(debugfs_spi_regs)]; -#endif -}; +struct spi_device *spidev; -static void msm_spi_clock_set(struct msm_spi *dd, int speed) +extern int samsung_oled_panel_init(struct msm_lcdc_panel_ops *ops); +int qspi_send_16bit(unsigned char id, unsigned data) { - int rc; - - rc = clk_set_rate(dd->clk, speed); - if (!rc) - dd->clock_speed = speed; -} - -/* Assumption: input_fifo=output_fifo */ -static void __init msm_spi_calculate_fifo_size(struct msm_spi *dd) -{ - u32 spi_iom; - int block; - int mult; - int words; - - spi_iom = readl(dd->base + SPI_IO_MODES); - block = (spi_iom & SPI_IO_M_INPUT_BLOCK_SIZE) >> 4; - mult = (spi_iom & SPI_IO_M_INPUT_FIFO_SIZE) >> 6; - switch (block) { - case 0: - words = 1; - break; - case 1: - words = 4; - break; - case 2: - words = 8; - break; - default: - goto fifo_size_err; - } - switch (mult) { - case 0: - dd->input_fifo_size = words * 2; - break; - case 1: - dd->input_fifo_size = words * 4; - break; - case 2: - dd->input_fifo_size = words * 8; - break; - default: - goto fifo_size_err; - } - - /* we can't use dma with 8 bytes of fifo since dm burst size is 16 */ - if (dd->input_fifo_size*sizeof(u32) < DM_BURST_SIZE) - dd->use_dma = 0; - if (dd->use_dma) { - dd->block_size = words*sizeof(u32); /* in bytes */ - dd->burst_size = max(dd->block_size, DM_BURST_SIZE); - } - - return; - -fifo_size_err: - printk(KERN_WARNING "%s: invalid FIFO size, SPI_IO_MODES=0x%x\n", - __func__, spi_iom); - return; -} - -static void msm_spi_read_word_from_fifo(struct msm_spi *dd) -{ - u32 data_in; - int i; - int shift; - - data_in = readl(dd->base + SPI_INPUT_FIFO); - if (dd->read_buf) { - for (i = 0; (i < dd->bytes_per_word) && - dd->rx_bytes_remaining; i++) { - /* The data format depends on bytes_per_word: - 4 bytes: 0x12345678 - 3 bytes: 0x00123456 - 2 bytes: 0x00001234 - 1 byte : 0x00000012 - */ - shift = 8 * (dd->bytes_per_word - i - 1); - *dd->read_buf++ = (data_in & (0xFF << shift)) >> shift; - dd->rx_bytes_remaining--; - } - } else { - if (dd->rx_bytes_remaining >= dd->bytes_per_word) - dd->rx_bytes_remaining -= dd->bytes_per_word; - else - dd->rx_bytes_remaining = 0; - } -} - -static void msm_spi_setup_dm_transfer(struct msm_spi *dd) -{ - dmov_box *box; - int bytes_to_send, num_rows, bytes_sent; - u32 num_transfers, timeout; - - atomic_set(&dd->rx_irq_called, 0); - bytes_sent = dd->cur_transfer->len - dd->tx_bytes_remaining; - /* We'll send in chunks of SPI_MAX_LEN if larger */ - bytes_to_send = dd->tx_bytes_remaining / SPI_MAX_LEN ? - SPI_MAX_LEN : dd->tx_bytes_remaining; - num_transfers = DIV_ROUND_UP(bytes_to_send, dd->bytes_per_word); - dd->unaligned_len = bytes_to_send % dd->burst_size; - /* We multiply by 8bitsinbyte and multiply by 1.5 for safety */ - timeout = bytes_to_send * 12 > SPI_MAX_TIMEOUT ? - 0 : roundup(bytes_to_send * 12, SPI_MIN_TRANS_TIME); - num_rows = bytes_to_send / dd->burst_size; - - dd->mode = SPI_DMOV_MODE; - - if (num_rows) { - /* src in 16 MSB, dst in 16 LSB */ - box = &dd->tx_dmov_cmd->box; - box->src_row_addr = dd->cur_transfer->tx_dma + bytes_sent; - box->src_dst_len = (dd->burst_size << 16) | dd->burst_size; - box->num_rows = (num_rows << 16) | num_rows; - box->row_offset = (dd->burst_size << 16) | 0; - - box = &dd->rx_dmov_cmd->box; - box->dst_row_addr = dd->cur_transfer->rx_dma + bytes_sent; - box->src_dst_len = (dd->burst_size << 16) | dd->burst_size; - box->num_rows = (num_rows << 16) | num_rows; - box->row_offset = (0 << 16) | dd->burst_size; - - dd->tx_dmov_cmd->cmd_ptr = CMD_PTR_LP | - DMOV_CMD_ADDR(dd->tx_dmov_cmd_dma + - offsetof(struct spi_dmov_cmd, box)); - dd->rx_dmov_cmd->cmd_ptr = CMD_PTR_LP | - DMOV_CMD_ADDR(dd->rx_dmov_cmd_dma + - offsetof(struct spi_dmov_cmd, box)); - } else { - dd->tx_dmov_cmd->cmd_ptr = CMD_PTR_LP | - DMOV_CMD_ADDR(dd->tx_dmov_cmd_dma + - offsetof(struct spi_dmov_cmd, single_pad)); - dd->rx_dmov_cmd->cmd_ptr = CMD_PTR_LP | - DMOV_CMD_ADDR(dd->rx_dmov_cmd_dma + - offsetof(struct spi_dmov_cmd, single_pad)); - } - - if (!dd->unaligned_len) { - dd->tx_dmov_cmd->box.cmd |= CMD_LC; - dd->rx_dmov_cmd->box.cmd |= CMD_LC; - } else { - dmov_s *tx_cmd = &(dd->tx_dmov_cmd->single_pad); - dmov_s *rx_cmd = &(dd->rx_dmov_cmd->single_pad); - u32 offset = dd->cur_transfer->len - dd->unaligned_len; - - dd->tx_dmov_cmd->box.cmd &= ~CMD_LC; - dd->rx_dmov_cmd->box.cmd &= ~CMD_LC; - - memset(dd->tx_padding, 0, dd->burst_size); - memset(dd->rx_padding, 0, dd->burst_size); - if (dd->write_buf) - memcpy(dd->tx_padding, dd->write_buf + offset, - dd->unaligned_len); - - tx_cmd->src = dd->tx_padding_dma; - rx_cmd->dst = dd->rx_padding_dma; - tx_cmd->len = rx_cmd->len = dd->burst_size; - } - /* This also takes care of the padding dummy buf - Since this is set to the correct length, the - dummy bytes won't be actually sent */ - if (dd->write_buf) - writel(num_transfers, dd->base + SPI_MX_OUTPUT_COUNT); - if (dd->read_buf) - writel(num_transfers, dd->base + SPI_MX_INPUT_COUNT); - /* Write timeout */ - writel(timeout, dd->base + SPI_TIME_OUT); -} - -static void msm_spi_enqueue_dm_commands(struct msm_spi *dd) -{ - if (dd->write_buf) - msm_dmov_enqueue_cmd(dd->tx_dma_chan, &dd->tx_hdr); - if (dd->read_buf) - msm_dmov_enqueue_cmd(dd->rx_dma_chan, &dd->rx_hdr); -} - -/* SPI core can send maximum of 4K transfers, because there is HW problem - with infinite mode. - Therefore, we are sending several chunks of 3K or less (depending on how - much is left). - Upon completion we send the next chunk, or complete the transfer if - everything is finished. -*/ -static int msm_spi_dm_send_next(struct msm_spi *dd) -{ - /* By now we should have sent all the bytes in FIFO mode, - * However to make things right, we'll check anyway. - */ - if (dd->mode != SPI_DMOV_MODE) - return 0; - - /* We need to send more chunks, if we sent max last time */ - if (dd->tx_bytes_remaining > SPI_MAX_LEN) { - dd->tx_bytes_remaining -= SPI_MAX_LEN; - writel((readl(dd->base + SPI_OPERATIONAL) - & ~SPI_OP_STATE) | SPI_OP_STATE_PAUSE, - dd->base + SPI_OPERATIONAL); - msm_spi_setup_dm_transfer(dd); - msm_spi_enqueue_dm_commands(dd); - - writel((readl(dd->base + SPI_OPERATIONAL) - & ~SPI_OP_STATE) | SPI_OP_STATE_RUN, - dd->base + SPI_OPERATIONAL); - return 1; - } + unsigned char buffer[4]; + int tmp; + tmp = (id<<13 | data)<<16; + buffer[0] = tmp >> 24; + buffer[1] = (tmp & 0x00FF0000) >> 16; + buffer[2] = (tmp & 0x0000FF00) >> 8; + buffer[3] = tmp & 0x000000FF; + spi_write(spidev,buffer,4); return 0; } - -static inline void msm_spi_ack_transfer(struct msm_spi *dd) -{ - writel(SPI_OP_MAX_INPUT_DONE_FLAG | SPI_OP_MAX_OUTPUT_DONE_FLAG, - dd->base + SPI_OPERATIONAL); - writel(0, dd->base + SPI_TIME_OUT); -} - -static irqreturn_t msm_spi_input_irq(int irq, void *dev_id) -{ - struct msm_spi *dd = dev_id; - - dd->stat_rx++; - - if (dd->mode == SPI_MODE_NONE) - return IRQ_HANDLED; - - if (dd->mode == SPI_DMOV_MODE) { - u32 op = readl(dd->base + SPI_OPERATIONAL); - if ((!dd->read_buf || op & SPI_OP_MAX_INPUT_DONE_FLAG) && - (!dd->write_buf || op & SPI_OP_MAX_OUTPUT_DONE_FLAG)) { - msm_spi_ack_transfer(dd); - if (dd->unaligned_len == 0) { - if (atomic_inc_return(&dd->rx_irq_called) == 1) - return IRQ_HANDLED; - } - complete(&dd->transfer_complete); - return IRQ_HANDLED; - } - return IRQ_NONE; - } - - /* fifo mode */ - while ((readl(dd->base + SPI_OPERATIONAL) & SPI_OP_IP_FIFO_NOT_EMPTY) && - (dd->rx_bytes_remaining > 0)) { - msm_spi_read_word_from_fifo(dd); - } - if (dd->rx_bytes_remaining == 0) - complete(&dd->transfer_complete); - - return IRQ_HANDLED; -} - -static void msm_spi_write_word_to_fifo(struct msm_spi *dd) -{ - u32 word; - u8 byte; - int i; - - word = 0; - if (dd->write_buf) { - for (i = 0; (i < dd->bytes_per_word) && - dd->tx_bytes_remaining; i++) { - dd->tx_bytes_remaining--; - byte = *dd->write_buf++; - word |= (byte << (BITS_PER_BYTE * (3 - i))); - } - } else - if (dd->tx_bytes_remaining > dd->bytes_per_word) - dd->tx_bytes_remaining -= dd->bytes_per_word; - else - dd->tx_bytes_remaining = 0; - writel(word, dd->base + SPI_OUTPUT_FIFO); -} - -static irqreturn_t msm_spi_output_irq(int irq, void *dev_id) -{ - struct msm_spi *dd = dev_id; - int count = 0; - - dd->stat_tx++; - - if (dd->mode == SPI_MODE_NONE) - return IRQ_HANDLED; - - if (dd->mode == SPI_DMOV_MODE) { - /* TX_ONLY transaction is handled here - This is the only place we send complete at tx and not rx */ - if (dd->read_buf == NULL && readl(dd->base + SPI_OPERATIONAL) & - SPI_OP_MAX_OUTPUT_DONE_FLAG) { - msm_spi_ack_transfer(dd); - complete(&dd->transfer_complete); - return IRQ_HANDLED; +int qspi_send_9bit(struct spi_msg *msg) +{ + int tmp = 0; + spidev->bits_per_word = 9; + tmp = (0x0 <<8 | msg->cmd)<<23; + msg->buffer[0] = tmp >> 24; + msg->buffer[1] = (tmp & 0x00FF0000) >> 16; + + if(msg->len != 0) { + int i = 0, j; + for(j = 2; i < msg->len; i++, j+=2){ + tmp &= 0x00000000; + tmp = (0x1<<8 | *(msg->data+i))<<23; + msg->buffer[j] = tmp >> 24; + msg->buffer[j+1] = (tmp & 0x00FF0000) >> 16; } - return IRQ_NONE; } - /* Output FIFO is empty. Transmit any outstanding write data. */ - /* There could be one word in input FIFO, so don't send more */ - /* than input_fifo_size - 1 more words. */ - while ((dd->tx_bytes_remaining > 0) && - (count < dd->input_fifo_size - 1) && - !(readl(dd->base + SPI_OPERATIONAL) & SPI_OP_OUTPUT_FIFO_FULL)) { - msm_spi_write_word_to_fifo(dd); - count++; - } - - return IRQ_HANDLED; -} - -static irqreturn_t msm_spi_error_irq(int irq, void *dev_id) -{ - struct spi_master *master = dev_id; - struct msm_spi *dd = spi_master_get_devdata(master); - u32 spi_err; + spi_read_write_lock(spidev, msg, NULL, 2, 1); - spi_err = readl(dd->base + SPI_ERROR_FLAGS); - if (spi_err & SPI_ERR_TIME_OUT_ERR) { - dev_warn(master->dev.parent, "SPI timeout error\n"); - msm_dmov_flush(dd->tx_dma_chan); - msm_dmov_flush(dd->rx_dma_chan); - } - if (spi_err & SPI_ERR_OUTPUT_OVER_RUN_ERR) - dev_warn(master->dev.parent, "SPI output overrun error\n"); - if (spi_err & SPI_ERR_INPUT_UNDER_RUN_ERR) - dev_warn(master->dev.parent, "SPI input underrun error\n"); - if (spi_err & SPI_ERR_OUTPUT_UNDER_RUN_ERR) - dev_warn(master->dev.parent, "SPI output underrun error\n"); - if (spi_err & SPI_ERR_INPUT_OVER_RUN_ERR) - dev_warn(master->dev.parent, "SPI input overrun error\n"); - if (spi_err & SPI_ERR_CLK_OVER_RUN_ERR) - dev_warn(master->dev.parent, "SPI clock overrun error\n"); - if (spi_err & SPI_ERR_CLK_UNDER_RUN_ERR) - dev_warn(master->dev.parent, "SPI clock underrun error\n"); - writel(SPI_ERR_MASK, dd->base + SPI_ERROR_FLAGS); - return IRQ_HANDLED; + return 0; } -static void msm_spi_unmap_dma_buffers(struct msm_spi *dd) -{ - struct device *dev; - dev = &dd->cur_msg->spi->dev; - if (!dd->cur_msg->is_dma_mapped) { - if (dd->cur_transfer->rx_buf) - dma_unmap_single(dev, dd->cur_transfer->rx_dma, - dd->cur_transfer->len, - DMA_FROM_DEVICE); - if (dd->cur_transfer->tx_buf) - dma_unmap_single(dev, dd->cur_transfer->tx_dma, - dd->cur_transfer->len, - DMA_TO_DEVICE); - } - /* If we padded the transfer, we copy it from the padding buf */ - if (dd->unaligned_len && dd->read_buf) { - u32 offset = dd->cur_transfer->len - dd->unaligned_len; - memcpy(dd->read_buf + offset, dd->rx_padding, - dd->unaligned_len); - } -} - -/** - * msm_use_dm - decides whether to use data mover for this - * transfer - * @dd: device - * @tr: transfer - * - * Start using DM if: - * 1. Transfer is longer than 3*block size. - * 2. Buffers should be aligned to cache line. - * 3. If Transfer is bigger than max_output_count, we accept only aligned to - * block size transfers. - */ -static inline int msm_use_dm(struct msm_spi *dd, struct spi_transfer *tr) +int qspi_send(unsigned char id, unsigned data) { - u32 cache_line = dma_get_cache_alignment(); + unsigned char buffer[2]; + int tmp; + tmp = (0x7000 | id<<9 | data)<<16; - if (!dd->use_dma) - return 0; + spidev->bits_per_word = 16; - if (tr->len < 3*dd->block_size) - return 0; + buffer[0] = tmp >> 24; + buffer[1] = (tmp & 0x00FF0000) >> 16; - if (tr->tx_buf) { - if (!IS_ALIGNED((size_t)tr->tx_buf, cache_line)) - return 0; - } - if (tr->rx_buf) { - if (!IS_ALIGNED((size_t)tr->rx_buf, cache_line)) - return 0; - } - return 1; + spi_write(spidev,buffer,2); + return 0; } -static void msm_spi_process_transfer(struct msm_spi *dd) -{ - u8 bpw; - u32 spi_config; - u32 spi_ioc; - u32 spi_iom; - u32 spi_ioc_orig; - u32 max_speed; - u32 chip_select; - u32 read_count; - u32 timeout; - - if (!dd->cur_transfer->len) - return; - dd->tx_bytes_remaining = dd->cur_transfer->len; - dd->rx_bytes_remaining = dd->cur_transfer->len; - dd->read_buf = dd->cur_transfer->rx_buf; - dd->write_buf = dd->cur_transfer->tx_buf; - init_completion(&dd->transfer_complete); - if (dd->cur_transfer->bits_per_word) - bpw = dd->cur_transfer->bits_per_word; - else - if (dd->cur_msg->spi->bits_per_word) - bpw = dd->cur_msg->spi->bits_per_word; - else - bpw = 8; - dd->bytes_per_word = (bpw + 7) / 8; - - if (dd->cur_transfer->speed_hz) - max_speed = dd->cur_transfer->speed_hz; - else - max_speed = dd->cur_msg->spi->max_speed_hz; - if (!dd->clock_speed || max_speed < dd->clock_speed) - msm_spi_clock_set(dd, max_speed); - - read_count = DIV_ROUND_UP(dd->cur_transfer->len, dd->bytes_per_word); - if (!msm_use_dm(dd, dd->cur_transfer)) { - dd->mode = SPI_FIFO_MODE; - /* read_count cannot exceed fifo_size, and only one READ COUNT - interrupt is generated per transaction, so for transactions - larger than fifo size READ COUNT must be disabled. - For those transactions we usually move to Data Mover mode. - */ - if (read_count <= dd->input_fifo_size) - writel(read_count, dd->base + SPI_MX_READ_COUNT); - else - writel(0, dd->base + SPI_MX_READ_COUNT); - } else - dd->mode = SPI_DMOV_MODE; - /* Write mode - fifo or data mover*/ - spi_iom = readl(dd->base + SPI_IO_MODES); - spi_iom &= ~(SPI_IO_M_INPUT_MODE | SPI_IO_M_OUTPUT_MODE); - spi_iom = (spi_iom | (dd->mode << 10)); - spi_iom = (spi_iom | (dd->mode << 8)); - /* Turn on packing for data mover */ - if (dd->mode == SPI_DMOV_MODE) - spi_iom |= SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN; - else - spi_iom &= ~(SPI_IO_M_PACK_EN | SPI_IO_M_UNPACK_EN); - writel(spi_iom, dd->base + SPI_IO_MODES); - - spi_config = readl(dd->base + SPI_CONFIG); - if ((bpw - 1) != (spi_config & SPI_CFG_N)) - spi_config = (spi_config & ~SPI_CFG_N) | (bpw - 1); - if (dd->cur_msg->spi->mode & SPI_CPHA) - spi_config &= ~SPI_CFG_INPUT_FIRST; - else - spi_config |= SPI_CFG_INPUT_FIRST; - if (dd->cur_msg->spi->mode & SPI_LOOP) - spi_config |= SPI_CFG_LOOPBACK; - else - spi_config &= ~SPI_CFG_LOOPBACK; - spi_config &= ~(SPI_NO_INPUT|SPI_NO_OUTPUT); - if (dd->mode == SPI_DMOV_MODE) { - if (dd->read_buf == NULL) - spi_config |= SPI_NO_INPUT; - if (dd->write_buf == NULL) - spi_config |= SPI_NO_OUTPUT; - } - writel(spi_config, dd->base + SPI_CONFIG); - - spi_ioc = readl(dd->base + SPI_IO_CONTROL); - spi_ioc_orig = spi_ioc; - if (dd->cur_msg->spi->mode & SPI_CPOL) - spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH; - else - spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH; - chip_select = dd->cur_msg->spi->chip_select << 2; - if ((spi_ioc & SPI_IO_C_CS_SELECT) != chip_select) - spi_ioc = (spi_ioc & ~SPI_IO_C_CS_SELECT) | chip_select; - if (!dd->cur_transfer->cs_change) - spi_ioc |= SPI_IO_C_MX_CS_MODE; - if (spi_ioc != spi_ioc_orig) - writel(spi_ioc, dd->base + SPI_IO_CONTROL); - - if (dd->mode == SPI_DMOV_MODE) { - msm_spi_setup_dm_transfer(dd); - msm_spi_enqueue_dm_commands(dd); - } - /* The output fifo interrupt handler will handle all writes after - the first. Restricting this to one write avoids contention - issues and race conditions between this thread and the int handler - */ - else if (dd->mode == SPI_FIFO_MODE) - msm_spi_write_word_to_fifo(dd); - - /* Only enter the RUN state after the first word is written into - the output FIFO. Otherwise, the output FIFO EMPTY interrupt - might fire before the first word is written resulting in a - possible race condition. - */ - writel((readl(dd->base + SPI_OPERATIONAL) - & ~SPI_OP_STATE) | SPI_OP_STATE_RUN, - dd->base + SPI_OPERATIONAL); - - timeout = 100 * msecs_to_jiffies( - DIV_ROUND_UP(dd->cur_transfer->len * 8, - max_speed / MSEC_PER_SEC)); - do { - if (!wait_for_completion_timeout(&dd->transfer_complete, - timeout)) { - dev_err(dd->dev, "%s: SPI transaction " - "timeout\n", __func__); - dd->cur_msg->status = -EIO; - if (dd->mode == SPI_DMOV_MODE) { - writel(0, dd->base + SPI_TIME_OUT); - msm_dmov_flush(dd->tx_dma_chan); - msm_dmov_flush(dd->rx_dma_chan); - } - break; - } - } while (msm_spi_dm_send_next(dd)); - - if (dd->mode == SPI_DMOV_MODE) - msm_spi_unmap_dma_buffers(dd); - dd->mode = SPI_MODE_NONE; - - writel(spi_ioc & ~SPI_IO_C_MX_CS_MODE, dd->base + SPI_IO_CONTROL); - writel((readl(dd->base + SPI_OPERATIONAL) - & ~SPI_OP_STATE) | SPI_OP_STATE_RESET, - dd->base + SPI_OPERATIONAL); -} - -/* workqueue - pull messages from queue & process */ -static void msm_spi_workq(struct work_struct *work) +static int msm_spi_probe(struct spi_device *spi) { - struct msm_spi *dd = - container_of(work, struct msm_spi, work_data); - unsigned long flags; - u32 spi_op; - bool status_error = 0; - - spi_op = readl(dd->base + SPI_OPERATIONAL); - if (spi_op & SPI_OP_STATE_VALID) { - spi_op &= ~SPI_OP_STATE; - spi_op |= SPI_OP_STATE_RUN; - } else { - dev_err(dd->dev, "%s: SPI operational state not valid\n", - __func__); - status_error = 1; - } - - dd->transfer_in_progress = 1; - spin_lock_irqsave(&dd->queue_lock, flags); - while (!list_empty(&dd->queue)) { - dd->cur_msg = list_entry(dd->queue.next, - struct spi_message, queue); - list_del_init(&dd->cur_msg->queue); - spin_unlock_irqrestore(&dd->queue_lock, flags); - if (status_error) - dd->cur_msg->status = -EIO; - else { - list_for_each_entry(dd->cur_transfer, - &dd->cur_msg->transfers, - transfer_list) { - msm_spi_process_transfer(dd); - if (dd->cur_msg->status == -EINPROGRESS) - dd->cur_msg->status = 0; - } - } - if (dd->cur_msg->complete) - dd->cur_msg->complete(dd->cur_msg->context); - spin_lock_irqsave(&dd->queue_lock, flags); - } - spin_unlock_irqrestore(&dd->queue_lock, flags); - dd->transfer_in_progress = 0; + printk(" %s \n", __func__); + spidev = spi; + return 0 ; } -static int msm_spi_transfer(struct spi_device *spi, struct spi_message *msg) +static int msm_spi_remove(struct platform_device *pdev) { - struct msm_spi *dd; - unsigned long flags; - struct spi_transfer *tr; - - dd = spi_master_get_devdata(spi->master); - if (dd->suspended) - return -EBUSY; - - if (list_empty(&msg->transfers) || !msg->complete) - return -EINVAL; - - list_for_each_entry(tr, &msg->transfers, transfer_list) { - void *tx_buf = (void *)tr->tx_buf; - void *rx_buf = tr->rx_buf; - unsigned len = tr->len; - - /* Check message parameters */ - if (tr->speed_hz > dd->max_clock_speed || (tr->bits_per_word && - (tr->bits_per_word < 4 || tr->bits_per_word > 32)) || - (tx_buf == NULL && rx_buf == NULL)) { - dev_err(&spi->dev, "Invalid transfer: %d Hz, %d bpw" - "tx=%p, rx=%p\n", - tr->speed_hz, tr->bits_per_word, - tx_buf, rx_buf); - goto error; - } - - if (!msm_use_dm(dd, tr) || msg->is_dma_mapped) - continue; - - /* Do DMA mapping "early" for better error reporting */ - if (tx_buf != NULL) { - tr->tx_dma = dma_map_single(&spi->dev, tx_buf, len, - DMA_TO_DEVICE); - if (dma_mapping_error(NULL, tr->tx_dma)) { - dev_err(&spi->dev, "dma %cX %d bytes error\n", - 'T', len); - goto error; - } - } - - if (rx_buf != NULL) { - tr->rx_dma = dma_map_single(&spi->dev, rx_buf, len, - DMA_FROM_DEVICE); - if (dma_mapping_error(NULL, tr->rx_dma)) { - dev_err(&spi->dev, "dma %cX %d bytes error\n", - 'R', len); - if (tx_buf != NULL) - dma_unmap_single(NULL, tr->tx_dma, - len, DMA_TO_DEVICE); - goto error; - } - } - } - - spin_lock_irqsave(&dd->queue_lock, flags); - list_add_tail(&msg->queue, &dd->queue); - spin_unlock_irqrestore(&dd->queue_lock, flags); - queue_work(dd->workqueue, &dd->work_data); + spidev = NULL; return 0; - -error: - list_for_each_entry_continue_reverse(tr, &msg->transfers, transfer_list) - { - if (msm_use_dm(dd, tr) && !msg->is_dma_mapped) { - if (tr->rx_buf != NULL) - dma_unmap_single(&spi->dev, tr->rx_dma, tr->len, - DMA_TO_DEVICE); - if (tr->tx_buf != NULL) - dma_unmap_single(&spi->dev, tr->tx_dma, tr->len, - DMA_FROM_DEVICE); - } - } - return -EINVAL; } -static int msm_spi_setup(struct spi_device *spi) -{ - struct msm_spi *dd; - int rc = 0; - u32 spi_ioc; - u32 spi_config; - u32 mask; - - if (spi->bits_per_word < 4 || spi->bits_per_word > 32) { - dev_err(&spi->dev, "%s: invalid bits_per_word %d\n", - __func__, spi->bits_per_word); - rc = -EINVAL; - } - if (spi->chip_select > SPI_NUM_CHIPSELECTS-1) { - dev_err(&spi->dev, "%s, chip select %d exceeds max value %d\n", - __func__, spi->chip_select, SPI_NUM_CHIPSELECTS - 1); - rc = -EINVAL; - } - - if (rc) - goto err_setup_exit; - - dd = spi_master_get_devdata(spi->master); - spi_ioc = readl(dd->base + SPI_IO_CONTROL); - mask = SPI_IO_C_CS_N_POLARITY_0 << spi->chip_select; - if (spi->mode & SPI_CS_HIGH) - spi_ioc |= mask; - else - spi_ioc &= ~mask; - if (spi->mode & SPI_CPOL) - spi_ioc |= SPI_IO_C_CLK_IDLE_HIGH; - else - spi_ioc &= ~SPI_IO_C_CLK_IDLE_HIGH; - writel(spi_ioc, dd->base + SPI_IO_CONTROL); - - spi_config = readl(dd->base + SPI_CONFIG); - if (spi->mode & SPI_LOOP) - spi_config |= SPI_CFG_LOOPBACK; - else - spi_config &= ~SPI_CFG_LOOPBACK; - if (spi->mode & SPI_CPHA) - spi_config &= ~SPI_CFG_INPUT_FIRST; - else - spi_config |= SPI_CFG_INPUT_FIRST; - writel(spi_config, dd->base + SPI_CONFIG); -err_setup_exit: - return rc; -} +static struct spi_driver spi_qsd = { + .driver = { + .name = "spi_qsd", + .owner = THIS_MODULE, + }, + .probe = msm_spi_probe, + .remove = __devexit_p(msm_spi_remove), +}; -#ifdef CONFIG_DEBUG_FS -static int debugfs_iomem_x32_set(void *data, u64 val) -{ - iowrite32(val, data); - wmb(); - return 0; -} -static int debugfs_iomem_x32_get(void *data, u64 *val) +static int __init spi_qsd_init(void) { - *val = ioread32(data); - return 0; + int rc; + rc = spi_register_driver(&spi_qsd); + return rc; } +module_init(spi_qsd_init); -DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, debugfs_iomem_x32_get, - debugfs_iomem_x32_set, "0x%08llx\n"); - -static void spi_debugfs_init(struct msm_spi *dd) +static void __exit spi_qsd_exit(void) { - dd->dent_spi = debugfs_create_dir(dev_name(dd->dev), NULL); - if (dd->dent_spi) { - int i; - for (i = 0; i < ARRAY_SIZE(debugfs_spi_regs); i++) { - dd->debugfs_spi_regs[i] = - debugfs_create_file( - debugfs_spi_regs[i].name, - debugfs_spi_regs[i].mode, - dd->dent_spi, - dd->base + debugfs_spi_regs[i].offset, - &fops_iomem_x32); - } - } + spi_unregister_driver(&spi_qsd); } +module_exit(spi_qsd_exit); -static void spi_debugfs_exit(struct msm_spi *dd) -{ - if (dd->dent_spi) { - int i; - debugfs_remove_recursive(dd->dent_spi); - dd->dent_spi = NULL; - for (i = 0; i < ARRAY_SIZE(debugfs_spi_regs); i++) - dd->debugfs_spi_regs[i] = NULL; - } -} #else -static void spi_debugfs_init(struct msm_spi *dd) {} -static void spi_debugfs_exit(struct msm_spi *dd) {} -#endif -/* ===Device attributes begin=== */ -static ssize_t show_stats(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct spi_master *master = dev_get_drvdata(dev); - struct msm_spi *dd = spi_master_get_devdata(master); - - return snprintf(buf, PAGE_SIZE, - "Device %s\n" - "use_dma ? %s\n" - "DMA configuration:\n" - "tx_ch=%d, rx_ch=%d, tx_crci= %d, rx_crci=%d\n" - "--statistics--\n" - "Rx isrs = %d\n" - "Tx isrs = %d\n" - "DMA error = %d\n" - "--debug--\n" - "NA yet\n", - dev_name(dev), - dd->use_dma ? "yes" : "no", - dd->tx_dma_chan, - dd->rx_dma_chan, - dd->tx_dma_crci, - dd->rx_dma_crci, - dd->stat_rx, - dd->stat_tx, - dd->stat_dmov_err - ); -} +void __iomem *spi_base; +struct clk *spi_clk ; -/* Reset statistics on write */ -static ssize_t set_stats(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +int qspi_send_16bit(unsigned char id, unsigned data) { - struct msm_spi *dd = dev_get_drvdata(dev); - dd->stat_rx = 0; - dd->stat_tx = 0; - dd->stat_dmov_err = 0; - return count; -} - -static DEVICE_ATTR(stats, S_IRUGO | S_IWUSR, show_stats, set_stats); - -static struct attribute *dev_attrs[] = { - &dev_attr_stats.attr, - NULL, -}; + unsigned err ; -static struct attribute_group dev_attr_grp = { - .attrs = dev_attrs, -}; -/* ===Device attributes end=== */ + /* bit-5: OUTPUT_FIFO_NOT_EMPTY */ + clk_enable(spi_clk); + while( readl(spi_base+SPI_OPERATIONAL) & (1<<5) ) + { + if( (err=readl(spi_base+SPI_ERROR_FLAGS)) ) + { + printk("\rERROR: SPI_ERROR_FLAGS=%d\r", err); + return -1; + } + } -/** - * spi_dmov_tx_complete_func - DataMover tx completion callback - * - * Executed in IRQ context (Data Mover's IRQ) DataMover's - * spinlock @msm_dmov_lock held. - */ -static void spi_dmov_tx_complete_func(struct msm_dmov_cmd *cmd, - unsigned int result, - struct msm_dmov_errdata *err) -{ - struct msm_spi *dd; + writel( (id<<13 | data)<<16, spi_base+SPI_OUTPUT_FIFO );/*AUO*/ + udelay(1000); + clk_disable(spi_clk); - if (!(result & DMOV_RSLT_VALID)) { - pr_err("Invalid DMOV result: rc=0x%08x, cmd = %p", result, cmd); - return; - } - /* restore original context */ - dd = container_of(cmd, struct msm_spi, tx_hdr); - if (result & DMOV_RSLT_DONE) - dd->stat_tx++; - else { - /* Error or flush */ - if (result & DMOV_RSLT_ERROR) { - dev_err(dd->dev, "DMA error (0x%08x)\n", result); - dd->stat_dmov_err++; - } - if (result & DMOV_RSLT_FLUSH) { - /* - * Flushing normally happens in process of - * removing, when we are waiting for outstanding - * DMA commands to be flushed. - */ - dev_info(dd->dev, - "DMA channel flushed (0x%08x)\n", result); - } - if (err) - dev_err(dd->dev, - "Flush data(%08x %08x %08x %08x %08x %08x)\n", - err->flush[0], err->flush[1], err->flush[2], - err->flush[3], err->flush[4], err->flush[5]); - dd->cur_msg->status = -EIO; - writel(0, dd->base + SPI_TIME_OUT); - complete(&dd->transfer_complete); - } + return 0; } -/** - * spi_dmov_rx_complete_func - DataMover rx completion callback - * - * Executed in IRQ context (Data Mover's IRQ) - * DataMover's spinlock @msm_dmov_lock held. - */ -static void spi_dmov_rx_complete_func(struct msm_dmov_cmd *cmd, - unsigned int result, - struct msm_dmov_errdata *err) +int qspi_send_9bit(unsigned char id, unsigned data) { - struct msm_spi *dd; + unsigned err ; - if (!(result & DMOV_RSLT_VALID)) { - pr_err("Invalid DMOV result(rc = 0x%08x, cmd = %p)", - result, cmd); - return; - } - /* restore original context */ - dd = container_of(cmd, struct msm_spi, rx_hdr); - if (result & DMOV_RSLT_DONE) { - dd->stat_rx++; - if (atomic_inc_return(&dd->rx_irq_called) == 1) - return; - complete(&dd->transfer_complete); - } else { - /** Error or flush */ - if (result & DMOV_RSLT_ERROR) { - dev_err(dd->dev, "DMA error(0x%08x)\n", result); - dd->stat_dmov_err++; - } - if (result & DMOV_RSLT_FLUSH) { - dev_info(dd->dev, - "DMA channel flushed(0x%08x)\n", result); - } - if (err) - dev_err(dd->dev, - "Flush data(%08x %08x %08x %08x %08x %08x)\n", - err->flush[0], err->flush[1], err->flush[2], - err->flush[3], err->flush[4], err->flush[5]); - dd->cur_msg->status = -EIO; - writel(0, dd->base + SPI_TIME_OUT); - complete(&dd->transfer_complete); - } -} + /* bit-5: OUTPUT_FIFO_NOT_EMPTY */ + clk_enable(spi_clk); + while( readl(spi_base+SPI_OPERATIONAL) & (1<<5) ) + { + if( (err=readl(spi_base+SPI_ERROR_FLAGS)) ) + { + printk("\rERROR: SPI_ERROR_FLAGS=%d\r", err); + return -1; + } + } -static inline u32 get_chunk_size(struct msm_spi *dd) -{ - u32 cache_line = dma_get_cache_alignment(); + writel( ((id<<8) | data)<<23, spi_base+SPI_OUTPUT_FIFO);/*sharp*/ - return (roundup(sizeof(struct spi_dmov_cmd), DM_BYTE_ALIGN) + - roundup(dd->burst_size, cache_line))*2; + udelay(1000); + clk_disable(spi_clk); + return 0; } -static void msm_spi_teardown_dma(struct msm_spi *dd) -{ - int limit = 0; - - if (!dd->use_dma) - return; - - while (dd->mode == SPI_DMOV_MODE && limit++ < 50) { - msm_dmov_flush(dd->tx_dma_chan); - msm_dmov_flush(dd->rx_dma_chan); - msleep(10); - } - dma_free_coherent(NULL, get_chunk_size(dd), dd->tx_dmov_cmd, - dd->tx_dmov_cmd_dma); - dd->tx_dmov_cmd = dd->rx_dmov_cmd = NULL; - dd->tx_padding = dd->rx_padding = NULL; -} - -static __init int msm_spi_init_dma(struct msm_spi *dd) +int qspi_send(unsigned char id, unsigned data) { - dmov_box *box; - u32 cache_line = dma_get_cache_alignment(); - - /* Allocate all as one chunk, since all is smaller than page size */ - - /* We send NULL device, since it requires coherent_dma_mask id - device definition, we're okay with using system pool */ - dd->tx_dmov_cmd = dma_alloc_coherent(NULL, get_chunk_size(dd), - &dd->tx_dmov_cmd_dma, GFP_KERNEL); - if (dd->tx_dmov_cmd == NULL) - return -ENOMEM; + unsigned err ; - /* DMA addresses should be 64 bit aligned aligned */ - dd->rx_dmov_cmd = (struct spi_dmov_cmd *) - ALIGN((size_t)&dd->tx_dmov_cmd[1], DM_BYTE_ALIGN); - dd->rx_dmov_cmd_dma = ALIGN(dd->tx_dmov_cmd_dma + - sizeof(struct spi_dmov_cmd), DM_BYTE_ALIGN); - - /* Buffers should be aligned to cache line */ - dd->tx_padding = (u8 *)ALIGN((size_t)&dd->rx_dmov_cmd[1], cache_line); - dd->tx_padding_dma = ALIGN(dd->rx_dmov_cmd_dma + - sizeof(struct spi_dmov_cmd), cache_line); - dd->rx_padding = (u8 *)ALIGN((size_t)(dd->tx_padding + dd->burst_size), - cache_line); - dd->rx_padding_dma = ALIGN(dd->tx_padding_dma + dd->burst_size, - cache_line); - - /* Setup DM commands */ - box = &(dd->rx_dmov_cmd->box); - box->cmd = CMD_MODE_BOX | CMD_SRC_CRCI(dd->rx_dma_crci); - box->src_row_addr = (uint32_t)dd->mem_phys_addr + SPI_INPUT_FIFO; - dd->rx_hdr.cmdptr = DMOV_CMD_PTR_LIST | - DMOV_CMD_ADDR(dd->rx_dmov_cmd_dma + - offsetof(struct spi_dmov_cmd, cmd_ptr)); - dd->rx_hdr.complete_func = spi_dmov_rx_complete_func; - - box = &(dd->tx_dmov_cmd->box); - box->cmd = CMD_MODE_BOX | CMD_DST_CRCI(dd->tx_dma_crci); - box->dst_row_addr = (uint32_t)dd->mem_phys_addr + SPI_OUTPUT_FIFO; - dd->tx_hdr.cmdptr = DMOV_CMD_PTR_LIST | - DMOV_CMD_ADDR(dd->tx_dmov_cmd_dma + - offsetof(struct spi_dmov_cmd, cmd_ptr)); - dd->tx_hdr.complete_func = spi_dmov_tx_complete_func; - - dd->tx_dmov_cmd->single_pad.cmd = CMD_MODE_SINGLE | CMD_LC | - CMD_DST_CRCI(dd->tx_dma_crci); - dd->tx_dmov_cmd->single_pad.dst = (uint32_t)dd->mem_phys_addr + - SPI_OUTPUT_FIFO; - dd->rx_dmov_cmd->single_pad.cmd = CMD_MODE_SINGLE | CMD_LC | - CMD_SRC_CRCI(dd->rx_dma_crci); - dd->rx_dmov_cmd->single_pad.src = (uint32_t)dd->mem_phys_addr + - SPI_INPUT_FIFO; - - /* Clear remaining activities on channel */ - msm_dmov_flush(dd->tx_dma_chan); - msm_dmov_flush(dd->rx_dma_chan); + /* bit-5: OUTPUT_FIFO_NOT_EMPTY */ + clk_enable(spi_clk); + while( readl(spi_base+SPI_OPERATIONAL) & (1<<5) ) + { + if( (err=readl(spi_base+SPI_ERROR_FLAGS)) ) + { + printk("\rERROR: SPI_ERROR_FLAGS=%d\r", err); + return -1; + } + } + writel( (0x7000 | id<<9 | data)<<16, spi_base+SPI_OUTPUT_FIFO ); + udelay(100); + clk_disable(spi_clk); return 0; } static int __init msm_spi_probe(struct platform_device *pdev) { - struct spi_master *master; - struct msm_spi *dd; - struct resource *resource; - int rc = 0; - struct clk *pclk; - struct msm_spi_platform_data *pdata = pdev->dev.platform_data; - - master = spi_alloc_master(&pdev->dev, sizeof(struct msm_spi)); - if (!master) { - rc = -ENOMEM; - dev_err(&pdev->dev, "master allocation failed\n"); - goto err_probe_exit; - } - - /* the spi->mode bits understood by this driver: */ - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; - master->bus_num = pdev->id; - master->num_chipselect = SPI_NUM_CHIPSELECTS; - master->setup = msm_spi_setup; - master->transfer = msm_spi_transfer; - platform_set_drvdata(pdev, master); - dd = spi_master_get_devdata(master); - - dd->irq_in = platform_get_irq_byname(pdev, "irq_in"); - dd->irq_out = platform_get_irq_byname(pdev, "irq_out"); - dd->irq_err = platform_get_irq_byname(pdev, "irq_err"); - if ((dd->irq_in < 0) || (dd->irq_out < 0) || (dd->irq_err < 0)) - goto err_probe_res; - - resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!resource) { - rc = -ENXIO; - goto err_probe_res; - } - dd->mem_phys_addr = resource->start; - dd->mem_size = (resource->end - resource->start) + 1; - - if (pdata && pdata->dma_config) { - rc = pdata->dma_config(); - if (!rc) { - resource = platform_get_resource_byname(pdev, - IORESOURCE_DMA, "spidm_channels"); - if (resource) { - dd->rx_dma_chan = resource->start; - dd->tx_dma_chan = resource->end; - - resource = platform_get_resource_byname(pdev, - IORESOURCE_DMA, "spidm_crci"); - if (!resource) { - rc = -ENXIO; - goto err_probe_res; - } - dd->rx_dma_crci = resource->start; - dd->tx_dma_crci = resource->end; - dd->use_dma = 1; - } - } - } - - if (pdata && pdata->gpio_config) { - rc = pdata->gpio_config(); - if (rc) { - dev_err(&pdev->dev, "%s: error configuring GPIOs\n", - __func__); - goto err_probe_gpio; - } - } - - spin_lock_init(&dd->queue_lock); - INIT_LIST_HEAD(&dd->queue); - INIT_WORK(&dd->work_data, msm_spi_workq); - dd->workqueue = create_singlethread_workqueue( - dev_name(master->dev.parent)); - if (!dd->workqueue) - goto err_probe_workq; - - if (!request_mem_region(dd->mem_phys_addr, dd->mem_size, - SPI_QSD_NAME)) { - rc = -ENXIO; - goto err_probe_reqmem; - } - - dd->base = ioremap(dd->mem_phys_addr, dd->mem_size); - if (!dd->base) - goto err_probe_ioremap; - - dd->dev = &pdev->dev; - dd->clk = clk_get(&pdev->dev, "spi_clk"); - if (IS_ERR(dd->clk)) { - dev_err(&pdev->dev, "%s: unable to get spi_clk\n", __func__); - rc = PTR_ERR(dd->clk); - goto err_probe_clk_get; - } - rc = clk_enable(dd->clk); - if (rc) { - dev_err(&pdev->dev, "%s: unable to enable spi_clk\n", - __func__); - goto err_probe_clk_enable; - } - pclk = clk_get(&pdev->dev, "spi_pclk"); - if (!IS_ERR(pclk)) { - dd->pclk = pclk; - rc = clk_enable(dd->pclk); - if (rc) { - dev_err(&pdev->dev, "%s: unable to enable spi_pclk\n", - __func__); - goto err_probe_pclk_enable; - } - } - if (pdata && pdata->max_clock_speed) { - msm_spi_clock_set(dd, pdata->max_clock_speed); - dd->max_clock_speed = pdata->max_clock_speed; - } - msm_spi_calculate_fifo_size(dd); - writel(0x1, dd->base + SPI_SW_RESET); - if (dd->use_dma) { - rc = msm_spi_init_dma(dd); - if (rc) - goto err_probe_dma; - } - writel(0x00000000, dd->base + SPI_OPERATIONAL); - writel(0x00000000, dd->base + SPI_CONFIG); - writel(0x00000000, dd->base + SPI_IO_MODES); - writel(SPI_IO_C_NO_TRI_STATE, dd->base + SPI_IO_CONTROL); - if (!(readl(dd->base + SPI_OPERATIONAL) & SPI_OP_STATE_VALID)) { - dev_err(&pdev->dev, "%s: SPI operational state not valid\n", - __func__); - rc = -1; - goto err_probe_state; - } - writel(SPI_OP_STATE_RUN, dd->base + SPI_OPERATIONAL); - - dd->suspended = 0; - dd->transfer_in_progress = 0; - dd->mode = SPI_MODE_NONE; - - rc = request_irq(dd->irq_in, msm_spi_input_irq, IRQF_TRIGGER_RISING, - pdev->name, dd); - if (rc) - goto err_probe_irq1; - rc = request_irq(dd->irq_out, msm_spi_output_irq, IRQF_TRIGGER_RISING, - pdev->name, dd); - if (rc) - goto err_probe_irq2; - rc = request_irq(dd->irq_err, msm_spi_error_irq, IRQF_TRIGGER_RISING, - pdev->name, master); - if (rc) - goto err_probe_irq3; - - rc = spi_register_master(master); - if (rc) - goto err_probe_reg_master; - - rc = sysfs_create_group(&(dd->dev->kobj), &dev_attr_grp); - if (rc) { - dev_err(&pdev->dev, "failed to create dev. attrs : %d\n", rc); - goto err_attrs; - } + int rc ; + struct spi_platform_data *pdata = pdev->dev.platform_data; + + spi_base=ioremap(0xA1200000, 4096); + if(!spi_base) + return -1; + + spi_clk = clk_get(&pdev->dev, "spi_clk"); + if (IS_ERR(spi_clk)) { + dev_err(&pdev->dev, "%s: unable to get spi_clk\n", __func__); + rc = PTR_ERR(spi_clk); + goto err_probe_clk_get; + } + rc = clk_enable(spi_clk); + if (rc) { + dev_err(&pdev->dev, "%s: unable to enable spi_clk\n", + __func__); + goto err_probe_clk_enable; + } + + if(pdata == NULL) + clk_set_rate(spi_clk, 4800000); + else + clk_set_rate(spi_clk, pdata->clk_rate); - spi_debugfs_init(dd); + printk(KERN_DEBUG "spi clk = 0x%ld\n", clk_get_rate(spi_clk)); + printk("spi: SPI_CONFIG=%x\n", readl(spi_base+SPI_CONFIG)); + printk("spi: SPI_IO_CONTROL=%x\n", readl(spi_base+SPI_IO_CONTROL)); + printk("spi: SPI_OPERATIONAL=%x\n", readl(spi_base+SPI_OPERATIONAL)); + printk("spi: SPI_ERROR_FLAGS_EN=%x\n", readl(spi_base+SPI_ERROR_FLAGS_EN)); + printk("spi: SPI_ERROR_FLAGS=%x\n", readl(spi_base+SPI_ERROR_FLAGS)); + printk("-%s()\n", __FUNCTION__); + clk_disable(spi_clk); - return 0; + return 0 ; -err_attrs: -err_probe_reg_master: - free_irq(dd->irq_err, master); -err_probe_irq3: - free_irq(dd->irq_out, dd); -err_probe_irq2: - free_irq(dd->irq_in, dd); -err_probe_irq1: -err_probe_state: - msm_spi_teardown_dma(dd); -err_probe_dma: - if (dd->pclk) - clk_disable(dd->pclk); -err_probe_pclk_enable: - if (dd->pclk) - clk_put(dd->pclk); - clk_disable(dd->clk); -err_probe_clk_enable: - clk_put(dd->clk); err_probe_clk_get: - iounmap(dd->base); -err_probe_ioremap: - release_mem_region(dd->mem_phys_addr, dd->mem_size); -err_probe_reqmem: - destroy_workqueue(dd->workqueue); -err_probe_workq: -err_probe_gpio: - if (pdata && pdata->gpio_release) - pdata->gpio_release(); -err_probe_res: - spi_master_put(master); -err_probe_exit: - return rc; +err_probe_clk_enable: + return -1 ; } -#ifdef CONFIG_PM -static int msm_spi_suspend(struct platform_device *pdev, pm_message_t state) +static int __devexit msm_spi_remove(struct platform_device *pdev) { - struct spi_master *master = platform_get_drvdata(pdev); - struct msm_spi *dd; - int limit = 0; - - if (!master) - goto suspend_exit; - dd = spi_master_get_devdata(master); - if (!dd) - goto suspend_exit; - dd->suspended = 1; - while ((!list_empty(&dd->queue) || dd->transfer_in_progress) && - limit < 50) { - if (dd->mode == SPI_DMOV_MODE) { - msm_dmov_flush(dd->tx_dma_chan); - msm_dmov_flush(dd->rx_dma_chan); - } - limit++; - msleep(1); - } - - disable_irq(dd->irq_in); - disable_irq(dd->irq_out); - disable_irq(dd->irq_err); - clk_disable(dd->clk); - -suspend_exit: return 0; } -static int msm_spi_resume(struct platform_device *pdev) +static int msm_spi_suspend(struct platform_device *pdev, pm_message_t state) { - struct spi_master *master = platform_get_drvdata(pdev); - struct msm_spi *dd; - int rc; - - if (!master) - goto resume_exit; - dd = spi_master_get_devdata(master); - if (!dd) - goto resume_exit; - - rc = clk_enable(dd->clk); - if (rc) { - dev_err(dd->dev, "%s: unable to enable spi_clk\n", - __func__); - goto resume_exit; - } - - enable_irq(dd->irq_in); - enable_irq(dd->irq_out); - enable_irq(dd->irq_err); - dd->suspended = 0; -resume_exit: - return 0; + printk("+%s()\n", __FUNCTION__); + clk_disable(spi_clk); + return 0 ; } -#else -#define msm_spi_suspend NULL -#define msm_spi_resume NULL -#endif /* CONFIG_PM */ -static int __devexit msm_spi_remove(struct platform_device *pdev) +static int msm_spi_resume(struct platform_device *pdev) { - struct spi_master *master = platform_get_drvdata(pdev); - struct msm_spi *dd = spi_master_get_devdata(master); - struct msm_spi_platform_data *pdata = pdev->dev.platform_data; - - spi_debugfs_exit(dd); - sysfs_remove_group(&pdev->dev.kobj, &dev_attr_grp); - - free_irq(dd->irq_in, dd); - free_irq(dd->irq_out, dd); - free_irq(dd->irq_err, master); - - msm_spi_teardown_dma(dd); - - if (pdata && pdata->gpio_release) - pdata->gpio_release(); - - iounmap(dd->base); - release_mem_region(dd->mem_phys_addr, dd->mem_size); - clk_disable(dd->clk); - clk_put(dd->clk); - if (dd->pclk) { - clk_disable(dd->pclk); - clk_put(dd->pclk); - } - destroy_workqueue(dd->workqueue); - platform_set_drvdata(pdev, 0); - spi_unregister_master(master); - spi_master_put(master); - - return 0; + printk("+%s()\n", __FUNCTION__); + clk_enable(spi_clk); + return 0 ; } static struct platform_driver msm_spi_driver = { .probe = msm_spi_probe, .driver = { - .name = "msm_spi", + .name = "spi_qsd", .owner = THIS_MODULE, }, +#if 0 .suspend = msm_spi_suspend, .resume = msm_spi_resume, +#endif .remove = __exit_p(msm_spi_remove), }; @@ -1588,10 +276,6 @@ static int __init msm_spi_init(void) { return platform_driver_register(&msm_spi_driver); } -module_init(msm_spi_init); -static void __exit msm_spi_exit(void) -{ - platform_driver_unregister(&msm_spi_driver); -} -module_exit(msm_spi_exit); +fs_initcall(msm_spi_init); +#endif diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index b4d7710bc38d2..26456a49f5a8c 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -101,6 +101,14 @@ struct spi_device { */ }; +struct spi_msg +{ + u8 cmd; + u8 *data; + int len; + unsigned char buffer[128]; +}; + static inline struct spi_device *to_spi_device(struct device *dev) { return dev ? container_of(dev, struct spi_device, dev) : NULL; @@ -622,7 +630,15 @@ spi_read(struct spi_device *spi, u8 *buf, size_t len) extern int spi_write_then_read(struct spi_device *spi, const u8 *txbuf, unsigned n_tx, u8 *rxbuf, unsigned n_rx); - +/* + * htc workaround to support multiple clients: add mutex lock to avoid SPI commands conflict. + * @func: true for spi write, false for spi read + * @msg: spi write commands struct + * @buf: spi read buffer + * @size: read/wirte length + */ +extern int +spi_read_write_lock(struct spi_device *spidev, struct spi_msg * msg, char *buf, int size, int func); /** * spi_w8r8 - SPI synchronous 8 bit write followed by 8 bit read * @spi: device with which data will be exchanged @@ -763,6 +779,10 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n) { return 0; } #endif +struct spi_platform_data { + int clk_rate; +}; + /* If you're hotplugging an adapter with devices (parport, usb, etc) * use spi_new_device() to describe each device. You can also call From 7bb09c5589817b419fdb634dc13f1bb39f4e8a61 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Tue, 2 Nov 2010 23:12:21 -0400 Subject: [PATCH 1025/2556] board-supuersonic-rfkill: update board file. fixes bt --- arch/arm/mach-msm/board-supersonic-rfkill.c | 288 ++++++++++++++++++-- 1 file changed, 260 insertions(+), 28 deletions(-) diff --git a/arch/arm/mach-msm/board-supersonic-rfkill.c b/arch/arm/mach-msm/board-supersonic-rfkill.c index 990a011060fd7..3100c2405bbc8 100644 --- a/arch/arm/mach-msm/board-supersonic-rfkill.c +++ b/arch/arm/mach-msm/board-supersonic-rfkill.c @@ -13,28 +13,271 @@ * */ -#include -#include -#include +/* Control bluetooth power for supersonic platform */ + #include +#include +#include #include -#include +#include +#include #include +#include "gpio_chip.h" +#include "proc_comm.h" #include "board-supersonic.h" +#define HTC_RFKILL_DBG + static struct rfkill *bt_rfk; static const char bt_name[] = "bcm4329"; +static int pre_state; -static int bluetooth_set_power(void *data, bool blocked) +static uint32_t supersonic_bt_init_table[] = { + /* BT_RTS */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_UART1_RTS, + 0, + GPIO_OUTPUT, + GPIO_NO_PULL, + GPIO_8MA), + /* BT_CTS */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_UART1_CTS, + 0, + GPIO_INPUT, + GPIO_PULL_UP, + GPIO_8MA), + /* BT_RX */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_UART1_RX, + 0, + GPIO_INPUT, + GPIO_PULL_UP, + GPIO_8MA), + /* BT_TX */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_UART1_TX, + 0, + GPIO_OUTPUT, + GPIO_NO_PULL, + GPIO_8MA), + + /* BT_SHUTDOWN_N */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_SHUTDOWN_N, + 0, + GPIO_OUTPUT, + GPIO_NO_PULL, + GPIO_4MA), + /* BT_RESET_N */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_RESET_N, + 0, + GPIO_OUTPUT, + GPIO_NO_PULL, + GPIO_4MA), + + /* BT_HOST_WAKE */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_HOST_WAKE, + 0, + GPIO_INPUT, + GPIO_PULL_UP, + GPIO_4MA), + /* BT_CHIP_WAKE */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_CHIP_WAKE, + 0, + GPIO_OUTPUT, + GPIO_NO_PULL, + GPIO_4MA), +}; + +static uint32_t supersonic_bt_on_table[] = { + /* BT_RTS */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_UART1_RTS, + 2, + GPIO_OUTPUT, + GPIO_PULL_UP, + GPIO_8MA), + /* BT_CTS */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_UART1_CTS, + 2, + GPIO_INPUT, + GPIO_PULL_UP, + GPIO_8MA), + /* BT_RX */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_UART1_RX, + 2, + GPIO_INPUT, + GPIO_PULL_UP, + GPIO_8MA), + /* BT_TX */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_UART1_TX, + 2, + GPIO_OUTPUT, + GPIO_PULL_UP, + GPIO_8MA), + + /* BT_HOST_WAKE */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_HOST_WAKE, + 0, + GPIO_INPUT, + GPIO_NO_PULL, + GPIO_4MA), + /* BT_CHIP_WAKE */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_CHIP_WAKE, + 0, + GPIO_OUTPUT, + GPIO_NO_PULL, + GPIO_4MA), +}; + +static uint32_t supersonic_bt_off_table[] = { + /* BT_RTS */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_UART1_RTS, + 0, + GPIO_OUTPUT, + GPIO_NO_PULL, + GPIO_8MA), + /* BT_CTS */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_UART1_CTS, + 0, + GPIO_INPUT, + GPIO_PULL_UP, + GPIO_8MA), + /* BT_RX */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_UART1_RX, + 0, + GPIO_INPUT, + GPIO_PULL_UP, + GPIO_8MA), + /* BT_TX */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_UART1_TX, + 0, + GPIO_OUTPUT, + GPIO_NO_PULL, + GPIO_8MA), + + /* BT_HOST_WAKE */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_HOST_WAKE, + 0, + GPIO_INPUT, + GPIO_PULL_UP, + GPIO_4MA), + /* BT_CHIP_WAKE */ + PCOM_GPIO_CFG(SUPERSONIC_GPIO_BT_CHIP_WAKE, + 0, + GPIO_OUTPUT, + GPIO_NO_PULL, + GPIO_4MA), +}; + +static void config_bt_table(uint32_t *table, int len) { - if (!blocked) { - gpio_direction_output(SUPERSONIC_GPIO_BT_RESET_N, 1); - gpio_direction_output(SUPERSONIC_GPIO_BT_SHUTDOWN_N, 1); - } else { - gpio_direction_output(SUPERSONIC_GPIO_BT_SHUTDOWN_N, 0); - gpio_direction_output(SUPERSONIC_GPIO_BT_RESET_N, 0); + int n; + unsigned id; + for (n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); } +} + +static void supersonic_config_bt_init(void) +{ + /* set bt initial configuration*/ + config_bt_table(supersonic_bt_init_table, + ARRAY_SIZE(supersonic_bt_init_table)); + /* BT_RESET_N */ + gpio_direction_output(SUPERSONIC_GPIO_BT_RESET_N, 0); + + mdelay(5); + + /* BT_SHUTDOWN_N */ + gpio_direction_output(SUPERSONIC_GPIO_BT_SHUTDOWN_N, 1); + + /* BT_RESET_N */ + + gpio_direction_output(SUPERSONIC_GPIO_BT_RESET_N, 1); + + mdelay(15); + + /* BT_RESET_N */ + gpio_direction_output(SUPERSONIC_GPIO_BT_RESET_N, 0); + + /* BT_SHUTDOWN_N */ + gpio_direction_output(SUPERSONIC_GPIO_BT_SHUTDOWN_N, 0); + + /* BT_RTS */ + gpio_direction_output(SUPERSONIC_GPIO_BT_UART1_RTS, 0); + + /* BT_TX */ + gpio_direction_output(SUPERSONIC_GPIO_BT_UART1_TX, 0); + + /* BT_CHIP_WAKE */ + gpio_direction_output(SUPERSONIC_GPIO_BT_CHIP_WAKE, 0); + +} + +static void supersonic_config_bt_on(void) +{ + + #ifdef HTC_RFKILL_DBG + printk(KERN_INFO "-- RK ON --\n"); + #endif + + /* set bt on configuration*/ + config_bt_table(supersonic_bt_on_table, + ARRAY_SIZE(supersonic_bt_on_table)); + mdelay(5); + /* BT_SHUTDOWN_N */ + gpio_direction_output(SUPERSONIC_GPIO_BT_SHUTDOWN_N, 1); + + /* BT_RESET_N */ + gpio_direction_output(SUPERSONIC_GPIO_BT_RESET_N, 1); + + mdelay(5); + + /* BT_CHIP_WAKE */ + gpio_direction_output(SUPERSONIC_GPIO_BT_CHIP_WAKE, 1); + +} + +static void supersonic_config_bt_off(void) +{ + #ifdef HTC_RFKILL_DBG + printk(KERN_INFO "-- RK OFF --\n"); + #endif + + /* BT_RESET_N */ + gpio_direction_output(SUPERSONIC_GPIO_BT_RESET_N, 0); + + /* BT_SHUTDOWN_N */ + gpio_direction_output(SUPERSONIC_GPIO_BT_SHUTDOWN_N, 0); + + + config_bt_table(supersonic_bt_off_table, + ARRAY_SIZE(supersonic_bt_off_table)); + mdelay(5); + + /* BT_RTS */ + gpio_direction_output(SUPERSONIC_GPIO_BT_UART1_RTS, 0); + + /* BT_TX */ + gpio_direction_output(SUPERSONIC_GPIO_BT_UART1_TX, 0); + + /* BT_CHIP_WAKE */ + gpio_direction_output(SUPERSONIC_GPIO_BT_CHIP_WAKE, 0); +} + +static int bluetooth_set_power(void *data, bool blocked) +{ + if (pre_state == blocked) { + #ifdef HTC_RFKILL_DBG + printk(KERN_INFO "-- SAME ST --\n"); + #endif + return 0; + } else + pre_state = blocked; + + if (!blocked) + supersonic_config_bt_on(); + else + supersonic_config_bt_off(); + return 0; } @@ -45,28 +288,22 @@ static struct rfkill_ops supersonic_rfkill_ops = { static int supersonic_rfkill_probe(struct platform_device *pdev) { int rc = 0; - bool default_state = true; /* off */ + bool default_state = true; /* off */ - rc = gpio_request(SUPERSONIC_GPIO_BT_RESET_N, "bt_reset"); - if (rc) - goto err_gpio_reset; - rc = gpio_request(SUPERSONIC_GPIO_BT_SHUTDOWN_N, "bt_shutdown"); - if (rc) - goto err_gpio_shutdown; + supersonic_config_bt_init(); /* bt gpio initial config */ bluetooth_set_power(NULL, default_state); bt_rfk = rfkill_alloc(bt_name, &pdev->dev, RFKILL_TYPE_BLUETOOTH, - &supersonic_rfkill_ops, NULL); + &supersonic_rfkill_ops, NULL); if (!bt_rfk) { rc = -ENOMEM; - goto err_rfkill_alloc; + goto err_rfkill_reset; } rfkill_set_states(bt_rfk, default_state, false); /* userspace cannot take exclusive control */ - rc = rfkill_register(bt_rfk); if (rc) goto err_rfkill_reg; @@ -75,11 +312,7 @@ static int supersonic_rfkill_probe(struct platform_device *pdev) err_rfkill_reg: rfkill_destroy(bt_rfk); -err_rfkill_alloc: - gpio_free(SUPERSONIC_GPIO_BT_SHUTDOWN_N); -err_gpio_shutdown: - gpio_free(SUPERSONIC_GPIO_BT_RESET_N); -err_gpio_reset: +err_rfkill_reset: return rc; } @@ -87,8 +320,6 @@ static int supersonic_rfkill_remove(struct platform_device *dev) { rfkill_unregister(bt_rfk); rfkill_destroy(bt_rfk); - gpio_free(SUPERSONIC_GPIO_BT_SHUTDOWN_N); - gpio_free(SUPERSONIC_GPIO_BT_RESET_N); return 0; } @@ -104,6 +335,7 @@ static struct platform_driver supersonic_rfkill_driver = { static int __init supersonic_rfkill_init(void) { + pre_state = -1; if (!machine_is_supersonic()) return 0; From 8f569fc7199264c1b71de4a2a76efde23750c88f Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Mon, 8 Nov 2010 17:28:29 -0500 Subject: [PATCH 1026/2556] sensors: bma150: Use use correct device name Set name based on SPI_QSD Change-Id: I560eeb97322daafc8727e12dfe304caa63a8f833 --- include/linux/bma150.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/bma150.h b/include/linux/bma150.h index fe8ef225b03f3..072faa5b18d14 100644 --- a/include/linux/bma150.h +++ b/include/linux/bma150.h @@ -7,7 +7,12 @@ #include #define BMA150_I2C_NAME "bma150" + +#ifdef CONFIG_SPI_QSD +#define BMA150_G_SENSOR_NAME "bma150_uP_spi" +#else #define BMA150_G_SENSOR_NAME "bma150" +#endif #define BMAIO 0xA1 From b7981788bce1cf12b1fef524ef45f8e0fc289ea9 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Mon, 8 Nov 2010 19:59:08 -0500 Subject: [PATCH 1027/2556] input: cm3602_lightsensor: Support HTC driver variant Support for HTC LIGHTSENSOR_MICROP using Atmega common driver with calibration via ATAG data. Change-Id: I44962fa3b3320b49bde0e949139651ec6bfdfb39 --- arch/arm/include/asm/setup.h | 25 + arch/arm/include/asm/system.h | 4 + arch/arm/kernel/setup.c | 48 +- drivers/input/misc/Makefile | 6 +- .../misc/cm3602_lightsensor_microp_htc.c | 608 ++++++++++++++++++ 5 files changed, 686 insertions(+), 5 deletions(-) create mode 100644 drivers/input/misc/cm3602_lightsensor_microp_htc.c diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h index f1e5a9bca2491..d29001b65e0b8 100644 --- a/arch/arm/include/asm/setup.h +++ b/arch/arm/include/asm/setup.h @@ -126,6 +126,28 @@ struct tag_cmdline { char cmdline[1]; /* this is the minimum size */ }; +/* Microp version */ +#define ATAG_MICROP_VERSION 0x5441000a + +struct tag_microp_version { + char ver[4]; +}; + +/* Light sensor calibration value */ +#define ATAG_ALS 0x5441001b + +struct tag_als_kadc { + __u32 kadc; +}; + +/* Proximity sensor calibration values */ +#define ATAG_PS 0x5441001c + +struct tag_ps_kparam { + __u32 kparam1; + __u32 kparam2; +}; + /* acorn RiscPC specific information */ #define ATAG_ACORN 0x41000101 @@ -153,6 +175,9 @@ struct tag { struct tag_initrd initrd; struct tag_serialnr serialnr; struct tag_revision revision; + struct tag_microp_version microp_version; + struct tag_als_kadc als_kadc; + struct tag_ps_kparam ps_kparam; struct tag_videolfb videolfb; struct tag_cmdline cmdline; diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 97f6d60297d57..8011e02440391 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -77,6 +77,10 @@ extern unsigned int system_rev; extern unsigned int system_serial_low; extern unsigned int system_serial_high; extern unsigned int mem_fclk_21285; +extern char microp_ver[4]; +extern unsigned int als_kadc; +extern unsigned int ps_kparam1; +extern unsigned int ps_kparam2; struct pt_regs; diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 5ea4fb718b970..44a316c3bc728 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -85,6 +85,18 @@ unsigned int __atags_pointer __initdata; unsigned int system_rev; EXPORT_SYMBOL(system_rev); +char microp_ver[4]; +EXPORT_SYMBOL(microp_ver); + +unsigned int als_kadc; +EXPORT_SYMBOL(als_kadc); + +unsigned int ps_kparam1; +EXPORT_SYMBOL(ps_kparam1); + +unsigned int ps_kparam2; +EXPORT_SYMBOL(ps_kparam2); + unsigned int system_serial_low; EXPORT_SYMBOL(system_serial_low); @@ -646,13 +658,41 @@ static int __init parse_tag_revision(const struct tag *tag) __tagtable(ATAG_REVISION, parse_tag_revision); +static int __init parse_tag_microp_version(const struct tag *tag) +{ + int i; + + for (i = 0; i < 4; i++) + microp_ver[i] = tag->u.microp_version.ver[i]; + + return 0; +} + +__tagtable(ATAG_MICROP_VERSION, parse_tag_microp_version); + +static int __init parse_tag_als_calibration(const struct tag *tag) +{ + als_kadc = tag->u.als_kadc.kadc; + + return 0; +} + +__tagtable(ATAG_ALS, parse_tag_als_calibration); + +static int __init parse_tag_ps_calibration(const struct tag *tag) +{ + ps_kparam1 = tag->u.ps_kparam.kparam1; + ps_kparam2 = tag->u.ps_kparam.kparam2; + + return 0; +} + +__tagtable(ATAG_PS, parse_tag_ps_calibration); + static int __init parse_tag_cmdline(const struct tag *tag) { -#ifndef CONFIG_CMDLINE_FORCE strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE); -#else - pr_warning("Ignoring tag cmdline (using the default kernel command line)\n"); -#endif /* CONFIG_CMDLINE_FORCE */ + return 0; } diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 34ac3a2b19b1e..90a757e1b99d6 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -17,7 +17,6 @@ obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o -obj-$(CONFIG_INPUT_CAPELLA_CM3602) += capella_cm3602.o obj-$(CONFIG_INPUT_CM109) += cm109.o obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o @@ -47,3 +46,8 @@ obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o +ifeq ($(CONFIG_MICROP_COMMON),y) + obj-$(CONFIG_LIGHTSENSOR_MICROP) += cm3602_lightsensor_microp_htc.o +else + obj-$(CONFIG_INPUT_CAPELLA_CM3602) += capella_cm3602.o +endif diff --git a/drivers/input/misc/cm3602_lightsensor_microp_htc.c b/drivers/input/misc/cm3602_lightsensor_microp_htc.c new file mode 100644 index 0000000000000..236fa35070b5e --- /dev/null +++ b/drivers/input/misc/cm3602_lightsensor_microp_htc.c @@ -0,0 +1,608 @@ +/* + * + * Copyright (C) 2009 HTC, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct microp_ls_info { + struct microp_function_config *ls_config; + struct input_dev *ls_input_dev; + struct early_suspend early_suspend; + struct i2c_client *client; + struct workqueue_struct *ls_wq; + + uint32_t als_func; + uint32_t als_kadc; + uint32_t als_gadc; + uint8_t als_calibrating; + int als_intr_enabled; + int is_suspend; + int old_intr_cmd; +}; + +struct microp_ls_info *ls_info; +static int ls_enable_flag; +static int ls_enable_num; + +static void enable_intr_do_work(struct work_struct *w); +static DECLARE_DELAYED_WORK(enable_intr_work, enable_intr_do_work); + +static void lightsensor_do_work(struct work_struct *w); +static DECLARE_WORK(lightsensor_work, lightsensor_do_work); + +void set_ls_kvalue(struct microp_ls_info *li) +{ + + if (!li) { + pr_err("%s: ls_info is empty\n", __func__); + return; + } + + printk(KERN_INFO "%s: ALS calibrated als_kadc=0x%x\n", + __func__, als_kadc); + if (als_kadc >> 16 == ALS_CALIBRATED) + li->als_kadc = als_kadc & 0xFFFF; + else { + li->als_kadc = 0; + printk(KERN_INFO "%s: no ALS calibrated\n", __func__); + } + + if (li->als_kadc && li->ls_config->golden_adc > 0) { + li->als_kadc = (li->als_kadc > 0 && li->als_kadc < 0x400) ? + li->als_kadc : li->ls_config->golden_adc; + li->als_gadc = li->ls_config->golden_adc; + } else { + li->als_kadc = 1; + li->als_gadc = 1; + } + printk(KERN_INFO "%s: als_kadc=0x%x, als_gadc=0x%x\n", + __func__, li->als_kadc, li->als_gadc); +} + +static int upload_ls_table(struct microp_ls_info *li) +{ + uint8_t data[20]; + int i; + for (i = 0; i < 10; i++) { + if (li->ls_config->levels[i] < 0x3FF) { + data[i] = (uint8_t)(li->ls_config->levels[i] + * li->als_kadc / li->als_gadc >> 8); + data[i + 10] = (uint8_t)(li->ls_config->levels[i] + * li->als_kadc / li->als_gadc); + } else { + data[i] = (uint8_t)(li->ls_config->levels[i] >> 8); + data[i + 10] = (uint8_t)(li->ls_config->levels[i] & 0xFF); + } + printk("ls_table: data[%d] , data[%d] = %x, %x\n", i, i, data[i], data[i+10]); + } + + return microp_i2c_write(MICROP_I2C_WCMD_ADC_TABLE, data, 20); +} + +static int get_ls_adc_level(uint8_t *data) +{ + struct microp_ls_info *li = ls_info; + uint8_t i, adc_level = 0; + uint16_t adc_value = 0; + + data[0] = 0x00; + data[1] = li->ls_config->channel; + if (microp_read_adc(data)) + return -1; + + adc_value = data[0]<<8 | data[1]; + if (adc_value > 0x3FF) { + printk(KERN_WARNING "%s: get wrong value: 0x%X\n", + __func__, adc_value); + return -1; + } else { + if (!li->als_calibrating) { + adc_value = adc_value * li->als_gadc / li->als_kadc; + if (adc_value > 0x3FF) + adc_value = 0x3FF; + data[0] = adc_value >> 8; + data[1] = adc_value & 0xFF; + } + for (i = 0; i < 10; i++) { + if (adc_value <= + li->ls_config->levels[i]) { + adc_level = i; + if (li->ls_config->levels[i]) + break; + } + } + printk(KERN_DEBUG "ALS value: 0x%X, level: %d #\n", + adc_value, adc_level); + data[2] = adc_level; + } + + return 0; +} + +void report_lightseneor_data(void) +{ + uint8_t data[3]; + int ret; + struct microp_ls_info *li = ls_info; + + ret = get_ls_adc_level(data); + if (!ret) { + input_report_abs(li->ls_input_dev, + ABS_MISC, (int)data[2]); + input_sync(li->ls_input_dev); + } +} + +static int ls_microp_intr_enable(uint8_t enable) +{ + + int ret; + uint8_t data[2]; + struct microp_ls_info *li = ls_info; + + if (li->old_intr_cmd) { + data[0] = 0; + if (enable) + data[1] = 1; + else + data[1] = 0; + + ret = microp_i2c_write(MICROP_I2C_WCMD_AUTO_BL_CTL, data, 2); + } else { + ret = microp_write_interrupt(li->client, + li->ls_config->int_pin, enable); + } + + return ret; +} + +static void enable_intr_do_work(struct work_struct *w) +{ + struct microp_ls_info *li = ls_info; + int ret; + + if (ls_enable_flag) { + ret = ls_microp_intr_enable(1); + if (ret < 0) + pr_err("%s error\n", __func__); + else { + li->als_intr_enabled = 1; + ls_enable_flag = 0; + input_report_abs(li->ls_input_dev, ABS_MISC, -1); + input_sync(li->ls_input_dev); + } + } + + report_lightseneor_data(); +} + +static void lightsensor_do_work(struct work_struct *w) +{ + /* Wait for Framework event polling ready */ + if (ls_enable_num == 0) { + ls_enable_num = 1; + msleep(300); + } + + report_lightseneor_data(); +} + +static irqreturn_t lightsensor_irq_handler(int irq, void *data) +{ + struct microp_ls_info *li = ls_info; + queue_work(li->ls_wq, &lightsensor_work); + + return IRQ_HANDLED; +} + +static int ls_power(int enable) +{ + struct microp_ls_info *li = ls_info; + + if (li->ls_config->ls_gpio_on) + gpio_set_value(li->ls_config->ls_gpio_on, enable ? 0 : 1); + + if (li->ls_config->ls_power) + li->ls_config->ls_power(LS_PWR_ON, enable); + + return 0; +} + +static int lightsensor_enable(void) +{ + int ret; + struct microp_ls_info *li = ls_info; + + pr_info("%s\n", __func__); + + ls_enable_flag = 1; + if (li->is_suspend) { + li->als_intr_enabled = 1; + pr_err("%s: microp is suspended\n", __func__); + return 0; + } + if (!li->als_intr_enabled) { + ret = ls_microp_intr_enable(1); + if (ret < 0) + pr_err("%s: set auto light sensor fail\n", __func__); + else { + li->als_intr_enabled = 1; + /* report an invalid value first to ensure we trigger an event + * when adc_level is zero. + */ + input_report_abs(li->ls_input_dev, ABS_MISC, -1); + input_sync(li->ls_input_dev); + } + } + return 0; +} + +static int lightsensor_disable(void) +{ + /* update trigger data when done */ + struct microp_ls_info *li = ls_info; + int ret; + + pr_info("%s\n", __func__); + ls_enable_flag = 0; + if (li->is_suspend) { + li->als_intr_enabled = 0; + pr_err("%s: microp is suspended\n", __func__); + return 0; + } + + if (li->als_intr_enabled) { + ret = ls_microp_intr_enable(0); + if (ret < 0) + pr_err("%s: disable auto light sensor fail\n", + __func__); + else + li->als_intr_enabled = 0; + } + return 0; +} + +DEFINE_MUTEX(ls_i2c_api_lock); +static int lightsensor_opened; + +static int lightsensor_open(struct inode *inode, struct file *file) +{ + int rc = 0; + pr_debug("%s\n", __func__); + mutex_lock(&ls_i2c_api_lock); + if (lightsensor_opened) { + pr_err("%s: already opened\n", __func__); + rc = -EBUSY; + } + lightsensor_opened = 1; + mutex_unlock(&ls_i2c_api_lock); + return rc; +} + +static int lightsensor_release(struct inode *inode, struct file *file) +{ + pr_debug("%s\n", __func__); + mutex_lock(&ls_i2c_api_lock); + lightsensor_opened = 0; + mutex_unlock(&ls_i2c_api_lock); + return 0; +} + +static long lightsensor_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int rc, val; + struct microp_ls_info *li = ls_info; + mutex_lock(&ls_i2c_api_lock); + pr_debug("%s cmd %d\n", __func__, _IOC_NR(cmd)); + + switch (cmd) { + case LIGHTSENSOR_IOCTL_ENABLE: + if (get_user(val, (unsigned long __user *)arg)) { + rc = -EFAULT; + break; + } + pr_info("%s set value = %d\n", __func__, val); + rc = val ? lightsensor_enable() : lightsensor_disable(); + break; + case LIGHTSENSOR_IOCTL_GET_ENABLED: + val = li->als_intr_enabled; + pr_info("%s get enabled status: %d\n", __func__, val); + rc = put_user(val, (unsigned long __user *)arg); + break; + default: + pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd)); + rc = -EINVAL; + } + + mutex_unlock(&ls_i2c_api_lock); + return rc; +} + +static struct file_operations lightsensor_fops = { + .owner = THIS_MODULE, + .open = lightsensor_open, + .release = lightsensor_release, + .unlocked_ioctl = lightsensor_ioctl +}; + +static struct miscdevice lightsensor_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "lightsensor", + .fops = &lightsensor_fops +}; + +static ssize_t ls_adc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + uint8_t data[3]; + int ret; + + ret = get_ls_adc_level(data); + + ret = sprintf(buf, + "ADC[0x%03X] => level %d\n", + (data[0] << 8 | data[1]), data[2]); + + return ret; +} + +static DEVICE_ATTR(ls_adc, 0666, ls_adc_show, NULL); + +static ssize_t ls_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint8_t data[2] = {0, 0}; + int ret; + + microp_i2c_read(MICROP_I2C_RCMD_SPI_BL_STATUS, data, 2); + ret = sprintf(buf, "Light sensor Auto = %d, SPI enable = %d\n", + data[0], data[1]); + + return ret; +} + +static ssize_t ls_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct microp_ls_info *li = ls_info; + uint8_t enable = 0; + int ls_auto; + int ret; + + ls_auto = -1; + sscanf(buf, "%d", &ls_auto); + + if (ls_auto != 0 && ls_auto != 1 && ls_auto != 147) + return -EINVAL; + + if (ls_auto) { + enable = 1; + li->als_calibrating = (ls_auto == 147) ? 1 : 0; + li->als_intr_enabled = 1; + } else { + enable = 0; + li->als_calibrating = 0; + li->als_intr_enabled = 0; + } + + ret = ls_microp_intr_enable(enable); + if (ret < 0) + pr_err("%s: ls intr enable fail\n", __func__); + + return count; +} + +static DEVICE_ATTR(ls_auto, 0666, ls_enable_show, ls_enable_store); + +static ssize_t ls_kadc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct microp_ls_info *li = ls_info; + int ret; + + ret = sprintf(buf, "kadc = 0x%x, gadc = 0x%x, real kadc = 0x%x\n", + li->als_kadc, li->als_gadc, als_kadc); + + return ret; +} + +static ssize_t ls_kadc_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct microp_ls_info *li = ls_info; + int kadc_temp = 0; + + sscanf(buf, "%d", &kadc_temp); + if (kadc_temp <= 0 || li->ls_config->golden_adc <= 0) { + printk(KERN_ERR "%s: kadc_temp=0x%x, als_gadc=0x%x\n", + __func__, + kadc_temp, + li->ls_config->golden_adc); + return -EINVAL; + } + + li->als_kadc = kadc_temp; + li->als_gadc = li->ls_config->golden_adc; + printk(KERN_INFO "%s: als_kadc=0x%x, als_gadc=0x%x\n", + __func__, li->als_kadc, li->als_gadc); + + if (upload_ls_table(li) < 0) + printk(KERN_ERR "%s: upload ls table fail\n", __func__); + + return count; +} + +static DEVICE_ATTR(ls_kadc, 0666, ls_kadc_show, ls_kadc_store); + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void light_sensor_suspend(struct early_suspend *h) +{ + struct microp_ls_info *li = ls_info; + int ret; + + li->is_suspend = 1; + cancel_delayed_work(&enable_intr_work); + if (li->als_intr_enabled) { + ret = ls_microp_intr_enable(0); + if (ret < 0) + pr_err("%s: disable auto light sensor fail\n", + __func__); + else + li->als_intr_enabled = 0; + } + ls_power(0); +} + +static void light_sensor_resume(struct early_suspend *h) +{ + struct microp_ls_info *li = ls_info; + + ls_power(1); + queue_delayed_work(li->ls_wq, &enable_intr_work, msecs_to_jiffies(800)); + li->is_suspend = 0; +} +#endif + +static int lightsensor_probe(struct platform_device *pdev) +{ + int ret, irq; + struct microp_ls_info *li; + struct lightsensor_platform_data *pdata = pdev->dev.platform_data; + + li = kzalloc(sizeof(struct microp_ls_info), GFP_KERNEL); + if (!li) + return -ENOMEM; + ls_info = li; + li->client = dev_get_drvdata(&pdev->dev); + + if (!li->client) { + pr_err("%s: can't get microp i2c client\n", __func__); + return -1; + } + li->ls_input_dev = input_allocate_device(); + if (!li->ls_input_dev) { + pr_err("%s: could not allocate input device\n", __func__); + return -ENOMEM; + } + li->ls_input_dev->name = "lightsensor-level"; + set_bit(EV_ABS, li->ls_input_dev->evbit); + input_set_abs_params(li->ls_input_dev, ABS_MISC, 0, 9, 0, 0); + + ret = input_register_device(li->ls_input_dev); + if (ret < 0) { + pr_err("%s: can not register input device\n", + __func__); + return ret; + } + + ret = misc_register(&lightsensor_misc); + if (ret < 0) { + pr_err("%s: can not register misc device\n", + __func__); + return ret; + } + + li->ls_config = pdata->config; + irq = pdata->irq; + li->old_intr_cmd = pdata->old_intr_cmd; + ret = request_irq(irq, lightsensor_irq_handler, IRQF_TRIGGER_NONE, "lightsensor_microp", li); + if (ret < 0) { + pr_err("%s: request_irq(%d) failed for (%d)\n", + __func__, irq, ret); + return ret; + } + + set_ls_kvalue(li); + ret = upload_ls_table(li); + if (ret < 0) { + pr_err("%s: upload ls table fail\n", + __func__); + return ret; + } + + li->ls_wq = create_workqueue("ls_wq"); + if (li->ls_wq == NULL) + return -ENOMEM; + + if (li->ls_config->ls_gpio_on) { + ret = gpio_request(li->ls_config->ls_gpio_on, + "microp_i2c"); + if (ret < 0) { + pr_err("request gpio ls failed\n"); + return ret; + } + ret = gpio_direction_output(li->ls_config->ls_gpio_on, 0); + if (ret < 0) { + pr_err("gpio_direction_output ls failed\n"); + return ret; + } + } + ls_power(1); +#ifdef CONFIG_HAS_EARLYSUSPEND + li->early_suspend.level = + EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + li->early_suspend.suspend = light_sensor_suspend; + li->early_suspend.resume = light_sensor_resume; + register_early_suspend(&li->early_suspend); +#endif + ret = device_create_file(&li->client->dev, &dev_attr_ls_adc); + ret = device_create_file(&li->client->dev, &dev_attr_ls_auto); + ret = device_create_file(&li->client->dev, &dev_attr_ls_kadc); + + return 0; + +} + +static struct platform_driver lightsensor_driver = { + .probe = lightsensor_probe, + .driver = { .name = "lightsensor_microp", }, +}; + +static int __init light_sensor_init(void) +{ + return platform_driver_register(&lightsensor_driver); +} + +static void __exit light_sensor_exit(void) +{ + platform_driver_unregister(&lightsensor_driver); +} + +module_init(light_sensor_init); +module_exit(light_sensor_exit); + +MODULE_DESCRIPTION("HTC LIGHT SENSOR"); +MODULE_LICENSE("GPL"); From 629382732995b73a4501882a63befbe72a602105 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Mon, 1 Nov 2010 21:39:33 -0400 Subject: [PATCH 1028/2556] sensors: board-supersonic this fixed the proximity sensor and enabled the rest of the sensors to work \o/. anyhow the board file was the real winner here. but i kanged some other things along the way. msm_hsusb_8x50_phy_reset commented out for compile reasons and because i havent add the usb shit yet. Change-Id: I468ff8fc160ce39ca6a1ecc6067dc9ce36475a72 --- arch/arm/mach-msm/board-supersonic.c | 405 ++++++++++++++++++++------- drivers/i2c/busses/i2c-msm.c | 13 +- include/linux/capella_cm3602.h | 5 +- include/linux/i2c.h | 4 - include/linux/lightsensor.h | 7 + 5 files changed, 325 insertions(+), 109 deletions(-) diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index 18678c00a0bf7..4cf9415b5a256 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -27,8 +27,6 @@ #include // #include #include -#include -#include #include #include #include @@ -58,10 +56,17 @@ #include #include #include + +#include +#include +#include + + #ifdef CONFIG_MICROP_COMMON #include #endif +#include #include #include "board-supersonic-tpa2018d1.h" @@ -104,13 +109,6 @@ static struct platform_device htc_battery_pdev = { #ifdef CONFIG_MICROP_COMMON static int capella_cm3602_power(int pwr_device, uint8_t enable); static struct microp_function_config microp_functions[] = { - { - .name = "remote-key", - .category = MICROP_FUNCTION_REMOTEKEY, - .levels = {0, 33, 50, 110, 160, 220}, - .channel = 1, - .int_pin = 1 << 7, - }, { .name = "reset-int", .category = MICROP_FUNCTION_RESET_INT, @@ -161,7 +159,7 @@ static struct bma150_platform_data supersonic_g_sensor_pdata = { static int __capella_cm3602_power(int on) { int ret; - struct vreg *vreg = vreg_get(0, "gp1");; + struct vreg *vreg = vreg_get(0, "gp1"); if (!vreg) { printk(KERN_ERR "%s: vreg error\n", __func__); return -EIO; @@ -215,6 +213,13 @@ static struct capella_cm3602_platform_data capella_cm3602_pdata = { }; /* End Proximity Sensor (Capella_CM3602)*/ +static struct htc_headset_microp_platform_data htc_headset_microp_data = { + .remote_int = 1 << 7, + .remote_irq = MSM_uP_TO_INT(7), + .remote_enable_pin = 0, + .adc_channel = 0x01, + .adc_remote = {0, 33, 50, 110, 160, 220}, +}; static struct platform_device microp_devices[] = { { @@ -243,6 +248,13 @@ static struct platform_device microp_devices[] = { .platform_data = &capella_cm3602_pdata, }, }, + { + .name = "HTC_HEADSET_MICROP", + .id = -1, + .dev = { + .platform_data = &htc_headset_microp_data, + }, + }, }; static struct microp_i2c_platform_data microp_data = { @@ -277,15 +289,40 @@ static struct platform_device supersonic_leds = { }, }; -extern void msm_hsusb_8x50_phy_reset(void); static int supersonic_phy_init_seq[] = { 0xC, 0x31, 0x30, 0x32, 0x1D, 0x0D, 0x1D, 0x10, -1 }; + +// USB cable out: supersonic_uart_usb_switch(1) +// USB cable in: supersonic_uart_usb_switch(0) +static void supersonic_uart_usb_switch(int uart) +{ + printk(KERN_INFO "%s:uart:%d\n", __func__, uart); + gpio_set_value(SUPERSONIC_USB_UARTz_SW, uart?1:0); // XA and for USB cable in to reset wimax UART + + if(system_rev && uart) // XB + { + if (gpio_get_value(SUPERSONIC_WIMAX_CPU_UARTz_SW)) // Wimax UART + { + printk(KERN_INFO "%s:Wimax UART\n", __func__); + gpio_set_value(SUPERSONIC_USB_UARTz_SW,1); + gpio_set_value(SUPERSONIC_WIMAX_CPU_UARTz_SW,1); + } + else // USB, CPU UART + { + printk(KERN_INFO "%s:Non wimax UART\n", __func__); + gpio_set_value(SUPERSONIC_WIMAX_CPU_UARTz_SW, uart==2?1:0); + } + } +} + +extern void msm_hsusb_8x50_phy_reset(void); + static struct msm_hsusb_platform_data msm_hsusb_pdata = { .phy_init_seq = supersonic_phy_init_seq, - .usb_connected = notify_usb_connected, -// XXX: Car kit -// .usb_id_pin_gpio = SUPERSONIC_GPIO_USB_ID_PIN, -// .config_usb_id_gpios = config_supersonic_usb_id_gpios, +// .phy_reset = msm_hsusb_8x50_phy_reset, + .usb_id_pin_gpio = SUPERSONIC_GPIO_USB_ID_PIN, + .accessory_detect = 1, /* detect by ID pin gpio */ + .usb_uart_switch = supersonic_uart_usb_switch, }; @@ -424,28 +461,27 @@ XB : GPIO33 = 0 -> USB */ -// USB cable out: supersonic_uart_usb_switch(1) -// USB cable in: supersonic_uart_usb_switch(0) -static void supersonic_uart_usb_switch(int uart) +static int __init board_serialno_setup(char *serialno) { - printk(KERN_INFO "%s:uart:%d\n", __func__, uart); - gpio_set_value(SUPERSONIC_USB_UARTz_SW, uart?1:0); // XA and for USB cable in to reset wimax UART - - if(system_rev && uart) // XB - { - if (gpio_get_value(SUPERSONIC_WIMAX_CPU_UARTz_SW)) // Wimax UART - { - printk(KERN_INFO "%s:Wimax UART\n", __func__); - gpio_set_value(SUPERSONIC_USB_UARTz_SW,1); - gpio_set_value(SUPERSONIC_WIMAX_CPU_UARTz_SW,1); - } - else // USB, CPU UART - { - printk(KERN_INFO "%s:Non wimax UART\n", __func__); - gpio_set_value(SUPERSONIC_WIMAX_CPU_UARTz_SW, uart==2?1:0); - } +#ifdef CONFIG_USB_ANDROID_RNDIS + int i; + char *src = serialno; + + /* create a fake MAC address from our serial number. + * first byte is 0x02 to signify locally administered. + */ + rndis_pdata.ethaddr[0] = 0x02; + for (i = 0; *src; i++) { + /* XOR the USB serial across the remaining bytes */ + rndis_pdata.ethaddr[i % (ETH_ALEN - 1) + 1] ^= *src++; } +#endif + + android_usb_pdata.serial_number = serialno; + return 1; } +__setup("androidboot.serialno=", board_serialno_setup); + static struct platform_device supersonic_rfkill = { .name = "supersonic_rfkill", @@ -662,7 +698,7 @@ struct atmel_i2c_platform_data supersonic_atmel_ts_data[] = { .config_T6 = {0, 0, 0, 0, 0, 0}, .config_T7 = {50, 15, 50}, .config_T8 = {10, 0, 20, 10, 0, 0, 5, 0}, - .config_T9 = {139, 0, 0, 18, 12, 0, 16, 32, 3, 5, 0, 5, 2, 14, 2, 10, 25, 10, 0, 0, 0, 0, 0, 0, 0, 0, 143, 25, 146, 10, 40}, + .config_T9 = {139, 0, 0, 18, 12, 0, 16, 32, 3, 5, 0, 5, 2, 14, 5, 10, 25, 10, 0, 0, 0, 0, 0, 0, 0, 0, 143, 25, 146, 10, 40}, .config_T15 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .config_T19 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .config_T20 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, @@ -692,7 +728,7 @@ struct atmel_i2c_platform_data supersonic_atmel_ts_data[] = { .config_T6 = {0, 0, 0, 0, 0, 0}, .config_T7 = {100, 10, 50}, .config_T8 = {8, 0, 50, 50, 0, 0, 50, 0}, - .config_T9 = {3, 0, 0, 18, 12, 0, 32, 40, 2, 5, 0, 0, 0, 0, 2, 10, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 143, 47, 145, 81}, + .config_T9 = {139, 0, 0, 18, 12, 0, 16, 32, 3, 5, 0, 5, 2, 14, 5, 10, 25, 10, 0, 0, 0, 0, 0, 0, 0, 0, 143, 25, 146, 10, 20}, .config_T15 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .config_T19 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .config_T20 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, @@ -719,7 +755,7 @@ struct atmel_i2c_platform_data supersonic_atmel_ts_data[] = { .config_T6 = {0, 0, 0, 0, 0, 0}, .config_T7 = {100, 10, 50}, .config_T8 = {8, 0, 50, 50, 0, 0, 50, 0}, - .config_T9 = {3, 0, 0, 18, 12, 0, 64, 45, 3, 5, 0, 0, 0, 0, 2, 10, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 47, 143, 81}, + .config_T9 = {139, 0, 0, 18, 12, 0, 16, 32, 3, 5, 0, 5, 2, 14, 5, 10, 25, 10, 0, 0, 0, 0, 0, 0, 0, 0, 143, 25, 146, 10, 20}, .config_T15 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .config_T19 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, .config_T20 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, @@ -742,7 +778,7 @@ static struct regulator_init_data tps65023_data[5] = { { .constraints = { .name = "dcdc1", /* VREG_MSMC2_1V29 */ - .min_uV = 1000000, + .min_uV = 975000, .max_uV = 1300000, .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, }, @@ -780,26 +816,28 @@ static struct regulator_init_data tps65023_data[5] = { }, }; -static struct h2w_platform_data supersonic_h2w_data = { +static struct htc_headset_mgr_platform_data htc_headset_mgr_data = { }; -static struct platform_device supersonic_h2w = { - .name = "htc_headset", - .id = -1, - .dev = { - .platform_data = &supersonic_h2w_data, +static struct platform_device htc_headset_mgr = { + .name = "HTC_HEADSET_MGR", + .id = -1, + .dev = { + .platform_data = &htc_headset_mgr_data, }, }; -static struct audio_jack_platform_data supersonic_jack_data = { - .gpio = SUPERSONIC_GPIO_35MM_HEADSET_DET, +static struct htc_headset_gpio_platform_data htc_headset_gpio_data = { + .hpin_gpio = SUPERSONIC_GPIO_35MM_HEADSET_DET, + .key_enable_gpio = 0, + .mic_select_gpio = 0, }; -static struct platform_device supersonic_audio_jack = { - .name = "audio-jack", - .id = -1, - .dev = { - .platform_data = &supersonic_jack_data, +static struct platform_device htc_headset_gpio = { + .name = "HTC_HEADSET_GPIO", + .id = -1, + .dev = { + .platform_data = &htc_headset_gpio_data, }, }; @@ -814,6 +852,192 @@ static struct tpa2018d1_platform_data tpa2018_data = { .gpio_tpa2018_spk_en = SUPERSONIC_AUD_SPK_EN, }; +/* + * HDMI platform data + */ + +#if 1 +#define HDMI_DBG(s...) printk("[hdmi]" s) +#else +#define HDMI_DBG(s...) do {} while (0) +#endif + +static int hdmi_power(int on) +{ + HDMI_DBG("%s(%d)\n", __func__, on); + + switch(on) { + /* Power on/off sequence for normal or D2 sleep mode */ + case 0: + gpio_set_value(HDMI_RST, 0); + msleep(2); + gpio_set_value(V_HDMI_3V3_EN, 0); + gpio_set_value(V_VGA_5V_SIL9022A_EN, 0); + msleep(2); + gpio_set_value(V_HDMI_1V2_EN, 0); + break; + case 1: + gpio_set_value(V_HDMI_1V2_EN, 1); + msleep(2); + gpio_set_value(V_VGA_5V_SIL9022A_EN, 1); + gpio_set_value(V_HDMI_3V3_EN, 1); + msleep(2); + gpio_set_value(HDMI_RST, 1); + msleep(2); + break; + + /* Power on/off sequence for D3 sleep mode */ + case 2: + gpio_set_value(V_HDMI_3V3_EN, 0); + break; + case 3: + gpio_set_value(HDMI_RST, 0); + msleep(2); + gpio_set_value(V_HDMI_3V3_EN, 1); + gpio_set_value(V_VGA_5V_SIL9022A_EN, 1); + msleep(50); + gpio_set_value(HDMI_RST, 1); + msleep(10); + break; + case 4: + gpio_set_value(V_VGA_5V_SIL9022A_EN, 0); + break; + case 5: + gpio_set_value(V_VGA_5V_SIL9022A_EN, 1); + break; + + default: + return -EINVAL; + } + return 0; +} + +static uint32_t hdmi_gpio_on_table[] = { + PCOM_GPIO_CFG(SUPERSONIC_LCD_R0, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_R1, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_R2, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_R3, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_R4, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + + PCOM_GPIO_CFG(SUPERSONIC_LCD_G0, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_G1, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_G2, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_G3, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_G4, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_G5, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + + PCOM_GPIO_CFG(SUPERSONIC_LCD_B0, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_B1, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_B2, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_B3, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_B4, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + + PCOM_GPIO_CFG(SUPERSONIC_LCD_PCLK, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_VSYNC, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_HSYNC, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_DE, 1, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), +}; + +static uint32_t hdmi_gpio_off_table[] = { + PCOM_GPIO_CFG(SUPERSONIC_LCD_R0, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_R1, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_R2, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_R3, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_R4, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + + PCOM_GPIO_CFG(SUPERSONIC_LCD_G0, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_G1, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_G2, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_G3, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_G4, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_G5, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + + PCOM_GPIO_CFG(SUPERSONIC_LCD_B0, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_B1, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_B2, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_B3, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_B4, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + + PCOM_GPIO_CFG(SUPERSONIC_LCD_PCLK, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_VSYNC, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_HSYNC, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_LCD_DE, 0, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_2MA), +}; + + +static void suc_hdmi_gpio_on(void) +{ + HDMI_DBG("%s\n", __func__); + + config_gpio_table(hdmi_gpio_on_table, ARRAY_SIZE(hdmi_gpio_on_table)); +} + +static void suc_hdmi_gpio_off(void) +{ + int i = 0; + + HDMI_DBG("%s\n", __func__); + config_gpio_table(hdmi_gpio_off_table, ARRAY_SIZE(hdmi_gpio_off_table)); + + for (i = SUPERSONIC_LCD_R0; i <= SUPERSONIC_LCD_R4; i++) + gpio_set_value(i, 0); + for (i = SUPERSONIC_LCD_G0; i <= SUPERSONIC_LCD_G5; i++) + gpio_set_value(i, 0); + for (i = SUPERSONIC_LCD_B0; i <= SUPERSONIC_LCD_DE; i++) + gpio_set_value(i, 0); +} + +static struct hdmi_platform_data hdmi_device_data = { + .hdmi_res = { + .start = MSM_HDMI_FB_BASE, + .end = MSM_HDMI_FB_BASE + MSM_HDMI_FB_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + .power = hdmi_power, + .hdmi_gpio_on = suc_hdmi_gpio_on, + .hdmi_gpio_off = suc_hdmi_gpio_off, +}; + static struct tpa6130_platform_data headset_amp_platform_data = { .enable_rpc_server = 0, }; @@ -861,6 +1085,11 @@ static struct i2c_board_info i2c_devices[] = { I2C_BOARD_INFO("tpa2018d1", 0x58), .platform_data = &tpa2018_data, }, + { + I2C_BOARD_INFO("SiL902x-hdmi", 0x76 >> 1), + .platform_data = &hdmi_device_data, + .irq = MSM_uP_TO_INT(1), + }, }; #ifdef CONFIG_ARCH_QSD8X50 @@ -983,7 +1212,7 @@ static void supersonic_ov9665_clk_switch(void){ } static struct camera_flash_cfg msm_camera_sensor_flash_cfg = { -// .camera_flash = flashlight_control, + .camera_flash = flashlight_control, .num_flash_levels = FLASHLIGHT_NUM, .low_temp_limit = 10, .low_cap_limit = 15, @@ -1058,16 +1287,36 @@ static struct platform_device supersonic_flashlight_device = { }, }; +static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { + .rx_wakeup_irq = -1, + .inject_rx_on_wakeup = 0, + .exit_lpm_cb = bcm_bt_lpm_exit_lpm_locked, +}; + +static struct bcm_bt_lpm_platform_data bcm_bt_lpm_pdata = { + .gpio_wake = SUPERSONIC_GPIO_BT_CHIP_WAKE, + .gpio_host_wake = SUPERSONIC_GPIO_BT_HOST_WAKE, + .request_clock_off_locked = msm_hs_request_clock_off_locked, + .request_clock_on_locked = msm_hs_request_clock_on_locked, +}; + +struct platform_device bcm_bt_lpm_device = { + .name = "bcm_bt_lpm", + .id = 0, + .dev = { + .platform_data = &bcm_bt_lpm_pdata, + }, +}; + static struct platform_device *devices[] __initdata = { #ifndef CONFIG_MSM_SERIAL_DEBUGGER &msm_device_uart1, #endif -#ifdef CONFIG_SERIAL_MSM_HS + &bcm_bt_lpm_device, &msm_device_uart_dm1, -#endif - &supersonic_h2w, &htc_battery_pdev, - &supersonic_audio_jack, + &htc_headset_mgr, + &htc_headset_gpio, &ram_console_device, &supersonic_rfkill, &msm_device_smd, @@ -1135,40 +1384,6 @@ static struct msm_acpu_clock_platform_data supersonic_clock_data = { int supersonic_init_mmc(int sysrev); -#ifdef CONFIG_SERIAL_MSM_HS -extern void supersonic_config_bt_disable_active(void); -extern void supersonic_config_bt_disable_sleep(void); -extern int supersonic_is_bluetooth_off(void); - -static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { - .rx_wakeup_irq = MSM_GPIO_TO_INT(SUPERSONIC_GPIO_BT_HOST_WAKE), /*Chip to Device*/ - .inject_rx_on_wakeup = 0, - .exit_lpm_cb = bcm_bt_lpm_exit_lpm_locked, - -#if 0 - .config_as_uart = supersonic_config_bt_disable_active, - .config_as_gpio = supersonic_config_bt_disable_sleep, - .need_config = supersonic_is_bluetooth_off, -#endif -}; - -static struct bcm_bt_lpm_platform_data bcm_bt_lpm_pdata = { - .gpio_wake = SUPERSONIC_GPIO_BT_CHIP_WAKE, - .gpio_host_wake = SUPERSONIC_GPIO_BT_HOST_WAKE, - .request_clock_off_locked = msm_hs_request_clock_off_locked, - .request_clock_on_locked = msm_hs_request_clock_on_locked, -}; - -struct platform_device bcm_bt_lpm_device = { - .name = "bcm_bt_lpm", - .id = 0, - .dev = { - .platform_data = &bcm_bt_lpm_pdata, - }, -}; -#endif - - static int OJ_BMA_power(void) { int ret; @@ -1241,9 +1456,6 @@ static void __init supersonic_init(void) OJ_BMA_power(); - gpio_request(SUPERSONIC_GPIO_PROXIMITY_EN_N, "proximity_en"); - gpio_direction_output(SUPERSONIC_GPIO_PROXIMITY_EN_N, 0); - msm_acpu_clock_init(&supersonic_clock_data); #if defined(CONFIG_MSM_SERIAL_DEBUGGER) @@ -1264,11 +1476,9 @@ static void __init supersonic_init(void) msm_device_spi.dev.platform_data = &supersonic_spi_pdata; #endif - #ifdef CONFIG_SERIAL_MSM_HS msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; - msm_device_uart_dm1.name = "msm_serial_hs_bcm"; /* for bcm */ - #endif + config_gpio_table(usb_phy_3v3_table, ARRAY_SIZE(usb_phy_3v3_table)); config_gpio_table(camera_off_gpio_table, ARRAY_SIZE(camera_off_gpio_table)); gpio_request(SUPERSONIC_GPIO_TP_EN, "tp_en"); @@ -1299,9 +1509,6 @@ static void __init supersonic_init(void) pr_err("failed to create board_properties\n"); msm_init_pmic_vibrator(); - config_gpio_table(usb_phy_3v3_table, ARRAY_SIZE(usb_phy_3v3_table)); - gpio_set_value(SUPERSONIC_USB_PHY_3V3_ENABLE, 1); - } static void __init supersonic_fixup(struct machine_desc *desc, struct tag *tags, diff --git a/drivers/i2c/busses/i2c-msm.c b/drivers/i2c/busses/i2c-msm.c index ad76d794d6e81..1e2e493f44cc0 100644 --- a/drivers/i2c/busses/i2c-msm.c +++ b/drivers/i2c/busses/i2c-msm.c @@ -221,7 +221,7 @@ static void msm_i2c_interrupt_locked(struct msm_i2c_dev *dev) return; out_err: - dev_err(dev->dev, "error, status %x\n", status); + dev_err(dev->dev, "error, status %x (%02X)\n", status, dev->msg->addr); dev->ret = -EIO; out_complete: complete(dev->complete); @@ -286,6 +286,9 @@ msm_i2c_recover_bus_busy(struct msm_i2c_dev *dev) dev->base + I2C_WRITE_DATA); } + gpio_request(gpio_clk, "gpio_clk"); + gpio_request(gpio_dat, "gpio_dat"); + dev_warn(dev->dev, "i2c_scl: %d, i2c_sda: %d\n", gpio_get_value(gpio_clk), gpio_get_value(gpio_dat)); @@ -346,6 +349,7 @@ msm_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) ret = msm_i2c_poll_notbusy(dev, 1); if (ret) { + dev_err(dev->dev, "Still busy in starting xfer(%02X)\n", msgs->addr); ret = msm_i2c_recover_bus_busy(dev); if (ret) goto err; @@ -374,7 +378,9 @@ msm_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) */ timeout = wait_for_completion_timeout(&complete, HZ); - msm_i2c_poll_notbusy(dev, 0); /* Read may not have stopped in time */ + if (msm_i2c_poll_notbusy(dev, 0)) /* Read may not have stopped in time */ + dev_err(dev->dev, "Still busy after xfer completion (%02X)\n", + msgs->addr); spin_lock_irqsave(&dev->lock, flags); if (dev->flush_cnt) { @@ -397,8 +403,7 @@ msm_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) } if (ret < 0) { - dev_err(dev->dev, "Error during data xfer %x (%d)\n", - msgs[0].addr, ret); + dev_err(dev->dev, "Error during data xfer (%d) @%02X\n", ret, msgs->addr); msm_i2c_recover_bus_busy(dev); } err: diff --git a/include/linux/capella_cm3602.h b/include/linux/capella_cm3602.h index 7f1de9b912a20..c08e30209276e 100644 --- a/include/linux/capella_cm3602.h +++ b/include/linux/capella_cm3602.h @@ -29,8 +29,9 @@ #ifdef __KERNEL__ #define CAPELLA_CM3602 "capella_cm3602" struct capella_cm3602_platform_data { - int (*power)(int); /* power to the chip */ - int p_out; /* proximity-sensor outpuCAPELLA_CM3602_IOCTL_ENABLE,t */ + int (*power)(int, uint8_t); /* power to the chip */ + int p_out; /* proximity-sensor output */ + int p_en; }; #endif /* __KERNEL__ */ diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 903576df88dcd..e7559e1f8dd57 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -34,7 +34,6 @@ #include /* for struct device */ #include /* for completion */ #include -#include /* for struct device_node */ extern struct bus_type i2c_bus_type; extern struct device_type i2c_adapter_type; @@ -258,9 +257,6 @@ struct i2c_board_info { unsigned short addr; void *platform_data; struct dev_archdata *archdata; -#ifdef CONFIG_OF - struct device_node *of_node; -#endif int irq; }; diff --git a/include/linux/lightsensor.h b/include/linux/lightsensor.h index 501074f406bba..f133cb684c227 100644 --- a/include/linux/lightsensor.h +++ b/include/linux/lightsensor.h @@ -25,4 +25,11 @@ #define LIGHTSENSOR_IOCTL_GET_ENABLED _IOR(LIGHTSENSOR_IOCTL_MAGIC, 1, int *) #define LIGHTSENSOR_IOCTL_ENABLE _IOW(LIGHTSENSOR_IOCTL_MAGIC, 2, int *) +struct lightsensor_smd_platform_data { + const char *name; + uint16_t levels[10]; + uint16_t golden_adc; + int (*ls_power)(int, uint8_t); +}; + #endif From b3c0c37096e883151d59ae6f620fbe2f4340e611 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Mon, 1 Nov 2010 22:45:20 -0400 Subject: [PATCH 1029/2556] Supersonic-audio: clean up --- arch/arm/mach-msm/board-supersonic-audio.c | 23 ++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-msm/board-supersonic-audio.c b/arch/arm/mach-msm/board-supersonic-audio.c index d8f25dd33a182..c38c6480f8dae 100644 --- a/arch/arm/mach-msm/board-supersonic-audio.c +++ b/arch/arm/mach-msm/board-supersonic-audio.c @@ -27,7 +27,7 @@ #include "pmic.h" #include "board-supersonic-tpa2018d1.h" -#if 0 +#if 1 #define D(fmt, args...) printk(KERN_INFO "Audio: "fmt, ##args) #else #define D(fmt, args...) do {} while (0) @@ -67,9 +67,20 @@ static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { void supersonic_headset_enable(int en) { D("%s %d\n", __func__, en); - /* enable audio amp */ - if (en) mdelay(15); - gpio_set_value(SUPERSONIC_AUD_JACKHP_EN, !!en); + /* enable audio amp */ + if (en != headset_status) { + headset_status = en; + if(en) { + gpio_set_value(SUPERSONIC_AUD_JACKHP_EN, 1); + mdelay(10); + if (system_rev == 0) + set_headset_amp(1); + } else { + if (system_rev == 0) + set_headset_amp(0); + gpio_set_value(SUPERSONIC_AUD_JACKHP_EN, 0); + } + } } void supersonic_speaker_enable(int en) @@ -219,8 +230,8 @@ void supersonic_analog_init(void) pmic_spkr_set_mux_hpf_corner_freq(SPKR_FREQ_0_73KHZ); pmic_mic_set_volt(MIC_VOLT_1_80V); pmic_set_speaker_delay(SPKR_DLY_100MS); - - gpio_request(SUPERSONIC_AUD_JACKHP_EN, "aud_jackhp_en"); + + gpio_request(SUPERSONIC_AUD_JACKHP_EN, "aud_jackhp_en"); gpio_direction_output(SUPERSONIC_AUD_JACKHP_EN, 0); gpio_set_value(SUPERSONIC_AUD_JACKHP_EN, 0); From 22dfe6ece82a9167a16212f3775cfd4daa0d5e8f Mon Sep 17 00:00:00 2001 From: toastcfh Date: Fri, 29 Oct 2010 23:47:05 -0400 Subject: [PATCH 1030/2556] drivers_led: led_microp driver works with htc microp common driver. fixes leds on the Supersonic Change-Id: I2c060d9e02fc3f49d8ed3a0b16ff3b137f351e5f --- drivers/leds/Makefile | 4 +- drivers/leds/leds-microp.c | 778 +++++++++++++++++++++++++++++++++++++ 2 files changed, 780 insertions(+), 2 deletions(-) create mode 100644 drivers/leds/leds-microp.c diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 3efece22e57af..891073b4fccf6 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -40,8 +40,8 @@ obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o obj-$(CONFIG_LEDS_DELL_NETBOOKS) += dell-led.o obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o -obj-$(CONFIG_LEDS_NS2) += leds-ns2.o -obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o +obj-$(CONFIG_LEDS_PCA963X) += leds-pca963x.o +obj-$(CONFIG_MICROP_COMMON) += leds-microp.o # LED SPI Drivers obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o diff --git a/drivers/leds/leds-microp.c b/drivers/leds/leds-microp.c new file mode 100644 index 0000000000000..ecee931426aea --- /dev/null +++ b/drivers/leds/leds-microp.c @@ -0,0 +1,778 @@ +/* include/asm/mach-msm/leds-microp.c + * + * Copyright (C) 2009 HTC Corporation. + * + * 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. + * + */ + +#ifdef CONFIG_MICROP_COMMON +#include +#include +#include +#include +#include +#include +#include +#include + +static int microp_write_led_mode(struct led_classdev *led_cdev, + uint8_t mode, uint16_t off_timer) +{ + struct microp_led_data *ldata; + uint8_t data[7]; + int ret; + + ldata = container_of(led_cdev, struct microp_led_data, ldev); + + if (!strcmp(ldata->ldev.name, "green")) { + data[0] = 0x01; + data[1] = mode; + data[2] = off_timer >> 8; + data[3] = off_timer & 0xFF; + data[4] = 0x00; + data[5] = 0x00; + data[6] = 0x00; + } else if (!strcmp(ldata->ldev.name, "amber")) { + data[0] = 0x02; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + data[4] = mode; + data[5] = off_timer >> 8; + data[6] = off_timer & 0xFF; + } else if (!strcmp(ldata->ldev.name, "blue")) { + data[0] = 0x04; + data[1] = mode; + data[2] = off_timer >> 8; + data[3] = off_timer & 0xFF; + data[4] = 0x00; + data[5] = 0x00; + data[6] = 0x00; + } + + ret = microp_i2c_write(MICROP_I2C_WCMD_LED_MODE, data, 7); + if (ret == 0) { + mutex_lock(&ldata->led_data_mutex); + if (mode > 1) + ldata->blink = mode; + ldata->mode = mode; + mutex_unlock(&ldata->led_data_mutex); + } + return ret; +} + +static void microp_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct microp_led_data *ldata; + unsigned long flags; + int ret; + uint8_t mode; + + ldata = container_of(led_cdev, struct microp_led_data, ldev); + + if (brightness > 255) + brightness = 255; + led_cdev->brightness = brightness; + + spin_lock_irqsave(&ldata->brightness_lock, flags); + ldata->brightness = brightness; + spin_unlock_irqrestore(&ldata->brightness_lock, flags); + + if (brightness) + mode = 1; + else + mode = 0; + + ret = microp_write_led_mode(led_cdev, mode, 0xffff); + if (ret) + pr_err("%s: led_brightness_set failed to set mode\n", __func__); +} + +static void microp_led_jogball_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct microp_led_data *ldata; + unsigned long flags; + uint8_t data[3] = {0, 0, 0}; + int ret = 0; + + ldata = container_of(led_cdev, struct microp_led_data, ldev); + + spin_lock_irqsave(&ldata->brightness_lock, flags); + ldata->brightness = brightness; + spin_unlock_irqrestore(&ldata->brightness_lock, flags); + + switch (brightness) { + case 0: + data[0] = 0; + break; + case 1: + data[0] = 3; + data[1] = data[2] = 0xFF; + break; + case 3: + data[0] = 1; + data[1] = data[2] = 0xFF; + break; + case 7: + data[0] = 2; + data[1] = 0; + data[2] = 60; + break; + default: + pr_warning("%s: unknown value: %d\n", __func__, brightness); + break; + } + ret = microp_i2c_write(MICROP_I2C_WCMD_JOGBALL_LED_MODE, data, 3); + if (ret < 0) + pr_err("%s failed on set jogball mode:0x%2.2X\n", __func__, data[0]); +} + +static void microp_led_mobeam_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + ; +} + +static void microp_led_wimax_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct microp_led_data *ldata; + unsigned long flags; + uint8_t data[3] = {0, 0, 0}; + int ret = 0; + + ldata = container_of(led_cdev, struct microp_led_data, ldev); + + spin_lock_irqsave(&ldata->brightness_lock, flags); + ldata->brightness = brightness; + spin_unlock_irqrestore(&ldata->brightness_lock, flags); + + switch (brightness) { + case 0: + data[0] = 0; + break; + case 1: + case 2: + case 3: + case 4: + case 5: + case 129: + case 130: + case 131: + data[0] = brightness; + data[1] = data[2] = 0xFF; + break; + default: + pr_warning("%s: unknown value: %d\n", __func__, brightness); + break; + } + + ret = microp_i2c_write(MICROP_I2C_WCMD_JOGBALL_LED_MODE, data, 3); + if (ret < 0) + pr_err("%s failed on set wimax mode:0x%2.2X\n", __func__, data[0]); +} + +static void microp_led_gpo_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct microp_led_data *ldata; + unsigned long flags; + uint8_t enable, addr, data[3] = {0, 0, 0}; + int ret = 0; + + ldata = container_of(led_cdev, struct microp_led_data, ldev); + + spin_lock_irqsave(&ldata->brightness_lock, flags); + ldata->brightness = brightness; + spin_unlock_irqrestore(&ldata->brightness_lock, flags); + + enable = brightness ? 1 : 0; + if (enable) + addr = MICROP_I2C_WCMD_GPO_LED_STATUS_EN; + else + addr = MICROP_I2C_WCMD_GPO_LED_STATUS_DIS; + data[0] = ldata->led_config->mask_w[0]; + data[1] = ldata->led_config->mask_w[1]; + data[2] = ldata->led_config->mask_w[2]; + ret = microp_i2c_write(addr, data, 3);; + if (ret < 0) + pr_err("%s failed on set gpo led mode:%d\n", __func__, brightness); +} + +static void microp_led_pwm_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct microp_led_data *ldata; + unsigned long flags; + uint8_t data[4] = {0, 0, 0, 0}; + int ret = 0; + uint8_t value; + + ldata = container_of(led_cdev, struct microp_led_data, ldev); + + spin_lock_irqsave(&ldata->brightness_lock, flags); + ldata->brightness = brightness; + spin_unlock_irqrestore(&ldata->brightness_lock, flags); + + value = brightness >= 255 ? 0x20 : 0; + + data[0] = ldata->led_config->fade_time; + if (brightness) + data[1] = ldata->led_config->init_value ? + ldata->led_config->init_value : + brightness; + else + data[1] = 0x00; + data[2] = ldata->led_config->led_pin >> 8; + data[3] = ldata->led_config->led_pin; + + ret = microp_i2c_write(MICROP_I2C_WCMD_LED_PWM, data, 4); + if (ret < 0) + pr_err("%s failed on set pwm led mode:0x%2.2X\n", __func__, data[1]); +} + +static ssize_t microp_led_blink_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + int ret; + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + + mutex_lock(&ldata->led_data_mutex); + ret = sprintf(buf, "%d\n", ldata->blink ? ldata->blink - 1 : 0); + mutex_unlock(&ldata->led_data_mutex); + + return ret; +} + +static ssize_t microp_led_blink_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + int val, ret; + uint8_t mode; + + val = -1; + sscanf(buf, "%u", &val); + if (val < 0 || val > 255) + return -EINVAL; + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + + mutex_lock(&ldata->led_data_mutex); + switch (val) { + case 0: /* stop flashing */ + ldata->blink = 0; + if (led_cdev->brightness) + mode = 1; + else + mode = 0; + break; + case 1: + case 2: + case 3: + mode = val + 1; + break; + case 4: + if (!strcmp(ldata->ldev.name, "amber")) { + mode = val + 1; + break; + } + default: + mutex_unlock(&ldata->led_data_mutex); + return -EINVAL; + } + mutex_unlock(&ldata->led_data_mutex); + + ret = microp_write_led_mode(led_cdev, mode, 0xffff); + if (ret) + pr_err("%s:%s set blink failed\n", __func__, led_cdev->name); + + return count; +} + +static DEVICE_ATTR(blink, 0644, microp_led_blink_show, + microp_led_blink_store); + +static ssize_t microp_led_off_timer_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + uint8_t data[2]; + int ret, offtime; + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + + dev_dbg(dev, "Getting %s remaining time\n", led_cdev->name); + + if (!strcmp(ldata->ldev.name, "green")) + ret = microp_i2c_read(MICROP_I2C_RCMD_GREEN_LED_REMAIN_TIME, data, 2); + else if (!strcmp(ldata->ldev.name, "amber")) + ret = microp_i2c_read(MICROP_I2C_RCMD_AMBER_LED_REMAIN_TIME, data, 2); + else if (!strcmp(ldata->ldev.name, "blue")) + ret = microp_i2c_read(MICROP_I2C_RCMD_BLUE_LED_REMAIN_TIME, data, 2); + else { + pr_err("%s: Unknown led %s\n", __func__, ldata->ldev.name); + return -EINVAL; + } + + if (ret) + pr_err("%s: %s get off_timer failed\n", __func__, led_cdev->name); + + offtime = (int)((data[1] | data[0] << 8) * 2); + + ret = sprintf(buf, "Time remains %d:%d\n", offtime / 60, offtime % 60); + return ret; +} + +static ssize_t microp_led_off_timer_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + int min, sec, ret; + uint16_t off_timer; + + min = -1; + sec = -1; + sscanf(buf, "%d %d", &min, &sec); + + if (min < 0 || min > 255) + return -EINVAL; + if (sec < 0 || sec > 255) + return -EINVAL; + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + + dev_dbg(dev, "Setting %s off_timer to %d min %d sec\n", + led_cdev->name, min, sec); + + if (!min && !sec) + off_timer = 0xFFFF; + else + off_timer = (min * 60 + sec) / 2; + + ret = microp_write_led_mode(led_cdev, ldata->mode, off_timer); + if (ret) + pr_err("%s: %s set off_timer %d min %d sec failed\n", + __func__, led_cdev->name, min, sec); + + return count; +} + +static DEVICE_ATTR(off_timer, 0644, microp_led_off_timer_show, + microp_led_off_timer_store); + +static ssize_t microp_jogball_color_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + struct i2c_client *client; + int rpwm, gpwm, bpwm, ret; + uint8_t data[4]; + + rpwm = -1; + gpwm = -1; + bpwm = -1; + sscanf(buf, "%d %d %d", &rpwm, &gpwm, &bpwm); + + if (rpwm < 0 || rpwm > 255) + return -EINVAL; + if (gpwm < 0 || gpwm > 255) + return -EINVAL; + if (bpwm < 0 || bpwm > 255) + return -EINVAL; + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + client = to_i2c_client(dev->parent); + + dev_dbg(&client->dev, "Setting %s color to R=%d, G=%d, B=%d\n", + led_cdev->name, rpwm, gpwm, bpwm); + + data[0] = rpwm; + data[1] = gpwm; + data[2] = bpwm; + data[3] = 0x00; + + ret = microp_i2c_write(MICROP_I2C_WCMD_JOGBALL_LED_PWM_SET, data, 4); + if (ret) { + dev_err(&client->dev, "%s set color R=%d G=%d B=%d failed\n", + led_cdev->name, rpwm, gpwm, bpwm); + } + return count; +} + +static DEVICE_ATTR(color, 0644, NULL, microp_jogball_color_store); + +static ssize_t microp_mobeam_read_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint8_t data[2] = {0, 0}; + int ret; +pr_info("%s\n", __func__); + ret = microp_i2c_read(MICROP_I2C_RCMD_MOBEAM_STATUS, data, 2); + if (ret == 0) + ret = sprintf(buf, "%d %d\n", data[0], data[1]); + else + data[0] = data[1] = 0; + + return ret; +} +static DEVICE_ATTR(read_status, 0444, microp_mobeam_read_status_show, NULL); + +static ssize_t microp_mobeam_download_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int i, ret, num; + uint8_t data[73] ; /* Size of cbitstream array MAX 73 bytes. */ + +pr_info("%s\n", __func__); + memset(data, 0x00, sizeof(data)); + + num = *(buf++); + if (num < 0 || num > 73) + return -EINVAL; +pr_info("%s: count=%d\n", __func__, count); + for (i = 0; i < count; i++) + data[i] = *(buf + i); + + ret = microp_i2c_write(MICROP_I2C_WCMD_MOBEAM_DL, data, num); + if (ret != 0) + count = 0; + + return count; +} +static DEVICE_ATTR(data_download, 0644, NULL, microp_mobeam_download_store); + +static ssize_t microp_mobeam_send_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t data[2]; + unsigned char num; + int ret; + +pr_info("%s\n", __func__); + num = *buf; + + if (num < 0 || num > 73) + return -EINVAL; + + data[0] = (uint8_t)num; + data[1] = 0; + ret = microp_i2c_write(MICROP_I2C_WCMD_MOBEAM_SEND, data, 2); + if (ret != 0) + count = 0; + + return count; +} +static DEVICE_ATTR(send_data, 0644, NULL, microp_mobeam_send_store); + +static uint8_t leds_data[7]; + +static ssize_t microp_mobeam_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val; + uint8_t data[7]; + int ret; + +pr_info("%s\n", __func__); + val = -1; + sscanf(buf, "%u", &val); + if (val != 1 && val != 0) + return -EINVAL; + + if (val) { + ret = microp_i2c_read(MICROP_I2C_RCMD_LED_STATUS, leds_data, 7); + data[0] = 0x03; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + data[4] = 0x00; + data[5] = 0x00; + data[6] = 0x00; + ret = microp_i2c_write(MICROP_I2C_WCMD_LED_MODE, data, 7); + pr_info("%s: enabled\n", __func__); + msleep(150); + microp_mobeam_enable(1); + } else { + microp_mobeam_enable(0); + ret = microp_i2c_write(MICROP_I2C_WCMD_LED_MODE, leds_data, 7); + } + return count; +} +static DEVICE_ATTR(mobeam_enable, 0644, NULL, microp_mobeam_enable_store); + +static ssize_t microp_mobeam_stop_led(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int val; + uint8_t data[2]; + +pr_info("%s\n", __func__); + val = -1; + sscanf(buf, "%u", &val); + + data[0] = 0x00; + data[1] = 0x01; + + + return count; +} +static DEVICE_ATTR(stop_led, 0644, NULL, microp_mobeam_stop_led); + + +static int microp_led_probe(struct platform_device *pdev) +{ + struct microp_led_platform_data *pdata; + struct microp_led_data *ldata; + int i, ret; + + pdata = pdev->dev.platform_data; + if (pdata == NULL) { + pr_err("%s: platform data is NULL\n", __func__); + return -ENODEV; + } + + ldata = kzalloc(sizeof(struct microp_led_data) + * pdata->num_leds, GFP_KERNEL); + if (!ldata && pdata->num_leds) { + ret = -ENOMEM; + pr_err("%s: failed on allocate ldata\n", __func__); + goto err_exit; + } + + dev_set_drvdata(&pdev->dev, ldata); + for (i = 0; i < pdata->num_leds; i++) { + ldata[i].led_config = pdata->led_config + i; + ldata[i].ldev.name = pdata->led_config[i].name; + if (pdata->led_config[i].type == LED_JOGBALL) + ldata[i].ldev.brightness_set + = microp_led_jogball_brightness_set; + else if (pdata->led_config[i].type == LED_GPO) + ldata[i].ldev.brightness_set + = microp_led_gpo_brightness_set; + else if (pdata->led_config[i].type == LED_PWM) + ldata[i].ldev.brightness_set + = microp_led_pwm_brightness_set; + else if (pdata->led_config[i].type == LED_RGB) + ldata[i].ldev.brightness_set + = microp_led_brightness_set; + else if (pdata->led_config[i].type == LED_WIMAX) + ldata[i].ldev.brightness_set + = microp_led_wimax_brightness_set; + else if (pdata->led_config[i].type == LED_MOBEAM) + ldata[i].ldev.brightness_set + = microp_led_mobeam_brightness_set; + + mutex_init(&ldata[i].led_data_mutex); + spin_lock_init(&ldata[i].brightness_lock); + ret = led_classdev_register(&pdev->dev, &ldata[i].ldev); + if (ret < 0) { + pr_err("%s: failed on led_classdev_register [%s]\n", + __func__, ldata[i].ldev.name); + goto err_register_led_cdev; + } + } + + for (i = 0; i < pdata->num_leds; i++) { + if (pdata->led_config[i].type != LED_RGB) + continue; + ret = device_create_file(ldata[i].ldev.dev, &dev_attr_blink); + if (ret < 0) { + pr_err("%s: failed on create attr blink [%d]\n", __func__, i); + goto err_register_attr_blink; + } + } + + for (i = 0; i < pdata->num_leds; i++) { + if (pdata->led_config[i].type != LED_RGB) + continue; + ret = device_create_file(ldata[i].ldev.dev, &dev_attr_off_timer); + if (ret < 0) { + pr_err("%s: failed on create attr off_timer [%d]\n", + __func__, i); + goto err_register_attr_off_timer; + } + } + + for (i = 0; i < pdata->num_leds; i++) { + if (pdata->led_config[i].type != LED_JOGBALL) + continue; + ret = device_create_file(ldata[i].ldev.dev, &dev_attr_color); + if (ret < 0) { + pr_err("%s: failed on create attr jogball color\n", + __func__); + goto err_register_attr_color; + } else + break; + } + + for (i = 0; i < pdata->num_leds; i++) { + if (pdata->led_config[i].type != LED_MOBEAM) + continue; + ret = device_create_file(ldata[i].ldev.dev, + &dev_attr_data_download); + if (ret < 0) { + pr_err("%s: failed on create attr data download\n", + __func__); + goto err_create_mo_download_attr_file; + } + + ret = device_create_file(ldata[i].ldev.dev, + &dev_attr_send_data); + if (ret < 0) { + pr_err("%s: failed on create attr send data\n", + __func__); + goto err_create_mo_send_attr_file; + } + + ret = device_create_file(ldata[i].ldev.dev, + &dev_attr_read_status); + if (ret < 0) { + pr_err("%s: failed on create attr read status\n", + __func__); + goto err_create_mo_read_attr_file; + } + + ret = device_create_file(ldata[i].ldev.dev, + &dev_attr_mobeam_enable); + if (ret < 0) { + pr_err("%s: failed on create attr enable\n", + __func__); + goto err_create_mo_enable_attr_file; + } + + ret = device_create_file(ldata[i].ldev.dev, + &dev_attr_stop_led); + if (ret < 0) { + pr_err("%s: failed on create attr stop led\n", + __func__); + goto err_create_mo_stop_attr_file; + } + break; + } + + pr_info("%s: succeeded\n", __func__); + return 0; + +err_create_mo_stop_attr_file: + device_remove_file(ldata[i].ldev.dev, &dev_attr_mobeam_enable); +err_create_mo_enable_attr_file: + device_remove_file(ldata[i].ldev.dev, &dev_attr_read_status); +err_create_mo_read_attr_file: + device_remove_file(ldata[i].ldev.dev, &dev_attr_send_data); +err_create_mo_send_attr_file: + device_remove_file(ldata[i].ldev.dev, &dev_attr_data_download); +err_create_mo_download_attr_file: + i = pdata->num_leds; +err_register_attr_color: + for (i--; i >= 0; i--) { + if (pdata->led_config[i].type != LED_JOGBALL) + continue; + device_remove_file(ldata[i].ldev.dev, &dev_attr_color); + } + i = pdata->num_leds; +err_register_attr_off_timer: + for (i--; i >= 0; i--) { + if (pdata->led_config[i].type != LED_RGB) + continue; + device_remove_file(ldata[i].ldev.dev, + &dev_attr_off_timer); + } + i = pdata->num_leds; +err_register_attr_blink: + for (i--; i >= 0; i--) { + if (pdata->led_config[i].type != LED_RGB) + continue; + device_remove_file(ldata[i].ldev.dev, + &dev_attr_blink); + } + i = pdata->num_leds; + +err_register_led_cdev: + for (i--; i >= 0; i--) + led_classdev_unregister(&ldata[i].ldev); + kfree(ldata); + +err_exit: + return ret; +} + +static int __devexit microp_led_remove(struct platform_device *pdev) +{ + struct microp_led_platform_data *pdata; + struct microp_led_data *ldata; + int i; + + pdata = pdev->dev.platform_data; + ldata = platform_get_drvdata(pdev); + + for (i = 0; i < pdata->num_leds; i++) { + led_classdev_unregister(&ldata[i].ldev); + if (pdata->led_config[i].type == LED_RGB) { + device_remove_file(ldata[i].ldev.dev, + &dev_attr_off_timer); + device_remove_file(ldata[i].ldev.dev, + &dev_attr_blink); + } else if (pdata->led_config[i].type == LED_JOGBALL) + device_remove_file(ldata[i].ldev.dev, &dev_attr_color); + } + kfree(ldata); + + return 0; +} + +static struct platform_driver microp_led_driver = { + .probe = microp_led_probe, + .remove = __devexit_p(microp_led_remove), + .driver = { + .name = "leds-microp", + .owner = THIS_MODULE, + }, +}; + +int __init microp_led_init(void) +{ + return platform_driver_register(µp_led_driver); +} + +void microp_led_exit(void) +{ + platform_driver_unregister(µp_led_driver); +} + +module_init(microp_led_init); +module_exit(microp_led_exit); + +MODULE_DESCRIPTION("Atmega MicroP led driver"); +MODULE_LICENSE("GPL"); +#endif /* end of #ifdef CONFIG_MICROP_COMMON*/ + From 125a4a1f026f2077d808ccd447567bdbdf20c250 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 2 Nov 2010 13:20:02 -0400 Subject: [PATCH 1031/2556] usb: gadget: Fix a bunch of USB bugs on Supersonic Removed redundant callback in UMS driver that was causing it to remain disabled after the first disconnect. Added proper PHY reset. Fixed missing carkit switch device. Enabled UART switch for WiMAX. Change-Id: I0a2c36787944ec67cce27c498ec175002be56eea --- arch/arm/mach-msm/board-supersonic.c | 2 +- arch/arm/mach-msm/devices-qsd8x50.c | 212 +++++++++++++++++- arch/arm/mach-msm/include/mach/board_htc.h | 15 ++ arch/arm/mach-msm/include/mach/msm_hsusb.h | 12 +- arch/arm/mach-msm/include/mach/msm_hsusb_hw.h | 21 ++ 5 files changed, 259 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index 4cf9415b5a256..28812bb4c7078 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -319,7 +319,7 @@ extern void msm_hsusb_8x50_phy_reset(void); static struct msm_hsusb_platform_data msm_hsusb_pdata = { .phy_init_seq = supersonic_phy_init_seq, -// .phy_reset = msm_hsusb_8x50_phy_reset, + .phy_reset = msm_hsusb_8x50_phy_reset, .usb_id_pin_gpio = SUPERSONIC_GPIO_USB_ID_PIN, .accessory_detect = 1, /* detect by ID pin gpio */ .usb_uart_switch = supersonic_uart_usb_switch, diff --git a/arch/arm/mach-msm/devices-qsd8x50.c b/arch/arm/mach-msm/devices-qsd8x50.c index 078de83d7fd0d..05079116c031c 100644 --- a/arch/arm/mach-msm/devices-qsd8x50.c +++ b/arch/arm/mach-msm/devices-qsd8x50.c @@ -17,7 +17,7 @@ #include #include #include - +#include #include #include #include @@ -33,6 +33,216 @@ #include +#include +#include +#include +#ifdef CONFIG_PMIC8058 +#include +#endif + +int usb_phy_error; + +#define HSUSB_API_INIT_PHY_PROC 2 +#define HSUSB_API_PROG 0x30000064 +#define HSUSB_API_VERS MSM_RPC_VERS(1, 1) + +static void *usb_base; +#define MSM_USB_BASE ((unsigned)usb_base) +static unsigned ulpi_read(void __iomem *usb_base, unsigned reg) +{ + unsigned timeout = 100000; + + /* initiate read operation */ + writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), + USB_ULPI_VIEWPORT); + + /* wait for completion */ + while ((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout)) + cpu_relax(); + + if (timeout == 0) { + printk(KERN_ERR "ulpi_read: timeout %08x\n", + readl(USB_ULPI_VIEWPORT)); + return 0xffffffff; + } + return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT)); +} + +static int ulpi_write(void __iomem *usb_base, unsigned val, unsigned reg) +{ + unsigned timeout = 10000; + + /* initiate write operation */ + writel(ULPI_RUN | ULPI_WRITE | + ULPI_ADDR(reg) | ULPI_DATA(val), + USB_ULPI_VIEWPORT); + + /* wait for completion */ + while ((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout)) + cpu_relax(); + + if (timeout == 0) { + printk(KERN_ERR "ulpi_write: timeout\n"); + return -1; + } + + return 0; +} + +#define CLKRGM_APPS_RESET_USBH 37 +#define CLKRGM_APPS_RESET_USB_PHY 34 +static void msm_hsusb_apps_reset_link(int reset) +{ + int ret; + unsigned usb_id = CLKRGM_APPS_RESET_USBH; + + if (reset) + ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_ASSERT, + &usb_id, NULL); + else + ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_DEASSERT, + &usb_id, NULL); + if (ret) + printk(KERN_INFO "%s: Cannot set reset to %d (%d)\n", + __func__, reset, ret); +} + +static void msm_hsusb_apps_reset_phy(void) +{ + int ret; + unsigned usb_phy_id = CLKRGM_APPS_RESET_USB_PHY; + + ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_ASSERT, + &usb_phy_id, NULL); + if (ret) { + printk(KERN_INFO "%s: Cannot assert (%d)\n", __func__, ret); + return; + } + msleep(1); + ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_DEASSERT, + &usb_phy_id, NULL); + if (ret) { + printk(KERN_INFO "%s: Cannot assert (%d)\n", __func__, ret); + return; + } +} + +#define ULPI_VERIFY_MAX_LOOP_COUNT 3 +static int msm_hsusb_phy_verify_access(void __iomem *usb_base) +{ + int temp; + + for (temp = 0; temp < ULPI_VERIFY_MAX_LOOP_COUNT; temp++) { + if (ulpi_read(usb_base, ULPI_DEBUG) != (unsigned)-1) + break; + msm_hsusb_apps_reset_phy(); + } + + if (temp == ULPI_VERIFY_MAX_LOOP_COUNT) { + pr_err("%s: ulpi read failed for %d times\n", + __func__, ULPI_VERIFY_MAX_LOOP_COUNT); + return -1; + } + + return 0; +} + +static unsigned msm_hsusb_ulpi_read_with_reset(void __iomem *usb_base, unsigned reg) +{ + int temp; + unsigned res; + + for (temp = 0; temp < ULPI_VERIFY_MAX_LOOP_COUNT; temp++) { + res = ulpi_read(usb_base, reg); + if (res != -1) + return res; + msm_hsusb_apps_reset_phy(); + } + + pr_err("%s: ulpi read failed for %d times\n", + __func__, ULPI_VERIFY_MAX_LOOP_COUNT); + + return -1; +} + +static int msm_hsusb_ulpi_write_with_reset(void __iomem *usb_base, + unsigned val, unsigned reg) +{ + int temp; + int res; + + for (temp = 0; temp < ULPI_VERIFY_MAX_LOOP_COUNT; temp++) { + res = ulpi_write(usb_base, val, reg); + if (!res) + return 0; + msm_hsusb_apps_reset_phy(); + } + + pr_err("%s: ulpi write failed for %d times\n", + __func__, ULPI_VERIFY_MAX_LOOP_COUNT); + return -1; +} + +static int msm_hsusb_phy_caliberate(void __iomem *usb_base) +{ + int ret; + unsigned res; + + ret = msm_hsusb_phy_verify_access(usb_base); + if (ret) + return -ETIMEDOUT; + + res = msm_hsusb_ulpi_read_with_reset(usb_base, ULPI_FUNC_CTRL_CLR); + if (res == -1) + return -ETIMEDOUT; + + res = msm_hsusb_ulpi_write_with_reset(usb_base, + res | ULPI_SUSPENDM, + ULPI_FUNC_CTRL_CLR); + if (res) + return -ETIMEDOUT; + + msm_hsusb_apps_reset_phy(); + + return msm_hsusb_phy_verify_access(usb_base); +} + +#define USB_LINK_RESET_TIMEOUT (msecs_to_jiffies(10)) +void msm_hsusb_8x50_phy_reset(void) +{ + u32 temp; + unsigned long timeout; + printk(KERN_INFO "msm_hsusb_phy_reset\n"); + usb_base = ioremap(MSM_HSUSB_PHYS, 4096); + + msm_hsusb_apps_reset_link(1); + msm_hsusb_apps_reset_phy(); + msm_hsusb_apps_reset_link(0); + + /* select ULPI phy */ + temp = (readl(USB_PORTSC) & ~PORTSC_PTS_MASK); + writel(temp | PORTSC_PTS_ULPI, USB_PORTSC); + + if (msm_hsusb_phy_caliberate(usb_base)) { + usb_phy_error = 1; + return; + } + + /* soft reset phy */ + writel(USBCMD_RESET, USB_USBCMD); + timeout = jiffies + USB_LINK_RESET_TIMEOUT; + while (readl(USB_USBCMD) & USBCMD_RESET) { + if (time_after(jiffies, timeout)) { + pr_err("usb link reset timeout\n"); + break; + } + msleep(1); + } + usb_phy_error = 0; + + return; +} + static struct resource resources_uart1[] = { { .start = INT_UART1, diff --git a/arch/arm/mach-msm/include/mach/board_htc.h b/arch/arm/mach-msm/include/mach/board_htc.h index 994accd56272f..14ee1ec40d773 100644 --- a/arch/arm/mach-msm/include/mach/board_htc.h +++ b/arch/arm/mach-msm/include/mach/board_htc.h @@ -48,6 +48,10 @@ enum { /* common init routines for use by arch/arm/mach-msm/board-*.c */ void __init msm_add_usb_devices(void (*phy_reset) (void)); +void __init msm_add_usb_id_pin_function(void (*config_usb_id_gpios)(bool enable)); +void __init msm_add_usb_id_pin_gpio(int usb_id_pin_io); +void __init msm_enable_car_kit_detect(bool enable); +void __init msm_change_usb_id(__u16 vendor_id, __u16 product_id); void __init msm_add_mem_devices(struct msm_pmem_setting *setting); void __init msm_init_pmic_vibrator(void); @@ -65,4 +69,15 @@ void notify_usb_connected(int online); char *board_serialno(void); +/* + * Obviously, we need these in all project. + * To export a function to get these is too lousy. + * Each BSP can include board.h to get these. + * + * Jay, 15/May/09' + * */ +extern int panel_type; +extern unsigned engineer_id; +extern int usb_phy_error; + #endif diff --git a/arch/arm/mach-msm/include/mach/msm_hsusb.h b/arch/arm/mach-msm/include/mach/msm_hsusb.h index 93fb9a4727ad9..5ed4b459889c2 100644 --- a/arch/arm/mach-msm/include/mach/msm_hsusb.h +++ b/arch/arm/mach-msm/include/mach/msm_hsusb.h @@ -24,15 +24,25 @@ struct msm_hsusb_platform_data { /* hard reset the ULPI PHY */ void (*phy_reset)(void); + void (*phy_shutdown)(void); /* (de)assert the reset to the usb core */ void (*hw_reset)(bool enable); /* for notification when USB is connected or disconnected */ void (*usb_connected)(int); - + /* 1 : uart, 0 : usb */ + void (*usb_uart_switch)(int); + void (*config_usb_id_gpios)(bool enable); /* val, reg pairs terminated by -1 */ int *phy_init_seq; + + char *serial_number; + int usb_id_pin_gpio; + bool enable_car_kit_detect; + __u8 accessory_detect; }; +int usb_get_connect_type(void); + #endif diff --git a/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h b/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h index a2035d936598b..d7dae7a1f17e1 100644 --- a/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h +++ b/arch/arm/mach-msm/include/mach/msm_hsusb_hw.h @@ -204,6 +204,24 @@ struct ept_queue_item { #define PORTSC_FPR (1 << 6) #define PORTSC_SUSP (1 << 7) +#define OTGSC_BSVIE (1 << 27) /* R/W - BSV Interrupt Enable */ +#define OTGSC_DPIE (1 << 30) /* R/W - DataPulse Interrupt Enable */ +#define OTGSC_1MSE (1 << 29) /* R/W - 1ms Interrupt Enable */ +#define OTGSC_BSEIE (1 << 28) /* R/W - BSE Interrupt Enable */ +#define OTGSC_ASVIE (1 << 26) /* R/W - ASV Interrupt Enable */ +#define OTGSC_ASEIE (1 << 25) /* R/W - ASE Interrupt Enable */ +#define OTGSC_IDIE (1 << 24) /* R/W - ID Interrupt Enable */ +#define OTGSC_BSVIS (1 << 19) /* R/W - BSV Interrupt Status */ +#define OTGSC_IDPU (1 << 5) +#define OTGSC_ID (1 << 8) +#define OTGSC_IDIS (1 << 16) +#define B_SESSION_VALID (1 << 11) +#define OTGSC_INTR_MASK (OTGSC_BSVIE | OTGSC_DPIE | OTGSC_1MSE | \ + OTGSC_BSEIE | OTGSC_ASVIE | OTGSC_ASEIE | \ + OTGSC_IDIE) +#define OTGSC_INTR_STS_MASK (0x7f << 16) +#define CURRENT_CONNECT_STATUS (1 << 0) + /* test mode support */ #define J_TEST (0x0100) #define K_TEST (0x0200) @@ -226,4 +244,7 @@ struct ept_queue_item { #define PORTSC_LS (3 << 10) /* Read - Port's Line status */ #define PORTSC_PHCD (1 << 23) /* phy suspend mode */ +#define ULPI_DEBUG 0x15 +#define ULPI_SUSPENDM (1 << 6) + #endif /* __LINUX_USB_GADGET_MSM72K_UDC_H__ */ From ed8611e7f202837b56f6df572daad982d79dc38c Mon Sep 17 00:00:00 2001 From: toastcfh Date: Tue, 2 Nov 2010 23:20:27 -0400 Subject: [PATCH 1032/2556] Wimax/SQN: added SQN module support --- drivers/net/wimax/Kconfig | 1 + drivers/net/wimax/Makefile | 1 + drivers/net/wimax/SQN/Kconfig | 12 + drivers/net/wimax/SQN/Makefile | 116 ++ drivers/net/wimax/SQN/msg.h | 119 ++ drivers/net/wimax/SQN/sdio-driver.c | 476 ++++++++ drivers/net/wimax/SQN/sdio-driver.h | 31 + drivers/net/wimax/SQN/sdio-fw.c | 691 +++++++++++ drivers/net/wimax/SQN/sdio-fw.h | 24 + drivers/net/wimax/SQN/sdio-netdev.h | 50 + drivers/net/wimax/SQN/sdio-pm.c | 838 +++++++++++++ drivers/net/wimax/SQN/sdio-pm.h | 35 + drivers/net/wimax/SQN/sdio-sqn.h | 166 +++ drivers/net/wimax/SQN/sdio.c | 1696 +++++++++++++++++++++++++++ drivers/net/wimax/SQN/sdio.h | 18 + drivers/net/wimax/SQN/thp.c | 759 ++++++++++++ drivers/net/wimax/SQN/thp.h | 56 + drivers/net/wimax/SQN/thp_ioctl.h | 21 + drivers/net/wimax/SQN/version.h | 27 + 19 files changed, 5137 insertions(+) create mode 100644 drivers/net/wimax/SQN/Kconfig create mode 100644 drivers/net/wimax/SQN/Makefile create mode 100644 drivers/net/wimax/SQN/msg.h create mode 100644 drivers/net/wimax/SQN/sdio-driver.c create mode 100644 drivers/net/wimax/SQN/sdio-driver.h create mode 100644 drivers/net/wimax/SQN/sdio-fw.c create mode 100644 drivers/net/wimax/SQN/sdio-fw.h create mode 100644 drivers/net/wimax/SQN/sdio-netdev.h create mode 100644 drivers/net/wimax/SQN/sdio-pm.c create mode 100644 drivers/net/wimax/SQN/sdio-pm.h create mode 100644 drivers/net/wimax/SQN/sdio-sqn.h create mode 100644 drivers/net/wimax/SQN/sdio.c create mode 100644 drivers/net/wimax/SQN/sdio.h create mode 100644 drivers/net/wimax/SQN/thp.c create mode 100644 drivers/net/wimax/SQN/thp.h create mode 100644 drivers/net/wimax/SQN/thp_ioctl.h create mode 100644 drivers/net/wimax/SQN/version.h diff --git a/drivers/net/wimax/Kconfig b/drivers/net/wimax/Kconfig index 565018ec1e3ba..58383bf2c9ce5 100644 --- a/drivers/net/wimax/Kconfig +++ b/drivers/net/wimax/Kconfig @@ -11,6 +11,7 @@ if WIMAX menu "WiMAX Wireless Broadband devices" source "drivers/net/wimax/i2400m/Kconfig" +source "drivers/net/wimax/SQN/Kconfig" endmenu diff --git a/drivers/net/wimax/Makefile b/drivers/net/wimax/Makefile index 992bc02bc016f..64339c6efd709 100644 --- a/drivers/net/wimax/Makefile +++ b/drivers/net/wimax/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_WIMAX_I2400M) += i2400m/ +obj-$(CONFIG_WIMAX_SQN) += SQN/ # (from Sam Ravnborg) force kbuild to create built-in.o obj- := dummy.o diff --git a/drivers/net/wimax/SQN/Kconfig b/drivers/net/wimax/SQN/Kconfig new file mode 100644 index 0000000000000..0e2870ce635c8 --- /dev/null +++ b/drivers/net/wimax/SQN/Kconfig @@ -0,0 +1,12 @@ +config WIMAX_SQN + tristate "WiMAX SQN1210 cards support" + depends on WIMAX && MMC + ---help--- + This module adds support for wimax adapters based on + SQN1120 chipset. + + This driver uses the kernel's wimax subsystem. + + If you choose to build a module, it'll be called sequans_sdio. Say M if + unsure. + diff --git a/drivers/net/wimax/SQN/Makefile b/drivers/net/wimax/SQN/Makefile new file mode 100644 index 0000000000000..86228021566cf --- /dev/null +++ b/drivers/net/wimax/SQN/Makefile @@ -0,0 +1,116 @@ +# +# This is part of the Sequans SQN1130 driver. +# Copyright 2008 SEQUANS Communications +# Written by Andy Shevchenko , +# Dmitriy Chumak +# + +ifeq ($(KERNELRELEASE),) + +# The following two variables are used to build SDIO +# driver for foreign architecture, for example ARM, +# change them to meet your build environment +SQN_KERNEL_HEADERS_DIR := /usr/src/linux-headers-$(shell uname -r) +SQN_CROSS_COMPILER := arm-eabi- + + +help: + @echo -e "Available targets are: \n\n\ + \thelp -- print this help message\n\n\ + \tsdio_arm_release -- build release SDIO driver for ARM architecture\n\ + \tsdio_arm_debug -- build debug SDIO driver for ARM architecture\n\ + \tsdio -- build SDIO driver for x86 architecture\n\ + \tusb -- build USB driver\n\n\ + \tusb_debian_pkg -- make debian package for USB driver\n\ + \tusb_ubuntu_pkg -- make ubuntu package for USB driver\n\n\ + \tclean -- clean object files and binaries" + +sdio_arm_release: + $(MAKE) -C $(SQN_KERNEL_HEADERS_DIR) \ + ARCH=arm \ + CROSS_COMPILE=$(SQN_CROSS_COMPILER) \ + M=$(shell pwd) \ + CONFIG_USB_SQN='' \ + CONFIG_SDIO_SQN=m \ + SQN_TARGET=CONFIG_SDIO_SQN \ + +sdio_arm_debug: + $(MAKE) -C $(SQN_KERNEL_HEADERS_DIR) \ + ARCH=arm \ + CROSS_COMPILE=$(SQN_CROSS_COMPILER) \ + M=$(shell pwd) \ + CONFIG_USB_SQN='' \ + CONFIG_SDIO_SQN=m \ + SQN_TARGET=CONFIG_SDIO_SQN \ + EXTRA_CFLAGS='-DCONFIG_SDIO_SQN -DDEBUG -Wno-unused-function \ + -DSQN_DEBUG_TRACE -DSQN_DEBUG_DUMP -DSQN_DEBUG_LEVEL_INFO' + +sdio: + $(MAKE) -C $(SQN_KERNEL_HEADERS_DIR) \ + M=$(shell pwd) \ + CONFIG_USB_SQN='' \ + CONFIG_SDIO_SQN=m \ + SQN_TARGET=CONFIG_SDIO_SQN \ + +usb: + $(MAKE) -C $(SQN_KERNEL_HEADERS_DIR) \ + M=$(shell pwd) \ + CONFIG_USB_SQN=m \ + CONFIG_SDIO_SQN='' \ + SQN_TARGET=CONFIG_USB_SQN \ + +usb_debian_pkg: + @./build_deb.sh debian + +usb_ubuntu_pkg: + @./build_deb.sh ubuntu + +clean: + rm -vrf *.o *.ko .*.cmd *.mod.c .*.d modules.order Module.* .tmp_versions *~ + +else # KERNELRELEASE + +SQN_TARGET := SQN_SDIO +SQN_SDIO := m +SQN_USB := + +obj-$(SQN_SDIO) += sequans_sdio.o +obj-$(SQN_USB) += sequans_usb.o + +sequans_sdio-objs := sdio.o sdio-driver.o sdio-fw.o sdio-pm.o thp.o + +sequans_usb-objs := usb-driver.o thp.o + + +### Compile debug version of the module +#EXTRA_CFLAGS += -D$(SQN_TARGET) -DDEBUG -Wno-unused-function \ + -DSQN_DEBUG_TRACE -DSQN_DEBUG_DUMP -DSQN_DEBUG_LEVEL_INFO + +#EXTRA_CFLAGS += -D$(SQN_TARGET) -DDEBUG -Wno-unused-function \ + -DSQN_DEBUG_TRACE -DSQN_DEBUG_LEVEL_INFO + +#EXTRA_CFLAGS += -D$(SQN_TARGET) -DDEBUG -Wno-unused-function + + +### Compile release version of the module +EXTRA_CFLAGS += -D$(SQN_TARGET) -Wno-unused-function -Wno-unused-label \ + -Wno-unused-variable + + +### For TI kernel we need the following lines +### For other kernels we should comment them + +#EXTRA_CFLAGS += -DTI_KERNEL +#sequans_sdio-objs += sdio-ti.o + +### end of TI kernel specific options + + +### For ANDROID kernel we need the following lines +### For other kernels we should comment them + +EXTRA_CFLAGS += -DANDROID_KERNEL + +### end of HTC kernel specific options + +endif diff --git a/drivers/net/wimax/SQN/msg.h b/drivers/net/wimax/SQN/msg.h new file mode 100644 index 0000000000000..8f9331487554a --- /dev/null +++ b/drivers/net/wimax/SQN/msg.h @@ -0,0 +1,119 @@ +/* + * This is part of the Sequans SQN1130 driver. + * Copyright 2008 SEQUANS Communications + * Written by Andy Shevchenko , + * Dmitriy Chumak + * + * 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. + */ + +#ifndef _SQN_MSG_H +#define _SQN_MSG_H + +#include +#include +#include + +#include "version.h" + + +#define sqn_pr(level, fmt, arg...) \ +do { \ + char kthread_name[TASK_COMM_LEN] = { 0 }; \ + /* task_lock(current); */ \ + strncpy(kthread_name, current->comm \ + , sizeof(current->comm)); \ + /* task_unlock(current); */ \ + printk(level "%s: %s: %s: " fmt \ + , SQN_MODULE_NAME, kthread_name \ + , __func__ \ + , ##arg); \ +} while (0) + + +#if defined(DEBUG) + +#ifdef SQN_DEBUG_LEVEL_INFO +#define DEBUG_LEVEL KERN_INFO +#else +#define DEBUG_LEVEL KERN_DEBUG +#endif + +#define sqn_pr_dbg(fmt, arg...) sqn_pr(DEBUG_LEVEL, fmt, ##arg) + +#ifdef SQN_DEBUG_DUMP + +#define sqn_pr_dbg_dump(prefix, data, len) \ +do { \ + unsigned int i = 0; \ + unsigned int width = 16; \ + unsigned int len_ = (unsigned int)(len); \ + while (i < len_) { \ + if (i % width == 0) \ + printk(DEBUG_LEVEL "%s: %s: %04x ", \ + SQN_MODULE_NAME, (prefix), i); \ + printk("%02x ", ((unsigned char *)(data))[i++]);\ + if ((i % width == 0) || (i == len_)) \ + printk("\n"); \ + } \ +} while (0) + +#else /* !SQN_DEBUG_DUMP */ + +#define sqn_pr_dbg_dump(prefix, data, len) do {} while (0) + +#endif /* SQN_DEBUG_DUMP */ + +#ifdef SQN_DEBUG_TRACE + +#define sqn_pr_enter() sqn_pr_dbg("%s\n", "enter") +#define sqn_pr_leave() sqn_pr_dbg("%s\n", "leave") + +#else /* !SQN_DEBUG_TRACE */ + +#define sqn_pr_enter() do {} while (0) +#define sqn_pr_leave() do {} while (0) + +#endif /* SQN_DEBUG_TRACE */ + +#else /* !DEBUG */ + +#define sqn_pr_dbg(fmt, arg...) do {} while (0) +#define sqn_pr_dbg_dump(prefix, data, len) do {} while (0) + +#define sqn_pr_enter() do {} while (0) +#define sqn_pr_leave() do {} while (0) + +#endif /* DEBUG */ + + +#define sqn_pr_info(fmt, arg...) \ + pr_info("%s: " fmt, SQN_MODULE_NAME, ##arg) + +#define sqn_pr_warn(fmt, arg...) \ + pr_warning("%s: " fmt, SQN_MODULE_NAME, ##arg) + +#define sqn_pr_err(fmt, arg...) \ + pr_err("%s: " fmt, SQN_MODULE_NAME, ##arg) + + +#define sqn_pr_info_dump(prefix, data, len) \ +do { \ + unsigned int i = 0; \ + unsigned int width = 16; \ + unsigned int len_ = (unsigned int)(len); \ + while (i < len_) { \ + if (i % width == 0) \ + printk(KERN_INFO "%s: %s: %04x ", \ + SQN_MODULE_NAME, (prefix), i); \ + printk("%02x ", ((unsigned char *)(data))[i++]);\ + if ((i % width == 0) || (i == len_)) \ + printk("\n"); \ + } \ +} while (0) + + +#endif /* _SQN_MSG_H */ diff --git a/drivers/net/wimax/SQN/sdio-driver.c b/drivers/net/wimax/SQN/sdio-driver.c new file mode 100644 index 0000000000000..559d365dc3bdb --- /dev/null +++ b/drivers/net/wimax/SQN/sdio-driver.c @@ -0,0 +1,476 @@ +/* + * This is part of the Sequans SQN1130 driver. + * Copyright 2008 SEQUANS Communications + * Written by Andy Shevchenko , + * Dmitriy Chumak + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "sdio-netdev.h" +#include "version.h" +#include "msg.h" +#include "thp.h" +#include "sdio.h" +#include "sdio-pm.h" +#include "sdio-fw.h" +#include "sdio-driver.h" + +#define DRIVER_DEBUG 0 +#define SKB_DEBUG 0 +#define IGNORE_CARRIER_STATE 1 + +/*******************************************************************/ +/* Module parameter variables */ +/*******************************************************************/ + +/** firmware_name - specifies the name of firmware binary */ +char *firmware_name = SQN_DEFAULT_FW_NAME; + +/** + * load_firmware - boolean flag, controls whether firmware + * should be loaded or not + */ +int load_firmware = 1; + +bool drop_packet = false; + +module_param(firmware_name, charp, S_IRUGO); +module_param(load_firmware, bool, S_IRUGO); + +struct sqn_private *g_priv = 0; + +//reference sdio-driver.c +extern const uint8_t ss_macaddr[ETH_ALEN]; + +/*******************************************************************/ +/* Network interface functions */ +/*******************************************************************/ + +static int sqn_dev_open(struct net_device *dev) +{ + struct sqn_private *priv = netdev_priv(dev); + + sqn_pr_enter(); + + spin_lock(&priv->drv_lock); + netif_wake_queue(dev); + spin_unlock(&priv->drv_lock); + + sqn_pr_leave(); + return 0; +} + + +static int sqn_dev_stop(struct net_device *dev) +{ + struct sqn_private *priv = netdev_priv(dev); + + sqn_pr_enter(); + + spin_lock(&priv->drv_lock); + netif_stop_queue(dev); + spin_unlock(&priv->drv_lock); + + sqn_pr_leave(); + return 0; +} + + +/*******************************************************************/ +/* TX queue handlers */ +/*******************************************************************/ + +int sqn_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + unsigned long irq_flags = 0; + struct ethhdr *eth; + struct sqn_private *priv = netdev_priv(dev); + +#if DRIVER_DEBUG + printk(KERN_WARNING "sqn_hard_start_xmit \n"); +#endif + + sqn_pr_enter(); + + sqn_pr_dbg("skb->len = %d\n", skb->len); + +#if SKB_DEBUG + sqn_pr_info("%s: got skb [0x%p] from kernel, users %d\n", __func__, skb, atomic_read(&skb->users)); +#endif + + spin_lock_irqsave(&priv->drv_lock, irq_flags); + + //HTC code: for DDTM + if(drop_packet){ + eth = (struct ethhdr*) skb->data; + if(memcmp(eth->h_dest, ss_macaddr, ETH_ALEN) != 0){ + sqn_pr_dbg("HTC drop_packet enabled: not THP, drop it\n"); +#if DRIVER_DEBUG + printk(KERN_WARNING "sqn_hard_start_xmit: network packet\n"); +#endif + priv->stats.tx_dropped++; + priv->stats.tx_errors++; + dev_kfree_skb_any(skb); + goto out; + }else{ + sqn_pr_dbg("HTC drop_packet enabled: THP, let it live\n"); +#if DRIVER_DEBUG + printk(KERN_WARNING "sqn_hard_start_xmit: thp packet\n"); +#endif + } + } + + if (priv->removed) + goto out; + + if (skb->len < 1 || (skb->len > SQN_MAX_PDU_LEN)) { + sqn_pr_dbg("skb length %d not in range (1, %d)\n", skb->len, + SQN_MAX_PDU_LEN); + /* + * We'll never manage to send this one; + * drop it and return 'OK' + */ + priv->stats.tx_dropped++; + priv->stats.tx_errors++; + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + goto out; + } + + /* netif_stop_queue(priv->dev); */ + + priv->add_skb_to_tx_queue(priv, skb, 1); + + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + + dev->trans_start = jiffies; + + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + wake_up_interruptible(&priv->tx_waitq); +out: + sqn_pr_leave(); + return NETDEV_TX_OK; +} + + +static void sqn_tx_timeout(struct net_device *dev) +{ + /* struct sqn_private *priv = netdev_priv(dev); */ + + sqn_pr_enter(); + + sqn_pr_err("TX watch dog timeout\n"); + + sqn_pr_leave(); +} + + +static int sqn_tx_thread(void *data) +{ + struct net_device *dev = (struct net_device *) data; + struct sqn_private *priv = netdev_priv(dev); + int rv = 0; + unsigned long irq_flags = 0; + + sqn_pr_enter(); + + /* + * Set PF_NOFREEZE to prevent kernel to freeze this thread + * when going to suspend. We will manually stop it from + * driver's suspend handler. + */ + current->flags |= PF_NOFREEZE; + + for (;;) { + spin_lock_irqsave(&priv->drv_lock, irq_flags); + + if (!(priv->is_tx_queue_empty(priv)) || priv->removed) + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + else { + int rv = 0; + sqn_pr_dbg("wait for skb\n"); + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + + rv = wait_event_interruptible(priv->tx_waitq + , !(priv->is_tx_queue_empty(priv)) + || kthread_should_stop() + || priv->removed); + + /* + * If we've been interrupted by a signal, then we + * should stop a thread + */ + if (0 != rv) { + sqn_pr_dbg("got a signal from kernel %d\n", rv); + break; + } + } + + sqn_pr_dbg("got skb to send, wake up\n"); + + if (kthread_should_stop()) { + sqn_pr_dbg("break from main thread\n"); + break; + } + + spin_lock_irqsave(&priv->drv_lock, irq_flags); + if (priv->removed) { + sqn_pr_dbg("adapter removed; wait to die...\n"); + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + mdelay(1); + continue; + } + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + + rv= priv->hw_host_to_card(priv); + if (rv) + sqn_pr_dbg("failed to send PDU: %d\n", rv); + } + + sqn_pr_leave(); + return 0; +} + + +int sqn_start_tx_thread(struct sqn_private *priv) +{ + int rv = 0; + + sqn_pr_enter(); + + priv->tx_thread = kthread_run(sqn_tx_thread, priv->dev, "sqn_tx"); + + if (IS_ERR(priv->tx_thread)) { + sqn_pr_dbg("error creating TX thread.\n"); + rv = 1; + goto out; + } + + sqn_pr_leave(); +out: + return rv; +} + + +int sqn_stop_tx_thread(struct sqn_private *priv) +{ + int rv = 0; + + sqn_pr_enter(); + + kthread_stop(priv->tx_thread); + wake_up_interruptible(&priv->tx_waitq); + + sqn_pr_leave(); + + return rv; +} + + +/*******************************************************************/ +/* RX queue handlers */ +/*******************************************************************/ + +int sqn_rx_process(struct net_device *dev, struct sk_buff *skb) +{ + int rc = 0; + struct sqn_private *priv = netdev_priv(dev); + +#if DRIVER_DEBUG + printk(KERN_WARNING "sqn_rx_process \n"); +#endif + + sqn_pr_enter(); + + dev->last_rx = jiffies; + skb->protocol = eth_type_trans(skb, dev); + skb->dev = dev; + priv->stats.rx_packets++; + priv->stats.rx_bytes += skb->len; +#if SKB_DEBUG + sqn_pr_info("%s: push skb [0x%p] to kernel, users %d\n", __func__, skb, atomic_read(&skb->users)); +#endif + netif_rx(skb); + /* netif_receive_skb(skb); */ + + sqn_pr_leave(); + return rc; +} + + +/*******************************************************************/ +/* Interface statistics */ +/*******************************************************************/ + +static struct net_device_stats *sqn_get_stats(struct net_device *dev) +{ + struct sqn_private *priv = netdev_priv(dev); + + sqn_pr_enter(); + sqn_pr_leave(); + + return &priv->stats; +} + + +/*******************************************************************/ +/* Adding and removing procedures */ +/*******************************************************************/ + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) +static const struct net_device_ops sqn_netdev_ops = { + .ndo_open = sqn_dev_open + , .ndo_stop = sqn_dev_stop + , .ndo_start_xmit = sqn_hard_start_xmit + , .ndo_validate_addr = eth_validate_addr + , .ndo_tx_timeout = sqn_tx_timeout + , .ndo_get_stats = sqn_get_stats +}; +#endif + + +struct sqn_private *sqn_add_card(void *card, struct device *realdev) +{ + struct sqn_private *priv = 0; + u8 dummy_wimax_mac_addr[ETH_ALEN] = { 0x00, 0x16, 0x08, 0x00, 0x06, 0x53 }; + + /* Allocate an Ethernet device and register it */ + struct net_device *dev = alloc_netdev(sizeof(struct sqn_private), "wimax%d", ether_setup); + + sqn_pr_enter(); + + if (!dev) { + sqn_pr_err("init wimaxX device failed\n"); + goto done; + } + + priv = netdev_priv(dev); + g_priv = priv; + memset(priv, 0, sizeof(struct sqn_private)); + + /* + * Use dummy WiMAX mac address for development version (boot from + * flash) of WiMAX SDIO cards. Production cards use mac address from + * firmware which is loaded by driver. Random ethernet address can't be + * used if IPv4 convergence layer is enabled on WiMAX base station. + */ + memcpy(priv->mac_addr, dummy_wimax_mac_addr, ETH_ALEN); + + spin_lock_init(&priv->drv_lock); + + /* Fill the private stucture */ + priv->dev = dev; + priv->card = card; + + /* Setup the OS Interface to our functions */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) + dev->open = sqn_dev_open; + dev->stop = sqn_dev_stop; + dev->hard_start_xmit = sqn_hard_start_xmit; + dev->tx_timeout = sqn_tx_timeout; + dev->get_stats = sqn_get_stats; +#else + dev->netdev_ops = &sqn_netdev_ops; +#endif + + /* TODO: Make multicast possible */ + dev->flags &= ~IFF_MULTICAST; + + //wimax interface mtu must be 1400 (in spec) + dev->mtu = 1400; + SET_NETDEV_DEV(dev, realdev); + +done: + sqn_pr_leave(); + return priv; +} + + +int sqn_remove_card(struct sqn_private *priv) +{ + struct net_device *dev = priv->dev; + unsigned long irq_flags = 0; + + sqn_pr_enter(); + + dev = priv->dev; + + spin_lock_irqsave(&priv->drv_lock, irq_flags); + priv->removed = 1; + priv->dev = NULL; + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + + /* kthread_stop(priv->tx_thread); */ + /* wake_up_interruptible(&priv->tx_waitq); */ + free_netdev(dev); + + sqn_pr_leave(); + return 0; +} + + +int sqn_start_card(struct sqn_private *priv) +{ + struct net_device *dev = priv->dev; + + sqn_pr_enter(); + + if (register_netdev(dev)) { + sqn_pr_err("cannot register ethX device\n"); + return -1; + } + + sqn_pr_dbg("starting TX thread...\n"); + /* TODO: move waitq initializatio to add_card() */ + init_waitqueue_head(&priv->tx_waitq); + init_waitqueue_head(&priv->rx_waitq); + if (sqn_start_tx_thread(priv)) + goto err_init_adapter; + + sqn_pr_info("%s: Sequans WiMAX adapter\n", dev->name); + +#if IGNORE_CARRIER_STATE + netif_carrier_on(priv->dev); +#else + /* In release version this should be uncommented */ + /* netif_carrier_off(priv->dev); */ +#endif + +done: + sqn_pr_leave(); + return 0; + +err_init_adapter: + /* TODO: Free allocated resources */ + sqn_pr_err("error while init adapter\n"); + free_netdev(dev); + priv = NULL; + + goto done; +} + + +int sqn_stop_card(struct sqn_private *priv) +{ + struct net_device *dev = priv->dev; + + sqn_pr_enter(); + + unregister_netdev(dev); + + sqn_pr_leave(); + return 0; +} diff --git a/drivers/net/wimax/SQN/sdio-driver.h b/drivers/net/wimax/SQN/sdio-driver.h new file mode 100644 index 0000000000000..8c7162741141a --- /dev/null +++ b/drivers/net/wimax/SQN/sdio-driver.h @@ -0,0 +1,31 @@ +/* + * This is part of the Sequans SQN1130 driver. + * Copyright 2008 SEQUANS Communications + * Written by Andy Shevchenko + * + * 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. + */ + +#ifndef _SQN_MAIN_H +#define _SQN_MAIN_H + + +extern char *firmware_name; +extern int load_firmware; + + +struct sqn_private *sqn_add_card(void *card, struct device *realdev); +int sqn_remove_card(struct sqn_private *priv); + +int sqn_start_card(struct sqn_private *priv); +int sqn_stop_card(struct sqn_private *priv); + +int sqn_start_tx_thread(struct sqn_private *priv); +int sqn_stop_tx_thread(struct sqn_private *priv); + +int sqn_rx_process(struct net_device *dev, struct sk_buff *skb); + +#endif /* _SQN_MAIN_H */ diff --git a/drivers/net/wimax/SQN/sdio-fw.c b/drivers/net/wimax/SQN/sdio-fw.c new file mode 100644 index 0000000000000..062e8dc922790 --- /dev/null +++ b/drivers/net/wimax/SQN/sdio-fw.c @@ -0,0 +1,691 @@ +/* * This is part of the Sequans SQN1130 driver. + * Copyright 2008 SEQUANS Communications + * Written by Dmitriy Chumak + * + * 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. + */ + + +#include +#include +#include +#include + +#include "sdio.h" +#include "msg.h" +#include "sdio-sqn.h" +#include "sdio-netdev.h" +#include "version.h" +#include "sdio-fw.h" + +char *fw1130_name = "sqn1130.bin"; +char *fw1210_name = "sqn1210.bin"; + + +/* Tag, Lenght, Value struct */ +struct sqn_tlv { + u32 tag; +#define SWM_INFO_TAG_SQN_ROOT 0x80000000 /* SEQUANS root tag */ +#define SWM_INFO_TAG_SQN_MEMCPY 0x80000005 /* SEQUANS memcpy tag */ +#define SWM_INFO_TAG_SQN_MEMSET 0x80000006 /* SEQUANS memset tag */ +#define SWM_INFO_TAG_SQN_BOOTROM_GROUP 0x80040000 +#define SWM_INFO_TAG_SQN_ID_GROUP 0x80010000 /* SEQUANS identification group tag */ +#define SWM_INFO_TAG_SQN_MAC_ADDRESS 0x80010010 /* SEQUANS mac address tag */ + u32 length; + u8 value[0]; +}; + + +/* body of SWM_INFO_TAG_SQN_MEMCPY tag */ +struct sqn_tag_memcpy { + u32 address; + u32 access_size; + u32 data_size; + u8 data[0]; +}; + + +/* body of SWM_INFO_TAG_SQN_MEMSET tag */ +struct sqn_tag_memset { + u32 address; + u32 access_size; + u32 size; + u8 pattern; +}; + + +#define SQN_1130_SDRAM_BASE 0x00000000 +#define SQN_1130_SDRAM_END 0x03FFFFFF +#define SQN_1130_SDRAMCTL_BASE 0x4B400000 +#define SQN_1130_SDRAMCTL_END 0x4B4003FF + +#define SQN_1210_SDRAM_BASE 0x00000000 +#define SQN_1210_SDRAM_END 0x07FFFFFF +#define SQN_1210_SDRAMCTL_BASE 0x20002000 +#define SQN_1210_SDRAMCTL_END 0x2000207F + +static int is_good_ahb_address(u32 address, enum sqn_card_version card_version) +{ + u32 sdram_base = 0; + u32 sdram_end = 0; + u32 sdram_ctl_base = 0; + u32 sdram_ctl_end = 0; + int status = 0; + + sqn_pr_enter(); + + if (address % 4) + return 0; + + if (SQN_1130 == card_version) { + sqn_pr_dbg("using 1130 AHB address boundaries\n"); + sdram_base = SQN_1130_SDRAM_BASE; + sdram_end = SQN_1130_SDRAM_END; + sdram_ctl_base = SQN_1130_SDRAMCTL_BASE; + sdram_ctl_end = SQN_1130_SDRAMCTL_END; + } else if (SQN_1210 == card_version) { + sqn_pr_dbg("using 1210 AHB address boundaries\n"); + sdram_base = SQN_1210_SDRAM_BASE; + sdram_end = SQN_1210_SDRAM_END; + sdram_ctl_base = SQN_1210_SDRAMCTL_BASE; + sdram_ctl_end = SQN_1210_SDRAMCTL_END; + } else { + sqn_pr_warn("Can't check AHB address because of unknown" + " card version\n"); + status = 0; + goto out; + } + + status = ((sdram_base <= address && address < sdram_end) + || (sdram_ctl_base <= address && address < sdram_ctl_end)); +out: + sqn_pr_leave(); + return status; +} + +// Fix big buffer allocation problem during Firmware loading +/** + * sqn_alloc_big_buffer - tries to alloc a big buffer with kmalloc + * @buf: pointer to buffer + * @size: required buffer size + * @gfp_flags: GFP_* flags + * + * Tries to allocate a buffer of requested size with kmalloc. If it fails, + * then decrease buffer size in two times (adjusting the new size to be a + * multiple of 4) and try again. Use 6 retries in case of failures, after + * this give up and try to alloc 4KB buffer if requested size bigger than + * 4KB, otherwise allocate nothing and return 0. + * + * @return a real size of allocated buffer or 0 if allocation failed + */ + +static size_t sqn_alloc_big_buffer(u8 **buf, size_t size, gfp_t gfp_flags) +{ + size_t real_size = size; + int retries = 6; + + sqn_pr_enter(); + + /* Try to allocate buffer of requested size, if it failes try to + * allocate a twice smaller buffer. Repeat this number of + * times. */ + do + { + *buf = kmalloc(real_size, gfp_flags); + if (!(*buf)) { + real_size /= 2; + /* adjust the size to be a multiple of 4 */ + real_size += real_size % 4 ? 4 - real_size % 4 : 0; + } + } while (retries-- > 0 && !(*buf)); + + /* If all retries failed, then allocate 4KB buffer */ + if (!(*buf)) { + real_size = 4 * 1024; + if (size >= real_size) { + *buf = kmalloc(real_size, gfp_flags); + /* If it also failed, then just return 0, indicating + * that we failed to alloc buffer */ + if (!(*buf)) + real_size = 0; + } else { + /* We should _not_ return buffer bigger than requested */ + real_size = 0; + } + } + + sqn_pr_leave(); + + return real_size; +} + +#define SQN_SDIO_ADA_ADDR 0x00002060 +#define SQN_SDIO_ADA_RDWR 0x00002064 + + +static int write_data(struct sdio_func *func, u32 addr, void *data + , u32 size, u32 access_size) +{ + int rv = 0, retry = 0; + struct sqn_sdio_card *sqn_card = sdio_get_drvdata(func); + + sqn_pr_enter(); + sdio_claim_host(func); + + if (is_good_ahb_address(addr, sqn_card->version) + && 0 == (size % 4) && 4 == access_size) + { + /* write data using AHB */ + u8 *buf = 0; + size_t buf_size = 0; + u32 written_size = 0; + +#ifdef DEBUG + u8 *read_data = 0; +#endif + + sqn_pr_dbg("write data using AHB\n"); + sdio_writel(func, addr, SQN_SDIO_ADA_ADDR, &rv); + if (rv) { + sqn_pr_dbg("can't set SQN_SDIO_ADA_ADDR register\n"); + goto out; + } + sqn_pr_dbg("after SQN_SDIO_ADA_ADDR\n"); + + written_size = 0; + buf_size = sqn_alloc_big_buffer(&buf, size, GFP_KERNEL | GFP_DMA); + if (!buf) { + sqn_pr_err("failed to allocate buffer of %u bytes\n", size); + goto out; + } + + do { + memcpy(buf, data + written_size, buf_size); + rv = sdio_writesb(func, SQN_SDIO_ADA_RDWR, buf, buf_size); + if (rv) { + sqn_pr_dbg("can't write to SQN_SDIO_ADA_RDWR register\n"); + goto out; + } + written_size += buf_size; + if (written_size + buf_size > size) + buf_size = size - written_size; + } while (written_size < size); + kfree(buf); + + /* + * Workaround when sdio_writesb doesn't work because DMA + * alignment + */ + /* + int i = 0; + for (; i < size/4; ++i) { + sdio_writel(func, *((u32*)data + i), SQN_SDIO_ADA_RDWR, &rv); + if (rv) { + sqn_pr_dbg("can't write to SQN_SDIO_ADA_RDWR register\n"); + goto out; + } + } + */ + + sqn_pr_dbg("after SQN_SDIO_ADA_RDWR\n"); + + /* ******** only for debugging ******** */ + /* validate written data */ +/* #ifdef DEBUG */ +#if 0 + sqn_pr_dbg("reading data using AHB\n"); + sdio_writel(func, addr, SQN_SDIO_ADA_ADDR, &rv); + if (rv) { + sqn_pr_dbg("can't set SQN_SDIO_ADA_ADDR register\n"); + goto out; + } + sqn_pr_dbg("after SQN_SDIO_ADA_ADDR\n"); + + read_data = kmalloc(size, GFP_KERNEL); + rv = sdio_readsb(func, read_data, SQN_SDIO_ADA_RDWR, size); + if (rv) { + sqn_pr_dbg("can't read from SQN_SDIO_ADA_RDWR register\n"); + kfree(read_data); + goto out; + } + + if (memcmp(data, read_data, size)) + sqn_pr_dbg("WARNING: written data are __not__ equal\n"); + else + sqn_pr_dbg("OK: written data are equal\n"); + + kfree(read_data); +#endif /* DEBUG */ + /* ******** only for debugging ******** */ + + } else if (4 == access_size && size >= 4) { + /* write data using CMD53 */ + sqn_pr_dbg("write data using CMD53\n"); + rv = sdio_memcpy_toio(func, addr, data , size); + } else { + /* write data using CMD52 */ + /* not implemented yet, so we use CMD53 */ + /* rv = sdio_memcpy_toio(func, addr, data , size); */ + int i = 0; + sqn_pr_dbg("write data using CMD52\n"); + for (i = 0; i < size; ++i) { + sdio_writeb(func, *((u8*)data + i), addr + i, &rv); + if (rv) { + sqn_pr_dbg("can't write 1 byte to %xh addr using CMD52\n" + , addr + i); + goto out; + } + } + } + +out: + sdio_release_host(func); + sqn_pr_leave(); + return rv; +} + + +static int sqn_handle_memcpy_tag(struct sdio_func *func + , struct sqn_tag_memcpy * mcpy_tag) +{ + int rv = 0; + + sqn_pr_enter(); + + /* + * Convert values accordingly to platform "endianes" + * (big or little endian) because bootstrapper file + * data is big endian + */ + mcpy_tag->address = be32_to_cpu(mcpy_tag->address); + mcpy_tag->access_size = be32_to_cpu(mcpy_tag->access_size); + mcpy_tag->data_size = be32_to_cpu(mcpy_tag->data_size); + + /* sqn_pr_dbg("----------------------------------------\n"); */ + sqn_pr_dbg("address: 0x%02X access_size: %u data_size: %u\n" + , mcpy_tag->address, mcpy_tag->access_size + , mcpy_tag->data_size); + /* sqn_pr_dbg_dump("|", mcpy_tag->data, mcpy_tag->data_size); */ + + rv = write_data(func, mcpy_tag->address, mcpy_tag->data + , mcpy_tag->data_size, mcpy_tag->access_size); + + sqn_pr_leave(); + return rv; +} + + +static int sqn_handle_memset_tag(struct sdio_func *func + , struct sqn_tag_memset * mset_tag) +{ + int rv = 0; + u8 *buf = 0; + const u32 buf_size = 1024; + u32 left_bytes = 0; + + sqn_pr_enter(); + + /* + * Convert values accordingly to platform "endianes" + * (big or little endian) because bootstrapper file + * data is big endian + */ + mset_tag->address = be32_to_cpu(mset_tag->address); + mset_tag->access_size = be32_to_cpu(mset_tag->access_size); + mset_tag->size = be32_to_cpu(mset_tag->size); + + /* sqn_pr_dbg("----------------------------------------\n"); */ + sqn_pr_dbg("address: 0x%02X access_size: %u size: %u pattern 0x%02X\n" + , mset_tag->address, mset_tag->access_size + , mset_tag->size, mset_tag->pattern); + + buf = kmalloc(buf_size, GFP_KERNEL); + if (0 == buf) + return -ENOMEM; + + memset(buf, mset_tag->pattern, buf_size); + + left_bytes = mset_tag->size; + + while (left_bytes) { + u32 bytes_to_write = min(buf_size, left_bytes); + rv = write_data(func, mset_tag->address, buf, bytes_to_write, + mset_tag->access_size); + if (rv) + goto out; + left_bytes -= bytes_to_write; + } + +out: + kfree(buf); + sqn_pr_leave(); + return rv; +} + + +static int char_to_int(u8 c) +{ + int rv = 0; + + if ('0' <= c && c <= '9') { + rv = c - '0'; + } else if ('a' <= c && c <= 'f') { + rv = c - 'a' + 0xA; + } else if ('A' <= c && c <= 'F') { + rv = c - 'A' + 0xA; + } else { + rv = -1; + } + + return rv; +} + + +static int get_mac_addr_from_str(u8 *data, u32 length, u8 *result) +{ + int rv = 0; + int i = 0; + + sqn_pr_enter(); + + if (0 == length) { + rv = -1; + goto out; + } + + /* + * Check if we have delimiters on appropriate places: + * + * X X : X X : X X : X X : X X : X X + * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + */ + + if ( !( ( ':' == data[2] || '-' == data[2]) + && ( ':' == data[5] || '-' == data[5]) + && ( ':' == data[8] || '-' == data[8]) + && ( ':' == data[11] || '-' == data[11]) + && ( ':' == data[14] || '-' == data[14]) )) + { + sqn_pr_err("can't get mac address from firmware" + " - incorrect mac address\n"); + rv = -1; + goto out; + } + + i = 0; + while (i < length) { + int high = 0; + int low = 0; + + if ((high = char_to_int(data[i])) >= 0 + && (low = char_to_int(data[i + 1])) >= 0) + { + result[i/3] = low; + result[i/3] |= high << 4; + } else { + sqn_pr_err("can't get mac address from firmware" + " - incorrect mac address\n"); + rv = -1; + goto out; + } + + i += 3; + } + +out: + if (length > 0) { + data[length - 1] = 0; + sqn_pr_dbg("mac addr string: %s\n", data); + } + sqn_pr_leave(); + return rv; +} + + +static int sqn_handle_mac_addr_tag(struct sdio_func *func, u8 *data, u32 length) +{ + int rv = 0; + struct sqn_private *priv = + ((struct sqn_sdio_card *)sdio_get_drvdata(func))->priv; + + sqn_pr_enter(); + + /* + * This tag could contain one or two mac addresses in string + * form, delimited by some symbol (space or something else). + * Each mac address written as a string has constant length. + * Thus we can determine the number of mac addresses by the + * length of the tag: + * + * mac addr length in string form: XX:XX:XX:XX:XX:XX = 17 bytes + * tag length: 17 bytes [ + 1 byte + 17 bytes ] + */ + +#define MAC_ADDR_STRING_LEN 17 + + /* + * If we have only one mac addr we should increment it by one + * and use it. + * If we have two mac addresses we should use a second one. + */ + + if (MAC_ADDR_STRING_LEN <= length + && length < 2 * MAC_ADDR_STRING_LEN + 1) + { + sqn_pr_dbg("single mac address\n"); + /* we have only one mac addr */ + get_mac_addr_from_str(data, length, priv->mac_addr); + + // Andrew 0720 + // ++(priv->mac_addr[ETH_ALEN - 1]) + // real MAC: 38:E6:D8:86:00:00 + // hboot will store: 38:E6:D8:85:FF:FF (minus 1) + // sdio need to recovery it by plusing 1: 38:E6:D8:86:00:00 (plus 1) + + if ((++(priv->mac_addr[ETH_ALEN - 1])) == 0x00) + if ((++(priv->mac_addr[ETH_ALEN - 2])) == 0x00) + if ((++(priv->mac_addr[ETH_ALEN - 3])) == 0x00) + if ((++(priv->mac_addr[ETH_ALEN - 4])) == 0x00) + if ((++(priv->mac_addr[ETH_ALEN - 5])) == 0x00) + ++(priv->mac_addr[ETH_ALEN - 6]); + + } + else if (2 * MAC_ADDR_STRING_LEN + 1 == length) { /* we have two macs */ + sqn_pr_dbg("two mac addresses, using second\n"); + get_mac_addr_from_str(data + MAC_ADDR_STRING_LEN + 1 + , length - (MAC_ADDR_STRING_LEN + 1), priv->mac_addr); + } + else { /* incorrect data length */ + sqn_pr_err("can't get mac address from bootloader" + " - incorrect mac address length\n"); + rv = -1; + goto out; + } + + sqn_pr_info("setting MAC address from bootloader: " + "%02x:%02x:%02x:%02x:%02x:%02x\n", priv->mac_addr[0] + , priv->mac_addr[1], priv->mac_addr[2], priv->mac_addr[3] + , priv->mac_addr[4], priv->mac_addr[5]); + +out: + sqn_pr_leave(); + return rv; +} + + +/** sqn_load_bootstrapper - reads a binary boostrapper file, analize it + * and loads data to the card. + * + * Bootstrapper is consists of Tag, Length, Value (TLV) sections. + * Each section starts with 4 bytes tag. Then goes length of data (4 bytes) + * and then the data itself. + * + * All fields of bootstrapper file is in BIG ENDIAN format. + */ +static int sqn_load_bootstrapper(struct sdio_func *func, u8 *data, int size) +{ + struct sqn_tlv *tlv = (struct sqn_tlv*) data; + int rv = 0; + + sqn_pr_enter(); + + while (size > 0) { + /* + * Convert values accordingly to platform "endianes" + * (big or little endian) because bootstrapper file + * data is big endian + */ + tlv->tag = be32_to_cpu(tlv->tag); + tlv->length = be32_to_cpu(tlv->length); + + switch (tlv->tag) { + case SWM_INFO_TAG_SQN_ROOT: + case SWM_INFO_TAG_SQN_BOOTROM_GROUP: + case SWM_INFO_TAG_SQN_ID_GROUP: + /* + * This tag is a "container" tag - it's value field + * contains other tags + */ + + /* sqn_pr_dbg("========================================\n"); */ + sqn_pr_dbg("tag: CONTAINER %x length: %u\n", tlv->tag + , tlv->length); + /* sqn_pr_dbg_dump("|", tlv->value, tlv->length); */ + + /* + * If this is a buggy tag, adjust length to + * the rest of data + */ + if (0 == tlv->length) + tlv->length = size - sizeof(*tlv); + + rv = sqn_load_bootstrapper(func, (u8*) tlv->value + , tlv->length); + if (rv) + goto out; + break; + + case SWM_INFO_TAG_SQN_MEMCPY: + /* sqn_pr_dbg("========================================\n"); */ + sqn_pr_dbg("tag: SWM_INFO_TAG_SQN_MEMCPY length: %u\n" + , tlv->length); + /* sqn_pr_dbg_dump("|", tlv->value, tlv->length); */ + rv = sqn_handle_memcpy_tag(func + , (struct sqn_tag_memcpy*) tlv->value); + if (rv) + goto out; + break; + + case SWM_INFO_TAG_SQN_MEMSET: + /* sqn_pr_dbg("========================================\n"); */ + sqn_pr_dbg("tag: SWM_INFO_TAG_SQN_MEMSET length: %u\n" + , tlv->length); + /* sqn_pr_dbg_dump("|", tlv->value, tlv->length); */ + rv = sqn_handle_memset_tag(func + , (struct sqn_tag_memset*) tlv->value); + if (rv) + goto out; + break; + + case SWM_INFO_TAG_SQN_MAC_ADDRESS: + /* sqn_pr_dbg("========================================\n"); */ + sqn_pr_dbg("tag: SWM_INFO_TAG_SQN_MAC_ADDRESS length: %u\n" + , tlv->length); + /* sqn_pr_dbg_dump("|", tlv->value, tlv->length); */ + + rv = sqn_handle_mac_addr_tag(func, tlv->value + , tlv->length); + if (rv) + goto out; + break; + + default: + /* skip all other tags */ + /* sqn_pr_dbg("========================================\n"); */ + sqn_pr_dbg("tag: UNKNOWN %x length: %u\n" + , tlv->tag, tlv->length); + /* sqn_pr_dbg_dump("|", tlv->value, tlv->length); */ + break; + } + + /* increment tlv to point it to the beginning of the next + * sqn_tlv struct and decrement size accordingly + */ + size = (int)(size - (sizeof(*tlv) + tlv->length)); + tlv = (struct sqn_tlv*) ((u8*)tlv + sizeof(*tlv) + tlv->length); + } + + if (0 != size) { + /* something wrong with parsing of tlv values */ + rv = -1; + goto out; + } + +out: + sqn_pr_leave(); + return rv; +} + + +extern char *firmware_name; + +/** sqn_load_firmware - loads firmware to card + * @func: SDIO function, used to transfer data via SDIO interface, + * also used to obtain pointer to device structure. + * + * But now the only work it does - is loading of bootstrapper to card, + * because firmware is supposed to be loaded by a userspace program. + */ +int sqn_load_firmware(struct sdio_func *func) +{ + int rv = 0; + const struct firmware *fw = 0; +//Create a local firmware_name with path to replace original global firmware_name -- Tony Wu. + const char *firmware_name = "../../../data/wimax/Boot.bin"; + + struct sqn_sdio_card *sqn_card = sdio_get_drvdata(func); + + sqn_pr_enter(); + + sqn_pr_info("trying to find bootloader image: \"%s\"\n", firmware_name); + if ((rv = request_firmware(&fw, firmware_name, &func->dev))) + goto out; + + if (SQN_1130 == sqn_card->version) { + sdio_claim_host(func); + + /* properly setup registers for firmware loading */ + sqn_pr_dbg("setting up SQN_H_SDRAM_NO_EMR register\n"); + sdio_writeb(func, 0, SQN_H_SDRAM_NO_EMR, &rv); + if (rv) { + sdio_release_host(func); + goto out; + } + + sqn_pr_dbg("setting up SQN_H_SDRAMCTL_RSTN register\n"); + sdio_writeb(func, 1, SQN_H_SDRAMCTL_RSTN, &rv); + sdio_release_host(func); + if (rv) + goto out; + } + + sqn_pr_info("loading bootloader to the card...\n"); + if ((rv = sqn_load_bootstrapper(func, fw->data, fw->size))) + goto out; + + /* boot the card */ + sqn_pr_info("bootting the card...\n"); + sdio_claim_host(func); // by daniel + sdio_writeb(func, 1, SQN_H_CRSTN, &rv); + sdio_release_host(func); // by daniel + if (rv) + goto out; + sqn_pr_info(" done\n"); + +out: + sqn_pr_leave(); + return rv; +} diff --git a/drivers/net/wimax/SQN/sdio-fw.h b/drivers/net/wimax/SQN/sdio-fw.h new file mode 100644 index 0000000000000..3875091a23bc7 --- /dev/null +++ b/drivers/net/wimax/SQN/sdio-fw.h @@ -0,0 +1,24 @@ +/* + * This is part of the Sequans SQN1130 driver. + * Copyright 2008 SEQUANS Communications + * Written by Dmitriy Chumak + * + * 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. + */ + + +#ifndef _SQN_FIRMWARE_H +#define _SQN_FIRMWARE_H + + +#define SQN_DEFAULT_FW_NAME "sequans_boot.bin" +extern char *fw1130_name; +extern char *fw1210_name; + + +int sqn_load_firmware(struct sdio_func *func); + +#endif /* _SQN_FIRMWARE_H */ diff --git a/drivers/net/wimax/SQN/sdio-netdev.h b/drivers/net/wimax/SQN/sdio-netdev.h new file mode 100644 index 0000000000000..273e8801d3222 --- /dev/null +++ b/drivers/net/wimax/SQN/sdio-netdev.h @@ -0,0 +1,50 @@ +/* + * This is part of the Sequans SQN1130 driver. + * Copyright 2008 SEQUANS Communications + * Written by Andy Shevchenko , + * Dmitriy Chumak + * + * 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. + */ + +#ifndef _SQN_NETDEV_H +#define _SQN_NETDEV_H + +#include +#include +#include +#include +#include + + +/* TODO: Move to sqn_sdio.c */ +#define SQN_MAX_PDU_LEN 2048 /* Max PDU length */ + + +extern struct ethtool_ops sqn_ethtool_ops; + +struct sqn_private { + spinlock_t drv_lock; + void *card; + struct net_device *dev; + struct net_device_stats stats; + u8 mac_addr[ETH_ALEN]; + struct task_struct *tx_thread; /* Thread to service TX queue */ + wait_queue_head_t tx_waitq; + wait_queue_head_t rx_waitq; + struct work_struct rx_work_struct; + u8 removed; + + + int (*hw_host_to_card) (struct sqn_private *priv); + + void (*add_skb_to_tx_queue) (struct sqn_private *priv + , struct sk_buff *skb, u8 tail); + + int (*is_tx_queue_empty) (struct sqn_private *priv); +}; + +#endif /* _SQN_NETDEV_H */ diff --git a/drivers/net/wimax/SQN/sdio-pm.c b/drivers/net/wimax/SQN/sdio-pm.c new file mode 100644 index 0000000000000..6cec7605b2f53 --- /dev/null +++ b/drivers/net/wimax/SQN/sdio-pm.c @@ -0,0 +1,838 @@ + /* + * This is part of the Sequans SQN1130 driver. + * Copyright 2008 SEQUANS Communications + * Written by Dmitriy Chumak + * + * 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 file includes code that is responsible for + * power management and LSP notifications handling. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ANDROID_KERNEL +#include +#endif /* ANDROID_KERNEL */ + +#include "sdio-netdev.h" +#include "sdio.h" +#include "sdio-pm.h" +#include "msg.h" +#include "thp.h" +#include "sdio-sqn.h" + +#define IGNORE_CARRIER_STATE 1 +extern int mmc_wimax_get_hostwakeup_gpio(void); + +enum sqn_thsp_service { +#define THSP_LSP_SERVICE_BASE 0x10010000 + THSP_GET_MEDIA_CONNECTION_STATE = THSP_LSP_SERVICE_BASE + , THSP_MEDIA_CONNECTION_STATE_CHANGE + , THSP_SET_POWER_MODE /* deprecated */ + + , THSP_SET_HOST_POWER_MODE + , THSP_SET_HOST_POWER_MODE_ACK + + , THSP_SET_FW_POWER_MODE + , THSP_SET_FW_POWER_MODE_ACK + + , THSP_SQN_STATE_CHANGE + , THSP_SQN_STATE_CHANGE_REPLY + + , THSP_THP_AVAILABLE + , THSP_THP_AVAILABLE_REPLY +}; + + +/* Deprecated */ +/* enum sqn_power_mode { */ + /* SQN_PM_OPERATIONAL */ + /* , SQN_PM_SHUTDOWN */ + /* , SQN_PM_STANDBY */ +/* }; */ + + +enum sqn_host_power_mode { + LSP_HPM_OPERATIONAL + , LSP_HPM_ASLEEP +}; + + +enum sqn_fw_power_mode { + LSP_FPM_OPERATIONAL + , LSP_FPM_SHUTDOWN + , LSP_FPM_STANDBY +}; + + +enum sqn_pm_status { + SQN_PM_STATUS_SUCCES + , SQN_PM_STATUS_CHANGE_IN_PROGRESS + , SQN_PM_STATUS_UNKNOWN +}; + + +enum sqn_thsp_media_connection_state { + THSP_MEDIA_CONNECTION_DISCONNECTED + , THSP_MEDIA_CONNECTION_CONNECTING + , THSP_MEDIA_CONNECTION_CONNECTED + , THSP_MEDIA_CONNECTION_ATTACHED +}; + + +enum sqn_fw_state { + LSP_SQN_ACTIVE + , LSP_SQN_IDLE + , LSP_SQN_DROPPED + , LSP_SQN_REENTRY +}; + + +enum sqn_thp_available_reply { + LSP_THPA_ACK + , LSP_THPA_FINISHED + , LSP_THPA_EXIT +}; + + +struct sqn_eth_header { + u8 dst_addr[ETH_ALEN]; + u8 src_addr[ETH_ALEN]; + u16 len; +}; + + +struct sqn_lsp_header { + u32 id; + union { + u32 tid; + enum sqn_thsp_media_connection_state media_con_state; + + struct { + u32 tid; + enum sqn_host_power_mode mode; + } host_power; + + struct { + u32 tid; + enum sqn_fw_power_mode mode; + } fw_power; + + struct { + u32 tid; + enum sqn_fw_state state; + } fw_state; + + struct { + u32 tid; + enum sqn_thp_available_reply reply; + } thp_avl; + } u; +}; + + +struct sqn_lsp_packet { + struct sqn_thp_header thp_header; + struct sqn_lsp_header lsp_header; +}; + + +static u8 g_lsp_host_mac[] = {0x00, 0x16, 0x08, 0xff, 0x00, 0x06}; +static u8 g_lsp_device_mac[] = {0x00, 0x16, 0x08, 0xff, 0x00, 0x05}; + + +/* TODO: add all global variables to private per-card structure */ +static struct sk_buff *g_last_request_skb = 0; +static spinlock_t g_last_request_lock = SPIN_LOCK_UNLOCKED; +static u32 g_last_request_pm = 0; + + +/* TODO: move this to per-card private structure */ +static DECLARE_WAIT_QUEUE_HEAD(g_card_sleep_waitq); + + +/* Transaction ID for lsp requests */ +static u32 g_tid = 0; +static spinlock_t g_tid_lock = SPIN_LOCK_UNLOCKED; + + +static u32 get_current_tid(void) +{ + u32 tid = 0; + + spin_lock(&g_tid_lock); + tid = g_tid; + spin_unlock(&g_tid_lock); + + return tid; +} + + +static u32 get_next_tid(void) +{ + u32 tid = 0; + + spin_lock(&g_tid_lock); + g_tid += 1; + tid = g_tid; + spin_unlock(&g_tid_lock); + + return tid; +} + + +static void free_last_request(void) +{ + sqn_pr_enter(); + + spin_lock(&g_last_request_lock); + if (0 != g_last_request_skb) { + dev_kfree_skb_any(g_last_request_skb); + g_last_request_skb = 0; + } + spin_unlock(&g_last_request_lock); + + sqn_pr_leave(); +} + + +static struct sk_buff* lsp_to_skb(struct sqn_lsp_packet *lsp_packet) +{ + struct sqn_eth_header eth_header = { + .len = htons(sizeof(struct sqn_lsp_packet)) + }; + + struct sk_buff *skb = + __dev_alloc_skb(sizeof(eth_header) + sizeof(struct sqn_lsp_packet) + , GFP_ATOMIC | GFP_DMA); + + sqn_pr_enter(); + + if (0 == skb) + goto out; + + skb_reserve(skb, 2); + + memcpy(eth_header.dst_addr, g_lsp_device_mac, sizeof(g_lsp_device_mac)); + memcpy(eth_header.src_addr, g_lsp_host_mac, sizeof(g_lsp_host_mac)); + + memcpy(skb->data, ð_header, sizeof(eth_header)); + skb_put(skb, sizeof(eth_header)); + + memcpy(skb->data + skb->len, lsp_packet, sizeof(struct sqn_lsp_packet)); + skb_put(skb, sizeof(struct sqn_lsp_packet)); + + sqn_pr_leave(); + +out: + return skb; + +} + + +static struct sk_buff* construct_lsp_packet(u32 id, u32 param1, u32 param2) +{ + struct sqn_lsp_packet lsp_packet = { + .thp_header = { + .transport_version = 1 + , .flags = 1 + , .seq_number = 0 + , .ack_number = 0 + } + , .lsp_header = { + .id = htonl(id) + } + }; + + struct sk_buff *skb = 0; + + sqn_pr_enter(); + + switch (id) { + case THSP_GET_MEDIA_CONNECTION_STATE: + /* no parameters are needed */ + sqn_pr_dbg("id: THSP_GET_MEDIA_CONNECTION_STATE\n"); + lsp_packet.thp_header.length = + htons(sizeof(struct sqn_lsp_header) - 4); + lsp_packet.thp_header.total_length = + htonl(sizeof(struct sqn_lsp_header) - 4); + break; + case THSP_SET_POWER_MODE: + /* deprecated */ + sqn_pr_dbg("id: THSP_SET_POWER_MODE (deprecated)\n"); + break; + case THSP_SET_HOST_POWER_MODE: + lsp_packet.thp_header.length = + htons(sizeof(struct sqn_lsp_header)); + lsp_packet.thp_header.total_length = + htonl(sizeof(struct sqn_lsp_header)); + lsp_packet.lsp_header.u.host_power.tid = htonl(get_next_tid()); + lsp_packet.lsp_header.u.host_power.mode = htonl(param1); + sqn_pr_dbg("id: THSP_SET_HOST_POWER_MODE, tid: 0x%x, mode: %d\n" + , ntohl(lsp_packet.lsp_header.u.host_power.tid) + , param1); + break; + case THSP_SET_FW_POWER_MODE: + lsp_packet.thp_header.length = + htons(sizeof(struct sqn_lsp_header)); + lsp_packet.thp_header.total_length = + htonl(sizeof(struct sqn_lsp_header)); + lsp_packet.lsp_header.u.fw_power.tid = htonl(get_next_tid()); + lsp_packet.lsp_header.u.fw_power.mode = htonl(param1); + sqn_pr_dbg("id: THSP_SET_FW_POWER_MODE, tid: 0x%x, mode: %d\n" + , htonl(lsp_packet.lsp_header.u.fw_power.tid) + , param1); + break; + case THSP_SQN_STATE_CHANGE_REPLY: + lsp_packet.thp_header.length = + htons(sizeof(struct sqn_lsp_header) - 4); + lsp_packet.thp_header.total_length = + htonl(sizeof(struct sqn_lsp_header) - 4); + lsp_packet.lsp_header.u.fw_state.tid = htonl(param1); + sqn_pr_dbg("id: THSP_SQN_STATE_CHANGE_REPLY, tid: %xh\n" + , param1); + break; + case THSP_THP_AVAILABLE_REPLY: + lsp_packet.thp_header.length = + htons(sizeof(struct sqn_lsp_header)); + lsp_packet.thp_header.total_length = + htonl(sizeof(struct sqn_lsp_header)); + lsp_packet.lsp_header.u.thp_avl.tid = htonl(param1); + lsp_packet.lsp_header.u.thp_avl.reply = htonl(param2); + sqn_pr_dbg("id: THSP_THP_AVAILABLE_REPLY, tid: 0x%x, reply: %d\n" + , param1, param2); + break; + default: + sqn_pr_dbg("id: UNKNOWN\n"); + } + + skb = lsp_to_skb(&lsp_packet); + + sqn_pr_leave(); + + return skb; +} + + +int is_lsp_packet(const struct sk_buff *skb) +{ + struct sqn_eth_header *eth_hdr = (struct sqn_eth_header*)skb->data; + + /* sqn_pr_dbg_dump("skb________", skb->data, skb->len); */ + /* sqn_pr_dbg_dump("lsp_dev_mac", g_lsp_device_mac, sizeof(g_lsp_device_mac)); */ + /* sqn_pr_dbg_dump("skb_addr___", eth_hdr->src_addr, sizeof(g_lsp_device_mac)); */ + + return !memcmp(eth_hdr->dst_addr, g_lsp_host_mac + , sizeof(g_lsp_host_mac)); +} + + +static int sqn_set_power_mode_helper(struct sdio_func *func + , enum sqn_thsp_service command_id, u32 pm) +{ + unsigned long irq_flags = 0; + struct sqn_sdio_card *card = sdio_get_drvdata(func); + + sqn_pr_enter(); + + free_last_request(); + + spin_lock(&g_last_request_lock); + + g_last_request_pm = pm; + g_last_request_skb = construct_lsp_packet(command_id, pm, 0); + + if (0 == g_last_request_skb) + return 1; + + netif_stop_queue(card->priv->dev); + + /* + * We can't call sqn_sdio_tx_skb() from here, because we are not in + * process context + */ + skb_queue_tail(&card->tx_queue, g_last_request_skb); + g_last_request_skb = 0; + + spin_unlock(&g_last_request_lock); + + spin_lock_irqsave(&card->priv->drv_lock, irq_flags); + card->pm_complete = 0; + spin_unlock_irqrestore(&card->priv->drv_lock, irq_flags); + + wake_up_interruptible(&card->priv->tx_waitq); + + sqn_pr_leave(); + + return 0; +} + + +static int sqn_set_host_power_mode(struct sdio_func *func, enum sqn_host_power_mode pm) +{ + int rv = 0; + + sqn_pr_enter(); + + rv = sqn_set_power_mode_helper(func, THSP_SET_HOST_POWER_MODE, pm); + + sqn_pr_leave(); + + return rv; +} + + +static int sqn_set_fw_power_mode(struct sdio_func *func, enum sqn_fw_power_mode pm) +{ + int rv = 0; + + sqn_pr_enter(); + + rv = sqn_set_power_mode_helper(func, THSP_SET_FW_POWER_MODE, pm); + + sqn_pr_leave(); + + return rv; +} + + +static void signal_pm_request_completion(struct sqn_private *priv) +{ + struct sqn_sdio_card *card = priv->card; + unsigned long irq_flags = 0; + + sqn_pr_enter(); + + spin_lock_irqsave(&priv->drv_lock, irq_flags); + card->pm_complete = 1; + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + + wake_up_interruptible(&card->pm_waitq); + + sqn_pr_leave(); +} + + +void signal_card_sleep_completion(struct sqn_private *priv) +{ + struct sqn_sdio_card *card = priv->card; + unsigned long irq_flags = 0; + + sqn_pr_enter(); + + spin_lock_irqsave(&priv->drv_lock, irq_flags); + card->is_card_sleeps = 0; + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + wake_up_interruptible(&g_card_sleep_waitq); + sqn_pr_dbg("card sleep completion is signaled\n"); + + sqn_pr_leave(); +} + + +int sqn_notify_host_sleep(struct sdio_func *func) +{ + int rv = 0; + unsigned long irq_flags = 0; + u32 timeout = 0; + struct sqn_sdio_card *sqn_card = sdio_get_drvdata(func); + + sqn_pr_enter(); + + sqn_card->waiting_pm_notification = 1; + + sqn_pr_info("notify card about host goes to sleep...\n"); + sqn_set_host_power_mode(func, LSP_HPM_ASLEEP); + + timeout = 50; + sqn_pr_info("wait for completion (timeout %u msec)...\n", timeout); + rv = wait_event_interruptible_timeout(sqn_card->pm_waitq + , sqn_card->pm_complete, msecs_to_jiffies(timeout)); + if (-ERESTARTSYS == rv) { + sqn_pr_warn("got a signal from kernel %d\n", rv); + } else if (0 == rv) { + /* a timeout elapsed */ + sqn_pr_warn("timeout elapsed - still no ack from card" + ", assume that card in sleep mode now\n"); + sqn_card->is_card_sleeps = 1; + } else { + /* we got an ack from card */ + sqn_pr_info("card in sleep mode now\n"); + sqn_card->is_card_sleeps = 1; + rv = 0; + } + + sqn_card->pm_complete = 0; + sqn_card->waiting_pm_notification = 0; + + sqn_pr_leave(); + + return rv; +} + + +int sqn_notify_host_wakeup(struct sdio_func *func) +{ + int rv = 0; + + sqn_pr_enter(); + + rv = sqn_wakeup_fw(func); + + sqn_pr_leave(); + + return rv; +}; + + +static void handle_sqn_state_change_msg(struct sqn_private *priv + , struct sqn_lsp_packet *lsp) +{ + struct sqn_sdio_card *card = priv->card; + struct sk_buff *skb_reply = 0; + unsigned long irq_flags = 0; + const int card_state = ntohl(lsp->lsp_header.u.fw_state.state); + + sqn_pr_enter(); + + switch (card_state) { + case LSP_SQN_ACTIVE: + sqn_pr_info("card switched to ACTIVE state\n"); + spin_lock_irqsave(&priv->drv_lock, irq_flags); + card->is_card_sleeps = 0; + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + break; + case LSP_SQN_IDLE: + sqn_pr_info("card switched to IDLE state\n"); + spin_lock_irqsave(&priv->drv_lock, irq_flags); + card->is_card_sleeps = 1; + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + break; + case LSP_SQN_DROPPED: + sqn_pr_info("card switched to DROPPED state\n"); + spin_lock_irqsave(&priv->drv_lock, irq_flags); + card->is_card_sleeps = 1; + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + break; + case LSP_SQN_REENTRY: + sqn_pr_info("card switched to REENTRY state\n"); + spin_lock_irqsave(&priv->drv_lock, irq_flags); + card->is_card_sleeps = 1; + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + break; + default: + sqn_pr_info("card switched to UNSUPPORTED mode %d/0x%x\n" + , card_state, card_state); + spin_lock_irqsave(&priv->drv_lock, irq_flags); + card->is_card_sleeps = 0; + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + break; + } + skb_reply = construct_lsp_packet(THSP_SQN_STATE_CHANGE_REPLY + , ntohl(lsp->lsp_header.u.thp_avl.tid), 0); + if (0 != (skb_reply = sqn_sdio_prepare_skb_for_tx(skb_reply))) + sqn_sdio_tx_skb(card, skb_reply, 0); + wake_up_interruptible(&g_card_sleep_waitq); + + sqn_pr_leave(); +} + + +static void handle_thp_avl_msg(struct sqn_private *priv + , struct sqn_lsp_packet *lsp) +{ + struct sqn_sdio_card *card = priv->card; + struct sk_buff *skb_reply = 0; + enum sqn_thp_available_reply thp_rpl; + unsigned long irq_flags = 0; + + sqn_pr_enter(); + + spin_lock_irqsave(&priv->drv_lock, irq_flags); + /* if (card->is_card_sleeps) { */ + if (priv->is_tx_queue_empty(priv)) { + sqn_pr_dbg("TX queue empty, thp_rpl=FINISH\n"); + /* sqn_pr_dbg("card was asleep, thp_rpl=FINISH\n"); */ + thp_rpl = LSP_THPA_FINISHED; + card->is_card_sleeps = 1; + /* } else if (priv->is_tx_queue_empty(priv)) { */ + /* sqn_pr_dbg("card was not asleep and tx_queue is empty, thp_rpl=FINISHED\n"); */ + /* thp_rpl = LSP_THPA_FINISHED; */ + /* card->is_card_sleeps = 1; */ + } else { + /* sqn_pr_info("card was not asleep but tx_queue is no empty, thp_rpl=EXIT\n"); */ + sqn_pr_dbg("TX queue not empty, thp_rpl=ACK\n"); + /* sqn_pr_dbg("card was not asleep, thp_rpl=ACK\n"); */ + thp_rpl = LSP_THPA_ACK; + card->is_card_sleeps = 0; + } + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + skb_reply = construct_lsp_packet(THSP_THP_AVAILABLE_REPLY + , ntohl(lsp->lsp_header.u.thp_avl.tid) + , thp_rpl); + if (0 != (skb_reply = sqn_sdio_prepare_skb_for_tx(skb_reply))) + sqn_sdio_tx_skb(card, skb_reply, 0); + wake_up_interruptible(&g_card_sleep_waitq); + if (netif_queue_stopped(priv->dev)) + netif_wake_queue(priv->dev); + + sqn_pr_leave(); +} + + +int sqn_handle_lsp_packet(struct sqn_private *priv + , struct sk_buff *skb) +{ + struct sqn_sdio_card *card = priv->card; + unsigned long irq_flags = 0; + struct sqn_lsp_packet *lsp_response = (struct sqn_lsp_packet*) + ((u8*)skb->data + sizeof(struct sqn_eth_header)); + + sqn_pr_enter(); + + if (!is_lsp_packet(skb)) { + sqn_pr_dbg("not LSP packet\n"); + sqn_pr_leave(); + return 0; + } + + sqn_pr_dbg("LSP packet\n"); + + switch (ntohl(lsp_response->lsp_header.id)) { + case THSP_GET_MEDIA_CONNECTION_STATE: + sqn_pr_dbg("id: THSP_GET_MEDIA_CONNECTION_STATE state=%xh\n" + , ntohl(lsp_response->lsp_header.u.media_con_state)); + sqn_pr_warn("THSP_GET_MEDIA_CONNECTION_STATE not implemented\n"); + break; + case THSP_MEDIA_CONNECTION_STATE_CHANGE: + sqn_pr_dbg("id: THSP_MEDIA_CONNECTION_STATE_CHANGE state=%xh\n" + , ntohl(lsp_response->lsp_header.u.media_con_state)); + if (THSP_MEDIA_CONNECTION_ATTACHED + == ntohl(lsp_response->lsp_header.u.media_con_state)) + { + +#if IGNORE_CARRIER_STATE + /* netif_carrier_on(priv->dev); */ + sqn_pr_info("WiMAX carrier PRESENT [ignored]\n"); +#else + netif_carrier_on(priv->dev); + sqn_pr_info("WiMAX carrier PRESENT\n"); +#endif + } else { +#if IGNORE_CARRIER_STATE + /* netif_carrier_off(priv->dev); */ + sqn_pr_info("WiMAX carrier LOST [ignored]\n"); +#else + netif_carrier_off(priv->dev); + sqn_pr_info("WiMAX carrier LOST\n"); +#endif + } + break; + case THSP_SET_HOST_POWER_MODE_ACK: + sqn_pr_dbg("id: THSP_SET_HOST_POWER_MODE_ACK tid=0x%x\n" + , ntohl(lsp_response->lsp_header.u.host_power.tid)); + free_last_request(); + spin_lock_irqsave(&priv->drv_lock, irq_flags); + card->is_card_sleeps = 1; + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + signal_pm_request_completion(priv); + break; + case THSP_SET_FW_POWER_MODE_ACK: + sqn_pr_dbg("id: THSP_SET_FW_POWER_MODE_ACK tid=0x%x\n" + , ntohl(lsp_response->lsp_header.u.fw_power.tid)); + sqn_pr_dbg("THSP_SET_FW_POWER_MODE_ACK not implemented\n"); + break; + case THSP_SQN_STATE_CHANGE: + sqn_pr_dbg("id: THSP_SQN_STATE_CHANGE tid=0x%x, state=%xh\n" + , ntohl(lsp_response->lsp_header.u.fw_state.tid) + , ntohl(lsp_response->lsp_header.u.fw_state.state)); + handle_sqn_state_change_msg(priv, lsp_response); + break; + case THSP_THP_AVAILABLE: + sqn_pr_dbg("id: THSP_THP_AVAILABLE tid=0x%x, reply=%xh\n" + , ntohl(lsp_response->lsp_header.u.thp_avl.tid) + , ntohl(lsp_response->lsp_header.u.thp_avl.reply)); + handle_thp_avl_msg(priv, lsp_response); + break; + default: + sqn_pr_dbg("lsp_id: UNKNOWN=0x%x\n" + , ntohl(lsp_response->lsp_header.id)); + } + + dev_kfree_skb_any(skb); + sqn_pr_leave(); + + return 1; +} + + +int sqn_wakeup_fw(struct sdio_func *func) +{ + int rv = 0; + int ver = 0; + int counter = 0; + + int retry_cnt = 3; + u32 wakeup_delay = 0; + unsigned long timeout = msecs_to_jiffies(800); + + unsigned long irq_flags = 0; + struct sqn_private *priv = ((struct sqn_sdio_card *)sdio_get_drvdata(func))->priv; + struct sqn_sdio_card *card = priv->card; + u8 need_to_unlock_wakelock = 0; + + sqn_pr_enter(); + + sqn_pr_info("waking up the card...\n"); + + if (!wake_lock_active(&card->wakelock)) { + sqn_pr_dbg("lock wake_lock\n"); + wake_lock(&card->wakelock); + need_to_unlock_wakelock = 1; + } + +retry: + sdio_claim_host(func); + +#define SDIO_CCCR_CCCR_SDIO_VERSION_VALUE 0x11 + + wakeup_delay = 2; + counter = 5; + do { + ver = sdio_readb(func, SDIO_CCCR_CCCR_SDIO_VERSION, &rv); + // To avoid FW sutck in PLLOFF, SDIO isn't able to wake up it. + mdelay(wakeup_delay); + --counter; + } while((rv || ver != SDIO_CCCR_CCCR_SDIO_VERSION_VALUE) && counter > 0); + + if (rv) { + sqn_pr_err("error when reading SDIO_VERSION\n"); + sdio_release_host(func); + goto out; + } else { + sqn_pr_dbg("SDIO_VERSION has been read successfully\n"); + } + + sqn_pr_dbg("send wake-up signal to card\n"); + sdio_writeb(func, 1, SQN_SOC_SIGS_LSBS, &rv); + if (rv) + sqn_pr_err("error when writing to SQN_SOC_SIGS_LSBS: %d\n", rv); + + sdio_release_host(func); + + sqn_pr_info("wait for completion (timeout %d msec)...\n" + , jiffies_to_msecs(timeout)); + + rv = wait_event_interruptible_timeout(g_card_sleep_waitq + , 0 == card->is_card_sleeps, timeout); + + if (-ERESTARTSYS == rv) { + sqn_pr_warn("got a signal from kernel %d\n", rv); + } else if (0 == rv) { + rv = -1; + sqn_pr_err("can't wake up the card - timeout elapsed\n"); + + if (retry_cnt-- > 0) { + sqn_pr_info("retry wake up\n"); + goto retry; + } + sqn_pr_info("giving up to wake up the card\n"); + + spin_lock_irqsave(&priv->drv_lock, irq_flags); + card->is_card_sleeps = 0; + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + } else { + rv = 0; + sqn_pr_info("card is waked up successfully\n"); + } + +out: + if (need_to_unlock_wakelock && wake_lock_active(&card->wakelock)) { + sqn_pr_dbg("wake_lock is active, release it\n"); + wake_unlock(&card->wakelock); + } + + sqn_pr_leave(); + return rv; +} + +#ifdef ANDROID_KERNEL + +extern u8 sqn_is_gpio_irq_enabled; + +static void sqn_handle_android_early_suspend(struct early_suspend *h) +{ + sqn_pr_enter(); + sqn_pr_info("%s: enter\n", __func__); + + if (!sqn_is_gpio_irq_enabled) { + sqn_pr_info("enable GPIO%d interrupt\n", mmc_wimax_get_hostwakeup_gpio()); + enable_irq(MSM_GPIO_TO_INT(mmc_wimax_get_hostwakeup_gpio())); + enable_irq_wake(MSM_GPIO_TO_INT(mmc_wimax_get_hostwakeup_gpio())); + + sqn_is_gpio_irq_enabled = 1; + } + + sqn_pr_info("%s: leave\n", __func__); + sqn_pr_leave(); +} + + +static void sqn_handle_android_late_resume(struct early_suspend *h) +{ + sqn_pr_enter(); + sqn_pr_info("%s: enter\n", __func__); + + if (sqn_is_gpio_irq_enabled) { + sqn_pr_info("disable GPIO%d interrupt\n", (mmc_wimax_get_hostwakeup_gpio())); + disable_irq_wake(MSM_GPIO_TO_INT(mmc_wimax_get_hostwakeup_gpio())); + disable_irq(MSM_GPIO_TO_INT(mmc_wimax_get_hostwakeup_gpio())); + sqn_is_gpio_irq_enabled = 0; + } + + sqn_pr_info("%s: leave\n", __func__); + sqn_pr_leave(); +} + + +static struct early_suspend sqn_early_suspend_desc = { + .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + , .suspend = sqn_handle_android_early_suspend + , .resume = sqn_handle_android_late_resume +}; + + +void register_android_earlysuspend(void) +{ + sqn_pr_enter(); + + register_early_suspend(&sqn_early_suspend_desc); + + sqn_pr_leave(); +} + + +void unregister_android_earlysuspend(void) +{ + sqn_pr_enter(); + + unregister_early_suspend(&sqn_early_suspend_desc); + + sqn_pr_leave(); +} +#endif /* ANDROID_KERNEL */ diff --git a/drivers/net/wimax/SQN/sdio-pm.h b/drivers/net/wimax/SQN/sdio-pm.h new file mode 100644 index 0000000000000..540acc3b1a791 --- /dev/null +++ b/drivers/net/wimax/SQN/sdio-pm.h @@ -0,0 +1,35 @@ +/* + * This is part of the Sequans SQN1130 driver. + * Copyright 2008 SEQUANS Communications + * Written by Dmitriy Chumak + * + * 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. + */ + + +#ifndef _SQN_POWER_MANAGEMENT_H +#define _SQN_POWER_MANAGEMENT_H + + +int sqn_notify_host_sleep(struct sdio_func *func); + +int sqn_notify_host_wakeup(struct sdio_func *func); + + +int sqn_handle_lsp_packet(struct sqn_private *priv + , struct sk_buff *skb); + + +int sqn_wakeup_fw(struct sdio_func *func); + + +void signal_card_sleep_completion(struct sqn_private *priv); + +void register_android_earlysuspend(void); + +void unregister_android_earlysuspend(void); + +#endif /* _SQN_POWER_MANAGEMENT_H */ diff --git a/drivers/net/wimax/SQN/sdio-sqn.h b/drivers/net/wimax/SQN/sdio-sqn.h new file mode 100644 index 0000000000000..343963a331e76 --- /dev/null +++ b/drivers/net/wimax/SQN/sdio-sqn.h @@ -0,0 +1,166 @@ +/* + * This is part of the Sequans SQN1130 driver. + * Copyright 2008 SEQUANS Communications + * Written by Andy Shevchenko + * + * 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. + */ + +#ifndef _SQN_SDIO_H +#define _SQN_SDIO_H + +#include "version.h" + +#include +#include +#include +#include + + +enum sqn_card_version { + SQN_1130 = 1 + , SQN_1210 +}; + + +/* Card private information */ +struct sqn_sdio_card { + struct sqn_private *priv; + struct sdio_func *func; + u8 rstn_wr_fifo_flag; + u8 version; + struct sk_buff_head tx_queue; + struct mutex tx_mutex; +#define TX_QUEUE_MAX_LEN 1000 /* max length to which tx_queue is allowed to grow */ +#define TX_QUEUE_WM_LEN 800 /* length, from which we will continue transmission */ + struct sk_buff_head rx_queue; +#define RX_QUEUE_MAX_LEN 1000 /* max length to which rx_queue is allowed to grow */ +#define RX_QUEUE_WM_LEN 800 /* length, from which we will continue transmission */ + struct mutex rx_mutex; + struct mutex rxq_mutex; + wait_queue_head_t pm_waitq; + struct wake_lock wakelock; + struct timer_list wakelock_timer; + + /* Condition flags for event signaling */ + u8 pm_complete; + u8 it_thread_should_stop; + u8 is_card_sleeps; + u8 waiting_pm_notification; +}; + + +void sqn_sdio_stop_it_thread_from_itself(struct sqn_private *priv); + +struct sk_buff* sqn_sdio_prepare_skb_for_tx(struct sk_buff *skb); + +int sqn_sdio_tx_skb(struct sqn_sdio_card *card, struct sk_buff *skb + , u8 claim_host); + + +#define SQN_SDIO_PDU_MINLEN 2 +#define SQN_SDIO_PDU_MAXLEN 2047 + +/* Product IDs */ +#define SDIO_CMN_CISTPLMID_MANF 0x1002 /* Sequans manufacture ID register */ +#define SDIO_CMN_CISTPLMID_CARD 0x1004 /* Sequans SQN1130 card ID register */ + +#define SDIO_VENDOR_ID_SEQUANS 0x039d /* Sequans manufacture ID */ +#define SDIO_DEVICE_ID_SEQUANS_SQN1130 0x046a /* Sequans SQN1130 card ID */ +#define SDIO_DEVICE_ID_SEQUANS_SQN1210 0x1210 /* Sequans SQN1210-rev2 card ID */ + +#define SDIO_CCCR_CCCR_SDIO_VERSION 0x00 +#define SDIO_CCCR_IO_ABORT 0x06 + + +#define SQN_H_VERSION 0x240C + + +/* FIFO dependent list */ +#define SQN_SDIO_RDWR_BASE 0x2000 +#define SQN_SDIO_RDWR_FIFO(x) (SQN_SDIO_RDWR_BASE + (x)*4) + +#define SQN_SDIO_RDLEN_BASE 0x2002 +#define SQN_SDIO_RDLEN_FIFO(x) (SQN_SDIO_RDLEN_BASE + (x)*4) + +#define SQN_SDIO_PCRRT_BASE 0x2010 +#define SQN_SDIO_PCRRT_FIFO(x) (SQN_SDIO_PCRRT_BASE + (x)*2) + +#define SQN_SDIO_PCWRT_BASE 0x2011 +#define SQN_SDIO_PCWRT_FIFO(x) (SQN_SDIO_PCWRT_BASE + (x)*2) + +#define SQN_SDIO_SZ_RD_BASE 0x2018 +#define SQN_SDIO_SZ_RD_FIFO(x) (SQN_SDIO_SZ_RD_BASE + (x)*8) + +#define SQN_SDIO_WM_RD_BASE 0x201a +#define SQN_SDIO_WM_RD_FIFO(x) (SQN_SDIO_WM_RD_BASE + (x)*8) + +#define SQN_SDIO_SZ_WR_BASE 0x201c +#define SQN_SDIO_SZ_WR_FIFO(x) (SQN_SDIO_SZ_WR_BASE + (x)*8) + +#define SQN_SDIO_WM_WR_BASE 0x201e +#define SQN_SDIO_WM_WR_FIFO(x) (SQN_SDIO_WM_WR_BASE + (x)*8) + +#define SQN_SDIO_ESZ_WR_FIFO0 0x2032 /* FIFO0 */ +#define SQN_SDIO_ESZ_WR_FIFO1 0x2036 /* FIFO1 */ +#define SQN_SDIO_ESZ_WR_FIFO2 0x0000 /* No real FIFO */ + +#define SQN_SDIO_RSTN_RD_BASE 0x2038 +#define SQN_SDIO_RSTN_RD_FIFO(x) (SQN_SDIO_RSTN_RD_BASE + (x)*2) + +#define SQN_SDIO_RSTN_WR_BASE 0x2039 +#define SQN_SDIO_RSTN_WR_FIFO(x) (SQN_SDIO_RSTN_WR_BASE + (x)*2) + +#define SQN_SDIO_RD_LEVEL_BASE 0x2048 +#define SQN_SDIO_RD_FIFO_LEVEL(x) (SQN_SDIO_RD_LEVEL_BASE + (x)*4) + +#define SQN_SDIO_WR_LEVEL_BASE 0x204a +#define SQN_SDIO_WR_FIFO_LEVEL(x) (SQN_SDIO_WR_LEVEL_BASE + (x)*4) + +#define SQN_SDIO_RD_BYTESLEFT_BASE 0x2054 +#define SQN_SDIO_RD_FIFO_BYTESLEFT(x) (SQN_SDIO_RD_BYTESLEFT_BASE + (x)*4) + +#define SQN_SDIO_WR_BYTESLEFT_BASE 0x2056 +#define SQN_SDIO_WR_FIFO_BYTESLEFT(x) (SQN_SDIO_WR_BYTESLEFT_BASE + (x)*4) + +/* Interrupt registers */ +#define SQN_SDIO_IT_EN_LSBS 0x2044 +#define SQN_SDIO_IT_EN_MSBS 0x2045 +#define SQN_SDIO_IT_STATUS_LSBS 0x2046 +#define SQN_SDIO_IT_STATUS_MSBS 0x2047 + +/* Firmware loading registers */ +#define SQN_H_CRSTN 0x2404 +#define SQN_H_SDRAMCTL_RSTN 0x2414 +#define SQN_H_SDRAM_NO_EMR 0x2415 +#define SQN_H_BOOT_FROM_SPI 0x2411 + + +/* Interrupt flags (LSB) */ +#define SQN_SDIO_IT_WR_FIFO0_WM (1 << 0) +#define SQN_SDIO_IT_RD_FIFO0_WM (1 << 1) +#define SQN_SDIO_IT_WR_FIFO1_WM (1 << 2) +#define SQN_SDIO_IT_RD_FIFO1_WM (1 << 3) +#define SQN_SDIO_IT_WR_FIFO2_WM (1 << 4) +#define SQN_SDIO_IT_RD_FIFO2_WM (1 << 5) +#define SQN_SDIO_IT_RD_EMPTY_WR_FULL (1 << 6) +#define SQN_SDIO_IT_SW_SIGN (1 << 7) + +/* Interrupt flags (MSB) */ +#define SQN_SDIO_IT_WR_FIFO0_FULL_RST (1 << 0) +#define SQN_SDIO_IT_RD_FIFO0_EMPTY_RST (1 << 1) +#define SQN_SDIO_IT_WR_FIFO1_FULL_RST (1 << 2) +#define SQN_SDIO_IT_RD_FIFO1_EMPTY_RST (1 << 3) +#define SQN_SDIO_IT_WR_FIFO2_FULL_RST (1 << 4) +#define SQN_SDIO_IT_RD_FIFO2_EMPTY_RST (1 << 5) +#define SQN_SDIO_IT_RD_BEFORE_RDLEN (1 << 6) +#define SQN_SDIO_IT_UNSUPPORTED_CMD (1 << 7) + +/* Software signaling interrupts */ +#define SQN_SOC_SIGS_LSBS 0x2600 +#define SQN_HTS_SIGS 0x2608 + +#endif /* _SQN_SDIO_H */ diff --git a/drivers/net/wimax/SQN/sdio.c b/drivers/net/wimax/SQN/sdio.c new file mode 100644 index 0000000000000..176fb17fafa7b --- /dev/null +++ b/drivers/net/wimax/SQN/sdio.c @@ -0,0 +1,1696 @@ +/* + * This is part of the Sequans SQN1130 driver. + * Copyright 2008 SEQUANS Communications + * Written by Dmitriy Chumak , + * Andy Shevchenko + * + * Inspired by if_sdio.c, Copyright 2007 Pierre Ossman + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// GPIO_WAKEUP +#include +#include +#include + +#include "version.h" +#include "msg.h" +#include "sdio-netdev.h" +#include "sdio-sqn.h" +#include "sdio.h" +#include "thp.h" +#include "sdio-driver.h" +#include "sdio-fw.h" +#include "sdio-pm.h" + +#define SKB_DEBUG 0 +#define SDIO_CLK_DEBUG 0 +#define DUMP_NET_PKT 0 + + +static const struct sdio_device_id sqn_sdio_ids[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_SEQUANS, SDIO_DEVICE_ID_SEQUANS_SQN1130) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_SEQUANS, SDIO_DEVICE_ID_SEQUANS_SQN1210) }, + /* { SDIO_DEVICE(SDIO_ANY_ID, SDIO_ANY_ID) }, */ + { 0 }, +}; +MODULE_DEVICE_TABLE(sdio, sqn_sdio_ids); + +//HTC:WiMax power ON_OFF function and Card detect function +extern int mmc_wimax_power(int on); +extern void mmc_wimax_set_carddetect(int val); +extern int mmc_wimax_uart_switch(int uart); +extern int mmc_wimax_set_status(int on); + +/*******************************************************************/ +/* TX handlers */ +/*******************************************************************/ + +static void sqn_sdio_add_skb_to_tx_queue(struct sqn_private *priv + , struct sk_buff *skb, u8 tail) +{ + struct sqn_sdio_card *card = priv->card; + + sqn_pr_enter(); + + if (tail) + skb_queue_tail(&card->tx_queue, skb); + else + skb_queue_head(&card->tx_queue, skb); + + if (skb_queue_len(&card->tx_queue) > TX_QUEUE_MAX_LEN + && !netif_queue_stopped(priv->dev)) + { + sqn_pr_info("tx_queue len %d, disabling netif_queue\n" + , skb_queue_len(&card->tx_queue)); + netif_stop_queue(priv->dev); + } + + if (!card->waiting_pm_notification + && !wake_lock_active(&card->wakelock)) { + sqn_pr_dbg("lock wake_lock\n"); + wake_lock(&card->wakelock); + } + sqn_pr_leave(); +} + + +static int sqn_sdio_is_tx_queue_empty(struct sqn_private *priv) +{ + int rv = 0; + struct sqn_sdio_card *card = priv->card; + + sqn_pr_enter(); + + rv = skb_queue_empty(&card->tx_queue); + + sqn_pr_leave(); + + return rv; +} + + +static int sqn_sdio_get_rstn_wr_fifo_flag(struct sqn_private *priv) +{ + struct sqn_sdio_card *card = priv->card; + + sqn_pr_enter(); + + if (0 == card->rstn_wr_fifo_flag) { + int rv = 0; + sdio_claim_host(card->func); // by daniel + card->rstn_wr_fifo_flag = sdio_readb(card->func, + SQN_SDIO_RSTN_WR_FIFO(2), &rv); + sdio_release_host(card->func); // by daniel + sqn_pr_dbg("RSTN_WR_FIFO2 = %d\n", card->rstn_wr_fifo_flag); + if (rv) { + sqn_pr_err("sdio_readb(RSTN_WR_FIFO2) - return error\n"); + card->rstn_wr_fifo_flag = 0; + goto out; + } + } + + sqn_pr_leave(); +out: + return card->rstn_wr_fifo_flag; +} + + +static int sqn_sdio_recover_after_cmd53_timeout(struct sqn_sdio_card *card) +{ + int rv = 0; + + sqn_pr_enter(); + + sqn_pr_info("Try to recovery after SDIO timeout error\n"); + sdio_claim_host(card->func); + sdio_writeb(card->func, 1 << card->func->num, SDIO_CCCR_IO_ABORT, &rv); + sdio_release_host(card->func); + if (rv) { + sqn_pr_err("sdio_writeb(SDIO_CCCR_IO_ABORT) - return error %d\n" + , rv); + } + + sqn_pr_leave(); + + return rv; +} + + +/** +* sqn_sdio_cmd52_read_buf - read @size bytes into @buf buffer from +* address @addr using CMD52 +* @card: sqn sdio card structure +* @buf: buffer to return value, should be and address of u16, u32 variable +* @size: size of the @buf / count of bytes to read from @addr +* +* @return error status - 0 if success, !0 otherwise +*/ +static int sqn_sdio_cmd52_read_buf(struct sqn_sdio_card *card, void* buf, int size, int addr) +{ + u8 tmpbuf[4] = { 0xa7, 0xa7, 0xa7, 0xa7 }; + int i = 0; + int rv = 0; + + sqn_pr_enter(); + sqn_pr_info("Trying to read %d bytes from 0x%x address using CMD52\n", size, addr); + + sdio_claim_host(card->func); + for (i = 0; i < size; i++) { + tmpbuf[i] = sdio_readb(card->func, addr + i, &rv); + if (rv) { + sqn_pr_err("sdio_readb(%x) - return error %d\n", addr + i, rv); + break; + } + } + sdio_release_host(card->func); + + switch (size) { + case sizeof(u16): + *((u16*)buf) = le16_to_cpup((__le16 *)tmpbuf); + break; + case sizeof(u32): + *((u32*)buf) = le32_to_cpup((__le32 *)tmpbuf); + break; + default: + sqn_pr_err("unsupported buffer size: %d\n", size); + } + + sqn_pr_leave(); + + return rv; +} + + +static int sqn_sdio_dump_registers(struct sqn_sdio_card *card) +{ + u8 b8 = 0; + u16 b16 = 0; + int rv = 0; + + sqn_pr_enter(); + sqn_pr_info("------------------ REG DUMP BEGIN ------------------\n"); + + sdio_claim_host(card->func); + + b8 = sdio_readb(card->func, SQN_SDIO_IT_STATUS_LSBS, &rv); + if (rv) + sqn_pr_err("can't read SDIO_IT_STATUS_LSBS: %d\n", rv); + else + sqn_pr_info("SDIO_IT_STATUS_LSBS: 0x%x\n", b8); + + b8 = sdio_readb(card->func, SQN_SDIO_IT_STATUS_MSBS, &rv); + if (rv) + sqn_pr_err("can't read SDIO_IT_STATUS_MSBS: %d\n", rv); + else + sqn_pr_info("SDIO_IT_STATUS_MSBS: 0x%x\n", b8); + + b8 = sdio_readb(card->func, SQN_SDIO_RSTN_WR_FIFO(2), &rv); + if (rv) + sqn_pr_err("can't read SQN_SDIO_RSTN_WR_FIFO2: %d\n", rv); + else + sqn_pr_info("SQN_SDIO_RSTN_WR_FIFO: 0x%x\n", b8); + + b8 = sdio_readb(card->func, SQN_SOC_SIGS_LSBS, &rv); + if (rv) + sqn_pr_err("can't read SQN_SOC_SIGS_LSBS: %d\n", rv); + else + sqn_pr_info("SQN_SOC_SIGS_LSBS: 0x%x\n", b8); + + b8 = sdio_readb(card->func, SQN_HTS_SIGS, &rv); + if (rv) + sqn_pr_err("can't read SQN_HTS_SIGS: %d\n", rv); + else + sqn_pr_info("SQN_HTS_SIGS: 0x%x\n", b8); + + sdio_release_host(card->func); + + rv = sqn_sdio_cmd52_read_buf(card, &b16, sizeof(b16), SQN_SDIO_WR_FIFO_BYTESLEFT(2)); + if (rv) + sqn_pr_err("can't read SDIO_WR_FIFO_BYTESLEFT2: %d\n", rv); + else + sqn_pr_info("SDIO_WR_FIFO_BYTESLEFT2: 0x%x\n", b16); + + rv = sqn_sdio_cmd52_read_buf(card, &b16, sizeof(b16), SQN_SDIO_WR_FIFO_LEVEL(2)); + if (rv) + sqn_pr_err("can't read SQN_SDIO_WR_FIFO_LEVEL2: %d\n", rv); + else + sqn_pr_info("SQN_SDIO_WR_FIFO_LEVEL2: 0x%x\n", b16); + + rv = sqn_sdio_cmd52_read_buf(card, &b16, sizeof(b16), SQN_SDIO_RD_FIFO_LEVEL(2)); + if (rv) + sqn_pr_err("can't read SQN_SDIO_RD_FIFO_LEVEL2: %d\n", rv); + else + sqn_pr_info("SQN_SDIO_RD_FIFO_LEVEL2: 0x%x\n", b16); + + rv = sqn_sdio_cmd52_read_buf(card, &b16, sizeof(b16), SDIO_CMN_CISTPLMID_MANF); + if (rv) + sqn_pr_err("can't read SDIO_CMN_CISTPLMID_MANF: %d\n", rv); + else + sqn_pr_info("SDIO_CMN_CISTPLMID_MANF: 0x%x\n", b16); + + rv = sqn_sdio_cmd52_read_buf(card, &b16, sizeof(b16), SDIO_CMN_CISTPLMID_CARD); + if (rv) + sqn_pr_err("can't read SDIO_CMN_CISTPLMID_CARD: %d\n", rv); + else + sqn_pr_info("SDIO_CMN_CISTPLMID_CARD: 0x%x\n", b16); + + sqn_pr_info("------------------ REG DUMP END ------------------\n"); + sqn_pr_leave(); + + return rv; +} + + +static int sqn_sdio_get_wr_fifo_level(struct sqn_private *priv) +{ + int level = 0; + int rv = 0; + struct sqn_sdio_card *card = priv->card; + + sqn_pr_enter(); + + sdio_claim_host(card->func); // by daniel + /* level = sdio_readw(card->func, SQN_SDIO_WR_FIFO_LEVEL(2), &rv); */ + level = sdio_readl(card->func, 0x2050, &rv); + level = (u32)level >> sizeof(u16); + sdio_release_host(card->func); // by daniel + sqn_pr_dbg("SQN_SDIO_WR_FIFO_LEVEL2 = %d\n", level); + if (rv) { + sqn_pr_err("sdio_readw(WR_FIFO_LEVEL2) error %d\r", rv); + level = -1; + if (-ETIMEDOUT == rv) + sqn_pr_info("SDIO CMD53 timeout error\n"); + /* sqn_sdio_recover_after_cmd53_timeout(card); */ + /* sqn_sdio_dump_registers(card); */ + goto out; + } + + sqn_pr_leave(); +out: + return level; +} + +#if DUMP_NET_PKT +uint8_t is_thp_packet(uint8_t *dest_addr); +int is_lsp_packet(const struct sk_buff *skb); +#endif + +struct sk_buff* sqn_sdio_prepare_skb_for_tx(struct sk_buff *skb) +{ +#define PDU_LEN_SIZE 2 +#define CRC_SIZE 4 +#define PAD_TO_VALUE 512 + +#if DUMP_NET_PKT + struct ethhdr *eth = (struct ethhdr *)skb->data; +#endif + + /* + * Calculate padding, to workaround some SDIO controllers we need to pad + * each TX buffer so it size will be a multiple of PAD_TO_VALUE + */ + u32 padding = (skb->len + PDU_LEN_SIZE + CRC_SIZE) % PAD_TO_VALUE ? + PAD_TO_VALUE - (skb->len + PDU_LEN_SIZE + CRC_SIZE) % PAD_TO_VALUE : 0; + + sqn_pr_enter(); + + sqn_pr_dbg("length %d, padding %d\n", skb->len, padding); + + if (skb->len > (SQN_MAX_PDU_LEN - (PDU_LEN_SIZE + CRC_SIZE + padding))) + return 0; + +#if DUMP_NET_PKT + if (!is_thp_packet(eth->h_source) && !is_lsp_packet(skb)) { + sqn_pr_info("----------------------------------------------------------------------\n"); + sqn_pr_info("TX PDU length %d\n", skb->len); + sqn_pr_info_dump("TX PDU", skb->data, skb->len); + } +#endif + + /* + * Real size of the PDU is data_len + 2 bytes at begining of PDU + * for pdu_size + 4 bytes at the end of PDU for CRC of data + */ + if (skb_headroom(skb) < PDU_LEN_SIZE || skb_tailroom(skb) < CRC_SIZE + padding) { + struct sk_buff *origin_skb = skb; + gfp_t gfp_mask = GFP_DMA; + if (in_interrupt() || irqs_disabled()) + gfp_mask |= GFP_ATOMIC; + else + gfp_mask |= GFP_KERNEL; + sqn_pr_dbg("relocating TX skb, GFP mask %x\n", gfp_mask); +#if SKB_DEBUG + sqn_pr_info("%s: [0x%p] old before reloc, users %d\n", __func__, origin_skb, atomic_read(&origin_skb->users)); +#endif + skb = skb_copy_expand(skb, PDU_LEN_SIZE, CRC_SIZE + padding + , gfp_mask); +#if SKB_DEBUG + sqn_pr_info("%s: [0x%p] old after reloc, users %d\n", __func__, origin_skb, atomic_read(&origin_skb->users)); +#endif + dev_kfree_skb_any(origin_skb); + if (0 == skb) { + /* An error occured, likely there is no memory to + * expand skb, so we drop it. + */ + return 0; + } +#if SKB_DEBUG + sqn_pr_info("%s: [0x%p] new relocated, users %d\n", __func__, skb, atomic_read(&skb->users)); +#endif + } else { + sqn_pr_dbg("TX skb: headroom = %d tailroom = %d\n" + , skb_headroom(skb), skb_tailroom(skb)); + } + + /* + * Add size of PDU before ethernet frame + * It should be in little endian byte order + */ + *((u8*)skb->data -2) = (skb->len + CRC_SIZE) & 0xff; + *((u8*)skb->data -1) = ((skb->len + CRC_SIZE) >> 8) & 0xff; + skb_push(skb, PDU_LEN_SIZE); + + /* + * Add CRC to the end of ethernet frame + * Now it simply set to 0 + */ + memset(skb->tail, 0, CRC_SIZE); + skb_put(skb, CRC_SIZE + padding); + + sqn_pr_leave(); + + return skb; +} + + +int sqn_sdio_tx_skb(struct sqn_sdio_card *card, struct sk_buff *skb + , u8 claim_host) +{ + int rv = 0; + + sqn_pr_enter(); + + if (claim_host) + sdio_claim_host(card->func); + + rv = sdio_writesb(card->func, SQN_SDIO_RDWR_FIFO(2), skb->data, + skb->len); + if (rv) { + sqn_pr_err("call to sdio_writesb(RDWR_FIFO2) - return error %d\n", rv); + if (-ETIMEDOUT == rv) { + if (claim_host) { + sdio_release_host(card->func); + claim_host = 0; + } + sqn_pr_info("SDIO CMD53 timeout error: TX PDU length %d, PDU[0] 0x%x, PDU[1] 0x%x\n" + , skb->len, *((u8*)skb->data), *((u8*)skb->data + 1)); + /* sqn_sdio_dump_registers(card); */ + /* sqn_sdio_recover_after_cmd53_timeout(card); */ + } + goto release; + } +release: + if (claim_host) + sdio_release_host(card->func); +#if SKB_DEBUG + sqn_pr_info("%s: free skb [0x%p] after tx, users %d\n", __func__, skb, atomic_read(&skb->users)); +#endif + dev_kfree_skb_any(skb); + + sqn_pr_leave(); + return rv; +} + + +static void sqn_sdio_wake_lock_release_timer_fn(unsigned long data) +{ + struct sqn_sdio_card *card = (struct sqn_sdio_card*) data; + + sqn_pr_enter(); + + /* if TX and RX queues are empty, we can releas a wake_lock */ + if (wake_lock_active(&card->wakelock) + && skb_queue_empty(&card->tx_queue) + && skb_queue_empty(&card->rx_queue)) + { + sqn_pr_dbg("wake_lock is active, release it\n"); + + wake_unlock(&card->wakelock); + } + + sqn_pr_leave(); +} + +static void sqn_sdio_release_wake_lock(struct sqn_sdio_card *card) +{ + u32 delay = 0; + + sqn_pr_enter(); + +// #define SQN_WAKE_LOCK_RELEASE_DELAY_SECONDS 5 +#define SQN_WAKE_LOCK_RELEASE_DELAY_SECONDS 1 + + /* if TX and RX queues are empty, we will wait some time before + * doing actual wake_lock release */ + if (wake_lock_active(&card->wakelock) + && skb_queue_empty(&card->tx_queue) + && skb_queue_empty(&card->rx_queue)) + { + sqn_pr_dbg("shedule wake_lock release in %d sec\n" + , SQN_WAKE_LOCK_RELEASE_DELAY_SECONDS); + + delay = jiffies + msecs_to_jiffies( + SQN_WAKE_LOCK_RELEASE_DELAY_SECONDS * MSEC_PER_SEC); + + mod_timer(&card->wakelock_timer, delay); + } + +#undef SQN_WAKE_LOCK_RELEASE_DELAY_SECONDS + sqn_pr_leave(); +} + +static int sqn_sdio_host_to_card(struct sqn_private *priv) +{ + struct sqn_sdio_card *card = priv->card; + struct sk_buff *skb = 0; + unsigned long irq_flags = 0; + int level = 0; + int rv = 0; + u8 need_to_ulock_mutex = 0; + + sqn_pr_enter(); + + if (priv->removed) { + // sqn_pr_warn("%s: card/driver is removed, do nothing\n", __func__); // Andrew 0524 + goto drv_removed; + } + + spin_lock_irqsave(&priv->drv_lock, irq_flags); + if (card->is_card_sleeps) { + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + /* + * Ignore return value of sqn_wakeup_fw() and try + * to send PDU even if wake up failed + */ + sqn_wakeup_fw(card->func); + } else { + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + } + + if (0 == sqn_sdio_get_rstn_wr_fifo_flag(priv)) { + rv = -1; + goto dequeue_skb; + } + + sqn_pr_dbg("acquire TX mutex\n"); + if (!mutex_trylock(&card->tx_mutex)) { + sqn_pr_dbg("failed to acquire TX mutex, it means we are going" + " to remove a network interface\n"); + need_to_ulock_mutex = 0; + goto out; + } + need_to_ulock_mutex = 1; + + while (!priv->removed && !sqn_sdio_is_tx_queue_empty(priv)) { + skb = skb_dequeue(&card->tx_queue); + if (0 != (skb = sqn_sdio_prepare_skb_for_tx(skb))) { + if (0 == level) { + int count = 20; + while (0 == (level = sqn_sdio_get_wr_fifo_level(priv))) { + if (0 == count--) { + sqn_pr_err("WR_FIFO_LEVEL2 timeout\n"); + rv = -1; + goto free_skb; + } + mdelay(1); + } + + if (level < 0) { + rv = -1; + goto free_skb; + } + } + + sqn_sdio_tx_skb(card, skb, 1); + --level; + + if (!card->waiting_pm_notification + && netif_queue_stopped(priv->dev) + && skb_queue_len(&card->tx_queue) < TX_QUEUE_WM_LEN) + { + sqn_pr_info("tx_queue len %d, enabling netif_queue\n" + , skb_queue_len(&card->tx_queue)); + netif_wake_queue(priv->dev); + } else { + sqn_pr_dbg("tx_queue len %d\n" + , skb_queue_len(&card->tx_queue)); + } + } else { + priv->stats.tx_dropped++; + priv->stats.tx_errors++; + } + } +out: + if (need_to_ulock_mutex && mutex_is_locked(&card->tx_mutex)) { + mutex_unlock(&card->tx_mutex); + sqn_pr_dbg("release TX mutex\n"); + } + + sqn_sdio_release_wake_lock(card); + if (0 != rv) { + /* + * Failed to send PDU - assume that card was removed or + * crashed/reset so initiate card detection. + */ + + // Andrew 0424 + // Reset chip will cause WiMAX status to OFF and then SCAN, OPERATION + // Reset WiMAX chip + // It could avoid we hang in SDIO CMD53 timeout and recovery wimax again. + + sqn_pr_info("reset WiMAX chip\n"); + mmc_wimax_power(0); + mdelay(5); + mmc_wimax_power(1); + + sqn_pr_err("card seems to be dead/removed - initiate reinitialization\n"); + mmc_detect_change(card->func->card->host, 1); + } +drv_removed: + sqn_pr_leave(); + return rv; + +dequeue_skb: + if (!sqn_sdio_is_tx_queue_empty(priv)) { + sqn_pr_dbg("remove skb from TX queue because of error\n"); + skb = skb_dequeue(&card->tx_queue); + } +free_skb: + sqn_pr_dbg("free TX skb because of error\n"); + dev_kfree_skb_any(skb); + priv->stats.tx_dropped++; + priv->stats.tx_errors++; + goto out; +} + + +/*******************************************************************/ +/* RX handlers */ +/*******************************************************************/ +static void sqn_sdio_process_rx_queue(struct work_struct *work) +{ + struct sqn_private *priv = container_of(work, struct sqn_private + , rx_work_struct); + struct sqn_sdio_card *card = (struct sqn_sdio_card*) priv->card; + struct sk_buff *skb = 0; + u8 need_to_ulock_mutex = 0; + + sqn_pr_enter(); + + sqn_pr_dbg("acquire RXQ mutex\n"); + if (!mutex_trylock(&card->rxq_mutex)) { + sqn_pr_dbg("failed to acquire RXQ mutex, it means we are going" + " to remove a network interface\n"); + need_to_ulock_mutex = 0; + goto out; + } + need_to_ulock_mutex = 1; + + while (!priv->removed && 0 != (skb = skb_dequeue(&card->rx_queue))) { + sqn_rx_process(card->priv->dev, skb); + if (waitqueue_active(&priv->rx_waitq) + && skb_queue_len(&card->rx_queue) < RX_QUEUE_WM_LEN) + { + sqn_pr_info("rx_queue len %d, enabling rx\n" + , skb_queue_len(&card->rx_queue)); + wake_up_interruptible(&priv->rx_waitq); + } + } +out: + if (need_to_ulock_mutex && mutex_is_locked(&card->rxq_mutex)) { + mutex_unlock(&card->rxq_mutex); + sqn_pr_dbg("release RXQ mutex\n"); + } + + sqn_sdio_release_wake_lock(card); + sqn_pr_leave(); +} + + +static int sqn_sdio_card_to_host(struct sqn_sdio_card *card) +{ + u16 level = 0; + int rv = 0; + u8 need_to_ulock_mutex = 0; + + sqn_pr_enter(); + + if (card->priv->removed) { + // sqn_pr_warn("%s: card/driver is removed, do nothing\n", __func__); // Andrew 0524 + goto drv_removed; + } + + sqn_pr_dbg("acquire RX mutex\n"); + if (!mutex_trylock(&card->rx_mutex)) { + sqn_pr_dbg("failed to acquire RX mutex, it means we are going" + " to remove a network interface\n"); + need_to_ulock_mutex = 0; + goto out; + } + need_to_ulock_mutex = 1; + + /* + * NOTE: call to sdio_claim_host() is already done + * in sqn_sdio_it_lsb() - our caller + */ +check_level: + /* Find out how many PDUs we have to read */ + level = sdio_readw(card->func, SQN_SDIO_RD_FIFO_LEVEL(2), &rv); + if (rv) { + sqn_pr_err("ERROR reading SDIO_RD_FIFO_LEVEL\n"); + goto out; + } + + if (level == 0) { + sqn_pr_dbg("no more PDUs to read\n"); + if (rv < 0) + sqn_pr_warn("%s: no more PDUs left but status = %d\n", __func__, rv); + goto out; + } + + sqn_pr_dbg("PDUs to read %d\n", level); + + while (!card->priv->removed && level--) { + struct sk_buff *skb = 0; +#if DUMP_NET_PKT + struct ethhdr *eth = 0; +#endif + u16 size = 0; + + /* Get the size of PDU */ + size = sdio_readw(card->func, SQN_SDIO_RDLEN_FIFO(2), &rv); + if (rv) { + sqn_pr_err("can't get FIFO read length, status = %d\n", rv); + goto out; + } + sqn_pr_dbg("PDU #%u length %u\n", (u32)level, (u32)size); + + if (size > SQN_SDIO_PDU_MAXLEN || size < 1) { + sqn_pr_err("RX PDU length %u is not correct\n", + (u32)size); + card->priv->stats.rx_length_errors++; + card->priv->stats.rx_errors++; + continue; + } + + skb = __netdev_alloc_skb(card->priv->dev, SQN_SDIO_PDU_MAXLEN + , GFP_ATOMIC | GFP_DMA); + if (0 == skb) { + sqn_pr_err("failed to alloc RX buffer\n"); + rv = -ENOMEM; + goto out; + } +#if SKB_DEBUG + sqn_pr_info("%s: alloc skb [0x%p], users %d\n", __func__, skb, atomic_read(&skb->users)); +#endif + + rv = sdio_readsb(card->func, skb->data, SQN_SDIO_RDWR_FIFO(2), + (int)size); + if (rv) { + sqn_pr_err("RX PDU read failed: %d\n", rv); + continue; + } + skb_put(skb, size); + +#if DUMP_NET_PKT + eth = (struct ethhdr *)skb->data; + if (!is_thp_packet(eth->h_dest) && !is_lsp_packet(skb)) { + sqn_pr_info("----------------------------------------------------------------------\n"); + sqn_pr_info("RX PDU length %d\n", skb->len); + sqn_pr_info_dump("RX PDU", skb->data, skb->len); + } +#endif + + if (sqn_handle_lsp_packet(card->priv, skb)) + continue; + /* + * If we have some not LSP PDUs to read, then card is not + * asleep any more, so we should notify waiters about this + */ + if (card->is_card_sleeps) { + sqn_pr_info("got RX data, card is not asleep\n"); + signal_card_sleep_completion(card->priv); + } + + if (!card->waiting_pm_notification + && !wake_lock_active(&card->wakelock)) + { + sqn_pr_dbg("lock wake_lock\n"); + wake_lock(&card->wakelock); + } + + + /* + * Don't use internal RX queue, because kernel has its own. + * Just push RX packet directly to kernel + */ + sqn_rx_process(card->priv->dev, skb); + + /* skb_queue_tail(&card->rx_queue, skb); */ + /* if (skb_queue_len(&card->rx_queue) > RX_QUEUE_MAX_LEN) { */ + /* int rv = 0; */ + /* sqn_pr_info("rx_queue len %d, wait untill it'll be processed\n" */ + /* , skb_queue_len(&card->rx_queue)); */ + /* schedule_work(&card->priv->rx_work_struct); */ + /* rv = wait_event_interruptible(card->priv->rx_waitq */ + /* , skb_queue_len(&card->rx_queue) <= RX_QUEUE_WM_LEN); */ + /* |+ */ + /* * If we've been interrupted by a signal, then we */ + /* * should stop and return */ + /* +| */ + /* if (0 != rv) { */ + /* sqn_pr_warn("got a signal from kernel %d\n", rv); */ + /* goto out; */ + /* } */ + /* sqn_pr_info("rx_queue len %d, continue RX PDUs processing\n" */ + /* , skb_queue_len(&card->rx_queue)); */ + /* } */ + } + + /* sqn_pr_dbg("rx_queue len %d\n" */ + /* , skb_queue_len(&card->rx_queue)); */ + + /* schedule_work(&card->priv->rx_work_struct); */ + + sqn_pr_dbg("check is there more PDU to read\n"); + goto check_level; +out: + sqn_sdio_release_wake_lock(card); + if (need_to_ulock_mutex && mutex_is_locked(&card->rx_mutex)) { + mutex_unlock(&card->rx_mutex); + sqn_pr_dbg("release RX mutex\n"); + } + +drv_removed: + sqn_pr_leave(); + return rv; +} + + +/*******************************************************************/ +/* Interrupt handling */ +/*******************************************************************/ + +static int sqn_sdio_it_lsb(struct sdio_func *func) +{ + struct sqn_sdio_card *card = sdio_get_drvdata(func); + int rc = 0; + u8 status = 0; + unsigned long irq_flags = 0; + u8 is_card_sleeps = 0; + + sqn_pr_enter(); + + /* NOTE: call of sdio_claim_host() is already done */ + + /* Read the interrupt status */ + status = sdio_readb(func, SQN_SDIO_IT_STATUS_LSBS, &rc); + if (rc) + goto out; + + sqn_pr_dbg("interrupt(LSB): 0x%02X\n", (unsigned char) status); + + spin_lock_irqsave(&card->priv->drv_lock, irq_flags); + is_card_sleeps = card->is_card_sleeps; + spin_unlock_irqrestore(&card->priv->drv_lock, irq_flags); + + /* Handle interrupt */ + if (status & SQN_SDIO_IT_WR_FIFO2_WM) { + sqn_pr_dbg("skipping FIFO2 write watermark interrupt...\n"); + + /* Clear interrupt flag */ + sdio_writeb(func, SQN_SDIO_IT_WR_FIFO2_WM, + SQN_SDIO_IT_STATUS_LSBS, &rc); + } + + if (status & SQN_SDIO_IT_RD_FIFO2_WM) { + rc = sqn_sdio_card_to_host(card); + if (rc) + sqn_pr_err("can't read data from card, error %d\n", rc); + + /* Clear interrupt flag */ + sdio_writeb(func, SQN_SDIO_IT_RD_FIFO2_WM, + SQN_SDIO_IT_STATUS_LSBS, &rc); + } + +out: + sqn_pr_dbg("returned code: %d\n", rc); + sqn_pr_leave(); + return rc; +} + + +static int sqn_sdio_it_msb(struct sdio_func *func) +{ + int rc = 0; + u8 status = 0; + + sqn_pr_enter(); + + /* Read the interrupt status */ + status = sdio_readb(func, SQN_SDIO_IT_STATUS_MSBS, &rc); + if (rc) + goto out; + + sqn_pr_dbg("interrupt(MSB): 0x%02X\n", (unsigned char) status); + + /* TODO: Handle interrupt */ + sqn_pr_dbg("skipping any interrupt...\n"); + + /* Clear interrupt flag */ + sdio_writeb(func, 0xff, SQN_SDIO_IT_STATUS_MSBS, &rc); + +out: + sqn_pr_dbg("returned code: %d\n", rc); + sqn_pr_leave(); + return rc; +} + + +/* + * defined in "drivers/mmc/omap2430_hsmmc.c" + * in linux kernel from TI + */ +int sdio_int_enable(int enable, int slot); + + +void sqn_sdio_interrupt(struct sdio_func *func) +{ + unsigned long irq_flags = 0; + u8 is_card_sleeps = 0; + struct sqn_sdio_card *card = sdio_get_drvdata(func); + + sqn_pr_enter(); + + sqn_sdio_it_lsb(func); + + spin_lock_irqsave(&card->priv->drv_lock, irq_flags); + is_card_sleeps = card->is_card_sleeps; + spin_unlock_irqrestore(&card->priv->drv_lock, irq_flags); + + if (!is_card_sleeps) + sqn_sdio_it_msb(func); + + sqn_pr_leave(); +} + + +static int sqn_sdio_it_enable(struct sdio_func *func) +{ + u8 enable = 0; + int rv = 0; + + sqn_pr_enter(); + sdio_claim_host(func); + + /* enable LSB */ + enable = SQN_SDIO_IT_WR_FIFO2_WM | SQN_SDIO_IT_RD_FIFO2_WM | + SQN_SDIO_IT_SW_SIGN; + + sdio_writeb(func, enable, SQN_SDIO_IT_EN_LSBS, &rv); + sqn_pr_dbg("enabled LSBS interrupt: rv=0x%02X\n", rv); + if (rv) + goto out; + + sqn_pr_dbg("enabled interrupt(LSB): 0x%02X\n", + (unsigned char) enable); + + /* Set RD watermark to enable interrups for RX packets */ + sdio_writew(func, 1, SQN_SDIO_WM_RD_FIFO(2), &rv); + sqn_pr_dbg("enabled rd watermark: rv=%d\n", rv); + if (rv) { + sqn_pr_err("can't enable rd watermark: rv=%d\n", rv); + goto out; + } +out: + sdio_release_host(func); + sqn_pr_dbg("returned code: %d\n", rv); + sqn_pr_leave(); + return rv; +} + + +static int sqn_sdio_it_disable(struct sdio_func *func) +{ + int rc = 0; + + sqn_pr_enter(); + sdio_claim_host(func); + + /* disable LSB */ + sdio_writeb(func, 0, SQN_SDIO_IT_EN_LSBS, &rc); + if (rc) + goto out; + sqn_pr_dbg("disabled interrupt(LSB)\n"); + + /* disable MSB */ + sdio_writeb(func, 0, SQN_SDIO_IT_EN_MSBS, &rc); + if (rc) + goto out; + sqn_pr_dbg("disabled interrupt(MSB)\n"); +out: + sqn_pr_dbg("returned code: %d\n", rc); + sdio_release_host(func); + sqn_pr_leave(); + return rc; +} + + +void sqn_sdio_stop_it_thread_from_itself(struct sqn_private *priv) +{ + struct sqn_sdio_card *card = priv->card; + unsigned long irq_flags = 0; + sqn_pr_enter(); + + spin_lock_irqsave(&priv->drv_lock, irq_flags); + card->it_thread_should_stop = 1; + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + + sqn_pr_leave(); +} + +/*******************************************************************/ +/* Driver registration */ +/*******************************************************************/ + +#ifdef DEBUG +static void sqn_sdio_debug_test(struct sdio_func *func) +{ + /* int rc = 0; */ + /* int val = 0; */ + + sqn_pr_enter(); + sdio_claim_host(func); + + /* sqn_pr_dbg("write SQN_SOC_SIGS_LSBS\n"); */ + /* sdio_writeb(func, 1, SQN_SOC_SIGS_LSBS, &rc); */ + /* if (rc) */ + /* sqn_pr_dbg("error when writing to SQN_SOC_SIGS_LSBS: %d\n", rc); */ + +#if 0 + sqn_pr_dbg("readb 0x04\n"); + val = sdio_readb(func, 0x04, &rc); + if (rc) + sqn_pr_dbg("readb 0x04 failed %x\n", rc); + else + sqn_pr_dbg("readb 0x04 = %x\n", val); + + sqn_pr_dbg("readb 0x2028\n"); + val = sdio_readb(func, 0x2028, &rc); + if (rc) + sqn_pr_dbg("readb 0x2028 failed %x\n", rc); + else + sqn_pr_dbg("readb 0x2028 = %x\n", val); + + sqn_pr_dbg("readw 0x2028\n"); + val = sdio_readw(func, 0x2028, &rc); + if (rc) + sqn_pr_dbg("readw 0x2028 failed %x\n", rc); + else + sqn_pr_dbg("readw 0x2028 = %x\n", val); + + sqn_pr_dbg("readb RSTN\n"); + val = sdio_readb(func, SQN_SDIO_RSTN_WR_FIFO(2), &rc); + if (rc) + sqn_pr_dbg("readb RSTN failed %x\n", rc); + else + sqn_pr_dbg("readb RSTN = %x\n", val); + + sqn_pr_dbg("readl LEVEL\n"); + val = sdio_readw(func, SQN_SDIO_WR_FIFO_LEVEL(2), &rc); + if (rc) + sqn_pr_dbg("readl LEVEL failed %x\n", rc); + else + sqn_pr_dbg("readl LEVEL = %x\n", val); + + sqn_pr_dbg("readl 0x2060\n"); + val = sdio_readl(func, 0x2060, &rc); + if (rc) + sqn_pr_dbg("readl 0x2060 failed %x\n", rc); + else + sqn_pr_dbg("readl 0x2060 = %x\n", val); + + sqn_pr_dbg("writew SQN_SDIO_WM_RD_FIFO(2)\n"); + sdio_writel(func, 1, SQN_SDIO_WM_RD_FIFO(2), &rc); + if (rc) + sqn_pr_dbg("writel SQN_SDIO_WM_RD_FIFO(2) failed %x\n", rc); + else + sqn_pr_dbg("writel SQN_SDIO_WM_RD_FIFO(2) = %x\n", rc); +#endif + + sdio_release_host(func); + sqn_pr_leave(); +} + + +static void sqn_sdio_print_debug_info(struct sdio_func *func) +{ + sqn_pr_enter(); + + sqn_pr_info("sdio_func: device[%02x]: %04x:%04x\n", func->class, func->vendor, + func->device); + sqn_pr_info("sdio_func: block size: %d (maximum %d)\n", func->cur_blksize, + func->max_blksize); + sqn_pr_info("sdio_func: func->state: 0x%04x, card->state: 0x%04x\n" + , func->state, func->card->state); + sqn_pr_info("mmc_bus: clock=%u, width=%u, mode=%u, vdd=%u\n" + , func->card->host->ios.clock + , func->card->host->ios.bus_width + , func->card->host->ios.bus_mode + , func->card->host->ios.vdd); + + sqn_pr_dbg("host->caps=%x\n", (u32) func->card->host->caps); + + sqn_pr_leave(); +} +#endif /* DEBUG */ + + +static void sqn_sdio_free_tx_queue(struct sqn_sdio_card *card) +{ + struct sk_buff *skb = 0; + while (0 != (skb = skb_dequeue(&card->tx_queue))) + dev_kfree_skb_any(skb); +} + + +static void sqn_sdio_free_rx_queue(struct sqn_sdio_card *card) +{ + struct sk_buff *skb = 0; + while (0 != (skb = skb_dequeue(&card->rx_queue))) + dev_kfree_skb_any(skb); +} + + +static int check_boot_from_host_mode(struct sdio_func *func) +{ + int rv = 0; + int status = 0; + + sdio_claim_host(func); + status = sdio_readb(func, SQN_H_BOOT_FROM_SPI, &rv); + sdio_release_host(func); + + if (rv) + { + sqn_pr_err("can't read boot flags from device"); + return 0; + } + + return !status; +} + + +static int sqn_check_card_id(struct sdio_func *func) +{ + int rv = 0; + unsigned short manf_id = 0; + unsigned short card_id = 0; + + sqn_pr_enter(); + sqn_pr_info("Checking card IDs...\n"); + + manf_id = sdio_readw(func, SDIO_CMN_CISTPLMID_MANF, &rv); + if (rv) { + sqn_pr_err("can't read card manufacturer id\n"); + rv = 0; + goto out; + } + + card_id = sdio_readw(func, SDIO_CMN_CISTPLMID_CARD, &rv); + if (rv) { + sqn_pr_err("can't read card id\n"); + rv = 0; + goto out; + } + + if (manf_id != SDIO_VENDOR_ID_SEQUANS + || card_id != SDIO_DEVICE_ID_SEQUANS_SQN1130) + { + sqn_pr_info("found card with UNSUPPORTED manf_id=%x card_id=%x\n" + , manf_id, card_id); + rv = 0; + } else { + sqn_pr_info("found card with SUPPORTED manf_id=%x card_id=%x\n" + , manf_id, card_id); + rv = 1; + } + +out: + sqn_pr_leave(); + return rv; +} + + +static u8 sqn_get_card_version(struct sdio_func *func) +{ + int rv = 0; + u32 version = 0; + + sqn_pr_enter(); + + switch (func->device) { + case SDIO_DEVICE_ID_SEQUANS_SQN1130: + sqn_pr_info("found SQN1130 card\n"); + /* + * Let bootrom/firmware name to be overridden from userspace as + * a module parameter, so we change it only if it was not + * changed from its default value + */ + if (0 == strcmp(firmware_name, SQN_DEFAULT_FW_NAME)) + firmware_name = fw1130_name; + rv = SQN_1130; + break; + case SDIO_DEVICE_ID_SEQUANS_SQN1210: + sqn_pr_info("found SQN1210 card\n"); + /* + * Let firmware_name to be overridden from userspace as a module + * parameter, so we change firmware_name only if it was not + * changed from its default value + */ + if (0 == strcmp(firmware_name, SQN_DEFAULT_FW_NAME)) + firmware_name = fw1210_name; + rv = SQN_1210; + break; + default: + sqn_pr_info("found UNKNOWN card with vendor_id 0x%x" + " dev_id 0x%x\n", func->vendor, func->device); + rv = 0; + } + +/* Maintain in compilable state but don't use it for now */ +#if 0 +/* + * For production devices this is not needed, we can get a device id + * from sdio_func + */ + sqn_pr_info("Checking card version...\n"); + + sdio_claim_host(func); + version = sdio_readl(func, SQN_H_VERSION, &rv); + sdio_release_host(func); + if (rv) { + sqn_pr_err("failed to read card version\n"); + rv = 0; + goto out; + } + +#define SQN1130_MAJOR_VERSION 0x06 +#define SQN12x0_MAJOR_VERSION 0x0a + + if (SQN1130_MAJOR_VERSION == (version & 0xff)) + { + sqn_pr_info("found SQN_1130 card with version id 0x%x\n" + , version); + rv = SQN_1130; + } else if (SQN12x0_MAJOR_VERSION == (version & 0xff)) { + sqn_pr_info("found SQN_1210 card with version id 0x%x\n" + , version); + rv = SQN_1210; + } else { + sqn_pr_info("found UNKNOWN card with version id 0x%x\n" + , version); + rv = 0; + } +#endif + +out: + sqn_pr_leave(); + return rv; +} + + +extern u8 _g_card_sleeps; +extern struct sqn_private *g_priv; +struct msmsdcc_host; + +int msmsdcc_enable_clocks(struct msmsdcc_host *host); + +static irqreturn_t wimax_wakeup_gpio_irq_handler(int irq, void *dev_id) +{ + struct sqn_sdio_card *card = g_priv->card; + struct msmsdcc_host *msm_host = mmc_priv(card->func->card->host); + + sqn_pr_enter(); + +#if SDIO_CLK_DEBUG + /* Please, don't disable this log, it will be printed not often, only + * once when host is in sleep mode */ + // sqn_pr_info("WiMAX GPIO interrupt\n"); +#endif + + msmsdcc_enable_clocks(msm_host); + + + sqn_pr_leave(); + return IRQ_HANDLED; + +} + +int init_thp_handler(struct net_device *dev); +void cleanup_thp_handler(void); + +static int sqn_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + int rv = 0; + struct sqn_sdio_card *sqn_card = 0; + struct sqn_private *priv = 0; + int counter = 0; + int delay = 0; + + int err; + u32 irq; + u32 req_flags = IRQF_TRIGGER_RISING; + + sqn_pr_enter(); + + sqn_pr_info("module parameters: firmware_name='%s' load_firmware=%d\n" + , firmware_name, load_firmware); + +#ifdef DEBUG + sqn_sdio_print_debug_info(func); + /* sqn_sdio_debug_test(func); */ +#endif + + /* Allocate card's private data storage */ + sqn_card = kzalloc(sizeof(struct sqn_sdio_card), GFP_KERNEL); + if (!sqn_card) { + rv = -ENOMEM; + goto out; + } + + sqn_card->version = sqn_get_card_version(func); + + if (0 == sqn_card->version) { + rv = -EPROTO; + goto free_card; + } + + skb_queue_head_init(&sqn_card->tx_queue); + skb_queue_head_init(&sqn_card->rx_queue); + init_waitqueue_head(&sqn_card->pm_waitq); + mutex_init(&sqn_card->tx_mutex); + mutex_init(&sqn_card->rx_mutex); + mutex_init(&sqn_card->rxq_mutex); + + wake_lock_init(&sqn_card->wakelock, WAKE_LOCK_SUSPEND, "sqnsdio"); + setup_timer(&sqn_card->wakelock_timer + , sqn_sdio_wake_lock_release_timer_fn + , (unsigned long) sqn_card); + + sqn_card->func = func; + + /* Activate SDIO function and register interrupt handler */ + sdio_claim_host(func); + + rv = sdio_enable_func(func); + if (rv) + goto release; + + rv = sdio_claim_irq(func, sqn_sdio_interrupt); + if (rv) + goto disable; + + sdio_release_host(func); + + sdio_set_drvdata(func, sqn_card); + priv = sqn_add_card(sqn_card, &func->dev); + if (!priv) { + rv = -ENOMEM; + goto reclaim; + } + + sqn_card->priv = priv; + + INIT_WORK(&priv->rx_work_struct, sqn_sdio_process_rx_queue); + priv->card = sqn_card; + priv->hw_host_to_card = sqn_sdio_host_to_card; + priv->add_skb_to_tx_queue = sqn_sdio_add_skb_to_tx_queue; + priv->is_tx_queue_empty = sqn_sdio_is_tx_queue_empty; + + /* Load firmware if card needs it */ + if (check_boot_from_host_mode(sqn_card->func)) + { + rv = sqn_load_firmware(sqn_card->func); + if (rv) + goto err_activate_card; + } + + memcpy(priv->dev->dev_addr, priv->mac_addr, ETH_ALEN); + + rv = sqn_start_card(priv); + if (rv) + goto err_activate_card; + + /* We need to setup thp_handler now, to catch all THP packets + * as soon as they appear after interrupts are enabled + */ + rv = init_thp_handler(priv->dev); + if (rv) + goto unreg_netdev; + + /* Enable interrupts, now everything is set up */ + rv = sqn_sdio_it_enable(sqn_card->func); + if (rv) + goto clean_thp_handler; + + sqn_pr_info("wait until FW is started...\n"); + counter = 20; + delay = 500; + while (0 == sqn_sdio_get_rstn_wr_fifo_flag(priv) && --counter > 0) { + sqn_pr_dbg("FW is not started yet, sleep for %d msecs," + " %d retries left\n" + , delay + , counter); + msleep(delay); + } + + if (0 == sqn_card->rstn_wr_fifo_flag) + sqn_pr_warn("FW is still not started, anyway continue as is...\n"); + + sqn_pr_info("setup GPIO40 for wakeup form SQN1210\n"); + rv = irq = MSM_GPIO_TO_INT(40); //GPIO_40 as wakeup + + if (rv < 0) { + sqn_pr_warn("wimax-gpio to irq failed\n"); + goto disable; + } + + rv = request_irq(irq, wimax_wakeup_gpio_irq_handler, + req_flags, "WiMAX0", sqn_card->priv->dev); + if (rv) { + sqn_pr_warn("wimax-gpio request_irq failed=%d\n", rv); + goto disable; + } + + sqn_pr_dbg("disable GPIO40 interrupt\n"); + disable_irq(MSM_GPIO_TO_INT(40)); + + rv = init_thp(priv->dev); + if (rv) + goto clean_thp_handler; + +#ifdef DEBUG + /* sqn_sdio_debug_test(sqn_card->func); */ +#endif + +out: + sqn_pr_dbg("returned code: %d\n", rv); + if (0 == rv) + sqn_pr_info("card initialized successfuly\n"); + sqn_pr_leave(); + return rv; + +clean_thp: + cleanup_thp(); +clean_thp_handler: + cleanup_thp_handler(); +unreg_netdev: + unregister_netdev(priv->dev); +err_activate_card: + flush_scheduled_work(); + free_netdev(priv->dev); +reclaim: + sdio_claim_host(func); + sdio_release_irq(func); +disable: + sdio_disable_func(func); +release: + sdio_release_host(func); + sqn_sdio_free_tx_queue(sqn_card); + sqn_sdio_free_rx_queue(sqn_card); + + /* release a wake_lock if it was not done for a some reason */ + if (wake_lock_active(&sqn_card->wakelock)) { + sqn_pr_dbg("wake_lock is active, release it\n"); + wake_unlock(&sqn_card->wakelock); + } + + wake_lock_destroy(&sqn_card->wakelock); + +free_card: + kfree(sqn_card); + + goto out; +} + + +static void sqn_sdio_remove(struct sdio_func *func) +{ + struct sqn_sdio_card *sqn_card = sdio_get_drvdata(func); + u8 count = 0; + u32 delay = 0; + int rv = 0; + + sqn_pr_enter(); + + sqn_pr_info("free GPIO40 interrupt\n"); + free_irq(MSM_GPIO_TO_INT(40),sqn_card->priv->dev); + +#if defined(DEBUG) + sqn_sdio_print_debug_info(func); +#endif + cleanup_thp(); + + /* + * Let all running threads know that we are starting + * a remove procedure + */ + sqn_card->priv->removed = 1; + delay = 1000; + + sqn_sdio_it_disable(sqn_card->func); + + sqn_pr_info("wait until RX is finished\n"); + count = 5; + while (--count && !(rv = mutex_trylock(&sqn_card->rx_mutex))) + mdelay(delay); + if (!rv) + sqn_pr_warn("%s: failed to acquire RX mutex\n", __func__); + + sqn_stop_card(sqn_card->priv); + kthread_stop(sqn_card->priv->tx_thread); + wake_up_interruptible(&sqn_card->priv->tx_waitq); + + sqn_pr_info("wait until TX is finished\n"); + count = 5; + while (--count && !(rv = mutex_trylock(&sqn_card->tx_mutex))) + mdelay(delay); + if (!rv) + sqn_pr_warn("%s: failed to acquire TX mutex\n", __func__); + + sdio_claim_host(func); + sdio_release_irq(func); + sdio_disable_func(func); + sdio_release_host(func); + + sqn_remove_card(sqn_card->priv); + + sqn_sdio_free_tx_queue(sqn_card); + sqn_sdio_free_rx_queue(sqn_card); + + del_timer_sync(&sqn_card->wakelock_timer); + /* release a wake_lock if it was not done for a some reason */ + if (wake_lock_active(&sqn_card->wakelock)) { + sqn_pr_dbg("wake_lock is active, release it\n"); + wake_unlock(&sqn_card->wakelock); + } + + wake_lock_destroy(&sqn_card->wakelock); + + kfree(sqn_card); + sdio_set_drvdata(func, 0); + + sqn_pr_info("card removed successfuly\n"); + mmc_detect_change(func->card->host, msecs_to_jiffies(500)); + sqn_pr_leave(); +} + +u8 sqn_is_gpio_irq_enabled = 0; +int sqn_sdio_suspend(struct sdio_func *func, pm_message_t msg) +{ + int rv = 0; + /* unsigned long irq_flags = 0; */ + struct sqn_sdio_card *sqn_card = sdio_get_drvdata(func); + + sqn_pr_enter(); + sqn_pr_info("%s: enter\n", __func__); + sqn_pr_dbg("pm_message = %x\n", msg.event); + + WARN(!skb_queue_empty(&sqn_card->tx_queue) + , "BANG!!! TX queue is not empty in suspend(): %d" + , skb_queue_len(&sqn_card->tx_queue)); + + WARN(!skb_queue_empty(&sqn_card->rx_queue) + , "BANG!!! RX queue is not empty in suspend(): %d" + , skb_queue_len(&sqn_card->rx_queue)); + + if (sqn_card->is_card_sleeps) { + sqn_pr_info("card already asleep (pm_message = 0x%x)\n" + , msg.event); + goto out; + } + + /* Do nothing when system goes to power off */ + if (PM_EVENT_SUSPEND != msg.event) { + sqn_pr_warn("Not supported pm_message = %x\n", msg.event); + goto out; + } + + if (sqn_notify_host_sleep(func)) { + sqn_pr_warn("Failed to suspend\n"); + goto out; + } +out: + + if (!sqn_is_gpio_irq_enabled) { + sqn_pr_info("enable GPIO40 interrupt\n"); + enable_irq(MSM_GPIO_TO_INT(40)); + enable_irq_wake(MSM_GPIO_TO_INT(40)); + sqn_is_gpio_irq_enabled = 1; + } + + sqn_pr_info("%s: leave\n", __func__); + sqn_pr_leave(); + return rv; +} + + +int sqn_sdio_resume(struct sdio_func *func) +{ + int rv = 0; + struct sqn_sdio_card *sqn_card = sdio_get_drvdata(func); + + sqn_pr_enter(); + sqn_pr_info("%s: enter\n", __func__); + + if (netif_queue_stopped(sqn_card->priv->dev)) { + sqn_pr_dbg("wake netif_queue\n"); + netif_wake_queue(sqn_card->priv->dev); + } + + // Dima: we don't need this, card will be woken up when there will be + // some TX data + /* sqn_notify_host_wakeup(func); */ + + if (sqn_is_gpio_irq_enabled) { + sqn_pr_info("disable GPIO40 interrupt\n"); + disable_irq_wake(MSM_GPIO_TO_INT(40)); + disable_irq(MSM_GPIO_TO_INT(40)); + sqn_is_gpio_irq_enabled = 0; + } + + sqn_pr_info("%s: leave\n", __func__); + sqn_pr_leave(); + return rv; +} + + +static struct sdio_driver sqn_sdio_driver = { + .name = SQN_MODULE_NAME + , .id_table = sqn_sdio_ids + , .probe = sqn_sdio_probe + , .remove = sqn_sdio_remove +#if 0 +def ANDROID_KERNEL + , .suspend = sqn_sdio_suspend + , .resume = sqn_sdio_resume +#endif +}; + + +/*******************************************************************/ +/* Module initialization */ +/*******************************************************************/ + +static int __init sqn_sdio_init_module(void) +{ + int rc = 0; + + sqn_pr_enter(); + + sqn_pr_info("Sequans SDIO WiMAX driver, version %s\n" + , SQN_MODULE_VERSION); + sqn_pr_info("Copyright SEQUANS Communications\n"); + + // printk(KERN_WARNING "------------ %s ------------\n", __FUNCTION__); + mmc_wimax_power(1); + mmc_wimax_set_carddetect(1); + // thp_wimax_uart_switch(1); + mmc_wimax_set_status(1); + + rc = sdio_register_driver(&sqn_sdio_driver); + +#ifdef ANDROID_KERNEL + register_android_earlysuspend(); +#endif /* TI_KERNEL */ + + sqn_pr_info("Driver has been registered\n"); + + sqn_pr_leave(); + + return rc; +} + +static void __exit sqn_sdio_exit_module(void) +{ + sqn_pr_enter(); + + sdio_unregister_driver(&sqn_sdio_driver); + +#ifdef ANDROID_KERNEL + unregister_android_earlysuspend(); +#endif + + sqn_pr_info("Driver has been removed\n"); + + mmc_wimax_set_carddetect(0); + mmc_wimax_power(0); + // thp_wimax_uart_switch(0); + mmc_wimax_set_status(0); + + sqn_pr_leave(); +} + + +module_init(sqn_sdio_init_module); +module_exit(sqn_sdio_exit_module); + + +MODULE_DESCRIPTION("Sequans WiMAX driver for SDIO devices"); +MODULE_AUTHOR("Dmitriy Chumak, Andy Shevchenko"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(SQN_MODULE_VERSION); diff --git a/drivers/net/wimax/SQN/sdio.h b/drivers/net/wimax/SQN/sdio.h new file mode 100644 index 0000000000000..564897bcffbbe --- /dev/null +++ b/drivers/net/wimax/SQN/sdio.h @@ -0,0 +1,18 @@ +/* + * This is part of the Sequans SQN1130 driver. + * Copyright 2008 SEQUANS Communications + * Written by Dmitriy Chumak + * + * 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. + */ + +#ifndef _SQN_SDIO_WRAPPERS_H +#define _SQN_SDIO_WRAPPERS_H + +#include +#include + +#endif /* _SQN_SDIO_WRAPPERS_H */ diff --git a/drivers/net/wimax/SQN/thp.c b/drivers/net/wimax/SQN/thp.c new file mode 100644 index 0000000000000..58f9ba13c3a9b --- /dev/null +++ b/drivers/net/wimax/SQN/thp.c @@ -0,0 +1,759 @@ +/* + * This is part of the Sequans SQN1130 driver. + * Copyright 2009 SEQUANS Communications + * + * 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. + */ + + +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) +#include +#endif +#include +#include +#include +#include +#include +#include +#include + + +#include "version.h" +#include "msg.h" +#include "thp.h" + +#include "thp_ioctl.h" + +#define THP_TRACE 0 /* print info messages from THP read/write handlers */ +#define THP_HEADER_DUMP 0 /* verbosely dump header of THP TX/RX packets */ + +#define THP_DEBUG 0 +#define SKB_DEBUG 0 +#define DRVREV SQN_MODULE_VERSION + +extern bool drop_packet; + +const uint8_t host_macaddr[ETH_ALEN] = {0x00, 0x16, 0x08, 0xff, 0x00, 0x01}; +const uint8_t ss_macaddr[ETH_ALEN] = {0x00, 0x16, 0x08, 0xff, 0x00, 0x00}; + + +// Queue of packets destined to the Connection Manager +// TODO: check size of the queue, it's should always be one. +struct sk_buff_head to_sqntool_queue; + +DECLARE_WAIT_QUEUE_HEAD(to_sqntool_wait); + +struct net_device *this_device = NULL; + +struct packet_type rx_packet_type = { 0 }; +extern int mmc_wimax_uart_switch(int uart); + +uint8_t is_thp_packet(uint8_t *dest_addr) +{ + return (memcmp(dest_addr, host_macaddr, ETH_ALEN)==0); +} + +inline struct ethhdr *skb2ethhdr(const struct sk_buff *skb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) + return (struct ethhdr *)skb->mac.raw; +#else + return (struct ethhdr *)skb_mac_header(skb); +#endif +} + + +/* TODO: Fix kernel version */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) +int thp_handler(struct sk_buff *skb, struct net_device *pDev, struct packet_type *pPt, struct net_device *pOrigDev) +#else +int thp_handler(struct sk_buff *skb, struct net_device *pDev, struct packet_type *pPt) +#endif +{ + struct sk_buff *skb_thp = 0; + struct ethhdr *eth = 0; + + sqn_pr_enter(); + + /* We need only ETH_P_802_2 protocol packets with THP mac address */ + eth = skb2ethhdr(skb); + if(ntohs(skb->protocol) != ETH_P_802_2 || !is_thp_packet(eth->h_dest)) { + //for DDTM, drop all NOT THP packets + if(drop_packet) { + sqn_pr_dbg("HTC CODE: drop packet for DDTM\n"); + skb->pkt_type = PACKET_OTHERHOST; + } + goto not_thp_out; + } + + skb_thp = skb_clone(skb, GFP_ATOMIC); + /* Bugz 22554: strip CRC at the end of packet */ + skb_trim(skb_thp, skb_thp->len - 4); + +#if THP_TRACE + sqn_pr_info("%s: RX packet, len = %d\n", __func__, skb_thp->len); +#endif + sqn_pr_dbg("RX THP packet, length %d\n", skb_thp->len); + skb_queue_tail(&to_sqntool_queue, skb_thp); + + if(skb_queue_len(&to_sqntool_queue) == 256){ + skb_thp = skb_dequeue(&to_sqntool_queue); + kfree_skb(skb_thp); + } + + wake_up_interruptible(&to_sqntool_wait); //Wake up wait queue + +thp_out: + dev_kfree_skb_any(skb); + sqn_pr_leave(); + return NET_RX_DROP; +not_thp_out: + dev_kfree_skb_any(skb); + sqn_pr_leave(); + return NET_RX_SUCCESS; +} + +// Initialization function for THP handler +int init_thp_handler(struct net_device *dev) +{ + sqn_pr_enter(); +#if THP_DEBUG + printk(KERN_WARNING "init_thp_handler +\n"); +#endif + + skb_queue_head_init(&to_sqntool_queue); + + /* Define type of intercepted packets */ + rx_packet_type.type = htons(ETH_P_ALL); /* Intercept all packets */ + rx_packet_type.dev = dev; + rx_packet_type.func = thp_handler; /* Network packet handler function */ + + /* Register packet handler */ + dev_add_pack(&rx_packet_type); + +#if THP_DEBUG + printk(KERN_WARNING "init_thp_handler -\n"); +#endif + sqn_pr_leave(); + + return 0; +} + +// Clean up function for THP handler +void cleanup_thp_handler(void) +{ + sqn_pr_enter(); +#if THP_DEBUG + printk(KERN_WARNING "cleanup_thp_handler +\n"); +#endif + + /* unregister packet handler */ + dev_remove_pack(&rx_packet_type); + + if(!skb_queue_empty(&to_sqntool_queue)) + skb_queue_purge(&to_sqntool_queue) ; + +#if THP_DEBUG + printk(KERN_WARNING "cleanup_thp_handler -\n"); +#endif + sqn_pr_leave(); +} + +#define PROC_DIR_NAME "kthp" +#define DRV_REVISION "kthp/drvrev" +#define IFACE_FILENAME "kthp/iface_name" + +char procfs_dir[64] = PROC_DIR_NAME; + +static struct proc_dir_entry* kthp_proc_dir; + +extern struct net_device *this_device; + +/** PROC_FS Read Functions */ + +static int ifacename_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + + sqn_pr_enter(); +#if THP_DEBUG + printk(KERN_WARNING "ifacename_read +\n"); +#endif + + if(this_device) + len += sprintf(page, "%s\n", this_device->name); + + *eof = 1; + +#if THP_DEBUG + printk(KERN_WARNING "ifacename_read -\n"); +#endif + sqn_pr_leave(); + + return len; +} + + +static int drvrev_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + + sqn_pr_enter(); +#if THP_DEBUG + printk(KERN_WARNING "drvrev_read +\n"); +#endif + + len += sprintf(page, "%s\n", SQN_MODULE_VERSION); + + *eof = 1; + + sqn_pr_leave(); + + return len; +} + +static int install_entry(char *entry_name, read_proc_t* read_func) +{ + struct proc_dir_entry* proc; + + sqn_pr_enter(); + + proc = create_proc_read_entry(entry_name, S_IFREG | S_IRUGO | S_IWUSR + , NULL, (read_proc_t*)read_func, NULL); + + if (proc) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) + proc->owner = THIS_MODULE; +#endif + } else { + printk(KERN_ALERT"/proc/ %s failed", entry_name); + return 1; + } + + sqn_pr_leave(); + + return 0; +} + +int init_procfs_handler(void) +{ + sqn_pr_enter(); + + kthp_proc_dir = proc_mkdir(procfs_dir, NULL); + + if (kthp_proc_dir) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) + kthp_proc_dir->owner = THIS_MODULE; +#endif + } else { + remove_proc_entry(PROC_DIR_NAME, NULL); + return 1; + } + + if(install_entry(IFACE_FILENAME, ifacename_read) || + install_entry(DRV_REVISION, drvrev_read)) + { + return 1; + } + +#if THP_DEBUG + printk(KERN_WARNING "drvrev_read -\n"); +#endif + sqn_pr_leave(); + + return 0; +} + +void cleanup_procfs_handler(void) +{ + sqn_pr_enter(); +#if THP_DEBUG + printk(KERN_WARNING "cleanup_procfs_handler +\n"); +#endif + + remove_proc_entry(IFACE_FILENAME, NULL); + remove_proc_entry(DRV_REVISION, NULL); + + if (kthp_proc_dir) + remove_proc_entry(procfs_dir, NULL); + +#if THP_DEBUG + printk(KERN_WARNING "cleanup_procfs_handler -\n"); +#endif + sqn_pr_leave(); +} + + +#define THP_FILENAME "thp" + + +static dev_t dev_num; +//static int dev_index; +static struct cdev *thp_dev; +static struct class *thp_class; +static struct device *thp_device; + +static uint8_t once_open_flag = 0; + +const char thp_filename[64] = THP_FILENAME; + +/********************File operations*****************************/ +static int thp_open(struct inode*, struct file*); + +static ssize_t thp_release(struct inode*, struct file*); + +static ssize_t thp_read(struct file*, char*, size_t, loff_t*); + +static ssize_t thp_write(struct file *file, const char *buf, + size_t count, loff_t *ppos); + +static unsigned int thp_poll(struct file *filp, poll_table *wait); + +static int thp_ioctl(struct inode*, struct file*, unsigned int, unsigned long); + +struct file_operations thp_fops = +{ + .owner = THIS_MODULE + , .open = thp_open + , .release= thp_release + , .read = thp_read + , .write= thp_write + , .poll = thp_poll + , .ioctl = thp_ioctl +}; + +/********************** File Operations BEGIN *****************************/ + +static int thp_open(struct inode * inode, struct file * filp) +{ + sqn_pr_enter(); +#if THP_DEBUG + printk(KERN_WARNING "thp_open +\n"); +#endif + + // allow multiple open() call for supporting ioctl on HTC Supersonic + /* + if(once_open_flag) + return -EBUSY; + */ + + once_open_flag = 1; + +#if THP_DEBUG + printk(KERN_WARNING "thp_open -\n"); +#endif + sqn_pr_leave(); + + return 0; +} + +static ssize_t thp_release(struct inode *inode, struct file *filp) +{ + sqn_pr_enter(); + + once_open_flag = 0; + + if(!skb_queue_empty(&to_sqntool_queue)) + skb_queue_purge(&to_sqntool_queue); + + sqn_pr_leave(); + + return 0; +} + +static ssize_t thp_read(struct file *filp, char *buf, size_t count, loff_t*ppos) +{ + DECLARE_WAITQUEUE(wait, current); + + struct sk_buff_head *head = &to_sqntool_queue; + struct sk_buff *curr = NULL; + ssize_t retval; +#if THP_HEADER_DUMP + const struct sqn_thp_header *th = 0; +#endif + sqn_pr_enter(); +#if THP_DEBUG + printk(KERN_WARNING "thp_read +\n"); +#endif + + add_wait_queue(&to_sqntool_wait, &wait); + retval = -ERESTARTSYS; + + if(0 == this_device) { + printk(KERN_WARNING "thp_read() device removed\n"); + retval = -EINVAL; + goto out; + } + + while(1) + { + if(!skb_queue_empty(head)) + break; + if(signal_pending(current) || 0 == this_device) { + printk(KERN_WARNING "thp_read() interrupted by signal\n"); + retval = -EINTR; + goto out; + } + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + curr = skb_dequeue(head); + + if (count < curr->len) { + printk(KERN_WARNING "%s: userspace buffer is too small (%u bytes)" + " to hold THP packet (%u bytes)\n" + , __func__, count, curr->len); + retval = -EINVAL; + goto free_skb; + } else { + count = curr->len; + } + + if(copy_to_user(buf, curr->data, count)) + { + printk(KERN_ERR "error copying data to user space\n"); + retval = -EFAULT; + goto free_skb; + } +#if THP_TRACE + sqn_pr_info("%s: [to_user]: len = %d\n", __func__, count); +#endif +#if THP_HEADER_DUMP + th = (struct sqn_thp_header *) curr->data; + sqn_pr_info("%s: PKTLen: %4u | TVer: 0x0%x | Flags: 0x0%x | Len: %4u" + " | SeqNum: %5u | AckNum: %5u | TLen: %5u\n", __func__ + , count + , th->transport_version + , th->flags + , be16_to_cpu(th->length) + , be16_to_cpu(th->seq_number) + , be16_to_cpu(th->ack_number) + , be32_to_cpu(th->total_length)); +#endif + sqn_pr_dbg("[to_user]: len = %d\n", count); +#ifdef SQN_DEBUG_DUMP + sqn_pr_dbg_dump("RX:", curr->data, count); +#endif + +#if SKB_DEBUG + sqn_pr_info("%s: free skb [0x%p], users %d\n", __func__, curr, atomic_read(&curr->users)); +#endif + + retval = (ssize_t)count; +free_skb: + + dev_kfree_skb_any(curr); + +out: + set_current_state(TASK_RUNNING); + remove_wait_queue(&to_sqntool_wait, &wait); + +#if THP_DEBUG + printk(KERN_WARNING "thp_read -\n"); +#endif + sqn_pr_leave(); + + return retval; +} + + +static ssize_t thp_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + + ssize_t retval = -ENOMEM; + struct sk_buff *skb; + struct ethhdr ethh; + int size = count + ETH_HLEN; +#if THP_HEADER_DUMP + const struct sqn_thp_header *th = 0; +#endif + + sqn_pr_enter(); +#if THP_DEBUG + printk(KERN_WARNING "thp_write +\n"); +#endif + + if(0 == this_device) + return -ENODEV; + + skb = __dev_alloc_skb(size, GFP_ATOMIC | GFP_DMA); + if(skb == NULL) + return retval; + +#if SKB_DEBUG + sqn_pr_info("%s: [0x%p] alloc skb, users %d\n", __func__, skb, atomic_read(&skb->users)); +#endif + + memcpy(ethh.h_dest, ss_macaddr, ETH_ALEN); + memcpy(ethh.h_source, host_macaddr, ETH_ALEN); + ethh.h_proto = htons(count); + + memcpy(skb->data, ðh, sizeof(struct ethhdr)); + skb_put(skb, sizeof(struct ethhdr)); + + if(copy_from_user(skb->tail, buf, count)) { + dev_kfree_skb_any(skb); + return -EFAULT; + } + skb_put(skb, count); +#if THP_TRACE + sqn_pr_info("%s: [from_user]: len = %d\n", __func__, count); +#endif +#if THP_HEADER_DUMP + th = (struct sqn_thp_header *) buf; + sqn_pr_info("%s: PKTLen: %4u | TVer: 0x0%x | Flags: 0x0%x | Len: %4u" + " | SeqNum: %5u | AckNum: %5u | TLen: %5u\n", __func__ + , count + , th->transport_version + , th->flags + , be16_to_cpu(th->length) + , be16_to_cpu(th->seq_number) + , be16_to_cpu(th->ack_number) + , be32_to_cpu(th->total_length)); +#endif + sqn_pr_dbg("[from_user]: len = %d\n", count); +#ifdef SQN_DEBUG_DUMP + sqn_pr_dbg_dump("TX:", skb->data, count); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) + this_device->hard_start_xmit(skb, this_device); +#else + this_device->netdev_ops->ndo_start_xmit(skb, this_device); +#endif + + retval = count; + +#if THP_DEBUG + printk(KERN_WARNING "thp_write -\n"); +#endif + sqn_pr_leave(); + + return retval; +} + + +/* + +*/ +static unsigned int thp_poll(struct file *filp, poll_table *wait) +{ + unsigned int mask = 0; + + sqn_pr_enter(); +#if THP_DEBUG + //printk(KERN_WARNING "thp_poll +\n"); +#endif + + poll_wait(filp, &to_sqntool_wait, wait); + + if (0 == this_device) { + printk(KERN_WARNING "thp_poll() device removed\n"); + mask = POLLERR; + } else if(skb_queue_empty(&to_sqntool_queue)) { + mask = 0; + } else { + mask = (POLLIN | POLLRDNORM); + } + +#if THP_DEBUG + //printk(KERN_WARNING "thp_poll -\n"); +#endif + sqn_pr_leave(); + + return mask; +} + +static int thp_ioctl(struct inode* dev, struct file* handle, unsigned int cmd, unsigned long arg) +{ +#if THP_DEBUG + printk(KERN_WARNING "thp_ioctl +\n"); +#endif + sqn_pr_enter(); + + switch (cmd) { + case IOCTL_DROP_PACKETS: + printk(KERN_WARNING "IOCTL_DROP_PACKETS arg=%d\n",(int)arg); + if(arg == 1) + drop_packet = true; + else + drop_packet = false; + break; + + case IOCTL_SWITCH_UART: + printk(KERN_WARNING "IOCTL_SWITCH_UART arg=%d\n",(int)arg); + if(arg == 1) + mmc_wimax_uart_switch(2); // Wimax + else + mmc_wimax_uart_switch(0); // USB + break; + + default: + printk(KERN_WARNING "UNKNOWN OPERATION in thp_ioctl\n"); + return -1; + } + + sqn_pr_leave(); +#if THP_DEBUG + printk(KERN_WARNING "thp_ioctl -\n"); +#endif + + return 0; +} + +int init_thp_devfile(void) +{ + sqn_pr_enter(); +#if THP_DEBUG + printk(KERN_WARNING "init_thp_devfile +\n"); +#endif + + //Dynamic allocation of device number + if(alloc_chrdev_region(&dev_num, 0, 1, thp_filename)) + return -ENOMEM; + + thp_dev = cdev_alloc(); + if(thp_dev == NULL) + return -ENOMEM; + + thp_dev->ops = &thp_fops; + thp_dev->owner = THIS_MODULE; + + if(cdev_add(thp_dev, dev_num, 1)) + return -ENOMEM; + + thp_class = class_create(THIS_MODULE, thp_filename); + if (IS_ERR(thp_class)) + { + printk("class_create error(0x%x)\n",(unsigned int)(thp_class)); + return PTR_ERR(thp_class); + } + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) + thp_device = device_create(thp_class, NULL, dev_num, NULL, thp_filename); +#else + thp_device = device_create(thp_class, NULL, dev_num, thp_filename); +#endif + if (IS_ERR(thp_device)) + { + printk("device_create error(0x%x)\n", (unsigned int)(thp_device)); + return PTR_ERR(thp_device); + } + +#if THP_DEBUG + printk(KERN_WARNING "init_thp_devfile -\n"); +#endif + sqn_pr_leave(); + + return 0; +} + +/** + \brief dev-fs cleanup function + + * This function is called in module cleanup function + */ +void cleanup_thp_devfile(void) +{ + sqn_pr_enter(); +#if THP_DEBUG + printk(KERN_WARNING "cleanup_thp_devfile +\n"); +#endif + + /* Unregister entry from /dev */ + device_destroy(thp_class, dev_num); + class_destroy(thp_class); + unregister_chrdev_region(dev_num, 1); + cdev_del(thp_dev); + +#if THP_DEBUG + printk(KERN_WARNING "cleanup_thp_devfile -\n"); +#endif + sqn_pr_leave(); +} +/********************** File Operations END *****************************/ + +int thp_wimax_uart_switch(int on) +{ + printk("%s on%d\n", __func__, on); + + if (on) { + mmc_wimax_uart_switch(2); // Wimax + } + else { + mmc_wimax_uart_switch(0); // USB + } + + return 0; +} + +int init_thp(struct net_device* dev) +{ + sqn_pr_enter(); +#if THP_DEBUG + printk(KERN_WARNING "init_thp +\n"); +#endif + + if (0 == this_device) { + if(init_procfs_handler()) { + return -1; + } + + if(init_thp_devfile()) + return -1; + + /* Don't call init_thp_handler() here, it will be called from + * probe() before interrupts are enabled, to ensure that we will + * catch all THP packets as soon as they appear + */ + /* if (init_thp_handler(dev)) */ + /* return -1; */ + + this_device = dev; + + sqn_pr_info("KTHP initialized\n"); + } + +#if THP_DEBUG + printk(KERN_WARNING "init_thp -\n"); +#endif + sqn_pr_leave(); + + return 0; +} + + +void cleanup_thp(void) +{ + sqn_pr_enter(); +#if THP_DEBUG + printk(KERN_WARNING "cleanup_thp +\n"); +#endif + + if (this_device) { + cleanup_procfs_handler(); + cleanup_thp_handler(); + cleanup_thp_devfile(); + this_device = 0; + sqn_pr_info("KTHP cleaned up\n"); + } + +#if THP_DEBUG + printk(KERN_WARNING "cleanup_thp -\n"); +#endif + sqn_pr_leave(); +} diff --git a/drivers/net/wimax/SQN/thp.h b/drivers/net/wimax/SQN/thp.h new file mode 100644 index 0000000000000..1079561f6ef2d --- /dev/null +++ b/drivers/net/wimax/SQN/thp.h @@ -0,0 +1,56 @@ +/* + * This is part of the Sequans SQN1130 driver. + * Copyright 2009 SEQUANS Communications + * + * 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. + */ + +#ifndef _SQN_THP2_H +#define _SQN_THP2_H + + +struct sqn_thp_header { + /** Transport protocol version - must be 1 for now. */ + u8 transport_version; + + /* Flags Field is used to relay control information between THP peers + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * | 0 |DAK| 0 |ACK|EOF|MOF|BOF|NOF| + * + * NOF: No fragmentation + * BOF: Begining of fragmentation + * MOF: Middle of fragmentation + * EOF: End of fragmentation + * ACK: The sender acknowledge the reception of the "AckNumber" + * sequence number. DAK: The sender ask the receiver to + * acknowledge the seqence number "seqNumber".*/ + u8 flags; + + /** Length of the transported payload message, (without header). */ + u16 length; + + /** Sequence Number + * Which shall be incremented for each fragment (or no fragmented + * command). */ + u16 seq_number; + + /** Acknowledgment Number + * When ACK=DAK=NAK=0, the ackNumber is equal to the last sequence + * received number. */ + u16 ack_number; + + /** Length of the payload message before fragmentation. + * Note: In case of no fragmentation totalLength is equal to length.*/ + u32 total_length; +}; + + +int init_thp(struct net_device* dev); +int thp_wimax_uart_switch(int on); +void cleanup_thp(void); + +#endif /* _SQN_THP2_H */ + diff --git a/drivers/net/wimax/SQN/thp_ioctl.h b/drivers/net/wimax/SQN/thp_ioctl.h new file mode 100644 index 0000000000000..ef939e565e91f --- /dev/null +++ b/drivers/net/wimax/SQN/thp_ioctl.h @@ -0,0 +1,21 @@ +#ifndef _THP_IOCTL_H_ +#define _THP_IOCTL_H_ + +//ioctl group number +//must be a nonnegative 8-bit number +#define WIMAX_DEV_IOCTLID 'w' + +#define CMD_BASE 65 +//ioctl type within the group +//should be sequentially assigned numbers for each different ioctl operation +//must be a nonnegative 8-bit number +#define CMD_DROP_PACKETS CMD_BASE+0 +#define CMD_SIWTCH_UART CMD_BASE+1 + +//write only +//arg=1, drop tx/rx packets +//arg=0, normal mode +#define IOCTL_DROP_PACKETS _IOW(WIMAX_DEV_IOCTLID, CMD_DROP_PACKETS, int) +#define IOCTL_SWITCH_UART _IOW(WIMAX_DEV_IOCTLID, CMD_SIWTCH_UART, int) + +#endif //_THP_IOCTL_H_ diff --git a/drivers/net/wimax/SQN/version.h b/drivers/net/wimax/SQN/version.h new file mode 100644 index 0000000000000..5c2090b1b2c6d --- /dev/null +++ b/drivers/net/wimax/SQN/version.h @@ -0,0 +1,27 @@ +/* + * This is part of the Sequans SQN1130 driver. + * Copyright 2008 SEQUANS Communications + * Written by Andy Shevchenko + * + * 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. + */ + +#ifndef _SQN_VERSION_H +#define _SQN_VERSION_H + +#include + +#ifdef CONFIG_SDIO_SQN +#define SQN_MODULE_NAME "sequans_sdio" +#elif defined (CONFIG_USB_SQN) +#define SQN_MODULE_NAME "sequans_usb" +#else +#define SQN_MODULE_NAME "sequans_xxx" +#endif + +#define SQN_MODULE_VERSION "1.2.153" + +#endif /* _SQN_VERSION_H */ From e2bdfeed15ffc98192fb0dd2d472407d72229ab0 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sun, 7 Nov 2010 20:42:43 -0500 Subject: [PATCH 1033/2556] atmel touch driver: update atmel touch driver with the latest from HTC. still needed the hack for 3 point. though i think this can possibly changed in board later. i dont have the gimped touch sensor so someone who does will need to do it. finger_type: 2, max finger: 5 = not gimp finger_type:1,max finger 3 = gimp Change-Id: I3984e63bb798b32c6d8fc1430e0db22af3885a99 --- arch/arm/mach-msm/board-supersonic.c | 2 - drivers/input/touchscreen/atmel.c | 1129 +++++++++++++++++--------- include/linux/atmel_qt602240.h | 243 +++++- 3 files changed, 958 insertions(+), 416 deletions(-) diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index 28812bb4c7078..8ba3a85774c88 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -683,8 +683,6 @@ static int supersonic_atmel_ts_power(int on) struct atmel_i2c_platform_data supersonic_atmel_ts_data[] = { { .version = 0x016, - .display_width = 480, - .display_height = 800, .abs_x_min = 34, .abs_x_max = 990, .abs_y_min = 15, diff --git a/drivers/input/touchscreen/atmel.c b/drivers/input/touchscreen/atmel.c index 46c38780134b6..b9ca36673fc20 100644 --- a/drivers/input/touchscreen/atmel.c +++ b/drivers/input/touchscreen/atmel.c @@ -27,11 +27,16 @@ #include #include #include +#include #include #define ATMEL_EN_SYSFS #define ATMEL_I2C_RETRY_TIMES 10 +/* config_setting */ +#define NONE 0 +#define CONNECTED 1 + struct atmel_ts_data { struct i2c_client *client; struct input_dev *input_dev; @@ -63,10 +68,15 @@ struct atmel_ts_data { uint8_t calibration_confirm; uint64_t timestamp; struct atmel_config_data config_setting[2]; + int8_t noise_config[3]; uint8_t status; uint8_t GCAF_sample; uint8_t *GCAF_level; uint8_t noisethr; + uint8_t noisethr_config; + uint8_t diag_command; + uint8_t *ATCH_EXT; + int pre_data[3]; #ifdef ATMEL_EN_SYSFS struct device dev; #endif @@ -176,7 +186,7 @@ uint8_t get_object_size(struct atmel_ts_data *ts, uint8_t object_type) return 0; } -uint8_t get_report_ids_size(struct atmel_ts_data *ts, uint8_t object_type) +uint8_t get_rid(struct atmel_ts_data *ts, uint8_t object_type) { uint8_t loop_i; for (loop_i = 0; loop_i < ts->id->num_declared_objects; loop_i++) { @@ -203,7 +213,7 @@ static ssize_t atmel_gpio_show(struct device *dev, ret = strlen(buf) + 1; return ret; } -static DEVICE_ATTR(gpio, 0444, atmel_gpio_show, NULL); +static DEVICE_ATTR(gpio, S_IRUGO, atmel_gpio_show, NULL); static ssize_t atmel_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -216,7 +226,7 @@ static ssize_t atmel_vendor_show(struct device *dev, return ret; } -static DEVICE_ATTR(vendor, 0444, atmel_vendor_show, NULL); +static DEVICE_ATTR(vendor, S_IRUGO, atmel_vendor_show, NULL); static uint16_t atmel_reg_addr; @@ -224,7 +234,7 @@ static ssize_t atmel_register_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0; - uint8_t ptr[1]; + uint8_t ptr[1] = { 0 }; struct atmel_ts_data *ts_data; ts_data = private_ts; if (i2c_atmel_read(ts_data->client, atmel_reg_addr, ptr, 1) < 0) { @@ -279,16 +289,19 @@ static ssize_t atmel_register_store(struct device *dev, buf_zero, simple_strtol(buf_tmp, NULL, 10) - atmel_reg_addr + 1); if (buf[9] == 'r') { i2c_atmel_write_byte_data(ts_data->client, - get_object_address(ts_data, GEN_COMMANDPROCESSOR_T6) + 1, 0x55); + get_object_address(ts_data, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_BACKUPNV, 0x55); i2c_atmel_write_byte_data(ts_data->client, - get_object_address(ts_data, GEN_COMMANDPROCESSOR_T6), 0x11); + get_object_address(ts_data, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_RESET, 0x11); } } return count; } -static DEVICE_ATTR(register, 0644, atmel_register_show, atmel_register_store); +static DEVICE_ATTR(register, (S_IWUSR|S_IRUGO), + atmel_register_show, atmel_register_store); static ssize_t atmel_regdump_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -296,7 +309,7 @@ static ssize_t atmel_regdump_show(struct device *dev, int count = 0, ret_t = 0; struct atmel_ts_data *ts_data; uint16_t loop_i; - uint8_t ptr[1]; + uint8_t ptr[1] = { 0 }; ts_data = private_ts; if (ts_data->id->version >= 0x14) { for (loop_i = 230; loop_i <= 425; loop_i++) { @@ -321,13 +334,14 @@ static ssize_t atmel_regdump_dump(struct device *dev, struct atmel_ts_data *ts_data; ts_data = private_ts; if (buf[0] >= '0' && buf[0] <= '9' && buf[1] == '\n') - ts_data->debug_log_level = buf[0] - 0x30; + ts_data->debug_log_level = buf[0] - '0'; return count; } -static DEVICE_ATTR(regdump, 0644, atmel_regdump_show, atmel_regdump_dump); +static DEVICE_ATTR(regdump, (S_IWUSR|S_IRUGO), + atmel_regdump_show, atmel_regdump_dump); static ssize_t atmel_debug_level_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -347,12 +361,86 @@ static ssize_t atmel_debug_level_dump(struct device *dev, struct atmel_ts_data *ts_data; ts_data = private_ts; if (buf[0] >= '0' && buf[0] <= '9' && buf[1] == '\n') - ts_data->debug_log_level = buf[0] - 0x30; + ts_data->debug_log_level = buf[0] - '0'; + + return count; +} + +static DEVICE_ATTR(debug_level, (S_IWUSR|S_IRUGO), + atmel_debug_level_show, atmel_debug_level_dump); + +static ssize_t atmel_diag_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct atmel_ts_data *ts_data; + size_t count = 0; + uint8_t data[T37_PAGE_SIZE]; + uint8_t loop_i, loop_j; + int16_t rawdata; + int x, y; + ts_data = private_ts; + memset(data, 0x0, sizeof(data)); + + if (ts_data->diag_command != T6_CFG_DIAG_CMD_DELTAS && + ts_data->diag_command != T6_CFG_DIAG_CMD_REF) + return count; + + i2c_atmel_write_byte_data(ts_data->client, + get_object_address(ts_data, GEN_COMMANDPROCESSOR_T6) + T6_CFG_DIAG, + ts_data->diag_command); + + x = T28_CFG_MODE0_X + ts_data->config_setting[NONE].config_T28[T28_CFG_MODE]; + y = T28_CFG_MODE0_Y - ts_data->config_setting[NONE].config_T28[T28_CFG_MODE]; + count += sprintf(buf, "Channel: %d * %d\n", x, y); + + for (loop_i = 0; loop_i < 4; loop_i++) { + for (loop_j = 0; + !(data[T37_MODE] == ts_data->diag_command && data[T37_PAGE] == loop_i) && loop_j < 10; loop_j++) { + msleep(5); + i2c_atmel_read(ts_data->client, + get_object_address(ts_data, DIAGNOSTIC_T37), data, 2); + } + if (loop_j == 10) + printk(KERN_ERR "%s: Diag data not ready\n", __func__); + + i2c_atmel_read(ts_data->client, + get_object_address(ts_data, DIAGNOSTIC_T37) + + T37_DATA, data, T37_PAGE_SIZE); + for (loop_j = 0; loop_j < T37_PAGE_SIZE - 1; loop_j += 2) { + if ((loop_i * 64 + loop_j / 2) >= (x * y)) { + count += sprintf(buf + count, "\n"); + return count; + } else { + rawdata = data[loop_j+1] << 8 | data[loop_j]; + count += sprintf(buf + count, "%5d", rawdata); + if (((loop_i * 64 + loop_j / 2) % y) == (y - 1)) + count += sprintf(buf + count, "\n"); + } + } + i2c_atmel_write_byte_data(ts_data->client, + get_object_address(ts_data, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_DIAG, T6_CFG_DIAG_CMD_PAGEUP); + + } + + return count; +} + +static ssize_t atmel_diag_dump(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct atmel_ts_data *ts_data; + ts_data = private_ts; + if (buf[0] == '1') + ts_data->diag_command = T6_CFG_DIAG_CMD_DELTAS; + if (buf[0] == '2') + ts_data->diag_command = T6_CFG_DIAG_CMD_REF; return count; } -static DEVICE_ATTR(debug_level, 0644, atmel_debug_level_show, atmel_debug_level_dump); +static DEVICE_ATTR(diag, (S_IWUSR|S_IRUGO), + atmel_diag_show, atmel_diag_dump); static struct kobject *android_touch_kobj; @@ -391,6 +479,11 @@ static int atmel_touch_sysfs_init(void) printk(KERN_ERR "%s: sysfs_create_file failed\n", __func__); return ret; } + ret = sysfs_create_file(android_touch_kobj, &dev_attr_diag.attr); + if (ret) { + printk(KERN_ERR "%s: sysfs_create_file failed\n", __func__); + return ret; + } return 0; } @@ -406,24 +499,31 @@ static void atmel_touch_sysfs_deinit(void) #endif static int check_delta(struct atmel_ts_data*ts) { - int8_t data[128]; + int8_t data[T37_DATA + T37_PAGE_SIZE]; uint8_t loop_i; int16_t rawdata, count = 0; memset(data, 0xFF, sizeof(data)); i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + 5, 0x10); + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_DIAG, T6_CFG_DIAG_CMD_DELTAS); - for (loop_i = 0; !(data[0] == 0x10 && data[1] == 0x00) && loop_i < 10; loop_i++) { + for (loop_i = 0; + !(data[T37_MODE] == T6_CFG_DIAG_CMD_DELTAS && data[T37_PAGE] == T37_PAGE_NUM0) && loop_i < 10; loop_i++) { msleep(5); - i2c_atmel_read(ts->client, get_object_address(ts, DIAGNOSTIC_T37), data, 2); + i2c_atmel_read(ts->client, + get_object_address(ts, DIAGNOSTIC_T37), data, 2); } if (loop_i == 10) printk(KERN_ERR "%s: Diag data not ready\n", __func__); - i2c_atmel_read(ts->client, get_object_address(ts, DIAGNOSTIC_T37), data, 128); - if (data[0] == 0x10 && data[1] == 0x00) { - for (loop_i = 2; loop_i < 127; loop_i += 2) { + i2c_atmel_read(ts->client, + get_object_address(ts, DIAGNOSTIC_T37), + data, T37_DATA + T37_PAGE_SIZE); + if (data[T37_MODE] == T6_CFG_DIAG_CMD_DELTAS && + data[T37_PAGE] == T37_PAGE_NUM0) { + for (loop_i = T37_DATA; + loop_i < (T37_DATA + T37_PAGE_SIZE - 1); loop_i += 2) { rawdata = data[loop_i+1] << 8 | data[loop_i]; if (abs(rawdata) > 50) count++; @@ -436,53 +536,65 @@ static int check_delta(struct atmel_ts_data*ts) static void check_calibration(struct atmel_ts_data*ts) { - uint8_t data[82]; + uint8_t data[T37_DATA + T37_TCH_FLAG_SIZE]; uint8_t loop_i, loop_j, x_limit = 0, check_mask, tch_ch = 0, atch_ch = 0; memset(data, 0xFF, sizeof(data)); i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + 5, 0xF3); + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_DIAG, T6_CFG_DIAG_CMD_TCH); - for (loop_i = 0; !(data[0] == 0xF3 && data[1] == 0x00) && loop_i < 10; loop_i++) { + for (loop_i = 0; + !(data[T37_MODE] == T6_CFG_DIAG_CMD_TCH && data[T37_PAGE] == T37_PAGE_NUM0) && loop_i < 10; loop_i++) { msleep(5); - i2c_atmel_read(ts->client, get_object_address(ts, DIAGNOSTIC_T37), data, 2); + i2c_atmel_read(ts->client, + get_object_address(ts, DIAGNOSTIC_T37), data, 2); } if (loop_i == 10) printk(KERN_ERR "%s: Diag data not ready\n", __func__); - i2c_atmel_read(ts->client, get_object_address(ts, DIAGNOSTIC_T37), data, 82); - if (data[0] == 0xF3 && data[1] == 0x00) { - x_limit = 16 + ts->config_setting[0].config_T28[2]; + i2c_atmel_read(ts->client, get_object_address(ts, DIAGNOSTIC_T37), data, + T37_DATA + T37_TCH_FLAG_SIZE); + if (data[T37_MODE] == T6_CFG_DIAG_CMD_TCH && + data[T37_PAGE] == T37_PAGE_NUM0) { + x_limit = T28_CFG_MODE0_X + ts->config_setting[NONE].config_T28[T28_CFG_MODE]; x_limit = x_limit << 1; - - for (loop_i = 0; loop_i < x_limit; loop_i += 2) { - for (loop_j = 0; loop_j < 8; loop_j++) { - check_mask = 1 << loop_j; - if (data[2 + loop_i] & check_mask) - tch_ch++; - if (data[3 + loop_i] & check_mask) - tch_ch++; - if (data[42 + loop_i] & check_mask) - atch_ch++; - if (data[43 + loop_i] & check_mask) - atch_ch++; + if (x_limit <= 40) { + for (loop_i = 0; loop_i < x_limit; loop_i += 2) { + for (loop_j = 0; loop_j < BITS_PER_BYTE; loop_j++) { + check_mask = BIT_MASK(loop_j); + if (data[T37_DATA + T37_TCH_FLAG_IDX + loop_i] & + check_mask) + tch_ch++; + if (data[T37_DATA + T37_TCH_FLAG_IDX + loop_i + 1] & + check_mask) + tch_ch++; + if (data[T37_DATA + T37_ATCH_FLAG_IDX + loop_i] & + check_mask) + atch_ch++; + if (data[T37_DATA + T37_ATCH_FLAG_IDX + loop_i + 1] & + check_mask) + atch_ch++; + } } } } i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + 5, 0x01); + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_DIAG, T6_CFG_DIAG_CMD_PAGEUP); if (tch_ch && (atch_ch == 0)) { if (jiffies > (ts->timestamp + HZ/2) && (ts->calibration_confirm == 1)) { ts->calibration_confirm = 2; printk(KERN_INFO "%s: calibration confirm\n", __func__); i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 6, - ts->config_setting[ts->status].config_T8[6]); + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + T8_CFG_ATCHCALST, + ts->config_setting[ts->status].config_T8[T8_CFG_ATCHCALST]); i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 7, - ts->config_setting[ts->status].config_T8[7]); + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + + T8_CFG_ATCHCALSTHR, + ts->config_setting[ts->status].config_T8[T8_CFG_ATCHCALSTHR]); } if (ts->calibration_confirm < 2) ts->calibration_confirm = 1; @@ -490,7 +602,219 @@ static void check_calibration(struct atmel_ts_data*ts) } else if ((tch_ch - 25) <= atch_ch && (tch_ch || atch_ch)) { ts->calibration_confirm = 0; i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + 2, 0x55); + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_CALIBRATE, 0x55); + } +} + +static void msg_process_finger_data(struct atmel_finger_data *fdata, uint8_t *data) +{ + fdata->x = data[T9_MSG_XPOSMSB] << 2 | data[T9_MSG_XYPOSLSB] >> 6; + fdata->y = data[T9_MSG_YPOSMSB] << 2 | (data[T9_MSG_XYPOSLSB] & 0x0C) >> 2; + fdata->w = data[T9_MSG_TCHAREA]; + fdata->z = data[T9_MSG_TCHAMPLITUDE]; +} + +static void msg_process_multitouch(struct atmel_ts_data *ts, uint8_t *data, uint8_t idx) +{ + if (ts->calibration_confirm < 2 && ts->id->version == 0x16) + check_calibration(ts); + + msg_process_finger_data(&ts->finger_data[idx], data); + if (data[T9_MSG_STATUS] & T9_MSG_STATUS_RELEASE) { + if (ts->grip_suppression & BIT(idx)) + ts->grip_suppression &= ~BIT(idx); + if (ts->finger_pressed & BIT(idx)) { + ts->finger_count--; + ts->finger_pressed &= ~BIT(idx); + if (!ts->first_pressed) { + if (!ts->finger_count) + ts->first_pressed = 1; + printk(KERN_INFO "E%d@%d,%d\n", + idx + 1, ts->finger_data[idx].x, ts->finger_data[idx].y); + } + if (ts->finger_count == 0 && ts->id->version >= 0x20) { + if (ts->pre_data[0] == 2) + ts->pre_data[0] = 0; + else if (idx == 0 && ts->pre_data[0] == 0 && ts->finger_data[0].y > 750 + && ((ts->finger_data[0].y - ts->pre_data[2]) > 135)) { + uint8_t ATCH_NOR[4] = {0, 1, 0, 0}; + i2c_atmel_write(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 6, + ATCH_NOR, 4); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9) + 7, + ts->config_setting[ts->status].config[0]); + if (ts->GCAF_sample < 32) + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_CALIBRATE, 0x55); + ts->pre_data[0] = 1; + printk(KERN_INFO "%s: calibration confirm\n", __func__); + } + } + } + } else if ((data[T9_MSG_STATUS] & (T9_MSG_STATUS_DETECT|T9_MSG_STATUS_PRESS)) && + !(ts->finger_pressed & BIT(idx))) { + if (ts->filter_level[0]) { + if (ts->finger_data[idx].x < ts->filter_level[FL_XLOGRIPMIN] || + ts->finger_data[idx].x > ts->filter_level[FL_XHIGRIPMAX]) + ts->grip_suppression |= BIT(idx); + else if ((ts->finger_data[idx].x < ts->filter_level[FL_XLOGRIPMAX] || + ts->finger_data[idx].x > ts->filter_level[FL_XHIGRIPMIN]) && + (ts->grip_suppression & BIT(idx))) + ts->grip_suppression |= BIT(idx); + else if (ts->finger_data[idx].x > ts->filter_level[FL_XLOGRIPMAX] && + ts->finger_data[idx].x < ts->filter_level[FL_XHIGRIPMIN]) + ts->grip_suppression &= ~BIT(idx); + } + if (!(ts->grip_suppression & BIT(idx))) { + if (!ts->first_pressed) + printk(KERN_INFO "S%d@%d,%d\n", + idx + 1, ts->finger_data[idx].x, ts->finger_data[idx].y); + ts->finger_count++; + ts->finger_pressed |= BIT(idx); + if (ts->id->version >= 0x20) { + if (ts->pre_data[0] == 0 && idx == 0) { + ts->pre_data[1] = ts->finger_data[0].x; + ts->pre_data[2] = ts->finger_data[0].y; + } + if (ts->pre_data[0] == 0 && ts->finger_count > 1) + ts->pre_data[0] = 2; + } + } + } +} + +static void msg_process_multitouch_legacy(struct atmel_ts_data *ts, uint8_t *data, uint8_t idx) +{ + /* for issue debug only */ + if ((data[T9_MSG_STATUS] & (T9_MSG_STATUS_PRESS|T9_MSG_STATUS_RELEASE)) == + (T9_MSG_STATUS_PRESS|T9_MSG_STATUS_RELEASE)) + printk(KERN_INFO "x60 ISSUE happened: %x, %x, %x, %x, %x, %x, %x\n", + data[0], data[1], data[2], data[3], data[4], data[5], data[6]); + + msg_process_finger_data(&ts->finger_data[idx], data); + if ((data[T9_MSG_STATUS] & T9_MSG_STATUS_RELEASE) && + (ts->finger_pressed & BIT(idx))) { + ts->finger_count--; + ts->finger_pressed &= ~BIT(idx); + } else if ((data[T9_MSG_STATUS] & (T9_MSG_STATUS_DETECT|T9_MSG_STATUS_PRESS)) && + !(ts->finger_pressed & BIT(idx))) { + ts->finger_count++; + ts->finger_pressed |= BIT(idx); + } +} + +static void msg_process_noisesuppression(struct atmel_ts_data *ts, uint8_t *data) +{ + uint8_t loop_i; + + if (ts->status == CONNECTED && data[T22_MSG_GCAFDEPTH] >= ts->GCAF_sample) { + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_POWERCONFIG_T7) + + T7_CFG_IDLEACQINT, 0x08); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_POWERCONFIG_T7) + + T7_CFG_ACTVACQINT, 0x08); + for (loop_i = 0; loop_i < 5; loop_i++) { + if (ts->GCAF_sample < ts->GCAF_level[loop_i]) { + ts->GCAF_sample = ts->GCAF_level[loop_i]; + break; + } + } + if (loop_i == 5) + ts->GCAF_sample += 24; + if (ts->GCAF_sample >= 63) { + ts->GCAF_sample = 63; + if (ts->noise_config[0]) { + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9) + + T9_CFG_TCHTHR, ts->noise_config[NC_TCHTHR]); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9) + + T9_CFG_TCHDI, ts->noise_config[NC_TCHDI]); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, PROCG_NOISESUPPRESSION_T22) + + T22_CFG_NOISETHR, ts->noise_config[NC_NOISETHR]); + } else { + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, PROCG_NOISESUPPRESSION_T22) + + T22_CFG_NOISETHR, ts->config_setting[CONNECTED].config[CB_NOISETHR]); + } + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + + T8_CFG_ATCHCALSTHR, 0x1); + } + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, SPT_CTECONFIG_T28) + + T28_CFG_ACTVGCAFDEPTH, ts->GCAF_sample); + } + if (data[T22_MSG_STATUS] & (T22_MSG_STATUS_FHERR|T22_MSG_STATUS_GCAFERR) && + ts->GCAF_sample == 63) { + ts->noisethr_config += 3; + if (ts->noisethr && ts->noisethr_config > ts->noisethr) + ts->noisethr_config = ts->noisethr; + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, PROCG_NOISESUPPRESSION_T22) + + T22_CFG_NOISETHR, ts->noisethr_config); + } +} + +static void compatible_input_report(struct input_dev *idev, + struct atmel_finger_data *fdata, uint8_t press, uint8_t last) +{ + if (!press) + input_report_abs(idev, ABS_MT_TOUCH_MAJOR, 0); + else { + input_report_abs(idev, ABS_MT_TOUCH_MAJOR, fdata->z); + input_report_abs(idev, ABS_MT_WIDTH_MAJOR, fdata->w); + input_report_abs(idev, ABS_MT_POSITION_X, fdata->x); + input_report_abs(idev, ABS_MT_POSITION_Y, fdata->y); + input_mt_sync(idev); + } +} + +#ifndef CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT +static void htc_input_report(struct input_dev *idev, + struct atmel_finger_data *fdata, uint8_t press, uint8_t last) +{ + if (!press) { + input_report_abs(idev, ABS_MT_AMPLITUDE, 0); + input_report_abs(idev, ABS_MT_POSITION, BIT(31)); + } else { + input_report_abs(idev, ABS_MT_AMPLITUDE, fdata->z << 16 | fdata->w); + input_report_abs(idev, ABS_MT_POSITION, + (last ? BIT(31) : 0) | fdata->x << 16 | fdata->y); + } +} +#endif + +static void multi_input_report(struct atmel_ts_data *ts) +{ + uint8_t loop_i, finger_report = 0; + + for (loop_i = 0; loop_i < ts->finger_support; loop_i++) { + if (ts->finger_pressed & BIT(loop_i)) { + if (ts->id->version >= 0x15) { +#ifdef CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT + compatible_input_report(ts->input_dev, &ts->finger_data[loop_i], + 1, (ts->finger_count == ++finger_report)); +#else + htc_input_report(ts->input_dev, &ts->finger_data[loop_i], + 1, (ts->finger_count == ++finger_report)); +#endif + } else { + compatible_input_report(ts->input_dev, &ts->finger_data[loop_i], + 1, (ts->finger_count == ++finger_report)); + } + if (ts->debug_log_level & 0x2) + printk(KERN_INFO "Finger %d=> X:%d, Y:%d w:%d, z:%d, F:%d\n", + loop_i + 1, + ts->finger_data[loop_i].x, ts->finger_data[loop_i].y, + ts->finger_data[loop_i].w, ts->finger_data[loop_i].z, + ts->finger_count); + } } } @@ -498,213 +822,83 @@ static void atmel_ts_work_func(struct work_struct *work) { int ret; struct atmel_ts_data *ts = container_of(work, struct atmel_ts_data, work); - uint8_t data[ts->finger_support * 9]; + uint8_t data[7]; + uint8_t loop_i, loop_j, report_type, msg_byte_num = 7; - uint8_t loop_i, loop_j, report_type, msg_num, msg_byte_num = 8, finger_report; - msg_num = (ts->finger_count && ts->id->version >= 0x15) - ? ts->finger_count : 1; + memset(data, 0x0, sizeof(data)); ret = i2c_atmel_read(ts->client, get_object_address(ts, - GEN_MESSAGEPROCESSOR_T5), data, msg_num * 9 - 2); + GEN_MESSAGEPROCESSOR_T5), data, 7); if (ts->debug_log_level & 0x1) { - for (loop_i = 0; loop_i < msg_num * 9 - 2; loop_i++) + for (loop_i = 0; loop_i < 7; loop_i++) printk("0x%2.2X ", data[loop_i]); printk("\n"); } if (ts->id->version >= 0x15) { - for (loop_i = 0; loop_i < msg_num; loop_i++) { - report_type = data[loop_i * 9] - ts->finger_type; + report_type = data[MSG_RID] - ts->finger_type; if (report_type >= 0 && report_type < ts->finger_support) { - if (ts->calibration_confirm < 2 && ts->id->version >= 0x16) - check_calibration(ts); - ts->finger_data[report_type].x = data[loop_i * 9 + 2] << 2 | data[loop_i * 9 + 4] >> 6; - ts->finger_data[report_type].y = data[loop_i * 9 + 3] << 2 | (data[loop_i * 9 + 4] & 0x0C) >> 2; - ts->finger_data[report_type].w = data[loop_i * 9 + 5]; - ts->finger_data[report_type].z = data[loop_i * 9 + 6]; - if (data[loop_i * 9 + 1] & 0x20) { - if ((ts->grip_suppression >> report_type) & 1) - ts->grip_suppression &= ~(1 << report_type); - if (((ts->finger_pressed >> report_type) & 1) == 1) { - ts->finger_count--; - ts->finger_pressed &= ~(1 << report_type); - if (!ts->first_pressed) { - if (!ts->finger_count) - ts->first_pressed = 1; - printk(KERN_INFO "E%d@%d,%d\n", report_type + 1, - ts->finger_data[report_type].x, ts->finger_data[report_type].y); - } - } - } else if ((data[loop_i * 9 + 1] & 0xC0) && (((ts->finger_pressed >> report_type) & 1) == 0)) { - if (ts->filter_level[0]) { - if (ts->finger_data[report_type].x < ts->filter_level[0] || ts->finger_data[report_type].x > ts->filter_level[3]) - ts->grip_suppression |= 1 << report_type; - else if ((ts->finger_data[report_type].x < ts->filter_level[1] || ts->finger_data[report_type].x > ts->filter_level[2]) - && ((ts->grip_suppression >> report_type) & 1)) - ts->grip_suppression |= 1 << report_type; - else if (ts->finger_data[report_type].x > ts->filter_level[1] && ts->finger_data[report_type].x < ts->filter_level[2]) - ts->grip_suppression &= ~(1 << report_type); - } - if (((ts->grip_suppression >> report_type) & 1) == 0) { - if (!ts->first_pressed) - printk(KERN_INFO "S%d@%d,%d\n", report_type + 1, - ts->finger_data[report_type].x, ts->finger_data[report_type].y); - ts->finger_count++; - ts->finger_pressed |= 1 << report_type; - } - } + msg_process_multitouch(ts, data, report_type); } else { - if (data[loop_i * 9] == get_report_ids_size(ts, GEN_COMMANDPROCESSOR_T6)) { + if (data[MSG_RID] == get_rid(ts, GEN_COMMANDPROCESSOR_T6)) { printk(KERN_INFO "Touch Status: "); msg_byte_num = 5; - } else if (data[loop_i * 9] == get_report_ids_size(ts, PROCI_GRIPFACESUPPRESSION_T20)) { - if (ts->calibration_confirm < 2 && ts->id->version >= 0x16) + } else if (data[MSG_RID] == get_rid(ts, PROCI_GRIPFACESUPPRESSION_T20)) { + if (ts->calibration_confirm < 2 && ts->id->version == 0x16) check_calibration(ts); - ts->face_suppression = data[loop_i * 9 + 1]; + ts->face_suppression = data[T20_MSG_STATUS]; printk(KERN_INFO "Touch Face suppression %s: ", ts->face_suppression ? "Active" : "Inactive"); msg_byte_num = 2; - } else if (data[loop_i * 9] == get_report_ids_size(ts, PROCG_NOISESUPPRESSION_T22)) { - if (data[loop_i * 9 + 1] == 0x10) /* reduce message print */ + } else if (data[MSG_RID] == get_rid(ts, PROCG_NOISESUPPRESSION_T22)) { + if (data[T22_MSG_STATUS] == T22_MSG_STATUS_GCAFCHG) /* reduce message print */ msg_byte_num = 0; else { printk(KERN_INFO "Touch Noise suppression: "); msg_byte_num = 4; - if (ts->status && data[loop_i * 9 + 2] >= ts->GCAF_sample) { - i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, GEN_POWERCONFIG_T7), 0x08); - i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, GEN_POWERCONFIG_T7) + 1, 0x08); - for (loop_j = 0; loop_j < 5; loop_j++) { - if (ts->GCAF_sample < ts->GCAF_level[loop_j]) { - ts->GCAF_sample = ts->GCAF_level[loop_j]; - break; - } - } - if (loop_j == 5) - ts->GCAF_sample += 24; - if (ts->GCAF_sample >= 63) { - ts->GCAF_sample = 63; - i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, PROCG_NOISESUPPRESSION_T22) + 8, - ts->config_setting[1].config[1]); - i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 7, 0x1); - } - i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, SPT_CTECONFIG_T28) + 4, ts->GCAF_sample); - } - if (data[loop_i * 9 + 1] & 0x0C && ts->GCAF_sample == 63) { - ts->noisethr += 30; - if (ts->noisethr > 255) - ts->noisethr = 255; - i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, PROCG_NOISESUPPRESSION_T22) + 8, - ts->noisethr); - } + msg_process_noisesuppression(ts, data); } } - if (data[loop_i * 9] != 0xFF) { + if (data[MSG_RID] != 0xFF) { for (loop_j = 0; loop_j < msg_byte_num; loop_j++) - printk("0x%2.2X ", data[loop_i * 9 + loop_j]); + printk("0x%2.2X ", data[loop_j]); if (msg_byte_num) printk("\n"); } } - if (loop_i == msg_num - 1) { - if (!ts->finger_count || ts->face_suppression) { - ts->finger_pressed = 0; - ts->finger_count = 0; -#ifdef CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT - input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); -#else - input_report_abs(ts->input_dev, ABS_MT_AMPLITUDE, 0); - input_report_abs(ts->input_dev, ABS_MT_POSITION, 1 << 31); -#endif - if (ts->debug_log_level & 0x2) - printk(KERN_INFO "Finger leave\n"); - } else { - for (loop_i = 0, finger_report = 0; loop_i < ts->finger_support; loop_i++) { - if (((ts->finger_pressed >> loop_i) & 1) == 1) { - finger_report++; + if (!ts->finger_count || ts->face_suppression) { + ts->finger_pressed = 0; + ts->finger_count = 0; #ifdef CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT - input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, - ts->finger_data[loop_i].z); - input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, - ts->finger_data[loop_i].w); - input_report_abs(ts->input_dev, ABS_MT_POSITION_X, - ts->finger_data[loop_i].x); - input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, - ts->finger_data[loop_i].y); - input_mt_sync(ts->input_dev); + compatible_input_report(ts->input_dev, NULL, 0, 1); #else - input_report_abs(ts->input_dev, ABS_MT_AMPLITUDE, - ts->finger_data[loop_i].z << 16 | ts->finger_data[loop_i].w); - input_report_abs(ts->input_dev, ABS_MT_POSITION, - (ts->finger_count == finger_report) << 31 | - ts->finger_data[loop_i].x << 16 | ts->finger_data[loop_i].y); + htc_input_report(ts->input_dev, NULL, 0, 1); #endif - if (ts->debug_log_level & 0x2) - printk(KERN_INFO "Finger %d=> X:%d, Y:%d w:%d, z:%d, F:%d\n", - loop_i + 1, ts->finger_data[loop_i].x, - ts->finger_data[loop_i].y, ts->finger_data[loop_i].w, - ts->finger_data[loop_i].z, ts->finger_count); - } - } - } -#ifdef CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT - input_sync(ts->input_dev); -#endif - } - } - } else { /*read one message one time */ - report_type = data[0] - ts->finger_type; - if (report_type >= 0 && report_type < ts->finger_support) { - /* for issue debug only */ - if ((data[1] & 0x60) == 0x60) - printk(KERN_INFO"x60 ISSUE happened: %x, %x, %x, %x, %x, %x, %x, %x\n", - data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); - if ((data[1] & 0x20) && (((ts->finger_pressed >> report_type) & 1) == 1)) { - ts->finger_count--; - ts->finger_pressed &= ~(1 << report_type); - } else if ((data[1] & 0xC0) && (((ts->finger_pressed >> report_type) & 1) == 0)) { - ts->finger_count++; - ts->finger_pressed |= 1 << report_type; - } - ts->finger_data[report_type].x = data[2] << 2 | data[4] >> 6; - ts->finger_data[report_type].y = data[3] << 2 | (data[4] & 0x0C) >> 2; - ts->finger_data[report_type].w = data[5]; - ts->finger_data[report_type].z = data[6]; - if (!ts->finger_count) { - ts->finger_pressed = 0; - input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); if (ts->debug_log_level & 0x2) printk(KERN_INFO "Finger leave\n"); } else { - for (loop_i = 0; loop_i < ts->finger_support; loop_i++) { - if (((ts->finger_pressed >> loop_i) & 1) == 1) { - input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, - ts->finger_data[loop_i].z); - input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, - ts->finger_data[loop_i].w); - input_report_abs(ts->input_dev, ABS_MT_POSITION_X, - ts->finger_data[loop_i].x); - input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, - ts->finger_data[loop_i].y); - input_mt_sync(ts->input_dev); - if (ts->debug_log_level & 0x2) - printk(KERN_INFO "Finger %d=> X:%d, Y:%d w:%d, z:%d, F:%d\n", - loop_i + 1, ts->finger_data[loop_i].x, - ts->finger_data[loop_i].y, ts->finger_data[loop_i].w, - ts->finger_data[loop_i].z, ts->finger_count); - } - } + multi_input_report(ts); } +#ifdef CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT input_sync(ts->input_dev); - } else - printk(KERN_INFO"RAW data: %x, %x, %x, %x, %x, %x, %x, %x\n", - data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); +#endif + } else { /*read one message one time */ + report_type = data[MSG_RID] - ts->finger_type; + if (report_type >= 0 && report_type < ts->finger_support) { + msg_process_multitouch_legacy(ts, data, report_type); + if (!ts->finger_count) { + ts->finger_pressed = 0; + compatible_input_report(ts->input_dev, NULL, 0, 1); + if (ts->debug_log_level & 0x2) + printk(KERN_INFO "Finger leave\n"); + } else { + multi_input_report(ts); + } + input_sync(ts->input_dev); + } else + printk(KERN_INFO"RAW data: %x, %x, %x, %x, %x, %x, %x\n", + data[0], data[1], data[2], data[3], data[4], data[5], data[6]); } enable_irq(ts->client->irq); } @@ -718,73 +912,120 @@ static irqreturn_t atmel_ts_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static void cable_tp_status_handler_func(int connected) +static void cable_tp_status_handler_func(int connect_status) { struct atmel_ts_data *ts; + + printk(KERN_INFO "Touch: cable change to %d\n", connect_status); ts = private_ts; - printk(KERN_INFO "Touch: cable change to %d\n", connected); - if (connected != ts->status) { - if (connected) - ts->status = 1; - else - ts->status = 0; - - if (ts->config_setting[1].config[0]) { - if (ts->status) { + if (connect_status != ts->status) { + ts->status = connect_status ? CONNECTED : NONE; + if (ts->config_setting[CONNECTED].config[0]) { + if (ts->status == CONNECTED && ts->id->version < 0x20) { ts->calibration_confirm = 2; i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 6, - ts->config_setting[ts->status].config_T8[6]); + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + + T8_CFG_ATCHCALST, + ts->config_setting[ts->status].config_T8[T8_CFG_ATCHCALST]); i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 7, - ts->config_setting[ts->status].config_T8[7]); + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + + T8_CFG_ATCHCALSTHR , + ts->config_setting[ts->status].config_T8[T8_CFG_ATCHCALSTHR]); } i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9) + 7, - ts->config_setting[ts->status].config[0]); - if (!ts->status) + get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9) + + T9_CFG_TCHTHR, + ts->config_setting[ts->status].config[CB_TCHTHR]); + if (ts->status == NONE) { i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, PROCG_NOISESUPPRESSION_T22) + 8, - ts->config_setting[ts->status].config[1]); + get_object_address(ts, PROCG_NOISESUPPRESSION_T22) + + T22_CFG_NOISETHR, + ts->config_setting[NONE].config[CB_NOISETHR]); + ts->noisethr_config = + ts->config_setting[CONNECTED].config[CB_NOISETHR]; + } i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, SPT_CTECONFIG_T28) + 3, - ts->config_setting[ts->status].config[2]); + get_object_address(ts, SPT_CTECONFIG_T28) + + T28_CFG_IDLEGCAFDEPTH, + ts->config_setting[ts->status].config[CB_IDLEGCAFDEPTH]); i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, SPT_CTECONFIG_T28) + 4, - ts->config_setting[ts->status].config[3]); - ts->GCAF_sample = ts->config_setting[1].config[3]; + get_object_address(ts, SPT_CTECONFIG_T28) + + T28_CFG_ACTVGCAFDEPTH, + ts->config_setting[ts->status].config[CB_ACTVGCAFDEPTH]); + ts->GCAF_sample = + ts->config_setting[CONNECTED].config[CB_ACTVGCAFDEPTH]; } else { - if (ts->config_setting[1].config_T7 != NULL) + if (ts->config_setting[CONNECTED].config_T7 != NULL) i2c_atmel_write(ts->client, get_object_address(ts, GEN_POWERCONFIG_T7), ts->config_setting[ts->status].config_T7, get_object_size(ts, GEN_POWERCONFIG_T7)); - if (ts->config_setting[1].config_T8 != NULL) + if (ts->config_setting[CONNECTED].config_T8 != NULL) i2c_atmel_write(ts->client, get_object_address(ts, GEN_ACQUISITIONCONFIG_T8), - ts->config_setting[1].config_T8, + ts->config_setting[CONNECTED].config_T8, get_object_size(ts, GEN_ACQUISITIONCONFIG_T8)); if (ts->config_setting[ts->status].config_T9 != NULL) i2c_atmel_write(ts->client, get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9), ts->config_setting[ts->status].config_T9, get_object_size(ts, TOUCH_MULTITOUCHSCREEN_T9)); - if (ts->config_setting[ts->status].config_T22 != NULL) + if (ts->config_setting[ts->status].config_T22 != NULL) { i2c_atmel_write(ts->client, get_object_address(ts, PROCG_NOISESUPPRESSION_T22), ts->config_setting[ts->status].config_T22, get_object_size(ts, PROCG_NOISESUPPRESSION_T22)); + ts->noisethr_config = + ts->config_setting[ts->status].config_T22[8]; + } if (ts->config_setting[ts->status].config_T28 != NULL) { i2c_atmel_write(ts->client, get_object_address(ts, SPT_CTECONFIG_T28), ts->config_setting[ts->status].config_T28, get_object_size(ts, SPT_CTECONFIG_T28)); - ts->GCAF_sample = ts->config_setting[ts->status].config_T28[4]; + ts->GCAF_sample = + ts->config_setting[ts->status].config_T28[T28_CFG_ACTVGCAFDEPTH]; } } } } +static int read_object_table(struct atmel_ts_data *ts) +{ + uint8_t i, type_count = 0; + uint8_t data[6]; + memset(data, 0x0, sizeof(data)); + + ts->object_table = kzalloc(sizeof(struct object_t)*ts->id->num_declared_objects, GFP_KERNEL); + if (ts->object_table == NULL) { + printk(KERN_ERR "%s: allocate object_table failed\n", __func__); + return -ENOMEM; + } + + for (i = 0; i < ts->id->num_declared_objects; i++) { + i2c_atmel_read(ts->client, i * 6 + 0x07, data, 6); + ts->object_table[i].object_type = data[OBJ_TABLE_TYPE]; + ts->object_table[i].i2c_address = + data[OBJ_TABLE_LSB] | data[OBJ_TABLE_MSB] << 8; + ts->object_table[i].size = data[OBJ_TABLE_SIZE] + 1; + ts->object_table[i].instances = data[OBJ_TABLE_INSTANCES]; + ts->object_table[i].num_report_ids = data[OBJ_TABLE_RIDS]; + if (data[OBJ_TABLE_RIDS]) { + ts->object_table[i].report_ids = type_count + 1; + type_count += data[OBJ_TABLE_RIDS]; + } + if (data[OBJ_TABLE_TYPE] == TOUCH_MULTITOUCHSCREEN_T9) + ts->finger_type = ts->object_table[i].report_ids; + printk(KERN_INFO + "Type: %2.2X, Start: %4.4X, Size: %2X, Instance: %2X, RD#: %2X, %2X\n", + ts->object_table[i].object_type , ts->object_table[i].i2c_address, + ts->object_table[i].size, ts->object_table[i].instances, + ts->object_table[i].num_report_ids, ts->object_table[i].report_ids); + } + + return 0; +} + static struct t_usb_status_notifier cable_status_handler = { .name = "usb_tp_connected", .func = cable_tp_status_handler_func, @@ -795,11 +1036,11 @@ static int atmel_ts_probe(struct i2c_client *client, { struct atmel_ts_data *ts; struct atmel_i2c_platform_data *pdata; - int ret = 0, i = 0, intr = 0; + int ret = 0, intr = 0; uint8_t loop_i; struct i2c_msg msg[2]; uint8_t data[16]; - uint8_t type_count = 0, CRC_check = 0; + uint8_t CRC_check = 0; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { printk(KERN_ERR"%s: need I2C_FUNC_I2C\n", __func__); @@ -859,10 +1100,12 @@ static int atmel_ts_probe(struct i2c_client *client, printk(KERN_INFO "No Atmel chip inside\n"); goto err_detect_failed; } - printk(KERN_INFO "Touch: 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X\n", + printk(KERN_INFO + "Touch: 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X\n", data[0], data[1], data[2], data[3], data[4], data[5], data[6]); - if (data[1] & 0x24) { + if (data[MSG_RID] == 0x01 && + (data[T6_MSG_STATUS] & (T6_MSG_STATUS_SIGERR|T6_MSG_STATUS_COMSERR))) { printk(KERN_INFO "atmel_ts_probe(): init err: %x\n", data[1]); goto err_detect_failed; } else { @@ -872,7 +1115,8 @@ static int atmel_ts_probe(struct i2c_client *client, break; } ret = i2c_transfer(client->adapter, msg, 1); - printk(KERN_INFO "Touch: 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X\n", + printk(KERN_INFO + "Touch: 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X\n", data[0], data[1], data[2], data[3], data[4], data[5], data[6]); msleep(10); } @@ -886,18 +1130,19 @@ static int atmel_ts_probe(struct i2c_client *client, } ret = i2c_atmel_read(client, 0x00, data, 7); - ts->id->family_id = data[0]; - ts->id->variant_id = data[1]; + ts->id->family_id = data[INFO_BLK_FID]; + ts->id->variant_id = data[INFO_BLK_VID]; if (ts->id->family_id == 0x80 && ts->id->variant_id == 0x10) - ts->id->version = data[2] + 6; + ts->id->version = data[INFO_BLK_VER] + 6; else - ts->id->version = data[2]; - ts->id->build = data[3]; - ts->id->matrix_x_size = data[4]; - ts->id->matrix_y_size = data[5]; - ts->id->num_declared_objects = data[6]; - - printk(KERN_INFO "info block: 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X\n", + ts->id->version = data[INFO_BLK_VER]; + ts->id->build = data[INFO_BLK_BUILD]; + ts->id->matrix_x_size = data[INFO_BLK_XSIZE]; + ts->id->matrix_y_size = data[INFO_BLK_YSIZE]; + ts->id->num_declared_objects = data[INFO_BLK_OBJS]; + + printk(KERN_INFO + "info block: 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X\n", ts->id->family_id, ts->id->variant_id, ts->id->version, ts->id->build, ts->id->matrix_x_size, ts->id->matrix_y_size, @@ -917,38 +1162,21 @@ static int atmel_ts_probe(struct i2c_client *client, /* END */ - + /* Read object table. */ - ts->object_table = kzalloc(sizeof(struct object_t)*ts->id->num_declared_objects, GFP_KERNEL); - if (ts->object_table == NULL) { - printk(KERN_ERR"%s: allocate object_table failed\n", __func__); + ret = read_object_table(ts); + if (ret < 0) goto err_alloc_failed; - } - for (i = 0; i < ts->id->num_declared_objects; i++) { - ret = i2c_atmel_read(client, i * 6 + 0x07, data, 6); - ts->object_table[i].object_type = data[0]; - ts->object_table[i].i2c_address = data[1] | data[2] << 8; - ts->object_table[i].size = data[3] + 1; - ts->object_table[i].instances = data[4]; - ts->object_table[i].num_report_ids = data[5]; - if (data[5]) { - ts->object_table[i].report_ids = type_count + 1; - type_count += data[5]; - } - if (data[0] == 9) - ts->finger_type = ts->object_table[i].report_ids; - printk(KERN_INFO "Type: %2.2X, Start: %4.4X, Size: %2X, Instance: %2X, RD#: %2X, %2X\n", - ts->object_table[i].object_type , ts->object_table[i].i2c_address, - ts->object_table[i].size, ts->object_table[i].instances, - ts->object_table[i].num_report_ids, ts->object_table[i].report_ids); - } if (pdata) { while (pdata->version > ts->id->version) pdata++; if (pdata->source) { i2c_atmel_write_byte_data(client, - get_object_address(ts, SPT_GPIOPWM_T19), 0x7); + get_object_address(ts, SPT_GPIOPWM_T19) + T19_CFG_CTRL, + T19_CFG_CTRL_ENABLE | + T19_CFG_CTRL_RPTEN | + T19_CFG_CTRL_FORCERPT); for (loop_i = 0; loop_i < 10; loop_i++) { if (!gpio_get_value(intr)) break; @@ -956,29 +1184,32 @@ static int atmel_ts_probe(struct i2c_client *client, } if (loop_i == 10) printk(KERN_ERR "No Messages when check source\n"); - for (loop_i = 0; loop_i < 10; loop_i++) { + for (loop_i = 0; loop_i < 100; loop_i++) { i2c_atmel_read(ts->client, get_object_address(ts, GEN_MESSAGEPROCESSOR_T5), data, 2); - if (data[0] == get_report_ids_size(ts, SPT_GPIOPWM_T19)) { - while ((data[1] >> 3) != pdata->source) + if (data[MSG_RID] == get_rid(ts, SPT_GPIOPWM_T19)) { + while ((data[T19_MSG_STATUS] >> 3) != pdata->source) pdata++; break; } } } - ts->finger_support = pdata->config_T9[14]; - printk(KERN_INFO"finger_type: %d, max finger: %d\n", ts->finger_type, ts->finger_support); + ts->finger_support = pdata->config_T9[T9_CFG_NUMTOUCH]; + printk(KERN_INFO + "finger_type: %d, max finger: %d\n", + ts->finger_type, ts->finger_support); /* infoamtion block CRC check */ if (pdata->object_crc[0]) { ret = i2c_atmel_write_byte_data(client, - get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + 2, 0x55); + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_CALIBRATE, 0x55); for (loop_i = 0; loop_i < 10; loop_i++) { if (!gpio_get_value(intr)) { ret = i2c_atmel_read(ts->client, get_object_address(ts, - GEN_MESSAGEPROCESSOR_T5), data, 5); - if (data[0] == get_report_ids_size(ts, GEN_COMMANDPROCESSOR_T6)) + GEN_MESSAGEPROCESSOR_T5), data, 5); + if (data[MSG_RID] == get_rid(ts, GEN_COMMANDPROCESSOR_T6)) break; } msleep(10); @@ -987,8 +1218,12 @@ static int atmel_ts_probe(struct i2c_client *client, printk(KERN_INFO "Touch: No checksum read\n"); else { for (loop_i = 0; loop_i < 3; loop_i++) { - if (pdata->object_crc[loop_i] != data[loop_i + 2]) { - printk(KERN_ERR"CRC Error: %x, %x\n", pdata->object_crc[loop_i], data[loop_i + 2]); + if (pdata->object_crc[loop_i] != + data[T6_MSG_CHECKSUM + loop_i]) { + printk(KERN_ERR + "CRC Error: %x, %x\n", + pdata->object_crc[loop_i], + data[T6_MSG_CHECKSUM + loop_i]); break; } } @@ -1010,88 +1245,149 @@ static int atmel_ts_probe(struct i2c_client *client, ts->abs_width_min = pdata->abs_width_min; ts->abs_width_max = pdata->abs_width_max; ts->GCAF_level = pdata->GCAF_level; - printk(KERN_INFO "GCAF_level: %d, %d, %d, %d, %d\n", - ts->GCAF_level[0], ts->GCAF_level[1], ts->GCAF_level[2], - ts->GCAF_level[3], ts->GCAF_level[4]); + if (ts->id->version >= 0x20) + ts->ATCH_EXT = &pdata->config_T8[6]; ts->filter_level = pdata->filter_level; - printk(KERN_INFO "filter_level: %d, %d, %d, %d\n", - ts->filter_level[0], ts->filter_level[1], ts->filter_level[2], ts->filter_level[3]); - ts->config_setting[0].config_T7 - = ts->config_setting[1].config_T7 + if (usb_get_connect_type()) + ts->status = CONNECTED; + + ts->config_setting[NONE].config_T7 + = ts->config_setting[CONNECTED].config_T7 = pdata->config_T7; - ts->config_setting[0].config_T8 - = ts->config_setting[1].config_T8 + ts->config_setting[NONE].config_T8 + = ts->config_setting[CONNECTED].config_T8 = pdata->config_T8; - ts->config_setting[0].config_T9 = pdata->config_T9; - ts->config_setting[0].config_T22 = pdata->config_T22; - ts->config_setting[0].config_T28 = pdata->config_T28; + ts->config_setting[NONE].config_T9 = pdata->config_T9; + ts->config_setting[NONE].config_T22 = pdata->config_T22; + ts->config_setting[NONE].config_T28 = pdata->config_T28; + + if (pdata->noise_config[0]) + for (loop_i = 0; loop_i < 3; loop_i++) + ts->noise_config[loop_i] = pdata->noise_config[loop_i]; if (pdata->cable_config[0]) { - ts->config_setting[0].config[0] = pdata->config_T9[7]; - ts->config_setting[0].config[1] = pdata->config_T22[8]; - ts->config_setting[0].config[2] = pdata->config_T28[3]; - ts->config_setting[0].config[3] = pdata->config_T28[4]; + ts->config_setting[NONE].config[CB_TCHTHR] = + pdata->config_T9[T9_CFG_TCHTHR]; + ts->config_setting[NONE].config[CB_NOISETHR] = + pdata->config_T22[T22_CFG_NOISETHR]; + ts->config_setting[NONE].config[CB_IDLEGCAFDEPTH] = + pdata->config_T28[T28_CFG_IDLEGCAFDEPTH]; + ts->config_setting[NONE].config[CB_ACTVGCAFDEPTH] = + pdata->config_T28[T28_CFG_ACTVGCAFDEPTH]; for (loop_i = 0; loop_i < 4; loop_i++) - ts->config_setting[1].config[loop_i] = pdata->cable_config[loop_i]; - ts->GCAF_sample = ts->config_setting[1].config[3]; - ts->noisethr = pdata->cable_config[1]; + ts->config_setting[CONNECTED].config[loop_i] = + pdata->cable_config[loop_i]; + ts->GCAF_sample = + ts->config_setting[CONNECTED].config[CB_ACTVGCAFDEPTH]; + if (ts->id->version >= 0x20) + ts->noisethr = pdata->cable_config[CB_TCHTHR] - + pdata->config_T9[T9_CFG_TCHHYST]; + else + ts->noisethr = pdata->cable_config[CB_TCHTHR]; + ts->noisethr_config = + ts->config_setting[CONNECTED].config[CB_NOISETHR]; } else { if (pdata->cable_config_T7[0]) - ts->config_setting[1].config_T7 = pdata->cable_config_T7; + ts->config_setting[CONNECTED].config_T7 = + pdata->cable_config_T7; if (pdata->cable_config_T8[0]) - ts->config_setting[1].config_T8 = pdata->cable_config_T8; + ts->config_setting[CONNECTED].config_T8 = + pdata->cable_config_T8; if (pdata->cable_config_T9[0]) { - ts->config_setting[1].config_T9 = pdata->cable_config_T9; - ts->config_setting[1].config_T22 = pdata->cable_config_T22; - ts->config_setting[1].config_T28 = pdata->cable_config_T28; - ts->GCAF_sample = ts->config_setting[ts->status].config_T28[4]; + ts->config_setting[CONNECTED].config_T9 = + pdata->cable_config_T9; + ts->config_setting[CONNECTED].config_T22 = + pdata->cable_config_T22; + ts->config_setting[CONNECTED].config_T28 = + pdata->cable_config_T28; + ts->GCAF_sample = + ts->config_setting[CONNECTED].config_T28[T28_CFG_ACTVGCAFDEPTH]; } + if (ts->status == CONNECTED) + ts->noisethr = (ts->id->version >= 0x20) ? + pdata->cable_config_T9[T9_CFG_TCHTHR] - pdata->cable_config_T9[T9_CFG_TCHHYST] : + pdata->cable_config_T9[T9_CFG_TCHTHR]; + else + ts->noisethr = (ts->id->version >= 0x20) ? + pdata->config_T9[T9_CFG_TCHTHR] - pdata->config_T9[T9_CFG_TCHHYST] : + pdata->config_T9[T9_CFG_TCHTHR]; + ts->noisethr_config = pdata->cable_config_T22[T22_CFG_NOISETHR]; + } -#if 0 -///fix me!! TODO if (usb_get_connect_type()) - ts->status = 1; -#endif + if (!CRC_check) { printk(KERN_INFO "Touch: Config reload\n"); i2c_atmel_write(ts->client, get_object_address(ts, SPT_CTECONFIG_T28), pdata->config_T28, get_object_size(ts, SPT_CTECONFIG_T28)); - ret = i2c_atmel_write_byte_data(client, get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + 1, 0x55); + ret = i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_BACKUPNV, 0x55); msleep(10); - ret = i2c_atmel_write_byte_data(client, get_object_address(ts, GEN_COMMANDPROCESSOR_T6), 0x11); - msleep(64); - - i2c_atmel_write(ts->client, get_object_address(ts, GEN_COMMANDPROCESSOR_T6), - pdata->config_T6, get_object_size(ts, GEN_COMMANDPROCESSOR_T6)); - i2c_atmel_write(ts->client, get_object_address(ts, GEN_POWERCONFIG_T7), - pdata->config_T7, get_object_size(ts, GEN_POWERCONFIG_T7)); - i2c_atmel_write(ts->client, get_object_address(ts, GEN_ACQUISITIONCONFIG_T8), - pdata->config_T8, get_object_size(ts, GEN_ACQUISITIONCONFIG_T8)); - i2c_atmel_write(ts->client, get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9), - pdata->config_T9, get_object_size(ts, TOUCH_MULTITOUCHSCREEN_T9)); - i2c_atmel_write(ts->client, get_object_address(ts, TOUCH_KEYARRAY_T15), - pdata->config_T15, get_object_size(ts, TOUCH_KEYARRAY_T15)); - i2c_atmel_write(ts->client, get_object_address(ts, SPT_GPIOPWM_T19), - pdata->config_T19, get_object_size(ts, SPT_GPIOPWM_T19)); - i2c_atmel_write(ts->client, get_object_address(ts, PROCI_GRIPFACESUPPRESSION_T20), - pdata->config_T20, get_object_size(ts, PROCI_GRIPFACESUPPRESSION_T20)); - i2c_atmel_write(ts->client, get_object_address(ts, PROCG_NOISESUPPRESSION_T22), - pdata->config_T22, get_object_size(ts, PROCG_NOISESUPPRESSION_T22)); - i2c_atmel_write(ts->client, get_object_address(ts, TOUCH_PROXIMITY_T23), - pdata->config_T23, get_object_size(ts, TOUCH_PROXIMITY_T23)); - i2c_atmel_write(ts->client, get_object_address(ts, PROCI_ONETOUCHGESTUREPROCESSOR_T24), - pdata->config_T24, get_object_size(ts, PROCI_ONETOUCHGESTUREPROCESSOR_T24)); - i2c_atmel_write(ts->client, get_object_address(ts, SPT_SELFTEST_T25), - pdata->config_T25, get_object_size(ts, SPT_SELFTEST_T25)); - i2c_atmel_write(ts->client, get_object_address(ts, PROCI_TWOTOUCHGESTUREPROCESSOR_T27), - pdata->config_T27, get_object_size(ts, PROCI_TWOTOUCHGESTUREPROCESSOR_T27)); - i2c_atmel_write(ts->client, get_object_address(ts, SPT_CTECONFIG_T28), - pdata->config_T28, get_object_size(ts, SPT_CTECONFIG_T28)); + ret = i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_RESET, 0x11); + msleep(100); + + i2c_atmel_write(ts->client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6), + pdata->config_T6, + get_object_size(ts, GEN_COMMANDPROCESSOR_T6)); + i2c_atmel_write(ts->client, + get_object_address(ts, GEN_POWERCONFIG_T7), + pdata->config_T7, + get_object_size(ts, GEN_POWERCONFIG_T7)); + i2c_atmel_write(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8), + pdata->config_T8, + get_object_size(ts, GEN_ACQUISITIONCONFIG_T8)); + i2c_atmel_write(ts->client, + get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9), + pdata->config_T9, + get_object_size(ts, TOUCH_MULTITOUCHSCREEN_T9)); + i2c_atmel_write(ts->client, + get_object_address(ts, TOUCH_KEYARRAY_T15), + pdata->config_T15, + get_object_size(ts, TOUCH_KEYARRAY_T15)); + i2c_atmel_write(ts->client, + get_object_address(ts, SPT_GPIOPWM_T19), + pdata->config_T19, + get_object_size(ts, SPT_GPIOPWM_T19)); + i2c_atmel_write(ts->client, + get_object_address(ts, PROCI_GRIPFACESUPPRESSION_T20), + pdata->config_T20, + get_object_size(ts, PROCI_GRIPFACESUPPRESSION_T20)); + i2c_atmel_write(ts->client, + get_object_address(ts, PROCG_NOISESUPPRESSION_T22), + pdata->config_T22, + get_object_size(ts, PROCG_NOISESUPPRESSION_T22)); + i2c_atmel_write(ts->client, + get_object_address(ts, TOUCH_PROXIMITY_T23), + pdata->config_T23, + get_object_size(ts, TOUCH_PROXIMITY_T23)); + i2c_atmel_write(ts->client, + get_object_address(ts, PROCI_ONETOUCHGESTUREPROCESSOR_T24), + pdata->config_T24, + get_object_size(ts, PROCI_ONETOUCHGESTUREPROCESSOR_T24)); + i2c_atmel_write(ts->client, + get_object_address(ts, SPT_SELFTEST_T25), + pdata->config_T25, + get_object_size(ts, SPT_SELFTEST_T25)); + i2c_atmel_write(ts->client, + get_object_address(ts, PROCI_TWOTOUCHGESTUREPROCESSOR_T27), + pdata->config_T27, + get_object_size(ts, PROCI_TWOTOUCHGESTUREPROCESSOR_T27)); + i2c_atmel_write(ts->client, + get_object_address(ts, SPT_CTECONFIG_T28), + pdata->config_T28, + get_object_size(ts, SPT_CTECONFIG_T28)); - ret = i2c_atmel_write_byte_data(client, get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + 1, 0x55); + ret = i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_BACKUPNV, 0x55); for (loop_i = 0; loop_i < 10; loop_i++) { if (!gpio_get_value(intr)) @@ -1100,60 +1396,71 @@ static int atmel_ts_probe(struct i2c_client *client, msleep(10); } - i2c_atmel_read(client, get_object_address(ts, GEN_MESSAGEPROCESSOR_T5), data, 7); - printk(KERN_INFO "Touch: 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X\n", + i2c_atmel_read(client, + get_object_address(ts, GEN_MESSAGEPROCESSOR_T5), data, 7); + printk(KERN_INFO + "Touch: 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X\n", data[0], data[1], data[2], data[3], data[4], data[5], data[6]); - ret = i2c_atmel_write_byte_data(client, get_object_address(ts, GEN_COMMANDPROCESSOR_T6), 0x11); - msleep(64); + ret = i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_RESET, 0x11); + msleep(100); - i2c_atmel_read(client, get_object_address(ts, GEN_MESSAGEPROCESSOR_T5), data, 7); - printk(KERN_INFO "Touch: 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X 0x%2.2X\n", - data[0], data[1], data[2], data[3], data[4], data[5], data[6]); - /* For Ace */ } - if (ts->status) { + if (ts->status == CONNECTED) { printk(KERN_INFO "Touch: set cable config\n"); - if (ts->config_setting[1].config[0]) { + if (ts->config_setting[CONNECTED].config[0]) { i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9) + 7, - ts->config_setting[1].config[0]); + get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9) + + T9_CFG_TCHTHR, + ts->config_setting[CONNECTED].config[CB_TCHTHR]); i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, SPT_CTECONFIG_T28) + 3, - ts->config_setting[1].config[2]); + get_object_address(ts, SPT_CTECONFIG_T28) + + T28_CFG_IDLEGCAFDEPTH, + ts->config_setting[CONNECTED].config[CB_IDLEGCAFDEPTH]); i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, SPT_CTECONFIG_T28) + 4, - ts->config_setting[1].config[3]); + get_object_address(ts, SPT_CTECONFIG_T28) + + T28_CFG_ACTVGCAFDEPTH, + ts->config_setting[CONNECTED].config[CB_ACTVGCAFDEPTH]); } else { - if (ts->config_setting[1].config_T7 != NULL) + if (ts->config_setting[CONNECTED].config_T7 != NULL) i2c_atmel_write(ts->client, get_object_address(ts, GEN_POWERCONFIG_T7), - ts->config_setting[1].config_T7, + ts->config_setting[CONNECTED].config_T7, get_object_size(ts, GEN_POWERCONFIG_T7)); - if (ts->config_setting[1].config_T8 != NULL) + if (ts->config_setting[CONNECTED].config_T8 != NULL) i2c_atmel_write(ts->client, get_object_address(ts, GEN_ACQUISITIONCONFIG_T8), - ts->config_setting[1].config_T8, + ts->config_setting[CONNECTED].config_T8, get_object_size(ts, GEN_ACQUISITIONCONFIG_T8)); - if (ts->config_setting[1].config_T9 != NULL) + if (ts->config_setting[CONNECTED].config_T9 != NULL) i2c_atmel_write(ts->client, get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9), - ts->config_setting[1].config_T9, + ts->config_setting[CONNECTED].config_T9, get_object_size(ts, TOUCH_MULTITOUCHSCREEN_T9)); - if (ts->config_setting[1].config_T22 != NULL) + if (ts->config_setting[CONNECTED].config_T22 != NULL) i2c_atmel_write(ts->client, get_object_address(ts, PROCG_NOISESUPPRESSION_T22), - ts->config_setting[1].config_T22, + ts->config_setting[CONNECTED].config_T22, get_object_size(ts, PROCG_NOISESUPPRESSION_T22)); - if (ts->config_setting[1].config_T28 != NULL) { + if (ts->config_setting[CONNECTED].config_T28 != NULL) { i2c_atmel_write(ts->client, get_object_address(ts, SPT_CTECONFIG_T28), - ts->config_setting[1].config_T28, + ts->config_setting[CONNECTED].config_T28, get_object_size(ts, SPT_CTECONFIG_T28)); } } } + if (ts->id->version >= 0x20) { + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9) + T9_CFG_TCHTHR, + ts->config_setting[ts->status].config[0] + 10); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + T8_CFG_ATCHCALSTHR, + ts->config_setting[ts->status].config[0] + 5); + } } ts->input_dev = input_allocate_device(); if (ts->input_dev == NULL) { @@ -1181,7 +1488,7 @@ static int atmel_ts_probe(struct i2c_client *client, input_set_abs_params(ts->input_dev, ABS_MT_AMPLITUDE, 0, ((ts->abs_pressure_max << 16) | ts->abs_width_max), 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_POSITION, - 0, ((1 << 31) | (ts->abs_x_max << 16) | ts->abs_y_max), 0, 0); + 0, (BIT(31) | (ts->abs_x_max << 16) | ts->abs_y_max), 0, 0); #endif @@ -1270,10 +1577,17 @@ static int atmel_ts_suspend(struct i2c_client *client, pm_message_t mesg) ts->finger_count = 0; ts->first_pressed = 0; + if (ts->id->version >= 0x20) { + ts->pre_data[0] = 0; + i2c_atmel_write(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + T8_CFG_ATCHCALST, + ts->ATCH_EXT, 4); + } + i2c_atmel_write_byte_data(client, - get_object_address(ts, GEN_POWERCONFIG_T7), 0x0); + get_object_address(ts, GEN_POWERCONFIG_T7) + T7_CFG_IDLEACQINT, 0x0); i2c_atmel_write_byte_data(client, - get_object_address(ts, GEN_POWERCONFIG_T7) + 1, 0x0); + get_object_address(ts, GEN_POWERCONFIG_T7) + T7_CFG_ACTVACQINT, 0x0); return 0; } @@ -1282,24 +1596,49 @@ static int atmel_ts_resume(struct i2c_client *client) { struct atmel_ts_data *ts = i2c_get_clientdata(client); - i2c_atmel_write(ts->client, get_object_address(ts, GEN_POWERCONFIG_T7), - ts->config_setting[ts->status].config_T7, get_object_size(ts, GEN_POWERCONFIG_T7)); - if (ts->config_setting[1].config[0] && ts->status && !check_delta(ts)) { - ts->calibration_confirm = 2; + if (ts->id->version >= 0x20) { i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 6, - ts->config_setting[ts->status].config_T8[6]); + get_object_address(ts, TOUCH_MULTITOUCHSCREEN_T9) + T9_CFG_TCHTHR, + ts->config_setting[ts->status].config[0] + 10); i2c_atmel_write_byte_data(ts->client, - get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 7, - ts->config_setting[ts->status].config_T8[7]); + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + T8_CFG_ATCHCALSTHR, + ts->config_setting[ts->status].config[0] + 5); + } + + i2c_atmel_write(ts->client, + get_object_address(ts, GEN_POWERCONFIG_T7), + ts->config_setting[ts->status].config_T7, + get_object_size(ts, GEN_POWERCONFIG_T7)); + if (ts->id->version == 0x16) { + if (ts->config_setting[CONNECTED].config[0] && ts->status && + !check_delta(ts)) { + ts->calibration_confirm = 2; + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + + T8_CFG_ATCHCALST, + ts->config_setting[ts->status].config_T8[T8_CFG_ATCHCALST]); + i2c_atmel_write_byte_data(ts->client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + + T8_CFG_ATCHCALSTHR, + ts->config_setting[ts->status].config_T8[T8_CFG_ATCHCALSTHR]); + } else { + ts->calibration_confirm = 0; + msleep(1); + i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_CALIBRATE, 0x55); + i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + + T8_CFG_ATCHCALST, 0x0); + i2c_atmel_write_byte_data(client, + get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + + T8_CFG_ATCHCALSTHR, 0x0); + } } else { - ts->calibration_confirm = 0; - i2c_atmel_write_byte_data(client, - get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + 2, 0x55); - i2c_atmel_write_byte_data(client, - get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 6, 0x0); + msleep(1); i2c_atmel_write_byte_data(client, - get_object_address(ts, GEN_ACQUISITIONCONFIG_T8) + 7, 0x0); + get_object_address(ts, GEN_COMMANDPROCESSOR_T6) + + T6_CFG_CALIBRATE, 0x55); } enable_irq(client->irq); diff --git a/include/linux/atmel_qt602240.h b/include/linux/atmel_qt602240.h index 6c4b9d2e3f7e1..8d468aadd25ce 100644 --- a/include/linux/atmel_qt602240.h +++ b/include/linux/atmel_qt602240.h @@ -1,8 +1,25 @@ #ifndef _LINUX_ATMEL_H #define _LINUX_ATMEL_H +#include + #define ATMEL_QT602240_NAME "atmel_qt602240" +#define INFO_BLK_FID 0 +#define INFO_BLK_VID 1 +#define INFO_BLK_VER 2 +#define INFO_BLK_BUILD 3 +#define INFO_BLK_XSIZE 4 +#define INFO_BLK_YSIZE 5 +#define INFO_BLK_OBJS 6 + +#define OBJ_TABLE_TYPE 0 +#define OBJ_TABLE_LSB 1 +#define OBJ_TABLE_MSB 2 +#define OBJ_TABLE_SIZE 3 +#define OBJ_TABLE_INSTANCES 4 +#define OBJ_TABLE_RIDS 5 + #define RESERVED_T0 0u #define RESERVED_T1 1u #define DEBUG_DELTAS_T2 2u @@ -26,7 +43,7 @@ #define PROCI_GRIPFACESUPPRESSION_T20 20u #define RESERVED_T21 21u #define PROCG_NOISESUPPRESSION_T22 22u -#define TOUCH_PROXIMITY_T23 23u +#define TOUCH_PROXIMITY_T23 23u #define PROCI_ONETOUCHGESTUREPROCESSOR_T24 24u #define SPT_SELFTEST_T25 25u #define DEBUG_CTERANGE_T26 26u @@ -36,7 +53,207 @@ #define SPT_GATE_T30 30u #define TOUCH_KEYSET_T31 31u #define TOUCH_XSLIDERSET_T32 32u -#define DIAGNOSTIC_T37 37u +#define DIAGNOSTIC_T37 37u + +#define T37_PAGE_SIZE 128 + +#define T37_TCH_FLAG_SIZE 80 +#define T37_TCH_FLAG_IDX 0 +#define T37_ATCH_FLAG_IDX 40 + +#define T37_MODE 0 +#define T37_PAGE 1 +#define T37_DATA 2 /* n bytes */ + +#define T37_PAGE_NUM0 0 +#define T37_PAGE_NUM1 1 +#define T37_PAGE_NUM2 2 +#define T37_PAGE_NUM3 3 + +#define MSG_RID 0 + +#define T6_CFG_RESET 0 +#define T6_CFG_BACKUPNV 1 +#define T6_CFG_CALIBRATE 2 +#define T6_CFG_REPORTALL 3 +/* Reserved */ +#define T6_CFG_DIAG 5 + +#define T6_CFG_DIAG_CMD_PAGEUP 0x01 +#define T6_CFG_DIAG_CMD_PAGEDOWN 0x02 +#define T6_CFG_DIAG_CMD_DELTAS 0x10 +#define T6_CFG_DIAG_CMD_REF 0x11 +#define T6_CFG_DIAG_CMD_CTE 0x31 +#define T6_CFG_DIAG_CMD_TCH 0xF3 + +#define T6_MSG_STATUS 1 +#define T6_MSG_CHECKSUM 2 /* three bytes */ + +#define T6_MSG_STATUS_COMSERR BIT(2) +#define T6_MSG_STATUS_CFGERR BIT(3) +#define T6_MSG_STATUS_CAL BIT(4) +#define T6_MSG_STATUS_SIGERR BIT(5) +#define T6_MSG_STATUS_OFL BIT(6) +#define T6_MSG_STATUS_RESET BIT(7) + +#define T7_CFG_IDLEACQINT 0 +#define T7_CFG_ACTVACQINT 1 +#define T7_CFG_ACTV2IDLETO 2 + +#define T8_CFG_CHRGTIME 0 +/* Reserved */ +#define T8_CFG_TCHDRIFT 2 +#define T8_CFG_DRIFTST 3 +#define T8_CFG_TCHAUTOCAL 4 +#define T8_CFG_SYNC 5 +#define T8_CFG_ATCHCALST 6 +#define T8_CFG_ATCHCALSTHR 7 +#define T8_CFG_ATCHFRCCALTHR 8 /* FW v2.x */ +#define T8_CFG_ATCHFRCCALRATIO 9 /* FW v2.x */ + +#define T9_CFG_CTRL 0 +#define T9_CFG_XORIGIN 1 +#define T9_CFG_YORIGIN 2 +#define T9_CFG_XSIZE 3 +#define T9_CFG_YSIZE 4 +#define T9_CFG_AKSCFG 5 +#define T9_CFG_BLEN 6 +#define T9_CFG_TCHTHR 7 +#define T9_CFG_TCHDI 8 +#define T9_CFG_ORIENT 9 +#define T9_CFG_MRGTIMEOUT 10 +#define T9_CFG_MOVHYSTI 11 +#define T9_CFG_MOVHYSTN 12 +#define T9_CFG_MOVFILTER 13 +#define T9_CFG_NUMTOUCH 14 +#define T9_CFG_MRGHYST 15 +#define T9_CFG_MRGTHR 16 +#define T9_CFG_AMPHYST 17 +#define T9_CFG_XRANGE 18 /* two bytes */ +#define T9_CFG_YRANGE 20 /* two bytes */ +#define T9_CFG_XLOCLIP 22 +#define T9_CFG_XHICLIP 23 +#define T9_CFG_YLOCLIP 24 +#define T9_CFG_YHICLIP 25 +#define T9_CFG_XEDGECTRL 26 +#define T9_CFG_XEDGEDIST 27 +#define T9_CFG_YEDGECTRL 28 +#define T9_CFG_YEDGEDIST 29 +#define T9_CFG_JUMPLIMIT 30 +#define T9_CFG_TCHHYST 31 /* FW v2.x */ + +#define T9_MSG_STATUS 1 +#define T9_MSG_XPOSMSB 2 +#define T9_MSG_YPOSMSB 3 +#define T9_MSG_XYPOSLSB 4 +#define T9_MSG_TCHAREA 5 +#define T9_MSG_TCHAMPLITUDE 6 +#define T9_MSG_TCHVECTOR 7 + +#define T9_MSG_STATUS_UNGRIP BIT(0) /* FW v2.x */ +#define T9_MSG_STATUS_SUPPRESS BIT(1) +#define T9_MSG_STATUS_AMP BIT(2) +#define T9_MSG_STATUS_VECTOR BIT(3) +#define T9_MSG_STATUS_MOVE BIT(4) +#define T9_MSG_STATUS_RELEASE BIT(5) +#define T9_MSG_STATUS_PRESS BIT(6) +#define T9_MSG_STATUS_DETECT BIT(7) + +#define T20_CFG_CTRL 0 +#define T20_CFG_XLOGRIP 1 +#define T20_CFG_XHIGRIP 2 +#define T20_CFG_YLOGRIP 3 +#define T20_CFG_YHIGRIP 4 +#define T20_CFG_MAXTCHS 5 +/* Reserved */ +#define T20_CFG_SZTHR1 7 +#define T20_CFG_SZTHR2 8 +#define T20_CFG_SHPTHR1 9 +#define T20_CFG_SHPTHR2 10 +#define T20_CFG_SHPEXTTO 11 + +#define T20_MSG_STATUS 1 + +#define T20_MSG_STATUS_FACESUP BIT(0) + +#define T22_CFG_CTRL 0 +/* Reserved */ +#define T22_CFG_GCAFUL 3 /* two bytes */ +#define T22_CFG_GCAFLL 5 /* two bytes */ +#define T22_CFG_ACTVGCAFVALID 7 +#define T22_CFG_NOISETHR 8 +/* Reserved */ +#define T22_CFG_FREQHOPSCALE 10 +#define T22_CFG_FREQ 11 /* five bytes */ +#define T22_CFG_IDLEGCAFVAILD 16 + +#define T22_MSG_STATUS 1 +#define T22_MSG_GCAFDEPTH 2 +#define T22_MSG_FREQINDEX 3 + +#define T22_MSG_STATUS_FHCHG BIT(0) +#define T22_MSG_STATUS_GCAFERR BIT(2) +#define T22_MSG_STATUS_FHERR BIT(3) +#define T22_MSG_STATUS_GCAFCHG BIT(4) + +#define T19_CFG_CTRL 0 +#define T19_CFG_REPORTMASK 1 +#define T19_CFG_DIR 2 +#define T19_CFG_INTPULLUP 3 +#define T19_CFG_OUT 4 +#define T19_CFG_WAKE 5 +#define T19_CFG_PWM 6 +#define T19_CFG_PERIOD 7 +#define T19_CFG_DUTY0 8 +#define T19_CFG_DUTY1 9 +#define T19_CFG_DUTY2 10 +#define T19_CFG_DUTY3 11 +#define T19_CFG_TRIGGER0 12 +#define T19_CFG_TRIGGER1 13 +#define T19_CFG_TRIGGER2 14 +#define T19_CFG_TRIGGER3 15 + +#define T19_CFG_CTRL_ENABLE BIT(0) +#define T19_CFG_CTRL_RPTEN BIT(1) +#define T19_CFG_CTRL_FORCERPT BIT(2) + +#define T19_MSG_STATUS 1 + +#define T25_CFG_CTRL 0 +#define T25_CFG_CMD 1 + +#define T25_MSG_STATUS 1 +#define T25_MSG_INFO 2 /* five bytes */ + +#define T28_CFG_CTRL 0 +#define T28_CFG_CMD 1 +#define T28_CFG_MODE 2 +#define T28_CFG_IDLEGCAFDEPTH 3 +#define T28_CFG_ACTVGCAFDEPTH 4 +#define T28_CFG_VOLTAGE 5 + +#define T28_CFG_MODE0_X 16 +#define T28_CFG_MODE0_Y 14 + +#define T28_MSG_STATUS 1 + +/* cable_config[] of atmel_i2c_platform_data */ +/* config[] of atmel_config_data */ +#define CB_TCHTHR 0 +#define CB_NOISETHR 1 +#define CB_IDLEGCAFDEPTH 2 +#define CB_ACTVGCAFDEPTH 3 + +#define NC_TCHTHR 0 +#define NC_TCHDI 1 +#define NC_NOISETHR 2 + +/* filter_level */ +#define FL_XLOGRIPMIN 0 +#define FL_XLOGRIPMAX 1 +#define FL_XHIGRIPMIN 2 +#define FL_XHIGRIPMAX 3 + struct info_id_t { uint8_t family_id; uint8_t variant_id; @@ -81,22 +298,11 @@ struct atmel_i2c_platform_data { uint8_t abs_width_min; uint8_t abs_width_max; int gpio_irq; - int mach_type; int (*power)(int on); - const char *input_name; - uint16_t key_type; - struct atmel_virtual_key *virtual_key; - uint8_t virtual_key_num; - uint8_t inactive_left; - uint8_t inactive_right; - uint8_t inactive_top; - uint8_t inactive_bottom; - uint16_t gap_area; - uint16_t key_area; int8_t config_T6[6]; int8_t config_T7[3]; - int8_t config_T8[8]; - int8_t config_T9[31]; + int8_t config_T8[10]; + int8_t config_T9[32]; int8_t config_T15[11]; int8_t config_T19[12]; int8_t config_T20[12]; @@ -109,14 +315,13 @@ struct atmel_i2c_platform_data { uint8_t object_crc[3]; int8_t cable_config[4]; int8_t cable_config_T7[3]; - int8_t cable_config_T8[8]; - int8_t cable_config_T9[31]; + int8_t cable_config_T8[10]; + int8_t cable_config_T9[32]; int8_t cable_config_T22[17]; int8_t cable_config_T28[6]; + int8_t noise_config[3]; uint16_t filter_level[4]; uint8_t GCAF_level[5]; - int display_width; /* display width in pixel */ - int display_height; /* display height in pixel */ }; struct atmel_config_data { From 94e6a9146fd33eb051c14c7c3d1e70793800f314 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Mon, 8 Nov 2010 22:50:32 -0500 Subject: [PATCH 1034/2556] usb: gadget: Add MSM72K HTC variant driver Change-Id: I57e4eab66eb0cdc4b2c55cfffdeb75a1aa9f26c8 --- arch/arm/mach-msm/acpuclock-arm11.c | 16 +- arch/arm/mach-msm/acpuclock-qsd8x50.c | 16 +- arch/arm/mach-msm/include/mach/clk.h | 1 + arch/arm/mach-msm/include/mach/msm_hsusb.h | 7 + drivers/usb/gadget/Kconfig | 28 + drivers/usb/gadget/Makefile | 13 +- drivers/usb/gadget/android.c | 11 + drivers/usb/gadget/msm72k_udc_htc.c | 2892 ++++++++++++++++++++ 8 files changed, 2976 insertions(+), 8 deletions(-) create mode 100644 drivers/usb/gadget/msm72k_udc_htc.c diff --git a/arch/arm/mach-msm/acpuclock-arm11.c b/arch/arm/mach-msm/acpuclock-arm11.c index 1e599a782088b..1900b04cf85ea 100644 --- a/arch/arm/mach-msm/acpuclock-arm11.c +++ b/arch/arm/mach-msm/acpuclock-arm11.c @@ -95,6 +95,8 @@ struct clkctl_acpu_speed { short up; }; +static unsigned long max_axi_rate; + /* * ACPU speed table. Complete table is shown but certain speeds are commented * out to optimized speed switching. Initialize loops_per_jiffy to 0. @@ -436,7 +438,7 @@ int acpuclk_set_rate(unsigned long rate, enum setrate_reason reason) static void __init acpuclk_init(void) { - struct clkctl_acpu_speed *speed; + struct clkctl_acpu_speed *speed, *max_s; uint32_t div, sel; int rc; @@ -472,9 +474,21 @@ static void __init acpuclk_init(void) if (rc < 0) pr_err("Setting AXI min rate failed!\n"); + for (speed = acpu_freq_tbl; speed->a11clk_khz != 0; speed++) + ; + + max_s = speed - 1; + max_axi_rate = max_s->axiclk_khz * 1000; + printk(KERN_INFO "ACPU running at %d KHz\n", speed->a11clk_khz); } +unsigned long acpuclk_get_max_axi_rate(void) +{ + return max_axi_rate; +} +EXPORT_SYMBOL(acpuclk_get_max_axi_rate); + unsigned long acpuclk_get_rate(void) { WARN_ONCE(drv_state.current_speed == NULL, diff --git a/arch/arm/mach-msm/acpuclock-qsd8x50.c b/arch/arm/mach-msm/acpuclock-qsd8x50.c index 7100d7f5258c9..db246db077022 100644 --- a/arch/arm/mach-msm/acpuclock-qsd8x50.c +++ b/arch/arm/mach-msm/acpuclock-qsd8x50.c @@ -58,6 +58,8 @@ struct clkctl_acpu_speed { unsigned axiclk_khz; }; +static unsigned long max_axi_rate; + /* clock sources */ #define CLK_TCXO 0 /* 19.2 MHz */ #define CLK_GLOBAL_PLL 1 /* 768 MHz */ @@ -409,7 +411,7 @@ static int pll_request(unsigned id, unsigned on) static void __init acpuclk_init(void) { - struct clkctl_acpu_speed *speed; + struct clkctl_acpu_speed *speed, *max_s; unsigned init_khz; init_khz = acpuclk_find_speed(); @@ -461,7 +463,19 @@ static void __init acpuclk_init(void) init_khz, speed->acpu_khz); loops_per_jiffy = drv_state.current_speed->lpj; + + for (speed = acpu_freq_tbl; speed->acpu_khz != 0; speed++) + ; + + max_s = speed - 1; + max_axi_rate = max_s->axiclk_khz * 1000; +} + +unsigned long acpuclk_get_max_axi_rate(void) +{ + return max_axi_rate; } +EXPORT_SYMBOL(acpuclk_get_max_axi_rate); unsigned long acpuclk_get_rate(void) { diff --git a/arch/arm/mach-msm/include/mach/clk.h b/arch/arm/mach-msm/include/mach/clk.h index c05ca40478c78..998f1ac2a1764 100644 --- a/arch/arm/mach-msm/include/mach/clk.h +++ b/arch/arm/mach-msm/include/mach/clk.h @@ -54,4 +54,5 @@ int clk_reset(struct clk *clk, enum clk_reset_action action); /* Set clock-specific configuration parameters */ int clk_set_flags(struct clk *clk, unsigned long flags); +unsigned long acpuclk_get_max_axi_rate(void); #endif diff --git a/arch/arm/mach-msm/include/mach/msm_hsusb.h b/arch/arm/mach-msm/include/mach/msm_hsusb.h index 5ed4b459889c2..9a8598a70896d 100644 --- a/arch/arm/mach-msm/include/mach/msm_hsusb.h +++ b/arch/arm/mach-msm/include/mach/msm_hsusb.h @@ -34,13 +34,20 @@ struct msm_hsusb_platform_data { /* 1 : uart, 0 : usb */ void (*usb_uart_switch)(int); void (*config_usb_id_gpios)(bool enable); + void (*usb_hub_enable)(bool); + void (*serial_debug_gpios)(int); + int (*china_ac_detect)(void); + void (*disable_usb_charger)(void); /* val, reg pairs terminated by -1 */ int *phy_init_seq; char *serial_number; int usb_id_pin_gpio; + int dock_pin_gpio; + int id_pin_irq; bool enable_car_kit_detect; __u8 accessory_detect; + bool dock_detect; }; int usb_get_connect_type(void); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 9df64e685c912..8ce1ca8142103 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -581,6 +581,10 @@ config USB_MSM_72K default USB_GADGET select USB_GADGET_SELECTED +config USB_MSM_72K_HTC + boolean "Use HTC driver variant" + depends on USB_MSM_72K + config USB_GADGET_DUMMY_HCD boolean "Dummy HCD (DEVELOPMENT)" depends on USB=y || (USB=m && USB_GADGET=m) @@ -1096,4 +1100,28 @@ config USB_G_WEBCAM endchoice +if USB_MSM_72K_HTC + +config USB_ACCESSORY_DETECT + boolean "USB ACCESSORY DETECT" + depends on USB_ANDROID + default n + +config USB_ACCESSORY_DETECT_BY_ADC + boolean "DETECT USB ACCESSORY BY PMIC ADC" + depends on USB_ANDROID && USB_ACCESSORY_DETECT + default n + +config DOCK_ACCESSORY_DETECT + boolean "DOCK ACCESSORY DETECT" + depends on USB_ANDROID + default n + +config USB_BYPASS_VBUS_NOTIFY + boolean "USB BYPASS VBUS NOTIFY" + depends on USB_ANDROID + default n + +endif # USB_MSM_72K_HTC + endif # USB_GADGET diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 982820588ee35..b4bcfa5702599 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -21,14 +21,15 @@ fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o obj-$(CONFIG_USB_M66592) += m66592-udc.o obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o -obj-$(CONFIG_USB_CI13XXX_PCI) += ci13xxx_pci.o +obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o -obj-$(CONFIG_USB_EG20T) += pch_udc.o -obj-$(CONFIG_USB_PXA_U2O) += mv_udc.o -mv_udc-y := mv_udc_core.o mv_udc_phy.o -obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o -obj-$(CONFIG_USB_MSM_72K) += msm72k_udc.o + +ifeq ($(CONFIG_USB_MSM_72K_HTC),y) + obj-$(CONFIG_USB_MSM_72K_HTC) += msm72k_udc_htc.o +else + obj-$(CONFIG_USB_MSM_72K) += msm72k_udc.o +endif # # USB gadget drivers diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index eb6af6d538d6c..e6119a80265b0 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -443,6 +443,17 @@ void android_enable_function(struct usb_function *f, int enable) } } +void android_set_serialno(char *serialno) +{ + strings_dev[STRING_SERIAL_IDX].s = serialno; +} + +int android_get_model_id(void) +{ + struct android_dev *dev = _android_dev; + return dev->product_id; +} + static int android_probe(struct platform_device *pdev) { struct android_usb_platform_data *pdata = pdev->dev.platform_data; diff --git a/drivers/usb/gadget/msm72k_udc_htc.c b/drivers/usb/gadget/msm72k_udc_htc.c new file mode 100644 index 0000000000000..f442659ed9520 --- /dev/null +++ b/drivers/usb/gadget/msm72k_udc_htc.c @@ -0,0 +1,2892 @@ +/* + * Driver for HighSpeed USB Client Controller in MSM7K + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * Brian Swetland + * + * 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 + +#include + +#include +#include +#include +#include +#ifdef CONFIG_ARCH_MSM7X30 +#include +#endif +#ifdef CONFIG_USB_ACCESSORY_DETECT_BY_ADC +#include +#endif +#include + +static const char driver_name[] = "msm72k_udc"; + +/* #define DEBUG */ +/* #define VERBOSE */ + +#define MSM_USB_BASE ((unsigned) ui->addr) + +#define DRIVER_DESC "MSM 72K USB Peripheral Controller" + +#define EPT_FLAG_IN 0x0001 + +#define SETUP_BUF_SIZE 4096 + + +static const char *const ep_name[] = { + "ep0out", "ep1out", "ep2out", "ep3out", + "ep4out", "ep5out", "ep6out", "ep7out", + "ep8out", "ep9out", "ep10out", "ep11out", + "ep12out", "ep13out", "ep14out", "ep15out", + "ep0in", "ep1in", "ep2in", "ep3in", + "ep4in", "ep5in", "ep6in", "ep7in", + "ep8in", "ep9in", "ep10in", "ep11in", + "ep12in", "ep13in", "ep14in", "ep15in" +}; + +static struct usb_info *the_usb_info; +/* current state of VBUS */ +static int vbus; +static int use_mfg_serialno; +static char mfg_df_serialno[16]; +static int disable_charger; + +#if defined (CONFIG_DOCK_ACCESSORY_DETECT) || defined(CONFIG_USB_ACCESSORY_DETECT) +#ifdef CONFIG_USB_ACCESSORY_DETECT_BY_ADC +extern int htc_get_usb_accessory_adc_level(uint32_t *buffer); +#endif + + +static struct switch_dev dock_switch = { + .name = "dock", +}; + +#define DOCK_STATE_UNDOCKED 0 +#define DOCK_STATE_DESK (1 << 0) +#define DOCK_STATE_CAR (1 << 1) +#endif + +#include +#include + +static struct wake_lock vbus_idle_wake_lock; +static struct perf_lock usb_perf_lock; + +struct msm_request { + struct usb_request req; + + /* saved copy of req.complete */ + void (*gadget_complete)(struct usb_ep *ep, + struct usb_request *req); + + + struct usb_info *ui; + struct msm_request *next; + + unsigned busy:1; + unsigned live:1; + unsigned alloced:1; + unsigned dead:1; + + dma_addr_t dma; + dma_addr_t item_dma; + + struct ept_queue_item *item; +}; + +#define to_msm_request(r) container_of(r, struct msm_request, req) +#define to_msm_endpoint(r) container_of(r, struct msm_endpoint, ep) + +struct msm_endpoint { + struct usb_ep ep; + struct usb_info *ui; + struct msm_request *req; /* head of pending requests */ + struct msm_request *last; + unsigned flags; + + /* bit number (0-31) in various status registers + ** as well as the index into the usb_info's array + ** of all endpoints + */ + unsigned char bit; + unsigned char num; + + /* pointers to DMA transfer list area */ + /* these are allocated from the usb_info dma space */ + struct ept_queue_head *head; +}; + +static void usb_do_work(struct work_struct *w); +static void check_charger(struct work_struct *w); +#ifdef CONFIG_USB_ACCESSORY_DETECT +static void accessory_detect_work(struct work_struct *w); +#endif +#ifdef CONFIG_DOCK_ACCESSORY_DETECT +static void dock_detect_work(struct work_struct *w); +static void dock_detect_init(struct usb_info *ui); +#endif +extern int android_switch_function(unsigned func); +extern int android_show_function(char *buf); +extern void android_set_serialno(char *serialno); + +#define USB_STATE_IDLE 0 +#define USB_STATE_ONLINE 1 +#define USB_STATE_OFFLINE 2 + +#define USB_FLAG_START 0x0001 +#define USB_FLAG_VBUS_ONLINE 0x0002 +#define USB_FLAG_VBUS_OFFLINE 0x0004 +#define USB_FLAG_RESET 0x0008 + +enum usb_connect_type { + CONNECT_TYPE_NONE = 0, + CONNECT_TYPE_USB, + CONNECT_TYPE_AC, + CONNECT_TYPE_UNKNOWN, +}; + +struct usb_info { + /* lock for register/queue/device state changes */ + spinlock_t lock; + + /* single request used for handling setup transactions */ + struct usb_request *setup_req; + + struct platform_device *pdev; + int irq; + void *addr; + + unsigned state; + unsigned flags; + + unsigned online:1; + unsigned running:1; + + struct dma_pool *pool; + + /* dma page to back the queue heads and items */ + unsigned char *buf; + dma_addr_t dma; + + struct ept_queue_head *head; + + /* used for allocation */ + unsigned next_item; + unsigned next_ifc_num; + + /* endpoints are ordered based on their status bits, + ** so they are OUT0, OUT1, ... OUT15, IN0, IN1, ... IN15 + */ + struct msm_endpoint ept[32]; + + int *phy_init_seq; + void (*phy_reset)(void); + void (*hw_reset)(bool en); + void (*usb_uart_switch)(int); + void (*serial_debug_gpios)(int); + void (*usb_hub_enable)(bool); + int (*china_ac_detect)(void); + void (*disable_usb_charger)(void); + + /* for notification when USB is connected or disconnected */ + void (*usb_connected)(int); + + struct workqueue_struct *usb_wq; + struct work_struct work; + struct delayed_work chg_work; + struct work_struct detect_work; + struct work_struct dock_work; + struct work_struct notifier_work; + unsigned phy_status; + unsigned phy_fail_count; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + +#define ep0out ept[0] +#define ep0in ept[16] + + struct clk *clk; + struct clk *coreclk; + struct clk *pclk; + struct clk *otgclk; + struct clk *ebi1clk; + + unsigned int ep0_dir; + u16 test_mode; + + u8 remote_wakeup; + enum usb_connect_type connect_type; + u8 in_lpm; + + /* for accessory detection */ + bool dock_detect; + u8 accessory_detect; + u8 mfg_usb_carkit_enable; + int idpin_irq; + int dockpin_irq; + int usb_id_pin_gpio; + int dock_pin_gpio; + void (*config_usb_id_gpios)(bool output_enable); + /* 0: none, 1: carkit, 2: usb headset */ + u8 accessory_type; + struct timer_list ac_detect_timer; + int ac_detect_count; +}; + +static const struct usb_ep_ops msm72k_ep_ops; + + +static int msm72k_pullup(struct usb_gadget *_gadget, int is_active); +static int msm72k_set_halt(struct usb_ep *_ep, int value); +static void flush_endpoint(struct msm_endpoint *ept); + +static DEFINE_MUTEX(notify_sem); +static void send_usb_connect_notify(struct work_struct *w) +{ + static struct t_usb_status_notifier *notifier; + struct usb_info *ui = container_of(w, struct usb_info, + notifier_work); + if (!ui) + return; + + printk(KERN_INFO "usb: send connect type %d\n", ui->connect_type); + mutex_lock(¬ify_sem); + list_for_each_entry(notifier, + &g_lh_usb_notifier_list, + notifier_link) { + if (notifier->func != NULL) { + /* Notify other drivers about connect type. */ + /* use slow charging for unknown type*/ + if (ui->connect_type == CONNECT_TYPE_UNKNOWN) + notifier->func(CONNECT_TYPE_USB); + else + notifier->func(ui->connect_type); + } + } + mutex_unlock(¬ify_sem); +} + +int usb_register_notifier(struct t_usb_status_notifier *notifier) +{ + if (!notifier || !notifier->name || !notifier->func) + return -EINVAL; + + mutex_lock(¬ify_sem); + list_add(¬ifier->notifier_link, + &g_lh_usb_notifier_list); + mutex_unlock(¬ify_sem); + return 0; +} + +static int usb_ep_get_stall(struct msm_endpoint *ept) +{ + unsigned int n; + struct usb_info *ui = ept->ui; + + n = readl(USB_ENDPTCTRL(ept->num)); + if (ept->flags & EPT_FLAG_IN) + return (CTRL_TXS & n) ? 1 : 0; + else + return (CTRL_RXS & n) ? 1 : 0; +} + +static unsigned ulpi_read(struct usb_info *ui, unsigned reg) +{ + unsigned timeout = 100000; + + /* initiate read operation */ + writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), + USB_ULPI_VIEWPORT); + + /* wait for completion */ + while ((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout)) ; + + if (timeout == 0) { + ERROR("ulpi_read: timeout %08x\n", readl(USB_ULPI_VIEWPORT)); + return 0xffffffff; + } + return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT)); +} + +static int ulpi_write(struct usb_info *ui, unsigned val, unsigned reg) +{ + unsigned timeout = 10000; + + /* initiate write operation */ + writel(ULPI_RUN | ULPI_WRITE | + ULPI_ADDR(reg) | ULPI_DATA(val), + USB_ULPI_VIEWPORT); + + /* wait for completion */ + while ((readl(USB_ULPI_VIEWPORT) & ULPI_RUN) && (--timeout)) ; + + if (timeout == 0) { + printk(KERN_ERR "ulpi_write: timeout\n"); + return -1; + } + + return 0; +} + +static void ulpi_init(struct usb_info *ui) +{ + int *seq = ui->phy_init_seq; + + if (!seq) + return; + + while (seq[0] >= 0) { + INFO("ulpi: write 0x%02x to 0x%02x\n", seq[0], seq[1]); + ulpi_write(ui, seq[0], seq[1]); + seq += 2; + } +} + +static void init_endpoints(struct usb_info *ui) +{ + unsigned n; + + for (n = 0; n < 32; n++) { + struct msm_endpoint *ept = ui->ept + n; + + ept->ui = ui; + ept->bit = n; + ept->num = n & 15; + ept->ep.name = ep_name[n]; + ept->ep.ops = &msm72k_ep_ops; + + if (ept->bit > 15) { + /* IN endpoint */ + ept->head = ui->head + (ept->num << 1) + 1; + ept->flags = EPT_FLAG_IN; + } else { + /* OUT endpoint */ + ept->head = ui->head + (ept->num << 1); + ept->flags = 0; + } + + } +} + +static void config_ept(struct msm_endpoint *ept) +{ + unsigned cfg = CONFIG_MAX_PKT(ept->ep.maxpacket) | CONFIG_ZLT; + + if (ept->bit == 0) + /* ep0 out needs interrupt-on-setup */ + cfg |= CONFIG_IOS; + + ept->head->config = cfg; + ept->head->next = TERMINATE; +#if 0 + if (ept->ep.maxpacket) + INFO("ept #%d %s max:%d head:%p bit:%d\n", + ept->num, (ept->flags & EPT_FLAG_IN) ? "in" : "out", + ept->ep.maxpacket, ept->head, ept->bit); +#endif +} + +static void configure_endpoints(struct usb_info *ui) +{ + unsigned n; + + for (n = 0; n < 32; n++) + config_ept(ui->ept + n); +} + +struct usb_request *usb_ept_alloc_req(struct msm_endpoint *ept, + unsigned bufsize, gfp_t gfp_flags) +{ + struct usb_info *ui = ept->ui; + struct msm_request *req; + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + goto fail1; + + req->item = dma_pool_alloc(ui->pool, gfp_flags, &req->item_dma); + if (!req->item) + goto fail2; + + if (bufsize) { + req->req.buf = kmalloc(bufsize, gfp_flags); + if (!req->req.buf) + goto fail3; + req->alloced = 1; + } + + return &req->req; + +fail3: + dma_pool_free(ui->pool, req->item, req->item_dma); +fail2: + kfree(req); +fail1: + return 0; +} + +static void do_free_req(struct usb_info *ui, struct msm_request *req) +{ + if (req->alloced) + kfree(req->req.buf); + + dma_pool_free(ui->pool, req->item, req->item_dma); + kfree(req); +} + + +static void usb_ept_enable(struct msm_endpoint *ept, int yes, + unsigned char ep_type) +{ + struct usb_info *ui = ept->ui; + int in = ept->flags & EPT_FLAG_IN; + unsigned n; + + n = readl(USB_ENDPTCTRL(ept->num)); + + if (in) { + n = (n & (~CTRL_TXT_MASK)); + if (yes) { + n |= CTRL_TXE | CTRL_TXR; + } else { + n &= (~CTRL_TXE); + } + if (yes) { + switch (ep_type) { + case USB_ENDPOINT_XFER_BULK: + n |= CTRL_TXT_BULK; + break; + case USB_ENDPOINT_XFER_INT: + n |= CTRL_TXT_INT; + break; + case USB_ENDPOINT_XFER_ISOC: + n |= CTRL_TXT_ISOCH; + break; + default: + pr_err("%s: unsupported ep_type %d for %s\n", + __func__, ep_type, ept->ep.name); + break; + } + } + } else { + n = (n & (~CTRL_RXT_MASK)); + if (yes) { + n |= CTRL_RXE | CTRL_RXR; + } else { + n &= ~(CTRL_RXE); + } + if (yes) { + switch (ep_type) { + case USB_ENDPOINT_XFER_BULK: + n |= CTRL_RXT_BULK; + break; + case USB_ENDPOINT_XFER_INT: + n |= CTRL_RXT_INT; + break; + case USB_ENDPOINT_XFER_ISOC: + n |= CTRL_RXT_ISOCH; + break; + default: + pr_err("%s: unsupported ep_type %d for %s\n", + __func__, ep_type, ept->ep.name); + break; + } + } + } + writel(n, USB_ENDPTCTRL(ept->num)); + +#if 0 + INFO("ept %d %s %s\n", + ept->num, in ? "in" : "out", yes ? "enabled" : "disabled"); +#endif +} + +static void usb_ept_start(struct msm_endpoint *ept) +{ + struct usb_info *ui = ept->ui; + struct msm_request *req = ept->req; + int i, cnt; + unsigned n = 1 << ept->bit; + + BUG_ON(req->live); + + /* link the hw queue head to the request's transaction item */ + ept->head->next = req->item_dma; + ept->head->info = 0; + + /* during high throughput testing it is observed that + * ept stat bit is not set even thoguh all the data + * structures are updated properly and ept prime bit + * is set. To workaround the issue, try to check if + * ept stat bit otherwise try to re-prime the ept + */ + for (i = 0; i < 5; i++) { + writel(n, USB_ENDPTPRIME); + for (cnt = 0; cnt < 3000; cnt++) { + if (!(readl(USB_ENDPTPRIME) & n) && + (readl(USB_ENDPTSTAT) & n)) + goto DONE; + udelay(1); + } + } + + if (!(readl(USB_ENDPTSTAT) & n)) { + pr_err("Unable to prime the ept%d%s\n", + ept->num, + ept->flags & EPT_FLAG_IN ? "in" : "out"); + return; + } + +DONE: + /* mark this chain of requests as live */ + while (req) { + req->live = 1; + req = req->next; + } + +} + +int usb_ept_queue_xfer(struct msm_endpoint *ept, struct usb_request *_req) +{ + unsigned long flags; + struct msm_request *req = to_msm_request(_req); + struct msm_request *last; + struct usb_info *ui = ept->ui; + struct ept_queue_item *item = req->item; + unsigned length = req->req.length; + + if (length > 0x4000) + return -EMSGSIZE; + + spin_lock_irqsave(&ui->lock, flags); + + if (req->busy) { + req->req.status = -EBUSY; + spin_unlock_irqrestore(&ui->lock, flags); + INFO("usb_ept_queue_xfer() tried to queue busy request\n"); + return -EBUSY; + } + + if (!ui->online && (ept->num != 0)) { + req->req.status = -ESHUTDOWN; + spin_unlock_irqrestore(&ui->lock, flags); + INFO("usb_ept_queue_xfer() called while offline\n"); + return -ESHUTDOWN; + } + + req->busy = 1; + req->live = 0; + req->next = 0; + req->req.status = -EBUSY; + + req->dma = dma_map_single(NULL, req->req.buf, length, + (ept->flags & EPT_FLAG_IN) ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + + /* prepare the transaction descriptor item for the hardware */ + item->next = TERMINATE; + item->info = INFO_BYTES(length) | INFO_IOC | INFO_ACTIVE; + item->page0 = req->dma; + item->page1 = (req->dma + 0x1000) & 0xfffff000; + item->page2 = (req->dma + 0x2000) & 0xfffff000; + item->page3 = (req->dma + 0x3000) & 0xfffff000; + + /* Add the new request to the end of the queue */ + last = ept->last; + if (last) { + /* Already requests in the queue. add us to the + * end, but let the completion interrupt actually + * start things going, to avoid hw issues + */ + last->next = req; + + /* only modify the hw transaction next pointer if + * that request is not live + */ + if (!last->live) + last->item->next = req->item_dma; + } else { + /* queue was empty -- kick the hardware */ + ept->req = req; + usb_ept_start(ept); + } + ept->last = req; + + spin_unlock_irqrestore(&ui->lock, flags); + return 0; +} + +/* --- endpoint 0 handling --- */ + +static void ep0_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct msm_request *r = to_msm_request(req); + struct msm_endpoint *ept = to_msm_endpoint(ep); + struct usb_info *ui = ept->ui; + + req->complete = r->gadget_complete; + r->gadget_complete = 0; + if (req->complete) + req->complete(&ui->ep0in.ep, req); +} + +static void ep0_queue_ack_complete(struct usb_ep *ep, + struct usb_request *_req) +{ + struct msm_request *r = to_msm_request(_req); + struct msm_endpoint *ept = to_msm_endpoint(ep); + struct usb_info *ui = ept->ui; + struct usb_request *req = ui->setup_req; + + /* queue up the receive of the ACK response from the host */ + if (_req->status == 0 && _req->actual == _req->length) { + req->length = 0; + if (ui->ep0_dir == USB_DIR_IN) + usb_ept_queue_xfer(&ui->ep0out, req); + else + usb_ept_queue_xfer(&ui->ep0in, req); + _req->complete = r->gadget_complete; + r->gadget_complete = 0; + if (_req->complete) + _req->complete(&ui->ep0in.ep, _req); + } else + ep0_complete(ep, _req); +} + +static void ep0_setup_ack_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct msm_endpoint *ept = to_msm_endpoint(ep); + struct usb_info *ui = ept->ui; + unsigned int temp; + + if (!ui->test_mode) + return; + + switch (ui->test_mode) { + case J_TEST: + pr_info("usb electrical test mode: (J)\n"); + temp = readl(USB_PORTSC) & (~PORTSC_PTC); + writel(temp | PORTSC_PTC_J_STATE, USB_PORTSC); + break; + + case K_TEST: + pr_info("usb electrical test mode: (K)\n"); + temp = readl(USB_PORTSC) & (~PORTSC_PTC); + writel(temp | PORTSC_PTC_K_STATE, USB_PORTSC); + break; + + case SE0_NAK_TEST: + pr_info("usb electrical test mode: (SE0-NAK)\n"); + temp = readl(USB_PORTSC) & (~PORTSC_PTC); + writel(temp | PORTSC_PTC_SE0_NAK, USB_PORTSC); + break; + + case TST_PKT_TEST: + pr_info("usb electrical test mode: (TEST_PKT)\n"); + temp = readl(USB_PORTSC) & (~PORTSC_PTC); + writel(temp | PORTSC_PTC_TST_PKT, USB_PORTSC); + break; + } +} + +static void ep0_setup_ack(struct usb_info *ui) +{ + struct usb_request *req = ui->setup_req; + req->length = 0; + req->complete = ep0_setup_ack_complete; + usb_ept_queue_xfer(&ui->ep0in, req); +} + +static void ep0_setup_stall(struct usb_info *ui) +{ + writel((1<<16) | (1<<0), USB_ENDPTCTRL(0)); +} + +static void ep0_setup_send(struct usb_info *ui, unsigned length) +{ + struct usb_request *req = ui->setup_req; + struct msm_request *r = to_msm_request(req); + struct msm_endpoint *ept = &ui->ep0in; + + req->length = length; + req->complete = ep0_queue_ack_complete; + r->gadget_complete = 0; + usb_ept_queue_xfer(ept, req); +} + +static void handle_setup(struct usb_info *ui) +{ + struct usb_ctrlrequest ctl; + struct usb_request *req = ui->setup_req; + int ret; + + memcpy(&ctl, ui->ep0out.head->setup_data, sizeof(ctl)); + writel(EPT_RX(0), USB_ENDPTSETUPSTAT); + + if (ctl.bRequestType & USB_DIR_IN) + ui->ep0_dir = USB_DIR_IN; + else + ui->ep0_dir = USB_DIR_OUT; + + /* any pending ep0 transactions must be canceled */ + flush_endpoint(&ui->ep0out); + flush_endpoint(&ui->ep0in); + +#if 0 + INFO("setup: type=%02x req=%02x val=%04x idx=%04x len=%04x\n", + ctl.bRequestType, ctl.bRequest, ctl.wValue, + ctl.wIndex, ctl.wLength); +#endif + + if ((ctl.bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) == + (USB_DIR_IN | USB_TYPE_STANDARD)) { + if (ctl.bRequest == USB_REQ_GET_STATUS) { + if (ctl.wLength != 2) + goto stall; + switch (ctl.bRequestType & USB_RECIP_MASK) { + case USB_RECIP_ENDPOINT: + { + struct msm_endpoint *ept; + unsigned num = + ctl.wIndex & USB_ENDPOINT_NUMBER_MASK; + u16 temp = 0; + + if (num == 0) { + memset(req->buf, 0, 2); + break; + } + if (ctl.wIndex & USB_ENDPOINT_DIR_MASK) + num += 16; + ept = &ui->ep0out + num; + temp = usb_ep_get_stall(ept); + temp = temp << USB_ENDPOINT_HALT; + memcpy(req->buf, &temp, 2); + break; + } + case USB_RECIP_DEVICE: + { + u16 temp = 0; + + temp = 1 << USB_DEVICE_SELF_POWERED; + temp |= (ui->remote_wakeup << + USB_DEVICE_REMOTE_WAKEUP); + memcpy(req->buf, &temp, 2); + break; + } + case USB_RECIP_INTERFACE: + memset(req->buf, 0, 2); + break; + default: + goto stall; + } + ep0_setup_send(ui, 2); + return; + } + } + if (ctl.bRequestType == + (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)) { + if ((ctl.bRequest == USB_REQ_CLEAR_FEATURE) || + (ctl.bRequest == USB_REQ_SET_FEATURE)) { + if ((ctl.wValue == 0) && (ctl.wLength == 0)) { + unsigned num = ctl.wIndex & 0x0f; + + if (num != 0) { + struct msm_endpoint *ept; + + if (ctl.wIndex & 0x80) + num += 16; + ept = &ui->ep0out + num; + + if (ctl.bRequest == USB_REQ_SET_FEATURE) + msm72k_set_halt(&ept->ep, 1); + else + msm72k_set_halt(&ept->ep, 0); + } + goto ack; + } + } + } + if (ctl.bRequestType == (USB_DIR_OUT | USB_TYPE_STANDARD)) { + if (ctl.bRequest == USB_REQ_SET_CONFIGURATION) + ui->online = !!ctl.wValue; + else if (ctl.bRequest == USB_REQ_SET_ADDRESS) { + /* write address delayed (will take effect + ** after the next IN txn) + */ + writel((ctl.wValue << 25) | (1 << 24), USB_DEVICEADDR); + goto ack; + } else if (ctl.bRequest == USB_REQ_SET_FEATURE) { + switch (ctl.wValue) { + case USB_DEVICE_TEST_MODE: + switch (ctl.wIndex) { + case J_TEST: + case K_TEST: + case SE0_NAK_TEST: + if (!ui->test_mode) { + disable_charger = 1; + queue_delayed_work(ui->usb_wq, &ui->chg_work, 0); + } + case TST_PKT_TEST: + ui->test_mode = ctl.wIndex; + goto ack; + } + goto stall; + case USB_DEVICE_REMOTE_WAKEUP: + ui->remote_wakeup = 1; + goto ack; + } + } else if ((ctl.bRequest == USB_REQ_CLEAR_FEATURE) && + (ctl.wValue == USB_DEVICE_REMOTE_WAKEUP)) { + ui->remote_wakeup = 0; + goto ack; + } + } + + /* delegate if we get here */ + if (ui->driver) { + ret = ui->driver->setup(&ui->gadget, &ctl); + if (ret >= 0) + return; + } + +stall: + /* stall ep0 on error */ + ep0_setup_stall(ui); + return; + +ack: + ep0_setup_ack(ui); +} + +static void handle_endpoint(struct usb_info *ui, unsigned bit) +{ + struct msm_endpoint *ept = ui->ept + bit; + struct msm_request *req; + unsigned long flags; + unsigned info; + +#if 0 + INFO("handle_endpoint() %d %s req=%p(%08x)\n", + ept->num, (ept->flags & EPT_FLAG_IN) ? "in" : "out", + ept->req, ept->req ? ept->req->item_dma : 0); +#endif + + /* expire all requests that are no longer active */ + spin_lock_irqsave(&ui->lock, flags); + while ((req = ept->req)) { + info = req->item->info; + + /* if we've processed all live requests, time to + * restart the hardware on the next non-live request + */ + if (!req->live) { + usb_ept_start(ept); + break; + } + + /* if the transaction is still in-flight, stop here */ + if (info & INFO_ACTIVE) + break; + + /* advance ept queue to the next request */ + ept->req = req->next; + if (ept->req == 0) + ept->last = 0; + + dma_unmap_single(NULL, req->dma, req->req.length, + (ept->flags & EPT_FLAG_IN) ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (info & (INFO_HALTED | INFO_BUFFER_ERROR | INFO_TXN_ERROR)) { + /* XXX pass on more specific error code */ + req->req.status = -EIO; + req->req.actual = 0; + INFO("msm72k_udc: ept %d %s error. info=%08x\n", + ept->num, + (ept->flags & EPT_FLAG_IN) ? "in" : "out", + info); + } else { + req->req.status = 0; + req->req.actual = + req->req.length - ((info >> 16) & 0x7FFF); + } + req->busy = 0; + req->live = 0; + if (req->dead) + do_free_req(ui, req); + + if (req->req.complete) { + spin_unlock_irqrestore(&ui->lock, flags); + req->req.complete(&ept->ep, &req->req); + spin_lock_irqsave(&ui->lock, flags); + } + } + spin_unlock_irqrestore(&ui->lock, flags); +} + +#define FLUSH_WAIT_US 5 +#define FLUSH_TIMEOUT (2 * (USEC_PER_SEC / FLUSH_WAIT_US)) +static void flush_endpoint_hw(struct usb_info *ui, unsigned bits) +{ + uint32_t unflushed = 0; + uint32_t stat = 0; + int cnt = 0; + + /* flush endpoint, canceling transactions + ** - this can take a "large amount of time" (per databook) + ** - the flush can fail in some cases, thus we check STAT + ** and repeat if we're still operating + ** (does the fact that this doesn't use the tripwire matter?!) + */ + while (cnt < FLUSH_TIMEOUT) { + writel(bits, USB_ENDPTFLUSH); + while (((unflushed = readl(USB_ENDPTFLUSH)) & bits) && + cnt < FLUSH_TIMEOUT) { + cnt++; + udelay(FLUSH_WAIT_US); + } + + stat = readl(USB_ENDPTSTAT); + if (cnt >= FLUSH_TIMEOUT) + goto err; + if (!(stat & bits)) + goto done; + cnt++; + udelay(FLUSH_WAIT_US); + } + +err: + pr_warning("%s: Could not complete flush! NOT GOOD! " + "stat: %x unflushed: %x bits: %x\n", __func__, + stat, unflushed, bits); +done: + return; +} + +static void flush_endpoint_sw(struct msm_endpoint *ept) +{ + struct usb_info *ui = ept->ui; + struct msm_request *req; + unsigned long flags; + + /* inactive endpoints have nothing to do here */ + if (ept->ep.maxpacket == 0) + return; + + /* put the queue head in a sane state */ + ept->head->info = 0; + ept->head->next = TERMINATE; + + /* cancel any pending requests */ + spin_lock_irqsave(&ui->lock, flags); + req = ept->req; + ept->req = 0; + ept->last = 0; + while (req != 0) { + req->busy = 0; + req->live = 0; + req->req.status = -ECONNRESET; + req->req.actual = 0; + if (req->req.complete) { + spin_unlock_irqrestore(&ui->lock, flags); + req->req.complete(&ept->ep, &req->req); + spin_lock_irqsave(&ui->lock, flags); + } + if (req->dead) + do_free_req(ui, req); + req = req->next; + } + spin_unlock_irqrestore(&ui->lock, flags); +} + +static void flush_endpoint(struct msm_endpoint *ept) +{ + flush_endpoint_hw(ept->ui, (1 << ept->bit)); + flush_endpoint_sw(ept); +} + +static void flush_all_endpoints(struct usb_info *ui) +{ + unsigned n; + + flush_endpoint_hw(ui, 0xffffffff); + + for (n = 0; n < 32; n++) + flush_endpoint_sw(ui->ept + n); +} + + +static irqreturn_t usb_interrupt(int irq, void *data) +{ + struct usb_info *ui = data; + unsigned n; + + n = readl(USB_USBSTS); + writel(n, USB_USBSTS); + + /* somehow we got an IRQ while in the reset sequence: ignore it */ + if (ui->running == 0) + return IRQ_HANDLED; + + if (n & STS_PCI) { + switch (readl(USB_PORTSC) & PORTSC_PSPD_MASK) { + case PORTSC_PSPD_FS: + INFO("usb: portchange USB_SPEED_FULL\n"); + ui->gadget.speed = USB_SPEED_FULL; + break; + case PORTSC_PSPD_LS: + INFO("usb: portchange USB_SPEED_LOW\n"); + ui->gadget.speed = USB_SPEED_LOW; + break; + case PORTSC_PSPD_HS: + INFO("usb: portchange USB_SPEED_HIGH\n"); + ui->gadget.speed = USB_SPEED_HIGH; + break; + } + } + + if (n & STS_URI) { + INFO("usb: reset\n"); + + writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT); + writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE); + writel(0xffffffff, USB_ENDPTFLUSH); + writel(0, USB_ENDPTCTRL(1)); + + if (ui->online != 0) { + /* marking us offline will cause ept queue attempts + ** to fail + */ + ui->online = 0; + + flush_all_endpoints(ui); + + /* XXX: we can't seem to detect going offline, + * XXX: so deconfigure on reset for the time being + */ + if (ui->driver) { + printk(KERN_INFO "usb: notify offline\n"); + ui->driver->disconnect(&ui->gadget); + } + } + if (ui->connect_type != CONNECT_TYPE_USB) { + ui->connect_type = CONNECT_TYPE_USB; + queue_work(ui->usb_wq, &ui->notifier_work); + ui->ac_detect_count = 0; + del_timer_sync(&ui->ac_detect_timer); + } + } + + if (n & STS_SLI) + INFO("usb: suspend\n"); + + if (n & STS_UI) { + n = readl(USB_ENDPTSETUPSTAT); + if (n & EPT_RX(0)) + handle_setup(ui); + + n = readl(USB_ENDPTCOMPLETE); + writel(n, USB_ENDPTCOMPLETE); + while (n) { + unsigned bit = __ffs(n); + handle_endpoint(ui, bit); + n = n & (~(1 << bit)); + } + } + return IRQ_HANDLED; +} + +int usb_get_connect_type(void) +{ + if (!the_usb_info) + return 0; + return the_usb_info->connect_type; +} +EXPORT_SYMBOL(usb_get_connect_type); + +void msm_hsusb_request_reset(void) +{ + struct usb_info *ui = the_usb_info; + unsigned long flags; + if (!ui) + return; + spin_lock_irqsave(&ui->lock, flags); + ui->flags |= USB_FLAG_RESET; + queue_work(ui->usb_wq, &ui->work); + spin_unlock_irqrestore(&ui->lock, flags); +} + +static ssize_t show_usb_cable_connect(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned length; + if (!the_usb_info) + return 0; + length = sprintf(buf, "%d\n", + (the_usb_info->connect_type == CONNECT_TYPE_USB)?1:0); + return length; +} + +static DEVICE_ATTR(usb_cable_connect, 0444, show_usb_cable_connect, NULL); + +#if 0 +static ssize_t show_usb_function_switch(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return android_show_function(buf); +} + +static ssize_t store_usb_function_switch(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned u; + ssize_t ret; + + u = simple_strtoul(buf, NULL, 10); + ret = android_switch_function(u); + + if (ret == 0) + return count; + else + return 0; +} + +static DEVICE_ATTR(usb_function_switch, 0666, + show_usb_function_switch, store_usb_function_switch); +#endif + +static ssize_t show_usb_serial_number(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned length; + struct msm_hsusb_platform_data *pdata = dev->platform_data; + + length = sprintf(buf, "%s", pdata->serial_number); + return length; +} + +static ssize_t store_usb_serial_number(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct msm_hsusb_platform_data *pdata = dev->platform_data; + + if (buf[0] == '0' || buf[0] == '1') { + memset(mfg_df_serialno, 0x0, sizeof(mfg_df_serialno)); + if (buf[0] == '0') { + strncpy(mfg_df_serialno, "000000000000", + strlen("000000000000")); + use_mfg_serialno = 1; + android_set_serialno(mfg_df_serialno); + } else { + strncpy(mfg_df_serialno, pdata->serial_number, + strlen(pdata->serial_number)); + use_mfg_serialno = 0; + android_set_serialno(pdata->serial_number); + } + /* reset_device */ + msm_hsusb_request_reset(); + } + + return count; +} + +static DEVICE_ATTR(usb_serial_number, 0644, + show_usb_serial_number, store_usb_serial_number); + +static ssize_t show_dummy_usb_serial_number(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned length; + struct msm_hsusb_platform_data *pdata = dev->platform_data; + + if (use_mfg_serialno) + length = sprintf(buf, "%s", mfg_df_serialno); /* dummy */ + else + length = sprintf(buf, "%s", pdata->serial_number); /* Real */ + return length; +} + +static ssize_t store_dummy_usb_serial_number(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int data_buff_size = (sizeof(mfg_df_serialno) > strlen(buf))? + strlen(buf):sizeof(mfg_df_serialno); + int loop_i; + + /* avoid overflow, mfg_df_serialno[16] always is 0x0 */ + if (data_buff_size == 16) + data_buff_size--; + + for (loop_i = 0; loop_i < data_buff_size; loop_i++) { + if (buf[loop_i] >= 0x30 && buf[loop_i] <= 0x39) /* 0-9 */ + continue; + else if (buf[loop_i] >= 0x41 && buf[loop_i] <= 0x5A) /* A-Z */ + continue; + if (buf[loop_i] == 0x0A) /* Line Feed */ + continue; + else { + printk(KERN_WARNING "%s(): get invaild char (0x%2.2X)\n", + __func__, buf[loop_i]); + return -EINVAL; + } + } + + use_mfg_serialno = 1; + memset(mfg_df_serialno, 0x0, sizeof(mfg_df_serialno)); + strncpy(mfg_df_serialno, buf, data_buff_size); + android_set_serialno(mfg_df_serialno); + /*device_reset */ + msm_hsusb_request_reset(); + + return count; +} + +static DEVICE_ATTR(dummy_usb_serial_number, 0644, + show_dummy_usb_serial_number, store_dummy_usb_serial_number); + +static ssize_t show_USB_ID_status(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct usb_info *ui = the_usb_info; + int value = 1; + unsigned length; + + if (!ui) + return 0; + if (ui->usb_id_pin_gpio != 0) { + value = gpio_get_value(ui->usb_id_pin_gpio); + printk(KERN_INFO "usb: id pin status %d\n", value); + } + length = sprintf(buf, "%d", value); + return length; +} + +static DEVICE_ATTR(USB_ID_status, 0444, + show_USB_ID_status, NULL); + +static ssize_t show_usb_car_kit_enable(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct usb_info *ui = the_usb_info; + int value = 1; + unsigned length; + + if (!ui) + return 0; + if (ui->accessory_detect == 0) { + value = 0; + } + printk(KERN_INFO "usb: USB_car_kit_enable %d\n", ui->accessory_detect); + length = sprintf(buf, "%d", value); + return length; +} + +static DEVICE_ATTR(usb_car_kit_enable, 0444, + show_usb_car_kit_enable, NULL);/*for kar kit AP check if car kit enable*/ + +static ssize_t show_usb_phy_setting(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_info *ui = the_usb_info; + unsigned length = 0; + int i; + + for (i = 0; i <= 0x14; i++) + length += sprintf(buf + length, "0x%x = 0x%x\n", i, ulpi_read(ui, i)); + + for (i = 0x30; i <= 0x37; i++) + length += sprintf(buf + length, "0x%x = 0x%x\n", i, ulpi_read(ui, i)); + + return length; +} + +static ssize_t store_usb_phy_setting(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_info *ui = the_usb_info; + char *token[10]; + unsigned reg; + unsigned value; + int i; + + printk(KERN_INFO "%s\n", buf); + for (i = 0; i < 2; i++) + token[i] = strsep((char **)&buf, " "); + + reg = simple_strtoul(token[0], NULL, 16); + value = simple_strtoul(token[1], NULL, 16); + printk(KERN_INFO "Set 0x%x = 0x%x\n", reg, value); + + ulpi_write(ui, value, reg); + + return 0; +} + +static DEVICE_ATTR(usb_phy_setting, 0666, + show_usb_phy_setting, store_usb_phy_setting); + +#ifdef CONFIG_USB_ACCESSORY_DETECT +static ssize_t show_mfg_carkit_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned length; + struct usb_info *ui = the_usb_info; + + length = sprintf(buf, "%d", ui->mfg_usb_carkit_enable); + printk(KERN_INFO "%s: %d\n", __func__, + ui->mfg_usb_carkit_enable); + return length; + +} + +static ssize_t store_mfg_carkit_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_info *ui = the_usb_info; + unsigned char uc; + + if (buf[0] != '0' && buf[0] != '1') { + printk(KERN_ERR "Can't enable/disable carkit\n"); + return -EINVAL; + } + uc = buf[0] - '0'; + printk(KERN_INFO "%s: %d\n", __func__, uc); + ui->mfg_usb_carkit_enable = uc; + if (uc == 1 && ui->accessory_type == 1 && + board_mfg_mode() == 1) { + switch_set_state(&dock_switch, DOCK_STATE_CAR); + printk(KERN_INFO "carkit: set state %d\n", DOCK_STATE_CAR); + } + return count; +} + +static DEVICE_ATTR(usb_mfg_carkit_enable, 0644, + show_mfg_carkit_enable, store_mfg_carkit_enable); +#endif + +#if defined (CONFIG_DOCK_ACCESSORY_DETECT) || defined(CONFIG_USB_ACCESSORY_DETECT) +static ssize_t dock_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_info *ui = the_usb_info; + if (ui->accessory_type == 1) + return sprintf(buf, "online\n"); + else if (ui->accessory_type == 3) /*desk dock*/ + return sprintf(buf, "online\n"); + else + return sprintf(buf, "offline\n"); +} +static DEVICE_ATTR(status, S_IRUGO | S_IWUSR, dock_status_show, NULL); +#endif + +static void usb_prepare(struct usb_info *ui) +{ + int ret; + spin_lock_init(&ui->lock); + + memset(ui->buf, 0, 4096); + ui->head = (void *) (ui->buf + 0); + + /* only important for reset/reinit */ + memset(ui->ept, 0, sizeof(ui->ept)); + ui->next_item = 0; + ui->next_ifc_num = 0; + + init_endpoints(ui); + + ui->ep0in.ep.maxpacket = 64; + ui->ep0out.ep.maxpacket = 64; + + ui->setup_req = + usb_ept_alloc_req(&ui->ep0in, SETUP_BUF_SIZE, GFP_KERNEL); + + ui->usb_wq = create_singlethread_workqueue("msm_hsusb"); + if (ui->usb_wq == 0) { + printk(KERN_ERR "usb: fail to create workqueue\n"); + return; + } + INIT_WORK(&ui->work, usb_do_work); +#ifdef CONFIG_USB_ACCESSORY_DETECT + INIT_WORK(&ui->detect_work, accessory_detect_work); +#endif +#ifdef CONFIG_DOCK_ACCESSORY_DETECT + if (ui->dock_detect) { + INIT_WORK(&ui->dock_work, dock_detect_work); + dock_detect_init(ui); + } +#endif + + INIT_WORK(&ui->notifier_work, send_usb_connect_notify); + INIT_DELAYED_WORK(&ui->chg_work, check_charger); + + ret = device_create_file(&ui->pdev->dev, + &dev_attr_usb_cable_connect); + if (ret != 0) + printk(KERN_WARNING "dev_attr_usb_cable_connect failed\n"); + +#if 0 + ret = device_create_file(&ui->pdev->dev, + &dev_attr_usb_function_switch); + if (ret != 0) + printk(KERN_WARNING "dev_attr_usb_function_switch failed\n"); +#endif + + ret = device_create_file(&ui->pdev->dev, + &dev_attr_usb_serial_number); + if (ret != 0) + printk(KERN_WARNING "dev_attr_usb_serial_number failed\n"); + + ret = device_create_file(&ui->pdev->dev, + &dev_attr_dummy_usb_serial_number); + if (ret != 0) + printk(KERN_WARNING "dev_attr_dummy_usb_serial_number failed\n"); + + ret = device_create_file(&ui->pdev->dev, + &dev_attr_USB_ID_status); + if (ret != 0) + printk(KERN_WARNING "dev_attr_USB_ID_status failed\n"); + + ret = device_create_file(&ui->pdev->dev, + &dev_attr_usb_phy_setting); + if (ret != 0) + printk(KERN_WARNING "dev_attr_usb_phy_setting failed\n"); + +#ifdef CONFIG_USB_ACCESSORY_DETECT + ret = device_create_file(&ui->pdev->dev, + &dev_attr_usb_mfg_carkit_enable); + if (ret != 0) + printk(KERN_WARNING "dev_attr_usb_mfg_carkit_enable failed\n"); +#endif + + ret = device_create_file(&ui->pdev->dev, + &dev_attr_usb_car_kit_enable);/*for kar kit AP check if car kit enable*/ + if (ret != 0) + printk(KERN_WARNING "dev_attr_usb_car_kit_enable failed\n"); + +} + +static int usb_wakeup_phy(struct usb_info *ui) +{ + int i; + + /*writel(readl(USB_USBCMD) & ~ULPI_STP_CTRL, USB_USBCMD);*/ + + /* some circuits automatically clear PHCD bit */ + for (i = 0; i < 5 && (readl(USB_PORTSC) & PORTSC_PHCD); i++) { + writel(readl(USB_PORTSC) & ~PORTSC_PHCD, USB_PORTSC); + mdelay(1); + } + + if ((readl(USB_PORTSC) & PORTSC_PHCD)) { + pr_err("%s: cannot clear phcd bit\n", __func__); + return -1; + } + + return 0; +} + +static void usb_suspend_phy(struct usb_info *ui) +{ + printk(KERN_INFO "%s\n", __func__); +#ifdef CONFIG_ARCH_MSM7X00A + /* disable unused interrupt */ + ulpi_write(ui, 0x01, 0x0d); + ulpi_write(ui, 0x01, 0x10); + + /* disable interface protect circuit to drop current consumption */ + ulpi_write(ui, (1 << 7), 0x08); + /* clear the SuspendM bit -> suspend the PHY */ + ulpi_write(ui, 1 << 6, 0x06); +#else +#ifdef CONFIG_ARCH_MSM7X30 + ulpi_write(ui, 0x0, 0x0D); + ulpi_write(ui, 0x0, 0x10); +#endif + /* clear VBusValid and SessionEnd rising interrupts */ + ulpi_write(ui, (1 << 1) | (1 << 3), 0x0f); + /* clear VBusValid and SessionEnd falling interrupts */ + ulpi_write(ui, (1 << 1) | (1 << 3), 0x12); + ulpi_read(ui, 0x14); /* clear PHY interrupt latch register*/ + + ulpi_write(ui, 0x08, 0x09);/* turn off PLL on integrated phy */ + + /* set phy to be in lpm */ + writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); + mdelay(1); + if (!(readl(USB_PORTSC) & PORTSC_PHCD)) + printk(KERN_INFO "%s: unable to set lpm\n", __func__); +#endif +} + +static void usb_reset(struct usb_info *ui) +{ + unsigned long flags; + printk(KERN_INFO "hsusb: reset controller\n"); + + spin_lock_irqsave(&ui->lock, flags); + ui->running = 0; + spin_unlock_irqrestore(&ui->lock, flags); + + /* disable usb interrupts */ + writel(0, USB_USBINTR); + + /* wait for a while after enable usb clk*/ + msleep(5); + + /* To prevent phantom packets being received by the usb core on + * some devices, put the controller into reset prior to + * resetting the phy. */ + writel(2, USB_USBCMD); + msleep(10); + +#if 0 + /* we should flush and shutdown cleanly if already running */ + writel(0xffffffff, USB_ENDPTFLUSH); + msleep(2); +#endif + + if (ui->phy_reset) + ui->phy_reset(); + + msleep(100); + + /* RESET */ + writel(2, USB_USBCMD); + msleep(10); + +#ifdef CONFIG_ARCH_MSM7X00A + /* INCR4 BURST mode */ + writel(0x01, USB_SBUSCFG); +#else + /* bursts of unspecified length. */ + writel(0, USB_AHBBURST); + /* Use the AHB transactor */ + writel(0, USB_AHBMODE); +#endif + + /* select DEVICE mode */ + writel(0x12, USB_USBMODE); + msleep(1); + + /* select ULPI phy */ + writel(0x80000000, USB_PORTSC); + + ulpi_init(ui); + + writel(ui->dma, USB_ENDPOINTLISTADDR); + + configure_endpoints(ui); + + /* marking us offline will cause ept queue attempts to fail */ + ui->online = 0; + + /* terminate any pending transactions */ + flush_all_endpoints(ui); + + if (ui->driver) { + printk(KERN_INFO "usb: notify offline\n"); + ui->driver->disconnect(&ui->gadget); + } + + /* enable interrupts */ + writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR); + + /* go to RUN mode (D+ pullup enable) */ + msm72k_pullup(&ui->gadget, 1); + + spin_lock_irqsave(&ui->lock, flags); + ui->running = 1; + spin_unlock_irqrestore(&ui->lock, flags); +} + +static void usb_start(struct usb_info *ui) +{ + unsigned long flags; + + spin_lock_irqsave(&ui->lock, flags); + ui->flags |= USB_FLAG_START; +/*if msm_hsusb_set_vbus_state set 1, but usb did not init, the ui =NULL, */ +/*it would cause reboot with usb, it did not swith to USB and ADB fail*/ +/*So when USB start, check again*/ + if (vbus) { + ui->flags |= USB_FLAG_VBUS_ONLINE; + } else { + ui->flags |= USB_FLAG_VBUS_OFFLINE; + } + /* online->switch to USB, offline->switch to uart */ + if (ui->usb_uart_switch) + ui->usb_uart_switch(!vbus); + + queue_work(ui->usb_wq, &ui->work); + spin_unlock_irqrestore(&ui->lock, flags); +} + +static int usb_free(struct usb_info *ui, int ret) +{ + INFO("usb_free(%d)\n", ret); + + if (ui->irq) + free_irq(ui->irq, 0); + if (ui->pool) + dma_pool_destroy(ui->pool); + if (ui->dma) + dma_free_coherent(&ui->pdev->dev, 4096, ui->buf, ui->dma); + if (ui->addr) + iounmap(ui->addr); + if (ui->clk) + clk_put(ui->clk); + if (ui->pclk) + clk_put(ui->pclk); + if (ui->otgclk) + clk_put(ui->otgclk); + if (ui->coreclk) + clk_put(ui->coreclk); + if (ui->ebi1clk) + clk_put(ui->ebi1clk); + kfree(ui); + return ret; +} + +static void usb_do_work_check_vbus(struct usb_info *ui) +{ + unsigned long iflags; + + spin_lock_irqsave(&ui->lock, iflags); +#if defined(CONFIG_USB_BYPASS_VBUS_NOTIFY) + ui->flags |= USB_FLAG_VBUS_ONLINE; + pr_info("usb: fake vbus\n"); +#else + if (vbus) + ui->flags |= USB_FLAG_VBUS_ONLINE; + else + ui->flags |= USB_FLAG_VBUS_OFFLINE; +#endif + spin_unlock_irqrestore(&ui->lock, iflags); +} + +static void usb_lpm_enter(struct usb_info *ui) +{ + unsigned long iflags; + if (ui->in_lpm) + return; + printk(KERN_INFO "usb: lpm enter\n"); + spin_lock_irqsave(&ui->lock, iflags); + usb_suspend_phy(ui); + if (ui->otgclk) + clk_disable(ui->otgclk); + clk_disable(ui->pclk); + clk_disable(ui->clk); + if (ui->coreclk) + clk_disable(ui->coreclk); + clk_set_rate(ui->ebi1clk, 0); + ui->in_lpm = 1; + spin_unlock_irqrestore(&ui->lock, iflags); + + if (board_mfg_mode() == 1) {/*for MFG adb unstable in FROYO ROM*/ + printk(KERN_INFO "usb: idle_wake_unlock and perf unlock\n"); + wake_unlock(&vbus_idle_wake_lock); + if (is_perf_lock_active(&usb_perf_lock)) + perf_unlock(&usb_perf_lock); + } +} + +static void usb_lpm_exit(struct usb_info *ui) +{ + if (!ui->in_lpm) + return; + printk(KERN_INFO "usb: lpm exit\n"); + clk_set_rate(ui->ebi1clk, acpuclk_get_max_axi_rate()); + udelay(10); + if (ui->coreclk) + clk_enable(ui->coreclk); + clk_enable(ui->clk); + clk_enable(ui->pclk); + if (ui->otgclk) + clk_enable(ui->otgclk); + usb_wakeup_phy(ui); + ui->in_lpm = 0; + + if (board_mfg_mode() == 1) {/*for MFG adb unstable in FROYO ROM*/ + printk(KERN_INFO "usb: idle_wake_lock and perf lock\n"); + wake_lock(&vbus_idle_wake_lock); + if (!is_perf_lock_active(&usb_perf_lock)) + perf_lock(&usb_perf_lock); + } +} + +#ifdef CONFIG_DOCK_ACCESSORY_DETECT +static irqreturn_t dock_interrupt(int irq, void *data) +{ + struct usb_info *ui = data; + disable_irq_nosync(ui->dockpin_irq); + queue_work(ui->usb_wq, &ui->dock_work); + return IRQ_HANDLED; +} +static void dock_detect_work(struct work_struct *w) +{ + struct usb_info *ui = container_of(w, struct usb_info, dock_work); + int value; + + value = gpio_get_value(ui->dock_pin_gpio); + + if (value == 0) { + set_irq_type(ui->dockpin_irq, IRQF_TRIGGER_HIGH); + switch_set_state(&dock_switch, DOCK_STATE_DESK); + ui->accessory_type = 3; + printk(KERN_INFO "usb:dock: set state %d\n", DOCK_STATE_DESK); + } else { + set_irq_type(ui->dockpin_irq, IRQF_TRIGGER_LOW); + switch_set_state(&dock_switch, DOCK_STATE_UNDOCKED); + ui->accessory_type = 0; + printk(KERN_INFO "usb:dock: set state %d\n", DOCK_STATE_UNDOCKED); + } + enable_irq(ui->dockpin_irq); + + +} +static void dock_detect_init(struct usb_info *ui) +{ + int ret; + + if (ui->dock_pin_gpio == 0) + return; + if (ui->dockpin_irq == 0) + ui->dockpin_irq = gpio_to_irq(ui->dock_pin_gpio); + + ret = request_irq(ui->dockpin_irq, dock_interrupt, + IRQF_TRIGGER_LOW, + "dock_irq", ui); + if (ret < 0) { + printk(KERN_ERR "%s: request_irq failed\n", __func__); + return; + } + printk(KERN_INFO "%s: dock irq %d\n", __func__, + ui->dockpin_irq); + ret = set_irq_wake(ui->dockpin_irq, 1); + if (ret < 0) { + printk(KERN_ERR "%s: set_irq_wake failed\n", __func__); + goto err; + } + + if (switch_dev_register(&dock_switch) < 0) { + printk(KERN_ERR "usb: fail to register dock switch!\n"); + goto err; + } + + ret = device_create_file(dock_switch.dev, &dev_attr_status); + if (ret != 0) + printk(KERN_WARNING "dev_attr_status failed\n"); + + return; + +err: + free_irq(ui->dockpin_irq, 0); +} +#endif + + +#ifdef CONFIG_USB_ACCESSORY_DETECT +static void carkit_detect(struct usb_info *ui) +{ + unsigned n; + int value; + unsigned in_lpm; + + msleep(100); + value = gpio_get_value(ui->usb_id_pin_gpio); + printk(KERN_INFO "usb: usb ID pin = %d\n", value); + in_lpm = ui->in_lpm; + if (value == 0) { + if (in_lpm) + usb_lpm_exit(ui); + + n = readl(USB_OTGSC); + /* ID pull-up register */ + writel(n | OTGSC_IDPU, USB_OTGSC); + + msleep(100); + n = readl(USB_OTGSC); + + if (n & OTGSC_ID) { + printk(KERN_INFO "usb: carkit inserted\n"); + if ((board_mfg_mode() == 0) || (board_mfg_mode() == 1 && + ui->mfg_usb_carkit_enable == 1)) { + switch_set_state(&dock_switch, DOCK_STATE_CAR); + printk(KERN_INFO "carkit: set state %d\n", DOCK_STATE_CAR); + } + ui->accessory_type = 1; + } else + ui->accessory_type = 0; + if (in_lpm) + usb_lpm_enter(ui); + } else { + if (ui->accessory_type == 1) + printk(KERN_INFO "usb: carkit removed\n"); + switch_set_state(&dock_switch, DOCK_STATE_UNDOCKED); + printk(KERN_INFO "carkit: set state %d\n", DOCK_STATE_UNDOCKED); + ui->accessory_type = 0; + } +} + +#ifdef CONFIG_USB_ACCESSORY_DETECT_BY_ADC +static void accessory_detect_by_adc(struct usb_info *ui) +{ + int value; + msleep(100); + value = gpio_get_value(ui->usb_id_pin_gpio); + printk(KERN_INFO "usb: usb ID pin = %d\n", value); + if (value == 0) { + uint32_t adc_value = 0xffffffff; + htc_get_usb_accessory_adc_level(&adc_value); + printk(KERN_INFO "usb: accessory adc = 0x%x\n", adc_value); + if (adc_value >= 0x2112 && adc_value <= 0x3D53) { + printk(KERN_INFO "usb: headset inserted\n"); + ui->accessory_type = 2; + headset_ext_detect(USB_AUDIO_OUT); + } else if (adc_value >= 0x88A && adc_value <= 0x1E38) { + printk(KERN_INFO "usb: carkit inserted\n"); + ui->accessory_type = 1; + if ((board_mfg_mode() == 0) || (board_mfg_mode() == 1 && + ui->mfg_usb_carkit_enable == 1)) { + switch_set_state(&dock_switch, DOCK_STATE_CAR); + printk(KERN_INFO "carkit: set state %d\n", DOCK_STATE_CAR); + } + } else + ui->accessory_type = 0; + } else { + if (ui->accessory_type == 2) { + printk(KERN_INFO "usb: headset removed\n"); + headset_ext_detect(USB_NO_HEADSET); + } else if (ui->accessory_type == 1) { + printk(KERN_INFO "usb: carkit removed\n"); + switch_set_state(&dock_switch, DOCK_STATE_UNDOCKED); + } + ui->accessory_type = 0; + } + +} +#endif + +static void accessory_detect_work(struct work_struct *w) +{ + struct usb_info *ui = container_of(w, struct usb_info, detect_work); + int value; + + if (!ui->accessory_detect) + return; + + if (ui->accessory_detect == 1) + carkit_detect(ui); +#ifdef CONFIG_USB_ACCESSORY_DETECT_BY_ADC + else if (ui->accessory_detect == 2) + accessory_detect_by_adc(ui); +#endif + + value = gpio_get_value(ui->usb_id_pin_gpio); + if (value == 0) + set_irq_type(ui->idpin_irq, IRQF_TRIGGER_HIGH); + else + set_irq_type(ui->idpin_irq, IRQF_TRIGGER_LOW); + enable_irq(ui->idpin_irq); +} + +static irqreturn_t usbid_interrupt(int irq, void *data) +{ + struct usb_info *ui = data; + + disable_irq_nosync(ui->idpin_irq); + printk(KERN_INFO "usb: id interrupt\n"); + queue_work(ui->usb_wq, &ui->detect_work); + return IRQ_HANDLED; +} + +static void accessory_detect_init(struct usb_info *ui) +{ + int ret; + printk(KERN_INFO "%s: id pin %d\n", __func__, + ui->usb_id_pin_gpio); + + if (ui->usb_id_pin_gpio == 0) + return; + if (ui->idpin_irq == 0) + ui->idpin_irq = gpio_to_irq(ui->usb_id_pin_gpio); + + ret = request_irq(ui->idpin_irq, usbid_interrupt, + IRQF_TRIGGER_LOW, + "car_kit_irq", ui); + if (ret < 0) { + printk(KERN_ERR "%s: request_irq failed\n", __func__); + return; + } + + ret = set_irq_wake(ui->idpin_irq, 1); + if (ret < 0) { + printk(KERN_ERR "%s: set_irq_wake failed\n", __func__); + goto err; + } + + if (switch_dev_register(&dock_switch) < 0) { + printk(KERN_ERR "usb: fail to register dock switch!\n"); + goto err; + } + + ret = device_create_file(dock_switch.dev, &dev_attr_status); + if (ret != 0) + printk(KERN_WARNING "dev_attr_status failed\n"); + + return; +err: + free_irq(ui->idpin_irq, 0); +} + +#endif + +#define DELAY_FOR_CHECK_CHG msecs_to_jiffies(300) + +static void charger_detect_by_uart(struct usb_info *ui) +{ + int is_china_ac; + + /*UART*/ + if (ui->usb_uart_switch) + ui->usb_uart_switch(1); + + is_china_ac = ui->china_ac_detect(); + + if (is_china_ac) { + ui->connect_type = CONNECT_TYPE_AC; + queue_work(ui->usb_wq, &ui->notifier_work); + usb_lpm_enter(ui); + printk(KERN_INFO "usb: AC charger\n"); + } else { + ui->connect_type = CONNECT_TYPE_UNKNOWN; + queue_delayed_work(ui->usb_wq, &ui->chg_work, + DELAY_FOR_CHECK_CHG); + printk(KERN_INFO "usb: not AC charger\n"); + + /*set uart to gpo*/ + if (ui->serial_debug_gpios) + ui->serial_debug_gpios(0); + /*turn on USB HUB*/ + if (ui->usb_hub_enable) + ui->usb_hub_enable(1); + + /*USB*/ + if (ui->usb_uart_switch) + ui->usb_uart_switch(0); + + usb_lpm_exit(ui); + usb_reset(ui); + } +} + +static void charger_detect(struct usb_info *ui) +{ + if (!vbus) + return; + msleep(10); + /* detect shorted D+/D-, indicating AC power */ + if ((readl(USB_PORTSC) & PORTSC_LS) != PORTSC_LS) { + printk(KERN_INFO "usb: not AC charger\n"); + ui->connect_type = CONNECT_TYPE_UNKNOWN; + queue_delayed_work(ui->usb_wq, &ui->chg_work, + DELAY_FOR_CHECK_CHG); + mod_timer(&ui->ac_detect_timer, jiffies + (3 * HZ)); + } else { + printk(KERN_INFO "usb: AC charger\n"); + ui->connect_type = CONNECT_TYPE_AC; + queue_work(ui->usb_wq, &ui->notifier_work); + writel(0x00080000, USB_USBCMD); + msleep(10); + usb_lpm_enter(ui); + } +} + +static void check_charger(struct work_struct *w) +{ + struct usb_info *ui = container_of(w, struct usb_info, chg_work.work); + if (disable_charger) { + printk(KERN_INFO "usb: disable charger\n"); + if (ui->disable_usb_charger) + ui->disable_usb_charger(); + disable_charger = 0; + return; + } + /* unknown charger */ + if (vbus && ui->connect_type == CONNECT_TYPE_UNKNOWN) + queue_work(ui->usb_wq, &ui->notifier_work); +} + +static void usb_do_work(struct work_struct *w) +{ + struct usb_info *ui = container_of(w, struct usb_info, work); + unsigned long iflags; + unsigned flags, _vbus; + + + for (;;) { + spin_lock_irqsave(&ui->lock, iflags); + flags = ui->flags; + ui->flags = 0; + _vbus = vbus; + spin_unlock_irqrestore(&ui->lock, iflags); + + /* give up if we have nothing to do */ + if (flags == 0) + break; + switch (ui->state) { + case USB_STATE_IDLE: + if (flags & USB_FLAG_START) { + pr_info("hsusb: IDLE -> ONLINE\n"); + + if (ui->china_ac_detect) + charger_detect_by_uart(ui); + else { + usb_lpm_exit(ui); + usb_reset(ui); + + charger_detect(ui); + } + + ui->state = USB_STATE_ONLINE; +#ifdef CONFIG_USB_ACCESSORY_DETECT + if (ui->accessory_detect) + accessory_detect_init(ui); +#endif + usb_do_work_check_vbus(ui); + } + break; + case USB_STATE_ONLINE: + /* If at any point when we were online, we received + * the signal to go offline, we must honor it + */ + if (flags & USB_FLAG_VBUS_OFFLINE) { + pr_info("hsusb: ONLINE -> OFFLINE\n"); + + /* synchronize with irq context */ + spin_lock_irqsave(&ui->lock, iflags); + ui->running = 0; + ui->online = 0; + writel(0x00080000, USB_USBCMD); + spin_unlock_irqrestore(&ui->lock, iflags); + + if (ui->connect_type != CONNECT_TYPE_NONE) { + ui->connect_type = CONNECT_TYPE_NONE; + queue_work(ui->usb_wq, &ui->notifier_work); + } + if (ui->in_lpm) { + usb_lpm_exit(ui); + msleep(5); + } + + /* terminate any transactions, etc */ + flush_all_endpoints(ui); + + if (ui->driver) { + printk(KERN_INFO "usb: notify offline\n"); + ui->driver->disconnect(&ui->gadget); + } + + if (ui->phy_reset) + ui->phy_reset(); + + /* power down phy, clock down usb */ + usb_lpm_enter(ui); + ui->ac_detect_count = 0; + del_timer_sync(&ui->ac_detect_timer); + + ui->state = USB_STATE_OFFLINE; + usb_do_work_check_vbus(ui); + break; + } + if (flags & USB_FLAG_RESET) { + pr_info("hsusb: ONLINE -> RESET\n"); + if (ui->connect_type == CONNECT_TYPE_AC) { + pr_info("hsusb: RESET -> ONLINE\n"); + break; + } + spin_lock_irqsave(&ui->lock, iflags); + ui->online = 0; + msm72k_pullup(&ui->gadget, 0); + spin_unlock_irqrestore(&ui->lock, iflags); + usb_reset(ui); + pr_info("hsusb: RESET -> ONLINE\n"); + break; + } + break; + case USB_STATE_OFFLINE: + /* If we were signaled to go online and vbus is still + * present when we received the signal, go online. + */ + if ((flags & USB_FLAG_VBUS_ONLINE) && _vbus) { + pr_info("hsusb: OFFLINE -> ONLINE\n"); + + if (ui->china_ac_detect) + charger_detect_by_uart(ui); + else { + usb_lpm_exit(ui); + usb_reset(ui); + charger_detect(ui); + } + + ui->state = USB_STATE_ONLINE; + usb_do_work_check_vbus(ui); + } + break; + } + } +} + +/* FIXME - the callers of this function should use a gadget API instead. + * This is called from htc_battery.c and board-halibut.c + * WARNING - this can get called before this driver is initialized. + */ +void msm_hsusb_set_vbus_state(int online) +{ + unsigned long flags = 0; + struct usb_info *ui = the_usb_info; + printk(KERN_INFO "%s: %d\n", __func__, online); + + if (ui) + spin_lock_irqsave(&ui->lock, flags); + if (vbus != online) { + vbus = online; + if (ui) { + if (online) { + ui->flags |= USB_FLAG_VBUS_ONLINE; + } else { + ui->flags |= USB_FLAG_VBUS_OFFLINE; + } + + if (online) { + /*USB*/ + if (ui->usb_uart_switch) + ui->usb_uart_switch(0); + } else { + /*turn off USB HUB*/ + if (ui->usb_hub_enable) + ui->usb_hub_enable(0); + + /*UART*/ + if (ui->usb_uart_switch) + ui->usb_uart_switch(1); + /*configure uart pin to alternate function*/ + if (ui->serial_debug_gpios) + ui->serial_debug_gpios(1); + } + + queue_work(ui->usb_wq, &ui->work); + } + } + if (ui) + spin_unlock_irqrestore(&ui->lock, flags); +} + +#if defined(CONFIG_DEBUG_FS) && 0 + +void usb_function_reenumerate(void) +{ + struct usb_info *ui = the_usb_info; + + /* disable and re-enable the D+ pullup */ + msm72k_pullup(&ui->gadget, false); + msleep(10); + msm72k_pullup(&ui->gadget, true); +} + +static char debug_buffer[PAGE_SIZE]; + +static ssize_t debug_read_status(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct usb_info *ui = file->private_data; + char *buf = debug_buffer; + unsigned long flags; + struct msm_endpoint *ept; + struct msm_request *req; + int n; + int i = 0; + + spin_lock_irqsave(&ui->lock, flags); + + i += scnprintf(buf + i, PAGE_SIZE - i, + "regs: setup=%08x prime=%08x stat=%08x done=%08x\n", + readl(USB_ENDPTSETUPSTAT), + readl(USB_ENDPTPRIME), + readl(USB_ENDPTSTAT), + readl(USB_ENDPTCOMPLETE)); + i += scnprintf(buf + i, PAGE_SIZE - i, + "regs: cmd=%08x sts=%08x intr=%08x port=%08x\n\n", + readl(USB_USBCMD), + readl(USB_USBSTS), + readl(USB_USBINTR), + readl(USB_PORTSC)); + + + for (n = 0; n < 32; n++) { + ept = ui->ept + n; + if (ept->ep.maxpacket == 0) + continue; + + i += scnprintf(buf + i, PAGE_SIZE - i, + "ept%d %s cfg=%08x active=%08x next=%08x info=%08x\n", + ept->num, (ept->flags & EPT_FLAG_IN) ? "in " : "out", + ept->head->config, ept->head->active, + ept->head->next, ept->head->info); + + for (req = ept->req; req; req = req->next) + i += scnprintf(buf + i, PAGE_SIZE - i, + " req @%08x next=%08x info=%08x page0=%08x %c %c\n", + req->item_dma, req->item->next, + req->item->info, req->item->page0, + req->busy ? 'B' : ' ', + req->live ? 'L' : ' ' + ); + } + + i += scnprintf(buf + i, PAGE_SIZE - i, + "phy failure count: %d\n", ui->phy_fail_count); + + spin_unlock_irqrestore(&ui->lock, flags); + + return simple_read_from_buffer(ubuf, count, ppos, buf, i); +} + +static ssize_t debug_write_reset(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct usb_info *ui = file->private_data; + unsigned long flags; + + spin_lock_irqsave(&ui->lock, flags); + ui->flags |= USB_FLAG_RESET; + queue_work(ui->usb_wq, &ui->work); + spin_unlock_irqrestore(&ui->lock, flags); + + return count; +} + +static ssize_t debug_write_cycle(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + usb_function_reenumerate(); + return count; +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +const struct file_operations debug_stat_ops = { + .open = debug_open, + .read = debug_read_status, +}; + +const struct file_operations debug_reset_ops = { + .open = debug_open, + .write = debug_write_reset, +}; + +const struct file_operations debug_cycle_ops = { + .open = debug_open, + .write = debug_write_cycle, +}; + +static void usb_debugfs_init(struct usb_info *ui) +{ + struct dentry *dent; + dent = debugfs_create_dir("usb", 0); + if (IS_ERR(dent)) + return; + + debugfs_create_file("status", 0444, dent, ui, &debug_stat_ops); + debugfs_create_file("reset", 0220, dent, ui, &debug_reset_ops); + debugfs_create_file("cycle", 0220, dent, ui, &debug_cycle_ops); +} +#else +static void usb_debugfs_init(struct usb_info *ui) {} +#endif + +static int +msm72k_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) +{ + struct msm_endpoint *ept = to_msm_endpoint(_ep); + unsigned char ep_type = + desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + + if (ep_type == USB_ENDPOINT_XFER_BULK) + _ep->maxpacket = le16_to_cpu(desc->wMaxPacketSize); + else + _ep->maxpacket = le16_to_cpu(64); + config_ept(ept); + usb_ept_enable(ept, 1, ep_type); + return 0; +} + +static int msm72k_disable(struct usb_ep *_ep) +{ + struct msm_endpoint *ept = to_msm_endpoint(_ep); + + usb_ept_enable(ept, 0, 0); + return 0; +} + +static struct usb_request * +msm72k_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + return usb_ept_alloc_req(to_msm_endpoint(_ep), 0, gfp_flags); +} + +static void +msm72k_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct msm_request *req = to_msm_request(_req); + struct msm_endpoint *ept = to_msm_endpoint(_ep); + struct usb_info *ui = ept->ui; + unsigned long flags; + int dead = 0; + + spin_lock_irqsave(&ui->lock, flags); + /* defer freeing resources if request is still busy */ + if (req->busy) + dead = req->dead = 1; + spin_unlock_irqrestore(&ui->lock, flags); + + /* if req->dead, then we will clean up when the request finishes */ + if (!dead) + do_free_req(ui, req); +} + +static int +msm72k_queue(struct usb_ep *_ep, struct usb_request *req, gfp_t gfp_flags) +{ + struct msm_endpoint *ep = to_msm_endpoint(_ep); + struct usb_info *ui = ep->ui; + + if (ep == &ui->ep0in) { + struct msm_request *r = to_msm_request(req); + if (!req->length) + goto ep_queue_done; + else { + if (ui->ep0_dir == USB_DIR_OUT) { + ep = &ui->ep0out; + ep->ep.driver_data = ui->ep0in.ep.driver_data; + } + /* ep0_queue_ack_complete queue a receive for ACK before + ** calling req->complete + */ + r->gadget_complete = req->complete; + req->complete = ep0_queue_ack_complete; + } + } +ep_queue_done: + return usb_ept_queue_xfer(ep, req); +} + +static int msm72k_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct msm_endpoint *ep = to_msm_endpoint(_ep); + struct msm_request *req = to_msm_request(_req); + struct usb_info *ui = ep->ui; + + struct msm_request *cur, *prev; + unsigned long flags; + + if (!_ep || !_req) + return -EINVAL; + + spin_lock_irqsave(&ui->lock, flags); + cur = ep->req; + prev = NULL; + + while (cur != 0) { + if (cur == req) { + req->busy = 0; + req->live = 0; + req->req.status = -ECONNRESET; + req->req.actual = 0; + if (req->req.complete) { + spin_unlock_irqrestore(&ui->lock, flags); + req->req.complete(&ep->ep, &req->req); + spin_lock_irqsave(&ui->lock, flags); + } + if (req->dead) + do_free_req(ui, req); + /* remove from linked list */ + if (prev) + prev->next = cur->next; + else + ep->req = cur->next; + if (ep->last == cur) + ep->last = prev; + /* break from loop */ + cur = NULL; + } else { + prev = cur; + cur = cur->next; + } + } + spin_unlock_irqrestore(&ui->lock, flags); + + return 0; +} + +static int +msm72k_set_halt(struct usb_ep *_ep, int value) +{ + struct msm_endpoint *ept = to_msm_endpoint(_ep); + struct usb_info *ui = ept->ui; + unsigned int in = ept->flags & EPT_FLAG_IN; + unsigned int n; + unsigned long flags; + + spin_lock_irqsave(&ui->lock, flags); + n = readl(USB_ENDPTCTRL(ept->num)); + + if (in) { + if (value) + n |= CTRL_TXS; + else { + n &= ~CTRL_TXS; + n |= CTRL_TXR; + } + } else { + if (value) + n |= CTRL_RXS; + else { + n &= ~CTRL_RXS; + n |= CTRL_RXR; + } + } + writel(n, USB_ENDPTCTRL(ept->num)); + spin_unlock_irqrestore(&ui->lock, flags); + + return 0; +} + +static int +msm72k_fifo_status(struct usb_ep *_ep) +{ + return -EOPNOTSUPP; +} + +static void +msm72k_fifo_flush(struct usb_ep *_ep) +{ + flush_endpoint(to_msm_endpoint(_ep)); +} + +static const struct usb_ep_ops msm72k_ep_ops = { + .enable = msm72k_enable, + .disable = msm72k_disable, + + .alloc_request = msm72k_alloc_request, + .free_request = msm72k_free_request, + + .queue = msm72k_queue, + .dequeue = msm72k_dequeue, + + .set_halt = msm72k_set_halt, + .fifo_status = msm72k_fifo_status, + .fifo_flush = msm72k_fifo_flush, +}; + +static int msm72k_get_frame(struct usb_gadget *_gadget) +{ + struct usb_info *ui = container_of(_gadget, struct usb_info, gadget); + + /* frame number is in bits 13:3 */ + return (readl(USB_FRINDEX) >> 3) & 0x000007FF; +} + +/* VBUS reporting logically comes from a transceiver */ +static int msm72k_udc_vbus_session(struct usb_gadget *_gadget, int is_active) +{ + msm_hsusb_set_vbus_state(is_active); + return 0; +} + +/* drivers may have software control over D+ pullup */ +static int msm72k_pullup(struct usb_gadget *_gadget, int is_active) +{ + struct usb_info *ui = container_of(_gadget, struct usb_info, gadget); + + u32 cmd = (8 << 16); + + /* disable/enable D+ pullup */ + if (is_active) { + pr_info("msm_hsusb: enable pullup\n"); + writel(cmd | 1, USB_USBCMD); + } else { + pr_info("msm_hsusb: disable pullup\n"); + writel(cmd, USB_USBCMD); + +#ifndef CONFIG_ARCH_MSM7X00A + ulpi_write(ui, 0x48, 0x04); +#endif + } + + return 0; +} + +static int msm72k_wakeup(struct usb_gadget *_gadget) +{ + struct usb_info *ui = container_of(_gadget, struct usb_info, gadget); + unsigned long flags; + + if (!ui->remote_wakeup) { + pr_err("%s: remote wakeup not supported\n", __func__); + return -ENOTSUPP; + } + + if (!ui->online) { + pr_err("%s: device is not configured\n", __func__); + return -ENODEV; + } + + spin_lock_irqsave(&ui->lock, flags); + if ((readl(USB_PORTSC) & PORTSC_SUSP) == PORTSC_SUSP) { + pr_info("%s: enabling force resume\n", __func__); + writel(readl(USB_PORTSC) | PORTSC_FPR, USB_PORTSC); + } + spin_unlock_irqrestore(&ui->lock, flags); + + return 0; +} + +static const struct usb_gadget_ops msm72k_ops = { + .get_frame = msm72k_get_frame, + .vbus_session = msm72k_udc_vbus_session, + .pullup = msm72k_pullup, + /* .wakeup = msm72k_wakeup, */ +}; + +static ssize_t usb_remote_wakeup(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_info *ui = the_usb_info; + + msm72k_wakeup(&ui->gadget); + + return count; +} +static DEVICE_ATTR(wakeup, S_IWUSR, 0, usb_remote_wakeup); + +static void ac_detect_expired(unsigned long _data) +{ + struct usb_info *ui = (struct usb_info *) _data; + u32 delay = 0; + + printk(KERN_INFO "%s: count = %d, connect_type = 0x%04x\n", __func__, + ui->ac_detect_count, ui->connect_type); + + if (ui->connect_type == CONNECT_TYPE_USB || ui->ac_detect_count >= 3) + return; + + /* detect shorted D+/D-, indicating AC power */ + if ((readl(USB_PORTSC) & PORTSC_LS) != PORTSC_LS) { + ui->ac_detect_count++; + /* detect delay: 3 sec, 5 sec, 10 sec */ + if (ui->ac_detect_count == 1) + delay = 5 * HZ; + else if (ui->ac_detect_count == 2) + delay = 10 * HZ; + + mod_timer(&ui->ac_detect_timer, jiffies + delay); + } else { + printk(KERN_INFO "usb: AC charger\n"); + ui->connect_type = CONNECT_TYPE_AC; + queue_work(ui->usb_wq, &ui->notifier_work); + writel(0x00080000, USB_USBCMD); + mdelay(10); + usb_lpm_enter(ui); + } +} + +static int msm72k_probe(struct platform_device *pdev) +{ + struct resource *res; + struct usb_info *ui; + int irq; + int ret; + + INFO("msm72k_probe\n"); + ui = kzalloc(sizeof(struct usb_info), GFP_KERNEL); + if (!ui) + return -ENOMEM; + + spin_lock_init(&ui->lock); + ui->pdev = pdev; + + if (pdev->dev.platform_data) { + struct msm_hsusb_platform_data *pdata = pdev->dev.platform_data; + ui->phy_reset = pdata->phy_reset; + ui->phy_init_seq = pdata->phy_init_seq; + ui->usb_connected = pdata->usb_connected; + ui->usb_uart_switch = pdata->usb_uart_switch; + ui->serial_debug_gpios = pdata->serial_debug_gpios; + ui->usb_hub_enable = pdata->usb_hub_enable; + ui->china_ac_detect = pdata->china_ac_detect; + ui->disable_usb_charger = pdata->disable_usb_charger; + + ui->accessory_detect = pdata->accessory_detect; + printk(KERN_INFO "usb: accessory detect %d\n", + ui->accessory_detect); + ui->usb_id_pin_gpio = pdata->usb_id_pin_gpio; + printk(KERN_INFO "usb: id_pin_gpio %d\n", + pdata->usb_id_pin_gpio); + + ui->dock_detect = pdata->dock_detect; + printk(KERN_INFO "usb: dock detect %d\n", + ui->dock_detect); + ui->dock_pin_gpio = pdata->dock_pin_gpio; + printk(KERN_INFO "usb: dock pin gpio %d\n", + ui->dock_pin_gpio); + + ui->idpin_irq = pdata->id_pin_irq; + if (pdata->config_usb_id_gpios) + ui->config_usb_id_gpios = pdata->config_usb_id_gpios; + } + + irq = platform_get_irq(pdev, 0); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res || (irq < 0)) + return usb_free(ui, -ENODEV); + + ui->addr = ioremap(res->start, 4096); + if (!ui->addr) + return usb_free(ui, -ENOMEM); + + ui->buf = dma_alloc_coherent(&pdev->dev, 4096, &ui->dma, GFP_KERNEL); + if (!ui->buf) + return usb_free(ui, -ENOMEM); + + ui->pool = dma_pool_create("msm72k_udc", NULL, 32, 32, 0); + if (!ui->pool) + return usb_free(ui, -ENOMEM); + + INFO("msm72k_probe() io=%p, irq=%d, dma=%p(%x)\n", + ui->addr, irq, ui->buf, ui->dma); + +#ifdef CONFIG_ARCH_MSM7X30 + msm_hsusb_rpc_connect(); +#endif + ui->clk = clk_get(&pdev->dev, "usb_hs_clk"); + if (IS_ERR(ui->clk)) + return usb_free(ui, PTR_ERR(ui->clk)); + + ui->pclk = clk_get(&pdev->dev, "usb_hs_pclk"); + if (IS_ERR(ui->pclk)) + return usb_free(ui, PTR_ERR(ui->pclk)); + + ui->otgclk = clk_get(&pdev->dev, "usb_otg_clk"); + if (IS_ERR(ui->otgclk)) + ui->otgclk = NULL; + + ui->coreclk = clk_get(&pdev->dev, "usb_hs_core_clk"); + if (IS_ERR(ui->coreclk)) + ui->coreclk = NULL; + + ui->ebi1clk = clk_get(NULL, "ebi1_clk"); + if (IS_ERR(ui->ebi1clk)) + return usb_free(ui, PTR_ERR(ui->ebi1clk)); + + /* clear interrupts before requesting irq */ + if (ui->coreclk) + clk_enable(ui->coreclk); + clk_enable(ui->clk); + clk_enable(ui->pclk); + if (ui->otgclk) + clk_enable(ui->otgclk); + writel(0, USB_USBINTR); + writel(0, USB_OTGSC); + if (ui->coreclk) + clk_disable(ui->coreclk); + if (ui->otgclk) + clk_disable(ui->otgclk); + clk_disable(ui->pclk); + clk_disable(ui->clk); + + ui->in_lpm = 1; + ret = request_irq(irq, usb_interrupt, 0, pdev->name, ui); + if (ret) + return usb_free(ui, ret); + enable_irq_wake(irq); + ui->irq = irq; + + ui->gadget.ops = &msm72k_ops; + ui->gadget.is_dualspeed = 1; + device_initialize(&ui->gadget.dev); + dev_set_name(&ui->gadget.dev, "gadget"); + ui->gadget.dev.parent = &pdev->dev; + ui->gadget.dev.dma_mask = pdev->dev.dma_mask; + + the_usb_info = ui; + + usb_debugfs_init(ui); + + usb_prepare(ui); + + /* initialize mfg serial number */ + + if (board_mfg_mode() == 1) { + use_mfg_serialno = 1; + wake_lock_init(&vbus_idle_wake_lock, WAKE_LOCK_IDLE, "usb_idle_lock"); + perf_lock_init(&usb_perf_lock, PERF_LOCK_HIGHEST, "usb"); + } else + use_mfg_serialno = 0; + strncpy(mfg_df_serialno, "000000000000", strlen("000000000000")); + + ui->ac_detect_count = 0; + ui->ac_detect_timer.data = (unsigned long) ui; + ui->ac_detect_timer.function = ac_detect_expired; + init_timer(&ui->ac_detect_timer); + return 0; +} + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct usb_info *ui = the_usb_info; + int retval, n; + + if (!driver + || driver->speed < USB_SPEED_FULL + || !driver->bind + || !driver->disconnect + || !driver->setup) + return -EINVAL; + if (!ui) + return -ENODEV; + if (ui->driver) + return -EBUSY; + + /* first hook up the driver ... */ + ui->driver = driver; + ui->gadget.dev.driver = &driver->driver; + ui->gadget.name = driver_name; + INIT_LIST_HEAD(&ui->gadget.ep_list); + ui->gadget.ep0 = &ui->ep0in.ep; + INIT_LIST_HEAD(&ui->gadget.ep0->ep_list); + ui->gadget.speed = USB_SPEED_UNKNOWN; + + for (n = 1; n < 16; n++) { + struct msm_endpoint *ept = ui->ept + n; + list_add_tail(&ept->ep.ep_list, &ui->gadget.ep_list); + ept->ep.maxpacket = 512; + } + for (n = 17; n < 32; n++) { + struct msm_endpoint *ept = ui->ept + n; + list_add_tail(&ept->ep.ep_list, &ui->gadget.ep_list); + ept->ep.maxpacket = 512; + } + + retval = device_add(&ui->gadget.dev); + if (retval) + goto fail; + + retval = driver->bind(&ui->gadget); + if (retval) { + INFO("bind to driver %s --> error %d\n", + driver->driver.name, retval); + device_del(&ui->gadget.dev); + goto fail; + } + + /* create sysfs node for remote wakeup */ + retval = device_create_file(&ui->gadget.dev, &dev_attr_wakeup); + if (retval != 0) + INFO("failed to create sysfs entry: (wakeup) error: (%d)\n", + retval); + INFO("msm72k_udc: registered gadget driver '%s'\n", + driver->driver.name); + usb_start(ui); + + return 0; + +fail: + ui->driver = NULL; + ui->gadget.dev.driver = NULL; + return retval; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct usb_info *dev = the_usb_info; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver || !driver->unbind) + return -EINVAL; + + device_remove_file(&dev->gadget.dev, &dev_attr_wakeup); + driver->unbind(&dev->gadget); + dev->gadget.dev.driver = NULL; + dev->driver = NULL; + + device_del(&dev->gadget.dev); + + VDEBUG("unregistered gadget driver '%s'\n", driver->driver.name); + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + + +static struct platform_driver usb_driver = { + .probe = msm72k_probe, + .driver = { .name = "msm_hsusb", }, +}; + +static int __init init(void) +{ + return platform_driver_register(&usb_driver); +} +module_init(init); + +static void __exit cleanup(void) +{ + platform_driver_unregister(&usb_driver); +} +module_exit(cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Mike Lockwood, Brian Swetland"); +MODULE_LICENSE("GPL"); From 2b4ba28a54c55db2dddc00df6ead6844f4e5cb71 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 9 Nov 2010 00:26:27 -0500 Subject: [PATCH 1035/2556] video: msm: Updated MDDI from HTC Change-Id: I8352acff6d60d1bcaf2504b9ef2602de43ab11eb --- arch/arm/mach-msm/include/mach/msm_fb.h | 188 +++++---- drivers/video/msm/hdmi/hdmi_lcdc.c | 28 -- drivers/video/msm/mddi.c | 410 +++++++++++++------ drivers/video/msm/mddi_client_simple.c | 2 +- drivers/video/msm/mddi_hw.h | 30 +- drivers/video/msm/mdp.c | 513 ++++++++++++++++++++---- drivers/video/msm/mdp_hw.h | 238 +++++++---- drivers/video/msm/mdp_hw_legacy.c | 104 ++++- drivers/video/msm/mdp_lcdc.c | 382 +++++++++++++++++- drivers/video/msm/msm_fb.c | 418 ++++++++++++++++++- kernel/power/fbearlysuspend.c | 2 +- 11 files changed, 1886 insertions(+), 429 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/msm_fb.h b/arch/arm/mach-msm/include/mach/msm_fb.h index ac18c5e64e90e..cad39e53756c3 100644 --- a/arch/arm/mach-msm/include/mach/msm_fb.h +++ b/arch/arm/mach-msm/include/mach/msm_fb.h @@ -27,17 +27,16 @@ struct mddi_info; #define MSM_MDP_OUT_IF_FMT_RGB888 2 /* mdp override operations */ -#define MSM_MDP_PANEL_IGNORE_PIXEL_DATA (1 << 0) -#define MSM_MDP_PANEL_FLIP_UD (1 << 1) -#define MSM_MDP_PANEL_FLIP_LR (1 << 2) -#define MSM_MDP4_MDDI_DMA_SWITCH (1 << 3) +#define MSM_MDP_PANEL_IGNORE_PIXEL_DATA (1 << 0) +#define MSM_MDP_PANEL_FLIP_UD (1 << 1) +#define MSM_MDP_PANEL_FLIP_LR (1 << 2) +#define MSM_MDP4_MDDI_DMA_SWITCH (1 << 3) +#define MSM_MDP_DMA_PACK_ALIGN_LSB (1 << 4) +#define MSM_MDP_RGB_PANEL_SELE_REFRESH (1 << 5) /* mddi type */ -#define MSM_MDP_MDDI_TYPE_I 0 -#define MSM_MDP_MDDI_TYPE_II 1 - -/* lcdc override operations */ -#define MSM_MDP_LCDC_DMA_PACK_ALIGN_LSB (1 << 0) +#define MSM_MDP_MDDI_TYPE_I 0 +#define MSM_MDP_MDDI_TYPE_II 1 struct msm_fb_data { int xres; /* x resolution in pixels */ @@ -56,12 +55,13 @@ enum { MSM_MDDI_EMDH_INTERFACE, MSM_EBI2_INTERFACE, MSM_LCDC_INTERFACE, + MSM_TV_INTERFACE, - MSM_MDP_NUM_INTERFACES = MSM_LCDC_INTERFACE + 1, + MSM_MDP_NUM_INTERFACES = MSM_TV_INTERFACE + 1 }; #define MSMFB_CAP_PARTIAL_UPDATES (1 << 0) -#define MSMFB_CAP_CABC (1 << 1) +#define MSMFB_CAP_CABC (1 << 1) struct msm_lcdc_timing { unsigned int clk_rate; /* dclk freq */ @@ -104,34 +104,33 @@ struct msm_panel_data { /* capabilities supported by the panel */ uint32_t caps; - /* - * For samsung driver IC, we always need to indicate where - * to draw. So we pass update_into to mddi client. - * - */ - struct { - int left; - int top; - int eright; /* exclusive */ - int ebottom; /* exclusive */ - } update_info; - + /* + * For samsung driver IC, we always need to indicate where + * to draw. So we pass update_into to mddi client. + * + */ + struct { + int left; + int top; + int eright; /* exclusive */ + int ebottom; /* exclusive */ + } update_info; }; enum { - MDP_DMA_P = 0, - MDP_DMA_S, + MDP_DMA_P = 0, + MDP_DMA_S, }; struct msm_mdp_platform_data { - /* from the enum above */ - int dma_channel; - unsigned overrides; - unsigned color_format; - int tearing_check; - unsigned sync_config; - unsigned sync_thresh; - unsigned sync_start_pos; + /* from the enum above */ + int dma_channel; + unsigned overrides; + unsigned color_format; + int tearing_check; + unsigned sync_config; + unsigned sync_thresh; + unsigned sync_start_pos; }; struct msm_mddi_client_data { @@ -159,8 +158,6 @@ struct msm_mddi_platform_data { /* fixup the mfr name, product id */ void (*fixup)(uint16_t *mfr_name, uint16_t *product_id); - int vsync_irq; - struct resource *fb_resource; /*optional*/ /* number of clients in the list that follows */ int num_clients; @@ -192,6 +189,11 @@ struct msm_lcdc_panel_ops { int (*uninit)(struct msm_lcdc_panel_ops *); int (*blank)(struct msm_lcdc_panel_ops *); int (*unblank)(struct msm_lcdc_panel_ops *); + int (*shutdown)(struct msm_lcdc_panel_ops *); +#ifdef CONFIG_PANEL_SELF_REFRESH + int (*refresh_enable)(struct msm_lcdc_panel_ops *); + int (*refresh_disable)(struct msm_lcdc_panel_ops *); +#endif }; struct msm_lcdc_platform_data { @@ -200,19 +202,20 @@ struct msm_lcdc_platform_data { int fb_id; struct msm_fb_data *fb_data; struct resource *fb_resource; - unsigned overrides; }; struct msm_tvenc_platform_data { - struct msm_tvenc_panel_ops *panel_ops; - int fb_id; - struct msm_fb_data *fb_data; - struct resource *fb_resource; - int (*video_relay)(int on_off); - }; + struct msm_tvenc_panel_ops *panel_ops; + int fb_id; + struct msm_fb_data *fb_data; + struct resource *fb_resource; + int (*video_relay)(int on_off); +}; struct mdp_blit_req; struct fb_info; +struct mdp_overlay; +struct msmfb_overlay_data; struct mdp_device { struct device dev; void (*dma)(struct mdp_device *mdp, uint32_t addr, @@ -221,12 +224,25 @@ struct mdp_device { void (*dma_wait)(struct mdp_device *mdp, int interface); int (*blit)(struct mdp_device *mdp, struct fb_info *fb, struct mdp_blit_req *req); +#ifdef CONFIG_FB_MSM_OVERLAY + int (*overlay_get)(struct mdp_device *mdp, struct fb_info *fb, + struct mdp_overlay *req); + int (*overlay_set)(struct mdp_device *mdp, struct fb_info *fb, + struct mdp_overlay *req); + int (*overlay_unset)(struct mdp_device *mdp, struct fb_info *fb, + int ndx); + int (*overlay_play)(struct mdp_device *mdp, struct fb_info *fb, + struct msmfb_overlay_data *req, struct file **p_src_file); +#endif void (*set_grp_disp)(struct mdp_device *mdp, uint32_t disp_id); void (*configure_dma)(struct mdp_device *mdp); int (*check_output_format)(struct mdp_device *mdp, int bpp); int (*set_output_format)(struct mdp_device *mdp, int bpp); - unsigned overrides; + void (*set_panel_size)(struct mdp_device *mdp, int width, int height); unsigned color_format; + unsigned overrides; + uint32_t width; /*panel width*/ + uint32_t height; /*panel height*/ }; struct class_interface; @@ -234,18 +250,22 @@ int register_mdp_client(struct class_interface *class_intf); /**** private client data structs go below this line ***/ +/* + * Panel private data, include backlight stuff + * 9/28 09', Jay + * */ struct panel_data { - int panel_id; - u32 caps; - int shrink; - /* backlight data */ - u8 *pwm; - int min_level; - /* default_br used in turn on backlight, must sync with setting in user space */ - int default_br; - int (*shrink_br)(int brightness); - int (*change_cabcmode)(struct msm_mddi_client_data *client_data, - int mode, u8 dimming); + int panel_id; + u32 caps; + int shrink; + /* backlight data */ + u8 *pwm; + int min_level; + /* default_br used in turn on backlight, must sync with setting in user space */ + int default_br; + int (*shrink_br)(int brightness); + int (*change_cabcmode)(struct msm_mddi_client_data *client_data, + int mode, u8 dimming); }; struct msm_mddi_bridge_platform_data { @@ -260,27 +280,55 @@ struct msm_mddi_bridge_platform_data { int (*unblank)(struct msm_mddi_bridge_platform_data *, struct msm_mddi_client_data *); int (*shutdown)(struct msm_mddi_bridge_platform_data *, - struct msm_mddi_client_data *); + struct msm_mddi_client_data *); struct msm_fb_data fb_data; - - struct panel_data panel_conf; - /* for those MDDI client which need to re-position display region - after each update or static electricity strike. It should be - implemented in board-xxx-panel due to the function itself need to - send the screen dimensional info of screen to MDDI client. - */ - void (*adjust)(struct msm_mddi_client_data *); + struct panel_data panel_conf; + /* for those MDDI client which need to re-position display region + after each update or static electricity strike. It should be + implemented in board-xxx-panel due to the function itself need to + send the screen dimensional info of screen to MDDI client. + */ + void (*adjust)(struct msm_mddi_client_data *); #define SAMSUNG_D 0 #define SAMSUNG_S6 1 - int bridge_type; - int panel_type; - - /* board file will identify what capabilities the panel supports */ - uint32_t panel_caps; - /* backlight data */ - u8 *pwm; + int bridge_type; + int panel_type; + uint32_t caps; + /* backlight data */ + u8 *pwm; }; +/* + * This is used to communicate event between msm_fb, mddi, mddi_client, + * and board. + * It's mainly used to reset the display system. + * Also, it is used for battery power policy. + * + */ +#define NOTIFY_MDDI 0x00000000 +#define NOTIFY_POWER 0x00000001 +#define NOTIFY_MSM_FB 0x00000010 + +extern int register_display_notifier(struct notifier_block *nb); +extern int display_notifier_call_chain(unsigned long val, void *data); + +#define display_notifier(fn, pri) { \ + static struct notifier_block fn##_nb = \ + { .notifier_call = fn, .priority = pri }; \ + register_display_notifier(&fn##_nb); \ +} + +#if (defined(CONFIG_USB_FUNCTION_PROJECTOR) || defined(CONFIG_USB_ANDROID_PROJECTOR)) +/* For USB Projector to quick access the frame buffer info */ +struct msm_fb_info { + unsigned char *fb_addr; + int msmfb_area; + int xres; + int yres; +}; +extern int msmfb_get_var(struct msm_fb_info *tmp); +extern int msmfb_get_fb_area(void); +#endif #endif diff --git a/drivers/video/msm/hdmi/hdmi_lcdc.c b/drivers/video/msm/hdmi/hdmi_lcdc.c index 45bbbb35a34b2..32dbc0c868444 100644 --- a/drivers/video/msm/hdmi/hdmi_lcdc.c +++ b/drivers/video/msm/hdmi/hdmi_lcdc.c @@ -40,34 +40,6 @@ #define HDMI_DBG(s...) do {} while (0) #endif -struct mdp_lcdc_info { - struct mdp_info *mdp; - struct clk *mdp_clk; - struct clk *pclk; - struct clk *pad_pclk; - struct msm_panel_data fb_panel_data; - struct platform_device fb_pdev; - struct msm_lcdc_platform_data *pdata; - uint32_t fb_start; - - struct msmfb_callback frame_start_cb; - wait_queue_head_t vsync_waitq; - int got_vsync; - - struct { - uint32_t clk_rate; - uint32_t hsync_ctl; - uint32_t vsync_period; - uint32_t vsync_pulse_width; - uint32_t display_hctl; - uint32_t display_vstart; - uint32_t display_vend; - uint32_t hsync_skew; - uint32_t polarity; - } parms; - atomic_t blank_count; - struct mutex blank_lock; -}; struct mdp_lcdc_info *_lcdc; static struct mdp_device *mdp_dev; diff --git a/drivers/video/msm/mddi.c b/drivers/video/msm/mddi.c index 2f337da613c7e..15d4b725b084e 100644 --- a/drivers/video/msm/mddi.c +++ b/drivers/video/msm/mddi.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -32,7 +31,8 @@ #include #include #include - +#include +#include #include #include "mddi_hw.h" @@ -45,7 +45,9 @@ #define CMD_GET_CLIENT_STATUS 0x0602 static uint32_t mddi_debug_flags; - +#ifdef CONFIG_MSM_MDP40 +static struct clk *mdp_clk; +#endif union mddi_rev { unsigned char raw[MDDI_REV_BUFFER_SIZE]; struct mddi_rev_packet hdr; @@ -67,8 +69,6 @@ struct mddi_info { char __iomem *base; int irq; struct clk *clk; - struct clk *pclk; - unsigned long clk_rate; struct msm_mddi_client_data client_data; /* buffer for rev encap packets */ @@ -97,19 +97,20 @@ struct mddi_info { void (*power_client)(struct msm_mddi_client_data *, int); - /* used to save/restore pad config during suspend */ - uint32_t pad_ctrl; - /* client device published to bind us to the * appropriate mddi_client driver */ char client_name[20]; struct platform_device client_pdev; - struct resource client_vsync_res; + unsigned type; + char debugfs_buf[32]; }; static void mddi_init_rev_encap(struct mddi_info *mddi); +/* FIXME: Workaround for Novatek +static void mddi_skew_calibration(struct mddi_info *mddi); +*/ #define mddi_readl(r) readl(mddi->base + (MDDI_##r)) #define mddi_writel(v, r) writel((v), mddi->base + (MDDI_##r)) @@ -167,7 +168,7 @@ static void mddi_handle_rev_data(struct mddi_info *mddi, union mddi_rev *rev) printk(KERN_INFO "rev: got reg %x = %x without " " pending read\n", rev->reg.register_address, - rev->reg.register_data_list); + rev->reg.u.reg_data); break; } if (ri->reg != rev->reg.register_address) { @@ -175,12 +176,12 @@ static void mddi_handle_rev_data(struct mddi_info *mddi, union mddi_rev *rev) "wrong register, expected " "%x\n", rev->reg.register_address, - rev->reg.register_data_list, ri->reg); + rev->reg.u.reg_data, ri->reg); break; } mddi->reg_read = NULL; ri->status = 0; - ri->result = rev->reg.register_data_list; + ri->result = rev->reg.u.reg_data; complete(&ri->done); break; default: @@ -358,7 +359,7 @@ static irqreturn_t mddi_isr(int irq, void *data) static long mddi_wait_interrupt_timeout(struct mddi_info *mddi, uint32_t intmask, int timeout) { - unsigned long irq_flags; + unsigned long irq_flags=0; spin_lock_irqsave(&mddi->int_lock, irq_flags); mddi->got_int &= ~intmask; @@ -402,14 +403,11 @@ static uint16_t mddi_init_registers(struct mddi_info *mddi) mddi_writel(0x0001, VERSION); mddi_writel(MDDI_HOST_BYTES_PER_SUBFRAME, BPS); mddi_writel(0x0003, SPM); /* subframes per media */ - mddi_writel(0x0005, TA1_LEN); + if (mddi->type == MSM_MDP_MDDI_TYPE_II) + mddi_writel(0x00C8, TA1_LEN); + else + mddi_writel(0x0005, TA1_LEN); mddi_writel(MDDI_HOST_TA2_LEN, TA2_LEN); - mddi_writel(0x0096, DRIVE_HI); - /* 0x32 normal, 0x50 for Toshiba display */ - - /* XXX: should we use 0x32? */ - mddi_writel(0x0050, DRIVE_LO); - mddi_writel(0x003C, DISP_WAKE); /* wakeup counter */ mddi_writel(MDDI_HOST_REV_RATE_DIV, REV_RATE_DIV); @@ -427,14 +425,26 @@ static uint16_t mddi_init_registers(struct mddi_info *mddi) udelay(5); } - /* XXX: NEED SUPPORT FOR 1.2 */ - /* Recommendation from PAD hw team */ - mddi_writel(0xa850f, PAD_CTL); - -#if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP40) + if (mddi->type == MSM_MDP_MDDI_TYPE_II) + mddi_writel(0x402a850f, PAD_CTL); + else + mddi_writel(0xa850f, PAD_CTL); + +#if defined (CONFIG_ARCH_QSD8X50) || defined (CONFIG_ARCH_MSM7X30) + /* Only for novatek driver IC*/ + mddi_writel(0x00C8, DRIVE_HI); + /* 0x32 normal, 0x50 for Toshiba display */ + mddi_writel(0x0050, DRIVE_LO); mddi_writel(0x00320000, PAD_IO_CTL); - mddi_writel(0x00220020, PAD_CAL); + if (mddi->type == MSM_MDP_MDDI_TYPE_II) + mddi_writel(0x40880020, PAD_CAL); + else + mddi_writel(0x00220020, PAD_CAL); +#else + mddi_writel(0x0096, DRIVE_HI); + /* 0x32 normal, 0x50 for Toshiba display */ + mddi_writel(0x0050, DRIVE_LO); #endif /* Need an even number for counts */ @@ -464,15 +474,11 @@ static void mddi_suspend(struct msm_mddi_client_data *cdata) /* turn off the link */ mddi_writel(MDDI_CMD_RESET, CMD); mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); - /* save pad ctrl and power down the drivers */ - mddi->pad_ctrl = mddi_readl(PAD_CTL); - mddi_writel(0, PAD_CTL); - /* release rate request to not hold any high speed plls */ - clk_set_rate(mddi->clk, 0); /* turn off the clock */ - if (mddi->pclk) - clk_disable(mddi->pclk); clk_disable(mddi->clk); +#ifdef CONFIG_MSM_MDP40 + clk_disable(mdp_clk); +#endif wake_unlock(&mddi->idle_lock); } @@ -481,21 +487,26 @@ static void mddi_resume(struct msm_mddi_client_data *cdata) struct mddi_info *mddi = container_of(cdata, struct mddi_info, client_data); wake_lock(&mddi->idle_lock); - clk_enable(mddi->clk); - if (mddi->pclk) - clk_enable(mddi->pclk); - clk_set_rate(mddi->clk, mddi->clk_rate); - mddi_writel(mddi->pad_ctrl, PAD_CTL); mddi_set_auto_hibernate(&mddi->client_data, 0); /* turn on the client */ if (mddi->power_client) mddi->power_client(&mddi->client_data, 1); +#ifdef CONFIG_MSM_MDP40 + clk_enable(mdp_clk); +#endif + /* turn on the clock */ + clk_enable(mddi->clk); /* set up the local registers */ mddi->rev_data_curr = 0; mddi_init_registers(mddi); +/* FIXME: Workaround for Novatek + if (mddi->type == MSM_MDP_MDDI_TYPE_II) + mddi_skew_calibration(mddi); +*/ mddi_writel(mddi->int_enable, INTEN); mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD); - mddi_writel(MDDI_CMD_SEND_RTD, CMD); + if (mddi->type == MSM_MDP_MDDI_TYPE_I) + mddi_writel(MDDI_CMD_SEND_RTD, CMD); mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); mddi_set_auto_hibernate(&mddi->client_data, 1); wake_unlock(&mddi->idle_lock); @@ -519,36 +530,44 @@ static int __init mddi_get_client_caps(struct mddi_info *mddi) mddi_writel(MDDI_CMD_LINK_ACTIVE, CMD); mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + /*FIXME: mddi host can't get caps on MDDI type 2*/ + if (mddi->type == MSM_MDP_MDDI_TYPE_I) { + for (j = 0; j < 3; j++) { + /* the toshiba vga panel does not respond to get + * caps unless you SEND_RTD, but the first SEND_RTD + * will fail... + */ + for (i = 0; i < 4; i++) { + uint32_t stat; + + mddi_writel(MDDI_CMD_SEND_RTD, CMD); + mdelay(1); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + stat = mddi_readl(STAT); + printk(KERN_INFO "mddi cmd send rtd: int %x, stat %x, " + "rtd val %x\n", mddi_readl(INT), stat, + mddi_readl(RTD_VAL)); + if ((stat & MDDI_STAT_RTD_MEAS_FAIL) == 0) { + mdelay(1); + break; + } + msleep(1); + } - for (j = 0; j < 3; j++) { - /* the toshiba vga panel does not respond to get - * caps unless you SEND_RTD, but the first SEND_RTD - * will fail... - */ - for (i = 0; i < 4; i++) { - uint32_t stat; - - mddi_writel(MDDI_CMD_SEND_RTD, CMD); + mddi_writel(CMD_GET_CLIENT_CAP, CMD); + mdelay(1); mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); - stat = mddi_readl(STAT); - printk(KERN_INFO "mddi cmd send rtd: int %x, stat %x, " - "rtd val %x\n", mddi_readl(INT), stat, - mddi_readl(RTD_VAL)); - if ((stat & MDDI_STAT_RTD_MEAS_FAIL) == 0) - break; - msleep(1); - } - - mddi_writel(CMD_GET_CLIENT_CAP, CMD); - mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); - wait_event_timeout(mddi->int_wait, mddi->flags & FLAG_HAVE_CAPS, + wait_event_timeout(mddi->int_wait, mddi->flags & FLAG_HAVE_CAPS, HZ / 100); - if (mddi->flags & FLAG_HAVE_CAPS) - break; - printk(KERN_INFO "mddi_init, timeout waiting for caps\n"); - } - return mddi->flags & FLAG_HAVE_CAPS; + if (mddi->flags & FLAG_HAVE_CAPS) + break; + printk(KERN_INFO KERN_ERR "mddi_init, timeout waiting for " + "caps\n"); + } + return (mddi->flags & FLAG_HAVE_CAPS); + } else + return 1; } /* link must be active when this is called */ @@ -589,53 +608,86 @@ int mddi_check_status(struct mddi_info *mddi) } -void mddi_remote_write(struct msm_mddi_client_data *cdata, uint32_t val, - uint32_t reg) +/* + * mddi_remote_write_vals - send the register access packet + * + * @cdata: mddi layer dedicated structure, holding info needed by mddi + * @val : parameters + * @reg : cmd + * @nr_bytes: size of parameters in bytes + * + * jay, Nov 13, 08' + * extend the single parameter to multiple. + */ +void mddi_remote_write_vals(struct msm_mddi_client_data *cdata, uint8_t * val, + uint32_t reg, unsigned int nr_bytes) { struct mddi_info *mddi = container_of(cdata, struct mddi_info, client_data); struct mddi_llentry *ll; struct mddi_register_access *ra; - /* unsigned s; */ + dma_addr_t bus_addr = 0; mutex_lock(&mddi->reg_write_lock); ll = mddi->reg_write_data; ra = &(ll->u.r); - ra->length = 14 + 4; + ra->length = 14 + nr_bytes; ra->type = TYPE_REGISTER_ACCESS; ra->client_id = 0; - ra->read_write_info = MDDI_WRITE | 1; + ra->read_write_info = MDDI_WRITE | (nr_bytes / 4); ra->crc16 = 0; ra->register_address = reg; - ra->register_data_list = val; ll->flags = 1; + /* register access packet header occupies 14 bytes */ ll->header_count = 14; - ll->data_count = 4; - ll->data = mddi->reg_write_addr + offsetof(struct mddi_llentry, - u.r.register_data_list); - ll->next = 0; - ll->reserved = 0; + ll->data_count = nr_bytes; /* num of bytes in the data field */ - /* s = mddi_readl(STAT); */ - /* printk(KERN_INFO "mddi_remote_write(%x, %x), stat = %x\n", val, - * reg, s); */ + if (nr_bytes == 4) { + uint32_t *prm = (uint32_t *)val; - mddi_writel(mddi->reg_write_addr, PRI_PTR); + ll->data = mddi->reg_write_addr + + offsetof(struct mddi_llentry, u.r.u.reg_data); + ra->u.reg_data = *prm; + } else { + int dma_retry = 5; - /* s = mddi_readl(STAT); */ - /* printk(KERN_INFO "mddi_remote_write(%x, %x) sent, stat = %x\n", - * val, reg, s); */ + while (dma_retry--) { + bus_addr = dma_map_single(NULL, (void *)val, nr_bytes, + DMA_TO_DEVICE); + if (dma_mapping_error(NULL, bus_addr) == 0) + break; + msleep(1); + } + if (dma_retry == 0) { + printk(KERN_ERR "%s: dma map fail!\n", __func__); + return; + } + ll->data = bus_addr; + ra->u.reg_data_list = (uint32_t *)bus_addr; + } + ll->next = 0; + ll->reserved = 0; + + /* inform mddi to start */ + mddi_writel(mddi->reg_write_addr, PRI_PTR); mddi_wait_interrupt(mddi, MDDI_INT_PRI_LINK_LIST_DONE); - /* printk(KERN_INFO "mddi_remote_write(%x, %x) done, stat = %x\n", - * val, reg, s); */ + if (bus_addr) + dma_unmap_single(NULL, bus_addr, nr_bytes, DMA_TO_DEVICE); mutex_unlock(&mddi->reg_write_lock); } +void mddi_remote_write(struct msm_mddi_client_data *cdata, uint32_t val, + uint32_t reg) +{ + uint8_t * p = (uint8_t *)&val; + mddi_remote_write_vals(cdata, p, reg, 4); +} + uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg) { struct mddi_info *mddi = container_of(cdata, struct mddi_info, @@ -645,7 +697,7 @@ uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg) struct reg_read_info ri; unsigned s; int retry_count = 2; - unsigned long irq_flags; + unsigned long irq_flags=0; mutex_lock(&mddi->reg_read_lock); @@ -672,9 +724,13 @@ uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg) ri.reg = reg; ri.status = -1; + ri.result = -1; do { init_completion(&ri.done); + if (mddi->type == MSM_MDP_MDDI_TYPE_II) + mddi_set_auto_hibernate(&mddi->client_data, 0); + mddi_writel(MDDI_CMD_SEND_RTD, CMD); mddi->reg_read = &ri; mddi_writel(mddi->reg_read_addr, PRI_PTR); @@ -687,10 +743,14 @@ uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg) /* while((s & MDDI_STAT_PRI_LINK_LIST_DONE) == 0){ */ /* s = mddi_readl(STAT); */ /* } */ - - /* Enable Periodic Reverse Encapsulation. */ - mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 1, CMD); - mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + if (mddi->type == MSM_MDP_MDDI_TYPE_II) { + mddi_writel(MDDI_CMD_SEND_REV_ENCAP, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_REV_DATA_AVAIL); + } else { + /* Enable Periodic Reverse Encapsulation. */ + mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 1, CMD); + mddi_wait_interrupt(mddi, MDDI_INT_NO_CMD_PKTS_PEND); + } if (wait_for_completion_timeout(&ri.done, HZ/10) == 0 && !ri.done.done) { printk(KERN_INFO "mddi_remote_read(%x) timeout " @@ -718,6 +778,8 @@ uint32_t mddi_remote_read(struct msm_mddi_client_data *cdata, uint32_t reg) "MDDI_CMD_SEND_RTD: int %x, stat %x, rtd val %x " "curr_rev_ptr %x\n", mddi_readl(INT), mddi_readl(STAT), mddi_readl(RTD_VAL), mddi_readl(CURR_REV_PTR)); + if (mddi->type == MSM_MDP_MDDI_TYPE_II) + mddi_set_auto_hibernate(&mddi->client_data, 1); } while (retry_count-- > 0); /* Disable Periodic Reverse Encapsulation. */ mddi_writel(MDDI_CMD_PERIODIC_REV_ENCAP | 0, CMD); @@ -736,33 +798,32 @@ static int __init mddi_clk_setup(struct platform_device *pdev, unsigned long clk_rate) { int ret; - +#ifdef CONFIG_MSM_MDP40 + mdp_clk = clk_get(&pdev->dev, "mdp_clk"); + if (IS_ERR(mdp_clk)) { + printk(KERN_INFO "mddi: failed to get mdp clk"); + return PTR_ERR(mdp_clk); + } + ret = clk_enable(mdp_clk); + if (ret) + goto fail; +#endif /* set up the clocks */ mddi->clk = clk_get(&pdev->dev, "mddi_clk"); if (IS_ERR(mddi->clk)) { printk(KERN_INFO "mddi: failed to get clock\n"); return PTR_ERR(mddi->clk); } - mddi->pclk = clk_get(&pdev->dev, "mddi_pclk"); - if (IS_ERR(mddi->pclk)) - mddi->pclk = NULL; - - clk_enable(mddi->clk); - if (mddi->pclk) - clk_enable(mddi->pclk); - - mddi->clk_rate = clk_rate; - ret = clk_set_rate(mddi->clk, mddi->clk_rate); + ret = clk_enable(mddi->clk); if (ret) goto fail; + ret = clk_set_rate(mddi->clk, clk_rate); + if (ret) + goto fail; + printk(KERN_DEBUG "mddi runs at %ld\n", clk_get_rate(mddi->clk)); return 0; fail: - if (mddi->pclk) { - clk_disable(mddi->pclk); - clk_put(mddi->pclk); - } - clk_disable(mddi->clk); clk_put(mddi->clk); return ret; } @@ -786,8 +847,98 @@ static int __init mddi_rev_data_setup(struct mddi_info *mddi) sizeof(*mddi->reg_write_data); return 0; } +/* FIXME: Workaround for Novatek +static void mddi_skew_calibration(struct mddi_info *mddi) +{ + struct msm_mddi_platform_data *pdata = mddi->client_pdev.dev.platform_data; + + clk_set_rate( mddi->clk, 50000000); + mdelay(1); + mddi_writel(MDDI_CMD_SKEW_CALIBRATION, CMD); + mdelay(1); + clk_set_rate( mddi->clk, pdata->clk_rate); + mdelay(1); +} +*/ + +static ssize_t mddi_reg_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t mddi_reg_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct mddi_info *mddi = (struct mddi_info*)file->private_data; -static int __devinit mddi_probe(struct platform_device *pdev) + return simple_read_from_buffer(user_buf, count, ppos, mddi->debugfs_buf, strlen(mddi->debugfs_buf)); +} + +static ssize_t mddi_reg_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + unsigned int reg, data; + char debug_buf[32], type; + int cnt, len; + struct mddi_info *mddi = file->private_data; + + memset(debug_buf, 0x00, sizeof(debug_buf)); + + if (count > sizeof(debug_buf)) + return -EFAULT; + + if (copy_from_user(debug_buf, user_buf, count)) + return -EFAULT; + + debug_buf[count] = 0; /* end of string */ + + if (debug_buf[0] == 'w') { + cnt = sscanf(debug_buf, "%s %x %x", &type ,®, &data); + mddi_set_auto_hibernate(&mddi->client_data, 0); + mddi_remote_write(&mddi->client_data, data, reg); + mddi_set_auto_hibernate(&mddi->client_data, 1); + + len = snprintf(mddi->debugfs_buf, sizeof(mddi->debugfs_buf), + "[W] reg=0x%x val=0x%x\n", reg, data); + printk(KERN_INFO "%s: reg=%x val=%x\n", __func__, reg, data); + } else { + cnt = sscanf(debug_buf, "%s %x", &type ,®); + + len = snprintf(mddi->debugfs_buf, sizeof(mddi->debugfs_buf), + "[R] reg=0x%x val=0x%x\n", reg, + mddi_remote_read(&mddi->client_data, reg)); + + printk(KERN_INFO "%s: reg=%x val=%x buf=%s\n", __func__, reg, + mddi_remote_read(&mddi->client_data, reg), debug_buf); + } + + return count; +} + +static struct file_operations mddi_reg_debugfs_fops[] = { + { + .open = mddi_reg_open, + .read = mddi_reg_read, + .write = mddi_reg_write, + } +}; + +int mddi_reg_debugfs_init(struct mddi_info *mddi) +{ + struct dentry *mddi_reg_dent; + + mddi_reg_dent = debugfs_create_dir("mddi", 0); + if (IS_ERR(mddi_reg_dent)) + return PTR_ERR(mddi_reg_dent); + + debugfs_create_file("reg", 0666, mddi_reg_dent, mddi, + &mddi_reg_debugfs_fops[0]); + + return 0; +} + +static int __init mddi_probe(struct platform_device *pdev) { struct msm_mddi_platform_data *pdata = pdev->dev.platform_data; struct mddi_info *mddi = &mddi_info[pdev->id]; @@ -815,6 +966,8 @@ static int __devinit mddi_probe(struct platform_device *pdev) printk(KERN_INFO "mddi: init() base=0x%p irq=%d\n", mddi->base, mddi->irq); mddi->power_client = pdata->power_client; + if (pdata->type != MSM_MDP_MDDI_TYPE_I) + mddi->type = pdata->type; mutex_init(&mddi->reg_write_lock); mutex_init(&mddi->reg_read_lock); @@ -847,9 +1000,10 @@ static int __devinit mddi_probe(struct platform_device *pdev) } /* turn on the mddi client bridge chip */ + #if 0 /*advised by SKY*/ if (mddi->power_client) mddi->power_client(&mddi->client_data, 1); - + #endif /* initialize the mddi registers */ mddi_set_auto_hibernate(&mddi->client_data, 0); mddi_writel(MDDI_CMD_RESET, CMD); @@ -862,8 +1016,6 @@ static int __devinit mddi_probe(struct platform_device *pdev) goto error_mddi_version; } - printk(KERN_INFO "mddi: host core version: 0x%02x\n", mddi->version); - /* read the capabilities off the client */ if (!mddi_get_client_caps(mddi)) { printk(KERN_INFO "mddi: no client found\n"); @@ -872,14 +1024,19 @@ static int __devinit mddi_probe(struct platform_device *pdev) printk(KERN_INFO "mddi powerdown: stat %x\n", mddi_readl(STAT)); msleep(100); printk(KERN_INFO "mddi powerdown: stat %x\n", mddi_readl(STAT)); - return 0; + goto dummy_client; } - mddi_set_auto_hibernate(&mddi->client_data, 1); - pr_info("%s: got mfr %04x product %04x\n", __func__, - mddi->caps.Mfr_Name, mddi->caps.Product_Code); + mddi_set_auto_hibernate(&mddi->client_data, 1); - if (mddi->caps.Mfr_Name == 0 && mddi->caps.Product_Code == 0) + /* + * FIXME: User kernel defconfig to link dedicated mddi client driver. + */ +#if 0 + if ( mddi->caps.Mfr_Name == 0 && mddi->caps.Product_Code == 0) +#else + if (mddi->caps.Mfr_Name == 0 ) +#endif pdata->fixup(&mddi->caps.Mfr_Name, &mddi->caps.Product_Code); mddi->client_pdev.id = 0; @@ -897,8 +1054,17 @@ static int __devinit mddi_probe(struct platform_device *pdev) } } - if (i >= pdata->num_clients) + if (i >= pdata->num_clients) { +dummy_client: + mddi->client_data.private_client_data = + pdata->client_platform_data[0].client_data; + mddi->client_pdev.name = + pdata->client_platform_data[0].name; + mddi->client_pdev.id = + pdata->client_platform_data[0].id; mddi->client_pdev.name = "mddi_c_dummy"; + clk_disable(mddi->clk); + } printk(KERN_INFO "mddi: registering panel %s\n", mddi->client_pdev.name); @@ -906,6 +1072,7 @@ static int __devinit mddi_probe(struct platform_device *pdev) mddi->client_data.resume = mddi_resume; mddi->client_data.activate_link = mddi_activate_link; mddi->client_data.remote_write = mddi_remote_write; + mddi->client_data.remote_write_vals = mddi_remote_write_vals; mddi->client_data.remote_read = mddi_remote_read; mddi->client_data.auto_hibernate = mddi_set_auto_hibernate; mddi->client_data.fb_resource = pdata->fb_resource; @@ -920,18 +1087,11 @@ static int __devinit mddi_probe(struct platform_device *pdev) goto error_mddi_interface; } - if (pdata->vsync_irq) { - mddi->client_vsync_res.start = pdata->vsync_irq; - mddi->client_vsync_res.end = pdata->vsync_irq; - mddi->client_vsync_res.flags = IORESOURCE_IRQ; - mddi->client_vsync_res.name = "vsync"; - mddi->client_pdev.resource = &mddi->client_vsync_res; - mddi->client_pdev.num_resources = 1; - } - mddi->client_pdev.dev.platform_data = &mddi->client_data; printk(KERN_INFO "mddi: publish: %s\n", mddi->client_name); platform_device_register(&mddi->client_pdev); + mddi_reg_debugfs_init(mddi); + return 0; error_mddi_interface: diff --git a/drivers/video/msm/mddi_client_simple.c b/drivers/video/msm/mddi_client_simple.c index 1613fca03344f..b27a809dd14ca 100644 --- a/drivers/video/msm/mddi_client_simple.c +++ b/drivers/video/msm/mddi_client_simple.c @@ -182,7 +182,7 @@ static int mddi_simple_probe(struct platform_device *pdev) panel->panel_data.resume = mddi_simple_resume; panel->panel_data.blank = mddi_simple_blank; panel->panel_data.unblank = mddi_simple_unblank; - panel->panel_data.caps = bridge_data->panel_caps; + panel->panel_data.caps = bridge_data->caps; panel->panel_data.fb_data = &bridge_data->fb_data; panel->pdev.name = "msm_panel"; diff --git a/drivers/video/msm/mddi_hw.h b/drivers/video/msm/mddi_hw.h index 47bb4494c5f71..7858b0e16e977 100644 --- a/drivers/video/msm/mddi_hw.h +++ b/drivers/video/msm/mddi_hw.h @@ -53,9 +53,9 @@ #define MDDI_MF_CNT 0x0084 #define MDDI_CURR_REV_PTR 0x0088 #define MDDI_CORE_VER 0x008c -#define MDDI_FIFO_ALLOC 0x0090 -#define MDDI_PAD_IO_CTL 0x00a0 -#define MDDI_PAD_CAL 0x00a4 +#define MDDI_SF_LEN_CTL_REG 0x0094 +#define MDDI_PAD_IO_CTL 0x00a0 +#define MDDI_PAD_CAL 0x00a4 #define MDDI_INT_PRI_PTR_READ 0x0001 #define MDDI_INT_SEC_PTR_READ 0x0002 @@ -115,9 +115,7 @@ #define MDDI_CMD_LINK_ACTIVE 0x0900 #define MDDI_CMD_PERIODIC_REV_ENCAP 0x0A00 #define MDDI_CMD_FORCE_NEW_REV_PTR 0x0C00 - - - +#define MDDI_CMD_SKEW_CALIBRATION 0x0D00 #define MDDI_VIDEO_REV_PKT_SIZE 0x40 #define MDDI_CLIENT_CAPABILITY_REV_PKT_SIZE 0x60 #define MDDI_MAX_REV_PKT_SIZE 0x60 @@ -128,15 +126,19 @@ /* MDP sends 256 pixel packets, so lower value hibernates more without * significantly increasing latency of waiting for next subframe */ #define MDDI_HOST_BYTES_PER_SUBFRAME 0x3C00 - -#if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP40) +#if defined (CONFIG_ARCH_QSD8X50) || defined (CONFIG_ARCH_MSM7X30) #define MDDI_HOST_TA2_LEN 0x001a -#define MDDI_HOST_REV_RATE_DIV 0x0004 #else #define MDDI_HOST_TA2_LEN 0x000c -#define MDDI_HOST_REV_RATE_DIV 0x0002 #endif +#if defined (CONFIG_ARCH_QSD8X50) +#define MDDI_HOST_REV_RATE_DIV 0x0004 +#elif defined (CONFIG_ARCH_MSM7X30) +#define MDDI_HOST_REV_RATE_DIV 0x0010 +#else +#define MDDI_HOST_REV_RATE_DIV 0x0002 +#endif struct __attribute__((packed)) mddi_rev_packet { uint16_t length; @@ -293,8 +295,12 @@ struct __attribute__((packed)) mddi_register_access { uint16_t crc16; - uint32_t register_data_list; - /* list of 4-byte register data values for/from client registers */ + union { + uint32_t reg_data; + uint32_t *reg_data_list; + } u; + + uint16_t crc_data; }; struct __attribute__((packed)) mddi_llentry { diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 9f32396aa689a..0fe5fa781109d 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -21,10 +21,10 @@ #include #include #include +#include #include #include #include -#include #include #include @@ -32,13 +32,41 @@ #include "mdp_hw.h" #include "mdp_ppp.h" +#include struct class *mdp_class; #define MDP_CMD_DEBUG_ACCESS_BASE (0x10000) +static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue); static unsigned int mdp_irq_mask; -static struct mdp_info *the_mdp; +static unsigned int mdp_dma_timer_enable = 0; +struct clk *mdp_clk_to_disable_later = 0; +static struct mdp_blit_req *timeout_req; +#ifdef CONFIG_FB_MSM_OVERLAY +extern int mdp4_overlay_get(struct mdp_device *mdp_dev, struct fb_info *info, struct mdp_overlay *req); +extern int mdp4_overlay_set(struct mdp_device *mdp_dev, struct fb_info *info, struct mdp_overlay *req); +extern int mdp4_overlay_unset(struct mdp_device *mdp_dev, struct fb_info *info, int ndx); +extern int mdp4_overlay_play(struct mdp_device *mdp_dev, struct fb_info *info, struct msmfb_overlay_data *req, + struct file **pp_src_file); +extern void mdp4_mddi_overlay(void *priv, uint32_t addr, uint32_t stride, + uint32_t width, uint32_t height, uint32_t x, + uint32_t y); +#include "mdp4.h" +#endif + + +static void mdp_do_standby_timer(unsigned long data) +{ + struct mdp_info *mdp = (struct mdp_info *) data; + if (!mdp_irq_mask) { + clk_set_rate(mdp->ebi1_clk, 0); + mdp->state |= MDP_STATE_STANDBY; + } else { + mod_timer(&mdp->standby_timer, + jiffies + msecs_to_jiffies(200)); + } +} static int locked_enable_mdp_irq(struct mdp_info *mdp, uint32_t mask) { @@ -52,11 +80,19 @@ static int locked_enable_mdp_irq(struct mdp_info *mdp, uint32_t mask) } /* if the mdp irq is not already enabled enable it */ if (!mdp_irq_mask) { - clk_set_rate(mdp->ebi1_clk, 128000000); clk_enable(mdp->clk); - if (mdp->pclk) - clk_enable(mdp->pclk); enable_irq(mdp->irq); + if (mdp->state & MDP_STATE_STANDBY) { +#ifdef CONFIG_MSM_MDP40 + clk_set_rate(mdp->ebi1_clk, 153000000); +#else + clk_set_rate(mdp->ebi1_clk, 128000000); +#endif + mdp->state &= ~MDP_STATE_STANDBY; + } else { + del_timer_sync(&mdp->standby_timer); + barrier(); + } } /* clear out any previous irqs for the requested mask*/ @@ -71,7 +107,7 @@ static int locked_enable_mdp_irq(struct mdp_info *mdp, uint32_t mask) static int enable_mdp_irq(struct mdp_info *mdp, uint32_t mask) { - unsigned long flags; + unsigned long flags=0; int ret; spin_lock_irqsave(&mdp->lock, flags); @@ -96,18 +132,18 @@ static int locked_disable_mdp_irq(struct mdp_info *mdp, uint32_t mask) /* if no one is waiting on the interrupt, disable it */ if (!mdp_irq_mask) { disable_irq_nosync(mdp->irq); - if (mdp->pclk) - clk_disable(mdp->pclk); if (mdp->clk) clk_disable(mdp->clk); - clk_set_rate(mdp->ebi1_clk, 0); + if (!(mdp->state & MDP_STATE_STANDBY)) + mod_timer(&mdp->standby_timer, + jiffies + msecs_to_jiffies(200)); } return 0; } int disable_mdp_irq(struct mdp_info *mdp, uint32_t mask) { - unsigned long irq_flags; + unsigned long irq_flags=0; int ret; spin_lock_irqsave(&mdp->lock, irq_flags); @@ -119,7 +155,7 @@ int disable_mdp_irq(struct mdp_info *mdp, uint32_t mask) static irqreturn_t mdp_isr(int irq, void *data) { uint32_t status; - unsigned long irq_flags; + unsigned long irq_flags=0; struct mdp_info *mdp = data; int i; @@ -130,8 +166,19 @@ static irqreturn_t mdp_isr(int irq, void *data) // pr_info("%s: status=%08x (irq_mask=%08x)\n", __func__, status, // mdp_irq_mask); - status &= mdp_irq_mask; + if (mdp_dma_timer_enable) { + del_timer_sync(&mdp->dma_timer); + mdp_dma_timer_enable = 0; + } + + status &= mdp_irq_mask; +#ifdef CONFIG_MSM_MDP40 + if (mdp->mdp_dev.overrides & MSM_MDP4_MDDI_DMA_SWITCH) { + if(status && mdp->out_if[MSM_MDDI_PMDH_INTERFACE].dma_cb != NULL) + status |= (INTR_OVERLAY0_DONE | MDP_DMA_S_DONE); + } +#endif for (i = 0; i < MSM_MDP_NUM_INTERFACES; ++i) { struct mdp_out_interface *out_if = &mdp->out_if[i]; if (status & out_if->dma_mask) { @@ -147,8 +194,9 @@ static irqreturn_t mdp_isr(int irq, void *data) } } +#ifndef CONFIG_MSM_MDP40 mdp_ppp_handle_isr(mdp, status); - +#endif if (status) locked_disable_mdp_irq(mdp, status); @@ -156,10 +204,43 @@ static irqreturn_t mdp_isr(int irq, void *data) return IRQ_HANDLED; } +static void mdp_do_dma_timer(unsigned long data) +{ + uint32_t status; + struct mdp_info *mdp = (struct mdp_info *) data; + unsigned long irq_flags=0; + int i; + + spin_lock_irqsave(&mdp->lock, irq_flags); + + status = mdp_readl(mdp, MDP_INTR_STATUS); + mdp_writel(mdp, mdp_irq_mask, MDP_INTR_CLEAR); + + for (i = 0; i < MSM_MDP_NUM_INTERFACES; ++i) { + struct mdp_out_interface *out_if = &mdp->out_if[i]; + if (mdp_irq_mask & out_if->dma_mask) { + if (out_if->dma_cb) { + out_if->dma_cb->func(out_if->dma_cb); + out_if->dma_cb = NULL; + } + wake_up(&out_if->dma_waitqueue); + } + if (mdp_irq_mask & out_if->irq_mask) { + out_if->irq_cb->func(out_if->irq_cb); + out_if->irq_cb = NULL; + } + } + + locked_disable_mdp_irq(mdp, mdp_irq_mask); + + spin_unlock_irqrestore(&mdp->lock, irq_flags); + +} + static uint32_t mdp_check_mask(struct mdp_info *mdp, uint32_t mask) { uint32_t ret; - unsigned long irq_flags; + unsigned long irq_flags=0; spin_lock_irqsave(&mdp->lock, irq_flags); ret = mdp_irq_mask & mask; @@ -167,32 +248,41 @@ static uint32_t mdp_check_mask(struct mdp_info *mdp, uint32_t mask) return ret; } +void mdp_dump_blit(struct mdp_blit_req *req) +{ + pr_info("%s: src: w=%d h=%d f=0x%x offs=0x%x mem_id=%d\n", __func__, + req->src.width, req->src.height, req->src.format, + req->src.offset, req->src.memory_id); + pr_info("%s: dst: w=%d h=%d f=0x%x offs=0x%x mem_id=%d\n", __func__, + req->dst.width, req->dst.height, req->dst.format, + req->dst.offset, req->dst.memory_id); + pr_info("%s: src_rect: x=%d y=%d w=%d h=%d\n", __func__, + req->src_rect.x, req->src_rect.y, req->src_rect.w, + req->src_rect.h); + pr_info("%s: dst_rect: x=%d y=%d w=%d h=%d\n", __func__, + req->dst_rect.x, req->dst_rect.y, req->dst_rect.w, + req->dst_rect.h); + pr_info("%s: alpha=0x%08x\n", __func__, req->alpha); + pr_info("%s: transp_max=0x%08x\n", __func__, req->transp_mask); + pr_info("%s: flags=%08x\n", __func__, req->flags); +} + int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq) { int ret = 0; - unsigned long irq_flags; + unsigned long irq_flags=0; // pr_info("%s: WAITING for 0x%x\n", __func__, mask); - wait_event_timeout(*wq, !mdp_check_mask(mdp, mask), - msecs_to_jiffies(1000)); + wait_event_timeout(*wq, !mdp_check_mask(mdp, mask), HZ); spin_lock_irqsave(&mdp->lock, irq_flags); if (mdp_irq_mask & mask) { + locked_disable_mdp_irq(mdp, mask); pr_warning("%s: timeout waiting for mdp to complete 0x%x\n", __func__, mask); - pr_info("GLBL_CLK_ENA: %08X\n", - readl(MSM_CLK_CTL_BASE + 0x0000)); - pr_info("GLBL_CLK_STATE: %08X\n", - readl(MSM_CLK_CTL_BASE + 0x0004)); - pr_info("GLBL_SLEEP_EN: %08X\n", - readl(MSM_CLK_CTL_BASE + 0x001C)); - pr_info("GLBL_CLK_ENA_2: %08X\n", - readl(MSM_CLK_CTL_BASE + 0x0220)); - pr_info("GLBL_CLK_STATE_2: %08X\n", - readl(MSM_CLK_CTL_BASE + 0x0224)); - pr_info("GLBL_CLK_SLEEP_EN_2: %08X\n", - readl(MSM_CLK_CTL_BASE + 0x023C)); - locked_disable_mdp_irq(mdp, mask); + if(timeout_req) + mdp_dump_blit(timeout_req); + ret = -ETIMEDOUT; } else { // pr_info("%s: SUCCESS waiting for 0x%x\n", __func__, mask); @@ -214,6 +304,7 @@ static void mdp_dma_wait(struct mdp_device *mdp_dev, int interface) case MSM_MDDI_PMDH_INTERFACE: case MSM_MDDI_EMDH_INTERFACE: case MSM_LCDC_INTERFACE: + case MSM_TV_INTERFACE: BUG_ON(!mdp->out_if[interface].registered); mask = mdp->out_if[interface].dma_mask; wq = &mdp->out_if[interface].dma_waitqueue; @@ -234,8 +325,167 @@ static void mdp_dma_wait(struct mdp_device *mdp_dev, int interface) BUG(); } } +/* +static int mdp_ppp_wait(struct mdp_info *mdp) +{ + return mdp_wait(mdp, DL0_ROI_DONE, &mdp_ppp_waitqueue); +} +*/ +#ifndef CONFIG_MSM_MDP40 +static void mdp_dmas_to_mddi(void *priv, uint32_t addr, uint32_t stride, + uint32_t width, uint32_t height, uint32_t x, uint32_t y) +{ + struct mdp_info *mdp = priv; + uint32_t dma2_cfg; + uint32_t video_packet_parameter = 0; + uint16_t ld_param = 1; + + dma2_cfg = DMA_PACK_TIGHT | + DMA_PACK_ALIGN_LSB | + DMA_OUT_SEL_AHB | + DMA_IBUF_NONCONTIGUOUS; + + dma2_cfg |= mdp->dma_format; + +#if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30 + if (mdp->dma_format == DMA_IBUF_FORMAT_RGB888_OR_ARGB8888) +#else + if (mdp->dma_format == DMA_IBUF_FORMAT_XRGB8888) +#endif + dma2_cfg |= DMA_PACK_PATTERN_BGR; + else + dma2_cfg |= DMA_PACK_PATTERN_RGB; -static void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride, + dma2_cfg |= DMA_OUT_SEL_MDDI; + + dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY; + + dma2_cfg |= DMA_DITHER_EN; + + if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB565) { + dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB565; + } else if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB666) { + dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB666; + } + + /* setup size, address, and stride */ + mdp_writel(mdp, (height << 16) | (width), MDP_DMA_S_SIZE); + mdp_writel(mdp, addr, MDP_DMA_S_IBUF_ADDR); + mdp_writel(mdp, stride, MDP_DMA_S_IBUF_Y_STRIDE); + + /* set y & x offset and MDDI transaction parameters */ + mdp_writel(mdp, (y << 16) | (x), MDP_DMA_S_OUT_XY); + mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL); + if (mdp->mdp_dev.overrides & MSM_MDP_PANEL_IGNORE_PIXEL_DATA) { + mdp_writel(mdp, (video_packet_parameter << 16) | 0xE3, + MDP_MDDI_PARAM); + } + else { + mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM, + MDP_MDDI_PARAM); + } + + mdp_writel(mdp, dma2_cfg, MDP_DMA_S_CONFIG); + mdp_writel(mdp, 0, MDP_DMA_S_START); +} + +static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, + uint32_t width, uint32_t height, uint32_t x, + uint32_t y) +{ + struct mdp_info *mdp = priv; + uint32_t dma2_cfg = 0; + uint32_t video_packet_parameter = 0; + uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */ + +#if !defined(CONFIG_MSM_MDP30) + dma2_cfg = DMA_PACK_TIGHT | + DMA_PACK_ALIGN_LSB | + DMA_OUT_SEL_AHB | + DMA_IBUF_NONCONTIGUOUS; + +#endif + dma2_cfg |= mdp->dma_format; + +#if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30 + if (mdp->dma_format == DMA_IBUF_FORMAT_RGB888_OR_ARGB8888) +#else + if (mdp->dma_format == DMA_IBUF_FORMAT_XRGB8888) +#endif + dma2_cfg |= DMA_PACK_PATTERN_BGR; + else + dma2_cfg |= DMA_PACK_PATTERN_RGB; + + dma2_cfg |= DMA_OUT_SEL_MDDI; + + dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY; + +#if !defined(CONFIG_MSM_MDP30) + dma2_cfg |= DMA_DITHER_EN; +#endif + + if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB565) { + dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB565; + } else if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB666) { + dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB666; + } + + +#if defined(CONFIG_MSM_MDP30) || defined(CONFIG_MSM_MDP302) + writel(height << 16 | width, mdp->base + 0x90004); + writel(addr, mdp->base + 0x90008); + writel(stride, mdp->base + 0x9000c); + + /* set y & x offset and MDDI transaction parameters */ + writel(y << 16 | x, mdp->base + 0x90010); + writel(ld_param, mdp->base + 0x00090); + writel((video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM, + mdp->base + 0x00094); + + writel(dma2_cfg, mdp->base + 0x90000); + + /* start DMA2 */ + writel(0, mdp->base + 0x0044); +#elif defined(CONFIG_MSM_MDP22) + /* setup size, address, and stride */ + mdp_writel(mdp, (height << 16) | (width), + MDP_CMD_DEBUG_ACCESS_BASE + 0x0184); + mdp_writel(mdp, addr, MDP_CMD_DEBUG_ACCESS_BASE + 0x0188); + mdp_writel(mdp, stride, MDP_CMD_DEBUG_ACCESS_BASE + 0x018C); + + /* set y & x offset and MDDI transaction parameters */ + mdp_writel(mdp, (y << 16) | (x), MDP_CMD_DEBUG_ACCESS_BASE + 0x0194); + mdp_writel(mdp, ld_param, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0); + mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM, + MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4); + + mdp_writel(mdp, dma2_cfg, MDP_CMD_DEBUG_ACCESS_BASE + 0x0180); + + /* start DMA2 */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0044); +#else + /* setup size, address, and stride */ + mdp_writel(mdp, (height << 16) | (width), MDP_DMA_P_SIZE); + mdp_writel(mdp, addr, MDP_DMA_P_IBUF_ADDR); + mdp_writel(mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE); + + /* set y & x offset and MDDI transaction parameters */ + mdp_writel(mdp, (y << 16) | (x), MDP_DMA_P_OUT_XY); + mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL); + mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM, + MDP_MDDI_PARAM); + + mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG); + mdp_writel(mdp, 0, MDP_DMA_P_START); +#endif +} +#endif /* ifndef CONFIG_MSM_MDP40 */ + +void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride, uint32_t width, uint32_t height, uint32_t x, uint32_t y, struct msmfb_callback *callback, int interface) { @@ -243,7 +493,7 @@ static void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride, struct mdp_out_interface *out_if; unsigned long flags; - if (interface < 0 || interface > MSM_MDP_NUM_INTERFACES || + if (interface < 0 || interface >= MSM_MDP_NUM_INTERFACES || !mdp->out_if[interface].registered) { pr_err("%s: Unknown interface: %d\n", __func__, interface); BUG(); @@ -252,29 +502,35 @@ static void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride, spin_lock_irqsave(&mdp->lock, flags); if (locked_enable_mdp_irq(mdp, out_if->dma_mask)) { + /* something wrong in dma, workaround it */ + mdp_dma_timer_enable = 1; pr_err("%s: busy\n", __func__); - goto done; } out_if->dma_cb = callback; out_if->dma_start(out_if->priv, addr, stride, width, height, x, y); -done: + + if (mdp_dma_timer_enable) + mod_timer(&mdp->dma_timer, + jiffies + msecs_to_jiffies(17)); + spin_unlock_irqrestore(&mdp->lock, flags); } -void mdp_configure_dma_format(struct mdp_device *mdp_dev) + +void mdp_configure_dma(struct mdp_device *mdp_dev) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); uint32_t dma_cfg; - if (!mdp->dma_format_dirty) + if (!mdp->dma_config_dirty) return; dma_cfg = mdp_readl(mdp, MDP_DMA_P_CONFIG); dma_cfg &= ~DMA_IBUF_FORMAT_MASK; dma_cfg &= ~DMA_PACK_PATTERN_MASK; dma_cfg |= (mdp->dma_format | mdp->dma_pack_pattern); mdp_writel(mdp, dma_cfg, MDP_DMA_P_CONFIG); - mdp->dma_format_dirty = false; + mdp->dma_config_dirty = false; return; } @@ -292,17 +548,24 @@ int mdp_check_output_format(struct mdp_device *mdp_dev, int bpp) return 0; } -static int mdp_set_output_format(struct mdp_device *mdp_dev, int bpp) +void mdp_set_panel_size(struct mdp_device *mdp_dev, int width, int height) +{ + struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + mdp->mdp_dev.width = width; + mdp->mdp_dev.height = height; +} + +int mdp_set_output_format(struct mdp_device *mdp_dev, int bpp) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); - uint32_t format, pack_pattern; + uint32_t format, pack_pattern = DMA_PACK_PATTERN_RGB; switch (bpp) { case 16: format = DMA_IBUF_FORMAT_RGB565; pack_pattern = DMA_PACK_PATTERN_RGB; break; -#ifdef CONFIG_MSM_MDP22 +#if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30 case 24: case 32: format = DMA_IBUF_FORMAT_RGB888_OR_ARGB8888; @@ -320,23 +583,79 @@ static int mdp_set_output_format(struct mdp_device *mdp_dev, int bpp) default: return -EINVAL; } - if (format != mdp->dma_format || - pack_pattern != mdp->dma_pack_pattern) { + if (format != mdp->dma_format || pack_pattern != mdp->dma_pack_pattern) { mdp->dma_format = format; mdp->dma_pack_pattern = pack_pattern; - mdp->dma_format_dirty = true; + mdp->dma_config_dirty = true; } return 0; } +/* +static void dump_req(struct mdp_blit_req *req, + unsigned long src_start, unsigned long src_len, + unsigned long dst_start, unsigned long dst_len) +{ + pr_err("flags: 0x%x\n", req->flags); + pr_err("src_start: 0x%08lx\n", src_start); + pr_err("src_len: 0x%08lx\n", src_len); + pr_err("src.offset: 0x%x\n", req->src.offset); + pr_err("src.format: 0x%x\n", req->src.format); + pr_err("src.width: %d\n", req->src.width); + pr_err("src.height: %d\n", req->src.height); + pr_err("src_rect.x: %d\n", req->src_rect.x); + pr_err("src_rect.y: %d\n", req->src_rect.y); + pr_err("src_rect.w: %d\n", req->src_rect.w); + pr_err("src_rect.h: %d\n", req->src_rect.h); + + pr_err("dst_start: 0x%08lx\n", dst_start); + pr_err("dst_len: 0x%08lx\n", dst_len); + pr_err("dst.offset: 0x%x\n", req->dst.offset); + pr_err("dst.format: 0x%x\n", req->dst.format); + pr_err("dst.width: %d\n", req->dst.width); + pr_err("dst.height: %d\n", req->dst.height); + pr_err("dst_rect.x: %d\n", req->dst_rect.x); + pr_err("dst_rect.y: %d\n", req->dst_rect.y); + pr_err("dst_rect.w: %d\n", req->dst_rect.w); + pr_err("dst_rect.h: %d\n", req->dst_rect.h); +} +int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req, + struct file *src_file, unsigned long src_start, unsigned long src_len, + struct file *dst_file, unsigned long dst_start, unsigned long dst_len) +{ + int ret; + enable_mdp_irq(mdp, DL0_ROI_DONE); + ret = mdp_ppp_blit(mdp, req, + src_file, src_start, src_len, + dst_file, dst_start, dst_len); + if (unlikely(ret)) { + disable_mdp_irq(mdp, DL0_ROI_DONE); + return ret; + } + ret = mdp_ppp_wait(mdp); + if (unlikely(ret)) { + printk(KERN_ERR "%s: failed!\n", __func__); + pr_err("original request:\n"); + dump_req(mdp->req, src_start, src_len, dst_start, dst_len); + pr_err("dead request:\n"); + dump_req(req, src_start, src_len, dst_start, dst_len); + BUG(); + return ret; + } + return 0; +} +*/ int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb, struct mdp_blit_req *req) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + timeout_req = req; return mdp_ppp_blit(mdp, fb, req); } + + void mdp_set_grp_disp(struct mdp_device *mdp_dev, unsigned disp_id) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); @@ -351,7 +670,7 @@ int mdp_out_if_register(struct mdp_device *mdp_dev, int interface, mdp_dma_start_func_t dma_start) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); - unsigned long flags; + unsigned long flags=0; int ret = 0; if (interface < 0 || interface >= MSM_MDP_NUM_INTERFACES) { @@ -384,7 +703,7 @@ int mdp_out_if_req_irq(struct mdp_device *mdp_dev, int interface, uint32_t mask, struct msmfb_callback *cb) { struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); - unsigned long flags; + unsigned long flags=0; int ret = 0; if (interface < 0 || interface >= MSM_MDP_NUM_INTERFACES) { @@ -430,8 +749,9 @@ int register_mdp_client(struct class_interface *cint) int mdp_probe(struct platform_device *pdev) { struct resource *resource; - int ret; + int ret = -EINVAL; struct mdp_info *mdp; + struct msm_mdp_platform_data *pdata = pdev->dev.platform_data; resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!resource) { @@ -463,13 +783,56 @@ int mdp_probe(struct platform_device *pdev) mdp->mdp_dev.dma = mdp_dma; mdp->mdp_dev.dma_wait = mdp_dma_wait; mdp->mdp_dev.blit = mdp_blit; +#ifdef CONFIG_FB_MSM_OVERLAY + mdp->mdp_dev.overlay_get = mdp4_overlay_get; + mdp->mdp_dev.overlay_set = mdp4_overlay_set; + mdp->mdp_dev.overlay_unset = mdp4_overlay_unset; + mdp->mdp_dev.overlay_play = mdp4_overlay_play; +#endif mdp->mdp_dev.set_grp_disp = mdp_set_grp_disp; mdp->mdp_dev.set_output_format = mdp_set_output_format; + mdp->mdp_dev.set_panel_size = mdp_set_panel_size; mdp->mdp_dev.check_output_format = mdp_check_output_format; + mdp->mdp_dev.configure_dma = mdp_configure_dma; mdp->enable_irq = enable_mdp_irq; mdp->disable_irq = disable_mdp_irq; + if (pdata == NULL || pdata->overrides == 0) + mdp->mdp_dev.overrides = 0; + else if(pdata->overrides) + mdp->mdp_dev.overrides = pdata->overrides; + + if (pdata == NULL || pdata->color_format == 0) + mdp->mdp_dev.color_format = MSM_MDP_OUT_IF_FMT_RGB565; + else if(pdata->color_format) + mdp->mdp_dev.color_format = pdata->color_format; + +#ifdef CONFIG_MSM_MDP40 + if (mdp->mdp_dev.overrides & MSM_MDP4_MDDI_DMA_SWITCH) { + ret = mdp_out_if_register(&mdp->mdp_dev, + MSM_MDDI_PMDH_INTERFACE, mdp, INTR_OVERLAY0_DONE + | MDP_DMA_S_DONE, mdp4_mddi_overlay); + } else { + ret = mdp_out_if_register(&mdp->mdp_dev, + MSM_MDDI_PMDH_INTERFACE, mdp, INTR_OVERLAY0_DONE, + mdp4_mddi_overlay); + } +#else + if (pdata == NULL || pdata->dma_channel == MDP_DMA_P) { + ret = mdp_out_if_register(&mdp->mdp_dev, + MSM_MDDI_PMDH_INTERFACE, mdp, MDP_DMA_P_DONE, + mdp_dma_to_mddi); + } else if (pdata->dma_channel == MDP_DMA_S) { + ret = mdp_out_if_register(&mdp->mdp_dev, + MSM_MDDI_PMDH_INTERFACE, mdp, MDP_DMA_S_DONE, + mdp_dmas_to_mddi); + } +#endif + + if (ret) + goto error_mddi_pmdh_register; + mdp->clk = clk_get(&pdev->dev, "mdp_clk"); if (IS_ERR(mdp->clk)) { printk(KERN_INFO "mdp: failed to get mdp clk"); @@ -477,17 +840,12 @@ int mdp_probe(struct platform_device *pdev) goto error_get_mdp_clk; } - mdp->pclk = clk_get(&pdev->dev, "mdp_pclk"); - if (IS_ERR(mdp->pclk)) - mdp->pclk = NULL; - - mdp->ebi1_clk = clk_get(NULL, "ebi1_clk"); - if (IS_ERR(mdp->ebi1_clk)) { - pr_err("mdp: failed to get ebi1 clk\n"); - ret = PTR_ERR(mdp->ebi1_clk); - goto error_get_ebi1_clk; - } - + mdp->ebi1_clk = clk_get(NULL, "ebi1_clk"); + if (IS_ERR(mdp->ebi1_clk)) { + pr_err("mdp: failed to get ebi1 clk\n"); + ret = PTR_ERR(mdp->ebi1_clk); + goto error_get_ebi1_clk; + } ret = request_irq(mdp->irq, mdp_isr, IRQF_DISABLED, "msm_mdp", mdp); if (ret) @@ -495,10 +853,29 @@ int mdp_probe(struct platform_device *pdev) disable_irq(mdp->irq); clk_enable(mdp->clk); - if (mdp->pclk) - clk_enable(mdp->pclk); + mdp_clk_to_disable_later = mdp->clk; + +#ifdef CONFIG_MSM_MDP40 + //MDP_DISP_INTF_SEL + if (mdp_readl(mdp, 0xc0000)) + mdp_writel(mdp, 0x8, 0x0038); + else + mdp_writel(mdp, 0xa, 0x0038); //mddi + //FIXME: should select mddi or lcdc interface + //mdp_writel(mdp, 0x8, 0x0038); //lcdc +#endif + +#ifdef CONFIG_MSM_MDP40 + mdp4_hw_init(mdp); +#else mdp_hw_init(mdp); +#endif +#if defined CONFIG_MSM_MDP302 + /* enable the tearing check in MDP */ + if(pdata != NULL && pdata->tearing_check) + mdp_check_tearing(mdp, pdata); +#endif /* register mdp device */ mdp->mdp_dev.dev.parent = &pdev->dev; mdp->mdp_dev.dev.class = mdp_class; @@ -512,24 +889,22 @@ int mdp_probe(struct platform_device *pdev) if (ret) goto error_device_register; - the_mdp = mdp; + setup_timer(&mdp->standby_timer, mdp_do_standby_timer, (unsigned long )mdp); + setup_timer(&mdp->dma_timer, mdp_do_dma_timer, (unsigned long )mdp); + pr_info("%s: initialized\n", __func__); return 0; error_device_register: - if (mdp->pclk) - clk_disable(mdp->pclk); - clk_disable(mdp->clk); free_irq(mdp->irq, mdp); error_request_irq: clk_put(mdp->ebi1_clk); error_get_ebi1_clk: - if (mdp->pclk) - clk_put(mdp->pclk); clk_put(mdp->clk); error_get_mdp_clk: +error_mddi_pmdh_register: iounmap(mdp->base); error_ioremap: error_get_irq: @@ -544,12 +919,8 @@ static struct platform_driver msm_mdp_driver = { static int __init mdp_lateinit(void) { - struct mdp_info *mdp = the_mdp; - if (the_mdp) { - if (mdp->pclk) - clk_disable(mdp->pclk); - clk_disable(mdp->clk); - } + if (mdp_clk_to_disable_later) + clk_disable(mdp_clk_to_disable_later); return 0; } diff --git a/drivers/video/msm/mdp_hw.h b/drivers/video/msm/mdp_hw.h index 1bd670b8691af..d5c6ddc27a92d 100644 --- a/drivers/video/msm/mdp_hw.h +++ b/drivers/video/msm/mdp_hw.h @@ -52,14 +52,56 @@ struct mdp_info { struct mdp_out_interface out_if[MSM_MDP_NUM_INTERFACES]; int dma_format; int dma_pack_pattern; - bool dma_format_dirty; + bool dma_config_dirty; struct mdp_blit_req *req; + uint32_t state; + struct timer_list standby_timer; + struct timer_list dma_timer; int (*enable_irq)(struct mdp_info *mdp, uint32_t mask); int (*disable_irq)(struct mdp_info *mdp, uint32_t mask); }; -void mdp_configure_dma_format(struct mdp_device *mdp_dev); +struct mdp_lcdc_info { + struct mdp_info *mdp; + struct clk *mdp_clk; + struct clk *mdp_pclk; + struct clk *pclk; + struct clk *pad_pclk; + struct msm_panel_data fb_panel_data; + struct platform_device fb_pdev; + struct msm_lcdc_platform_data *pdata; + uint32_t fb_start; + + struct msmfb_callback frame_start_cb; + wait_queue_head_t vsync_waitq; + int got_vsync; + unsigned color_format; + struct { + uint32_t clk_rate; + uint32_t hsync_ctl; + uint32_t vsync_period; + uint32_t vsync_pulse_width; + uint32_t display_hctl; + uint32_t display_vstart; + uint32_t display_vend; + uint32_t hsync_skew; + uint32_t polarity; + } parms; + atomic_t blank_count; + struct mutex blank_lock; +}; + +struct panel_icm_info { + bool icm_mode; + bool icm_doable; + bool clock_enabled; + int panel_update; + struct mutex icm_lock; + struct mdp_lcdc_info *lcdc; + spinlock_t lock; + void (*force_leave)(void); +}; extern int mdp_out_if_register(struct mdp_device *mdp_dev, int interface, void *private_data, uint32_t dma_mask, @@ -71,56 +113,77 @@ extern int mdp_out_if_req_irq(struct mdp_device *mdp_dev, int interface, struct mdp_blit_req; struct mdp_device; +void mdp_ppp_dump_debug(const struct mdp_info *mdp); int mdp_hw_init(struct mdp_info *mdp); - +void mdp_check_tearing(struct mdp_info *mdp, struct msm_mdp_platform_data *pdata); +void mdp_dump_blit(struct mdp_blit_req *req); int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #define mdp_writel(mdp, value, offset) writel(value, mdp->base + offset) #define mdp_readl(mdp, offset) readl(mdp->base + offset) +#define panel_to_lcdc(p) container_of((p), struct mdp_lcdc_info, fb_panel_data) + +/* define mdp state for multi purpose */ +#define MDP_STATE_STANDBY (1 << 0) -#if defined(CONFIG_MSM_MDP22) -#define MDP_SYNC_CONFIG_0 (0x00000) -#define MDP_SYNC_CONFIG_1 (0x00004) -#define MDP_SYNC_CONFIG_2 (0x00008) -#define MDP_SYNC_STATUS_0 (0x0000c) -#define MDP_SYNC_STATUS_1 (0x00010) -#define MDP_SYNC_STATUS_2 (0x00014) -#define MDP_SYNC_THRESH_0 (0x00018) -#define MDP_SYNC_THRESH_1 (0x0001c) + +#ifdef CONFIG_MSM_MDP302 +#define MDP_SYNC_CONFIG_0 ( 0x00300) +#define MDP_SYNC_CONFIG_1 ( 0x00304) +#define MDP_SYNC_CONFIG_2 ( 0x00308) +#else +#define MDP_SYNC_CONFIG_0 ( 0x00000) +#define MDP_SYNC_CONFIG_1 ( 0x00004) +#define MDP_SYNC_CONFIG_2 ( 0x00008) #endif -#if defined(CONFIG_MSM_MDP40) -#define MDP_DISP_INTF_SEL (0x00038) -#define MDP_MAX_RD_PENDING_CMD_CONFIG (0x0004c) -#define MDP_INTR_ENABLE (0x00050) -#define MDP_INTR_STATUS (0x00054) -#define MDP_INTR_CLEAR (0x00058) -#define MDP_EBI2_LCD0 (0x00060) -#define MDP_EBI2_LCD1 (0x00064) -#define MDP_EBI2_PORTMAP_MODE (0x00070) +#define MDP_SYNC_STATUS_0 ( 0x0000c) +#define MDP_SYNC_STATUS_1 ( 0x00010) +#define MDP_SYNC_STATUS_2 ( 0x00014) + +#ifdef CONFIG_MSM_MDP302 +#define MDP_SYNC_THRESH_0 ( 0x00200) +#define MDP_SYNC_THRESH_1 ( 0x00204) #else -#define MDP_INTR_ENABLE (0x00020) -#define MDP_INTR_STATUS (0x00024) -#define MDP_INTR_CLEAR (0x00028) -#define MDP_DISPLAY0_START (0x00030) -#define MDP_DISPLAY1_START (0x00034) -#define MDP_DISPLAY_STATUS (0x00038) -#define MDP_EBI2_LCD0 (0x0003c) -#define MDP_EBI2_LCD1 (0x00040) -#define MDP_EBI2_PORTMAP_MODE (0x0005c) +#define MDP_SYNC_THRESH_0 ( 0x00018) +#define MDP_SYNC_THRESH_1 ( 0x0001c) #endif +#ifdef CONFIG_MSM_MDP40 +#define MDP_INTR_ENABLE ( 0x0050) +#define MDP_INTR_STATUS ( 0x0054) +#define MDP_INTR_CLEAR ( 0x0058) +#define MDP_EBI2_LCD0 ( 0x0060) +#define MDP_EBI2_LCD1 ( 0x0064) +#define MDP_EBI2_PORTMAP_MODE ( 0x0070) + +#define MDP_DMA_P_HIST_INTR_STATUS ( 0x95014) +#define MDP_DMA_P_HIST_INTR_CLEAR ( 0x95018) +#define MDP_DMA_P_HIST_INTR_ENABLE ( 0x9501C) +#else +#define MDP_INTR_ENABLE ( 0x00020) +#define MDP_INTR_STATUS ( 0x00024) +#define MDP_INTR_CLEAR ( 0x00028) +#define MDP_EBI2_LCD0 ( 0x0003c) +#define MDP_EBI2_LCD1 ( 0x00040) +#define MDP_EBI2_PORTMAP_MODE ( 0x0005c) +#endif +#define MDP_DISPLAY0_START ( 0x00030) +#define MDP_DISPLAY1_START ( 0x00034) +#define MDP_DISPLAY_STATUS ( 0x00038) +/* CONFIG_MSM_MDP302 */ +#define MDP_TEAR_CHECK_EN ( 0x0020c) +#define MDP_PRIM_START_POS ( 0x00210) -#if defined(CONFIG_MSM_MDP22) +#ifndef CONFIG_MSM_MDP31 #define MDP_DISPLAY0_ADDR (0x00054) #define MDP_DISPLAY1_ADDR (0x00058) #define MDP_PPP_CMD_MODE (0x00060) -#elif defined(CONFIG_MSM_MDP31) +#else #define MDP_DISPLAY0_ADDR (0x10000) #define MDP_DISPLAY1_ADDR (0x10004) #define MDP_PPP_CMD_MODE (0x10060) #endif -#if defined(CONFIG_MSM_MDP22) #define MDP_TV_OUT_STATUS (0x00064) #define MDP_HW_VERSION (0x00070) #define MDP_SW_RESET (0x00074) @@ -130,13 +193,10 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #define MDP_SECONDARY_VSYNC_OUT_CTRL (0x00084) #define MDP_EXTERNAL_VSYNC_OUT_CTRL (0x00088) #define MDP_VSYNC_CTRL (0x0008c) -#endif - -#if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP40) #define MDP_MDDI_PARAM_WR_SEL (0x00090) #define MDP_MDDI_PARAM (0x00094) #define MDP_MDDI_DATA_XFR (0x00098) -#endif + #if defined(CONFIG_MSM_MDP40) #define MDP_LAYERMIXER_IN_CFG (0x10100) @@ -284,45 +344,57 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #define MDP_TEST_CAPTURED_DCLK (0xd0210) #define MDP_TEST_MISR_CAPT_VAL_DCLK (0xd0214) +#ifdef CONFIG_MSM_MDP40 +#define MDP_DMA_P_START (0x000c) +#else +#define MDP_DMA_P_START (0x00044) +#endif #define MDP_DMA_P_CONFIG (0x90000) #define MDP_DMA_P_SIZE (0x90004) #define MDP_DMA_P_IBUF_ADDR (0x90008) #define MDP_DMA_P_IBUF_Y_STRIDE (0x9000c) #define MDP_DMA_P_OUT_XY (0x90010) - -#ifdef CONFIG_MSM_MDP40 -#define MDP_DMA_P_START (0x0000c) -#define MDP_DMA_P_FETCH_CFG (0x91004) -#define MDP_DMA_P_HIST_INTR_STATUS (0x95014) -#define MDP_DMA_P_HIST_INTR_CLEAR (0x95018) -#define MDP_DMA_P_HIST_INTR_ENABLE (0x9501C) -#else -#define MDP_DMA_P_START (0x00044) #define MDP_DMA_P_COLOR_CORRECT_CONFIG (0x90070) -#endif -#if defined(CONFIG_MSM_MDP31) -#define MDP_LCDC_BASE (0xe0000) -#elif defined(CONFIG_MSM_MDP40) -#define MDP_LCDC_BASE (0xc0000) -#endif +#define MDP_DMA_S_START (0x00048) +#define MDP_DMA_S_CONFIG (0xa0000) +#define MDP_DMA_S_SIZE (0xa0004) +#define MDP_DMA_S_IBUF_ADDR (0xa0008) +#define MDP_DMA_S_IBUF_Y_STRIDE (0xa000c) +#define MDP_DMA_S_OUT_XY (0xa0010) -#ifdef MDP_LCDC_BASE -#define MDP_LCDC_EN (MDP_LCDC_BASE + 0x00) -#define MDP_LCDC_HSYNC_CTL (MDP_LCDC_BASE + 0x04) -#define MDP_LCDC_VSYNC_PERIOD (MDP_LCDC_BASE + 0x08) -#define MDP_LCDC_VSYNC_PULSE_WIDTH (MDP_LCDC_BASE + 0x0c) -#define MDP_LCDC_DISPLAY_HCTL (MDP_LCDC_BASE + 0x10) -#define MDP_LCDC_DISPLAY_V_START (MDP_LCDC_BASE + 0x14) -#define MDP_LCDC_DISPLAY_V_END (MDP_LCDC_BASE + 0x18) -#define MDP_LCDC_ACTIVE_HCTL (MDP_LCDC_BASE + 0x1c) -#define MDP_LCDC_ACTIVE_V_START (MDP_LCDC_BASE + 0x20) -#define MDP_LCDC_ACTIVE_V_END (MDP_LCDC_BASE + 0x24) -#define MDP_LCDC_BORDER_CLR (MDP_LCDC_BASE + 0x28) -#define MDP_LCDC_UNDERFLOW_CTL (MDP_LCDC_BASE + 0x2c) -#define MDP_LCDC_HSYNC_SKEW (MDP_LCDC_BASE + 0x30) -#define MDP_LCDC_TEST_CTL (MDP_LCDC_BASE + 0x34) -#define MDP_LCDC_CTL_POLARITY (MDP_LCDC_BASE + 0x38) +#ifdef CONFIG_MSM_MDP40 +#define MDP_LCDC_EN (0xc0000) +#define MDP_LCDC_HSYNC_CTL (0xc0004) +#define MDP_LCDC_VSYNC_PERIOD (0xc0008) +#define MDP_LCDC_VSYNC_PULSE_WIDTH (0xc000c) +#define MDP_LCDC_DISPLAY_HCTL (0xc0010) +#define MDP_LCDC_DISPLAY_V_START (0xc0014) +#define MDP_LCDC_DISPLAY_V_END (0xc0018) +#define MDP_LCDC_ACTIVE_HCTL (0xc001c) +#define MDP_LCDC_ACTIVE_V_START (0xc0020) +#define MDP_LCDC_ACTIVE_V_END (0xc0024) +#define MDP_LCDC_BORDER_CLR (0xc0028) +#define MDP_LCDC_UNDERFLOW_CTL (0xc002c) +#define MDP_LCDC_HSYNC_SKEW (0xc0030) +#define MDP_LCDC_TEST_CTL (0xc0034) +#define MDP_LCDC_CTL_POLARITY (0xc0038) +#else +#define MDP_LCDC_EN (0xe0000) +#define MDP_LCDC_HSYNC_CTL (0xe0004) +#define MDP_LCDC_VSYNC_PERIOD (0xe0008) +#define MDP_LCDC_VSYNC_PULSE_WIDTH (0xe000c) +#define MDP_LCDC_DISPLAY_HCTL (0xe0010) +#define MDP_LCDC_DISPLAY_V_START (0xe0014) +#define MDP_LCDC_DISPLAY_V_END (0xe0018) +#define MDP_LCDC_ACTIVE_HCTL (0xe001c) +#define MDP_LCDC_ACTIVE_V_START (0xe0020) +#define MDP_LCDC_ACTIVE_V_END (0xe0024) +#define MDP_LCDC_BORDER_CLR (0xe0028) +#define MDP_LCDC_UNDERFLOW_CTL (0xe002c) +#define MDP_LCDC_HSYNC_SKEW (0xe0030) +#define MDP_LCDC_TEST_CTL (0xe0034) +#define MDP_LCDC_CTL_POLARITY (0xe0038) #endif #define MDP_PPP_SCALE_STATUS (0x50000) @@ -335,6 +407,7 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #define DL0_ROI_DONE (1<<0) #define TV_OUT_DMA3_DONE (1<<6) #define TV_ENC_UNDERRUN (1<<7) +#define TV_OUT_FRAME_START (1<<13) #if defined(CONFIG_MSM_MDP40) #define MDP_OVERLAYPROC0_DONE (1 << 0) @@ -343,12 +416,26 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); #define MDP_LCDC_FRAME_START (1 << 7) #elif defined(CONFIG_MSM_MDP22) #define MDP_DMA_P_DONE (1 << 2) -#elif defined(CONFIG_MSM_MDP31) +#define MDP_DMA_S_DONE (1 << 3) +#else /* CONFIG_MSM_MDP31 */ + +#ifdef CONFIG_MSM_MDP40 +#define MDP_DMA_P_DONE (1 << 4) +#else #define MDP_DMA_P_DONE (1 << 14) +#endif + +#define MDP_DMA_S_DONE (1 << 2) #define MDP_LCDC_UNDERFLOW (1 << 16) + +#ifdef CONFIG_MSM_MDP40 +#define MDP_LCDC_FRAME_START (1 << 7) +#else #define MDP_LCDC_FRAME_START (1 << 15) #endif +#endif + #define MDP_TOP_LUMA 16 #define MDP_TOP_CHROMA 0 #define MDP_BOTTOM_LUMA 19 @@ -372,9 +459,15 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq); (((a)<<(bit*3))|((x)<<(bit*2))|((y)<mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB565) + mdp_writel(mdp, (MDDI_VDO_PACKET_DESC_RGB565 << 16) | MDDI_VDO_PACKET_PRIM, + MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4); + else + mdp_writel(mdp, (MDDI_VDO_PACKET_DESC_RGB666 << 16) | MDDI_VDO_PACKET_PRIM, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4); - mdp_writel(mdp, dma2_cfg, MDP_CMD_DEBUG_ACCESS_BASE + 0x0180); /* start DMA2 */ @@ -73,8 +76,12 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, /* set y & x offset and MDDI transaction parameters */ mdp_writel(mdp, (y << 16) | (x), MDP_DMA_P_OUT_XY); mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL); - mdp_writel(mdp, (MDDI_VDO_PACKET_DESC << 16) | MDDI_VDO_PACKET_PRIM, - MDP_MDDI_PARAM); + if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB565) + mdp_writel(mdp, (MDDI_VDO_PACKET_DESC_RGB565 << 16) | MDDI_VDO_PACKET_PRIM, + MDP_MDDI_PARAM); + else + mdp_writel(mdp, (MDDI_VDO_PACKET_DESC_RGB666 << 16) | MDDI_VDO_PACKET_PRIM, + MDP_MDDI_PARAM); mdp_writel(mdp, 0x1, MDP_MDDI_DATA_XFR); mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG); @@ -82,17 +89,23 @@ static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride, #endif } #endif - +#if defined CONFIG_MSM_MDP302 +void mdp_check_tearing(struct mdp_info *mdp, struct msm_mdp_platform_data *pdata) +{ mdp_writel(mdp, pdata->sync_config, MDP_SYNC_CONFIG_0); + mdp_writel(mdp, 1, MDP_TEAR_CHECK_EN); + mdp_writel(mdp, pdata->sync_thresh, MDP_SYNC_THRESH_0); + mdp_writel(mdp, pdata->sync_start_pos, MDP_PRIM_START_POS); +} +#endif +#if 0 int mdp_hw_init(struct mdp_info *mdp) { int n; -#ifdef CONFIG_FB_MSM_MDDI n = mdp_out_if_register(&mdp->mdp_dev, MSM_MDDI_PMDH_INTERFACE, mdp, MDP_DMA_P_DONE, mdp_dma_to_mddi); if (n) return n; -#endif mdp_writel(mdp, 0, MDP_INTR_ENABLE); @@ -154,3 +167,76 @@ int mdp_hw_init(struct mdp_info *mdp) return 0; } +#endif + +int mdp_hw_init(struct mdp_info *mdp) +{ + int n; + int lcdc_enabled; + + mdp_irq_mask = 0; + + mdp_writel(mdp, 0, MDP_INTR_ENABLE); + + /* debug interface write access */ + mdp_writel(mdp, 1, 0x60); + mdp_writel(mdp, 1, MDP_EBI2_PORTMAP_MODE); + +#ifndef CONFIG_MSM_MDP22 + lcdc_enabled = mdp_readl(mdp, MDP_LCDC_EN); + /* disable lcdc */ + mdp_writel(mdp, 0, MDP_LCDC_EN); + /* enable auto clock gating for all blocks by default */ + mdp_writel(mdp, 0xffffffff, MDP_CGC_EN); + /* reset color/gamma correct parms */ + mdp_writel(mdp, 0, MDP_DMA_P_COLOR_CORRECT_CONFIG); +#endif + + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01f8); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01fc); + mdp_writel(mdp, 1, 0x60); + + for (n = 0; n < ARRAY_SIZE(csc_color_lut); n++) + mdp_writel(mdp, csc_color_lut[n].val, csc_color_lut[n].reg); + + /* clear up unused fg/main registers */ + /* comp.plane 2&3 ystride */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0120); + + /* unpacked pattern */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x012c); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0130); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0134); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0158); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x015c); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0160); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0170); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0174); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x017c); + + /* comp.plane 2 & 3 */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0114); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0118); + + /* clear unused bg registers */ + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01c8); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01d0); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01dc); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e0); + mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e4); + + for (n = 0; n < ARRAY_SIZE(csc_matrix_config_table); n++) + mdp_writel(mdp, csc_matrix_config_table[n].val, + csc_matrix_config_table[n].reg); + + mdp_ppp_init_scale(mdp); + +#ifndef CONFIG_MSM_MDP31 + mdp_writel(mdp, 0x04000400, MDP_COMMAND_CONFIG); +#endif +#ifndef CONFIG_MSM_MDP22 + if (lcdc_enabled) + mdp_writel(mdp, 1, MDP_LCDC_EN); +#endif + return 0; +} \ No newline at end of file diff --git a/drivers/video/msm/mdp_lcdc.c b/drivers/video/msm/mdp_lcdc.c index 119ceb4f417cc..210186fe5bc60 100644 --- a/drivers/video/msm/mdp_lcdc.c +++ b/drivers/video/msm/mdp_lcdc.c @@ -21,17 +21,34 @@ #include #include #include -#include #include #include +#include #include #include - +#include #include #include "mdp_hw.h" +#ifdef CONFIG_MSM_MDP40 +#include "mdp4.h" +#endif +#ifdef CONFIG_PANEL_SELF_REFRESH +#include +#endif +#if 0 +#define D(fmt, args...) printk(KERN_INFO "Dispaly: " fmt, ##args) +#else +#define D(fmt, args...) do {} while (0) +#endif + +#if defined(CONFIG_ARCH_MSM7227) +#define LCDC_MUX_CTL (MSM_TGPIO1_BASE + 0x278) +#endif + +<<<<<<< HEAD struct mdp_lcdc_info { struct mdp_info *mdp; struct clk *mdp_clk; @@ -59,10 +76,150 @@ struct mdp_lcdc_info { uint32_t polarity; } parms; }; +======= +>>>>>>> video: msm: Updated MDDI from HTC static struct mdp_device *mdp_dev; -#define panel_to_lcdc(p) container_of((p), struct mdp_lcdc_info, fb_panel_data) +#ifdef CONFIG_MSM_MDP40 +static struct mdp4_overlay_pipe *lcdc_pipe; +#endif + +#ifdef CONFIG_PANEL_SELF_REFRESH +#if 0 +#define ICM_DBG(s...) printk("[icm]" s) +#else +#define ICM_DBG(s...) do {} while (0) +#endif + +/* set the timeout to 200 milliseconds */ +#define PANEL_ENTER_IDLE_TIMEOUT HZ/5 +/* Afetr setting ICM=1, we need to keep sending the RGB signal more than 2-frame */ +#define PANEL_IDLE_STABLE_TIMEOUT 48 + +static struct task_struct *th_display; +struct panel_icm_info *panel_icm; +DECLARE_WAIT_QUEUE_HEAD(panel_update_wait_queue); +#endif + +#ifdef CONFIG_PANEL_SELF_REFRESH +static int icm_check_panel_update(void) +{ + int ret; + unsigned long irq_flags = 0; + + spin_lock_irqsave(&panel_icm->lock, irq_flags); + ret = panel_icm->panel_update; + spin_unlock_irqrestore(&panel_icm->lock, irq_flags); + return ret; +} + +static int icm_thread(void *data) +{ + struct mdp_lcdc_info *lcdc; + struct msm_lcdc_panel_ops *panel_ops; + int rc; + unsigned long irq_flags = 0; + + lcdc = data; + panel_ops = lcdc->pdata->panel_ops; + while (1) { + rc = wait_event_timeout(panel_update_wait_queue, icm_check_panel_update() == 1, PANEL_ENTER_IDLE_TIMEOUT); + ICM_DBG("ICM Thread:wake up rc=%d \n", rc); + mutex_lock(&panel_icm->icm_lock); + if (rc == 0 && icm_check_panel_update() != 1) {/* wait_timeout */ + ICM_DBG("EnterICM: icm_mode=%d icm_doable=%d \n", panel_icm->icm_mode, panel_icm->icm_doable); + if (panel_icm->icm_mode == false && panel_icm->icm_doable == true) { + + if (panel_ops->refresh_enable) + panel_ops->refresh_enable(panel_ops); + + panel_icm->icm_mode = true; + msleep(PANEL_IDLE_STABLE_TIMEOUT); + + mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); + clk_disable(lcdc->pad_pclk); + clk_disable(lcdc->pclk); + clk_disable(lcdc->mdp_clk); + panel_icm->clock_enabled = false; + pr_info("EnterICM: enter ICM MODE done!!!\n"); + } + } else {/* get update event, no timeout */ + ICM_DBG("Leave ICM: icm_mode=%d icm_doable=%d \n", panel_icm->icm_mode, panel_icm->icm_doable); + if (panel_icm->icm_mode == true && panel_icm->icm_doable == true) { + clk_enable(lcdc->mdp_clk); + clk_enable(lcdc->pclk); + clk_enable(lcdc->pad_pclk); + mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); + panel_icm->clock_enabled = true; + + if (panel_ops->refresh_disable) + panel_ops->refresh_disable(panel_ops); + + panel_icm->icm_mode = false; + pr_info("LeaveICM: leave ICM MODE done !!!\n"); + } + spin_lock_irqsave(&panel_icm->lock, irq_flags); + panel_icm->panel_update = 0; + spin_unlock_irqrestore(&panel_icm->lock, irq_flags); + } + mutex_unlock(&panel_icm->icm_lock); + } /* end while */ + return 0; +} + +static void icm_force_leave(void) +{ + struct msm_lcdc_panel_ops *panel_ops; + unsigned long irq_flags = 0; + + panel_ops = panel_icm->lcdc->pdata->panel_ops; + + mutex_lock(&panel_icm->icm_lock); + ICM_DBG("Force Leave ICM: icm_mode=%d icm_doable=%d \n", panel_icm->icm_mode, panel_icm->icm_doable); + if (panel_icm->icm_mode == true) { + clk_enable(panel_icm->lcdc->mdp_clk); + clk_enable(panel_icm->lcdc->pclk); + clk_enable(panel_icm->lcdc->pad_pclk); + mdp_writel(panel_icm->lcdc->mdp, 1, MDP_LCDC_EN); + panel_icm->clock_enabled = true; + if (panel_ops->refresh_disable) + panel_ops->refresh_disable(panel_ops); + panel_icm->icm_mode = false; + panel_icm->icm_doable = true; + pr_info("ForceLeaveICM: leave ICM MODE done !!!\n"); + } + spin_lock_irqsave(&panel_icm->lock, irq_flags); + panel_icm->panel_update = 0; + spin_unlock_irqrestore(&panel_icm->lock, irq_flags); + mutex_unlock(&panel_icm->icm_lock); +} + +static int icm_init(struct mdp_lcdc_info *lcdc) +{ + int ret = 0; + + /* init panel_icm_info */ + panel_icm = kzalloc(sizeof(struct panel_icm_info), GFP_KERNEL); + if (!panel_icm) + return -ENOMEM; + panel_icm->icm_doable = 1; + panel_icm->clock_enabled = true; + panel_icm->lcdc = lcdc; + panel_icm->force_leave = icm_force_leave; + mutex_init(&panel_icm->icm_lock); + th_display = kthread_run(icm_thread, lcdc, "panel-enterIdle"); + if (IS_ERR(th_display)) { + ret = PTR_ERR(th_display); + pr_err("%s: panel_icm_thread create fail:%d!!!\n", __func__, ret); + goto error_create_thread; + } + return ret; +error_create_thread: + kfree(panel_icm); + return ret; +} +#endif static int lcdc_unblank(struct msm_panel_data *fb_panel) { @@ -70,7 +227,9 @@ static int lcdc_unblank(struct msm_panel_data *fb_panel) struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; pr_info("%s: ()\n", __func__); - panel_ops->unblank(panel_ops); + + if (panel_ops->unblank) + panel_ops->unblank(panel_ops); return 0; } @@ -81,23 +240,70 @@ static int lcdc_blank(struct msm_panel_data *fb_panel) struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; pr_info("%s: ()\n", __func__); - panel_ops->blank(panel_ops); + + if (panel_ops->blank) + panel_ops->blank(panel_ops); return 0; } +static int lcdc_shutdown(struct msm_panel_data *fb_panel) +{ + struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; + + pr_info("%s: ()\n", __func__); + + if (panel_ops->shutdown) + panel_ops->shutdown(panel_ops); + + return 0; +} + static int lcdc_suspend(struct msm_panel_data *fb_panel) { struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; pr_info("%s: suspending\n", __func__); +#if defined(CONFIG_ARCH_MSM7227) + writel(0x0, LCDC_MUX_CTL); + D("suspend_lcdc_mux_ctl = %x\n", readl(LCDC_MUX_CTL)); +#endif +#ifdef CONFIG_PANEL_SELF_REFRESH + if (lcdc->mdp->mdp_dev.overrides & MSM_MDP_RGB_PANEL_SELE_REFRESH) { + mutex_lock(&panel_icm->icm_lock); + panel_icm->icm_doable = false; + pr_info("[ICM %s]: icm mode=%d, clock_enabled=%d\n", __func__, panel_icm->icm_mode, panel_icm->clock_enabled); + if (panel_icm->icm_mode == true && panel_icm->clock_enabled == false) { + if (panel_ops->refresh_disable) + panel_ops->refresh_disable(panel_ops); + panel_icm->icm_mode = false; + } else { + mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); + clk_disable(lcdc->pad_pclk); + clk_disable(lcdc->pclk); + clk_disable(lcdc->mdp_clk); + } + panel_icm->clock_enabled = false; + mutex_unlock(&panel_icm->icm_lock); + } else { + mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); + clk_disable(lcdc->pad_pclk); + clk_disable(lcdc->pclk); + clk_disable(lcdc->mdp_clk); + } +#else mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); clk_disable(lcdc->pad_pclk); clk_disable(lcdc->pclk); if (lcdc->mdp_pclk) clk_disable(lcdc->mdp_pclk); clk_disable(lcdc->mdp_clk); +#endif + if (panel_ops->uninit) + panel_ops->uninit(panel_ops); return 0; } @@ -105,15 +311,33 @@ static int lcdc_suspend(struct msm_panel_data *fb_panel) static int lcdc_resume(struct msm_panel_data *fb_panel) { struct mdp_lcdc_info *lcdc = panel_to_lcdc(fb_panel); + struct msm_lcdc_panel_ops *panel_ops = lcdc->pdata->panel_ops; pr_info("%s: resuming\n", __func__); + if (panel_ops->init) { + if (panel_ops->init(panel_ops) < 0) + printk(KERN_ERR "LCD init fail!\n"); + } clk_enable(lcdc->mdp_clk); if (lcdc->mdp_pclk) clk_enable(lcdc->mdp_pclk); clk_enable(lcdc->pclk); clk_enable(lcdc->pad_pclk); +#if defined(CONFIG_ARCH_MSM7227) + writel(0x1, LCDC_MUX_CTL); + D("resume_lcdc_mux_ctl = %x\n", readl(LCDC_MUX_CTL)); +#endif + mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); +#ifdef CONFIG_PANEL_SELF_REFRESH + if (lcdc->mdp->mdp_dev.overrides & MSM_MDP_RGB_PANEL_SELE_REFRESH) { + mutex_lock(&panel_icm->icm_lock); + panel_icm->icm_doable = true; + panel_icm->clock_enabled = true; + mutex_unlock(&panel_icm->icm_lock); + } +#endif return 0; } @@ -130,9 +354,12 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) clk_enable(lcdc->pclk); clk_enable(lcdc->pad_pclk); +#ifdef CONFIG_MSM_MDP40 + mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); + mdelay(10); +#endif clk_set_rate(lcdc->pclk, lcdc->parms.clk_rate); clk_set_rate(lcdc->pad_pclk, lcdc->parms.clk_rate); - /* write the lcdc params */ mdp_writel(lcdc->mdp, lcdc->parms.hsync_ctl, MDP_LCDC_HSYNC_CTL); mdp_writel(lcdc->mdp, lcdc->parms.vsync_period, MDP_LCDC_VSYNC_PERIOD); @@ -150,10 +377,13 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START); mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END); mdp_writel(lcdc->mdp, lcdc->parms.polarity, MDP_LCDC_CTL_POLARITY); +<<<<<<< HEAD fb_size = ((fb_panel->fb_data->yres & 0x7ff) << 16) | (fb_panel->fb_data->xres & 0x7ff); +======= +>>>>>>> video: msm: Updated MDDI from HTC /* config the dma_p block that drives the lcdc data */ mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_DMA_P_IBUF_ADDR); mdp_writel(lcdc->mdp, fb_size, MDP_DMA_P_SIZE); @@ -166,6 +396,7 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) #endif dma_cfg = mdp_readl(lcdc->mdp, MDP_DMA_P_CONFIG); +<<<<<<< HEAD dma_cfg &= ~(DMA_PACK_PATTERN_MASK | DMA_PACK_ALIGN_MASK); dma_cfg |= (DMA_PACK_ALIGN_MSB | DMA_PACK_PATTERN_RGB | @@ -185,6 +416,23 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; +======= + if (lcdc->mdp->mdp_dev.overrides & MSM_MDP_DMA_PACK_ALIGN_LSB) + dma_cfg &= ~DMA_PACK_ALIGN_MSB; + else + dma_cfg |= DMA_PACK_ALIGN_MSB; + + dma_cfg |= (DMA_PACK_PATTERN_RGB | + DMA_DITHER_EN); + dma_cfg |= DMA_OUT_SEL_LCDC; + dma_cfg &= ~DMA_DST_BITS_MASK; + if(lcdc->color_format == MSM_MDP_OUT_IF_FMT_RGB565) + dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; + else if (lcdc->color_format == MSM_MDP_OUT_IF_FMT_RGB666) + dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; + else if (lcdc->color_format == MSM_MDP_OUT_IF_FMT_RGB888) + dma_cfg |= DMA_DSTC0G_8BITS | DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; +>>>>>>> video: msm: Updated MDDI from HTC mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG); @@ -241,19 +489,62 @@ static void lcdc_frame_start(struct msmfb_callback *cb) wake_up(&lcdc->vsync_waitq); } +#ifdef CONFIG_MSM_MDP40 +static void lcdc_overlay_start(void *priv, uint32_t addr, uint32_t stride, + uint32_t width, uint32_t height, uint32_t x, + uint32_t y) +{ + struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + + struct mdp4_overlay_pipe *pipe; + pipe = lcdc_pipe; + pipe->srcp0_addr = addr; + + if (mdp->dma_config_dirty) + { + if(mdp->dma_format == DMA_IBUF_FORMAT_RGB565) { + pipe->src_format = MDP_RGB_565; + pipe->srcp0_ystride = pipe->src_width * 2; + } else if(mdp->dma_format == DMA_IBUF_FORMAT_XRGB8888) { + pipe->src_format = MDP_RGBA_8888; + pipe->srcp0_ystride = pipe->src_width * 4; + } + mdp4_overlay_format2pipe(pipe); + mdp_writel(pipe->mdp, 0, MDP_LCDC_EN); + mdelay(30); + mdp4_overlay_dmap_xy(pipe); + mdp4_overlay_dmap_cfg(pipe, 1); + mdp4_overlayproc_cfg(pipe); + mdp4_overlay_rgb_setup(pipe); + mdp4_overlay_reg_flush(pipe, 1); /* rgb1 and mixer0 */ + mdp_writel(pipe->mdp, 1, MDP_LCDC_EN); + mdp->dma_config_dirty = false; + } else { + mdp4_overlay_rgb_setup(pipe); + mdp4_overlay_reg_flush(pipe, 1); /* rgb1 and mixer0 */ + } + +} +#else static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride, uint32_t width, uint32_t height, uint32_t x, uint32_t y) { struct mdp_lcdc_info *lcdc = priv; +<<<<<<< HEAD struct mdp_info *mdp = lcdc->mdp; uint32_t dma2_cfg; #ifdef CONFIG_MSM_MDP31 if (lcdc->mdp->dma_format_dirty) { +======= + struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + if (mdp->dma_config_dirty) + { +>>>>>>> video: msm: Updated MDDI from HTC mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); - mdelay(20); - mdp_configure_dma_format(mdp_dev); + mdelay(30); + mdp_dev->configure_dma(mdp_dev); mdp_writel(lcdc->mdp, 1, MDP_LCDC_EN); } @@ -302,6 +593,7 @@ static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride, mdp_writel(mdp, 0x11, MDP_OVERLAY_REG_FLUSH); #endif } +#endif static void precompute_timing_parms(struct mdp_lcdc_info *lcdc) { @@ -314,21 +606,21 @@ static void precompute_timing_parms(struct mdp_lcdc_info *lcdc) unsigned int display_vstart; unsigned int display_vend; - hsync_period = (timing->hsync_back_porch + + hsync_period = (timing->hsync_pulse_width + timing->hsync_back_porch + fb_data->xres + timing->hsync_front_porch); - hsync_start_x = timing->hsync_back_porch; + hsync_start_x = (timing->hsync_pulse_width + timing->hsync_back_porch); hsync_end_x = hsync_start_x + fb_data->xres - 1; - vsync_period = (timing->vsync_back_porch + + vsync_period = (timing->vsync_pulse_width + timing->vsync_back_porch + fb_data->yres + timing->vsync_front_porch); vsync_period *= hsync_period; - display_vstart = timing->vsync_back_porch; + display_vstart = timing->vsync_pulse_width + timing->vsync_back_porch; display_vstart *= hsync_period; display_vstart += timing->hsync_skew; - display_vend = (timing->vsync_back_porch + fb_data->yres) * - hsync_period; + display_vend = (timing->vsync_pulse_width + timing->vsync_back_porch + + fb_data->yres) * hsync_period; display_vend += timing->hsync_skew - 1; /* register values we pre-compute at init time from the timing @@ -355,6 +647,10 @@ static int mdp_lcdc_probe(struct platform_device *pdev) struct msm_lcdc_platform_data *pdata = pdev->dev.platform_data; struct mdp_lcdc_info *lcdc; int ret = 0; +#ifdef CONFIG_MSM_MDP40 + struct mdp4_overlay_pipe *pipe; + int ptype; +#endif if (!pdata) { pr_err("%s: no LCDC platform data found\n", __func__); @@ -396,14 +692,51 @@ static int mdp_lcdc_probe(struct platform_device *pdev) lcdc->frame_start_cb.func = lcdc_frame_start; platform_set_drvdata(pdev, lcdc); - +#ifdef CONFIG_MSM_MDP40 + mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, INTR_OVERLAY0_DONE, + lcdc_overlay_start); +#else mdp_out_if_register(mdp_dev, MSM_LCDC_INTERFACE, lcdc, MDP_DMA_P_DONE, lcdc_dma_start); - +#endif precompute_timing_parms(lcdc); lcdc->fb_start = pdata->fb_resource->start; lcdc->mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); + if(lcdc->mdp->mdp_dev.color_format) + lcdc->color_format = lcdc->mdp->mdp_dev.color_format; + else + lcdc->color_format = MSM_MDP_OUT_IF_FMT_RGB565; + +#ifdef CONFIG_MSM_MDP40 + if (lcdc_pipe == NULL) { + ptype = mdp4_overlay_format2type(MDP_RGB_565); + pipe = mdp4_overlay_pipe_alloc(ptype); + if (!pipe) + goto err_mdp4_overlay_pipe_alloc; + pipe->mixer_stage = MDP4_MIXER_STAGE_BASE; + pipe->mixer_num = MDP4_MIXER0; + pipe->src_format = MDP_RGB_565; + mdp4_overlay_format2pipe(pipe); + pipe->mdp = lcdc->mdp; + + lcdc_pipe = pipe; /* keep it */ + } else { + pipe = lcdc_pipe; + } + + pipe->src_height = pdata->fb_data->yres; + pipe->src_width = pdata->fb_data->xres; + pipe->src_h = pdata->fb_data->yres; + pipe->src_w = pdata->fb_data->xres; + pipe->src_y = 0; + pipe->src_x = 0; + pipe->srcp0_addr = (uint32_t) lcdc->fb_start; + pipe->srcp0_ystride = pdata->fb_data->xres * 2; + + mdp4_overlay_rgb_setup(pipe); + mdp4_mixer_stage_up(pipe); +#endif lcdc->fb_panel_data.suspend = lcdc_suspend; lcdc->fb_panel_data.resume = lcdc_resume; @@ -414,21 +747,18 @@ static int mdp_lcdc_probe(struct platform_device *pdev) lcdc->fb_panel_data.unblank = lcdc_unblank; lcdc->fb_panel_data.fb_data = pdata->fb_data; lcdc->fb_panel_data.interface_type = MSM_LCDC_INTERFACE; - + lcdc->fb_panel_data.shutdown = lcdc_shutdown; ret = lcdc_hw_init(lcdc); if (ret) { pr_err("%s: Cannot initialize the mdp_lcdc\n", __func__); goto err_hw_init; } - lcdc->fb_pdev.name = "msm_panel"; lcdc->fb_pdev.id = pdata->fb_id; lcdc->fb_pdev.resource = pdata->fb_resource; lcdc->fb_pdev.num_resources = 1; lcdc->fb_pdev.dev.platform_data = &lcdc->fb_panel_data; - if (pdata->panel_ops->init) - pdata->panel_ops->init(pdata->panel_ops); ret = platform_device_register(&lcdc->fb_pdev); if (ret) { @@ -437,11 +767,23 @@ static int mdp_lcdc_probe(struct platform_device *pdev) } pr_info("%s: initialized\n", __func__); +#ifdef CONFIG_PANEL_SELF_REFRESH + if (lcdc->mdp->mdp_dev.overrides & MSM_MDP_RGB_PANEL_SELE_REFRESH) { + ret = icm_init(lcdc); + if (ret) { + pr_err("%s: Cannot init dispaly selfrefresh \n", __func__); + goto err_plat_dev_reg; + } + } +#endif return 0; err_plat_dev_reg: err_hw_init: +#ifdef CONFIG_MSM_MDP40 +err_mdp4_overlay_pipe_alloc: +#endif platform_set_drvdata(pdev, NULL); clk_put(lcdc->pad_pclk); err_get_pad_pclk: diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index a21a44a08d742..829e4d2b58258 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -33,6 +32,8 @@ #include #include #include +#include +#include "mdp_hw.h" #define MSMFB_DEBUG 1 #ifdef CONFIG_FB_MSM_LOGO @@ -55,6 +56,11 @@ extern int load_565rle_image(char *filename); #define BLIT_TIME 0x4 #define SHOW_UPDATES 0x8 +#ifdef CONFIG_PANEL_SELF_REFRESH +extern struct panel_icm_info *panel_icm; +extern wait_queue_head_t panel_update_wait_queue; +#endif + #define DLOG(mask, fmt, args...) \ do { \ if (msmfb_debug_mask & mask) \ @@ -63,12 +69,12 @@ do { \ #define BITS_PER_PIXEL(info) (info->fb->var.bits_per_pixel) #define BYTES_PER_PIXEL(info) (info->fb->var.bits_per_pixel >> 3) - static int msmfb_debug_mask; module_param_named(msmfb_debug_mask, msmfb_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); struct mdp_device *mdp; +static atomic_t mdpclk_on = ATOMIC_INIT(1); struct msmfb_info { struct fb_info *fb; @@ -91,6 +97,12 @@ struct msmfb_info { struct early_suspend earlier_suspend; struct early_suspend early_suspend; + +#ifdef CONFIG_HTC_ONMODE_CHARGING + struct early_suspend onchg_earlier_suspend; + struct early_suspend onchg_suspend; +#endif + struct wake_lock idle_lock; spinlock_t update_lock; struct mutex panel_init_lock; @@ -103,6 +115,51 @@ struct msmfb_info { ktime_t vsync_request_time; }; +#ifdef CONFIG_FB_MSM_OVERLAY +#define USE_OVERLAY 1 +struct overlay_waitevent{ + uint32_t waked_up; + wait_queue_head_t event_wait; +}; +static struct overlay_waitevent overlay_event; +DEFINE_MUTEX(overlay_event_lock); +#endif + +#if (defined(CONFIG_USB_FUNCTION_PROJECTOR) || defined(CONFIG_USB_ANDROID_PROJECTOR)) +static spinlock_t fb_data_lock = SPIN_LOCK_UNLOCKED; +static struct msm_fb_info msm_fb_data; +int msmfb_get_var(struct msm_fb_info *tmp) +{ + unsigned long flags; + spin_lock_irqsave(&fb_data_lock, flags); + memcpy(tmp, &msm_fb_data, sizeof(msm_fb_data)); + spin_unlock_irqrestore(&fb_data_lock, flags); + return 0; +} + +/* projector need this, and very much */ +int msmfb_get_fb_area(void) +{ + int area; + unsigned long flags; + spin_lock_irqsave(&fb_data_lock, flags); + area = msm_fb_data.msmfb_area; + spin_unlock_irqrestore(&fb_data_lock, flags); + return area; +} + +static void msmfb_set_var(unsigned char *addr, int area) +{ + unsigned long flags; + + spin_lock_irqsave(&fb_data_lock, flags); + msm_fb_data.fb_addr = addr; + msm_fb_data.msmfb_area = area; + spin_unlock_irqrestore(&fb_data_lock, flags); + +} +#endif + static int msmfb_open(struct fb_info *info, int user) { return 0; @@ -116,7 +173,7 @@ static int msmfb_release(struct fb_info *info, int user) /* Called from dma interrupt handler, must not sleep */ static void msmfb_handle_dma_interrupt(struct msmfb_callback *callback) { - unsigned long irq_flags; + unsigned long irq_flags=0; struct msmfb_info *msmfb = container_of(callback, struct msmfb_info, dma_callback); #if PRINT_FPS @@ -153,7 +210,7 @@ static int msmfb_start_dma(struct msmfb_info *msmfb) { uint32_t x, y, w, h; unsigned addr; - unsigned long irq_flags; + unsigned long irq_flags=0; uint32_t yoffset; s64 time_since_request; struct msm_panel_data *panel = msmfb->panel; @@ -232,7 +289,10 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, { struct msmfb_info *msmfb = info->par; struct msm_panel_data *panel = msmfb->panel; - unsigned long irq_flags; +#ifdef CONFIG_PANEL_SELF_REFRESH + struct mdp_lcdc_info *lcdc = panel_to_lcdc(panel); +#endif + unsigned long irq_flags=0; int sleeping; int retry = 1; #if PRINT_FPS @@ -244,6 +304,25 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, DLOG(SHOW_UPDATES, "update %d %d %d %d %d %d\n", left, top, eright, ebottom, yoffset, pan_display); + + if (msmfb->sleeping != AWAKE) + DLOG(SUSPEND_RESUME, "pan_update in state(%d)\n", msmfb->sleeping); + +#ifdef CONFIG_PANEL_SELF_REFRESH + if (lcdc->mdp->mdp_dev.overrides & MSM_MDP_RGB_PANEL_SELE_REFRESH) { + spin_lock_irqsave(&panel_icm->lock, irq_flags); + panel_icm->panel_update = 1; + spin_unlock_irqrestore(&panel_icm->lock, irq_flags); + wake_up(&panel_update_wait_queue); + } +#endif + +#if (defined(CONFIG_USB_FUNCTION_PROJECTOR) || defined(CONFIG_USB_ANDROID_PROJECTOR)) + /* Jay, 8/1/09' */ + msmfb_set_var(msmfb->fb->screen_base, yoffset); +#endif + +>>>>>>> 2eaad42... video: msm: Updated MDDI from HTC restart: spin_lock_irqsave(&msmfb->update_lock, irq_flags); @@ -260,8 +339,8 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, sleeping = msmfb->sleeping; /* on a full update, if the last frame has not completed, wait for it */ - if ((pan_display && msmfb->frame_requested != msmfb->frame_done) || - sleeping == UPDATING) { + if (pan_display && (msmfb->frame_requested != msmfb->frame_done || + sleeping == UPDATING)) { int ret; spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); ret = wait_event_interruptible_timeout(msmfb->frame_wq, @@ -360,8 +439,7 @@ static void power_on_panel(struct work_struct *work) struct msmfb_info *msmfb = container_of(work, struct msmfb_info, resume_work); struct msm_panel_data *panel = msmfb->panel; - unsigned long irq_flags; - + unsigned long irq_flags=0; mutex_lock(&msmfb->panel_init_lock); DLOG(SUSPEND_RESUME, "turning on panel\n"); if (msmfb->sleeping == UPDATING) { @@ -381,6 +459,37 @@ static void power_on_panel(struct work_struct *work) mutex_unlock(&msmfb->panel_init_lock); } +<<<<<<< HEAD +======= +static BLOCKING_NOTIFIER_HEAD(display_chain_head); +int register_display_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&display_chain_head, nb); +} +static int display_notifier_callback(struct notifier_block *nfb, + unsigned long action, + void *ignored) +{ + //struct msmfb_info *msm_fb = (struct msmfb_info *)ignored; + + switch (action) { + case NOTIFY_MSM_FB: + printk(KERN_DEBUG "NOTIFY_MSM_FB\n"); + //msmfb_resume(&msm_fb->early_suspend); + break; + case NOTIFY_POWER: + /* nothing to do */ + break; + default: + printk(KERN_ERR "%s: unknown action in 0x%lx\n", + __func__, action); + return NOTIFY_BAD; + } + return NOTIFY_OK; +} + +/* -------------------------------------------------------------------------- */ +>>>>>>> 2eaad42... video: msm: Updated MDDI from HTC #ifdef CONFIG_HAS_EARLYSUSPEND /* turn off the panel */ static void msmfb_earlier_suspend(struct early_suspend *h) @@ -388,7 +497,7 @@ static void msmfb_earlier_suspend(struct early_suspend *h) struct msmfb_info *msmfb = container_of(h, struct msmfb_info, earlier_suspend); struct msm_panel_data *panel = msmfb->panel; - unsigned long irq_flags; + unsigned long irq_flags=0; mutex_lock(&msmfb->panel_init_lock); msmfb->sleeping = SLEEPING; @@ -397,12 +506,12 @@ static void msmfb_earlier_suspend(struct early_suspend *h) spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); wait_event_timeout(msmfb->frame_wq, msmfb->frame_requested == msmfb->frame_done, HZ/10); - +#ifndef CONFIG_MSM_MDP40 mdp->dma(mdp, virt_to_phys(msmfb->black), 0, msmfb->fb->var.xres, msmfb->fb->var.yres, 0, 0, NULL, panel->interface_type); mdp->dma_wait(mdp, panel->interface_type); - +#endif /* turn off the panel */ panel->blank(panel); } @@ -413,14 +522,46 @@ static void msmfb_suspend(struct early_suspend *h) early_suspend); struct msm_panel_data *panel = msmfb->panel; /* suspend the panel */ +#ifdef CONFIG_FB_MSM_OVERLAY + /*check whether overlay done*/ + wait_event_interruptible_timeout( + overlay_event.event_wait, + (overlay_event.waked_up == ~USE_OVERLAY), + 10*HZ); + /*wait until USE_OVERLAY flag is off and set mdpclk_on as off*/ + atomic_set(&mdpclk_on, 0); + pr_info("wait event : %X\n", overlay_event.waked_up); +#endif panel->suspend(panel); mutex_unlock(&msmfb->panel_init_lock); } +<<<<<<< HEAD static void msmfb_resume(struct early_suspend *h) { struct msmfb_info *msmfb = container_of(h, struct msmfb_info, early_suspend); +======= +static void msmfb_resume_handler(struct early_suspend *h) +{ + struct msmfb_info *msmfb = container_of(h, struct msmfb_info, + early_suspend); +#ifdef CONFIG_HTC_ONMODE_CHARGING + if (msmfb->fb_resumed == 1) { + DLOG(SUSPEND_RESUME, "fb is resumed by onchg. skip resume\n"); + return; + } +#endif + queue_work(msmfb->resume_workqueue, &msmfb->msmfb_resume_work); + wait_event_interruptible_timeout(msmfb->frame_wq, msmfb->fb_resumed==1,HZ/2); +} + +#ifdef CONFIG_HTC_ONMODE_CHARGING +static void msmfb_onchg_earlier_suspend(struct early_suspend *h) +{ + struct msmfb_info *msmfb = container_of(h, struct msmfb_info, + onchg_earlier_suspend); +>>>>>>> 2eaad42... video: msm: Updated MDDI from HTC struct msm_panel_data *panel = msmfb->panel; unsigned long irq_flags; @@ -434,6 +575,72 @@ static void msmfb_resume(struct early_suspend *h) msmfb->sleeping = WAKING; DLOG(SUSPEND_RESUME, "ready, waiting for full update\n"); spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); +<<<<<<< HEAD +======= + wait_event_timeout(msmfb->frame_wq, + msmfb->frame_requested == msmfb->frame_done, HZ/10); +#ifndef CONFIG_MSM_MDP40 + mdp->dma(mdp, virt_to_phys(msmfb->black), 0, + msmfb->fb->var.xres, msmfb->fb->var.yres, 0, 0, + NULL, panel->interface_type); + mdp->dma_wait(mdp, panel->interface_type); +#endif + /* turn off the panel */ + panel->blank(panel); +} + +static void msmfb_onchg_suspend(struct early_suspend *h) +{ + struct msmfb_info *msmfb = container_of(h, struct msmfb_info, + onchg_suspend); + struct msm_panel_data *panel = msmfb->panel; + /* suspend the panel */ +#ifdef CONFIG_FB_MSM_OVERLAY + /*check whether overlay done*/ + wait_event_interruptible_timeout( + overlay_event.event_wait, + (overlay_event.waked_up == ~USE_OVERLAY), + 10*HZ); + /*wait until USE_OVERLAY flag is off and set mdpclk_on as off*/ + atomic_set(&mdpclk_on, 0); + pr_info("wait event : %X\n", overlay_event.waked_up); +#endif + panel->suspend(panel); + msmfb->fb_resumed = 0; + mutex_unlock(&msmfb->panel_init_lock); +} + +static void msmfb_onchg_resume_handler(struct early_suspend *h) +{ + struct msmfb_info *msmfb = container_of(h, struct msmfb_info, + onchg_suspend); + queue_work(msmfb->resume_workqueue, &msmfb->msmfb_resume_work); + wait_event_interruptible_timeout(msmfb->frame_wq, msmfb->fb_resumed == 1, HZ/2); +} +#endif + +static void msmfb_resume(struct work_struct *work) +{ + struct msmfb_info *msmfb = + container_of(work, struct msmfb_info, msmfb_resume_work); + struct msm_panel_data *panel = msmfb->panel; + unsigned long irq_flags=0; + + if (panel->resume(panel)) { + printk(KERN_INFO "msmfb: panel resume failed, not resuming " + "fb\n"); + return; + } + spin_lock_irqsave(&msmfb->update_lock, irq_flags); + msmfb->frame_requested = msmfb->frame_done = msmfb->update_frame = 0; + msmfb->sleeping = WAKING; + DLOG(SUSPEND_RESUME, "ready, waiting for full update\n"); + spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); + msmfb->fb_resumed = 1; + wake_up(&msmfb->frame_wq); + + atomic_set(&mdpclk_on, 1); +>>>>>>> 2eaad42... video: msm: Updated MDDI from HTC } #endif @@ -479,7 +686,6 @@ static int msmfb_set_par(struct fb_info *info) return -1; mdp->set_output_format(mdp, var->bits_per_pixel); fix->line_length = var->xres * var->bits_per_pixel / 8; - return 0; } @@ -553,6 +759,92 @@ static int msmfb_blit(struct fb_info *info, } return 0; } +#ifdef CONFIG_FB_MSM_OVERLAY +static int msmfb_overlay_get(struct fb_info *info, void __user *p) +{ + struct mdp_overlay req; + int ret; + + if (copy_from_user(&req, p, sizeof(req))) + return -EFAULT; + + ret = mdp->overlay_get(mdp, info, &req); + + if (ret) { + printk(KERN_ERR "%s: ioctl failed \n", + __func__); + return ret; + } + if (copy_to_user(p, &req, sizeof(req))) { + printk(KERN_ERR "%s: copy2user failed \n", + __func__); + return -EFAULT; + } + + return 0; +} + +static int msmfb_overlay_set(struct fb_info *info, void __user *p) +{ + struct mdp_overlay req; + int ret; + + if (copy_from_user(&req, p, sizeof(req))) + return -EFAULT; + + printk(KERN_INFO "%s(%d) dst rect info w=%d h=%d x=%d y=%d rotator=%d\n", __func__, __LINE__, req.dst_rect.w, req.dst_rect.h, req.dst_rect.x, req.dst_rect.y, req.user_data[0]); + ret = mdp->overlay_set(mdp, info, &req); + if (ret) { + printk(KERN_ERR "%s:ioctl failed \n", + __func__); + return ret; + } + + if (copy_to_user(p, &req, sizeof(req))) { + printk(KERN_ERR "%s: copy2user failed \n", + __func__); + return -EFAULT; + } + + return 0; +} + +static int msmfb_overlay_unset(struct fb_info *info, unsigned long *argp) +{ + int ret, ndx; + + ret = copy_from_user(&ndx, argp, sizeof(ndx)); + if (ret) { + printk(KERN_ERR "%s:msmfb_overlay_unset ioctl failed \n", + __func__); + return ret; + } + + return mdp->overlay_unset(mdp, info, ndx); +} + +static int msmfb_overlay_play(struct fb_info *info, unsigned long *argp) +{ + int ret; + struct msmfb_overlay_data req; + struct file *p_src_file = 0; + + ret = copy_from_user(&req, argp, sizeof(req)); + if (ret) { + printk(KERN_ERR "%s:msmfb_overlay_play ioctl failed \n", + __func__); + return ret; + } + + ret = mdp->overlay_play(mdp, info, &req, &p_src_file); + + if (p_src_file) + put_pmem_file(p_src_file); + + return ret; +} + +#endif DEFINE_MUTEX(mdp_ppp_lock); @@ -560,7 +852,7 @@ DEFINE_MUTEX(mdp_ppp_lock); static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; - int ret; + int ret = 0; #if PRINT_BLIT_TIME ktime_t t1, t2; #endif @@ -574,19 +866,51 @@ static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg) t1 = ktime_get(); #endif ret = msmfb_blit(p, argp); + if (ret) + return ret; #if PRINT_BLIT_TIME t2 = ktime_get(); DLOG(BLIT_TIME, "total %lld\n", ktime_to_ns(t2) - ktime_to_ns(t1)); #endif - if (ret) - return ret; break; +#ifdef CONFIG_FB_MSM_OVERLAY + case MSMFB_OVERLAY_GET: + ret = msmfb_overlay_get(p, argp); + break; + case MSMFB_OVERLAY_SET: + if(!atomic_read(&mdpclk_on)) { + printk(KERN_ERR "MSMFB_OVERLAY_SET during suspend\n"); + ret = -EINVAL; + } else { + mutex_lock(&overlay_event_lock); + overlay_event.waked_up = USE_OVERLAY; + mutex_unlock(&overlay_event_lock); + ret = msmfb_overlay_set(p, argp); + } + printk(KERN_INFO "MSMFB_OVERLAY_SET ret=%d\n", ret); + break; + case MSMFB_OVERLAY_UNSET: + ret = msmfb_overlay_unset(p, argp); + mutex_lock(&overlay_event_lock); + overlay_event.waked_up = ~USE_OVERLAY; + wake_up(&overlay_event.event_wait); + mutex_unlock(&overlay_event_lock); + printk(KERN_INFO "MSMFB_OVERLAY_UNSET ret=%d\n", ret); + break; + case MSMFB_OVERLAY_PLAY: + if(!atomic_read(&mdpclk_on)) { + printk(KERN_ERR "MSMFB_OVERLAY_PLAY during suspend\n"); + ret = -EINVAL; + } else + ret = msmfb_overlay_play(p, argp); + break; +#endif default: printk(KERN_INFO "msmfb unknown ioctl: %d\n", cmd); return -EINVAL; } - return 0; + return ret; } static struct fb_ops msmfb_ops = { @@ -620,7 +944,7 @@ static ssize_t debug_read(struct file *file, char __user *buf, size_t count, static char buffer[4096]; int n = 0; struct msmfb_info *msmfb = (struct msmfb_info *)file->private_data; - unsigned long irq_flags; + unsigned long irq_flags=0; spin_lock_irqsave(&msmfb->update_lock, irq_flags); n = scnprintf(buffer, debug_bufmax, "yoffset %d\n", msmfb->yoffset); @@ -644,6 +968,8 @@ static struct file_operations debug_fops = { }; #endif +#define BITS_PER_PIXEL_DEF 16 + static void setup_fb_info(struct msmfb_info *msmfb) { struct fb_info *fb_info = msmfb->fb; @@ -659,13 +985,14 @@ static void setup_fb_info(struct msmfb_info *msmfb) fb_info->fix.type = FB_TYPE_PACKED_PIXELS; fb_info->fix.visual = FB_VISUAL_TRUECOLOR; fb_info->fix.line_length = msmfb->xres * 2; + fb_info->var.xres = msmfb->xres; fb_info->var.yres = msmfb->yres; fb_info->var.width = msmfb->panel->fb_data->width; fb_info->var.height = msmfb->panel->fb_data->height; fb_info->var.xres_virtual = msmfb->xres; fb_info->var.yres_virtual = msmfb->yres * 2; - fb_info->var.bits_per_pixel = 16; + fb_info->var.bits_per_pixel = BITS_PER_PIXEL_DEF; fb_info->var.accel_flags = 0; fb_info->var.yoffset = 0; @@ -674,8 +1001,11 @@ static void setup_fb_info(struct msmfb_info *msmfb) /* set the param in the fixed screen, so userspace can't * change it. This will be used to check for the * capability. */ + + /* FIX ME: every panel support partial update? fb_info->fix.reserved[0] = 0x5444; fb_info->fix.reserved[1] = 0x5055; + */ /* This preloads the value so that if userspace doesn't * change it, it will be a full update */ @@ -696,6 +1026,7 @@ static void setup_fb_info(struct msmfb_info *msmfb) fb_info->var.blue.msb_right = 0; mdp->set_output_format(mdp, fb_info->var.bits_per_pixel); + mdp->set_panel_size(mdp, msmfb->xres, msmfb->yres); r = fb_alloc_cmap(&fb_info->cmap, 16, 0); fb_info->pseudo_palette = PP; @@ -703,6 +1034,14 @@ static void setup_fb_info(struct msmfb_info *msmfb) PP[0] = 0; for (r = 1; r < 16; r++) PP[r] = 0xffffffff; + + /* Jay add, 7/1/09' */ +#if (defined(CONFIG_USB_FUNCTION_PROJECTOR) || defined(CONFIG_USB_ANDROID_PROJECTOR)) + msm_fb_data.xres = msmfb->xres; + msm_fb_data.yres = msmfb->yres; + printk(KERN_INFO "setup_fb_info msmfb->xres %d, msmfb->yres %d\n", + msmfb->xres,msmfb->yres); +#endif } static int setup_fbmem(struct msmfb_info *msmfb, struct platform_device *pdev) @@ -767,6 +1106,11 @@ static int msmfb_probe(struct platform_device *pdev) if (ret) goto error_setup_fbmem; +#if (defined(CONFIG_USB_FUNCTION_PROJECTOR) || defined(CONFIG_USB_ANDROID_PROJECTOR)) + /* Jay, 8/1/09' */ + msmfb_set_var(msmfb->fb->screen_base, 0); +#endif + setup_fb_info(msmfb); spin_lock_init(&msmfb->update_lock); @@ -808,7 +1152,6 @@ static int msmfb_probe(struct platform_device *pdev) hrtimer_init(&msmfb->fake_vsync, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - msmfb->fake_vsync.function = msmfb_fake_vsync; ret = register_framebuffer(fb); @@ -817,6 +1160,13 @@ static int msmfb_probe(struct platform_device *pdev) msmfb->sleeping = WAKING; +#ifdef CONFIG_FB_MSM_OVERLAY + /*init wait event*/ + init_waitqueue_head(&overlay_event.event_wait); + /*init waked_up value*/ + overlay_event.waked_up = ~USE_OVERLAY; +#endif + #ifdef CONFIG_FB_MSM_LOGO if (!load_565rle_image(INIT_IMAGE_FILE)) { /* Flip buffer */ @@ -840,6 +1190,34 @@ static int msmfb_probe(struct platform_device *pdev) return ret; } +<<<<<<< HEAD +======= +static void msmfb_shutdown(struct platform_device *pdev) +{ + struct msm_panel_data *panel = pdev->dev.platform_data; + struct fb_info *fb; + struct msmfb_info *msmfb; + + printk(KERN_INFO "%s\n", __func__); + fb = registered_fb[0]; + if (!fb) { + printk(KERN_ERR "fb0 unavailable.\n"); + return; + } + msmfb = fb->par; + + mdp->dma(mdp, virt_to_phys(msmfb->black), 0, + msmfb->fb->var.xres, msmfb->fb->var.yres, 0, 0, + NULL, panel->interface_type); + + if (panel->blank) + panel->blank(panel); + + if (panel->shutdown) + panel->shutdown(panel); +} + +>>>>>>> 2eaad42... video: msm: Updated MDDI from HTC static struct platform_driver msm_panel_driver = { /* need to write remove */ .probe = msmfb_probe, diff --git a/kernel/power/fbearlysuspend.c b/kernel/power/fbearlysuspend.c index 15137650149c2..83104fe471038 100644 --- a/kernel/power/fbearlysuspend.c +++ b/kernel/power/fbearlysuspend.c @@ -47,7 +47,7 @@ static void stop_drawing_early_suspend(struct early_suspend *h) } /* tell userspace to start drawing */ -static void start_drawing_late_resume(struct early_suspend *h) +void start_drawing_late_resume(struct early_suspend *h) { unsigned long irq_flags; From e2733e8cb55a5aceabbcd168d326da7482fa12a0 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 9 Nov 2010 00:55:07 -0500 Subject: [PATCH 1036/2556] msm: microp: Wrap microp setup in ifdefs We don't need this on mahimahi/dream/sapphire. Change-Id: Iae087d265ea4a4a74fe61a4895f535d0d2434a50 --- arch/arm/include/asm/setup.h | 4 ++++ arch/arm/include/asm/system.h | 2 ++ arch/arm/kernel/setup.c | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h index d29001b65e0b8..db64de809d29b 100644 --- a/arch/arm/include/asm/setup.h +++ b/arch/arm/include/asm/setup.h @@ -126,6 +126,7 @@ struct tag_cmdline { char cmdline[1]; /* this is the minimum size */ }; +#ifdef CONFIG_MICROP_COMMON /* Microp version */ #define ATAG_MICROP_VERSION 0x5441000a @@ -147,6 +148,7 @@ struct tag_ps_kparam { __u32 kparam1; __u32 kparam2; }; +#endif /* acorn RiscPC specific information */ #define ATAG_ACORN 0x41000101 @@ -175,9 +177,11 @@ struct tag { struct tag_initrd initrd; struct tag_serialnr serialnr; struct tag_revision revision; +#ifdef CONFIG_MICROP_COMMON struct tag_microp_version microp_version; struct tag_als_kadc als_kadc; struct tag_ps_kparam ps_kparam; +#endif struct tag_videolfb videolfb; struct tag_cmdline cmdline; diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 8011e02440391..913c582e32dd8 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -77,10 +77,12 @@ extern unsigned int system_rev; extern unsigned int system_serial_low; extern unsigned int system_serial_high; extern unsigned int mem_fclk_21285; +#ifdef CONFIG_MICROP_COMMON extern char microp_ver[4]; extern unsigned int als_kadc; extern unsigned int ps_kparam1; extern unsigned int ps_kparam2; +#endif struct pt_regs; diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 44a316c3bc728..a4f4872296636 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -85,6 +85,7 @@ unsigned int __atags_pointer __initdata; unsigned int system_rev; EXPORT_SYMBOL(system_rev); +#ifdef CONFIG_MICROP_COMMON char microp_ver[4]; EXPORT_SYMBOL(microp_ver); @@ -96,6 +97,7 @@ EXPORT_SYMBOL(ps_kparam1); unsigned int ps_kparam2; EXPORT_SYMBOL(ps_kparam2); +#endif unsigned int system_serial_low; EXPORT_SYMBOL(system_serial_low); @@ -658,6 +660,7 @@ static int __init parse_tag_revision(const struct tag *tag) __tagtable(ATAG_REVISION, parse_tag_revision); +#ifdef CONFIG_MICROP_COMMON static int __init parse_tag_microp_version(const struct tag *tag) { int i; @@ -688,6 +691,7 @@ static int __init parse_tag_ps_calibration(const struct tag *tag) } __tagtable(ATAG_PS, parse_tag_ps_calibration); +#endif static int __init parse_tag_cmdline(const struct tag *tag) { From fd34569f8763cc153869aa688cfcad3759aa3783 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 9 Nov 2010 02:37:07 -0500 Subject: [PATCH 1037/2556] sensors: Add BMA150 SPI and updated AKM8973 drivers Change-Id: I4f813cd038e06933743ca0842fa242b73a18d743 --- drivers/misc/Kconfig | 7 + drivers/misc/Makefile | 2 + drivers/misc/bma150_spi.c | 487 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 496 insertions(+) create mode 100644 drivers/misc/bma150_spi.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 0ba84caf4fd36..88c6e8e40251c 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -501,6 +501,13 @@ config WL127X_RFKILL Creates an rfkill entry in sysfs for power control of Bluetooth TI wl127x chips. +config SENSORS_BMA150_SPI + tristate "BMA150 G-sensor Driver" + depends on MICROP_COMMON + default y + help + BMA150 G-sensor Driver implemented by HTC. + config APANIC bool "Android kernel panic diagnostics driver" default n diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 8d61e582a0beb..1d91d2d44702b 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -51,4 +51,6 @@ obj-$(CONFIG_SENSORS_AK8975) += akm8975.o obj-$(CONFIG_SENSORS_AKM8973) += akm8973.o obj-$(CONFIG_SENSORS_AKM8976) += akm8976.o obj-$(CONFIG_VP_A1026) += a1026.o +obj-$(CONFIG_SENSORS_BMA150_SPI)+= bma150_spi.o obj-$(CONFIG_MSM_720P_CORE) += video_core/720p/ + diff --git a/drivers/misc/bma150_spi.c b/drivers/misc/bma150_spi.c new file mode 100644 index 0000000000000..dc952ed769d54 --- /dev/null +++ b/drivers/misc/bma150_spi.c @@ -0,0 +1,487 @@ +/* drivers/misc/bma150_spi.c - bma150 G-sensor driver + * + * Copyright (C) 2009 HTC Corporation. + * + * 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 + +struct early_suspend bma_early_suspend; + +static struct bma150_platform_data *this_pdata; + +static struct mutex gsensor_RW_mutex; +static struct mutex gsensor_set_mode_mutex; + +static int spi_microp_enable(uint8_t on) +{ + int ret; + ret = microp_spi_vote_enable(SPI_GSENSOR, on); + if (ret < 0) + printk(KERN_ERR "%s: i2c_write_block fail\n", __func__); + + return ret; +} + +static int spi_gsensor_read(uint8_t *data) +{ + int ret; + + mutex_lock(&gsensor_RW_mutex); + + ret = microp_i2c_write(MICROP_I2C_WCMD_GSENSOR_REG_DATA_REQ, data, 1); + if (ret < 0) { + printk(KERN_ERR "%s: i2c_write_block fail\n", __func__); + mutex_unlock(&gsensor_RW_mutex); + return ret; + } + + ret = microp_i2c_read(MICROP_I2C_RCMD_GSENSOR_REG_DATA, data, 2); + if (ret < 0) { + printk(KERN_ERR "%s: i2c_read_block fail\n", __func__); + mutex_unlock(&gsensor_RW_mutex); + return ret; + } + + mutex_unlock(&gsensor_RW_mutex); + + return ret; +} + +static int spi_gsensor_write(uint8_t *data) +{ + int ret; + + mutex_lock(&gsensor_RW_mutex); + + ret = microp_i2c_write(MICROP_I2C_WCMD_GSENSOR_REG, data, 2); + if (ret < 0) { + printk(KERN_ERR "%s: i2c_write_block fail\n", __func__); + mutex_unlock(&gsensor_RW_mutex); + return ret; + } + + mutex_unlock(&gsensor_RW_mutex); + + return ret; +} + +static int spi_gsensor_init_hw(void) +{ + char buffer[2]; + + memset(buffer, 0x0, sizeof(buffer)); + buffer[0] = RANGE_BWIDTH_REG; + if (spi_gsensor_read(buffer) < 0) + return -EIO; + + /*printk("spi_gsensor_init_hw,read RANGE_BWIDTH_REG = %x " + , buffer[1]);*/ + + buffer[1] = (buffer[1]&0xe0); + buffer[0] = RANGE_BWIDTH_REG; + if (spi_gsensor_write(buffer) < 0) + return -EIO; + + buffer[0] = SMB150_CONF2_REG; + if (spi_gsensor_read(buffer) < 0) + return -EIO; + + buffer[1] = buffer[1]|1<<3; + buffer[0] = SMB150_CONF2_REG; + if (spi_gsensor_write(buffer) < 0) + return -EIO; + + return 0; +} + +/* +static int spi_gsensor_read_version(void) +{ + uint8_t buffer[2]; + int ret = -EIO; + + buffer[0] = VERSION_REG; + buffer[1] = 1; + ret = spi_gsensor_read(buffer); + if (ret < 0) { + printk(KERN_ERR "%s: get al_version fail(%d)\n", __func__, ret); + return ret; + } + printk(KERN_INFO "%s: al_version: 0x%2.2X\n", __func__, buffer[0]); + + buffer[0] = CHIP_ID_REG; + buffer[1] = 1; + ret = spi_gsensor_read(buffer); + if (ret < 0) { + printk(KERN_ERR "%s: get chip_id fail(%d)\n", __func__, ret); + return ret; + } + printk(KERN_INFO "%s: chip_id: 0x%2.2X\n", __func__, buffer[0]); + return 0; +} +*/ +static int spi_bma150_TransRBuff(short *rbuf) +{ + int ret; + unsigned char buffer[6]; + memset(buffer, 0, 6); + + mutex_lock(&gsensor_RW_mutex); + + buffer[0] = 1; + ret = microp_i2c_write(MICROP_I2C_WCMD_GSENSOR_DATA_REQ, buffer, 1); + if (ret < 0) { + printk(KERN_ERR "%s: i2c_write_block fail\n", __func__); + mutex_unlock(&gsensor_RW_mutex); + return ret; + } + + if (this_pdata && this_pdata->microp_new_cmd && + this_pdata->microp_new_cmd == 1) { + /*printk(KERN_DEBUG "%s: New MicroP command\n", __func__);*/ + ret = microp_i2c_read(MICROP_I2C_RCMD_GSENSOR_DATA, buffer, 6); + rbuf[0] = buffer[0]<<2|buffer[1]>>6; + if (rbuf[0]&0x200) + rbuf[0] -= 1<<10; + rbuf[1] = buffer[2]<<2|buffer[3]>>6; + if (rbuf[1]&0x200) + rbuf[1] -= 1<<10; + rbuf[2] = buffer[4]<<2|buffer[5]>>6; + if (rbuf[2]&0x200) + rbuf[2] -= 1<<10; + } else { + /* For Passion with V01 ~ V05 Microp */ + /*printk(KERN_DEBUG "%s: Old MicroP command\n", __func__);*/ + ret = microp_i2c_read(MICROP_I2C_RCMD_GSENSOR_X_DATA, + buffer, 2); + if (ret < 0) { + printk(KERN_ERR "%s: i2c_read_block fail\n", __func__); + mutex_unlock(&gsensor_RW_mutex); + return ret; + } + rbuf[0] = buffer[0]<<2|buffer[1]>>6; + if (rbuf[0]&0x200) + rbuf[0] -= 1<<10; + + ret = microp_i2c_read(MICROP_I2C_RCMD_GSENSOR_Y_DATA, + buffer, 2); + if (ret < 0) { + printk(KERN_ERR "%s: i2c_read_block fail\n", __func__); + mutex_unlock(&gsensor_RW_mutex); + return ret; + } + rbuf[1] = buffer[0]<<2|buffer[1]>>6; + if (rbuf[1]&0x200) + rbuf[1] -= 1<<10; + + ret = microp_i2c_read(MICROP_I2C_RCMD_GSENSOR_Z_DATA, + buffer, 2); + if (ret < 0) { + printk(KERN_ERR "%s: i2c_read_block fail\n", __func__); + mutex_unlock(&gsensor_RW_mutex); + return ret; + } + rbuf[2] = buffer[0]<<2|buffer[1]>>6; + if (rbuf[2]&0x200) + rbuf[2] -= 1<<10; + } +/* printk("X=%d, Y=%d, Z=%d\n",rbuf[0],rbuf[1],rbuf[2]);*/ + +/* printk(KERN_DEBUG "%s: 0x%2.2X 0x%2.2X 0x%2.2X \ +0x%2.2X 0x%2.2X 0x%2.2X\n", + __func__, buffer[0], buffer[1], buffer[2], \ + buffer[3], buffer[4], buffer[5]);*/ + + mutex_unlock(&gsensor_RW_mutex); + + return 1; +} + +static int __spi_bma150_set_mode(char mode) +{ + char buffer[2]; + int ret; + mutex_lock(&gsensor_set_mode_mutex); + if (mode == BMA_MODE_NORMAL) { + spi_microp_enable(1); + printk(KERN_INFO "%s: BMA get into NORMAL mode!\n", + __func__); + } + + buffer[0] = SMB150_CTRL_REG; + ret = spi_gsensor_read(buffer); + if (ret < 0) { + mutex_unlock(&gsensor_set_mode_mutex); + return -1; + } + + buffer[1] = (buffer[1]&0xfe)|mode; + buffer[0] = SMB150_CTRL_REG; + ret = spi_gsensor_write(buffer); + + if (mode == BMA_MODE_SLEEP) { + spi_microp_enable(0); + printk(KERN_INFO "%s: BMA get into SLEEP mode!\n", + __func__); + } + mutex_unlock(&gsensor_set_mode_mutex); + return ret; +} + + +static int spi_bma150_open(struct inode *inode, struct file *file) +{ + return nonseekable_open(inode, file); +} + +static int spi_bma150_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int spi_bma150_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + char rwbuf[8]; + char *toRbuf; + int ret = -1; + short buf[8], temp; + + switch (cmd) { + case BMA_IOCTL_READ: + case BMA_IOCTL_WRITE: + case BMA_IOCTL_SET_MODE: + if (copy_from_user(&rwbuf, argp, sizeof(rwbuf))) + return -EFAULT; + break; + case BMA_IOCTL_READ_ACCELERATION: + if (copy_from_user(&buf, argp, sizeof(buf))) + return -EFAULT; + break; + default: + break; + } + + switch (cmd) { + case BMA_IOCTL_INIT: + ret = spi_gsensor_init_hw(); + if (ret < 0) + return ret; + break; + + case BMA_IOCTL_READ: + if (rwbuf[0] < 1) + return -EINVAL; + ret = spi_gsensor_read(&rwbuf[1]); + if (ret < 0) + return ret; + break; + case BMA_IOCTL_WRITE: + if (rwbuf[0] < 2) + return -EINVAL; + ret = spi_gsensor_write(&rwbuf[1]); + if (ret < 0) + return ret; + break; + case BMA_IOCTL_READ_ACCELERATION: + ret = spi_bma150_TransRBuff(&buf[0]); + if (ret < 0) + return ret; + break; + case BMA_IOCTL_SET_MODE: + /*printk(KERN_DEBUG + "%s: BMA_IOCTL_SET_MODE by ioctl = %d\n", + __func__,rwbuf[0]);*/ + ret = __spi_bma150_set_mode(rwbuf[0]); + if (ret < 0) + return ret; + break; + case BMA_IOCTL_GET_INT: + temp = 0; + break; + case BMA_IOCTL_GET_CHIP_LAYOUT: + if (this_pdata) + temp = this_pdata->chip_layout; + break; + default: + return -ENOTTY; + } + + switch (cmd) { + case BMA_IOCTL_READ: + toRbuf = &rwbuf[1]; + if (copy_to_user(argp, toRbuf, sizeof(rwbuf)-1)) + return -EFAULT; + break; + case BMA_IOCTL_READ_ACCELERATION: + if (copy_to_user(argp, &buf, sizeof(buf))) + return -EFAULT; + break; + case BMA_IOCTL_GET_INT: + if (copy_to_user(argp, &temp, sizeof(temp))) + return -EFAULT; + break; + case BMA_IOCTL_GET_CHIP_LAYOUT: + if (copy_to_user(argp, &temp, sizeof(temp))) + return -EFAULT; + break; + default: + break; + } + + return 0; +} + +static struct file_operations spi_bma_fops = { + .owner = THIS_MODULE, + .open = spi_bma150_open, + .release = spi_bma150_release, + .ioctl = spi_bma150_ioctl, +}; + +static struct miscdevice spi_bma_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "bma150", + .fops = &spi_bma_fops, +}; + +static void bma150_early_suspend(struct early_suspend *handler) +{ + int ret = 0; + ret = __spi_bma150_set_mode(BMA_MODE_SLEEP); + + printk(KERN_DEBUG + "%s: spi_bma150_set_mode returned = %d!\n", + __func__, ret); +} + +static void bma150_early_resume(struct early_suspend *handler) +{ + /*printk(KERN_DEBUG + "%s: spi_bma150_set_mode returned = %d!\n", + __func__, ret);*/ +} + +static int spi_gsensor_initial(void) +{ + int ret; +/* ret = spi_microp_enable(1); + if (ret < 0) { + printk(KERN_ERR "%s: spi_microp_enable fail\n", __func__); + return ret; + }*/ +/* ret = spi_gsensor_read_version(); + if (ret < 0) { + printk(KERN_ERR "%s: get version fail\n", __func__); + return ret; + }*/ + +/* ret = microp_gsensor_init_hw(client); + if (ret < 0) { + printk(KERN_ERR "%s: init g-sensor fail\n", __func__); + return ret; + } +*/ + ret = misc_register(&spi_bma_device); + if (ret < 0) { + printk(KERN_ERR "%s: init misc_register fail\n", __func__); + return ret; + } + + mutex_init(&gsensor_RW_mutex); + mutex_init(&gsensor_set_mode_mutex); + + + ret = spi_microp_enable(1); + if (ret) { + printk(KERN_ERR "%s: spi_microp_enable(1) fail!\n", __func__); + goto err_spi_enable; + } + + ret = __spi_bma150_set_mode(BMA_MODE_SLEEP); + if (ret) { + printk(KERN_ERR "%s: set BMA_MODE_SLEEP fail!\n", __func__); + goto err_set_mode; + } + + bma_early_suspend.suspend = bma150_early_suspend; + bma_early_suspend.resume = bma150_early_resume; + register_early_suspend(&bma_early_suspend); + + return 0; + +err_set_mode: + spi_microp_enable(0); +err_spi_enable: + misc_deregister(&spi_bma_device); + + return ret; +} + +static int spi_bma150_probe(struct platform_device *pdev) +{ + printk(KERN_INFO "%s: G-sensor connect with microP: " + "start initial\n", __func__); + + this_pdata = pdev->dev.platform_data; +/* + printk(KERN_DEBUG "%s: this_pdata->microp_new_cmd = %d\n", + __func__, this_pdata->microp_new_cmd); +*/ + spi_gsensor_initial(); + + return 0; +} + +static int spi_bma150_remove(struct platform_device *pdev) +{ + mutex_destroy(&gsensor_set_mode_mutex); + return 0; +} + +static struct platform_driver spi_bma150_driver = { + .probe = spi_bma150_probe, + .remove = spi_bma150_remove, + .driver = { + .name = BMA150_G_SENSOR_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init spi_bma150_init(void) +{ + return platform_driver_register(&spi_bma150_driver); + +} + +static void __exit spi_bma150_exit(void) +{ + platform_driver_unregister(&spi_bma150_driver); +} + +module_init(spi_bma150_init); +module_exit(spi_bma150_exit); + +MODULE_DESCRIPTION("BMA150 G-sensor driver"); +MODULE_LICENSE("GPL"); From 0b32a0069d51aefc0d238a25f7f74c2eb2dc4597 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 9 Nov 2010 02:49:08 -0500 Subject: [PATCH 1038/2556] sensors: akm8973: Add HTC-variant driver. Enabled automatically with MICROP_COMMON. Change-Id: Iaaf644a31e0beb60e3406a04a7b6250175470423 --- drivers/misc/Makefile | 6 +- drivers/misc/akm8973.c | 12 + drivers/misc/akm8973_htc.c | 795 +++++++++++++++++++++++++++++++++++++ 3 files changed, 812 insertions(+), 1 deletion(-) create mode 100644 drivers/misc/akm8973_htc.c diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 1d91d2d44702b..94f117cbe79b5 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -48,7 +48,11 @@ obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o obj-$(CONFIG_WL127X_RFKILL) += wl127x-rfkill.o obj-$(CONFIG_APANIC) += apanic.o obj-$(CONFIG_SENSORS_AK8975) += akm8975.o -obj-$(CONFIG_SENSORS_AKM8973) += akm8973.o +ifeq ($(CONFIG_MICROP_COMMON),y) + obj-$(CONFIG_SENSORS_AKM8973) += akm8973_htc.o +else + obj-$(CONFIG_SENSORS_AKM8973) += akm8973.o +endif obj-$(CONFIG_SENSORS_AKM8976) += akm8976.o obj-$(CONFIG_VP_A1026) += a1026.o obj-$(CONFIG_SENSORS_BMA150_SPI)+= bma150_spi.o diff --git a/drivers/misc/akm8973.c b/drivers/misc/akm8973.c index 3e756fcee0d80..20600b6e59b51 100644 --- a/drivers/misc/akm8973.c +++ b/drivers/misc/akm8973.c @@ -27,7 +27,10 @@ #include #include #include +<<<<<<< HEAD #include +======= +>>>>>>> 8988ee2... sensors: akm8973: Add HTC-variant driver. #define DEBUG 0 #define MAX_FAILURE_COUNT 3 @@ -309,12 +312,18 @@ static int AKECS_GetCloseStatus(void) static void AKECS_CloseDone(void) { +<<<<<<< HEAD mutex_lock(&akmd_lock); +======= +>>>>>>> 8988ee2... sensors: akm8973: Add HTC-variant driver. atomic_set(&m_flag, 1); atomic_set(&a_flag, 1); atomic_set(&t_flag, 1); atomic_set(&mv_flag, 1); +<<<<<<< HEAD mutex_unlock(&akmd_lock); +======= +>>>>>>> 8988ee2... sensors: akm8973: Add HTC-variant driver. } static int akm_aot_open(struct inode *inode, struct file *file) @@ -341,7 +350,10 @@ static int akm_aot_release(struct inode *inode, struct file *file) atomic_set(&open_count, 0); wake_up(&open_wq); disable_irq(this_client->irq); +<<<<<<< HEAD mutex_unlock(&akmd_lock); +======= +>>>>>>> 8988ee2... sensors: akm8973: Add HTC-variant driver. return 0; } diff --git a/drivers/misc/akm8973_htc.c b/drivers/misc/akm8973_htc.c new file mode 100644 index 0000000000000..a7e6ccbc8eb6d --- /dev/null +++ b/drivers/misc/akm8973_htc.c @@ -0,0 +1,795 @@ +/* drivers/i2c/chips/akm8973.c - akm8973 compass driver + * + * Copyright (C) 2008-2009 HTC Corporation. + * + * 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 + +#define DEBUG 0 +#define MAX_FAILURE_COUNT 3 + +static struct i2c_client *this_client; + +struct akm8973_data { + struct input_dev *input_dev; + struct work_struct work; + struct early_suspend early_suspend_akm; +}; + +/* Addresses to scan -- protected by sense_data_mutex */ +static char sense_data[RBUFF_SIZE + 1]; +static struct mutex sense_data_mutex; +#define AKM8973_RETRY_COUNT 10 +static DECLARE_WAIT_QUEUE_HEAD(data_ready_wq); +static DECLARE_WAIT_QUEUE_HEAD(open_wq); + +static atomic_t data_ready; +static atomic_t open_count; +static atomic_t open_flag; +static atomic_t reserve_open_flag; + +static atomic_t m_flag; +static atomic_t a_flag; +static atomic_t t_flag; +static atomic_t mv_flag; + +static int failure_count = 0; + +static short akmd_delay = 0; + +static atomic_t suspend_flag = ATOMIC_INIT(0); + +static struct akm8973_platform_data *pdata; + +static int AKI2C_RxData(char *rxData, int length) +{ + uint8_t loop_i; + struct i2c_msg msgs[] = { + { + .addr = this_client->addr, + .flags = 0, + .len = 1, + .buf = rxData, + }, + { + .addr = this_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = rxData, + }, + }; + + for (loop_i = 0; loop_i < AKM8973_RETRY_COUNT; loop_i++) { + if (i2c_transfer(this_client->adapter, msgs, 2) > 0) { + break; + } + mdelay(10); + } + + if (loop_i >= AKM8973_RETRY_COUNT) { + printk(KERN_ERR "%s retry over %d\n", __func__, AKM8973_RETRY_COUNT); + return -EIO; + } + return 0; +} + +static int AKI2C_TxData(char *txData, int length) +{ + uint8_t loop_i; + struct i2c_msg msg[] = { + { + .addr = this_client->addr, + .flags = 0, + .len = length, + .buf = txData, + }, + }; + + for (loop_i = 0; loop_i < AKM8973_RETRY_COUNT; loop_i++) { + if (i2c_transfer(this_client->adapter, msg, 1) > 0) { + break; + } + mdelay(10); + } + + if (loop_i >= AKM8973_RETRY_COUNT) { + printk(KERN_ERR "%s retry over %d\n", __func__, AKM8973_RETRY_COUNT); + return -EIO; + } + return 0; +} + +static void AKECS_Reset(void) +{ + gpio_set_value(pdata->reset, 0); + udelay(120); + gpio_set_value(pdata->reset, 1); +} + +static int AKECS_StartMeasure(void) +{ + char buffer[2]; + + atomic_set(&data_ready, 0); + + /* Set measure mode */ + buffer[0] = AKECS_REG_MS1; + buffer[1] = AKECS_MODE_MEASURE; + + /* Set data */ + return AKI2C_TxData(buffer, 2); +} + +static int AKECS_PowerDown(void) +{ + char buffer[2]; + int ret; + + /* Set powerdown mode */ + buffer[0] = AKECS_REG_MS1; + buffer[1] = AKECS_MODE_POWERDOWN; + /* Set data */ + ret = AKI2C_TxData(buffer, 2); + if (ret < 0) + return ret; + + /* Dummy read for clearing INT pin */ + buffer[0] = AKECS_REG_TMPS; + /* Read data */ + ret = AKI2C_RxData(buffer, 1); + if (ret < 0) + return ret; + return ret; +} + +static int AKECS_StartE2PRead(void) +{ + char buffer[2]; + + /* Set measure mode */ + buffer[0] = AKECS_REG_MS1; + buffer[1] = AKECS_MODE_E2P_READ; + /* Set data */ + return AKI2C_TxData(buffer, 2); +} + +static int AKECS_GetData(void) +{ + char buffer[RBUFF_SIZE + 1]; + int ret; + + memset(buffer, 0, RBUFF_SIZE + 1); + buffer[0] = AKECS_REG_ST; + ret = AKI2C_RxData(buffer, RBUFF_SIZE+1); + if (ret < 0) + return ret; + + mutex_lock(&sense_data_mutex); + memcpy(sense_data, buffer, sizeof(buffer)); + atomic_set(&data_ready, 1); + wake_up(&data_ready_wq); + mutex_unlock(&sense_data_mutex); + + return 0; +} + +static int AKECS_SetMode(char mode) +{ + int ret; + + switch (mode) { + case AKECS_MODE_MEASURE: + ret = AKECS_StartMeasure(); + break; + case AKECS_MODE_E2P_READ: + ret = AKECS_StartE2PRead(); + break; + case AKECS_MODE_POWERDOWN: + ret = AKECS_PowerDown(); + break; + default: + return -EINVAL; + } + + /* wait at least 300us after changing mode */ + mdelay(1); + return ret; +} + +static int AKECS_TransRBuff(char *rbuf, int size) +{ + wait_event_interruptible_timeout(data_ready_wq, + atomic_read(&data_ready), 1000); + if (!atomic_read(&data_ready)) { + if (!atomic_read(&suspend_flag)) { + printk(KERN_ERR + "AKM8973 AKECS_TransRBUFF: Data not ready\n"); + failure_count++; + if (failure_count >= MAX_FAILURE_COUNT) { + printk(KERN_ERR + "AKM8973 AKECS_TransRBUFF: successive %d failure.\n", + failure_count); + atomic_set(&open_flag, -1); + wake_up(&open_wq); + failure_count = 0; + } + } + return -1; + } + + mutex_lock(&sense_data_mutex); + memcpy(&rbuf[1], &sense_data[1], size); + atomic_set(&data_ready, 0); + mutex_unlock(&sense_data_mutex); + + failure_count = 0; + return 0; +} + + +static void AKECS_Report_Value(short *rbuf) +{ + struct akm8973_data *data = i2c_get_clientdata(this_client); +#if DEBUG + printk(KERN_INFO"AKECS_Report_Value: yaw = %d, pitch = %d, roll = %d\n", rbuf[0], + rbuf[1], rbuf[2]); + printk(KERN_INFO" tmp = %d, m_stat= %d, g_stat=%d\n", rbuf[3], + rbuf[4], rbuf[5]); + printk(KERN_INFO" G_Sensor: x = %d LSB, y = %d LSB, z = %d LSB\n", + rbuf[6], rbuf[7], rbuf[8]); +#endif + /* Report magnetic sensor information */ + if (atomic_read(&m_flag)) { + input_report_abs(data->input_dev, ABS_RX, rbuf[0]); + input_report_abs(data->input_dev, ABS_RY, rbuf[1]); + input_report_abs(data->input_dev, ABS_RZ, rbuf[2]); + input_report_abs(data->input_dev, ABS_RUDDER, rbuf[4]); + } + + /* Report acceleration sensor information */ + if (atomic_read(&a_flag)) { + input_report_abs(data->input_dev, ABS_X, rbuf[6]); + input_report_abs(data->input_dev, ABS_Y, rbuf[7]); + input_report_abs(data->input_dev, ABS_Z, rbuf[8]); + input_report_abs(data->input_dev, ABS_WHEEL, rbuf[5]); + } + + /* Report temperature information */ + if (atomic_read(&t_flag)) + input_report_abs(data->input_dev, ABS_THROTTLE, rbuf[3]); + + if (atomic_read(&mv_flag)) { + input_report_abs(data->input_dev, ABS_HAT0X, rbuf[9]); + input_report_abs(data->input_dev, ABS_HAT0Y, rbuf[10]); + input_report_abs(data->input_dev, ABS_BRAKE, rbuf[11]); + } + + input_sync(data->input_dev); +} + +static int AKECS_GetOpenStatus(void) +{ + wait_event_interruptible(open_wq, (atomic_read(&open_flag) != 0)); + return atomic_read(&open_flag); +} + +static int AKECS_GetCloseStatus(void) +{ + wait_event_interruptible(open_wq, (atomic_read(&open_flag) <= 0)); + return atomic_read(&open_flag); +} + +static void AKECS_CloseDone(void) +{ + atomic_set(&m_flag, 0); + atomic_set(&a_flag, 0); + atomic_set(&t_flag, 0); + atomic_set(&mv_flag, 0); +} + +static int akm_aot_open(struct inode *inode, struct file *file) +{ + int ret = -1; + if (atomic_cmpxchg(&open_count, 0, 1) == 0) { + if (atomic_cmpxchg(&open_flag, 0, 1) == 0) { + atomic_set(&reserve_open_flag, 1); + wake_up(&open_wq); + ret = 0; + } + } + return ret; +} + +static int akm_aot_release(struct inode *inode, struct file *file) +{ + atomic_set(&reserve_open_flag, 0); + atomic_set(&open_flag, 0); + atomic_set(&open_count, 0); + wake_up(&open_wq); + return 0; +} + +static int +akm_aot_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + short flag; + + switch (cmd) { + case ECS_IOCTL_APP_SET_MFLAG: + case ECS_IOCTL_APP_SET_AFLAG: + case ECS_IOCTL_APP_SET_TFLAG: + case ECS_IOCTL_APP_SET_MVFLAG: + if (copy_from_user(&flag, argp, sizeof(flag))) + return -EFAULT; + if (flag < 0 || flag > 1) + return -EINVAL; + break; + case ECS_IOCTL_APP_SET_DELAY: + if (copy_from_user(&flag, argp, sizeof(flag))) + return -EFAULT; + break; + default: + break; + } + + switch (cmd) { + case ECS_IOCTL_APP_SET_MFLAG: + atomic_set(&m_flag, flag); + break; + case ECS_IOCTL_APP_GET_MFLAG: + flag = atomic_read(&m_flag); + break; + case ECS_IOCTL_APP_SET_AFLAG: + atomic_set(&a_flag, flag); + break; + case ECS_IOCTL_APP_GET_AFLAG: + flag = atomic_read(&a_flag); + break; + case ECS_IOCTL_APP_SET_TFLAG: + atomic_set(&t_flag, flag); + break; + case ECS_IOCTL_APP_GET_TFLAG: + flag = atomic_read(&t_flag); + break; + case ECS_IOCTL_APP_SET_MVFLAG: + atomic_set(&mv_flag, flag); + break; + case ECS_IOCTL_APP_GET_MVFLAG: + flag = atomic_read(&mv_flag); + break; + case ECS_IOCTL_APP_SET_DELAY: + akmd_delay = flag; + break; + case ECS_IOCTL_APP_GET_DELAY: + flag = akmd_delay; + break; + default: + return -ENOTTY; + } + + switch (cmd) { + case ECS_IOCTL_APP_GET_MFLAG: + case ECS_IOCTL_APP_GET_AFLAG: + case ECS_IOCTL_APP_GET_TFLAG: + case ECS_IOCTL_APP_GET_MVFLAG: + case ECS_IOCTL_APP_GET_DELAY: + if (copy_to_user(argp, &flag, sizeof(flag))) + return -EFAULT; + break; + default: + break; + } + + return 0; +} + +static int akmd_open(struct inode *inode, struct file *file) +{ + return nonseekable_open(inode, file); +} + +static int akmd_release(struct inode *inode, struct file *file) +{ + AKECS_CloseDone(); + return 0; +} + +static int +akmd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + + void __user *argp = (void __user *)arg; + + char msg[RBUFF_SIZE + 1], rwbuf[5]; + int ret = -1, status; + short mode, value[12], delay; + char project_name[64]; + short layouts[4][3][3]; + int i, j, k; + + + switch (cmd) { + case ECS_IOCTL_WRITE: + case ECS_IOCTL_READ: + if (copy_from_user(&rwbuf, argp, sizeof(rwbuf))) + return -EFAULT; + break; + case ECS_IOCTL_SET_MODE: + if (copy_from_user(&mode, argp, sizeof(mode))) + return -EFAULT; + break; + case ECS_IOCTL_SET_YPR: + if (copy_from_user(&value, argp, sizeof(value))) + return -EFAULT; + break; + default: + break; + } + + switch (cmd) { + case ECS_IOCTL_WRITE: + if (rwbuf[0] < 2) + return -EINVAL; + ret = AKI2C_TxData(&rwbuf[1], rwbuf[0]); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_READ: + if (rwbuf[0] < 1) + return -EINVAL; + ret = AKI2C_RxData(&rwbuf[1], rwbuf[0]); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_RESET: + AKECS_Reset(); + break; + case ECS_IOCTL_SET_MODE: + ret = AKECS_SetMode((char)mode); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_GETDATA: + ret = AKECS_TransRBuff(msg, RBUFF_SIZE); + if (ret < 0) + return ret; + break; + case ECS_IOCTL_SET_YPR: + AKECS_Report_Value(value); + break; + case ECS_IOCTL_GET_OPEN_STATUS: + status = AKECS_GetOpenStatus(); + break; + case ECS_IOCTL_GET_CLOSE_STATUS: + status = AKECS_GetCloseStatus(); + break; + case ECS_IOCTL_GET_DELAY: + delay = akmd_delay; + break; + case ECS_IOCTL_GET_PROJECT_NAME: + strncpy(project_name, pdata->project_name, 64); + break; + case ECS_IOCTL_GET_MATRIX: + for (i = 0; i < 4; i++) + for (j = 0; j < 3; j++) + for (k = 0; k < 3; k++) + layouts[i][j][k] = + pdata->layouts[i][j][k]; + break; + default: + return -ENOTTY; + } + + switch (cmd) { + case ECS_IOCTL_READ: + if (copy_to_user(argp, &rwbuf, sizeof(rwbuf))) + return -EFAULT; + break; + case ECS_IOCTL_GETDATA: + if (copy_to_user(argp, &msg, sizeof(msg))) + return -EFAULT; + break; + case ECS_IOCTL_GET_OPEN_STATUS: + case ECS_IOCTL_GET_CLOSE_STATUS: + if (copy_to_user(argp, &status, sizeof(status))) + return -EFAULT; + break; + case ECS_IOCTL_GET_DELAY: + if (copy_to_user(argp, &delay, sizeof(delay))) + return -EFAULT; + break; + case ECS_IOCTL_GET_PROJECT_NAME: + if (copy_to_user(argp, project_name, sizeof(project_name))) + return -EFAULT; + break; + case ECS_IOCTL_GET_MATRIX: + if (copy_to_user(argp, layouts, sizeof(layouts))) + return -EFAULT; + break; + default: + break; + } + + return 0; +} + +static void akm_work_func(struct work_struct *work) +{ + if (AKECS_GetData() < 0) + printk(KERN_ERR "AKM8973 akm_work_func: Get data failed\n"); + enable_irq(this_client->irq); +} + +static irqreturn_t akm8973_interrupt(int irq, void *dev_id) +{ + struct akm8973_data *data = dev_id; + disable_irq_nosync(this_client->irq); + schedule_work(&data->work); + return IRQ_HANDLED; +} + +static void akm8973_early_suspend(struct early_suspend *handler) +{ + atomic_set(&suspend_flag, 1); + atomic_set(&reserve_open_flag, atomic_read(&open_flag)); + atomic_set(&open_flag, 0); + wake_up(&open_wq); + disable_irq(this_client->irq); +} + +static void akm8973_early_resume(struct early_suspend *handler) +{ + enable_irq(this_client->irq); + atomic_set(&suspend_flag, 0); + atomic_set(&open_flag, atomic_read(&reserve_open_flag)); + wake_up(&open_wq); +} + +static struct file_operations akmd_fops = { + .owner = THIS_MODULE, + .open = akmd_open, + .release = akmd_release, + .ioctl = akmd_ioctl, +}; + +static struct file_operations akm_aot_fops = { + .owner = THIS_MODULE, + .open = akm_aot_open, + .release = akm_aot_release, + .ioctl = akm_aot_ioctl, +}; + + +static struct miscdevice akm_aot_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "akm8973_aot", + .fops = &akm_aot_fops, +}; + + +static struct miscdevice akmd_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "akm8973_daemon", + .fops = &akmd_fops, +}; + +int akm8973_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct akm8973_data *akm; + int err = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + err = -ENODEV; + goto exit_check_functionality_failed; + } + + akm = kzalloc(sizeof(struct akm8973_data), GFP_KERNEL); + if (!akm) { + err = -ENOMEM; + goto exit_alloc_data_failed; + } + + INIT_WORK(&akm->work, akm_work_func); + i2c_set_clientdata(client, akm); + + pdata = client->dev.platform_data; + if (pdata == NULL) { + printk(KERN_ERR"AKM8973 akm8973_probe: platform data is NULL\n"); + goto exit_platform_data_null; + } + this_client = client; + + if (pdata && pdata->reset) { + err = gpio_request(pdata->reset, "akm8973"); + if (err < 0) { + printk(KERN_ERR "%s: request reset gpio failed\n", + __func__); + goto err_request_reset_gpio; + } + err = gpio_direction_output(pdata->reset, 1); + if (err < 0) { + printk(KERN_ERR + "%s: request reset gpio failed\n", __func__); + goto err_set_reset_gpio; + } + } else { + printk(KERN_ERR "%s: pdata or pdata->reset is NULL\n", + __func__); + goto err_request_reset_gpio; + } + + err = AKECS_PowerDown(); + if (err < 0) { + printk(KERN_ERR"AKM8973 akm8973_probe: set power down mode error\n"); + goto exit_set_mode_failed; + } + + err = request_irq(client->irq, akm8973_interrupt, IRQF_TRIGGER_HIGH, + "akm8973", akm); + + if (err < 0) { + printk(KERN_ERR"AKM8973 akm8973_probe: request irq failed\n"); + goto exit_irq_request_failed; + } + + akm->input_dev = input_allocate_device(); + + if (!akm->input_dev) { + err = -ENOMEM; + printk(KERN_ERR + "AKM8973 akm8973_probe: Failed to allocate input device\n"); + goto exit_input_dev_alloc_failed; + } + + set_bit(EV_ABS, akm->input_dev->evbit); + /* yaw */ + input_set_abs_params(akm->input_dev, ABS_RX, 0, 360, 0, 0); + /* pitch */ + input_set_abs_params(akm->input_dev, ABS_RY, -180, 180, 0, 0); + /* roll */ + input_set_abs_params(akm->input_dev, ABS_RZ, -90, 90, 0, 0); + /* x-axis acceleration */ + input_set_abs_params(akm->input_dev, ABS_X, -1872, 1872, 0, 0); + /* y-axis acceleration */ + input_set_abs_params(akm->input_dev, ABS_Y, -1872, 1872, 0, 0); + /* z-axis acceleration */ + input_set_abs_params(akm->input_dev, ABS_Z, -1872, 1872, 0, 0); + /* temparature */ + input_set_abs_params(akm->input_dev, ABS_THROTTLE, -30, 85, 0, 0); + /* status of magnetic sensor */ + input_set_abs_params(akm->input_dev, ABS_RUDDER, -32768, 3, 0, 0); + /* status of acceleration sensor */ + input_set_abs_params(akm->input_dev, ABS_WHEEL, -32768, 3, 0, 0); + /* step count */ + input_set_abs_params(akm->input_dev, ABS_GAS, 0, 65535, 0, 0); + /* x-axis of raw magnetic vector */ + input_set_abs_params(akm->input_dev, ABS_HAT0X, -2048, 2032, 0, 0); + /* y-axis of raw magnetic vector */ + input_set_abs_params(akm->input_dev, ABS_HAT0Y, -2048, 2032, 0, 0); + /* z-axis of raw magnetic vector */ + input_set_abs_params(akm->input_dev, ABS_BRAKE, -2048, 2032, 0, 0); + + akm->input_dev->name = "compass"; + + err = input_register_device(akm->input_dev); + + if (err) { + printk(KERN_ERR + "AKM8973 akm8973_probe: Unable to register input device: %s\n", + akm->input_dev->name); + goto exit_input_register_device_failed; + } + + err = misc_register(&akmd_device); + if (err) { + printk(KERN_ERR "AKM8973 akm8973_probe: akmd_device register failed\n"); + goto exit_misc_device_register_failed; + } + + err = misc_register(&akm_aot_device); + if (err) { + printk(KERN_ERR + "AKM8973 akm8973_probe: akm_aot_device register failed\n"); + goto exit_misc_device_register_failed; + } + + mutex_init(&sense_data_mutex); + + init_waitqueue_head(&data_ready_wq); + init_waitqueue_head(&open_wq); + + /* As default, do not report all information */ + atomic_set(&m_flag, 0); + atomic_set(&a_flag, 0); + atomic_set(&t_flag, 0); + atomic_set(&mv_flag, 0); + + akm->early_suspend_akm.suspend = akm8973_early_suspend; + akm->early_suspend_akm.resume = akm8973_early_resume; + register_early_suspend(&akm->early_suspend_akm); + + return 0; + +exit_misc_device_register_failed: +exit_input_register_device_failed: + input_free_device(akm->input_dev); +exit_input_dev_alloc_failed: + free_irq(client->irq, akm); +exit_irq_request_failed: +exit_set_mode_failed: +err_set_reset_gpio: + gpio_free(pdata->reset); +err_request_reset_gpio: +exit_platform_data_null: + kfree(akm); +exit_alloc_data_failed: +exit_check_functionality_failed: + return err; + +} + +static int akm8973_remove(struct i2c_client *client) +{ + struct akm8973_data *akm = i2c_get_clientdata(client); + free_irq(client->irq, akm); + input_unregister_device(akm->input_dev); + kfree(akm); + if (pdata && pdata->reset) + gpio_free(pdata->reset); + return 0; +} +static const struct i2c_device_id akm8973_id[] = { + { AKM8973_I2C_NAME, 0 }, + { } +}; + +static struct i2c_driver akm8973_driver = { + .probe = akm8973_probe, + .remove = akm8973_remove, + .id_table = akm8973_id, + .driver = { + .name = AKM8973_I2C_NAME, + }, +}; + +static int __init akm8973_init(void) +{ + printk(KERN_INFO "AKM8973 compass driver: init\n"); + return i2c_add_driver(&akm8973_driver); +} + +static void __exit akm8973_exit(void) +{ + i2c_del_driver(&akm8973_driver); +} + +module_init(akm8973_init); +module_exit(akm8973_exit); + +MODULE_AUTHOR("viral wang "); +MODULE_DESCRIPTION("AKM8973 compass driver"); +MODULE_LICENSE("GPL"); From e3c099967a7fff5c346db4f7a30d486147944ce1 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Mon, 22 Nov 2010 11:40:46 -0500 Subject: [PATCH 1039/2556] supersonic: Fix AC charging * Remove the smb329 driver which is not used on Supersonic * Add the tps65200 switch charger driver * Update HTC gadget and battery code --- arch/arm/mach-msm/Makefile | 1 - arch/arm/mach-msm/board-supersonic-smb329.c | 178 ---------- arch/arm/mach-msm/board-supersonic-smb329.h | 32 -- arch/arm/mach-msm/htc_battery.c | 166 ++++++++-- arch/arm/mach-msm/include/mach/htc_battery.h | 13 +- drivers/mfd/Kconfig | 9 + drivers/mfd/Makefile | 1 + drivers/mfd/tps65200.c | 324 +++++++++++++++++++ drivers/usb/gadget/msm72k_udc_htc.c | 55 +--- include/linux/{smb329.h => tps65200.h} | 10 +- 10 files changed, 502 insertions(+), 287 deletions(-) delete mode 100755 arch/arm/mach-msm/board-supersonic-smb329.c delete mode 100644 arch/arm/mach-msm/board-supersonic-smb329.h create mode 100644 drivers/mfd/tps65200.c rename include/linux/{smb329.h => tps65200.h} (78%) diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 782bb8914ca32..354484f9f3fb6 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -103,7 +103,6 @@ obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-wifi.o htc_awb_cal.o obj-$(CONFIG_MACH_SUPERSONIC) += msm_vibrator.o obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-microp.o obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-tpa2018d1.o -obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-smb329.o obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-tpa6130.o obj-$(CONFIG_MACH_SUPERSONIC) += htc_wifi_nvs.o htc_bluetooth.o obj-$(CONFIG_MACH_SUPERSONIC) += htc_acoustic_qsd.o diff --git a/arch/arm/mach-msm/board-supersonic-smb329.c b/arch/arm/mach-msm/board-supersonic-smb329.c deleted file mode 100755 index 5167ba93c6b92..0000000000000 --- a/arch/arm/mach-msm/board-supersonic-smb329.c +++ /dev/null @@ -1,178 +0,0 @@ -/* drivers/i2c/chips/smb329.c - * - * SMB329B Switch Charger (SUMMIT Microelectronics) - * - * Copyright (C) 2009 HTC Corporation - * Author: Justin Lin - * - * 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 "board-supersonic-smb329.h" - -static struct smb329_data { - struct i2c_client *client; - uint8_t version; - struct work_struct work; - struct mutex state_lock; - int chg_state; -} smb329; - -static int smb329_i2c_write(uint8_t *value, uint8_t reg, uint8_t num_bytes) -{ - int ret; - struct i2c_msg msg; - - /* write the first byte of buffer as the register address */ - value[0] = reg; - msg.addr = smb329.client->addr; - msg.len = num_bytes + 1; - msg.flags = 0; - msg.buf = value; - - ret = i2c_transfer(smb329.client->adapter, &msg, 1); - - return (ret >= 0) ? 0 : ret; -} - -static int smb329_i2c_read(uint8_t *value, uint8_t reg, uint8_t num_bytes) -{ - int ret; - struct i2c_msg msg[2]; - - /* setup the address to read */ - msg[0].addr = smb329.client->addr; - msg[0].len = 1; - msg[0].flags = 0; - msg[0].buf = ® - - /* setup the read buffer */ - msg[1].addr = smb329.client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = num_bytes; - msg[1].buf = value; - - ret = i2c_transfer(smb329.client->adapter, msg, 2); - - return (ret >= 0) ? 0 : ret; -} - -static int smb329_i2c_write_byte(uint8_t value, uint8_t reg) -{ - int ret; - uint8_t buf[2] = { 0 }; - - buf[1] = value; - ret = smb329_i2c_write(buf, reg, 1); - if (ret) - pr_err("smb329: write byte error (%d)\n", ret); - - return ret; -} - -static int smb329_i2c_read_byte(uint8_t *value, uint8_t reg) -{ - int ret = smb329_i2c_read(value, reg, 1); - if (ret) - pr_err("smb329: read byte error (%d)\n", ret); - - return ret; -} - -int smb329_set_charger_ctrl(uint32_t ctl) -{ - mutex_lock(&smb329.state_lock); - smb329.chg_state = ctl; - schedule_work(&smb329.work); - mutex_unlock(&smb329.state_lock); - return 0; -} - -static void smb329_work_func(struct work_struct *work) -{ - mutex_lock(&smb329.state_lock); - - switch (smb329.chg_state) { - case SMB329_ENABLE_FAST_CHG: - pr_info("smb329: charger on (fast)\n"); - smb329_i2c_write_byte(0x84, 0x31); - smb329_i2c_write_byte(0xD0, 0x01); - smb329_i2c_write_byte(0x08, 0x05); - if ((smb329.version & 0x18) == 0x0) - smb329_i2c_write_byte(0xA9, 0x00); - break; - - case SMB329_DISABLE_CHG: - case SMB329_ENABLE_SLOW_CHG: - pr_info("smb329: charger off/slow\n"); - smb329_i2c_write_byte(0x88, 0x31); - smb329_i2c_write_byte(0x08, 0x05); - break; - default: - pr_err("smb329: unknown charger state %d\n", - smb329.chg_state); - } - - mutex_unlock(&smb329.state_lock); -} - -static int smb329_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { - dev_dbg(&client->dev, "[SMB329]:I2C fail\n"); - return -EIO; - } - - smb329.client = client; - mutex_init(&smb329.state_lock); - INIT_WORK(&smb329.work, smb329_work_func); - - smb329_i2c_read_byte(&smb329.version, 0x3B); - pr_info("smb329 version: 0x%02x\n", smb329.version); - - return 0; -} - -static const struct i2c_device_id smb329_id[] = { - { "smb329", 0 }, - { }, -}; - -static struct i2c_driver smb329_driver = { - .driver.name = "smb329", - .id_table = smb329_id, - .probe = smb329_probe, -}; - -static int __init smb329_init(void) -{ - int ret = i2c_add_driver(&smb329_driver); - if (ret) - pr_err("smb329_init: failed\n"); - - return ret; -} - -module_init(smb329_init); - -MODULE_AUTHOR("Justin Lin "); -MODULE_DESCRIPTION("SUMMIT Microelectronics SMB329B switch charger"); -MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-supersonic-smb329.h b/arch/arm/mach-msm/board-supersonic-smb329.h deleted file mode 100644 index 13b326fa71dfa..0000000000000 --- a/arch/arm/mach-msm/board-supersonic-smb329.h +++ /dev/null @@ -1,32 +0,0 @@ -/* include/linux/smb329.h - smb329 switch charger driver - * - * Copyright (C) 2009 HTC Corporation. - * - * 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_SMB329_H -#define _LINUX_SMB329_H - -#ifdef __KERNEL__ - -enum { - SMB329_DISABLE_CHG, - SMB329_ENABLE_SLOW_CHG, - SMB329_ENABLE_FAST_CHG, -}; - -extern int smb329_set_charger_ctrl(uint32_t ctl); - -#endif /* __KERNEL__ */ - -#endif /* _LINUX_SMB329_H */ - diff --git a/arch/arm/mach-msm/htc_battery.c b/arch/arm/mach-msm/htc_battery.c index 2426d1b6cd0a4..0075583fa8dec 100644 --- a/arch/arm/mach-msm/htc_battery.c +++ b/arch/arm/mach-msm/htc_battery.c @@ -30,6 +30,8 @@ #include /* Jay, to register display notifier */ #include #include +#include +#include #ifdef CONFIG_HTC_BATTCHG_SMEM #include "smd_private.h" #endif @@ -43,8 +45,6 @@ #include #endif -#include - static struct wake_lock vbus_wake_lock; enum { @@ -185,11 +185,11 @@ static struct power_supply htc_power_supplies[] = { }; static int update_batt_info(void); -extern void notify_usb_connected(int online); +static void usb_status_notifier_func(int online); //static int g_usb_online; static struct t_usb_status_notifier usb_status_notifier = { .name = "htc_battery", - .func = notify_usb_connected, + .func = usb_status_notifier_func, }; /* Move cable detection/notification to standard PMIC RPC. */ @@ -445,7 +445,7 @@ static int htc_set_smem_cable_type(u32 cable_type) { return -1; } #endif #if 1 //JH //this is for packet filter (notify port list while USB in/out) int update_port_list_charging_state(int enable); -#endif +#endif static int htc_cable_status_update(int status) { int rc = 0; @@ -598,7 +598,7 @@ EXPORT_SYMBOL(htc_get_usb_accessory_adc_level); /* A9 reports USB charging when helf AC cable in and China AC charger. */ /* notify userspace USB charging first, and then usb driver will notify AC while D+/D- Line short. */ -void notify_usb_connected(int online) +static void usb_status_notifier_func(int online) { #if 1 pr_info("batt:online=%d",online); @@ -611,9 +611,13 @@ void notify_usb_connected(int online) blocking_notifier_call_chain(&cable_status_notifier_list, htc_batt_info.rep.charging_source, NULL); - power_supply_changed(&htc_power_supplies[CHARGER_AC]); - power_supply_changed(&htc_power_supplies[CHARGER_USB]); - power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + if (htc_battery_initial) { + power_supply_changed(&htc_power_supplies[CHARGER_AC]); + power_supply_changed(&htc_power_supplies[CHARGER_USB]); + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + } else { + pr_err("\n\n ### htc_battery_code is not inited yet! ###\n\n"); + } update_wake_lock(htc_batt_info.rep.charging_source); #else mutex_lock(&htc_batt_info.lock); @@ -981,13 +985,17 @@ static int htc_battery_get_charging_status(void) break; case CHARGER_USB: case CHARGER_AC: +#if !defined(CONFIG_BATTERY_DS2746) if ((htc_charge_full) && (htc_batt_info.rep.full_level == 100)) { htc_batt_info.rep.level = 100; } - +#endif level = htc_batt_info.rep.level; if (level == 100){ htc_charge_full = 1;} + else + htc_charge_full = 0; + if (htc_charge_full) ret = POWER_SUPPLY_STATUS_FULL; else if (htc_batt_info.rep.charging_enabled != 0) @@ -1014,12 +1022,20 @@ static int htc_battery_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_HEALTH: val->intval = POWER_SUPPLY_HEALTH_GOOD; - if (machine_is_paradise() && (htc_batt_info.rep.batt_temp >= 500 || - htc_batt_info.rep.batt_temp <= 0)) - val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; - else if (!machine_is_paradise() && (htc_batt_info.rep.batt_temp >= 480 || - htc_batt_info.rep.batt_temp <= 0)) - val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + if (machine_is_paradise()) { + if (htc_batt_info.rep.batt_temp >= 500 || + htc_batt_info.rep.batt_temp <= 0) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + } else if (machine_is_spade()) { + if (htc_batt_info.rep.batt_temp >= 450 || + htc_batt_info.rep.batt_temp <= 0) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + } else { + if (htc_batt_info.rep.batt_temp >= 480 || + htc_batt_info.rep.batt_temp <= 0) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + } + if (htc_batt_debug_mask & HTC_BATT_DEBUG_USER_QUERY) BATT_LOG("%s: %s: health=%d", __func__, psy->name, val->intval); break; @@ -1070,9 +1086,8 @@ static struct device_attribute htc_battery_attrs[] = { #ifdef CONFIG_HTC_BATTCHG_SMEM __ATTR(smem_raw, S_IRUGO, htc_battery_show_smem, NULL), __ATTR(smem_text, S_IRUGO, htc_battery_show_smem, NULL), -#else - __ATTR(batt_attr_text, S_IRUGO, htc_battery_show_batt_attr, NULL), #endif + __ATTR(batt_attr_text, S_IRUGO, htc_battery_show_batt_attr, NULL), }; enum { @@ -1194,7 +1209,6 @@ static ssize_t htc_battery_set_full_level(struct device *dev, { int rc = 0; unsigned long percent = 100; - unsigned long param = 0; percent = simple_strtoul(buf, NULL, 10); @@ -1214,9 +1228,8 @@ static ssize_t htc_battery_set_full_level(struct device *dev, mutex_lock(&htc_batt_info.lock); htc_full_level_flag = 1; htc_batt_info.rep.full_level = percent; - param = percent; blocking_notifier_call_chain(&cable_status_notifier_list, - 0xff, (void *) ¶m); + 0xff, (void *) &htc_batt_info.rep.full_level); mutex_unlock(&htc_batt_info.lock); } rc = 0; @@ -1288,7 +1301,7 @@ static int update_batt_info(void) ret = -1; } break; -#elif CONFIG_BATTERY_DS2746 +#elif defined(CONFIG_BATTERY_DS2746) case GUAGE_DS2746: if (ds2746_get_battery_info(&htc_batt_info.rep)) { BATT_ERR("%s: ds2746 read failed!!!", __func__); @@ -1378,6 +1391,89 @@ static ssize_t htc_battery_show_property(struct device *dev, return i; } +static irqreturn_t tps65200_int_detection(int irq, void *data) +{ + struct htc_battery_tps65200_int *ip = data; + + BATT_LOG("%s: over voltage is detected.", __func__); + + disable_irq_nosync(ip->chg_int); + + ip->tps65200_reg = 0; + + schedule_delayed_work(&ip->int_work, msecs_to_jiffies(200)); + + return IRQ_HANDLED; +} + +static void htc_battery_tps65200_int_func(struct work_struct *work) +{ + struct htc_battery_tps65200_int *ip; + int fault_bit; + ip = container_of(work, struct htc_battery_tps65200_int, + int_work.work); + + switch (ip->tps65200_reg) { + case CHECK_INT1: + /* read twice. First read to trigger TPS65200 clear fault bit + on INT1. Second read to make sure that fault bit is cleared + and call off ovp function.*/ + fault_bit = tps_set_charger_ctrl(CHECK_INT1); + BATT_LOG("INT1 value: %d", fault_bit); + fault_bit = tps_set_charger_ctrl(CHECK_INT1); + + if (fault_bit) { +#ifdef CONFIG_HTC_BATTCHG_SMEM + smem_batt_info->over_vchg = 1; +#else + htc_batt_info.rep.over_vchg = 1; +#endif + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + schedule_delayed_work(&ip->int_work, + msecs_to_jiffies(5000)); + BATT_LOG("OVER_VOLTAGE: " + "over voltage fault bit on TPS65200 is raised:" + " %d", fault_bit); + } else { +#ifdef CONFIG_HTC_BATTCHG_SMEM + smem_batt_info->over_vchg = 0; +#else + htc_batt_info.rep.over_vchg = 0; +#endif + cancel_delayed_work(&ip->int_work); + enable_irq(ip->chg_int); + } + break; + default: + fault_bit = tps_set_charger_ctrl(CHECK_INT2); + BATT_LOG("Read TPS65200 INT2 register value: %x", fault_bit); + if (fault_bit) { + fault_bit = tps_set_charger_ctrl(CHECK_INT2); + BATT_LOG("Read TPS65200 INT2 register value: %x" + , fault_bit); + fault_bit = tps_set_charger_ctrl(CHECK_INT2); + BATT_LOG("Read TPS65200 INT2 register value: %x" + , fault_bit); + fault_bit = tps_set_charger_ctrl(CHECK_CONTROL); +#ifdef CONFIG_HTC_BATTCHG_SMEM + smem_batt_info->reserve4 = 1; +#endif + cancel_delayed_work(&ip->int_work); + enable_irq(ip->chg_int); + } else { + fault_bit = tps_set_charger_ctrl(CHECK_INT1); + BATT_LOG("Read TPS65200 INT1 register value: %x" + , fault_bit); + if (fault_bit) { + ip->tps65200_reg = CHECK_INT1; + schedule_delayed_work(&ip->int_work, + msecs_to_jiffies(200)); + } + } + break; + } +} + static int htc_battery_core_probe(struct platform_device *pdev) { int i, rc; @@ -1535,8 +1631,8 @@ static int ds2784_notifier_func(struct notifier_block *nfb, case DS2784_CHARGING_CONTROL: if (htc_batt_info.charger == LINEAR_CHARGER) battery_charging_ctrl(arg); - else if(htc_batt_info.charger == SWITCH_CHARGER) - set_charger_ctrl(arg); +// else if(htc_batt_info.charger == SWITCH_CHARGER) +// set_charger_ctrl(arg); break; case DS2784_LEVEL_UPDATE: htc_battery_status_update(arg); @@ -1560,6 +1656,7 @@ static struct notifier_block ds2784_notifier = { static int htc_battery_probe(struct platform_device *pdev) { + int rc = 0; struct htc_battery_platform_data *pdata = pdev->dev.platform_data; htc_batt_info.device_id = pdev->id; @@ -1582,11 +1679,30 @@ static int htc_battery_probe(struct platform_device *pdev) #ifdef CONFIG_BATTERY_DS2784 if (pdata->guage_driver == GUAGE_DS2784) ds2784_register_notifier(&ds2784_notifier); -#elif CONFIG_BATTERY_DS2746 +#elif defined(CONFIG_BATTERY_DS2746) if (pdata->guage_driver == GUAGE_DS2746) ds2746_register_notifier(&ds2784_notifier); #endif + if (system_rev >= 1) { + if (pdata->int_data.chg_int) { + BATT_LOG("init over voltage interrupt detection."); + INIT_DELAYED_WORK(&pdata->int_data.int_work, + htc_battery_tps65200_int_func); + + rc = request_irq(pdata->int_data.chg_int, + tps65200_int_detection, + IRQF_TRIGGER_LOW, + "over_voltage_interrupt", + &pdata->int_data); + + if (rc) { + BATT_LOG("request irq failed"); + return rc; + } + } + } + return 0; } diff --git a/arch/arm/mach-msm/include/mach/htc_battery.h b/arch/arm/mach-msm/include/mach/htc_battery.h index 9b1b4d0ea73bc..fa9f6fd37bc70 100644 --- a/arch/arm/mach-msm/include/mach/htc_battery.h +++ b/arch/arm/mach-msm/include/mach/htc_battery.h @@ -22,7 +22,10 @@ #define SET_ICL500 0X65 #define SET_ICL100 0X66 #define CHECK_INT2 0X67 - +#define OVERTEMP_VREG_4060 0XC8 +#define NORMALTEMP_VREG_4200 0XC9 +#define CHECK_INT1 0XCA +#define CHECK_CONTROL 0xCB /* information about the system we're running on */ extern unsigned int system_rev; @@ -67,6 +70,13 @@ struct battery_info_reply { u32 over_vchg; /* 0:normal, 1:over voltage charger */ s32 eval_current; /* System loading current from ADC */ }; + +struct htc_battery_tps65200_int { + int chg_int; + int tps65200_reg; + struct delayed_work int_work; +}; + struct htc_battery_platform_data { int (*func_show_batt_attr)(struct device_attribute *attr, char *buf); @@ -77,6 +87,7 @@ struct htc_battery_platform_data { int guage_driver; int m2a_cable_detect; int charger; + struct htc_battery_tps65200_int int_data; }; #if CONFIG_HTC_BATTCHG diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 58d2a62e1d671..168cba49f1596 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -143,6 +143,15 @@ config TPS6507X This driver can also be built as a module. If so, the module will be called tps6507x. +config TPS65200 + tristate "tps65200 Driver" + depends on I2C + help + TPS65200 Switch charger implemented by HTC. + This could enable battery driver to set + Charging current 100mA~500mA or 500mA~1000mA + by GPIO or Register + config MENELAUS bool "Texas Instruments TWL92330/Menelaus PM chip" depends on I2C=y && ARCH_OMAP2 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index d534b8bb5a927..eff53307c8de6 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_TPS6507X) += tps6507x.o +obj-$(CONFIG_TPS65200) += tps65200.o obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o diff --git a/drivers/mfd/tps65200.c b/drivers/mfd/tps65200.c new file mode 100644 index 0000000000000..df2768b6bb1ac --- /dev/null +++ b/drivers/mfd/tps65200.c @@ -0,0 +1,324 @@ +/* drivers/i2c/chips/tps65200.c + * + * Copyright (C) 2009 HTC Corporation + * Author: Josh Hsiao + * + * 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 + +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +/** + * Insmod parameters + */ +I2C_CLIENT_INSMOD_1(tps65200); + +static int tps65200_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int tps65200_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info); +static int tps65200_remove(struct i2c_client *client); + + +/* Supersonic for Switch charger */ +struct tps65200_i2c_client { + struct i2c_client *client; + u8 address; + /* max numb of i2c_msg required is for read =2 */ + struct i2c_msg xfer_msg[2]; + /* To lock access to xfer_msg */ + struct mutex xfer_lock; +}; +static struct tps65200_i2c_client tps65200_i2c_module; +/** +Function:tps65200_i2c_write +Target: Write a byte to Switch charger +Timing: TBD +INPUT: value-> write value + reg -> reg offset + num-> number of byte to write +return :TRUE-->OK + FALSE-->Fail + */ +static int tps65200_i2c_write(u8 *value, u8 reg, u8 num_bytes) +{ + int ret; + struct tps65200_i2c_client *tps; + struct i2c_msg *msg; + + tps = &tps65200_i2c_module; + + mutex_lock(&tps->xfer_lock); + /* + * [MSG1]: fill the register address data + * fill the data Tx buffer + */ + msg = &tps->xfer_msg[0]; + msg->addr = tps->address; + msg->len = num_bytes + 1; + msg->flags = 0; + msg->buf = value; + /* over write the first byte of buffer with the register address */ + *value = reg; + ret = i2c_transfer(tps->client->adapter, tps->xfer_msg, 1); + mutex_unlock(&tps->xfer_lock); + + /* i2cTransfer returns num messages.translate it pls.. */ + if (ret >= 0) + ret = 0; + return ret; +} + + +/** +Function:tps65200_i2c_read +Target: Read a byte from Switch charger +Timing: TBD +INPUT: value-> store buffer + reg -> reg offset to read + num-> number of byte to read +return :TRUE-->OK + FALSE-->Fail + */ +static int tps65200_i2c_read(u8 *value, u8 reg, u8 num_bytes) +{ + int ret; + u8 val; + struct tps65200_i2c_client *tps; + struct i2c_msg *msg; + + tps = &tps65200_i2c_module; + + mutex_lock(&tps->xfer_lock); + /* [MSG1] fill the register address data */ + msg = &tps->xfer_msg[0]; + msg->addr = tps->address; + msg->len = 1; + msg->flags = 0; /* Read the register value */ + val = reg; + msg->buf = &val; + /* [MSG2] fill the data rx buffer */ + msg = &tps->xfer_msg[1]; + msg->addr = tps->address; + msg->flags = I2C_M_RD; /* Read the register value */ + msg->len = num_bytes; /* only n bytes */ + msg->buf = value; + ret = i2c_transfer(tps->client->adapter, tps->xfer_msg, 2); + mutex_unlock(&tps->xfer_lock); + + /* i2cTransfer returns num messages.translate it pls.. */ + if (ret >= 0) + ret = 0; + return ret; +} + + +/** +Function:tps65200_i2c_write_byte +Target: Write a byte from Switch charger +Timing: TBD +INPUT: value-> store buffer + reg -> reg offset to read +return :TRUE-->OK + FALSE-->Fail + */ +static int tps65200_i2c_write_byte(u8 value, u8 reg) +{ + /* 2 bytes offset 1 contains the data offset 0 is used by i2c_write */ + int result; + u8 temp_buffer[2] = { 0 }; + /* offset 1 contains the data */ + temp_buffer[1] = value; + result = tps65200_i2c_write(temp_buffer, reg, 1); + if (result != 0) + pr_info("TPS65200 I2C write fail = %d\n", result); + + return result; +} + +/** +Function:tps65200_i2c_read_byte +Target: Read a byte from Switch charger +Timing: TBD +INPUT: value-> store buffer + reg -> reg offset to read +return :TRUE-->OK + FALSE-->Fail + */ +static int tps65200_i2c_read_byte(u8 *value, u8 reg) +{ + int result = 0; + result = tps65200_i2c_read(value, reg, 1); + if (result != 0) + pr_info("TPS65200 I2C read fail = %d\n", result); + + return result; +} + +int tps_set_charger_ctrl(u32 ctl) +{ + int result = 0; + u8 version; + u8 status; + u8 regh; + + switch (ctl) { + case DISABLE: + pr_info("Switch charger OFF\n"); + tps65200_i2c_write_byte(0x29, 0x01); + tps65200_i2c_write_byte(0x28, 0x00); + tps65200_i2c_read_byte(&status, 0x09); + pr_info("TPS65200 INT2 %x\n", status); + break; + case ENABLE_SLOW_CHG: + pr_info("Switch charger ON (SLOW)\n"); + tps65200_i2c_write_byte(0x29, 0x01); + tps65200_i2c_write_byte(0x2A, 0x00); + tps65200_i2c_write_byte(0x86, 0x03); + break; + case ENABLE_FAST_CHG: + pr_info("Switch charger ON (FAST)\n"); + tps65200_i2c_write_byte(0x29, 0x01); + tps65200_i2c_write_byte(0x2A, 0x00); + tps65200_i2c_write_byte(0x86, 0x03); + tps65200_i2c_write_byte(0xA3, 0x02); + tps65200_i2c_read_byte(®h, 0x01); + pr_info("1.batt: Switch charger ON (FAST): regh 0x01=%x\n", regh); + tps65200_i2c_read_byte(®h, 0x00); + pr_info("2.batt: Switch charger ON (FAST): regh 0x00=%x\n", regh); + tps65200_i2c_read_byte(®h, 0x03); + pr_info("2.batt: Switch charger ON (FAST): regh 0x03=%x\n", regh); + tps65200_i2c_read_byte(®h, 0x02); + pr_info("2.batt: Switch charger ON (FAST): regh 0x02=%x\n", regh); + break; + case CHECK_CHG: + pr_info("Switch charger CHECK \n"); + tps65200_i2c_read_byte(&status, 0x06); + pr_info("TPS65200 STATUS_A%x\n", status); + break; + case SET_ICL500: + pr_info("Switch charger SET_ICL500 \n"); + tps65200_i2c_write_byte(0xA3, 0x02); + break; + case SET_ICL100: + pr_info("Switch charger SET_ICL100 \n"); + tps65200_i2c_write_byte(0x23, 0x02); + break; + case CHECK_INT2: + pr_info("Switch charger CHECK_INT2 \n"); + tps65200_i2c_read_byte(&status, 0x09); + pr_info("TPS65200 INT2 %x\n", status); + break; + default: + pr_info("%s: Not supported battery ctr called.!", __func__); + result = -EINVAL; + break; + } + + return result; +} +EXPORT_SYMBOL(tps_set_charger_ctrl); +static int cable_status_handler_func(struct notifier_block *nfb, + unsigned long action, void *param) +{ + u32 ctl = (u32)action; + pr_info("TPS65200 Switch charger set control%d\n", ctl); + tps_set_charger_ctrl(ctl); + + return NOTIFY_OK; +} + +static struct notifier_block cable_status_handler = { + .notifier_call = cable_status_handler_func, +}; + +static int tps65200_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) +{ + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA | + I2C_FUNC_SMBUS_BYTE)) + return -ENODEV; + + strlcpy(info->type, "tps65200", I2C_NAME_SIZE); + + return 0; +} + +static int tps65200_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tps65200_i2c_client *data = &tps65200_i2c_module; + + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { + dev_dbg(&client->dev, "[TPS65200]:I2C fail\n"); + return -EIO; + } + + register_notifier_cable_status(&cable_status_handler); + + data->address = client->addr; + data->client = client; + mutex_init(&data->xfer_lock); + pr_info("[TPS65200]: Driver registration done\n"); + return 0; +} + +static int tps65200_remove(struct i2c_client *client) +{ + struct tps65200_i2c_client *data = i2c_get_clientdata(client); + int idx; + if (data->client && data->client != client) + i2c_unregister_device(data->client); + tps65200_i2c_module.client = NULL; + return 0; +} +static const struct i2c_device_id tps65200_id[] = { + { "tps65200", 0 }, + { }, +}; +static struct i2c_driver tps65200_driver = { + .driver.name = "tps65200", + .id_table = tps65200_id, + .probe = tps65200_probe, + .remove = tps65200_remove, +}; + +static int __init sensors_tps65200_init(void) +{ + int res; + + res = i2c_add_driver(&tps65200_driver); + if (res) { + pr_info("[TPS65200]: Driver registration failed \n"); + return res; + } + return res; +} + +static void __exit sensors_tps65200_exit(void) +{ + i2c_del_driver(&tps65200_driver); +} + +MODULE_AUTHOR("Josh Hsiao "); +MODULE_DESCRIPTION("tps65200 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_tps65200_init); +module_exit(sensors_tps65200_exit); diff --git a/drivers/usb/gadget/msm72k_udc_htc.c b/drivers/usb/gadget/msm72k_udc_htc.c index f442659ed9520..bcbf95e17acfa 100644 --- a/drivers/usb/gadget/msm72k_udc_htc.c +++ b/drivers/usb/gadget/msm72k_udc_htc.c @@ -262,8 +262,6 @@ struct usb_info { void (*config_usb_id_gpios)(bool output_enable); /* 0: none, 1: carkit, 2: usb headset */ u8 accessory_type; - struct timer_list ac_detect_timer; - int ac_detect_count; }; static const struct usb_ep_ops msm72k_ep_ops; @@ -800,7 +798,6 @@ static void handle_setup(struct usb_info *ui) { u16 temp = 0; - temp = 1 << USB_DEVICE_SELF_POWERED; temp |= (ui->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); memcpy(req->buf, &temp, 2); @@ -1105,8 +1102,6 @@ static irqreturn_t usb_interrupt(int irq, void *data) if (ui->connect_type != CONNECT_TYPE_USB) { ui->connect_type = CONNECT_TYPE_USB; queue_work(ui->usb_wq, &ui->notifier_work); - ui->ac_detect_count = 0; - del_timer_sync(&ui->ac_detect_timer); } } @@ -1493,7 +1488,6 @@ static void usb_prepare(struct usb_info *ui) if (ret != 0) printk(KERN_WARNING "dev_attr_usb_mfg_carkit_enable failed\n"); #endif - ret = device_create_file(&ui->pdev->dev, &dev_attr_usb_car_kit_enable);/*for kar kit AP check if car kit enable*/ if (ret != 0) @@ -1765,7 +1759,7 @@ static void dock_detect_work(struct work_struct *w) value = gpio_get_value(ui->dock_pin_gpio); - if (value == 0) { + if (value == 0 && vbus) { set_irq_type(ui->dockpin_irq, IRQF_TRIGGER_HIGH); switch_set_state(&dock_switch, DOCK_STATE_DESK); ui->accessory_type = 3; @@ -2027,7 +2021,6 @@ static void charger_detect(struct usb_info *ui) ui->connect_type = CONNECT_TYPE_UNKNOWN; queue_delayed_work(ui->usb_wq, &ui->chg_work, DELAY_FOR_CHECK_CHG); - mod_timer(&ui->ac_detect_timer, jiffies + (3 * HZ)); } else { printk(KERN_INFO "usb: AC charger\n"); ui->connect_type = CONNECT_TYPE_AC; @@ -2128,8 +2121,6 @@ static void usb_do_work(struct work_struct *w) /* power down phy, clock down usb */ usb_lpm_enter(ui); - ui->ac_detect_count = 0; - del_timer_sync(&ui->ac_detect_timer); ui->state = USB_STATE_OFFLINE; usb_do_work_check_vbus(ui); @@ -2209,6 +2200,15 @@ void msm_hsusb_set_vbus_state(int online) /*configure uart pin to alternate function*/ if (ui->serial_debug_gpios) ui->serial_debug_gpios(1); +#ifdef CONFIG_DOCK_ACCESSORY_DETECT + if (ui->accessory_type == 3) { + set_irq_type(ui->dockpin_irq, IRQF_TRIGGER_LOW); + switch_set_state(&dock_switch, DOCK_STATE_UNDOCKED); + ui->accessory_type = 0; + printk(KERN_INFO "usb:dock: vbus offline\n"); + enable_irq(ui->dockpin_irq); + } +#endif } queue_work(ui->usb_wq, &ui->work); @@ -2610,37 +2610,6 @@ static ssize_t usb_remote_wakeup(struct device *dev, } static DEVICE_ATTR(wakeup, S_IWUSR, 0, usb_remote_wakeup); -static void ac_detect_expired(unsigned long _data) -{ - struct usb_info *ui = (struct usb_info *) _data; - u32 delay = 0; - - printk(KERN_INFO "%s: count = %d, connect_type = 0x%04x\n", __func__, - ui->ac_detect_count, ui->connect_type); - - if (ui->connect_type == CONNECT_TYPE_USB || ui->ac_detect_count >= 3) - return; - - /* detect shorted D+/D-, indicating AC power */ - if ((readl(USB_PORTSC) & PORTSC_LS) != PORTSC_LS) { - ui->ac_detect_count++; - /* detect delay: 3 sec, 5 sec, 10 sec */ - if (ui->ac_detect_count == 1) - delay = 5 * HZ; - else if (ui->ac_detect_count == 2) - delay = 10 * HZ; - - mod_timer(&ui->ac_detect_timer, jiffies + delay); - } else { - printk(KERN_INFO "usb: AC charger\n"); - ui->connect_type = CONNECT_TYPE_AC; - queue_work(ui->usb_wq, &ui->notifier_work); - writel(0x00080000, USB_USBCMD); - mdelay(10); - usb_lpm_enter(ui); - } -} - static int msm72k_probe(struct platform_device *pdev) { struct resource *res; @@ -2775,10 +2744,6 @@ static int msm72k_probe(struct platform_device *pdev) use_mfg_serialno = 0; strncpy(mfg_df_serialno, "000000000000", strlen("000000000000")); - ui->ac_detect_count = 0; - ui->ac_detect_timer.data = (unsigned long) ui; - ui->ac_detect_timer.function = ac_detect_expired; - init_timer(&ui->ac_detect_timer); return 0; } diff --git a/include/linux/smb329.h b/include/linux/tps65200.h similarity index 78% rename from include/linux/smb329.h rename to include/linux/tps65200.h index 8ce3440e0bc0e..6048cecb4f930 100644 --- a/include/linux/smb329.h +++ b/include/linux/tps65200.h @@ -10,14 +10,14 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ -#ifndef _SMB329_H_ -#define _SMB329_H_ +#ifndef _TPS65200_H_ +#define _TPS65200_H_ #include #include -#ifdef CONFIG_SMB329 -extern int set_charger_ctrl(u32 ctl); +#ifdef CONFIG_TPS65200 +extern int tps_set_charger_ctrl(u32 ctl); #else -static int set_charger_ctrl(u32 ctl) {return 0 ; } +static int tps_set_charger_ctrl(u32 ctl) {return 0 ; } #endif #endif From bdede93870563f9f74a6599ae5710b50690ac70b Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Thu, 30 Dec 2010 10:07:08 -0500 Subject: [PATCH 1040/2556] supersonic: Update for new camera drivers Change-Id: I1858db367ad2dfc469a131d110253fb8077b7683 --- .../arm/configs/cyanogen_supersonic_defconfig | 1143 +++++++++++------ arch/arm/mach-msm/board-supersonic.c | 227 ++-- 2 files changed, 870 insertions(+), 500 deletions(-) diff --git a/arch/arm/configs/cyanogen_supersonic_defconfig b/arch/arm/configs/cyanogen_supersonic_defconfig index b64e42e1e6a46..d814fde885d4f 100644 --- a/arch/arm/configs/cyanogen_supersonic_defconfig +++ b/arch/arm/configs/cyanogen_supersonic_defconfig @@ -1,30 +1,59 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.34.5 -# Mon Sep 6 22:59:00 2010 -# -CONFIG_ARM=y -CONFIG_SYS_SUPPORTS_APM_EMULATION=y -CONFIG_GENERIC_GPIO=y -CONFIG_GENERIC_TIME=y +# Linux/x86_64 2.6.37 Kernel Configuration +# Mon Jan 24 21:20:52 2011 +# +CONFIG_64BIT=y +# CONFIG_X86_32 is not set +CONFIG_X86_64=y +CONFIG_X86=y +CONFIG_INSTRUCTION_DECODER=y +CONFIG_OUTPUT_FORMAT="elf64-x86-64" +CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig" +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_CLOCKSOURCE_WATCHDOG=y CONFIG_GENERIC_CLOCKEVENTS=y -CONFIG_HAVE_PROC_CPU=y -CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y +CONFIG_LOCKDEP_SUPPORT=y CONFIG_STACKTRACE_SUPPORT=y CONFIG_HAVE_LATENCYTOP_SUPPORT=y -CONFIG_LOCKDEP_SUPPORT=y -CONFIG_TRACE_IRQFLAGS_SUPPORT=y -CONFIG_HARDIRQS_SW_RESEND=y -CONFIG_GENERIC_IRQ_PROBE=y -CONFIG_RWSEM_GENERIC_SPINLOCK=y -CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_MMU=y +CONFIG_ZONE_DMA=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_IOMAP=y +CONFIG_GENERIC_BUG=y +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_GPIO=y +CONFIG_ARCH_MAY_HAVE_PC_FDC=y +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y CONFIG_GENERIC_CALIBRATE_DELAY=y -CONFIG_NEED_DMA_MAP_STATE=y -CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y -CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_ARCH_HAS_CPU_RELAX=y +CONFIG_ARCH_HAS_DEFAULT_IDLE=y +CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y +CONFIG_HAVE_SETUP_PER_CPU_AREA=y +CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y +CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y +# CONFIG_HAVE_CPUMASK_OF_CPU_MAP is not set +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ZONE_DMA32=y +CONFIG_ARCH_POPULATES_NODE_MAP=y +CONFIG_AUDIT_ARCH=y +CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING=y +CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y +CONFIG_X86_TRAMPOLINE=y +CONFIG_ARCH_HWEIGHT_CFLAGS="-fcall-saved-rdi -fcall-saved-rsi -fcall-saved-rdx -fcall-saved-rcx -fcall-saved-r8 -fcall-saved-r9 -fcall-saved-r10 -fcall-saved-r11" +# CONFIG_KTIME_SCALAR is not set CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" CONFIG_CONSTRUCTORS=y +CONFIG_HAVE_IRQ_WORK=y +CONFIG_IRQ_WORK=y # # General setup @@ -33,9 +62,12 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_LOCK_KERNEL=y CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" CONFIG_LOCALVERSION="-cyanogenmod" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_BZIP2=y +CONFIG_HAVE_KERNEL_LZMA=y CONFIG_HAVE_KERNEL_LZO=y CONFIG_KERNEL_GZIP=y # CONFIG_KERNEL_BZIP2 is not set @@ -47,13 +79,29 @@ CONFIG_KERNEL_GZIP=y # CONFIG_BSD_PROCESS_ACCT is not set # CONFIG_TASKSTATS is not set # CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +# CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED is not set +CONFIG_HAVE_SPARSE_IRQ=y +CONFIG_GENERIC_IRQ_PROBE=y +# CONFIG_GENERIC_PENDING_IRQ is not set +# CONFIG_AUTO_IRQ_AFFINITY is not set +# CONFIG_IRQ_PER_CPU is not set +# CONFIG_HARDIRQS_SW_RESEND is not set +# CONFIG_SPARSE_IRQ is not set # # RCU Subsystem # -CONFIG_TREE_RCU=y -# CONFIG_TREE_PREEMPT_RCU is not set +CONFIG_TREE_PREEMPT_RCU=y # CONFIG_TINY_RCU is not set +# CONFIG_TINY_PREEMPT_RCU is not set +CONFIG_PREEMPT_RCU=y # CONFIG_RCU_TRACE is not set CONFIG_RCU_FANOUT=32 # CONFIG_RCU_FANOUT_EXACT is not set @@ -61,6 +109,7 @@ CONFIG_RCU_FANOUT=32 CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=17 +CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y CONFIG_CGROUPS=y # CONFIG_CGROUP_DEBUG is not set # CONFIG_CGROUP_NS is not set @@ -72,9 +121,11 @@ CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_SCHED=y CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y -# CONFIG_SYSFS_DEPRECATED_V2 is not set -# CONFIG_RELAY is not set +CONFIG_BLK_CGROUP=y +# CONFIG_DEBUG_BLK_CGROUP is not set # CONFIG_NAMESPACES is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" CONFIG_RD_GZIP=y @@ -86,7 +137,6 @@ CONFIG_SYSCTL=y CONFIG_ANON_INODES=y CONFIG_PANIC_TIMEOUT=5 CONFIG_EMBEDDED=y -CONFIG_UID16=y # CONFIG_SYSCTL_SYSCALL is not set CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_ALL is not set @@ -95,6 +145,7 @@ CONFIG_HOTPLUG=y CONFIG_PRINTK=y CONFIG_BUG=y # CONFIG_ELF_CORE is not set +CONFIG_PCSPKR_PLATFORM=y CONFIG_BASE_FULL=y CONFIG_FUTEX=y CONFIG_EPOLL=y @@ -105,14 +156,15 @@ CONFIG_SHMEM=y CONFIG_ASHMEM=y CONFIG_AIO=y CONFIG_HAVE_PERF_EVENTS=y -CONFIG_PERF_USE_VMALLOC=y # # Kernel Performance Events And Counters # -# CONFIG_PERF_EVENTS is not set +CONFIG_PERF_EVENTS=y # CONFIG_PERF_COUNTERS is not set +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set CONFIG_VM_EVENT_COUNTERS=y +CONFIG_PCI_QUIRKS=y CONFIG_COMPAT_BRK=y CONFIG_SLAB=y # CONFIG_SLUB is not set @@ -120,16 +172,27 @@ CONFIG_SLAB=y # CONFIG_PROFILING is not set CONFIG_HAVE_OPROFILE=y # CONFIG_KPROBES is not set +# CONFIG_JUMP_LABEL is not set +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_HAVE_IOREMAP_PROT=y CONFIG_HAVE_KPROBES=y CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_OPTPROBES=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_HW_BREAKPOINT=y +CONFIG_HAVE_MIXED_BREAKPOINTS_REGS=y +CONFIG_HAVE_USER_RETURN_NOTIFIER=y +CONFIG_HAVE_PERF_EVENTS_NMI=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y # # GCOV-based kernel profiling # # CONFIG_GCOV_KERNEL is not set -CONFIG_SLOW_WORK=y -# CONFIG_SLOW_WORK_DEBUG is not set -CONFIG_HAVE_GENERIC_DMA_COHERENT=y +# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set CONFIG_SLABINFO=y CONFIG_RT_MUTEXES=y CONFIG_BASE_SMALL=0 @@ -140,11 +203,9 @@ CONFIG_MODULE_FORCE_UNLOAD=y # CONFIG_MODVERSIONS is not set # CONFIG_MODULE_SRCVERSION_ALL is not set CONFIG_BLOCK=y -CONFIG_LBDAF=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_BLK_DEV_INTEGRITY is not set -CONFIG_BLK_CGROUP=y -# CONFIG_DEBUG_BLK_CGROUP is not set +# CONFIG_BLK_DEV_THROTTLING is not set # # IO Schedulers @@ -153,14 +214,10 @@ CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y CONFIG_CFQ_GROUP_IOSCHED=y -# CONFIG_DEBUG_CFQ_IOSCHED is not set -CONFIG_IOSCHED_BFQ=y -CONFIG_CGROUP_BFQIO=y # CONFIG_DEFAULT_DEADLINE is not set -# CONFIG_DEFAULT_CFQ is not set -CONFIG_DEFAULT_BFQ=y +CONFIG_DEFAULT_CFQ=y # CONFIG_DEFAULT_NOOP is not set -CONFIG_DEFAULT_IOSCHED="bfq" +CONFIG_DEFAULT_IOSCHED="cfq" # CONFIG_INLINE_SPIN_TRYLOCK is not set # CONFIG_INLINE_SPIN_TRYLOCK_BH is not set # CONFIG_INLINE_SPIN_LOCK is not set @@ -193,221 +250,158 @@ CONFIG_DEFAULT_IOSCHED="bfq" CONFIG_FREEZER=y # -# System Type -# -CONFIG_MMU=y -# CONFIG_ARCH_AAEC2000 is not set -# CONFIG_ARCH_INTEGRATOR is not set -# CONFIG_ARCH_REALVIEW is not set -# CONFIG_ARCH_VERSATILE is not set -# CONFIG_ARCH_AT91 is not set -# CONFIG_ARCH_BCMRING is not set -# CONFIG_ARCH_CLPS711X is not set -# CONFIG_ARCH_GEMINI is not set -# CONFIG_ARCH_EBSA110 is not set -# CONFIG_ARCH_EP93XX is not set -# CONFIG_ARCH_FOOTBRIDGE is not set -# CONFIG_ARCH_MXC is not set -# CONFIG_ARCH_STMP3XXX is not set -# CONFIG_ARCH_NETX is not set -# CONFIG_ARCH_H720X is not set -# CONFIG_ARCH_IOP13XX is not set -# CONFIG_ARCH_IOP32X is not set -# CONFIG_ARCH_IOP33X is not set -# CONFIG_ARCH_IXP23XX is not set -# CONFIG_ARCH_IXP2000 is not set -# CONFIG_ARCH_IXP4XX is not set -# CONFIG_ARCH_L7200 is not set -# CONFIG_ARCH_DOVE is not set -# CONFIG_ARCH_KIRKWOOD is not set -# CONFIG_ARCH_LOKI is not set -# CONFIG_ARCH_MV78XX0 is not set -# CONFIG_ARCH_ORION5X is not set -# CONFIG_ARCH_MMP is not set -# CONFIG_ARCH_KS8695 is not set -# CONFIG_ARCH_NS9XXX is not set -# CONFIG_ARCH_W90X900 is not set -# CONFIG_ARCH_NUC93X is not set -# CONFIG_ARCH_PNX4008 is not set -# CONFIG_ARCH_PXA is not set -CONFIG_ARCH_MSM=y -# CONFIG_ARCH_SHMOBILE is not set -# CONFIG_ARCH_RPC is not set -# CONFIG_ARCH_SA1100 is not set -# CONFIG_ARCH_S3C2410 is not set -# CONFIG_ARCH_S3C64XX is not set -# CONFIG_ARCH_S5P6440 is not set -# CONFIG_ARCH_S5P6442 is not set -# CONFIG_ARCH_S5PC1XX is not set -# CONFIG_ARCH_S5PV210 is not set -# CONFIG_ARCH_SHARK is not set -# CONFIG_ARCH_LH7A40X is not set -# CONFIG_ARCH_U300 is not set -# CONFIG_ARCH_U8500 is not set -# CONFIG_ARCH_NOMADIK is not set -# CONFIG_ARCH_DAVINCI is not set -# CONFIG_ARCH_OMAP is not set -# CONFIG_ARCH_MSM7X00A is not set -CONFIG_ARCH_QSD8X50=y -CONFIG_ARCH_MSM_SCORPION=y -CONFIG_MSM_MDP31=y -CONFIG_MSM_AMSS_VERSION=3200 -# CONFIG_MSM_AMSS_VERSION_6210 is not set -# CONFIG_MSM_AMSS_VERSION_6220 is not set -# CONFIG_MSM_AMSS_VERSION_6225 is not set -# CONFIG_MSM_AMSS_VERSION_6350 is not set -CONFIG_MSM_AMSS_VERSION_3200=y -# CONFIG_MSM_AMSS_SUPPORT_256MB_EBI1 is not set -CONFIG_MSM_DEBUG_UART_NONE=y -# CONFIG_MSM_DEBUG_UART1 is not set -# CONFIG_MSM_DEBUG_UART2 is not set -# CONFIG_MSM_DEBUG_UART3 is not set - -# -# MSM Board Type -# -# CONFIG_HOLES_IN_ZONE is not set -# CONFIG_MACH_SWORDFISH is not set -# CONFIG_MACH_MAHIMAHI is not set -CONFIG_MACH_SUPERSONIC=y -# CONFIG_MACH_QSD8X50_FFA is not set -CONFIG_HTC_BATTCHG=y -CONFIG_HTC_BATTCHG_SMEM=y -# CONFIG_HTC_PWRSPLY is not set -# CONFIG_HTC_PWRSINK is not set -CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 -CONFIG_MSM7X00A_USE_GP_TIMER=y -# CONFIG_MSM7X00A_USE_DG_TIMER is not set -CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y -# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set -# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set -# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set -# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set -CONFIG_MSM7X00A_SLEEP_MODE=0 -# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set -CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE=y -# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP is not set -# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set -# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set -CONFIG_MSM7X00A_IDLE_SLEEP_MODE=1 -CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000 -CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 -CONFIG_MSM_IDLE_STATS=y -CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 -CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 -CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 -CONFIG_MSM_FIQ_SUPPORT=y -CONFIG_MSM_SERIAL_DEBUGGER=y -CONFIG_MSM_SERIAL_DEBUGGER_NO_SLEEP=y -# CONFIG_MSM_SERIAL_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON is not set -# CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE is not set -CONFIG_MSM_SMD=y -CONFIG_MSM_ONCRPCROUTER=y -CONFIG_MSM_RPCSERVERS=y -# CONFIG_MSM_CPU_FREQ_SCREEN is not set -# CONFIG_MSM_HW3D is not set -CONFIG_MSM_QDSP6=y -CONFIG_HTC_ACOUSTIC_QSD=y -# CONFIG_MSM_CLOCK_CTRL_DEBUG is not set -CONFIG_WIFI_CONTROL_FUNC=y -# CONFIG_WIFI_MEM_PREALLOC is not set -CONFIG_ARCH_MSM_FLASHLIGHT=y -CONFIG_MICROP_COMMON=y -CONFIG_HTC_HEADSET_MGR=y -# CONFIG_HTC_HEADSET_H2W is not set -CONFIG_HTC_HEADSET_GPIO=y -CONFIG_HTC_HEADSET_MICROP=y -# CONFIG_HTC_HEADSET_PMIC is not set -# CONFIG_VIRTUAL_KPANIC_PARTITION is not set - -# -# Processor Type -# -CONFIG_CPU_32v6K=y -CONFIG_CPU_V7=y -CONFIG_CPU_32v7=y -CONFIG_CPU_ABRT_EV7=y -CONFIG_CPU_PABRT_V7=y -CONFIG_CPU_CACHE_V7=y -CONFIG_CPU_CACHE_VIPT=y -CONFIG_CPU_COPY_V6=y -CONFIG_CPU_TLB_V7=y -CONFIG_VERIFY_PERMISSION_FAULT=y -CONFIG_CPU_HAS_ASID=y -CONFIG_CPU_CP15=y -CONFIG_CPU_CP15_MMU=y - -# -# Processor Features -# -CONFIG_ARM_THUMB=y -CONFIG_ARM_THUMBEE=y -# CONFIG_CPU_ICACHE_DISABLE is not set -# CONFIG_CPU_DCACHE_DISABLE is not set -# CONFIG_CPU_BPREDICT_DISABLE is not set -CONFIG_HAS_TLS_REG=y -CONFIG_ARM_L1_CACHE_SHIFT=5 -CONFIG_CPU_HAS_PMU=y -# CONFIG_ARM_ERRATA_430973 is not set -# CONFIG_ARM_ERRATA_458693 is not set -# CONFIG_ARM_ERRATA_460075 is not set - -# -# Bus support -# -# CONFIG_PCI_SYSCALL is not set -# CONFIG_ARCH_SUPPORTS_MSI is not set -# CONFIG_PCCARD is not set - -# -# Kernel Features +# Processor type and features # CONFIG_TICK_ONESHOT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_GENERIC_CLOCKEVENTS_BUILD=y -CONFIG_VMSPLIT_3G=y -# CONFIG_VMSPLIT_2G is not set -# CONFIG_VMSPLIT_1G is not set -CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_SMP is not set +CONFIG_X86_MPPARSE=y +CONFIG_X86_EXTENDED_PLATFORM=y +# CONFIG_X86_VSMP is not set +CONFIG_SCHED_OMIT_FRAME_POINTER=y +# CONFIG_PARAVIRT_GUEST is not set +CONFIG_NO_BOOTMEM=y +# CONFIG_MEMTEST is not set +# CONFIG_MK8 is not set +# CONFIG_MPSC is not set +# CONFIG_MCORE2 is not set +# CONFIG_MATOM is not set +CONFIG_GENERIC_CPU=y +CONFIG_X86_CPU=y +CONFIG_X86_INTERNODE_CACHE_SHIFT=6 +CONFIG_X86_CMPXCHG=y +CONFIG_X86_L1_CACHE_SHIFT=6 +CONFIG_X86_XADD=y +CONFIG_X86_WP_WORKS_OK=y +CONFIG_X86_TSC=y +CONFIG_X86_CMPXCHG64=y +CONFIG_X86_CMOV=y +CONFIG_X86_MINIMUM_CPU_FAMILY=64 +CONFIG_X86_DEBUGCTLMSR=y +# CONFIG_PROCESSOR_SELECT is not set +CONFIG_CPU_SUP_INTEL=y +CONFIG_CPU_SUP_AMD=y +CONFIG_CPU_SUP_CENTAUR=y +CONFIG_HPET_TIMER=y +CONFIG_DMI=y +CONFIG_GART_IOMMU=y +# CONFIG_CALGARY_IOMMU is not set +# CONFIG_AMD_IOMMU is not set +CONFIG_SWIOTLB=y +CONFIG_IOMMU_HELPER=y +# CONFIG_IOMMU_API is not set +CONFIG_NR_CPUS=1 +# CONFIG_IRQ_TIME_ACCOUNTING is not set # CONFIG_PREEMPT_NONE is not set # CONFIG_PREEMPT_VOLUNTARY is not set CONFIG_PREEMPT=y -CONFIG_HZ=100 -# CONFIG_THUMB2_KERNEL is not set -CONFIG_AEABI=y -# CONFIG_OABI_COMPAT is not set -# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set -# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set -# CONFIG_HIGHMEM is not set -CONFIG_VMALLOC_RESERVE=0x08000000 +CONFIG_X86_LOCAL_APIC=y +CONFIG_X86_IO_APIC=y +# CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS is not set +# CONFIG_X86_MCE is not set +# CONFIG_I8K is not set +# CONFIG_MICROCODE is not set +# CONFIG_X86_MSR is not set +# CONFIG_X86_CPUID is not set +CONFIG_ARCH_PHYS_ADDR_T_64BIT=y +CONFIG_ARCH_DMA_ADDR_T_64BIT=y +CONFIG_DIRECT_GBPAGES=y +CONFIG_ARCH_SPARSEMEM_DEFAULT=y +CONFIG_ARCH_SPARSEMEM_ENABLE=y +CONFIG_ARCH_SELECT_MEMORY_MODEL=y +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 CONFIG_SELECT_MEMORY_MODEL=y -CONFIG_FLATMEM_MANUAL=y -# CONFIG_DISCONTIGMEM_MANUAL is not set -# CONFIG_SPARSEMEM_MANUAL is not set -CONFIG_FLATMEM=y -CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_SPARSEMEM_MANUAL=y +CONFIG_SPARSEMEM=y +CONFIG_HAVE_MEMORY_PRESENT=y +CONFIG_SPARSEMEM_EXTREME=y +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y +CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOGETHER=y +CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_HAVE_MEMBLOCK=y +# CONFIG_MEMORY_HOTPLUG is not set CONFIG_PAGEFLAGS_EXTENDED=y CONFIG_SPLIT_PTLOCK_CPUS=4 -# CONFIG_PHYS_ADDR_T_64BIT is not set -CONFIG_ZONE_DMA_FLAG=0 +CONFIG_PHYS_ADDR_T_64BIT=y +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_BOUNCE=y CONFIG_VIRT_TO_BUS=y # CONFIG_KSM is not set CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 -CONFIG_ALIGNMENT_TRAP=y -# CONFIG_UACCESS_WITH_MEMCPY is not set - -# -# Boot options -# -CONFIG_ZBOOT_ROM_TEXT=0x0 -CONFIG_ZBOOT_ROM_BSS=0x0 -CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" -# CONFIG_XIP_KERNEL is not set +CONFIG_NEED_PER_CPU_KM=y +# CONFIG_X86_CHECK_BIOS_CORRUPTION is not set +CONFIG_X86_RESERVE_LOW=64 +CONFIG_MTRR=y +CONFIG_MTRR_SANITIZER=y +CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT=0 +CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT=1 +CONFIG_X86_PAT=y +CONFIG_ARCH_USES_PG_UNCACHED=y +# CONFIG_EFI is not set +CONFIG_SECCOMP=y +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_HZ_100 is not set +CONFIG_HZ_250=y +# CONFIG_HZ_300 is not set +# CONFIG_HZ_1000 is not set +CONFIG_HZ=250 +CONFIG_SCHED_HRTICK=y # CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +CONFIG_PHYSICAL_START=0x1000000 +CONFIG_RELOCATABLE=y +CONFIG_PHYSICAL_ALIGN=0x1000000 +# CONFIG_CMDLINE_BOOL is not set +CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y # -# CPU Power Management +# Power management and ACPI options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND_NVS=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_PM_RUNTIME is not set +CONFIG_PM_OPS=y +CONFIG_ACPI=y +CONFIG_ACPI_SLEEP=y +# CONFIG_ACPI_PROCFS is not set +# CONFIG_ACPI_PROCFS_POWER is not set +# CONFIG_ACPI_EC_DEBUGFS is not set +CONFIG_ACPI_PROC_EVENT=y +CONFIG_ACPI_AC=y +CONFIG_ACPI_BATTERY=y +CONFIG_ACPI_BUTTON=y +CONFIG_ACPI_FAN=y +# CONFIG_ACPI_DOCK is not set +CONFIG_ACPI_PROCESSOR=y +# CONFIG_ACPI_PROCESSOR_AGGREGATOR is not set +CONFIG_ACPI_THERMAL=y +# CONFIG_ACPI_CUSTOM_DSDT is not set +CONFIG_ACPI_BLACKLIST_YEAR=0 +# CONFIG_ACPI_DEBUG is not set +# CONFIG_ACPI_PCI_SLOT is not set +CONFIG_X86_PM_TIMER=y +# CONFIG_ACPI_CONTAINER is not set +# CONFIG_ACPI_SBS is not set +# CONFIG_ACPI_HED is not set +# CONFIG_ACPI_APEI is not set +# CONFIG_SFI is not set + +# +# CPU Frequency scaling # CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_TABLE=y @@ -426,48 +420,60 @@ CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y -# CONFIG_CPU_IDLE is not set # -# Floating point emulation +# CPUFreq processor drivers # +# CONFIG_X86_PCC_CPUFREQ is not set +# CONFIG_X86_ACPI_CPUFREQ is not set +# CONFIG_X86_POWERNOW_K8 is not set +# CONFIG_X86_SPEEDSTEP_CENTRINO is not set +# CONFIG_X86_P4_CLOCKMOD is not set # -# At least one emulation must be selected +# shared options # -CONFIG_VFP=y -CONFIG_VFPv3=y -CONFIG_NEON=y +# CONFIG_X86_SPEEDSTEP_LIB is not set +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y +# CONFIG_INTEL_IDLE is not set # -# Userspace binary formats +# Memory power savings # -CONFIG_BINFMT_ELF=y -CONFIG_HAVE_AOUT=y -# CONFIG_BINFMT_AOUT is not set -# CONFIG_BINFMT_MISC is not set +# CONFIG_I7300_IDLE is not set # -# Power management options +# Bus options (PCI etc.) # -CONFIG_PM=y -# CONFIG_PM_DEBUG is not set -CONFIG_PM_SLEEP=y -CONFIG_SUSPEND=y -CONFIG_SUSPEND_FREEZER=y -CONFIG_HAS_WAKELOCK=y -CONFIG_HAS_EARLYSUSPEND=y -CONFIG_WAKELOCK=y -CONFIG_WAKELOCK_STAT=y -CONFIG_USER_WAKELOCK=y -CONFIG_EARLYSUSPEND=y -# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set -# CONFIG_CONSOLE_EARLYSUSPEND is not set -CONFIG_FB_EARLYSUSPEND=y -# CONFIG_APM_EMULATION is not set -# CONFIG_PM_RUNTIME is not set -CONFIG_PM_OPS=y -CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_PCI=y +CONFIG_PCI_DIRECT=y +# CONFIG_PCI_MMCONFIG is not set +CONFIG_PCI_DOMAINS=y +# CONFIG_PCI_CNB20LE_QUIRK is not set +# CONFIG_PCIEPORTBUS is not set +CONFIG_ARCH_SUPPORTS_MSI=y +# CONFIG_PCI_MSI is not set +# CONFIG_PCI_DEBUG is not set +# CONFIG_PCI_STUB is not set +CONFIG_HT_IRQ=y +# CONFIG_PCI_IOV is not set +CONFIG_PCI_IOAPIC=y +CONFIG_ISA_DMA_API=y +CONFIG_AMD_NB=y +# CONFIG_PCCARD is not set +# CONFIG_HOTPLUG_PCI is not set + +# +# Executable file formats / Emulations +# +CONFIG_BINFMT_ELF=y +# CONFIG_HAVE_AOUT is not set +# CONFIG_BINFMT_MISC is not set +# CONFIG_IA32_EMULATION is not set +# CONFIG_COMPAT_FOR_U64_ALIGNMENT is not set +CONFIG_HAVE_TEXT_POKE_SMP=y CONFIG_NET=y # @@ -494,7 +500,7 @@ CONFIG_IP_MULTIPLE_TABLES=y # CONFIG_IP_ROUTE_VERBOSE is not set # CONFIG_IP_PNP is not set # CONFIG_NET_IPIP is not set -# CONFIG_NET_IPGRE is not set +# CONFIG_NET_IPGRE_DEMUX is not set # CONFIG_ARPD is not set # CONFIG_SYN_COOKIES is not set # CONFIG_INET_AH is not set @@ -537,6 +543,7 @@ CONFIG_IPV6_MULTIPLE_TABLES=y CONFIG_ANDROID_PARANOID_NETWORK=y CONFIG_NET_ACTIVITY_STATS=y # CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set CONFIG_NETFILTER=y # CONFIG_NETFILTER_DEBUG is not set CONFIG_NETFILTER_ADVANCED=y @@ -549,7 +556,6 @@ CONFIG_NETFILTER_NETLINK=y CONFIG_NETFILTER_NETLINK_QUEUE=y CONFIG_NETFILTER_NETLINK_LOG=y CONFIG_NF_CONNTRACK=y -CONFIG_NF_CT_ACCT=y CONFIG_NF_CONNTRACK_MARK=y CONFIG_NF_CONNTRACK_EVENTS=y CONFIG_NF_CT_PROTO_DCCP=y @@ -567,20 +573,37 @@ CONFIG_NF_CONNTRACK_SIP=y CONFIG_NF_CONNTRACK_TFTP=y CONFIG_NF_CT_NETLINK=y CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y # CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set # CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set + +# +# Xtables matches +# # CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set CONFIG_NETFILTER_XT_MATCH_COMMENT=y CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y CONFIG_NETFILTER_XT_MATCH_CONNMARK=y CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set # CONFIG_NETFILTER_XT_MATCH_DCCP is not set # CONFIG_NETFILTER_XT_MATCH_DSCP is not set # CONFIG_NETFILTER_XT_MATCH_ESP is not set @@ -593,6 +616,7 @@ CONFIG_NETFILTER_XT_MATCH_LIMIT=y CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +# CONFIG_NETFILTER_XT_MATCH_OSF is not set CONFIG_NETFILTER_XT_MATCH_OWNER=y CONFIG_NETFILTER_XT_MATCH_POLICY=y # CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set @@ -601,7 +625,6 @@ CONFIG_NETFILTER_XT_MATCH_QUOTA=y # CONFIG_NETFILTER_XT_MATCH_RATEEST is not set # CONFIG_NETFILTER_XT_MATCH_REALM is not set CONFIG_NETFILTER_XT_MATCH_RECENT=y -# CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT is not set # CONFIG_NETFILTER_XT_MATCH_SCTP is not set CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y @@ -609,7 +632,6 @@ CONFIG_NETFILTER_XT_MATCH_STRING=y # CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y -# CONFIG_NETFILTER_XT_MATCH_OSF is not set # CONFIG_IP_VS is not set # @@ -646,7 +668,6 @@ CONFIG_NF_NAT_PPTP=y # CONFIG_NF_NAT_H323 is not set CONFIG_NF_NAT_SIP=y # CONFIG_IP_NF_MANGLE is not set -# CONFIG_IP_NF_TARGET_TTL is not set # CONFIG_IP_NF_RAW is not set CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y @@ -655,6 +676,7 @@ CONFIG_IP_NF_ARP_MANGLE=y # # IPv6: Netfilter Configuration # +# CONFIG_NF_DEFRAG_IPV6 is not set # CONFIG_NF_CONNTRACK_IPV6 is not set # CONFIG_IP6_NF_QUEUE is not set # CONFIG_IP6_NF_IPTABLES is not set @@ -664,6 +686,7 @@ CONFIG_IP_NF_ARP_MANGLE=y # CONFIG_RDS is not set # CONFIG_TIPC is not set # CONFIG_ATM is not set +# CONFIG_L2TP is not set CONFIG_STP=y CONFIG_BRIDGE=y CONFIG_BRIDGE_IGMP_SNOOPING=y @@ -732,9 +755,11 @@ CONFIG_NET_ACT_MIRRED=y # CONFIG_NET_ACT_PEDIT is not set # CONFIG_NET_ACT_SIMP is not set # CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set # CONFIG_NET_CLS_IND is not set CONFIG_NET_SCH_FIFO=y # CONFIG_DCB is not set +CONFIG_DNS_RESOLVER=y # # Network testing @@ -760,6 +785,7 @@ CONFIG_BT_HIDP=y CONFIG_BT_HCIUART=y CONFIG_BT_HCIUART_H4=y # CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set CONFIG_BT_HCIUART_LL=y # CONFIG_BT_HCIVHCI is not set # CONFIG_BT_MRVL is not set @@ -788,6 +814,8 @@ CONFIG_RFKILL=y CONFIG_RFKILL_LEDS=y # CONFIG_RFKILL_INPUT is not set # CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set # # Device Drivers @@ -812,10 +840,8 @@ CONFIG_MTD=y # CONFIG_MTD_TESTS is not set # CONFIG_MTD_CONCAT is not set CONFIG_MTD_PARTITIONS=y -# CONFIG_MTD_LAZYECCSTATS is not set # CONFIG_MTD_REDBOOT_PARTS is not set CONFIG_MTD_CMDLINE_PARTS=y -# CONFIG_MTD_AFS_PARTS is not set # CONFIG_MTD_AR7_PARTS is not set # @@ -829,6 +855,7 @@ CONFIG_MTD_BLOCK=y # CONFIG_INFTL is not set # CONFIG_RFD_FTL is not set # CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set # CONFIG_MTD_OOPS is not set # @@ -854,12 +881,14 @@ CONFIG_MTD_CFI_I2=y # Mapping drivers for chip access # # CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_TS5500 is not set +# CONFIG_MTD_INTEL_VR_NOR is not set # CONFIG_MTD_PLATRAM is not set # # Self-contained MTD device drivers # -CONFIG_MTD_MSM_NAND=y +# CONFIG_MTD_PMC551 is not set # CONFIG_MTD_DATAFLASH is not set # CONFIG_MTD_M25P80 is not set # CONFIG_MTD_SST25L is not set @@ -882,13 +911,21 @@ CONFIG_MTD_NAND_IDS=y # LPDDR flash memory drivers # # CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_PNP=y +CONFIG_PNP_DEBUG_MESSAGES=y # -# UBI - Unsorted block images +# Protocols # -# CONFIG_MTD_UBI is not set -# CONFIG_PARPORT is not set +CONFIG_PNPACPI=y CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set # CONFIG_BLK_DEV_COW_COMMON is not set CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_CRYPTOLOOP is not set @@ -897,23 +934,43 @@ CONFIG_BLK_DEV_LOOP=y # DRBD disabled because PROC_FS, INET or CONNECTOR not selected # # CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_SX8 is not set # CONFIG_BLK_DEV_RAM is not set # CONFIG_CDROM_PKTCDVD is not set # CONFIG_ATA_OVER_ETH is not set -# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_HD is not set +# CONFIG_BLK_DEV_RBD is not set CONFIG_MISC_DEVICES=y # CONFIG_AD525X_DPOT is not set CONFIG_ANDROID_PMEM=y +# CONFIG_IBM_ASM is not set +# CONFIG_PHANTOM is not set +# CONFIG_SGI_IOC4 is not set +# CONFIG_TIFM_CORE is not set # CONFIG_ICS932S401 is not set # CONFIG_ENCLOSURE_SERVICES is not set CONFIG_KERNEL_DEBUGGER_CORE=y +# CONFIG_CS5535_MFGPT is not set +# CONFIG_HP_ILO is not set +# CONFIG_APDS9802ALS is not set # CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set # CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +CONFIG_SENSORS_AKM8973=y +# CONFIG_SENSORS_AKM8976 is not set +CONFIG_VP_A1026=y # CONFIG_DS1682 is not set # CONFIG_TI_DAC7512 is not set CONFIG_UID_STAT=y +# CONFIG_VMWARE_BALLOON is not set +# CONFIG_BMP085 is not set +# CONFIG_PCH_PHUB is not set # CONFIG_WL127X_RFKILL is not set -CONFIG_SENSORS_BMA150_SPI=y # CONFIG_APANIC is not set # CONFIG_C2PORT is not set @@ -925,7 +982,13 @@ CONFIG_SENSORS_BMA150_SPI=y # CONFIG_EEPROM_LEGACY is not set # CONFIG_EEPROM_MAX6875 is not set # CONFIG_EEPROM_93CX6 is not set +# CONFIG_CB710_CORE is not set # CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set CONFIG_HAVE_IDE=y # CONFIG_IDE is not set @@ -943,13 +1006,21 @@ CONFIG_MD=y CONFIG_BLK_DEV_DM=y CONFIG_DM_DEBUG=y CONFIG_DM_CRYPT=y -CONFIG_DM_CRYPT_GLOBAL_WORKQUEUES=y # CONFIG_DM_SNAPSHOT is not set # CONFIG_DM_MIRROR is not set # CONFIG_DM_ZERO is not set # CONFIG_DM_MULTIPATH is not set # CONFIG_DM_DELAY is not set CONFIG_DM_UEVENT=y +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_FIREWIRE is not set +# CONFIG_FIREWIRE_NOSY is not set +# CONFIG_I2O is not set +# CONFIG_MACINTOSH_DRIVERS is not set CONFIG_NETDEVICES=y # CONFIG_IFB is not set CONFIG_DUMMY=y @@ -958,17 +1029,20 @@ CONFIG_DUMMY=y # CONFIG_EQUALIZER is not set CONFIG_TUN=y # CONFIG_VETH is not set +# CONFIG_NET_SB1000 is not set +# CONFIG_ARCNET is not set +CONFIG_MII=y # CONFIG_PHYLIB is not set CONFIG_NET_ETHERNET=y -CONFIG_MII=y -# CONFIG_AX88796 is not set -CONFIG_SMC91X=y -# CONFIG_DM9000 is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNGEM is not set +# CONFIG_CASSINI is not set +# CONFIG_NET_VENDOR_3COM is not set # CONFIG_ENC28J60 is not set # CONFIG_ETHOC is not set -# CONFIG_SMC911X is not set -# CONFIG_SMSC911X is not set # CONFIG_DNET is not set +# CONFIG_NET_TULIP is not set +# CONFIG_HP100 is not set # CONFIG_IBM_NEW_EMAC_ZMII is not set # CONFIG_IBM_NEW_EMAC_RGMII is not set # CONFIG_IBM_NEW_EMAC_TAH is not set @@ -976,18 +1050,72 @@ CONFIG_SMC91X=y # CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set # CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set # CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_NET_PCI is not set # CONFIG_B44 is not set -# CONFIG_KS8842 is not set # CONFIG_KS8851 is not set # CONFIG_KS8851_MLL is not set +# CONFIG_ATL2 is not set CONFIG_NETDEV_1000=y +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_E1000E is not set +# CONFIG_IP1000 is not set +# CONFIG_IGB is not set +# CONFIG_IGBVF is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_R8169 is not set +# CONFIG_SIS190 is not set +# CONFIG_SKGE is not set +# CONFIG_SKY2 is not set +# CONFIG_VIA_VELOCITY is not set +# CONFIG_TIGON3 is not set +# CONFIG_BNX2 is not set +# CONFIG_CNIC is not set +# CONFIG_QLA3XXX is not set +# CONFIG_ATL1 is not set +# CONFIG_ATL1E is not set +# CONFIG_ATL1C is not set +# CONFIG_JME is not set +# CONFIG_STMMAC_ETH is not set +# CONFIG_PCH_GBE is not set CONFIG_NETDEV_10000=y +# CONFIG_CHELSIO_T1 is not set +CONFIG_CHELSIO_T3_DEPENDS=y +# CONFIG_CHELSIO_T3 is not set +CONFIG_CHELSIO_T4_DEPENDS=y +# CONFIG_CHELSIO_T4 is not set +CONFIG_CHELSIO_T4VF_DEPENDS=y +# CONFIG_CHELSIO_T4VF is not set +# CONFIG_ENIC is not set +# CONFIG_IXGBE is not set +# CONFIG_IXGB is not set +# CONFIG_S2IO is not set +# CONFIG_VXGE is not set +# CONFIG_MYRI10GE is not set +# CONFIG_NETXEN_NIC is not set +# CONFIG_NIU is not set +# CONFIG_MLX4_EN is not set +# CONFIG_MLX4_CORE is not set +# CONFIG_TEHUTI is not set +# CONFIG_BNX2X is not set +# CONFIG_QLCNIC is not set +# CONFIG_QLGE is not set +# CONFIG_BNA is not set +# CONFIG_SFC is not set +# CONFIG_BE2NET is not set +# CONFIG_TR is not set CONFIG_WLAN=y -# CONFIG_HOSTAP is not set -# CONFIG_TIWLAN1251 is not set +# CONFIG_AIRO is not set +# CONFIG_ATMEL is not set +# CONFIG_PRISM54 is not set CONFIG_BCM4329=m CONFIG_BCM4329_FW_PATH="/system/etc/firmware/fw_bcm4329.bin" CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" +# CONFIG_HOSTAP is not set +# CONFIG_TIWLAN1251 is not set # # WiMAX Wireless Broadband devices @@ -999,6 +1127,12 @@ CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" # CONFIG_WIMAX_I2400M_SDIO is not set CONFIG_WIMAX_SQN=m # CONFIG_WAN is not set + +# +# CAIF transport drivers +# +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set CONFIG_PPP=y # CONFIG_PPP_MULTILINK is not set # CONFIG_PPP_FILTER is not set @@ -1008,16 +1142,15 @@ CONFIG_PPP_DEFLATE=y CONFIG_PPP_BSDCOMP=y CONFIG_PPP_MPPE=y # CONFIG_PPPOE is not set -# CONFIG_PPPOL2TP is not set CONFIG_PPPOLAC=y CONFIG_PPPOPNS=y # CONFIG_SLIP is not set CONFIG_SLHC=y # CONFIG_NETCONSOLE is not set -CONFIG_MSM_RMNET=y -# CONFIG_MSM_RMNET_DEBUG is not set # CONFIG_NETPOLL is not set # CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_VMXNET3 is not set +# CONFIG_GAN_ETH is not set # CONFIG_ISDN is not set # CONFIG_PHONE is not set @@ -1047,13 +1180,15 @@ CONFIG_INPUT_KEYRESET=y # CONFIG_INPUT_TABLET is not set CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_ATMEL=y +CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT=y # CONFIG_TOUCHSCREEN_ADS7846 is not set # CONFIG_TOUCHSCREEN_AD7877 is not set -# CONFIG_TOUCHSCREEN_AD7879_I2C is not set -# CONFIG_TOUCHSCREEN_AD7879_SPI is not set # CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set # CONFIG_TOUCHSCREEN_CYPRESS_TMG is not set # CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set # CONFIG_TOUCHSCREEN_EETI is not set # CONFIG_TOUCHSCREEN_FUJITSU is not set # CONFIG_TOUCHSCREEN_GUNZE is not set @@ -1065,15 +1200,19 @@ CONFIG_TOUCHSCREEN_ATMEL=y # CONFIG_TOUCHSCREEN_INEXIO is not set # CONFIG_TOUCHSCREEN_MK712 is not set # CONFIG_TOUCHSCREEN_PENMOUNT is not set -# CONFIG_TOUCHSCREEN_MSM is not set +# CONFIG_TOUCHSCREEN_QT602240 is not set # CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set -CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT=y # CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set # CONFIG_TOUCHSCREEN_TOUCHWIN is not set # CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set # CONFIG_TOUCHSCREEN_TOUCHIT213 is not set # CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_PCSPKR is not set +# CONFIG_INPUT_APANEL is not set +# CONFIG_INPUT_ATLAS_BTNS is not set # CONFIG_INPUT_ATI_REMOTE is not set # CONFIG_INPUT_ATI_REMOTE2 is not set CONFIG_INPUT_KEYCHORD=y @@ -1082,10 +1221,15 @@ CONFIG_INPUT_KEYCHORD=y # CONFIG_INPUT_YEALINK is not set # CONFIG_INPUT_CM109 is not set CONFIG_INPUT_UINPUT=y +# CONFIG_INPUT_WINBOND_CIR is not set CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_PCF8574 is not set # CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set -CONFIG_LIGHTSENSOR_MICROP=y +# CONFIG_INPUT_ADXL34X is not set # CONFIG_INPUT_CAPELLA_CM3602 is not set +# CONFIG_INPUT_CAPELLA_CM3602_HTC is not set +CONFIG_LIGHTSENSOR_MICROP=y +# CONFIG_INPUT_OPTICALJOYSTICK is not set # # Hardware I/O ports @@ -1100,49 +1244,83 @@ CONFIG_LIGHTSENSOR_MICROP=y # CONFIG_DEVMEM is not set # CONFIG_DEVKMEM is not set # CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set +# CONFIG_NOZOMI is not set # # Serial drivers # # CONFIG_SERIAL_8250 is not set +CONFIG_FIX_EARLYCON_MEM=y # # Non-8250 serial port support # # CONFIG_SERIAL_MAX3100 is not set -CONFIG_SERIAL_CORE=y -CONFIG_SERIAL_CORE_CONSOLE=y -CONFIG_SERIAL_MSM=y -CONFIG_SERIAL_MSM_CONSOLE=y -# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set -CONFIG_SERIAL_MSM_HS=y -CONFIG_SERIAL_BCM_BT_LPM=y +# CONFIG_SERIAL_MAX3107 is not set +# CONFIG_SERIAL_MFD_HSU is not set +# CONFIG_SERIAL_JSM is not set # CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set CONFIG_UNIX98_PTYS=y # CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set # CONFIG_LEGACY_PTYS is not set +# CONFIG_TTY_PRINTK is not set # CONFIG_IPMI_HANDLER is not set # CONFIG_HW_RANDOM is not set +# CONFIG_NVRAM is not set # CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_MWAVE is not set # CONFIG_RAW_DRIVER is not set +# CONFIG_HPET is not set +# CONFIG_HANGCHECK_TIMER is not set # CONFIG_TCG_TPM is not set -# CONFIG_DCC_TTY is not set +# CONFIG_TELCLOCK is not set +CONFIG_DEVPORT=y +# CONFIG_RAMOOPS is not set CONFIG_I2C=y CONFIG_I2C_BOARDINFO=y CONFIG_I2C_COMPAT=y # CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_MUX is not set CONFIG_I2C_HELPER_AUTO=y # # I2C Hardware Bus support # +# +# PC SMBus host controller drivers +# +# CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set +# CONFIG_I2C_ALI15X3 is not set +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_I801 is not set +# CONFIG_I2C_ISCH is not set +# CONFIG_I2C_PIIX4 is not set +# CONFIG_I2C_NFORCE2 is not set +# CONFIG_I2C_SIS5595 is not set +# CONFIG_I2C_SIS630 is not set +# CONFIG_I2C_SIS96X is not set +# CONFIG_I2C_VIA is not set +# CONFIG_I2C_VIAPRO is not set + +# +# ACPI drivers +# +# CONFIG_I2C_SCMI is not set + # # I2C system bus drivers (mostly embedded / system-on-chip) # # CONFIG_I2C_GPIO is not set -CONFIG_I2C_MSM=y +# CONFIG_I2C_INTEL_MID is not set # CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set # CONFIG_I2C_SIMTEC is not set # CONFIG_I2C_XILINX is not set @@ -1155,19 +1333,7 @@ CONFIG_I2C_MSM=y # # Other I2C/SMBus bus drivers # -# CONFIG_I2C_PCA_PLATFORM is not set # CONFIG_I2C_STUB is not set - -# -# Miscellaneous I2C Chip support -# -CONFIG_SENSORS_AKM8973=y -# CONFIG_SENSORS_AKM8976 is not set -CONFIG_TPS65200=y -# CONFIG_SENSORS_MT9T013 is not set -CONFIG_VP_A1026=y -# CONFIG_SENSORS_PCA963X is not set -CONFIG_AMP_TPA6130A=y # CONFIG_I2C_DEBUG_CORE is not set # CONFIG_I2C_DEBUG_ALGO is not set # CONFIG_I2C_DEBUG_BUS is not set @@ -1180,7 +1346,7 @@ CONFIG_SPI_MASTER=y # # CONFIG_SPI_BITBANG is not set # CONFIG_SPI_GPIO is not set -CONFIG_SPI_QSD=y +# CONFIG_SPI_TOPCLIFF_PCH is not set # CONFIG_SPI_XILINX is not set # CONFIG_SPI_DESIGNWARE is not set @@ -1194,7 +1360,7 @@ CONFIG_SPI_QSD=y # PPS support # # CONFIG_PPS is not set -CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y CONFIG_GPIOLIB=y # CONFIG_DEBUG_GPIO is not set # CONFIG_GPIO_SYSFS is not set @@ -1202,7 +1368,10 @@ CONFIG_GPIOLIB=y # # Memory mapped GPIO expanders: # +# CONFIG_GPIO_BASIC_MMIO is not set # CONFIG_GPIO_IT8761E is not set +# CONFIG_GPIO_SCH is not set +# CONFIG_GPIO_VX855 is not set # # I2C GPIO expanders: @@ -1211,11 +1380,17 @@ CONFIG_GPIOLIB=y # CONFIG_GPIO_MAX732X is not set # CONFIG_GPIO_PCA953X is not set # CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set # CONFIG_GPIO_ADP5588 is not set # # PCI GPIO expanders: # +# CONFIG_GPIO_CS5535 is not set +# CONFIG_GPIO_BT8XX is not set +# CONFIG_GPIO_LANGWELL is not set +# CONFIG_GPIO_PCH is not set +# CONFIG_GPIO_RDC321X is not set # # SPI GPIO expanders: @@ -1223,21 +1398,27 @@ CONFIG_GPIOLIB=y # CONFIG_GPIO_MAX7301 is not set # CONFIG_GPIO_MCP23S08 is not set # CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set # # AC97 GPIO expanders: # + +# +# MODULbus GPIO expanders: +# # CONFIG_W1 is not set CONFIG_POWER_SUPPLY=y # CONFIG_POWER_SUPPLY_DEBUG is not set # CONFIG_PDA_POWER is not set -# CONFIG_BATTERY_DS2760 is not set +# CONFIG_TEST_POWER is not set # CONFIG_BATTERY_DS2782 is not set # CONFIG_BATTERY_DS2784 is not set +# CONFIG_BATTERY_BQ20Z75 is not set # CONFIG_BATTERY_BQ27x00 is not set # CONFIG_BATTERY_MAX17040 is not set # CONFIG_HWMON is not set -# CONFIG_THERMAL is not set +CONFIG_THERMAL=y # CONFIG_WATCHDOG is not set CONFIG_SSB_POSSIBLE=y @@ -1245,33 +1426,38 @@ CONFIG_SSB_POSSIBLE=y # Sonics Silicon Backplane # # CONFIG_SSB is not set - -# -# Multifunction device drivers -# +CONFIG_MFD_SUPPORT=y # CONFIG_MFD_CORE is not set # CONFIG_MFD_88PM860X is not set # CONFIG_MFD_SM501 is not set -# CONFIG_MFD_ASIC3 is not set -# CONFIG_HTC_EGPIO is not set # CONFIG_HTC_PASIC3 is not set # CONFIG_HTC_I2CPLD is not set # CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +CONFIG_TPS65200=y # CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC35892 is not set # CONFIG_MFD_TMIO is not set -# CONFIG_MFD_TC6393XB is not set # CONFIG_PMIC_DA903X is not set # CONFIG_PMIC_ADP5520 is not set # CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8998 is not set # CONFIG_MFD_WM8400 is not set -# CONFIG_MFD_WM831X is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set # CONFIG_MFD_WM8350_I2C is not set # CONFIG_MFD_WM8994 is not set # CONFIG_MFD_PCF50633 is not set -# CONFIG_MFD_MC13783 is not set -# CONFIG_AB3100_CORE is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set # CONFIG_EZX_PCAP is not set -# CONFIG_AB4500_CORE is not set +# CONFIG_MFD_TIMBERDALE is not set +# CONFIG_LPC_SCH is not set +# CONFIG_MFD_RDC321X is not set +# CONFIG_MFD_JANZ_CMODIO is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_VX855 is not set CONFIG_REGULATOR=y CONFIG_REGULATOR_DEBUG=y # CONFIG_REGULATOR_DUMMY is not set @@ -1282,9 +1468,13 @@ CONFIG_REGULATOR_DEBUG=y # CONFIG_REGULATOR_MAX1586 is not set # CONFIG_REGULATOR_MAX8649 is not set # CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set # CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set CONFIG_REGULATOR_TPS65023=y # CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_AD5398 is not set CONFIG_MEDIA_SUPPORT=y # @@ -1302,39 +1492,30 @@ CONFIG_MEDIA_SUPPORT=y # # Qualcomm MSM Camera And Video # -CONFIG_MSM_CAMERA=y -# CONFIG_MSM_CAMERA_OLD is not set -CONFIG_MSM_CAMERA_LEGACY=y -# CONFIG_MSM_CAMERA_7X30 is not set -CONFIG_720P_CAMERA=y -# CONFIG_MSM_CAMERA_DEBUG is not set # # Camera Sensor Selection # -# CONFIG_MT9T013 is not set -# CONFIG_MT9D112 is not set -# CONFIG_MT9P012 is not set -# CONFIG_S5K3E2FX is not set -# CONFIG_S5K4E1GX is not set -CONFIG_OV8810=y -CONFIG_OV9665=y -# CONFIG_S5K3H1GX is not set -# CONFIG_MT9V113 is not set # CONFIG_DAB is not set # # Graphics support # +# CONFIG_AGP is not set +CONFIG_VGA_ARB=y +CONFIG_VGA_ARB_MAX_GPUS=16 +# CONFIG_VGA_SWITCHEROO is not set +# CONFIG_DRM is not set +# CONFIG_STUB_POULSBO is not set # CONFIG_VGASTATE is not set CONFIG_VIDEO_OUTPUT_CONTROL=y CONFIG_FB=y # CONFIG_FIRMWARE_EDID is not set # CONFIG_FB_DDC is not set # CONFIG_FB_BOOT_VESA_SUPPORT is not set -CONFIG_FB_CFB_FILLRECT=y -CONFIG_FB_CFB_COPYAREA=y -CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_FILLRECT is not set +# CONFIG_FB_CFB_COPYAREA is not set +# CONFIG_FB_CFB_IMAGEBLIT is not set # CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set # CONFIG_FB_SYS_FILLRECT is not set # CONFIG_FB_SYS_COPYAREA is not set @@ -1350,16 +1531,42 @@ CONFIG_FB_CFB_IMAGEBLIT=y # # Frame buffer hardware drivers # +# CONFIG_FB_CIRRUS is not set +# CONFIG_FB_PM2 is not set +# CONFIG_FB_CYBER2000 is not set +# CONFIG_FB_ARC is not set +# CONFIG_FB_ASILIANT is not set +# CONFIG_FB_IMSTT is not set +# CONFIG_FB_VGA16 is not set +# CONFIG_FB_VESA is not set +# CONFIG_FB_N411 is not set +# CONFIG_FB_HGA is not set # CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_NVIDIA is not set +# CONFIG_FB_RIVA is not set +# CONFIG_FB_LE80578 is not set +# CONFIG_FB_MATROX is not set +# CONFIG_FB_RADEON is not set +# CONFIG_FB_ATY128 is not set +# CONFIG_FB_ATY is not set +# CONFIG_FB_S3 is not set +# CONFIG_FB_SAVAGE is not set +# CONFIG_FB_SIS is not set +# CONFIG_FB_VIA is not set +# CONFIG_FB_NEOMAGIC is not set +# CONFIG_FB_KYRO is not set +# CONFIG_FB_3DFX is not set +# CONFIG_FB_VOODOO1 is not set +# CONFIG_FB_VT8623 is not set +# CONFIG_FB_TRIDENT is not set +# CONFIG_FB_ARK is not set +# CONFIG_FB_PM3 is not set +# CONFIG_FB_CARMINE is not set +# CONFIG_FB_GEODE is not set # CONFIG_FB_VIRTUAL is not set # CONFIG_FB_METRONOME is not set # CONFIG_FB_MB862XX is not set # CONFIG_FB_BROADSHEET is not set -CONFIG_FB_MSM=y -CONFIG_FB_MSM_LCDC=y -CONFIG_GPU_MSM_KGSL=y -CONFIG_MSM_KGSL_MMU=y -CONFIG_MSM_HDMI=y # CONFIG_BACKLIGHT_LCD_SUPPORT is not set # @@ -1377,17 +1584,16 @@ CONFIG_HID=y # Special HID drivers # CONFIG_HID_APPLE=y +# CONFIG_HID_ELECOM is not set # CONFIG_HID_MAGICMOUSE is not set # CONFIG_HID_WACOM is not set CONFIG_USB_SUPPORT=y CONFIG_USB_ARCH_HAS_HCD=y -# CONFIG_USB_ARCH_HAS_OHCI is not set -# CONFIG_USB_ARCH_HAS_EHCI is not set +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_ARCH_HAS_EHCI=y # CONFIG_USB is not set # CONFIG_USB_OTG_WHITELIST is not set # CONFIG_USB_OTG_BLACKLIST_HUB is not set -# CONFIG_USB_MUSB_HDRC is not set -# CONFIG_USB_GADGET_MUSB_HDRC is not set # # NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may @@ -1398,54 +1604,42 @@ CONFIG_USB_GADGET=y # CONFIG_USB_GADGET_DEBUG_FS is not set CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_USB_GADGET_SELECTED=y -# CONFIG_USB_GADGET_AT91 is not set -# CONFIG_USB_GADGET_ATMEL_USBA is not set -# CONFIG_USB_GADGET_FSL_USB2 is not set -# CONFIG_USB_GADGET_LH7A40X is not set -# CONFIG_USB_GADGET_OMAP is not set -# CONFIG_USB_GADGET_PXA25X is not set -# CONFIG_USB_GADGET_R8A66597 is not set -# CONFIG_USB_GADGET_PXA27X is not set -# CONFIG_USB_GADGET_S3C_HSOTG is not set -# CONFIG_USB_GADGET_IMX is not set -# CONFIG_USB_GADGET_S3C2410 is not set +CONFIG_USB_GADGET_R8A66597=y +CONFIG_USB_R8A66597=y # CONFIG_USB_GADGET_M66592 is not set # CONFIG_USB_GADGET_AMD5536UDC is not set -# CONFIG_USB_GADGET_FSL_QE is not set # CONFIG_USB_GADGET_CI13XXX is not set # CONFIG_USB_GADGET_NET2280 is not set # CONFIG_USB_GADGET_GOKU is not set # CONFIG_USB_GADGET_LANGWELL is not set -CONFIG_USB_GADGET_MSM_72K=y -CONFIG_USB_MSM_72K=y -# CONFIG_USB_GADGET_DUMMY_HCD is not set CONFIG_USB_GADGET_DUALSPEED=y # CONFIG_USB_ZERO is not set -# CONFIG_USB_AUDIO is not set # CONFIG_USB_ETH is not set # CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set # CONFIG_USB_FILE_STORAGE is not set # CONFIG_USB_MASS_STORAGE is not set # CONFIG_USB_G_SERIAL is not set -# CONFIG_USB_MIDI_GADGET is not set # CONFIG_USB_G_PRINTER is not set CONFIG_USB_ANDROID=y # CONFIG_USB_ANDROID_ACM is not set CONFIG_USB_ANDROID_ADB=y # CONFIG_USB_ANDROID_DIAG is not set CONFIG_USB_ANDROID_MASS_STORAGE=y +# CONFIG_USB_ANDROID_MTP is not set CONFIG_USB_ANDROID_RNDIS=y CONFIG_USB_ANDROID_RNDIS_WCEIS=y # CONFIG_USB_CDC_COMPOSITE is not set -# CONFIG_USB_G_NOKIA is not set # CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set # # OTG and related infrastructure # # CONFIG_USB_GPIO_VBUS is not set -# CONFIG_USB_ULPI is not set # CONFIG_NOP_USB_XCEIV is not set +# CONFIG_UWB is not set CONFIG_MMC=y # CONFIG_MMC_DEBUG is not set CONFIG_MMC_UNSAFE_RESUME=y @@ -1456,6 +1650,7 @@ CONFIG_MMC_PARANOID_SD_INIT=y # MMC/SD/SDIO Card Drivers # CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 # CONFIG_MMC_BLOCK_BOUNCE is not set CONFIG_MMC_BLOCK_DEFERRED_RESUME=y # CONFIG_SDIO_UART is not set @@ -1465,8 +1660,11 @@ CONFIG_MMC_BLOCK_DEFERRED_RESUME=y # MMC/SD/SDIO Host Controller Drivers # # CONFIG_MMC_SDHCI is not set -CONFIG_MMC_MSM7X00A=y +# CONFIG_MMC_WBSD is not set +# CONFIG_MMC_TIFM_SD is not set # CONFIG_MMC_SPI is not set +# CONFIG_MMC_CB710 is not set +# CONFIG_MMC_VIA_SDMMC is not set # CONFIG_MEMSTICK is not set CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y @@ -1474,15 +1672,19 @@ CONFIG_LEDS_CLASS=y # # LED drivers # +# CONFIG_LEDS_ALIX2 is not set # CONFIG_LEDS_PCA9532 is not set CONFIG_LEDS_GPIO=y CONFIG_LEDS_GPIO_PLATFORM=y # CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set CONFIG_LEDS_CPLD=y # CONFIG_LEDS_PCA955X is not set # CONFIG_LEDS_DAC124S085 is not set # CONFIG_LEDS_REGULATOR is not set # CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_INTEL_SS4200 is not set # CONFIG_LEDS_LT3593 is not set CONFIG_LEDS_TRIGGERS=y @@ -1502,6 +1704,8 @@ CONFIG_LEDS_TRIGGER_SLEEP=y CONFIG_SWITCH=y CONFIG_SWITCH_GPIO=y # CONFIG_ACCESSIBILITY is not set +# CONFIG_INFINIBAND is not set +# CONFIG_EDAC is not set CONFIG_RTC_LIB=y CONFIG_RTC_CLASS=y CONFIG_RTC_HCTOSYS=y @@ -1524,9 +1728,11 @@ CONFIG_RTC_INTF_ALARM_DEV=y # CONFIG_RTC_DRV_DS1307 is not set # CONFIG_RTC_DRV_DS1374 is not set # CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set # CONFIG_RTC_DRV_MAX6900 is not set # CONFIG_RTC_DRV_RS5C372 is not set # CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set # CONFIG_RTC_DRV_X1205 is not set # CONFIG_RTC_DRV_PCF8563 is not set # CONFIG_RTC_DRV_PCF8583 is not set @@ -1569,17 +1775,19 @@ CONFIG_RTC_INTF_ALARM_DEV=y # # on-CPU RTC drivers # -CONFIG_RTC_DRV_MSM7X00A=y # CONFIG_DMADEVICES is not set # CONFIG_AUXDISPLAY is not set # CONFIG_UIO is not set - -# -# TI VLYNQ -# CONFIG_STAGING=y # CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_ET131X is not set +# CONFIG_SLICOSS is not set # CONFIG_ECHO is not set +# CONFIG_BRCM80211 is not set +# CONFIG_RT2860 is not set +# CONFIG_COMEDI is not set +# CONFIG_R8187SE is not set +# CONFIG_RTL8192E is not set # # Android @@ -1598,24 +1806,56 @@ CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d CONFIG_ANDROID_TIMED_OUTPUT=y CONFIG_ANDROID_TIMED_GPIO=y CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_POHMELFS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_VT6655 is not set +# CONFIG_HYPERV is not set +# CONFIG_VME_BUS is not set +# CONFIG_IIO is not set +# CONFIG_ZRAM is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_FB_SM7XX is not set +# CONFIG_CRYSTALHD is not set # -# Qualcomm MSM Camera And Video +# Texas Instruments shared transport line discipline # +# CONFIG_ST_BT is not set +# CONFIG_ADIS16255 is not set +# CONFIG_FB_XGI is not set +# CONFIG_SMB_FS is not set +# CONFIG_ACPI_QUICKSTART is not set +CONFIG_MACH_NO_WESTBRIDGE=y +# CONFIG_ATH6K_LEGACY is not set +# CONFIG_FT1000 is not set # -# Camera Sensor Selection +# Speakup console speech # -# CONFIG_POHMELFS is not set +CONFIG_X86_PLATFORM_DEVICES=y +# CONFIG_ASUS_LAPTOP is not set +# CONFIG_SONY_LAPTOP is not set +# CONFIG_IDEAPAD_LAPTOP is not set +# CONFIG_THINKPAD_ACPI is not set +# CONFIG_SENSORS_HDAPS is not set +# CONFIG_INTEL_MENLOW is not set +# CONFIG_ACPI_WMI is not set +# CONFIG_ACPI_ASUS is not set +# CONFIG_TOPSTAR_LAPTOP is not set +# CONFIG_TOSHIBA_BT_RFKILL is not set +# CONFIG_ACPI_CMPC is not set +# CONFIG_INTEL_IPS is not set +# CONFIG_IBM_RTL is not set # -# RAR Register Driver +# Firmware Drivers # -# CONFIG_RAR_REGISTER is not set -# CONFIG_IIO is not set -# CONFIG_BATMAN_ADV is not set -# CONFIG_STRIP is not set -# CONFIG_FB_SM7XX is not set +# CONFIG_EDD is not set +CONFIG_FIRMWARE_MEMMAP=y +# CONFIG_DELL_RBU is not set +# CONFIG_DCDBAS is not set +CONFIG_DMIID=y +# CONFIG_ISCSI_IBFT_FIND is not set # # File systems @@ -1639,13 +1879,14 @@ CONFIG_FS_POSIX_ACL=y # CONFIG_OCFS2_FS is not set # CONFIG_BTRFS_FS is not set # CONFIG_NILFS2_FS is not set +CONFIG_EXPORTFS=m CONFIG_FILE_LOCKING=y CONFIG_FSNOTIFY=y # CONFIG_DNOTIFY is not set -CONFIG_INOTIFY=y CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set # CONFIG_QUOTA is not set -# CONFIG_AUTOFS_FS is not set +# CONFIG_QUOTACTL is not set # CONFIG_AUTOFS4_FS is not set CONFIG_FUSE_FS=m # CONFIG_CUSE is not set @@ -1675,16 +1916,19 @@ CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" # Pseudo filesystems # CONFIG_PROC_FS=y +# CONFIG_PROC_KCORE is not set CONFIG_PROC_SYSCTL=y CONFIG_PROC_PAGE_MONITOR=y CONFIG_SYSFS=y CONFIG_TMPFS=y # CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLBFS is not set # CONFIG_HUGETLB_PAGE is not set # CONFIG_CONFIGFS_FS is not set CONFIG_MISC_FILESYSTEMS=y # CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set +# CONFIG_ECRYPT_FS is not set # CONFIG_HFS_FS is not set # CONFIG_HFSPLUS_FS is not set # CONFIG_BEFS_FS is not set @@ -1697,16 +1941,17 @@ CONFIG_YAFFS_YAFFS1=y CONFIG_YAFFS_YAFFS2=y CONFIG_YAFFS_AUTO_YAFFS2=y CONFIG_YAFFS_DISABLE_TAGS_ECC=y -# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set # CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set -CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y # CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set # CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set # CONFIG_YAFFS_DISABLE_BACKGROUND is not set +CONFIG_YAFFS_XATTR=y # CONFIG_JFFS2_FS is not set # CONFIG_LOGFS is not set # CONFIG_CRAMFS is not set CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_XATTR is not set +# CONFIG_SQUASHFS_LZO is not set CONFIG_SQUASHFS_EMBEDDED=y CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 # CONFIG_VXFS_FS is not set @@ -1717,47 +1962,38 @@ CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 # CONFIG_ROMFS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set -CONFIG_AUFS_FS=y -CONFIG_AUFS_BRANCH_MAX_127=y -# CONFIG_AUFS_BRANCH_MAX_511 is not set -# CONFIG_AUFS_BRANCH_MAX_1023 is not set -# CONFIG_AUFS_BRANCH_MAX_32767 is not set -# CONFIG_AUFS_HNOTIFY is not set -# CONFIG_AUFS_RDU is not set -# CONFIG_AUFS_SP_IATTR is not set -# CONFIG_AUFS_SHWH is not set -# CONFIG_AUFS_BR_RAMFS is not set -# CONFIG_AUFS_BR_FUSE is not set -CONFIG_AUFS_BDEV_LOOP=y -# CONFIG_AUFS_DEBUG is not set CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=m CONFIG_NFS_V3=y CONFIG_NFS_V3_ACL=y CONFIG_NFS_V4=y # CONFIG_NFS_V4_1 is not set +# CONFIG_NFS_USE_LEGACY_DNS is not set +CONFIG_NFS_USE_KERNEL_DNS=y +# CONFIG_NFS_USE_NEW_IDMAPPER is not set CONFIG_NFSD=m +CONFIG_NFSD_DEPRECATED=y CONFIG_NFSD_V2_ACL=y CONFIG_NFSD_V3=y CONFIG_NFSD_V3_ACL=y CONFIG_NFSD_V4=y CONFIG_LOCKD=m CONFIG_LOCKD_V4=y -CONFIG_EXPORTFS=m CONFIG_NFS_ACL_SUPPORT=m CONFIG_NFS_COMMON=y CONFIG_SUNRPC=m CONFIG_SUNRPC_GSS=m CONFIG_RPCSEC_GSS_KRB5=m -# CONFIG_RPCSEC_GSS_SPKM3 is not set -# CONFIG_SMB_FS is not set # CONFIG_CEPH_FS is not set CONFIG_CIFS=m # CONFIG_CIFS_STATS is not set CONFIG_CIFS_WEAK_PW_HASH=y +# CONFIG_CIFS_UPCALL is not set CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y # CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_DFS_UPCALL is not set +# CONFIG_CIFS_ACL is not set # CONFIG_CIFS_EXPERIMENTAL is not set # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set @@ -1813,6 +2049,7 @@ CONFIG_NLS_ISO8859_1=y # # Kernel hacking # +CONFIG_TRACE_IRQFLAGS_SUPPORT=y CONFIG_PRINTK_TIME=y CONFIG_ENABLE_WARN_DEPRECATED=y CONFIG_ENABLE_MUST_CHECK=y @@ -1824,9 +2061,8 @@ CONFIG_DEBUG_FS=y # CONFIG_HEADERS_CHECK is not set CONFIG_DEBUG_KERNEL=y # CONFIG_DEBUG_SHIRQ is not set -CONFIG_DETECT_SOFTLOCKUP=y -# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set -CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set CONFIG_DETECT_HUNG_TASK=y # CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 @@ -1841,8 +2077,10 @@ CONFIG_TIMER_STATS=y # CONFIG_RT_MUTEX_TESTER is not set # CONFIG_DEBUG_SPINLOCK is not set # CONFIG_DEBUG_MUTEXES is not set +CONFIG_BKL=y # CONFIG_DEBUG_LOCK_ALLOC is not set # CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set # CONFIG_LOCK_STAT is not set # CONFIG_DEBUG_SPINLOCK_SLEEP is not set # CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set @@ -1851,12 +2089,16 @@ CONFIG_STACKTRACE=y CONFIG_DEBUG_BUGVERBOSE=y # CONFIG_DEBUG_INFO is not set # CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_VIRTUAL is not set # CONFIG_DEBUG_WRITECOUNT is not set # CONFIG_DEBUG_MEMORY_INIT is not set # CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set # CONFIG_DEBUG_SG is not set # CONFIG_DEBUG_NOTIFIERS is not set # CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_ARCH_WANT_FRAME_POINTERS=y +CONFIG_FRAME_POINTER=y # CONFIG_BOOT_PRINTK_DELAY is not set # CONFIG_RCU_TORTURE_TEST is not set # CONFIG_RCU_CPU_STALL_DETECTOR is not set @@ -1867,30 +2109,61 @@ CONFIG_DEBUG_BUGVERBOSE=y # CONFIG_FAULT_INJECTION is not set # CONFIG_LATENCYTOP is not set # CONFIG_SYSCTL_SYSCALL_CHECK is not set -# CONFIG_PAGE_POISONING is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_USER_STACKTRACE_SUPPORT=y CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST=y +CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_C_RECORDMCOUNT=y CONFIG_TRACING_SUPPORT=y # CONFIG_FTRACE is not set +# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set # CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set # CONFIG_SAMPLES is not set CONFIG_HAVE_ARCH_KGDB=y # CONFIG_KGDB is not set -CONFIG_ARM_UNWIND=y -# CONFIG_DEBUG_USER is not set -# CONFIG_DEBUG_ERRORS is not set +CONFIG_HAVE_ARCH_KMEMCHECK=y +# CONFIG_STRICT_DEVMEM is not set +CONFIG_X86_VERBOSE_BOOTUP=y +CONFIG_EARLY_PRINTK=y +# CONFIG_EARLY_PRINTK_DBGP is not set +# CONFIG_DEBUG_STACKOVERFLOW is not set # CONFIG_DEBUG_STACK_USAGE is not set -# CONFIG_DEBUG_LL is not set -# CONFIG_OC_ETM is not set +# CONFIG_X86_PTDUMP is not set +CONFIG_DEBUG_RODATA=y +CONFIG_DEBUG_RODATA_TEST=y +# CONFIG_DEBUG_NX_TEST is not set +# CONFIG_IOMMU_DEBUG is not set +# CONFIG_IOMMU_STRESS is not set +CONFIG_HAVE_MMIOTRACE_SUPPORT=y +CONFIG_IO_DELAY_TYPE_0X80=0 +CONFIG_IO_DELAY_TYPE_0XED=1 +CONFIG_IO_DELAY_TYPE_UDELAY=2 +CONFIG_IO_DELAY_TYPE_NONE=3 +CONFIG_IO_DELAY_0X80=y +# CONFIG_IO_DELAY_0XED is not set +# CONFIG_IO_DELAY_UDELAY is not set +# CONFIG_IO_DELAY_NONE is not set +CONFIG_DEFAULT_IO_DELAY_TYPE=0 +# CONFIG_DEBUG_BOOT_PARAMS is not set +# CONFIG_CPA_DEBUG is not set +# CONFIG_OPTIMIZE_INLINING is not set +# CONFIG_DEBUG_STRICT_USER_COPY_CHECKS is not set # # Security options # -# CONFIG_KEYS is not set +CONFIG_KEYS=y +# CONFIG_KEYS_DEBUG_PROC_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set # CONFIG_SECURITY is not set # CONFIG_SECURITYFS is not set -# CONFIG_DEFAULT_SECURITY_SELINUX is not set -# CONFIG_DEFAULT_SECURITY_SMACK is not set -# CONFIG_DEFAULT_SECURITY_TOMOYO is not set CONFIG_DEFAULT_SECURITY_DAC=y CONFIG_DEFAULT_SECURITY="" CONFIG_CRYPTO=y @@ -1907,9 +2180,10 @@ CONFIG_CRYPTO_BLKCIPHER2=y CONFIG_CRYPTO_HASH=y CONFIG_CRYPTO_HASH2=y CONFIG_CRYPTO_RNG2=y -CONFIG_CRYPTO_PCOMP=y +CONFIG_CRYPTO_PCOMP2=y CONFIG_CRYPTO_MANAGER=y CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y # CONFIG_CRYPTO_GF128MUL is not set # CONFIG_CRYPTO_NULL is not set CONFIG_CRYPTO_WORKQUEUE=y @@ -1946,6 +2220,7 @@ CONFIG_CRYPTO_HMAC=y # Digest # CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_CRC32C_INTEL is not set # CONFIG_CRYPTO_GHASH is not set # CONFIG_CRYPTO_MD4 is not set CONFIG_CRYPTO_MD5=y @@ -1959,11 +2234,14 @@ CONFIG_CRYPTO_SHA1=y # CONFIG_CRYPTO_SHA512 is not set # CONFIG_CRYPTO_TGR192 is not set # CONFIG_CRYPTO_WP512 is not set +# CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL is not set # # Ciphers # CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_AES_X86_64 is not set +# CONFIG_CRYPTO_AES_NI_INTEL is not set # CONFIG_CRYPTO_ANUBIS is not set CONFIG_CRYPTO_ARC4=y # CONFIG_CRYPTO_BLOWFISH is not set @@ -1974,11 +2252,13 @@ CONFIG_CRYPTO_DES=y # CONFIG_CRYPTO_FCRYPT is not set # CONFIG_CRYPTO_KHAZAD is not set # CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SALSA20_X86_64 is not set # CONFIG_CRYPTO_SEED is not set # CONFIG_CRYPTO_SERPENT is not set # CONFIG_CRYPTO_TEA is not set CONFIG_CRYPTO_TWOFISH=y CONFIG_CRYPTO_TWOFISH_COMMON=y +# CONFIG_CRYPTO_TWOFISH_X86_64 is not set # # Compression @@ -1992,12 +2272,20 @@ CONFIG_CRYPTO_DEFLATE=y # # CONFIG_CRYPTO_ANSI_CPRNG is not set # CONFIG_CRYPTO_HW is not set +CONFIG_HAVE_KVM=y +CONFIG_VIRTUALIZATION=y +# CONFIG_KVM is not set +# CONFIG_VHOST_NET is not set +# CONFIG_VIRTIO_PCI is not set +# CONFIG_VIRTIO_BALLOON is not set # CONFIG_BINARY_PRINTF is not set # # Library routines # CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_FIRST_BIT=y +CONFIG_GENERIC_FIND_NEXT_BIT=y CONFIG_GENERIC_FIND_LAST_BIT=y CONFIG_CRC_CCITT=y CONFIG_CRC16=y @@ -2009,7 +2297,6 @@ CONFIG_LIBCRC32C=y CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y CONFIG_DECOMPRESS_GZIP=y -CONFIG_GENERIC_ALLOCATOR=y CONFIG_REED_SOLOMON=y CONFIG_REED_SOLOMON_ENC8=y CONFIG_REED_SOLOMON_DEC8=y diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index 8ba3a85774c88..8ac29132058a1 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -71,7 +71,7 @@ #include "board-supersonic-tpa2018d1.h" -#define SMEM_SPINLOCK_I2C 6 +#define SMEM_SPINLOCK_I2C 6 #ifdef CONFIG_ARCH_QSD8X50 extern unsigned char *get_bt_bd_ram(void); @@ -110,7 +110,7 @@ static struct platform_device htc_battery_pdev = { static int capella_cm3602_power(int pwr_device, uint8_t enable); static struct microp_function_config microp_functions[] = { { - .name = "reset-int", + .name = "reset-int", .category = MICROP_FUNCTION_RESET_INT, .int_pin = 1 << 8, }, @@ -216,8 +216,8 @@ static struct capella_cm3602_platform_data capella_cm3602_pdata = { static struct htc_headset_microp_platform_data htc_headset_microp_data = { .remote_int = 1 << 7, .remote_irq = MSM_uP_TO_INT(7), - .remote_enable_pin = 0, - .adc_channel = 0x01, + .remote_enable_pin = 0, + .adc_channel = 0x01, .adc_remote = {0, 33, 50, 110, 160, 220}, }; @@ -252,13 +252,13 @@ static struct platform_device microp_devices[] = { .name = "HTC_HEADSET_MICROP", .id = -1, .dev = { - .platform_data = &htc_headset_microp_data, + .platform_data = &htc_headset_microp_data, }, }, }; static struct microp_i2c_platform_data microp_data = { - .num_functions = ARRAY_SIZE(microp_functions), + .num_functions = ARRAY_SIZE(microp_functions), .microp_function = microp_functions, .num_devices = ARRAY_SIZE(microp_devices), .microp_devices = microp_devices, @@ -296,23 +296,23 @@ static int supersonic_phy_init_seq[] = { 0xC, 0x31, 0x30, 0x32, 0x1D, 0x0D, 0x1D // USB cable in: supersonic_uart_usb_switch(0) static void supersonic_uart_usb_switch(int uart) { - printk(KERN_INFO "%s:uart:%d\n", __func__, uart); - gpio_set_value(SUPERSONIC_USB_UARTz_SW, uart?1:0); // XA and for USB cable in to reset wimax UART - - if(system_rev && uart) // XB - { - if (gpio_get_value(SUPERSONIC_WIMAX_CPU_UARTz_SW)) // Wimax UART - { - printk(KERN_INFO "%s:Wimax UART\n", __func__); - gpio_set_value(SUPERSONIC_USB_UARTz_SW,1); - gpio_set_value(SUPERSONIC_WIMAX_CPU_UARTz_SW,1); - } - else // USB, CPU UART - { - printk(KERN_INFO "%s:Non wimax UART\n", __func__); - gpio_set_value(SUPERSONIC_WIMAX_CPU_UARTz_SW, uart==2?1:0); - } - } + printk(KERN_INFO "%s:uart:%d\n", __func__, uart); + gpio_set_value(SUPERSONIC_USB_UARTz_SW, uart?1:0); // XA and for USB cable in to reset wimax UART + + if(system_rev && uart) // XB + { + if (gpio_get_value(SUPERSONIC_WIMAX_CPU_UARTz_SW)) // Wimax UART + { + printk(KERN_INFO "%s:Wimax UART\n", __func__); + gpio_set_value(SUPERSONIC_USB_UARTz_SW,1); + gpio_set_value(SUPERSONIC_WIMAX_CPU_UARTz_SW,1); + } + else // USB, CPU UART + { + printk(KERN_INFO "%s:Non wimax UART\n", __func__); + gpio_set_value(SUPERSONIC_WIMAX_CPU_UARTz_SW, uart==2?1:0); + } + } } extern void msm_hsusb_8x50_phy_reset(void); @@ -320,7 +320,7 @@ extern void msm_hsusb_8x50_phy_reset(void); static struct msm_hsusb_platform_data msm_hsusb_pdata = { .phy_init_seq = supersonic_phy_init_seq, .phy_reset = msm_hsusb_8x50_phy_reset, - .usb_id_pin_gpio = SUPERSONIC_GPIO_USB_ID_PIN, + .usb_id_pin_gpio = SUPERSONIC_GPIO_USB_ID_PIN, .accessory_detect = 1, /* detect by ID pin gpio */ .usb_uart_switch = supersonic_uart_usb_switch, }; @@ -442,7 +442,7 @@ static struct android_usb_platform_data android_usb_pdata = { static struct platform_device android_usb_device = { .name = "android_usb", .id = -1, - .dev = { + .dev = { .platform_data = &android_usb_pdata, }, }; @@ -453,11 +453,11 @@ CPU_WIMAX_SW -> GPIO160 USB_UART#_SW -> GPIO33 XA : GPIO33 = 0 -> USB - GPIO33 = 1 -> CPU UART + GPIO33 = 1 -> CPU UART XB : GPIO33 = 0 -> USB - GPIO33 = 1 , GPIO160 = 0 -> CPU UART // SUPERSONIC_WIMAX_CPU_UARTz_SW (GPIO160) - GPIO33 = 1 , GPIO160 = 1 -> Wimax UART // SUPERSONIC_USB_UARTz_SW (GPIO33) + GPIO33 = 1 , GPIO160 = 0 -> CPU UART // SUPERSONIC_WIMAX_CPU_UARTz_SW (GPIO160) + GPIO33 = 1 , GPIO160 = 1 -> Wimax UART // SUPERSONIC_USB_UARTz_SW (GPIO33) */ @@ -821,12 +821,12 @@ static struct platform_device htc_headset_mgr = { .name = "HTC_HEADSET_MGR", .id = -1, .dev = { - .platform_data = &htc_headset_mgr_data, + .platform_data = &htc_headset_mgr_data, }, }; static struct htc_headset_gpio_platform_data htc_headset_gpio_data = { - .hpin_gpio = SUPERSONIC_GPIO_35MM_HEADSET_DET, + .hpin_gpio = SUPERSONIC_GPIO_35MM_HEADSET_DET, .key_enable_gpio = 0, .mic_select_gpio = 0, }; @@ -835,7 +835,7 @@ static struct platform_device htc_headset_gpio = { .name = "HTC_HEADSET_GPIO", .id = -1, .dev = { - .platform_data = &htc_headset_gpio_data, + .platform_data = &htc_headset_gpio_data, }, }; @@ -1064,7 +1064,12 @@ static struct i2c_board_info i2c_devices[] = { .platform_data = &compass_platform_data, .irq = MSM_GPIO_TO_INT(SUPERSONIC_GPIO_COMPASS_INT_N), }, - + { + I2C_BOARD_INFO("s5k3h1gx", 0x20 >> 1), + },/*samsung for 2nd source main camera*/ + { + I2C_BOARD_INFO("s5k6aafx", 0x78 >> 1), + },/*samsung 2nd camera 2nd source*/ { I2C_BOARD_INFO("ov8810", 0x6C >> 1), }, @@ -1124,8 +1129,6 @@ static uint32_t camera_off_gpio_table[] = { PCOM_GPIO_CFG(13, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* HSYNC */ PCOM_GPIO_CFG(14, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* VSYNC */ PCOM_GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* MCLK */ - PCOM_GPIO_CFG(99, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), /* CAM1_RST */ - PCOM_GPIO_CFG(100, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), /* CAM1_PWD */ }; static uint32_t camera_on_gpio_table[] = { @@ -1183,10 +1186,10 @@ static struct msm_camera_device_platform_data msm_camera_device_data = { .ioext.appsz = MSM_CLK_CTL_SIZE, }; -static void supersonic_ov8810_clk_switch(void){ +static void supersonic_maincam_clk_switch(void){ int rc = 0; - pr_info("SuperSoinc: clk switch (supersonic)(ov8810)\n"); - rc = gpio_request(SUPERSONIC_CLK_SWITCH, "ov8810"); + pr_info("SuperSoinc: clk switch (supersonic)(maincam)\n"); + rc = gpio_request(SUPERSONIC_CLK_SWITCH, "maincam"); if (rc < 0) pr_err("GPIO (%d) request fail\n", SUPERSONIC_CLK_SWITCH); else @@ -1196,10 +1199,10 @@ static void supersonic_ov8810_clk_switch(void){ return; } -static void supersonic_ov9665_clk_switch(void){ +static void supersonic_seccam_clk_switch(void){ int rc = 0; - pr_info("SuperSoinc: Doing clk switch (supersonic)(ov9665)\n"); - rc = gpio_request(SUPERSONIC_CLK_SWITCH, "ov9665"); + pr_info("SuperSoinc: Doing clk switch (supersonic)(2ndcam)\n"); + rc = gpio_request(SUPERSONIC_CLK_SWITCH, "seccam"); if (rc < 0) pr_err("GPIO (%d) request fail\n", SUPERSONIC_CLK_SWITCH); else @@ -1209,6 +1212,36 @@ static void supersonic_ov9665_clk_switch(void){ return; } +enum msm_camera_source camera_source; +static void supersonic_set_source(enum msm_camera_source source) +{ + camera_source = source; +} + +enum msm_camera_source supersonic_get_source(void){ + return camera_source; +} + +static int camera_main_probed = 0; +static int supersonic_camera_main_get_probe(void) +{ + return camera_main_probed; +} +static void supersonic_camera_main_set_probe(int probed) +{ + camera_main_probed = probed; +} + +static int camera_sec_probed = 0; +static int supersonic_camera_sec_get_probe(void) +{ + return camera_sec_probed; +} +static void supersonic_camera_sec_set_probe(int probed) +{ + camera_sec_probed = probed; +} + static struct camera_flash_cfg msm_camera_sensor_flash_cfg = { .camera_flash = flashlight_control, .num_flash_levels = FLASHLIGHT_NUM, @@ -1216,11 +1249,57 @@ static struct camera_flash_cfg msm_camera_sensor_flash_cfg = { .low_cap_limit = 15, }; +/*2nd source for 2nd camera*/ +static struct msm_camera_sensor_info msm_camera_sensor_s5k6aafx_data = { + .sensor_name = "s5k6aafx", + .sensor_reset = SUPERSONIC_MAINCAM_RST, + .sensor_pwd = SUPERSONIC_2NDCAM_PWD, + .camera_clk_switch = supersonic_seccam_clk_switch, + .camera_main_get_probe = supersonic_camera_sec_get_probe, + .camera_main_set_probe = supersonic_camera_sec_get_probe, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .waked_up = 0, + .need_suspend = 0, +}; + +static struct platform_device msm_camera_sensor_s5k6aafx = { + .name = "msm_camera_s5k6aafx", + .dev = { + .platform_data = &msm_camera_sensor_s5k6aafx_data, + }, +}; + +/*samsung for 2nd source main camera*/ +static struct msm_camera_sensor_info msm_camera_sensor_s5k3h1_data = { + .sensor_name = "s5k3h1gx", + .sensor_reset = SUPERSONIC_MAINCAM_RST, + .sensor_pwd = SUPERSONIC_MAINCAM_PWD, + .camera_clk_switch = supersonic_maincam_clk_switch, + .camera_set_source = supersonic_set_source, + .camera_main_get_probe = supersonic_camera_main_get_probe, + .camera_main_set_probe = supersonic_camera_main_set_probe, + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_cfg = &msm_camera_sensor_flash_cfg, +}; + +static struct platform_device msm_camera_sensor_s5k3h1 = { + .name = "msm_camera_s5k3h1gx", + .dev = { + .platform_data = &msm_camera_sensor_s5k3h1_data, + }, +}; static struct msm_camera_sensor_info msm_camera_sensor_ov8810_data = { - .sensor_name = "ov8810", - .sensor_reset = SUPERSONIC_MAINCAM_RST, /* CAM1_RST */ - .sensor_pwd = SUPERSONIC_MAINCAM_PWD, /* CAM1_PWDN, enabled in a9 */ - .camera_clk_switch = supersonic_ov8810_clk_switch, + .sensor_name = "ov8810", + .sensor_reset = SUPERSONIC_MAINCAM_RST, + .sensor_pwd = SUPERSONIC_MAINCAM_PWD, + .camera_clk_switch = supersonic_maincam_clk_switch, + .camera_set_source = supersonic_set_source, + .camera_main_get_probe = supersonic_camera_main_get_probe, + .camera_main_set_probe = supersonic_camera_main_set_probe, .pdata = &msm_camera_device_data, .resource = msm_camera_resources, .num_resources = ARRAY_SIZE(msm_camera_resources), @@ -1230,17 +1309,20 @@ static struct msm_camera_sensor_info msm_camera_sensor_ov8810_data = { }; static struct platform_device msm_camera_sensor_ov8810 = { - .name = "msm_camera_ov8810", - .dev = { - .platform_data = &msm_camera_sensor_ov8810_data, - }, + .name = "msm_camera_ov8810", + .dev = { + .platform_data = &msm_camera_sensor_ov8810_data, + }, }; static struct msm_camera_sensor_info msm_camera_sensor_ov9665_data = { .sensor_name = "ov9665", .sensor_reset = SUPERSONIC_MAINCAM_RST, - .sensor_pwd = SUPERSONIC_2NDCAM_PWD, - .camera_clk_switch = supersonic_ov9665_clk_switch, + .sensor_pwd = SUPERSONIC_2NDCAM_PWD, + .camera_clk_switch = supersonic_seccam_clk_switch, + .camera_get_source = supersonic_get_source, + .camera_main_get_probe = supersonic_camera_sec_get_probe, + .camera_main_get_probe = supersonic_camera_sec_get_probe, .pdata = &msm_camera_device_data, .resource = msm_camera_resources, .num_resources = ARRAY_SIZE(msm_camera_resources), @@ -1250,11 +1332,10 @@ static struct msm_camera_sensor_info msm_camera_sensor_ov9665_data = { static struct platform_device msm_camera_sensor_ov9665 = { .name = "msm_camera_ov9665", - .dev = { + .dev = { .platform_data = &msm_camera_sensor_ov9665_data, }, }; - static void config_supersonic_flashlight_gpios(void) { static uint32_t flashlight_gpio_table[] = { @@ -1270,7 +1351,7 @@ static void config_supersonic_flashlight_gpios(void) } static struct flashlight_platform_data supersonic_flashlight_data = { - .gpio_init = config_supersonic_flashlight_gpios, + .gpio_init = config_supersonic_flashlight_gpios, .torch = SUPERSONIC_GPIO_FLASHLIGHT_TORCH, .flash = SUPERSONIC_GPIO_FLASHLIGHT_FLASH, .flash_adj = SUPERSONIC_GPIO_FLASHLIGHT_FLASH_ADJ, @@ -1286,24 +1367,24 @@ static struct platform_device supersonic_flashlight_device = { }; static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { - .rx_wakeup_irq = -1, - .inject_rx_on_wakeup = 0, - .exit_lpm_cb = bcm_bt_lpm_exit_lpm_locked, + .rx_wakeup_irq = -1, + .inject_rx_on_wakeup = 0, + .exit_lpm_cb = bcm_bt_lpm_exit_lpm_locked, }; static struct bcm_bt_lpm_platform_data bcm_bt_lpm_pdata = { - .gpio_wake = SUPERSONIC_GPIO_BT_CHIP_WAKE, - .gpio_host_wake = SUPERSONIC_GPIO_BT_HOST_WAKE, - .request_clock_off_locked = msm_hs_request_clock_off_locked, - .request_clock_on_locked = msm_hs_request_clock_on_locked, + .gpio_wake = SUPERSONIC_GPIO_BT_CHIP_WAKE, + .gpio_host_wake = SUPERSONIC_GPIO_BT_HOST_WAKE, + .request_clock_off_locked = msm_hs_request_clock_off_locked, + .request_clock_on_locked = msm_hs_request_clock_on_locked, }; struct platform_device bcm_bt_lpm_device = { - .name = "bcm_bt_lpm", - .id = 0, - .dev = { - .platform_data = &bcm_bt_lpm_pdata, - }, + .name = "bcm_bt_lpm", + .id = 0, + .dev = { + .platform_data = &bcm_bt_lpm_pdata, + }, }; static struct platform_device *devices[] __initdata = { @@ -1337,7 +1418,9 @@ static struct platform_device *devices[] __initdata = { &android_pmem_ciq2_device, &android_pmem_ciq3_device, #endif + &msm_camera_sensor_s5k3h1, &msm_camera_sensor_ov8810, + &msm_camera_sensor_s5k6aafx, &msm_kgsl_device, &msm_device_i2c, &msm_camera_sensor_ov9665, @@ -1409,9 +1492,9 @@ static ssize_t supersonic_virtual_keys_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return sprintf(buf, - __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":43:835:86:50" - ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":165:835:100:50" - ":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":300:835:110:50" + __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":43:835:86:50" + ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":165:835:100:50" + ":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":300:835:110:50" ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":425:835:90:50" "\n"); } @@ -1435,7 +1518,7 @@ static struct attribute_group supersonic_properties_attr_group = { static void supersonic_reset(void) { - gpio_set_value(SUPERSONIC_GPIO_PS_HOLD, 0); + gpio_set_value(SUPERSONIC_GPIO_PS_HOLD, 0); } /* system_rev == higher 16bits of PCBID @@ -1458,7 +1541,7 @@ static void __init supersonic_init(void) #if defined(CONFIG_MSM_SERIAL_DEBUGGER) msm_serial_debug_init(MSM_UART1_PHYS, INT_UART1, - &msm_device_uart1.dev, 1, MSM_GPIO_TO_INT(SUPERSONIC_GPIO_UART1_RX)); + &msm_device_uart1.dev, 1, MSM_GPIO_TO_INT(SUPERSONIC_GPIO_UART1_RX)); #endif #ifdef CONFIG_ARCH_QSD8X50 @@ -1537,8 +1620,8 @@ extern struct sys_timer msm_timer; MACHINE_START(SUPERSONIC, "supersonic") #ifdef CONFIG_MSM_DEBUG_UART - .phys_io = MSM_DEBUG_UART_PHYS, - .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, #endif .boot_params = 0x20000100, .fixup = supersonic_fixup, From e15f169c9f34474684f1fa9da52a430bb09882a3 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Thu, 30 Dec 2010 12:01:23 -0500 Subject: [PATCH 1041/2556] Various updates from HTC * Better panel support on Supersonic * Fix memory barriers in kgsl * Updated charger/battery driver Change-Id: Ic0ed8a0c2466cad85d5db0220235e248c2230d56 --- arch/arm/mach-msm/board-supersonic-panel.c | 64 ++++----------------- arch/arm/mach-msm/board-supersonic.c | 2 + arch/arm/mach-msm/htc_battery.c | 59 ++++++++----------- drivers/input/keyreset.c | 10 ++-- drivers/input/misc/keychord.c | 29 +++++++++- drivers/mfd/tps65200.c | 40 +++++++++++-- drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c | 2 + drivers/video/msm/hdmi/fb-hdmi.c | 6 ++ include/linux/smb329.h | 23 ++++++++ 9 files changed, 137 insertions(+), 98 deletions(-) create mode 100644 include/linux/smb329.h diff --git a/arch/arm/mach-msm/board-supersonic-panel.c b/arch/arm/mach-msm/board-supersonic-panel.c index da9e6772973bb..e9702419f1733 100644 --- a/arch/arm/mach-msm/board-supersonic-panel.c +++ b/arch/arm/mach-msm/board-supersonic-panel.c @@ -35,13 +35,6 @@ #include "devices.h" #include "proc_comm.h" -/* -** Innitial T2 Settings -** Novatek Panels are picky, most start 50fps @ 372, the minority starts @ 378 -** So defaulting at 380, should support all. keyword: *should* -*/ -int savedT2 = 380; // 380 default, should support around 53+fps for all Novatek Panels - #if 1 #define B(s...) printk(s) #else @@ -201,9 +194,6 @@ struct nov_regs { {0x6A18, 0xff}, {0x6A17, 0x01}, {0xF402, 0x14}, - - {0xb101, 0x01}, // T2 init - Put back in, just in case if T2 is defaulted, - {0xb102, 0x7c}, // it gets defaulted to 380, and not 340. For Novatek Panels. {0x3500, 0x00}, {0x1100, 0x0}, @@ -215,8 +205,8 @@ struct s1d_regs { unsigned val; } s1d13775_init_seq[] = { {0x001C, 0x1500}, - {0x0020, 0x3047}, - {0x0024, 0x4014}, // original value 401A, last 5 bits set PLL clock div, 1A=27 div, 14 = ~53 fps, and is the sweet spot for a pure register epson fix - netarchy + {0x0020, 0x3043}, + {0x0024, 0x401A}, {0x0028, 0x031A}, {0x002C, 0x0001}, {REG_WAIT, 0x0004}, /* increase delay 1ms -> 4ms */ @@ -226,7 +216,7 @@ struct s1d_regs { {0x002C, 0x0002}, {REG_WAIT, 0x0004}, /* increase delay 1ms -> 4ms */ {0x002C, 0x0003}, - {0x0100, 0x3703}, // last 5 bits change pixel clock divider, 00011 (3) sets divider to 4, we like 4 combined with modded PLL clock div - netarchy + {0x0100, 0x3702}, {0x0104, 0x0180}, {0x0140, 0x003F}, {0x0144, 0x00EF}, @@ -250,7 +240,7 @@ struct s1d_regs { {0x028C, 0x0001}, {0x0294, 0x0000}, {0x0400, 0x8000}, - {0x0404, 0x100F}, // original value 1001, Bits 0-9 set the Tearing Effect Delay (gets rid of tearing) - netarchy + {0x0404, 0x10C8}, {0x0480, 0x0001}, {0x0500, 0x0000}, {0x0504, 0x0011}, @@ -659,25 +649,6 @@ static int supersonic_panel_blank(struct msm_mddi_bridge_platform_data *bridge_data, struct msm_mddi_client_data *client_data) { - /* For Novatek Panels Only */ - if (panel_type == 1) - { - /* - ** Get the T2 value into readT2 - */ - unsigned readT2; - readT2 = 0; - readT2 |= client_data->remote_read(client_data, 0xb101) << 8; - readT2 |= client_data->remote_read(client_data, 0xb102); - /* readT2 value safety check */ - if (readT2 < 245 || readT2 > 999) - { - readT2 = 380; - } - savedT2 = readT2; - } - /* Done */ - B(KERN_DEBUG "%s\n", __func__); suc_backlight_switch(LED_OFF); backlight_control(0); @@ -691,39 +662,26 @@ supersonic_panel_unblank(struct msm_mddi_bridge_platform_data *bridge_data, B(KERN_DEBUG "%s\n", __func__); if (panel_type == PANEL_AUO) { suc_backlight_switch(LED_FULL); + client_data->remote_write(client_data, 0x01, 0xB101); + client_data->remote_write(client_data, 0x82, 0xB102); + client_data->remote_write(client_data, 0x5A, 0xB107); + client_data->remote_write(client_data, 0x00, 0x4400); + client_data->remote_write(client_data, 0xC8, 0x4401); client_data->remote_write(client_data, 0x00, 0x2900); msleep(100); client_data->remote_write(client_data, 0x24, 0x5300); } else { suc_backlight_switch(LED_FULL); + client_data->remote_write(client_data, 0x3043, 0x0020); + client_data->remote_write(client_data, 0x10C8, 0x0404); client_data->remote_write(client_data, 0x4000, 0x0600); msleep(10); qspi_send_9bit(0x0, 0x29); client_data->remote_write(client_data, 0x7000, 0x0324); client_data->remote_write(client_data, 0x4000, 0x0600); - client_data->remote_write(client_data, 0x100F, 0x0404); // Set anti-tearing on unblank for epson fix (including the intitial unblank at boot time) - netarchy - client_data->remote_write(client_data, 0x3703, 0x0100); // Set Pixel Clock Divider to 4 on unblank - client_data->remote_write(client_data, 0x4014, 0x0024); // Set PLL clock divider to 15 on unblank } backlight_control(1); - - /* For Novatek Panels Only */ - if (panel_type == 1) - { - /* - ** Check saved value just to be safe - */ - if (savedT2 < 245 || savedT2 > 999) - { - savedT2 = 380; - } - /* Write savedT2 to T2 */ - client_data->remote_write(client_data, (0xff00 & savedT2) >> 8, 0xb101); - client_data->remote_write(client_data, (0x00ff & savedT2), 0xb102); - } - /* Done */ - return 0; } diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index 8ac29132058a1..13bc8c85c3e43 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -96,6 +96,8 @@ static struct htc_battery_platform_data htc_battery_pdev_data = { .guage_driver = GUAGE_MODEM, .m2a_cable_detect = 1, .charger = SWITCH_CHARGER, + /* After the state of SUC XA, MCHG_EN is chanage to CHG_INT*/ + .int_data.chg_int = MSM_GPIO_TO_INT(SUPERSONIC_GPIO_MCHG_EN_N), }; static struct platform_device htc_battery_pdev = { diff --git a/arch/arm/mach-msm/htc_battery.c b/arch/arm/mach-msm/htc_battery.c index 0075583fa8dec..77781093a44c7 100644 --- a/arch/arm/mach-msm/htc_battery.c +++ b/arch/arm/mach-msm/htc_battery.c @@ -41,10 +41,12 @@ #endif #ifdef CONFIG_BATTERY_DS2784 #include -#elif defined(CONFIG_BATTERY_DS2746) +#elif CONFIG_BATTERY_DS2746 #include #endif +#include + static struct wake_lock vbus_wake_lock; enum { @@ -445,7 +447,7 @@ static int htc_set_smem_cable_type(u32 cable_type) { return -1; } #endif #if 1 //JH //this is for packet filter (notify port list while USB in/out) int update_port_list_charging_state(int enable); -#endif +#endif static int htc_cable_status_update(int status) { int rc = 0; @@ -611,13 +613,9 @@ static void usb_status_notifier_func(int online) blocking_notifier_call_chain(&cable_status_notifier_list, htc_batt_info.rep.charging_source, NULL); - if (htc_battery_initial) { - power_supply_changed(&htc_power_supplies[CHARGER_AC]); - power_supply_changed(&htc_power_supplies[CHARGER_USB]); - power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); - } else { - pr_err("\n\n ### htc_battery_code is not inited yet! ###\n\n"); - } + power_supply_changed(&htc_power_supplies[CHARGER_AC]); + power_supply_changed(&htc_power_supplies[CHARGER_USB]); + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); update_wake_lock(htc_batt_info.rep.charging_source); #else mutex_lock(&htc_batt_info.lock); @@ -985,17 +983,13 @@ static int htc_battery_get_charging_status(void) break; case CHARGER_USB: case CHARGER_AC: -#if !defined(CONFIG_BATTERY_DS2746) if ((htc_charge_full) && (htc_batt_info.rep.full_level == 100)) { htc_batt_info.rep.level = 100; } -#endif + level = htc_batt_info.rep.level; if (level == 100){ htc_charge_full = 1;} - else - htc_charge_full = 0; - if (htc_charge_full) ret = POWER_SUPPLY_STATUS_FULL; else if (htc_batt_info.rep.charging_enabled != 0) @@ -1022,20 +1016,12 @@ static int htc_battery_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_HEALTH: val->intval = POWER_SUPPLY_HEALTH_GOOD; - if (machine_is_paradise()) { - if (htc_batt_info.rep.batt_temp >= 500 || - htc_batt_info.rep.batt_temp <= 0) - val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; - } else if (machine_is_spade()) { - if (htc_batt_info.rep.batt_temp >= 450 || - htc_batt_info.rep.batt_temp <= 0) - val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; - } else { - if (htc_batt_info.rep.batt_temp >= 480 || - htc_batt_info.rep.batt_temp <= 0) - val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; - } - + if (machine_is_paradise() && (htc_batt_info.rep.batt_temp >= 500 || + htc_batt_info.rep.batt_temp <= 0)) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + else if (!machine_is_paradise() && (htc_batt_info.rep.batt_temp >= 480 || + htc_batt_info.rep.batt_temp <= 0)) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; if (htc_batt_debug_mask & HTC_BATT_DEBUG_USER_QUERY) BATT_LOG("%s: %s: health=%d", __func__, psy->name, val->intval); break; @@ -1086,8 +1072,9 @@ static struct device_attribute htc_battery_attrs[] = { #ifdef CONFIG_HTC_BATTCHG_SMEM __ATTR(smem_raw, S_IRUGO, htc_battery_show_smem, NULL), __ATTR(smem_text, S_IRUGO, htc_battery_show_smem, NULL), -#endif +#else __ATTR(batt_attr_text, S_IRUGO, htc_battery_show_batt_attr, NULL), +#endif }; enum { @@ -1191,7 +1178,7 @@ static ssize_t htc_battery_charger_switch(struct device *dev, enable = simple_strtoul(buf, NULL, 10); - if (enable > 1 || enable < 0) + if (enable > 1) return -EINVAL; mutex_lock(&htc_batt_info.rpc_lock); @@ -1209,6 +1196,7 @@ static ssize_t htc_battery_set_full_level(struct device *dev, { int rc = 0; unsigned long percent = 100; + unsigned long param = 0; percent = simple_strtoul(buf, NULL, 10); @@ -1228,8 +1216,9 @@ static ssize_t htc_battery_set_full_level(struct device *dev, mutex_lock(&htc_batt_info.lock); htc_full_level_flag = 1; htc_batt_info.rep.full_level = percent; + param = percent; blocking_notifier_call_chain(&cable_status_notifier_list, - 0xff, (void *) &htc_batt_info.rep.full_level); + 0xff, (void *) ¶m); mutex_unlock(&htc_batt_info.lock); } rc = 0; @@ -1270,7 +1259,7 @@ static int htc_battery_create_attrs(struct device * dev) device_remove_file(dev, &htc_battery_attrs[i]); htc_delta_attrs_failed: while (j--) - device_remove_file(dev, &htc_set_delta_attrs[i]); + device_remove_file(dev, &htc_set_delta_attrs[j]); succeed: return rc; } @@ -1301,7 +1290,7 @@ static int update_batt_info(void) ret = -1; } break; -#elif defined(CONFIG_BATTERY_DS2746) +#elif CONFIG_BATTERY_DS2746 case GUAGE_DS2746: if (ds2746_get_battery_info(&htc_batt_info.rep)) { BATT_ERR("%s: ds2746 read failed!!!", __func__); @@ -1631,7 +1620,7 @@ static int ds2784_notifier_func(struct notifier_block *nfb, case DS2784_CHARGING_CONTROL: if (htc_batt_info.charger == LINEAR_CHARGER) battery_charging_ctrl(arg); -// else if(htc_batt_info.charger == SWITCH_CHARGER) +// else if(htc_batt_info.charger == SWITCH_CHARGER) // set_charger_ctrl(arg); break; case DS2784_LEVEL_UPDATE: @@ -1679,7 +1668,7 @@ static int htc_battery_probe(struct platform_device *pdev) #ifdef CONFIG_BATTERY_DS2784 if (pdata->guage_driver == GUAGE_DS2784) ds2784_register_notifier(&ds2784_notifier); -#elif defined(CONFIG_BATTERY_DS2746) +#elif CONFIG_BATTERY_DS2746 if (pdata->guage_driver == GUAGE_DS2746) ds2746_register_notifier(&ds2784_notifier); #endif diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c index 36208fe0baae6..54b6f982cfbe6 100644 --- a/drivers/input/keyreset.c +++ b/drivers/input/keyreset.c @@ -19,10 +19,10 @@ #include #include #include -#include #include +#include - +#define KEYRESET_DELAY 3*HZ struct keyreset_state { struct input_handler input_handler; unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; @@ -36,15 +36,16 @@ struct keyreset_state { int (*reset_fn)(void); }; -int restart_requested; +static int restart_requested; static void deferred_restart(struct work_struct *dummy) { + pr_info("keyreset::%s in\n", __func__); restart_requested = 2; sys_sync(); restart_requested = 3; kernel_restart(NULL); } -static DECLARE_WORK(restart_work, deferred_restart); +static DECLARE_DELAYED_WORK(restart_work, deferred_restart); static void keyreset_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) @@ -88,6 +89,7 @@ static void keyreset_event(struct input_handle *handle, unsigned int type, state->restart_disabled = 1; if (restart_requested) panic("keyboard reset failed, %d", restart_requested); + if (state->reset_fn) { restart_requested = state->reset_fn(); } else { diff --git a/drivers/input/misc/keychord.c b/drivers/input/misc/keychord.c index ca23905f30467..5e5d78534f3d5 100644 --- a/drivers/input/misc/keychord.c +++ b/drivers/input/misc/keychord.c @@ -27,6 +27,8 @@ #define KEYCHORD_NAME "keychord" #define BUFFER_SIZE 16 +#define MATCH_COUNT 3 +#define KEYCHORD_TIMEOUT 5*HZ MODULE_AUTHOR("Mike Lockwood "); MODULE_DESCRIPTION("Key chord input driver"); @@ -60,8 +62,20 @@ struct keychord_device { unsigned char head; unsigned char tail; __u16 buff[BUFFER_SIZE]; + struct delayed_work keychord_work; + int8_t keychord_match_count; }; +static void keychord_timeout(struct work_struct *work) +{ + struct keychord_device *kdev = + container_of(work, struct keychord_device, keychord_work.work); + + pr_info("%s: match_count=%d\n", __func__, kdev->keychord_match_count); + if (kdev->keychord_match_count) + kdev->keychord_match_count = 0; +} + static int check_keychord(struct keychord_device *kdev, struct input_keychord *keychord) { @@ -75,8 +89,18 @@ static int check_keychord(struct keychord_device *kdev, return 0; } - /* we have a match */ - return 1; + if (kdev->keychord_match_count++ == 0) + schedule_delayed_work(&kdev->keychord_work, KEYCHORD_TIMEOUT); + + if (kdev->keychord_match_count == MATCH_COUNT) { + if (cancel_delayed_work(&kdev->keychord_work)) { + kdev->keychord_match_count = 0; + return 1; + } else + pr_info("%s: timeout already started\n", __func__); + } + + return 0; } static void keychord_event(struct input_handle *handle, unsigned int type, @@ -344,6 +368,7 @@ static int keychord_open(struct inode *inode, struct file *file) file->private_data = kdev; + INIT_DELAYED_WORK(&kdev->keychord_work, keychord_timeout); return 0; } diff --git a/drivers/mfd/tps65200.c b/drivers/mfd/tps65200.c index df2768b6bb1ac..4d987dc8f78bd 100644 --- a/drivers/mfd/tps65200.c +++ b/drivers/mfd/tps65200.c @@ -22,7 +22,7 @@ #include static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; - +static int tps65200_initial = -1; /** * Insmod parameters */ @@ -177,19 +177,21 @@ int tps_set_charger_ctrl(u32 ctl) u8 status; u8 regh; + if (tps65200_initial < 0) + return 0; + switch (ctl) { case DISABLE: pr_info("Switch charger OFF\n"); tps65200_i2c_write_byte(0x29, 0x01); tps65200_i2c_write_byte(0x28, 0x00); - tps65200_i2c_read_byte(&status, 0x09); - pr_info("TPS65200 INT2 %x\n", status); break; case ENABLE_SLOW_CHG: pr_info("Switch charger ON (SLOW)\n"); tps65200_i2c_write_byte(0x29, 0x01); tps65200_i2c_write_byte(0x2A, 0x00); tps65200_i2c_write_byte(0x86, 0x03); + tps65200_i2c_write_byte(0x63, 0x02); break; case ENABLE_FAST_CHG: pr_info("Switch charger ON (FAST)\n"); @@ -209,7 +211,8 @@ int tps_set_charger_ctrl(u32 ctl) case CHECK_CHG: pr_info("Switch charger CHECK \n"); tps65200_i2c_read_byte(&status, 0x06); - pr_info("TPS65200 STATUS_A%x\n", status); + tps65200_i2c_read_byte(®h, 0x09); + pr_info("TPS65200 STATUS_A%x, INT2:%x\n", status, regh); break; case SET_ICL500: pr_info("Switch charger SET_ICL500 \n"); @@ -219,10 +222,38 @@ int tps_set_charger_ctrl(u32 ctl) pr_info("Switch charger SET_ICL100 \n"); tps65200_i2c_write_byte(0x23, 0x02); break; + case CHECK_INT1: + pr_info("Switch charger CHECK_INT1 \n"); + tps65200_i2c_read_byte(&status, 0x08); + pr_info("Switch charger CHECK_INT1: regh 0x08h=%x\n", status); + result = (int)status; + break; case CHECK_INT2: pr_info("Switch charger CHECK_INT2 \n"); tps65200_i2c_read_byte(&status, 0x09); pr_info("TPS65200 INT2 %x\n", status); + result = (int)status; + break; + case CHECK_CONTROL: + pr_info("Switch charger CHECK_CONTROL \n"); + tps65200_i2c_read_byte(&status, 0x00); + pr_info("TPS65200 regh 0x00=%x\n", regh); + break; + case OVERTEMP_VREG_4060: + pr_info("Switch charger OVERTEMP_VREG_4060 \n"); + tps65200_i2c_read_byte(®h, 0x02); + regh = (regh & 0xC0) | 0x1C; + tps65200_i2c_write_byte(regh, 0x02); + tps65200_i2c_read_byte(®h, 0x02); + pr_info("Switch charger OVERTEMP_VREG_4060: regh 0x02=%x\n", regh); + break; + case NORMALTEMP_VREG_4200: + pr_info("Switch charger NORMALTEMP_VREG_4200 \n"); + tps65200_i2c_read_byte(®h, 0x02); + regh = (regh & 0xC0) | 0X23; + tps65200_i2c_write_byte(regh, 0x02); + tps65200_i2c_read_byte(®h, 0x02); + pr_info("Switch charger NORMALTEMP_VREG_4200: regh 0x02=%x\n", regh); break; default: pr_info("%s: Not supported battery ctr called.!", __func__); @@ -275,6 +306,7 @@ static int tps65200_probe(struct i2c_client *client, data->address = client->addr; data->client = client; mutex_init(&data->xfer_lock); + tps65200_initial = 1; pr_info("[TPS65200]: Driver registration done\n"); return 0; } diff --git a/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c index a6ff8d9d73c66..37760be19fe29 100644 --- a/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c +++ b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c @@ -46,6 +46,8 @@ kgsl_cmdstream_readtimestamp(struct kgsl_device *device, KGSL_CMDSTREAM_GET_EOP_TIMESTAMP(device, (unsigned int *)×tamp); + rmb(); + KGSL_CMD_VDBG("return %d\n", timestamp); return timestamp; diff --git a/drivers/video/msm/hdmi/fb-hdmi.c b/drivers/video/msm/hdmi/fb-hdmi.c index 7daf6d1abb9cc..ba6a309c7ba94 100644 --- a/drivers/video/msm/hdmi/fb-hdmi.c +++ b/drivers/video/msm/hdmi/fb-hdmi.c @@ -88,6 +88,11 @@ static int hdmifb_release(struct fb_info *info, int user) return 0; } +static int hdmifb_read(struct fb_info *info, int user) +{ + pr_info("%s\n", __func__); + return 0; +} static int hdmifb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { u32 size; @@ -412,6 +417,7 @@ static struct fb_ops hdmi_fb_ops = { .fb_copyarea = hdmifb_copyarea, .fb_imageblit = hdmifb_imageblit, .fb_ioctl = hdmifb_ioctl, + .fb_read = hdmifb_read, }; #ifdef CONFIG_HAS_EARLYSUSPEND diff --git a/include/linux/smb329.h b/include/linux/smb329.h new file mode 100644 index 0000000000000..8ce3440e0bc0e --- /dev/null +++ b/include/linux/smb329.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2007 HTC Incorporated + * + * 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 _SMB329_H_ +#define _SMB329_H_ +#include +#include + +#ifdef CONFIG_SMB329 +extern int set_charger_ctrl(u32 ctl); +#else +static int set_charger_ctrl(u32 ctl) {return 0 ; } +#endif +#endif From 07376811f99d986e40889cb50b1113a2a9abb112 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 2 Nov 2010 03:05:15 -0400 Subject: [PATCH 1042/2556] msm: QDSP6/DAL/MICROP upgrade from HTC Includes Atmega microp common driver, DAL, new QDSP6, and HTC headset drivers. Change-Id: I2567917f40b5e467a93087bc3456e738d9535fdb --- arch/arm/mach-msm/Kconfig | 42 + arch/arm/mach-msm/Makefile | 27 +- arch/arm/mach-msm/atmega_microp_common.c | 833 ++++++++++++++++ arch/arm/mach-msm/dal.c | 32 +- arch/arm/mach-msm/dal.h | 2 +- arch/arm/mach-msm/drv_callback.c | 60 ++ arch/arm/mach-msm/htc_acoustic_qsd.c | 137 ++- arch/arm/mach-msm/htc_headset_gpio.c | 271 +++++ arch/arm/mach-msm/htc_headset_mgr.c | 926 ++++++++++++++++++ arch/arm/mach-msm/htc_headset_microp.c | 418 ++++++++ .../arm/mach-msm/include/mach/atmega_microp.h | 265 +++++ arch/arm/mach-msm/include/mach/drv_callback.h | 28 + .../mach-msm/include/mach/htc_acoustic_qsd.h | 2 + .../mach-msm/include/mach/htc_headset_gpio.h | 29 + .../mach-msm/include/mach/htc_headset_mgr.h | 394 ++++++++ .../include/mach/htc_headset_microp.h | 48 + .../arm/mach-msm/include/mach/msm_audio_qcp.h | 59 ++ .../mach-msm/include/mach/msm_qdsp6_audio.h | 13 +- arch/arm/mach-msm/qdsp6/Makefile | 6 +- arch/arm/mach-msm/qdsp6/audio_ctl.c | 44 +- arch/arm/mach-msm/qdsp6/mp3.c | 2 +- arch/arm/mach-msm/qdsp6/msm_q6venc.c | 21 +- arch/arm/mach-msm/qdsp6/pcm_in.c | 2 +- arch/arm/mach-msm/qdsp6/pcm_out.c | 12 +- arch/arm/mach-msm/qdsp6/q6audio.c | 286 +++++- include/linux/msm_audio.h | 97 +- 26 files changed, 3948 insertions(+), 108 deletions(-) create mode 100644 arch/arm/mach-msm/atmega_microp_common.c create mode 100644 arch/arm/mach-msm/drv_callback.c create mode 100644 arch/arm/mach-msm/htc_headset_gpio.c create mode 100644 arch/arm/mach-msm/htc_headset_mgr.c create mode 100644 arch/arm/mach-msm/htc_headset_microp.c create mode 100644 arch/arm/mach-msm/include/mach/atmega_microp.h create mode 100644 arch/arm/mach-msm/include/mach/drv_callback.h create mode 100644 arch/arm/mach-msm/include/mach/htc_headset_gpio.h create mode 100644 arch/arm/mach-msm/include/mach/htc_headset_mgr.h create mode 100644 arch/arm/mach-msm/include/mach/htc_headset_microp.h create mode 100644 arch/arm/mach-msm/include/mach/msm_audio_qcp.h diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index bbb5e09004d1c..e936fe8042f93 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -569,6 +569,21 @@ config MSM_ADSP help Provides access to registers needed by the userspace aDSP library. +config HTC_ACOUSTIC + tristate "HTC acoustic driver" + default y + help + The driver provide user space use shared memory allocate by using + RPC code. + +config HTC_ACOUSTIC_QSD + tristate "HTC acoustic driver for QSD" + depends on ARCH_QSD8X50 + default n + help + Provides user space use shared memory allocate by using RPC code. + Provides headset amp, mic bias and speaker amp control. + config MSM_ADSP_REPORT_EVENTS bool "Report modem events from the DSP" default y @@ -616,6 +631,32 @@ config ARCH_MSM_FLASHLIGHT help The flashlight driver is for MSM series. +config MICROP_COMMON + tristate "MICROP COMMON Driver" + depends on I2C + help + HTC Microp-P support. + +config HTC_HEADSET_MGR + tristate "HTC headset manager driver" + default n + help + Provides support of HTC headset manager. + +config HTC_HEADSET_GPIO + tristate "HTC GPIO headset detection driver" + depends on HTC_HEADSET_MGR + default n + help + Provides support of HTC GPIO headset detection. + +config HTC_HEADSET_MICROP + tristate "HTC Micro-P headset detection driver" + depends on HTC_HEADSET_MGR && MICROP_COMMON + default n + help + Provides support of HTC Micro-P headset detection. + config VIRTUAL_KPANIC_PARTITION bool "Create virtual kpanic partition" default n @@ -638,4 +679,5 @@ config VIRTUAL_KPANIC_SRC help Sets the partition to steal from to make the virtual one. + endif diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 354484f9f3fb6..33870c01524a2 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -6,6 +6,7 @@ endif obj-y += nand_partitions.o obj-y += pmic.o +obj-y += drv_callback.o obj-$(CONFIG_ARCH_MSM_ARM11) += acpuclock-arm11.o idle.o obj-$(CONFIG_ARCH_MSM_SCORPION) += idle-v7.o @@ -14,13 +15,6 @@ obj-$(CONFIG_ARCH_MSM_SCORPION) += arch-init-scorpion.o obj-$(CONFIG_ARCH_QSD8X50) += acpuclock-qsd8x50.o obj-$(CONFIG_ARCH_MSM7X30) += acpuclock-7x30.o -ifndef CONFIG_ARCH_MSM8X60 -obj-y += irq.o -endif - -obj-$(CONFIG_ARCH_MSM8X60) += clock-dummy.o iommu.o iommu_dev.o devices-msm8x60-iommu.o -obj-$(CONFIG_MSM_PROC_COMM) += proc_comm.o clock-pcom.o vreg.o -obj-$(CONFIG_MSM_PROC_COMM) += clock.o obj-$(CONFIG_ARCH_QSD8X50) += sirc.o obj-$(CONFIG_MSM_FIQ_SUPPORT) += fiq_glue.o obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o @@ -47,30 +41,30 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq.o obj-$(CONFIG_MSM_REMOTE_SPINLOCK) += remote_spinlock.o obj-$(CONFIG_MSM_SSBI) += ssbi.o -obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o -obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o -obj-$(CONFIG_MACH_TROUT) += board-trout-keypad.o -obj-$(CONFIG_MACH_TROUT) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o obj-$(CONFIG_MACH_TROUT) += board-trout-rfkill.o obj-$(CONFIG_MACH_TROUT) += board-trout-wifi.o obj-$(CONFIG_MACH_TROUT) += devices_htc.o obj-$(CONFIG_TROUT_BATTCHG) += htc_battery_trout.o + obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire.o board-sapphire-gpio.o obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-mmc.o board-sapphire-wifi.o obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-rfkill.o msm_vibrator.o -obj-$(CONFIG_MACH_SAPPHIRE) += devices-msm7x00.o devices_htc.o -obj-$(CONFIG_MACH_SAPPHIRE) += htc_akm_cal.o htc_wifi_nvs.o htc_acoustic.o +obj-$(CONFIG_MACH_SAPPHIRE) += devices_htc.o +obj-$(CONFIG_MACH_SAPPHIRE) += htc_akm_cal.o htc_wifi_nvs.o + obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi.o board-mahimahi-panel.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-keypad.o board-mahimahi-mmc.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-rfkill.o htc_wifi_nvs.o htc_awb_cal.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-wifi.o board-mahimahi-audio.o obj-$(CONFIG_MACH_MAHIMAHI) += msm_vibrator.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-microp.o -obj-$(CONFIG_MACH_MAHIMAHI) += htc_acoustic_qsd.o +obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-flashlight.o + obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-panel.o fish_battery.o + obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-keypad.o fish_battery.o obj-$(CONFIG_MACH_SWORDFISH) += board-swordfish-panel.o @@ -113,6 +107,11 @@ obj-$(CONFIG_HTC_PWRSPLY) += htc_power_supply.o obj-$(CONFIG_HTC_HEADSET) += htc_headset.o obj-$(CONFIG_HTC_35MM_JACK) += htc_35mm_jack.o +obj-$(CONFIG_MICROP_COMMON) += atmega_microp_common.o +obj-$(CONFIG_HTC_HEADSET_MGR) += htc_headset_mgr.o +obj-$(CONFIG_HTC_HEADSET_GPIO) += htc_headset_gpio.o +obj-$(CONFIG_HTC_HEADSET_MICROP) += htc_headset_microp.o + obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-tpa2018d1.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-smb329.o diff --git a/arch/arm/mach-msm/atmega_microp_common.c b/arch/arm/mach-msm/atmega_microp_common.c new file mode 100644 index 0000000000000..fa788eb17e094 --- /dev/null +++ b/arch/arm/mach-msm/atmega_microp_common.c @@ -0,0 +1,833 @@ +/* arch/arm/mach-msm/atmega_microp_common.c + * Copyright (C) 2009 HTC Corporation. + * + * 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 "proc_comm.h" + + +#define I2C_READ_RETRY_TIMES 10 +#define I2C_WRITE_RETRY_TIMES 10 +#define MICROP_I2C_WRITE_BLOCK_SIZE 80 + +static struct i2c_client *private_microp_client; +static struct microp_ops *board_ops; + +static int microp_rw_delay; + +static char *hex2string(uint8_t *data, int len) +{ + static char buf[MICROP_I2C_WRITE_BLOCK_SIZE*4]; + int i; + + i = (sizeof(buf) - 1) / 4; + if (len > i) + len = i; + + for (i = 0; i < len; i++) + sprintf(buf + i * 4, "[%02X]", data[i]); + + return buf; +} + +static int i2c_read_block(struct i2c_client *client, uint8_t addr, + uint8_t *data, int length) +{ + int retry; + struct microp_i2c_client_data *cdata; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + } + }; + + cdata = i2c_get_clientdata(client); + mutex_lock(&cdata->microp_i2c_rw_mutex); + msleep(1); + for (retry = 0; retry <= I2C_READ_RETRY_TIMES; retry++) { + if (i2c_transfer(client->adapter, msgs, 2) == 2) + break; + msleep(microp_rw_delay); + } + mutex_unlock(&cdata->microp_i2c_rw_mutex); + dev_dbg(&client->dev, "R [%02X] = %s\n", + addr, hex2string(data, length)); + + if (retry > I2C_READ_RETRY_TIMES) { + dev_err(&client->dev, "i2c_read_block retry over %d\n", + I2C_READ_RETRY_TIMES); + return -EIO; + } + + return 0; +} + +static int i2c_write_block(struct i2c_client *client, uint8_t addr, + uint8_t *data, int length) +{ + int retry; + uint8_t buf[MICROP_I2C_WRITE_BLOCK_SIZE]; + int i; + struct microp_i2c_client_data *cdata; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length + 1, + .buf = buf, + } + }; + + dev_dbg(&client->dev, "W [%02X] = %s\n", + addr, hex2string(data, length)); + + cdata = i2c_get_clientdata(client); + if (length + 1 > MICROP_I2C_WRITE_BLOCK_SIZE) { + dev_err(&client->dev, "i2c_write_block length too long\n"); + return -E2BIG; + } + + buf[0] = addr; + for (i = 0; i < length; i++) + buf[i+1] = data[i]; + + mutex_lock(&cdata->microp_i2c_rw_mutex); + msleep(1); + for (retry = 0; retry <= I2C_WRITE_RETRY_TIMES; retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) + break; + msleep(microp_rw_delay); + } + if (retry > I2C_WRITE_RETRY_TIMES) { + dev_err(&client->dev, "i2c_write_block retry over %d\n", + I2C_WRITE_RETRY_TIMES); + mutex_unlock(&cdata->microp_i2c_rw_mutex); + return -EIO; + } + if (addr == MICROP_I2C_WCMD_LCM_BURST_EN) + udelay(500);/*1.5ms for microp SPI write */ + mutex_unlock(&cdata->microp_i2c_rw_mutex); + + return 0; +} + +int microp_i2c_read(uint8_t addr, uint8_t *data, int length) +{ + struct i2c_client *client = private_microp_client; + + if (!client) { + printk(KERN_ERR "%s: dataset: client is empty\n", __func__); + return -EIO; + } + + if (i2c_read_block(client, addr, data, length) < 0) { + dev_err(&client->dev, "%s: write microp i2c fail\n", __func__); + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL(microp_i2c_read); + +int microp_i2c_write(uint8_t addr, uint8_t *data, int length) +{ + struct i2c_client *client = private_microp_client; + + if (!client) { + printk(KERN_ERR "%s: dataset: client is empty\n", __func__); + return -EIO; + } + + if (i2c_write_block(client, addr, data, length) < 0) { + dev_err(&client->dev, "%s: write microp i2c fail\n", __func__); + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL(microp_i2c_write); + +void microp_mobeam_enable(int enable) +{ + if (enable) + microp_rw_delay = 500; + else + microp_rw_delay = 5; +} +EXPORT_SYMBOL(microp_mobeam_enable); + +void microp_register_ops(struct microp_ops *ops) +{ + board_ops = ops; +} + +int microp_function_check(struct i2c_client *client, uint8_t category) +{ + struct microp_i2c_platform_data *pdata; + int i, ret = -1; + + pdata = client->dev.platform_data; + + for (i = 0; i < pdata->num_functions; i++) { + if (pdata->microp_function[i].category == category) { + ret = i; + break; + } + } + if (ret < 0) + pr_err("%s: No function %d !!\n", __func__, category); + + return ret; +} + +int microp_write_interrupt(struct i2c_client *client, + uint16_t interrupt, uint8_t enable) +{ + uint8_t data[2], addr; + int ret = -1; + + if (enable) + addr = MICROP_I2C_WCMD_GPI_INT_CTL_EN; + else + addr = MICROP_I2C_WCMD_GPI_INT_CTL_DIS; + + data[0] = interrupt >> 8; + data[1] = interrupt & 0xFF; + ret = i2c_write_block(client, addr, data, 2); + + if (ret < 0) + dev_err(&client->dev, "%s: %s 0x%x interrupt failed\n", + __func__, (enable ? "enable" : "disable"), interrupt); + return ret; +} + +int microp_read_adc(uint8_t *data) +{ + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + int ret = 0; + + client = private_microp_client; + cdata = i2c_get_clientdata(client); + + mutex_lock(&cdata->microp_adc_mutex); + if (i2c_write_block(client, MICROP_I2C_WCMD_READ_ADC_VALUE_REQ, + data, 2) < 0) { + dev_err(&client->dev, "%s: request adc fail\n", __func__); + ret = -EIO; + goto exit; + } + memset(data, 0x00, sizeof(data)); + if (i2c_read_block(client, MICROP_I2C_RCMD_ADC_VALUE, data, 2) < 0) { + dev_err(&client->dev, "%s: read adc fail\n", __func__); + ret = -EIO; + goto exit; + } +exit: + mutex_unlock(&cdata->microp_adc_mutex); + return ret; +} + +EXPORT_SYMBOL(microp_read_adc); + +int microp_read_gpio_status(uint8_t *data) +{ + struct i2c_client *client; + struct microp_i2c_platform_data *pdata; + int length; + + client = private_microp_client; + pdata = client->dev.platform_data; + + if (pdata->cmd_diff & CMD_83_DIFF) + length = 2; + else + length = 3; + memset(data, 0x00, sizeof(data)); + if (i2c_read_block(client, MICROP_I2C_RCMD_GPIO_STATUS, + data, length) < 0) { + dev_err(&client->dev, "%s: read gpio status fail\n", __func__); + return -EIO; + } + return 0; +} + +static void microp_pm_power_off(struct i2c_client *client) +{ + return; +} + +static void microp_reset_system(void) +{ + return; +} + +static int microp_oj_intr_enable(struct i2c_client *client, uint8_t enable) +{ + struct microp_i2c_client_data *cdata; + + cdata = i2c_get_clientdata(client); + enable = enable ? 1 : 0; + return microp_write_interrupt(client, + cdata->int_pin.int_oj, enable); +} + +static int microp_spi_enable(struct i2c_client *client, uint8_t enable) +{ + uint8_t data; + int ret = 0; + + data = enable ? 1 : 0; + ret = i2c_write_block(client, MICROP_I2C_WCMD_SPI_EN, &data, 1); + if (ret != 0) + printk(KERN_ERR "%s: set SPI %s fail\n", __func__, + (enable ? "enable" : "disable")); + + return ret; +} + +int microp_spi_vote_enable(int spi_device, uint8_t enable) +{ + struct i2c_client *client = private_microp_client; + struct microp_i2c_client_data *cdata; + struct microp_i2c_platform_data *pdata; + uint8_t data[2] = {0, 0}; + int ret = 0; + + if (!client) { + printk(KERN_ERR "%s: dataset: client is empty\n", __func__); + return -EIO; + } + cdata = i2c_get_clientdata(client); + pdata = client->dev.platform_data; + + if (spi_device == SPI_OJ) + microp_oj_intr_enable(client, enable); + + mutex_lock(&cdata->microp_adc_mutex); + if (enable) + cdata->spi_devices_vote |= spi_device; + else + cdata->spi_devices_vote &= ~spi_device; + + ret = i2c_read_block(client, MICROP_I2C_RCMD_SPI_BL_STATUS, data, 2); + if (ret != 0) { + printk(KERN_ERR "%s: read SPI/BL status fail\n", __func__); + goto exit; + } + + if ((data[1] & 0x01) == + ((pdata->spi_devices & cdata->spi_devices_vote) ? 1 : 0)) + goto exit; + + if (pdata->spi_devices & cdata->spi_devices_vote) + enable = 1; + else + enable = 0; + mutex_unlock(&cdata->microp_adc_mutex); + + ret = microp_spi_enable(client, enable); + return ret; + +exit: + mutex_unlock(&cdata->microp_adc_mutex); + return ret; + +} + +EXPORT_SYMBOL(microp_spi_vote_enable); + +static void microp_reset_microp(struct i2c_client *client) +{ + struct microp_i2c_platform_data *pdata; + + pdata = client->dev.platform_data; + + gpio_set_value(pdata->gpio_reset, 0); + udelay(120); + gpio_set_value(pdata->gpio_reset, 1); + mdelay(5); +} + +static ssize_t microp_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct microp_i2c_client_data *cdata; + + cdata = i2c_get_clientdata(to_i2c_client(dev)); + + return sprintf(buf, "%04X\n", cdata->version); +} + +static DEVICE_ATTR(version, 0644, microp_version_show, NULL); + +static ssize_t microp_reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + int val; + + val = -1; + sscanf(buf, "%u", &val); + if (val != 1) + return -EINVAL; + + client = to_i2c_client(dev); + cdata = i2c_get_clientdata(client); + + microp_reset_microp(client); + if (board_ops->init_microp_func) + board_ops->init_microp_func(client); + + return count; +} + +static DEVICE_ATTR(reset, 0644, NULL, microp_reset_store); + +static ssize_t microp_gpio_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint8_t data[3] = {0, 0, 0}; + int ret; + + microp_read_gpio_status(data); + ret = sprintf(buf, "PB = 0x%x, PC = 0x%x, PD = 0x%x\n", + data[0], data[1], data[2]); + + return ret; +} + +static ssize_t microp_gpio_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + int enable = 0, tmp[3] = {0, 0, 0}; + uint8_t addr, data[3] = {0, 0, 0}; + + sscanf(buf, "%d %d %d %d", &enable, &tmp[0], &tmp[1], &tmp[2]); + + if (enable != 0 && enable != 1) + return -EINVAL; + + client = to_i2c_client(dev); + cdata = i2c_get_clientdata(client); + + if (enable) + addr = MICROP_I2C_WCMD_GPO_LED_STATUS_EN; + else + addr = MICROP_I2C_WCMD_GPO_LED_STATUS_DIS; + data[0] = (uint8_t)tmp[0]; + data[1] = (uint8_t)tmp[1]; + data[2] = (uint8_t)tmp[2]; + i2c_write_block(client, addr, data, 3); + + return count; +} + +static DEVICE_ATTR(gpio, 0644, microp_gpio_show, + microp_gpio_store); + +static irqreturn_t microp_intr_irq_handler(int irq, void *dev_id) +{ + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + + client = to_i2c_client(dev_id); + cdata = i2c_get_clientdata(client); + + disable_irq_nosync(client->irq); + queue_work(cdata->microp_queue, &cdata->microp_intr_work); + return IRQ_HANDLED; +} + +static void microp_int_dispatch(u32 status) +{ + unsigned int mask; + int irq; + + while (status) { + mask = status & -status; + irq = fls(mask) - 1; + status &= ~mask; + generic_handle_irq(FIRST_MICROP_IRQ + irq); + } +} + +static enum hrtimer_restart hr_dispath_irq_func(struct hrtimer *data) +{ + struct i2c_client *client = private_microp_client; + struct microp_i2c_client_data *cdata; + + cdata = i2c_get_clientdata(client); + microp_int_dispatch(cdata->intr_status); + cdata->intr_status = 0; + return HRTIMER_NORESTART; +} + + +static void microp_intr_work_func(struct work_struct *work) +{ + struct i2c_client *client = private_microp_client; + struct microp_i2c_client_data *cdata; + struct microp_i2c_platform_data *pdata; + uint8_t data[3]; + uint16_t intr_status = 0; + int sd_insert = 0; + ktime_t zero_debounce; + + zero_debounce = ktime_set(0, 0); /* No debounce time */ + + if (!client) { + printk(KERN_ERR "%s: dataset: client is empty\n", __func__); + return; + } + + cdata = i2c_get_clientdata(client); + pdata = client->dev.platform_data; + + memset(data, 0x00, sizeof(data)); + if (i2c_read_block(client, MICROP_I2C_RCMD_GPI_INT_STATUS, + data, 2) < 0) + dev_err(&client->dev, "%s: read interrupt status fail\n", + __func__); + intr_status = data[0]<<8 | data[1]; + if (i2c_write_block(client, MICROP_I2C_WCMD_GPI_INT_STATUS_CLR, + data, 2) < 0) + dev_err(&client->dev, "%s: clear interrupt status fail\n", + __func__); + + if (intr_status & cdata->int_pin.int_reset) { + dev_info(&client->dev, "Reset button is pressed\n"); + microp_reset_system(); + } + if (intr_status & cdata->int_pin.int_simcard) { + dev_info(&client->dev, "SIM Card is plugged/unplugged\n"); + microp_pm_power_off(client); + } + + if (intr_status & cdata->int_pin.int_sdcard) { + dev_info(&client->dev, "SD Card is plugged/unplugged\n"); + msleep(300); + microp_read_gpio_status(data); + sd_insert = ((data[0] << 16 | data[1] << 8 | data[2]) + & cdata->gpio.sdcard) ? 1 : 0; + if (sd_insert != cdata->sdcard_is_in) { + cdata->sdcard_is_in = sd_insert; + cnf_driver_event("sdcard_detect", &cdata->sdcard_is_in); + } + } + + cdata->intr_status = intr_status; + hrtimer_start(&cdata->gen_irq_timer, zero_debounce, HRTIMER_MODE_REL); + enable_irq(client->irq); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void microp_early_suspend(struct early_suspend *h) +{ + struct microp_i2c_client_data *cdata; + struct i2c_client *client = private_microp_client; + struct microp_i2c_platform_data *pdata; + + if (!client) { + printk(KERN_ERR "%s: dataset: client is empty\n", __func__); + return; + } + cdata = i2c_get_clientdata(client); + pdata = client->dev.platform_data; + + atomic_set(&cdata->microp_is_suspend, 1); +} + +static void microp_late_resume(struct early_suspend *h) +{ + struct i2c_client *client = private_microp_client; + struct microp_i2c_client_data *cdata; + struct microp_i2c_platform_data *pdata; + + if (!client) { + printk(KERN_ERR "%s: dataset: client is empty\n", __func__); + return; + } + cdata = i2c_get_clientdata(client); + pdata = client->dev.platform_data; + + atomic_set(&cdata->microp_is_suspend, 0); +} +#endif + +static int __devexit microp_i2c_remove(struct i2c_client *client) +{ + struct microp_i2c_platform_data *pdata; + struct microp_i2c_client_data *cdata; + + pdata = client->dev.platform_data; + cdata = i2c_get_clientdata(client); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&cdata->early_suspend); +#endif + + if (client->irq) + free_irq(client->irq, &client->dev); + + gpio_free(pdata->gpio_reset); + + device_remove_file(&client->dev, &dev_attr_reset); + device_remove_file(&client->dev, &dev_attr_version); + device_remove_file(&client->dev, &dev_attr_gpio); + destroy_workqueue(cdata->microp_queue); + kfree(cdata); + + return 0; +} + +static int microp_i2c_suspend(struct i2c_client *client, + pm_message_t mesg) +{ + return 0; +} + +static int microp_i2c_resume(struct i2c_client *client) +{ + return 0; +} + +static void register_microp_devices(struct platform_device *devices, int num) +{ + int i; + for (i = 0; i < num; i++) { + platform_device_register(devices + i); + dev_set_drvdata(&(devices + i)->dev, private_microp_client); + } +} + +static int microp_i2c_probe(struct i2c_client *client + , const struct i2c_device_id *id) +{ + struct microp_i2c_platform_data *pdata; + struct microp_i2c_client_data *cdata; + uint8_t data[6]; + int ret; + + cdata = kzalloc(sizeof(struct microp_i2c_client_data), GFP_KERNEL); + if (!cdata) { + ret = -ENOMEM; + dev_err(&client->dev, "failed on allocat cdata\n"); + goto err_cdata; + } + + i2c_set_clientdata(client, cdata); + + mutex_init(&cdata->microp_adc_mutex); + mutex_init(&cdata->microp_i2c_rw_mutex); + + private_microp_client = client; + pdata = client->dev.platform_data; + if (!pdata) { + ret = -EBUSY; + dev_err(&client->dev, "failed on get pdata\n"); + goto err_exit; + } + pdata->dev_id = (void *)&client->dev; + microp_rw_delay = 5; + + ret = i2c_read_block(client, MICROP_I2C_RCMD_VERSION, data, 2); + if (ret || !(data[0] && data[1])) { + ret = -ENODEV; + dev_err(&client->dev, "failed on get microp version\n"); + goto err_exit; + } + dev_info(&client->dev, "microp version [%02X][%02X]\n", + data[0], data[1]); + + ret = gpio_request(pdata->gpio_reset, "atmega_microp"); + if (ret < 0) { + dev_err(&client->dev, "failed on request gpio reset\n"); + goto err_exit; + } + ret = gpio_direction_output(pdata->gpio_reset, 1); + if (ret < 0) { + dev_err(&client->dev, + "failed on gpio_direction_output reset\n"); + goto err_gpio_reset; + } + + cdata->version = data[0] << 8 | data[1]; + atomic_set(&cdata->microp_is_suspend, 0); + + cdata->spi_devices_vote = pdata->spi_devices_init; + + cdata->intr_status = 0; + hrtimer_init(&cdata->gen_irq_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + cdata->gen_irq_timer.function = hr_dispath_irq_func; + + cdata->microp_queue = create_singlethread_workqueue("microp_work_q"); + if (cdata->microp_queue == NULL) { + ret = -ENOMEM; + goto err_create_work_queue; + } + + if (client->irq) { + INIT_WORK(&cdata->microp_intr_work, microp_intr_work_func); + + ret = request_irq(client->irq, microp_intr_irq_handler, + IRQF_TRIGGER_LOW, "microp_intrrupt", + &client->dev); + if (ret) { + dev_err(&client->dev, "request_irq failed\n"); + goto err_intr; + } + ret = set_irq_wake(client->irq, 1); + if (ret) { + dev_err(&client->dev, "set_irq_wake failed\n"); + goto err_intr; + } + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + cdata->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + cdata->early_suspend.suspend = microp_early_suspend; + cdata->early_suspend.resume = microp_late_resume; + register_early_suspend(&cdata->early_suspend); +#endif + ret = device_create_file(&client->dev, &dev_attr_reset); + ret = device_create_file(&client->dev, &dev_attr_version); + ret = device_create_file(&client->dev, &dev_attr_gpio); + + register_microp_devices(pdata->microp_devices, pdata->num_devices); + if (board_ops->init_microp_func) { + ret = board_ops->init_microp_func(client); + if (ret) { + dev_err(&client->dev, + "failed on microp function initialize\n"); + goto err_fun_init; + } + } + + return 0; + +err_fun_init: +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&cdata->early_suspend); +#endif + device_remove_file(&client->dev, &dev_attr_reset); + device_remove_file(&client->dev, &dev_attr_version); + device_remove_file(&client->dev, &dev_attr_gpio); + destroy_workqueue(cdata->microp_queue); +err_intr: +err_create_work_queue: + kfree(cdata); +err_gpio_reset: + gpio_free(pdata->gpio_reset); +err_exit: + private_microp_client = NULL; +err_cdata: + return ret; +} + +static const struct i2c_device_id microp_i2c_id[] = { + { MICROP_I2C_NAME, 0 }, + { } +}; + +static struct i2c_driver microp_i2c_driver = { + .driver = { + .name = MICROP_I2C_NAME, + }, + .id_table = microp_i2c_id, + .probe = microp_i2c_probe, + .suspend = microp_i2c_suspend, + .resume = microp_i2c_resume, + .remove = __devexit_p(microp_i2c_remove), +}; + +static void microp_irq_ack(unsigned int irq) +{ + ; +} + +static void microp_irq_mask(unsigned int irq) +{ + ; +} + +static void microp_irq_unmask(unsigned int irq) +{ + ; +} + +static struct irq_chip microp_irq_chip = { + .name = "microp", + .disable = microp_irq_mask, + .ack = microp_irq_ack, + .mask = microp_irq_mask, + .unmask = microp_irq_unmask, +}; + +static int __init microp_common_init(void) +{ + int ret; + int n, MICROP_IRQ_END = FIRST_MICROP_IRQ + NR_MICROP_IRQS; + + for (n = FIRST_MICROP_IRQ; n < MICROP_IRQ_END; n++) { + set_irq_chip(n, µp_irq_chip); + set_irq_handler(n, handle_level_irq); + set_irq_flags(n, IRQF_VALID); + } + + ret = i2c_add_driver(µp_i2c_driver); + if (ret) + return ret; + return 0; +} + +static void __exit microp_common_exit(void) +{ + i2c_del_driver(µp_i2c_driver); +} + +module_init(microp_common_init); +module_exit(microp_common_exit); + +MODULE_AUTHOR("Eric Huang "); +MODULE_DESCRIPTION("Atmega MicroP driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/dal.c b/arch/arm/mach-msm/dal.c index 971abbbcefc3a..265ff5a1bddb2 100644 --- a/arch/arm/mach-msm/dal.c +++ b/arch/arm/mach-msm/dal.c @@ -568,27 +568,27 @@ int dal_call_f5(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t il } int dal_call_f9(struct dal_client *client, uint32_t ddi, void *obuf, - uint32_t olen) + uint32_t olen) { - uint32_t tmp[128]; - int res; + uint32_t tmp[128]; + int res; - if (olen > sizeof(tmp) - 8) - return -EINVAL; - tmp[0] = olen; + if (olen > sizeof(tmp) - 8) + return -EINVAL; + tmp[0] = olen; - res = dal_call(client, ddi, 9, tmp, sizeof(uint32_t), tmp, - sizeof(tmp)); + res = dal_call(client, ddi, 9, tmp, sizeof(uint32_t), tmp, + sizeof(tmp)); - if (res >= 4) - res = (int)tmp[0]; + if (res >= 4) + res = (int)tmp[0]; - if (!res) { - if (tmp[1] > olen) - return -EIO; - memcpy(obuf, &tmp[2], tmp[1]); - } - return res; + if (!res) { + if (tmp[1] > olen) + return -EIO; + memcpy(obuf, &tmp[2], tmp[1]); + } + return res; } int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1, diff --git a/arch/arm/mach-msm/dal.h b/arch/arm/mach-msm/dal.h index 6e4368295be31..ea4b92a577026 100644 --- a/arch/arm/mach-msm/dal.h +++ b/arch/arm/mach-msm/dal.h @@ -51,7 +51,7 @@ int dal_call_f1(struct dal_client *client, uint32_t ddi, int dal_call_f5(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t ilen); int dal_call_f9(struct dal_client *client, uint32_t ddi, - void *obuf, uint32_t olen); + void *obuf, uint32_t olen); int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1, uint32_t ilen1, void *ibuf2, uint32_t ilen2, void *obuf, uint32_t olen); diff --git a/arch/arm/mach-msm/drv_callback.c b/arch/arm/mach-msm/drv_callback.c new file mode 100644 index 0000000000000..3ba36f96ecddb --- /dev/null +++ b/arch/arm/mach-msm/drv_callback.c @@ -0,0 +1,60 @@ +/* linux/arch/arm/mach-msm/drv_callback.c + * + * Copyright (C) 2009 HTC Corporation. + * + * 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 + +static DECLARE_RWSEM(cnf_driver_list_lock); +static LIST_HEAD(cnf_driver_list); + +int cnf_driver_register(struct cnf_driver *driver) +{ + if(driver){ + rwlock_init(&driver->cnfdrv_list_lock); + + down_write(&cnf_driver_list_lock); + list_add_tail(&driver->next_drv, &cnf_driver_list); + up_write(&cnf_driver_list_lock); + + return 0; + } + else{ + printk(KERN_WARNING "Configurable driver %s failed to " + "register (NULL driver)\n", driver->name); + return -1; + } +} + +int cnf_driver_event(const char *name, void *argu) +{ + struct list_head *listptr; + int ret = -EINVAL; + + down_read(&cnf_driver_list_lock); + list_for_each(listptr, &cnf_driver_list){ + struct cnf_driver *driver; + + driver = list_entry(listptr, struct cnf_driver, next_drv); + if(strcmp(driver->name, name) == 0){ + ret = driver->func(argu); + break; + } + } + up_read(&cnf_driver_list_lock); + + return ret; +} diff --git a/arch/arm/mach-msm/htc_acoustic_qsd.c b/arch/arm/mach-msm/htc_acoustic_qsd.c index 390ab6d7ff2dd..d925329e1fb96 100644 --- a/arch/arm/mach-msm/htc_acoustic_qsd.c +++ b/arch/arm/mach-msm/htc_acoustic_qsd.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "smd_private.h" @@ -43,6 +44,7 @@ #define ONCRPC_UPDATE_ADIE_PROC (2) #define ONCRPC_ENABLE_AUX_PGA_LOOPBACK_PROC (3) #define ONCRPC_FORCE_HEADSET_SPEAKER_PROC (4) +#define ONCRPC_SET_AUX_PGA_GAIN_PROC (5) #define HTC_ACOUSTIC_TABLE_SIZE (0x20000) @@ -54,6 +56,9 @@ static struct msm_rpc_endpoint *endpoint; static struct mutex api_lock; static struct mutex rpc_connect_lock; static struct qsd_acoustic_ops *the_ops; +struct class *htc_class; +static int hac_enable_flag; +struct mutex acoustic_lock; void acoustic_register_ops(struct qsd_acoustic_ops *ops) { @@ -77,7 +82,7 @@ static int is_rpc_connect(void) return 0; } -int turn_mic_bias_on(int on) +int enable_mic_bias(int on) { D("%s called %d\n", __func__, on); if (the_ops->enable_mic_bias) @@ -85,8 +90,29 @@ int turn_mic_bias_on(int on) return 0; } + +int turn_mic_bias_on(int on) +{ + return enable_mic_bias(on); +} EXPORT_SYMBOL(turn_mic_bias_on); +int enable_mos_test(int enable) +{ + static int mos_test_enable; + int res = 0; + if (enable != mos_test_enable) { + D("%s called %d\n", __func__, enable); + if (enable) + res = q6audio_reinit_acdb("default_mos.acdb"); + else + res = q6audio_reinit_acdb("default.acdb"); + mos_test_enable = enable; + } + return res; +} +EXPORT_SYMBOL(enable_mos_test); + int force_headset_speaker_on(int enable) { struct speaker_headset_req { @@ -125,6 +151,25 @@ int enable_aux_loopback(uint32_t enable) } EXPORT_SYMBOL(enable_aux_loopback); +int set_aux_gain(int level) +{ + struct aux_gain_req { + struct rpc_request_hdr hdr; + int level; + } aux_req; + + D("%s called %d\n", __func__, level); + + if (is_rpc_connect() == -1) + return -1; + + aux_req.level = cpu_to_be32(level); + return msm_rpc_call(endpoint, + ONCRPC_SET_AUX_PGA_GAIN_PROC, + &aux_req, sizeof(aux_req), 5 * HZ); +} +EXPORT_SYMBOL(set_aux_gain); + static int acoustic_mmap(struct file *file, struct vm_area_struct *vma) { unsigned long pgoff; @@ -299,15 +344,103 @@ static struct miscdevice acoustic_misc = { .fops = &acoustic_fops, }; +static ssize_t htc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + char *s = buf; + mutex_lock(&acoustic_lock); + s += sprintf(s, "%d\n", hac_enable_flag); + mutex_unlock(&acoustic_lock); + return s - buf; + +} +static ssize_t htc_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + + if (count == (strlen("enable") + 1) && + strncmp(buf, "enable", strlen("enable")) == 0) { + mutex_lock(&acoustic_lock); + + if (hac_enable_flag == 0) + pr_info("Enable HAC\n"); + hac_enable_flag = 1; + + mutex_unlock(&acoustic_lock); + return count; + } + if (count == (strlen("disable") + 1) && + strncmp(buf, "disable", strlen("disable")) == 0) { + mutex_lock(&acoustic_lock); + + if (hac_enable_flag == 1) + pr_info("Disable HAC\n"); + hac_enable_flag = 0; + + mutex_unlock(&acoustic_lock); + return count; + } + pr_err("hac_flag_store: invalid argument\n"); + return -EINVAL; + +} + +static DEVICE_ATTR(flag, 0666, htc_show, htc_store); + static int __init acoustic_init(void) { + int ret = 0; mutex_init(&api_lock); mutex_init(&rpc_connect_lock); - return misc_register(&acoustic_misc); + ret = misc_register(&acoustic_misc); + if (ret < 0) { + pr_err("failed to register misc device!\n"); + return ret; + } + + htc_class = class_create(THIS_MODULE, "htc_acoustic"); + if (IS_ERR(htc_class)) { + ret = PTR_ERR(htc_class); + htc_class = NULL; + goto err_create_class; + } + acoustic_misc.this_device = + device_create(htc_class, NULL, 0 , NULL, "hac"); + if (IS_ERR(acoustic_misc.this_device)) { + ret = PTR_ERR(acoustic_misc.this_device); + acoustic_misc.this_device = NULL; + goto err_create_class; + } + + ret = device_create_file(acoustic_misc.this_device, &dev_attr_flag); + if (ret < 0) + goto err_create_class_device; + + mutex_init(&acoustic_lock); + +#if defined(CONFIG_HTC_HEADSET_MGR) + struct headset_notifier notifier; + notifier.id = HEADSET_REG_MIC_BIAS; + notifier.func = enable_mic_bias; + headset_notifier_register(¬ifier); +#endif + + return 0; + +err_create_class_device: + device_destroy(htc_class, 0); +err_create_class: + return ret; + } static void __exit acoustic_exit(void) { + device_remove_file(acoustic_misc.this_device, &dev_attr_flag); + device_destroy(htc_class, 0); + class_destroy(htc_class); + misc_deregister(&acoustic_misc); } diff --git a/arch/arm/mach-msm/htc_headset_gpio.c b/arch/arm/mach-msm/htc_headset_gpio.c new file mode 100644 index 0000000000000..c8fa9c4675927 --- /dev/null +++ b/arch/arm/mach-msm/htc_headset_gpio.c @@ -0,0 +1,271 @@ +/* + * + * /arch/arm/mach-msm/htc_headset_gpio.c + * + * HTC GPIO headset detection driver. + * + * Copyright (C) 2010 HTC, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DRIVER_NAME "HS_GPIO" + +/* #define DEBUG */ + +#ifdef DEBUG +#define AJ_DBG(fmt, arg...) \ + printk(KERN_INFO "[Audio Jack] %s " fmt "\r\n", __func__, ## arg) +#else +#define AJ_DBG(fmt, arg...) do {} while (0) +#endif + +struct audio_jack_info { + unsigned int irq_jack; + int audio_jack_detect; + int key_enable_gpio; + int mic_select_gpio; + int audio_jack_flag; + + struct hrtimer detection_timer; + ktime_t debounce_time; + + struct work_struct work; + + spinlock_t spin_lock; + + struct wake_lock audiojack_wake_lock; +}; + +static struct audio_jack_info *pjack_info; + +void hs_gpio_key_enable(int enable) +{ + DBG_MSG(); + + if (pjack_info->key_enable_gpio) + gpio_set_value(pjack_info->key_enable_gpio, enable); +} + +void hs_gpio_mic_select(int enable) +{ + DBG_MSG(); + + if (pjack_info->mic_select_gpio) + gpio_set_value(pjack_info->mic_select_gpio, enable); +} + +static irqreturn_t detect_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + hs_notify_hpin_irq(); + + AJ_DBG(""); + + do { + value1 = gpio_get_value(pjack_info->audio_jack_detect); + set_irq_type(pjack_info->irq_jack, value1 ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(pjack_info->audio_jack_detect); + } while (value1 != value2 && retry_limit-- > 0); + + AJ_DBG("value2 = %d (%d retries)", value2, (10-retry_limit)); + + if ((pjack_info->audio_jack_flag == 0) ^ value2) { + wake_lock_timeout(&pjack_info->audiojack_wake_lock, 4*HZ); + + /* Do the rest of the work in timer context */ + hrtimer_start(&pjack_info->detection_timer, + pjack_info->debounce_time, HRTIMER_MODE_REL); + } + + return IRQ_HANDLED; +} + +static enum hrtimer_restart detect_35mm_event_timer_func(struct hrtimer *data) +{ + int state; + + AJ_DBG(""); + state = !gpio_get_value(pjack_info->audio_jack_detect); + if (pjack_info->audio_jack_flag != state) { + pjack_info->audio_jack_flag = state; + schedule_work(&pjack_info->work); + } + + return HRTIMER_NORESTART; +} + +static void audiojack_work_func(struct work_struct *work) +{ + int is_insert; + unsigned long flags = 0; + + spin_lock_irqsave(&pjack_info->spin_lock, flags); + is_insert = pjack_info->audio_jack_flag; + spin_unlock_irqrestore(&pjack_info->spin_lock, flags); + + htc_35mm_remote_notify_insert_ext_headset(is_insert); + + if (is_insert) + pjack_info->debounce_time = ktime_set(0, 200000000); + else + pjack_info->debounce_time = ktime_set(0, 500000000); +} + +static void hs_gpio_register(void) +{ + struct headset_notifier notifier; + + if (pjack_info->mic_select_gpio) { + notifier.id = HEADSET_REG_MIC_SELECT; + notifier.func = hs_gpio_mic_select; + headset_notifier_register(¬ifier); + } + + if (pjack_info->key_enable_gpio) { + notifier.id = HEADSET_REG_KEY_ENABLE; + notifier.func = hs_gpio_key_enable; + headset_notifier_register(¬ifier); + } +} + +static int audiojack_probe(struct platform_device *pdev) +{ + int ret; + struct htc_headset_gpio_platform_data *pdata = pdev->dev.platform_data; + + SYS_MSG("++++++++++++++++++++"); + + pjack_info = kzalloc(sizeof(struct audio_jack_info), GFP_KERNEL); + if (!pjack_info) + return -ENOMEM; + + pjack_info->audio_jack_detect = pdata->hpin_gpio; + pjack_info->key_enable_gpio = pdata->key_enable_gpio; + pjack_info->mic_select_gpio = pdata->mic_select_gpio; + pjack_info->audio_jack_flag = 0; + + pjack_info->debounce_time = ktime_set(0, 500000000); + hrtimer_init(&pjack_info->detection_timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + pjack_info->detection_timer.function = detect_35mm_event_timer_func; + + INIT_WORK(&pjack_info->work, audiojack_work_func); + + spin_lock_init(&pjack_info->spin_lock); + wake_lock_init(&pjack_info->audiojack_wake_lock, + WAKE_LOCK_SUSPEND, "audiojack"); + + if (pjack_info->audio_jack_detect) { + ret = gpio_request(pjack_info->audio_jack_detect, + "3.5mm_detect"); + if (ret < 0) + goto err_request_detect_gpio; + + ret = gpio_direction_input(pjack_info->audio_jack_detect); + if (ret < 0) + goto err_set_detect_gpio; + + pjack_info->irq_jack = + gpio_to_irq(pjack_info->audio_jack_detect); + if (pjack_info->irq_jack < 0) { + ret = pjack_info->irq_jack; + goto err_request_detect_irq; + } + + ret = request_irq(pjack_info->irq_jack, + detect_irq_handler, + IRQF_TRIGGER_LOW, "35mm_headset", NULL); + if (ret < 0) + goto err_request_detect_irq; + + ret = set_irq_wake(pjack_info->irq_jack, 1); + if (ret < 0) + goto err_set_irq_wake; + } + + hs_gpio_register(); + + SYS_MSG("--------------------"); + + return 0; + +err_set_irq_wake: + if (pjack_info->audio_jack_detect) + free_irq(pjack_info->irq_jack, 0); +err_request_detect_irq: +err_set_detect_gpio: + if (pjack_info->audio_jack_detect) + gpio_free(pjack_info->audio_jack_detect); +err_request_detect_gpio: + printk(KERN_ERR "Audiojack: Failed in audiojack_probe\n"); + + return ret; +} + +static int audiojack_remove(struct platform_device *pdev) +{ + if (pjack_info->audio_jack_detect) + free_irq(pjack_info->irq_jack, 0); + + if (pjack_info->audio_jack_detect) + gpio_free(pjack_info->audio_jack_detect); + + return 0; +} + +static struct platform_driver audiojack_driver = { + .probe = audiojack_probe, + .remove = audiojack_remove, + .driver = { + .name = "HTC_HEADSET_GPIO", + .owner = THIS_MODULE, + }, +}; + +static int __init audiojack_init(void) +{ + return platform_driver_register(&audiojack_driver); +} + +static void __exit audiojack_exit(void) +{ + platform_driver_unregister(&audiojack_driver); +} + +module_init(audiojack_init); +module_exit(audiojack_exit); + +MODULE_DESCRIPTION("HTC GPIO headset detection driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/htc_headset_mgr.c b/arch/arm/mach-msm/htc_headset_mgr.c new file mode 100644 index 0000000000000..f7d40069e00a6 --- /dev/null +++ b/arch/arm/mach-msm/htc_headset_mgr.c @@ -0,0 +1,926 @@ +/* + * + * /arch/arm/mach-msm/htc_headset_mgr.c + * + * HTC headset manager driver. + * + * Copyright (C) 2010 HTC, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#define DRIVER_NAME "HS_MGR" + +/* #define CONFIG_DEBUG_H2W */ + +/*Delay 200ms when 11pin device plug in*/ +#define H2W_DETECT_DELAY msecs_to_jiffies(200) +#define BUTTON_H2W_DELAY msecs_to_jiffies(10) + +#define H2WI(fmt, arg...) \ + printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg) +#define H2WE(fmt, arg...) \ + printk(KERN_ERR "[H2W] %s " fmt "\r\n", __func__, ## arg) + +#ifdef CONFIG_DEBUG_H2W +#define H2W_DBG(fmt, arg...) \ + printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg) +#else +#define H2W_DBG(fmt, arg...) do {} while (0) +#endif + +static struct workqueue_struct *detect_wq; + +static void insert_35mm_do_work(struct work_struct *work); +static DECLARE_WORK(insert_35mm_work, insert_35mm_do_work); +static void remove_35mm_do_work(struct work_struct *work); +static DECLARE_WORK(remove_35mm_work, remove_35mm_do_work); + +static struct workqueue_struct *button_wq; + +static void button_35mm_do_work(struct work_struct *w); +static DECLARE_DELAYED_WORK(button_35mm_work, button_35mm_do_work); + +static int hs_mgr_rpc_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len); + +static struct msm_rpc_server hs_rpc_server = { + .prog = HS_RPC_SERVER_PROG, + .vers = HS_RPC_SERVER_VERS, + .rpc_call = hs_mgr_rpc_call, +}; + +struct button_work { + struct delayed_work key_work; + int key_code; +}; + +static struct h2w_info *hi; +static struct hs_notifier_func hs_mgr_notifier; + +void hs_notify_hpin_irq(void) +{ + hi->hpin_jiffies = jiffies; + SYS_MSG("HPIN IRQ"); +} + +int hs_hpin_stable(void) +{ + unsigned long last_hpin_jiffies = 0; + unsigned long unstable_jiffies = 1.2 * HZ; + + last_hpin_jiffies = hi->hpin_jiffies; + + if (time_before_eq(jiffies, last_hpin_jiffies + unstable_jiffies)) + return 0; + + return 1; +} + +int headset_notifier_register(struct headset_notifier *notifier) +{ + if (!notifier->func) { + SYS_MSG("NULL register function"); + return 0; + } + + switch (notifier->id) { + case HEADSET_REG_REMOTE_ADC: + SYS_MSG("Register REMOTE_ADC notifier"); + hs_mgr_notifier.remote_adc = notifier->func; + break; + case HEADSET_REG_RPC_KEY: + SYS_MSG("Register RPC_KEY notifier"); + hs_mgr_notifier.rpc_key = notifier->func; + break; + case HEADSET_REG_MIC_STATUS: + SYS_MSG("Register MIC_STATUS notifier"); + hs_mgr_notifier.mic_status = notifier->func; + break; + case HEADSET_REG_MIC_BIAS: + SYS_MSG("Register MIC_BIAS notifier"); + hs_mgr_notifier.mic_bias_enable = notifier->func; + break; + case HEADSET_REG_MIC_SELECT: + SYS_MSG("Register MIC_SELECT notifier"); + hs_mgr_notifier.mic_select = notifier->func; + break; + case HEADSET_REG_KEY_INT_ENABLE: + SYS_MSG("Register KEY_INT_ENABLE notifier"); + hs_mgr_notifier.key_int_enable = notifier->func; + break; + case HEADSET_REG_KEY_ENABLE: + SYS_MSG("Register KEY_ENABLE notifier"); + hs_mgr_notifier.key_enable = notifier->func; + break; + default: + SYS_MSG("Unknown register ID"); + return 0; + } + + return 1; +} + +static int hs_mgr_rpc_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + struct hs_rpc_server_args_key *args_key; + + DBG_MSG(""); + + switch (req->procedure) { + case HS_RPC_SERVER_PROC_NULL: + SYS_MSG("RPC_SERVER_NULL"); + break; + case HS_RPC_SERVER_PROC_KEY: + args_key = (struct hs_rpc_server_args_key *)(req + 1); + args_key->adc = be32_to_cpu(args_key->adc); + SYS_MSG("RPC_SERVER_KEY ADC = %u (0x%X)", + args_key->adc, args_key->adc); + if (hs_mgr_notifier.rpc_key) + hs_mgr_notifier.rpc_key(args_key->adc); + else + SYS_MSG("RPC_KEY notify function doesn't exist"); + break; + default: + SYS_MSG("Unknown RPC procedure"); + return -EINVAL; + } + + return 0; +} + +static ssize_t h2w_print_name(struct switch_dev *sdev, char *buf) +{ + return sprintf(buf, "Headset\n"); +} + +void button_pressed(int type) +{ + printk(KERN_INFO "[H2W] button_pressed %d\n", type); + atomic_set(&hi->btn_state, type); + input_report_key(hi->input, type, 1); + input_sync(hi->input); +} + +void button_released(int type) +{ + printk(KERN_INFO "[H2W] button_released %d\n", type); + atomic_set(&hi->btn_state, 0); + input_report_key(hi->input, type, 0); + input_sync(hi->input); +} + +void headset_button_event(int is_press, int type) +{ + if (!hs_hpin_stable()) { + H2WI("The HPIN is unstable, SKIP THE BUTTON EVENT."); + return; + } + + if (!is_press) { + if (hi->ignore_btn) + hi->ignore_btn = 0; + else + button_released(type); + } else { + if (!hi->ignore_btn && !atomic_read(&hi->btn_state)) + button_pressed(type); + } +} + +static void set_35mm_hw_state(int state) +{ + if (hi->mic_bias_state != state && hs_mgr_notifier.mic_bias_enable) { + hs_mgr_notifier.mic_bias_enable(state); + hi->mic_bias_state = state; + if (state) /* Wait for MIC bias stable */ + msleep(HS_DELAY_MIC_BIAS); + } + + if (hs_mgr_notifier.mic_select) + hs_mgr_notifier.mic_select(state); + + if (hs_mgr_notifier.key_enable) + hs_mgr_notifier.key_enable(state); + + if (hs_mgr_notifier.key_int_enable) + hs_mgr_notifier.key_int_enable(state); +} + +static void insert_11pin_35mm(int *state) +{ + int mic = HEADSET_NO_MIC; + + SYS_MSG("Insert USB 3.5mm headset"); + set_35mm_hw_state(1); + + if (hs_mgr_notifier.mic_status) + mic = hs_mgr_notifier.mic_status(); + + if (mic == HEADSET_NO_MIC) { + /* without microphone */ + *state |= BIT_HEADSET_NO_MIC; + hi->h2w_35mm_status = HTC_35MM_NO_MIC; + printk(KERN_INFO "11pin_3.5mm without microphone\n"); + } else { /* with microphone */ + *state |= BIT_HEADSET; + hi->h2w_35mm_status = HTC_35MM_MIC; + printk(KERN_INFO "11pin_3.5mm with microphone\n"); + } +} + +static void remove_11pin_35mm(void) +{ + SYS_MSG("Remove USB 3.5mm headset"); + + set_35mm_hw_state(0); + + if (atomic_read(&hi->btn_state)) + button_released(atomic_read(&hi->btn_state)); + hi->h2w_35mm_status = HTC_35MM_UNPLUG; +} + +static void button_35mm_do_work(struct work_struct *w) +{ + int key; + struct button_work *work; + + work = container_of(w, struct button_work, key_work.work); + hi->key_level_flag = work->key_code; + + if (!hi->is_ext_insert && !hi->h2w_35mm_status) { + kfree(work); + H2WI("3.5mm headset is plugged out, skip report key event"); + return; + } + + if (hi->key_level_flag) { + switch (hi->key_level_flag) { + case 1: + H2WI("3.5mm RC: Play Pressed"); + key = HS_MGR_KEYCODE_MEDIA; + break; + case 2: + H2WI("3.5mm RC: BACKWARD Pressed"); + key = HS_MGR_KEYCODE_BACKWARD; + break; + case 3: + H2WI("3.5mm RC: FORWARD Pressed"); + key = HS_MGR_KEYCODE_FORWARD; + break; + default: + H2WI("3.5mm RC: WRONG Button Pressed"); + return; + } + headset_button_event(1, key); + } else { /* key release */ + if (atomic_read(&hi->btn_state)) + headset_button_event(0, atomic_read(&hi->btn_state)); + else + H2WI("3.5mm RC: WRONG Button Release"); + } + + wake_lock_timeout(&hi->headset_wake_lock, 1.5 * HZ); + + kfree(work); +} + +static void enable_metrico_headset(int enable) +{ + if (enable && !hi->metrico_status) { +#if 0 + enable_mos_test(1); +#endif + hi->metrico_status = 1; + printk(KERN_INFO "Enable metrico headset\n"); + } + + if (!enable && hi->metrico_status) { +#if 0 + enable_mos_test(0); +#endif + hi->metrico_status = 0; + printk(KERN_INFO "Disable metrico headset\n"); + } +} + +static void remove_35mm_do_work(struct work_struct *work) +{ + int state; + + wake_lock_timeout(&hi->headset_wake_lock, 2.5 * HZ); + + H2W_DBG(""); + /*To solve the insert, remove, insert headset problem*/ + if (time_before_eq(jiffies, hi->insert_jiffies)) + msleep(800); + if (hi->is_ext_insert) { + H2WI("Skip 3.5mm headset plug out!!!"); + return; + } + + SYS_MSG("Remove 3.5mm headset"); + set_35mm_hw_state(0); + + /* For HW Metrico lab test */ + if (hi->metrico_status) + enable_metrico_headset(0); + + if (atomic_read(&hi->btn_state)) + button_released(atomic_read(&hi->btn_state)); + hi->ext_35mm_status = HTC_35MM_UNPLUG; + + mutex_lock(&hi->mutex_lock); + state = switch_get_state(&hi->sdev); + + if (hi->usb_dev_type == USB_HEADSET) { + hi->usb_dev_status = STATUS_CONNECTED_ENABLED; + state &= ~(BIT_35MM_HEADSET | BIT_HEADSET); + state |= BIT_HEADSET_NO_MIC; + switch_set_state(&hi->sdev, state); + mutex_unlock(&hi->mutex_lock); + } else if (hi->usb_dev_type == H2W_TVOUT) { + state &= ~(BIT_HEADSET | BIT_35MM_HEADSET); + state |= BIT_HEADSET_NO_MIC; + switch_set_state(&hi->sdev, state); +#if 0 + } else if (hi->cable_in1 && !gpio_get_value(hi->cable_in1)) { + state &= ~BIT_35MM_HEADSET; + switch_set_state(&hi->sdev, state); + queue_delayed_work(detect_wq, &detect_h2w_work, + HS_DELAY_ZERO_JIFFIES); +#endif + } else { + state &= ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | + BIT_35MM_HEADSET); + switch_set_state(&hi->sdev, state); + } + + mutex_unlock(&hi->mutex_lock); +} + +static void insert_35mm_do_work(struct work_struct *work) +{ + int state; + int i, mic1, mic2; + + H2W_DBG(""); + hi->insert_jiffies = jiffies + HZ; + + wake_lock_timeout(&hi->headset_wake_lock, 1.5 * HZ); + +#if 0 + if (hi->usb_dev_type && hi->is_ext_insert && + hi->usb_dev_type != H2W_TVOUT && hi->usb_dev_type != USB_HEADSET) + remove_headset(); + else if (hi->usb_dev_type == USB_HEADSET) + hi->usb_dev_status = STATUS_CONNECTED_DISABLED; +#endif + + if (hi->usb_dev_type == USB_HEADSET) + hi->usb_dev_status = STATUS_CONNECTED_DISABLED; + + if (hi->is_ext_insert) { + SYS_MSG("Insert 3.5mm headset"); + set_35mm_hw_state(1); + hi->ignore_btn = 0; + + mic1 = mic2 = HEADSET_NO_MIC; + if (hs_mgr_notifier.mic_status) { + if (hi->ext_35mm_status == HTC_35MM_NO_MIC || + hi->h2w_35mm_status == HTC_35MM_NO_MIC) + for (i = 0; i < 10; i++) { + mic1 = hs_mgr_notifier.mic_status(); + msleep(HS_DELAY_MIC_DETECT); + mic2 = hs_mgr_notifier.mic_status(); + if (mic1 == mic2) + break; + } + else + mic1 = mic2 = hs_mgr_notifier.mic_status(); + } + + /* For HW Metrico lab test */ + if (mic2 == HEADSET_METRICO && !hi->metrico_status) + enable_metrico_headset(1); + + mutex_lock(&hi->mutex_lock); + state = switch_get_state(&hi->sdev); + state &= ~(BIT_HEADSET | BIT_HEADSET_NO_MIC); + if (mic2 == HEADSET_NO_MIC || mic1 != mic2) { + state |= BIT_HEADSET_NO_MIC; + printk(KERN_INFO "3.5mm_headset without microphone\n"); + } else { + state |= BIT_HEADSET; + printk(KERN_INFO "3.5mm_headset with microphone\n"); + } + + state |= BIT_35MM_HEADSET; + switch_set_state(&hi->sdev, state); + if (state & BIT_HEADSET_NO_MIC) + hi->ext_35mm_status = HTC_35MM_NO_MIC; + else + hi->ext_35mm_status = HTC_35MM_MIC; + mutex_unlock(&hi->mutex_lock); + } +} + +int htc_35mm_remote_notify_insert_ext_headset(int insert) +{ + if (hi) { + mutex_lock(&hi->mutex_lock); + hi->is_ext_insert = insert; + mutex_unlock(&hi->mutex_lock); + + H2WI(" %d", hi->is_ext_insert); + if (!hi->is_ext_insert) + queue_work(detect_wq, &remove_35mm_work); + else + queue_work(detect_wq, &insert_35mm_work); + } + return 1; +} + +int htc_35mm_remote_notify_microp_ready(void) +{ + if (hi) { + if (hi->is_ext_insert) + queue_work(detect_wq, &insert_35mm_work); +#if 0 + if (hi->h2w_35mm_status) + insert_headset(NORMAL_HEARPHONE); +#endif + } + return 1; +} + +int htc_35mm_remote_notify_button_status(int key_level) +{ + struct button_work *work; + + if (hi->ext_35mm_status == HTC_35MM_NO_MIC || + hi->h2w_35mm_status == HTC_35MM_NO_MIC) { + SYS_MSG("MIC re-detection"); + msleep(HS_DELAY_MIC_DETECT); + queue_work(detect_wq, &insert_35mm_work); + } else if (!hs_hpin_stable()) { + H2WI("The HPIN is unstable, SKIP THE BUTTON EVENT."); + return 1; + } else { + work = kzalloc(sizeof(struct button_work), GFP_KERNEL); + if (!work) { + printk(KERN_INFO "Failed to allocate button memory\n"); + return 1; + } + work->key_code = key_level; + INIT_DELAYED_WORK(&work->key_work, button_35mm_do_work); + queue_delayed_work(button_wq, &work->key_work, + HS_JIFFIES_BUTTON); + } + + return 1; +} + +static void usb_headset_detect(int type) +{ + int state; + + mutex_lock(&hi->mutex_lock); + state = switch_get_state(&hi->sdev); + + switch (type) { + case NO_DEVICE: + if (hi->usb_dev_type == USB_HEADSET) { + printk(KERN_INFO "Remove USB headset\n"); + hi->usb_dev_type = NO_DEVICE; + hi->usb_dev_status = STATUS_DISCONNECTED; + state &= ~BIT_USB_HEADSET; + if (!hi->is_ext_insert) + state &= ~BIT_HEADSET_NO_MIC; + } + break; + case USB_HEADSET: + printk(KERN_INFO "Insert USB headset\n"); + hi->usb_dev_type = USB_HEADSET; + if (hi->is_ext_insert) { + printk(KERN_INFO "Disable USB headset\n"); + hi->usb_dev_status = STATUS_CONNECTED_DISABLED; + state |= BIT_USB_HEADSET; + } else { + printk(KERN_INFO "Enable USB headset\n"); + hi->usb_dev_status = STATUS_CONNECTED_ENABLED; + state |= (BIT_USB_HEADSET | BIT_HEADSET_NO_MIC); + } + break; + default: + printk(KERN_INFO "Unknown headset type\n"); + } + + switch_set_state(&hi->sdev, state); + mutex_unlock(&hi->mutex_lock); +} + +void headset_ext_detect(int type) +{ + switch (type) { + case NO_DEVICE: + if (hi->usb_dev_type == USB_HEADSET) + usb_headset_detect(type); + break; + case USB_HEADSET: + usb_headset_detect(type); + break; + default: + printk(KERN_INFO "Unknown headset type\n"); + } +} + +int switch_send_event(unsigned int bit, int on) +{ + unsigned long state; + + mutex_lock(&hi->mutex_lock); + state = switch_get_state(&hi->sdev); + state &= ~(bit); + + if (on) + state |= bit; + + switch_set_state(&hi->sdev, state); + mutex_unlock(&hi->mutex_lock); + return 0; +} + +static ssize_t tty_flag_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char *s = buf; + mutex_lock(&hi->mutex_lock); + s += sprintf(s, "%d\n", hi->tty_enable_flag); + mutex_unlock(&hi->mutex_lock); + return (s - buf); +} + +static ssize_t tty_flag_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int state; + + mutex_lock(&hi->mutex_lock); + state = switch_get_state(&hi->sdev); + state &= ~(BIT_TTY_FULL | BIT_TTY_VCO | BIT_TTY_HCO); + + if (count == (strlen("enable") + 1) && + strncmp(buf, "enable", strlen("enable")) == 0) { + hi->tty_enable_flag = 1; + switch_set_state(&hi->sdev, state | BIT_TTY_FULL); + mutex_unlock(&hi->mutex_lock); + printk(KERN_INFO "Enable TTY FULL\n"); + return count; + } + if (count == (strlen("vco_enable") + 1) && + strncmp(buf, "vco_enable", strlen("vco_enable")) == 0) { + hi->tty_enable_flag = 2; + switch_set_state(&hi->sdev, state | BIT_TTY_VCO); + mutex_unlock(&hi->mutex_lock); + printk(KERN_INFO "Enable TTY VCO\n"); + return count; + } + if (count == (strlen("hco_enable") + 1) && + strncmp(buf, "hco_enable", strlen("hco_enable")) == 0) { + hi->tty_enable_flag = 3; + switch_set_state(&hi->sdev, state | BIT_TTY_HCO); + mutex_unlock(&hi->mutex_lock); + printk(KERN_INFO "Enable TTY HCO\n"); + return count; + } + if (count == (strlen("disable") + 1) && + strncmp(buf, "disable", strlen("disable")) == 0) { + hi->tty_enable_flag = 0; + switch_set_state(&hi->sdev, state); + mutex_unlock(&hi->mutex_lock); + printk(KERN_INFO "Disable TTY\n"); + return count; + } + printk(KERN_ERR "tty_enable_flag_store: invalid argument\n"); + return -EINVAL; +} +static DEVICE_ACCESSORY_ATTR(tty, 0666, tty_flag_show, tty_flag_store); + +static ssize_t fm_flag_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int state; + + mutex_lock(&hi->mutex_lock); + state = switch_get_state(&hi->sdev); + state &= ~(BIT_FM_HEADSET | BIT_FM_SPEAKER); + + if (count == (strlen("fm_headset") + 1) && + strncmp(buf, "fm_headset", strlen("fm_headset")) == 0) { + hi->fm_flag = 1; + state |= BIT_FM_HEADSET; + printk(KERN_INFO "Enable FM HEADSET\n"); + } else if (count == (strlen("fm_speaker") + 1) && + strncmp(buf, "fm_speaker", strlen("fm_speaker")) == 0) { + hi->fm_flag = 2; + state |= BIT_FM_SPEAKER; + printk(KERN_INFO "Enable FM SPEAKER\n"); + } else if (count == (strlen("disable") + 1) && + strncmp(buf, "disable", strlen("disable")) == 0) { + hi->fm_flag = 0 ; + printk(KERN_INFO "Disable FM\n"); + } else { + mutex_unlock(&hi->mutex_lock); + printk(KERN_ERR "fm_enable_flag_store: invalid argument\n"); + return -EINVAL; + } + switch_set_state(&hi->sdev, state); + mutex_unlock(&hi->mutex_lock); + return count; +} + +static ssize_t fm_flag_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char *s = buf; + char *show_str; + mutex_lock(&hi->mutex_lock); + if (hi->fm_flag == 0) + show_str = "disable"; + if (hi->fm_flag == 1) + show_str = "fm_headset"; + if (hi->fm_flag == 2) + show_str = "fm_speaker"; + + s += sprintf(s, "%s\n", show_str); + mutex_unlock(&hi->mutex_lock); + return (s - buf); +} +static DEVICE_ACCESSORY_ATTR(fm, 0666, fm_flag_show, fm_flag_store); + +static int register_common_headset(struct h2w_info *h2w, int create_attr) +{ + int ret = 0; + hi = h2w; + + hi->htc_accessory_class = class_create(THIS_MODULE, "htc_accessory"); + if (IS_ERR(hi->htc_accessory_class)) { + ret = PTR_ERR(hi->htc_accessory_class); + hi->htc_accessory_class = NULL; + goto err_create_class; + } + + hi->tty_dev = device_create(hi->htc_accessory_class, + NULL, 0, "%s", "tty"); + if (unlikely(IS_ERR(hi->tty_dev))) { + ret = PTR_ERR(hi->tty_dev); + hi->tty_dev = NULL; + goto err_create_tty_device; + } + + /* register the attributes */ + ret = device_create_file(hi->tty_dev, &dev_attr_tty); + if (ret) + goto err_create_tty_device_file; + + hi->fm_dev = device_create(hi->htc_accessory_class, + NULL, 0, "%s", "fm"); + if (unlikely(IS_ERR(hi->fm_dev))) { + ret = PTR_ERR(hi->fm_dev); + hi->fm_dev = NULL; + goto err_create_fm_device; + } + + /* register the attributes */ + ret = device_create_file(hi->fm_dev, &dev_attr_fm); + if (ret) + goto err_create_fm_device_file; + + return 0; + +err_create_fm_device_file: + device_unregister(hi->fm_dev); +err_create_fm_device: + device_remove_file(hi->tty_dev, &dev_attr_tty); +err_create_tty_device_file: + device_unregister(hi->tty_dev); +err_create_tty_device: + class_destroy(hi->htc_accessory_class); +err_create_class: + + return ret; +} + +static void unregister_common_headset(struct h2w_info *h2w) +{ + hi = h2w; + device_remove_file(hi->tty_dev, &dev_attr_tty); + device_unregister(hi->tty_dev); + device_remove_file(hi->fm_dev, &dev_attr_fm); + device_unregister(hi->fm_dev); + class_destroy(hi->htc_accessory_class); +} + +static int htc_35mm_probe(struct platform_device *pdev) +{ + int ret; + + struct htc_headset_mgr_platform_data *pdata = pdev->dev.platform_data; + + SYS_MSG("++++++++++++++++++++"); + + hi = kzalloc(sizeof(struct h2w_info), GFP_KERNEL); + if (!hi) + return -ENOMEM; + + hi->driver_flag = pdata->driver_flag; + hi->hpin_jiffies = jiffies; + + hi->ext_35mm_status = 0; + hi->h2w_35mm_status = 0; + hi->is_ext_insert = 0; + hi->mic_bias_state = 0; + hi->key_level_flag = -1; + + atomic_set(&hi->btn_state, 0); + hi->ignore_btn = 0; + hi->usb_dev_type = NO_DEVICE; + hi->tty_enable_flag = 0; + hi->fm_flag = 0; + hi->mic_switch_flag = 1; + hi->rc_flag = 0; + + hi->insert_11pin_35mm = insert_11pin_35mm; + hi->remove_11pin_35mm = remove_11pin_35mm; + + mutex_init(&hi->mutex_lock); + mutex_init(&hi->mutex_rc_lock); + + wake_lock_init(&hi->headset_wake_lock, WAKE_LOCK_SUSPEND, "headset"); + + hi->sdev.name = "h2w"; + hi->sdev.print_name = h2w_print_name; + + ret = switch_dev_register(&hi->sdev); + if (ret < 0) + goto err_switch_dev_register; + + detect_wq = create_workqueue("detection"); + if (detect_wq == NULL) { + ret = -ENOMEM; + goto err_create_detect_work_queue; + } + button_wq = create_workqueue("button"); + if (button_wq == NULL) { + ret = -ENOMEM; + goto err_create_button_work_queue; + } + + hi->input = input_allocate_device(); + if (!hi->input) { + ret = -ENOMEM; + goto err_request_input_dev; + } + + hi->input->name = "h2w headset"; + set_bit(EV_SYN, hi->input->evbit); + set_bit(EV_KEY, hi->input->evbit); + set_bit(KEY_END, hi->input->keybit); + set_bit(KEY_MUTE, hi->input->keybit); + set_bit(KEY_VOLUMEDOWN, hi->input->keybit); + set_bit(KEY_VOLUMEUP, hi->input->keybit); + set_bit(KEY_NEXTSONG, hi->input->keybit); + set_bit(KEY_PLAYPAUSE, hi->input->keybit); + set_bit(KEY_PREVIOUSSONG, hi->input->keybit); + set_bit(KEY_MEDIA, hi->input->keybit); + set_bit(KEY_SEND, hi->input->keybit); + + ret = input_register_device(hi->input); + if (ret < 0) + goto err_register_input_dev; + + ret = register_common_headset(hi, 0); + if (ret) + goto err_register_common_headset; + + if (hi->driver_flag & DRIVER_HS_MGR_RPC_SERVER) { + /* Create RPC server */ + ret = msm_rpc_create_server(&hs_rpc_server); + if (ret < 0) { + SYS_MSG("Failed to create RPC server"); + goto err_create_rpc_server; + } + SYS_MSG("Create RPC server successfully"); + } + + SYS_MSG("--------------------"); + + return 0; + +err_create_rpc_server: + +err_register_common_headset: + input_unregister_device(hi->input); + +err_register_input_dev: + input_free_device(hi->input); + +err_request_input_dev: + destroy_workqueue(button_wq); + +err_create_button_work_queue: + destroy_workqueue(detect_wq); + +err_create_detect_work_queue: + switch_dev_unregister(&hi->sdev); + +err_switch_dev_register: + + printk(KERN_ERR "H2W: Failed to register driver\n"); + + return ret; +} + +static int htc_35mm_remove(struct platform_device *pdev) +{ + H2W_DBG(""); + +#if 0 + if ((switch_get_state(&hi->sdev) & + (BIT_HEADSET | BIT_HEADSET_NO_MIC)) != 0) + remove_headset(); +#endif + + unregister_common_headset(hi); + input_unregister_device(hi->input); + destroy_workqueue(detect_wq); + destroy_workqueue(button_wq); + switch_dev_unregister(&hi->sdev); + + return 0; +} + +static struct platform_driver htc_35mm_driver = { + .probe = htc_35mm_probe, + .remove = htc_35mm_remove, + .driver = { + .name = "HTC_HEADSET_MGR", + .owner = THIS_MODULE, + }, +}; + + +static int __init htc_35mm_init(void) +{ + H2W_DBG(""); + return platform_driver_register(&htc_35mm_driver); +} + +static void __exit htc_35mm_exit(void) +{ + platform_driver_unregister(&htc_35mm_driver); +} + +module_init(htc_35mm_init); +module_exit(htc_35mm_exit); + +MODULE_DESCRIPTION("HTC headset manager driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/htc_headset_microp.c b/arch/arm/mach-msm/htc_headset_microp.c new file mode 100644 index 0000000000000..2a23568cb04b2 --- /dev/null +++ b/arch/arm/mach-msm/htc_headset_microp.c @@ -0,0 +1,418 @@ +/* + * + * /arch/arm/mach-msm/htc_headset_microp.c + * + * HTC Micro-P headset detection driver. + * + * Copyright (C) 2010 HTC, 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 +#include + +#define DRIVER_NAME "HS_MICROP" + +static struct htc_headset_microp_info *hi; + +static struct workqueue_struct *detect_wq; +static void detect_microp_work_func(struct work_struct *work); +static DECLARE_DELAYED_WORK(detect_microp_work, detect_microp_work_func); + +static struct workqueue_struct *button_wq; +static void button_microp_work_func(struct work_struct *work); +static DECLARE_WORK(button_microp_work, button_microp_work_func); + +static void hs_microp_key_enable(int enable) +{ + uint8_t addr; + uint8_t data[3]; + + DBG_MSG(); + + if (hi->pdata.remote_enable_pin) { + addr = (enable) ? MICROP_I2C_WCMD_GPO_LED_STATUS_EN : + MICROP_I2C_WCMD_GPO_LED_STATUS_DIS; + data[0] = (hi->pdata.remote_enable_pin >> 16) & 0xFF; + data[1] = (hi->pdata.remote_enable_pin >> 8) & 0xFF; + data[2] = (hi->pdata.remote_enable_pin >> 0) & 0xFF; + microp_i2c_write(addr, data, 3); + } +} + +static int headset_microp_enable_interrupt(int interrupt, int enable) +{ + uint8_t addr = 0x00; + uint8_t data[2]; + + DBG_MSG(); + + addr = (enable) ? MICROP_I2C_WCMD_GPI_INT_CTL_EN : + MICROP_I2C_WCMD_GPI_INT_CTL_DIS; + + memset(data, 0x00, sizeof(data)); + data[0] = (uint8_t) (interrupt >> 8); + data[1] = (uint8_t) interrupt; + + return microp_i2c_write(addr, data, 2); +} + +static int headset_microp_enable_button_int(int enable) +{ + int ret = 0; + + DBG_MSG(); + + if (hi->pdata.remote_int) + ret = headset_microp_enable_interrupt(hi->pdata.remote_int, + enable); + + return ret; +} + +static int headset_microp_remote_adc(int *adc) +{ + int ret = 0; + uint8_t data[2]; + + DBG_MSG(); + + data[0] = 0x00; + data[1] = hi->pdata.adc_channel; + ret = microp_read_adc(data); + if (ret != 0) { + SYS_MSG("Failed to read Micro-P ADC"); + return 0; + } + + *adc = data[0] << 8 | data[1]; + SYS_MSG("Remote ADC %d (0x%X)", *adc, *adc); + + return 1; +} + +static int headset_microp_mic_status(void) +{ + int ret = HEADSET_NO_MIC; + int adc = 0; + + DBG_MSG(); + + ret = headset_microp_remote_adc(&adc); + if (!ret) { + SYS_MSG("Failed to read Micro-P remote ADC"); + return HEADSET_NO_MIC; + } + + if (hi->pdata.adc_metrico[0] && hi->pdata.adc_metrico[1] && + adc >= hi->pdata.adc_metrico[0] && adc <= hi->pdata.adc_metrico[1]) + ret = HEADSET_METRICO; /* For Metrico lab test */ + else if (adc >= HS_DEF_MIC_ADC_10_BIT) + ret = HEADSET_MIC; + else + ret = HEADSET_NO_MIC; + + return ret; +} + +static void detect_microp_work_func(struct work_struct *work) +{ + int insert = 0; + int gpio_status = 0; + uint8_t data[3]; + + DBG_MSG(); + + microp_read_gpio_status(data); + gpio_status = data[0] << 16 | data[1] << 8 | data[2]; + insert = (gpio_status & hi->hpin_gpio_mask) ? 0 : 1; + htc_35mm_remote_notify_insert_ext_headset(insert); + + hi->hpin_debounce = (insert) ? HS_JIFFIES_REMOVE : HS_JIFFIES_INSERT; +} + +static void button_microp_work_func(struct work_struct *work) +{ + int ret = 0; + int keycode = -1; + uint8_t data[2]; + + DBG_MSG(); + + memset(data, 0x00, sizeof(data)); + ret = microp_i2c_read(MICROP_I2C_RCMD_REMOTE_KEYCODE, data, 2); + if (ret != 0) { + SYS_MSG("Failed to read Micro-P remote key code"); + return; + } + + if (!data[1]) + keycode = -1; /* no key code */ + else if (data[1] & 0x80) + keycode = 0; /* release key code */ + else + keycode = (int) data[1]; + + SYS_MSG("Key code %d", keycode); + + htc_35mm_remote_notify_button_status(keycode); +} + +static irqreturn_t htc_headset_microp_detect_irq(int irq, void *data) +{ + hs_notify_hpin_irq(); + + DBG_MSG(); + + queue_delayed_work(detect_wq, &detect_microp_work, hi->hpin_debounce); + + return IRQ_HANDLED; +} + +static irqreturn_t htc_headset_microp_button_irq(int irq, void *data) +{ + DBG_MSG(); + + queue_work(button_wq, &button_microp_work); + + return IRQ_HANDLED; +} + +static void hs_microp_register(void) +{ + struct headset_notifier notifier; + + if (hi->pdata.adc_channel) { + notifier.id = HEADSET_REG_REMOTE_ADC; + notifier.func = headset_microp_remote_adc; + headset_notifier_register(¬ifier); + + notifier.id = HEADSET_REG_MIC_STATUS; + notifier.func = headset_microp_mic_status; + headset_notifier_register(¬ifier); + } + + if (hi->pdata.remote_int) { + notifier.id = HEADSET_REG_KEY_INT_ENABLE; + notifier.func = headset_microp_enable_button_int; + headset_notifier_register(¬ifier); + } + + if (hi->pdata.remote_enable_pin) { + notifier.id = HEADSET_REG_KEY_ENABLE; + notifier.func = hs_microp_key_enable; + headset_notifier_register(¬ifier); + } +} + +static int htc_headset_microp_probe(struct platform_device *pdev) +{ + int i = 0; + int ret = 0; + uint8_t data[12]; + + struct htc_headset_microp_platform_data *pdata = NULL; + + SYS_MSG("++++++++++++++++++++"); + + pdata = pdev->dev.platform_data; + + hi = kzalloc(sizeof(struct htc_headset_microp_info), GFP_KERNEL); + if (!hi) { + SYS_MSG("Failed to allocate memory for headset info"); + return -ENOMEM; + } + + hi->pdata.hpin_int = pdata->hpin_int; + hi->pdata.hpin_irq = pdata->hpin_irq; + if (pdata->hpin_mask[0] || pdata->hpin_mask[1] || pdata->hpin_mask[2]) + memcpy(hi->pdata.hpin_mask, pdata->hpin_mask, + sizeof(hi->pdata.hpin_mask)); + + hi->pdata.remote_int = pdata->remote_int; + hi->pdata.remote_irq = pdata->remote_irq; + hi->pdata.remote_enable_pin = pdata->remote_enable_pin; + hi->pdata.adc_channel = pdata->adc_channel; + + if (pdata->adc_remote[5]) + memcpy(hi->pdata.adc_remote, pdata->adc_remote, + sizeof(hi->pdata.adc_remote)); + + if (pdata->adc_metrico[0] && pdata->adc_metrico[1]) + memcpy(hi->pdata.adc_metrico, pdata->adc_metrico, + sizeof(hi->pdata.adc_metrico)); + + hi->hpin_debounce = HS_JIFFIES_INSERT; + + if (hi->pdata.hpin_int) { + hi->hpin_gpio_mask = pdata->hpin_mask[0] << 16 | + pdata->hpin_mask[1] << 8 | + pdata->hpin_mask[2]; + } + + detect_wq = create_workqueue("detect"); + if (detect_wq == NULL) { + ret = -ENOMEM; + SYS_MSG("Failed to create detect workqueue"); + goto err_create_detect_work_queue; + } + + button_wq = create_workqueue("button"); + if (button_wq == NULL) { + ret = -ENOMEM; + SYS_MSG("Failed to create button workqueue"); + goto err_create_button_work_queue; + } + + if (hi->pdata.hpin_int) { + ret = headset_microp_enable_interrupt(hi->pdata.hpin_int, 1); + if (ret != 0) { + SYS_MSG("Failed to enable Micro-P HPIN interrupt"); + goto err_enable_microp_hpin_interrupt; + } + } + + if (hi->pdata.hpin_irq) { + ret = request_irq(hi->pdata.hpin_irq, + htc_headset_microp_detect_irq, + IRQF_TRIGGER_NONE, + "HTC_HEADSET_MICROP_BUTTON", NULL); + if (ret < 0) { + ret = -EINVAL; + SYS_MSG("Failed to request Micro-P IRQ (ERROR %d)", + ret); + goto err_request_microp_detect_irq; + } + } + + if (hi->pdata.adc_remote[5]) { + memset(data, 0x00, sizeof(data)); + for (i = 0; i < 6; i++) + data[i + 6] = (uint8_t) hi->pdata.adc_remote[i]; + ret = microp_i2c_write(MICROP_I2C_WCMD_REMOTEKEY_TABLE, + data, 12); + + if (ret != 0) { + ret = -EIO; + SYS_MSG("Failed to write Micro-P ADC table"); + goto err_write_microp_adc_table; + } + } + + if (hi->pdata.remote_int) { + ret = headset_microp_enable_interrupt(hi->pdata.remote_int, 1); + if (ret != 0) { + SYS_MSG("Failed to enable Micro-P remote interrupt"); + goto err_enable_microp_remote_interrupt; + } + } + + if (hi->pdata.remote_irq) { + ret = request_irq(hi->pdata.remote_irq, + htc_headset_microp_button_irq, + IRQF_TRIGGER_NONE, + "HTC_HEADSET_MICROP_BUTTON", NULL); + if (ret < 0) { + ret = -EINVAL; + SYS_MSG("Failed to request Micro-P IRQ (ERROR %d)", + ret); + goto err_request_microp_button_irq; + } + } + + hs_microp_register(); + + SYS_MSG("--------------------"); + + return 0; + +err_request_microp_button_irq: + if (hi->pdata.remote_int) + headset_microp_enable_interrupt(hi->pdata.remote_int, 0); + +err_enable_microp_remote_interrupt: +err_write_microp_adc_table: + if (hi->pdata.hpin_irq) + free_irq(hi->pdata.hpin_irq, 0); + +err_request_microp_detect_irq: + if (hi->pdata.hpin_int) + headset_microp_enable_interrupt(hi->pdata.hpin_int, 0); + +err_enable_microp_hpin_interrupt: + destroy_workqueue(button_wq); + +err_create_button_work_queue: + destroy_workqueue(detect_wq); + +err_create_detect_work_queue: + kfree(hi); + + return ret; +} + +static int htc_headset_microp_remove(struct platform_device *pdev) +{ + DBG_MSG(); + + if (hi->pdata.remote_irq) + free_irq(hi->pdata.remote_irq, 0); + + if (hi->pdata.remote_int) + headset_microp_enable_interrupt(hi->pdata.remote_int, 0); + + if (hi->pdata.hpin_irq) + free_irq(hi->pdata.hpin_irq, 0); + + if (hi->pdata.hpin_int) + headset_microp_enable_interrupt(hi->pdata.hpin_int, 0); + + destroy_workqueue(button_wq); + destroy_workqueue(detect_wq); + + kfree(hi); + + return 0; +} + +static struct platform_driver htc_headset_microp_driver = { + .probe = htc_headset_microp_probe, + .remove = htc_headset_microp_remove, + .driver = { + .name = "HTC_HEADSET_MICROP", + .owner = THIS_MODULE, + }, +}; + +static int __init htc_headset_microp_init(void) +{ + DBG_MSG(); + return platform_driver_register(&htc_headset_microp_driver); +} + +static void __exit htc_headset_microp_exit(void) +{ + DBG_MSG(); + platform_driver_unregister(&htc_headset_microp_driver); +} + +module_init(htc_headset_microp_init); +module_exit(htc_headset_microp_exit); + +MODULE_DESCRIPTION("HTC Micro-P headset detection driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/include/mach/atmega_microp.h b/arch/arm/mach-msm/include/mach/atmega_microp.h new file mode 100644 index 0000000000000..2b04dc58b3015 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/atmega_microp.h @@ -0,0 +1,265 @@ +/* include/asm/mach-msm/atmega_microp.h + * + * Copyright (C) 2009 HTC Corporation. + * + * 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_ATMEGA_MICROP_H +#define _LINUX_ATMEGA_MICROP_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define MICROP_I2C_NAME "atmega-microp" + +#define MICROP_FUNCTION_LSENSOR 1 +#define MICROP_FUNCTION_REMOTEKEY 2 +#define MICROP_FUNCTION_LCD_BL 3 +#define MICROP_FUNCTION_RMK_VALUE 4 +#define MICROP_FUNCTION_INTR 11 +#define MICROP_FUNCTION_GSENSOR 12 +#define MICROP_FUNCTION_LED 13 +#define MICROP_FUNCTION_HPIN 14 +#define MICROP_FUNCTION_RESET_INT 15 +#define MICROP_FUNCTION_SIM_CARD 16 +#define MICROP_FUNCTION_SDCARD 17 +#define MICROP_FUNCTION_OJ 18 +#define MICROP_FUNCTION_P 19 + +#define HEADSET_NO_MIC 0 +#define HEADSET_MIC 1 +#define HEADSET_METRICO 2 + +#define LED_RGB (1 << 0) +#define LED_JOGBALL (1 << 1) +#define LED_GPO (1 << 2) +#define LED_PWM (1 << 3) +#define LED_WIMAX (1 << 4) +#define LED_MOBEAM (1 << 5) + +#define SPI_GSENSOR (1 << 0) +#define SPI_LCM (1 << 1) +#define SPI_OJ (1 << 2) + +#define LS_PWR_ON (1 << 0) +#define PS_PWR_ON (1 << 1) + +#define ALS_BACKLIGHT (1 << 0) +#define ALS_VKEY_LED (1 << 1) + +#define CMD_83_DIFF (1 << 0) +#define CMD_25_DIFF (1 << 1) + +#define ALS_CALIBRATED 0x6DA5 + +#define MICROP_I2C_WCMD_MISC 0x20 +#define MICROP_I2C_WCMD_SPI_EN 0x21 +#define MICROP_I2C_WCMD_LCM_BL_MANU_CTL 0x22 +#define MICROP_I2C_WCMD_AUTO_BL_CTL 0x23 +#define MICROP_I2C_RCMD_SPI_BL_STATUS 0x24 +#define MICROP_I2C_WCMD_LED_PWM 0x25 +#define MICROP_I2C_WCMD_BL_EN 0x26 +#define MICROP_I2C_RCMD_VERSION 0x30 +#define MICROP_I2C_WCMD_ADC_TABLE 0x42 +#define MICROP_I2C_WCMD_LED_MODE 0x53 +#define MICROP_I2C_RCMD_GREEN_LED_REMAIN_TIME 0x54 +#define MICROP_I2C_RCMD_AMBER_LED_REMAIN_TIME 0x55 +#define MICROP_I2C_RCMD_LED_REMAIN_TIME 0x56 +#define MICROP_I2C_RCMD_BLUE_LED_REMAIN_TIME 0x57 +#define MICROP_I2C_RCMD_LED_STATUS 0x58 +#define MICROP_I2C_WCMD_JOGBALL_LED_MODE 0x5A +#define MICROP_I2C_WCMD_JOGBALL_LED_PWM_SET 0x5C +#define MICROP_I2C_WCMD_READ_ADC_VALUE_REQ 0x60 +#define MICROP_I2C_RCMD_ADC_VALUE 0x62 +#define MICROP_I2C_WCMD_REMOTEKEY_TABLE 0x63 +#define MICROP_I2C_WCMD_LCM_BURST 0x6A +#define MICROP_I2C_WCMD_LCM_BURST_EN 0x6B +#define MICROP_I2C_WCMD_LCM_REGISTER 0x70 +#define MICROP_I2C_WCMD_GSENSOR_REG 0x73 +#define MICROP_I2C_WCMD_GSENSOR_REG_DATA_REQ 0x74 +#define MICROP_I2C_RCMD_GSENSOR_REG_DATA 0x75 +#define MICROP_I2C_WCMD_GSENSOR_DATA_REQ 0x76 +#define MICROP_I2C_RCMD_GSENSOR_X_DATA 0x77 +#define MICROP_I2C_RCMD_GSENSOR_Y_DATA 0x78 +#define MICROP_I2C_RCMD_GSENSOR_Z_DATA 0x79 +#define MICROP_I2C_RCMD_GSENSOR_DATA 0x7A +#define MICROP_I2C_WCMD_OJ_REG 0x7B +#define MICROP_I2C_WCMD_OJ_REG_DATA_REQ 0x7C +#define MICROP_I2C_RCMD_OJ_REG_DATA 0x7D +#define MICROP_I2C_WCMD_OJ_POS_DATA_REQ 0x7E +#define MICROP_I2C_RCMD_OJ_POS_DATA 0x7F +#define MICROP_I2C_WCMD_GPI_INT_CTL_EN 0x80 +#define MICROP_I2C_WCMD_GPI_INT_CTL_DIS 0x81 +#define MICROP_I2C_RCMD_GPI_INT_STATUS 0x82 +#define MICROP_I2C_RCMD_GPIO_STATUS 0x83 +#define MICROP_I2C_WCMD_GPI_INT_STATUS_CLR 0x84 +#define MICROP_I2C_RCMD_GPI_INT_SETTING 0x85 +#define MICROP_I2C_RCMD_REMOTE_KEYCODE 0x87 +#define MICROP_I2C_WCMD_REMOTE_KEY_DEBN_TIME 0x88 +#define MICROP_I2C_WCMD_REMOTE_PLUG_DEBN_TIME 0x89 +#define MICROP_I2C_WCMD_SIMCARD_DEBN_TIME 0x8A +#define MICROP_I2C_WCMD_GPO_LED_STATUS_EN 0x90 +#define MICROP_I2C_WCMD_GPO_LED_STATUS_DIS 0x91 +#define MICROP_I2C_WCMD_OJ_INT_STATUS 0xA8 +#define MICROP_I2C_RCMD_MOBEAM_STATUS 0xB1 +#define MICROP_I2C_WCMD_MOBEAM_DL 0xB2 +#define MICROP_I2C_WCMD_MOBEAM_SEND 0xB3 + +struct microp_function_config { + const char *name; + uint8_t category; + uint8_t init_value; + uint8_t channel; + uint8_t fade_time; + uint32_t sub_categ; + uint16_t levels[10]; + uint16_t dutys[10]; + uint16_t int_pin; + uint16_t golden_adc; + uint8_t mask_r[3]; + uint8_t mask_w[3]; + uint32_t ls_gpio_on; + int (*ls_power)(int, uint8_t); +}; + +struct microp_i2c_platform_data { + struct microp_function_config *microp_function; + struct platform_device *microp_devices; + int num_devices; + int num_functions; + uint32_t gpio_reset; + uint32_t microp_ls_on; + void *dev_id; + uint8_t microp_mic_status; + uint8_t function_node[20]; + uint32_t cmd_diff; + uint32_t spi_devices; + uint32_t spi_devices_init; +}; + +struct microp_led_config { + const char *name; + uint32_t type; + uint8_t init_value; + uint8_t fade_time; + uint16_t led_pin; + uint8_t mask_w[3]; +}; + +struct microp_led_platform_data { + struct microp_led_config *led_config; + int num_leds; +}; + +struct microp_int_pin { + uint16_t int_gsensor; + uint16_t int_lsensor; + uint16_t int_reset; + uint16_t int_simcard; + uint16_t int_hpin; + uint16_t int_remotekey; + uint16_t int_sdcard; + uint16_t int_oj; + uint16_t int_psensor; +}; + +struct microp_gpio_status { + uint32_t hpin; + uint32_t sdcard; + uint32_t psensor; +}; + +struct microp_function_node { + uint8_t lsensor; + uint8_t psensor; +}; + +struct microp_led_data { + struct led_classdev ldev; + struct microp_led_config *led_config; + struct mutex led_data_mutex; + spinlock_t brightness_lock; + enum led_brightness brightness; + uint8_t mode; + uint8_t blink; +}; + +struct microp_i2c_client_data { + struct mutex microp_adc_mutex; + struct mutex microp_i2c_rw_mutex; + uint16_t version; + struct workqueue_struct *microp_queue; + struct work_struct microp_intr_work; + struct delayed_work ls_on_work; + struct delayed_work hpin_enable_intr_work; + struct delayed_work hpin_debounce_work; + struct early_suspend early_suspend; + struct microp_int_pin int_pin; + struct microp_gpio_status gpio; + struct microp_function_node fnode; + struct wake_lock hpin_wake_lock; + + atomic_t microp_is_suspend; + atomic_t als_intr_enabled; + atomic_t als_intr_enable_flag; + int headset_is_in; + int sdcard_is_in; + uint32_t spi_devices_vote; + uint32_t pwr_devices_vote; + uint32_t als_func; + struct hrtimer gen_irq_timer; + uint16_t intr_status; +}; + +struct lightsensor_platform_data{ + struct i2c_client *client; + struct microp_function_config *config; + int irq; + int old_intr_cmd; +}; + +struct microp_ops { + int (*init_microp_func)(struct i2c_client *); + int (*als_pwr_enable)(int pwr_device, uint8_t en); + int (*als_intr_enable)(struct i2c_client *, + uint32_t als_func, uint8_t en); + void (*als_level_change)(struct i2c_client *, uint8_t *data); + void (*headset_enable)(int en); + void (*spi_enable)(int en); + void (*notifier_func)(struct i2c_client *, struct microp_led_data *); + void (*led_gpio_set)(struct microp_led_data *); +}; + +int microp_i2c_read(uint8_t addr, uint8_t *data, int length); +int microp_i2c_write(uint8_t addr, uint8_t *data, int length); +int microp_function_check(struct i2c_client *client, uint8_t category); +int microp_read_gpio_status(uint8_t *data); +int microp_write_interrupt(struct i2c_client *client, + uint16_t interrupt, uint8_t enable); +void microp_get_als_kvalue(int i); +int microp_spi_vote_enable(int spi_device, uint8_t enable); +void microp_register_ops(struct microp_ops *ops); + +int microp_read_adc(uint8_t *data); +void microp_mobeam_enable(int enable); + +#endif /* _LINUX_ATMEGA_MICROP_H */ diff --git a/arch/arm/mach-msm/include/mach/drv_callback.h b/arch/arm/mach-msm/include/mach/drv_callback.h new file mode 100644 index 0000000000000..56ed3ccfe33b6 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/drv_callback.h @@ -0,0 +1,28 @@ +/* linux/arch/arm/mach-msm/drv_callback.h + * + * Copyright (C) 2009 HTC Corporation. + * + * 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 + +struct cnf_driver { + const char *name; + int (*func) (void *); + + /* configurable driver list lock */ + rwlock_t cnfdrv_list_lock; + struct list_head next_drv; +}; + +int cnf_driver_register(struct cnf_driver *); +int cnf_driver_event(const char *, void *argu); diff --git a/arch/arm/mach-msm/include/mach/htc_acoustic_qsd.h b/arch/arm/mach-msm/include/mach/htc_acoustic_qsd.h index 2139bf9f903a5..d42c6e009ce57 100644 --- a/arch/arm/mach-msm/include/mach/htc_acoustic_qsd.h +++ b/arch/arm/mach-msm/include/mach/htc_acoustic_qsd.h @@ -24,6 +24,8 @@ void acoustic_register_ops(struct qsd_acoustic_ops *ops); int turn_mic_bias_on(int on); int force_headset_speaker_on(int enable); int enable_aux_loopback(uint32_t enable); +int set_aux_gain(int level); +int enable_mos_test(int enable); #endif diff --git a/arch/arm/mach-msm/include/mach/htc_headset_gpio.h b/arch/arm/mach-msm/include/mach/htc_headset_gpio.h new file mode 100644 index 0000000000000..f18ba47a67c5a --- /dev/null +++ b/arch/arm/mach-msm/include/mach/htc_headset_gpio.h @@ -0,0 +1,29 @@ +/* + * + * /arch/arm/mach-msm/include/mach/htc_headset_gpio.h + * + * HTC GPIO headset detection driver. + * + * Copyright (C) 2010 HTC, 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 HTC_HEADSET_GPIO_H +#define HTC_HEADSET_GPIO_H + +struct htc_headset_gpio_platform_data { + unsigned int hpin_gpio; + unsigned int key_enable_gpio; + unsigned int mic_select_gpio; +}; + +#endif diff --git a/arch/arm/mach-msm/include/mach/htc_headset_mgr.h b/arch/arm/mach-msm/include/mach/htc_headset_mgr.h new file mode 100644 index 0000000000000..c01f847bcd633 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/htc_headset_mgr.h @@ -0,0 +1,394 @@ +/* + * + * /arch/arm/mach-msm/include/mach/htc_headset_mgr.h + * + * HTC headset manager driver. + * + * Copyright (C) 2010 HTC, 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 HTC_HEADSET_MGR_H +#define HTC_HEADSET_MGR_H + +#include + +#include +#include +#include + +#define SYS_MSG(fmt, arg...) \ + printk(KERN_INFO "[" DRIVER_NAME "] (%s) " fmt "\n", __func__, ## arg) +#if 0 +#define DBG_MSG(fmt, arg...) \ + printk(KERN_INFO "##### [" DRIVER_NAME "] (%s) " fmt "\n", \ + __func__, ## arg) +#else +#define DBG_MSG(fmt, arg...) {} +#endif + +#define DEVICE_ACCESSORY_ATTR(_name, _mode, _show, _store) \ + struct device_attribute dev_attr_##_name = \ + __ATTR(flag, _mode, _show, _store) + +#define DRIVER_HS_MGR_RPC_SERVER (1 << 0) + +#define HS_DEF_MIC_ADC_10_BIT 200 +#define HS_DEF_MIC_ADC_16_BIT 14894 /* (0.5 / 2.2) * (2 ^ 16) */ + +#define HS_DELAY_ZERO 0 +#define HS_DELAY_MIC_BIAS 200 +#define HS_DELAY_MIC_DETECT 500 +#define HS_DELAY_INSERT 500 +#define HS_DELAY_REMOVE 200 +#define HS_DELAY_BUTTON 500 + +#define HS_JIFFIES_ZERO msecs_to_jiffies(HS_DELAY_ZERO) +#define HS_JIFFIES_MIC_BIAS msecs_to_jiffies(HS_DELAY_MIC_BIAS) +#define HS_JIFFIES_MIC_DETECT msecs_to_jiffies(HS_DELAY_MIC_DETECT) +#define HS_JIFFIES_INSERT msecs_to_jiffies(HS_DELAY_INSERT) +#define HS_JIFFIES_REMOVE msecs_to_jiffies(HS_DELAY_REMOVE) +#define HS_JIFFIES_BUTTON msecs_to_jiffies(HS_DELAY_BUTTON) + +/* Definitions for Headset RPC Server */ +#define HS_RPC_SERVER_PROG 0x30100004 +#define HS_RPC_SERVER_VERS 0x00000000 +#define HS_RPC_SERVER_PROC_NULL 0 +#define HS_RPC_SERVER_PROC_KEY 1 + +/* Definitions for Headset RPC Client */ +#define HS_RPC_CLIENT_PROG 0x30100005 +#define HS_RPC_CLIENT_VERS 0x00000000 +#define HS_RPC_CLIENT_PROC_NULL 0 +#define HS_RPC_CLIENT_PROC_ADC 1 + +#define HS_MGR_KEYCODE_END KEY_END /* 107 */ +#define HS_MGR_KEYCODE_MUTE KEY_MUTE /* 113 */ +#define HS_MGR_KEYCODE_VOLDOWN KEY_VOLUMEDOWN /* 114 */ +#define HS_MGR_KEYCODE_VOLUP KEY_VOLUMEUP /* 115 */ +#define HS_MGR_KEYCODE_FORWARD KEY_NEXTSONG /* 163 */ +#define HS_MGR_KEYCODE_PLAY KEY_PLAYPAUSE /* 164 */ +#define HS_MGR_KEYCODE_BACKWARD KEY_PREVIOUSSONG /* 165 */ +#define HS_MGR_KEYCODE_MEDIA KEY_MEDIA /* 226 */ +#define HS_MGR_KEYCODE_SEND KEY_SEND /* 231 */ + +#define HEADSET_NO_MIC 0 +#define HEADSET_MIC 1 +#define HEADSET_METRICO 2 + +#define HTC_35MM_UNPLUG 0 +#define HTC_35MM_NO_MIC 1 +#define HTC_35MM_MIC 2 + +enum { + HEADSET_REG_REMOTE_ADC, + HEADSET_REG_RPC_KEY, + HEADSET_REG_MIC_STATUS, + HEADSET_REG_MIC_BIAS, + HEADSET_REG_MIC_SELECT, + HEADSET_REG_KEY_INT_ENABLE, + HEADSET_REG_KEY_ENABLE, +}; + +enum { + HS_MGR_KEY_INVALID = -1, + HS_MGR_KEY_NONE = 0, + HS_MGR_KEY_PLAY = 1, + HS_MGR_KEY_BACKWARD = 2, + HS_MGR_KEY_FORWARD = 3, +}; + +struct hs_rpc_server_args_key { + uint32_t adc; +}; + +struct hs_rpc_client_req_adc { + struct rpc_request_hdr hdr; +}; + +struct hs_rpc_client_rep_adc { + struct rpc_reply_hdr hdr; + uint32_t adc; +}; + +struct headset_notifier { + int id; + void *func; +}; + +struct hs_notifier_func { + int (*remote_adc)(int *); + void (*rpc_key)(int); + int (*mic_status)(void); + int (*mic_bias_enable)(int); + void (*mic_select)(int); + int (*key_int_enable)(int); + void (*key_enable)(int); +}; + +struct htc_headset_mgr_platform_data { + unsigned int driver_flag; + + int cable_in1; + int cable_in2; + int h2w_clk; + int h2w_data; + int debug_uart; + int headset_mic_35mm; + + void (*h2w_power)(int); + void (*config)(int); + void (*set_dat)(int); + void (*set_clk)(int); + void (*set_dat_dir)(int); + void (*set_clk_dir)(int); + int (*get_dat)(void); + int (*get_clk)(void); +}; + +#define BIT_HEADSET (1 << 0) +#define BIT_HEADSET_NO_MIC (1 << 1) +#define BIT_TTY_FULL (1 << 2) +#define BIT_FM_HEADSET (1 << 3) +#define BIT_FM_SPEAKER (1 << 4) +#define BIT_TTY_VCO (1 << 5) +#define BIT_TTY_HCO (1 << 6) +#define BIT_35MM_HEADSET (1 << 7) +#define BIT_TV_OUT (1 << 8) +#define BIT_USB_CRADLE (1 << 9) +#define BIT_TV_OUT_AUDIO (1 << 10) +#define BIT_HDMI_CABLE (1 << 11) +#define BIT_HDMI_AUDIO (1 << 12) +#define BIT_USB_HEADSET (1 << 13) + +enum { + STATUS_DISCONNECTED = 0, + STATUS_CONNECTED_ENABLED = 1, + STATUS_CONNECTED_DISABLED = 2, +}; + +enum { + H2W_GPIO = 0, + H2W_UART1 = 1, + H2W_UART3 = 2, + H2W_BT = 3 +}; + +enum { + NO_DEVICE = 0, + HTC_HEADSET = 1, + NORMAL_HEARPHONE = 2, + H2W_DEVICE = 3, + USB_CRADLE = 4, + UART_DEBUG = 5, + H2W_TVOUT = 6, + USB_HEADSET = 7, +}; + +#define RESEND_DELAY (3) /* ms */ +#define MAX_ACK_RESEND_TIMES (6) /* follow spec */ +#define MAX_HOST_RESEND_TIMES (3) /* follow spec */ +#define MAX_HYGEIA_RESEND_TIMES (5) + +#define H2W_ASCR_DEVICE_INI (0x01) +#define H2W_ASCR_ACT_EN (0x02) +#define H2W_ASCR_PHONE_IN (0x04) +#define H2W_ASCR_RESET (0x08) +#define H2W_ASCR_AUDIO_IN (0x10) + +#define H2W_LED_OFF (0x0) +#define H2W_LED_BKL (0x1) +#define H2W_LED_MTL (0x2) + +#define H2W_PhoneIn (0x01) +#define H2W_MuteLed (0x02) + +typedef enum { + /* === system group 0x0000~0x00FF === */ + /* (R) Accessory type register */ + H2W_SYSTEM = 0x0000, + /* (R) Maximum group address */ + H2W_MAX_GP_ADD = 0x0001, + /* (R/W) Accessory system control register0 */ + H2W_ASCR0 = 0x0002, + + /* === key group 0x0100~0x01FF === */ + /* (R) Key group maximum sub address */ + H2W_KEY_MAXADD = 0x0100, + /* (R) ASCII key press down flag */ + H2W_ASCII_DOWN = 0x0101, + /* (R) ASCII key release up flag */ + H2W_ASCII_UP = 0x0102, + /* (R) Function key status flag */ + H2W_FNKEY_UPDOWN = 0x0103, + /* (R/W) Key device status */ + H2W_KD_STATUS = 0x0104, + + /* === led group 0x0200~0x02FF === */ + /* (R) LED group maximum sub address */ + H2W_LED_MAXADD = 0x0200, + /* (R/W) LED control register0 */ + H2W_LEDCT0 = 0x0201, + + /* === crdl group 0x0300~0x03FF === */ + /* (R) Cardle group maximum sub address */ + H2W_CRDL_MAXADD = 0x0300, + /* (R/W) Cardle group function control register0 */ + H2W_CRDLCT0 = 0x0301, + + /* === car kit group 0x0400~0x04FF === */ + H2W_CARKIT_MAXADD = 0x0400, + + /* === usb host group 0x0500~0x05FF === */ + H2W_USBHOST_MAXADD = 0x0500, + + /* === medical group 0x0600~0x06FF === */ + H2W_MED_MAXADD = 0x0600, + H2W_MED_CONTROL = 0x0601, + H2W_MED_IN_DATA = 0x0602, +} H2W_ADDR; + +typedef struct H2W_INFO { + /* system group */ + unsigned char CLK_SP; + int SLEEP_PR; + unsigned char HW_REV; + int AUDIO_DEVICE; + unsigned char ACC_CLASS; + unsigned char MAX_GP_ADD; + + /* key group */ + int KEY_MAXADD; + int ASCII_DOWN; + int ASCII_UP; + int FNKEY_UPDOWN; + int KD_STATUS; + + /* led group */ + int LED_MAXADD; + int LEDCT0; + + /* medical group */ + int MED_MAXADD; + unsigned char AP_ID; + unsigned char AP_EN; + unsigned char DATA_EN; +} H2W_INFO; + +typedef enum { + H2W_500KHz = 1, + H2W_250KHz = 2, + H2W_166KHz = 3, + H2W_125KHz = 4, + H2W_100KHz = 5, + H2W_83KHz = 6, + H2W_71KHz = 7, + H2W_62KHz = 8, + H2W_55KHz = 9, + H2W_50KHz = 10, +} H2W_SPEED; + +struct h2w_info { + unsigned int driver_flag; + + unsigned long hpin_jiffies; + + struct class *htc_accessory_class; + struct device *tty_dev; + struct device *fm_dev; + struct device *mic_dev; + struct device *mute_dev; + struct device *phonein_dev; + struct mutex mutex_lock; + struct mutex mutex_rc_lock; + + struct switch_dev sdev; + struct input_dev *input; + unsigned long insert_jiffies; + + int ignore_btn; + atomic_t btn_state; + + int tty_enable_flag; + int fm_flag; + int mic_switch_flag; + int rc_flag; + + unsigned int irq; + unsigned int irq_btn; + unsigned int irq_btn_35mm; + + int cable_in1; + int cable_in2; + int h2w_clk; + int h2w_data; + int debug_uart; + int headset_mic_35mm; + + /* The variables were used by 35mm headset*/ + int key_level_flag; + int ext_35mm_status; + int h2w_35mm_status; + int is_ext_insert; + int mic_bias_state; + int metrico_status; /* For HW Metrico lab test */ + + /* The variables are used by USB headset */ + int usb_dev_type; + int usb_dev_status; + + void (*insert_11pin_35mm)(int *); + void (*remove_11pin_35mm)(void); + + void (*configure) (int); + int (*get_path) (void); + void (*h2w_power)(int); + void (*set_dat)(int); + void (*set_clk)(int); + void (*set_dat_dir)(int); + void (*set_clk_dir)(int); + int (*get_dat)(void); + int (*get_clk)(void); + + H2W_INFO h2w_info; + H2W_SPEED speed; + + struct wake_lock headset_wake_lock; +}; + +int headset_notifier_register(struct headset_notifier *notifier); + +void insert_headset(int); +void remove_headset(void); + +void headset_button_event(int is_press, int type); +void button_pressed(int type); +void button_released(int type); + +void button_h2w_do_work(struct work_struct *w); +void detect_h2w_do_work(struct work_struct *w); + +void headset_ext_detect(int type); + +extern int switch_send_event(unsigned int bit, int on); + +/* notify the 3.5mm driver of events */ +int htc_35mm_remote_notify_ext_headset_irq(int insert); +int htc_35mm_remote_notify_insert_ext_headset(int insert); +int htc_35mm_remote_notify_microp_ready(void); +int htc_35mm_remote_notify_button_status(int key_level); +int htc_35mm_remote_notify_irq_enable(int enable); + +void hs_notify_hpin_irq(void); +int hs_hpin_stable(void); + +#endif diff --git a/arch/arm/mach-msm/include/mach/htc_headset_microp.h b/arch/arm/mach-msm/include/mach/htc_headset_microp.h new file mode 100644 index 0000000000000..5239c0cb2b318 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/htc_headset_microp.h @@ -0,0 +1,48 @@ +/* + * + * /arch/arm/mach-msm/include/mach/htc_headset_microp.h + * + * HTC Micro-P headset detection driver. + * + * Copyright (C) 2010 HTC, 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 HTC_HEADSET_MICROP_H +#define HTC_HEADSET_MICROP_H + +struct htc_headset_microp_platform_data { + /* Headset detection */ + int hpin_int; + unsigned int hpin_irq; + uint8_t hpin_mask[3]; + + /* Remote key detection */ + int remote_int; + unsigned int remote_irq; + + /* Remote key interrupt enable */ + unsigned int remote_enable_pin; + + /* ADC tables */ + uint8_t adc_channel; + uint16_t adc_remote[6]; + uint16_t adc_metrico[2]; +}; + +struct htc_headset_microp_info { + struct htc_headset_microp_platform_data pdata; + int hpin_gpio_mask; + unsigned int hpin_debounce; +}; + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_audio_qcp.h b/arch/arm/mach-msm/include/mach/msm_audio_qcp.h new file mode 100644 index 0000000000000..28c234667e00a --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_audio_qcp.h @@ -0,0 +1,59 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __MSM_AUDIO_QCP_H +#define __MSM_AUDIO_QCP_H + +#include + +#define CDMA_RATE_BLANK 0x00 +#define CDMA_RATE_EIGHTH 0x01 +#define CDMA_RATE_QUARTER 0x02 +#define CDMA_RATE_HALF 0x03 +#define CDMA_RATE_FULL 0x04 +#define CDMA_RATE_ERASURE 0x05 + +struct msm_audio_qcelp_config { + uint32_t channels; + uint32_t cdma_rate; + uint32_t min_bit_rate; + uint32_t max_bit_rate; +}; + +struct msm_audio_evrc_config { + uint32_t channels; + uint32_t cdma_rate; + uint32_t min_bit_rate; + uint32_t max_bit_rate; + uint8_t bit_rate_reduction; + uint8_t hi_pass_filter; + uint8_t noise_suppressor; + uint8_t post_filter; +}; + +#endif /* __MSM_AUDIO_QCP_H */ diff --git a/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h index acc9ce308fe90..fcc8ce39ceb33 100644 --- a/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h +++ b/arch/arm/mach-msm/include/mach/msm_qdsp6_audio.h @@ -69,12 +69,20 @@ struct audio_client *q6voice_open(uint32_t flags, uint32_t acdb_id); struct audio_client *q6audio_open_mp3(uint32_t bufsz, uint32_t rate, uint32_t channels, uint32_t acdb_id); +struct audio_client *q6fm_open(void); + +struct audio_client *q6audio_open_aac(uint32_t bufsz, uint32_t rate, + uint32_t flags, void *data, uint32_t acdb_id); + +struct audio_client *q6audio_open_qcelp(uint32_t bufsz, uint32_t rate, + void *data, uint32_t acdb_id); + int q6audio_close(struct audio_client *ac); int q6voice_close(struct audio_client *ac); int q6audio_mp3_close(struct audio_client *ac); - -struct audio_client *q6fm_open(void); int q6fm_close(struct audio_client *ac); +int q6audio_aac_close(struct audio_client *ac); +int q6audio_qcelp_close(struct audio_client *ac); int q6audio_read(struct audio_client *ac, struct audio_buffer *ab); int q6audio_write(struct audio_client *ac, struct audio_buffer *ab); @@ -95,6 +103,7 @@ struct q6audio_analog_ops { void (*bt_sco_enable)(int en); void (*int_mic_enable)(int en); void (*ext_mic_enable)(int en); + void (*i2s_enable)(int en); int (*get_rx_vol)(uint8_t hw, int level); }; diff --git a/arch/arm/mach-msm/qdsp6/Makefile b/arch/arm/mach-msm/qdsp6/Makefile index 05cb351e7b45e..b7eb950de8d7d 100644 --- a/arch/arm/mach-msm/qdsp6/Makefile +++ b/arch/arm/mach-msm/qdsp6/Makefile @@ -2,8 +2,10 @@ obj-y += q6audio.o obj-y += pcm_out.o obj-y += pcm_in.o obj-y += mp3.o -obj-y += routing.o +#obj-y += routing.o obj-y += audio_ctl.o obj-y += msm_q6vdec.o obj-y += msm_q6venc.o -obj-y += dsp_debug.o \ No newline at end of file +#obj-y += aac_in.o +#obj-y += qcelp_in.o +obj-y += dsp_debug.o diff --git a/arch/arm/mach-msm/qdsp6/audio_ctl.c b/arch/arm/mach-msm/qdsp6/audio_ctl.c index 23d9eabbdd138..cc175b68c21e5 100644 --- a/arch/arm/mach-msm/qdsp6/audio_ctl.c +++ b/arch/arm/mach-msm/qdsp6/audio_ctl.c @@ -21,6 +21,7 @@ #include #include +#include #define BUFSZ (0) @@ -77,7 +78,7 @@ static int q6_voice_stop(void) return 0; } -int q6_fm_start(void) +static int q6_fm_start(void) { int rc = 0; @@ -102,7 +103,7 @@ int q6_fm_start(void) return rc; } -int q6_fm_stop(void) +static int q6_fm_stop(void) { mutex_lock(&fm_lock); if (fm_started) { @@ -128,40 +129,28 @@ static long q6_ioctl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { case AUDIO_SWITCH_DEVICE: rc = copy_from_user(&id, (void *)arg, sizeof(id)); - if (rc) { - pr_err("%s: bad user address\n", __func__); - rc = -EFAULT; - } else + if (!rc) rc = q6audio_do_routing(id[0], id[1]); break; case AUDIO_SET_VOLUME: rc = copy_from_user(&n, (void *)arg, sizeof(n)); - if (rc) { - pr_err("%s: bad user address\n", __func__); - rc = -EFAULT; - } else + if (!rc) rc = q6audio_set_rx_volume(n); break; case AUDIO_SET_MUTE: rc = copy_from_user(&n, (void *)arg, sizeof(n)); - if (rc) { - pr_err("%s: bad user address\n", __func__); - rc = -EFAULT; - } else + if (!rc) rc = q6audio_set_tx_mute(n); break; case AUDIO_UPDATE_ACDB: rc = copy_from_user(&id, (void *)arg, sizeof(id)); - if (rc) { - pr_err("%s: bad user address\n", __func__); - rc = -EFAULT; - } else + if (!rc) rc = q6audio_update_acdb(id[0], id[1]); break; case AUDIO_START_VOICE: - if (arg == 0) + if (arg == 0) { id[0] = id[1] = 0; - else if (copy_from_user(&id, (void *)arg, sizeof(id))) { + } else if (copy_from_user(&id, (void*) arg, sizeof(id))) { pr_info("voice: copy acdb_id from user failed\n"); rc = -EFAULT; break; @@ -179,14 +168,19 @@ static long q6_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case AUDIO_REINIT_ACDB: rc = copy_from_user(&filename, (void *)arg, sizeof(filename)); - if (rc) { - pr_err("%s: bad user address\n", __func__); - rc = -EFAULT; - } else + if (!rc) rc = q6audio_reinit_acdb(filename); break; + case AUDIO_ENABLE_AUXPGA_LOOPBACK: { + uint32_t enable; + if (copy_from_user(&enable, (void*) arg, sizeof(enable))) { + rc = -EFAULT; + break; + } + rc = enable_aux_loopback(enable); + break; + } default: - pr_info("%s: unknown %d\n", __func__, cmd); rc = -EINVAL; } diff --git a/arch/arm/mach-msm/qdsp6/mp3.c b/arch/arm/mach-msm/qdsp6/mp3.c index 759afb16182ba..6eb3301be8b36 100644 --- a/arch/arm/mach-msm/qdsp6/mp3.c +++ b/arch/arm/mach-msm/qdsp6/mp3.c @@ -19,9 +19,9 @@ #include #include #include -#include #include #include +#include #include diff --git a/arch/arm/mach-msm/qdsp6/msm_q6venc.c b/arch/arm/mach-msm/qdsp6/msm_q6venc.c index 7b889767476fa..760b41594e0dd 100644 --- a/arch/arm/mach-msm/qdsp6/msm_q6venc.c +++ b/arch/arm/mach-msm/qdsp6/msm_q6venc.c @@ -162,6 +162,7 @@ struct venc_pmem_list { struct venc_dev { bool is_active; bool pmem_freed; + bool stop_called; enum venc_state_type state; struct list_head venc_msg_list_head; struct list_head venc_msg_list_free; @@ -820,17 +821,10 @@ static int venc_q6_stop(struct venc_dev *dvenc) { int ret = 0; struct venc_pmem_list *plist; - unsigned long flags; wake_up(&dvenc->venc_msg_evt); - spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags); - if (!dvenc->pmem_freed) { - list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) - put_pmem_file(plist->buf.file); - dvenc->pmem_freed = 1; - } - spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags); - + list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) + put_pmem_file(plist->buf.file); dvenc->state = VENC_STATE_STOP; return ret; } @@ -1164,17 +1158,12 @@ static int q6venc_release(struct inode *inode, struct file *file) list_del(&l->list); kfree(l); } - list_for_each_entry_safe(l, n, &dvenc->venc_msg_list_head, list) { - list_del(&l->list); - kfree(l); - } spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags); - if (!dvenc->pmem_freed) { + if (!dvenc->stop_called) { list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) put_pmem_file(plist->buf.file); - dvenc->pmem_freed = 1; + dvenc->stop_called = 1; } - spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags); list_for_each_entry_safe(plist, m, &dvenc->venc_pmem_list_head, list) { list_del(&plist->list); diff --git a/arch/arm/mach-msm/qdsp6/pcm_in.c b/arch/arm/mach-msm/qdsp6/pcm_in.c index d5a8f7716eced..3b3d23bfd7a9a 100644 --- a/arch/arm/mach-msm/qdsp6/pcm_in.c +++ b/arch/arm/mach-msm/qdsp6/pcm_in.c @@ -160,7 +160,7 @@ static ssize_t q6_in_read(struct file *file, char __user *buf, if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) { audio_client_dump(ac); pr_err("pcm_read: timeout. dsp dead?\n"); - q6audio_dsp_not_responding(); + BUG(); } xfer = count; diff --git a/arch/arm/mach-msm/qdsp6/pcm_out.c b/arch/arm/mach-msm/qdsp6/pcm_out.c index 6d041d8c686ae..16a1e9c70ec09 100644 --- a/arch/arm/mach-msm/qdsp6/pcm_out.c +++ b/arch/arm/mach-msm/qdsp6/pcm_out.c @@ -19,9 +19,9 @@ #include #include #include -#include #include #include +#include #include @@ -56,12 +56,6 @@ static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { case AUDIO_SET_VOLUME: { int vol; - if (!pcm->ac) { - pr_err("%s: cannot set volume before AUDIO_START!\n", - __func__); - rc = -EINVAL; - break; - } if (copy_from_user(&vol, (void*) arg, sizeof(vol))) { rc = -EFAULT; break; @@ -183,14 +177,14 @@ static ssize_t pcm_write(struct file *file, const char __user *buf, if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) { audio_client_dump(ac); pr_err("pcm_write: timeout. dsp dead?\n"); - q6audio_dsp_not_responding(); + BUG(); } xfer = count; if (xfer > ab->size) xfer = ab->size; - if (copy_from_user(ab->data, buf, xfer)) + if (copy_from_user(ab->data, buf, xfer)) return -EFAULT; buf += xfer; diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c index 352c94011b7e5..b865ab67f6c89 100644 --- a/arch/arm/mach-msm/qdsp6/q6audio.c +++ b/arch/arm/mach-msm/qdsp6/q6audio.c @@ -16,10 +16,10 @@ #include #include -#include #include #include #include +#include #include #include @@ -33,7 +33,7 @@ #include "dal_adie.h" #include #include - +#include #include #include "q6audio_devices.h" @@ -178,12 +178,12 @@ int q6_device_volume(uint32_t device_id, int level) } } -static inline int adie_open(struct dal_client *client) +static inline int adie_open(struct dal_client *client) { return dal_call_f0(client, DAL_OP_OPEN, 0); } -static inline int adie_close(struct dal_client *client) +static inline int adie_close(struct dal_client *client) { return dal_call_f0(client, DAL_OP_CLOSE, 0); } @@ -349,7 +349,7 @@ static int audio_ioctl(struct audio_client *ac, void *ptr, uint32_t len) if (!wait_event_timeout(ac->wait, (ac->cb_status != -EBUSY), 5*HZ)) { dal_trace_dump(ac->client); pr_err("audio_ioctl: timeout. dsp dead?\n"); - q6audio_dsp_not_responding(); + BUG(); } return ac->cb_status; } @@ -439,6 +439,156 @@ static int audio_mp3_open(struct audio_client *ac, uint32_t bufsz, return audio_ioctl(ac, &rpc, sizeof(rpc)); } +static int audio_aac_open(struct audio_client *ac, uint32_t bufsz, + void *data) +{ + struct aac_format *af = data; + struct adsp_open_command rpc; + uint32_t *aac_type; + int idx = sizeof(uint32_t); + struct adsp_audio_binary_format *fmt = &(rpc.format.binary); + + memset(&rpc, 0, sizeof(rpc)); + + fmt->format = ADSP_AUDIO_FORMAT_MPEG4_AAC; + aac_type = (uint32_t *)(fmt->data); + switch (af->block_formats) { + case 0xffff: + if (ac->flags & AUDIO_FLAG_WRITE) + *aac_type = ADSP_AUDIO_AAC_ADTS; + else + *aac_type = ADSP_AUDIO_AAC_MPEG4_ADTS; + break; + case 0: + if (ac->flags & AUDIO_FLAG_WRITE) + *aac_type = ADSP_AUDIO_AAC_ADIF; + else + *aac_type = ADSP_AUDIO_AAC_RAW; + break; + case 1: + *aac_type = ADSP_AUDIO_AAC_RAW; + break; + case 2: + *aac_type = ADSP_AUDIO_AAC_LOAS; + break; + case 3: + *aac_type = ADSP_AUDIO_AAC_FRAMED_RAW; + break; + case 4: + *aac_type = ADSP_AUDIO_AAC_RAW; + break; + default: + pr_err("unsupported AAC type %d\n", af->block_formats); + return -EINVAL; + } + + TRACE("aac_open: type %x, obj %d, idx %d\n", + *aac_type, af->audio_object_type, idx); + fmt->data[idx++] = (u8)(((af->audio_object_type & 0x1F) << 3) | + ((af->sample_rate >> 1) & 0x7)); + fmt->data[idx] = (u8)(((af->sample_rate & 0x1) << 7) | + ((af->channel_config & 0x7) << 3)); + + switch (af->audio_object_type) { + case AAC_OBJECT_ER_LC: + case AAC_OBJECT_ER_LTP: + case AAC_OBJECT_ER_LD: + /* extension flag */ + fmt->data[idx++] |= 0x1; + fmt->data[idx] = (u8)( + ((af->aac_section_data_resilience_flag & 0x1) << 7) | + ((af->aac_scalefactor_data_resilience_flag & 0x1) << 6) | + ((af->aac_spectral_data_resilience_flag & 0x1) << 5) | + ((af->ep_config & 0x3) << 2)); + break; + + case AAC_OBJECT_ER_SCALABLE: + fmt->data[idx++] |= 0x1; + /* extension flag */ + fmt->data[idx++] = (u8)( + ((af->aac_section_data_resilience_flag & 0x1) << 4) | + ((af->aac_scalefactor_data_resilience_flag & 0x1) << 3) | + ((af->aac_spectral_data_resilience_flag & 0x1) << 2) | + ((af->ep_config >> 1) & 0x1)); + fmt->data[idx] = (u8)((af->ep_config & 0x1) + << 7); + break; + + case AAC_OBJECT_BSAC: + fmt->data[++idx] = (u8)((af->ep_config & 0x3) + << 6); + break; + + default: + pr_err("dbg unknown object type \n"); + break; + } + fmt->num_bytes = idx + 1; + + TRACE("aac_open: format %x%x %x%x%x%x %x%x, \n", + fmt->data[0], fmt->data[1], fmt->data[2], fmt->data[3], + fmt->data[4], fmt->data[5], fmt->data[6], fmt->data[7]); + + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.config.aac.bit_rate = af->bit_rate; + if (ac->flags & AUDIO_FLAG_WRITE) { + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK; + } else { + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; + } + + if ((af->sbr_on_flag == 0) && (af->sbr_ps_on_flag == 0)) { + rpc.config.aac.encoder_mode = + ADSP_AUDIO_ENC_AAC_LC_ONLY_MODE; + } else if ((af->sbr_on_flag == 1) && (af->sbr_ps_on_flag == 0)) { + rpc.config.aac.encoder_mode = + ADSP_AUDIO_ENC_AAC_PLUS_MODE; + } else if ((af->sbr_on_flag == 1) && (af->sbr_ps_on_flag == 1)) { + rpc.config.aac.encoder_mode = + ADSP_AUDIO_ENC_ENHANCED_AAC_PLUS_MODE; + } else { + pr_err("unsupported SBR flag\n"); + return -EINVAL; + } + rpc.buf_max_size = bufsz; /* XXX ??? */ + rpc.hdr.response_type = 0; + + TRACE("aac_open: opcode %x, stream_context 0x%x, " + "mode %d, bytes %d, bbuffer size %d\n", + rpc.hdr.opcode, rpc.stream_context, + rpc.config.aac.encoder_mode, fmt->num_bytes, bufsz); + + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + +static int audio_qcelp_open(struct audio_client *ac, uint32_t bufsz, + void *data) +{ + struct msm_audio_qcelp_config *qf = data; + struct adsp_open_command rpc; + struct adsp_audio_standard_format *fmt = &(rpc.format.standard); + + memset(&rpc, 0, sizeof(rpc)); + + fmt->format = ADSP_AUDIO_FORMAT_V13K_FS; + fmt->sampling_rate = 8000; + fmt->channels = 1; + fmt->bits_per_sample = 16; + fmt->is_signed = 1; + fmt->is_interleaved = 0; + + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; + rpc.config.qcelp13k.min_rate = (uint16_t) qf->min_bit_rate; + rpc.config.qcelp13k.max_rate = (uint16_t) qf->max_bit_rate; + rpc.buf_max_size = bufsz; /* XXX ??? */ + + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} + static int audio_close(struct audio_client *ac) { TRACE("%p: close\n", ac); @@ -585,7 +735,10 @@ static void callback(void *data, int len, void *cookie) { struct adsp_event_hdr *e = data; struct audio_client *ac; + struct adsp_buffer_event *abe = data; + TRACE("audio callback: context %d, event 0x%x, status %d\n", + e->context, e->event_id, e->status); if (e->context >= SESSION_MAX) { pr_err("audio callback: bogus session %d\n", @@ -612,6 +765,9 @@ static void callback(void *data, int len, void *cookie) if (e->event_id == ADSP_AUDIO_EVT_STATUS_BUF_DONE) { TRACE("%p: CB done (%d)\n", ac, e->status); + TRACE("%p: actual_size %d, buffer_size %d\n", + ac, abe->buffer.actual_size, ac->buf[ac->dsp_buf].size); + if (e->status) pr_err("buffer status %d\n", e->status); ac->buf[ac->dsp_buf].used = 0; @@ -893,6 +1049,10 @@ static void audio_rx_analog_enable(int en) if (analog_ops->receiver_enable) analog_ops->receiver_enable(en); break; + case ADSP_AUDIO_DEVICE_ID_I2S_SPKR: + if (analog_ops->i2s_enable) + analog_ops->i2s_enable(en); + break; } } @@ -938,7 +1098,8 @@ static int audio_update_acdb(uint32_t adev, uint32_t acdb_id) return -EINVAL; } - audio_set_table(ac_control, adev, sz); + if (sz > 0) + audio_set_table(ac_control, adev, sz); return 0; } @@ -1214,7 +1375,7 @@ int q6audio_reinit_acdb(char* filename) { return 0; mutex_lock(&audio_path_lock); - if (strlen(filename) < 0 || !strcmp(filename, acdb_file)) { + if (strlen(filename) < 0) { res = -EINVAL; goto done; } @@ -1254,6 +1415,7 @@ int q6audio_update_acdb(uint32_t id_src, uint32_t id_dst) int q6audio_set_tx_mute(int mute) { uint32_t adev; + int rc; if (q6audio_init()) return 0; @@ -1266,8 +1428,8 @@ int q6audio_set_tx_mute(int mute) } adev = audio_tx_device_id; - audio_tx_mute(ac_control, adev, mute); - tx_mute_status = mute; + rc = audio_tx_mute(ac_control, adev, mute); + if (!rc) tx_mute_status = mute; mutex_unlock(&audio_path_lock); return 0; } @@ -1459,7 +1621,7 @@ struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, if (rc == 0) break; if (retry == 0) - q6audio_dsp_not_responding(); + BUG(); pr_err("q6audio: open pcm error %d, retrying\n", rc); msleep(1); } @@ -1484,7 +1646,7 @@ struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, if (rc == 0) break; if (retry == 0) - q6audio_dsp_not_responding(); + BUG(); pr_err("q6audio: stream start error %d, retrying\n", rc); } @@ -1524,9 +1686,10 @@ struct audio_client *q6voice_open(uint32_t flags, uint32_t acdb_id) return 0; ac->flags = flags; - if (ac->flags & AUDIO_FLAG_WRITE) + if (ac->flags & AUDIO_FLAG_WRITE) { audio_rx_path_enable(1, acdb_id); - else { + audio_rx_mute(ac_control, ADSP_AUDIO_DEVICE_ID_VOICE, 0); + } else { tx_clk_freq = 8000; audio_tx_path_enable(1, acdb_id); } @@ -1536,9 +1699,10 @@ struct audio_client *q6voice_open(uint32_t flags, uint32_t acdb_id) int q6voice_close(struct audio_client *ac) { - if (ac->flags & AUDIO_FLAG_WRITE) + if (ac->flags & AUDIO_FLAG_WRITE) { + audio_rx_mute(ac_control, ADSP_AUDIO_DEVICE_ID_VOICE, 1); audio_rx_path_enable(0, 0); - else + } else audio_tx_path_enable(0, 0); audio_client_free(ac); @@ -1585,17 +1749,68 @@ int q6audio_async(struct audio_client *ac) return audio_ioctl(ac, &rpc, sizeof(rpc)); } +struct audio_client *q6audio_open_aac(uint32_t bufsz, uint32_t rate, + uint32_t flags, void *data, uint32_t acdb_id) +{ + struct audio_client *ac; + + TRACE("q6audio_open_aac flags=%d rate=%d\n", flags, rate); + + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(bufsz); + if (!ac) + return 0; + + ac->flags = flags; + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(1, acdb_id); + else { + /* TODO: consider concourrency with voice call */ + tx_clk_freq = rate; + audio_tx_path_enable(1, acdb_id); + } + + audio_aac_open(ac, bufsz, data); + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); + + if (!(ac->flags & AUDIO_FLAG_WRITE)) { + ac->buf[0].used = 1; + ac->buf[1].used = 1; + q6audio_read(ac, &ac->buf[0]); + q6audio_read(ac, &ac->buf[1]); + } + audio_prevent_sleep(); + return ac; +} + +int q6audio_aac_close(struct audio_client *ac) +{ + audio_close(ac); + if (ac->flags & AUDIO_FLAG_WRITE) + audio_rx_path_enable(0, 0); + else + audio_tx_path_enable(0, 0); + + audio_client_free(ac); + audio_allow_sleep(); + return 0; +} + struct audio_client *q6fm_open(void) { struct audio_client *ac; + printk("q6fm_open()\n"); + if (q6audio_init()) return 0; -/* if (audio_rx_device_id != ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO && + if (audio_rx_device_id != ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO && audio_rx_device_id != ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO) return 0; -*/ + ac = audio_client_alloc(0); if (!ac) return 0; @@ -1615,3 +1830,40 @@ int q6fm_close(struct audio_client *ac) return 0; } +struct audio_client *q6audio_open_qcelp(uint32_t bufsz, uint32_t rate, + void *data, uint32_t acdb_id) +{ + struct audio_client *ac; + + if (q6audio_init()) + return 0; + + ac = audio_client_alloc(bufsz); + if (!ac) + return 0; + + ac->flags = AUDIO_FLAG_READ; + tx_clk_freq = rate; + audio_tx_path_enable(1, acdb_id); + + audio_qcelp_open(ac, bufsz, data); + audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); + + ac->buf[0].used = 1; + ac->buf[1].used = 1; + q6audio_read(ac, &ac->buf[0]); + q6audio_read(ac, &ac->buf[1]); + + audio_prevent_sleep(); + return ac; +} + +int q6audio_qcelp_close(struct audio_client *ac) +{ + audio_close(ac); + audio_tx_path_enable(0, 0); + audio_client_free(ac); + audio_allow_sleep(); + return 0; +} + diff --git a/include/linux/msm_audio.h b/include/linux/msm_audio.h index 8c1fb13937259..4f7562d89fb75 100644 --- a/include/linux/msm_audio.h +++ b/include/linux/msm_audio.h @@ -40,6 +40,14 @@ #define AUDIO_SET_NS _IOW(AUDIO_IOCTL_MAGIC, 13, unsigned) #define AUDIO_SET_TX_IIR _IOW(AUDIO_IOCTL_MAGIC, 14, unsigned) #define AUDIO_PAUSE _IOW(AUDIO_IOCTL_MAGIC, 15, unsigned) +#define AUDIO_SET_AAC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 15, unsigned) +#define AUDIO_WAIT_ADSP_DONE _IOR(AUDIO_IOCTL_MAGIC, 16, unsigned) +#define AUDIO_ADSP_PAUSE _IOR(AUDIO_IOCTL_MAGIC, 17, unsigned) +#define AUDIO_ADSP_RESUME _IOR(AUDIO_IOCTL_MAGIC, 18, unsigned) +#define AUDIO_PLAY_DTMF _IOW(AUDIO_IOCTL_MAGIC, 19, unsigned) +#define AUDIO_GET_AAC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 20, unsigned) +#define AUDIO_GET_AMRNB_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 21, unsigned) +#define AUDIO_SET_AMRNB_ENC_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 22, unsigned) #define AUDIO_GET_PCM_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 30, unsigned) #define AUDIO_SET_PCM_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 31, unsigned) #define AUDIO_SWITCH_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 32, unsigned) @@ -47,9 +55,12 @@ #define AUDIO_UPDATE_ACDB _IOW(AUDIO_IOCTL_MAGIC, 34, unsigned) #define AUDIO_START_VOICE _IOW(AUDIO_IOCTL_MAGIC, 35, unsigned) #define AUDIO_STOP_VOICE _IOW(AUDIO_IOCTL_MAGIC, 36, unsigned) -#define AUDIO_START_FM _IOW(AUDIO_IOCTL_MAGIC, 37, unsigned) -#define AUDIO_STOP_FM _IOW(AUDIO_IOCTL_MAGIC, 38, unsigned) +#define AUDIO_START_FM _IOW(AUDIO_IOCTL_MAGIC, 37, unsigned) +#define AUDIO_STOP_FM _IOW(AUDIO_IOCTL_MAGIC, 38, unsigned) #define AUDIO_REINIT_ACDB _IOW(AUDIO_IOCTL_MAGIC, 39, unsigned) +#define AUDIO_ENABLE_AUXPGA_LOOPBACK _IOW(AUDIO_IOCTL_MAGIC, 40, unsigned) +#define AUDIO_SET_AUXPGA_GAIN _IOW(AUDIO_IOCTL_MAGIC, 41, unsigned) +#define AUDIO_SET_RX_MUTE _IOW(AUDIO_IOCTL_MAGIC, 42, unsigned) #define AUDIO_MAX_COMMON_IOCTL_NUM 100 @@ -70,6 +81,88 @@ struct msm_audio_stats { uint32_t unused[2]; }; +struct msm_mute_info { + uint32_t mute; + uint32_t path; +}; + +#define AUDIO_AAC_FORMAT_ADTS -1 +#define AUDIO_AAC_FORMAT_RAW 0x0000 +#define AUDIO_AAC_FORMAT_PSUEDO_RAW 0x0001 +#define AUDIO_AAC_FORMAT_LOAS 0x0002 + +#define AUDIO_AAC_OBJECT_LC 0x0002 +#define AUDIO_AAC_OBJECT_LTP 0x0004 +#define AUDIO_AAC_OBJECT_ERLC 0x0011 + +#define AUDIO_AAC_SEC_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SEC_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SCA_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SCA_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SPEC_DATA_RES_ON 0x0001 +#define AUDIO_AAC_SPEC_DATA_RES_OFF 0x0000 + +#define AUDIO_AAC_SBR_ON_FLAG_ON 0x0001 +#define AUDIO_AAC_SBR_ON_FLAG_OFF 0x0000 + +#define AUDIO_AAC_SBR_PS_ON_FLAG_ON 0x0001 +#define AUDIO_AAC_SBR_PS_ON_FLAG_OFF 0x0000 + +/* Primary channel on both left and right channels */ +#define AUDIO_AAC_DUAL_MONO_PL_PR 0 +/* Secondary channel on both left and right channels */ +#define AUDIO_AAC_DUAL_MONO_SL_SR 1 +/* Primary channel on right channel and 2nd on left channel */ +#define AUDIO_AAC_DUAL_MONO_SL_PR 2 +/* 2nd channel on right channel and primary on left channel */ +#define AUDIO_AAC_DUAL_MONO_PL_SR 3 + +#define AAC_OBJECT_ER_LC 17 +#define AAC_OBJECT_ER_LTP 19 +#define AAC_OBJECT_ER_SCALABLE 20 +#define AAC_OBJECT_BSAC 22 +#define AAC_OBJECT_ER_LD 23 + +struct aac_format { + uint16_t sample_rate; + uint16_t channel_config; + uint16_t block_formats; + uint16_t audio_object_type; + uint16_t ep_config; + uint16_t aac_section_data_resilience_flag; + uint16_t aac_scalefactor_data_resilience_flag; + uint16_t aac_spectral_data_resilience_flag; + uint16_t sbr_on_flag; + uint16_t sbr_ps_on_flag; + uint32_t bit_rate; +}; + +struct msm_audio_aac_config { + signed short format; + unsigned short audio_object; + unsigned short ep_config; /* 0 ~ 3 useful only obj = ERLC */ + unsigned short aac_section_data_resilience_flag; + unsigned short aac_scalefactor_data_resilience_flag; + unsigned short aac_spectral_data_resilience_flag; + unsigned short sbr_on_flag; + unsigned short sbr_ps_on_flag; + unsigned short dual_mono_mode; + unsigned short channel_configuration; +}; + +struct msm_audio_amrnb_enc_config { + unsigned short voicememoencweight1; + unsigned short voicememoencweight2; + unsigned short voicememoencweight3; + unsigned short voicememoencweight4; + unsigned short dtx_mode_enable; /* 0xFFFF - enable, 0- disable */ + unsigned short test_mode_enable; /* 0xFFFF - enable, 0- disable */ + unsigned short enc_mode; /* 0-MR475,1-MR515,2-MR59,3-MR67,4-MR74 + 5-MR795, 6- MR102, 7- MR122(default) */ +}; + /* Audio routing */ #define SND_IOCTL_MAGIC 's' From 8ebc8207541a05c85e99436fa317b050df53ac31 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Mon, 24 Jan 2011 22:23:57 -0500 Subject: [PATCH 1043/2556] mach-msm: add a few functions to generic_gpio to help with forward porting for the Supersonic --- arch/arm/mach-msm/gpio.c | 11 ++++++ arch/arm/mach-msm/include/mach/gpio.h | 54 +++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/arch/arm/mach-msm/gpio.c b/arch/arm/mach-msm/gpio.c index 2fe2e81fd79f8..7a3d75e95c8b3 100644 --- a/arch/arm/mach-msm/gpio.c +++ b/arch/arm/mach-msm/gpio.c @@ -467,6 +467,17 @@ void msm_gpio_exit_sleep(void) } } +void config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for (n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} +EXPORT_SYMBOL(config_gpio_table); + static int __init msm_init_gpio(void) { int i, j = 0; diff --git a/arch/arm/mach-msm/include/mach/gpio.h b/arch/arm/mach-msm/include/mach/gpio.h index 9e4345f5e4e90..0971190a7826f 100644 --- a/arch/arm/mach-msm/include/mach/gpio.h +++ b/arch/arm/mach-msm/include/mach/gpio.h @@ -21,6 +21,60 @@ #define FIRST_BOARD_GPIO NR_GPIO_IRQS +/* GPIO TLMM (Top Level Multiplexing) Definitions */ + +/* GPIO TLMM: Function -- GPIO specific */ + +/* GPIO TLMM: Direction */ +enum { + GPIO_CFG_INPUT, + GPIO_CFG_OUTPUT, +}; + +/* GPIO TLMM: Pullup/Pulldown */ +enum { + GPIO_CFG_NO_PULL, + GPIO_CFG_PULL_DOWN, + GPIO_CFG_KEEPER, + GPIO_CFG_PULL_UP, +}; + +/* GPIO TLMM: Drive Strength */ +enum { + GPIO_CFG_2MA, + GPIO_CFG_4MA, + GPIO_CFG_6MA, + GPIO_CFG_8MA, + GPIO_CFG_10MA, + GPIO_CFG_12MA, + GPIO_CFG_14MA, + GPIO_CFG_16MA, +}; + +enum { + GPIO_CFG_ENABLE, + GPIO_CFG_DISABLE, +}; + +extern void config_gpio_table(uint32_t *table, int len); +extern int gpio_configure(unsigned int gpio, unsigned long flags); + +#define GPIO_CFG(gpio, func, dir, pull, drvstr) \ + ((((gpio) & 0x3FF) << 4) | \ + ((func) & 0xf) | \ + (((dir) & 0x1) << 14) | \ + (((pull) & 0x3) << 15) | \ + (((drvstr) & 0xF) << 17)) + +/** + * extract GPIO pin from bit-field used for gpio_tlmm_config + */ +#define GPIO_PIN(gpio_cfg) (((gpio_cfg) >> 4) & 0x3ff) +#define GPIO_FUNC(gpio_cfg) (((gpio_cfg) >> 0) & 0xf) +#define GPIO_DIR(gpio_cfg) (((gpio_cfg) >> 14) & 0x1) +#define GPIO_PULL(gpio_cfg) (((gpio_cfg) >> 15) & 0x3) +#define GPIO_DRVSTR(gpio_cfg) (((gpio_cfg) >> 17) & 0xf) + #define gpio_get_value __gpio_get_value #define gpio_set_value __gpio_set_value #define gpio_cansleep __gpio_cansleep From 9cd397eb3540c3b2704bd100a48727d9f07d2263 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Mon, 24 Jan 2011 22:25:49 -0500 Subject: [PATCH 1044/2556] sensors: bma150 header updated to support the bma150_spi driver for microp common --- include/linux/bma150.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/linux/bma150.h b/include/linux/bma150.h index 072faa5b18d14..dd6ee393d2dcf 100644 --- a/include/linux/bma150.h +++ b/include/linux/bma150.h @@ -7,7 +7,6 @@ #include #define BMA150_I2C_NAME "bma150" - #ifdef CONFIG_SPI_QSD #define BMA150_G_SENSOR_NAME "bma150_uP_spi" #else @@ -57,7 +56,7 @@ #define BMA_IOCTL_READ_ACCELERATION _IOWR(BMAIO, 0x34, short[7]) #define BMA_IOCTL_SET_MODE _IOW(BMAIO, 0x35, short) #define BMA_IOCTL_GET_INT _IOR(BMAIO, 0x36, short) - +#define BMA_IOCTL_GET_CHIP_LAYOUT _IOR(BMAIO, 0x37, short) /* range and bandwidth */ #define BMA_RANGE_2G 0 @@ -78,6 +77,8 @@ struct bma150_platform_data { int intr; + int microp_new_cmd; + int chip_layout; }; #endif From 0576b33882592a449f40f032040808cd9a2b4cc5 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Thu, 31 Mar 2011 21:46:09 -0400 Subject: [PATCH 1045/2556] msm: irqs: add HTC irq --- arch/arm/mach-msm/include/mach/irqs.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/include/mach/irqs.h b/arch/arm/mach-msm/include/mach/irqs.h index e1d9267948e6b..63fe01fcb0e89 100644 --- a/arch/arm/mach-msm/include/mach/irqs.h +++ b/arch/arm/mach-msm/include/mach/irqs.h @@ -19,9 +19,13 @@ #define MSM_IRQ_BIT(irq) (1 << ((irq) & 31)) +#define NR_BOARD_IRQS 64 +#define NR_MICROP_IRQS 16 + #define FIRST_SIRC_IRQ (NR_MSM_IRQS) #define FIRST_GPIO_IRQ (NR_MSM_IRQS + NR_SIRC_IRQS) #define FIRST_BOARD_IRQ (NR_MSM_IRQS + NR_SIRC_IRQS + NR_GPIO_IRQS) +#define FIRST_MICROP_IRQ (FIRST_BOARD_IRQ + NR_BOARD_IRQS) #if defined(CONFIG_ARCH_MSM7X30) #include "irqs-7x30.h" @@ -36,8 +40,16 @@ #error "Unknown architecture specification" #endif +#if defined(CONFIG_MACH_SUPERSONIC) +#define NR_IRQS (NR_MSM_IRQS + NR_SIRC_IRQS + NR_GPIO_IRQS + NR_BOARD_IRQS \ + + NR_MICROP_IRQS) +#define MSM_INT_TO_GPIO(n) ((n) - NR_MSM_IRQS) +#define MSM_GPIO_TO_INT(n) (FIRST_GPIO_IRQ + (n)) +#define MSM_uP_TO_INT(n) (FIRST_MICROP_IRQ + (n)) +#else #define NR_IRQS (NR_MSM_IRQS + NR_SIRC_IRQS + NR_GPIO_IRQS + NR_BOARD_IRQS) +#endif + #define MSM_GPIO_TO_INT(n) (FIRST_GPIO_IRQ + (n)) #define MSM_INT_TO_REG(base, irq) (base + irq / 32) - #endif From 04bad907546d7541beeeb085cc8230dca55b98a5 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Mon, 24 Jan 2011 22:33:35 -0500 Subject: [PATCH 1046/2556] mach-msm: fixed up msmfb header from a bad merge. forgot to git add this one >_> --- drivers/video/msm/msm_fb.c | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index 829e4d2b58258..7dc33011f8255 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -305,9 +305,6 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, DLOG(SHOW_UPDATES, "update %d %d %d %d %d %d\n", left, top, eright, ebottom, yoffset, pan_display); - if (msmfb->sleeping != AWAKE) - DLOG(SUSPEND_RESUME, "pan_update in state(%d)\n", msmfb->sleeping); - #ifdef CONFIG_PANEL_SELF_REFRESH if (lcdc->mdp->mdp_dev.overrides & MSM_MDP_RGB_PANEL_SELE_REFRESH) { spin_lock_irqsave(&panel_icm->lock, irq_flags); @@ -322,8 +319,6 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, msmfb_set_var(msmfb->fb->screen_base, yoffset); #endif ->>>>>>> 2eaad42... video: msm: Updated MDDI from HTC -restart: spin_lock_irqsave(&msmfb->update_lock, irq_flags); /* if we are sleeping, on a pan_display wait 10ms (to throttle back @@ -459,13 +454,13 @@ static void power_on_panel(struct work_struct *work) mutex_unlock(&msmfb->panel_init_lock); } -<<<<<<< HEAD -======= + static BLOCKING_NOTIFIER_HEAD(display_chain_head); int register_display_notifier(struct notifier_block *nb) { return blocking_notifier_chain_register(&display_chain_head, nb); -} +} + static int display_notifier_callback(struct notifier_block *nfb, unsigned long action, void *ignored) @@ -489,7 +484,7 @@ static int display_notifier_callback(struct notifier_block *nfb, } /* -------------------------------------------------------------------------- */ ->>>>>>> 2eaad42... video: msm: Updated MDDI from HTC + #ifdef CONFIG_HAS_EARLYSUSPEND /* turn off the panel */ static void msmfb_earlier_suspend(struct early_suspend *h) @@ -536,12 +531,6 @@ static void msmfb_suspend(struct early_suspend *h) mutex_unlock(&msmfb->panel_init_lock); } -<<<<<<< HEAD -static void msmfb_resume(struct early_suspend *h) -{ - struct msmfb_info *msmfb = container_of(h, struct msmfb_info, - early_suspend); -======= static void msmfb_resume_handler(struct early_suspend *h) { struct msmfb_info *msmfb = container_of(h, struct msmfb_info, @@ -561,7 +550,7 @@ static void msmfb_onchg_earlier_suspend(struct early_suspend *h) { struct msmfb_info *msmfb = container_of(h, struct msmfb_info, onchg_earlier_suspend); ->>>>>>> 2eaad42... video: msm: Updated MDDI from HTC + struct msm_panel_data *panel = msmfb->panel; unsigned long irq_flags; @@ -575,8 +564,7 @@ static void msmfb_onchg_earlier_suspend(struct early_suspend *h) msmfb->sleeping = WAKING; DLOG(SUSPEND_RESUME, "ready, waiting for full update\n"); spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); -<<<<<<< HEAD -======= + wait_event_timeout(msmfb->frame_wq, msmfb->frame_requested == msmfb->frame_done, HZ/10); #ifndef CONFIG_MSM_MDP40 @@ -640,7 +628,6 @@ static void msmfb_resume(struct work_struct *work) wake_up(&msmfb->frame_wq); atomic_set(&mdpclk_on, 1); ->>>>>>> 2eaad42... video: msm: Updated MDDI from HTC } #endif @@ -1190,8 +1177,6 @@ static int msmfb_probe(struct platform_device *pdev) return ret; } -<<<<<<< HEAD -======= static void msmfb_shutdown(struct platform_device *pdev) { struct msm_panel_data *panel = pdev->dev.platform_data; @@ -1217,7 +1202,6 @@ static void msmfb_shutdown(struct platform_device *pdev) panel->shutdown(panel); } ->>>>>>> 2eaad42... video: msm: Updated MDDI from HTC static struct platform_driver msm_panel_driver = { /* need to write remove */ .probe = msmfb_probe, From 9edfde63a805d2aec586011180c04ea75087803d Mon Sep 17 00:00:00 2001 From: toastcfh Date: Mon, 24 Jan 2011 23:59:16 -0500 Subject: [PATCH 1047/2556] mach-msm: board.h: adding usb_status_notifier to the header file. --- arch/arm/mach-msm/include/mach/board.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index 913d1b6ee7072..5884d767ecd1c 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -174,8 +174,17 @@ int __init msm_add_sdcc(unsigned int controller, struct msm_mmc_platform_data *plat, unsigned int stat_irq, unsigned long stat_irq_flags); -#ifdef CONFIG_USB_MSM_72K +#if defined(CONFIG_USB_FUNCTION_MSM_HSUSB) || defined(CONFIG_USB_MSM_72K) void msm_hsusb_set_vbus_state(int online); +/* START: add USB connected notify function */ +struct t_usb_status_notifier{ + struct list_head notifier_link; + const char *name; + void (*func)(int online); +}; + int usb_register_notifier(struct t_usb_status_notifier *); + static LIST_HEAD(g_lh_usb_notifier_list); +/* END: add USB connected notify function */ #else static inline void msm_hsusb_set_vbus_state(int online) {} #endif From a2f0fede4f01196ee4737d1fc460f9660b607f8c Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Thu, 4 Nov 2010 11:50:15 -0400 Subject: [PATCH 1048/2556] msm: Consolidate code for reading ATAG data --- arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/htc_board_tags.c | 257 +++++++++++++++++++++++++ arch/arm/mach-msm/include/mach/board.h | 16 ++ 3 files changed, 274 insertions(+) create mode 100644 arch/arm/mach-msm/htc_board_tags.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 33870c01524a2..e907f0318bd2e 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -7,6 +7,7 @@ endif obj-y += nand_partitions.o obj-y += pmic.o obj-y += drv_callback.o +obj-y += htc_board_tags.o obj-$(CONFIG_ARCH_MSM_ARM11) += acpuclock-arm11.o idle.o obj-$(CONFIG_ARCH_MSM_SCORPION) += idle-v7.o diff --git a/arch/arm/mach-msm/htc_board_tags.c b/arch/arm/mach-msm/htc_board_tags.c new file mode 100644 index 0000000000000..f0cb064c47fe4 --- /dev/null +++ b/arch/arm/mach-msm/htc_board_tags.c @@ -0,0 +1,257 @@ +/* linux/arch/arm/mach-msm/devices.c + * + * Copyright (C) 2008 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 + +#include +#include +#include +#include + +#include "smd_private.h" + +#include +#include +#include +#include +#include + +static char *df_serialno = "000000000000"; +static char *board_sn; + +#define MFG_GPIO_TABLE_MAX_SIZE 0x400 +static unsigned char mfg_gpio_table[MFG_GPIO_TABLE_MAX_SIZE]; + +static int mfg_mode; +int __init board_mfg_mode_init(char *s) +{ + if (!strcmp(s, "normal")) + mfg_mode = 0; + else if (!strcmp(s, "factory2")) + mfg_mode = 1; + else if (!strcmp(s, "recovery")) + mfg_mode = 2; + else if (!strcmp(s, "charge")) + mfg_mode = 3; + else if (!strcmp(s, "power_test")) + mfg_mode = 4; + else if (!strcmp(s, "offmode_charging")) + mfg_mode = 5; + + return 1; +} +__setup("androidboot.mode=", board_mfg_mode_init); + + +int board_mfg_mode(void) +{ + return mfg_mode; +} + +EXPORT_SYMBOL(board_mfg_mode); + +static int __init board_serialno_setup(char *serialno) +{ + char *str; + + /* use default serial number when mode is factory2 */ + if (board_mfg_mode() == 1 || !strlen(serialno)) + str = df_serialno; + else + str = serialno; + board_sn = str; + return 1; +} +__setup("androidboot.serialno=", board_serialno_setup); + +char *board_serialno(void) +{ + return board_sn; +} + +EXPORT_SYMBOL(board_serialno); + +#define ATAG_SMI 0x4d534D71 +/* setup calls mach->fixup, then parse_tags, parse_cmdline + * We need to setup meminfo in mach->fixup, so this function + * will need to traverse each tag to find smi tag. + */ +int __init parse_tag_smi(const struct tag *tags) +{ + int smi_sz = 0, find = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_SMI) { + printk(KERN_DEBUG "find the smi tag\n"); + find = 1; + break; + } + } + if (!find) + return -1; + + printk(KERN_DEBUG "parse_tag_smi: smi size = %d\n", t->u.mem.size); + smi_sz = t->u.mem.size; + return smi_sz; +} +__tagtable(ATAG_SMI, parse_tag_smi); + +#define ATAG_HWID 0x4d534D72 +int __init parse_tag_hwid(const struct tag *tags) +{ + int hwid = 0, find = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_HWID) { + printk(KERN_DEBUG "find the hwid tag\n"); + find = 1; + break; + } + } + + if (find) + hwid = t->u.revision.rev; + printk(KERN_DEBUG "parse_tag_hwid: hwid = 0x%x\n", hwid); + return hwid; +} +__tagtable(ATAG_HWID, parse_tag_hwid); + +#define ATAG_SKUID 0x4d534D73 +int __init parse_tag_skuid(const struct tag *tags) +{ + int skuid = 0, find = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_SKUID) { + printk(KERN_DEBUG "find the skuid tag\n"); + find = 1; + break; + } + } + + if (find) + skuid = t->u.revision.rev; + printk(KERN_DEBUG "parse_tag_skuid: hwid = 0x%x\n", skuid); + return skuid; +} +__tagtable(ATAG_SKUID, parse_tag_skuid); + +#define ATAG_HERO_PANEL_TYPE 0x4d534D74 +int panel_type; +int __init tag_panel_parsing(const struct tag *tags) +{ + panel_type = tags->u.revision.rev; + + printk(KERN_DEBUG "%s: panel type = %d\n", __func__, + panel_type); + + return panel_type; +} +__tagtable(ATAG_HERO_PANEL_TYPE, tag_panel_parsing); + +#define ATAG_ENGINEERID 0x4d534D75 +unsigned engineer_id; +int __init parse_tag_engineerid(const struct tag *tags) +{ + int engineerid = 0, find = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_ENGINEERID) { + printk(KERN_DEBUG "find the engineer tag\n"); + find = 1; + break; + } + } + + if (find) { + engineer_id = t->u.revision.rev; + engineerid = t->u.revision.rev; + } + printk(KERN_DEBUG "parse_tag_engineerid: 0x%x\n", engineerid); + return engineerid; +} +__tagtable(ATAG_ENGINEERID, parse_tag_engineerid); + +#define ATAG_MFG_GPIO_TABLE 0x59504551 +int __init parse_tag_mfg_gpio_table(const struct tag *tags) +{ + unsigned char *dptr = (unsigned char *)(&tags->u); + __u32 size; + + size = min((__u32)(tags->hdr.size - 2) * sizeof(__u32), (__u32)MFG_GPIO_TABLE_MAX_SIZE); + memcpy(mfg_gpio_table, dptr, size); + return 0; +} +__tagtable(ATAG_MFG_GPIO_TABLE, parse_tag_mfg_gpio_table); + +char * board_get_mfg_sleep_gpio_table(void) +{ + return mfg_gpio_table; +} +EXPORT_SYMBOL(board_get_mfg_sleep_gpio_table); + +static char *emmc_tag; +static int __init board_set_emmc_tag(char *get_hboot_emmc) +{ + if (strlen(get_hboot_emmc)) + emmc_tag = get_hboot_emmc; + else + emmc_tag = NULL; + return 1; +} +__setup("androidboot.emmc=", board_set_emmc_tag); + +int board_emmc_boot(void) +{ + if (emmc_tag) { + if (!strcmp(emmc_tag, "true")) + return 1; + } + + return 0; +} + +#define ATAG_MEMSIZE 0x5441001e +unsigned memory_size; +int __init parse_tag_memsize(const struct tag *tags) +{ + int mem_size = 0, find = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_MEMSIZE) { + printk(KERN_DEBUG "find the memsize tag\n"); + find = 1; + break; + } + } + + if (find) { + memory_size = t->u.revision.rev; + mem_size = t->u.revision.rev; + } + printk(KERN_DEBUG "parse_tag_memsize: %d\n", memory_size); + return mem_size; +} +__tagtable(ATAG_MEMSIZE, parse_tag_memsize); + diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index 5884d767ecd1c..cd8c2d4da9d9d 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -189,4 +189,20 @@ struct t_usb_status_notifier{ static inline void msm_hsusb_set_vbus_state(int online) {} #endif +char *board_serialno(void); +int __init parse_tag_skuid(const struct tag *tags); +int __init parse_tag_engineerid(const struct tag *tags); +int __init parse_tag_memsize(const struct tag *tags); +int board_mfg_mode(void); +void __init msm_snddev_init(void); +void msm_snddev_poweramp_on(void); +void msm_snddev_poweramp_off(void); +void msm_snddev_hsed_pamp_on(void); +void msm_snddev_hsed_pamp_off(void); +void msm_snddev_tx_route_config(void); +void msm_snddev_tx_route_deconfig(void); + +extern int emmc_partition_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data); + #endif From 4094f4d570c1fe84e72fd120a472d34e3333a113 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Tue, 25 Jan 2011 20:09:56 -0500 Subject: [PATCH 1049/2556] supersonic: minor changes to drivers all over the place for 2.6.38. also a couple gitfails in there from cherry-picking cleaned up. --- arch/arm/mach-msm/devices_htc.c | 20 ++++++++++++++++++-- arch/arm/mach-msm/qdsp6/msm_q6venc.c | 17 +++++++++++++++-- drivers/misc/akm8973.c | 12 ------------ drivers/misc/akm8973_htc.c | 12 ++++++------ drivers/misc/bma150_spi.c | 3 +-- drivers/usb/gadget/msm72k_udc_htc.c | 8 ++++---- 6 files changed, 44 insertions(+), 28 deletions(-) diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c index f270df4054198..e23d8d7e033da 100644 --- a/arch/arm/mach-msm/devices_htc.c +++ b/arch/arm/mach-msm/devices_htc.c @@ -36,8 +36,6 @@ #include #include -static char *df_serialno = "000000000000"; - #if 0 struct platform_device *devices[] __initdata = { &msm_device_nand, @@ -195,6 +193,8 @@ static struct platform_device android_usb_device = { void __init msm_add_usb_devices(void (*phy_reset) (void)) { /* setup */ + android_usb_pdata.serial_number = board_serialno(); + if (phy_reset) msm_hsusb_pdata.phy_reset = phy_reset; msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; @@ -354,6 +354,7 @@ int __init msm_add_serial_devices(unsigned num) return platform_device_register(msm_serial_devices[num]); } #endif +<<<<<<< HEAD #define ATAG_SMI 0x4d534D71 /* setup calls mach->fixup, then parse_tags, parse_cmdline @@ -424,6 +425,19 @@ int __init parse_tag_skuid(const struct tag *tags) } __tagtable(ATAG_SKUID, parse_tag_skuid); +#define ATAG_HERO_PANEL_TYPE 0x4d534D74 +int panel_type; +int __init tag_panel_parsing(const struct tag *tags) +{ + panel_type = tags->u.revision.rev; + + printk(KERN_DEBUG "%s: panel type = %d\n", __func__, + panel_type); + + return panel_type; +} +__tagtable(ATAG_HERO_PANEL_TYPE, tag_panel_parsing); + #define ATAG_ENGINEERID 0x4d534D75 int __init parse_tag_engineerid(const struct tag *tags) { @@ -497,3 +511,5 @@ static int __init board_serialno_setup(char *serialno) } __setup("androidboot.serialno=", board_serialno_setup); +======= +>>>>>>> 0bf3625... msm: Consolidate code for reading ATAG data diff --git a/arch/arm/mach-msm/qdsp6/msm_q6venc.c b/arch/arm/mach-msm/qdsp6/msm_q6venc.c index 760b41594e0dd..e8c93b4813da4 100644 --- a/arch/arm/mach-msm/qdsp6/msm_q6venc.c +++ b/arch/arm/mach-msm/qdsp6/msm_q6venc.c @@ -821,10 +821,17 @@ static int venc_q6_stop(struct venc_dev *dvenc) { int ret = 0; struct venc_pmem_list *plist; + unsigned long flags; wake_up(&dvenc->venc_msg_evt); - list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) - put_pmem_file(plist->buf.file); + spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags); + if (!dvenc->pmem_freed) { + list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) + put_pmem_file(plist->buf.file); + dvenc->pmem_freed = 1; + } + spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags); + dvenc->state = VENC_STATE_STOP; return ret; } @@ -1158,6 +1165,12 @@ static int q6venc_release(struct inode *inode, struct file *file) list_del(&l->list); kfree(l); } + + list_for_each_entry_safe(l, n, &dvenc->venc_msg_list_head, list) { + list_del(&l->list); + kfree(l); + } + spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags); if (!dvenc->stop_called) { list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) diff --git a/drivers/misc/akm8973.c b/drivers/misc/akm8973.c index 20600b6e59b51..3e756fcee0d80 100644 --- a/drivers/misc/akm8973.c +++ b/drivers/misc/akm8973.c @@ -27,10 +27,7 @@ #include #include #include -<<<<<<< HEAD #include -======= ->>>>>>> 8988ee2... sensors: akm8973: Add HTC-variant driver. #define DEBUG 0 #define MAX_FAILURE_COUNT 3 @@ -312,18 +309,12 @@ static int AKECS_GetCloseStatus(void) static void AKECS_CloseDone(void) { -<<<<<<< HEAD mutex_lock(&akmd_lock); -======= ->>>>>>> 8988ee2... sensors: akm8973: Add HTC-variant driver. atomic_set(&m_flag, 1); atomic_set(&a_flag, 1); atomic_set(&t_flag, 1); atomic_set(&mv_flag, 1); -<<<<<<< HEAD mutex_unlock(&akmd_lock); -======= ->>>>>>> 8988ee2... sensors: akm8973: Add HTC-variant driver. } static int akm_aot_open(struct inode *inode, struct file *file) @@ -350,10 +341,7 @@ static int akm_aot_release(struct inode *inode, struct file *file) atomic_set(&open_count, 0); wake_up(&open_wq); disable_irq(this_client->irq); -<<<<<<< HEAD mutex_unlock(&akmd_lock); -======= ->>>>>>> 8988ee2... sensors: akm8973: Add HTC-variant driver. return 0; } diff --git a/drivers/misc/akm8973_htc.c b/drivers/misc/akm8973_htc.c index a7e6ccbc8eb6d..901cb99da65d8 100644 --- a/drivers/misc/akm8973_htc.c +++ b/drivers/misc/akm8973_htc.c @@ -330,9 +330,9 @@ static int akm_aot_release(struct inode *inode, struct file *file) return 0; } -static int -akm_aot_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long +akm_aot_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + { void __user *argp = (void __user *)arg; short flag; @@ -417,9 +417,9 @@ static int akmd_release(struct inode *inode, struct file *file) return 0; } -static int -akmd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) +static long +akmd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + { void __user *argp = (void __user *)arg; diff --git a/drivers/misc/bma150_spi.c b/drivers/misc/bma150_spi.c index dc952ed769d54..5735c532f9687 100644 --- a/drivers/misc/bma150_spi.c +++ b/drivers/misc/bma150_spi.c @@ -259,8 +259,7 @@ static int spi_bma150_release(struct inode *inode, struct file *file) return 0; } -static int spi_bma150_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) +static int spi_bma150_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; char rwbuf[8]; diff --git a/drivers/usb/gadget/msm72k_udc_htc.c b/drivers/usb/gadget/msm72k_udc_htc.c index bcbf95e17acfa..5b7915f2353ce 100644 --- a/drivers/usb/gadget/msm72k_udc_htc.c +++ b/drivers/usb/gadget/msm72k_udc_htc.c @@ -2747,14 +2747,14 @@ static int msm72k_probe(struct platform_device *pdev) return 0; } -int usb_gadget_register_driver(struct usb_gadget_driver *driver) +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) { struct usb_info *ui = the_usb_info; int retval, n; if (!driver || driver->speed < USB_SPEED_FULL - || !driver->bind + || !bind || !driver->disconnect || !driver->setup) return -EINVAL; @@ -2787,7 +2787,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) if (retval) goto fail; - retval = driver->bind(&ui->gadget); + retval = bind(&ui->gadget); if (retval) { INFO("bind to driver %s --> error %d\n", driver->driver.name, retval); @@ -2811,7 +2811,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) ui->gadget.dev.driver = NULL; return retval; } -EXPORT_SYMBOL(usb_gadget_register_driver); +EXPORT_SYMBOL(usb_gadget_probe_driver); int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) { From 5288a651ccc17131a0c9dc82afd5e9faa1e0f90b Mon Sep 17 00:00:00 2001 From: toastcfh Date: Tue, 25 Jan 2011 20:55:57 -0500 Subject: [PATCH 1050/2556] revert: QDSP changes. this reverts qdsp changes in Change-Id: I2567917f40b5e467a93087bc3456e738d9535fdb --- arch/arm/mach-msm/dal.c | 32 +-- arch/arm/mach-msm/dal.h | 2 +- arch/arm/mach-msm/qdsp6/Makefile | 6 +- arch/arm/mach-msm/qdsp6/audio_ctl.c | 44 +++-- arch/arm/mach-msm/qdsp6/mp3.c | 2 +- arch/arm/mach-msm/qdsp6/msm_q6venc.c | 8 +- arch/arm/mach-msm/qdsp6/pcm_in.c | 2 +- arch/arm/mach-msm/qdsp6/pcm_out.c | 12 +- arch/arm/mach-msm/qdsp6/q6audio.c | 286 ++------------------------- 9 files changed, 75 insertions(+), 319 deletions(-) diff --git a/arch/arm/mach-msm/dal.c b/arch/arm/mach-msm/dal.c index 265ff5a1bddb2..971abbbcefc3a 100644 --- a/arch/arm/mach-msm/dal.c +++ b/arch/arm/mach-msm/dal.c @@ -568,27 +568,27 @@ int dal_call_f5(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t il } int dal_call_f9(struct dal_client *client, uint32_t ddi, void *obuf, - uint32_t olen) + uint32_t olen) { - uint32_t tmp[128]; - int res; + uint32_t tmp[128]; + int res; - if (olen > sizeof(tmp) - 8) - return -EINVAL; - tmp[0] = olen; + if (olen > sizeof(tmp) - 8) + return -EINVAL; + tmp[0] = olen; - res = dal_call(client, ddi, 9, tmp, sizeof(uint32_t), tmp, - sizeof(tmp)); + res = dal_call(client, ddi, 9, tmp, sizeof(uint32_t), tmp, + sizeof(tmp)); - if (res >= 4) - res = (int)tmp[0]; + if (res >= 4) + res = (int)tmp[0]; - if (!res) { - if (tmp[1] > olen) - return -EIO; - memcpy(obuf, &tmp[2], tmp[1]); - } - return res; + if (!res) { + if (tmp[1] > olen) + return -EIO; + memcpy(obuf, &tmp[2], tmp[1]); + } + return res; } int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1, diff --git a/arch/arm/mach-msm/dal.h b/arch/arm/mach-msm/dal.h index ea4b92a577026..6e4368295be31 100644 --- a/arch/arm/mach-msm/dal.h +++ b/arch/arm/mach-msm/dal.h @@ -51,7 +51,7 @@ int dal_call_f1(struct dal_client *client, uint32_t ddi, int dal_call_f5(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t ilen); int dal_call_f9(struct dal_client *client, uint32_t ddi, - void *obuf, uint32_t olen); + void *obuf, uint32_t olen); int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1, uint32_t ilen1, void *ibuf2, uint32_t ilen2, void *obuf, uint32_t olen); diff --git a/arch/arm/mach-msm/qdsp6/Makefile b/arch/arm/mach-msm/qdsp6/Makefile index b7eb950de8d7d..05cb351e7b45e 100644 --- a/arch/arm/mach-msm/qdsp6/Makefile +++ b/arch/arm/mach-msm/qdsp6/Makefile @@ -2,10 +2,8 @@ obj-y += q6audio.o obj-y += pcm_out.o obj-y += pcm_in.o obj-y += mp3.o -#obj-y += routing.o +obj-y += routing.o obj-y += audio_ctl.o obj-y += msm_q6vdec.o obj-y += msm_q6venc.o -#obj-y += aac_in.o -#obj-y += qcelp_in.o -obj-y += dsp_debug.o +obj-y += dsp_debug.o \ No newline at end of file diff --git a/arch/arm/mach-msm/qdsp6/audio_ctl.c b/arch/arm/mach-msm/qdsp6/audio_ctl.c index cc175b68c21e5..23d9eabbdd138 100644 --- a/arch/arm/mach-msm/qdsp6/audio_ctl.c +++ b/arch/arm/mach-msm/qdsp6/audio_ctl.c @@ -21,7 +21,6 @@ #include #include -#include #define BUFSZ (0) @@ -78,7 +77,7 @@ static int q6_voice_stop(void) return 0; } -static int q6_fm_start(void) +int q6_fm_start(void) { int rc = 0; @@ -103,7 +102,7 @@ static int q6_fm_start(void) return rc; } -static int q6_fm_stop(void) +int q6_fm_stop(void) { mutex_lock(&fm_lock); if (fm_started) { @@ -129,28 +128,40 @@ static long q6_ioctl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { case AUDIO_SWITCH_DEVICE: rc = copy_from_user(&id, (void *)arg, sizeof(id)); - if (!rc) + if (rc) { + pr_err("%s: bad user address\n", __func__); + rc = -EFAULT; + } else rc = q6audio_do_routing(id[0], id[1]); break; case AUDIO_SET_VOLUME: rc = copy_from_user(&n, (void *)arg, sizeof(n)); - if (!rc) + if (rc) { + pr_err("%s: bad user address\n", __func__); + rc = -EFAULT; + } else rc = q6audio_set_rx_volume(n); break; case AUDIO_SET_MUTE: rc = copy_from_user(&n, (void *)arg, sizeof(n)); - if (!rc) + if (rc) { + pr_err("%s: bad user address\n", __func__); + rc = -EFAULT; + } else rc = q6audio_set_tx_mute(n); break; case AUDIO_UPDATE_ACDB: rc = copy_from_user(&id, (void *)arg, sizeof(id)); - if (!rc) + if (rc) { + pr_err("%s: bad user address\n", __func__); + rc = -EFAULT; + } else rc = q6audio_update_acdb(id[0], id[1]); break; case AUDIO_START_VOICE: - if (arg == 0) { + if (arg == 0) id[0] = id[1] = 0; - } else if (copy_from_user(&id, (void*) arg, sizeof(id))) { + else if (copy_from_user(&id, (void *)arg, sizeof(id))) { pr_info("voice: copy acdb_id from user failed\n"); rc = -EFAULT; break; @@ -168,19 +179,14 @@ static long q6_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case AUDIO_REINIT_ACDB: rc = copy_from_user(&filename, (void *)arg, sizeof(filename)); - if (!rc) - rc = q6audio_reinit_acdb(filename); - break; - case AUDIO_ENABLE_AUXPGA_LOOPBACK: { - uint32_t enable; - if (copy_from_user(&enable, (void*) arg, sizeof(enable))) { + if (rc) { + pr_err("%s: bad user address\n", __func__); rc = -EFAULT; - break; - } - rc = enable_aux_loopback(enable); + } else + rc = q6audio_reinit_acdb(filename); break; - } default: + pr_info("%s: unknown %d\n", __func__, cmd); rc = -EINVAL; } diff --git a/arch/arm/mach-msm/qdsp6/mp3.c b/arch/arm/mach-msm/qdsp6/mp3.c index 6eb3301be8b36..759afb16182ba 100644 --- a/arch/arm/mach-msm/qdsp6/mp3.c +++ b/arch/arm/mach-msm/qdsp6/mp3.c @@ -19,9 +19,9 @@ #include #include #include +#include #include #include -#include #include diff --git a/arch/arm/mach-msm/qdsp6/msm_q6venc.c b/arch/arm/mach-msm/qdsp6/msm_q6venc.c index e8c93b4813da4..7b889767476fa 100644 --- a/arch/arm/mach-msm/qdsp6/msm_q6venc.c +++ b/arch/arm/mach-msm/qdsp6/msm_q6venc.c @@ -162,7 +162,6 @@ struct venc_pmem_list { struct venc_dev { bool is_active; bool pmem_freed; - bool stop_called; enum venc_state_type state; struct list_head venc_msg_list_head; struct list_head venc_msg_list_free; @@ -1165,18 +1164,17 @@ static int q6venc_release(struct inode *inode, struct file *file) list_del(&l->list); kfree(l); } - list_for_each_entry_safe(l, n, &dvenc->venc_msg_list_head, list) { list_del(&l->list); kfree(l); } - spin_lock_irqsave(&dvenc->venc_pmem_list_lock, flags); - if (!dvenc->stop_called) { + if (!dvenc->pmem_freed) { list_for_each_entry(plist, &dvenc->venc_pmem_list_head, list) put_pmem_file(plist->buf.file); - dvenc->stop_called = 1; + dvenc->pmem_freed = 1; } + spin_unlock_irqrestore(&dvenc->venc_pmem_list_lock, flags); list_for_each_entry_safe(plist, m, &dvenc->venc_pmem_list_head, list) { list_del(&plist->list); diff --git a/arch/arm/mach-msm/qdsp6/pcm_in.c b/arch/arm/mach-msm/qdsp6/pcm_in.c index 3b3d23bfd7a9a..d5a8f7716eced 100644 --- a/arch/arm/mach-msm/qdsp6/pcm_in.c +++ b/arch/arm/mach-msm/qdsp6/pcm_in.c @@ -160,7 +160,7 @@ static ssize_t q6_in_read(struct file *file, char __user *buf, if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) { audio_client_dump(ac); pr_err("pcm_read: timeout. dsp dead?\n"); - BUG(); + q6audio_dsp_not_responding(); } xfer = count; diff --git a/arch/arm/mach-msm/qdsp6/pcm_out.c b/arch/arm/mach-msm/qdsp6/pcm_out.c index 16a1e9c70ec09..6d041d8c686ae 100644 --- a/arch/arm/mach-msm/qdsp6/pcm_out.c +++ b/arch/arm/mach-msm/qdsp6/pcm_out.c @@ -19,9 +19,9 @@ #include #include #include +#include #include #include -#include #include @@ -56,6 +56,12 @@ static long pcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { case AUDIO_SET_VOLUME: { int vol; + if (!pcm->ac) { + pr_err("%s: cannot set volume before AUDIO_START!\n", + __func__); + rc = -EINVAL; + break; + } if (copy_from_user(&vol, (void*) arg, sizeof(vol))) { rc = -EFAULT; break; @@ -177,14 +183,14 @@ static ssize_t pcm_write(struct file *file, const char __user *buf, if (!wait_event_timeout(ac->wait, (ab->used == 0), 5*HZ)) { audio_client_dump(ac); pr_err("pcm_write: timeout. dsp dead?\n"); - BUG(); + q6audio_dsp_not_responding(); } xfer = count; if (xfer > ab->size) xfer = ab->size; - if (copy_from_user(ab->data, buf, xfer)) + if (copy_from_user(ab->data, buf, xfer)) return -EFAULT; buf += xfer; diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c index b865ab67f6c89..352c94011b7e5 100644 --- a/arch/arm/mach-msm/qdsp6/q6audio.c +++ b/arch/arm/mach-msm/qdsp6/q6audio.c @@ -16,10 +16,10 @@ #include #include +#include #include #include #include -#include #include #include @@ -33,7 +33,7 @@ #include "dal_adie.h" #include #include -#include + #include #include "q6audio_devices.h" @@ -178,12 +178,12 @@ int q6_device_volume(uint32_t device_id, int level) } } -static inline int adie_open(struct dal_client *client) +static inline int adie_open(struct dal_client *client) { return dal_call_f0(client, DAL_OP_OPEN, 0); } -static inline int adie_close(struct dal_client *client) +static inline int adie_close(struct dal_client *client) { return dal_call_f0(client, DAL_OP_CLOSE, 0); } @@ -349,7 +349,7 @@ static int audio_ioctl(struct audio_client *ac, void *ptr, uint32_t len) if (!wait_event_timeout(ac->wait, (ac->cb_status != -EBUSY), 5*HZ)) { dal_trace_dump(ac->client); pr_err("audio_ioctl: timeout. dsp dead?\n"); - BUG(); + q6audio_dsp_not_responding(); } return ac->cb_status; } @@ -439,156 +439,6 @@ static int audio_mp3_open(struct audio_client *ac, uint32_t bufsz, return audio_ioctl(ac, &rpc, sizeof(rpc)); } -static int audio_aac_open(struct audio_client *ac, uint32_t bufsz, - void *data) -{ - struct aac_format *af = data; - struct adsp_open_command rpc; - uint32_t *aac_type; - int idx = sizeof(uint32_t); - struct adsp_audio_binary_format *fmt = &(rpc.format.binary); - - memset(&rpc, 0, sizeof(rpc)); - - fmt->format = ADSP_AUDIO_FORMAT_MPEG4_AAC; - aac_type = (uint32_t *)(fmt->data); - switch (af->block_formats) { - case 0xffff: - if (ac->flags & AUDIO_FLAG_WRITE) - *aac_type = ADSP_AUDIO_AAC_ADTS; - else - *aac_type = ADSP_AUDIO_AAC_MPEG4_ADTS; - break; - case 0: - if (ac->flags & AUDIO_FLAG_WRITE) - *aac_type = ADSP_AUDIO_AAC_ADIF; - else - *aac_type = ADSP_AUDIO_AAC_RAW; - break; - case 1: - *aac_type = ADSP_AUDIO_AAC_RAW; - break; - case 2: - *aac_type = ADSP_AUDIO_AAC_LOAS; - break; - case 3: - *aac_type = ADSP_AUDIO_AAC_FRAMED_RAW; - break; - case 4: - *aac_type = ADSP_AUDIO_AAC_RAW; - break; - default: - pr_err("unsupported AAC type %d\n", af->block_formats); - return -EINVAL; - } - - TRACE("aac_open: type %x, obj %d, idx %d\n", - *aac_type, af->audio_object_type, idx); - fmt->data[idx++] = (u8)(((af->audio_object_type & 0x1F) << 3) | - ((af->sample_rate >> 1) & 0x7)); - fmt->data[idx] = (u8)(((af->sample_rate & 0x1) << 7) | - ((af->channel_config & 0x7) << 3)); - - switch (af->audio_object_type) { - case AAC_OBJECT_ER_LC: - case AAC_OBJECT_ER_LTP: - case AAC_OBJECT_ER_LD: - /* extension flag */ - fmt->data[idx++] |= 0x1; - fmt->data[idx] = (u8)( - ((af->aac_section_data_resilience_flag & 0x1) << 7) | - ((af->aac_scalefactor_data_resilience_flag & 0x1) << 6) | - ((af->aac_spectral_data_resilience_flag & 0x1) << 5) | - ((af->ep_config & 0x3) << 2)); - break; - - case AAC_OBJECT_ER_SCALABLE: - fmt->data[idx++] |= 0x1; - /* extension flag */ - fmt->data[idx++] = (u8)( - ((af->aac_section_data_resilience_flag & 0x1) << 4) | - ((af->aac_scalefactor_data_resilience_flag & 0x1) << 3) | - ((af->aac_spectral_data_resilience_flag & 0x1) << 2) | - ((af->ep_config >> 1) & 0x1)); - fmt->data[idx] = (u8)((af->ep_config & 0x1) - << 7); - break; - - case AAC_OBJECT_BSAC: - fmt->data[++idx] = (u8)((af->ep_config & 0x3) - << 6); - break; - - default: - pr_err("dbg unknown object type \n"); - break; - } - fmt->num_bytes = idx + 1; - - TRACE("aac_open: format %x%x %x%x%x%x %x%x, \n", - fmt->data[0], fmt->data[1], fmt->data[2], fmt->data[3], - fmt->data[4], fmt->data[5], fmt->data[6], fmt->data[7]); - - rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; - rpc.config.aac.bit_rate = af->bit_rate; - if (ac->flags & AUDIO_FLAG_WRITE) { - rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_WRITE; - rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK; - } else { - rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; - rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; - } - - if ((af->sbr_on_flag == 0) && (af->sbr_ps_on_flag == 0)) { - rpc.config.aac.encoder_mode = - ADSP_AUDIO_ENC_AAC_LC_ONLY_MODE; - } else if ((af->sbr_on_flag == 1) && (af->sbr_ps_on_flag == 0)) { - rpc.config.aac.encoder_mode = - ADSP_AUDIO_ENC_AAC_PLUS_MODE; - } else if ((af->sbr_on_flag == 1) && (af->sbr_ps_on_flag == 1)) { - rpc.config.aac.encoder_mode = - ADSP_AUDIO_ENC_ENHANCED_AAC_PLUS_MODE; - } else { - pr_err("unsupported SBR flag\n"); - return -EINVAL; - } - rpc.buf_max_size = bufsz; /* XXX ??? */ - rpc.hdr.response_type = 0; - - TRACE("aac_open: opcode %x, stream_context 0x%x, " - "mode %d, bytes %d, bbuffer size %d\n", - rpc.hdr.opcode, rpc.stream_context, - rpc.config.aac.encoder_mode, fmt->num_bytes, bufsz); - - return audio_ioctl(ac, &rpc, sizeof(rpc)); -} - -static int audio_qcelp_open(struct audio_client *ac, uint32_t bufsz, - void *data) -{ - struct msm_audio_qcelp_config *qf = data; - struct adsp_open_command rpc; - struct adsp_audio_standard_format *fmt = &(rpc.format.standard); - - memset(&rpc, 0, sizeof(rpc)); - - fmt->format = ADSP_AUDIO_FORMAT_V13K_FS; - fmt->sampling_rate = 8000; - fmt->channels = 1; - fmt->bits_per_sample = 16; - fmt->is_signed = 1; - fmt->is_interleaved = 0; - - rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; - rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; - rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; - rpc.config.qcelp13k.min_rate = (uint16_t) qf->min_bit_rate; - rpc.config.qcelp13k.max_rate = (uint16_t) qf->max_bit_rate; - rpc.buf_max_size = bufsz; /* XXX ??? */ - - return audio_ioctl(ac, &rpc, sizeof(rpc)); -} - static int audio_close(struct audio_client *ac) { TRACE("%p: close\n", ac); @@ -735,10 +585,7 @@ static void callback(void *data, int len, void *cookie) { struct adsp_event_hdr *e = data; struct audio_client *ac; - struct adsp_buffer_event *abe = data; - TRACE("audio callback: context %d, event 0x%x, status %d\n", - e->context, e->event_id, e->status); if (e->context >= SESSION_MAX) { pr_err("audio callback: bogus session %d\n", @@ -765,9 +612,6 @@ static void callback(void *data, int len, void *cookie) if (e->event_id == ADSP_AUDIO_EVT_STATUS_BUF_DONE) { TRACE("%p: CB done (%d)\n", ac, e->status); - TRACE("%p: actual_size %d, buffer_size %d\n", - ac, abe->buffer.actual_size, ac->buf[ac->dsp_buf].size); - if (e->status) pr_err("buffer status %d\n", e->status); ac->buf[ac->dsp_buf].used = 0; @@ -1049,10 +893,6 @@ static void audio_rx_analog_enable(int en) if (analog_ops->receiver_enable) analog_ops->receiver_enable(en); break; - case ADSP_AUDIO_DEVICE_ID_I2S_SPKR: - if (analog_ops->i2s_enable) - analog_ops->i2s_enable(en); - break; } } @@ -1098,8 +938,7 @@ static int audio_update_acdb(uint32_t adev, uint32_t acdb_id) return -EINVAL; } - if (sz > 0) - audio_set_table(ac_control, adev, sz); + audio_set_table(ac_control, adev, sz); return 0; } @@ -1375,7 +1214,7 @@ int q6audio_reinit_acdb(char* filename) { return 0; mutex_lock(&audio_path_lock); - if (strlen(filename) < 0) { + if (strlen(filename) < 0 || !strcmp(filename, acdb_file)) { res = -EINVAL; goto done; } @@ -1415,7 +1254,6 @@ int q6audio_update_acdb(uint32_t id_src, uint32_t id_dst) int q6audio_set_tx_mute(int mute) { uint32_t adev; - int rc; if (q6audio_init()) return 0; @@ -1428,8 +1266,8 @@ int q6audio_set_tx_mute(int mute) } adev = audio_tx_device_id; - rc = audio_tx_mute(ac_control, adev, mute); - if (!rc) tx_mute_status = mute; + audio_tx_mute(ac_control, adev, mute); + tx_mute_status = mute; mutex_unlock(&audio_path_lock); return 0; } @@ -1621,7 +1459,7 @@ struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, if (rc == 0) break; if (retry == 0) - BUG(); + q6audio_dsp_not_responding(); pr_err("q6audio: open pcm error %d, retrying\n", rc); msleep(1); } @@ -1646,7 +1484,7 @@ struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, if (rc == 0) break; if (retry == 0) - BUG(); + q6audio_dsp_not_responding(); pr_err("q6audio: stream start error %d, retrying\n", rc); } @@ -1686,10 +1524,9 @@ struct audio_client *q6voice_open(uint32_t flags, uint32_t acdb_id) return 0; ac->flags = flags; - if (ac->flags & AUDIO_FLAG_WRITE) { + if (ac->flags & AUDIO_FLAG_WRITE) audio_rx_path_enable(1, acdb_id); - audio_rx_mute(ac_control, ADSP_AUDIO_DEVICE_ID_VOICE, 0); - } else { + else { tx_clk_freq = 8000; audio_tx_path_enable(1, acdb_id); } @@ -1699,10 +1536,9 @@ struct audio_client *q6voice_open(uint32_t flags, uint32_t acdb_id) int q6voice_close(struct audio_client *ac) { - if (ac->flags & AUDIO_FLAG_WRITE) { - audio_rx_mute(ac_control, ADSP_AUDIO_DEVICE_ID_VOICE, 1); + if (ac->flags & AUDIO_FLAG_WRITE) audio_rx_path_enable(0, 0); - } else + else audio_tx_path_enable(0, 0); audio_client_free(ac); @@ -1749,68 +1585,17 @@ int q6audio_async(struct audio_client *ac) return audio_ioctl(ac, &rpc, sizeof(rpc)); } -struct audio_client *q6audio_open_aac(uint32_t bufsz, uint32_t rate, - uint32_t flags, void *data, uint32_t acdb_id) -{ - struct audio_client *ac; - - TRACE("q6audio_open_aac flags=%d rate=%d\n", flags, rate); - - if (q6audio_init()) - return 0; - - ac = audio_client_alloc(bufsz); - if (!ac) - return 0; - - ac->flags = flags; - if (ac->flags & AUDIO_FLAG_WRITE) - audio_rx_path_enable(1, acdb_id); - else { - /* TODO: consider concourrency with voice call */ - tx_clk_freq = rate; - audio_tx_path_enable(1, acdb_id); - } - - audio_aac_open(ac, bufsz, data); - audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); - - if (!(ac->flags & AUDIO_FLAG_WRITE)) { - ac->buf[0].used = 1; - ac->buf[1].used = 1; - q6audio_read(ac, &ac->buf[0]); - q6audio_read(ac, &ac->buf[1]); - } - audio_prevent_sleep(); - return ac; -} - -int q6audio_aac_close(struct audio_client *ac) -{ - audio_close(ac); - if (ac->flags & AUDIO_FLAG_WRITE) - audio_rx_path_enable(0, 0); - else - audio_tx_path_enable(0, 0); - - audio_client_free(ac); - audio_allow_sleep(); - return 0; -} - struct audio_client *q6fm_open(void) { struct audio_client *ac; - printk("q6fm_open()\n"); - if (q6audio_init()) return 0; - if (audio_rx_device_id != ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO && +/* if (audio_rx_device_id != ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO && audio_rx_device_id != ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO) return 0; - +*/ ac = audio_client_alloc(0); if (!ac) return 0; @@ -1830,40 +1615,3 @@ int q6fm_close(struct audio_client *ac) return 0; } -struct audio_client *q6audio_open_qcelp(uint32_t bufsz, uint32_t rate, - void *data, uint32_t acdb_id) -{ - struct audio_client *ac; - - if (q6audio_init()) - return 0; - - ac = audio_client_alloc(bufsz); - if (!ac) - return 0; - - ac->flags = AUDIO_FLAG_READ; - tx_clk_freq = rate; - audio_tx_path_enable(1, acdb_id); - - audio_qcelp_open(ac, bufsz, data); - audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); - - ac->buf[0].used = 1; - ac->buf[1].used = 1; - q6audio_read(ac, &ac->buf[0]); - q6audio_read(ac, &ac->buf[1]); - - audio_prevent_sleep(); - return ac; -} - -int q6audio_qcelp_close(struct audio_client *ac) -{ - audio_close(ac); - audio_tx_path_enable(0, 0); - audio_client_free(ac); - audio_allow_sleep(); - return 0; -} - From 2bca71b76482551cb349512fdbd2df8c9000fd67 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Wed, 26 Jan 2011 20:27:56 -0500 Subject: [PATCH 1051/2556] msm: supersonic: wlan: Fix BT-coex in A2DP mode --- arch/arm/mach-msm/board-supersonic-wifi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/board-supersonic-wifi.c b/arch/arm/mach-msm/board-supersonic-wifi.c index 21801a708fad1..56936b0450b85 100644 --- a/arch/arm/mach-msm/board-supersonic-wifi.c +++ b/arch/arm/mach-msm/board-supersonic-wifi.c @@ -132,6 +132,7 @@ static int __init supersonic_wifi_init(void) printk("%s: start\n", __func__); supersonic_wifi_update_nvs("sd_oobonly=1\r\n"); + supersonic_wifi_update_nvs("btc_params80=0\n", 1); supersonic_init_wifi_mem(); ret = platform_device_register(&supersonic_wifi_device); return ret; From 8a69cf5baf3b7ffc10881cd1abf7da89fa4a66b7 Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 26 Jan 2011 21:39:46 -0500 Subject: [PATCH 1052/2556] drivers: misc: bma150_spi: add mutex Change-Id: I6f206204aecf720b6bb71aa47ae31e7f5dabf60e Signed-off-by: Dima Zavin --- drivers/misc/bma150_spi.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/misc/bma150_spi.c b/drivers/misc/bma150_spi.c index 5735c532f9687..fe2a1e8ad19e2 100644 --- a/drivers/misc/bma150_spi.c +++ b/drivers/misc/bma150_spi.c @@ -248,6 +248,7 @@ static int __spi_bma150_set_mode(char mode) return ret; } +static DEFINE_MUTEX(bma150_lock); static int spi_bma150_open(struct inode *inode, struct file *file) { @@ -259,7 +260,7 @@ static int spi_bma150_release(struct inode *inode, struct file *file) return 0; } -static int spi_bma150_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static long spi_bma150_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; char rwbuf[8]; @@ -282,31 +283,36 @@ static int spi_bma150_ioctl(struct file *file, unsigned int cmd, unsigned long a break; } + mutex_lock(&bma150_lock); switch (cmd) { case BMA_IOCTL_INIT: ret = spi_gsensor_init_hw(); if (ret < 0) - return ret; + goto err; break; case BMA_IOCTL_READ: - if (rwbuf[0] < 1) - return -EINVAL; + if (rwbuf[0] < 1) { + ret = -EINVAL; + goto err; + } ret = spi_gsensor_read(&rwbuf[1]); if (ret < 0) - return ret; + goto err; break; case BMA_IOCTL_WRITE: - if (rwbuf[0] < 2) - return -EINVAL; + if (rwbuf[0] < 2) { + ret = -EINVAL; + goto err; + } ret = spi_gsensor_write(&rwbuf[1]); if (ret < 0) - return ret; + goto err; break; case BMA_IOCTL_READ_ACCELERATION: ret = spi_bma150_TransRBuff(&buf[0]); if (ret < 0) - return ret; + goto err; break; case BMA_IOCTL_SET_MODE: /*printk(KERN_DEBUG @@ -324,8 +330,10 @@ static int spi_bma150_ioctl(struct file *file, unsigned int cmd, unsigned long a temp = this_pdata->chip_layout; break; default: - return -ENOTTY; + ret = -ENOTTY; + goto err; } + mutex_unlock(&bma150_lock); switch (cmd) { case BMA_IOCTL_READ: @@ -350,6 +358,9 @@ static int spi_bma150_ioctl(struct file *file, unsigned int cmd, unsigned long a } return 0; +err: + mutex_unlock(&bma150_lock); + return ret; } static struct file_operations spi_bma_fops = { From 3278f2f7547b80c0374e883d7581c629d54022cd Mon Sep 17 00:00:00 2001 From: toastcfh Date: Wed, 26 Jan 2011 21:47:45 -0500 Subject: [PATCH 1053/2556] msm: supersonic: Set built-in flag for wimax and wlan devices --- arch/arm/mach-msm/board-supersonic-mmc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-msm/board-supersonic-mmc.c b/arch/arm/mach-msm/board-supersonic-mmc.c index fb58d9a5b100b..0c2b1d196c93c 100644 --- a/arch/arm/mach-msm/board-supersonic-mmc.c +++ b/arch/arm/mach-msm/board-supersonic-mmc.c @@ -204,6 +204,7 @@ static unsigned int supersonic_wifi_status(struct device *dev) static struct mmc_platform_data supersonic_wifi_data = { .ocr_mask = MMC_VDD_28_29, + .built_in = 1, .status = supersonic_wifi_status, .register_status_notify = supersonic_wifi_status_register, .embedded_sdio = &supersonic_wifi_emb_data, @@ -318,6 +319,7 @@ EXPORT_SYMBOL(supersonic_wimax_set_carddetect); static struct mmc_platform_data supersonic_wimax_data = { .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30, + .built_in = 1, .status = supersonic_wimax_status, .register_status_notify = supersonic_wimax_status_register, .embedded_sdio = NULL, From 0fa2e24b09d6b93717466a65791767391443b468 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Wed, 26 Jan 2011 21:51:44 -0500 Subject: [PATCH 1054/2556] msm: supersonic: add flag option to supersonic_wifi_update_nvs --- arch/arm/mach-msm/board-supersonic-wifi.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-msm/board-supersonic-wifi.c b/arch/arm/mach-msm/board-supersonic-wifi.c index 56936b0450b85..b2a5e283c5d74 100644 --- a/arch/arm/mach-msm/board-supersonic-wifi.c +++ b/arch/arm/mach-msm/board-supersonic-wifi.c @@ -99,8 +99,9 @@ static struct platform_device supersonic_wifi_device = { }; extern unsigned char *get_wifi_nvs_ram(void); +extern int wifi_calibration_size_set(void); -static unsigned supersonic_wifi_update_nvs(char *str) +static unsigned supersonic_wifi_update_nvs(char *str, int add_flag) { #define NVS_LEN_OFFSET 0x0C #define NVS_DATA_OFFSET 0x40 @@ -117,9 +118,15 @@ static unsigned supersonic_wifi_update_nvs(char *str) if (ptr[NVS_DATA_OFFSET + len -1] == 0) len -= 1; - strcpy(ptr + NVS_DATA_OFFSET + len, str); - len += strlen(str); + if (add_flag) { + strcpy(ptr + NVS_DATA_OFFSET + len, str); + len += strlen(str); + } else { + if (strnstr(ptr + NVS_DATA_OFFSET, str, len)) + len -= strlen(str); + } memcpy(ptr + NVS_LEN_OFFSET, &len, sizeof(len)); + wifi_calibration_size_set(); return 0; } @@ -131,7 +138,7 @@ static int __init supersonic_wifi_init(void) return 0; printk("%s: start\n", __func__); - supersonic_wifi_update_nvs("sd_oobonly=1\r\n"); + supersonic_wifi_update_nvs("sd_oobonly=1\r\n", 0); supersonic_wifi_update_nvs("btc_params80=0\n", 1); supersonic_init_wifi_mem(); ret = platform_device_register(&supersonic_wifi_device); From 80438a178b775f5f4d651bd0412d15d540c03e9b Mon Sep 17 00:00:00 2001 From: toastcfh Date: Thu, 31 Mar 2011 22:14:41 -0400 Subject: [PATCH 1055/2556] basic build env --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2372fed455d86..f44476fed535e 100644 --- a/Makefile +++ b/Makefile @@ -167,6 +167,8 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ -e s/sh[234].*/sh/ ) +SUBARCH := arm + # Cross compiling and selecting different set of gcc/bin-utils # --------------------------------------------------------------------------- # @@ -189,7 +191,7 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ # Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile export KBUILD_BUILDHOST := $(SUBARCH) ARCH ?= $(SUBARCH) -CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%) +CROSS_COMPILE ?= arm-eabi- # Architecture as present in compile.h UTS_MACHINE := $(ARCH) From bdc728a17b271afc3809049033cfcc7fd285f950 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sat, 29 Jan 2011 20:23:57 -0500 Subject: [PATCH 1056/2556] supersonic: minor changes after rebase --- arch/arm/mach-msm/Makefile | 1 - arch/arm/mach-msm/board-supersonic.c | 2 +- drivers/video/msm/mdp_lcdc.c | 66 +++------------------------- 3 files changed, 6 insertions(+), 63 deletions(-) diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index e907f0318bd2e..b2f777b38d1e6 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -101,7 +101,6 @@ obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-tpa2018d1.o obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-tpa6130.o obj-$(CONFIG_MACH_SUPERSONIC) += htc_wifi_nvs.o htc_bluetooth.o obj-$(CONFIG_MACH_SUPERSONIC) += htc_acoustic_qsd.o -obj-$(CONFIG_MACH_SUPERSONIC) += devices_htc.o obj-$(CONFIG_HTC_PWRSINK) += htc_pwrsink.o obj-$(CONFIG_HTC_PWRSPLY) += htc_power_supply.o diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index 13bc8c85c3e43..1a96e455ced86 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -15,7 +15,7 @@ * */ -#include +//#include #include #include #include diff --git a/drivers/video/msm/mdp_lcdc.c b/drivers/video/msm/mdp_lcdc.c index 210186fe5bc60..b99dceba8822b 100644 --- a/drivers/video/msm/mdp_lcdc.c +++ b/drivers/video/msm/mdp_lcdc.c @@ -48,37 +48,6 @@ #define LCDC_MUX_CTL (MSM_TGPIO1_BASE + 0x278) #endif -<<<<<<< HEAD -struct mdp_lcdc_info { - struct mdp_info *mdp; - struct clk *mdp_clk; - struct clk *mdp_pclk; - struct clk *pclk; - struct clk *pad_pclk; - struct msm_panel_data fb_panel_data; - struct platform_device fb_pdev; - struct msm_lcdc_platform_data *pdata; - uint32_t fb_start; - - struct msmfb_callback frame_start_cb; - wait_queue_head_t vsync_waitq; - int got_vsync; - - struct { - uint32_t clk_rate; - uint32_t hsync_ctl; - uint32_t vsync_period; - uint32_t vsync_pulse_width; - uint32_t display_hctl; - uint32_t display_vstart; - uint32_t display_vend; - uint32_t hsync_skew; - uint32_t polarity; - } parms; -}; -======= ->>>>>>> video: msm: Updated MDDI from HTC - static struct mdp_device *mdp_dev; #ifdef CONFIG_MSM_MDP40 @@ -377,13 +346,10 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_START); mdp_writel(lcdc->mdp, 0, MDP_LCDC_ACTIVE_V_END); mdp_writel(lcdc->mdp, lcdc->parms.polarity, MDP_LCDC_CTL_POLARITY); -<<<<<<< HEAD fb_size = ((fb_panel->fb_data->yres & 0x7ff) << 16) | (fb_panel->fb_data->xres & 0x7ff); -======= ->>>>>>> video: msm: Updated MDDI from HTC /* config the dma_p block that drives the lcdc data */ mdp_writel(lcdc->mdp, lcdc->fb_start, MDP_DMA_P_IBUF_ADDR); mdp_writel(lcdc->mdp, fb_size, MDP_DMA_P_SIZE); @@ -396,7 +362,6 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) #endif dma_cfg = mdp_readl(lcdc->mdp, MDP_DMA_P_CONFIG); -<<<<<<< HEAD dma_cfg &= ~(DMA_PACK_PATTERN_MASK | DMA_PACK_ALIGN_MASK); dma_cfg |= (DMA_PACK_ALIGN_MSB | DMA_PACK_PATTERN_RGB | @@ -416,23 +381,6 @@ static int lcdc_hw_init(struct mdp_lcdc_info *lcdc) dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; -======= - if (lcdc->mdp->mdp_dev.overrides & MSM_MDP_DMA_PACK_ALIGN_LSB) - dma_cfg &= ~DMA_PACK_ALIGN_MSB; - else - dma_cfg |= DMA_PACK_ALIGN_MSB; - - dma_cfg |= (DMA_PACK_PATTERN_RGB | - DMA_DITHER_EN); - dma_cfg |= DMA_OUT_SEL_LCDC; - dma_cfg &= ~DMA_DST_BITS_MASK; - if(lcdc->color_format == MSM_MDP_OUT_IF_FMT_RGB565) - dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS; - else if (lcdc->color_format == MSM_MDP_OUT_IF_FMT_RGB666) - dma_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS; - else if (lcdc->color_format == MSM_MDP_OUT_IF_FMT_RGB888) - dma_cfg |= DMA_DSTC0G_8BITS | DMA_DSTC1B_8BITS | DMA_DSTC2R_8BITS; ->>>>>>> video: msm: Updated MDDI from HTC mdp_writel(lcdc->mdp, dma_cfg, MDP_DMA_P_CONFIG); @@ -531,17 +479,13 @@ static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride, uint32_t y) { struct mdp_lcdc_info *lcdc = priv; -<<<<<<< HEAD + struct mdp_info *mdp = lcdc->mdp; uint32_t dma2_cfg; #ifdef CONFIG_MSM_MDP31 - if (lcdc->mdp->dma_format_dirty) { -======= - struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev); - if (mdp->dma_config_dirty) - { ->>>>>>> video: msm: Updated MDDI from HTC + if (lcdc->mdp->dma_config_dirty) { + mdp_writel(lcdc->mdp, 0, MDP_LCDC_EN); mdelay(30); mdp_dev->configure_dma(mdp_dev); @@ -551,7 +495,7 @@ static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride, mdp_writel(lcdc->mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE); mdp_writel(lcdc->mdp, addr, MDP_DMA_P_IBUF_ADDR); #else - if (lcdc->mdp->dma_format_dirty) { + if (lcdc->mdp->dma_config_dirty) { uint32_t fmt; uint32_t dma_fmt; uint32_t dma_ptrn; @@ -583,7 +527,7 @@ static void lcdc_dma_start(void *priv, uint32_t addr, uint32_t stride, DMA_PACK_ALIGN_MASK); dma2_cfg |= dma_ptrn | dma_fmt | DMA_PACK_ALIGN_MSB; mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG); - lcdc->mdp->dma_format_dirty = false; + lcdc->mdp->dma_config_dirty = false; } mdp_writel(mdp, addr, MDP_PIPE_RGB_SRC_ADDR(0)); From 8d3ec479039f01870e8f6e0de171cbfcdcc35a16 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Tue, 25 Jan 2011 12:41:53 -0500 Subject: [PATCH 1057/2556] supersonic: updates for 2.6.37. includes changed along wit a few functions. just updateing the drivers and board files needed for the supersonic. --- arch/arm/mach-msm/atmega_microp_common.c | 1 + arch/arm/mach-msm/board-supersonic-mmc.c | 15 +++++---------- arch/arm/mach-msm/board-supersonic-rfkill.c | 3 +-- arch/arm/mach-msm/board-supersonic-tpa2018d1.c | 2 +- arch/arm/mach-msm/board-supersonic.c | 4 ++-- arch/arm/mach-msm/htc_headset_microp.c | 1 + drivers/misc/akm8973_htc.c | 4 ++-- drivers/misc/bma150_spi.c | 2 +- 8 files changed, 14 insertions(+), 18 deletions(-) diff --git a/arch/arm/mach-msm/atmega_microp_common.c b/arch/arm/mach-msm/atmega_microp_common.c index fa788eb17e094..21b50ea9759cf 100644 --- a/arch/arm/mach-msm/atmega_microp_common.c +++ b/arch/arm/mach-msm/atmega_microp_common.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/mach-msm/board-supersonic-mmc.c b/arch/arm/mach-msm/board-supersonic-mmc.c index 0c2b1d196c93c..348c8d9869511 100644 --- a/arch/arm/mach-msm/board-supersonic-mmc.c +++ b/arch/arm/mach-msm/board-supersonic-mmc.c @@ -21,11 +21,11 @@ #include #include #include -#include +#include #include #include -#include +#include #include @@ -133,18 +133,13 @@ static unsigned int supersonic_sdslot_status(struct device *dev) static unsigned int supersonic_sdslot_type = MMC_TYPE_SD; -static struct mmc_platform_data supersonic_sdslot_data = { +static struct msm_mmc_platform_data supersonic_sdslot_data = { .ocr_mask = SUPERSONIC_MMC_VDD, .status = supersonic_sdslot_status, .translate_vdd = supersonic_sdslot_switchvdd, // .slot_type = &supersonic_sdslot_type, }; -int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, - unsigned int stat_irq, unsigned long stat_irq_flags); - - - /* ---- WIFI ---- */ static uint32_t wifi_on_gpio_table[] = { @@ -202,7 +197,7 @@ static unsigned int supersonic_wifi_status(struct device *dev) return supersonic_wifi_cd; } -static struct mmc_platform_data supersonic_wifi_data = { +static struct msm_mmc_platform_data supersonic_wifi_data = { .ocr_mask = MMC_VDD_28_29, .built_in = 1, .status = supersonic_wifi_status, @@ -317,7 +312,7 @@ void supersonic_wimax_set_carddetect(int val) } EXPORT_SYMBOL(supersonic_wimax_set_carddetect); -static struct mmc_platform_data supersonic_wimax_data = { +static struct msm_mmc_platform_data supersonic_wimax_data = { .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30, .built_in = 1, .status = supersonic_wimax_status, diff --git a/arch/arm/mach-msm/board-supersonic-rfkill.c b/arch/arm/mach-msm/board-supersonic-rfkill.c index 3100c2405bbc8..8236350f862de 100644 --- a/arch/arm/mach-msm/board-supersonic-rfkill.c +++ b/arch/arm/mach-msm/board-supersonic-rfkill.c @@ -20,10 +20,9 @@ #include #include #include -#include +#include #include -#include "gpio_chip.h" #include "proc_comm.h" #include "board-supersonic.h" diff --git a/arch/arm/mach-msm/board-supersonic-tpa2018d1.c b/arch/arm/mach-msm/board-supersonic-tpa2018d1.c index 4cb4c83029be1..a2b6f3074f93f 100644 --- a/arch/arm/mach-msm/board-supersonic-tpa2018d1.c +++ b/arch/arm/mach-msm/board-supersonic-tpa2018d1.c @@ -247,7 +247,7 @@ static struct file_operations tpa2018d1_fops = { .owner = THIS_MODULE, .open = tpa2018d1_open, .release = tpa2018d1_release, - .ioctl = tpa2018d1_ioctl, + .unlocked_ioctl = tpa2018d1_ioctl, }; static struct miscdevice tpa2018d1_device = { diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index 1a96e455ced86..935775150548e 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -1602,13 +1602,13 @@ static void __init supersonic_fixup(struct machine_desc *desc, struct tag *tags, /* First Bank 256MB */ mi->nr_banks = 1; mi->bank[0].start = PHYS_OFFSET; - mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); +// mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); mi->bank[0].size = MSM_EBI1_BANK0_SIZE; /*(219*1024*1024);*/ /* Second Bank 128MB */ mi->nr_banks++; mi->bank[1].start = MSM_EBI1_BANK1_BASE; - mi->bank[1].node = PHYS_TO_NID(MSM_EBI1_BANK1_BASE); +// mi->bank[1].node = PHYS_TO_NID(MSM_EBI1_BANK1_BASE); mi->bank[1].size = MSM_EBI1_BANK1_SIZE; } diff --git a/arch/arm/mach-msm/htc_headset_microp.c b/arch/arm/mach-msm/htc_headset_microp.c index 2a23568cb04b2..363d256546709 100644 --- a/arch/arm/mach-msm/htc_headset_microp.c +++ b/arch/arm/mach-msm/htc_headset_microp.c @@ -20,6 +20,7 @@ #include #include #include +#include #include diff --git a/drivers/misc/akm8973_htc.c b/drivers/misc/akm8973_htc.c index 901cb99da65d8..fe86f97b91d12 100644 --- a/drivers/misc/akm8973_htc.c +++ b/drivers/misc/akm8973_htc.c @@ -573,14 +573,14 @@ static struct file_operations akmd_fops = { .owner = THIS_MODULE, .open = akmd_open, .release = akmd_release, - .ioctl = akmd_ioctl, + .unlocked_ioctl = akmd_ioctl, }; static struct file_operations akm_aot_fops = { .owner = THIS_MODULE, .open = akm_aot_open, .release = akm_aot_release, - .ioctl = akm_aot_ioctl, + .unlocked_ioctl = akm_aot_ioctl, }; diff --git a/drivers/misc/bma150_spi.c b/drivers/misc/bma150_spi.c index fe2a1e8ad19e2..f00a3c9cb5f39 100644 --- a/drivers/misc/bma150_spi.c +++ b/drivers/misc/bma150_spi.c @@ -367,7 +367,7 @@ static struct file_operations spi_bma_fops = { .owner = THIS_MODULE, .open = spi_bma150_open, .release = spi_bma150_release, - .ioctl = spi_bma150_ioctl, + .unlocked_ioctl = spi_bma150_ioctl, }; static struct miscdevice spi_bma_device = { From 3baa0fbf5234ae7d959a58c932fc204016fc419a Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Mon, 4 Apr 2011 22:53:20 -0400 Subject: [PATCH 1058/2556] defconfig: disable CPU_IDLE --- arch/arm/configs/pershoot_mahimahi_defconfig | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/arm/configs/pershoot_mahimahi_defconfig b/arch/arm/configs/pershoot_mahimahi_defconfig index 27852108fce1b..0764ae43a6691 100644 --- a/arch/arm/configs/pershoot_mahimahi_defconfig +++ b/arch/arm/configs/pershoot_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.2 Kernel Configuration -# Wed Mar 30 05:42:44 2011 +# Mon Apr 4 22:48:39 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -464,9 +464,7 @@ CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y # CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set -CONFIG_CPU_IDLE=y -CONFIG_CPU_IDLE_GOV_LADDER=y -CONFIG_CPU_IDLE_GOV_MENU=y +# CONFIG_CPU_IDLE is not set CONFIG_CPU_FREQ_MSM=y # From 24db9b3ec4d2b5ec08d6bc8709186539699191c8 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Mon, 7 Feb 2011 15:19:34 +0100 Subject: [PATCH 1059/2556] ALSA: HDA: New AD1984A model for Dell Precision R5500 commit 677cd904aba939bc4cfdc3c1eada8ec46582127e upstream. For codec AD1984A, add a new model to support Dell Precision R5500 or the microphone jack won't work correctly. BugLink: http://bugs.launchpad.net/bugs/741516 Tested-by: Kent Baxley Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_analog.c | 89 ++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 8dabab798689b..7aee90044c685 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -4352,6 +4352,84 @@ static int ad1984a_thinkpad_init(struct hda_codec *codec) return 0; } +/* + * Precision R5500 + * 0x12 - HP/line-out + * 0x13 - speaker (mono) + * 0x15 - mic-in + */ + +static struct hda_verb ad1984a_precision_verbs[] = { + /* Unmute main output path */ + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x1f}, /* 0dB */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) + 0x17}, /* 0dB */ + /* Analog mixer; mute as default */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Select mic as input */ + {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x27}, /* 0dB */ + /* Configure as mic */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ + /* HP unmute */ + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* turn on EAPD */ + {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, + /* unsolicited event for pin-sense */ + {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, + { } /* end */ +}; + +static struct snd_kcontrol_new ad1984a_precision_mixers[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), + { } /* end */ +}; + + +/* mute internal speaker if HP is plugged */ +static void ad1984a_precision_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_jack_detect(codec, 0x12); + snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + + +/* unsolicited event for HP jack sensing */ +static void ad1984a_precision_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != AD1884A_HP_EVENT) + return; + ad1984a_precision_automute(codec); +} + +/* initialize jack-sensing, too */ +static int ad1984a_precision_init(struct hda_codec *codec) +{ + ad198x_init(codec); + ad1984a_precision_automute(codec); + return 0; +} + + /* * HP Touchsmart * port-A (0x11) - front hp-out @@ -4481,6 +4559,7 @@ enum { AD1884A_MOBILE, AD1884A_THINKPAD, AD1984A_TOUCHSMART, + AD1984A_PRECISION, AD1884A_MODELS }; @@ -4490,9 +4569,11 @@ static const char * const ad1884a_models[AD1884A_MODELS] = { [AD1884A_MOBILE] = "mobile", [AD1884A_THINKPAD] = "thinkpad", [AD1984A_TOUCHSMART] = "touchsmart", + [AD1984A_PRECISION] = "precision", }; static struct snd_pci_quirk ad1884a_cfg_tbl[] = { + SND_PCI_QUIRK(0x1028, 0x04ac, "Precision R5500", AD1984A_PRECISION), SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE), SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP), SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE), @@ -4586,6 +4667,14 @@ static int patch_ad1884a(struct hda_codec *codec) codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event; codec->patch_ops.init = ad1984a_thinkpad_init; break; + case AD1984A_PRECISION: + spec->mixers[0] = ad1984a_precision_mixers; + spec->init_verbs[spec->num_init_verbs++] = + ad1984a_precision_verbs; + spec->multiout.dig_out_nid = 0; + codec->patch_ops.unsol_event = ad1984a_precision_unsol_event; + codec->patch_ops.init = ad1984a_precision_init; + break; case AD1984A_TOUCHSMART: spec->mixers[0] = ad1984a_touchsmart_mixers; spec->init_verbs[0] = ad1984a_touchsmart_verbs; From 1190f9f79730358e098f05d200af8c5d16a91302 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 23 Mar 2011 22:54:32 +0100 Subject: [PATCH 1060/2556] ALSA: hda - Fix SPDIF out regression on ALC889 commit 20b67dddcc5f29d3d0c900225d85e0ac655bc69d upstream. The commit 5a8cfb4e8ae317d283f84122ed20faa069c5e0c4 ALSA: hda - Use ALC_INIT_DEFAULT for really default initialization changed to use the default initialization method for ALC889, but this caused a regression on SPDIF output on some machines. This seems due to the COEF setup included in the default init procedure. For making SPDIF working again, the COEF-setup has to be avoided for the id 0889. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=24342 Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c2eb6a7c2b3c8..d21ce8bc919bc 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1360,7 +1360,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) case 0x10ec0883: case 0x10ec0885: case 0x10ec0887: - case 0x10ec0889: + /*case 0x10ec0889:*/ /* this causes an SPDIF problem */ alc889_coef_init(codec); break; case 0x10ec0888: From 952ef7855e21fead362d72c9f422bb0f2ec02687 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 24 Mar 2011 09:50:15 +0100 Subject: [PATCH 1061/2556] ALSA: Fix yet another race in disconnection commit a45e3d6b13e97506b616980c0f122c3389bcefa4 upstream. This patch fixes a race between snd_card_file_remove() and snd_card_disconnect(). When the card is added to shutdown_files list in snd_card_disconnect(), but it's freed in snd_card_file_remove() at the same time, the shutdown_files list gets corrupted. The list member must be freed in snd_card_file_remove() as well. Reported-and-tested-by: Russ Dill Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/init.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/core/init.c b/sound/core/init.c index 3e65da21a08c0..a0080aa45ae96 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -848,6 +848,7 @@ int snd_card_file_add(struct snd_card *card, struct file *file) return -ENOMEM; mfile->file = file; mfile->disconnected_f_op = NULL; + INIT_LIST_HEAD(&mfile->shutdown_list); spin_lock(&card->files_lock); if (card->shutdown) { spin_unlock(&card->files_lock); @@ -883,6 +884,9 @@ int snd_card_file_remove(struct snd_card *card, struct file *file) list_for_each_entry(mfile, &card->files_list, list) { if (mfile->file == file) { list_del(&mfile->list); + spin_lock(&shutdown_lock); + list_del(&mfile->shutdown_list); + spin_unlock(&shutdown_lock); if (mfile->disconnected_f_op) fops_put(mfile->disconnected_f_op); found = mfile; From c27b92295ab4c6b90b1cee94c4c9c1b4732e1c2e Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 25 Mar 2011 17:51:54 +1100 Subject: [PATCH 1062/2556] ALSA: vmalloc buffers should use normal mmap commit 3674f19dabd15f9541079a588149a370d888f4e6 upstream. It's a big no-no to use pgprot_noncached() when mmap'ing such buffers into userspace since they are mapped cachable in kernel space. This can cause all sort of interesting things ranging from to garbled sound to lockups on various architectures. I've observed that usb-audio is broken on powerpc 4xx for example because of that. Also remove the now unused snd_pcm_lib_mmap_noncached(). It's an arch business to know when to use uncached mappings, there's already hacks for MIPS inside snd_pcm_default_mmap() and other archs are supposed to use dma_mmap_coherent(). (See my separate patch that adds dma_mmap_coherent() to powerpc) Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- include/sound/pcm.h | 4 +--- sound/core/pcm_native.c | 9 --------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index e731f8d719347..ec2678131e112 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1030,9 +1030,7 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, struct vm_area_s #define snd_pcm_lib_mmap_iomem NULL #endif -int snd_pcm_lib_mmap_noncached(struct snd_pcm_substream *substream, - struct vm_area_struct *area); -#define snd_pcm_lib_mmap_vmalloc snd_pcm_lib_mmap_noncached +#define snd_pcm_lib_mmap_vmalloc NULL static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) { diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 4be45e7be8ad0..6848dd9c70a91 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3201,15 +3201,6 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); #endif /* SNDRV_PCM_INFO_MMAP */ -/* mmap callback with pgprot_noncached */ -int snd_pcm_lib_mmap_noncached(struct snd_pcm_substream *substream, - struct vm_area_struct *area) -{ - area->vm_page_prot = pgprot_noncached(area->vm_page_prot); - return snd_pcm_default_mmap(substream, area); -} -EXPORT_SYMBOL(snd_pcm_lib_mmap_noncached); - /* * mmap DMA buffer */ From 6c66673c8abb839c99b9692d02ec8032a11e67af Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 23 Mar 2011 19:29:39 +0100 Subject: [PATCH 1063/2556] perf: Better fit max unprivileged mlock pages for tools needs commit 880f57318450dbead6a03f9e31a1468924d6dd88 upstream. The maximum kilobytes of locked memory that an unprivileged user can reserve is of 512 kB = 128 pages by default, scaled to the number of onlined CPUs, which fits well with the tools that use 128 data pages by default. However tools actually use 129 pages, because they need one more for the user control page. Thus the default mlock threshold is not sufficient for the default tools needs and we always end up to evaluate the constant mlock rlimit policy, which doesn't have this scaling with the number of online CPUs. Hence, on systems that have more than 16 CPUs, we overlap the rlimit threshold and fail to mmap: $ perf record ls Error: failed to mmap with 1 (Operation not permitted) Just increase the max unprivileged mlock threshold by one page so that it supports well perf tools even after 16 CPUs. Reported-by: Han Pingtian Reported-by: Peter Zijlstra Reported-by: Arnaldo Carvalho de Melo Signed-off-by: Frederic Weisbecker Acked-by: Arnaldo Carvalho de Melo Cc: Stephane Eranian LKML-Reference: <1300904979-5508-1-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/perf_event.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/perf_event.c b/kernel/perf_event.c index ad02feadb6b02..f81f9d6395ad0 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -62,7 +62,8 @@ static struct srcu_struct pmus_srcu; */ int sysctl_perf_event_paranoid __read_mostly = 1; -int sysctl_perf_event_mlock __read_mostly = 512; /* 'free' kb per user */ +/* Minimum for 128 pages + 1 for the user control page */ +int sysctl_perf_event_mlock __read_mostly = 516; /* 'free' kb per user */ /* * max perf event sample rate From 7cb252174f786f9ccefa519454b9de5743acaccf Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 23 Mar 2011 02:44:30 +0000 Subject: [PATCH 1064/2556] myri10ge: fix rmmod crash commit cda6587c21a887254c8ed4b58da8fcc4040ab557 upstream. Rmmod myri10ge crash at free_netdev() -> netif_napi_del(), because napi structures are already deallocated. To fix call netif_napi_del() before kfree() at myri10ge_free_slices(). Signed-off-by: Stanislaw Gruszka Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/myri10ge/myri10ge.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index ea5cfe2c3a040..24386a804ea40 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -3645,6 +3645,7 @@ static void myri10ge_free_slices(struct myri10ge_priv *mgp) dma_free_coherent(&pdev->dev, bytes, ss->fw_stats, ss->fw_stats_bus); ss->fw_stats = NULL; + netif_napi_del(&ss->napi); } } kfree(mgp->ss); From 8a09d32eb7481e2966fe051b9006bd8497d409ec Mon Sep 17 00:00:00 2001 From: Bud Brown Date: Wed, 23 Mar 2011 20:47:11 +0100 Subject: [PATCH 1065/2556] cciss: fix lost command issue commit 1ddd5049545e0aa1a0ed19bca4d9c9c3ce1ac8a2 upstream. Under certain workloads a command may seem to get lost. IOW, the Smart Array thinks all commands have been completed but we still have commands in our completion queue. This may lead to system instability, filesystems going read-only, or even panics depending on the affected filesystem. We add an extra read to force the write to complete. Testing shows this extra read avoids the problem. Signed-off-by: Mike Miller Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/block/cciss.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h index 579f749184930..554bbd907d144 100644 --- a/drivers/block/cciss.h +++ b/drivers/block/cciss.h @@ -222,6 +222,7 @@ static void SA5_submit_command( ctlr_info_t *h, CommandList_struct *c) h->ctlr, c->busaddr); #endif /* CCISS_DEBUG */ writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET); + readl(h->vaddr + SA5_REQUEST_PORT_OFFSET); h->commands_outstanding++; if ( h->commands_outstanding > h->max_outstanding) h->max_outstanding = h->commands_outstanding; From dd6809706f2e100ecd9afb5435bfed847c57f2e6 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Mon, 21 Mar 2011 18:27:21 +0530 Subject: [PATCH 1066/2556] ath9k: Fix kernel panic in AR2427 commit 61e1b0b00c793ad5a32fe2181c9f77115fed5dc4 upstream. Kernel panic occurs just after AR2427 establishes connection with AP. Unless aggregation is enabled we don't initialize the TID structure. Thus accesing the elements of the TID structure when aggregation is disabled, leads to NULL pointer dereferencing. [ 191.320358] Call Trace: [ 191.320364] [] ? ath9k_tx+0xa7/0x200 [ath9k] [ 191.320376] [] ? __ieee80211_tx+0x5c/0x1e0 [mac80211] [ 191.320386] [] ? ieee80211_tx+0x7b/0x90 [mac80211] [ 191.320395] [] ? ieee80211_xmit+0x9d/0x1d0 [mac80211] [ 191.320401] [] ? wake_up_state+0xf/0x20 [ 191.320405] [] ? signal_wake_up+0x28/0x40 [ 191.320410] [] ? default_spin_lock_flags+0x8/0x10 [ 191.320420] [] ? ieee80211_subif_start_xmit+0x2e8/0x7c0 [mac80211] [ 191.320425] [] ? do_page_fault+0x295/0x3a0 [ 191.320431] [] ? dev_hard_start_xmit+0x1ad/0x210 [ 191.320436] [] ? sch_direct_xmit+0x105/0x170 [ 191.320445] [] ? get_sta_flags+0x2a/0x40 [mac80211] [ 191.320449] [] ? dev_queue_xmit+0x37f/0x4b0 [ 191.320452] [] ? eth_header+0x0/0xb0 [ 191.320456] [] ? neigh_resolve_output+0xe9/0x310 [ 191.320461] [] ? ip6_output_finish+0xa5/0x110 [ 191.320464] [] ? ip6_output2+0x134/0x250 [ 191.320468] [] ? ip6_output+0x6d/0x100 [ 191.320471] [] ? mld_sendpack+0x395/0x3e0 [ 191.320475] [] ? add_grhead+0x31/0xa0 [ 191.320478] [] ? mld_send_cr+0x1bc/0x2b0 [ 191.320482] [] ? irq_exit+0x39/0x70 [ 191.320485] [] ? mld_ifc_timer_expire+0x10/0x40 [ 191.320489] [] ? run_timer_softirq+0x13e/0x2c0 [ 191.320493] [] ? common_interrupt+0x30/0x40 [ 191.320498] [] ? mld_ifc_timer_expire+0x0/0x40 [ 191.320502] [] ? __do_softirq+0x98/0x1b0 [ 191.320506] [] ? do_softirq+0x45/0x50 [ 191.320509] [] ? irq_exit+0x65/0x70 [ 191.320513] [] ? smp_apic_timer_interrupt+0x5c/0x8b [ 191.320516] [] ? apic_timer_interrupt+0x31/0x40 [ 191.320521] [] ? k_getrusage+0x12b/0x2f0 [ 191.320525] [] ? acpi_idle_enter_simple+0x117/0x148 [ 191.320529] [] ? cpuidle_idle_call+0x7a/0x100 [ 191.320532] [] ? cpu_idle+0x94/0xd0 [ 191.320536] [] ? rest_init+0x58/0x60 [ 191.320541] [] ? start_kernel+0x351/0x357 [ 191.320544] [] ? unknown_bootoption+0x0/0x19e [ 191.320548] [] ? i386_start_kernel+0xaa/0xb1 [ 191.320550] Code: 03 66 3d 00 03 0f 84 7c 02 00 00 83 c3 18 0f b6 03 8b 4d e0 89 c3 83 e3 0f 6b c3 48 89 5d d8 8d 04 06 8d 50 0c 89 55 d0 8b 40 20 <8b> 00 3b 01 0f 85 8e 02 00 00 f6 47 20 40 0f 84 29 ff ff ff 8b [ 191.320634] EIP: [] ath_tx_start+0x474/0x770 [ath9k] SS:ESP 0068:c0761a90 [ 191.320642] CR2: 0000000000000000 [ 191.320647] ---[ end trace 9296ef23b9076ece ]--- [ 191.320650] Kernel panic - not syncing: Fatal exception in interrupt Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/xmit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 07b7804aec5ba..5c9d83b103f7c 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1699,8 +1699,8 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct ath_buf *bf, u8 tidno; spin_lock_bh(&txctl->txq->axq_lock); - - if (ieee80211_is_data_qos(hdr->frame_control) && txctl->an) { + if ((sc->sc_flags & SC_OP_TXAGGR) && txctl->an && + ieee80211_is_data_qos(hdr->frame_control)) { tidno = ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK; tid = ATH_AN_2_TID(txctl->an, tidno); From 2f74a0681817b04ebcad2068935807a9a554c3e8 Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Wed, 23 Mar 2011 11:42:57 -0400 Subject: [PATCH 1067/2556] sound/oss/opl3: validate voice and channel indexes commit 4d00135a680727f6c3be78f8befaac009030e4df upstream. User-controllable indexes for voice and channel values may cause reading and writing beyond the bounds of their respective arrays, leading to potentially exploitable memory corruption. Validate these indexes. Signed-off-by: Dan Rosenberg Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/oss/opl3.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/sound/oss/opl3.c b/sound/oss/opl3.c index 938c48c43585e..f4ffdff9b2451 100644 --- a/sound/oss/opl3.c +++ b/sound/oss/opl3.c @@ -849,6 +849,10 @@ static int opl3_load_patch(int dev, int format, const char __user *addr, static void opl3_panning(int dev, int voice, int value) { + + if (voice < 0 || voice >= devc->nr_voice) + return; + devc->voc[voice].panning = value; } @@ -1066,8 +1070,15 @@ static int opl3_alloc_voice(int dev, int chn, int note, struct voice_alloc_info static void opl3_setup_voice(int dev, int voice, int chn) { - struct channel_info *info = - &synth_devs[dev]->chn_info[chn]; + struct channel_info *info; + + if (voice < 0 || voice >= devc->nr_voice) + return; + + if (chn < 0 || chn > 15) + return; + + info = &synth_devs[dev]->chn_info[chn]; opl3_set_instr(dev, voice, info->pgm_num); From 523c503eab8faa13bdbdb4b21846a71c8dc92b69 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 21 Mar 2011 20:01:00 +0100 Subject: [PATCH 1068/2556] mac80211: initialize sta->last_rx in sta_info_alloc commit 8bc8aecdc5e26cfda12dbd6867af4aa67836da6a upstream. This field is used to determine the inactivity time. When in AP mode, hostapd uses it for kicking out inactive clients after a while. Without this patch, hostapd immediately deauthenticates a new client if it checks the inactivity time before the client sends its first data frame. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- net/mac80211/sta_info.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index c426504ed1cfe..604216e2ee3d4 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -243,6 +243,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, memcpy(sta->sta.addr, addr, ETH_ALEN); sta->local = local; sta->sdata = sdata; + sta->last_rx = jiffies; ewma_init(&sta->avg_signal, 1024, 8); From e7f208ebb98b445a92f0bbc2d420599671450f88 Mon Sep 17 00:00:00 2001 From: John Hughes Date: Wed, 4 Nov 2009 19:01:22 +0100 Subject: [PATCH 1069/2556] ses: show devices for enclosures with no page 7 commit 877a55979c189c590e819a61cbbe2b7947875f17 upstream. enclosure page 7 gives us the "pretty" names of the enclosure slots. Without a page 7, we can still use the enclosure code as long as we make up numeric names for the slots. Unfortunately, the current code fails to add any devices because the check for page 10 is in the wrong place if we have no page 7. Fix it so that devices show up even if the enclosure has no page 7. Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/ses.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 7f5a6a86f820f..3b00e907b9174 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -390,9 +390,9 @@ static void ses_enclosure_data_process(struct enclosure_device *edev, len = (desc_ptr[2] << 8) + desc_ptr[3]; /* skip past overall descriptor */ desc_ptr += len + 4; - if (ses_dev->page10) - addl_desc_ptr = ses_dev->page10 + 8; } + if (ses_dev->page10) + addl_desc_ptr = ses_dev->page10 + 8; type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; components = 0; for (i = 0; i < types; i++, type_ptr += 4) { From c289b973f884ea230b38b7aa1878adc2c77a32f2 Mon Sep 17 00:00:00 2001 From: "Krishnasamy, Somasundaram" Date: Mon, 28 Feb 2011 18:13:22 -0500 Subject: [PATCH 1070/2556] ses: Avoid kernel panic when lun 0 is not mapped commit d1e12de804f9d8ad114786ca7c2ce593cba79891 upstream. During device discovery, scsi mid layer sends INQUIRY command to LUN 0. If the LUN 0 is not mapped to host, it creates a temporary scsi_device with LUN id 0 and sends REPORT_LUNS command to it. After the REPORT_LUNS succeeds, it walks through the LUN table and adds each LUN found to sysfs. At the end of REPORT_LUNS lun table scan, it will delete the temporary scsi_device of LUN 0. When scsi devices are added to sysfs, it calls add_dev function of all the registered class interfaces. If ses driver has been registered, ses_intf_add() of ses module will be called. This function calls scsi_device_enclosure() to check the inquiry data for EncServ bit. Since inquiry was not allocated for temporary LUN 0 scsi_device, it will cause NULL pointer exception. To fix the problem, sdev->inquiry is checked for NULL before reading it. Signed-off-by: Somasundaram Krishnasamy Signed-off-by: Babu Moger Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- include/scsi/scsi_device.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 85867dcde3352..bfd36ff14c991 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -461,7 +461,7 @@ static inline int scsi_device_qas(struct scsi_device *sdev) } static inline int scsi_device_enclosure(struct scsi_device *sdev) { - return sdev->inquiry[6] & (1<<6); + return sdev->inquiry ? (sdev->inquiry[6] & (1<<6)) : 1; } static inline int scsi_device_protection(struct scsi_device *sdev) From 2e8baeac894365899a6934987bf2dfc6a28d4dd6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 5 Mar 2011 13:21:51 +0100 Subject: [PATCH 1071/2556] PCI/ACPI: Report ASPM support to BIOS if not disabled from command line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 8b8bae901ce23addbdcdb54fa1696fb2d049feb5 upstream. We need to distinguish the situation in which ASPM support is disabled from the command line or through .config from the situation in which it is disabled, because the hardware or BIOS can't handle it. In the former case we should not report ASPM support to the BIOS through ACPI _OSC, but in the latter case we should do that. Introduce pcie_aspm_support_enabled() that can be used by acpi_pci_root_add() to determine whether or not it should report ASPM support to the BIOS through _OSC. References: https://bugzilla.kernel.org/show_bug.cgi?id=29722 References: https://bugzilla.kernel.org/show_bug.cgi?id=20232 Reported-and-tested-by: Ortwin Glück Reviewed-by: Kenji Kaneshige Tested-by: Kenji Kaneshige Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/pci_root.c | 2 +- drivers/pci/pcie/aspm.c | 7 +++++++ include/linux/pci.h | 7 +++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 85249395623ba..c7358dd68b31c 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -564,7 +564,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) /* Indicate support for various _OSC capabilities. */ if (pci_ext_cfg_avail(root->bus->self)) flags |= OSC_EXT_PCI_CONFIG_SUPPORT; - if (pcie_aspm_enabled()) + if (pcie_aspm_support_enabled()) flags |= OSC_ACTIVE_STATE_PWR_SUPPORT | OSC_CLOCK_PWR_CAPABILITY_SUPPORT; if (pci_msi_enabled()) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 3188cd96b3386..bbdb4fd85b9cf 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -69,6 +69,7 @@ struct pcie_link_state { }; static int aspm_disabled, aspm_force, aspm_clear_state; +static bool aspm_support_enabled = true; static DEFINE_MUTEX(aspm_lock); static LIST_HEAD(link_list); @@ -896,6 +897,7 @@ static int __init pcie_aspm_disable(char *str) { if (!strcmp(str, "off")) { aspm_disabled = 1; + aspm_support_enabled = false; printk(KERN_INFO "PCIe ASPM is disabled\n"); } else if (!strcmp(str, "force")) { aspm_force = 1; @@ -930,3 +932,8 @@ int pcie_aspm_enabled(void) } EXPORT_SYMBOL(pcie_aspm_enabled); +bool pcie_aspm_support_enabled(void) +{ + return aspm_support_enabled; +} +EXPORT_SYMBOL(pcie_aspm_support_enabled); diff --git a/include/linux/pci.h b/include/linux/pci.h index 559d028970752..6002bcade0801 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1002,12 +1002,11 @@ extern bool pcie_ports_auto; #endif #ifndef CONFIG_PCIEASPM -static inline int pcie_aspm_enabled(void) -{ - return 0; -} +static inline int pcie_aspm_enabled(void) { return 0; } +static inline bool pcie_aspm_support_enabled(void) { return false; } #else extern int pcie_aspm_enabled(void); +extern bool pcie_aspm_support_enabled(void); #endif #ifdef CONFIG_PCIEAER From eb7ad98797ba117b37c07517793e836ac58f33c8 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Wed, 9 Mar 2011 11:49:13 -0600 Subject: [PATCH 1072/2556] eCryptfs: Unlock page in write_begin error path commit 50f198ae16ac66508d4b8d5a40967a8507ad19ee upstream. Unlock the page in error path of ecryptfs_write_begin(). This may happen, for example, if decryption fails while bring the page up-to-date. Signed-off-by: Tyler Hicks Signed-off-by: Greg Kroah-Hartman --- fs/ecryptfs/mmap.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c index cc64fca89f8dc..eb9d9672ebd85 100644 --- a/fs/ecryptfs/mmap.c +++ b/fs/ecryptfs/mmap.c @@ -374,6 +374,11 @@ static int ecryptfs_write_begin(struct file *file, && (pos != 0)) zero_user(page, 0, PAGE_CACHE_SIZE); out: + if (unlikely(rc)) { + unlock_page(page); + page_cache_release(page); + *pagep = NULL; + } return rc; } From a0d088491fdbf655fde5b27a6a4d53f7495a8a11 Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Thu, 17 Mar 2011 12:48:50 +0100 Subject: [PATCH 1073/2556] eCryptfs: ecryptfs_keyring_auth_tok_for_sig() bug fix commit 1821df040ac3cd6a57518739f345da6d50ea9d3f upstream. The pointer '(*auth_tok_key)' is set to NULL in case request_key() fails, in order to prevent its use by functions calling ecryptfs_keyring_auth_tok_for_sig(). Signed-off-by: Roberto Sassu Signed-off-by: Tyler Hicks Signed-off-by: Greg Kroah-Hartman --- fs/ecryptfs/keystore.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index c1436cff6f2d7..4feb78c23651a 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -1563,6 +1563,7 @@ int ecryptfs_keyring_auth_tok_for_sig(struct key **auth_tok_key, printk(KERN_ERR "Could not find key with description: [%s]\n", sig); rc = process_request_key_err(PTR_ERR(*auth_tok_key)); + (*auth_tok_key) = NULL; goto out; } (*auth_tok) = ecryptfs_get_key_payload_data(*auth_tok_key); From 1dcbe29b9a9a0e8832a8d670487380d0581d657a Mon Sep 17 00:00:00 2001 From: Tadeusz Struk Date: Sun, 13 Mar 2011 16:56:17 +0800 Subject: [PATCH 1074/2556] crypto: aesni-intel - fixed problem with packets that are not multiple of 64bytes commit 60af520cf264ea26b2af3a6871bbd71850522aea upstream. This patch fixes problem with packets that are not multiple of 64bytes. Signed-off-by: Adrian Hoban Signed-off-by: Aidan O'Mahony Signed-off-by: Gabriele Paoloni Signed-off-by: Tadeusz Struk Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- arch/x86/crypto/aesni-intel_asm.S | 5 ++++- arch/x86/crypto/aesni-intel_glue.c | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/arch/x86/crypto/aesni-intel_asm.S b/arch/x86/crypto/aesni-intel_asm.S index 8fe2a4966b7af..4292df78c9da0 100644 --- a/arch/x86/crypto/aesni-intel_asm.S +++ b/arch/x86/crypto/aesni-intel_asm.S @@ -1612,6 +1612,7 @@ _zero_cipher_left_encrypt: movdqa SHUF_MASK(%rip), %xmm10 PSHUFB_XMM %xmm10, %xmm0 + ENCRYPT_SINGLE_BLOCK %xmm0, %xmm1 # Encrypt(K, Yn) sub $16, %r11 add %r13, %r11 @@ -1634,7 +1635,9 @@ _zero_cipher_left_encrypt: # GHASH computation for the last <16 byte block sub %r13, %r11 add $16, %r11 - PSHUFB_XMM %xmm10, %xmm1 + + movdqa SHUF_MASK(%rip), %xmm10 + PSHUFB_XMM %xmm10, %xmm0 # shuffle xmm0 back to output as ciphertext diff --git a/arch/x86/crypto/aesni-intel_glue.c b/arch/x86/crypto/aesni-intel_glue.c index e1e60c7d5813c..b375b2a7a14f8 100644 --- a/arch/x86/crypto/aesni-intel_glue.c +++ b/arch/x86/crypto/aesni-intel_glue.c @@ -828,9 +828,15 @@ static int rfc4106_init(struct crypto_tfm *tfm) struct cryptd_aead *cryptd_tfm; struct aesni_rfc4106_gcm_ctx *ctx = (struct aesni_rfc4106_gcm_ctx *) PTR_ALIGN((u8 *)crypto_tfm_ctx(tfm), AESNI_ALIGN); + struct crypto_aead *cryptd_child; + struct aesni_rfc4106_gcm_ctx *child_ctx; cryptd_tfm = cryptd_alloc_aead("__driver-gcm-aes-aesni", 0, 0); if (IS_ERR(cryptd_tfm)) return PTR_ERR(cryptd_tfm); + + cryptd_child = cryptd_aead_child(cryptd_tfm); + child_ctx = aesni_rfc4106_gcm_ctx_get(cryptd_child); + memcpy(child_ctx, ctx, sizeof(*ctx)); ctx->cryptd_tfm = cryptd_tfm; tfm->crt_aead.reqsize = sizeof(struct aead_request) + crypto_aead_reqsize(&cryptd_tfm->base); @@ -925,6 +931,9 @@ static int rfc4106_set_key(struct crypto_aead *parent, const u8 *key, int ret = 0; struct crypto_tfm *tfm = crypto_aead_tfm(parent); struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(parent); + struct crypto_aead *cryptd_child = cryptd_aead_child(ctx->cryptd_tfm); + struct aesni_rfc4106_gcm_ctx *child_ctx = + aesni_rfc4106_gcm_ctx_get(cryptd_child); u8 *new_key_mem = NULL; if (key_len < 4) { @@ -968,6 +977,7 @@ static int rfc4106_set_key(struct crypto_aead *parent, const u8 *key, goto exit; } ret = rfc4106_set_hash_subkey(ctx->hash_subkey, key, key_len); + memcpy(child_ctx, ctx, sizeof(*ctx)); exit: kfree(new_key_mem); return ret; @@ -999,7 +1009,6 @@ static int rfc4106_encrypt(struct aead_request *req) int ret; struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm); - struct crypto_aead *cryptd_child = cryptd_aead_child(ctx->cryptd_tfm); if (!irq_fpu_usable()) { struct aead_request *cryptd_req = @@ -1008,6 +1017,7 @@ static int rfc4106_encrypt(struct aead_request *req) aead_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base); return crypto_aead_encrypt(cryptd_req); } else { + struct crypto_aead *cryptd_child = cryptd_aead_child(ctx->cryptd_tfm); kernel_fpu_begin(); ret = cryptd_child->base.crt_aead.encrypt(req); kernel_fpu_end(); @@ -1020,7 +1030,6 @@ static int rfc4106_decrypt(struct aead_request *req) int ret; struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm); - struct crypto_aead *cryptd_child = cryptd_aead_child(ctx->cryptd_tfm); if (!irq_fpu_usable()) { struct aead_request *cryptd_req = @@ -1029,6 +1038,7 @@ static int rfc4106_decrypt(struct aead_request *req) aead_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base); return crypto_aead_decrypt(cryptd_req); } else { + struct crypto_aead *cryptd_child = cryptd_aead_child(ctx->cryptd_tfm); kernel_fpu_begin(); ret = cryptd_child->base.crt_aead.decrypt(req); kernel_fpu_end(); From 723342c39003aa64889f8bd5255e7f876434ef7a Mon Sep 17 00:00:00 2001 From: Arjan Mels Date: Tue, 5 Apr 2011 20:26:11 +0200 Subject: [PATCH 1075/2556] staging: usbip: bugfixes related to kthread conversion commit d2dd0b07c3e725d386d20294ec906f7ddef207fa upstream. When doing a usb port reset do a queued reset instead to prevent a deadlock: the reset will cause the driver to unbind, causing the usb_driver_lock_for_reset to stall. Signed-off-by: Arjan Mels Cc: Takahiro Hirofuchi Cc: Max Vozeler Cc: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/staging/usbip/stub_rx.c | 40 +++++++++++++-------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/drivers/staging/usbip/stub_rx.c b/drivers/staging/usbip/stub_rx.c index ae6ac82754a4d..8e60332efaef3 100644 --- a/drivers/staging/usbip/stub_rx.c +++ b/drivers/staging/usbip/stub_rx.c @@ -170,33 +170,23 @@ static int tweak_set_configuration_cmd(struct urb *urb) static int tweak_reset_device_cmd(struct urb *urb) { - struct usb_ctrlrequest *req; - __u16 value; - __u16 index; - int ret; - - req = (struct usb_ctrlrequest *) urb->setup_packet; - value = le16_to_cpu(req->wValue); - index = le16_to_cpu(req->wIndex); - - usbip_uinfo("reset_device (port %d) to %s\n", index, - dev_name(&urb->dev->dev)); + struct stub_priv *priv = (struct stub_priv *) urb->context; + struct stub_device *sdev = priv->sdev; - /* all interfaces should be owned by usbip driver, so just reset it. */ - ret = usb_lock_device_for_reset(urb->dev, NULL); - if (ret < 0) { - dev_err(&urb->dev->dev, "lock for reset\n"); - return ret; - } - - /* try to reset the device */ - ret = usb_reset_device(urb->dev); - if (ret < 0) - dev_err(&urb->dev->dev, "device reset\n"); + usbip_uinfo("reset_device %s\n", dev_name(&urb->dev->dev)); - usb_unlock_device(urb->dev); - - return ret; + /* + * usb_lock_device_for_reset caused a deadlock: it causes the driver + * to unbind. In the shutdown the rx thread is signalled to shut down + * but this thread is pending in the usb_lock_device_for_reset. + * + * Instead queue the reset. + * + * Unfortunatly an existing usbip connection will be dropped due to + * driver unbinding. + */ + usb_queue_reset_device(sdev->interface); + return 0; } /* From 5aa02704b9ee67c5b2ee26d54c5f4eb99e93ba9a Mon Sep 17 00:00:00 2001 From: Arjan Mels Date: Tue, 5 Apr 2011 20:26:38 +0200 Subject: [PATCH 1076/2556] staging: usbip: bugfix add number of packets for isochronous frames commit 1325f85fa49f57df034869de430f7c302ae23109 upstream. The number_of_packets was not transmitted for RET_SUBMIT packets. The linux client used the stored number_of_packet from the submitted request. The windows userland client does not do this however and needs to know the number_of_packets to determine the size of the transmission. Signed-off-by: Arjan Mels Cc: Takahiro Hirofuchi Cc: Max Vozeler Signed-off-by: Greg Kroah-Hartman --- drivers/staging/usbip/usbip_common.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c index 210ef16bab8d2..63e2a2b91a401 100644 --- a/drivers/staging/usbip/usbip_common.c +++ b/drivers/staging/usbip/usbip_common.c @@ -334,10 +334,11 @@ void usbip_dump_header(struct usbip_header *pdu) usbip_udbg("CMD_UNLINK: seq %u\n", pdu->u.cmd_unlink.seqnum); break; case USBIP_RET_SUBMIT: - usbip_udbg("RET_SUBMIT: st %d al %u sf %d ec %d\n", + usbip_udbg("RET_SUBMIT: st %d al %u sf %d #p %d ec %d\n", pdu->u.ret_submit.status, pdu->u.ret_submit.actual_length, pdu->u.ret_submit.start_frame, + pdu->u.ret_submit.number_of_packets, pdu->u.ret_submit.error_count); case USBIP_RET_UNLINK: usbip_udbg("RET_UNLINK: status %d\n", pdu->u.ret_unlink.status); @@ -625,6 +626,7 @@ static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb, rpdu->status = urb->status; rpdu->actual_length = urb->actual_length; rpdu->start_frame = urb->start_frame; + rpdu->number_of_packets = urb->number_of_packets; rpdu->error_count = urb->error_count; } else { /* vhci_rx.c */ @@ -632,6 +634,7 @@ static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb, urb->status = rpdu->status; urb->actual_length = rpdu->actual_length; urb->start_frame = rpdu->start_frame; + urb->number_of_packets = rpdu->number_of_packets; urb->error_count = rpdu->error_count; } } @@ -700,11 +703,13 @@ static void correct_endian_ret_submit(struct usbip_header_ret_submit *pdu, cpu_to_be32s(&pdu->status); cpu_to_be32s(&pdu->actual_length); cpu_to_be32s(&pdu->start_frame); + cpu_to_be32s(&pdu->number_of_packets); cpu_to_be32s(&pdu->error_count); } else { be32_to_cpus(&pdu->status); be32_to_cpus(&pdu->actual_length); be32_to_cpus(&pdu->start_frame); + cpu_to_be32s(&pdu->number_of_packets); be32_to_cpus(&pdu->error_count); } } From 8974a58bc1263e537da05ec712496e47e4bb8be4 Mon Sep 17 00:00:00 2001 From: Arjan Mels Date: Tue, 5 Apr 2011 20:26:59 +0200 Subject: [PATCH 1077/2556] staging: usbip: bugfix for isochronous packets and optimization commit 28276a28d8b3cd19f4449991faad4945fe557656 upstream. For isochronous packets the actual_length is the sum of the actual length of each of the packets, however between the packets might be padding, so it is not sufficient to just send the first actual_length bytes of the buffer. To fix this and simultanesouly optimize the bandwidth the content of the isochronous packets are send without the padding, the padding is restored on the receiving end. Signed-off-by: Arjan Mels Cc: Takahiro Hirofuchi Cc: Max Vozeler Signed-off-by: Greg Kroah-Hartman --- drivers/staging/usbip/stub_tx.c | 74 ++++++++++++++++++++++------ drivers/staging/usbip/usbip_common.c | 57 +++++++++++++++++++++ drivers/staging/usbip/usbip_common.h | 2 + drivers/staging/usbip/vhci_rx.c | 3 ++ 4 files changed, 122 insertions(+), 14 deletions(-) diff --git a/drivers/staging/usbip/stub_tx.c b/drivers/staging/usbip/stub_tx.c index d7136e2c86faa..b7a493c1df461 100644 --- a/drivers/staging/usbip/stub_tx.c +++ b/drivers/staging/usbip/stub_tx.c @@ -169,7 +169,6 @@ static int stub_send_ret_submit(struct stub_device *sdev) struct stub_priv *priv, *tmp; struct msghdr msg; - struct kvec iov[3]; size_t txsize; size_t total_size = 0; @@ -179,28 +178,73 @@ static int stub_send_ret_submit(struct stub_device *sdev) struct urb *urb = priv->urb; struct usbip_header pdu_header; void *iso_buffer = NULL; + struct kvec *iov = NULL; + int iovnum = 0; txsize = 0; memset(&pdu_header, 0, sizeof(pdu_header)); memset(&msg, 0, sizeof(msg)); - memset(&iov, 0, sizeof(iov)); - usbip_dbg_stub_tx("setup txdata urb %p\n", urb); + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) + iovnum = 2 + urb->number_of_packets; + else + iovnum = 2; + + iov = kzalloc(iovnum * sizeof(struct kvec), GFP_KERNEL); + if (!iov) { + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC); + return -1; + } + + iovnum = 0; /* 1. setup usbip_header */ setup_ret_submit_pdu(&pdu_header, urb); + usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n", + pdu_header.base.seqnum, urb); + /*usbip_dump_header(pdu_header);*/ usbip_header_correct_endian(&pdu_header, 1); - iov[0].iov_base = &pdu_header; - iov[0].iov_len = sizeof(pdu_header); + iov[iovnum].iov_base = &pdu_header; + iov[iovnum].iov_len = sizeof(pdu_header); + iovnum++; txsize += sizeof(pdu_header); /* 2. setup transfer buffer */ - if (usb_pipein(urb->pipe) && urb->actual_length > 0) { - iov[1].iov_base = urb->transfer_buffer; - iov[1].iov_len = urb->actual_length; + if (usb_pipein(urb->pipe) && + usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS && + urb->actual_length > 0) { + iov[iovnum].iov_base = urb->transfer_buffer; + iov[iovnum].iov_len = urb->actual_length; + iovnum++; txsize += urb->actual_length; + } else if (usb_pipein(urb->pipe) && + usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + /* + * For isochronous packets: actual length is the sum of + * the actual length of the individual, packets, but as + * the packet offsets are not changed there will be + * padding between the packets. To optimally use the + * bandwidth the padding is not transmitted. + */ + + int i; + for (i = 0; i < urb->number_of_packets; i++) { + iov[iovnum].iov_base = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + iov[iovnum].iov_len = urb->iso_frame_desc[i].actual_length; + iovnum++; + txsize += urb->iso_frame_desc[i].actual_length; + } + + if (txsize != sizeof(pdu_header) + urb->actual_length) { + dev_err(&sdev->interface->dev, + "actual length of urb (%d) does not match iso packet sizes (%d)\n", + urb->actual_length, txsize-sizeof(pdu_header)); + kfree(iov); + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); + return -1; + } } /* 3. setup iso_packet_descriptor */ @@ -211,32 +255,34 @@ static int stub_send_ret_submit(struct stub_device *sdev) if (!iso_buffer) { usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC); + kfree(iov); return -1; } - iov[2].iov_base = iso_buffer; - iov[2].iov_len = len; + iov[iovnum].iov_base = iso_buffer; + iov[iovnum].iov_len = len; txsize += len; + iovnum++; } - ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov, - 3, txsize); + ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, + iov, iovnum, txsize); if (ret != txsize) { dev_err(&sdev->interface->dev, "sendmsg failed!, retval %d for %zd\n", ret, txsize); + kfree(iov); kfree(iso_buffer); usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); return -1; } + kfree(iov); kfree(iso_buffer); - usbip_dbg_stub_tx("send txdata\n"); total_size += txsize; } - spin_lock_irqsave(&sdev->priv_lock, flags); list_for_each_entry_safe(priv, tmp, &sdev->priv_free, list) { diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c index 63e2a2b91a401..2108ca15542e6 100644 --- a/drivers/staging/usbip/usbip_common.c +++ b/drivers/staging/usbip/usbip_common.c @@ -835,6 +835,7 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) int size = np * sizeof(*iso); int i; int ret; + int total_length = 0; if (!usb_pipeisoc(urb->pipe)) return 0; @@ -864,19 +865,75 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) return -EPIPE; } + for (i = 0; i < np; i++) { iso = buff + (i * sizeof(*iso)); usbip_iso_pakcet_correct_endian(iso, 0); usbip_pack_iso(iso, &urb->iso_frame_desc[i], 0); + total_length += urb->iso_frame_desc[i].actual_length; } kfree(buff); + if (total_length != urb->actual_length) { + dev_err(&urb->dev->dev, + "total length of iso packets (%d) not equal to actual length of buffer (%d)\n", + total_length, urb->actual_length); + + if (ud->side == USBIP_STUB) + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + else + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + + return -EPIPE; + } + return ret; } EXPORT_SYMBOL_GPL(usbip_recv_iso); +/* + * This functions restores the padding which was removed for optimizing + * the bandwidth during transfer over tcp/ip + * + * buffer and iso packets need to be stored and be in propeper endian in urb + * before calling this function + */ +int usbip_pad_iso(struct usbip_device *ud, struct urb *urb) +{ + int np = urb->number_of_packets; + int i; + int ret; + int actualoffset = urb->actual_length; + + if (!usb_pipeisoc(urb->pipe)) + return 0; + + /* if no packets or length of data is 0, then nothing to unpack */ + if (np == 0 || urb->actual_length == 0) + return 0; + + /* + * if actual_length is transfer_buffer_length then no padding is + * present. + */ + if (urb->actual_length == urb->transfer_buffer_length) + return 0; + + /* + * loop over all packets from last to first (to prevent overwritting + * memory when padding) and move them into the proper place + */ + for (i = np-1; i > 0; i--) { + actualoffset -= urb->iso_frame_desc[i].actual_length; + memmove(urb->transfer_buffer + urb->iso_frame_desc[i].offset, + urb->transfer_buffer + actualoffset, + urb->iso_frame_desc[i].actual_length); + } + return ret; +} +EXPORT_SYMBOL_GPL(usbip_pad_iso); /* some members of urb must be substituted before. */ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb) diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h index d280e234e0670..baa4c09bb98c6 100644 --- a/drivers/staging/usbip/usbip_common.h +++ b/drivers/staging/usbip/usbip_common.h @@ -393,6 +393,8 @@ void usbip_header_correct_endian(struct usbip_header *pdu, int send); int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb); /* some members of urb must be substituted before. */ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb); +/* some members of urb must be substituted before. */ +int usbip_pad_iso(struct usbip_device *ud, struct urb *urb); void *usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen); diff --git a/drivers/staging/usbip/vhci_rx.c b/drivers/staging/usbip/vhci_rx.c index bf69914709410..109002a347b9a 100644 --- a/drivers/staging/usbip/vhci_rx.c +++ b/drivers/staging/usbip/vhci_rx.c @@ -99,6 +99,9 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev, if (usbip_recv_iso(ud, urb) < 0) return; + /* restore the padding in iso packets */ + if (usbip_pad_iso(ud, urb) < 0) + return; if (usbip_dbg_flag_vhci_rx) usbip_dump_urb(urb); From 5fc41ed0596b18d0be02d046e2c989d6fc231f3b Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Mon, 21 Mar 2011 14:41:37 +0100 Subject: [PATCH 1078/2556] staging: hv: use sync_bitops when interacting with the hypervisor commit 22356585712d1ff08fbfed152edd8b386873b238 upstream. Locking is required when tweaking bits located in a shared page, use the sync_ version of bitops. Without this change vmbus_on_event() will miss events and as a result, vmbus_isr() will not schedule the receive tasklet. Signed-off-by: Olaf Hering Acked-by: Haiyang Zhang Acked-by: Hank Janssen Signed-off-by: Greg Kroah-Hartman --- drivers/staging/hv/channel.c | 8 ++++---- drivers/staging/hv/connection.c | 4 ++-- drivers/staging/hv/vmbus_drv.c | 2 +- drivers/staging/hv/vmbus_private.h | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/staging/hv/channel.c b/drivers/staging/hv/channel.c index 45a627d77b417..09e596a506b0e 100644 --- a/drivers/staging/hv/channel.c +++ b/drivers/staging/hv/channel.c @@ -76,14 +76,14 @@ static void vmbus_setevent(struct vmbus_channel *channel) if (channel->offermsg.monitor_allocated) { /* Each u32 represents 32 channels */ - set_bit(channel->offermsg.child_relid & 31, + sync_set_bit(channel->offermsg.child_relid & 31, (unsigned long *) gVmbusConnection.SendInterruptPage + (channel->offermsg.child_relid >> 5)); monitorpage = gVmbusConnection.MonitorPages; monitorpage++; /* Get the child to parent monitor page */ - set_bit(channel->monitor_bit, + sync_set_bit(channel->monitor_bit, (unsigned long *)&monitorpage->trigger_group [channel->monitor_grp].pending); @@ -99,7 +99,7 @@ static void VmbusChannelClearEvent(struct vmbus_channel *channel) if (Channel->offermsg.monitor_allocated) { /* Each u32 represents 32 channels */ - clear_bit(Channel->offermsg.child_relid & 31, + sync_clear_bit(Channel->offermsg.child_relid & 31, (unsigned long *)gVmbusConnection.SendInterruptPage + (Channel->offermsg.child_relid >> 5)); @@ -107,7 +107,7 @@ static void VmbusChannelClearEvent(struct vmbus_channel *channel) (struct hv_monitor_page *)gVmbusConnection.MonitorPages; monitorPage++; /* Get the child to parent monitor page */ - clear_bit(Channel->monitor_bit, + sync_clear_bit(Channel->monitor_bit, (unsigned long *)&monitorPage->trigger_group [Channel->monitor_grp].Pending); } diff --git a/drivers/staging/hv/connection.c b/drivers/staging/hv/connection.c index c2e298ff48344..0739eb7b6ee11 100644 --- a/drivers/staging/hv/connection.c +++ b/drivers/staging/hv/connection.c @@ -281,7 +281,7 @@ void VmbusOnEvents(void) for (dword = 0; dword < maxdword; dword++) { if (recvInterruptPage[dword]) { for (bit = 0; bit < 32; bit++) { - if (test_and_clear_bit(bit, (unsigned long *)&recvInterruptPage[dword])) { + if (sync_test_and_clear_bit(bit, (unsigned long *)&recvInterruptPage[dword])) { relid = (dword << 5) + bit; DPRINT_DBG(VMBUS, "event detected for relid - %d", relid); @@ -320,7 +320,7 @@ int VmbusPostMessage(void *buffer, size_t bufferLen) int VmbusSetEvent(u32 childRelId) { /* Each u32 represents 32 channels */ - set_bit(childRelId & 31, + sync_set_bit(childRelId & 31, (unsigned long *)gVmbusConnection.SendInterruptPage + (childRelId >> 5)); diff --git a/drivers/staging/hv/vmbus_drv.c b/drivers/staging/hv/vmbus_drv.c index 84fdb64d3ceb2..87e6cf2086f08 100644 --- a/drivers/staging/hv/vmbus_drv.c +++ b/drivers/staging/hv/vmbus_drv.c @@ -291,7 +291,7 @@ static int vmbus_on_isr(struct hv_driver *drv) event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT; /* Since we are a child, we only need to check bit 0 */ - if (test_and_clear_bit(0, (unsigned long *) &event->flags32[0])) { + if (sync_test_and_clear_bit(0, (unsigned long *) &event->flags32[0])) { DPRINT_DBG(VMBUS, "received event %d", event->flags32[0]); ret |= 0x2; } diff --git a/drivers/staging/hv/vmbus_private.h b/drivers/staging/hv/vmbus_private.h index 07f6d22eeabb5..c75b2d7fb2f06 100644 --- a/drivers/staging/hv/vmbus_private.h +++ b/drivers/staging/hv/vmbus_private.h @@ -31,6 +31,7 @@ #include "channel_mgmt.h" #include "ring_buffer.h" #include +#include /* From 68abeb71d51d40b805cd3596cfc4f45105e57cad Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Wed, 6 Apr 2011 15:18:00 -0700 Subject: [PATCH 1079/2556] staging: hv: Fix GARP not sent after Quick Migration commit c996edcf1c451b81740abbcca5257ed7e353fcc6 upstream. After Quick Migration, the network is not immediately operational in the current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, I added another netif_notify_peers() into a scheduled work, otherwise GARP packet will not be sent after quick migration, and cause network disconnection. Thanks to Mike Surcouf for reporting the bug and testing the patch. Reported-by: Mike Surcouf Tested-by: Mike Surcouf Signed-off-by: Haiyang Zhang Signed-off-by: Hank Janssen Signed-off-by: Abhishek Kane Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/staging/hv/netvsc_drv.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/staging/hv/netvsc_drv.c b/drivers/staging/hv/netvsc_drv.c index b41c9640b72dc..f433addaae569 100644 --- a/drivers/staging/hv/netvsc_drv.c +++ b/drivers/staging/hv/netvsc_drv.c @@ -46,6 +46,7 @@ struct net_device_context { /* point back to our device context */ struct vm_device *device_ctx; unsigned long avail; + struct work_struct work; }; struct netvsc_driver_context { @@ -225,6 +226,7 @@ static void netvsc_linkstatus_callback(struct hv_device *device_obj, unsigned int status) { struct vm_device *device_ctx = to_vm_device(device_obj); + struct net_device_context *ndev_ctx; struct net_device *net = dev_get_drvdata(&device_ctx->device); if (!net) { @@ -237,6 +239,8 @@ static void netvsc_linkstatus_callback(struct hv_device *device_obj, netif_carrier_on(net); netif_wake_queue(net); netif_notify_peers(net); + ndev_ctx = netdev_priv(net); + schedule_work(&ndev_ctx->work); } else { netif_carrier_off(net); netif_stop_queue(net); @@ -336,6 +340,25 @@ static const struct net_device_ops device_ops = { .ndo_set_mac_address = eth_mac_addr, }; +/* + * Send GARP packet to network peers after migrations. + * After Quick Migration, the network is not immediately operational in the + * current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, add + * another netif_notify_peers() into a scheduled work, otherwise GARP packet + * will not be sent after quick migration, and cause network disconnection. + */ +static void netvsc_send_garp(struct work_struct *w) +{ + struct net_device_context *ndev_ctx; + struct net_device *net; + + msleep(20); + ndev_ctx = container_of(w, struct net_device_context, work); + net = dev_get_drvdata(&ndev_ctx->device_ctx->device); + netif_notify_peers(net); +} + + static int netvsc_probe(struct device *device) { struct driver_context *driver_ctx = @@ -364,6 +387,7 @@ static int netvsc_probe(struct device *device) net_device_ctx->device_ctx = device_ctx; net_device_ctx->avail = ring_size; dev_set_drvdata(device, net); + INIT_WORK(&net_device_ctx->work, netvsc_send_garp); /* Notify the netvsc driver of the new device */ ret = net_drv_obj->base.OnDeviceAdd(device_obj, &device_info); From 76e61a187353bb6798284196b874938a8fbe3db9 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Mon, 28 Mar 2011 14:13:35 -0700 Subject: [PATCH 1080/2556] Relax si_code check in rt_sigqueueinfo and rt_tgsigqueueinfo commit 243b422af9ea9af4ead07a8ad54c90d4f9b6081a upstream. Commit da48524eb206 ("Prevent rt_sigqueueinfo and rt_tgsigqueueinfo from spoofing the signal code") made the check on si_code too strict. There are several legitimate places where glibc wants to queue a negative si_code different from SI_QUEUE: - This was first noticed with glibc's aio implementation, which wants to queue a signal with si_code SI_ASYNCIO; the current kernel causes glibc's tst-aio4 test to fail because rt_sigqueueinfo() fails with EPERM. - Further examination of the glibc source shows that getaddrinfo_a() wants to use SI_ASYNCNL (which the kernel does not even define). The timer_create() fallback code wants to queue signals with SI_TIMER. As suggested by Oleg Nesterov , loosen the check to forbid only the problematic SI_TKILL case. Reported-by: Klaus Dittrich Acked-by: Julien Tinnes Signed-off-by: Roland Dreier Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/signal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/signal.c b/kernel/signal.c index 31751868de885..bf11d2697e9e2 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2423,7 +2423,7 @@ SYSCALL_DEFINE3(rt_sigqueueinfo, pid_t, pid, int, sig, /* Not even root can pretend to send signals from the kernel. * Nor can they impersonate a kill()/tgkill(), which adds source info. */ - if (info.si_code != SI_QUEUE) { + if (info.si_code >= 0 || info.si_code == SI_TKILL) { /* We used to allow any < 0 si_code */ WARN_ON_ONCE(info.si_code < 0); return -EPERM; @@ -2443,7 +2443,7 @@ long do_rt_tgsigqueueinfo(pid_t tgid, pid_t pid, int sig, siginfo_t *info) /* Not even root can pretend to send signals from the kernel. * Nor can they impersonate a kill()/tgkill(), which adds source info. */ - if (info->si_code != SI_QUEUE) { + if (info->si_code >= 0 || info->si_code == SI_TKILL) { /* We used to allow any < 0 si_code */ WARN_ON_ONCE(info->si_code < 0); return -EPERM; From 668c06b8508308dc98e2dcf644c340d3c15ef99c Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Sat, 26 Mar 2011 09:14:57 +1100 Subject: [PATCH 1081/2556] xfs: register the inode cache shrinker before quotachecks commit 704b2907c2d47ceb187c0e25a6bbc2174b198f2f upstream. During mount, we can do a quotacheck that involves a bulkstat pass on all inodes. If there are more inodes in the filesystem than can be held in memory, we require the inode cache shrinker to run to ensure that we don't run out of memory. Unfortunately, the inode cache shrinker is not registered until we get to the end of the superblock setup process, which is after a quotacheck is run if it is needed. Hence we need to register the inode cache shrinker earlier in the mount process so that we don't OOM during mount. This requires that we also initialise the syncd work before we register the shrinker, so we nee dto juggle that around as well. While there, make sure that we have set up the block sizes in the VFS superblock correctly before the quotacheck is run so that any inodes that are cached as a result of the quotacheck have their block size fields set up correctly. Signed-off-by: Dave Chinner Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- fs/xfs/linux-2.6/xfs_super.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 9731898083ae8..ad485b60340fb 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -1551,10 +1551,14 @@ xfs_fs_fill_super( if (error) goto out_free_sb; - error = xfs_mountfs(mp); - if (error) - goto out_filestream_unmount; - + /* + * we must configure the block size in the superblock before we run the + * full mount process as the mount process can lookup and cache inodes. + * For the same reason we must also initialise the syncd and register + * the inode cache shrinker so that inodes can be reclaimed during + * operations like a quotacheck that iterate all inodes in the + * filesystem. + */ sb->s_magic = XFS_SB_MAGIC; sb->s_blocksize = mp->m_sb.sb_blocksize; sb->s_blocksize_bits = ffs(sb->s_blocksize) - 1; @@ -1562,6 +1566,16 @@ xfs_fs_fill_super( sb->s_time_gran = 1; set_posix_acl_flag(sb); + error = xfs_syncd_init(mp); + if (error) + goto out_filestream_unmount; + + xfs_inode_shrinker_register(mp); + + error = xfs_mountfs(mp); + if (error) + goto out_syncd_stop; + root = igrab(VFS_I(mp->m_rootip)); if (!root) { error = ENOENT; @@ -1577,14 +1591,11 @@ xfs_fs_fill_super( goto fail_vnrele; } - error = xfs_syncd_init(mp); - if (error) - goto fail_vnrele; - - xfs_inode_shrinker_register(mp); - return 0; + out_syncd_stop: + xfs_inode_shrinker_unregister(mp); + xfs_syncd_stop(mp); out_filestream_unmount: xfs_filestream_unmount(mp); out_free_sb: @@ -1608,6 +1619,9 @@ xfs_fs_fill_super( } fail_unmount: + xfs_inode_shrinker_unregister(mp); + xfs_syncd_stop(mp); + /* * Blow away any referenced inode in the filestreams cache. * This can and will cause log traffic as inodes go inactive From 53a624a9467771174585e85b4b6b3d7f23ec9526 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 29 Mar 2011 18:10:53 +0200 Subject: [PATCH 1082/2556] amd64_edac: Fix potential memleak commit a9f0fbe2bbf328f869fc5ee5a12c6a4118c32689 upstream. We check the pointers together but at least one of them could be invalid due to failed allocation. Since we cannot continue if either of the two allocations has failed, exit early by freeing them both. Reported-by: Mauro Carvalho Chehab Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/edac/amd64_edac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 23e03554f0d3a..7e0e66037e025 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -2765,7 +2765,7 @@ static int __init amd64_edac_init(void) mcis = kzalloc(amd_nb_num() * sizeof(mcis[0]), GFP_KERNEL); ecc_stngs = kzalloc(amd_nb_num() * sizeof(ecc_stngs[0]), GFP_KERNEL); if (!(mcis && ecc_stngs)) - goto err_ret; + goto err_free; msrs = msrs_alloc(); if (!msrs) From 665c7153a7d73d524ed6b4783c3bd8a521285a9b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 26 Feb 2011 17:34:38 +0100 Subject: [PATCH 1083/2556] watchdog: s3c2410_wdt.c: Convert release_resource to release_region/release_mem_region commit f72401e94d159bc4b2beab51d74e956da2c32e0a upstream. Request_mem_region should be used with release_mem_region, not release_resource. The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression x,E; @@ *x = request_mem_region(...) ... when != release_mem_region(x) when != x = E * release_resource(x); // Signed-off-by: Julia Lawall Signed-off-by: Wim Van Sebroeck Signed-off-by: Greg Kroah-Hartman --- drivers/watchdog/s3c2410_wdt.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index ae53662c29bce..8303c576c57e0 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -402,7 +402,6 @@ static inline void s3c2410wdt_cpufreq_deregister(void) static int __devinit s3c2410wdt_probe(struct platform_device *pdev) { - struct resource *res; struct device *dev; unsigned int wtcon; int started = 0; @@ -416,20 +415,19 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) /* get the memory region for the watchdog timer */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { + wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (wdt_mem == NULL) { dev_err(dev, "no memory resource specified\n"); return -ENOENT; } - size = resource_size(res); - wdt_mem = request_mem_region(res->start, size, pdev->name); - if (wdt_mem == NULL) { + size = resource_size(wdt_mem); + if (!request_mem_region(wdt_mem->start, size, pdev->name)) { dev_err(dev, "failed to get memory region\n"); return -EBUSY; } - wdt_base = ioremap(res->start, size); + wdt_base = ioremap(wdt_mem->start, size); if (wdt_base == NULL) { dev_err(dev, "failed to ioremap() region\n"); ret = -EINVAL; @@ -524,8 +522,8 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) iounmap(wdt_base); err_req: - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; return ret; } @@ -545,8 +543,7 @@ static int __devexit s3c2410wdt_remove(struct platform_device *dev) iounmap(wdt_base); - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, resource_size(wdt_mem)); wdt_mem = NULL; return 0; } From 984814d5a002305f9f53ec8e8d3bbefc30a13583 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 26 Feb 2011 17:34:39 +0100 Subject: [PATCH 1084/2556] watchdog: Convert release_resource to release_region/release_mem_region commit f712eacf02ecfbf4f1686addb8c569841549b0b7 upstream. Request_mem_region should be used with release_mem_region, not release_resource. In pnx4008_wdt.c, a missing clk_put is added as well. The semantic match that finds the first problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression x,E; @@ *x = request_mem_region(...) ... when != release_mem_region(x) when != x = E * release_resource(x); // Signed-off-by: Julia Lawall Signed-off-by: Wim Van Sebroeck Signed-off-by: Greg Kroah-Hartman --- drivers/watchdog/davinci_wdt.c | 22 ++++++++++------------ drivers/watchdog/max63xx_wdt.c | 20 ++++++++------------ drivers/watchdog/pnx4008_wdt.c | 28 +++++++++++++--------------- 3 files changed, 31 insertions(+), 39 deletions(-) diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c index 596ba604e78d1..51b5551b4e3fe 100644 --- a/drivers/watchdog/davinci_wdt.c +++ b/drivers/watchdog/davinci_wdt.c @@ -202,7 +202,6 @@ static struct miscdevice davinci_wdt_miscdev = { static int __devinit davinci_wdt_probe(struct platform_device *pdev) { int ret = 0, size; - struct resource *res; struct device *dev = &pdev->dev; wdt_clk = clk_get(dev, NULL); @@ -216,31 +215,31 @@ static int __devinit davinci_wdt_probe(struct platform_device *pdev) dev_info(dev, "heartbeat %d sec\n", heartbeat); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { + wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (wdt_mem == NULL) { dev_err(dev, "failed to get memory region resource\n"); return -ENOENT; } - size = resource_size(res); - wdt_mem = request_mem_region(res->start, size, pdev->name); - - if (wdt_mem == NULL) { + size = resource_size(wdt_mem); + if (!request_mem_region(wdt_mem->start, size, pdev->name)) { dev_err(dev, "failed to get memory region\n"); return -ENOENT; } - wdt_base = ioremap(res->start, size); + wdt_base = ioremap(wdt_mem->start, size); if (!wdt_base) { dev_err(dev, "failed to map memory region\n"); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; return -ENOMEM; } ret = misc_register(&davinci_wdt_miscdev); if (ret < 0) { dev_err(dev, "cannot register misc device\n"); - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; } else { set_bit(WDT_DEVICE_INITED, &wdt_status); } @@ -253,8 +252,7 @@ static int __devexit davinci_wdt_remove(struct platform_device *pdev) { misc_deregister(&davinci_wdt_miscdev); if (wdt_mem) { - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, resource_size(wdt_mem)); wdt_mem = NULL; } diff --git a/drivers/watchdog/max63xx_wdt.c b/drivers/watchdog/max63xx_wdt.c index 3053ff05ca410..1fe9bc5a96517 100644 --- a/drivers/watchdog/max63xx_wdt.c +++ b/drivers/watchdog/max63xx_wdt.c @@ -270,7 +270,6 @@ static int __devinit max63xx_wdt_probe(struct platform_device *pdev) { int ret = 0; int size; - struct resource *res; struct device *dev = &pdev->dev; struct max63xx_timeout *table; @@ -294,21 +293,19 @@ static int __devinit max63xx_wdt_probe(struct platform_device *pdev) max63xx_pdev = pdev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { + wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (wdt_mem == NULL) { dev_err(dev, "failed to get memory region resource\n"); return -ENOENT; } - size = resource_size(res); - wdt_mem = request_mem_region(res->start, size, pdev->name); - - if (wdt_mem == NULL) { + size = resource_size(wdt_mem); + if (!request_mem_region(wdt_mem->start, size, pdev->name)) { dev_err(dev, "failed to get memory region\n"); return -ENOENT; } - wdt_base = ioremap(res->start, size); + wdt_base = ioremap(wdt_mem->start, size); if (!wdt_base) { dev_err(dev, "failed to map memory region\n"); ret = -ENOMEM; @@ -326,8 +323,8 @@ static int __devinit max63xx_wdt_probe(struct platform_device *pdev) out_unmap: iounmap(wdt_base); out_request: - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; return ret; } @@ -336,8 +333,7 @@ static int __devexit max63xx_wdt_remove(struct platform_device *pdev) { misc_deregister(&max63xx_wdt_miscdev); if (wdt_mem) { - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, resource_size(wdt_mem)); wdt_mem = NULL; } diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c index bf5b97c546eb4..8c8c7d54497c4 100644 --- a/drivers/watchdog/pnx4008_wdt.c +++ b/drivers/watchdog/pnx4008_wdt.c @@ -254,7 +254,6 @@ static struct miscdevice pnx4008_wdt_miscdev = { static int __devinit pnx4008_wdt_probe(struct platform_device *pdev) { int ret = 0, size; - struct resource *res; if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) heartbeat = DEFAULT_HEARTBEAT; @@ -262,42 +261,42 @@ static int __devinit pnx4008_wdt_probe(struct platform_device *pdev) printk(KERN_INFO MODULE_NAME "PNX4008 Watchdog Timer: heartbeat %d sec\n", heartbeat); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { + wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (wdt_mem == NULL) { printk(KERN_INFO MODULE_NAME "failed to get memory region resouce\n"); return -ENOENT; } - size = resource_size(res); - wdt_mem = request_mem_region(res->start, size, pdev->name); + size = resource_size(wdt_mem); - if (wdt_mem == NULL) { + if (!request_mem_region(wdt_mem->start, size, pdev->name)) { printk(KERN_INFO MODULE_NAME "failed to get memory region\n"); return -ENOENT; } - wdt_base = (void __iomem *)IO_ADDRESS(res->start); + wdt_base = (void __iomem *)IO_ADDRESS(wdt_mem->start); wdt_clk = clk_get(&pdev->dev, NULL); if (IS_ERR(wdt_clk)) { ret = PTR_ERR(wdt_clk); - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; goto out; } ret = clk_enable(wdt_clk); if (ret) { - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; + clk_put(wdt_clk); goto out; } ret = misc_register(&pnx4008_wdt_miscdev); if (ret < 0) { printk(KERN_ERR MODULE_NAME "cannot register misc device\n"); - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; clk_disable(wdt_clk); clk_put(wdt_clk); } else { @@ -320,8 +319,7 @@ static int __devexit pnx4008_wdt_remove(struct platform_device *pdev) clk_put(wdt_clk); if (wdt_mem) { - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, resource_size(wdt_mem)); wdt_mem = NULL; } return 0; From 7910591c1c2663dd3f421f5a80fbd5c439af3aef Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Wed, 16 Mar 2011 20:01:07 -0700 Subject: [PATCH 1085/2556] watchdog: sp5100_tco.c: Check if firmware has set correct value in tcobase. commit 90d241edd13bdeef70f264b569f7e150bf23621e upstream. Stefano found SP5100 TCO watchdog driver using wrong address. [ 9.148536] SP5100 TCO timer: SP5100 TCO WatchDog Timer Driver v0.01 [ 9.148628] DEBUG __ioremap_caller WARNING address=b8fe00 size=8 valid=1 reserved=1 and e820 said that range is RAM. We should check if we can use that reading out. BIOS could just program wrong address there. Reported-by: Stefano Stabellini Signed-off-by:Yinghai Lu Acked-by: Mike Waychison Tested-by: Konrad Rzeszutek Wilk Signed-off-by: Wim Van Sebroeck Signed-off-by: Greg Kroah-Hartman --- drivers/watchdog/sp5100_tco.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 808372883e88e..c7ea4bedfe63e 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -42,6 +42,7 @@ #define PFX TCO_MODULE_NAME ": " /* internal variables */ +static u32 tcobase_phys; static void __iomem *tcobase; static unsigned int pm_iobase; static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */ @@ -305,10 +306,18 @@ static unsigned char __devinit sp5100_tco_setupdevice(void) /* Low three bits of BASE0 are reserved. */ val = val << 8 | (inb(SP5100_IO_PM_DATA_REG) & 0xf8); + if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, + "SP5100 TCO")) { + printk(KERN_ERR PFX "mmio address 0x%04x already in use\n", + val); + goto unreg_region; + } + tcobase_phys = val; + tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE); if (tcobase == 0) { printk(KERN_ERR PFX "failed to get tcobase address\n"); - goto unreg_region; + goto unreg_mem_region; } /* Enable watchdog decode bit */ @@ -346,7 +355,8 @@ static unsigned char __devinit sp5100_tco_setupdevice(void) /* Done */ return 1; - iounmap(tcobase); +unreg_mem_region: + release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); unreg_region: release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); exit: @@ -401,6 +411,7 @@ static int __devinit sp5100_tco_init(struct platform_device *dev) exit: iounmap(tcobase); + release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); return ret; } @@ -414,6 +425,7 @@ static void __devexit sp5100_tco_cleanup(void) /* Deregister */ misc_deregister(&sp5100_tco_miscdev); iounmap(tcobase); + release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); } From cfc637297be0a982188b60be83096b40dbf35070 Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Sun, 20 Mar 2011 15:32:06 +0000 Subject: [PATCH 1086/2556] irda: validate peer name and attribute lengths commit d370af0ef7951188daeb15bae75db7ba57c67846 upstream. Length fields provided by a peer for names and attributes may be longer than the destination array sizes. Validate lengths to prevent stack buffer overflows. Signed-off-by: Dan Rosenberg Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/irda/iriap.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/irda/iriap.c b/net/irda/iriap.c index 5b743bdd89ba2..36477538cea8e 100644 --- a/net/irda/iriap.c +++ b/net/irda/iriap.c @@ -656,10 +656,16 @@ static void iriap_getvaluebyclass_indication(struct iriap_cb *self, n = 1; name_len = fp[n++]; + + IRDA_ASSERT(name_len < IAS_MAX_CLASSNAME + 1, return;); + memcpy(name, fp+n, name_len); n+=name_len; name[name_len] = '\0'; attr_len = fp[n++]; + + IRDA_ASSERT(attr_len < IAS_MAX_ATTRIBNAME + 1, return;); + memcpy(attr, fp+n, attr_len); n+=attr_len; attr[attr_len] = '\0'; From f7b5886892a642bfdc39619a63794d17ec333896 Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Sat, 19 Mar 2011 20:14:30 +0000 Subject: [PATCH 1087/2556] irda: prevent heap corruption on invalid nickname commit d50e7e3604778bfc2dc40f440e0742dbae399d54 upstream. Invalid nicknames containing only spaces will result in an underflow in a memcpy size calculation, subsequently destroying the heap and panicking. v2 also catches the case where the provided nickname is longer than the buffer size, which can result in controllable heap corruption. Signed-off-by: Dan Rosenberg Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/irda/irnet/irnet_ppp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/irda/irnet/irnet_ppp.c b/net/irda/irnet/irnet_ppp.c index 7c567b8aa89a9..2bb2beb6a373d 100644 --- a/net/irda/irnet/irnet_ppp.c +++ b/net/irda/irnet/irnet_ppp.c @@ -105,6 +105,9 @@ irnet_ctrl_write(irnet_socket * ap, while(isspace(start[length - 1])) length--; + DABORT(length < 5 || length > NICKNAME_MAX_LEN + 5, + -EINVAL, CTRL_ERROR, "Invalid nickname.\n"); + /* Copy the name for later reuse */ memcpy(ap->rname, start + 5, length - 5); ap->rname[length - 5] = '\0'; From 81dacc2d64030457bfd85ba901deb17a682e6196 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 20 Mar 2011 15:28:03 +0000 Subject: [PATCH 1088/2556] powerpc: Fix accounting of softirq time when idle commit ad5d1c888e556bc00c4e86f452cad4a3a87d22c1 upstream. commit cf9efce0ce31 (powerpc: Account time using timebase rather than PURR) used in_irq() to detect if the time was spent in interrupt processing. This only catches hardirq context so if we are in softirq context and in the idle loop we end up accounting it as idle time. If we instead use in_interrupt() we catch both softirq and hardirq time. The issue was found when running a network intensive workload. top showed the following: 0.0%us, 1.1%sy, 0.0%ni, 85.7%id, 0.0%wa, 9.9%hi, 3.3%si, 0.0%st 85.7% idle. But this was wildly different to the perf events data. To confirm the suspicion I ran something to keep the core busy: # yes > /dev/null & 8.2%us, 0.0%sy, 0.0%ni, 0.0%id, 0.0%wa, 10.3%hi, 81.4%si, 0.0%st We only got 8.2% of the CPU for the userspace task and softirq has shot up to 81.4%. With the patch below top shows the correct stats: 0.0%us, 0.0%sy, 0.0%ni, 5.3%id, 0.0%wa, 13.3%hi, 81.3%si, 0.0%st Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 09d31dbf43f99..aa9269600ca21 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -356,7 +356,7 @@ void account_system_vtime(struct task_struct *tsk) } get_paca()->user_time_scaled += user_scaled; - if (in_irq() || idle_task(smp_processor_id()) != tsk) { + if (in_interrupt() || idle_task(smp_processor_id()) != tsk) { account_system_time(tsk, 0, delta, sys_scaled); if (stolen) account_steal_time(stolen); From 78df5839e68ea51a460b6ab7c266049c583014dc Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 27 Mar 2011 22:50:49 +0900 Subject: [PATCH 1089/2556] nilfs2: fix data loss in mmap page write for hole blocks commit 34094537943113467faee98fe67c8a3d3f9a0a8b upstream. From the result of a function test of mmap, mmap write to shared pages turned out to be broken for hole blocks. It doesn't write out filled blocks and the data will be lost after umount. This is due to a bug that the target file is not queued for log writer when filling hole blocks. Also, nilfs_page_mkwrite function exits normal code path even after successfully filled hole blocks due to a change of block_page_mkwrite function; just after nilfs was merged into the mainline, block_page_mkwrite() started to return VM_FAULT_LOCKED instead of zero by the patch "mm: close page_mkwrite races" (commit: b827e496c893de0c). The current nilfs_page_mkwrite() is not handling this value properly. This corrects nilfs_page_mkwrite() and will resolve the data loss problem in mmap write. [This should be applied to every kernel since 2.6.30 but a fix is needed for 2.6.37 and prior kernels] Signed-off-by: Ryusuke Konishi Tested-by: Ryusuke Konishi Signed-off-by: Greg Kroah-Hartman --- fs/nilfs2/file.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/nilfs2/file.c b/fs/nilfs2/file.c index 2f560c9fb8081..f49e6287c879b 100644 --- a/fs/nilfs2/file.c +++ b/fs/nilfs2/file.c @@ -72,10 +72,9 @@ static int nilfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) /* * check to see if the page is mapped already (no holes) */ - if (PageMappedToDisk(page)) { - unlock_page(page); + if (PageMappedToDisk(page)) goto mapped; - } + if (page_has_buffers(page)) { struct buffer_head *bh, *head; int fully_mapped = 1; @@ -90,7 +89,6 @@ static int nilfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) if (fully_mapped) { SetPageMappedToDisk(page); - unlock_page(page); goto mapped; } } @@ -105,16 +103,17 @@ static int nilfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) return VM_FAULT_SIGBUS; ret = block_page_mkwrite(vma, vmf, nilfs_get_block); - if (unlikely(ret)) { + if (ret != VM_FAULT_LOCKED) { nilfs_transaction_abort(inode->i_sb); return ret; } + nilfs_set_file_dirty(inode, 1 << (PAGE_SHIFT - inode->i_blkbits)); nilfs_transaction_commit(inode->i_sb); mapped: SetPageChecked(page); wait_on_page_writeback(page); - return 0; + return VM_FAULT_LOCKED; } static const struct vm_operations_struct nilfs_file_vm_ops = { From e70216d4a904397fb6885f633ca4ec7fc887f337 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Mar 2011 20:45:40 +0000 Subject: [PATCH 1090/2556] ASoC: Explicitly say registerless widgets have no register commit 0ca03cd7d0fa3bfbd56958136a10f19733c4ce12 upstream. This stops code that handles widgets generically from attempting to access registers for these widgets. Signed-off-by: Mark Brown Acked-by: Liam Girdwood Signed-off-by: Greg Kroah-Hartman --- include/sound/soc-dapm.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 8031769ac4858..60f94fbd3a10d 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -45,25 +45,25 @@ /* platform domain */ #define SND_SOC_DAPM_INPUT(wname) \ { .id = snd_soc_dapm_input, .name = wname, .kcontrols = NULL, \ - .num_kcontrols = 0} + .num_kcontrols = 0, .reg = SND_SOC_NOPM } #define SND_SOC_DAPM_OUTPUT(wname) \ { .id = snd_soc_dapm_output, .name = wname, .kcontrols = NULL, \ - .num_kcontrols = 0} + .num_kcontrols = 0, .reg = SND_SOC_NOPM } #define SND_SOC_DAPM_MIC(wname, wevent) \ { .id = snd_soc_dapm_mic, .name = wname, .kcontrols = NULL, \ - .num_kcontrols = 0, .event = wevent, \ + .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD} #define SND_SOC_DAPM_HP(wname, wevent) \ { .id = snd_soc_dapm_hp, .name = wname, .kcontrols = NULL, \ - .num_kcontrols = 0, .event = wevent, \ + .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} #define SND_SOC_DAPM_SPK(wname, wevent) \ { .id = snd_soc_dapm_spk, .name = wname, .kcontrols = NULL, \ - .num_kcontrols = 0, .event = wevent, \ + .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} #define SND_SOC_DAPM_LINE(wname, wevent) \ { .id = snd_soc_dapm_line, .name = wname, .kcontrols = NULL, \ - .num_kcontrols = 0, .event = wevent, \ + .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} /* path domain */ @@ -177,11 +177,11 @@ /* events that are pre and post DAPM */ #define SND_SOC_DAPM_PRE(wname, wevent) \ { .id = snd_soc_dapm_pre, .name = wname, .kcontrols = NULL, \ - .num_kcontrols = 0, .event = wevent, \ + .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD} #define SND_SOC_DAPM_POST(wname, wevent) \ { .id = snd_soc_dapm_post, .name = wname, .kcontrols = NULL, \ - .num_kcontrols = 0, .event = wevent, \ + .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD} /* stream domain */ From ad5d054f79f20dfaecf23322783e652eee303d98 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 25 Mar 2011 16:51:44 +0100 Subject: [PATCH 1091/2556] ASoC: imx: set watermarks for mx2-dma commit 2c4cf17a52f04fbe929977252d5b8ab81d2c6e9b upstream. They got accidently removed by f0fba2a (ASoC: multi-component - ASoC Multi-Component Support). Reintroduce them and get rid of the superfluous defines because the fiq-driver has its own hardcoded values. Signed-off-by: Wolfram Sang Acked-by: Liam Girdwood Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/imx/imx-pcm-dma-mx2.c | 5 +++++ sound/soc/imx/imx-ssi.h | 3 --- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c index 671ef8dd524cb..b2ed764fd8962 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/imx/imx-pcm-dma-mx2.c @@ -303,6 +303,11 @@ static struct snd_soc_platform_driver imx_soc_platform_mx2 = { static int __devinit imx_soc_platform_probe(struct platform_device *pdev) { + struct imx_ssi *ssi = platform_get_drvdata(pdev); + + ssi->dma_params_tx.burstsize = 6; + ssi->dma_params_rx.burstsize = 4; + return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2); } diff --git a/sound/soc/imx/imx-ssi.h b/sound/soc/imx/imx-ssi.h index a4406a1348927..dc8a87530e3e9 100644 --- a/sound/soc/imx/imx-ssi.h +++ b/sound/soc/imx/imx-ssi.h @@ -234,7 +234,4 @@ void imx_pcm_free(struct snd_pcm *pcm); */ #define IMX_SSI_DMABUF_SIZE (64 * 1024) -#define DMA_RXFIFO_BURST 0x4 -#define DMA_TXFIFO_BURST 0x6 - #endif /* _IMX_SSI_H */ From e34910dd0fcf32e9a0e5ff6f3249910cff4c06d8 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 25 Mar 2011 16:51:45 +0100 Subject: [PATCH 1092/2556] ASoC: imx: fix burstsize for DMA commit e1bb31b444668bc957c337d33803db7cb3330745 upstream. SSI counts in words, the DMA engine in bytes. (Wrong) factor got removed in bf974a0 (ASoC i.MX: switch to new DMA api). Signed-off-by: Wolfram Sang Acked-by: Liam Girdwood Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/imx/imx-pcm-dma-mx2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c index b2ed764fd8962..aab7765f401a0 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/imx/imx-pcm-dma-mx2.c @@ -110,12 +110,12 @@ static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream, slave_config.direction = DMA_TO_DEVICE; slave_config.dst_addr = dma_params->dma_addr; slave_config.dst_addr_width = buswidth; - slave_config.dst_maxburst = dma_params->burstsize; + slave_config.dst_maxburst = dma_params->burstsize * buswidth; } else { slave_config.direction = DMA_FROM_DEVICE; slave_config.src_addr = dma_params->dma_addr; slave_config.src_addr_width = buswidth; - slave_config.src_maxburst = dma_params->burstsize; + slave_config.src_maxburst = dma_params->burstsize * buswidth; } ret = dmaengine_slave_config(iprtd->dma_chan, &slave_config); From 1fa9ba2832bd843ecc4a155896fadc836178e799 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 27 Mar 2011 14:40:01 +0100 Subject: [PATCH 1093/2556] ASoC: Fix CODEC device name for Corgi commit 326b9bdc2a0e4d556a0f444085dca103bcd505de upstream. Got typoed in the multi-component changes. Signed-off-by: Mark Brown Acked-by: Liam Girdwood Signed-off-by: Greg Kroah-Hartman --- sound/soc/pxa/corgi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index 784cff5f67e81..9027da466caea 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -310,7 +310,7 @@ static struct snd_soc_dai_link corgi_dai = { .cpu_dai_name = "pxa2xx-i2s", .codec_dai_name = "wm8731-hifi", .platform_name = "pxa-pcm-audio", - .codec_name = "wm8731-codec-0.001b", + .codec_name = "wm8731-codec.0-001b", .init = corgi_wm8731_init, .ops = &corgi_ops, }; From 4454ec7e0b90ab5b26d9d9dcd7a025a40bfa7c1d Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 30 Mar 2011 08:24:25 +0200 Subject: [PATCH 1094/2556] ALSA: ens1371: fix Creative Ectiva support commit 6ebb8a4a43e34f999ab36f27f972f3cd751cda4f upstream. To make the EV1938 chip work, add a magic bit and an extra delay. Signed-off-by: Clemens Ladisch Tested-by: Tino Schmidt Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/ens1370.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 537cfba829a58..863eafea691f2 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -229,6 +229,7 @@ MODULE_PARM_DESC(lineio, "Line In to Rear Out (0 = auto, 1 = force)."); #define ES_REG_1371_CODEC 0x14 /* W/R: Codec Read/Write register address */ #define ES_1371_CODEC_RDY (1<<31) /* codec ready */ #define ES_1371_CODEC_WIP (1<<30) /* codec register access in progress */ +#define EV_1938_CODEC_MAGIC (1<<26) #define ES_1371_CODEC_PIRD (1<<23) /* codec read/write select register */ #define ES_1371_CODEC_WRITE(a,d) ((((a)&0x7f)<<16)|(((d)&0xffff)<<0)) #define ES_1371_CODEC_READS(a) ((((a)&0x7f)<<16)|ES_1371_CODEC_PIRD) @@ -603,12 +604,18 @@ static void snd_es1370_codec_write(struct snd_ak4531 *ak4531, #ifdef CHIP1371 +static inline bool is_ev1938(struct ensoniq *ensoniq) +{ + return ensoniq->pci->device == 0x8938; +} + static void snd_es1371_codec_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) { struct ensoniq *ensoniq = ac97->private_data; - unsigned int t, x; + unsigned int t, x, flag; + flag = is_ev1938(ensoniq) ? EV_1938_CODEC_MAGIC : 0; mutex_lock(&ensoniq->src_mutex); for (t = 0; t < POLL_COUNT; t++) { if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) { @@ -630,7 +637,8 @@ static void snd_es1371_codec_write(struct snd_ac97 *ac97, 0x00010000) break; } - outl(ES_1371_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1371_CODEC)); + outl(ES_1371_CODEC_WRITE(reg, val) | flag, + ES_REG(ensoniq, 1371_CODEC)); /* restore SRC reg */ snd_es1371_wait_src_ready(ensoniq); outl(x, ES_REG(ensoniq, 1371_SMPRATE)); @@ -647,8 +655,9 @@ static unsigned short snd_es1371_codec_read(struct snd_ac97 *ac97, unsigned short reg) { struct ensoniq *ensoniq = ac97->private_data; - unsigned int t, x, fail = 0; + unsigned int t, x, flag, fail = 0; + flag = is_ev1938(ensoniq) ? EV_1938_CODEC_MAGIC : 0; __again: mutex_lock(&ensoniq->src_mutex); for (t = 0; t < POLL_COUNT; t++) { @@ -671,7 +680,8 @@ static unsigned short snd_es1371_codec_read(struct snd_ac97 *ac97, 0x00010000) break; } - outl(ES_1371_CODEC_READS(reg), ES_REG(ensoniq, 1371_CODEC)); + outl(ES_1371_CODEC_READS(reg) | flag, + ES_REG(ensoniq, 1371_CODEC)); /* restore SRC reg */ snd_es1371_wait_src_ready(ensoniq); outl(x, ES_REG(ensoniq, 1371_SMPRATE)); @@ -683,6 +693,11 @@ static unsigned short snd_es1371_codec_read(struct snd_ac97 *ac97, /* now wait for the stinkin' data (RDY) */ for (t = 0; t < POLL_COUNT; t++) { if ((x = inl(ES_REG(ensoniq, 1371_CODEC))) & ES_1371_CODEC_RDY) { + if (is_ev1938(ensoniq)) { + for (t = 0; t < 100; t++) + inl(ES_REG(ensoniq, CONTROL)); + x = inl(ES_REG(ensoniq, 1371_CODEC)); + } mutex_unlock(&ensoniq->src_mutex); return ES_1371_CODEC_READ(x); } From 254e648fde86796dd76175133448d9c0470e57ab Mon Sep 17 00:00:00 2001 From: Kelly Anderson Date: Fri, 1 Apr 2011 11:58:25 +0200 Subject: [PATCH 1095/2556] ALSA: pcm: fix infinite loop in snd_pcm_update_hw_ptr0() commit 12ff414e2e4512f59fe191dc18e856e2939a1c79 upstream. When period interrupts are disabled, snd_pcm_update_hw_ptr0() compares the current time against the time estimated for the current hardware pointer to detect xruns. The somewhat fuzzy threshold in the while loop makes it possible that hdelta becomes negative; the comparison being done with unsigned types then makes the loop go through the entire 263 negative range, and, depending on the value, never reach an unsigned value that is small enough to stop the loop. Doing this with interrupts disabled results in the machine locking up. To prevent this, ensure that the loop condition uses signed types for both operands so that the comparison is correctly done. Many thanks to Kelly Anderson for debugging this. Reported-by: Nix Reported-by: "Christopher K." Reported-and-tested-by: Kelly Anderson Signed-off-by: Kelly Anderson [cl: remove unneeded casts; use a temp variable] Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/pcm_lib.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index a82e3756a72d8..64449cb8f8737 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -375,6 +375,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, } if (runtime->no_period_wakeup) { + snd_pcm_sframes_t xrun_threshold; /* * Without regular period interrupts, we have to check * the elapsed time to detect xruns. @@ -383,7 +384,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, if (jdelta < runtime->hw_ptr_buffer_jiffies / 2) goto no_delta_check; hdelta = jdelta - delta * HZ / runtime->rate; - while (hdelta > runtime->hw_ptr_buffer_jiffies / 2 + 1) { + xrun_threshold = runtime->hw_ptr_buffer_jiffies / 2 + 1; + while (hdelta > xrun_threshold) { delta += runtime->buffer_size; hw_base += runtime->buffer_size; if (hw_base >= runtime->boundary) From 8cb2913bdc74f4a53c11455a2024bf9dbb699cb1 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Thu, 31 Mar 2011 09:36:19 +0200 Subject: [PATCH 1096/2556] ALSA: HDA: Add dock mic quirk for Lenovo Thinkpad X220 commit 840126579da56edae8ecc4a0d85198f742982f10 upstream. This quirk is needed for the docking station mic of Lenovo Thinkpad X220 to function correctly. BugLink: http://bugs.launchpad.net/bugs/746259 Tested-by: James Ferguson Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_conexant.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 4d5004e693f03..158a4236760fa 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -3130,6 +3130,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS), SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS), SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT5066_IDEAPAD), /* Fallback for Lenovos without dock mic */ {} From 9fd832d903aa50c9386bd208f62262387d419e87 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 5 Apr 2011 07:55:24 +0200 Subject: [PATCH 1097/2556] ALSA: HDA: Fix dock mic for Lenovo X220-tablet commit b2cb1292b1c7c73abbdc0e07ef3aab056fc2615f upstream. Without the "thinkpad" quirk, the dock mic in Lenovo X220 tablet edition won't work. BugLink: http://bugs.launchpad.net/bugs/751033 Tested-by: James Ferguson Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_conexant.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 158a4236760fa..e33d69eea7989 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -3131,6 +3131,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS), SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT5066_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS), SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT5066_IDEAPAD), /* Fallback for Lenovos without dock mic */ {} From 2e6de29f28f83051b83856237e331f6552f1276b Mon Sep 17 00:00:00 2001 From: Aaron Plattner Date: Wed, 6 Apr 2011 17:19:04 -0700 Subject: [PATCH 1098/2556] ALSA: hda - HDMI: Fix MCP7x audio infoframe checksums commit 1f348522844bb1f6e7b10d50b9e8aa89a2511b09 upstream. The MCP7x hardware computes the audio infoframe channel count automatically, but requires the audio driver to set the audio infoframe checksum manually via the Nv_VERB_SET_Info_Frame_Checksum control verb. When audio starts playing, nvhdmi_8ch_7x_pcm_prepare sets the checksum to (0x71 - chan - chanmask). For example, for 2ch audio, chan == 1 and chanmask == 0 so the checksum is set to 0x70. When audio playback finishes and the device is closed, nvhdmi_8ch_7x_pcm_close resets the channel formats, causing the channel count to revert to 8ch. Since the checksum is not reset, the hardware starts generating audio infoframes with invalid checksums. This causes some displays to blank the video. Fix this by updating the checksum and channel mask when the device is closed and also when it is first initialized. In addition, make sure that the channel mask is appropriate for an 8ch infoframe by setting it to 0x13 (FL FR LFE FC RL RR RLC RRC). Signed-off-by: Aaron Plattner Acked-by: Stephen Warren Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_hdmi.c | 70 ++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index ec0fa2dd0a279..520f94a411658 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1276,6 +1276,39 @@ static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo, stream_tag, format, substream); } +static void nvhdmi_8ch_7x_set_info_frame_parameters(struct hda_codec *codec, + int channels) +{ + unsigned int chanmask; + int chan = channels ? (channels - 1) : 1; + + switch (channels) { + default: + case 0: + case 2: + chanmask = 0x00; + break; + case 4: + chanmask = 0x08; + break; + case 6: + chanmask = 0x0b; + break; + case 8: + chanmask = 0x13; + break; + } + + /* Set the audio infoframe channel allocation and checksum fields. The + * channel count is computed implicitly by the hardware. */ + snd_hda_codec_write(codec, 0x1, 0, + Nv_VERB_SET_Channel_Allocation, chanmask); + + snd_hda_codec_write(codec, 0x1, 0, + Nv_VERB_SET_Info_Frame_Checksum, + (0x71 - chan - chanmask)); +} + static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) @@ -1294,6 +1327,10 @@ static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo, AC_VERB_SET_STREAM_FORMAT, 0); } + /* The audio hardware sends a channel count of 0x7 (8ch) when all the + * streams are disabled. */ + nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8); + return snd_hda_multi_out_dig_close(codec, &spec->multiout); } @@ -1304,37 +1341,16 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { int chs; - unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id; + unsigned int dataDCC1, dataDCC2, channel_id; int i; mutex_lock(&codec->spdif_mutex); chs = substream->runtime->channels; - chan = chs ? (chs - 1) : 1; - switch (chs) { - default: - case 0: - case 2: - chanmask = 0x00; - break; - case 4: - chanmask = 0x08; - break; - case 6: - chanmask = 0x0b; - break; - case 8: - chanmask = 0x13; - break; - } dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT; dataDCC2 = 0x2; - /* set the Audio InforFrame Channel Allocation */ - snd_hda_codec_write(codec, 0x1, 0, - Nv_VERB_SET_Channel_Allocation, chanmask); - /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) snd_hda_codec_write(codec, @@ -1409,10 +1425,7 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, } } - /* set the Audio Info Frame Checksum */ - snd_hda_codec_write(codec, 0x1, 0, - Nv_VERB_SET_Info_Frame_Checksum, - (0x71 - chan - chanmask)); + nvhdmi_8ch_7x_set_info_frame_parameters(codec, chs); mutex_unlock(&codec->spdif_mutex); return 0; @@ -1508,6 +1521,11 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) spec->multiout.max_channels = 8; spec->pcm_playback = &nvhdmi_pcm_playback_8ch_7x; codec->patch_ops = nvhdmi_patch_ops_8ch_7x; + + /* Initialize the audio infoframe channel mask and checksum to something + * valid */ + nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8); + return 0; } From 2743bc470c3a9c5f0bfdc085d6ed7b716865bc00 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Thu, 7 Apr 2011 11:43:00 +0200 Subject: [PATCH 1099/2556] ALSA: HDA: Fix single internal mic on ALC275 (Sony Vaio VPCSB1C5E) commit 262ac22d21ee2bf3e1655b2e5e45cc94b356e62f upstream. In cases where there is only one internal mic connected to ADC 0x11, alc275_setup_dual_adc won't handle the case, so we need to add the ADC node to the array of candidates. BugLink: http://bugs.launchpad.net/bugs/752792 Reported-by: Vincenzo Pii Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d21ce8bc919bc..e164a4bdf48f5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -14191,7 +14191,7 @@ static hda_nid_t alc269vb_capsrc_nids[1] = { }; static hda_nid_t alc269_adc_candidates[] = { - 0x08, 0x09, 0x07, + 0x08, 0x09, 0x07, 0x11, }; #define alc269_modes alc260_modes From 95bd481686a4848a902a340c4b4b1673cf97092f Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Tue, 22 Mar 2011 23:54:49 +0000 Subject: [PATCH 1100/2556] net: fix ethtool->set_flags not intended -EINVAL return value commit 673e63c688f43104c73aad8ea4237f7ad41fa14d upstream. After commit d5dbda23804156ae6f35025ade5307a49d1db6d7 "ethtool: Add support for vlan accleration.", drivers that have NETIF_F_HW_VLAN_TX, and/or NETIF_F_HW_VLAN_RX feature, but do not allow enable/disable vlan acceleration via ethtool set_flags, always return -EINVAL from that function. Fix by returning -EINVAL only if requested features do not match current settings and can not be changed by driver. Change any driver that define ethtool->set_flags to use ethtool_invalid_flags() to avoid similar problems in the future (also on drivers that do not have the problem). Tested with modified (to reproduce this bug) myri10ge driver. Signed-off-by: Stanislaw Gruszka Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/netxen/netxen_nic_ethtool.c | 2 +- drivers/net/qlcnic/qlcnic_ethtool.c | 2 +- drivers/net/s2io.c | 2 +- drivers/net/vmxnet3/vmxnet3_ethtool.c | 4 ++-- drivers/net/vxge/vxge-ethtool.c | 4 ++-- include/linux/ethtool.h | 1 + net/core/ethtool.c | 17 ++++++++++++++++- 7 files changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/net/netxen/netxen_nic_ethtool.c b/drivers/net/netxen/netxen_nic_ethtool.c index 587498e140bb8..3de98cb002ed5 100644 --- a/drivers/net/netxen/netxen_nic_ethtool.c +++ b/drivers/net/netxen/netxen_nic_ethtool.c @@ -901,7 +901,7 @@ static int netxen_nic_set_flags(struct net_device *netdev, u32 data) struct netxen_adapter *adapter = netdev_priv(netdev); int hw_lro; - if (data & ~ETH_FLAG_LRO) + if (ethtool_invalid_flags(netdev, data, ETH_FLAG_LRO)) return -EINVAL; if (!(adapter->capabilities & NX_FW_CAPABILITY_HW_LRO)) diff --git a/drivers/net/qlcnic/qlcnic_ethtool.c b/drivers/net/qlcnic/qlcnic_ethtool.c index 4c14510e2a87e..45b2755d6cba3 100644 --- a/drivers/net/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/qlcnic/qlcnic_ethtool.c @@ -1003,7 +1003,7 @@ static int qlcnic_set_flags(struct net_device *netdev, u32 data) struct qlcnic_adapter *adapter = netdev_priv(netdev); int hw_lro; - if (data & ~ETH_FLAG_LRO) + if (ethtool_invalid_flags(netdev, data, ETH_FLAG_LRO)) return -EINVAL; if (!(adapter->capabilities & QLCNIC_FW_CAPABILITY_HW_LRO)) diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index 39c17cecb8b98..0cdff2baaa359 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -6726,7 +6726,7 @@ static int s2io_ethtool_set_flags(struct net_device *dev, u32 data) int rc = 0; int changed = 0; - if (data & ~ETH_FLAG_LRO) + if (ethtool_invalid_flags(dev, data, ETH_FLAG_LRO)) return -EINVAL; if (data & ETH_FLAG_LRO) { diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c index 81254be85b926..51f2ef142a5b6 100644 --- a/drivers/net/vmxnet3/vmxnet3_ethtool.c +++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c @@ -304,8 +304,8 @@ vmxnet3_set_flags(struct net_device *netdev, u32 data) u8 lro_present = (netdev->features & NETIF_F_LRO) == 0 ? 0 : 1; unsigned long flags; - if (data & ~ETH_FLAG_LRO) - return -EOPNOTSUPP; + if (ethtool_invalid_flags(netdev, data, ETH_FLAG_LRO)) + return -EINVAL; if (lro_requested ^ lro_present) { /* toggle the LRO feature*/ diff --git a/drivers/net/vxge/vxge-ethtool.c b/drivers/net/vxge/vxge-ethtool.c index 1dd3a21b3a436..c5eb034107fdd 100644 --- a/drivers/net/vxge/vxge-ethtool.c +++ b/drivers/net/vxge/vxge-ethtool.c @@ -1117,8 +1117,8 @@ static int vxge_set_flags(struct net_device *dev, u32 data) struct vxgedev *vdev = netdev_priv(dev); enum vxge_hw_status status; - if (data & ~ETH_FLAG_RXHASH) - return -EOPNOTSUPP; + if (ethtool_invalid_flags(dev, data, ETH_FLAG_RXHASH)) + return -EINVAL; if (!!(data & ETH_FLAG_RXHASH) == vdev->devh->config.rth_en) return 0; diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index a3c1874171c1c..a04b6cee1cb43 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -591,6 +591,7 @@ int ethtool_op_set_ufo(struct net_device *dev, u32 data); u32 ethtool_op_get_flags(struct net_device *dev); int ethtool_op_set_flags(struct net_device *dev, u32 data, u32 supported); void ethtool_ntuple_flush(struct net_device *dev); +bool ethtool_invalid_flags(struct net_device *dev, u32 data, u32 supported); /** * ðtool_ops - Alter and report network device settings diff --git a/net/core/ethtool.c b/net/core/ethtool.c index ff2302910b5eb..6c7c610866db1 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -146,9 +146,24 @@ u32 ethtool_op_get_flags(struct net_device *dev) } EXPORT_SYMBOL(ethtool_op_get_flags); +/* Check if device can enable (or disable) particular feature coded in "data" + * argument. Flags "supported" describe features that can be toggled by device. + * If feature can not be toggled, it state (enabled or disabled) must match + * hardcoded device features state, otherwise flags are marked as invalid. + */ +bool ethtool_invalid_flags(struct net_device *dev, u32 data, u32 supported) +{ + u32 features = dev->features & flags_dup_features; + /* "data" can contain only flags_dup_features bits, + * see __ethtool_set_flags */ + + return (features & ~supported) != (data & ~supported); +} +EXPORT_SYMBOL(ethtool_invalid_flags); + int ethtool_op_set_flags(struct net_device *dev, u32 data, u32 supported) { - if (data & ~supported) + if (ethtool_invalid_flags(dev, data, supported)) return -EINVAL; dev->features = ((dev->features & ~flags_dup_features) | From e64b5314d770cb43ac1aa8c5f1c423f992c804e1 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 4 Apr 2011 11:03:16 -0400 Subject: [PATCH 1101/2556] drm/radeon/kms: add some new ontario pci ids commit 758f231ea280d0e5f01d537f26ad8f5c0e3de1cc upstream. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- include/drm/drm_pciids.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index 5ff1194dc2ea1..6724bf3c1ff11 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -458,6 +458,8 @@ {0x1002, 0x9803, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x9804, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x9805, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9806, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9807, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0, 0, 0} #define r128_PCI_IDS \ From 8c00d8c60ad1c0221d47122f46ba0266add6ba84 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Thu, 24 Mar 2011 23:28:31 +0000 Subject: [PATCH 1102/2556] drm/radeon/kms: add some sanity checks to obj info record parsingi (v2) commit 97ea530f6fac1f9632b0c4792a2a56411454adbe upstream. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=35502 agd5f: also add sanity check to connector records. v2: fix one more case. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_atombios.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 02d5c415f4993..99768d9d91dac 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -675,7 +675,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) ATOM_ENCODER_CAP_RECORD *cap_record; u16 caps = 0; - while (record->ucRecordType > 0 && + while (record->ucRecordSize > 0 && + record->ucRecordType > 0 && record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { switch (record->ucRecordType) { case ATOM_ENCODER_CAP_RECORD_TYPE: @@ -720,7 +721,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) break; } - while (record->ucRecordType > 0 && + while (record->ucRecordSize > 0 && + record->ucRecordType > 0 && record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { switch (record->ucRecordType) { case ATOM_I2C_RECORD_TYPE: @@ -782,10 +784,9 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) ATOM_HPD_INT_RECORD *hpd_record; ATOM_I2C_ID_CONFIG_ACCESS *i2c_config; - while (record->ucRecordType > 0 - && record-> - ucRecordType <= - ATOM_MAX_OBJECT_RECORD_NUMBER) { + while (record->ucRecordSize > 0 && + record->ucRecordType > 0 && + record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { switch (record->ucRecordType) { case ATOM_I2C_RECORD_TYPE: i2c_record = From 91d6c1f8a77a4efec2543de434a924fdc3e6f5e1 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 5 Apr 2011 17:20:50 -0400 Subject: [PATCH 1103/2556] inotify: fix double free/corruption of stuct user commit d0de4dc584ec6aa3b26fffea320a8457827768fc upstream. On an error path in inotify_init1 a normal user can trigger a double free of struct user. This is a regression introduced by a2ae4cc9a16e ("inotify: stop kernel memory leak on file creation failure"). We fix this by making sure that if a group exists the user reference is dropped when the group is cleaned up. We should not explictly drop the reference on error and also drop the reference when the group is cleaned up. The new lifetime rules are that an inotify group lives from inotify_new_group to the last fsnotify_put_group. Since the struct user and inotify_devs are directly tied to this lifetime they are only changed/updated in those two locations. We get rid of all special casing of struct user or user->inotify_devs. Signed-off-by: Eric Paris Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/notify/inotify/inotify_fsnotify.c | 1 + fs/notify/inotify/inotify_user.c | 39 ++++++++++------------------ 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index a91b69a6a291b..0348d0c8f65e8 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -198,6 +198,7 @@ static void inotify_free_group_priv(struct fsnotify_group *group) idr_for_each(&group->inotify_data.idr, idr_callback, group); idr_remove_all(&group->inotify_data.idr); idr_destroy(&group->inotify_data.idr); + atomic_dec(&group->inotify_data.user->inotify_devs); free_uid(group->inotify_data.user); } diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 4cd5d5d78f9fa..aec9b4a4ed112 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -290,7 +290,6 @@ static int inotify_fasync(int fd, struct file *file, int on) static int inotify_release(struct inode *ignored, struct file *file) { struct fsnotify_group *group = file->private_data; - struct user_struct *user = group->inotify_data.user; pr_debug("%s: group=%p\n", __func__, group); @@ -299,8 +298,6 @@ static int inotify_release(struct inode *ignored, struct file *file) /* free this group, matching get was inotify_init->fsnotify_obtain_group */ fsnotify_put_group(group); - atomic_dec(&user->inotify_devs); - return 0; } @@ -697,7 +694,7 @@ static int inotify_update_watch(struct fsnotify_group *group, struct inode *inod return ret; } -static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsigned int max_events) +static struct fsnotify_group *inotify_new_group(unsigned int max_events) { struct fsnotify_group *group; @@ -710,8 +707,14 @@ static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsign spin_lock_init(&group->inotify_data.idr_lock); idr_init(&group->inotify_data.idr); group->inotify_data.last_wd = 0; - group->inotify_data.user = user; group->inotify_data.fa = NULL; + group->inotify_data.user = get_current_user(); + + if (atomic_inc_return(&group->inotify_data.user->inotify_devs) > + inotify_max_user_instances) { + fsnotify_put_group(group); + return ERR_PTR(-EMFILE); + } return group; } @@ -721,7 +724,6 @@ static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsign SYSCALL_DEFINE1(inotify_init1, int, flags) { struct fsnotify_group *group; - struct user_struct *user; int ret; /* Check the IN_* constants for consistency. */ @@ -731,31 +733,16 @@ SYSCALL_DEFINE1(inotify_init1, int, flags) if (flags & ~(IN_CLOEXEC | IN_NONBLOCK)) return -EINVAL; - user = get_current_user(); - if (unlikely(atomic_read(&user->inotify_devs) >= - inotify_max_user_instances)) { - ret = -EMFILE; - goto out_free_uid; - } - /* fsnotify_obtain_group took a reference to group, we put this when we kill the file in the end */ - group = inotify_new_group(user, inotify_max_queued_events); - if (IS_ERR(group)) { - ret = PTR_ERR(group); - goto out_free_uid; - } - - atomic_inc(&user->inotify_devs); + group = inotify_new_group(inotify_max_queued_events); + if (IS_ERR(group)) + return PTR_ERR(group); ret = anon_inode_getfd("inotify", &inotify_fops, group, O_RDONLY | flags); - if (ret >= 0) - return ret; + if (ret < 0) + fsnotify_put_group(group); - fsnotify_put_group(group); - atomic_dec(&user->inotify_devs); -out_free_uid: - free_uid(user); return ret; } From c43194250ee8bb20e2d6d1edba9553842a21679c Mon Sep 17 00:00:00 2001 From: Chase Douglas Date: Fri, 1 Apr 2011 17:03:39 -0400 Subject: [PATCH 1104/2556] HID: hid-magicmouse: Increase evdev buffer size commit cc5e0f08ca2a66fc4c6984ccff74fd529e969fac upstream. The evdev buffer isn't big enough when you get many fingers on the device. Bump up the buffer to a reasonable size, matching what other multitouch devices use. Without this change, events may be discarded in the evdev buffer before they are read. Reported-by: Simon Budig Cc: Henrik Rydberg Cc: Jiri Kosina Signed-off-by: Chase Douglas Acked-by: Henrik Rydberg Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-magicmouse.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 318cc40df92d6..418c399d3ef77 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -418,6 +418,8 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h input_set_abs_params(input, ABS_MT_POSITION_Y, -2456, 2565, 4, 0); } + + input_set_events_per_packet(input, 60); } if (report_undeciphered) { From 80b9edca1c11ec8118ab30451af9c1d492770c90 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 28 Mar 2011 13:13:56 +0200 Subject: [PATCH 1105/2556] perf: Fix task_struct reference leak commit fd1edb3aa2c1d92618d8f0c6d15d44ea41fcac6a upstream. sys_perf_event_open() had an imbalance in the number of task refs it took causing memory leakage Cc: Jiri Olsa Cc: Oleg Nesterov Signed-off-by: Peter Zijlstra LKML-Reference: Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/perf_event.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/perf_event.c b/kernel/perf_event.c index f81f9d6395ad0..07fcc8568fac8 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -5917,6 +5917,11 @@ SYSCALL_DEFINE5(perf_event_open, goto err_alloc; } + if (task) { + put_task_struct(task); + task = NULL; + } + /* * Look up the group leader (we will attach this event to it): */ From f8e3c0bb58a569babfb8d72e7e0655686dbcbab1 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 31 Mar 2011 03:33:29 +0200 Subject: [PATCH 1106/2556] perf: Rebase max unprivileged mlock threshold on top of page size commit 20443384fe090c5f8aeb016e7e85659c5bbdd69f upstream. Ensure we allow 512 kiB + 1 page for user control without assuming a 4096 bytes page size. Reported-by: Peter Zijlstra Signed-off-by: Frederic Weisbecker Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Stephane Eranian LKML-Reference: <1301535209-9679-1-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/perf_event.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 07fcc8568fac8..b2536bd2b6b53 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -62,8 +62,8 @@ static struct srcu_struct pmus_srcu; */ int sysctl_perf_event_paranoid __read_mostly = 1; -/* Minimum for 128 pages + 1 for the user control page */ -int sysctl_perf_event_mlock __read_mostly = 516; /* 'free' kb per user */ +/* Minimum for 512 kiB + 1 user control page */ +int sysctl_perf_event_mlock __read_mostly = 512 + (PAGE_SIZE / 1024); /* 'free' kiB per user */ /* * max perf event sample rate From 1ffe8eb4c593f56a60880e3929be66ee15d3622c Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Sat, 19 Mar 2011 20:43:43 +0000 Subject: [PATCH 1107/2556] ROSE: prevent heap corruption with bad facilities commit be20250c13f88375345ad99950190685eda51eb8 upstream. When parsing the FAC_NATIONAL_DIGIS facilities field, it's possible for a remote host to provide more digipeaters than expected, resulting in heap corruption. Check against ROSE_MAX_DIGIS to prevent overflows, and abort facilities parsing on failure. Additionally, when parsing the FAC_CCITT_DEST_NSAP and FAC_CCITT_SRC_NSAP facilities fields, a remote host can provide a length of less than 10, resulting in an underflow in a memcpy size, causing a kernel panic due to massive heap corruption. A length of greater than 20 results in a stack overflow of the callsign array. Abort facilities parsing on these invalid length values. Signed-off-by: Dan Rosenberg Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/rose/rose_subr.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/net/rose/rose_subr.c b/net/rose/rose_subr.c index 1734abba26a29..174d51c9ce377 100644 --- a/net/rose/rose_subr.c +++ b/net/rose/rose_subr.c @@ -290,10 +290,15 @@ static int rose_parse_national(unsigned char *p, struct rose_facilities_struct * facilities->source_ndigis = 0; facilities->dest_ndigis = 0; for (pt = p + 2, lg = 0 ; lg < l ; pt += AX25_ADDR_LEN, lg += AX25_ADDR_LEN) { - if (pt[6] & AX25_HBIT) + if (pt[6] & AX25_HBIT) { + if (facilities->dest_ndigis >= ROSE_MAX_DIGIS) + return -1; memcpy(&facilities->dest_digis[facilities->dest_ndigis++], pt, AX25_ADDR_LEN); - else + } else { + if (facilities->source_ndigis >= ROSE_MAX_DIGIS) + return -1; memcpy(&facilities->source_digis[facilities->source_ndigis++], pt, AX25_ADDR_LEN); + } } } p += l + 2; @@ -333,6 +338,11 @@ static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *fac case 0xC0: l = p[1]; + + /* Prevent overflows*/ + if (l < 10 || l > 20) + return -1; + if (*p == FAC_CCITT_DEST_NSAP) { memcpy(&facilities->source_addr, p + 7, ROSE_ADDR_LEN); memcpy(callsign, p + 12, l - 10); @@ -373,12 +383,16 @@ int rose_parse_facilities(unsigned char *p, switch (*p) { case FAC_NATIONAL: /* National */ len = rose_parse_national(p + 1, facilities, facilities_len - 1); + if (len < 0) + return 0; facilities_len -= len + 1; p += len + 1; break; case FAC_CCITT: /* CCITT */ len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1); + if (len < 0) + return 0; facilities_len -= len + 1; p += len + 1; break; From f54f2ff7105d2d931c638009bae3ba4d03d33b01 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Mon, 28 Mar 2011 02:01:25 +0000 Subject: [PATCH 1108/2556] Btrfs: Fix uninitialized root flags for subvolumes commit 08fe4db170b4193603d9d31f40ebaf652d07ac9c upstream. root_item->flags and root_item->byte_limit are not initialized when a subvolume is created. This bug is not revealed until we added readonly snapshot support - now you mount a btrfs filesystem and you may find the subvolumes in it are readonly. To work around this problem, we steal a bit from root_item->inode_item->flags, and use it to indicate if those fields have been properly initialized. When we read a tree root from disk, we check if the bit is set, and if not we'll set the flag and initialize the two fields of the root item. Reported-by: Andreas Philipp Signed-off-by: Li Zefan Tested-by: Andreas Philipp Signed-off-by: Chris Mason Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/ctree.h | 4 ++++ fs/btrfs/disk-io.c | 4 +++- fs/btrfs/ioctl.c | 4 ++++ fs/btrfs/root-tree.c | 18 ++++++++++++++++++ fs/btrfs/transaction.c | 1 + 5 files changed, 30 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 7f78cc78fdd0a..bd64b4101f5f1 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1284,6 +1284,8 @@ struct btrfs_root { #define BTRFS_INODE_NOATIME (1 << 9) #define BTRFS_INODE_DIRSYNC (1 << 10) +#define BTRFS_INODE_ROOT_ITEM_INIT (1 << 31) + /* some macros to generate set/get funcs for the struct fields. This * assumes there is a lefoo_to_cpu for every type, so lets make a simple * one for u8: @@ -2355,6 +2357,8 @@ int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid); int btrfs_find_orphan_roots(struct btrfs_root *tree_root); int btrfs_set_root_node(struct btrfs_root_item *item, struct extent_buffer *node); +void btrfs_check_and_init_root_item(struct btrfs_root_item *item); + /* dir-item.c */ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, const char *name, diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index e1aa8d607bc7d..edd9efa515675 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1184,8 +1184,10 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root, root->commit_root = btrfs_root_node(root); BUG_ON(!root->node); out: - if (location->objectid != BTRFS_TREE_LOG_OBJECTID) + if (location->objectid != BTRFS_TREE_LOG_OBJECTID) { root->ref_cows = 1; + btrfs_check_and_init_root_item(&root->root_item); + } return root; } diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 5fdb2abc4fa78..2ff51e69dea83 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -294,6 +294,10 @@ static noinline int create_subvol(struct btrfs_root *root, inode_item->nbytes = cpu_to_le64(root->leafsize); inode_item->mode = cpu_to_le32(S_IFDIR | 0755); + root_item.flags = 0; + root_item.byte_limit = 0; + inode_item->flags = cpu_to_le64(BTRFS_INODE_ROOT_ITEM_INIT); + btrfs_set_root_bytenr(&root_item, leaf->start); btrfs_set_root_generation(&root_item, trans->transid); btrfs_set_root_level(&root_item, 0); diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c index 6a1086e83ffc8..3e45c3206e7f5 100644 --- a/fs/btrfs/root-tree.c +++ b/fs/btrfs/root-tree.c @@ -471,3 +471,21 @@ int btrfs_add_root_ref(struct btrfs_trans_handle *trans, btrfs_free_path(path); return 0; } + +/* + * Old btrfs forgets to init root_item->flags and root_item->byte_limit + * for subvolumes. To work around this problem, we steal a bit from + * root_item->inode_item->flags, and use it to indicate if those fields + * have been properly initialized. + */ +void btrfs_check_and_init_root_item(struct btrfs_root_item *root_item) +{ + u64 inode_flags = le64_to_cpu(root_item->inode.flags); + + if (!(inode_flags & BTRFS_INODE_ROOT_ITEM_INIT)) { + inode_flags |= BTRFS_INODE_ROOT_ITEM_INIT; + root_item->inode.flags = cpu_to_le64(inode_flags); + root_item->flags = 0; + root_item->byte_limit = 0; + } +} diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 3d73c8d93bbb1..f3d66819025c4 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -970,6 +970,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, record_root_in_trans(trans, root); btrfs_set_root_last_snapshot(&root->root_item, trans->transid); memcpy(new_root_item, &root->root_item, sizeof(*new_root_item)); + btrfs_check_and_init_root_item(new_root_item); root_flags = btrfs_root_flags(new_root_item); if (pending->readonly) From a41fc5e39b21e83d9dc01ef3e0f02b8434c3d823 Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Tue, 29 Mar 2011 15:38:12 -0700 Subject: [PATCH 1109/2556] x86, mtrr, pat: Fix one cpu getting out of sync during resume commit 84ac7cdbdd0f04df6b96153f7a79127fd6e45467 upstream. On laptops with core i5/i7, there were reports that after resume graphics workloads were performing poorly on a specific AP, while the other cpu's were ok. This was observed on a 32bit kernel specifically. Debug showed that the PAT init was not happening on that AP during resume and hence it contributing to the poor workload performance on that cpu. On this system, resume flow looked like this: 1. BP starts the resume sequence and we reinit BP's MTRR's/PAT early on using mtrr_bp_restore() 2. Resume sequence brings all AP's online 3. Resume sequence now kicks off the MTRR reinit on all the AP's. 4. For some reason, between point 2 and 3, we moved from BP to one of the AP's. My guess is that printk() during resume sequence is contributing to this. We don't see similar behavior with the 64bit kernel but there is no guarantee that at this point the remaining resume sequence (after AP's bringup) has to happen on BP. 5. set_mtrr() was assuming that we are still on BP and skipped the MTRR/PAT init on that cpu (because of 1 above) 6. But we were on an AP and this led to not reprogramming PAT on this cpu leading to bad performance. Fix this by doing unconditional mtrr_if->set_all() in set_mtrr() during MTRR/PAT init. This might be unnecessary if we are still running on BP. But it is of no harm and will guarantee that after resume, all the cpu's will be in sync with respect to the MTRR/PAT registers. Signed-off-by: Suresh Siddha LKML-Reference: <1301438292-28370-1-git-send-email-eric@anholt.net> Signed-off-by: Eric Anholt Tested-by: Keith Packard Signed-off-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/mtrr/main.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/arch/x86/kernel/cpu/mtrr/main.c b/arch/x86/kernel/cpu/mtrr/main.c index bebabec5b448d..151787e382c77 100644 --- a/arch/x86/kernel/cpu/mtrr/main.c +++ b/arch/x86/kernel/cpu/mtrr/main.c @@ -292,14 +292,24 @@ set_mtrr(unsigned int reg, unsigned long base, unsigned long size, mtrr_type typ /* * HACK! - * We use this same function to initialize the mtrrs on boot. - * The state of the boot cpu's mtrrs has been saved, and we want - * to replicate across all the APs. - * If we're doing that @reg is set to something special... + * + * We use this same function to initialize the mtrrs during boot, + * resume, runtime cpu online and on an explicit request to set a + * specific MTRR. + * + * During boot or suspend, the state of the boot cpu's mtrrs has been + * saved, and we want to replicate that across all the cpus that come + * online (either at the end of boot or resume or during a runtime cpu + * online). If we're doing that, @reg is set to something special and on + * this cpu we still do mtrr_if->set_all(). During boot/resume, this + * is unnecessary if at this point we are still on the cpu that started + * the boot/resume sequence. But there is no guarantee that we are still + * on the same cpu. So we do mtrr_if->set_all() on this cpu aswell to be + * sure that we are in sync with everyone else. */ if (reg != ~0U) mtrr_if->set(reg, base, size, type); - else if (!mtrr_aps_delayed_init) + else mtrr_if->set_all(); /* Wait for the others */ From b544388a92e32a0f8a2a1cccc70fcf455b92fb5d Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Thu, 31 Mar 2011 00:01:58 -0700 Subject: [PATCH 1110/2556] Input: synaptics - fix crash in synaptics_module_init() commit 708748670c7c6dd5bd3b141473086e6937e72737 upstream. 'struct dmi_system_id' arrays must always have a terminator to keep dmi_check_system() from looking at data (and possibly crashing) it isn't supposed to look at. The issue went unnoticed until ef8313bb1a22e7d2125d9d758aa8a81f1de91d81, but was introduced about a year earlier with 7705d548cbe33f18ea7713b9a07aa11047aaeca4 (which also similarly changed lifebook.c, but the problem there got eliminated shortly afterwards). The first hunk therefore is a stable candidate back to 2.6.33, while the full change is needed only on 2.6.38. Signed-off-by: Jan Beulich Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/synaptics.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index aa186cf6c5145..e06e045bf907a 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -836,8 +836,8 @@ static const struct dmi_system_id __initconst toshiba_dmi_table[] = { }, }, - { } #endif + { } }; static bool broken_olpc_ec; @@ -851,8 +851,8 @@ static const struct dmi_system_id __initconst olpc_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "XO"), }, }, - { } #endif + { } }; void __init synaptics_module_init(void) From d0325b60bff25923b488313f2ef10d88050bd67e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 25 Mar 2011 17:43:41 +0100 Subject: [PATCH 1111/2556] ath9k: fix a chip wakeup related crash in ath9k_start commit f62d816fc4324afbb7cf90110c70b6a14139b225 upstream. When the chip is still asleep when ath9k_start is called, ath9k_hw_configpcipowersave can trigger a data bus error. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index a09d15f7aa6e0..0848e09954749 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1063,6 +1063,8 @@ static int ath9k_start(struct ieee80211_hw *hw) "Starting driver with initial channel: %d MHz\n", curchan->center_freq); + ath9k_ps_wakeup(sc); + mutex_lock(&sc->mutex); if (ath9k_wiphy_started(sc)) { @@ -1179,6 +1181,8 @@ static int ath9k_start(struct ieee80211_hw *hw) mutex_unlock: mutex_unlock(&sc->mutex); + ath9k_ps_restore(sc); + return r; } From fbb739ca1d3b734687c06f7eea0e25df2c18d876 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 25 Mar 2011 15:30:38 +0100 Subject: [PATCH 1112/2556] mac80211: fix a crash in minstrel_ht in HT mode with no supported MCS rates commit 4dc217df68a17a57f8464c74c1b4785e40bddf77 upstream. When a client connects in HT mode but does not provide any valid MCS rates, the function that finds the next sample rate gets stuck in an infinite loop. Fix this by falling back to legacy rates if no usable MCS rates are found. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- net/mac80211/rc80211_minstrel_ht.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 165a4518bb48e..cac35ff14b86e 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -639,18 +639,14 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs; struct ieee80211_local *local = hw_to_local(mp->hw); u16 sta_cap = sta->ht_cap.cap; + int n_supported = 0; int ack_dur; int stbc; int i; /* fall back to the old minstrel for legacy stations */ - if (!sta->ht_cap.ht_supported) { - msp->is_ht = false; - memset(&msp->legacy, 0, sizeof(msp->legacy)); - msp->legacy.r = msp->ratelist; - msp->legacy.sample_table = msp->sample_table; - return mac80211_minstrel.rate_init(priv, sband, sta, &msp->legacy); - } + if (!sta->ht_cap.ht_supported) + goto use_legacy; BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS); @@ -705,7 +701,22 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, mi->groups[i].supported = mcs->rx_mask[minstrel_mcs_groups[i].streams - 1]; + + if (mi->groups[i].supported) + n_supported++; } + + if (!n_supported) + goto use_legacy; + + return; + +use_legacy: + msp->is_ht = false; + memset(&msp->legacy, 0, sizeof(msp->legacy)); + msp->legacy.r = msp->ratelist; + msp->legacy.sample_table = msp->sample_table; + return mac80211_minstrel.rate_init(priv, sband, sta, &msp->legacy); } static void From 896b07961ed8c48bfa3431db8ee101e4486a41dc Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Mon, 21 Mar 2011 16:44:35 +0100 Subject: [PATCH 1113/2556] staging: IIO: IMU: ADIS16400: Fix up SPI messages cs_change behavior commit fc5b85b0ad1f9da948e4d683710081a9bda357cb upstream. cs_change must not be set in the last transfer of a spi message Signed-off-by: Michael Hennerich Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/imu/adis16400_core.c | 2 -- drivers/staging/iio/imu/adis16400_ring.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/drivers/staging/iio/imu/adis16400_core.c b/drivers/staging/iio/imu/adis16400_core.c index cfb108a1545b3..1007e3ded92fe 100644 --- a/drivers/staging/iio/imu/adis16400_core.c +++ b/drivers/staging/iio/imu/adis16400_core.c @@ -93,7 +93,6 @@ static int adis16400_spi_write_reg_16(struct device *dev, .tx_buf = st->tx + 2, .bits_per_word = 8, .len = 2, - .cs_change = 1, }, }; @@ -137,7 +136,6 @@ static int adis16400_spi_read_reg_16(struct device *dev, .rx_buf = st->rx, .bits_per_word = 8, .len = 2, - .cs_change = 1, }, }; diff --git a/drivers/staging/iio/imu/adis16400_ring.c b/drivers/staging/iio/imu/adis16400_ring.c index 33293fba9bccd..ac381ca126fcf 100644 --- a/drivers/staging/iio/imu/adis16400_ring.c +++ b/drivers/staging/iio/imu/adis16400_ring.c @@ -122,12 +122,10 @@ static int adis16400_spi_read_burst(struct device *dev, u8 *rx) .tx_buf = st->tx, .bits_per_word = 8, .len = 2, - .cs_change = 0, }, { .rx_buf = rx, .bits_per_word = 8, .len = 24, - .cs_change = 1, }, }; From 5731ed538d85a166ad1547f920efc49386239205 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Mon, 21 Mar 2011 16:44:36 +0100 Subject: [PATCH 1114/2556] staging: IIO: IMU: ADIS16400: Add delay after self test commit c59c95ce6ace6d256401fc3a3648a95375ef4e63 upstream. Add delay after self test to satisfy timing requirements. Increase start-up delay. Signed-off-by: Michael Hennerich Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/imu/adis16400.h | 3 ++- drivers/staging/iio/imu/adis16400_core.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/iio/imu/adis16400.h b/drivers/staging/iio/imu/adis16400.h index 6ff33e1ad8c19..90e90f0e65e88 100644 --- a/drivers/staging/iio/imu/adis16400.h +++ b/drivers/staging/iio/imu/adis16400.h @@ -17,7 +17,8 @@ #ifndef SPI_ADIS16400_H_ #define SPI_ADIS16400_H_ -#define ADIS16400_STARTUP_DELAY 220 /* ms */ +#define ADIS16400_STARTUP_DELAY 290 /* ms */ +#define ADIS16400_MTEST_DELAY 90 /* ms */ #define ADIS16400_READ_REG(a) a #define ADIS16400_WRITE_REG(a) ((a) | 0x80) diff --git a/drivers/staging/iio/imu/adis16400_core.c b/drivers/staging/iio/imu/adis16400_core.c index 1007e3ded92fe..633b3a4e42891 100644 --- a/drivers/staging/iio/imu/adis16400_core.c +++ b/drivers/staging/iio/imu/adis16400_core.c @@ -373,7 +373,7 @@ static int adis16400_self_test(struct device *dev) dev_err(dev, "problem starting self test"); goto err_ret; } - + msleep(ADIS16400_MTEST_DELAY); adis16400_check_status(dev); err_ret: From 0937eb5be5088bc38dba4ccf01b2218029637918 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Mon, 21 Mar 2011 16:44:37 +0100 Subject: [PATCH 1115/2556] staging: IIO: IMU: ADIS16400: Fix addresses of GYRO and ACCEL calibration offset commit b181119723d62b19904e1f12e467e996631c5a29 upstream. Signed-off-by: Michael Hennerich Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/imu/adis16400_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/iio/imu/adis16400_core.c b/drivers/staging/iio/imu/adis16400_core.c index 633b3a4e42891..2107edb3ebce1 100644 --- a/drivers/staging/iio/imu/adis16400_core.c +++ b/drivers/staging/iio/imu/adis16400_core.c @@ -495,12 +495,12 @@ static int adis16400_initial_setup(struct adis16400_state *st) _reg) static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_X, ADIS16400_XGYRO_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_Y, ADIS16400_XGYRO_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_Z, ADIS16400_XGYRO_OFF); +static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_Y, ADIS16400_YGYRO_OFF); +static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_Z, ADIS16400_ZGYRO_OFF); static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_X, ADIS16400_XACCL_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_Y, ADIS16400_XACCL_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_Z, ADIS16400_XACCL_OFF); +static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_Y, ADIS16400_YACCL_OFF); +static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_Z, ADIS16400_ZACCL_OFF); static IIO_DEV_ATTR_IN_NAMED_RAW(0, supply, adis16400_read_14bit_signed, From 49e6fc727d1621fc084df4dd7f53dec7a2448f9a Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Mon, 21 Mar 2011 16:44:38 +0100 Subject: [PATCH 1116/2556] staging: IIO: IMU: ADIS16400: Make sure only enabled scan_elements are pushed into the ring commit 0fea4d6192cb82789e28905240d3c1dac6529c7c upstream. Signed-off-by: Michael Hennerich Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/imu/adis16400_ring.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/staging/iio/imu/adis16400_ring.c b/drivers/staging/iio/imu/adis16400_ring.c index ac381ca126fcf..da28cb4288af3 100644 --- a/drivers/staging/iio/imu/adis16400_ring.c +++ b/drivers/staging/iio/imu/adis16400_ring.c @@ -160,9 +160,10 @@ static void adis16400_trigger_bh_to_ring(struct work_struct *work_s) work_trigger_to_ring); struct iio_ring_buffer *ring = st->indio_dev->ring; - int i = 0; + int i = 0, j; s16 *data; size_t datasize = ring->access.get_bytes_per_datum(ring); + unsigned long mask = ring->scan_mask; data = kmalloc(datasize , GFP_KERNEL); if (data == NULL) { @@ -172,9 +173,12 @@ static void adis16400_trigger_bh_to_ring(struct work_struct *work_s) if (ring->scan_count) if (adis16400_spi_read_burst(&st->indio_dev->dev, st->rx) >= 0) - for (; i < ring->scan_count; i++) + for (; i < ring->scan_count; i++) { + j = __ffs(mask); + mask &= ~(1 << j); data[i] = be16_to_cpup( - (__be16 *)&(st->rx[i*2])); + (__be16 *)&(st->rx[j*2])); + } /* Guaranteed to be aligned with 8 byte boundary */ if (ring->scan_timestamp) From 623ec9f6000b2f61bae3a4b31181ac7258569a55 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 25 Mar 2011 18:33:57 +0200 Subject: [PATCH 1117/2556] UBIFS: do not read flash unnecessarily commit 8b229c76765816796eec7ccd428f03bd8de8b525 upstream. This fix makes the 'dbg_check_old_index()' function return immediately if debugging is disabled, instead of executing incorrect 'goto out' which causes UBIFS to: 1. Allocate memory 2. Read the flash On every commit. OK, we do not commit that often, but it is still silly to do unneeded I/O anyway. Credits to coverity for spotting this silly issue. Signed-off-by: Artem Bityutskiy Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ubifs/commit.c b/fs/ubifs/commit.c index 02429d81ca337..32bcb2c467e7b 100644 --- a/fs/ubifs/commit.c +++ b/fs/ubifs/commit.c @@ -521,7 +521,7 @@ int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot) size_t sz; if (!(ubifs_chk_flags & UBIFS_CHK_OLD_IDX)) - goto out; + return 0; INIT_LIST_HEAD(&list); From e808fb3d4947fcd6046b462bdc19721a25f26069 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 25 Mar 2011 19:09:54 +0200 Subject: [PATCH 1118/2556] UBIFS: fix oops on error path in read_pnode commit 54acbaaa523ca0bd284a18f67ad213c379679e86 upstream. Thanks to coverity which spotted that UBIFS will oops if 'kmalloc()' in 'read_pnode()' fails and we dereference a NULL 'pnode' pointer when we 'goto out'. Signed-off-by: Artem Bityutskiy Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/lpt.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/ubifs/lpt.c b/fs/ubifs/lpt.c index 72775d35b99e5..ef5155e109a27 100644 --- a/fs/ubifs/lpt.c +++ b/fs/ubifs/lpt.c @@ -1270,10 +1270,9 @@ static int read_pnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip) lnum = branch->lnum; offs = branch->offs; pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_NOFS); - if (!pnode) { - err = -ENOMEM; - goto out; - } + if (!pnode) + return -ENOMEM; + if (lnum == 0) { /* * This pnode was not written which just means that the LEB From 0b23e6b9d92cd5f7faa1450cbd41bad5dce4d207 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 4 Apr 2011 17:16:39 +0300 Subject: [PATCH 1119/2556] UBIFS: fix debugging failure in dbg_check_space_info commit 7da6443aca9be29c6948dcbd636ad50154d0bc0c upstream. This patch fixes a debugging failure with which looks like this: UBIFS error (pid 32313): dbg_check_space_info: free space changed from 6019344 to 6022654 The reason for this failure is described in the comment this patch adds to the code. But in short - 'c->freeable_cnt' may be different before and after re-mounting, and this is normal. So the debugging code should make sure that free space calculations do not depend on 'c->freeable_cnt'. A similar issue has been reported here: http://lists.infradead.org/pipermail/linux-mtd/2011-April/034647.html This patch should fix it. For the -stable guys: this patch is only relevant for kernels 2.6.30 onwards. Signed-off-by: Artem Bityutskiy Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/debug.c | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/fs/ubifs/debug.c b/fs/ubifs/debug.c index 0bee4dbffc31f..5b9e9855b22c6 100644 --- a/fs/ubifs/debug.c +++ b/fs/ubifs/debug.c @@ -961,11 +961,39 @@ void dbg_dump_index(struct ubifs_info *c) void dbg_save_space_info(struct ubifs_info *c) { struct ubifs_debug_info *d = c->dbg; - - ubifs_get_lp_stats(c, &d->saved_lst); + int freeable_cnt; spin_lock(&c->space_lock); + memcpy(&d->saved_lst, &c->lst, sizeof(struct ubifs_lp_stats)); + + /* + * We use a dirty hack here and zero out @c->freeable_cnt, because it + * affects the free space calculations, and UBIFS might not know about + * all freeable eraseblocks. Indeed, we know about freeable eraseblocks + * only when we read their lprops, and we do this only lazily, upon the + * need. So at any given point of time @c->freeable_cnt might be not + * exactly accurate. + * + * Just one example about the issue we hit when we did not zero + * @c->freeable_cnt. + * 1. The file-system is mounted R/O, c->freeable_cnt is %0. We save the + * amount of free space in @d->saved_free + * 2. We re-mount R/W, which makes UBIFS to read the "lsave" + * information from flash, where we cache LEBs from various + * categories ('ubifs_remount_fs()' -> 'ubifs_lpt_init()' + * -> 'lpt_init_wr()' -> 'read_lsave()' -> 'ubifs_lpt_lookup()' + * -> 'ubifs_get_pnode()' -> 'update_cats()' + * -> 'ubifs_add_to_cat()'). + * 3. Lsave contains a freeable eraseblock, and @c->freeable_cnt + * becomes %1. + * 4. We calculate the amount of free space when the re-mount is + * finished in 'dbg_check_space_info()' and it does not match + * @d->saved_free. + */ + freeable_cnt = c->freeable_cnt; + c->freeable_cnt = 0; d->saved_free = ubifs_get_free_space_nolock(c); + c->freeable_cnt = freeable_cnt; spin_unlock(&c->space_lock); } @@ -982,12 +1010,15 @@ int dbg_check_space_info(struct ubifs_info *c) { struct ubifs_debug_info *d = c->dbg; struct ubifs_lp_stats lst; - long long avail, free; + long long free; + int freeable_cnt; spin_lock(&c->space_lock); - avail = ubifs_calc_available(c, c->min_idx_lebs); + freeable_cnt = c->freeable_cnt; + c->freeable_cnt = 0; + free = ubifs_get_free_space_nolock(c); + c->freeable_cnt = freeable_cnt; spin_unlock(&c->space_lock); - free = ubifs_get_free_space(c); if (free != d->saved_free) { ubifs_err("free space changed from %lld to %lld", From 3f9346cf211cdb10f053d7cb9752f7fd6723e16b Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 31 Mar 2011 18:36:52 +0200 Subject: [PATCH 1120/2556] quota: Don't write quota info in dquot_commit() commit b03f24567ce7caf2420b8be4c6eb74c191d59a91 upstream. There's no reason to write quota info in dquot_commit(). The writing is a relict from the old days when we didn't have dquot_acquire() and dquot_release() and thus dquot_commit() could have created / removed quota structures from the file. These days dquot_commit() only updates usage counters / limits in quota structure and thus there's no need to write quota info. This also fixes an issue with journaling filesystem which didn't reserve enough space in the transaction for write of quota info (it could have been dirty at the time of dquot_commit() because of a race with other operation changing it). Reported-and-tested-by: Lukas Czerner Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/quota/dquot.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index a2a622e079f08..b59ee61f4b9a0 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -442,7 +442,7 @@ EXPORT_SYMBOL(dquot_acquire); */ int dquot_commit(struct dquot *dquot) { - int ret = 0, ret2 = 0; + int ret = 0; struct quota_info *dqopt = sb_dqopt(dquot->dq_sb); mutex_lock(&dqopt->dqio_mutex); @@ -454,15 +454,10 @@ int dquot_commit(struct dquot *dquot) spin_unlock(&dq_list_lock); /* Inactive dquot can be only if there was error during read/init * => we have better not writing it */ - if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) { + if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot); - if (info_dirty(&dqopt->info[dquot->dq_type])) { - ret2 = dqopt->ops[dquot->dq_type]->write_file_info( - dquot->dq_sb, dquot->dq_type); - } - if (ret >= 0) - ret = ret2; - } + else + ret = -EIO; out_sem: mutex_unlock(&dqopt->dqio_mutex); return ret; From 3fe6bb7272a1f409098795f65c1d0ab562cdcd9c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 7 Apr 2011 07:35:50 -0700 Subject: [PATCH 1121/2556] mm: avoid wrapping vm_pgoff in mremap() commit 982134ba62618c2d69fbbbd166d0a11ee3b7e3d8 upstream. The normal mmap paths all avoid creating a mapping where the pgoff inside the mapping could wrap around due to overflow. However, an expanding mremap() can take such a non-wrapping mapping and make it bigger and cause a wrapping condition. Noticed by Robert Swiecki when running a system call fuzzer, where it caused a BUG_ON() due to terminally confusing the vma_prio_tree code. A vma dumping patch by Hugh then pinpointed the crazy wrapped case. Reported-and-tested-by: Robert Swiecki Acked-by: Hugh Dickins Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/mremap.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mm/mremap.c b/mm/mremap.c index 1de98d492ddcd..a7c1f9f9b9415 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -277,9 +277,16 @@ static struct vm_area_struct *vma_to_resize(unsigned long addr, if (old_len > vma->vm_end - addr) goto Efault; - if (vma->vm_flags & (VM_DONTEXPAND | VM_PFNMAP)) { - if (new_len > old_len) + /* Need to be careful about a growing mapping */ + if (new_len > old_len) { + unsigned long pgoff; + + if (vma->vm_flags & (VM_DONTEXPAND | VM_PFNMAP)) goto Efault; + pgoff = (addr - vma->vm_start) >> PAGE_SHIFT; + pgoff += vma->vm_pgoff; + if (pgoff + (new_len >> PAGE_SHIFT) < pgoff) + goto Einval; } if (vma->vm_flags & VM_LOCKED) { From 1a3a01f9b2aa34ee33f9872e1a3e19debae5b98d Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 6 Apr 2011 10:13:32 -0700 Subject: [PATCH 1122/2556] Revert "net/sunrpc: Use static const char arrays" commit 0867659fa3c245bf203d837a82e0f6ea5079c2c5 upstream. This reverts commit 411b5e05617593efebc06241dbc56f42150f2abe. Olga Kornievskaia reports: Problem: linux client mounting linux server using rc4-hmac-md5 enctype. gssd fails with create a context after receiving a reply from the server. Diagnose: putting printout statements in the server kernel and kerberos libraries revealed that client and server derived different integrity keys. Server kernel code was at fault due the the commit [aglo@skydive linux-pnfs]$ git show 411b5e05617593efebc06241dbc56f42150f2abe Trond: The problem is that since it relies on virt_to_page(), you cannot call sg_set_buf() for data in the const section. Reported-by: Olga Kornievskaia Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/auth_gss/gss_krb5_mech.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index f375decc024b4..778e5dfc51449 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c @@ -427,7 +427,7 @@ static int context_derive_keys_rc4(struct krb5_ctx *ctx) { struct crypto_hash *hmac; - static const char sigkeyconstant[] = "signaturekey"; + char sigkeyconstant[] = "signaturekey"; int slen = strlen(sigkeyconstant) + 1; /* include null terminator */ struct hash_desc desc; struct scatterlist sg[1]; From 23ca28c48d8e5339a8be4d136a8ba2ec94aa841f Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Mon, 4 Apr 2011 13:18:44 -0400 Subject: [PATCH 1123/2556] iwlwifi: accept EEPROM version 0x423 for iwl6000 commit 3d7dc7e8c1566acb0fc55df228b2ed91f5638e9d upstream. A number of these devices have appeared "in the wild", and apparently the Windows driver is perfectly happy to support this EEPROM version. Signed-off-by: John W. Linville Acked-by: Wey-Yi Guy Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl-eeprom.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.h b/drivers/net/wireless/iwlwifi/iwl-eeprom.h index 9e6f31355eee8..c0cd307dc2e82 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.h @@ -241,7 +241,7 @@ struct iwl_eeprom_enhanced_txpwr { /* 6x00 Specific */ #define EEPROM_6000_TX_POWER_VERSION (4) -#define EEPROM_6000_EEPROM_VERSION (0x434) +#define EEPROM_6000_EEPROM_VERSION (0x423) /* 6x50 Specific */ #define EEPROM_6050_TX_POWER_VERSION (4) From 4f1c3d9ebf87c2f937824624dff7478c2923a2b6 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 2 Apr 2011 11:31:29 +0200 Subject: [PATCH 1124/2556] p54usb: IDs for two new devices commit 220107610c7c2c9703e09eb363e8ab31025b9315 upstream. Reported-by: Mark Davis [via p54/devices wiki] Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/p54/p54usb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index 9b344a921e742..e18358725b698 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -56,6 +56,7 @@ static struct usb_device_id p54u_table[] __devinitdata = { {USB_DEVICE(0x0846, 0x4210)}, /* Netgear WG121 the second ? */ {USB_DEVICE(0x0846, 0x4220)}, /* Netgear WG111 */ {USB_DEVICE(0x09aa, 0x1000)}, /* Spinnaker Proto board */ + {USB_DEVICE(0x0bf8, 0x1007)}, /* Fujitsu E-5400 USB */ {USB_DEVICE(0x0cde, 0x0006)}, /* Medion 40900, Roper Europe */ {USB_DEVICE(0x0db0, 0x6826)}, /* MSI UB54G (MS-6826) */ {USB_DEVICE(0x107b, 0x55f2)}, /* Gateway WGU-210 (Gemtek) */ @@ -68,6 +69,7 @@ static struct usb_device_id p54u_table[] __devinitdata = { {USB_DEVICE(0x1915, 0x2235)}, /* Linksys WUSB54G Portable OEM */ {USB_DEVICE(0x2001, 0x3701)}, /* DLink DWL-G120 Spinnaker */ {USB_DEVICE(0x2001, 0x3703)}, /* DLink DWL-G122 */ + {USB_DEVICE(0x2001, 0x3762)}, /* Conceptronic C54U */ {USB_DEVICE(0x5041, 0x2234)}, /* Linksys WUSB54G */ {USB_DEVICE(0x5041, 0x2235)}, /* Linksys WUSB54G Portable */ From d6cdf976903232d385274ffa4bbfac182b3991d6 Mon Sep 17 00:00:00 2001 From: RA-Jay Hung Date: Mon, 10 Jan 2011 11:27:43 +0100 Subject: [PATCH 1125/2556] rt2x00: Fix radio off hang issue for PCIE interface commit 7f6e144fb99a4a70d3c5ad5f074204c5b89a6f65 upstream. PCI/PCIE radio off behavior is different from SOC/USB. They mainly use MCU command to disable DMA, TX/RX and enter power saving mode. Signed-off-by: RA-Jay Hung Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville Signed-off-by: Stanislaw Gruszka Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/rt2x00/rt2800lib.c | 6 ----- drivers/net/wireless/rt2x00/rt2800pci.c | 36 +++++++------------------ 2 files changed, 10 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 54917a2813981..e2a528da36417 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -2810,10 +2810,7 @@ void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev) rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_DMA_BUSY, 0); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_DMA_BUSY, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1); rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); /* Wait for DMA, ignore error */ @@ -2823,9 +2820,6 @@ void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev) rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 0); rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - - rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0); - rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0); } EXPORT_SYMBOL_GPL(rt2800_disable_radio); diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index 3b3f1e45ab3e5..37a38b5a2a8f7 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -475,39 +475,23 @@ static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev) static void rt2800pci_disable_radio(struct rt2x00_dev *rt2x00dev) { - u32 reg; - - rt2800_disable_radio(rt2x00dev); - - rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00001280); - - rt2800_register_read(rt2x00dev, WPDMA_RST_IDX, ®); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX0, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX1, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX2, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX3, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX4, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX5, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DRX_IDX0, 1); - rt2800_register_write(rt2x00dev, WPDMA_RST_IDX, reg); - - rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f); - rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); + if (rt2x00_is_soc(rt2x00dev)) { + rt2800_disable_radio(rt2x00dev); + rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0); + rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0); + } } static int rt2800pci_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { - /* - * Always put the device to sleep (even when we intend to wakeup!) - * if the device is booting and wasn't asleep it will return - * failure when attempting to wakeup. - */ - rt2800_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0xff, 2); - if (state == STATE_AWAKE) { - rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKUP, 0, 0); + rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKUP, 0, 0x02); rt2800pci_mcu_status(rt2x00dev, TOKEN_WAKUP); + } else if (state == STATE_SLEEP) { + rt2800_register_write(rt2x00dev, H2M_MAILBOX_STATUS, 0xffffffff); + rt2800_register_write(rt2x00dev, H2M_MAILBOX_CID, 0xffffffff); + rt2800_mcu_request(rt2x00dev, MCU_SLEEP, 0x01, 0xff, 0x01); } return 0; From dd3e5dee2c55cbb4f8afc54def21554e3e6b48f1 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Mon, 4 Apr 2011 13:50:32 +0200 Subject: [PATCH 1126/2556] rt2x00: fix cancelling uninitialized work commit 37f4ee0b6b39640828dac5937a482c20e8ac794f upstream. {rx,tx}done_work's are only initialized for usb devices. Signed-off-by: Stanislaw Gruszka Acked-by: Ivo van Doorn Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/rt2x00/rt2x00dev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 9597a03242cce..2b77a291aa970 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1031,8 +1031,10 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) * Stop all work. */ cancel_work_sync(&rt2x00dev->intf_work); - cancel_work_sync(&rt2x00dev->rxdone_work); - cancel_work_sync(&rt2x00dev->txdone_work); + if (rt2x00_is_usb(rt2x00dev)) { + cancel_work_sync(&rt2x00dev->rxdone_work); + cancel_work_sync(&rt2x00dev->txdone_work); + } /* * Free the tx status fifo. From 1ff9aebbec267d7a877988df6ced21b95966dda8 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 1 Apr 2011 19:42:02 +0300 Subject: [PATCH 1127/2556] wl12xx: fix potential buffer overflow in testmode nvs push commit 09b661b33268698d3b453dceb78cda129ad899b4 upstream. We were allocating the size of the NVS file struct and not checking whether the length of the buffer passed was correct before copying it into the allocated memory. This is a security hole because buffer overflows can occur if the userspace passes a bigger file than what is expected. With this patch, we check if the size of the data passed from userspace matches the size required. This bug was introduced in 2.6.36. Reported-by: Ido Yariv Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/wl12xx/testmode.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/wl12xx/testmode.c b/drivers/net/wireless/wl12xx/testmode.c index e64403b6896d7..6ec06a4a4c6df 100644 --- a/drivers/net/wireless/wl12xx/testmode.c +++ b/drivers/net/wireless/wl12xx/testmode.c @@ -204,7 +204,10 @@ static int wl1271_tm_cmd_nvs_push(struct wl1271 *wl, struct nlattr *tb[]) kfree(wl->nvs); - wl->nvs = kzalloc(sizeof(struct wl1271_nvs_file), GFP_KERNEL); + if (len != sizeof(struct wl1271_nvs_file)) + return -EINVAL; + + wl->nvs = kzalloc(len, GFP_KERNEL); if (!wl->nvs) { wl1271_error("could not allocate memory for the nvs file"); ret = -ENOMEM; From bb03f46aa73a6231fa8bcabe419c654300c098da Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 27 Feb 2011 14:51:54 -0300 Subject: [PATCH 1128/2556] media/radio/wl1273: fix build errors commit 1b149bbe9156d2eb2afd5a072bd61ad0d4bfaca7 upstream. RADIO_WL1273 needs to make sure that the mfd core is built to avoid build errors: ERROR: "mfd_add_devices" [drivers/mfd/wl1273-core.ko] undefined! ERROR: "mfd_remove_devices" [drivers/mfd/wl1273-core.ko] undefined! Signed-off-by: Randy Dunlap Cc: Matti Aaltonen Signed-off-by: Mauro Carvalho Chehab Cc: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/media/radio/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index ecdffa6aac664..c0902465bdfd7 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -441,6 +441,7 @@ config RADIO_TIMBERDALE config RADIO_WL1273 tristate "Texas Instruments WL1273 I2C FM Radio" depends on I2C && VIDEO_V4L2 + select MFD_CORE select MFD_WL1273_CORE select FW_LOADER ---help--- From c9c37ae7d59c7bebbc25f03161966e6ccf366430 Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Wed, 30 Mar 2011 14:02:46 -0400 Subject: [PATCH 1129/2556] b43: allocate receive buffers big enough for max frame len + offset commit c85ce65ecac078ab1a1835c87c4a6319cf74660a upstream. Otherwise, skb_put inside of dma_rx can fail... https://bugzilla.kernel.org/show_bug.cgi?id=32042 Signed-off-by: John W. Linville Acked-by: Larry Finger Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/b43/dma.c | 2 +- drivers/net/wireless/b43/dma.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index 3d5566e7af0ad..ff0f5ba14b2cf 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -1536,7 +1536,7 @@ static void dma_rx(struct b43_dmaring *ring, int *slot) dmaaddr = meta->dmaaddr; goto drop_recycle_buffer; } - if (unlikely(len > ring->rx_buffersize)) { + if (unlikely(len + ring->frameoffset > ring->rx_buffersize)) { /* The data did not fit into one descriptor buffer * and is split over multiple buffers. * This should never happen, as we try to allocate buffers diff --git a/drivers/net/wireless/b43/dma.h b/drivers/net/wireless/b43/dma.h index a01c2100f1664..e8a80a1251bf6 100644 --- a/drivers/net/wireless/b43/dma.h +++ b/drivers/net/wireless/b43/dma.h @@ -163,7 +163,7 @@ struct b43_dmadesc_generic { /* DMA engine tuning knobs */ #define B43_TXRING_SLOTS 256 #define B43_RXRING_SLOTS 64 -#define B43_DMA0_RX_BUFFERSIZE IEEE80211_MAX_FRAME_LEN +#define B43_DMA0_RX_BUFFERSIZE (B43_DMA0_RX_FRAMEOFFSET + IEEE80211_MAX_FRAME_LEN) /* Pointer poison */ #define B43_DMA_PTR_POISON ((void *)ERR_PTR(-ENOMEM)) From a0ef5893ec7ae42f502993f6e4f6e60e6f941ce0 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Mon, 14 Feb 2011 13:54:26 +0300 Subject: [PATCH 1130/2556] Bluetooth: sco: fix information leak to userspace commit c4c896e1471aec3b004a693c689f60be3b17ac86 upstream. struct sco_conninfo has one padding byte in the end. Local variable cinfo of type sco_conninfo is copied to userspace with this uninizialized one byte, leading to old stack contents leak. Signed-off-by: Vasiliy Kulikov Signed-off-by: Gustavo F. Padovan Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/sco.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 960c6d1637da0..926ed39912ea7 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -703,6 +703,7 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user break; } + memset(&cinfo, 0, sizeof(cinfo)); cinfo.hci_handle = sco_pi(sk)->conn->hcon->handle; memcpy(cinfo.dev_class, sco_pi(sk)->conn->hcon->dev_class, 3); From 1d7b65a52f9bba3af57f1145e18fefff6a8df06a Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Mon, 14 Feb 2011 16:49:23 +0100 Subject: [PATCH 1131/2556] bridge: netfilter: fix information leak commit d846f71195d57b0bbb143382647c2c6638b04c5a upstream. Struct tmp is copied from userspace. It is not checked whether the "name" field is NULL terminated. This may lead to buffer overflow and passing contents of kernel stack as a module name to try_then_request_module() and, consequently, to modprobe commandline. It would be seen by all userspace processes. Signed-off-by: Vasiliy Kulikov Signed-off-by: Patrick McHardy Signed-off-by: Greg Kroah-Hartman --- net/bridge/netfilter/ebtables.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 16df0532d4b9a..47acf4a50efe8 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1107,6 +1107,8 @@ static int do_replace(struct net *net, const void __user *user, if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter)) return -ENOMEM; + tmp.name[sizeof(tmp.name) - 1] = 0; + countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids; newinfo = vmalloc(sizeof(*newinfo) + countersize); if (!newinfo) From f4fc0e56a21f9b5c345965f49da91f330b7cf202 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Mon, 14 Feb 2011 13:54:31 +0300 Subject: [PATCH 1132/2556] Bluetooth: bnep: fix buffer overflow commit 43629f8f5ea32a998d06d1bb41eefa0e821ff573 upstream. Struct ca is copied from userspace. It is not checked whether the "device" field is NULL terminated. This potentially leads to BUG() inside of alloc_netdev_mqs() and/or information leak by creating a device with a name made of contents of kernel stack. Signed-off-by: Vasiliy Kulikov Signed-off-by: Gustavo F. Padovan Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/bnep/sock.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c index 2862f53b66b15..d935da71ab3b5 100644 --- a/net/bluetooth/bnep/sock.c +++ b/net/bluetooth/bnep/sock.c @@ -88,6 +88,7 @@ static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long sockfd_put(nsock); return -EBADFD; } + ca.device[sizeof(ca.device)-1] = 0; err = bnep_add_connection(&ca, nsock); if (!err) { From b2d10ef61c4f466e6f117045358cb48675d49734 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Thu, 24 Mar 2011 14:51:21 -0300 Subject: [PATCH 1133/2556] Bluetooth: add support for Apple MacBook Pro 8,2 commit 63a8588debd4dc72becb9e27add9343c76301c7d upstream. Just adding the vendor details makes it work fine. Signed-off-by: Marc-Antoine Perennou Signed-off-by: Gustavo F. Padovan Signed-off-by: Greg Kroah-Hartman Tested-by: Grant Likely --- drivers/bluetooth/btusb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 700a3840fddc2..f44ca40a28faa 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -71,6 +71,9 @@ static struct usb_device_id btusb_table[] = { /* Apple MacBookAir3,1, MacBookAir3,2 */ { USB_DEVICE(0x05ac, 0x821b) }, + /* Apple MacBookPro8,2 */ + { USB_DEVICE(0x05ac, 0x821a) }, + /* AVM BlueFRITZ! USB v2.0 */ { USB_DEVICE(0x057c, 0x3800) }, From b8b602b32fb7533406880478b3872d1e1c6911dc Mon Sep 17 00:00:00 2001 From: Goldwyn Rodrigues Date: Thu, 17 Feb 2011 09:44:40 -0600 Subject: [PATCH 1134/2556] Treat writes as new when holes span across page boundaries commit 272b62c1f0f6f742046e45b50b6fec98860208a0 upstream. When a hole spans across page boundaries, the next write forces a read of the block. This could end up reading existing garbage data from the disk in ocfs2_map_page_blocks. This leads to non-zero holes. In order to avoid this, mark the writes as new when the holes span across page boundaries. Signed-off-by: Goldwyn Rodrigues Signed-off-by: jlbec Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/aops.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 1fbb0e20131bf..bbba782cce264 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -1026,6 +1026,12 @@ static int ocfs2_prepare_page_for_write(struct inode *inode, u64 *p_blkno, ocfs2_figure_cluster_boundaries(OCFS2_SB(inode->i_sb), cpos, &cluster_start, &cluster_end); + /* treat the write as new if the a hole/lseek spanned across + * the page boundary. + */ + new = new | ((i_size_read(inode) <= page_offset(page)) && + (page_offset(page) <= user_pos)); + if (page == wc->w_target_page) { map_from = user_pos & (PAGE_CACHE_SIZE - 1); map_to = map_from + user_len; From fc50df845604fa6802b693fda447f598e23e12c2 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Tue, 29 Mar 2011 13:31:25 +0200 Subject: [PATCH 1135/2556] char/tpm: Fix unitialized usage of data buffer commit 1309d7afbed112f0e8e90be9af975550caa0076b upstream. This patch fixes information leakage to the userspace by initializing the data buffer to zero. Reported-by: Peter Huewe Signed-off-by: Peter Huewe Signed-off-by: Marcel Selhorst [ Also removed the silly "* sizeof(u8)". If that isn't 1, we have way deeper problems than a simple multiplication can fix. - Linus ] Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 1f46f1cd9225c..7beb0e25f1e1e 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -980,7 +980,7 @@ int tpm_open(struct inode *inode, struct file *file) return -EBUSY; } - chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); + chip->data_buffer = kzalloc(TPM_BUFSIZE, GFP_KERNEL); if (chip->data_buffer == NULL) { clear_bit(0, &chip->is_open); put_device(chip->dev); From ebb12d3d3feb70061157ec5f71d7366f7b6e6dd9 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Tue, 15 Mar 2011 13:36:05 +0100 Subject: [PATCH 1136/2556] netfilter: ip_tables: fix infoleak to userspace commit 78b79876761b86653df89c48a7010b5cbd41a84a upstream. Structures ipt_replace, compat_ipt_replace, and xt_get_revision are copied from userspace. Fields of these structs that are zero-terminated strings are not checked. When they are used as argument to a format string containing "%s" in request_module(), some sensitive information is leaked to userspace via argument of spawned modprobe process. The first and the third bugs were introduced before the git epoch; the second was introduced in 2722971c (v2.6.17-rc1). To trigger the bug one should have CAP_NET_ADMIN. Signed-off-by: Vasiliy Kulikov Signed-off-by: Patrick McHardy Signed-off-by: Greg Kroah-Hartman --- net/ipv4/netfilter/ip_tables.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 652efea013dcc..3039101cbaed9 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -1261,6 +1261,7 @@ do_replace(struct net *net, const void __user *user, unsigned int len) /* overflow check */ if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) return -ENOMEM; + tmp.name[sizeof(tmp.name)-1] = 0; newinfo = xt_alloc_table_info(tmp.size); if (!newinfo) @@ -1805,6 +1806,7 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len) return -ENOMEM; if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) return -ENOMEM; + tmp.name[sizeof(tmp.name)-1] = 0; newinfo = xt_alloc_table_info(tmp.size); if (!newinfo) @@ -2034,6 +2036,7 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) ret = -EFAULT; break; } + rev.name[sizeof(rev.name)-1] = 0; if (cmd == IPT_SO_GET_REVISION_TARGET) target = 1; From 53a080a724fd79d44132cc6e2f08246021503622 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 20 Mar 2011 15:40:06 +0100 Subject: [PATCH 1137/2556] netfilter: xtables: fix reentrancy commit db856674ac69e31946e56085239757cca3f7655f upstream. commit f3c5c1bfd4308 (make ip_tables reentrant) introduced a race in handling the stackptr restore, at the end of ipt_do_table() We should do it before the call to xt_info_rdunlock_bh(), or we allow cpu preemption and another cpu overwrites stackptr of original one. A second fix is to change the underflow test to check the origptr value instead of 0 to detect underflow, or else we allow a jump from different hooks. Signed-off-by: Eric Dumazet Cc: Jan Engelhardt Signed-off-by: Patrick McHardy Signed-off-by: Greg Kroah-Hartman --- net/ipv4/netfilter/ip_tables.c | 4 ++-- net/ipv6/netfilter/ip6_tables.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 3039101cbaed9..92fb4c5e5c9bd 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -387,7 +387,7 @@ ipt_do_table(struct sk_buff *skb, verdict = (unsigned)(-v) - 1; break; } - if (*stackptr == 0) { + if (*stackptr <= origptr) { e = get_entry(table_base, private->underflow[hook]); pr_debug("Underflow (this is normal) " @@ -427,10 +427,10 @@ ipt_do_table(struct sk_buff *skb, /* Verdict */ break; } while (!acpar.hotdrop); - xt_info_rdunlock_bh(); pr_debug("Exiting %s; resetting sp from %u to %u\n", __func__, *stackptr, origptr); *stackptr = origptr; + xt_info_rdunlock_bh(); #ifdef DEBUG_ALLOW_ALL return NF_ACCEPT; #else diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 7d227c644f727..830921e7e82b3 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -410,7 +410,7 @@ ip6t_do_table(struct sk_buff *skb, verdict = (unsigned)(-v) - 1; break; } - if (*stackptr == 0) + if (*stackptr <= origptr) e = get_entry(table_base, private->underflow[hook]); else @@ -441,8 +441,8 @@ ip6t_do_table(struct sk_buff *skb, break; } while (!acpar.hotdrop); - xt_info_rdunlock_bh(); *stackptr = origptr; + xt_info_rdunlock_bh(); #ifdef DEBUG_ALLOW_ALL return NF_ACCEPT; From a988a0f40682cd871e3f5ad61fc779e5c7d36f5e Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Tue, 15 Mar 2011 13:35:21 +0100 Subject: [PATCH 1138/2556] netfilter: arp_tables: fix infoleak to userspace commit 42eab94fff18cb1091d3501cd284d6bd6cc9c143 upstream. Structures ipt_replace, compat_ipt_replace, and xt_get_revision are copied from userspace. Fields of these structs that are zero-terminated strings are not checked. When they are used as argument to a format string containing "%s" in request_module(), some sensitive information is leaked to userspace via argument of spawned modprobe process. The first bug was introduced before the git epoch; the second is introduced by 6b7d31fc (v2.6.15-rc1); the third is introduced by 6b7d31fc (v2.6.15-rc1). To trigger the bug one should have CAP_NET_ADMIN. Signed-off-by: Vasiliy Kulikov Signed-off-by: Patrick McHardy Signed-off-by: Greg Kroah-Hartman --- net/ipv4/netfilter/arp_tables.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index e855fffaed95b..6d79aa10e6299 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -1065,6 +1065,7 @@ static int do_replace(struct net *net, const void __user *user, /* overflow check */ if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) return -ENOMEM; + tmp.name[sizeof(tmp.name)-1] = 0; newinfo = xt_alloc_table_info(tmp.size); if (!newinfo) @@ -1486,6 +1487,7 @@ static int compat_do_replace(struct net *net, void __user *user, return -ENOMEM; if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) return -ENOMEM; + tmp.name[sizeof(tmp.name)-1] = 0; newinfo = xt_alloc_table_info(tmp.size); if (!newinfo) @@ -1738,6 +1740,7 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len ret = -EFAULT; break; } + rev.name[sizeof(rev.name)-1] = 0; try_then_request_module(xt_find_revision(NFPROTO_ARP, rev.name, rev.revision, 1, &ret), From 5e8ba35db5884f7e3449eba28886a8f10c7e911e Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Sun, 20 Mar 2011 15:42:52 +0100 Subject: [PATCH 1139/2556] netfilter: ipt_CLUSTERIP: fix buffer overflow commit 961ed183a9fd080cf306c659b8736007e44065a5 upstream. 'buffer' string is copied from userspace. It is not checked whether it is zero terminated. This may lead to overflow inside of simple_strtoul(). Changli Gao suggested to copy not more than user supplied 'size' bytes. It was introduced before the git epoch. Files "ipt_CLUSTERIP/*" are root writable only by default, however, on some setups permissions might be relaxed to e.g. network admin user. Signed-off-by: Vasiliy Kulikov Acked-by: Changli Gao Signed-off-by: Patrick McHardy Signed-off-by: Greg Kroah-Hartman --- net/ipv4/netfilter/ipt_CLUSTERIP.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index 1e26a4897655b..af7dec683e104 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -669,8 +669,11 @@ static ssize_t clusterip_proc_write(struct file *file, const char __user *input, char buffer[PROC_WRITELEN+1]; unsigned long nodenum; - if (copy_from_user(buffer, input, PROC_WRITELEN)) + if (size > PROC_WRITELEN) + return -EIO; + if (copy_from_user(buffer, input, size)) return -EFAULT; + buffer[size] = 0; if (*buffer == '+') { nodenum = simple_strtoul(buffer+1, NULL, 10); From 7a25e7511718fa16f65ff783d65f7f42fca823e6 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Tue, 15 Mar 2011 13:37:13 +0100 Subject: [PATCH 1140/2556] ipv6: netfilter: ip6_tables: fix infoleak to userspace commit 6a8ab060779779de8aea92ce3337ca348f973f54 upstream. Structures ip6t_replace, compat_ip6t_replace, and xt_get_revision are copied from userspace. Fields of these structs that are zero-terminated strings are not checked. When they are used as argument to a format string containing "%s" in request_module(), some sensitive information is leaked to userspace via argument of spawned modprobe process. The first bug was introduced before the git epoch; the second was introduced in 3bc3fe5e (v2.6.25-rc1); the third is introduced by 6b7d31fc (v2.6.15-rc1). To trigger the bug one should have CAP_NET_ADMIN. Signed-off-by: Vasiliy Kulikov Signed-off-by: Patrick McHardy Signed-off-by: Greg Kroah-Hartman --- net/ipv6/netfilter/ip6_tables.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 830921e7e82b3..eadafbfc9ef4b 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -1274,6 +1274,7 @@ do_replace(struct net *net, const void __user *user, unsigned int len) /* overflow check */ if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) return -ENOMEM; + tmp.name[sizeof(tmp.name)-1] = 0; newinfo = xt_alloc_table_info(tmp.size); if (!newinfo) @@ -1820,6 +1821,7 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len) return -ENOMEM; if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) return -ENOMEM; + tmp.name[sizeof(tmp.name)-1] = 0; newinfo = xt_alloc_table_info(tmp.size); if (!newinfo) @@ -2049,6 +2051,7 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) ret = -EFAULT; break; } + rev.name[sizeof(rev.name)-1] = 0; if (cmd == IP6T_SO_GET_REVISION_TARGET) target = 1; From 5b9cd5191326733d0d8d2377d7fbd8981a40f343 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 4 Feb 2011 15:24:14 +0300 Subject: [PATCH 1141/2556] scsi_transport_iscsi: make priv_sess file writeable only by root commit 523f3c80bc41d663d5b35c0cd6ce0fad7f3e7188 upstream. Signed-off-by: Vasiliy Kulikov Acked-by: Mike Christie Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_transport_iscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index f905ecb5704d7..01543d297b53a 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -1847,7 +1847,7 @@ store_priv_session_##field(struct device *dev, \ #define iscsi_priv_session_rw_attr(field, format) \ iscsi_priv_session_attr_show(field, format) \ iscsi_priv_session_attr_store(field) \ -static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO | S_IWUGO, \ +static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO | S_IWUSR, \ show_priv_session_##field, \ store_priv_session_##field) iscsi_priv_session_rw_attr(recovery_tmo, "%d"); From 33295acabad896b34fdbcbe01b00394f80ee9768 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 4 Feb 2011 15:23:43 +0300 Subject: [PATCH 1142/2556] mfd: ab8500: world-writable debugfs register-* files commit 44bdcb54df2714da18c4a0c6f711a350ab4ed93c upstream. Don't allow everybody to interact with hardware registers. Signed-off-by: Vasiliy Kulikov Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/ab8500-debugfs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 3c1541ae72232..64748e42ac039 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -585,18 +585,18 @@ static int __devinit ab8500_debug_probe(struct platform_device *plf) goto exit_destroy_dir; ab8500_bank_file = debugfs_create_file("register-bank", - (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, &ab8500_bank_fops); + (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_bank_fops); if (!ab8500_bank_file) goto exit_destroy_reg; ab8500_address_file = debugfs_create_file("register-address", - (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, + (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_address_fops); if (!ab8500_address_file) goto exit_destroy_bank; ab8500_val_file = debugfs_create_file("register-value", - (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, &ab8500_val_fops); + (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_val_fops); if (!ab8500_val_file) goto exit_destroy_address; From 75cdaf7725a40c9b62fdf7d300a47e9e024a09c1 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 4 Feb 2011 15:23:39 +0300 Subject: [PATCH 1143/2556] mfd: ab3500: world-writable debugfs register-* files commit 90c861c2a83d974684974441093ff8a50e6b430b upstream. Don't allow everybody to interact with hardware registers. Signed-off-by: Vasiliy Kulikov Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/ab3550-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c index 5fbca346b998d..681984df1c286 100644 --- a/drivers/mfd/ab3550-core.c +++ b/drivers/mfd/ab3550-core.c @@ -1053,17 +1053,17 @@ static inline void ab3550_setup_debugfs(struct ab3550 *ab) goto exit_destroy_dir; ab3550_bank_file = debugfs_create_file("register-bank", - (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_bank_fops); + (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_bank_fops); if (!ab3550_bank_file) goto exit_destroy_reg; ab3550_address_file = debugfs_create_file("register-address", - (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_address_fops); + (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_address_fops); if (!ab3550_address_file) goto exit_destroy_bank; ab3550_val_file = debugfs_create_file("register-value", - (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_val_fops); + (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_val_fops); if (!ab3550_val_file) goto exit_destroy_address; From 3e9b1b39e0a6a7bb012f2b7b4a7e7ae91bcf90ac Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 4 Feb 2011 15:23:36 +0300 Subject: [PATCH 1144/2556] mfd: ab3100: world-writable debugfs *_priv files commit f8a0697722d12a201588225999cfc8bfcbc82781 upstream. Don't allow everybody to change device hardware registers. Signed-off-by: Vasiliy Kulikov Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/ab3100-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c index 4193af5f27439..1707d224232d1 100644 --- a/drivers/mfd/ab3100-core.c +++ b/drivers/mfd/ab3100-core.c @@ -613,7 +613,7 @@ static void ab3100_setup_debugfs(struct ab3100 *ab3100) ab3100_get_priv.ab3100 = ab3100; ab3100_get_priv.mode = false; ab3100_get_reg_file = debugfs_create_file("get_reg", - S_IWUGO, ab3100_dir, &ab3100_get_priv, + S_IWUSR, ab3100_dir, &ab3100_get_priv, &ab3100_get_set_reg_fops); if (!ab3100_get_reg_file) { err = -ENOMEM; @@ -623,7 +623,7 @@ static void ab3100_setup_debugfs(struct ab3100 *ab3100) ab3100_set_priv.ab3100 = ab3100; ab3100_set_priv.mode = true; ab3100_set_reg_file = debugfs_create_file("set_reg", - S_IWUGO, ab3100_dir, &ab3100_set_priv, + S_IWUSR, ab3100_dir, &ab3100_set_priv, &ab3100_get_set_reg_fops); if (!ab3100_set_reg_file) { err = -ENOMEM; From 89371f01f085a0c9977417085427f080564f7b08 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Tue, 22 Mar 2011 16:34:53 -0700 Subject: [PATCH 1145/2556] drivers/rtc/rtc-ds1511.c: world-writable sysfs nvram file commit 49d50fb1c28738ef6bad0c2b87d5355a1653fed5 upstream. Don't allow everybogy to write to NVRAM. Signed-off-by: Vasiliy Kulikov Cc: Andy Sharp Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/rtc/rtc-ds1511.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c index 37268e97de49e..afeb5469708b8 100644 --- a/drivers/rtc/rtc-ds1511.c +++ b/drivers/rtc/rtc-ds1511.c @@ -485,7 +485,7 @@ ds1511_nvram_write(struct file *filp, struct kobject *kobj, static struct bin_attribute ds1511_nvram_attr = { .attr = { .name = "nvram", - .mode = S_IRUGO | S_IWUGO, + .mode = S_IRUGO | S_IWUSR, }, .size = DS1511_RAM_MAX, .read = ds1511_nvram_read, From 94e8a16b0f87528287f0ed68ca7f1855ac621bfb Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Tue, 22 Mar 2011 16:34:01 -0700 Subject: [PATCH 1146/2556] drivers/misc/ep93xx_pwm.c: world-writable sysfs files commit deb187e72470b0382d4f0cb859e76e1ebc3a1082 upstream. Don't allow everybody to change device settings. Signed-off-by: Vasiliy Kulikov Acked-by: Hartley Sweeten Cc: Matthieu Crapet Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ep93xx_pwm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 46b3439673e90..16d7179e2f9b8 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -249,11 +249,11 @@ static ssize_t ep93xx_pwm_set_invert(struct device *dev, static DEVICE_ATTR(min_freq, S_IRUGO, ep93xx_pwm_get_min_freq, NULL); static DEVICE_ATTR(max_freq, S_IRUGO, ep93xx_pwm_get_max_freq, NULL); -static DEVICE_ATTR(freq, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(freq, S_IWUSR | S_IRUGO, ep93xx_pwm_get_freq, ep93xx_pwm_set_freq); -static DEVICE_ATTR(duty_percent, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(duty_percent, S_IWUSR | S_IRUGO, ep93xx_pwm_get_duty_percent, ep93xx_pwm_set_duty_percent); -static DEVICE_ATTR(invert, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(invert, S_IWUSR | S_IRUGO, ep93xx_pwm_get_invert, ep93xx_pwm_set_invert); static struct attribute *ep93xx_pwm_attrs[] = { From 42fa7c8b992022e7f96bb2e027bf36000038a439 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Tue, 22 Mar 2011 16:30:20 -0700 Subject: [PATCH 1147/2556] drivers/leds/leds-lp5523.c: world-writable engine* sysfs files commit ccd7510fd8dea5b4b2af87fb2aef2ebd6b23b76b upstream. Don't allow everybody to change LED settings. Signed-off-by: Vasiliy Kulikov Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/leds/leds-lp5523.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index d0c4068ecddd7..e19fed25f1376 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -713,7 +713,7 @@ static ssize_t store_current(struct device *dev, } /* led class device attributes */ -static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current, store_current); +static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current); static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); static struct attribute *lp5523_led_attributes[] = { @@ -727,21 +727,21 @@ static struct attribute_group lp5523_led_attribute_group = { }; /* device attributes */ -static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, show_engine1_mode, store_engine1_mode); -static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUSR, show_engine2_mode, store_engine2_mode); -static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUSR, show_engine3_mode, store_engine3_mode); -static DEVICE_ATTR(engine1_leds, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine1_leds, S_IRUGO | S_IWUSR, show_engine1_leds, store_engine1_leds); -static DEVICE_ATTR(engine2_leds, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine2_leds, S_IRUGO | S_IWUSR, show_engine2_leds, store_engine2_leds); -static DEVICE_ATTR(engine3_leds, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine3_leds, S_IRUGO | S_IWUSR, show_engine3_leds, store_engine3_leds); -static DEVICE_ATTR(engine1_load, S_IWUGO, NULL, store_engine1_load); -static DEVICE_ATTR(engine2_load, S_IWUGO, NULL, store_engine2_load); -static DEVICE_ATTR(engine3_load, S_IWUGO, NULL, store_engine3_load); +static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load); +static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load); +static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load); static DEVICE_ATTR(selftest, S_IRUGO, lp5523_selftest, NULL); static struct attribute *lp5523_attributes[] = { From 182016ec35485381efc0566d88a51d0942fb354d Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Tue, 22 Mar 2011 16:30:19 -0700 Subject: [PATCH 1148/2556] drivers/leds/leds-lp5521.c: world-writable sysfs engine* files commit 67d1da79b25c05d9a38b820bb5b5d89c91070ab2 upstream. Don't allow everybody to change LED settings. Signed-off-by: Vasiliy Kulikov Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/leds/leds-lp5521.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 80a3ae3c00b93..c0cff64a1ae64 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -534,7 +534,7 @@ static ssize_t lp5521_selftest(struct device *dev, } /* led class device attributes */ -static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current, store_current); +static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current); static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); static struct attribute *lp5521_led_attributes[] = { @@ -548,15 +548,15 @@ static struct attribute_group lp5521_led_attribute_group = { }; /* device attributes */ -static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, show_engine1_mode, store_engine1_mode); -static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUSR, show_engine2_mode, store_engine2_mode); -static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUSR, show_engine3_mode, store_engine3_mode); -static DEVICE_ATTR(engine1_load, S_IWUGO, NULL, store_engine1_load); -static DEVICE_ATTR(engine2_load, S_IWUGO, NULL, store_engine2_load); -static DEVICE_ATTR(engine3_load, S_IWUGO, NULL, store_engine3_load); +static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load); +static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load); +static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load); static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); static struct attribute *lp5521_attributes[] = { From 0fc073e4b3b2cd9f5770190e585349adf9743ee3 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Thu, 17 Mar 2011 01:40:10 +0000 Subject: [PATCH 1149/2556] econet: 4 byte infoleak to the network commit 67c5c6cb8129c595f21e88254a3fc6b3b841ae8e upstream. struct aunhdr has 4 padding bytes between 'pad' and 'handle' fields on x86_64. These bytes are not initialized in the variable 'ah' before sending 'ah' to the network. This leads to 4 bytes kernel stack infoleak. This bug was introduced before the git epoch. Signed-off-by: Vasiliy Kulikov Acked-by: Phil Blundell Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/econet/af_econet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c index 0c2826337919a..116d3fd3d6691 100644 --- a/net/econet/af_econet.c +++ b/net/econet/af_econet.c @@ -435,10 +435,10 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, udpdest.sin_addr.s_addr = htonl(network | addr.station); } + memset(&ah, 0, sizeof(ah)); ah.port = port; ah.cb = cb & 0x7f; ah.code = 2; /* magic */ - ah.pad = 0; /* tack our header on the front of the iovec */ size = sizeof(struct aunhdr); From 7f96f975d3419d9155be5c82c6a1eac2f840b761 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 4 Apr 2011 15:21:02 +0200 Subject: [PATCH 1150/2556] netfilter: h323: bug in parsing of ASN1 SEQOF field commit b4232a22776aa5d063f890d21ca69870dbbe431b upstream. Static analyzer of clang found a dead store which appears to be a bug in reading count of items in SEQOF field, only the lower byte of word is stored. This may lead to corrupted read and communication shutdown. The bug has been in the module since it's first inclusion into linux kernel. [Patrick: the bug is real, but without practical consequence since the largest amount of sequence-of members we parse is 30.] Signed-off-by: David Sterba Signed-off-by: Patrick McHardy Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_h323_asn1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_h323_asn1.c b/net/netfilter/nf_conntrack_h323_asn1.c index 867882313e499..bcd5ed6b71304 100644 --- a/net/netfilter/nf_conntrack_h323_asn1.c +++ b/net/netfilter/nf_conntrack_h323_asn1.c @@ -631,7 +631,7 @@ static int decode_seqof(bitstr_t *bs, const struct field_t *f, CHECK_BOUND(bs, 2); count = *bs->cur++; count <<= 8; - count = *bs->cur++; + count += *bs->cur++; break; case SEMI: BYTE_ALIGN(bs); From 42f9f8d3b8952cff082276a7e3f21e071113384e Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Wed, 23 Mar 2011 10:53:41 -0400 Subject: [PATCH 1151/2556] sound/oss: remove offset from load_patch callbacks commit b769f49463711205d57286e64cf535ed4daf59e9 upstream. Was: [PATCH] sound/oss/midi_synth: prevent underflow, use of uninitialized value, and signedness issue The offset passed to midi_synth_load_patch() can be essentially arbitrary. If it's greater than the header length, this will result in a copy_from_user(dst, src, negative_val). While this will just return -EFAULT on x86, on other architectures this may cause memory corruption. Additionally, the length field of the sysex_info structure may not be initialized prior to its use. Finally, a signed comparison may result in an unintentionally large loop. On suggestion by Takashi Iwai, version two removes the offset argument from the load_patch callbacks entirely, which also resolves similar issues in opl3. Compile tested only. v3 adjusts comments and hopefully gets copy offsets right. Signed-off-by: Dan Rosenberg Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/oss/dev_table.h | 2 +- sound/oss/midi_synth.c | 30 +++++++++++++----------------- sound/oss/midi_synth.h | 2 +- sound/oss/opl3.c | 8 ++------ sound/oss/sequencer.c | 2 +- 5 files changed, 18 insertions(+), 26 deletions(-) diff --git a/sound/oss/dev_table.h b/sound/oss/dev_table.h index b7617bee6388f..0199a317c5a9a 100644 --- a/sound/oss/dev_table.h +++ b/sound/oss/dev_table.h @@ -271,7 +271,7 @@ struct synth_operations void (*reset) (int dev); void (*hw_control) (int dev, unsigned char *event); int (*load_patch) (int dev, int format, const char __user *addr, - int offs, int count, int pmgr_flag); + int count, int pmgr_flag); void (*aftertouch) (int dev, int voice, int pressure); void (*controller) (int dev, int voice, int ctrl_num, int value); void (*panning) (int dev, int voice, int value); diff --git a/sound/oss/midi_synth.c b/sound/oss/midi_synth.c index 3c09374ea5bf1..2292c230d7e6f 100644 --- a/sound/oss/midi_synth.c +++ b/sound/oss/midi_synth.c @@ -476,7 +476,7 @@ EXPORT_SYMBOL(midi_synth_hw_control); int midi_synth_load_patch(int dev, int format, const char __user *addr, - int offs, int count, int pmgr_flag) + int count, int pmgr_flag) { int orig_dev = synth_devs[dev]->midi_dev; @@ -491,33 +491,29 @@ midi_synth_load_patch(int dev, int format, const char __user *addr, if (!prefix_cmd(orig_dev, 0xf0)) return 0; + /* Invalid patch format */ if (format != SYSEX_PATCH) - { -/* printk("MIDI Error: Invalid patch format (key) 0x%x\n", format);*/ return -EINVAL; - } + + /* Patch header too short */ if (count < hdr_size) - { -/* printk("MIDI Error: Patch header too short\n");*/ return -EINVAL; - } + count -= hdr_size; /* - * Copy the header from user space but ignore the first bytes which have - * been transferred already. + * Copy the header from user space */ - if(copy_from_user(&((char *) &sysex)[offs], &(addr)[offs], hdr_size - offs)) + if (copy_from_user(&sysex, addr, hdr_size)) return -EFAULT; - - if (count < sysex.len) - { -/* printk(KERN_WARNING "MIDI Warning: Sysex record too short (%d<%d)\n", count, (int) sysex.len);*/ + + /* Sysex record too short */ + if ((unsigned)count < (unsigned)sysex.len) sysex.len = count; - } - left = sysex.len; - src_offs = 0; + + left = sysex.len; + src_offs = 0; for (i = 0; i < left && !signal_pending(current); i++) { diff --git a/sound/oss/midi_synth.h b/sound/oss/midi_synth.h index 6bc9d00bc77c4..b64ddd6c4abc0 100644 --- a/sound/oss/midi_synth.h +++ b/sound/oss/midi_synth.h @@ -8,7 +8,7 @@ int midi_synth_open (int dev, int mode); void midi_synth_close (int dev); void midi_synth_hw_control (int dev, unsigned char *event); int midi_synth_load_patch (int dev, int format, const char __user * addr, - int offs, int count, int pmgr_flag); + int count, int pmgr_flag); void midi_synth_panning (int dev, int channel, int pressure); void midi_synth_aftertouch (int dev, int channel, int pressure); void midi_synth_controller (int dev, int channel, int ctrl_num, int value); diff --git a/sound/oss/opl3.c b/sound/oss/opl3.c index f4ffdff9b2451..407cd677950bc 100644 --- a/sound/oss/opl3.c +++ b/sound/oss/opl3.c @@ -820,7 +820,7 @@ static void opl3_hw_control(int dev, unsigned char *event) } static int opl3_load_patch(int dev, int format, const char __user *addr, - int offs, int count, int pmgr_flag) + int count, int pmgr_flag) { struct sbi_instrument ins; @@ -830,11 +830,7 @@ static int opl3_load_patch(int dev, int format, const char __user *addr, return -EINVAL; } - /* - * What the fuck is going on here? We leave junk in the beginning - * of ins and then check the field pretty close to that beginning? - */ - if(copy_from_user(&((char *) &ins)[offs], addr + offs, sizeof(ins) - offs)) + if (copy_from_user(&ins, addr, sizeof(ins))) return -EFAULT; if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) diff --git a/sound/oss/sequencer.c b/sound/oss/sequencer.c index 5ea1098ac427a..30bcfe470f831 100644 --- a/sound/oss/sequencer.c +++ b/sound/oss/sequencer.c @@ -241,7 +241,7 @@ int sequencer_write(int dev, struct file *file, const char __user *buf, int coun return -ENXIO; fmt = (*(short *) &event_rec[0]) & 0xffff; - err = synth_devs[dev]->load_patch(dev, fmt, buf, p + 4, c, 0); + err = synth_devs[dev]->load_patch(dev, fmt, buf + p, c, 0); if (err < 0) return err; From 4a349aa0c074ad2c46a6d2a1d3500da7ae300c4d Mon Sep 17 00:00:00 2001 From: Alexander Strakh Date: Tue, 25 Jan 2011 18:00:13 -0300 Subject: [PATCH 1152/2556] drivers/media/video/tlg2300/pd-video.c: Remove second mutex_unlock in pd_vidioc_s_fmt commit a07500ef690fcbec76e879ee2093d7ca69883825 upstream. Error path in file drivers/media/video/tlg2300/pd-video.c: 1. First mutex_unlock on &pd->lock in line 767 (in function that called from line 805) 2. Second in line 806 805 pd_vidioc_s_fmt(pd, &f->fmt.pix); 806 mutex_unlock(&pd->lock); Found by Linux Device Drivers Verification Project Signed-off-by: Alexander Strakh Acked-by: Huang Shijie Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/video/tlg2300/pd-video.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c index df33a1d188bbf..a794ae62aebfc 100644 --- a/drivers/media/video/tlg2300/pd-video.c +++ b/drivers/media/video/tlg2300/pd-video.c @@ -764,10 +764,8 @@ static int pd_vidioc_s_fmt(struct poseidon *pd, struct v4l2_pix_format *pix) } ret |= send_set_req(pd, VIDEO_ROSOLU_SEL, vid_resol, &cmd_status); - if (ret || cmd_status) { - mutex_unlock(&pd->lock); + if (ret || cmd_status) return -EBUSY; - } pix_def->pixelformat = pix->pixelformat; /* save it */ pix->height = (context->tvnormid & V4L2_STD_525_60) ? 480 : 576; From 88210a07190401c7f5293a8c9eb26c693536bd1b Mon Sep 17 00:00:00 2001 From: Chun-Yi Lee Date: Mon, 28 Mar 2011 16:52:02 +0800 Subject: [PATCH 1153/2556] acer-wmi: does not set persistence state by rfkill_init_sw_state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 8215af019040ce9182728afee9642d8fdeb17f59 upstream. Acer BIOS keeps devices state when system reboot, but reset to default device states (Wlan on, Bluetooth off, wwan on) if system cold boot. That means BIOS's initial state is not always real persistence. So, removed rfkill_init_sw_state because it sets initial state to persistence then replicate to other new killswitch when rfkill-input enabled. After removed it, acer-wmi set initial soft-block state after rfkill register, and doesn't allow set_block until rfkill initial finished. Reference: bko#31002 https://bugzilla.kernel.org/show_bug.cgi?id=31002 Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Dmitry Torokhov Cc: Corentin Chary Cc: Oldřich Jedlička Cc: Johannes Berg Signed-off-by: Chun-Yi Lee Signed-off-by: Matthew Garrett Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/acer-wmi.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 38b34a73866a8..fa54ba70657ae 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -222,6 +222,7 @@ struct acer_debug { static struct rfkill *wireless_rfkill; static struct rfkill *bluetooth_rfkill; static struct rfkill *threeg_rfkill; +static bool rfkill_inited; /* Each low-level interface must define at least some of the following */ struct wmi_interface { @@ -1161,9 +1162,13 @@ static int acer_rfkill_set(void *data, bool blocked) { acpi_status status; u32 cap = (unsigned long)data; - status = set_u32(!blocked, cap); - if (ACPI_FAILURE(status)) - return -ENODEV; + + if (rfkill_inited) { + status = set_u32(!blocked, cap); + if (ACPI_FAILURE(status)) + return -ENODEV; + } + return 0; } @@ -1187,14 +1192,16 @@ static struct rfkill *acer_rfkill_register(struct device *dev, return ERR_PTR(-ENOMEM); status = get_device_status(&state, cap); - if (ACPI_SUCCESS(status)) - rfkill_init_sw_state(rfkill_dev, !state); err = rfkill_register(rfkill_dev); if (err) { rfkill_destroy(rfkill_dev); return ERR_PTR(err); } + + if (ACPI_SUCCESS(status)) + rfkill_set_sw_state(rfkill_dev, !state); + return rfkill_dev; } @@ -1229,6 +1236,8 @@ static int acer_rfkill_init(struct device *dev) } } + rfkill_inited = true; + schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); return 0; From 8f114f1414335ed352e54d0343909d7b69a81432 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 11 Apr 2011 16:01:32 -0700 Subject: [PATCH 1154/2556] Revert "x86: Cleanup highmap after brk is concluded" This reverts upstream commit e5f15b45ddf3afa2bbbb10c7ea34fb32b6de0a0e It caused problems in the stable tree and should not have been there. Cc: Yinghai Lu Cc: Stefano Stabellini Cc: H. Peter Anvin Cc: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/head64.c | 3 +++ arch/x86/kernel/setup.c | 5 ----- arch/x86/mm/init.c | 19 +++++++++++++++++++ arch/x86/mm/init_64.c | 11 +++++------ 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index 5655c2272adb8..2d2673c28aff2 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c @@ -77,6 +77,9 @@ void __init x86_64_start_kernel(char * real_mode_data) /* Make NULL pointers segfault */ zap_identity_mappings(); + /* Cleanup the over mapped high alias */ + cleanup_highmap(); + max_pfn_mapped = KERNEL_IMAGE_SIZE >> PAGE_SHIFT; for (i = 0; i < NUM_EXCEPTION_VECTORS; i++) { diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index e543fe9311e4a..d3cfe26c0252a 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -297,9 +297,6 @@ static void __init init_gbpages(void) static inline void init_gbpages(void) { } -static void __init cleanup_highmap(void) -{ -} #endif static void __init reserve_brk(void) @@ -925,8 +922,6 @@ void __init setup_arch(char **cmdline_p) */ reserve_brk(); - cleanup_highmap(); - memblock.current_limit = get_max_mapped(); memblock_x86_fill(); diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index f13ff3a226736..947f42abe820e 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -279,6 +279,25 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, load_cr3(swapper_pg_dir); #endif +#ifdef CONFIG_X86_64 + if (!after_bootmem && !start) { + pud_t *pud; + pmd_t *pmd; + + mmu_cr4_features = read_cr4(); + + /* + * _brk_end cannot change anymore, but it and _end may be + * located on different 2M pages. cleanup_highmap(), however, + * can only consider _end when it runs, so destroy any + * mappings beyond _brk_end here. + */ + pud = pud_offset(pgd_offset_k(_brk_end), _brk_end); + pmd = pmd_offset(pud, _brk_end - 1); + while (++pmd <= pmd_offset(pud, (unsigned long)_end - 1)) + pmd_clear(pmd); + } +#endif __flush_tlb_all(); if (!after_bootmem && e820_table_end > e820_table_start) diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 68f9921ae9c16..c14a5422e1522 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -51,7 +51,6 @@ #include #include #include -#include static int __init parse_direct_gbpages_off(char *arg) { @@ -294,18 +293,18 @@ void __init init_extra_mapping_uc(unsigned long phys, unsigned long size) * to the compile time generated pmds. This results in invalid pmds up * to the point where we hit the physaddr 0 mapping. * - * We limit the mappings to the region from _text to _brk_end. _brk_end - * is rounded up to the 2MB boundary. This catches the invalid pmds as + * We limit the mappings to the region from _text to _end. _end is + * rounded up to the 2MB boundary. This catches the invalid pmds as * well, as they are located before _text: */ void __init cleanup_highmap(void) { unsigned long vaddr = __START_KERNEL_map; - unsigned long vaddr_end = __START_KERNEL_map + (max_pfn_mapped << PAGE_SHIFT); - unsigned long end = roundup((unsigned long)_brk_end, PMD_SIZE) - 1; + unsigned long end = roundup((unsigned long)_end, PMD_SIZE) - 1; pmd_t *pmd = level2_kernel_pgt; + pmd_t *last_pmd = pmd + PTRS_PER_PMD; - for (; vaddr + PMD_SIZE - 1 < vaddr_end; pmd++, vaddr += PMD_SIZE) { + for (; pmd < last_pmd; pmd++, vaddr += PMD_SIZE) { if (pmd_none(*pmd)) continue; if (vaddr < (unsigned long) _text || vaddr > end) From 2b086ef28939e3cd943664fc84dc54d01d261c2c Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Tue, 22 Mar 2011 23:01:26 +0000 Subject: [PATCH 1155/2556] Squashfs: Use vmalloc rather than kmalloc for zlib workspace commit 117a91e0f25fd7698e20ac3dfa62086be3dc82a3 upstream. Bugzilla bug 31422 reports occasional "page allocation failure. order:4" at Squashfs mount time. Fix this by making zlib workspace allocation use vmalloc rather than kmalloc. Reported-by: Mehmet Giritli Signed-off-by: Phillip Lougher Signed-off-by: Greg Kroah-Hartman --- fs/squashfs/zlib_wrapper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c index 4661ae2b1cec8..04ae9a5b70a66 100644 --- a/fs/squashfs/zlib_wrapper.c +++ b/fs/squashfs/zlib_wrapper.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "squashfs_fs.h" #include "squashfs_fs_sb.h" @@ -37,8 +38,7 @@ static void *zlib_init(struct squashfs_sb_info *dummy) z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL); if (stream == NULL) goto failed; - stream->workspace = kmalloc(zlib_inflate_workspacesize(), - GFP_KERNEL); + stream->workspace = vmalloc(zlib_inflate_workspacesize()); if (stream->workspace == NULL) goto failed; @@ -56,7 +56,7 @@ static void zlib_free(void *strm) z_stream *stream = strm; if (stream) - kfree(stream->workspace); + vfree(stream->workspace); kfree(stream); } From 3c215f5c980a983e5674cf0fd55a4e5f561333ba Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Tue, 15 Mar 2011 22:09:55 +0000 Subject: [PATCH 1156/2556] Squashfs: handle corruption of directory structure commit 44cff8a9ee8a974f9e931df910688e7fc1f0b0f9 upstream. Handle the rare case where a directory metadata block is uncompressed and corrupted, leading to a kernel oops in directory scanning (memcpy). Normally corruption is detected at the decompression stage and dealt with then, however, this will not happen if: - metadata isn't compressed (users can optionally request no metadata compression), or - the compressed metadata block was larger than the original, in which case the uncompressed version was used, or - the data was corrupt after decompression This patch fixes this by adding some sanity checks against known maximum values. Signed-off-by: Phillip Lougher Signed-off-by: Greg Kroah-Hartman --- fs/squashfs/dir.c | 9 +++++++++ fs/squashfs/namei.c | 12 ++++++++++++ 2 files changed, 21 insertions(+) diff --git a/fs/squashfs/dir.c b/fs/squashfs/dir.c index 0dc340aa2be97..3f79cd1d0c197 100644 --- a/fs/squashfs/dir.c +++ b/fs/squashfs/dir.c @@ -172,6 +172,11 @@ static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir) length += sizeof(dirh); dir_count = le32_to_cpu(dirh.count) + 1; + + /* dir_count should never be larger than 256 */ + if (dir_count > 256) + goto failed_read; + while (dir_count--) { /* * Read directory entry. @@ -183,6 +188,10 @@ static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir) size = le16_to_cpu(dire->size) + 1; + /* size should never be larger than SQUASHFS_NAME_LEN */ + if (size > SQUASHFS_NAME_LEN) + goto failed_read; + err = squashfs_read_metadata(inode->i_sb, dire->name, &block, &offset, size); if (err < 0) diff --git a/fs/squashfs/namei.c b/fs/squashfs/namei.c index 7a9464d08cf63..5d922a6701ab7 100644 --- a/fs/squashfs/namei.c +++ b/fs/squashfs/namei.c @@ -176,6 +176,11 @@ static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry, length += sizeof(dirh); dir_count = le32_to_cpu(dirh.count) + 1; + + /* dir_count should never be larger than 256 */ + if (dir_count > 256) + goto data_error; + while (dir_count--) { /* * Read directory entry. @@ -187,6 +192,10 @@ static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry, size = le16_to_cpu(dire->size) + 1; + /* size should never be larger than SQUASHFS_NAME_LEN */ + if (size > SQUASHFS_NAME_LEN) + goto data_error; + err = squashfs_read_metadata(dir->i_sb, dire->name, &block, &offset, size); if (err < 0) @@ -228,6 +237,9 @@ static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry, d_add(dentry, inode); return ERR_PTR(0); +data_error: + err = -EIO; + read_failure: ERROR("Unable to read directory block [%llx:%x]\n", squashfs_i(dir)->start + msblk->directory_table, From fe48c5e4b5b2cc1f95d3750a54c458944cc34523 Mon Sep 17 00:00:00 2001 From: "Philip A. Prindeville" Date: Wed, 30 Mar 2011 12:59:26 +0000 Subject: [PATCH 1157/2556] atm/solos-pci: Don't include frame pseudo-header on transmit hex-dump commit 18b429e74eeafe42e947b1b0f9a760c7153a0b5c upstream. Omit pkt_hdr preamble when dumping transmitted packet as hex-dump; we can pull this up because the frame has already been sent, and dumping it is the last thing we do with it before freeing it. Also include the size, vpi, and vci in the debug as is done on receive. Use "port" consistently instead of "device" intermittently. Signed-off-by: Philip Prindeville Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/atm/solos-pci.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index 25ef1a4556e62..de738e65c047f 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -697,7 +697,7 @@ void solos_bh(unsigned long card_arg) size); } if (atmdebug) { - dev_info(&card->dev->dev, "Received: device %d\n", port); + dev_info(&card->dev->dev, "Received: port %d\n", port); dev_info(&card->dev->dev, "size: %d VPI: %d VCI: %d\n", size, le16_to_cpu(header->vpi), le16_to_cpu(header->vci)); @@ -1018,8 +1018,15 @@ static uint32_t fpga_tx(struct solos_card *card) /* Clean up and free oldskb now it's gone */ if (atmdebug) { + struct pkt_hdr *header = (void *)oldskb->data; + int size = le16_to_cpu(header->size); + + skb_pull(oldskb, sizeof(*header)); dev_info(&card->dev->dev, "Transmitted: port %d\n", port); + dev_info(&card->dev->dev, "size: %d VPI: %d VCI: %d\n", + size, le16_to_cpu(header->vpi), + le16_to_cpu(header->vci)); print_buffer(oldskb); } From a691a7c96629740253fc52c30460607b98e05328 Mon Sep 17 00:00:00 2001 From: "Philip A. Prindeville" Date: Wed, 30 Mar 2011 13:17:04 +0000 Subject: [PATCH 1158/2556] atm/solos-pci: Don't flap VCs when carrier state changes commit c031235b395433350f25943b7580a5e343c7b7b2 upstream. Don't flap VCs when carrier state changes; higher-level protocols can detect loss of connectivity and act accordingly. This is more consistent with how other network interfaces work. We no longer use release_vccs() so we can delete it. release_vccs() was duplicated from net/atm/common.c; make the corresponding function exported, since other code duplicates it and could leverage it if it were public. Signed-off-by: Philip A. Prindeville Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/atm/solos-pci.c | 26 +------------------------- include/linux/atmdev.h | 1 + net/atm/common.c | 1 + 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index de738e65c047f..b836e11a8a34e 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -165,7 +165,6 @@ static uint32_t fpga_tx(struct solos_card *); static irqreturn_t solos_irq(int irq, void *dev_id); static struct atm_vcc* find_vcc(struct atm_dev *dev, short vpi, int vci); static int list_vccs(int vci); -static void release_vccs(struct atm_dev *dev); static int atm_init(struct solos_card *, struct device *); static void atm_remove(struct solos_card *); static int send_command(struct solos_card *card, int dev, const char *buf, size_t size); @@ -384,7 +383,6 @@ static int process_status(struct solos_card *card, int port, struct sk_buff *skb /* Anything but 'Showtime' is down */ if (strcmp(state_str, "Showtime")) { atm_dev_signal_change(card->atmdev[port], ATM_PHY_SIG_LOST); - release_vccs(card->atmdev[port]); dev_info(&card->dev->dev, "Port %d: %s\n", port, state_str); return 0; } @@ -830,28 +828,6 @@ static int list_vccs(int vci) return num_found; } -static void release_vccs(struct atm_dev *dev) -{ - int i; - - write_lock_irq(&vcc_sklist_lock); - for (i = 0; i < VCC_HTABLE_SIZE; i++) { - struct hlist_head *head = &vcc_hash[i]; - struct hlist_node *node, *tmp; - struct sock *s; - struct atm_vcc *vcc; - - sk_for_each_safe(s, node, tmp, head) { - vcc = atm_sk(s); - if (vcc->dev == dev) { - vcc_release_async(vcc, -EPIPE); - sk_del_node_init(s); - } - } - } - write_unlock_irq(&vcc_sklist_lock); -} - static int popen(struct atm_vcc *vcc) { @@ -1269,7 +1245,7 @@ static int atm_init(struct solos_card *card, struct device *parent) card->atmdev[i]->ci_range.vci_bits = 16; card->atmdev[i]->dev_data = card; card->atmdev[i]->phy_data = (void *)(unsigned long)i; - atm_dev_signal_change(card->atmdev[i], ATM_PHY_SIG_UNKNOWN); + atm_dev_signal_change(card->atmdev[i], ATM_PHY_SIG_FOUND); skb = alloc_skb(sizeof(*header), GFP_ATOMIC); if (!skb) { diff --git a/include/linux/atmdev.h b/include/linux/atmdev.h index 475f8c42c0e92..381f4cec82602 100644 --- a/include/linux/atmdev.h +++ b/include/linux/atmdev.h @@ -443,6 +443,7 @@ void atm_dev_signal_change(struct atm_dev *dev, char signal); void vcc_insert_socket(struct sock *sk); +void atm_dev_release_vccs(struct atm_dev *dev); /* * This is approximately the algorithm used by alloc_skb. diff --git a/net/atm/common.c b/net/atm/common.c index 1b9c52a02cd31..22b963d06a10b 100644 --- a/net/atm/common.c +++ b/net/atm/common.c @@ -252,6 +252,7 @@ void atm_dev_release_vccs(struct atm_dev *dev) } write_unlock_irq(&vcc_sklist_lock); } +EXPORT_SYMBOL(atm_dev_release_vccs); static int adjust_tp(struct atm_trafprm *tp, unsigned char aal) { From 53224a1f02f493bb1a37bad94b596cc4314777e6 Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Mon, 4 Apr 2011 16:00:49 -0400 Subject: [PATCH 1159/2556] ext4: fix a double free in ext4_register_li_request commit 46e4690bbd9a4f8d9e7c4f34e34b48f703ad47e0 upstream. In ext4_register_li_request, we malloc a ext4_li_request and inserts it into ext4_li_info->li_request_list. In case of any error later, we free it in the end. But if we have some error in ext4_run_lazyinit_thread, the whole li_request_list will be dropped and freed in it. So we will double free this ext4_li_request. This patch just sets elr to NULL after it is inserted to the list so that the latter kfree won't double free it. Signed-off-by: Tao Ma Reviewed-by: Lukas Czerner Signed-off-by: "Theodore Ts'o" Signed-off-by: Greg Kroah-Hartman --- fs/ext4/super.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 4381efee3dbc1..243deb021e7b7 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2978,6 +2978,12 @@ static int ext4_register_li_request(struct super_block *sb, mutex_unlock(&ext4_li_info->li_list_mtx); sbi->s_li_request = elr; + /* + * set elr to NULL here since it has been inserted to + * the request_list and the removal and free of it is + * handled by ext4_clear_request_list from now on. + */ + elr = NULL; if (!(ext4_li_info->li_state & EXT4_LAZYINIT_RUNNING)) { ret = ext4_run_lazyinit_thread(); From 6fc45e2d18e042ac3d7677fcec64e2f0308f8b5f Mon Sep 17 00:00:00 2001 From: Yongqiang Yang Date: Mon, 4 Apr 2011 15:40:24 -0400 Subject: [PATCH 1160/2556] ext4: fix credits computing for indirect mapped files commit 5b41395fcc0265fc9f193aef9df39ce49d64677c upstream. When writing a contiguous set of blocks, two indirect blocks could be needed depending on how the blocks are aligned, so we need to increase the number of credits needed by one. [ Also fixed a another bug which could further underestimate the number of journal credits needed by 1; the code was using integer division instead of DIV_ROUND_UP() -- tytso] Signed-off-by: Yongqiang Yang Signed-off-by: "Theodore Ts'o" Signed-off-by: Greg Kroah-Hartman --- fs/ext4/inode.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 9f7f9e49914fa..fee51dbf74dcd 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5460,13 +5460,12 @@ static int ext4_indirect_trans_blocks(struct inode *inode, int nrblocks, /* if nrblocks are contiguous */ if (chunk) { /* - * With N contiguous data blocks, it need at most - * N/EXT4_ADDR_PER_BLOCK(inode->i_sb) indirect blocks - * 2 dindirect blocks - * 1 tindirect block + * With N contiguous data blocks, we need at most + * N/EXT4_ADDR_PER_BLOCK(inode->i_sb) + 1 indirect blocks, + * 2 dindirect blocks, and 1 tindirect block */ - indirects = nrblocks / EXT4_ADDR_PER_BLOCK(inode->i_sb); - return indirects + 3; + return DIV_ROUND_UP(nrblocks, + EXT4_ADDR_PER_BLOCK(inode->i_sb)) + 4; } /* * if nrblocks are not contiguous, worse case, each block touch From 39932bd2baf36a8521b89795ac21efbec7e0d976 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 24 Mar 2011 22:51:14 -0400 Subject: [PATCH 1161/2556] nfsd: fix auth_domain reference leak on nlm operations commit 954032d2527f2fce7355ba70709b5e143d6b686f upstream. This was noticed by users who performed more than 2^32 lock operations and hence made this counter overflow (eventually leading to use-after-free's). Setting rq_client to NULL here means that it won't later get auth_domain_put() when it should be. Appears to have been introduced in 2.5.42 by "[PATCH] kNFSd: Move auth domain lookup into svcauth" which moved most of the rq_client handling to common svcauth code, but left behind this one line. Cc: Neil Brown Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/lockd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c index 0c6d816701374..7c831a2731fa1 100644 --- a/fs/nfsd/lockd.c +++ b/fs/nfsd/lockd.c @@ -38,7 +38,6 @@ nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp) exp_readlock(); nfserr = nfsd_open(rqstp, &fh, S_IFREG, NFSD_MAY_LOCK, filp); fh_put(&fh); - rqstp->rq_client = NULL; exp_readunlock(); /* We return nlm error codes as nlm doesn't know * about nfsd, but nfsd does know about nlm.. From 212795f2be795d6538f225d157b33d58fb64d067 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 28 Mar 2011 15:15:09 +0800 Subject: [PATCH 1162/2556] nfsd4: fix oops on lock failure commit 23fcf2ec93fb8573a653408316af599939ff9a8e upstream. Lock stateid's can have access_bmap 0 if they were only partially initialized (due to a failed lock request); handle that case in free_generic_stateid. ------------[ cut here ]------------ kernel BUG at fs/nfsd/nfs4state.c:380! invalid opcode: 0000 [#1] SMP last sysfs file: /sys/kernel/mm/ksm/run Modules linked in: nfs fscache md4 nls_utf8 cifs ip6table_filter ip6_tables ebtable_nat ebtables ipt_MASQUERADE iptable_nat nf_nat bridge stp llc nfsd lockd nfs_acl auth_rpcgss sunrpc ipv6 ppdev parport_pc parport pcnet32 mii pcspkr microcode i2c_piix4 BusLogic floppy [last unloaded: mperf] Pid: 1468, comm: nfsd Not tainted 2.6.38+ #120 VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform EIP: 0060:[] EFLAGS: 00010297 CPU: 0 EIP is at nfs4_access_to_omode+0x1c/0x29 [nfsd] EAX: ffffffff EBX: dd758120 ECX: 00000000 EDX: 00000004 ESI: dd758120 EDI: ddfe657c EBP: dd54dde0 ESP: dd54dde0 DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 Process nfsd (pid: 1468, ti=dd54c000 task=ddc92580 task.ti=dd54c000) Stack: dd54ddf0 e24f19ca 00000000 ddfe6560 dd54de08 e24f1a5d dd758130 deee3a20 ddfe6560 31270000 dd54df1c e24f52fd 0000000f dd758090 e2505dd0 0be304cf dbb51d68 0000000e ddfe657c ddcd8020 dd758130 dd758128 dd7580d8 dd54de68 Call Trace: [] free_generic_stateid+0x1c/0x3e [nfsd] [] release_lockowner+0x71/0x8a [nfsd] [] nfsd4_lock+0x617/0x66c [nfsd] [] ? nfsd_setuser+0x199/0x1bb [nfsd] [] ? nfsd_setuser_and_check_port+0x65/0x81 [nfsd] [] ? _cond_resched+0x8/0x1c [] ? slab_pre_alloc_hook.clone.33+0x23/0x27 [] ? kmem_cache_alloc+0x1a/0xd2 [] ? __call_rcu+0xd7/0xdd [] ? fh_verify+0x401/0x452 [nfsd] [] ? nfsd4_encode_operation+0x52/0x117 [nfsd] [] ? nfsd4_putfh+0x33/0x3b [nfsd] [] ? nfsd4_delegreturn+0xd4/0xd4 [nfsd] [] nfsd4_proc_compound+0x1ea/0x33e [nfsd] [] nfsd_dispatch+0xd1/0x1a5 [nfsd] [] svc_process_common+0x282/0x46f [sunrpc] [] svc_process+0xdc/0xfa [sunrpc] [] nfsd+0xd6/0x115 [nfsd] [] ? nfsd_shutdown+0x24/0x24 [nfsd] [] kthread+0x62/0x67 [] ? kthread_worker_fn+0x114/0x114 [] kernel_thread_helper+0x6/0x10 Code: eb 05 b8 00 00 27 4f 8d 65 f4 5b 5e 5f 5d c3 83 e0 03 55 83 f8 02 89 e5 74 17 83 f8 03 74 05 48 75 09 eb 09 b8 02 00 00 00 eb 0b <0f> 0b 31 c0 eb 05 b8 01 00 00 00 5d c3 55 89 e5 57 56 89 d6 8d EIP: [] nfs4_access_to_omode+0x1c/0x29 [nfsd] SS:ESP 0068:dd54dde0 ---[ end trace 2b0bf6c6557cb284 ]--- The trace route is: -> nfsd4_lock() -> if (lock->lk_is_new) { -> alloc_init_lock_stateid() 3739: stp->st_access_bmap = 0; ->if (status && lock->lk_is_new && lock_sop) -> release_lockowner() -> free_generic_stateid() -> nfs4_access_bmap_to_omode() -> nfs4_access_to_omode() 380: BUG(); ***** This problem was introduced by 0997b173609b9229ece28941c118a2a9b278796e. Reported-by: Mi Jinlong Tested-by: Mi Jinlong Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4state.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index f0e448a512c63..96aaaa47fd0a3 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -397,10 +397,13 @@ static void unhash_generic_stateid(struct nfs4_stateid *stp) static void free_generic_stateid(struct nfs4_stateid *stp) { - int oflag = nfs4_access_bmap_to_omode(stp); + int oflag; - nfs4_file_put_access(stp->st_file, oflag); - put_nfs4_file(stp->st_file); + if (stp->st_access_bmap) { + oflag = nfs4_access_bmap_to_omode(stp); + nfs4_file_put_access(stp->st_file, oflag); + put_nfs4_file(stp->st_file); + } kmem_cache_free(stateid_slab, stp); } From 1be99f6c95e6c887756f789a60d15771235acd0c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 14 Apr 2011 13:03:56 -0700 Subject: [PATCH 1163/2556] Linux 2.6.38.3 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6c155251f7228..e47e39e2bc2c1 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 38 -EXTRAVERSION = .2 +EXTRAVERSION = .3 NAME = Flesh-Eating Bats with Fangs # *DOCUMENTATION* From 0ae9ada802a9ddbda230b1befeaea3d870748c2d Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Mon, 7 Feb 2011 15:19:34 +0100 Subject: [PATCH 1164/2556] ALSA: HDA: New AD1984A model for Dell Precision R5500 commit 677cd904aba939bc4cfdc3c1eada8ec46582127e upstream. For codec AD1984A, add a new model to support Dell Precision R5500 or the microphone jack won't work correctly. BugLink: http://bugs.launchpad.net/bugs/741516 Tested-by: Kent Baxley Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_analog.c | 89 ++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 8dabab798689b..7aee90044c685 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -4352,6 +4352,84 @@ static int ad1984a_thinkpad_init(struct hda_codec *codec) return 0; } +/* + * Precision R5500 + * 0x12 - HP/line-out + * 0x13 - speaker (mono) + * 0x15 - mic-in + */ + +static struct hda_verb ad1984a_precision_verbs[] = { + /* Unmute main output path */ + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x1f}, /* 0dB */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) + 0x17}, /* 0dB */ + /* Analog mixer; mute as default */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Select mic as input */ + {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x27}, /* 0dB */ + /* Configure as mic */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ + /* HP unmute */ + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* turn on EAPD */ + {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, + /* unsolicited event for pin-sense */ + {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, + { } /* end */ +}; + +static struct snd_kcontrol_new ad1984a_precision_mixers[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x13, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), + { } /* end */ +}; + + +/* mute internal speaker if HP is plugged */ +static void ad1984a_precision_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_jack_detect(codec, 0x12); + snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + + +/* unsolicited event for HP jack sensing */ +static void ad1984a_precision_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != AD1884A_HP_EVENT) + return; + ad1984a_precision_automute(codec); +} + +/* initialize jack-sensing, too */ +static int ad1984a_precision_init(struct hda_codec *codec) +{ + ad198x_init(codec); + ad1984a_precision_automute(codec); + return 0; +} + + /* * HP Touchsmart * port-A (0x11) - front hp-out @@ -4481,6 +4559,7 @@ enum { AD1884A_MOBILE, AD1884A_THINKPAD, AD1984A_TOUCHSMART, + AD1984A_PRECISION, AD1884A_MODELS }; @@ -4490,9 +4569,11 @@ static const char * const ad1884a_models[AD1884A_MODELS] = { [AD1884A_MOBILE] = "mobile", [AD1884A_THINKPAD] = "thinkpad", [AD1984A_TOUCHSMART] = "touchsmart", + [AD1984A_PRECISION] = "precision", }; static struct snd_pci_quirk ad1884a_cfg_tbl[] = { + SND_PCI_QUIRK(0x1028, 0x04ac, "Precision R5500", AD1984A_PRECISION), SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE), SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP), SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE), @@ -4586,6 +4667,14 @@ static int patch_ad1884a(struct hda_codec *codec) codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event; codec->patch_ops.init = ad1984a_thinkpad_init; break; + case AD1984A_PRECISION: + spec->mixers[0] = ad1984a_precision_mixers; + spec->init_verbs[spec->num_init_verbs++] = + ad1984a_precision_verbs; + spec->multiout.dig_out_nid = 0; + codec->patch_ops.unsol_event = ad1984a_precision_unsol_event; + codec->patch_ops.init = ad1984a_precision_init; + break; case AD1984A_TOUCHSMART: spec->mixers[0] = ad1984a_touchsmart_mixers; spec->init_verbs[0] = ad1984a_touchsmart_verbs; From de0eb2c5549bef5b786074be8ba0e952c29e10d2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 23 Mar 2011 22:54:32 +0100 Subject: [PATCH 1165/2556] ALSA: hda - Fix SPDIF out regression on ALC889 commit 20b67dddcc5f29d3d0c900225d85e0ac655bc69d upstream. The commit 5a8cfb4e8ae317d283f84122ed20faa069c5e0c4 ALSA: hda - Use ALC_INIT_DEFAULT for really default initialization changed to use the default initialization method for ALC889, but this caused a regression on SPDIF output on some machines. This seems due to the COEF setup included in the default init procedure. For making SPDIF working again, the COEF-setup has to be avoided for the id 0889. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=24342 Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c2eb6a7c2b3c8..d21ce8bc919bc 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1360,7 +1360,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) case 0x10ec0883: case 0x10ec0885: case 0x10ec0887: - case 0x10ec0889: + /*case 0x10ec0889:*/ /* this causes an SPDIF problem */ alc889_coef_init(codec); break; case 0x10ec0888: From 6a99ee31581998c7a4445283f02c50e342776433 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 24 Mar 2011 09:50:15 +0100 Subject: [PATCH 1166/2556] ALSA: Fix yet another race in disconnection commit a45e3d6b13e97506b616980c0f122c3389bcefa4 upstream. This patch fixes a race between snd_card_file_remove() and snd_card_disconnect(). When the card is added to shutdown_files list in snd_card_disconnect(), but it's freed in snd_card_file_remove() at the same time, the shutdown_files list gets corrupted. The list member must be freed in snd_card_file_remove() as well. Reported-and-tested-by: Russ Dill Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/init.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/core/init.c b/sound/core/init.c index 3e65da21a08c0..a0080aa45ae96 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -848,6 +848,7 @@ int snd_card_file_add(struct snd_card *card, struct file *file) return -ENOMEM; mfile->file = file; mfile->disconnected_f_op = NULL; + INIT_LIST_HEAD(&mfile->shutdown_list); spin_lock(&card->files_lock); if (card->shutdown) { spin_unlock(&card->files_lock); @@ -883,6 +884,9 @@ int snd_card_file_remove(struct snd_card *card, struct file *file) list_for_each_entry(mfile, &card->files_list, list) { if (mfile->file == file) { list_del(&mfile->list); + spin_lock(&shutdown_lock); + list_del(&mfile->shutdown_list); + spin_unlock(&shutdown_lock); if (mfile->disconnected_f_op) fops_put(mfile->disconnected_f_op); found = mfile; From 882fd8577c3b43a9e5325c644b9c3cf7bcbaf573 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Fri, 25 Mar 2011 17:51:54 +1100 Subject: [PATCH 1167/2556] ALSA: vmalloc buffers should use normal mmap commit 3674f19dabd15f9541079a588149a370d888f4e6 upstream. It's a big no-no to use pgprot_noncached() when mmap'ing such buffers into userspace since they are mapped cachable in kernel space. This can cause all sort of interesting things ranging from to garbled sound to lockups on various architectures. I've observed that usb-audio is broken on powerpc 4xx for example because of that. Also remove the now unused snd_pcm_lib_mmap_noncached(). It's an arch business to know when to use uncached mappings, there's already hacks for MIPS inside snd_pcm_default_mmap() and other archs are supposed to use dma_mmap_coherent(). (See my separate patch that adds dma_mmap_coherent() to powerpc) Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- include/sound/pcm.h | 4 +--- sound/core/pcm_native.c | 9 --------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index e731f8d719347..ec2678131e112 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1030,9 +1030,7 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, struct vm_area_s #define snd_pcm_lib_mmap_iomem NULL #endif -int snd_pcm_lib_mmap_noncached(struct snd_pcm_substream *substream, - struct vm_area_struct *area); -#define snd_pcm_lib_mmap_vmalloc snd_pcm_lib_mmap_noncached +#define snd_pcm_lib_mmap_vmalloc NULL static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) { diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 4be45e7be8ad0..6848dd9c70a91 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3201,15 +3201,6 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); #endif /* SNDRV_PCM_INFO_MMAP */ -/* mmap callback with pgprot_noncached */ -int snd_pcm_lib_mmap_noncached(struct snd_pcm_substream *substream, - struct vm_area_struct *area) -{ - area->vm_page_prot = pgprot_noncached(area->vm_page_prot); - return snd_pcm_default_mmap(substream, area); -} -EXPORT_SYMBOL(snd_pcm_lib_mmap_noncached); - /* * mmap DMA buffer */ From 7a7e0b344ca1f16868c586d0e26c35853989bf58 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 23 Mar 2011 19:29:39 +0100 Subject: [PATCH 1168/2556] perf: Better fit max unprivileged mlock pages for tools needs commit 880f57318450dbead6a03f9e31a1468924d6dd88 upstream. The maximum kilobytes of locked memory that an unprivileged user can reserve is of 512 kB = 128 pages by default, scaled to the number of onlined CPUs, which fits well with the tools that use 128 data pages by default. However tools actually use 129 pages, because they need one more for the user control page. Thus the default mlock threshold is not sufficient for the default tools needs and we always end up to evaluate the constant mlock rlimit policy, which doesn't have this scaling with the number of online CPUs. Hence, on systems that have more than 16 CPUs, we overlap the rlimit threshold and fail to mmap: $ perf record ls Error: failed to mmap with 1 (Operation not permitted) Just increase the max unprivileged mlock threshold by one page so that it supports well perf tools even after 16 CPUs. Reported-by: Han Pingtian Reported-by: Peter Zijlstra Reported-by: Arnaldo Carvalho de Melo Signed-off-by: Frederic Weisbecker Acked-by: Arnaldo Carvalho de Melo Cc: Stephane Eranian LKML-Reference: <1300904979-5508-1-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/perf_event.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/perf_event.c b/kernel/perf_event.c index ad02feadb6b02..f81f9d6395ad0 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -62,7 +62,8 @@ static struct srcu_struct pmus_srcu; */ int sysctl_perf_event_paranoid __read_mostly = 1; -int sysctl_perf_event_mlock __read_mostly = 512; /* 'free' kb per user */ +/* Minimum for 128 pages + 1 for the user control page */ +int sysctl_perf_event_mlock __read_mostly = 516; /* 'free' kb per user */ /* * max perf event sample rate From 31de8583825fffeb27ada5d5f6eabf9aecb06efa Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 23 Mar 2011 02:44:30 +0000 Subject: [PATCH 1169/2556] myri10ge: fix rmmod crash commit cda6587c21a887254c8ed4b58da8fcc4040ab557 upstream. Rmmod myri10ge crash at free_netdev() -> netif_napi_del(), because napi structures are already deallocated. To fix call netif_napi_del() before kfree() at myri10ge_free_slices(). Signed-off-by: Stanislaw Gruszka Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/myri10ge/myri10ge.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index ea5cfe2c3a040..24386a804ea40 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -3645,6 +3645,7 @@ static void myri10ge_free_slices(struct myri10ge_priv *mgp) dma_free_coherent(&pdev->dev, bytes, ss->fw_stats, ss->fw_stats_bus); ss->fw_stats = NULL; + netif_napi_del(&ss->napi); } } kfree(mgp->ss); From 8675167979456c2c5c99c5068ebb836902936f4b Mon Sep 17 00:00:00 2001 From: Bud Brown Date: Wed, 23 Mar 2011 20:47:11 +0100 Subject: [PATCH 1170/2556] cciss: fix lost command issue commit 1ddd5049545e0aa1a0ed19bca4d9c9c3ce1ac8a2 upstream. Under certain workloads a command may seem to get lost. IOW, the Smart Array thinks all commands have been completed but we still have commands in our completion queue. This may lead to system instability, filesystems going read-only, or even panics depending on the affected filesystem. We add an extra read to force the write to complete. Testing shows this extra read avoids the problem. Signed-off-by: Mike Miller Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/block/cciss.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h index 579f749184930..554bbd907d144 100644 --- a/drivers/block/cciss.h +++ b/drivers/block/cciss.h @@ -222,6 +222,7 @@ static void SA5_submit_command( ctlr_info_t *h, CommandList_struct *c) h->ctlr, c->busaddr); #endif /* CCISS_DEBUG */ writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET); + readl(h->vaddr + SA5_REQUEST_PORT_OFFSET); h->commands_outstanding++; if ( h->commands_outstanding > h->max_outstanding) h->max_outstanding = h->commands_outstanding; From feaaca984aeb38bb7c0cfae9646a7e7304ab423d Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Mon, 21 Mar 2011 18:27:21 +0530 Subject: [PATCH 1171/2556] ath9k: Fix kernel panic in AR2427 commit 61e1b0b00c793ad5a32fe2181c9f77115fed5dc4 upstream. Kernel panic occurs just after AR2427 establishes connection with AP. Unless aggregation is enabled we don't initialize the TID structure. Thus accesing the elements of the TID structure when aggregation is disabled, leads to NULL pointer dereferencing. [ 191.320358] Call Trace: [ 191.320364] [] ? ath9k_tx+0xa7/0x200 [ath9k] [ 191.320376] [] ? __ieee80211_tx+0x5c/0x1e0 [mac80211] [ 191.320386] [] ? ieee80211_tx+0x7b/0x90 [mac80211] [ 191.320395] [] ? ieee80211_xmit+0x9d/0x1d0 [mac80211] [ 191.320401] [] ? wake_up_state+0xf/0x20 [ 191.320405] [] ? signal_wake_up+0x28/0x40 [ 191.320410] [] ? default_spin_lock_flags+0x8/0x10 [ 191.320420] [] ? ieee80211_subif_start_xmit+0x2e8/0x7c0 [mac80211] [ 191.320425] [] ? do_page_fault+0x295/0x3a0 [ 191.320431] [] ? dev_hard_start_xmit+0x1ad/0x210 [ 191.320436] [] ? sch_direct_xmit+0x105/0x170 [ 191.320445] [] ? get_sta_flags+0x2a/0x40 [mac80211] [ 191.320449] [] ? dev_queue_xmit+0x37f/0x4b0 [ 191.320452] [] ? eth_header+0x0/0xb0 [ 191.320456] [] ? neigh_resolve_output+0xe9/0x310 [ 191.320461] [] ? ip6_output_finish+0xa5/0x110 [ 191.320464] [] ? ip6_output2+0x134/0x250 [ 191.320468] [] ? ip6_output+0x6d/0x100 [ 191.320471] [] ? mld_sendpack+0x395/0x3e0 [ 191.320475] [] ? add_grhead+0x31/0xa0 [ 191.320478] [] ? mld_send_cr+0x1bc/0x2b0 [ 191.320482] [] ? irq_exit+0x39/0x70 [ 191.320485] [] ? mld_ifc_timer_expire+0x10/0x40 [ 191.320489] [] ? run_timer_softirq+0x13e/0x2c0 [ 191.320493] [] ? common_interrupt+0x30/0x40 [ 191.320498] [] ? mld_ifc_timer_expire+0x0/0x40 [ 191.320502] [] ? __do_softirq+0x98/0x1b0 [ 191.320506] [] ? do_softirq+0x45/0x50 [ 191.320509] [] ? irq_exit+0x65/0x70 [ 191.320513] [] ? smp_apic_timer_interrupt+0x5c/0x8b [ 191.320516] [] ? apic_timer_interrupt+0x31/0x40 [ 191.320521] [] ? k_getrusage+0x12b/0x2f0 [ 191.320525] [] ? acpi_idle_enter_simple+0x117/0x148 [ 191.320529] [] ? cpuidle_idle_call+0x7a/0x100 [ 191.320532] [] ? cpu_idle+0x94/0xd0 [ 191.320536] [] ? rest_init+0x58/0x60 [ 191.320541] [] ? start_kernel+0x351/0x357 [ 191.320544] [] ? unknown_bootoption+0x0/0x19e [ 191.320548] [] ? i386_start_kernel+0xaa/0xb1 [ 191.320550] Code: 03 66 3d 00 03 0f 84 7c 02 00 00 83 c3 18 0f b6 03 8b 4d e0 89 c3 83 e3 0f 6b c3 48 89 5d d8 8d 04 06 8d 50 0c 89 55 d0 8b 40 20 <8b> 00 3b 01 0f 85 8e 02 00 00 f6 47 20 40 0f 84 29 ff ff ff 8b [ 191.320634] EIP: [] ath_tx_start+0x474/0x770 [ath9k] SS:ESP 0068:c0761a90 [ 191.320642] CR2: 0000000000000000 [ 191.320647] ---[ end trace 9296ef23b9076ece ]--- [ 191.320650] Kernel panic - not syncing: Fatal exception in interrupt Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/xmit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 07b7804aec5ba..5c9d83b103f7c 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1699,8 +1699,8 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct ath_buf *bf, u8 tidno; spin_lock_bh(&txctl->txq->axq_lock); - - if (ieee80211_is_data_qos(hdr->frame_control) && txctl->an) { + if ((sc->sc_flags & SC_OP_TXAGGR) && txctl->an && + ieee80211_is_data_qos(hdr->frame_control)) { tidno = ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK; tid = ATH_AN_2_TID(txctl->an, tidno); From cdea7b2643cb36f305a779093478b00a43b57e52 Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Wed, 23 Mar 2011 11:42:57 -0400 Subject: [PATCH 1172/2556] sound/oss/opl3: validate voice and channel indexes commit 4d00135a680727f6c3be78f8befaac009030e4df upstream. User-controllable indexes for voice and channel values may cause reading and writing beyond the bounds of their respective arrays, leading to potentially exploitable memory corruption. Validate these indexes. Signed-off-by: Dan Rosenberg Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/oss/opl3.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/sound/oss/opl3.c b/sound/oss/opl3.c index 938c48c43585e..f4ffdff9b2451 100644 --- a/sound/oss/opl3.c +++ b/sound/oss/opl3.c @@ -849,6 +849,10 @@ static int opl3_load_patch(int dev, int format, const char __user *addr, static void opl3_panning(int dev, int voice, int value) { + + if (voice < 0 || voice >= devc->nr_voice) + return; + devc->voc[voice].panning = value; } @@ -1066,8 +1070,15 @@ static int opl3_alloc_voice(int dev, int chn, int note, struct voice_alloc_info static void opl3_setup_voice(int dev, int voice, int chn) { - struct channel_info *info = - &synth_devs[dev]->chn_info[chn]; + struct channel_info *info; + + if (voice < 0 || voice >= devc->nr_voice) + return; + + if (chn < 0 || chn > 15) + return; + + info = &synth_devs[dev]->chn_info[chn]; opl3_set_instr(dev, voice, info->pgm_num); From 37f246b0bbda91df0a393b0492e888ce013fca2f Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 21 Mar 2011 20:01:00 +0100 Subject: [PATCH 1173/2556] mac80211: initialize sta->last_rx in sta_info_alloc commit 8bc8aecdc5e26cfda12dbd6867af4aa67836da6a upstream. This field is used to determine the inactivity time. When in AP mode, hostapd uses it for kicking out inactive clients after a while. Without this patch, hostapd immediately deauthenticates a new client if it checks the inactivity time before the client sends its first data frame. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- net/mac80211/sta_info.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index c426504ed1cfe..604216e2ee3d4 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -243,6 +243,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, memcpy(sta->sta.addr, addr, ETH_ALEN); sta->local = local; sta->sdata = sdata; + sta->last_rx = jiffies; ewma_init(&sta->avg_signal, 1024, 8); From dcc051db0a7fef13e360f97407ccdc4b588e9b18 Mon Sep 17 00:00:00 2001 From: John Hughes Date: Wed, 4 Nov 2009 19:01:22 +0100 Subject: [PATCH 1174/2556] ses: show devices for enclosures with no page 7 commit 877a55979c189c590e819a61cbbe2b7947875f17 upstream. enclosure page 7 gives us the "pretty" names of the enclosure slots. Without a page 7, we can still use the enclosure code as long as we make up numeric names for the slots. Unfortunately, the current code fails to add any devices because the check for page 10 is in the wrong place if we have no page 7. Fix it so that devices show up even if the enclosure has no page 7. Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/ses.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 7f5a6a86f820f..3b00e907b9174 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -390,9 +390,9 @@ static void ses_enclosure_data_process(struct enclosure_device *edev, len = (desc_ptr[2] << 8) + desc_ptr[3]; /* skip past overall descriptor */ desc_ptr += len + 4; - if (ses_dev->page10) - addl_desc_ptr = ses_dev->page10 + 8; } + if (ses_dev->page10) + addl_desc_ptr = ses_dev->page10 + 8; type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; components = 0; for (i = 0; i < types; i++, type_ptr += 4) { From 8c371e2424018c7009a5af0f10131a8cbb38b061 Mon Sep 17 00:00:00 2001 From: "Krishnasamy, Somasundaram" Date: Mon, 28 Feb 2011 18:13:22 -0500 Subject: [PATCH 1175/2556] ses: Avoid kernel panic when lun 0 is not mapped commit d1e12de804f9d8ad114786ca7c2ce593cba79891 upstream. During device discovery, scsi mid layer sends INQUIRY command to LUN 0. If the LUN 0 is not mapped to host, it creates a temporary scsi_device with LUN id 0 and sends REPORT_LUNS command to it. After the REPORT_LUNS succeeds, it walks through the LUN table and adds each LUN found to sysfs. At the end of REPORT_LUNS lun table scan, it will delete the temporary scsi_device of LUN 0. When scsi devices are added to sysfs, it calls add_dev function of all the registered class interfaces. If ses driver has been registered, ses_intf_add() of ses module will be called. This function calls scsi_device_enclosure() to check the inquiry data for EncServ bit. Since inquiry was not allocated for temporary LUN 0 scsi_device, it will cause NULL pointer exception. To fix the problem, sdev->inquiry is checked for NULL before reading it. Signed-off-by: Somasundaram Krishnasamy Signed-off-by: Babu Moger Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- include/scsi/scsi_device.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 85867dcde3352..bfd36ff14c991 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -461,7 +461,7 @@ static inline int scsi_device_qas(struct scsi_device *sdev) } static inline int scsi_device_enclosure(struct scsi_device *sdev) { - return sdev->inquiry[6] & (1<<6); + return sdev->inquiry ? (sdev->inquiry[6] & (1<<6)) : 1; } static inline int scsi_device_protection(struct scsi_device *sdev) From c79e0210367465f37005a6e1a91c62125c470dc8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 5 Mar 2011 13:21:51 +0100 Subject: [PATCH 1176/2556] PCI/ACPI: Report ASPM support to BIOS if not disabled from command line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 8b8bae901ce23addbdcdb54fa1696fb2d049feb5 upstream. We need to distinguish the situation in which ASPM support is disabled from the command line or through .config from the situation in which it is disabled, because the hardware or BIOS can't handle it. In the former case we should not report ASPM support to the BIOS through ACPI _OSC, but in the latter case we should do that. Introduce pcie_aspm_support_enabled() that can be used by acpi_pci_root_add() to determine whether or not it should report ASPM support to the BIOS through _OSC. References: https://bugzilla.kernel.org/show_bug.cgi?id=29722 References: https://bugzilla.kernel.org/show_bug.cgi?id=20232 Reported-and-tested-by: Ortwin Glück Reviewed-by: Kenji Kaneshige Tested-by: Kenji Kaneshige Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/pci_root.c | 2 +- drivers/pci/pcie/aspm.c | 7 +++++++ include/linux/pci.h | 7 +++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 85249395623ba..c7358dd68b31c 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -564,7 +564,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) /* Indicate support for various _OSC capabilities. */ if (pci_ext_cfg_avail(root->bus->self)) flags |= OSC_EXT_PCI_CONFIG_SUPPORT; - if (pcie_aspm_enabled()) + if (pcie_aspm_support_enabled()) flags |= OSC_ACTIVE_STATE_PWR_SUPPORT | OSC_CLOCK_PWR_CAPABILITY_SUPPORT; if (pci_msi_enabled()) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 3188cd96b3386..bbdb4fd85b9cf 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -69,6 +69,7 @@ struct pcie_link_state { }; static int aspm_disabled, aspm_force, aspm_clear_state; +static bool aspm_support_enabled = true; static DEFINE_MUTEX(aspm_lock); static LIST_HEAD(link_list); @@ -896,6 +897,7 @@ static int __init pcie_aspm_disable(char *str) { if (!strcmp(str, "off")) { aspm_disabled = 1; + aspm_support_enabled = false; printk(KERN_INFO "PCIe ASPM is disabled\n"); } else if (!strcmp(str, "force")) { aspm_force = 1; @@ -930,3 +932,8 @@ int pcie_aspm_enabled(void) } EXPORT_SYMBOL(pcie_aspm_enabled); +bool pcie_aspm_support_enabled(void) +{ + return aspm_support_enabled; +} +EXPORT_SYMBOL(pcie_aspm_support_enabled); diff --git a/include/linux/pci.h b/include/linux/pci.h index 559d028970752..6002bcade0801 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1002,12 +1002,11 @@ extern bool pcie_ports_auto; #endif #ifndef CONFIG_PCIEASPM -static inline int pcie_aspm_enabled(void) -{ - return 0; -} +static inline int pcie_aspm_enabled(void) { return 0; } +static inline bool pcie_aspm_support_enabled(void) { return false; } #else extern int pcie_aspm_enabled(void); +extern bool pcie_aspm_support_enabled(void); #endif #ifdef CONFIG_PCIEAER From 4132d21dc32d6cc752c5590e83a7e675930e6752 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Wed, 9 Mar 2011 11:49:13 -0600 Subject: [PATCH 1177/2556] eCryptfs: Unlock page in write_begin error path commit 50f198ae16ac66508d4b8d5a40967a8507ad19ee upstream. Unlock the page in error path of ecryptfs_write_begin(). This may happen, for example, if decryption fails while bring the page up-to-date. Signed-off-by: Tyler Hicks Signed-off-by: Greg Kroah-Hartman --- fs/ecryptfs/mmap.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c index cc64fca89f8dc..eb9d9672ebd85 100644 --- a/fs/ecryptfs/mmap.c +++ b/fs/ecryptfs/mmap.c @@ -374,6 +374,11 @@ static int ecryptfs_write_begin(struct file *file, && (pos != 0)) zero_user(page, 0, PAGE_CACHE_SIZE); out: + if (unlikely(rc)) { + unlock_page(page); + page_cache_release(page); + *pagep = NULL; + } return rc; } From 8bdabad52d6dd4a0bc8b921e4c093b7b4a9c852a Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Thu, 17 Mar 2011 12:48:50 +0100 Subject: [PATCH 1178/2556] eCryptfs: ecryptfs_keyring_auth_tok_for_sig() bug fix commit 1821df040ac3cd6a57518739f345da6d50ea9d3f upstream. The pointer '(*auth_tok_key)' is set to NULL in case request_key() fails, in order to prevent its use by functions calling ecryptfs_keyring_auth_tok_for_sig(). Signed-off-by: Roberto Sassu Signed-off-by: Tyler Hicks Signed-off-by: Greg Kroah-Hartman --- fs/ecryptfs/keystore.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index c1436cff6f2d7..4feb78c23651a 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -1563,6 +1563,7 @@ int ecryptfs_keyring_auth_tok_for_sig(struct key **auth_tok_key, printk(KERN_ERR "Could not find key with description: [%s]\n", sig); rc = process_request_key_err(PTR_ERR(*auth_tok_key)); + (*auth_tok_key) = NULL; goto out; } (*auth_tok) = ecryptfs_get_key_payload_data(*auth_tok_key); From 8b7cc345f0e68b394e4007c22a44770abdc8fefd Mon Sep 17 00:00:00 2001 From: Tadeusz Struk Date: Sun, 13 Mar 2011 16:56:17 +0800 Subject: [PATCH 1179/2556] crypto: aesni-intel - fixed problem with packets that are not multiple of 64bytes commit 60af520cf264ea26b2af3a6871bbd71850522aea upstream. This patch fixes problem with packets that are not multiple of 64bytes. Signed-off-by: Adrian Hoban Signed-off-by: Aidan O'Mahony Signed-off-by: Gabriele Paoloni Signed-off-by: Tadeusz Struk Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- arch/x86/crypto/aesni-intel_asm.S | 5 ++++- arch/x86/crypto/aesni-intel_glue.c | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/arch/x86/crypto/aesni-intel_asm.S b/arch/x86/crypto/aesni-intel_asm.S index 8fe2a4966b7af..4292df78c9da0 100644 --- a/arch/x86/crypto/aesni-intel_asm.S +++ b/arch/x86/crypto/aesni-intel_asm.S @@ -1612,6 +1612,7 @@ _zero_cipher_left_encrypt: movdqa SHUF_MASK(%rip), %xmm10 PSHUFB_XMM %xmm10, %xmm0 + ENCRYPT_SINGLE_BLOCK %xmm0, %xmm1 # Encrypt(K, Yn) sub $16, %r11 add %r13, %r11 @@ -1634,7 +1635,9 @@ _zero_cipher_left_encrypt: # GHASH computation for the last <16 byte block sub %r13, %r11 add $16, %r11 - PSHUFB_XMM %xmm10, %xmm1 + + movdqa SHUF_MASK(%rip), %xmm10 + PSHUFB_XMM %xmm10, %xmm0 # shuffle xmm0 back to output as ciphertext diff --git a/arch/x86/crypto/aesni-intel_glue.c b/arch/x86/crypto/aesni-intel_glue.c index e1e60c7d5813c..b375b2a7a14f8 100644 --- a/arch/x86/crypto/aesni-intel_glue.c +++ b/arch/x86/crypto/aesni-intel_glue.c @@ -828,9 +828,15 @@ static int rfc4106_init(struct crypto_tfm *tfm) struct cryptd_aead *cryptd_tfm; struct aesni_rfc4106_gcm_ctx *ctx = (struct aesni_rfc4106_gcm_ctx *) PTR_ALIGN((u8 *)crypto_tfm_ctx(tfm), AESNI_ALIGN); + struct crypto_aead *cryptd_child; + struct aesni_rfc4106_gcm_ctx *child_ctx; cryptd_tfm = cryptd_alloc_aead("__driver-gcm-aes-aesni", 0, 0); if (IS_ERR(cryptd_tfm)) return PTR_ERR(cryptd_tfm); + + cryptd_child = cryptd_aead_child(cryptd_tfm); + child_ctx = aesni_rfc4106_gcm_ctx_get(cryptd_child); + memcpy(child_ctx, ctx, sizeof(*ctx)); ctx->cryptd_tfm = cryptd_tfm; tfm->crt_aead.reqsize = sizeof(struct aead_request) + crypto_aead_reqsize(&cryptd_tfm->base); @@ -925,6 +931,9 @@ static int rfc4106_set_key(struct crypto_aead *parent, const u8 *key, int ret = 0; struct crypto_tfm *tfm = crypto_aead_tfm(parent); struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(parent); + struct crypto_aead *cryptd_child = cryptd_aead_child(ctx->cryptd_tfm); + struct aesni_rfc4106_gcm_ctx *child_ctx = + aesni_rfc4106_gcm_ctx_get(cryptd_child); u8 *new_key_mem = NULL; if (key_len < 4) { @@ -968,6 +977,7 @@ static int rfc4106_set_key(struct crypto_aead *parent, const u8 *key, goto exit; } ret = rfc4106_set_hash_subkey(ctx->hash_subkey, key, key_len); + memcpy(child_ctx, ctx, sizeof(*ctx)); exit: kfree(new_key_mem); return ret; @@ -999,7 +1009,6 @@ static int rfc4106_encrypt(struct aead_request *req) int ret; struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm); - struct crypto_aead *cryptd_child = cryptd_aead_child(ctx->cryptd_tfm); if (!irq_fpu_usable()) { struct aead_request *cryptd_req = @@ -1008,6 +1017,7 @@ static int rfc4106_encrypt(struct aead_request *req) aead_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base); return crypto_aead_encrypt(cryptd_req); } else { + struct crypto_aead *cryptd_child = cryptd_aead_child(ctx->cryptd_tfm); kernel_fpu_begin(); ret = cryptd_child->base.crt_aead.encrypt(req); kernel_fpu_end(); @@ -1020,7 +1030,6 @@ static int rfc4106_decrypt(struct aead_request *req) int ret; struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm); - struct crypto_aead *cryptd_child = cryptd_aead_child(ctx->cryptd_tfm); if (!irq_fpu_usable()) { struct aead_request *cryptd_req = @@ -1029,6 +1038,7 @@ static int rfc4106_decrypt(struct aead_request *req) aead_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base); return crypto_aead_decrypt(cryptd_req); } else { + struct crypto_aead *cryptd_child = cryptd_aead_child(ctx->cryptd_tfm); kernel_fpu_begin(); ret = cryptd_child->base.crt_aead.decrypt(req); kernel_fpu_end(); From bf1262ee30ef0725418e7b1d72db2259e9ecd29f Mon Sep 17 00:00:00 2001 From: Arjan Mels Date: Tue, 5 Apr 2011 20:26:11 +0200 Subject: [PATCH 1180/2556] staging: usbip: bugfixes related to kthread conversion commit d2dd0b07c3e725d386d20294ec906f7ddef207fa upstream. When doing a usb port reset do a queued reset instead to prevent a deadlock: the reset will cause the driver to unbind, causing the usb_driver_lock_for_reset to stall. Signed-off-by: Arjan Mels Cc: Takahiro Hirofuchi Cc: Max Vozeler Cc: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/staging/usbip/stub_rx.c | 40 +++++++++++++-------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/drivers/staging/usbip/stub_rx.c b/drivers/staging/usbip/stub_rx.c index ae6ac82754a4d..8e60332efaef3 100644 --- a/drivers/staging/usbip/stub_rx.c +++ b/drivers/staging/usbip/stub_rx.c @@ -170,33 +170,23 @@ static int tweak_set_configuration_cmd(struct urb *urb) static int tweak_reset_device_cmd(struct urb *urb) { - struct usb_ctrlrequest *req; - __u16 value; - __u16 index; - int ret; - - req = (struct usb_ctrlrequest *) urb->setup_packet; - value = le16_to_cpu(req->wValue); - index = le16_to_cpu(req->wIndex); - - usbip_uinfo("reset_device (port %d) to %s\n", index, - dev_name(&urb->dev->dev)); + struct stub_priv *priv = (struct stub_priv *) urb->context; + struct stub_device *sdev = priv->sdev; - /* all interfaces should be owned by usbip driver, so just reset it. */ - ret = usb_lock_device_for_reset(urb->dev, NULL); - if (ret < 0) { - dev_err(&urb->dev->dev, "lock for reset\n"); - return ret; - } - - /* try to reset the device */ - ret = usb_reset_device(urb->dev); - if (ret < 0) - dev_err(&urb->dev->dev, "device reset\n"); + usbip_uinfo("reset_device %s\n", dev_name(&urb->dev->dev)); - usb_unlock_device(urb->dev); - - return ret; + /* + * usb_lock_device_for_reset caused a deadlock: it causes the driver + * to unbind. In the shutdown the rx thread is signalled to shut down + * but this thread is pending in the usb_lock_device_for_reset. + * + * Instead queue the reset. + * + * Unfortunatly an existing usbip connection will be dropped due to + * driver unbinding. + */ + usb_queue_reset_device(sdev->interface); + return 0; } /* From 6fc44b13ff9d58f2b9f799a7c9098801e38561a5 Mon Sep 17 00:00:00 2001 From: Arjan Mels Date: Tue, 5 Apr 2011 20:26:38 +0200 Subject: [PATCH 1181/2556] staging: usbip: bugfix add number of packets for isochronous frames commit 1325f85fa49f57df034869de430f7c302ae23109 upstream. The number_of_packets was not transmitted for RET_SUBMIT packets. The linux client used the stored number_of_packet from the submitted request. The windows userland client does not do this however and needs to know the number_of_packets to determine the size of the transmission. Signed-off-by: Arjan Mels Cc: Takahiro Hirofuchi Cc: Max Vozeler Signed-off-by: Greg Kroah-Hartman --- drivers/staging/usbip/usbip_common.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c index 210ef16bab8d2..63e2a2b91a401 100644 --- a/drivers/staging/usbip/usbip_common.c +++ b/drivers/staging/usbip/usbip_common.c @@ -334,10 +334,11 @@ void usbip_dump_header(struct usbip_header *pdu) usbip_udbg("CMD_UNLINK: seq %u\n", pdu->u.cmd_unlink.seqnum); break; case USBIP_RET_SUBMIT: - usbip_udbg("RET_SUBMIT: st %d al %u sf %d ec %d\n", + usbip_udbg("RET_SUBMIT: st %d al %u sf %d #p %d ec %d\n", pdu->u.ret_submit.status, pdu->u.ret_submit.actual_length, pdu->u.ret_submit.start_frame, + pdu->u.ret_submit.number_of_packets, pdu->u.ret_submit.error_count); case USBIP_RET_UNLINK: usbip_udbg("RET_UNLINK: status %d\n", pdu->u.ret_unlink.status); @@ -625,6 +626,7 @@ static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb, rpdu->status = urb->status; rpdu->actual_length = urb->actual_length; rpdu->start_frame = urb->start_frame; + rpdu->number_of_packets = urb->number_of_packets; rpdu->error_count = urb->error_count; } else { /* vhci_rx.c */ @@ -632,6 +634,7 @@ static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb, urb->status = rpdu->status; urb->actual_length = rpdu->actual_length; urb->start_frame = rpdu->start_frame; + urb->number_of_packets = rpdu->number_of_packets; urb->error_count = rpdu->error_count; } } @@ -700,11 +703,13 @@ static void correct_endian_ret_submit(struct usbip_header_ret_submit *pdu, cpu_to_be32s(&pdu->status); cpu_to_be32s(&pdu->actual_length); cpu_to_be32s(&pdu->start_frame); + cpu_to_be32s(&pdu->number_of_packets); cpu_to_be32s(&pdu->error_count); } else { be32_to_cpus(&pdu->status); be32_to_cpus(&pdu->actual_length); be32_to_cpus(&pdu->start_frame); + cpu_to_be32s(&pdu->number_of_packets); be32_to_cpus(&pdu->error_count); } } From 731f6dfde499e4b0d1eda6c6a4262e6fa330d08a Mon Sep 17 00:00:00 2001 From: Arjan Mels Date: Tue, 5 Apr 2011 20:26:59 +0200 Subject: [PATCH 1182/2556] staging: usbip: bugfix for isochronous packets and optimization commit 28276a28d8b3cd19f4449991faad4945fe557656 upstream. For isochronous packets the actual_length is the sum of the actual length of each of the packets, however between the packets might be padding, so it is not sufficient to just send the first actual_length bytes of the buffer. To fix this and simultanesouly optimize the bandwidth the content of the isochronous packets are send without the padding, the padding is restored on the receiving end. Signed-off-by: Arjan Mels Cc: Takahiro Hirofuchi Cc: Max Vozeler Signed-off-by: Greg Kroah-Hartman --- drivers/staging/usbip/stub_tx.c | 74 ++++++++++++++++++++++------ drivers/staging/usbip/usbip_common.c | 57 +++++++++++++++++++++ drivers/staging/usbip/usbip_common.h | 2 + drivers/staging/usbip/vhci_rx.c | 3 ++ 4 files changed, 122 insertions(+), 14 deletions(-) diff --git a/drivers/staging/usbip/stub_tx.c b/drivers/staging/usbip/stub_tx.c index d7136e2c86faa..b7a493c1df461 100644 --- a/drivers/staging/usbip/stub_tx.c +++ b/drivers/staging/usbip/stub_tx.c @@ -169,7 +169,6 @@ static int stub_send_ret_submit(struct stub_device *sdev) struct stub_priv *priv, *tmp; struct msghdr msg; - struct kvec iov[3]; size_t txsize; size_t total_size = 0; @@ -179,28 +178,73 @@ static int stub_send_ret_submit(struct stub_device *sdev) struct urb *urb = priv->urb; struct usbip_header pdu_header; void *iso_buffer = NULL; + struct kvec *iov = NULL; + int iovnum = 0; txsize = 0; memset(&pdu_header, 0, sizeof(pdu_header)); memset(&msg, 0, sizeof(msg)); - memset(&iov, 0, sizeof(iov)); - usbip_dbg_stub_tx("setup txdata urb %p\n", urb); + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) + iovnum = 2 + urb->number_of_packets; + else + iovnum = 2; + + iov = kzalloc(iovnum * sizeof(struct kvec), GFP_KERNEL); + if (!iov) { + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC); + return -1; + } + + iovnum = 0; /* 1. setup usbip_header */ setup_ret_submit_pdu(&pdu_header, urb); + usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n", + pdu_header.base.seqnum, urb); + /*usbip_dump_header(pdu_header);*/ usbip_header_correct_endian(&pdu_header, 1); - iov[0].iov_base = &pdu_header; - iov[0].iov_len = sizeof(pdu_header); + iov[iovnum].iov_base = &pdu_header; + iov[iovnum].iov_len = sizeof(pdu_header); + iovnum++; txsize += sizeof(pdu_header); /* 2. setup transfer buffer */ - if (usb_pipein(urb->pipe) && urb->actual_length > 0) { - iov[1].iov_base = urb->transfer_buffer; - iov[1].iov_len = urb->actual_length; + if (usb_pipein(urb->pipe) && + usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS && + urb->actual_length > 0) { + iov[iovnum].iov_base = urb->transfer_buffer; + iov[iovnum].iov_len = urb->actual_length; + iovnum++; txsize += urb->actual_length; + } else if (usb_pipein(urb->pipe) && + usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + /* + * For isochronous packets: actual length is the sum of + * the actual length of the individual, packets, but as + * the packet offsets are not changed there will be + * padding between the packets. To optimally use the + * bandwidth the padding is not transmitted. + */ + + int i; + for (i = 0; i < urb->number_of_packets; i++) { + iov[iovnum].iov_base = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + iov[iovnum].iov_len = urb->iso_frame_desc[i].actual_length; + iovnum++; + txsize += urb->iso_frame_desc[i].actual_length; + } + + if (txsize != sizeof(pdu_header) + urb->actual_length) { + dev_err(&sdev->interface->dev, + "actual length of urb (%d) does not match iso packet sizes (%d)\n", + urb->actual_length, txsize-sizeof(pdu_header)); + kfree(iov); + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); + return -1; + } } /* 3. setup iso_packet_descriptor */ @@ -211,32 +255,34 @@ static int stub_send_ret_submit(struct stub_device *sdev) if (!iso_buffer) { usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC); + kfree(iov); return -1; } - iov[2].iov_base = iso_buffer; - iov[2].iov_len = len; + iov[iovnum].iov_base = iso_buffer; + iov[iovnum].iov_len = len; txsize += len; + iovnum++; } - ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov, - 3, txsize); + ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, + iov, iovnum, txsize); if (ret != txsize) { dev_err(&sdev->interface->dev, "sendmsg failed!, retval %d for %zd\n", ret, txsize); + kfree(iov); kfree(iso_buffer); usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); return -1; } + kfree(iov); kfree(iso_buffer); - usbip_dbg_stub_tx("send txdata\n"); total_size += txsize; } - spin_lock_irqsave(&sdev->priv_lock, flags); list_for_each_entry_safe(priv, tmp, &sdev->priv_free, list) { diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c index 63e2a2b91a401..2108ca15542e6 100644 --- a/drivers/staging/usbip/usbip_common.c +++ b/drivers/staging/usbip/usbip_common.c @@ -835,6 +835,7 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) int size = np * sizeof(*iso); int i; int ret; + int total_length = 0; if (!usb_pipeisoc(urb->pipe)) return 0; @@ -864,19 +865,75 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) return -EPIPE; } + for (i = 0; i < np; i++) { iso = buff + (i * sizeof(*iso)); usbip_iso_pakcet_correct_endian(iso, 0); usbip_pack_iso(iso, &urb->iso_frame_desc[i], 0); + total_length += urb->iso_frame_desc[i].actual_length; } kfree(buff); + if (total_length != urb->actual_length) { + dev_err(&urb->dev->dev, + "total length of iso packets (%d) not equal to actual length of buffer (%d)\n", + total_length, urb->actual_length); + + if (ud->side == USBIP_STUB) + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + else + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + + return -EPIPE; + } + return ret; } EXPORT_SYMBOL_GPL(usbip_recv_iso); +/* + * This functions restores the padding which was removed for optimizing + * the bandwidth during transfer over tcp/ip + * + * buffer and iso packets need to be stored and be in propeper endian in urb + * before calling this function + */ +int usbip_pad_iso(struct usbip_device *ud, struct urb *urb) +{ + int np = urb->number_of_packets; + int i; + int ret; + int actualoffset = urb->actual_length; + + if (!usb_pipeisoc(urb->pipe)) + return 0; + + /* if no packets or length of data is 0, then nothing to unpack */ + if (np == 0 || urb->actual_length == 0) + return 0; + + /* + * if actual_length is transfer_buffer_length then no padding is + * present. + */ + if (urb->actual_length == urb->transfer_buffer_length) + return 0; + + /* + * loop over all packets from last to first (to prevent overwritting + * memory when padding) and move them into the proper place + */ + for (i = np-1; i > 0; i--) { + actualoffset -= urb->iso_frame_desc[i].actual_length; + memmove(urb->transfer_buffer + urb->iso_frame_desc[i].offset, + urb->transfer_buffer + actualoffset, + urb->iso_frame_desc[i].actual_length); + } + return ret; +} +EXPORT_SYMBOL_GPL(usbip_pad_iso); /* some members of urb must be substituted before. */ int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb) diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h index d280e234e0670..baa4c09bb98c6 100644 --- a/drivers/staging/usbip/usbip_common.h +++ b/drivers/staging/usbip/usbip_common.h @@ -393,6 +393,8 @@ void usbip_header_correct_endian(struct usbip_header *pdu, int send); int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb); /* some members of urb must be substituted before. */ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb); +/* some members of urb must be substituted before. */ +int usbip_pad_iso(struct usbip_device *ud, struct urb *urb); void *usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen); diff --git a/drivers/staging/usbip/vhci_rx.c b/drivers/staging/usbip/vhci_rx.c index bf69914709410..109002a347b9a 100644 --- a/drivers/staging/usbip/vhci_rx.c +++ b/drivers/staging/usbip/vhci_rx.c @@ -99,6 +99,9 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev, if (usbip_recv_iso(ud, urb) < 0) return; + /* restore the padding in iso packets */ + if (usbip_pad_iso(ud, urb) < 0) + return; if (usbip_dbg_flag_vhci_rx) usbip_dump_urb(urb); From 3795580e35e7c28a52e26f41cdb029187d97a100 Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Mon, 21 Mar 2011 14:41:37 +0100 Subject: [PATCH 1183/2556] staging: hv: use sync_bitops when interacting with the hypervisor commit 22356585712d1ff08fbfed152edd8b386873b238 upstream. Locking is required when tweaking bits located in a shared page, use the sync_ version of bitops. Without this change vmbus_on_event() will miss events and as a result, vmbus_isr() will not schedule the receive tasklet. Signed-off-by: Olaf Hering Acked-by: Haiyang Zhang Acked-by: Hank Janssen Signed-off-by: Greg Kroah-Hartman --- drivers/staging/hv/channel.c | 8 ++++---- drivers/staging/hv/connection.c | 4 ++-- drivers/staging/hv/vmbus_drv.c | 2 +- drivers/staging/hv/vmbus_private.h | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/staging/hv/channel.c b/drivers/staging/hv/channel.c index 45a627d77b417..09e596a506b0e 100644 --- a/drivers/staging/hv/channel.c +++ b/drivers/staging/hv/channel.c @@ -76,14 +76,14 @@ static void vmbus_setevent(struct vmbus_channel *channel) if (channel->offermsg.monitor_allocated) { /* Each u32 represents 32 channels */ - set_bit(channel->offermsg.child_relid & 31, + sync_set_bit(channel->offermsg.child_relid & 31, (unsigned long *) gVmbusConnection.SendInterruptPage + (channel->offermsg.child_relid >> 5)); monitorpage = gVmbusConnection.MonitorPages; monitorpage++; /* Get the child to parent monitor page */ - set_bit(channel->monitor_bit, + sync_set_bit(channel->monitor_bit, (unsigned long *)&monitorpage->trigger_group [channel->monitor_grp].pending); @@ -99,7 +99,7 @@ static void VmbusChannelClearEvent(struct vmbus_channel *channel) if (Channel->offermsg.monitor_allocated) { /* Each u32 represents 32 channels */ - clear_bit(Channel->offermsg.child_relid & 31, + sync_clear_bit(Channel->offermsg.child_relid & 31, (unsigned long *)gVmbusConnection.SendInterruptPage + (Channel->offermsg.child_relid >> 5)); @@ -107,7 +107,7 @@ static void VmbusChannelClearEvent(struct vmbus_channel *channel) (struct hv_monitor_page *)gVmbusConnection.MonitorPages; monitorPage++; /* Get the child to parent monitor page */ - clear_bit(Channel->monitor_bit, + sync_clear_bit(Channel->monitor_bit, (unsigned long *)&monitorPage->trigger_group [Channel->monitor_grp].Pending); } diff --git a/drivers/staging/hv/connection.c b/drivers/staging/hv/connection.c index c2e298ff48344..0739eb7b6ee11 100644 --- a/drivers/staging/hv/connection.c +++ b/drivers/staging/hv/connection.c @@ -281,7 +281,7 @@ void VmbusOnEvents(void) for (dword = 0; dword < maxdword; dword++) { if (recvInterruptPage[dword]) { for (bit = 0; bit < 32; bit++) { - if (test_and_clear_bit(bit, (unsigned long *)&recvInterruptPage[dword])) { + if (sync_test_and_clear_bit(bit, (unsigned long *)&recvInterruptPage[dword])) { relid = (dword << 5) + bit; DPRINT_DBG(VMBUS, "event detected for relid - %d", relid); @@ -320,7 +320,7 @@ int VmbusPostMessage(void *buffer, size_t bufferLen) int VmbusSetEvent(u32 childRelId) { /* Each u32 represents 32 channels */ - set_bit(childRelId & 31, + sync_set_bit(childRelId & 31, (unsigned long *)gVmbusConnection.SendInterruptPage + (childRelId >> 5)); diff --git a/drivers/staging/hv/vmbus_drv.c b/drivers/staging/hv/vmbus_drv.c index 84fdb64d3ceb2..87e6cf2086f08 100644 --- a/drivers/staging/hv/vmbus_drv.c +++ b/drivers/staging/hv/vmbus_drv.c @@ -291,7 +291,7 @@ static int vmbus_on_isr(struct hv_driver *drv) event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT; /* Since we are a child, we only need to check bit 0 */ - if (test_and_clear_bit(0, (unsigned long *) &event->flags32[0])) { + if (sync_test_and_clear_bit(0, (unsigned long *) &event->flags32[0])) { DPRINT_DBG(VMBUS, "received event %d", event->flags32[0]); ret |= 0x2; } diff --git a/drivers/staging/hv/vmbus_private.h b/drivers/staging/hv/vmbus_private.h index 07f6d22eeabb5..c75b2d7fb2f06 100644 --- a/drivers/staging/hv/vmbus_private.h +++ b/drivers/staging/hv/vmbus_private.h @@ -31,6 +31,7 @@ #include "channel_mgmt.h" #include "ring_buffer.h" #include +#include /* From 18b4f6ad93bcd97d1fba87c0b44816ecbdb307e5 Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Wed, 6 Apr 2011 15:18:00 -0700 Subject: [PATCH 1184/2556] staging: hv: Fix GARP not sent after Quick Migration commit c996edcf1c451b81740abbcca5257ed7e353fcc6 upstream. After Quick Migration, the network is not immediately operational in the current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, I added another netif_notify_peers() into a scheduled work, otherwise GARP packet will not be sent after quick migration, and cause network disconnection. Thanks to Mike Surcouf for reporting the bug and testing the patch. Reported-by: Mike Surcouf Tested-by: Mike Surcouf Signed-off-by: Haiyang Zhang Signed-off-by: Hank Janssen Signed-off-by: Abhishek Kane Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/staging/hv/netvsc_drv.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/staging/hv/netvsc_drv.c b/drivers/staging/hv/netvsc_drv.c index b41c9640b72dc..f433addaae569 100644 --- a/drivers/staging/hv/netvsc_drv.c +++ b/drivers/staging/hv/netvsc_drv.c @@ -46,6 +46,7 @@ struct net_device_context { /* point back to our device context */ struct vm_device *device_ctx; unsigned long avail; + struct work_struct work; }; struct netvsc_driver_context { @@ -225,6 +226,7 @@ static void netvsc_linkstatus_callback(struct hv_device *device_obj, unsigned int status) { struct vm_device *device_ctx = to_vm_device(device_obj); + struct net_device_context *ndev_ctx; struct net_device *net = dev_get_drvdata(&device_ctx->device); if (!net) { @@ -237,6 +239,8 @@ static void netvsc_linkstatus_callback(struct hv_device *device_obj, netif_carrier_on(net); netif_wake_queue(net); netif_notify_peers(net); + ndev_ctx = netdev_priv(net); + schedule_work(&ndev_ctx->work); } else { netif_carrier_off(net); netif_stop_queue(net); @@ -336,6 +340,25 @@ static const struct net_device_ops device_ops = { .ndo_set_mac_address = eth_mac_addr, }; +/* + * Send GARP packet to network peers after migrations. + * After Quick Migration, the network is not immediately operational in the + * current context when receiving RNDIS_STATUS_MEDIA_CONNECT event. So, add + * another netif_notify_peers() into a scheduled work, otherwise GARP packet + * will not be sent after quick migration, and cause network disconnection. + */ +static void netvsc_send_garp(struct work_struct *w) +{ + struct net_device_context *ndev_ctx; + struct net_device *net; + + msleep(20); + ndev_ctx = container_of(w, struct net_device_context, work); + net = dev_get_drvdata(&ndev_ctx->device_ctx->device); + netif_notify_peers(net); +} + + static int netvsc_probe(struct device *device) { struct driver_context *driver_ctx = @@ -364,6 +387,7 @@ static int netvsc_probe(struct device *device) net_device_ctx->device_ctx = device_ctx; net_device_ctx->avail = ring_size; dev_set_drvdata(device, net); + INIT_WORK(&net_device_ctx->work, netvsc_send_garp); /* Notify the netvsc driver of the new device */ ret = net_drv_obj->base.OnDeviceAdd(device_obj, &device_info); From 2cc2555a96fe4bddd1f6d990d1b86ffb91497f3c Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Mon, 28 Mar 2011 14:13:35 -0700 Subject: [PATCH 1185/2556] Relax si_code check in rt_sigqueueinfo and rt_tgsigqueueinfo commit 243b422af9ea9af4ead07a8ad54c90d4f9b6081a upstream. Commit da48524eb206 ("Prevent rt_sigqueueinfo and rt_tgsigqueueinfo from spoofing the signal code") made the check on si_code too strict. There are several legitimate places where glibc wants to queue a negative si_code different from SI_QUEUE: - This was first noticed with glibc's aio implementation, which wants to queue a signal with si_code SI_ASYNCIO; the current kernel causes glibc's tst-aio4 test to fail because rt_sigqueueinfo() fails with EPERM. - Further examination of the glibc source shows that getaddrinfo_a() wants to use SI_ASYNCNL (which the kernel does not even define). The timer_create() fallback code wants to queue signals with SI_TIMER. As suggested by Oleg Nesterov , loosen the check to forbid only the problematic SI_TKILL case. Reported-by: Klaus Dittrich Acked-by: Julien Tinnes Signed-off-by: Roland Dreier Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/signal.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/signal.c b/kernel/signal.c index 31751868de885..bf11d2697e9e2 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2423,7 +2423,7 @@ SYSCALL_DEFINE3(rt_sigqueueinfo, pid_t, pid, int, sig, /* Not even root can pretend to send signals from the kernel. * Nor can they impersonate a kill()/tgkill(), which adds source info. */ - if (info.si_code != SI_QUEUE) { + if (info.si_code >= 0 || info.si_code == SI_TKILL) { /* We used to allow any < 0 si_code */ WARN_ON_ONCE(info.si_code < 0); return -EPERM; @@ -2443,7 +2443,7 @@ long do_rt_tgsigqueueinfo(pid_t tgid, pid_t pid, int sig, siginfo_t *info) /* Not even root can pretend to send signals from the kernel. * Nor can they impersonate a kill()/tgkill(), which adds source info. */ - if (info->si_code != SI_QUEUE) { + if (info->si_code >= 0 || info->si_code == SI_TKILL) { /* We used to allow any < 0 si_code */ WARN_ON_ONCE(info->si_code < 0); return -EPERM; From 5b74dec635bd4a0838d1efcdd685e0b358a0d7df Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Sat, 26 Mar 2011 09:14:57 +1100 Subject: [PATCH 1186/2556] xfs: register the inode cache shrinker before quotachecks commit 704b2907c2d47ceb187c0e25a6bbc2174b198f2f upstream. During mount, we can do a quotacheck that involves a bulkstat pass on all inodes. If there are more inodes in the filesystem than can be held in memory, we require the inode cache shrinker to run to ensure that we don't run out of memory. Unfortunately, the inode cache shrinker is not registered until we get to the end of the superblock setup process, which is after a quotacheck is run if it is needed. Hence we need to register the inode cache shrinker earlier in the mount process so that we don't OOM during mount. This requires that we also initialise the syncd work before we register the shrinker, so we nee dto juggle that around as well. While there, make sure that we have set up the block sizes in the VFS superblock correctly before the quotacheck is run so that any inodes that are cached as a result of the quotacheck have their block size fields set up correctly. Signed-off-by: Dave Chinner Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- fs/xfs/linux-2.6/xfs_super.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 9731898083ae8..ad485b60340fb 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -1551,10 +1551,14 @@ xfs_fs_fill_super( if (error) goto out_free_sb; - error = xfs_mountfs(mp); - if (error) - goto out_filestream_unmount; - + /* + * we must configure the block size in the superblock before we run the + * full mount process as the mount process can lookup and cache inodes. + * For the same reason we must also initialise the syncd and register + * the inode cache shrinker so that inodes can be reclaimed during + * operations like a quotacheck that iterate all inodes in the + * filesystem. + */ sb->s_magic = XFS_SB_MAGIC; sb->s_blocksize = mp->m_sb.sb_blocksize; sb->s_blocksize_bits = ffs(sb->s_blocksize) - 1; @@ -1562,6 +1566,16 @@ xfs_fs_fill_super( sb->s_time_gran = 1; set_posix_acl_flag(sb); + error = xfs_syncd_init(mp); + if (error) + goto out_filestream_unmount; + + xfs_inode_shrinker_register(mp); + + error = xfs_mountfs(mp); + if (error) + goto out_syncd_stop; + root = igrab(VFS_I(mp->m_rootip)); if (!root) { error = ENOENT; @@ -1577,14 +1591,11 @@ xfs_fs_fill_super( goto fail_vnrele; } - error = xfs_syncd_init(mp); - if (error) - goto fail_vnrele; - - xfs_inode_shrinker_register(mp); - return 0; + out_syncd_stop: + xfs_inode_shrinker_unregister(mp); + xfs_syncd_stop(mp); out_filestream_unmount: xfs_filestream_unmount(mp); out_free_sb: @@ -1608,6 +1619,9 @@ xfs_fs_fill_super( } fail_unmount: + xfs_inode_shrinker_unregister(mp); + xfs_syncd_stop(mp); + /* * Blow away any referenced inode in the filestreams cache. * This can and will cause log traffic as inodes go inactive From 433c1941c2776165e02d2273be32daf75f123b75 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 29 Mar 2011 18:10:53 +0200 Subject: [PATCH 1187/2556] amd64_edac: Fix potential memleak commit a9f0fbe2bbf328f869fc5ee5a12c6a4118c32689 upstream. We check the pointers together but at least one of them could be invalid due to failed allocation. Since we cannot continue if either of the two allocations has failed, exit early by freeing them both. Reported-by: Mauro Carvalho Chehab Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/edac/amd64_edac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 23e03554f0d3a..7e0e66037e025 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -2765,7 +2765,7 @@ static int __init amd64_edac_init(void) mcis = kzalloc(amd_nb_num() * sizeof(mcis[0]), GFP_KERNEL); ecc_stngs = kzalloc(amd_nb_num() * sizeof(ecc_stngs[0]), GFP_KERNEL); if (!(mcis && ecc_stngs)) - goto err_ret; + goto err_free; msrs = msrs_alloc(); if (!msrs) From ccfdcce13d96dfe1d641efc302ad380ed31b205e Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 26 Feb 2011 17:34:38 +0100 Subject: [PATCH 1188/2556] watchdog: s3c2410_wdt.c: Convert release_resource to release_region/release_mem_region commit f72401e94d159bc4b2beab51d74e956da2c32e0a upstream. Request_mem_region should be used with release_mem_region, not release_resource. The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression x,E; @@ *x = request_mem_region(...) ... when != release_mem_region(x) when != x = E * release_resource(x); // Signed-off-by: Julia Lawall Signed-off-by: Wim Van Sebroeck Signed-off-by: Greg Kroah-Hartman --- drivers/watchdog/s3c2410_wdt.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index ae53662c29bce..8303c576c57e0 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -402,7 +402,6 @@ static inline void s3c2410wdt_cpufreq_deregister(void) static int __devinit s3c2410wdt_probe(struct platform_device *pdev) { - struct resource *res; struct device *dev; unsigned int wtcon; int started = 0; @@ -416,20 +415,19 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) /* get the memory region for the watchdog timer */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { + wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (wdt_mem == NULL) { dev_err(dev, "no memory resource specified\n"); return -ENOENT; } - size = resource_size(res); - wdt_mem = request_mem_region(res->start, size, pdev->name); - if (wdt_mem == NULL) { + size = resource_size(wdt_mem); + if (!request_mem_region(wdt_mem->start, size, pdev->name)) { dev_err(dev, "failed to get memory region\n"); return -EBUSY; } - wdt_base = ioremap(res->start, size); + wdt_base = ioremap(wdt_mem->start, size); if (wdt_base == NULL) { dev_err(dev, "failed to ioremap() region\n"); ret = -EINVAL; @@ -524,8 +522,8 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev) iounmap(wdt_base); err_req: - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; return ret; } @@ -545,8 +543,7 @@ static int __devexit s3c2410wdt_remove(struct platform_device *dev) iounmap(wdt_base); - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, resource_size(wdt_mem)); wdt_mem = NULL; return 0; } From cbbfd73caaf36d8c4e608be8bb8c3a5d204d91e4 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 26 Feb 2011 17:34:39 +0100 Subject: [PATCH 1189/2556] watchdog: Convert release_resource to release_region/release_mem_region commit f712eacf02ecfbf4f1686addb8c569841549b0b7 upstream. Request_mem_region should be used with release_mem_region, not release_resource. In pnx4008_wdt.c, a missing clk_put is added as well. The semantic match that finds the first problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression x,E; @@ *x = request_mem_region(...) ... when != release_mem_region(x) when != x = E * release_resource(x); // Signed-off-by: Julia Lawall Signed-off-by: Wim Van Sebroeck Signed-off-by: Greg Kroah-Hartman --- drivers/watchdog/davinci_wdt.c | 22 ++++++++++------------ drivers/watchdog/max63xx_wdt.c | 20 ++++++++------------ drivers/watchdog/pnx4008_wdt.c | 28 +++++++++++++--------------- 3 files changed, 31 insertions(+), 39 deletions(-) diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c index 596ba604e78d1..51b5551b4e3fe 100644 --- a/drivers/watchdog/davinci_wdt.c +++ b/drivers/watchdog/davinci_wdt.c @@ -202,7 +202,6 @@ static struct miscdevice davinci_wdt_miscdev = { static int __devinit davinci_wdt_probe(struct platform_device *pdev) { int ret = 0, size; - struct resource *res; struct device *dev = &pdev->dev; wdt_clk = clk_get(dev, NULL); @@ -216,31 +215,31 @@ static int __devinit davinci_wdt_probe(struct platform_device *pdev) dev_info(dev, "heartbeat %d sec\n", heartbeat); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { + wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (wdt_mem == NULL) { dev_err(dev, "failed to get memory region resource\n"); return -ENOENT; } - size = resource_size(res); - wdt_mem = request_mem_region(res->start, size, pdev->name); - - if (wdt_mem == NULL) { + size = resource_size(wdt_mem); + if (!request_mem_region(wdt_mem->start, size, pdev->name)) { dev_err(dev, "failed to get memory region\n"); return -ENOENT; } - wdt_base = ioremap(res->start, size); + wdt_base = ioremap(wdt_mem->start, size); if (!wdt_base) { dev_err(dev, "failed to map memory region\n"); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; return -ENOMEM; } ret = misc_register(&davinci_wdt_miscdev); if (ret < 0) { dev_err(dev, "cannot register misc device\n"); - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; } else { set_bit(WDT_DEVICE_INITED, &wdt_status); } @@ -253,8 +252,7 @@ static int __devexit davinci_wdt_remove(struct platform_device *pdev) { misc_deregister(&davinci_wdt_miscdev); if (wdt_mem) { - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, resource_size(wdt_mem)); wdt_mem = NULL; } diff --git a/drivers/watchdog/max63xx_wdt.c b/drivers/watchdog/max63xx_wdt.c index 3053ff05ca410..1fe9bc5a96517 100644 --- a/drivers/watchdog/max63xx_wdt.c +++ b/drivers/watchdog/max63xx_wdt.c @@ -270,7 +270,6 @@ static int __devinit max63xx_wdt_probe(struct platform_device *pdev) { int ret = 0; int size; - struct resource *res; struct device *dev = &pdev->dev; struct max63xx_timeout *table; @@ -294,21 +293,19 @@ static int __devinit max63xx_wdt_probe(struct platform_device *pdev) max63xx_pdev = pdev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { + wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (wdt_mem == NULL) { dev_err(dev, "failed to get memory region resource\n"); return -ENOENT; } - size = resource_size(res); - wdt_mem = request_mem_region(res->start, size, pdev->name); - - if (wdt_mem == NULL) { + size = resource_size(wdt_mem); + if (!request_mem_region(wdt_mem->start, size, pdev->name)) { dev_err(dev, "failed to get memory region\n"); return -ENOENT; } - wdt_base = ioremap(res->start, size); + wdt_base = ioremap(wdt_mem->start, size); if (!wdt_base) { dev_err(dev, "failed to map memory region\n"); ret = -ENOMEM; @@ -326,8 +323,8 @@ static int __devinit max63xx_wdt_probe(struct platform_device *pdev) out_unmap: iounmap(wdt_base); out_request: - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; return ret; } @@ -336,8 +333,7 @@ static int __devexit max63xx_wdt_remove(struct platform_device *pdev) { misc_deregister(&max63xx_wdt_miscdev); if (wdt_mem) { - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, resource_size(wdt_mem)); wdt_mem = NULL; } diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c index bf5b97c546eb4..8c8c7d54497c4 100644 --- a/drivers/watchdog/pnx4008_wdt.c +++ b/drivers/watchdog/pnx4008_wdt.c @@ -254,7 +254,6 @@ static struct miscdevice pnx4008_wdt_miscdev = { static int __devinit pnx4008_wdt_probe(struct platform_device *pdev) { int ret = 0, size; - struct resource *res; if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) heartbeat = DEFAULT_HEARTBEAT; @@ -262,42 +261,42 @@ static int __devinit pnx4008_wdt_probe(struct platform_device *pdev) printk(KERN_INFO MODULE_NAME "PNX4008 Watchdog Timer: heartbeat %d sec\n", heartbeat); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { + wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (wdt_mem == NULL) { printk(KERN_INFO MODULE_NAME "failed to get memory region resouce\n"); return -ENOENT; } - size = resource_size(res); - wdt_mem = request_mem_region(res->start, size, pdev->name); + size = resource_size(wdt_mem); - if (wdt_mem == NULL) { + if (!request_mem_region(wdt_mem->start, size, pdev->name)) { printk(KERN_INFO MODULE_NAME "failed to get memory region\n"); return -ENOENT; } - wdt_base = (void __iomem *)IO_ADDRESS(res->start); + wdt_base = (void __iomem *)IO_ADDRESS(wdt_mem->start); wdt_clk = clk_get(&pdev->dev, NULL); if (IS_ERR(wdt_clk)) { ret = PTR_ERR(wdt_clk); - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; goto out; } ret = clk_enable(wdt_clk); if (ret) { - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; + clk_put(wdt_clk); goto out; } ret = misc_register(&pnx4008_wdt_miscdev); if (ret < 0) { printk(KERN_ERR MODULE_NAME "cannot register misc device\n"); - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, size); + wdt_mem = NULL; clk_disable(wdt_clk); clk_put(wdt_clk); } else { @@ -320,8 +319,7 @@ static int __devexit pnx4008_wdt_remove(struct platform_device *pdev) clk_put(wdt_clk); if (wdt_mem) { - release_resource(wdt_mem); - kfree(wdt_mem); + release_mem_region(wdt_mem->start, resource_size(wdt_mem)); wdt_mem = NULL; } return 0; From 2c00912e3e6949b6ae733da7ee05f65362ee00f2 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Wed, 16 Mar 2011 20:01:07 -0700 Subject: [PATCH 1190/2556] watchdog: sp5100_tco.c: Check if firmware has set correct value in tcobase. commit 90d241edd13bdeef70f264b569f7e150bf23621e upstream. Stefano found SP5100 TCO watchdog driver using wrong address. [ 9.148536] SP5100 TCO timer: SP5100 TCO WatchDog Timer Driver v0.01 [ 9.148628] DEBUG __ioremap_caller WARNING address=b8fe00 size=8 valid=1 reserved=1 and e820 said that range is RAM. We should check if we can use that reading out. BIOS could just program wrong address there. Reported-by: Stefano Stabellini Signed-off-by:Yinghai Lu Acked-by: Mike Waychison Tested-by: Konrad Rzeszutek Wilk Signed-off-by: Wim Van Sebroeck Signed-off-by: Greg Kroah-Hartman --- drivers/watchdog/sp5100_tco.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 808372883e88e..c7ea4bedfe63e 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -42,6 +42,7 @@ #define PFX TCO_MODULE_NAME ": " /* internal variables */ +static u32 tcobase_phys; static void __iomem *tcobase; static unsigned int pm_iobase; static DEFINE_SPINLOCK(tco_lock); /* Guards the hardware */ @@ -305,10 +306,18 @@ static unsigned char __devinit sp5100_tco_setupdevice(void) /* Low three bits of BASE0 are reserved. */ val = val << 8 | (inb(SP5100_IO_PM_DATA_REG) & 0xf8); + if (!request_mem_region_exclusive(val, SP5100_WDT_MEM_MAP_SIZE, + "SP5100 TCO")) { + printk(KERN_ERR PFX "mmio address 0x%04x already in use\n", + val); + goto unreg_region; + } + tcobase_phys = val; + tcobase = ioremap(val, SP5100_WDT_MEM_MAP_SIZE); if (tcobase == 0) { printk(KERN_ERR PFX "failed to get tcobase address\n"); - goto unreg_region; + goto unreg_mem_region; } /* Enable watchdog decode bit */ @@ -346,7 +355,8 @@ static unsigned char __devinit sp5100_tco_setupdevice(void) /* Done */ return 1; - iounmap(tcobase); +unreg_mem_region: + release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); unreg_region: release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); exit: @@ -401,6 +411,7 @@ static int __devinit sp5100_tco_init(struct platform_device *dev) exit: iounmap(tcobase); + release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); return ret; } @@ -414,6 +425,7 @@ static void __devexit sp5100_tco_cleanup(void) /* Deregister */ misc_deregister(&sp5100_tco_miscdev); iounmap(tcobase); + release_mem_region(tcobase_phys, SP5100_WDT_MEM_MAP_SIZE); release_region(pm_iobase, SP5100_PM_IOPORTS_SIZE); } From d835d7545fa28207ced8c3522e790c0d84a1dbf3 Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Sun, 20 Mar 2011 15:32:06 +0000 Subject: [PATCH 1191/2556] irda: validate peer name and attribute lengths commit d370af0ef7951188daeb15bae75db7ba57c67846 upstream. Length fields provided by a peer for names and attributes may be longer than the destination array sizes. Validate lengths to prevent stack buffer overflows. Signed-off-by: Dan Rosenberg Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/irda/iriap.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/irda/iriap.c b/net/irda/iriap.c index 5b743bdd89ba2..36477538cea8e 100644 --- a/net/irda/iriap.c +++ b/net/irda/iriap.c @@ -656,10 +656,16 @@ static void iriap_getvaluebyclass_indication(struct iriap_cb *self, n = 1; name_len = fp[n++]; + + IRDA_ASSERT(name_len < IAS_MAX_CLASSNAME + 1, return;); + memcpy(name, fp+n, name_len); n+=name_len; name[name_len] = '\0'; attr_len = fp[n++]; + + IRDA_ASSERT(attr_len < IAS_MAX_ATTRIBNAME + 1, return;); + memcpy(attr, fp+n, attr_len); n+=attr_len; attr[attr_len] = '\0'; From 57d9c81c515f9a9ab75f496a601d0c221b4154b7 Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Sat, 19 Mar 2011 20:14:30 +0000 Subject: [PATCH 1192/2556] irda: prevent heap corruption on invalid nickname commit d50e7e3604778bfc2dc40f440e0742dbae399d54 upstream. Invalid nicknames containing only spaces will result in an underflow in a memcpy size calculation, subsequently destroying the heap and panicking. v2 also catches the case where the provided nickname is longer than the buffer size, which can result in controllable heap corruption. Signed-off-by: Dan Rosenberg Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/irda/irnet/irnet_ppp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/irda/irnet/irnet_ppp.c b/net/irda/irnet/irnet_ppp.c index 7c567b8aa89a9..2bb2beb6a373d 100644 --- a/net/irda/irnet/irnet_ppp.c +++ b/net/irda/irnet/irnet_ppp.c @@ -105,6 +105,9 @@ irnet_ctrl_write(irnet_socket * ap, while(isspace(start[length - 1])) length--; + DABORT(length < 5 || length > NICKNAME_MAX_LEN + 5, + -EINVAL, CTRL_ERROR, "Invalid nickname.\n"); + /* Copy the name for later reuse */ memcpy(ap->rname, start + 5, length - 5); ap->rname[length - 5] = '\0'; From 7633c02779ce0f9be5108f19fc2af25a15f08a2e Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 20 Mar 2011 15:28:03 +0000 Subject: [PATCH 1193/2556] powerpc: Fix accounting of softirq time when idle commit ad5d1c888e556bc00c4e86f452cad4a3a87d22c1 upstream. commit cf9efce0ce31 (powerpc: Account time using timebase rather than PURR) used in_irq() to detect if the time was spent in interrupt processing. This only catches hardirq context so if we are in softirq context and in the idle loop we end up accounting it as idle time. If we instead use in_interrupt() we catch both softirq and hardirq time. The issue was found when running a network intensive workload. top showed the following: 0.0%us, 1.1%sy, 0.0%ni, 85.7%id, 0.0%wa, 9.9%hi, 3.3%si, 0.0%st 85.7% idle. But this was wildly different to the perf events data. To confirm the suspicion I ran something to keep the core busy: # yes > /dev/null & 8.2%us, 0.0%sy, 0.0%ni, 0.0%id, 0.0%wa, 10.3%hi, 81.4%si, 0.0%st We only got 8.2% of the CPU for the userspace task and softirq has shot up to 81.4%. With the patch below top shows the correct stats: 0.0%us, 0.0%sy, 0.0%ni, 5.3%id, 0.0%wa, 13.3%hi, 81.3%si, 0.0%st Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/time.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 09d31dbf43f99..aa9269600ca21 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -356,7 +356,7 @@ void account_system_vtime(struct task_struct *tsk) } get_paca()->user_time_scaled += user_scaled; - if (in_irq() || idle_task(smp_processor_id()) != tsk) { + if (in_interrupt() || idle_task(smp_processor_id()) != tsk) { account_system_time(tsk, 0, delta, sys_scaled); if (stolen) account_steal_time(stolen); From 5d2a4a244e87f6ef9696f136762fa4aa549b8d9e Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 27 Mar 2011 22:50:49 +0900 Subject: [PATCH 1194/2556] nilfs2: fix data loss in mmap page write for hole blocks commit 34094537943113467faee98fe67c8a3d3f9a0a8b upstream. From the result of a function test of mmap, mmap write to shared pages turned out to be broken for hole blocks. It doesn't write out filled blocks and the data will be lost after umount. This is due to a bug that the target file is not queued for log writer when filling hole blocks. Also, nilfs_page_mkwrite function exits normal code path even after successfully filled hole blocks due to a change of block_page_mkwrite function; just after nilfs was merged into the mainline, block_page_mkwrite() started to return VM_FAULT_LOCKED instead of zero by the patch "mm: close page_mkwrite races" (commit: b827e496c893de0c). The current nilfs_page_mkwrite() is not handling this value properly. This corrects nilfs_page_mkwrite() and will resolve the data loss problem in mmap write. [This should be applied to every kernel since 2.6.30 but a fix is needed for 2.6.37 and prior kernels] Signed-off-by: Ryusuke Konishi Tested-by: Ryusuke Konishi Signed-off-by: Greg Kroah-Hartman --- fs/nilfs2/file.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/nilfs2/file.c b/fs/nilfs2/file.c index 2f560c9fb8081..f49e6287c879b 100644 --- a/fs/nilfs2/file.c +++ b/fs/nilfs2/file.c @@ -72,10 +72,9 @@ static int nilfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) /* * check to see if the page is mapped already (no holes) */ - if (PageMappedToDisk(page)) { - unlock_page(page); + if (PageMappedToDisk(page)) goto mapped; - } + if (page_has_buffers(page)) { struct buffer_head *bh, *head; int fully_mapped = 1; @@ -90,7 +89,6 @@ static int nilfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) if (fully_mapped) { SetPageMappedToDisk(page); - unlock_page(page); goto mapped; } } @@ -105,16 +103,17 @@ static int nilfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) return VM_FAULT_SIGBUS; ret = block_page_mkwrite(vma, vmf, nilfs_get_block); - if (unlikely(ret)) { + if (ret != VM_FAULT_LOCKED) { nilfs_transaction_abort(inode->i_sb); return ret; } + nilfs_set_file_dirty(inode, 1 << (PAGE_SHIFT - inode->i_blkbits)); nilfs_transaction_commit(inode->i_sb); mapped: SetPageChecked(page); wait_on_page_writeback(page); - return 0; + return VM_FAULT_LOCKED; } static const struct vm_operations_struct nilfs_file_vm_ops = { From 84672c0dcacfef6b67f031ac21c20324a87fab26 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 23 Mar 2011 20:45:40 +0000 Subject: [PATCH 1195/2556] ASoC: Explicitly say registerless widgets have no register commit 0ca03cd7d0fa3bfbd56958136a10f19733c4ce12 upstream. This stops code that handles widgets generically from attempting to access registers for these widgets. Signed-off-by: Mark Brown Acked-by: Liam Girdwood Signed-off-by: Greg Kroah-Hartman --- include/sound/soc-dapm.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 8031769ac4858..60f94fbd3a10d 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -45,25 +45,25 @@ /* platform domain */ #define SND_SOC_DAPM_INPUT(wname) \ { .id = snd_soc_dapm_input, .name = wname, .kcontrols = NULL, \ - .num_kcontrols = 0} + .num_kcontrols = 0, .reg = SND_SOC_NOPM } #define SND_SOC_DAPM_OUTPUT(wname) \ { .id = snd_soc_dapm_output, .name = wname, .kcontrols = NULL, \ - .num_kcontrols = 0} + .num_kcontrols = 0, .reg = SND_SOC_NOPM } #define SND_SOC_DAPM_MIC(wname, wevent) \ { .id = snd_soc_dapm_mic, .name = wname, .kcontrols = NULL, \ - .num_kcontrols = 0, .event = wevent, \ + .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD} #define SND_SOC_DAPM_HP(wname, wevent) \ { .id = snd_soc_dapm_hp, .name = wname, .kcontrols = NULL, \ - .num_kcontrols = 0, .event = wevent, \ + .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} #define SND_SOC_DAPM_SPK(wname, wevent) \ { .id = snd_soc_dapm_spk, .name = wname, .kcontrols = NULL, \ - .num_kcontrols = 0, .event = wevent, \ + .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} #define SND_SOC_DAPM_LINE(wname, wevent) \ { .id = snd_soc_dapm_line, .name = wname, .kcontrols = NULL, \ - .num_kcontrols = 0, .event = wevent, \ + .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} /* path domain */ @@ -177,11 +177,11 @@ /* events that are pre and post DAPM */ #define SND_SOC_DAPM_PRE(wname, wevent) \ { .id = snd_soc_dapm_pre, .name = wname, .kcontrols = NULL, \ - .num_kcontrols = 0, .event = wevent, \ + .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD} #define SND_SOC_DAPM_POST(wname, wevent) \ { .id = snd_soc_dapm_post, .name = wname, .kcontrols = NULL, \ - .num_kcontrols = 0, .event = wevent, \ + .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD} /* stream domain */ From 7a22bbd822a8188c9b202668b9656b35bf35c3e0 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 25 Mar 2011 16:51:44 +0100 Subject: [PATCH 1196/2556] ASoC: imx: set watermarks for mx2-dma commit 2c4cf17a52f04fbe929977252d5b8ab81d2c6e9b upstream. They got accidently removed by f0fba2a (ASoC: multi-component - ASoC Multi-Component Support). Reintroduce them and get rid of the superfluous defines because the fiq-driver has its own hardcoded values. Signed-off-by: Wolfram Sang Acked-by: Liam Girdwood Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/imx/imx-pcm-dma-mx2.c | 5 +++++ sound/soc/imx/imx-ssi.h | 3 --- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c index 671ef8dd524cb..b2ed764fd8962 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/imx/imx-pcm-dma-mx2.c @@ -303,6 +303,11 @@ static struct snd_soc_platform_driver imx_soc_platform_mx2 = { static int __devinit imx_soc_platform_probe(struct platform_device *pdev) { + struct imx_ssi *ssi = platform_get_drvdata(pdev); + + ssi->dma_params_tx.burstsize = 6; + ssi->dma_params_rx.burstsize = 4; + return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2); } diff --git a/sound/soc/imx/imx-ssi.h b/sound/soc/imx/imx-ssi.h index a4406a1348927..dc8a87530e3e9 100644 --- a/sound/soc/imx/imx-ssi.h +++ b/sound/soc/imx/imx-ssi.h @@ -234,7 +234,4 @@ void imx_pcm_free(struct snd_pcm *pcm); */ #define IMX_SSI_DMABUF_SIZE (64 * 1024) -#define DMA_RXFIFO_BURST 0x4 -#define DMA_TXFIFO_BURST 0x6 - #endif /* _IMX_SSI_H */ From 16227b2ce1c3249cfe5c131508fc81bbd069302f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 25 Mar 2011 16:51:45 +0100 Subject: [PATCH 1197/2556] ASoC: imx: fix burstsize for DMA commit e1bb31b444668bc957c337d33803db7cb3330745 upstream. SSI counts in words, the DMA engine in bytes. (Wrong) factor got removed in bf974a0 (ASoC i.MX: switch to new DMA api). Signed-off-by: Wolfram Sang Acked-by: Liam Girdwood Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/imx/imx-pcm-dma-mx2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c index b2ed764fd8962..aab7765f401a0 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/imx/imx-pcm-dma-mx2.c @@ -110,12 +110,12 @@ static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream, slave_config.direction = DMA_TO_DEVICE; slave_config.dst_addr = dma_params->dma_addr; slave_config.dst_addr_width = buswidth; - slave_config.dst_maxburst = dma_params->burstsize; + slave_config.dst_maxburst = dma_params->burstsize * buswidth; } else { slave_config.direction = DMA_FROM_DEVICE; slave_config.src_addr = dma_params->dma_addr; slave_config.src_addr_width = buswidth; - slave_config.src_maxburst = dma_params->burstsize; + slave_config.src_maxburst = dma_params->burstsize * buswidth; } ret = dmaengine_slave_config(iprtd->dma_chan, &slave_config); From d4bc12aa1a6e58512753c9bd71ee0d872fad8bb2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 27 Mar 2011 14:40:01 +0100 Subject: [PATCH 1198/2556] ASoC: Fix CODEC device name for Corgi commit 326b9bdc2a0e4d556a0f444085dca103bcd505de upstream. Got typoed in the multi-component changes. Signed-off-by: Mark Brown Acked-by: Liam Girdwood Signed-off-by: Greg Kroah-Hartman --- sound/soc/pxa/corgi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index 784cff5f67e81..9027da466caea 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -310,7 +310,7 @@ static struct snd_soc_dai_link corgi_dai = { .cpu_dai_name = "pxa2xx-i2s", .codec_dai_name = "wm8731-hifi", .platform_name = "pxa-pcm-audio", - .codec_name = "wm8731-codec-0.001b", + .codec_name = "wm8731-codec.0-001b", .init = corgi_wm8731_init, .ops = &corgi_ops, }; From 0ec480bf09836703cf91cd8287b584d8a46d277e Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Wed, 30 Mar 2011 08:24:25 +0200 Subject: [PATCH 1199/2556] ALSA: ens1371: fix Creative Ectiva support commit 6ebb8a4a43e34f999ab36f27f972f3cd751cda4f upstream. To make the EV1938 chip work, add a magic bit and an extra delay. Signed-off-by: Clemens Ladisch Tested-by: Tino Schmidt Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/ens1370.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 537cfba829a58..863eafea691f2 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -229,6 +229,7 @@ MODULE_PARM_DESC(lineio, "Line In to Rear Out (0 = auto, 1 = force)."); #define ES_REG_1371_CODEC 0x14 /* W/R: Codec Read/Write register address */ #define ES_1371_CODEC_RDY (1<<31) /* codec ready */ #define ES_1371_CODEC_WIP (1<<30) /* codec register access in progress */ +#define EV_1938_CODEC_MAGIC (1<<26) #define ES_1371_CODEC_PIRD (1<<23) /* codec read/write select register */ #define ES_1371_CODEC_WRITE(a,d) ((((a)&0x7f)<<16)|(((d)&0xffff)<<0)) #define ES_1371_CODEC_READS(a) ((((a)&0x7f)<<16)|ES_1371_CODEC_PIRD) @@ -603,12 +604,18 @@ static void snd_es1370_codec_write(struct snd_ak4531 *ak4531, #ifdef CHIP1371 +static inline bool is_ev1938(struct ensoniq *ensoniq) +{ + return ensoniq->pci->device == 0x8938; +} + static void snd_es1371_codec_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) { struct ensoniq *ensoniq = ac97->private_data; - unsigned int t, x; + unsigned int t, x, flag; + flag = is_ev1938(ensoniq) ? EV_1938_CODEC_MAGIC : 0; mutex_lock(&ensoniq->src_mutex); for (t = 0; t < POLL_COUNT; t++) { if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) { @@ -630,7 +637,8 @@ static void snd_es1371_codec_write(struct snd_ac97 *ac97, 0x00010000) break; } - outl(ES_1371_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1371_CODEC)); + outl(ES_1371_CODEC_WRITE(reg, val) | flag, + ES_REG(ensoniq, 1371_CODEC)); /* restore SRC reg */ snd_es1371_wait_src_ready(ensoniq); outl(x, ES_REG(ensoniq, 1371_SMPRATE)); @@ -647,8 +655,9 @@ static unsigned short snd_es1371_codec_read(struct snd_ac97 *ac97, unsigned short reg) { struct ensoniq *ensoniq = ac97->private_data; - unsigned int t, x, fail = 0; + unsigned int t, x, flag, fail = 0; + flag = is_ev1938(ensoniq) ? EV_1938_CODEC_MAGIC : 0; __again: mutex_lock(&ensoniq->src_mutex); for (t = 0; t < POLL_COUNT; t++) { @@ -671,7 +680,8 @@ static unsigned short snd_es1371_codec_read(struct snd_ac97 *ac97, 0x00010000) break; } - outl(ES_1371_CODEC_READS(reg), ES_REG(ensoniq, 1371_CODEC)); + outl(ES_1371_CODEC_READS(reg) | flag, + ES_REG(ensoniq, 1371_CODEC)); /* restore SRC reg */ snd_es1371_wait_src_ready(ensoniq); outl(x, ES_REG(ensoniq, 1371_SMPRATE)); @@ -683,6 +693,11 @@ static unsigned short snd_es1371_codec_read(struct snd_ac97 *ac97, /* now wait for the stinkin' data (RDY) */ for (t = 0; t < POLL_COUNT; t++) { if ((x = inl(ES_REG(ensoniq, 1371_CODEC))) & ES_1371_CODEC_RDY) { + if (is_ev1938(ensoniq)) { + for (t = 0; t < 100; t++) + inl(ES_REG(ensoniq, CONTROL)); + x = inl(ES_REG(ensoniq, 1371_CODEC)); + } mutex_unlock(&ensoniq->src_mutex); return ES_1371_CODEC_READ(x); } From 978326ec44998633b838c89ebd4b1856991f33a8 Mon Sep 17 00:00:00 2001 From: Kelly Anderson Date: Fri, 1 Apr 2011 11:58:25 +0200 Subject: [PATCH 1200/2556] ALSA: pcm: fix infinite loop in snd_pcm_update_hw_ptr0() commit 12ff414e2e4512f59fe191dc18e856e2939a1c79 upstream. When period interrupts are disabled, snd_pcm_update_hw_ptr0() compares the current time against the time estimated for the current hardware pointer to detect xruns. The somewhat fuzzy threshold in the while loop makes it possible that hdelta becomes negative; the comparison being done with unsigned types then makes the loop go through the entire 263 negative range, and, depending on the value, never reach an unsigned value that is small enough to stop the loop. Doing this with interrupts disabled results in the machine locking up. To prevent this, ensure that the loop condition uses signed types for both operands so that the comparison is correctly done. Many thanks to Kelly Anderson for debugging this. Reported-by: Nix Reported-by: "Christopher K." Reported-and-tested-by: Kelly Anderson Signed-off-by: Kelly Anderson [cl: remove unneeded casts; use a temp variable] Signed-off-by: Clemens Ladisch Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/pcm_lib.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index a82e3756a72d8..64449cb8f8737 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -375,6 +375,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, } if (runtime->no_period_wakeup) { + snd_pcm_sframes_t xrun_threshold; /* * Without regular period interrupts, we have to check * the elapsed time to detect xruns. @@ -383,7 +384,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, if (jdelta < runtime->hw_ptr_buffer_jiffies / 2) goto no_delta_check; hdelta = jdelta - delta * HZ / runtime->rate; - while (hdelta > runtime->hw_ptr_buffer_jiffies / 2 + 1) { + xrun_threshold = runtime->hw_ptr_buffer_jiffies / 2 + 1; + while (hdelta > xrun_threshold) { delta += runtime->buffer_size; hw_base += runtime->buffer_size; if (hw_base >= runtime->boundary) From 69aa0b17a3ba3ceede0b8d899f8b2634534d3cf3 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Thu, 31 Mar 2011 09:36:19 +0200 Subject: [PATCH 1201/2556] ALSA: HDA: Add dock mic quirk for Lenovo Thinkpad X220 commit 840126579da56edae8ecc4a0d85198f742982f10 upstream. This quirk is needed for the docking station mic of Lenovo Thinkpad X220 to function correctly. BugLink: http://bugs.launchpad.net/bugs/746259 Tested-by: James Ferguson Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_conexant.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 4d5004e693f03..158a4236760fa 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -3130,6 +3130,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS), SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS), SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT5066_IDEAPAD), /* Fallback for Lenovos without dock mic */ {} From be96cfa628fd1dfd45f46816f2626311056ab7e7 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 5 Apr 2011 07:55:24 +0200 Subject: [PATCH 1202/2556] ALSA: HDA: Fix dock mic for Lenovo X220-tablet commit b2cb1292b1c7c73abbdc0e07ef3aab056fc2615f upstream. Without the "thinkpad" quirk, the dock mic in Lenovo X220 tablet edition won't work. BugLink: http://bugs.launchpad.net/bugs/751033 Tested-by: James Ferguson Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_conexant.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 158a4236760fa..e33d69eea7989 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -3131,6 +3131,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS), SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT5066_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS), SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT5066_IDEAPAD), /* Fallback for Lenovos without dock mic */ {} From acc2941919fcba048ee6bdc04c86de1ae31100c4 Mon Sep 17 00:00:00 2001 From: Aaron Plattner Date: Wed, 6 Apr 2011 17:19:04 -0700 Subject: [PATCH 1203/2556] ALSA: hda - HDMI: Fix MCP7x audio infoframe checksums commit 1f348522844bb1f6e7b10d50b9e8aa89a2511b09 upstream. The MCP7x hardware computes the audio infoframe channel count automatically, but requires the audio driver to set the audio infoframe checksum manually via the Nv_VERB_SET_Info_Frame_Checksum control verb. When audio starts playing, nvhdmi_8ch_7x_pcm_prepare sets the checksum to (0x71 - chan - chanmask). For example, for 2ch audio, chan == 1 and chanmask == 0 so the checksum is set to 0x70. When audio playback finishes and the device is closed, nvhdmi_8ch_7x_pcm_close resets the channel formats, causing the channel count to revert to 8ch. Since the checksum is not reset, the hardware starts generating audio infoframes with invalid checksums. This causes some displays to blank the video. Fix this by updating the checksum and channel mask when the device is closed and also when it is first initialized. In addition, make sure that the channel mask is appropriate for an 8ch infoframe by setting it to 0x13 (FL FR LFE FC RL RR RLC RRC). Signed-off-by: Aaron Plattner Acked-by: Stephen Warren Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_hdmi.c | 70 ++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index ec0fa2dd0a279..520f94a411658 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1276,6 +1276,39 @@ static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo, stream_tag, format, substream); } +static void nvhdmi_8ch_7x_set_info_frame_parameters(struct hda_codec *codec, + int channels) +{ + unsigned int chanmask; + int chan = channels ? (channels - 1) : 1; + + switch (channels) { + default: + case 0: + case 2: + chanmask = 0x00; + break; + case 4: + chanmask = 0x08; + break; + case 6: + chanmask = 0x0b; + break; + case 8: + chanmask = 0x13; + break; + } + + /* Set the audio infoframe channel allocation and checksum fields. The + * channel count is computed implicitly by the hardware. */ + snd_hda_codec_write(codec, 0x1, 0, + Nv_VERB_SET_Channel_Allocation, chanmask); + + snd_hda_codec_write(codec, 0x1, 0, + Nv_VERB_SET_Info_Frame_Checksum, + (0x71 - chan - chanmask)); +} + static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) @@ -1294,6 +1327,10 @@ static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo, AC_VERB_SET_STREAM_FORMAT, 0); } + /* The audio hardware sends a channel count of 0x7 (8ch) when all the + * streams are disabled. */ + nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8); + return snd_hda_multi_out_dig_close(codec, &spec->multiout); } @@ -1304,37 +1341,16 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { int chs; - unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id; + unsigned int dataDCC1, dataDCC2, channel_id; int i; mutex_lock(&codec->spdif_mutex); chs = substream->runtime->channels; - chan = chs ? (chs - 1) : 1; - switch (chs) { - default: - case 0: - case 2: - chanmask = 0x00; - break; - case 4: - chanmask = 0x08; - break; - case 6: - chanmask = 0x0b; - break; - case 8: - chanmask = 0x13; - break; - } dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT; dataDCC2 = 0x2; - /* set the Audio InforFrame Channel Allocation */ - snd_hda_codec_write(codec, 0x1, 0, - Nv_VERB_SET_Channel_Allocation, chanmask); - /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) snd_hda_codec_write(codec, @@ -1409,10 +1425,7 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo, } } - /* set the Audio Info Frame Checksum */ - snd_hda_codec_write(codec, 0x1, 0, - Nv_VERB_SET_Info_Frame_Checksum, - (0x71 - chan - chanmask)); + nvhdmi_8ch_7x_set_info_frame_parameters(codec, chs); mutex_unlock(&codec->spdif_mutex); return 0; @@ -1508,6 +1521,11 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) spec->multiout.max_channels = 8; spec->pcm_playback = &nvhdmi_pcm_playback_8ch_7x; codec->patch_ops = nvhdmi_patch_ops_8ch_7x; + + /* Initialize the audio infoframe channel mask and checksum to something + * valid */ + nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8); + return 0; } From b10e05728db184deaab555b9978fb8b7cb745158 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Thu, 7 Apr 2011 11:43:00 +0200 Subject: [PATCH 1204/2556] ALSA: HDA: Fix single internal mic on ALC275 (Sony Vaio VPCSB1C5E) commit 262ac22d21ee2bf3e1655b2e5e45cc94b356e62f upstream. In cases where there is only one internal mic connected to ADC 0x11, alc275_setup_dual_adc won't handle the case, so we need to add the ADC node to the array of candidates. BugLink: http://bugs.launchpad.net/bugs/752792 Reported-by: Vincenzo Pii Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d21ce8bc919bc..e164a4bdf48f5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -14191,7 +14191,7 @@ static hda_nid_t alc269vb_capsrc_nids[1] = { }; static hda_nid_t alc269_adc_candidates[] = { - 0x08, 0x09, 0x07, + 0x08, 0x09, 0x07, 0x11, }; #define alc269_modes alc260_modes From 2a985af04eb094217218df35f877fca0e3c02298 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Tue, 22 Mar 2011 23:54:49 +0000 Subject: [PATCH 1205/2556] net: fix ethtool->set_flags not intended -EINVAL return value commit 673e63c688f43104c73aad8ea4237f7ad41fa14d upstream. After commit d5dbda23804156ae6f35025ade5307a49d1db6d7 "ethtool: Add support for vlan accleration.", drivers that have NETIF_F_HW_VLAN_TX, and/or NETIF_F_HW_VLAN_RX feature, but do not allow enable/disable vlan acceleration via ethtool set_flags, always return -EINVAL from that function. Fix by returning -EINVAL only if requested features do not match current settings and can not be changed by driver. Change any driver that define ethtool->set_flags to use ethtool_invalid_flags() to avoid similar problems in the future (also on drivers that do not have the problem). Tested with modified (to reproduce this bug) myri10ge driver. Signed-off-by: Stanislaw Gruszka Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/netxen/netxen_nic_ethtool.c | 2 +- drivers/net/qlcnic/qlcnic_ethtool.c | 2 +- drivers/net/s2io.c | 2 +- drivers/net/vmxnet3/vmxnet3_ethtool.c | 4 ++-- drivers/net/vxge/vxge-ethtool.c | 4 ++-- include/linux/ethtool.h | 1 + net/core/ethtool.c | 17 ++++++++++++++++- 7 files changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/net/netxen/netxen_nic_ethtool.c b/drivers/net/netxen/netxen_nic_ethtool.c index 587498e140bb8..3de98cb002ed5 100644 --- a/drivers/net/netxen/netxen_nic_ethtool.c +++ b/drivers/net/netxen/netxen_nic_ethtool.c @@ -901,7 +901,7 @@ static int netxen_nic_set_flags(struct net_device *netdev, u32 data) struct netxen_adapter *adapter = netdev_priv(netdev); int hw_lro; - if (data & ~ETH_FLAG_LRO) + if (ethtool_invalid_flags(netdev, data, ETH_FLAG_LRO)) return -EINVAL; if (!(adapter->capabilities & NX_FW_CAPABILITY_HW_LRO)) diff --git a/drivers/net/qlcnic/qlcnic_ethtool.c b/drivers/net/qlcnic/qlcnic_ethtool.c index 4c14510e2a87e..45b2755d6cba3 100644 --- a/drivers/net/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/qlcnic/qlcnic_ethtool.c @@ -1003,7 +1003,7 @@ static int qlcnic_set_flags(struct net_device *netdev, u32 data) struct qlcnic_adapter *adapter = netdev_priv(netdev); int hw_lro; - if (data & ~ETH_FLAG_LRO) + if (ethtool_invalid_flags(netdev, data, ETH_FLAG_LRO)) return -EINVAL; if (!(adapter->capabilities & QLCNIC_FW_CAPABILITY_HW_LRO)) diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index 39c17cecb8b98..0cdff2baaa359 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -6726,7 +6726,7 @@ static int s2io_ethtool_set_flags(struct net_device *dev, u32 data) int rc = 0; int changed = 0; - if (data & ~ETH_FLAG_LRO) + if (ethtool_invalid_flags(dev, data, ETH_FLAG_LRO)) return -EINVAL; if (data & ETH_FLAG_LRO) { diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c index 81254be85b926..51f2ef142a5b6 100644 --- a/drivers/net/vmxnet3/vmxnet3_ethtool.c +++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c @@ -304,8 +304,8 @@ vmxnet3_set_flags(struct net_device *netdev, u32 data) u8 lro_present = (netdev->features & NETIF_F_LRO) == 0 ? 0 : 1; unsigned long flags; - if (data & ~ETH_FLAG_LRO) - return -EOPNOTSUPP; + if (ethtool_invalid_flags(netdev, data, ETH_FLAG_LRO)) + return -EINVAL; if (lro_requested ^ lro_present) { /* toggle the LRO feature*/ diff --git a/drivers/net/vxge/vxge-ethtool.c b/drivers/net/vxge/vxge-ethtool.c index 1dd3a21b3a436..c5eb034107fdd 100644 --- a/drivers/net/vxge/vxge-ethtool.c +++ b/drivers/net/vxge/vxge-ethtool.c @@ -1117,8 +1117,8 @@ static int vxge_set_flags(struct net_device *dev, u32 data) struct vxgedev *vdev = netdev_priv(dev); enum vxge_hw_status status; - if (data & ~ETH_FLAG_RXHASH) - return -EOPNOTSUPP; + if (ethtool_invalid_flags(dev, data, ETH_FLAG_RXHASH)) + return -EINVAL; if (!!(data & ETH_FLAG_RXHASH) == vdev->devh->config.rth_en) return 0; diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index a3c1874171c1c..a04b6cee1cb43 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -591,6 +591,7 @@ int ethtool_op_set_ufo(struct net_device *dev, u32 data); u32 ethtool_op_get_flags(struct net_device *dev); int ethtool_op_set_flags(struct net_device *dev, u32 data, u32 supported); void ethtool_ntuple_flush(struct net_device *dev); +bool ethtool_invalid_flags(struct net_device *dev, u32 data, u32 supported); /** * ðtool_ops - Alter and report network device settings diff --git a/net/core/ethtool.c b/net/core/ethtool.c index ff2302910b5eb..6c7c610866db1 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -146,9 +146,24 @@ u32 ethtool_op_get_flags(struct net_device *dev) } EXPORT_SYMBOL(ethtool_op_get_flags); +/* Check if device can enable (or disable) particular feature coded in "data" + * argument. Flags "supported" describe features that can be toggled by device. + * If feature can not be toggled, it state (enabled or disabled) must match + * hardcoded device features state, otherwise flags are marked as invalid. + */ +bool ethtool_invalid_flags(struct net_device *dev, u32 data, u32 supported) +{ + u32 features = dev->features & flags_dup_features; + /* "data" can contain only flags_dup_features bits, + * see __ethtool_set_flags */ + + return (features & ~supported) != (data & ~supported); +} +EXPORT_SYMBOL(ethtool_invalid_flags); + int ethtool_op_set_flags(struct net_device *dev, u32 data, u32 supported) { - if (data & ~supported) + if (ethtool_invalid_flags(dev, data, supported)) return -EINVAL; dev->features = ((dev->features & ~flags_dup_features) | From 4e3465377f3e956dd7dcaa994911d59971fb185d Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 4 Apr 2011 11:03:16 -0400 Subject: [PATCH 1206/2556] drm/radeon/kms: add some new ontario pci ids commit 758f231ea280d0e5f01d537f26ad8f5c0e3de1cc upstream. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- include/drm/drm_pciids.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index 5ff1194dc2ea1..6724bf3c1ff11 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -458,6 +458,8 @@ {0x1002, 0x9803, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x9804, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x9805, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9806, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9807, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0, 0, 0} #define r128_PCI_IDS \ From 315b809a6e4a3635729cc2db1c1838098b411991 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Thu, 24 Mar 2011 23:28:31 +0000 Subject: [PATCH 1207/2556] drm/radeon/kms: add some sanity checks to obj info record parsingi (v2) commit 97ea530f6fac1f9632b0c4792a2a56411454adbe upstream. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=35502 agd5f: also add sanity check to connector records. v2: fix one more case. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_atombios.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 02d5c415f4993..99768d9d91dac 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -675,7 +675,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) ATOM_ENCODER_CAP_RECORD *cap_record; u16 caps = 0; - while (record->ucRecordType > 0 && + while (record->ucRecordSize > 0 && + record->ucRecordType > 0 && record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { switch (record->ucRecordType) { case ATOM_ENCODER_CAP_RECORD_TYPE: @@ -720,7 +721,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) break; } - while (record->ucRecordType > 0 && + while (record->ucRecordSize > 0 && + record->ucRecordType > 0 && record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { switch (record->ucRecordType) { case ATOM_I2C_RECORD_TYPE: @@ -782,10 +784,9 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) ATOM_HPD_INT_RECORD *hpd_record; ATOM_I2C_ID_CONFIG_ACCESS *i2c_config; - while (record->ucRecordType > 0 - && record-> - ucRecordType <= - ATOM_MAX_OBJECT_RECORD_NUMBER) { + while (record->ucRecordSize > 0 && + record->ucRecordType > 0 && + record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { switch (record->ucRecordType) { case ATOM_I2C_RECORD_TYPE: i2c_record = From 777f709a6cbad9876eb0f10ce6aa63590cc3dadb Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 5 Apr 2011 17:20:50 -0400 Subject: [PATCH 1208/2556] inotify: fix double free/corruption of stuct user commit d0de4dc584ec6aa3b26fffea320a8457827768fc upstream. On an error path in inotify_init1 a normal user can trigger a double free of struct user. This is a regression introduced by a2ae4cc9a16e ("inotify: stop kernel memory leak on file creation failure"). We fix this by making sure that if a group exists the user reference is dropped when the group is cleaned up. We should not explictly drop the reference on error and also drop the reference when the group is cleaned up. The new lifetime rules are that an inotify group lives from inotify_new_group to the last fsnotify_put_group. Since the struct user and inotify_devs are directly tied to this lifetime they are only changed/updated in those two locations. We get rid of all special casing of struct user or user->inotify_devs. Signed-off-by: Eric Paris Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/notify/inotify/inotify_fsnotify.c | 1 + fs/notify/inotify/inotify_user.c | 39 ++++++++++------------------ 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index a91b69a6a291b..0348d0c8f65e8 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -198,6 +198,7 @@ static void inotify_free_group_priv(struct fsnotify_group *group) idr_for_each(&group->inotify_data.idr, idr_callback, group); idr_remove_all(&group->inotify_data.idr); idr_destroy(&group->inotify_data.idr); + atomic_dec(&group->inotify_data.user->inotify_devs); free_uid(group->inotify_data.user); } diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 4cd5d5d78f9fa..aec9b4a4ed112 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -290,7 +290,6 @@ static int inotify_fasync(int fd, struct file *file, int on) static int inotify_release(struct inode *ignored, struct file *file) { struct fsnotify_group *group = file->private_data; - struct user_struct *user = group->inotify_data.user; pr_debug("%s: group=%p\n", __func__, group); @@ -299,8 +298,6 @@ static int inotify_release(struct inode *ignored, struct file *file) /* free this group, matching get was inotify_init->fsnotify_obtain_group */ fsnotify_put_group(group); - atomic_dec(&user->inotify_devs); - return 0; } @@ -697,7 +694,7 @@ static int inotify_update_watch(struct fsnotify_group *group, struct inode *inod return ret; } -static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsigned int max_events) +static struct fsnotify_group *inotify_new_group(unsigned int max_events) { struct fsnotify_group *group; @@ -710,8 +707,14 @@ static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsign spin_lock_init(&group->inotify_data.idr_lock); idr_init(&group->inotify_data.idr); group->inotify_data.last_wd = 0; - group->inotify_data.user = user; group->inotify_data.fa = NULL; + group->inotify_data.user = get_current_user(); + + if (atomic_inc_return(&group->inotify_data.user->inotify_devs) > + inotify_max_user_instances) { + fsnotify_put_group(group); + return ERR_PTR(-EMFILE); + } return group; } @@ -721,7 +724,6 @@ static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsign SYSCALL_DEFINE1(inotify_init1, int, flags) { struct fsnotify_group *group; - struct user_struct *user; int ret; /* Check the IN_* constants for consistency. */ @@ -731,31 +733,16 @@ SYSCALL_DEFINE1(inotify_init1, int, flags) if (flags & ~(IN_CLOEXEC | IN_NONBLOCK)) return -EINVAL; - user = get_current_user(); - if (unlikely(atomic_read(&user->inotify_devs) >= - inotify_max_user_instances)) { - ret = -EMFILE; - goto out_free_uid; - } - /* fsnotify_obtain_group took a reference to group, we put this when we kill the file in the end */ - group = inotify_new_group(user, inotify_max_queued_events); - if (IS_ERR(group)) { - ret = PTR_ERR(group); - goto out_free_uid; - } - - atomic_inc(&user->inotify_devs); + group = inotify_new_group(inotify_max_queued_events); + if (IS_ERR(group)) + return PTR_ERR(group); ret = anon_inode_getfd("inotify", &inotify_fops, group, O_RDONLY | flags); - if (ret >= 0) - return ret; + if (ret < 0) + fsnotify_put_group(group); - fsnotify_put_group(group); - atomic_dec(&user->inotify_devs); -out_free_uid: - free_uid(user); return ret; } From 032e8e78e2cada06d110c53612f386e4055ff79e Mon Sep 17 00:00:00 2001 From: Chase Douglas Date: Fri, 1 Apr 2011 17:03:39 -0400 Subject: [PATCH 1209/2556] HID: hid-magicmouse: Increase evdev buffer size commit cc5e0f08ca2a66fc4c6984ccff74fd529e969fac upstream. The evdev buffer isn't big enough when you get many fingers on the device. Bump up the buffer to a reasonable size, matching what other multitouch devices use. Without this change, events may be discarded in the evdev buffer before they are read. Reported-by: Simon Budig Cc: Henrik Rydberg Cc: Jiri Kosina Signed-off-by: Chase Douglas Acked-by: Henrik Rydberg Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-magicmouse.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 318cc40df92d6..418c399d3ef77 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -418,6 +418,8 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h input_set_abs_params(input, ABS_MT_POSITION_Y, -2456, 2565, 4, 0); } + + input_set_events_per_packet(input, 60); } if (report_undeciphered) { From ca6c2f5352c08c9ddc7e399584bdfb3ff6589204 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 28 Mar 2011 13:13:56 +0200 Subject: [PATCH 1210/2556] perf: Fix task_struct reference leak commit fd1edb3aa2c1d92618d8f0c6d15d44ea41fcac6a upstream. sys_perf_event_open() had an imbalance in the number of task refs it took causing memory leakage Cc: Jiri Olsa Cc: Oleg Nesterov Signed-off-by: Peter Zijlstra LKML-Reference: Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/perf_event.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/perf_event.c b/kernel/perf_event.c index f81f9d6395ad0..07fcc8568fac8 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -5917,6 +5917,11 @@ SYSCALL_DEFINE5(perf_event_open, goto err_alloc; } + if (task) { + put_task_struct(task); + task = NULL; + } + /* * Look up the group leader (we will attach this event to it): */ From 70ce4c5e3b001ac8fe10b069f79951520b6089a8 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 31 Mar 2011 03:33:29 +0200 Subject: [PATCH 1211/2556] perf: Rebase max unprivileged mlock threshold on top of page size commit 20443384fe090c5f8aeb016e7e85659c5bbdd69f upstream. Ensure we allow 512 kiB + 1 page for user control without assuming a 4096 bytes page size. Reported-by: Peter Zijlstra Signed-off-by: Frederic Weisbecker Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Stephane Eranian LKML-Reference: <1301535209-9679-1-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/perf_event.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 07fcc8568fac8..b2536bd2b6b53 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -62,8 +62,8 @@ static struct srcu_struct pmus_srcu; */ int sysctl_perf_event_paranoid __read_mostly = 1; -/* Minimum for 128 pages + 1 for the user control page */ -int sysctl_perf_event_mlock __read_mostly = 516; /* 'free' kb per user */ +/* Minimum for 512 kiB + 1 user control page */ +int sysctl_perf_event_mlock __read_mostly = 512 + (PAGE_SIZE / 1024); /* 'free' kiB per user */ /* * max perf event sample rate From 6615b496b85cd0a05eb757651edf244192511bc0 Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Sat, 19 Mar 2011 20:43:43 +0000 Subject: [PATCH 1212/2556] ROSE: prevent heap corruption with bad facilities commit be20250c13f88375345ad99950190685eda51eb8 upstream. When parsing the FAC_NATIONAL_DIGIS facilities field, it's possible for a remote host to provide more digipeaters than expected, resulting in heap corruption. Check against ROSE_MAX_DIGIS to prevent overflows, and abort facilities parsing on failure. Additionally, when parsing the FAC_CCITT_DEST_NSAP and FAC_CCITT_SRC_NSAP facilities fields, a remote host can provide a length of less than 10, resulting in an underflow in a memcpy size, causing a kernel panic due to massive heap corruption. A length of greater than 20 results in a stack overflow of the callsign array. Abort facilities parsing on these invalid length values. Signed-off-by: Dan Rosenberg Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/rose/rose_subr.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/net/rose/rose_subr.c b/net/rose/rose_subr.c index 1734abba26a29..174d51c9ce377 100644 --- a/net/rose/rose_subr.c +++ b/net/rose/rose_subr.c @@ -290,10 +290,15 @@ static int rose_parse_national(unsigned char *p, struct rose_facilities_struct * facilities->source_ndigis = 0; facilities->dest_ndigis = 0; for (pt = p + 2, lg = 0 ; lg < l ; pt += AX25_ADDR_LEN, lg += AX25_ADDR_LEN) { - if (pt[6] & AX25_HBIT) + if (pt[6] & AX25_HBIT) { + if (facilities->dest_ndigis >= ROSE_MAX_DIGIS) + return -1; memcpy(&facilities->dest_digis[facilities->dest_ndigis++], pt, AX25_ADDR_LEN); - else + } else { + if (facilities->source_ndigis >= ROSE_MAX_DIGIS) + return -1; memcpy(&facilities->source_digis[facilities->source_ndigis++], pt, AX25_ADDR_LEN); + } } } p += l + 2; @@ -333,6 +338,11 @@ static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *fac case 0xC0: l = p[1]; + + /* Prevent overflows*/ + if (l < 10 || l > 20) + return -1; + if (*p == FAC_CCITT_DEST_NSAP) { memcpy(&facilities->source_addr, p + 7, ROSE_ADDR_LEN); memcpy(callsign, p + 12, l - 10); @@ -373,12 +383,16 @@ int rose_parse_facilities(unsigned char *p, switch (*p) { case FAC_NATIONAL: /* National */ len = rose_parse_national(p + 1, facilities, facilities_len - 1); + if (len < 0) + return 0; facilities_len -= len + 1; p += len + 1; break; case FAC_CCITT: /* CCITT */ len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1); + if (len < 0) + return 0; facilities_len -= len + 1; p += len + 1; break; From aa6b6bda1fcae89d7b678c4d401a39140871cfba Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Mon, 28 Mar 2011 02:01:25 +0000 Subject: [PATCH 1213/2556] Btrfs: Fix uninitialized root flags for subvolumes commit 08fe4db170b4193603d9d31f40ebaf652d07ac9c upstream. root_item->flags and root_item->byte_limit are not initialized when a subvolume is created. This bug is not revealed until we added readonly snapshot support - now you mount a btrfs filesystem and you may find the subvolumes in it are readonly. To work around this problem, we steal a bit from root_item->inode_item->flags, and use it to indicate if those fields have been properly initialized. When we read a tree root from disk, we check if the bit is set, and if not we'll set the flag and initialize the two fields of the root item. Reported-by: Andreas Philipp Signed-off-by: Li Zefan Tested-by: Andreas Philipp Signed-off-by: Chris Mason Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/ctree.h | 4 ++++ fs/btrfs/disk-io.c | 4 +++- fs/btrfs/ioctl.c | 4 ++++ fs/btrfs/root-tree.c | 18 ++++++++++++++++++ fs/btrfs/transaction.c | 1 + 5 files changed, 30 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 7f78cc78fdd0a..bd64b4101f5f1 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1284,6 +1284,8 @@ struct btrfs_root { #define BTRFS_INODE_NOATIME (1 << 9) #define BTRFS_INODE_DIRSYNC (1 << 10) +#define BTRFS_INODE_ROOT_ITEM_INIT (1 << 31) + /* some macros to generate set/get funcs for the struct fields. This * assumes there is a lefoo_to_cpu for every type, so lets make a simple * one for u8: @@ -2355,6 +2357,8 @@ int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid); int btrfs_find_orphan_roots(struct btrfs_root *tree_root); int btrfs_set_root_node(struct btrfs_root_item *item, struct extent_buffer *node); +void btrfs_check_and_init_root_item(struct btrfs_root_item *item); + /* dir-item.c */ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, const char *name, diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index e1aa8d607bc7d..edd9efa515675 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1184,8 +1184,10 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root, root->commit_root = btrfs_root_node(root); BUG_ON(!root->node); out: - if (location->objectid != BTRFS_TREE_LOG_OBJECTID) + if (location->objectid != BTRFS_TREE_LOG_OBJECTID) { root->ref_cows = 1; + btrfs_check_and_init_root_item(&root->root_item); + } return root; } diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 5fdb2abc4fa78..2ff51e69dea83 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -294,6 +294,10 @@ static noinline int create_subvol(struct btrfs_root *root, inode_item->nbytes = cpu_to_le64(root->leafsize); inode_item->mode = cpu_to_le32(S_IFDIR | 0755); + root_item.flags = 0; + root_item.byte_limit = 0; + inode_item->flags = cpu_to_le64(BTRFS_INODE_ROOT_ITEM_INIT); + btrfs_set_root_bytenr(&root_item, leaf->start); btrfs_set_root_generation(&root_item, trans->transid); btrfs_set_root_level(&root_item, 0); diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c index 6a1086e83ffc8..3e45c3206e7f5 100644 --- a/fs/btrfs/root-tree.c +++ b/fs/btrfs/root-tree.c @@ -471,3 +471,21 @@ int btrfs_add_root_ref(struct btrfs_trans_handle *trans, btrfs_free_path(path); return 0; } + +/* + * Old btrfs forgets to init root_item->flags and root_item->byte_limit + * for subvolumes. To work around this problem, we steal a bit from + * root_item->inode_item->flags, and use it to indicate if those fields + * have been properly initialized. + */ +void btrfs_check_and_init_root_item(struct btrfs_root_item *root_item) +{ + u64 inode_flags = le64_to_cpu(root_item->inode.flags); + + if (!(inode_flags & BTRFS_INODE_ROOT_ITEM_INIT)) { + inode_flags |= BTRFS_INODE_ROOT_ITEM_INIT; + root_item->inode.flags = cpu_to_le64(inode_flags); + root_item->flags = 0; + root_item->byte_limit = 0; + } +} diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 3d73c8d93bbb1..f3d66819025c4 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -970,6 +970,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, record_root_in_trans(trans, root); btrfs_set_root_last_snapshot(&root->root_item, trans->transid); memcpy(new_root_item, &root->root_item, sizeof(*new_root_item)); + btrfs_check_and_init_root_item(new_root_item); root_flags = btrfs_root_flags(new_root_item); if (pending->readonly) From 3c54e20f359e30e683a43edfaf8f97ae62c44b07 Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Tue, 29 Mar 2011 15:38:12 -0700 Subject: [PATCH 1214/2556] x86, mtrr, pat: Fix one cpu getting out of sync during resume commit 84ac7cdbdd0f04df6b96153f7a79127fd6e45467 upstream. On laptops with core i5/i7, there were reports that after resume graphics workloads were performing poorly on a specific AP, while the other cpu's were ok. This was observed on a 32bit kernel specifically. Debug showed that the PAT init was not happening on that AP during resume and hence it contributing to the poor workload performance on that cpu. On this system, resume flow looked like this: 1. BP starts the resume sequence and we reinit BP's MTRR's/PAT early on using mtrr_bp_restore() 2. Resume sequence brings all AP's online 3. Resume sequence now kicks off the MTRR reinit on all the AP's. 4. For some reason, between point 2 and 3, we moved from BP to one of the AP's. My guess is that printk() during resume sequence is contributing to this. We don't see similar behavior with the 64bit kernel but there is no guarantee that at this point the remaining resume sequence (after AP's bringup) has to happen on BP. 5. set_mtrr() was assuming that we are still on BP and skipped the MTRR/PAT init on that cpu (because of 1 above) 6. But we were on an AP and this led to not reprogramming PAT on this cpu leading to bad performance. Fix this by doing unconditional mtrr_if->set_all() in set_mtrr() during MTRR/PAT init. This might be unnecessary if we are still running on BP. But it is of no harm and will guarantee that after resume, all the cpu's will be in sync with respect to the MTRR/PAT registers. Signed-off-by: Suresh Siddha LKML-Reference: <1301438292-28370-1-git-send-email-eric@anholt.net> Signed-off-by: Eric Anholt Tested-by: Keith Packard Signed-off-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/mtrr/main.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/arch/x86/kernel/cpu/mtrr/main.c b/arch/x86/kernel/cpu/mtrr/main.c index bebabec5b448d..151787e382c77 100644 --- a/arch/x86/kernel/cpu/mtrr/main.c +++ b/arch/x86/kernel/cpu/mtrr/main.c @@ -292,14 +292,24 @@ set_mtrr(unsigned int reg, unsigned long base, unsigned long size, mtrr_type typ /* * HACK! - * We use this same function to initialize the mtrrs on boot. - * The state of the boot cpu's mtrrs has been saved, and we want - * to replicate across all the APs. - * If we're doing that @reg is set to something special... + * + * We use this same function to initialize the mtrrs during boot, + * resume, runtime cpu online and on an explicit request to set a + * specific MTRR. + * + * During boot or suspend, the state of the boot cpu's mtrrs has been + * saved, and we want to replicate that across all the cpus that come + * online (either at the end of boot or resume or during a runtime cpu + * online). If we're doing that, @reg is set to something special and on + * this cpu we still do mtrr_if->set_all(). During boot/resume, this + * is unnecessary if at this point we are still on the cpu that started + * the boot/resume sequence. But there is no guarantee that we are still + * on the same cpu. So we do mtrr_if->set_all() on this cpu aswell to be + * sure that we are in sync with everyone else. */ if (reg != ~0U) mtrr_if->set(reg, base, size, type); - else if (!mtrr_aps_delayed_init) + else mtrr_if->set_all(); /* Wait for the others */ From 5e885c8c180b1c7f180bd13abc328f35083a8291 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Thu, 31 Mar 2011 00:01:58 -0700 Subject: [PATCH 1215/2556] Input: synaptics - fix crash in synaptics_module_init() commit 708748670c7c6dd5bd3b141473086e6937e72737 upstream. 'struct dmi_system_id' arrays must always have a terminator to keep dmi_check_system() from looking at data (and possibly crashing) it isn't supposed to look at. The issue went unnoticed until ef8313bb1a22e7d2125d9d758aa8a81f1de91d81, but was introduced about a year earlier with 7705d548cbe33f18ea7713b9a07aa11047aaeca4 (which also similarly changed lifebook.c, but the problem there got eliminated shortly afterwards). The first hunk therefore is a stable candidate back to 2.6.33, while the full change is needed only on 2.6.38. Signed-off-by: Jan Beulich Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/synaptics.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index aa186cf6c5145..e06e045bf907a 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -836,8 +836,8 @@ static const struct dmi_system_id __initconst toshiba_dmi_table[] = { }, }, - { } #endif + { } }; static bool broken_olpc_ec; @@ -851,8 +851,8 @@ static const struct dmi_system_id __initconst olpc_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "XO"), }, }, - { } #endif + { } }; void __init synaptics_module_init(void) From 411bd1e094b708745711a3b25bec72a442dea0f0 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 25 Mar 2011 17:43:41 +0100 Subject: [PATCH 1216/2556] ath9k: fix a chip wakeup related crash in ath9k_start commit f62d816fc4324afbb7cf90110c70b6a14139b225 upstream. When the chip is still asleep when ath9k_start is called, ath9k_hw_configpcipowersave can trigger a data bus error. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index a09d15f7aa6e0..0848e09954749 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1063,6 +1063,8 @@ static int ath9k_start(struct ieee80211_hw *hw) "Starting driver with initial channel: %d MHz\n", curchan->center_freq); + ath9k_ps_wakeup(sc); + mutex_lock(&sc->mutex); if (ath9k_wiphy_started(sc)) { @@ -1179,6 +1181,8 @@ static int ath9k_start(struct ieee80211_hw *hw) mutex_unlock: mutex_unlock(&sc->mutex); + ath9k_ps_restore(sc); + return r; } From 099ad2ced6d52aeb2a61a0d13fc67cf66b4ff755 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 25 Mar 2011 15:30:38 +0100 Subject: [PATCH 1217/2556] mac80211: fix a crash in minstrel_ht in HT mode with no supported MCS rates commit 4dc217df68a17a57f8464c74c1b4785e40bddf77 upstream. When a client connects in HT mode but does not provide any valid MCS rates, the function that finds the next sample rate gets stuck in an infinite loop. Fix this by falling back to legacy rates if no usable MCS rates are found. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- net/mac80211/rc80211_minstrel_ht.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 165a4518bb48e..cac35ff14b86e 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -639,18 +639,14 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs; struct ieee80211_local *local = hw_to_local(mp->hw); u16 sta_cap = sta->ht_cap.cap; + int n_supported = 0; int ack_dur; int stbc; int i; /* fall back to the old minstrel for legacy stations */ - if (!sta->ht_cap.ht_supported) { - msp->is_ht = false; - memset(&msp->legacy, 0, sizeof(msp->legacy)); - msp->legacy.r = msp->ratelist; - msp->legacy.sample_table = msp->sample_table; - return mac80211_minstrel.rate_init(priv, sband, sta, &msp->legacy); - } + if (!sta->ht_cap.ht_supported) + goto use_legacy; BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS); @@ -705,7 +701,22 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, mi->groups[i].supported = mcs->rx_mask[minstrel_mcs_groups[i].streams - 1]; + + if (mi->groups[i].supported) + n_supported++; } + + if (!n_supported) + goto use_legacy; + + return; + +use_legacy: + msp->is_ht = false; + memset(&msp->legacy, 0, sizeof(msp->legacy)); + msp->legacy.r = msp->ratelist; + msp->legacy.sample_table = msp->sample_table; + return mac80211_minstrel.rate_init(priv, sband, sta, &msp->legacy); } static void From 6efb80f2abea080c57e11b36686bbe0c9cc1b855 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Mon, 21 Mar 2011 16:44:35 +0100 Subject: [PATCH 1218/2556] staging: IIO: IMU: ADIS16400: Fix up SPI messages cs_change behavior commit fc5b85b0ad1f9da948e4d683710081a9bda357cb upstream. cs_change must not be set in the last transfer of a spi message Signed-off-by: Michael Hennerich Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/imu/adis16400_core.c | 2 -- drivers/staging/iio/imu/adis16400_ring.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/drivers/staging/iio/imu/adis16400_core.c b/drivers/staging/iio/imu/adis16400_core.c index cfb108a1545b3..1007e3ded92fe 100644 --- a/drivers/staging/iio/imu/adis16400_core.c +++ b/drivers/staging/iio/imu/adis16400_core.c @@ -93,7 +93,6 @@ static int adis16400_spi_write_reg_16(struct device *dev, .tx_buf = st->tx + 2, .bits_per_word = 8, .len = 2, - .cs_change = 1, }, }; @@ -137,7 +136,6 @@ static int adis16400_spi_read_reg_16(struct device *dev, .rx_buf = st->rx, .bits_per_word = 8, .len = 2, - .cs_change = 1, }, }; diff --git a/drivers/staging/iio/imu/adis16400_ring.c b/drivers/staging/iio/imu/adis16400_ring.c index 33293fba9bccd..ac381ca126fcf 100644 --- a/drivers/staging/iio/imu/adis16400_ring.c +++ b/drivers/staging/iio/imu/adis16400_ring.c @@ -122,12 +122,10 @@ static int adis16400_spi_read_burst(struct device *dev, u8 *rx) .tx_buf = st->tx, .bits_per_word = 8, .len = 2, - .cs_change = 0, }, { .rx_buf = rx, .bits_per_word = 8, .len = 24, - .cs_change = 1, }, }; From 30b4c4b20420e8276e79c8f1d211e4e5072f5d3b Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Mon, 21 Mar 2011 16:44:36 +0100 Subject: [PATCH 1219/2556] staging: IIO: IMU: ADIS16400: Add delay after self test commit c59c95ce6ace6d256401fc3a3648a95375ef4e63 upstream. Add delay after self test to satisfy timing requirements. Increase start-up delay. Signed-off-by: Michael Hennerich Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/imu/adis16400.h | 3 ++- drivers/staging/iio/imu/adis16400_core.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/iio/imu/adis16400.h b/drivers/staging/iio/imu/adis16400.h index 6ff33e1ad8c19..90e90f0e65e88 100644 --- a/drivers/staging/iio/imu/adis16400.h +++ b/drivers/staging/iio/imu/adis16400.h @@ -17,7 +17,8 @@ #ifndef SPI_ADIS16400_H_ #define SPI_ADIS16400_H_ -#define ADIS16400_STARTUP_DELAY 220 /* ms */ +#define ADIS16400_STARTUP_DELAY 290 /* ms */ +#define ADIS16400_MTEST_DELAY 90 /* ms */ #define ADIS16400_READ_REG(a) a #define ADIS16400_WRITE_REG(a) ((a) | 0x80) diff --git a/drivers/staging/iio/imu/adis16400_core.c b/drivers/staging/iio/imu/adis16400_core.c index 1007e3ded92fe..633b3a4e42891 100644 --- a/drivers/staging/iio/imu/adis16400_core.c +++ b/drivers/staging/iio/imu/adis16400_core.c @@ -373,7 +373,7 @@ static int adis16400_self_test(struct device *dev) dev_err(dev, "problem starting self test"); goto err_ret; } - + msleep(ADIS16400_MTEST_DELAY); adis16400_check_status(dev); err_ret: From 40c7132159525890a93ec2b808436b4667629255 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Mon, 21 Mar 2011 16:44:37 +0100 Subject: [PATCH 1220/2556] staging: IIO: IMU: ADIS16400: Fix addresses of GYRO and ACCEL calibration offset commit b181119723d62b19904e1f12e467e996631c5a29 upstream. Signed-off-by: Michael Hennerich Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/imu/adis16400_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/iio/imu/adis16400_core.c b/drivers/staging/iio/imu/adis16400_core.c index 633b3a4e42891..2107edb3ebce1 100644 --- a/drivers/staging/iio/imu/adis16400_core.c +++ b/drivers/staging/iio/imu/adis16400_core.c @@ -495,12 +495,12 @@ static int adis16400_initial_setup(struct adis16400_state *st) _reg) static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_X, ADIS16400_XGYRO_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_Y, ADIS16400_XGYRO_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_Z, ADIS16400_XGYRO_OFF); +static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_Y, ADIS16400_YGYRO_OFF); +static ADIS16400_DEV_ATTR_CALIBBIAS(GYRO_Z, ADIS16400_ZGYRO_OFF); static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_X, ADIS16400_XACCL_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_Y, ADIS16400_XACCL_OFF); -static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_Z, ADIS16400_XACCL_OFF); +static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_Y, ADIS16400_YACCL_OFF); +static ADIS16400_DEV_ATTR_CALIBBIAS(ACCEL_Z, ADIS16400_ZACCL_OFF); static IIO_DEV_ATTR_IN_NAMED_RAW(0, supply, adis16400_read_14bit_signed, From c887414d3bbf1e5adca00f21e0c057edb3e4211f Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Mon, 21 Mar 2011 16:44:38 +0100 Subject: [PATCH 1221/2556] staging: IIO: IMU: ADIS16400: Make sure only enabled scan_elements are pushed into the ring commit 0fea4d6192cb82789e28905240d3c1dac6529c7c upstream. Signed-off-by: Michael Hennerich Acked-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/imu/adis16400_ring.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/staging/iio/imu/adis16400_ring.c b/drivers/staging/iio/imu/adis16400_ring.c index ac381ca126fcf..da28cb4288af3 100644 --- a/drivers/staging/iio/imu/adis16400_ring.c +++ b/drivers/staging/iio/imu/adis16400_ring.c @@ -160,9 +160,10 @@ static void adis16400_trigger_bh_to_ring(struct work_struct *work_s) work_trigger_to_ring); struct iio_ring_buffer *ring = st->indio_dev->ring; - int i = 0; + int i = 0, j; s16 *data; size_t datasize = ring->access.get_bytes_per_datum(ring); + unsigned long mask = ring->scan_mask; data = kmalloc(datasize , GFP_KERNEL); if (data == NULL) { @@ -172,9 +173,12 @@ static void adis16400_trigger_bh_to_ring(struct work_struct *work_s) if (ring->scan_count) if (adis16400_spi_read_burst(&st->indio_dev->dev, st->rx) >= 0) - for (; i < ring->scan_count; i++) + for (; i < ring->scan_count; i++) { + j = __ffs(mask); + mask &= ~(1 << j); data[i] = be16_to_cpup( - (__be16 *)&(st->rx[i*2])); + (__be16 *)&(st->rx[j*2])); + } /* Guaranteed to be aligned with 8 byte boundary */ if (ring->scan_timestamp) From dca70d5b87a077db871cde341262042d3dd8243f Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 25 Mar 2011 18:33:57 +0200 Subject: [PATCH 1222/2556] UBIFS: do not read flash unnecessarily commit 8b229c76765816796eec7ccd428f03bd8de8b525 upstream. This fix makes the 'dbg_check_old_index()' function return immediately if debugging is disabled, instead of executing incorrect 'goto out' which causes UBIFS to: 1. Allocate memory 2. Read the flash On every commit. OK, we do not commit that often, but it is still silly to do unneeded I/O anyway. Credits to coverity for spotting this silly issue. Signed-off-by: Artem Bityutskiy Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ubifs/commit.c b/fs/ubifs/commit.c index 02429d81ca337..32bcb2c467e7b 100644 --- a/fs/ubifs/commit.c +++ b/fs/ubifs/commit.c @@ -521,7 +521,7 @@ int dbg_check_old_index(struct ubifs_info *c, struct ubifs_zbranch *zroot) size_t sz; if (!(ubifs_chk_flags & UBIFS_CHK_OLD_IDX)) - goto out; + return 0; INIT_LIST_HEAD(&list); From 5fa2bf4c2d4264da28d425d9de13955008dd253b Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 25 Mar 2011 19:09:54 +0200 Subject: [PATCH 1223/2556] UBIFS: fix oops on error path in read_pnode commit 54acbaaa523ca0bd284a18f67ad213c379679e86 upstream. Thanks to coverity which spotted that UBIFS will oops if 'kmalloc()' in 'read_pnode()' fails and we dereference a NULL 'pnode' pointer when we 'goto out'. Signed-off-by: Artem Bityutskiy Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/lpt.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fs/ubifs/lpt.c b/fs/ubifs/lpt.c index 72775d35b99e5..ef5155e109a27 100644 --- a/fs/ubifs/lpt.c +++ b/fs/ubifs/lpt.c @@ -1270,10 +1270,9 @@ static int read_pnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip) lnum = branch->lnum; offs = branch->offs; pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_NOFS); - if (!pnode) { - err = -ENOMEM; - goto out; - } + if (!pnode) + return -ENOMEM; + if (lnum == 0) { /* * This pnode was not written which just means that the LEB From c91a297be661578c6e8019efac247e5893784e43 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 4 Apr 2011 17:16:39 +0300 Subject: [PATCH 1224/2556] UBIFS: fix debugging failure in dbg_check_space_info commit 7da6443aca9be29c6948dcbd636ad50154d0bc0c upstream. This patch fixes a debugging failure with which looks like this: UBIFS error (pid 32313): dbg_check_space_info: free space changed from 6019344 to 6022654 The reason for this failure is described in the comment this patch adds to the code. But in short - 'c->freeable_cnt' may be different before and after re-mounting, and this is normal. So the debugging code should make sure that free space calculations do not depend on 'c->freeable_cnt'. A similar issue has been reported here: http://lists.infradead.org/pipermail/linux-mtd/2011-April/034647.html This patch should fix it. For the -stable guys: this patch is only relevant for kernels 2.6.30 onwards. Signed-off-by: Artem Bityutskiy Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/debug.c | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/fs/ubifs/debug.c b/fs/ubifs/debug.c index 0bee4dbffc31f..5b9e9855b22c6 100644 --- a/fs/ubifs/debug.c +++ b/fs/ubifs/debug.c @@ -961,11 +961,39 @@ void dbg_dump_index(struct ubifs_info *c) void dbg_save_space_info(struct ubifs_info *c) { struct ubifs_debug_info *d = c->dbg; - - ubifs_get_lp_stats(c, &d->saved_lst); + int freeable_cnt; spin_lock(&c->space_lock); + memcpy(&d->saved_lst, &c->lst, sizeof(struct ubifs_lp_stats)); + + /* + * We use a dirty hack here and zero out @c->freeable_cnt, because it + * affects the free space calculations, and UBIFS might not know about + * all freeable eraseblocks. Indeed, we know about freeable eraseblocks + * only when we read their lprops, and we do this only lazily, upon the + * need. So at any given point of time @c->freeable_cnt might be not + * exactly accurate. + * + * Just one example about the issue we hit when we did not zero + * @c->freeable_cnt. + * 1. The file-system is mounted R/O, c->freeable_cnt is %0. We save the + * amount of free space in @d->saved_free + * 2. We re-mount R/W, which makes UBIFS to read the "lsave" + * information from flash, where we cache LEBs from various + * categories ('ubifs_remount_fs()' -> 'ubifs_lpt_init()' + * -> 'lpt_init_wr()' -> 'read_lsave()' -> 'ubifs_lpt_lookup()' + * -> 'ubifs_get_pnode()' -> 'update_cats()' + * -> 'ubifs_add_to_cat()'). + * 3. Lsave contains a freeable eraseblock, and @c->freeable_cnt + * becomes %1. + * 4. We calculate the amount of free space when the re-mount is + * finished in 'dbg_check_space_info()' and it does not match + * @d->saved_free. + */ + freeable_cnt = c->freeable_cnt; + c->freeable_cnt = 0; d->saved_free = ubifs_get_free_space_nolock(c); + c->freeable_cnt = freeable_cnt; spin_unlock(&c->space_lock); } @@ -982,12 +1010,15 @@ int dbg_check_space_info(struct ubifs_info *c) { struct ubifs_debug_info *d = c->dbg; struct ubifs_lp_stats lst; - long long avail, free; + long long free; + int freeable_cnt; spin_lock(&c->space_lock); - avail = ubifs_calc_available(c, c->min_idx_lebs); + freeable_cnt = c->freeable_cnt; + c->freeable_cnt = 0; + free = ubifs_get_free_space_nolock(c); + c->freeable_cnt = freeable_cnt; spin_unlock(&c->space_lock); - free = ubifs_get_free_space(c); if (free != d->saved_free) { ubifs_err("free space changed from %lld to %lld", From 47c2bfc9cff176abaf6bace2762d83f10a8bd47e Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 31 Mar 2011 18:36:52 +0200 Subject: [PATCH 1225/2556] quota: Don't write quota info in dquot_commit() commit b03f24567ce7caf2420b8be4c6eb74c191d59a91 upstream. There's no reason to write quota info in dquot_commit(). The writing is a relict from the old days when we didn't have dquot_acquire() and dquot_release() and thus dquot_commit() could have created / removed quota structures from the file. These days dquot_commit() only updates usage counters / limits in quota structure and thus there's no need to write quota info. This also fixes an issue with journaling filesystem which didn't reserve enough space in the transaction for write of quota info (it could have been dirty at the time of dquot_commit() because of a race with other operation changing it). Reported-and-tested-by: Lukas Czerner Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/quota/dquot.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index a2a622e079f08..b59ee61f4b9a0 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -442,7 +442,7 @@ EXPORT_SYMBOL(dquot_acquire); */ int dquot_commit(struct dquot *dquot) { - int ret = 0, ret2 = 0; + int ret = 0; struct quota_info *dqopt = sb_dqopt(dquot->dq_sb); mutex_lock(&dqopt->dqio_mutex); @@ -454,15 +454,10 @@ int dquot_commit(struct dquot *dquot) spin_unlock(&dq_list_lock); /* Inactive dquot can be only if there was error during read/init * => we have better not writing it */ - if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) { + if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot); - if (info_dirty(&dqopt->info[dquot->dq_type])) { - ret2 = dqopt->ops[dquot->dq_type]->write_file_info( - dquot->dq_sb, dquot->dq_type); - } - if (ret >= 0) - ret = ret2; - } + else + ret = -EIO; out_sem: mutex_unlock(&dqopt->dqio_mutex); return ret; From 501498105d3e361d81aaa2964a7dfea0db7606b7 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 7 Apr 2011 07:35:50 -0700 Subject: [PATCH 1226/2556] mm: avoid wrapping vm_pgoff in mremap() commit 982134ba62618c2d69fbbbd166d0a11ee3b7e3d8 upstream. The normal mmap paths all avoid creating a mapping where the pgoff inside the mapping could wrap around due to overflow. However, an expanding mremap() can take such a non-wrapping mapping and make it bigger and cause a wrapping condition. Noticed by Robert Swiecki when running a system call fuzzer, where it caused a BUG_ON() due to terminally confusing the vma_prio_tree code. A vma dumping patch by Hugh then pinpointed the crazy wrapped case. Reported-and-tested-by: Robert Swiecki Acked-by: Hugh Dickins Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/mremap.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mm/mremap.c b/mm/mremap.c index 1de98d492ddcd..a7c1f9f9b9415 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -277,9 +277,16 @@ static struct vm_area_struct *vma_to_resize(unsigned long addr, if (old_len > vma->vm_end - addr) goto Efault; - if (vma->vm_flags & (VM_DONTEXPAND | VM_PFNMAP)) { - if (new_len > old_len) + /* Need to be careful about a growing mapping */ + if (new_len > old_len) { + unsigned long pgoff; + + if (vma->vm_flags & (VM_DONTEXPAND | VM_PFNMAP)) goto Efault; + pgoff = (addr - vma->vm_start) >> PAGE_SHIFT; + pgoff += vma->vm_pgoff; + if (pgoff + (new_len >> PAGE_SHIFT) < pgoff) + goto Einval; } if (vma->vm_flags & VM_LOCKED) { From 9aadbea83a7946ef4006d93aebedd3cf028ee898 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 6 Apr 2011 10:13:32 -0700 Subject: [PATCH 1227/2556] Revert "net/sunrpc: Use static const char arrays" commit 0867659fa3c245bf203d837a82e0f6ea5079c2c5 upstream. This reverts commit 411b5e05617593efebc06241dbc56f42150f2abe. Olga Kornievskaia reports: Problem: linux client mounting linux server using rc4-hmac-md5 enctype. gssd fails with create a context after receiving a reply from the server. Diagnose: putting printout statements in the server kernel and kerberos libraries revealed that client and server derived different integrity keys. Server kernel code was at fault due the the commit [aglo@skydive linux-pnfs]$ git show 411b5e05617593efebc06241dbc56f42150f2abe Trond: The problem is that since it relies on virt_to_page(), you cannot call sg_set_buf() for data in the const section. Reported-by: Olga Kornievskaia Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/auth_gss/gss_krb5_mech.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index f375decc024b4..778e5dfc51449 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c @@ -427,7 +427,7 @@ static int context_derive_keys_rc4(struct krb5_ctx *ctx) { struct crypto_hash *hmac; - static const char sigkeyconstant[] = "signaturekey"; + char sigkeyconstant[] = "signaturekey"; int slen = strlen(sigkeyconstant) + 1; /* include null terminator */ struct hash_desc desc; struct scatterlist sg[1]; From 3f7691666ea73e5b3f09d925e0786c13c5c0ccdd Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Mon, 4 Apr 2011 13:18:44 -0400 Subject: [PATCH 1228/2556] iwlwifi: accept EEPROM version 0x423 for iwl6000 commit 3d7dc7e8c1566acb0fc55df228b2ed91f5638e9d upstream. A number of these devices have appeared "in the wild", and apparently the Windows driver is perfectly happy to support this EEPROM version. Signed-off-by: John W. Linville Acked-by: Wey-Yi Guy Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl-eeprom.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.h b/drivers/net/wireless/iwlwifi/iwl-eeprom.h index 9e6f31355eee8..c0cd307dc2e82 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.h @@ -241,7 +241,7 @@ struct iwl_eeprom_enhanced_txpwr { /* 6x00 Specific */ #define EEPROM_6000_TX_POWER_VERSION (4) -#define EEPROM_6000_EEPROM_VERSION (0x434) +#define EEPROM_6000_EEPROM_VERSION (0x423) /* 6x50 Specific */ #define EEPROM_6050_TX_POWER_VERSION (4) From f3d47fded228334d238a54523738fadee721350a Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 2 Apr 2011 11:31:29 +0200 Subject: [PATCH 1229/2556] p54usb: IDs for two new devices commit 220107610c7c2c9703e09eb363e8ab31025b9315 upstream. Reported-by: Mark Davis [via p54/devices wiki] Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/p54/p54usb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index 9b344a921e742..e18358725b698 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -56,6 +56,7 @@ static struct usb_device_id p54u_table[] __devinitdata = { {USB_DEVICE(0x0846, 0x4210)}, /* Netgear WG121 the second ? */ {USB_DEVICE(0x0846, 0x4220)}, /* Netgear WG111 */ {USB_DEVICE(0x09aa, 0x1000)}, /* Spinnaker Proto board */ + {USB_DEVICE(0x0bf8, 0x1007)}, /* Fujitsu E-5400 USB */ {USB_DEVICE(0x0cde, 0x0006)}, /* Medion 40900, Roper Europe */ {USB_DEVICE(0x0db0, 0x6826)}, /* MSI UB54G (MS-6826) */ {USB_DEVICE(0x107b, 0x55f2)}, /* Gateway WGU-210 (Gemtek) */ @@ -68,6 +69,7 @@ static struct usb_device_id p54u_table[] __devinitdata = { {USB_DEVICE(0x1915, 0x2235)}, /* Linksys WUSB54G Portable OEM */ {USB_DEVICE(0x2001, 0x3701)}, /* DLink DWL-G120 Spinnaker */ {USB_DEVICE(0x2001, 0x3703)}, /* DLink DWL-G122 */ + {USB_DEVICE(0x2001, 0x3762)}, /* Conceptronic C54U */ {USB_DEVICE(0x5041, 0x2234)}, /* Linksys WUSB54G */ {USB_DEVICE(0x5041, 0x2235)}, /* Linksys WUSB54G Portable */ From 47893311a8a9ea3c5163bf8dbef7d4a63d513dc9 Mon Sep 17 00:00:00 2001 From: RA-Jay Hung Date: Mon, 10 Jan 2011 11:27:43 +0100 Subject: [PATCH 1230/2556] rt2x00: Fix radio off hang issue for PCIE interface commit 7f6e144fb99a4a70d3c5ad5f074204c5b89a6f65 upstream. PCI/PCIE radio off behavior is different from SOC/USB. They mainly use MCU command to disable DMA, TX/RX and enter power saving mode. Signed-off-by: RA-Jay Hung Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville Signed-off-by: Stanislaw Gruszka Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/rt2x00/rt2800lib.c | 6 ----- drivers/net/wireless/rt2x00/rt2800pci.c | 36 +++++++------------------ 2 files changed, 10 insertions(+), 32 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 54917a2813981..e2a528da36417 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -2810,10 +2810,7 @@ void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev) rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_DMA_BUSY, 0); rt2x00_set_field32(®, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_RX_DMA_BUSY, 0); - rt2x00_set_field32(®, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1); rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg); /* Wait for DMA, ignore error */ @@ -2823,9 +2820,6 @@ void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev) rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_TX, 0); rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - - rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0); - rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0); } EXPORT_SYMBOL_GPL(rt2800_disable_radio); diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index 3b3f1e45ab3e5..37a38b5a2a8f7 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -475,39 +475,23 @@ static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev) static void rt2800pci_disable_radio(struct rt2x00_dev *rt2x00dev) { - u32 reg; - - rt2800_disable_radio(rt2x00dev); - - rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00001280); - - rt2800_register_read(rt2x00dev, WPDMA_RST_IDX, ®); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX0, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX1, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX2, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX3, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX4, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX5, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DRX_IDX0, 1); - rt2800_register_write(rt2x00dev, WPDMA_RST_IDX, reg); - - rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f); - rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); + if (rt2x00_is_soc(rt2x00dev)) { + rt2800_disable_radio(rt2x00dev); + rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0); + rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0); + } } static int rt2800pci_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { - /* - * Always put the device to sleep (even when we intend to wakeup!) - * if the device is booting and wasn't asleep it will return - * failure when attempting to wakeup. - */ - rt2800_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0xff, 2); - if (state == STATE_AWAKE) { - rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKUP, 0, 0); + rt2800_mcu_request(rt2x00dev, MCU_WAKEUP, TOKEN_WAKUP, 0, 0x02); rt2800pci_mcu_status(rt2x00dev, TOKEN_WAKUP); + } else if (state == STATE_SLEEP) { + rt2800_register_write(rt2x00dev, H2M_MAILBOX_STATUS, 0xffffffff); + rt2800_register_write(rt2x00dev, H2M_MAILBOX_CID, 0xffffffff); + rt2800_mcu_request(rt2x00dev, MCU_SLEEP, 0x01, 0xff, 0x01); } return 0; From a2e37d18cc0dedcdd6fb3905a9c83929f4397c8f Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Mon, 4 Apr 2011 13:50:32 +0200 Subject: [PATCH 1231/2556] rt2x00: fix cancelling uninitialized work commit 37f4ee0b6b39640828dac5937a482c20e8ac794f upstream. {rx,tx}done_work's are only initialized for usb devices. Signed-off-by: Stanislaw Gruszka Acked-by: Ivo van Doorn Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/rt2x00/rt2x00dev.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 9597a03242cce..2b77a291aa970 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1031,8 +1031,10 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) * Stop all work. */ cancel_work_sync(&rt2x00dev->intf_work); - cancel_work_sync(&rt2x00dev->rxdone_work); - cancel_work_sync(&rt2x00dev->txdone_work); + if (rt2x00_is_usb(rt2x00dev)) { + cancel_work_sync(&rt2x00dev->rxdone_work); + cancel_work_sync(&rt2x00dev->txdone_work); + } /* * Free the tx status fifo. From 5c1155acfdf7a826c67fd7d63daa5f7c424e108a Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 1 Apr 2011 19:42:02 +0300 Subject: [PATCH 1232/2556] wl12xx: fix potential buffer overflow in testmode nvs push commit 09b661b33268698d3b453dceb78cda129ad899b4 upstream. We were allocating the size of the NVS file struct and not checking whether the length of the buffer passed was correct before copying it into the allocated memory. This is a security hole because buffer overflows can occur if the userspace passes a bigger file than what is expected. With this patch, we check if the size of the data passed from userspace matches the size required. This bug was introduced in 2.6.36. Reported-by: Ido Yariv Signed-off-by: Luciano Coelho Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/wl12xx/testmode.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/wl12xx/testmode.c b/drivers/net/wireless/wl12xx/testmode.c index e64403b6896d7..6ec06a4a4c6df 100644 --- a/drivers/net/wireless/wl12xx/testmode.c +++ b/drivers/net/wireless/wl12xx/testmode.c @@ -204,7 +204,10 @@ static int wl1271_tm_cmd_nvs_push(struct wl1271 *wl, struct nlattr *tb[]) kfree(wl->nvs); - wl->nvs = kzalloc(sizeof(struct wl1271_nvs_file), GFP_KERNEL); + if (len != sizeof(struct wl1271_nvs_file)) + return -EINVAL; + + wl->nvs = kzalloc(len, GFP_KERNEL); if (!wl->nvs) { wl1271_error("could not allocate memory for the nvs file"); ret = -ENOMEM; From 2bce9f509f6e7a0b7fd88d63a0c64ca445b77f74 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 27 Feb 2011 14:51:54 -0300 Subject: [PATCH 1233/2556] media/radio/wl1273: fix build errors commit 1b149bbe9156d2eb2afd5a072bd61ad0d4bfaca7 upstream. RADIO_WL1273 needs to make sure that the mfd core is built to avoid build errors: ERROR: "mfd_add_devices" [drivers/mfd/wl1273-core.ko] undefined! ERROR: "mfd_remove_devices" [drivers/mfd/wl1273-core.ko] undefined! Signed-off-by: Randy Dunlap Cc: Matti Aaltonen Signed-off-by: Mauro Carvalho Chehab Cc: Mike Frysinger Signed-off-by: Greg Kroah-Hartman --- drivers/media/radio/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index ecdffa6aac664..c0902465bdfd7 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -441,6 +441,7 @@ config RADIO_TIMBERDALE config RADIO_WL1273 tristate "Texas Instruments WL1273 I2C FM Radio" depends on I2C && VIDEO_V4L2 + select MFD_CORE select MFD_WL1273_CORE select FW_LOADER ---help--- From ad8619787fea12263cb03a59d2d7b427918679f9 Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Wed, 30 Mar 2011 14:02:46 -0400 Subject: [PATCH 1234/2556] b43: allocate receive buffers big enough for max frame len + offset commit c85ce65ecac078ab1a1835c87c4a6319cf74660a upstream. Otherwise, skb_put inside of dma_rx can fail... https://bugzilla.kernel.org/show_bug.cgi?id=32042 Signed-off-by: John W. Linville Acked-by: Larry Finger Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/b43/dma.c | 2 +- drivers/net/wireless/b43/dma.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index 3d5566e7af0ad..ff0f5ba14b2cf 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -1536,7 +1536,7 @@ static void dma_rx(struct b43_dmaring *ring, int *slot) dmaaddr = meta->dmaaddr; goto drop_recycle_buffer; } - if (unlikely(len > ring->rx_buffersize)) { + if (unlikely(len + ring->frameoffset > ring->rx_buffersize)) { /* The data did not fit into one descriptor buffer * and is split over multiple buffers. * This should never happen, as we try to allocate buffers diff --git a/drivers/net/wireless/b43/dma.h b/drivers/net/wireless/b43/dma.h index a01c2100f1664..e8a80a1251bf6 100644 --- a/drivers/net/wireless/b43/dma.h +++ b/drivers/net/wireless/b43/dma.h @@ -163,7 +163,7 @@ struct b43_dmadesc_generic { /* DMA engine tuning knobs */ #define B43_TXRING_SLOTS 256 #define B43_RXRING_SLOTS 64 -#define B43_DMA0_RX_BUFFERSIZE IEEE80211_MAX_FRAME_LEN +#define B43_DMA0_RX_BUFFERSIZE (B43_DMA0_RX_FRAMEOFFSET + IEEE80211_MAX_FRAME_LEN) /* Pointer poison */ #define B43_DMA_PTR_POISON ((void *)ERR_PTR(-ENOMEM)) From 1a657649889e557cdea128f03135eb1bdbbbb8a5 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Mon, 14 Feb 2011 13:54:26 +0300 Subject: [PATCH 1235/2556] Bluetooth: sco: fix information leak to userspace commit c4c896e1471aec3b004a693c689f60be3b17ac86 upstream. struct sco_conninfo has one padding byte in the end. Local variable cinfo of type sco_conninfo is copied to userspace with this uninizialized one byte, leading to old stack contents leak. Signed-off-by: Vasiliy Kulikov Signed-off-by: Gustavo F. Padovan Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/sco.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 6973b3482c648..813b06e805263 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -720,6 +720,7 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user break; } + memset(&cinfo, 0, sizeof(cinfo)); cinfo.hci_handle = sco_pi(sk)->conn->hcon->handle; memcpy(cinfo.dev_class, sco_pi(sk)->conn->hcon->dev_class, 3); From 7de7ad3c46c5d79611c6f5ff6ed7e01cb7e77c4c Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Mon, 14 Feb 2011 16:49:23 +0100 Subject: [PATCH 1236/2556] bridge: netfilter: fix information leak commit d846f71195d57b0bbb143382647c2c6638b04c5a upstream. Struct tmp is copied from userspace. It is not checked whether the "name" field is NULL terminated. This may lead to buffer overflow and passing contents of kernel stack as a module name to try_then_request_module() and, consequently, to modprobe commandline. It would be seen by all userspace processes. Signed-off-by: Vasiliy Kulikov Signed-off-by: Patrick McHardy Signed-off-by: Greg Kroah-Hartman --- net/bridge/netfilter/ebtables.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 16df0532d4b9a..47acf4a50efe8 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1107,6 +1107,8 @@ static int do_replace(struct net *net, const void __user *user, if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter)) return -ENOMEM; + tmp.name[sizeof(tmp.name) - 1] = 0; + countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids; newinfo = vmalloc(sizeof(*newinfo) + countersize); if (!newinfo) From 6b851142881372e1cdfecaa47cc17b5058df04e1 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Mon, 14 Feb 2011 13:54:31 +0300 Subject: [PATCH 1237/2556] Bluetooth: bnep: fix buffer overflow commit 43629f8f5ea32a998d06d1bb41eefa0e821ff573 upstream. Struct ca is copied from userspace. It is not checked whether the "device" field is NULL terminated. This potentially leads to BUG() inside of alloc_netdev_mqs() and/or information leak by creating a device with a name made of contents of kernel stack. Signed-off-by: Vasiliy Kulikov Signed-off-by: Gustavo F. Padovan Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/bnep/sock.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c index 2862f53b66b15..d935da71ab3b5 100644 --- a/net/bluetooth/bnep/sock.c +++ b/net/bluetooth/bnep/sock.c @@ -88,6 +88,7 @@ static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long sockfd_put(nsock); return -EBADFD; } + ca.device[sizeof(ca.device)-1] = 0; err = bnep_add_connection(&ca, nsock); if (!err) { From 80cfc9a5ab40d0629d5f216f3ba006860ec25c6b Mon Sep 17 00:00:00 2001 From: Marc-Antoine Perennou Date: Thu, 24 Mar 2011 14:51:21 -0300 Subject: [PATCH 1238/2556] Bluetooth: add support for Apple MacBook Pro 8,2 commit 63a8588debd4dc72becb9e27add9343c76301c7d upstream. Just adding the vendor details makes it work fine. Signed-off-by: Marc-Antoine Perennou Signed-off-by: Gustavo F. Padovan Signed-off-by: Greg Kroah-Hartman Tested-by: Grant Likely --- drivers/bluetooth/btusb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 700a3840fddc2..f44ca40a28faa 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -71,6 +71,9 @@ static struct usb_device_id btusb_table[] = { /* Apple MacBookAir3,1, MacBookAir3,2 */ { USB_DEVICE(0x05ac, 0x821b) }, + /* Apple MacBookPro8,2 */ + { USB_DEVICE(0x05ac, 0x821a) }, + /* AVM BlueFRITZ! USB v2.0 */ { USB_DEVICE(0x057c, 0x3800) }, From 855850d3fd5b25360de8adce3f97e09000db56e0 Mon Sep 17 00:00:00 2001 From: Goldwyn Rodrigues Date: Thu, 17 Feb 2011 09:44:40 -0600 Subject: [PATCH 1239/2556] Treat writes as new when holes span across page boundaries commit 272b62c1f0f6f742046e45b50b6fec98860208a0 upstream. When a hole spans across page boundaries, the next write forces a read of the block. This could end up reading existing garbage data from the disk in ocfs2_map_page_blocks. This leads to non-zero holes. In order to avoid this, mark the writes as new when the holes span across page boundaries. Signed-off-by: Goldwyn Rodrigues Signed-off-by: jlbec Signed-off-by: Greg Kroah-Hartman --- fs/ocfs2/aops.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 1fbb0e20131bf..bbba782cce264 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -1026,6 +1026,12 @@ static int ocfs2_prepare_page_for_write(struct inode *inode, u64 *p_blkno, ocfs2_figure_cluster_boundaries(OCFS2_SB(inode->i_sb), cpos, &cluster_start, &cluster_end); + /* treat the write as new if the a hole/lseek spanned across + * the page boundary. + */ + new = new | ((i_size_read(inode) <= page_offset(page)) && + (page_offset(page) <= user_pos)); + if (page == wc->w_target_page) { map_from = user_pos & (PAGE_CACHE_SIZE - 1); map_to = map_from + user_len; From 40310a1584af67d01ce3c0cef73d933d7bd42e83 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Tue, 29 Mar 2011 13:31:25 +0200 Subject: [PATCH 1240/2556] char/tpm: Fix unitialized usage of data buffer commit 1309d7afbed112f0e8e90be9af975550caa0076b upstream. This patch fixes information leakage to the userspace by initializing the data buffer to zero. Reported-by: Peter Huewe Signed-off-by: Peter Huewe Signed-off-by: Marcel Selhorst [ Also removed the silly "* sizeof(u8)". If that isn't 1, we have way deeper problems than a simple multiplication can fix. - Linus ] Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/char/tpm/tpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 1f46f1cd9225c..7beb0e25f1e1e 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -980,7 +980,7 @@ int tpm_open(struct inode *inode, struct file *file) return -EBUSY; } - chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); + chip->data_buffer = kzalloc(TPM_BUFSIZE, GFP_KERNEL); if (chip->data_buffer == NULL) { clear_bit(0, &chip->is_open); put_device(chip->dev); From 0e37b3b50697ae047cec7c62b7ef3127a53516f8 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Tue, 15 Mar 2011 13:36:05 +0100 Subject: [PATCH 1241/2556] netfilter: ip_tables: fix infoleak to userspace commit 78b79876761b86653df89c48a7010b5cbd41a84a upstream. Structures ipt_replace, compat_ipt_replace, and xt_get_revision are copied from userspace. Fields of these structs that are zero-terminated strings are not checked. When they are used as argument to a format string containing "%s" in request_module(), some sensitive information is leaked to userspace via argument of spawned modprobe process. The first and the third bugs were introduced before the git epoch; the second was introduced in 2722971c (v2.6.17-rc1). To trigger the bug one should have CAP_NET_ADMIN. Signed-off-by: Vasiliy Kulikov Signed-off-by: Patrick McHardy Signed-off-by: Greg Kroah-Hartman --- net/ipv4/netfilter/ip_tables.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 652efea013dcc..3039101cbaed9 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -1261,6 +1261,7 @@ do_replace(struct net *net, const void __user *user, unsigned int len) /* overflow check */ if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) return -ENOMEM; + tmp.name[sizeof(tmp.name)-1] = 0; newinfo = xt_alloc_table_info(tmp.size); if (!newinfo) @@ -1805,6 +1806,7 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len) return -ENOMEM; if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) return -ENOMEM; + tmp.name[sizeof(tmp.name)-1] = 0; newinfo = xt_alloc_table_info(tmp.size); if (!newinfo) @@ -2034,6 +2036,7 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) ret = -EFAULT; break; } + rev.name[sizeof(rev.name)-1] = 0; if (cmd == IPT_SO_GET_REVISION_TARGET) target = 1; From 0bf75db0fd674858d42e9dbfe8b963c6a4b404b1 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sun, 20 Mar 2011 15:40:06 +0100 Subject: [PATCH 1242/2556] netfilter: xtables: fix reentrancy commit db856674ac69e31946e56085239757cca3f7655f upstream. commit f3c5c1bfd4308 (make ip_tables reentrant) introduced a race in handling the stackptr restore, at the end of ipt_do_table() We should do it before the call to xt_info_rdunlock_bh(), or we allow cpu preemption and another cpu overwrites stackptr of original one. A second fix is to change the underflow test to check the origptr value instead of 0 to detect underflow, or else we allow a jump from different hooks. Signed-off-by: Eric Dumazet Cc: Jan Engelhardt Signed-off-by: Patrick McHardy Signed-off-by: Greg Kroah-Hartman --- net/ipv4/netfilter/ip_tables.c | 4 ++-- net/ipv6/netfilter/ip6_tables.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 3039101cbaed9..92fb4c5e5c9bd 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -387,7 +387,7 @@ ipt_do_table(struct sk_buff *skb, verdict = (unsigned)(-v) - 1; break; } - if (*stackptr == 0) { + if (*stackptr <= origptr) { e = get_entry(table_base, private->underflow[hook]); pr_debug("Underflow (this is normal) " @@ -427,10 +427,10 @@ ipt_do_table(struct sk_buff *skb, /* Verdict */ break; } while (!acpar.hotdrop); - xt_info_rdunlock_bh(); pr_debug("Exiting %s; resetting sp from %u to %u\n", __func__, *stackptr, origptr); *stackptr = origptr; + xt_info_rdunlock_bh(); #ifdef DEBUG_ALLOW_ALL return NF_ACCEPT; #else diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 7d227c644f727..830921e7e82b3 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -410,7 +410,7 @@ ip6t_do_table(struct sk_buff *skb, verdict = (unsigned)(-v) - 1; break; } - if (*stackptr == 0) + if (*stackptr <= origptr) e = get_entry(table_base, private->underflow[hook]); else @@ -441,8 +441,8 @@ ip6t_do_table(struct sk_buff *skb, break; } while (!acpar.hotdrop); - xt_info_rdunlock_bh(); *stackptr = origptr; + xt_info_rdunlock_bh(); #ifdef DEBUG_ALLOW_ALL return NF_ACCEPT; From dfd609e74f4e1ffeb30eb438e4523a4a57ed8bc4 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Tue, 15 Mar 2011 13:35:21 +0100 Subject: [PATCH 1243/2556] netfilter: arp_tables: fix infoleak to userspace commit 42eab94fff18cb1091d3501cd284d6bd6cc9c143 upstream. Structures ipt_replace, compat_ipt_replace, and xt_get_revision are copied from userspace. Fields of these structs that are zero-terminated strings are not checked. When they are used as argument to a format string containing "%s" in request_module(), some sensitive information is leaked to userspace via argument of spawned modprobe process. The first bug was introduced before the git epoch; the second is introduced by 6b7d31fc (v2.6.15-rc1); the third is introduced by 6b7d31fc (v2.6.15-rc1). To trigger the bug one should have CAP_NET_ADMIN. Signed-off-by: Vasiliy Kulikov Signed-off-by: Patrick McHardy Signed-off-by: Greg Kroah-Hartman --- net/ipv4/netfilter/arp_tables.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index e855fffaed95b..6d79aa10e6299 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -1065,6 +1065,7 @@ static int do_replace(struct net *net, const void __user *user, /* overflow check */ if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) return -ENOMEM; + tmp.name[sizeof(tmp.name)-1] = 0; newinfo = xt_alloc_table_info(tmp.size); if (!newinfo) @@ -1486,6 +1487,7 @@ static int compat_do_replace(struct net *net, void __user *user, return -ENOMEM; if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) return -ENOMEM; + tmp.name[sizeof(tmp.name)-1] = 0; newinfo = xt_alloc_table_info(tmp.size); if (!newinfo) @@ -1738,6 +1740,7 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len ret = -EFAULT; break; } + rev.name[sizeof(rev.name)-1] = 0; try_then_request_module(xt_find_revision(NFPROTO_ARP, rev.name, rev.revision, 1, &ret), From 4c883085cb8b431b7cdadbf46c24d07de6599549 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Sun, 20 Mar 2011 15:42:52 +0100 Subject: [PATCH 1244/2556] netfilter: ipt_CLUSTERIP: fix buffer overflow commit 961ed183a9fd080cf306c659b8736007e44065a5 upstream. 'buffer' string is copied from userspace. It is not checked whether it is zero terminated. This may lead to overflow inside of simple_strtoul(). Changli Gao suggested to copy not more than user supplied 'size' bytes. It was introduced before the git epoch. Files "ipt_CLUSTERIP/*" are root writable only by default, however, on some setups permissions might be relaxed to e.g. network admin user. Signed-off-by: Vasiliy Kulikov Acked-by: Changli Gao Signed-off-by: Patrick McHardy Signed-off-by: Greg Kroah-Hartman --- net/ipv4/netfilter/ipt_CLUSTERIP.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index 1e26a4897655b..af7dec683e104 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -669,8 +669,11 @@ static ssize_t clusterip_proc_write(struct file *file, const char __user *input, char buffer[PROC_WRITELEN+1]; unsigned long nodenum; - if (copy_from_user(buffer, input, PROC_WRITELEN)) + if (size > PROC_WRITELEN) + return -EIO; + if (copy_from_user(buffer, input, size)) return -EFAULT; + buffer[size] = 0; if (*buffer == '+') { nodenum = simple_strtoul(buffer+1, NULL, 10); From 6fa47e336dc5db01d935a9dee875cf80cc9df27c Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Tue, 15 Mar 2011 13:37:13 +0100 Subject: [PATCH 1245/2556] ipv6: netfilter: ip6_tables: fix infoleak to userspace commit 6a8ab060779779de8aea92ce3337ca348f973f54 upstream. Structures ip6t_replace, compat_ip6t_replace, and xt_get_revision are copied from userspace. Fields of these structs that are zero-terminated strings are not checked. When they are used as argument to a format string containing "%s" in request_module(), some sensitive information is leaked to userspace via argument of spawned modprobe process. The first bug was introduced before the git epoch; the second was introduced in 3bc3fe5e (v2.6.25-rc1); the third is introduced by 6b7d31fc (v2.6.15-rc1). To trigger the bug one should have CAP_NET_ADMIN. Signed-off-by: Vasiliy Kulikov Signed-off-by: Patrick McHardy Signed-off-by: Greg Kroah-Hartman --- net/ipv6/netfilter/ip6_tables.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 830921e7e82b3..eadafbfc9ef4b 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -1274,6 +1274,7 @@ do_replace(struct net *net, const void __user *user, unsigned int len) /* overflow check */ if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) return -ENOMEM; + tmp.name[sizeof(tmp.name)-1] = 0; newinfo = xt_alloc_table_info(tmp.size); if (!newinfo) @@ -1820,6 +1821,7 @@ compat_do_replace(struct net *net, void __user *user, unsigned int len) return -ENOMEM; if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters)) return -ENOMEM; + tmp.name[sizeof(tmp.name)-1] = 0; newinfo = xt_alloc_table_info(tmp.size); if (!newinfo) @@ -2049,6 +2051,7 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) ret = -EFAULT; break; } + rev.name[sizeof(rev.name)-1] = 0; if (cmd == IP6T_SO_GET_REVISION_TARGET) target = 1; From 0aa94d29cdeccb359706d8b6227b3e6451dd7f2a Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 4 Feb 2011 15:24:14 +0300 Subject: [PATCH 1246/2556] scsi_transport_iscsi: make priv_sess file writeable only by root commit 523f3c80bc41d663d5b35c0cd6ce0fad7f3e7188 upstream. Signed-off-by: Vasiliy Kulikov Acked-by: Mike Christie Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_transport_iscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index f905ecb5704d7..01543d297b53a 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -1847,7 +1847,7 @@ store_priv_session_##field(struct device *dev, \ #define iscsi_priv_session_rw_attr(field, format) \ iscsi_priv_session_attr_show(field, format) \ iscsi_priv_session_attr_store(field) \ -static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO | S_IWUGO, \ +static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO | S_IWUSR, \ show_priv_session_##field, \ store_priv_session_##field) iscsi_priv_session_rw_attr(recovery_tmo, "%d"); From e6c701ae569a748f3c32e712102910ffb247b757 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 4 Feb 2011 15:23:43 +0300 Subject: [PATCH 1247/2556] mfd: ab8500: world-writable debugfs register-* files commit 44bdcb54df2714da18c4a0c6f711a350ab4ed93c upstream. Don't allow everybody to interact with hardware registers. Signed-off-by: Vasiliy Kulikov Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/ab8500-debugfs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 3c1541ae72232..64748e42ac039 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -585,18 +585,18 @@ static int __devinit ab8500_debug_probe(struct platform_device *plf) goto exit_destroy_dir; ab8500_bank_file = debugfs_create_file("register-bank", - (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, &ab8500_bank_fops); + (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_bank_fops); if (!ab8500_bank_file) goto exit_destroy_reg; ab8500_address_file = debugfs_create_file("register-address", - (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, + (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_address_fops); if (!ab8500_address_file) goto exit_destroy_bank; ab8500_val_file = debugfs_create_file("register-value", - (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, &ab8500_val_fops); + (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_val_fops); if (!ab8500_val_file) goto exit_destroy_address; From 6ae8bac9ac88fc25bcb2ccc6b8626cfa12c7c7a6 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 4 Feb 2011 15:23:39 +0300 Subject: [PATCH 1248/2556] mfd: ab3500: world-writable debugfs register-* files commit 90c861c2a83d974684974441093ff8a50e6b430b upstream. Don't allow everybody to interact with hardware registers. Signed-off-by: Vasiliy Kulikov Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/ab3550-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c index 5fbca346b998d..681984df1c286 100644 --- a/drivers/mfd/ab3550-core.c +++ b/drivers/mfd/ab3550-core.c @@ -1053,17 +1053,17 @@ static inline void ab3550_setup_debugfs(struct ab3550 *ab) goto exit_destroy_dir; ab3550_bank_file = debugfs_create_file("register-bank", - (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_bank_fops); + (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_bank_fops); if (!ab3550_bank_file) goto exit_destroy_reg; ab3550_address_file = debugfs_create_file("register-address", - (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_address_fops); + (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_address_fops); if (!ab3550_address_file) goto exit_destroy_bank; ab3550_val_file = debugfs_create_file("register-value", - (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_val_fops); + (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_val_fops); if (!ab3550_val_file) goto exit_destroy_address; From 03c442c89bad5aa180b0dc93a029d7e1873e9954 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 4 Feb 2011 15:23:36 +0300 Subject: [PATCH 1249/2556] mfd: ab3100: world-writable debugfs *_priv files commit f8a0697722d12a201588225999cfc8bfcbc82781 upstream. Don't allow everybody to change device hardware registers. Signed-off-by: Vasiliy Kulikov Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/ab3100-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c index 4193af5f27439..1707d224232d1 100644 --- a/drivers/mfd/ab3100-core.c +++ b/drivers/mfd/ab3100-core.c @@ -613,7 +613,7 @@ static void ab3100_setup_debugfs(struct ab3100 *ab3100) ab3100_get_priv.ab3100 = ab3100; ab3100_get_priv.mode = false; ab3100_get_reg_file = debugfs_create_file("get_reg", - S_IWUGO, ab3100_dir, &ab3100_get_priv, + S_IWUSR, ab3100_dir, &ab3100_get_priv, &ab3100_get_set_reg_fops); if (!ab3100_get_reg_file) { err = -ENOMEM; @@ -623,7 +623,7 @@ static void ab3100_setup_debugfs(struct ab3100 *ab3100) ab3100_set_priv.ab3100 = ab3100; ab3100_set_priv.mode = true; ab3100_set_reg_file = debugfs_create_file("set_reg", - S_IWUGO, ab3100_dir, &ab3100_set_priv, + S_IWUSR, ab3100_dir, &ab3100_set_priv, &ab3100_get_set_reg_fops); if (!ab3100_set_reg_file) { err = -ENOMEM; From f5d8cc742bef29d70c23d8c35ea99ab308098368 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Tue, 22 Mar 2011 16:34:53 -0700 Subject: [PATCH 1250/2556] drivers/rtc/rtc-ds1511.c: world-writable sysfs nvram file commit 49d50fb1c28738ef6bad0c2b87d5355a1653fed5 upstream. Don't allow everybogy to write to NVRAM. Signed-off-by: Vasiliy Kulikov Cc: Andy Sharp Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/rtc/rtc-ds1511.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c index 37268e97de49e..afeb5469708b8 100644 --- a/drivers/rtc/rtc-ds1511.c +++ b/drivers/rtc/rtc-ds1511.c @@ -485,7 +485,7 @@ ds1511_nvram_write(struct file *filp, struct kobject *kobj, static struct bin_attribute ds1511_nvram_attr = { .attr = { .name = "nvram", - .mode = S_IRUGO | S_IWUGO, + .mode = S_IRUGO | S_IWUSR, }, .size = DS1511_RAM_MAX, .read = ds1511_nvram_read, From a7f902e8c99affcb3de9afdd0e9e4cca9ada4d11 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Tue, 22 Mar 2011 16:34:01 -0700 Subject: [PATCH 1251/2556] drivers/misc/ep93xx_pwm.c: world-writable sysfs files commit deb187e72470b0382d4f0cb859e76e1ebc3a1082 upstream. Don't allow everybody to change device settings. Signed-off-by: Vasiliy Kulikov Acked-by: Hartley Sweeten Cc: Matthieu Crapet Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ep93xx_pwm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c index 46b3439673e90..16d7179e2f9b8 100644 --- a/drivers/misc/ep93xx_pwm.c +++ b/drivers/misc/ep93xx_pwm.c @@ -249,11 +249,11 @@ static ssize_t ep93xx_pwm_set_invert(struct device *dev, static DEVICE_ATTR(min_freq, S_IRUGO, ep93xx_pwm_get_min_freq, NULL); static DEVICE_ATTR(max_freq, S_IRUGO, ep93xx_pwm_get_max_freq, NULL); -static DEVICE_ATTR(freq, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(freq, S_IWUSR | S_IRUGO, ep93xx_pwm_get_freq, ep93xx_pwm_set_freq); -static DEVICE_ATTR(duty_percent, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(duty_percent, S_IWUSR | S_IRUGO, ep93xx_pwm_get_duty_percent, ep93xx_pwm_set_duty_percent); -static DEVICE_ATTR(invert, S_IWUGO | S_IRUGO, +static DEVICE_ATTR(invert, S_IWUSR | S_IRUGO, ep93xx_pwm_get_invert, ep93xx_pwm_set_invert); static struct attribute *ep93xx_pwm_attrs[] = { From c611f1951403aa7416d5df71dadbfc5199879393 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Tue, 22 Mar 2011 16:30:20 -0700 Subject: [PATCH 1252/2556] drivers/leds/leds-lp5523.c: world-writable engine* sysfs files commit ccd7510fd8dea5b4b2af87fb2aef2ebd6b23b76b upstream. Don't allow everybody to change LED settings. Signed-off-by: Vasiliy Kulikov Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/leds/leds-lp5523.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index d0c4068ecddd7..e19fed25f1376 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -713,7 +713,7 @@ static ssize_t store_current(struct device *dev, } /* led class device attributes */ -static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current, store_current); +static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current); static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); static struct attribute *lp5523_led_attributes[] = { @@ -727,21 +727,21 @@ static struct attribute_group lp5523_led_attribute_group = { }; /* device attributes */ -static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, show_engine1_mode, store_engine1_mode); -static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUSR, show_engine2_mode, store_engine2_mode); -static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUSR, show_engine3_mode, store_engine3_mode); -static DEVICE_ATTR(engine1_leds, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine1_leds, S_IRUGO | S_IWUSR, show_engine1_leds, store_engine1_leds); -static DEVICE_ATTR(engine2_leds, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine2_leds, S_IRUGO | S_IWUSR, show_engine2_leds, store_engine2_leds); -static DEVICE_ATTR(engine3_leds, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine3_leds, S_IRUGO | S_IWUSR, show_engine3_leds, store_engine3_leds); -static DEVICE_ATTR(engine1_load, S_IWUGO, NULL, store_engine1_load); -static DEVICE_ATTR(engine2_load, S_IWUGO, NULL, store_engine2_load); -static DEVICE_ATTR(engine3_load, S_IWUGO, NULL, store_engine3_load); +static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load); +static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load); +static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load); static DEVICE_ATTR(selftest, S_IRUGO, lp5523_selftest, NULL); static struct attribute *lp5523_attributes[] = { From db27bd572acc701904cc83f0ce1b4fd9996b7e55 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Tue, 22 Mar 2011 16:30:19 -0700 Subject: [PATCH 1253/2556] drivers/leds/leds-lp5521.c: world-writable sysfs engine* files commit 67d1da79b25c05d9a38b820bb5b5d89c91070ab2 upstream. Don't allow everybody to change LED settings. Signed-off-by: Vasiliy Kulikov Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/leds/leds-lp5521.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 80a3ae3c00b93..c0cff64a1ae64 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -534,7 +534,7 @@ static ssize_t lp5521_selftest(struct device *dev, } /* led class device attributes */ -static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current, store_current); +static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current); static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); static struct attribute *lp5521_led_attributes[] = { @@ -548,15 +548,15 @@ static struct attribute_group lp5521_led_attribute_group = { }; /* device attributes */ -static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, show_engine1_mode, store_engine1_mode); -static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUSR, show_engine2_mode, store_engine2_mode); -static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUGO, +static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUSR, show_engine3_mode, store_engine3_mode); -static DEVICE_ATTR(engine1_load, S_IWUGO, NULL, store_engine1_load); -static DEVICE_ATTR(engine2_load, S_IWUGO, NULL, store_engine2_load); -static DEVICE_ATTR(engine3_load, S_IWUGO, NULL, store_engine3_load); +static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load); +static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load); +static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load); static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); static struct attribute *lp5521_attributes[] = { From 93ce25bca8fd5d36ac044214a7d0adc30d5e5b3a Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Thu, 17 Mar 2011 01:40:10 +0000 Subject: [PATCH 1254/2556] econet: 4 byte infoleak to the network commit 67c5c6cb8129c595f21e88254a3fc6b3b841ae8e upstream. struct aunhdr has 4 padding bytes between 'pad' and 'handle' fields on x86_64. These bytes are not initialized in the variable 'ah' before sending 'ah' to the network. This leads to 4 bytes kernel stack infoleak. This bug was introduced before the git epoch. Signed-off-by: Vasiliy Kulikov Acked-by: Phil Blundell Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/econet/af_econet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c index 0c2826337919a..116d3fd3d6691 100644 --- a/net/econet/af_econet.c +++ b/net/econet/af_econet.c @@ -435,10 +435,10 @@ static int econet_sendmsg(struct kiocb *iocb, struct socket *sock, udpdest.sin_addr.s_addr = htonl(network | addr.station); } + memset(&ah, 0, sizeof(ah)); ah.port = port; ah.cb = cb & 0x7f; ah.code = 2; /* magic */ - ah.pad = 0; /* tack our header on the front of the iovec */ size = sizeof(struct aunhdr); From a91f85fb6adf6b03f04fb778e8f819cf414269b4 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Mon, 4 Apr 2011 15:21:02 +0200 Subject: [PATCH 1255/2556] netfilter: h323: bug in parsing of ASN1 SEQOF field commit b4232a22776aa5d063f890d21ca69870dbbe431b upstream. Static analyzer of clang found a dead store which appears to be a bug in reading count of items in SEQOF field, only the lower byte of word is stored. This may lead to corrupted read and communication shutdown. The bug has been in the module since it's first inclusion into linux kernel. [Patrick: the bug is real, but without practical consequence since the largest amount of sequence-of members we parse is 30.] Signed-off-by: David Sterba Signed-off-by: Patrick McHardy Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_h323_asn1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_h323_asn1.c b/net/netfilter/nf_conntrack_h323_asn1.c index 867882313e499..bcd5ed6b71304 100644 --- a/net/netfilter/nf_conntrack_h323_asn1.c +++ b/net/netfilter/nf_conntrack_h323_asn1.c @@ -631,7 +631,7 @@ static int decode_seqof(bitstr_t *bs, const struct field_t *f, CHECK_BOUND(bs, 2); count = *bs->cur++; count <<= 8; - count = *bs->cur++; + count += *bs->cur++; break; case SEMI: BYTE_ALIGN(bs); From a690f459204d492a4815e2179019f588654bbf0d Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Wed, 23 Mar 2011 10:53:41 -0400 Subject: [PATCH 1256/2556] sound/oss: remove offset from load_patch callbacks commit b769f49463711205d57286e64cf535ed4daf59e9 upstream. Was: [PATCH] sound/oss/midi_synth: prevent underflow, use of uninitialized value, and signedness issue The offset passed to midi_synth_load_patch() can be essentially arbitrary. If it's greater than the header length, this will result in a copy_from_user(dst, src, negative_val). While this will just return -EFAULT on x86, on other architectures this may cause memory corruption. Additionally, the length field of the sysex_info structure may not be initialized prior to its use. Finally, a signed comparison may result in an unintentionally large loop. On suggestion by Takashi Iwai, version two removes the offset argument from the load_patch callbacks entirely, which also resolves similar issues in opl3. Compile tested only. v3 adjusts comments and hopefully gets copy offsets right. Signed-off-by: Dan Rosenberg Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/oss/dev_table.h | 2 +- sound/oss/midi_synth.c | 30 +++++++++++++----------------- sound/oss/midi_synth.h | 2 +- sound/oss/opl3.c | 8 ++------ sound/oss/sequencer.c | 2 +- 5 files changed, 18 insertions(+), 26 deletions(-) diff --git a/sound/oss/dev_table.h b/sound/oss/dev_table.h index b7617bee6388f..0199a317c5a9a 100644 --- a/sound/oss/dev_table.h +++ b/sound/oss/dev_table.h @@ -271,7 +271,7 @@ struct synth_operations void (*reset) (int dev); void (*hw_control) (int dev, unsigned char *event); int (*load_patch) (int dev, int format, const char __user *addr, - int offs, int count, int pmgr_flag); + int count, int pmgr_flag); void (*aftertouch) (int dev, int voice, int pressure); void (*controller) (int dev, int voice, int ctrl_num, int value); void (*panning) (int dev, int voice, int value); diff --git a/sound/oss/midi_synth.c b/sound/oss/midi_synth.c index 3c09374ea5bf1..2292c230d7e6f 100644 --- a/sound/oss/midi_synth.c +++ b/sound/oss/midi_synth.c @@ -476,7 +476,7 @@ EXPORT_SYMBOL(midi_synth_hw_control); int midi_synth_load_patch(int dev, int format, const char __user *addr, - int offs, int count, int pmgr_flag) + int count, int pmgr_flag) { int orig_dev = synth_devs[dev]->midi_dev; @@ -491,33 +491,29 @@ midi_synth_load_patch(int dev, int format, const char __user *addr, if (!prefix_cmd(orig_dev, 0xf0)) return 0; + /* Invalid patch format */ if (format != SYSEX_PATCH) - { -/* printk("MIDI Error: Invalid patch format (key) 0x%x\n", format);*/ return -EINVAL; - } + + /* Patch header too short */ if (count < hdr_size) - { -/* printk("MIDI Error: Patch header too short\n");*/ return -EINVAL; - } + count -= hdr_size; /* - * Copy the header from user space but ignore the first bytes which have - * been transferred already. + * Copy the header from user space */ - if(copy_from_user(&((char *) &sysex)[offs], &(addr)[offs], hdr_size - offs)) + if (copy_from_user(&sysex, addr, hdr_size)) return -EFAULT; - - if (count < sysex.len) - { -/* printk(KERN_WARNING "MIDI Warning: Sysex record too short (%d<%d)\n", count, (int) sysex.len);*/ + + /* Sysex record too short */ + if ((unsigned)count < (unsigned)sysex.len) sysex.len = count; - } - left = sysex.len; - src_offs = 0; + + left = sysex.len; + src_offs = 0; for (i = 0; i < left && !signal_pending(current); i++) { diff --git a/sound/oss/midi_synth.h b/sound/oss/midi_synth.h index 6bc9d00bc77c4..b64ddd6c4abc0 100644 --- a/sound/oss/midi_synth.h +++ b/sound/oss/midi_synth.h @@ -8,7 +8,7 @@ int midi_synth_open (int dev, int mode); void midi_synth_close (int dev); void midi_synth_hw_control (int dev, unsigned char *event); int midi_synth_load_patch (int dev, int format, const char __user * addr, - int offs, int count, int pmgr_flag); + int count, int pmgr_flag); void midi_synth_panning (int dev, int channel, int pressure); void midi_synth_aftertouch (int dev, int channel, int pressure); void midi_synth_controller (int dev, int channel, int ctrl_num, int value); diff --git a/sound/oss/opl3.c b/sound/oss/opl3.c index f4ffdff9b2451..407cd677950bc 100644 --- a/sound/oss/opl3.c +++ b/sound/oss/opl3.c @@ -820,7 +820,7 @@ static void opl3_hw_control(int dev, unsigned char *event) } static int opl3_load_patch(int dev, int format, const char __user *addr, - int offs, int count, int pmgr_flag) + int count, int pmgr_flag) { struct sbi_instrument ins; @@ -830,11 +830,7 @@ static int opl3_load_patch(int dev, int format, const char __user *addr, return -EINVAL; } - /* - * What the fuck is going on here? We leave junk in the beginning - * of ins and then check the field pretty close to that beginning? - */ - if(copy_from_user(&((char *) &ins)[offs], addr + offs, sizeof(ins) - offs)) + if (copy_from_user(&ins, addr, sizeof(ins))) return -EFAULT; if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) diff --git a/sound/oss/sequencer.c b/sound/oss/sequencer.c index 5ea1098ac427a..30bcfe470f831 100644 --- a/sound/oss/sequencer.c +++ b/sound/oss/sequencer.c @@ -241,7 +241,7 @@ int sequencer_write(int dev, struct file *file, const char __user *buf, int coun return -ENXIO; fmt = (*(short *) &event_rec[0]) & 0xffff; - err = synth_devs[dev]->load_patch(dev, fmt, buf, p + 4, c, 0); + err = synth_devs[dev]->load_patch(dev, fmt, buf + p, c, 0); if (err < 0) return err; From 45a01b6969b3508eddcf7cc406d366efecdf5133 Mon Sep 17 00:00:00 2001 From: Alexander Strakh Date: Tue, 25 Jan 2011 18:00:13 -0300 Subject: [PATCH 1257/2556] drivers/media/video/tlg2300/pd-video.c: Remove second mutex_unlock in pd_vidioc_s_fmt commit a07500ef690fcbec76e879ee2093d7ca69883825 upstream. Error path in file drivers/media/video/tlg2300/pd-video.c: 1. First mutex_unlock on &pd->lock in line 767 (in function that called from line 805) 2. Second in line 806 805 pd_vidioc_s_fmt(pd, &f->fmt.pix); 806 mutex_unlock(&pd->lock); Found by Linux Device Drivers Verification Project Signed-off-by: Alexander Strakh Acked-by: Huang Shijie Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/video/tlg2300/pd-video.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c index df33a1d188bbf..a794ae62aebfc 100644 --- a/drivers/media/video/tlg2300/pd-video.c +++ b/drivers/media/video/tlg2300/pd-video.c @@ -764,10 +764,8 @@ static int pd_vidioc_s_fmt(struct poseidon *pd, struct v4l2_pix_format *pix) } ret |= send_set_req(pd, VIDEO_ROSOLU_SEL, vid_resol, &cmd_status); - if (ret || cmd_status) { - mutex_unlock(&pd->lock); + if (ret || cmd_status) return -EBUSY; - } pix_def->pixelformat = pix->pixelformat; /* save it */ pix->height = (context->tvnormid & V4L2_STD_525_60) ? 480 : 576; From e9740f5811a9e336a629fbbe5fe5ebe15f9c00ee Mon Sep 17 00:00:00 2001 From: Chun-Yi Lee Date: Mon, 28 Mar 2011 16:52:02 +0800 Subject: [PATCH 1258/2556] acer-wmi: does not set persistence state by rfkill_init_sw_state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 8215af019040ce9182728afee9642d8fdeb17f59 upstream. Acer BIOS keeps devices state when system reboot, but reset to default device states (Wlan on, Bluetooth off, wwan on) if system cold boot. That means BIOS's initial state is not always real persistence. So, removed rfkill_init_sw_state because it sets initial state to persistence then replicate to other new killswitch when rfkill-input enabled. After removed it, acer-wmi set initial soft-block state after rfkill register, and doesn't allow set_block until rfkill initial finished. Reference: bko#31002 https://bugzilla.kernel.org/show_bug.cgi?id=31002 Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Dmitry Torokhov Cc: Corentin Chary Cc: Oldřich Jedlička Cc: Johannes Berg Signed-off-by: Chun-Yi Lee Signed-off-by: Matthew Garrett Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/acer-wmi.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 38b34a73866a8..fa54ba70657ae 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -222,6 +222,7 @@ struct acer_debug { static struct rfkill *wireless_rfkill; static struct rfkill *bluetooth_rfkill; static struct rfkill *threeg_rfkill; +static bool rfkill_inited; /* Each low-level interface must define at least some of the following */ struct wmi_interface { @@ -1161,9 +1162,13 @@ static int acer_rfkill_set(void *data, bool blocked) { acpi_status status; u32 cap = (unsigned long)data; - status = set_u32(!blocked, cap); - if (ACPI_FAILURE(status)) - return -ENODEV; + + if (rfkill_inited) { + status = set_u32(!blocked, cap); + if (ACPI_FAILURE(status)) + return -ENODEV; + } + return 0; } @@ -1187,14 +1192,16 @@ static struct rfkill *acer_rfkill_register(struct device *dev, return ERR_PTR(-ENOMEM); status = get_device_status(&state, cap); - if (ACPI_SUCCESS(status)) - rfkill_init_sw_state(rfkill_dev, !state); err = rfkill_register(rfkill_dev); if (err) { rfkill_destroy(rfkill_dev); return ERR_PTR(err); } + + if (ACPI_SUCCESS(status)) + rfkill_set_sw_state(rfkill_dev, !state); + return rfkill_dev; } @@ -1229,6 +1236,8 @@ static int acer_rfkill_init(struct device *dev) } } + rfkill_inited = true; + schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); return 0; From 27fadaa861246727ad5919863281677a36231dcf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 11 Apr 2011 16:01:32 -0700 Subject: [PATCH 1259/2556] Revert "x86: Cleanup highmap after brk is concluded" This reverts upstream commit e5f15b45ddf3afa2bbbb10c7ea34fb32b6de0a0e It caused problems in the stable tree and should not have been there. Cc: Yinghai Lu Cc: Stefano Stabellini Cc: H. Peter Anvin Cc: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/head64.c | 3 +++ arch/x86/kernel/setup.c | 5 ----- arch/x86/mm/init.c | 19 +++++++++++++++++++ arch/x86/mm/init_64.c | 11 +++++------ 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index 5655c2272adb8..2d2673c28aff2 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c @@ -77,6 +77,9 @@ void __init x86_64_start_kernel(char * real_mode_data) /* Make NULL pointers segfault */ zap_identity_mappings(); + /* Cleanup the over mapped high alias */ + cleanup_highmap(); + max_pfn_mapped = KERNEL_IMAGE_SIZE >> PAGE_SHIFT; for (i = 0; i < NUM_EXCEPTION_VECTORS; i++) { diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index e543fe9311e4a..d3cfe26c0252a 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -297,9 +297,6 @@ static void __init init_gbpages(void) static inline void init_gbpages(void) { } -static void __init cleanup_highmap(void) -{ -} #endif static void __init reserve_brk(void) @@ -925,8 +922,6 @@ void __init setup_arch(char **cmdline_p) */ reserve_brk(); - cleanup_highmap(); - memblock.current_limit = get_max_mapped(); memblock_x86_fill(); diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index f13ff3a226736..947f42abe820e 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -279,6 +279,25 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, load_cr3(swapper_pg_dir); #endif +#ifdef CONFIG_X86_64 + if (!after_bootmem && !start) { + pud_t *pud; + pmd_t *pmd; + + mmu_cr4_features = read_cr4(); + + /* + * _brk_end cannot change anymore, but it and _end may be + * located on different 2M pages. cleanup_highmap(), however, + * can only consider _end when it runs, so destroy any + * mappings beyond _brk_end here. + */ + pud = pud_offset(pgd_offset_k(_brk_end), _brk_end); + pmd = pmd_offset(pud, _brk_end - 1); + while (++pmd <= pmd_offset(pud, (unsigned long)_end - 1)) + pmd_clear(pmd); + } +#endif __flush_tlb_all(); if (!after_bootmem && e820_table_end > e820_table_start) diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 68f9921ae9c16..c14a5422e1522 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -51,7 +51,6 @@ #include #include #include -#include static int __init parse_direct_gbpages_off(char *arg) { @@ -294,18 +293,18 @@ void __init init_extra_mapping_uc(unsigned long phys, unsigned long size) * to the compile time generated pmds. This results in invalid pmds up * to the point where we hit the physaddr 0 mapping. * - * We limit the mappings to the region from _text to _brk_end. _brk_end - * is rounded up to the 2MB boundary. This catches the invalid pmds as + * We limit the mappings to the region from _text to _end. _end is + * rounded up to the 2MB boundary. This catches the invalid pmds as * well, as they are located before _text: */ void __init cleanup_highmap(void) { unsigned long vaddr = __START_KERNEL_map; - unsigned long vaddr_end = __START_KERNEL_map + (max_pfn_mapped << PAGE_SHIFT); - unsigned long end = roundup((unsigned long)_brk_end, PMD_SIZE) - 1; + unsigned long end = roundup((unsigned long)_end, PMD_SIZE) - 1; pmd_t *pmd = level2_kernel_pgt; + pmd_t *last_pmd = pmd + PTRS_PER_PMD; - for (; vaddr + PMD_SIZE - 1 < vaddr_end; pmd++, vaddr += PMD_SIZE) { + for (; pmd < last_pmd; pmd++, vaddr += PMD_SIZE) { if (pmd_none(*pmd)) continue; if (vaddr < (unsigned long) _text || vaddr > end) From 3170c7d866308867848b6eeca2fcf15e23c752e9 Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Tue, 22 Mar 2011 23:01:26 +0000 Subject: [PATCH 1260/2556] Squashfs: Use vmalloc rather than kmalloc for zlib workspace commit 117a91e0f25fd7698e20ac3dfa62086be3dc82a3 upstream. Bugzilla bug 31422 reports occasional "page allocation failure. order:4" at Squashfs mount time. Fix this by making zlib workspace allocation use vmalloc rather than kmalloc. Reported-by: Mehmet Giritli Signed-off-by: Phillip Lougher Signed-off-by: Greg Kroah-Hartman --- fs/squashfs/zlib_wrapper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c index 4661ae2b1cec8..04ae9a5b70a66 100644 --- a/fs/squashfs/zlib_wrapper.c +++ b/fs/squashfs/zlib_wrapper.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "squashfs_fs.h" #include "squashfs_fs_sb.h" @@ -37,8 +38,7 @@ static void *zlib_init(struct squashfs_sb_info *dummy) z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL); if (stream == NULL) goto failed; - stream->workspace = kmalloc(zlib_inflate_workspacesize(), - GFP_KERNEL); + stream->workspace = vmalloc(zlib_inflate_workspacesize()); if (stream->workspace == NULL) goto failed; @@ -56,7 +56,7 @@ static void zlib_free(void *strm) z_stream *stream = strm; if (stream) - kfree(stream->workspace); + vfree(stream->workspace); kfree(stream); } From e8ac4f67e533c9571ae06f8c6c4b543faeaf13a8 Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Tue, 15 Mar 2011 22:09:55 +0000 Subject: [PATCH 1261/2556] Squashfs: handle corruption of directory structure commit 44cff8a9ee8a974f9e931df910688e7fc1f0b0f9 upstream. Handle the rare case where a directory metadata block is uncompressed and corrupted, leading to a kernel oops in directory scanning (memcpy). Normally corruption is detected at the decompression stage and dealt with then, however, this will not happen if: - metadata isn't compressed (users can optionally request no metadata compression), or - the compressed metadata block was larger than the original, in which case the uncompressed version was used, or - the data was corrupt after decompression This patch fixes this by adding some sanity checks against known maximum values. Signed-off-by: Phillip Lougher Signed-off-by: Greg Kroah-Hartman --- fs/squashfs/dir.c | 9 +++++++++ fs/squashfs/namei.c | 12 ++++++++++++ 2 files changed, 21 insertions(+) diff --git a/fs/squashfs/dir.c b/fs/squashfs/dir.c index 0dc340aa2be97..3f79cd1d0c197 100644 --- a/fs/squashfs/dir.c +++ b/fs/squashfs/dir.c @@ -172,6 +172,11 @@ static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir) length += sizeof(dirh); dir_count = le32_to_cpu(dirh.count) + 1; + + /* dir_count should never be larger than 256 */ + if (dir_count > 256) + goto failed_read; + while (dir_count--) { /* * Read directory entry. @@ -183,6 +188,10 @@ static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir) size = le16_to_cpu(dire->size) + 1; + /* size should never be larger than SQUASHFS_NAME_LEN */ + if (size > SQUASHFS_NAME_LEN) + goto failed_read; + err = squashfs_read_metadata(inode->i_sb, dire->name, &block, &offset, size); if (err < 0) diff --git a/fs/squashfs/namei.c b/fs/squashfs/namei.c index 7a9464d08cf63..5d922a6701ab7 100644 --- a/fs/squashfs/namei.c +++ b/fs/squashfs/namei.c @@ -176,6 +176,11 @@ static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry, length += sizeof(dirh); dir_count = le32_to_cpu(dirh.count) + 1; + + /* dir_count should never be larger than 256 */ + if (dir_count > 256) + goto data_error; + while (dir_count--) { /* * Read directory entry. @@ -187,6 +192,10 @@ static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry, size = le16_to_cpu(dire->size) + 1; + /* size should never be larger than SQUASHFS_NAME_LEN */ + if (size > SQUASHFS_NAME_LEN) + goto data_error; + err = squashfs_read_metadata(dir->i_sb, dire->name, &block, &offset, size); if (err < 0) @@ -228,6 +237,9 @@ static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry, d_add(dentry, inode); return ERR_PTR(0); +data_error: + err = -EIO; + read_failure: ERROR("Unable to read directory block [%llx:%x]\n", squashfs_i(dir)->start + msblk->directory_table, From 0c9003789f56621127cac4547e78cb490e174682 Mon Sep 17 00:00:00 2001 From: "Philip A. Prindeville" Date: Wed, 30 Mar 2011 12:59:26 +0000 Subject: [PATCH 1262/2556] atm/solos-pci: Don't include frame pseudo-header on transmit hex-dump commit 18b429e74eeafe42e947b1b0f9a760c7153a0b5c upstream. Omit pkt_hdr preamble when dumping transmitted packet as hex-dump; we can pull this up because the frame has already been sent, and dumping it is the last thing we do with it before freeing it. Also include the size, vpi, and vci in the debug as is done on receive. Use "port" consistently instead of "device" intermittently. Signed-off-by: Philip Prindeville Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/atm/solos-pci.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index 25ef1a4556e62..de738e65c047f 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -697,7 +697,7 @@ void solos_bh(unsigned long card_arg) size); } if (atmdebug) { - dev_info(&card->dev->dev, "Received: device %d\n", port); + dev_info(&card->dev->dev, "Received: port %d\n", port); dev_info(&card->dev->dev, "size: %d VPI: %d VCI: %d\n", size, le16_to_cpu(header->vpi), le16_to_cpu(header->vci)); @@ -1018,8 +1018,15 @@ static uint32_t fpga_tx(struct solos_card *card) /* Clean up and free oldskb now it's gone */ if (atmdebug) { + struct pkt_hdr *header = (void *)oldskb->data; + int size = le16_to_cpu(header->size); + + skb_pull(oldskb, sizeof(*header)); dev_info(&card->dev->dev, "Transmitted: port %d\n", port); + dev_info(&card->dev->dev, "size: %d VPI: %d VCI: %d\n", + size, le16_to_cpu(header->vpi), + le16_to_cpu(header->vci)); print_buffer(oldskb); } From 77f74c355086d3fe62f20dd117b9053eb09db139 Mon Sep 17 00:00:00 2001 From: "Philip A. Prindeville" Date: Wed, 30 Mar 2011 13:17:04 +0000 Subject: [PATCH 1263/2556] atm/solos-pci: Don't flap VCs when carrier state changes commit c031235b395433350f25943b7580a5e343c7b7b2 upstream. Don't flap VCs when carrier state changes; higher-level protocols can detect loss of connectivity and act accordingly. This is more consistent with how other network interfaces work. We no longer use release_vccs() so we can delete it. release_vccs() was duplicated from net/atm/common.c; make the corresponding function exported, since other code duplicates it and could leverage it if it were public. Signed-off-by: Philip A. Prindeville Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/atm/solos-pci.c | 26 +------------------------- include/linux/atmdev.h | 1 + net/atm/common.c | 1 + 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index de738e65c047f..b836e11a8a34e 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -165,7 +165,6 @@ static uint32_t fpga_tx(struct solos_card *); static irqreturn_t solos_irq(int irq, void *dev_id); static struct atm_vcc* find_vcc(struct atm_dev *dev, short vpi, int vci); static int list_vccs(int vci); -static void release_vccs(struct atm_dev *dev); static int atm_init(struct solos_card *, struct device *); static void atm_remove(struct solos_card *); static int send_command(struct solos_card *card, int dev, const char *buf, size_t size); @@ -384,7 +383,6 @@ static int process_status(struct solos_card *card, int port, struct sk_buff *skb /* Anything but 'Showtime' is down */ if (strcmp(state_str, "Showtime")) { atm_dev_signal_change(card->atmdev[port], ATM_PHY_SIG_LOST); - release_vccs(card->atmdev[port]); dev_info(&card->dev->dev, "Port %d: %s\n", port, state_str); return 0; } @@ -830,28 +828,6 @@ static int list_vccs(int vci) return num_found; } -static void release_vccs(struct atm_dev *dev) -{ - int i; - - write_lock_irq(&vcc_sklist_lock); - for (i = 0; i < VCC_HTABLE_SIZE; i++) { - struct hlist_head *head = &vcc_hash[i]; - struct hlist_node *node, *tmp; - struct sock *s; - struct atm_vcc *vcc; - - sk_for_each_safe(s, node, tmp, head) { - vcc = atm_sk(s); - if (vcc->dev == dev) { - vcc_release_async(vcc, -EPIPE); - sk_del_node_init(s); - } - } - } - write_unlock_irq(&vcc_sklist_lock); -} - static int popen(struct atm_vcc *vcc) { @@ -1269,7 +1245,7 @@ static int atm_init(struct solos_card *card, struct device *parent) card->atmdev[i]->ci_range.vci_bits = 16; card->atmdev[i]->dev_data = card; card->atmdev[i]->phy_data = (void *)(unsigned long)i; - atm_dev_signal_change(card->atmdev[i], ATM_PHY_SIG_UNKNOWN); + atm_dev_signal_change(card->atmdev[i], ATM_PHY_SIG_FOUND); skb = alloc_skb(sizeof(*header), GFP_ATOMIC); if (!skb) { diff --git a/include/linux/atmdev.h b/include/linux/atmdev.h index 475f8c42c0e92..381f4cec82602 100644 --- a/include/linux/atmdev.h +++ b/include/linux/atmdev.h @@ -443,6 +443,7 @@ void atm_dev_signal_change(struct atm_dev *dev, char signal); void vcc_insert_socket(struct sock *sk); +void atm_dev_release_vccs(struct atm_dev *dev); /* * This is approximately the algorithm used by alloc_skb. diff --git a/net/atm/common.c b/net/atm/common.c index 1b9c52a02cd31..22b963d06a10b 100644 --- a/net/atm/common.c +++ b/net/atm/common.c @@ -252,6 +252,7 @@ void atm_dev_release_vccs(struct atm_dev *dev) } write_unlock_irq(&vcc_sklist_lock); } +EXPORT_SYMBOL(atm_dev_release_vccs); static int adjust_tp(struct atm_trafprm *tp, unsigned char aal) { From 5e30c16023a166237224448f67246742427a77ef Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Mon, 4 Apr 2011 16:00:49 -0400 Subject: [PATCH 1264/2556] ext4: fix a double free in ext4_register_li_request commit 46e4690bbd9a4f8d9e7c4f34e34b48f703ad47e0 upstream. In ext4_register_li_request, we malloc a ext4_li_request and inserts it into ext4_li_info->li_request_list. In case of any error later, we free it in the end. But if we have some error in ext4_run_lazyinit_thread, the whole li_request_list will be dropped and freed in it. So we will double free this ext4_li_request. This patch just sets elr to NULL after it is inserted to the list so that the latter kfree won't double free it. Signed-off-by: Tao Ma Reviewed-by: Lukas Czerner Signed-off-by: "Theodore Ts'o" Signed-off-by: Greg Kroah-Hartman --- fs/ext4/super.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 4381efee3dbc1..243deb021e7b7 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2978,6 +2978,12 @@ static int ext4_register_li_request(struct super_block *sb, mutex_unlock(&ext4_li_info->li_list_mtx); sbi->s_li_request = elr; + /* + * set elr to NULL here since it has been inserted to + * the request_list and the removal and free of it is + * handled by ext4_clear_request_list from now on. + */ + elr = NULL; if (!(ext4_li_info->li_state & EXT4_LAZYINIT_RUNNING)) { ret = ext4_run_lazyinit_thread(); From 31015356ff05612b3f5648d323cea9d743783f15 Mon Sep 17 00:00:00 2001 From: Yongqiang Yang Date: Mon, 4 Apr 2011 15:40:24 -0400 Subject: [PATCH 1265/2556] ext4: fix credits computing for indirect mapped files commit 5b41395fcc0265fc9f193aef9df39ce49d64677c upstream. When writing a contiguous set of blocks, two indirect blocks could be needed depending on how the blocks are aligned, so we need to increase the number of credits needed by one. [ Also fixed a another bug which could further underestimate the number of journal credits needed by 1; the code was using integer division instead of DIV_ROUND_UP() -- tytso] Signed-off-by: Yongqiang Yang Signed-off-by: "Theodore Ts'o" Signed-off-by: Greg Kroah-Hartman --- fs/ext4/inode.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 9f7f9e49914fa..fee51dbf74dcd 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5460,13 +5460,12 @@ static int ext4_indirect_trans_blocks(struct inode *inode, int nrblocks, /* if nrblocks are contiguous */ if (chunk) { /* - * With N contiguous data blocks, it need at most - * N/EXT4_ADDR_PER_BLOCK(inode->i_sb) indirect blocks - * 2 dindirect blocks - * 1 tindirect block + * With N contiguous data blocks, we need at most + * N/EXT4_ADDR_PER_BLOCK(inode->i_sb) + 1 indirect blocks, + * 2 dindirect blocks, and 1 tindirect block */ - indirects = nrblocks / EXT4_ADDR_PER_BLOCK(inode->i_sb); - return indirects + 3; + return DIV_ROUND_UP(nrblocks, + EXT4_ADDR_PER_BLOCK(inode->i_sb)) + 4; } /* * if nrblocks are not contiguous, worse case, each block touch From 27829fed6c02aa689f6ef98256180f67d4df65de Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 24 Mar 2011 22:51:14 -0400 Subject: [PATCH 1266/2556] nfsd: fix auth_domain reference leak on nlm operations commit 954032d2527f2fce7355ba70709b5e143d6b686f upstream. This was noticed by users who performed more than 2^32 lock operations and hence made this counter overflow (eventually leading to use-after-free's). Setting rq_client to NULL here means that it won't later get auth_domain_put() when it should be. Appears to have been introduced in 2.5.42 by "[PATCH] kNFSd: Move auth domain lookup into svcauth" which moved most of the rq_client handling to common svcauth code, but left behind this one line. Cc: Neil Brown Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/lockd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c index 0c6d816701374..7c831a2731fa1 100644 --- a/fs/nfsd/lockd.c +++ b/fs/nfsd/lockd.c @@ -38,7 +38,6 @@ nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp) exp_readlock(); nfserr = nfsd_open(rqstp, &fh, S_IFREG, NFSD_MAY_LOCK, filp); fh_put(&fh); - rqstp->rq_client = NULL; exp_readunlock(); /* We return nlm error codes as nlm doesn't know * about nfsd, but nfsd does know about nlm.. From 6b0e89f098957148561d732a14566b50281e24d4 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 28 Mar 2011 15:15:09 +0800 Subject: [PATCH 1267/2556] nfsd4: fix oops on lock failure commit 23fcf2ec93fb8573a653408316af599939ff9a8e upstream. Lock stateid's can have access_bmap 0 if they were only partially initialized (due to a failed lock request); handle that case in free_generic_stateid. ------------[ cut here ]------------ kernel BUG at fs/nfsd/nfs4state.c:380! invalid opcode: 0000 [#1] SMP last sysfs file: /sys/kernel/mm/ksm/run Modules linked in: nfs fscache md4 nls_utf8 cifs ip6table_filter ip6_tables ebtable_nat ebtables ipt_MASQUERADE iptable_nat nf_nat bridge stp llc nfsd lockd nfs_acl auth_rpcgss sunrpc ipv6 ppdev parport_pc parport pcnet32 mii pcspkr microcode i2c_piix4 BusLogic floppy [last unloaded: mperf] Pid: 1468, comm: nfsd Not tainted 2.6.38+ #120 VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform EIP: 0060:[] EFLAGS: 00010297 CPU: 0 EIP is at nfs4_access_to_omode+0x1c/0x29 [nfsd] EAX: ffffffff EBX: dd758120 ECX: 00000000 EDX: 00000004 ESI: dd758120 EDI: ddfe657c EBP: dd54dde0 ESP: dd54dde0 DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 Process nfsd (pid: 1468, ti=dd54c000 task=ddc92580 task.ti=dd54c000) Stack: dd54ddf0 e24f19ca 00000000 ddfe6560 dd54de08 e24f1a5d dd758130 deee3a20 ddfe6560 31270000 dd54df1c e24f52fd 0000000f dd758090 e2505dd0 0be304cf dbb51d68 0000000e ddfe657c ddcd8020 dd758130 dd758128 dd7580d8 dd54de68 Call Trace: [] free_generic_stateid+0x1c/0x3e [nfsd] [] release_lockowner+0x71/0x8a [nfsd] [] nfsd4_lock+0x617/0x66c [nfsd] [] ? nfsd_setuser+0x199/0x1bb [nfsd] [] ? nfsd_setuser_and_check_port+0x65/0x81 [nfsd] [] ? _cond_resched+0x8/0x1c [] ? slab_pre_alloc_hook.clone.33+0x23/0x27 [] ? kmem_cache_alloc+0x1a/0xd2 [] ? __call_rcu+0xd7/0xdd [] ? fh_verify+0x401/0x452 [nfsd] [] ? nfsd4_encode_operation+0x52/0x117 [nfsd] [] ? nfsd4_putfh+0x33/0x3b [nfsd] [] ? nfsd4_delegreturn+0xd4/0xd4 [nfsd] [] nfsd4_proc_compound+0x1ea/0x33e [nfsd] [] nfsd_dispatch+0xd1/0x1a5 [nfsd] [] svc_process_common+0x282/0x46f [sunrpc] [] svc_process+0xdc/0xfa [sunrpc] [] nfsd+0xd6/0x115 [nfsd] [] ? nfsd_shutdown+0x24/0x24 [nfsd] [] kthread+0x62/0x67 [] ? kthread_worker_fn+0x114/0x114 [] kernel_thread_helper+0x6/0x10 Code: eb 05 b8 00 00 27 4f 8d 65 f4 5b 5e 5f 5d c3 83 e0 03 55 83 f8 02 89 e5 74 17 83 f8 03 74 05 48 75 09 eb 09 b8 02 00 00 00 eb 0b <0f> 0b 31 c0 eb 05 b8 01 00 00 00 5d c3 55 89 e5 57 56 89 d6 8d EIP: [] nfs4_access_to_omode+0x1c/0x29 [nfsd] SS:ESP 0068:dd54dde0 ---[ end trace 2b0bf6c6557cb284 ]--- The trace route is: -> nfsd4_lock() -> if (lock->lk_is_new) { -> alloc_init_lock_stateid() 3739: stp->st_access_bmap = 0; ->if (status && lock->lk_is_new && lock_sop) -> release_lockowner() -> free_generic_stateid() -> nfs4_access_bmap_to_omode() -> nfs4_access_to_omode() 380: BUG(); ***** This problem was introduced by 0997b173609b9229ece28941c118a2a9b278796e. Reported-by: Mi Jinlong Tested-by: Mi Jinlong Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4state.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index f0e448a512c63..96aaaa47fd0a3 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -397,10 +397,13 @@ static void unhash_generic_stateid(struct nfs4_stateid *stp) static void free_generic_stateid(struct nfs4_stateid *stp) { - int oflag = nfs4_access_bmap_to_omode(stp); + int oflag; - nfs4_file_put_access(stp->st_file, oflag); - put_nfs4_file(stp->st_file); + if (stp->st_access_bmap) { + oflag = nfs4_access_bmap_to_omode(stp); + nfs4_file_put_access(stp->st_file, oflag); + put_nfs4_file(stp->st_file); + } kmem_cache_free(stateid_slab, stp); } From 88e22b908979f438b41f52120bec891f1d4873ff Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 14 Apr 2011 13:03:56 -0700 Subject: [PATCH 1268/2556] Linux 2.6.38.3 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2372fed455d86..5728aed8b764b 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 38 -EXTRAVERSION = .2 +EXTRAVERSION = .3 NAME = Flesh-Eating Bats with Fangs # *DOCUMENTATION* From 833923adc70f9493f0860ffff7e326612c1ff513 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 13 Apr 2011 08:07:28 -0700 Subject: [PATCH 1269/2556] vm: fix vm_pgoff wrap in stack expansion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a626ca6a656450e9f4df91d0dda238fff23285f4 upstream. Commit 982134ba6261 ("mm: avoid wrapping vm_pgoff in mremap()") fixed the case of a expanding mapping causing vm_pgoff wrapping when you used mremap. But there was another case where we expand mappings hiding in plain sight: the automatic stack expansion. This fixes that case too. This one also found by Robert Święcki, using his nasty system call fuzzer tool. Good job. Reported-and-tested-by: Robert Święcki Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/mmap.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/mm/mmap.c b/mm/mmap.c index 2ec8eb5a9cdd0..8c05e5b43b69c 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1814,11 +1814,14 @@ static int expand_downwards(struct vm_area_struct *vma, size = vma->vm_end - address; grow = (vma->vm_start - address) >> PAGE_SHIFT; - error = acct_stack_growth(vma, size, grow); - if (!error) { - vma->vm_start = address; - vma->vm_pgoff -= grow; - perf_event_mmap(vma); + error = -ENOMEM; + if (grow <= vma->vm_pgoff) { + error = acct_stack_growth(vma, size, grow); + if (!error) { + vma->vm_start = address; + vma->vm_pgoff -= grow; + perf_event_mmap(vma); + } } } vma_unlock_anon_vma(vma); From 5ce1071c2390f8a27569bf9b41f8038ded424dff Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 7 Apr 2011 10:31:25 -0400 Subject: [PATCH 1270/2556] drm/radeon/kms: pll tweaks for rv6xx commit 9bb09fa1b5b07459279301ac6220d575f307597b upstream. Prefer minm over maxp. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=35994 Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/atombios_crtc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index e967cc869b3b2..08612572c5581 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -531,6 +531,12 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; else pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV; + + if ((rdev->family == CHIP_R600) || + (rdev->family == CHIP_RV610) || + (rdev->family == CHIP_RV630) || + (rdev->family == CHIP_RV670)) + pll->flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP; } else { pll->flags |= RADEON_PLL_LEGACY; From 9b659653fa4b6fc626ba31f82c7cb57e6bdd6089 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 12 Apr 2011 13:33:27 -0400 Subject: [PATCH 1271/2556] drm/radeon/kms: fix suspend on rv530 asics commit 71e16bfbd2b1c63d4d97cc5059694c9346aee340 upstream. Apparently only rv515 asics need the workaround added in f24d86f1a49505cdea56728b853a5d0a3f8e3d11 (drm/radeon/kms: fix resume regression for some r5xx laptops). Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=34709 Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/atom.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c index 258fa5e7a2d9a..d71d375149f85 100644 --- a/drivers/gpu/drm/radeon/atom.c +++ b/drivers/gpu/drm/radeon/atom.c @@ -32,6 +32,7 @@ #include "atom.h" #include "atom-names.h" #include "atom-bits.h" +#include "radeon.h" #define ATOM_COND_ABOVE 0 #define ATOM_COND_ABOVEOREQUAL 1 @@ -101,7 +102,9 @@ static void debug_print_spaces(int n) static uint32_t atom_iio_execute(struct atom_context *ctx, int base, uint32_t index, uint32_t data) { + struct radeon_device *rdev = ctx->card->dev->dev_private; uint32_t temp = 0xCDCDCDCD; + while (1) switch (CU8(base)) { case ATOM_IIO_NOP: @@ -112,7 +115,8 @@ static uint32_t atom_iio_execute(struct atom_context *ctx, int base, base += 3; break; case ATOM_IIO_WRITE: - (void)ctx->card->ioreg_read(ctx->card, CU16(base + 1)); + if (rdev->family == CHIP_RV515) + (void)ctx->card->ioreg_read(ctx->card, CU16(base + 1)); ctx->card->ioreg_write(ctx->card, CU16(base + 1), temp); base += 3; break; From 1bd6bcd950ab6bd208a4748ff593d612a21b4284 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 14 Mar 2011 13:48:08 -0400 Subject: [PATCH 1272/2556] cifs: always do is_path_accessible check in cifs_mount commit 70945643722ffeac779d2529a348f99567fa5c33 upstream. Currently, we skip doing the is_path_accessible check in cifs_mount if there is no prefixpath. I have a report of at least one server however that allows a TREE_CONNECT to a share that has a DFS referral at its root. The reporter in this case was using a UNC that had no prefixpath, so the is_path_accessible check was not triggered and the box later hit a BUG() because we were chasing a DFS referral on the root dentry for the mount. This patch fixes this by removing the check for a zero-length prefixpath. That should make the is_path_accessible check be done in this situation and should allow the client to chase the DFS referral at mount time instead. Reported-and-Tested-by: Yogesh Sharma Signed-off-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/connect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 8d6c17ab593da..d3d836d9c5a29 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2826,7 +2826,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, remote_path_check: /* check if a whole path (including prepath) is not remote */ - if (!rc && cifs_sb->prepathlen && tcon) { + if (!rc && tcon) { /* build_path_to_root works only when we have a valid tcon */ full_path = cifs_build_path_to_root(cifs_sb, tcon); if (full_path == NULL) { From 7cb41edf395b70a6e48162382c5577967ce92bbf Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 5 Apr 2011 16:23:47 -0700 Subject: [PATCH 1273/2556] cifs: check for private_data before trying to put it commit 7797069305d13252fd66cf722aa8f2cbeb3c95cd upstream. cifs_close doesn't check that the filp->private_data is non-NULL before trying to put it. That can cause an oops in certain error conditions that can occur on open or lookup before the private_data is set. Reported-by: Ben Greear Signed-off-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/file.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index e964b1cd5dd09..7b2e8ec2709b3 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -575,8 +575,10 @@ static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush) int cifs_close(struct inode *inode, struct file *file) { - cifsFileInfo_put(file->private_data); - file->private_data = NULL; + if (file->private_data != NULL) { + cifsFileInfo_put(file->private_data); + file->private_data = NULL; + } /* return code from the ->release op is always ignored */ return 0; From 591c80b5a9a9331a9b2a6d1b7d9b97d3445107c9 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 25 Mar 2011 16:25:57 -0400 Subject: [PATCH 1274/2556] cifs: set ra_pages in backing_dev_info commit 2b6c26a0a62cc0bab0ad487533d5581d7c293fef upstream. Commit 522440ed made cifs set backing_dev_info on the mapping attached to new inodes. This change caused a fairly significant read performance regression, as cifs started doing page-sized reads exclusively. By virtue of the fact that they're allocated as part of cifs_sb_info by kzalloc, the ra_pages on cifs BDIs get set to 0, which prevents any readahead. This forces the normal read codepaths to use readpage instead of readpages causing a four-fold increase in the number of read calls with the default rsize. Fix it by setting ra_pages in the BDI to the same value as that in the default_backing_dev_info. Fixes https://bugzilla.kernel.org/show_bug.cgi?id=31662 Reported-and-Tested-by: Till Signed-off-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/cifsfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index f2970136d17d0..c0def4f5602a7 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -127,6 +127,7 @@ cifs_read_super(struct super_block *sb, void *data, kfree(cifs_sb); return rc; } + cifs_sb->bdi.ra_pages = default_backing_dev_info.ra_pages; #ifdef CONFIG_CIFS_DFS_UPCALL /* copy mount params to sb for use in submounts */ From 3b76dafa88d04ea91e9b16a4cd7489977d60602b Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sat, 2 Apr 2011 07:34:30 -0400 Subject: [PATCH 1275/2556] cifs: wrap received signature check in srv_mutex commit 157c249114508aa71daa308a426e15d81a4eed00 upstream. While testing my patchset to fix asynchronous writes, I hit a bunch of signature problems when testing with signing on. The problem seems to be that signature checks on receive can be running at the same time as a process that is sending, or even that multiple receives can be checking signatures at the same time, clobbering the same data structures. While we're at it, clean up the comments over cifs_calculate_signature and add a note that the srv_mutex should be held when calling this function. This patch seems to fix the problems for me, but I'm not clear on whether it's the best approach. If it is, then this should probably go to stable too. Cc: Shirish Pargaonkar Signed-off-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/cifsencrypt.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index a51585f9852b4..96b9a34db48b5 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -30,12 +30,13 @@ #include #include -/* Calculate and return the CIFS signature based on the mac key and SMB PDU */ -/* the 16 byte signature must be allocated by the caller */ -/* Note we only use the 1st eight bytes */ -/* Note that the smb header signature field on input contains the - sequence number before this function is called */ - +/* + * Calculate and return the CIFS signature based on the mac key and SMB PDU. + * The 16 byte signature must be allocated by the caller. Note we only use the + * 1st eight bytes and that the smb header signature field on input contains + * the sequence number before this function is called. Also, this function + * should be called with the server->srv_mutex held. + */ static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, char *signature) { @@ -209,8 +210,10 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu, cpu_to_le32(expected_sequence_number); cifs_pdu->Signature.Sequence.Reserved = 0; + mutex_lock(&server->srv_mutex); rc = cifs_calculate_signature(cifs_pdu, server, what_we_think_sig_should_be); + mutex_unlock(&server->srv_mutex); if (rc) return rc; From 86d3c0e94b7cc32bd6ee638771fea5bf4360c039 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 4 Feb 2011 09:23:33 -0300 Subject: [PATCH 1276/2556] video: sn9c102: world-wirtable sysfs files commit 14ddc3188d50855ae2a419a6aced995e2834e5d4 upstream. Don't allow everybody to change video settings. Signed-off-by: Vasiliy Kulikov Acked-by: Mauro Carvalho Chehab Acked-by: Luca Risolia Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/video/sn9c102/sn9c102_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index 84984f64b234a..ce56a1cdbf0a6 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -1430,9 +1430,9 @@ static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, sn9c102_show_i2c_reg, sn9c102_store_i2c_reg); static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, sn9c102_show_i2c_val, sn9c102_store_i2c_val); -static DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green); -static DEVICE_ATTR(blue, S_IWUGO, NULL, sn9c102_store_blue); -static DEVICE_ATTR(red, S_IWUGO, NULL, sn9c102_store_red); +static DEVICE_ATTR(green, S_IWUSR, NULL, sn9c102_store_green); +static DEVICE_ATTR(blue, S_IWUSR, NULL, sn9c102_store_blue); +static DEVICE_ATTR(red, S_IWUSR, NULL, sn9c102_store_red); static DEVICE_ATTR(frame_header, S_IRUGO, sn9c102_show_frame_header, NULL); From 27a2ce012061fc1b12754136e2f64e060e01c904 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 4 Feb 2011 15:24:19 +0300 Subject: [PATCH 1277/2556] UBIFS: restrict world-writable debugfs files commit 8c559d30b4e59cf6994215ada1fe744928f494bf upstream. Don't allow everybody to dump sensitive information about filesystems. Signed-off-by: Vasiliy Kulikov Signed-off-by: Artem Bityutskiy Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/debug.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/ubifs/debug.c b/fs/ubifs/debug.c index 5b9e9855b22c6..b67ed36f16a12 100644 --- a/fs/ubifs/debug.c +++ b/fs/ubifs/debug.c @@ -2844,19 +2844,19 @@ int dbg_debugfs_init_fs(struct ubifs_info *c) } fname = "dump_lprops"; - dent = debugfs_create_file(fname, S_IWUGO, d->dfs_dir, c, &dfs_fops); + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c, &dfs_fops); if (IS_ERR(dent)) goto out_remove; d->dfs_dump_lprops = dent; fname = "dump_budg"; - dent = debugfs_create_file(fname, S_IWUGO, d->dfs_dir, c, &dfs_fops); + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c, &dfs_fops); if (IS_ERR(dent)) goto out_remove; d->dfs_dump_budg = dent; fname = "dump_tnc"; - dent = debugfs_create_file(fname, S_IWUGO, d->dfs_dir, c, &dfs_fops); + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c, &dfs_fops); if (IS_ERR(dent)) goto out_remove; d->dfs_dump_tnc = dent; From 58541cc27531727f7120683a7cb123ee3ada4bd4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 28 Mar 2011 12:05:31 +0200 Subject: [PATCH 1278/2556] ALSA: hda - Fix pin-config of Gigabyte mobo commit c6b358748e19ce7e230b0926ac42696bc485a562 upstream. Use pin-fix instead of the static quirk for Gigabyte mobos 1458:a002. Bugzilla: https://bugzilla.novell.com/show_bug.cgi?id=677256 Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e164a4bdf48f5..da7cdca4776d5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9932,7 +9932,6 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = { SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL), SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG), @@ -10769,6 +10768,7 @@ enum { PINFIX_LENOVO_Y530, PINFIX_PB_M5210, PINFIX_ACER_ASPIRE_7736, + PINFIX_GIGABYTE_880GM, }; static const struct alc_fixup alc882_fixups[] = { @@ -10800,6 +10800,13 @@ static const struct alc_fixup alc882_fixups[] = { .type = ALC_FIXUP_SKU, .v.sku = ALC_FIXUP_SKU_IGNORE, }, + [PINFIX_GIGABYTE_880GM] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x1114410 }, /* set as speaker */ + { } + } + }, }; static struct snd_pci_quirk alc882_fixup_tbl[] = { @@ -10807,6 +10814,7 @@ static struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", PINFIX_LENOVO_Y530), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX), SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte", PINFIX_GIGABYTE_880GM), {} }; @@ -18851,8 +18859,6 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x1179, 0xff6e, "Toshiba NB20x", ALC662_AUTO), SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L", - ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x152d, 0x2304, "Quanta WH1", ALC663_ASUS_H13), SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x1631, 0xc10c, "PB RS65", ALC663_ASUS_M51VA), @@ -19526,6 +19532,7 @@ enum { ALC662_FIXUP_IDEAPAD, ALC272_FIXUP_MARIO, ALC662_FIXUP_CZC_P10T, + ALC662_FIXUP_GIGABYTE, }; static const struct alc_fixup alc662_fixups[] = { @@ -19554,12 +19561,20 @@ static const struct alc_fixup alc662_fixups[] = { {} } }, + [ALC662_FIXUP_GIGABYTE] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x1114410 }, /* set as speaker */ + { } + } + }, }; static struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte", ALC662_FIXUP_GIGABYTE), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), From 7795defc92b6977fbd4335f76a97ad087b02d953 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sun, 13 Mar 2011 06:54:31 +0000 Subject: [PATCH 1279/2556] NET: cdc-phonet, handle empty phonet header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 468c3f924f043cad7a04f4f4d5224a2c9bc886c1 upstream. Currently, for N 5800 XM I get: cdc_phonet: probe of 1-6:1.10 failed with error -22 It's because phonet_header is empty. Extra altsetting looks like there: E 05 24 00 01 10 03 24 ab 05 24 06 0a 0b 04 24 fd .$....$..$....$. E 00 . I don't see the header used anywhere so just check if the phonet descriptor is there, not the structure itself. Signed-off-by: Jiri Slaby Cc: Rémi Denis-Courmont Cc: David S. Miller Acked-by: Rémi Denis-Courmont Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/cdc-phonet.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c index 109751bad3bb3..e7ce8afa28bda 100644 --- a/drivers/net/usb/cdc-phonet.c +++ b/drivers/net/usb/cdc-phonet.c @@ -328,13 +328,13 @@ int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) { static const char ifname[] = "usbpn%d"; const struct usb_cdc_union_desc *union_header = NULL; - const struct usb_cdc_header_desc *phonet_header = NULL; const struct usb_host_interface *data_desc; struct usb_interface *data_intf; struct usb_device *usbdev = interface_to_usbdev(intf); struct net_device *dev; struct usbpn_dev *pnd; u8 *data; + int phonet = 0; int len, err; data = intf->altsetting->extra; @@ -355,10 +355,7 @@ int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) (struct usb_cdc_union_desc *)data; break; case 0xAB: - if (phonet_header || dlen < 5) - break; - phonet_header = - (struct usb_cdc_header_desc *)data; + phonet = 1; break; } } @@ -366,7 +363,7 @@ int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) len -= dlen; } - if (!union_header || !phonet_header) + if (!union_header || !phonet) return -EINVAL; data_intf = usb_ifnum_to_if(usbdev, union_header->bSlaveInterface0); From 3577818dd12f2908a6bdeb6283080d995f08f569 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Tue, 1 Mar 2011 14:28:02 +0000 Subject: [PATCH 1280/2556] x86: Fix a bogus unwind annotation in lib/semaphore_32.S commit e938c287ea8d977e079f07464ac69923412663ce upstream. 'simple' would have required specifying current frame address and return address location manually, but that's obviously not the case (and not necessary) here. Signed-off-by: Jan Beulich LKML-Reference: <4D6D1082020000780003454C@vpn.id2.novell.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/lib/semaphore_32.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/lib/semaphore_32.S b/arch/x86/lib/semaphore_32.S index 648fe47417823..f35eec78a68e5 100644 --- a/arch/x86/lib/semaphore_32.S +++ b/arch/x86/lib/semaphore_32.S @@ -36,7 +36,7 @@ */ #ifdef CONFIG_SMP ENTRY(__write_lock_failed) - CFI_STARTPROC simple + CFI_STARTPROC FRAME 2: LOCK_PREFIX addl $ RW_LOCK_BIAS,(%eax) From 98cf27b486964537b26a515ab91cb233d416e300 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 24 Feb 2011 15:33:24 -0500 Subject: [PATCH 1281/2556] tioca: Fix assignment from incompatible pointer warnings commit b4a6b3436531f6c5256e6d60d388c3c28ff1a0e9 upstream. The prototype for sn_pci_provider->{dma_map,dma_map_consistent} expects an unsigned long instead of a u64. Signed-off-by: Jeff Mahoney Signed-off-by: Tony Luck Signed-off-by: Greg Kroah-Hartman --- arch/ia64/sn/pci/tioca_provider.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/ia64/sn/pci/tioca_provider.c b/arch/ia64/sn/pci/tioca_provider.c index 4d4536e3b6f3d..9c271be9919aa 100644 --- a/arch/ia64/sn/pci/tioca_provider.c +++ b/arch/ia64/sn/pci/tioca_provider.c @@ -509,7 +509,7 @@ tioca_dma_unmap(struct pci_dev *pdev, dma_addr_t bus_addr, int dir) * use the GART mapped mode. */ static u64 -tioca_dma_map(struct pci_dev *pdev, u64 paddr, size_t byte_count, int dma_flags) +tioca_dma_map(struct pci_dev *pdev, unsigned long paddr, size_t byte_count, int dma_flags) { u64 mapaddr; From f57e91ef4769159b5dd5c697afcf6cca22db46f5 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 24 Feb 2011 17:23:09 -0500 Subject: [PATCH 1282/2556] mca.c: Fix cast from integer to pointer warning commit c1d036c4d1cb00b7e8473a2ad0a78f13e13a8183 upstream. ia64_mca_cpu_init has a void *data local variable that is assigned the value from either __get_free_pages() or mca_bootmem(). The problem is that __get_free_pages returns an unsigned long and mca_bootmem, via alloc_bootmem(), returns a void *. format_mca_init_stack takes the void *, and it's also used with __pa(), but that casts it to long anyway. This results in the following build warning: arch/ia64/kernel/mca.c:1898: warning: assignment makes pointer from integer without a cast Cast the return of __get_free_pages to a void * to avoid the warning. Signed-off-by: Jeff Mahoney Signed-off-by: Tony Luck Signed-off-by: Greg Kroah-Hartman --- arch/ia64/kernel/mca.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index 1753f6a30d55e..ff14ab26c508b 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c @@ -1859,7 +1859,8 @@ ia64_mca_cpu_init(void *cpu_data) data = mca_bootmem(); first_time = 0; } else - data = __get_free_pages(GFP_KERNEL, get_order(sz)); + data = (void *)__get_free_pages(GFP_KERNEL, + get_order(sz)); if (!data) panic("Could not allocate MCA memory for cpu %d\n", cpu); From c2961b693a892ddc79c1548af50e92124f48cd1c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 12 Apr 2011 14:15:51 -0700 Subject: [PATCH 1283/2556] vm: fix mlock() on stack guard page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 95042f9eb78a8d9a17455e2ef263f2f310ecef15 upstream. Commit 53a7706d5ed8 ("mlock: do not hold mmap_sem for extended periods of time") changed mlock() to care about the exact number of pages that __get_user_pages() had brought it. Before, it would only care about errors. And that doesn't work, because we also handled one page specially in __mlock_vma_pages_range(), namely the stack guard page. So when that case was handled, the number of pages that the function returned was off by one. In particular, it could be zero, and then the caller would end up not making any progress at all. Rather than try to fix up that off-by-one error for the mlock case specially, this just moves the logic to handle the stack guard page into__get_user_pages() itself, thus making all the counts come out right automatically. Reported-by: Robert Święcki Cc: Hugh Dickins Cc: Oleg Nesterov Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/memory.c | 26 ++++++++++++++++++-------- mm/mlock.c | 13 ------------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/mm/memory.c b/mm/memory.c index 5823698c2b71a..f17746a5acb13 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1410,6 +1410,13 @@ struct page *follow_page(struct vm_area_struct *vma, unsigned long address, return page; } +static inline int stack_guard_page(struct vm_area_struct *vma, unsigned long addr) +{ + return (vma->vm_flags & VM_GROWSDOWN) && + (vma->vm_start == addr) && + !vma_stack_continue(vma->vm_prev, addr); +} + int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, int nr_pages, unsigned int gup_flags, struct page **pages, struct vm_area_struct **vmas, @@ -1439,7 +1446,6 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, vma = find_extend_vma(mm, start); if (!vma && in_gate_area(tsk, start)) { unsigned long pg = start & PAGE_MASK; - struct vm_area_struct *gate_vma = get_gate_vma(tsk); pgd_t *pgd; pud_t *pud; pmd_t *pmd; @@ -1464,10 +1470,11 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, pte_unmap(pte); return i ? : -EFAULT; } + vma = get_gate_vma(tsk); if (pages) { struct page *page; - page = vm_normal_page(gate_vma, start, *pte); + page = vm_normal_page(vma, start, *pte); if (!page) { if (!(gup_flags & FOLL_DUMP) && is_zero_pfn(pte_pfn(*pte))) @@ -1481,12 +1488,7 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, get_page(page); } pte_unmap(pte); - if (vmas) - vmas[i] = gate_vma; - i++; - start += PAGE_SIZE; - nr_pages--; - continue; + goto next_page; } if (!vma || @@ -1500,6 +1502,13 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, continue; } + /* + * If we don't actually want the page itself, + * and it's the stack guard page, just skip it. + */ + if (!pages && stack_guard_page(vma, start)) + goto next_page; + do { struct page *page; unsigned int foll_flags = gup_flags; @@ -1569,6 +1578,7 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, flush_anon_page(vma, page, start); flush_dcache_page(page); } +next_page: if (vmas) vmas[i] = vma; i++; diff --git a/mm/mlock.c b/mm/mlock.c index c3924c7f00bea..da23be42c9067 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -135,13 +135,6 @@ void munlock_vma_page(struct page *page) } } -static inline int stack_guard_page(struct vm_area_struct *vma, unsigned long addr) -{ - return (vma->vm_flags & VM_GROWSDOWN) && - (vma->vm_start == addr) && - !vma_stack_continue(vma->vm_prev, addr); -} - /** * __mlock_vma_pages_range() - mlock a range of pages in the vma. * @vma: target vma @@ -188,12 +181,6 @@ static long __mlock_vma_pages_range(struct vm_area_struct *vma, if (vma->vm_flags & VM_LOCKED) gup_flags |= FOLL_MLOCK; - /* We don't try to access the guard page of a stack vma */ - if (stack_guard_page(vma, start)) { - addr += PAGE_SIZE; - nr_pages--; - } - return __get_user_pages(current, mm, addr, nr_pages, gup_flags, NULL, NULL, nonblocking); } From 8eb83708d2595c06de19d5a304eaf984ab7d31f0 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 29 Mar 2011 09:45:21 +0300 Subject: [PATCH 1284/2556] UBIFS: fix assertion warnings commit c88ac00c5af70c2a0741da14b22cdcf8507ddd92 upstream. This patch fixes UBIFS assertion warnings like: UBIFS assert failed in ubifs_leb_unmap at 135 (pid 29365) Pid: 29365, comm: integck Tainted: G I 2.6.37-ubi-2.6+ #34 Call Trace: [] ubifs_lpt_init+0x95e/0x9ee [ubifs] [] ubifs_remount_fs+0x2c7/0x762 [ubifs] [] do_remount_sb+0xb6/0x101 [] ? do_mount+0x191/0x78e [] do_mount+0x258/0x78e [] ? alloc_pages_current+0xa2/0xc5 [] sys_mount+0x83/0xbd [] system_call_fastpath+0x16/0x1b They happen when we re-mount from R/O mode to R/W mode. While re-mounting, we write to the media, but we still have the c->ro_mount flag set. The fix is very simple - just clear the flag before starting re-mounting R/W. These warnings are caused by the following commit: 2ef13294d29bcfb306e0d360f1b97f37b647b0c0 For -stable guys: this bug was introduced in 2.6.38, this is materieal for 2.6.38-stable. Signed-off-by: Artem Bityutskiy Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/super.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 6e11c2975dcf5..0f029e1732d1a 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1543,6 +1543,7 @@ static int ubifs_remount_rw(struct ubifs_info *c) mutex_lock(&c->umount_mutex); dbg_save_space_info(c); c->remounting_rw = 1; + c->ro_mount = 0; c->always_chk_crc = 1; err = check_free_space(c); @@ -1648,7 +1649,6 @@ static int ubifs_remount_rw(struct ubifs_info *c) } dbg_gen("re-mounted read-write"); - c->ro_mount = 0; c->remounting_rw = 0; c->always_chk_crc = 0; err = dbg_check_space_info(c); @@ -1656,6 +1656,7 @@ static int ubifs_remount_rw(struct ubifs_info *c) return err; out: + c->ro_mount = 1; vfree(c->orph_buf); c->orph_buf = NULL; if (c->bgt) { From 9ab7481b393d49efad749608fcbbceb90c9c5b07 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 31 Mar 2011 10:29:26 +0200 Subject: [PATCH 1285/2556] perf: Fix task context scheduling commit ab711fe08297de1485fff0a366e6db8828cafd6a upstream. Jiri reported: | | - once an event is created by sys_perf_event_open, task context | is created and it stays even if the event is closed, until the | task is finished ... thats what I see in code and I assume it's | correct | | - when the task opens event, perf_sched_events jump label is | incremented and following callbacks are started from scheduler | | __perf_event_task_sched_in | __perf_event_task_sched_out | | These callback *in/out set/unset cpuctx->task_ctx value to the | task context. | | - close is called on event on CPU 0: | - the task is scheduled on CPU 0 | - __perf_event_task_sched_in is called | - cpuctx->task_ctx is set | - perf_sched_events jump label is decremented and == 0 | - __perf_event_task_sched_out is not called | - cpuctx->task_ctx on CPU 0 stays set | | - exit is called on CPU 1: | - the task is scheduled on CPU 1 | - perf_event_exit_task is called | - task_ctx_sched_out unsets cpuctx->task_ctx on CPU 1 | - put_ctx destroys the context | | - another call of perf_rotate_context on CPU 0 will use invalid | task_ctx pointer, and eventualy panic. | Cure this the simplest possibly way by partially reverting the jump_label optimization for the sched_out case. Reported-and-tested-by: Jiri Olsa Signed-off-by: Peter Zijlstra Cc: Oleg Nesterov LKML-Reference: <1301520405.4859.213.camel@twins> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- include/linux/perf_event.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index dda5b0a3ff601..a9bd9c0af44ba 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1052,7 +1052,7 @@ void perf_event_task_sched_out(struct task_struct *task, struct task_struct *nex { perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 1, NULL, 0); - COND_STMT(&perf_task_events, __perf_event_task_sched_out(task, next)); + __perf_event_task_sched_out(task, next); } extern void perf_event_mmap(struct vm_area_struct *vma); From 3e2b22819cdd86f0ad7fa61165d15e018ee96ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Tue, 22 Mar 2011 11:40:32 +0000 Subject: [PATCH 1286/2556] bridge: Fix possibly wrong MLD queries' ethernet source address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit a7bff75b087e7a355838a32efe61707cfa73c194 ] The ipv6_dev_get_saddr() is currently called with an uninitialized destination address. Although in tests it usually seemed to nevertheless always fetch the right source address, there seems to be a possible race condition. Therefore this commit changes this, first setting the destination address and only after that fetching the source address. Reported-by: Jan Beulich Signed-off-by: Linus Lüssing Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/bridge/br_multicast.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 030a002ff8eee..f61eb2eff3fdd 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -445,9 +445,9 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, ip6h->payload_len = htons(8 + sizeof(*mldq)); ip6h->nexthdr = IPPROTO_HOPOPTS; ip6h->hop_limit = 1; + ipv6_addr_set(&ip6h->daddr, htonl(0xff020000), 0, 0, htonl(1)); ipv6_dev_get_saddr(dev_net(br->dev), br->dev, &ip6h->daddr, 0, &ip6h->saddr); - ipv6_addr_set(&ip6h->daddr, htonl(0xff020000), 0, 0, htonl(1)); ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); hopopt = (u8 *)(ip6h + 1); From 152f99e6e48538e4c4dc71a72d35888413b5ee92 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 30 Mar 2011 16:57:46 -0700 Subject: [PATCH 1287/2556] fib: add rtnl locking in ip_fib_net_exit [ Upstream commit e2666f84958adb3a034b98e99699b55705117e01 ] Daniel J Blueman reported a lockdep splat in trie_firstleaf(), caused by RTNL being not locked before a call to fib_table_flush() Reported-by: Daniel J Blueman Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/fib_frontend.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 1d2cdd43a878b..8725e783d9cdb 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1041,6 +1041,7 @@ static void ip_fib_net_exit(struct net *net) fib4_rules_exit(net); #endif + rtnl_lock(); for (i = 0; i < FIB_TABLE_HASHSZ; i++) { struct fib_table *tb; struct hlist_head *head; @@ -1053,6 +1054,7 @@ static void ip_fib_net_exit(struct net *net) fib_free_table(tb); } } + rtnl_unlock(); kfree(net->ipv4.fib_table_hash); } From 36063b029e5168c421666b9cd56dcd425ebbab4c Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Wed, 16 Mar 2011 17:57:13 +0000 Subject: [PATCH 1288/2556] gianfar: Fall back to software tcp/udp checksum on older controllers [ Upstream commit 4363c2fddb1399b728ef21ee8101c148a311ea45 ] As specified by errata eTSEC49 of MPC8548 and errata eTSEC12 of MPC83xx, older revisions of gianfar controllers will be unable to calculate a TCP/UDP packet checksum for some alignments of the appropriate FCB. This patch checks for FCB alignment on such controllers and falls back to software checksumming if the alignment is known to be bad. Signed-off-by: Alex Dubov Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/gianfar.c | 16 ++++++++++++++-- drivers/net/gianfar.h | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 5ed8f9f9419f1..3da19a5559773 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -950,6 +950,11 @@ static void gfar_detect_errata(struct gfar_private *priv) (pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0)) priv->errata |= GFAR_ERRATA_A002; + /* MPC8313 Rev < 2.0, MPC8548 rev 2.0 */ + if ((pvr == 0x80850010 && mod == 0x80b0 && rev < 0x0020) || + (pvr == 0x80210020 && mod == 0x8030 && rev == 0x0020)) + priv->errata |= GFAR_ERRATA_12; + if (priv->errata) dev_info(dev, "enabled errata workarounds, flags: 0x%x\n", priv->errata); @@ -2156,8 +2161,15 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Set up checksumming */ if (CHECKSUM_PARTIAL == skb->ip_summed) { fcb = gfar_add_fcb(skb); - lstatus |= BD_LFLAG(TXBD_TOE); - gfar_tx_checksum(skb, fcb); + /* as specified by errata */ + if (unlikely(gfar_has_errata(priv, GFAR_ERRATA_12) + && ((unsigned long)fcb % 0x20) > 0x18)) { + __skb_pull(skb, GMAC_FCB_LEN); + skb_checksum_help(skb); + } else { + lstatus |= BD_LFLAG(TXBD_TOE); + gfar_tx_checksum(skb, fcb); + } } if (vlan_tx_tag_present(skb)) { diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index 54de4135e932b..ec5d595ce2e26 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -1039,6 +1039,7 @@ enum gfar_errata { GFAR_ERRATA_74 = 0x01, GFAR_ERRATA_76 = 0x02, GFAR_ERRATA_A002 = 0x04, + GFAR_ERRATA_12 = 0x08, /* a.k.a errata eTSEC49 */ }; /* Struct stolen almost completely (and shamelessly) from the FCC enet source From 8f25b03628eee69953d729c09eb5d47e2dde6f28 Mon Sep 17 00:00:00 2001 From: James Chapman Date: Mon, 21 Mar 2011 18:10:25 -0700 Subject: [PATCH 1289/2556] l2tp: fix possible oops on l2tp_eth module unload [ Upstream commit 8aa525a9340da4227797a06221ca08399006635f ] A struct used in the l2tp_eth driver for registering network namespace ops was incorrectly marked as __net_initdata, leading to oops when module unloaded. BUG: unable to handle kernel paging request at ffffffffa00ec098 IP: [] ops_exit_list+0x7/0x4b PGD 142d067 PUD 1431063 PMD 195da8067 PTE 0 Oops: 0000 [#1] SMP last sysfs file: /sys/module/l2tp_eth/refcnt Call Trace: [] ? unregister_pernet_operations+0x32/0x93 [] ? unregister_pernet_device+0x2b/0x38 [] ? sys_delete_module+0x1b8/0x222 [] ? do_munmap+0x254/0x318 [] ? page_fault+0x25/0x30 [] ? system_call_fastpath+0x16/0x1b Signed-off-by: James Chapman Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/l2tp/l2tp_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index 8d9ce0accc982..a8193f52c13c2 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c @@ -283,7 +283,7 @@ static __net_init int l2tp_eth_init_net(struct net *net) return 0; } -static __net_initdata struct pernet_operations l2tp_eth_net_ops = { +static struct pernet_operations l2tp_eth_net_ops = { .init = l2tp_eth_init_net, .id = &l2tp_eth_net_id, .size = sizeof(struct l2tp_eth_net), From 03700d4553b9401fa37c7d942bf7b58d6565fe21 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 21 Mar 2011 18:23:34 -0700 Subject: [PATCH 1290/2556] net ipv6: Fix duplicate /proc/sys/net/ipv6/neigh directory entries. [ Upstream commit 9d2a8fa96a44ba242de3a6f56acaef7a40a97b97 ] When I was fixing issues with unregisgtering tables under /proc/sys/net/ipv6/neigh by adding a mount point it appears I missed a critical ordering issue, in the ipv6 initialization. I had not realized that ipv6_sysctl_register is called at the very end of the ipv6 initialization and in particular after we call neigh_sysctl_register from ndisc_init. "neigh" needs to be initialized in ipv6_static_sysctl_register which is the first ipv6 table to initialized, and definitely before ndisc_init. This removes the weirdness of duplicate tables while still providing a "neigh" mount point which prevents races in sysctl unregistering. This was initially reported at https://bugzilla.kernel.org/show_bug.cgi?id=31232 Reported-by: sunkan@zappa.cx Signed-off-by: Eric W. Biederman Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/sysctl_net_ipv6.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index 7cb65ef79f9cd..6dcf5e7d661bd 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -17,6 +17,16 @@ static struct ctl_table empty[1]; +static ctl_table ipv6_static_skeleton[] = { + { + .procname = "neigh", + .maxlen = 0, + .mode = 0555, + .child = empty, + }, + { } +}; + static ctl_table ipv6_table_template[] = { { .procname = "route", @@ -37,12 +47,6 @@ static ctl_table ipv6_table_template[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "neigh", - .maxlen = 0, - .mode = 0555, - .child = empty, - }, { } }; @@ -160,7 +164,7 @@ static struct ctl_table_header *ip6_base; int ipv6_static_sysctl_register(void) { - ip6_base = register_sysctl_paths(net_ipv6_ctl_path, empty); + ip6_base = register_sysctl_paths(net_ipv6_ctl_path, ipv6_static_skeleton); if (ip6_base == NULL) return -ENOMEM; return 0; From 5d0fad1c60f42b4578ddbfd2ec54216f2f2155fc Mon Sep 17 00:00:00 2001 From: Dan Siemon Date: Tue, 15 Mar 2011 13:56:07 +0000 Subject: [PATCH 1291/2556] net_sched: fix ip_tos2prio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4a2b9c3756077c05dd8666e458a751d2248b61b6 ] ECN support incorrectly maps ECN BESTEFFORT packets to TC_PRIO_FILLER (1) instead of TC_PRIO_BESTEFFORT (0) This means ECN enabled flows are placed in pfifo_fast/prio low priority band, giving ECN enabled flows [ECT(0) and CE codepoints] higher drop probabilities. This is rather unfortunate, given we would like ECN being more widely used. Ref : http://www.coverfire.com/archives/2011/03/13/pfifo_fast-and-ecn/ Signed-off-by: Dan Siemon Signed-off-by: Eric Dumazet Cc: Dave Täht Cc: Jonathan Morton Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/route.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 6ed6603c2f6db..fabfe8168b909 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -171,7 +171,7 @@ static struct dst_ops ipv4_dst_ops = { const __u8 ip_tos2prio[16] = { TC_PRIO_BESTEFFORT, - ECN_OR_COST(FILLER), + ECN_OR_COST(BESTEFFORT), TC_PRIO_BESTEFFORT, ECN_OR_COST(BESTEFFORT), TC_PRIO_BULK, From 215f7206f540704ead83838eacef25ec13b763fb Mon Sep 17 00:00:00 2001 From: Ulrich Weber Date: Wed, 6 Apr 2011 14:04:49 -0700 Subject: [PATCH 1292/2556] pppoe: drop PPPOX_ZOMBIEs in pppoe_flush_dev [ Upstream commit ae07b0b221b6ab2edf9e3abd518aec6cd3f1ba66 ] otherwise we loop forever if a PPPoE socket was set to PPPOX_ZOMBIE state by a PADT message when the ethernet device is going down afterwards. Signed-off-by: Ulrich Weber Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/pppoe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c index 78c0e3c9b2b5f..71b1d8fbc301d 100644 --- a/drivers/net/pppoe.c +++ b/drivers/net/pppoe.c @@ -317,7 +317,7 @@ static void pppoe_flush_dev(struct net_device *dev) lock_sock(sk); if (po->pppoe_dev == dev && - sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) { + sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) { pppox_unbind_sock(sk); sk->sk_state = PPPOX_ZOMBIE; sk->sk_state_change(sk); From a83f2b584b54a123c408692eb19e0c97022be607 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 30 Mar 2011 17:51:36 -0700 Subject: [PATCH 1293/2556] sctp: Pass __GFP_NOWARN to hash table allocation attempts. [ Upstream commit a84b50ceb7d640437d0dc28a2bef0d0de054de89 ] Like DCCP and other similar pieces of code, there are mechanisms here to try allocating smaller hash tables if the allocation fails. So pass in __GFP_NOWARN like the others do instead of emitting a scary message. Reported-by: Dave Jones Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sctp/protocol.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index e58f9476f29c5..dec012d32ff55 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1204,7 +1204,7 @@ SCTP_STATIC __init int sctp_init(void) if ((sctp_assoc_hashsize > (64 * 1024)) && order > 0) continue; sctp_assoc_hashtable = (struct sctp_hashbucket *) - __get_free_pages(GFP_ATOMIC, order); + __get_free_pages(GFP_ATOMIC|__GFP_NOWARN, order); } while (!sctp_assoc_hashtable && --order > 0); if (!sctp_assoc_hashtable) { pr_err("Failed association hash alloc\n"); @@ -1237,7 +1237,7 @@ SCTP_STATIC __init int sctp_init(void) if ((sctp_port_hashsize > (64 * 1024)) && order > 0) continue; sctp_port_hashtable = (struct sctp_bind_hashbucket *) - __get_free_pages(GFP_ATOMIC, order); + __get_free_pages(GFP_ATOMIC|__GFP_NOWARN, order); } while (!sctp_port_hashtable && --order > 0); if (!sctp_port_hashtable) { pr_err("Failed bind hash alloc\n"); From edc45d1e743b8b5a9366b7cafb53c80f4267fe1f Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Mon, 14 Mar 2011 10:57:03 +0000 Subject: [PATCH 1294/2556] tcp: avoid cwnd moderation in undo [ Upstream commit 67d4120a1793138bc9f4a6eb61d0fc5298ed97e0 ] In the current undo logic, cwnd is moderated after it was restored to the value prior entering fast-recovery. It was moderated first in tcp_try_undo_recovery then again in tcp_complete_cwr. Since the undo indicates recovery was false, these moderations are not necessary. If the undo is triggered when most of the outstanding data have been acknowledged, the (restored) cwnd is falsely pulled down to a small value. This patch removes these cwnd moderations if cwnd is undone a) during fast-recovery b) by receiving DSACKs past fast-recovery Signed-off-by: Yuchung Cheng Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_input.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 65f6c04062453..5f1ea7cb18afc 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2659,7 +2659,7 @@ static void DBGUNDO(struct sock *sk, const char *msg) #define DBGUNDO(x...) do { } while (0) #endif -static void tcp_undo_cwr(struct sock *sk, const int undo) +static void tcp_undo_cwr(struct sock *sk, const int undo_ssthresh) { struct tcp_sock *tp = tcp_sk(sk); @@ -2671,14 +2671,13 @@ static void tcp_undo_cwr(struct sock *sk, const int undo) else tp->snd_cwnd = max(tp->snd_cwnd, tp->snd_ssthresh << 1); - if (undo && tp->prior_ssthresh > tp->snd_ssthresh) { + if (undo_ssthresh && tp->prior_ssthresh > tp->snd_ssthresh) { tp->snd_ssthresh = tp->prior_ssthresh; TCP_ECN_withdraw_cwr(tp); } } else { tp->snd_cwnd = max(tp->snd_cwnd, tp->snd_ssthresh); } - tcp_moderate_cwnd(tp); tp->snd_cwnd_stamp = tcp_time_stamp; } @@ -2822,8 +2821,11 @@ static int tcp_try_undo_loss(struct sock *sk) static inline void tcp_complete_cwr(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); - tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh); - tp->snd_cwnd_stamp = tcp_time_stamp; + /* Do not moderate cwnd if it's already undone in cwr or recovery */ + if (tp->undo_marker && tp->snd_cwnd > tp->snd_ssthresh) { + tp->snd_cwnd = tp->snd_ssthresh; + tp->snd_cwnd_stamp = tcp_time_stamp; + } tcp_ca_event(sk, CA_EVENT_COMPLETE_CWR); } From e5a1f3787f4b43e828de2818faa6a0f658b87a63 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Tue, 15 Mar 2011 21:12:49 +0000 Subject: [PATCH 1295/2556] xfrm: Refcount destination entry on xfrm_lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit fbd5060875d25f7764fd1c3d35b83a8ed1d88d7b ] We return a destination entry without refcount if a socket policy is found in xfrm_lookup. This triggers a warning on a negative refcount when freeeing this dst entry. So take a refcount in this case to fix it. This refcount was forgotten when xfrm changed to cache bundles instead of policies for outgoing flows. Signed-off-by: Steffen Klassert Acked-by: Timo Teräs Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/xfrm/xfrm_policy.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 6459588befc33..8da2741d7997b 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1778,6 +1778,8 @@ int __xfrm_lookup(struct net *net, struct dst_entry **dst_p, struct flowi *fl, goto no_transform; } + dst_hold(&xdst->u.dst); + spin_lock_bh(&xfrm_policy_sk_bundle_lock); xdst->u.dst.next = xfrm_policy_sk_bundles; xfrm_policy_sk_bundles = &xdst->u.dst; From 01b7e60bf848901c3a95794c90888e4e33ab751f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 18 Mar 2011 00:27:27 +0000 Subject: [PATCH 1296/2556] vlan: should take into account needed_headroom [ Upstream commit d870bfb9d366c5d466c0f5419a4ec95a3f71ea8a ] Commit c95b819ad7 (gre: Use needed_headroom) made gre use needed_headroom instead of hard_header_len This uncover a bug in vlan code. We should make sure vlan devices take into account their real_dev->needed_headroom or we risk a crash in ipgre_header(), because we dont have enough room to push IP header in skb. Reported-by: Diddi Oscarsson Signed-off-by: Eric Dumazet Cc: Patrick McHardy Cc: Herbert Xu Acked-by: Herbert Xu Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/8021q/vlan_dev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index be737539f34d1..ed68d0722e531 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -707,6 +707,7 @@ static int vlan_dev_init(struct net_device *dev) dev->fcoe_ddp_xid = real_dev->fcoe_ddp_xid; #endif + dev->needed_headroom = real_dev->needed_headroom; if (real_dev->features & NETIF_F_HW_VLAN_TX) { dev->header_ops = real_dev->header_ops; dev->hard_header_len = real_dev->hard_header_len; From 5f1c356a3fadc0c19922d660da723b79bcc9aad7 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 18 Mar 2011 05:27:28 +0000 Subject: [PATCH 1297/2556] bridge: Reset IPCB when entering IP stack on NF_FORWARD [ Upstream commit 6b1e960fdbd75dcd9bcc3ba5ff8898ff1ad30b6e ] Whenever we enter the IP stack proper from bridge netfilter we need to ensure that the skb is in a form the IP stack expects it to be in. The entry point on NF_FORWARD did not meet the requirements of the IP stack, therefore leading to potential crashes/panics. This patch fixes the problem. Signed-off-by: Herbert Xu Acked-by: Stephen Hemminger Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/bridge/br_netfilter.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 4b5b66d07bba7..49d50ea5dbbc6 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -741,6 +741,9 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb, nf_bridge->mask |= BRNF_PKT_TYPE; } + if (br_parse_ip_options(skb)) + return NF_DROP; + /* The physdev module checks on this */ nf_bridge->mask |= BRNF_BRIDGED; nf_bridge->physoutdev = skb->dev; From 26a92fe2e4418a73b554c9e335840d424a2a4136 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Wed, 9 Mar 2011 12:54:27 -0800 Subject: [PATCH 1298/2556] sparc: Fix .size directive for do_int_load [ Upstream commit 35043c428f1fcb92feb5792f5878a8852ee00771 ] gas used to accept (and ignore?) .size directives which referred to undefined symbols, as this does. In binutils 2.21 these are treated as errors. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- arch/sparc/kernel/una_asm_64.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sparc/kernel/una_asm_64.S b/arch/sparc/kernel/una_asm_64.S index be183fe41443f..1c8d33228b2a6 100644 --- a/arch/sparc/kernel/una_asm_64.S +++ b/arch/sparc/kernel/una_asm_64.S @@ -127,7 +127,7 @@ do_int_load: wr %o5, 0x0, %asi retl mov 0, %o0 - .size __do_int_load, .-__do_int_load + .size do_int_load, .-do_int_load .section __ex_table,"a" .word 4b, __retl_efault From 247a80437d061450c5e9f371547aef4e34baec16 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 9 Mar 2011 13:00:47 -0800 Subject: [PATCH 1299/2556] sparc32: Fix might-be-used-uninitialized warning in do_sparc_fault(). [ Upstream commit c816be7b5f24585baa9eba1f2413935f771d6ad6 ] When we try to handle vmalloc faults, we can take a code path which uses "code" before we actually set it. Amusingly gcc-3.3 notices this yet gcc-4.x does not. Reported-by: Bob Breuer Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- arch/sparc/mm/fault_32.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/sparc/mm/fault_32.c b/arch/sparc/mm/fault_32.c index 5b836f5aea90b..b10ac4d62378a 100644 --- a/arch/sparc/mm/fault_32.c +++ b/arch/sparc/mm/fault_32.c @@ -240,11 +240,10 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write, * only copy the information from the master page table, * nothing more. */ + code = SEGV_MAPERR; if (!ARCH_SUN4C && address >= TASK_SIZE) goto vmalloc_fault; - code = SEGV_MAPERR; - /* * If we're in an interrupt or have no user * context, we must not take the fault.. From 790dcd55449886058b77e6c49fa8b3834568a8ed Mon Sep 17 00:00:00 2001 From: Tkhai Kirill Date: Thu, 31 Mar 2011 00:52:38 -0700 Subject: [PATCH 1300/2556] sparc32: Pass task_struct to schedule_tail() in ret_from_fork [ Upstream commit 47c7c97a93a5b8f719093dbf83555090b3b8228b ] We have to pass task_struct of previous process to function schedule_tail(). Currently in ret_from_fork previous thread_info is passed: switch_to: mov %g6, %g3 /* previous thread_info in g6 */ ret_from_fork: call schedule_tail mov %g3, %o0 /* previous thread_info is passed */ void schedule_tail(struct task_struct *prev); Signed-off-by: Tkhai Kirill Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- arch/sparc/kernel/entry.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index 1504df8ddf70a..906ee3e24cc96 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S @@ -1283,7 +1283,7 @@ linux_syscall_trace: .globl ret_from_fork ret_from_fork: call schedule_tail - mov %g3, %o0 + ld [%g3 + TI_TASK], %o0 b ret_sys_call ld [%sp + STACKFRAME_SZ + PT_I0], %o0 From 5b00f4b47e31f38c965143036b37f3b436083fc2 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sat, 26 Feb 2011 23:40:02 -0800 Subject: [PATCH 1301/2556] sparc64: Fix build errors with gcc-4.6.0 [ Upstream commit c6fee0810df4e0f4cf9c4834d2569ca01c02cffc ] Most of the warnings emitted (we fail arch/sparc file builds with -Werror) were legitimate but harmless, however one case (n2_pcr_write) was a genuine bug. Based almost entirely upon a patch by Sam Ravnborg. Reported-by: Dennis Gilmore Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- arch/sparc/kernel/iommu.c | 3 --- arch/sparc/kernel/ldc.c | 28 ++++++++++++++++++---------- arch/sparc/kernel/pci.c | 1 + arch/sparc/kernel/pci_common.c | 11 +++++++---- arch/sparc/kernel/pci_fire.c | 2 -- arch/sparc/kernel/pci_schizo.c | 4 +--- arch/sparc/kernel/pci_sun4v.c | 3 +-- arch/sparc/kernel/pcr.c | 2 +- arch/sparc/kernel/ptrace_64.c | 3 ++- arch/sparc/kernel/smp_64.c | 11 ++++------- arch/sparc/kernel/traps_64.c | 3 +-- 11 files changed, 36 insertions(+), 35 deletions(-) diff --git a/arch/sparc/kernel/iommu.c b/arch/sparc/kernel/iommu.c index 72509d0e34be2..6f01e8c83197e 100644 --- a/arch/sparc/kernel/iommu.c +++ b/arch/sparc/kernel/iommu.c @@ -333,13 +333,10 @@ static void dma_4u_free_coherent(struct device *dev, size_t size, void *cpu, dma_addr_t dvma) { struct iommu *iommu; - iopte_t *iopte; unsigned long flags, order, npages; npages = IO_PAGE_ALIGN(size) >> IO_PAGE_SHIFT; iommu = dev->archdata.iommu; - iopte = iommu->page_table + - ((dvma - iommu->page_table_map_base) >> IO_PAGE_SHIFT); spin_lock_irqsave(&iommu->lock, flags); diff --git a/arch/sparc/kernel/ldc.c b/arch/sparc/kernel/ldc.c index df39a0f0d27af..732b0bce6001c 100644 --- a/arch/sparc/kernel/ldc.c +++ b/arch/sparc/kernel/ldc.c @@ -790,16 +790,20 @@ static void send_events(struct ldc_channel *lp, unsigned int event_mask) static irqreturn_t ldc_rx(int irq, void *dev_id) { struct ldc_channel *lp = dev_id; - unsigned long orig_state, hv_err, flags; + unsigned long orig_state, flags; unsigned int event_mask; spin_lock_irqsave(&lp->lock, flags); orig_state = lp->chan_state; - hv_err = sun4v_ldc_rx_get_state(lp->id, - &lp->rx_head, - &lp->rx_tail, - &lp->chan_state); + + /* We should probably check for hypervisor errors here and + * reset the LDC channel if we get one. + */ + sun4v_ldc_rx_get_state(lp->id, + &lp->rx_head, + &lp->rx_tail, + &lp->chan_state); ldcdbg(RX, "RX state[0x%02lx:0x%02lx] head[0x%04lx] tail[0x%04lx]\n", orig_state, lp->chan_state, lp->rx_head, lp->rx_tail); @@ -904,16 +908,20 @@ static irqreturn_t ldc_rx(int irq, void *dev_id) static irqreturn_t ldc_tx(int irq, void *dev_id) { struct ldc_channel *lp = dev_id; - unsigned long flags, hv_err, orig_state; + unsigned long flags, orig_state; unsigned int event_mask = 0; spin_lock_irqsave(&lp->lock, flags); orig_state = lp->chan_state; - hv_err = sun4v_ldc_tx_get_state(lp->id, - &lp->tx_head, - &lp->tx_tail, - &lp->chan_state); + + /* We should probably check for hypervisor errors here and + * reset the LDC channel if we get one. + */ + sun4v_ldc_tx_get_state(lp->id, + &lp->tx_head, + &lp->tx_tail, + &lp->chan_state); ldcdbg(TX, " TX state[0x%02lx:0x%02lx] head[0x%04lx] tail[0x%04lx]\n", orig_state, lp->chan_state, lp->tx_head, lp->tx_tail); diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index 4137579d9adcd..f255382b02b86 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -675,6 +675,7 @@ static void __devinit pci_bus_register_of_sysfs(struct pci_bus *bus) * humanoid. */ err = sysfs_create_file(&dev->dev.kobj, &dev_attr_obppath.attr); + (void) err; } list_for_each_entry(child_bus, &bus->children, node) pci_bus_register_of_sysfs(child_bus); diff --git a/arch/sparc/kernel/pci_common.c b/arch/sparc/kernel/pci_common.c index 6c7a33af3ba62..6e3874b644880 100644 --- a/arch/sparc/kernel/pci_common.c +++ b/arch/sparc/kernel/pci_common.c @@ -295,14 +295,17 @@ static int sun4v_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, unsigned int bus = bus_dev->number; unsigned int device = PCI_SLOT(devfn); unsigned int func = PCI_FUNC(devfn); - unsigned long ret; if (config_out_of_range(pbm, bus, devfn, where)) { /* Do nothing. */ } else { - ret = pci_sun4v_config_put(devhandle, - HV_PCI_DEVICE_BUILD(bus, device, func), - where, size, value); + /* We don't check for hypervisor errors here, but perhaps + * we should and influence our return value depending upon + * what kind of error is thrown. + */ + pci_sun4v_config_put(devhandle, + HV_PCI_DEVICE_BUILD(bus, device, func), + where, size, value); } return PCIBIOS_SUCCESSFUL; } diff --git a/arch/sparc/kernel/pci_fire.c b/arch/sparc/kernel/pci_fire.c index efb896d687540..75dfeb60ef6eb 100644 --- a/arch/sparc/kernel/pci_fire.c +++ b/arch/sparc/kernel/pci_fire.c @@ -214,11 +214,9 @@ static int pci_fire_msi_setup(struct pci_pbm_info *pbm, unsigned long msiqid, static int pci_fire_msi_teardown(struct pci_pbm_info *pbm, unsigned long msi) { - unsigned long msiqid; u64 val; val = upa_readq(pbm->pbm_regs + MSI_MAP(msi)); - msiqid = (val & MSI_MAP_EQNUM); val &= ~MSI_MAP_VALID; diff --git a/arch/sparc/kernel/pci_schizo.c b/arch/sparc/kernel/pci_schizo.c index 445a47a2fb3dd..4620eb76aef4c 100644 --- a/arch/sparc/kernel/pci_schizo.c +++ b/arch/sparc/kernel/pci_schizo.c @@ -1313,7 +1313,7 @@ static int __devinit schizo_pbm_init(struct pci_pbm_info *pbm, const struct linux_prom64_registers *regs; struct device_node *dp = op->dev.of_node; const char *chipset_name; - int is_pbm_a, err; + int err; switch (chip_type) { case PBM_CHIP_TYPE_TOMATILLO: @@ -1343,8 +1343,6 @@ static int __devinit schizo_pbm_init(struct pci_pbm_info *pbm, */ regs = of_get_property(dp, "reg", NULL); - is_pbm_a = ((regs[0].phys_addr & 0x00700000) == 0x00600000); - pbm->next = pci_pbm_root; pci_pbm_root = pbm; diff --git a/arch/sparc/kernel/pci_sun4v.c b/arch/sparc/kernel/pci_sun4v.c index 743344aa6d8a6..859abfd789373 100644 --- a/arch/sparc/kernel/pci_sun4v.c +++ b/arch/sparc/kernel/pci_sun4v.c @@ -580,7 +580,7 @@ static int __devinit pci_sun4v_iommu_init(struct pci_pbm_info *pbm) { static const u32 vdma_default[] = { 0x80000000, 0x80000000 }; struct iommu *iommu = pbm->iommu; - unsigned long num_tsb_entries, sz, tsbsize; + unsigned long num_tsb_entries, sz; u32 dma_mask, dma_offset; const u32 *vdma; @@ -596,7 +596,6 @@ static int __devinit pci_sun4v_iommu_init(struct pci_pbm_info *pbm) dma_mask = (roundup_pow_of_two(vdma[1]) - 1UL); num_tsb_entries = vdma[1] / IO_PAGE_SIZE; - tsbsize = num_tsb_entries * sizeof(iopte_t); dma_offset = vdma[0]; diff --git a/arch/sparc/kernel/pcr.c b/arch/sparc/kernel/pcr.c index 7c2ced612b8f5..8ac23e6600804 100644 --- a/arch/sparc/kernel/pcr.c +++ b/arch/sparc/kernel/pcr.c @@ -81,7 +81,7 @@ static void n2_pcr_write(u64 val) unsigned long ret; ret = sun4v_niagara2_setperf(HV_N2_PERF_SPARC_CTL, val); - if (val != HV_EOK) + if (ret != HV_EOK) write_pcr(val); } diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c index 9ccc812bc09e6..96ee50a806613 100644 --- a/arch/sparc/kernel/ptrace_64.c +++ b/arch/sparc/kernel/ptrace_64.c @@ -1086,6 +1086,7 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs) asmlinkage void syscall_trace_leave(struct pt_regs *regs) { +#ifdef CONFIG_AUDITSYSCALL if (unlikely(current->audit_context)) { unsigned long tstate = regs->tstate; int result = AUDITSC_SUCCESS; @@ -1095,7 +1096,7 @@ asmlinkage void syscall_trace_leave(struct pt_regs *regs) audit_syscall_exit(result, regs->u_regs[UREG_I0]); } - +#endif if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_exit(regs, regs->u_regs[UREG_G1]); diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c index 555a76d1f4a18..3e94a8c232388 100644 --- a/arch/sparc/kernel/smp_64.c +++ b/arch/sparc/kernel/smp_64.c @@ -189,7 +189,7 @@ static inline long get_delta (long *rt, long *master) void smp_synchronize_tick_client(void) { long i, delta, adj, adjust_latency = 0, done = 0; - unsigned long flags, rt, master_time_stamp, bound; + unsigned long flags, rt, master_time_stamp; #if DEBUG_TICK_SYNC struct { long rt; /* roundtrip time */ @@ -208,10 +208,8 @@ void smp_synchronize_tick_client(void) { for (i = 0; i < NUM_ROUNDS; i++) { delta = get_delta(&rt, &master_time_stamp); - if (delta == 0) { + if (delta == 0) done = 1; /* let's lock on to this... */ - bound = rt; - } if (!done) { if (i > 0) { @@ -933,13 +931,12 @@ void smp_flush_dcache_page_impl(struct page *page, int cpu) void flush_dcache_page_all(struct mm_struct *mm, struct page *page) { void *pg_addr; - int this_cpu; u64 data0; if (tlb_type == hypervisor) return; - this_cpu = get_cpu(); + preempt_disable(); #ifdef CONFIG_DEBUG_DCFLUSH atomic_inc(&dcpage_flushes); @@ -964,7 +961,7 @@ void flush_dcache_page_all(struct mm_struct *mm, struct page *page) } __local_flush_dcache_page(page); - put_cpu(); + preempt_enable(); } void __irq_entry smp_new_mmu_context_version_client(int irq, struct pt_regs *regs) diff --git a/arch/sparc/kernel/traps_64.c b/arch/sparc/kernel/traps_64.c index 1e9770936c3b8..1ed547bd850f8 100644 --- a/arch/sparc/kernel/traps_64.c +++ b/arch/sparc/kernel/traps_64.c @@ -2152,7 +2152,7 @@ static void user_instruction_dump(unsigned int __user *pc) void show_stack(struct task_struct *tsk, unsigned long *_ksp) { - unsigned long fp, thread_base, ksp; + unsigned long fp, ksp; struct thread_info *tp; int count = 0; #ifdef CONFIG_FUNCTION_GRAPH_TRACER @@ -2173,7 +2173,6 @@ void show_stack(struct task_struct *tsk, unsigned long *_ksp) flushw_all(); fp = ksp + STACK_BIAS; - thread_base = (unsigned long) tp; printk("Call Trace:\n"); do { From db06752c7f64c445aeae2ae520a48c35e78789ed Mon Sep 17 00:00:00 2001 From: Darren Hart Date: Thu, 14 Apr 2011 15:41:57 -0700 Subject: [PATCH 1302/2556] futex: Set FLAGS_HAS_TIMEOUT during futex_wait restart setup commit 0cd9c6494ee5c19aef085152bc37f3a4e774a9e1 upstream. The FLAGS_HAS_TIMEOUT flag was not getting set, causing the restart_block to restart futex_wait() without a timeout after a signal. Commit b41277dc7a18ee332d in 2.6.38 introduced the regression by accidentally removing the the FLAGS_HAS_TIMEOUT assignment from futex_wait() during the setup of the restart block. Restore the originaly behavior. Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=32922 Reported-by: Tim Smith Reported-by: Torsten Hilbrich Signed-off-by: Darren Hart Signed-off-by: Eric Dumazet Cc: Peter Zijlstra Cc: John Kacur Link: http://lkml.kernel.org/r/%3Cdaac0eb3af607f72b9a4d3126b2ba8fb5ed3b883.1302820917.git.dvhart%40linux.intel.com%3E Signed-off-by: Thomas Gleixner Signed-off-by: Greg Kroah-Hartman --- kernel/futex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/futex.c b/kernel/futex.c index b766d28accd6b..d5065e8283dac 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -1886,7 +1886,7 @@ static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val, restart->futex.val = val; restart->futex.time = abs_time->tv64; restart->futex.bitset = bitset; - restart->futex.flags = flags; + restart->futex.flags = flags | FLAGS_HAS_TIMEOUT; ret = -ERESTART_RESTARTBLOCK; From 280a1c38c907ab1e2617bdcef66de6bc70897253 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 22 Mar 2011 16:34:40 -0700 Subject: [PATCH 1303/2556] kstrto*: converting strings to integers done (hopefully) right commit 33ee3b2e2eb9b4b6c64dcf9ed66e2ac3124e748c upstream. 1. simple_strto*() do not contain overflow checks and crufty, libc way to indicate failure. 2. strict_strto*() also do not have overflow checks but the name and comments pretend they do. 3. Both families have only "long long" and "long" variants, but users want strtou8() 4. Both "simple" and "strict" prefixes are wrong: Simple doesn't exactly say what's so simple, strict should not exist because conversion should be strict by default. The solution is to use "k" prefix and add convertors for more types. Enter kstrtoull() kstrtoll() kstrtoul() kstrtol() kstrtouint() kstrtoint() kstrtou64() kstrtos64() kstrtou32() kstrtos32() kstrtou16() kstrtos16() kstrtou8() kstrtos8() Include runtime testsuite (somewhat incomplete) as well. strict_strto*() become deprecated, stubbed to kstrto*() and eventually will be removed altogether. Use kstrto*() in code today! Note: on some archs _kstrtoul() and _kstrtol() are left in tree, even if they'll be unused at runtime. This is temporarily solution, because I don't want to hardcode list of archs where these functions aren't needed. Current solution with sizeof() and __alignof__ at least always works. Signed-off-by: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/video/via/viafbdev.h | 3 - include/linux/kernel.h | 70 +++- lib/Kconfig.debug | 3 + lib/Makefile | 2 + lib/kstrtox.c | 227 +++++++++++ lib/test-kstrtox.c | 739 +++++++++++++++++++++++++++++++++++ lib/vsprintf.c | 141 ------- scripts/checkpatch.pl | 4 +- 8 files changed, 1039 insertions(+), 150 deletions(-) create mode 100644 lib/kstrtox.c create mode 100644 lib/test-kstrtox.c diff --git a/drivers/video/via/viafbdev.h b/drivers/video/via/viafbdev.h index d66f963e930e3..137996dc547e9 100644 --- a/drivers/video/via/viafbdev.h +++ b/drivers/video/via/viafbdev.h @@ -94,9 +94,6 @@ extern int viafb_LCD_ON; extern int viafb_DVI_ON; extern int viafb_hotplug; -extern int strict_strtoul(const char *cp, unsigned int base, - unsigned long *res); - u8 viafb_gpio_i2c_read_lvds(struct lvds_setting_information *plvds_setting_info, struct lvds_chip_information *plvds_chip_info, u8 index); diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 2fe6e84894a4b..00cec4dc0ae25 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -187,14 +187,76 @@ NORET_TYPE void do_exit(long error_code) ATTRIB_NORET; NORET_TYPE void complete_and_exit(struct completion *, long) ATTRIB_NORET; + +/* Internal, do not use. */ +int __must_check _kstrtoul(const char *s, unsigned int base, unsigned long *res); +int __must_check _kstrtol(const char *s, unsigned int base, long *res); + +int __must_check kstrtoull(const char *s, unsigned int base, unsigned long long *res); +int __must_check kstrtoll(const char *s, unsigned int base, long long *res); +static inline int __must_check kstrtoul(const char *s, unsigned int base, unsigned long *res) +{ + /* + * We want to shortcut function call, but + * __builtin_types_compatible_p(unsigned long, unsigned long long) = 0. + */ + if (sizeof(unsigned long) == sizeof(unsigned long long) && + __alignof__(unsigned long) == __alignof__(unsigned long long)) + return kstrtoull(s, base, (unsigned long long *)res); + else + return _kstrtoul(s, base, res); +} + +static inline int __must_check kstrtol(const char *s, unsigned int base, long *res) +{ + /* + * We want to shortcut function call, but + * __builtin_types_compatible_p(long, long long) = 0. + */ + if (sizeof(long) == sizeof(long long) && + __alignof__(long) == __alignof__(long long)) + return kstrtoll(s, base, (long long *)res); + else + return _kstrtol(s, base, res); +} + +int __must_check kstrtouint(const char *s, unsigned int base, unsigned int *res); +int __must_check kstrtoint(const char *s, unsigned int base, int *res); + +static inline int __must_check kstrtou64(const char *s, unsigned int base, u64 *res) +{ + return kstrtoull(s, base, res); +} + +static inline int __must_check kstrtos64(const char *s, unsigned int base, s64 *res) +{ + return kstrtoll(s, base, res); +} + +static inline int __must_check kstrtou32(const char *s, unsigned int base, u32 *res) +{ + return kstrtouint(s, base, res); +} + +static inline int __must_check kstrtos32(const char *s, unsigned int base, s32 *res) +{ + return kstrtoint(s, base, res); +} + +int __must_check kstrtou16(const char *s, unsigned int base, u16 *res); +int __must_check kstrtos16(const char *s, unsigned int base, s16 *res); +int __must_check kstrtou8(const char *s, unsigned int base, u8 *res); +int __must_check kstrtos8(const char *s, unsigned int base, s8 *res); + extern unsigned long simple_strtoul(const char *,char **,unsigned int); extern long simple_strtol(const char *,char **,unsigned int); extern unsigned long long simple_strtoull(const char *,char **,unsigned int); extern long long simple_strtoll(const char *,char **,unsigned int); -extern int __must_check strict_strtoul(const char *, unsigned int, unsigned long *); -extern int __must_check strict_strtol(const char *, unsigned int, long *); -extern int __must_check strict_strtoull(const char *, unsigned int, unsigned long long *); -extern int __must_check strict_strtoll(const char *, unsigned int, long long *); +#define strict_strtoul kstrtoul +#define strict_strtol kstrtol +#define strict_strtoull kstrtoull +#define strict_strtoll kstrtoll + extern int sprintf(char * buf, const char * fmt, ...) __attribute__ ((format (printf, 2, 3))); extern int vsprintf(char *buf, const char *, va_list) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 2b97418c67e2f..d1b5951690839 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1236,3 +1236,6 @@ source "samples/Kconfig" source "lib/Kconfig.kgdb" source "lib/Kconfig.kmemcheck" + +config TEST_KSTRTOX + tristate "Test kstrto*() family of functions at runtime" diff --git a/lib/Makefile b/lib/Makefile index cbb774f7d41d0..dfbf7d82ce98b 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -22,6 +22,8 @@ lib-y += kobject.o kref.o klist.o obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \ string_helpers.o gcd.o lcm.o list_sort.o uuid.o flex_array.o +obj-y += kstrtox.o +obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o ifeq ($(CONFIG_DEBUG_KOBJECT),y) CFLAGS_kobject.o += -DDEBUG diff --git a/lib/kstrtox.c b/lib/kstrtox.c new file mode 100644 index 0000000000000..05672e819f8c4 --- /dev/null +++ b/lib/kstrtox.c @@ -0,0 +1,227 @@ +/* + * Convert integer string representation to an integer. + * If an integer doesn't fit into specified type, -E is returned. + * + * Integer starts with optional sign. + * kstrtou*() functions do not accept sign "-". + * + * Radix 0 means autodetection: leading "0x" implies radix 16, + * leading "0" implies radix 8, otherwise radix is 10. + * Autodetection hints work after optional sign, but not before. + * + * If -E is returned, result is not touched. + */ +#include +#include +#include +#include +#include +#include + +static inline char _tolower(const char c) +{ + return c | 0x20; +} + +static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) +{ + unsigned long long acc; + int ok; + + if (base == 0) { + if (s[0] == '0') { + if (_tolower(s[1]) == 'x' && isxdigit(s[2])) + base = 16; + else + base = 8; + } else + base = 10; + } + if (base == 16 && s[0] == '0' && _tolower(s[1]) == 'x') + s += 2; + + acc = 0; + ok = 0; + while (*s) { + unsigned int val; + + if ('0' <= *s && *s <= '9') + val = *s - '0'; + else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f') + val = _tolower(*s) - 'a' + 10; + else if (*s == '\n') { + if (*(s + 1) == '\0') + break; + else + return -EINVAL; + } else + return -EINVAL; + + if (val >= base) + return -EINVAL; + if (acc > div_u64(ULLONG_MAX - val, base)) + return -ERANGE; + acc = acc * base + val; + ok = 1; + + s++; + } + if (!ok) + return -EINVAL; + *res = acc; + return 0; +} + +int kstrtoull(const char *s, unsigned int base, unsigned long long *res) +{ + if (s[0] == '+') + s++; + return _kstrtoull(s, base, res); +} +EXPORT_SYMBOL(kstrtoull); + +int kstrtoll(const char *s, unsigned int base, long long *res) +{ + unsigned long long tmp; + int rv; + + if (s[0] == '-') { + rv = _kstrtoull(s + 1, base, &tmp); + if (rv < 0) + return rv; + if ((long long)(-tmp) >= 0) + return -ERANGE; + *res = -tmp; + } else { + rv = kstrtoull(s, base, &tmp); + if (rv < 0) + return rv; + if ((long long)tmp < 0) + return -ERANGE; + *res = tmp; + } + return 0; +} +EXPORT_SYMBOL(kstrtoll); + +/* Internal, do not use. */ +int _kstrtoul(const char *s, unsigned int base, unsigned long *res) +{ + unsigned long long tmp; + int rv; + + rv = kstrtoull(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (unsigned long long)(unsigned long)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(_kstrtoul); + +/* Internal, do not use. */ +int _kstrtol(const char *s, unsigned int base, long *res) +{ + long long tmp; + int rv; + + rv = kstrtoll(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (long long)(long)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(_kstrtol); + +int kstrtouint(const char *s, unsigned int base, unsigned int *res) +{ + unsigned long long tmp; + int rv; + + rv = kstrtoull(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (unsigned long long)(unsigned int)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtouint); + +int kstrtoint(const char *s, unsigned int base, int *res) +{ + long long tmp; + int rv; + + rv = kstrtoll(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (long long)(int)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtoint); + +int kstrtou16(const char *s, unsigned int base, u16 *res) +{ + unsigned long long tmp; + int rv; + + rv = kstrtoull(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (unsigned long long)(u16)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtou16); + +int kstrtos16(const char *s, unsigned int base, s16 *res) +{ + long long tmp; + int rv; + + rv = kstrtoll(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (long long)(s16)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtos16); + +int kstrtou8(const char *s, unsigned int base, u8 *res) +{ + unsigned long long tmp; + int rv; + + rv = kstrtoull(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (unsigned long long)(u8)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtou8); + +int kstrtos8(const char *s, unsigned int base, s8 *res) +{ + long long tmp; + int rv; + + rv = kstrtoll(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (long long)(s8)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtos8); diff --git a/lib/test-kstrtox.c b/lib/test-kstrtox.c new file mode 100644 index 0000000000000..325c2f9ecebdc --- /dev/null +++ b/lib/test-kstrtox.c @@ -0,0 +1,739 @@ +#include +#include +#include + +#define for_each_test(i, test) \ + for (i = 0; i < sizeof(test) / sizeof(test[0]); i++) + +struct test_fail { + const char *str; + unsigned int base; +}; + +#define DEFINE_TEST_FAIL(test) \ + const struct test_fail test[] __initdata + +#define DECLARE_TEST_OK(type, test_type) \ + test_type { \ + const char *str; \ + unsigned int base; \ + type expected_res; \ + } + +#define DEFINE_TEST_OK(type, test) \ + const type test[] __initdata + +#define TEST_FAIL(fn, type, fmt, test) \ +{ \ + unsigned int i; \ + \ + for_each_test(i, test) { \ + const struct test_fail *t = &test[i]; \ + type tmp; \ + int rv; \ + \ + tmp = 0; \ + rv = fn(t->str, t->base, &tmp); \ + if (rv >= 0) { \ + WARN(1, "str '%s', base %u, expected -E, got %d/" fmt "\n", \ + t->str, t->base, rv, tmp); \ + continue; \ + } \ + } \ +} + +#define TEST_OK(fn, type, fmt, test) \ +{ \ + unsigned int i; \ + \ + for_each_test(i, test) { \ + const typeof(test[0]) *t = &test[i]; \ + type res; \ + int rv; \ + \ + rv = fn(t->str, t->base, &res); \ + if (rv != 0) { \ + WARN(1, "str '%s', base %u, expected 0/" fmt ", got %d\n", \ + t->str, t->base, t->expected_res, rv); \ + continue; \ + } \ + if (res != t->expected_res) { \ + WARN(1, "str '%s', base %u, expected " fmt ", got " fmt "\n", \ + t->str, t->base, t->expected_res, res); \ + continue; \ + } \ + } \ +} + +static void __init test_kstrtoull_ok(void) +{ + DECLARE_TEST_OK(unsigned long long, struct test_ull); + static DEFINE_TEST_OK(struct test_ull, test_ull_ok) = { + {"0", 10, 0ULL}, + {"1", 10, 1ULL}, + {"127", 10, 127ULL}, + {"128", 10, 128ULL}, + {"129", 10, 129ULL}, + {"255", 10, 255ULL}, + {"256", 10, 256ULL}, + {"257", 10, 257ULL}, + {"32767", 10, 32767ULL}, + {"32768", 10, 32768ULL}, + {"32769", 10, 32769ULL}, + {"65535", 10, 65535ULL}, + {"65536", 10, 65536ULL}, + {"65537", 10, 65537ULL}, + {"2147483647", 10, 2147483647ULL}, + {"2147483648", 10, 2147483648ULL}, + {"2147483649", 10, 2147483649ULL}, + {"4294967295", 10, 4294967295ULL}, + {"4294967296", 10, 4294967296ULL}, + {"4294967297", 10, 4294967297ULL}, + {"9223372036854775807", 10, 9223372036854775807ULL}, + {"9223372036854775808", 10, 9223372036854775808ULL}, + {"9223372036854775809", 10, 9223372036854775809ULL}, + {"18446744073709551614", 10, 18446744073709551614ULL}, + {"18446744073709551615", 10, 18446744073709551615ULL}, + + {"00", 8, 00ULL}, + {"01", 8, 01ULL}, + {"0177", 8, 0177ULL}, + {"0200", 8, 0200ULL}, + {"0201", 8, 0201ULL}, + {"0377", 8, 0377ULL}, + {"0400", 8, 0400ULL}, + {"0401", 8, 0401ULL}, + {"077777", 8, 077777ULL}, + {"0100000", 8, 0100000ULL}, + {"0100001", 8, 0100001ULL}, + {"0177777", 8, 0177777ULL}, + {"0200000", 8, 0200000ULL}, + {"0200001", 8, 0200001ULL}, + {"017777777777", 8, 017777777777ULL}, + {"020000000000", 8, 020000000000ULL}, + {"020000000001", 8, 020000000001ULL}, + {"037777777777", 8, 037777777777ULL}, + {"040000000000", 8, 040000000000ULL}, + {"040000000001", 8, 040000000001ULL}, + {"0777777777777777777777", 8, 0777777777777777777777ULL}, + {"01000000000000000000000", 8, 01000000000000000000000ULL}, + {"01000000000000000000001", 8, 01000000000000000000001ULL}, + {"01777777777777777777776", 8, 01777777777777777777776ULL}, + {"01777777777777777777777", 8, 01777777777777777777777ULL}, + + {"0x0", 16, 0x0ULL}, + {"0x1", 16, 0x1ULL}, + {"0x7f", 16, 0x7fULL}, + {"0x80", 16, 0x80ULL}, + {"0x81", 16, 0x81ULL}, + {"0xff", 16, 0xffULL}, + {"0x100", 16, 0x100ULL}, + {"0x101", 16, 0x101ULL}, + {"0x7fff", 16, 0x7fffULL}, + {"0x8000", 16, 0x8000ULL}, + {"0x8001", 16, 0x8001ULL}, + {"0xffff", 16, 0xffffULL}, + {"0x10000", 16, 0x10000ULL}, + {"0x10001", 16, 0x10001ULL}, + {"0x7fffffff", 16, 0x7fffffffULL}, + {"0x80000000", 16, 0x80000000ULL}, + {"0x80000001", 16, 0x80000001ULL}, + {"0xffffffff", 16, 0xffffffffULL}, + {"0x100000000", 16, 0x100000000ULL}, + {"0x100000001", 16, 0x100000001ULL}, + {"0x7fffffffffffffff", 16, 0x7fffffffffffffffULL}, + {"0x8000000000000000", 16, 0x8000000000000000ULL}, + {"0x8000000000000001", 16, 0x8000000000000001ULL}, + {"0xfffffffffffffffe", 16, 0xfffffffffffffffeULL}, + {"0xffffffffffffffff", 16, 0xffffffffffffffffULL}, + + {"0\n", 0, 0ULL}, + }; + TEST_OK(kstrtoull, unsigned long long, "%llu", test_ull_ok); +} + +static void __init test_kstrtoull_fail(void) +{ + static DEFINE_TEST_FAIL(test_ull_fail) = { + {"", 0}, + {"", 8}, + {"", 10}, + {"", 16}, + {"\n", 0}, + {"\n", 8}, + {"\n", 10}, + {"\n", 16}, + {"\n0", 0}, + {"\n0", 8}, + {"\n0", 10}, + {"\n0", 16}, + {"+", 0}, + {"+", 8}, + {"+", 10}, + {"+", 16}, + {"-", 0}, + {"-", 8}, + {"-", 10}, + {"-", 16}, + {"0x", 0}, + {"0x", 16}, + {"0X", 0}, + {"0X", 16}, + {"0 ", 0}, + {"1+", 0}, + {"1-", 0}, + {" 2", 0}, + /* base autodetection */ + {"0x0z", 0}, + {"0z", 0}, + {"a", 0}, + /* digit >= base */ + {"2", 2}, + {"8", 8}, + {"a", 10}, + {"A", 10}, + {"g", 16}, + {"G", 16}, + /* overflow */ + {"10000000000000000000000000000000000000000000000000000000000000000", 2}, + {"2000000000000000000000", 8}, + {"18446744073709551616", 10}, + {"10000000000000000", 16}, + /* negative */ + {"-0", 0}, + {"-0", 8}, + {"-0", 10}, + {"-0", 16}, + {"-1", 0}, + {"-1", 8}, + {"-1", 10}, + {"-1", 16}, + /* sign is first character if any */ + {"-+1", 0}, + {"-+1", 8}, + {"-+1", 10}, + {"-+1", 16}, + /* nothing after \n */ + {"0\n0", 0}, + {"0\n0", 8}, + {"0\n0", 10}, + {"0\n0", 16}, + {"0\n+", 0}, + {"0\n+", 8}, + {"0\n+", 10}, + {"0\n+", 16}, + {"0\n-", 0}, + {"0\n-", 8}, + {"0\n-", 10}, + {"0\n-", 16}, + {"0\n ", 0}, + {"0\n ", 8}, + {"0\n ", 10}, + {"0\n ", 16}, + }; + TEST_FAIL(kstrtoull, unsigned long long, "%llu", test_ull_fail); +} + +static void __init test_kstrtoll_ok(void) +{ + DECLARE_TEST_OK(long long, struct test_ll); + static DEFINE_TEST_OK(struct test_ll, test_ll_ok) = { + {"0", 10, 0LL}, + {"1", 10, 1LL}, + {"127", 10, 127LL}, + {"128", 10, 128LL}, + {"129", 10, 129LL}, + {"255", 10, 255LL}, + {"256", 10, 256LL}, + {"257", 10, 257LL}, + {"32767", 10, 32767LL}, + {"32768", 10, 32768LL}, + {"32769", 10, 32769LL}, + {"65535", 10, 65535LL}, + {"65536", 10, 65536LL}, + {"65537", 10, 65537LL}, + {"2147483647", 10, 2147483647LL}, + {"2147483648", 10, 2147483648LL}, + {"2147483649", 10, 2147483649LL}, + {"4294967295", 10, 4294967295LL}, + {"4294967296", 10, 4294967296LL}, + {"4294967297", 10, 4294967297LL}, + {"9223372036854775807", 10, 9223372036854775807LL}, + + {"-1", 10, -1LL}, + {"-2", 10, -2LL}, + {"-9223372036854775808", 10, LLONG_MIN}, + }; + TEST_OK(kstrtoll, long long, "%lld", test_ll_ok); +} + +static void __init test_kstrtoll_fail(void) +{ + static DEFINE_TEST_FAIL(test_ll_fail) = { + {"9223372036854775808", 10}, + {"9223372036854775809", 10}, + {"18446744073709551614", 10}, + {"18446744073709551615", 10}, + {"-9223372036854775809", 10}, + {"-18446744073709551614", 10}, + {"-18446744073709551615", 10}, + /* negative zero isn't an integer in Linux */ + {"-0", 0}, + {"-0", 8}, + {"-0", 10}, + {"-0", 16}, + /* sign is first character if any */ + {"-+1", 0}, + {"-+1", 8}, + {"-+1", 10}, + {"-+1", 16}, + }; + TEST_FAIL(kstrtoll, long long, "%lld", test_ll_fail); +} + +static void __init test_kstrtou64_ok(void) +{ + DECLARE_TEST_OK(u64, struct test_u64); + static DEFINE_TEST_OK(struct test_u64, test_u64_ok) = { + {"0", 10, 0}, + {"1", 10, 1}, + {"126", 10, 126}, + {"127", 10, 127}, + {"128", 10, 128}, + {"129", 10, 129}, + {"254", 10, 254}, + {"255", 10, 255}, + {"256", 10, 256}, + {"257", 10, 257}, + {"32766", 10, 32766}, + {"32767", 10, 32767}, + {"32768", 10, 32768}, + {"32769", 10, 32769}, + {"65534", 10, 65534}, + {"65535", 10, 65535}, + {"65536", 10, 65536}, + {"65537", 10, 65537}, + {"2147483646", 10, 2147483646}, + {"2147483647", 10, 2147483647}, + {"2147483648", 10, 2147483648}, + {"2147483649", 10, 2147483649}, + {"4294967294", 10, 4294967294}, + {"4294967295", 10, 4294967295}, + {"4294967296", 10, 4294967296}, + {"4294967297", 10, 4294967297}, + {"9223372036854775806", 10, 9223372036854775806ULL}, + {"9223372036854775807", 10, 9223372036854775807ULL}, + {"9223372036854775808", 10, 9223372036854775808ULL}, + {"9223372036854775809", 10, 9223372036854775809ULL}, + {"18446744073709551614", 10, 18446744073709551614ULL}, + {"18446744073709551615", 10, 18446744073709551615ULL}, + }; + TEST_OK(kstrtou64, u64, "%llu", test_u64_ok); +} + +static void __init test_kstrtou64_fail(void) +{ + static DEFINE_TEST_FAIL(test_u64_fail) = { + {"-2", 10}, + {"-1", 10}, + {"18446744073709551616", 10}, + {"18446744073709551617", 10}, + }; + TEST_FAIL(kstrtou64, u64, "%llu", test_u64_fail); +} + +static void __init test_kstrtos64_ok(void) +{ + DECLARE_TEST_OK(s64, struct test_s64); + static DEFINE_TEST_OK(struct test_s64, test_s64_ok) = { + {"-128", 10, -128}, + {"-127", 10, -127}, + {"-1", 10, -1}, + {"0", 10, 0}, + {"1", 10, 1}, + {"126", 10, 126}, + {"127", 10, 127}, + {"128", 10, 128}, + {"129", 10, 129}, + {"254", 10, 254}, + {"255", 10, 255}, + {"256", 10, 256}, + {"257", 10, 257}, + {"32766", 10, 32766}, + {"32767", 10, 32767}, + {"32768", 10, 32768}, + {"32769", 10, 32769}, + {"65534", 10, 65534}, + {"65535", 10, 65535}, + {"65536", 10, 65536}, + {"65537", 10, 65537}, + {"2147483646", 10, 2147483646}, + {"2147483647", 10, 2147483647}, + {"2147483648", 10, 2147483648}, + {"2147483649", 10, 2147483649}, + {"4294967294", 10, 4294967294}, + {"4294967295", 10, 4294967295}, + {"4294967296", 10, 4294967296}, + {"4294967297", 10, 4294967297}, + {"9223372036854775806", 10, 9223372036854775806LL}, + {"9223372036854775807", 10, 9223372036854775807LL}, + }; + TEST_OK(kstrtos64, s64, "%lld", test_s64_ok); +} + +static void __init test_kstrtos64_fail(void) +{ + static DEFINE_TEST_FAIL(test_s64_fail) = { + {"9223372036854775808", 10}, + {"9223372036854775809", 10}, + {"18446744073709551614", 10}, + {"18446744073709551615", 10}, + {"18446744073709551616", 10}, + {"18446744073709551617", 10}, + }; + TEST_FAIL(kstrtos64, s64, "%lld", test_s64_fail); +} + +static void __init test_kstrtou32_ok(void) +{ + DECLARE_TEST_OK(u32, struct test_u32); + static DEFINE_TEST_OK(struct test_u32, test_u32_ok) = { + {"0", 10, 0}, + {"1", 10, 1}, + {"126", 10, 126}, + {"127", 10, 127}, + {"128", 10, 128}, + {"129", 10, 129}, + {"254", 10, 254}, + {"255", 10, 255}, + {"256", 10, 256}, + {"257", 10, 257}, + {"32766", 10, 32766}, + {"32767", 10, 32767}, + {"32768", 10, 32768}, + {"32769", 10, 32769}, + {"65534", 10, 65534}, + {"65535", 10, 65535}, + {"65536", 10, 65536}, + {"65537", 10, 65537}, + {"2147483646", 10, 2147483646}, + {"2147483647", 10, 2147483647}, + {"2147483648", 10, 2147483648}, + {"2147483649", 10, 2147483649}, + {"4294967294", 10, 4294967294}, + {"4294967295", 10, 4294967295}, + }; + TEST_OK(kstrtou32, u32, "%u", test_u32_ok); +} + +static void __init test_kstrtou32_fail(void) +{ + static DEFINE_TEST_FAIL(test_u32_fail) = { + {"-2", 10}, + {"-1", 10}, + {"4294967296", 10}, + {"4294967297", 10}, + {"9223372036854775806", 10}, + {"9223372036854775807", 10}, + {"9223372036854775808", 10}, + {"9223372036854775809", 10}, + {"18446744073709551614", 10}, + {"18446744073709551615", 10}, + {"18446744073709551616", 10}, + {"18446744073709551617", 10}, + }; + TEST_FAIL(kstrtou32, u32, "%u", test_u32_fail); +} + +static void __init test_kstrtos32_ok(void) +{ + DECLARE_TEST_OK(s32, struct test_s32); + static DEFINE_TEST_OK(struct test_s32, test_s32_ok) = { + {"-128", 10, -128}, + {"-127", 10, -127}, + {"-1", 10, -1}, + {"0", 10, 0}, + {"1", 10, 1}, + {"126", 10, 126}, + {"127", 10, 127}, + {"128", 10, 128}, + {"129", 10, 129}, + {"254", 10, 254}, + {"255", 10, 255}, + {"256", 10, 256}, + {"257", 10, 257}, + {"32766", 10, 32766}, + {"32767", 10, 32767}, + {"32768", 10, 32768}, + {"32769", 10, 32769}, + {"65534", 10, 65534}, + {"65535", 10, 65535}, + {"65536", 10, 65536}, + {"65537", 10, 65537}, + {"2147483646", 10, 2147483646}, + {"2147483647", 10, 2147483647}, + }; + TEST_OK(kstrtos32, s32, "%d", test_s32_ok); +} + +static void __init test_kstrtos32_fail(void) +{ + static DEFINE_TEST_FAIL(test_s32_fail) = { + {"2147483648", 10}, + {"2147483649", 10}, + {"4294967294", 10}, + {"4294967295", 10}, + {"4294967296", 10}, + {"4294967297", 10}, + {"9223372036854775806", 10}, + {"9223372036854775807", 10}, + {"9223372036854775808", 10}, + {"9223372036854775809", 10}, + {"18446744073709551614", 10}, + {"18446744073709551615", 10}, + {"18446744073709551616", 10}, + {"18446744073709551617", 10}, + }; + TEST_FAIL(kstrtos32, s32, "%d", test_s32_fail); +} + +static void __init test_kstrtou16_ok(void) +{ + DECLARE_TEST_OK(u16, struct test_u16); + static DEFINE_TEST_OK(struct test_u16, test_u16_ok) = { + {"0", 10, 0}, + {"1", 10, 1}, + {"126", 10, 126}, + {"127", 10, 127}, + {"128", 10, 128}, + {"129", 10, 129}, + {"254", 10, 254}, + {"255", 10, 255}, + {"256", 10, 256}, + {"257", 10, 257}, + {"32766", 10, 32766}, + {"32767", 10, 32767}, + {"32768", 10, 32768}, + {"32769", 10, 32769}, + {"65534", 10, 65534}, + {"65535", 10, 65535}, + }; + TEST_OK(kstrtou16, u16, "%hu", test_u16_ok); +} + +static void __init test_kstrtou16_fail(void) +{ + static DEFINE_TEST_FAIL(test_u16_fail) = { + {"-2", 10}, + {"-1", 10}, + {"65536", 10}, + {"65537", 10}, + {"2147483646", 10}, + {"2147483647", 10}, + {"2147483648", 10}, + {"2147483649", 10}, + {"4294967294", 10}, + {"4294967295", 10}, + {"4294967296", 10}, + {"4294967297", 10}, + {"9223372036854775806", 10}, + {"9223372036854775807", 10}, + {"9223372036854775808", 10}, + {"9223372036854775809", 10}, + {"18446744073709551614", 10}, + {"18446744073709551615", 10}, + {"18446744073709551616", 10}, + {"18446744073709551617", 10}, + }; + TEST_FAIL(kstrtou16, u16, "%hu", test_u16_fail); +} + +static void __init test_kstrtos16_ok(void) +{ + DECLARE_TEST_OK(s16, struct test_s16); + static DEFINE_TEST_OK(struct test_s16, test_s16_ok) = { + {"-130", 10, -130}, + {"-129", 10, -129}, + {"-128", 10, -128}, + {"-127", 10, -127}, + {"-1", 10, -1}, + {"0", 10, 0}, + {"1", 10, 1}, + {"126", 10, 126}, + {"127", 10, 127}, + {"128", 10, 128}, + {"129", 10, 129}, + {"254", 10, 254}, + {"255", 10, 255}, + {"256", 10, 256}, + {"257", 10, 257}, + {"32766", 10, 32766}, + {"32767", 10, 32767}, + }; + TEST_OK(kstrtos16, s16, "%hd", test_s16_ok); +} + +static void __init test_kstrtos16_fail(void) +{ + static DEFINE_TEST_FAIL(test_s16_fail) = { + {"32768", 10}, + {"32769", 10}, + {"65534", 10}, + {"65535", 10}, + {"65536", 10}, + {"65537", 10}, + {"2147483646", 10}, + {"2147483647", 10}, + {"2147483648", 10}, + {"2147483649", 10}, + {"4294967294", 10}, + {"4294967295", 10}, + {"4294967296", 10}, + {"4294967297", 10}, + {"9223372036854775806", 10}, + {"9223372036854775807", 10}, + {"9223372036854775808", 10}, + {"9223372036854775809", 10}, + {"18446744073709551614", 10}, + {"18446744073709551615", 10}, + {"18446744073709551616", 10}, + {"18446744073709551617", 10}, + }; + TEST_FAIL(kstrtos16, s16, "%hd", test_s16_fail); +} + +static void __init test_kstrtou8_ok(void) +{ + DECLARE_TEST_OK(u8, struct test_u8); + static DEFINE_TEST_OK(struct test_u8, test_u8_ok) = { + {"0", 10, 0}, + {"1", 10, 1}, + {"126", 10, 126}, + {"127", 10, 127}, + {"128", 10, 128}, + {"129", 10, 129}, + {"254", 10, 254}, + {"255", 10, 255}, + }; + TEST_OK(kstrtou8, u8, "%hhu", test_u8_ok); +} + +static void __init test_kstrtou8_fail(void) +{ + static DEFINE_TEST_FAIL(test_u8_fail) = { + {"-2", 10}, + {"-1", 10}, + {"256", 10}, + {"257", 10}, + {"32766", 10}, + {"32767", 10}, + {"32768", 10}, + {"32769", 10}, + {"65534", 10}, + {"65535", 10}, + {"65536", 10}, + {"65537", 10}, + {"2147483646", 10}, + {"2147483647", 10}, + {"2147483648", 10}, + {"2147483649", 10}, + {"4294967294", 10}, + {"4294967295", 10}, + {"4294967296", 10}, + {"4294967297", 10}, + {"9223372036854775806", 10}, + {"9223372036854775807", 10}, + {"9223372036854775808", 10}, + {"9223372036854775809", 10}, + {"18446744073709551614", 10}, + {"18446744073709551615", 10}, + {"18446744073709551616", 10}, + {"18446744073709551617", 10}, + }; + TEST_FAIL(kstrtou8, u8, "%hhu", test_u8_fail); +} + +static void __init test_kstrtos8_ok(void) +{ + DECLARE_TEST_OK(s8, struct test_s8); + static DEFINE_TEST_OK(struct test_s8, test_s8_ok) = { + {"-128", 10, -128}, + {"-127", 10, -127}, + {"-1", 10, -1}, + {"0", 10, 0}, + {"1", 10, 1}, + {"126", 10, 126}, + {"127", 10, 127}, + }; + TEST_OK(kstrtos8, s8, "%hhd", test_s8_ok); +} + +static void __init test_kstrtos8_fail(void) +{ + static DEFINE_TEST_FAIL(test_s8_fail) = { + {"-130", 10}, + {"-129", 10}, + {"128", 10}, + {"129", 10}, + {"254", 10}, + {"255", 10}, + {"256", 10}, + {"257", 10}, + {"32766", 10}, + {"32767", 10}, + {"32768", 10}, + {"32769", 10}, + {"65534", 10}, + {"65535", 10}, + {"65536", 10}, + {"65537", 10}, + {"2147483646", 10}, + {"2147483647", 10}, + {"2147483648", 10}, + {"2147483649", 10}, + {"4294967294", 10}, + {"4294967295", 10}, + {"4294967296", 10}, + {"4294967297", 10}, + {"9223372036854775806", 10}, + {"9223372036854775807", 10}, + {"9223372036854775808", 10}, + {"9223372036854775809", 10}, + {"18446744073709551614", 10}, + {"18446744073709551615", 10}, + {"18446744073709551616", 10}, + {"18446744073709551617", 10}, + }; + TEST_FAIL(kstrtos8, s8, "%hhd", test_s8_fail); +} + +static int __init test_kstrtox_init(void) +{ + test_kstrtoull_ok(); + test_kstrtoull_fail(); + test_kstrtoll_ok(); + test_kstrtoll_fail(); + + test_kstrtou64_ok(); + test_kstrtou64_fail(); + test_kstrtos64_ok(); + test_kstrtos64_fail(); + + test_kstrtou32_ok(); + test_kstrtou32_fail(); + test_kstrtos32_ok(); + test_kstrtos32_fail(); + + test_kstrtou16_ok(); + test_kstrtou16_fail(); + test_kstrtos16_ok(); + test_kstrtos16_fail(); + + test_kstrtou8_ok(); + test_kstrtou8_fail(); + test_kstrtos8_ok(); + test_kstrtos8_fail(); + return -EINVAL; +} +module_init(test_kstrtox_init); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/lib/vsprintf.c b/lib/vsprintf.c index d3023df8477f6..f3fd99a6ad628 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -120,147 +120,6 @@ long long simple_strtoll(const char *cp, char **endp, unsigned int base) } EXPORT_SYMBOL(simple_strtoll); -/** - * strict_strtoul - convert a string to an unsigned long strictly - * @cp: The string to be converted - * @base: The number base to use - * @res: The converted result value - * - * strict_strtoul converts a string to an unsigned long only if the - * string is really an unsigned long string, any string containing - * any invalid char at the tail will be rejected and -EINVAL is returned, - * only a newline char at the tail is acceptible because people generally - * change a module parameter in the following way: - * - * echo 1024 > /sys/module/e1000/parameters/copybreak - * - * echo will append a newline to the tail. - * - * It returns 0 if conversion is successful and *res is set to the converted - * value, otherwise it returns -EINVAL and *res is set to 0. - * - * simple_strtoul just ignores the successive invalid characters and - * return the converted value of prefix part of the string. - */ -int strict_strtoul(const char *cp, unsigned int base, unsigned long *res) -{ - char *tail; - unsigned long val; - - *res = 0; - if (!*cp) - return -EINVAL; - - val = simple_strtoul(cp, &tail, base); - if (tail == cp) - return -EINVAL; - - if ((tail[0] == '\0') || (tail[0] == '\n' && tail[1] == '\0')) { - *res = val; - return 0; - } - - return -EINVAL; -} -EXPORT_SYMBOL(strict_strtoul); - -/** - * strict_strtol - convert a string to a long strictly - * @cp: The string to be converted - * @base: The number base to use - * @res: The converted result value - * - * strict_strtol is similiar to strict_strtoul, but it allows the first - * character of a string is '-'. - * - * It returns 0 if conversion is successful and *res is set to the converted - * value, otherwise it returns -EINVAL and *res is set to 0. - */ -int strict_strtol(const char *cp, unsigned int base, long *res) -{ - int ret; - if (*cp == '-') { - ret = strict_strtoul(cp + 1, base, (unsigned long *)res); - if (!ret) - *res = -(*res); - } else { - ret = strict_strtoul(cp, base, (unsigned long *)res); - } - - return ret; -} -EXPORT_SYMBOL(strict_strtol); - -/** - * strict_strtoull - convert a string to an unsigned long long strictly - * @cp: The string to be converted - * @base: The number base to use - * @res: The converted result value - * - * strict_strtoull converts a string to an unsigned long long only if the - * string is really an unsigned long long string, any string containing - * any invalid char at the tail will be rejected and -EINVAL is returned, - * only a newline char at the tail is acceptible because people generally - * change a module parameter in the following way: - * - * echo 1024 > /sys/module/e1000/parameters/copybreak - * - * echo will append a newline to the tail of the string. - * - * It returns 0 if conversion is successful and *res is set to the converted - * value, otherwise it returns -EINVAL and *res is set to 0. - * - * simple_strtoull just ignores the successive invalid characters and - * return the converted value of prefix part of the string. - */ -int strict_strtoull(const char *cp, unsigned int base, unsigned long long *res) -{ - char *tail; - unsigned long long val; - - *res = 0; - if (!*cp) - return -EINVAL; - - val = simple_strtoull(cp, &tail, base); - if (tail == cp) - return -EINVAL; - if ((tail[0] == '\0') || (tail[0] == '\n' && tail[1] == '\0')) { - *res = val; - return 0; - } - - return -EINVAL; -} -EXPORT_SYMBOL(strict_strtoull); - -/** - * strict_strtoll - convert a string to a long long strictly - * @cp: The string to be converted - * @base: The number base to use - * @res: The converted result value - * - * strict_strtoll is similiar to strict_strtoull, but it allows the first - * character of a string is '-'. - * - * It returns 0 if conversion is successful and *res is set to the converted - * value, otherwise it returns -EINVAL and *res is set to 0. - */ -int strict_strtoll(const char *cp, unsigned int base, long long *res) -{ - int ret; - if (*cp == '-') { - ret = strict_strtoull(cp + 1, base, (unsigned long long *)res); - if (!ret) - *res = -(*res); - } else { - ret = strict_strtoull(cp, base, (unsigned long long *)res); - } - - return ret; -} -EXPORT_SYMBOL(strict_strtoll); - static noinline_for_stack int skip_atoi(const char **s) { diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 4c0383da1c9a2..58434b346b0ff 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2809,9 +2809,9 @@ sub process { WARN("consider using a completion\n" . $herecurr); } -# recommend strict_strto* over simple_strto* +# recommend kstrto* over simple_strto* if ($line =~ /\bsimple_(strto.*?)\s*\(/) { - WARN("consider using strict_$1 in preference to simple_$1\n" . $herecurr); + WARN("consider using kstrto* in preference to simple_$1\n" . $herecurr); } # check for __initcall(), use device_initcall() explicitly please if ($line =~ /^.\s*__initcall\s*\(/) { From 6f517d4325c1c1da7ec91b6cfd58c67a72c3b421 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 14 Apr 2011 15:22:21 -0700 Subject: [PATCH 1304/2556] mm/thp: use conventional format for boolean attributes commit e27e6151b154ff6e5e8162efa291bc60196d29ea upstream. The conventional format for boolean attributes in sysfs is numeric ("0" or "1" followed by new-line). Any boolean attribute can then be read and written using a generic function. Using the strings "yes [no]", "[yes] no" (read), "yes" and "no" (write) will frustrate this. [akpm@linux-foundation.org: use kstrtoul()] [akpm@linux-foundation.org: test_bit() doesn't return 1/0, per Neil] Signed-off-by: Ben Hutchings Cc: Andrea Arcangeli Cc: Mel Gorman Cc: Johannes Weiner Cc: Rik van Riel Cc: Hugh Dickins Tested-by: David Rientjes Cc: NeilBrown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/huge_memory.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 113e35c475020..8f76561944178 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -244,24 +244,28 @@ static ssize_t single_flag_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf, enum transparent_hugepage_flag flag) { - if (test_bit(flag, &transparent_hugepage_flags)) - return sprintf(buf, "[yes] no\n"); - else - return sprintf(buf, "yes [no]\n"); + return sprintf(buf, "%d\n", + !!test_bit(flag, &transparent_hugepage_flags)); } + static ssize_t single_flag_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count, enum transparent_hugepage_flag flag) { - if (!memcmp("yes", buf, - min(sizeof("yes")-1, count))) { + unsigned long value; + int ret; + + ret = kstrtoul(buf, 10, &value); + if (ret < 0) + return ret; + if (value > 1) + return -EINVAL; + + if (value) set_bit(flag, &transparent_hugepage_flags); - } else if (!memcmp("no", buf, - min(sizeof("no")-1, count))) { + else clear_bit(flag, &transparent_hugepage_flags); - } else - return -EINVAL; return count; } From 2fa669669e7646c45037b3d9a2bb9cc9a0c7f928 Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Thu, 14 Apr 2011 15:22:20 -0700 Subject: [PATCH 1305/2556] ramfs: fix memleak on no-mmu arch commit b836aec53e2bce71de1d5415313380688c851477 upstream. On no-mmu arch, there is a memleak during shmem test. The cause of this memleak is ramfs_nommu_expand_for_mapping() added page refcount to 2 which makes iput() can't free that pages. The simple test file is like this: int main(void) { int i; key_t k = ftok("/etc", 42); for ( i=0; i<100; ++i) { int id = shmget(k, 10000, 0644|IPC_CREAT); if (id == -1) { printf("shmget error\n"); } if(shmctl(id, IPC_RMID, NULL ) == -1) { printf("shm rm error\n"); return -1; } } printf("run ok...\n"); return 0; } And the result: root:/> free total used free shared buffers Mem: 60320 17912 42408 0 0 -/+ buffers: 17912 42408 root:/> shmem run ok... root:/> free total used free shared buffers Mem: 60320 19096 41224 0 0 -/+ buffers: 19096 41224 root:/> shmem run ok... root:/> free total used free shared buffers Mem: 60320 20296 40024 0 0 -/+ buffers: 20296 40024 ... After this patch the test result is:(no memleak anymore) root:/> free total used free shared buffers Mem: 60320 16668 43652 0 0 -/+ buffers: 16668 43652 root:/> shmem run ok... root:/> free total used free shared buffers Mem: 60320 16668 43652 0 0 -/+ buffers: 16668 43652 Signed-off-by: Bob Liu Acked-by: Hugh Dickins Signed-off-by: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/ramfs/file-nommu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c index 9eead2c796b7f..fbb0b478a346f 100644 --- a/fs/ramfs/file-nommu.c +++ b/fs/ramfs/file-nommu.c @@ -112,6 +112,7 @@ int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize) SetPageDirty(page); unlock_page(page); + put_page(page); } return 0; From 9bf82fb0f4eb4035fae7c5436364e7b8b8b81e17 Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Thu, 14 Apr 2011 15:22:13 -0700 Subject: [PATCH 1306/2556] oom-kill: remove boost_dying_task_prio() commit 341aea2bc48bf652777fb015cc2b3dfa9a451817 upstream. This is an almost-revert of commit 93b43fa ("oom: give the dying task a higher priority"). That commit dramatically improved oom killer logic when a fork-bomb occurs. But I've found that it has nasty corner case. Now cpu cgroup has strange default RT runtime. It's 0! That said, if a process under cpu cgroup promote RT scheduling class, the process never run at all. If an admin inserts a !RT process into a cpu cgroup by setting rtruntime=0, usually it runs perfectly because a !RT task isn't affected by the rtruntime knob. But if it promotes an RT task via an explicit setscheduler() syscall or an OOM, the task can't run at all. In short, the oom killer doesn't work at all if admins are using cpu cgroup and don't touch the rtruntime knob. Eventually, kernel may hang up when oom kill occur. I and the original author Luis agreed to disable this logic. Signed-off-by: KOSAKI Motohiro Acked-by: Luis Claudio R. Goncalves Acked-by: KAMEZAWA Hiroyuki Reviewed-by: Minchan Kim Acked-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/oom_kill.c | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/mm/oom_kill.c b/mm/oom_kill.c index 33b58615072cc..ea16f720705ed 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -83,24 +83,6 @@ static bool has_intersects_mems_allowed(struct task_struct *tsk, } #endif /* CONFIG_NUMA */ -/* - * If this is a system OOM (not a memcg OOM) and the task selected to be - * killed is not already running at high (RT) priorities, speed up the - * recovery by boosting the dying task to the lowest FIFO priority. - * That helps with the recovery and avoids interfering with RT tasks. - */ -static void boost_dying_task_prio(struct task_struct *p, - struct mem_cgroup *mem) -{ - struct sched_param param = { .sched_priority = 1 }; - - if (mem) - return; - - if (!rt_task(p)) - sched_setscheduler_nocheck(p, SCHED_FIFO, ¶m); -} - /* * The process p may have detached its own ->mm while exiting or through * use_mm(), but one or more of its subthreads may still have a valid @@ -452,13 +434,6 @@ static int oom_kill_task(struct task_struct *p, struct mem_cgroup *mem) set_tsk_thread_flag(p, TIF_MEMDIE); force_sig(SIGKILL, p); - /* - * We give our sacrificial lamb high priority and access to - * all the memory it needs. That way it should be able to - * exit() and clear out its resources quickly... - */ - boost_dying_task_prio(p, mem); - return 0; } #undef K @@ -482,7 +457,6 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, */ if (p->flags & PF_EXITING) { set_tsk_thread_flag(p, TIF_MEMDIE); - boost_dying_task_prio(p, mem); return 0; } @@ -701,7 +675,6 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, */ if (fatal_signal_pending(current)) { set_thread_flag(TIF_MEMDIE); - boost_dying_task_prio(current, NULL); return; } From 800f0cbf5b425df719f1905f9eff71b44dc244ab Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 14 Apr 2011 15:22:07 -0700 Subject: [PATCH 1307/2556] MAINTAINERS: update STABLE BRANCH info commit d00ebeac5f24f290636f7a895dafc124b2930a08 upstream. Drop Chris Wright from STABLE maintainers. He hasn't done STABLE release work for quite some time. Signed-off-by: Randy Dunlap Acked-by: Chris Wright Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index f1bc3dc6b3699..80540e33f1ec3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5944,7 +5944,6 @@ F: arch/alpha/kernel/srm_env.c STABLE BRANCH M: Greg Kroah-Hartman -M: Chris Wright L: stable@kernel.org S: Maintained From 78caa50ce2c5d8ccf9547978aa74e1ba5de08b00 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 13 Apr 2011 10:31:52 +0300 Subject: [PATCH 1308/2556] UBIFS: fix oops when R/O file-system is fsync'ed commit 78530bf7f2559b317c04991b52217c1608d5a58d upstream. This patch fixes severe UBIFS bug: UBIFS oopses when we 'fsync()' an file on R/O-mounter file-system. We (the UBIFS authors) incorrectly thought that VFS would not propagate 'fsync()' down to the file-system if it is read-only, but this is not the case. It is easy to exploit this bug using the following simple perl script: use strict; use File::Sync qw(fsync sync); die "File path is not specified" if not defined $ARGV[0]; my $path = $ARGV[0]; open FILE, "<", "$path" or die "Cannot open $path: $!"; fsync(\*FILE) or die "cannot fsync $path: $!"; close FILE or die "Cannot close $path: $!"; Thanks to Reuben Dowle for reporting about this issue. Signed-off-by: Artem Bityutskiy Reported-by: Reuben Dowle Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/file.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index d77db7e36484e..fe14f4df4ca0b 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -1309,6 +1309,9 @@ int ubifs_fsync(struct file *file, int datasync) dbg_gen("syncing inode %lu", inode->i_ino); + if (inode->i_sb->s_flags & MS_RDONLY) + return 0; + /* * VFS has already synchronized dirty pages for this inode. Synchronize * the inode unless this is a 'datasync()' call. From d7b7f0c401303dd7eafcff11752fe74d3b75c766 Mon Sep 17 00:00:00 2001 From: Boris Ostrovsky Date: Tue, 15 Mar 2011 12:13:44 -0400 Subject: [PATCH 1309/2556] x86, AMD: Set ARAT feature on AMD processors commit b87cf80af3ba4b4c008b4face3c68d604e1715c6 upstream. Support for Always Running APIC timer (ARAT) was introduced in commit db954b5898dd3ef3ef93f4144158ea8f97deb058. This feature allows us to avoid switching timers from LAPIC to something else (e.g. HPET) and go into timer broadcasts when entering deep C-states. AMD processors don't provide a CPUID bit for that feature but they also keep APIC timers running in deep C-states (except for cases when the processor is affected by erratum 400). Therefore we should set ARAT feature bit on AMD CPUs. Tested-by: Borislav Petkov Acked-by: Andreas Herrmann Acked-by: Mark Langsdorf Acked-by: Thomas Gleixner Signed-off-by: Boris Ostrovsky LKML-Reference: <1300205624-4813-1-git-send-email-ostr@amd64.org> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/amd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 7c7bedb83c5a4..48eaa1b6fc46d 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -594,6 +594,10 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c) } } #endif + + /* As a rule processors have APIC timer running in deep C states */ + if (c->x86 >= 0xf && !cpu_has_amd_erratum(amd_erratum_400)) + set_cpu_cap(c, X86_FEATURE_ARAT); } #ifdef CONFIG_X86_32 From 669479155adaf2c0e21934ef1911c9e583d10f5e Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Fri, 15 Apr 2011 14:47:40 +0200 Subject: [PATCH 1310/2556] x86, amd: Disable GartTlbWlkErr when BIOS forgets it commit 5bbc097d890409d8eff4e3f1d26f11a9d6b7c07e upstream. This patch disables GartTlbWlk errors on AMD Fam10h CPUs if the BIOS forgets to do is (or is just too old). Letting these errors enabled can cause a sync-flood on the CPU causing a reboot. The AMD BKDG recommends disabling GART TLB Wlk Error completely. This patch is the fix for https://bugzilla.kernel.org/show_bug.cgi?id=33012 on my machine. Signed-off-by: Joerg Roedel Link: http://lkml.kernel.org/r/20110415131152.GJ18463@8bytes.org Tested-by: Alexandre Demers Signed-off-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/msr-index.h | 4 ++++ arch/x86/kernel/cpu/amd.c | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 43a18c77676d9..99b402c6a9158 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -92,11 +92,15 @@ #define MSR_IA32_MC0_ADDR 0x00000402 #define MSR_IA32_MC0_MISC 0x00000403 +#define MSR_AMD64_MC0_MASK 0xc0010044 + #define MSR_IA32_MCx_CTL(x) (MSR_IA32_MC0_CTL + 4*(x)) #define MSR_IA32_MCx_STATUS(x) (MSR_IA32_MC0_STATUS + 4*(x)) #define MSR_IA32_MCx_ADDR(x) (MSR_IA32_MC0_ADDR + 4*(x)) #define MSR_IA32_MCx_MISC(x) (MSR_IA32_MC0_MISC + 4*(x)) +#define MSR_AMD64_MCx_MASK(x) (MSR_AMD64_MC0_MASK + (x)) + /* These are consecutive and not in the normal 4er MCE bank block */ #define MSR_IA32_MC0_CTL2 0x00000280 #define MSR_IA32_MCx_CTL2(x) (MSR_IA32_MC0_CTL2 + (x)) diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 48eaa1b6fc46d..a2b9c7d413230 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -598,6 +598,25 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c) /* As a rule processors have APIC timer running in deep C states */ if (c->x86 >= 0xf && !cpu_has_amd_erratum(amd_erratum_400)) set_cpu_cap(c, X86_FEATURE_ARAT); + + /* + * Disable GART TLB Walk Errors on Fam10h. We do this here + * because this is always needed when GART is enabled, even in a + * kernel which has no MCE support built in. + */ + if (c->x86 == 0x10) { + /* + * BIOS should disable GartTlbWlk Errors themself. If + * it doesn't do it here as suggested by the BKDG. + * + * Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=33012 + */ + u64 mask; + + rdmsrl(MSR_AMD64_MCx_MASK(4), mask); + mask |= (1 << 10); + wrmsrl(MSR_AMD64_MCx_MASK(4), mask); + } } #ifdef CONFIG_X86_32 From 0b76f8fcb3f302aaf18c65337a472b32d8a74b67 Mon Sep 17 00:00:00 2001 From: Tim Chen Date: Fri, 15 Apr 2011 11:39:29 -0700 Subject: [PATCH 1311/2556] vfs: Fix absolute RCU path walk failures due to uninitialized seq number commit c1530019e311c91d14b24d8e74d233152d806e45 upstream. During RCU walk in path_lookupat and path_openat, the rcu lookup frequently failed if looking up an absolute path, because when root directory was looked up, seq number was not properly set in nameidata. We dropped out of RCU walk in nameidata_drop_rcu due to mismatch in directory entry's seq number. We reverted to slow path walk that need to take references. With the following patch, I saw a 50% increase in an exim mail server benchmark throughput on a 4-socket Nehalem-EX system. Signed-off-by: Tim Chen Reviewed-by: Andi Kleen Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/namei.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/namei.c b/fs/namei.c index a4689eb2df285..3095ca8a31adf 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -712,6 +712,7 @@ static __always_inline void set_root_rcu(struct nameidata *nd) do { seq = read_seqcount_begin(&fs->seq); nd->root = fs->root; + nd->seq = __read_seqcount_begin(&nd->root.dentry->d_seq); } while (read_seqcount_retry(&fs->seq, seq)); } } From c421122f3dea5b5c42133f67a8084e6c0793a35c Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 5 Apr 2011 13:57:53 +0100 Subject: [PATCH 1312/2556] ARM: 6864/1: hw_breakpoint: clear DBGVCR out of reset commit e89c0d7090c54d7b11b9b091e495a1ae345dd3ff upstream. The DBGVCR, used for configuring vector catch debug events, is UNKNOWN out of reset on ARMv7. When enabling monitor mode, this must be zeroed to avoid UNPREDICTABLE behaviour. This patch adds the zeroing code to the debug reset path. Reported-by: Stepan Moskovchenko Signed-off-by: Will Deacon Signed-off-by: Russell King Signed-off-by: Greg Kroah-Hartman --- arch/arm/kernel/hw_breakpoint.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c index 44b84fe6e1b0f..7e9a0c7f19889 100644 --- a/arch/arm/kernel/hw_breakpoint.c +++ b/arch/arm/kernel/hw_breakpoint.c @@ -868,6 +868,13 @@ static void reset_ctrl_regs(void *info) */ asm volatile("mcr p14, 0, %0, c1, c0, 4" : : "r" (0)); isb(); + + /* + * Clear any configured vector-catch events before + * enabling monitor mode. + */ + asm volatile("mcr p14, 0, %0, c0, c7, 0" : : "r" (0)); + isb(); } if (enable_monitor_mode()) From 93be96977dd8ef5bee7cf746e699d03627496e23 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Sun, 17 Apr 2011 10:20:19 +0200 Subject: [PATCH 1313/2556] i2c-algo-bit: Call pre/post_xfer for bit_test commit d3b3e15da14ded61c9654db05863b04a2435f4cc upstream. Apparently some distros set i2c-algo-bit.bit_test to 1 by default. In some cases this causes i2c_bit_add_bus to fail and prevents the i2c bus from being added. In the radeon case, we fail to add the ddc i2c buses which prevents the driver from being able to detect attached monitors. The i2c bus works fine even if bit_test fails. This is likely due to gpio switching that is required and handled in the pre/post_xfer hooks, so call the pre/post_xfer hooks in the bit test as well. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=36221 Signed-off-by: Alex Deucher Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/algos/i2c-algo-bit.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c index 38319a69bd0a9..d6d58684712bc 100644 --- a/drivers/i2c/algos/i2c-algo-bit.c +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -232,9 +232,17 @@ static int i2c_inb(struct i2c_adapter *i2c_adap) * Sanity check for the adapter hardware - check the reaction of * the bus lines only if it seems to be idle. */ -static int test_bus(struct i2c_algo_bit_data *adap, char *name) +static int test_bus(struct i2c_adapter *i2c_adap) { - int scl, sda; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + const char *name = i2c_adap->name; + int scl, sda, ret; + + if (adap->pre_xfer) { + ret = adap->pre_xfer(i2c_adap); + if (ret < 0) + return -ENODEV; + } if (adap->getscl == NULL) pr_info("%s: Testing SDA only, SCL is not readable\n", name); @@ -297,11 +305,19 @@ static int test_bus(struct i2c_algo_bit_data *adap, char *name) "while pulling SCL high!\n", name); goto bailout; } + + if (adap->post_xfer) + adap->post_xfer(i2c_adap); + pr_info("%s: Test OK\n", name); return 0; bailout: sdahi(adap); sclhi(adap); + + if (adap->post_xfer) + adap->post_xfer(i2c_adap); + return -ENODEV; } @@ -607,7 +623,7 @@ static int __i2c_bit_add_bus(struct i2c_adapter *adap, int ret; if (bit_test) { - ret = test_bus(bit_adap, adap->name); + ret = test_bus(adap); if (ret < 0) return -ENODEV; } From 2ff9ca5e095881fc9a5e3e86e5821ff0ad84724b Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 18 Mar 2011 04:26:24 -0400 Subject: [PATCH 1314/2556] RTC: add missing "return 0" in new alarm func for rtc-bfin.c commit 8c122b96866580c99e44f3f07ac93a993d964ec3 upstream. The new bfin_rtc_alarm_irq_enable function forgot to add a "return 0" to the end leading to the build warning: drivers/rtc/rtc-bfin.c: In function 'bfin_rtc_alarm_irq_enable': drivers/rtc/rtc-bfin.c:253: warning: control reaches end of non-void function CC: Thomas Gleixner CC: Alessandro Zummo Signed-off-by: Mike Frysinger Signed-off-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/rtc/rtc-bfin.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c index 17971d93354d2..0e61e2dad5d37 100644 --- a/drivers/rtc/rtc-bfin.c +++ b/drivers/rtc/rtc-bfin.c @@ -276,6 +276,8 @@ static int bfin_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) bfin_rtc_int_set_alarm(rtc); else bfin_rtc_int_clear(~(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)); + + return 0; } static int bfin_rtc_read_time(struct device *dev, struct rtc_time *tm) From 97c5a1c2d96e5dffe7e0c93cd0f5a50038c8e9e3 Mon Sep 17 00:00:00 2001 From: Ken Chen Date: Fri, 8 Apr 2011 12:20:16 -0700 Subject: [PATCH 1315/2556] sched: Fix erroneous all_pinned logic commit b30aef17f71cf9e24b10c11cbb5e5f0ebe8a85ab upstream. The scheduler load balancer has specific code to deal with cases of unbalanced system due to lots of unmovable tasks (for example because of hard CPU affinity). In those situation, it excludes the busiest CPU that has pinned tasks for load balance consideration such that it can perform second 2nd load balance pass on the rest of the system. This all works as designed if there is only one cgroup in the system. However, when we have multiple cgroups, this logic has false positives and triggers multiple load balance passes despite there are actually no pinned tasks at all. The reason it has false positives is that the all pinned logic is deep in the lowest function of can_migrate_task() and is too low level: load_balance_fair() iterates each task group and calls balance_tasks() to migrate target load. Along the way, balance_tasks() will also set a all_pinned variable. Given that task-groups are iterated, this all_pinned variable is essentially the status of last group in the scanning process. Task group can have number of reasons that no load being migrated, none due to cpu affinity. However, this status bit is being propagated back up to the higher level load_balance(), which incorrectly think that no tasks were moved. It kick off the all pinned logic and start multiple passes attempt to move load onto puller CPU. To fix this, move the all_pinned aggregation up at the iterator level. This ensures that the status is aggregated over all task-groups, not just last one in the list. Signed-off-by: Ken Chen Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/BANLkTi=ernzNawaR5tJZEsV_QVnfxqXmsQ@mail.gmail.com Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/sched_fair.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index 0c26e2df450ee..7406f3688060f 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c @@ -2043,21 +2043,20 @@ balance_tasks(struct rq *this_rq, int this_cpu, struct rq *busiest, enum cpu_idle_type idle, int *all_pinned, int *this_best_prio, struct cfs_rq *busiest_cfs_rq) { - int loops = 0, pulled = 0, pinned = 0; + int loops = 0, pulled = 0; long rem_load_move = max_load_move; struct task_struct *p, *n; if (max_load_move == 0) goto out; - pinned = 1; - list_for_each_entry_safe(p, n, &busiest_cfs_rq->tasks, se.group_node) { if (loops++ > sysctl_sched_nr_migrate) break; if ((p->se.load.weight >> 1) > rem_load_move || - !can_migrate_task(p, busiest, this_cpu, sd, idle, &pinned)) + !can_migrate_task(p, busiest, this_cpu, sd, idle, + all_pinned)) continue; pull_task(busiest, p, this_rq, this_cpu); @@ -2092,9 +2091,6 @@ balance_tasks(struct rq *this_rq, int this_cpu, struct rq *busiest, */ schedstat_add(sd, lb_gained[idle], pulled); - if (all_pinned) - *all_pinned = pinned; - return max_load_move - rem_load_move; } @@ -3297,6 +3293,7 @@ static int load_balance(int this_cpu, struct rq *this_rq, * still unbalanced. ld_moved simply stays zero, so it is * correctly treated as an imbalance. */ + all_pinned = 1; local_irq_save(flags); double_rq_lock(this_rq, busiest); ld_moved = move_tasks(this_rq, this_cpu, busiest, From 411999731208babec42c9d17f931a8d87586b05f Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Thu, 14 Apr 2011 15:22:12 -0700 Subject: [PATCH 1316/2556] vmscan: all_unreclaimable() use zone->all_unreclaimable as a name commit 929bea7c714220fc76ce3f75bef9056477c28e74 upstream. all_unreclaimable check in direct reclaim has been introduced at 2.6.19 by following commit. 2006 Sep 25; commit 408d8544; oom: use unreclaimable info And it went through strange history. firstly, following commit broke the logic unintentionally. 2008 Apr 29; commit a41f24ea; page allocator: smarter retry of costly-order allocations Two years later, I've found obvious meaningless code fragment and restored original intention by following commit. 2010 Jun 04; commit bb21c7ce; vmscan: fix do_try_to_free_pages() return value when priority==0 But, the logic didn't works when 32bit highmem system goes hibernation and Minchan slightly changed the algorithm and fixed it . 2010 Sep 22: commit d1908362: vmscan: check all_unreclaimable in direct reclaim path But, recently, Andrey Vagin found the new corner case. Look, struct zone { .. int all_unreclaimable; .. unsigned long pages_scanned; .. } zone->all_unreclaimable and zone->pages_scanned are neigher atomic variables nor protected by lock. Therefore zones can become a state of zone->page_scanned=0 and zone->all_unreclaimable=1. In this case, current all_unreclaimable() return false even though zone->all_unreclaimabe=1. This resulted in the kernel hanging up when executing a loop of the form 1. fork 2. mmap 3. touch memory 4. read memory 5. munmmap as described in http://www.gossamer-threads.com/lists/linux/kernel/1348725#1348725 Is this ignorable minor issue? No. Unfortunately, x86 has very small dma zone and it become zone->all_unreclamble=1 easily. and if it become all_unreclaimable=1, it never restore all_unreclaimable=0. Why? if all_unreclaimable=1, vmscan only try DEF_PRIORITY reclaim and a-few-lru-pages>>DEF_PRIORITY always makes 0. that mean no page scan at all! Eventually, oom-killer never works on such systems. That said, we can't use zone->pages_scanned for this purpose. This patch restore all_unreclaimable() use zone->all_unreclaimable as old. and in addition, to add oom_killer_disabled check to avoid reintroduce the issue of commit d1908362 ("vmscan: check all_unreclaimable in direct reclaim path"). Reported-by: Andrey Vagin Signed-off-by: KOSAKI Motohiro Cc: Nick Piggin Reviewed-by: Minchan Kim Reviewed-by: KAMEZAWA Hiroyuki Acked-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/vmscan.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 3b4a41d724894..06655208884c9 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -1988,17 +1989,12 @@ static bool zone_reclaimable(struct zone *zone) return zone->pages_scanned < zone_reclaimable_pages(zone) * 6; } -/* - * As hibernation is going on, kswapd is freezed so that it can't mark - * the zone into all_unreclaimable. It can't handle OOM during hibernation. - * So let's check zone's unreclaimable in direct reclaim as well as kswapd. - */ +/* All zones in zonelist are unreclaimable? */ static bool all_unreclaimable(struct zonelist *zonelist, struct scan_control *sc) { struct zoneref *z; struct zone *zone; - bool all_unreclaimable = true; for_each_zone_zonelist_nodemask(zone, z, zonelist, gfp_zone(sc->gfp_mask), sc->nodemask) { @@ -2006,13 +2002,11 @@ static bool all_unreclaimable(struct zonelist *zonelist, continue; if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL)) continue; - if (zone_reclaimable(zone)) { - all_unreclaimable = false; - break; - } + if (!zone->all_unreclaimable) + return false; } - return all_unreclaimable; + return true; } /* @@ -2108,6 +2102,14 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist, if (sc->nr_reclaimed) return sc->nr_reclaimed; + /* + * As hibernation is going on, kswapd is freezed so that it can't mark + * the zone into all_unreclaimable. Thus bypassing all_unreclaimable + * check. + */ + if (oom_killer_disabled) + return 0; + /* top priority shrink_zones still had more to do? don't OOM, then */ if (scanning_global_lru(sc) && !all_unreclaimable(zonelist, sc)) return 1; From 510edc2527fd3d25b6119d980dddff7213868b19 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Thu, 14 Apr 2011 15:22:09 -0700 Subject: [PATCH 1317/2556] brk: COMPAT_BRK: fix detection of randomized brk commit 4471a675dfc7ca676c165079e91c712b09dc9ce4 upstream. 5520e89 ("brk: fix min_brk lower bound computation for COMPAT_BRK") tried to get the whole logic of brk randomization for legacy (libc5-based) applications finally right. It turns out that the way to detect whether brk has actually been randomized in the end or not introduced by that patch still doesn't work for those binaries, as reported by Geert: : /sbin/init from my old m68k ramdisk exists prematurely. : : Before the patch: : : | brk(0x80005c8e) = 0x80006000 : : After the patch: : : | brk(0x80005c8e) = 0x80005c8e : : Old libc5 considers brk() to have failed if the return value is not : identical to the requested value. I don't like it, but currently see no better option than a bit flag in task_struct to catch the CONFIG_COMPAT_BRK && randomize_va_space == 2 case. Signed-off-by: Jiri Kosina Tested-by: Geert Uytterhoeven Reported-by: Geert Uytterhoeven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/binfmt_elf.c | 6 +++++- include/linux/sched.h | 3 +++ mm/mmap.c | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index d5b640ba6cb1a..70470b2b7f73d 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -941,9 +941,13 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) current->mm->start_stack = bprm->p; #ifdef arch_randomize_brk - if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1)) + if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1)) { current->mm->brk = current->mm->start_brk = arch_randomize_brk(current->mm); +#ifdef CONFIG_COMPAT_BRK + current->brk_randomized = 1; +#endif + } #endif if (current->personality & MMAP_PAGE_ZERO) { diff --git a/include/linux/sched.h b/include/linux/sched.h index 777d8a5ed06be..ed6c384ba6c89 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1254,6 +1254,9 @@ struct task_struct { #endif struct mm_struct *mm, *active_mm; +#ifdef CONFIG_COMPAT_BRK + unsigned brk_randomized:1; +#endif #if defined(SPLIT_RSS_COUNTING) struct task_rss_stat rss_stat; #endif diff --git a/mm/mmap.c b/mm/mmap.c index 8c05e5b43b69c..e27e0cf0de03c 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -259,7 +259,7 @@ SYSCALL_DEFINE1(brk, unsigned long, brk) * randomize_va_space to 2, which will still cause mm->start_brk * to be arbitrarily shifted */ - if (mm->start_brk > PAGE_ALIGN(mm->end_data)) + if (current->brk_randomized) min_brk = mm->start_brk; else min_brk = mm->end_data; From faaf43f0c4af76458d0f82fe6aa6735a40e4aa4d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 22 Mar 2011 11:31:37 +0200 Subject: [PATCH 1318/2556] usb: musb: temporarily make it bool commit 7a180e70cfc56e131bfe4796773df2acfc7d4180 upstream. Due to the recent changes to musb's glue layers, we can't compile musb-hdrc as a module - compilation will break due to undefined symbol musb_debug. In order to fix that, we need a big re-work of the debug support on the MUSB driver. Because that would mean a lot of new code coming into the -rc series, it's best to defer that to next merge window and for now just disable module support for MUSB. Once we get the refactor of the debugging support done, we can simply revert this patch and things will go back to normal again. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 4cbb7e4b368d2..74073b363c30f 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -14,7 +14,7 @@ config USB_MUSB_HDRC select TWL4030_USB if MACH_OMAP_3430SDP select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA select USB_OTG_UTILS - tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' + bool 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' help Say Y here if your system has a dual role high speed USB controller based on the Mentor Graphics silicon IP. Then @@ -30,8 +30,8 @@ config USB_MUSB_HDRC If you do not know what this is, please say N. - To compile this driver as a module, choose M here; the - module will be called "musb-hdrc". +# To compile this driver as a module, choose M here; the +# module will be called "musb-hdrc". choice prompt "Platform Glue Layer" From e4cd0daa1f8735f2263dadca3882c2ffa224b06c Mon Sep 17 00:00:00 2001 From: Christian Simon Date: Mon, 28 Mar 2011 21:54:47 +0200 Subject: [PATCH 1319/2556] USB: ftdi_sio: Added IDs for CTI USB Serial Devices commit 5a9443f08c83c294c5c806a689c1184b27cb26b3 upstream. I added new ProdutIds for two devices from CTI GmbH Leipzig. Signed-off-by: Christian Simon Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 2 ++ drivers/usb/serial/ftdi_sio_ids.h | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index f349a3629d00c..f9f0510827b03 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -151,6 +151,8 @@ static struct ftdi_sio_quirk ftdi_stmclite_quirk = { * /sys/bus/usb/ftdi_sio/new_id, then send patch/report! */ static struct usb_device_id id_table_combined [] = { + { USB_DEVICE(FTDI_VID, FTDI_CTI_MINI_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CTI_NANO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CANDAPTER_PID) }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 117e8e6f93c68..8e350192fd114 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -1140,3 +1140,12 @@ #define QIHARDWARE_VID 0x20B7 #define MILKYMISTONE_JTAGSERIAL_PID 0x0713 +/* + * CTI GmbH RS485 Converter http://www.cti-lean.com/ + */ +/* USB-485-Mini*/ +#define FTDI_CTI_MINI_PID 0xF608 +/* USB-Nano-485*/ +#define FTDI_CTI_NANO_PID 0xF60B + + From f2de099d01761aefb37022adc686772fbf2a8ae8 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 8 Apr 2011 17:38:22 +0200 Subject: [PATCH 1320/2556] USB: ftdi_sio: add PID for OCT DK201 docking station commit 11a31d84129dc3133417d626643d714c9df5317e upstream. Add PID 0x0103 for serial port of the OCT DK201 docking station. Reported-by: Jan Hoogenraad Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/ftdi_sio_ids.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index f9f0510827b03..be477a486284e 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -527,6 +527,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_8_PID) }, { USB_DEVICE(IDTECH_VID, IDTECH_IDT1221U_PID) }, { USB_DEVICE(OCT_VID, OCT_US101_PID) }, + { USB_DEVICE(OCT_VID, OCT_DK201_PID) }, { USB_DEVICE(FTDI_VID, FTDI_HE_TIRA1_PID), .driver_info = (kernel_ulong_t)&ftdi_HE_TIRA1_quirk }, { USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID), diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 8e350192fd114..c434686f4cd35 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -572,6 +572,7 @@ /* Note: OCT US101 is also rebadged as Dick Smith Electronics (NZ) XH6381 */ /* Also rebadged as Dick Smith Electronics (Aus) XH6451 */ /* Also rebadged as SIIG Inc. model US2308 hardware version 1 */ +#define OCT_DK201_PID 0x0103 /* OCT DK201 USB docking station */ #define OCT_US101_PID 0x0421 /* OCT US101 USB to RS-232 */ /* From 4fcb66124aab60e3913dc4f6dfb0a09ad0b4786d Mon Sep 17 00:00:00 2001 From: Paul Friedrich Date: Fri, 18 Mar 2011 11:13:55 +0100 Subject: [PATCH 1321/2556] USB: ftdi_sio: add ids for Hameg HO720 and HO730 commit c53c2fab40cf16e13af66f40bfd27200cda98d2f upstream. usb serial: ftdi_sio: add two missing USB ID's for Hameg interfaces HO720 and HO730 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 2 ++ drivers/usb/serial/ftdi_sio_ids.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index be477a486284e..31bba6ddfb279 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -788,6 +788,8 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, HAMEG_HO820_PID) }, + { USB_DEVICE(FTDI_VID, HAMEG_HO720_PID) }, + { USB_DEVICE(FTDI_VID, HAMEG_HO730_PID) }, { USB_DEVICE(FTDI_VID, HAMEG_HO870_PID) }, { USB_DEVICE(FTDI_VID, MJSG_GENERIC_PID) }, { USB_DEVICE(FTDI_VID, MJSG_SR_RADIO_PID) }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index c434686f4cd35..4e873ce579bbc 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -300,6 +300,8 @@ * Hameg HO820 and HO870 interface (using VID 0x0403) */ #define HAMEG_HO820_PID 0xed74 +#define HAMEG_HO730_PID 0xed73 +#define HAMEG_HO720_PID 0xed72 #define HAMEG_HO870_PID 0xed71 /* From dd8eece9b71a865b6f223c2f84058788870cd655 Mon Sep 17 00:00:00 2001 From: "Marius B. Kotsbak" Date: Tue, 22 Mar 2011 00:01:53 +0100 Subject: [PATCH 1322/2556] USB: option: Added support for Samsung GT-B3730/GT-B3710 LTE USB modem. commit 80f9df3e0093ad9f1eeefd2ff7fd27daaa518d25 upstream. Bind only modem AT command endpoint to option. Signed-off-by: Marius B. Kotsbak Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 75c7f456eed52..d77ff0435896b 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -407,6 +407,10 @@ static void option_instat_callback(struct urb *urb); /* ONDA MT825UP HSDPA 14.2 modem */ #define ONDA_MT825UP 0x000b +/* Samsung products */ +#define SAMSUNG_VENDOR_ID 0x04e8 +#define SAMSUNG_PRODUCT_GT_B3730 0x6889 + /* some devices interfaces need special handling due to a number of reasons */ enum option_blacklist_reason { OPTION_BLACKLIST_NONE = 0, @@ -968,6 +972,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD100) }, { USB_DEVICE(CELOT_VENDOR_ID, CELOT_PRODUCT_CT680M) }, /* CT-650 CDMA 450 1xEVDO modem */ { USB_DEVICE(ONDA_VENDOR_ID, ONDA_MT825UP) }, /* ONDA MT825UP modem */ + { USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_GT_B3730, USB_CLASS_CDC_DATA, 0x00, 0x00) }, /* Samsung GT-B3730/GT-B3710 LTE USB modem.*/ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); From c8b265db1e63fdb296ec2721f5c1473c44bc4dc5 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 18 Apr 2011 10:35:30 -0700 Subject: [PATCH 1323/2556] next_pidmap: fix overflow condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c78193e9c7bcbf25b8237ad0dec82f805c4ea69b upstream. next_pidmap() just quietly accepted whatever 'last' pid that was passed in, which is not all that safe when one of the users is /proc. Admittedly the proc code should do some sanity checking on the range (and that will be the next commit), but that doesn't mean that the helper functions should just do that pidmap pointer arithmetic without checking the range of its arguments. So clamp 'last' to PID_MAX_LIMIT. The fact that we then do "last+1" doesn't really matter, the for-loop does check against the end of the pidmap array properly (it's only the actual pointer arithmetic overflow case we need to worry about, and going one bit beyond isn't going to overflow). [ Use PID_MAX_LIMIT rather than pid_max as per Eric Biederman ] Reported-by: Tavis Ormandy Analyzed-by: Robert Święcki Cc: Eric W. Biederman Cc: Pavel Emelyanov Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- include/linux/pid.h | 2 +- kernel/pid.c | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/linux/pid.h b/include/linux/pid.h index 49f1c2f66e951..ec9f2df57f1b8 100644 --- a/include/linux/pid.h +++ b/include/linux/pid.h @@ -117,7 +117,7 @@ extern struct pid *find_vpid(int nr); */ extern struct pid *find_get_pid(int nr); extern struct pid *find_ge_pid(int nr, struct pid_namespace *); -int next_pidmap(struct pid_namespace *pid_ns, int last); +int next_pidmap(struct pid_namespace *pid_ns, unsigned int last); extern struct pid *alloc_pid(struct pid_namespace *ns); extern void free_pid(struct pid *pid); diff --git a/kernel/pid.c b/kernel/pid.c index 39b65b69584f5..6aeebc20d34b2 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -217,11 +217,14 @@ static int alloc_pidmap(struct pid_namespace *pid_ns) return -1; } -int next_pidmap(struct pid_namespace *pid_ns, int last) +int next_pidmap(struct pid_namespace *pid_ns, unsigned int last) { int offset; struct pidmap *map, *end; + if (last >= PID_MAX_LIMIT) + return -1; + offset = (last + 1) & BITS_PER_PAGE_MASK; map = &pid_ns->pidmap[(last + 1)/BITS_PER_PAGE]; end = &pid_ns->pidmap[PIDMAP_ENTRIES]; From a506a5b9282e4b944ed314d2b30ce387c44ff9fe Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 18 Apr 2011 10:36:54 -0700 Subject: [PATCH 1324/2556] proc: do proper range check on readdir offset commit d8bdc59f215e62098bc5b4256fd9928bf27053a1 upstream. Rather than pass in some random truncated offset to the pid-related functions, check that the offset is in range up-front. This is just cleanup, the previous commit fixed the real problem. Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/proc/base.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index d49c4b5d2c3e9..71a85d80f83c5 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -3066,11 +3066,16 @@ static int proc_pid_fill_cache(struct file *filp, void *dirent, filldir_t filldi /* for the /proc/ directory itself, after non-process stuff has been done */ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) { - unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY; - struct task_struct *reaper = get_proc_task(filp->f_path.dentry->d_inode); + unsigned int nr; + struct task_struct *reaper; struct tgid_iter iter; struct pid_namespace *ns; + if (filp->f_pos >= PID_MAX_LIMIT + TGID_OFFSET) + goto out_no_task; + nr = filp->f_pos - FIRST_PROCESS_ENTRY; + + reaper = get_proc_task(filp->f_path.dentry->d_inode); if (!reaper) goto out_no_task; From 4efa7d8da6914e9cce94f405649e407ffa20a04e Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Thu, 7 Apr 2011 21:44:21 +0000 Subject: [PATCH 1325/2556] powerpc: Fix oops if scan_dispatch_log is called too early commit 84ffae55af79d7b8834fd0c08d0d1ebf2c77f91e upstream. We currently enable interrupts before the dispatch log for the boot cpu is setup. If a timer interrupt comes in early enough we oops in scan_dispatch_log: Unable to handle kernel paging request for data at address 0x00000010 ... .scan_dispatch_log+0xb0/0x170 .account_system_vtime+0xa0/0x220 .irq_enter+0x88/0xc0 .do_IRQ+0x48/0x230 The patch below adds a check to scan_dispatch_log to ensure the dispatch log has been allocated. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/time.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index aa9269600ca21..02d54e1892d75 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -229,6 +229,9 @@ static u64 scan_dispatch_log(u64 stop_tb) u64 stolen = 0; u64 dtb; + if (!dtl) + return 0; + if (i == vpa->dtl_idx) return 0; while (i < vpa->dtl_idx) { From fae123f8208d7d56b35c0c8e9a299e052f62dfcc Mon Sep 17 00:00:00 2001 From: Eric B Munson Date: Fri, 15 Apr 2011 08:12:30 +0000 Subject: [PATCH 1326/2556] powerpc/perf_event: Skip updating kernel counters if register value shrinks commit 86c74ab317c1ef4d37325e0d7ca8a01a796b0bd7 upstream. Because of speculative event roll back, it is possible for some event coutners to decrease between reads on POWER7. This causes a problem with the way that counters are updated. Delta calues are calculated in a 64 bit value and the top 32 bits are masked. If the register value has decreased, this leaves us with a very large positive value added to the kernel counters. This patch protects against this by skipping the update if the delta would be negative. This can lead to a lack of precision in the coutner values, but from my testing the value is typcially fewer than 10 samples at a time. Signed-off-by: Eric B Munson Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/perf_event.c | 37 ++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/arch/powerpc/kernel/perf_event.c b/arch/powerpc/kernel/perf_event.c index 97e0ae414940e..26e56e346912e 100644 --- a/arch/powerpc/kernel/perf_event.c +++ b/arch/powerpc/kernel/perf_event.c @@ -398,6 +398,25 @@ static int check_excludes(struct perf_event **ctrs, unsigned int cflags[], return 0; } +static u64 check_and_compute_delta(u64 prev, u64 val) +{ + u64 delta = (val - prev) & 0xfffffffful; + + /* + * POWER7 can roll back counter values, if the new value is smaller + * than the previous value it will cause the delta and the counter to + * have bogus values unless we rolled a counter over. If a coutner is + * rolled back, it will be smaller, but within 256, which is the maximum + * number of events to rollback at once. If we dectect a rollback + * return 0. This can lead to a small lack of precision in the + * counters. + */ + if (prev > val && (prev - val) < 256) + delta = 0; + + return delta; +} + static void power_pmu_read(struct perf_event *event) { s64 val, delta, prev; @@ -416,10 +435,11 @@ static void power_pmu_read(struct perf_event *event) prev = local64_read(&event->hw.prev_count); barrier(); val = read_pmc(event->hw.idx); + delta = check_and_compute_delta(prev, val); + if (!delta) + return; } while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev); - /* The counters are only 32 bits wide */ - delta = (val - prev) & 0xfffffffful; local64_add(delta, &event->count); local64_sub(delta, &event->hw.period_left); } @@ -449,8 +469,9 @@ static void freeze_limited_counters(struct cpu_hw_events *cpuhw, val = (event->hw.idx == 5) ? pmc5 : pmc6; prev = local64_read(&event->hw.prev_count); event->hw.idx = 0; - delta = (val - prev) & 0xfffffffful; - local64_add(delta, &event->count); + delta = check_and_compute_delta(prev, val); + if (delta) + local64_add(delta, &event->count); } } @@ -458,14 +479,16 @@ static void thaw_limited_counters(struct cpu_hw_events *cpuhw, unsigned long pmc5, unsigned long pmc6) { struct perf_event *event; - u64 val; + u64 val, prev; int i; for (i = 0; i < cpuhw->n_limited; ++i) { event = cpuhw->limited_counter[i]; event->hw.idx = cpuhw->limited_hwidx[i]; val = (event->hw.idx == 5) ? pmc5 : pmc6; - local64_set(&event->hw.prev_count, val); + prev = local64_read(&event->hw.prev_count); + if (check_and_compute_delta(prev, val)) + local64_set(&event->hw.prev_count, val); perf_event_update_userpage(event); } } @@ -1197,7 +1220,7 @@ static void record_and_restart(struct perf_event *event, unsigned long val, /* we don't have to worry about interrupts here */ prev = local64_read(&event->hw.prev_count); - delta = (val - prev) & 0xfffffffful; + delta = check_and_compute_delta(prev, val); local64_add(delta, &event->count); /* From a14f54520a7a50f1d591d848c65955eb0feb993b Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Mon, 4 Apr 2011 17:57:37 +0100 Subject: [PATCH 1327/2556] usb: Fix qcserial memory leak on rmmod commit 10c9ab15d6aee153968d150c05b3ee3df89673de upstream. qcprobe function allocates serial->private but this is never freed, this patch adds a new function qc_release() which frees serial->private, after calling usb_wwan_release Signed-off-by: Steven Hardy Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/qcserial.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 8858201eb1d39..6e3b933457f4e 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -205,6 +205,18 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) return retval; } +static void qc_release(struct usb_serial *serial) +{ + struct usb_wwan_intf_private *priv = usb_get_serial_data(serial); + + dbg("%s", __func__); + + /* Call usb_wwan release & free the private data allocated in qcprobe */ + usb_wwan_release(serial); + usb_set_serial_data(serial, NULL); + kfree(priv); +} + static struct usb_serial_driver qcdevice = { .driver = { .owner = THIS_MODULE, @@ -222,7 +234,7 @@ static struct usb_serial_driver qcdevice = { .chars_in_buffer = usb_wwan_chars_in_buffer, .attach = usb_wwan_startup, .disconnect = usb_wwan_disconnect, - .release = usb_wwan_release, + .release = qc_release, #ifdef CONFIG_PM .suspend = usb_wwan_suspend, .resume = usb_wwan_resume, From 4f377dfbba0ff6d83fcf9f4939d51ca54b8d4485 Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Mon, 4 Apr 2011 17:59:55 +0100 Subject: [PATCH 1328/2556] usb: qcserial avoid pointing to freed memory commit 99ab3f9e4eaec35fd2d7159c31b71f17f7e613e3 upstream. Rework the qcprobe logic such that serial->private is not set when qcprobe exits with -ENODEV, otherwise serial->private will point to freed memory on -ENODEV Signed-off-by: Steven Hardy Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/qcserial.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 6e3b933457f4e..cd638648479ad 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -111,7 +111,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) ifnum = intf->desc.bInterfaceNumber; dbg("This Interface = %d", ifnum); - data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), + data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); if (!data) return -ENOMEM; @@ -134,8 +134,10 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) { dbg("QDL port found"); - if (serial->interface->num_altsetting == 1) - return 0; + if (serial->interface->num_altsetting == 1) { + retval = 0; /* Success */ + break; + } retval = usb_set_interface(serial->dev, ifnum, 1); if (retval < 0) { @@ -145,7 +147,6 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) retval = -ENODEV; kfree(data); } - return retval; } break; @@ -177,7 +178,6 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) retval = -ENODEV; kfree(data); } - return retval; } else if (ifnum==3) { /* * NMEA (serial line 9600 8N1) @@ -199,9 +199,12 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) dev_err(&serial->dev->dev, "unknown number of interfaces: %d\n", nintf); kfree(data); - return -ENODEV; + retval = -ENODEV; } + /* Set serial->private if not returning -ENODEV */ + if (retval != -ENODEV) + usb_set_serial_data(serial, data); return retval; } From c0cc35919317b579b23514c1385f246f348852ad Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Mon, 4 Apr 2011 18:02:25 +0100 Subject: [PATCH 1329/2556] usb: qcserial add missing errorpath kfrees commit cb62d65f966146a39fdde548cb474dacf1d00fa5 upstream. There are two -ENODEV error paths in qcprobe where the allocated private data is not freed, this patch adds the two missing kfrees to avoid leaking memory on the error path Signed-off-by: Steven Hardy Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/qcserial.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index cd638648479ad..54a9dab1f33b4 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -167,6 +167,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) "Could not set interface, error %d\n", retval); retval = -ENODEV; + kfree(data); } } else if (ifnum == 2) { dbg("Modem port found"); @@ -191,6 +192,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) "Could not set interface, error %d\n", retval); retval = -ENODEV; + kfree(data); } } break; From 05cf278c7141fd2a4373e62161416e85aeb688bc Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 5 Apr 2011 13:36:15 -0400 Subject: [PATCH 1330/2556] USB: EHCI: unlink unused QHs when the controller is stopped commit 94ae4976e253757e9b03a44d27d41b20f1829d80 upstream. This patch (as1458) fixes a problem affecting ultra-reliable systems: When hardware failover of an EHCI controller occurs, the data structures do not get released correctly. This is because the routine responsible for removing unused QHs from the async schedule assumes the controller is running properly (the frame counter is used in determining how long the QH has been idle) -- but when a failover causes the controller to be electronically disconnected from the PCI bus, obviously it stops running. The solution is simple: Allow scan_async() to remove a QH from the async schedule if it has been idle for long enough _or_ if the controller is stopped. Signed-off-by: Alan Stern Reported-and-Tested-by: Dan Duval Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-q.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 5add8b5ddda88..baf7362b0e444 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1245,24 +1245,27 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) static void scan_async (struct ehci_hcd *ehci) { + bool stopped; struct ehci_qh *qh; enum ehci_timer_action action = TIMER_IO_WATCHDOG; ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index); timer_action_done (ehci, TIMER_ASYNC_SHRINK); rescan: + stopped = !HC_IS_RUNNING(ehci_to_hcd(ehci)->state); qh = ehci->async->qh_next.qh; if (likely (qh != NULL)) { do { /* clean any finished work for this qh */ - if (!list_empty (&qh->qtd_list) - && qh->stamp != ehci->stamp) { + if (!list_empty(&qh->qtd_list) && (stopped || + qh->stamp != ehci->stamp)) { int temp; /* unlinks could happen here; completion * reporting drops the lock. rescan using * the latest schedule, but don't rescan - * qhs we already finished (no looping). + * qhs we already finished (no looping) + * unless the controller is stopped. */ qh = qh_get (qh); qh->stamp = ehci->stamp; @@ -1283,9 +1286,9 @@ static void scan_async (struct ehci_hcd *ehci) */ if (list_empty(&qh->qtd_list) && qh->qh_state == QH_STATE_LINKED) { - if (!ehci->reclaim - && ((ehci->stamp - qh->stamp) & 0x1fff) - >= (EHCI_SHRINK_FRAMES * 8)) + if (!ehci->reclaim && (stopped || + ((ehci->stamp - qh->stamp) & 0x1fff) + >= EHCI_SHRINK_FRAMES * 8)) start_unlink_async(ehci, qh); else action = TIMER_ASYNC_SHRINK; From 9b53284b310075ecc40e70187383e2af268e4078 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 18 Mar 2011 21:29:01 -0700 Subject: [PATCH 1331/2556] USB: fix formatting of SuperSpeed endpoints in /proc/bus/usb/devices commit 2868a2b1ba8f9c7f6c4170519ebb6c62934df70e upstream. Isochronous and interrupt SuperSpeed endpoints use the same mechanisms for decoding bInterval values as HighSpeed ones so adjust the code accordingly. Also bandwidth reservation for SuperSpeed matches highspeed, not low/full speed. Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devices.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index a3d2e2399655b..96fdfb815f895 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -221,7 +221,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end, break; case USB_ENDPOINT_XFER_INT: type = "Int."; - if (speed == USB_SPEED_HIGH) + if (speed == USB_SPEED_HIGH || speed == USB_SPEED_SUPER) interval = 1 << (desc->bInterval - 1); else interval = desc->bInterval; @@ -229,7 +229,8 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end, default: /* "can't happen" */ return start; } - interval *= (speed == USB_SPEED_HIGH) ? 125 : 1000; + interval *= (speed == USB_SPEED_HIGH || + speed == USB_SPEED_SUPER) ? 125 : 1000; if (interval % 1000) unit = 'u'; else { @@ -542,8 +543,9 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, if (level == 0) { int max; - /* high speed reserves 80%, full/low reserves 90% */ - if (usbdev->speed == USB_SPEED_HIGH) + /* super/high speed reserves 80%, full/low reserves 90% */ + if (usbdev->speed == USB_SPEED_HIGH || + usbdev->speed == USB_SPEED_SUPER) max = 800; else max = FRAME_TIME_MAX_USECS_ALLOC; From 879d2ccad7347a306adaf8bf90c4146d91a4ea7e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 20 Mar 2011 02:15:17 -0700 Subject: [PATCH 1332/2556] USB: xhci - fix unsafe macro definitions commit 5a6c2f3ff039154872ce597952f8b8900ea0d732 upstream. Macro arguments used in expressions need to be enclosed in parenthesis to avoid unpleasant surprises. This should be queued for kernels back to 2.6.31 Signed-off-by: Dmitry Torokhov Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 62bc1bc2bd72e..19040c54be0dc 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -232,7 +232,7 @@ struct xhci_op_regs { * notification type that matches a bit set in this bit field. */ #define DEV_NOTE_MASK (0xffff) -#define ENABLE_DEV_NOTE(x) (1 << x) +#define ENABLE_DEV_NOTE(x) (1 << (x)) /* Most of the device notification types should only be used for debug. * SW does need to pay attention to function wake notifications. */ @@ -601,11 +601,11 @@ struct xhci_ep_ctx { #define EP_STATE_STOPPED 3 #define EP_STATE_ERROR 4 /* Mult - Max number of burtst within an interval, in EP companion desc. */ -#define EP_MULT(p) ((p & 0x3) << 8) +#define EP_MULT(p) (((p) & 0x3) << 8) /* bits 10:14 are Max Primary Streams */ /* bit 15 is Linear Stream Array */ /* Interval - period between requests to an endpoint - 125u increments. */ -#define EP_INTERVAL(p) ((p & 0xff) << 16) +#define EP_INTERVAL(p) (((p) & 0xff) << 16) #define EP_INTERVAL_TO_UFRAMES(p) (1 << (((p) >> 16) & 0xff)) #define EP_MAXPSTREAMS_MASK (0x1f << 10) #define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK) From b9d75083e022102b4f24dd0f8ebaa14ab4568bbf Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 23 Mar 2011 22:41:23 -0700 Subject: [PATCH 1333/2556] USB: xhci - fix math in xhci_get_endpoint_interval() commit dfa49c4ad120a784ef1ff0717168aa79f55a483a upstream. When parsing exponent-expressed intervals we subtract 1 from the value and then expect it to match with original + 1, which is highly unlikely, and we end with frequent spew: usb 3-4: ep 0x83 - rounding interval to 512 microframes Also, parsing interval for fullspeed isochronous endpoints was incorrect - according to USB spec they use exponent-based intervals (but xHCI spec claims frame-based intervals). I trust USB spec more, especially since USB core agrees with it. This should be queued for stable kernels back to 2.6.31. Reviewed-by: Micah Elizabeth Scott Signed-off-by: Dmitry Torokhov Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 90 +++++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 28 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index a9534396e85ba..0de3100053271 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -920,6 +920,47 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud return 0; } +/* + * Convert interval expressed as 2^(bInterval - 1) == interval into + * straight exponent value 2^n == interval. + * + */ +static unsigned int xhci_parse_exponent_interval(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + unsigned int interval; + + interval = clamp_val(ep->desc.bInterval, 1, 16) - 1; + if (interval != ep->desc.bInterval - 1) + dev_warn(&udev->dev, + "ep %#x - rounding interval to %d microframes\n", + ep->desc.bEndpointAddress, + 1 << interval); + + return interval; +} + +/* + * Convert bInterval expressed in frames (in 1-255 range) to exponent of + * microframes, rounded down to nearest power of 2. + */ +static unsigned int xhci_parse_frame_interval(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + unsigned int interval; + + interval = fls(8 * ep->desc.bInterval) - 1; + interval = clamp_val(interval, 3, 10); + if ((1 << interval) != 8 * ep->desc.bInterval) + dev_warn(&udev->dev, + "ep %#x - rounding interval to %d microframes, ep desc says %d microframes\n", + ep->desc.bEndpointAddress, + 1 << interval, + 8 * ep->desc.bInterval); + + return interval; +} + /* Return the polling or NAK interval. * * The polling interval is expressed in "microframes". If xHCI's Interval field @@ -937,45 +978,38 @@ static inline unsigned int xhci_get_endpoint_interval(struct usb_device *udev, case USB_SPEED_HIGH: /* Max NAK rate */ if (usb_endpoint_xfer_control(&ep->desc) || - usb_endpoint_xfer_bulk(&ep->desc)) + usb_endpoint_xfer_bulk(&ep->desc)) { interval = ep->desc.bInterval; + break; + } /* Fall through - SS and HS isoc/int have same decoding */ + case USB_SPEED_SUPER: if (usb_endpoint_xfer_int(&ep->desc) || - usb_endpoint_xfer_isoc(&ep->desc)) { - if (ep->desc.bInterval == 0) - interval = 0; - else - interval = ep->desc.bInterval - 1; - if (interval > 15) - interval = 15; - if (interval != ep->desc.bInterval + 1) - dev_warn(&udev->dev, "ep %#x - rounding interval to %d microframes\n", - ep->desc.bEndpointAddress, 1 << interval); + usb_endpoint_xfer_isoc(&ep->desc)) { + interval = xhci_parse_exponent_interval(udev, ep); } break; - /* Convert bInterval (in 1-255 frames) to microframes and round down to - * nearest power of 2. - */ + case USB_SPEED_FULL: + if (usb_endpoint_xfer_int(&ep->desc)) { + interval = xhci_parse_exponent_interval(udev, ep); + break; + } + /* + * Fall through for isochronous endpoint interval decoding + * since it uses the same rules as low speed interrupt + * endpoints. + */ + case USB_SPEED_LOW: if (usb_endpoint_xfer_int(&ep->desc) || - usb_endpoint_xfer_isoc(&ep->desc)) { - interval = fls(8*ep->desc.bInterval) - 1; - if (interval > 10) - interval = 10; - if (interval < 3) - interval = 3; - if ((1 << interval) != 8*ep->desc.bInterval) - dev_warn(&udev->dev, - "ep %#x - rounding interval" - " to %d microframes, " - "ep desc says %d microframes\n", - ep->desc.bEndpointAddress, - 1 << interval, - 8*ep->desc.bInterval); + usb_endpoint_xfer_isoc(&ep->desc)) { + + interval = xhci_parse_frame_interval(udev, ep); } break; + default: BUG(); } From 994133a056515517a16bfac485e9741188a71912 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 12 Apr 2011 23:06:28 -0700 Subject: [PATCH 1334/2556] USB: xhci - also free streams when resetting devices commit 2dea75d96ade3c7cd2bfe73f99c7b3291dc3d03a upstream. Currently, when resetting a device, xHCI driver disables all but one endpoints and frees their rings, but leaves alone any streams that might have been allocated. Later, when users try to free allocated streams, we oops in xhci_setup_no_streams_ep_input_ctx() because ep->ring is NULL. Let's free not only rings but also stream data as well, so that calling free_streams() on a device that was reset will be safe. This should be queued for stable trees back to 2.6.35. Reviewed-by: Micah Elizabeth Scott Signed-off-by: Dmitry Torokhov Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 2083fc2179b2a..150349d1fdac5 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -2335,10 +2335,18 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) /* Everything but endpoint 0 is disabled, so free or cache the rings. */ last_freed_endpoint = 1; for (i = 1; i < 31; ++i) { - if (!virt_dev->eps[i].ring) - continue; - xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i); - last_freed_endpoint = i; + struct xhci_virt_ep *ep = &virt_dev->eps[i]; + + if (ep->ep_state & EP_HAS_STREAMS) { + xhci_free_stream_info(xhci, ep->stream_info); + ep->stream_info = NULL; + ep->ep_state &= ~EP_HAS_STREAMS; + } + + if (ep->ring) { + xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i); + last_freed_endpoint = i; + } } xhci_dbg(xhci, "Output context after successful reset device cmd:\n"); xhci_dbg_ctx(xhci, virt_dev->out_ctx, last_freed_endpoint); From 8e458f117e43e960bfeb1f8703d643adb8de31bb Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 28 Sep 2010 00:57:32 -0400 Subject: [PATCH 1335/2556] USB: Fix unplug of device with active streams commit b214f191d95ba4b5a35aebd69cd129cf7e3b1884 upstream. If I unplug a device while the UAS driver is loaded, I get an oops in usb_free_streams(). This is because usb_unbind_interface() calls usb_disable_interface() which calls usb_disable_endpoint() which sets ep_out and ep_in to NULL. Then the UAS driver calls usb_pipe_endpoint() which returns a NULL pointer and passes an array of NULL pointers to usb_free_streams(). I think the correct fix for this is to check for the NULL pointer in usb_free_streams() rather than making the driver check for this situation. My original patch for this checked for dev->state == USB_STATE_NOTATTACHED, but the call to usb_disable_interface() is conditional, so not all drivers would want this check. Note from Sarah Sharp: This patch does avoid a potential dereference, but the real fix (which will be implemented later) is to set the .soft_unbind flag in the usb_driver structure for the UAS driver, and all drivers that allocate streams. The driver should free any streams when it is unbound from the interface. This avoids leaking stream rings in the xHCI driver when usb_disable_interface() is called. This should be queued for stable trees back to 2.6.35. Signed-off-by: Matthew Wilcox Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index c34a935c7a379..fd95b9e94bb10 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1885,7 +1885,7 @@ void usb_free_streams(struct usb_interface *interface, /* Streams only apply to bulk endpoints. */ for (i = 0; i < num_eps; i++) - if (!usb_endpoint_xfer_bulk(&eps[i]->desc)) + if (!eps[i] || !usb_endpoint_xfer_bulk(&eps[i]->desc)) return; hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags); From dedac5a7b2df0bade917238934c6c7c645280137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Thu, 7 Apr 2011 16:17:47 +0200 Subject: [PATCH 1336/2556] radeon: Fix KMS CP writeback on big endian machines. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit dc66b325f161bb651493c7d96ad44876b629cf6a upstream. This is necessary even with PCI(e) GART, and it makes writeback work even with AGP on my PowerBook. Might still be unreliable with older revisions of UniNorth and other AGP bridges though. Signed-off-by: Michel Dänzer Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_fence.c | 2 +- drivers/gpu/drm/radeon/radeon_ring.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index 171b0b2e3a644..2b0ee623cd319 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -80,7 +80,7 @@ static bool radeon_fence_poll_locked(struct radeon_device *rdev) scratch_index = R600_WB_EVENT_OFFSET + rdev->fence_drv.scratch_reg - rdev->scratch.reg_base; else scratch_index = RADEON_WB_SCRATCH_OFFSET + rdev->fence_drv.scratch_reg - rdev->scratch.reg_base; - seq = rdev->wb.wb[scratch_index/4]; + seq = le32_to_cpu(rdev->wb.wb[scratch_index/4]); } else seq = RREG32(rdev->fence_drv.scratch_reg); if (seq != rdev->fence_drv.last_seq) { diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index 06e79822a2bff..d6edfeb6f3411 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -248,7 +248,7 @@ void radeon_ib_pool_fini(struct radeon_device *rdev) void radeon_ring_free_size(struct radeon_device *rdev) { if (rdev->wb.enabled) - rdev->cp.rptr = rdev->wb.wb[RADEON_WB_CP_RPTR_OFFSET/4]; + rdev->cp.rptr = le32_to_cpu(rdev->wb.wb[RADEON_WB_CP_RPTR_OFFSET/4]); else { if (rdev->family >= CHIP_R600) rdev->cp.rptr = RREG32(R600_CP_RB_RPTR); From 2951119b25479349af6ebf1bfa3111bee3c29896 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 16 Mar 2011 15:36:29 -0300 Subject: [PATCH 1337/2556] Bluetooth: Fix HCI_RESET command synchronization commit f630cf0d5434e3923e1b8226ffa2753ead6b0ce5 upstream. We can't send new commands before a cmd_complete for the HCI_RESET command shows up. Reported-by: Mikko Vinni Reported-by: Justin P. Mattock Reported-by: Ed Tomlinson Signed-off-by: Gustavo F. Padovan Tested-by: Justin P. Mattock Tested-by: Mikko Vinni Tested-by: Ed Tomlinson --- include/net/bluetooth/hci.h | 2 ++ net/bluetooth/hci_core.c | 5 ++++- net/bluetooth/hci_event.c | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 29a7a8ca04389..84d3b23f45320 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -76,6 +76,8 @@ enum { HCI_INQUIRY, HCI_RAW, + + HCI_RESET, }; /* HCI ioctl defines */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9c4541bc488ab..7d0a3043cbe13 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -184,6 +184,7 @@ static void hci_reset_req(struct hci_dev *hdev, unsigned long opt) BT_DBG("%s %ld", hdev->name, opt); /* Reset device */ + set_bit(HCI_RESET, &hdev->flags); hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL); } @@ -210,8 +211,10 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt) /* Mandatory initialization */ /* Reset */ - if (!test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks)) + if (!test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks)) { + set_bit(HCI_RESET, &hdev->flags); hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL); + } /* Read Local Supported Features */ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a290854fdaa6a..a63bcf039ee1b 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -183,6 +183,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%x", hdev->name, status); + clear_bit(HCI_RESET, &hdev->flags); + hci_req_complete(hdev, HCI_OP_RESET, status); } @@ -1464,7 +1466,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) break; } - if (ev->ncmd) { + if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); if (!skb_queue_empty(&hdev->cmd_q)) tasklet_schedule(&hdev->cmd_task); From 2226d5bb341cae0c736aad18ec24e1dfb625272d Mon Sep 17 00:00:00 2001 From: Kyle McMartin Date: Mon, 24 Jan 2011 11:13:04 -0500 Subject: [PATCH 1338/2556] perf tool: Fix gcc 4.6.0 issues commit fb7d0b3cefb80a105f7fd26bbc62e0cbf9192822 upstream. GCC 4.6.0 in Fedora rawhide turned up some compile errors in tools/perf due to the -Werror=unused-but-set-variable flag. I've gone through and annotated some of the assignments that had side effects (ie: return value from a function) with the __used annotation, and in some cases, just removed unused code. In a few cases, we were assigning something useful, but not using it in later parts of the function. kyle@dreadnought:~/src% gcc --version gcc (GCC) 4.6.0 20110122 (Red Hat 4.6.0-0.3) Cc: Ingo Molnar LKML-Reference: <20110124161304.GK27353@bombadil.infradead.org> Signed-off-by: Kyle McMartin [ committer note: Fixed up the annotation fixes, as that code moved recently ] Signed-off-by: Arnaldo Carvalho de Melo [Backported to 2.6.38.2 by deleting unused but set variables] Signed-off-by: Thomas Meyer Signed-off-by: Greg Kroah-Hartman --- tools/perf/bench/sched-pipe.c | 2 +- tools/perf/builtin-sched.c | 12 +++--------- tools/perf/builtin-top.c | 5 +---- tools/perf/util/header.c | 2 +- tools/perf/util/hist.c | 3 --- .../perf/util/scripting-engines/trace-event-python.c | 3 +-- tools/perf/util/symbol.c | 4 ++-- tools/perf/util/trace-event-parse.c | 2 +- tools/perf/util/ui/browsers/map.c | 2 +- 9 files changed, 11 insertions(+), 24 deletions(-) diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c index d9ab3ce446acf..0c7454f8b8a98 100644 --- a/tools/perf/bench/sched-pipe.c +++ b/tools/perf/bench/sched-pipe.c @@ -55,7 +55,7 @@ int bench_sched_pipe(int argc, const char **argv, * discarding returned value of read(), write() * causes error in building environment for perf */ - int ret, wait_stat; + int __used ret, wait_stat; pid_t pid, retpid; argc = parse_options(argc, argv, options, diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 29acb894e0351..ae7225c53c29d 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -369,11 +369,6 @@ static void process_sched_event(struct task_desc *this_task __used, struct sched_atom *atom) { int ret = 0; - u64 now; - long long delta; - - now = get_nsecs(); - delta = start_time + atom->timestamp - now; switch (atom->type) { case SCHED_EVENT_RUN: @@ -562,7 +557,7 @@ static void wait_for_tasks(void) static void run_one_test(void) { - u64 T0, T1, delta, avg_delta, fluct, std_dev; + u64 T0, T1, delta, avg_delta, fluct; T0 = get_nsecs(); wait_for_tasks(); @@ -578,7 +573,6 @@ static void run_one_test(void) else fluct = delta - avg_delta; sum_fluct += fluct; - std_dev = sum_fluct / nr_runs / sqrt(nr_runs); if (!run_avg) run_avg = delta; run_avg = (run_avg*9 + delta)/10; @@ -799,7 +793,7 @@ replay_switch_event(struct trace_switch_event *switch_event, u64 timestamp, struct thread *thread __used) { - struct task_desc *prev, *next; + struct task_desc *prev, __used *next; u64 timestamp0; s64 delta; @@ -1404,7 +1398,7 @@ map_switch_event(struct trace_switch_event *switch_event, u64 timestamp, struct thread *thread __used) { - struct thread *sched_out, *sched_in; + struct thread *sched_out __used, *sched_in; int new_shortname; u64 timestamp0; s64 delta; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 5a29d9cd94862..b0f6925b127d6 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -183,7 +183,6 @@ static int parse_source(struct sym_entry *syme) FILE *file; char command[PATH_MAX*2]; const char *path; - u64 len; if (!syme) return -1; @@ -212,8 +211,6 @@ static int parse_source(struct sym_entry *syme) } path = map->dso->long_name; - len = sym->end - sym->start; - sprintf(command, "objdump --start-address=%#0*" PRIx64 " --stop-address=%#0*" PRIx64 " -dS %s", BITS_PER_LONG / 4, map__rip_2objdump(map, sym->start), @@ -1296,7 +1293,7 @@ static int __cmd_top(void) { pthread_t thread; struct perf_evsel *counter; - int i, ret; + int i, ret __used; /* * FIXME: perf_session__new should allow passing a O_MMAP, so that all this * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 0866bcdb5e8e7..9721e2fa9ece1 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1144,7 +1144,7 @@ int event__synthesize_tracing_data(int fd, struct list_head *pattrs, { event_t ev; ssize_t size = 0, aligned_size = 0, padding; - int err = 0; + int err __used = 0; memset(&ev, 0, sizeof(ev)); diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index df51560f16f7e..5214b703250e0 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1095,7 +1095,6 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head, char command[PATH_MAX * 2]; FILE *file; int err = 0; - u64 len; char symfs_filename[PATH_MAX]; if (filename) { @@ -1140,8 +1139,6 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head, filename, sym->name, map->unmap_ip(map, sym->start), map->unmap_ip(map, sym->end)); - len = sym->end - sym->start; - pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso->long_name, sym, sym->name); diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index c6d99334bdfa8..2040b85385273 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -248,8 +248,7 @@ static void python_process_event(int cpu, void *data, context = PyCObject_FromVoidPtr(scripting_context, NULL); PyTuple_SetItem(t, n++, PyString_FromString(handler_name)); - PyTuple_SetItem(t, n++, - PyCObject_FromVoidPtr(scripting_context, NULL)); + PyTuple_SetItem(t, n++, context); if (handler) { PyTuple_SetItem(t, n++, PyInt_FromLong(cpu)); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index b1bf490aff880..ba6d48949092f 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1525,8 +1525,8 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) symbol_conf.symfs, self->long_name); break; case DSO__ORIG_GUEST_KMODULE: - if (map->groups && map->groups->machine) - root_dir = map->groups->machine->root_dir; + if (map->groups && machine) + root_dir = machine->root_dir; else root_dir = ""; snprintf(name, size, "%s%s%s", symbol_conf.symfs, diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 73a02223c6292..d8e622dd738aa 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -153,7 +153,7 @@ void parse_proc_kallsyms(char *file, unsigned int size __unused) char *next = NULL; char *addr_str; char ch; - int ret; + int ret __used; int i; line = strtok_r(file, "\n", &next); diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c index e5158369106ee..8462bffe20bc8 100644 --- a/tools/perf/util/ui/browsers/map.c +++ b/tools/perf/util/ui/browsers/map.c @@ -41,7 +41,7 @@ static int ui_entry__read(const char *title, char *bf, size_t size, int width) out_free_form: newtPopWindow(); newtFormDestroy(form); - return 0; + return err; } struct map_browser { From 6935b2f7905bc973676cec541c1ebbbd3bd5692f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 12 Apr 2011 13:39:14 -0700 Subject: [PATCH 1339/2556] bridge: reset IPCB in br_parse_ip_options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f8e9881c2aef1e982e5abc25c046820cd0b7cf64 upstream. Commit 462fb2af9788a82 (bridge : Sanitize skb before it enters the IP stack), missed one IPCB init before calling ip_options_compile() Thanks to Scot Doyle for his tests and bug reports. Reported-by: Scot Doyle Signed-off-by: Eric Dumazet Cc: Hiroaki SHIMODA Acked-by: Bandan Das Acked-by: Stephen Hemminger Cc: Jan Lübbe Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/bridge/br_netfilter.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 49d50ea5dbbc6..333bcaa1377c6 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -249,11 +249,9 @@ static int br_parse_ip_options(struct sk_buff *skb) goto drop; } - /* Zero out the CB buffer if no options present */ - if (iph->ihl == 5) { - memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); + memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); + if (iph->ihl == 5) return 0; - } opt->optlen = iph->ihl*4 - sizeof(struct iphdr); if (ip_options_compile(dev_net(dev), opt, skb)) From 50038a29ee9d62aba6e66109d0b9c235bc0e31e2 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 14 Apr 2011 05:55:37 +0000 Subject: [PATCH 1340/2556] ip: ip_options_compile() resilient to NULL skb route commit c65353daf137dd41f3ede3baf62d561fca076228 upstream. Scot Doyle demonstrated ip_options_compile() could be called with an skb without an attached route, using a setup involving a bridge, netfilter, and forged IP packets. Let's make ip_options_compile() and ip_options_rcv_srr() a bit more robust, instead of changing bridge/netfilter code. With help from Hiroaki SHIMODA. Reported-by: Scot Doyle Tested-by: Scot Doyle Signed-off-by: Eric Dumazet Cc: Stephen Hemminger Acked-by: Hiroaki SHIMODA Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ip_options.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index 1906fa35860c8..b0413e300e561 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -329,7 +329,7 @@ int ip_options_compile(struct net *net, pp_ptr = optptr + 2; goto error; } - if (skb) { + if (rt) { memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4); opt->is_changed = 1; } @@ -371,7 +371,7 @@ int ip_options_compile(struct net *net, goto error; } opt->ts = optptr - iph; - if (skb) { + if (rt) { memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4); timeptr = (__be32*)&optptr[optptr[2]+3]; } @@ -603,7 +603,7 @@ int ip_options_rcv_srr(struct sk_buff *skb) unsigned long orefdst; int err; - if (!opt->srr) + if (!opt->srr || !rt) return 0; if (skb->pkt_type != PACKET_HOST) From 8fd62c82872a5a721c9fb0071ca0f7a49c1732e4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 21 Apr 2011 14:34:46 -0700 Subject: [PATCH 1341/2556] Linux 2.6.38.4 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e47e39e2bc2c1..e71224305abf8 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 38 -EXTRAVERSION = .3 +EXTRAVERSION = .4 NAME = Flesh-Eating Bats with Fangs # *DOCUMENTATION* From 4cc0cfd01e8a4fde3045f084021fd324575c877d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 13 Apr 2011 08:07:28 -0700 Subject: [PATCH 1342/2556] vm: fix vm_pgoff wrap in stack expansion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a626ca6a656450e9f4df91d0dda238fff23285f4 upstream. Commit 982134ba6261 ("mm: avoid wrapping vm_pgoff in mremap()") fixed the case of a expanding mapping causing vm_pgoff wrapping when you used mremap. But there was another case where we expand mappings hiding in plain sight: the automatic stack expansion. This fixes that case too. This one also found by Robert Święcki, using his nasty system call fuzzer tool. Good job. Reported-and-tested-by: Robert Święcki Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/mmap.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/mm/mmap.c b/mm/mmap.c index 2ec8eb5a9cdd0..8c05e5b43b69c 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1814,11 +1814,14 @@ static int expand_downwards(struct vm_area_struct *vma, size = vma->vm_end - address; grow = (vma->vm_start - address) >> PAGE_SHIFT; - error = acct_stack_growth(vma, size, grow); - if (!error) { - vma->vm_start = address; - vma->vm_pgoff -= grow; - perf_event_mmap(vma); + error = -ENOMEM; + if (grow <= vma->vm_pgoff) { + error = acct_stack_growth(vma, size, grow); + if (!error) { + vma->vm_start = address; + vma->vm_pgoff -= grow; + perf_event_mmap(vma); + } } } vma_unlock_anon_vma(vma); From 4464eaa08e9fef837556dbee33766fb43c3718e8 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 7 Apr 2011 10:31:25 -0400 Subject: [PATCH 1343/2556] drm/radeon/kms: pll tweaks for rv6xx commit 9bb09fa1b5b07459279301ac6220d575f307597b upstream. Prefer minm over maxp. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=35994 Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/atombios_crtc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index e967cc869b3b2..08612572c5581 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -531,6 +531,12 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV; else pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV; + + if ((rdev->family == CHIP_R600) || + (rdev->family == CHIP_RV610) || + (rdev->family == CHIP_RV630) || + (rdev->family == CHIP_RV670)) + pll->flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP; } else { pll->flags |= RADEON_PLL_LEGACY; From 2a3467164465a4d4d49866341a30f5dde9bb74a8 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 12 Apr 2011 13:33:27 -0400 Subject: [PATCH 1344/2556] drm/radeon/kms: fix suspend on rv530 asics commit 71e16bfbd2b1c63d4d97cc5059694c9346aee340 upstream. Apparently only rv515 asics need the workaround added in f24d86f1a49505cdea56728b853a5d0a3f8e3d11 (drm/radeon/kms: fix resume regression for some r5xx laptops). Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=34709 Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/atom.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c index 258fa5e7a2d9a..d71d375149f85 100644 --- a/drivers/gpu/drm/radeon/atom.c +++ b/drivers/gpu/drm/radeon/atom.c @@ -32,6 +32,7 @@ #include "atom.h" #include "atom-names.h" #include "atom-bits.h" +#include "radeon.h" #define ATOM_COND_ABOVE 0 #define ATOM_COND_ABOVEOREQUAL 1 @@ -101,7 +102,9 @@ static void debug_print_spaces(int n) static uint32_t atom_iio_execute(struct atom_context *ctx, int base, uint32_t index, uint32_t data) { + struct radeon_device *rdev = ctx->card->dev->dev_private; uint32_t temp = 0xCDCDCDCD; + while (1) switch (CU8(base)) { case ATOM_IIO_NOP: @@ -112,7 +115,8 @@ static uint32_t atom_iio_execute(struct atom_context *ctx, int base, base += 3; break; case ATOM_IIO_WRITE: - (void)ctx->card->ioreg_read(ctx->card, CU16(base + 1)); + if (rdev->family == CHIP_RV515) + (void)ctx->card->ioreg_read(ctx->card, CU16(base + 1)); ctx->card->ioreg_write(ctx->card, CU16(base + 1), temp); base += 3; break; From 5e4ee2297b57d38880a7a7d0f6574dd2d7581c7a Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 14 Mar 2011 13:48:08 -0400 Subject: [PATCH 1345/2556] cifs: always do is_path_accessible check in cifs_mount commit 70945643722ffeac779d2529a348f99567fa5c33 upstream. Currently, we skip doing the is_path_accessible check in cifs_mount if there is no prefixpath. I have a report of at least one server however that allows a TREE_CONNECT to a share that has a DFS referral at its root. The reporter in this case was using a UNC that had no prefixpath, so the is_path_accessible check was not triggered and the box later hit a BUG() because we were chasing a DFS referral on the root dentry for the mount. This patch fixes this by removing the check for a zero-length prefixpath. That should make the is_path_accessible check be done in this situation and should allow the client to chase the DFS referral at mount time instead. Reported-and-Tested-by: Yogesh Sharma Signed-off-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/connect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 8d6c17ab593da..d3d836d9c5a29 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2826,7 +2826,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, remote_path_check: /* check if a whole path (including prepath) is not remote */ - if (!rc && cifs_sb->prepathlen && tcon) { + if (!rc && tcon) { /* build_path_to_root works only when we have a valid tcon */ full_path = cifs_build_path_to_root(cifs_sb, tcon); if (full_path == NULL) { From 5a3589697222ffa1ffedb3f5f1568cd8037e4973 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 5 Apr 2011 16:23:47 -0700 Subject: [PATCH 1346/2556] cifs: check for private_data before trying to put it commit 7797069305d13252fd66cf722aa8f2cbeb3c95cd upstream. cifs_close doesn't check that the filp->private_data is non-NULL before trying to put it. That can cause an oops in certain error conditions that can occur on open or lookup before the private_data is set. Reported-by: Ben Greear Signed-off-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/file.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index e964b1cd5dd09..7b2e8ec2709b3 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -575,8 +575,10 @@ static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush) int cifs_close(struct inode *inode, struct file *file) { - cifsFileInfo_put(file->private_data); - file->private_data = NULL; + if (file->private_data != NULL) { + cifsFileInfo_put(file->private_data); + file->private_data = NULL; + } /* return code from the ->release op is always ignored */ return 0; From a07f1bfabdef56710dd9c9bdc101beec18eaa04a Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 25 Mar 2011 16:25:57 -0400 Subject: [PATCH 1347/2556] cifs: set ra_pages in backing_dev_info commit 2b6c26a0a62cc0bab0ad487533d5581d7c293fef upstream. Commit 522440ed made cifs set backing_dev_info on the mapping attached to new inodes. This change caused a fairly significant read performance regression, as cifs started doing page-sized reads exclusively. By virtue of the fact that they're allocated as part of cifs_sb_info by kzalloc, the ra_pages on cifs BDIs get set to 0, which prevents any readahead. This forces the normal read codepaths to use readpage instead of readpages causing a four-fold increase in the number of read calls with the default rsize. Fix it by setting ra_pages in the BDI to the same value as that in the default_backing_dev_info. Fixes https://bugzilla.kernel.org/show_bug.cgi?id=31662 Reported-and-Tested-by: Till Signed-off-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/cifsfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index f2970136d17d0..c0def4f5602a7 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -127,6 +127,7 @@ cifs_read_super(struct super_block *sb, void *data, kfree(cifs_sb); return rc; } + cifs_sb->bdi.ra_pages = default_backing_dev_info.ra_pages; #ifdef CONFIG_CIFS_DFS_UPCALL /* copy mount params to sb for use in submounts */ From c45b1f155fe706ed626db1987f139f9915a495d3 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sat, 2 Apr 2011 07:34:30 -0400 Subject: [PATCH 1348/2556] cifs: wrap received signature check in srv_mutex commit 157c249114508aa71daa308a426e15d81a4eed00 upstream. While testing my patchset to fix asynchronous writes, I hit a bunch of signature problems when testing with signing on. The problem seems to be that signature checks on receive can be running at the same time as a process that is sending, or even that multiple receives can be checking signatures at the same time, clobbering the same data structures. While we're at it, clean up the comments over cifs_calculate_signature and add a note that the srv_mutex should be held when calling this function. This patch seems to fix the problems for me, but I'm not clear on whether it's the best approach. If it is, then this should probably go to stable too. Cc: Shirish Pargaonkar Signed-off-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/cifsencrypt.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index a51585f9852b4..96b9a34db48b5 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -30,12 +30,13 @@ #include #include -/* Calculate and return the CIFS signature based on the mac key and SMB PDU */ -/* the 16 byte signature must be allocated by the caller */ -/* Note we only use the 1st eight bytes */ -/* Note that the smb header signature field on input contains the - sequence number before this function is called */ - +/* + * Calculate and return the CIFS signature based on the mac key and SMB PDU. + * The 16 byte signature must be allocated by the caller. Note we only use the + * 1st eight bytes and that the smb header signature field on input contains + * the sequence number before this function is called. Also, this function + * should be called with the server->srv_mutex held. + */ static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, char *signature) { @@ -209,8 +210,10 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu, cpu_to_le32(expected_sequence_number); cifs_pdu->Signature.Sequence.Reserved = 0; + mutex_lock(&server->srv_mutex); rc = cifs_calculate_signature(cifs_pdu, server, what_we_think_sig_should_be); + mutex_unlock(&server->srv_mutex); if (rc) return rc; From e3016b2acd0c790d5a4ebbc7742741057ec5b206 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 4 Feb 2011 09:23:33 -0300 Subject: [PATCH 1349/2556] video: sn9c102: world-wirtable sysfs files commit 14ddc3188d50855ae2a419a6aced995e2834e5d4 upstream. Don't allow everybody to change video settings. Signed-off-by: Vasiliy Kulikov Acked-by: Mauro Carvalho Chehab Acked-by: Luca Risolia Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/video/sn9c102/sn9c102_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index 84984f64b234a..ce56a1cdbf0a6 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -1430,9 +1430,9 @@ static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, sn9c102_show_i2c_reg, sn9c102_store_i2c_reg); static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, sn9c102_show_i2c_val, sn9c102_store_i2c_val); -static DEVICE_ATTR(green, S_IWUGO, NULL, sn9c102_store_green); -static DEVICE_ATTR(blue, S_IWUGO, NULL, sn9c102_store_blue); -static DEVICE_ATTR(red, S_IWUGO, NULL, sn9c102_store_red); +static DEVICE_ATTR(green, S_IWUSR, NULL, sn9c102_store_green); +static DEVICE_ATTR(blue, S_IWUSR, NULL, sn9c102_store_blue); +static DEVICE_ATTR(red, S_IWUSR, NULL, sn9c102_store_red); static DEVICE_ATTR(frame_header, S_IRUGO, sn9c102_show_frame_header, NULL); From 04fca00b6f5bea42c47f7ad495c2a849aedf4dac Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 4 Feb 2011 15:24:19 +0300 Subject: [PATCH 1350/2556] UBIFS: restrict world-writable debugfs files commit 8c559d30b4e59cf6994215ada1fe744928f494bf upstream. Don't allow everybody to dump sensitive information about filesystems. Signed-off-by: Vasiliy Kulikov Signed-off-by: Artem Bityutskiy Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/debug.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/ubifs/debug.c b/fs/ubifs/debug.c index 5b9e9855b22c6..b67ed36f16a12 100644 --- a/fs/ubifs/debug.c +++ b/fs/ubifs/debug.c @@ -2844,19 +2844,19 @@ int dbg_debugfs_init_fs(struct ubifs_info *c) } fname = "dump_lprops"; - dent = debugfs_create_file(fname, S_IWUGO, d->dfs_dir, c, &dfs_fops); + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c, &dfs_fops); if (IS_ERR(dent)) goto out_remove; d->dfs_dump_lprops = dent; fname = "dump_budg"; - dent = debugfs_create_file(fname, S_IWUGO, d->dfs_dir, c, &dfs_fops); + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c, &dfs_fops); if (IS_ERR(dent)) goto out_remove; d->dfs_dump_budg = dent; fname = "dump_tnc"; - dent = debugfs_create_file(fname, S_IWUGO, d->dfs_dir, c, &dfs_fops); + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, c, &dfs_fops); if (IS_ERR(dent)) goto out_remove; d->dfs_dump_tnc = dent; From 944dc8302fe84e96eabb0694040bd3cb920ff229 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 28 Mar 2011 12:05:31 +0200 Subject: [PATCH 1351/2556] ALSA: hda - Fix pin-config of Gigabyte mobo commit c6b358748e19ce7e230b0926ac42696bc485a562 upstream. Use pin-fix instead of the static quirk for Gigabyte mobos 1458:a002. Bugzilla: https://bugzilla.novell.com/show_bug.cgi?id=677256 Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e164a4bdf48f5..da7cdca4776d5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9932,7 +9932,6 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = { SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL), SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG), @@ -10769,6 +10768,7 @@ enum { PINFIX_LENOVO_Y530, PINFIX_PB_M5210, PINFIX_ACER_ASPIRE_7736, + PINFIX_GIGABYTE_880GM, }; static const struct alc_fixup alc882_fixups[] = { @@ -10800,6 +10800,13 @@ static const struct alc_fixup alc882_fixups[] = { .type = ALC_FIXUP_SKU, .v.sku = ALC_FIXUP_SKU_IGNORE, }, + [PINFIX_GIGABYTE_880GM] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x1114410 }, /* set as speaker */ + { } + } + }, }; static struct snd_pci_quirk alc882_fixup_tbl[] = { @@ -10807,6 +10814,7 @@ static struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", PINFIX_LENOVO_Y530), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX), SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte", PINFIX_GIGABYTE_880GM), {} }; @@ -18851,8 +18859,6 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x1179, 0xff6e, "Toshiba NB20x", ALC662_AUTO), SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L", - ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x152d, 0x2304, "Quanta WH1", ALC663_ASUS_H13), SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x1631, 0xc10c, "PB RS65", ALC663_ASUS_M51VA), @@ -19526,6 +19532,7 @@ enum { ALC662_FIXUP_IDEAPAD, ALC272_FIXUP_MARIO, ALC662_FIXUP_CZC_P10T, + ALC662_FIXUP_GIGABYTE, }; static const struct alc_fixup alc662_fixups[] = { @@ -19554,12 +19561,20 @@ static const struct alc_fixup alc662_fixups[] = { {} } }, + [ALC662_FIXUP_GIGABYTE] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x14, 0x1114410 }, /* set as speaker */ + { } + } + }, }; static struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte", ALC662_FIXUP_GIGABYTE), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), From aeeb6d46bc32daf8495eee52db97254ad4f4befa Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sun, 13 Mar 2011 06:54:31 +0000 Subject: [PATCH 1352/2556] NET: cdc-phonet, handle empty phonet header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 468c3f924f043cad7a04f4f4d5224a2c9bc886c1 upstream. Currently, for N 5800 XM I get: cdc_phonet: probe of 1-6:1.10 failed with error -22 It's because phonet_header is empty. Extra altsetting looks like there: E 05 24 00 01 10 03 24 ab 05 24 06 0a 0b 04 24 fd .$....$..$....$. E 00 . I don't see the header used anywhere so just check if the phonet descriptor is there, not the structure itself. Signed-off-by: Jiri Slaby Cc: Rémi Denis-Courmont Cc: David S. Miller Acked-by: Rémi Denis-Courmont Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/cdc-phonet.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c index 109751bad3bb3..e7ce8afa28bda 100644 --- a/drivers/net/usb/cdc-phonet.c +++ b/drivers/net/usb/cdc-phonet.c @@ -328,13 +328,13 @@ int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) { static const char ifname[] = "usbpn%d"; const struct usb_cdc_union_desc *union_header = NULL; - const struct usb_cdc_header_desc *phonet_header = NULL; const struct usb_host_interface *data_desc; struct usb_interface *data_intf; struct usb_device *usbdev = interface_to_usbdev(intf); struct net_device *dev; struct usbpn_dev *pnd; u8 *data; + int phonet = 0; int len, err; data = intf->altsetting->extra; @@ -355,10 +355,7 @@ int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) (struct usb_cdc_union_desc *)data; break; case 0xAB: - if (phonet_header || dlen < 5) - break; - phonet_header = - (struct usb_cdc_header_desc *)data; + phonet = 1; break; } } @@ -366,7 +363,7 @@ int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) len -= dlen; } - if (!union_header || !phonet_header) + if (!union_header || !phonet) return -EINVAL; data_intf = usb_ifnum_to_if(usbdev, union_header->bSlaveInterface0); From 9e86a28ae8aa692da40114d4b3a0119439c6b6b0 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Tue, 1 Mar 2011 14:28:02 +0000 Subject: [PATCH 1353/2556] x86: Fix a bogus unwind annotation in lib/semaphore_32.S commit e938c287ea8d977e079f07464ac69923412663ce upstream. 'simple' would have required specifying current frame address and return address location manually, but that's obviously not the case (and not necessary) here. Signed-off-by: Jan Beulich LKML-Reference: <4D6D1082020000780003454C@vpn.id2.novell.com> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/lib/semaphore_32.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/lib/semaphore_32.S b/arch/x86/lib/semaphore_32.S index 648fe47417823..f35eec78a68e5 100644 --- a/arch/x86/lib/semaphore_32.S +++ b/arch/x86/lib/semaphore_32.S @@ -36,7 +36,7 @@ */ #ifdef CONFIG_SMP ENTRY(__write_lock_failed) - CFI_STARTPROC simple + CFI_STARTPROC FRAME 2: LOCK_PREFIX addl $ RW_LOCK_BIAS,(%eax) From f41a18a35afc2252d80254402a227aeb27c8c267 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 24 Feb 2011 15:33:24 -0500 Subject: [PATCH 1354/2556] tioca: Fix assignment from incompatible pointer warnings commit b4a6b3436531f6c5256e6d60d388c3c28ff1a0e9 upstream. The prototype for sn_pci_provider->{dma_map,dma_map_consistent} expects an unsigned long instead of a u64. Signed-off-by: Jeff Mahoney Signed-off-by: Tony Luck Signed-off-by: Greg Kroah-Hartman --- arch/ia64/sn/pci/tioca_provider.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/ia64/sn/pci/tioca_provider.c b/arch/ia64/sn/pci/tioca_provider.c index 4d4536e3b6f3d..9c271be9919aa 100644 --- a/arch/ia64/sn/pci/tioca_provider.c +++ b/arch/ia64/sn/pci/tioca_provider.c @@ -509,7 +509,7 @@ tioca_dma_unmap(struct pci_dev *pdev, dma_addr_t bus_addr, int dir) * use the GART mapped mode. */ static u64 -tioca_dma_map(struct pci_dev *pdev, u64 paddr, size_t byte_count, int dma_flags) +tioca_dma_map(struct pci_dev *pdev, unsigned long paddr, size_t byte_count, int dma_flags) { u64 mapaddr; From ea27c41129d5dd1d674990857bca4613e0b9f79b Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 24 Feb 2011 17:23:09 -0500 Subject: [PATCH 1355/2556] mca.c: Fix cast from integer to pointer warning commit c1d036c4d1cb00b7e8473a2ad0a78f13e13a8183 upstream. ia64_mca_cpu_init has a void *data local variable that is assigned the value from either __get_free_pages() or mca_bootmem(). The problem is that __get_free_pages returns an unsigned long and mca_bootmem, via alloc_bootmem(), returns a void *. format_mca_init_stack takes the void *, and it's also used with __pa(), but that casts it to long anyway. This results in the following build warning: arch/ia64/kernel/mca.c:1898: warning: assignment makes pointer from integer without a cast Cast the return of __get_free_pages to a void * to avoid the warning. Signed-off-by: Jeff Mahoney Signed-off-by: Tony Luck Signed-off-by: Greg Kroah-Hartman --- arch/ia64/kernel/mca.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index 1753f6a30d55e..ff14ab26c508b 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c @@ -1859,7 +1859,8 @@ ia64_mca_cpu_init(void *cpu_data) data = mca_bootmem(); first_time = 0; } else - data = __get_free_pages(GFP_KERNEL, get_order(sz)); + data = (void *)__get_free_pages(GFP_KERNEL, + get_order(sz)); if (!data) panic("Could not allocate MCA memory for cpu %d\n", cpu); From 72cc40c6b67760e48425561b7d6195be83f152cc Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 12 Apr 2011 14:15:51 -0700 Subject: [PATCH 1356/2556] vm: fix mlock() on stack guard page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 95042f9eb78a8d9a17455e2ef263f2f310ecef15 upstream. Commit 53a7706d5ed8 ("mlock: do not hold mmap_sem for extended periods of time") changed mlock() to care about the exact number of pages that __get_user_pages() had brought it. Before, it would only care about errors. And that doesn't work, because we also handled one page specially in __mlock_vma_pages_range(), namely the stack guard page. So when that case was handled, the number of pages that the function returned was off by one. In particular, it could be zero, and then the caller would end up not making any progress at all. Rather than try to fix up that off-by-one error for the mlock case specially, this just moves the logic to handle the stack guard page into__get_user_pages() itself, thus making all the counts come out right automatically. Reported-by: Robert Święcki Cc: Hugh Dickins Cc: Oleg Nesterov Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/memory.c | 26 ++++++++++++++++++-------- mm/mlock.c | 13 ------------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/mm/memory.c b/mm/memory.c index 5823698c2b71a..f17746a5acb13 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1410,6 +1410,13 @@ struct page *follow_page(struct vm_area_struct *vma, unsigned long address, return page; } +static inline int stack_guard_page(struct vm_area_struct *vma, unsigned long addr) +{ + return (vma->vm_flags & VM_GROWSDOWN) && + (vma->vm_start == addr) && + !vma_stack_continue(vma->vm_prev, addr); +} + int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, int nr_pages, unsigned int gup_flags, struct page **pages, struct vm_area_struct **vmas, @@ -1439,7 +1446,6 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, vma = find_extend_vma(mm, start); if (!vma && in_gate_area(tsk, start)) { unsigned long pg = start & PAGE_MASK; - struct vm_area_struct *gate_vma = get_gate_vma(tsk); pgd_t *pgd; pud_t *pud; pmd_t *pmd; @@ -1464,10 +1470,11 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, pte_unmap(pte); return i ? : -EFAULT; } + vma = get_gate_vma(tsk); if (pages) { struct page *page; - page = vm_normal_page(gate_vma, start, *pte); + page = vm_normal_page(vma, start, *pte); if (!page) { if (!(gup_flags & FOLL_DUMP) && is_zero_pfn(pte_pfn(*pte))) @@ -1481,12 +1488,7 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, get_page(page); } pte_unmap(pte); - if (vmas) - vmas[i] = gate_vma; - i++; - start += PAGE_SIZE; - nr_pages--; - continue; + goto next_page; } if (!vma || @@ -1500,6 +1502,13 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, continue; } + /* + * If we don't actually want the page itself, + * and it's the stack guard page, just skip it. + */ + if (!pages && stack_guard_page(vma, start)) + goto next_page; + do { struct page *page; unsigned int foll_flags = gup_flags; @@ -1569,6 +1578,7 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, flush_anon_page(vma, page, start); flush_dcache_page(page); } +next_page: if (vmas) vmas[i] = vma; i++; diff --git a/mm/mlock.c b/mm/mlock.c index c3924c7f00bea..da23be42c9067 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -135,13 +135,6 @@ void munlock_vma_page(struct page *page) } } -static inline int stack_guard_page(struct vm_area_struct *vma, unsigned long addr) -{ - return (vma->vm_flags & VM_GROWSDOWN) && - (vma->vm_start == addr) && - !vma_stack_continue(vma->vm_prev, addr); -} - /** * __mlock_vma_pages_range() - mlock a range of pages in the vma. * @vma: target vma @@ -188,12 +181,6 @@ static long __mlock_vma_pages_range(struct vm_area_struct *vma, if (vma->vm_flags & VM_LOCKED) gup_flags |= FOLL_MLOCK; - /* We don't try to access the guard page of a stack vma */ - if (stack_guard_page(vma, start)) { - addr += PAGE_SIZE; - nr_pages--; - } - return __get_user_pages(current, mm, addr, nr_pages, gup_flags, NULL, NULL, nonblocking); } From 0b74830b7153a982b83c2bcb1bff85b951c54833 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 29 Mar 2011 09:45:21 +0300 Subject: [PATCH 1357/2556] UBIFS: fix assertion warnings commit c88ac00c5af70c2a0741da14b22cdcf8507ddd92 upstream. This patch fixes UBIFS assertion warnings like: UBIFS assert failed in ubifs_leb_unmap at 135 (pid 29365) Pid: 29365, comm: integck Tainted: G I 2.6.37-ubi-2.6+ #34 Call Trace: [] ubifs_lpt_init+0x95e/0x9ee [ubifs] [] ubifs_remount_fs+0x2c7/0x762 [ubifs] [] do_remount_sb+0xb6/0x101 [] ? do_mount+0x191/0x78e [] do_mount+0x258/0x78e [] ? alloc_pages_current+0xa2/0xc5 [] sys_mount+0x83/0xbd [] system_call_fastpath+0x16/0x1b They happen when we re-mount from R/O mode to R/W mode. While re-mounting, we write to the media, but we still have the c->ro_mount flag set. The fix is very simple - just clear the flag before starting re-mounting R/W. These warnings are caused by the following commit: 2ef13294d29bcfb306e0d360f1b97f37b647b0c0 For -stable guys: this bug was introduced in 2.6.38, this is materieal for 2.6.38-stable. Signed-off-by: Artem Bityutskiy Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/super.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 6e11c2975dcf5..0f029e1732d1a 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1543,6 +1543,7 @@ static int ubifs_remount_rw(struct ubifs_info *c) mutex_lock(&c->umount_mutex); dbg_save_space_info(c); c->remounting_rw = 1; + c->ro_mount = 0; c->always_chk_crc = 1; err = check_free_space(c); @@ -1648,7 +1649,6 @@ static int ubifs_remount_rw(struct ubifs_info *c) } dbg_gen("re-mounted read-write"); - c->ro_mount = 0; c->remounting_rw = 0; c->always_chk_crc = 0; err = dbg_check_space_info(c); @@ -1656,6 +1656,7 @@ static int ubifs_remount_rw(struct ubifs_info *c) return err; out: + c->ro_mount = 1; vfree(c->orph_buf); c->orph_buf = NULL; if (c->bgt) { From d7954c8eff99d3370378150ecd21d7b6f9a68a98 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 31 Mar 2011 10:29:26 +0200 Subject: [PATCH 1358/2556] perf: Fix task context scheduling commit ab711fe08297de1485fff0a366e6db8828cafd6a upstream. Jiri reported: | | - once an event is created by sys_perf_event_open, task context | is created and it stays even if the event is closed, until the | task is finished ... thats what I see in code and I assume it's | correct | | - when the task opens event, perf_sched_events jump label is | incremented and following callbacks are started from scheduler | | __perf_event_task_sched_in | __perf_event_task_sched_out | | These callback *in/out set/unset cpuctx->task_ctx value to the | task context. | | - close is called on event on CPU 0: | - the task is scheduled on CPU 0 | - __perf_event_task_sched_in is called | - cpuctx->task_ctx is set | - perf_sched_events jump label is decremented and == 0 | - __perf_event_task_sched_out is not called | - cpuctx->task_ctx on CPU 0 stays set | | - exit is called on CPU 1: | - the task is scheduled on CPU 1 | - perf_event_exit_task is called | - task_ctx_sched_out unsets cpuctx->task_ctx on CPU 1 | - put_ctx destroys the context | | - another call of perf_rotate_context on CPU 0 will use invalid | task_ctx pointer, and eventualy panic. | Cure this the simplest possibly way by partially reverting the jump_label optimization for the sched_out case. Reported-and-tested-by: Jiri Olsa Signed-off-by: Peter Zijlstra Cc: Oleg Nesterov LKML-Reference: <1301520405.4859.213.camel@twins> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- include/linux/perf_event.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index dda5b0a3ff601..a9bd9c0af44ba 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1052,7 +1052,7 @@ void perf_event_task_sched_out(struct task_struct *task, struct task_struct *nex { perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 1, NULL, 0); - COND_STMT(&perf_task_events, __perf_event_task_sched_out(task, next)); + __perf_event_task_sched_out(task, next); } extern void perf_event_mmap(struct vm_area_struct *vma); From 85815545ce9ad6966905b9590dbfa163b0d4d73c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20L=C3=BCssing?= Date: Tue, 22 Mar 2011 11:40:32 +0000 Subject: [PATCH 1359/2556] bridge: Fix possibly wrong MLD queries' ethernet source address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit a7bff75b087e7a355838a32efe61707cfa73c194 ] The ipv6_dev_get_saddr() is currently called with an uninitialized destination address. Although in tests it usually seemed to nevertheless always fetch the right source address, there seems to be a possible race condition. Therefore this commit changes this, first setting the destination address and only after that fetching the source address. Reported-by: Jan Beulich Signed-off-by: Linus Lüssing Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/bridge/br_multicast.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 030a002ff8eee..f61eb2eff3fdd 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -445,9 +445,9 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, ip6h->payload_len = htons(8 + sizeof(*mldq)); ip6h->nexthdr = IPPROTO_HOPOPTS; ip6h->hop_limit = 1; + ipv6_addr_set(&ip6h->daddr, htonl(0xff020000), 0, 0, htonl(1)); ipv6_dev_get_saddr(dev_net(br->dev), br->dev, &ip6h->daddr, 0, &ip6h->saddr); - ipv6_addr_set(&ip6h->daddr, htonl(0xff020000), 0, 0, htonl(1)); ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); hopopt = (u8 *)(ip6h + 1); From d247716f543fe4630ef774638a2a9dc06bb80077 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 30 Mar 2011 16:57:46 -0700 Subject: [PATCH 1360/2556] fib: add rtnl locking in ip_fib_net_exit [ Upstream commit e2666f84958adb3a034b98e99699b55705117e01 ] Daniel J Blueman reported a lockdep splat in trie_firstleaf(), caused by RTNL being not locked before a call to fib_table_flush() Reported-by: Daniel J Blueman Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/fib_frontend.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 1d2cdd43a878b..8725e783d9cdb 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1041,6 +1041,7 @@ static void ip_fib_net_exit(struct net *net) fib4_rules_exit(net); #endif + rtnl_lock(); for (i = 0; i < FIB_TABLE_HASHSZ; i++) { struct fib_table *tb; struct hlist_head *head; @@ -1053,6 +1054,7 @@ static void ip_fib_net_exit(struct net *net) fib_free_table(tb); } } + rtnl_unlock(); kfree(net->ipv4.fib_table_hash); } From 3b7cdb4c44d103b52fc692e3789756e36eb9834e Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Wed, 16 Mar 2011 17:57:13 +0000 Subject: [PATCH 1361/2556] gianfar: Fall back to software tcp/udp checksum on older controllers [ Upstream commit 4363c2fddb1399b728ef21ee8101c148a311ea45 ] As specified by errata eTSEC49 of MPC8548 and errata eTSEC12 of MPC83xx, older revisions of gianfar controllers will be unable to calculate a TCP/UDP packet checksum for some alignments of the appropriate FCB. This patch checks for FCB alignment on such controllers and falls back to software checksumming if the alignment is known to be bad. Signed-off-by: Alex Dubov Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/gianfar.c | 16 ++++++++++++++-- drivers/net/gianfar.h | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 5ed8f9f9419f1..3da19a5559773 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -950,6 +950,11 @@ static void gfar_detect_errata(struct gfar_private *priv) (pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0)) priv->errata |= GFAR_ERRATA_A002; + /* MPC8313 Rev < 2.0, MPC8548 rev 2.0 */ + if ((pvr == 0x80850010 && mod == 0x80b0 && rev < 0x0020) || + (pvr == 0x80210020 && mod == 0x8030 && rev == 0x0020)) + priv->errata |= GFAR_ERRATA_12; + if (priv->errata) dev_info(dev, "enabled errata workarounds, flags: 0x%x\n", priv->errata); @@ -2156,8 +2161,15 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Set up checksumming */ if (CHECKSUM_PARTIAL == skb->ip_summed) { fcb = gfar_add_fcb(skb); - lstatus |= BD_LFLAG(TXBD_TOE); - gfar_tx_checksum(skb, fcb); + /* as specified by errata */ + if (unlikely(gfar_has_errata(priv, GFAR_ERRATA_12) + && ((unsigned long)fcb % 0x20) > 0x18)) { + __skb_pull(skb, GMAC_FCB_LEN); + skb_checksum_help(skb); + } else { + lstatus |= BD_LFLAG(TXBD_TOE); + gfar_tx_checksum(skb, fcb); + } } if (vlan_tx_tag_present(skb)) { diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index 54de4135e932b..ec5d595ce2e26 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -1039,6 +1039,7 @@ enum gfar_errata { GFAR_ERRATA_74 = 0x01, GFAR_ERRATA_76 = 0x02, GFAR_ERRATA_A002 = 0x04, + GFAR_ERRATA_12 = 0x08, /* a.k.a errata eTSEC49 */ }; /* Struct stolen almost completely (and shamelessly) from the FCC enet source From 31869750c720b45a8d00eb25177eeb1f77769da1 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 21 Mar 2011 18:23:34 -0700 Subject: [PATCH 1362/2556] net ipv6: Fix duplicate /proc/sys/net/ipv6/neigh directory entries. [ Upstream commit 9d2a8fa96a44ba242de3a6f56acaef7a40a97b97 ] When I was fixing issues with unregisgtering tables under /proc/sys/net/ipv6/neigh by adding a mount point it appears I missed a critical ordering issue, in the ipv6 initialization. I had not realized that ipv6_sysctl_register is called at the very end of the ipv6 initialization and in particular after we call neigh_sysctl_register from ndisc_init. "neigh" needs to be initialized in ipv6_static_sysctl_register which is the first ipv6 table to initialized, and definitely before ndisc_init. This removes the weirdness of duplicate tables while still providing a "neigh" mount point which prevents races in sysctl unregistering. This was initially reported at https://bugzilla.kernel.org/show_bug.cgi?id=31232 Reported-by: sunkan@zappa.cx Signed-off-by: Eric W. Biederman Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/sysctl_net_ipv6.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index 7cb65ef79f9cd..6dcf5e7d661bd 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -17,6 +17,16 @@ static struct ctl_table empty[1]; +static ctl_table ipv6_static_skeleton[] = { + { + .procname = "neigh", + .maxlen = 0, + .mode = 0555, + .child = empty, + }, + { } +}; + static ctl_table ipv6_table_template[] = { { .procname = "route", @@ -37,12 +47,6 @@ static ctl_table ipv6_table_template[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "neigh", - .maxlen = 0, - .mode = 0555, - .child = empty, - }, { } }; @@ -160,7 +164,7 @@ static struct ctl_table_header *ip6_base; int ipv6_static_sysctl_register(void) { - ip6_base = register_sysctl_paths(net_ipv6_ctl_path, empty); + ip6_base = register_sysctl_paths(net_ipv6_ctl_path, ipv6_static_skeleton); if (ip6_base == NULL) return -ENOMEM; return 0; From 310584edd9ac4e23e2fa55243f97c5bc1645cd50 Mon Sep 17 00:00:00 2001 From: Dan Siemon Date: Tue, 15 Mar 2011 13:56:07 +0000 Subject: [PATCH 1363/2556] net_sched: fix ip_tos2prio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4a2b9c3756077c05dd8666e458a751d2248b61b6 ] ECN support incorrectly maps ECN BESTEFFORT packets to TC_PRIO_FILLER (1) instead of TC_PRIO_BESTEFFORT (0) This means ECN enabled flows are placed in pfifo_fast/prio low priority band, giving ECN enabled flows [ECT(0) and CE codepoints] higher drop probabilities. This is rather unfortunate, given we would like ECN being more widely used. Ref : http://www.coverfire.com/archives/2011/03/13/pfifo_fast-and-ecn/ Signed-off-by: Dan Siemon Signed-off-by: Eric Dumazet Cc: Dave Täht Cc: Jonathan Morton Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/route.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 6ed6603c2f6db..fabfe8168b909 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -171,7 +171,7 @@ static struct dst_ops ipv4_dst_ops = { const __u8 ip_tos2prio[16] = { TC_PRIO_BESTEFFORT, - ECN_OR_COST(FILLER), + ECN_OR_COST(BESTEFFORT), TC_PRIO_BESTEFFORT, ECN_OR_COST(BESTEFFORT), TC_PRIO_BULK, From 1b9d02032ccf70c7d03334a16b62002fcdb97de3 Mon Sep 17 00:00:00 2001 From: Ulrich Weber Date: Wed, 6 Apr 2011 14:04:49 -0700 Subject: [PATCH 1364/2556] pppoe: drop PPPOX_ZOMBIEs in pppoe_flush_dev [ Upstream commit ae07b0b221b6ab2edf9e3abd518aec6cd3f1ba66 ] otherwise we loop forever if a PPPoE socket was set to PPPOX_ZOMBIE state by a PADT message when the ethernet device is going down afterwards. Signed-off-by: Ulrich Weber Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/pppoe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c index 78c0e3c9b2b5f..71b1d8fbc301d 100644 --- a/drivers/net/pppoe.c +++ b/drivers/net/pppoe.c @@ -317,7 +317,7 @@ static void pppoe_flush_dev(struct net_device *dev) lock_sock(sk); if (po->pppoe_dev == dev && - sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) { + sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) { pppox_unbind_sock(sk); sk->sk_state = PPPOX_ZOMBIE; sk->sk_state_change(sk); From 844ada8e3d64923e0c2447b050e84db84fd0811c Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 30 Mar 2011 17:51:36 -0700 Subject: [PATCH 1365/2556] sctp: Pass __GFP_NOWARN to hash table allocation attempts. [ Upstream commit a84b50ceb7d640437d0dc28a2bef0d0de054de89 ] Like DCCP and other similar pieces of code, there are mechanisms here to try allocating smaller hash tables if the allocation fails. So pass in __GFP_NOWARN like the others do instead of emitting a scary message. Reported-by: Dave Jones Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sctp/protocol.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index e58f9476f29c5..dec012d32ff55 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1204,7 +1204,7 @@ SCTP_STATIC __init int sctp_init(void) if ((sctp_assoc_hashsize > (64 * 1024)) && order > 0) continue; sctp_assoc_hashtable = (struct sctp_hashbucket *) - __get_free_pages(GFP_ATOMIC, order); + __get_free_pages(GFP_ATOMIC|__GFP_NOWARN, order); } while (!sctp_assoc_hashtable && --order > 0); if (!sctp_assoc_hashtable) { pr_err("Failed association hash alloc\n"); @@ -1237,7 +1237,7 @@ SCTP_STATIC __init int sctp_init(void) if ((sctp_port_hashsize > (64 * 1024)) && order > 0) continue; sctp_port_hashtable = (struct sctp_bind_hashbucket *) - __get_free_pages(GFP_ATOMIC, order); + __get_free_pages(GFP_ATOMIC|__GFP_NOWARN, order); } while (!sctp_port_hashtable && --order > 0); if (!sctp_port_hashtable) { pr_err("Failed bind hash alloc\n"); From b01b1df85ba4dececfbe4120b7d935a9a2fa9fb7 Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Mon, 14 Mar 2011 10:57:03 +0000 Subject: [PATCH 1366/2556] tcp: avoid cwnd moderation in undo [ Upstream commit 67d4120a1793138bc9f4a6eb61d0fc5298ed97e0 ] In the current undo logic, cwnd is moderated after it was restored to the value prior entering fast-recovery. It was moderated first in tcp_try_undo_recovery then again in tcp_complete_cwr. Since the undo indicates recovery was false, these moderations are not necessary. If the undo is triggered when most of the outstanding data have been acknowledged, the (restored) cwnd is falsely pulled down to a small value. This patch removes these cwnd moderations if cwnd is undone a) during fast-recovery b) by receiving DSACKs past fast-recovery Signed-off-by: Yuchung Cheng Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_input.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 65f6c04062453..5f1ea7cb18afc 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2659,7 +2659,7 @@ static void DBGUNDO(struct sock *sk, const char *msg) #define DBGUNDO(x...) do { } while (0) #endif -static void tcp_undo_cwr(struct sock *sk, const int undo) +static void tcp_undo_cwr(struct sock *sk, const int undo_ssthresh) { struct tcp_sock *tp = tcp_sk(sk); @@ -2671,14 +2671,13 @@ static void tcp_undo_cwr(struct sock *sk, const int undo) else tp->snd_cwnd = max(tp->snd_cwnd, tp->snd_ssthresh << 1); - if (undo && tp->prior_ssthresh > tp->snd_ssthresh) { + if (undo_ssthresh && tp->prior_ssthresh > tp->snd_ssthresh) { tp->snd_ssthresh = tp->prior_ssthresh; TCP_ECN_withdraw_cwr(tp); } } else { tp->snd_cwnd = max(tp->snd_cwnd, tp->snd_ssthresh); } - tcp_moderate_cwnd(tp); tp->snd_cwnd_stamp = tcp_time_stamp; } @@ -2822,8 +2821,11 @@ static int tcp_try_undo_loss(struct sock *sk) static inline void tcp_complete_cwr(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); - tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh); - tp->snd_cwnd_stamp = tcp_time_stamp; + /* Do not moderate cwnd if it's already undone in cwr or recovery */ + if (tp->undo_marker && tp->snd_cwnd > tp->snd_ssthresh) { + tp->snd_cwnd = tp->snd_ssthresh; + tp->snd_cwnd_stamp = tcp_time_stamp; + } tcp_ca_event(sk, CA_EVENT_COMPLETE_CWR); } From 4d61c2dec7bd7c23017f2270902141613eabaf60 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Tue, 15 Mar 2011 21:12:49 +0000 Subject: [PATCH 1367/2556] xfrm: Refcount destination entry on xfrm_lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit fbd5060875d25f7764fd1c3d35b83a8ed1d88d7b ] We return a destination entry without refcount if a socket policy is found in xfrm_lookup. This triggers a warning on a negative refcount when freeeing this dst entry. So take a refcount in this case to fix it. This refcount was forgotten when xfrm changed to cache bundles instead of policies for outgoing flows. Signed-off-by: Steffen Klassert Acked-by: Timo Teräs Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/xfrm/xfrm_policy.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 6459588befc33..8da2741d7997b 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1778,6 +1778,8 @@ int __xfrm_lookup(struct net *net, struct dst_entry **dst_p, struct flowi *fl, goto no_transform; } + dst_hold(&xdst->u.dst); + spin_lock_bh(&xfrm_policy_sk_bundle_lock); xdst->u.dst.next = xfrm_policy_sk_bundles; xfrm_policy_sk_bundles = &xdst->u.dst; From a2ead1b2e242a01a23776ff3740e9c3becebdf02 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 18 Mar 2011 00:27:27 +0000 Subject: [PATCH 1368/2556] vlan: should take into account needed_headroom [ Upstream commit d870bfb9d366c5d466c0f5419a4ec95a3f71ea8a ] Commit c95b819ad7 (gre: Use needed_headroom) made gre use needed_headroom instead of hard_header_len This uncover a bug in vlan code. We should make sure vlan devices take into account their real_dev->needed_headroom or we risk a crash in ipgre_header(), because we dont have enough room to push IP header in skb. Reported-by: Diddi Oscarsson Signed-off-by: Eric Dumazet Cc: Patrick McHardy Cc: Herbert Xu Acked-by: Herbert Xu Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/8021q/vlan_dev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index be737539f34d1..ed68d0722e531 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -707,6 +707,7 @@ static int vlan_dev_init(struct net_device *dev) dev->fcoe_ddp_xid = real_dev->fcoe_ddp_xid; #endif + dev->needed_headroom = real_dev->needed_headroom; if (real_dev->features & NETIF_F_HW_VLAN_TX) { dev->header_ops = real_dev->header_ops; dev->hard_header_len = real_dev->hard_header_len; From e8251b753997fc53e11870260f212bdac800329f Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Fri, 18 Mar 2011 05:27:28 +0000 Subject: [PATCH 1369/2556] bridge: Reset IPCB when entering IP stack on NF_FORWARD [ Upstream commit 6b1e960fdbd75dcd9bcc3ba5ff8898ff1ad30b6e ] Whenever we enter the IP stack proper from bridge netfilter we need to ensure that the skb is in a form the IP stack expects it to be in. The entry point on NF_FORWARD did not meet the requirements of the IP stack, therefore leading to potential crashes/panics. This patch fixes the problem. Signed-off-by: Herbert Xu Acked-by: Stephen Hemminger Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/bridge/br_netfilter.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 4b5b66d07bba7..49d50ea5dbbc6 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -741,6 +741,9 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb, nf_bridge->mask |= BRNF_PKT_TYPE; } + if (br_parse_ip_options(skb)) + return NF_DROP; + /* The physdev module checks on this */ nf_bridge->mask |= BRNF_BRIDGED; nf_bridge->physoutdev = skb->dev; From 343de10932eac96340009c229abe89447e6d6f1b Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Wed, 9 Mar 2011 12:54:27 -0800 Subject: [PATCH 1370/2556] sparc: Fix .size directive for do_int_load [ Upstream commit 35043c428f1fcb92feb5792f5878a8852ee00771 ] gas used to accept (and ignore?) .size directives which referred to undefined symbols, as this does. In binutils 2.21 these are treated as errors. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- arch/sparc/kernel/una_asm_64.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sparc/kernel/una_asm_64.S b/arch/sparc/kernel/una_asm_64.S index be183fe41443f..1c8d33228b2a6 100644 --- a/arch/sparc/kernel/una_asm_64.S +++ b/arch/sparc/kernel/una_asm_64.S @@ -127,7 +127,7 @@ do_int_load: wr %o5, 0x0, %asi retl mov 0, %o0 - .size __do_int_load, .-__do_int_load + .size do_int_load, .-do_int_load .section __ex_table,"a" .word 4b, __retl_efault From 684bc3bf9a0fbefdfd89445b13d98f008bcef25e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 9 Mar 2011 13:00:47 -0800 Subject: [PATCH 1371/2556] sparc32: Fix might-be-used-uninitialized warning in do_sparc_fault(). [ Upstream commit c816be7b5f24585baa9eba1f2413935f771d6ad6 ] When we try to handle vmalloc faults, we can take a code path which uses "code" before we actually set it. Amusingly gcc-3.3 notices this yet gcc-4.x does not. Reported-by: Bob Breuer Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- arch/sparc/mm/fault_32.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/sparc/mm/fault_32.c b/arch/sparc/mm/fault_32.c index 5b836f5aea90b..b10ac4d62378a 100644 --- a/arch/sparc/mm/fault_32.c +++ b/arch/sparc/mm/fault_32.c @@ -240,11 +240,10 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write, * only copy the information from the master page table, * nothing more. */ + code = SEGV_MAPERR; if (!ARCH_SUN4C && address >= TASK_SIZE) goto vmalloc_fault; - code = SEGV_MAPERR; - /* * If we're in an interrupt or have no user * context, we must not take the fault.. From f80b965cf1b67cc6d71fff30edd94b49aa8c3912 Mon Sep 17 00:00:00 2001 From: Tkhai Kirill Date: Thu, 31 Mar 2011 00:52:38 -0700 Subject: [PATCH 1372/2556] sparc32: Pass task_struct to schedule_tail() in ret_from_fork [ Upstream commit 47c7c97a93a5b8f719093dbf83555090b3b8228b ] We have to pass task_struct of previous process to function schedule_tail(). Currently in ret_from_fork previous thread_info is passed: switch_to: mov %g6, %g3 /* previous thread_info in g6 */ ret_from_fork: call schedule_tail mov %g3, %o0 /* previous thread_info is passed */ void schedule_tail(struct task_struct *prev); Signed-off-by: Tkhai Kirill Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- arch/sparc/kernel/entry.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index 1504df8ddf70a..906ee3e24cc96 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S @@ -1283,7 +1283,7 @@ linux_syscall_trace: .globl ret_from_fork ret_from_fork: call schedule_tail - mov %g3, %o0 + ld [%g3 + TI_TASK], %o0 b ret_sys_call ld [%sp + STACKFRAME_SZ + PT_I0], %o0 From d5f29bdda5ae5d88ee80c38661f4643cae4e3f2f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sat, 26 Feb 2011 23:40:02 -0800 Subject: [PATCH 1373/2556] sparc64: Fix build errors with gcc-4.6.0 [ Upstream commit c6fee0810df4e0f4cf9c4834d2569ca01c02cffc ] Most of the warnings emitted (we fail arch/sparc file builds with -Werror) were legitimate but harmless, however one case (n2_pcr_write) was a genuine bug. Based almost entirely upon a patch by Sam Ravnborg. Reported-by: Dennis Gilmore Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- arch/sparc/kernel/iommu.c | 3 --- arch/sparc/kernel/ldc.c | 28 ++++++++++++++++++---------- arch/sparc/kernel/pci.c | 1 + arch/sparc/kernel/pci_common.c | 11 +++++++---- arch/sparc/kernel/pci_fire.c | 2 -- arch/sparc/kernel/pci_schizo.c | 4 +--- arch/sparc/kernel/pci_sun4v.c | 3 +-- arch/sparc/kernel/pcr.c | 2 +- arch/sparc/kernel/ptrace_64.c | 3 ++- arch/sparc/kernel/smp_64.c | 11 ++++------- arch/sparc/kernel/traps_64.c | 3 +-- 11 files changed, 36 insertions(+), 35 deletions(-) diff --git a/arch/sparc/kernel/iommu.c b/arch/sparc/kernel/iommu.c index 72509d0e34be2..6f01e8c83197e 100644 --- a/arch/sparc/kernel/iommu.c +++ b/arch/sparc/kernel/iommu.c @@ -333,13 +333,10 @@ static void dma_4u_free_coherent(struct device *dev, size_t size, void *cpu, dma_addr_t dvma) { struct iommu *iommu; - iopte_t *iopte; unsigned long flags, order, npages; npages = IO_PAGE_ALIGN(size) >> IO_PAGE_SHIFT; iommu = dev->archdata.iommu; - iopte = iommu->page_table + - ((dvma - iommu->page_table_map_base) >> IO_PAGE_SHIFT); spin_lock_irqsave(&iommu->lock, flags); diff --git a/arch/sparc/kernel/ldc.c b/arch/sparc/kernel/ldc.c index df39a0f0d27af..732b0bce6001c 100644 --- a/arch/sparc/kernel/ldc.c +++ b/arch/sparc/kernel/ldc.c @@ -790,16 +790,20 @@ static void send_events(struct ldc_channel *lp, unsigned int event_mask) static irqreturn_t ldc_rx(int irq, void *dev_id) { struct ldc_channel *lp = dev_id; - unsigned long orig_state, hv_err, flags; + unsigned long orig_state, flags; unsigned int event_mask; spin_lock_irqsave(&lp->lock, flags); orig_state = lp->chan_state; - hv_err = sun4v_ldc_rx_get_state(lp->id, - &lp->rx_head, - &lp->rx_tail, - &lp->chan_state); + + /* We should probably check for hypervisor errors here and + * reset the LDC channel if we get one. + */ + sun4v_ldc_rx_get_state(lp->id, + &lp->rx_head, + &lp->rx_tail, + &lp->chan_state); ldcdbg(RX, "RX state[0x%02lx:0x%02lx] head[0x%04lx] tail[0x%04lx]\n", orig_state, lp->chan_state, lp->rx_head, lp->rx_tail); @@ -904,16 +908,20 @@ static irqreturn_t ldc_rx(int irq, void *dev_id) static irqreturn_t ldc_tx(int irq, void *dev_id) { struct ldc_channel *lp = dev_id; - unsigned long flags, hv_err, orig_state; + unsigned long flags, orig_state; unsigned int event_mask = 0; spin_lock_irqsave(&lp->lock, flags); orig_state = lp->chan_state; - hv_err = sun4v_ldc_tx_get_state(lp->id, - &lp->tx_head, - &lp->tx_tail, - &lp->chan_state); + + /* We should probably check for hypervisor errors here and + * reset the LDC channel if we get one. + */ + sun4v_ldc_tx_get_state(lp->id, + &lp->tx_head, + &lp->tx_tail, + &lp->chan_state); ldcdbg(TX, " TX state[0x%02lx:0x%02lx] head[0x%04lx] tail[0x%04lx]\n", orig_state, lp->chan_state, lp->tx_head, lp->tx_tail); diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index 4137579d9adcd..f255382b02b86 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -675,6 +675,7 @@ static void __devinit pci_bus_register_of_sysfs(struct pci_bus *bus) * humanoid. */ err = sysfs_create_file(&dev->dev.kobj, &dev_attr_obppath.attr); + (void) err; } list_for_each_entry(child_bus, &bus->children, node) pci_bus_register_of_sysfs(child_bus); diff --git a/arch/sparc/kernel/pci_common.c b/arch/sparc/kernel/pci_common.c index 6c7a33af3ba62..6e3874b644880 100644 --- a/arch/sparc/kernel/pci_common.c +++ b/arch/sparc/kernel/pci_common.c @@ -295,14 +295,17 @@ static int sun4v_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, unsigned int bus = bus_dev->number; unsigned int device = PCI_SLOT(devfn); unsigned int func = PCI_FUNC(devfn); - unsigned long ret; if (config_out_of_range(pbm, bus, devfn, where)) { /* Do nothing. */ } else { - ret = pci_sun4v_config_put(devhandle, - HV_PCI_DEVICE_BUILD(bus, device, func), - where, size, value); + /* We don't check for hypervisor errors here, but perhaps + * we should and influence our return value depending upon + * what kind of error is thrown. + */ + pci_sun4v_config_put(devhandle, + HV_PCI_DEVICE_BUILD(bus, device, func), + where, size, value); } return PCIBIOS_SUCCESSFUL; } diff --git a/arch/sparc/kernel/pci_fire.c b/arch/sparc/kernel/pci_fire.c index efb896d687540..75dfeb60ef6eb 100644 --- a/arch/sparc/kernel/pci_fire.c +++ b/arch/sparc/kernel/pci_fire.c @@ -214,11 +214,9 @@ static int pci_fire_msi_setup(struct pci_pbm_info *pbm, unsigned long msiqid, static int pci_fire_msi_teardown(struct pci_pbm_info *pbm, unsigned long msi) { - unsigned long msiqid; u64 val; val = upa_readq(pbm->pbm_regs + MSI_MAP(msi)); - msiqid = (val & MSI_MAP_EQNUM); val &= ~MSI_MAP_VALID; diff --git a/arch/sparc/kernel/pci_schizo.c b/arch/sparc/kernel/pci_schizo.c index 445a47a2fb3dd..4620eb76aef4c 100644 --- a/arch/sparc/kernel/pci_schizo.c +++ b/arch/sparc/kernel/pci_schizo.c @@ -1313,7 +1313,7 @@ static int __devinit schizo_pbm_init(struct pci_pbm_info *pbm, const struct linux_prom64_registers *regs; struct device_node *dp = op->dev.of_node; const char *chipset_name; - int is_pbm_a, err; + int err; switch (chip_type) { case PBM_CHIP_TYPE_TOMATILLO: @@ -1343,8 +1343,6 @@ static int __devinit schizo_pbm_init(struct pci_pbm_info *pbm, */ regs = of_get_property(dp, "reg", NULL); - is_pbm_a = ((regs[0].phys_addr & 0x00700000) == 0x00600000); - pbm->next = pci_pbm_root; pci_pbm_root = pbm; diff --git a/arch/sparc/kernel/pci_sun4v.c b/arch/sparc/kernel/pci_sun4v.c index 743344aa6d8a6..859abfd789373 100644 --- a/arch/sparc/kernel/pci_sun4v.c +++ b/arch/sparc/kernel/pci_sun4v.c @@ -580,7 +580,7 @@ static int __devinit pci_sun4v_iommu_init(struct pci_pbm_info *pbm) { static const u32 vdma_default[] = { 0x80000000, 0x80000000 }; struct iommu *iommu = pbm->iommu; - unsigned long num_tsb_entries, sz, tsbsize; + unsigned long num_tsb_entries, sz; u32 dma_mask, dma_offset; const u32 *vdma; @@ -596,7 +596,6 @@ static int __devinit pci_sun4v_iommu_init(struct pci_pbm_info *pbm) dma_mask = (roundup_pow_of_two(vdma[1]) - 1UL); num_tsb_entries = vdma[1] / IO_PAGE_SIZE; - tsbsize = num_tsb_entries * sizeof(iopte_t); dma_offset = vdma[0]; diff --git a/arch/sparc/kernel/pcr.c b/arch/sparc/kernel/pcr.c index 7c2ced612b8f5..8ac23e6600804 100644 --- a/arch/sparc/kernel/pcr.c +++ b/arch/sparc/kernel/pcr.c @@ -81,7 +81,7 @@ static void n2_pcr_write(u64 val) unsigned long ret; ret = sun4v_niagara2_setperf(HV_N2_PERF_SPARC_CTL, val); - if (val != HV_EOK) + if (ret != HV_EOK) write_pcr(val); } diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c index 9ccc812bc09e6..96ee50a806613 100644 --- a/arch/sparc/kernel/ptrace_64.c +++ b/arch/sparc/kernel/ptrace_64.c @@ -1086,6 +1086,7 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs) asmlinkage void syscall_trace_leave(struct pt_regs *regs) { +#ifdef CONFIG_AUDITSYSCALL if (unlikely(current->audit_context)) { unsigned long tstate = regs->tstate; int result = AUDITSC_SUCCESS; @@ -1095,7 +1096,7 @@ asmlinkage void syscall_trace_leave(struct pt_regs *regs) audit_syscall_exit(result, regs->u_regs[UREG_I0]); } - +#endif if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_exit(regs, regs->u_regs[UREG_G1]); diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c index 555a76d1f4a18..3e94a8c232388 100644 --- a/arch/sparc/kernel/smp_64.c +++ b/arch/sparc/kernel/smp_64.c @@ -189,7 +189,7 @@ static inline long get_delta (long *rt, long *master) void smp_synchronize_tick_client(void) { long i, delta, adj, adjust_latency = 0, done = 0; - unsigned long flags, rt, master_time_stamp, bound; + unsigned long flags, rt, master_time_stamp; #if DEBUG_TICK_SYNC struct { long rt; /* roundtrip time */ @@ -208,10 +208,8 @@ void smp_synchronize_tick_client(void) { for (i = 0; i < NUM_ROUNDS; i++) { delta = get_delta(&rt, &master_time_stamp); - if (delta == 0) { + if (delta == 0) done = 1; /* let's lock on to this... */ - bound = rt; - } if (!done) { if (i > 0) { @@ -933,13 +931,12 @@ void smp_flush_dcache_page_impl(struct page *page, int cpu) void flush_dcache_page_all(struct mm_struct *mm, struct page *page) { void *pg_addr; - int this_cpu; u64 data0; if (tlb_type == hypervisor) return; - this_cpu = get_cpu(); + preempt_disable(); #ifdef CONFIG_DEBUG_DCFLUSH atomic_inc(&dcpage_flushes); @@ -964,7 +961,7 @@ void flush_dcache_page_all(struct mm_struct *mm, struct page *page) } __local_flush_dcache_page(page); - put_cpu(); + preempt_enable(); } void __irq_entry smp_new_mmu_context_version_client(int irq, struct pt_regs *regs) diff --git a/arch/sparc/kernel/traps_64.c b/arch/sparc/kernel/traps_64.c index 1e9770936c3b8..1ed547bd850f8 100644 --- a/arch/sparc/kernel/traps_64.c +++ b/arch/sparc/kernel/traps_64.c @@ -2152,7 +2152,7 @@ static void user_instruction_dump(unsigned int __user *pc) void show_stack(struct task_struct *tsk, unsigned long *_ksp) { - unsigned long fp, thread_base, ksp; + unsigned long fp, ksp; struct thread_info *tp; int count = 0; #ifdef CONFIG_FUNCTION_GRAPH_TRACER @@ -2173,7 +2173,6 @@ void show_stack(struct task_struct *tsk, unsigned long *_ksp) flushw_all(); fp = ksp + STACK_BIAS; - thread_base = (unsigned long) tp; printk("Call Trace:\n"); do { From 49f34201364510fe7352571c79922e06fd9d5396 Mon Sep 17 00:00:00 2001 From: Darren Hart Date: Thu, 14 Apr 2011 15:41:57 -0700 Subject: [PATCH 1374/2556] futex: Set FLAGS_HAS_TIMEOUT during futex_wait restart setup commit 0cd9c6494ee5c19aef085152bc37f3a4e774a9e1 upstream. The FLAGS_HAS_TIMEOUT flag was not getting set, causing the restart_block to restart futex_wait() without a timeout after a signal. Commit b41277dc7a18ee332d in 2.6.38 introduced the regression by accidentally removing the the FLAGS_HAS_TIMEOUT assignment from futex_wait() during the setup of the restart block. Restore the originaly behavior. Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=32922 Reported-by: Tim Smith Reported-by: Torsten Hilbrich Signed-off-by: Darren Hart Signed-off-by: Eric Dumazet Cc: Peter Zijlstra Cc: John Kacur Link: http://lkml.kernel.org/r/%3Cdaac0eb3af607f72b9a4d3126b2ba8fb5ed3b883.1302820917.git.dvhart%40linux.intel.com%3E Signed-off-by: Thomas Gleixner Signed-off-by: Greg Kroah-Hartman --- kernel/futex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/futex.c b/kernel/futex.c index b766d28accd6b..d5065e8283dac 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -1886,7 +1886,7 @@ static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val, restart->futex.val = val; restart->futex.time = abs_time->tv64; restart->futex.bitset = bitset; - restart->futex.flags = flags; + restart->futex.flags = flags | FLAGS_HAS_TIMEOUT; ret = -ERESTART_RESTARTBLOCK; From ab4c07c1a00b47f4ffc541b842e4b1d11d631102 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 22 Mar 2011 16:34:40 -0700 Subject: [PATCH 1375/2556] kstrto*: converting strings to integers done (hopefully) right commit 33ee3b2e2eb9b4b6c64dcf9ed66e2ac3124e748c upstream. 1. simple_strto*() do not contain overflow checks and crufty, libc way to indicate failure. 2. strict_strto*() also do not have overflow checks but the name and comments pretend they do. 3. Both families have only "long long" and "long" variants, but users want strtou8() 4. Both "simple" and "strict" prefixes are wrong: Simple doesn't exactly say what's so simple, strict should not exist because conversion should be strict by default. The solution is to use "k" prefix and add convertors for more types. Enter kstrtoull() kstrtoll() kstrtoul() kstrtol() kstrtouint() kstrtoint() kstrtou64() kstrtos64() kstrtou32() kstrtos32() kstrtou16() kstrtos16() kstrtou8() kstrtos8() Include runtime testsuite (somewhat incomplete) as well. strict_strto*() become deprecated, stubbed to kstrto*() and eventually will be removed altogether. Use kstrto*() in code today! Note: on some archs _kstrtoul() and _kstrtol() are left in tree, even if they'll be unused at runtime. This is temporarily solution, because I don't want to hardcode list of archs where these functions aren't needed. Current solution with sizeof() and __alignof__ at least always works. Signed-off-by: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/video/via/viafbdev.h | 3 - include/linux/kernel.h | 70 +++- lib/Kconfig.debug | 3 + lib/Makefile | 2 + lib/kstrtox.c | 227 +++++++++++ lib/test-kstrtox.c | 739 +++++++++++++++++++++++++++++++++++ lib/vsprintf.c | 141 ------- scripts/checkpatch.pl | 4 +- 8 files changed, 1039 insertions(+), 150 deletions(-) create mode 100644 lib/kstrtox.c create mode 100644 lib/test-kstrtox.c diff --git a/drivers/video/via/viafbdev.h b/drivers/video/via/viafbdev.h index d66f963e930e3..137996dc547e9 100644 --- a/drivers/video/via/viafbdev.h +++ b/drivers/video/via/viafbdev.h @@ -94,9 +94,6 @@ extern int viafb_LCD_ON; extern int viafb_DVI_ON; extern int viafb_hotplug; -extern int strict_strtoul(const char *cp, unsigned int base, - unsigned long *res); - u8 viafb_gpio_i2c_read_lvds(struct lvds_setting_information *plvds_setting_info, struct lvds_chip_information *plvds_chip_info, u8 index); diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 2fe6e84894a4b..00cec4dc0ae25 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -187,14 +187,76 @@ NORET_TYPE void do_exit(long error_code) ATTRIB_NORET; NORET_TYPE void complete_and_exit(struct completion *, long) ATTRIB_NORET; + +/* Internal, do not use. */ +int __must_check _kstrtoul(const char *s, unsigned int base, unsigned long *res); +int __must_check _kstrtol(const char *s, unsigned int base, long *res); + +int __must_check kstrtoull(const char *s, unsigned int base, unsigned long long *res); +int __must_check kstrtoll(const char *s, unsigned int base, long long *res); +static inline int __must_check kstrtoul(const char *s, unsigned int base, unsigned long *res) +{ + /* + * We want to shortcut function call, but + * __builtin_types_compatible_p(unsigned long, unsigned long long) = 0. + */ + if (sizeof(unsigned long) == sizeof(unsigned long long) && + __alignof__(unsigned long) == __alignof__(unsigned long long)) + return kstrtoull(s, base, (unsigned long long *)res); + else + return _kstrtoul(s, base, res); +} + +static inline int __must_check kstrtol(const char *s, unsigned int base, long *res) +{ + /* + * We want to shortcut function call, but + * __builtin_types_compatible_p(long, long long) = 0. + */ + if (sizeof(long) == sizeof(long long) && + __alignof__(long) == __alignof__(long long)) + return kstrtoll(s, base, (long long *)res); + else + return _kstrtol(s, base, res); +} + +int __must_check kstrtouint(const char *s, unsigned int base, unsigned int *res); +int __must_check kstrtoint(const char *s, unsigned int base, int *res); + +static inline int __must_check kstrtou64(const char *s, unsigned int base, u64 *res) +{ + return kstrtoull(s, base, res); +} + +static inline int __must_check kstrtos64(const char *s, unsigned int base, s64 *res) +{ + return kstrtoll(s, base, res); +} + +static inline int __must_check kstrtou32(const char *s, unsigned int base, u32 *res) +{ + return kstrtouint(s, base, res); +} + +static inline int __must_check kstrtos32(const char *s, unsigned int base, s32 *res) +{ + return kstrtoint(s, base, res); +} + +int __must_check kstrtou16(const char *s, unsigned int base, u16 *res); +int __must_check kstrtos16(const char *s, unsigned int base, s16 *res); +int __must_check kstrtou8(const char *s, unsigned int base, u8 *res); +int __must_check kstrtos8(const char *s, unsigned int base, s8 *res); + extern unsigned long simple_strtoul(const char *,char **,unsigned int); extern long simple_strtol(const char *,char **,unsigned int); extern unsigned long long simple_strtoull(const char *,char **,unsigned int); extern long long simple_strtoll(const char *,char **,unsigned int); -extern int __must_check strict_strtoul(const char *, unsigned int, unsigned long *); -extern int __must_check strict_strtol(const char *, unsigned int, long *); -extern int __must_check strict_strtoull(const char *, unsigned int, unsigned long long *); -extern int __must_check strict_strtoll(const char *, unsigned int, long long *); +#define strict_strtoul kstrtoul +#define strict_strtol kstrtol +#define strict_strtoull kstrtoull +#define strict_strtoll kstrtoll + extern int sprintf(char * buf, const char * fmt, ...) __attribute__ ((format (printf, 2, 3))); extern int vsprintf(char *buf, const char *, va_list) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 44553231091f8..7909c96e4da6b 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1237,3 +1237,6 @@ source "samples/Kconfig" source "lib/Kconfig.kgdb" source "lib/Kconfig.kmemcheck" + +config TEST_KSTRTOX + tristate "Test kstrto*() family of functions at runtime" diff --git a/lib/Makefile b/lib/Makefile index cbb774f7d41d0..dfbf7d82ce98b 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -22,6 +22,8 @@ lib-y += kobject.o kref.o klist.o obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \ string_helpers.o gcd.o lcm.o list_sort.o uuid.o flex_array.o +obj-y += kstrtox.o +obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o ifeq ($(CONFIG_DEBUG_KOBJECT),y) CFLAGS_kobject.o += -DDEBUG diff --git a/lib/kstrtox.c b/lib/kstrtox.c new file mode 100644 index 0000000000000..05672e819f8c4 --- /dev/null +++ b/lib/kstrtox.c @@ -0,0 +1,227 @@ +/* + * Convert integer string representation to an integer. + * If an integer doesn't fit into specified type, -E is returned. + * + * Integer starts with optional sign. + * kstrtou*() functions do not accept sign "-". + * + * Radix 0 means autodetection: leading "0x" implies radix 16, + * leading "0" implies radix 8, otherwise radix is 10. + * Autodetection hints work after optional sign, but not before. + * + * If -E is returned, result is not touched. + */ +#include +#include +#include +#include +#include +#include + +static inline char _tolower(const char c) +{ + return c | 0x20; +} + +static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) +{ + unsigned long long acc; + int ok; + + if (base == 0) { + if (s[0] == '0') { + if (_tolower(s[1]) == 'x' && isxdigit(s[2])) + base = 16; + else + base = 8; + } else + base = 10; + } + if (base == 16 && s[0] == '0' && _tolower(s[1]) == 'x') + s += 2; + + acc = 0; + ok = 0; + while (*s) { + unsigned int val; + + if ('0' <= *s && *s <= '9') + val = *s - '0'; + else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f') + val = _tolower(*s) - 'a' + 10; + else if (*s == '\n') { + if (*(s + 1) == '\0') + break; + else + return -EINVAL; + } else + return -EINVAL; + + if (val >= base) + return -EINVAL; + if (acc > div_u64(ULLONG_MAX - val, base)) + return -ERANGE; + acc = acc * base + val; + ok = 1; + + s++; + } + if (!ok) + return -EINVAL; + *res = acc; + return 0; +} + +int kstrtoull(const char *s, unsigned int base, unsigned long long *res) +{ + if (s[0] == '+') + s++; + return _kstrtoull(s, base, res); +} +EXPORT_SYMBOL(kstrtoull); + +int kstrtoll(const char *s, unsigned int base, long long *res) +{ + unsigned long long tmp; + int rv; + + if (s[0] == '-') { + rv = _kstrtoull(s + 1, base, &tmp); + if (rv < 0) + return rv; + if ((long long)(-tmp) >= 0) + return -ERANGE; + *res = -tmp; + } else { + rv = kstrtoull(s, base, &tmp); + if (rv < 0) + return rv; + if ((long long)tmp < 0) + return -ERANGE; + *res = tmp; + } + return 0; +} +EXPORT_SYMBOL(kstrtoll); + +/* Internal, do not use. */ +int _kstrtoul(const char *s, unsigned int base, unsigned long *res) +{ + unsigned long long tmp; + int rv; + + rv = kstrtoull(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (unsigned long long)(unsigned long)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(_kstrtoul); + +/* Internal, do not use. */ +int _kstrtol(const char *s, unsigned int base, long *res) +{ + long long tmp; + int rv; + + rv = kstrtoll(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (long long)(long)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(_kstrtol); + +int kstrtouint(const char *s, unsigned int base, unsigned int *res) +{ + unsigned long long tmp; + int rv; + + rv = kstrtoull(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (unsigned long long)(unsigned int)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtouint); + +int kstrtoint(const char *s, unsigned int base, int *res) +{ + long long tmp; + int rv; + + rv = kstrtoll(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (long long)(int)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtoint); + +int kstrtou16(const char *s, unsigned int base, u16 *res) +{ + unsigned long long tmp; + int rv; + + rv = kstrtoull(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (unsigned long long)(u16)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtou16); + +int kstrtos16(const char *s, unsigned int base, s16 *res) +{ + long long tmp; + int rv; + + rv = kstrtoll(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (long long)(s16)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtos16); + +int kstrtou8(const char *s, unsigned int base, u8 *res) +{ + unsigned long long tmp; + int rv; + + rv = kstrtoull(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (unsigned long long)(u8)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtou8); + +int kstrtos8(const char *s, unsigned int base, s8 *res) +{ + long long tmp; + int rv; + + rv = kstrtoll(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (long long)(s8)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtos8); diff --git a/lib/test-kstrtox.c b/lib/test-kstrtox.c new file mode 100644 index 0000000000000..325c2f9ecebdc --- /dev/null +++ b/lib/test-kstrtox.c @@ -0,0 +1,739 @@ +#include +#include +#include + +#define for_each_test(i, test) \ + for (i = 0; i < sizeof(test) / sizeof(test[0]); i++) + +struct test_fail { + const char *str; + unsigned int base; +}; + +#define DEFINE_TEST_FAIL(test) \ + const struct test_fail test[] __initdata + +#define DECLARE_TEST_OK(type, test_type) \ + test_type { \ + const char *str; \ + unsigned int base; \ + type expected_res; \ + } + +#define DEFINE_TEST_OK(type, test) \ + const type test[] __initdata + +#define TEST_FAIL(fn, type, fmt, test) \ +{ \ + unsigned int i; \ + \ + for_each_test(i, test) { \ + const struct test_fail *t = &test[i]; \ + type tmp; \ + int rv; \ + \ + tmp = 0; \ + rv = fn(t->str, t->base, &tmp); \ + if (rv >= 0) { \ + WARN(1, "str '%s', base %u, expected -E, got %d/" fmt "\n", \ + t->str, t->base, rv, tmp); \ + continue; \ + } \ + } \ +} + +#define TEST_OK(fn, type, fmt, test) \ +{ \ + unsigned int i; \ + \ + for_each_test(i, test) { \ + const typeof(test[0]) *t = &test[i]; \ + type res; \ + int rv; \ + \ + rv = fn(t->str, t->base, &res); \ + if (rv != 0) { \ + WARN(1, "str '%s', base %u, expected 0/" fmt ", got %d\n", \ + t->str, t->base, t->expected_res, rv); \ + continue; \ + } \ + if (res != t->expected_res) { \ + WARN(1, "str '%s', base %u, expected " fmt ", got " fmt "\n", \ + t->str, t->base, t->expected_res, res); \ + continue; \ + } \ + } \ +} + +static void __init test_kstrtoull_ok(void) +{ + DECLARE_TEST_OK(unsigned long long, struct test_ull); + static DEFINE_TEST_OK(struct test_ull, test_ull_ok) = { + {"0", 10, 0ULL}, + {"1", 10, 1ULL}, + {"127", 10, 127ULL}, + {"128", 10, 128ULL}, + {"129", 10, 129ULL}, + {"255", 10, 255ULL}, + {"256", 10, 256ULL}, + {"257", 10, 257ULL}, + {"32767", 10, 32767ULL}, + {"32768", 10, 32768ULL}, + {"32769", 10, 32769ULL}, + {"65535", 10, 65535ULL}, + {"65536", 10, 65536ULL}, + {"65537", 10, 65537ULL}, + {"2147483647", 10, 2147483647ULL}, + {"2147483648", 10, 2147483648ULL}, + {"2147483649", 10, 2147483649ULL}, + {"4294967295", 10, 4294967295ULL}, + {"4294967296", 10, 4294967296ULL}, + {"4294967297", 10, 4294967297ULL}, + {"9223372036854775807", 10, 9223372036854775807ULL}, + {"9223372036854775808", 10, 9223372036854775808ULL}, + {"9223372036854775809", 10, 9223372036854775809ULL}, + {"18446744073709551614", 10, 18446744073709551614ULL}, + {"18446744073709551615", 10, 18446744073709551615ULL}, + + {"00", 8, 00ULL}, + {"01", 8, 01ULL}, + {"0177", 8, 0177ULL}, + {"0200", 8, 0200ULL}, + {"0201", 8, 0201ULL}, + {"0377", 8, 0377ULL}, + {"0400", 8, 0400ULL}, + {"0401", 8, 0401ULL}, + {"077777", 8, 077777ULL}, + {"0100000", 8, 0100000ULL}, + {"0100001", 8, 0100001ULL}, + {"0177777", 8, 0177777ULL}, + {"0200000", 8, 0200000ULL}, + {"0200001", 8, 0200001ULL}, + {"017777777777", 8, 017777777777ULL}, + {"020000000000", 8, 020000000000ULL}, + {"020000000001", 8, 020000000001ULL}, + {"037777777777", 8, 037777777777ULL}, + {"040000000000", 8, 040000000000ULL}, + {"040000000001", 8, 040000000001ULL}, + {"0777777777777777777777", 8, 0777777777777777777777ULL}, + {"01000000000000000000000", 8, 01000000000000000000000ULL}, + {"01000000000000000000001", 8, 01000000000000000000001ULL}, + {"01777777777777777777776", 8, 01777777777777777777776ULL}, + {"01777777777777777777777", 8, 01777777777777777777777ULL}, + + {"0x0", 16, 0x0ULL}, + {"0x1", 16, 0x1ULL}, + {"0x7f", 16, 0x7fULL}, + {"0x80", 16, 0x80ULL}, + {"0x81", 16, 0x81ULL}, + {"0xff", 16, 0xffULL}, + {"0x100", 16, 0x100ULL}, + {"0x101", 16, 0x101ULL}, + {"0x7fff", 16, 0x7fffULL}, + {"0x8000", 16, 0x8000ULL}, + {"0x8001", 16, 0x8001ULL}, + {"0xffff", 16, 0xffffULL}, + {"0x10000", 16, 0x10000ULL}, + {"0x10001", 16, 0x10001ULL}, + {"0x7fffffff", 16, 0x7fffffffULL}, + {"0x80000000", 16, 0x80000000ULL}, + {"0x80000001", 16, 0x80000001ULL}, + {"0xffffffff", 16, 0xffffffffULL}, + {"0x100000000", 16, 0x100000000ULL}, + {"0x100000001", 16, 0x100000001ULL}, + {"0x7fffffffffffffff", 16, 0x7fffffffffffffffULL}, + {"0x8000000000000000", 16, 0x8000000000000000ULL}, + {"0x8000000000000001", 16, 0x8000000000000001ULL}, + {"0xfffffffffffffffe", 16, 0xfffffffffffffffeULL}, + {"0xffffffffffffffff", 16, 0xffffffffffffffffULL}, + + {"0\n", 0, 0ULL}, + }; + TEST_OK(kstrtoull, unsigned long long, "%llu", test_ull_ok); +} + +static void __init test_kstrtoull_fail(void) +{ + static DEFINE_TEST_FAIL(test_ull_fail) = { + {"", 0}, + {"", 8}, + {"", 10}, + {"", 16}, + {"\n", 0}, + {"\n", 8}, + {"\n", 10}, + {"\n", 16}, + {"\n0", 0}, + {"\n0", 8}, + {"\n0", 10}, + {"\n0", 16}, + {"+", 0}, + {"+", 8}, + {"+", 10}, + {"+", 16}, + {"-", 0}, + {"-", 8}, + {"-", 10}, + {"-", 16}, + {"0x", 0}, + {"0x", 16}, + {"0X", 0}, + {"0X", 16}, + {"0 ", 0}, + {"1+", 0}, + {"1-", 0}, + {" 2", 0}, + /* base autodetection */ + {"0x0z", 0}, + {"0z", 0}, + {"a", 0}, + /* digit >= base */ + {"2", 2}, + {"8", 8}, + {"a", 10}, + {"A", 10}, + {"g", 16}, + {"G", 16}, + /* overflow */ + {"10000000000000000000000000000000000000000000000000000000000000000", 2}, + {"2000000000000000000000", 8}, + {"18446744073709551616", 10}, + {"10000000000000000", 16}, + /* negative */ + {"-0", 0}, + {"-0", 8}, + {"-0", 10}, + {"-0", 16}, + {"-1", 0}, + {"-1", 8}, + {"-1", 10}, + {"-1", 16}, + /* sign is first character if any */ + {"-+1", 0}, + {"-+1", 8}, + {"-+1", 10}, + {"-+1", 16}, + /* nothing after \n */ + {"0\n0", 0}, + {"0\n0", 8}, + {"0\n0", 10}, + {"0\n0", 16}, + {"0\n+", 0}, + {"0\n+", 8}, + {"0\n+", 10}, + {"0\n+", 16}, + {"0\n-", 0}, + {"0\n-", 8}, + {"0\n-", 10}, + {"0\n-", 16}, + {"0\n ", 0}, + {"0\n ", 8}, + {"0\n ", 10}, + {"0\n ", 16}, + }; + TEST_FAIL(kstrtoull, unsigned long long, "%llu", test_ull_fail); +} + +static void __init test_kstrtoll_ok(void) +{ + DECLARE_TEST_OK(long long, struct test_ll); + static DEFINE_TEST_OK(struct test_ll, test_ll_ok) = { + {"0", 10, 0LL}, + {"1", 10, 1LL}, + {"127", 10, 127LL}, + {"128", 10, 128LL}, + {"129", 10, 129LL}, + {"255", 10, 255LL}, + {"256", 10, 256LL}, + {"257", 10, 257LL}, + {"32767", 10, 32767LL}, + {"32768", 10, 32768LL}, + {"32769", 10, 32769LL}, + {"65535", 10, 65535LL}, + {"65536", 10, 65536LL}, + {"65537", 10, 65537LL}, + {"2147483647", 10, 2147483647LL}, + {"2147483648", 10, 2147483648LL}, + {"2147483649", 10, 2147483649LL}, + {"4294967295", 10, 4294967295LL}, + {"4294967296", 10, 4294967296LL}, + {"4294967297", 10, 4294967297LL}, + {"9223372036854775807", 10, 9223372036854775807LL}, + + {"-1", 10, -1LL}, + {"-2", 10, -2LL}, + {"-9223372036854775808", 10, LLONG_MIN}, + }; + TEST_OK(kstrtoll, long long, "%lld", test_ll_ok); +} + +static void __init test_kstrtoll_fail(void) +{ + static DEFINE_TEST_FAIL(test_ll_fail) = { + {"9223372036854775808", 10}, + {"9223372036854775809", 10}, + {"18446744073709551614", 10}, + {"18446744073709551615", 10}, + {"-9223372036854775809", 10}, + {"-18446744073709551614", 10}, + {"-18446744073709551615", 10}, + /* negative zero isn't an integer in Linux */ + {"-0", 0}, + {"-0", 8}, + {"-0", 10}, + {"-0", 16}, + /* sign is first character if any */ + {"-+1", 0}, + {"-+1", 8}, + {"-+1", 10}, + {"-+1", 16}, + }; + TEST_FAIL(kstrtoll, long long, "%lld", test_ll_fail); +} + +static void __init test_kstrtou64_ok(void) +{ + DECLARE_TEST_OK(u64, struct test_u64); + static DEFINE_TEST_OK(struct test_u64, test_u64_ok) = { + {"0", 10, 0}, + {"1", 10, 1}, + {"126", 10, 126}, + {"127", 10, 127}, + {"128", 10, 128}, + {"129", 10, 129}, + {"254", 10, 254}, + {"255", 10, 255}, + {"256", 10, 256}, + {"257", 10, 257}, + {"32766", 10, 32766}, + {"32767", 10, 32767}, + {"32768", 10, 32768}, + {"32769", 10, 32769}, + {"65534", 10, 65534}, + {"65535", 10, 65535}, + {"65536", 10, 65536}, + {"65537", 10, 65537}, + {"2147483646", 10, 2147483646}, + {"2147483647", 10, 2147483647}, + {"2147483648", 10, 2147483648}, + {"2147483649", 10, 2147483649}, + {"4294967294", 10, 4294967294}, + {"4294967295", 10, 4294967295}, + {"4294967296", 10, 4294967296}, + {"4294967297", 10, 4294967297}, + {"9223372036854775806", 10, 9223372036854775806ULL}, + {"9223372036854775807", 10, 9223372036854775807ULL}, + {"9223372036854775808", 10, 9223372036854775808ULL}, + {"9223372036854775809", 10, 9223372036854775809ULL}, + {"18446744073709551614", 10, 18446744073709551614ULL}, + {"18446744073709551615", 10, 18446744073709551615ULL}, + }; + TEST_OK(kstrtou64, u64, "%llu", test_u64_ok); +} + +static void __init test_kstrtou64_fail(void) +{ + static DEFINE_TEST_FAIL(test_u64_fail) = { + {"-2", 10}, + {"-1", 10}, + {"18446744073709551616", 10}, + {"18446744073709551617", 10}, + }; + TEST_FAIL(kstrtou64, u64, "%llu", test_u64_fail); +} + +static void __init test_kstrtos64_ok(void) +{ + DECLARE_TEST_OK(s64, struct test_s64); + static DEFINE_TEST_OK(struct test_s64, test_s64_ok) = { + {"-128", 10, -128}, + {"-127", 10, -127}, + {"-1", 10, -1}, + {"0", 10, 0}, + {"1", 10, 1}, + {"126", 10, 126}, + {"127", 10, 127}, + {"128", 10, 128}, + {"129", 10, 129}, + {"254", 10, 254}, + {"255", 10, 255}, + {"256", 10, 256}, + {"257", 10, 257}, + {"32766", 10, 32766}, + {"32767", 10, 32767}, + {"32768", 10, 32768}, + {"32769", 10, 32769}, + {"65534", 10, 65534}, + {"65535", 10, 65535}, + {"65536", 10, 65536}, + {"65537", 10, 65537}, + {"2147483646", 10, 2147483646}, + {"2147483647", 10, 2147483647}, + {"2147483648", 10, 2147483648}, + {"2147483649", 10, 2147483649}, + {"4294967294", 10, 4294967294}, + {"4294967295", 10, 4294967295}, + {"4294967296", 10, 4294967296}, + {"4294967297", 10, 4294967297}, + {"9223372036854775806", 10, 9223372036854775806LL}, + {"9223372036854775807", 10, 9223372036854775807LL}, + }; + TEST_OK(kstrtos64, s64, "%lld", test_s64_ok); +} + +static void __init test_kstrtos64_fail(void) +{ + static DEFINE_TEST_FAIL(test_s64_fail) = { + {"9223372036854775808", 10}, + {"9223372036854775809", 10}, + {"18446744073709551614", 10}, + {"18446744073709551615", 10}, + {"18446744073709551616", 10}, + {"18446744073709551617", 10}, + }; + TEST_FAIL(kstrtos64, s64, "%lld", test_s64_fail); +} + +static void __init test_kstrtou32_ok(void) +{ + DECLARE_TEST_OK(u32, struct test_u32); + static DEFINE_TEST_OK(struct test_u32, test_u32_ok) = { + {"0", 10, 0}, + {"1", 10, 1}, + {"126", 10, 126}, + {"127", 10, 127}, + {"128", 10, 128}, + {"129", 10, 129}, + {"254", 10, 254}, + {"255", 10, 255}, + {"256", 10, 256}, + {"257", 10, 257}, + {"32766", 10, 32766}, + {"32767", 10, 32767}, + {"32768", 10, 32768}, + {"32769", 10, 32769}, + {"65534", 10, 65534}, + {"65535", 10, 65535}, + {"65536", 10, 65536}, + {"65537", 10, 65537}, + {"2147483646", 10, 2147483646}, + {"2147483647", 10, 2147483647}, + {"2147483648", 10, 2147483648}, + {"2147483649", 10, 2147483649}, + {"4294967294", 10, 4294967294}, + {"4294967295", 10, 4294967295}, + }; + TEST_OK(kstrtou32, u32, "%u", test_u32_ok); +} + +static void __init test_kstrtou32_fail(void) +{ + static DEFINE_TEST_FAIL(test_u32_fail) = { + {"-2", 10}, + {"-1", 10}, + {"4294967296", 10}, + {"4294967297", 10}, + {"9223372036854775806", 10}, + {"9223372036854775807", 10}, + {"9223372036854775808", 10}, + {"9223372036854775809", 10}, + {"18446744073709551614", 10}, + {"18446744073709551615", 10}, + {"18446744073709551616", 10}, + {"18446744073709551617", 10}, + }; + TEST_FAIL(kstrtou32, u32, "%u", test_u32_fail); +} + +static void __init test_kstrtos32_ok(void) +{ + DECLARE_TEST_OK(s32, struct test_s32); + static DEFINE_TEST_OK(struct test_s32, test_s32_ok) = { + {"-128", 10, -128}, + {"-127", 10, -127}, + {"-1", 10, -1}, + {"0", 10, 0}, + {"1", 10, 1}, + {"126", 10, 126}, + {"127", 10, 127}, + {"128", 10, 128}, + {"129", 10, 129}, + {"254", 10, 254}, + {"255", 10, 255}, + {"256", 10, 256}, + {"257", 10, 257}, + {"32766", 10, 32766}, + {"32767", 10, 32767}, + {"32768", 10, 32768}, + {"32769", 10, 32769}, + {"65534", 10, 65534}, + {"65535", 10, 65535}, + {"65536", 10, 65536}, + {"65537", 10, 65537}, + {"2147483646", 10, 2147483646}, + {"2147483647", 10, 2147483647}, + }; + TEST_OK(kstrtos32, s32, "%d", test_s32_ok); +} + +static void __init test_kstrtos32_fail(void) +{ + static DEFINE_TEST_FAIL(test_s32_fail) = { + {"2147483648", 10}, + {"2147483649", 10}, + {"4294967294", 10}, + {"4294967295", 10}, + {"4294967296", 10}, + {"4294967297", 10}, + {"9223372036854775806", 10}, + {"9223372036854775807", 10}, + {"9223372036854775808", 10}, + {"9223372036854775809", 10}, + {"18446744073709551614", 10}, + {"18446744073709551615", 10}, + {"18446744073709551616", 10}, + {"18446744073709551617", 10}, + }; + TEST_FAIL(kstrtos32, s32, "%d", test_s32_fail); +} + +static void __init test_kstrtou16_ok(void) +{ + DECLARE_TEST_OK(u16, struct test_u16); + static DEFINE_TEST_OK(struct test_u16, test_u16_ok) = { + {"0", 10, 0}, + {"1", 10, 1}, + {"126", 10, 126}, + {"127", 10, 127}, + {"128", 10, 128}, + {"129", 10, 129}, + {"254", 10, 254}, + {"255", 10, 255}, + {"256", 10, 256}, + {"257", 10, 257}, + {"32766", 10, 32766}, + {"32767", 10, 32767}, + {"32768", 10, 32768}, + {"32769", 10, 32769}, + {"65534", 10, 65534}, + {"65535", 10, 65535}, + }; + TEST_OK(kstrtou16, u16, "%hu", test_u16_ok); +} + +static void __init test_kstrtou16_fail(void) +{ + static DEFINE_TEST_FAIL(test_u16_fail) = { + {"-2", 10}, + {"-1", 10}, + {"65536", 10}, + {"65537", 10}, + {"2147483646", 10}, + {"2147483647", 10}, + {"2147483648", 10}, + {"2147483649", 10}, + {"4294967294", 10}, + {"4294967295", 10}, + {"4294967296", 10}, + {"4294967297", 10}, + {"9223372036854775806", 10}, + {"9223372036854775807", 10}, + {"9223372036854775808", 10}, + {"9223372036854775809", 10}, + {"18446744073709551614", 10}, + {"18446744073709551615", 10}, + {"18446744073709551616", 10}, + {"18446744073709551617", 10}, + }; + TEST_FAIL(kstrtou16, u16, "%hu", test_u16_fail); +} + +static void __init test_kstrtos16_ok(void) +{ + DECLARE_TEST_OK(s16, struct test_s16); + static DEFINE_TEST_OK(struct test_s16, test_s16_ok) = { + {"-130", 10, -130}, + {"-129", 10, -129}, + {"-128", 10, -128}, + {"-127", 10, -127}, + {"-1", 10, -1}, + {"0", 10, 0}, + {"1", 10, 1}, + {"126", 10, 126}, + {"127", 10, 127}, + {"128", 10, 128}, + {"129", 10, 129}, + {"254", 10, 254}, + {"255", 10, 255}, + {"256", 10, 256}, + {"257", 10, 257}, + {"32766", 10, 32766}, + {"32767", 10, 32767}, + }; + TEST_OK(kstrtos16, s16, "%hd", test_s16_ok); +} + +static void __init test_kstrtos16_fail(void) +{ + static DEFINE_TEST_FAIL(test_s16_fail) = { + {"32768", 10}, + {"32769", 10}, + {"65534", 10}, + {"65535", 10}, + {"65536", 10}, + {"65537", 10}, + {"2147483646", 10}, + {"2147483647", 10}, + {"2147483648", 10}, + {"2147483649", 10}, + {"4294967294", 10}, + {"4294967295", 10}, + {"4294967296", 10}, + {"4294967297", 10}, + {"9223372036854775806", 10}, + {"9223372036854775807", 10}, + {"9223372036854775808", 10}, + {"9223372036854775809", 10}, + {"18446744073709551614", 10}, + {"18446744073709551615", 10}, + {"18446744073709551616", 10}, + {"18446744073709551617", 10}, + }; + TEST_FAIL(kstrtos16, s16, "%hd", test_s16_fail); +} + +static void __init test_kstrtou8_ok(void) +{ + DECLARE_TEST_OK(u8, struct test_u8); + static DEFINE_TEST_OK(struct test_u8, test_u8_ok) = { + {"0", 10, 0}, + {"1", 10, 1}, + {"126", 10, 126}, + {"127", 10, 127}, + {"128", 10, 128}, + {"129", 10, 129}, + {"254", 10, 254}, + {"255", 10, 255}, + }; + TEST_OK(kstrtou8, u8, "%hhu", test_u8_ok); +} + +static void __init test_kstrtou8_fail(void) +{ + static DEFINE_TEST_FAIL(test_u8_fail) = { + {"-2", 10}, + {"-1", 10}, + {"256", 10}, + {"257", 10}, + {"32766", 10}, + {"32767", 10}, + {"32768", 10}, + {"32769", 10}, + {"65534", 10}, + {"65535", 10}, + {"65536", 10}, + {"65537", 10}, + {"2147483646", 10}, + {"2147483647", 10}, + {"2147483648", 10}, + {"2147483649", 10}, + {"4294967294", 10}, + {"4294967295", 10}, + {"4294967296", 10}, + {"4294967297", 10}, + {"9223372036854775806", 10}, + {"9223372036854775807", 10}, + {"9223372036854775808", 10}, + {"9223372036854775809", 10}, + {"18446744073709551614", 10}, + {"18446744073709551615", 10}, + {"18446744073709551616", 10}, + {"18446744073709551617", 10}, + }; + TEST_FAIL(kstrtou8, u8, "%hhu", test_u8_fail); +} + +static void __init test_kstrtos8_ok(void) +{ + DECLARE_TEST_OK(s8, struct test_s8); + static DEFINE_TEST_OK(struct test_s8, test_s8_ok) = { + {"-128", 10, -128}, + {"-127", 10, -127}, + {"-1", 10, -1}, + {"0", 10, 0}, + {"1", 10, 1}, + {"126", 10, 126}, + {"127", 10, 127}, + }; + TEST_OK(kstrtos8, s8, "%hhd", test_s8_ok); +} + +static void __init test_kstrtos8_fail(void) +{ + static DEFINE_TEST_FAIL(test_s8_fail) = { + {"-130", 10}, + {"-129", 10}, + {"128", 10}, + {"129", 10}, + {"254", 10}, + {"255", 10}, + {"256", 10}, + {"257", 10}, + {"32766", 10}, + {"32767", 10}, + {"32768", 10}, + {"32769", 10}, + {"65534", 10}, + {"65535", 10}, + {"65536", 10}, + {"65537", 10}, + {"2147483646", 10}, + {"2147483647", 10}, + {"2147483648", 10}, + {"2147483649", 10}, + {"4294967294", 10}, + {"4294967295", 10}, + {"4294967296", 10}, + {"4294967297", 10}, + {"9223372036854775806", 10}, + {"9223372036854775807", 10}, + {"9223372036854775808", 10}, + {"9223372036854775809", 10}, + {"18446744073709551614", 10}, + {"18446744073709551615", 10}, + {"18446744073709551616", 10}, + {"18446744073709551617", 10}, + }; + TEST_FAIL(kstrtos8, s8, "%hhd", test_s8_fail); +} + +static int __init test_kstrtox_init(void) +{ + test_kstrtoull_ok(); + test_kstrtoull_fail(); + test_kstrtoll_ok(); + test_kstrtoll_fail(); + + test_kstrtou64_ok(); + test_kstrtou64_fail(); + test_kstrtos64_ok(); + test_kstrtos64_fail(); + + test_kstrtou32_ok(); + test_kstrtou32_fail(); + test_kstrtos32_ok(); + test_kstrtos32_fail(); + + test_kstrtou16_ok(); + test_kstrtou16_fail(); + test_kstrtos16_ok(); + test_kstrtos16_fail(); + + test_kstrtou8_ok(); + test_kstrtou8_fail(); + test_kstrtos8_ok(); + test_kstrtos8_fail(); + return -EINVAL; +} +module_init(test_kstrtox_init); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/lib/vsprintf.c b/lib/vsprintf.c index d3023df8477f6..f3fd99a6ad628 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -120,147 +120,6 @@ long long simple_strtoll(const char *cp, char **endp, unsigned int base) } EXPORT_SYMBOL(simple_strtoll); -/** - * strict_strtoul - convert a string to an unsigned long strictly - * @cp: The string to be converted - * @base: The number base to use - * @res: The converted result value - * - * strict_strtoul converts a string to an unsigned long only if the - * string is really an unsigned long string, any string containing - * any invalid char at the tail will be rejected and -EINVAL is returned, - * only a newline char at the tail is acceptible because people generally - * change a module parameter in the following way: - * - * echo 1024 > /sys/module/e1000/parameters/copybreak - * - * echo will append a newline to the tail. - * - * It returns 0 if conversion is successful and *res is set to the converted - * value, otherwise it returns -EINVAL and *res is set to 0. - * - * simple_strtoul just ignores the successive invalid characters and - * return the converted value of prefix part of the string. - */ -int strict_strtoul(const char *cp, unsigned int base, unsigned long *res) -{ - char *tail; - unsigned long val; - - *res = 0; - if (!*cp) - return -EINVAL; - - val = simple_strtoul(cp, &tail, base); - if (tail == cp) - return -EINVAL; - - if ((tail[0] == '\0') || (tail[0] == '\n' && tail[1] == '\0')) { - *res = val; - return 0; - } - - return -EINVAL; -} -EXPORT_SYMBOL(strict_strtoul); - -/** - * strict_strtol - convert a string to a long strictly - * @cp: The string to be converted - * @base: The number base to use - * @res: The converted result value - * - * strict_strtol is similiar to strict_strtoul, but it allows the first - * character of a string is '-'. - * - * It returns 0 if conversion is successful and *res is set to the converted - * value, otherwise it returns -EINVAL and *res is set to 0. - */ -int strict_strtol(const char *cp, unsigned int base, long *res) -{ - int ret; - if (*cp == '-') { - ret = strict_strtoul(cp + 1, base, (unsigned long *)res); - if (!ret) - *res = -(*res); - } else { - ret = strict_strtoul(cp, base, (unsigned long *)res); - } - - return ret; -} -EXPORT_SYMBOL(strict_strtol); - -/** - * strict_strtoull - convert a string to an unsigned long long strictly - * @cp: The string to be converted - * @base: The number base to use - * @res: The converted result value - * - * strict_strtoull converts a string to an unsigned long long only if the - * string is really an unsigned long long string, any string containing - * any invalid char at the tail will be rejected and -EINVAL is returned, - * only a newline char at the tail is acceptible because people generally - * change a module parameter in the following way: - * - * echo 1024 > /sys/module/e1000/parameters/copybreak - * - * echo will append a newline to the tail of the string. - * - * It returns 0 if conversion is successful and *res is set to the converted - * value, otherwise it returns -EINVAL and *res is set to 0. - * - * simple_strtoull just ignores the successive invalid characters and - * return the converted value of prefix part of the string. - */ -int strict_strtoull(const char *cp, unsigned int base, unsigned long long *res) -{ - char *tail; - unsigned long long val; - - *res = 0; - if (!*cp) - return -EINVAL; - - val = simple_strtoull(cp, &tail, base); - if (tail == cp) - return -EINVAL; - if ((tail[0] == '\0') || (tail[0] == '\n' && tail[1] == '\0')) { - *res = val; - return 0; - } - - return -EINVAL; -} -EXPORT_SYMBOL(strict_strtoull); - -/** - * strict_strtoll - convert a string to a long long strictly - * @cp: The string to be converted - * @base: The number base to use - * @res: The converted result value - * - * strict_strtoll is similiar to strict_strtoull, but it allows the first - * character of a string is '-'. - * - * It returns 0 if conversion is successful and *res is set to the converted - * value, otherwise it returns -EINVAL and *res is set to 0. - */ -int strict_strtoll(const char *cp, unsigned int base, long long *res) -{ - int ret; - if (*cp == '-') { - ret = strict_strtoull(cp + 1, base, (unsigned long long *)res); - if (!ret) - *res = -(*res); - } else { - ret = strict_strtoull(cp, base, (unsigned long long *)res); - } - - return ret; -} -EXPORT_SYMBOL(strict_strtoll); - static noinline_for_stack int skip_atoi(const char **s) { diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 4c0383da1c9a2..58434b346b0ff 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2809,9 +2809,9 @@ sub process { WARN("consider using a completion\n" . $herecurr); } -# recommend strict_strto* over simple_strto* +# recommend kstrto* over simple_strto* if ($line =~ /\bsimple_(strto.*?)\s*\(/) { - WARN("consider using strict_$1 in preference to simple_$1\n" . $herecurr); + WARN("consider using kstrto* in preference to simple_$1\n" . $herecurr); } # check for __initcall(), use device_initcall() explicitly please if ($line =~ /^.\s*__initcall\s*\(/) { From bb6bb11280ba6a424de78ba6c9388cbc95bfc572 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 14 Apr 2011 15:22:21 -0700 Subject: [PATCH 1376/2556] mm/thp: use conventional format for boolean attributes commit e27e6151b154ff6e5e8162efa291bc60196d29ea upstream. The conventional format for boolean attributes in sysfs is numeric ("0" or "1" followed by new-line). Any boolean attribute can then be read and written using a generic function. Using the strings "yes [no]", "[yes] no" (read), "yes" and "no" (write) will frustrate this. [akpm@linux-foundation.org: use kstrtoul()] [akpm@linux-foundation.org: test_bit() doesn't return 1/0, per Neil] Signed-off-by: Ben Hutchings Cc: Andrea Arcangeli Cc: Mel Gorman Cc: Johannes Weiner Cc: Rik van Riel Cc: Hugh Dickins Tested-by: David Rientjes Cc: NeilBrown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/huge_memory.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 113e35c475020..8f76561944178 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -244,24 +244,28 @@ static ssize_t single_flag_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf, enum transparent_hugepage_flag flag) { - if (test_bit(flag, &transparent_hugepage_flags)) - return sprintf(buf, "[yes] no\n"); - else - return sprintf(buf, "yes [no]\n"); + return sprintf(buf, "%d\n", + !!test_bit(flag, &transparent_hugepage_flags)); } + static ssize_t single_flag_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count, enum transparent_hugepage_flag flag) { - if (!memcmp("yes", buf, - min(sizeof("yes")-1, count))) { + unsigned long value; + int ret; + + ret = kstrtoul(buf, 10, &value); + if (ret < 0) + return ret; + if (value > 1) + return -EINVAL; + + if (value) set_bit(flag, &transparent_hugepage_flags); - } else if (!memcmp("no", buf, - min(sizeof("no")-1, count))) { + else clear_bit(flag, &transparent_hugepage_flags); - } else - return -EINVAL; return count; } From 4d4adc6ef5f0a4457d91d1e039ba617bfc8779d0 Mon Sep 17 00:00:00 2001 From: Bob Liu Date: Thu, 14 Apr 2011 15:22:20 -0700 Subject: [PATCH 1377/2556] ramfs: fix memleak on no-mmu arch commit b836aec53e2bce71de1d5415313380688c851477 upstream. On no-mmu arch, there is a memleak during shmem test. The cause of this memleak is ramfs_nommu_expand_for_mapping() added page refcount to 2 which makes iput() can't free that pages. The simple test file is like this: int main(void) { int i; key_t k = ftok("/etc", 42); for ( i=0; i<100; ++i) { int id = shmget(k, 10000, 0644|IPC_CREAT); if (id == -1) { printf("shmget error\n"); } if(shmctl(id, IPC_RMID, NULL ) == -1) { printf("shm rm error\n"); return -1; } } printf("run ok...\n"); return 0; } And the result: root:/> free total used free shared buffers Mem: 60320 17912 42408 0 0 -/+ buffers: 17912 42408 root:/> shmem run ok... root:/> free total used free shared buffers Mem: 60320 19096 41224 0 0 -/+ buffers: 19096 41224 root:/> shmem run ok... root:/> free total used free shared buffers Mem: 60320 20296 40024 0 0 -/+ buffers: 20296 40024 ... After this patch the test result is:(no memleak anymore) root:/> free total used free shared buffers Mem: 60320 16668 43652 0 0 -/+ buffers: 16668 43652 root:/> shmem run ok... root:/> free total used free shared buffers Mem: 60320 16668 43652 0 0 -/+ buffers: 16668 43652 Signed-off-by: Bob Liu Acked-by: Hugh Dickins Signed-off-by: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/ramfs/file-nommu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c index 9eead2c796b7f..fbb0b478a346f 100644 --- a/fs/ramfs/file-nommu.c +++ b/fs/ramfs/file-nommu.c @@ -112,6 +112,7 @@ int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize) SetPageDirty(page); unlock_page(page); + put_page(page); } return 0; From ad9ad5d4e3ffc59da2c0e2a225978b513b2beb2d Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Thu, 14 Apr 2011 15:22:13 -0700 Subject: [PATCH 1378/2556] oom-kill: remove boost_dying_task_prio() commit 341aea2bc48bf652777fb015cc2b3dfa9a451817 upstream. This is an almost-revert of commit 93b43fa ("oom: give the dying task a higher priority"). That commit dramatically improved oom killer logic when a fork-bomb occurs. But I've found that it has nasty corner case. Now cpu cgroup has strange default RT runtime. It's 0! That said, if a process under cpu cgroup promote RT scheduling class, the process never run at all. If an admin inserts a !RT process into a cpu cgroup by setting rtruntime=0, usually it runs perfectly because a !RT task isn't affected by the rtruntime knob. But if it promotes an RT task via an explicit setscheduler() syscall or an OOM, the task can't run at all. In short, the oom killer doesn't work at all if admins are using cpu cgroup and don't touch the rtruntime knob. Eventually, kernel may hang up when oom kill occur. I and the original author Luis agreed to disable this logic. Signed-off-by: KOSAKI Motohiro Acked-by: Luis Claudio R. Goncalves Acked-by: KAMEZAWA Hiroyuki Reviewed-by: Minchan Kim Acked-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/oom_kill.c | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/mm/oom_kill.c b/mm/oom_kill.c index 33b58615072cc..ea16f720705ed 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -83,24 +83,6 @@ static bool has_intersects_mems_allowed(struct task_struct *tsk, } #endif /* CONFIG_NUMA */ -/* - * If this is a system OOM (not a memcg OOM) and the task selected to be - * killed is not already running at high (RT) priorities, speed up the - * recovery by boosting the dying task to the lowest FIFO priority. - * That helps with the recovery and avoids interfering with RT tasks. - */ -static void boost_dying_task_prio(struct task_struct *p, - struct mem_cgroup *mem) -{ - struct sched_param param = { .sched_priority = 1 }; - - if (mem) - return; - - if (!rt_task(p)) - sched_setscheduler_nocheck(p, SCHED_FIFO, ¶m); -} - /* * The process p may have detached its own ->mm while exiting or through * use_mm(), but one or more of its subthreads may still have a valid @@ -452,13 +434,6 @@ static int oom_kill_task(struct task_struct *p, struct mem_cgroup *mem) set_tsk_thread_flag(p, TIF_MEMDIE); force_sig(SIGKILL, p); - /* - * We give our sacrificial lamb high priority and access to - * all the memory it needs. That way it should be able to - * exit() and clear out its resources quickly... - */ - boost_dying_task_prio(p, mem); - return 0; } #undef K @@ -482,7 +457,6 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, */ if (p->flags & PF_EXITING) { set_tsk_thread_flag(p, TIF_MEMDIE); - boost_dying_task_prio(p, mem); return 0; } @@ -701,7 +675,6 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, */ if (fatal_signal_pending(current)) { set_thread_flag(TIF_MEMDIE); - boost_dying_task_prio(current, NULL); return; } From cda24233fa38d46fc7d32dbd1d8e782e03768bb6 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 14 Apr 2011 15:22:07 -0700 Subject: [PATCH 1379/2556] MAINTAINERS: update STABLE BRANCH info commit d00ebeac5f24f290636f7a895dafc124b2930a08 upstream. Drop Chris Wright from STABLE maintainers. He hasn't done STABLE release work for quite some time. Signed-off-by: Randy Dunlap Acked-by: Chris Wright Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index f1bc3dc6b3699..80540e33f1ec3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5944,7 +5944,6 @@ F: arch/alpha/kernel/srm_env.c STABLE BRANCH M: Greg Kroah-Hartman -M: Chris Wright L: stable@kernel.org S: Maintained From ee6d9264c13bb4c88a52c39f5ea3f37ec7be6f8e Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 13 Apr 2011 10:31:52 +0300 Subject: [PATCH 1380/2556] UBIFS: fix oops when R/O file-system is fsync'ed commit 78530bf7f2559b317c04991b52217c1608d5a58d upstream. This patch fixes severe UBIFS bug: UBIFS oopses when we 'fsync()' an file on R/O-mounter file-system. We (the UBIFS authors) incorrectly thought that VFS would not propagate 'fsync()' down to the file-system if it is read-only, but this is not the case. It is easy to exploit this bug using the following simple perl script: use strict; use File::Sync qw(fsync sync); die "File path is not specified" if not defined $ARGV[0]; my $path = $ARGV[0]; open FILE, "<", "$path" or die "Cannot open $path: $!"; fsync(\*FILE) or die "cannot fsync $path: $!"; close FILE or die "Cannot close $path: $!"; Thanks to Reuben Dowle for reporting about this issue. Signed-off-by: Artem Bityutskiy Reported-by: Reuben Dowle Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/file.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index d77db7e36484e..fe14f4df4ca0b 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -1309,6 +1309,9 @@ int ubifs_fsync(struct file *file, int datasync) dbg_gen("syncing inode %lu", inode->i_ino); + if (inode->i_sb->s_flags & MS_RDONLY) + return 0; + /* * VFS has already synchronized dirty pages for this inode. Synchronize * the inode unless this is a 'datasync()' call. From 12391b1867f59f0cf7d0898bf9231db187a67b42 Mon Sep 17 00:00:00 2001 From: Boris Ostrovsky Date: Tue, 15 Mar 2011 12:13:44 -0400 Subject: [PATCH 1381/2556] x86, AMD: Set ARAT feature on AMD processors commit b87cf80af3ba4b4c008b4face3c68d604e1715c6 upstream. Support for Always Running APIC timer (ARAT) was introduced in commit db954b5898dd3ef3ef93f4144158ea8f97deb058. This feature allows us to avoid switching timers from LAPIC to something else (e.g. HPET) and go into timer broadcasts when entering deep C-states. AMD processors don't provide a CPUID bit for that feature but they also keep APIC timers running in deep C-states (except for cases when the processor is affected by erratum 400). Therefore we should set ARAT feature bit on AMD CPUs. Tested-by: Borislav Petkov Acked-by: Andreas Herrmann Acked-by: Mark Langsdorf Acked-by: Thomas Gleixner Signed-off-by: Boris Ostrovsky LKML-Reference: <1300205624-4813-1-git-send-email-ostr@amd64.org> Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/amd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 7c7bedb83c5a4..48eaa1b6fc46d 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -594,6 +594,10 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c) } } #endif + + /* As a rule processors have APIC timer running in deep C states */ + if (c->x86 >= 0xf && !cpu_has_amd_erratum(amd_erratum_400)) + set_cpu_cap(c, X86_FEATURE_ARAT); } #ifdef CONFIG_X86_32 From cd1e974b08622d7a328d1effb22713704193c522 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Fri, 15 Apr 2011 14:47:40 +0200 Subject: [PATCH 1382/2556] x86, amd: Disable GartTlbWlkErr when BIOS forgets it commit 5bbc097d890409d8eff4e3f1d26f11a9d6b7c07e upstream. This patch disables GartTlbWlk errors on AMD Fam10h CPUs if the BIOS forgets to do is (or is just too old). Letting these errors enabled can cause a sync-flood on the CPU causing a reboot. The AMD BKDG recommends disabling GART TLB Wlk Error completely. This patch is the fix for https://bugzilla.kernel.org/show_bug.cgi?id=33012 on my machine. Signed-off-by: Joerg Roedel Link: http://lkml.kernel.org/r/20110415131152.GJ18463@8bytes.org Tested-by: Alexandre Demers Signed-off-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/msr-index.h | 4 ++++ arch/x86/kernel/cpu/amd.c | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 43a18c77676d9..99b402c6a9158 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -92,11 +92,15 @@ #define MSR_IA32_MC0_ADDR 0x00000402 #define MSR_IA32_MC0_MISC 0x00000403 +#define MSR_AMD64_MC0_MASK 0xc0010044 + #define MSR_IA32_MCx_CTL(x) (MSR_IA32_MC0_CTL + 4*(x)) #define MSR_IA32_MCx_STATUS(x) (MSR_IA32_MC0_STATUS + 4*(x)) #define MSR_IA32_MCx_ADDR(x) (MSR_IA32_MC0_ADDR + 4*(x)) #define MSR_IA32_MCx_MISC(x) (MSR_IA32_MC0_MISC + 4*(x)) +#define MSR_AMD64_MCx_MASK(x) (MSR_AMD64_MC0_MASK + (x)) + /* These are consecutive and not in the normal 4er MCE bank block */ #define MSR_IA32_MC0_CTL2 0x00000280 #define MSR_IA32_MCx_CTL2(x) (MSR_IA32_MC0_CTL2 + (x)) diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 48eaa1b6fc46d..a2b9c7d413230 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -598,6 +598,25 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c) /* As a rule processors have APIC timer running in deep C states */ if (c->x86 >= 0xf && !cpu_has_amd_erratum(amd_erratum_400)) set_cpu_cap(c, X86_FEATURE_ARAT); + + /* + * Disable GART TLB Walk Errors on Fam10h. We do this here + * because this is always needed when GART is enabled, even in a + * kernel which has no MCE support built in. + */ + if (c->x86 == 0x10) { + /* + * BIOS should disable GartTlbWlk Errors themself. If + * it doesn't do it here as suggested by the BKDG. + * + * Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=33012 + */ + u64 mask; + + rdmsrl(MSR_AMD64_MCx_MASK(4), mask); + mask |= (1 << 10); + wrmsrl(MSR_AMD64_MCx_MASK(4), mask); + } } #ifdef CONFIG_X86_32 From 9da573a886cef8095b49baae25671d8336e2e961 Mon Sep 17 00:00:00 2001 From: Tim Chen Date: Fri, 15 Apr 2011 11:39:29 -0700 Subject: [PATCH 1383/2556] vfs: Fix absolute RCU path walk failures due to uninitialized seq number commit c1530019e311c91d14b24d8e74d233152d806e45 upstream. During RCU walk in path_lookupat and path_openat, the rcu lookup frequently failed if looking up an absolute path, because when root directory was looked up, seq number was not properly set in nameidata. We dropped out of RCU walk in nameidata_drop_rcu due to mismatch in directory entry's seq number. We reverted to slow path walk that need to take references. With the following patch, I saw a 50% increase in an exim mail server benchmark throughput on a 4-socket Nehalem-EX system. Signed-off-by: Tim Chen Reviewed-by: Andi Kleen Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/namei.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/namei.c b/fs/namei.c index a4689eb2df285..3095ca8a31adf 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -712,6 +712,7 @@ static __always_inline void set_root_rcu(struct nameidata *nd) do { seq = read_seqcount_begin(&fs->seq); nd->root = fs->root; + nd->seq = __read_seqcount_begin(&nd->root.dentry->d_seq); } while (read_seqcount_retry(&fs->seq, seq)); } } From 93f3fccbe5d08dcdc9ba6f549195c0fa4aa6dbf7 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 5 Apr 2011 13:57:53 +0100 Subject: [PATCH 1384/2556] ARM: 6864/1: hw_breakpoint: clear DBGVCR out of reset commit e89c0d7090c54d7b11b9b091e495a1ae345dd3ff upstream. The DBGVCR, used for configuring vector catch debug events, is UNKNOWN out of reset on ARMv7. When enabling monitor mode, this must be zeroed to avoid UNPREDICTABLE behaviour. This patch adds the zeroing code to the debug reset path. Reported-by: Stepan Moskovchenko Signed-off-by: Will Deacon Signed-off-by: Russell King Signed-off-by: Greg Kroah-Hartman --- arch/arm/kernel/hw_breakpoint.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c index 44b84fe6e1b0f..7e9a0c7f19889 100644 --- a/arch/arm/kernel/hw_breakpoint.c +++ b/arch/arm/kernel/hw_breakpoint.c @@ -868,6 +868,13 @@ static void reset_ctrl_regs(void *info) */ asm volatile("mcr p14, 0, %0, c1, c0, 4" : : "r" (0)); isb(); + + /* + * Clear any configured vector-catch events before + * enabling monitor mode. + */ + asm volatile("mcr p14, 0, %0, c0, c7, 0" : : "r" (0)); + isb(); } if (enable_monitor_mode()) From 89a2b4bb5c9f531ff098c49001dc0f77cada8425 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Sun, 17 Apr 2011 10:20:19 +0200 Subject: [PATCH 1385/2556] i2c-algo-bit: Call pre/post_xfer for bit_test commit d3b3e15da14ded61c9654db05863b04a2435f4cc upstream. Apparently some distros set i2c-algo-bit.bit_test to 1 by default. In some cases this causes i2c_bit_add_bus to fail and prevents the i2c bus from being added. In the radeon case, we fail to add the ddc i2c buses which prevents the driver from being able to detect attached monitors. The i2c bus works fine even if bit_test fails. This is likely due to gpio switching that is required and handled in the pre/post_xfer hooks, so call the pre/post_xfer hooks in the bit test as well. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=36221 Signed-off-by: Alex Deucher Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/algos/i2c-algo-bit.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c index 38319a69bd0a9..d6d58684712bc 100644 --- a/drivers/i2c/algos/i2c-algo-bit.c +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -232,9 +232,17 @@ static int i2c_inb(struct i2c_adapter *i2c_adap) * Sanity check for the adapter hardware - check the reaction of * the bus lines only if it seems to be idle. */ -static int test_bus(struct i2c_algo_bit_data *adap, char *name) +static int test_bus(struct i2c_adapter *i2c_adap) { - int scl, sda; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + const char *name = i2c_adap->name; + int scl, sda, ret; + + if (adap->pre_xfer) { + ret = adap->pre_xfer(i2c_adap); + if (ret < 0) + return -ENODEV; + } if (adap->getscl == NULL) pr_info("%s: Testing SDA only, SCL is not readable\n", name); @@ -297,11 +305,19 @@ static int test_bus(struct i2c_algo_bit_data *adap, char *name) "while pulling SCL high!\n", name); goto bailout; } + + if (adap->post_xfer) + adap->post_xfer(i2c_adap); + pr_info("%s: Test OK\n", name); return 0; bailout: sdahi(adap); sclhi(adap); + + if (adap->post_xfer) + adap->post_xfer(i2c_adap); + return -ENODEV; } @@ -607,7 +623,7 @@ static int __i2c_bit_add_bus(struct i2c_adapter *adap, int ret; if (bit_test) { - ret = test_bus(bit_adap, adap->name); + ret = test_bus(adap); if (ret < 0) return -ENODEV; } From ad746aa9917e776add866d3fe6f6218a29aa4805 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 18 Mar 2011 04:26:24 -0400 Subject: [PATCH 1386/2556] RTC: add missing "return 0" in new alarm func for rtc-bfin.c commit 8c122b96866580c99e44f3f07ac93a993d964ec3 upstream. The new bfin_rtc_alarm_irq_enable function forgot to add a "return 0" to the end leading to the build warning: drivers/rtc/rtc-bfin.c: In function 'bfin_rtc_alarm_irq_enable': drivers/rtc/rtc-bfin.c:253: warning: control reaches end of non-void function CC: Thomas Gleixner CC: Alessandro Zummo Signed-off-by: Mike Frysinger Signed-off-by: John Stultz Signed-off-by: Greg Kroah-Hartman --- drivers/rtc/rtc-bfin.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c index 17971d93354d2..0e61e2dad5d37 100644 --- a/drivers/rtc/rtc-bfin.c +++ b/drivers/rtc/rtc-bfin.c @@ -276,6 +276,8 @@ static int bfin_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) bfin_rtc_int_set_alarm(rtc); else bfin_rtc_int_clear(~(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)); + + return 0; } static int bfin_rtc_read_time(struct device *dev, struct rtc_time *tm) From d283552433653776c3f362ffe2d091e908201fbb Mon Sep 17 00:00:00 2001 From: Ken Chen Date: Fri, 8 Apr 2011 12:20:16 -0700 Subject: [PATCH 1387/2556] sched: Fix erroneous all_pinned logic commit b30aef17f71cf9e24b10c11cbb5e5f0ebe8a85ab upstream. The scheduler load balancer has specific code to deal with cases of unbalanced system due to lots of unmovable tasks (for example because of hard CPU affinity). In those situation, it excludes the busiest CPU that has pinned tasks for load balance consideration such that it can perform second 2nd load balance pass on the rest of the system. This all works as designed if there is only one cgroup in the system. However, when we have multiple cgroups, this logic has false positives and triggers multiple load balance passes despite there are actually no pinned tasks at all. The reason it has false positives is that the all pinned logic is deep in the lowest function of can_migrate_task() and is too low level: load_balance_fair() iterates each task group and calls balance_tasks() to migrate target load. Along the way, balance_tasks() will also set a all_pinned variable. Given that task-groups are iterated, this all_pinned variable is essentially the status of last group in the scanning process. Task group can have number of reasons that no load being migrated, none due to cpu affinity. However, this status bit is being propagated back up to the higher level load_balance(), which incorrectly think that no tasks were moved. It kick off the all pinned logic and start multiple passes attempt to move load onto puller CPU. To fix this, move the all_pinned aggregation up at the iterator level. This ensures that the status is aggregated over all task-groups, not just last one in the list. Signed-off-by: Ken Chen Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/BANLkTi=ernzNawaR5tJZEsV_QVnfxqXmsQ@mail.gmail.com Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/sched_fair.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index 8faa29ac949fb..ee0cf650cb423 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c @@ -2047,21 +2047,20 @@ balance_tasks(struct rq *this_rq, int this_cpu, struct rq *busiest, enum cpu_idle_type idle, int *all_pinned, int *this_best_prio, struct cfs_rq *busiest_cfs_rq) { - int loops = 0, pulled = 0, pinned = 0; + int loops = 0, pulled = 0; long rem_load_move = max_load_move; struct task_struct *p, *n; if (max_load_move == 0) goto out; - pinned = 1; - list_for_each_entry_safe(p, n, &busiest_cfs_rq->tasks, se.group_node) { if (loops++ > sysctl_sched_nr_migrate) break; if ((p->se.load.weight >> 1) > rem_load_move || - !can_migrate_task(p, busiest, this_cpu, sd, idle, &pinned)) + !can_migrate_task(p, busiest, this_cpu, sd, idle, + all_pinned)) continue; pull_task(busiest, p, this_rq, this_cpu); @@ -2096,9 +2095,6 @@ balance_tasks(struct rq *this_rq, int this_cpu, struct rq *busiest, */ schedstat_add(sd, lb_gained[idle], pulled); - if (all_pinned) - *all_pinned = pinned; - return max_load_move - rem_load_move; } @@ -3301,6 +3297,7 @@ static int load_balance(int this_cpu, struct rq *this_rq, * still unbalanced. ld_moved simply stays zero, so it is * correctly treated as an imbalance. */ + all_pinned = 1; local_irq_save(flags); double_rq_lock(this_rq, busiest); ld_moved = move_tasks(this_rq, this_cpu, busiest, From 0f00f08f776fdb920da1b1b9e03dfeb7314e8d46 Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Thu, 14 Apr 2011 15:22:12 -0700 Subject: [PATCH 1388/2556] vmscan: all_unreclaimable() use zone->all_unreclaimable as a name commit 929bea7c714220fc76ce3f75bef9056477c28e74 upstream. all_unreclaimable check in direct reclaim has been introduced at 2.6.19 by following commit. 2006 Sep 25; commit 408d8544; oom: use unreclaimable info And it went through strange history. firstly, following commit broke the logic unintentionally. 2008 Apr 29; commit a41f24ea; page allocator: smarter retry of costly-order allocations Two years later, I've found obvious meaningless code fragment and restored original intention by following commit. 2010 Jun 04; commit bb21c7ce; vmscan: fix do_try_to_free_pages() return value when priority==0 But, the logic didn't works when 32bit highmem system goes hibernation and Minchan slightly changed the algorithm and fixed it . 2010 Sep 22: commit d1908362: vmscan: check all_unreclaimable in direct reclaim path But, recently, Andrey Vagin found the new corner case. Look, struct zone { .. int all_unreclaimable; .. unsigned long pages_scanned; .. } zone->all_unreclaimable and zone->pages_scanned are neigher atomic variables nor protected by lock. Therefore zones can become a state of zone->page_scanned=0 and zone->all_unreclaimable=1. In this case, current all_unreclaimable() return false even though zone->all_unreclaimabe=1. This resulted in the kernel hanging up when executing a loop of the form 1. fork 2. mmap 3. touch memory 4. read memory 5. munmmap as described in http://www.gossamer-threads.com/lists/linux/kernel/1348725#1348725 Is this ignorable minor issue? No. Unfortunately, x86 has very small dma zone and it become zone->all_unreclamble=1 easily. and if it become all_unreclaimable=1, it never restore all_unreclaimable=0. Why? if all_unreclaimable=1, vmscan only try DEF_PRIORITY reclaim and a-few-lru-pages>>DEF_PRIORITY always makes 0. that mean no page scan at all! Eventually, oom-killer never works on such systems. That said, we can't use zone->pages_scanned for this purpose. This patch restore all_unreclaimable() use zone->all_unreclaimable as old. and in addition, to add oom_killer_disabled check to avoid reintroduce the issue of commit d1908362 ("vmscan: check all_unreclaimable in direct reclaim path"). Reported-by: Andrey Vagin Signed-off-by: KOSAKI Motohiro Cc: Nick Piggin Reviewed-by: Minchan Kim Reviewed-by: KAMEZAWA Hiroyuki Acked-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/vmscan.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 3b4a41d724894..06655208884c9 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -1988,17 +1989,12 @@ static bool zone_reclaimable(struct zone *zone) return zone->pages_scanned < zone_reclaimable_pages(zone) * 6; } -/* - * As hibernation is going on, kswapd is freezed so that it can't mark - * the zone into all_unreclaimable. It can't handle OOM during hibernation. - * So let's check zone's unreclaimable in direct reclaim as well as kswapd. - */ +/* All zones in zonelist are unreclaimable? */ static bool all_unreclaimable(struct zonelist *zonelist, struct scan_control *sc) { struct zoneref *z; struct zone *zone; - bool all_unreclaimable = true; for_each_zone_zonelist_nodemask(zone, z, zonelist, gfp_zone(sc->gfp_mask), sc->nodemask) { @@ -2006,13 +2002,11 @@ static bool all_unreclaimable(struct zonelist *zonelist, continue; if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL)) continue; - if (zone_reclaimable(zone)) { - all_unreclaimable = false; - break; - } + if (!zone->all_unreclaimable) + return false; } - return all_unreclaimable; + return true; } /* @@ -2108,6 +2102,14 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist, if (sc->nr_reclaimed) return sc->nr_reclaimed; + /* + * As hibernation is going on, kswapd is freezed so that it can't mark + * the zone into all_unreclaimable. Thus bypassing all_unreclaimable + * check. + */ + if (oom_killer_disabled) + return 0; + /* top priority shrink_zones still had more to do? don't OOM, then */ if (scanning_global_lru(sc) && !all_unreclaimable(zonelist, sc)) return 1; From 3608428decef537a1d9d7f5f6df1c6314f480fef Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Thu, 14 Apr 2011 15:22:09 -0700 Subject: [PATCH 1389/2556] brk: COMPAT_BRK: fix detection of randomized brk commit 4471a675dfc7ca676c165079e91c712b09dc9ce4 upstream. 5520e89 ("brk: fix min_brk lower bound computation for COMPAT_BRK") tried to get the whole logic of brk randomization for legacy (libc5-based) applications finally right. It turns out that the way to detect whether brk has actually been randomized in the end or not introduced by that patch still doesn't work for those binaries, as reported by Geert: : /sbin/init from my old m68k ramdisk exists prematurely. : : Before the patch: : : | brk(0x80005c8e) = 0x80006000 : : After the patch: : : | brk(0x80005c8e) = 0x80005c8e : : Old libc5 considers brk() to have failed if the return value is not : identical to the requested value. I don't like it, but currently see no better option than a bit flag in task_struct to catch the CONFIG_COMPAT_BRK && randomize_va_space == 2 case. Signed-off-by: Jiri Kosina Tested-by: Geert Uytterhoeven Reported-by: Geert Uytterhoeven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/binfmt_elf.c | 6 +++++- include/linux/sched.h | 3 +++ mm/mmap.c | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index d5b640ba6cb1a..70470b2b7f73d 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -941,9 +941,13 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) current->mm->start_stack = bprm->p; #ifdef arch_randomize_brk - if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1)) + if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1)) { current->mm->brk = current->mm->start_brk = arch_randomize_brk(current->mm); +#ifdef CONFIG_COMPAT_BRK + current->brk_randomized = 1; +#endif + } #endif if (current->personality & MMAP_PAGE_ZERO) { diff --git a/include/linux/sched.h b/include/linux/sched.h index 536014c6b4f2e..3f1c7cff1121a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1254,6 +1254,9 @@ struct task_struct { #endif struct mm_struct *mm, *active_mm; +#ifdef CONFIG_COMPAT_BRK + unsigned brk_randomized:1; +#endif #if defined(SPLIT_RSS_COUNTING) struct task_rss_stat rss_stat; #endif diff --git a/mm/mmap.c b/mm/mmap.c index 8c05e5b43b69c..e27e0cf0de03c 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -259,7 +259,7 @@ SYSCALL_DEFINE1(brk, unsigned long, brk) * randomize_va_space to 2, which will still cause mm->start_brk * to be arbitrarily shifted */ - if (mm->start_brk > PAGE_ALIGN(mm->end_data)) + if (current->brk_randomized) min_brk = mm->start_brk; else min_brk = mm->end_data; From 42025b04967183c78f8feaea13803b5e71a912a5 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 22 Mar 2011 11:31:37 +0200 Subject: [PATCH 1390/2556] usb: musb: temporarily make it bool commit 7a180e70cfc56e131bfe4796773df2acfc7d4180 upstream. Due to the recent changes to musb's glue layers, we can't compile musb-hdrc as a module - compilation will break due to undefined symbol musb_debug. In order to fix that, we need a big re-work of the debug support on the MUSB driver. Because that would mean a lot of new code coming into the -rc series, it's best to defer that to next merge window and for now just disable module support for MUSB. Once we get the refactor of the debugging support done, we can simply revert this patch and things will go back to normal again. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 4cbb7e4b368d2..74073b363c30f 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -14,7 +14,7 @@ config USB_MUSB_HDRC select TWL4030_USB if MACH_OMAP_3430SDP select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA select USB_OTG_UTILS - tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' + bool 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' help Say Y here if your system has a dual role high speed USB controller based on the Mentor Graphics silicon IP. Then @@ -30,8 +30,8 @@ config USB_MUSB_HDRC If you do not know what this is, please say N. - To compile this driver as a module, choose M here; the - module will be called "musb-hdrc". +# To compile this driver as a module, choose M here; the +# module will be called "musb-hdrc". choice prompt "Platform Glue Layer" From 0302eca3bd4d66271f484eda32b40d8dcc4f8e7c Mon Sep 17 00:00:00 2001 From: Christian Simon Date: Mon, 28 Mar 2011 21:54:47 +0200 Subject: [PATCH 1391/2556] USB: ftdi_sio: Added IDs for CTI USB Serial Devices commit 5a9443f08c83c294c5c806a689c1184b27cb26b3 upstream. I added new ProdutIds for two devices from CTI GmbH Leipzig. Signed-off-by: Christian Simon Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 2 ++ drivers/usb/serial/ftdi_sio_ids.h | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index f349a3629d00c..f9f0510827b03 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -151,6 +151,8 @@ static struct ftdi_sio_quirk ftdi_stmclite_quirk = { * /sys/bus/usb/ftdi_sio/new_id, then send patch/report! */ static struct usb_device_id id_table_combined [] = { + { USB_DEVICE(FTDI_VID, FTDI_CTI_MINI_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CTI_NANO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CANDAPTER_PID) }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 117e8e6f93c68..8e350192fd114 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -1140,3 +1140,12 @@ #define QIHARDWARE_VID 0x20B7 #define MILKYMISTONE_JTAGSERIAL_PID 0x0713 +/* + * CTI GmbH RS485 Converter http://www.cti-lean.com/ + */ +/* USB-485-Mini*/ +#define FTDI_CTI_MINI_PID 0xF608 +/* USB-Nano-485*/ +#define FTDI_CTI_NANO_PID 0xF60B + + From d6f878b34e373fda779bb27bb568deca5911a0c0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 8 Apr 2011 17:38:22 +0200 Subject: [PATCH 1392/2556] USB: ftdi_sio: add PID for OCT DK201 docking station commit 11a31d84129dc3133417d626643d714c9df5317e upstream. Add PID 0x0103 for serial port of the OCT DK201 docking station. Reported-by: Jan Hoogenraad Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/ftdi_sio_ids.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index f9f0510827b03..be477a486284e 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -527,6 +527,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_8_PID) }, { USB_DEVICE(IDTECH_VID, IDTECH_IDT1221U_PID) }, { USB_DEVICE(OCT_VID, OCT_US101_PID) }, + { USB_DEVICE(OCT_VID, OCT_DK201_PID) }, { USB_DEVICE(FTDI_VID, FTDI_HE_TIRA1_PID), .driver_info = (kernel_ulong_t)&ftdi_HE_TIRA1_quirk }, { USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID), diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 8e350192fd114..c434686f4cd35 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -572,6 +572,7 @@ /* Note: OCT US101 is also rebadged as Dick Smith Electronics (NZ) XH6381 */ /* Also rebadged as Dick Smith Electronics (Aus) XH6451 */ /* Also rebadged as SIIG Inc. model US2308 hardware version 1 */ +#define OCT_DK201_PID 0x0103 /* OCT DK201 USB docking station */ #define OCT_US101_PID 0x0421 /* OCT US101 USB to RS-232 */ /* From aa024d1f32c189f0af73fbd4660e0b8094ed95f0 Mon Sep 17 00:00:00 2001 From: Paul Friedrich Date: Fri, 18 Mar 2011 11:13:55 +0100 Subject: [PATCH 1393/2556] USB: ftdi_sio: add ids for Hameg HO720 and HO730 commit c53c2fab40cf16e13af66f40bfd27200cda98d2f upstream. usb serial: ftdi_sio: add two missing USB ID's for Hameg interfaces HO720 and HO730 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 2 ++ drivers/usb/serial/ftdi_sio_ids.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index be477a486284e..31bba6ddfb279 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -788,6 +788,8 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, HAMEG_HO820_PID) }, + { USB_DEVICE(FTDI_VID, HAMEG_HO720_PID) }, + { USB_DEVICE(FTDI_VID, HAMEG_HO730_PID) }, { USB_DEVICE(FTDI_VID, HAMEG_HO870_PID) }, { USB_DEVICE(FTDI_VID, MJSG_GENERIC_PID) }, { USB_DEVICE(FTDI_VID, MJSG_SR_RADIO_PID) }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index c434686f4cd35..4e873ce579bbc 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -300,6 +300,8 @@ * Hameg HO820 and HO870 interface (using VID 0x0403) */ #define HAMEG_HO820_PID 0xed74 +#define HAMEG_HO730_PID 0xed73 +#define HAMEG_HO720_PID 0xed72 #define HAMEG_HO870_PID 0xed71 /* From 7e0538112dd8ecb105886a42fdb59084e215ee6f Mon Sep 17 00:00:00 2001 From: "Marius B. Kotsbak" Date: Tue, 22 Mar 2011 00:01:53 +0100 Subject: [PATCH 1394/2556] USB: option: Added support for Samsung GT-B3730/GT-B3710 LTE USB modem. commit 80f9df3e0093ad9f1eeefd2ff7fd27daaa518d25 upstream. Bind only modem AT command endpoint to option. Signed-off-by: Marius B. Kotsbak Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 75c7f456eed52..d77ff0435896b 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -407,6 +407,10 @@ static void option_instat_callback(struct urb *urb); /* ONDA MT825UP HSDPA 14.2 modem */ #define ONDA_MT825UP 0x000b +/* Samsung products */ +#define SAMSUNG_VENDOR_ID 0x04e8 +#define SAMSUNG_PRODUCT_GT_B3730 0x6889 + /* some devices interfaces need special handling due to a number of reasons */ enum option_blacklist_reason { OPTION_BLACKLIST_NONE = 0, @@ -968,6 +972,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD100) }, { USB_DEVICE(CELOT_VENDOR_ID, CELOT_PRODUCT_CT680M) }, /* CT-650 CDMA 450 1xEVDO modem */ { USB_DEVICE(ONDA_VENDOR_ID, ONDA_MT825UP) }, /* ONDA MT825UP modem */ + { USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_GT_B3730, USB_CLASS_CDC_DATA, 0x00, 0x00) }, /* Samsung GT-B3730/GT-B3710 LTE USB modem.*/ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); From de17cd0ce567c5de100ab99a5b03f784c54e64a2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 18 Apr 2011 10:35:30 -0700 Subject: [PATCH 1395/2556] next_pidmap: fix overflow condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c78193e9c7bcbf25b8237ad0dec82f805c4ea69b upstream. next_pidmap() just quietly accepted whatever 'last' pid that was passed in, which is not all that safe when one of the users is /proc. Admittedly the proc code should do some sanity checking on the range (and that will be the next commit), but that doesn't mean that the helper functions should just do that pidmap pointer arithmetic without checking the range of its arguments. So clamp 'last' to PID_MAX_LIMIT. The fact that we then do "last+1" doesn't really matter, the for-loop does check against the end of the pidmap array properly (it's only the actual pointer arithmetic overflow case we need to worry about, and going one bit beyond isn't going to overflow). [ Use PID_MAX_LIMIT rather than pid_max as per Eric Biederman ] Reported-by: Tavis Ormandy Analyzed-by: Robert Święcki Cc: Eric W. Biederman Cc: Pavel Emelyanov Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- include/linux/pid.h | 2 +- kernel/pid.c | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/linux/pid.h b/include/linux/pid.h index 49f1c2f66e951..ec9f2df57f1b8 100644 --- a/include/linux/pid.h +++ b/include/linux/pid.h @@ -117,7 +117,7 @@ extern struct pid *find_vpid(int nr); */ extern struct pid *find_get_pid(int nr); extern struct pid *find_ge_pid(int nr, struct pid_namespace *); -int next_pidmap(struct pid_namespace *pid_ns, int last); +int next_pidmap(struct pid_namespace *pid_ns, unsigned int last); extern struct pid *alloc_pid(struct pid_namespace *ns); extern void free_pid(struct pid *pid); diff --git a/kernel/pid.c b/kernel/pid.c index 39b65b69584f5..6aeebc20d34b2 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -217,11 +217,14 @@ static int alloc_pidmap(struct pid_namespace *pid_ns) return -1; } -int next_pidmap(struct pid_namespace *pid_ns, int last) +int next_pidmap(struct pid_namespace *pid_ns, unsigned int last) { int offset; struct pidmap *map, *end; + if (last >= PID_MAX_LIMIT) + return -1; + offset = (last + 1) & BITS_PER_PAGE_MASK; map = &pid_ns->pidmap[(last + 1)/BITS_PER_PAGE]; end = &pid_ns->pidmap[PIDMAP_ENTRIES]; From d7112d551c57d5b5f3b7aa342c9c16f79d6c806b Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 18 Apr 2011 10:36:54 -0700 Subject: [PATCH 1396/2556] proc: do proper range check on readdir offset commit d8bdc59f215e62098bc5b4256fd9928bf27053a1 upstream. Rather than pass in some random truncated offset to the pid-related functions, check that the offset is in range up-front. This is just cleanup, the previous commit fixed the real problem. Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/proc/base.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index 4147a124cf49e..bbf7576e57706 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -3106,11 +3106,16 @@ static int proc_pid_fill_cache(struct file *filp, void *dirent, filldir_t filldi /* for the /proc/ directory itself, after non-process stuff has been done */ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) { - unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY; - struct task_struct *reaper = get_proc_task(filp->f_path.dentry->d_inode); + unsigned int nr; + struct task_struct *reaper; struct tgid_iter iter; struct pid_namespace *ns; + if (filp->f_pos >= PID_MAX_LIMIT + TGID_OFFSET) + goto out_no_task; + nr = filp->f_pos - FIRST_PROCESS_ENTRY; + + reaper = get_proc_task(filp->f_path.dentry->d_inode); if (!reaper) goto out_no_task; From 2ce8eb6a1adb76ee957219dc23be3c81fb4a3ee1 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Thu, 7 Apr 2011 21:44:21 +0000 Subject: [PATCH 1397/2556] powerpc: Fix oops if scan_dispatch_log is called too early commit 84ffae55af79d7b8834fd0c08d0d1ebf2c77f91e upstream. We currently enable interrupts before the dispatch log for the boot cpu is setup. If a timer interrupt comes in early enough we oops in scan_dispatch_log: Unable to handle kernel paging request for data at address 0x00000010 ... .scan_dispatch_log+0xb0/0x170 .account_system_vtime+0xa0/0x220 .irq_enter+0x88/0xc0 .do_IRQ+0x48/0x230 The patch below adds a check to scan_dispatch_log to ensure the dispatch log has been allocated. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/time.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index aa9269600ca21..02d54e1892d75 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -229,6 +229,9 @@ static u64 scan_dispatch_log(u64 stop_tb) u64 stolen = 0; u64 dtb; + if (!dtl) + return 0; + if (i == vpa->dtl_idx) return 0; while (i < vpa->dtl_idx) { From 9cdc8a8009d7652817d2a4015a481ddee3d61505 Mon Sep 17 00:00:00 2001 From: Eric B Munson Date: Fri, 15 Apr 2011 08:12:30 +0000 Subject: [PATCH 1398/2556] powerpc/perf_event: Skip updating kernel counters if register value shrinks commit 86c74ab317c1ef4d37325e0d7ca8a01a796b0bd7 upstream. Because of speculative event roll back, it is possible for some event coutners to decrease between reads on POWER7. This causes a problem with the way that counters are updated. Delta calues are calculated in a 64 bit value and the top 32 bits are masked. If the register value has decreased, this leaves us with a very large positive value added to the kernel counters. This patch protects against this by skipping the update if the delta would be negative. This can lead to a lack of precision in the coutner values, but from my testing the value is typcially fewer than 10 samples at a time. Signed-off-by: Eric B Munson Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/perf_event.c | 37 ++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/arch/powerpc/kernel/perf_event.c b/arch/powerpc/kernel/perf_event.c index 97e0ae414940e..26e56e346912e 100644 --- a/arch/powerpc/kernel/perf_event.c +++ b/arch/powerpc/kernel/perf_event.c @@ -398,6 +398,25 @@ static int check_excludes(struct perf_event **ctrs, unsigned int cflags[], return 0; } +static u64 check_and_compute_delta(u64 prev, u64 val) +{ + u64 delta = (val - prev) & 0xfffffffful; + + /* + * POWER7 can roll back counter values, if the new value is smaller + * than the previous value it will cause the delta and the counter to + * have bogus values unless we rolled a counter over. If a coutner is + * rolled back, it will be smaller, but within 256, which is the maximum + * number of events to rollback at once. If we dectect a rollback + * return 0. This can lead to a small lack of precision in the + * counters. + */ + if (prev > val && (prev - val) < 256) + delta = 0; + + return delta; +} + static void power_pmu_read(struct perf_event *event) { s64 val, delta, prev; @@ -416,10 +435,11 @@ static void power_pmu_read(struct perf_event *event) prev = local64_read(&event->hw.prev_count); barrier(); val = read_pmc(event->hw.idx); + delta = check_and_compute_delta(prev, val); + if (!delta) + return; } while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev); - /* The counters are only 32 bits wide */ - delta = (val - prev) & 0xfffffffful; local64_add(delta, &event->count); local64_sub(delta, &event->hw.period_left); } @@ -449,8 +469,9 @@ static void freeze_limited_counters(struct cpu_hw_events *cpuhw, val = (event->hw.idx == 5) ? pmc5 : pmc6; prev = local64_read(&event->hw.prev_count); event->hw.idx = 0; - delta = (val - prev) & 0xfffffffful; - local64_add(delta, &event->count); + delta = check_and_compute_delta(prev, val); + if (delta) + local64_add(delta, &event->count); } } @@ -458,14 +479,16 @@ static void thaw_limited_counters(struct cpu_hw_events *cpuhw, unsigned long pmc5, unsigned long pmc6) { struct perf_event *event; - u64 val; + u64 val, prev; int i; for (i = 0; i < cpuhw->n_limited; ++i) { event = cpuhw->limited_counter[i]; event->hw.idx = cpuhw->limited_hwidx[i]; val = (event->hw.idx == 5) ? pmc5 : pmc6; - local64_set(&event->hw.prev_count, val); + prev = local64_read(&event->hw.prev_count); + if (check_and_compute_delta(prev, val)) + local64_set(&event->hw.prev_count, val); perf_event_update_userpage(event); } } @@ -1197,7 +1220,7 @@ static void record_and_restart(struct perf_event *event, unsigned long val, /* we don't have to worry about interrupts here */ prev = local64_read(&event->hw.prev_count); - delta = (val - prev) & 0xfffffffful; + delta = check_and_compute_delta(prev, val); local64_add(delta, &event->count); /* From 6b97da124c2bf338f63144443a43b82c47971639 Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Mon, 4 Apr 2011 17:57:37 +0100 Subject: [PATCH 1399/2556] usb: Fix qcserial memory leak on rmmod commit 10c9ab15d6aee153968d150c05b3ee3df89673de upstream. qcprobe function allocates serial->private but this is never freed, this patch adds a new function qc_release() which frees serial->private, after calling usb_wwan_release Signed-off-by: Steven Hardy Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/qcserial.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 8858201eb1d39..6e3b933457f4e 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -205,6 +205,18 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) return retval; } +static void qc_release(struct usb_serial *serial) +{ + struct usb_wwan_intf_private *priv = usb_get_serial_data(serial); + + dbg("%s", __func__); + + /* Call usb_wwan release & free the private data allocated in qcprobe */ + usb_wwan_release(serial); + usb_set_serial_data(serial, NULL); + kfree(priv); +} + static struct usb_serial_driver qcdevice = { .driver = { .owner = THIS_MODULE, @@ -222,7 +234,7 @@ static struct usb_serial_driver qcdevice = { .chars_in_buffer = usb_wwan_chars_in_buffer, .attach = usb_wwan_startup, .disconnect = usb_wwan_disconnect, - .release = usb_wwan_release, + .release = qc_release, #ifdef CONFIG_PM .suspend = usb_wwan_suspend, .resume = usb_wwan_resume, From 5a62c3d273492b38884bd8cd27c0c0b3614e7aef Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Mon, 4 Apr 2011 17:59:55 +0100 Subject: [PATCH 1400/2556] usb: qcserial avoid pointing to freed memory commit 99ab3f9e4eaec35fd2d7159c31b71f17f7e613e3 upstream. Rework the qcprobe logic such that serial->private is not set when qcprobe exits with -ENODEV, otherwise serial->private will point to freed memory on -ENODEV Signed-off-by: Steven Hardy Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/qcserial.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 6e3b933457f4e..cd638648479ad 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -111,7 +111,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) ifnum = intf->desc.bInterfaceNumber; dbg("This Interface = %d", ifnum); - data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), + data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); if (!data) return -ENOMEM; @@ -134,8 +134,10 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) { dbg("QDL port found"); - if (serial->interface->num_altsetting == 1) - return 0; + if (serial->interface->num_altsetting == 1) { + retval = 0; /* Success */ + break; + } retval = usb_set_interface(serial->dev, ifnum, 1); if (retval < 0) { @@ -145,7 +147,6 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) retval = -ENODEV; kfree(data); } - return retval; } break; @@ -177,7 +178,6 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) retval = -ENODEV; kfree(data); } - return retval; } else if (ifnum==3) { /* * NMEA (serial line 9600 8N1) @@ -199,9 +199,12 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) dev_err(&serial->dev->dev, "unknown number of interfaces: %d\n", nintf); kfree(data); - return -ENODEV; + retval = -ENODEV; } + /* Set serial->private if not returning -ENODEV */ + if (retval != -ENODEV) + usb_set_serial_data(serial, data); return retval; } From 096bcf055d8b08b3f6a42816d7520f4db59bc899 Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Mon, 4 Apr 2011 18:02:25 +0100 Subject: [PATCH 1401/2556] usb: qcserial add missing errorpath kfrees commit cb62d65f966146a39fdde548cb474dacf1d00fa5 upstream. There are two -ENODEV error paths in qcprobe where the allocated private data is not freed, this patch adds the two missing kfrees to avoid leaking memory on the error path Signed-off-by: Steven Hardy Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/qcserial.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index cd638648479ad..54a9dab1f33b4 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -167,6 +167,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) "Could not set interface, error %d\n", retval); retval = -ENODEV; + kfree(data); } } else if (ifnum == 2) { dbg("Modem port found"); @@ -191,6 +192,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) "Could not set interface, error %d\n", retval); retval = -ENODEV; + kfree(data); } } break; From d3500312c8dd8ade8ca105326b909c79ae1418d5 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 5 Apr 2011 13:36:15 -0400 Subject: [PATCH 1402/2556] USB: EHCI: unlink unused QHs when the controller is stopped commit 94ae4976e253757e9b03a44d27d41b20f1829d80 upstream. This patch (as1458) fixes a problem affecting ultra-reliable systems: When hardware failover of an EHCI controller occurs, the data structures do not get released correctly. This is because the routine responsible for removing unused QHs from the async schedule assumes the controller is running properly (the frame counter is used in determining how long the QH has been idle) -- but when a failover causes the controller to be electronically disconnected from the PCI bus, obviously it stops running. The solution is simple: Allow scan_async() to remove a QH from the async schedule if it has been idle for long enough _or_ if the controller is stopped. Signed-off-by: Alan Stern Reported-and-Tested-by: Dan Duval Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-q.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 5add8b5ddda88..baf7362b0e444 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1245,24 +1245,27 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) static void scan_async (struct ehci_hcd *ehci) { + bool stopped; struct ehci_qh *qh; enum ehci_timer_action action = TIMER_IO_WATCHDOG; ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index); timer_action_done (ehci, TIMER_ASYNC_SHRINK); rescan: + stopped = !HC_IS_RUNNING(ehci_to_hcd(ehci)->state); qh = ehci->async->qh_next.qh; if (likely (qh != NULL)) { do { /* clean any finished work for this qh */ - if (!list_empty (&qh->qtd_list) - && qh->stamp != ehci->stamp) { + if (!list_empty(&qh->qtd_list) && (stopped || + qh->stamp != ehci->stamp)) { int temp; /* unlinks could happen here; completion * reporting drops the lock. rescan using * the latest schedule, but don't rescan - * qhs we already finished (no looping). + * qhs we already finished (no looping) + * unless the controller is stopped. */ qh = qh_get (qh); qh->stamp = ehci->stamp; @@ -1283,9 +1286,9 @@ static void scan_async (struct ehci_hcd *ehci) */ if (list_empty(&qh->qtd_list) && qh->qh_state == QH_STATE_LINKED) { - if (!ehci->reclaim - && ((ehci->stamp - qh->stamp) & 0x1fff) - >= (EHCI_SHRINK_FRAMES * 8)) + if (!ehci->reclaim && (stopped || + ((ehci->stamp - qh->stamp) & 0x1fff) + >= EHCI_SHRINK_FRAMES * 8)) start_unlink_async(ehci, qh); else action = TIMER_ASYNC_SHRINK; From b08b256a360ff4eba25780ccfa824e7b7c821ccf Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 18 Mar 2011 21:29:01 -0700 Subject: [PATCH 1403/2556] USB: fix formatting of SuperSpeed endpoints in /proc/bus/usb/devices commit 2868a2b1ba8f9c7f6c4170519ebb6c62934df70e upstream. Isochronous and interrupt SuperSpeed endpoints use the same mechanisms for decoding bInterval values as HighSpeed ones so adjust the code accordingly. Also bandwidth reservation for SuperSpeed matches highspeed, not low/full speed. Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devices.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index a3d2e2399655b..96fdfb815f895 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -221,7 +221,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end, break; case USB_ENDPOINT_XFER_INT: type = "Int."; - if (speed == USB_SPEED_HIGH) + if (speed == USB_SPEED_HIGH || speed == USB_SPEED_SUPER) interval = 1 << (desc->bInterval - 1); else interval = desc->bInterval; @@ -229,7 +229,8 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end, default: /* "can't happen" */ return start; } - interval *= (speed == USB_SPEED_HIGH) ? 125 : 1000; + interval *= (speed == USB_SPEED_HIGH || + speed == USB_SPEED_SUPER) ? 125 : 1000; if (interval % 1000) unit = 'u'; else { @@ -542,8 +543,9 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, if (level == 0) { int max; - /* high speed reserves 80%, full/low reserves 90% */ - if (usbdev->speed == USB_SPEED_HIGH) + /* super/high speed reserves 80%, full/low reserves 90% */ + if (usbdev->speed == USB_SPEED_HIGH || + usbdev->speed == USB_SPEED_SUPER) max = 800; else max = FRAME_TIME_MAX_USECS_ALLOC; From 59de7c3d393bc46183b814c3e2d2f1c0a2d87752 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 20 Mar 2011 02:15:17 -0700 Subject: [PATCH 1404/2556] USB: xhci - fix unsafe macro definitions commit 5a6c2f3ff039154872ce597952f8b8900ea0d732 upstream. Macro arguments used in expressions need to be enclosed in parenthesis to avoid unpleasant surprises. This should be queued for kernels back to 2.6.31 Signed-off-by: Dmitry Torokhov Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 62bc1bc2bd72e..19040c54be0dc 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -232,7 +232,7 @@ struct xhci_op_regs { * notification type that matches a bit set in this bit field. */ #define DEV_NOTE_MASK (0xffff) -#define ENABLE_DEV_NOTE(x) (1 << x) +#define ENABLE_DEV_NOTE(x) (1 << (x)) /* Most of the device notification types should only be used for debug. * SW does need to pay attention to function wake notifications. */ @@ -601,11 +601,11 @@ struct xhci_ep_ctx { #define EP_STATE_STOPPED 3 #define EP_STATE_ERROR 4 /* Mult - Max number of burtst within an interval, in EP companion desc. */ -#define EP_MULT(p) ((p & 0x3) << 8) +#define EP_MULT(p) (((p) & 0x3) << 8) /* bits 10:14 are Max Primary Streams */ /* bit 15 is Linear Stream Array */ /* Interval - period between requests to an endpoint - 125u increments. */ -#define EP_INTERVAL(p) ((p & 0xff) << 16) +#define EP_INTERVAL(p) (((p) & 0xff) << 16) #define EP_INTERVAL_TO_UFRAMES(p) (1 << (((p) >> 16) & 0xff)) #define EP_MAXPSTREAMS_MASK (0x1f << 10) #define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK) From a3557f12d41b7cf7512e5518ff4f438fcab4066c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 23 Mar 2011 22:41:23 -0700 Subject: [PATCH 1405/2556] USB: xhci - fix math in xhci_get_endpoint_interval() commit dfa49c4ad120a784ef1ff0717168aa79f55a483a upstream. When parsing exponent-expressed intervals we subtract 1 from the value and then expect it to match with original + 1, which is highly unlikely, and we end with frequent spew: usb 3-4: ep 0x83 - rounding interval to 512 microframes Also, parsing interval for fullspeed isochronous endpoints was incorrect - according to USB spec they use exponent-based intervals (but xHCI spec claims frame-based intervals). I trust USB spec more, especially since USB core agrees with it. This should be queued for stable kernels back to 2.6.31. Reviewed-by: Micah Elizabeth Scott Signed-off-by: Dmitry Torokhov Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 90 +++++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 28 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index a9534396e85ba..0de3100053271 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -920,6 +920,47 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud return 0; } +/* + * Convert interval expressed as 2^(bInterval - 1) == interval into + * straight exponent value 2^n == interval. + * + */ +static unsigned int xhci_parse_exponent_interval(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + unsigned int interval; + + interval = clamp_val(ep->desc.bInterval, 1, 16) - 1; + if (interval != ep->desc.bInterval - 1) + dev_warn(&udev->dev, + "ep %#x - rounding interval to %d microframes\n", + ep->desc.bEndpointAddress, + 1 << interval); + + return interval; +} + +/* + * Convert bInterval expressed in frames (in 1-255 range) to exponent of + * microframes, rounded down to nearest power of 2. + */ +static unsigned int xhci_parse_frame_interval(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + unsigned int interval; + + interval = fls(8 * ep->desc.bInterval) - 1; + interval = clamp_val(interval, 3, 10); + if ((1 << interval) != 8 * ep->desc.bInterval) + dev_warn(&udev->dev, + "ep %#x - rounding interval to %d microframes, ep desc says %d microframes\n", + ep->desc.bEndpointAddress, + 1 << interval, + 8 * ep->desc.bInterval); + + return interval; +} + /* Return the polling or NAK interval. * * The polling interval is expressed in "microframes". If xHCI's Interval field @@ -937,45 +978,38 @@ static inline unsigned int xhci_get_endpoint_interval(struct usb_device *udev, case USB_SPEED_HIGH: /* Max NAK rate */ if (usb_endpoint_xfer_control(&ep->desc) || - usb_endpoint_xfer_bulk(&ep->desc)) + usb_endpoint_xfer_bulk(&ep->desc)) { interval = ep->desc.bInterval; + break; + } /* Fall through - SS and HS isoc/int have same decoding */ + case USB_SPEED_SUPER: if (usb_endpoint_xfer_int(&ep->desc) || - usb_endpoint_xfer_isoc(&ep->desc)) { - if (ep->desc.bInterval == 0) - interval = 0; - else - interval = ep->desc.bInterval - 1; - if (interval > 15) - interval = 15; - if (interval != ep->desc.bInterval + 1) - dev_warn(&udev->dev, "ep %#x - rounding interval to %d microframes\n", - ep->desc.bEndpointAddress, 1 << interval); + usb_endpoint_xfer_isoc(&ep->desc)) { + interval = xhci_parse_exponent_interval(udev, ep); } break; - /* Convert bInterval (in 1-255 frames) to microframes and round down to - * nearest power of 2. - */ + case USB_SPEED_FULL: + if (usb_endpoint_xfer_int(&ep->desc)) { + interval = xhci_parse_exponent_interval(udev, ep); + break; + } + /* + * Fall through for isochronous endpoint interval decoding + * since it uses the same rules as low speed interrupt + * endpoints. + */ + case USB_SPEED_LOW: if (usb_endpoint_xfer_int(&ep->desc) || - usb_endpoint_xfer_isoc(&ep->desc)) { - interval = fls(8*ep->desc.bInterval) - 1; - if (interval > 10) - interval = 10; - if (interval < 3) - interval = 3; - if ((1 << interval) != 8*ep->desc.bInterval) - dev_warn(&udev->dev, - "ep %#x - rounding interval" - " to %d microframes, " - "ep desc says %d microframes\n", - ep->desc.bEndpointAddress, - 1 << interval, - 8*ep->desc.bInterval); + usb_endpoint_xfer_isoc(&ep->desc)) { + + interval = xhci_parse_frame_interval(udev, ep); } break; + default: BUG(); } From 6dd7239a4ecca7e24c73bee1972cdcd705da71ae Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 12 Apr 2011 23:06:28 -0700 Subject: [PATCH 1406/2556] USB: xhci - also free streams when resetting devices commit 2dea75d96ade3c7cd2bfe73f99c7b3291dc3d03a upstream. Currently, when resetting a device, xHCI driver disables all but one endpoints and frees their rings, but leaves alone any streams that might have been allocated. Later, when users try to free allocated streams, we oops in xhci_setup_no_streams_ep_input_ctx() because ep->ring is NULL. Let's free not only rings but also stream data as well, so that calling free_streams() on a device that was reset will be safe. This should be queued for stable trees back to 2.6.35. Reviewed-by: Micah Elizabeth Scott Signed-off-by: Dmitry Torokhov Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 2083fc2179b2a..150349d1fdac5 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -2335,10 +2335,18 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) /* Everything but endpoint 0 is disabled, so free or cache the rings. */ last_freed_endpoint = 1; for (i = 1; i < 31; ++i) { - if (!virt_dev->eps[i].ring) - continue; - xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i); - last_freed_endpoint = i; + struct xhci_virt_ep *ep = &virt_dev->eps[i]; + + if (ep->ep_state & EP_HAS_STREAMS) { + xhci_free_stream_info(xhci, ep->stream_info); + ep->stream_info = NULL; + ep->ep_state &= ~EP_HAS_STREAMS; + } + + if (ep->ring) { + xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i); + last_freed_endpoint = i; + } } xhci_dbg(xhci, "Output context after successful reset device cmd:\n"); xhci_dbg_ctx(xhci, virt_dev->out_ctx, last_freed_endpoint); From 126e86bb7c01bef34b8bc10022ff95a0a20c4016 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Tue, 28 Sep 2010 00:57:32 -0400 Subject: [PATCH 1407/2556] USB: Fix unplug of device with active streams commit b214f191d95ba4b5a35aebd69cd129cf7e3b1884 upstream. If I unplug a device while the UAS driver is loaded, I get an oops in usb_free_streams(). This is because usb_unbind_interface() calls usb_disable_interface() which calls usb_disable_endpoint() which sets ep_out and ep_in to NULL. Then the UAS driver calls usb_pipe_endpoint() which returns a NULL pointer and passes an array of NULL pointers to usb_free_streams(). I think the correct fix for this is to check for the NULL pointer in usb_free_streams() rather than making the driver check for this situation. My original patch for this checked for dev->state == USB_STATE_NOTATTACHED, but the call to usb_disable_interface() is conditional, so not all drivers would want this check. Note from Sarah Sharp: This patch does avoid a potential dereference, but the real fix (which will be implemented later) is to set the .soft_unbind flag in the usb_driver structure for the UAS driver, and all drivers that allocate streams. The driver should free any streams when it is unbound from the interface. This avoids leaking stream rings in the xHCI driver when usb_disable_interface() is called. This should be queued for stable trees back to 2.6.35. Signed-off-by: Matthew Wilcox Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index c34a935c7a379..fd95b9e94bb10 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1885,7 +1885,7 @@ void usb_free_streams(struct usb_interface *interface, /* Streams only apply to bulk endpoints. */ for (i = 0; i < num_eps; i++) - if (!usb_endpoint_xfer_bulk(&eps[i]->desc)) + if (!eps[i] || !usb_endpoint_xfer_bulk(&eps[i]->desc)) return; hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags); From 0f2c413793165da28f7f9743a26dfb38a9a29a3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Thu, 7 Apr 2011 16:17:47 +0200 Subject: [PATCH 1408/2556] radeon: Fix KMS CP writeback on big endian machines. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit dc66b325f161bb651493c7d96ad44876b629cf6a upstream. This is necessary even with PCI(e) GART, and it makes writeback work even with AGP on my PowerBook. Might still be unreliable with older revisions of UniNorth and other AGP bridges though. Signed-off-by: Michel Dänzer Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_fence.c | 2 +- drivers/gpu/drm/radeon/radeon_ring.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c index 171b0b2e3a644..2b0ee623cd319 100644 --- a/drivers/gpu/drm/radeon/radeon_fence.c +++ b/drivers/gpu/drm/radeon/radeon_fence.c @@ -80,7 +80,7 @@ static bool radeon_fence_poll_locked(struct radeon_device *rdev) scratch_index = R600_WB_EVENT_OFFSET + rdev->fence_drv.scratch_reg - rdev->scratch.reg_base; else scratch_index = RADEON_WB_SCRATCH_OFFSET + rdev->fence_drv.scratch_reg - rdev->scratch.reg_base; - seq = rdev->wb.wb[scratch_index/4]; + seq = le32_to_cpu(rdev->wb.wb[scratch_index/4]); } else seq = RREG32(rdev->fence_drv.scratch_reg); if (seq != rdev->fence_drv.last_seq) { diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index 06e79822a2bff..d6edfeb6f3411 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -248,7 +248,7 @@ void radeon_ib_pool_fini(struct radeon_device *rdev) void radeon_ring_free_size(struct radeon_device *rdev) { if (rdev->wb.enabled) - rdev->cp.rptr = rdev->wb.wb[RADEON_WB_CP_RPTR_OFFSET/4]; + rdev->cp.rptr = le32_to_cpu(rdev->wb.wb[RADEON_WB_CP_RPTR_OFFSET/4]); else { if (rdev->family >= CHIP_R600) rdev->cp.rptr = RREG32(R600_CP_RB_RPTR); From 5cb0567e14102ff93077c90036df7161eb12c105 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 16 Mar 2011 15:36:29 -0300 Subject: [PATCH 1409/2556] Bluetooth: Fix HCI_RESET command synchronization commit f630cf0d5434e3923e1b8226ffa2753ead6b0ce5 upstream. We can't send new commands before a cmd_complete for the HCI_RESET command shows up. Reported-by: Mikko Vinni Reported-by: Justin P. Mattock Reported-by: Ed Tomlinson Signed-off-by: Gustavo F. Padovan Tested-by: Justin P. Mattock Tested-by: Mikko Vinni Tested-by: Ed Tomlinson --- include/net/bluetooth/hci.h | 2 ++ net/bluetooth/hci_core.c | 5 ++++- net/bluetooth/hci_event.c | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 339b2ea173ddf..dc74690112e5e 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -76,6 +76,8 @@ enum { HCI_INQUIRY, HCI_RAW, + + HCI_RESET, }; /* HCI ioctl defines */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 75604cee46b3a..8a6f0af8e142a 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -184,6 +184,7 @@ static void hci_reset_req(struct hci_dev *hdev, unsigned long opt) BT_DBG("%s %ld", hdev->name, opt); /* Reset device */ + set_bit(HCI_RESET, &hdev->flags); hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL); } @@ -210,8 +211,10 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt) /* Mandatory initialization */ /* Reset */ - if (!test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks)) + if (!test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks)) { + set_bit(HCI_RESET, &hdev->flags); hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL); + } /* Read Local Supported Features */ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f5cd163027b76..67cd8d52dfdbb 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -183,6 +183,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%x", hdev->name, status); + clear_bit(HCI_RESET, &hdev->flags); + hci_req_complete(hdev, HCI_OP_RESET, status); } @@ -1465,7 +1467,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) break; } - if (ev->ncmd) { + if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); if (!skb_queue_empty(&hdev->cmd_q)) tasklet_schedule(&hdev->cmd_task); From 5668974fb4fa9616cc55adbb23fdc9fdb14273d1 Mon Sep 17 00:00:00 2001 From: Kyle McMartin Date: Mon, 24 Jan 2011 11:13:04 -0500 Subject: [PATCH 1410/2556] perf tool: Fix gcc 4.6.0 issues commit fb7d0b3cefb80a105f7fd26bbc62e0cbf9192822 upstream. GCC 4.6.0 in Fedora rawhide turned up some compile errors in tools/perf due to the -Werror=unused-but-set-variable flag. I've gone through and annotated some of the assignments that had side effects (ie: return value from a function) with the __used annotation, and in some cases, just removed unused code. In a few cases, we were assigning something useful, but not using it in later parts of the function. kyle@dreadnought:~/src% gcc --version gcc (GCC) 4.6.0 20110122 (Red Hat 4.6.0-0.3) Cc: Ingo Molnar LKML-Reference: <20110124161304.GK27353@bombadil.infradead.org> Signed-off-by: Kyle McMartin [ committer note: Fixed up the annotation fixes, as that code moved recently ] Signed-off-by: Arnaldo Carvalho de Melo [Backported to 2.6.38.2 by deleting unused but set variables] Signed-off-by: Thomas Meyer Signed-off-by: Greg Kroah-Hartman --- tools/perf/bench/sched-pipe.c | 2 +- tools/perf/builtin-sched.c | 12 +++--------- tools/perf/builtin-top.c | 5 +---- tools/perf/util/header.c | 2 +- tools/perf/util/hist.c | 3 --- .../perf/util/scripting-engines/trace-event-python.c | 3 +-- tools/perf/util/symbol.c | 4 ++-- tools/perf/util/trace-event-parse.c | 2 +- tools/perf/util/ui/browsers/map.c | 2 +- 9 files changed, 11 insertions(+), 24 deletions(-) diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c index d9ab3ce446acf..0c7454f8b8a98 100644 --- a/tools/perf/bench/sched-pipe.c +++ b/tools/perf/bench/sched-pipe.c @@ -55,7 +55,7 @@ int bench_sched_pipe(int argc, const char **argv, * discarding returned value of read(), write() * causes error in building environment for perf */ - int ret, wait_stat; + int __used ret, wait_stat; pid_t pid, retpid; argc = parse_options(argc, argv, options, diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 29acb894e0351..ae7225c53c29d 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -369,11 +369,6 @@ static void process_sched_event(struct task_desc *this_task __used, struct sched_atom *atom) { int ret = 0; - u64 now; - long long delta; - - now = get_nsecs(); - delta = start_time + atom->timestamp - now; switch (atom->type) { case SCHED_EVENT_RUN: @@ -562,7 +557,7 @@ static void wait_for_tasks(void) static void run_one_test(void) { - u64 T0, T1, delta, avg_delta, fluct, std_dev; + u64 T0, T1, delta, avg_delta, fluct; T0 = get_nsecs(); wait_for_tasks(); @@ -578,7 +573,6 @@ static void run_one_test(void) else fluct = delta - avg_delta; sum_fluct += fluct; - std_dev = sum_fluct / nr_runs / sqrt(nr_runs); if (!run_avg) run_avg = delta; run_avg = (run_avg*9 + delta)/10; @@ -799,7 +793,7 @@ replay_switch_event(struct trace_switch_event *switch_event, u64 timestamp, struct thread *thread __used) { - struct task_desc *prev, *next; + struct task_desc *prev, __used *next; u64 timestamp0; s64 delta; @@ -1404,7 +1398,7 @@ map_switch_event(struct trace_switch_event *switch_event, u64 timestamp, struct thread *thread __used) { - struct thread *sched_out, *sched_in; + struct thread *sched_out __used, *sched_in; int new_shortname; u64 timestamp0; s64 delta; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 5a29d9cd94862..b0f6925b127d6 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -183,7 +183,6 @@ static int parse_source(struct sym_entry *syme) FILE *file; char command[PATH_MAX*2]; const char *path; - u64 len; if (!syme) return -1; @@ -212,8 +211,6 @@ static int parse_source(struct sym_entry *syme) } path = map->dso->long_name; - len = sym->end - sym->start; - sprintf(command, "objdump --start-address=%#0*" PRIx64 " --stop-address=%#0*" PRIx64 " -dS %s", BITS_PER_LONG / 4, map__rip_2objdump(map, sym->start), @@ -1296,7 +1293,7 @@ static int __cmd_top(void) { pthread_t thread; struct perf_evsel *counter; - int i, ret; + int i, ret __used; /* * FIXME: perf_session__new should allow passing a O_MMAP, so that all this * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 0866bcdb5e8e7..9721e2fa9ece1 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -1144,7 +1144,7 @@ int event__synthesize_tracing_data(int fd, struct list_head *pattrs, { event_t ev; ssize_t size = 0, aligned_size = 0, padding; - int err = 0; + int err __used = 0; memset(&ev, 0, sizeof(ev)); diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index df51560f16f7e..5214b703250e0 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1095,7 +1095,6 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head, char command[PATH_MAX * 2]; FILE *file; int err = 0; - u64 len; char symfs_filename[PATH_MAX]; if (filename) { @@ -1140,8 +1139,6 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head, filename, sym->name, map->unmap_ip(map, sym->start), map->unmap_ip(map, sym->end)); - len = sym->end - sym->start; - pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso->long_name, sym, sym->name); diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index c6d99334bdfa8..2040b85385273 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -248,8 +248,7 @@ static void python_process_event(int cpu, void *data, context = PyCObject_FromVoidPtr(scripting_context, NULL); PyTuple_SetItem(t, n++, PyString_FromString(handler_name)); - PyTuple_SetItem(t, n++, - PyCObject_FromVoidPtr(scripting_context, NULL)); + PyTuple_SetItem(t, n++, context); if (handler) { PyTuple_SetItem(t, n++, PyInt_FromLong(cpu)); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index b1bf490aff880..ba6d48949092f 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1525,8 +1525,8 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) symbol_conf.symfs, self->long_name); break; case DSO__ORIG_GUEST_KMODULE: - if (map->groups && map->groups->machine) - root_dir = map->groups->machine->root_dir; + if (map->groups && machine) + root_dir = machine->root_dir; else root_dir = ""; snprintf(name, size, "%s%s%s", symbol_conf.symfs, diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 73a02223c6292..d8e622dd738aa 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -153,7 +153,7 @@ void parse_proc_kallsyms(char *file, unsigned int size __unused) char *next = NULL; char *addr_str; char ch; - int ret; + int ret __used; int i; line = strtok_r(file, "\n", &next); diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c index e5158369106ee..8462bffe20bc8 100644 --- a/tools/perf/util/ui/browsers/map.c +++ b/tools/perf/util/ui/browsers/map.c @@ -41,7 +41,7 @@ static int ui_entry__read(const char *title, char *bf, size_t size, int width) out_free_form: newtPopWindow(); newtFormDestroy(form); - return 0; + return err; } struct map_browser { From 696c8cb98de96261bd5436cc39fe3216e0535790 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 12 Apr 2011 13:39:14 -0700 Subject: [PATCH 1411/2556] bridge: reset IPCB in br_parse_ip_options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f8e9881c2aef1e982e5abc25c046820cd0b7cf64 upstream. Commit 462fb2af9788a82 (bridge : Sanitize skb before it enters the IP stack), missed one IPCB init before calling ip_options_compile() Thanks to Scot Doyle for his tests and bug reports. Reported-by: Scot Doyle Signed-off-by: Eric Dumazet Cc: Hiroaki SHIMODA Acked-by: Bandan Das Acked-by: Stephen Hemminger Cc: Jan Lübbe Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/bridge/br_netfilter.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 49d50ea5dbbc6..333bcaa1377c6 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -249,11 +249,9 @@ static int br_parse_ip_options(struct sk_buff *skb) goto drop; } - /* Zero out the CB buffer if no options present */ - if (iph->ihl == 5) { - memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); + memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); + if (iph->ihl == 5) return 0; - } opt->optlen = iph->ihl*4 - sizeof(struct iphdr); if (ip_options_compile(dev_net(dev), opt, skb)) From 38e2db16141aeffbbe2a85e40f51e89eeb35c899 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 14 Apr 2011 05:55:37 +0000 Subject: [PATCH 1412/2556] ip: ip_options_compile() resilient to NULL skb route commit c65353daf137dd41f3ede3baf62d561fca076228 upstream. Scot Doyle demonstrated ip_options_compile() could be called with an skb without an attached route, using a setup involving a bridge, netfilter, and forged IP packets. Let's make ip_options_compile() and ip_options_rcv_srr() a bit more robust, instead of changing bridge/netfilter code. With help from Hiroaki SHIMODA. Reported-by: Scot Doyle Tested-by: Scot Doyle Signed-off-by: Eric Dumazet Cc: Stephen Hemminger Acked-by: Hiroaki SHIMODA Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ip_options.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index 1906fa35860c8..b0413e300e561 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -329,7 +329,7 @@ int ip_options_compile(struct net *net, pp_ptr = optptr + 2; goto error; } - if (skb) { + if (rt) { memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4); opt->is_changed = 1; } @@ -371,7 +371,7 @@ int ip_options_compile(struct net *net, goto error; } opt->ts = optptr - iph; - if (skb) { + if (rt) { memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4); timeptr = (__be32*)&optptr[optptr[2]+3]; } @@ -603,7 +603,7 @@ int ip_options_rcv_srr(struct sk_buff *skb) unsigned long orefdst; int err; - if (!opt->srr) + if (!opt->srr || !rt) return 0; if (skb->pkt_type != PACKET_HOST) From 95562ec65299f565718ba24d902270adee57b151 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 21 Apr 2011 14:34:46 -0700 Subject: [PATCH 1413/2556] Linux 2.6.38.4 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5728aed8b764b..e3b3c0ab43da6 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 38 -EXTRAVERSION = .3 +EXTRAVERSION = .4 NAME = Flesh-Eating Bats with Fangs # *DOCUMENTATION* From ad07ef13b2464f0b3d885f9741cc5496c4cefbed Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Tue, 22 Mar 2011 12:27:11 -0700 Subject: [PATCH 1414/2556] Use BT POWER force_active parameter for rx data. We were going to active mode during rx processing which defeats the purpose of force_active parameter for HID devices. Based on a report and discussion with Leijun Tao Change-Id: I0dc5ccae131e8a2245328eba5cc35159e7656e8a Signed-off-by: Jaikumar Ganesh --- net/bluetooth/hci_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8a6f0af8e142a..ddfacd5b94e02 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1641,7 +1641,7 @@ static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb) if (conn) { register struct hci_proto *hp; - hci_conn_enter_active_mode(conn, 1); + hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active); /* Send to upper protocol */ hp = hci_proto[HCI_PROTO_L2CAP]; From c20608b31136a1cb8f3911c9cd06eec107ad5768 Mon Sep 17 00:00:00 2001 From: Pierre Tardy Date: Sun, 6 Feb 2011 19:03:46 +0100 Subject: [PATCH 1415/2556] mmc: add per device quirk placeholder Some cards have quirks valid for every platforms using current platform quirk hooks leads to a lot of code and debug duplication. So we inspire a bit from what exists in PCI subsystem and do our own per vendorid/deviceid quirk. We still drop the complexity of the pci quirk system (with special section tables, and so on). That can be added later if needed. Change-Id: Ib67a3e97486023267f5ea3e7c6ef8fc99b13a704 Signed-off-by: Pierre Tardy Acked-by: Linus Walleij Acked-by: Ohad Ben-Cohen Signed-off-by: Chris Ball --- drivers/mmc/core/Makefile | 3 +- drivers/mmc/core/core.h | 2 ++ drivers/mmc/core/quirks.c | 62 +++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/sdio.c | 1 + include/linux/mmc/card.h | 2 ++ 5 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 drivers/mmc/core/quirks.c diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 86b4791193326..639501970b412 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_MMC) += mmc_core.o mmc_core-y := core.o bus.o host.o \ mmc.o mmc_ops.o sd.o sd_ops.o \ sdio.o sdio_ops.o sdio_bus.o \ - sdio_cis.o sdio_io.o sdio_irq.o + sdio_cis.o sdio_io.o sdio_irq.o \ + quirks.o mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index ca1fdde29df6c..20b1c0831eac8 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -61,6 +61,8 @@ int mmc_attach_mmc(struct mmc_host *host); int mmc_attach_sd(struct mmc_host *host); int mmc_attach_sdio(struct mmc_host *host); +void mmc_fixup_device(struct mmc_card *card); + /* Module parameters */ extern int use_spi_crc; diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c new file mode 100644 index 0000000000000..aa811fc5db104 --- /dev/null +++ b/drivers/mmc/core/quirks.c @@ -0,0 +1,62 @@ +/* + * This file contains work-arounds for many known sdio hardware + * bugs. + * + * Copyright (c) 2011 Pierre Tardy + * Inspired from pci fixup code: + * Copyright (c) 1999 Martin Mares + * + */ + +#include +#include +#include +#include + +/* + * The world is not perfect and supplies us with broken mmc/sdio devices. + * For at least a part of these bugs we need a work-around + */ + +struct mmc_fixup { + u16 vendor, device; /* You can use SDIO_ANY_ID here of course */ + void (*vendor_fixup)(struct mmc_card *card, int data); + int data; +}; + +/* + * This hook just adds a quirk unconditionnally + */ +static void __maybe_unused add_quirk(struct mmc_card *card, int data) +{ + card->quirks |= data; +} + +/* + * This hook just removes a quirk unconditionnally + */ +static void __maybe_unused remove_quirk(struct mmc_card *card, int data) +{ + card->quirks &= ~data; +} + +static const struct mmc_fixup mmc_fixup_methods[] = { + { 0 } +}; + +void mmc_fixup_device(struct mmc_card *card) +{ + const struct mmc_fixup *f; + + for (f = mmc_fixup_methods; f->vendor_fixup; f++) { + if ((f->vendor == card->cis.vendor + || f->vendor == (u16) SDIO_ANY_ID) && + (f->device == card->cis.device + || f->device == (u16) SDIO_ANY_ID)) { + dev_dbg(&card->dev, "calling %pF\n", f->vendor_fixup); + f->vendor_fixup(card, f->data); + } + } +} +EXPORT_SYMBOL(mmc_fixup_device); + diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 6779207bbd108..c827f2311bfcd 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -486,6 +486,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, card = oldcard; } + mmc_fixup_device(card); if (card->type == MMC_TYPE_SD_COMBO) { err = mmc_sd_setup_card(host, card, oldcard != NULL); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 8ce082781ccb4..a498d536542f0 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -148,6 +148,8 @@ struct mmc_card { struct dentry *debugfs_root; }; +void mmc_fixup_device(struct mmc_card *dev); + #define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC) #define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD) #define mmc_card_sdio(c) ((c)->type == MMC_TYPE_SDIO) From 413b6875130e103d67ea6608f47798d1547d741e Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Sun, 13 Mar 2011 08:48:37 -0500 Subject: [PATCH 1416/2556] MMC: Extends card quicks with MMC/SD quirks matching the CID. The current mechanism is SDIO-only. This allows us to create function-specific quirks, without creating messy Kconfig dependencies, or polluting core/ with function-specific code. Change-Id: If31a151c20a8a1fddb0774674821e9fdc4aa61a0 Signed-off-by: Andrei Warkentin --- drivers/mmc/core/core.h | 2 - drivers/mmc/core/quirks.c | 58 ++++++++++-------------- drivers/mmc/core/sdio.c | 2 +- include/linux/mmc/card.h | 92 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 114 insertions(+), 40 deletions(-) diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 20b1c0831eac8..ca1fdde29df6c 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -61,8 +61,6 @@ int mmc_attach_mmc(struct mmc_host *host); int mmc_attach_sd(struct mmc_host *host); int mmc_attach_sdio(struct mmc_host *host); -void mmc_fixup_device(struct mmc_card *card); - /* Module parameters */ extern int use_spi_crc; diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c index aa811fc5db104..981c11343f457 100644 --- a/drivers/mmc/core/quirks.c +++ b/drivers/mmc/core/quirks.c @@ -11,48 +11,34 @@ #include #include #include -#include - -/* - * The world is not perfect and supplies us with broken mmc/sdio devices. - * For at least a part of these bugs we need a work-around - */ - -struct mmc_fixup { - u16 vendor, device; /* You can use SDIO_ANY_ID here of course */ - void (*vendor_fixup)(struct mmc_card *card, int data); - int data; -}; - -/* - * This hook just adds a quirk unconditionnally - */ -static void __maybe_unused add_quirk(struct mmc_card *card, int data) -{ - card->quirks |= data; -} - -/* - * This hook just removes a quirk unconditionnally - */ -static void __maybe_unused remove_quirk(struct mmc_card *card, int data) -{ - card->quirks &= ~data; -} static const struct mmc_fixup mmc_fixup_methods[] = { - { 0 } + END_FIXUP }; -void mmc_fixup_device(struct mmc_card *card) +void mmc_fixup_device(struct mmc_card *card, + const struct mmc_fixup *table) { const struct mmc_fixup *f; - - for (f = mmc_fixup_methods; f->vendor_fixup; f++) { - if ((f->vendor == card->cis.vendor - || f->vendor == (u16) SDIO_ANY_ID) && - (f->device == card->cis.device - || f->device == (u16) SDIO_ANY_ID)) { + u64 rev = cid_rev_card(card); + + /* Non-core specific workarounds. */ + if (!table) + table = mmc_fixup_methods; + + for (f = table; f->vendor_fixup; f++) { + if ((f->manfid == CID_MANFID_ANY + || f->manfid == card->cid.manfid) && + (f->oemid == CID_OEMID_ANY + || f->oemid == card->cid.oemid) && + (f->name == CID_NAME_ANY + || !strcmp(f->name, card->cid.prod_name)) && + (f->cis_vendor == card->cis.vendor + || f->cis_vendor == (u16) SDIO_ANY_ID) && + (f->cis_device == card->cis.device + || f->cis_device == (u16) SDIO_ANY_ID) && + rev >= f->rev_start && + rev <= f->rev_end) { dev_dbg(&card->dev, "calling %pF\n", f->vendor_fixup); f->vendor_fixup(card, f->data); } diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index c827f2311bfcd..ee4b3d2f766a9 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -486,7 +486,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, card = oldcard; } - mmc_fixup_device(card); + mmc_fixup_device(card, NULL); if (card->type == MMC_TYPE_SD_COMBO) { err = mmc_sd_setup_card(host, card, oldcard != NULL); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index a498d536542f0..f48c1260e6b60 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -11,6 +11,7 @@ #define LINUX_MMC_CARD_H #include +#include struct mmc_cid { unsigned int manfid; @@ -148,7 +149,93 @@ struct mmc_card { struct dentry *debugfs_root; }; -void mmc_fixup_device(struct mmc_card *dev); +/* + * The world is not perfect and supplies us with broken mmc/sdio devices. + * For at least a part of these bugs we need a work-around + */ + +struct mmc_fixup { + + /* CID-specific fields. */ + const char *name; + + /* Valid revision range */ + u64 rev_start, rev_end; + + unsigned int manfid; + unsigned short oemid; + + /* SDIO-specfic fields. You can use SDIO_ANY_ID here of course */ + u16 cis_vendor, cis_device; + + void (*vendor_fixup)(struct mmc_card *card, int data); + int data; +}; + +#define CID_MANFID_ANY (-1ul) +#define CID_OEMID_ANY ((unsigned short) -1) +#define CID_NAME_ANY (NULL) + +#define END_FIXUP { 0 } + +#define _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end, \ + _cis_vendor, _cis_device, \ + _fixup, _data) \ + { \ + .name = (_name), \ + .manfid = (_manfid), \ + .oemid = (_oemid), \ + .rev_start = (_rev_start), \ + .rev_end = (_rev_end), \ + .cis_vendor = (_cis_vendor), \ + .cis_device = (_cis_device), \ + .vendor_fixup = (_fixup), \ + .data = (_data), \ + } + +#define MMC_FIXUP_REV(_name, _manfid, _oemid, _rev_start, _rev_end, \ + _fixup, _data) \ + _FIXUP_EXT(_name, _manfid, \ + _oemid, _rev_start, _rev_end, \ + SDIO_ANY_ID, SDIO_ANY_ID, \ + _fixup, _data) \ + +#define MMC_FIXUP(_name, _manfid, _oemid, _fixup, _data) \ + MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data) + +#define SDIO_FIXUP(_vendor, _device, _fixup, _data) \ + _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_ANY, \ + CID_OEMID_ANY, 0, -1ull, \ + _vendor, _device, \ + _fixup, _data) \ + +#define cid_rev(hwrev, fwrev, year, month) \ + (((u64) hwrev) << 40 | \ + ((u64) fwrev) << 32 | \ + ((u64) year) << 16 | \ + ((u64) month)) + +#define cid_rev_card(card) \ + cid_rev(card->cid.hwrev, \ + card->cid.fwrev, \ + card->cid.year, \ + card->cid.month) + +/* + * This hook just adds a quirk unconditionnally + */ +static inline void __maybe_unused add_quirk(struct mmc_card *card, int data) +{ + card->quirks |= data; +} + +/* + * This hook just removes a quirk unconditionnally + */ +static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) +{ + card->quirks &= ~data; +} #define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC) #define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD) @@ -199,4 +286,7 @@ struct mmc_driver { extern int mmc_register_driver(struct mmc_driver *); extern void mmc_unregister_driver(struct mmc_driver *); +extern void mmc_fixup_device(struct mmc_card *card, + const struct mmc_fixup *table); + #endif From 872e407f55390148fb03112f48319c04360cc3d1 Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Wed, 9 Mar 2011 15:35:04 -0600 Subject: [PATCH 1417/2556] MMC: Support for block quirks. Block quirks implemented using core/quirks.c support. Change-Id: I81d9ad57a7ae95c60ee8026f090c8df7c75fd069 Signed-off-by: Andrei Warkentin --- drivers/mmc/card/block.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 73deb479b18cc..c11fff7ee7c23 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -693,6 +693,11 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) return 0; } +static const struct mmc_fixup blk_fixups[] = +{ + END_FIXUP +}; + static int mmc_blk_probe(struct mmc_card *card) { struct mmc_blk_data *md; @@ -720,6 +725,8 @@ static int mmc_blk_probe(struct mmc_card *card) cap_str, md->read_only ? "(ro)" : ""); mmc_set_drvdata(card, md); + mmc_fixup_device(card, blk_fixups); + #ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME mmc_set_bus_resume_policy(card->host, 1); #endif From b6fe8f23814c7f7161c2dc970fd1efc9c2335ac6 Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Wed, 6 Apr 2011 16:33:44 -0500 Subject: [PATCH 1418/2556] MMC: Expose mmc_switch to higher drivers. Needed for Sandisk workaround (manipulate EXT_CSD). Change-Id: I7bfe50a1503ac73ae072db718b60c27526521e41 Signed-off-by: Andrei Warkentin --- drivers/mmc/core/mmc_ops.c | 11 +++++++++++ drivers/mmc/core/mmc_ops.h | 1 - include/linux/mmc/core.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 60842f878dedb..1a6ac280a1cf2 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -387,6 +387,15 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc) return err; } +/** + * mmc_switch - modify EXT_CSD register + * @card: the MMC card associated with the data transfer + * @set: cmd set values + * @index: EXT_CSD register index + * @value: value to program into EXT_CSD register + * + * Modifies the EXT_CSD register for selected card. + */ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value) { int err; @@ -434,6 +443,8 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value) return 0; } +EXPORT_SYMBOL(mmc_switch); + int mmc_send_status(struct mmc_card *card, u32 *status) { int err; diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index e6d44b8a18db5..9276946fa5b71 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -20,7 +20,6 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid); int mmc_set_relative_addr(struct mmc_card *card); int mmc_send_csd(struct mmc_card *card, u32 *csd); int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); -int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value); int mmc_send_status(struct mmc_card *card, u32 *status); int mmc_send_cid(struct mmc_host *host, u32 *cid); int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 64e013f1cfb82..c11bef9ca5443 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -135,6 +135,7 @@ extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *); extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, struct mmc_command *, int); +extern int mmc_switch(struct mmc_card *, u8, u8, u8); #define MMC_ERASE_ARG 0x00000000 #define MMC_SECURE_ERASE_ARG 0x80000000 From ab7ba4e507a26af7276e3f51a9076ef71fdd6494 Mon Sep 17 00:00:00 2001 From: Andrei Warkentin Date: Wed, 6 Apr 2011 16:30:00 -0500 Subject: [PATCH 1419/2556] MMC: Fix erase/trim for certain SanDisk cards. CMD38 argument is passed through EXT_CSD[113]. Change-Id: I47e9d5e2cf44d9274a65a3b1955026185cb8f2b8 Signed-off-by: Andrei Warkentin --- drivers/mmc/card/block.c | 37 ++++++++++++++++++++++++++++++++++++- include/linux/mmc/card.h | 1 + 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index c11fff7ee7c23..78a19abc30b23 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -50,6 +50,13 @@ MODULE_ALIAS("mmc:block"); static DEFINE_MUTEX(block_mutex); +#define INAND_CMD38_ARG_EXT_CSD 113 +#define INAND_CMD38_ARG_ERASE 0x00 +#define INAND_CMD38_ARG_TRIM 0x01 +#define INAND_CMD38_ARG_SECERASE 0x80 +#define INAND_CMD38_ARG_SECTRIM1 0x81 +#define INAND_CMD38_ARG_SECTRIM2 0x88 + /* * The defaults come from config options but can be overriden by module * or bootarg options. @@ -280,6 +287,15 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) else arg = MMC_ERASE_ARG; + if (card->quirks & MMC_QUIRK_INAND_CMD38) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + INAND_CMD38_ARG_EXT_CSD, + arg == MMC_TRIM_ARG ? + INAND_CMD38_ARG_TRIM : + INAND_CMD38_ARG_ERASE); + if (err) + goto out; + } err = mmc_erase(card, from, nr, arg); out: spin_lock_irq(&md->lock); @@ -314,9 +330,26 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq, else arg = MMC_SECURE_ERASE_ARG; + if (card->quirks & MMC_QUIRK_INAND_CMD38) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + INAND_CMD38_ARG_EXT_CSD, + arg == MMC_SECURE_TRIM1_ARG ? + INAND_CMD38_ARG_SECTRIM1 : + INAND_CMD38_ARG_SECERASE); + if (err) + goto out; + } err = mmc_erase(card, from, nr, arg); - if (!err && arg == MMC_SECURE_TRIM1_ARG) + if (!err && arg == MMC_SECURE_TRIM1_ARG) { + if (card->quirks & MMC_QUIRK_INAND_CMD38) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + INAND_CMD38_ARG_EXT_CSD, + INAND_CMD38_ARG_SECTRIM2); + if (err) + goto out; + } err = mmc_erase(card, from, nr, MMC_SECURE_TRIM2_ARG); + } out: spin_lock_irq(&md->lock); __blk_end_request(req, err, blk_rq_bytes(req)); @@ -695,6 +728,8 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) static const struct mmc_fixup blk_fixups[] = { + MMC_FIXUP("SEM16G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38), + MMC_FIXUP("SEM32G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38), END_FIXUP }; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index f48c1260e6b60..48ddeeec301d5 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -122,6 +122,7 @@ struct mmc_card { /* for byte mode */ #define MMC_QUIRK_NONSTD_SDIO (1<<2) /* non-standard SDIO card attached */ /* (missing CIA registers) */ +#define MMC_QUIRK_INAND_CMD38 (1<<3) /* iNAND devices have broken CMD38 */ unsigned int erase_size; /* erase size in sectors */ unsigned int erase_shift; /* if erase unit is power 2 */ From 119f311fc5b656adf370587b7d7334b09b3ab606 Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Fri, 15 Apr 2011 15:22:09 -0700 Subject: [PATCH 1420/2556] net: Reorder incoming packets in PPPoLAC and PPPoPNS. PPP handles packet loss but does not work with out of order packets. This change performs reordering of incoming data packets within a sliding window of one second. Since sequence number is optional, receiving a packet without it will drop all queued packets. Currently the logic is triggered by incoming packets, so queued packets have to wait till another packet is arrived. It is done for simplicity since no additional locks or threads are required. For reliable protocols, a retransmission will kick it. For unreliable protocols, queued packets just seem like packet loss. Time-critical protocols might be broken, but they never work with queueing anyway. Signed-off-by: Chia-chi Yeh --- drivers/net/pppolac.c | 95 ++++++++++++++++++++++++++++++++++------ drivers/net/pppopns.c | 87 ++++++++++++++++++++++++++++++++---- include/linux/if_pppox.h | 22 +++++----- 3 files changed, 173 insertions(+), 31 deletions(-) diff --git a/drivers/net/pppolac.c b/drivers/net/pppolac.c index af3202a920a00..c94b8507d92bb 100644 --- a/drivers/net/pppolac.c +++ b/drivers/net/pppolac.c @@ -15,12 +15,15 @@ */ /* This driver handles L2TP data packets between a UDP socket and a PPP channel. - * To keep things simple, only one session per socket is permitted. Packets are - * sent via the socket, so it must keep connected to the same address. One must - * not set sequencing in ICCN but let LNS controll it. Currently this driver - * only works on IPv4 due to the lack of UDP encapsulation support in IPv6. */ + * The socket must keep connected, and only one session per socket is permitted. + * Sequencing of outgoing packets is controlled by LNS. Incoming packets with + * sequences are reordered within a sliding window of one second. Currently + * reordering only happens when a packet is received. It is done for simplicity + * since no additional locks or threads are required. This driver only works on + * IPv4 due to the lack of UDP encapsulation support in IPv6. */ #include +#include #include #include #include @@ -53,14 +56,28 @@ static inline union unaligned *unaligned(void *ptr) return (union unaligned *)ptr; } +struct meta { + __u32 sequence; + __u32 timestamp; +}; + +static inline struct meta *skb_meta(struct sk_buff *skb) +{ + return (struct meta *)skb->cb; +} + +/******************************************************************************/ + static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb) { struct sock *sk = (struct sock *)sk_udp->sk_user_data; struct pppolac_opt *opt = &pppox_sk(sk)->proto.lac; + struct meta *meta = skb_meta(skb); + __u32 now = jiffies; __u8 bits; __u8 *ptr; - /* Drop the packet if it is too short. */ + /* Drop the packet if L2TP header is missing. */ if (skb->len < sizeof(struct udphdr) + 6) goto drop; @@ -99,9 +116,12 @@ static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb) if (unaligned(ptr)->u32 != opt->local) goto drop; - /* Check the sequence if it is present. According to RFC 2661 section - * 5.4, the only thing to do is to update opt->sequencing. */ - opt->sequencing = bits & L2TP_SEQUENCE_BIT; + /* Check the sequence if it is present. */ + if (bits & L2TP_SEQUENCE_BIT) { + meta->sequence = ptr[4] << 8 | ptr[5]; + if ((__s16)(meta->sequence - opt->recv_sequence) < 0) + goto drop; + } /* Skip PPP address and control if they are present. */ if (skb->len >= 2 && skb->data[0] == PPP_ADDR && @@ -112,7 +132,54 @@ static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb) if (skb->len >= 1 && skb->data[0] & 1) skb_push(skb, 1)[0] = 0; - /* Finally, deliver the packet to PPP channel. */ + /* Drop the packet if PPP protocol is missing. */ + if (skb->len < 2) + goto drop; + + /* Perform reordering if sequencing is enabled. */ + atomic_set(&opt->sequencing, bits & L2TP_SEQUENCE_BIT); + if (bits & L2TP_SEQUENCE_BIT) { + struct sk_buff *skb1; + + /* Insert the packet into receive queue in order. */ + skb_set_owner_r(skb, sk); + skb_queue_walk(&sk->sk_receive_queue, skb1) { + struct meta *meta1 = skb_meta(skb1); + __s16 order = meta->sequence - meta1->sequence; + if (order == 0) + goto drop; + if (order < 0) { + meta->timestamp = meta1->timestamp; + skb_insert(skb1, skb, &sk->sk_receive_queue); + skb = NULL; + break; + } + } + if (skb) { + meta->timestamp = now; + skb_queue_tail(&sk->sk_receive_queue, skb); + } + + /* Remove packets from receive queue as long as + * 1. the receive buffer is full, + * 2. they are queued longer than one second, or + * 3. there are no missing packets before them. */ + skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) { + meta = skb_meta(skb); + if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf && + now - meta->timestamp < HZ && + meta->sequence != opt->recv_sequence) + break; + skb_unlink(skb, &sk->sk_receive_queue); + opt->recv_sequence = (__u16)(meta->sequence + 1); + skb_orphan(skb); + ppp_input(&pppox_sk(sk)->chan, skb); + } + return NET_RX_SUCCESS; + } + + /* Flush receive queue if sequencing is disabled. */ + skb_queue_purge(&sk->sk_receive_queue); skb_orphan(skb); ppp_input(&pppox_sk(sk)->chan, skb); return NET_RX_SUCCESS; @@ -163,14 +230,14 @@ static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb) skb->data[1] = PPP_CTRL; /* Install L2TP header. */ - if (opt->sequencing) { + if (atomic_read(&opt->sequencing)) { skb_push(skb, 10); skb->data[0] = L2TP_SEQUENCE_BIT; - skb->data[6] = opt->sequence >> 8; - skb->data[7] = opt->sequence; + skb->data[6] = opt->xmit_sequence >> 8; + skb->data[7] = opt->xmit_sequence; skb->data[8] = 0; skb->data[9] = 0; - opt->sequence++; + opt->xmit_sequence++; } else { skb_push(skb, 6); skb->data[0] = 0; @@ -246,6 +313,7 @@ static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr, po->chan.mtu = PPP_MTU - 80; po->proto.lac.local = unaligned(&addr->local)->u32; po->proto.lac.remote = unaligned(&addr->remote)->u32; + atomic_set(&po->proto.lac.sequencing, 1); po->proto.lac.backlog_rcv = sk_udp->sk_backlog_rcv; error = ppp_register_channel(&po->chan); @@ -283,6 +351,7 @@ static int pppolac_release(struct socket *sock) if (sk->sk_state != PPPOX_NONE) { struct sock *sk_udp = (struct sock *)pppox_sk(sk)->chan.private; lock_sock(sk_udp); + skb_queue_purge(&sk->sk_receive_queue); pppox_unbind_sock(sk); udp_sk(sk_udp)->encap_type = 0; udp_sk(sk_udp)->encap_rcv = NULL; diff --git a/drivers/net/pppopns.c b/drivers/net/pppopns.c index 298097127c90f..fb8198447938b 100644 --- a/drivers/net/pppopns.c +++ b/drivers/net/pppopns.c @@ -16,11 +16,14 @@ /* This driver handles PPTP data packets between a RAW socket and a PPP channel. * The socket is created in the kernel space and connected to the same address - * of the control socket. To keep things simple, packets are always sent with - * sequence but without acknowledgement. This driver should work on both IPv4 - * and IPv6. */ + * of the control socket. Outgoing packets are always sent with sequences but + * without acknowledgements. Incoming packets with sequences are reordered + * within a sliding window of one second. Currently reordering only happens when + * a packet is received. It is done for simplicity since no additional locks or + * threads are required. This driver should work on both IPv4 and IPv6. */ #include +#include #include #include #include @@ -52,21 +55,35 @@ struct header { __u32 sequence; } __attribute__((packed)); +struct meta { + __u32 sequence; + __u32 timestamp; +}; + +static inline struct meta *skb_meta(struct sk_buff *skb) +{ + return (struct meta *)skb->cb; +} + +/******************************************************************************/ + static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) { struct sock *sk = (struct sock *)sk_raw->sk_user_data; struct pppopns_opt *opt = &pppox_sk(sk)->proto.pns; + struct meta *meta = skb_meta(skb); + __u32 now = jiffies; struct header *hdr; /* Skip transport header */ skb_pull(skb, skb_transport_header(skb) - skb->data); - /* Drop the packet if it is too short. */ + /* Drop the packet if GRE header is missing. */ if (skb->len < GRE_HEADER_SIZE) goto drop; + hdr = (struct header *)skb->data; /* Check the header. */ - hdr = (struct header *)skb->data; if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local || (hdr->bits & PPTP_GRE_BITS_MASK) != PPTP_GRE_BITS) goto drop; @@ -81,6 +98,13 @@ static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) if (skb->len != ntohs(hdr->length)) goto drop; + /* Check the sequence if it is present. */ + if (hdr->bits & PPTP_GRE_SEQ_BIT) { + meta->sequence = ntohl(hdr->sequence); + if ((__s32)(meta->sequence - opt->recv_sequence) < 0) + goto drop; + } + /* Skip PPP address and control if they are present. */ if (skb->len >= 2 && skb->data[0] == PPP_ADDR && skb->data[1] == PPP_CTRL) @@ -90,7 +114,53 @@ static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) if (skb->len >= 1 && skb->data[0] & 1) skb_push(skb, 1)[0] = 0; - /* Finally, deliver the packet to PPP channel. */ + /* Drop the packet if PPP protocol is missing. */ + if (skb->len < 2) + goto drop; + + /* Perform reordering if sequencing is enabled. */ + if (hdr->bits & PPTP_GRE_SEQ_BIT) { + struct sk_buff *skb1; + + /* Insert the packet into receive queue in order. */ + skb_set_owner_r(skb, sk); + skb_queue_walk(&sk->sk_receive_queue, skb1) { + struct meta *meta1 = skb_meta(skb1); + __s32 order = meta->sequence - meta1->sequence; + if (order == 0) + goto drop; + if (order < 0) { + meta->timestamp = meta1->timestamp; + skb_insert(skb1, skb, &sk->sk_receive_queue); + skb = NULL; + break; + } + } + if (skb) { + meta->timestamp = now; + skb_queue_tail(&sk->sk_receive_queue, skb); + } + + /* Remove packets from receive queue as long as + * 1. the receive buffer is full, + * 2. they are queued longer than one second, or + * 3. there are no missing packets before them. */ + skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) { + meta = skb_meta(skb); + if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf && + now - meta->timestamp < HZ && + meta->sequence != opt->recv_sequence) + break; + skb_unlink(skb, &sk->sk_receive_queue); + opt->recv_sequence = meta->sequence + 1; + skb_orphan(skb); + ppp_input(&pppox_sk(sk)->chan, skb); + } + return NET_RX_SUCCESS; + } + + /* Flush receive queue if sequencing is disabled. */ + skb_queue_purge(&sk->sk_receive_queue); skb_orphan(skb); ppp_input(&pppox_sk(sk)->chan, skb); return NET_RX_SUCCESS; @@ -151,8 +221,8 @@ static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb) hdr->type = PPTP_GRE_TYPE; hdr->length = htons(length); hdr->call = opt->remote; - hdr->sequence = htonl(opt->sequence); - opt->sequence++; + hdr->sequence = htonl(opt->xmit_sequence); + opt->xmit_sequence++; /* Now send the packet via the delivery queue. */ skb_set_owner_w(skb, sk_raw); @@ -261,6 +331,7 @@ static int pppopns_release(struct socket *sock) if (sk->sk_state != PPPOX_NONE) { struct sock *sk_raw = (struct sock *)pppox_sk(sk)->chan.private; lock_sock(sk_raw); + skb_queue_purge(&sk->sk_receive_queue); pppox_unbind_sock(sk); sk_raw->sk_data_ready = pppox_sk(sk)->proto.pns.data_ready; sk_raw->sk_backlog_rcv = pppox_sk(sk)->proto.pns.backlog_rcv; diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h index 3e837352b4e1e..999ccd3fff378 100644 --- a/include/linux/if_pppox.h +++ b/include/linux/if_pppox.h @@ -173,19 +173,21 @@ struct pptp_opt { }; struct pppolac_opt { - __u32 local; - __u32 remote; - __u16 sequence; - __u8 sequencing; - int (*backlog_rcv)(struct sock *sk_udp, struct sk_buff *skb); + __u32 local; + __u32 remote; + __u32 recv_sequence; + __u32 xmit_sequence; + atomic_t sequencing; + int (*backlog_rcv)(struct sock *sk_udp, struct sk_buff *skb); }; struct pppopns_opt { - __u16 local; - __u16 remote; - __u32 sequence; - void (*data_ready)(struct sock *sk_raw, int length); - int (*backlog_rcv)(struct sock *sk_raw, struct sk_buff *skb); + __u16 local; + __u16 remote; + __u32 recv_sequence; + __u32 xmit_sequence; + void (*data_ready)(struct sock *sk_raw, int length); + int (*backlog_rcv)(struct sock *sk_raw, struct sk_buff *skb); }; #include From 394e2180e9362973baea635bed270abfb65d2666 Mon Sep 17 00:00:00 2001 From: Greg Goldman Date: Wed, 20 Apr 2011 11:23:06 -0700 Subject: [PATCH 1421/2556] net: wireless: bcm4329: Clean ARP offload table on IP update Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/dhd.h | 5 ++ drivers/net/wireless/bcm4329/dhd_common.c | 78 ++++++++++++++++++++++- drivers/net/wireless/bcm4329/dhd_linux.c | 55 ++++++++++++++++ 3 files changed, 137 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcm4329/dhd.h b/drivers/net/wireless/bcm4329/dhd.h index 3fb5c6bf38e38..5c0ad80804e9f 100644 --- a/drivers/net/wireless/bcm4329/dhd.h +++ b/drivers/net/wireless/bcm4329/dhd.h @@ -449,4 +449,9 @@ extern char nv_path[MOD_PARAM_PATHLEN]; extern void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar); extern void dhd_wait_event_wakeup(dhd_pub_t*dhd); +/* dhd_commn arp offload wrapers */ +extern void dhd_arp_cleanup(dhd_pub_t *dhd); +int dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen); +void dhd_arp_offload_add_ip(dhd_pub_t *dhd, u32 ipaddr); + #endif /* _dhd_h_ */ diff --git a/drivers/net/wireless/bcm4329/dhd_common.c b/drivers/net/wireless/bcm4329/dhd_common.c index 8fcb95fde827d..e50da1414c9e7 100644 --- a/drivers/net/wireless/bcm4329/dhd_common.c +++ b/drivers/net/wireless/bcm4329/dhd_common.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_common.c,v 1.5.6.8.2.6.6.69.4.25 2011/02/11 21:16:02 Exp $ + * $Id: dhd_common.c,v 1.5.6.8.2.6.6.69.4.25 2011-02-11 21:16:02 Exp $ */ #include #include @@ -1220,6 +1220,82 @@ dhd_arp_offload_enable(dhd_pub_t * dhd, int arp_enable) } #endif + +void dhd_arp_cleanup(dhd_pub_t *dhd) +{ +#ifdef ARP_OFFLOAD_SUPPORT + int ret = 0; + int iov_len = 0; + char iovbuf[128]; + + if (dhd == NULL) return; + + dhd_os_proto_block(dhd); + + iov_len = bcm_mkiovar("arp_hostip_clear", 0, 0, iovbuf, sizeof(iovbuf)); + if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, iov_len)) < 0) + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); + + iov_len = bcm_mkiovar("arp_table_clear", 0, 0, iovbuf, sizeof(iovbuf)); + if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, iov_len)) < 0) + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); + + dhd_os_proto_unblock(dhd); + +#endif /* ARP_OFFLOAD_SUPPORT */ +} + +void dhd_arp_offload_add_ip(dhd_pub_t *dhd, u32 ipaddr) +{ +#ifdef ARP_OFFLOAD_SUPPORT + int iov_len = 0; + char iovbuf[32]; + int retcode; + + dhd_os_proto_block(dhd); + + iov_len = bcm_mkiovar("arp_hostip", (char *)&ipaddr, 4, iovbuf, sizeof(iovbuf)); + retcode = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, iov_len); + + dhd_os_proto_unblock(dhd); + + if (retcode) + DHD_TRACE(("%s: ARP ip addr add failed, retcode = %d\n", + __FUNCTION__, retcode)); + else + DHD_TRACE(("%s: ARP ipaddr entry added\n", + __FUNCTION__)); +#endif /* ARP_OFFLOAD_SUPPORT */ +} + + +int dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen) +{ +#ifdef ARP_OFFLOAD_SUPPORT + int retcode; + int iov_len = 0; + + if (!buf) + return -1; + + dhd_os_proto_block(dhd); + + iov_len = bcm_mkiovar("arp_hostip", 0, 0, buf, buflen); + retcode = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, buflen); + + dhd_os_proto_unblock(dhd); + + if (retcode) { + DHD_TRACE(("%s: ioctl WLC_GET_VAR error %d\n", + __FUNCTION__, retcode)); + + return -1; + } +#endif /* ARP_OFFLOAD_SUPPORT */ + return 0; +} + + int dhd_preinit_ioctls(dhd_pub_t *dhd) { diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index 7b779d5ff9009..287fa24ba3611 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -192,6 +193,12 @@ void wifi_del_dev(void) } #endif /* defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) */ +static int dhd_device_event(struct notifier_block *this, unsigned long event, + void *ptr); + +static struct notifier_block dhd_notifier = { + .notifier_call = dhd_device_event +}; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) #include @@ -2006,6 +2013,7 @@ dhd_del_if(dhd_info_t *dhd, int ifidx) up(&dhd->sysioc_sem); } + dhd_pub_t * dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) { @@ -2162,6 +2170,8 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) register_early_suspend(&dhd->early_suspend); #endif + register_inetaddr_notifier(&dhd_notifier); + return &dhd->pub; fail: @@ -2322,6 +2332,48 @@ static struct net_device_ops dhd_ops_virt = { }; #endif +static int dhd_device_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; + dhd_info_t *dhd; + dhd_pub_t *dhd_pub; + + if (!ifa) + return NOTIFY_DONE; + + dhd = *(dhd_info_t **)netdev_priv(ifa->ifa_dev->dev); + dhd_pub = &dhd->pub; + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 31)) + if (ifa->ifa_dev->dev->netdev_ops == &dhd_ops_pri) { +#else + if (ifa->ifa_dev->dev->open == &dhd_open) { +#endif + switch (event) { + case NETDEV_UP: + DHD_TRACE(("%s: [%s] Up IP: 0x%x\n", + __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); + + dhd_arp_cleanup(dhd_pub); + break; + + case NETDEV_DOWN: + DHD_TRACE(("%s: [%s] Down IP: 0x%x\n", + __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); + + dhd_arp_cleanup(dhd_pub); + break; + + default: + DHD_TRACE(("%s: [%s] Event: %lu\n", + __FUNCTION__, ifa->ifa_label, event)); + break; + } + } + return NOTIFY_DONE; +} + int dhd_net_attach(dhd_pub_t *dhdp, int ifidx) { @@ -2395,6 +2447,7 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx) dhd->pub.mac.octet[0], dhd->pub.mac.octet[1], dhd->pub.mac.octet[2], dhd->pub.mac.octet[3], dhd->pub.mac.octet[4], dhd->pub.mac.octet[5]); + #if defined(CONFIG_WIRELESS_EXT) #if defined(CONFIG_FIRST_SCAN) #ifdef SOFTAP @@ -2460,6 +2513,8 @@ dhd_detach(dhd_pub_t *dhdp) dhd_if_t *ifp; int i; + unregister_inetaddr_notifier(&dhd_notifier); + #if defined(CONFIG_HAS_EARLYSUSPEND) if (dhd->early_suspend.suspend) unregister_early_suspend(&dhd->early_suspend); From b543dc6f7f0d6c9c1ceeecf9b9c786f03c5dcf2c Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Thu, 21 Apr 2011 21:10:13 -0400 Subject: [PATCH 1422/2556] mahimahi: q6audio: adjust in-call audio --- arch/arm/mach-msm/board-mahimahi-audio.c | 10 +++++----- arch/arm/mach-msm/qdsp6/q6audio.c | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi-audio.c b/arch/arm/mach-msm/board-mahimahi-audio.c index 88c9d475ee744..c6c7db37be861 100644 --- a/arch/arm/mach-msm/board-mahimahi-audio.c +++ b/arch/arm/mach-msm/board-mahimahi-audio.c @@ -39,12 +39,12 @@ static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { .max_gain = 1100, }, [Q6_HW_HEADSET] = { - .min_gain = -1500, - .max_gain = 1100, + .min_gain = -1700, + .max_gain = 900, }, [Q6_HW_SPEAKER] = { .min_gain = -2000, - .max_gain = 800, + .max_gain = 600, }, [Q6_HW_TTY] = { .min_gain = -1500, @@ -52,11 +52,11 @@ static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { }, [Q6_HW_BT_SCO] = { .min_gain = -2000, - .max_gain = 800, + .max_gain = 600, }, [Q6_HW_BT_A2DP] = { .min_gain = -2000, - .max_gain = 800, + .max_gain = 600, }, }; diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c index 352c94011b7e5..e8315caf8ca6f 100644 --- a/arch/arm/mach-msm/qdsp6/q6audio.c +++ b/arch/arm/mach-msm/qdsp6/q6audio.c @@ -50,12 +50,12 @@ static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { .max_gain = 1100, }, [Q6_HW_HEADSET] = { - .min_gain = -1500, - .max_gain = 1100, + .min_gain = -1700, + .max_gain = 900, }, [Q6_HW_SPEAKER] = { .min_gain = -2000, - .max_gain = 800, + .max_gain = 600, }, [Q6_HW_TTY] = { .min_gain = -1500, @@ -63,11 +63,11 @@ static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { }, [Q6_HW_BT_SCO] = { .min_gain = -2000, - .max_gain = 800, + .max_gain = 600, }, [Q6_HW_BT_A2DP] = { .min_gain = -2000, - .max_gain = 800, + .max_gain = 600, }, }; From b1be363bf20af87a6ded8fdfd76bc0c908da013d Mon Sep 17 00:00:00 2001 From: Chiranjeevi Velempati Date: Wed, 16 Mar 2011 13:46:23 +0530 Subject: [PATCH 1423/2556] USB: f_mass_storage: Disable write cache support As part of mode sense CBW command, communicate to the host that write cache support is enabled and due to this during the write commnd, the host is asking for FUA and because of this write performance is degrading. Hence during mode sense command, intimate the host that write cache is not supported. (cherry picked from commit a2da2c47967c3f80a7127b0c698aae300b9c5b91) Change-Id: I6ec66ff11181eeb70d62d31b7ddfbbf87880a885 CRs-Fixed: 278310 Signed-off-by: Chiranjeevi Velempati --- drivers/usb/gadget/f_mass_storage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index f7489fc4ffb01..5c81b6a7c85e5 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -1400,7 +1400,7 @@ static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh) memset(buf+2, 0, 10); /* None of the fields are changeable */ if (!changeable_values) { - buf[2] = 0x04; /* Write cache enable, */ + buf[2] = 0x00; /* Write cache disable, */ /* Read cache not disabled */ /* No cache retention priorities */ put_unaligned_be16(0xffff, &buf[4]); From 190407e7cf6a7747108487e0b1be770b2eec1905 Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Thu, 21 Apr 2011 21:46:31 -0400 Subject: [PATCH 1424/2556] Revert: mahimahi: acpuclock: changes which were taken from ARM11. This was done way back when, to help alleviate a drain issue that was ocurring. The calculation should be left to khz * 1000, as this is proper on this platform. --- arch/arm/mach-msm/acpuclock-qsd8x50.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-msm/acpuclock-qsd8x50.c b/arch/arm/mach-msm/acpuclock-qsd8x50.c index 7100d7f5258c9..c345adfd7f9a8 100644 --- a/arch/arm/mach-msm/acpuclock-qsd8x50.c +++ b/arch/arm/mach-msm/acpuclock-qsd8x50.c @@ -477,24 +477,22 @@ unsigned long acpuclk_power_collapse(int from_idle) { int ret = acpuclk_get_rate(); enum setrate_reason reason = (from_idle) ? SETRATE_PC_IDLE : SETRATE_PC; - ret *= 1000; if (ret > drv_state.power_collapse_khz) - acpuclk_set_rate(drv_state.power_collapse_khz, reason); - return ret; + acpuclk_set_rate(drv_state.power_collapse_khz * 1000, reason); + return ret * 1000; } unsigned long acpuclk_get_wfi_rate(void) { - return drv_state.wait_for_irq_khz; + return drv_state.wait_for_irq_khz * 1000; } unsigned long acpuclk_wait_for_irq(void) { int ret = acpuclk_get_rate(); - ret *= 1000; if (ret > drv_state.wait_for_irq_khz) - acpuclk_set_rate(drv_state.wait_for_irq_khz, 1); - return ret; + acpuclk_set_rate(drv_state.wait_for_irq_khz * 1000, 1); + return ret * 1000; } void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *clkdata) From 62d7159272da3593b18d34743b10de1991903696 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Thu, 14 Apr 2011 21:30:06 -0400 Subject: [PATCH 1425/2556] media: video: Update ov8810 sensor driver from HTC --- drivers/media/video/msm/ov8810.c | 339 ++++++++++++++++++------------- 1 file changed, 200 insertions(+), 139 deletions(-) diff --git a/drivers/media/video/msm/ov8810.c b/drivers/media/video/msm/ov8810.c index 8e29135d7e42b..e0f1b6adef579 100644 --- a/drivers/media/video/msm/ov8810.c +++ b/drivers/media/video/msm/ov8810.c @@ -230,6 +230,7 @@ int global_mode; extern unsigned char *get_cam_awb_cal(void); static int sensor_probe_node = 0; +static int preview_frame_count = 0; static struct wake_lock ov8810_wake_lock; @@ -735,7 +736,7 @@ static int ov8810_i2c_rxdata(unsigned short saddr, CDBG("%s: raddr=0x%X\n", __func__, *rxdata); if (i2c_transfer(ov8810_client->adapter, msgs, 2) < 0) { - pr_err("ov8810_i2c_rxdata failed!\n"); + pr_err("[CAM]ov8810_i2c_rxdata failed!\n"); return -EIO; } CDBG("%s: rxdata=0x%X\n", __func__, *rxdata); @@ -754,7 +755,7 @@ static int32_t ov8810_i2c_txdata(unsigned short saddr, }, }; if (i2c_transfer(ov8810_client->adapter, msg, 1) < 0) { - pr_err("ov8810_i2c_txdata faild 0x%x\n", ov8810_client->addr); + pr_err("[CAM]ov8810_i2c_txdata faild 0x%x\n", ov8810_client->addr); return -EIO; } @@ -779,7 +780,7 @@ static int32_t ov8810_i2c_read(unsigned short raddr, rc = ov8810_i2c_rxdata(ov8810_client->addr, buf, rlen); if (rc < 0) { - pr_err("ov8810_i2c_read 0x%x failed!\n", raddr); + pr_err("[CAM]ov8810_i2c_read 0x%x failed!\n", raddr); printk(KERN_ERR "starting read retry policy count:%d\n", count); udelay(10); count++; @@ -814,7 +815,7 @@ static int32_t ov8810_i2c_write_b(unsigned short saddr, rc = ov8810_i2c_txdata(saddr, buf, 3); if (rc < 0) { - pr_err("i2c_write_b failed, addr = 0x%x, val = 0x%x!\n", + pr_err("[CAM]i2c_write_b failed, addr = 0x%x, val = 0x%x!\n", waddr, bdata); pr_err(KERN_ERR "starting read retry policy count:%d\n", count); udelay(10); @@ -835,13 +836,13 @@ static int32_t ov8810_i2c_write_b(unsigned short saddr, static int ov8810_update_lsc_table(struct sensor_cfg_data *cdata) { int i = 0; - pr_info("[LSC calibration]ov8810_update_lsc_table\n"); + pr_info("[CAM][LSC calibration]ov8810_update_lsc_table\n"); for (i = 0; i < 144; i++) { ov8810_i2c_write_b( ov8810_client->addr, cdata->cfg.lsctable.lsc_table[i].reg_addr, cdata->cfg.lsctable.lsc_table[i].reg_val); - pr_info("[LSC calibration]update_lsc_table: 0x%x, 0x%x\n", + pr_info("[CAM][LSC calibration]update_lsc_table: 0x%x, 0x%x\n", cdata->cfg.lsctable.lsc_table[i].reg_addr, cdata->cfg.lsctable.lsc_table[i].reg_val); } @@ -896,7 +897,7 @@ static int ov8810_i2c_read_fuseid(struct sensor_cfg_data *cdata) cdata->cfg.fuse.fuse_id_word3 = 0; cdata->cfg.fuse.fuse_id_word4 = 0; for (count = 0; count < MAX_FUSE_ID_INFO; count++) - pr_info("Ov8810 Get fuse: fuse_id[%d]: %x\n", + pr_info("[CAM]Ov8810 Get fuse: fuse_id[%d]: %x\n", count, fuse_id[count]); return 0; } @@ -905,7 +906,7 @@ static int ov8810_i2c_read_fuseid(struct sensor_cfg_data *cdata) static int32_t ov8810_af_i2c_write(uint16_t data) { uint8_t code_val_msb, code_val_lsb; /* S3_to_0; */ - uint32_t rc = 0; + int32_t rc = 0; /* S3_to_0 = 0x9; S[3:0] */ code_val_msb = data >> 4; /* D[9:4] */ code_val_lsb = ((data & 0x000F) << 4) | S3_to_0; @@ -916,14 +917,14 @@ static int32_t ov8810_af_i2c_write(uint16_t data) OV8810_AF_MSB, code_val_msb); if (rc < 0) { - pr_err("Unable to write code_val_msb = %d\n", code_val_msb); + pr_err("[CAM]Unable to write code_val_msb = %d\n", code_val_msb); return rc; } rc = ov8810_i2c_write_b(ov8810_client->addr, OV8810_AF_LSB, code_val_lsb); if (rc < 0) { - pr_err("Unable to write code_val_lsb = %disclaimer\n", + pr_err("[CAM]Unable to write code_val_lsb = %disclaimer\n", code_val_lsb); return rc; } @@ -949,7 +950,7 @@ static int32_t ov8810_move_focus(int direction, int32_t num_steps) } else if (direction == MOVE_FAR) { step_direction = -1; } else { - pr_err("Illegal focus direction\n"); + pr_err("[CAM]Illegal focus direction\n"); return -EINVAL;; /* CAMERA_INVALID_PARM; */ } @@ -1243,7 +1244,7 @@ static int32_t ov8810_write_exp_gain CDBG("updated_BLC = %d\n", updated_BLC); if (updated_BLC == 5) { /*50 BLC trigger by gain 40 BLC every frame */ - pr_info("### BLC to 0x50 ###\n"); + pr_info("[CAM]### BLC to 0x50 ###\n"); #if 0 ov8810_i2c_write_b(ov8810_client->addr, 0x3071, 0x50); #endif @@ -1268,26 +1269,26 @@ static int32_t ov8810_write_exp_gain } #if 0 - pr_info("Stella: backup_line_gain[0].line = %d\n", + pr_info("[CAM]Stella: backup_line_gain[0].line = %d\n", backup_line_gain[0].line); - pr_info("Stella: backup_line_gain[0].mul = %d\n", + pr_info("[CAM]Stella: backup_line_gain[0].mul = %d\n", backup_line_gain[0].mul); - pr_info("Stella: backup_line_gain[0].gain = %d\n", + pr_info("[CAM]Stella: backup_line_gain[0].gain = %d\n", backup_line_gain[0].gain); - pr_info("Stella: backup_line_gain[0].extra_line_length = %d\n", + pr_info("[CAM]Stella: backup_line_gain[0].extra_line_length = %d\n", backup_line_gain[0].extra_line_length); - pr_info("Stella: backup_line_gain[1].line = %d\n", + pr_info("[CAM]Stella: backup_line_gain[1].line = %d\n", backup_line_gain[1].line); - pr_info("Stella: backup_line_gain[1].mul = %d\n", + pr_info("[CAM]Stella: backup_line_gain[1].mul = %d\n", backup_line_gain[1].mul); - pr_info("Stella: backup_line_gain[1].gain = %d\n", + pr_info("[CAM]Stella: backup_line_gain[1].gain = %d\n", backup_line_gain[1].gain); - pr_info("Stella: backup_line_gain[1].extra_line_length = %d\n", + pr_info("[CAM]Stella: backup_line_gain[1].extra_line_length = %d\n", backup_line_gain[1].extra_line_length); - pr_info("Stella: phy_line=%d\n", phy_line); - pr_info("Stella: phy_gain=%d\n", phy_gain); - pr_info("Stella: phy_extra_line_length=%d\n", phy_extra_line_length); + pr_info("[CAM]Stella: phy_line=%d\n", phy_line); + pr_info("[CAM]Stella: phy_gain=%d\n", phy_gain); + pr_info("[CAM]Stella: phy_extra_line_length=%d\n", phy_extra_line_length); #endif extra_line_msb = (uint16_t)(phy_extra_line_length & 0xFF00) >> 8; @@ -1303,7 +1304,7 @@ static int32_t ov8810_write_exp_gain rc = ov8810_i2c_read(OV8810_REG_MUL_GAIN, &ori_reg_mul_gain, 2); if (rc < 0) { - pr_err("read OV8810_REG_MUL_GAIN fail\n"); + pr_err("[CAM]read OV8810_REG_MUL_GAIN fail\n"); return rc; } @@ -1379,13 +1380,13 @@ static int32_t ov8810_write_exp_gain } if (ov8810_ctrl->sensormode == SENSOR_RAW_SNAPSHOT_MODE) { - pr_info("sleep 500 ms for safety raw snapshot"); + pr_info("[CAM]sleep 500 ms for safety raw snapshot"); msleep(500); } /* STREAM ON for SNAPSHOT */ if (ov8810_ctrl->sensormode == SENSOR_SNAPSHOT_MODE) { - pr_info("ov8810_ctrl: STREAM ON for SNAPSHOT\n"); + pr_info("[CAM]ov8810_ctrl: STREAM ON for SNAPSHOT\n"); rc = ov8810_i2c_write_b(ov8810_client->addr, OV8810_REG_MODE_SELECT, OV8810_MODE_SELECT_STREAM); @@ -1540,7 +1541,7 @@ static int32_t HTC_update_ov8810_lsc_registers(void) } for (i = 0; i < LSC_table_length; i++) { - pr_info("[LSC calibration] read LSC table 0x%x, 0x%x\n", + pr_info("[CAM][LSC calibration] read LSC table 0x%x, 0x%x\n", awb_lsc_data_ptr->LSC_table[i].reg_addr, awb_lsc_data_ptr->LSC_table[i].reg_val); } @@ -1551,7 +1552,7 @@ static int32_t HTC_update_ov8810_lsc_registers(void) 150 * sizeof(struct reg_addr_val_pair_struct), 0) && awb_lsc_data_ptr->LSC_table_CRC != 0) { - pr_info("[LSC calibration]checksum pass,use calibrated LSC\n"); + pr_info("[CAM][LSC calibration]checksum pass,use calibrated LSC\n"); for (i = 0; i < LSC_table_length; i++) { ov8810_i2c_write_b(ov8810_client->addr, @@ -1565,7 +1566,7 @@ static int32_t HTC_update_ov8810_lsc_registers(void) OV8810_REG_MODE_SELECT, OV8810_MODE_SELECT_STREAM); } else {/*use default LSC table*/ - pr_info("[LSC calibration]checksum fail\n"); + pr_info("[CAM][LSC calibration]checksum fail\n"); return false; } return true; @@ -1576,6 +1577,8 @@ static int32_t initialize_ov8810_registers(void) int32_t i, array_length; int32_t rc = 0; + struct msm_camera_sensor_info *sdata = ov8810_pdev->dev.platform_data; + mdelay(5); ov8810_i2c_write_b( ov8810_client->addr, @@ -1600,8 +1603,9 @@ static int32_t initialize_ov8810_registers(void) } /*use calibrated LSC table*/ + if (!sdata->sensor_lc_disable) { /* 0902 disable old LSC method */ if (HTC_update_ov8810_lsc_registers()) { - pr_info("[LSC calibration] use calibrated LSC table done!\n"); + pr_info("[CAM][LSC calibration] use calibrated LSC table done!\n"); } else {/*use default LSC table*/ array_length = sizeof(lsc_table_array) / sizeof(lsc_table_array[0]); @@ -1611,8 +1615,13 @@ static int32_t initialize_ov8810_registers(void) lsc_table_array[i].reg_addr, lsc_table_array[i].reg_val); } - pr_info("[LSC calibration] use default LSC table done\n"); + pr_info("[CAM][LSC calibration] use default LSC table done\n"); } + } else { + /* add streaming on */ + ov8810_i2c_write_b(ov8810_client->addr, + OV8810_REG_MODE_SELECT, OV8810_MODE_SELECT_STREAM); + } return rc; } /* end of initialize_ov8810_ov8m0vc_registers. */ @@ -1628,12 +1637,12 @@ static int32_t ov8810_setting(int rt) write_cnt = 0; - pr_info("ov8810_setting rt = %d\n", rt); + pr_info("[CAM]ov8810_setting rt = %d\n", rt); if (rt == FULL_SIZE) { ov8810_i2c_read(0x30b7, &i2c_ret, 1); - pr_info("0x30b7, i2c_ret = 0x%X\n", i2c_ret); + pr_info("[CAM]0x30b7, i2c_ret = 0x%X\n", i2c_ret); /*Retry writing group update bottom to ensure capture settings can be updated Weiting0331*/ while (i2c_ret != 0x84) { @@ -1649,7 +1658,7 @@ static int32_t ov8810_setting(int rt) msleep(50); ov8810_i2c_read(0x30b7, &i2c_ret, 1); - pr_info("retry 0x30b7, i2c_ret = 0x%X\n", i2c_ret); + pr_info("[CAM]retry 0x30b7, i2c_ret = 0x%X\n", i2c_ret); }; } @@ -1661,7 +1670,7 @@ static int32_t ov8810_setting(int rt) } ov8810_i2c_read(OV8810_REG_MODE_SELECT, &i2c_ret, 1); - pr_info("OV8810_REG_MODE_SELECT, i2c_ret = 0x%X\n", i2c_ret); + pr_info("[CAM]OV8810_REG_MODE_SELECT, i2c_ret = 0x%X\n", i2c_ret); switch (rt) { @@ -1686,6 +1695,7 @@ static int32_t ov8810_setting(int rt) uint16_t fl_line = 0; fl_line = OV8810_QTR_SIZE_HEIGHT + ov8810_ver_qtr_blk_lines_array[cam_mode_sel]; + pr_info("%s fl_line = %d\n", __func__, __LINE__); rc = ov8810_i2c_write_b(ov8810_client->addr, REG_FRAME_LENGTH_LINES_MSB, (fl_line & 0xFF00) >> 8); @@ -1698,7 +1708,7 @@ static int32_t ov8810_setting(int rt) return rc; #if 0 if (cam_mode_sel > 0) { - pr_info("andy write binning ctrl 0x00, cam_mode_sel %d\n", cam_mode_sel); + pr_info("[CAM]andy write binning ctrl 0x00, cam_mode_sel %d\n", cam_mode_sel); rc = ov8810_i2c_write_b(ov8810_client->addr, //weiting ori c0 REG_BINNING_CONTROL, 0x00); if (rc < 0) @@ -1744,7 +1754,7 @@ static int32_t ov8810_setting(int rt) rc = ov8810_i2c_read(OV8810_REG_MUL_GAIN, &ori_reg_mul_gain, 2); if (rc < 0) { - pr_err("read OV8810_REG_MUL_GAIN fail\n"); + pr_err("[CAM]read OV8810_REG_MUL_GAIN fail\n"); return rc; } ori_reg_mul_gain_8bit = @@ -1789,8 +1799,7 @@ static int32_t ov8810_setting(int rt) break; case FULL_SIZE: - if (rc < 0) - return rc; + array_length = sizeof(ov8810_full_settings_array) / sizeof(ov8810_full_settings_array[0]); /* Configure sensor for QXGA capture mode */ @@ -1811,7 +1820,7 @@ static int32_t ov8810_setting(int rt) } /*disablt LSC for calibration*/ - pr_info("[LSC calibration] global_mode=%d!!!!\n", global_mode); + pr_info("[CAM][LSC calibration] global_mode=%d!!!!\n", global_mode); /*take raw picture for LSC calibration*/ if (global_mode) { /*disable sensor LSC*/ @@ -1820,7 +1829,7 @@ static int32_t ov8810_setting(int rt) rc = ov8810_i2c_write_b(ov8810_client->addr, 0x30f8, 0x00); /*mirror off*/ rc = ov8810_i2c_write_b(ov8810_client->addr, 0x3316, 0x02); - pr_info("[LSC calibration]turn off LSC!Mirror On\n"); + pr_info("[CAM][LSC calibration]turn off LSC!Mirror On\n"); /*fix gain & linecount*/ /*Gain=0x9,exp=008d*/ @@ -1830,7 +1839,7 @@ static int32_t ov8810_setting(int rt) rc = ov8810_i2c_write_b(ov8810_client->addr, 0x3002, 0x00); /*AEC_LSB*/ rc = ov8810_i2c_write_b(ov8810_client->addr, 0x3003, 0x8d); - pr_info("[LSC calibration]fix gain & linecount\n"); + pr_info("[CAM][LSC calibration]fix gain & linecount\n"); global_mode = 0; } @@ -1862,7 +1871,9 @@ static int32_t ov8810_video_config(int mode) ov8810_ctrl->sensormode = mode; - pr_info("%s cam_mode_sel %d cur_sel %d \n", __func__, cam_mode_sel, cur_sel); + pr_info("[CAM]%s cam_mode_sel %d cur_sel %d \n", __func__, cam_mode_sel, cur_sel); + + preview_frame_count = 0; if (ov8810_ctrl->curr_res != ov8810_ctrl->prev_res || pre_sel != cur_sel @@ -1931,14 +1942,14 @@ static int32_t ov8810_set_sensor_mode(int mode, break; case SENSOR_SNAPSHOT_MODE: - pr_info("KPI PA: start sensor snapshot config: %d\n", __LINE__); + pr_info("[CAM]KPI PA: start sensor snapshot config: %d\n", __LINE__); sinfo->kpi_sensor_start = ktime_to_ns(ktime_get()); rc = ov8810_snapshot_config(mode); break; case SENSOR_RAW_SNAPSHOT_MODE: /*global_mode = 1; //20100330 vincent lsc calibration*/ - pr_info("KPI PA: start sensor snapshot config: %d\n", __LINE__); + pr_info("[CAM]KPI PA: start sensor snapshot config: %d\n", __LINE__); sinfo->kpi_sensor_start = ktime_to_ns(ktime_get()); rc = ov8810_raw_snapshot_config(mode); break; @@ -1962,31 +1973,33 @@ static int ov8810_probe_read_id(const struct msm_camera_sensor_info *data) uint16_t chipidh = 0; /*, chipidl;*/ uint16_t def_chipid = 0; msleep(20); - pr_info("%s, ov8810_probe_init_sensor 1\n", __func__); + pr_info("[CAM]%s, ov8810_probe_init_sensor 1\n", __func__); /* 3. Read sensor Model ID: */ if (ov8810_i2c_read(OV8810_PIDH_REG, &chipidh, 2) < 0) { rc = -1; - pr_err("read sensor id fail\n"); + pr_err("[CAM]read sensor id fail\n"); } - pr_info("ov8810 model_id + ver = 0x%x\n", chipidh); + pr_info("[CAM]ov8810 model_id + ver = 0x%x\n", chipidh); /* 4. Compare sensor ID to OV8810 ID: */ def_chipid = (((OV8810_PID << 8) & 0xFF00) + (OV8810_VER & 0x00FF)); - pr_info("%s, Expected id=0x%x\n", __func__, def_chipid); + pr_info("[CAM]%s, Expected id=0x%x\n", __func__, def_chipid); if (chipidh < def_chipid) { rc = -ENODEV; - pr_err("read sensor id incorrect\n"); + pr_err("[CAM]read sensor id incorrect\n"); } - pr_info("%s, vreg_get vreg_af_actuator\n", __func__); + pr_info("[CAM]%s, vreg_get vreg_af_actuator\n", __func__); vreg_af_actuator = vreg_get(0, "gp5"); if (IS_ERR(vreg_af_actuator)) return PTR_ERR(vreg_af_actuator); +#ifdef CONFIG_ARCH_QSD8X50 data->camera_set_source(MAIN_SOURCE); - pr_info(" ov8810_probe_init_sensor finishes\n"); +#endif + pr_info("[CAM]ov8810_probe_init_sensor finishes\n"); return rc; } @@ -2000,12 +2013,12 @@ static int ov8810_sensor_open_init(struct msm_camera_sensor_info *data) uint16_t ov8810_nl_region_code_per_step = 35; /*101;*/ uint16_t ov8810_l_region_code_per_step = 20; /*18;*/ int timeout; - pr_info("Calling ov8810_sensor_open_init\n"); + pr_info("[CAM]Calling ov8810_sensor_open_init\n"); down(&ov8810_sem); if (data == NULL) { - pr_info("data is a NULL pointer\n"); + pr_info("[CAM]data is a NULL pointer\n"); return -EINVAL; } /*check whether resume done*/ @@ -2013,7 +2026,7 @@ static int ov8810_sensor_open_init(struct msm_camera_sensor_info *data) ov8810_event.event_wait, ov8810_event.waked_up, 30*HZ); - pr_info("wait event : %d timeout:%d\n", ov8810_event.waked_up, timeout); + pr_info("[CAM]wait event : %d timeout:%d\n", ov8810_event.waked_up, timeout); if (timeout == 0) { up(&ov8810_sem); return rc; @@ -2021,7 +2034,7 @@ static int ov8810_sensor_open_init(struct msm_camera_sensor_info *data) msm_camio_probe_on(ov8810_pdev); ov8810_ctrl = kzalloc(sizeof(struct ov8810_ctrl), GFP_KERNEL); if (!ov8810_ctrl) { - pr_err("ov8810_init failed!\n"); + pr_err("[CAM]ov8810_init failed!\n"); rc = -ENOMEM; goto init_done; } @@ -2037,19 +2050,22 @@ static int ov8810_sensor_open_init(struct msm_camera_sensor_info *data) /*switch pclk and mclk between main cam and 2nd cam*/ /*only for supersonic*/ - pr_info("doing clk switch (ov8810)\n"); + pr_info("[CAM]doing clk switch (ov8810)\n"); if(data->camera_clk_switch != NULL) data->camera_clk_switch(); - /*PWD config*/ - pr_info("%s, GPIO(%d) sensor_pwd 0\n", __func__, data->sensor_pwd); + msm_camio_camif_pad_reg_reset(); + msleep(20); + + /*PWD and RST config*/ + pr_info("[CAM]%s, GPIO(%d) sensor_pwd 0\n", __func__, data->sensor_pwd); rc = gpio_request(data->sensor_pwd, "ov8810"); if (!rc) gpio_direction_output(data->sensor_pwd, 0); else - pr_err("GPIO (%d) request faile\n", data->sensor_pwd); + pr_err("[CAM]GPIO (%d) request faile\n", data->sensor_pwd); gpio_free(data->sensor_pwd); - mdelay(3); + msleep(5); /* enable mclk first */ msm_camio_clk_rate_set(OV8810_DEFAULT_CLOCK_RATE); @@ -2060,34 +2076,26 @@ static int ov8810_sensor_open_init(struct msm_camera_sensor_info *data) if (!rc) gpio_direction_output(data->sensor_reset, 1); else - pr_err("GPIO (%d) request faile\n", data->sensor_reset); + pr_err("[CAM]GPIO (%d) request faile\n", data->sensor_reset); gpio_free(data->sensor_reset); - msleep(20); + msleep(1); + /*read sensor id*/ rc = ov8810_probe_read_id(data); ov8810_ctrl->sensormode = SENSOR_PREVIEW_MODE ; - pr_info("%s, initialize_ov8810_registers: %d\n", __func__, __LINE__); + pr_info("[CAM]%s, initialize_ov8810_registers: %d\n", __func__, __LINE__); if (rc < 0) goto init_fail; - #if 1 /*move to probe up sensor*/ +#ifdef CONFIG_ARCH_QSD8X50 /* Initialize Sensor registers */ rc = initialize_ov8810_registers(); if (rc < 0) return rc; - pr_info("%s, ov8810_setting preview %d\n", __func__, __LINE__); - - if (ov8810_ctrl->curr_res != ov8810_ctrl->prev_res) { - rc = ov8810_setting(ov8810_ctrl->prev_res); - if (rc < 0) - goto init_fail; - } else { - ov8810_ctrl->curr_res = ov8810_ctrl->prev_res; - } - #endif +#endif - pr_info("%s, enable AF actuator %d\n", __func__, __LINE__); + pr_info("[CAM]%s, enable AF actuator %d\n", __func__, __LINE__); /* enable AF actuator */ rc = vreg_enable(vreg_af_actuator); @@ -2096,18 +2104,18 @@ static int ov8810_sensor_open_init(struct msm_camera_sensor_info *data) rc = vreg_set_level(vreg_af_actuator, 2800); /*2v8*/ if (rc) { - pr_err("vreg_af_actuator vreg_set_level 2v8 failed!\n"); + pr_err("[CAM]vreg_af_actuator vreg_set_level 2v8 failed!\n"); goto init_fail; } } else { - pr_err("vreg_af_actuator vreg_enable failed!\n"); - goto init_fail; + pr_err("[CAM]vreg_af_actuator vreg_enable failed!\n"); + goto init_fail; } msleep(20); - pr_info("%s, set step_position_table %d\n", __func__, __LINE__); + pr_info("[CAM]%s, set step_position_table %d\n", __func__, __LINE__); ov8810_ctrl->fps = 30*Q8; @@ -2126,16 +2134,14 @@ static int ov8810_sensor_open_init(struct msm_camera_sensor_info *data) } /* generate test pattern */ - pr_info("%s, generate test pattern, %d, rc=%d\n", + pr_info("[CAM]%s, generate test pattern, %d, rc=%d\n", __func__, __LINE__, rc); - if (rc < 0) - goto init_fail; - else + if (rc >= 0) goto init_done; /* reset the driver state */ init_fail: - pr_err("%s: init_fail\n", __func__); + pr_err("[CAM]%s: init_fail\n", __func__); vreg_disable(vreg_af_actuator); if (ov8810_ctrl) { kfree(ov8810_ctrl); @@ -2143,7 +2149,7 @@ static int ov8810_sensor_open_init(struct msm_camera_sensor_info *data) } init_done: up(&ov8810_sem); - pr_info("%s: init_done\n", __func__); + pr_info("[CAM]%s: init_done\n", __func__); return rc; } /*endof ov8810_sensor_open_init*/ @@ -2164,16 +2170,16 @@ static int ov8810_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { int rc = 0; - pr_info("ov8810_probe called!\n"); + pr_info("[CAM]ov8810_probe called!\n"); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - pr_err("i2c_check_functionality failed\n"); + pr_err("[CAM]i2c_check_functionality failed\n"); goto probe_failure; } ov8810_sensorw = kzalloc(sizeof(struct ov8810_work), GFP_KERNEL); if (!ov8810_sensorw) { - pr_err("kzalloc failed.\n"); + pr_err("[CAM]kzalloc failed.\n"); rc = -ENOMEM; goto probe_failure; } @@ -2184,23 +2190,32 @@ static int ov8810_i2c_probe(struct i2c_client *client, msleep(50); - pr_info("ov8810_probe successed! rc = %d\n", rc); + pr_info("[CAM]ov8810_probe successed! rc = %d\n", rc); return 0; probe_failure: - pr_err("ov8810_probe failed! rc = %d\n", rc); + pr_err("[CAM]ov8810_probe failed! rc = %d\n", rc); return rc; } static int ov8810_probe_init_done(const struct msm_camera_sensor_info *data) { - gpio_request(data->sensor_pwd, "ov8810"); - gpio_direction_output(data->sensor_pwd, 1); + int rc; + rc = gpio_request(data->sensor_pwd, "ov8810"); + if (!rc) + gpio_direction_output(data->sensor_pwd, 1); + else + pr_err("[CAM]GPIO (%d) request faile\n", data->sensor_pwd); gpio_free(data->sensor_pwd); mdelay(1); - gpio_request(data->sensor_reset, "ov8810"); - gpio_direction_output(data->sensor_reset, 0); +#ifdef CONFIG_ARCH_QSD8X50 + rc = gpio_request(data->sensor_reset, "ov8810"); + if (!rc) + gpio_direction_output(data->sensor_reset, 0); + else + pr_err("GPIO (%d) request faile\n", data->sensor_reset); gpio_free(data->sensor_reset); +#endif return 0; } @@ -2213,16 +2228,16 @@ static int ov8810_suspend(struct platform_device *pdev, pm_message_t state) return 0; ov8810_event.waked_up = 0; - pr_info("ov8810: camera suspend\n"); + pr_info("[CAM]ov8810: camera suspend\n"); - pr_info("%s, vreg_af_actuator vreg_disable\n", __func__); + pr_info("[CAM]%s, vreg_af_actuator vreg_disable\n", __func__); vreg_disable(vreg_af_actuator); rc = gpio_request(sinfo->sensor_reset, "ov8810"); if (!rc) gpio_direction_output(sinfo->sensor_reset, 0); else - pr_info("ov8810: request GPIO(sensor_reset) :%d faile\n", + pr_info("[CAM]ov8810: request GPIO(sensor_reset) :%d faile\n", sinfo->sensor_reset); gpio_free(sinfo->sensor_reset); @@ -2231,12 +2246,12 @@ static int ov8810_suspend(struct platform_device *pdev, pm_message_t state) if (!rc) gpio_direction_output(sinfo->sensor_pwd, 0); else - pr_info("ov8810: request GPIO(sensor_reset) :%d faile\n", + pr_info("[CAM]ov8810: request GPIO(sensor_reset) :%d faile\n", sinfo->sensor_pwd); gpio_free(sinfo->sensor_pwd); - pr_info("ov8810:suspend done\n"); + pr_info("[CAM]ov8810:suspend done\n"); return rc; } @@ -2244,7 +2259,7 @@ static void ov8810_resume(struct early_suspend *handler) { int rc = 0; struct msm_camera_sensor_info *sinfo = ov8810_pdev->dev.platform_data; - pr_info("ov8810_resume\n"); + pr_info("[CAM]ov8810_resume\n"); /*check whether need resume*/ if (!sinfo->need_suspend) @@ -2252,18 +2267,18 @@ static void ov8810_resume(struct early_suspend *handler) /*check whether already suspend*/ if (ov8810_event.waked_up == 1) { - pr_info("Ov8810: No nesesary to do Resume\n"); + pr_info("[CAM]Ov8810: No nesesary to do Resume\n"); return; } mdelay(5); /*power down setup*/ - pr_info("%s, sensor_pwd 0\n", __func__); + pr_info("[CAM]%s, sensor_pwd 0\n", __func__); rc = gpio_request(sinfo->sensor_pwd, "ov8810"); if (!rc) gpio_direction_output(sinfo->sensor_pwd, 0); else - pr_err("GPIO (%d) request faile\n", sinfo->sensor_pwd); + pr_err("[CAM]GPIO (%d) request faile\n", sinfo->sensor_pwd); gpio_free(sinfo->sensor_pwd); mdelay(5); /*reset setup */ @@ -2271,16 +2286,16 @@ static void ov8810_resume(struct early_suspend *handler) if (!rc) gpio_direction_output(sinfo->sensor_reset, 1); else - pr_err("GPIO (%d) request faile\n", sinfo->sensor_reset); + pr_err("[CAM]GPIO (%d) request faile\n", sinfo->sensor_reset); gpio_free(sinfo->sensor_reset); /*init msm,clk ,GPIO,enable*/ - pr_info("%s, msm_camio_probe_on\n", __func__); + pr_info("[CAM]%s, msm_camio_probe_on\n", __func__); msm_camio_probe_on(ov8810_pdev); msm_camio_clk_enable(CAMIO_MDC_CLK); /*set MCLK*/ - pr_info("%s, msm_camio_clk_rate_set = %d\n", + pr_info("[CAM]%s, msm_camio_clk_rate_set = %d\n", __func__, OV8810_DEFAULT_CLOCK_RATE); msm_camio_clk_rate_set(OV8810_DEFAULT_CLOCK_RATE); msleep(100); @@ -2288,7 +2303,7 @@ static void ov8810_resume(struct early_suspend *handler) /*read sensor id*/ rc = ov8810_probe_read_id(sinfo); if (rc < 0) - pr_err("OV8810 resume faile :can not read sensor ID\n"); + pr_err("[CAM]OV8810 resume faile :can not read sensor ID\n"); /* Initialize Sensor registers */ rc = initialize_ov8810_registers(); @@ -2302,7 +2317,7 @@ static void ov8810_resume(struct early_suspend *handler) msm_camio_clk_disable(CAMIO_MDC_CLK); ov8810_event.waked_up = 1; - pr_info("ov8810:resume done\n"); + pr_info("[CAM]ov8810:resume done\n"); wake_up(&ov8810_event.event_wait); return; } @@ -2389,31 +2404,31 @@ static struct kobject *android_ov8810; static int ov8810_sysfs_init(void) { int ret = 0; - pr_info("ov8810:kobject creat and add\n"); + pr_info("[CAM]ov8810:kobject creat and add\n"); android_ov8810 = kobject_create_and_add("android_camera", NULL); if (android_ov8810 == NULL) { - pr_info("ov8810_sysfs_init: subsystem_register failed\n"); + pr_info("[CAM]ov8810_sysfs_init: subsystem_register failed\n"); ret = -ENOMEM; return ret ; } - pr_info("Ov8810:sysfs_create_file\n"); + pr_info("[CAM]Ov8810:sysfs_create_file\n"); ret = sysfs_create_file(android_ov8810, &dev_attr_sensor.attr); if (ret) { - pr_info("ov8810_sysfs_init: sysfs_create_file failed\n"); + pr_info("[CAM]ov8810_sysfs_init: sysfs_create_file failed\n"); ret = -EFAULT; goto error; } ret = sysfs_create_file(android_ov8810, &dev_attr_cam_mode.attr); if (ret) { - pr_info("ov8810_sysfs_init: dev_attr_cam_mode failed\n"); + pr_info("[CAM]ov8810_sysfs_init: dev_attr_cam_mode failed\n"); ret = -EFAULT; goto error; } ret = sysfs_create_file(android_ov8810, &dev_attr_node.attr); if (ret) { - pr_info("ov8810_sysfs_init: dev_attr_node failed\n"); + pr_info("[CAM]ov8810_sysfs_init: dev_attr_node failed\n"); ret = -EFAULT; goto error; } @@ -2425,6 +2440,16 @@ static int ov8810_sysfs_init(void) return ret; } +#ifdef CONFIG_ARCH_MSM7X30 +uint8_t ov8810_preview_skip_frame(void) +{ + if (ov8810_ctrl->sensormode == SENSOR_PREVIEW_MODE && preview_frame_count < 2) { + preview_frame_count++; + return 1; + } + return 0; +} +#endif int ov8810_sensor_config(void __user *argp) { @@ -2542,12 +2567,12 @@ int ov8810_sensor_config(void __user *argp) case CFG_SET_DEFAULT_FOCUS: rc = ov8810_set_default_focus( - cdata.cfg.focus.steps); + (uint8_t)cdata.cfg.focus.steps); break; case CFG_SET_EFFECT: rc = ov8810_set_default_focus( - cdata.cfg.effect); + (uint8_t)cdata.cfg.effect); break; case CFG_I2C_IOCTL_R_OTP:{ @@ -2587,20 +2612,29 @@ static int ov8810_sensor_release(void) int rc = -EBADF; down(&ov8810_sem); - /*Pull hi PWD*/ - gpio_request(ov8810_ctrl->sensordata->sensor_pwd, "ov8810"); - gpio_direction_output(ov8810_ctrl->sensordata->sensor_pwd, 1); - gpio_free(ov8810_ctrl->sensordata->sensor_pwd); + msleep(35); + + if (ov8810_ctrl) { + rc = gpio_request(ov8810_ctrl->sensordata->sensor_pwd, "ov8810"); + if (!rc) + gpio_direction_output(ov8810_ctrl->sensordata->sensor_pwd, 1); + else + pr_err("[CAM]GPIO (%d) request faile\n", ov8810_ctrl->sensordata->sensor_pwd); + gpio_free(ov8810_ctrl->sensordata->sensor_pwd); +#ifdef CONFIG_ARCH_QSD8X50 /*Pull low RST*/ gpio_request(ov8810_ctrl->sensordata->sensor_reset, "ov8810"); gpio_direction_output(ov8810_ctrl->sensordata->sensor_reset, 0); gpio_free(ov8810_ctrl->sensordata->sensor_reset); +#endif + } - pr_info("vreg_af_actuator vreg_disable\n"); + pr_info("[CAM]vreg_af_actuator vreg_disable\n"); vreg_disable(vreg_af_actuator); msleep(20); - pr_info("%s, %d\n", __func__, __LINE__); - /*MCLK off*/ + + pr_info("[CAM]%s, %d\n", __func__, __LINE__); + msm_camio_probe_off(ov8810_pdev); if (ov8810_ctrl) { kfree(ov8810_ctrl); @@ -2608,7 +2642,7 @@ static int ov8810_sensor_release(void) } mdelay(3); allow_suspend(); - pr_info("ov8810_release completed\n"); + pr_info("[CAM]ov8810_release completed\n"); up(&ov8810_sem); return rc; @@ -2624,11 +2658,11 @@ static int ov8810_sensor_probe(struct msm_camera_sensor_info *info, goto probe_fail; } - pr_info("ov8810 s->node %d\n", s->node); + pr_info("[CAM]ov8810 s->node %d\n", s->node); sensor_probe_node = s->node; /*switch pclk and mclk between main cam and 2nd cam*/ /*only for supersonic*/ - pr_info("Ov8810: doing clk switch (ov8810)\n"); + pr_info("[CAM]Ov8810: doing clk switch (ov8810)\n"); if(info->camera_clk_switch != NULL) info->camera_clk_switch(); mdelay(5); @@ -2637,7 +2671,7 @@ static int ov8810_sensor_probe(struct msm_camera_sensor_info *info, if (!rc) gpio_direction_output(info->sensor_pwd, 0); else - pr_err("GPIO (%d) request faile\n", info->sensor_pwd); + pr_err("[CAM]GPIO (%d) request faile\n", info->sensor_pwd); gpio_free(info->sensor_pwd); mdelay(5); /*reset setup */ @@ -2645,11 +2679,11 @@ static int ov8810_sensor_probe(struct msm_camera_sensor_info *info, if (!rc) gpio_direction_output(info->sensor_reset, 1); else - pr_err("GPIO (%d) request faile\n", info->sensor_reset); + pr_err("[CAM]GPIO (%d) request faile\n", info->sensor_reset); gpio_free(info->sensor_reset); /*set MCLK*/ - pr_info("%s, msm_camio_clk_rate_set %d\n", + pr_info("[CAM]%s, msm_camio_clk_rate_set %d\n", __func__, OV8810_DEFAULT_CLOCK_RATE); msm_camio_clk_rate_set(OV8810_DEFAULT_CLOCK_RATE); msleep(100); @@ -2665,10 +2699,16 @@ static int ov8810_sensor_probe(struct msm_camera_sensor_info *info, if (info->camera_main_set_probe != NULL) info->camera_main_set_probe(true); + init_suspend(); s->s_init = ov8810_sensor_open_init; s->s_release = ov8810_sensor_release; s->s_config = ov8810_sensor_config; + +#ifdef CONFIG_ARCH_MSM7X30 + info->preview_skip_frame = ov8810_preview_skip_frame; +#endif + msleep(20); ov8810_probe_init_done(info); /*register late resuem*/ @@ -2679,56 +2719,77 @@ static int ov8810_sensor_probe(struct msm_camera_sensor_info *info, ov8810_event.waked_up = 1; /*write sysfs*/ ov8810_sysfs_init(); - pr_info("%s: ov8810_probe_init_done %d\n", __func__, __LINE__); + pr_info("[CAM]%s: ov8810_probe_init_done %d\n", __func__, __LINE__); goto probe_done; probe_fail: - pr_err("SENSOR PROBE FAILS!\n"); + pr_err("[CAM]SENSOR PROBE FAILS!\n"); probe_done: return rc; } +#ifndef CONFIG_ARCH_QSD8X50 static int ov8810_vreg_enable(struct platform_device *pdev) { struct msm_camera_sensor_info *sdata = pdev->dev.platform_data; int rc; - pr_info("%s camera vreg on\n", __func__); + pr_info("[CAM]%s camera vreg on\n", __func__); if (sdata->camera_power_on == NULL) { - pr_err("sensor platform_data didnt register\n"); + pr_err("[CAM]sensor platform_data didnt register\n"); return -EIO; } rc = sdata->camera_power_on(); return rc; } +#endif - +#if 0 static int ov8810_vreg_disable(struct platform_device *pdev) { struct msm_camera_sensor_info *sdata = pdev->dev.platform_data; int rc; printk(KERN_INFO "%s camera vreg off\n", __func__); if (sdata->camera_power_off == NULL) { - pr_err("sensor platform_data didnt register\n"); + pr_err("[CAM]sensor platform_data didnt register\n"); return -EIO; } rc = sdata->camera_power_off(); return rc; } - +#endif static int __ov8810_probe(struct platform_device *pdev) { struct msm_camera_sensor_info *sdata = pdev->dev.platform_data; - printk("__ov8810_probe\n"); + printk("[CAM]__ov8810_probe\n"); ov8810_pdev = pdev; + if (sdata->camera_main_get_probe != NULL) { if (sdata->camera_main_get_probe()) { - pr_info("__ov8810_probe camera main get probed already.\n"); + pr_info("[CAM]__ov8810_probe camera main get probed already.\n"); return 0; } } + +#ifndef CONFIG_ARCH_QSD8X50 + { + int rc; + rc = gpio_request(sdata->sensor_pwd, "ov8810"); + if (!rc) + gpio_direction_output(sdata->sensor_pwd, 1); + else + pr_err("[CAM]GPIO (%d) request faile\n", sdata->sensor_pwd); + gpio_free(sdata->sensor_pwd); + udelay(200); + + rc = ov8810_vreg_enable(pdev); + if (rc < 0) + pr_err("[CAM]__ov8810_probe fail sensor power on error\n"); + } +#endif + return msm_camera_drv_start(pdev, ov8810_sensor_probe); } From fb4db84943df54b333caa36255a512ec2f604d76 Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Thu, 21 Apr 2011 22:04:23 -0400 Subject: [PATCH 1426/2556] defconfig: regen for 38.4 --- arch/arm/configs/pershoot_mahimahi_defconfig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm/configs/pershoot_mahimahi_defconfig b/arch/arm/configs/pershoot_mahimahi_defconfig index 0764ae43a6691..08a89af2f2a7a 100644 --- a/arch/arm/configs/pershoot_mahimahi_defconfig +++ b/arch/arm/configs/pershoot_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux/arm 2.6.38.2 Kernel Configuration -# Mon Apr 4 22:48:39 2011 +# Linux/arm 2.6.38.4 Kernel Configuration +# Thu Apr 21 22:04:06 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -1942,6 +1942,7 @@ CONFIG_BRANCH_PROFILE_NONE=y # CONFIG_SAMPLES is not set CONFIG_HAVE_ARCH_KGDB=y # CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set # CONFIG_STRICT_DEVMEM is not set # CONFIG_ARM_UNWIND is not set # CONFIG_DEBUG_USER is not set From e5a67779b5db597c09370c860fd8d1e226fa4a0c Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 26 Apr 2011 11:36:07 -0700 Subject: [PATCH 1427/2556] net: wireless: bcm4329: Prevent working thread to run during suspend path Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/dhd.h | 2 +- drivers/net/wireless/bcm4329/dhd_linux.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcm4329/dhd.h b/drivers/net/wireless/bcm4329/dhd.h index 5c0ad80804e9f..9aeca7e4f30ab 100644 --- a/drivers/net/wireless/bcm4329/dhd.h +++ b/drivers/net/wireless/bcm4329/dhd.h @@ -180,7 +180,7 @@ typedef struct dhd_pub { wait_event_interruptible_timeout(a, FALSE, HZ/100); \ } \ } while (0) - #define DHD_PM_RESUME_WAIT(a) _DHD_PM_RESUME_WAIT(a, 30) + #define DHD_PM_RESUME_WAIT(a) _DHD_PM_RESUME_WAIT(a, 200) #define DHD_PM_RESUME_WAIT_FOREVER(a) _DHD_PM_RESUME_WAIT(a, ~0) #define DHD_PM_RESUME_RETURN_ERROR(a) do { if (dhd_mmc_suspend) return a; } while (0) #define DHD_PM_RESUME_RETURN do { if (dhd_mmc_suspend) return; } while (0) diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index 287fa24ba3611..3262bf8ea704d 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -159,14 +159,21 @@ static int wifi_remove(struct platform_device *pdev) up(&wifi_control_sem); return 0; } + static int wifi_suspend(struct platform_device *pdev, pm_message_t state) { DHD_TRACE(("##> %s\n", __FUNCTION__)); +#if defined(OOB_INTR_ONLY) + bcmsdh_oob_intr_set(0); +#endif /* (OOB_INTR_ONLY) */ return 0; } static int wifi_resume(struct platform_device *pdev) { DHD_TRACE(("##> %s\n", __FUNCTION__)); +#if defined(OOB_INTR_ONLY) + bcmsdh_oob_intr_set(1); +#endif /* (OOB_INTR_ONLY) */ return 0; } From f17030972ff248fcd9757de7298a178e54d7d5f2 Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Wed, 27 Apr 2011 13:59:57 -0400 Subject: [PATCH 1428/2556] timer: import get_xtime_and_monotonic_and_sleep_offset from AOSP's, android-2.6.39 --- include/linux/time.h | 2 ++ kernel/hrtimer.c | 8 ++++++-- kernel/time/timekeeping.c | 21 +++++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/include/linux/time.h b/include/linux/time.h index 1e6d3b59238d3..90e3f0a6b36e8 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -127,6 +127,8 @@ struct timespec current_kernel_time(void); struct timespec __current_kernel_time(void); /* does not take xtime_lock */ struct timespec __get_wall_to_monotonic(void); /* does not take xtime_lock */ struct timespec get_monotonic_coarse(void); +void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, + struct timespec *wtom, struct timespec *sleep); #define CURRENT_TIME (current_kernel_time()) #define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 }) diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 0c8d7c0486154..c2236623b3ccf 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -84,7 +84,7 @@ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) = static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base) { ktime_t xtim, tomono; - struct timespec xts, tom; + struct timespec xts, tom, slp; unsigned long seq; do { @@ -93,6 +93,8 @@ static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base) tom = __get_wall_to_monotonic(); } while (read_seqretry(&xtime_lock, seq)); + get_xtime_and_monotonic_and_sleep_offset(&xts, &tom, &slp); + xtim = timespec_to_ktime(xts); tomono = timespec_to_ktime(tom); base->clock_base[CLOCK_REALTIME].softirq_time = xtim; @@ -611,7 +613,7 @@ static int hrtimer_reprogram(struct hrtimer *timer, static void retrigger_next_event(void *arg) { struct hrtimer_cpu_base *base; - struct timespec realtime_offset, wtm; + struct timespec realtime_offset, wtm, sleep; unsigned long seq; if (!hrtimer_hres_active()) @@ -621,6 +623,8 @@ static void retrigger_next_event(void *arg) seq = read_seqbegin(&xtime_lock); wtm = __get_wall_to_monotonic(); } while (read_seqretry(&xtime_lock, seq)); + get_xtime_and_monotonic_and_sleep_offset(&realtime_offset, &wtm, + &sleep); set_normalized_timespec(&realtime_offset, -wtm.tv_sec, -wtm.tv_nsec); base = &__get_cpu_var(hrtimer_bases); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index d27c7562902cb..3991ffa03dc8d 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -946,3 +946,24 @@ struct timespec get_monotonic_coarse(void) now.tv_nsec + mono.tv_nsec); return now; } + +/** + * get_xtime_and_monotonic_and_sleep_offset() - get xtime, wall_to_monotonic, + and sleep offsets. + * @xtim: pointer to timespec to be set with xtime + * @wtom: pointer to timespec to be set with wall_to_monotonic + * @sleep: pointer to timespec to be set with time in suspend + */ + +void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, + struct timespec *wtom, struct timespec *sleep) +{ + unsigned long seq; + + do { + seq = read_seqbegin(&xtime_lock); + *xtim = xtime; + *wtom = wall_to_monotonic; + *sleep = total_sleep_time; + } while (read_seqretry(&xtime_lock, seq)); +} From 64f6ad02fe65b20d136ea8b4f153df2507112d3b Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 30 Mar 2011 12:37:49 -0700 Subject: [PATCH 1429/2556] power: wakelock: use get_xtime_and_monotonic_and_sleep_offset in get_expired_time Change-Id: I6ebe6b954b2ff328d46898d683650dafb9c4fe74 Signed-off-by: Erik Gilling --- kernel/power/wakelock.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c index ee9781c5adb28..c10d0ee7907ed 100644 --- a/kernel/power/wakelock.c +++ b/kernel/power/wakelock.c @@ -60,19 +60,15 @@ int get_expired_time(struct wake_lock *lock, ktime_t *expire_time) struct timespec kt; struct timespec tomono; struct timespec delta; - unsigned long seq; + struct timespec sleep; long timeout; if (!(lock->flags & WAKE_LOCK_AUTO_EXPIRE)) return 0; - do { - seq = read_seqbegin(&xtime_lock); - timeout = lock->expires - jiffies; - if (timeout > 0) - return 0; - kt = current_kernel_time(); - tomono = __get_wall_to_monotonic(); - } while (read_seqretry(&xtime_lock, seq)); + get_xtime_and_monotonic_and_sleep_offset(&kt, &tomono, &sleep); + timeout = lock->expires - jiffies; + if (timeout > 0) + return 0; jiffies_to_timespec(-timeout, &delta); set_normalized_timespec(&ts, kt.tv_sec + tomono.tv_sec - delta.tv_sec, kt.tv_nsec + tomono.tv_nsec - delta.tv_nsec); From f388557076c48dcd8f831cbb99cf2de0cc282193 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Wed, 2 Feb 2011 11:52:56 -0500 Subject: [PATCH 1430/2556] USB: gadget: f_accessory: New gadget driver for android USB accesories Signed-off-by: Mike Lockwood --- drivers/usb/gadget/Kconfig | 7 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/android.c | 13 + drivers/usb/gadget/f_accessory.c | 788 +++++++++++++++++++++++++++++++ include/linux/usb/f_accessory.h | 59 +++ 5 files changed, 868 insertions(+) create mode 100644 drivers/usb/gadget/f_accessory.c create mode 100644 include/linux/usb/f_accessory.h diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 9df64e685c912..9a8eae15468d0 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -975,6 +975,13 @@ config USB_ANDROID_RNDIS_WCEIS If you enable this option, the device is no longer CDC ethernet compatible. + +config USB_ANDROID_ACCESSORY + boolean "Android USB accessory function" + depends on USB_ANDROID + help + Provides Android USB Accessory support for android gadget driver. + config USB_CDC_COMPOSITE tristate "CDC Composite Device (Ethernet and ACM)" depends on NET diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 982820588ee35..5e32009725da8 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_USB_ANDROID_ADB) += f_adb.o obj-$(CONFIG_USB_ANDROID_MASS_STORAGE) += f_mass_storage.o obj-$(CONFIG_USB_ANDROID_MTP) += f_mtp.o obj-$(CONFIG_USB_ANDROID_RNDIS) += f_rndis.o u_ether.o +obj-$(CONFIG_USB_ANDROID_ACCESSORY) += f_accessory.o # MSM specific obj-$(CONFIG_USB_ANDROID_DIAG) += diag.o diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index eb6af6d538d6c..2ebc8183fd52f 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -430,6 +430,19 @@ void android_enable_function(struct usb_function *f, int enable) } } #endif +#ifdef CONFIG_USB_ANDROID_ACCESSORY + if (!strcmp(f->name, "accessory") && enable) { + struct usb_function *func; + + /* disable everything else (and keep adb for now) */ + list_for_each_entry(func, &android_config_driver.functions, list) { + if (strcmp(func->name, "accessory") + && strcmp(func->name, "adb")) { + usb_function_set_enabled(func, 0); + } + } + } +#endif update_dev_desc(dev); diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c new file mode 100644 index 0000000000000..24886aa29e84f --- /dev/null +++ b/drivers/usb/gadget/f_accessory.c @@ -0,0 +1,788 @@ +/* + * Gadget Function Driver for Android USB accessories + * + * Copyright (C) 2011 Google, Inc. + * Author: Mike Lockwood + * + * 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. + * + */ + +/* #define DEBUG */ +/* #define VERBOSE_DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define BULK_BUFFER_SIZE 16384 +#define ACC_STRING_SIZE 256 + +/* String IDs */ +#define INTERFACE_STRING_INDEX 0 + +/* number of tx and rx requests to allocate */ +#define TX_REQ_MAX 4 +#define RX_REQ_MAX 2 + +struct acc_dev { + struct usb_function function; + struct usb_composite_dev *cdev; + spinlock_t lock; + + struct usb_ep *ep_in; + struct usb_ep *ep_out; + + int online:1; + + /* strings sent by the host */ + char manufacturer[ACC_STRING_SIZE]; + char model[ACC_STRING_SIZE]; + char type[ACC_STRING_SIZE]; + char version[ACC_STRING_SIZE]; + + /* for acc_complete_set_string */ + int string_index; + + /* synchronize access to our device file */ + atomic_t open_excl; + + struct list_head tx_idle; + + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + struct usb_request *rx_req[RX_REQ_MAX]; + int rx_done; + struct delayed_work work; +}; + +static struct usb_interface_descriptor acc_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 3, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0, +}; + +static struct usb_endpoint_descriptor acc_highspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor acc_highspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor acc_fullspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor acc_fullspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_acc_descs[] = { + (struct usb_descriptor_header *) &acc_interface_desc, + (struct usb_descriptor_header *) &acc_fullspeed_in_desc, + (struct usb_descriptor_header *) &acc_fullspeed_out_desc, + NULL, +}; + +static struct usb_descriptor_header *hs_acc_descs[] = { + (struct usb_descriptor_header *) &acc_interface_desc, + (struct usb_descriptor_header *) &acc_highspeed_in_desc, + (struct usb_descriptor_header *) &acc_highspeed_out_desc, + NULL, +}; + +static struct usb_string acc_string_defs[] = { + [INTERFACE_STRING_INDEX].s = "Android Accessory Interface", + { }, /* end of list */ +}; + +static struct usb_gadget_strings acc_string_table = { + .language = 0x0409, /* en-US */ + .strings = acc_string_defs, +}; + +static struct usb_gadget_strings *acc_strings[] = { + &acc_string_table, + NULL, +}; + +/* temporary variable used between acc_open() and acc_gadget_bind() */ +static struct acc_dev *_acc_dev; + +static inline struct acc_dev *func_to_dev(struct usb_function *f) +{ + return container_of(f, struct acc_dev, function); +} + +static struct usb_request *acc_request_new(struct usb_ep *ep, int buffer_size) +{ + struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) + return NULL; + + /* now allocate buffers for the requests */ + req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + + return req; +} + +static void acc_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +static inline int _lock(atomic_t *excl) +{ + if (atomic_inc_return(excl) == 1) { + return 0; + } else { + atomic_dec(excl); + return -1; + } +} + +static inline void _unlock(atomic_t *excl) +{ + atomic_dec(excl); +} + +/* add a request to the tail of a list */ +static void req_put(struct acc_dev *dev, struct list_head *head, + struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&req->list, head); + spin_unlock_irqrestore(&dev->lock, flags); +} + +/* remove a request from the head of a list */ +static struct usb_request *req_get(struct acc_dev *dev, struct list_head *head) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&dev->lock, flags); + if (list_empty(head)) { + req = 0; + } else { + req = list_first_entry(head, struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&dev->lock, flags); + return req; +} + +static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) +{ + struct acc_dev *dev = _acc_dev; + + if (req->status != 0) + dev->online = 0; + + req_put(dev, &dev->tx_idle, req); + + wake_up(&dev->write_wq); +} + +static void acc_complete_out(struct usb_ep *ep, struct usb_request *req) +{ + struct acc_dev *dev = _acc_dev; + + dev->rx_done = 1; + if (req->status != 0) + dev->online = 0; + + wake_up(&dev->read_wq); +} + +static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) +{ + struct acc_dev *dev = ep->driver_data; + struct usb_composite_dev *cdev = dev->cdev; + char *string_dest = NULL; + int length = req->actual; + + if (req->status != 0) { + DBG(cdev, "acc_complete_set_string, err %d\n", req->status); + return; + } + + if (length > ACC_STRING_SIZE) { + DBG(cdev, "accessory string too long (length %d)\n", length); + return; + } + + switch (dev->string_index) { + case ACCESSORY_STRING_MANUFACTURER: + string_dest = dev->manufacturer; + break; + case ACCESSORY_STRING_MODEL: + string_dest = dev->model; + break; + case ACCESSORY_STRING_TYPE: + string_dest = dev->type; + break; + case ACCESSORY_STRING_VERSION: + string_dest = dev->version; + break; + } + if (string_dest) { + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + if (string_dest) + memcpy(string_dest, cdev->req->buf, length); + spin_unlock_irqrestore(&dev->lock, flags); + } else { + DBG(cdev, "unknown accessory string index %d\n", + dev->string_index); + } +} + +static int __init create_bulk_endpoints(struct acc_dev *dev, + struct usb_endpoint_descriptor *in_desc, + struct usb_endpoint_descriptor *out_desc) +{ + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + struct usb_ep *ep; + int i; + + DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); + + ep = usb_ep_autoconfig(cdev->gadget, in_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_in = 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 ep_out got %s\n", ep->name); + 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 ep_out got %s\n", ep->name); + ep->driver_data = dev; /* claim the endpoint */ + dev->ep_out = ep; + + /* now allocate requests for our endpoints */ + for (i = 0; i < TX_REQ_MAX; i++) { + req = acc_request_new(dev->ep_in, BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = acc_complete_in; + req_put(dev, &dev->tx_idle, req); + } + for (i = 0; i < RX_REQ_MAX; i++) { + req = acc_request_new(dev->ep_out, BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = acc_complete_out; + dev->rx_req[i] = req; + } + + return 0; + +fail: + printk(KERN_ERR "acc_bind() could not allocate requests\n"); + return -1; +} + +static ssize_t acc_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct acc_dev *dev = fp->private_data; + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + int r = count, xfer; + int ret = 0; + + DBG(cdev, "acc_read(%d)\n", count); + + if (count > BULK_BUFFER_SIZE) + count = BULK_BUFFER_SIZE; + + /* we will block until we're online */ + DBG(cdev, "acc_read: waiting for online\n"); + ret = wait_event_interruptible(dev->read_wq, dev->online); + if (ret < 0) { + r = ret; + goto done; + } + +requeue_req: + /* queue a request */ + req = dev->rx_req[0]; + req->length = count; + dev->rx_done = 0; + ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); + if (ret < 0) { + r = -EIO; + goto done; + } else { + DBG(cdev, "rx %p queue\n", req); + } + + /* wait for a request to complete */ + ret = wait_event_interruptible(dev->read_wq, dev->rx_done); + if (ret < 0) { + r = ret; + usb_ep_dequeue(dev->ep_out, req); + goto done; + } + if (dev->online) { + /* If we got a 0-len packet, throw it back and try again. */ + if (req->actual == 0) + goto requeue_req; + + DBG(cdev, "rx %p %d\n", req, req->actual); + xfer = (req->actual < count) ? req->actual : count; + r = xfer; + if (copy_to_user(buf, req->buf, xfer)) + r = -EFAULT; + } else + r = -EIO; + +done: + DBG(cdev, "acc_read returning %d\n", r); + return r; +} + +static ssize_t acc_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct acc_dev *dev = fp->private_data; + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req = 0; + int r = count, xfer; + int ret; + + DBG(cdev, "acc_write(%d)\n", count); + + spin_lock_irq(&dev->lock); + if (!dev->online) { + spin_unlock_irq(&dev->lock); + return -ENODEV; + } + spin_unlock_irq(&dev->lock); + + while (count > 0) { + if (!dev->online) { + DBG(cdev, "acc_write dev->error\n"); + r = -EIO; + break; + } + + /* get an idle tx request to use */ + req = 0; + ret = wait_event_interruptible(dev->write_wq, + ((req = req_get(dev, &dev->tx_idle)) || !dev->online)); + if (!req) { + r = ret; + break; + } + + if (count > BULK_BUFFER_SIZE) + xfer = BULK_BUFFER_SIZE; + else + xfer = count; + if (copy_from_user(req->buf, buf, xfer)) { + r = -EFAULT; + break; + } + + req->length = xfer; + ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); + if (ret < 0) { + DBG(cdev, "acc_write: xfer error %d\n", ret); + r = -EIO; + break; + } + + buf += xfer; + count -= xfer; + + /* zero this so we don't try to free it on error exit */ + req = 0; + } + + if (req) + req_put(dev, &dev->tx_idle, req); + + DBG(cdev, "acc_write returning %d\n", r); + return r; +} + +static long acc_ioctl(struct file *fp, unsigned code, unsigned long value) +{ + struct acc_dev *dev = fp->private_data; + int ret = -EINVAL; + + if (dev->function.disabled) + return -ENODEV; + + switch (code) { + case ACCESSORY_GET_STRING_MANUFACTURER: + spin_lock_irq(&dev->lock); + ret = strlen(dev->manufacturer) + 1; + if (copy_to_user((void __user *)value, dev->manufacturer, ret)) + ret = -EFAULT; + spin_unlock_irq(&dev->lock); + break; + + case ACCESSORY_GET_STRING_MODEL: + spin_lock_irq(&dev->lock); + ret = strlen(dev->model) + 1; + if (copy_to_user((void __user *)value, dev->model, ret)) + ret = -EFAULT; + spin_unlock_irq(&dev->lock); + break; + + case ACCESSORY_GET_STRING_TYPE: + spin_lock_irq(&dev->lock); + ret = strlen(dev->type) + 1; + if (copy_to_user((void __user *)value, dev->type, ret)) + ret = -EFAULT; + spin_unlock_irq(&dev->lock); + break; + + case ACCESSORY_GET_STRING_VERSION: + spin_lock_irq(&dev->lock); + ret = strlen(dev->version) + 1; + if (copy_to_user((void __user *)value, dev->version, ret)) + ret = -EFAULT; + spin_unlock_irq(&dev->lock); + break; + } + + return ret; +} + +static int acc_open(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "acc_open\n"); + if (_lock(&_acc_dev->open_excl)) + return -EBUSY; + + fp->private_data = _acc_dev; + return 0; +} + +static int acc_release(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "acc_release\n"); + + _unlock(&_acc_dev->open_excl); + return 0; +} + +/* file operations for /dev/acc_usb */ +static const struct file_operations acc_fops = { + .owner = THIS_MODULE, + .read = acc_read, + .write = acc_write, + .unlocked_ioctl = acc_ioctl, + .open = acc_open, + .release = acc_release, +}; + +static struct miscdevice acc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "usb_accessory", + .fops = &acc_fops, +}; + +static int +acc_function_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct acc_dev *dev = func_to_dev(f); + int id; + int ret; + + dev->cdev = cdev; + DBG(cdev, "acc_function_bind dev: %p\n", dev); + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + acc_interface_desc.bInterfaceNumber = id; + + /* allocate endpoints */ + ret = create_bulk_endpoints(dev, &acc_fullspeed_in_desc, + &acc_fullspeed_out_desc); + if (ret) + return ret; + + /* support high speed hardware */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + acc_highspeed_in_desc.bEndpointAddress = + acc_fullspeed_in_desc.bEndpointAddress; + acc_highspeed_out_desc.bEndpointAddress = + acc_fullspeed_out_desc.bEndpointAddress; + } + + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + f->name, dev->ep_in->name, dev->ep_out->name); + return 0; +} + +static void +acc_function_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct acc_dev *dev = func_to_dev(f); + struct usb_request *req; + int i; + + spin_lock_irq(&dev->lock); + while ((req = req_get(dev, &dev->tx_idle))) + acc_request_free(req, dev->ep_in); + for (i = 0; i < RX_REQ_MAX; i++) + acc_request_free(dev->rx_req[i], dev->ep_out); + dev->online = 0; + spin_unlock_irq(&dev->lock); + + misc_deregister(&acc_device); + kfree(_acc_dev); + _acc_dev = NULL; +} + +static void acc_work(struct work_struct *data) +{ + struct delayed_work *delayed = to_delayed_work(data); + struct acc_dev *dev = + container_of(delayed, struct acc_dev, work); + android_enable_function(&dev->function, 1); +} + +static int acc_function_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct acc_dev *dev = func_to_dev(f); + struct usb_composite_dev *cdev = dev->cdev; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + +/* + printk(KERN_INFO "acc_function_setup " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); +*/ + + if (dev->function.disabled && ctrl->bRequestType == + (USB_DIR_OUT | USB_TYPE_VENDOR)) { + if (ctrl->bRequest == ACCESSORY_START) { + schedule_delayed_work(&dev->work, msecs_to_jiffies(10)); + value = 0; + } else if (ctrl->bRequest == ACCESSORY_SEND_STRING) { + dev->string_index = w_index; + cdev->gadget->ep0->driver_data = dev; + cdev->req->complete = acc_complete_set_string; + value = w_length; + } + } + + if (value >= 0) { + cdev->req->zero = 0; + cdev->req->length = value; + value = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC); + if (value < 0) + ERROR(cdev, "%s setup response queue error\n", + __func__); + } + + if (value == -EOPNOTSUPP) + VDBG(cdev, + "unknown class-specific control req " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + return value; +} + +static int acc_function_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct acc_dev *dev = func_to_dev(f); + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + DBG(cdev, "acc_function_set_alt intf: %d alt: %d\n", intf, alt); + ret = usb_ep_enable(dev->ep_in, + ep_choose(cdev->gadget, + &acc_highspeed_in_desc, + &acc_fullspeed_in_desc)); + if (ret) + return ret; + ret = usb_ep_enable(dev->ep_out, + ep_choose(cdev->gadget, + &acc_highspeed_out_desc, + &acc_fullspeed_out_desc)); + if (ret) { + usb_ep_disable(dev->ep_in); + return ret; + } + if (!dev->function.disabled) + dev->online = 1; + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + return 0; +} + +static void acc_function_disable(struct usb_function *f) +{ + struct acc_dev *dev = func_to_dev(f); + struct usb_composite_dev *cdev = dev->cdev; + + DBG(cdev, "acc_function_disable\n"); + dev->online = 0; + usb_ep_disable(dev->ep_in); + usb_ep_disable(dev->ep_out); + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + + VDBG(cdev, "%s disabled\n", dev->function.name); +} + +static int acc_bind_config(struct usb_configuration *c) +{ + struct acc_dev *dev; + int ret; + + printk(KERN_INFO "acc_bind_config\n"); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + /* allocate a string ID for our interface */ + if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) { + ret = usb_string_id(c->cdev); + if (ret < 0) + return ret; + acc_string_defs[INTERFACE_STRING_INDEX].id = ret; + acc_interface_desc.iInterface = ret; + } + + spin_lock_init(&dev->lock); + init_waitqueue_head(&dev->read_wq); + init_waitqueue_head(&dev->write_wq); + atomic_set(&dev->open_excl, 0); + INIT_LIST_HEAD(&dev->tx_idle); + INIT_DELAYED_WORK(&dev->work, acc_work); + + dev->cdev = c->cdev; + dev->function.name = "accessory"; + dev->function.strings = acc_strings, + dev->function.descriptors = fs_acc_descs; + dev->function.hs_descriptors = hs_acc_descs; + dev->function.bind = acc_function_bind; + dev->function.unbind = acc_function_unbind; + dev->function.setup = acc_function_setup; + dev->function.set_alt = acc_function_set_alt; + dev->function.disable = acc_function_disable; + dev->function.disabled = 1; + + /* _acc_dev must be set before calling usb_gadget_register_driver */ + _acc_dev = dev; + + ret = misc_register(&acc_device); + if (ret) + goto err1; + + ret = usb_add_function(c, &dev->function); + if (ret) + goto err2; + + return 0; + +err2: + misc_deregister(&acc_device); +err1: + kfree(dev); + printk(KERN_ERR "USB accessory gadget driver failed to initialize\n"); + return ret; +} + +static struct android_usb_function acc_function = { + .name = "accessory", + .bind_config = acc_bind_config, +}; + +static int __init init(void) +{ + printk(KERN_INFO "f_accessory init\n"); + android_register_function(&acc_function); + return 0; +} +module_init(init); diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h new file mode 100644 index 0000000000000..474f12eb0181f --- /dev/null +++ b/include/linux/usb/f_accessory.h @@ -0,0 +1,59 @@ +/* + * Gadget Function Driver for Android USB accessories + * + * Copyright (C) 2011 Google, Inc. + * Author: Mike Lockwood + * + * 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_USB_F_ACCESSORY_H +#define __LINUX_USB_F_ACCESSORY_H + +/* Use Google Vendor ID when in accessory mode */ +#define USB_ACCESSORY_VENDOR_ID 0x18D1 + + +/* Product ID to use when in accessory mode */ +#define USB_ACCESSORY_PRODUCT_ID 0x2D00 + +/* Product ID to use when in accessory mode and adb is enabled */ +#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01 + +/* + * Indexes for strings sent by the host to identify the accessory. + * The host sends these as vendor requests: + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_SEND_STRING + * value: 0 + * index: string ID + * data zero terminated UTF8 string + * + * The device can later retrieve these strings via the + * ACCESSORY_GET_STRING_* ioctls + */ +#define ACCESSORY_STRING_MANUFACTURER 0 +#define ACCESSORY_STRING_MODEL 1 +#define ACCESSORY_STRING_TYPE 2 +#define ACCESSORY_STRING_VERSION 3 + +/* control requests */ +#define ACCESSORY_SEND_STRING 52 +#define ACCESSORY_START 53 + +/* Sends an event to the accessory via the interrupt endpoint */ +#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256]) +#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256]) +#define ACCESSORY_GET_STRING_TYPE _IOW('M', 3, char[256]) +#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256]) + +#endif /* __LINUX_USB_F_ACCESSORY_H */ From 1e4e39786c9f60811159966e093468ef31c0b453 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Wed, 9 Mar 2011 21:48:20 -0500 Subject: [PATCH 1431/2556] USB: gadget: f_accessory: Misc improvements and cleanup: - Add URI string - Replace type string with a description string - Add a control call to retrieve accessory protocol version (currently 1) - Driver read() and write() calls now fail after USB disconnect until driver file is closed and reopened. - Misc cleanup work Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_accessory.c | 150 ++++++++++++++++--------------- include/linux/usb/f_accessory.h | 42 ++++++--- 2 files changed, 107 insertions(+), 85 deletions(-) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 24886aa29e84f..2ebc0363843e3 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -41,6 +41,8 @@ #define BULK_BUFFER_SIZE 16384 #define ACC_STRING_SIZE 256 +#define PROTOCOL_VERSION 1 + /* String IDs */ #define INTERFACE_STRING_INDEX 0 @@ -56,13 +58,19 @@ struct acc_dev { struct usb_ep *ep_in; struct usb_ep *ep_out; + /* set to 1 when we connect */ int online:1; + /* Set to 1 when we disconnect. + * Not cleared until our file is closed. + */ + int disconnected:1; /* strings sent by the host */ char manufacturer[ACC_STRING_SIZE]; char model[ACC_STRING_SIZE]; - char type[ACC_STRING_SIZE]; + char description[ACC_STRING_SIZE]; char version[ACC_STRING_SIZE]; + char uri[ACC_STRING_SIZE]; /* for acc_complete_set_string */ int string_index; @@ -180,21 +188,6 @@ static void acc_request_free(struct usb_request *req, struct usb_ep *ep) } } -static inline int _lock(atomic_t *excl) -{ - if (atomic_inc_return(excl) == 1) { - return 0; - } else { - atomic_dec(excl); - return -1; - } -} - -static inline void _unlock(atomic_t *excl) -{ - atomic_dec(excl); -} - /* add a request to the tail of a list */ static void req_put(struct acc_dev *dev, struct list_head *head, struct usb_request *req) @@ -227,8 +220,10 @@ static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) { struct acc_dev *dev = _acc_dev; - if (req->status != 0) + if (req->status != 0) { dev->online = 0; + dev->disconnected = 1; + } req_put(dev, &dev->tx_idle, req); @@ -240,8 +235,10 @@ static void acc_complete_out(struct usb_ep *ep, struct usb_request *req) struct acc_dev *dev = _acc_dev; dev->rx_done = 1; - if (req->status != 0) + if (req->status != 0) { dev->online = 0; + dev->disconnected = 1; + } wake_up(&dev->read_wq); } @@ -258,11 +255,6 @@ static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) return; } - if (length > ACC_STRING_SIZE) { - DBG(cdev, "accessory string too long (length %d)\n", length); - return; - } - switch (dev->string_index) { case ACCESSORY_STRING_MANUFACTURER: string_dest = dev->manufacturer; @@ -270,19 +262,26 @@ static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) case ACCESSORY_STRING_MODEL: string_dest = dev->model; break; - case ACCESSORY_STRING_TYPE: - string_dest = dev->type; + case ACCESSORY_STRING_DESCRIPTION: + string_dest = dev->description; break; case ACCESSORY_STRING_VERSION: string_dest = dev->version; break; + case ACCESSORY_STRING_URI: + string_dest = dev->uri; + break; } if (string_dest) { unsigned long flags; + if (length >= ACC_STRING_SIZE) + length = ACC_STRING_SIZE - 1; + spin_lock_irqsave(&dev->lock, flags); - if (string_dest) - memcpy(string_dest, cdev->req->buf, length); + memcpy(string_dest, cdev->req->buf, length); + /* ensure zero termination */ + string_dest[length] = 0; spin_unlock_irqrestore(&dev->lock, flags); } else { DBG(cdev, "unknown accessory string index %d\n", @@ -348,6 +347,10 @@ static int __init create_bulk_endpoints(struct acc_dev *dev, fail: printk(KERN_ERR "acc_bind() could not allocate requests\n"); + while ((req = req_get(dev, &dev->tx_idle))) + acc_request_free(req, dev->ep_in); + for (i = 0; i < RX_REQ_MAX; i++) + acc_request_free(dev->rx_req[i], dev->ep_out); return -1; } @@ -362,6 +365,9 @@ static ssize_t acc_read(struct file *fp, char __user *buf, DBG(cdev, "acc_read(%d)\n", count); + if (dev->disconnected) + return -ENODEV; + if (count > BULK_BUFFER_SIZE) count = BULK_BUFFER_SIZE; @@ -422,12 +428,8 @@ static ssize_t acc_write(struct file *fp, const char __user *buf, DBG(cdev, "acc_write(%d)\n", count); - spin_lock_irq(&dev->lock); - if (!dev->online) { - spin_unlock_irq(&dev->lock); + if (!dev->online || dev->disconnected) return -ENODEV; - } - spin_unlock_irq(&dev->lock); while (count > 0) { if (!dev->online) { @@ -479,52 +481,42 @@ static ssize_t acc_write(struct file *fp, const char __user *buf, static long acc_ioctl(struct file *fp, unsigned code, unsigned long value) { struct acc_dev *dev = fp->private_data; - int ret = -EINVAL; + char *src = NULL; + int ret; if (dev->function.disabled) return -ENODEV; switch (code) { case ACCESSORY_GET_STRING_MANUFACTURER: - spin_lock_irq(&dev->lock); - ret = strlen(dev->manufacturer) + 1; - if (copy_to_user((void __user *)value, dev->manufacturer, ret)) - ret = -EFAULT; - spin_unlock_irq(&dev->lock); - break; - + src = dev->manufacturer; + break; case ACCESSORY_GET_STRING_MODEL: - spin_lock_irq(&dev->lock); - ret = strlen(dev->model) + 1; - if (copy_to_user((void __user *)value, dev->model, ret)) - ret = -EFAULT; - spin_unlock_irq(&dev->lock); - break; - - case ACCESSORY_GET_STRING_TYPE: - spin_lock_irq(&dev->lock); - ret = strlen(dev->type) + 1; - if (copy_to_user((void __user *)value, dev->type, ret)) - ret = -EFAULT; - spin_unlock_irq(&dev->lock); - break; - + src = dev->model; + break; + case ACCESSORY_GET_STRING_DESCRIPTION: + src = dev->description; + break; case ACCESSORY_GET_STRING_VERSION: - spin_lock_irq(&dev->lock); - ret = strlen(dev->version) + 1; - if (copy_to_user((void __user *)value, dev->version, ret)) - ret = -EFAULT; - spin_unlock_irq(&dev->lock); - break; + src = dev->version; + break; + case ACCESSORY_GET_STRING_URI: + src = dev->uri; + break; } + if (!src) + return -EINVAL; + ret = strlen(src) + 1; + if (copy_to_user((void __user *)value, src, ret)) + ret = -EFAULT; return ret; } static int acc_open(struct inode *ip, struct file *fp) { printk(KERN_INFO "acc_open\n"); - if (_lock(&_acc_dev->open_excl)) + if (atomic_xchg(&_acc_dev->open_excl, 1)) return -EBUSY; fp->private_data = _acc_dev; @@ -535,7 +527,8 @@ static int acc_release(struct inode *ip, struct file *fp) { printk(KERN_INFO "acc_release\n"); - _unlock(&_acc_dev->open_excl); + WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0)); + _acc_dev->disconnected = 0; return 0; } @@ -626,6 +619,8 @@ static int acc_function_setup(struct usb_function *f, struct acc_dev *dev = func_to_dev(f); struct usb_composite_dev *cdev = dev->cdev; int value = -EOPNOTSUPP; + u8 b_requestType = ctrl->bRequestType; + u8 b_request = ctrl->bRequest; u16 w_index = le16_to_cpu(ctrl->wIndex); u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); @@ -633,20 +628,27 @@ static int acc_function_setup(struct usb_function *f, /* printk(KERN_INFO "acc_function_setup " "%02x.%02x v%04x i%04x l%u\n", - ctrl->bRequestType, ctrl->bRequest, + b_requestType, b_request, w_value, w_index, w_length); */ - if (dev->function.disabled && ctrl->bRequestType == - (USB_DIR_OUT | USB_TYPE_VENDOR)) { - if (ctrl->bRequest == ACCESSORY_START) { - schedule_delayed_work(&dev->work, msecs_to_jiffies(10)); - value = 0; - } else if (ctrl->bRequest == ACCESSORY_SEND_STRING) { - dev->string_index = w_index; - cdev->gadget->ep0->driver_data = dev; - cdev->req->complete = acc_complete_set_string; - value = w_length; + if (dev->function.disabled) { + if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) { + if (b_request == ACCESSORY_START) { + schedule_delayed_work( + &dev->work, msecs_to_jiffies(10)); + value = 0; + } else if (b_request == ACCESSORY_SEND_STRING) { + dev->string_index = w_index; + cdev->gadget->ep0->driver_data = dev; + cdev->req->complete = acc_complete_set_string; + value = w_length; + } + } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { + if (b_request == ACCESSORY_GET_PROTOCOL) { + *((u16 *)cdev->req->buf) = PROTOCOL_VERSION; + value = sizeof(u16); + } } } diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h index 474f12eb0181f..977ad0a69c7e1 100644 --- a/include/linux/usb/f_accessory.h +++ b/include/linux/usb/f_accessory.h @@ -28,9 +28,24 @@ /* Product ID to use when in accessory mode and adb is enabled */ #define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01 -/* - * Indexes for strings sent by the host to identify the accessory. - * The host sends these as vendor requests: +/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */ +#define ACCESSORY_STRING_MANUFACTURER 0 +#define ACCESSORY_STRING_MODEL 1 +#define ACCESSORY_STRING_DESCRIPTION 2 +#define ACCESSORY_STRING_VERSION 3 +#define ACCESSORY_STRING_URI 4 + +/* Control request for retrieving device's protocol version (currently 1) + * + * requestType: USB_DIR_IN | USB_TYPE_VENDOR + * request: ACCESSORY_GET_PROTOCOL + * value: 0 + * index: 0 + * data version number (16 bits little endian) + */ +#define ACCESSORY_GET_PROTOCOL 51 + +/* Control request for host to send a string to the device * * requestType: USB_DIR_OUT | USB_TYPE_VENDOR * request: ACCESSORY_SEND_STRING @@ -41,19 +56,24 @@ * The device can later retrieve these strings via the * ACCESSORY_GET_STRING_* ioctls */ -#define ACCESSORY_STRING_MANUFACTURER 0 -#define ACCESSORY_STRING_MODEL 1 -#define ACCESSORY_STRING_TYPE 2 -#define ACCESSORY_STRING_VERSION 3 - -/* control requests */ #define ACCESSORY_SEND_STRING 52 + +/* Control request for starting device in accessory mode. + * The host sends this after setting all its strings to the device. + * + * requestType: USB_DIR_OUT | USB_TYPE_VENDOR + * request: ACCESSORY_START + * value: 0 + * index: 0 + * data none + */ #define ACCESSORY_START 53 -/* Sends an event to the accessory via the interrupt endpoint */ +/* ioctls for retrieving strings set by the host */ #define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256]) #define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256]) -#define ACCESSORY_GET_STRING_TYPE _IOW('M', 3, char[256]) +#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256]) #define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256]) +#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256]) #endif /* __LINUX_USB_F_ACCESSORY_H */ From e7f995f1a7181934faf9e00ee46aaa20d97ebf89 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Thu, 10 Mar 2011 16:12:49 -0500 Subject: [PATCH 1432/2556] USB: gadget: f_accessory: Clear accessory strings when USB is disconnected Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_accessory.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 2ebc0363843e3..ef21c05caaedc 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -216,14 +216,25 @@ static struct usb_request *req_get(struct acc_dev *dev, struct list_head *head) return req; } +static void acc_set_disconnected(struct acc_dev *dev) +{ + dev->online = 0; + dev->disconnected = 1; + + /* clear all accessory strings */ + memset(dev->manufacturer, 0, sizeof(dev->manufacturer)); + memset(dev->model, 0, sizeof(dev->model)); + memset(dev->description, 0, sizeof(dev->description)); + memset(dev->version, 0, sizeof(dev->version)); + memset(dev->uri, 0, sizeof(dev->uri)); +} + static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) { struct acc_dev *dev = _acc_dev; - if (req->status != 0) { - dev->online = 0; - dev->disconnected = 1; - } + if (req->status != 0) + acc_set_disconnected(dev); req_put(dev, &dev->tx_idle, req); @@ -235,10 +246,8 @@ static void acc_complete_out(struct usb_ep *ep, struct usb_request *req) struct acc_dev *dev = _acc_dev; dev->rx_done = 1; - if (req->status != 0) { - dev->online = 0; - dev->disconnected = 1; - } + if (req->status != 0) + acc_set_disconnected(dev); wake_up(&dev->read_wq); } @@ -706,7 +715,7 @@ static void acc_function_disable(struct usb_function *f) struct usb_composite_dev *cdev = dev->cdev; DBG(cdev, "acc_function_disable\n"); - dev->online = 0; + acc_set_disconnected(dev); usb_ep_disable(dev->ep_in); usb_ep_disable(dev->ep_out); From 23aa8398e51c09515d7579450930a16ab247dfce Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Sat, 12 Mar 2011 19:59:12 -0500 Subject: [PATCH 1433/2556] USB: gadget: f_accessory: Clear previous strings on ACCESSORY_GET_PROTOCOL Clearing strings on disconnect does not work since we may receive a disconnect on some devices when transitioning into accessory mode. We require an accessory to send ACCESSORY_GET_PROTOCOL before sending any strings, so any strings from a previous session will be cleared. Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_accessory.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index ef21c05caaedc..f6a5922e8c3b7 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -220,13 +220,6 @@ static void acc_set_disconnected(struct acc_dev *dev) { dev->online = 0; dev->disconnected = 1; - - /* clear all accessory strings */ - memset(dev->manufacturer, 0, sizeof(dev->manufacturer)); - memset(dev->model, 0, sizeof(dev->model)); - memset(dev->description, 0, sizeof(dev->description)); - memset(dev->version, 0, sizeof(dev->version)); - memset(dev->uri, 0, sizeof(dev->uri)); } static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) @@ -657,6 +650,13 @@ static int acc_function_setup(struct usb_function *f, if (b_request == ACCESSORY_GET_PROTOCOL) { *((u16 *)cdev->req->buf) = PROTOCOL_VERSION; value = sizeof(u16); + + /* clear any strings left over from a previous session */ + memset(dev->manufacturer, 0, sizeof(dev->manufacturer)); + memset(dev->model, 0, sizeof(dev->model)); + memset(dev->description, 0, sizeof(dev->description)); + memset(dev->version, 0, sizeof(dev->version)); + memset(dev->uri, 0, sizeof(dev->uri)); } } } From 129d2728d4a95ac4ede6e15dc89d7d1aa0eb96fb Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Sat, 12 Mar 2011 21:35:28 -0500 Subject: [PATCH 1434/2556] USB: gadget: f_accessory: Clear disconnected flag when driver file is opened Fixes a race condition that can occur when entering accessory mode. Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_accessory.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index f6a5922e8c3b7..baeae5976b661 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -521,6 +521,7 @@ static int acc_open(struct inode *ip, struct file *fp) if (atomic_xchg(&_acc_dev->open_excl, 1)) return -EBUSY; + _acc_dev->disconnected = 0; fp->private_data = _acc_dev; return 0; } From 52bdea2a2fa4796587d2ef3aee1e1a645a50ef1a Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 14 Mar 2011 18:28:55 -0400 Subject: [PATCH 1435/2556] USB: gadget: f_accessory: Add string for accessory's unique serial number Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_accessory.c | 8 ++++++++ include/linux/usb/f_accessory.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index baeae5976b661..38918cdf6c5b6 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -71,6 +71,7 @@ struct acc_dev { char description[ACC_STRING_SIZE]; char version[ACC_STRING_SIZE]; char uri[ACC_STRING_SIZE]; + char serial[ACC_STRING_SIZE]; /* for acc_complete_set_string */ int string_index; @@ -273,6 +274,9 @@ static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) case ACCESSORY_STRING_URI: string_dest = dev->uri; break; + case ACCESSORY_STRING_SERIAL: + string_dest = dev->serial; + break; } if (string_dest) { unsigned long flags; @@ -505,6 +509,9 @@ static long acc_ioctl(struct file *fp, unsigned code, unsigned long value) case ACCESSORY_GET_STRING_URI: src = dev->uri; break; + case ACCESSORY_GET_STRING_SERIAL: + src = dev->serial; + break; } if (!src) return -EINVAL; @@ -658,6 +665,7 @@ static int acc_function_setup(struct usb_function *f, memset(dev->description, 0, sizeof(dev->description)); memset(dev->version, 0, sizeof(dev->version)); memset(dev->uri, 0, sizeof(dev->uri)); + memset(dev->serial, 0, sizeof(dev->serial)); } } } diff --git a/include/linux/usb/f_accessory.h b/include/linux/usb/f_accessory.h index 977ad0a69c7e1..ebcc5f34f554d 100644 --- a/include/linux/usb/f_accessory.h +++ b/include/linux/usb/f_accessory.h @@ -34,6 +34,7 @@ #define ACCESSORY_STRING_DESCRIPTION 2 #define ACCESSORY_STRING_VERSION 3 #define ACCESSORY_STRING_URI 4 +#define ACCESSORY_STRING_SERIAL 5 /* Control request for retrieving device's protocol version (currently 1) * @@ -75,5 +76,6 @@ #define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256]) #define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256]) #define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256]) +#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256]) #endif /* __LINUX_USB_F_ACCESSORY_H */ From 34672ccd67c4e38717bd9d2ec659d88c30fd0e7e Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Sun, 27 Mar 2011 08:00:19 -0700 Subject: [PATCH 1436/2556] USB: gadget: f_accessory: Set bNumEndpoints to correct value of 2 Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_accessory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index 38918cdf6c5b6..ad3c1738336f9 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -92,7 +92,7 @@ static struct usb_interface_descriptor acc_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, - .bNumEndpoints = 3, + .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, .bInterfaceProtocol = 0, From e1e372b219e23f633e153d94f152f3a182086b34 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Tue, 1 Mar 2011 18:41:20 -0800 Subject: [PATCH 1437/2556] [ARM] usb: msm72k_udc: Fix handling of OUT packets on ep0 with length > 0 The gadget level completion function was getting called for the ACK packet instead of the data packet, making it impossible to read the data from the host. Signed-off-by: Mike Lockwood --- drivers/usb/gadget/msm72k_udc.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c index 3ae42edc037da..9740bd8cd6f4e 100644 --- a/drivers/usb/gadget/msm72k_udc.c +++ b/drivers/usb/gadget/msm72k_udc.c @@ -54,6 +54,7 @@ static const char driver_name[] = "msm72k_udc"; #define SETUP_BUF_SIZE 4096 +typedef void (*completion_func)(struct usb_ep *ep, struct usb_request *req); static const char *const ep_name[] = { "ep0out", "ep1out", "ep2out", "ep3out", @@ -73,9 +74,7 @@ struct msm_request { struct usb_request req; /* saved copy of req.complete */ - void (*gadget_complete)(struct usb_ep *ep, - struct usb_request *req); - + completion_func gadget_complete; struct usb_info *ui; struct msm_request *next; @@ -508,7 +507,7 @@ static void ep0_complete(struct usb_ep *ep, struct usb_request *req) struct usb_info *ui = ept->ui; req->complete = r->gadget_complete; - r->gadget_complete = 0; + r->gadget_complete = NULL; if (req->complete) req->complete(&ui->ep0in.ep, req); } @@ -516,6 +515,13 @@ static void ep0_complete(struct usb_ep *ep, struct usb_request *req) static void ep0_queue_ack_complete(struct usb_ep *ep, struct usb_request *req) { struct msm_endpoint *ept = to_msm_endpoint(ep); + struct msm_request *r = to_msm_request(req); + completion_func gadget_complete = r->gadget_complete; + + if (gadget_complete) { + r->gadget_complete = NULL; + gadget_complete(ep, req); + } /* queue up the receive of the ACK response from the host */ if (req->status == 0) { @@ -587,7 +593,7 @@ static void ep0_setup_send(struct usb_info *ui, unsigned length) req->length = length; req->complete = ep0_queue_ack_complete; - r->gadget_complete = 0; + r->gadget_complete = NULL; usb_ept_queue_xfer(ept, req); } From c6767eafd7cb3f1491e798f1e13b75d9e693ce99 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Tue, 1 Mar 2011 18:51:36 -0800 Subject: [PATCH 1438/2556] [ARM] msm: mahimahi: Add platform data for USB accessory function Signed-off-by: Mike Lockwood --- arch/arm/mach-msm/board-mahimahi.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 81d0f7051fc7d..618e7c9c5207f 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -143,6 +144,11 @@ static char *usb_functions_rndis_adb[] = { "adb", }; +#ifdef CONFIG_USB_ANDROID_ACCESSORY +static char *usb_functions_accessory[] = { "accessory" }; +static char *usb_functions_accessory_adb[] = { "accessory", "adb" }; +#endif + #ifdef CONFIG_USB_ANDROID_DIAG static char *usb_functions_adb_diag[] = { "usb_mass_storage", @@ -154,6 +160,9 @@ static char *usb_functions_adb_diag[] = { static char *usb_functions_all[] = { #ifdef CONFIG_USB_ANDROID_RNDIS "rndis", +#endif +#ifdef CONFIG_USB_ANDROID_ACCESSORY + "accessory", #endif "usb_mass_storage", "adb", @@ -186,6 +195,20 @@ static struct android_usb_product usb_products[] = { .num_functions = ARRAY_SIZE(usb_functions_rndis_adb), .functions = usb_functions_rndis_adb, }, +#ifdef CONFIG_USB_ANDROID_ACCESSORY + { + .vendor_id = USB_ACCESSORY_VENDOR_ID, + .product_id = USB_ACCESSORY_PRODUCT_ID, + .num_functions = ARRAY_SIZE(usb_functions_accessory), + .functions = usb_functions_accessory, + }, + { + .vendor_id = USB_ACCESSORY_VENDOR_ID, + .product_id = USB_ACCESSORY_ADB_PRODUCT_ID, + .num_functions = ARRAY_SIZE(usb_functions_accessory_adb), + .functions = usb_functions_accessory_adb, + }, +#endif #ifdef CONFIG_USB_ANDROID_DIAG { .product_id = 0x4e17, From d3bd1388fa98aead63201e59a6dd689a151927b9 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Fri, 10 Dec 2010 16:30:15 -0800 Subject: [PATCH 1439/2556] USB: gadget: composite: Use separate switches for connected and config state Also remove disconnect debouncing, which didn't actually work on some platforms Signed-off-by: Mike Lockwood --- drivers/usb/gadget/composite.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index b793304d7968c..05b7bf13a7c06 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1132,9 +1132,6 @@ static void composite_disconnect(struct usb_gadget *gadget) if (cdev->config) reset_config(cdev); - if (composite->disconnect) - composite->disconnect(cdev); - cdev->connected = 0; schedule_work(&cdev->switch_work); spin_unlock_irqrestore(&cdev->lock, flags); @@ -1200,7 +1197,6 @@ composite_unbind(struct usb_gadget *gadget) } switch_dev_unregister(&cdev->sw_connected); switch_dev_unregister(&cdev->sw_config); - device_remove_file(&gadget->dev, &dev_attr_suspended); kfree(cdev); set_gadget_data(gadget, NULL); composite = NULL; From ef202dad1c7549c41baf29b6d0605fea52098a2e Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Sat, 30 Apr 2011 14:29:15 -0400 Subject: [PATCH 1440/2556] defconfig: regen and enable USB_ANDROID_ACCESSORY as per: CyanogenMod/cm-kernel (android-msm-2.6.37) e23855ce45fef1f91bbe718a8ff302ed2e93ccb7 ([ARM] msm: mahimahi: enable CONFIG_USB_ANDROID_ACCESSORY) Author: Mike Lockwood () --- arch/arm/configs/pershoot_mahimahi_defconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/configs/pershoot_mahimahi_defconfig b/arch/arm/configs/pershoot_mahimahi_defconfig index 08a89af2f2a7a..e06e0ee003bd9 100644 --- a/arch/arm/configs/pershoot_mahimahi_defconfig +++ b/arch/arm/configs/pershoot_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.4 Kernel Configuration -# Thu Apr 21 22:04:06 2011 +# Sat Apr 30 14:28:33 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -1501,6 +1501,7 @@ CONFIG_USB_ANDROID_MASS_STORAGE=y # CONFIG_USB_ANDROID_MTP is not set CONFIG_USB_ANDROID_RNDIS=y CONFIG_USB_ANDROID_RNDIS_WCEIS=y +CONFIG_USB_ANDROID_ACCESSORY=y # CONFIG_USB_CDC_COMPOSITE is not set # CONFIG_USB_G_MULTI is not set # CONFIG_USB_G_HID is not set From d25774fe4b3fc6866ef85bcc3af0897a9c3163f4 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sat, 30 Apr 2011 15:16:50 -0400 Subject: [PATCH 1441/2556] msm: power: Enhanced power management from HTC * Support for "offmode charging" aka "hibernate mode" * Reset VFP context on suspend/resume * MSMFB power management * Acquire bootup no-halt lock for faster boot time * Add support for perflock Conflicts: arch/arm/mach-msm/Makefile --- arch/arm/include/asm/vfp.h | 6 + arch/arm/mach-msm/Kconfig | 56 ++- arch/arm/mach-msm/Makefile | 13 +- arch/arm/mach-msm/board-supersonic.c | 2 +- arch/arm/mach-msm/devices_htc.c | 158 ------ arch/arm/mach-msm/htc_set_perflock.c | 78 +++ arch/arm/mach-msm/include/mach/board_htc.h | 1 + arch/arm/mach-msm/include/mach/irqs.h | 3 +- arch/arm/mach-msm/include/mach/perflock.h | 68 +++ arch/arm/mach-msm/perflock.c | 480 +++++++++++++++++++ arch/arm/mach-msm/pm.c | 92 +++- arch/arm/vfp/vfpmodule.c | 48 ++ drivers/usb/gadget/Makefile | 7 +- drivers/video/msm/mddi_client_epson.c | 1 + drivers/video/msm/mddi_client_novb9f6_5582.c | 1 + drivers/video/msm/msm_fb.c | 102 ++-- include/linux/earlysuspend.h | 4 + kernel/power/earlysuspend.c | 163 ++++++- kernel/power/main.c | 47 +- kernel/power/power.h | 4 + 20 files changed, 1103 insertions(+), 231 deletions(-) create mode 100644 arch/arm/mach-msm/htc_set_perflock.c create mode 100644 arch/arm/mach-msm/include/mach/perflock.h create mode 100644 arch/arm/mach-msm/perflock.c diff --git a/arch/arm/include/asm/vfp.h b/arch/arm/include/asm/vfp.h index f4ab34fd4f72c..ea2e3ac0cac01 100644 --- a/arch/arm/include/asm/vfp.h +++ b/arch/arm/include/asm/vfp.h @@ -82,3 +82,9 @@ #define VFPOPDESC_UNUSED_BIT (24) #define VFPOPDESC_UNUSED_MASK (0xFF << VFPOPDESC_UNUSED_BIT) #define VFPOPDESC_OPDESC_MASK (~(VFPOPDESC_LENGTH_MASK | VFPOPDESC_UNUSED_MASK)) + +#ifndef __ASSEMBLY__ +int vfp_flush_context(void); +void vfp_reinit(void); +#endif + diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index e936fe8042f93..3b9b4715f8ea5 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -79,6 +79,44 @@ config MSM_MDP40 depends on ARCH_MSM7X30 default y +config PERFLOCK + depends on CPU_FREQ + depends on ARCH_QSD8X50 || ARCH_MSM7X30 || ARCH_MSM7X00A || ARCH_MSM7227 || ARCH_MSM7225 + default n + bool "HTC Performance Lock" + +config PERFLOCK_BOOT_LOCK + depends on PERFLOCK + depends on ARCH_QSD8X50 || ARCH_MSM7X30 || ARCH_MSM7X00A || ARCH_MSM7227 || ARCH_MSM7225 + default n + bool "Boot Time Performance Lock" + +config PERFLOCK_SCREEN_POLICY + depends on PERFLOCK + depends on ARCH_QSD8X50 || ARCH_MSM7X00A || ARCH_MSM7227 || ARCH_MSM7225 + default n + bool "Change Cpufreq Policy while Screen ON/OFF" + +config PERFLOCK_SCREEN_ON_MIN + depends on PERFLOCK_SCREEN_POLICY + int "Minimum speed while screen on" + default MSM_CPU_FREQ_MIN + +config PERFLOCK_SCREEN_ON_MAX + depends on PERFLOCK_SCREEN_POLICY + int "Maximum speed while screen on" + default MSM_CPU_FREQ_MAX + +config PERFLOCK_SCREEN_OFF_MIN + depends on PERFLOCK_SCREEN_POLICY + int "Minimum speed while screen off" + default MSM_CPU_FREQ_MIN + +config PERFLOCK_SCREEN_OFF_MAX + depends on PERFLOCK_SCREEN_POLICY + int "Maximum speed while screen off" + default MSM_CPU_FREQ_MAX + config MSM_REMOTE_SPINLOCK_DEKKERS bool @@ -317,6 +355,14 @@ config HTC_PWRSINK default y bool "HTC Power Sink Driver" +config HTC_POWER_COLLAPSE_MAGIC + default n + bool "Check Power Collapse State" + +config HTC_ONMODE_CHARGING + default n + bool "Low-power hibernate charging support" + config CACHE_FLUSH_RANGE_LIMIT hex "Cache flush range limit" default 0x40000 @@ -365,7 +411,7 @@ choice config MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT bool "Wait for interrupt" -endchoice +endchoice config MSM7X00A_SLEEP_MODE int @@ -400,7 +446,7 @@ choice config MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT bool "Wait for interrupt" -endchoice +endchoice config MSM7X00A_IDLE_SLEEP_MODE int @@ -503,7 +549,7 @@ config MSM_SCM bool config MSM_DAL - default n + default n bool "MSM Driver Access Layer (DAL RPC)" help Support for the DAL RPC interface used to communicate with @@ -618,7 +664,7 @@ config WIFI_CONTROL_FUNC bool "Enable WiFi control function abstraction" help Enables Power/Reset/Carddetect function abstraction - + config WIFI_MEM_PREALLOC depends on WIFI_CONTROL_FUNC bool "Preallocate memory for WiFi buffers" @@ -678,6 +724,6 @@ config VIRTUAL_KPANIC_SRC default "cache" help Sets the partition to steal from to make the virtual one. - + endif diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index b2f777b38d1e6..1f5a73262b265 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -1,9 +1,9 @@ +obj-y += proc_comm.o obj-y += io.o timer.o obj-y += memory.o ifndef CONFIG_ARCH_MSM8X60 obj-y += dma.o endif - obj-y += nand_partitions.o obj-y += pmic.o obj-y += drv_callback.o @@ -16,6 +16,13 @@ obj-$(CONFIG_ARCH_MSM_SCORPION) += arch-init-scorpion.o obj-$(CONFIG_ARCH_QSD8X50) += acpuclock-qsd8x50.o obj-$(CONFIG_ARCH_MSM7X30) += acpuclock-7x30.o +ifndef CONFIG_ARCH_MSM8X60 +obj-y += irq.o +endif + +obj-$(CONFIG_ARCH_MSM8X60) += clock-dummy.o iommu.o iommu_dev.o devices-msm8x60-iommu.o +obj-$(CONFIG_MSM_PROC_COMM) += proc_comm.o clock-pcom.o vreg.o +obj-$(CONFIG_MSM_PROC_COMM) += clock.o obj-$(CONFIG_ARCH_QSD8X50) += sirc.o obj-$(CONFIG_MSM_FIQ_SUPPORT) += fiq_glue.o obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o @@ -101,7 +108,9 @@ obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-tpa2018d1.o obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-tpa6130.o obj-$(CONFIG_MACH_SUPERSONIC) += htc_wifi_nvs.o htc_bluetooth.o obj-$(CONFIG_MACH_SUPERSONIC) += htc_acoustic_qsd.o +obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-flashlight.o +obj-$(CONFIG_HTC_BATTCHG) += htc_battery.o obj-$(CONFIG_HTC_PWRSINK) += htc_pwrsink.o obj-$(CONFIG_HTC_PWRSPLY) += htc_power_supply.o obj-$(CONFIG_HTC_HEADSET) += htc_headset.o @@ -111,6 +120,8 @@ obj-$(CONFIG_MICROP_COMMON) += atmega_microp_common.o obj-$(CONFIG_HTC_HEADSET_MGR) += htc_headset_mgr.o obj-$(CONFIG_HTC_HEADSET_GPIO) += htc_headset_gpio.o obj-$(CONFIG_HTC_HEADSET_MICROP) += htc_headset_microp.o +obj-$(CONFIG_PERFLOCK) += perflock.o +obj-$(CONFIG_PERFLOCK) += htc_set_perflock.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-tpa2018d1.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-smb329.o diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index 935775150548e..7a97ebd604a15 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -53,7 +53,7 @@ #include #include #include -#include +#include "board-supersonic-flashlight.h" #include #include diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c index e23d8d7e033da..a69053f6692ed 100644 --- a/arch/arm/mach-msm/devices_htc.c +++ b/arch/arm/mach-msm/devices_htc.c @@ -354,162 +354,4 @@ int __init msm_add_serial_devices(unsigned num) return platform_device_register(msm_serial_devices[num]); } #endif -<<<<<<< HEAD -#define ATAG_SMI 0x4d534D71 -/* setup calls mach->fixup, then parse_tags, parse_cmdline - * We need to setup meminfo in mach->fixup, so this function - * will need to traverse each tag to find smi tag. - */ -int __init parse_tag_smi(const struct tag *tags) -{ - int smi_sz = 0, find = 0; - struct tag *t = (struct tag *)tags; - - for (; t->hdr.size; t = tag_next(t)) { - if (t->hdr.tag == ATAG_SMI) { - printk(KERN_DEBUG "find the smi tag\n"); - find = 1; - break; - } - } - if (!find) - return -1; - - printk(KERN_DEBUG "parse_tag_smi: smi size = %d\n", t->u.mem.size); - smi_sz = t->u.mem.size; - return smi_sz; -} -__tagtable(ATAG_SMI, parse_tag_smi); - - -#define ATAG_HWID 0x4d534D72 -int __init parse_tag_hwid(const struct tag *tags) -{ - int hwid = 0, find = 0; - struct tag *t = (struct tag *)tags; - - for (; t->hdr.size; t = tag_next(t)) { - if (t->hdr.tag == ATAG_HWID) { - printk(KERN_DEBUG "find the hwid tag\n"); - find = 1; - break; - } - } - - if (find) - hwid = t->u.revision.rev; - printk(KERN_DEBUG "parse_tag_hwid: hwid = 0x%x\n", hwid); - return hwid; -} -__tagtable(ATAG_HWID, parse_tag_hwid); - -#define ATAG_SKUID 0x4d534D73 -int __init parse_tag_skuid(const struct tag *tags) -{ - int skuid = 0, find = 0; - struct tag *t = (struct tag *)tags; - - for (; t->hdr.size; t = tag_next(t)) { - if (t->hdr.tag == ATAG_SKUID) { - printk(KERN_DEBUG "find the skuid tag\n"); - find = 1; - break; - } - } - - if (find) - skuid = t->u.revision.rev; - printk(KERN_DEBUG "parse_tag_skuid: hwid = 0x%x\n", skuid); - return skuid; -} -__tagtable(ATAG_SKUID, parse_tag_skuid); - -#define ATAG_HERO_PANEL_TYPE 0x4d534D74 -int panel_type; -int __init tag_panel_parsing(const struct tag *tags) -{ - panel_type = tags->u.revision.rev; - - printk(KERN_DEBUG "%s: panel type = %d\n", __func__, - panel_type); - - return panel_type; -} -__tagtable(ATAG_HERO_PANEL_TYPE, tag_panel_parsing); - -#define ATAG_ENGINEERID 0x4d534D75 -int __init parse_tag_engineerid(const struct tag *tags) -{ - int engineerid = 0, find = 0; - struct tag *t = (struct tag *)tags; - - for (; t->hdr.size; t = tag_next(t)) { - if (t->hdr.tag == ATAG_ENGINEERID) { - printk(KERN_DEBUG "find the engineer tag\n"); - find = 1; - break; - } - } - - if (find) - engineerid = t->u.revision.rev; - printk(KERN_DEBUG "parse_tag_engineerid: hwid = 0x%x\n", engineerid); - return engineerid; -} -__tagtable(ATAG_ENGINEERID, parse_tag_engineerid); - -static int mfg_mode; -int __init board_mfg_mode_init(char *s) -{ - if (!strcmp(s, "normal")) - mfg_mode = 0; - else if (!strcmp(s, "factory2")) - mfg_mode = 1; - else if (!strcmp(s, "recovery")) - mfg_mode = 2; - else if (!strcmp(s, "charge")) - mfg_mode = 3; - - return 1; -} -__setup("androidboot.mode=", board_mfg_mode_init); - - -int board_mfg_mode(void) -{ - return mfg_mode; -} - -static int __init board_serialno_setup(char *serialno) -{ -#ifdef CONFIG_USB_ANDROID_RNDIS - int i; - char *src; -#endif - char *str; - - /* use default serial number when mode is factory2 */ - if (mfg_mode == 1 || !strlen(serialno)) - str = df_serialno; - else - str = serialno; - -#ifdef CONFIG_USB_ANDROID_RNDIS - /* create a fake MAC address from our serial number. - * first byte is 0x02 to signify locally administered. - */ - rndis_pdata.ethaddr[0] = 0x02; - src = str; - for (i = 0; *src; i++) { - /* XOR the USB serial across the remaining bytes */ - rndis_pdata.ethaddr[i % (ETH_ALEN - 1) + 1] ^= *src++; - } -#endif - android_usb_pdata.serial_number = str; - return 1; -} - -__setup("androidboot.serialno=", board_serialno_setup); -======= ->>>>>>> 0bf3625... msm: Consolidate code for reading ATAG data diff --git a/arch/arm/mach-msm/htc_set_perflock.c b/arch/arm/mach-msm/htc_set_perflock.c new file mode 100644 index 0000000000000..3509f08ef6820 --- /dev/null +++ b/arch/arm/mach-msm/htc_set_perflock.c @@ -0,0 +1,78 @@ +/* arch/arm/mach-msm/htc_set_perflock.c + * + * Copyright (C) 2008 HTC Corporation + * Author: Eiven Peng + * + * 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 + +static struct mutex lock; +static struct perf_lock media_perf_lock; +static uint32_t num = 0; + +static int perflock_open(struct inode *inode, struct file *file) +{ + mutex_lock(&lock); + if (num == 0) { + perf_lock(&media_perf_lock); + printk(KERN_DEBUG "[perflock] Perflock enabled.\n"); + } + num++; + printk(KERN_DEBUG "[perflock] Perflock node is opened by [%s]/[PID=%d],numbers of opened nodes = [%d].\n", + current->comm, current->pid, num); + mutex_unlock(&lock); + + return 0; +} + +static int perflock_release(struct inode *inode, struct file *file) +{ + mutex_lock(&lock); + num--; + printk(KERN_DEBUG "[perflock] Perflock node is closed by [%s]/[PID=%d], numbers of opened nodes = [%d].\n", + current->comm, current->pid, num); + if (num == 0) { + perf_unlock(&media_perf_lock); + printk(KERN_DEBUG "[perflock] Perflock disabled.\n"); + } + mutex_unlock(&lock); + + return 0; +} + +static struct file_operations perflock_fops = { + .owner = THIS_MODULE, + .open = perflock_open, + .release = perflock_release, +}; + +struct miscdevice perflock_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "perflock", + .fops = &perflock_fops, +}; + +static int __init set_perflock_init(void) { + mutex_init(&lock); + perf_lock_init(&media_perf_lock, PERF_LOCK_HIGHEST, "media"); + return misc_register(&perflock_misc); +} + +device_initcall(set_perflock_init); diff --git a/arch/arm/mach-msm/include/mach/board_htc.h b/arch/arm/mach-msm/include/mach/board_htc.h index 14ee1ec40d773..9647ed1c4b3dc 100644 --- a/arch/arm/mach-msm/include/mach/board_htc.h +++ b/arch/arm/mach-msm/include/mach/board_htc.h @@ -63,6 +63,7 @@ int __init board_mfg_mode(void); int __init parse_tag_smi(const struct tag *tags); int __init parse_tag_hwid(const struct tag * tags); int __init parse_tag_skuid(const struct tag * tags); +int __init tag_panel_parsing(const struct tag *tags); int parse_tag_engineerid(const struct tag * tags); void notify_usb_connected(int online); diff --git a/arch/arm/mach-msm/include/mach/irqs.h b/arch/arm/mach-msm/include/mach/irqs.h index 63fe01fcb0e89..1fa4f73321312 100644 --- a/arch/arm/mach-msm/include/mach/irqs.h +++ b/arch/arm/mach-msm/include/mach/irqs.h @@ -44,7 +44,8 @@ #define NR_IRQS (NR_MSM_IRQS + NR_SIRC_IRQS + NR_GPIO_IRQS + NR_BOARD_IRQS \ + NR_MICROP_IRQS) #define MSM_INT_TO_GPIO(n) ((n) - NR_MSM_IRQS) -#define MSM_GPIO_TO_INT(n) (FIRST_GPIO_IRQ + (n)) +#define MSM_uP_TO_INT(n) (FIRST_MICROP_IRQ + (n)) +#define MSM_INT_TO_GPIO(n) ((n) - NR_MSM_IRQS) #define MSM_uP_TO_INT(n) (FIRST_MICROP_IRQ + (n)) #else #define NR_IRQS (NR_MSM_IRQS + NR_SIRC_IRQS + NR_GPIO_IRQS + NR_BOARD_IRQS) diff --git a/arch/arm/mach-msm/include/mach/perflock.h b/arch/arm/mach-msm/include/mach/perflock.h new file mode 100644 index 0000000000000..34fcd7534e778 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/perflock.h @@ -0,0 +1,68 @@ +/* arch/arm/mach-msm/perflock.h + * + * MSM performance lock driver header + * + * Copyright (C) 2008 HTC Corporation + * Author: Eiven Peng + * + * 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 __ARCH_ARM_MACH_PERF_LOCK_H +#define __ARCH_ARM_MACH_PERF_LOCK_H + +#include + +/* + * Performance level determine differnt EBI1 rate + */ + +enum { + PERF_LOCK_MEDIUM, /* Medium performance */ + PERF_LOCK_HIGH, /* High performance */ + PERF_LOCK_HIGHEST, /* Highest performance */ + PERF_LOCK_INVALID, +}; + +struct perf_lock { + struct list_head link; + unsigned int flags; + unsigned int level; + const char *name; +}; + +struct perflock_platform_data { + unsigned int *perf_acpu_table; + unsigned int table_size; +}; + +#ifndef CONFIG_PERFLOCK +static inline void __init perflock_init( + struct perflock_platform_data *pdata) { return; } +static inline void perf_lock_init(struct perf_lock *lock, + unsigned int level, const char *name) { return; } +static inline void perf_lock(struct perf_lock *lock) { return; } +static inline void perf_unlock(struct perf_lock *lock) { return; } +static inline int is_perf_lock_active(struct perf_lock *lock) { return 0; } +static inline int is_perf_locked(void) { return 0; } +#else +extern void __init perflock_init(struct perflock_platform_data *pdata); +extern void perf_lock_init(struct perf_lock *lock, + unsigned int level, const char *name); +extern void perf_lock(struct perf_lock *lock); +extern void perf_unlock(struct perf_lock *lock); +extern int is_perf_lock_active(struct perf_lock *lock); +extern int is_perf_locked(void); +#endif + + +#endif + diff --git a/arch/arm/mach-msm/perflock.c b/arch/arm/mach-msm/perflock.c new file mode 100644 index 0000000000000..574dffc850a98 --- /dev/null +++ b/arch/arm/mach-msm/perflock.c @@ -0,0 +1,480 @@ +/* arch/arm/mach-msm/perflock.c + * + * Copyright (C) 2008 HTC Corporation + * Author: Eiven Peng + * + * 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 "proc_comm.h" +#include "acpuclock.h" + +#define PERF_LOCK_INITIALIZED (1U << 0) +#define PERF_LOCK_ACTIVE (1U << 1) + +enum { + PERF_LOCK_DEBUG = 1U << 0, + PERF_EXPIRE_DEBUG = 1U << 1, + PERF_CPUFREQ_NOTIFY_DEBUG = 1U << 2, + PERF_CPUFREQ_LOCK_DEBUG = 1U << 3, + PERF_SCREEN_ON_POLICY_DEBUG = 1U << 4, +}; + +static LIST_HEAD(active_perf_locks); +static LIST_HEAD(inactive_perf_locks); +static DEFINE_SPINLOCK(list_lock); +static DEFINE_SPINLOCK(policy_update_lock); +static int initialized; +static unsigned int *perf_acpu_table; +static unsigned int table_size; +static unsigned int curr_lock_speed; +static struct cpufreq_policy *cpufreq_policy; + +#ifdef CONFIG_PERF_LOCK_DEBUG +static int debug_mask = PERF_LOCK_DEBUG | PERF_EXPIRE_DEBUG | + PERF_CPUFREQ_NOTIFY_DEBUG | PERF_CPUFREQ_LOCK_DEBUG; +#else +static int debug_mask = PERF_CPUFREQ_LOCK_DEBUG | PERF_SCREEN_ON_POLICY_DEBUG; +#endif +module_param_call(debug_mask, param_set_int, param_get_int, + &debug_mask, S_IWUSR | S_IRUGO); + +static unsigned int get_perflock_speed(void); +static void print_active_locks(void); + +#ifdef CONFIG_PERFLOCK_SCREEN_POLICY +/* Increase cpufreq minumum frequency when screen on. + Pull down to lowest speed when screen off. */ +static unsigned int screen_off_policy_req; +static unsigned int screen_on_policy_req; +static void perflock_early_suspend(struct early_suspend *handler) +{ + unsigned long irqflags; + + spin_lock_irqsave(&policy_update_lock, irqflags); + if (screen_on_policy_req) { + screen_on_policy_req--; + spin_unlock_irqrestore(&policy_update_lock, irqflags); + return; + } + screen_off_policy_req++; + spin_unlock_irqrestore(&policy_update_lock, irqflags); + + if (cpufreq_policy) + cpufreq_update_policy(cpufreq_policy->cpu); +} + +static void perflock_late_resume(struct early_suspend *handler) +{ + unsigned long irqflags; + +/* + * This workaround is for hero project + * May cause potential bug: + * Accidentally set cpu in high freq in screen off mode. + * senario: in screen off early suspended state, runs the following sequence: + * 1.perflock_late_resume():acpuclk_set_rate(high freq);screen_on_pilicy_req=1; + * 2.perflock_early_suspend():if(screen_on_policy_req) return; + * 3.perflock_notifier_call(): only set policy's min and max + */ +#ifdef CONFIG_MACH_HERO + /* Work around for display driver, + * need to increase cpu speed immediately. + */ + unsigned int lock_speed = get_perflock_speed() / 1000; + if (lock_speed > CONFIG_PERFLOCK_SCREEN_ON_MIN) + acpuclk_set_rate(lock_speed * 1000, 0); + else + acpuclk_set_rate(CONFIG_PERFLOCK_SCREEN_ON_MIN * 1000, 0); +#endif + + spin_lock_irqsave(&policy_update_lock, irqflags); + if (screen_off_policy_req) { + screen_off_policy_req--; + spin_unlock_irqrestore(&policy_update_lock, irqflags); + return; + } + screen_on_policy_req++; + spin_unlock_irqrestore(&policy_update_lock, irqflags); + + if (cpufreq_policy) + cpufreq_update_policy(cpufreq_policy->cpu); +} + +static struct early_suspend perflock_power_suspend = { + .suspend = perflock_early_suspend, + .resume = perflock_late_resume, + .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, +}; + +/* 7k projects need to raise up cpu freq before panel resume for stability */ +#if defined(CONFIG_HTC_ONMODE_CHARGING) && \ + (defined(CONFIG_ARCH_MSM7225) || \ + defined(CONFIG_ARCH_MSM7227) || \ + defined(CONFIG_ARCH_MSM7201A)) +static struct early_suspend perflock_onchg_suspend = { + .suspend = perflock_early_suspend, + .resume = perflock_late_resume, + .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, +}; +#endif + +static int __init perflock_screen_policy_init(void) +{ + register_early_suspend(&perflock_power_suspend); +/* 7k projects need to raise up cpu freq before panel resume for stability */ +#if defined(CONFIG_HTC_ONMODE_CHARGING) && \ + (defined(CONFIG_ARCH_MSM7225) || \ + defined(CONFIG_ARCH_MSM7227) || \ + defined(CONFIG_ARCH_MSM7201A)) + register_onchg_suspend(&perflock_onchg_suspend); +#endif + screen_on_policy_req++; + if (cpufreq_policy) + cpufreq_update_policy(cpufreq_policy->cpu); + + return 0; +} + +late_initcall(perflock_screen_policy_init); +#endif + +#if 0 +static unsigned int policy_min = CONFIG_MSM_CPU_FREQ_MIN; +static unsigned int policy_max = CONFIG_MSM_CPU_FREQ_MAX; +#else +static unsigned int policy_min; +static unsigned int policy_max; +#endif +static int param_set_cpu_min_max(const char *val, struct kernel_param *kp) +{ + int ret; + ret = param_set_int(val, kp); + if (cpufreq_policy) + cpufreq_update_policy(cpufreq_policy->cpu); + return ret; +} + +module_param_call(min_cpu_khz, param_set_cpu_min_max, param_get_int, + &policy_min, S_IWUSR | S_IRUGO); +module_param_call(max_cpu_khz, param_set_cpu_min_max, param_get_int, + &policy_max, S_IWUSR | S_IRUGO); + +static int perflock_notifier_call(struct notifier_block *self, + unsigned long event, void *data) +{ + struct cpufreq_policy *policy = data; + unsigned int lock_speed; + unsigned long irqflags; + + spin_lock_irqsave(&policy_update_lock, irqflags); + if (debug_mask & PERF_CPUFREQ_NOTIFY_DEBUG) + pr_info("%s: event=%ld, policy->min=%d, policy->max=%d", + __func__, event, policy->min, policy->max); + + if (event == CPUFREQ_START) + cpufreq_policy = policy; + else if (event == CPUFREQ_NOTIFY) { + /* Each time cpufreq_update_policy, + * min/max will reset, need to set it again. */ +#ifdef CONFIG_PERFLOCK_SCREEN_POLICY + if (screen_on_policy_req) { + if (debug_mask & PERF_SCREEN_ON_POLICY_DEBUG) + pr_info("%s: screen_on_policy_req %d," + "policy_min %d\n", __func__, + screen_on_policy_req, + CONFIG_PERFLOCK_SCREEN_ON_MIN); + policy_min = CONFIG_PERFLOCK_SCREEN_ON_MIN; + policy_max = CONFIG_PERFLOCK_SCREEN_ON_MAX; + screen_on_policy_req--; + } else if (screen_off_policy_req) { + if (debug_mask & PERF_SCREEN_ON_POLICY_DEBUG) + pr_info("%s: screen_off_policy_req %d," + "policy_min %d\n", __func__, + screen_off_policy_req, + CONFIG_MSM_CPU_FREQ_MIN); + policy_min = CONFIG_PERFLOCK_SCREEN_OFF_MIN; + policy_max = CONFIG_PERFLOCK_SCREEN_OFF_MAX; + screen_off_policy_req--; + } +#endif + lock_speed = get_perflock_speed() / 1000; + if (lock_speed) { + policy->min = lock_speed; + policy->max = lock_speed; + if (debug_mask & PERF_CPUFREQ_LOCK_DEBUG) { + pr_info("%s: cpufreq lock speed %d\n", + __func__, lock_speed); + print_active_locks(); + } + } else { + policy->min = policy_min; + policy->max = policy_max; + if (debug_mask & PERF_CPUFREQ_LOCK_DEBUG) + pr_info("%s: cpufreq recover policy %d %d\n", + __func__, policy->min, policy->max); + } + curr_lock_speed = lock_speed; + } + spin_unlock_irqrestore(&policy_update_lock, irqflags); + + return 0; +} + +static struct notifier_block perflock_notifier = { + .notifier_call = perflock_notifier_call, +}; + +static unsigned int get_perflock_speed(void) +{ + unsigned long irqflags; + struct perf_lock *lock; + unsigned int perf_level = 0; + + /* Get the maxmimum perf level. */ + if (list_empty(&active_perf_locks)) + return 0; + + spin_lock_irqsave(&list_lock, irqflags); + list_for_each_entry(lock, &active_perf_locks, link) { + if (lock->level > perf_level) + perf_level = lock->level; + } + spin_unlock_irqrestore(&list_lock, irqflags); + + return perf_acpu_table[perf_level]; +} + +static void print_active_locks(void) +{ + unsigned long irqflags; + struct perf_lock *lock; + + spin_lock_irqsave(&list_lock, irqflags); + list_for_each_entry(lock, &active_perf_locks, link) { + pr_info("active perf lock '%s'\n", lock->name); + } + spin_unlock_irqrestore(&list_lock, irqflags); +} + +/** + * perf_lock_init - acquire a perf lock + * @lock: perf lock to acquire + * @level: performance level of @lock + * @name: the name of @lock + * + * Acquire @lock with @name and @level. (It doesn't activate the lock.) + */ +void perf_lock_init(struct perf_lock *lock, + unsigned int level, const char *name) +{ + unsigned long irqflags = 0; + + WARN_ON(!name); + WARN_ON(level >= PERF_LOCK_INVALID); + WARN_ON(lock->flags & PERF_LOCK_INITIALIZED); + + if ((!name) || (level >= PERF_LOCK_INVALID) || + (lock->flags & PERF_LOCK_INITIALIZED)) { + pr_err("%s: ERROR \"%s\" flags %x level %d\n", + __func__, name, lock->flags, level); + return; + } + lock->name = name; + lock->flags = PERF_LOCK_INITIALIZED; + lock->level = level; + + INIT_LIST_HEAD(&lock->link); + spin_lock_irqsave(&list_lock, irqflags); + list_add(&lock->link, &inactive_perf_locks); + spin_unlock_irqrestore(&list_lock, irqflags); +} +EXPORT_SYMBOL(perf_lock_init); + +/** + * perf_lock - activate a perf lock + * @lock: perf lock to activate + * + * Activate @lock.(Need to init_perf_lock before activate) + */ +void perf_lock(struct perf_lock *lock) +{ + unsigned long irqflags; + + WARN_ON(!initialized); + WARN_ON((lock->flags & PERF_LOCK_INITIALIZED) == 0); + WARN_ON(lock->flags & PERF_LOCK_ACTIVE); + + spin_lock_irqsave(&list_lock, irqflags); + if (debug_mask & PERF_LOCK_DEBUG) + pr_info("%s: '%s', flags %d level %d\n", + __func__, lock->name, lock->flags, lock->level); + if (lock->flags & PERF_LOCK_ACTIVE) { + pr_err("%s: over-locked\n", __func__); + return; + } + lock->flags |= PERF_LOCK_ACTIVE; + list_del(&lock->link); + list_add(&lock->link, &active_perf_locks); + spin_unlock_irqrestore(&list_lock, irqflags); + + /* Update cpufreq policy - scaling_min/scaling_max */ + if (cpufreq_policy && + (curr_lock_speed != (get_perflock_speed() / 1000))) + cpufreq_update_policy(cpufreq_policy->cpu); +} +EXPORT_SYMBOL(perf_lock); + +#define PERF_UNLOCK_DELAY (HZ) +static void do_expire_perf_locks(struct work_struct *work) +{ + if (debug_mask & PERF_EXPIRE_DEBUG) + pr_info("%s: timed out to unlock\n", __func__); + + if (cpufreq_policy && + (curr_lock_speed != (get_perflock_speed() / 1000))) { + if (debug_mask & PERF_EXPIRE_DEBUG) + pr_info("%s: update cpufreq policy\n", __func__); + cpufreq_update_policy(cpufreq_policy->cpu); + } +} +static DECLARE_DELAYED_WORK(work_expire_perf_locks, do_expire_perf_locks); + +/** + * perf_unlock - de-activate a perf lock + * @lock: perf lock to de-activate + * + * de-activate @lock. + */ +void perf_unlock(struct perf_lock *lock) +{ + unsigned long irqflags; + + WARN_ON(!initialized); + WARN_ON((lock->flags & PERF_LOCK_ACTIVE) == 0); + + spin_lock_irqsave(&list_lock, irqflags); + if (debug_mask & PERF_LOCK_DEBUG) + pr_info("%s: '%s', flags %d level %d\n", + __func__, lock->name, lock->flags, lock->level); + if (!(lock->flags & PERF_LOCK_ACTIVE)) { + pr_err("%s: under-locked\n", __func__); + return; + } + lock->flags &= ~PERF_LOCK_ACTIVE; + list_del(&lock->link); + list_add(&lock->link, &inactive_perf_locks); + spin_unlock_irqrestore(&list_lock, irqflags); + + /* Prevent lock/unlock quickly, add a timeout to release perf_lock */ + if (cpufreq_policy && + (curr_lock_speed != (get_perflock_speed() / 1000))) + schedule_delayed_work(&work_expire_perf_locks, + PERF_UNLOCK_DELAY); +} +EXPORT_SYMBOL(perf_unlock); + +/** + * is_perf_lock_active - query if a perf_lock is active or not + * @lock: target perf lock + * RETURN: 0: inactive; 1: active + * + * query if @lock is active or not + */ +inline int is_perf_lock_active(struct perf_lock *lock) +{ + return (lock->flags & PERF_LOCK_ACTIVE); +} +EXPORT_SYMBOL(is_perf_lock_active); + +/** + * is_perf_locked - query if there is any perf lock activates + * RETURN: 0: no perf lock activates 1: at least a perf lock activates + */ +int is_perf_locked(void) +{ + return (!list_empty(&active_perf_locks)); +} +EXPORT_SYMBOL(is_perf_locked); + + +#ifdef CONFIG_PERFLOCK_BOOT_LOCK +/* Stop cpufreq and lock cpu, shorten boot time. */ +#define BOOT_LOCK_TIMEOUT (60 * HZ) +static struct perf_lock boot_perf_lock; + +static void do_expire_boot_lock(struct work_struct *work) +{ + perf_unlock(&boot_perf_lock); + pr_info("Release 'boot-time' perf_lock\n"); +} +static DECLARE_DELAYED_WORK(work_expire_boot_lock, do_expire_boot_lock); +#endif + +static void perf_acpu_table_fixup(void) +{ + int i; + for (i = 0; i < table_size; ++i) { + if (perf_acpu_table[i] > policy_max * 1000) + perf_acpu_table[i] = policy_max * 1000; + else if (perf_acpu_table[i] < policy_min * 1000) + perf_acpu_table[i] = policy_min * 1000; + } +} + +void __init perflock_init(struct perflock_platform_data *pdata) +{ + struct cpufreq_policy policy; + struct cpufreq_frequency_table *table = + cpufreq_frequency_get_table(smp_processor_id()); + + BUG_ON(cpufreq_frequency_table_cpuinfo(&policy, table)); + policy_min = policy.cpuinfo.min_freq; + policy_max = policy.cpuinfo.max_freq; + + if (!pdata) + goto invalid_config; + + perf_acpu_table = pdata->perf_acpu_table; + table_size = pdata->table_size; + if (!perf_acpu_table || !table_size) + goto invalid_config; + if (table_size < PERF_LOCK_INVALID) + goto invalid_config; + + perf_acpu_table_fixup(); + cpufreq_register_notifier(&perflock_notifier, CPUFREQ_POLICY_NOTIFIER); + + initialized = 1; + +#ifdef CONFIG_PERFLOCK_BOOT_LOCK + /* Stop cpufreq and lock cpu, shorten boot time. */ + perf_lock_init(&boot_perf_lock, PERF_LOCK_HIGHEST, "boot-time"); + perf_lock(&boot_perf_lock); + schedule_delayed_work(&work_expire_boot_lock, BOOT_LOCK_TIMEOUT); + pr_info("Acquire 'boot-time' perf_lock\n"); +#endif + + return; + +invalid_config: + pr_err("%s: invalid configuration data, %p %d %d\n", __func__, + perf_acpu_table, table_size, PERF_LOCK_INVALID); +} diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index 0997341981c2f..801ab7643c172 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -29,6 +29,9 @@ #include #include #include +#ifdef CONFIG_VFP +#include +#endif #include "smd_private.h" #include "acpuclock.h" @@ -130,6 +133,7 @@ void msm_timer_exit_idle(int low_power); int msm_irq_idle_sleep_allowed(void); int msm_irq_pending(void); int clks_print_running(void); +extern int board_mfg_mode(void); #ifdef CONFIG_AXI_SCREEN_POLICY static int axi_rate; @@ -198,7 +202,7 @@ msm_pm_wait_state(uint32_t wait_all_set, uint32_t wait_all_clear, for (i = 0; i < 100000; i++) { state = smsm_get_state(PM_SMSM_READ_STATE); - if (((wait_all_set || wait_all_clear) && + if (((wait_all_set || wait_all_clear) && !(~state & wait_all_set) && !(state & wait_all_clear)) || (state & wait_any_set) || (~state & wait_any_clear)) return 0; @@ -209,23 +213,37 @@ msm_pm_wait_state(uint32_t wait_all_set, uint32_t wait_all_clear, return -ETIMEDOUT; } +/* + * For speeding up boot time: + * During booting up, disable entering arch_idle() by disable_hlt() + * Enable it after booting up BOOT_LOCK_TIMEOUT sec. + */ +#define BOOT_LOCK_TIMEOUT_NORMAL (60 * HZ) +#define BOOT_LOCK_TIMEOUT_SHORT (10 * HZ) +static void do_expire_boot_lock(struct work_struct *work) +{ + enable_hlt(); + pr_info("Release 'boot-time' no_halt_lock\n"); +} +static DECLARE_DELAYED_WORK(work_expire_boot_lock, do_expire_boot_lock); + static void msm_pm_enter_prep_hw(void) { #if defined(CONFIG_ARCH_MSM7X30) - writel(1, A11S_PWRDOWN); - writel(4, A11S_SECOP); + writel(1, A11S_PWRDOWN); + writel(4, A11S_SECOP); #else #if defined(CONFIG_ARCH_QSD8X50) - writel(0x1b, A11S_CLK_SLEEP_EN); + writel(0x1b, A11S_CLK_SLEEP_EN); #else - writel(0x1f, A11S_CLK_SLEEP_EN); + writel(0x1f, A11S_CLK_SLEEP_EN); #endif - writel(1, A11S_PWRDOWN); - writel(0, A11S_STANDBY_CTL); + writel(1, A11S_PWRDOWN); + writel(0, A11S_STANDBY_CTL); #if defined(CONFIG_ARCH_MSM_ARM11) - writel(0, A11RAMBACKBIAS); + writel(0, A11RAMBACKBIAS); #endif #endif } @@ -248,6 +266,13 @@ void msm_fiq_exit_sleep(void); static inline void msm_fiq_exit_sleep(void) { } #endif +#ifdef CONFIG_HTC_POWER_COLLAPSE_MAGIC +/* Set magic number in SMEM for power collapse state */ +#define HTC_POWER_COLLAPSE_ADD (MSM_SHARED_RAM_BASE + 0x000F8000 + 0x000007F8) +#define HTC_POWER_COLLAPSE_MAGIC_NUM (HTC_POWER_COLLAPSE_ADD - 0x04) +unsigned int magic_num; +#endif + static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) { uint32_t saved_vector[2]; @@ -389,6 +414,10 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) clk_set_rate(axi_clk, sleep_axi_rate); #endif } +#ifdef CONFIG_HTC_POWER_COLLAPSE_MAGIC + magic_num = 0xAAAA1111; + writel(magic_num, HTC_POWER_COLLAPSE_MAGIC_NUM); +#endif if (sleep_mode < MSM_PM_SLEEP_MODE_APPS_SLEEP) { if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) smsm_print_sleep_info(); @@ -400,10 +429,23 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) printk(KERN_INFO "msm_sleep(): vector %x %x -> " "%x %x\n", saved_vector[0], saved_vector[1], msm_pm_reset_vector[0], msm_pm_reset_vector[1]); +#ifdef CONFIG_VFP + if (from_idle) + vfp_flush_context(); +#endif + + if (!from_idle) printk(KERN_INFO "[R] suspend end\n"); + /* reset idle sleep mode when suspend. */ + if (!from_idle) msm_pm_idle_sleep_mode = CONFIG_MSM7X00A_IDLE_SLEEP_MODE; collapsed = msm_pm_collapse(); + if (!from_idle) printk(KERN_INFO "[R] resume start\n"); msm_pm_reset_vector[0] = saved_vector[0]; msm_pm_reset_vector[1] = saved_vector[1]; if (collapsed) { +#ifdef CONFIG_VFP + if (from_idle) + vfp_reinit(); +#endif cpu_init(); __asm__("cpsie a"); msm_fiq_exit_sleep(); @@ -419,7 +461,10 @@ static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle) msm_arch_idle(); rv = 0; } - +#ifdef CONFIG_HTC_POWER_COLLAPSE_MAGIC + magic_num = 0xBBBB9999; + writel(magic_num, HTC_POWER_COLLAPSE_MAGIC_NUM); +#endif if (sleep_mode <= MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT) { if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK) printk(KERN_INFO "msm_sleep(): exit power collapse %ld" @@ -740,8 +785,8 @@ void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns) if (msm_pm_debug_mask & MSM_PM_DEBUG_SUSPEND) printk("%s: Requested %lldns (%lldbs), Giving %ubs\n", - __func__, max_sleep_time_ns, - max_sleep_time_bs, + __func__, max_sleep_time_ns, + max_sleep_time_bs, msm_pm_max_sleep_time); } EXPORT_SYMBOL(msm_pm_set_max_sleep_time); @@ -787,6 +832,29 @@ static void __init msm_pm_axi_init(void) } #endif +static void __init boot_lock_nohalt(void) +{ + int nohalt_timeout; + + /* normal/factory2/recovery */ + switch (board_mfg_mode()) { + case 0: /* normal */ + case 1: /* factory2 */ + case 2: /* recovery */ + nohalt_timeout = BOOT_LOCK_TIMEOUT_NORMAL; + break; + case 3: /* charge */ + case 4: /* power_test */ + case 5: /* offmode_charge */ + default: + nohalt_timeout = BOOT_LOCK_TIMEOUT_SHORT; + break; + } + disable_hlt(); + schedule_delayed_work(&work_expire_boot_lock, nohalt_timeout); + pr_info("Acquire 'boot-time' no_halt_lock %ds\n", nohalt_timeout / HZ); +} + static int __init msm_pm_init(void) { pm_power_off = msm_pm_power_off; @@ -809,6 +877,8 @@ static int __init msm_pm_init(void) create_proc_read_entry("msm_pm_stats", S_IRUGO, NULL, msm_pm_read_proc, NULL); #endif + + boot_lock_nohalt(); return 0; } diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index 0797cb528b463..7ed63ac12a96e 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c @@ -377,6 +377,54 @@ static void vfp_enable(void *unused) set_copro_access(access | CPACC_FULL(10) | CPACC_FULL(11)); } +int vfp_flush_context(void) +{ + unsigned long flags; + struct thread_info *ti; + u32 fpexc; + u32 cpu; + int saved = 0; + + local_irq_save(flags); + + ti = current_thread_info(); + fpexc = fmrx(FPEXC); + cpu = ti->cpu; + +#ifdef CONFIG_SMP + /* On SMP, if VFP is enabled, save the old state */ + if ((fpexc & FPEXC_EN) && last_VFP_context[cpu]) { + last_VFP_context[cpu]->hard.cpu = cpu; +#else + /* If there is a VFP context we must save it. */ + if (last_VFP_context[cpu]) { + /* Enable VFP so we can save the old state. */ + fmxr(FPEXC, fpexc | FPEXC_EN); + isb(); +#endif + vfp_save_state(last_VFP_context[cpu], fpexc); + + /* disable, just in case */ + fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); + saved = 1; + } + last_VFP_context[cpu] = NULL; + + local_irq_restore(flags); + + return saved; +} + +void vfp_reinit(void) +{ + /* ensure we have access to the vfp */ + vfp_enable(NULL); + + /* and disable it to ensure the next usage restores the state */ + fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); +} + + #ifdef CONFIG_PM #include diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index b4bcfa5702599..e142ac3074b2e 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -24,12 +24,7 @@ obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o - -ifeq ($(CONFIG_USB_MSM_72K_HTC),y) - obj-$(CONFIG_USB_MSM_72K_HTC) += msm72k_udc_htc.o -else - obj-$(CONFIG_USB_MSM_72K) += msm72k_udc.o -endif +obj-$(CONFIG_USB_MSM_72K_HTC) += msm72k_udc_htc.o # # USB gadget drivers diff --git a/drivers/video/msm/mddi_client_epson.c b/drivers/video/msm/mddi_client_epson.c index 7517acf10f9d7..d5ea5e3e9d223 100644 --- a/drivers/video/msm/mddi_client_epson.c +++ b/drivers/video/msm/mddi_client_epson.c @@ -20,6 +20,7 @@ #include #include #include +#include static DECLARE_WAIT_QUEUE_HEAD(epson_vsync_wait); diff --git a/drivers/video/msm/mddi_client_novb9f6_5582.c b/drivers/video/msm/mddi_client_novb9f6_5582.c index 47575ce7b8106..0229530a27efb 100644 --- a/drivers/video/msm/mddi_client_novb9f6_5582.c +++ b/drivers/video/msm/mddi_client_novb9f6_5582.c @@ -20,6 +20,7 @@ #include #include #include +#include static DECLARE_WAIT_QUEUE_HEAD(novtec_vsync_wait); diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index 7dc33011f8255..588fe7ca0f5dd 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -35,6 +35,10 @@ #include #include "mdp_hw.h" +extern void start_drawing_late_resume(struct early_suspend *h); +static void msmfb_resume_handler(struct early_suspend *h); +static void msmfb_resume(struct work_struct *work); + #define MSMFB_DEBUG 1 #ifdef CONFIG_FB_MSM_LOGO #define INIT_IMAGE_FILE "/logo.rle" @@ -63,7 +67,7 @@ extern wait_queue_head_t panel_update_wait_queue; #define DLOG(mask, fmt, args...) \ do { \ - if (msmfb_debug_mask & mask) \ + if ((msmfb_debug_mask | SUSPEND_RESUME) & mask) \ printk(KERN_INFO "msmfb: "fmt, ##args); \ } while (0) @@ -94,14 +98,12 @@ struct msmfb_info { int ebottom; /* exclusive */ } update_info; char *black; - - struct early_suspend earlier_suspend; - struct early_suspend early_suspend; - #ifdef CONFIG_HTC_ONMODE_CHARGING struct early_suspend onchg_earlier_suspend; struct early_suspend onchg_suspend; #endif + struct early_suspend earlier_suspend; + struct early_suspend early_suspend; struct wake_lock idle_lock; spinlock_t update_lock; @@ -109,10 +111,12 @@ struct msmfb_info { wait_queue_head_t frame_wq; struct workqueue_struct *resume_workqueue; struct work_struct resume_work; + struct work_struct msmfb_resume_work; struct msmfb_callback dma_callback; struct msmfb_callback vsync_callback; struct hrtimer fake_vsync; ktime_t vsync_request_time; + unsigned fb_resumed; }; #ifdef CONFIG_FB_MSM_OVERLAY @@ -318,7 +322,10 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, /* Jay, 8/1/09' */ msmfb_set_var(msmfb->fb->screen_base, yoffset); #endif + if (msmfb->sleeping != AWAKE) + DLOG(SUSPEND_RESUME, "pan_update in state(%d)\n", msmfb->sleeping); +restart: spin_lock_irqsave(&msmfb->update_lock, irq_flags); /* if we are sleeping, on a pan_display wait 10ms (to throttle back @@ -454,36 +461,32 @@ static void power_on_panel(struct work_struct *work) mutex_unlock(&msmfb->panel_init_lock); } - -static BLOCKING_NOTIFIER_HEAD(display_chain_head); -int register_display_notifier(struct notifier_block *nb) -{ - return blocking_notifier_chain_register(&display_chain_head, nb); -} - -static int display_notifier_callback(struct notifier_block *nfb, - unsigned long action, - void *ignored) -{ +static BLOCKING_NOTIFIER_HEAD(display_chain_head); +int register_display_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&display_chain_head, nb); +} +static int display_notifier_callback(struct notifier_block *nfb, + unsigned long action, + void *ignored) +{ //struct msmfb_info *msm_fb = (struct msmfb_info *)ignored; - + switch (action) { case NOTIFY_MSM_FB: - printk(KERN_DEBUG "NOTIFY_MSM_FB\n"); - //msmfb_resume(&msm_fb->early_suspend); - break; + printk(KERN_DEBUG "NOTIFY_MSM_FB\n"); + //msmfb_resume(&msm_fb->early_suspend); + break; case NOTIFY_POWER: - /* nothing to do */ - break; + /* nothing to do */ + break; default: - printk(KERN_ERR "%s: unknown action in 0x%lx\n", - __func__, action); - return NOTIFY_BAD; + printk(KERN_ERR "%s: unknown action in 0x%lx\n", + __func__, action); + return NOTIFY_BAD; } return NOTIFY_OK; -} - -/* -------------------------------------------------------------------------- */ +} #ifdef CONFIG_HAS_EARLYSUSPEND /* turn off the panel */ @@ -528,17 +531,18 @@ static void msmfb_suspend(struct early_suspend *h) pr_info("wait event : %X\n", overlay_event.waked_up); #endif panel->suspend(panel); + msmfb->fb_resumed = 0; mutex_unlock(&msmfb->panel_init_lock); } static void msmfb_resume_handler(struct early_suspend *h) { struct msmfb_info *msmfb = container_of(h, struct msmfb_info, - early_suspend); + early_suspend); #ifdef CONFIG_HTC_ONMODE_CHARGING if (msmfb->fb_resumed == 1) { - DLOG(SUSPEND_RESUME, "fb is resumed by onchg. skip resume\n"); - return; + DLOG(SUSPEND_RESUME, "fb is resumed by onchg. skip resume\n"); + return; } #endif queue_work(msmfb->resume_workqueue, &msmfb->msmfb_resume_work); @@ -549,30 +553,26 @@ static void msmfb_resume_handler(struct early_suspend *h) static void msmfb_onchg_earlier_suspend(struct early_suspend *h) { struct msmfb_info *msmfb = container_of(h, struct msmfb_info, - onchg_earlier_suspend); + onchg_earlier_suspend); struct msm_panel_data *panel = msmfb->panel; - unsigned long irq_flags; + unsigned long irq_flags=0; - if (panel->resume(panel)) { - printk(KERN_INFO "msmfb: panel resume failed, not resuming " - "fb\n"); - return; - } + mutex_lock(&msmfb->panel_init_lock); + msmfb->sleeping = SLEEPING; + wake_up(&msmfb->frame_wq); spin_lock_irqsave(&msmfb->update_lock, irq_flags); - msmfb->frame_requested = msmfb->frame_done = msmfb->update_frame = 0; - msmfb->sleeping = WAKING; - DLOG(SUSPEND_RESUME, "ready, waiting for full update\n"); spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); - wait_event_timeout(msmfb->frame_wq, - msmfb->frame_requested == msmfb->frame_done, HZ/10); #ifndef CONFIG_MSM_MDP40 mdp->dma(mdp, virt_to_phys(msmfb->black), 0, msmfb->fb->var.xres, msmfb->fb->var.yres, 0, 0, NULL, panel->interface_type); mdp->dma_wait(mdp, panel->interface_type); #endif + wait_event_timeout(msmfb->frame_wq, + msmfb->frame_requested == msmfb->frame_done, HZ/10); + /* turn off the panel */ panel->blank(panel); } @@ -1116,14 +1116,25 @@ static int msmfb_probe(struct platform_device *pdev) wake_lock_init(&msmfb->idle_lock, WAKE_LOCK_IDLE, "msmfb_idle_lock"); #ifdef CONFIG_HAS_EARLYSUSPEND + INIT_WORK(&msmfb->msmfb_resume_work, msmfb_resume); msmfb->early_suspend.suspend = msmfb_suspend; - msmfb->early_suspend.resume = msmfb_resume; + msmfb->early_suspend.resume = msmfb_resume_handler; msmfb->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; register_early_suspend(&msmfb->early_suspend); msmfb->earlier_suspend.suspend = msmfb_earlier_suspend; msmfb->earlier_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN; register_early_suspend(&msmfb->earlier_suspend); +#ifdef CONFIG_HTC_ONMODE_CHARGING + msmfb->onchg_suspend.suspend = msmfb_onchg_suspend; + msmfb->onchg_suspend.resume = msmfb_onchg_resume_handler; + msmfb->onchg_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + register_onchg_suspend(&msmfb->onchg_suspend); + + msmfb->onchg_earlier_suspend.suspend = msmfb_onchg_earlier_suspend; + msmfb->onchg_earlier_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN; + register_onchg_suspend(&msmfb->onchg_earlier_suspend); +#endif #endif #if MSMFB_DEBUG @@ -1165,6 +1176,8 @@ static int msmfb_probe(struct platform_device *pdev) fb->var.yres, 0, 1); } #endif + /* Jay, 29/12/08' */ + display_notifier(display_notifier_callback, NOTIFY_MSM_FB); return 0; error_register_framebuffer: @@ -1205,6 +1218,7 @@ static void msmfb_shutdown(struct platform_device *pdev) static struct platform_driver msm_panel_driver = { /* need to write remove */ .probe = msmfb_probe, + .shutdown = msmfb_shutdown, .driver = {.name = "msm_panel"}, }; diff --git a/include/linux/earlysuspend.h b/include/linux/earlysuspend.h index 8343b817af31b..772b94f562053 100755 --- a/include/linux/earlysuspend.h +++ b/include/linux/earlysuspend.h @@ -47,6 +47,10 @@ struct early_suspend { #ifdef CONFIG_HAS_EARLYSUSPEND void register_early_suspend(struct early_suspend *handler); void unregister_early_suspend(struct early_suspend *handler); +#ifdef CONFIG_HTC_ONMODE_CHARGING +void register_onchg_suspend(struct early_suspend *handler); +void unregister_onchg_suspend(struct early_suspend *handler); +#endif #else #define register_early_suspend(handler) do { } while (0) #define unregister_early_suspend(handler) do { } while (0) diff --git a/kernel/power/earlysuspend.c b/kernel/power/earlysuspend.c index 84bed51dcdce7..1c26be4e9015b 100644 --- a/kernel/power/earlysuspend.c +++ b/kernel/power/earlysuspend.c @@ -38,11 +38,22 @@ static DECLARE_WORK(early_suspend_work, early_suspend); static DECLARE_WORK(late_resume_work, late_resume); static DEFINE_SPINLOCK(state_lock); enum { + SUSPENDED_ON = 0x0, SUSPEND_REQUESTED = 0x1, SUSPENDED = 0x2, SUSPEND_REQUESTED_AND_SUSPENDED = SUSPEND_REQUESTED | SUSPENDED, }; static int state; +#ifdef CONFIG_HTC_ONMODE_CHARGING +static LIST_HEAD(onchg_suspend_handlers); +static void onchg_suspend(struct work_struct *work); +static void onchg_resume(struct work_struct *work); + +static DECLARE_WORK(onchg_suspend_work, onchg_suspend); +static DECLARE_WORK(onchg_resume_work, onchg_resume); + +static int state_onchg; +#endif void register_early_suspend(struct early_suspend *handler) { @@ -76,10 +87,15 @@ static void early_suspend(struct work_struct *work) unsigned long irqflags; int abort = 0; + pr_info("[R] early_suspend start\n"); mutex_lock(&early_suspend_lock); spin_lock_irqsave(&state_lock, irqflags); - if (state == SUSPEND_REQUESTED) + if (state == SUSPEND_REQUESTED) { state |= SUSPENDED; +#ifdef CONFIG_HTC_ONMODE_CHARGING + state_onchg = SUSPEND_REQUESTED_AND_SUSPENDED; +#endif + } else abort = 1; spin_unlock_irqrestore(&state_lock, irqflags); @@ -102,12 +118,14 @@ static void early_suspend(struct work_struct *work) if (debug_mask & DEBUG_SUSPEND) pr_info("early_suspend: sync\n"); + pr_info("[R] early_suspend: sync\n"); sys_sync(); abort: spin_lock_irqsave(&state_lock, irqflags); if (state == SUSPEND_REQUESTED_AND_SUSPENDED) wake_unlock(&main_wake_lock); spin_unlock_irqrestore(&state_lock, irqflags); + pr_info("[R] early_suspend end\n"); } static void late_resume(struct work_struct *work) @@ -116,10 +134,15 @@ static void late_resume(struct work_struct *work) unsigned long irqflags; int abort = 0; + pr_info("[R] late_resume start\n"); mutex_lock(&early_suspend_lock); spin_lock_irqsave(&state_lock, irqflags); - if (state == SUSPENDED) + if (state == SUSPENDED) { state &= ~SUSPENDED; +#ifdef CONFIG_HTC_ONMODE_CHARGING + state_onchg &= ~SUSPEND_REQUESTED_AND_SUSPENDED; +#endif + } else abort = 1; spin_unlock_irqrestore(&state_lock, irqflags); @@ -138,7 +161,143 @@ static void late_resume(struct work_struct *work) pr_info("late_resume: done\n"); abort: mutex_unlock(&early_suspend_lock); + pr_info("[R] late_resume end\n"); +} + +#ifdef CONFIG_HTC_ONMODE_CHARGING +void register_onchg_suspend(struct early_suspend *handler) +{ + struct list_head *pos; + mutex_lock(&early_suspend_lock); + list_for_each(pos, &onchg_suspend_handlers) { + struct early_suspend *e; + e = list_entry(pos, struct early_suspend, link); + if (e->level > handler->level) + break; + } + list_add_tail(&handler->link, pos); + mutex_unlock(&early_suspend_lock); +} +EXPORT_SYMBOL(register_onchg_suspend); + +void unregister_onchg_suspend(struct early_suspend *handler) +{ + mutex_lock(&early_suspend_lock); + list_del(&handler->link); + mutex_unlock(&early_suspend_lock); +} +EXPORT_SYMBOL(unregister_onchg_suspend); + + +static void onchg_suspend(struct work_struct *work) +{ + struct early_suspend *pos; + unsigned long irqflags; + int abort = 0; + + pr_info("[R] onchg_suspend start\n"); + mutex_lock(&early_suspend_lock); + spin_lock_irqsave(&state_lock, irqflags); + if (state == SUSPEND_REQUESTED_AND_SUSPENDED && + state_onchg == SUSPEND_REQUESTED) + state_onchg |= SUSPENDED; + else + abort = 1; + spin_unlock_irqrestore(&state_lock, irqflags); + + if (abort) { + if (debug_mask & DEBUG_SUSPEND) + pr_info("onchg_suspend: abort, state %d, state_onchg: %d\n", state, state_onchg); + mutex_unlock(&early_suspend_lock); + goto abort; + } + + if (debug_mask & DEBUG_SUSPEND) + pr_info("onchg_suspend: call handlers\n"); + + list_for_each_entry(pos, &onchg_suspend_handlers, link) { + if (pos->suspend != NULL) + pos->suspend(pos); + } + mutex_unlock(&early_suspend_lock); + +abort: + pr_info("[R] onchg_suspend end\n"); +} + +static void onchg_resume(struct work_struct *work) +{ + struct early_suspend *pos; + unsigned long irqflags; + int abort = 0; + + pr_info("[R] onchg_resume start\n"); + mutex_lock(&early_suspend_lock); + spin_lock_irqsave(&state_lock, irqflags); + if ( state == SUSPEND_REQUESTED_AND_SUSPENDED && + state_onchg == SUSPENDED) + state_onchg &= ~SUSPENDED; + else + abort = 1; + spin_unlock_irqrestore(&state_lock, irqflags); + + if (abort) { + if (debug_mask & DEBUG_SUSPEND) + pr_info("onchg_resume: abort, state %d, state_onchg: %d\n", state, state_onchg); + goto abort; + } + if (debug_mask & DEBUG_SUSPEND) + pr_info("onchg_resume: call handlers\n"); + list_for_each_entry_reverse(pos, &onchg_suspend_handlers, link) + if (pos->resume != NULL) + pos->resume(pos); + if (debug_mask & DEBUG_SUSPEND) + pr_info("onchg_resume: done\n"); +abort: + mutex_unlock(&early_suspend_lock); + pr_info("[R] onchg_resume end\n"); +} + +void request_onchg_state(int on) +{ + unsigned long irqflags; + int old_sleep; + + spin_lock_irqsave(&state_lock, irqflags); + if (debug_mask & DEBUG_USER_STATE) { + struct timespec ts; + struct rtc_time tm; + getnstimeofday(&ts); + rtc_time_to_tm(ts.tv_sec, &tm); + pr_info("request_onchg_state: %s (%d.%d)->%d at %lld " + "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", + on == 1 ? "on" : "off", + state, + !(state_onchg & SUSPEND_REQUESTED), + on, + ktime_to_ns(ktime_get()), + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); + } + if (state == SUSPEND_REQUESTED_AND_SUSPENDED) { + old_sleep = state_onchg & SUSPEND_REQUESTED; + if (!old_sleep && on == 0) { + state_onchg |= SUSPEND_REQUESTED; + queue_work(suspend_work_queue, &onchg_suspend_work); + } + else if (old_sleep && on ==1) { + state_onchg &= ~SUSPEND_REQUESTED; + queue_work(suspend_work_queue, &onchg_resume_work); + } + } + spin_unlock_irqrestore(&state_lock, irqflags); +} + +int get_onchg_state(void) +{ + return state_onchg; } +#endif void request_suspend_state(suspend_state_t new_state) { diff --git a/kernel/power/main.c b/kernel/power/main.c index bd70a6f21befb..50f7f4441bd88 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -3,7 +3,7 @@ * * Copyright (c) 2003 Patrick Mochel * Copyright (c) 2003 Open Source Development Lab - * + * * This file is released under the GPLv2 * */ @@ -144,7 +144,7 @@ struct kobject *power_kobj; * 'standby' (Power-On Suspend), 'mem' (Suspend-to-RAM), and * 'disk' (Suspend-to-Disk). * - * store() accepts one of those strings, translates it into the + * store() accepts one of those strings, translates it into the * proper enumerated value, and initiates a suspend transition. */ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, @@ -316,6 +316,46 @@ power_attr(wake_lock); power_attr(wake_unlock); #endif +#ifdef CONFIG_HTC_ONMODE_CHARGING +static ssize_t state_onchg_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + char *s = buf; + if (get_onchg_state()) + s += sprintf(s, "chgoff "); + else + s += sprintf(s, "chgon "); + + if (s != buf) + /* convert the last space to a newline */ + *(s-1) = '\n'; + + return (s - buf); +} + +static ssize_t +state_onchg_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + char *p; + int len; + + p = memchr(buf, '\n', n); + len = p ? p - buf : n; + + if (len == 5 || len == 6 || len == 7) { + if (!strncmp(buf, "chgon", len)) + request_onchg_state(1); + else if (!strncmp(buf, "chgoff", len)) + request_onchg_state(0); + } + + return 0; +} + +power_attr(state_onchg); +#endif + static struct attribute * g[] = { &state_attr.attr, #ifdef CONFIG_PM_TRACE @@ -332,6 +372,9 @@ static struct attribute * g[] = { &wake_lock_attr.attr, &wake_unlock_attr.attr, #endif +#ifdef CONFIG_HTC_ONMODE_CHARGING + &state_onchg_attr.attr, +#endif #endif NULL, }; diff --git a/kernel/power/power.h b/kernel/power/power.h index 3237ad456e77d..c37fbfbe0b734 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -264,4 +264,8 @@ ssize_t wake_unlock_store(struct kobject *kobj, struct kobj_attribute *attr, /* kernel/power/earlysuspend.c */ void request_suspend_state(suspend_state_t state); suspend_state_t get_suspend_state(void); +#ifdef CONFIG_HTC_ONMODE_CHARGING +void request_onchg_state(int on); +int get_onchg_state(void); +#endif #endif From e309aca455a8aa60a4ebbe4577cc9749043b9c9e Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sat, 30 Apr 2011 16:14:51 -0400 Subject: [PATCH 1442/2556] supersonic: flashlight: move flashlight to its own board file --- .../mach-msm/board-supersonic-flashlight.c | 490 ++++++++++++++++++ .../mach-msm/board-supersonic-flashlight.h | 57 ++ 2 files changed, 547 insertions(+) create mode 100644 arch/arm/mach-msm/board-supersonic-flashlight.c create mode 100644 arch/arm/mach-msm/board-supersonic-flashlight.h diff --git a/arch/arm/mach-msm/board-supersonic-flashlight.c b/arch/arm/mach-msm/board-supersonic-flashlight.c new file mode 100644 index 0000000000000..8264e1d79bb5a --- /dev/null +++ b/arch/arm/mach-msm/board-supersonic-flashlight.c @@ -0,0 +1,490 @@ +/* + * arch/arm/mach-msm/msm_flashlight.c - The flashlight driver + * Copyright (C) 2009 HTC Corporation + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +/*#include */ +#include "board-supersonic-flashlight.h" +#include + +struct flashlight_struct { + struct led_classdev fl_lcdev; + struct early_suspend early_suspend_flashlight; + struct hrtimer timer; + struct wake_lock wake_lock; + spinlock_t spin_lock; + uint32_t gpio_torch; + uint32_t gpio_flash; + uint32_t gpio_flash_adj; + uint32_t flash_sw_timeout_ms; + enum flashlight_mode_flags mode_status; + unsigned long spinlock_flags; + unsigned flash_adj_gpio_status; + /* inactive: 0x0 + * active: 0x1 + * force disable flashlight function: 0x2 */ + uint8_t flash_adj_value; + uint8_t led_count; +}; + +/* disable it, we didn't need to adjust GPIO */ +/* #define FLASHLIGHT_ADJ_FUNC */ + +static struct flashlight_struct *this_fl_str; + +static void flashlight_hw_command(uint8_t addr, uint8_t data) +{ + uint8_t loop_i, loop_j; + const uint8_t fl_addr_to_rising_count[4] = { 17, 18, 19, 20 }; + uint8_t loop_tmp; + if (!this_fl_str->gpio_torch && !this_fl_str->gpio_torch) { + printk(KERN_ERR "%s: not setup GPIO??? torch: %d, flash: %d\n", + __func__, this_fl_str->gpio_torch, + this_fl_str->gpio_flash); + return; + } + for (loop_j = 0; loop_j < 2; loop_j++) { + if (!loop_j) + loop_tmp = fl_addr_to_rising_count[addr]; + else + loop_tmp = data; + for (loop_i = 0; loop_i < loop_tmp; loop_i++) { + gpio_direction_output(this_fl_str->gpio_torch, 0); + udelay(2); + gpio_direction_output(this_fl_str->gpio_torch, 1); + udelay(2); + } + udelay(500); + } +} + +static void flashlight_turn_off(void) +{ + gpio_direction_output(this_fl_str->gpio_flash, 0); + gpio_direction_output(this_fl_str->gpio_torch, 0); + this_fl_str->mode_status = FL_MODE_OFF; + this_fl_str->fl_lcdev.brightness = LED_OFF; + wake_unlock(&this_fl_str->wake_lock); +} + +static enum hrtimer_restart flashlight_hrtimer_func(struct hrtimer *timer) +{ + struct flashlight_struct *fl_str = container_of(timer, + struct flashlight_struct, timer); + wake_unlock(&fl_str->wake_lock); + spin_lock_irqsave(&fl_str->spin_lock, fl_str->spinlock_flags); + flashlight_turn_off(); + spin_unlock_irqrestore(&fl_str->spin_lock, fl_str->spinlock_flags); + printk(KERN_INFO "%s: turn off flash mode\n", __func__); + return HRTIMER_NORESTART; +} + +int flashlight_control(int mode) +{ + int ret = 0; + uint32_t flash_ns = ktime_to_ns(ktime_get()); + +#if 0 /* disable flash_adj_value check now */ + if (this_fl_str->flash_adj_value == 2) { + printk(KERN_WARNING "%s: force disable function!\n", __func__); + return -EIO; + } +#endif + spin_lock_irqsave(&this_fl_str->spin_lock, + this_fl_str->spinlock_flags); + if (this_fl_str->mode_status == FL_MODE_FLASH) { + hrtimer_cancel(&this_fl_str->timer); + wake_unlock(&this_fl_str->wake_lock); + flashlight_turn_off(); + } + switch (mode) { + case FL_MODE_OFF: + flashlight_turn_off(); + break; + case FL_MODE_TORCH: + flashlight_hw_command(3, 1); + flashlight_hw_command(0, 15); + flashlight_hw_command(2, 4); + this_fl_str->mode_status = FL_MODE_TORCH; + this_fl_str->fl_lcdev.brightness = LED_HALF; + break; + case FL_MODE_TORCH_LED_A: + flashlight_hw_command(3, 1); + flashlight_hw_command(0, 15); + flashlight_hw_command(2, 3); + this_fl_str->mode_status = FL_MODE_TORCH_LED_A; + this_fl_str->fl_lcdev.brightness = 1; + break; + case FL_MODE_TORCH_LED_B: + flashlight_hw_command(3, 1); + flashlight_hw_command(0, 15); + flashlight_hw_command(2, 2); + this_fl_str->mode_status = FL_MODE_TORCH_LED_B; + this_fl_str->fl_lcdev.brightness = 2; + break; + case FL_MODE_FLASH: + flashlight_hw_command(2, 4); + gpio_direction_output(this_fl_str->gpio_flash, 1); + this_fl_str->mode_status = FL_MODE_FLASH; + this_fl_str->fl_lcdev.brightness = LED_FULL; + hrtimer_start(&this_fl_str->timer, + ktime_set(this_fl_str->flash_sw_timeout_ms / 1000, + (this_fl_str->flash_sw_timeout_ms % 1000) * + NSEC_PER_MSEC), HRTIMER_MODE_REL); + wake_lock(&this_fl_str->wake_lock); + break; + case FL_MODE_PRE_FLASH: + flashlight_hw_command(3, 1); + flashlight_hw_command(0, 9); + flashlight_hw_command(2, 4); + this_fl_str->mode_status = FL_MODE_PRE_FLASH; + this_fl_str->fl_lcdev.brightness = LED_HALF + 1; + break; + case FL_MODE_TORCH_LEVEL_1: + flashlight_hw_command(3, 8); + flashlight_hw_command(0, 15); + flashlight_hw_command(2, 4); + this_fl_str->mode_status = FL_MODE_TORCH_LEVEL_1; + this_fl_str->fl_lcdev.brightness = LED_HALF - 2; + break; + case FL_MODE_TORCH_LEVEL_2: + flashlight_hw_command(3, 4); + flashlight_hw_command(0, 15); + flashlight_hw_command(2, 4); + this_fl_str->mode_status = FL_MODE_TORCH_LEVEL_2; + this_fl_str->fl_lcdev.brightness = LED_HALF - 1; + break; + case FL_MODE_DEATH_RAY: + pr_info("%s: death ray\n", __func__); + hrtimer_cancel(&this_fl_str->timer); + gpio_direction_output(this_fl_str->gpio_flash, 0); + udelay(40); + gpio_direction_output(this_fl_str->gpio_flash, 1); + this_fl_str->mode_status = 0; + this_fl_str->fl_lcdev.brightness = 3; + wake_lock(&this_fl_str->wake_lock); + break; + default: + printk(KERN_ERR "%s: unknown flash_light flags: %d\n", + __func__, mode); + ret = -EINVAL; + break; + } + + printk(KERN_DEBUG "%s: mode: %d, %u\n", FLASHLIGHT_NAME, mode, + flash_ns/(1000*1000)); + + spin_unlock_irqrestore(&this_fl_str->spin_lock, + this_fl_str->spinlock_flags); + return ret; +} + +static void fl_lcdev_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct flashlight_struct *fl_str; + enum flashlight_mode_flags mode; + + fl_str = container_of(led_cdev, struct flashlight_struct, fl_lcdev); + if (brightness > 0 && brightness <= LED_HALF) { + /* Torch mode */ + if (brightness == (LED_HALF - 2)) + mode = FL_MODE_TORCH_LEVEL_1; + else if (brightness == (LED_HALF - 1)) + mode = FL_MODE_TORCH_LEVEL_2; + else if (brightness == 1 && fl_str->led_count) + mode = FL_MODE_TORCH_LED_A; + else if (brightness == 2 && fl_str->led_count) + mode = FL_MODE_TORCH_LED_B; + else if (brightness == 3) + mode = FL_MODE_DEATH_RAY; + else + mode = FL_MODE_TORCH; + } else if (brightness > LED_HALF && brightness <= LED_FULL) { + /* Flashlight mode */ + if (brightness == (LED_HALF + 1)) + mode = FL_MODE_PRE_FLASH; /* pre-flash mode */ + else + mode = FL_MODE_FLASH; + } else + /* off and else */ + mode = FL_MODE_OFF; + flashlight_control(mode); + + return; +} + +static void flashlight_early_suspend(struct early_suspend *handler) +{ + struct flashlight_struct *fl_str = container_of(handler, + struct flashlight_struct, early_suspend_flashlight); + if (fl_str != NULL && fl_str->mode_status) { + spin_lock_irqsave(&fl_str->spin_lock, fl_str->spinlock_flags); + flashlight_turn_off(); + spin_unlock_irqrestore(&fl_str->spin_lock, + fl_str->spinlock_flags); + } +} + +static void flashlight_late_resume(struct early_suspend *handler) +{ + /* + struct flashlight_struct *fl_str = container_of(handler, + struct flashlight_struct, early_suspend_flashlight); + */ +} + +static int flashlight_setup_gpio(struct flashlight_platform_data *flashlight, + struct flashlight_struct *fl_str) +{ + int ret = 0; + if (flashlight->gpio_init) + flashlight->gpio_init(); + if (flashlight->torch) { + ret = gpio_request(flashlight->torch, "fl_torch"); + if (ret < 0) { + printk(KERN_ERR "%s: gpio_request(torch) failed\n", + __func__); + return ret; + } + fl_str->gpio_torch = flashlight->torch; + } + + if (flashlight->flash) { + ret = gpio_request(flashlight->flash, "fl_flash"); + if (ret < 0) { + printk(KERN_ERR "%s: gpio_request(flash) failed\n", + __func__); + return ret; + } + fl_str->gpio_flash = flashlight->flash; + } + + if (flashlight->flash_adj) { + ret = gpio_request(flashlight->flash_adj, "fl_flash_adj"); + if (ret < 0) { + printk(KERN_ERR "%s: gpio_request(flash_adj) failed\n", + __func__); + return ret; + } + fl_str->gpio_flash_adj = flashlight->flash_adj; + gpio_set_value(fl_str->gpio_flash_adj, 0); + fl_str->flash_adj_gpio_status = 0; + printk(KERN_DEBUG "%s: enable flash_adj function\n", + FLASHLIGHT_NAME); + } + if (flashlight->flash_duration_ms) + fl_str->flash_sw_timeout_ms = flashlight->flash_duration_ms; + else /* load default value */ + fl_str->flash_sw_timeout_ms = 600; + return ret; +} + +static int flashlight_free_gpio(struct flashlight_platform_data *flashlight, + struct flashlight_struct *fl_str) +{ + int ret = 0; + if (fl_str->gpio_torch) { + gpio_free(flashlight->torch); + fl_str->gpio_torch = 0; + } + + if (fl_str->gpio_flash) { + gpio_free(flashlight->flash); + fl_str->gpio_flash = 0; + } + + if (fl_str->gpio_flash_adj) { + gpio_free(flashlight->flash_adj); + fl_str->gpio_flash_adj = 0; + } + + return ret; +} + +#ifdef FLASHLIGHT_ADJ_FUNC +static ssize_t show_flash_adj(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + length = sprintf(buf, "%d\n", this_fl_str->flash_adj_value); + return length; +} + +static ssize_t store_flash_adj(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + static int tmp, adj_tmp; + if ((buf[0] == '0' || buf[0] == '1' || buf[0] == '2') + && buf[1] == '\n') { + spin_lock_irqsave(&this_fl_str->spin_lock, + this_fl_str->spinlock_flags); + tmp = buf[0] - 0x30; + if (tmp == this_fl_str->flash_adj_value) { + spin_unlock_irqrestore(&this_fl_str->spin_lock, + this_fl_str->spinlock_flags); + printk(KERN_NOTICE "%s: status is same(%d)\n", + __func__, this_fl_str->flash_adj_value); + return count; + } + adj_tmp = this_fl_str->gpio_flash_adj; + switch (tmp) { + case 2: + flashlight_turn_off(); + break; + case 1: + /* + if (this_fl_str->flash_adj_gpio_status) { + gpio_set_value(adj_tmp, 0); + this_fl_str->flash_adj_gpio_status = 0; + } + */ + break; + case 0: + /* + if (!this_fl_str->flash_adj_gpio_status) { + gpio_set_value(adj_tmp, 1); + this_fl_str->flash_adj_gpio_status = 1; + } + */ + break; + } + this_fl_str->flash_adj_value = tmp; + spin_unlock_irqrestore(&this_fl_str->spin_lock, + this_fl_str->spinlock_flags); + } + return count; +} + +static DEVICE_ATTR(flash_adj, 0666, show_flash_adj, store_flash_adj); +#endif + +static int flashlight_probe(struct platform_device *pdev) +{ + + struct flashlight_platform_data *flashlight = pdev->dev.platform_data; + struct flashlight_struct *fl_str; + int err = 0; + + fl_str = kzalloc(sizeof(struct flashlight_struct), GFP_KERNEL); + if (!fl_str) { + printk(KERN_ERR "%s: kzalloc fail !!!\n", __func__); + return -ENOMEM; + } + + err = flashlight_setup_gpio(flashlight, fl_str); + if (err < 0) { + printk(KERN_ERR "%s: setup GPIO fail !!!\n", __func__); + goto fail_free_mem; + } + spin_lock_init(&fl_str->spin_lock); + wake_lock_init(&fl_str->wake_lock, WAKE_LOCK_SUSPEND, pdev->name); + fl_str->fl_lcdev.name = pdev->name; + fl_str->fl_lcdev.brightness_set = fl_lcdev_brightness_set; + fl_str->fl_lcdev.brightness = 0; + err = led_classdev_register(&pdev->dev, &fl_str->fl_lcdev); + if (err < 0) { + printk(KERN_ERR "failed on led_classdev_register\n"); + goto fail_free_gpio; + } +#ifdef FLASHLIGHT_ADJ_FUNC + if (fl_str->gpio_flash_adj) { + printk(KERN_DEBUG "%s: flash_adj exist, create attr file\n", + __func__); + err = device_create_file(fl_str->fl_lcdev.dev, + &dev_attr_flash_adj); + if (err != 0) + printk(KERN_WARNING "dev_attr_flash_adj failed\n"); + } +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + fl_str->early_suspend_flashlight.suspend = flashlight_early_suspend; + fl_str->early_suspend_flashlight.resume = flashlight_late_resume; + register_early_suspend(&fl_str->early_suspend_flashlight); +#endif + hrtimer_init(&fl_str->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + fl_str->timer.function = flashlight_hrtimer_func; + fl_str->led_count = flashlight->led_count; + + this_fl_str = fl_str; + printk(KERN_INFO "%s: The Flashlight Driver is ready\n", __func__); + return 0; + +fail_free_gpio: + wake_lock_destroy(&fl_str->wake_lock); + flashlight_free_gpio(flashlight, fl_str); +fail_free_mem: + kfree(fl_str); + printk(KERN_ERR "%s: The Flashlight driver is Failure\n", __func__); + return err; +} + +static int flashlight_remove(struct platform_device *pdev) +{ + struct flashlight_platform_data *flashlight = pdev->dev.platform_data; + + flashlight_turn_off(); + hrtimer_cancel(&this_fl_str->timer); + unregister_early_suspend(&this_fl_str->early_suspend_flashlight); +#ifdef FLASHLIGHT_ADJ_FUNC + if (this_fl_str->gpio_flash_adj) { + device_remove_file(this_fl_str->fl_lcdev.dev, + &dev_attr_flash_adj); + } +#endif + led_classdev_unregister(&this_fl_str->fl_lcdev); + wake_lock_destroy(&this_fl_str->wake_lock); + flashlight_free_gpio(flashlight, this_fl_str); + + kfree(this_fl_str); + return 0; +} + +static struct platform_driver flashlight_driver = { + .probe = flashlight_probe, + .remove = flashlight_remove, + .driver = { + .name = FLASHLIGHT_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init flashlight_init(void) +{ + return platform_driver_register(&flashlight_driver); +} + +static void __exit flashlight_exit(void) +{ + platform_driver_unregister(&flashlight_driver); +} + +module_init(flashlight_init); +module_exit(flashlight_exit); + +MODULE_DESCRIPTION("flash light driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-supersonic-flashlight.h b/arch/arm/mach-msm/board-supersonic-flashlight.h new file mode 100644 index 0000000000000..08efd10298f50 --- /dev/null +++ b/arch/arm/mach-msm/board-supersonic-flashlight.h @@ -0,0 +1,57 @@ +/* + * arch/arm/mach-msm/include/mach/msm_flashlight.h - The flashlight header + * Copyright (C) 2009 HTC Corporation + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ASM_ARCH_MSM8X50_FLASHLIGHT_H +#define __ASM_ARCH_MSM8X50_FLASHLIGHT_H +#include + +#define FLASHLIGHT_NAME "flashlight" + +#define FLASHLIGHT_OFF 0 +#define FLASHLIGHT_TORCH 1 +#define FLASHLIGHT_FLASH 2 +#define FLASHLIGHT_NUM 3 + +enum flashlight_mode_flags { + FL_MODE_OFF = 0, + FL_MODE_TORCH, + FL_MODE_FLASH, + FL_MODE_PRE_FLASH, + FL_MODE_TORCH_LED_A, + FL_MODE_TORCH_LED_B, + FL_MODE_TORCH_LEVEL_1, + FL_MODE_TORCH_LEVEL_2, + FL_MODE_DEATH_RAY, +}; + +struct flashlight_platform_data { + void (*gpio_init) (void); + uint32_t torch; + uint32_t flash; + uint32_t flash_adj; + uint32_t flash_duration_ms; + uint8_t led_count; /* 0: 1 LED, 1: 2 LED */ +}; + +int flashlight_control(int mode); +int aat1271_flashlight_control(int mode); +int adp1650_flashlight_control(int mode); + +#undef __ASM_ARCH_MSM8X50_FLASHLIGHT_H +#endif From eb854564deda3ed06542d015469ef3fbf70e4fc0 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sat, 5 Feb 2011 10:00:20 -0500 Subject: [PATCH 1443/2556] drivers: mmc: import wimax related changes from HTC to mmc driver while preserving the current API. --- arch/arm/mach-msm/dma.c | 11 +- arch/arm/mach-msm/include/mach/dma.h | 1 + arch/arm/mach-msm/include/mach/mmc.h | 2 + drivers/mmc/core/bus.c | 8 + drivers/mmc/core/core.c | 82 +++- drivers/mmc/core/core.h | 1 + drivers/mmc/core/host.c | 1 + drivers/mmc/core/mmc.c | 21 + drivers/mmc/core/sd.c | 18 + drivers/mmc/core/sd_ops.c | 2 + drivers/mmc/core/sdio.c | 33 +- drivers/mmc/host/msm_sdcc.c | 572 +++++++++++++++++++++++++-- drivers/mmc/host/msm_sdcc.h | 8 +- include/linux/mmc/card.h | 3 + include/linux/mmc/host.h | 3 +- include/linux/mmc/sdio.h | 2 + 16 files changed, 715 insertions(+), 53 deletions(-) diff --git a/arch/arm/mach-msm/dma.c b/arch/arm/mach-msm/dma.c index cfb78c407a3ed..d6650f66b086a 100644 --- a/arch/arm/mach-msm/dma.c +++ b/arch/arm/mach-msm/dma.c @@ -56,7 +56,7 @@ void msm_dmov_stop_cmd(unsigned id, struct msm_dmov_cmd *cmd, int graceful) } EXPORT_SYMBOL(msm_dmov_stop_cmd); -void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd) +void msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd) { unsigned long irq_flags; unsigned int status; @@ -93,6 +93,15 @@ void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd) } spin_unlock_irqrestore(&msm_dmov_lock, irq_flags); } +EXPORT_SYMBOL(msm_dmov_enqueue_cmd_ext); + +void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd) +{ + /* Disable callback function (for backwards compatibility) */ + cmd->execute_func = NULL; + + msm_dmov_enqueue_cmd_ext(id, cmd); +} EXPORT_SYMBOL(msm_dmov_enqueue_cmd); void msm_dmov_flush(unsigned int id) diff --git a/arch/arm/mach-msm/include/mach/dma.h b/arch/arm/mach-msm/include/mach/dma.h index 87fb28ff64819..f1ccd428e6056 100644 --- a/arch/arm/mach-msm/include/mach/dma.h +++ b/arch/arm/mach-msm/include/mach/dma.h @@ -36,6 +36,7 @@ struct msm_dmov_cmd { #ifndef CONFIG_ARCH_MSM8X60 void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd); +void msm_dmov_enqueue_cmd_ext(unsigned id, struct msm_dmov_cmd *cmd); void msm_dmov_stop_cmd(unsigned id, struct msm_dmov_cmd *cmd, int graceful); void msm_dmov_flush(unsigned int id); int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr); diff --git a/arch/arm/mach-msm/include/mach/mmc.h b/arch/arm/mach-msm/include/mach/mmc.h index 77515ab374b8b..71768d3d3d5c7 100644 --- a/arch/arm/mach-msm/include/mach/mmc.h +++ b/arch/arm/mach-msm/include/mach/mmc.h @@ -22,6 +22,8 @@ struct msm_mmc_platform_data { unsigned int (*status)(struct device *); struct embedded_sdio_data *embedded_sdio; int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); + unsigned int *slot_type; + unsigned dat0_gpio; }; #endif diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 63667a8f140c4..b81e979f363ca 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -39,6 +39,8 @@ static ssize_t mmc_type_show(struct device *dev, return sprintf(buf, "SDIO\n"); case MMC_TYPE_SD_COMBO: return sprintf(buf, "SDcombo\n"); + case MMC_TYPE_SDIO_WIMAX: + return sprintf(buf, "SDwimax\n"); default: return -EFAULT; } @@ -79,6 +81,9 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) case MMC_TYPE_SD_COMBO: type = "SDcombo"; break; + case MMC_TYPE_SDIO_WIMAX: + type = "SDwimax"; + break; default: type = NULL; } @@ -284,6 +289,9 @@ int mmc_add_card(struct mmc_card *card) type = "SD-combo"; if (mmc_card_blockaddr(card)) type = "SDHC-combo"; + break; + case MMC_TYPE_SDIO_WIMAX: + type = "SD-WiMAX"; default: type = "?"; break; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 63b9c4efd1742..25d0c63324e2e 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -205,6 +205,10 @@ static void mmc_wait_done(struct mmc_request *mrq) complete(mrq->done_data); } +struct msmsdcc_host; +void msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq); +void msmsdcc_stop_data(struct msmsdcc_host *host); + /** * mmc_wait_for_req - start a request and wait for completion * @host: MMC host to start command @@ -216,6 +220,10 @@ static void mmc_wait_done(struct mmc_request *mrq) */ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) { +#ifdef CONFIG_WIMAX + int ret = 0; +#endif + DECLARE_COMPLETION_ONSTACK(complete); mrq->done_data = &complete; @@ -223,7 +231,20 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) mmc_start_request(host, mrq); +#ifdef CONFIG_WIMAX + ret = wait_for_completion_timeout(&complete, msecs_to_jiffies(5000)); + if (ret <= 0) { + struct msmsdcc_host *msm_host = mmc_priv(host); + printk("[ERR] %s: %s wait_for_completion_timeout!\n", __func__, mmc_hostname(host)); + + msmsdcc_stop_data(msm_host); + + mrq->cmd->error = -ETIMEDOUT; + msmsdcc_request_end(msm_host, mrq); + } +#else wait_for_completion(&complete); +#endif } EXPORT_SYMBOL(mmc_wait_for_req); @@ -302,6 +323,7 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card) unsigned int timeout_us, limit_us; timeout_us = data->timeout_ns / 1000; + if (mmc_host_clk_rate(card->host)) timeout_us += data->timeout_clks * 1000 / (mmc_host_clk_rate(card->host) / 1000); @@ -866,6 +888,11 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc, { int result = 0; int min_uV, max_uV; + int enabled; + + enabled = regulator_is_enabled(supply); + if (enabled < 0) + return enabled; if (vdd_bit) { int tmp; @@ -963,7 +990,7 @@ void mmc_set_timing(struct mmc_host *host, unsigned int timing) * If a host does all the power sequencing itself, ignore the * initial MMC_POWER_UP stage. */ -static void mmc_power_up(struct mmc_host *host) +void mmc_power_up(struct mmc_host *host) { int bit; @@ -1003,8 +1030,9 @@ static void mmc_power_up(struct mmc_host *host) */ mmc_delay(10); } +EXPORT_SYMBOL(mmc_power_up); -static void mmc_power_off(struct mmc_host *host) +void mmc_power_off(struct mmc_host *host) { host->ios.clock = 0; host->ios.vdd = 0; @@ -1017,6 +1045,7 @@ static void mmc_power_off(struct mmc_host *host) host->ios.timing = MMC_TIMING_LEGACY; mmc_set_ios(host); } +EXPORT_SYMBOL(mmc_power_off); /* * Cleanup when the last reference to the bus operator is dropped. @@ -1060,9 +1089,10 @@ static inline void mmc_bus_put(struct mmc_host *host) int mmc_resume_bus(struct mmc_host *host) { unsigned long flags; + int err = 0; if (!mmc_bus_needs_resume(host)) - return -EINVAL; + return 0; printk("%s: Starting deferred resume\n", mmc_hostname(host)); spin_lock_irqsave(&host->lock, flags); @@ -1074,15 +1104,20 @@ int mmc_resume_bus(struct mmc_host *host) if (host->bus_ops && !host->bus_dead) { mmc_power_up(host); BUG_ON(!host->bus_ops->resume); - host->bus_ops->resume(host); + err = host->bus_ops->resume(host); + if (err) + goto end; } if (host->bus_ops->detect && !host->bus_dead) host->bus_ops->detect(host); +end: mmc_bus_put(host); - printk("%s: Deferred resume completed\n", mmc_hostname(host)); - return 0; + host->bus_resume_flags &= ~MMC_BUSRESUME_NEEDS_RESUME; + printk(KERN_INFO "%s: Deferred resume %s\n", mmc_hostname(host), + err ? "failed" : "completed"); + return err; } EXPORT_SYMBOL(mmc_resume_bus); @@ -1160,6 +1195,26 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay) EXPORT_SYMBOL(mmc_detect_change); +void mmc_remove_sd_card(struct work_struct *work) +{ + struct mmc_host *host = + container_of(work, struct mmc_host, remove.work); + printk(KERN_INFO "%s: %s\n", mmc_hostname(host), + __func__); + mmc_bus_get(host); + if (host->bus_ops && !host->bus_dead) { + if (host->bus_ops->remove) + host->bus_ops->remove(host); + mmc_claim_host(host); + mmc_detach_bus(host); + mmc_release_host(host); + } + mmc_bus_put(host); + wake_unlock(&mmc_delayed_work_wake_lock); + printk(KERN_INFO "%s: %s exit\n", mmc_hostname(host), + __func__); +} + void mmc_init_erase(struct mmc_card *card) { unsigned int sz; @@ -1745,8 +1800,9 @@ EXPORT_SYMBOL(mmc_card_can_sleep); /** * mmc_suspend_host - suspend a host * @host: mmc host + * @state: suspend mode (PM_SUSPEND_xxx) */ -int mmc_suspend_host(struct mmc_host *host) +int mmc_suspend_host(struct mmc_host *host, pm_message_t state) { int err = 0; @@ -1824,11 +1880,23 @@ int mmc_resume_host(struct mmc_host *host) printk(KERN_WARNING "%s: error %d during resume " "(card was removed?)\n", mmc_hostname(host), err); + if (host->bus_ops->remove) + host->bus_ops->remove(host); + mmc_claim_host(host); + mmc_detach_bus(host); + mmc_release_host(host); + /* no need to bother upper layers */ err = 0; } } mmc_bus_put(host); + /* + * We add a slight delay here so that resume can progress + * in parallel. + */ + mmc_detect_change(host, 1); + return err; } EXPORT_SYMBOL(mmc_resume_host); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index ca1fdde29df6c..e4266edfc5302 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -54,6 +54,7 @@ static inline void mmc_delay(unsigned int ms) } void mmc_rescan(struct work_struct *work); +void mmc_remove_sd_card(struct work_struct *work); void mmc_start_host(struct mmc_host *host); void mmc_stop_host(struct mmc_host *host); diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 1fce4856c5443..571436c95d8fb 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -288,6 +288,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); INIT_DELAYED_WORK(&host->detect, mmc_rescan); + INIT_DELAYED_WORK(&host->remove, mmc_remove_sd_card); INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable); #ifdef CONFIG_PM host->pm_notify.notifier_call = mmc_pm_notify; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 16006ef153fe0..d822ae0232e10 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -721,6 +721,25 @@ static int mmc_awake(struct mmc_host *host) return err; } +#ifdef CONFIG_MMC_UNSAFE_RESUME + +static const struct mmc_bus_ops mmc_ops = { + .awake = mmc_awake, + .sleep = mmc_sleep, + .remove = mmc_remove, + .detect = mmc_detect, + .suspend = mmc_suspend, + .resume = mmc_resume, + .power_restore = mmc_power_restore, +}; + +static void mmc_attach_bus_ops(struct mmc_host *host) +{ + mmc_attach_bus(host, &mmc_ops); +} + +#else + static const struct mmc_bus_ops mmc_ops = { .awake = mmc_awake, .sleep = mmc_sleep, @@ -752,6 +771,8 @@ static void mmc_attach_bus_ops(struct mmc_host *host) mmc_attach_bus(host, bus_ops); } +#endif + /* * Starting point for MMC card init. */ diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 51a9f30c12093..1d19ae12bfbee 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -793,6 +793,23 @@ static int mmc_sd_power_restore(struct mmc_host *host) return ret; } +#ifdef CONFIG_MMC_UNSAFE_RESUME + +static const struct mmc_bus_ops mmc_sd_ops = { + .remove = mmc_sd_remove, + .detect = mmc_sd_detect, + .suspend = mmc_sd_suspend, + .resume = mmc_sd_resume, + .power_restore = mmc_sd_power_restore, +}; + +static void mmc_sd_attach_bus_ops(struct mmc_host *host) +{ + mmc_attach_bus(host, &mmc_sd_ops); +} + +#else + static const struct mmc_bus_ops mmc_sd_ops = { .remove = mmc_sd_remove, .detect = mmc_sd_detect, @@ -820,6 +837,7 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host) mmc_attach_bus(host, bus_ops); } +#endif /* * Starting point for SD card init. */ diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index 797cdb5887fd0..7ca5774c28ab5 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c @@ -79,6 +79,8 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, * we cannot use the retries field in mmc_command. */ for (i = 0;i <= retries;i++) { + memset(&mrq, 0, sizeof(struct mmc_request)); + err = mmc_app_cmd(host, card); if (err) { /* no point in retrying; no APP commands allowed */ diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index ee4b3d2f766a9..882c6075b0d5c 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -154,8 +154,9 @@ static int sdio_enable_wide(struct mmc_card *card) { int ret; u8 ctrl; + unsigned int width = MMC_BUS_WIDTH_4; - if (!(card->host->caps & MMC_CAP_4_BIT_DATA)) + if (!(card->host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) return 0; if (card->cccr.low_speed && !card->cccr.wide_bus) @@ -165,13 +166,23 @@ static int sdio_enable_wide(struct mmc_card *card) if (ret) return ret; + if (card->host->caps & MMC_CAP_8_BIT_DATA) { + width = MMC_BUS_WIDTH_8; + ctrl |= SDIO_BUS_WIDTH_8BIT; + } else { + width = MMC_BUS_WIDTH_4; + ctrl |= SDIO_BUS_WIDTH_4BIT; + } + ctrl |= SDIO_BUS_WIDTH_4BIT; ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL); if (ret) return ret; - return 1; + mmc_set_bus_width(card->host, width); + + return 0; } /* @@ -525,7 +536,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, /* * Switch to wider bus (if supported). */ - err = sdio_enable_4bit_bus(card); + err = sdio_enable_wide(card); if (err > 0) mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); else if (err) @@ -798,14 +809,20 @@ int mmc_attach_sdio(struct mmc_host *host) * The number of functions on the card is encoded inside * the ocr. */ - funcs = (ocr & 0x70000000) >> 28; - card->sdio_funcs = 0; + card->sdio_funcs = funcs = (ocr & 0x70000000) >> 28; #ifdef CONFIG_MMC_EMBEDDED_SDIO if (host->embedded_sdio_data.funcs) card->sdio_funcs = funcs = host->embedded_sdio_data.num_funcs; #endif + /* + * If needed, disconnect card detection pull-up resistor. + */ + err = sdio_disable_cd(card); + if (err) + goto remove; + /* * Initialize (but don't add) all present functions. */ @@ -937,10 +954,8 @@ int sdio_reset_comm(struct mmc_card *card) */ mmc_set_clock(host, mmc_sdio_get_max_clock(card)); - err = sdio_enable_4bit_bus(card); - if (err > 0) - mmc_set_bus_width(host, MMC_BUS_WIDTH_4); - else if (err) + err = sdio_enable_wide(card); + if (err) goto err; mmc_release_host(host); diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index b29e38197da95..048e22c6de5cd 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -40,36 +40,136 @@ #include #include #include +#include #include #include #include #include +#include #include "msm_sdcc.h" #define DRIVER_NAME "msm-sdcc" +#define DBG(host, fmt, args...) \ + pr_debug("%s: %s: " fmt "\n", mmc_hostname(host->mmc), __func__ , args) + +#define IRQ_DEBUG 0 + +#define DISABLE_SVLTE_BUSCLK_PWRSAVE 1 + +#if defined(CONFIG_DEBUG_FS) +static void msmsdcc_dbg_createhost(struct msmsdcc_host *); +static struct dentry *debugfs_dir; +static struct dentry *debugfs_file; +static int msmsdcc_dbg_init(void); +#endif + #define BUSCLK_PWRSAVE 1 #define BUSCLK_TIMEOUT (HZ) +#define SQN_BUSCLK_TIMEOUT (5 * HZ) + static unsigned int msmsdcc_fmin = 144000; -static unsigned int msmsdcc_fmax = 49152000; +static unsigned int msmsdcc_fmax = 50000000; static unsigned int msmsdcc_4bit = 1; static unsigned int msmsdcc_pwrsave = 1; static unsigned int msmsdcc_piopoll = 1; static unsigned int msmsdcc_sdioirq; +static unsigned int msmsdcc_sdioirq = 1; +static unsigned long msmsdcc_irqtime; #define PIO_SPINMAX 30 #define CMD_SPINMAX 20 +#define WRITE_WAIT_DAT0_MAX 10 + +#define VERBOSE_COMMAND_TIMEOUTS 1 +#define SDC_CLK_VERBOSE 1 + +#ifdef CONFIG_WIMAX +extern int mmc_wimax_get_status(void); +extern int mmc_wimax_get_busclk_pwrsave(void); +extern void mmc_wimax_enable_host_wakeup(int on); +#else +static int mmc_wimax_get_status(void) { return 0; } +static int mmc_wimax_get_busclk_pwrsave(void) { return 0; } +#endif + +#if IRQ_DEBUG == 1 +static char *irq_status_bits[] = { "cmdcrcfail", "datcrcfail", "cmdtimeout", + "dattimeout", "txunderrun", "rxoverrun", + "cmdrespend", "cmdsent", "dataend", NULL, + "datablkend", "cmdactive", "txactive", + "rxactive", "txhalfempty", "rxhalffull", + "txfifofull", "rxfifofull", "txfifoempty", + "rxfifoempty", "txdataavlbl", "rxdataavlbl", + "sdiointr", "progdone", "atacmdcompl", + "sdiointrope", "ccstimeout", NULL, NULL, + NULL, NULL, NULL }; + +static void +msmsdcc_print_status(struct msmsdcc_host *host, char *hdr, uint32_t status) +{ + int i; + + printk(KERN_DEBUG "%s-%s ", mmc_hostname(host->mmc), hdr); + for (i = 0; i < 32; i++) { + if (status & (1 << i)) + printk("%s ", irq_status_bits[i]); + } + printk("\n"); +} +#endif + +static int is_sd_platform(struct msm_mmc_platform_data *plat) +{ + if (plat->slot_type && *plat->slot_type == MMC_TYPE_SD) + return 1; + + return 0; +} + +#if BUSCLK_PWRSAVE +static int is_wimax_platform(struct msm_mmc_platform_data *plat) +{ + if (plat->slot_type && *plat->slot_type == MMC_TYPE_SDIO_WIMAX) + return 1; + + return 0; +} static inline void msmsdcc_disable_clocks(struct msmsdcc_host *host, int deferr) { + u32 delay = BUSCLK_TIMEOUT; + + if (is_wimax_platform(host->plat) && mmc_wimax_get_status()) { + if (host->curr.mrq) { + printk("%s [WiMAX] %s curr.mrq != NULL", __func__, mmc_hostname(host->mmc)); + return; + } + + if (!host->clks_on) { + printk("%s [WiMAX] %s clks_on is OFF", __func__, mmc_hostname(host->mmc)); + return; + } + } + WARN_ON(!host->clks_on); + if (host->curr.mrq) + printk("%s call %s()", mmc_hostname(host->mmc), __func__); + BUG_ON(host->curr.mrq); + if (is_wimax_platform(host->plat) && mmc_wimax_get_status()) { + if (!mmc_wimax_get_busclk_pwrsave()) + return; + else + delay = SQN_BUSCLK_TIMEOUT; + } + if (deferr) { mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT); } else { @@ -78,12 +178,52 @@ msmsdcc_disable_clocks(struct msmsdcc_host *host, int deferr) * timer fired */ if (host->clks_on) { +#if SDC_CLK_VERBOSE + if (is_wimax_platform(host->plat)) { +#ifdef CONFIG_WIMAX + mmc_wimax_enable_host_wakeup(1); +#endif + pr_info("%s: Disable clocks\n", mmc_hostname(host->mmc)); + } +#endif clk_disable(host->clk); clk_disable(host->pclk); host->clks_on = 0; } } } +EXPORT_SYMBOL(msmsdcc_disable_clocks); + +int +msmsdcc_get_sdc_clocks(struct msmsdcc_host *host) +{ + if (host->clks_on) + return 1; + else + return 0; +} +EXPORT_SYMBOL(msmsdcc_get_sdc_clocks); + +static void +msmsdcc_busclk_expired(unsigned long _data) +{ + struct msmsdcc_host *host = (struct msmsdcc_host *) _data; + unsigned long flags; + + /* dev_info(mmc_dev(host->mmc), "Bus clock timer expired - S\n"); */ +#if SDC_CLK_VERBOSE + if (is_wimax_platform(host->plat)) { + pr_info("%s: Bus clock timer expired\n", mmc_hostname(host->mmc)); + } +#endif + + spin_lock_irqsave(&host->lock, flags); + if (host->clks_on) + msmsdcc_disable_clocks(host, 0); + spin_unlock_irqrestore(&host->lock, flags); +} + +#endif static inline int msmsdcc_enable_clocks(struct msmsdcc_host *host) @@ -93,6 +233,15 @@ msmsdcc_enable_clocks(struct msmsdcc_host *host) del_timer_sync(&host->busclk_timer); if (!host->clks_on) { +#if SDC_CLK_VERBOSE + if (is_wimax_platform(host->plat)) { + pr_info("%s: Enable clocks\n", mmc_hostname(host->mmc)); + +#ifdef CONFIG_WIMAX + mmc_wimax_enable_host_wakeup(0); +#endif + } +#endif rc = clk_enable(host->pclk); if (rc) return rc; @@ -107,6 +256,19 @@ msmsdcc_enable_clocks(struct msmsdcc_host *host) } return 0; } +EXPORT_SYMBOL(msmsdcc_enable_clocks); + +static char *mmc_type_str(unsigned int slot_type) +{ + switch (slot_type) { + case MMC_TYPE_MMC: return "MMC"; + case MMC_TYPE_SD: return "SD"; + case MMC_TYPE_SDIO: return "SDIO"; + case MMC_TYPE_SD_COMBO: return "SDIO(combo)"; + case MMC_TYPE_SDIO_WIMAX: return "SDIO(WiMAX)"; + default: return "Unknown type"; + } +} static inline unsigned int msmsdcc_readl(struct msmsdcc_host *host, unsigned int reg) @@ -131,7 +293,7 @@ static void msmsdcc_reset_and_restore(struct msmsdcc_host *host) { u32 mci_clk = 0; u32 mci_mask0 = 0; - int ret = 0; + int ret; /* Save the controller state */ mci_clk = readl(host->base + MMCICLOCK); @@ -185,17 +347,30 @@ msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq) mmc_request_done(host->mmc, mrq); spin_lock(&host->lock); } +EXPORT_SYMBOL(msmsdcc_request_end); -static void +void msmsdcc_stop_data(struct msmsdcc_host *host) { host->curr.data = NULL; host->curr.got_dataend = 0; } +EXPORT_SYMBOL(msmsdcc_stop_data); uint32_t msmsdcc_fifo_addr(struct msmsdcc_host *host) { - return host->memres->start + MMCIFIFO; + switch (host->pdev_id) { + case 1: + return MSM_SDC1_PHYS + MMCIFIFO; + case 2: + return MSM_SDC2_PHYS + MMCIFIFO; + case 3: + return MSM_SDC3_PHYS + MMCIFIFO; + case 4: + return MSM_SDC4_PHYS + MMCIFIFO; + } + BUG(); + return 0; } static inline void @@ -543,9 +718,11 @@ msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data, msmsdcc_start_command_deferred(host, cmd, &c); host->cmd_c = c; } - msm_dmov_enqueue_cmd(host->dma.channel, &host->dma.hdr); + dsb(); + msm_dmov_enqueue_cmd_ext(host->dma.channel, &host->dma.hdr); if (data->flags & MMC_DATA_WRITE) host->prog_scan = true; + } else { msmsdcc_writel(host, timeout, MMCIDATATIMER); @@ -564,9 +741,27 @@ msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data, static void msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c) { + struct mmc_data *mdata = cmd->mrq->data; + if (cmd == cmd->mrq->stop) c |= MCI_CSPM_MCIABORT; + if (is_sd_platform(host->plat) && (cmd->opcode == 12) && + (mdata->flags & MMC_DATA_WRITE) && (mdata->blocks > 64)) { + int i; + unsigned dat0 = 67; + + if (host->plat->dat0_gpio) + dat0 = host->plat->dat0_gpio; + + for (i = 0; i < WRITE_WAIT_DAT0_MAX; i++) { + if (gpio_get_value(dat0)) + break; + else + udelay(300); + } + } + host->stats.cmds++; msmsdcc_start_command_deferred(host, cmd, &c); @@ -606,10 +801,16 @@ msmsdcc_pio_read(struct msmsdcc_host *host, char *buffer, unsigned int remain) { uint32_t *ptr = (uint32_t *) buffer; int count = 0; - - if (remain % 4) - remain = ((remain >> 2) + 1) << 2; - +#ifdef CONFIG_WIMAX + unsigned int val = 0; //For 2 bytes data access and consider normal 4 bytes SDIO alignment + + /* For 2 bytes data access and consider normal 4 bytes SDIO alignment */ + if ( (remain < 4) ) { + val = msmsdcc_readl(host, MMCIFIFO); + memcpy(ptr, &val, remain); + count += remain; + }else +#endif while (msmsdcc_readl(host, MMCISTATUS) & MCI_RXDATAAVLBL) { *ptr = msmsdcc_readl(host, MMCIFIFO + (count % MCI_FIFOSIZE)); ptr++; @@ -629,6 +830,22 @@ msmsdcc_pio_write(struct msmsdcc_host *host, char *buffer, void __iomem *base = host->base; char *ptr = buffer; +#ifdef CONFIG_WIMAX + unsigned int val = 0; //For 2 bytes data access and consider normal 4 bytes SDIO alignment + + /* For 2 bytes data access and consider normal 4 bytes SDIO alignment */ + if ( (remain < 4) ) { + memcpy(&val, ptr, remain); + writel(val, base + MMCIFIFO); + // check the data end + do { + status = readl(base + MMCISTATUS); + }while (!(status & MCI_DATABLOCKEND)); + + return remain; + } else { +#endif + do { unsigned int count, maxcnt, sz; @@ -648,6 +865,9 @@ msmsdcc_pio_write(struct msmsdcc_host *host, char *buffer, } while (status & MCI_TXFIFOHALFEMPTY); return ptr - buffer; +#ifdef CONFIG_WIMAX + } +#endif } static int @@ -668,7 +888,46 @@ msmsdcc_pio_irq(int irq, void *dev_id) struct msmsdcc_host *host = dev_id; uint32_t status; + spin_lock(&host->lock); status = msmsdcc_readl(host, MMCISTATUS); +#if IRQ_DEBUG + msmsdcc_print_status(host, "irq1-r", status); +#endif + + /* Workaround when we found sg is NULL (SST) */ + if (host->pio.sg == NULL) { + if (host->pio.sg == NULL) { + printk(KERN_INFO "%s: pio scatter list is null - ", mmc_hostname(host->mmc)); + } + + if (status & MCI_RXACTIVE) { + int read_cnt = 0; + while (msmsdcc_readl(host, MMCISTATUS) & MCI_RXDATAAVLBL) { + msmsdcc_readl(host, MMCIFIFO + (read_cnt % MCI_FIFOSIZE)); + read_cnt += sizeof(uint32_t); + if ((read_cnt) > MCI_FIFOSIZE) + break; + } + msmsdcc_writel(host, MCI_RXDATAAVLBLMASK, MMCIMASK1); + printk("RX\n"); + } + if (status & MCI_TXACTIVE) { + struct mmc_request *mrq; + + msmsdcc_writel(host, 0, MMCIMASK1); + mrq = host->curr.mrq; + if (mrq) { + mrq->data->error = -EIO; + if (mrq->done) + mrq->done(mrq); + host->curr.mrq = NULL; + } + printk("TX\n"); + } + + spin_unlock(&host->lock); + return IRQ_HANDLED; + } do { unsigned long flags; @@ -732,6 +991,7 @@ msmsdcc_pio_irq(int irq, void *dev_id) if (!host->curr.xfer_remain) msmsdcc_writel(host, 0, MMCIMASK1); + spin_unlock(&host->lock); return IRQ_HANDLED; } @@ -834,8 +1094,11 @@ msmsdcc_handle_irq_data(struct msmsdcc_host *host, u32 status, * Check to see if there is still data to be read, * and simulate a PIO irq. */ - if (readl(base + MMCISTATUS) & MCI_RXDATAAVLBL) + if (readl(base + MMCISTATUS) & MCI_RXDATAAVLBL) { + spin_unlock(&host->lock); msmsdcc_pio_irq(1, host); + spin_lock(&host->lock); + } msmsdcc_stop_data(host); if (!data->error) @@ -861,11 +1124,20 @@ msmsdcc_irq(int irq, void *dev_id) do { status = msmsdcc_readl(host, MMCISTATUS); - status &= msmsdcc_readl(host, MMCIMASK0); + +#if IRQ_DEBUG + msmsdcc_print_status(host, "irq0-r", status); +#endif + status &= (msmsdcc_readl(host, MMCIMASK0) | + MCI_DATABLOCKENDMASK); + msmsdcc_writel(host, status, MMCICLEAR); if (status & MCI_SDIOINTR) status &= ~MCI_SDIOINTR; +#if IRQ_DEBUG + msmsdcc_print_status(host, "irq0-p", status); +#endif if (!status) break; @@ -904,7 +1176,7 @@ msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq) host->stats.reqs++; - if (host->eject) { + if (host->eject || (mmc->card && mmc->card->removed)) { if (mrq->data && !(mrq->data->flags & MMC_DATA_READ)) { mrq->cmd->error = 0; mrq->data->bytes_xfered = mrq->data->blksz * @@ -980,11 +1252,13 @@ msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->power_mode) { case MMC_POWER_OFF: + htc_pwrsink_set(PWRSINK_SDCARD, 0); break; case MMC_POWER_UP: pwr |= MCI_PWR_UP; break; case MMC_POWER_ON: + htc_pwrsink_set(PWRSINK_SDCARD, 100); pwr |= MCI_PWR_ON; break; } @@ -1034,6 +1308,8 @@ msmsdcc_check_status(unsigned long data) { struct msmsdcc_host *host = (struct msmsdcc_host *)data; unsigned int status; + unsigned long duration; + int sdcard = is_sd_platform(host->plat); if (!host->plat->status) { mmc_detect_change(host->mmc, 0); @@ -1045,19 +1321,33 @@ msmsdcc_check_status(unsigned long data) if (status ^ host->oldstat) { pr_info("%s: Slot status change detected (%d -> %d)\n", mmc_hostname(host->mmc), host->oldstat, status); - if (status && !host->plat->built_in) - mmc_detect_change(host->mmc, (5 * HZ) / 2); - else - mmc_detect_change(host->mmc, 0); + duration = jiffies - msmsdcc_irqtime; + + if (status) { + if (sdcard) { + if (duration < (7 * HZ)) + duration = (7 * HZ) - duration; + else + duration = 10; + } else + duration = (5 * HZ) / 2; + } else + duration = 0; + + mmc_detect_change(host->mmc, duration); + + if (sdcard) + msmsdcc_irqtime = jiffies; } host->oldstat = status; out: if (host->timer.function) + { mod_timer(&host->timer, jiffies + HZ); + } } - static irqreturn_t msmsdcc_platform_status_irq(int irq, void *dev_id) { @@ -1078,15 +1368,6 @@ msmsdcc_status_notify_cb(int card_present, void *dev_id) msmsdcc_check_status((unsigned long) host); } -static void -msmsdcc_busclk_expired(unsigned long _data) -{ - struct msmsdcc_host *host = (struct msmsdcc_host *) _data; - - if (host->clks_on) - msmsdcc_disable_clocks(host, 0); -} - static int msmsdcc_init_dma(struct msmsdcc_host *host) { @@ -1114,6 +1395,58 @@ msmsdcc_init_dma(struct msmsdcc_host *host) return 0; } +#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ +static void +do_resume_work(struct work_struct *work) +{ + struct msmsdcc_host *host = + container_of(work, struct msmsdcc_host, resume_task); + struct mmc_host *mmc = host->mmc; + + if (mmc) { + mmc_resume_host(mmc); + if (host->stat_irq) + enable_irq(host->stat_irq); + } +} + +#endif + + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void msmsdcc_early_suspend(struct early_suspend *h) +{ + struct msmsdcc_host *host = + container_of(h, struct msmsdcc_host, early_suspend); + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + host->polling_enabled = host->mmc->caps & MMC_CAP_NEEDS_POLL; + host->mmc->caps &= ~MMC_CAP_NEEDS_POLL; + spin_unlock_irqrestore(&host->lock, flags); + + if (is_wimax_platform(host->plat)) { + if (host->clks_on) { + msmsdcc_disable_clocks(host, 0); + } + } +}; +static void msmsdcc_late_resume(struct early_suspend *h) +{ + struct msmsdcc_host *host = + container_of(h, struct msmsdcc_host, early_suspend); + unsigned long flags; + + if (host->polling_enabled) { + spin_lock_irqsave(&host->lock, flags); + host->mmc->caps |= MMC_CAP_NEEDS_POLL; + mmc_detect_change(host->mmc, 0); + spin_unlock_irqrestore(&host->lock, flags); + } + +}; +#endif + static int msmsdcc_probe(struct platform_device *pdev) { @@ -1185,6 +1518,7 @@ msmsdcc_probe(struct platform_device *pdev) host->memres = memres; host->dmares = dmares; spin_lock_init(&host->lock); + #ifdef CONFIG_MMC_EMBEDDED_SDIO if (plat->embedded_sdio) mmc_set_embedded_sdio_data(mmc, @@ -1195,7 +1529,6 @@ msmsdcc_probe(struct platform_device *pdev) #endif tasklet_init(&host->dma_tlet, msmsdcc_dma_complete_tlet, (unsigned long)host); - /* * Setup DMA */ @@ -1297,11 +1630,11 @@ msmsdcc_probe(struct platform_device *pdev) host->oldstat = host->plat->status(mmc_dev(host->mmc)); host->eject = !host->oldstat; } - +#if BUSCLK_PWRSAVE init_timer(&host->busclk_timer); host->busclk_timer.data = (unsigned long) host; host->busclk_timer.function = msmsdcc_busclk_expired; - +#endif ret = request_irq(cmd_irqres->start, msmsdcc_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host); if (ret) @@ -1315,12 +1648,23 @@ msmsdcc_probe(struct platform_device *pdev) mmc_set_drvdata(pdev, mmc); mmc_add_host(mmc); +#ifdef CONFIG_HAS_EARLYSUSPEND + host->early_suspend.suspend = msmsdcc_early_suspend; + host->early_suspend.resume = msmsdcc_late_resume; + host->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; + register_early_suspend(&host->early_suspend); +#endif + pr_info("%s: Qualcomm MSM SDCC at 0x%016llx irq %d,%d dma %d\n", mmc_hostname(mmc), (unsigned long long)memres->start, (unsigned int) cmd_irqres->start, (unsigned int) host->stat_irq, host->dma.channel); + pr_info("%s: Platform slot type: %s\n", mmc_hostname(mmc), + (plat->slot_type) ? mmc_type_str(*plat->slot_type) : "N/A"); pr_info("%s: 4 bit data mode %s\n", mmc_hostname(mmc), (mmc->caps & MMC_CAP_4_BIT_DATA ? "enabled" : "disabled")); + pr_info("%s: 8 bit data mode %s\n", mmc_hostname(mmc), + (mmc->caps & MMC_CAP_8_BIT_DATA ? "enabled" : "disabled")); pr_info("%s: MMC clock %u -> %u Hz, PCLK %u Hz\n", mmc_hostname(mmc), msmsdcc_fmin, msmsdcc_fmax, host->pclk_rate); pr_info("%s: Slot eject status = %d\n", mmc_hostname(mmc), host->eject); @@ -1338,6 +1682,13 @@ msmsdcc_probe(struct platform_device *pdev) if (host->timer.function) pr_info("%s: Polling status mode enabled\n", mmc_hostname(mmc)); +#if defined(CONFIG_DEBUG_FS) + msmsdcc_dbg_createhost(host); +#endif + +#if BUSCLK_PWRSAVE + msmsdcc_disable_clocks(host, 1); +#endif return 0; cmd_irq_free: free_irq(cmd_irqres->start, host); @@ -1345,7 +1696,8 @@ msmsdcc_probe(struct platform_device *pdev) if (host->stat_irq) free_irq(host->stat_irq, host); clk_disable: - msmsdcc_disable_clocks(host, 0); + clk_disable(host->clk); + clk_disable(host->pclk); clk_put: clk_put(host->clk); pclk_put: @@ -1380,6 +1732,10 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state) struct mmc_host *mmc = mmc_get_drvdata(dev); int rc = 0; +#if SDC_CLK_VERBOSE + printk("%s enter\n", __func__); +#endif + if (mmc) { struct msmsdcc_host *host = mmc_priv(mmc); @@ -1390,12 +1746,29 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state) mmc->pm_flags |= MMC_PM_KEEP_POWER; if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) - rc = mmc_suspend_host(mmc); + rc = mmc_suspend_host(mmc, state); if (!rc) msmsdcc_writel(host, 0, MMCIMASK0); - if (host->clks_on) - msmsdcc_disable_clocks(host, 0); +#if BUSCLK_PWRSAVE + del_timer_sync(&host->busclk_timer); +#endif + if (host->clks_on) { +#if SDC_CLK_VERBOSE + if (is_wimax_platform(host->plat)) { + pr_info("%s: Disable clocks in %s\n", mmc_hostname(host->mmc), __func__); + } +#endif + // For suspend case + clk_disable(host->clk); + clk_disable(host->pclk); + host->clks_on = 0; + } } + +#if SDC_CLK_VERBOSE + printk("%s leave\n", __func__); +#endif + return rc; } @@ -1404,6 +1777,10 @@ msmsdcc_resume(struct platform_device *dev) { struct mmc_host *mmc = mmc_get_drvdata(dev); +#if SDC_CLK_VERBOSE + printk("%s enter\n", __func__); +#endif + if (mmc) { struct msmsdcc_host *host = mmc_priv(mmc); @@ -1411,14 +1788,23 @@ msmsdcc_resume(struct platform_device *dev) msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0); - if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) + if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) { +#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ + schedule_work(&host->resume_task); +#else mmc_resume_host(mmc); +#endif + } + if (host->stat_irq) enable_irq(host->stat_irq); #if BUSCLK_PWRSAVE + if (host->clks_on) msmsdcc_disable_clocks(host, 1); #endif } + + return 0; } #else @@ -1443,10 +1829,128 @@ static int __init msmsdcc_init(void) static void __exit msmsdcc_exit(void) { platform_driver_unregister(&msmsdcc_driver); +#if defined(CONFIG_DEBUG_FS) + debugfs_remove(debugfs_file); + debugfs_remove(debugfs_dir); +#endif +} + +#ifndef MODULE +static int __init msmsdcc_pwrsave_setup(char *__unused) +{ + msmsdcc_pwrsave = 1; + return 1; +} + +static int __init msmsdcc_nopwrsave_setup(char *__unused) +{ + msmsdcc_pwrsave = 0; + return 1; +} + + +static int __init msmsdcc_fmin_setup(char *str) +{ + unsigned int n; + + if (!get_option(&str, &n)) + return 0; + msmsdcc_fmin = n; + return 1; +} + +static int __init msmsdcc_fmax_setup(char *str) +{ + unsigned int n; + + if (!get_option(&str, &n)) + return 0; + msmsdcc_fmax = n; + return 1; } +#endif + +__setup("msmsdcc_pwrsave", msmsdcc_pwrsave_setup); +__setup("msmsdcc_nopwrsave", msmsdcc_nopwrsave_setup); +__setup("msmsdcc_fmin=", msmsdcc_fmin_setup); +__setup("msmsdcc_fmax=", msmsdcc_fmax_setup); module_init(msmsdcc_init); module_exit(msmsdcc_exit); MODULE_DESCRIPTION("Qualcomm MSM 7X00A Multimedia Card Interface driver"); MODULE_LICENSE("GPL"); + +#if defined(CONFIG_DEBUG_FS) + +static int +msmsdcc_dbg_state_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t +msmsdcc_dbg_state_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct msmsdcc_host *host = (struct msmsdcc_host *) file->private_data; + char buf[1024]; + int max, i; + + i = 0; + max = sizeof(buf) - 1; + + i += scnprintf(buf + i, max - i, "STAT: %p %p %p\n", host->curr.mrq, + host->curr.cmd, host->curr.data); + if (host->curr.cmd) { + struct mmc_command *cmd = host->curr.cmd; + + i += scnprintf(buf + i, max - i, "CMD : %.8x %.8x %.8x\n", + cmd->opcode, cmd->arg, cmd->flags); + } + if (host->curr.data) { + struct mmc_data *data = host->curr.data; + i += scnprintf(buf + i, max - i, + "DAT0: %.8x %.8x %.8x %.8x %.8x %.8x\n", + data->timeout_ns, data->timeout_clks, + data->blksz, data->blocks, data->error, + data->flags); + i += scnprintf(buf + i, max - i, "DAT1: %.8x %.8x %.8x %p\n", + host->curr.xfer_size, host->curr.xfer_remain, + host->curr.data_xfered, host->dma.sg); + } + + return simple_read_from_buffer(ubuf, count, ppos, buf, i); +} + +static const struct file_operations msmsdcc_dbg_state_ops = { + .read = msmsdcc_dbg_state_read, + .open = msmsdcc_dbg_state_open, +}; + +static void msmsdcc_dbg_createhost(struct msmsdcc_host *host) +{ + if (debugfs_dir) { + debugfs_file = debugfs_create_file(mmc_hostname(host->mmc), + 0644, debugfs_dir, host, + &msmsdcc_dbg_state_ops); + } +} + +static int __init msmsdcc_dbg_init(void) +{ + int err; + + debugfs_dir = debugfs_create_dir("msmsdcc", 0); + if (IS_ERR(debugfs_dir)) { + err = PTR_ERR(debugfs_dir); + debugfs_dir = NULL; + return err; + } + + return 0; +} +device_initcall(msmsdcc_dbg_init); +#endif + diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h index 939557af266d6..de4d95c84c14d 100644 --- a/drivers/mmc/host/msm_sdcc.h +++ b/drivers/mmc/host/msm_sdcc.h @@ -235,8 +235,14 @@ struct msmsdcc_host { struct msmsdcc_pio_data pio; int cmdpoll; struct msmsdcc_stats stats; - +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; + int polling_enabled; +#endif struct tasklet_struct dma_tlet; +#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ + struct work_struct resume_task; +#endif /* Command parameters */ unsigned int cmd_timeout; unsigned int cmd_pio_irqmask; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 48ddeeec301d5..f6e87b401d2e3 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -110,6 +110,8 @@ struct mmc_card { #define MMC_TYPE_SD 1 /* SD card */ #define MMC_TYPE_SDIO 2 /* SDIO card */ #define MMC_TYPE_SD_COMBO 3 /* SD combo (IO+mem) card */ +#define MMC_TYPE_SDIO_WIMAX 4 /* SDIO card of WIMAX */ + unsigned int state; /* (our) card state */ #define MMC_STATE_PRESENT (1<<0) /* present in sysfs */ #define MMC_STATE_READONLY (1<<1) /* card is read-only */ @@ -148,6 +150,7 @@ struct mmc_card { struct sdio_func_tuple *tuples; /* unknown common tuples */ struct dentry *debugfs_root; + unsigned int removed; }; /* diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index fd81f2a26b8a4..00066ed0ba62d 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -223,6 +223,7 @@ struct mmc_host { int claim_cnt; /* "claim" nesting count */ struct delayed_work detect; + struct delayed_work remove; const struct mmc_bus_ops *bus_ops; /* current bus driver */ unsigned int bus_refs; /* reference counter */ @@ -295,7 +296,7 @@ static inline void mmc_set_bus_resume_policy(struct mmc_host *host, int manual) extern int mmc_resume_bus(struct mmc_host *host); -extern int mmc_suspend_host(struct mmc_host *); +extern int mmc_suspend_host(struct mmc_host *, pm_message_t); extern int mmc_resume_host(struct mmc_host *); extern int mmc_power_save_host(struct mmc_host *host); diff --git a/include/linux/mmc/sdio.h b/include/linux/mmc/sdio.h index 245cdacee5443..b544861617632 100644 --- a/include/linux/mmc/sdio.h +++ b/include/linux/mmc/sdio.h @@ -96,6 +96,8 @@ #define SDIO_BUS_WIDTH_1BIT 0x00 #define SDIO_BUS_WIDTH_4BIT 0x02 +#define SDIO_BUS_WIDTH_8BIT 0x03 + #define SDIO_BUS_ECSI 0x20 /* Enable continuous SPI interrupt */ #define SDIO_BUS_SCSI 0x40 /* Support continuous SPI interrupt */ From 69c8b4efdf8f39aa9e532cf22c366b38168d1c0d Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sat, 5 Feb 2011 10:03:13 -0500 Subject: [PATCH 1444/2556] drivers: wimax: import SQN driver. related mmc changes are in previous commit for mmc --- drivers/net/wimax/Makefile | 2 + drivers/net/wimax/SQN/Makefile | 17 +- drivers/net/wimax/SQN/msg.c | 944 ++++++++++++++++++++++++ drivers/net/wimax/SQN/msg.h | 35 +- drivers/net/wimax/SQN/sdio-driver.c | 19 +- drivers/net/wimax/SQN/sdio-fw.c | 61 +- drivers/net/wimax/SQN/sdio-pm.c | 85 ++- drivers/net/wimax/SQN/sdio-sqn.h | 1 + drivers/net/wimax/SQN/sdio.c | 467 +++++++++--- drivers/net/wimax/SQN/sdio.h | 2 + drivers/net/wimax/SQN/sdio_netlink.c | 57 ++ drivers/net/wimax/SQN/sdio_netlink.h | 17 + drivers/net/wimax/SQN/thp.c | 86 +-- drivers/net/wimax/SQN/thp_ioctl.h | 2 + drivers/net/wimax/wimaxdbg/Makefile | 10 + drivers/net/wimax/wimaxdbg/wimaxdbg.c | 192 +++++ drivers/net/wimax/wimaxuart/Makefile | 10 + drivers/net/wimax/wimaxuart/wimaxuart.c | 93 +++ 18 files changed, 1882 insertions(+), 218 deletions(-) create mode 100644 drivers/net/wimax/SQN/msg.c create mode 100644 drivers/net/wimax/SQN/sdio_netlink.c create mode 100644 drivers/net/wimax/SQN/sdio_netlink.h create mode 100644 drivers/net/wimax/wimaxdbg/Makefile create mode 100644 drivers/net/wimax/wimaxdbg/wimaxdbg.c create mode 100644 drivers/net/wimax/wimaxuart/Makefile create mode 100644 drivers/net/wimax/wimaxuart/wimaxuart.c diff --git a/drivers/net/wimax/Makefile b/drivers/net/wimax/Makefile index 64339c6efd709..65c573279170c 100644 --- a/drivers/net/wimax/Makefile +++ b/drivers/net/wimax/Makefile @@ -1,6 +1,8 @@ obj-$(CONFIG_WIMAX_I2400M) += i2400m/ obj-$(CONFIG_WIMAX_SQN) += SQN/ +obj-$(CONFIG_WIMAX_SQN) += wimaxdbg/ +obj-$(CONFIG_WIMAX_SQN) += wimaxuart/ # (from Sam Ravnborg) force kbuild to create built-in.o obj- := dummy.o diff --git a/drivers/net/wimax/SQN/Makefile b/drivers/net/wimax/SQN/Makefile index 86228021566cf..88b82cd23454b 100644 --- a/drivers/net/wimax/SQN/Makefile +++ b/drivers/net/wimax/SQN/Makefile @@ -77,7 +77,7 @@ SQN_USB := obj-$(SQN_SDIO) += sequans_sdio.o obj-$(SQN_USB) += sequans_usb.o -sequans_sdio-objs := sdio.o sdio-driver.o sdio-fw.o sdio-pm.o thp.o +sequans_sdio-objs := sdio.o sdio-driver.o sdio-fw.o sdio-pm.o thp.o sdio_netlink.o msg.o sequans_usb-objs := usb-driver.o thp.o @@ -96,21 +96,6 @@ sequans_usb-objs := usb-driver.o thp.o EXTRA_CFLAGS += -D$(SQN_TARGET) -Wno-unused-function -Wno-unused-label \ -Wno-unused-variable - -### For TI kernel we need the following lines -### For other kernels we should comment them - -#EXTRA_CFLAGS += -DTI_KERNEL -#sequans_sdio-objs += sdio-ti.o - -### end of TI kernel specific options - - -### For ANDROID kernel we need the following lines -### For other kernels we should comment them - EXTRA_CFLAGS += -DANDROID_KERNEL -### end of HTC kernel specific options - endif diff --git a/drivers/net/wimax/SQN/msg.c b/drivers/net/wimax/SQN/msg.c new file mode 100644 index 0000000000000..5ac66fb67c5a5 --- /dev/null +++ b/drivers/net/wimax/SQN/msg.c @@ -0,0 +1,944 @@ +#include "msg.h" + +#define LARGESTRING 1024 + +#define SPERW (7 * 24 * 3600) +#define SPERD (24 * 3600) +#define SPERH (3600) +#define SPERM (60) + +#define SQN_PRT_MODULE_NAME "wimax_prt" + +void printTime32(u_char *data); + +void sqn_pr_info_dump(char *prefix, unsigned char *data, unsigned int len) { + unsigned int i = 0, pos = 0, temp = 0; + unsigned int width = 16; + unsigned int len_ = (unsigned int)(len); + + char buf[LARGESTRING]; + + int opCode = 0; + int bHandle = 0; + + // sequans_xxx: RX PDU: 0000 ff ff ff ff ff ff 00 1e 90 21 0b d4 08 00 45 00 + // while (i < len_) { // Andrew 0903 + if (i < len_) { + if (i % width == 0) + { + if (len_ >= 40 && !bHandle) { // ARP [ + if ( (((unsigned char *)(data))[i+12] == 0x08) && + (((unsigned char *)(data))[i+13] == 0x06) ) { + + bHandle = 1; + + // Opcode + opCode = 0; + if ( (((unsigned char *)(data))[i+20] == 0x00) && + (((unsigned char *)(data))[i+21] == 0x01) ) { + printk(KERN_INFO "%s: [ARP request] - ", SQN_PRT_MODULE_NAME); + opCode = 1; + } + else if ( (((unsigned char *)(data))[i+20] == 0x00) && + (((unsigned char *)(data))[i+21] == 0x02) ) { + printk(KERN_INFO "%s: [ARP reply] - ", SQN_PRT_MODULE_NAME); + opCode = 2; + } + + if (opCode == 1) { // request + printk("Who has "); + for (pos=38; pos<42; pos++) { + if (pos<41) + printk("%d.", ((unsigned char *)(data))[pos]); + else + printk("%d Tell ", ((unsigned char *)(data))[pos]); + } + for (pos=28; pos<32; pos++) { + if (pos<31) + printk("%d.", ((unsigned char *)(data))[pos]); + else + printk("%d, ", ((unsigned char *)(data))[pos]); + } + } + else if (opCode == 2) { // reply + for (pos=28; pos<32; pos++) { + if (pos<31) + printk("%d.", ((unsigned char *)(data))[pos]); + else + printk("%d is at ", ((unsigned char *)(data))[pos]); + } + + for (pos=22; pos<28; pos++) { + if (pos<27) + printk("%02x:", ((unsigned char *)(data))[pos]); + else + printk("%02x, ", ((unsigned char *)(data))[pos]); + } + } + + // Destination MAC + printk("Dst MAC: "); + for (pos=0; pos<6; pos++) { + if (pos<5) + printk("%02x:", ((unsigned char *)(data))[pos]); + else + printk("%02x, ", ((unsigned char *)(data))[pos]); + } + + // Source MAC + printk("Src MAC: "); + for (pos=6; pos<12; pos++) { + if (pos<11) + printk("%02x:", ((unsigned char *)(data))[pos]); + else + printk("%02x\n", ((unsigned char *)(data))[pos]); + } + + } + } // ARP ] + + if (len_ >= 34 && !bHandle) { // ICMP [ + // IP: 0x0800 + if ( (((unsigned char *)(data))[i+12] == 0x08) && + (((unsigned char *)(data))[i+13] == 0x00) && + (((unsigned char *)(data))[i+23] == 0x01) + ) { + bHandle = 1; + if ( (((unsigned char *)(data))[i+34] == 0x00) ) { + printk(KERN_INFO "%s: [ICMP] Echo Reply, ", SQN_PRT_MODULE_NAME); + } + else if ( (((unsigned char *)(data))[i+34] == 0x03) ) { + printk(KERN_INFO "%s: [ICMP] Destination Unreachable, ", SQN_PRT_MODULE_NAME); + } + else if ( (((unsigned char *)(data))[i+34] == 0x05) ) { + printk(KERN_INFO "%s: [ICMP] Redirect, ", SQN_PRT_MODULE_NAME); + } + else if ( (((unsigned char *)(data))[i+34] == 0x08) ) { + printk(KERN_INFO "%s: [ICMP] Echo Request, ", SQN_PRT_MODULE_NAME); + } + else if ( (((unsigned char *)(data))[i+34] == 0x09) ) { + printk(KERN_INFO "%s: [ICMP] Router Adventisement, ", SQN_PRT_MODULE_NAME); + } + + // Source IP + printk("Src IP: "); + for (pos=26; pos<30; pos++) { + if (pos<29) + printk("%d.", ((unsigned char *)(data))[pos]); + else + printk("%d, ", ((unsigned char *)(data))[pos]); + } + + // Destination IP + printk("Dst IP: "); + for (pos=30; pos<34; pos++) { + if (pos<33) + printk("%d.", ((unsigned char *)(data))[pos]); + else + printk("%d\n", ((unsigned char *)(data))[pos]); + } + } + } // ICMP ] + + if (len_ >= 300 && !bHandle) { // DHCP [ + // IP: 0x0800, UDP: 0x11, port: 0x0044, 0x0043 + if ( (((unsigned char *)(data))[i+12] == 0x08) && + (((unsigned char *)(data))[i+13] == 0x00) && + (((unsigned char *)(data))[i+23] == 0x11) && + + ( + (((unsigned char *)(data))[i+34] == 0x00) && + ((((unsigned char *)(data))[i+35] == 0x44) || (((unsigned char *)(data))[i+35] == 0x43)) && + (((unsigned char *)(data))[i+36] == 0x00) && + ((((unsigned char *)(data))[i+37] == 0x43) || (((unsigned char *)(data))[i+37] == 0x44)) + ) + ) { + + bHandle = 1; + pos = 282; + + while (pos < len_ && data[pos] != 255) { // while option [ + + switch ( ((unsigned char *)(data))[pos] ) { // Option case [ + + case 0: // pad + break; + + case 1: // Subnetmask + printk(KERN_INFO "%s: Subnet Mask: ", SQN_PRT_MODULE_NAME); + for (temp=0; temp<4; temp++) { + if (temp<3) + printk("%d.", ((unsigned char *)(data))[pos+2+temp]); + else + printk("%d\n", ((unsigned char *)(data))[pos+2+temp]); + } + break; + + case 51: // IP address leasetime + case 58: // T1 + case 59: // T2 + if (((unsigned char *)(data))[pos] == 51) { + printk(KERN_INFO "%s: IP Address Lease Time: ", SQN_PRT_MODULE_NAME); + printTime32(data + pos + 2); + } + + else if (((unsigned char *)(data))[pos] == 58) { + printk(KERN_INFO "%s: Renew Time Value: ", SQN_PRT_MODULE_NAME); + printTime32(data + pos + 2); + } + else if (((unsigned char *)(data))[pos] == 59) { + // printk(KERN_INFO "%s: Option: (59) Rebinding Time Value", SQN_PRT_MODULE_NAME); + // printTime32(data + pos + 2); + } + // printk(KERN_INFO "%s: Length:%d\n", SQN_PRT_MODULE_NAME, (((unsigned char *)(data))[pos+1]) ); + + break; + + case 54: // Server identifier + /* + printk(KERN_INFO "%s: Server Identifier\n", SQN_PRT_MODULE_NAME); + // printk(KERN_INFO "%s: Length:%d\n", SQN_PRT_MODULE_NAME, (((unsigned char *)(data))[pos+1]) ); + printk(KERN_INFO "%s: Server IP: ", SQN_PRT_MODULE_NAME); + for (temp=0; temp<4; temp++) { + if (temp<3) + printk("%d.", ((unsigned char *)(data))[pos+2+temp]); + else + printk("%d\n", ((unsigned char *)(data))[pos+2+temp]); + } + */ + break; + + case 53: // DHCP message type + if ((((unsigned char *)(data))[pos+2]) == 1) { + printk(KERN_INFO "%s: [DHCP Discover]\n", SQN_PRT_MODULE_NAME); + // Source IP + printk(KERN_INFO "%s: Src IP: ", SQN_PRT_MODULE_NAME); + for (temp=26; temp<30; temp++) { + if (temp<29) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Destination IP + printk(KERN_INFO "%s: Dst IP: ", SQN_PRT_MODULE_NAME); + for (temp=30; temp<34; temp++) { + if (temp<33) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Client MAC + printk(KERN_INFO "%s: Client MAC: ", SQN_PRT_MODULE_NAME); + for (temp=70; temp<76; temp++) { + if (temp<75) + printk("%02x:", ((unsigned char *)(data))[temp]); + else + printk("%02x\n", ((unsigned char *)(data))[temp]); + } + } + else if ((((unsigned char *)(data))[pos+2]) == 2) { + printk(KERN_INFO "%s: [DHCP Offer]\n", SQN_PRT_MODULE_NAME); + + // Source IP + printk(KERN_INFO "%s: Src IP: ", SQN_PRT_MODULE_NAME); + for (temp=26; temp<30; temp++) { + if (temp<29) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Destination IP + printk(KERN_INFO "%s: Dst IP: ", SQN_PRT_MODULE_NAME); + for (temp=30; temp<34; temp++) { + if (temp<33) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Your IP + printk(KERN_INFO "%s: Your IP: ", SQN_PRT_MODULE_NAME); + for (temp=58; temp<62; temp++) { + if (temp<61) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Server IP + printk(KERN_INFO "%s: Server IP: ", SQN_PRT_MODULE_NAME); + for (temp=62; temp<66; temp++) { + if (temp<65) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Client MAC + printk(KERN_INFO "%s: Client MAC: ", SQN_PRT_MODULE_NAME); + for (temp=70; temp<76; temp++) { + if (temp<75) + printk("%02x:", ((unsigned char *)(data))[temp]); + else + printk("%02x\n", ((unsigned char *)(data))[temp]); + } + } + else if ((((unsigned char *)(data))[pos+2]) == 3) { + printk(KERN_INFO "%s: [DHCP Request]\n", SQN_PRT_MODULE_NAME); + // Source IP + printk(KERN_INFO "%s: Src IP: ", SQN_PRT_MODULE_NAME); + for (temp=26; temp<30; temp++) { + if (temp<29) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Destination IP + printk(KERN_INFO "%s: Dst IP: ", SQN_PRT_MODULE_NAME); + for (temp=30; temp<34; temp++) { + if (temp<33) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Client MAC + printk(KERN_INFO "%s: Client MAC: ", SQN_PRT_MODULE_NAME); + for (temp=70; temp<76; temp++) { + if (temp<75) + printk("%02x:", ((unsigned char *)(data))[temp]); + else + printk("%02x\n", ((unsigned char *)(data))[temp]); + } + } + else if ((((unsigned char *)(data))[pos+2]) == 4) { + printk(KERN_INFO "%s: [DHCP Decline]\n", SQN_PRT_MODULE_NAME); + // Source IP + printk(KERN_INFO "%s: Src IP: ", SQN_PRT_MODULE_NAME); + for (temp=26; temp<30; temp++) { + if (temp<29) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Destination IP + printk(KERN_INFO "%s: Dst IP: ", SQN_PRT_MODULE_NAME); + for (temp=30; temp<34; temp++) { + if (temp<33) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Client IP + printk(KERN_INFO "%s: Client IP: ", SQN_PRT_MODULE_NAME); + for (temp=54; temp<58; temp++) { + if (temp<57) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Your IP + printk(KERN_INFO "%s: Your IP: ", SQN_PRT_MODULE_NAME); + for (temp=58; temp<62; temp++) { + if (temp<61) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Server IP + printk(KERN_INFO "%s: Server IP: ", SQN_PRT_MODULE_NAME); + for (temp=62; temp<66; temp++) { + if (temp<65) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Client MAC + printk(KERN_INFO "%s: Client MAC: ", SQN_PRT_MODULE_NAME); + for (temp=70; temp<76; temp++) { + if (temp<75) + printk("%02x:", ((unsigned char *)(data))[temp]); + else + printk("%02x\n", ((unsigned char *)(data))[temp]); + } + } + else if ((((unsigned char *)(data))[pos+2]) == 5) { + printk(KERN_INFO "%s: [DHCP Ack]\n", SQN_PRT_MODULE_NAME); + // Source IP + printk(KERN_INFO "%s: Src IP: ", SQN_PRT_MODULE_NAME); + for (temp=26; temp<30; temp++) { + if (temp<29) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Destination IP + printk(KERN_INFO "%s: Dst IP: ", SQN_PRT_MODULE_NAME); + for (temp=30; temp<34; temp++) { + if (temp<33) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Client IP + printk(KERN_INFO "%s: Client IP: ", SQN_PRT_MODULE_NAME); + for (temp=54; temp<58; temp++) { + if (temp<57) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Your IP + printk(KERN_INFO "%s: Your IP: ", SQN_PRT_MODULE_NAME); + for (temp=58; temp<62; temp++) { + if (temp<61) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Server IP + printk(KERN_INFO "%s: Server IP: ", SQN_PRT_MODULE_NAME); + for (temp=62; temp<66; temp++) { + if (temp<65) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Client MAC + printk(KERN_INFO "%s: Client MAC: ", SQN_PRT_MODULE_NAME); + for (temp=70; temp<76; temp++) { + if (temp<75) + printk("%02x:", ((unsigned char *)(data))[temp]); + else + printk("%02x\n", ((unsigned char *)(data))[temp]); + } + } + else if ((((unsigned char *)(data))[pos+2]) == 6) { + printk(KERN_INFO "%s: [DHCP Nack]\n", SQN_PRT_MODULE_NAME); + // Source IP + printk(KERN_INFO "%s: Src IP: ", SQN_PRT_MODULE_NAME); + for (temp=26; temp<30; temp++) { + if (temp<29) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Destination IP + printk(KERN_INFO "%s: Dst IP: ", SQN_PRT_MODULE_NAME); + for (temp=30; temp<34; temp++) { + if (temp<33) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Client IP + printk(KERN_INFO "%s: Client IP: ", SQN_PRT_MODULE_NAME); + for (temp=54; temp<58; temp++) { + if (temp<57) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Your IP + printk(KERN_INFO "%s: Your IP: ", SQN_PRT_MODULE_NAME); + for (temp=58; temp<62; temp++) { + if (temp<61) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Server IP + printk(KERN_INFO "%s: Server IP: ", SQN_PRT_MODULE_NAME); + for (temp=62; temp<66; temp++) { + if (temp<65) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Client MAC + printk(KERN_INFO "%s: Client MAC: ", SQN_PRT_MODULE_NAME); + for (temp=70; temp<76; temp++) { + if (temp<75) + printk("%02x:", ((unsigned char *)(data))[temp]); + else + printk("%02x\n", ((unsigned char *)(data))[temp]); + } + } + else if ((((unsigned char *)(data))[pos+2]) == 7) { + printk(KERN_INFO "%s: [DHCP Release]\n", SQN_PRT_MODULE_NAME); + // Source IP + printk(KERN_INFO "%s: Src IP: ", SQN_PRT_MODULE_NAME); + for (temp=26; temp<30; temp++) { + if (temp<29) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Destination IP + printk(KERN_INFO "%s: Dst IP: ", SQN_PRT_MODULE_NAME); + for (temp=30; temp<34; temp++) { + if (temp<33) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Client IP + printk(KERN_INFO "%s: Client IP: ", SQN_PRT_MODULE_NAME); + for (temp=54; temp<58; temp++) { + if (temp<57) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Your IP + printk(KERN_INFO "%s: Your IP: ", SQN_PRT_MODULE_NAME); + for (temp=58; temp<62; temp++) { + if (temp<61) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Server IP + printk(KERN_INFO "%s: Server IP: ", SQN_PRT_MODULE_NAME); + for (temp=62; temp<66; temp++) { + if (temp<65) + printk("%d.", ((unsigned char *)(data))[temp]); + else + printk("%d\n", ((unsigned char *)(data))[temp]); + } + + // Client MAC + printk(KERN_INFO "%s: Client MAC: ", SQN_PRT_MODULE_NAME); + for (temp=70; temp<76; temp++) { + if (temp<75) + printk("%02x:", ((unsigned char *)(data))[temp]); + else + printk("%02x\n", ((unsigned char *)(data))[temp]); + } + } + else { + printk(KERN_INFO "%s: Type: Unknown\n", SQN_PRT_MODULE_NAME); + } + break; + + case 61: // Client identifier + printk(KERN_INFO "%s: Client identifier\n", SQN_PRT_MODULE_NAME); + printk(KERN_INFO "%s: Client MAC: ", SQN_PRT_MODULE_NAME); + for (temp=0; temp<6; temp++) { + if (temp<5) + printk("%02x:", ((unsigned char *)(data))[pos+3+temp]); + else + printk("%02x\n", ((unsigned char *)(data))[pos+3+temp]); + } + break; + + default: + break; + + } // Option case ] + + // This might go wrong if a mallformed packet is received. + // Maybe from a bogus server which is instructed to reply + // with invalid data and thus causing an exploit. + // My head hurts... but I think it's solved by the checking + // for pos= 34 && !bHandle) { // HTTP [ + // IP: 0x0800, TCP: 0x06, port: 0x0050 (80) + if ( (((unsigned char *)(data))[i+12] == 0x08) && + (((unsigned char *)(data))[i+13] == 0x00) && + (((unsigned char *)(data))[i+23] == 0x06) && + ( + ((((unsigned char *)(data))[i+34] == 0x00) && (((unsigned char *)(data))[i+35] == 0x50)) || + ((((unsigned char *)(data))[i+36] == 0x00) && (((unsigned char *)(data))[i+37] == 0x50)) + ) + ) { + bHandle = 1; + printk(KERN_INFO "%s: [HTTP] request, ", SQN_PRT_MODULE_NAME); + + // Source IP + printk("Src IP: "); + for (pos=26; pos<30; pos++) { + if (pos<29) + printk("%d.", ((unsigned char *)(data))[pos]); + else + printk("%d, ", ((unsigned char *)(data))[pos]); + } + + // Destination IP + printk("Dst IP: "); + for (pos=30; pos<34; pos++) { + if (pos<33) + printk("%d.", ((unsigned char *)(data))[pos]); + else + printk("%d\n", ((unsigned char *)(data))[pos]); + } + } + } // HTTP ] + + if (len_ >= 34 && !bHandle) { // DNS [ + // IP: 0x0800, UDP: 0x11, port: 0x0035 + if ( (((unsigned char *)(data))[i+12] == 0x08) && + (((unsigned char *)(data))[i+13] == 0x00) && + (((unsigned char *)(data))[i+23] == 0x11) && + ( + ((((unsigned char *)(data))[i+34] == 0x00) && (((unsigned char *)(data))[i+35] == 0x35)) || + ((((unsigned char *)(data))[i+36] == 0x00) && (((unsigned char *)(data))[i+37] == 0x35)) + ) + ) { + bHandle = 1; + printk(KERN_INFO "%s: [DNS] query, ", SQN_PRT_MODULE_NAME); + + // Source IP + printk("Src IP: "); + for (pos=26; pos<30; pos++) { + if (pos<29) + printk("%d.", ((unsigned char *)(data))[pos]); + else + printk("%d, ", ((unsigned char *)(data))[pos]); + } + + // Destination IP + printk("Dst IP: "); + for (pos=30; pos<34; pos++) { + if (pos<33) + printk("%d.", ((unsigned char *)(data))[pos]); + else + printk("%d\n", ((unsigned char *)(data))[pos]); + } + } + } // DNS ] + + else if (len_ >= 34 && !bHandle) { // NTP [ + // IP: 0x0800, UDP: 0x11, port: 0x007b + if ( (((unsigned char *)(data))[i+12] == 0x08) && + (((unsigned char *)(data))[i+13] == 0x00) && + (((unsigned char *)(data))[i+23] == 0x11) && + (((unsigned char *)(data))[i+34] == 0x00) && + (((unsigned char *)(data))[i+35] == 0x7b) + ) { + bHandle = 1; + printk(KERN_INFO "%s: [NTP] Sync active, ", SQN_PRT_MODULE_NAME); + + // Source IP + printk("Src IP: "); + for (pos=26; pos<30; pos++) { + if (pos<29) + printk("%d.", ((unsigned char *)(data))[pos]); + else + printk("%d, ", ((unsigned char *)(data))[pos]); + } + + // Destination IP + printk("Dst IP: "); + for (pos=30; pos<34; pos++) { + if (pos<33) + printk("%d.", ((unsigned char *)(data))[pos]); + else + printk("%d\n", ((unsigned char *)(data))[pos]); + } + } + } // NTP ] + + if (len_ >= 12 && !bHandle) { // IPv6 [ + // IPv6: 0x86DD, UDP: 0x11 + if ( (((unsigned char *)(data))[i+12] == 0x86) && + (((unsigned char *)(data))[i+13] == 0xDD) + ) { + bHandle = 1; + printk(KERN_INFO "%s: [IPv6] Network packets, ", SQN_PRT_MODULE_NAME); + + // Source IP + printk("Dst MAC: "); + for (pos=0; pos<6; pos++) { + if (pos<5) + printk("%d:", ((unsigned char *)(data))[pos]); + else + printk("%d, ", ((unsigned char *)(data))[pos]); + } + + // Destination IP + printk("Src MAC: "); + for (pos=6; pos<12; pos++) { + if (pos<11) + printk("%d:", ((unsigned char *)(data))[pos]); + else + printk("%d\n", ((unsigned char *)(data))[pos]); + } + } + } // IPv6 ] + + if (len_ >= 34 && !bHandle) { // Unknown UDP [ + // IP: 0x0800, UDP: 0x11 + if ( (((unsigned char *)(data))[i+12] == 0x08) && + (((unsigned char *)(data))[i+13] == 0x00) && + (((unsigned char *)(data))[i+23] == 0x11) + ) { + bHandle = 1; + printk(KERN_INFO "%s: [UDP] Network packets, ", SQN_PRT_MODULE_NAME); + + // Source IP + printk("Src IP: "); + for (pos=26; pos<30; pos++) { + if (pos<29) + printk("%d.", ((unsigned char *)(data))[pos]); + else + printk("%d, ", ((unsigned char *)(data))[pos]); + } + + // Destination IP + printk("Dst IP: "); + for (pos=30; pos<34; pos++) { + if (pos<33) + printk("%d.", ((unsigned char *)(data))[pos]); + else + printk("%d, ", ((unsigned char *)(data))[pos]); + } + + printk("Port: %d%d\n", ((unsigned char *)(data))[i+34], ((unsigned char *)(data))[i+35]); + } + } // Unknown ] + + if (len_ >= 34 && !bHandle) { // Unknown TCP [ + // IP: 0x0800, TCP: 0x06 + if ( (((unsigned char *)(data))[i+12] == 0x08) && + (((unsigned char *)(data))[i+13] == 0x00) && + (((unsigned char *)(data))[i+23] == 0x06) + ) { + bHandle = 1; + printk(KERN_INFO "%s: [TCP] Network packets, ", SQN_PRT_MODULE_NAME); + + // Source IP + printk("Src IP: "); + for (pos=26; pos<30; pos++) { + if (pos<29) + printk("%d.", ((unsigned char *)(data))[pos]); + else + printk("%d, ", ((unsigned char *)(data))[pos]); + } + + // Destination IP + printk("Dst IP: "); + for (pos=30; pos<34; pos++) { + if (pos<33) + printk("%d.", ((unsigned char *)(data))[pos]); + else + printk("%d, ", ((unsigned char *)(data))[pos]); + } + + printk("Port: %d%d\n", ((unsigned char *)(data))[i+34], ((unsigned char *)(data))[i+35]); + } + } // Unknown ] + + // Andrew 0903 + // printk(KERN_INFO "%s: %s: %04x ", SQN_PRT_MODULE_NAME, (prefix), i); + } // if (i % width == 0) + + // Andrew 0903 + // printk("%02x ", ((unsigned char *)(data))[i++]); + if ((i % width == 0) || (i == len_)) + printk("\n"); + } +} + + +#define MAX_DUMP_LEN 48 + +void sqn_pr_info_dump_rawdata(char *prefix, unsigned char *data, unsigned int len) { + unsigned int i = 0; + unsigned int width = 16; + unsigned int len_ = (unsigned int)(len); + + if (len_ > MAX_DUMP_LEN) { + len_ = MAX_DUMP_LEN; + } + + // sequans_xxx: RX PDU: 0000 ff ff ff ff ff ff 00 1e 90 21 0b d4 08 00 45 00 + while (i < len_) { + if (i % width == 0) + printk(KERN_INFO "%s: %s: %04x ", SQN_PRT_MODULE_NAME, (prefix), i); + + printk("%02x ", ((unsigned char *)(data))[i++]); + + if ((i % width == 0) || (i == len_)) + printk("\n"); + } +} + +int sqn_filter_packet_check(char *prefix, unsigned char *data, unsigned int len) { + unsigned int i = 0, pos = 0, temp = 0; + unsigned int width = 16; + unsigned int len_ = (unsigned int)(len); + + char buf[LARGESTRING]; + + int bHandle = 0, bFilter = 0; + + if (i < len_) { + // Unblocked list: + if (len_ >= 40 && !bHandle) { // ARP [ + if ( (((unsigned char *)(data))[i+12] == 0x08) && + (((unsigned char *)(data))[i+13] == 0x06) ) { + bHandle = 1; + bFilter = 0; + } + } // ARP ] + + if (len_ >= 34 && !bHandle) { // ICMP [ + // IP: 0x0800 + if ( (((unsigned char *)(data))[i+12] == 0x08) && + (((unsigned char *)(data))[i+13] == 0x00) && + (((unsigned char *)(data))[i+23] == 0x01) + ) { + bHandle = 1; + bFilter = 0; + } + } // ICMP ] + + if (len_ >= 300 && !bHandle) { // DHCP [ + // IP: 0x0800, UDP: 0x11, port: 0x0044, 0x0043 + if ( (((unsigned char *)(data))[i+12] == 0x08) && + (((unsigned char *)(data))[i+13] == 0x00) && + (((unsigned char *)(data))[i+23] == 0x11) && + + ( + (((unsigned char *)(data))[i+34] == 0x00) && + ((((unsigned char *)(data))[i+35] == 0x44) || (((unsigned char *)(data))[i+35] == 0x43)) && + (((unsigned char *)(data))[i+36] == 0x00) && + ((((unsigned char *)(data))[i+37] == 0x43) || (((unsigned char *)(data))[i+37] == 0x44)) + ) + ) { + + bHandle = 1; + bFilter = 0; + } + } // DHCP ] + + if (len_ >= 34 && !bHandle) { // HTTP [ + // IP: 0x0800, TCP: 0x06, port: 0x0050 (80) + if ( (((unsigned char *)(data))[i+12] == 0x08) && + (((unsigned char *)(data))[i+13] == 0x00) && + (((unsigned char *)(data))[i+23] == 0x06) && + ( + ((((unsigned char *)(data))[i+34] == 0x00) && (((unsigned char *)(data))[i+35] == 0x50)) || + ((((unsigned char *)(data))[i+36] == 0x00) && (((unsigned char *)(data))[i+37] == 0x50)) + ) + ) { + sqn_pr_info("Drop HTTP packets len:%d\n", len_); + bHandle = 1; + bFilter = 1; + } + } // HTTP ] + + if (len_ >= 34 && !bHandle) { // DNS [ + // IP: 0x0800, UDP: 0x11, port: 0x0035 + if ( (((unsigned char *)(data))[i+12] == 0x08) && + (((unsigned char *)(data))[i+13] == 0x00) && + (((unsigned char *)(data))[i+23] == 0x11) && + ( + ((((unsigned char *)(data))[i+34] == 0x00) && (((unsigned char *)(data))[i+35] == 0x35)) || + ((((unsigned char *)(data))[i+36] == 0x00) && (((unsigned char *)(data))[i+37] == 0x35)) + ) + ) { + bHandle = 1; + bFilter = 0; + } + } // DNS ] + + else if (len_ >= 34 && !bHandle) { // NTP [ + // IP: 0x0800, UDP: 0x11, port: 0x007b + if ( (((unsigned char *)(data))[i+12] == 0x08) && + (((unsigned char *)(data))[i+13] == 0x00) && + (((unsigned char *)(data))[i+23] == 0x11) && + (((unsigned char *)(data))[i+34] == 0x00) && + (((unsigned char *)(data))[i+35] == 0x7b) + ) { + sqn_pr_info("Drop NTP packets len:%d\n", len_); + bHandle = 1; + bFilter = 1; + } + } // NTP ] + + // Block list: + if (len_ >= 12 && !bHandle) { // IPv6 [ + // IPv6: 0x86DD, UDP: 0x11 + if ( (((unsigned char *)(data))[i+12] == 0x86) && + (((unsigned char *)(data))[i+13] == 0xDD) + ) { + sqn_pr_info("Drop IPv6 packets len:%d\n", len_); + bHandle = 1; + bFilter = 1; + } + } // IPv6 ] + + if (len_ >= 34 && !bHandle) { // Unknown UDP [ + // IP: 0x0800, UDP: 0x11 + if ( (((unsigned char *)(data))[i+12] == 0x08) && + (((unsigned char *)(data))[i+13] == 0x00) && + (((unsigned char *)(data))[i+23] == 0x11) + ) { + sqn_pr_info("Drop UDP packets len:%d\n", len_); + bHandle = 1; + bFilter = 1; + } + } // Unknown UDP ] + + if (len_ >= 34 && !bHandle) { // Unknown TCP [ + // IP: 0x0800, TCP: 0x06 + if ( (((unsigned char *)(data))[i+12] == 0x08) && + (((unsigned char *)(data))[i+13] == 0x00) && + (((unsigned char *)(data))[i+23] == 0x06) + ) { + sqn_pr_info("Drop TCP packets len:%d\n", len_); + bHandle = 1; + bFilter = 1; + } + } // Unknown TCP ] + } + + return bFilter; +} + +// print the data as a 32bits time-value +void printTime32(u_char *data) { + int t = (data[0] << 24) + (data[1] << 16) + (data[2] <<8 ) + data[3]; + printk("%d (", t); + if (t > SPERW) { printk("%dw", t / (SPERW)); t %= SPERW; } + if (t > SPERD) { printk("%dd", t / (SPERD)); t %= SPERD; } + if (t > SPERH) { printk("%dh", t / (SPERH)); t %= SPERH; } + if (t > SPERM) { printk("%dm", t / (SPERM)); t %= SPERM; } + if (t > 0) printk("%ds", t); + printk(")"); +} diff --git a/drivers/net/wimax/SQN/msg.h b/drivers/net/wimax/SQN/msg.h index 8f9331487554a..10c3d0946bf46 100644 --- a/drivers/net/wimax/SQN/msg.h +++ b/drivers/net/wimax/SQN/msg.h @@ -19,7 +19,6 @@ #include "version.h" - #define sqn_pr(level, fmt, arg...) \ do { \ char kthread_name[TASK_COMM_LEN] = { 0 }; \ @@ -34,18 +33,10 @@ do { \ } while (0) -#if defined(DEBUG) - -#ifdef SQN_DEBUG_LEVEL_INFO -#define DEBUG_LEVEL KERN_INFO -#else -#define DEBUG_LEVEL KERN_DEBUG -#endif - -#define sqn_pr_dbg(fmt, arg...) sqn_pr(DEBUG_LEVEL, fmt, ##arg) +#define SQN_DEBUG_DUMP 1 #ifdef SQN_DEBUG_DUMP - +#define DEBUG_LEVEL KERN_INFO #define sqn_pr_dbg_dump(prefix, data, len) \ do { \ unsigned int i = 0; \ @@ -60,13 +51,22 @@ do { \ printk("\n"); \ } \ } while (0) - #else /* !SQN_DEBUG_DUMP */ #define sqn_pr_dbg_dump(prefix, data, len) do {} while (0) - #endif /* SQN_DEBUG_DUMP */ + +#if defined(DEBUG) + +#ifdef SQN_DEBUG_LEVEL_INFO +#define DEBUG_LEVEL KERN_INFO +#else +#define DEBUG_LEVEL KERN_DEBUG +#endif + +#define sqn_pr_dbg(fmt, arg...) sqn_pr(DEBUG_LEVEL, fmt, ##arg) + #ifdef SQN_DEBUG_TRACE #define sqn_pr_enter() sqn_pr_dbg("%s\n", "enter") @@ -82,7 +82,6 @@ do { \ #else /* !DEBUG */ #define sqn_pr_dbg(fmt, arg...) do {} while (0) -#define sqn_pr_dbg_dump(prefix, data, len) do {} while (0) #define sqn_pr_enter() do {} while (0) #define sqn_pr_leave() do {} while (0) @@ -100,11 +99,17 @@ do { \ pr_err("%s: " fmt, SQN_MODULE_NAME, ##arg) +void sqn_pr_info_dump(char *prefix, unsigned char *data, unsigned int len); +void sqn_pr_info_dump_rawdata(char *prefix, unsigned char *data, unsigned int len); +int sqn_filter_packet_check(char *prefix, unsigned char *data, unsigned int len); + +/* #define sqn_pr_info_dump(prefix, data, len) \ do { \ unsigned int i = 0; \ unsigned int width = 16; \ unsigned int len_ = (unsigned int)(len); \ + sqn_pr_info_trace(prefix, data, len); \ while (i < len_) { \ if (i % width == 0) \ printk(KERN_INFO "%s: %s: %04x ", \ @@ -114,6 +119,6 @@ do { \ printk("\n"); \ } \ } while (0) - +*/ #endif /* _SQN_MSG_H */ diff --git a/drivers/net/wimax/SQN/sdio-driver.c b/drivers/net/wimax/SQN/sdio-driver.c index 559d365dc3bdb..158eada700bff 100644 --- a/drivers/net/wimax/SQN/sdio-driver.c +++ b/drivers/net/wimax/SQN/sdio-driver.c @@ -30,6 +30,7 @@ #define DRIVER_DEBUG 0 #define SKB_DEBUG 0 #define IGNORE_CARRIER_STATE 1 +#define SDIO_CLAIM_HOST_DEBUG 0 /*******************************************************************/ /* Module parameter variables */ @@ -132,8 +133,11 @@ int sqn_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) } } - if (priv->removed) - goto out; + if (priv->removed) { + spin_unlock_irqrestore(&priv->drv_lock, irq_flags); + dev_kfree_skb_any(skb); + return NETDEV_TX_LOCKED; + } if (skb->len < 1 || (skb->len > SQN_MAX_PDU_LEN)) { sqn_pr_dbg("skb length %d not in range (1, %d)\n", skb->len, @@ -229,7 +233,7 @@ static int sqn_tx_thread(void *data) if (priv->removed) { sqn_pr_dbg("adapter removed; wait to die...\n"); spin_unlock_irqrestore(&priv->drv_lock, irq_flags); - mdelay(1); + ssleep(1); continue; } spin_unlock_irqrestore(&priv->drv_lock, irq_flags); @@ -288,6 +292,10 @@ int sqn_rx_process(struct net_device *dev, struct sk_buff *skb) int rc = 0; struct sqn_private *priv = netdev_priv(dev); +#if SDIO_CLAIM_HOST_DEBUG + /* sqn_pr_info("%s+\n", __func__); */ +#endif + #if DRIVER_DEBUG printk(KERN_WARNING "sqn_rx_process \n"); #endif @@ -306,6 +314,11 @@ int sqn_rx_process(struct net_device *dev, struct sk_buff *skb) /* netif_receive_skb(skb); */ sqn_pr_leave(); + +#if SDIO_CLAIM_HOST_DEBUG + /* sqn_pr_info("%s-\n", __func__); */ +#endif + return rc; } diff --git a/drivers/net/wimax/SQN/sdio-fw.c b/drivers/net/wimax/SQN/sdio-fw.c index 062e8dc922790..a0fc256846106 100644 --- a/drivers/net/wimax/SQN/sdio-fw.c +++ b/drivers/net/wimax/SQN/sdio-fw.c @@ -119,43 +119,58 @@ static int is_good_ahb_address(u32 address, enum sqn_card_version card_version) * this give up and try to alloc 4KB buffer if requested size bigger than * 4KB, otherwise allocate nothing and return 0. * - * @return a real size of allocated buffer or 0 if allocation failed + * @return a real size of allocated buffer or 0 if allocation failed + * + * Normal: 3912*4kB 4833*8kB 0*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 54312kB */ static size_t sqn_alloc_big_buffer(u8 **buf, size_t size, gfp_t gfp_flags) { size_t real_size = size; - int retries = 6; + // int retries = 6; + // int retries = 3; sqn_pr_enter(); /* Try to allocate buffer of requested size, if it failes try to * allocate a twice smaller buffer. Repeat this number of * times. */ + /* do { *buf = kmalloc(real_size, gfp_flags); + printk("%s: kmalloc %d in %x trial:%d\n", __func__, real_size, *buf, retries); + if (!(*buf)) { - real_size /= 2; - /* adjust the size to be a multiple of 4 */ + printk("%s: kmalloc %d failed, trial:%d\n", __func__, real_size, retries); + // real_size /= 2; + real_size /= 4; + // adjust the size to be a multiple of 4 real_size += real_size % 4 ? 4 - real_size % 4 : 0; } } while (retries-- > 0 && !(*buf)); + */ - /* If all retries failed, then allocate 4KB buffer */ + // If all retries failed, then allocate 4KB buffer if (!(*buf)) { - real_size = 4 * 1024; + real_size = 8 * 1024; if (size >= real_size) { *buf = kmalloc(real_size, gfp_flags); - /* If it also failed, then just return 0, indicating - * that we failed to alloc buffer */ + // printk("%s: kmalloc %d in %x\n", __func__, real_size, *buf); + + // If it also failed, then just return 0, indicating + // that we failed to alloc buffer if (!(*buf)) real_size = 0; } else { - /* We should _not_ return buffer bigger than requested */ - real_size = 0; + // We should _not_ return buffer bigger than requested + // real_size = 0; + + // printk("%s: We should _not_ return buffer bigger than requested size:%d real_size:%d\n", __func__, size, real_size); + *buf = kmalloc(size, gfp_flags); + real_size = size; } - } + } sqn_pr_leave(); @@ -478,19 +493,19 @@ static int sqn_handle_mac_addr_tag(struct sdio_func *func, u8 *data, u32 length) sqn_pr_dbg("single mac address\n"); /* we have only one mac addr */ get_mac_addr_from_str(data, length, priv->mac_addr); - + // Andrew 0720 // ++(priv->mac_addr[ETH_ALEN - 1]) - // real MAC: 38:E6:D8:86:00:00 + // real MAC: 38:E6:D8:86:00:00 // hboot will store: 38:E6:D8:85:FF:FF (minus 1) // sdio need to recovery it by plusing 1: 38:E6:D8:86:00:00 (plus 1) - + if ((++(priv->mac_addr[ETH_ALEN - 1])) == 0x00) - if ((++(priv->mac_addr[ETH_ALEN - 2])) == 0x00) - if ((++(priv->mac_addr[ETH_ALEN - 3])) == 0x00) - if ((++(priv->mac_addr[ETH_ALEN - 4])) == 0x00) - if ((++(priv->mac_addr[ETH_ALEN - 5])) == 0x00) - ++(priv->mac_addr[ETH_ALEN - 6]); + if ((++(priv->mac_addr[ETH_ALEN - 2])) == 0x00) + if ((++(priv->mac_addr[ETH_ALEN - 3])) == 0x00) + if ((++(priv->mac_addr[ETH_ALEN - 4])) == 0x00) + if ((++(priv->mac_addr[ETH_ALEN - 5])) == 0x00) + ++(priv->mac_addr[ETH_ALEN - 6]); } else if (2 * MAC_ADDR_STRING_LEN + 1 == length) { /* we have two macs */ @@ -673,7 +688,7 @@ int sqn_load_firmware(struct sdio_func *func) } sqn_pr_info("loading bootloader to the card...\n"); - if ((rv = sqn_load_bootstrapper(func, fw->data, fw->size))) + if ((rv = sqn_load_bootstrapper(func, (u8*) fw->data, fw->size))) goto out; /* boot the card */ @@ -686,6 +701,12 @@ int sqn_load_firmware(struct sdio_func *func) sqn_pr_info(" done\n"); out: + // To avoid kzalloc leakage in /drivers/base/firmware_class.c + if (fw) { + release_firmware(fw); + fw = NULL; + } + sqn_pr_leave(); return rv; } diff --git a/drivers/net/wimax/SQN/sdio-pm.c b/drivers/net/wimax/SQN/sdio-pm.c index 6cec7605b2f53..1fa658d09ac7c 100644 --- a/drivers/net/wimax/SQN/sdio-pm.c +++ b/drivers/net/wimax/SQN/sdio-pm.c @@ -24,10 +24,10 @@ #include #include #include +#include +#include -#ifdef ANDROID_KERNEL #include -#endif /* ANDROID_KERNEL */ #include "sdio-netdev.h" #include "sdio.h" @@ -36,8 +36,47 @@ #include "thp.h" #include "sdio-sqn.h" +#define SDIO_CLAIM_HOST_DEBUG 0 + +#if SDIO_CLAIM_HOST_DEBUG +#define sqn_sdio_claim_host(func) \ +({ \ + struct mmc_host *h = (func)->card->host; \ + sqn_pr_info("%s: claim_host+\n", __func__); \ + sqn_pr_info("%s: BEFORE claim: claimed %d, claim_cnt %d, claimer 0x%p\n" \ + , __func__, h->claimed, h->claim_cnt, h->claimer); \ + sdio_claim_host((func)); \ + sqn_pr_info("%s: AFTER claim: claimed %d, claim_cnt %d, claimer 0x%p\n" \ + , __func__, h->claimed, h->claim_cnt, h->claimer); \ + sqn_pr_info("%s: claim_host-\n", __func__); \ +}) + +#define sqn_sdio_release_host(func) \ +({ \ + struct mmc_host *h = (func)->card->host; \ + sqn_pr_info("%s: release_host+\n", __func__); \ + sqn_pr_info("%s: BEFORE release: claimed %d, claim_cnt %d, claimer 0x%p\n" \ + , __func__, h->claimed, h->claim_cnt, h->claimer); \ + sdio_release_host((func)); \ + sqn_pr_info("%s: AFTER release: claimed %d, claim_cnt %d, claimer 0x%p\n" \ + , __func__, h->claimed, h->claim_cnt, h->claimer); \ + sqn_pr_info("%s: release_host-\n", __func__); \ +}) +#else +#define sqn_sdio_claim_host(func) \ +({ \ + sdio_claim_host((func)); \ +}) + +#define sqn_sdio_release_host(func) \ +({ \ + sdio_release_host((func)); \ +}) +#endif + #define IGNORE_CARRIER_STATE 1 extern int mmc_wimax_get_hostwakeup_gpio(void); +extern void mmc_wimax_enable_host_wakeup(int on); enum sqn_thsp_service { #define THSP_LSP_SERVICE_BASE 0x10010000 @@ -163,8 +202,7 @@ static u32 g_last_request_pm = 0; /* TODO: move this to per-card private structure */ -static DECLARE_WAIT_QUEUE_HEAD(g_card_sleep_waitq); - +DECLARE_WAIT_QUEUE_HEAD(g_card_sleep_waitq); /* Transaction ID for lsp requests */ static u32 g_tid = 0; @@ -697,9 +735,8 @@ int sqn_wakeup_fw(struct sdio_func *func) u8 need_to_unlock_wakelock = 0; sqn_pr_enter(); - sqn_pr_info("waking up the card...\n"); - + if (!wake_lock_active(&card->wakelock)) { sqn_pr_dbg("lock wake_lock\n"); wake_lock(&card->wakelock); @@ -707,7 +744,10 @@ int sqn_wakeup_fw(struct sdio_func *func) } retry: - sdio_claim_host(func); + if (priv->removed) + goto out; + + sqn_sdio_claim_host(func); #define SDIO_CCCR_CCCR_SDIO_VERSION_VALUE 0x11 @@ -722,7 +762,7 @@ int sqn_wakeup_fw(struct sdio_func *func) if (rv) { sqn_pr_err("error when reading SDIO_VERSION\n"); - sdio_release_host(func); + sqn_sdio_release_host(func); goto out; } else { sqn_pr_dbg("SDIO_VERSION has been read successfully\n"); @@ -733,13 +773,16 @@ int sqn_wakeup_fw(struct sdio_func *func) if (rv) sqn_pr_err("error when writing to SQN_SOC_SIGS_LSBS: %d\n", rv); - sdio_release_host(func); + sqn_sdio_release_host(func); sqn_pr_info("wait for completion (timeout %d msec)...\n" , jiffies_to_msecs(timeout)); rv = wait_event_interruptible_timeout(g_card_sleep_waitq - , 0 == card->is_card_sleeps, timeout); + , 0 == card->is_card_sleeps || priv->removed, timeout); + + if (priv->removed) + goto out; if (-ERESTARTSYS == rv) { sqn_pr_warn("got a signal from kernel %d\n", rv); @@ -747,7 +790,7 @@ int sqn_wakeup_fw(struct sdio_func *func) rv = -1; sqn_pr_err("can't wake up the card - timeout elapsed\n"); - if (retry_cnt-- > 0) { + if (retry_cnt-- > 0 && card->is_card_sleeps) { sqn_pr_info("retry wake up\n"); goto retry; } @@ -771,22 +814,14 @@ int sqn_wakeup_fw(struct sdio_func *func) return rv; } -#ifdef ANDROID_KERNEL - -extern u8 sqn_is_gpio_irq_enabled; +extern void mmc_wimax_enable_host_wakeup(int on); static void sqn_handle_android_early_suspend(struct early_suspend *h) { sqn_pr_enter(); sqn_pr_info("%s: enter\n", __func__); - if (!sqn_is_gpio_irq_enabled) { - sqn_pr_info("enable GPIO%d interrupt\n", mmc_wimax_get_hostwakeup_gpio()); - enable_irq(MSM_GPIO_TO_INT(mmc_wimax_get_hostwakeup_gpio())); - enable_irq_wake(MSM_GPIO_TO_INT(mmc_wimax_get_hostwakeup_gpio())); - - sqn_is_gpio_irq_enabled = 1; - } + mmc_wimax_enable_host_wakeup(1); sqn_pr_info("%s: leave\n", __func__); sqn_pr_leave(); @@ -798,12 +833,7 @@ static void sqn_handle_android_late_resume(struct early_suspend *h) sqn_pr_enter(); sqn_pr_info("%s: enter\n", __func__); - if (sqn_is_gpio_irq_enabled) { - sqn_pr_info("disable GPIO%d interrupt\n", (mmc_wimax_get_hostwakeup_gpio())); - disable_irq_wake(MSM_GPIO_TO_INT(mmc_wimax_get_hostwakeup_gpio())); - disable_irq(MSM_GPIO_TO_INT(mmc_wimax_get_hostwakeup_gpio())); - sqn_is_gpio_irq_enabled = 0; - } + mmc_wimax_enable_host_wakeup(0); sqn_pr_info("%s: leave\n", __func__); sqn_pr_leave(); @@ -835,4 +865,3 @@ void unregister_android_earlysuspend(void) sqn_pr_leave(); } -#endif /* ANDROID_KERNEL */ diff --git a/drivers/net/wimax/SQN/sdio-sqn.h b/drivers/net/wimax/SQN/sdio-sqn.h index 343963a331e76..9a9897f57c595 100644 --- a/drivers/net/wimax/SQN/sdio-sqn.h +++ b/drivers/net/wimax/SQN/sdio-sqn.h @@ -133,6 +133,7 @@ int sqn_sdio_tx_skb(struct sqn_sdio_card *card, struct sk_buff *skb #define SQN_SDIO_IT_STATUS_MSBS 0x2047 /* Firmware loading registers */ +#define SQN_H_GRSTN 0x2400 #define SQN_H_CRSTN 0x2404 #define SQN_H_SDRAMCTL_RSTN 0x2414 #define SQN_H_SDRAM_NO_EMR 0x2415 diff --git a/drivers/net/wimax/SQN/sdio.c b/drivers/net/wimax/SQN/sdio.c index 176fb17fafa7b..c6c1893d46f32 100644 --- a/drivers/net/wimax/SQN/sdio.c +++ b/drivers/net/wimax/SQN/sdio.c @@ -46,8 +46,65 @@ #define SKB_DEBUG 0 #define SDIO_CLK_DEBUG 0 -#define DUMP_NET_PKT 0 +#define DUMP_NET_PKT 1 +int dump_net_pkt = 0; +#define RESET_BY_SDIO 0 //Rollback to default disabled HW Reset +#define RESET_BY_WIMAXTRACKER 0 + +#if RESET_BY_WIMAXTRACKER +#include "sdio_netlink.h" +#endif + +#define SDIO_CLAIM_HOST_DEBUG 0 +int claim_host_dbg = 0; + +#if SDIO_CLAIM_HOST_DEBUG +#define sqn_sdio_claim_host(func) \ +({ \ + struct mmc_host *h = (func)->card->host; \ + u8 was_blocked = 0; \ + if (mmc_wimax_get_cliam_host_status()) { \ + if (h->claimed) { \ + was_blocked = 1; \ + sqn_pr_info("%s: claim_host+ current 0x%p\n", __func__, current); \ + sqn_pr_info("%s: will block\n", __func__); \ + sqn_pr_info("%s: BEFORE claim: claimed %d, claim_cnt %d, claimer 0x%p\n" \ + , __func__, h->claimed, h->claim_cnt, h->claimer); \ + } \ + } \ + sdio_claim_host((func)); \ + if (mmc_wimax_get_cliam_host_status()) { \ + if (was_blocked) { \ + sqn_pr_info("%s: AFTER claim: claimed %d, claim_cnt %d, claimer 0x%p\n" \ + , __func__, h->claimed, h->claim_cnt, h->claimer); \ + sqn_pr_info("%s: claim_host- current 0x%p\n", __func__, current); \ + } \ + } \ +}) + +#define sqn_sdio_release_host(func) \ +({ \ + /* struct mmc_host *h = (func)->card->host; */ \ + /* sqn_pr_info("%s: release_host+\n", __func__); */ \ + /* sqn_pr_info("%s: BEFORE release: claimed %d, claim_cnt %d, claimer 0x%p\n" */ \ + /* , __func__, h->claimed, h->claim_cnt, h->claimer); */ \ + sdio_release_host((func)); \ + /* sqn_pr_info("%s: AFTER release: claimed %d, claim_cnt %d, claimer 0x%p\n" */ \ + /* , __func__, h->claimed, h->claim_cnt, h->claimer); */ \ + /* sqn_pr_info("%s: release_host-\n", __func__); */ \ +}) +#else +#define sqn_sdio_claim_host(func) \ +({ \ + sdio_claim_host((func)); \ +}) + +#define sqn_sdio_release_host(func) \ +({ \ + sdio_release_host((func)); \ +}) +#endif static const struct sdio_device_id sqn_sdio_ids[] = { { SDIO_DEVICE(SDIO_VENDOR_ID_SEQUANS, SDIO_DEVICE_ID_SEQUANS_SQN1130) }, @@ -57,11 +114,24 @@ static const struct sdio_device_id sqn_sdio_ids[] = { }; MODULE_DEVICE_TABLE(sdio, sqn_sdio_ids); +// Wakeup interrupt +extern void mmc_wimax_enable_host_wakeup(int on); + //HTC:WiMax power ON_OFF function and Card detect function extern int mmc_wimax_power(int on); extern void mmc_wimax_set_carddetect(int val); extern int mmc_wimax_uart_switch(int uart); extern int mmc_wimax_set_status(int on); +extern int mmc_wimax_get_hostwakeup_gpio(void); +extern int mmc_wimax_get_netlog_status(void); +extern int mmc_wimax_get_cliam_host_status(void); +extern int mmc_wimax_set_CMD53_timeout_trigger_counter(int counter); +extern int mmc_wimax_get_CMD53_timeout_trigger_counter(void); +extern int mmc_wimax_get_sdio_hw_reset(void); +extern int mmc_wimax_get_packet_filter(void); + +extern int mmc_wimax_get_netlog_withraw_status(void); +extern int mmc_wimax_get_sdio_interrupt_log(void); /*******************************************************************/ /* TX handlers */ @@ -119,10 +189,12 @@ static int sqn_sdio_get_rstn_wr_fifo_flag(struct sqn_private *priv) if (0 == card->rstn_wr_fifo_flag) { int rv = 0; - sdio_claim_host(card->func); // by daniel + + sqn_sdio_claim_host(card->func); card->rstn_wr_fifo_flag = sdio_readb(card->func, SQN_SDIO_RSTN_WR_FIFO(2), &rv); - sdio_release_host(card->func); // by daniel + + sqn_sdio_release_host(card->func); sqn_pr_dbg("RSTN_WR_FIFO2 = %d\n", card->rstn_wr_fifo_flag); if (rv) { sqn_pr_err("sdio_readb(RSTN_WR_FIFO2) - return error\n"); @@ -144,9 +216,10 @@ static int sqn_sdio_recover_after_cmd53_timeout(struct sqn_sdio_card *card) sqn_pr_enter(); sqn_pr_info("Try to recovery after SDIO timeout error\n"); - sdio_claim_host(card->func); + + sqn_sdio_claim_host(card->func); sdio_writeb(card->func, 1 << card->func->num, SDIO_CCCR_IO_ABORT, &rv); - sdio_release_host(card->func); + sqn_sdio_release_host(card->func); if (rv) { sqn_pr_err("sdio_writeb(SDIO_CCCR_IO_ABORT) - return error %d\n" , rv); @@ -176,7 +249,7 @@ static int sqn_sdio_cmd52_read_buf(struct sqn_sdio_card *card, void* buf, int si sqn_pr_enter(); sqn_pr_info("Trying to read %d bytes from 0x%x address using CMD52\n", size, addr); - sdio_claim_host(card->func); + sqn_sdio_claim_host(card->func); for (i = 0; i < size; i++) { tmpbuf[i] = sdio_readb(card->func, addr + i, &rv); if (rv) { @@ -184,7 +257,7 @@ static int sqn_sdio_cmd52_read_buf(struct sqn_sdio_card *card, void* buf, int si break; } } - sdio_release_host(card->func); + sqn_sdio_release_host(card->func); switch (size) { case sizeof(u16): @@ -212,8 +285,7 @@ static int sqn_sdio_dump_registers(struct sqn_sdio_card *card) sqn_pr_enter(); sqn_pr_info("------------------ REG DUMP BEGIN ------------------\n"); - sdio_claim_host(card->func); - + sqn_sdio_claim_host(card->func); b8 = sdio_readb(card->func, SQN_SDIO_IT_STATUS_LSBS, &rv); if (rv) sqn_pr_err("can't read SDIO_IT_STATUS_LSBS: %d\n", rv); @@ -244,7 +316,7 @@ static int sqn_sdio_dump_registers(struct sqn_sdio_card *card) else sqn_pr_info("SQN_HTS_SIGS: 0x%x\n", b8); - sdio_release_host(card->func); + sqn_sdio_release_host(card->func); rv = sqn_sdio_cmd52_read_buf(card, &b16, sizeof(b16), SQN_SDIO_WR_FIFO_BYTESLEFT(2)); if (rv) @@ -291,11 +363,11 @@ static int sqn_sdio_get_wr_fifo_level(struct sqn_private *priv) sqn_pr_enter(); - sdio_claim_host(card->func); // by daniel + sqn_sdio_claim_host(card->func); /* level = sdio_readw(card->func, SQN_SDIO_WR_FIFO_LEVEL(2), &rv); */ level = sdio_readl(card->func, 0x2050, &rv); level = (u32)level >> sizeof(u16); - sdio_release_host(card->func); // by daniel + sqn_sdio_release_host(card->func); sqn_pr_dbg("SQN_SDIO_WR_FIFO_LEVEL2 = %d\n", level); if (rv) { sqn_pr_err("sdio_readw(WR_FIFO_LEVEL2) error %d\r", rv); @@ -342,13 +414,38 @@ struct sk_buff* sqn_sdio_prepare_skb_for_tx(struct sk_buff *skb) return 0; #if DUMP_NET_PKT - if (!is_thp_packet(eth->h_source) && !is_lsp_packet(skb)) { - sqn_pr_info("----------------------------------------------------------------------\n"); + if (mmc_wimax_get_netlog_status()) { + sqn_pr_info("[PRT]-------------------------------------------------------------------\n"); sqn_pr_info("TX PDU length %d\n", skb->len); - sqn_pr_info_dump("TX PDU", skb->data, skb->len); - } + + if (is_thp_packet(eth->h_source)) { + sqn_pr_info("TX THP packet\n"); + } + else if (is_lsp_packet(skb)) { + sqn_pr_info("TX LSP packet\n"); + } + else if (!is_thp_packet(eth->h_source) && !is_lsp_packet(skb)) { + sqn_pr_info_dump("TX PDU", skb->data, skb->len); + } + } + + if (mmc_wimax_get_netlog_withraw_status()) { + // if (!is_thp_packet(eth->h_source) && !is_lsp_packet(skb)) + { + sqn_pr_info("[RAW]-------------------------------------------------------------------\n"); + sqn_pr_info("TX PDU length %d\n", skb->len); + sqn_pr_info_dump_rawdata("TX PDU", skb->data, skb->len); + } + } #endif + if (mmc_wimax_get_packet_filter()) { + if (sqn_filter_packet_check("TX PDU", skb->data, skb->len)) { + // sqn_pr_info("Drop TX packets len:%d\n", skb->len); + return 0; + } + } + /* * Real size of the PDU is data_len + 2 bytes at begining of PDU * for pdu_size + 4 bytes at the end of PDU for CRC of data @@ -413,7 +510,7 @@ int sqn_sdio_tx_skb(struct sqn_sdio_card *card, struct sk_buff *skb sqn_pr_enter(); if (claim_host) - sdio_claim_host(card->func); + sqn_sdio_claim_host(card->func); rv = sdio_writesb(card->func, SQN_SDIO_RDWR_FIFO(2), skb->data, skb->len); @@ -421,7 +518,7 @@ int sqn_sdio_tx_skb(struct sqn_sdio_card *card, struct sk_buff *skb sqn_pr_err("call to sdio_writesb(RDWR_FIFO2) - return error %d\n", rv); if (-ETIMEDOUT == rv) { if (claim_host) { - sdio_release_host(card->func); + sqn_sdio_release_host(card->func); claim_host = 0; } sqn_pr_info("SDIO CMD53 timeout error: TX PDU length %d, PDU[0] 0x%x, PDU[1] 0x%x\n" @@ -432,8 +529,8 @@ int sqn_sdio_tx_skb(struct sqn_sdio_card *card, struct sk_buff *skb goto release; } release: - if (claim_host) - sdio_release_host(card->func); + if (claim_host) + sqn_sdio_release_host(card->func); #if SKB_DEBUG sqn_pr_info("%s: free skb [0x%p] after tx, users %d\n", __func__, skb, atomic_read(&skb->users)); #endif @@ -579,24 +676,53 @@ static int sqn_sdio_host_to_card(struct sqn_private *priv) } sqn_sdio_release_wake_lock(card); - if (0 != rv) { + + if ((0 != rv) || (mmc_wimax_get_CMD53_timeout_trigger_counter())) { /* * Failed to send PDU - assume that card was removed or * crashed/reset so initiate card detection. */ - // Andrew 0424 - // Reset chip will cause WiMAX status to OFF and then SCAN, OPERATION - // Reset WiMAX chip - // It could avoid we hang in SDIO CMD53 timeout and recovery wimax again. - - sqn_pr_info("reset WiMAX chip\n"); - mmc_wimax_power(0); - mdelay(5); - mmc_wimax_power(1); - - sqn_pr_err("card seems to be dead/removed - initiate reinitialization\n"); - mmc_detect_change(card->func->card->host, 1); + if (mmc_wimax_get_CMD53_timeout_trigger_counter()) { + sqn_pr_info("Force CMD53 timeout to reset SDIO!\n"); + mmc_wimax_set_CMD53_timeout_trigger_counter(mmc_wimax_get_CMD53_timeout_trigger_counter()-1); + } + + // Reset WiMAX chip + if (mmc_wimax_get_sdio_hw_reset()) { // mmc_wimax_get_sdio_hw_reset [ + sqn_pr_info("reset WiMAX chip by SDIO\n"); + + // HW Reset + mmc_wimax_power(0); + mdelay(5); + mmc_wimax_power(1); + // To avoid re-initialized SDIO card failed + priv->removed = 1; + + sqn_pr_err("card seems to be dead/removed - initiate reinitialization\n"); + mmc_detect_change(card->func->card->host, 1); + + // SW Reset + // It could avoid we hang in SDIO CMD53 timeout and recovery wimax again. + /* + netif_carrier_off(priv->dev); + priv->removed = 1; + sqn_sdio_claim_host(card->func); + // software card reset + sdio_writeb(card->func, 0, SQN_H_GRSTN, &rv); + sqn_sdio_release_host(card->func); + mmc_detect_change(card->func->card->host, 0); + */ + } + else + { +#if RESET_BY_WIMAXTRACKER + sqn_pr_info("reset WiMAX chip by WimaxTracker\n"); + udp_broadcast(1,"ResetWimax_BySDIO\n"); +#else + sqn_pr_info("No reset WiMAX chip\n"); +#endif + } // mmc_wimax_get_sdio_hw_reset ] } drv_removed: sqn_pr_leave(); @@ -667,6 +793,12 @@ static int sqn_sdio_card_to_host(struct sqn_sdio_card *card) sqn_pr_enter(); +#if SDIO_CLAIM_HOST_DEBUG + if (mmc_wimax_get_cliam_host_status()) { + sqn_pr_info("%s+\n", __func__); + } +#endif + if (card->priv->removed) { // sqn_pr_warn("%s: card/driver is removed, do nothing\n", __func__); // Andrew 0524 goto drv_removed; @@ -682,7 +814,7 @@ static int sqn_sdio_card_to_host(struct sqn_sdio_card *card) need_to_ulock_mutex = 1; /* - * NOTE: call to sdio_claim_host() is already done + * NOTE: call to sqn_sdio_claim_host() is already done * in sqn_sdio_it_lsb() - our caller */ check_level: @@ -708,6 +840,12 @@ static int sqn_sdio_card_to_host(struct sqn_sdio_card *card) struct ethhdr *eth = 0; #endif u16 size = 0; + +#if SDIO_CLAIM_HOST_DEBUG + if (mmc_wimax_get_cliam_host_status()) { + sqn_pr_info("%s: 0\n", __func__); + } +#endif /* Get the size of PDU */ size = sdio_readw(card->func, SQN_SDIO_RDLEN_FIFO(2), &rv); @@ -745,23 +883,63 @@ static int sqn_sdio_card_to_host(struct sqn_sdio_card *card) skb_put(skb, size); #if DUMP_NET_PKT - eth = (struct ethhdr *)skb->data; - if (!is_thp_packet(eth->h_dest) && !is_lsp_packet(skb)) { - sqn_pr_info("----------------------------------------------------------------------\n"); - sqn_pr_info("RX PDU length %d\n", skb->len); - sqn_pr_info_dump("RX PDU", skb->data, skb->len); + if (mmc_wimax_get_netlog_status()) { + eth = (struct ethhdr *)skb->data; + + sqn_pr_info("[PRT]-------------------------------------------------------------------\n"); + sqn_pr_info("RX PDU length %d\n", skb->len); + + if (is_thp_packet(eth->h_dest)) { + sqn_pr_info("RX THP packet\n"); + } + else if (is_lsp_packet(skb)) { + sqn_pr_info("RX LSP packet\n"); + } + else if (!is_thp_packet(eth->h_dest) && !is_lsp_packet(skb)) { + sqn_pr_info_dump("RX PDU", skb->data, skb->len); + } + } + + if (mmc_wimax_get_netlog_withraw_status()) { + eth = (struct ethhdr *)skb->data; + // if (!is_thp_packet(eth->h_dest) && !is_lsp_packet(skb)) + { + sqn_pr_info("[RAW]-------------------------------------------------------------------\n"); + sqn_pr_info("RX PDU length %d\n", skb->len); + sqn_pr_info_dump_rawdata("RX PDU", skb->data, skb->len); + } } #endif - if (sqn_handle_lsp_packet(card->priv, skb)) - continue; + if (sqn_handle_lsp_packet(card->priv, skb)) { +#if SDIO_CLAIM_HOST_DEBUG + if (mmc_wimax_get_cliam_host_status()) { + sqn_pr_info("%s: 1\n", __func__); + } +#endif + continue; + } + /* * If we have some not LSP PDUs to read, then card is not * asleep any more, so we should notify waiters about this */ + +#if SDIO_CLAIM_HOST_DEBUG + if (mmc_wimax_get_cliam_host_status()) { + sqn_pr_info("%s: 2\n", __func__); + } +#endif + if (card->is_card_sleeps) { sqn_pr_info("got RX data, card is not asleep\n"); - signal_card_sleep_completion(card->priv); + /* signal_card_sleep_completion(card->priv); */ + card->is_card_sleeps = 0; +#if SDIO_CLAIM_HOST_DEBUG + if (mmc_wimax_get_cliam_host_status()) { + sqn_pr_info("%s: 3\n", __func__); + } +#endif } if (!card->waiting_pm_notification @@ -814,6 +992,12 @@ static int sqn_sdio_card_to_host(struct sqn_sdio_card *card) } drv_removed: + +#if SDIO_CLAIM_HOST_DEBUG + if (mmc_wimax_get_cliam_host_status()) { + sqn_pr_info("%s-\n", __func__); + } +#endif sqn_pr_leave(); return rv; } @@ -833,7 +1017,11 @@ static int sqn_sdio_it_lsb(struct sdio_func *func) sqn_pr_enter(); - /* NOTE: call of sdio_claim_host() is already done */ +#if SDIO_CLAIM_HOST_DEBUG + /* sqn_pr_info("%s+\n", __func__); */ +#endif + + /* NOTE: call of sqn_sdio_claim_host() is already done */ /* Read the interrupt status */ status = sdio_readb(func, SQN_SDIO_IT_STATUS_LSBS, &rc); @@ -867,6 +1055,11 @@ static int sqn_sdio_it_lsb(struct sdio_func *func) out: sqn_pr_dbg("returned code: %d\n", rc); + +#if SDIO_CLAIM_HOST_DEBUG + /* sqn_pr_info("%s-\n", __func__); */ +#endif + sqn_pr_leave(); return rc; } @@ -879,6 +1072,10 @@ static int sqn_sdio_it_msb(struct sdio_func *func) sqn_pr_enter(); +#if SDIO_CLAIM_HOST_DEBUG + /* sqn_pr_info("%s+\n", __func__); */ +#endif + /* Read the interrupt status */ status = sdio_readb(func, SQN_SDIO_IT_STATUS_MSBS, &rc); if (rc) @@ -894,6 +1091,11 @@ static int sqn_sdio_it_msb(struct sdio_func *func) out: sqn_pr_dbg("returned code: %d\n", rc); + +#if SDIO_CLAIM_HOST_DEBUG + /* sqn_pr_info("%s-\n", __func__); */ +#endif + sqn_pr_leave(); return rc; } @@ -912,8 +1114,23 @@ void sqn_sdio_interrupt(struct sdio_func *func) u8 is_card_sleeps = 0; struct sqn_sdio_card *card = sdio_get_drvdata(func); +#if SDIO_CLAIM_HOST_DEBUG + struct mmc_host *h = (func)->card->host; +#endif + sqn_pr_enter(); +#if SDIO_CLAIM_HOST_DEBUG + /* sqn_pr_info("%s+\n", __func__); */ +#endif + +#if SDIO_CLAIM_HOST_DEBUG + if (mmc_wimax_get_cliam_host_status()) { + sqn_pr_info("%s+: mmc_host: claimed %d, claim_cnt %d, claimer 0x%p\n" + , __func__, h->claimed, h->claim_cnt, h->claimer); + } +#endif + sqn_sdio_it_lsb(func); spin_lock_irqsave(&card->priv->drv_lock, irq_flags); @@ -923,6 +1140,12 @@ void sqn_sdio_interrupt(struct sdio_func *func) if (!is_card_sleeps) sqn_sdio_it_msb(func); +#if SDIO_CLAIM_HOST_DEBUG + if (mmc_wimax_get_cliam_host_status()) { + sqn_pr_info("%s-\n", __func__); + } +#endif + sqn_pr_leave(); } @@ -933,8 +1156,7 @@ static int sqn_sdio_it_enable(struct sdio_func *func) int rv = 0; sqn_pr_enter(); - sdio_claim_host(func); - + sqn_sdio_claim_host(func); /* enable LSB */ enable = SQN_SDIO_IT_WR_FIFO2_WM | SQN_SDIO_IT_RD_FIFO2_WM | SQN_SDIO_IT_SW_SIGN; @@ -955,7 +1177,7 @@ static int sqn_sdio_it_enable(struct sdio_func *func) goto out; } out: - sdio_release_host(func); + sqn_sdio_release_host(func); sqn_pr_dbg("returned code: %d\n", rv); sqn_pr_leave(); return rv; @@ -967,7 +1189,7 @@ static int sqn_sdio_it_disable(struct sdio_func *func) int rc = 0; sqn_pr_enter(); - sdio_claim_host(func); + sqn_sdio_claim_host(func); /* disable LSB */ sdio_writeb(func, 0, SQN_SDIO_IT_EN_LSBS, &rc); @@ -982,7 +1204,7 @@ static int sqn_sdio_it_disable(struct sdio_func *func) sqn_pr_dbg("disabled interrupt(MSB)\n"); out: sqn_pr_dbg("returned code: %d\n", rc); - sdio_release_host(func); + sqn_sdio_release_host(func); sqn_pr_leave(); return rc; } @@ -1012,7 +1234,7 @@ static void sqn_sdio_debug_test(struct sdio_func *func) /* int val = 0; */ sqn_pr_enter(); - sdio_claim_host(func); + sqn_sdio_claim_host(func); /* sqn_pr_dbg("write SQN_SOC_SIGS_LSBS\n"); */ /* sdio_writeb(func, 1, SQN_SOC_SIGS_LSBS, &rc); */ @@ -1070,7 +1292,7 @@ static void sqn_sdio_debug_test(struct sdio_func *func) sqn_pr_dbg("writel SQN_SDIO_WM_RD_FIFO(2) = %x\n", rc); #endif - sdio_release_host(func); + sqn_sdio_release_host(func); sqn_pr_leave(); } @@ -1119,9 +1341,9 @@ static int check_boot_from_host_mode(struct sdio_func *func) int rv = 0; int status = 0; - sdio_claim_host(func); + sqn_sdio_claim_host(func); status = sdio_readb(func, SQN_H_BOOT_FROM_SPI, &rv); - sdio_release_host(func); + sqn_sdio_release_host(func); if (rv) { @@ -1218,9 +1440,9 @@ static u8 sqn_get_card_version(struct sdio_func *func) */ sqn_pr_info("Checking card version...\n"); - sdio_claim_host(func); + sqn_sdio_claim_host(func); version = sdio_readl(func, SQN_H_VERSION, &rv); - sdio_release_host(func); + sqn_sdio_release_host(func); if (rv) { sqn_pr_err("failed to read card version\n"); rv = 0; @@ -1257,6 +1479,11 @@ extern struct sqn_private *g_priv; struct msmsdcc_host; int msmsdcc_enable_clocks(struct msmsdcc_host *host); +void msmsdcc_disable_clocks(struct msmsdcc_host *host, int deferr); +int msmsdcc_get_sdc_clocks(struct msmsdcc_host *host); +int sqn_sdio_get_sdc_clocks(void); +void sqn_sdio_set_sdc_clocks(int on); +int sqn_sdio_notify_host_wakeup(void); static irqreturn_t wimax_wakeup_gpio_irq_handler(int irq, void *dev_id) { @@ -1271,14 +1498,64 @@ static irqreturn_t wimax_wakeup_gpio_irq_handler(int irq, void *dev_id) // sqn_pr_info("WiMAX GPIO interrupt\n"); #endif - msmsdcc_enable_clocks(msm_host); - + // To avoid flush the logging in kmsg, remove it. + if (mmc_wimax_get_sdio_interrupt_log()) { + if (printk_ratelimit()) + sqn_pr_info("WiMAX GPIO interrupt\n"); + } + + if (!sqn_sdio_get_sdc_clocks()) { + msmsdcc_enable_clocks(msm_host); + msmsdcc_disable_clocks(msm_host, 5 * HZ); + } sqn_pr_leave(); return IRQ_HANDLED; } +int sqn_sdio_notify_host_wakeup(void) +{ + struct sqn_sdio_card *card = g_priv->card; + int rv = 0; + + sqn_pr_enter(); + + rv = sqn_wakeup_fw(card->func); + + sqn_pr_leave(); + + return rv; +} +EXPORT_SYMBOL(sqn_sdio_notify_host_wakeup); + +int sqn_sdio_get_sdc_clocks(void) +{ + struct sqn_sdio_card *card = g_priv->card; + struct msmsdcc_host *host = mmc_priv(card->func->card->host); + + return msmsdcc_get_sdc_clocks(host); +} +EXPORT_SYMBOL(sqn_sdio_get_sdc_clocks); + +void sqn_sdio_set_sdc_clocks(int on) +{ + struct sqn_sdio_card *card = g_priv->card; + struct msmsdcc_host *host = mmc_priv(card->func->card->host); + + sqn_pr_enter(); + + if (on) { + msmsdcc_enable_clocks(host); + } + else { + msmsdcc_disable_clocks(host, 0); + } + + sqn_pr_leave(); +} +EXPORT_SYMBOL(sqn_sdio_set_sdc_clocks); + int init_thp_handler(struct net_device *dev); void cleanup_thp_handler(void); @@ -1334,7 +1611,7 @@ static int sqn_sdio_probe(struct sdio_func *func, sqn_card->func = func; /* Activate SDIO function and register interrupt handler */ - sdio_claim_host(func); + sqn_sdio_claim_host(func); rv = sdio_enable_func(func); if (rv) @@ -1344,7 +1621,7 @@ static int sqn_sdio_probe(struct sdio_func *func, if (rv) goto disable; - sdio_release_host(func); + sqn_sdio_release_host(func); sdio_set_drvdata(func, sqn_card); priv = sqn_add_card(sqn_card, &func->dev); @@ -1401,8 +1678,9 @@ static int sqn_sdio_probe(struct sdio_func *func, if (0 == sqn_card->rstn_wr_fifo_flag) sqn_pr_warn("FW is still not started, anyway continue as is...\n"); - sqn_pr_info("setup GPIO40 for wakeup form SQN1210\n"); - rv = irq = MSM_GPIO_TO_INT(40); //GPIO_40 as wakeup + + sqn_pr_info("setup GPIO%d for wakeup form SQN1210\n", mmc_wimax_get_hostwakeup_gpio()); + rv = irq = MSM_GPIO_TO_INT(mmc_wimax_get_hostwakeup_gpio()); //HOST WAKEUP GPIO as wakeup if (rv < 0) { sqn_pr_warn("wimax-gpio to irq failed\n"); @@ -1410,14 +1688,14 @@ static int sqn_sdio_probe(struct sdio_func *func, } rv = request_irq(irq, wimax_wakeup_gpio_irq_handler, - req_flags, "WiMAX0", sqn_card->priv->dev); + req_flags, "WiMAX0", sqn_card->priv->dev); // IRQF_TRIGGER_RISING, raising trigger if (rv) { sqn_pr_warn("wimax-gpio request_irq failed=%d\n", rv); goto disable; } - sqn_pr_dbg("disable GPIO40 interrupt\n"); - disable_irq(MSM_GPIO_TO_INT(40)); + sqn_pr_dbg("disable GPIO%d interrupt\n", mmc_wimax_get_hostwakeup_gpio()); + disable_irq(MSM_GPIO_TO_INT(mmc_wimax_get_hostwakeup_gpio())); rv = init_thp(priv->dev); if (rv) @@ -1444,12 +1722,12 @@ static int sqn_sdio_probe(struct sdio_func *func, flush_scheduled_work(); free_netdev(priv->dev); reclaim: - sdio_claim_host(func); + sqn_sdio_claim_host(func); sdio_release_irq(func); disable: sdio_disable_func(func); release: - sdio_release_host(func); + sqn_sdio_release_host(func); sqn_sdio_free_tx_queue(sqn_card); sqn_sdio_free_rx_queue(sqn_card); @@ -1468,6 +1746,8 @@ static int sqn_sdio_probe(struct sdio_func *func, } +extern wait_queue_head_t g_card_sleep_waitq; + static void sqn_sdio_remove(struct sdio_func *func) { struct sqn_sdio_card *sqn_card = sdio_get_drvdata(func); @@ -1477,8 +1757,8 @@ static void sqn_sdio_remove(struct sdio_func *func) sqn_pr_enter(); - sqn_pr_info("free GPIO40 interrupt\n"); - free_irq(MSM_GPIO_TO_INT(40),sqn_card->priv->dev); + sqn_pr_info("free GPIO%d interrupt\n", mmc_wimax_get_hostwakeup_gpio()); + free_irq(MSM_GPIO_TO_INT(mmc_wimax_get_hostwakeup_gpio()),sqn_card->priv->dev); #if defined(DEBUG) sqn_sdio_print_debug_info(func); @@ -1493,6 +1773,7 @@ static void sqn_sdio_remove(struct sdio_func *func) delay = 1000; sqn_sdio_it_disable(sqn_card->func); + wake_up_interruptible(&g_card_sleep_waitq); sqn_pr_info("wait until RX is finished\n"); count = 5; @@ -1502,8 +1783,8 @@ static void sqn_sdio_remove(struct sdio_func *func) sqn_pr_warn("%s: failed to acquire RX mutex\n", __func__); sqn_stop_card(sqn_card->priv); - kthread_stop(sqn_card->priv->tx_thread); wake_up_interruptible(&sqn_card->priv->tx_waitq); + kthread_stop(sqn_card->priv->tx_thread); sqn_pr_info("wait until TX is finished\n"); count = 5; @@ -1512,10 +1793,10 @@ static void sqn_sdio_remove(struct sdio_func *func) if (!rv) sqn_pr_warn("%s: failed to acquire TX mutex\n", __func__); - sdio_claim_host(func); + sqn_sdio_claim_host(func); sdio_release_irq(func); sdio_disable_func(func); - sdio_release_host(func); + sqn_sdio_release_host(func); sqn_remove_card(sqn_card->priv); @@ -1539,7 +1820,6 @@ static void sqn_sdio_remove(struct sdio_func *func) sqn_pr_leave(); } -u8 sqn_is_gpio_irq_enabled = 0; int sqn_sdio_suspend(struct sdio_func *func, pm_message_t msg) { int rv = 0; @@ -1576,12 +1856,7 @@ int sqn_sdio_suspend(struct sdio_func *func, pm_message_t msg) } out: - if (!sqn_is_gpio_irq_enabled) { - sqn_pr_info("enable GPIO40 interrupt\n"); - enable_irq(MSM_GPIO_TO_INT(40)); - enable_irq_wake(MSM_GPIO_TO_INT(40)); - sqn_is_gpio_irq_enabled = 1; - } + mmc_wimax_enable_host_wakeup(1); sqn_pr_info("%s: leave\n", __func__); sqn_pr_leave(); @@ -1606,29 +1881,28 @@ int sqn_sdio_resume(struct sdio_func *func) // some TX data /* sqn_notify_host_wakeup(func); */ - if (sqn_is_gpio_irq_enabled) { - sqn_pr_info("disable GPIO40 interrupt\n"); - disable_irq_wake(MSM_GPIO_TO_INT(40)); - disable_irq(MSM_GPIO_TO_INT(40)); - sqn_is_gpio_irq_enabled = 0; - } + mmc_wimax_enable_host_wakeup(0); sqn_pr_info("%s: leave\n", __func__); sqn_pr_leave(); return rv; } +int sqn_sdio_dump_net_pkt(int on) { + + printk("[SDIO] %s: dump_net_pkt: %d\n", __func__, on); + dump_net_pkt = on; + + return 0; +} static struct sdio_driver sqn_sdio_driver = { .name = SQN_MODULE_NAME , .id_table = sqn_sdio_ids , .probe = sqn_sdio_probe , .remove = sqn_sdio_remove -#if 0 -def ANDROID_KERNEL - , .suspend = sqn_sdio_suspend - , .resume = sqn_sdio_resume -#endif +// , .suspend = sqn_sdio_suspend +// , .resume = sqn_sdio_resume }; @@ -1651,12 +1925,17 @@ static int __init sqn_sdio_init_module(void) mmc_wimax_set_carddetect(1); // thp_wimax_uart_switch(1); mmc_wimax_set_status(1); - + dump_net_pkt = mmc_wimax_get_netlog_status(); + claim_host_dbg = mmc_wimax_get_cliam_host_status(); + + rc = sdio_register_driver(&sqn_sdio_driver); -#ifdef ANDROID_KERNEL register_android_earlysuspend(); -#endif /* TI_KERNEL */ + +#if RESET_BY_WIMAXTRACKER + sdio_netlink_register(); +#endif sqn_pr_info("Driver has been registered\n"); @@ -1671,9 +1950,7 @@ static void __exit sqn_sdio_exit_module(void) sdio_unregister_driver(&sqn_sdio_driver); -#ifdef ANDROID_KERNEL unregister_android_earlysuspend(); -#endif sqn_pr_info("Driver has been removed\n"); @@ -1682,6 +1959,10 @@ static void __exit sqn_sdio_exit_module(void) // thp_wimax_uart_switch(0); mmc_wimax_set_status(0); +#if RESET_BY_WIMAXTRACKER + sdio_netlink_deregister(); +#endif + sqn_pr_leave(); } diff --git a/drivers/net/wimax/SQN/sdio.h b/drivers/net/wimax/SQN/sdio.h index 564897bcffbbe..0f8bcd07a22fd 100644 --- a/drivers/net/wimax/SQN/sdio.h +++ b/drivers/net/wimax/SQN/sdio.h @@ -15,4 +15,6 @@ #include #include +int sqn_sdio_dump_net_pkt(int on); + #endif /* _SQN_SDIO_WRAPPERS_H */ diff --git a/drivers/net/wimax/SQN/sdio_netlink.c b/drivers/net/wimax/SQN/sdio_netlink.c new file mode 100644 index 0000000000000..46068e4e6ca54 --- /dev/null +++ b/drivers/net/wimax/SQN/sdio_netlink.c @@ -0,0 +1,57 @@ +#include "sdio_netlink.h" + +struct sock *netlink_sock; + +void udp_broadcast(int gid,void *payload) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + int size=strlen(payload)+1; + int len = NLMSG_SPACE(size); + void *data; + int ret; + + skb = alloc_skb(len, GFP_KERNEL); + if (!skb) + return; + nlh= NLMSG_PUT(skb, 0, 0, 0, size); + nlh->nlmsg_flags = 0; + data=NLMSG_DATA(nlh); + memcpy(data, payload, size); + NETLINK_CB(skb).pid = 0; /* from kernel */ + NETLINK_CB(skb).dst_group = gid; /* unicast */ + ret=netlink_broadcast(netlink_sock, skb, 0, gid, GFP_KERNEL); + + if (ret <0) + { + printk("[SDIO] %s send failed\n", __func__); + return; + } + return; + +nlmsg_failure: /* Used by NLMSG_PUT */ + if (skb) + kfree_skb(skb); +} + +void MyTimerFunction(unsigned long data) +{ + udp_broadcast(1,"ResetWimax_BySDIO\n"); +} + +void udp_receive(struct sk_buff *skb) +{ +} + +int sdio_netlink_register(void) +{ + netlink_sock = netlink_kernel_create(&init_net, NETLINK_USERSOCK, 0,udp_receive, NULL, THIS_MODULE); + return 0; +} + +void sdio_netlink_deregister(void) +{ + sock_release(netlink_sock->sk_socket); + printk("[SDIO] %s: netlink driver remove successfully\n", __func__); +} + diff --git a/drivers/net/wimax/SQN/sdio_netlink.h b/drivers/net/wimax/SQN/sdio_netlink.h new file mode 100644 index 0000000000000..b8c0c3e1d737e --- /dev/null +++ b/drivers/net/wimax/SQN/sdio_netlink.h @@ -0,0 +1,17 @@ +#ifndef _SDIO_NETLINK_H +#define _SDIO_NETLINK_H + +#include +#include +#include +#include +#include +#include + +void udp_broadcast(int gid,void *payload); +void MyTimerFunction(unsigned long data); +void udp_receive(struct sk_buff *skb); +int sdio_netlink_register(void); +void sdio_netlink_deregister(void); + +#endif diff --git a/drivers/net/wimax/SQN/thp.c b/drivers/net/wimax/SQN/thp.c index 58f9ba13c3a9b..9bc7ca7778da1 100644 --- a/drivers/net/wimax/SQN/thp.c +++ b/drivers/net/wimax/SQN/thp.c @@ -46,6 +46,8 @@ extern bool drop_packet; const uint8_t host_macaddr[ETH_ALEN] = {0x00, 0x16, 0x08, 0xff, 0x00, 0x01}; const uint8_t ss_macaddr[ETH_ALEN] = {0x00, 0x16, 0x08, 0xff, 0x00, 0x00}; +extern int sqn_sdio_dump_net_pkt(int on); +extern int mmc_wimax_get_thp_log(void); // Queue of packets destined to the Connection Manager // TODO: check size of the queue, it's should always be one. @@ -331,7 +333,7 @@ struct file_operations thp_fops = , .read = thp_read , .write= thp_write , .poll = thp_poll - , .ioctl = thp_ioctl + , .unlocked_ioctl = thp_ioctl }; /********************** File Operations BEGIN *****************************/ @@ -380,9 +382,8 @@ static ssize_t thp_read(struct file *filp, char *buf, size_t count, loff_t*ppos) struct sk_buff_head *head = &to_sqntool_queue; struct sk_buff *curr = NULL; ssize_t retval; -#if THP_HEADER_DUMP const struct sqn_thp_header *th = 0; -#endif + sqn_pr_enter(); #if THP_DEBUG printk(KERN_WARNING "thp_read +\n"); @@ -427,25 +428,21 @@ static ssize_t thp_read(struct file *filp, char *buf, size_t count, loff_t*ppos) retval = -EFAULT; goto free_skb; } -#if THP_TRACE - sqn_pr_info("%s: [to_user]: len = %d\n", __func__, count); -#endif -#if THP_HEADER_DUMP - th = (struct sqn_thp_header *) curr->data; - sqn_pr_info("%s: PKTLen: %4u | TVer: 0x0%x | Flags: 0x0%x | Len: %4u" - " | SeqNum: %5u | AckNum: %5u | TLen: %5u\n", __func__ - , count - , th->transport_version - , th->flags - , be16_to_cpu(th->length) - , be16_to_cpu(th->seq_number) - , be16_to_cpu(th->ack_number) - , be32_to_cpu(th->total_length)); -#endif - sqn_pr_dbg("[to_user]: len = %d\n", count); -#ifdef SQN_DEBUG_DUMP - sqn_pr_dbg_dump("RX:", curr->data, count); -#endif + + if (mmc_wimax_get_thp_log()) { + sqn_pr_info("%s: [to_user]: len = %d\n", __func__, count); + th = (struct sqn_thp_header *) curr->data; + sqn_pr_info("%s: PKTLen: %4u | TVer: 0x0%x | Flags: 0x0%x | Len: %4u" + " | SeqNum: %5u | AckNum: %5u | TLen: %5u\n", __func__ + , count + , th->transport_version + , th->flags + , be16_to_cpu(th->length) + , be16_to_cpu(th->seq_number) + , be16_to_cpu(th->ack_number) + , be32_to_cpu(th->total_length)); + sqn_pr_dbg_dump("THP RX:", curr->data, count); + } #if SKB_DEBUG sqn_pr_info("%s: free skb [0x%p], users %d\n", __func__, curr, atomic_read(&curr->users)); @@ -477,9 +474,7 @@ static ssize_t thp_write(struct file *file, const char *buf, struct sk_buff *skb; struct ethhdr ethh; int size = count + ETH_HLEN; -#if THP_HEADER_DUMP const struct sqn_thp_header *th = 0; -#endif sqn_pr_enter(); #if THP_DEBUG @@ -509,25 +504,22 @@ static ssize_t thp_write(struct file *file, const char *buf, return -EFAULT; } skb_put(skb, count); -#if THP_TRACE - sqn_pr_info("%s: [from_user]: len = %d\n", __func__, count); -#endif -#if THP_HEADER_DUMP - th = (struct sqn_thp_header *) buf; - sqn_pr_info("%s: PKTLen: %4u | TVer: 0x0%x | Flags: 0x0%x | Len: %4u" - " | SeqNum: %5u | AckNum: %5u | TLen: %5u\n", __func__ - , count - , th->transport_version - , th->flags - , be16_to_cpu(th->length) - , be16_to_cpu(th->seq_number) - , be16_to_cpu(th->ack_number) - , be32_to_cpu(th->total_length)); -#endif - sqn_pr_dbg("[from_user]: len = %d\n", count); -#ifdef SQN_DEBUG_DUMP - sqn_pr_dbg_dump("TX:", skb->data, count); -#endif + + if (mmc_wimax_get_thp_log()) { + sqn_pr_info("%s: [from_user]: len = %d\n", __func__, count); + th = (struct sqn_thp_header *) buf; + sqn_pr_info("%s: PKTLen: %4u | TVer: 0x0%x | Flags: 0x0%x | Len: %4u" + " | SeqNum: %5u | AckNum: %5u | TLen: %5u\n", __func__ + , count + , th->transport_version + , th->flags + , be16_to_cpu(th->length) + , be16_to_cpu(th->seq_number) + , be16_to_cpu(th->ack_number) + , be32_to_cpu(th->total_length)); + + sqn_pr_dbg_dump("THP TX:", skb->data, count); + } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) this_device->hard_start_xmit(skb, this_device); @@ -601,6 +593,14 @@ static int thp_ioctl(struct inode* dev, struct file* handle, unsigned int cmd, u mmc_wimax_uart_switch(0); // USB break; + case IOCTL_SWITCH_NETLOG: + printk(KERN_WARNING "IOCTL_SWITCH_NETLOG arg=%d\n",(int)arg); + if(arg == 0) + sqn_sdio_dump_net_pkt(0); // Enable netlog + else + sqn_sdio_dump_net_pkt(1); // Disable netlog + break; + default: printk(KERN_WARNING "UNKNOWN OPERATION in thp_ioctl\n"); return -1; diff --git a/drivers/net/wimax/SQN/thp_ioctl.h b/drivers/net/wimax/SQN/thp_ioctl.h index ef939e565e91f..4d284307439d1 100644 --- a/drivers/net/wimax/SQN/thp_ioctl.h +++ b/drivers/net/wimax/SQN/thp_ioctl.h @@ -11,11 +11,13 @@ //must be a nonnegative 8-bit number #define CMD_DROP_PACKETS CMD_BASE+0 #define CMD_SIWTCH_UART CMD_BASE+1 +#define CMD_SIWTCH_NETLOG CMD_BASE+2 //write only //arg=1, drop tx/rx packets //arg=0, normal mode #define IOCTL_DROP_PACKETS _IOW(WIMAX_DEV_IOCTLID, CMD_DROP_PACKETS, int) #define IOCTL_SWITCH_UART _IOW(WIMAX_DEV_IOCTLID, CMD_SIWTCH_UART, int) +#define IOCTL_SWITCH_NETLOG _IOW(WIMAX_DEV_IOCTLID, CMD_SIWTCH_NETLOG, int) #endif //_THP_IOCTL_H_ diff --git a/drivers/net/wimax/wimaxdbg/Makefile b/drivers/net/wimax/wimaxdbg/Makefile new file mode 100644 index 0000000000000..3301bf036458b --- /dev/null +++ b/drivers/net/wimax/wimaxdbg/Makefile @@ -0,0 +1,10 @@ +MODULE_NAME = wimaxdbg +KDIR = /lib/modules/$(CURRENT)/build + +obj-m := $(MODULE_NAME).o +all: + @echo "making $(MODULE_NAME)" + @echo "PWD=$(PWD)" + $(MAKE) -C $(KDIR) M=$(PWD) modules +clean: + $(MAKE) -C $(KDIR) M=$(PWD) clean diff --git a/drivers/net/wimax/wimaxdbg/wimaxdbg.c b/drivers/net/wimax/wimaxdbg/wimaxdbg.c new file mode 100644 index 0000000000000..25118e9ec8700 --- /dev/null +++ b/drivers/net/wimax/wimaxdbg/wimaxdbg.c @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include +#include + +extern int mmc_wimax_set_netlog_status(int on); +extern int mmc_wimax_set_cliam_host_status(int on); +// extern int sqn_sdio_get_sdc_clocks(void); +// extern void sqn_sdio_set_sdc_clocks(int on); +extern int mmc_wimax_set_busclk_pwrsave(int on); +extern int mmc_wimax_set_CMD53_timeout_trigger_counter(int counter); +extern int mmc_wimax_get_CMD53_timeout_trigger_counter(void); + +extern int sqn_sdio_notify_host_wakeup(void); +extern int mmc_wimax_set_thp_log(int on); +extern int mmc_wimax_set_sdio_hw_reset(int on); +extern int mmc_wimax_set_packet_filter(int on); + +extern int mmc_wimax_set_netlog_withraw_status(int on); +extern int mmc_wimax_set_sdio_interrupt_log(int on); + +#define BUF_LEN 100 + +// # insmod wimaxdbg + +static char *wimaxdbg_name=NULL; +module_param(wimaxdbg_name,charp,0); + +static int dbg_para = 0; +static struct proc_dir_entry *wimaxdbg_proc_file; + +ssize_t wimaxdbg_write(struct file *file, const char *buffer, unsigned long count, void *data) +{ + char buf[16]; + unsigned long len = count; + int n; + + printk(KERN_INFO "%d (%s)\n", (int)len, __func__); + + if (len >= sizeof(buf)) + len = sizeof(buf) - 1; + + if (copy_from_user(buf, buffer, len)) + return -EFAULT; + + buf[len] = '\0'; + + n = simple_strtol(buf, NULL, 10); + dbg_para = n; + + printk(KERN_INFO "%s: dbg_parameter:%d\n", __func__, dbg_para); + if (dbg_para < 2) { // 0: netlog off, 1: netlog on + printk(KERN_INFO "%s: mmc_wimax_set_netlog_status:%d\n", __func__, dbg_para); + mmc_wimax_set_netlog_status(dbg_para); + } + else if (dbg_para == 3) { // 3: sdc_clock off + printk(KERN_INFO "%s: sqn_sdio_set_sdc_clocks:0\n", __func__); + // sqn_sdio_set_sdc_clocks(0); // Need to insert sequans_sdio.ko first + } + else if (dbg_para == 4) { // 4: sdc_clock on + printk(KERN_INFO "%s: sqn_sdio_set_sdc_clocks:1\n", __func__); + // sqn_sdio_set_sdc_clocks(1); // Need to insert sequans_sdio.ko first + } + else if (dbg_para == 5) { // 5: claim_host debug on + printk(KERN_INFO "%s: mmc_wimax_set_cliam_host_status:1\n", __func__); + mmc_wimax_set_cliam_host_status(1); + } + else if (dbg_para == 6) { // 6: claim host debug off + printk(KERN_INFO "%s: mmc_wimax_set_cliam_host_status:0\n", __func__); + mmc_wimax_set_cliam_host_status(0); + } + else if (dbg_para == 7) { // 7: Turn off dynamic SDC CLK OFF + printk(KERN_INFO "%s: mmc_wimax_set_busclk_pwrsave:0\n", __func__); + mmc_wimax_set_busclk_pwrsave(0); + } + else if (dbg_para == 8) { // 8: Turn on dynamic SDC CLK OFF + printk(KERN_INFO "%s: mmc_wimax_set_busclk_pwrsave:1\n", __func__); + mmc_wimax_set_busclk_pwrsave(1); + } + else if (dbg_para == 9) { // 9: Disable force CMD53 timeout testing + printk(KERN_INFO "%s: mmc_wimax_set_CMD53_timeout_trigger_counter:0\n", __func__); + mmc_wimax_set_CMD53_timeout_trigger_counter(0); + } + else if (dbg_para == 10) { // 10: Force CMD53 timeout testing + printk(KERN_INFO "%s: mmc_wimax_set_CMD53_timeout_trigger_counter:1\n", __func__); + mmc_wimax_set_CMD53_timeout_trigger_counter(5); + } + else if (dbg_para == 11) { // 11: Manually re-send host wakeup + // printk(KERN_INFO "%s: sqn_sdio_notify_host_wakeup\n", __func__); + // sqn_sdio_notify_host_wakeup(); // Need to insert sequans_sdio.ko first + } + else if (dbg_para == 12) { // 12: Disable THP logging + printk(KERN_INFO "%s: mmc_wimax_set_thp_log_status:0\n", __func__); + mmc_wimax_set_thp_log(0); + } + else if (dbg_para == 13) { // 13: Enable THP logging + printk(KERN_INFO "%s: mmc_wimax_set_thp_log_status:1\n", __func__); + mmc_wimax_set_thp_log(1); + } + else if (dbg_para == 14) { // 14: Disable SDIO HW RESET, default is disabled it. + printk(KERN_INFO "%s: mmc_wimax_set_sdio_hw_reset:0\n", __func__); + mmc_wimax_set_sdio_hw_reset(0); + } + else if (dbg_para == 15) { // 15: Enable SDIO HW RESET + printk(KERN_INFO "%s: mmc_wimax_set_sdio_hw_reset:1\n", __func__); + mmc_wimax_set_sdio_hw_reset(1); + } + else if (dbg_para == 16) { // 16: Disable SDIO Packet filter + printk(KERN_INFO "%s: mmc_wimax_set_packet_filter:0\n", __func__); + mmc_wimax_set_packet_filter(0); + } + else if (dbg_para == 17) { // 17: Enable SDIO Packet filter + printk(KERN_INFO "%s: mmc_wimax_set_packet_filter:1\n", __func__); + mmc_wimax_set_packet_filter(1); + } + else if (dbg_para == 18) { // 18: Disable SDIO GPIO interrupt logging + printk(KERN_INFO "%s: mmc_wimax_set_sdio_interrupt_log:0\n", __func__); + mmc_wimax_set_sdio_interrupt_log(0); + } + else if (dbg_para == 19) { // 19: Enable SDIO GPIO interrupt logging + printk(KERN_INFO "%s: mmc_wimax_set_sdio_interrupt_log:1\n", __func__); + mmc_wimax_set_sdio_interrupt_log(1); + } + else if (dbg_para == 20) { // 20: Disable dumping raw data for network packets + printk(KERN_INFO "%s: mmc_wimax_set_netlog_withraw_status:0\n", __func__); + mmc_wimax_set_netlog_withraw_status(0); + } + else if (dbg_para == 21) { // 21: Enable dumping raw data for network packets + printk(KERN_INFO "%s: mmc_wimax_set_netlog_withraw_status:1\n", __func__); + mmc_wimax_set_netlog_withraw_status(1); + } + else { + printk(KERN_INFO "%s: None function:%d\n", __func__, dbg_para); + } + + return (len); +} + +ssize_t wimaxdbg_read(char *buf,char **start,off_t offset,int count,int *eof,void *data) +{ + int len=0; + + if(offset>0) + return 0; + + /* + sprintf(buf,"wimxdbg: %d\nsdcclk:%d\n", dbg_para, sqn_sdio_get_sdc_clocks()); + + for(len=0;lenread_proc = wimaxdbg_read; + wimaxdbg_proc_file->write_proc = wimaxdbg_write; + + dbg_para = 0; + + return 0; +} + +void wimaxdbg_cleanup(void) +{ + printk(KERN_INFO "%s: mimaxdbg_module_claen called. Module is now clean\n", __func__); + remove_proc_entry("wimaxdbg",NULL); +} + +module_init(wimaxdbg_init); +module_exit(wimaxdbg_cleanup); + +MODULE_DESCRIPTION("HTC wimaxdbg for SDIO devices"); +MODULE_AUTHOR("HTC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wimax/wimaxuart/Makefile b/drivers/net/wimax/wimaxuart/Makefile new file mode 100644 index 0000000000000..537f716c2e162 --- /dev/null +++ b/drivers/net/wimax/wimaxuart/Makefile @@ -0,0 +1,10 @@ +MODULE_NAME = wimaxuart +KDIR = /lib/modules/$(CURRENT)/build + +obj-m := $(MODULE_NAME).o +all: + @echo "making $(MODULE_NAME)" + @echo "PWD=$(PWD)" + $(MAKE) -C $(KDIR) M=$(PWD) modules +clean: + $(MAKE) -C $(KDIR) M=$(PWD) clean diff --git a/drivers/net/wimax/wimaxuart/wimaxuart.c b/drivers/net/wimax/wimaxuart/wimaxuart.c new file mode 100644 index 0000000000000..07a2993c5f3ec --- /dev/null +++ b/drivers/net/wimax/wimaxuart/wimaxuart.c @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include +#include + +#define BUF_LEN 100 + +// # insmod wimaxuart + +static char *wimaxuart_name=NULL; +module_param(wimaxuart_name,charp,0); + +extern int mmc_wimax_uart_switch(int uart); + +static struct proc_dir_entry *wimaxuart_proc_file; +int uart_switch = 0; + +ssize_t wimaxuart_write(struct file *file, const char *buffer, unsigned long count, void *data) +{ + char buf[16]; + unsigned long len = count; + int n; + + printk(KERN_INFO "%d (%s)\n", (int)len, __func__); + + if (len >= sizeof(buf)) + len = sizeof(buf) - 1; + + if (copy_from_user(buf, buffer, len)) + return -EFAULT; + + buf[len] = '\0'; + + n = simple_strtol(buf, NULL, 10); + uart_switch = n; + + printk("%s: uart_switch:%d\n", __func__, uart_switch); + mmc_wimax_uart_switch(uart_switch); + + return (len); +} + +ssize_t wimaxuart_read(char *buf,char **start,off_t offset,int count,int *eof,void *data) +{ + int len=0; + + if(offset>0) + return 0; + + sprintf(buf,"wimaxuart: %d\n", uart_switch); + + for(len=0;lenread_proc = wimaxuart_read; + wimaxuart_proc_file->write_proc = wimaxuart_write; + + uart_switch = 0; + + return 0; +} + +void wimaxuart_cleanup(void) +{ + printk(KERN_INFO "%s: wimaxuart_module_claen called. Module is now clean\n", __func__); + remove_proc_entry("wimaxuart",NULL); +} + +module_init(wimaxuart_init); +module_exit(wimaxuart_cleanup); + +MODULE_DESCRIPTION("HTC wimaxuart for SDIO devices"); +MODULE_AUTHOR("HTC"); +MODULE_LICENSE("GPL"); From 3bf890b6def9ebca97de6a3897a3f6edf658c330 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sat, 5 Feb 2011 10:16:16 -0500 Subject: [PATCH 1445/2556] drivers: mtd: msm_nand: adds 3 attributes for HTC SSD HW INFO tool and cleans up bit. --- drivers/mtd/devices/msm_nand.c | 811 ++++++++++++++++++--------------- 1 file changed, 440 insertions(+), 371 deletions(-) diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index a379a3588dd0f..052872022107a 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -14,8 +14,8 @@ */ #include +#include #include -#include #include #include #include @@ -30,19 +30,37 @@ #include +#if defined(CONFIG_ARCH_MSM7X30) +#define MSM_NAND_BASE 0xA0200000 +#else +#define MSM_NAND_BASE 0xA0A00000 +#endif + #include "msm_nand.h" #define MSM_NAND_DMA_BUFFER_SIZE SZ_4K #define MSM_NAND_DMA_BUFFER_SLOTS \ (MSM_NAND_DMA_BUFFER_SIZE / (sizeof(((atomic_t *)0)->counter) * 8)) -#define SUPPORT_WRONG_ECC_CONFIG 1 - #define MSM_NAND_CFG0_RAW 0xA80420C0 #define MSM_NAND_CFG1_RAW 0x5045D +#define SUPPORT_WRONG_ECC_CONFIG 1 +#define IGNORE_ARM9_CONFIG 0 #define VERBOSE 0 +static struct nand_hw_info *nand_info; +struct nand_hw_info { + uint32_t flash_id; + uint8_t maker_id; + uint8_t maker_name[10]; + uint8_t width; + uint32_t size; + uint32_t block_count; + uint32_t page_count; + uint32_t page_size; +}; + struct msm_nand_chip { struct device *dev; wait_queue_head_t wait_queue; @@ -51,26 +69,15 @@ struct msm_nand_chip { uint8_t *dma_buffer; dma_addr_t dma_addr; unsigned CFG0, CFG1; - unsigned page_shift; - unsigned last_sector; - unsigned last_sectorsz; #if SUPPORT_WRONG_ECC_CONFIG uint32_t ecc_buf_cfg; uint32_t saved_ecc_buf_cfg; #endif + struct nand_hw_info dev_info; }; #define CFG1_WIDE_FLASH (1U << 1) -#ifdef CONFIG_ARCH_MSM7X30 -#define BUF_STAT_UNCORRECTABLE (1U << 8) -#define BUF_STAT_NUM_ERRS_MASK (0xf) -#else -#define BUF_STAT_UNCORRECTABLE (1U << 3) -#define BUF_STAT_NUM_ERRS_MASK (0x7) -#endif - - /* TODO: move datamover code out */ #define SRC_CRCI_NAND_CMD CMD_SRC_CRCI(DMOV_NAND_CRCI_CMD) @@ -83,25 +90,80 @@ struct msm_nand_chip { ((uint8_t *)(vaddr) - (chip)->dma_buffer)) /** - * msm_nand_oob_64 - oob info for large (2KB) page + * msm_nand_oob_64 - oob info for 2KB page */ static struct nand_ecclayout msm_nand_oob_64 = { + .eccbytes = 40, + .eccpos = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + }, .oobavail = 16, .oobfree = { {30, 16}, } }; -/* +/** * msm_nand_oob_128 - oob info for 4KB page */ static struct nand_ecclayout msm_nand_oob_128 = { - .oobavail = 32, - .oobfree = { + .eccbytes = 80, + .eccpos = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + }, + .oobavail = 32, + .oobfree = { {70, 32}, } }; +struct flash_identification { + uint32_t flash_id; + uint32_t mask; + uint32_t density; + uint32_t widebus; + uint32_t pagesize; + uint32_t blksize; + uint32_t oobsize; +}; + +static struct flash_identification supported_flash[] = +{ + /* Flash ID ID Mask Density(MB) Wid Pgsz Blksz oobsz Manuf */ + {0x00000000, 0xFFFFFFFF, 0, 0, 0, 0, 0, }, /*ONFI*/ + {0x1500aaec, 0xFF00FFFF, (256<<20), 0, 2048, (2048<<6), 64, }, /*Samsung 2Gbit*/ + {0x5500baec, 0xFF00FFFF, (256<<20), 1, 2048, (2048<<6), 64, }, /*Samsung 2Gbit*/ + {0x6600bcec, 0xFF00FFFF, (512<<20), 1, 4096, (4096<<6), 128,}, /*Samsung 4Gbit 4K Page*/ + {0x0000aaec, 0x0000FFFF, (256<<20), 1, 2048, (2048<<6), 64, }, /*Samsung 2Gbit*/ + {0x0000acec, 0x0000FFFF, (512<<20), 1, 2048, (2048<<6), 64, }, /*Samsung 4Gbit*/ + {0x0000bcec, 0x0000FFFF, (512<<20), 1, 2048, (2048<<6), 64, }, /*Samsung 4Gbit*/ + {0x6601b3ec, 0xFFFFFFFF, (1024<<20),1, 4096, (4096<<6), 128,}, /*Samsung 8Gbit 4Kpage*/ + {0x0000b3ec, 0x0000FFFF, (1024<<20),1, 2048, (2048<<6), 64, }, /*Samsung 8Gbit*/ + + {0x1500aa98, 0xFFFFFFFF, (256<<20), 0, 2048, (2048<<6), 64, }, /*Toshiba 2Gbit*/ + {0x5500ba98, 0xFFFFFFFF, (256<<20), 1, 2048, (2048<<6), 64, }, /*Toshiba 2Gbit*/ + + {0xd580b12c, 0xFFFFFFFF, (128<<20), 1, 2048, (2048<<6), 64, }, /*Micron 1Gbit*/ + {0x0000ba2c, 0x0000FFFF, (256<<20), 1, 2048, (2048<<6), 64, }, /*Micron 2Gbit*/ + {0x0000bc2c, 0x0000FFFF, (512<<20), 1, 2048, (2048<<6), 64, }, /*Micron 4Gbit*/ + {0x0000b32c, 0x0000FFFF, (1024<<20),1, 2048, (2048<<6), 64, }, /*Micron 8Gbit*/ + + {0x0000baad, 0x0000FFFF, (256<<20), 1, 2048, (2048<<6), 64, }, /*Hynix 2Gbit*/ + {0x0000bcad, 0x0000FFFF, (512<<20), 1, 2048, (2048<<6), 64, }, /*Hynix 4Gbit*/ + {0x0000b3ad, 0x0000FFFF, (1024<<20),1, 2048, (2048<<6), 64, }, /*Hynix 8Gbit*/ + /* Note: Width flag is 0 for 8 bit Flash and 1 for 16 bit flash */ + /* Note: The First row will be filled at runtime during ONFI probe */ +}; static void *msm_nand_get_dma_buffer(struct msm_nand_chip *chip, size_t size) { @@ -197,12 +259,17 @@ uint32_t flash_read_id(struct msm_nand_chip *chip) dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; + dsb(); msm_dmov_exec_cmd( chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + dsb(); + pr_info("status: %x\n", dma_buffer->data[3]); + pr_info("nandid: %x maker %02x device %02x\n", + dma_buffer->data[4], dma_buffer->data[4] & 0xff, + (dma_buffer->data[4] >> 8) & 0xff); rv = dma_buffer->data[4]; - pr_info("msn_nand: nandid %x status %x\n", rv, dma_buffer->data[3]); msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); return rv; } @@ -236,16 +303,15 @@ int flash_read_config(struct msm_nand_chip *chip) dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; + dsb(); msm_dmov_exec_cmd( chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + dsb(); chip->CFG0 = dma_buffer->cfg0; chip->CFG1 = dma_buffer->cfg1; - pr_info("msm_nand: read CFG0 = %x CFG1 = %x\n", chip->CFG0, chip->CFG1); - pr_info("msm_nand: CFG0 cw/page=%d ud_sz=%d ecc_sz=%d spare_sz=%d\n", - (chip->CFG0 >> 6) & 7, (chip->CFG0 >> 9) & 0x3ff, - (chip->CFG0 >> 19) & 15, (chip->CFG0 >> 23) & 15); + pr_info("msm_nand: read CFG0 = %x, CFG1 = %x\n", chip->CFG0, chip->CFG1); msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); @@ -277,9 +343,11 @@ unsigned flash_rd_reg(struct msm_nand_chip *chip, unsigned addr) (msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP; dma_buffer->data = 0xeeeeeeee; + dsb(); msm_dmov_exec_cmd( chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + dsb(); rv = dma_buffer->data; msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); @@ -308,9 +376,11 @@ void flash_wr_reg(struct msm_nand_chip *chip, unsigned addr, unsigned val) (msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP; dma_buffer->data = val; + dsb(); msm_dmov_exec_cmd( chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + dsb(); msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); } @@ -331,44 +401,6 @@ msm_nand_dma_map(struct device *dev, void *addr, size_t size, return dma_map_page(dev, page, offset, size, dir); } -static int msm_nand_check_empty(struct mtd_info *mtd, struct mtd_oob_ops *ops, - unsigned long *uncorrected) -{ - unsigned int p, n, end; - uint8_t *datbuf = ops->datbuf; - uint8_t *oobbuf = ops->oobbuf; - size_t oobsize; - int page_count; - - if (ops->mode == MTD_OOB_RAW) - return false; - - page_count = ops->retlen / mtd->writesize; - oobsize = (ops->mode == MTD_OOB_AUTO) ? mtd->oobavail : mtd->oobsize; - - for_each_set_bit(p, uncorrected, page_count) { - if (datbuf) { - datbuf = ops->datbuf + p * mtd->writesize; - for (n = 0; n < mtd->writesize; n++) { - /* empty blocks read 0x54 at these offsets */ - if (datbuf[n] != ((n % 516 == 3) ? 0x54 : 0xff)) - return false; - } - } - if (oobbuf) { - n = p * oobsize; - end = min(n + oobsize, ops->oobretlen); - for(; n < end; n++) - if (oobbuf[n] != 0xff) - return false; - } - if (ops->datbuf) - for (n = 3; n < mtd->writesize; n+= 516) - datbuf[n] = 0xff; - } - return true; -} - static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { @@ -397,11 +429,11 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, } *dma_buffer; dmov_s *cmd; unsigned n; - unsigned page = from >> chip->page_shift; - uint32_t oob_len = ops->ooblen; + unsigned page = 0; + uint32_t oob_len; uint32_t sectordatasize; uint32_t sectoroobsize; - int err, pageerr; + int err, pageerr, rawerr; dma_addr_t data_dma_addr = 0; dma_addr_t oob_dma_addr = 0; dma_addr_t data_dma_addr_curr = 0; @@ -410,51 +442,46 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, unsigned page_count; unsigned pages_read = 0; unsigned start_sector = 0; - uint32_t sector_corrected; - uint32_t page_corrected; - uint32_t total_corrected = 0; - uint32_t total_uncorrected = 0; - unsigned long uncorrected_noalloc = 0; - unsigned long *uncorrected = &uncorrected_noalloc; + uint32_t ecc_errors; + uint32_t total_ecc_errors = 0; + unsigned cwperpage; + + if (mtd->writesize == 2048) + page = from >> 11; + + if (mtd->writesize == 4096) + page = from >> 12; + + oob_len = ops->ooblen; + cwperpage = (mtd->writesize >> 9); if (from & (mtd->writesize - 1)) { pr_err("%s: unsupported from, 0x%llx\n", __func__, from); return -EINVAL; } - if (ops->mode != MTD_OOB_RAW) { - if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { - /* when ops->datbuf is NULL, ops->len can be ooblen */ - pr_err("%s: unsupported ops->len, %d\n", - __func__, ops->len); - return -EINVAL; - } - } else { - if (ops->datbuf != NULL && - (ops->len % (mtd->writesize + mtd->oobsize)) != 0) { - pr_err("%s: unsupported ops->len," - " %d for MTD_OOB_RAW\n", __func__, ops->len); - return -EINVAL; - } + if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { + /* when ops->datbuf is NULL, ops->len may refer to ooblen */ + pr_err("%s: unsupported ops->len, %d\n", + __func__, ops->len); + return -EINVAL; } - - if (ops->mode != MTD_OOB_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { + if (ops->ooblen != 0 && ops->ooboffs != 0) { pr_err("%s: unsupported ops->ooboffs, %d\n", __func__, ops->ooboffs); return -EINVAL; } if (ops->oobbuf && !ops->datbuf && ops->mode == MTD_OOB_AUTO) - start_sector = chip->last_sector; + start_sector = cwperpage - 1; if (ops->oobbuf && !ops->datbuf) { - unsigned tmpoobsz = (ops->mode == MTD_OOB_AUTO) ? - mtd->oobavail : mtd->oobsize; - page_count = DIV_ROUND_UP(ops->ooblen, tmpoobsz); - } else if (ops->mode != MTD_OOB_RAW) + page_count = ops->ooblen / ((ops->mode == MTD_OOB_AUTO) ? + mtd->oobavail : mtd->oobsize); + if ((page_count == 0) && (ops->ooblen)) + page_count = 1; + } else page_count = ops->len / mtd->writesize; - else - page_count = ops->len / (mtd->writesize + mtd->oobsize); #if 0 /* yaffs reads more oob data than it needs */ if (ops->ooblen >= sectoroobsize * 4) { @@ -472,7 +499,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, /* memset(ops->datbuf, 0x55, ops->len); */ data_dma_addr_curr = data_dma_addr = msm_nand_dma_map(chip->dev, ops->datbuf, ops->len, - DMA_FROM_DEVICE); + DMA_FROM_DEVICE); if (dma_mapping_error(chip->dev, data_dma_addr)) { pr_err("msm_nand_read_oob: failed to get dma addr " "for %p\n", ops->datbuf); @@ -483,7 +510,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, memset(ops->oobbuf, 0xff, ops->ooblen); oob_dma_addr_curr = oob_dma_addr = msm_nand_dma_map(chip->dev, ops->oobbuf, - ops->ooblen, DMA_BIDIRECTIONAL); + ops->ooblen, DMA_BIDIRECTIONAL); if (dma_mapping_error(chip->dev, oob_dma_addr)) { pr_err("msm_nand_read_oob: failed to get dma addr " "for %p\n", ops->oobbuf); @@ -491,14 +518,6 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, goto err_dma_map_oobbuf_failed; } } - if (BITS_TO_LONGS(page_count) > 1) { - uncorrected = kzalloc(BITS_TO_LONGS(page_count) * sizeof(long), - GFP_NOIO); - if (!uncorrected) { - err = -ENOMEM; - goto err_alloc_uncorrected_failed; - } - } wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer( @@ -513,35 +532,24 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, cmd = dma_buffer->cmd; /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ - if (ops->mode != MTD_OOB_RAW) { - dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ_ECC; - dma_buffer->data.cfg0 = - (chip->CFG0 & ~(7U << 6)) | - ((chip->last_sector - start_sector) << 6); - dma_buffer->data.cfg1 = chip->CFG1; - } else { - dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; - dma_buffer->data.cfg0 = - (MSM_NAND_CFG0_RAW & ~(7U << 6)) | - (chip->last_sector << 6); - dma_buffer->data.cfg1 = MSM_NAND_CFG1_RAW | - (chip->CFG1 & CFG1_WIDE_FLASH); - } - + dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ_ECC; dma_buffer->data.addr0 = (page << 16) | oob_col; /* qc example is (page >> 16) && 0xff !? */ dma_buffer->data.addr1 = (page >> 16) & 0xff; /* flash0 + undoc bit */ dma_buffer->data.chipsel = 0 | 4; + dma_buffer->data.cfg0 = + (chip->CFG0 & ~(7U << 6)) + | (((cwperpage-1) - start_sector) << 6); + dma_buffer->data.cfg1 = chip->CFG1; /* GO bit for the EXEC register */ dma_buffer->data.exec = 1; - BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.result)); - for (n = start_sector; n <= chip->last_sector; n++) { + for (n = start_sector; n < cwperpage; n++) { /* flash + buffer status return words */ dma_buffer->data.result[n].flash_status = 0xeeeeeeee; dma_buffer->data.result[n].buffer_status = 0xeeeeeeee; @@ -604,13 +612,8 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, * (only valid if status says success) */ if (ops->datbuf) { - if (ops->mode != MTD_OOB_RAW) - sectordatasize = - (n < chip->last_sector) ? - 516 : chip->last_sectorsz; - else - sectordatasize = 528; - + sectordatasize = (n < (cwperpage - 1)) + ? 516 : (512 - ((cwperpage - 1) << 2)); cmd->cmd = 0; cmd->src = MSM_NAND_FLASH_BUFFER; cmd->dst = data_dma_addr_curr; @@ -619,14 +622,13 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, cmd++; } - if (ops->oobbuf && (n == chip->last_sector || - ops->mode != MTD_OOB_AUTO)) { + if (ops->oobbuf && (n == (cwperpage - 1) + || ops->mode != MTD_OOB_AUTO)) { cmd->cmd = 0; - if (n == chip->last_sector) { + if (n == (cwperpage - 1)) { cmd->src = MSM_NAND_FLASH_BUFFER + - chip->last_sectorsz; - sectoroobsize = - (chip->last_sector + 1) * 4; + (512 - ((cwperpage - 1) << 2)); + sectoroobsize = (cwperpage << 2); if (ops->mode != MTD_OOB_AUTO) sectoroobsize += 10; } else { @@ -667,61 +669,109 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; + dsb(); msm_dmov_exec_cmd( chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR( msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + dsb(); /* if any of the writes failed (0x10), or there * was a protection violation (0x100), we lose */ - pageerr = 0; - page_corrected = 0; - for (n = start_sector; n <= chip->last_sector; n++) { - uint32_t buf_stat = - dma_buffer->data.result[n].buffer_status; - if (buf_stat & BUF_STAT_UNCORRECTABLE) { - total_uncorrected++; - uncorrected[BIT_WORD(pages_read)] |= - BIT_MASK(pages_read); - pageerr = -EBADMSG; - break; - } + pageerr = rawerr = 0; + for (n = start_sector; n < cwperpage; n++) { if (dma_buffer->data.result[n].flash_status & 0x110) { - pageerr = -EIO; + rawerr = -EIO; break; } - sector_corrected =buf_stat & BUF_STAT_NUM_ERRS_MASK; - page_corrected += sector_corrected; - if (sector_corrected > 1) - pageerr = -EUCLEAN; } - if ((!pageerr && page_corrected) || pageerr == -EUCLEAN) { - total_corrected += page_corrected; - /* not thread safe */ - mtd->ecc_stats.corrected += page_corrected; + if (rawerr) { + if (ops->datbuf) { + uint8_t *datbuf = ops->datbuf + + pages_read * mtd->writesize; + + dma_sync_single_for_cpu(chip->dev, + data_dma_addr_curr-mtd->writesize, + mtd->writesize, DMA_BIDIRECTIONAL); + + for (n = 0; n < mtd->writesize; n++) { + /* empty blocks read 0x54 at + * these offsets + */ + if (n % 516 == 3 && datbuf[n] == 0x54) + datbuf[n] = 0xff; + if (datbuf[n] != 0xff) { + pageerr = rawerr; + break; + } + } + + dma_sync_single_for_device(chip->dev, + data_dma_addr_curr-mtd->writesize, + mtd->writesize, DMA_BIDIRECTIONAL); + + } + if (ops->oobbuf) { + for (n = 0; n < ops->ooblen; n++) { + if (ops->oobbuf[n] != 0xff) { + pageerr = rawerr; + break; + } + } + } + } + if (pageerr) { + for (n = start_sector; n < cwperpage; n++) { + if (dma_buffer->data.result[n].buffer_status & + 0x8) { + /* not thread safe */ + mtd->ecc_stats.failed++; + pageerr = -EBADMSG; + break; + } + } + } + if (!rawerr) { /* check for corretable errors */ + for (n = start_sector; n < cwperpage; n++) { + ecc_errors = dma_buffer->data. + result[n].buffer_status & 0x7; + if (ecc_errors) { + total_ecc_errors += ecc_errors; + /* not thread safe */ + mtd->ecc_stats.corrected += ecc_errors; + if (ecc_errors > 1) + pageerr = -EUCLEAN; + } + } } if (pageerr && (pageerr != -EUCLEAN || err == 0)) err = pageerr; #if VERBOSE - pr_info("status: %x %x %x %x %x %x %x %x " - "%x %x %x %x %x %x %x %x\n", - dma_buffer->data.result[0].flash_status, - dma_buffer->data.result[0].buffer_status, - dma_buffer->data.result[1].flash_status, - dma_buffer->data.result[1].buffer_status, - dma_buffer->data.result[2].flash_status, - dma_buffer->data.result[2].buffer_status, - dma_buffer->data.result[3].flash_status, - dma_buffer->data.result[3].buffer_status, - dma_buffer->data.result[4].flash_status, - dma_buffer->data.result[4].buffer_status, - dma_buffer->data.result[5].flash_status, - dma_buffer->data.result[5].buffer_status, - dma_buffer->data.result[6].flash_status, - dma_buffer->data.result[6].buffer_status, - dma_buffer->data.result[7].flash_status, - dma_buffer->data.result[7].buffer_status); + if (rawerr && !pageerr) { + pr_err("msm_nand_read_oob %llx %x %x empty page\n", + (loff_t)page * mtd->writesize, ops->len, + ops->ooblen); + } else { + pr_info("status: %x %x %x %x %x %x %x %x %x \ + %x %x %x %x %x %x %x \n", + dma_buffer->data.result[0].flash_status, + dma_buffer->data.result[0].buffer_status, + dma_buffer->data.result[1].flash_status, + dma_buffer->data.result[1].buffer_status, + dma_buffer->data.result[2].flash_status, + dma_buffer->data.result[2].buffer_status, + dma_buffer->data.result[3].flash_status, + dma_buffer->data.result[3].buffer_status); + dma_buffer->data.result[4].buffer_status); + dma_buffer->data.result[4].buffer_status); + dma_buffer->data.result[5].buffer_status); + dma_buffer->data.result[5].buffer_status); + dma_buffer->data.result[6].buffer_status); + dma_buffer->data.result[6].buffer_status); + dma_buffer->data.result[7].buffer_status); + dma_buffer->data.result[7].buffer_status); + } #endif if (err && err != -EUCLEAN && err != -EBADMSG) break; @@ -730,35 +780,22 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, } msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); -err_alloc_uncorrected_failed: if (ops->oobbuf) { dma_unmap_page(chip->dev, oob_dma_addr, - ops->ooblen, DMA_FROM_DEVICE); + ops->ooblen, DMA_FROM_DEVICE); } err_dma_map_oobbuf_failed: if (ops->datbuf) { dma_unmap_page(chip->dev, data_dma_addr, - ops->len, DMA_FROM_DEVICE); + ops->len, DMA_FROM_DEVICE); } - if (ops->mode != MTD_OOB_RAW) - ops->retlen = mtd->writesize * pages_read; - else - ops->retlen = (mtd->writesize + mtd->oobsize) * - pages_read; + ops->retlen = mtd->writesize * pages_read; ops->oobretlen = ops->ooblen - oob_len; - - if (err == -EBADMSG && msm_nand_check_empty(mtd, ops, uncorrected)) - err = 0; - else if (total_uncorrected) - mtd->ecc_stats.failed += total_uncorrected; /* not threadsafe */ - if (uncorrected != &uncorrected_noalloc) - kfree(uncorrected); - if (err) pr_err("msm_nand_read_oob %llx %x %x failed %d, corrected %d\n", from, ops->datbuf ? ops->len : 0, ops->ooblen, err, - total_corrected); + total_ecc_errors); return err; } @@ -807,8 +844,8 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) } *dma_buffer; dmov_s *cmd; unsigned n; - unsigned page = to >> chip->page_shift; - uint32_t oob_len = ops->ooblen; + unsigned page = 0; + uint32_t oob_len; uint32_t sectordatawritesize; int err; dma_addr_t data_dma_addr = 0; @@ -817,36 +854,36 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) dma_addr_t oob_dma_addr_curr = 0; unsigned page_count; unsigned pages_written = 0; + unsigned cwperpage; + + if (mtd->writesize == 2048) + page = to >> 11; + + if (mtd->writesize == 4096) + page = to >> 12; + + oob_len = ops->ooblen; + cwperpage = (mtd->writesize >> 9); if (to & (mtd->writesize - 1)) { pr_err("%s: unsupported to, 0x%llx\n", __func__, to); return -EINVAL; } - - if (ops->mode != MTD_OOB_RAW) { - if (ops->ooblen != 0 && ops->mode != MTD_OOB_AUTO) { - pr_err("%s: unsupported ops->mode,%d\n", - __func__, ops->mode); - return -EINVAL; - } - if ((ops->len % mtd->writesize) != 0) { - pr_err("%s: unsupported ops->len, %d\n", - __func__, ops->len); - return -EINVAL; - } - } else { - if ((ops->len % (mtd->writesize + mtd->oobsize)) != 0) { - pr_err("%s: unsupported ops->len, " - "%d for MTD_OOB_RAW mode\n", - __func__, ops->len); - return -EINVAL; - } + if (ops->ooblen != 0 && ops->mode != MTD_OOB_AUTO) { + pr_err("%s: unsupported ops->mode, %d\n", + __func__, ops->mode); + return -EINVAL; } if (ops->datbuf == NULL) { pr_err("%s: unsupported ops->datbuf == NULL\n", __func__); return -EINVAL; } + if ((ops->len % mtd->writesize) != 0) { + pr_err("%s: unsupported ops->len, %d\n", + __func__, ops->len); + return -EINVAL; + } #if 0 /* yaffs writes more oob data than it needs */ if (ops->ooblen >= sectoroobsize * 4) { pr_err("%s: unsupported ops->ooblen, %d\n", @@ -854,7 +891,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) return -EINVAL; } #endif - if (ops->mode != MTD_OOB_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { + if (ops->ooblen != 0 && ops->ooboffs != 0) { pr_err("%s: unsupported ops->ooboffs, %d\n", __func__, ops->ooboffs); return -EINVAL; @@ -863,7 +900,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) if (ops->datbuf) { data_dma_addr_curr = data_dma_addr = msm_nand_dma_map(chip->dev, ops->datbuf, - ops->len, DMA_TO_DEVICE); + ops->len, DMA_TO_DEVICE); if (dma_mapping_error(chip->dev, data_dma_addr)) { pr_err("msm_nand_write_oob: failed to get dma addr " "for %p\n", ops->datbuf); @@ -873,7 +910,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) if (ops->oobbuf) { oob_dma_addr_curr = oob_dma_addr = msm_nand_dma_map(chip->dev, ops->oobbuf, - ops->ooblen, DMA_TO_DEVICE); + ops->ooblen, DMA_TO_DEVICE); if (dma_mapping_error(chip->dev, oob_dma_addr)) { pr_err("msm_nand_write_oob: failed to get dma addr " "for %p\n", ops->oobbuf); @@ -881,10 +918,8 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) goto err_dma_map_oobbuf_failed; } } - if (ops->mode != MTD_OOB_RAW) - page_count = ops->len / mtd->writesize; - else - page_count = ops->len / (mtd->writesize + mtd->oobsize); + + page_count = ops->len / mtd->writesize; wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer(chip, sizeof(*dma_buffer)))); @@ -892,31 +927,22 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) while (page_count-- > 0) { cmd = dma_buffer->cmd; - /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ - if (ops->mode != MTD_OOB_RAW) { - dma_buffer->data.cfg0 = chip->CFG0; - dma_buffer->data.cfg1 = chip->CFG1; - } else { - dma_buffer->data.cfg0 = - (MSM_NAND_CFG0_RAW & ~(7U << 6)) | - (chip->last_sector << 6); - dma_buffer->data.cfg1 = MSM_NAND_CFG1_RAW | - (chip->CFG1 & CFG1_WIDE_FLASH); - } - + /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ dma_buffer->data.cmd = MSM_NAND_CMD_PRG_PAGE; dma_buffer->data.addr0 = page << 16; dma_buffer->data.addr1 = (page >> 16) & 0xff; dma_buffer->data.chipsel = 0 | 4; /* flash0 + undoc bit */ dma_buffer->data.zeroes = 0; + dma_buffer->data.cfg0 = chip->CFG0; + dma_buffer->data.cfg1 = chip->CFG1; /* GO bit for the EXEC register */ dma_buffer->data.exec = 1; BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.flash_status)); - for (n = 0; n <= chip->last_sector ; n++) { + for (n = 0; n < cwperpage ; n++) { /* status return words */ dma_buffer->data.flash_status[n] = 0xeeeeeeee; /* block on cmd ready, then @@ -954,13 +980,9 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) #endif } - /* write data block */ - if (ops->mode != MTD_OOB_RAW) - sectordatawritesize = (n < chip->last_sector) ? - 516 : chip->last_sectorsz; - else - sectordatawritesize = 528; - + /* write data block */ + sectordatawritesize = (n < (cwperpage - 1)) ? + 516 : (512 - ((cwperpage - 1) << 2)); cmd->cmd = 0; cmd->src = data_dma_addr_curr; data_dma_addr_curr += sectordatawritesize; @@ -969,13 +991,14 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) cmd++; if (ops->oobbuf) { - if (n == chip->last_sector) { + if (n == (cwperpage - 1)) { cmd->cmd = 0; cmd->src = oob_dma_addr_curr; cmd->dst = MSM_NAND_FLASH_BUFFER + - chip->last_sectorsz; - cmd->len = 516 - chip->last_sectorsz; - if (oob_len <= cmd->len) + (512 - ((cwperpage - 1) << 2)); + if ((cwperpage << 2) < oob_len) + cmd->len = (cwperpage << 2); + else cmd->len = oob_len; oob_dma_addr_curr += cmd->len; oob_len -= cmd->len; @@ -1041,16 +1064,18 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; + dsb(); msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR( msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + dsb(); /* if any of the writes failed (0x10), or there was a * protection violation (0x100), or the program success * bit (0x80) is unset, we lose */ err = 0; - for (n = 0; n <= chip->last_sector ; n++) { + for (n = 0; n < cwperpage; n++) { if (dma_buffer->data.flash_status[n] & 0x110) { if (dma_buffer->data.flash_status[n] & 0x10) pr_err("msm_nand: critical write error," @@ -1065,8 +1090,8 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) } #if VERBOSE - pr_info("write page %d: status: %x %x %x %x %x %x %x %x\n", - page, dma_buffer->data.flash_status[0], + pr_info("write page %d: status: %x %x %x %x %x %x %x %x\n", page, + dma_buffer->data.flash_status[0], dma_buffer->data.flash_status[1], dma_buffer->data.flash_status[2], dma_buffer->data.flash_status[3], @@ -1080,18 +1105,14 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) pages_written++; page++; } - if (ops->mode != MTD_OOB_RAW) - ops->retlen = mtd->writesize * pages_written; - else - ops->retlen = (mtd->writesize + mtd->oobsize) * pages_written; - + ops->retlen = mtd->writesize * pages_written; ops->oobretlen = ops->ooblen - oob_len; msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); if (ops->oobbuf) dma_unmap_page(chip->dev, oob_dma_addr, - ops->ooblen, DMA_TO_DEVICE); + ops->ooblen, DMA_TO_DEVICE); err_dma_map_oobbuf_failed: if (ops->datbuf) dma_unmap_page(chip->dev, data_dma_addr, @@ -1129,7 +1150,13 @@ msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) unsigned cmdptr; unsigned data[9]; } *dma_buffer; - unsigned page = instr->addr >> chip->page_shift; + unsigned page = 0; + + if (mtd->writesize == 2048) + page = instr->addr >> 11; + + if (mtd->writesize == 4096) + page = instr->addr >> 12; if (instr->addr & (mtd->erasesize - 1)) { pr_err("%s: unsupported erase address, 0x%llx\n", @@ -1185,13 +1212,14 @@ msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) dma_buffer->cmd[4].len = 4; BUILD_BUG_ON(4 != ARRAY_SIZE(dma_buffer->cmd) - 1); - dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; + dsb(); msm_dmov_exec_cmd( chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + dsb(); /* we fail if there was an operation error, a mpu error, or the * erase success bit was not set. @@ -1243,7 +1271,16 @@ msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) } *dma_buffer; dmov_s *cmd; uint8_t *buf; - unsigned page = ofs >> chip->page_shift; + unsigned page = 0; + unsigned cwperpage; + + if (mtd->writesize == 2048) + page = ofs >> 11; + + if (mtd->writesize == 4096) + page = ofs >> 12; + + cwperpage = (mtd->writesize >> 9); /* Check for invalid offset */ if (ofs > mtd->size) @@ -1271,10 +1308,10 @@ msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) if (chip->CFG1 & CFG1_WIDE_FLASH) dma_buffer->data.addr0 = (page << 16) | - ((528 * chip->last_sector) >> 1); + ((528*(cwperpage-1)) >> 1); else dma_buffer->data.addr0 = (page << 16) | - (528 * chip->last_sector); + (528*(cwperpage-1)); dma_buffer->data.addr1 = (page >> 16) & 0xff; dma_buffer->data.chipsel = 0 | 4; @@ -1309,8 +1346,7 @@ msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) cmd++; cmd->cmd = 0; - cmd->src = MSM_NAND_FLASH_BUFFER + - (mtd->writesize - 528 * chip->last_sector); + cmd->src = MSM_NAND_FLASH_BUFFER + (mtd->writesize - (528*(cwperpage-1))); cmd->dst = msm_virt_to_dma(chip, buf); cmd->len = 4; cmd++; @@ -1323,8 +1359,10 @@ msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; + dsb(); msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + dsb(); ret = 0; if (dma_buffer->data.result.flash_status & 0x110) @@ -1349,36 +1387,18 @@ msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) static int msm_nand_block_markbad(struct mtd_info *mtd, loff_t ofs) { - struct mtd_oob_ops ops; + /* struct msm_nand_chip *this = mtd->priv; */ int ret; - uint8_t *buf; - /* Check for invalid offset */ - if (ofs > mtd->size) - return -EINVAL; - if (ofs & (mtd->erasesize - 1)) { - pr_err("%s: unsupported block address, 0x%x\n", - __func__, (uint32_t)ofs); - return -EINVAL; + ret = msm_nand_block_isbad(mtd, ofs); + if (ret) { + /* If it was bad already, return success and do nothing */ + if (ret > 0) + return 0; + return ret; } - /* - Write all 0s to the first page - This will set the BB marker to 0 - */ - - /* Use the already existing zero page */ - buf = page_address(ZERO_PAGE()); - - ops.mode = MTD_OOB_RAW; - ops.len = mtd->writesize + mtd->oobsize; - ops.retlen = 0; - ops.ooblen = 0; - ops.datbuf = buf; - ops.oobbuf = NULL; - ret = msm_nand_write_oob(mtd, ofs, &ops); - - return ret; + return -EIO; } /** @@ -1398,6 +1418,43 @@ static void msm_nand_resume(struct mtd_info *mtd) { } +/** + * Export 3 attributes for HTC SSD HW INFO tool + * >info :basic HW spec of this NAND chip + * >vendor :vendor information + * >pagesize:page size, either 2048 or 4096 + */ +static int param_get_vendor_name(char *buffer, struct kernel_param *kp) +{ + return sprintf(buffer, "%s", nand_info->maker_name); +} +module_param_call(vendor, NULL, param_get_vendor_name, NULL, S_IRUGO); + +static int param_get_nand_info(char *buffer, struct kernel_param *kp) +{ + int result = 0; + result += sprintf(buffer, "<< NAND INFO >>\n"); + result += sprintf(buffer + result, "flash id\t =%X\n", + nand_info->flash_id); + result += sprintf(buffer + result, "vendor\t\t =%s\n", + nand_info->maker_name); + result += sprintf(buffer + result, "width\t\t =%d bits\n", + nand_info->width); + result += sprintf(buffer + result, "size\t\t =%d MB\n", + nand_info->size>>20); + result += sprintf(buffer + result, "block count\t =%d\n", + nand_info->block_count); + result += sprintf(buffer + result, "page count\t =%d", + nand_info->page_count); + return result; +} +module_param_call(info, NULL, param_get_nand_info, NULL, S_IRUGO); + +static int param_get_page_size(char *buffer, struct kernel_param *kp) +{ + return sprintf(buffer, "%d", nand_info->page_size); +} +module_param_call(pagesize, NULL, param_get_page_size, NULL, S_IRUGO); /** * msm_nand_scan - [msm_nand Interface] Scan for the msm_nand device @@ -1413,115 +1470,99 @@ int msm_nand_scan(struct mtd_info *mtd, int maxchips) { unsigned n; struct msm_nand_chip *chip = mtd->priv; - uint32_t flash_id; - uint32_t manid; - uint32_t devid; - uint32_t devcfg; - uint32_t busw16; - struct nand_flash_dev *flashdev = NULL; - struct nand_manufacturers *flashman = NULL; - - if (flash_read_config(chip)) { - pr_err("ERRROR: could not save CFG0 & CFG1 state\n"); - return -ENODEV; - } - pr_info("msm_nand: NAND_READ_ID = %x\n", - flash_rd_reg(chip, MSM_NAND_READ_ID)); - flash_wr_reg(chip, MSM_NAND_READ_ID, 0x12345678); + uint32_t flash_id = 0, i = 1, mtd_writesize; + uint8_t dev_found = 0; + uint8_t wide_bus; + uint8_t index; + /* Read the Flash ID from the Nand Flash Device */ flash_id = flash_read_id(chip); - manid = flash_id & 0xff; - devid = (flash_id >> 8) & 0xff; - devcfg = (flash_id >> 24) & 0xff; - - for (n = 0; !flashman && nand_manuf_ids[n].id; ++n) - if (nand_manuf_ids[n].id == manid) - flashman = &nand_manuf_ids[n]; - for (n = 0; !flashdev && nand_flash_ids[n].id; ++n) - if (nand_flash_ids[n].id == devid) - flashdev = &nand_flash_ids[n]; - if (!flashdev || !flashman) { - pr_err("ERROR: unknown nand device manuf=%x devid=%x\n", - manid, devid); - return -ENOENT; + for (index = 1; index < ARRAY_SIZE(supported_flash); index++) { + if ((flash_id & supported_flash[index].mask) == + (supported_flash[index].flash_id & + (supported_flash[index].mask))) { + dev_found = 1; + break; + } } - if (!flashdev->pagesize) { - mtd->erasesize = (64 * 1024) << ((devcfg >> 4) & 0x3); - mtd->writesize = 1024 << (devcfg & 0x3); - mtd->oobsize = (8 << ((devcfg >> 2) & 1)) * - (mtd->writesize / 512); - busw16 = devcfg & (1 << 6) ? CFG1_WIDE_FLASH : 0; - } else { - mtd->writesize = flashdev->pagesize; - mtd->erasesize = flashdev->erasesize; - mtd->oobsize = flashdev->pagesize / 32; - busw16 = flashdev->options & NAND_BUSWIDTH_16 ? - CFG1_WIDE_FLASH : 0; - } - mtd->size = flashdev->chipsize << 20; - pr_info("msm_nand: manuf %s (0x%x) device 0x%x blocksz %x pagesz %x " - "size %llx\n", flashman->name, flashman->id, flashdev->id, - mtd->erasesize, mtd->writesize, mtd->size); - - if (mtd->writesize == 2048) { - chip->page_shift = 11; - } else if (mtd->writesize == 4096) { - chip->page_shift = 12; + if (dev_found) { + wide_bus = supported_flash[index].widebus; + mtd->size = supported_flash[index].density * i; + mtd->writesize = supported_flash[index].pagesize * i; + mtd->oobsize = supported_flash[index].oobsize * i; + mtd->erasesize = supported_flash[index].blksize * i; + mtd_writesize = mtd->writesize; + + pr_info("Found a supported NAND device\n"); + pr_info("NAND Id : 0x%X\n", supported_flash[index]. + flash_id); + pr_info("Buswidth : %d Bits \n", (wide_bus) ? 16 : 8); + pr_info("Density : %lld MByte\n", (mtd->size>>20)); + pr_info("Pagesize : %d Bytes\n", mtd->writesize); + pr_info("Erasesize: %d Bytes\n", mtd->erasesize); + pr_info("Oobsize : %d Bytes\n", mtd->oobsize); } else { - pr_err("%s: Unsupported page size (%d)\n", __func__, - mtd->writesize); - return -EINVAL; + pr_err("Unsupported Nand,Id: 0x%x \n", flash_id); + return -ENODEV; } - chip->last_sector = (mtd->writesize / 512) - 1; - chip->last_sectorsz = mtd->writesize - chip->last_sector * 516; - - if (mtd->oobsize == 64) { - mtd->ecclayout = &msm_nand_oob_64; - } else if (mtd->oobsize == 128) { - mtd->ecclayout = &msm_nand_oob_128; - } else { - pr_err("%s: Unsupported oob size (%d)\n", __func__, - mtd->oobsize); - return -EINVAL; + if (flash_read_config(chip)) { + pr_err("ERROR: could not save CFG0 & CFG1 state\n"); + return -ENODEV; } - mtd->oobavail = mtd->ecclayout->oobavail; - - chip->CFG0 = (chip->last_sector << 6) /* codewords per page */ + chip->CFG0 = (((mtd->writesize >> 9)-1) << 6) /* 4/8 cw/per page for 2/4k */ | (516 << 9) /* 516 user data bytes */ | (10 << 19) /* 10 parity bytes */ | (5 << 27) /* 5 address cycles */ | (1 << 30) /* Read status before data */ | (1 << 31) /* Send read cmd */ /* 0 spare bytes for 16 bit nand or 1 spare bytes for 8 bit */ - | ((busw16 & CFG1_WIDE_FLASH) ? (0 << 23) : (1 << 23)); - chip->CFG1 = (0 << 0) /* Enable ecc */ + | ((wide_bus) ? (0 << 23) : (1 << 23)); + chip->CFG1 = chip->CFG1 +#if IGNORE_ARM9_CONFIG + /* use ARM11 own setting on CFG1 */ | (7 << 2) /* 8 recovery cycles */ | (0 << 5) /* Allow CS deassertion */ - | ((mtd->writesize - (528 * chip->last_sector) + 1) << 6) - /* Bad block marker location */ - | (0 << 16) /* Bad block in user data area */ | (2 << 17) /* 6 cycle tWB/tRB */ - | (busw16 & CFG1_WIDE_FLASH); /* preserve wide flag */ - - pr_info("msm_nand: save CFG0 = %x CFG1 = %x\n", chip->CFG0, chip->CFG1); - pr_info("msm_nand: CFG0: cw/page=%d ud_sz=%d ecc_sz=%d spare_sz=%d " +#endif + | ((mtd->writesize - (528 * ((mtd->writesize >> 9) - 1)) + 1) + << 6) /* Bad block marker location */ + | (wide_bus << 1); /* Wide flash bit */ + chip->CFG1 = chip->CFG1 + & ~(1 << 0) /* Enable ecc */ + & ~(1 << 16); /* Bad block in user data area */ + + pr_info("CFG0 Init : 0x%08x \n", chip->CFG0); + pr_info("CFG1 Init : 0x%08x \n", chip->CFG1); + pr_info("CFG0: cw/page=%d ud_sz=%d ecc_sz=%d spare_sz=%d " "num_addr_cycles=%d\n", (chip->CFG0 >> 6) & 7, (chip->CFG0 >> 9) & 0x3ff, (chip->CFG0 >> 19) & 15, (chip->CFG0 >> 23) & 15, (chip->CFG0 >> 27) & 7); n = flash_rd_reg(chip, MSM_NAND_DEV_CMD1); - pr_info("msm_nand: DEV_CMD1: %x\n", n); + pr_info("DEV_CMD1: %x\n", n); n = flash_rd_reg(chip, MSM_NAND_EBI2_ECC_BUF_CFG); - pr_info("msm_nand: NAND_EBI2_ECC_BUF_CFG: %x\n", n); + pr_info(KERN_INFO "NAND_EBI2_ECC_BUF_CFG: %x\n", n); #if SUPPORT_WRONG_ECC_CONFIG chip->ecc_buf_cfg = 0x203; chip->saved_ecc_buf_cfg = n; #endif + if (mtd->oobsize == 64) { + mtd->oobavail = msm_nand_oob_64.oobavail; + mtd->ecclayout = &msm_nand_oob_64; + } else if (mtd->oobsize == 128) { + mtd->oobavail = msm_nand_oob_128.oobavail; + mtd->ecclayout = &msm_nand_oob_128; + } else { + pr_err("Unsupported Nand, oobsize: 0x%x \n", + mtd->oobsize); + return -ENODEV; + } + /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; @@ -1542,6 +1583,31 @@ int msm_nand_scan(struct mtd_info *mtd, int maxchips) mtd->block_markbad = msm_nand_block_markbad; mtd->owner = THIS_MODULE; + /* Information provides to HTC SSD HW Info tool */ + nand_info = &chip->dev_info; + nand_info->flash_id = flash_id; + nand_info->maker_id = (flash_id & 0xff); + switch (nand_info->maker_id) { + case 0xec: + strcpy(nand_info->maker_name, "Samsung"); + break; + case 0xad: + strcpy(nand_info->maker_name, "Hynix"); + break; + case 0x2c: + strcpy(nand_info->maker_name, "Micron"); + break; + default: + strcpy(nand_info->maker_name, "Unknown"); + break; + } + nand_info->width = (wide_bus? 16: 8); + nand_info->size = mtd->size; + nand_info->page_size = mtd->writesize; + nand_info->page_count = mtd->erasesize/mtd->writesize; + nand_info->block_count = mtd->size; + do_div(nand_info->block_count, nand_info->page_size * nand_info->page_count); + /* Unlock whole block */ /* msm_nand_unlock_all(mtd); */ @@ -1581,8 +1647,7 @@ static int __devinit msm_nand_probe(struct platform_device *pdev) { struct msm_nand_info *info; struct flash_platform_data *pdata = pdev->dev.platform_data; - int err; - int i; + int err, i; if (pdev->num_resources != 1) { pr_err("invalid num_resources"); @@ -1624,16 +1689,20 @@ static int __devinit msm_nand_probe(struct platform_device *pdev) } #ifdef CONFIG_MTD_PARTITIONS + /* Re-calculate the partition offset and size with correct page size */ + for (i = 0; i < pdata->nr_parts; i++) { + pdata->parts[i].offset = pdata->parts[i].offset + * info->mtd.erasesize; + pdata->parts[i].size = pdata->parts[i].size + * info->mtd.erasesize; + } + err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); if (err > 0) add_mtd_partitions(&info->mtd, info->parts, err); - else if (err <= 0 && pdata && pdata->parts) { - for (i = 0; i < pdata->nr_parts; ++i) { - pdata->parts[i].offset *= info->mtd.erasesize; - pdata->parts[i].size *= info->mtd.erasesize; - } + else if (err <= 0 && pdata && pdata->parts) add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); - } else + else #endif err = add_mtd_device(&info->mtd); From 57a9238053b03b19faa4cbd094da9cb8a2b84e75 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sat, 5 Feb 2011 10:19:25 -0500 Subject: [PATCH 1446/2556] msm: supersonic-mmc: updates board to include changes for the latest SQN driver from HTC --- arch/arm/mach-msm/board-supersonic-mmc.c | 255 ++++++++++++++++++++--- 1 file changed, 231 insertions(+), 24 deletions(-) diff --git a/arch/arm/mach-msm/board-supersonic-mmc.c b/arch/arm/mach-msm/board-supersonic-mmc.c index 348c8d9869511..0d9a656eda1b4 100644 --- a/arch/arm/mach-msm/board-supersonic-mmc.c +++ b/arch/arm/mach-msm/board-supersonic-mmc.c @@ -137,7 +137,7 @@ static struct msm_mmc_platform_data supersonic_sdslot_data = { .ocr_mask = SUPERSONIC_MMC_VDD, .status = supersonic_sdslot_status, .translate_vdd = supersonic_sdslot_switchvdd, -// .slot_type = &supersonic_sdslot_type, + .slot_type = &supersonic_sdslot_type, }; /* ---- WIFI ---- */ @@ -199,7 +199,7 @@ static unsigned int supersonic_wifi_status(struct device *dev) static struct msm_mmc_platform_data supersonic_wifi_data = { .ocr_mask = MMC_VDD_28_29, - .built_in = 1, + .built_in = 1, .status = supersonic_wifi_status, .register_status_notify = supersonic_wifi_status_register, .embedded_sdio = &supersonic_wifi_emb_data, @@ -282,8 +282,18 @@ static uint32_t wimax_off_gpio_table[] = { static void (*wimax_status_cb)(int card_present, void *dev_id); static void *wimax_status_cb_devid; -static int supersonic_wimax_cd = 0; -static int supersonic_wimax_sdio_status = 0; +static int mmc_wimax_cd = 0; +static int mmc_wimax_sdio_status = 0; +static int mmc_wimax_netlog_status = 0; +static int mmc_wimax_sdio_interrupt_log_status = 0; +static int mmc_wimax_netlog_withraw_status = 0; +static int mmc_wimax_cliam_host_status = 0; +static int mmc_wimax_busclk_pwrsave = 1; // Default is dynamic CLK OFF +static int mmc_wimax_CMD53_timeout_trigger_counter = 0; +static int mmc_wimax_hostwakeup_gpio = 40; // GPIO40 +static int mmc_wimax_thp_log_status = 0; +static int mmc_wimax_sdio_hw_reset = 0; // Rollback to default disabled HW RESET +static int mmc_wimax_packet_filter = 0; static int supersonic_wimax_status_register(void (*callback)(int card_present, void *dev_id), void *dev_id) { @@ -298,26 +308,29 @@ static int supersonic_wimax_status_register(void (*callback)(int card_present, v static unsigned int supersonic_wimax_status(struct device *dev) { printk("%s\n", __func__); - return supersonic_wimax_cd; + return mmc_wimax_cd; } -void supersonic_wimax_set_carddetect(int val) +void mmc_wimax_set_carddetect(int val) { printk("%s: %d\n", __func__, val); - supersonic_wimax_cd = val; + mmc_wimax_cd = val; if (wimax_status_cb) { wimax_status_cb(val, wimax_status_cb_devid); } else printk(KERN_WARNING "%s: Nobody to notify\n", __func__); } -EXPORT_SYMBOL(supersonic_wimax_set_carddetect); +EXPORT_SYMBOL(mmc_wimax_set_carddetect); + +static unsigned int supersonic_wimax_type = MMC_TYPE_SDIO_WIMAX; static struct msm_mmc_platform_data supersonic_wimax_data = { .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30, - .built_in = 1, + .built_in = 1, .status = supersonic_wimax_status, .register_status_notify = supersonic_wimax_status_register, .embedded_sdio = NULL, + .slot_type = &supersonic_wimax_type, }; struct _vreg @@ -338,18 +351,27 @@ XB : GPIO33 = 0 -> USB GPIO33 = 1 , GPIO160 = 0 -> CPU UART GPIO33 = 1 , GPIO160 = 1 -> Wimax UART */ -int supersonic_wimax_uart_switch(int uart) +int wimax_uart_switch = 0; +int mmc_wimax_uart_switch(int uart) { printk("%s uart:%d\n", __func__, uart); - + wimax_uart_switch = uart; + gpio_set_value(SUPERSONIC_USB_UARTz_SW, uart?1:0); if(system_rev && uart) gpio_set_value(SUPERSONIC_WIMAX_CPU_UARTz_SW, uart==2?1:0); return uart?1:0; } -EXPORT_SYMBOL(supersonic_wimax_uart_switch); +EXPORT_SYMBOL(mmc_wimax_uart_switch); + +int mmc_wimax_get_uart_switch(void) +{ + printk("%s uart:%d\n", __func__, wimax_uart_switch); + return wimax_uart_switch?1:0; +} +EXPORT_SYMBOL(mmc_wimax_get_uart_switch); -int supersonic_wimax_power(int on) +int mmc_wimax_power(int on) { printk("%s\n", __func__); @@ -357,10 +379,10 @@ int supersonic_wimax_power(int on) /*Power ON sequence*/ gpio_set_value(154, 1); gpio_set_value(48, 1); - mdelay(5); + mdelay(5); gpio_set_value(106, 0); - gpio_set_value(156, 1); - gpio_set_value(155, 1); + gpio_set_value(156, 1); + gpio_set_value(155, 1); mdelay(5); gpio_set_value(106, 1); mdelay(1150); @@ -386,22 +408,206 @@ int supersonic_wimax_power(int on) } return 0; } -EXPORT_SYMBOL(supersonic_wimax_power); +EXPORT_SYMBOL(mmc_wimax_power); + +int mmc_wimax_set_status(int on) +{ + printk(KERN_INFO "%s on:%d\n", __func__, on); + mmc_wimax_sdio_status = on; + return 0; +} +EXPORT_SYMBOL(mmc_wimax_set_status); + +int mmc_wimax_get_status(void) +{ + //printk(KERN_INFO "%s status:%d\n", __func__, mmc_wimax_sdio_status); + return mmc_wimax_sdio_status; +} +EXPORT_SYMBOL(mmc_wimax_get_status); + +int mmc_wimax_set_cliam_host_status(int on) +{ + printk(KERN_INFO "%s on:%d\n", __func__, on); + mmc_wimax_cliam_host_status = on; + return 0; +} +EXPORT_SYMBOL(mmc_wimax_set_cliam_host_status); -int supersonic_wimax_set_status(int on) +int mmc_wimax_get_cliam_host_status(void) +{ + //printk(KERN_INFO "%s status:%d\n", __func__, mmc_wimax_sdio_status); + return mmc_wimax_cliam_host_status; +} +EXPORT_SYMBOL(mmc_wimax_get_cliam_host_status); + +int mmc_wimax_set_netlog_status(int on) { printk(KERN_INFO "%s on:%d\n", __func__, on); - supersonic_wimax_sdio_status = on; + mmc_wimax_netlog_status = on; return 0; } -EXPORT_SYMBOL(supersonic_wimax_set_status); +EXPORT_SYMBOL(mmc_wimax_set_netlog_status); + +int mmc_wimax_get_netlog_status(void) +{ + //printk(KERN_INFO "%s status:%d\n", __func__, mmc_wimax_sdio_status); + return mmc_wimax_netlog_status; +} +EXPORT_SYMBOL(mmc_wimax_get_netlog_status); -int supersonic_wimax_get_status() +int mmc_wimax_set_netlog_withraw_status(int on) { - //printk(KERN_INFO "%s status:%d\n", __func__, supersonic_wimax_sdio_status); - return supersonic_wimax_sdio_status; + printk(KERN_INFO "%s on:%d\n", __func__, on); + mmc_wimax_netlog_withraw_status = on; + return 0; +} +EXPORT_SYMBOL(mmc_wimax_set_netlog_withraw_status); + +int mmc_wimax_get_netlog_withraw_status(void) +{ + //printk(KERN_INFO "%s status:%d\n", __func__, mmc_wimax_netlog_withraw_status); + return mmc_wimax_netlog_withraw_status; +} +EXPORT_SYMBOL(mmc_wimax_get_netlog_withraw_status); + +int mmc_wimax_set_sdio_interrupt_log(int on) +{ + printk(KERN_INFO "%s on:%d\n", __func__, on); + mmc_wimax_sdio_interrupt_log_status = on; + return 0; +} +EXPORT_SYMBOL(mmc_wimax_set_sdio_interrupt_log); + +int mmc_wimax_get_sdio_interrupt_log(void) +{ + //printk(KERN_INFO "%s status:%d\n", __func__, mmc_wimax_sdio_interrupt_log_status); + return mmc_wimax_sdio_interrupt_log_status; +} +EXPORT_SYMBOL(mmc_wimax_get_sdio_interrupt_log); + +int mmc_wimax_set_packet_filter(int on) +{ + printk(KERN_INFO "%s on:%d\n", __func__, on); + mmc_wimax_packet_filter = on; + return 0; +} +EXPORT_SYMBOL(mmc_wimax_set_packet_filter); + +int mmc_wimax_get_packet_filter(void) +{ + //printk(KERN_INFO "%s status:%d\n", __func__, mmc_wimax_packet_filter); + return mmc_wimax_packet_filter; +} +EXPORT_SYMBOL(mmc_wimax_get_packet_filter); + +int mmc_wimax_set_thp_log(int on) +{ + printk(KERN_INFO "%s on:%d\n", __func__, on); + mmc_wimax_thp_log_status = on; + return 0; +} +EXPORT_SYMBOL(mmc_wimax_set_thp_log); + +int mmc_wimax_get_thp_log(void) +{ + //printk(KERN_INFO "%s status:%d\n", __func__, mmc_wimax_thp_log_status); + return mmc_wimax_thp_log_status; +} +EXPORT_SYMBOL(mmc_wimax_get_thp_log); + +int mmc_wimax_set_busclk_pwrsave(int on) +{ + printk(KERN_INFO "%s on:%d\n", __func__, on); + mmc_wimax_busclk_pwrsave = on; + return 0; +} +EXPORT_SYMBOL(mmc_wimax_set_busclk_pwrsave); + +int mmc_wimax_get_busclk_pwrsave(void) +{ + //printk(KERN_INFO "%s status:%d\n", __func__, mmc_wimax_busclk_pwrsave); + return mmc_wimax_busclk_pwrsave; +} +EXPORT_SYMBOL(mmc_wimax_get_busclk_pwrsave); + +int mmc_wimax_set_sdio_hw_reset(int on) +{ + printk(KERN_INFO "%s on:%d\n", __func__, on); + mmc_wimax_sdio_hw_reset = on; + return 0; +} +EXPORT_SYMBOL(mmc_wimax_set_sdio_hw_reset); + +int mmc_wimax_get_sdio_hw_reset(void) +{ + //printk(KERN_INFO "%s status:%d\n", __func__, mmc_wimax_sdio_hw_reset); + return mmc_wimax_sdio_hw_reset; +} +EXPORT_SYMBOL(mmc_wimax_get_sdio_hw_reset); + +int mmc_wimax_set_CMD53_timeout_trigger_counter(int counter) +{ + printk(KERN_INFO "%s counter:%d\n", __func__, counter); + mmc_wimax_CMD53_timeout_trigger_counter = counter; + return 0; +} +EXPORT_SYMBOL(mmc_wimax_set_CMD53_timeout_trigger_counter); + +int mmc_wimax_get_CMD53_timeout_trigger_counter(void) +{ + //printk(KERN_INFO "%s counter:%d\n", __func__, mmc_wimax_CMD53_timeout_trigger_counter); + return mmc_wimax_CMD53_timeout_trigger_counter; +} +EXPORT_SYMBOL(mmc_wimax_get_CMD53_timeout_trigger_counter); + +int mmc_wimax_get_hostwakeup_gpio(void) +{ + return mmc_wimax_hostwakeup_gpio; +} +EXPORT_SYMBOL(mmc_wimax_get_hostwakeup_gpio); + +static int mmc_wimax_is_gpio_irq_enabled = 0; + +int mmc_wimax_set_gpio_irq_enabled(int on) +{ + printk(KERN_INFO "%s on:%d\n", __func__, on); + mmc_wimax_is_gpio_irq_enabled = on; + return 0; +} +EXPORT_SYMBOL(mmc_wimax_set_gpio_irq_enabled); + +int mmc_wimax_get_gpio_irq_enabled(void) +{ + return mmc_wimax_is_gpio_irq_enabled; +} +EXPORT_SYMBOL(mmc_wimax_get_gpio_irq_enabled); + +void mmc_wimax_enable_host_wakeup(int on) +{ + if (mmc_wimax_sdio_status) + { + if (on) { + if (!mmc_wimax_is_gpio_irq_enabled) { + printk("set GPIO%d as waketup source\n", mmc_wimax_get_hostwakeup_gpio()); + enable_irq(MSM_GPIO_TO_INT(mmc_wimax_get_hostwakeup_gpio())); + enable_irq_wake(MSM_GPIO_TO_INT(mmc_wimax_get_hostwakeup_gpio())); + mmc_wimax_is_gpio_irq_enabled = 1; + } + } + else { + if (mmc_wimax_is_gpio_irq_enabled) { + printk("disable GPIO%d wakeup source\n", mmc_wimax_get_hostwakeup_gpio()); + disable_irq_wake(MSM_GPIO_TO_INT(mmc_wimax_get_hostwakeup_gpio())); + disable_irq_nosync(MSM_GPIO_TO_INT(mmc_wimax_get_hostwakeup_gpio())); + mmc_wimax_is_gpio_irq_enabled = 0; + } + } + } + else { + printk("%s mmc_wimax_sdio_status is OFF\n", __func__); + } } -EXPORT_SYMBOL(supersonic_wimax_get_status); +EXPORT_SYMBOL(mmc_wimax_enable_host_wakeup); int __init supersonic_init_mmc(unsigned int sys_rev) { @@ -414,6 +620,7 @@ int __init supersonic_init_mmc(unsigned int sys_rev) /* initial WIFI_SHUTDOWN# */ id = PCOM_GPIO_CFG(SUPERSONIC_GPIO_WIFI_SHUTDOWN_N, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + gpio_set_value(SUPERSONIC_GPIO_WIFI_SHUTDOWN_N, 0); msm_add_sdcc(1, &supersonic_wifi_data, 0, 0); From 82f61ebf092a4aa096cd607bb2314c150641439a Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sat, 5 Feb 2011 13:02:43 -0500 Subject: [PATCH 1447/2556] msm: supersonic: clean up and fix card requests. add suspend and resume to sdio_func header --- arch/arm/mach-msm/board-supersonic-mmc.c | 2 -- drivers/net/wimax/SQN/sdio.c | 4 ++-- include/linux/mmc/sdio_func.h | 2 ++ 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-msm/board-supersonic-mmc.c b/arch/arm/mach-msm/board-supersonic-mmc.c index 0d9a656eda1b4..a5637b0be539b 100644 --- a/arch/arm/mach-msm/board-supersonic-mmc.c +++ b/arch/arm/mach-msm/board-supersonic-mmc.c @@ -199,7 +199,6 @@ static unsigned int supersonic_wifi_status(struct device *dev) static struct msm_mmc_platform_data supersonic_wifi_data = { .ocr_mask = MMC_VDD_28_29, - .built_in = 1, .status = supersonic_wifi_status, .register_status_notify = supersonic_wifi_status_register, .embedded_sdio = &supersonic_wifi_emb_data, @@ -326,7 +325,6 @@ static unsigned int supersonic_wimax_type = MMC_TYPE_SDIO_WIMAX; static struct msm_mmc_platform_data supersonic_wimax_data = { .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30, - .built_in = 1, .status = supersonic_wimax_status, .register_status_notify = supersonic_wimax_status_register, .embedded_sdio = NULL, diff --git a/drivers/net/wimax/SQN/sdio.c b/drivers/net/wimax/SQN/sdio.c index c6c1893d46f32..9fcf59a7028c0 100644 --- a/drivers/net/wimax/SQN/sdio.c +++ b/drivers/net/wimax/SQN/sdio.c @@ -1901,8 +1901,8 @@ static struct sdio_driver sqn_sdio_driver = { , .id_table = sqn_sdio_ids , .probe = sqn_sdio_probe , .remove = sqn_sdio_remove -// , .suspend = sqn_sdio_suspend -// , .resume = sqn_sdio_resume + , .suspend = sqn_sdio_suspend + , .resume = sqn_sdio_resume }; diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h index 557acae8cf95b..10c026ebc1f85 100755 --- a/include/linux/mmc/sdio_func.h +++ b/include/linux/mmc/sdio_func.h @@ -88,6 +88,8 @@ struct sdio_driver { int (*probe)(struct sdio_func *, const struct sdio_device_id *); void (*remove)(struct sdio_func *); + int (*suspend)(struct sdio_func *, pm_message_t state); + int (*resume)(struct sdio_func *); struct device_driver drv; }; From e09753acb40bc78bfc659c996c0048cce7829a2a Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 15 Feb 2011 19:28:31 -0500 Subject: [PATCH 1448/2556] Fix "drivers: mtd: msm_nand: adds 3 attributes for HTC SSD HW INFO tool and cleans up bit." --- drivers/mtd/devices/msm_nand.c | 738 +++++++++++++++++---------------- 1 file changed, 372 insertions(+), 366 deletions(-) diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c index 052872022107a..0e4c0bffc90c5 100644 --- a/drivers/mtd/devices/msm_nand.c +++ b/drivers/mtd/devices/msm_nand.c @@ -14,8 +14,8 @@ */ #include -#include #include +#include #include #include #include @@ -30,23 +30,17 @@ #include -#if defined(CONFIG_ARCH_MSM7X30) -#define MSM_NAND_BASE 0xA0200000 -#else -#define MSM_NAND_BASE 0xA0A00000 -#endif - #include "msm_nand.h" #define MSM_NAND_DMA_BUFFER_SIZE SZ_4K #define MSM_NAND_DMA_BUFFER_SLOTS \ (MSM_NAND_DMA_BUFFER_SIZE / (sizeof(((atomic_t *)0)->counter) * 8)) +#define SUPPORT_WRONG_ECC_CONFIG 1 + #define MSM_NAND_CFG0_RAW 0xA80420C0 #define MSM_NAND_CFG1_RAW 0x5045D -#define SUPPORT_WRONG_ECC_CONFIG 1 -#define IGNORE_ARM9_CONFIG 0 #define VERBOSE 0 static struct nand_hw_info *nand_info; @@ -69,6 +63,9 @@ struct msm_nand_chip { uint8_t *dma_buffer; dma_addr_t dma_addr; unsigned CFG0, CFG1; + unsigned page_shift; + unsigned last_sector; + unsigned last_sectorsz; #if SUPPORT_WRONG_ECC_CONFIG uint32_t ecc_buf_cfg; uint32_t saved_ecc_buf_cfg; @@ -78,6 +75,15 @@ struct msm_nand_chip { #define CFG1_WIDE_FLASH (1U << 1) +#ifdef CONFIG_ARCH_MSM7X30 +#define BUF_STAT_UNCORRECTABLE (1U << 8) +#define BUF_STAT_NUM_ERRS_MASK (0xf) +#else +#define BUF_STAT_UNCORRECTABLE (1U << 3) +#define BUF_STAT_NUM_ERRS_MASK (0x7) +#endif + + /* TODO: move datamover code out */ #define SRC_CRCI_NAND_CMD CMD_SRC_CRCI(DMOV_NAND_CRCI_CMD) @@ -90,80 +96,25 @@ struct msm_nand_chip { ((uint8_t *)(vaddr) - (chip)->dma_buffer)) /** - * msm_nand_oob_64 - oob info for 2KB page + * msm_nand_oob_64 - oob info for large (2KB) page */ static struct nand_ecclayout msm_nand_oob_64 = { - .eccbytes = 40, - .eccpos = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - }, .oobavail = 16, .oobfree = { {30, 16}, } }; -/** +/* * msm_nand_oob_128 - oob info for 4KB page */ static struct nand_ecclayout msm_nand_oob_128 = { - .eccbytes = 80, - .eccpos = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, - 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, - }, - .oobavail = 32, - .oobfree = { + .oobavail = 32, + .oobfree = { {70, 32}, } }; -struct flash_identification { - uint32_t flash_id; - uint32_t mask; - uint32_t density; - uint32_t widebus; - uint32_t pagesize; - uint32_t blksize; - uint32_t oobsize; -}; - -static struct flash_identification supported_flash[] = -{ - /* Flash ID ID Mask Density(MB) Wid Pgsz Blksz oobsz Manuf */ - {0x00000000, 0xFFFFFFFF, 0, 0, 0, 0, 0, }, /*ONFI*/ - {0x1500aaec, 0xFF00FFFF, (256<<20), 0, 2048, (2048<<6), 64, }, /*Samsung 2Gbit*/ - {0x5500baec, 0xFF00FFFF, (256<<20), 1, 2048, (2048<<6), 64, }, /*Samsung 2Gbit*/ - {0x6600bcec, 0xFF00FFFF, (512<<20), 1, 4096, (4096<<6), 128,}, /*Samsung 4Gbit 4K Page*/ - {0x0000aaec, 0x0000FFFF, (256<<20), 1, 2048, (2048<<6), 64, }, /*Samsung 2Gbit*/ - {0x0000acec, 0x0000FFFF, (512<<20), 1, 2048, (2048<<6), 64, }, /*Samsung 4Gbit*/ - {0x0000bcec, 0x0000FFFF, (512<<20), 1, 2048, (2048<<6), 64, }, /*Samsung 4Gbit*/ - {0x6601b3ec, 0xFFFFFFFF, (1024<<20),1, 4096, (4096<<6), 128,}, /*Samsung 8Gbit 4Kpage*/ - {0x0000b3ec, 0x0000FFFF, (1024<<20),1, 2048, (2048<<6), 64, }, /*Samsung 8Gbit*/ - - {0x1500aa98, 0xFFFFFFFF, (256<<20), 0, 2048, (2048<<6), 64, }, /*Toshiba 2Gbit*/ - {0x5500ba98, 0xFFFFFFFF, (256<<20), 1, 2048, (2048<<6), 64, }, /*Toshiba 2Gbit*/ - - {0xd580b12c, 0xFFFFFFFF, (128<<20), 1, 2048, (2048<<6), 64, }, /*Micron 1Gbit*/ - {0x0000ba2c, 0x0000FFFF, (256<<20), 1, 2048, (2048<<6), 64, }, /*Micron 2Gbit*/ - {0x0000bc2c, 0x0000FFFF, (512<<20), 1, 2048, (2048<<6), 64, }, /*Micron 4Gbit*/ - {0x0000b32c, 0x0000FFFF, (1024<<20),1, 2048, (2048<<6), 64, }, /*Micron 8Gbit*/ - - {0x0000baad, 0x0000FFFF, (256<<20), 1, 2048, (2048<<6), 64, }, /*Hynix 2Gbit*/ - {0x0000bcad, 0x0000FFFF, (512<<20), 1, 2048, (2048<<6), 64, }, /*Hynix 4Gbit*/ - {0x0000b3ad, 0x0000FFFF, (1024<<20),1, 2048, (2048<<6), 64, }, /*Hynix 8Gbit*/ - /* Note: Width flag is 0 for 8 bit Flash and 1 for 16 bit flash */ - /* Note: The First row will be filled at runtime during ONFI probe */ -}; static void *msm_nand_get_dma_buffer(struct msm_nand_chip *chip, size_t size) { @@ -259,17 +210,12 @@ uint32_t flash_read_id(struct msm_nand_chip *chip) dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; - dsb(); msm_dmov_exec_cmd( chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); - dsb(); - pr_info("status: %x\n", dma_buffer->data[3]); - pr_info("nandid: %x maker %02x device %02x\n", - dma_buffer->data[4], dma_buffer->data[4] & 0xff, - (dma_buffer->data[4] >> 8) & 0xff); rv = dma_buffer->data[4]; + pr_info("msn_nand: nandid %x status %x\n", rv, dma_buffer->data[3]); msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); return rv; } @@ -303,15 +249,16 @@ int flash_read_config(struct msm_nand_chip *chip) dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; - dsb(); msm_dmov_exec_cmd( chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); - dsb(); chip->CFG0 = dma_buffer->cfg0; chip->CFG1 = dma_buffer->cfg1; - pr_info("msm_nand: read CFG0 = %x, CFG1 = %x\n", chip->CFG0, chip->CFG1); + pr_info("msm_nand: read CFG0 = %x CFG1 = %x\n", chip->CFG0, chip->CFG1); + pr_info("msm_nand: CFG0 cw/page=%d ud_sz=%d ecc_sz=%d spare_sz=%d\n", + (chip->CFG0 >> 6) & 7, (chip->CFG0 >> 9) & 0x3ff, + (chip->CFG0 >> 19) & 15, (chip->CFG0 >> 23) & 15); msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); @@ -343,11 +290,9 @@ unsigned flash_rd_reg(struct msm_nand_chip *chip, unsigned addr) (msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP; dma_buffer->data = 0xeeeeeeee; - dsb(); msm_dmov_exec_cmd( chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); - dsb(); rv = dma_buffer->data; msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); @@ -376,11 +321,9 @@ void flash_wr_reg(struct msm_nand_chip *chip, unsigned addr, unsigned val) (msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP; dma_buffer->data = val; - dsb(); msm_dmov_exec_cmd( chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); - dsb(); msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); } @@ -401,6 +344,44 @@ msm_nand_dma_map(struct device *dev, void *addr, size_t size, return dma_map_page(dev, page, offset, size, dir); } +static int msm_nand_check_empty(struct mtd_info *mtd, struct mtd_oob_ops *ops, + unsigned long *uncorrected) +{ + unsigned int p, n, end; + uint8_t *datbuf = ops->datbuf; + uint8_t *oobbuf = ops->oobbuf; + size_t oobsize; + int page_count; + + if (ops->mode == MTD_OOB_RAW) + return false; + + page_count = ops->retlen / mtd->writesize; + oobsize = (ops->mode == MTD_OOB_AUTO) ? mtd->oobavail : mtd->oobsize; + + for_each_set_bit(p, uncorrected, page_count) { + if (datbuf) { + datbuf = ops->datbuf + p * mtd->writesize; + for (n = 0; n < mtd->writesize; n++) { + /* empty blocks read 0x54 at these offsets */ + if (datbuf[n] != ((n % 516 == 3) ? 0x54 : 0xff)) + return false; + } + } + if (oobbuf) { + n = p * oobsize; + end = min(n + oobsize, ops->oobretlen); + for(; n < end; n++) + if (oobbuf[n] != 0xff) + return false; + } + if (ops->datbuf) + for (n = 3; n < mtd->writesize; n+= 516) + datbuf[n] = 0xff; + } + return true; +} + static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { @@ -429,11 +410,11 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, } *dma_buffer; dmov_s *cmd; unsigned n; - unsigned page = 0; - uint32_t oob_len; + unsigned page = from >> chip->page_shift; + uint32_t oob_len = ops->ooblen; uint32_t sectordatasize; uint32_t sectoroobsize; - int err, pageerr, rawerr; + int err, pageerr; dma_addr_t data_dma_addr = 0; dma_addr_t oob_dma_addr = 0; dma_addr_t data_dma_addr_curr = 0; @@ -442,46 +423,51 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, unsigned page_count; unsigned pages_read = 0; unsigned start_sector = 0; - uint32_t ecc_errors; - uint32_t total_ecc_errors = 0; - unsigned cwperpage; - - if (mtd->writesize == 2048) - page = from >> 11; - - if (mtd->writesize == 4096) - page = from >> 12; - - oob_len = ops->ooblen; - cwperpage = (mtd->writesize >> 9); + uint32_t sector_corrected; + uint32_t page_corrected; + uint32_t total_corrected = 0; + uint32_t total_uncorrected = 0; + unsigned long uncorrected_noalloc = 0; + unsigned long *uncorrected = &uncorrected_noalloc; if (from & (mtd->writesize - 1)) { pr_err("%s: unsupported from, 0x%llx\n", __func__, from); return -EINVAL; } - if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { - /* when ops->datbuf is NULL, ops->len may refer to ooblen */ - pr_err("%s: unsupported ops->len, %d\n", - __func__, ops->len); - return -EINVAL; + if (ops->mode != MTD_OOB_RAW) { + if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { + /* when ops->datbuf is NULL, ops->len can be ooblen */ + pr_err("%s: unsupported ops->len, %d\n", + __func__, ops->len); + return -EINVAL; + } + } else { + if (ops->datbuf != NULL && + (ops->len % (mtd->writesize + mtd->oobsize)) != 0) { + pr_err("%s: unsupported ops->len," + " %d for MTD_OOB_RAW\n", __func__, ops->len); + return -EINVAL; + } } - if (ops->ooblen != 0 && ops->ooboffs != 0) { + + if (ops->mode != MTD_OOB_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { pr_err("%s: unsupported ops->ooboffs, %d\n", __func__, ops->ooboffs); return -EINVAL; } if (ops->oobbuf && !ops->datbuf && ops->mode == MTD_OOB_AUTO) - start_sector = cwperpage - 1; + start_sector = chip->last_sector; if (ops->oobbuf && !ops->datbuf) { - page_count = ops->ooblen / ((ops->mode == MTD_OOB_AUTO) ? - mtd->oobavail : mtd->oobsize); - if ((page_count == 0) && (ops->ooblen)) - page_count = 1; - } else + unsigned tmpoobsz = (ops->mode == MTD_OOB_AUTO) ? + mtd->oobavail : mtd->oobsize; + page_count = DIV_ROUND_UP(ops->ooblen, tmpoobsz); + } else if (ops->mode != MTD_OOB_RAW) page_count = ops->len / mtd->writesize; + else + page_count = ops->len / (mtd->writesize + mtd->oobsize); #if 0 /* yaffs reads more oob data than it needs */ if (ops->ooblen >= sectoroobsize * 4) { @@ -499,7 +485,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, /* memset(ops->datbuf, 0x55, ops->len); */ data_dma_addr_curr = data_dma_addr = msm_nand_dma_map(chip->dev, ops->datbuf, ops->len, - DMA_FROM_DEVICE); + DMA_FROM_DEVICE); if (dma_mapping_error(chip->dev, data_dma_addr)) { pr_err("msm_nand_read_oob: failed to get dma addr " "for %p\n", ops->datbuf); @@ -510,7 +496,7 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, memset(ops->oobbuf, 0xff, ops->ooblen); oob_dma_addr_curr = oob_dma_addr = msm_nand_dma_map(chip->dev, ops->oobbuf, - ops->ooblen, DMA_BIDIRECTIONAL); + ops->ooblen, DMA_BIDIRECTIONAL); if (dma_mapping_error(chip->dev, oob_dma_addr)) { pr_err("msm_nand_read_oob: failed to get dma addr " "for %p\n", ops->oobbuf); @@ -518,6 +504,14 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, goto err_dma_map_oobbuf_failed; } } + if (BITS_TO_LONGS(page_count) > 1) { + uncorrected = kzalloc(BITS_TO_LONGS(page_count) * sizeof(long), + GFP_NOIO); + if (!uncorrected) { + err = -ENOMEM; + goto err_alloc_uncorrected_failed; + } + } wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer( @@ -532,24 +526,35 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, cmd = dma_buffer->cmd; /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ - dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ_ECC; + if (ops->mode != MTD_OOB_RAW) { + dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ_ECC; + dma_buffer->data.cfg0 = + (chip->CFG0 & ~(7U << 6)) | + ((chip->last_sector - start_sector) << 6); + dma_buffer->data.cfg1 = chip->CFG1; + } else { + dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; + dma_buffer->data.cfg0 = + (MSM_NAND_CFG0_RAW & ~(7U << 6)) | + (chip->last_sector << 6); + dma_buffer->data.cfg1 = MSM_NAND_CFG1_RAW | + (chip->CFG1 & CFG1_WIDE_FLASH); + } + dma_buffer->data.addr0 = (page << 16) | oob_col; /* qc example is (page >> 16) && 0xff !? */ dma_buffer->data.addr1 = (page >> 16) & 0xff; /* flash0 + undoc bit */ dma_buffer->data.chipsel = 0 | 4; - dma_buffer->data.cfg0 = - (chip->CFG0 & ~(7U << 6)) - | (((cwperpage-1) - start_sector) << 6); - dma_buffer->data.cfg1 = chip->CFG1; /* GO bit for the EXEC register */ dma_buffer->data.exec = 1; + BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.result)); - for (n = start_sector; n < cwperpage; n++) { + for (n = start_sector; n <= chip->last_sector; n++) { /* flash + buffer status return words */ dma_buffer->data.result[n].flash_status = 0xeeeeeeee; dma_buffer->data.result[n].buffer_status = 0xeeeeeeee; @@ -612,8 +617,13 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, * (only valid if status says success) */ if (ops->datbuf) { - sectordatasize = (n < (cwperpage - 1)) - ? 516 : (512 - ((cwperpage - 1) << 2)); + if (ops->mode != MTD_OOB_RAW) + sectordatasize = + (n < chip->last_sector) ? + 516 : chip->last_sectorsz; + else + sectordatasize = 528; + cmd->cmd = 0; cmd->src = MSM_NAND_FLASH_BUFFER; cmd->dst = data_dma_addr_curr; @@ -622,13 +632,14 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, cmd++; } - if (ops->oobbuf && (n == (cwperpage - 1) - || ops->mode != MTD_OOB_AUTO)) { + if (ops->oobbuf && (n == chip->last_sector || + ops->mode != MTD_OOB_AUTO)) { cmd->cmd = 0; - if (n == (cwperpage - 1)) { + if (n == chip->last_sector) { cmd->src = MSM_NAND_FLASH_BUFFER + - (512 - ((cwperpage - 1) << 2)); - sectoroobsize = (cwperpage << 2); + chip->last_sectorsz; + sectoroobsize = + (chip->last_sector + 1) * 4; if (ops->mode != MTD_OOB_AUTO) sectoroobsize += 10; } else { @@ -669,109 +680,61 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; - dsb(); msm_dmov_exec_cmd( chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR( msm_virt_to_dma(chip, &dma_buffer->cmdptr))); - dsb(); /* if any of the writes failed (0x10), or there * was a protection violation (0x100), we lose */ - pageerr = rawerr = 0; - for (n = start_sector; n < cwperpage; n++) { - if (dma_buffer->data.result[n].flash_status & 0x110) { - rawerr = -EIO; + pageerr = 0; + page_corrected = 0; + for (n = start_sector; n <= chip->last_sector; n++) { + uint32_t buf_stat = + dma_buffer->data.result[n].buffer_status; + if (buf_stat & BUF_STAT_UNCORRECTABLE) { + total_uncorrected++; + uncorrected[BIT_WORD(pages_read)] |= + BIT_MASK(pages_read); + pageerr = -EBADMSG; break; } - } - if (rawerr) { - if (ops->datbuf) { - uint8_t *datbuf = ops->datbuf + - pages_read * mtd->writesize; - - dma_sync_single_for_cpu(chip->dev, - data_dma_addr_curr-mtd->writesize, - mtd->writesize, DMA_BIDIRECTIONAL); - - for (n = 0; n < mtd->writesize; n++) { - /* empty blocks read 0x54 at - * these offsets - */ - if (n % 516 == 3 && datbuf[n] == 0x54) - datbuf[n] = 0xff; - if (datbuf[n] != 0xff) { - pageerr = rawerr; - break; - } - } - - dma_sync_single_for_device(chip->dev, - data_dma_addr_curr-mtd->writesize, - mtd->writesize, DMA_BIDIRECTIONAL); - - } - if (ops->oobbuf) { - for (n = 0; n < ops->ooblen; n++) { - if (ops->oobbuf[n] != 0xff) { - pageerr = rawerr; - break; - } - } - } - } - if (pageerr) { - for (n = start_sector; n < cwperpage; n++) { - if (dma_buffer->data.result[n].buffer_status & - 0x8) { - /* not thread safe */ - mtd->ecc_stats.failed++; - pageerr = -EBADMSG; - break; - } + if (dma_buffer->data.result[n].flash_status & 0x110) { + pageerr = -EIO; + break; } + sector_corrected =buf_stat & BUF_STAT_NUM_ERRS_MASK; + page_corrected += sector_corrected; + if (sector_corrected > 1) + pageerr = -EUCLEAN; } - if (!rawerr) { /* check for corretable errors */ - for (n = start_sector; n < cwperpage; n++) { - ecc_errors = dma_buffer->data. - result[n].buffer_status & 0x7; - if (ecc_errors) { - total_ecc_errors += ecc_errors; - /* not thread safe */ - mtd->ecc_stats.corrected += ecc_errors; - if (ecc_errors > 1) - pageerr = -EUCLEAN; - } - } + if ((!pageerr && page_corrected) || pageerr == -EUCLEAN) { + total_corrected += page_corrected; + /* not thread safe */ + mtd->ecc_stats.corrected += page_corrected; } if (pageerr && (pageerr != -EUCLEAN || err == 0)) err = pageerr; #if VERBOSE - if (rawerr && !pageerr) { - pr_err("msm_nand_read_oob %llx %x %x empty page\n", - (loff_t)page * mtd->writesize, ops->len, - ops->ooblen); - } else { - pr_info("status: %x %x %x %x %x %x %x %x %x \ - %x %x %x %x %x %x %x \n", - dma_buffer->data.result[0].flash_status, - dma_buffer->data.result[0].buffer_status, - dma_buffer->data.result[1].flash_status, - dma_buffer->data.result[1].buffer_status, - dma_buffer->data.result[2].flash_status, - dma_buffer->data.result[2].buffer_status, - dma_buffer->data.result[3].flash_status, - dma_buffer->data.result[3].buffer_status); - dma_buffer->data.result[4].buffer_status); - dma_buffer->data.result[4].buffer_status); - dma_buffer->data.result[5].buffer_status); - dma_buffer->data.result[5].buffer_status); - dma_buffer->data.result[6].buffer_status); - dma_buffer->data.result[6].buffer_status); - dma_buffer->data.result[7].buffer_status); - dma_buffer->data.result[7].buffer_status); - } + pr_info("status: %x %x %x %x %x %x %x %x " + "%x %x %x %x %x %x %x %x\n", + dma_buffer->data.result[0].flash_status, + dma_buffer->data.result[0].buffer_status, + dma_buffer->data.result[1].flash_status, + dma_buffer->data.result[1].buffer_status, + dma_buffer->data.result[2].flash_status, + dma_buffer->data.result[2].buffer_status, + dma_buffer->data.result[3].flash_status, + dma_buffer->data.result[3].buffer_status, + dma_buffer->data.result[4].flash_status, + dma_buffer->data.result[4].buffer_status, + dma_buffer->data.result[5].flash_status, + dma_buffer->data.result[5].buffer_status, + dma_buffer->data.result[6].flash_status, + dma_buffer->data.result[6].buffer_status, + dma_buffer->data.result[7].flash_status, + dma_buffer->data.result[7].buffer_status); #endif if (err && err != -EUCLEAN && err != -EBADMSG) break; @@ -780,22 +743,35 @@ static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, } msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); +err_alloc_uncorrected_failed: if (ops->oobbuf) { dma_unmap_page(chip->dev, oob_dma_addr, - ops->ooblen, DMA_FROM_DEVICE); + ops->ooblen, DMA_FROM_DEVICE); } err_dma_map_oobbuf_failed: if (ops->datbuf) { dma_unmap_page(chip->dev, data_dma_addr, - ops->len, DMA_FROM_DEVICE); + ops->len, DMA_FROM_DEVICE); } - ops->retlen = mtd->writesize * pages_read; + if (ops->mode != MTD_OOB_RAW) + ops->retlen = mtd->writesize * pages_read; + else + ops->retlen = (mtd->writesize + mtd->oobsize) * + pages_read; ops->oobretlen = ops->ooblen - oob_len; + + if (err == -EBADMSG && msm_nand_check_empty(mtd, ops, uncorrected)) + err = 0; + else if (total_uncorrected) + mtd->ecc_stats.failed += total_uncorrected; /* not threadsafe */ + if (uncorrected != &uncorrected_noalloc) + kfree(uncorrected); + if (err) pr_err("msm_nand_read_oob %llx %x %x failed %d, corrected %d\n", from, ops->datbuf ? ops->len : 0, ops->ooblen, err, - total_ecc_errors); + total_corrected); return err; } @@ -844,8 +820,8 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) } *dma_buffer; dmov_s *cmd; unsigned n; - unsigned page = 0; - uint32_t oob_len; + unsigned page = to >> chip->page_shift; + uint32_t oob_len = ops->ooblen; uint32_t sectordatawritesize; int err; dma_addr_t data_dma_addr = 0; @@ -854,36 +830,36 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) dma_addr_t oob_dma_addr_curr = 0; unsigned page_count; unsigned pages_written = 0; - unsigned cwperpage; - - if (mtd->writesize == 2048) - page = to >> 11; - - if (mtd->writesize == 4096) - page = to >> 12; - - oob_len = ops->ooblen; - cwperpage = (mtd->writesize >> 9); if (to & (mtd->writesize - 1)) { pr_err("%s: unsupported to, 0x%llx\n", __func__, to); return -EINVAL; } - if (ops->ooblen != 0 && ops->mode != MTD_OOB_AUTO) { - pr_err("%s: unsupported ops->mode, %d\n", - __func__, ops->mode); - return -EINVAL; + + if (ops->mode != MTD_OOB_RAW) { + if (ops->ooblen != 0 && ops->mode != MTD_OOB_AUTO) { + pr_err("%s: unsupported ops->mode,%d\n", + __func__, ops->mode); + return -EINVAL; + } + if ((ops->len % mtd->writesize) != 0) { + pr_err("%s: unsupported ops->len, %d\n", + __func__, ops->len); + return -EINVAL; + } + } else { + if ((ops->len % (mtd->writesize + mtd->oobsize)) != 0) { + pr_err("%s: unsupported ops->len, " + "%d for MTD_OOB_RAW mode\n", + __func__, ops->len); + return -EINVAL; + } } if (ops->datbuf == NULL) { pr_err("%s: unsupported ops->datbuf == NULL\n", __func__); return -EINVAL; } - if ((ops->len % mtd->writesize) != 0) { - pr_err("%s: unsupported ops->len, %d\n", - __func__, ops->len); - return -EINVAL; - } #if 0 /* yaffs writes more oob data than it needs */ if (ops->ooblen >= sectoroobsize * 4) { pr_err("%s: unsupported ops->ooblen, %d\n", @@ -891,7 +867,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) return -EINVAL; } #endif - if (ops->ooblen != 0 && ops->ooboffs != 0) { + if (ops->mode != MTD_OOB_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { pr_err("%s: unsupported ops->ooboffs, %d\n", __func__, ops->ooboffs); return -EINVAL; @@ -900,7 +876,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) if (ops->datbuf) { data_dma_addr_curr = data_dma_addr = msm_nand_dma_map(chip->dev, ops->datbuf, - ops->len, DMA_TO_DEVICE); + ops->len, DMA_TO_DEVICE); if (dma_mapping_error(chip->dev, data_dma_addr)) { pr_err("msm_nand_write_oob: failed to get dma addr " "for %p\n", ops->datbuf); @@ -910,7 +886,7 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) if (ops->oobbuf) { oob_dma_addr_curr = oob_dma_addr = msm_nand_dma_map(chip->dev, ops->oobbuf, - ops->ooblen, DMA_TO_DEVICE); + ops->ooblen, DMA_TO_DEVICE); if (dma_mapping_error(chip->dev, oob_dma_addr)) { pr_err("msm_nand_write_oob: failed to get dma addr " "for %p\n", ops->oobbuf); @@ -918,8 +894,10 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) goto err_dma_map_oobbuf_failed; } } - - page_count = ops->len / mtd->writesize; + if (ops->mode != MTD_OOB_RAW) + page_count = ops->len / mtd->writesize; + else + page_count = ops->len / (mtd->writesize + mtd->oobsize); wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer(chip, sizeof(*dma_buffer)))); @@ -927,22 +905,31 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) while (page_count-- > 0) { cmd = dma_buffer->cmd; - /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ + /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ + if (ops->mode != MTD_OOB_RAW) { + dma_buffer->data.cfg0 = chip->CFG0; + dma_buffer->data.cfg1 = chip->CFG1; + } else { + dma_buffer->data.cfg0 = + (MSM_NAND_CFG0_RAW & ~(7U << 6)) | + (chip->last_sector << 6); + dma_buffer->data.cfg1 = MSM_NAND_CFG1_RAW | + (chip->CFG1 & CFG1_WIDE_FLASH); + } + dma_buffer->data.cmd = MSM_NAND_CMD_PRG_PAGE; dma_buffer->data.addr0 = page << 16; dma_buffer->data.addr1 = (page >> 16) & 0xff; dma_buffer->data.chipsel = 0 | 4; /* flash0 + undoc bit */ dma_buffer->data.zeroes = 0; - dma_buffer->data.cfg0 = chip->CFG0; - dma_buffer->data.cfg1 = chip->CFG1; /* GO bit for the EXEC register */ dma_buffer->data.exec = 1; BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.flash_status)); - for (n = 0; n < cwperpage ; n++) { + for (n = 0; n <= chip->last_sector ; n++) { /* status return words */ dma_buffer->data.flash_status[n] = 0xeeeeeeee; /* block on cmd ready, then @@ -980,9 +967,13 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) #endif } - /* write data block */ - sectordatawritesize = (n < (cwperpage - 1)) ? - 516 : (512 - ((cwperpage - 1) << 2)); + /* write data block */ + if (ops->mode != MTD_OOB_RAW) + sectordatawritesize = (n < chip->last_sector) ? + 516 : chip->last_sectorsz; + else + sectordatawritesize = 528; + cmd->cmd = 0; cmd->src = data_dma_addr_curr; data_dma_addr_curr += sectordatawritesize; @@ -991,14 +982,13 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) cmd++; if (ops->oobbuf) { - if (n == (cwperpage - 1)) { + if (n == chip->last_sector) { cmd->cmd = 0; cmd->src = oob_dma_addr_curr; cmd->dst = MSM_NAND_FLASH_BUFFER + - (512 - ((cwperpage - 1) << 2)); - if ((cwperpage << 2) < oob_len) - cmd->len = (cwperpage << 2); - else + chip->last_sectorsz; + cmd->len = 516 - chip->last_sectorsz; + if (oob_len <= cmd->len) cmd->len = oob_len; oob_dma_addr_curr += cmd->len; oob_len -= cmd->len; @@ -1064,18 +1054,16 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; - dsb(); msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR( msm_virt_to_dma(chip, &dma_buffer->cmdptr))); - dsb(); /* if any of the writes failed (0x10), or there was a * protection violation (0x100), or the program success * bit (0x80) is unset, we lose */ err = 0; - for (n = 0; n < cwperpage; n++) { + for (n = 0; n <= chip->last_sector ; n++) { if (dma_buffer->data.flash_status[n] & 0x110) { if (dma_buffer->data.flash_status[n] & 0x10) pr_err("msm_nand: critical write error," @@ -1090,8 +1078,8 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) } #if VERBOSE - pr_info("write page %d: status: %x %x %x %x %x %x %x %x\n", page, - dma_buffer->data.flash_status[0], + pr_info("write page %d: status: %x %x %x %x %x %x %x %x\n", + page, dma_buffer->data.flash_status[0], dma_buffer->data.flash_status[1], dma_buffer->data.flash_status[2], dma_buffer->data.flash_status[3], @@ -1105,14 +1093,18 @@ msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) pages_written++; page++; } - ops->retlen = mtd->writesize * pages_written; + if (ops->mode != MTD_OOB_RAW) + ops->retlen = mtd->writesize * pages_written; + else + ops->retlen = (mtd->writesize + mtd->oobsize) * pages_written; + ops->oobretlen = ops->ooblen - oob_len; msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); if (ops->oobbuf) dma_unmap_page(chip->dev, oob_dma_addr, - ops->ooblen, DMA_TO_DEVICE); + ops->ooblen, DMA_TO_DEVICE); err_dma_map_oobbuf_failed: if (ops->datbuf) dma_unmap_page(chip->dev, data_dma_addr, @@ -1150,13 +1142,7 @@ msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) unsigned cmdptr; unsigned data[9]; } *dma_buffer; - unsigned page = 0; - - if (mtd->writesize == 2048) - page = instr->addr >> 11; - - if (mtd->writesize == 4096) - page = instr->addr >> 12; + unsigned page = instr->addr >> chip->page_shift; if (instr->addr & (mtd->erasesize - 1)) { pr_err("%s: unsupported erase address, 0x%llx\n", @@ -1212,14 +1198,13 @@ msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) dma_buffer->cmd[4].len = 4; BUILD_BUG_ON(4 != ARRAY_SIZE(dma_buffer->cmd) - 1); + dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; - dsb(); msm_dmov_exec_cmd( chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); - dsb(); /* we fail if there was an operation error, a mpu error, or the * erase success bit was not set. @@ -1271,16 +1256,7 @@ msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) } *dma_buffer; dmov_s *cmd; uint8_t *buf; - unsigned page = 0; - unsigned cwperpage; - - if (mtd->writesize == 2048) - page = ofs >> 11; - - if (mtd->writesize == 4096) - page = ofs >> 12; - - cwperpage = (mtd->writesize >> 9); + unsigned page = ofs >> chip->page_shift; /* Check for invalid offset */ if (ofs > mtd->size) @@ -1308,10 +1284,10 @@ msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) if (chip->CFG1 & CFG1_WIDE_FLASH) dma_buffer->data.addr0 = (page << 16) | - ((528*(cwperpage-1)) >> 1); + ((528 * chip->last_sector) >> 1); else dma_buffer->data.addr0 = (page << 16) | - (528*(cwperpage-1)); + (528 * chip->last_sector); dma_buffer->data.addr1 = (page >> 16) & 0xff; dma_buffer->data.chipsel = 0 | 4; @@ -1346,7 +1322,8 @@ msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) cmd++; cmd->cmd = 0; - cmd->src = MSM_NAND_FLASH_BUFFER + (mtd->writesize - (528*(cwperpage-1))); + cmd->src = MSM_NAND_FLASH_BUFFER + + (mtd->writesize - 528 * chip->last_sector); cmd->dst = msm_virt_to_dma(chip, buf); cmd->len = 4; cmd++; @@ -1359,10 +1336,8 @@ msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; - dsb(); msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); - dsb(); ret = 0; if (dma_buffer->data.result.flash_status & 0x110) @@ -1387,18 +1362,36 @@ msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) static int msm_nand_block_markbad(struct mtd_info *mtd, loff_t ofs) { - /* struct msm_nand_chip *this = mtd->priv; */ + struct mtd_oob_ops ops; int ret; + uint8_t *buf; - ret = msm_nand_block_isbad(mtd, ofs); - if (ret) { - /* If it was bad already, return success and do nothing */ - if (ret > 0) - return 0; - return ret; + /* Check for invalid offset */ + if (ofs > mtd->size) + return -EINVAL; + if (ofs & (mtd->erasesize - 1)) { + pr_err("%s: unsupported block address, 0x%x\n", + __func__, (uint32_t)ofs); + return -EINVAL; } - return -EIO; + /* + Write all 0s to the first page + This will set the BB marker to 0 + */ + + /* Use the already existing zero page */ + buf = page_address(ZERO_PAGE()); + + ops.mode = MTD_OOB_RAW; + ops.len = mtd->writesize + mtd->oobsize; + ops.retlen = 0; + ops.ooblen = 0; + ops.datbuf = buf; + ops.oobbuf = NULL; + ret = msm_nand_write_oob(mtd, ofs, &ops); + + return ret; } /** @@ -1470,99 +1463,115 @@ int msm_nand_scan(struct mtd_info *mtd, int maxchips) { unsigned n; struct msm_nand_chip *chip = mtd->priv; - uint32_t flash_id = 0, i = 1, mtd_writesize; - uint8_t dev_found = 0; - uint8_t wide_bus; - uint8_t index; + uint32_t flash_id; + uint32_t manid; + uint32_t devid; + uint32_t devcfg; + uint32_t busw16; + struct nand_flash_dev *flashdev = NULL; + struct nand_manufacturers *flashman = NULL; + + if (flash_read_config(chip)) { + pr_err("ERRROR: could not save CFG0 & CFG1 state\n"); + return -ENODEV; + } + pr_info("msm_nand: NAND_READ_ID = %x\n", + flash_rd_reg(chip, MSM_NAND_READ_ID)); + flash_wr_reg(chip, MSM_NAND_READ_ID, 0x12345678); - /* Read the Flash ID from the Nand Flash Device */ flash_id = flash_read_id(chip); - for (index = 1; index < ARRAY_SIZE(supported_flash); index++) { - if ((flash_id & supported_flash[index].mask) == - (supported_flash[index].flash_id & - (supported_flash[index].mask))) { - dev_found = 1; - break; - } + manid = flash_id & 0xff; + devid = (flash_id >> 8) & 0xff; + devcfg = (flash_id >> 24) & 0xff; + + for (n = 0; !flashman && nand_manuf_ids[n].id; ++n) + if (nand_manuf_ids[n].id == manid) + flashman = &nand_manuf_ids[n]; + for (n = 0; !flashdev && nand_flash_ids[n].id; ++n) + if (nand_flash_ids[n].id == devid) + flashdev = &nand_flash_ids[n]; + if (!flashdev || !flashman) { + pr_err("ERROR: unknown nand device manuf=%x devid=%x\n", + manid, devid); + return -ENOENT; } - if (dev_found) { - wide_bus = supported_flash[index].widebus; - mtd->size = supported_flash[index].density * i; - mtd->writesize = supported_flash[index].pagesize * i; - mtd->oobsize = supported_flash[index].oobsize * i; - mtd->erasesize = supported_flash[index].blksize * i; - mtd_writesize = mtd->writesize; - - pr_info("Found a supported NAND device\n"); - pr_info("NAND Id : 0x%X\n", supported_flash[index]. - flash_id); - pr_info("Buswidth : %d Bits \n", (wide_bus) ? 16 : 8); - pr_info("Density : %lld MByte\n", (mtd->size>>20)); - pr_info("Pagesize : %d Bytes\n", mtd->writesize); - pr_info("Erasesize: %d Bytes\n", mtd->erasesize); - pr_info("Oobsize : %d Bytes\n", mtd->oobsize); + if (!flashdev->pagesize) { + mtd->erasesize = (64 * 1024) << ((devcfg >> 4) & 0x3); + mtd->writesize = 1024 << (devcfg & 0x3); + mtd->oobsize = (8 << ((devcfg >> 2) & 1)) * + (mtd->writesize / 512); + busw16 = devcfg & (1 << 6) ? CFG1_WIDE_FLASH : 0; } else { - pr_err("Unsupported Nand,Id: 0x%x \n", flash_id); - return -ENODEV; + mtd->writesize = flashdev->pagesize; + mtd->erasesize = flashdev->erasesize; + mtd->oobsize = flashdev->pagesize / 32; + busw16 = flashdev->options & NAND_BUSWIDTH_16 ? + CFG1_WIDE_FLASH : 0; + } + mtd->size = flashdev->chipsize << 20; + pr_info("msm_nand: manuf %s (0x%x) device 0x%x blocksz %x pagesz %x " + "size %llx\n", flashman->name, flashman->id, flashdev->id, + mtd->erasesize, mtd->writesize, mtd->size); + + if (mtd->writesize == 2048) { + chip->page_shift = 11; + } else if (mtd->writesize == 4096) { + chip->page_shift = 12; + } else { + pr_err("%s: Unsupported page size (%d)\n", __func__, + mtd->writesize); + return -EINVAL; } - if (flash_read_config(chip)) { - pr_err("ERROR: could not save CFG0 & CFG1 state\n"); - return -ENODEV; + chip->last_sector = (mtd->writesize / 512) - 1; + chip->last_sectorsz = mtd->writesize - chip->last_sector * 516; + + if (mtd->oobsize == 64) { + mtd->ecclayout = &msm_nand_oob_64; + } else if (mtd->oobsize == 128) { + mtd->ecclayout = &msm_nand_oob_128; + } else { + pr_err("%s: Unsupported oob size (%d)\n", __func__, + mtd->oobsize); + return -EINVAL; } - chip->CFG0 = (((mtd->writesize >> 9)-1) << 6) /* 4/8 cw/per page for 2/4k */ + mtd->oobavail = mtd->ecclayout->oobavail; + + chip->CFG0 = (chip->last_sector << 6) /* codewords per page */ | (516 << 9) /* 516 user data bytes */ | (10 << 19) /* 10 parity bytes */ | (5 << 27) /* 5 address cycles */ | (1 << 30) /* Read status before data */ | (1 << 31) /* Send read cmd */ /* 0 spare bytes for 16 bit nand or 1 spare bytes for 8 bit */ - | ((wide_bus) ? (0 << 23) : (1 << 23)); - chip->CFG1 = chip->CFG1 -#if IGNORE_ARM9_CONFIG - /* use ARM11 own setting on CFG1 */ + | ((busw16 & CFG1_WIDE_FLASH) ? (0 << 23) : (1 << 23)); + chip->CFG1 = (0 << 0) /* Enable ecc */ | (7 << 2) /* 8 recovery cycles */ | (0 << 5) /* Allow CS deassertion */ + | ((mtd->writesize - (528 * chip->last_sector) + 1) << 6) + /* Bad block marker location */ + | (0 << 16) /* Bad block in user data area */ | (2 << 17) /* 6 cycle tWB/tRB */ -#endif - | ((mtd->writesize - (528 * ((mtd->writesize >> 9) - 1)) + 1) - << 6) /* Bad block marker location */ - | (wide_bus << 1); /* Wide flash bit */ - chip->CFG1 = chip->CFG1 - & ~(1 << 0) /* Enable ecc */ - & ~(1 << 16); /* Bad block in user data area */ - - pr_info("CFG0 Init : 0x%08x \n", chip->CFG0); - pr_info("CFG1 Init : 0x%08x \n", chip->CFG1); - pr_info("CFG0: cw/page=%d ud_sz=%d ecc_sz=%d spare_sz=%d " + | (busw16 & CFG1_WIDE_FLASH); /* preserve wide flag */ + + pr_info("msm_nand: save CFG0 = %x CFG1 = %x\n", chip->CFG0, chip->CFG1); + pr_info("msm_nand: CFG0: cw/page=%d ud_sz=%d ecc_sz=%d spare_sz=%d " "num_addr_cycles=%d\n", (chip->CFG0 >> 6) & 7, (chip->CFG0 >> 9) & 0x3ff, (chip->CFG0 >> 19) & 15, (chip->CFG0 >> 23) & 15, (chip->CFG0 >> 27) & 7); n = flash_rd_reg(chip, MSM_NAND_DEV_CMD1); - pr_info("DEV_CMD1: %x\n", n); + pr_info("msm_nand: DEV_CMD1: %x\n", n); n = flash_rd_reg(chip, MSM_NAND_EBI2_ECC_BUF_CFG); - pr_info(KERN_INFO "NAND_EBI2_ECC_BUF_CFG: %x\n", n); + pr_info("msm_nand: NAND_EBI2_ECC_BUF_CFG: %x\n", n); #if SUPPORT_WRONG_ECC_CONFIG chip->ecc_buf_cfg = 0x203; chip->saved_ecc_buf_cfg = n; #endif - if (mtd->oobsize == 64) { - mtd->oobavail = msm_nand_oob_64.oobavail; - mtd->ecclayout = &msm_nand_oob_64; - } else if (mtd->oobsize == 128) { - mtd->oobavail = msm_nand_oob_128.oobavail; - mtd->ecclayout = &msm_nand_oob_128; - } else { - pr_err("Unsupported Nand, oobsize: 0x%x \n", - mtd->oobsize); - return -ENODEV; - } - /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; @@ -1601,7 +1610,7 @@ int msm_nand_scan(struct mtd_info *mtd, int maxchips) strcpy(nand_info->maker_name, "Unknown"); break; } - nand_info->width = (wide_bus? 16: 8); + nand_info->width = (CFG1_WIDE_FLASH? 16: 8); nand_info->size = mtd->size; nand_info->page_size = mtd->writesize; nand_info->page_count = mtd->erasesize/mtd->writesize; @@ -1647,7 +1656,8 @@ static int __devinit msm_nand_probe(struct platform_device *pdev) { struct msm_nand_info *info; struct flash_platform_data *pdata = pdev->dev.platform_data; - int err, i; + int err; + int i; if (pdev->num_resources != 1) { pr_err("invalid num_resources"); @@ -1689,20 +1699,16 @@ static int __devinit msm_nand_probe(struct platform_device *pdev) } #ifdef CONFIG_MTD_PARTITIONS - /* Re-calculate the partition offset and size with correct page size */ - for (i = 0; i < pdata->nr_parts; i++) { - pdata->parts[i].offset = pdata->parts[i].offset - * info->mtd.erasesize; - pdata->parts[i].size = pdata->parts[i].size - * info->mtd.erasesize; - } - err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); if (err > 0) add_mtd_partitions(&info->mtd, info->parts, err); - else if (err <= 0 && pdata && pdata->parts) + else if (err <= 0 && pdata && pdata->parts) { + for (i = 0; i < pdata->nr_parts; ++i) { + pdata->parts[i].offset *= info->mtd.erasesize; + pdata->parts[i].size *= info->mtd.erasesize; + } add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); - else + } else #endif err = add_mtd_device(&info->mtd); From b5a7418bf98ff2e476d1c5af80667a2388a49152 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 15 Feb 2011 20:11:19 -0500 Subject: [PATCH 1449/2556] mmc: Add missing break --- drivers/mmc/core/bus.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index b81e979f363ca..238ef08acd83e 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -292,6 +292,7 @@ int mmc_add_card(struct mmc_card *card) break; case MMC_TYPE_SDIO_WIMAX: type = "SD-WiMAX"; + break; default: type = "?"; break; From 2a70fe218c34e9a04445f9992ccb483dbe426cff Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 15 Feb 2011 20:32:41 -0500 Subject: [PATCH 1450/2556] supersonic: Fix wifi sleep * Changed the wifi section of board-incrediblec-mmc.c to match more closely to the bravo. Shamelessly kanged from Slayher, thx br0 :) BLAME TOAST! BLAME TOAST! --- arch/arm/mach-msm/board-supersonic-mmc.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-msm/board-supersonic-mmc.c b/arch/arm/mach-msm/board-supersonic-mmc.c index a5637b0be539b..e18514d91a1e5 100644 --- a/arch/arm/mach-msm/board-supersonic-mmc.c +++ b/arch/arm/mach-msm/board-supersonic-mmc.c @@ -199,6 +199,7 @@ static unsigned int supersonic_wifi_status(struct device *dev) static struct msm_mmc_platform_data supersonic_wifi_data = { .ocr_mask = MMC_VDD_28_29, + .built_in = 1, .status = supersonic_wifi_status, .register_status_notify = supersonic_wifi_status_register, .embedded_sdio = &supersonic_wifi_emb_data, @@ -216,18 +217,16 @@ int supersonic_wifi_set_carddetect(int val) } EXPORT_SYMBOL(supersonic_wifi_set_carddetect); +static int supersonic_wifi_power_state; + int supersonic_wifi_power(int on) { - int rc = 0; - printk(KERN_INFO "%s: %d\n", __func__, on); if (on) { config_gpio_table(wifi_on_gpio_table, ARRAY_SIZE(wifi_on_gpio_table)); mdelay(50); - if (rc) - return rc; } else { config_gpio_table(wifi_off_gpio_table, ARRAY_SIZE(wifi_off_gpio_table)); @@ -235,14 +234,18 @@ int supersonic_wifi_power(int on) mdelay(100); gpio_set_value(SUPERSONIC_GPIO_WIFI_SHUTDOWN_N, on); /* WIFI_SHUTDOWN */ - mdelay(100); + mdelay(200); + + supersonic_wifi_power_state = on; return 0; } -EXPORT_SYMBOL(supersonic_wifi_power); + +static int supersonic_wifi_reset_state; int supersonic_wifi_reset(int on) { printk(KERN_INFO "%s: do nothing\n", __func__); + supersonic_wifi_reset_state = on; return 0; } @@ -611,8 +614,6 @@ int __init supersonic_init_mmc(unsigned int sys_rev) { uint32_t id; - wifi_status_cb = NULL; - printk(KERN_INFO "%s()+\n", __func__); /* initial WIFI_SHUTDOWN# */ From 70206b0c58f29e287a6eea11bbcfc6b81ab770a3 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Thu, 17 Feb 2011 21:00:52 -0500 Subject: [PATCH 1451/2556] msm: supersonic-mmc: fix SQN sleep bug. -adds built-in fuction -add supersonic_wimax_power_state -together killing the sleep bug, win --- arch/arm/mach-msm/board-supersonic-mmc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/mach-msm/board-supersonic-mmc.c b/arch/arm/mach-msm/board-supersonic-mmc.c index e18514d91a1e5..fa35e0adb29cb 100644 --- a/arch/arm/mach-msm/board-supersonic-mmc.c +++ b/arch/arm/mach-msm/board-supersonic-mmc.c @@ -328,6 +328,7 @@ static unsigned int supersonic_wimax_type = MMC_TYPE_SDIO_WIMAX; static struct msm_mmc_platform_data supersonic_wimax_data = { .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30, + .built_in = 1, .status = supersonic_wimax_status, .register_status_notify = supersonic_wimax_status_register, .embedded_sdio = NULL, @@ -372,6 +373,9 @@ int mmc_wimax_get_uart_switch(void) } EXPORT_SYMBOL(mmc_wimax_get_uart_switch); +static int supersonic_wimax_power_state; + + int mmc_wimax_power(int on) { printk("%s\n", __func__); @@ -406,7 +410,9 @@ int mmc_wimax_power(int on) gpio_set_value(154, 0); gpio_set_value(48, 0); mdelay(5); + } +supersonic_wimax_power_state = on; return 0; } EXPORT_SYMBOL(mmc_wimax_power); From 9f6697f851c383e6c3b13596ed25c831c1506829 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Sun, 20 Feb 2011 23:05:27 -0500 Subject: [PATCH 1452/2556] wimax: sqn: Add mutexes --- drivers/net/wimax/SQN/thp.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/net/wimax/SQN/thp.c b/drivers/net/wimax/SQN/thp.c index 9bc7ca7778da1..39b83af96df4d 100644 --- a/drivers/net/wimax/SQN/thp.c +++ b/drivers/net/wimax/SQN/thp.c @@ -43,6 +43,8 @@ extern bool drop_packet; +static struct mutex thp_lock; + const uint8_t host_macaddr[ETH_ALEN] = {0x00, 0x16, 0x08, 0xff, 0x00, 0x01}; const uint8_t ss_macaddr[ETH_ALEN] = {0x00, 0x16, 0x08, 0xff, 0x00, 0x00}; @@ -340,11 +342,13 @@ struct file_operations thp_fops = static int thp_open(struct inode * inode, struct file * filp) { + mutex_lock(&thp_lock); + sqn_pr_enter(); #if THP_DEBUG printk(KERN_WARNING "thp_open +\n"); #endif - + // allow multiple open() call for supporting ioctl on HTC Supersonic /* if(once_open_flag) @@ -358,11 +362,14 @@ static int thp_open(struct inode * inode, struct file * filp) #endif sqn_pr_leave(); + mutex_unlock(&thp_lock); return 0; } static ssize_t thp_release(struct inode *inode, struct file *filp) { + mutex_lock(&thp_lock); + sqn_pr_enter(); once_open_flag = 0; @@ -372,6 +379,7 @@ static ssize_t thp_release(struct inode *inode, struct file *filp) sqn_pr_leave(); + mutex_unlock(&thp_lock); return 0; } @@ -574,6 +582,7 @@ static int thp_ioctl(struct inode* dev, struct file* handle, unsigned int cmd, u #if THP_DEBUG printk(KERN_WARNING "thp_ioctl +\n"); #endif + mutex_lock(&thp_lock); sqn_pr_enter(); switch (cmd) { @@ -611,6 +620,7 @@ static int thp_ioctl(struct inode* dev, struct file* handle, unsigned int cmd, u printk(KERN_WARNING "thp_ioctl -\n"); #endif + mutex_unlock(&thp_lock); return 0; } @@ -724,7 +734,7 @@ int init_thp(struct net_device* dev) /* return -1; */ this_device = dev; - + mutex_init(&thp_lock); sqn_pr_info("KTHP initialized\n"); } From f000c57eac5dc15ca88ce766fc22897b6e413088 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Sun, 20 Feb 2011 23:06:12 -0500 Subject: [PATCH 1453/2556] supersonic: wlan: Fix BT coex and OOB --- arch/arm/mach-msm/board-supersonic-wifi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-supersonic-wifi.c b/arch/arm/mach-msm/board-supersonic-wifi.c index b2a5e283c5d74..49f130cd55cec 100644 --- a/arch/arm/mach-msm/board-supersonic-wifi.c +++ b/arch/arm/mach-msm/board-supersonic-wifi.c @@ -77,7 +77,7 @@ static struct resource supersonic_wifi_resources[] = { .name = "bcm4329_wlan_irq", .start = MSM_GPIO_TO_INT(SUPERSONIC_GPIO_WIFI_IRQ), .end = MSM_GPIO_TO_INT(SUPERSONIC_GPIO_WIFI_IRQ), - .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL, + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE, }, }; @@ -140,6 +140,7 @@ static int __init supersonic_wifi_init(void) printk("%s: start\n", __func__); supersonic_wifi_update_nvs("sd_oobonly=1\r\n", 0); supersonic_wifi_update_nvs("btc_params80=0\n", 1); + supersonic_wifi_update_nvs("btc_params70=0x32\n", 1); supersonic_init_wifi_mem(); ret = platform_device_register(&supersonic_wifi_device); return ret; From fd282b00b030acd2f384050cc9cd997a0d14ca09 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Sun, 20 Feb 2011 23:06:42 -0500 Subject: [PATCH 1454/2556] mmc: msm: Disable sdioirq --- drivers/mmc/host/msm_sdcc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 048e22c6de5cd..6a8689caab378 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -76,7 +76,6 @@ static unsigned int msmsdcc_4bit = 1; static unsigned int msmsdcc_pwrsave = 1; static unsigned int msmsdcc_piopoll = 1; static unsigned int msmsdcc_sdioirq; -static unsigned int msmsdcc_sdioirq = 1; static unsigned long msmsdcc_irqtime; #define PIO_SPINMAX 30 From 949131280eaa633392a33ece90231f0cc8e50e5e Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Thu, 10 Mar 2011 17:11:51 -0500 Subject: [PATCH 1455/2556] wimax: SQN: Use proper unlocked_ioctl --- drivers/net/wimax/SQN/thp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wimax/SQN/thp.c b/drivers/net/wimax/SQN/thp.c index 39b83af96df4d..2926bc1ab3680 100644 --- a/drivers/net/wimax/SQN/thp.c +++ b/drivers/net/wimax/SQN/thp.c @@ -325,7 +325,7 @@ static ssize_t thp_write(struct file *file, const char *buf, static unsigned int thp_poll(struct file *filp, poll_table *wait); -static int thp_ioctl(struct inode*, struct file*, unsigned int, unsigned long); +static long thp_ioctl(struct file*, unsigned int, unsigned long); struct file_operations thp_fops = { @@ -577,7 +577,7 @@ static unsigned int thp_poll(struct file *filp, poll_table *wait) return mask; } -static int thp_ioctl(struct inode* dev, struct file* handle, unsigned int cmd, unsigned long arg) +static long thp_ioctl(struct file* handle, unsigned int cmd, unsigned long arg) { #if THP_DEBUG printk(KERN_WARNING "thp_ioctl +\n"); From 6fe24751136a2b5a161772e42aad8e9740a8f58c Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Sun, 13 Mar 2011 21:47:06 -0400 Subject: [PATCH 1456/2556] supersonic: Add missing I2S enable for HDMI audio --- arch/arm/mach-msm/board-supersonic-audio.c | 40 +++++++++++++++++++++- arch/arm/mach-msm/qdsp6/q6audio.c | 4 +++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-supersonic-audio.c b/arch/arm/mach-msm/board-supersonic-audio.c index c38c6480f8dae..b774e6592c2fa 100644 --- a/arch/arm/mach-msm/board-supersonic-audio.c +++ b/arch/arm/mach-msm/board-supersonic-audio.c @@ -35,6 +35,7 @@ static struct mutex mic_lock; static struct mutex bt_sco_lock; +static struct mutex hdmi_i2s_lock; static int headset_status = 0; static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { @@ -139,6 +140,42 @@ void supersonic_receiver_enable(int en) } } +static uint32_t hdmi_i2s_enable[] = { + PCOM_GPIO_CFG(SUPERSONIC_I2S_CLK, 2, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_I2S_WS, 1, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_I2S_DOUT, 1, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_2MA), +}; + +static uint32_t hdmi_i2s_disable[] = { + PCOM_GPIO_CFG(SUPERSONIC_I2S_CLK, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_I2S_WS, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_2MA), + PCOM_GPIO_CFG(SUPERSONIC_I2S_DOUT, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_2MA), +}; + +void supersonic_hdmi_i2s_enable(int en) +{ + static int hdmi_i2s_refcount; + D("%s %d\n", __func__, en); + mutex_lock(&hdmi_i2s_lock); + if (en) { + if (++hdmi_i2s_refcount == 1) + config_gpio_table(hdmi_i2s_enable, + ARRAY_SIZE(hdmi_i2s_enable)); + } else { + if (--hdmi_i2s_refcount == 0) { + config_gpio_table(hdmi_i2s_disable, ARRAY_SIZE(hdmi_i2s_disable)); + } + } + mutex_unlock(&hdmi_i2s_lock); +} + + static uint32_t bt_sco_enable[] = { PCOM_GPIO_CFG(SUPERSONIC_BT_PCM_OUT, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), @@ -231,7 +268,6 @@ void supersonic_analog_init(void) pmic_mic_set_volt(MIC_VOLT_1_80V); pmic_set_speaker_delay(SPKR_DLY_100MS); - gpio_request(SUPERSONIC_AUD_JACKHP_EN, "aud_jackhp_en"); gpio_direction_output(SUPERSONIC_AUD_JACKHP_EN, 0); gpio_set_value(SUPERSONIC_AUD_JACKHP_EN, 0); @@ -266,6 +302,7 @@ static struct q6audio_analog_ops ops = { .bt_sco_enable = supersonic_bt_sco_enable, .int_mic_enable = supersonic_int_mic_enable, .ext_mic_enable = supersonic_ext_mic_enable, + .i2s_enable = supersonic_hdmi_i2s_enable, .get_rx_vol = supersonic_get_rx_vol, }; @@ -273,6 +310,7 @@ void __init supersonic_audio_init(void) { mutex_init(&mic_lock); mutex_init(&bt_sco_lock); + mutex_init(&hdmi_i2s_lock); q6audio_register_analog_ops(&ops); acoustic_register_ops(&acoustic); } diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c index e8315caf8ca6f..5568fb6b201ad 100644 --- a/arch/arm/mach-msm/qdsp6/q6audio.c +++ b/arch/arm/mach-msm/qdsp6/q6audio.c @@ -893,6 +893,10 @@ static void audio_rx_analog_enable(int en) if (analog_ops->receiver_enable) analog_ops->receiver_enable(en); break; + case ADSP_AUDIO_DEVICE_ID_I2S_SPKR: + if (analog_ops->i2s_enable) + analog_ops->i2s_enable(en); + break; } } From 9e8d36c0a4cfb46c076f3420b83920ea912b08f5 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 15 Mar 2011 13:16:34 -0400 Subject: [PATCH 1457/2556] supersonic: audio: Add missing gpio_request --- arch/arm/mach-msm/board-supersonic-audio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-msm/board-supersonic-audio.c b/arch/arm/mach-msm/board-supersonic-audio.c index b774e6592c2fa..eb78de46846ad 100644 --- a/arch/arm/mach-msm/board-supersonic-audio.c +++ b/arch/arm/mach-msm/board-supersonic-audio.c @@ -268,6 +268,7 @@ void supersonic_analog_init(void) pmic_mic_set_volt(MIC_VOLT_1_80V); pmic_set_speaker_delay(SPKR_DLY_100MS); + gpio_request(SUPERSONIC_AUD_JACKHP_EN, "aud_jackhp_en"); gpio_direction_output(SUPERSONIC_AUD_JACKHP_EN, 0); gpio_set_value(SUPERSONIC_AUD_JACKHP_EN, 0); From d63e9c1f08db2704df2054cfa3f5b2938c391dad Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 15 Mar 2011 13:18:18 -0400 Subject: [PATCH 1458/2556] supersonic: Update tpa2018d1 AMP driver Also fix ioctl signature --- .../arm/mach-msm/board-supersonic-tpa2018d1.c | 266 ++++++++---------- 1 file changed, 125 insertions(+), 141 deletions(-) diff --git a/arch/arm/mach-msm/board-supersonic-tpa2018d1.c b/arch/arm/mach-msm/board-supersonic-tpa2018d1.c index a2b6f3074f93f..bf601df41468a 100644 --- a/arch/arm/mach-msm/board-supersonic-tpa2018d1.c +++ b/arch/arm/mach-msm/board-supersonic-tpa2018d1.c @@ -1,6 +1,6 @@ -/* drivers/i2c/chips/tpa2018d1.c +/* driver/i2c/chip/tap2018d1.c * - * TI TPA2018D1 Speaker Amplifier + * TI TPA2018D1 Speaker Amp * * Copyright (C) 2009 HTC Corporation * @@ -15,48 +15,46 @@ * */ -/* TODO: content validation in TPA2018_SET_CONFIG */ - -#include -#include -#include -#include +#include #include +#include +#include #include #include #include #include +#include +#include +#include #include -#include #include #include "board-supersonic-tpa2018d1.h" +#define DEBUG (0) + static struct i2c_client *this_client; static struct tpa2018d1_platform_data *pdata; -static int is_on; -static char spk_amp_cfg[8]; -static const char spk_amp_on[8] = { /* same length as spk_amp_cfg */ - 0x01, 0xc3, 0x20, 0x01, 0x00, 0x08, 0x1a, 0x21 -}; -static const char spk_amp_off[] = {0x01, 0xa2}; -static DEFINE_MUTEX(spk_amp_lock); +struct mutex spk_amp_lock; static int tpa2018d1_opened; +static int last_spkamp_state; +static char SPK_AMP_CFG[8]; +static char DEFAULT_SPK_AMP_ON[] = + {0x01, 0xc3, 0x20, 0x01, 0x00, 0x08, 0x1a, 0x21}; +static char SPK_AMP_0FF[] = {0x01, 0xa2}; static char *config_data; static int tpa2018d1_num_modes; -#define DEBUG 0 - -static int tpa2018_i2c_write(const char *txData, int length) +static int tpa2018_i2c_write(char *txData, int length) { struct i2c_msg msg[] = { { - .addr = this_client->addr, - .flags = 0, - .len = length, - .buf = txData, - }, + .addr = this_client->addr, + .flags = 0, + .len = length, + .buf = txData, + }, }; if (i2c_transfer(this_client->adapter, msg, 1) < 0) { @@ -68,6 +66,7 @@ static int tpa2018_i2c_write(const char *txData, int length) static int tpa2018_i2c_read(char *rxData, int length) { + int rc; struct i2c_msg msgs[] = { { .addr = this_client->addr, @@ -77,18 +76,18 @@ static int tpa2018_i2c_read(char *rxData, int length) }, }; - if (i2c_transfer(this_client->adapter, msgs, 1) < 0) { - pr_err("%s: I2C transfer error\n", __func__); - return -EIO; + rc = i2c_transfer(this_client->adapter, msgs, 1); + if (rc < 0) { + pr_err("%s: transfer error %d\n", __func__, rc); + return rc; } #if DEBUG - do { + { int i = 0; for (i = 0; i < length; i++) - pr_info("%s: rx[%d] = %2x\n", - __func__, i, rxData[i]); - } while(0); + pr_info("%s: rx[%d] = %2x\n", __func__, i, rxData[i]); + } #endif return 0; @@ -121,125 +120,117 @@ static int tpa2018d1_release(struct inode *inode, struct file *file) return 0; } -static int tpa2018d1_read_config(void __user *argp) +void tpa2018d1_set_speaker_amp(int on) { - int rc = 0; - unsigned char reg_idx = 0x01; - unsigned char tmp[7]; - - if (!is_on) { + mutex_lock(&spk_amp_lock); + if (on && !last_spkamp_state) { gpio_set_value(pdata->gpio_tpa2018_spk_en, 1); - msleep(5); /* According to TPA2018D1 Spec */ + mdelay(5); /* According to TPA2018D1 Spec */ + if (tpa2018_i2c_write(SPK_AMP_CFG, sizeof(SPK_AMP_CFG)) == 0) { + last_spkamp_state = 1; + pr_info("%s: ON, value = %x %x\n", __func__, SPK_AMP_CFG[0], SPK_AMP_CFG[1]); + } + } else if (!on && last_spkamp_state) { + if (tpa2018_i2c_write(SPK_AMP_0FF, sizeof(SPK_AMP_0FF)) == 0) { + last_spkamp_state = 0; + mdelay(2); + gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); + pr_info("%s: OFF\n", __func__); + } } - - rc = tpa2018_i2c_write(®_idx, sizeof(reg_idx)); - if (rc < 0) - goto err; - - rc = tpa2018_i2c_read(tmp, sizeof(tmp)); - if (rc < 0) - goto err; - - if (copy_to_user(argp, &tmp, sizeof(tmp))) - rc = -EFAULT; - -err: - if (!is_on) - gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); - return rc; + mutex_unlock(&spk_amp_lock); } -static int tpa2018d1_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long +tpa2018d1_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { void __user *argp = (void __user *)arg; int rc = 0; + unsigned char tmp[7]; int mode = -1; int offset = 0; + unsigned char reg_idx[1] = {0x01}; struct tpa2018d1_config_data cfg; - mutex_lock(&spk_amp_lock); - switch (cmd) { case TPA2018_SET_CONFIG: - if (copy_from_user(spk_amp_cfg, argp, sizeof(spk_amp_cfg))) - rc = -EFAULT; + if (copy_from_user(SPK_AMP_CFG, argp, sizeof(SPK_AMP_CFG))) + /* TODO: content validation? */ break; - - case TPA2018_READ_CONFIG: - rc = tpa2018d1_read_config(argp); - break; - case TPA2018_SET_MODE: - if (copy_from_user(&mode, argp, sizeof(mode))) { - rc = -EFAULT; - break; - } + if (copy_from_user(&mode, argp, sizeof(mode))) + return -EFAULT; if (mode >= tpa2018d1_num_modes || mode < 0) { - pr_err("%s: unsupported tpa2018d1 mode %d\n", - __func__, mode); - rc = -EINVAL; - break; + pr_err("unsupported tpa2018d1 mode %d\n", mode); + return -EINVAL; } - if (!config_data) { - pr_err("%s: no config data!\n", __func__); - rc = -EIO; - break; - } - memcpy(spk_amp_cfg, config_data + mode * TPA2018D1_CMD_LEN, - TPA2018D1_CMD_LEN); + memcpy(SPK_AMP_CFG, config_data + mode * TPA2018D1_CMD_LEN, + TPA2018D1_CMD_LEN); break; + case TPA2018_READ_CONFIG: + mutex_lock(&spk_amp_lock); + if (!last_spkamp_state) { + gpio_set_value(pdata->gpio_tpa2018_spk_en, 1); + mdelay(5); /* According to TPA2018D1 Spec */ + } + + rc = tpa2018_i2c_write(reg_idx, sizeof(reg_idx)); + if (rc < 0) + goto err; + rc = tpa2018_i2c_read(tmp, sizeof(tmp)); + if (rc < 0) + goto err; + + if (copy_to_user(argp, &tmp, sizeof(tmp))) + rc = -EFAULT; +err: + if (!last_spkamp_state) + gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); + mutex_unlock(&spk_amp_lock); + break; case TPA2018_SET_PARAM: + cfg.mode_num = 0; + cfg.cmd_data = 0; if (copy_from_user(&cfg, argp, sizeof(cfg))) { pr_err("%s: copy from user failed.\n", __func__); - rc = -EFAULT; - break; + return -EFAULT; } tpa2018d1_num_modes = cfg.mode_num; if (tpa2018d1_num_modes > TPA2018_NUM_MODES) { pr_err("%s: invalid number of modes %d\n", __func__, - tpa2018d1_num_modes); - rc = -EINVAL; - break; + tpa2018d1_num_modes); + return -EINVAL; } if (cfg.data_len != tpa2018d1_num_modes*TPA2018D1_CMD_LEN) { - pr_err("%s: invalid data length %d, expecting %d\n", - __func__, cfg.data_len, - tpa2018d1_num_modes * TPA2018D1_CMD_LEN); - rc = -EINVAL; - break; + pr_err("%s: invalid data length %d, expecting %d\n", + __func__, cfg.data_len, + tpa2018d1_num_modes * TPA2018D1_CMD_LEN); + return -EINVAL; } - /* Free the old data */ - if (config_data) - kfree(config_data); config_data = kmalloc(cfg.data_len, GFP_KERNEL); if (!config_data) { pr_err("%s: out of memory\n", __func__); - rc = -ENOMEM; - break; + return -ENOMEM; } if (copy_from_user(config_data, cfg.cmd_data, cfg.data_len)) { pr_err("%s: copy data from user failed.\n", __func__); kfree(config_data); - config_data = NULL; - rc = -EFAULT; - break; + return -EFAULT; } /* replace default setting with playback setting */ if (tpa2018d1_num_modes >= TPA2018_MODE_PLAYBACK) { offset = TPA2018_MODE_PLAYBACK * TPA2018D1_CMD_LEN; - memcpy(spk_amp_cfg, config_data + offset, + memcpy(SPK_AMP_CFG, config_data + offset, TPA2018D1_CMD_LEN); } break; - default: - pr_err("%s: invalid command %d\n", __func__, _IOC_NR(cmd)); + pr_err("%s: Invalid command\n", __func__); rc = -EINVAL; break; } - mutex_unlock(&spk_amp_lock); return rc; } @@ -256,42 +247,19 @@ static struct miscdevice tpa2018d1_device = { .fops = &tpa2018d1_fops, }; -void tpa2018d1_set_speaker_amp(int on) -{ - if (!pdata) { - pr_err("%s: no platform data!\n", __func__); - return; - } - mutex_lock(&spk_amp_lock); - if (on && !is_on) { - gpio_set_value(pdata->gpio_tpa2018_spk_en, 1); - msleep(5); /* According to TPA2018D1 Spec */ - - if (tpa2018_i2c_write(spk_amp_cfg, sizeof(spk_amp_cfg)) == 0) { - is_on = 1; - pr_info("%s: ON\n", __func__); - } - } else if (!on && is_on) { - if (tpa2018_i2c_write(spk_amp_off, sizeof(spk_amp_off)) == 0) { - is_on = 0; - msleep(2); - gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); - pr_info("%s: OFF\n", __func__); - } - } - mutex_unlock(&spk_amp_lock); -} - -static int tpa2018d1_probe(struct i2c_client *client, const struct i2c_device_id *id) +int tpa2018d1_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret = 0; pdata = client->dev.platform_data; - if (!pdata) { - ret = -EINVAL; - pr_err("%s: platform data is NULL\n", __func__); - goto err_no_pdata; + if (pdata == NULL) { + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (pdata == NULL) { + ret = -ENOMEM; + pr_err("%s: platform data is NULL\n", __func__); + goto err_alloc_data_failed; + } } this_client = client; @@ -299,20 +267,20 @@ static int tpa2018d1_probe(struct i2c_client *client, const struct i2c_device_id ret = gpio_request(pdata->gpio_tpa2018_spk_en, "tpa2018"); if (ret < 0) { pr_err("%s: gpio request aud_spk_en pin failed\n", __func__); - goto err_free_gpio; + goto err_free_gpio_all; } ret = gpio_direction_output(pdata->gpio_tpa2018_spk_en, 1); if (ret < 0) { pr_err("%s: request aud_spk_en gpio direction failed\n", __func__); - goto err_free_gpio; + goto err_free_gpio_all; } if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { pr_err("%s: i2c check functionality error\n", __func__); ret = -ENODEV; - goto err_free_gpio; + goto err_free_gpio_all; } gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); /* Default Low */ @@ -320,17 +288,25 @@ static int tpa2018d1_probe(struct i2c_client *client, const struct i2c_device_id ret = misc_register(&tpa2018d1_device); if (ret) { pr_err("%s: tpa2018d1_device register failed\n", __func__); - goto err_free_gpio; + goto err_free_gpio_all; } - memcpy(spk_amp_cfg, spk_amp_on, sizeof(spk_amp_on)); + memcpy(SPK_AMP_CFG, DEFAULT_SPK_AMP_ON, sizeof(DEFAULT_SPK_AMP_ON)); return 0; -err_free_gpio: +err_free_gpio_all: gpio_free(pdata->gpio_tpa2018_spk_en); -err_no_pdata: +err_alloc_data_failed: return ret; } +static int tpa2018d1_remove(struct i2c_client *client) +{ + struct tpa2018d1_platform_data *p2018data = i2c_get_clientdata(client); + kfree(p2018data); + + return 0; +} + static int tpa2018d1_suspend(struct i2c_client *client, pm_message_t mesg) { return 0; @@ -348,6 +324,7 @@ static const struct i2c_device_id tpa2018d1_id[] = { static struct i2c_driver tpa2018d1_driver = { .probe = tpa2018d1_probe, + .remove = tpa2018d1_remove, .suspend = tpa2018d1_suspend, .resume = tpa2018d1_resume, .id_table = tpa2018d1_id, @@ -359,10 +336,17 @@ static struct i2c_driver tpa2018d1_driver = { static int __init tpa2018d1_init(void) { pr_info("%s\n", __func__); + mutex_init(&spk_amp_lock); return i2c_add_driver(&tpa2018d1_driver); } +static void __exit tpa2018d1_exit(void) +{ + i2c_del_driver(&tpa2018d1_driver); +} + module_init(tpa2018d1_init); +module_exit(tpa2018d1_exit); -MODULE_DESCRIPTION("tpa2018d1 speaker amp driver"); +MODULE_DESCRIPTION("TPA2018D1 Speaker Amp driver"); MODULE_LICENSE("GPL"); From 813600f484a2fd7d522ef389648f3a063e738c20 Mon Sep 17 00:00:00 2001 From: agrabren Date: Tue, 1 Mar 2011 01:22:09 -0500 Subject: [PATCH 1459/2556] video: msm: changes to mdp_ppp to enable HDMI mirror mode --- drivers/video/msm/hdmi/fb-hdmi.c | 186 ++++++++++++++++++++++++++- drivers/video/msm/hdmi/transmitter.c | 2 +- drivers/video/msm/mdp.c | 9 ++ drivers/video/msm/mdp_ppp.c | 12 ++ drivers/video/msm/msm_fb.c | 43 +++++-- include/linux/htc_hdmi.h | 31 ++++- 6 files changed, 262 insertions(+), 21 deletions(-) diff --git a/drivers/video/msm/hdmi/fb-hdmi.c b/drivers/video/msm/hdmi/fb-hdmi.c index ba6a309c7ba94..28a5b525b2a46 100644 --- a/drivers/video/msm/hdmi/fb-hdmi.c +++ b/drivers/video/msm/hdmi/fb-hdmi.c @@ -46,6 +46,8 @@ #define HDMI_DBG(s...) do {} while (0) #endif +#define BITS_PER_PIXEL 16 + struct update_info_t { int left; int top; @@ -68,6 +70,12 @@ struct hdmifb_info { int yres; unsigned long state; atomic_t use_count; + int vsyncMode; + int mirroring; + int doubleBuffering; + struct mdp_blit_req* vsyncBlitReq; + struct mdp_blit_req mirrorReq; + struct mirror_statistics mirror_stats; }; static struct mdp_device *mdp; @@ -291,13 +299,143 @@ static int hdmifb_blit(struct fb_info *info, void __user *p) if (copy_from_user(&req, &list->req[i], sizeof(req))) return -EFAULT; req.flags |= MDP_DITHER; - ret = mdp->blit(mdp, info, &req); + + /* Copy the requested blit in case we're mirroring */ + if (_hdmi_fb->mirroring) + { + memcpy(&_hdmi_fb->mirrorReq, &req, sizeof(struct mdp_blit_req)); + + /* Default double-buffering off */ + _hdmi_fb->doubleBuffering = 0; + + /* Are we rotating? */ + if ((req.flags & MDP_ROT_MASK) == MDP_ROT_90 || (req.flags & MDP_ROT_MASK) == MDP_ROT_270) + { + /* Are we scaling at the same time? */ + if (req.src_rect.w != req.dst_rect.w || req.src_rect.h != req.dst_rect.h) + { + _hdmi_fb->doubleBuffering = 1; + } + else + { + // Switch us back to buffer 0 + mdp->dma(mdp, _hdmi_fb->fb->fix.smem_start, _hdmi_fb->fb->var.xres * 2, + _hdmi_fb->fb->var.xres, _hdmi_fb->fb->var.yres, 0, 0, + &_hdmi_fb->dma_callback, _hdmi_fb->panel->interface_type); + } + } + } + + ret = mdp->blit(mdp, info, &req); if (ret) return ret; } return 0; } +void reportUnderflow(void) +{ + if (_hdmi_fb) _hdmi_fb->mirror_stats.underflows++; +} + +int hdmi_usePanelSync(void) +{ + if (_hdmi_fb->mirroring && + (_hdmi_fb->vsyncMode == VSYNC_NONE || _hdmi_fb->vsyncMode == VSYNC_HDMI_ONLY)) + return 0; + return 1; +} + +int hdmi_useHdmiSync(void) +{ + if (_hdmi_fb->mirroring && + (_hdmi_fb->vsyncMode == VSYNC_NONE || _hdmi_fb->vsyncMode == VSYNC_PANEL_ONLY)) + return 0; + return 1; +} + +void blit_on_vsync(struct mdp_blit_req* blitReq) +{ + struct msm_panel_data *panel = _hdmi_fb->panel; + + /* Request mirror blit and vsync callback */ + _hdmi_fb->vsyncBlitReq = blitReq; + panel->request_vsync(panel, &_hdmi_fb->vsync_callback); + return; +} + +void hdmi_DoBlit(int fb0Offset) +{ + int yoffset = 0; + + /* Always track frame counts, so we can measure FPS of panel-only mode */ + _hdmi_fb->mirror_stats.frames++; + + /* This handles the disabled case */ + if (_hdmi_fb->mirroring == 0 || _hdmi_fb->mirrorReq.src.width == 0) + return; + + /* Verify HDMI is enabled */ + if ((test_bit(fb_enabled, &_hdmi_fb->state) == 0) || + (test_bit(hdmi_enabled, &_hdmi_fb->state) == 0)) + return; + + /* Set the proper offsets */ + _hdmi_fb->mirrorReq.src.offset = fb0Offset; + + if (_hdmi_fb->doubleBuffering) + { + if (_hdmi_fb->mirrorReq.dst.offset == 0) + { + yoffset = _hdmi_fb->mirrorReq.dst.height; + _hdmi_fb->mirrorReq.dst.offset = yoffset * _hdmi_fb->mirrorReq.dst.width * BITS_PER_PIXEL / 8; + } + else + { + _hdmi_fb->mirrorReq.dst.offset = 0; + yoffset = 0; + } + } + else + { + if (_hdmi_fb->mirrorReq.dst.offset != 0) + { + // Force us back to the primary buffer + _hdmi_fb->mirrorReq.dst.offset = 0; + mdp->dma(mdp, _hdmi_fb->fb->fix.smem_start, _hdmi_fb->fb->var.xres * 2, + _hdmi_fb->fb->var.xres, _hdmi_fb->fb->var.yres, 0, 0, + &_hdmi_fb->dma_callback, _hdmi_fb->panel->interface_type); + } + } + + /* Either schedule the blit on vsync, or do it now */ + if (hdmi_useHdmiSync() && !_hdmi_fb->doubleBuffering) + { + blit_on_vsync(&_hdmi_fb->mirrorReq); + } + else + { + mdp->blit(mdp, _hdmi_fb->fb, &_hdmi_fb->mirrorReq); + } + + if (_hdmi_fb->doubleBuffering) + { + if (hdmi_useHdmiSync()) + { + hdmifb_pan_update(_hdmi_fb->fb, 0, 0, + _hdmi_fb->fb->var.xres, _hdmi_fb->fb->var.yres, yoffset); + } + else + { + mdp->dma(mdp, _hdmi_fb->mirrorReq.dst.offset + _hdmi_fb->fb->fix.smem_start, + _hdmi_fb->fb->var.xres * 2, _hdmi_fb->fb->var.xres, _hdmi_fb->fb->var.yres, 0, 0, + &_hdmi_fb->dma_callback, _hdmi_fb->panel->interface_type); + } + } + + return; +} + enum ioctl_cmd_index { CMD_SET_MODE, CMD_GET_MODE, @@ -340,6 +478,7 @@ static int hdmifb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg) get_user(val, (unsigned __user *) arg); //pr_info("[hdmi] SET_MODE: %d\n", val); ret = hdmifb_change_mode(p, val); + _hdmi_fb->mirroring = 0; break; case HDMI_GET_MODE: /* @@ -351,6 +490,7 @@ static int hdmifb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg) break; case HDMI_DISABLE: get_user(val, (unsigned __user *) arg); + _hdmi_fb->mirroring = 0; ret = hdmifb_pause(p, 1); break; case HDMI_ENABLE: @@ -400,6 +540,37 @@ static int hdmifb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg) &dinfo, sizeof(dinfo)); break; } + + case HDMI_GET_MIRRORING: + ret = put_user(hdmi_fb->mirroring, (unsigned __user *) arg); + break; + case HDMI_SET_MIRRORING: + get_user(val, (unsigned __user *) arg); + hdmi_fb->mirroring = val; + memset(&hdmi_fb->mirror_stats, 0, sizeof(struct mirror_statistics)); + hdmi_fb->mirror_stats.statisticsTime = ktime_to_ns(ktime_get()); + break; + case HDMI_GET_STATISTICS: + { + struct mirror_statistics temp; + + memcpy(&temp, &hdmi_fb->mirror_stats, sizeof(struct mirror_statistics)); + temp.statisticsTime = ktime_to_ns(ktime_get()) - hdmi_fb->mirror_stats.statisticsTime; + ret = copy_to_user((unsigned __user *) arg, &temp, sizeof(struct mirror_statistics)); + } + break; + case HDMI_CLEAR_STATISTICS: + memset(&hdmi_fb->mirror_stats, 0, sizeof(struct mirror_statistics)); + hdmi_fb->mirror_stats.statisticsTime = ktime_to_ns(ktime_get()); + break; + case HDMI_GET_VSYNC_MODE: + ret = put_user(hdmi_fb->vsyncMode, (unsigned __user *) arg); + break; + case HDMI_SET_VSYNC_MODE: + get_user(val, (unsigned __user *) arg); + hdmi_fb->vsyncMode = val; + ret = 0; + break; default: printk(KERN_ERR "hdmi: unknown cmd, cmd = %d\n", cmd); } @@ -460,8 +631,6 @@ static void hdmifb_resume(struct early_suspend *h) } #endif -#define BITS_PER_PIXEL 16 - static void setup_fb_info(struct hdmifb_info *hdmi_fb) { struct fb_info *fb_info = hdmi_fb->fb; @@ -560,6 +729,14 @@ static void hdmi_handle_vsync(struct msmfb_callback *callback) vsync_callback); struct msm_panel_data *panel = hdmi->panel; + /* Handle blitting requests */ + if (hdmi->vsyncBlitReq) + { + mdp->blit(mdp, _hdmi_fb->fb, hdmi->vsyncBlitReq); + hdmi->vsyncBlitReq = NULL; + return; + } + spin_lock_irqsave(&hdmi->update_lock, irq_flags); x = hdmi->update_info.left; y = hdmi->update_info.top; @@ -617,6 +794,9 @@ static int hdmifb_probe(struct platform_device *pdev) if (!info) return -ENOMEM; + /* Zero out the structure before using it */ + memset(info, 0, sizeof(struct hdmifb_info)); + hdmi_fb = info->par; _hdmi_fb = hdmi_fb; hdmi_fb->fb = info; diff --git a/drivers/video/msm/hdmi/transmitter.c b/drivers/video/msm/hdmi/transmitter.c index f0c56b85a85b8..0f2a7da805886 100644 --- a/drivers/video/msm/hdmi/transmitter.c +++ b/drivers/video/msm/hdmi/transmitter.c @@ -382,7 +382,7 @@ static int hdmi_get_cable_state(struct hdmi_device *hdmi_device, int *connect) container_of(hdmi_device, struct hdmi_info, hdmi_dev); *connect = hdmi->cable_connected; #endif - HDMI_DBG("%s, state=%s\n", __func__, *connect ? "on" : "off" ); +// HDMI_DBG("%s, state=%s\n", __func__, *connect ? "on" : "off" ); return 0; } diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 0fe5fa781109d..49ab7f6f808ee 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -36,6 +36,9 @@ struct class *mdp_class; +/* Used to report LCDC underflows */ +void reportUnderflow(void); + #define MDP_CMD_DEBUG_ACCESS_BASE (0x10000) static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue); @@ -172,6 +175,12 @@ static irqreturn_t mdp_isr(int irq, void *data) mdp_dma_timer_enable = 0; } + if (status & MDP_LCDC_UNDERFLOW) + { + pr_err("%s: LCDC Underflow\n", __func__); + reportUnderflow(); + } + status &= mdp_irq_mask; #ifdef CONFIG_MSM_MDP40 if (mdp->mdp_dev.overrides & MSM_MDP4_MDDI_DMA_SWITCH) { diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c index 8f563236366bb..56ecedfd3fd14 100644 --- a/drivers/video/msm/mdp_ppp.c +++ b/drivers/video/msm/mdp_ppp.c @@ -685,6 +685,18 @@ static int get_img(struct mdp_img *img, struct fb_info *info, struct file *file; unsigned long vstart; + if (img->memory_id & 0x40000000) + { + struct fb_info *fb = registered_fb[img->memory_id & 0x0000FFFF]; + if (fb) + { + *start = fb->fix.smem_start; + *len = fb->fix.smem_len; + } + *filep = NULL; + return 0; + } + if (!get_pmem_file(img->memory_id, start, &vstart, len, filep)) return 0; else if (!get_msm_hw3d_file(img->memory_id, &img->offset, start, len, diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index 588fe7ca0f5dd..53ee34f459f35 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -39,6 +39,9 @@ extern void start_drawing_late_resume(struct early_suspend *h); static void msmfb_resume_handler(struct early_suspend *h); static void msmfb_resume(struct work_struct *work); +void hdmi_DoBlit(int offset); +int hdmi_usePanelSync(void); + #define MSMFB_DEBUG 1 #ifdef CONFIG_FB_MSM_LOGO #define INIT_IMAGE_FILE "/logo.rle" @@ -189,6 +192,7 @@ static void msmfb_handle_dma_interrupt(struct msmfb_callback *callback) spin_lock_irqsave(&msmfb->update_lock, irq_flags); msmfb->frame_done = msmfb->frame_requested; + if (msmfb->sleeping == UPDATING && msmfb->frame_done == msmfb->update_frame) { DLOG(SUSPEND_RESUME, "full update completed\n"); @@ -415,19 +419,32 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, msmfb->yoffset); spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); - /* if the panel is all the way on wait for vsync, otherwise sleep - * for 16 ms (long enough for the dma to panel) and then begin dma */ - msmfb->vsync_request_time = ktime_get(); - if (panel->request_vsync && (sleeping == AWAKE)) { - wake_lock_timeout(&msmfb->idle_lock, HZ/4); - panel->request_vsync(panel, &msmfb->vsync_callback); - } else { - if (!hrtimer_active(&msmfb->fake_vsync)) { - hrtimer_start(&msmfb->fake_vsync, - ktime_set(0, NSEC_PER_SEC/60), - HRTIMER_MODE_REL); - } - } + if (!hdmi_usePanelSync()) + { + msmfb->vsync_request_time = ktime_get(); + msmfb_start_dma(msmfb); + } + else + { + /* if the panel is all the way on wait for vsync, otherwise sleep + * for 16 ms (long enough for the dma to panel) and then begin dma */ + msmfb->vsync_request_time = ktime_get(); + if (panel->request_vsync && (sleeping == AWAKE)) { + wake_lock_timeout(&msmfb->idle_lock, HZ/4); + panel->request_vsync(panel, &msmfb->vsync_callback); + } else { + if (!hrtimer_active(&msmfb->fake_vsync)) { + hrtimer_start(&msmfb->fake_vsync, + ktime_set(0, NSEC_PER_SEC/60), + HRTIMER_MODE_REL); + } + } + } + + /* We did the DMA, now blit the data to the other display */ + hdmi_DoBlit(msmfb->xres * msmfb->yoffset * BYTES_PER_PIXEL(msmfb)); + + return; } static void msmfb_update(struct fb_info *info, uint32_t left, uint32_t top, diff --git a/include/linux/htc_hdmi.h b/include/linux/htc_hdmi.h index 1ad580ff6d127..315ee5f2312ac 100644 --- a/include/linux/htc_hdmi.h +++ b/include/linux/htc_hdmi.h @@ -34,6 +34,13 @@ enum { #define HDMI_GET_EDID _IOR(HDMI_IOCTL_MAGIC, 9, unsigned) #define HDMI_GET_DISPLAY_INFO _IOR(HDMI_IOCTL_MAGIC, 10, unsigned) +#define HDMI_GET_MIRRORING _IOR(HDMI_IOCTL_MAGIC, 30, unsigned) +#define HDMI_SET_MIRRORING _IOW(HDMI_IOCTL_MAGIC, 31, unsigned) +#define HDMI_GET_STATISTICS _IOR(HDMI_IOCTL_MAGIC, 32, unsigned) +#define HDMI_CLEAR_STATISTICS _IOW(HDMI_IOCTL_MAGIC, 33, unsigned) +#define HDMI_GET_VSYNC_MODE _IOR(HDMI_IOCTL_MAGIC, 34, unsigned) +#define HDMI_SET_VSYNC_MODE _IOW(HDMI_IOCTL_MAGIC, 35, unsigned) + #define ASPECT(w, h) (w << 8 | h) struct video_mode { unsigned short width, height, refresh_rate, aspect; @@ -47,10 +54,26 @@ enum { }; struct display_info { - unsigned int visible_width; /* in mm */ - unsigned int visible_height; - unsigned int resolution_width; /* in pixel */ - unsigned int resolution_height; + unsigned int visible_width; /* in mm */ + unsigned int visible_height; + unsigned int resolution_width; /* in pixel */ + unsigned int resolution_height; +}; + +/* Gathered statistics for mirroring */ +struct mirror_statistics { + unsigned int frames; /* Number of panel frames requested */ + unsigned int underflows; /* Number of times we underflowed the LCDC */ + s64 statisticsTime; /* Mirror time, in ns */ +}; + +/* Panel state while mirroring */ +enum { + VSYNC_ALL = 0, + VSYNC_PANEL_ONLY, + VSYNC_HDMI_ONLY, + VSYNC_NONE }; #endif + From 5a70c2f1d8216f5518d02834df5812bc4e32957d Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 12 Apr 2011 12:48:33 -0400 Subject: [PATCH 1460/2556] video: msm: Fix ifdef fail which caused tearing --- drivers/video/msm/msm_fb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index 53ee34f459f35..cb4adf6e45cb3 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -426,6 +426,7 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, } else { +#endif /* if the panel is all the way on wait for vsync, otherwise sleep * for 16 ms (long enough for the dma to panel) and then begin dma */ msmfb->vsync_request_time = ktime_get(); @@ -439,6 +440,7 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, HRTIMER_MODE_REL); } } +#ifdef CONFIG_MSM_HDMI } /* We did the DMA, now blit the data to the other display */ From 4c161a3371d5292b27534e513d157451b418cc34 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Sun, 24 Apr 2011 21:12:30 -0400 Subject: [PATCH 1461/2556] supersonic: Fix include for cm3602 --- arch/arm/mach-msm/board-supersonic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index 7a97ebd604a15..be1a22ec802af 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include From 59f49aa810c49929cf36df86ec3f5c9aa71edf01 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sat, 30 Apr 2011 17:10:21 -0400 Subject: [PATCH 1462/2556] Revert "video: msm: Fix ifdef fail which caused tearing" This reverts commit e64c4049bdcc11d635c90a2751ba8333d03cf9a0. --- drivers/video/msm/msm_fb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index cb4adf6e45cb3..d3fb8e43fd784 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -421,12 +421,13 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, if (!hdmi_usePanelSync()) { +#endif msmfb->vsync_request_time = ktime_get(); msmfb_start_dma(msmfb); +#ifdef CONFIG_MSM_HDMI } else { -#endif /* if the panel is all the way on wait for vsync, otherwise sleep * for 16 ms (long enough for the dma to panel) and then begin dma */ msmfb->vsync_request_time = ktime_get(); @@ -440,7 +441,6 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, HRTIMER_MODE_REL); } } -#ifdef CONFIG_MSM_HDMI } /* We did the DMA, now blit the data to the other display */ From f1c7a2c5f32d210636d35a8d3dbf83c007996805 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Sun, 10 Apr 2011 20:37:26 -0400 Subject: [PATCH 1463/2556] msm: Fix build on non-supersonic devices --- arch/arm/mach-msm/Kconfig | 11 ++++++----- arch/arm/mach-msm/Makefile | 1 - arch/arm/mach-msm/board-halibut.c | 10 ---------- arch/arm/mach-msm/board-mahimahi-audio.c | 10 ---------- arch/arm/mach-msm/board-mahimahi-mmc.c | 10 ---------- arch/arm/mach-msm/board-mahimahi.c | 14 -------------- arch/arm/mach-msm/board-supersonic-microp.c | 2 +- arch/arm/mach-msm/board-swordfish-mmc.c | 19 ------------------- arch/arm/mach-msm/board-trout.c | 10 ---------- drivers/video/msm/mdp.c | 4 ++++ drivers/video/msm/msm_fb.c | 5 ++++- include/linux/capella_cm3602.h | 2 +- 12 files changed, 16 insertions(+), 82 deletions(-) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 3b9b4715f8ea5..a76e2321a82b5 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -201,7 +201,7 @@ config MACH_MSM7X30_FLUID config MACH_SWORDFISH depends on ARCH_QSD8X50 - default y + default n bool "Swordfish Board (QCT SURF8250)" help Support for the Qualcomm SURF8250 eval board. @@ -295,21 +295,21 @@ config MACH_MAHIMAHI config MACH_SUPERSONIC depends on ARCH_QSD8X50 - default y + default n bool "Supersonic (HTC EVO 4G)" help Select this to support the Supersonic device config MACH_QSD8X50_FFA depends on ARCH_QSD8X50 - default y + default n bool "8x50-ffa" help Select this to support the 8x50 ffa device config MACH_MSM7X30_SURF depends on ARCH_MSM7X30 - default y + default n bool "QCT SURF7x30 Board" help Select this to support the Qualcomm SURF7X30 development board @@ -617,6 +617,7 @@ config MSM_ADSP config HTC_ACOUSTIC tristate "HTC acoustic driver" + depends on ARCH_MSM7X00A default y help The driver provide user space use shared memory allocate by using @@ -625,7 +626,7 @@ config HTC_ACOUSTIC config HTC_ACOUSTIC_QSD tristate "HTC acoustic driver for QSD" depends on ARCH_QSD8X50 - default n + default y help Provides user space use shared memory allocate by using RPC code. Provides headset amp, mic bias and speaker amp control. diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 1f5a73262b265..d16bb101fecd3 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -67,7 +67,6 @@ obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-rfkill.o htc_wifi_nvs.o htc_awb_ca obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-wifi.o board-mahimahi-audio.o obj-$(CONFIG_MACH_MAHIMAHI) += msm_vibrator.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-microp.o -obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-flashlight.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o diff --git a/arch/arm/mach-msm/board-halibut.c b/arch/arm/mach-msm/board-halibut.c index 0c37a36df6cd2..42a4dc38a6d65 100644 --- a/arch/arm/mach-msm/board-halibut.c +++ b/arch/arm/mach-msm/board-halibut.c @@ -131,16 +131,6 @@ static uint32_t camera_on_gpio_table[] = { PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_16MA), /* MCLK */ }; -static void config_gpio_table(uint32_t *table, int len) -{ - int n; - unsigned id; - for (n = 0; n < len; n++) { - id = table[n]; - msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); - } -} - static void config_camera_on_gpios(void) { config_gpio_table(camera_on_gpio_table, diff --git a/arch/arm/mach-msm/board-mahimahi-audio.c b/arch/arm/mach-msm/board-mahimahi-audio.c index c6c7db37be861..6cb01b11edd48 100644 --- a/arch/arm/mach-msm/board-mahimahi-audio.c +++ b/arch/arm/mach-msm/board-mahimahi-audio.c @@ -129,16 +129,6 @@ void mahimahi_receiver_enable(int en) } } -static void config_gpio_table(uint32_t *table, int len) -{ - int n; - unsigned id; - for (n = 0; n < len; n++) { - id = table[n]; - msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); - } -} - static uint32_t bt_sco_enable[] = { PCOM_GPIO_CFG(MAHIMAHI_BT_PCM_OUT, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), diff --git a/arch/arm/mach-msm/board-mahimahi-mmc.c b/arch/arm/mach-msm/board-mahimahi-mmc.c index 2d339e25ced43..de9463d580709 100644 --- a/arch/arm/mach-msm/board-mahimahi-mmc.c +++ b/arch/arm/mach-msm/board-mahimahi-mmc.c @@ -45,16 +45,6 @@ static int __init mahimahi_disablesdcard_setup(char *str) __setup("board_mahimahi.disable_sdcard=", mahimahi_disablesdcard_setup); -static void config_gpio_table(uint32_t *table, int len) -{ - int n; - unsigned id; - for(n = 0; n < len; n++) { - id = table[n]; - msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); - } -} - static uint32_t sdcard_on_gpio_table[] = { PCOM_GPIO_CFG(62, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ PCOM_GPIO_CFG(63, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 618e7c9c5207f..b213bff533fb7 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -70,8 +70,6 @@ extern void __init mahimahi_audio_init(void); extern int microp_headset_has_mic(void); -static void config_gpio_table(uint32_t *table, int len); - static int mahimahi_phy_init_seq[] = { 0x0C, 0x31, 0x31, 0x32, @@ -550,8 +548,6 @@ static struct i2c_board_info rev_CX_i2c_devices[] = { }, }; -static void config_gpio_table(uint32_t *table, int len); - static uint32_t camera_off_gpio_table[] = { /* CAMERA */ PCOM_GPIO_CFG(0, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT0 */ @@ -969,16 +965,6 @@ static int __init board_serialno_setup(char *serialno) } __setup("androidboot.serialno=", board_serialno_setup); -static void config_gpio_table(uint32_t *table, int len) -{ - int n; - unsigned id; - for(n = 0; n < len; n++) { - id = table[n]; - msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); - } -} - static struct msm_acpu_clock_platform_data mahimahi_clock_data = { .acpu_switch_time_us = 20, .max_speed_delta_khz = 256000, diff --git a/arch/arm/mach-msm/board-supersonic-microp.c b/arch/arm/mach-msm/board-supersonic-microp.c index 8b89bd3b34c86..c1b8f4def28d2 100644 --- a/arch/arm/mach-msm/board-supersonic-microp.c +++ b/arch/arm/mach-msm/board-supersonic-microp.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-msm/board-swordfish-mmc.c b/arch/arm/mach-msm/board-swordfish-mmc.c index bb0b1734b6072..d4424e3a7ca3a 100644 --- a/arch/arm/mach-msm/board-swordfish-mmc.c +++ b/arch/arm/mach-msm/board-swordfish-mmc.c @@ -42,25 +42,6 @@ static void __iomem *fpga_base; extern int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, unsigned int stat_irq, unsigned long stat_irq_flags); -static int config_gpio_table(unsigned *table, int len, int enable) -{ - int n; - int rc = 0; - - for (n = 0; n < len; n++) { - unsigned dis = !enable; - unsigned id = table[n]; - - if (msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, &dis)) { - pr_err("%s: id=0x%08x dis=%d\n", __func__, table[n], - dis); - rc = -1; - } - } - - return rc; -} - static unsigned sdc1_gpio_table[] = { PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index d69cc21d59fdc..ddff77eae5263 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -723,16 +723,6 @@ static uint32_t camera_on_gpio_table[] = { PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_16MA), /* MCLK */ }; -static void config_gpio_table(uint32_t *table, int len) -{ - int n; - unsigned id; - for(n = 0; n < len; n++) { - id = table[n]; - msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); - } -} - static void config_camera_on_gpios(void) { config_gpio_table(camera_on_gpio_table, diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 49ab7f6f808ee..19b4d321556ac 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -36,8 +36,10 @@ struct class *mdp_class; +#ifdef CONFIG_MSM_HDMI /* Used to report LCDC underflows */ void reportUnderflow(void); +#endif #define MDP_CMD_DEBUG_ACCESS_BASE (0x10000) @@ -175,11 +177,13 @@ static irqreturn_t mdp_isr(int irq, void *data) mdp_dma_timer_enable = 0; } +#ifdef CONFIG_MSM_HDMI if (status & MDP_LCDC_UNDERFLOW) { pr_err("%s: LCDC Underflow\n", __func__); reportUnderflow(); } +#endif status &= mdp_irq_mask; #ifdef CONFIG_MSM_MDP40 diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index d3fb8e43fd784..6ea49327f095c 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -39,8 +39,10 @@ extern void start_drawing_late_resume(struct early_suspend *h); static void msmfb_resume_handler(struct early_suspend *h); static void msmfb_resume(struct work_struct *work); +#ifdef CONFIG_MSM_HDMI void hdmi_DoBlit(int offset); int hdmi_usePanelSync(void); +#endif #define MSMFB_DEBUG 1 #ifdef CONFIG_FB_MSM_LOGO @@ -419,6 +421,7 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, msmfb->yoffset); spin_unlock_irqrestore(&msmfb->update_lock, irq_flags); +#ifdef CONFIG_MSM_HDMI if (!hdmi_usePanelSync()) { #endif @@ -445,7 +448,7 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, /* We did the DMA, now blit the data to the other display */ hdmi_DoBlit(msmfb->xres * msmfb->yoffset * BYTES_PER_PIXEL(msmfb)); - +#endif return; } diff --git a/include/linux/capella_cm3602.h b/include/linux/capella_cm3602.h index c08e30209276e..cf09da0c18aa9 100644 --- a/include/linux/capella_cm3602.h +++ b/include/linux/capella_cm3602.h @@ -29,7 +29,7 @@ #ifdef __KERNEL__ #define CAPELLA_CM3602 "capella_cm3602" struct capella_cm3602_platform_data { - int (*power)(int, uint8_t); /* power to the chip */ + int (*power)(int); /* power to the chip */ int p_out; /* proximity-sensor output */ int p_en; }; From 0b6b51df7f1b0b9646702895ffa66f5e9e035873 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 12 Apr 2011 12:48:33 -0400 Subject: [PATCH 1464/2556] video: msm: Fix ifdef fail which caused tearing --- drivers/video/msm/msm_fb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index 6ea49327f095c..513913526c1d8 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -424,13 +424,12 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, #ifdef CONFIG_MSM_HDMI if (!hdmi_usePanelSync()) { -#endif msmfb->vsync_request_time = ktime_get(); msmfb_start_dma(msmfb); -#ifdef CONFIG_MSM_HDMI } else { +#endif /* if the panel is all the way on wait for vsync, otherwise sleep * for 16 ms (long enough for the dma to panel) and then begin dma */ msmfb->vsync_request_time = ktime_get(); @@ -444,6 +443,7 @@ static void msmfb_pan_update(struct fb_info *info, uint32_t left, uint32_t top, HRTIMER_MODE_REL); } } +#ifdef CONFIG_MSM_HDMI } /* We did the DMA, now blit the data to the other display */ From f4776c15a184220487a41c25879a5b8e802e4db6 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sat, 30 Apr 2011 19:38:35 -0400 Subject: [PATCH 1465/2556] include: linux: add capella_cm3602 for the htc version of the driver --- include/linux/capella_cm3602_htc.h | 42 ++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 include/linux/capella_cm3602_htc.h diff --git a/include/linux/capella_cm3602_htc.h b/include/linux/capella_cm3602_htc.h new file mode 100644 index 0000000000000..0aaf5c9c8bc4f --- /dev/null +++ b/include/linux/capella_cm3602_htc.h @@ -0,0 +1,42 @@ +/* include/linux/capella_cm3602.h + * + * Copyright (C) 2009 Google, Inc. + * Author: Iliyan Malchev + * + * 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_CAPELLA_CM3602_H +#define __LINUX_CAPELLA_CM3602_H + +#include +#include + +#define CAPELLA_CM3602_IOCTL_MAGIC 'c' +#define CAPELLA_CM3602_IOCTL_GET_ENABLED \ + _IOR(CAPELLA_CM3602_IOCTL_MAGIC, 1, int *) +#define CAPELLA_CM3602_IOCTL_ENABLE \ + _IOW(CAPELLA_CM3602_IOCTL_MAGIC, 2, int *) + +#ifdef __KERNEL__ +#define CAPELLA_CM3602 "capella_cm3602" +#define LS_PWR_ON (1 << 0) +#define PS_PWR_ON (1 << 1) +struct capella_cm3602_platform_data { + int (*power)(int, uint8_t); /* power to the chip */ + int (*enable)(uint8_t); /* enable to the chip */ + int p_out; /* proximity-sensor output */ + int p_en; /* proximity-sensor enable */ + int irq; +}; +#endif /* __KERNEL__ */ + +#endif From e187c0b50784a018451aabfdab05ec282c6b122d Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sat, 30 Apr 2011 19:40:35 -0400 Subject: [PATCH 1466/2556] drvicers: mmc: remove static on msmsdcc_request_end as its now a exported symbol --- drivers/mmc/host/msm_sdcc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 6a8689caab378..50a711efddffe 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -322,7 +322,7 @@ static void msmsdcc_reset_and_restore(struct msmsdcc_host *host) mmc_hostname(host->mmc), host->clk_rate, ret); } -static void +void msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq) { BUG_ON(host->curr.data); From 5e2962a002c6afb9034f5ded2d69137ac6252ad5 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sat, 30 Apr 2011 19:49:59 -0400 Subject: [PATCH 1467/2556] configs: supersonic: update defconfig --- .../arm/configs/cyanogen_supersonic_defconfig | 1038 +++++++---------- 1 file changed, 406 insertions(+), 632 deletions(-) diff --git a/arch/arm/configs/cyanogen_supersonic_defconfig b/arch/arm/configs/cyanogen_supersonic_defconfig index d814fde885d4f..710b72e47c39c 100644 --- a/arch/arm/configs/cyanogen_supersonic_defconfig +++ b/arch/arm/configs/cyanogen_supersonic_defconfig @@ -1,77 +1,45 @@ # # Automatically generated make config: don't edit -# Linux/x86_64 2.6.37 Kernel Configuration -# Mon Jan 24 21:20:52 2011 -# -CONFIG_64BIT=y -# CONFIG_X86_32 is not set -CONFIG_X86_64=y -CONFIG_X86=y -CONFIG_INSTRUCTION_DECODER=y -CONFIG_OUTPUT_FORMAT="elf64-x86-64" -CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig" -CONFIG_GENERIC_CMOS_UPDATE=y -CONFIG_CLOCKSOURCE_WATCHDOG=y +# Linux/arm 2.6.38.4 Kernel Configuration +# Sat Apr 30 18:43:34 2011 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set CONFIG_GENERIC_CLOCKEVENTS=y -CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y -CONFIG_LOCKDEP_SUPPORT=y +CONFIG_HAVE_PROC_CPU=y CONFIG_STACKTRACE_SUPPORT=y CONFIG_HAVE_LATENCYTOP_SUPPORT=y -CONFIG_MMU=y -CONFIG_ZONE_DMA=y -CONFIG_NEED_DMA_MAP_STATE=y -CONFIG_NEED_SG_DMA_LENGTH=y -CONFIG_GENERIC_ISA_DMA=y -CONFIG_GENERIC_IOMAP=y -CONFIG_GENERIC_BUG=y -CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y -CONFIG_GENERIC_HWEIGHT=y -CONFIG_GENERIC_GPIO=y -CONFIG_ARCH_MAY_HAVE_PC_FDC=y -# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_CPUFREQ=y CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y CONFIG_GENERIC_CALIBRATE_DELAY=y -CONFIG_GENERIC_TIME_VSYSCALL=y -CONFIG_ARCH_HAS_CPU_RELAX=y -CONFIG_ARCH_HAS_DEFAULT_IDLE=y -CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y -CONFIG_HAVE_SETUP_PER_CPU_AREA=y -CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y -CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y -# CONFIG_HAVE_CPUMASK_OF_CPU_MAP is not set -CONFIG_ARCH_HIBERNATION_POSSIBLE=y -CONFIG_ARCH_SUSPEND_POSSIBLE=y -CONFIG_ZONE_DMA32=y -CONFIG_ARCH_POPULATES_NODE_MAP=y -CONFIG_AUDIT_ARCH=y -CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING=y -CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y -CONFIG_X86_TRAMPOLINE=y -CONFIG_ARCH_HWEIGHT_CFLAGS="-fcall-saved-rdi -fcall-saved-rsi -fcall-saved-rdx -fcall-saved-rcx -fcall-saved-r8 -fcall-saved-r9 -fcall-saved-r10 -fcall-saved-r11" -# CONFIG_KTIME_SCALAR is not set +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" CONFIG_CONSTRUCTORS=y CONFIG_HAVE_IRQ_WORK=y -CONFIG_IRQ_WORK=y # # General setup # CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y -CONFIG_LOCK_KERNEL=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" CONFIG_LOCALVERSION="-cyanogenmod" -# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_LOCALVERSION_AUTO=y CONFIG_HAVE_KERNEL_GZIP=y -CONFIG_HAVE_KERNEL_BZIP2=y CONFIG_HAVE_KERNEL_LZMA=y CONFIG_HAVE_KERNEL_LZO=y -CONFIG_KERNEL_GZIP=y -# CONFIG_KERNEL_BZIP2 is not set -# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_GZIP is not set +CONFIG_KERNEL_LZMA=y # CONFIG_KERNEL_LZO is not set # CONFIG_SWAP is not set # CONFIG_SYSVIPC is not set @@ -85,31 +53,25 @@ CONFIG_HAVE_GENERIC_HARDIRQS=y # IRQ subsystem # CONFIG_GENERIC_HARDIRQS=y -CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y # CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED is not set CONFIG_HAVE_SPARSE_IRQ=y -CONFIG_GENERIC_IRQ_PROBE=y # CONFIG_GENERIC_PENDING_IRQ is not set # CONFIG_AUTO_IRQ_AFFINITY is not set # CONFIG_IRQ_PER_CPU is not set -# CONFIG_HARDIRQS_SW_RESEND is not set # CONFIG_SPARSE_IRQ is not set # # RCU Subsystem # -CONFIG_TREE_PREEMPT_RCU=y -# CONFIG_TINY_RCU is not set +# CONFIG_TREE_PREEMPT_RCU is not set +CONFIG_TINY_RCU=y # CONFIG_TINY_PREEMPT_RCU is not set -CONFIG_PREEMPT_RCU=y +# CONFIG_PREEMPT_RCU is not set # CONFIG_RCU_TRACE is not set -CONFIG_RCU_FANOUT=32 -# CONFIG_RCU_FANOUT_EXACT is not set # CONFIG_TREE_RCU_TRACE is not set CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=17 -CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y CONFIG_CGROUPS=y # CONFIG_CGROUP_DEBUG is not set # CONFIG_CGROUP_NS is not set @@ -121,9 +83,9 @@ CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_SCHED=y CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y -CONFIG_BLK_CGROUP=y -# CONFIG_DEBUG_BLK_CGROUP is not set +# CONFIG_BLK_CGROUP is not set # CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set # CONFIG_SYSFS_DEPRECATED is not set # CONFIG_RELAY is not set CONFIG_BLK_DEV_INITRD=y @@ -131,12 +93,15 @@ CONFIG_INITRAMFS_SOURCE="" CONFIG_RD_GZIP=y # CONFIG_RD_BZIP2 is not set # CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_SYSCTL=y CONFIG_ANON_INODES=y CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y CONFIG_EMBEDDED=y +CONFIG_UID16=y # CONFIG_SYSCTL_SYSCALL is not set CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_ALL is not set @@ -145,7 +110,6 @@ CONFIG_HOTPLUG=y CONFIG_PRINTK=y CONFIG_BUG=y # CONFIG_ELF_CORE is not set -CONFIG_PCSPKR_PLATFORM=y CONFIG_BASE_FULL=y CONFIG_FUTEX=y CONFIG_EPOLL=y @@ -156,15 +120,14 @@ CONFIG_SHMEM=y CONFIG_ASHMEM=y CONFIG_AIO=y CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y # # Kernel Performance Events And Counters # -CONFIG_PERF_EVENTS=y +# CONFIG_PERF_EVENTS is not set # CONFIG_PERF_COUNTERS is not set -# CONFIG_DEBUG_PERF_USE_VMALLOC is not set CONFIG_VM_EVENT_COUNTERS=y -CONFIG_PCI_QUIRKS=y CONFIG_COMPAT_BRK=y CONFIG_SLAB=y # CONFIG_SLUB is not set @@ -172,27 +135,17 @@ CONFIG_SLAB=y # CONFIG_PROFILING is not set CONFIG_HAVE_OPROFILE=y # CONFIG_KPROBES is not set -# CONFIG_JUMP_LABEL is not set -CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y -CONFIG_HAVE_IOREMAP_PROT=y CONFIG_HAVE_KPROBES=y CONFIG_HAVE_KRETPROBES=y -CONFIG_HAVE_OPTPROBES=y -CONFIG_HAVE_ARCH_TRACEHOOK=y -CONFIG_HAVE_DMA_ATTRS=y CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y CONFIG_HAVE_DMA_API_DEBUG=y -CONFIG_HAVE_HW_BREAKPOINT=y -CONFIG_HAVE_MIXED_BREAKPOINTS_REGS=y -CONFIG_HAVE_USER_RETURN_NOTIFIER=y -CONFIG_HAVE_PERF_EVENTS_NMI=y -CONFIG_HAVE_ARCH_JUMP_LABEL=y # # GCOV-based kernel profiling # # CONFIG_GCOV_KERNEL is not set -# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y CONFIG_SLABINFO=y CONFIG_RT_MUTEXES=y CONFIG_BASE_SMALL=0 @@ -203,9 +156,9 @@ CONFIG_MODULE_FORCE_UNLOAD=y # CONFIG_MODVERSIONS is not set # CONFIG_MODULE_SRCVERSION_ALL is not set CONFIG_BLOCK=y +CONFIG_LBDAF=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_BLK_DEV_INTEGRITY is not set -# CONFIG_BLK_DEV_THROTTLING is not set # # IO Schedulers @@ -213,11 +166,13 @@ CONFIG_BLOCK=y CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y -CONFIG_CFQ_GROUP_IOSCHED=y +CONFIG_IOSCHED_BFQ=y +# CONFIG_CGROUP_BFQIO is not set # CONFIG_DEFAULT_DEADLINE is not set -CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_CFQ is not set +CONFIG_DEFAULT_BFQ=y # CONFIG_DEFAULT_NOOP is not set -CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_DEFAULT_IOSCHED="bfq" # CONFIG_INLINE_SPIN_TRYLOCK is not set # CONFIG_INLINE_SPIN_TRYLOCK_BH is not set # CONFIG_INLINE_SPIN_LOCK is not set @@ -250,158 +205,245 @@ CONFIG_DEFAULT_IOSCHED="cfq" CONFIG_FREEZER=y # -# Processor type and features +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +CONFIG_ARCH_MSM=y +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_S5PV310 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_TCC_926 is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_ARCH_MSM7X00A is not set +# CONFIG_ARCH_MSM7X30 is not set +CONFIG_ARCH_QSD8X50=y +# CONFIG_ARCH_MSM8X60 is not set +CONFIG_ARCH_MSM_SCORPION=y +CONFIG_HAS_MSM_DEBUG_UART_PHYS=y +CONFIG_MSM_MDP31=y +# CONFIG_PERFLOCK is not set + +# +# Qualcomm MSM Board Type +# +# CONFIG_MACH_SWORDFISH is not set +CONFIG_MACH_QSD8X50_SURF=y +# CONFIG_MACH_QSD8X50A_ST1_5 is not set +CONFIG_MSM_DEBUG_UART_NONE=y +# CONFIG_MSM_DEBUG_UART1 is not set +# CONFIG_MSM_DEBUG_UART2 is not set +# CONFIG_MSM_DEBUG_UART3 is not set +CONFIG_MSM_PROC_COMM=y +# CONFIG_MACH_MAHIMAHI is not set +CONFIG_MACH_SUPERSONIC=y +# CONFIG_MACH_QSD8X50_FFA is not set +# CONFIG_HTC_HEADSET is not set +# CONFIG_HTC_35MM_JACK is not set +CONFIG_HTC_BATTCHG=y +CONFIG_HTC_BATTCHG_SMEM=y +# CONFIG_HTC_PWRSPLY is not set +# CONFIG_HTC_PWRSINK is not set +# CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set +# CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_MSM7X00A_USE_GP_TIMER=y +# CONFIG_MSM7X00A_USE_DG_TIMER is not set +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=1 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_FIQ_SUPPORT=y +# CONFIG_MSM_SERIAL_DEBUGGER is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_DAL=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y +CONFIG_MSM_CPU_FREQ_MAX=998400 +CONFIG_MSM_CPU_FREQ_MIN=245760 +# CONFIG_AXI_SCREEN_POLICY is not set +# CONFIG_MSM_HW3D is not set +CONFIG_HTC_ACOUSTIC_QSD=y +CONFIG_MSM_QDSP6=y +CONFIG_WIFI_CONTROL_FUNC=y +# CONFIG_WIFI_MEM_PREALLOC is not set +CONFIG_ARCH_MSM_FLASHLIGHT=n +CONFIG_MICROP_COMMON=y +CONFIG_HTC_HEADSET_MGR=y +CONFIG_HTC_HEADSET_GPIO=y +CONFIG_HTC_HEADSET_MICROP=y +# CONFIG_VIRTUAL_KPANIC_PARTITION is not set + +# +# System MMU +# + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_VERIFY_PERMISSION_FAULT=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_CPU_HAS_PMU=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features # CONFIG_TICK_ONESHOT=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y CONFIG_GENERIC_CLOCKEVENTS_BUILD=y -# CONFIG_SMP is not set -CONFIG_X86_MPPARSE=y -CONFIG_X86_EXTENDED_PLATFORM=y -# CONFIG_X86_VSMP is not set -CONFIG_SCHED_OMIT_FRAME_POINTER=y -# CONFIG_PARAVIRT_GUEST is not set -CONFIG_NO_BOOTMEM=y -# CONFIG_MEMTEST is not set -# CONFIG_MK8 is not set -# CONFIG_MPSC is not set -# CONFIG_MCORE2 is not set -# CONFIG_MATOM is not set -CONFIG_GENERIC_CPU=y -CONFIG_X86_CPU=y -CONFIG_X86_INTERNODE_CACHE_SHIFT=6 -CONFIG_X86_CMPXCHG=y -CONFIG_X86_L1_CACHE_SHIFT=6 -CONFIG_X86_XADD=y -CONFIG_X86_WP_WORKS_OK=y -CONFIG_X86_TSC=y -CONFIG_X86_CMPXCHG64=y -CONFIG_X86_CMOV=y -CONFIG_X86_MINIMUM_CPU_FAMILY=64 -CONFIG_X86_DEBUGCTLMSR=y -# CONFIG_PROCESSOR_SELECT is not set -CONFIG_CPU_SUP_INTEL=y -CONFIG_CPU_SUP_AMD=y -CONFIG_CPU_SUP_CENTAUR=y -CONFIG_HPET_TIMER=y -CONFIG_DMI=y -CONFIG_GART_IOMMU=y -# CONFIG_CALGARY_IOMMU is not set -# CONFIG_AMD_IOMMU is not set -CONFIG_SWIOTLB=y -CONFIG_IOMMU_HELPER=y -# CONFIG_IOMMU_API is not set -CONFIG_NR_CPUS=1 -# CONFIG_IRQ_TIME_ACCOUNTING is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 # CONFIG_PREEMPT_NONE is not set # CONFIG_PREEMPT_VOLUNTARY is not set CONFIG_PREEMPT=y -CONFIG_X86_LOCAL_APIC=y -CONFIG_X86_IO_APIC=y -# CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS is not set -# CONFIG_X86_MCE is not set -# CONFIG_I8K is not set -# CONFIG_MICROCODE is not set -# CONFIG_X86_MSR is not set -# CONFIG_X86_CPUID is not set -CONFIG_ARCH_PHYS_ADDR_T_64BIT=y -CONFIG_ARCH_DMA_ADDR_T_64BIT=y -CONFIG_DIRECT_GBPAGES=y -CONFIG_ARCH_SPARSEMEM_DEFAULT=y -CONFIG_ARCH_SPARSEMEM_ENABLE=y -CONFIG_ARCH_SELECT_MEMORY_MODEL=y -CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000 +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_VMALLOC_RESERVE=0x08000000 CONFIG_SELECT_MEMORY_MODEL=y -CONFIG_SPARSEMEM_MANUAL=y -CONFIG_SPARSEMEM=y -CONFIG_HAVE_MEMORY_PRESENT=y -CONFIG_SPARSEMEM_EXTREME=y -CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y -CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOGETHER=y -CONFIG_SPARSEMEM_VMEMMAP=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y CONFIG_HAVE_MEMBLOCK=y -# CONFIG_MEMORY_HOTPLUG is not set CONFIG_PAGEFLAGS_EXTENDED=y CONFIG_SPLIT_PTLOCK_CPUS=4 -CONFIG_PHYS_ADDR_T_64BIT=y -CONFIG_ZONE_DMA_FLAG=1 -CONFIG_BOUNCE=y +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y # CONFIG_KSM is not set CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 CONFIG_NEED_PER_CPU_KM=y -# CONFIG_X86_CHECK_BIOS_CORRUPTION is not set -CONFIG_X86_RESERVE_LOW=64 -CONFIG_MTRR=y -CONFIG_MTRR_SANITIZER=y -CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT=0 -CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT=1 -CONFIG_X86_PAT=y -CONFIG_ARCH_USES_PG_UNCACHED=y -# CONFIG_EFI is not set -CONFIG_SECCOMP=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set # CONFIG_CC_STACKPROTECTOR is not set -# CONFIG_HZ_100 is not set -CONFIG_HZ_250=y -# CONFIG_HZ_300 is not set -# CONFIG_HZ_1000 is not set -CONFIG_HZ=250 -CONFIG_SCHED_HRTICK=y +# CONFIG_DEPRECATED_PARAM_STRUCT is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set # CONFIG_KEXEC is not set # CONFIG_CRASH_DUMP is not set -CONFIG_PHYSICAL_START=0x1000000 -CONFIG_RELOCATABLE=y -CONFIG_PHYSICAL_ALIGN=0x1000000 -# CONFIG_CMDLINE_BOOL is not set -CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y +# CONFIG_AUTO_ZRELADDR is not set # -# Power management and ACPI options -# -CONFIG_PM=y -# CONFIG_PM_DEBUG is not set -CONFIG_PM_SLEEP=y -CONFIG_SUSPEND_NVS=y -CONFIG_SUSPEND=y -CONFIG_SUSPEND_FREEZER=y -CONFIG_HAS_WAKELOCK=y -CONFIG_HAS_EARLYSUSPEND=y -CONFIG_WAKELOCK=y -CONFIG_WAKELOCK_STAT=y -CONFIG_USER_WAKELOCK=y -CONFIG_EARLYSUSPEND=y -# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set -CONFIG_FB_EARLYSUSPEND=y -# CONFIG_PM_RUNTIME is not set -CONFIG_PM_OPS=y -CONFIG_ACPI=y -CONFIG_ACPI_SLEEP=y -# CONFIG_ACPI_PROCFS is not set -# CONFIG_ACPI_PROCFS_POWER is not set -# CONFIG_ACPI_EC_DEBUGFS is not set -CONFIG_ACPI_PROC_EVENT=y -CONFIG_ACPI_AC=y -CONFIG_ACPI_BATTERY=y -CONFIG_ACPI_BUTTON=y -CONFIG_ACPI_FAN=y -# CONFIG_ACPI_DOCK is not set -CONFIG_ACPI_PROCESSOR=y -# CONFIG_ACPI_PROCESSOR_AGGREGATOR is not set -CONFIG_ACPI_THERMAL=y -# CONFIG_ACPI_CUSTOM_DSDT is not set -CONFIG_ACPI_BLACKLIST_YEAR=0 -# CONFIG_ACPI_DEBUG is not set -# CONFIG_ACPI_PCI_SLOT is not set -CONFIG_X86_PM_TIMER=y -# CONFIG_ACPI_CONTAINER is not set -# CONFIG_ACPI_SBS is not set -# CONFIG_ACPI_HED is not set -# CONFIG_ACPI_APEI is not set -# CONFIG_SFI is not set - -# -# CPU Frequency scaling +# CPU Power Management # CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_TABLE=y @@ -410,8 +452,8 @@ CONFIG_CPU_FREQ_STAT=y CONFIG_CPU_FREQ_STAT_DETAILS=y # CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set -CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y -# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set CONFIG_CPU_FREQ_GOV_PERFORMANCE=y @@ -420,60 +462,48 @@ CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y # -# CPUFreq processor drivers +# Floating point emulation # -# CONFIG_X86_PCC_CPUFREQ is not set -# CONFIG_X86_ACPI_CPUFREQ is not set -# CONFIG_X86_POWERNOW_K8 is not set -# CONFIG_X86_SPEEDSTEP_CENTRINO is not set -# CONFIG_X86_P4_CLOCKMOD is not set # -# shared options +# At least one emulation must be selected # -# CONFIG_X86_SPEEDSTEP_LIB is not set -CONFIG_CPU_IDLE=y -CONFIG_CPU_IDLE_GOV_LADDER=y -CONFIG_CPU_IDLE_GOV_MENU=y -# CONFIG_INTEL_IDLE is not set +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y # -# Memory power savings -# -# CONFIG_I7300_IDLE is not set - +# Userspace binary formats # -# Bus options (PCI etc.) -# -CONFIG_PCI=y -CONFIG_PCI_DIRECT=y -# CONFIG_PCI_MMCONFIG is not set -CONFIG_PCI_DOMAINS=y -# CONFIG_PCI_CNB20LE_QUIRK is not set -# CONFIG_PCIEPORTBUS is not set -CONFIG_ARCH_SUPPORTS_MSI=y -# CONFIG_PCI_MSI is not set -# CONFIG_PCI_DEBUG is not set -# CONFIG_PCI_STUB is not set -CONFIG_HT_IRQ=y -# CONFIG_PCI_IOV is not set -CONFIG_PCI_IOAPIC=y -CONFIG_ISA_DMA_API=y -CONFIG_AMD_NB=y -# CONFIG_PCCARD is not set -# CONFIG_HOTPLUG_PCI is not set +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set # -# Executable file formats / Emulations +# Power management options # -CONFIG_BINFMT_ELF=y -# CONFIG_HAVE_AOUT is not set -# CONFIG_BINFMT_MISC is not set -# CONFIG_IA32_EMULATION is not set -# CONFIG_COMPAT_FOR_U64_ALIGNMENT is not set -CONFIG_HAVE_TEXT_POKE_SMP=y +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM_OPS=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_NET=y # @@ -695,9 +725,6 @@ CONFIG_BRIDGE_IGMP_SNOOPING=y # CONFIG_DECNET is not set CONFIG_LLC=y # CONFIG_LLC2 is not set -# CONFIG_IPX is not set -# CONFIG_ATALK is not set -# CONFIG_X25 is not set # CONFIG_LAPB is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set @@ -759,7 +786,7 @@ CONFIG_NET_ACT_MIRRED=y # CONFIG_NET_CLS_IND is not set CONFIG_NET_SCH_FIFO=y # CONFIG_DCB is not set -CONFIG_DNS_RESOLVER=y +# CONFIG_BATMAN_ADV is not set # # Network testing @@ -842,6 +869,7 @@ CONFIG_MTD=y CONFIG_MTD_PARTITIONS=y # CONFIG_MTD_REDBOOT_PARTS is not set CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set # CONFIG_MTD_AR7_PARTS is not set # @@ -881,14 +909,12 @@ CONFIG_MTD_CFI_I2=y # Mapping drivers for chip access # # CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_TS5500 is not set -# CONFIG_MTD_INTEL_VR_NOR is not set # CONFIG_MTD_PLATRAM is not set # # Self-contained MTD device drivers # -# CONFIG_MTD_PMC551 is not set +CONFIG_MTD_MSM_NAND=y # CONFIG_MTD_DATAFLASH is not set # CONFIG_MTD_M25P80 is not set # CONFIG_MTD_SST25L is not set @@ -913,19 +939,7 @@ CONFIG_MTD_NAND_IDS=y # CONFIG_MTD_LPDDR is not set # CONFIG_MTD_UBI is not set # CONFIG_PARPORT is not set -CONFIG_PNP=y -CONFIG_PNP_DEBUG_MESSAGES=y - -# -# Protocols -# -CONFIG_PNPACPI=y CONFIG_BLK_DEV=y -# CONFIG_BLK_DEV_FD is not set -# CONFIG_BLK_CPQ_DA is not set -# CONFIG_BLK_CPQ_CISS_DA is not set -# CONFIG_BLK_DEV_DAC960 is not set -# CONFIG_BLK_DEV_UMEM is not set # CONFIG_BLK_DEV_COW_COMMON is not set CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_CRYPTOLOOP is not set @@ -934,24 +948,17 @@ CONFIG_BLK_DEV_LOOP=y # DRBD disabled because PROC_FS, INET or CONNECTOR not selected # # CONFIG_BLK_DEV_NBD is not set -# CONFIG_BLK_DEV_SX8 is not set # CONFIG_BLK_DEV_RAM is not set # CONFIG_CDROM_PKTCDVD is not set # CONFIG_ATA_OVER_ETH is not set -# CONFIG_BLK_DEV_HD is not set +# CONFIG_MG_DISK is not set # CONFIG_BLK_DEV_RBD is not set CONFIG_MISC_DEVICES=y # CONFIG_AD525X_DPOT is not set CONFIG_ANDROID_PMEM=y -# CONFIG_IBM_ASM is not set -# CONFIG_PHANTOM is not set -# CONFIG_SGI_IOC4 is not set -# CONFIG_TIFM_CORE is not set # CONFIG_ICS932S401 is not set # CONFIG_ENCLOSURE_SERVICES is not set CONFIG_KERNEL_DEBUGGER_CORE=y -# CONFIG_CS5535_MFGPT is not set -# CONFIG_HP_ILO is not set # CONFIG_APDS9802ALS is not set # CONFIG_ISL29003 is not set # CONFIG_ISL29020 is not set @@ -967,10 +974,9 @@ CONFIG_VP_A1026=y # CONFIG_DS1682 is not set # CONFIG_TI_DAC7512 is not set CONFIG_UID_STAT=y -# CONFIG_VMWARE_BALLOON is not set # CONFIG_BMP085 is not set -# CONFIG_PCH_PHUB is not set # CONFIG_WL127X_RFKILL is not set +CONFIG_SENSORS_BMA150_SPI=y # CONFIG_APANIC is not set # CONFIG_C2PORT is not set @@ -982,7 +988,6 @@ CONFIG_UID_STAT=y # CONFIG_EEPROM_LEGACY is not set # CONFIG_EEPROM_MAX6875 is not set # CONFIG_EEPROM_93CX6 is not set -# CONFIG_CB710_CORE is not set # CONFIG_IWMC3200TOP is not set # @@ -1008,19 +1013,11 @@ CONFIG_DM_DEBUG=y CONFIG_DM_CRYPT=y # CONFIG_DM_SNAPSHOT is not set # CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set # CONFIG_DM_ZERO is not set # CONFIG_DM_MULTIPATH is not set # CONFIG_DM_DELAY is not set CONFIG_DM_UEVENT=y -# CONFIG_FUSION is not set - -# -# IEEE 1394 (FireWire) support -# -# CONFIG_FIREWIRE is not set -# CONFIG_FIREWIRE_NOSY is not set -# CONFIG_I2O is not set -# CONFIG_MACINTOSH_DRIVERS is not set CONFIG_NETDEVICES=y # CONFIG_IFB is not set CONFIG_DUMMY=y @@ -1029,93 +1026,16 @@ CONFIG_DUMMY=y # CONFIG_EQUALIZER is not set CONFIG_TUN=y # CONFIG_VETH is not set -# CONFIG_NET_SB1000 is not set -# CONFIG_ARCNET is not set CONFIG_MII=y # CONFIG_PHYLIB is not set -CONFIG_NET_ETHERNET=y -# CONFIG_HAPPYMEAL is not set -# CONFIG_SUNGEM is not set -# CONFIG_CASSINI is not set -# CONFIG_NET_VENDOR_3COM is not set -# CONFIG_ENC28J60 is not set -# CONFIG_ETHOC is not set -# CONFIG_DNET is not set -# CONFIG_NET_TULIP is not set -# CONFIG_HP100 is not set -# CONFIG_IBM_NEW_EMAC_ZMII is not set -# CONFIG_IBM_NEW_EMAC_RGMII is not set -# CONFIG_IBM_NEW_EMAC_TAH is not set -# CONFIG_IBM_NEW_EMAC_EMAC4 is not set -# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set -# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set -# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set -# CONFIG_NET_PCI is not set -# CONFIG_B44 is not set -# CONFIG_KS8851 is not set -# CONFIG_KS8851_MLL is not set -# CONFIG_ATL2 is not set -CONFIG_NETDEV_1000=y -# CONFIG_ACENIC is not set -# CONFIG_DL2K is not set -# CONFIG_E1000 is not set -# CONFIG_E1000E is not set -# CONFIG_IP1000 is not set -# CONFIG_IGB is not set -# CONFIG_IGBVF is not set -# CONFIG_NS83820 is not set -# CONFIG_HAMACHI is not set -# CONFIG_YELLOWFIN is not set -# CONFIG_R8169 is not set -# CONFIG_SIS190 is not set -# CONFIG_SKGE is not set -# CONFIG_SKY2 is not set -# CONFIG_VIA_VELOCITY is not set -# CONFIG_TIGON3 is not set -# CONFIG_BNX2 is not set -# CONFIG_CNIC is not set -# CONFIG_QLA3XXX is not set -# CONFIG_ATL1 is not set -# CONFIG_ATL1E is not set -# CONFIG_ATL1C is not set -# CONFIG_JME is not set -# CONFIG_STMMAC_ETH is not set -# CONFIG_PCH_GBE is not set -CONFIG_NETDEV_10000=y -# CONFIG_CHELSIO_T1 is not set -CONFIG_CHELSIO_T3_DEPENDS=y -# CONFIG_CHELSIO_T3 is not set -CONFIG_CHELSIO_T4_DEPENDS=y -# CONFIG_CHELSIO_T4 is not set -CONFIG_CHELSIO_T4VF_DEPENDS=y -# CONFIG_CHELSIO_T4VF is not set -# CONFIG_ENIC is not set -# CONFIG_IXGBE is not set -# CONFIG_IXGB is not set -# CONFIG_S2IO is not set -# CONFIG_VXGE is not set -# CONFIG_MYRI10GE is not set -# CONFIG_NETXEN_NIC is not set -# CONFIG_NIU is not set -# CONFIG_MLX4_EN is not set -# CONFIG_MLX4_CORE is not set -# CONFIG_TEHUTI is not set -# CONFIG_BNX2X is not set -# CONFIG_QLCNIC is not set -# CONFIG_QLGE is not set -# CONFIG_BNA is not set -# CONFIG_SFC is not set -# CONFIG_BE2NET is not set -# CONFIG_TR is not set +# CONFIG_NET_ETHERNET is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set CONFIG_WLAN=y -# CONFIG_AIRO is not set -# CONFIG_ATMEL is not set -# CONFIG_PRISM54 is not set CONFIG_BCM4329=m CONFIG_BCM4329_FW_PATH="/system/etc/firmware/fw_bcm4329.bin" CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" # CONFIG_HOSTAP is not set -# CONFIG_TIWLAN1251 is not set # # WiMAX Wireless Broadband devices @@ -1131,8 +1051,6 @@ CONFIG_WIMAX_SQN=m # # CAIF transport drivers # -# CONFIG_FDDI is not set -# CONFIG_HIPPI is not set CONFIG_PPP=y # CONFIG_PPP_MULTILINK is not set # CONFIG_PPP_FILTER is not set @@ -1147,9 +1065,10 @@ CONFIG_PPPOPNS=y # CONFIG_SLIP is not set CONFIG_SLHC=y # CONFIG_NETCONSOLE is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set # CONFIG_NETPOLL is not set # CONFIG_NET_POLL_CONTROLLER is not set -# CONFIG_VMXNET3 is not set # CONFIG_GAN_ETH is not set # CONFIG_ISDN is not set # CONFIG_PHONE is not set @@ -1186,7 +1105,7 @@ CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT=y # CONFIG_TOUCHSCREEN_AD7879 is not set # CONFIG_TOUCHSCREEN_BU21013 is not set # CONFIG_TOUCHSCREEN_CY8CTMG110 is not set -# CONFIG_TOUCHSCREEN_CYPRESS_TMG is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set # CONFIG_TOUCHSCREEN_DYNAPRO is not set # CONFIG_TOUCHSCREEN_HAMPSHIRE is not set # CONFIG_TOUCHSCREEN_EETI is not set @@ -1201,18 +1120,18 @@ CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT=y # CONFIG_TOUCHSCREEN_MK712 is not set # CONFIG_TOUCHSCREEN_PENMOUNT is not set # CONFIG_TOUCHSCREEN_QT602240 is not set +# CONFIG_TOUCHSCREEN_MSM is not set # CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set # CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set # CONFIG_TOUCHSCREEN_TOUCHWIN is not set # CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set # CONFIG_TOUCHSCREEN_TOUCHIT213 is not set # CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set # CONFIG_TOUCHSCREEN_TPS6507X is not set CONFIG_INPUT_MISC=y # CONFIG_INPUT_AD714X is not set -# CONFIG_INPUT_PCSPKR is not set -# CONFIG_INPUT_APANEL is not set -# CONFIG_INPUT_ATLAS_BTNS is not set # CONFIG_INPUT_ATI_REMOTE is not set # CONFIG_INPUT_ATI_REMOTE2 is not set CONFIG_INPUT_KEYCHORD=y @@ -1221,15 +1140,12 @@ CONFIG_INPUT_KEYCHORD=y # CONFIG_INPUT_YEALINK is not set # CONFIG_INPUT_CM109 is not set CONFIG_INPUT_UINPUT=y -# CONFIG_INPUT_WINBOND_CIR is not set CONFIG_INPUT_GPIO=y # CONFIG_INPUT_PCF8574 is not set # CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set # CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set # CONFIG_INPUT_CAPELLA_CM3602 is not set -# CONFIG_INPUT_CAPELLA_CM3602_HTC is not set -CONFIG_LIGHTSENSOR_MICROP=y -# CONFIG_INPUT_OPTICALJOYSTICK is not set # # Hardware I/O ports @@ -1245,40 +1161,39 @@ CONFIG_LIGHTSENSOR_MICROP=y # CONFIG_DEVKMEM is not set # CONFIG_SERIAL_NONSTANDARD is not set # CONFIG_N_GSM is not set -# CONFIG_NOZOMI is not set # # Serial drivers # # CONFIG_SERIAL_8250 is not set -CONFIG_FIX_EARLYCON_MEM=y # # Non-8250 serial port support # # CONFIG_SERIAL_MAX3100 is not set # CONFIG_SERIAL_MAX3107 is not set -# CONFIG_SERIAL_MFD_HSU is not set -# CONFIG_SERIAL_JSM is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_BCM_BT_LPM=y # CONFIG_SERIAL_TIMBERDALE is not set # CONFIG_SERIAL_ALTERA_JTAGUART is not set # CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_IFX6X60 is not set CONFIG_UNIX98_PTYS=y # CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set # CONFIG_LEGACY_PTYS is not set # CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set # CONFIG_IPMI_HANDLER is not set # CONFIG_HW_RANDOM is not set -# CONFIG_NVRAM is not set # CONFIG_R3964 is not set -# CONFIG_APPLICOM is not set -# CONFIG_MWAVE is not set # CONFIG_RAW_DRIVER is not set -# CONFIG_HPET is not set -# CONFIG_HANGCHECK_TIMER is not set # CONFIG_TCG_TPM is not set -# CONFIG_TELCLOCK is not set -CONFIG_DEVPORT=y +# CONFIG_DCC_TTY is not set # CONFIG_RAMOOPS is not set CONFIG_I2C=y CONFIG_I2C_BOARDINFO=y @@ -1291,34 +1206,12 @@ CONFIG_I2C_HELPER_AUTO=y # I2C Hardware Bus support # -# -# PC SMBus host controller drivers -# -# CONFIG_I2C_ALI1535 is not set -# CONFIG_I2C_ALI1563 is not set -# CONFIG_I2C_ALI15X3 is not set -# CONFIG_I2C_AMD756 is not set -# CONFIG_I2C_AMD8111 is not set -# CONFIG_I2C_I801 is not set -# CONFIG_I2C_ISCH is not set -# CONFIG_I2C_PIIX4 is not set -# CONFIG_I2C_NFORCE2 is not set -# CONFIG_I2C_SIS5595 is not set -# CONFIG_I2C_SIS630 is not set -# CONFIG_I2C_SIS96X is not set -# CONFIG_I2C_VIA is not set -# CONFIG_I2C_VIAPRO is not set - -# -# ACPI drivers -# -# CONFIG_I2C_SCMI is not set - # # I2C system bus drivers (mostly embedded / system-on-chip) # +# CONFIG_I2C_DESIGNWARE is not set # CONFIG_I2C_GPIO is not set -# CONFIG_I2C_INTEL_MID is not set +CONFIG_I2C_MSM=y # CONFIG_I2C_OCORES is not set # CONFIG_I2C_PCA_PLATFORM is not set # CONFIG_I2C_SIMTEC is not set @@ -1346,7 +1239,8 @@ CONFIG_SPI_MASTER=y # # CONFIG_SPI_BITBANG is not set # CONFIG_SPI_GPIO is not set -# CONFIG_SPI_TOPCLIFF_PCH is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_QSD=y # CONFIG_SPI_XILINX is not set # CONFIG_SPI_DESIGNWARE is not set @@ -1360,7 +1254,11 @@ CONFIG_SPI_MASTER=y # PPS support # # CONFIG_PPS is not set -CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y + +# +# PPS generators support +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y CONFIG_GPIOLIB=y # CONFIG_DEBUG_GPIO is not set # CONFIG_GPIO_SYSFS is not set @@ -1370,15 +1268,12 @@ CONFIG_GPIOLIB=y # # CONFIG_GPIO_BASIC_MMIO is not set # CONFIG_GPIO_IT8761E is not set -# CONFIG_GPIO_SCH is not set -# CONFIG_GPIO_VX855 is not set # # I2C GPIO expanders: # # CONFIG_GPIO_MAX7300 is not set # CONFIG_GPIO_MAX732X is not set -# CONFIG_GPIO_PCA953X is not set # CONFIG_GPIO_PCF857X is not set # CONFIG_GPIO_SX150X is not set # CONFIG_GPIO_ADP5588 is not set @@ -1386,11 +1281,6 @@ CONFIG_GPIOLIB=y # # PCI GPIO expanders: # -# CONFIG_GPIO_CS5535 is not set -# CONFIG_GPIO_BT8XX is not set -# CONFIG_GPIO_LANGWELL is not set -# CONFIG_GPIO_PCH is not set -# CONFIG_GPIO_RDC321X is not set # # SPI GPIO expanders: @@ -1417,8 +1307,10 @@ CONFIG_POWER_SUPPLY=y # CONFIG_BATTERY_BQ20Z75 is not set # CONFIG_BATTERY_BQ27x00 is not set # CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_GPIO is not set # CONFIG_HWMON is not set -CONFIG_THERMAL=y +# CONFIG_THERMAL is not set # CONFIG_WATCHDOG is not set CONFIG_SSB_POSSIBLE=y @@ -1430,6 +1322,8 @@ CONFIG_MFD_SUPPORT=y # CONFIG_MFD_CORE is not set # CONFIG_MFD_88PM860X is not set # CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set # CONFIG_HTC_PASIC3 is not set # CONFIG_HTC_I2CPLD is not set # CONFIG_TPS65010 is not set @@ -1437,8 +1331,11 @@ CONFIG_MFD_SUPPORT=y CONFIG_TPS65200=y # CONFIG_TWL4030_CORE is not set # CONFIG_MFD_STMPE is not set -# CONFIG_MFD_TC35892 is not set +# CONFIG_MFD_TC3589X is not set # CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set # CONFIG_PMIC_DA903X is not set # CONFIG_PMIC_ADP5520 is not set # CONFIG_MFD_MAX8925 is not set @@ -1452,12 +1349,8 @@ CONFIG_TPS65200=y # CONFIG_MFD_MC13XXX is not set # CONFIG_ABX500_CORE is not set # CONFIG_EZX_PCAP is not set -# CONFIG_MFD_TIMBERDALE is not set -# CONFIG_LPC_SCH is not set -# CONFIG_MFD_RDC321X is not set -# CONFIG_MFD_JANZ_CMODIO is not set # CONFIG_MFD_TPS6586X is not set -# CONFIG_MFD_VX855 is not set +# CONFIG_MFD_WL1273_CORE is not set CONFIG_REGULATOR=y CONFIG_REGULATOR_DEBUG=y # CONFIG_REGULATOR_DUMMY is not set @@ -1475,6 +1368,7 @@ CONFIG_REGULATOR_TPS65023=y # CONFIG_REGULATOR_TPS6507X is not set # CONFIG_REGULATOR_ISL6271A is not set # CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_TPS6524X is not set CONFIG_MEDIA_SUPPORT=y # @@ -1487,41 +1381,47 @@ CONFIG_MEDIA_SUPPORT=y # # Multimedia drivers # -# CONFIG_IR_CORE is not set +# CONFIG_RC_CORE is not set # # Qualcomm MSM Camera And Video # +CONFIG_MSM_CAMERA=y +CONFIG_720P_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set # # Camera Sensor Selection # -# CONFIG_DAB is not set +# CONFIG_MT9T013 is not set +# CONFIG_MT9D112 is not set +# CONFIG_MT9P012 is not set +# CONFIG_S5K3E2FX is not set +CONFIG_S5K6AAFX=y +CONFIG_OV8810=y +CONFIG_OV9665=y +CONFIG_S5K3H1GX=y # # Graphics support # -# CONFIG_AGP is not set -CONFIG_VGA_ARB=y -CONFIG_VGA_ARB_MAX_GPUS=16 -# CONFIG_VGA_SWITCHEROO is not set # CONFIG_DRM is not set -# CONFIG_STUB_POULSBO is not set # CONFIG_VGASTATE is not set CONFIG_VIDEO_OUTPUT_CONTROL=y CONFIG_FB=y # CONFIG_FIRMWARE_EDID is not set # CONFIG_FB_DDC is not set # CONFIG_FB_BOOT_VESA_SUPPORT is not set -# CONFIG_FB_CFB_FILLRECT is not set -# CONFIG_FB_CFB_COPYAREA is not set -# CONFIG_FB_CFB_IMAGEBLIT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y # CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set # CONFIG_FB_SYS_FILLRECT is not set # CONFIG_FB_SYS_COPYAREA is not set # CONFIG_FB_SYS_IMAGEBLIT is not set # CONFIG_FB_FOREIGN_ENDIAN is not set # CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set # CONFIG_FB_SVGALIB is not set # CONFIG_FB_MACMODES is not set # CONFIG_FB_BACKLIGHT is not set @@ -1531,42 +1431,21 @@ CONFIG_FB=y # # Frame buffer hardware drivers # -# CONFIG_FB_CIRRUS is not set -# CONFIG_FB_PM2 is not set -# CONFIG_FB_CYBER2000 is not set -# CONFIG_FB_ARC is not set -# CONFIG_FB_ASILIANT is not set -# CONFIG_FB_IMSTT is not set -# CONFIG_FB_VGA16 is not set -# CONFIG_FB_VESA is not set -# CONFIG_FB_N411 is not set -# CONFIG_FB_HGA is not set # CONFIG_FB_S1D13XXX is not set -# CONFIG_FB_NVIDIA is not set -# CONFIG_FB_RIVA is not set -# CONFIG_FB_LE80578 is not set -# CONFIG_FB_MATROX is not set -# CONFIG_FB_RADEON is not set -# CONFIG_FB_ATY128 is not set -# CONFIG_FB_ATY is not set -# CONFIG_FB_S3 is not set -# CONFIG_FB_SAVAGE is not set -# CONFIG_FB_SIS is not set -# CONFIG_FB_VIA is not set -# CONFIG_FB_NEOMAGIC is not set -# CONFIG_FB_KYRO is not set -# CONFIG_FB_3DFX is not set -# CONFIG_FB_VOODOO1 is not set -# CONFIG_FB_VT8623 is not set -# CONFIG_FB_TRIDENT is not set -# CONFIG_FB_ARK is not set -# CONFIG_FB_PM3 is not set -# CONFIG_FB_CARMINE is not set -# CONFIG_FB_GEODE is not set # CONFIG_FB_VIRTUAL is not set # CONFIG_FB_METRONOME is not set # CONFIG_FB_MB862XX is not set # CONFIG_FB_BROADSHEET is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_LEGACY_MDP=y +CONFIG_FB_MSM_MDP_PPP=y +CONFIG_FB_MSM_LCDC=y +CONFIG_FB_MSM_MDDI=y +CONFIG_FB_MSM_MDDI_EPSON=y +CONFIG_FB_MSM_MDDI_NOVTEC=y +CONFIG_GPU_MSM_KGSL=y +CONFIG_MSM_KGSL_MMU=y +CONFIG_MSM_HDMI=y # CONFIG_BACKLIGHT_LCD_SUPPORT is not set # @@ -1589,11 +1468,12 @@ CONFIG_HID_APPLE=y # CONFIG_HID_WACOM is not set CONFIG_USB_SUPPORT=y CONFIG_USB_ARCH_HAS_HCD=y -CONFIG_USB_ARCH_HAS_OHCI=y +# CONFIG_USB_ARCH_HAS_OHCI is not set CONFIG_USB_ARCH_HAS_EHCI=y # CONFIG_USB is not set # CONFIG_USB_OTG_WHITELIST is not set # CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MUSB_HDRC is not set # # NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may @@ -1604,17 +1484,17 @@ CONFIG_USB_GADGET=y # CONFIG_USB_GADGET_DEBUG_FS is not set CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_USB_GADGET_SELECTED=y -CONFIG_USB_GADGET_R8A66597=y -CONFIG_USB_R8A66597=y +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA_U2O is not set # CONFIG_USB_GADGET_M66592 is not set -# CONFIG_USB_GADGET_AMD5536UDC is not set -# CONFIG_USB_GADGET_CI13XXX is not set -# CONFIG_USB_GADGET_NET2280 is not set -# CONFIG_USB_GADGET_GOKU is not set -# CONFIG_USB_GADGET_LANGWELL is not set +# CONFIG_USB_GADGET_CI13XXX_MSM is not set +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_MSM_72K=y +CONFIG_USB_MSM_72K_HTC=y CONFIG_USB_GADGET_DUALSPEED=y # CONFIG_USB_ZERO is not set # CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set # CONFIG_USB_GADGETFS is not set # CONFIG_USB_FUNCTIONFS is not set # CONFIG_USB_FILE_STORAGE is not set @@ -1624,25 +1504,32 @@ CONFIG_USB_GADGET_DUALSPEED=y CONFIG_USB_ANDROID=y # CONFIG_USB_ANDROID_ACM is not set CONFIG_USB_ANDROID_ADB=y -# CONFIG_USB_ANDROID_DIAG is not set +CONFIG_USB_ANDROID_DIAG=y CONFIG_USB_ANDROID_MASS_STORAGE=y # CONFIG_USB_ANDROID_MTP is not set CONFIG_USB_ANDROID_RNDIS=y CONFIG_USB_ANDROID_RNDIS_WCEIS=y +CONFIG_USB_ANDROID_ACCESSORY=y # CONFIG_USB_CDC_COMPOSITE is not set # CONFIG_USB_G_MULTI is not set # CONFIG_USB_G_HID is not set # CONFIG_USB_G_DBGP is not set +CONFIG_USB_ACCESSORY_DETECT=y +# CONFIG_USB_ACCESSORY_DETECT_BY_ADC is not set +CONFIG_DOCK_ACCESSORY_DETECT=y +# CONFIG_USB_BYPASS_VBUS_NOTIFY is not set # # OTG and related infrastructure # # CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set # CONFIG_NOP_USB_XCEIV is not set -# CONFIG_UWB is not set +# CONFIG_USB_MSM_OTG_72K is not set CONFIG_MMC=y # CONFIG_MMC_DEBUG is not set CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_CLKGATE is not set CONFIG_MMC_EMBEDDED_SDIO=y CONFIG_MMC_PARANOID_SD_INIT=y @@ -1660,11 +1547,9 @@ CONFIG_MMC_BLOCK_DEFERRED_RESUME=y # MMC/SD/SDIO Host Controller Drivers # # CONFIG_MMC_SDHCI is not set -# CONFIG_MMC_WBSD is not set -# CONFIG_MMC_TIFM_SD is not set +CONFIG_MMC_MSM=y # CONFIG_MMC_SPI is not set -# CONFIG_MMC_CB710 is not set -# CONFIG_MMC_VIA_SDMMC is not set +# CONFIG_MMC_DW is not set # CONFIG_MEMSTICK is not set CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y @@ -1672,7 +1557,6 @@ CONFIG_LEDS_CLASS=y # # LED drivers # -# CONFIG_LEDS_ALIX2 is not set # CONFIG_LEDS_PCA9532 is not set CONFIG_LEDS_GPIO=y CONFIG_LEDS_GPIO_PLATFORM=y @@ -1684,7 +1568,6 @@ CONFIG_LEDS_CPLD=y # CONFIG_LEDS_DAC124S085 is not set # CONFIG_LEDS_REGULATOR is not set # CONFIG_LEDS_BD2802 is not set -# CONFIG_LEDS_INTEL_SS4200 is not set # CONFIG_LEDS_LT3593 is not set CONFIG_LEDS_TRIGGERS=y @@ -1701,11 +1584,10 @@ CONFIG_LEDS_TRIGGER_SLEEP=y # # iptables trigger is under Netfilter config (LED target) # +# CONFIG_NFC_DEVICES is not set CONFIG_SWITCH=y CONFIG_SWITCH_GPIO=y # CONFIG_ACCESSIBILITY is not set -# CONFIG_INFINIBAND is not set -# CONFIG_EDAC is not set CONFIG_RTC_LIB=y CONFIG_RTC_CLASS=y CONFIG_RTC_HCTOSYS=y @@ -1775,19 +1657,15 @@ CONFIG_RTC_INTF_ALARM_DEV=y # # on-CPU RTC drivers # +CONFIG_RTC_DRV_MSM7X00A=y # CONFIG_DMADEVICES is not set # CONFIG_AUXDISPLAY is not set # CONFIG_UIO is not set CONFIG_STAGING=y # CONFIG_STAGING_EXCLUDE_BUILD is not set -# CONFIG_ET131X is not set -# CONFIG_SLICOSS is not set # CONFIG_ECHO is not set # CONFIG_BRCM80211 is not set -# CONFIG_RT2860 is not set # CONFIG_COMEDI is not set -# CONFIG_R8187SE is not set -# CONFIG_RTL8192E is not set # # Android @@ -1807,24 +1685,14 @@ CONFIG_ANDROID_TIMED_OUTPUT=y CONFIG_ANDROID_TIMED_GPIO=y CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_POHMELFS is not set -# CONFIG_AUTOFS_FS is not set -# CONFIG_VT6655 is not set -# CONFIG_HYPERV is not set -# CONFIG_VME_BUS is not set # CONFIG_IIO is not set # CONFIG_ZRAM is not set -# CONFIG_BATMAN_ADV is not set # CONFIG_FB_SM7XX is not set -# CONFIG_CRYSTALHD is not set # # Texas Instruments shared transport line discipline # # CONFIG_ST_BT is not set -# CONFIG_ADIS16255 is not set -# CONFIG_FB_XGI is not set -# CONFIG_SMB_FS is not set -# CONFIG_ACPI_QUICKSTART is not set CONFIG_MACH_NO_WESTBRIDGE=y # CONFIG_ATH6K_LEGACY is not set # CONFIG_FT1000 is not set @@ -1832,30 +1700,8 @@ CONFIG_MACH_NO_WESTBRIDGE=y # # Speakup console speech # -CONFIG_X86_PLATFORM_DEVICES=y -# CONFIG_ASUS_LAPTOP is not set -# CONFIG_SONY_LAPTOP is not set -# CONFIG_IDEAPAD_LAPTOP is not set -# CONFIG_THINKPAD_ACPI is not set -# CONFIG_SENSORS_HDAPS is not set -# CONFIG_INTEL_MENLOW is not set -# CONFIG_ACPI_WMI is not set -# CONFIG_ACPI_ASUS is not set -# CONFIG_TOPSTAR_LAPTOP is not set -# CONFIG_TOSHIBA_BT_RFKILL is not set -# CONFIG_ACPI_CMPC is not set -# CONFIG_INTEL_IPS is not set -# CONFIG_IBM_RTL is not set - -# -# Firmware Drivers -# -# CONFIG_EDD is not set -CONFIG_FIRMWARE_MEMMAP=y -# CONFIG_DELL_RBU is not set -# CONFIG_DCDBAS is not set -CONFIG_DMIID=y -# CONFIG_ISCSI_IBFT_FIND is not set +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set # # File systems @@ -1873,13 +1719,11 @@ CONFIG_JBD2=y CONFIG_FS_MBCACHE=y # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set -CONFIG_FS_POSIX_ACL=y # CONFIG_XFS_FS is not set # CONFIG_GFS2_FS is not set -# CONFIG_OCFS2_FS is not set # CONFIG_BTRFS_FS is not set # CONFIG_NILFS2_FS is not set -CONFIG_EXPORTFS=m +# CONFIG_FS_POSIX_ACL is not set CONFIG_FILE_LOCKING=y CONFIG_FSNOTIFY=y # CONFIG_DNOTIFY is not set @@ -1888,7 +1732,7 @@ CONFIG_INOTIFY_USER=y # CONFIG_QUOTA is not set # CONFIG_QUOTACTL is not set # CONFIG_AUTOFS4_FS is not set -CONFIG_FUSE_FS=m +CONFIG_FUSE_FS=y # CONFIG_CUSE is not set # @@ -1916,19 +1760,15 @@ CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" # Pseudo filesystems # CONFIG_PROC_FS=y -# CONFIG_PROC_KCORE is not set CONFIG_PROC_SYSCTL=y CONFIG_PROC_PAGE_MONITOR=y CONFIG_SYSFS=y CONFIG_TMPFS=y # CONFIG_TMPFS_POSIX_ACL is not set -# CONFIG_HUGETLBFS is not set # CONFIG_HUGETLB_PAGE is not set # CONFIG_CONFIGFS_FS is not set CONFIG_MISC_FILESYSTEMS=y -# CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set -# CONFIG_ECRYPT_FS is not set # CONFIG_HFS_FS is not set # CONFIG_HFSPLUS_FS is not set # CONFIG_BEFS_FS is not set @@ -1949,50 +1789,23 @@ CONFIG_YAFFS_XATTR=y # CONFIG_JFFS2_FS is not set # CONFIG_LOGFS is not set # CONFIG_CRAMFS is not set -CONFIG_SQUASHFS=y -# CONFIG_SQUASHFS_XATTR is not set -# CONFIG_SQUASHFS_LZO is not set -CONFIG_SQUASHFS_EMBEDDED=y -CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_SQUASHFS is not set # CONFIG_VXFS_FS is not set # CONFIG_MINIX_FS is not set # CONFIG_OMFS_FS is not set -# CONFIG_HPFS_FS is not set # CONFIG_QNX4FS_FS is not set # CONFIG_ROMFS_FS is not set # CONFIG_SYSV_FS is not set -# CONFIG_UFS_FS is not set CONFIG_NETWORK_FILESYSTEMS=y -CONFIG_NFS_FS=m -CONFIG_NFS_V3=y -CONFIG_NFS_V3_ACL=y -CONFIG_NFS_V4=y -# CONFIG_NFS_V4_1 is not set -# CONFIG_NFS_USE_LEGACY_DNS is not set -CONFIG_NFS_USE_KERNEL_DNS=y -# CONFIG_NFS_USE_NEW_IDMAPPER is not set -CONFIG_NFSD=m -CONFIG_NFSD_DEPRECATED=y -CONFIG_NFSD_V2_ACL=y -CONFIG_NFSD_V3=y -CONFIG_NFSD_V3_ACL=y -CONFIG_NFSD_V4=y -CONFIG_LOCKD=m -CONFIG_LOCKD_V4=y -CONFIG_NFS_ACL_SUPPORT=m -CONFIG_NFS_COMMON=y -CONFIG_SUNRPC=m -CONFIG_SUNRPC_GSS=m -CONFIG_RPCSEC_GSS_KRB5=m +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set # CONFIG_CEPH_FS is not set -CONFIG_CIFS=m +CONFIG_CIFS=y # CONFIG_CIFS_STATS is not set CONFIG_CIFS_WEAK_PW_HASH=y -# CONFIG_CIFS_UPCALL is not set CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y # CONFIG_CIFS_DEBUG2 is not set -# CONFIG_CIFS_DFS_UPCALL is not set # CONFIG_CIFS_ACL is not set # CONFIG_CIFS_EXPERIMENTAL is not set # CONFIG_NCP_FS is not set @@ -2043,13 +1856,11 @@ CONFIG_NLS_ISO8859_1=y # CONFIG_NLS_ISO8859_15 is not set # CONFIG_NLS_KOI8_R is not set # CONFIG_NLS_KOI8_U is not set -# CONFIG_NLS_UTF8 is not set -# CONFIG_DLM is not set +CONFIG_NLS_UTF8=y # # Kernel hacking # -CONFIG_TRACE_IRQFLAGS_SUPPORT=y CONFIG_PRINTK_TIME=y CONFIG_ENABLE_WARN_DEPRECATED=y CONFIG_ENABLE_MUST_CHECK=y @@ -2066,7 +1877,7 @@ CONFIG_DEBUG_KERNEL=y CONFIG_DETECT_HUNG_TASK=y # CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 -# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHED_DEBUG=y CONFIG_SCHEDSTATS=y CONFIG_TIMER_STATS=y # CONFIG_DEBUG_OBJECTS is not set @@ -2077,7 +1888,7 @@ CONFIG_TIMER_STATS=y # CONFIG_RT_MUTEX_TESTER is not set # CONFIG_DEBUG_SPINLOCK is not set # CONFIG_DEBUG_MUTEXES is not set -CONFIG_BKL=y +# CONFIG_BKL is not set # CONFIG_DEBUG_LOCK_ALLOC is not set # CONFIG_PROVE_LOCKING is not set # CONFIG_SPARSE_RCU_POINTER is not set @@ -2089,7 +1900,6 @@ CONFIG_STACKTRACE=y CONFIG_DEBUG_BUGVERBOSE=y # CONFIG_DEBUG_INFO is not set # CONFIG_DEBUG_VM is not set -# CONFIG_DEBUG_VIRTUAL is not set # CONFIG_DEBUG_WRITECOUNT is not set # CONFIG_DEBUG_MEMORY_INIT is not set # CONFIG_DEBUG_LIST is not set @@ -2097,11 +1907,9 @@ CONFIG_DEBUG_BUGVERBOSE=y # CONFIG_DEBUG_SG is not set # CONFIG_DEBUG_NOTIFIERS is not set # CONFIG_DEBUG_CREDENTIALS is not set -CONFIG_ARCH_WANT_FRAME_POINTERS=y CONFIG_FRAME_POINTER=y # CONFIG_BOOT_PRINTK_DELAY is not set # CONFIG_RCU_TORTURE_TEST is not set -# CONFIG_RCU_CPU_STALL_DETECTOR is not set # CONFIG_BACKTRACE_SELF_TEST is not set # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set # CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set @@ -2109,58 +1917,33 @@ CONFIG_FRAME_POINTER=y # CONFIG_FAULT_INJECTION is not set # CONFIG_LATENCYTOP is not set # CONFIG_SYSCTL_SYSCALL_CHECK is not set -# CONFIG_DEBUG_PAGEALLOC is not set -CONFIG_USER_STACKTRACE_SUPPORT=y +# CONFIG_PAGE_POISONING is not set CONFIG_HAVE_FUNCTION_TRACER=y CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y -CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST=y -CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y CONFIG_HAVE_DYNAMIC_FTRACE=y CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y -CONFIG_HAVE_SYSCALL_TRACEPOINTS=y CONFIG_HAVE_C_RECORDMCOUNT=y CONFIG_TRACING_SUPPORT=y # CONFIG_FTRACE is not set -# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set # CONFIG_DYNAMIC_DEBUG is not set # CONFIG_DMA_API_DEBUG is not set # CONFIG_ATOMIC64_SELFTEST is not set # CONFIG_SAMPLES is not set CONFIG_HAVE_ARCH_KGDB=y # CONFIG_KGDB is not set -CONFIG_HAVE_ARCH_KMEMCHECK=y +# CONFIG_TEST_KSTRTOX is not set # CONFIG_STRICT_DEVMEM is not set -CONFIG_X86_VERBOSE_BOOTUP=y -CONFIG_EARLY_PRINTK=y -# CONFIG_EARLY_PRINTK_DBGP is not set -# CONFIG_DEBUG_STACKOVERFLOW is not set +# CONFIG_ARM_UNWIND is not set +# CONFIG_DEBUG_USER is not set +CONFIG_DEBUG_ERRORS=y # CONFIG_DEBUG_STACK_USAGE is not set -# CONFIG_X86_PTDUMP is not set -CONFIG_DEBUG_RODATA=y -CONFIG_DEBUG_RODATA_TEST=y -# CONFIG_DEBUG_NX_TEST is not set -# CONFIG_IOMMU_DEBUG is not set -# CONFIG_IOMMU_STRESS is not set -CONFIG_HAVE_MMIOTRACE_SUPPORT=y -CONFIG_IO_DELAY_TYPE_0X80=0 -CONFIG_IO_DELAY_TYPE_0XED=1 -CONFIG_IO_DELAY_TYPE_UDELAY=2 -CONFIG_IO_DELAY_TYPE_NONE=3 -CONFIG_IO_DELAY_0X80=y -# CONFIG_IO_DELAY_0XED is not set -# CONFIG_IO_DELAY_UDELAY is not set -# CONFIG_IO_DELAY_NONE is not set -CONFIG_DEFAULT_IO_DELAY_TYPE=0 -# CONFIG_DEBUG_BOOT_PARAMS is not set -# CONFIG_CPA_DEBUG is not set -# CONFIG_OPTIMIZE_INLINING is not set -# CONFIG_DEBUG_STRICT_USER_COPY_CHECKS is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set # # Security options # -CONFIG_KEYS=y -# CONFIG_KEYS_DEBUG_PROC_KEYS is not set +# CONFIG_KEYS is not set # CONFIG_SECURITY_DMESG_RESTRICT is not set # CONFIG_SECURITY is not set # CONFIG_SECURITYFS is not set @@ -2220,9 +2003,8 @@ CONFIG_CRYPTO_HMAC=y # Digest # CONFIG_CRYPTO_CRC32C=y -# CONFIG_CRYPTO_CRC32C_INTEL is not set # CONFIG_CRYPTO_GHASH is not set -# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD4=y CONFIG_CRYPTO_MD5=y # CONFIG_CRYPTO_MICHAEL_MIC is not set # CONFIG_CRYPTO_RMD128 is not set @@ -2234,14 +2016,11 @@ CONFIG_CRYPTO_SHA1=y # CONFIG_CRYPTO_SHA512 is not set # CONFIG_CRYPTO_TGR192 is not set # CONFIG_CRYPTO_WP512 is not set -# CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL is not set # # Ciphers # CONFIG_CRYPTO_AES=y -# CONFIG_CRYPTO_AES_X86_64 is not set -# CONFIG_CRYPTO_AES_NI_INTEL is not set # CONFIG_CRYPTO_ANUBIS is not set CONFIG_CRYPTO_ARC4=y # CONFIG_CRYPTO_BLOWFISH is not set @@ -2252,13 +2031,11 @@ CONFIG_CRYPTO_DES=y # CONFIG_CRYPTO_FCRYPT is not set # CONFIG_CRYPTO_KHAZAD is not set # CONFIG_CRYPTO_SALSA20 is not set -# CONFIG_CRYPTO_SALSA20_X86_64 is not set # CONFIG_CRYPTO_SEED is not set # CONFIG_CRYPTO_SERPENT is not set # CONFIG_CRYPTO_TEA is not set CONFIG_CRYPTO_TWOFISH=y CONFIG_CRYPTO_TWOFISH_COMMON=y -# CONFIG_CRYPTO_TWOFISH_X86_64 is not set # # Compression @@ -2271,21 +2048,15 @@ CONFIG_CRYPTO_DEFLATE=y # Random Number Generation # # CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set # CONFIG_CRYPTO_HW is not set -CONFIG_HAVE_KVM=y -CONFIG_VIRTUALIZATION=y -# CONFIG_KVM is not set -# CONFIG_VHOST_NET is not set -# CONFIG_VIRTIO_PCI is not set -# CONFIG_VIRTIO_BALLOON is not set # CONFIG_BINARY_PRINTF is not set # # Library routines # CONFIG_BITREVERSE=y -CONFIG_GENERIC_FIND_FIRST_BIT=y -CONFIG_GENERIC_FIND_NEXT_BIT=y CONFIG_GENERIC_FIND_LAST_BIT=y CONFIG_CRC_CCITT=y CONFIG_CRC16=y @@ -2296,7 +2067,10 @@ CONFIG_CRC32=y CONFIG_LIBCRC32C=y CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y CONFIG_REED_SOLOMON=y CONFIG_REED_SOLOMON_ENC8=y CONFIG_REED_SOLOMON_DEC8=y From e75298f29b1aba4a744a06ea04314367fc5fa439 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sun, 1 May 2011 14:15:27 -0400 Subject: [PATCH 1468/2556] input: misc: update Kconfig to include HTC's capella cm3602 driver --- drivers/input/misc/Kconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 299089e14d202..1e70baa4a6197 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -476,4 +476,9 @@ config INPUT_CAPELLA_CM3602 Say Y here to enable the Capella CM3602 Short Distance Proximity Sensor with Ambient Light Sensor. +config LIGHTSENSOR_MICROP + tristate "LIGHTSENSOR MICROP Driver" + help + HTC LIGHTSENSOR Microp support. + endif From faa8ee7cc9eaa6958f0c2dc532faec69c961b9d7 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 8 Apr 2011 20:13:18 +0200 Subject: [PATCH 1469/2556] ath9k_hw: fix stopping rx DMA during resets commit 5882da02e9d9089b7e8c739f3e774aaeeff8b7ba upstream. During PHY errors, the MAC can sometimes fail to enter an idle state on older hardware (before AR9380) after an rx stop has been requested. This typically shows up in the kernel log with messages like these: ath: Could not stop RX, we could be confusing the DMA engine when we start RX up ------------[ cut here ]------------ WARNING: at drivers/net/wireless/ath/ath9k/recv.c:504 ath_stoprecv+0xcc/0xf0 [ath9k]() Call Trace: [<8023f0e8>] dump_stack+0x8/0x34 [<80075050>] warn_slowpath_common+0x78/0xa4 [<80075094>] warn_slowpath_null+0x18/0x24 [<80d66d60>] ath_stoprecv+0xcc/0xf0 [ath9k] [<80d642cc>] ath_set_channel+0xbc/0x270 [ath9k] [<80d65254>] ath_radio_disable+0x4a4/0x7fc [ath9k] When this happens, the state that the MAC enters is easy to identify and does not result in bogus DMA traffic, however to ensure a working state after a channel change, the hardware should still be reset. This patch adds detection for this specific MAC state, after which the above warnings completely disappear in my tests. Signed-off-by: Felix Fietkau Cc: Kyungwan Nam Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/hw.c | 9 --------- drivers/net/wireless/ath/ath9k/mac.c | 25 ++++++++++++++++++++++--- drivers/net/wireless/ath/ath9k/mac.h | 2 +- drivers/net/wireless/ath/ath9k/recv.c | 6 +++--- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 7c0a7c48eea3b..a3b77ae3827c2 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1218,15 +1218,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ah->txchainmask = common->tx_chainmask; ah->rxchainmask = common->rx_chainmask; - if ((common->bus_ops->ath_bus_type != ATH_USB) && !ah->chip_fullsleep) { - ath9k_hw_abortpcurecv(ah); - if (!ath9k_hw_stopdmarecv(ah)) { - ath_dbg(common, ATH_DBG_XMIT, - "Failed to stop receive dma\n"); - bChannelChange = false; - } - } - if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) return -EIO; diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 2915b11edefb9..76d1f8c7db376 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -770,28 +770,47 @@ void ath9k_hw_abortpcurecv(struct ath_hw *ah) } EXPORT_SYMBOL(ath9k_hw_abortpcurecv); -bool ath9k_hw_stopdmarecv(struct ath_hw *ah) +bool ath9k_hw_stopdmarecv(struct ath_hw *ah, bool *reset) { #define AH_RX_STOP_DMA_TIMEOUT 10000 /* usec */ #define AH_RX_TIME_QUANTUM 100 /* usec */ struct ath_common *common = ath9k_hw_common(ah); + u32 mac_status, last_mac_status = 0; int i; + /* Enable access to the DMA observation bus */ + REG_WRITE(ah, AR_MACMISC, + ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) | + (AR_MACMISC_MISC_OBS_BUS_1 << + AR_MACMISC_MISC_OBS_BUS_MSB_S))); + REG_WRITE(ah, AR_CR, AR_CR_RXD); /* Wait for rx enable bit to go low */ for (i = AH_RX_STOP_DMA_TIMEOUT / AH_TIME_QUANTUM; i != 0; i--) { if ((REG_READ(ah, AR_CR) & AR_CR_RXE) == 0) break; + + if (!AR_SREV_9300_20_OR_LATER(ah)) { + mac_status = REG_READ(ah, AR_DMADBG_7) & 0x7f0; + if (mac_status == 0x1c0 && mac_status == last_mac_status) { + *reset = true; + break; + } + + last_mac_status = mac_status; + } + udelay(AH_TIME_QUANTUM); } if (i == 0) { ath_err(common, - "DMA failed to stop in %d ms AR_CR=0x%08x AR_DIAG_SW=0x%08x\n", + "DMA failed to stop in %d ms AR_CR=0x%08x AR_DIAG_SW=0x%08x DMADBG_7=0x%08x\n", AH_RX_STOP_DMA_TIMEOUT / 1000, REG_READ(ah, AR_CR), - REG_READ(ah, AR_DIAG_SW)); + REG_READ(ah, AR_DIAG_SW), + REG_READ(ah, AR_DMADBG_7)); return false; } else { return true; diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 7512f97e8f49a..d9cc2996107e9 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -692,7 +692,7 @@ bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set); void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp); void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning); void ath9k_hw_abortpcurecv(struct ath_hw *ah); -bool ath9k_hw_stopdmarecv(struct ath_hw *ah); +bool ath9k_hw_stopdmarecv(struct ath_hw *ah, bool *reset); int ath9k_hw_beaconq_setup(struct ath_hw *ah); /* Interrupt Handling */ diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 3867a2e8de39c..89546bc302906 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -513,12 +513,12 @@ int ath_startrecv(struct ath_softc *sc) bool ath_stoprecv(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; - bool stopped; + bool stopped, reset = false; spin_lock_bh(&sc->rx.rxbuflock); ath9k_hw_abortpcurecv(ah); ath9k_hw_setrxfilter(ah, 0); - stopped = ath9k_hw_stopdmarecv(ah); + stopped = ath9k_hw_stopdmarecv(ah, &reset); if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ath_edma_stop_recv(sc); @@ -533,7 +533,7 @@ bool ath_stoprecv(struct ath_softc *sc) "confusing the DMA engine when we start RX up\n"); ATH_DBG_WARN_ON_ONCE(!stopped); } - return stopped; + return stopped || reset; } void ath_flushrecv(struct ath_softc *sc) From f18f3747d73891ef607162a8eec4ffe211e21628 Mon Sep 17 00:00:00 2001 From: amit salecha Date: Mon, 11 Apr 2011 02:10:22 +0000 Subject: [PATCH 1470/2556] netxen: limit skb frags for non tso packet commit c968bdf6912cad6d0fc63d7037cc1c870604a808 upstream. Machines are getting deadlock in four node cluster environment. All nodes are accessing (find /gfs2 -depth -print|cpio -ocv > /dev/null) 200 GB storage on a GFS2 filesystem. This result in memory fragmentation and driver receives 18 frags for 1448 byte packets. For non tso packet, fw drops the tx request, if it has >14 frags. Fixing it by pulling extra frags. Signed-off-by: Amit Kumar Salecha Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/netxen/netxen_nic.h | 4 ++-- drivers/net/netxen/netxen_nic_main.c | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h index a11380544e6c5..10f86e0c0d744 100644 --- a/drivers/net/netxen/netxen_nic.h +++ b/drivers/net/netxen/netxen_nic.h @@ -174,7 +174,7 @@ #define MAX_NUM_CARDS 4 -#define MAX_BUFFERS_PER_CMD 32 +#define NETXEN_MAX_FRAGS_PER_TX 14 #define MAX_TSO_HEADER_DESC 2 #define MGMT_CMD_DESC_RESV 4 #define TX_STOP_THRESH ((MAX_SKB_FRAGS >> 2) + MAX_TSO_HEADER_DESC \ @@ -558,7 +558,7 @@ struct netxen_recv_crb { */ struct netxen_cmd_buffer { struct sk_buff *skb; - struct netxen_skb_frag frag_array[MAX_BUFFERS_PER_CMD + 1]; + struct netxen_skb_frag frag_array[MAX_SKB_FRAGS + 1]; u32 frag_count; }; diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index 33fac32e0d9fd..28139df4734ca 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -1841,6 +1841,8 @@ netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) struct cmd_desc_type0 *hwdesc, *first_desc; struct pci_dev *pdev; int i, k; + int delta = 0; + struct skb_frag_struct *frag; u32 producer; int frag_count, no_of_desc; @@ -1848,6 +1850,21 @@ netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) frag_count = skb_shinfo(skb)->nr_frags + 1; + /* 14 frags supported for normal packet and + * 32 frags supported for TSO packet + */ + if (!skb_is_gso(skb) && frag_count > NETXEN_MAX_FRAGS_PER_TX) { + + for (i = 0; i < (frag_count - NETXEN_MAX_FRAGS_PER_TX); i++) { + frag = &skb_shinfo(skb)->frags[i]; + delta += frag->size; + } + + if (!__pskb_pull_tail(skb, delta)) + goto drop_packet; + + frag_count = 1 + skb_shinfo(skb)->nr_frags; + } /* 4 fragments per cmd des */ no_of_desc = (frag_count + 3) >> 2; From f82e213b907b6cd6113e2414f92271a0b1ecb90f Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Wed, 6 Apr 2011 20:40:31 +0200 Subject: [PATCH 1471/2556] ath: add missing regdomain pair 0x5c mapping commit bd39a274fb7b43374c797bafdb7f506598f36f77 upstream. Joe Culler reported a problem with his AR9170 device: > ath: EEPROM regdomain: 0x5c > ath: EEPROM indicates we should expect a direct regpair map > ath: invalid regulatory domain/country code 0x5c > ath: Invalid EEPROM contents It turned out that the regdomain 'APL7_FCCA' was not mapped yet. According to Luis R. Rodriguez [Atheros' engineer] APL7 maps to FCC_CTL and FCCA maps to FCC_CTL as well, so the attached patch should be correct. Reported-by: Joe Culler Acked-by: Luis R. Rodriguez Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/regd_common.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/regd_common.h b/drivers/net/wireless/ath/regd_common.h index 248c670fdfbef..5c2cfe6941524 100644 --- a/drivers/net/wireless/ath/regd_common.h +++ b/drivers/net/wireless/ath/regd_common.h @@ -195,6 +195,7 @@ static struct reg_dmn_pair_mapping regDomainPairs[] = { {APL9_WORLD, CTL_ETSI, CTL_ETSI}, {APL3_FCCA, CTL_FCC, CTL_FCC}, + {APL7_FCCA, CTL_FCC, CTL_FCC}, {APL1_ETSIC, CTL_FCC, CTL_ETSI}, {APL2_ETSIC, CTL_FCC, CTL_ETSI}, {APL2_APLD, CTL_FCC, NO_CTL}, From 343b1a2525647d9035710c03042fe7e27b91a951 Mon Sep 17 00:00:00 2001 From: Liu Yuan Date: Tue, 19 Apr 2011 13:47:58 +0200 Subject: [PATCH 1472/2556] block, blk-sysfs: Fix an err return path in blk_register_queue() commit ed5302d3c25006a9edc7a7fbea97a30483f89ef7 upstream. We do not call blk_trace_remove_sysfs() in err return path if kobject_add() fails. This path fixes it. Signed-off-by: Liu Yuan Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/blk-sysfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 41fb69150b4d3..3655e193e60ea 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -511,8 +511,10 @@ int blk_register_queue(struct gendisk *disk) return ret; ret = kobject_add(&q->kobj, kobject_get(&dev->kobj), "%s", "queue"); - if (ret < 0) + if (ret < 0) { + blk_trace_remove_sysfs(dev); return ret; + } kobject_uevent(&q->kobj, KOBJ_ADD); From c6fedb562695213645926348c9885dcc81324e4c Mon Sep 17 00:00:00 2001 From: Jason Conti Date: Thu, 7 Apr 2011 21:09:57 +0200 Subject: [PATCH 1473/2556] p54: Initialize extra_len in p54_tx_80211 commit a6756da9eace8b4af73e9dea43f1fc2889224c94 upstream. This patch fixes a very serious off-by-one bug in the driver, which could leave the device in an unresponsive state. The problem was that the extra_len variable [used to reserve extra scratch buffer space for the firmware] was left uninitialized. Because p54_assign_address later needs the value to reserve additional space, the resulting frame could be to big for the small device's memory window and everything would immediately come to a grinding halt. Reference: https://bugs.launchpad.net/bugs/722185 Acked-by: Christian Lamparter Signed-off-by: Jason Conti Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/p54/txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index f618b9623e5a6..2cfdd3890bd17 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -705,7 +705,7 @@ int p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb) struct p54_tx_info *p54info; struct p54_hdr *hdr; struct p54_tx_data *txhdr; - unsigned int padding, len, extra_len; + unsigned int padding, len, extra_len = 0; int i, j, ridx; u16 hdr_flags = 0, aid = 0; u8 rate, queue = 0, crypt_offset = 0; From da0309a4a778c46eaba98c58a2b821349d658520 Mon Sep 17 00:00:00 2001 From: Amit Kumar Salecha Date: Tue, 12 Apr 2011 17:05:55 +0000 Subject: [PATCH 1474/2556] qlcnic: limit skb frags for non tso packet commit 91a403caf0f26c71ce4407fd235b2d6fb225fba9 upstream. Machines are getting deadlock in four node cluster environment. All nodes are accessing (find /gfs2 -depth -print|cpio -ocv > /dev/null) 200 GB storage on a GFS2 filesystem. This result in memory fragmentation and driver receives 18 frags for 1448 byte packets. For non tso packet, fw drops the tx request, if it has >14 frags. Fixing it by pulling extra frags. Signed-off-by: Amit Kumar Salecha Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/qlcnic/qlcnic.h | 1 + drivers/net/qlcnic/qlcnic_main.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/net/qlcnic/qlcnic.h b/drivers/net/qlcnic/qlcnic.h index 44e316fd67b85..0f136ff44b42f 100644 --- a/drivers/net/qlcnic/qlcnic.h +++ b/drivers/net/qlcnic/qlcnic.h @@ -99,6 +99,7 @@ #define TX_UDPV6_PKT 0x0c /* Tx defines */ +#define QLCNIC_MAX_FRAGS_PER_TX 14 #define MAX_TSO_HEADER_DESC 2 #define MGMT_CMD_DESC_RESV 4 #define TX_STOP_THRESH ((MAX_SKB_FRAGS >> 2) + MAX_TSO_HEADER_DESC \ diff --git a/drivers/net/qlcnic/qlcnic_main.c b/drivers/net/qlcnic/qlcnic_main.c index 37c04b4fade3b..92619d752430c 100644 --- a/drivers/net/qlcnic/qlcnic_main.c +++ b/drivers/net/qlcnic/qlcnic_main.c @@ -2099,6 +2099,7 @@ qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) struct cmd_desc_type0 *hwdesc, *first_desc; struct pci_dev *pdev; struct ethhdr *phdr; + int delta = 0; int i, k; u32 producer; @@ -2118,6 +2119,19 @@ qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) } frag_count = skb_shinfo(skb)->nr_frags + 1; + /* 14 frags supported for normal packet and + * 32 frags supported for TSO packet + */ + if (!skb_is_gso(skb) && frag_count > QLCNIC_MAX_FRAGS_PER_TX) { + + for (i = 0; i < (frag_count - QLCNIC_MAX_FRAGS_PER_TX); i++) + delta += skb_shinfo(skb)->frags[i].size; + + if (!__pskb_pull_tail(skb, delta)) + goto drop_packet; + + frag_count = 1 + skb_shinfo(skb)->nr_frags; + } /* 4 fragments per cmd des */ no_of_desc = (frag_count + 3) >> 2; From 9acb84c8847ed2008ea86d1e0635f30b3db17026 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 15 Apr 2011 18:08:26 -0400 Subject: [PATCH 1475/2556] nfsd4: fix struct file leak on delegation commit 4ee63624fd927376b97ead3a8d00728d437bc8e8 upstream. Introduced by acfdf5c383b38f7f4dddae41b97c97f1ae058f49. Reported-by: Gerhard Heift Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4state.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 96aaaa47fd0a3..201915af6864d 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -258,6 +258,7 @@ static void nfs4_put_deleg_lease(struct nfs4_file *fp) if (atomic_dec_and_test(&fp->fi_delegees)) { vfs_setlease(fp->fi_deleg_file, F_UNLCK, &fp->fi_lease); fp->fi_lease = NULL; + fput(fp->fi_deleg_file); fp->fi_deleg_file = NULL; } } From eceb743cdc65d1b78d91f786cc2a2acb9ab2d116 Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Mon, 18 Apr 2011 11:48:55 -0400 Subject: [PATCH 1476/2556] nfsd4: Fix filp leak commit a96e5b90804be8b540d30f4a1453fc87f95b3149 upstream. 23fcf2ec93fb8573a653408316af599939ff9a8e (nfsd4: fix oops on lock failure) The above patch breaks free path for stp->st_file. If stp was inserted into sop->so_stateids, we have to free stp->st_file refcount. Because stp->st_file refcount itself is taken whether or not any refcounts are taken on the stp->st_file->fi_fds[]. Signed-off-by: OGAWA Hirofumi Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 201915af6864d..18c356cd50d8a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -403,8 +403,8 @@ static void free_generic_stateid(struct nfs4_stateid *stp) if (stp->st_access_bmap) { oflag = nfs4_access_bmap_to_omode(stp); nfs4_file_put_access(stp->st_file, oflag); - put_nfs4_file(stp->st_file); } + put_nfs4_file(stp->st_file); kmem_cache_free(stateid_slab, stp); } From 045707f94023998aa702536bb2fb441d8a049475 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 16 Mar 2011 19:12:10 +0530 Subject: [PATCH 1477/2556] virtio: Decrement avail idx on buffer detach commit b3258ff1d6086bd2b9eeb556844a868ad7d49bc8 upstream. When detaching a buffer from a vq, the avail.idx value should be decremented as well. This was noticed by hot-unplugging a virtio console port and then plugging in a new one on the same number (re-using the vqs which were just 'disowned'). qemu reported 'Guest moved used index from 0 to 256' when any IO was attempted on the new port. Reported-by: juzhang Signed-off-by: Amit Shah Signed-off-by: Rusty Russell Signed-off-by: Greg Kroah-Hartman --- drivers/virtio/virtio_ring.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index cc2f73e03475b..b0043fb26a4d5 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -371,6 +371,7 @@ void *virtqueue_detach_unused_buf(struct virtqueue *_vq) /* detach_buf clears data, so grab it now. */ buf = vq->data[i]; detach_buf(vq, i); + vq->vring.avail->idx--; END_USE(vq); return buf; } From 111f02f8a5dcd4ca9ba0d9934fa1d423f735b66b Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Mon, 18 Apr 2011 15:45:45 +0200 Subject: [PATCH 1478/2556] x86, gart: Set DISTLBWALKPRB bit always commit c34151a742d84ae65db2088ea30495063f697fbe upstream. The DISTLBWALKPRB bit must be set for the GART because the gatt table is mapped UC. But the current code does not set the bit at boot when the BIOS setup the aperture correctly. Fix that by setting this bit when enabling the GART instead of the other places. Cc: Borislav Petkov Signed-off-by: Joerg Roedel Link: http://lkml.kernel.org/r/1303134346-5805-4-git-send-email-joerg.roedel@amd.com Signed-off-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/gart.h | 4 ++-- arch/x86/kernel/aperture_64.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/include/asm/gart.h b/arch/x86/include/asm/gart.h index 43085bfc99c30..3e7349f91afa4 100644 --- a/arch/x86/include/asm/gart.h +++ b/arch/x86/include/asm/gart.h @@ -66,7 +66,7 @@ static inline void gart_set_size_and_enable(struct pci_dev *dev, u32 order) * Don't enable translation but enable GART IO and CPU accesses. * Also, set DISTLBWALKPRB since GART tables memory is UC. */ - ctl = DISTLBWALKPRB | order << 1; + ctl = order << 1; pci_write_config_dword(dev, AMD64_GARTAPERTURECTL, ctl); } @@ -83,7 +83,7 @@ static inline void enable_gart_translation(struct pci_dev *dev, u64 addr) /* Enable GART translation for this hammer. */ pci_read_config_dword(dev, AMD64_GARTAPERTURECTL, &ctl); - ctl |= GARTEN; + ctl |= GARTEN | DISTLBWALKPRB; ctl &= ~(DISGARTCPU | DISGARTIO); pci_write_config_dword(dev, AMD64_GARTAPERTURECTL, ctl); } diff --git a/arch/x86/kernel/aperture_64.c b/arch/x86/kernel/aperture_64.c index 5955a7800a966..f6a1c2395b51f 100644 --- a/arch/x86/kernel/aperture_64.c +++ b/arch/x86/kernel/aperture_64.c @@ -500,7 +500,7 @@ int __init gart_iommu_hole_init(void) * Don't enable translation yet but enable GART IO and CPU * accesses and set DISTLBWALKPRB since GART table memory is UC. */ - u32 ctl = DISTLBWALKPRB | aper_order << 1; + u32 ctl = aper_order << 1; bus = amd_nb_bus_dev_ranges[i].bus; dev_base = amd_nb_bus_dev_ranges[i].dev_base; From a35cb5371d340fbaa459efc78e0d00ca3cd8e05e Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Mon, 18 Apr 2011 15:45:46 +0200 Subject: [PATCH 1479/2556] x86, gart: Make sure GART does not map physmem above 1TB commit 665d3e2af83c8fbd149534db8f57d82fa6fa6753 upstream. The GART can only map physical memory below 1TB. Make sure the gart driver in the kernel does not try to map memory above 1TB. Signed-off-by: Joerg Roedel Link: http://lkml.kernel.org/r/1303134346-5805-5-git-send-email-joerg.roedel@amd.com Signed-off-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/pci-gart_64.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/pci-gart_64.c b/arch/x86/kernel/pci-gart_64.c index c01ffa5b9b87e..197a46ff5148b 100644 --- a/arch/x86/kernel/pci-gart_64.c +++ b/arch/x86/kernel/pci-gart_64.c @@ -81,6 +81,9 @@ static u32 gart_unmapped_entry; #define AGPEXTERN #endif +/* GART can only remap to physical addresses < 1TB */ +#define GART_MAX_PHYS_ADDR (1ULL << 40) + /* backdoor interface to AGP driver */ AGPEXTERN int agp_memory_reserved; AGPEXTERN __u32 *agp_gatt_table; @@ -212,9 +215,13 @@ static dma_addr_t dma_map_area(struct device *dev, dma_addr_t phys_mem, size_t size, int dir, unsigned long align_mask) { unsigned long npages = iommu_num_pages(phys_mem, size, PAGE_SIZE); - unsigned long iommu_page = alloc_iommu(dev, npages, align_mask); + unsigned long iommu_page; int i; + if (unlikely(phys_mem + size > GART_MAX_PHYS_ADDR)) + return bad_dma_addr; + + iommu_page = alloc_iommu(dev, npages, align_mask); if (iommu_page == -1) { if (!nonforced_iommu(dev, phys_mem, size)) return phys_mem; From 6a6a3e00ccd23f5b9d146a4b0591c8b61b4d0bb2 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 2 Nov 2010 08:05:51 +0100 Subject: [PATCH 1480/2556] intel-iommu: Fix use after release during device attach commit 7a6610139a1e1d9297dd1c5d178022eac36839cb upstream. Obtain the new pgd pointer before releasing the page containing this value. Signed-off-by: Jan Kiszka Reviewed-by: Sheng Yang Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- drivers/pci/intel-iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 4789f8e8bf7ad..35463ddf10a16 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -3627,9 +3627,9 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, pte = dmar_domain->pgd; if (dma_pte_present(pte)) { - free_pgtable_page(dmar_domain->pgd); dmar_domain->pgd = (struct dma_pte *) phys_to_virt(dma_pte_addr(pte)); + free_pgtable_page(pte); } dmar_domain->agaw--; } From 6a815cf43b27504d50e0153b6d60039c4558a429 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Fri, 4 Mar 2011 14:52:16 -0700 Subject: [PATCH 1481/2556] intel-iommu: Unlink domain from iommu commit a97590e56d0d58e1dd262353f7cbd84e81d8e600 upstream. When we remove a device, we unlink the iommu from the domain, but we never do the reverse unlinking of the domain from the iommu. This means that we never clear iommu->domain_ids, eventually leading to resource exhaustion if we repeatedly bind and unbind a device to a driver. Also free empty domains to avoid a resource leak. Signed-off-by: Alex Williamson Acked-by: Donald Dutile Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- drivers/pci/intel-iommu.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 35463ddf10a16..292f2233295ef 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -3260,9 +3260,15 @@ static int device_notifier(struct notifier_block *nb, if (!domain) return 0; - if (action == BUS_NOTIFY_UNBOUND_DRIVER && !iommu_pass_through) + if (action == BUS_NOTIFY_UNBOUND_DRIVER && !iommu_pass_through) { domain_remove_one_dev_info(domain, pdev); + if (!(domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) && + !(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY) && + list_empty(&domain->devices)) + domain_exit(domain); + } + return 0; } @@ -3411,6 +3417,11 @@ static void domain_remove_one_dev_info(struct dmar_domain *domain, domain->iommu_count--; domain_update_iommu_cap(domain); spin_unlock_irqrestore(&domain->iommu_lock, tmp_flags); + + spin_lock_irqsave(&iommu->lock, tmp_flags); + clear_bit(domain->id, iommu->domain_ids); + iommu->domains[domain->id] = NULL; + spin_unlock_irqrestore(&iommu->lock, tmp_flags); } spin_unlock_irqrestore(&device_domain_lock, flags); From 5aefedf020635bfb1c1390b920c6b8e442be25b0 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Fri, 4 Mar 2011 14:52:30 -0700 Subject: [PATCH 1482/2556] intel-iommu: Fix get_domain_for_dev() error path commit 2fe9723df8e45fd247782adea244a5e653c30bf4 upstream. If we run out of domain_ids and fail iommu_attach_domain(), we fall into domain_exit() without having setup enough of the domain structure for this to do anything useful. In fact, it typically runs off into the weeds walking the bogus domain->devices list. Just free the domain. Signed-off-by: Alex Williamson Acked-by: Donald Dutile Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- drivers/pci/intel-iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 292f2233295ef..5dc5d3e3508e8 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -1835,7 +1835,7 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw) ret = iommu_attach_domain(domain, iommu); if (ret) { - domain_exit(domain); + free_domain_mem(domain); goto error; } From c5fd52b24b7ea7ba603e99e33235857e3deeee3f Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 19 Apr 2011 15:24:59 -0400 Subject: [PATCH 1483/2556] drm/radeon/kms: pll tweaks for r7xx commit 5785e53ffa73f77fb19e378c899027afc07272bc upstream. Prefer min m to max p only on pre-r7xx asics. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=36197 Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/atombios_crtc.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 08612572c5581..bede31c6571bd 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -532,10 +532,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, else pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV; - if ((rdev->family == CHIP_R600) || - (rdev->family == CHIP_RV610) || - (rdev->family == CHIP_RV630) || - (rdev->family == CHIP_RV670)) + if (rdev->family < CHIP_RV770) pll->flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP; } else { pll->flags |= RADEON_PLL_LEGACY; @@ -565,7 +562,6 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { if (ss_enabled) { if (ss->refdiv) { - pll->flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP; pll->flags |= RADEON_PLL_USE_REF_DIV; pll->reference_div = ss->refdiv; if (ASIC_IS_AVIVO(rdev)) From e0b0be738453aabea3ada4617a7f30c020deb9d3 Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Tue, 19 Apr 2011 23:50:48 +0200 Subject: [PATCH 1484/2556] drm/nouveau: fix notifier memory corruption bug commit a18d89ca026140eb8ac4459bf70a01c571dd9a32 upstream. nouveau_bo_wr32 expects offset to be in words, but we pass value in bytes, so after commit 73412c3854c877e5f37ad944ee8977addde4d35a ("drm/nouveau: allocate kernel's notifier object at end of block") we started to overwrite some memory after notifier buffer object (previously m2mf_ntfy was always 0, so it didn't matter it was a value in bytes). Reported-by: Dominik Brodowski Reported-by: Nigel Cunningham Signed-off-by: Marcin Slusarz Cc: Ben Skeggs Cc: Pekka Paalanen Signed-off-by: Ben Skeggs Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/nouveau/nouveau_fbcon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 60769d2f9a668..7826be0653e19 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -181,13 +181,13 @@ nouveau_fbcon_sync(struct fb_info *info) OUT_RING (chan, 0); } - nouveau_bo_wr32(chan->notifier_bo, chan->m2mf_ntfy + 3, 0xffffffff); + nouveau_bo_wr32(chan->notifier_bo, chan->m2mf_ntfy/4 + 3, 0xffffffff); FIRE_RING(chan); mutex_unlock(&chan->mutex); ret = -EBUSY; for (i = 0; i < 100000; i++) { - if (!nouveau_bo_rd32(chan->notifier_bo, chan->m2mf_ntfy + 3)) { + if (!nouveau_bo_rd32(chan->notifier_bo, chan->m2mf_ntfy/4 + 3)) { ret = 0; break; } From fee1e99cca4a66a3d6d2983c1e1782a0e2747f57 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 14 Apr 2011 11:19:50 -0400 Subject: [PATCH 1485/2556] drm/radeon/kms: fix bad shift in atom iio table parser commit 8e461123f28e6b17456225e70eb834b3b30d28bb upstream. Noticed by Patrick Lowry. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/atom.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c index d71d375149f85..7bd7456890974 100644 --- a/drivers/gpu/drm/radeon/atom.c +++ b/drivers/gpu/drm/radeon/atom.c @@ -135,7 +135,7 @@ static uint32_t atom_iio_execute(struct atom_context *ctx, int base, case ATOM_IIO_MOVE_INDEX: temp &= ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << - CU8(base + 2)); + CU8(base + 3)); temp |= ((index >> CU8(base + 2)) & (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base + @@ -145,7 +145,7 @@ static uint32_t atom_iio_execute(struct atom_context *ctx, int base, case ATOM_IIO_MOVE_DATA: temp &= ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << - CU8(base + 2)); + CU8(base + 3)); temp |= ((data >> CU8(base + 2)) & (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base + @@ -155,7 +155,7 @@ static uint32_t atom_iio_execute(struct atom_context *ctx, int base, case ATOM_IIO_MOVE_ATTR: temp &= ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << - CU8(base + 2)); + CU8(base + 3)); temp |= ((ctx-> io_attr >> CU8(base + 2)) & (0xFFFFFFFF >> (32 - From 72846ac3462c7d2bebc25c9a4d3e8f5739c5fb66 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 12 Apr 2011 18:06:51 +0100 Subject: [PATCH 1486/2556] drm/i915: Sanitize the output registers after resume commit f6e5b1603b8bb7131b6778d0d4e2e5dda120a379 upstream. Similar to booting, we need to inspect the state left by the BIOS and remove any conflicting bits before we take over. The example reported by Seth Forshee is very similar to the bug we encountered with the state left by grub2, that the crtc pipe<->planning mapping was reversed from our expectations and so we failed to turn off the outputs when booting or, in this case, resuming. This may be in fact the same bug, but triggered at resume time. This patch rearranges the code we already have to clear up the conflicting state upon init and calls it from reset (which is called after we have lost control of the hardware, i.e. along both the boot and resume paths) instead. Reported-and-tested-by: Seth Forshee Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=35796 Signed-off-by: Chris Wilson Reviewed-by: Keith Packard Signed-off-by: Keith Packard Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_display.c | 68 +++++++++++++++------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 49fb54fd9a187..ecf8f947b4b79 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5630,36 +5630,6 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, return ret; } -static void intel_crtc_reset(struct drm_crtc *crtc) -{ - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - /* Reset flags back to the 'unknown' status so that they - * will be correctly set on the initial modeset. - */ - intel_crtc->dpms_mode = -1; -} - -static struct drm_crtc_helper_funcs intel_helper_funcs = { - .dpms = intel_crtc_dpms, - .mode_fixup = intel_crtc_mode_fixup, - .mode_set = intel_crtc_mode_set, - .mode_set_base = intel_pipe_set_base, - .mode_set_base_atomic = intel_pipe_set_base_atomic, - .load_lut = intel_crtc_load_lut, - .disable = intel_crtc_disable, -}; - -static const struct drm_crtc_funcs intel_crtc_funcs = { - .reset = intel_crtc_reset, - .cursor_set = intel_crtc_cursor_set, - .cursor_move = intel_crtc_cursor_move, - .gamma_set = intel_crtc_gamma_set, - .set_config = drm_crtc_helper_set_config, - .destroy = intel_crtc_destroy, - .page_flip = intel_crtc_page_flip, -}; - static void intel_sanitize_modesetting(struct drm_device *dev, int pipe, int plane) { @@ -5710,6 +5680,42 @@ static void intel_sanitize_modesetting(struct drm_device *dev, } } +static void intel_crtc_reset(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + /* Reset flags back to the 'unknown' status so that they + * will be correctly set on the initial modeset. + */ + intel_crtc->dpms_mode = -1; + + /* We need to fix up any BIOS configuration that conflicts with + * our expectations. + */ + intel_sanitize_modesetting(dev, intel_crtc->pipe, intel_crtc->plane); +} + +static struct drm_crtc_helper_funcs intel_helper_funcs = { + .dpms = intel_crtc_dpms, + .mode_fixup = intel_crtc_mode_fixup, + .mode_set = intel_crtc_mode_set, + .mode_set_base = intel_pipe_set_base, + .mode_set_base_atomic = intel_pipe_set_base_atomic, + .load_lut = intel_crtc_load_lut, + .disable = intel_crtc_disable, +}; + +static const struct drm_crtc_funcs intel_crtc_funcs = { + .reset = intel_crtc_reset, + .cursor_set = intel_crtc_cursor_set, + .cursor_move = intel_crtc_cursor_move, + .gamma_set = intel_crtc_gamma_set, + .set_config = drm_crtc_helper_set_config, + .destroy = intel_crtc_destroy, + .page_flip = intel_crtc_page_flip, +}; + static void intel_crtc_init(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -5759,8 +5765,6 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) setup_timer(&intel_crtc->idle_timer, intel_crtc_idle_timer, (unsigned long)intel_crtc); - - intel_sanitize_modesetting(dev, intel_crtc->pipe, intel_crtc->plane); } int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, From 60edc65d5d2c1b61d20f28b70b3ac5140dc82f6a Mon Sep 17 00:00:00 2001 From: Mathew McKernan Date: Tue, 12 Apr 2011 06:51:37 +0100 Subject: [PATCH 1487/2556] drm/i915/tv: Remember the detected TV type commit d5627663f2088fa4be447fdcfd52bcb233448d85 upstream. During detect() we would probe the connection bits to determine if there was a TV attached, and what video input type (Component, S-Video, Composite, etc) to use. However, we promptly discarded this vital bit of information and never propagated it to where it was used to determine the correct modes and setup the control registers. Fix it! This fixes a regression from 7b334fcb45b757ffb093696ca3de1b0c8b4a33f1. Reported-and-tested-by: Mathew McKernan Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=35977 Signed-off-by: Mathew McKernan Signed-off-by: Chris Wilson Acked-by: Paul Menzel Signed-off-by: Keith Packard Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_tv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index fe4a53a50b833..65edb227ae811 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1380,7 +1380,9 @@ intel_tv_detect(struct drm_connector *connector, bool force) if (type < 0) return connector_status_disconnected; + intel_tv->type = type; intel_tv_find_better_format(connector); + return connector_status_connected; } From 7b394b6f584438e9048825dd4571299945ce9b54 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sun, 27 Mar 2011 04:05:00 +0400 Subject: [PATCH 1488/2556] tty/n_gsm: fix bug in CRC calculation for gsm1 mode commit 9db4e4381a8e881ff65a5d3400bfa471f84217e7 upstream. Problem description: gsm_queue() calculate a CRC for arrived frames. As a last step of CRC calculation it call gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs); This work perfectly for the case of GSM0 mode as gsm->received_fcs contain the last piece of data required to generate final CRC. gsm->received_fcs is not used for GSM1 mode. Thus we put an additional byte to CRC calculation. As result we get a wrong CRC and reject incoming frame. Signed-off-by: Mikhail Kshevetskiy Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index aa2e5d3eb01a4..c4b0ef1a38747 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -1659,8 +1659,12 @@ static void gsm_queue(struct gsm_mux *gsm) if ((gsm->control & ~PF) == UI) gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len); - /* generate final CRC with received FCS */ - gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs); + if (gsm->encoding == 0){ + /* WARNING: gsm->received_fcs is used for gsm->encoding = 0 only. + In this case it contain the last piece of data + required to generate final CRC */ + gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs); + } if (gsm->fcs != GOOD_FCS) { gsm->bad_fcs++; if (debug & 4) From 1eb710f4e01a9afab735828c87ef13e2e6b57386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 11 Apr 2011 10:59:09 +0200 Subject: [PATCH 1489/2556] serial/imx: read cts state only after acking cts change irq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 5680e94148a86e8c31fdc5cb0ea0d5c6810c05b0 upstream. If cts changes between reading the level at the cts input (USR1_RTSS) and acking the irq (USR1_RTSD) the last edge doesn't generate an irq and uart_handle_cts_change is called with a outdated value for cts. The race was introduced by commit ceca629 ([ARM] 2971/1: i.MX uart handle rts irq) Reported-by: Arwed Springer Tested-by: Arwed Springer Signed-off-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index dfcf4b1878aa3..0d66751c0cbc0 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -382,12 +382,13 @@ static void imx_start_tx(struct uart_port *port) static irqreturn_t imx_rtsint(int irq, void *dev_id) { struct imx_port *sport = dev_id; - unsigned int val = readl(sport->port.membase + USR1) & USR1_RTSS; + unsigned int val; unsigned long flags; spin_lock_irqsave(&sport->port.lock, flags); writel(USR1_RTSD, sport->port.membase + USR1); + val = readl(sport->port.membase + USR1) & USR1_RTSS; uart_handle_cts_change(&sport->port, !!val); wake_up_interruptible(&sport->port.state->port.delta_msr_wait); From 8196315af8f0ce8856088501beab055196e3476b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 8 Apr 2011 16:32:16 +0900 Subject: [PATCH 1490/2556] ASoC: Fix output PGA enabling in wm_hubs CODECs commit 39cca168bdfaef9d0c496ec27f292445d6184946 upstream. The output PGA was not being powered up in headphone and speaker paths, removing the ability to offer volume control and mute with the output PGA. Signed-off-by: Mark Brown Acked-by: Liam Girdwood Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/wm_hubs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 516892706063d..d365f4344f96d 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -739,12 +739,12 @@ static const struct snd_soc_dapm_route analogue_routes[] = { { "SPKL", "Input Switch", "MIXINL" }, { "SPKL", "IN1LP Switch", "IN1LP" }, - { "SPKL", "Output Switch", "Left Output Mixer" }, + { "SPKL", "Output Switch", "Left Output PGA" }, { "SPKL", NULL, "TOCLK" }, { "SPKR", "Input Switch", "MIXINR" }, { "SPKR", "IN1RP Switch", "IN1RP" }, - { "SPKR", "Output Switch", "Right Output Mixer" }, + { "SPKR", "Output Switch", "Right Output PGA" }, { "SPKR", NULL, "TOCLK" }, { "SPKL Boost", "Direct Voice Switch", "Direct Voice" }, @@ -766,8 +766,8 @@ static const struct snd_soc_dapm_route analogue_routes[] = { { "SPKOUTRP", NULL, "SPKR Driver" }, { "SPKOUTRN", NULL, "SPKR Driver" }, - { "Left Headphone Mux", "Mixer", "Left Output Mixer" }, - { "Right Headphone Mux", "Mixer", "Right Output Mixer" }, + { "Left Headphone Mux", "Mixer", "Left Output PGA" }, + { "Right Headphone Mux", "Mixer", "Right Output PGA" }, { "Headphone PGA", NULL, "Left Headphone Mux" }, { "Headphone PGA", NULL, "Right Headphone Mux" }, From b87e6b59960437a1d9c0ddb1723ec20cc33d18d2 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 12 Apr 2011 19:33:28 +0200 Subject: [PATCH 1491/2556] ASoC: codecs: JZ4740: Fix OOPS commit 1fdf9b49e9e7788d09bad4b08a6a821ac39798f3 upstream. Commit ce6120cc(ASoC: Decouple DAPM from CODECs) changed the signature of snd_soc_dapm_widgets_new to take an pointer to a snd_soc_dapm_context instead of a snd_soc_codec. The call to snd_soc_dapm_widgets_new in jz4740_codec_dev_probe was not updated to reflect this change, which results in a compiletime warning and a runtime OOPS. Since the core code calls snd_soc_dapm_widgets_new after the codec has been registered it can be dropped here. Signed-off-by: Lars-Peter Clausen Acked-by: Liam Girdwood Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/jz4740.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c index f7cd346fd7275..f5ccdbf7ebc6e 100644 --- a/sound/soc/codecs/jz4740.c +++ b/sound/soc/codecs/jz4740.c @@ -308,8 +308,6 @@ static int jz4740_codec_dev_probe(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(dapm, jz4740_codec_dapm_routes, ARRAY_SIZE(jz4740_codec_dapm_routes)); - snd_soc_dapm_new_widgets(codec); - jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; From cb6f6c06a5e23fe12f1898a072edc670420ebc1f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 15 Apr 2011 10:11:12 +0200 Subject: [PATCH 1492/2556] ALSA: hda - Add a fix-up for Acer dmic with ALC271x codec commit 6981d184376e74391c23c116a068f8d1305f0e57 upstream. Acer laptops with ALC271x needs a magic initialization for digital-mic to make it working with mono streams (and PulseAudio). Added a fix-up applied to Acer with ALC271x generically. Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index da7cdca4776d5..8ff0223fb10f2 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -14945,6 +14945,23 @@ static void alc269_fixup_hweq(struct hda_codec *codec, alc_write_coef_idx(codec, 0x1e, coef | 0x80); } +static void alc271_fixup_dmic(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + static struct hda_verb verbs[] = { + {0x20, AC_VERB_SET_COEF_INDEX, 0x0d}, + {0x20, AC_VERB_SET_PROC_COEF, 0x4000}, + {} + }; + unsigned int cfg; + + if (strcmp(codec->chip_name, "ALC271X")) + return; + cfg = snd_hda_codec_get_pincfg(codec, 0x12); + if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED) + snd_hda_sequence_write(codec, verbs); +} + enum { ALC269_FIXUP_SONY_VAIO, ALC275_FIXUP_SONY_VAIO_GPIO2, @@ -14953,6 +14970,7 @@ enum { ALC269_FIXUP_ASUS_G73JW, ALC269_FIXUP_LENOVO_EAPD, ALC275_FIXUP_SONY_HWEQ, + ALC271_FIXUP_DMIC, }; static const struct alc_fixup alc269_fixups[] = { @@ -15006,7 +15024,11 @@ static const struct alc_fixup alc269_fixups[] = { .v.func = alc269_fixup_hweq, .chained = true, .chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2 - } + }, + [ALC271_FIXUP_DMIC] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc271_fixup_dmic, + }, }; static struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -15015,6 +15037,7 @@ static struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), + SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE), From 068e0202c8c0822c36025198607d1e47d779c86f Mon Sep 17 00:00:00 2001 From: Maxime Bizon Date: Wed, 16 Mar 2011 14:58:32 +0100 Subject: [PATCH 1493/2556] ahci: don't enable port irq before handler is registered commit 7b3a24c57d2eeda8dba9c205342b12689c4679f9 upstream. The ahci_pmp_attach() & ahci_pmp_detach() unmask port irqs, but they are also called during port initialization, before ahci host irq handler is registered. On ce4100 platform, this sometimes triggers "irq 4: nobody cared" message when loading driver. Fixed this by not touching the register if the port is in frozen state, and mark all uninitialized port as frozen. Signed-off-by: Maxime Bizon Acked-by: Tejun Heo Signed-off-by: Jeff Garzik Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libahci.c | 17 +++++++++++++++-- drivers/ata/libata-core.c | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 26d452339e98f..8498eb5cd413b 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -1897,7 +1897,17 @@ static void ahci_pmp_attach(struct ata_port *ap) ahci_enable_fbs(ap); pp->intr_mask |= PORT_IRQ_BAD_PMP; - writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); + + /* + * We must not change the port interrupt mask register if the + * port is marked frozen, the value in pp->intr_mask will be + * restored later when the port is thawed. + * + * Note that during initialization, the port is marked as + * frozen since the irq handler is not yet registered. + */ + if (!(ap->pflags & ATA_PFLAG_FROZEN)) + writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); } static void ahci_pmp_detach(struct ata_port *ap) @@ -1913,7 +1923,10 @@ static void ahci_pmp_detach(struct ata_port *ap) writel(cmd, port_mmio + PORT_CMD); pp->intr_mask &= ~PORT_IRQ_BAD_PMP; - writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); + + /* see comment above in ahci_pmp_attach() */ + if (!(ap->pflags & ATA_PFLAG_FROZEN)) + writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); } int ahci_port_resume(struct ata_port *ap) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index d4e52e2148593..4ccce0f371a36 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5479,8 +5479,8 @@ struct ata_port *ata_port_alloc(struct ata_host *host) ap = kzalloc(sizeof(*ap), GFP_KERNEL); if (!ap) return NULL; - - ap->pflags |= ATA_PFLAG_INITIALIZING; + + ap->pflags |= ATA_PFLAG_INITIALIZING | ATA_PFLAG_FROZEN; ap->lock = &host->lock; ap->print_id = -1; ap->host = host; From 0ab7f6fb02a9a588974c78485c57ef2ebb7aed6f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 16 Mar 2011 11:14:55 +0100 Subject: [PATCH 1494/2556] libata: Implement ATA_FLAG_NO_DIPM and apply it to mcp65 commit ae01b2493c3bf03c504c32ac4ebb01d528508db3 upstream. NVIDIA mcp65 familiy of controllers cause command timeouts when DIPM is used. Implement ATA_FLAG_NO_DIPM and apply it. This problem was reported by Stefan Bader in the following thread. http://thread.gmane.org/gmane.linux.ide/48841 stable: applicable to 2.6.37 and 38. Signed-off-by: Tejun Heo Reported-by: Stefan Bader Signed-off-by: Jeff Garzik Signed-off-by: Greg Kroah-Hartman --- drivers/ata/ahci.c | 2 +- drivers/ata/libata-eh.c | 6 ++++-- include/linux/libata.h | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 34e08f63b6889..54c096b1a71cb 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -150,7 +150,7 @@ static const struct ata_port_info ahci_port_info[] = { { AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ), - .flags = AHCI_FLAG_COMMON, + .flags = AHCI_FLAG_COMMON | ATA_FLAG_NO_DIPM, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index e16850e8d2f8a..fe18c2de41ba1 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -3276,6 +3276,7 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, struct ata_eh_context *ehc = &link->eh_context; struct ata_device *dev, *link_dev = NULL, *lpm_dev = NULL; enum ata_lpm_policy old_policy = link->lpm_policy; + bool no_dipm = ap->flags & ATA_FLAG_NO_DIPM; unsigned int hints = ATA_LPM_EMPTY | ATA_LPM_HIPM; unsigned int err_mask; int rc; @@ -3292,7 +3293,7 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, */ ata_for_each_dev(dev, link, ENABLED) { bool hipm = ata_id_has_hipm(dev->id); - bool dipm = ata_id_has_dipm(dev->id); + bool dipm = ata_id_has_dipm(dev->id) && !no_dipm; /* find the first enabled and LPM enabled devices */ if (!link_dev) @@ -3349,7 +3350,8 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, /* host config updated, enable DIPM if transitioning to MIN_POWER */ ata_for_each_dev(dev, link, ENABLED) { - if (policy == ATA_LPM_MIN_POWER && ata_id_has_dipm(dev->id)) { + if (policy == ATA_LPM_MIN_POWER && !no_dipm && + ata_id_has_dipm(dev->id)) { err_mask = ata_dev_set_feature(dev, SETFEATURES_SATA_ENABLE, SATA_DIPM); if (err_mask && err_mask != AC_ERR_DEV) { diff --git a/include/linux/libata.h b/include/linux/libata.h index c9c5d7ad1a2bc..1f000807847d2 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -203,6 +203,7 @@ enum { * management */ ATA_FLAG_SW_ACTIVITY = (1 << 22), /* driver supports sw activity * led */ + ATA_FLAG_NO_DIPM = (1 << 23), /* host not happy with DIPM */ /* bits 24:31 of ap->flags are reserved for LLD specific flags */ From fb57290759588bbcb60d6d9104f8947fdba5aa19 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sat, 23 Apr 2011 18:42:56 +0100 Subject: [PATCH 1495/2556] kconfig: Avoid buffer underrun in choice input commit 3ba41621156681afcdbcd624e3191cbc65eb94f4 upstream. Commit 40aee729b350 ('kconfig: fix default value for choice input') fixed some cases where kconfig would select the wrong option from a choice with a single valid option and thus enter an infinite loop. However, this broke the test for user input of the form 'N?', because when kconfig selects the single valid option the input is zero-length and the test will read the byte before the input buffer. If this happens to contain '?' (as it will in a mips build on Debian unstable today) then kconfig again enters an infinite loop. Signed-off-by: Ben Hutchings Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- scripts/kconfig/conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 659326c3e8957..006ad817cd5f0 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -332,7 +332,7 @@ static int conf_choice(struct menu *menu) } if (!child) continue; - if (line[strlen(line) - 1] == '?') { + if (line[0] && line[strlen(line) - 1] == '?') { print_help(child); continue; } From 75db8ad812878495309d3d0b40467e9b9b61b29a Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 21 Apr 2011 14:49:55 +0300 Subject: [PATCH 1496/2556] UBIFS: fix master node recovery commit 6e0d9fd38b750d678bf9fd07db23582f52fafa55 upstream. This patch fixes the following symptoms: 1. Unmount UBIFS cleanly. 2. Start mounting UBIFS R/W and have a power cut immediately 3. Start mounting UBIFS R/O, this succeeds 4. Try to re-mount UBIFS R/W - this fails immediately or later on, because UBIFS will write the master node to the flash area which has been written before. The analysis of the problem: 1. UBIFS is unmounted cleanly, both copies of the master node are clean. 2. UBIFS is being mounter R/W, starts changing master node copy 1, and a power cut happens. The copy N1 becomes corrupted. 3. UBIFS is being mounted R/O. It notices the copy N1 is corrupted and reads copy N2. Copy N2 is clean. 4. Because of R/O mode, UBIFS cannot recover copy 1. 5. The mount code (ubifs_mount()) sees that the master node is clean, so it decides that no recovery is needed. 6. We are re-mounting R/W. UBIFS believes no recovery is needed and starts updating the master node, but copy N1 is still corrupted and was not recovered! Fix this problem by marking the master node as dirty every time we recover it and we are in R/O mode. This forces further recovery and the UBIFS cleans-up the corruptions and recovers the copy N1 when re-mounting R/W later. Signed-off-by: Artem Bityutskiy Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/recovery.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/fs/ubifs/recovery.c b/fs/ubifs/recovery.c index 77e9b874b6c22..c0c590feabe1a 100644 --- a/fs/ubifs/recovery.c +++ b/fs/ubifs/recovery.c @@ -300,6 +300,32 @@ int ubifs_recover_master_node(struct ubifs_info *c) goto out_free; } memcpy(c->rcvrd_mst_node, c->mst_node, UBIFS_MST_NODE_SZ); + + /* + * We had to recover the master node, which means there was an + * unclean reboot. However, it is possible that the master node + * is clean at this point, i.e., %UBIFS_MST_DIRTY is not set. + * E.g., consider the following chain of events: + * + * 1. UBIFS was cleanly unmounted, so the master node is clean + * 2. UBIFS is being mounted R/W and starts changing the master + * node in the first (%UBIFS_MST_LNUM). A power cut happens, + * so this LEB ends up with some amount of garbage at the + * end. + * 3. UBIFS is being mounted R/O. We reach this place and + * recover the master node from the second LEB + * (%UBIFS_MST_LNUM + 1). But we cannot update the media + * because we are being mounted R/O. We have to defer the + * operation. + * 4. However, this master node (@c->mst_node) is marked as + * clean (since the step 1). And if we just return, the + * mount code will be confused and won't recover the master + * node when it is re-mounter R/W later. + * + * Thus, to force the recovery by marking the master node as + * dirty. + */ + c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY); } else { /* Write the recovered master node */ c->max_sqnum = le64_to_cpu(mst->ch.sqnum) - 1; From 7bf87075b2ce9d35a99916b446622ba8825e7142 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Wed, 23 Feb 2011 21:39:59 +0800 Subject: [PATCH 1497/2556] ideapad: read brightness setting on brightness key notify commit 2165136585b5c7d6f118f1d90fbde550bb7de212 upstream. BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=25922 On ideapad Y530, the brightness key notify will be blocked if the last notify is not responsed by getting the brightness value. Read value when we get the notify shall fix the problem and will not have any difference on other ideapads. Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/ideapad-laptop.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 114d95247cdf8..21b101899baee 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -459,6 +459,8 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) if (test_bit(vpc_bit, &vpc1)) { if (vpc_bit == 9) ideapad_sync_rfk_state(adevice); + else if (vpc_bit == 4) + read_ec_data(handle, 0x12, &vpc2); else ideapad_input_report(priv, vpc_bit); } From 36d08a041f93f2c5370f95f9611c9065bb79e4fb Mon Sep 17 00:00:00 2001 From: Kyle McMartin Date: Tue, 22 Mar 2011 16:19:50 -0400 Subject: [PATCH 1498/2556] ACPI battery: fribble sysfs files from a resume notifier commit 25be5821521640eb00b7eb219ffe59664510d073 upstream. Commit da8aeb92 re-poked the battery on resume, but Linus reports that it broke his eee and partially reverted it in b23fffd7. Unfortunately this also results in my x201s giving crack values until the sysfs files are poked again. In the revert message, it was suggested that we poke it from a PM notifier, so let's do that. With this in place, I haven't noticed the units going nutty on my gnome-power-manager across a dozen suspends or so... Signed-off-by: Kyle McMartin Acked-by: Rafael J. Wysocki Signed-off-by: Len Brown Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/battery.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index ac1a599f51476..fcc13ac0aa187 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -33,6 +33,7 @@ #include #include #include +#include #ifdef CONFIG_ACPI_PROCFS_POWER #include @@ -102,6 +103,7 @@ struct acpi_battery { struct mutex lock; struct power_supply bat; struct acpi_device *device; + struct notifier_block pm_nb; unsigned long update_time; int rate_now; int capacity_now; @@ -940,6 +942,21 @@ static void acpi_battery_notify(struct acpi_device *device, u32 event) power_supply_changed(&battery->bat); } +static int battery_notify(struct notifier_block *nb, + unsigned long mode, void *_unused) +{ + struct acpi_battery *battery = container_of(nb, struct acpi_battery, + pm_nb); + switch (mode) { + case PM_POST_SUSPEND: + sysfs_remove_battery(battery); + sysfs_add_battery(battery); + break; + } + + return 0; +} + static int acpi_battery_add(struct acpi_device *device) { int result = 0; @@ -972,6 +989,10 @@ static int acpi_battery_add(struct acpi_device *device) #endif kfree(battery); } + + battery->pm_nb.notifier_call = battery_notify; + register_pm_notifier(&battery->pm_nb); + return result; } @@ -982,6 +1003,7 @@ static int acpi_battery_remove(struct acpi_device *device, int type) if (!device || !acpi_driver_data(device)) return -EINVAL; battery = acpi_driver_data(device); + unregister_pm_notifier(&battery->pm_nb); #ifdef CONFIG_ACPI_PROCFS_POWER acpi_battery_remove_fs(device); #endif From 5002397e6c6f9c2f5fb92d8990eb2663e43a3012 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 14 Jan 2011 00:06:27 +0100 Subject: [PATCH 1499/2556] ath9k_hw: partially revert "fix dma descriptor rx error bit parsing" commit 115dad7a7f42e68840392767323ceb9306dbdb36 upstream. The rx error bit parsing was changed to consider PHY errors and various decryption errors separately. While correct according to the documentation, this is causing spurious decryption error reports in some situations. Fix this by restoring the original order of the checks in those places, where the errors are meant to be mutually exclusive. If a CRC error is reported, then MIC failure and decryption errors are irrelevant, and a PHY error is unlikely. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/ar9003_mac.c | 8 ++++---- drivers/net/wireless/ath/ath9k/mac.c | 14 ++++++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index 4ceddbbdfcee6..038a0cbfc6e7c 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -615,7 +615,7 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, */ if (rxsp->status11 & AR_CRCErr) rxs->rs_status |= ATH9K_RXERR_CRC; - if (rxsp->status11 & AR_PHYErr) { + else if (rxsp->status11 & AR_PHYErr) { phyerr = MS(rxsp->status11, AR_PHYErrCode); /* * If we reach a point here where AR_PostDelimCRCErr is @@ -638,11 +638,11 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, rxs->rs_phyerr = phyerr; } - } - if (rxsp->status11 & AR_DecryptCRCErr) + } else if (rxsp->status11 & AR_DecryptCRCErr) rxs->rs_status |= ATH9K_RXERR_DECRYPT; - if (rxsp->status11 & AR_MichaelErr) + else if (rxsp->status11 & AR_MichaelErr) rxs->rs_status |= ATH9K_RXERR_MIC; + if (rxsp->status11 & AR_KeyMiss) rxs->rs_status |= ATH9K_RXERR_DECRYPT; } diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 76d1f8c7db376..e9fc97d4c9017 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -690,17 +690,23 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, rs->rs_flags |= ATH9K_RX_DECRYPT_BUSY; if ((ads.ds_rxstatus8 & AR_RxFrameOK) == 0) { + /* + * Treat these errors as mutually exclusive to avoid spurious + * extra error reports from the hardware. If a CRC error is + * reported, then decryption and MIC errors are irrelevant, + * the frame is going to be dropped either way + */ if (ads.ds_rxstatus8 & AR_CRCErr) rs->rs_status |= ATH9K_RXERR_CRC; - if (ads.ds_rxstatus8 & AR_PHYErr) { + else if (ads.ds_rxstatus8 & AR_PHYErr) { rs->rs_status |= ATH9K_RXERR_PHY; phyerr = MS(ads.ds_rxstatus8, AR_PHYErrCode); rs->rs_phyerr = phyerr; - } - if (ads.ds_rxstatus8 & AR_DecryptCRCErr) + } else if (ads.ds_rxstatus8 & AR_DecryptCRCErr) rs->rs_status |= ATH9K_RXERR_DECRYPT; - if (ads.ds_rxstatus8 & AR_MichaelErr) + else if (ads.ds_rxstatus8 & AR_MichaelErr) rs->rs_status |= ATH9K_RXERR_MIC; + if (ads.ds_rxstatus8 & AR_KeyMiss) rs->rs_status |= ATH9K_RXERR_DECRYPT; } From 718731e7c909827e15dd3a43d169eeb4bf6c6f8d Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 20 Apr 2011 18:02:45 +0300 Subject: [PATCH 1500/2556] UBIFS: fix false space checking failure commit 8c230d9a5b5ec7970139acb7e2d165d7a3fe9f9e upstream. This patch fixes UBIFS mount failure when the debugging support is enabled, we are recovering from a power cut, we were first mounter R/O and we are re-mounting R/W. In this case we should not assume that the amount of free space before we have re-mounted R/W and after are equivalent, because when we have mounted R/O the file-system is in a non-committed state so the amount of free space is slightly smaller, due to the fact that we cannot predict the amount of free space precisely before we commit. This patch fixes the issue by skipping the debugging check in case of recovery. This issue was reported by Caizhiyong here: http://thread.gmane.org/gmane.linux.drivers.mtd/34350/focus=34387 Signed-off-by: Artem Bityutskiy Reported-by: Caizhiyong Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/super.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 0f029e1732d1a..e94d9628a49e4 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1643,15 +1643,27 @@ static int ubifs_remount_rw(struct ubifs_info *c) if (err) goto out; + dbg_gen("re-mounted read-write"); + c->remounting_rw = 0; + if (c->need_recovery) { c->need_recovery = 0; ubifs_msg("deferred recovery completed"); + } else { + /* + * Do not run the debugging space check if the were doing + * recovery, because when we saved the information we had the + * file-system in a state where the TNC and lprops has been + * modified in memory, but all the I/O operations (including a + * commit) were deferred. So the file-system was in + * "non-committed" state. Now the file-system is in committed + * state, and of course the amount of free space will change + * because, for example, the old index size was imprecise. + */ + err = dbg_check_space_info(c); } - dbg_gen("re-mounted read-write"); - c->remounting_rw = 0; c->always_chk_crc = 0; - err = dbg_check_space_info(c); mutex_unlock(&c->umount_mutex); return err; From db952a764fa4a58b374363878f9eb5aa5525b81f Mon Sep 17 00:00:00 2001 From: Carsten Otte Date: Wed, 20 Apr 2011 10:15:36 +0200 Subject: [PATCH 1501/2556] kvm-390: Let kernel exit SIE instruction on work commit 9ff4cfb3fcfd48b49fdd9be7381b3be340853aa4 upstream. From: Christian Borntraeger This patch fixes the sie exit on interrupts. The low level interrupt handler returns to the PSW address in pt_regs and not to the PSW address in the lowcore. Without this fix a cpu bound guest might never leave guest state since the host interrupt handler would blindly return to the SIE instruction, even on need_resched and friends. Signed-off-by: Carsten Otte Signed-off-by: Christian Borntraeger Signed-off-by: Martin Schwidefsky Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/sie64a.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/s390/kvm/sie64a.S b/arch/s390/kvm/sie64a.S index 7e9d30d567b0a..ab0e041ac54cf 100644 --- a/arch/s390/kvm/sie64a.S +++ b/arch/s390/kvm/sie64a.S @@ -48,10 +48,10 @@ sie_irq_handler: tm __TI_flags+7(%r2),_TIF_EXIT_SIE jz 0f larl %r2,sie_exit # work pending, leave sie - stg %r2,__LC_RETURN_PSW+8 + stg %r2,SPI_PSW+8(0,%r15) br %r14 0: larl %r2,sie_reenter # re-enter with guest id - stg %r2,__LC_RETURN_PSW+8 + stg %r2,SPI_PSW+8(0,%r15) 1: br %r14 /* From 642dbe81ce8c46e26a190ab641633858961b9ad4 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 20 Apr 2011 10:15:34 +0200 Subject: [PATCH 1502/2556] pfault: fix token handling commit e35c76cd47c244eaa7a74adaabde4d0a1cadb907 upstream. f6649a7e "[S390] cleanup lowcore access from external interrupts" changed handling of external interrupts. Instead of letting the external interrupt handlers accessing the per cpu lowcore the entry code of the kernel reads already all fields that are necessary and passes them to the handlers. The pfault interrupt handler was incorrectly converted. It tries to dereference a value which used to be a pointer to a lowcore field. After the conversion however it is not anymore the pointer to the field but its content. So instead of a dereference only a cast is needed to get the task pointer that caused the pfault. Fixes a NULL pointer dereference and a subsequent kernel crash: Unable to handle kernel pointer dereference at virtual kernel address (null) Oops: 0004 [#1] SMP Modules linked in: nfsd exportfs nfs lockd fscache nfs_acl auth_rpcgss sunrpc loop qeth_l3 qeth vmur ccwgroup ext3 jbd mbcache dm_mod dasd_eckd_mod dasd_diag_mod dasd_mod CPU: 0 Not tainted 2.6.38-2-s390x #1 Process cron (pid: 1106, task: 000000001f962f78, ksp: 000000001fa0f9d0) Krnl PSW : 0404200180000000 000000000002c03e (pfault_interrupt+0xa2/0x138) R:0 T:1 IO:0 EX:0 Key:0 M:1 W:0 P:0 AS:0 CC:2 PM:0 EA:3 Krnl GPRS: 0000000000000000 0000000000000001 0000000000000000 0000000000000001 000000001f962f78 0000000000518968 0000000090000002 000000001ff03280 0000000000000000 000000000064f000 000000001f962f78 0000000000002603 0000000006002603 0000000000000000 000000001ff7fe68 000000001ff7fe48 Krnl Code: 000000000002c036: 5820d010 l %r2,16(%r13) 000000000002c03a: 1832 lr %r3,%r2 000000000002c03c: 1a31 ar %r3,%r1 >000000000002c03e: ba23d010 cs %r2,%r3,16(%r13) 000000000002c042: a744fffc brc 4,2c03a 000000000002c046: a7290002 lghi %r2,2 000000000002c04a: e320d0000024 stg %r2,0(%r13) 000000000002c050: 07f0 bcr 15,%r0 Call Trace: ([<000000001f962f78>] 0x1f962f78) [<000000000001acda>] do_extint+0xf6/0x138 [<000000000039b6ca>] ext_no_vtime+0x30/0x34 [<000000007d706e04>] 0x7d706e04 Last Breaking-Event-Address: [<0000000000000000>] 0x0 For stable maintainers: the first kernel which contains this bug is 2.6.37. Reported-by: Stephen Powell Cc: Jonathan Nieder Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky Signed-off-by: Greg Kroah-Hartman --- arch/s390/mm/fault.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 2c57806c0858e..0f900c811cb6a 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -558,9 +558,9 @@ static void pfault_interrupt(unsigned int ext_int_code, * Get the token (= address of the task structure of the affected task). */ #ifdef CONFIG_64BIT - tsk = *(struct task_struct **) param64; + tsk = (struct task_struct *) param64; #else - tsk = *(struct task_struct **) param32; + tsk = (struct task_struct *) param32; #endif if (subcode & 0x0080) { From 2aaf1f781cb77812e5d9678d7b126ec13cebf283 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 26 Apr 2011 11:33:18 +0200 Subject: [PATCH 1503/2556] ACPI / PM: Avoid infinite recurrence while registering power resources commit 7bed50c5edf5cba8dd515a31191cbfb6065ddc85 upstream. There is at least one BIOS with a DSDT containing a power resource object with a _PR0 entry pointing back to that power resource. In consequence, while registering that power resource acpi_bus_get_power_flags() sees that it depends on itself and tries to register it again, which leads to an infinitely deep recurrence. This problem was introduced by commit bf325f9538d8c89312be305b9779e (ACPI / PM: Register power resource devices as soon as they are needed). To fix this problem use the observation that power resources cannot be power manageable and prevent acpi_bus_get_power_flags() from being called for power resource objects. References: https://bugzilla.kernel.org/show_bug.cgi?id=31872 Reported-and-tested-by: Pascal Dormeau Signed-off-by: Rafael J. Wysocki Acked-by: Len Brown Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/scan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index b99e624946074..8eee69faf235b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -944,6 +944,10 @@ static int acpi_bus_get_flags(struct acpi_device *device) if (ACPI_SUCCESS(status)) device->flags.lockable = 1; + /* Power resources cannot be power manageable. */ + if (device->device_type == ACPI_BUS_TYPE_POWER) + return 0; + /* Presence of _PS0|_PR0 indicates 'power manageable' */ status = acpi_get_handle(device->handle, "_PS0", &temp); if (ACPI_FAILURE(status)) From 7c650d5dfafac48213c0a652b7b1a18a3c391538 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Tue, 19 Apr 2011 16:29:36 -0500 Subject: [PATCH 1504/2556] slub: fix panic with DISCONTIGMEM commit 4a5fa3590f09999f6db41bc386bce40848fa9f63 upstream. Slub makes assumptions about page_to_nid() which are violated by DISCONTIGMEM and !NUMA. This violation results in a panic because page_to_nid() can be non-zero for pages in the discontiguous ranges and this leads to a null return by get_node(). The assertion by the maintainer is that DISCONTIGMEM should only be allowed when NUMA is also defined. However, at least six architectures: alpha, ia64, m32r, m68k, mips, parisc violate this. The panic is a regression against slab, so just mark slub broken in the problem configuration to prevent users reporting these panics. Acked-by: David Rientjes Acked-by: Pekka Enberg Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- init/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/init/Kconfig b/init/Kconfig index be788c0957d4a..47dd02f4d9c5a 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1209,6 +1209,7 @@ config SLAB per cpu and per node queues. config SLUB + depends on BROKEN || NUMA || !DISCONTIGMEM bool "SLUB (Unqueued Allocator)" help SLUB is a slab allocator that minimizes cache line usage From 6a682f634ba9615d3498d1e20a23e9d4fcb39f16 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Wed, 20 Apr 2011 19:27:13 -0700 Subject: [PATCH 1505/2556] set memory ranges in N_NORMAL_MEMORY when onlined commit d9b41e0b54fd7e164daf1e9c539c1070398aa02e upstream. When a DISCONTIGMEM memory range is brought online as a NUMA node, it also needs to have its bet set in N_NORMAL_MEMORY. This is necessary for generic kernel code that utilizes N_NORMAL_MEMORY as a subset of N_ONLINE for memory savings. These types of hacks can hopefully be removed once DISCONTIGMEM is either removed or abstracted away from CONFIG_NUMA. Fixes a panic in the slub code which only initializes structures for N_NORMAL_MEMORY to save memory: Backtrace: [<000000004021c938>] add_partial+0x28/0x98 [<000000004021faa0>] __slab_free+0x1d0/0x1d8 [<000000004021fd04>] kmem_cache_free+0xc4/0x128 [<000000004033bf9c>] ida_get_new_above+0x21c/0x2c0 [<00000000402a8980>] sysfs_new_dirent+0xd0/0x238 [<00000000402a974c>] create_dir+0x5c/0x168 [<00000000402a9ab0>] sysfs_create_dir+0x98/0x128 [<000000004033d6c4>] kobject_add_internal+0x114/0x258 [<000000004033d9ac>] kobject_add_varg+0x7c/0xa0 [<000000004033df20>] kobject_add+0x50/0x90 [<000000004033dfb4>] kobject_create_and_add+0x54/0xc8 [<00000000407862a0>] cgroup_init+0x138/0x1f0 [<000000004077ce50>] start_kernel+0x5a0/0x840 [<000000004011fa3c>] start_parisc+0xa4/0xb8 [<00000000404bb034>] packet_ioctl+0x16c/0x208 [<000000004049ac30>] ip_mroute_setsockopt+0x260/0xf20 Signed-off-by: David Rientjes Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- arch/parisc/mm/init.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index f4f4d700833af..7fd8aadd8a8ee 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -266,8 +266,10 @@ static void __init setup_bootmem(void) } memset(pfnnid_map, 0xff, sizeof(pfnnid_map)); - for (i = 0; i < npmem_ranges; i++) + for (i = 0; i < npmem_ranges; i++) { + node_set_state(i, N_NORMAL_MEMORY); node_set_online(i); + } #endif /* From 725bb43507cf71e2f48ec929403abe60c35254e8 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Sun, 3 Apr 2011 12:40:24 -0300 Subject: [PATCH 1506/2556] FLEXCOP-PCI: fix __xlate_proc_name-warning for flexcop-pci commit b934c20de1398d4a82d2ecfeb588a214a910f13f upstream. This patch fixes the warning about bad names for sys-fs and other kernel-things. The flexcop-pci driver was using '/'-characters in it, which is not good. This has been fixed in several attempts by several people, but obviously never made it into the kernel. Signed-off-by: Patrick Boettcher Cc: Steffen Barszus Cc: Boris Cuber Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/dvb/b2c2/flexcop-pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/b2c2/flexcop-pci.c b/drivers/media/dvb/b2c2/flexcop-pci.c index 227c0200b70a5..4f3e3ceaa7c90 100644 --- a/drivers/media/dvb/b2c2/flexcop-pci.c +++ b/drivers/media/dvb/b2c2/flexcop-pci.c @@ -38,7 +38,7 @@ MODULE_PARM_DESC(debug, DEBSTATUS); #define DRIVER_VERSION "0.1" -#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV PCI Driver" +#define DRIVER_NAME "flexcop-pci" #define DRIVER_AUTHOR "Patrick Boettcher " struct flexcop_pci { From 960b9affa8645e9cfe669295f05a8cd9b8fe77a0 Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 14 Mar 2011 17:45:48 +0530 Subject: [PATCH 1507/2556] virtio: console: Enable call to hvc_remove() on console port remove commit afa2689e19073cd2e762d0f2c1358fab1ab9f18c upstream. This call was disabled as hot-unplugging one virtconsole port led to another virtconsole port freezing. Upon testing it again, this now works, so enable it. In addition, a bug was found in qemu wherein removing a port of one type caused the guest output from another port to stop working. I doubt it was just this bug that caused it (since disabling the hvc_remove() call did allow other ports to continue working), but since it's all solved now, we're fine with hot-unplugging of virtconsole ports. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell Signed-off-by: Greg Kroah-Hartman --- drivers/char/virtio_console.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 84b164d1eb2b1..838568a7dbf56 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1280,18 +1280,7 @@ static void unplug_port(struct port *port) spin_lock_irq(&pdrvdata_lock); list_del(&port->cons.list); spin_unlock_irq(&pdrvdata_lock); -#if 0 - /* - * hvc_remove() not called as removing one hvc port - * results in other hvc ports getting frozen. - * - * Once this is resolved in hvc, this functionality - * will be enabled. Till that is done, the -EPIPE - * return from get_chars() above will help - * hvc_console.c to clean up on ports we remove here. - */ hvc_remove(port->cons.hvc); -#endif } /* Remove unused data this port might have received. */ From 3ae700492f0839d01ece23c32e9827b05c29a0a2 Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Wed, 27 Apr 2011 15:26:50 -0700 Subject: [PATCH 1508/2556] oom: use pte pages in OOM score commit f755a042d82b51b54f3bdd0890e5ea56c0fb6807 upstream. PTE pages eat up memory just like anything else, but we do not account for them in any way in the OOM scores. They are also _guaranteed_ to get freed up when a process is OOM killed, while RSS is not. Reported-by: Dave Hansen Signed-off-by: KOSAKI Motohiro Cc: Hugh Dickins Cc: KAMEZAWA Hiroyuki Cc: Oleg Nesterov Acked-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/oom_kill.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mm/oom_kill.c b/mm/oom_kill.c index ea16f720705ed..49ea0cc9ae129 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -172,10 +172,13 @@ unsigned int oom_badness(struct task_struct *p, struct mem_cgroup *mem, /* * The baseline for the badness score is the proportion of RAM that each - * task's rss and swap space use. + * task's rss, pagetable and swap space use. */ - points = (get_mm_rss(p->mm) + get_mm_counter(p->mm, MM_SWAPENTS)) * 1000 / - totalpages; + points = get_mm_rss(p->mm) + p->mm->nr_ptes; + points += get_mm_counter(p->mm, MM_SWAPENTS); + + points *= 1000; + points /= totalpages; task_unlock(p); /* From 649717e59e69038212d8e28bfa5fb66bfddb9dea Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 27 Apr 2011 15:26:56 -0700 Subject: [PATCH 1509/2556] mm: check if PTE is already allocated during page fault commit cc03638df20acbec5d0d0d9e07234aadde9e698d upstream. With transparent hugepage support, handle_mm_fault() has to be careful that a normal PMD has been established before handling a PTE fault. To achieve this, it used __pte_alloc() directly instead of pte_alloc_map as pte_alloc_map is unsafe to run against a huge PMD. pte_offset_map() is called once it is known the PMD is safe. pte_alloc_map() is smart enough to check if a PTE is already present before calling __pte_alloc but this check was lost. As a consequence, PTEs may be allocated unnecessarily and the page table lock taken. Thi useless PTE does get cleaned up but it's a performance hit which is visible in page_test from aim9. This patch simply re-adds the check normally done by pte_alloc_map to check if the PTE needs to be allocated before taking the page table lock. The effect is noticable in page_test from aim9. AIM9 2.6.38-vanilla 2.6.38-checkptenone creat-clo 446.10 ( 0.00%) 424.47 (-5.10%) page_test 38.10 ( 0.00%) 42.04 ( 9.37%) brk_test 52.45 ( 0.00%) 51.57 (-1.71%) exec_test 382.00 ( 0.00%) 456.90 (16.39%) fork_test 60.11 ( 0.00%) 67.79 (11.34%) MMTests Statistics: duration Total Elapsed Time (seconds) 611.90 612.22 (While this affects 2.6.38, it is a performance rather than a functional bug and normally outside the rules -stable. While the big performance differences are to a microbench, the difference in fork and exec performance may be significant enough that -stable wants to consider the patch) Reported-by: Raz Ben Yehuda Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Reviewed-by: Andrea Arcangeli Reviewed-by: Minchan Kim Acked-by: Johannes Weiner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/memory.c b/mm/memory.c index f17746a5acb13..ab88d09c4a817 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3332,7 +3332,7 @@ int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma, * run pte_offset_map on the pmd, if an huge pmd could * materialize from under us from a different thread. */ - if (unlikely(__pte_alloc(mm, vma, pmd, address))) + if (unlikely(pmd_none(*pmd)) && __pte_alloc(mm, vma, pmd, address)) return VM_FAULT_OOM; /* if an huge pmd materialized from under us just retry later */ if (unlikely(pmd_trans_huge(*pmd))) From 28785447dc596d0612513010e7bb23cce9c88e50 Mon Sep 17 00:00:00 2001 From: Andrea Arcangeli Date: Wed, 27 Apr 2011 15:26:45 -0700 Subject: [PATCH 1510/2556] mm: thp: fix /dev/zero MAP_PRIVATE and vm_flags cleanups commit 78f11a255749d09025f54d4e2df4fbcb031530e2 upstream. The huge_memory.c THP page fault was allowed to run if vm_ops was null (which would succeed for /dev/zero MAP_PRIVATE, as the f_op->mmap wouldn't setup a special vma->vm_ops and it would fallback to regular anonymous memory) but other THP logics weren't fully activated for vmas with vm_file not NULL (/dev/zero has a not NULL vma->vm_file). So this removes the vm_file checks so that /dev/zero also can safely use THP (the other albeit safer approach to fix this bug would have been to prevent the THP initial page fault to run if vm_file was set). After removing the vm_file checks, this also makes huge_memory.c stricter in khugepaged for the DEBUG_VM=y case. It doesn't replace the vm_file check with a is_pfn_mapping check (but it keeps checking for VM_PFNMAP under VM_BUG_ON) because for a is_cow_mapping() mapping VM_PFNMAP should only be allowed to exist before the first page fault, and in turn when vma->anon_vma is null (so preventing khugepaged registration). So I tend to think the previous comment saying if vm_file was set, VM_PFNMAP might have been set and we could still be registered in khugepaged (despite anon_vma was not NULL to be registered in khugepaged) was too paranoid. The is_linear_pfn_mapping check is also I think superfluous (as described by comment) but under DEBUG_VM it is safe to stay. Addresses https://bugzilla.kernel.org/show_bug.cgi?id=33682 Signed-off-by: Andrea Arcangeli Reported-by: Caspar Zhang Acked-by: Mel Gorman Acked-by: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- include/linux/huge_mm.h | 2 +- include/linux/mm.h | 3 ++- mm/huge_memory.c | 43 +++++++++++++++++++++++------------------ 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h index df29c8fde36be..8847c8c29791c 100644 --- a/include/linux/huge_mm.h +++ b/include/linux/huge_mm.h @@ -117,7 +117,7 @@ static inline void vma_adjust_trans_huge(struct vm_area_struct *vma, unsigned long end, long adjust_next) { - if (!vma->anon_vma || vma->vm_ops || vma->vm_file) + if (!vma->anon_vma || vma->vm_ops) return; __vma_adjust_trans_huge(vma, start, end, adjust_next); } diff --git a/include/linux/mm.h b/include/linux/mm.h index c67adb4ea5ebe..248c94685037b 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -137,7 +137,8 @@ extern unsigned int kobjsize(const void *objp); #define VM_RandomReadHint(v) ((v)->vm_flags & VM_RAND_READ) /* - * special vmas that are non-mergable, non-mlock()able + * Special vmas that are non-mergable, non-mlock()able. + * Note: mm/huge_memory.c VM_NO_THP depends on this definition. */ #define VM_SPECIAL (VM_IO | VM_DONTEXPAND | VM_RESERVED | VM_PFNMAP) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 8f76561944178..56cac93f155d1 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1400,6 +1400,9 @@ int split_huge_page(struct page *page) return ret; } +#define VM_NO_THP (VM_SPECIAL|VM_INSERTPAGE|VM_MIXEDMAP|VM_SAO| \ + VM_HUGETLB|VM_SHARED|VM_MAYSHARE) + int hugepage_madvise(struct vm_area_struct *vma, unsigned long *vm_flags, int advice) { @@ -1408,11 +1411,7 @@ int hugepage_madvise(struct vm_area_struct *vma, /* * Be somewhat over-protective like KSM for now! */ - if (*vm_flags & (VM_HUGEPAGE | - VM_SHARED | VM_MAYSHARE | - VM_PFNMAP | VM_IO | VM_DONTEXPAND | - VM_RESERVED | VM_HUGETLB | VM_INSERTPAGE | - VM_MIXEDMAP | VM_SAO)) + if (*vm_flags & (VM_HUGEPAGE | VM_NO_THP)) return -EINVAL; *vm_flags &= ~VM_NOHUGEPAGE; *vm_flags |= VM_HUGEPAGE; @@ -1428,11 +1427,7 @@ int hugepage_madvise(struct vm_area_struct *vma, /* * Be somewhat over-protective like KSM for now! */ - if (*vm_flags & (VM_NOHUGEPAGE | - VM_SHARED | VM_MAYSHARE | - VM_PFNMAP | VM_IO | VM_DONTEXPAND | - VM_RESERVED | VM_HUGETLB | VM_INSERTPAGE | - VM_MIXEDMAP | VM_SAO)) + if (*vm_flags & (VM_NOHUGEPAGE | VM_NO_THP)) return -EINVAL; *vm_flags &= ~VM_HUGEPAGE; *vm_flags |= VM_NOHUGEPAGE; @@ -1566,10 +1561,14 @@ int khugepaged_enter_vma_merge(struct vm_area_struct *vma) * page fault if needed. */ return 0; - if (vma->vm_file || vma->vm_ops) + if (vma->vm_ops) /* khugepaged not yet working on file or special mappings */ return 0; - VM_BUG_ON(is_linear_pfn_mapping(vma) || is_pfn_mapping(vma)); + /* + * If is_pfn_mapping() is true is_learn_pfn_mapping() must be + * true too, verify it here. + */ + VM_BUG_ON(is_linear_pfn_mapping(vma) || vma->vm_flags & VM_NO_THP); hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK; hend = vma->vm_end & HPAGE_PMD_MASK; if (hstart < hend) @@ -1818,12 +1817,15 @@ static void collapse_huge_page(struct mm_struct *mm, (vma->vm_flags & VM_NOHUGEPAGE)) goto out; - /* VM_PFNMAP vmas may have vm_ops null but vm_file set */ - if (!vma->anon_vma || vma->vm_ops || vma->vm_file) + if (!vma->anon_vma || vma->vm_ops) goto out; if (is_vma_temporary_stack(vma)) goto out; - VM_BUG_ON(is_linear_pfn_mapping(vma) || is_pfn_mapping(vma)); + /* + * If is_pfn_mapping() is true is_learn_pfn_mapping() must be + * true too, verify it here. + */ + VM_BUG_ON(is_linear_pfn_mapping(vma) || vma->vm_flags & VM_NO_THP); pgd = pgd_offset(mm, address); if (!pgd_present(*pgd)) @@ -2056,13 +2058,16 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages, progress++; continue; } - /* VM_PFNMAP vmas may have vm_ops null but vm_file set */ - if (!vma->anon_vma || vma->vm_ops || vma->vm_file) + if (!vma->anon_vma || vma->vm_ops) goto skip; if (is_vma_temporary_stack(vma)) goto skip; - - VM_BUG_ON(is_linear_pfn_mapping(vma) || is_pfn_mapping(vma)); + /* + * If is_pfn_mapping() is true is_learn_pfn_mapping() + * must be true too, verify it here. + */ + VM_BUG_ON(is_linear_pfn_mapping(vma) || + vma->vm_flags & VM_NO_THP); hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK; hend = vma->vm_end & HPAGE_PMD_MASK; From 8858587af25efc06d5cce42676786b3d7a9160f2 Mon Sep 17 00:00:00 2001 From: Michael Schmitz Date: Tue, 26 Apr 2011 14:51:53 +1200 Subject: [PATCH 1511/2556] m68k/mm: Set all online nodes in N_NORMAL_MEMORY commit 4aac0b4815ba592052758f4b468f253d383dc9d6 upstream. For m68k, N_NORMAL_MEMORY represents all nodes that have present memory since it does not support HIGHMEM. This patch sets the bit at the time node_present_pages has been set by free_area_init_node. At the time the node is brought online, the node state would have to be done unconditionally since information about present memory has not yet been recorded. If N_NORMAL_MEMORY is not accurate, slub may encounter errors since it uses this nodemask to setup per-cache kmem_cache_node data structures. This pach is an alternative to the one proposed by David Rientjes attempting to set node state immediately when bringing the node online. Signed-off-by: Michael Schmitz Tested-by: Thorsten Glaser Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- arch/m68k/mm/motorola.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/m68k/mm/motorola.c b/arch/m68k/mm/motorola.c index 02b7a03e42268..8b3db1c587fca 100644 --- a/arch/m68k/mm/motorola.c +++ b/arch/m68k/mm/motorola.c @@ -300,6 +300,8 @@ void __init paging_init(void) zones_size[ZONE_DMA] = m68k_memory[i].size >> PAGE_SHIFT; free_area_init_node(i, zones_size, m68k_memory[i].addr >> PAGE_SHIFT, NULL); + if (node_present_pages(i)) + node_set_state(i, N_NORMAL_MEMORY); } } From 146129d6d999532951565a0c25bc98e724b1b07e Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 27 Apr 2011 15:26:41 -0700 Subject: [PATCH 1512/2556] vfs: avoid large kmalloc()s for the fdtable commit 6d4831c283530a5f2c6bd8172c13efa236eb149d upstream. Azurit reports large increases in system time after 2.6.36 when running Apache. It was bisected down to a892e2d7dcdfa6c76e6 ("vfs: use kmalloc() to allocate fdmem if possible"). That patch caused the vfs to use kmalloc() for very large allocations and this is causing excessive work (and presumably excessive reclaim) within the page allocator. Fix it by falling back to vmalloc() earlier - when the allocation attempt would have been considered "costly" by reclaim. Reported-by: azurIt Tested-by: azurIt Acked-by: Changli Gao Cc: Americo Wang Cc: Jiri Slaby Acked-by: Eric Dumazet Cc: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/file.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/fs/file.c b/fs/file.c index 0be344755c020..4c6992d8f3ba1 100644 --- a/fs/file.c +++ b/fs/file.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -39,14 +40,17 @@ int sysctl_nr_open_max = 1024 * 1024; /* raised later */ */ static DEFINE_PER_CPU(struct fdtable_defer, fdtable_defer_list); -static inline void *alloc_fdmem(unsigned int size) +static void *alloc_fdmem(unsigned int size) { - void *data; - - data = kmalloc(size, GFP_KERNEL|__GFP_NOWARN); - if (data != NULL) - return data; - + /* + * Very large allocations can stress page reclaim, so fall back to + * vmalloc() if the allocation size will be considered "large" by the VM. + */ + if (size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER)) { + void *data = kmalloc(size, GFP_KERNEL|__GFP_NOWARN); + if (data != NULL) + return data; + } return vmalloc(size); } From 558cc1035db8ac0a510b4582f5354de1510224d6 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 27 Apr 2011 11:49:09 -0400 Subject: [PATCH 1513/2556] nfs: don't lose MS_SYNCHRONOUS on remount of noac mount commit 26c4c170731f00008f4317a2888a0a07ac99d90d upstream. On a remount, the VFS layer will clear the MS_SYNCHRONOUS bit on the assumption that the flags on the mount syscall will have it set if the remounted fs is supposed to keep it. In the case of "noac" though, MS_SYNCHRONOUS is implied. A remount of such a mount will lose the MS_SYNCHRONOUS flag since "sync" isn't part of the mount options. Reported-by: Max Matveev Signed-off-by: Jeff Layton Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- fs/nfs/super.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index b68c8607770fc..6a2ec5043a0c3 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2077,6 +2077,15 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data) if (error < 0) goto out; + /* + * noac is a special case. It implies -o sync, but that's not + * necessarily reflected in the mtab options. do_remount_sb + * will clear MS_SYNCHRONOUS if -o sync wasn't specified in the + * remount options, so we have to explicitly reset it. + */ + if (data->flags & NFS_MOUNT_NOAC) + *flags |= MS_SYNCHRONOUS; + /* compare new mount options with old ones */ error = nfs_compare_remount_data(nfss, data); out: From 20857f111ec4c3e5aea039c5c06d5254aeeb620c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 15 Apr 2011 17:34:18 -0400 Subject: [PATCH 1514/2556] NFSv4.1: Ensure state manager thread dies on last umount commit 47c2199b6eb5fbe38ddb844db7cdbd914d304f9c upstream. Currently, the state manager may continue to try recovering state forever even after the last filesystem to reference that nfs_client has umounted. Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- fs/nfs/nfs4state.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 0592288f9f067..6221640397dd2 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1600,7 +1600,7 @@ static void nfs4_state_manager(struct nfs_client *clp) int status = 0; /* Ensure exclusive access to NFSv4 state */ - for(;;) { + do { if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) { /* We're going to have to re-establish a clientid */ status = nfs4_reclaim_lease(clp); @@ -1684,7 +1684,7 @@ static void nfs4_state_manager(struct nfs_client *clp) break; if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0) break; - } + } while (atomic_read(&clp->cl_count) > 1); return; out_error: printk(KERN_WARNING "Error: state manager failed on NFSv4 server %s" From 85b79c0b5b206bc9d1f5279d007d4845ad0e03a5 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Wed, 27 Apr 2011 15:26:51 -0700 Subject: [PATCH 1515/2556] um: mdd support for 64 bit atomic operations commit 57d8e02e3cd21bccf2b84b26b42feb79e1f0f83e upstream. This adds support for 64 bit atomic operations on 32 bit UML systems. XFS needs them since 2.6.38. $ make ARCH=um SUBARCH=i386 ... LD .tmp_vmlinux1 fs/built-in.o: In function `xlog_regrant_reserve_log_space': xfs_log.c:(.text+0xd8584): undefined reference to `atomic64_read_386' xfs_log.c:(.text+0xd85ac): undefined reference to `cmpxchg8b_emu' ... Addresses https://bugzilla.kernel.org/show_bug.cgi?id=32812 Reported-by: Martin Walch Tested-by: Martin Walch Cc: Martin Walch Signed-off-by: Richard Weinberger Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- arch/um/sys-i386/Makefile | 2 +- arch/um/sys-i386/atomic64_cx8_32.S | 225 +++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 arch/um/sys-i386/atomic64_cx8_32.S diff --git a/arch/um/sys-i386/Makefile b/arch/um/sys-i386/Makefile index 804b28dd0328a..b1da91c1b200d 100644 --- a/arch/um/sys-i386/Makefile +++ b/arch/um/sys-i386/Makefile @@ -4,7 +4,7 @@ obj-y = bug.o bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \ ptrace_user.o setjmp.o signal.o stub.o stub_segv.o syscalls.o sysrq.o \ - sys_call_table.o tls.o + sys_call_table.o tls.o atomic64_cx8_32.o obj-$(CONFIG_BINFMT_ELF) += elfcore.o diff --git a/arch/um/sys-i386/atomic64_cx8_32.S b/arch/um/sys-i386/atomic64_cx8_32.S new file mode 100644 index 0000000000000..1e901d3d4a956 --- /dev/null +++ b/arch/um/sys-i386/atomic64_cx8_32.S @@ -0,0 +1,225 @@ +/* + * atomic64_t for 586+ + * + * Copied from arch/x86/lib/atomic64_cx8_32.S + * + * Copyright © 2010 Luca Barbieri + * + * 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. + * + */ + +#include +#include +#include + +.macro SAVE reg + pushl_cfi %\reg + CFI_REL_OFFSET \reg, 0 +.endm + +.macro RESTORE reg + popl_cfi %\reg + CFI_RESTORE \reg +.endm + +.macro read64 reg + movl %ebx, %eax + movl %ecx, %edx +/* we need LOCK_PREFIX since otherwise cmpxchg8b always does the write */ + LOCK_PREFIX + cmpxchg8b (\reg) +.endm + +ENTRY(atomic64_read_cx8) + CFI_STARTPROC + + read64 %ecx + ret + CFI_ENDPROC +ENDPROC(atomic64_read_cx8) + +ENTRY(atomic64_set_cx8) + CFI_STARTPROC + +1: +/* we don't need LOCK_PREFIX since aligned 64-bit writes + * are atomic on 586 and newer */ + cmpxchg8b (%esi) + jne 1b + + ret + CFI_ENDPROC +ENDPROC(atomic64_set_cx8) + +ENTRY(atomic64_xchg_cx8) + CFI_STARTPROC + + movl %ebx, %eax + movl %ecx, %edx +1: + LOCK_PREFIX + cmpxchg8b (%esi) + jne 1b + + ret + CFI_ENDPROC +ENDPROC(atomic64_xchg_cx8) + +.macro addsub_return func ins insc +ENTRY(atomic64_\func\()_return_cx8) + CFI_STARTPROC + SAVE ebp + SAVE ebx + SAVE esi + SAVE edi + + movl %eax, %esi + movl %edx, %edi + movl %ecx, %ebp + + read64 %ebp +1: + movl %eax, %ebx + movl %edx, %ecx + \ins\()l %esi, %ebx + \insc\()l %edi, %ecx + LOCK_PREFIX + cmpxchg8b (%ebp) + jne 1b + +10: + movl %ebx, %eax + movl %ecx, %edx + RESTORE edi + RESTORE esi + RESTORE ebx + RESTORE ebp + ret + CFI_ENDPROC +ENDPROC(atomic64_\func\()_return_cx8) +.endm + +addsub_return add add adc +addsub_return sub sub sbb + +.macro incdec_return func ins insc +ENTRY(atomic64_\func\()_return_cx8) + CFI_STARTPROC + SAVE ebx + + read64 %esi +1: + movl %eax, %ebx + movl %edx, %ecx + \ins\()l $1, %ebx + \insc\()l $0, %ecx + LOCK_PREFIX + cmpxchg8b (%esi) + jne 1b + +10: + movl %ebx, %eax + movl %ecx, %edx + RESTORE ebx + ret + CFI_ENDPROC +ENDPROC(atomic64_\func\()_return_cx8) +.endm + +incdec_return inc add adc +incdec_return dec sub sbb + +ENTRY(atomic64_dec_if_positive_cx8) + CFI_STARTPROC + SAVE ebx + + read64 %esi +1: + movl %eax, %ebx + movl %edx, %ecx + subl $1, %ebx + sbb $0, %ecx + js 2f + LOCK_PREFIX + cmpxchg8b (%esi) + jne 1b + +2: + movl %ebx, %eax + movl %ecx, %edx + RESTORE ebx + ret + CFI_ENDPROC +ENDPROC(atomic64_dec_if_positive_cx8) + +ENTRY(atomic64_add_unless_cx8) + CFI_STARTPROC + SAVE ebp + SAVE ebx +/* these just push these two parameters on the stack */ + SAVE edi + SAVE esi + + movl %ecx, %ebp + movl %eax, %esi + movl %edx, %edi + + read64 %ebp +1: + cmpl %eax, 0(%esp) + je 4f +2: + movl %eax, %ebx + movl %edx, %ecx + addl %esi, %ebx + adcl %edi, %ecx + LOCK_PREFIX + cmpxchg8b (%ebp) + jne 1b + + movl $1, %eax +3: + addl $8, %esp + CFI_ADJUST_CFA_OFFSET -8 + RESTORE ebx + RESTORE ebp + ret +4: + cmpl %edx, 4(%esp) + jne 2b + xorl %eax, %eax + jmp 3b + CFI_ENDPROC +ENDPROC(atomic64_add_unless_cx8) + +ENTRY(atomic64_inc_not_zero_cx8) + CFI_STARTPROC + SAVE ebx + + read64 %esi +1: + testl %eax, %eax + je 4f +2: + movl %eax, %ebx + movl %edx, %ecx + addl $1, %ebx + adcl $0, %ecx + LOCK_PREFIX + cmpxchg8b (%esi) + jne 1b + + movl $1, %eax +3: + RESTORE ebx + ret +4: + testl %edx, %edx + jne 2b + jmp 3b + CFI_ENDPROC +ENDPROC(atomic64_inc_not_zero_cx8) From f0578c398d86a9d69ef59765e55b9b56cbafb93e Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 22 Apr 2011 07:51:33 +1000 Subject: [PATCH 1516/2556] drm: select FRAMEBUFFER_CONSOLE_PRIMARY if we have FRAMEBUFFER_CONSOLE commit bf5192edcbc1f0a7f9c054649dbf1a0b3210d9b7 upstream. Multi-gpu/switcheroo relies on this option to get the console on the correct GPU at bootup, some distros enable it but it seems some get it wrong. Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 0902d44600394..4b4b5455b00ff 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -24,6 +24,7 @@ config DRM_KMS_HELPER depends on DRM select FB select FRAMEBUFFER_CONSOLE if !EXPERT + select FRAMEBUFFER_CONSOLE_DETECT_PRIMARY if FRAMEBUFFER_CONSOLE help FB and CRTC helpers for KMS drivers. From 0b7c6323a28f3fde67a26bc6b2a889d3f23b12c3 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Thu, 14 Apr 2011 20:55:16 +0400 Subject: [PATCH 1517/2556] agp: fix arbitrary kernel memory writes commit 194b3da873fd334ef183806db751473512af29ce upstream. pg_start is copied from userspace on AGPIOC_BIND and AGPIOC_UNBIND ioctl cmds of agp_ioctl() and passed to agpioc_bind_wrap(). As said in the comment, (pg_start + mem->page_count) may wrap in case of AGPIOC_BIND, and it is not checked at all in case of AGPIOC_UNBIND. As a result, user with sufficient privileges (usually "video" group) may generate either local DoS or privilege escalation. Signed-off-by: Vasiliy Kulikov Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/char/agp/generic.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 012cba0d6d965..745e7baff3c9c 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -1089,8 +1089,8 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) return -EINVAL; } - /* AK: could wrap */ - if ((pg_start + mem->page_count) > num_entries) + if (((pg_start + mem->page_count) > num_entries) || + ((pg_start + mem->page_count) < pg_start)) return -EINVAL; j = pg_start; @@ -1124,7 +1124,7 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) { size_t i; struct agp_bridge_data *bridge; - int mask_type; + int mask_type, num_entries; bridge = mem->bridge; if (!bridge) @@ -1136,6 +1136,11 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) if (type != mem->type) return -EINVAL; + num_entries = agp_num_entries(); + if (((pg_start + mem->page_count) > num_entries) || + ((pg_start + mem->page_count) < pg_start)) + return -EINVAL; + mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); if (mask_type != 0) { /* The generic routines know nothing of memory types */ From 767a7e6906bd2b31c14bd57df8834da0cc81606e Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Thu, 14 Apr 2011 20:55:19 +0400 Subject: [PATCH 1518/2556] agp: fix OOM and buffer overflow commit b522f02184b413955f3bc952e3776ce41edc6355 upstream. page_count is copied from userspace. agp_allocate_memory() tries to check whether this number is too big, but doesn't take into account the wrap case. Also agp_create_user_memory() doesn't check whether alloc_size is calculated from num_agp_pages variable without overflow. This may lead to allocation of too small buffer with following buffer overflow. Another problem in agp code is not addressed in the patch - kernel memory exhaustion (AGPIOC_RESERVE and AGPIOC_ALLOCATE ioctls). It is not checked whether requested pid is a pid of the caller (no check in agpioc_reserve_wrap()). Each allocation is limited to 16KB, though, there is no per-process limit. This might lead to OOM situation, which is not even solved in case of the caller death by OOM killer - the memory is allocated for another (faked) process. Signed-off-by: Vasiliy Kulikov Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/char/agp/generic.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 745e7baff3c9c..b072648dc3f64 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -115,6 +115,9 @@ static struct agp_memory *agp_create_user_memory(unsigned long num_agp_pages) struct agp_memory *new; unsigned long alloc_size = num_agp_pages*sizeof(struct page *); + if (INT_MAX/sizeof(struct page *) < num_agp_pages) + return NULL; + new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL); if (new == NULL) return NULL; @@ -234,11 +237,14 @@ struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge, int scratch_pages; struct agp_memory *new; size_t i; + int cur_memory; if (!bridge) return NULL; - if ((atomic_read(&bridge->current_memory_agp) + page_count) > bridge->max_memory_agp) + cur_memory = atomic_read(&bridge->current_memory_agp); + if ((cur_memory + page_count > bridge->max_memory_agp) || + (cur_memory + page_count < page_count)) return NULL; if (type >= AGP_USER_TYPES) { From 21db55a9dc4e52d8d2cac30f47efcec3afccb0c5 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 28 Jan 2011 16:47:44 +0100 Subject: [PATCH 1519/2556] iwlwifi: do not set tx power when channel is changing commit f844a709a7d8f8be61a571afc31dfaca9e779621 upstream. Mac80211 can request for tx power and channel change in one ->config call. If that happens, *_send_tx_power functions will try to setup tx power for old channel, what can be not correct because we already change the band. I.e error "Failed to get channel info for channel 140 [0]", can be printed frequently when operating in software scanning mode. Signed-off-by: Stanislaw Gruszka Acked-by: Wey-Yi Guy Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl-3945.c | 2 +- drivers/net/wireless/iwlwifi/iwl-4965.c | 2 +- drivers/net/wireless/iwlwifi/iwl-agn-rxon.c | 5 ++--- drivers/net/wireless/iwlwifi/iwl-core.c | 13 ++++++++++--- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index 39b6f16c87fae..4e7b58bb1a1b5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -1823,7 +1823,7 @@ int iwl3945_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) /* If we issue a new RXON command which required a tune then we must * send a new TXPOWER command or we won't be able to Tx any frames */ - rc = priv->cfg->ops->lib->send_tx_power(priv); + rc = iwl_set_tx_power(priv, priv->tx_power_next, true); if (rc) { IWL_ERR(priv, "Error setting Tx power (%d).\n", rc); return rc; diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 91a9f52534699..992caa0231d06 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -1571,7 +1571,7 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *c /* If we issue a new RXON command which required a tune then we must * send a new TXPOWER command or we won't be able to Tx any frames */ - ret = iwl_set_tx_power(priv, priv->tx_power_user_lmt, true); + ret = iwl_set_tx_power(priv, priv->tx_power_next, true); if (ret) { IWL_ERR(priv, "Error sending TX power (%d)\n", ret); return ret; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c index 6d140bd532918..ee802fe0e4c45 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c @@ -288,10 +288,9 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) * If we issue a new RXON command which required a tune then we must * send a new TXPOWER command or we won't be able to Tx any frames. * - * FIXME: which RXON requires a tune? Can we optimise this out in - * some cases? + * It's expected we set power here if channel is changing. */ - ret = iwl_set_tx_power(priv, priv->tx_power_user_lmt, true); + ret = iwl_set_tx_power(priv, priv->tx_power_next, true); if (ret) { IWL_ERR(priv, "Error sending TX power (%d)\n", ret); return ret; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index efbde1f1a8bfc..91cac6fb41fd6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -1161,6 +1161,8 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) { int ret; s8 prev_tx_power; + bool defer; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; lockdep_assert_held(&priv->mutex); @@ -1188,10 +1190,15 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) if (!iwl_is_ready_rf(priv)) return -EIO; - /* scan complete use tx_power_next, need to be updated */ + /* scan complete and commit_rxon use tx_power_next value, + * it always need to be updated for newest request */ priv->tx_power_next = tx_power; - if (test_bit(STATUS_SCANNING, &priv->status) && !force) { - IWL_DEBUG_INFO(priv, "Deferring tx power set while scanning\n"); + + /* do not set tx power when scanning or channel changing */ + defer = test_bit(STATUS_SCANNING, &priv->status) || + memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging)); + if (defer && !force) { + IWL_DEBUG_INFO(priv, "Deferring tx power set\n"); return 0; } From 87cb0add07ea816857eda33c70e274b2ec17bb2e Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Mon, 14 Mar 2011 14:15:06 +0100 Subject: [PATCH 1520/2556] iwl3945: do not deprecate software scan commit 3bda50e3eaf58a4b9c4ce34204e5faa15c8b1b97 upstream. Software scanning can be used for workaround some performance problems, so do not deprecate it. Signed-off-by: Stanislaw Gruszka Acked-by: Wey-Yi Guy Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl3945-base.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 371abbf60eac4..cf0699a05ba73 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -3995,8 +3995,7 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e * "the hard way", rather than using device's scan. */ if (iwl3945_mod_params.disable_hw_scan) { - dev_printk(KERN_DEBUG, &(pdev->dev), - "sw scan support is deprecated\n"); + IWL_DEBUG_INFO(priv, "Disabling hw_scan\n"); iwl3945_hw_ops.hw_scan = NULL; } @@ -4318,8 +4317,7 @@ MODULE_PARM_DESC(debug, "debug output mask"); #endif module_param_named(disable_hw_scan, iwl3945_mod_params.disable_hw_scan, int, S_IRUGO); -MODULE_PARM_DESC(disable_hw_scan, - "disable hardware scanning (default 0) (deprecated)"); +MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)"); module_param_named(fw_restart3945, iwl3945_mod_params.restart_fw, int, S_IRUGO); MODULE_PARM_DESC(fw_restart3945, "restart firmware in case of error"); From f8317705c4db3eaa68c9a9a7dd7dfc321f8057f3 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Tue, 29 Mar 2011 11:24:21 +0200 Subject: [PATCH 1521/2556] iwl3945: disable hw scan by default commit 0263aa45293838b514b8af674a03faf040991a90 upstream. After new NetworkManager 0.8.996 changes, hardware scanning is causing microcode errors as reported here: https://bugzilla.redhat.com/show_bug.cgi?id=683571 and sometimes kernel crashes: https://bugzilla.redhat.com/show_bug.cgi?id=688252 Also with hw scan there are very bad performance on some systems as reported here: https://bugzilla.redhat.com/show_bug.cgi?id=671366 Since Intel no longer supports 3945, there is no chance to get proper firmware fixes, we need workaround problems by disable hardware scanning by default. Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl3945-base.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index cf0699a05ba73..a8340f72085a6 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -94,6 +94,7 @@ MODULE_LICENSE("GPL"); struct iwl_mod_params iwl3945_mod_params = { .sw_crypto = 1, .restart_fw = 1, + .disable_hw_scan = 1, /* the rest are 0 by default */ }; @@ -4317,7 +4318,7 @@ MODULE_PARM_DESC(debug, "debug output mask"); #endif module_param_named(disable_hw_scan, iwl3945_mod_params.disable_hw_scan, int, S_IRUGO); -MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)"); +MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 1)"); module_param_named(fw_restart3945, iwl3945_mod_params.restart_fw, int, S_IRUGO); MODULE_PARM_DESC(fw_restart3945, "restart firmware in case of error"); From 9ec3e481f696880fd11e24ff54da6252d3d1a986 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 13 Apr 2011 10:56:51 +0200 Subject: [PATCH 1522/2556] iwlegacy: fix tx_power initialization commit 332704a51498a7e29aa92c19dc03f11f80b71bfe upstream. priv->tx_power_next is not initialized to max supported power, but instead default value is used, what cause errors like [ 58.597834] iwl3945 0000:03:00.0: Requested user TXPOWER 15 above upper limit 14. [ 58.597839] iwl3945 0000:03:00.0: Error setting Tx power (-22). if maximum tx power read from the eeprom is smaller than default. In consequence card is unable to initialize properly. Fix the problem and cleanup tx power initialization. Reported-and-tested-by: Robin Dong Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl-3945-hw.h | 2 -- drivers/net/wireless/iwlwifi/iwl-agn.c | 6 ------ drivers/net/wireless/iwlwifi/iwl-core.c | 9 +++++++-- drivers/net/wireless/iwlwifi/iwl-eeprom.c | 7 ------- drivers/net/wireless/iwlwifi/iwl3945-base.c | 4 ---- 5 files changed, 7 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h index 65b5834da28c7..c2dd4cdeb0d6f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h @@ -74,8 +74,6 @@ /* RSSI to dBm */ #define IWL39_RSSI_OFFSET 95 -#define IWL_DEFAULT_TX_POWER 0x0F - /* * EEPROM related constants, enums, and structures. */ diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index c1cfd9952e520..35239f04927c0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -3841,12 +3841,6 @@ static int iwl_init_drv(struct iwl_priv *priv) priv->dynamic_frag_thresh = BT_FRAG_THRESHOLD_DEF; } - /* Set the tx_power_user_lmt to the lowest power level - * this value will get overwritten by channel max power avg - * from eeprom */ - priv->tx_power_user_lmt = IWLAGN_TX_POWER_TARGET_POWER_MIN; - priv->tx_power_next = IWLAGN_TX_POWER_TARGET_POWER_MIN; - ret = iwl_init_channel_map(priv); if (ret) { IWL_ERR(priv, "initializing regulatory failed: %d\n", ret); diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 91cac6fb41fd6..294e9fcb7eef8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -168,6 +168,7 @@ int iwlcore_init_geos(struct iwl_priv *priv) struct ieee80211_channel *geo_ch; struct ieee80211_rate *rates; int i = 0; + s8 max_tx_power = 0; if (priv->bands[IEEE80211_BAND_2GHZ].n_bitrates || priv->bands[IEEE80211_BAND_5GHZ].n_bitrates) { @@ -244,8 +245,8 @@ int iwlcore_init_geos(struct iwl_priv *priv) geo_ch->flags |= ch->ht40_extension_channel; - if (ch->max_power_avg > priv->tx_power_device_lmt) - priv->tx_power_device_lmt = ch->max_power_avg; + if (ch->max_power_avg > max_tx_power) + max_tx_power = ch->max_power_avg; } else { geo_ch->flags |= IEEE80211_CHAN_DISABLED; } @@ -258,6 +259,10 @@ int iwlcore_init_geos(struct iwl_priv *priv) geo_ch->flags); } + priv->tx_power_device_lmt = max_tx_power; + priv->tx_power_user_lmt = max_tx_power; + priv->tx_power_next = max_tx_power; + if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) && priv->cfg->sku & IWL_SKU_A) { IWL_INFO(priv, "Incorrectly detected BG card as ABG. " diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c index 358cfd7e5af19..8b3c12753f9e8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c @@ -724,13 +724,6 @@ int iwl_init_channel_map(struct iwl_priv *priv) flags & EEPROM_CHANNEL_RADAR)) ? "" : "not "); - /* Set the tx_power_user_lmt to the highest power - * supported by any channel */ - if (eeprom_ch_info[ch].max_power_avg > - priv->tx_power_user_lmt) - priv->tx_power_user_lmt = - eeprom_ch_info[ch].max_power_avg; - ch_info++; } } diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index a8340f72085a6..64917edc9f5f3 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -3859,10 +3859,6 @@ static int iwl3945_init_drv(struct iwl_priv *priv) priv->force_reset[IWL_FW_RESET].reset_duration = IWL_DELAY_NEXT_FORCE_FW_RELOAD; - - priv->tx_power_user_lmt = IWL_DEFAULT_TX_POWER; - priv->tx_power_next = IWL_DEFAULT_TX_POWER; - if (eeprom->version < EEPROM_3945_EEPROM_VERSION) { IWL_WARN(priv, "Unsupported EEPROM version: 0x%04X\n", eeprom->version); From 80ac2fd6758b75a1f1db112821635e3411185073 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Mon, 18 Apr 2011 10:17:17 -0700 Subject: [PATCH 1523/2556] Input: xen-kbdfront - fix mouse getting stuck after save/restore commit c36b58e8a9112017c2bcc322cc98e71241814303 upstream. Mouse gets "stuck" after restore of PV guest but buttons are in working condition. If driver has been configured for ABS coordinates at start it will get XENKBD_TYPE_POS events and then suddenly after restore it'll start getting XENKBD_TYPE_MOTION events, that will be dropped later and they won't get into user-space. Regression was introduced by hunk 5 and 6 of 5ea5254aa0ad269cfbd2875c973ef25ab5b5e9db ("Input: xen-kbdfront - advertise either absolute or relative coordinates"). Driver on restore should ask xen for request-abs-pointer again if it is available. So restore parts that did it before 5ea5254. Acked-by: Olaf Hering Signed-off-by: Igor Mammedov [v1: Expanded the commit description] Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: Dmitry Torokhov --- drivers/input/xen-kbdfront.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c index 53e62732ee96f..c35ab940f1f00 100644 --- a/drivers/input/xen-kbdfront.c +++ b/drivers/input/xen-kbdfront.c @@ -286,7 +286,7 @@ static void xenkbd_backend_changed(struct xenbus_device *dev, enum xenbus_state backend_state) { struct xenkbd_info *info = dev_get_drvdata(&dev->dev); - int val; + int ret, val; switch (backend_state) { case XenbusStateInitialising: @@ -299,6 +299,16 @@ static void xenkbd_backend_changed(struct xenbus_device *dev, case XenbusStateInitWait: InitWait: + ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend, + "feature-abs-pointer", "%d", &val); + if (ret < 0) + val = 0; + if (val) { + ret = xenbus_printf(XBT_NIL, info->xbdev->nodename, + "request-abs-pointer", "1"); + if (ret) + pr_warning("can't request abs-pointer\n"); + } xenbus_switch_state(dev, XenbusStateConnected); break; From 60584ef99395a89d136399bbc127289a4aa29dc7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 2 May 2011 09:30:53 -0700 Subject: [PATCH 1524/2556] Linux 2.6.38.5 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e71224305abf8..20ed7d1955cb2 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 38 -EXTRAVERSION = .4 +EXTRAVERSION = .5 NAME = Flesh-Eating Bats with Fangs # *DOCUMENTATION* From 170267fd5d8de0883eb2af7c2a6ab9d123b229a8 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 8 Apr 2011 20:13:18 +0200 Subject: [PATCH 1525/2556] ath9k_hw: fix stopping rx DMA during resets commit 5882da02e9d9089b7e8c739f3e774aaeeff8b7ba upstream. During PHY errors, the MAC can sometimes fail to enter an idle state on older hardware (before AR9380) after an rx stop has been requested. This typically shows up in the kernel log with messages like these: ath: Could not stop RX, we could be confusing the DMA engine when we start RX up ------------[ cut here ]------------ WARNING: at drivers/net/wireless/ath/ath9k/recv.c:504 ath_stoprecv+0xcc/0xf0 [ath9k]() Call Trace: [<8023f0e8>] dump_stack+0x8/0x34 [<80075050>] warn_slowpath_common+0x78/0xa4 [<80075094>] warn_slowpath_null+0x18/0x24 [<80d66d60>] ath_stoprecv+0xcc/0xf0 [ath9k] [<80d642cc>] ath_set_channel+0xbc/0x270 [ath9k] [<80d65254>] ath_radio_disable+0x4a4/0x7fc [ath9k] When this happens, the state that the MAC enters is easy to identify and does not result in bogus DMA traffic, however to ensure a working state after a channel change, the hardware should still be reset. This patch adds detection for this specific MAC state, after which the above warnings completely disappear in my tests. Signed-off-by: Felix Fietkau Cc: Kyungwan Nam Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/hw.c | 9 --------- drivers/net/wireless/ath/ath9k/mac.c | 25 ++++++++++++++++++++++--- drivers/net/wireless/ath/ath9k/mac.h | 2 +- drivers/net/wireless/ath/ath9k/recv.c | 6 +++--- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 7c0a7c48eea3b..a3b77ae3827c2 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1218,15 +1218,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ah->txchainmask = common->tx_chainmask; ah->rxchainmask = common->rx_chainmask; - if ((common->bus_ops->ath_bus_type != ATH_USB) && !ah->chip_fullsleep) { - ath9k_hw_abortpcurecv(ah); - if (!ath9k_hw_stopdmarecv(ah)) { - ath_dbg(common, ATH_DBG_XMIT, - "Failed to stop receive dma\n"); - bChannelChange = false; - } - } - if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) return -EIO; diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 2915b11edefb9..76d1f8c7db376 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -770,28 +770,47 @@ void ath9k_hw_abortpcurecv(struct ath_hw *ah) } EXPORT_SYMBOL(ath9k_hw_abortpcurecv); -bool ath9k_hw_stopdmarecv(struct ath_hw *ah) +bool ath9k_hw_stopdmarecv(struct ath_hw *ah, bool *reset) { #define AH_RX_STOP_DMA_TIMEOUT 10000 /* usec */ #define AH_RX_TIME_QUANTUM 100 /* usec */ struct ath_common *common = ath9k_hw_common(ah); + u32 mac_status, last_mac_status = 0; int i; + /* Enable access to the DMA observation bus */ + REG_WRITE(ah, AR_MACMISC, + ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) | + (AR_MACMISC_MISC_OBS_BUS_1 << + AR_MACMISC_MISC_OBS_BUS_MSB_S))); + REG_WRITE(ah, AR_CR, AR_CR_RXD); /* Wait for rx enable bit to go low */ for (i = AH_RX_STOP_DMA_TIMEOUT / AH_TIME_QUANTUM; i != 0; i--) { if ((REG_READ(ah, AR_CR) & AR_CR_RXE) == 0) break; + + if (!AR_SREV_9300_20_OR_LATER(ah)) { + mac_status = REG_READ(ah, AR_DMADBG_7) & 0x7f0; + if (mac_status == 0x1c0 && mac_status == last_mac_status) { + *reset = true; + break; + } + + last_mac_status = mac_status; + } + udelay(AH_TIME_QUANTUM); } if (i == 0) { ath_err(common, - "DMA failed to stop in %d ms AR_CR=0x%08x AR_DIAG_SW=0x%08x\n", + "DMA failed to stop in %d ms AR_CR=0x%08x AR_DIAG_SW=0x%08x DMADBG_7=0x%08x\n", AH_RX_STOP_DMA_TIMEOUT / 1000, REG_READ(ah, AR_CR), - REG_READ(ah, AR_DIAG_SW)); + REG_READ(ah, AR_DIAG_SW), + REG_READ(ah, AR_DMADBG_7)); return false; } else { return true; diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 7512f97e8f49a..d9cc2996107e9 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -692,7 +692,7 @@ bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set); void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp); void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning); void ath9k_hw_abortpcurecv(struct ath_hw *ah); -bool ath9k_hw_stopdmarecv(struct ath_hw *ah); +bool ath9k_hw_stopdmarecv(struct ath_hw *ah, bool *reset); int ath9k_hw_beaconq_setup(struct ath_hw *ah); /* Interrupt Handling */ diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 3867a2e8de39c..89546bc302906 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -513,12 +513,12 @@ int ath_startrecv(struct ath_softc *sc) bool ath_stoprecv(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; - bool stopped; + bool stopped, reset = false; spin_lock_bh(&sc->rx.rxbuflock); ath9k_hw_abortpcurecv(ah); ath9k_hw_setrxfilter(ah, 0); - stopped = ath9k_hw_stopdmarecv(ah); + stopped = ath9k_hw_stopdmarecv(ah, &reset); if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ath_edma_stop_recv(sc); @@ -533,7 +533,7 @@ bool ath_stoprecv(struct ath_softc *sc) "confusing the DMA engine when we start RX up\n"); ATH_DBG_WARN_ON_ONCE(!stopped); } - return stopped; + return stopped || reset; } void ath_flushrecv(struct ath_softc *sc) From f4a9261a7541b275af82ac696618f3aa65f23372 Mon Sep 17 00:00:00 2001 From: amit salecha Date: Mon, 11 Apr 2011 02:10:22 +0000 Subject: [PATCH 1526/2556] netxen: limit skb frags for non tso packet commit c968bdf6912cad6d0fc63d7037cc1c870604a808 upstream. Machines are getting deadlock in four node cluster environment. All nodes are accessing (find /gfs2 -depth -print|cpio -ocv > /dev/null) 200 GB storage on a GFS2 filesystem. This result in memory fragmentation and driver receives 18 frags for 1448 byte packets. For non tso packet, fw drops the tx request, if it has >14 frags. Fixing it by pulling extra frags. Signed-off-by: Amit Kumar Salecha Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/netxen/netxen_nic.h | 4 ++-- drivers/net/netxen/netxen_nic_main.c | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h index a11380544e6c5..10f86e0c0d744 100644 --- a/drivers/net/netxen/netxen_nic.h +++ b/drivers/net/netxen/netxen_nic.h @@ -174,7 +174,7 @@ #define MAX_NUM_CARDS 4 -#define MAX_BUFFERS_PER_CMD 32 +#define NETXEN_MAX_FRAGS_PER_TX 14 #define MAX_TSO_HEADER_DESC 2 #define MGMT_CMD_DESC_RESV 4 #define TX_STOP_THRESH ((MAX_SKB_FRAGS >> 2) + MAX_TSO_HEADER_DESC \ @@ -558,7 +558,7 @@ struct netxen_recv_crb { */ struct netxen_cmd_buffer { struct sk_buff *skb; - struct netxen_skb_frag frag_array[MAX_BUFFERS_PER_CMD + 1]; + struct netxen_skb_frag frag_array[MAX_SKB_FRAGS + 1]; u32 frag_count; }; diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index 33fac32e0d9fd..28139df4734ca 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -1841,6 +1841,8 @@ netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) struct cmd_desc_type0 *hwdesc, *first_desc; struct pci_dev *pdev; int i, k; + int delta = 0; + struct skb_frag_struct *frag; u32 producer; int frag_count, no_of_desc; @@ -1848,6 +1850,21 @@ netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) frag_count = skb_shinfo(skb)->nr_frags + 1; + /* 14 frags supported for normal packet and + * 32 frags supported for TSO packet + */ + if (!skb_is_gso(skb) && frag_count > NETXEN_MAX_FRAGS_PER_TX) { + + for (i = 0; i < (frag_count - NETXEN_MAX_FRAGS_PER_TX); i++) { + frag = &skb_shinfo(skb)->frags[i]; + delta += frag->size; + } + + if (!__pskb_pull_tail(skb, delta)) + goto drop_packet; + + frag_count = 1 + skb_shinfo(skb)->nr_frags; + } /* 4 fragments per cmd des */ no_of_desc = (frag_count + 3) >> 2; From 11d56f0acf14ed0c46b739bb0a801d3ba0068d86 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Wed, 6 Apr 2011 20:40:31 +0200 Subject: [PATCH 1527/2556] ath: add missing regdomain pair 0x5c mapping commit bd39a274fb7b43374c797bafdb7f506598f36f77 upstream. Joe Culler reported a problem with his AR9170 device: > ath: EEPROM regdomain: 0x5c > ath: EEPROM indicates we should expect a direct regpair map > ath: invalid regulatory domain/country code 0x5c > ath: Invalid EEPROM contents It turned out that the regdomain 'APL7_FCCA' was not mapped yet. According to Luis R. Rodriguez [Atheros' engineer] APL7 maps to FCC_CTL and FCCA maps to FCC_CTL as well, so the attached patch should be correct. Reported-by: Joe Culler Acked-by: Luis R. Rodriguez Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/regd_common.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/regd_common.h b/drivers/net/wireless/ath/regd_common.h index 248c670fdfbef..5c2cfe6941524 100644 --- a/drivers/net/wireless/ath/regd_common.h +++ b/drivers/net/wireless/ath/regd_common.h @@ -195,6 +195,7 @@ static struct reg_dmn_pair_mapping regDomainPairs[] = { {APL9_WORLD, CTL_ETSI, CTL_ETSI}, {APL3_FCCA, CTL_FCC, CTL_FCC}, + {APL7_FCCA, CTL_FCC, CTL_FCC}, {APL1_ETSIC, CTL_FCC, CTL_ETSI}, {APL2_ETSIC, CTL_FCC, CTL_ETSI}, {APL2_APLD, CTL_FCC, NO_CTL}, From ad07e8008a40ef01e0d46cad747a12cd75af0f08 Mon Sep 17 00:00:00 2001 From: Liu Yuan Date: Tue, 19 Apr 2011 13:47:58 +0200 Subject: [PATCH 1528/2556] block, blk-sysfs: Fix an err return path in blk_register_queue() commit ed5302d3c25006a9edc7a7fbea97a30483f89ef7 upstream. We do not call blk_trace_remove_sysfs() in err return path if kobject_add() fails. This path fixes it. Signed-off-by: Liu Yuan Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/blk-sysfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 41fb69150b4d3..3655e193e60ea 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -511,8 +511,10 @@ int blk_register_queue(struct gendisk *disk) return ret; ret = kobject_add(&q->kobj, kobject_get(&dev->kobj), "%s", "queue"); - if (ret < 0) + if (ret < 0) { + blk_trace_remove_sysfs(dev); return ret; + } kobject_uevent(&q->kobj, KOBJ_ADD); From 89a072352a9c9136d6ece85e1a180cc537d9580c Mon Sep 17 00:00:00 2001 From: Jason Conti Date: Thu, 7 Apr 2011 21:09:57 +0200 Subject: [PATCH 1529/2556] p54: Initialize extra_len in p54_tx_80211 commit a6756da9eace8b4af73e9dea43f1fc2889224c94 upstream. This patch fixes a very serious off-by-one bug in the driver, which could leave the device in an unresponsive state. The problem was that the extra_len variable [used to reserve extra scratch buffer space for the firmware] was left uninitialized. Because p54_assign_address later needs the value to reserve additional space, the resulting frame could be to big for the small device's memory window and everything would immediately come to a grinding halt. Reference: https://bugs.launchpad.net/bugs/722185 Acked-by: Christian Lamparter Signed-off-by: Jason Conti Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/p54/txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index f618b9623e5a6..2cfdd3890bd17 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -705,7 +705,7 @@ int p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb) struct p54_tx_info *p54info; struct p54_hdr *hdr; struct p54_tx_data *txhdr; - unsigned int padding, len, extra_len; + unsigned int padding, len, extra_len = 0; int i, j, ridx; u16 hdr_flags = 0, aid = 0; u8 rate, queue = 0, crypt_offset = 0; From 75f014b78c4d45d565c0ce106d7d62cd58a17bb7 Mon Sep 17 00:00:00 2001 From: Amit Kumar Salecha Date: Tue, 12 Apr 2011 17:05:55 +0000 Subject: [PATCH 1530/2556] qlcnic: limit skb frags for non tso packet commit 91a403caf0f26c71ce4407fd235b2d6fb225fba9 upstream. Machines are getting deadlock in four node cluster environment. All nodes are accessing (find /gfs2 -depth -print|cpio -ocv > /dev/null) 200 GB storage on a GFS2 filesystem. This result in memory fragmentation and driver receives 18 frags for 1448 byte packets. For non tso packet, fw drops the tx request, if it has >14 frags. Fixing it by pulling extra frags. Signed-off-by: Amit Kumar Salecha Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/qlcnic/qlcnic.h | 1 + drivers/net/qlcnic/qlcnic_main.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/net/qlcnic/qlcnic.h b/drivers/net/qlcnic/qlcnic.h index 44e316fd67b85..0f136ff44b42f 100644 --- a/drivers/net/qlcnic/qlcnic.h +++ b/drivers/net/qlcnic/qlcnic.h @@ -99,6 +99,7 @@ #define TX_UDPV6_PKT 0x0c /* Tx defines */ +#define QLCNIC_MAX_FRAGS_PER_TX 14 #define MAX_TSO_HEADER_DESC 2 #define MGMT_CMD_DESC_RESV 4 #define TX_STOP_THRESH ((MAX_SKB_FRAGS >> 2) + MAX_TSO_HEADER_DESC \ diff --git a/drivers/net/qlcnic/qlcnic_main.c b/drivers/net/qlcnic/qlcnic_main.c index 37c04b4fade3b..92619d752430c 100644 --- a/drivers/net/qlcnic/qlcnic_main.c +++ b/drivers/net/qlcnic/qlcnic_main.c @@ -2099,6 +2099,7 @@ qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) struct cmd_desc_type0 *hwdesc, *first_desc; struct pci_dev *pdev; struct ethhdr *phdr; + int delta = 0; int i, k; u32 producer; @@ -2118,6 +2119,19 @@ qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) } frag_count = skb_shinfo(skb)->nr_frags + 1; + /* 14 frags supported for normal packet and + * 32 frags supported for TSO packet + */ + if (!skb_is_gso(skb) && frag_count > QLCNIC_MAX_FRAGS_PER_TX) { + + for (i = 0; i < (frag_count - QLCNIC_MAX_FRAGS_PER_TX); i++) + delta += skb_shinfo(skb)->frags[i].size; + + if (!__pskb_pull_tail(skb, delta)) + goto drop_packet; + + frag_count = 1 + skb_shinfo(skb)->nr_frags; + } /* 4 fragments per cmd des */ no_of_desc = (frag_count + 3) >> 2; From 575eae895c6c52834ba728cf564d6cff439c09a5 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 15 Apr 2011 18:08:26 -0400 Subject: [PATCH 1531/2556] nfsd4: fix struct file leak on delegation commit 4ee63624fd927376b97ead3a8d00728d437bc8e8 upstream. Introduced by acfdf5c383b38f7f4dddae41b97c97f1ae058f49. Reported-by: Gerhard Heift Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4state.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 96aaaa47fd0a3..201915af6864d 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -258,6 +258,7 @@ static void nfs4_put_deleg_lease(struct nfs4_file *fp) if (atomic_dec_and_test(&fp->fi_delegees)) { vfs_setlease(fp->fi_deleg_file, F_UNLCK, &fp->fi_lease); fp->fi_lease = NULL; + fput(fp->fi_deleg_file); fp->fi_deleg_file = NULL; } } From c1cc22677d272a7a192200b19bfc2d9e03e641ab Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Mon, 18 Apr 2011 11:48:55 -0400 Subject: [PATCH 1532/2556] nfsd4: Fix filp leak commit a96e5b90804be8b540d30f4a1453fc87f95b3149 upstream. 23fcf2ec93fb8573a653408316af599939ff9a8e (nfsd4: fix oops on lock failure) The above patch breaks free path for stp->st_file. If stp was inserted into sop->so_stateids, we have to free stp->st_file refcount. Because stp->st_file refcount itself is taken whether or not any refcounts are taken on the stp->st_file->fi_fds[]. Signed-off-by: OGAWA Hirofumi Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 201915af6864d..18c356cd50d8a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -403,8 +403,8 @@ static void free_generic_stateid(struct nfs4_stateid *stp) if (stp->st_access_bmap) { oflag = nfs4_access_bmap_to_omode(stp); nfs4_file_put_access(stp->st_file, oflag); - put_nfs4_file(stp->st_file); } + put_nfs4_file(stp->st_file); kmem_cache_free(stateid_slab, stp); } From a9a5cb680fc4c6f91d8234e4afc90afad22a37ad Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Wed, 16 Mar 2011 19:12:10 +0530 Subject: [PATCH 1533/2556] virtio: Decrement avail idx on buffer detach commit b3258ff1d6086bd2b9eeb556844a868ad7d49bc8 upstream. When detaching a buffer from a vq, the avail.idx value should be decremented as well. This was noticed by hot-unplugging a virtio console port and then plugging in a new one on the same number (re-using the vqs which were just 'disowned'). qemu reported 'Guest moved used index from 0 to 256' when any IO was attempted on the new port. Reported-by: juzhang Signed-off-by: Amit Shah Signed-off-by: Rusty Russell Signed-off-by: Greg Kroah-Hartman --- drivers/virtio/virtio_ring.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index cc2f73e03475b..b0043fb26a4d5 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -371,6 +371,7 @@ void *virtqueue_detach_unused_buf(struct virtqueue *_vq) /* detach_buf clears data, so grab it now. */ buf = vq->data[i]; detach_buf(vq, i); + vq->vring.avail->idx--; END_USE(vq); return buf; } From 513d3caa15b4f17203ac72d0fca2a646c6dea703 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Mon, 18 Apr 2011 15:45:45 +0200 Subject: [PATCH 1534/2556] x86, gart: Set DISTLBWALKPRB bit always commit c34151a742d84ae65db2088ea30495063f697fbe upstream. The DISTLBWALKPRB bit must be set for the GART because the gatt table is mapped UC. But the current code does not set the bit at boot when the BIOS setup the aperture correctly. Fix that by setting this bit when enabling the GART instead of the other places. Cc: Borislav Petkov Signed-off-by: Joerg Roedel Link: http://lkml.kernel.org/r/1303134346-5805-4-git-send-email-joerg.roedel@amd.com Signed-off-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/gart.h | 4 ++-- arch/x86/kernel/aperture_64.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/include/asm/gart.h b/arch/x86/include/asm/gart.h index 43085bfc99c30..3e7349f91afa4 100644 --- a/arch/x86/include/asm/gart.h +++ b/arch/x86/include/asm/gart.h @@ -66,7 +66,7 @@ static inline void gart_set_size_and_enable(struct pci_dev *dev, u32 order) * Don't enable translation but enable GART IO and CPU accesses. * Also, set DISTLBWALKPRB since GART tables memory is UC. */ - ctl = DISTLBWALKPRB | order << 1; + ctl = order << 1; pci_write_config_dword(dev, AMD64_GARTAPERTURECTL, ctl); } @@ -83,7 +83,7 @@ static inline void enable_gart_translation(struct pci_dev *dev, u64 addr) /* Enable GART translation for this hammer. */ pci_read_config_dword(dev, AMD64_GARTAPERTURECTL, &ctl); - ctl |= GARTEN; + ctl |= GARTEN | DISTLBWALKPRB; ctl &= ~(DISGARTCPU | DISGARTIO); pci_write_config_dword(dev, AMD64_GARTAPERTURECTL, ctl); } diff --git a/arch/x86/kernel/aperture_64.c b/arch/x86/kernel/aperture_64.c index 5955a7800a966..f6a1c2395b51f 100644 --- a/arch/x86/kernel/aperture_64.c +++ b/arch/x86/kernel/aperture_64.c @@ -500,7 +500,7 @@ int __init gart_iommu_hole_init(void) * Don't enable translation yet but enable GART IO and CPU * accesses and set DISTLBWALKPRB since GART table memory is UC. */ - u32 ctl = DISTLBWALKPRB | aper_order << 1; + u32 ctl = aper_order << 1; bus = amd_nb_bus_dev_ranges[i].bus; dev_base = amd_nb_bus_dev_ranges[i].dev_base; From e23e2ea1c1284bd5d80a68bc19a6c80da7773097 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Mon, 18 Apr 2011 15:45:46 +0200 Subject: [PATCH 1535/2556] x86, gart: Make sure GART does not map physmem above 1TB commit 665d3e2af83c8fbd149534db8f57d82fa6fa6753 upstream. The GART can only map physical memory below 1TB. Make sure the gart driver in the kernel does not try to map memory above 1TB. Signed-off-by: Joerg Roedel Link: http://lkml.kernel.org/r/1303134346-5805-5-git-send-email-joerg.roedel@amd.com Signed-off-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/pci-gart_64.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/pci-gart_64.c b/arch/x86/kernel/pci-gart_64.c index c01ffa5b9b87e..197a46ff5148b 100644 --- a/arch/x86/kernel/pci-gart_64.c +++ b/arch/x86/kernel/pci-gart_64.c @@ -81,6 +81,9 @@ static u32 gart_unmapped_entry; #define AGPEXTERN #endif +/* GART can only remap to physical addresses < 1TB */ +#define GART_MAX_PHYS_ADDR (1ULL << 40) + /* backdoor interface to AGP driver */ AGPEXTERN int agp_memory_reserved; AGPEXTERN __u32 *agp_gatt_table; @@ -212,9 +215,13 @@ static dma_addr_t dma_map_area(struct device *dev, dma_addr_t phys_mem, size_t size, int dir, unsigned long align_mask) { unsigned long npages = iommu_num_pages(phys_mem, size, PAGE_SIZE); - unsigned long iommu_page = alloc_iommu(dev, npages, align_mask); + unsigned long iommu_page; int i; + if (unlikely(phys_mem + size > GART_MAX_PHYS_ADDR)) + return bad_dma_addr; + + iommu_page = alloc_iommu(dev, npages, align_mask); if (iommu_page == -1) { if (!nonforced_iommu(dev, phys_mem, size)) return phys_mem; From d2cec366b9b900828ec045af91fdac7f7f491e4d Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 2 Nov 2010 08:05:51 +0100 Subject: [PATCH 1536/2556] intel-iommu: Fix use after release during device attach commit 7a6610139a1e1d9297dd1c5d178022eac36839cb upstream. Obtain the new pgd pointer before releasing the page containing this value. Signed-off-by: Jan Kiszka Reviewed-by: Sheng Yang Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- drivers/pci/intel-iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 4789f8e8bf7ad..35463ddf10a16 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -3627,9 +3627,9 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, pte = dmar_domain->pgd; if (dma_pte_present(pte)) { - free_pgtable_page(dmar_domain->pgd); dmar_domain->pgd = (struct dma_pte *) phys_to_virt(dma_pte_addr(pte)); + free_pgtable_page(pte); } dmar_domain->agaw--; } From 9f5acf48f5cf2b8b858d9cc3dc0004d685222668 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Fri, 4 Mar 2011 14:52:16 -0700 Subject: [PATCH 1537/2556] intel-iommu: Unlink domain from iommu commit a97590e56d0d58e1dd262353f7cbd84e81d8e600 upstream. When we remove a device, we unlink the iommu from the domain, but we never do the reverse unlinking of the domain from the iommu. This means that we never clear iommu->domain_ids, eventually leading to resource exhaustion if we repeatedly bind and unbind a device to a driver. Also free empty domains to avoid a resource leak. Signed-off-by: Alex Williamson Acked-by: Donald Dutile Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- drivers/pci/intel-iommu.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 35463ddf10a16..292f2233295ef 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -3260,9 +3260,15 @@ static int device_notifier(struct notifier_block *nb, if (!domain) return 0; - if (action == BUS_NOTIFY_UNBOUND_DRIVER && !iommu_pass_through) + if (action == BUS_NOTIFY_UNBOUND_DRIVER && !iommu_pass_through) { domain_remove_one_dev_info(domain, pdev); + if (!(domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) && + !(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY) && + list_empty(&domain->devices)) + domain_exit(domain); + } + return 0; } @@ -3411,6 +3417,11 @@ static void domain_remove_one_dev_info(struct dmar_domain *domain, domain->iommu_count--; domain_update_iommu_cap(domain); spin_unlock_irqrestore(&domain->iommu_lock, tmp_flags); + + spin_lock_irqsave(&iommu->lock, tmp_flags); + clear_bit(domain->id, iommu->domain_ids); + iommu->domains[domain->id] = NULL; + spin_unlock_irqrestore(&iommu->lock, tmp_flags); } spin_unlock_irqrestore(&device_domain_lock, flags); From 7355a18f350d2d13ab78645e81d7cbf7c3715ec7 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Fri, 4 Mar 2011 14:52:30 -0700 Subject: [PATCH 1538/2556] intel-iommu: Fix get_domain_for_dev() error path commit 2fe9723df8e45fd247782adea244a5e653c30bf4 upstream. If we run out of domain_ids and fail iommu_attach_domain(), we fall into domain_exit() without having setup enough of the domain structure for this to do anything useful. In fact, it typically runs off into the weeds walking the bogus domain->devices list. Just free the domain. Signed-off-by: Alex Williamson Acked-by: Donald Dutile Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- drivers/pci/intel-iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 292f2233295ef..5dc5d3e3508e8 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -1835,7 +1835,7 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw) ret = iommu_attach_domain(domain, iommu); if (ret) { - domain_exit(domain); + free_domain_mem(domain); goto error; } From eb2a1d029ff4c899b0a7b5b4af0e4b9101238eed Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 19 Apr 2011 15:24:59 -0400 Subject: [PATCH 1539/2556] drm/radeon/kms: pll tweaks for r7xx commit 5785e53ffa73f77fb19e378c899027afc07272bc upstream. Prefer min m to max p only on pre-r7xx asics. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=36197 Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/atombios_crtc.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 08612572c5581..bede31c6571bd 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -532,10 +532,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, else pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV; - if ((rdev->family == CHIP_R600) || - (rdev->family == CHIP_RV610) || - (rdev->family == CHIP_RV630) || - (rdev->family == CHIP_RV670)) + if (rdev->family < CHIP_RV770) pll->flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP; } else { pll->flags |= RADEON_PLL_LEGACY; @@ -565,7 +562,6 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { if (ss_enabled) { if (ss->refdiv) { - pll->flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP; pll->flags |= RADEON_PLL_USE_REF_DIV; pll->reference_div = ss->refdiv; if (ASIC_IS_AVIVO(rdev)) From d99efc3d77c864f9fba432a6a44020e56f9e1367 Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Tue, 19 Apr 2011 23:50:48 +0200 Subject: [PATCH 1540/2556] drm/nouveau: fix notifier memory corruption bug commit a18d89ca026140eb8ac4459bf70a01c571dd9a32 upstream. nouveau_bo_wr32 expects offset to be in words, but we pass value in bytes, so after commit 73412c3854c877e5f37ad944ee8977addde4d35a ("drm/nouveau: allocate kernel's notifier object at end of block") we started to overwrite some memory after notifier buffer object (previously m2mf_ntfy was always 0, so it didn't matter it was a value in bytes). Reported-by: Dominik Brodowski Reported-by: Nigel Cunningham Signed-off-by: Marcin Slusarz Cc: Ben Skeggs Cc: Pekka Paalanen Signed-off-by: Ben Skeggs Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/nouveau/nouveau_fbcon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 60769d2f9a668..7826be0653e19 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -181,13 +181,13 @@ nouveau_fbcon_sync(struct fb_info *info) OUT_RING (chan, 0); } - nouveau_bo_wr32(chan->notifier_bo, chan->m2mf_ntfy + 3, 0xffffffff); + nouveau_bo_wr32(chan->notifier_bo, chan->m2mf_ntfy/4 + 3, 0xffffffff); FIRE_RING(chan); mutex_unlock(&chan->mutex); ret = -EBUSY; for (i = 0; i < 100000; i++) { - if (!nouveau_bo_rd32(chan->notifier_bo, chan->m2mf_ntfy + 3)) { + if (!nouveau_bo_rd32(chan->notifier_bo, chan->m2mf_ntfy/4 + 3)) { ret = 0; break; } From 7078b061ccdb56eedc0901dbe4d0903d3429627c Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 14 Apr 2011 11:19:50 -0400 Subject: [PATCH 1541/2556] drm/radeon/kms: fix bad shift in atom iio table parser commit 8e461123f28e6b17456225e70eb834b3b30d28bb upstream. Noticed by Patrick Lowry. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/atom.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c index d71d375149f85..7bd7456890974 100644 --- a/drivers/gpu/drm/radeon/atom.c +++ b/drivers/gpu/drm/radeon/atom.c @@ -135,7 +135,7 @@ static uint32_t atom_iio_execute(struct atom_context *ctx, int base, case ATOM_IIO_MOVE_INDEX: temp &= ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << - CU8(base + 2)); + CU8(base + 3)); temp |= ((index >> CU8(base + 2)) & (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base + @@ -145,7 +145,7 @@ static uint32_t atom_iio_execute(struct atom_context *ctx, int base, case ATOM_IIO_MOVE_DATA: temp &= ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << - CU8(base + 2)); + CU8(base + 3)); temp |= ((data >> CU8(base + 2)) & (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base + @@ -155,7 +155,7 @@ static uint32_t atom_iio_execute(struct atom_context *ctx, int base, case ATOM_IIO_MOVE_ATTR: temp &= ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << - CU8(base + 2)); + CU8(base + 3)); temp |= ((ctx-> io_attr >> CU8(base + 2)) & (0xFFFFFFFF >> (32 - From 4754420404742f937ec438c36dc8288b435381b0 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 12 Apr 2011 18:06:51 +0100 Subject: [PATCH 1542/2556] drm/i915: Sanitize the output registers after resume commit f6e5b1603b8bb7131b6778d0d4e2e5dda120a379 upstream. Similar to booting, we need to inspect the state left by the BIOS and remove any conflicting bits before we take over. The example reported by Seth Forshee is very similar to the bug we encountered with the state left by grub2, that the crtc pipe<->planning mapping was reversed from our expectations and so we failed to turn off the outputs when booting or, in this case, resuming. This may be in fact the same bug, but triggered at resume time. This patch rearranges the code we already have to clear up the conflicting state upon init and calls it from reset (which is called after we have lost control of the hardware, i.e. along both the boot and resume paths) instead. Reported-and-tested-by: Seth Forshee Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=35796 Signed-off-by: Chris Wilson Reviewed-by: Keith Packard Signed-off-by: Keith Packard Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_display.c | 68 +++++++++++++++------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 49fb54fd9a187..ecf8f947b4b79 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5630,36 +5630,6 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, return ret; } -static void intel_crtc_reset(struct drm_crtc *crtc) -{ - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - /* Reset flags back to the 'unknown' status so that they - * will be correctly set on the initial modeset. - */ - intel_crtc->dpms_mode = -1; -} - -static struct drm_crtc_helper_funcs intel_helper_funcs = { - .dpms = intel_crtc_dpms, - .mode_fixup = intel_crtc_mode_fixup, - .mode_set = intel_crtc_mode_set, - .mode_set_base = intel_pipe_set_base, - .mode_set_base_atomic = intel_pipe_set_base_atomic, - .load_lut = intel_crtc_load_lut, - .disable = intel_crtc_disable, -}; - -static const struct drm_crtc_funcs intel_crtc_funcs = { - .reset = intel_crtc_reset, - .cursor_set = intel_crtc_cursor_set, - .cursor_move = intel_crtc_cursor_move, - .gamma_set = intel_crtc_gamma_set, - .set_config = drm_crtc_helper_set_config, - .destroy = intel_crtc_destroy, - .page_flip = intel_crtc_page_flip, -}; - static void intel_sanitize_modesetting(struct drm_device *dev, int pipe, int plane) { @@ -5710,6 +5680,42 @@ static void intel_sanitize_modesetting(struct drm_device *dev, } } +static void intel_crtc_reset(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + /* Reset flags back to the 'unknown' status so that they + * will be correctly set on the initial modeset. + */ + intel_crtc->dpms_mode = -1; + + /* We need to fix up any BIOS configuration that conflicts with + * our expectations. + */ + intel_sanitize_modesetting(dev, intel_crtc->pipe, intel_crtc->plane); +} + +static struct drm_crtc_helper_funcs intel_helper_funcs = { + .dpms = intel_crtc_dpms, + .mode_fixup = intel_crtc_mode_fixup, + .mode_set = intel_crtc_mode_set, + .mode_set_base = intel_pipe_set_base, + .mode_set_base_atomic = intel_pipe_set_base_atomic, + .load_lut = intel_crtc_load_lut, + .disable = intel_crtc_disable, +}; + +static const struct drm_crtc_funcs intel_crtc_funcs = { + .reset = intel_crtc_reset, + .cursor_set = intel_crtc_cursor_set, + .cursor_move = intel_crtc_cursor_move, + .gamma_set = intel_crtc_gamma_set, + .set_config = drm_crtc_helper_set_config, + .destroy = intel_crtc_destroy, + .page_flip = intel_crtc_page_flip, +}; + static void intel_crtc_init(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -5759,8 +5765,6 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) setup_timer(&intel_crtc->idle_timer, intel_crtc_idle_timer, (unsigned long)intel_crtc); - - intel_sanitize_modesetting(dev, intel_crtc->pipe, intel_crtc->plane); } int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, From 0c85ff3d9daa561683ed9358e3734f226a5a180f Mon Sep 17 00:00:00 2001 From: Mathew McKernan Date: Tue, 12 Apr 2011 06:51:37 +0100 Subject: [PATCH 1543/2556] drm/i915/tv: Remember the detected TV type commit d5627663f2088fa4be447fdcfd52bcb233448d85 upstream. During detect() we would probe the connection bits to determine if there was a TV attached, and what video input type (Component, S-Video, Composite, etc) to use. However, we promptly discarded this vital bit of information and never propagated it to where it was used to determine the correct modes and setup the control registers. Fix it! This fixes a regression from 7b334fcb45b757ffb093696ca3de1b0c8b4a33f1. Reported-and-tested-by: Mathew McKernan Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=35977 Signed-off-by: Mathew McKernan Signed-off-by: Chris Wilson Acked-by: Paul Menzel Signed-off-by: Keith Packard Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_tv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index fe4a53a50b833..65edb227ae811 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1380,7 +1380,9 @@ intel_tv_detect(struct drm_connector *connector, bool force) if (type < 0) return connector_status_disconnected; + intel_tv->type = type; intel_tv_find_better_format(connector); + return connector_status_connected; } From c03f33ae1ae04c6d5068c5a9176e6aaed2c09b50 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Sun, 27 Mar 2011 04:05:00 +0400 Subject: [PATCH 1544/2556] tty/n_gsm: fix bug in CRC calculation for gsm1 mode commit 9db4e4381a8e881ff65a5d3400bfa471f84217e7 upstream. Problem description: gsm_queue() calculate a CRC for arrived frames. As a last step of CRC calculation it call gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs); This work perfectly for the case of GSM0 mode as gsm->received_fcs contain the last piece of data required to generate final CRC. gsm->received_fcs is not used for GSM1 mode. Thus we put an additional byte to CRC calculation. As result we get a wrong CRC and reject incoming frame. Signed-off-by: Mikhail Kshevetskiy Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index aa2e5d3eb01a4..c4b0ef1a38747 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -1659,8 +1659,12 @@ static void gsm_queue(struct gsm_mux *gsm) if ((gsm->control & ~PF) == UI) gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len); - /* generate final CRC with received FCS */ - gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs); + if (gsm->encoding == 0){ + /* WARNING: gsm->received_fcs is used for gsm->encoding = 0 only. + In this case it contain the last piece of data + required to generate final CRC */ + gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs); + } if (gsm->fcs != GOOD_FCS) { gsm->bad_fcs++; if (debug & 4) From bc5f42122efac197af024bd39c40e93761ea04ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 11 Apr 2011 10:59:09 +0200 Subject: [PATCH 1545/2556] serial/imx: read cts state only after acking cts change irq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 5680e94148a86e8c31fdc5cb0ea0d5c6810c05b0 upstream. If cts changes between reading the level at the cts input (USR1_RTSS) and acking the irq (USR1_RTSD) the last edge doesn't generate an irq and uart_handle_cts_change is called with a outdated value for cts. The race was introduced by commit ceca629 ([ARM] 2971/1: i.MX uart handle rts irq) Reported-by: Arwed Springer Tested-by: Arwed Springer Signed-off-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index dfcf4b1878aa3..0d66751c0cbc0 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -382,12 +382,13 @@ static void imx_start_tx(struct uart_port *port) static irqreturn_t imx_rtsint(int irq, void *dev_id) { struct imx_port *sport = dev_id; - unsigned int val = readl(sport->port.membase + USR1) & USR1_RTSS; + unsigned int val; unsigned long flags; spin_lock_irqsave(&sport->port.lock, flags); writel(USR1_RTSD, sport->port.membase + USR1); + val = readl(sport->port.membase + USR1) & USR1_RTSS; uart_handle_cts_change(&sport->port, !!val); wake_up_interruptible(&sport->port.state->port.delta_msr_wait); From 22a817279c251e1d28f1a9fd66d4c873b02f034d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 8 Apr 2011 16:32:16 +0900 Subject: [PATCH 1546/2556] ASoC: Fix output PGA enabling in wm_hubs CODECs commit 39cca168bdfaef9d0c496ec27f292445d6184946 upstream. The output PGA was not being powered up in headphone and speaker paths, removing the ability to offer volume control and mute with the output PGA. Signed-off-by: Mark Brown Acked-by: Liam Girdwood Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/wm_hubs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 516892706063d..d365f4344f96d 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -739,12 +739,12 @@ static const struct snd_soc_dapm_route analogue_routes[] = { { "SPKL", "Input Switch", "MIXINL" }, { "SPKL", "IN1LP Switch", "IN1LP" }, - { "SPKL", "Output Switch", "Left Output Mixer" }, + { "SPKL", "Output Switch", "Left Output PGA" }, { "SPKL", NULL, "TOCLK" }, { "SPKR", "Input Switch", "MIXINR" }, { "SPKR", "IN1RP Switch", "IN1RP" }, - { "SPKR", "Output Switch", "Right Output Mixer" }, + { "SPKR", "Output Switch", "Right Output PGA" }, { "SPKR", NULL, "TOCLK" }, { "SPKL Boost", "Direct Voice Switch", "Direct Voice" }, @@ -766,8 +766,8 @@ static const struct snd_soc_dapm_route analogue_routes[] = { { "SPKOUTRP", NULL, "SPKR Driver" }, { "SPKOUTRN", NULL, "SPKR Driver" }, - { "Left Headphone Mux", "Mixer", "Left Output Mixer" }, - { "Right Headphone Mux", "Mixer", "Right Output Mixer" }, + { "Left Headphone Mux", "Mixer", "Left Output PGA" }, + { "Right Headphone Mux", "Mixer", "Right Output PGA" }, { "Headphone PGA", NULL, "Left Headphone Mux" }, { "Headphone PGA", NULL, "Right Headphone Mux" }, From 158554a00beea398e2e46cd43be7a43270b70b91 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 12 Apr 2011 19:33:28 +0200 Subject: [PATCH 1547/2556] ASoC: codecs: JZ4740: Fix OOPS commit 1fdf9b49e9e7788d09bad4b08a6a821ac39798f3 upstream. Commit ce6120cc(ASoC: Decouple DAPM from CODECs) changed the signature of snd_soc_dapm_widgets_new to take an pointer to a snd_soc_dapm_context instead of a snd_soc_codec. The call to snd_soc_dapm_widgets_new in jz4740_codec_dev_probe was not updated to reflect this change, which results in a compiletime warning and a runtime OOPS. Since the core code calls snd_soc_dapm_widgets_new after the codec has been registered it can be dropped here. Signed-off-by: Lars-Peter Clausen Acked-by: Liam Girdwood Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/jz4740.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c index f7cd346fd7275..f5ccdbf7ebc6e 100644 --- a/sound/soc/codecs/jz4740.c +++ b/sound/soc/codecs/jz4740.c @@ -308,8 +308,6 @@ static int jz4740_codec_dev_probe(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(dapm, jz4740_codec_dapm_routes, ARRAY_SIZE(jz4740_codec_dapm_routes)); - snd_soc_dapm_new_widgets(codec); - jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; From bc80c8f207d3fc3a780b4bad516c4444388540ed Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 15 Apr 2011 10:11:12 +0200 Subject: [PATCH 1548/2556] ALSA: hda - Add a fix-up for Acer dmic with ALC271x codec commit 6981d184376e74391c23c116a068f8d1305f0e57 upstream. Acer laptops with ALC271x needs a magic initialization for digital-mic to make it working with mono streams (and PulseAudio). Added a fix-up applied to Acer with ALC271x generically. Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index da7cdca4776d5..8ff0223fb10f2 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -14945,6 +14945,23 @@ static void alc269_fixup_hweq(struct hda_codec *codec, alc_write_coef_idx(codec, 0x1e, coef | 0x80); } +static void alc271_fixup_dmic(struct hda_codec *codec, + const struct alc_fixup *fix, int action) +{ + static struct hda_verb verbs[] = { + {0x20, AC_VERB_SET_COEF_INDEX, 0x0d}, + {0x20, AC_VERB_SET_PROC_COEF, 0x4000}, + {} + }; + unsigned int cfg; + + if (strcmp(codec->chip_name, "ALC271X")) + return; + cfg = snd_hda_codec_get_pincfg(codec, 0x12); + if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED) + snd_hda_sequence_write(codec, verbs); +} + enum { ALC269_FIXUP_SONY_VAIO, ALC275_FIXUP_SONY_VAIO_GPIO2, @@ -14953,6 +14970,7 @@ enum { ALC269_FIXUP_ASUS_G73JW, ALC269_FIXUP_LENOVO_EAPD, ALC275_FIXUP_SONY_HWEQ, + ALC271_FIXUP_DMIC, }; static const struct alc_fixup alc269_fixups[] = { @@ -15006,7 +15024,11 @@ static const struct alc_fixup alc269_fixups[] = { .v.func = alc269_fixup_hweq, .chained = true, .chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2 - } + }, + [ALC271_FIXUP_DMIC] = { + .type = ALC_FIXUP_FUNC, + .v.func = alc271_fixup_dmic, + }, }; static struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -15015,6 +15037,7 @@ static struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), + SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE), From f1ad97b738c5abbe0d3536e9ffdeed2960b81153 Mon Sep 17 00:00:00 2001 From: Maxime Bizon Date: Wed, 16 Mar 2011 14:58:32 +0100 Subject: [PATCH 1549/2556] ahci: don't enable port irq before handler is registered commit 7b3a24c57d2eeda8dba9c205342b12689c4679f9 upstream. The ahci_pmp_attach() & ahci_pmp_detach() unmask port irqs, but they are also called during port initialization, before ahci host irq handler is registered. On ce4100 platform, this sometimes triggers "irq 4: nobody cared" message when loading driver. Fixed this by not touching the register if the port is in frozen state, and mark all uninitialized port as frozen. Signed-off-by: Maxime Bizon Acked-by: Tejun Heo Signed-off-by: Jeff Garzik Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libahci.c | 17 +++++++++++++++-- drivers/ata/libata-core.c | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 26d452339e98f..8498eb5cd413b 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -1897,7 +1897,17 @@ static void ahci_pmp_attach(struct ata_port *ap) ahci_enable_fbs(ap); pp->intr_mask |= PORT_IRQ_BAD_PMP; - writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); + + /* + * We must not change the port interrupt mask register if the + * port is marked frozen, the value in pp->intr_mask will be + * restored later when the port is thawed. + * + * Note that during initialization, the port is marked as + * frozen since the irq handler is not yet registered. + */ + if (!(ap->pflags & ATA_PFLAG_FROZEN)) + writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); } static void ahci_pmp_detach(struct ata_port *ap) @@ -1913,7 +1923,10 @@ static void ahci_pmp_detach(struct ata_port *ap) writel(cmd, port_mmio + PORT_CMD); pp->intr_mask &= ~PORT_IRQ_BAD_PMP; - writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); + + /* see comment above in ahci_pmp_attach() */ + if (!(ap->pflags & ATA_PFLAG_FROZEN)) + writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); } int ahci_port_resume(struct ata_port *ap) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index d4e52e2148593..4ccce0f371a36 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5479,8 +5479,8 @@ struct ata_port *ata_port_alloc(struct ata_host *host) ap = kzalloc(sizeof(*ap), GFP_KERNEL); if (!ap) return NULL; - - ap->pflags |= ATA_PFLAG_INITIALIZING; + + ap->pflags |= ATA_PFLAG_INITIALIZING | ATA_PFLAG_FROZEN; ap->lock = &host->lock; ap->print_id = -1; ap->host = host; From e1dba3727f13bf89b25b784a56da157a2a4005c3 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 16 Mar 2011 11:14:55 +0100 Subject: [PATCH 1550/2556] libata: Implement ATA_FLAG_NO_DIPM and apply it to mcp65 commit ae01b2493c3bf03c504c32ac4ebb01d528508db3 upstream. NVIDIA mcp65 familiy of controllers cause command timeouts when DIPM is used. Implement ATA_FLAG_NO_DIPM and apply it. This problem was reported by Stefan Bader in the following thread. http://thread.gmane.org/gmane.linux.ide/48841 stable: applicable to 2.6.37 and 38. Signed-off-by: Tejun Heo Reported-by: Stefan Bader Signed-off-by: Jeff Garzik Signed-off-by: Greg Kroah-Hartman --- drivers/ata/ahci.c | 2 +- drivers/ata/libata-eh.c | 6 ++++-- include/linux/libata.h | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 34e08f63b6889..54c096b1a71cb 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -150,7 +150,7 @@ static const struct ata_port_info ahci_port_info[] = { { AHCI_HFLAGS (AHCI_HFLAG_NO_FPDMA_AA | AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ), - .flags = AHCI_FLAG_COMMON, + .flags = AHCI_FLAG_COMMON | ATA_FLAG_NO_DIPM, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &ahci_ops, diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index e16850e8d2f8a..fe18c2de41ba1 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -3276,6 +3276,7 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, struct ata_eh_context *ehc = &link->eh_context; struct ata_device *dev, *link_dev = NULL, *lpm_dev = NULL; enum ata_lpm_policy old_policy = link->lpm_policy; + bool no_dipm = ap->flags & ATA_FLAG_NO_DIPM; unsigned int hints = ATA_LPM_EMPTY | ATA_LPM_HIPM; unsigned int err_mask; int rc; @@ -3292,7 +3293,7 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, */ ata_for_each_dev(dev, link, ENABLED) { bool hipm = ata_id_has_hipm(dev->id); - bool dipm = ata_id_has_dipm(dev->id); + bool dipm = ata_id_has_dipm(dev->id) && !no_dipm; /* find the first enabled and LPM enabled devices */ if (!link_dev) @@ -3349,7 +3350,8 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, /* host config updated, enable DIPM if transitioning to MIN_POWER */ ata_for_each_dev(dev, link, ENABLED) { - if (policy == ATA_LPM_MIN_POWER && ata_id_has_dipm(dev->id)) { + if (policy == ATA_LPM_MIN_POWER && !no_dipm && + ata_id_has_dipm(dev->id)) { err_mask = ata_dev_set_feature(dev, SETFEATURES_SATA_ENABLE, SATA_DIPM); if (err_mask && err_mask != AC_ERR_DEV) { diff --git a/include/linux/libata.h b/include/linux/libata.h index c9c5d7ad1a2bc..1f000807847d2 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -203,6 +203,7 @@ enum { * management */ ATA_FLAG_SW_ACTIVITY = (1 << 22), /* driver supports sw activity * led */ + ATA_FLAG_NO_DIPM = (1 << 23), /* host not happy with DIPM */ /* bits 24:31 of ap->flags are reserved for LLD specific flags */ From 4fb705f7150fec7c5bd27d9b5bb38a9fbeb11bc6 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sat, 23 Apr 2011 18:42:56 +0100 Subject: [PATCH 1551/2556] kconfig: Avoid buffer underrun in choice input commit 3ba41621156681afcdbcd624e3191cbc65eb94f4 upstream. Commit 40aee729b350 ('kconfig: fix default value for choice input') fixed some cases where kconfig would select the wrong option from a choice with a single valid option and thus enter an infinite loop. However, this broke the test for user input of the form 'N?', because when kconfig selects the single valid option the input is zero-length and the test will read the byte before the input buffer. If this happens to contain '?' (as it will in a mips build on Debian unstable today) then kconfig again enters an infinite loop. Signed-off-by: Ben Hutchings Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- scripts/kconfig/conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 659326c3e8957..006ad817cd5f0 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -332,7 +332,7 @@ static int conf_choice(struct menu *menu) } if (!child) continue; - if (line[strlen(line) - 1] == '?') { + if (line[0] && line[strlen(line) - 1] == '?') { print_help(child); continue; } From 87b9000d551994b2e4faada850c798f4c01cd504 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 21 Apr 2011 14:49:55 +0300 Subject: [PATCH 1552/2556] UBIFS: fix master node recovery commit 6e0d9fd38b750d678bf9fd07db23582f52fafa55 upstream. This patch fixes the following symptoms: 1. Unmount UBIFS cleanly. 2. Start mounting UBIFS R/W and have a power cut immediately 3. Start mounting UBIFS R/O, this succeeds 4. Try to re-mount UBIFS R/W - this fails immediately or later on, because UBIFS will write the master node to the flash area which has been written before. The analysis of the problem: 1. UBIFS is unmounted cleanly, both copies of the master node are clean. 2. UBIFS is being mounter R/W, starts changing master node copy 1, and a power cut happens. The copy N1 becomes corrupted. 3. UBIFS is being mounted R/O. It notices the copy N1 is corrupted and reads copy N2. Copy N2 is clean. 4. Because of R/O mode, UBIFS cannot recover copy 1. 5. The mount code (ubifs_mount()) sees that the master node is clean, so it decides that no recovery is needed. 6. We are re-mounting R/W. UBIFS believes no recovery is needed and starts updating the master node, but copy N1 is still corrupted and was not recovered! Fix this problem by marking the master node as dirty every time we recover it and we are in R/O mode. This forces further recovery and the UBIFS cleans-up the corruptions and recovers the copy N1 when re-mounting R/W later. Signed-off-by: Artem Bityutskiy Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/recovery.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/fs/ubifs/recovery.c b/fs/ubifs/recovery.c index 77e9b874b6c22..c0c590feabe1a 100644 --- a/fs/ubifs/recovery.c +++ b/fs/ubifs/recovery.c @@ -300,6 +300,32 @@ int ubifs_recover_master_node(struct ubifs_info *c) goto out_free; } memcpy(c->rcvrd_mst_node, c->mst_node, UBIFS_MST_NODE_SZ); + + /* + * We had to recover the master node, which means there was an + * unclean reboot. However, it is possible that the master node + * is clean at this point, i.e., %UBIFS_MST_DIRTY is not set. + * E.g., consider the following chain of events: + * + * 1. UBIFS was cleanly unmounted, so the master node is clean + * 2. UBIFS is being mounted R/W and starts changing the master + * node in the first (%UBIFS_MST_LNUM). A power cut happens, + * so this LEB ends up with some amount of garbage at the + * end. + * 3. UBIFS is being mounted R/O. We reach this place and + * recover the master node from the second LEB + * (%UBIFS_MST_LNUM + 1). But we cannot update the media + * because we are being mounted R/O. We have to defer the + * operation. + * 4. However, this master node (@c->mst_node) is marked as + * clean (since the step 1). And if we just return, the + * mount code will be confused and won't recover the master + * node when it is re-mounter R/W later. + * + * Thus, to force the recovery by marking the master node as + * dirty. + */ + c->mst_node->flags |= cpu_to_le32(UBIFS_MST_DIRTY); } else { /* Write the recovered master node */ c->max_sqnum = le64_to_cpu(mst->ch.sqnum) - 1; From c3ff4ad5b4a07414015ee2fa93b0700e173b8bac Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Wed, 23 Feb 2011 21:39:59 +0800 Subject: [PATCH 1553/2556] ideapad: read brightness setting on brightness key notify commit 2165136585b5c7d6f118f1d90fbde550bb7de212 upstream. BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=25922 On ideapad Y530, the brightness key notify will be blocked if the last notify is not responsed by getting the brightness value. Read value when we get the notify shall fix the problem and will not have any difference on other ideapads. Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/ideapad-laptop.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 114d95247cdf8..21b101899baee 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -459,6 +459,8 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) if (test_bit(vpc_bit, &vpc1)) { if (vpc_bit == 9) ideapad_sync_rfk_state(adevice); + else if (vpc_bit == 4) + read_ec_data(handle, 0x12, &vpc2); else ideapad_input_report(priv, vpc_bit); } From 216b412cbf3da83ea4ed83b647026ff292c7abd3 Mon Sep 17 00:00:00 2001 From: Kyle McMartin Date: Tue, 22 Mar 2011 16:19:50 -0400 Subject: [PATCH 1554/2556] ACPI battery: fribble sysfs files from a resume notifier commit 25be5821521640eb00b7eb219ffe59664510d073 upstream. Commit da8aeb92 re-poked the battery on resume, but Linus reports that it broke his eee and partially reverted it in b23fffd7. Unfortunately this also results in my x201s giving crack values until the sysfs files are poked again. In the revert message, it was suggested that we poke it from a PM notifier, so let's do that. With this in place, I haven't noticed the units going nutty on my gnome-power-manager across a dozen suspends or so... Signed-off-by: Kyle McMartin Acked-by: Rafael J. Wysocki Signed-off-by: Len Brown Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/battery.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index ac1a599f51476..fcc13ac0aa187 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -33,6 +33,7 @@ #include #include #include +#include #ifdef CONFIG_ACPI_PROCFS_POWER #include @@ -102,6 +103,7 @@ struct acpi_battery { struct mutex lock; struct power_supply bat; struct acpi_device *device; + struct notifier_block pm_nb; unsigned long update_time; int rate_now; int capacity_now; @@ -940,6 +942,21 @@ static void acpi_battery_notify(struct acpi_device *device, u32 event) power_supply_changed(&battery->bat); } +static int battery_notify(struct notifier_block *nb, + unsigned long mode, void *_unused) +{ + struct acpi_battery *battery = container_of(nb, struct acpi_battery, + pm_nb); + switch (mode) { + case PM_POST_SUSPEND: + sysfs_remove_battery(battery); + sysfs_add_battery(battery); + break; + } + + return 0; +} + static int acpi_battery_add(struct acpi_device *device) { int result = 0; @@ -972,6 +989,10 @@ static int acpi_battery_add(struct acpi_device *device) #endif kfree(battery); } + + battery->pm_nb.notifier_call = battery_notify; + register_pm_notifier(&battery->pm_nb); + return result; } @@ -982,6 +1003,7 @@ static int acpi_battery_remove(struct acpi_device *device, int type) if (!device || !acpi_driver_data(device)) return -EINVAL; battery = acpi_driver_data(device); + unregister_pm_notifier(&battery->pm_nb); #ifdef CONFIG_ACPI_PROCFS_POWER acpi_battery_remove_fs(device); #endif From ce5fe399e508b41787ea04761fdf9a29c1caa7aa Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 14 Jan 2011 00:06:27 +0100 Subject: [PATCH 1555/2556] ath9k_hw: partially revert "fix dma descriptor rx error bit parsing" commit 115dad7a7f42e68840392767323ceb9306dbdb36 upstream. The rx error bit parsing was changed to consider PHY errors and various decryption errors separately. While correct according to the documentation, this is causing spurious decryption error reports in some situations. Fix this by restoring the original order of the checks in those places, where the errors are meant to be mutually exclusive. If a CRC error is reported, then MIC failure and decryption errors are irrelevant, and a PHY error is unlikely. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/ar9003_mac.c | 8 ++++---- drivers/net/wireless/ath/ath9k/mac.c | 14 ++++++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index 4ceddbbdfcee6..038a0cbfc6e7c 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -615,7 +615,7 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, */ if (rxsp->status11 & AR_CRCErr) rxs->rs_status |= ATH9K_RXERR_CRC; - if (rxsp->status11 & AR_PHYErr) { + else if (rxsp->status11 & AR_PHYErr) { phyerr = MS(rxsp->status11, AR_PHYErrCode); /* * If we reach a point here where AR_PostDelimCRCErr is @@ -638,11 +638,11 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, rxs->rs_phyerr = phyerr; } - } - if (rxsp->status11 & AR_DecryptCRCErr) + } else if (rxsp->status11 & AR_DecryptCRCErr) rxs->rs_status |= ATH9K_RXERR_DECRYPT; - if (rxsp->status11 & AR_MichaelErr) + else if (rxsp->status11 & AR_MichaelErr) rxs->rs_status |= ATH9K_RXERR_MIC; + if (rxsp->status11 & AR_KeyMiss) rxs->rs_status |= ATH9K_RXERR_DECRYPT; } diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 76d1f8c7db376..e9fc97d4c9017 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -690,17 +690,23 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, rs->rs_flags |= ATH9K_RX_DECRYPT_BUSY; if ((ads.ds_rxstatus8 & AR_RxFrameOK) == 0) { + /* + * Treat these errors as mutually exclusive to avoid spurious + * extra error reports from the hardware. If a CRC error is + * reported, then decryption and MIC errors are irrelevant, + * the frame is going to be dropped either way + */ if (ads.ds_rxstatus8 & AR_CRCErr) rs->rs_status |= ATH9K_RXERR_CRC; - if (ads.ds_rxstatus8 & AR_PHYErr) { + else if (ads.ds_rxstatus8 & AR_PHYErr) { rs->rs_status |= ATH9K_RXERR_PHY; phyerr = MS(ads.ds_rxstatus8, AR_PHYErrCode); rs->rs_phyerr = phyerr; - } - if (ads.ds_rxstatus8 & AR_DecryptCRCErr) + } else if (ads.ds_rxstatus8 & AR_DecryptCRCErr) rs->rs_status |= ATH9K_RXERR_DECRYPT; - if (ads.ds_rxstatus8 & AR_MichaelErr) + else if (ads.ds_rxstatus8 & AR_MichaelErr) rs->rs_status |= ATH9K_RXERR_MIC; + if (ads.ds_rxstatus8 & AR_KeyMiss) rs->rs_status |= ATH9K_RXERR_DECRYPT; } From 301f317a24ba923485c25d6fa27515f353ac0e1d Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 20 Apr 2011 18:02:45 +0300 Subject: [PATCH 1556/2556] UBIFS: fix false space checking failure commit 8c230d9a5b5ec7970139acb7e2d165d7a3fe9f9e upstream. This patch fixes UBIFS mount failure when the debugging support is enabled, we are recovering from a power cut, we were first mounter R/O and we are re-mounting R/W. In this case we should not assume that the amount of free space before we have re-mounted R/W and after are equivalent, because when we have mounted R/O the file-system is in a non-committed state so the amount of free space is slightly smaller, due to the fact that we cannot predict the amount of free space precisely before we commit. This patch fixes the issue by skipping the debugging check in case of recovery. This issue was reported by Caizhiyong here: http://thread.gmane.org/gmane.linux.drivers.mtd/34350/focus=34387 Signed-off-by: Artem Bityutskiy Reported-by: Caizhiyong Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/super.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 0f029e1732d1a..e94d9628a49e4 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1643,15 +1643,27 @@ static int ubifs_remount_rw(struct ubifs_info *c) if (err) goto out; + dbg_gen("re-mounted read-write"); + c->remounting_rw = 0; + if (c->need_recovery) { c->need_recovery = 0; ubifs_msg("deferred recovery completed"); + } else { + /* + * Do not run the debugging space check if the were doing + * recovery, because when we saved the information we had the + * file-system in a state where the TNC and lprops has been + * modified in memory, but all the I/O operations (including a + * commit) were deferred. So the file-system was in + * "non-committed" state. Now the file-system is in committed + * state, and of course the amount of free space will change + * because, for example, the old index size was imprecise. + */ + err = dbg_check_space_info(c); } - dbg_gen("re-mounted read-write"); - c->remounting_rw = 0; c->always_chk_crc = 0; - err = dbg_check_space_info(c); mutex_unlock(&c->umount_mutex); return err; From 900cb1cd2975b5e4b4195dd15d61da819eb17381 Mon Sep 17 00:00:00 2001 From: Carsten Otte Date: Wed, 20 Apr 2011 10:15:36 +0200 Subject: [PATCH 1557/2556] kvm-390: Let kernel exit SIE instruction on work commit 9ff4cfb3fcfd48b49fdd9be7381b3be340853aa4 upstream. From: Christian Borntraeger This patch fixes the sie exit on interrupts. The low level interrupt handler returns to the PSW address in pt_regs and not to the PSW address in the lowcore. Without this fix a cpu bound guest might never leave guest state since the host interrupt handler would blindly return to the SIE instruction, even on need_resched and friends. Signed-off-by: Carsten Otte Signed-off-by: Christian Borntraeger Signed-off-by: Martin Schwidefsky Signed-off-by: Greg Kroah-Hartman --- arch/s390/kvm/sie64a.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/s390/kvm/sie64a.S b/arch/s390/kvm/sie64a.S index 7e9d30d567b0a..ab0e041ac54cf 100644 --- a/arch/s390/kvm/sie64a.S +++ b/arch/s390/kvm/sie64a.S @@ -48,10 +48,10 @@ sie_irq_handler: tm __TI_flags+7(%r2),_TIF_EXIT_SIE jz 0f larl %r2,sie_exit # work pending, leave sie - stg %r2,__LC_RETURN_PSW+8 + stg %r2,SPI_PSW+8(0,%r15) br %r14 0: larl %r2,sie_reenter # re-enter with guest id - stg %r2,__LC_RETURN_PSW+8 + stg %r2,SPI_PSW+8(0,%r15) 1: br %r14 /* From a86ef2a8987e7d8dde3144ac72ea07262d26ad91 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 20 Apr 2011 10:15:34 +0200 Subject: [PATCH 1558/2556] pfault: fix token handling commit e35c76cd47c244eaa7a74adaabde4d0a1cadb907 upstream. f6649a7e "[S390] cleanup lowcore access from external interrupts" changed handling of external interrupts. Instead of letting the external interrupt handlers accessing the per cpu lowcore the entry code of the kernel reads already all fields that are necessary and passes them to the handlers. The pfault interrupt handler was incorrectly converted. It tries to dereference a value which used to be a pointer to a lowcore field. After the conversion however it is not anymore the pointer to the field but its content. So instead of a dereference only a cast is needed to get the task pointer that caused the pfault. Fixes a NULL pointer dereference and a subsequent kernel crash: Unable to handle kernel pointer dereference at virtual kernel address (null) Oops: 0004 [#1] SMP Modules linked in: nfsd exportfs nfs lockd fscache nfs_acl auth_rpcgss sunrpc loop qeth_l3 qeth vmur ccwgroup ext3 jbd mbcache dm_mod dasd_eckd_mod dasd_diag_mod dasd_mod CPU: 0 Not tainted 2.6.38-2-s390x #1 Process cron (pid: 1106, task: 000000001f962f78, ksp: 000000001fa0f9d0) Krnl PSW : 0404200180000000 000000000002c03e (pfault_interrupt+0xa2/0x138) R:0 T:1 IO:0 EX:0 Key:0 M:1 W:0 P:0 AS:0 CC:2 PM:0 EA:3 Krnl GPRS: 0000000000000000 0000000000000001 0000000000000000 0000000000000001 000000001f962f78 0000000000518968 0000000090000002 000000001ff03280 0000000000000000 000000000064f000 000000001f962f78 0000000000002603 0000000006002603 0000000000000000 000000001ff7fe68 000000001ff7fe48 Krnl Code: 000000000002c036: 5820d010 l %r2,16(%r13) 000000000002c03a: 1832 lr %r3,%r2 000000000002c03c: 1a31 ar %r3,%r1 >000000000002c03e: ba23d010 cs %r2,%r3,16(%r13) 000000000002c042: a744fffc brc 4,2c03a 000000000002c046: a7290002 lghi %r2,2 000000000002c04a: e320d0000024 stg %r2,0(%r13) 000000000002c050: 07f0 bcr 15,%r0 Call Trace: ([<000000001f962f78>] 0x1f962f78) [<000000000001acda>] do_extint+0xf6/0x138 [<000000000039b6ca>] ext_no_vtime+0x30/0x34 [<000000007d706e04>] 0x7d706e04 Last Breaking-Event-Address: [<0000000000000000>] 0x0 For stable maintainers: the first kernel which contains this bug is 2.6.37. Reported-by: Stephen Powell Cc: Jonathan Nieder Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky Signed-off-by: Greg Kroah-Hartman --- arch/s390/mm/fault.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 2c57806c0858e..0f900c811cb6a 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -558,9 +558,9 @@ static void pfault_interrupt(unsigned int ext_int_code, * Get the token (= address of the task structure of the affected task). */ #ifdef CONFIG_64BIT - tsk = *(struct task_struct **) param64; + tsk = (struct task_struct *) param64; #else - tsk = *(struct task_struct **) param32; + tsk = (struct task_struct *) param32; #endif if (subcode & 0x0080) { From 28af5e21f2d2438994bcfeb60b200b9a62c55c6c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 26 Apr 2011 11:33:18 +0200 Subject: [PATCH 1559/2556] ACPI / PM: Avoid infinite recurrence while registering power resources commit 7bed50c5edf5cba8dd515a31191cbfb6065ddc85 upstream. There is at least one BIOS with a DSDT containing a power resource object with a _PR0 entry pointing back to that power resource. In consequence, while registering that power resource acpi_bus_get_power_flags() sees that it depends on itself and tries to register it again, which leads to an infinitely deep recurrence. This problem was introduced by commit bf325f9538d8c89312be305b9779e (ACPI / PM: Register power resource devices as soon as they are needed). To fix this problem use the observation that power resources cannot be power manageable and prevent acpi_bus_get_power_flags() from being called for power resource objects. References: https://bugzilla.kernel.org/show_bug.cgi?id=31872 Reported-and-tested-by: Pascal Dormeau Signed-off-by: Rafael J. Wysocki Acked-by: Len Brown Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/scan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index b99e624946074..8eee69faf235b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -944,6 +944,10 @@ static int acpi_bus_get_flags(struct acpi_device *device) if (ACPI_SUCCESS(status)) device->flags.lockable = 1; + /* Power resources cannot be power manageable. */ + if (device->device_type == ACPI_BUS_TYPE_POWER) + return 0; + /* Presence of _PS0|_PR0 indicates 'power manageable' */ status = acpi_get_handle(device->handle, "_PS0", &temp); if (ACPI_FAILURE(status)) From 43d69f6936ff8aae527e8a7d952c5048090f086a Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Tue, 19 Apr 2011 16:29:36 -0500 Subject: [PATCH 1560/2556] slub: fix panic with DISCONTIGMEM commit 4a5fa3590f09999f6db41bc386bce40848fa9f63 upstream. Slub makes assumptions about page_to_nid() which are violated by DISCONTIGMEM and !NUMA. This violation results in a panic because page_to_nid() can be non-zero for pages in the discontiguous ranges and this leads to a null return by get_node(). The assertion by the maintainer is that DISCONTIGMEM should only be allowed when NUMA is also defined. However, at least six architectures: alpha, ia64, m32r, m68k, mips, parisc violate this. The panic is a regression against slab, so just mark slub broken in the problem configuration to prevent users reporting these panics. Acked-by: David Rientjes Acked-by: Pekka Enberg Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- init/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/init/Kconfig b/init/Kconfig index ce8838396fdf2..bd4d421a3aa48 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1224,6 +1224,7 @@ config SLAB per cpu and per node queues. config SLUB + depends on BROKEN || NUMA || !DISCONTIGMEM bool "SLUB (Unqueued Allocator)" help SLUB is a slab allocator that minimizes cache line usage From 59818dbfa175b981d8a99672efd7c2b66eecac82 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Wed, 20 Apr 2011 19:27:13 -0700 Subject: [PATCH 1561/2556] set memory ranges in N_NORMAL_MEMORY when onlined commit d9b41e0b54fd7e164daf1e9c539c1070398aa02e upstream. When a DISCONTIGMEM memory range is brought online as a NUMA node, it also needs to have its bet set in N_NORMAL_MEMORY. This is necessary for generic kernel code that utilizes N_NORMAL_MEMORY as a subset of N_ONLINE for memory savings. These types of hacks can hopefully be removed once DISCONTIGMEM is either removed or abstracted away from CONFIG_NUMA. Fixes a panic in the slub code which only initializes structures for N_NORMAL_MEMORY to save memory: Backtrace: [<000000004021c938>] add_partial+0x28/0x98 [<000000004021faa0>] __slab_free+0x1d0/0x1d8 [<000000004021fd04>] kmem_cache_free+0xc4/0x128 [<000000004033bf9c>] ida_get_new_above+0x21c/0x2c0 [<00000000402a8980>] sysfs_new_dirent+0xd0/0x238 [<00000000402a974c>] create_dir+0x5c/0x168 [<00000000402a9ab0>] sysfs_create_dir+0x98/0x128 [<000000004033d6c4>] kobject_add_internal+0x114/0x258 [<000000004033d9ac>] kobject_add_varg+0x7c/0xa0 [<000000004033df20>] kobject_add+0x50/0x90 [<000000004033dfb4>] kobject_create_and_add+0x54/0xc8 [<00000000407862a0>] cgroup_init+0x138/0x1f0 [<000000004077ce50>] start_kernel+0x5a0/0x840 [<000000004011fa3c>] start_parisc+0xa4/0xb8 [<00000000404bb034>] packet_ioctl+0x16c/0x208 [<000000004049ac30>] ip_mroute_setsockopt+0x260/0xf20 Signed-off-by: David Rientjes Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- arch/parisc/mm/init.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index f4f4d700833af..7fd8aadd8a8ee 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -266,8 +266,10 @@ static void __init setup_bootmem(void) } memset(pfnnid_map, 0xff, sizeof(pfnnid_map)); - for (i = 0; i < npmem_ranges; i++) + for (i = 0; i < npmem_ranges; i++) { + node_set_state(i, N_NORMAL_MEMORY); node_set_online(i); + } #endif /* From ecc20f3946b47db94d19a158856c6c2c6c9aca14 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Sun, 3 Apr 2011 12:40:24 -0300 Subject: [PATCH 1562/2556] FLEXCOP-PCI: fix __xlate_proc_name-warning for flexcop-pci commit b934c20de1398d4a82d2ecfeb588a214a910f13f upstream. This patch fixes the warning about bad names for sys-fs and other kernel-things. The flexcop-pci driver was using '/'-characters in it, which is not good. This has been fixed in several attempts by several people, but obviously never made it into the kernel. Signed-off-by: Patrick Boettcher Cc: Steffen Barszus Cc: Boris Cuber Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/dvb/b2c2/flexcop-pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/b2c2/flexcop-pci.c b/drivers/media/dvb/b2c2/flexcop-pci.c index 227c0200b70a5..4f3e3ceaa7c90 100644 --- a/drivers/media/dvb/b2c2/flexcop-pci.c +++ b/drivers/media/dvb/b2c2/flexcop-pci.c @@ -38,7 +38,7 @@ MODULE_PARM_DESC(debug, DEBSTATUS); #define DRIVER_VERSION "0.1" -#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV PCI Driver" +#define DRIVER_NAME "flexcop-pci" #define DRIVER_AUTHOR "Patrick Boettcher " struct flexcop_pci { From b1d6cb1b6a3879937c2000f72dfdadf7da28f89b Mon Sep 17 00:00:00 2001 From: Amit Shah Date: Mon, 14 Mar 2011 17:45:48 +0530 Subject: [PATCH 1563/2556] virtio: console: Enable call to hvc_remove() on console port remove commit afa2689e19073cd2e762d0f2c1358fab1ab9f18c upstream. This call was disabled as hot-unplugging one virtconsole port led to another virtconsole port freezing. Upon testing it again, this now works, so enable it. In addition, a bug was found in qemu wherein removing a port of one type caused the guest output from another port to stop working. I doubt it was just this bug that caused it (since disabling the hvc_remove() call did allow other ports to continue working), but since it's all solved now, we're fine with hot-unplugging of virtconsole ports. Signed-off-by: Amit Shah Signed-off-by: Rusty Russell Signed-off-by: Greg Kroah-Hartman --- drivers/char/virtio_console.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 84b164d1eb2b1..838568a7dbf56 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1280,18 +1280,7 @@ static void unplug_port(struct port *port) spin_lock_irq(&pdrvdata_lock); list_del(&port->cons.list); spin_unlock_irq(&pdrvdata_lock); -#if 0 - /* - * hvc_remove() not called as removing one hvc port - * results in other hvc ports getting frozen. - * - * Once this is resolved in hvc, this functionality - * will be enabled. Till that is done, the -EPIPE - * return from get_chars() above will help - * hvc_console.c to clean up on ports we remove here. - */ hvc_remove(port->cons.hvc); -#endif } /* Remove unused data this port might have received. */ From 4b174e2ae1e8d88015d230683b4f7c3130dbc085 Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Wed, 27 Apr 2011 15:26:50 -0700 Subject: [PATCH 1564/2556] oom: use pte pages in OOM score commit f755a042d82b51b54f3bdd0890e5ea56c0fb6807 upstream. PTE pages eat up memory just like anything else, but we do not account for them in any way in the OOM scores. They are also _guaranteed_ to get freed up when a process is OOM killed, while RSS is not. Reported-by: Dave Hansen Signed-off-by: KOSAKI Motohiro Cc: Hugh Dickins Cc: KAMEZAWA Hiroyuki Cc: Oleg Nesterov Acked-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/oom_kill.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mm/oom_kill.c b/mm/oom_kill.c index ea16f720705ed..49ea0cc9ae129 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -172,10 +172,13 @@ unsigned int oom_badness(struct task_struct *p, struct mem_cgroup *mem, /* * The baseline for the badness score is the proportion of RAM that each - * task's rss and swap space use. + * task's rss, pagetable and swap space use. */ - points = (get_mm_rss(p->mm) + get_mm_counter(p->mm, MM_SWAPENTS)) * 1000 / - totalpages; + points = get_mm_rss(p->mm) + p->mm->nr_ptes; + points += get_mm_counter(p->mm, MM_SWAPENTS); + + points *= 1000; + points /= totalpages; task_unlock(p); /* From bc1db5dfd436b42216e5a3693670efc30c9ea815 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 27 Apr 2011 15:26:56 -0700 Subject: [PATCH 1565/2556] mm: check if PTE is already allocated during page fault commit cc03638df20acbec5d0d0d9e07234aadde9e698d upstream. With transparent hugepage support, handle_mm_fault() has to be careful that a normal PMD has been established before handling a PTE fault. To achieve this, it used __pte_alloc() directly instead of pte_alloc_map as pte_alloc_map is unsafe to run against a huge PMD. pte_offset_map() is called once it is known the PMD is safe. pte_alloc_map() is smart enough to check if a PTE is already present before calling __pte_alloc but this check was lost. As a consequence, PTEs may be allocated unnecessarily and the page table lock taken. Thi useless PTE does get cleaned up but it's a performance hit which is visible in page_test from aim9. This patch simply re-adds the check normally done by pte_alloc_map to check if the PTE needs to be allocated before taking the page table lock. The effect is noticable in page_test from aim9. AIM9 2.6.38-vanilla 2.6.38-checkptenone creat-clo 446.10 ( 0.00%) 424.47 (-5.10%) page_test 38.10 ( 0.00%) 42.04 ( 9.37%) brk_test 52.45 ( 0.00%) 51.57 (-1.71%) exec_test 382.00 ( 0.00%) 456.90 (16.39%) fork_test 60.11 ( 0.00%) 67.79 (11.34%) MMTests Statistics: duration Total Elapsed Time (seconds) 611.90 612.22 (While this affects 2.6.38, it is a performance rather than a functional bug and normally outside the rules -stable. While the big performance differences are to a microbench, the difference in fork and exec performance may be significant enough that -stable wants to consider the patch) Reported-by: Raz Ben Yehuda Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Reviewed-by: Andrea Arcangeli Reviewed-by: Minchan Kim Acked-by: Johannes Weiner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/memory.c b/mm/memory.c index f17746a5acb13..ab88d09c4a817 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3332,7 +3332,7 @@ int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma, * run pte_offset_map on the pmd, if an huge pmd could * materialize from under us from a different thread. */ - if (unlikely(__pte_alloc(mm, vma, pmd, address))) + if (unlikely(pmd_none(*pmd)) && __pte_alloc(mm, vma, pmd, address)) return VM_FAULT_OOM; /* if an huge pmd materialized from under us just retry later */ if (unlikely(pmd_trans_huge(*pmd))) From 1032ecc77ce36abc865d5eceff906febbe6dde37 Mon Sep 17 00:00:00 2001 From: Andrea Arcangeli Date: Wed, 27 Apr 2011 15:26:45 -0700 Subject: [PATCH 1566/2556] mm: thp: fix /dev/zero MAP_PRIVATE and vm_flags cleanups commit 78f11a255749d09025f54d4e2df4fbcb031530e2 upstream. The huge_memory.c THP page fault was allowed to run if vm_ops was null (which would succeed for /dev/zero MAP_PRIVATE, as the f_op->mmap wouldn't setup a special vma->vm_ops and it would fallback to regular anonymous memory) but other THP logics weren't fully activated for vmas with vm_file not NULL (/dev/zero has a not NULL vma->vm_file). So this removes the vm_file checks so that /dev/zero also can safely use THP (the other albeit safer approach to fix this bug would have been to prevent the THP initial page fault to run if vm_file was set). After removing the vm_file checks, this also makes huge_memory.c stricter in khugepaged for the DEBUG_VM=y case. It doesn't replace the vm_file check with a is_pfn_mapping check (but it keeps checking for VM_PFNMAP under VM_BUG_ON) because for a is_cow_mapping() mapping VM_PFNMAP should only be allowed to exist before the first page fault, and in turn when vma->anon_vma is null (so preventing khugepaged registration). So I tend to think the previous comment saying if vm_file was set, VM_PFNMAP might have been set and we could still be registered in khugepaged (despite anon_vma was not NULL to be registered in khugepaged) was too paranoid. The is_linear_pfn_mapping check is also I think superfluous (as described by comment) but under DEBUG_VM it is safe to stay. Addresses https://bugzilla.kernel.org/show_bug.cgi?id=33682 Signed-off-by: Andrea Arcangeli Reported-by: Caspar Zhang Acked-by: Mel Gorman Acked-by: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- include/linux/huge_mm.h | 2 +- include/linux/mm.h | 3 ++- mm/huge_memory.c | 43 +++++++++++++++++++++++------------------ 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h index df29c8fde36be..8847c8c29791c 100644 --- a/include/linux/huge_mm.h +++ b/include/linux/huge_mm.h @@ -117,7 +117,7 @@ static inline void vma_adjust_trans_huge(struct vm_area_struct *vma, unsigned long end, long adjust_next) { - if (!vma->anon_vma || vma->vm_ops || vma->vm_file) + if (!vma->anon_vma || vma->vm_ops) return; __vma_adjust_trans_huge(vma, start, end, adjust_next); } diff --git a/include/linux/mm.h b/include/linux/mm.h index 25b6461ecaa5a..91e5b1ee7357d 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -137,7 +137,8 @@ extern unsigned int kobjsize(const void *objp); #define VM_RandomReadHint(v) ((v)->vm_flags & VM_RAND_READ) /* - * special vmas that are non-mergable, non-mlock()able + * Special vmas that are non-mergable, non-mlock()able. + * Note: mm/huge_memory.c VM_NO_THP depends on this definition. */ #define VM_SPECIAL (VM_IO | VM_DONTEXPAND | VM_RESERVED | VM_PFNMAP) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 8f76561944178..56cac93f155d1 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1400,6 +1400,9 @@ int split_huge_page(struct page *page) return ret; } +#define VM_NO_THP (VM_SPECIAL|VM_INSERTPAGE|VM_MIXEDMAP|VM_SAO| \ + VM_HUGETLB|VM_SHARED|VM_MAYSHARE) + int hugepage_madvise(struct vm_area_struct *vma, unsigned long *vm_flags, int advice) { @@ -1408,11 +1411,7 @@ int hugepage_madvise(struct vm_area_struct *vma, /* * Be somewhat over-protective like KSM for now! */ - if (*vm_flags & (VM_HUGEPAGE | - VM_SHARED | VM_MAYSHARE | - VM_PFNMAP | VM_IO | VM_DONTEXPAND | - VM_RESERVED | VM_HUGETLB | VM_INSERTPAGE | - VM_MIXEDMAP | VM_SAO)) + if (*vm_flags & (VM_HUGEPAGE | VM_NO_THP)) return -EINVAL; *vm_flags &= ~VM_NOHUGEPAGE; *vm_flags |= VM_HUGEPAGE; @@ -1428,11 +1427,7 @@ int hugepage_madvise(struct vm_area_struct *vma, /* * Be somewhat over-protective like KSM for now! */ - if (*vm_flags & (VM_NOHUGEPAGE | - VM_SHARED | VM_MAYSHARE | - VM_PFNMAP | VM_IO | VM_DONTEXPAND | - VM_RESERVED | VM_HUGETLB | VM_INSERTPAGE | - VM_MIXEDMAP | VM_SAO)) + if (*vm_flags & (VM_NOHUGEPAGE | VM_NO_THP)) return -EINVAL; *vm_flags &= ~VM_HUGEPAGE; *vm_flags |= VM_NOHUGEPAGE; @@ -1566,10 +1561,14 @@ int khugepaged_enter_vma_merge(struct vm_area_struct *vma) * page fault if needed. */ return 0; - if (vma->vm_file || vma->vm_ops) + if (vma->vm_ops) /* khugepaged not yet working on file or special mappings */ return 0; - VM_BUG_ON(is_linear_pfn_mapping(vma) || is_pfn_mapping(vma)); + /* + * If is_pfn_mapping() is true is_learn_pfn_mapping() must be + * true too, verify it here. + */ + VM_BUG_ON(is_linear_pfn_mapping(vma) || vma->vm_flags & VM_NO_THP); hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK; hend = vma->vm_end & HPAGE_PMD_MASK; if (hstart < hend) @@ -1818,12 +1817,15 @@ static void collapse_huge_page(struct mm_struct *mm, (vma->vm_flags & VM_NOHUGEPAGE)) goto out; - /* VM_PFNMAP vmas may have vm_ops null but vm_file set */ - if (!vma->anon_vma || vma->vm_ops || vma->vm_file) + if (!vma->anon_vma || vma->vm_ops) goto out; if (is_vma_temporary_stack(vma)) goto out; - VM_BUG_ON(is_linear_pfn_mapping(vma) || is_pfn_mapping(vma)); + /* + * If is_pfn_mapping() is true is_learn_pfn_mapping() must be + * true too, verify it here. + */ + VM_BUG_ON(is_linear_pfn_mapping(vma) || vma->vm_flags & VM_NO_THP); pgd = pgd_offset(mm, address); if (!pgd_present(*pgd)) @@ -2056,13 +2058,16 @@ static unsigned int khugepaged_scan_mm_slot(unsigned int pages, progress++; continue; } - /* VM_PFNMAP vmas may have vm_ops null but vm_file set */ - if (!vma->anon_vma || vma->vm_ops || vma->vm_file) + if (!vma->anon_vma || vma->vm_ops) goto skip; if (is_vma_temporary_stack(vma)) goto skip; - - VM_BUG_ON(is_linear_pfn_mapping(vma) || is_pfn_mapping(vma)); + /* + * If is_pfn_mapping() is true is_learn_pfn_mapping() + * must be true too, verify it here. + */ + VM_BUG_ON(is_linear_pfn_mapping(vma) || + vma->vm_flags & VM_NO_THP); hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK; hend = vma->vm_end & HPAGE_PMD_MASK; From f00c65086bfe6f69bb99fc62c2b41df9c3bee613 Mon Sep 17 00:00:00 2001 From: Michael Schmitz Date: Tue, 26 Apr 2011 14:51:53 +1200 Subject: [PATCH 1567/2556] m68k/mm: Set all online nodes in N_NORMAL_MEMORY commit 4aac0b4815ba592052758f4b468f253d383dc9d6 upstream. For m68k, N_NORMAL_MEMORY represents all nodes that have present memory since it does not support HIGHMEM. This patch sets the bit at the time node_present_pages has been set by free_area_init_node. At the time the node is brought online, the node state would have to be done unconditionally since information about present memory has not yet been recorded. If N_NORMAL_MEMORY is not accurate, slub may encounter errors since it uses this nodemask to setup per-cache kmem_cache_node data structures. This pach is an alternative to the one proposed by David Rientjes attempting to set node state immediately when bringing the node online. Signed-off-by: Michael Schmitz Tested-by: Thorsten Glaser Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- arch/m68k/mm/motorola.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/m68k/mm/motorola.c b/arch/m68k/mm/motorola.c index 02b7a03e42268..8b3db1c587fca 100644 --- a/arch/m68k/mm/motorola.c +++ b/arch/m68k/mm/motorola.c @@ -300,6 +300,8 @@ void __init paging_init(void) zones_size[ZONE_DMA] = m68k_memory[i].size >> PAGE_SHIFT; free_area_init_node(i, zones_size, m68k_memory[i].addr >> PAGE_SHIFT, NULL); + if (node_present_pages(i)) + node_set_state(i, N_NORMAL_MEMORY); } } From a59e1f33d21d869205f2f0449db77f7b67028d39 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 27 Apr 2011 15:26:41 -0700 Subject: [PATCH 1568/2556] vfs: avoid large kmalloc()s for the fdtable commit 6d4831c283530a5f2c6bd8172c13efa236eb149d upstream. Azurit reports large increases in system time after 2.6.36 when running Apache. It was bisected down to a892e2d7dcdfa6c76e6 ("vfs: use kmalloc() to allocate fdmem if possible"). That patch caused the vfs to use kmalloc() for very large allocations and this is causing excessive work (and presumably excessive reclaim) within the page allocator. Fix it by falling back to vmalloc() earlier - when the allocation attempt would have been considered "costly" by reclaim. Reported-by: azurIt Tested-by: azurIt Acked-by: Changli Gao Cc: Americo Wang Cc: Jiri Slaby Acked-by: Eric Dumazet Cc: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/file.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/fs/file.c b/fs/file.c index 0be344755c020..4c6992d8f3ba1 100644 --- a/fs/file.c +++ b/fs/file.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -39,14 +40,17 @@ int sysctl_nr_open_max = 1024 * 1024; /* raised later */ */ static DEFINE_PER_CPU(struct fdtable_defer, fdtable_defer_list); -static inline void *alloc_fdmem(unsigned int size) +static void *alloc_fdmem(unsigned int size) { - void *data; - - data = kmalloc(size, GFP_KERNEL|__GFP_NOWARN); - if (data != NULL) - return data; - + /* + * Very large allocations can stress page reclaim, so fall back to + * vmalloc() if the allocation size will be considered "large" by the VM. + */ + if (size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER)) { + void *data = kmalloc(size, GFP_KERNEL|__GFP_NOWARN); + if (data != NULL) + return data; + } return vmalloc(size); } From d8a540b4ae9ed6fe089affb0389eef0593ab4a4a Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 27 Apr 2011 11:49:09 -0400 Subject: [PATCH 1569/2556] nfs: don't lose MS_SYNCHRONOUS on remount of noac mount commit 26c4c170731f00008f4317a2888a0a07ac99d90d upstream. On a remount, the VFS layer will clear the MS_SYNCHRONOUS bit on the assumption that the flags on the mount syscall will have it set if the remounted fs is supposed to keep it. In the case of "noac" though, MS_SYNCHRONOUS is implied. A remount of such a mount will lose the MS_SYNCHRONOUS flag since "sync" isn't part of the mount options. Reported-by: Max Matveev Signed-off-by: Jeff Layton Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- fs/nfs/super.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index b68c8607770fc..6a2ec5043a0c3 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2077,6 +2077,15 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data) if (error < 0) goto out; + /* + * noac is a special case. It implies -o sync, but that's not + * necessarily reflected in the mtab options. do_remount_sb + * will clear MS_SYNCHRONOUS if -o sync wasn't specified in the + * remount options, so we have to explicitly reset it. + */ + if (data->flags & NFS_MOUNT_NOAC) + *flags |= MS_SYNCHRONOUS; + /* compare new mount options with old ones */ error = nfs_compare_remount_data(nfss, data); out: From 61b5fea3a4fe2185455c019133678f987c0aac48 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 15 Apr 2011 17:34:18 -0400 Subject: [PATCH 1570/2556] NFSv4.1: Ensure state manager thread dies on last umount commit 47c2199b6eb5fbe38ddb844db7cdbd914d304f9c upstream. Currently, the state manager may continue to try recovering state forever even after the last filesystem to reference that nfs_client has umounted. Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- fs/nfs/nfs4state.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 0592288f9f067..6221640397dd2 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1600,7 +1600,7 @@ static void nfs4_state_manager(struct nfs_client *clp) int status = 0; /* Ensure exclusive access to NFSv4 state */ - for(;;) { + do { if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) { /* We're going to have to re-establish a clientid */ status = nfs4_reclaim_lease(clp); @@ -1684,7 +1684,7 @@ static void nfs4_state_manager(struct nfs_client *clp) break; if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0) break; - } + } while (atomic_read(&clp->cl_count) > 1); return; out_error: printk(KERN_WARNING "Error: state manager failed on NFSv4 server %s" From 6ee5614d3a0049c3cf263419cb6a6516d23ef403 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Wed, 27 Apr 2011 15:26:51 -0700 Subject: [PATCH 1571/2556] um: mdd support for 64 bit atomic operations commit 57d8e02e3cd21bccf2b84b26b42feb79e1f0f83e upstream. This adds support for 64 bit atomic operations on 32 bit UML systems. XFS needs them since 2.6.38. $ make ARCH=um SUBARCH=i386 ... LD .tmp_vmlinux1 fs/built-in.o: In function `xlog_regrant_reserve_log_space': xfs_log.c:(.text+0xd8584): undefined reference to `atomic64_read_386' xfs_log.c:(.text+0xd85ac): undefined reference to `cmpxchg8b_emu' ... Addresses https://bugzilla.kernel.org/show_bug.cgi?id=32812 Reported-by: Martin Walch Tested-by: Martin Walch Cc: Martin Walch Signed-off-by: Richard Weinberger Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- arch/um/sys-i386/Makefile | 2 +- arch/um/sys-i386/atomic64_cx8_32.S | 225 +++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 arch/um/sys-i386/atomic64_cx8_32.S diff --git a/arch/um/sys-i386/Makefile b/arch/um/sys-i386/Makefile index 804b28dd0328a..b1da91c1b200d 100644 --- a/arch/um/sys-i386/Makefile +++ b/arch/um/sys-i386/Makefile @@ -4,7 +4,7 @@ obj-y = bug.o bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \ ptrace_user.o setjmp.o signal.o stub.o stub_segv.o syscalls.o sysrq.o \ - sys_call_table.o tls.o + sys_call_table.o tls.o atomic64_cx8_32.o obj-$(CONFIG_BINFMT_ELF) += elfcore.o diff --git a/arch/um/sys-i386/atomic64_cx8_32.S b/arch/um/sys-i386/atomic64_cx8_32.S new file mode 100644 index 0000000000000..1e901d3d4a956 --- /dev/null +++ b/arch/um/sys-i386/atomic64_cx8_32.S @@ -0,0 +1,225 @@ +/* + * atomic64_t for 586+ + * + * Copied from arch/x86/lib/atomic64_cx8_32.S + * + * Copyright © 2010 Luca Barbieri + * + * 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. + * + */ + +#include +#include +#include + +.macro SAVE reg + pushl_cfi %\reg + CFI_REL_OFFSET \reg, 0 +.endm + +.macro RESTORE reg + popl_cfi %\reg + CFI_RESTORE \reg +.endm + +.macro read64 reg + movl %ebx, %eax + movl %ecx, %edx +/* we need LOCK_PREFIX since otherwise cmpxchg8b always does the write */ + LOCK_PREFIX + cmpxchg8b (\reg) +.endm + +ENTRY(atomic64_read_cx8) + CFI_STARTPROC + + read64 %ecx + ret + CFI_ENDPROC +ENDPROC(atomic64_read_cx8) + +ENTRY(atomic64_set_cx8) + CFI_STARTPROC + +1: +/* we don't need LOCK_PREFIX since aligned 64-bit writes + * are atomic on 586 and newer */ + cmpxchg8b (%esi) + jne 1b + + ret + CFI_ENDPROC +ENDPROC(atomic64_set_cx8) + +ENTRY(atomic64_xchg_cx8) + CFI_STARTPROC + + movl %ebx, %eax + movl %ecx, %edx +1: + LOCK_PREFIX + cmpxchg8b (%esi) + jne 1b + + ret + CFI_ENDPROC +ENDPROC(atomic64_xchg_cx8) + +.macro addsub_return func ins insc +ENTRY(atomic64_\func\()_return_cx8) + CFI_STARTPROC + SAVE ebp + SAVE ebx + SAVE esi + SAVE edi + + movl %eax, %esi + movl %edx, %edi + movl %ecx, %ebp + + read64 %ebp +1: + movl %eax, %ebx + movl %edx, %ecx + \ins\()l %esi, %ebx + \insc\()l %edi, %ecx + LOCK_PREFIX + cmpxchg8b (%ebp) + jne 1b + +10: + movl %ebx, %eax + movl %ecx, %edx + RESTORE edi + RESTORE esi + RESTORE ebx + RESTORE ebp + ret + CFI_ENDPROC +ENDPROC(atomic64_\func\()_return_cx8) +.endm + +addsub_return add add adc +addsub_return sub sub sbb + +.macro incdec_return func ins insc +ENTRY(atomic64_\func\()_return_cx8) + CFI_STARTPROC + SAVE ebx + + read64 %esi +1: + movl %eax, %ebx + movl %edx, %ecx + \ins\()l $1, %ebx + \insc\()l $0, %ecx + LOCK_PREFIX + cmpxchg8b (%esi) + jne 1b + +10: + movl %ebx, %eax + movl %ecx, %edx + RESTORE ebx + ret + CFI_ENDPROC +ENDPROC(atomic64_\func\()_return_cx8) +.endm + +incdec_return inc add adc +incdec_return dec sub sbb + +ENTRY(atomic64_dec_if_positive_cx8) + CFI_STARTPROC + SAVE ebx + + read64 %esi +1: + movl %eax, %ebx + movl %edx, %ecx + subl $1, %ebx + sbb $0, %ecx + js 2f + LOCK_PREFIX + cmpxchg8b (%esi) + jne 1b + +2: + movl %ebx, %eax + movl %ecx, %edx + RESTORE ebx + ret + CFI_ENDPROC +ENDPROC(atomic64_dec_if_positive_cx8) + +ENTRY(atomic64_add_unless_cx8) + CFI_STARTPROC + SAVE ebp + SAVE ebx +/* these just push these two parameters on the stack */ + SAVE edi + SAVE esi + + movl %ecx, %ebp + movl %eax, %esi + movl %edx, %edi + + read64 %ebp +1: + cmpl %eax, 0(%esp) + je 4f +2: + movl %eax, %ebx + movl %edx, %ecx + addl %esi, %ebx + adcl %edi, %ecx + LOCK_PREFIX + cmpxchg8b (%ebp) + jne 1b + + movl $1, %eax +3: + addl $8, %esp + CFI_ADJUST_CFA_OFFSET -8 + RESTORE ebx + RESTORE ebp + ret +4: + cmpl %edx, 4(%esp) + jne 2b + xorl %eax, %eax + jmp 3b + CFI_ENDPROC +ENDPROC(atomic64_add_unless_cx8) + +ENTRY(atomic64_inc_not_zero_cx8) + CFI_STARTPROC + SAVE ebx + + read64 %esi +1: + testl %eax, %eax + je 4f +2: + movl %eax, %ebx + movl %edx, %ecx + addl $1, %ebx + adcl $0, %ecx + LOCK_PREFIX + cmpxchg8b (%esi) + jne 1b + + movl $1, %eax +3: + RESTORE ebx + ret +4: + testl %edx, %edx + jne 2b + jmp 3b + CFI_ENDPROC +ENDPROC(atomic64_inc_not_zero_cx8) From c6ef46e0daf5ddec1710ef112e8f668cc4053a7c Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 22 Apr 2011 07:51:33 +1000 Subject: [PATCH 1572/2556] drm: select FRAMEBUFFER_CONSOLE_PRIMARY if we have FRAMEBUFFER_CONSOLE commit bf5192edcbc1f0a7f9c054649dbf1a0b3210d9b7 upstream. Multi-gpu/switcheroo relies on this option to get the console on the correct GPU at bootup, some distros enable it but it seems some get it wrong. Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 0902d44600394..4b4b5455b00ff 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -24,6 +24,7 @@ config DRM_KMS_HELPER depends on DRM select FB select FRAMEBUFFER_CONSOLE if !EXPERT + select FRAMEBUFFER_CONSOLE_DETECT_PRIMARY if FRAMEBUFFER_CONSOLE help FB and CRTC helpers for KMS drivers. From 6d0508e22c62df30c2dee6652f1fcddd39ec92e4 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Thu, 14 Apr 2011 20:55:16 +0400 Subject: [PATCH 1573/2556] agp: fix arbitrary kernel memory writes commit 194b3da873fd334ef183806db751473512af29ce upstream. pg_start is copied from userspace on AGPIOC_BIND and AGPIOC_UNBIND ioctl cmds of agp_ioctl() and passed to agpioc_bind_wrap(). As said in the comment, (pg_start + mem->page_count) may wrap in case of AGPIOC_BIND, and it is not checked at all in case of AGPIOC_UNBIND. As a result, user with sufficient privileges (usually "video" group) may generate either local DoS or privilege escalation. Signed-off-by: Vasiliy Kulikov Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/char/agp/generic.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 012cba0d6d965..745e7baff3c9c 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -1089,8 +1089,8 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) return -EINVAL; } - /* AK: could wrap */ - if ((pg_start + mem->page_count) > num_entries) + if (((pg_start + mem->page_count) > num_entries) || + ((pg_start + mem->page_count) < pg_start)) return -EINVAL; j = pg_start; @@ -1124,7 +1124,7 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) { size_t i; struct agp_bridge_data *bridge; - int mask_type; + int mask_type, num_entries; bridge = mem->bridge; if (!bridge) @@ -1136,6 +1136,11 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) if (type != mem->type) return -EINVAL; + num_entries = agp_num_entries(); + if (((pg_start + mem->page_count) > num_entries) || + ((pg_start + mem->page_count) < pg_start)) + return -EINVAL; + mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); if (mask_type != 0) { /* The generic routines know nothing of memory types */ From 5d1a8d0a34f407a3a217e2c1bca249a591a7a2f6 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Thu, 14 Apr 2011 20:55:19 +0400 Subject: [PATCH 1574/2556] agp: fix OOM and buffer overflow commit b522f02184b413955f3bc952e3776ce41edc6355 upstream. page_count is copied from userspace. agp_allocate_memory() tries to check whether this number is too big, but doesn't take into account the wrap case. Also agp_create_user_memory() doesn't check whether alloc_size is calculated from num_agp_pages variable without overflow. This may lead to allocation of too small buffer with following buffer overflow. Another problem in agp code is not addressed in the patch - kernel memory exhaustion (AGPIOC_RESERVE and AGPIOC_ALLOCATE ioctls). It is not checked whether requested pid is a pid of the caller (no check in agpioc_reserve_wrap()). Each allocation is limited to 16KB, though, there is no per-process limit. This might lead to OOM situation, which is not even solved in case of the caller death by OOM killer - the memory is allocated for another (faked) process. Signed-off-by: Vasiliy Kulikov Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/char/agp/generic.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 745e7baff3c9c..b072648dc3f64 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -115,6 +115,9 @@ static struct agp_memory *agp_create_user_memory(unsigned long num_agp_pages) struct agp_memory *new; unsigned long alloc_size = num_agp_pages*sizeof(struct page *); + if (INT_MAX/sizeof(struct page *) < num_agp_pages) + return NULL; + new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL); if (new == NULL) return NULL; @@ -234,11 +237,14 @@ struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge, int scratch_pages; struct agp_memory *new; size_t i; + int cur_memory; if (!bridge) return NULL; - if ((atomic_read(&bridge->current_memory_agp) + page_count) > bridge->max_memory_agp) + cur_memory = atomic_read(&bridge->current_memory_agp); + if ((cur_memory + page_count > bridge->max_memory_agp) || + (cur_memory + page_count < page_count)) return NULL; if (type >= AGP_USER_TYPES) { From ebacf2eb419940c5cb7209c491e74060bc0fdccd Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 28 Jan 2011 16:47:44 +0100 Subject: [PATCH 1575/2556] iwlwifi: do not set tx power when channel is changing commit f844a709a7d8f8be61a571afc31dfaca9e779621 upstream. Mac80211 can request for tx power and channel change in one ->config call. If that happens, *_send_tx_power functions will try to setup tx power for old channel, what can be not correct because we already change the band. I.e error "Failed to get channel info for channel 140 [0]", can be printed frequently when operating in software scanning mode. Signed-off-by: Stanislaw Gruszka Acked-by: Wey-Yi Guy Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl-3945.c | 2 +- drivers/net/wireless/iwlwifi/iwl-4965.c | 2 +- drivers/net/wireless/iwlwifi/iwl-agn-rxon.c | 5 ++--- drivers/net/wireless/iwlwifi/iwl-core.c | 13 ++++++++++--- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index 39b6f16c87fae..4e7b58bb1a1b5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -1823,7 +1823,7 @@ int iwl3945_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) /* If we issue a new RXON command which required a tune then we must * send a new TXPOWER command or we won't be able to Tx any frames */ - rc = priv->cfg->ops->lib->send_tx_power(priv); + rc = iwl_set_tx_power(priv, priv->tx_power_next, true); if (rc) { IWL_ERR(priv, "Error setting Tx power (%d).\n", rc); return rc; diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 91a9f52534699..992caa0231d06 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -1571,7 +1571,7 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *c /* If we issue a new RXON command which required a tune then we must * send a new TXPOWER command or we won't be able to Tx any frames */ - ret = iwl_set_tx_power(priv, priv->tx_power_user_lmt, true); + ret = iwl_set_tx_power(priv, priv->tx_power_next, true); if (ret) { IWL_ERR(priv, "Error sending TX power (%d)\n", ret); return ret; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c index 6d140bd532918..ee802fe0e4c45 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c @@ -288,10 +288,9 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) * If we issue a new RXON command which required a tune then we must * send a new TXPOWER command or we won't be able to Tx any frames. * - * FIXME: which RXON requires a tune? Can we optimise this out in - * some cases? + * It's expected we set power here if channel is changing. */ - ret = iwl_set_tx_power(priv, priv->tx_power_user_lmt, true); + ret = iwl_set_tx_power(priv, priv->tx_power_next, true); if (ret) { IWL_ERR(priv, "Error sending TX power (%d)\n", ret); return ret; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index efbde1f1a8bfc..91cac6fb41fd6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -1161,6 +1161,8 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) { int ret; s8 prev_tx_power; + bool defer; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; lockdep_assert_held(&priv->mutex); @@ -1188,10 +1190,15 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) if (!iwl_is_ready_rf(priv)) return -EIO; - /* scan complete use tx_power_next, need to be updated */ + /* scan complete and commit_rxon use tx_power_next value, + * it always need to be updated for newest request */ priv->tx_power_next = tx_power; - if (test_bit(STATUS_SCANNING, &priv->status) && !force) { - IWL_DEBUG_INFO(priv, "Deferring tx power set while scanning\n"); + + /* do not set tx power when scanning or channel changing */ + defer = test_bit(STATUS_SCANNING, &priv->status) || + memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging)); + if (defer && !force) { + IWL_DEBUG_INFO(priv, "Deferring tx power set\n"); return 0; } From 1c3918ccbc8e1dbb205697c76ef64a608d8f79b0 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Mon, 14 Mar 2011 14:15:06 +0100 Subject: [PATCH 1576/2556] iwl3945: do not deprecate software scan commit 3bda50e3eaf58a4b9c4ce34204e5faa15c8b1b97 upstream. Software scanning can be used for workaround some performance problems, so do not deprecate it. Signed-off-by: Stanislaw Gruszka Acked-by: Wey-Yi Guy Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl3945-base.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 371abbf60eac4..cf0699a05ba73 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -3995,8 +3995,7 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e * "the hard way", rather than using device's scan. */ if (iwl3945_mod_params.disable_hw_scan) { - dev_printk(KERN_DEBUG, &(pdev->dev), - "sw scan support is deprecated\n"); + IWL_DEBUG_INFO(priv, "Disabling hw_scan\n"); iwl3945_hw_ops.hw_scan = NULL; } @@ -4318,8 +4317,7 @@ MODULE_PARM_DESC(debug, "debug output mask"); #endif module_param_named(disable_hw_scan, iwl3945_mod_params.disable_hw_scan, int, S_IRUGO); -MODULE_PARM_DESC(disable_hw_scan, - "disable hardware scanning (default 0) (deprecated)"); +MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)"); module_param_named(fw_restart3945, iwl3945_mod_params.restart_fw, int, S_IRUGO); MODULE_PARM_DESC(fw_restart3945, "restart firmware in case of error"); From 9102975c215f5e7f46d582c443184b5e0f5a315e Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Tue, 29 Mar 2011 11:24:21 +0200 Subject: [PATCH 1577/2556] iwl3945: disable hw scan by default commit 0263aa45293838b514b8af674a03faf040991a90 upstream. After new NetworkManager 0.8.996 changes, hardware scanning is causing microcode errors as reported here: https://bugzilla.redhat.com/show_bug.cgi?id=683571 and sometimes kernel crashes: https://bugzilla.redhat.com/show_bug.cgi?id=688252 Also with hw scan there are very bad performance on some systems as reported here: https://bugzilla.redhat.com/show_bug.cgi?id=671366 Since Intel no longer supports 3945, there is no chance to get proper firmware fixes, we need workaround problems by disable hardware scanning by default. Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl3945-base.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index cf0699a05ba73..a8340f72085a6 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -94,6 +94,7 @@ MODULE_LICENSE("GPL"); struct iwl_mod_params iwl3945_mod_params = { .sw_crypto = 1, .restart_fw = 1, + .disable_hw_scan = 1, /* the rest are 0 by default */ }; @@ -4317,7 +4318,7 @@ MODULE_PARM_DESC(debug, "debug output mask"); #endif module_param_named(disable_hw_scan, iwl3945_mod_params.disable_hw_scan, int, S_IRUGO); -MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)"); +MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 1)"); module_param_named(fw_restart3945, iwl3945_mod_params.restart_fw, int, S_IRUGO); MODULE_PARM_DESC(fw_restart3945, "restart firmware in case of error"); From 8157e82d1bdf716127dc03bd2a500e51b50056b0 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 13 Apr 2011 10:56:51 +0200 Subject: [PATCH 1578/2556] iwlegacy: fix tx_power initialization commit 332704a51498a7e29aa92c19dc03f11f80b71bfe upstream. priv->tx_power_next is not initialized to max supported power, but instead default value is used, what cause errors like [ 58.597834] iwl3945 0000:03:00.0: Requested user TXPOWER 15 above upper limit 14. [ 58.597839] iwl3945 0000:03:00.0: Error setting Tx power (-22). if maximum tx power read from the eeprom is smaller than default. In consequence card is unable to initialize properly. Fix the problem and cleanup tx power initialization. Reported-and-tested-by: Robin Dong Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl-3945-hw.h | 2 -- drivers/net/wireless/iwlwifi/iwl-agn.c | 6 ------ drivers/net/wireless/iwlwifi/iwl-core.c | 9 +++++++-- drivers/net/wireless/iwlwifi/iwl-eeprom.c | 7 ------- drivers/net/wireless/iwlwifi/iwl3945-base.c | 4 ---- 5 files changed, 7 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h index 65b5834da28c7..c2dd4cdeb0d6f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h @@ -74,8 +74,6 @@ /* RSSI to dBm */ #define IWL39_RSSI_OFFSET 95 -#define IWL_DEFAULT_TX_POWER 0x0F - /* * EEPROM related constants, enums, and structures. */ diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index c1cfd9952e520..35239f04927c0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -3841,12 +3841,6 @@ static int iwl_init_drv(struct iwl_priv *priv) priv->dynamic_frag_thresh = BT_FRAG_THRESHOLD_DEF; } - /* Set the tx_power_user_lmt to the lowest power level - * this value will get overwritten by channel max power avg - * from eeprom */ - priv->tx_power_user_lmt = IWLAGN_TX_POWER_TARGET_POWER_MIN; - priv->tx_power_next = IWLAGN_TX_POWER_TARGET_POWER_MIN; - ret = iwl_init_channel_map(priv); if (ret) { IWL_ERR(priv, "initializing regulatory failed: %d\n", ret); diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 91cac6fb41fd6..294e9fcb7eef8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -168,6 +168,7 @@ int iwlcore_init_geos(struct iwl_priv *priv) struct ieee80211_channel *geo_ch; struct ieee80211_rate *rates; int i = 0; + s8 max_tx_power = 0; if (priv->bands[IEEE80211_BAND_2GHZ].n_bitrates || priv->bands[IEEE80211_BAND_5GHZ].n_bitrates) { @@ -244,8 +245,8 @@ int iwlcore_init_geos(struct iwl_priv *priv) geo_ch->flags |= ch->ht40_extension_channel; - if (ch->max_power_avg > priv->tx_power_device_lmt) - priv->tx_power_device_lmt = ch->max_power_avg; + if (ch->max_power_avg > max_tx_power) + max_tx_power = ch->max_power_avg; } else { geo_ch->flags |= IEEE80211_CHAN_DISABLED; } @@ -258,6 +259,10 @@ int iwlcore_init_geos(struct iwl_priv *priv) geo_ch->flags); } + priv->tx_power_device_lmt = max_tx_power; + priv->tx_power_user_lmt = max_tx_power; + priv->tx_power_next = max_tx_power; + if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) && priv->cfg->sku & IWL_SKU_A) { IWL_INFO(priv, "Incorrectly detected BG card as ABG. " diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c index 358cfd7e5af19..8b3c12753f9e8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c @@ -724,13 +724,6 @@ int iwl_init_channel_map(struct iwl_priv *priv) flags & EEPROM_CHANNEL_RADAR)) ? "" : "not "); - /* Set the tx_power_user_lmt to the highest power - * supported by any channel */ - if (eeprom_ch_info[ch].max_power_avg > - priv->tx_power_user_lmt) - priv->tx_power_user_lmt = - eeprom_ch_info[ch].max_power_avg; - ch_info++; } } diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index a8340f72085a6..64917edc9f5f3 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -3859,10 +3859,6 @@ static int iwl3945_init_drv(struct iwl_priv *priv) priv->force_reset[IWL_FW_RESET].reset_duration = IWL_DELAY_NEXT_FORCE_FW_RELOAD; - - priv->tx_power_user_lmt = IWL_DEFAULT_TX_POWER; - priv->tx_power_next = IWL_DEFAULT_TX_POWER; - if (eeprom->version < EEPROM_3945_EEPROM_VERSION) { IWL_WARN(priv, "Unsupported EEPROM version: 0x%04X\n", eeprom->version); From a5e2686af890bfbcdd29fa96ddec03fea774c9c7 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Mon, 18 Apr 2011 10:17:17 -0700 Subject: [PATCH 1579/2556] Input: xen-kbdfront - fix mouse getting stuck after save/restore commit c36b58e8a9112017c2bcc322cc98e71241814303 upstream. Mouse gets "stuck" after restore of PV guest but buttons are in working condition. If driver has been configured for ABS coordinates at start it will get XENKBD_TYPE_POS events and then suddenly after restore it'll start getting XENKBD_TYPE_MOTION events, that will be dropped later and they won't get into user-space. Regression was introduced by hunk 5 and 6 of 5ea5254aa0ad269cfbd2875c973ef25ab5b5e9db ("Input: xen-kbdfront - advertise either absolute or relative coordinates"). Driver on restore should ask xen for request-abs-pointer again if it is available. So restore parts that did it before 5ea5254. Acked-by: Olaf Hering Signed-off-by: Igor Mammedov [v1: Expanded the commit description] Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: Dmitry Torokhov --- drivers/input/xen-kbdfront.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c index 53e62732ee96f..c35ab940f1f00 100644 --- a/drivers/input/xen-kbdfront.c +++ b/drivers/input/xen-kbdfront.c @@ -286,7 +286,7 @@ static void xenkbd_backend_changed(struct xenbus_device *dev, enum xenbus_state backend_state) { struct xenkbd_info *info = dev_get_drvdata(&dev->dev); - int val; + int ret, val; switch (backend_state) { case XenbusStateInitialising: @@ -299,6 +299,16 @@ static void xenkbd_backend_changed(struct xenbus_device *dev, case XenbusStateInitWait: InitWait: + ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend, + "feature-abs-pointer", "%d", &val); + if (ret < 0) + val = 0; + if (val) { + ret = xenbus_printf(XBT_NIL, info->xbdev->nodename, + "request-abs-pointer", "1"); + if (ret) + pr_warning("can't request abs-pointer\n"); + } xenbus_switch_state(dev, XenbusStateConnected); break; From d081e52dadeae1040d74116bbe5570e8128ad056 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 2 May 2011 09:30:53 -0700 Subject: [PATCH 1580/2556] Linux 2.6.38.5 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e3b3c0ab43da6..02acceab855a4 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 38 -EXTRAVERSION = .4 +EXTRAVERSION = .5 NAME = Flesh-Eating Bats with Fangs # *DOCUMENTATION* From 140bcef66546b206f82050315ac7e0eb5b111e8b Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Mon, 2 May 2011 19:09:03 -0400 Subject: [PATCH 1581/2556] defconfig: regen for .5 --- arch/arm/configs/pershoot_mahimahi_defconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/configs/pershoot_mahimahi_defconfig b/arch/arm/configs/pershoot_mahimahi_defconfig index e06e0ee003bd9..d6f04950491f1 100644 --- a/arch/arm/configs/pershoot_mahimahi_defconfig +++ b/arch/arm/configs/pershoot_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux/arm 2.6.38.4 Kernel Configuration -# Sat Apr 30 14:28:33 2011 +# Linux/arm 2.6.38.5 Kernel Configuration +# Mon May 2 19:07:50 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y From cf546a1fd7fd736f1cd471f059900d477a4b7096 Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Tue, 5 Apr 2011 13:27:31 -0400 Subject: [PATCH 1582/2556] pmcraid: reject negative request size commit 5f6279da3760ce48f478f2856aacebe0c59a39f3 upstream. There's a code path in pmcraid that can be reached via device ioctl that causes all sorts of ugliness, including heap corruption or triggering the OOM killer due to consecutive allocation of large numbers of pages. Not especially relevant from a security perspective, since users must have CAP_SYS_ADMIN to open the character device. First, the user can call pmcraid_chr_ioctl() with a type PMCRAID_PASSTHROUGH_IOCTL. A pmcraid_passthrough_ioctl_buffer is copied in, and the request_size variable is set to buffer->ioarcb.data_transfer_length, which is an arbitrary 32-bit signed value provided by the user. If a negative value is provided here, bad things can happen. For example, pmcraid_build_passthrough_ioadls() is called with this request_size, which immediately calls pmcraid_alloc_sglist() with a negative size. The resulting math on allocating a scatter list can result in an overflow in the kzalloc() call (if num_elem is 0, the sglist will be smaller than expected), or if num_elem is unexpectedly large the subsequent loop will call alloc_pages() repeatedly, a high number of pages will be allocated and the OOM killer might be invoked. Prevent this value from being negative in pmcraid_ioctl_passthrough(). Signed-off-by: Dan Rosenberg Cc: Anil Ravindranath Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/pmcraid.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 321cf3ae86308..ce0701d512f42 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -3814,6 +3814,9 @@ static long pmcraid_ioctl_passthrough( rc = -EFAULT; goto out_free_buffer; } + } else if (request_size < 0) { + rc = -EINVAL; + goto out_free_buffer; } /* check if we have any additional command parameters */ From 5c9843ab660296536df95d53f44979bece79a87b Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Tue, 5 Apr 2011 12:45:59 -0400 Subject: [PATCH 1583/2556] mpt2sas: prevent heap overflows and unchecked reads commit a1f74ae82d133ebb2aabb19d181944b4e83e9960 upstream. At two points in handling device ioctls via /dev/mpt2ctl, user-supplied length values are used to copy data from userspace into heap buffers without bounds checking, allowing controllable heap corruption and subsequently privilege escalation. Additionally, user-supplied values are used to determine the size of a copy_to_user() as well as the offset into the buffer to be read, with no bounds checking, allowing users to read arbitrary kernel memory. Signed-off-by: Dan Rosenberg Acked-by: Eric Moore Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/mpt2sas/mpt2sas_ctl.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c index e92b77af54849..3834c95cffa22 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c @@ -688,6 +688,13 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, goto out; } + /* Check for overflow and wraparound */ + if (karg.data_sge_offset * 4 > ioc->request_sz || + karg.data_sge_offset > (UINT_MAX / 4)) { + ret = -EINVAL; + goto out; + } + /* copy in request message frame from user */ if (copy_from_user(mpi_request, mf, karg.data_sge_offset*4)) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, @@ -1963,7 +1970,7 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state) Mpi2DiagBufferPostReply_t *mpi_reply; int rc, i; u8 buffer_type; - unsigned long timeleft; + unsigned long timeleft, request_size, copy_size; u16 smid; u16 ioc_status; u8 issue_reset = 0; @@ -1999,6 +2006,8 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state) return -ENOMEM; } + request_size = ioc->diag_buffer_sz[buffer_type]; + if ((karg.starting_offset % 4) || (karg.bytes_to_read % 4)) { printk(MPT2SAS_ERR_FMT "%s: either the starting_offset " "or bytes_to_read are not 4 byte aligned\n", ioc->name, @@ -2006,13 +2015,23 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state) return -EINVAL; } + if (karg.starting_offset > request_size) + return -EINVAL; + diag_data = (void *)(request_data + karg.starting_offset); dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: diag_buffer(%p), " "offset(%d), sz(%d)\n", ioc->name, __func__, diag_data, karg.starting_offset, karg.bytes_to_read)); + /* Truncate data on requests that are too large */ + if ((diag_data + karg.bytes_to_read < diag_data) || + (diag_data + karg.bytes_to_read > request_data + request_size)) + copy_size = request_size - karg.starting_offset; + else + copy_size = karg.bytes_to_read; + if (copy_to_user((void __user *)uarg->diagnostic_data, - diag_data, karg.bytes_to_read)) { + diag_data, copy_size)) { printk(MPT2SAS_ERR_FMT "%s: Unable to write " "mpt_diag_read_buffer_t data @ %p\n", ioc->name, __func__, diag_data); From 52adc5764d7cbeddce5e70aac1a14f534d004b60 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Fri, 8 Apr 2011 15:05:36 -0400 Subject: [PATCH 1584/2556] scsi_dh: fix reference counting in scsi_dh_activate error path commit 0b8393578c70bc1f09790eeae7d918f38da2e010 upstream. Commit db422318cbca55168cf965f655471dbf8be82433 ([SCSI] scsi_dh: propagate SCSI device deletion) introduced a regression where the device reference is not dropped prior to scsi_dh_activate's early return from the error path. Signed-off-by: Mike Snitzer Reviewed-by: Mike Christie Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/device_handler/scsi_dh.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index b837c5b3c8f9d..1367b919c4937 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c @@ -437,12 +437,14 @@ int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) unsigned long flags; struct scsi_device *sdev; struct scsi_device_handler *scsi_dh = NULL; + struct device *dev = NULL; spin_lock_irqsave(q->queue_lock, flags); sdev = q->queuedata; if (sdev && sdev->scsi_dh_data) scsi_dh = sdev->scsi_dh_data->scsi_dh; - if (!scsi_dh || !get_device(&sdev->sdev_gendev) || + dev = get_device(&sdev->sdev_gendev); + if (!scsi_dh || !dev || sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL) err = SCSI_DH_NOSYS; @@ -453,12 +455,13 @@ int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) if (err) { if (fn) fn(data, err); - return err; + goto out; } if (scsi_dh->activate) err = scsi_dh->activate(sdev, fn, data); - put_device(&sdev->sdev_gendev); +out: + put_device(dev); return err; } EXPORT_SYMBOL_GPL(scsi_dh_activate); From 1f74c190e1e97a38823c07fdc71780580a0fc03f Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 22 Apr 2011 10:39:59 -0500 Subject: [PATCH 1585/2556] put stricter guards on queue dead checks commit 86cbfb5607d4b81b1a993ff689bbd2addd5d3a9b upstream. SCSI uses request_queue->queuedata == NULL as a signal that the queue is dying. We set this state in the sdev release function. However, this allows a small window where we release the last reference but haven't quite got to this stage yet and so something will try to take a reference in scsi_request_fn and oops. It's very rare, but we had a report here, so we're pushing this as a bug fix The actual fix is to set request_queue->queuedata to NULL in scsi_remove_device() before we drop the reference. This causes correct automatic rejects from scsi_request_fn as people who hold additional references try to submit work and prevents anything from getting a new reference to the sdev that way. Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_sysfs.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 490ce213204e9..360b7cbadb446 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -322,14 +322,8 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work) kfree(evt); } - if (sdev->request_queue) { - sdev->request_queue->queuedata = NULL; - /* user context needed to free queue */ - scsi_free_queue(sdev->request_queue); - /* temporary expedient, try to catch use of queue lock - * after free of sdev */ - sdev->request_queue = NULL; - } + /* NULL queue means the device can't be used */ + sdev->request_queue = NULL; scsi_target_reap(scsi_target(sdev)); @@ -937,6 +931,12 @@ void __scsi_remove_device(struct scsi_device *sdev) if (sdev->host->hostt->slave_destroy) sdev->host->hostt->slave_destroy(sdev); transport_destroy_device(dev); + + /* cause the request function to reject all I/O requests */ + sdev->request_queue->queuedata = NULL; + + /* Freeing the queue signals to block that we're done */ + scsi_free_queue(sdev->request_queue); put_device(dev); } From 13ec44a014c358e98cdc6a9af3d891b7d3753d59 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 29 Apr 2011 14:10:55 +0200 Subject: [PATCH 1586/2556] ALSA: HDA: Fix automute for Gateway NV79 commit 94024cd1aefa0f8bcc9dfe46c05bd7ce3f471a1c upstream. The PCI SSID is 1025:031c and the codec SSID is 1025:031d, so the driver mistakes this for a SKU value, but looking at the numbers, this is obviously wrong. BugLink: http://bugs.launchpad.net/bugs/761861 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8ff0223fb10f2..ee365446f0582 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -19556,6 +19556,7 @@ enum { ALC272_FIXUP_MARIO, ALC662_FIXUP_CZC_P10T, ALC662_FIXUP_GIGABYTE, + ALC662_FIXUP_SKU_IGNORE, }; static const struct alc_fixup alc662_fixups[] = { @@ -19591,10 +19592,15 @@ static const struct alc_fixup alc662_fixups[] = { { } } }, + [ALC662_FIXUP_SKU_IGNORE] = { + .type = ALC_FIXUP_SKU, + .v.sku = ALC_FIXUP_SKU_IGNORE, + }, }; static struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte", ALC662_FIXUP_GIGABYTE), From cd529bb9b1e202f289ff9c2182f7d274e5e7160f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 May 2011 10:37:29 +0200 Subject: [PATCH 1587/2556] Revert "ALSA: hda - Fix pin-config of Gigabyte mobo" commit ebb47241ea0eac6a5a23404821a2d62f64c68496 upstream. This reverts commit c6b358748e19ce7e230b0926ac42696bc485a562. It turned out that there are different pin configurations for this PCI SSID, including multi-channel modes. And more proper fix for allowing line-out mutes will come up in 2.6.40 tree, so we won't need this fixup any more there. Reported-by: Andrew Clayton Reported-by: Emmanuel Benisty Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ee365446f0582..d7e251bc58f70 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9932,6 +9932,7 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = { SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL), SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG), @@ -10768,7 +10769,6 @@ enum { PINFIX_LENOVO_Y530, PINFIX_PB_M5210, PINFIX_ACER_ASPIRE_7736, - PINFIX_GIGABYTE_880GM, }; static const struct alc_fixup alc882_fixups[] = { @@ -10800,13 +10800,6 @@ static const struct alc_fixup alc882_fixups[] = { .type = ALC_FIXUP_SKU, .v.sku = ALC_FIXUP_SKU_IGNORE, }, - [PINFIX_GIGABYTE_880GM] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x1114410 }, /* set as speaker */ - { } - } - }, }; static struct snd_pci_quirk alc882_fixup_tbl[] = { @@ -10814,7 +10807,6 @@ static struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", PINFIX_LENOVO_Y530), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX), SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte", PINFIX_GIGABYTE_880GM), {} }; @@ -18882,6 +18874,8 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x1179, 0xff6e, "Toshiba NB20x", ALC662_AUTO), SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L", + ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x152d, 0x2304, "Quanta WH1", ALC663_ASUS_H13), SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x1631, 0xc10c, "PB RS65", ALC663_ASUS_M51VA), @@ -19555,7 +19549,6 @@ enum { ALC662_FIXUP_IDEAPAD, ALC272_FIXUP_MARIO, ALC662_FIXUP_CZC_P10T, - ALC662_FIXUP_GIGABYTE, ALC662_FIXUP_SKU_IGNORE, }; @@ -19585,13 +19578,6 @@ static const struct alc_fixup alc662_fixups[] = { {} } }, - [ALC662_FIXUP_GIGABYTE] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x1114410 }, /* set as speaker */ - { } - } - }, [ALC662_FIXUP_SKU_IGNORE] = { .type = ALC_FIXUP_SKU, .v.sku = ALC_FIXUP_SKU_IGNORE, @@ -19603,7 +19589,6 @@ static struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte", ALC662_FIXUP_GIGABYTE), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), From bbb267c7d49c3a9be39e7f59bae56500afee0ab3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 May 2011 13:55:36 +0200 Subject: [PATCH 1588/2556] ALSA: hda - Fix Realtek's chained fixup checks commit 24af2b1cc418d6791b1d9e56bf6070cccb752db3 upstream. The check of chained fixup list entry was done against the wrong element. A stupid mistake during refactoring. Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d7e251bc58f70..24a3acb63f691 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1774,11 +1774,11 @@ static void alc_apply_fixup(struct hda_codec *codec, int action) codec->chip_name, fix->type); break; } - if (!fix[id].chained) + if (!fix->chained) break; if (++depth > 10) break; - id = fix[id].chain_id; + id = fix->chain_id; } } From bcd0df39767d1c4352566d909291deac099b222b Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 1 May 2011 18:18:49 +0200 Subject: [PATCH 1589/2556] i2c-parport: Fix adapter list handling commit 56acc7a39ac4ac7638cdc32cd3d0832ebbc834e4 upstream. Use a standard list with proper locking to handle the list of adapters. Thankfully it only matters on systems with more than one parallel port, which are very rare. Thanks to Lukasz Kapiec for reporting the problem to me. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-parport.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c index 0eb1515541e72..2dbba163b1020 100644 --- a/drivers/i2c/busses/i2c-parport.c +++ b/drivers/i2c/busses/i2c-parport.c @@ -1,7 +1,7 @@ /* ------------------------------------------------------------------------ * * i2c-parport.c I2C bus over parallel port * * ------------------------------------------------------------------------ * - Copyright (C) 2003-2010 Jean Delvare + Copyright (C) 2003-2011 Jean Delvare Based on older i2c-philips-par.c driver Copyright (C) 1995-2000 Simon G. Vogl @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include "i2c-parport.h" /* ----- Device list ------------------------------------------------------ */ @@ -43,10 +45,11 @@ struct i2c_par { struct i2c_algo_bit_data algo_data; struct i2c_smbus_alert_setup alert_data; struct i2c_client *ara; - struct i2c_par *next; + struct list_head node; }; -static struct i2c_par *adapter_list; +static LIST_HEAD(adapter_list); +static DEFINE_MUTEX(adapter_list_lock); /* ----- Low-level parallel port access ----------------------------------- */ @@ -228,8 +231,9 @@ static void i2c_parport_attach (struct parport *port) } /* Add the new adapter to the list */ - adapter->next = adapter_list; - adapter_list = adapter; + mutex_lock(&adapter_list_lock); + list_add_tail(&adapter->node, &adapter_list); + mutex_unlock(&adapter_list_lock); return; ERROR1: @@ -241,11 +245,11 @@ static void i2c_parport_attach (struct parport *port) static void i2c_parport_detach (struct parport *port) { - struct i2c_par *adapter, *prev; + struct i2c_par *adapter, *_n; /* Walk the list */ - for (prev = NULL, adapter = adapter_list; adapter; - prev = adapter, adapter = adapter->next) { + mutex_lock(&adapter_list_lock); + list_for_each_entry_safe(adapter, _n, &adapter_list, node) { if (adapter->pdev->port == port) { if (adapter->ara) { parport_disable_irq(port); @@ -259,14 +263,11 @@ static void i2c_parport_detach (struct parport *port) parport_release(adapter->pdev); parport_unregister_device(adapter->pdev); - if (prev) - prev->next = adapter->next; - else - adapter_list = adapter->next; + list_del(&adapter->node); kfree(adapter); - return; } } + mutex_unlock(&adapter_list_lock); } static struct parport_driver i2c_parport_driver = { From 5a19c7ecdc631407d490d206b80ed790daad99cd Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 29 Apr 2011 18:08:37 +0200 Subject: [PATCH 1590/2556] workqueue: fix deadlock in worker_maybe_bind_and_lock() commit 5035b20fa5cd146b66f5f89619c20a4177fb736d upstream. If a rescuer and stop_machine() bringing down a CPU race with each other, they may deadlock on non-preemptive kernel. The CPU won't accept a new task, so the rescuer can't migrate to the target CPU, while stop_machine() can't proceed because the rescuer is holding one of the CPU retrying migration. GCWQ_DISASSOCIATED is never cleared and worker_maybe_bind_and_lock() retries indefinitely. This problem can be reproduced semi reliably while the system is entering suspend. http://thread.gmane.org/gmane.linux.kernel/1122051 A lot of kudos to Thilo-Alexander for reporting this tricky issue and painstaking testing. stable: This affects all kernels with cmwq, so all kernels since and including v2.6.36 need this fix. Signed-off-by: Tejun Heo Reported-by: Thilo-Alexander Ginkel Tested-by: Thilo-Alexander Ginkel Signed-off-by: Greg Kroah-Hartman --- kernel/workqueue.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index ee6578b578ad3..7b65ae747c6fd 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1283,8 +1283,14 @@ __acquires(&gcwq->lock) return true; spin_unlock_irq(&gcwq->lock); - /* CPU has come up inbetween, retry migration */ + /* + * We've raced with CPU hot[un]plug. Give it a breather + * and retry migration. cond_resched() is required here; + * otherwise, we might deadlock against cpu_stop trying to + * bring down the CPU on non-preemptive kernel. + */ cpu_relax(); + cond_resched(); } } From 0d15c412b9d90238f94d1ec0100f4035582b29cf Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 20 Apr 2011 15:57:14 +0200 Subject: [PATCH 1591/2556] iwlwifi: fix skb usage after free commit b25026981aecde3685dd0e45ad980fff9f528daa upstream. Since commit a120e912eb51e347f36c71b60a1d13af74d30e83 Author: Stanislaw Gruszka Date: Fri Feb 19 15:47:33 2010 -0800 iwlwifi: sanity check before counting number of tfds can be free we use skb->data after calling ieee80211_tx_status_irqsafe(), which could free skb instantly. On current kernels I do not observe practical problems related with bug, but on 2.6.35.y it cause random system hangs when stressing wireless link. Signed-off-by: Stanislaw Gruszka Acked-by: Wey-Yi Guy Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index 24a11b8f73bc1..f5aaf31c4c15e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -1207,12 +1207,16 @@ int iwlagn_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index) q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { tx_info = &txq->txb[txq->q.read_ptr]; - iwlagn_tx_status(priv, tx_info, - txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); + + if (WARN_ON_ONCE(tx_info->skb == NULL)) + continue; hdr = (struct ieee80211_hdr *)tx_info->skb->data; - if (hdr && ieee80211_is_data_qos(hdr->frame_control)) + if (ieee80211_is_data_qos(hdr->frame_control)) nfreed++; + + iwlagn_tx_status(priv, tx_info, + txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); tx_info->skb = NULL; if (priv->cfg->ops->lib->txq_inval_byte_cnt_tbl) From 40b2b978ebc16d96805c8330883967a0ca8aedf9 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 29 Apr 2011 17:51:06 +0200 Subject: [PATCH 1592/2556] iwlagn: fix "Received BA when not expected" commit bfd36103ec26599557c2bd3225a1f1c9267f8fcb upstream. Need to use broadcast sta_id for management frames, otherwise we broke BA session in the firmware and get messages like that: "Received BA when not expected" or (on older kernels): "BA scd_flow 0 does not match txq_id 10" This fix regression introduced in 2.6.35 during station management code rewrite by: commit 2a87c26bbe9587baeb9e56d3ce0b4971bd777643 Author: Johannes Berg Date: Fri Apr 30 11:30:45 2010 -0700 iwlwifi: use iwl_find_station less Patch partially resolve: https://bugzilla.kernel.org/show_bug.cgi?id=16691 However, there are still 11n performance problems on 4965 and 5xxx devices that need to be investigated. Signed-off-by: Stanislaw Gruszka Acked-by: Johannes Berg Acked-by: Wey-Yi Guy Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index f5aaf31c4c15e..c13542b495fed 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -561,12 +561,17 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) hdr_len = ieee80211_hdrlen(fc); - /* Find index into station table for destination station */ - sta_id = iwl_sta_id_or_broadcast(priv, ctx, info->control.sta); - if (sta_id == IWL_INVALID_STATION) { - IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", - hdr->addr1); - goto drop_unlock; + /* For management frames use broadcast id to do not break aggregation */ + if (!ieee80211_is_data(fc)) + sta_id = ctx->bcast_sta_id; + else { + /* Find index into station table for destination station */ + sta_id = iwl_sta_id_or_broadcast(priv, ctx, info->control.sta); + if (sta_id == IWL_INVALID_STATION) { + IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", + hdr->addr1); + goto drop_unlock; + } } IWL_DEBUG_TX(priv, "station Id %d\n", sta_id); From b000ec13075615ace30250ab6e355a3f0c956735 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Wed, 20 Apr 2011 09:00:49 +0000 Subject: [PATCH 1593/2556] atl1c: Fix work event interrupt/task races commit cb771838715b1c470bc5735bdae709b33b18e0ad upstream. The mechanism used to initiate work events from the interrupt handler has a classic read/modify/write race between the interrupt handler that sets the condition, and the worker task that reads and clears the condition. Close these races by using atomic bit fields. Cc: Jie Yang Signed-off-by: Tim Gardner Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/atl1c/atl1c.h | 6 +++--- drivers/net/atl1c/atl1c_main.c | 14 +++++--------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/net/atl1c/atl1c.h b/drivers/net/atl1c/atl1c.h index 9ab58097fa2e7..dec81102adc29 100644 --- a/drivers/net/atl1c/atl1c.h +++ b/drivers/net/atl1c/atl1c.h @@ -566,9 +566,9 @@ struct atl1c_adapter { #define __AT_TESTING 0x0001 #define __AT_RESETTING 0x0002 #define __AT_DOWN 0x0003 - u8 work_event; -#define ATL1C_WORK_EVENT_RESET 0x01 -#define ATL1C_WORK_EVENT_LINK_CHANGE 0x02 + unsigned long work_event; +#define ATL1C_WORK_EVENT_RESET 0 +#define ATL1C_WORK_EVENT_LINK_CHANGE 1 u32 msg_enable; bool have_msi; diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c index 3824382faecc1..dffc7f72e7d78 100644 --- a/drivers/net/atl1c/atl1c_main.c +++ b/drivers/net/atl1c/atl1c_main.c @@ -325,7 +325,7 @@ static void atl1c_link_chg_event(struct atl1c_adapter *adapter) } } - adapter->work_event |= ATL1C_WORK_EVENT_LINK_CHANGE; + set_bit(ATL1C_WORK_EVENT_LINK_CHANGE, &adapter->work_event); schedule_work(&adapter->common_task); } @@ -337,20 +337,16 @@ static void atl1c_common_task(struct work_struct *work) adapter = container_of(work, struct atl1c_adapter, common_task); netdev = adapter->netdev; - if (adapter->work_event & ATL1C_WORK_EVENT_RESET) { - adapter->work_event &= ~ATL1C_WORK_EVENT_RESET; + if (test_and_clear_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event)) { netif_device_detach(netdev); atl1c_down(adapter); atl1c_up(adapter); netif_device_attach(netdev); - return; } - if (adapter->work_event & ATL1C_WORK_EVENT_LINK_CHANGE) { - adapter->work_event &= ~ATL1C_WORK_EVENT_LINK_CHANGE; + if (test_and_clear_bit(ATL1C_WORK_EVENT_LINK_CHANGE, + &adapter->work_event)) atl1c_check_link_status(adapter); - } - return; } @@ -369,7 +365,7 @@ static void atl1c_tx_timeout(struct net_device *netdev) struct atl1c_adapter *adapter = netdev_priv(netdev); /* Do the reset outside of interrupt context */ - adapter->work_event |= ATL1C_WORK_EVENT_RESET; + set_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event); schedule_work(&adapter->common_task); } From 21fb89793df408cc116aa52399a5e6f7af459596 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 25 Apr 2011 18:17:09 +0300 Subject: [PATCH 1594/2556] UBIFS: do not free write-buffers when in R/O mode commit b50b9f408502a2ea90459ae36ba8cdc9cc005cfe upstream. Currently UBIFS has a small optimization - it frees write-buffers when it is re-mounted from R/W mode to R/O mode. Of course, when it is mounted R/O, it does not allocate write-buffers as well. This optimization is nice but it leads to subtle problems and complications in recovery, which I can reproduce using the integck test. The symptoms are that after a power cut the file-system cannot be mounted if we first mount it R/O, and then re-mount R/W - 'ubifs_rcvry_gc_commit()' prints: UBIFS error (pid 34456): could not find an empty LEB Analysis of the problem. When mounting R/W, the reply process sets journal heads to buds [1], but when mounting R/O - it does not do this, because the write-buffers are not allocated. So 'ubifs_rcvry_gc_commit()' works completely differently for the same file-system but for the following 2 cases: 1. mounting R/W after a power cut and recover 2. mounting R/O after a power cut, re-mounting R/W and run deferred recovery In the former case, we have journal heads seeked to the a bud, in the latter case, they are non-seeked (wbuf->lnum == -1). So in the latter case we do not try to recover the GC LEB by garbage-collecting to the GC head, but we just try to find an empty LEB, and there may be no empty LEBs, so we just fail. On the other hand, in the former case (mount R/W), we are able to make a GC LEB (@c->gc_lnum) by garbage-collecting. Thus, let's remove this small nice optimization and always allocate write-buffers. This should not make too big difference - we have only 3 of them, each of max. write unit size, which is usually 2KiB. So this is about 6KiB of RAM for the typical case, and only when mounted R/O. [1]: Note, currently the replay process is setting (seeking) the journal heads to _some_ buds, not necessarily to the buds which had been the journal heads before the power cut happened. This will be fixed separately. Signed-off-by: Artem Bityutskiy Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/log.c | 20 -------------------- fs/ubifs/super.c | 15 ++++----------- 2 files changed, 4 insertions(+), 31 deletions(-) diff --git a/fs/ubifs/log.c b/fs/ubifs/log.c index 4d0cb1241460f..40fa780ebea7d 100644 --- a/fs/ubifs/log.c +++ b/fs/ubifs/log.c @@ -174,26 +174,6 @@ void ubifs_add_bud(struct ubifs_info *c, struct ubifs_bud *bud) spin_unlock(&c->buds_lock); } -/** - * ubifs_create_buds_lists - create journal head buds lists for remount rw. - * @c: UBIFS file-system description object - */ -void ubifs_create_buds_lists(struct ubifs_info *c) -{ - struct rb_node *p; - - spin_lock(&c->buds_lock); - p = rb_first(&c->buds); - while (p) { - struct ubifs_bud *bud = rb_entry(p, struct ubifs_bud, rb); - struct ubifs_jhead *jhead = &c->jheads[bud->jhead]; - - list_add_tail(&bud->list, &jhead->buds_list); - p = rb_next(p); - } - spin_unlock(&c->buds_lock); -} - /** * ubifs_add_bud_to_log - add a new bud to the log. * @c: UBIFS file-system description object diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index e94d9628a49e4..e20cb5a9aee54 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1235,12 +1235,12 @@ static int mount_ubifs(struct ubifs_info *c) goto out_free; } + err = alloc_wbufs(c); + if (err) + goto out_cbuf; + sprintf(c->bgt_name, BGT_NAME_PATTERN, c->vi.ubi_num, c->vi.vol_id); if (!c->ro_mount) { - err = alloc_wbufs(c); - if (err) - goto out_cbuf; - /* Create background thread */ c->bgt = kthread_create(ubifs_bg_thread, c, "%s", c->bgt_name); if (IS_ERR(c->bgt)) { @@ -1603,12 +1603,6 @@ static int ubifs_remount_rw(struct ubifs_info *c) if (err) goto out; - err = alloc_wbufs(c); - if (err) - goto out; - - ubifs_create_buds_lists(c); - /* Create background thread */ c->bgt = kthread_create(ubifs_bg_thread, c, "%s", c->bgt_name); if (IS_ERR(c->bgt)) { @@ -1717,7 +1711,6 @@ static void ubifs_remount_ro(struct ubifs_info *c) if (err) ubifs_ro_mode(c, err); - free_wbufs(c); vfree(c->orph_buf); c->orph_buf = NULL; vfree(c->ileb_buf); From f038f4d893e9ef35dd38f1ca01001dfe21c5f8f5 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 25 Apr 2011 18:46:31 +0300 Subject: [PATCH 1595/2556] UBIFS: seek journal heads to the latest bud in replay commit 52c6e6f990669deac3f370f1603815adb55a1dbd upstream. This is the second fix of the following symptom: UBIFS error (pid 34456): could not find an empty LEB which sometimes happens after power cuts when we mount the file-system - UBIFS refuses it with the above error message which comes from the 'ubifs_rcvry_gc_commit()' function. I can reproduce this using the integck test with the UBIFS power cut emulation enabled. Analysis of the problem. Currently UBIFS replay seeks the journal heads to the last _replayed_ bud. But the buds are replayed out-of-order, so the replay basically seeks journal heads to the "random" bud belonging to this head, and not to the _last_ one. The result of this is that the GC head may be seeked to a full LEB with no free space, or very little free space. And 'ubifs_rcvry_gc_commit()' tries to find a fully or mostly dirty LEB to match the current GC head (because we need to garbage-collect that dirty LEB at one go, because we do not have @c->gc_lnum). So 'ubifs_find_dirty_leb()' fails and we fall back to finding an empty LEB and also fail. As a result - recovery fails and mounting fails. This patch teaches the replay to initialize the GC heads exactly to the latest buds, i.e. the buds which have the largest sequence number in corresponding log reference nodes. Signed-off-by: Artem Bityutskiy Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/replay.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c index eed0fcff8d731..d3d6d365bfc11 100644 --- a/fs/ubifs/replay.c +++ b/fs/ubifs/replay.c @@ -59,6 +59,7 @@ enum { * @new_size: truncation new size * @free: amount of free space in a bud * @dirty: amount of dirty space in a bud from padding and deletion nodes + * @jhead: journal head number of the bud * * UBIFS journal replay must compare node sequence numbers, which means it must * build a tree of node information to insert into the TNC. @@ -80,6 +81,7 @@ struct replay_entry { struct { int free; int dirty; + int jhead; }; }; }; @@ -159,6 +161,11 @@ static int set_bud_lprops(struct ubifs_info *c, struct replay_entry *r) err = PTR_ERR(lp); goto out; } + + /* Make sure the journal head points to the latest bud */ + err = ubifs_wbuf_seek_nolock(&c->jheads[r->jhead].wbuf, r->lnum, + c->leb_size - r->free, UBI_SHORTTERM); + out: ubifs_release_lprops(c); return err; @@ -627,10 +634,6 @@ static int replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, ubifs_assert(sleb->endpt - offs >= used); ubifs_assert(sleb->endpt % c->min_io_size == 0); - if (sleb->endpt + c->min_io_size <= c->leb_size && !c->ro_mount) - err = ubifs_wbuf_seek_nolock(&c->jheads[jhead].wbuf, lnum, - sleb->endpt, UBI_SHORTTERM); - *dirty = sleb->endpt - offs - used; *free = c->leb_size - sleb->endpt; @@ -653,12 +656,14 @@ static int replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, * @sqnum: sequence number * @free: amount of free space in bud * @dirty: amount of dirty space from padding and deletion nodes + * @jhead: journal head number for the bud * * This function inserts a reference node to the replay tree and returns zero * in case of success or a negative error code in case of failure. */ static int insert_ref_node(struct ubifs_info *c, int lnum, int offs, - unsigned long long sqnum, int free, int dirty) + unsigned long long sqnum, int free, int dirty, + int jhead) { struct rb_node **p = &c->replay_tree.rb_node, *parent = NULL; struct replay_entry *r; @@ -688,6 +693,7 @@ static int insert_ref_node(struct ubifs_info *c, int lnum, int offs, r->flags = REPLAY_REF; r->free = free; r->dirty = dirty; + r->jhead = jhead; rb_link_node(&r->rb, parent, p); rb_insert_color(&r->rb, &c->replay_tree); @@ -712,7 +718,7 @@ static int replay_buds(struct ubifs_info *c) if (err) return err; err = insert_ref_node(c, b->bud->lnum, b->bud->start, b->sqnum, - free, dirty); + free, dirty, b->bud->jhead); if (err) return err; } From 3fe962c04818a4634255beb3be9f236d36350543 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 15 Apr 2011 20:08:19 +0200 Subject: [PATCH 1596/2556] mmc: fix a race between card-detect rescan and clock-gate work instances commit 26fc8775b51484d8c0a671198639c6d5ae60533e upstream. Currently there is a race in the MMC core between a card-detect rescan work and the clock-gating work, scheduled from a command completion. Fix it by removing the dedicated clock-gating mutex and using the MMC standard locking mechanism instead. Signed-off-by: Guennadi Liakhovetski Cc: Simon Horman Cc: Magnus Damm Acked-by: Linus Walleij Signed-off-by: Chris Ball Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/core/host.c | 9 ++++----- include/linux/mmc/host.h | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index b3ac6c5bc5c62..9ae3dbfba3661 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -94,7 +94,7 @@ static void mmc_host_clk_gate_delayed(struct mmc_host *host) spin_unlock_irqrestore(&host->clk_lock, flags); return; } - mutex_lock(&host->clk_gate_mutex); + mmc_claim_host(host); spin_lock_irqsave(&host->clk_lock, flags); if (!host->clk_requests) { spin_unlock_irqrestore(&host->clk_lock, flags); @@ -104,7 +104,7 @@ static void mmc_host_clk_gate_delayed(struct mmc_host *host) pr_debug("%s: gated MCI clock\n", mmc_hostname(host)); } spin_unlock_irqrestore(&host->clk_lock, flags); - mutex_unlock(&host->clk_gate_mutex); + mmc_release_host(host); } /* @@ -130,7 +130,7 @@ void mmc_host_clk_ungate(struct mmc_host *host) { unsigned long flags; - mutex_lock(&host->clk_gate_mutex); + mmc_claim_host(host); spin_lock_irqsave(&host->clk_lock, flags); if (host->clk_gated) { spin_unlock_irqrestore(&host->clk_lock, flags); @@ -140,7 +140,7 @@ void mmc_host_clk_ungate(struct mmc_host *host) } host->clk_requests++; spin_unlock_irqrestore(&host->clk_lock, flags); - mutex_unlock(&host->clk_gate_mutex); + mmc_release_host(host); } /** @@ -218,7 +218,6 @@ static inline void mmc_host_clk_init(struct mmc_host *host) host->clk_gated = false; INIT_WORK(&host->clk_gate_work, mmc_host_clk_gate_work); spin_lock_init(&host->clk_lock); - mutex_init(&host->clk_gate_mutex); } /** diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index bcb793ec7374f..eb792cb6d745a 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -183,7 +183,6 @@ struct mmc_host { struct work_struct clk_gate_work; /* delayed clock gate */ unsigned int clk_old; /* old clock value cache */ spinlock_t clk_lock; /* lock for clk fields */ - struct mutex clk_gate_mutex; /* mutex for clock gating */ #endif /* host specific block data */ From 4e90d9686addf31f74821cacd54cda8c70a67038 Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Tue, 29 Mar 2011 00:46:12 -0400 Subject: [PATCH 1597/2556] mmc: sdhci-pci: Fix error case in sdhci_pci_probe_slot() commit 9fdcdbb0d84922e7ccda2f717a04ea62629f7e18 upstream. If pci_ioremap_bar() fails during probe, we "goto release;" and free the host, but then we return 0 -- which tells sdhci_pci_probe() that the probe succeeded. Since we think the probe succeeded, when we unload sdhci we'll go to sdhci_pci_remove_slot() and it will try to dereference slot->host, which is now NULL because we freed it in the error path earlier. The patch simply sets ret appropriately, so that sdhci_pci_probe() will detect the failure immediately and bail out. Signed-off-by: Chris Ball Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index f7e622c539baa..b1b2fc04dd72c 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -961,6 +961,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( host->ioaddr = pci_ioremap_bar(pdev, bar); if (!host->ioaddr) { dev_err(&pdev->dev, "failed to remap registers\n"); + ret = -ENOMEM; goto release; } From 40e6af80dbcb551530b6c442564df01f9db3af33 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 27 Apr 2011 14:24:19 +0100 Subject: [PATCH 1598/2556] mmc: sdhci: Check mrq->cmd in sdhci_tasklet_finish commit b7b4d3426d2b5ecab21578eb20d8e456a1aace8f upstream. It seems that under certain circumstances that the sdhci_tasklet_finish() call can be entered with mrq->cmd set to NULL, causing the system to crash with a NULL pointer de-reference. Unable to handle kernel NULL pointer dereference at virtual address 00000000 PC is at sdhci_tasklet_finish+0x34/0xe8 LR is at sdhci_tasklet_finish+0x24/0xe8 Seen on S3C6410 system. Signed-off-by: Ben Dooks Signed-off-by: Mark Brown Signed-off-by: Chris Ball Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 9e15f41f87be8..fd8f488df5fa0 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1345,7 +1345,7 @@ static void sdhci_tasklet_finish(unsigned long param) * upon error conditions. */ if (!(host->flags & SDHCI_DEVICE_DEAD) && - (mrq->cmd->error || + ((mrq->cmd && mrq->cmd->error) || (mrq->data && (mrq->data->error || (mrq->data->stop && mrq->data->stop->error))) || (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) { From d688cdef6b4638f6e4f8127770468a04758487e3 Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Wed, 27 Apr 2011 17:35:31 -0400 Subject: [PATCH 1599/2556] mmc: sdhci: Check mrq != NULL in sdhci_tasklet_finish commit 0c9c99a765321104cc5f9c97f949382a9ba4927e upstream. It seems that under certain circumstances the sdhci_tasklet_finish() call can be entered with mrq set to NULL, causing the system to crash with a NULL pointer de-reference. Seen on S3C6410 system. Based on a patch by Dimitris Papastamos. Reported-by: Dimitris Papastamos Signed-off-by: Chris Ball Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index fd8f488df5fa0..5d20661bc357b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1334,6 +1334,13 @@ static void sdhci_tasklet_finish(unsigned long param) host = (struct sdhci_host*)param; + /* + * If this tasklet gets rescheduled while running, it will + * be run again afterwards but without any active request. + */ + if (!host->mrq) + return; + spin_lock_irqsave(&host->lock, flags); del_timer(&host->timer); From c8873543070f8593ff52422d55802423ad64be31 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Sun, 1 May 2011 20:16:30 +1000 Subject: [PATCH 1600/2556] drm/radeon: fix regression on atom cards with hardcoded EDID record. commit eaa4f5e1d0b816291a59a47917e569c0384f2b6f upstream. Since fafcf94e2b5732d1e13b440291c53115d2b172e9 introduced an edid size, it seems to have broken this path. This manifest as oops on T500 Lenovo laptops with dual graphics primarily. Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=33812 Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_atombios.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 99768d9d91dac..a73f0e62a0782 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -1599,9 +1599,10 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct memcpy((u8 *)edid, (u8 *)&fake_edid_record->ucFakeEDIDString[0], fake_edid_record->ucFakeEDIDLength); - if (drm_edid_is_valid(edid)) + if (drm_edid_is_valid(edid)) { rdev->mode_info.bios_hardcoded_edid = edid; - else + rdev->mode_info.bios_hardcoded_edid_size = edid_size; + } else kfree(edid); } } From a41ee1d9242adc1cd4eaad4fcae727f778c394a9 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 2 May 2011 14:21:44 -0400 Subject: [PATCH 1601/2556] USB: fix regression in usbip by setting has_tt flag commit cee6a262550f53a13acfefbc1e3e5ff35c96182c upstream. This patch (as1460) fixes a regression in the usbip driver caused by the new check for Transaction Translators in USB-2 hubs. The root hub registered by vhci_hcd needs to have the has_tt flag set, because it can connect to low- and full-speed devices as well as high-speed devices. Signed-off-by: Alan Stern Reported-and-tested-by: Nikola Ciprich Signed-off-by: Greg Kroah-Hartman --- drivers/staging/usbip/vhci_hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c index a35fe61268de3..7284d0c18a4f6 100644 --- a/drivers/staging/usbip/vhci_hcd.c +++ b/drivers/staging/usbip/vhci_hcd.c @@ -1135,7 +1135,7 @@ static int vhci_hcd_probe(struct platform_device *pdev) usbip_uerr("create hcd failed\n"); return -ENOMEM; } - + hcd->has_tt = 1; /* this is private data for vhci_hcd */ the_controller = hcd_to_vhci(hcd); From 74f85b46c2c2ddb5af3c26de627a4701c1951eb6 Mon Sep 17 00:00:00 2001 From: "B.J. Buchalter" Date: Mon, 2 May 2011 13:33:42 -0400 Subject: [PATCH 1602/2556] firewire: Fix for broken configrom updates in quick succession commit 2e053a27d9d5ad5e0831e002cbf8043836fb2060 upstream. Current implementation of ohci_set_config_rom() uses a deferred bus reset via fw_schedule_bus_reset(). If clients add multiple unit descriptors to the config_rom in quick succession, the deferred bus reset may not have fired before succeeding update requests have come in. This can lead to an incorrect partial update of the config_rom for both addition and removal of config_rom descriptors, as the ohci_set_config_rom() routine will return -EBUSY if a previous pending update has not been completed yet; the requested update just gets dropped on the floor. This patch recognizes that the "in-flight" update can be modified until it has been processed by the bus-reset, and the locking in the bus_reset_tasklet ensures that the update is done atomically with respect to modifications made by ohci_set_config_rom(). The -EBUSY error case is simply removed. [Stefan R: The bug always existed at least theoretically. But it became easy to trigger since 2.6.36 commit 02d37bed188c "firewire: core: integrate software-forced bus resets with bus management" which introduced long mandatory delays between janitorial bus resets.] Signed-off-by: Benjamin Buchalter Signed-off-by: Stefan Richter Signed-off-by: Greg Kroah-Hartman --- drivers/firewire/ohci.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index bd3c61b6dd8d5..6a788c3070eab 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -2163,7 +2163,6 @@ static int ohci_set_config_rom(struct fw_card *card, { struct fw_ohci *ohci; unsigned long flags; - int ret = -EBUSY; __be32 *next_config_rom; dma_addr_t uninitialized_var(next_config_rom_bus); @@ -2204,22 +2203,37 @@ static int ohci_set_config_rom(struct fw_card *card, spin_lock_irqsave(&ohci->lock, flags); + /* + * If there is not an already pending config_rom update, + * push our new allocation into the ohci->next_config_rom + * and then mark the local variable as null so that we + * won't deallocate the new buffer. + * + * OTOH, if there is a pending config_rom update, just + * use that buffer with the new config_rom data, and + * let this routine free the unused DMA allocation. + */ + if (ohci->next_config_rom == NULL) { ohci->next_config_rom = next_config_rom; ohci->next_config_rom_bus = next_config_rom_bus; + next_config_rom = NULL; + } - copy_config_rom(ohci->next_config_rom, config_rom, length); + copy_config_rom(ohci->next_config_rom, config_rom, length); - ohci->next_header = config_rom[0]; - ohci->next_config_rom[0] = 0; + ohci->next_header = config_rom[0]; + ohci->next_config_rom[0] = 0; - reg_write(ohci, OHCI1394_ConfigROMmap, - ohci->next_config_rom_bus); - ret = 0; - } + reg_write(ohci, OHCI1394_ConfigROMmap, ohci->next_config_rom_bus); spin_unlock_irqrestore(&ohci->lock, flags); + /* If we didn't use the DMA allocation, delete it. */ + if (next_config_rom != NULL) + dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, + next_config_rom, next_config_rom_bus); + /* * Now initiate a bus reset to have the changes take * effect. We clean up the old config rom memory and DMA @@ -2227,13 +2241,10 @@ static int ohci_set_config_rom(struct fw_card *card, * controller could need to access it before the bus reset * takes effect. */ - if (ret == 0) - fw_schedule_bus_reset(&ohci->card, true, true); - else - dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, - next_config_rom, next_config_rom_bus); - return ret; + fw_schedule_bus_reset(&ohci->card, true, true); + + return 0; } static void ohci_send_request(struct fw_card *card, struct fw_packet *packet) From 4475d537057ad92380f3365ed10f876156a16766 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 27 Apr 2011 09:54:28 +0000 Subject: [PATCH 1603/2556] usbnet: add support for some Huawei modems with cdc-ether ports commit b3c914aa84f4e4bbb3efc8f41c359d96e5e932d2 upstream. Some newer Huawei devices (T-Mobile Rocket, others) have cdc-ether compatible ports, so recognize and expose them. Signed-off-by: Dan Williams Acked-by: Oliver Neukum Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/cdc_ether.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 9a60e415d76be..1189d726419e0 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -458,7 +458,7 @@ static const struct driver_info cdc_info = { .manage_power = cdc_manage_power, }; -static const struct driver_info mbm_info = { +static const struct driver_info wwan_info = { .description = "Mobile Broadband Network Device", .flags = FLAG_WWAN, .bind = cdc_bind, @@ -469,6 +469,7 @@ static const struct driver_info mbm_info = { /*-------------------------------------------------------------------------*/ +#define HUAWEI_VENDOR_ID 0x12D1 static const struct usb_device_id products [] = { /* @@ -578,8 +579,17 @@ static const struct usb_device_id products [] = { }, { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long)&mbm_info, + .driver_info = (unsigned long)&wwan_info, +}, { + /* Various Huawei modems with a network port like the UMG1831 */ + .match_flags = USB_DEVICE_ID_MATCH_VENDOR + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = HUAWEI_VENDOR_ID, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, + .bInterfaceProtocol = 255, + .driver_info = (unsigned long)&wwan_info, }, { }, // END }; From 658bdc683c81e8016a6be9e3d0c1e5aa139a9b5d Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Fri, 1 Apr 2011 14:12:02 -0300 Subject: [PATCH 1604/2556] v4l: make sure drivers supply a zeroed struct v4l2_subdev commit 80845a33165278f3236812009e9c568ba8c29938 upstream. Some v4l drivers currently don't initialize their struct v4l2_subdev with zeros, and this is a problem since some of the v4l2 code expects this. One example is the addition of internal_ops in commit 45f6f84, after that we are at risk of random oopses with these drivers when code in v4l2_device_register_subdev tries to dereference sd->internal_ops->*, as can be shown by the report at http://bugs.launchpad.net/bugs/745213 and analysis of its crash at https://lkml.org/lkml/2011/4/1/168 Use kzalloc within problematic drivers to ensure we have a zeroed struct v4l2_subdev. BugLink: http://bugs.launchpad.net/bugs/745213 Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/radio/saa7706h.c | 2 +- drivers/media/radio/tef6862.c | 2 +- drivers/media/video/m52790.c | 2 +- drivers/media/video/tda9840.c | 2 +- drivers/media/video/tea6415c.c | 2 +- drivers/media/video/tea6420.c | 2 +- drivers/media/video/upd64031a.c | 2 +- drivers/media/video/upd64083.c | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c index 585680ffbfb64..b1193dfc5087b 100644 --- a/drivers/media/radio/saa7706h.c +++ b/drivers/media/radio/saa7706h.c @@ -376,7 +376,7 @@ static int __devinit saa7706h_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%02x (%s)\n", client->addr << 1, client->adapter->name); - state = kmalloc(sizeof(struct saa7706h_state), GFP_KERNEL); + state = kzalloc(sizeof(struct saa7706h_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; sd = &state->sd; diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c index 7c0d77751f6e6..0991e1973678c 100644 --- a/drivers/media/radio/tef6862.c +++ b/drivers/media/radio/tef6862.c @@ -176,7 +176,7 @@ static int __devinit tef6862_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%02x (%s)\n", client->addr << 1, client->adapter->name); - state = kmalloc(sizeof(struct tef6862_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tef6862_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; state->freq = TEF6862_LO_FREQ; diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c index 5e1c9a81984ca..303ffa7df4aca 100644 --- a/drivers/media/video/m52790.c +++ b/drivers/media/video/m52790.c @@ -174,7 +174,7 @@ static int m52790_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - state = kmalloc(sizeof(struct m52790_state), GFP_KERNEL); + state = kzalloc(sizeof(struct m52790_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c index 5d4cf3b3d4350..22fa8202d5ca3 100644 --- a/drivers/media/video/tda9840.c +++ b/drivers/media/video/tda9840.c @@ -171,7 +171,7 @@ static int tda9840_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); if (sd == NULL) return -ENOMEM; v4l2_i2c_subdev_init(sd, client, &tda9840_ops); diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c index 19621ed523ec6..827425c5b866e 100644 --- a/drivers/media/video/tea6415c.c +++ b/drivers/media/video/tea6415c.c @@ -152,7 +152,7 @@ static int tea6415c_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); if (sd == NULL) return -ENOMEM; v4l2_i2c_subdev_init(sd, client, &tea6415c_ops); diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c index 5ea840401f218..f350b6c245001 100644 --- a/drivers/media/video/tea6420.c +++ b/drivers/media/video/tea6420.c @@ -125,7 +125,7 @@ static int tea6420_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); if (sd == NULL) return -ENOMEM; v4l2_i2c_subdev_init(sd, client, &tea6420_ops); diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c index f8138c75be8be..1aab96a882034 100644 --- a/drivers/media/video/upd64031a.c +++ b/drivers/media/video/upd64031a.c @@ -230,7 +230,7 @@ static int upd64031a_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - state = kmalloc(sizeof(struct upd64031a_state), GFP_KERNEL); + state = kzalloc(sizeof(struct upd64031a_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; sd = &state->sd; diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c index 28e0e6b6ca849..9bbe61700fd5c 100644 --- a/drivers/media/video/upd64083.c +++ b/drivers/media/video/upd64083.c @@ -202,7 +202,7 @@ static int upd64083_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - state = kmalloc(sizeof(struct upd64083_state), GFP_KERNEL); + state = kzalloc(sizeof(struct upd64083_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; sd = &state->sd; From a9e7fb583786f69426aae4e569f16c8d34b6bdf5 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Wed, 27 Apr 2011 19:01:44 -0300 Subject: [PATCH 1605/2556] imon: add conditional locking in change_protocol commit 23ef710e1a6c4d6b9ef1c2fa19410f7f1479401e upstream. The imon_ir_change_protocol function gets called two different ways, one way is from rc_register_device, for initial protocol selection/setup, and the other is via a userspace-initiated protocol change request, either by direct sysfs prodding or by something like ir-keytable. In the rc_register_device case, the imon context lock is already held, but when initiated from userspace, it is not, so we must acquire it, prior to calling send_packet, which requires that the lock is held. Without this change, there's an easily reproduceable deadlock when another function calls send_packet (such as either of the display write fops) after a userspace-initiated change_protocol. With a lock-debugging-enabled kernel, I was getting this: [ 15.014153] ===================================== [ 15.015048] [ BUG: bad unlock balance detected! ] [ 15.015048] ------------------------------------- [ 15.015048] ir-keytable/773 is trying to release lock (&ictx->lock) at: [ 15.015048] [] mutex_unlock+0xe/0x10 [ 15.015048] but there are no more locks to release! [ 15.015048] [ 15.015048] other info that might help us debug this: [ 15.015048] 2 locks held by ir-keytable/773: [ 15.015048] #0: (&buffer->mutex){+.+.+.}, at: [] sysfs_write_file+0x3c/0x144 [ 15.015048] #1: (s_active#87){.+.+.+}, at: [] sysfs_write_file+0xe7/0x144 [ 15.015048] [ 15.015048] stack backtrace: [ 15.015048] Pid: 773, comm: ir-keytable Not tainted 2.6.38.4-20.fc15.x86_64.debug #1 [ 15.015048] Call Trace: [ 15.015048] [] ? print_unlock_inbalance_bug+0xca/0xd5 [ 15.015048] [] ? lock_release_non_nested+0xc1/0x263 [ 15.015048] [] ? mutex_unlock+0xe/0x10 [ 15.015048] [] ? mutex_unlock+0xe/0x10 [ 15.015048] [] ? lock_release+0x17d/0x1a4 [ 15.015048] [] ? __mutex_unlock_slowpath+0xc5/0x125 [ 15.015048] [] ? mutex_unlock+0xe/0x10 [ 15.015048] [] ? send_packet+0x1c9/0x264 [imon] [ 15.015048] [] ? lock_release_non_nested+0xdb/0x263 [ 15.015048] [] ? imon_ir_change_protocol+0x126/0x15e [imon] [ 15.015048] [] ? store_protocols+0x1c3/0x286 [rc_core] [ 15.015048] [] ? dev_attr_store+0x20/0x22 [ 15.015048] [] ? sysfs_write_file+0x108/0x144 ... The original report that led to the investigation was the following: [ 1679.457305] INFO: task LCDd:8460 blocked for more than 120 seconds. [ 1679.457307] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. [ 1679.457309] LCDd D ffff88010fcd89c8 0 8460 1 0x00000000 [ 1679.457312] ffff8800d5a03b48 0000000000000082 0000000000000000 ffff8800d5a03fd8 [ 1679.457314] 00000000012dcd30 fffffffffffffffd ffff8800d5a03fd8 ffff88010fcd86f0 [ 1679.457316] ffff8800d5a03fd8 ffff8800d5a03fd8 ffff88010fcd89d0 ffff8800d5a03fd8 [ 1679.457319] Call Trace: [ 1679.457324] [] ? zone_statistics+0x75/0x90 [ 1679.457327] [] ? get_page_from_freelist+0x3c7/0x820 [ 1679.457330] [] __mutex_lock_slowpath+0x139/0x320 [ 1679.457335] [] mutex_lock+0x11/0x30 [ 1679.457338] [] display_open+0x66/0x130 [imon] [ 1679.457345] [] usb_open+0x180/0x310 [usbcore] [ 1679.457349] [] chrdev_open+0x1bb/0x2d0 [ 1679.457350] [] __dentry_open+0x10d/0x370 [ 1679.457352] [] ? chrdev_open+0x0/0x2d0 ... Bump the driver version here so its easier to tell if people have this locking fix or not, and also make locking during probe easier to follow. Reported-by: Benjamin Hodgetts Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/rc/imon.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index e7dc6b46fdfac..eee39570f23db 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -46,7 +46,7 @@ #define MOD_AUTHOR "Jarod Wilson " #define MOD_DESC "Driver for SoundGraph iMON MultiMedia IR/Display" #define MOD_NAME "imon" -#define MOD_VERSION "0.9.2" +#define MOD_VERSION "0.9.3" #define DISPLAY_MINOR_BASE 144 #define DEVICE_NAME "lcd%d" @@ -451,8 +451,9 @@ static int display_close(struct inode *inode, struct file *file) } /** - * Sends a packet to the device -- this function must be called - * with ictx->lock held. + * Sends a packet to the device -- this function must be called with + * ictx->lock held, or its unlock/lock sequence while waiting for tx + * to complete can/will lead to a deadlock. */ static int send_packet(struct imon_context *ictx) { @@ -982,12 +983,21 @@ static void imon_touch_display_timeout(unsigned long data) * the iMON remotes, and those used by the Windows MCE remotes (which is * really just RC-6), but only one or the other at a time, as the signals * are decoded onboard the receiver. + * + * This function gets called two different ways, one way is from + * rc_register_device, for initial protocol selection/setup, and the other is + * via a userspace-initiated protocol change request, either by direct sysfs + * prodding or by something like ir-keytable. In the rc_register_device case, + * the imon context lock is already held, but when initiated from userspace, + * it is not, so we must acquire it prior to calling send_packet, which + * requires that the lock is held. */ static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type) { int retval; struct imon_context *ictx = rc->priv; struct device *dev = ictx->dev; + bool unlock = false; unsigned char ir_proto_packet[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 }; @@ -1020,6 +1030,11 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type) memcpy(ictx->usb_tx_buf, &ir_proto_packet, sizeof(ir_proto_packet)); + if (!mutex_is_locked(&ictx->lock)) { + unlock = true; + mutex_lock(&ictx->lock); + } + retval = send_packet(ictx); if (retval) goto out; @@ -1028,6 +1043,9 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type) ictx->pad_mouse = false; out: + if (unlock) + mutex_unlock(&ictx->lock); + return retval; } @@ -2125,6 +2143,7 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf) goto rdev_setup_failed; } + mutex_unlock(&ictx->lock); return ictx; rdev_setup_failed: @@ -2196,6 +2215,7 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf, goto urb_submit_failed; } + mutex_unlock(&ictx->lock); return ictx; urb_submit_failed: @@ -2290,6 +2310,8 @@ static int __devinit imon_probe(struct usb_interface *interface, usb_set_intfdata(interface, ictx); if (ifnum == 0) { + mutex_lock(&ictx->lock); + if (product == 0xffdc && ictx->rf_device) { sysfs_err = sysfs_create_group(&interface->dev.kobj, &imon_rf_attr_group); @@ -2300,13 +2322,14 @@ static int __devinit imon_probe(struct usb_interface *interface, if (ictx->display_supported) imon_init_display(ictx, interface); + + mutex_unlock(&ictx->lock); } dev_info(dev, "iMON device (%04x:%04x, intf%d) on " "usb<%d:%d> initialized\n", vendor, product, ifnum, usbdev->bus->busnum, usbdev->devnum); - mutex_unlock(&ictx->lock); mutex_unlock(&driver_lock); return 0; From c4ac4195df7fcb85ade58dd0497e273dd10600e7 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 28 Apr 2011 15:55:52 -0400 Subject: [PATCH 1606/2556] flex_array: flex_array_prealloc takes a number of elements, not an end commit 5d30b10bd68df007e7ae21e77d1e0ce184b53040 upstream. Change flex_array_prealloc to take the number of elements for which space should be allocated instead of the last (inclusive) element. Users and documentation are updated accordingly. flex_arrays got introduced before they had users. When folks started using it, they ended up needing a different API than was coded up originally. This swaps over to the API that folks apparently need. Based-on-patch-by: Steffen Klassert Signed-off-by: Eric Paris Tested-by: Chris Richards Acked-by: Dave Hansen Signed-off-by: Greg Kroah-Hartman --- Documentation/flexible-arrays.txt | 4 ++-- include/linux/flex_array.h | 2 +- lib/flex_array.c | 13 ++++++++----- security/selinux/ss/policydb.c | 6 +++--- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Documentation/flexible-arrays.txt b/Documentation/flexible-arrays.txt index cb8a3a00cc926..df904aec99044 100644 --- a/Documentation/flexible-arrays.txt +++ b/Documentation/flexible-arrays.txt @@ -66,10 +66,10 @@ trick is to ensure that any needed memory allocations are done before entering atomic context, using: int flex_array_prealloc(struct flex_array *array, unsigned int start, - unsigned int end, gfp_t flags); + unsigned int nr_elements, gfp_t flags); This function will ensure that memory for the elements indexed in the range -defined by start and end has been allocated. Thereafter, a +defined by start and nr_elements has been allocated. Thereafter, a flex_array_put() call on an element in that range is guaranteed not to block. diff --git a/include/linux/flex_array.h b/include/linux/flex_array.h index 70e4efabe0fb6..ebeb2f3ad068d 100644 --- a/include/linux/flex_array.h +++ b/include/linux/flex_array.h @@ -61,7 +61,7 @@ struct flex_array { struct flex_array *flex_array_alloc(int element_size, unsigned int total, gfp_t flags); int flex_array_prealloc(struct flex_array *fa, unsigned int start, - unsigned int end, gfp_t flags); + unsigned int nr_elements, gfp_t flags); void flex_array_free(struct flex_array *fa); void flex_array_free_parts(struct flex_array *fa); int flex_array_put(struct flex_array *fa, unsigned int element_nr, void *src, diff --git a/lib/flex_array.c b/lib/flex_array.c index c0ea40ba20828..0c33b24498ba5 100644 --- a/lib/flex_array.c +++ b/lib/flex_array.c @@ -232,10 +232,10 @@ EXPORT_SYMBOL(flex_array_clear); /** * flex_array_prealloc - guarantee that array space exists - * @fa: the flex array for which to preallocate parts - * @start: index of first array element for which space is allocated - * @end: index of last (inclusive) element for which space is allocated - * @flags: page allocation flags + * @fa: the flex array for which to preallocate parts + * @start: index of first array element for which space is allocated + * @nr_elements: number of elements for which space is allocated + * @flags: page allocation flags * * This will guarantee that no future calls to flex_array_put() * will allocate memory. It can be used if you are expecting to @@ -245,13 +245,16 @@ EXPORT_SYMBOL(flex_array_clear); * Locking must be provided by the caller. */ int flex_array_prealloc(struct flex_array *fa, unsigned int start, - unsigned int end, gfp_t flags) + unsigned int nr_elements, gfp_t flags) { int start_part; int end_part; int part_nr; + unsigned int end; struct flex_array_part *part; + end = start + nr_elements - 1; + if (start >= fa->total_nr_elements || end >= fa->total_nr_elements) return -ENOSPC; if (elements_fit_in_base(fa)) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 57363562f0f88..f96f09cdd0366 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -497,7 +497,7 @@ static int policydb_index(struct policydb *p) goto out; rc = flex_array_prealloc(p->type_val_to_struct_array, 0, - p->p_types.nprim - 1, GFP_KERNEL | __GFP_ZERO); + p->p_types.nprim, GFP_KERNEL | __GFP_ZERO); if (rc) goto out; @@ -514,7 +514,7 @@ static int policydb_index(struct policydb *p) goto out; rc = flex_array_prealloc(p->sym_val_to_name[i], - 0, p->symtab[i].nprim - 1, + 0, p->symtab[i].nprim, GFP_KERNEL | __GFP_ZERO); if (rc) goto out; @@ -2286,7 +2286,7 @@ int policydb_read(struct policydb *p, void *fp) goto bad; /* preallocate so we don't have to worry about the put ever failing */ - rc = flex_array_prealloc(p->type_attr_map_array, 0, p->p_types.nprim - 1, + rc = flex_array_prealloc(p->type_attr_map_array, 0, p->p_types.nprim, GFP_KERNEL | __GFP_ZERO); if (rc) goto bad; From 18ab890cdc1e014d2ced35a5b8e606871ed5e6fc Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 28 Apr 2011 15:55:52 -0400 Subject: [PATCH 1607/2556] flex_arrays: allow zero length flex arrays commit bf69d41d198138e3c601e9a6645f4f1369aff7e0 upstream. Just like kmalloc will allow one to allocate a 0 length segment of memory flex arrays should do the same thing. It should bomb if you try to use something, but it should at least allow the allocation. This is needed because when SELinux switched to using flex_arrays in 2.6.38 the inability to allocate a 0 length array resulted in SELinux policy load returning -ENOSPC when previously it worked. Based-on-patch-by: Steffen Klassert Signed-off-by: Eric Paris Tested-by: Chris Richards Signed-off-by: Greg Kroah-Hartman --- lib/flex_array.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/flex_array.c b/lib/flex_array.c index 0c33b24498ba5..854b57bd7d9d3 100644 --- a/lib/flex_array.c +++ b/lib/flex_array.c @@ -253,9 +253,16 @@ int flex_array_prealloc(struct flex_array *fa, unsigned int start, unsigned int end; struct flex_array_part *part; + if (!start && !nr_elements) + return 0; + if (start >= fa->total_nr_elements) + return -ENOSPC; + if (!nr_elements) + return 0; + end = start + nr_elements - 1; - if (start >= fa->total_nr_elements || end >= fa->total_nr_elements) + if (end >= fa->total_nr_elements) return -ENOSPC; if (elements_fit_in_base(fa)) return 0; @@ -346,6 +353,8 @@ int flex_array_shrink(struct flex_array *fa) int part_nr; int ret = 0; + if (!fa->total_nr_elements) + return 0; if (elements_fit_in_base(fa)) return ret; for (part_nr = 0; part_nr < FLEX_ARRAY_NR_BASE_PTRS; part_nr++) { From 15f0758f185241ad9c358a5bf60ff0a21eccc218 Mon Sep 17 00:00:00 2001 From: Boris Ostrovsky Date: Fri, 29 Apr 2011 17:47:43 -0400 Subject: [PATCH 1608/2556] x86, AMD: Fix APIC timer erratum 400 affecting K8 Rev.A-E processors commit e20a2d205c05cef6b5783df339a7d54adeb50962 upstream. Older AMD K8 processors (Revisions A-E) are affected by erratum 400 (APIC timer interrupts don't occur in C states greater than C1). This, for example, means that X86_FEATURE_ARAT flag should not be set for these parts. This addresses regression introduced by commit b87cf80af3ba4b4c008b4face3c68d604e1715c6 ("x86, AMD: Set ARAT feature on AMD processors") where the system may become unresponsive until external interrupt (such as keyboard input) occurs. This results, for example, in time not being reported correctly, lack of progress on the system and other lockups. Reported-by: Joerg-Volker Peetz Tested-by: Joerg-Volker Peetz Acked-by: Borislav Petkov Signed-off-by: Boris Ostrovsky Link: http://lkml.kernel.org/r/1304113663-6586-1-git-send-email-ostr@amd64.org Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/amd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index a2b9c7d413230..b65c707716fa3 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -681,7 +681,7 @@ cpu_dev_register(amd_cpu_dev); */ const int amd_erratum_400[] = - AMD_OSVW_ERRATUM(1, AMD_MODEL_RANGE(0xf, 0x41, 0x2, 0xff, 0xf), + AMD_OSVW_ERRATUM(1, AMD_MODEL_RANGE(0x0f, 0x4, 0x2, 0xff, 0xf), AMD_MODEL_RANGE(0x10, 0x2, 0x1, 0xff, 0xf)); EXPORT_SYMBOL_GPL(amd_erratum_400); From bf4b1d070aeb3669d4b4e95c59c404d0e055c41c Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 15 Apr 2011 00:41:43 +0200 Subject: [PATCH 1609/2556] ath9k: fix the return value of ath_stoprecv commit 2232d31bf18ba02f5cd632bbfc3466aeca394c75 upstream. The patch 'ath9k_hw: fix stopping rx DMA during resets' added code to detect a condition where rx DMA was stopped, but the MAC failed to enter the idle state. This condition requires a hardware reset, however the return value of ath_stoprecv was 'true' in that case, which allowed it to skip the reset when issuing a fast channel change. Signed-off-by: Felix Fietkau Reported-by: Paul Stewart Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/recv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 89546bc302906..1e0c1e3b514c7 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -533,7 +533,7 @@ bool ath_stoprecv(struct ath_softc *sc) "confusing the DMA engine when we start RX up\n"); ATH_DBG_WARN_ON_ONCE(!stopped); } - return stopped || reset; + return stopped && !reset; } void ath_flushrecv(struct ath_softc *sc) From 6ee931e9812bdf3774cbd74571d2fdfba037aac9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 19 Apr 2011 20:44:04 +0200 Subject: [PATCH 1610/2556] mac80211: fix SMPS debugfs locking commit 243e6df4ed919880d079d717641ad699c6530a03 upstream. The locking with SMPS requests means that the debugs file should lock the mgd mutex, not the iflist mutex. Calls to __ieee80211_request_smps() need to hold that mutex, so add an assertion. This has always been wrong, but for some reason never been noticed, probably because the locking error only happens while unassociated. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- net/mac80211/cfg.c | 2 ++ net/mac80211/debugfs_netdev.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 9cd73b11506e8..40f7357c26998 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1471,6 +1471,8 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode old_req; int err; + lockdep_assert_held(&sdata->u.mgd.mtx); + old_req = sdata->u.mgd.req_smps; sdata->u.mgd.req_smps = smps_mode; diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 2dabdf7680d06..bae23ad4d8a34 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -172,9 +172,9 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata, if (sdata->vif.type != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; - mutex_lock(&local->iflist_mtx); + mutex_lock(&sdata->u.mgd.mtx); err = __ieee80211_request_smps(sdata, smps_mode); - mutex_unlock(&local->iflist_mtx); + mutex_unlock(&sdata->u.mgd.mtx); return err; } From 7a1bbc3614696ea5e90d728f570f3ded8d49b487 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 24 Apr 2011 01:54:57 +0000 Subject: [PATCH 1611/2556] af_unix: Only allow recv on connected seqpacket sockets. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a05d2ad1c1f391c7f514a1d1e09b5417968a7d07 upstream. This fixes the following oops discovered by Dan Aloni: > Anyway, the following is the output of the Oops that I got on the > Ubuntu kernel on which I first detected the problem > (2.6.37-12-generic). The Oops that followed will be more useful, I > guess. >[ 5594.669852] BUG: unable to handle kernel NULL pointer dereference > at           (null) > [ 5594.681606] IP: [] unix_dgram_recvmsg+0x1fb/0x420 > [ 5594.687576] PGD 2a05d067 PUD 2b951067 PMD 0 > [ 5594.693720] Oops: 0002 [#1] SMP > [ 5594.699888] last sysfs file: The bug was that unix domain sockets use a pseduo packet for connecting and accept uses that psudo packet to get the socket. In the buggy seqpacket case we were allowing unconnected sockets to call recvmsg and try to receive the pseudo packet. That is always wrong and as of commit 7361c36c5 the pseudo packet had become enough different from a normal packet that the kernel started oopsing. Do for seqpacket_recv what was done for seqpacket_send in 2.5 and only allow it on connected seqpacket sockets. Tested-by: Dan Aloni Signed-off-by: Eric W. Biederman Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/unix/af_unix.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 437a99e560e1b..7a79ad0ecf4a7 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -524,6 +524,8 @@ static int unix_dgram_connect(struct socket *, struct sockaddr *, int, int); static int unix_seqpacket_sendmsg(struct kiocb *, struct socket *, struct msghdr *, size_t); +static int unix_seqpacket_recvmsg(struct kiocb *, struct socket *, + struct msghdr *, size_t, int); static const struct proto_ops unix_stream_ops = { .family = PF_UNIX, @@ -583,7 +585,7 @@ static const struct proto_ops unix_seqpacket_ops = { .setsockopt = sock_no_setsockopt, .getsockopt = sock_no_getsockopt, .sendmsg = unix_seqpacket_sendmsg, - .recvmsg = unix_dgram_recvmsg, + .recvmsg = unix_seqpacket_recvmsg, .mmap = sock_no_mmap, .sendpage = sock_no_sendpage, }; @@ -1695,6 +1697,18 @@ static int unix_seqpacket_sendmsg(struct kiocb *kiocb, struct socket *sock, return unix_dgram_sendmsg(kiocb, sock, msg, len); } +static int unix_seqpacket_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t size, + int flags) +{ + struct sock *sk = sock->sk; + + if (sk->sk_state != TCP_ESTABLISHED) + return -ENOTCONN; + + return unix_dgram_recvmsg(iocb, sock, msg, size, flags); +} + static void unix_copy_addr(struct msghdr *msg, struct sock *sk) { struct unix_sock *u = unix_sk(sk); From 36f96751ce09f4ab400e93408cc602d2e080a799 Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Fri, 29 Apr 2011 15:48:07 +0100 Subject: [PATCH 1612/2556] ARM: 6891/1: prevent heap corruption in OABI semtimedop commit 0f22072ab50cac7983f9660d33974b45184da4f9 upstream. When CONFIG_OABI_COMPAT is set, the wrapper for semtimedop does not bound the nsops argument. A sufficiently large value will cause an integer overflow in allocation size, followed by copying too much data into the allocated buffer. Fix this by restricting nsops to SEMOPM. Untested. Signed-off-by: Dan Rosenberg Signed-off-by: Russell King Signed-off-by: Greg Kroah-Hartman --- arch/arm/kernel/sys_oabi-compat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/kernel/sys_oabi-compat.c b/arch/arm/kernel/sys_oabi-compat.c index 4ad8da15ef2b3..af0aaebf4de62 100644 --- a/arch/arm/kernel/sys_oabi-compat.c +++ b/arch/arm/kernel/sys_oabi-compat.c @@ -311,7 +311,7 @@ asmlinkage long sys_oabi_semtimedop(int semid, long err; int i; - if (nsops < 1) + if (nsops < 1 || nsops > SEMOPM) return -EINVAL; sops = kmalloc(sizeof(*sops) * nsops, GFP_KERNEL); if (!sops) From 7b17624a7f6b41678d7452ebd5a525ddc3aaf3d3 Mon Sep 17 00:00:00 2001 From: Lasse Collin Date: Sun, 1 May 2011 19:38:42 +0300 Subject: [PATCH 1613/2556] XZ decompressor: Fix decoding of empty LZMA2 streams commit 646032e3b05b32d3f20cb108a030593d9d792eb5 upstream. The old code considered valid empty LZMA2 streams to be corrupt. Note that a typical empty .xz file has no LZMA2 data at all, and thus most .xz files having no uncompressed data are handled correctly even without this fix. Signed-off-by: Lasse Collin Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- lib/xz/xz_dec_lzma2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/xz/xz_dec_lzma2.c b/lib/xz/xz_dec_lzma2.c index ea5fa4fe9d678..a6cdc969ea42a 100644 --- a/lib/xz/xz_dec_lzma2.c +++ b/lib/xz/xz_dec_lzma2.c @@ -969,6 +969,9 @@ XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s, */ tmp = b->in[b->in_pos++]; + if (tmp == 0x00) + return XZ_STREAM_END; + if (tmp >= 0xE0 || tmp == 0x01) { s->lzma2.need_props = true; s->lzma2.need_dict_reset = false; @@ -1001,9 +1004,6 @@ XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s, lzma_reset(s); } } else { - if (tmp == 0x00) - return XZ_STREAM_END; - if (tmp > 0x02) return XZ_DATA_ERROR; From 6f0a6240e8ce1659574e944eac04e871a9dc1fe7 Mon Sep 17 00:00:00 2001 From: Sachin Prabhu Date: Wed, 20 Apr 2011 13:09:35 +0100 Subject: [PATCH 1614/2556] Open with O_CREAT flag set fails to open existing files on non writable directories commit 1574dff8996ab1ed92c09012f8038b5566fce313 upstream. An open on a NFS4 share using the O_CREAT flag on an existing file for which we have permissions to open but contained in a directory with no write permissions will fail with EACCES. A tcpdump shows that the client had set the open mode to UNCHECKED which indicates that the file should be created if it doesn't exist and encountering an existing flag is not an error. Since in this case the file exists and can be opened by the user, the NFS server is wrong in attempting to check create permissions on the parent directory. The patch adds a conditional statement to check for create permissions only if the file doesn't exist. Signed-off-by: Sachin S. Prabhu Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/vfs.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index da1d9701f8e44..435f407377bbe 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1363,7 +1363,7 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, goto out; if (!(iap->ia_valid & ATTR_MODE)) iap->ia_mode = 0; - err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE); + err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC); if (err) goto out; @@ -1385,6 +1385,13 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, if (IS_ERR(dchild)) goto out_nfserr; + /* If file doesn't exist, check for permissions to create one */ + if (!dchild->d_inode) { + err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE); + if (err) + goto out; + } + err = fh_compose(resfhp, fhp->fh_export, dchild, fhp); if (err) goto out; From ba1ba1c0aaa659914bf9d47616453f6378add40f Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Tue, 19 Apr 2011 20:36:59 -0700 Subject: [PATCH 1615/2556] can: Add missing socket check in can/bcm release. commit c6914a6f261aca0c9f715f883a353ae7ff51fe83 upstream. We can get here with a NULL socket argument passed from userspace, so we need to handle it accordingly. Signed-off-by: Dave Jones Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/can/bcm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 092dc88a7c64c..63779ab2b759c 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1427,9 +1427,14 @@ static int bcm_init(struct sock *sk) static int bcm_release(struct socket *sock) { struct sock *sk = sock->sk; - struct bcm_sock *bo = bcm_sk(sk); + struct bcm_sock *bo; struct bcm_op *op, *next; + if (sk == NULL) + return 0; + + bo = bcm_sk(sk); + /* remove bcm_ops, timer, rx_unregister(), etc. */ unregister_netdevice_notifier(&bo->notifier); From c99afaf0385d61a8e1747511904c66838270b02b Mon Sep 17 00:00:00 2001 From: Timo Warns Date: Thu, 14 Apr 2011 15:21:56 -0700 Subject: [PATCH 1616/2556] fs/partitions/ldm.c: fix oops caused by corrupted partition table commit c340b1d640001c8c9ecff74f68fd90422ae2448a upstream. The kernel automatically evaluates partition tables of storage devices. The code for evaluating LDM partitions (in fs/partitions/ldm.c) contains a bug that causes a kernel oops on certain corrupted LDM partitions. A kernel subsystem seems to crash, because, after the oops, the kernel no longer recognizes newly connected storage devices. The patch validates the value of vblk_size. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Timo Warns Cc: Eugene Teo Cc: Harvey Harrison Cc: Richard Russon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/partitions/ldm.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/fs/partitions/ldm.c b/fs/partitions/ldm.c index b10e3540d5b71..ce4f624404255 100644 --- a/fs/partitions/ldm.c +++ b/fs/partitions/ldm.c @@ -1299,6 +1299,11 @@ static bool ldm_frag_add (const u8 *data, int size, struct list_head *frags) BUG_ON (!data || !frags); + if (size < 2 * VBLK_SIZE_HEAD) { + ldm_error("Value of size is to small."); + return false; + } + group = get_unaligned_be32(data + 0x08); rec = get_unaligned_be16(data + 0x0C); num = get_unaligned_be16(data + 0x0E); @@ -1306,6 +1311,10 @@ static bool ldm_frag_add (const u8 *data, int size, struct list_head *frags) ldm_error ("A VBLK claims to have %d parts.", num); return false; } + if (rec >= num) { + ldm_error("REC value (%d) exceeds NUM value (%d)", rec, num); + return false; + } list_for_each (item, frags) { f = list_entry (item, struct frag, list); @@ -1334,10 +1343,9 @@ static bool ldm_frag_add (const u8 *data, int size, struct list_head *frags) f->map |= (1 << rec); - if (num > 0) { - data += VBLK_SIZE_HEAD; - size -= VBLK_SIZE_HEAD; - } + data += VBLK_SIZE_HEAD; + size -= VBLK_SIZE_HEAD; + memcpy (f->data+rec*(size-VBLK_SIZE_HEAD)+VBLK_SIZE_HEAD, data, size); return true; From fcd9079e754fa9050ed50ef2ba294ccf5825b837 Mon Sep 17 00:00:00 2001 From: Lawrence Rust Date: Wed, 4 May 2011 23:25:10 -0300 Subject: [PATCH 1617/2556] cx88: Fix HVR4000 IR keymap [fixed in .39 in a much different way that is too big to backport to .38 - gregkh] Fixes the RC key input for Nova-S plus, HVR1100, HVR3000 and HVR4000 in the 2.6.38 kernel. Signed-off-by: Lawrence Rust Acked-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx88/cx88-input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index 06f7d1d009440..ac4f8e0750ef1 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -283,7 +283,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_PCHDTV_HD3000: case CX88_BOARD_PCHDTV_HD5500: case CX88_BOARD_HAUPPAUGE_IRONLY: - ir_codes = RC_MAP_HAUPPAUGE_NEW; + ir_codes = RC_MAP_RC5_HAUPPAUGE_NEW; ir->sampling = 1; break; case CX88_BOARD_WINFAST_DTV2000H: From 5ea2c1536c7e93485849c3eb05a022ea26144c6a Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Thu, 3 Feb 2011 15:29:52 +0200 Subject: [PATCH 1618/2556] KVM: SVM: check for progress after IRET interception commit bd3d1ec3d26b61120bb4f60b18ee99aa81839e6b upstream. When we enable an NMI window, we ask for an IRET intercept, since the IRET re-enables NMIs. However, the IRET intercept happens before the instruction executes, while the NMI window architecturally opens afterwards. To compensate for this mismatch, we only open the NMI window in the following exit, assuming that the IRET has by then executed; however, this assumption is not always correct; we may exit due to a host interrupt or page fault, without having executed the instruction. Fix by checking for forward progress by recording and comparing the IRET's rip. This is somewhat of a hack, since an unchaging rip does not mean that no forward progress has been made, but is the simplest fix for now. Signed-off-by: Avi Kivity Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 63fec1531e89b..d8a15a17d7671 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -135,6 +135,8 @@ struct vcpu_svm { u32 *msrpm; + ulong nmi_iret_rip; + struct nested_state nested; bool nmi_singlestep; @@ -2653,6 +2655,7 @@ static int iret_interception(struct vcpu_svm *svm) ++svm->vcpu.stat.nmi_window_exits; clr_intercept(svm, INTERCEPT_IRET); svm->vcpu.arch.hflags |= HF_IRET_MASK; + svm->nmi_iret_rip = kvm_rip_read(&svm->vcpu); return 1; } @@ -3474,7 +3477,12 @@ static void svm_complete_interrupts(struct vcpu_svm *svm) svm->int3_injected = 0; - if (svm->vcpu.arch.hflags & HF_IRET_MASK) { + /* + * If we've made progress since setting HF_IRET_MASK, we've + * executed an IRET and can allow NMI injection. + */ + if ((svm->vcpu.arch.hflags & HF_IRET_MASK) + && kvm_rip_read(&svm->vcpu) != svm->nmi_iret_rip) { svm->vcpu.arch.hflags &= ~(HF_NMI_MASK | HF_IRET_MASK); kvm_make_request(KVM_REQ_EVENT, &svm->vcpu); } From 8fac1f02574d9b19a805d19cea24bc74d19cd3e4 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 3 May 2011 15:15:55 -0400 Subject: [PATCH 1619/2556] drm/radeon/kms: add some new pci ids commit e2c85d8e3974c9041ad7b080846b28d2243e771b upstream. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- include/drm/drm_pciids.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index 6724bf3c1ff11..e893a90a74c55 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -153,6 +153,7 @@ {0x1002, 0x6729, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6739, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x673e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6740, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6741, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6742, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ @@ -185,6 +186,7 @@ {0x1002, 0x688D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6898, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6899, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x689b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \ {0x1002, 0x689c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HEMLOCK|RADEON_NEW_MEMMAP}, \ {0x1002, 0x689d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HEMLOCK|RADEON_NEW_MEMMAP}, \ {0x1002, 0x689e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \ @@ -195,7 +197,9 @@ {0x1002, 0x68b0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x68b8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \ {0x1002, 0x68b9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x68ba, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \ {0x1002, 0x68be, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x68bf, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \ {0x1002, 0x68c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x68c1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x68c7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ From 8bd26179f756612921ff54dbf4f9d2bd0afa01cd Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Wed, 20 Apr 2011 01:57:15 +0000 Subject: [PATCH 1620/2556] can: add missing socket check in can/raw release commit 10022a6c66e199d8f61d9044543f38785713cbbd upstream. v2: added space after 'if' according code style. We can get here with a NULL socket argument passed from userspace, so we need to handle it accordingly. Thanks to Dave Jones pointing at this issue in net/can/bcm.c Signed-off-by: Oliver Hartkopp Signed-off-by: David S. Miller Cc: Chuck Ebbert Signed-off-by: Greg Kroah-Hartman --- net/can/raw.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/can/raw.c b/net/can/raw.c index 883e9d74fddf6..241b2b60c7ee8 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -305,7 +305,12 @@ static int raw_init(struct sock *sk) static int raw_release(struct socket *sock) { struct sock *sk = sock->sk; - struct raw_sock *ro = raw_sk(sk); + struct raw_sock *ro; + + if (!sk) + return 0; + + ro = raw_sk(sk); unregister_netdevice_notifier(&ro->notifier); From a4dbc2902ff426f9ded7542eeb3f347442f7fc1f Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sun, 1 May 2011 09:42:07 -0500 Subject: [PATCH 1621/2556] fix oops in scsi_run_queue() commit c055f5b2614b4f758ae6cc86733f31fa4c2c5844 upstream. The recent commit closing the race window in device teardown: commit 86cbfb5607d4b81b1a993ff689bbd2addd5d3a9b Author: James Bottomley Date: Fri Apr 22 10:39:59 2011 -0500 [SCSI] put stricter guards on queue dead checks is causing a potential NULL deref in scsi_run_queue() because the q->queuedata may already be NULL by the time this function is called. Since we shouldn't be running a queue that is being torn down, simply add a NULL check in scsi_run_queue() to forestall this. Tested-by: Jim Schutt Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_lib.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index fb2bb35c62cbf..415fdf2bd9462 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -400,10 +400,15 @@ static inline int scsi_host_is_busy(struct Scsi_Host *shost) static void scsi_run_queue(struct request_queue *q) { struct scsi_device *sdev = q->queuedata; - struct Scsi_Host *shost = sdev->host; + struct Scsi_Host *shost; LIST_HEAD(starved_list); unsigned long flags; + /* if the device is dead, sdev will be NULL, so no queue to run */ + if (!sdev) + return; + + shost = sdev->host; if (scsi_target(sdev)->single_lun) scsi_single_lun_run(sdev); From 1e79fafb77908cd89d3ece2b297ff21ac55e0bef Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 4 May 2011 21:30:28 -0700 Subject: [PATCH 1622/2556] VM: skip the stack guard page lookup in get_user_pages only for mlock commit a1fde08c74e90accd62d4cfdbf580d2ede938fe7 upstream. The logic in __get_user_pages() used to skip the stack guard page lookup whenever the caller wasn't interested in seeing what the actual page was. But Michel Lespinasse points out that there are cases where we don't care about the physical page itself (so 'pages' may be NULL), but do want to make sure a page is mapped into the virtual address space. So using the existence of the "pages" array as an indication of whether to look up the guard page or not isn't actually so great, and we really should just use the FOLL_MLOCK bit. But because that bit was only set for the VM_LOCKED case (and not all vma's necessarily have it, even for mlock()), we couldn't do that originally. Fix that by moving the VM_LOCKED check deeper into the call-chain, which actually simplifies many things. Now mlock() gets simpler, and we can also check for FOLL_MLOCK in __get_user_pages() and the code ends up much more straightforward. Reported-and-reviewed-by: Michel Lespinasse Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/memory.c | 7 +++---- mm/mlock.c | 5 +---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/mm/memory.c b/mm/memory.c index ab88d09c4a817..8b2bb7dcdae5e 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1359,7 +1359,7 @@ struct page *follow_page(struct vm_area_struct *vma, unsigned long address, */ mark_page_accessed(page); } - if (flags & FOLL_MLOCK) { + if ((flags & FOLL_MLOCK) && (vma->vm_flags & VM_LOCKED)) { /* * The preliminary mapping check is mainly to avoid the * pointless overhead of lock_page on the ZERO_PAGE @@ -1503,10 +1503,9 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, } /* - * If we don't actually want the page itself, - * and it's the stack guard page, just skip it. + * For mlock, just skip the stack guard page. */ - if (!pages && stack_guard_page(vma, start)) + if ((gup_flags & FOLL_MLOCK) && stack_guard_page(vma, start)) goto next_page; do { diff --git a/mm/mlock.c b/mm/mlock.c index da23be42c9067..c8e77909c04fe 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -162,7 +162,7 @@ static long __mlock_vma_pages_range(struct vm_area_struct *vma, VM_BUG_ON(end > vma->vm_end); VM_BUG_ON(!rwsem_is_locked(&mm->mmap_sem)); - gup_flags = FOLL_TOUCH; + gup_flags = FOLL_TOUCH | FOLL_MLOCK; /* * We want to touch writable mappings with a write fault in order * to break COW, except for shared mappings because these don't COW @@ -178,9 +178,6 @@ static long __mlock_vma_pages_range(struct vm_area_struct *vma, if (vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)) gup_flags |= FOLL_FORCE; - if (vma->vm_flags & VM_LOCKED) - gup_flags |= FOLL_MLOCK; - return __get_user_pages(current, mm, addr, nr_pages, gup_flags, NULL, NULL, nonblocking); } From 678562e527fd9979f1765ffa1eb34738fc174425 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 9 May 2011 15:16:23 -0700 Subject: [PATCH 1623/2556] Linux 2.6.38.6 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 20ed7d1955cb2..c74c308507ef7 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 38 -EXTRAVERSION = .5 +EXTRAVERSION = .6 NAME = Flesh-Eating Bats with Fangs # *DOCUMENTATION* From 7eae3abda52902d3f52c62da20295ab1a43cfee1 Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Tue, 5 Apr 2011 13:27:31 -0400 Subject: [PATCH 1624/2556] pmcraid: reject negative request size commit 5f6279da3760ce48f478f2856aacebe0c59a39f3 upstream. There's a code path in pmcraid that can be reached via device ioctl that causes all sorts of ugliness, including heap corruption or triggering the OOM killer due to consecutive allocation of large numbers of pages. Not especially relevant from a security perspective, since users must have CAP_SYS_ADMIN to open the character device. First, the user can call pmcraid_chr_ioctl() with a type PMCRAID_PASSTHROUGH_IOCTL. A pmcraid_passthrough_ioctl_buffer is copied in, and the request_size variable is set to buffer->ioarcb.data_transfer_length, which is an arbitrary 32-bit signed value provided by the user. If a negative value is provided here, bad things can happen. For example, pmcraid_build_passthrough_ioadls() is called with this request_size, which immediately calls pmcraid_alloc_sglist() with a negative size. The resulting math on allocating a scatter list can result in an overflow in the kzalloc() call (if num_elem is 0, the sglist will be smaller than expected), or if num_elem is unexpectedly large the subsequent loop will call alloc_pages() repeatedly, a high number of pages will be allocated and the OOM killer might be invoked. Prevent this value from being negative in pmcraid_ioctl_passthrough(). Signed-off-by: Dan Rosenberg Cc: Anil Ravindranath Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/pmcraid.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 321cf3ae86308..ce0701d512f42 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -3814,6 +3814,9 @@ static long pmcraid_ioctl_passthrough( rc = -EFAULT; goto out_free_buffer; } + } else if (request_size < 0) { + rc = -EINVAL; + goto out_free_buffer; } /* check if we have any additional command parameters */ From b044f30938d89c5f31c3f3b41c5d07a1b8f2bf56 Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Tue, 5 Apr 2011 12:45:59 -0400 Subject: [PATCH 1625/2556] mpt2sas: prevent heap overflows and unchecked reads commit a1f74ae82d133ebb2aabb19d181944b4e83e9960 upstream. At two points in handling device ioctls via /dev/mpt2ctl, user-supplied length values are used to copy data from userspace into heap buffers without bounds checking, allowing controllable heap corruption and subsequently privilege escalation. Additionally, user-supplied values are used to determine the size of a copy_to_user() as well as the offset into the buffer to be read, with no bounds checking, allowing users to read arbitrary kernel memory. Signed-off-by: Dan Rosenberg Acked-by: Eric Moore Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/mpt2sas/mpt2sas_ctl.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c index e92b77af54849..3834c95cffa22 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c @@ -688,6 +688,13 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, goto out; } + /* Check for overflow and wraparound */ + if (karg.data_sge_offset * 4 > ioc->request_sz || + karg.data_sge_offset > (UINT_MAX / 4)) { + ret = -EINVAL; + goto out; + } + /* copy in request message frame from user */ if (copy_from_user(mpi_request, mf, karg.data_sge_offset*4)) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, @@ -1963,7 +1970,7 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state) Mpi2DiagBufferPostReply_t *mpi_reply; int rc, i; u8 buffer_type; - unsigned long timeleft; + unsigned long timeleft, request_size, copy_size; u16 smid; u16 ioc_status; u8 issue_reset = 0; @@ -1999,6 +2006,8 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state) return -ENOMEM; } + request_size = ioc->diag_buffer_sz[buffer_type]; + if ((karg.starting_offset % 4) || (karg.bytes_to_read % 4)) { printk(MPT2SAS_ERR_FMT "%s: either the starting_offset " "or bytes_to_read are not 4 byte aligned\n", ioc->name, @@ -2006,13 +2015,23 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state) return -EINVAL; } + if (karg.starting_offset > request_size) + return -EINVAL; + diag_data = (void *)(request_data + karg.starting_offset); dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: diag_buffer(%p), " "offset(%d), sz(%d)\n", ioc->name, __func__, diag_data, karg.starting_offset, karg.bytes_to_read)); + /* Truncate data on requests that are too large */ + if ((diag_data + karg.bytes_to_read < diag_data) || + (diag_data + karg.bytes_to_read > request_data + request_size)) + copy_size = request_size - karg.starting_offset; + else + copy_size = karg.bytes_to_read; + if (copy_to_user((void __user *)uarg->diagnostic_data, - diag_data, karg.bytes_to_read)) { + diag_data, copy_size)) { printk(MPT2SAS_ERR_FMT "%s: Unable to write " "mpt_diag_read_buffer_t data @ %p\n", ioc->name, __func__, diag_data); From 9655e28af14523e859f73ba759260afb37412ca1 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Fri, 8 Apr 2011 15:05:36 -0400 Subject: [PATCH 1626/2556] scsi_dh: fix reference counting in scsi_dh_activate error path commit 0b8393578c70bc1f09790eeae7d918f38da2e010 upstream. Commit db422318cbca55168cf965f655471dbf8be82433 ([SCSI] scsi_dh: propagate SCSI device deletion) introduced a regression where the device reference is not dropped prior to scsi_dh_activate's early return from the error path. Signed-off-by: Mike Snitzer Reviewed-by: Mike Christie Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/device_handler/scsi_dh.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index b837c5b3c8f9d..1367b919c4937 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c @@ -437,12 +437,14 @@ int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) unsigned long flags; struct scsi_device *sdev; struct scsi_device_handler *scsi_dh = NULL; + struct device *dev = NULL; spin_lock_irqsave(q->queue_lock, flags); sdev = q->queuedata; if (sdev && sdev->scsi_dh_data) scsi_dh = sdev->scsi_dh_data->scsi_dh; - if (!scsi_dh || !get_device(&sdev->sdev_gendev) || + dev = get_device(&sdev->sdev_gendev); + if (!scsi_dh || !dev || sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL) err = SCSI_DH_NOSYS; @@ -453,12 +455,13 @@ int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) if (err) { if (fn) fn(data, err); - return err; + goto out; } if (scsi_dh->activate) err = scsi_dh->activate(sdev, fn, data); - put_device(&sdev->sdev_gendev); +out: + put_device(dev); return err; } EXPORT_SYMBOL_GPL(scsi_dh_activate); From 144fdfd4994b23531bb7b55157b2e33644e34a8b Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 22 Apr 2011 10:39:59 -0500 Subject: [PATCH 1627/2556] put stricter guards on queue dead checks commit 86cbfb5607d4b81b1a993ff689bbd2addd5d3a9b upstream. SCSI uses request_queue->queuedata == NULL as a signal that the queue is dying. We set this state in the sdev release function. However, this allows a small window where we release the last reference but haven't quite got to this stage yet and so something will try to take a reference in scsi_request_fn and oops. It's very rare, but we had a report here, so we're pushing this as a bug fix The actual fix is to set request_queue->queuedata to NULL in scsi_remove_device() before we drop the reference. This causes correct automatic rejects from scsi_request_fn as people who hold additional references try to submit work and prevents anything from getting a new reference to the sdev that way. Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_sysfs.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 490ce213204e9..360b7cbadb446 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -322,14 +322,8 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work) kfree(evt); } - if (sdev->request_queue) { - sdev->request_queue->queuedata = NULL; - /* user context needed to free queue */ - scsi_free_queue(sdev->request_queue); - /* temporary expedient, try to catch use of queue lock - * after free of sdev */ - sdev->request_queue = NULL; - } + /* NULL queue means the device can't be used */ + sdev->request_queue = NULL; scsi_target_reap(scsi_target(sdev)); @@ -937,6 +931,12 @@ void __scsi_remove_device(struct scsi_device *sdev) if (sdev->host->hostt->slave_destroy) sdev->host->hostt->slave_destroy(sdev); transport_destroy_device(dev); + + /* cause the request function to reject all I/O requests */ + sdev->request_queue->queuedata = NULL; + + /* Freeing the queue signals to block that we're done */ + scsi_free_queue(sdev->request_queue); put_device(dev); } From 3a7d0cf5436d343712b64f21130dc7aa029eecee Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 29 Apr 2011 14:10:55 +0200 Subject: [PATCH 1628/2556] ALSA: HDA: Fix automute for Gateway NV79 commit 94024cd1aefa0f8bcc9dfe46c05bd7ce3f471a1c upstream. The PCI SSID is 1025:031c and the codec SSID is 1025:031d, so the driver mistakes this for a SKU value, but looking at the numbers, this is obviously wrong. BugLink: http://bugs.launchpad.net/bugs/761861 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8ff0223fb10f2..ee365446f0582 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -19556,6 +19556,7 @@ enum { ALC272_FIXUP_MARIO, ALC662_FIXUP_CZC_P10T, ALC662_FIXUP_GIGABYTE, + ALC662_FIXUP_SKU_IGNORE, }; static const struct alc_fixup alc662_fixups[] = { @@ -19591,10 +19592,15 @@ static const struct alc_fixup alc662_fixups[] = { { } } }, + [ALC662_FIXUP_SKU_IGNORE] = { + .type = ALC_FIXUP_SKU, + .v.sku = ALC_FIXUP_SKU_IGNORE, + }, }; static struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte", ALC662_FIXUP_GIGABYTE), From 2f81508bfe5bbf3882853220e43b6e33378da32f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 May 2011 10:37:29 +0200 Subject: [PATCH 1629/2556] Revert "ALSA: hda - Fix pin-config of Gigabyte mobo" commit ebb47241ea0eac6a5a23404821a2d62f64c68496 upstream. This reverts commit c6b358748e19ce7e230b0926ac42696bc485a562. It turned out that there are different pin configurations for this PCI SSID, including multi-channel modes. And more proper fix for allowing line-out mutes will come up in 2.6.40 tree, so we won't need this fixup any more there. Reported-by: Andrew Clayton Reported-by: Emmanuel Benisty Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ee365446f0582..d7e251bc58f70 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9932,6 +9932,7 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = { SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL), SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG), @@ -10768,7 +10769,6 @@ enum { PINFIX_LENOVO_Y530, PINFIX_PB_M5210, PINFIX_ACER_ASPIRE_7736, - PINFIX_GIGABYTE_880GM, }; static const struct alc_fixup alc882_fixups[] = { @@ -10800,13 +10800,6 @@ static const struct alc_fixup alc882_fixups[] = { .type = ALC_FIXUP_SKU, .v.sku = ALC_FIXUP_SKU_IGNORE, }, - [PINFIX_GIGABYTE_880GM] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x1114410 }, /* set as speaker */ - { } - } - }, }; static struct snd_pci_quirk alc882_fixup_tbl[] = { @@ -10814,7 +10807,6 @@ static struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", PINFIX_LENOVO_Y530), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX), SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte", PINFIX_GIGABYTE_880GM), {} }; @@ -18882,6 +18874,8 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = { ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x1179, 0xff6e, "Toshiba NB20x", ALC662_AUTO), SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L", + ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x152d, 0x2304, "Quanta WH1", ALC663_ASUS_H13), SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG), SND_PCI_QUIRK(0x1631, 0xc10c, "PB RS65", ALC663_ASUS_M51VA), @@ -19555,7 +19549,6 @@ enum { ALC662_FIXUP_IDEAPAD, ALC272_FIXUP_MARIO, ALC662_FIXUP_CZC_P10T, - ALC662_FIXUP_GIGABYTE, ALC662_FIXUP_SKU_IGNORE, }; @@ -19585,13 +19578,6 @@ static const struct alc_fixup alc662_fixups[] = { {} } }, - [ALC662_FIXUP_GIGABYTE] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { - { 0x14, 0x1114410 }, /* set as speaker */ - { } - } - }, [ALC662_FIXUP_SKU_IGNORE] = { .type = ALC_FIXUP_SKU, .v.sku = ALC_FIXUP_SKU_IGNORE, @@ -19603,7 +19589,6 @@ static struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), - SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte", ALC662_FIXUP_GIGABYTE), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), From 97788c2838bde60650ebbc2cb4f02f14e807d6b4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 May 2011 13:55:36 +0200 Subject: [PATCH 1630/2556] ALSA: hda - Fix Realtek's chained fixup checks commit 24af2b1cc418d6791b1d9e56bf6070cccb752db3 upstream. The check of chained fixup list entry was done against the wrong element. A stupid mistake during refactoring. Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d7e251bc58f70..24a3acb63f691 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1774,11 +1774,11 @@ static void alc_apply_fixup(struct hda_codec *codec, int action) codec->chip_name, fix->type); break; } - if (!fix[id].chained) + if (!fix->chained) break; if (++depth > 10) break; - id = fix[id].chain_id; + id = fix->chain_id; } } From f1ef03a54093acd99b7b99198be17b5f00170594 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 1 May 2011 18:18:49 +0200 Subject: [PATCH 1631/2556] i2c-parport: Fix adapter list handling commit 56acc7a39ac4ac7638cdc32cd3d0832ebbc834e4 upstream. Use a standard list with proper locking to handle the list of adapters. Thankfully it only matters on systems with more than one parallel port, which are very rare. Thanks to Lukasz Kapiec for reporting the problem to me. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-parport.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c index 0eb1515541e72..2dbba163b1020 100644 --- a/drivers/i2c/busses/i2c-parport.c +++ b/drivers/i2c/busses/i2c-parport.c @@ -1,7 +1,7 @@ /* ------------------------------------------------------------------------ * * i2c-parport.c I2C bus over parallel port * * ------------------------------------------------------------------------ * - Copyright (C) 2003-2010 Jean Delvare + Copyright (C) 2003-2011 Jean Delvare Based on older i2c-philips-par.c driver Copyright (C) 1995-2000 Simon G. Vogl @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include "i2c-parport.h" /* ----- Device list ------------------------------------------------------ */ @@ -43,10 +45,11 @@ struct i2c_par { struct i2c_algo_bit_data algo_data; struct i2c_smbus_alert_setup alert_data; struct i2c_client *ara; - struct i2c_par *next; + struct list_head node; }; -static struct i2c_par *adapter_list; +static LIST_HEAD(adapter_list); +static DEFINE_MUTEX(adapter_list_lock); /* ----- Low-level parallel port access ----------------------------------- */ @@ -228,8 +231,9 @@ static void i2c_parport_attach (struct parport *port) } /* Add the new adapter to the list */ - adapter->next = adapter_list; - adapter_list = adapter; + mutex_lock(&adapter_list_lock); + list_add_tail(&adapter->node, &adapter_list); + mutex_unlock(&adapter_list_lock); return; ERROR1: @@ -241,11 +245,11 @@ static void i2c_parport_attach (struct parport *port) static void i2c_parport_detach (struct parport *port) { - struct i2c_par *adapter, *prev; + struct i2c_par *adapter, *_n; /* Walk the list */ - for (prev = NULL, adapter = adapter_list; adapter; - prev = adapter, adapter = adapter->next) { + mutex_lock(&adapter_list_lock); + list_for_each_entry_safe(adapter, _n, &adapter_list, node) { if (adapter->pdev->port == port) { if (adapter->ara) { parport_disable_irq(port); @@ -259,14 +263,11 @@ static void i2c_parport_detach (struct parport *port) parport_release(adapter->pdev); parport_unregister_device(adapter->pdev); - if (prev) - prev->next = adapter->next; - else - adapter_list = adapter->next; + list_del(&adapter->node); kfree(adapter); - return; } } + mutex_unlock(&adapter_list_lock); } static struct parport_driver i2c_parport_driver = { From 8f19f677a6509cd31682c5595760834dccc4e27f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 29 Apr 2011 18:08:37 +0200 Subject: [PATCH 1632/2556] workqueue: fix deadlock in worker_maybe_bind_and_lock() commit 5035b20fa5cd146b66f5f89619c20a4177fb736d upstream. If a rescuer and stop_machine() bringing down a CPU race with each other, they may deadlock on non-preemptive kernel. The CPU won't accept a new task, so the rescuer can't migrate to the target CPU, while stop_machine() can't proceed because the rescuer is holding one of the CPU retrying migration. GCWQ_DISASSOCIATED is never cleared and worker_maybe_bind_and_lock() retries indefinitely. This problem can be reproduced semi reliably while the system is entering suspend. http://thread.gmane.org/gmane.linux.kernel/1122051 A lot of kudos to Thilo-Alexander for reporting this tricky issue and painstaking testing. stable: This affects all kernels with cmwq, so all kernels since and including v2.6.36 need this fix. Signed-off-by: Tejun Heo Reported-by: Thilo-Alexander Ginkel Tested-by: Thilo-Alexander Ginkel Signed-off-by: Greg Kroah-Hartman --- kernel/workqueue.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index ee6578b578ad3..7b65ae747c6fd 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1283,8 +1283,14 @@ __acquires(&gcwq->lock) return true; spin_unlock_irq(&gcwq->lock); - /* CPU has come up inbetween, retry migration */ + /* + * We've raced with CPU hot[un]plug. Give it a breather + * and retry migration. cond_resched() is required here; + * otherwise, we might deadlock against cpu_stop trying to + * bring down the CPU on non-preemptive kernel. + */ cpu_relax(); + cond_resched(); } } From b4d904ac728ef74e91bb11caaa6217d1d74e4062 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 20 Apr 2011 15:57:14 +0200 Subject: [PATCH 1633/2556] iwlwifi: fix skb usage after free commit b25026981aecde3685dd0e45ad980fff9f528daa upstream. Since commit a120e912eb51e347f36c71b60a1d13af74d30e83 Author: Stanislaw Gruszka Date: Fri Feb 19 15:47:33 2010 -0800 iwlwifi: sanity check before counting number of tfds can be free we use skb->data after calling ieee80211_tx_status_irqsafe(), which could free skb instantly. On current kernels I do not observe practical problems related with bug, but on 2.6.35.y it cause random system hangs when stressing wireless link. Signed-off-by: Stanislaw Gruszka Acked-by: Wey-Yi Guy Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index 24a11b8f73bc1..f5aaf31c4c15e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -1207,12 +1207,16 @@ int iwlagn_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index) q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { tx_info = &txq->txb[txq->q.read_ptr]; - iwlagn_tx_status(priv, tx_info, - txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); + + if (WARN_ON_ONCE(tx_info->skb == NULL)) + continue; hdr = (struct ieee80211_hdr *)tx_info->skb->data; - if (hdr && ieee80211_is_data_qos(hdr->frame_control)) + if (ieee80211_is_data_qos(hdr->frame_control)) nfreed++; + + iwlagn_tx_status(priv, tx_info, + txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); tx_info->skb = NULL; if (priv->cfg->ops->lib->txq_inval_byte_cnt_tbl) From 237f85236bc67a6353971605a4223a513932a57b Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Fri, 29 Apr 2011 17:51:06 +0200 Subject: [PATCH 1634/2556] iwlagn: fix "Received BA when not expected" commit bfd36103ec26599557c2bd3225a1f1c9267f8fcb upstream. Need to use broadcast sta_id for management frames, otherwise we broke BA session in the firmware and get messages like that: "Received BA when not expected" or (on older kernels): "BA scd_flow 0 does not match txq_id 10" This fix regression introduced in 2.6.35 during station management code rewrite by: commit 2a87c26bbe9587baeb9e56d3ce0b4971bd777643 Author: Johannes Berg Date: Fri Apr 30 11:30:45 2010 -0700 iwlwifi: use iwl_find_station less Patch partially resolve: https://bugzilla.kernel.org/show_bug.cgi?id=16691 However, there are still 11n performance problems on 4965 and 5xxx devices that need to be investigated. Signed-off-by: Stanislaw Gruszka Acked-by: Johannes Berg Acked-by: Wey-Yi Guy Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index f5aaf31c4c15e..c13542b495fed 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -561,12 +561,17 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) hdr_len = ieee80211_hdrlen(fc); - /* Find index into station table for destination station */ - sta_id = iwl_sta_id_or_broadcast(priv, ctx, info->control.sta); - if (sta_id == IWL_INVALID_STATION) { - IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", - hdr->addr1); - goto drop_unlock; + /* For management frames use broadcast id to do not break aggregation */ + if (!ieee80211_is_data(fc)) + sta_id = ctx->bcast_sta_id; + else { + /* Find index into station table for destination station */ + sta_id = iwl_sta_id_or_broadcast(priv, ctx, info->control.sta); + if (sta_id == IWL_INVALID_STATION) { + IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", + hdr->addr1); + goto drop_unlock; + } } IWL_DEBUG_TX(priv, "station Id %d\n", sta_id); From 43a7385ed9fe8db12c140c47d9b37e336a48f7e1 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Wed, 20 Apr 2011 09:00:49 +0000 Subject: [PATCH 1635/2556] atl1c: Fix work event interrupt/task races commit cb771838715b1c470bc5735bdae709b33b18e0ad upstream. The mechanism used to initiate work events from the interrupt handler has a classic read/modify/write race between the interrupt handler that sets the condition, and the worker task that reads and clears the condition. Close these races by using atomic bit fields. Cc: Jie Yang Signed-off-by: Tim Gardner Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/atl1c/atl1c.h | 6 +++--- drivers/net/atl1c/atl1c_main.c | 14 +++++--------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/net/atl1c/atl1c.h b/drivers/net/atl1c/atl1c.h index 9ab58097fa2e7..dec81102adc29 100644 --- a/drivers/net/atl1c/atl1c.h +++ b/drivers/net/atl1c/atl1c.h @@ -566,9 +566,9 @@ struct atl1c_adapter { #define __AT_TESTING 0x0001 #define __AT_RESETTING 0x0002 #define __AT_DOWN 0x0003 - u8 work_event; -#define ATL1C_WORK_EVENT_RESET 0x01 -#define ATL1C_WORK_EVENT_LINK_CHANGE 0x02 + unsigned long work_event; +#define ATL1C_WORK_EVENT_RESET 0 +#define ATL1C_WORK_EVENT_LINK_CHANGE 1 u32 msg_enable; bool have_msi; diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c index 3824382faecc1..dffc7f72e7d78 100644 --- a/drivers/net/atl1c/atl1c_main.c +++ b/drivers/net/atl1c/atl1c_main.c @@ -325,7 +325,7 @@ static void atl1c_link_chg_event(struct atl1c_adapter *adapter) } } - adapter->work_event |= ATL1C_WORK_EVENT_LINK_CHANGE; + set_bit(ATL1C_WORK_EVENT_LINK_CHANGE, &adapter->work_event); schedule_work(&adapter->common_task); } @@ -337,20 +337,16 @@ static void atl1c_common_task(struct work_struct *work) adapter = container_of(work, struct atl1c_adapter, common_task); netdev = adapter->netdev; - if (adapter->work_event & ATL1C_WORK_EVENT_RESET) { - adapter->work_event &= ~ATL1C_WORK_EVENT_RESET; + if (test_and_clear_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event)) { netif_device_detach(netdev); atl1c_down(adapter); atl1c_up(adapter); netif_device_attach(netdev); - return; } - if (adapter->work_event & ATL1C_WORK_EVENT_LINK_CHANGE) { - adapter->work_event &= ~ATL1C_WORK_EVENT_LINK_CHANGE; + if (test_and_clear_bit(ATL1C_WORK_EVENT_LINK_CHANGE, + &adapter->work_event)) atl1c_check_link_status(adapter); - } - return; } @@ -369,7 +365,7 @@ static void atl1c_tx_timeout(struct net_device *netdev) struct atl1c_adapter *adapter = netdev_priv(netdev); /* Do the reset outside of interrupt context */ - adapter->work_event |= ATL1C_WORK_EVENT_RESET; + set_bit(ATL1C_WORK_EVENT_RESET, &adapter->work_event); schedule_work(&adapter->common_task); } From f30c4957dfc87de6bce9a9397fa0388630d7e9de Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 25 Apr 2011 18:17:09 +0300 Subject: [PATCH 1636/2556] UBIFS: do not free write-buffers when in R/O mode commit b50b9f408502a2ea90459ae36ba8cdc9cc005cfe upstream. Currently UBIFS has a small optimization - it frees write-buffers when it is re-mounted from R/W mode to R/O mode. Of course, when it is mounted R/O, it does not allocate write-buffers as well. This optimization is nice but it leads to subtle problems and complications in recovery, which I can reproduce using the integck test. The symptoms are that after a power cut the file-system cannot be mounted if we first mount it R/O, and then re-mount R/W - 'ubifs_rcvry_gc_commit()' prints: UBIFS error (pid 34456): could not find an empty LEB Analysis of the problem. When mounting R/W, the reply process sets journal heads to buds [1], but when mounting R/O - it does not do this, because the write-buffers are not allocated. So 'ubifs_rcvry_gc_commit()' works completely differently for the same file-system but for the following 2 cases: 1. mounting R/W after a power cut and recover 2. mounting R/O after a power cut, re-mounting R/W and run deferred recovery In the former case, we have journal heads seeked to the a bud, in the latter case, they are non-seeked (wbuf->lnum == -1). So in the latter case we do not try to recover the GC LEB by garbage-collecting to the GC head, but we just try to find an empty LEB, and there may be no empty LEBs, so we just fail. On the other hand, in the former case (mount R/W), we are able to make a GC LEB (@c->gc_lnum) by garbage-collecting. Thus, let's remove this small nice optimization and always allocate write-buffers. This should not make too big difference - we have only 3 of them, each of max. write unit size, which is usually 2KiB. So this is about 6KiB of RAM for the typical case, and only when mounted R/O. [1]: Note, currently the replay process is setting (seeking) the journal heads to _some_ buds, not necessarily to the buds which had been the journal heads before the power cut happened. This will be fixed separately. Signed-off-by: Artem Bityutskiy Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/log.c | 20 -------------------- fs/ubifs/super.c | 15 ++++----------- 2 files changed, 4 insertions(+), 31 deletions(-) diff --git a/fs/ubifs/log.c b/fs/ubifs/log.c index 4d0cb1241460f..40fa780ebea7d 100644 --- a/fs/ubifs/log.c +++ b/fs/ubifs/log.c @@ -174,26 +174,6 @@ void ubifs_add_bud(struct ubifs_info *c, struct ubifs_bud *bud) spin_unlock(&c->buds_lock); } -/** - * ubifs_create_buds_lists - create journal head buds lists for remount rw. - * @c: UBIFS file-system description object - */ -void ubifs_create_buds_lists(struct ubifs_info *c) -{ - struct rb_node *p; - - spin_lock(&c->buds_lock); - p = rb_first(&c->buds); - while (p) { - struct ubifs_bud *bud = rb_entry(p, struct ubifs_bud, rb); - struct ubifs_jhead *jhead = &c->jheads[bud->jhead]; - - list_add_tail(&bud->list, &jhead->buds_list); - p = rb_next(p); - } - spin_unlock(&c->buds_lock); -} - /** * ubifs_add_bud_to_log - add a new bud to the log. * @c: UBIFS file-system description object diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index e94d9628a49e4..e20cb5a9aee54 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1235,12 +1235,12 @@ static int mount_ubifs(struct ubifs_info *c) goto out_free; } + err = alloc_wbufs(c); + if (err) + goto out_cbuf; + sprintf(c->bgt_name, BGT_NAME_PATTERN, c->vi.ubi_num, c->vi.vol_id); if (!c->ro_mount) { - err = alloc_wbufs(c); - if (err) - goto out_cbuf; - /* Create background thread */ c->bgt = kthread_create(ubifs_bg_thread, c, "%s", c->bgt_name); if (IS_ERR(c->bgt)) { @@ -1603,12 +1603,6 @@ static int ubifs_remount_rw(struct ubifs_info *c) if (err) goto out; - err = alloc_wbufs(c); - if (err) - goto out; - - ubifs_create_buds_lists(c); - /* Create background thread */ c->bgt = kthread_create(ubifs_bg_thread, c, "%s", c->bgt_name); if (IS_ERR(c->bgt)) { @@ -1717,7 +1711,6 @@ static void ubifs_remount_ro(struct ubifs_info *c) if (err) ubifs_ro_mode(c, err); - free_wbufs(c); vfree(c->orph_buf); c->orph_buf = NULL; vfree(c->ileb_buf); From 023d2bb9bbc68ef536abfbaf2183446f8ab09250 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 25 Apr 2011 18:46:31 +0300 Subject: [PATCH 1637/2556] UBIFS: seek journal heads to the latest bud in replay commit 52c6e6f990669deac3f370f1603815adb55a1dbd upstream. This is the second fix of the following symptom: UBIFS error (pid 34456): could not find an empty LEB which sometimes happens after power cuts when we mount the file-system - UBIFS refuses it with the above error message which comes from the 'ubifs_rcvry_gc_commit()' function. I can reproduce this using the integck test with the UBIFS power cut emulation enabled. Analysis of the problem. Currently UBIFS replay seeks the journal heads to the last _replayed_ bud. But the buds are replayed out-of-order, so the replay basically seeks journal heads to the "random" bud belonging to this head, and not to the _last_ one. The result of this is that the GC head may be seeked to a full LEB with no free space, or very little free space. And 'ubifs_rcvry_gc_commit()' tries to find a fully or mostly dirty LEB to match the current GC head (because we need to garbage-collect that dirty LEB at one go, because we do not have @c->gc_lnum). So 'ubifs_find_dirty_leb()' fails and we fall back to finding an empty LEB and also fail. As a result - recovery fails and mounting fails. This patch teaches the replay to initialize the GC heads exactly to the latest buds, i.e. the buds which have the largest sequence number in corresponding log reference nodes. Signed-off-by: Artem Bityutskiy Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/replay.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c index eed0fcff8d731..d3d6d365bfc11 100644 --- a/fs/ubifs/replay.c +++ b/fs/ubifs/replay.c @@ -59,6 +59,7 @@ enum { * @new_size: truncation new size * @free: amount of free space in a bud * @dirty: amount of dirty space in a bud from padding and deletion nodes + * @jhead: journal head number of the bud * * UBIFS journal replay must compare node sequence numbers, which means it must * build a tree of node information to insert into the TNC. @@ -80,6 +81,7 @@ struct replay_entry { struct { int free; int dirty; + int jhead; }; }; }; @@ -159,6 +161,11 @@ static int set_bud_lprops(struct ubifs_info *c, struct replay_entry *r) err = PTR_ERR(lp); goto out; } + + /* Make sure the journal head points to the latest bud */ + err = ubifs_wbuf_seek_nolock(&c->jheads[r->jhead].wbuf, r->lnum, + c->leb_size - r->free, UBI_SHORTTERM); + out: ubifs_release_lprops(c); return err; @@ -627,10 +634,6 @@ static int replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, ubifs_assert(sleb->endpt - offs >= used); ubifs_assert(sleb->endpt % c->min_io_size == 0); - if (sleb->endpt + c->min_io_size <= c->leb_size && !c->ro_mount) - err = ubifs_wbuf_seek_nolock(&c->jheads[jhead].wbuf, lnum, - sleb->endpt, UBI_SHORTTERM); - *dirty = sleb->endpt - offs - used; *free = c->leb_size - sleb->endpt; @@ -653,12 +656,14 @@ static int replay_bud(struct ubifs_info *c, int lnum, int offs, int jhead, * @sqnum: sequence number * @free: amount of free space in bud * @dirty: amount of dirty space from padding and deletion nodes + * @jhead: journal head number for the bud * * This function inserts a reference node to the replay tree and returns zero * in case of success or a negative error code in case of failure. */ static int insert_ref_node(struct ubifs_info *c, int lnum, int offs, - unsigned long long sqnum, int free, int dirty) + unsigned long long sqnum, int free, int dirty, + int jhead) { struct rb_node **p = &c->replay_tree.rb_node, *parent = NULL; struct replay_entry *r; @@ -688,6 +693,7 @@ static int insert_ref_node(struct ubifs_info *c, int lnum, int offs, r->flags = REPLAY_REF; r->free = free; r->dirty = dirty; + r->jhead = jhead; rb_link_node(&r->rb, parent, p); rb_insert_color(&r->rb, &c->replay_tree); @@ -712,7 +718,7 @@ static int replay_buds(struct ubifs_info *c) if (err) return err; err = insert_ref_node(c, b->bud->lnum, b->bud->start, b->sqnum, - free, dirty); + free, dirty, b->bud->jhead); if (err) return err; } From 2840abd72048e773d2e04af79f0694fb9b47ecc7 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 15 Apr 2011 20:08:19 +0200 Subject: [PATCH 1638/2556] mmc: fix a race between card-detect rescan and clock-gate work instances commit 26fc8775b51484d8c0a671198639c6d5ae60533e upstream. Currently there is a race in the MMC core between a card-detect rescan work and the clock-gating work, scheduled from a command completion. Fix it by removing the dedicated clock-gating mutex and using the MMC standard locking mechanism instead. Signed-off-by: Guennadi Liakhovetski Cc: Simon Horman Cc: Magnus Damm Acked-by: Linus Walleij Signed-off-by: Chris Ball Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/core/host.c | 9 ++++----- include/linux/mmc/host.h | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 571436c95d8fb..2401d9a807100 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -94,7 +94,7 @@ static void mmc_host_clk_gate_delayed(struct mmc_host *host) spin_unlock_irqrestore(&host->clk_lock, flags); return; } - mutex_lock(&host->clk_gate_mutex); + mmc_claim_host(host); spin_lock_irqsave(&host->clk_lock, flags); if (!host->clk_requests) { spin_unlock_irqrestore(&host->clk_lock, flags); @@ -104,7 +104,7 @@ static void mmc_host_clk_gate_delayed(struct mmc_host *host) pr_debug("%s: gated MCI clock\n", mmc_hostname(host)); } spin_unlock_irqrestore(&host->clk_lock, flags); - mutex_unlock(&host->clk_gate_mutex); + mmc_release_host(host); } /* @@ -130,7 +130,7 @@ void mmc_host_clk_ungate(struct mmc_host *host) { unsigned long flags; - mutex_lock(&host->clk_gate_mutex); + mmc_claim_host(host); spin_lock_irqsave(&host->clk_lock, flags); if (host->clk_gated) { spin_unlock_irqrestore(&host->clk_lock, flags); @@ -140,7 +140,7 @@ void mmc_host_clk_ungate(struct mmc_host *host) } host->clk_requests++; spin_unlock_irqrestore(&host->clk_lock, flags); - mutex_unlock(&host->clk_gate_mutex); + mmc_release_host(host); } /** @@ -218,7 +218,6 @@ static inline void mmc_host_clk_init(struct mmc_host *host) host->clk_gated = false; INIT_WORK(&host->clk_gate_work, mmc_host_clk_gate_work); spin_lock_init(&host->clk_lock); - mutex_init(&host->clk_gate_mutex); } /** diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 00066ed0ba62d..374cc632093fd 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -183,7 +183,6 @@ struct mmc_host { struct work_struct clk_gate_work; /* delayed clock gate */ unsigned int clk_old; /* old clock value cache */ spinlock_t clk_lock; /* lock for clk fields */ - struct mutex clk_gate_mutex; /* mutex for clock gating */ #endif /* host specific block data */ From 38d7eefe782c6f7a7493854cca4cb96745397ba0 Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Tue, 29 Mar 2011 00:46:12 -0400 Subject: [PATCH 1639/2556] mmc: sdhci-pci: Fix error case in sdhci_pci_probe_slot() commit 9fdcdbb0d84922e7ccda2f717a04ea62629f7e18 upstream. If pci_ioremap_bar() fails during probe, we "goto release;" and free the host, but then we return 0 -- which tells sdhci_pci_probe() that the probe succeeded. Since we think the probe succeeded, when we unload sdhci we'll go to sdhci_pci_remove_slot() and it will try to dereference slot->host, which is now NULL because we freed it in the error path earlier. The patch simply sets ret appropriately, so that sdhci_pci_probe() will detect the failure immediately and bail out. Signed-off-by: Chris Ball Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index f7e622c539baa..b1b2fc04dd72c 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -961,6 +961,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( host->ioaddr = pci_ioremap_bar(pdev, bar); if (!host->ioaddr) { dev_err(&pdev->dev, "failed to remap registers\n"); + ret = -ENOMEM; goto release; } From bfa92409ed3683cbc92ece3b8b5ed251531fed18 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 27 Apr 2011 14:24:19 +0100 Subject: [PATCH 1640/2556] mmc: sdhci: Check mrq->cmd in sdhci_tasklet_finish commit b7b4d3426d2b5ecab21578eb20d8e456a1aace8f upstream. It seems that under certain circumstances that the sdhci_tasklet_finish() call can be entered with mrq->cmd set to NULL, causing the system to crash with a NULL pointer de-reference. Unable to handle kernel NULL pointer dereference at virtual address 00000000 PC is at sdhci_tasklet_finish+0x34/0xe8 LR is at sdhci_tasklet_finish+0x24/0xe8 Seen on S3C6410 system. Signed-off-by: Ben Dooks Signed-off-by: Mark Brown Signed-off-by: Chris Ball Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 3820adf155d2e..3ad990c310e2d 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1345,7 +1345,7 @@ static void sdhci_tasklet_finish(unsigned long param) * upon error conditions. */ if (!(host->flags & SDHCI_DEVICE_DEAD) && - (mrq->cmd->error || + ((mrq->cmd && mrq->cmd->error) || (mrq->data && (mrq->data->error || (mrq->data->stop && mrq->data->stop->error))) || (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) { From 065357d88482d6b11a2c85ef47242abfc06890c7 Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Wed, 27 Apr 2011 17:35:31 -0400 Subject: [PATCH 1641/2556] mmc: sdhci: Check mrq != NULL in sdhci_tasklet_finish commit 0c9c99a765321104cc5f9c97f949382a9ba4927e upstream. It seems that under certain circumstances the sdhci_tasklet_finish() call can be entered with mrq set to NULL, causing the system to crash with a NULL pointer de-reference. Seen on S3C6410 system. Based on a patch by Dimitris Papastamos. Reported-by: Dimitris Papastamos Signed-off-by: Chris Ball Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 3ad990c310e2d..6e8971cbe35d1 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1334,6 +1334,13 @@ static void sdhci_tasklet_finish(unsigned long param) host = (struct sdhci_host*)param; + /* + * If this tasklet gets rescheduled while running, it will + * be run again afterwards but without any active request. + */ + if (!host->mrq) + return; + spin_lock_irqsave(&host->lock, flags); del_timer(&host->timer); From fa05c4c2bbb7c1fb5b749c0f3f98727bd2ddbff5 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Sun, 1 May 2011 20:16:30 +1000 Subject: [PATCH 1642/2556] drm/radeon: fix regression on atom cards with hardcoded EDID record. commit eaa4f5e1d0b816291a59a47917e569c0384f2b6f upstream. Since fafcf94e2b5732d1e13b440291c53115d2b172e9 introduced an edid size, it seems to have broken this path. This manifest as oops on T500 Lenovo laptops with dual graphics primarily. Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=33812 Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_atombios.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 99768d9d91dac..a73f0e62a0782 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -1599,9 +1599,10 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct memcpy((u8 *)edid, (u8 *)&fake_edid_record->ucFakeEDIDString[0], fake_edid_record->ucFakeEDIDLength); - if (drm_edid_is_valid(edid)) + if (drm_edid_is_valid(edid)) { rdev->mode_info.bios_hardcoded_edid = edid; - else + rdev->mode_info.bios_hardcoded_edid_size = edid_size; + } else kfree(edid); } } From f8ea6927a78d70f5a7a28c81645a555cac4e0e8d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 2 May 2011 14:21:44 -0400 Subject: [PATCH 1643/2556] USB: fix regression in usbip by setting has_tt flag commit cee6a262550f53a13acfefbc1e3e5ff35c96182c upstream. This patch (as1460) fixes a regression in the usbip driver caused by the new check for Transaction Translators in USB-2 hubs. The root hub registered by vhci_hcd needs to have the has_tt flag set, because it can connect to low- and full-speed devices as well as high-speed devices. Signed-off-by: Alan Stern Reported-and-tested-by: Nikola Ciprich Signed-off-by: Greg Kroah-Hartman --- drivers/staging/usbip/vhci_hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c index a35fe61268de3..7284d0c18a4f6 100644 --- a/drivers/staging/usbip/vhci_hcd.c +++ b/drivers/staging/usbip/vhci_hcd.c @@ -1135,7 +1135,7 @@ static int vhci_hcd_probe(struct platform_device *pdev) usbip_uerr("create hcd failed\n"); return -ENOMEM; } - + hcd->has_tt = 1; /* this is private data for vhci_hcd */ the_controller = hcd_to_vhci(hcd); From 8363a22ae02bde7b43bf1a9c7efb22cb5c7140c7 Mon Sep 17 00:00:00 2001 From: "B.J. Buchalter" Date: Mon, 2 May 2011 13:33:42 -0400 Subject: [PATCH 1644/2556] firewire: Fix for broken configrom updates in quick succession commit 2e053a27d9d5ad5e0831e002cbf8043836fb2060 upstream. Current implementation of ohci_set_config_rom() uses a deferred bus reset via fw_schedule_bus_reset(). If clients add multiple unit descriptors to the config_rom in quick succession, the deferred bus reset may not have fired before succeeding update requests have come in. This can lead to an incorrect partial update of the config_rom for both addition and removal of config_rom descriptors, as the ohci_set_config_rom() routine will return -EBUSY if a previous pending update has not been completed yet; the requested update just gets dropped on the floor. This patch recognizes that the "in-flight" update can be modified until it has been processed by the bus-reset, and the locking in the bus_reset_tasklet ensures that the update is done atomically with respect to modifications made by ohci_set_config_rom(). The -EBUSY error case is simply removed. [Stefan R: The bug always existed at least theoretically. But it became easy to trigger since 2.6.36 commit 02d37bed188c "firewire: core: integrate software-forced bus resets with bus management" which introduced long mandatory delays between janitorial bus resets.] Signed-off-by: Benjamin Buchalter Signed-off-by: Stefan Richter Signed-off-by: Greg Kroah-Hartman --- drivers/firewire/ohci.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index bd3c61b6dd8d5..6a788c3070eab 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -2163,7 +2163,6 @@ static int ohci_set_config_rom(struct fw_card *card, { struct fw_ohci *ohci; unsigned long flags; - int ret = -EBUSY; __be32 *next_config_rom; dma_addr_t uninitialized_var(next_config_rom_bus); @@ -2204,22 +2203,37 @@ static int ohci_set_config_rom(struct fw_card *card, spin_lock_irqsave(&ohci->lock, flags); + /* + * If there is not an already pending config_rom update, + * push our new allocation into the ohci->next_config_rom + * and then mark the local variable as null so that we + * won't deallocate the new buffer. + * + * OTOH, if there is a pending config_rom update, just + * use that buffer with the new config_rom data, and + * let this routine free the unused DMA allocation. + */ + if (ohci->next_config_rom == NULL) { ohci->next_config_rom = next_config_rom; ohci->next_config_rom_bus = next_config_rom_bus; + next_config_rom = NULL; + } - copy_config_rom(ohci->next_config_rom, config_rom, length); + copy_config_rom(ohci->next_config_rom, config_rom, length); - ohci->next_header = config_rom[0]; - ohci->next_config_rom[0] = 0; + ohci->next_header = config_rom[0]; + ohci->next_config_rom[0] = 0; - reg_write(ohci, OHCI1394_ConfigROMmap, - ohci->next_config_rom_bus); - ret = 0; - } + reg_write(ohci, OHCI1394_ConfigROMmap, ohci->next_config_rom_bus); spin_unlock_irqrestore(&ohci->lock, flags); + /* If we didn't use the DMA allocation, delete it. */ + if (next_config_rom != NULL) + dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, + next_config_rom, next_config_rom_bus); + /* * Now initiate a bus reset to have the changes take * effect. We clean up the old config rom memory and DMA @@ -2227,13 +2241,10 @@ static int ohci_set_config_rom(struct fw_card *card, * controller could need to access it before the bus reset * takes effect. */ - if (ret == 0) - fw_schedule_bus_reset(&ohci->card, true, true); - else - dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, - next_config_rom, next_config_rom_bus); - return ret; + fw_schedule_bus_reset(&ohci->card, true, true); + + return 0; } static void ohci_send_request(struct fw_card *card, struct fw_packet *packet) From 74d90d8cfe1074895dd0e75c88bd79c039ba44a0 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 27 Apr 2011 09:54:28 +0000 Subject: [PATCH 1645/2556] usbnet: add support for some Huawei modems with cdc-ether ports commit b3c914aa84f4e4bbb3efc8f41c359d96e5e932d2 upstream. Some newer Huawei devices (T-Mobile Rocket, others) have cdc-ether compatible ports, so recognize and expose them. Signed-off-by: Dan Williams Acked-by: Oliver Neukum Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/cdc_ether.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 9a60e415d76be..1189d726419e0 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -458,7 +458,7 @@ static const struct driver_info cdc_info = { .manage_power = cdc_manage_power, }; -static const struct driver_info mbm_info = { +static const struct driver_info wwan_info = { .description = "Mobile Broadband Network Device", .flags = FLAG_WWAN, .bind = cdc_bind, @@ -469,6 +469,7 @@ static const struct driver_info mbm_info = { /*-------------------------------------------------------------------------*/ +#define HUAWEI_VENDOR_ID 0x12D1 static const struct usb_device_id products [] = { /* @@ -578,8 +579,17 @@ static const struct usb_device_id products [] = { }, { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long)&mbm_info, + .driver_info = (unsigned long)&wwan_info, +}, { + /* Various Huawei modems with a network port like the UMG1831 */ + .match_flags = USB_DEVICE_ID_MATCH_VENDOR + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = HUAWEI_VENDOR_ID, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, + .bInterfaceProtocol = 255, + .driver_info = (unsigned long)&wwan_info, }, { }, // END }; From 0071b6dce2355364a2013c820cb8c96def50cd82 Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Fri, 1 Apr 2011 14:12:02 -0300 Subject: [PATCH 1646/2556] v4l: make sure drivers supply a zeroed struct v4l2_subdev commit 80845a33165278f3236812009e9c568ba8c29938 upstream. Some v4l drivers currently don't initialize their struct v4l2_subdev with zeros, and this is a problem since some of the v4l2 code expects this. One example is the addition of internal_ops in commit 45f6f84, after that we are at risk of random oopses with these drivers when code in v4l2_device_register_subdev tries to dereference sd->internal_ops->*, as can be shown by the report at http://bugs.launchpad.net/bugs/745213 and analysis of its crash at https://lkml.org/lkml/2011/4/1/168 Use kzalloc within problematic drivers to ensure we have a zeroed struct v4l2_subdev. BugLink: http://bugs.launchpad.net/bugs/745213 Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/radio/saa7706h.c | 2 +- drivers/media/radio/tef6862.c | 2 +- drivers/media/video/m52790.c | 2 +- drivers/media/video/tda9840.c | 2 +- drivers/media/video/tea6415c.c | 2 +- drivers/media/video/tea6420.c | 2 +- drivers/media/video/upd64031a.c | 2 +- drivers/media/video/upd64083.c | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c index 585680ffbfb64..b1193dfc5087b 100644 --- a/drivers/media/radio/saa7706h.c +++ b/drivers/media/radio/saa7706h.c @@ -376,7 +376,7 @@ static int __devinit saa7706h_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%02x (%s)\n", client->addr << 1, client->adapter->name); - state = kmalloc(sizeof(struct saa7706h_state), GFP_KERNEL); + state = kzalloc(sizeof(struct saa7706h_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; sd = &state->sd; diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c index 7c0d77751f6e6..0991e1973678c 100644 --- a/drivers/media/radio/tef6862.c +++ b/drivers/media/radio/tef6862.c @@ -176,7 +176,7 @@ static int __devinit tef6862_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%02x (%s)\n", client->addr << 1, client->adapter->name); - state = kmalloc(sizeof(struct tef6862_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tef6862_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; state->freq = TEF6862_LO_FREQ; diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c index 5e1c9a81984ca..303ffa7df4aca 100644 --- a/drivers/media/video/m52790.c +++ b/drivers/media/video/m52790.c @@ -174,7 +174,7 @@ static int m52790_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - state = kmalloc(sizeof(struct m52790_state), GFP_KERNEL); + state = kzalloc(sizeof(struct m52790_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c index 5d4cf3b3d4350..22fa8202d5ca3 100644 --- a/drivers/media/video/tda9840.c +++ b/drivers/media/video/tda9840.c @@ -171,7 +171,7 @@ static int tda9840_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); if (sd == NULL) return -ENOMEM; v4l2_i2c_subdev_init(sd, client, &tda9840_ops); diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c index 19621ed523ec6..827425c5b866e 100644 --- a/drivers/media/video/tea6415c.c +++ b/drivers/media/video/tea6415c.c @@ -152,7 +152,7 @@ static int tea6415c_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); if (sd == NULL) return -ENOMEM; v4l2_i2c_subdev_init(sd, client, &tea6415c_ops); diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c index 5ea840401f218..f350b6c245001 100644 --- a/drivers/media/video/tea6420.c +++ b/drivers/media/video/tea6420.c @@ -125,7 +125,7 @@ static int tea6420_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); if (sd == NULL) return -ENOMEM; v4l2_i2c_subdev_init(sd, client, &tea6420_ops); diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c index f8138c75be8be..1aab96a882034 100644 --- a/drivers/media/video/upd64031a.c +++ b/drivers/media/video/upd64031a.c @@ -230,7 +230,7 @@ static int upd64031a_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - state = kmalloc(sizeof(struct upd64031a_state), GFP_KERNEL); + state = kzalloc(sizeof(struct upd64031a_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; sd = &state->sd; diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c index 28e0e6b6ca849..9bbe61700fd5c 100644 --- a/drivers/media/video/upd64083.c +++ b/drivers/media/video/upd64083.c @@ -202,7 +202,7 @@ static int upd64083_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); - state = kmalloc(sizeof(struct upd64083_state), GFP_KERNEL); + state = kzalloc(sizeof(struct upd64083_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; sd = &state->sd; From 713865d85210674406adf745a067df448f8e6794 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Wed, 27 Apr 2011 19:01:44 -0300 Subject: [PATCH 1647/2556] imon: add conditional locking in change_protocol commit 23ef710e1a6c4d6b9ef1c2fa19410f7f1479401e upstream. The imon_ir_change_protocol function gets called two different ways, one way is from rc_register_device, for initial protocol selection/setup, and the other is via a userspace-initiated protocol change request, either by direct sysfs prodding or by something like ir-keytable. In the rc_register_device case, the imon context lock is already held, but when initiated from userspace, it is not, so we must acquire it, prior to calling send_packet, which requires that the lock is held. Without this change, there's an easily reproduceable deadlock when another function calls send_packet (such as either of the display write fops) after a userspace-initiated change_protocol. With a lock-debugging-enabled kernel, I was getting this: [ 15.014153] ===================================== [ 15.015048] [ BUG: bad unlock balance detected! ] [ 15.015048] ------------------------------------- [ 15.015048] ir-keytable/773 is trying to release lock (&ictx->lock) at: [ 15.015048] [] mutex_unlock+0xe/0x10 [ 15.015048] but there are no more locks to release! [ 15.015048] [ 15.015048] other info that might help us debug this: [ 15.015048] 2 locks held by ir-keytable/773: [ 15.015048] #0: (&buffer->mutex){+.+.+.}, at: [] sysfs_write_file+0x3c/0x144 [ 15.015048] #1: (s_active#87){.+.+.+}, at: [] sysfs_write_file+0xe7/0x144 [ 15.015048] [ 15.015048] stack backtrace: [ 15.015048] Pid: 773, comm: ir-keytable Not tainted 2.6.38.4-20.fc15.x86_64.debug #1 [ 15.015048] Call Trace: [ 15.015048] [] ? print_unlock_inbalance_bug+0xca/0xd5 [ 15.015048] [] ? lock_release_non_nested+0xc1/0x263 [ 15.015048] [] ? mutex_unlock+0xe/0x10 [ 15.015048] [] ? mutex_unlock+0xe/0x10 [ 15.015048] [] ? lock_release+0x17d/0x1a4 [ 15.015048] [] ? __mutex_unlock_slowpath+0xc5/0x125 [ 15.015048] [] ? mutex_unlock+0xe/0x10 [ 15.015048] [] ? send_packet+0x1c9/0x264 [imon] [ 15.015048] [] ? lock_release_non_nested+0xdb/0x263 [ 15.015048] [] ? imon_ir_change_protocol+0x126/0x15e [imon] [ 15.015048] [] ? store_protocols+0x1c3/0x286 [rc_core] [ 15.015048] [] ? dev_attr_store+0x20/0x22 [ 15.015048] [] ? sysfs_write_file+0x108/0x144 ... The original report that led to the investigation was the following: [ 1679.457305] INFO: task LCDd:8460 blocked for more than 120 seconds. [ 1679.457307] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. [ 1679.457309] LCDd D ffff88010fcd89c8 0 8460 1 0x00000000 [ 1679.457312] ffff8800d5a03b48 0000000000000082 0000000000000000 ffff8800d5a03fd8 [ 1679.457314] 00000000012dcd30 fffffffffffffffd ffff8800d5a03fd8 ffff88010fcd86f0 [ 1679.457316] ffff8800d5a03fd8 ffff8800d5a03fd8 ffff88010fcd89d0 ffff8800d5a03fd8 [ 1679.457319] Call Trace: [ 1679.457324] [] ? zone_statistics+0x75/0x90 [ 1679.457327] [] ? get_page_from_freelist+0x3c7/0x820 [ 1679.457330] [] __mutex_lock_slowpath+0x139/0x320 [ 1679.457335] [] mutex_lock+0x11/0x30 [ 1679.457338] [] display_open+0x66/0x130 [imon] [ 1679.457345] [] usb_open+0x180/0x310 [usbcore] [ 1679.457349] [] chrdev_open+0x1bb/0x2d0 [ 1679.457350] [] __dentry_open+0x10d/0x370 [ 1679.457352] [] ? chrdev_open+0x0/0x2d0 ... Bump the driver version here so its easier to tell if people have this locking fix or not, and also make locking during probe easier to follow. Reported-by: Benjamin Hodgetts Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/rc/imon.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index e7dc6b46fdfac..eee39570f23db 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -46,7 +46,7 @@ #define MOD_AUTHOR "Jarod Wilson " #define MOD_DESC "Driver for SoundGraph iMON MultiMedia IR/Display" #define MOD_NAME "imon" -#define MOD_VERSION "0.9.2" +#define MOD_VERSION "0.9.3" #define DISPLAY_MINOR_BASE 144 #define DEVICE_NAME "lcd%d" @@ -451,8 +451,9 @@ static int display_close(struct inode *inode, struct file *file) } /** - * Sends a packet to the device -- this function must be called - * with ictx->lock held. + * Sends a packet to the device -- this function must be called with + * ictx->lock held, or its unlock/lock sequence while waiting for tx + * to complete can/will lead to a deadlock. */ static int send_packet(struct imon_context *ictx) { @@ -982,12 +983,21 @@ static void imon_touch_display_timeout(unsigned long data) * the iMON remotes, and those used by the Windows MCE remotes (which is * really just RC-6), but only one or the other at a time, as the signals * are decoded onboard the receiver. + * + * This function gets called two different ways, one way is from + * rc_register_device, for initial protocol selection/setup, and the other is + * via a userspace-initiated protocol change request, either by direct sysfs + * prodding or by something like ir-keytable. In the rc_register_device case, + * the imon context lock is already held, but when initiated from userspace, + * it is not, so we must acquire it prior to calling send_packet, which + * requires that the lock is held. */ static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type) { int retval; struct imon_context *ictx = rc->priv; struct device *dev = ictx->dev; + bool unlock = false; unsigned char ir_proto_packet[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 }; @@ -1020,6 +1030,11 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type) memcpy(ictx->usb_tx_buf, &ir_proto_packet, sizeof(ir_proto_packet)); + if (!mutex_is_locked(&ictx->lock)) { + unlock = true; + mutex_lock(&ictx->lock); + } + retval = send_packet(ictx); if (retval) goto out; @@ -1028,6 +1043,9 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type) ictx->pad_mouse = false; out: + if (unlock) + mutex_unlock(&ictx->lock); + return retval; } @@ -2125,6 +2143,7 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf) goto rdev_setup_failed; } + mutex_unlock(&ictx->lock); return ictx; rdev_setup_failed: @@ -2196,6 +2215,7 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf, goto urb_submit_failed; } + mutex_unlock(&ictx->lock); return ictx; urb_submit_failed: @@ -2290,6 +2310,8 @@ static int __devinit imon_probe(struct usb_interface *interface, usb_set_intfdata(interface, ictx); if (ifnum == 0) { + mutex_lock(&ictx->lock); + if (product == 0xffdc && ictx->rf_device) { sysfs_err = sysfs_create_group(&interface->dev.kobj, &imon_rf_attr_group); @@ -2300,13 +2322,14 @@ static int __devinit imon_probe(struct usb_interface *interface, if (ictx->display_supported) imon_init_display(ictx, interface); + + mutex_unlock(&ictx->lock); } dev_info(dev, "iMON device (%04x:%04x, intf%d) on " "usb<%d:%d> initialized\n", vendor, product, ifnum, usbdev->bus->busnum, usbdev->devnum); - mutex_unlock(&ictx->lock); mutex_unlock(&driver_lock); return 0; From a201dc91933abb4bb27f8bc2505bb9a2660de835 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 28 Apr 2011 15:55:52 -0400 Subject: [PATCH 1648/2556] flex_array: flex_array_prealloc takes a number of elements, not an end commit 5d30b10bd68df007e7ae21e77d1e0ce184b53040 upstream. Change flex_array_prealloc to take the number of elements for which space should be allocated instead of the last (inclusive) element. Users and documentation are updated accordingly. flex_arrays got introduced before they had users. When folks started using it, they ended up needing a different API than was coded up originally. This swaps over to the API that folks apparently need. Based-on-patch-by: Steffen Klassert Signed-off-by: Eric Paris Tested-by: Chris Richards Acked-by: Dave Hansen Signed-off-by: Greg Kroah-Hartman --- Documentation/flexible-arrays.txt | 4 ++-- include/linux/flex_array.h | 2 +- lib/flex_array.c | 13 ++++++++----- security/selinux/ss/policydb.c | 6 +++--- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Documentation/flexible-arrays.txt b/Documentation/flexible-arrays.txt index cb8a3a00cc926..df904aec99044 100644 --- a/Documentation/flexible-arrays.txt +++ b/Documentation/flexible-arrays.txt @@ -66,10 +66,10 @@ trick is to ensure that any needed memory allocations are done before entering atomic context, using: int flex_array_prealloc(struct flex_array *array, unsigned int start, - unsigned int end, gfp_t flags); + unsigned int nr_elements, gfp_t flags); This function will ensure that memory for the elements indexed in the range -defined by start and end has been allocated. Thereafter, a +defined by start and nr_elements has been allocated. Thereafter, a flex_array_put() call on an element in that range is guaranteed not to block. diff --git a/include/linux/flex_array.h b/include/linux/flex_array.h index 70e4efabe0fb6..ebeb2f3ad068d 100644 --- a/include/linux/flex_array.h +++ b/include/linux/flex_array.h @@ -61,7 +61,7 @@ struct flex_array { struct flex_array *flex_array_alloc(int element_size, unsigned int total, gfp_t flags); int flex_array_prealloc(struct flex_array *fa, unsigned int start, - unsigned int end, gfp_t flags); + unsigned int nr_elements, gfp_t flags); void flex_array_free(struct flex_array *fa); void flex_array_free_parts(struct flex_array *fa); int flex_array_put(struct flex_array *fa, unsigned int element_nr, void *src, diff --git a/lib/flex_array.c b/lib/flex_array.c index c0ea40ba20828..0c33b24498ba5 100644 --- a/lib/flex_array.c +++ b/lib/flex_array.c @@ -232,10 +232,10 @@ EXPORT_SYMBOL(flex_array_clear); /** * flex_array_prealloc - guarantee that array space exists - * @fa: the flex array for which to preallocate parts - * @start: index of first array element for which space is allocated - * @end: index of last (inclusive) element for which space is allocated - * @flags: page allocation flags + * @fa: the flex array for which to preallocate parts + * @start: index of first array element for which space is allocated + * @nr_elements: number of elements for which space is allocated + * @flags: page allocation flags * * This will guarantee that no future calls to flex_array_put() * will allocate memory. It can be used if you are expecting to @@ -245,13 +245,16 @@ EXPORT_SYMBOL(flex_array_clear); * Locking must be provided by the caller. */ int flex_array_prealloc(struct flex_array *fa, unsigned int start, - unsigned int end, gfp_t flags) + unsigned int nr_elements, gfp_t flags) { int start_part; int end_part; int part_nr; + unsigned int end; struct flex_array_part *part; + end = start + nr_elements - 1; + if (start >= fa->total_nr_elements || end >= fa->total_nr_elements) return -ENOSPC; if (elements_fit_in_base(fa)) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 57363562f0f88..f96f09cdd0366 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -497,7 +497,7 @@ static int policydb_index(struct policydb *p) goto out; rc = flex_array_prealloc(p->type_val_to_struct_array, 0, - p->p_types.nprim - 1, GFP_KERNEL | __GFP_ZERO); + p->p_types.nprim, GFP_KERNEL | __GFP_ZERO); if (rc) goto out; @@ -514,7 +514,7 @@ static int policydb_index(struct policydb *p) goto out; rc = flex_array_prealloc(p->sym_val_to_name[i], - 0, p->symtab[i].nprim - 1, + 0, p->symtab[i].nprim, GFP_KERNEL | __GFP_ZERO); if (rc) goto out; @@ -2286,7 +2286,7 @@ int policydb_read(struct policydb *p, void *fp) goto bad; /* preallocate so we don't have to worry about the put ever failing */ - rc = flex_array_prealloc(p->type_attr_map_array, 0, p->p_types.nprim - 1, + rc = flex_array_prealloc(p->type_attr_map_array, 0, p->p_types.nprim, GFP_KERNEL | __GFP_ZERO); if (rc) goto bad; From aed7f28fb89b17e3647375fbe010816258e61c74 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 28 Apr 2011 15:55:52 -0400 Subject: [PATCH 1649/2556] flex_arrays: allow zero length flex arrays commit bf69d41d198138e3c601e9a6645f4f1369aff7e0 upstream. Just like kmalloc will allow one to allocate a 0 length segment of memory flex arrays should do the same thing. It should bomb if you try to use something, but it should at least allow the allocation. This is needed because when SELinux switched to using flex_arrays in 2.6.38 the inability to allocate a 0 length array resulted in SELinux policy load returning -ENOSPC when previously it worked. Based-on-patch-by: Steffen Klassert Signed-off-by: Eric Paris Tested-by: Chris Richards Signed-off-by: Greg Kroah-Hartman --- lib/flex_array.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/flex_array.c b/lib/flex_array.c index 0c33b24498ba5..854b57bd7d9d3 100644 --- a/lib/flex_array.c +++ b/lib/flex_array.c @@ -253,9 +253,16 @@ int flex_array_prealloc(struct flex_array *fa, unsigned int start, unsigned int end; struct flex_array_part *part; + if (!start && !nr_elements) + return 0; + if (start >= fa->total_nr_elements) + return -ENOSPC; + if (!nr_elements) + return 0; + end = start + nr_elements - 1; - if (start >= fa->total_nr_elements || end >= fa->total_nr_elements) + if (end >= fa->total_nr_elements) return -ENOSPC; if (elements_fit_in_base(fa)) return 0; @@ -346,6 +353,8 @@ int flex_array_shrink(struct flex_array *fa) int part_nr; int ret = 0; + if (!fa->total_nr_elements) + return 0; if (elements_fit_in_base(fa)) return ret; for (part_nr = 0; part_nr < FLEX_ARRAY_NR_BASE_PTRS; part_nr++) { From 3e70ebfbefb8e7b20f95457c7000057532b49540 Mon Sep 17 00:00:00 2001 From: Boris Ostrovsky Date: Fri, 29 Apr 2011 17:47:43 -0400 Subject: [PATCH 1650/2556] x86, AMD: Fix APIC timer erratum 400 affecting K8 Rev.A-E processors commit e20a2d205c05cef6b5783df339a7d54adeb50962 upstream. Older AMD K8 processors (Revisions A-E) are affected by erratum 400 (APIC timer interrupts don't occur in C states greater than C1). This, for example, means that X86_FEATURE_ARAT flag should not be set for these parts. This addresses regression introduced by commit b87cf80af3ba4b4c008b4face3c68d604e1715c6 ("x86, AMD: Set ARAT feature on AMD processors") where the system may become unresponsive until external interrupt (such as keyboard input) occurs. This results, for example, in time not being reported correctly, lack of progress on the system and other lockups. Reported-by: Joerg-Volker Peetz Tested-by: Joerg-Volker Peetz Acked-by: Borislav Petkov Signed-off-by: Boris Ostrovsky Link: http://lkml.kernel.org/r/1304113663-6586-1-git-send-email-ostr@amd64.org Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/amd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index a2b9c7d413230..b65c707716fa3 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -681,7 +681,7 @@ cpu_dev_register(amd_cpu_dev); */ const int amd_erratum_400[] = - AMD_OSVW_ERRATUM(1, AMD_MODEL_RANGE(0xf, 0x41, 0x2, 0xff, 0xf), + AMD_OSVW_ERRATUM(1, AMD_MODEL_RANGE(0x0f, 0x4, 0x2, 0xff, 0xf), AMD_MODEL_RANGE(0x10, 0x2, 0x1, 0xff, 0xf)); EXPORT_SYMBOL_GPL(amd_erratum_400); From aecd0a520c0938f5e9f92cf1720e61bdaea05845 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 15 Apr 2011 00:41:43 +0200 Subject: [PATCH 1651/2556] ath9k: fix the return value of ath_stoprecv commit 2232d31bf18ba02f5cd632bbfc3466aeca394c75 upstream. The patch 'ath9k_hw: fix stopping rx DMA during resets' added code to detect a condition where rx DMA was stopped, but the MAC failed to enter the idle state. This condition requires a hardware reset, however the return value of ath_stoprecv was 'true' in that case, which allowed it to skip the reset when issuing a fast channel change. Signed-off-by: Felix Fietkau Reported-by: Paul Stewart Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/recv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 89546bc302906..1e0c1e3b514c7 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -533,7 +533,7 @@ bool ath_stoprecv(struct ath_softc *sc) "confusing the DMA engine when we start RX up\n"); ATH_DBG_WARN_ON_ONCE(!stopped); } - return stopped || reset; + return stopped && !reset; } void ath_flushrecv(struct ath_softc *sc) From c79a80b34d5c94c71fffaaa4dc40e8329bc270a1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 19 Apr 2011 20:44:04 +0200 Subject: [PATCH 1652/2556] mac80211: fix SMPS debugfs locking commit 243e6df4ed919880d079d717641ad699c6530a03 upstream. The locking with SMPS requests means that the debugs file should lock the mgd mutex, not the iflist mutex. Calls to __ieee80211_request_smps() need to hold that mutex, so add an assertion. This has always been wrong, but for some reason never been noticed, probably because the locking error only happens while unassociated. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- net/mac80211/cfg.c | 2 ++ net/mac80211/debugfs_netdev.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 9cd73b11506e8..40f7357c26998 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1471,6 +1471,8 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode old_req; int err; + lockdep_assert_held(&sdata->u.mgd.mtx); + old_req = sdata->u.mgd.req_smps; sdata->u.mgd.req_smps = smps_mode; diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 2dabdf7680d06..bae23ad4d8a34 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -172,9 +172,9 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata, if (sdata->vif.type != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; - mutex_lock(&local->iflist_mtx); + mutex_lock(&sdata->u.mgd.mtx); err = __ieee80211_request_smps(sdata, smps_mode); - mutex_unlock(&local->iflist_mtx); + mutex_unlock(&sdata->u.mgd.mtx); return err; } From 6c253c1c2f96a6fb2e60a4e83b80aa6d112dfe92 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 24 Apr 2011 01:54:57 +0000 Subject: [PATCH 1653/2556] af_unix: Only allow recv on connected seqpacket sockets. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a05d2ad1c1f391c7f514a1d1e09b5417968a7d07 upstream. This fixes the following oops discovered by Dan Aloni: > Anyway, the following is the output of the Oops that I got on the > Ubuntu kernel on which I first detected the problem > (2.6.37-12-generic). The Oops that followed will be more useful, I > guess. >[ 5594.669852] BUG: unable to handle kernel NULL pointer dereference > at           (null) > [ 5594.681606] IP: [] unix_dgram_recvmsg+0x1fb/0x420 > [ 5594.687576] PGD 2a05d067 PUD 2b951067 PMD 0 > [ 5594.693720] Oops: 0002 [#1] SMP > [ 5594.699888] last sysfs file: The bug was that unix domain sockets use a pseduo packet for connecting and accept uses that psudo packet to get the socket. In the buggy seqpacket case we were allowing unconnected sockets to call recvmsg and try to receive the pseudo packet. That is always wrong and as of commit 7361c36c5 the pseudo packet had become enough different from a normal packet that the kernel started oopsing. Do for seqpacket_recv what was done for seqpacket_send in 2.5 and only allow it on connected seqpacket sockets. Tested-by: Dan Aloni Signed-off-by: Eric W. Biederman Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/unix/af_unix.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 437a99e560e1b..7a79ad0ecf4a7 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -524,6 +524,8 @@ static int unix_dgram_connect(struct socket *, struct sockaddr *, int, int); static int unix_seqpacket_sendmsg(struct kiocb *, struct socket *, struct msghdr *, size_t); +static int unix_seqpacket_recvmsg(struct kiocb *, struct socket *, + struct msghdr *, size_t, int); static const struct proto_ops unix_stream_ops = { .family = PF_UNIX, @@ -583,7 +585,7 @@ static const struct proto_ops unix_seqpacket_ops = { .setsockopt = sock_no_setsockopt, .getsockopt = sock_no_getsockopt, .sendmsg = unix_seqpacket_sendmsg, - .recvmsg = unix_dgram_recvmsg, + .recvmsg = unix_seqpacket_recvmsg, .mmap = sock_no_mmap, .sendpage = sock_no_sendpage, }; @@ -1695,6 +1697,18 @@ static int unix_seqpacket_sendmsg(struct kiocb *kiocb, struct socket *sock, return unix_dgram_sendmsg(kiocb, sock, msg, len); } +static int unix_seqpacket_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t size, + int flags) +{ + struct sock *sk = sock->sk; + + if (sk->sk_state != TCP_ESTABLISHED) + return -ENOTCONN; + + return unix_dgram_recvmsg(iocb, sock, msg, size, flags); +} + static void unix_copy_addr(struct msghdr *msg, struct sock *sk) { struct unix_sock *u = unix_sk(sk); From b479c3bbce5625b21c28f124f6127e49ac4d934f Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Fri, 29 Apr 2011 15:48:07 +0100 Subject: [PATCH 1654/2556] ARM: 6891/1: prevent heap corruption in OABI semtimedop commit 0f22072ab50cac7983f9660d33974b45184da4f9 upstream. When CONFIG_OABI_COMPAT is set, the wrapper for semtimedop does not bound the nsops argument. A sufficiently large value will cause an integer overflow in allocation size, followed by copying too much data into the allocated buffer. Fix this by restricting nsops to SEMOPM. Untested. Signed-off-by: Dan Rosenberg Signed-off-by: Russell King Signed-off-by: Greg Kroah-Hartman --- arch/arm/kernel/sys_oabi-compat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/kernel/sys_oabi-compat.c b/arch/arm/kernel/sys_oabi-compat.c index 4ad8da15ef2b3..af0aaebf4de62 100644 --- a/arch/arm/kernel/sys_oabi-compat.c +++ b/arch/arm/kernel/sys_oabi-compat.c @@ -311,7 +311,7 @@ asmlinkage long sys_oabi_semtimedop(int semid, long err; int i; - if (nsops < 1) + if (nsops < 1 || nsops > SEMOPM) return -EINVAL; sops = kmalloc(sizeof(*sops) * nsops, GFP_KERNEL); if (!sops) From 0c7f1863c818ab6d84ba6731de664c14e5c6c5f0 Mon Sep 17 00:00:00 2001 From: Lasse Collin Date: Sun, 1 May 2011 19:38:42 +0300 Subject: [PATCH 1655/2556] XZ decompressor: Fix decoding of empty LZMA2 streams commit 646032e3b05b32d3f20cb108a030593d9d792eb5 upstream. The old code considered valid empty LZMA2 streams to be corrupt. Note that a typical empty .xz file has no LZMA2 data at all, and thus most .xz files having no uncompressed data are handled correctly even without this fix. Signed-off-by: Lasse Collin Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- lib/xz/xz_dec_lzma2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/xz/xz_dec_lzma2.c b/lib/xz/xz_dec_lzma2.c index ea5fa4fe9d678..a6cdc969ea42a 100644 --- a/lib/xz/xz_dec_lzma2.c +++ b/lib/xz/xz_dec_lzma2.c @@ -969,6 +969,9 @@ XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s, */ tmp = b->in[b->in_pos++]; + if (tmp == 0x00) + return XZ_STREAM_END; + if (tmp >= 0xE0 || tmp == 0x01) { s->lzma2.need_props = true; s->lzma2.need_dict_reset = false; @@ -1001,9 +1004,6 @@ XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s, lzma_reset(s); } } else { - if (tmp == 0x00) - return XZ_STREAM_END; - if (tmp > 0x02) return XZ_DATA_ERROR; From 7c72d9c4cc7bf40f49fa9ce878d1e8d9682332ba Mon Sep 17 00:00:00 2001 From: Sachin Prabhu Date: Wed, 20 Apr 2011 13:09:35 +0100 Subject: [PATCH 1656/2556] Open with O_CREAT flag set fails to open existing files on non writable directories commit 1574dff8996ab1ed92c09012f8038b5566fce313 upstream. An open on a NFS4 share using the O_CREAT flag on an existing file for which we have permissions to open but contained in a directory with no write permissions will fail with EACCES. A tcpdump shows that the client had set the open mode to UNCHECKED which indicates that the file should be created if it doesn't exist and encountering an existing flag is not an error. Since in this case the file exists and can be opened by the user, the NFS server is wrong in attempting to check create permissions on the parent directory. The patch adds a conditional statement to check for create permissions only if the file doesn't exist. Signed-off-by: Sachin S. Prabhu Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- fs/nfsd/vfs.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index da1d9701f8e44..435f407377bbe 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1363,7 +1363,7 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, goto out; if (!(iap->ia_valid & ATTR_MODE)) iap->ia_mode = 0; - err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE); + err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC); if (err) goto out; @@ -1385,6 +1385,13 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, if (IS_ERR(dchild)) goto out_nfserr; + /* If file doesn't exist, check for permissions to create one */ + if (!dchild->d_inode) { + err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE); + if (err) + goto out; + } + err = fh_compose(resfhp, fhp->fh_export, dchild, fhp); if (err) goto out; From 9eaffd03b8735c83dbd83cd7a4a7c2adabcf35ff Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Tue, 19 Apr 2011 20:36:59 -0700 Subject: [PATCH 1657/2556] can: Add missing socket check in can/bcm release. commit c6914a6f261aca0c9f715f883a353ae7ff51fe83 upstream. We can get here with a NULL socket argument passed from userspace, so we need to handle it accordingly. Signed-off-by: Dave Jones Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/can/bcm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/can/bcm.c b/net/can/bcm.c index 092dc88a7c64c..63779ab2b759c 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1427,9 +1427,14 @@ static int bcm_init(struct sock *sk) static int bcm_release(struct socket *sock) { struct sock *sk = sock->sk; - struct bcm_sock *bo = bcm_sk(sk); + struct bcm_sock *bo; struct bcm_op *op, *next; + if (sk == NULL) + return 0; + + bo = bcm_sk(sk); + /* remove bcm_ops, timer, rx_unregister(), etc. */ unregister_netdevice_notifier(&bo->notifier); From e8fb3db949f205177bd4f05f5cd831d3dd57aa4a Mon Sep 17 00:00:00 2001 From: Timo Warns Date: Thu, 14 Apr 2011 15:21:56 -0700 Subject: [PATCH 1658/2556] fs/partitions/ldm.c: fix oops caused by corrupted partition table commit c340b1d640001c8c9ecff74f68fd90422ae2448a upstream. The kernel automatically evaluates partition tables of storage devices. The code for evaluating LDM partitions (in fs/partitions/ldm.c) contains a bug that causes a kernel oops on certain corrupted LDM partitions. A kernel subsystem seems to crash, because, after the oops, the kernel no longer recognizes newly connected storage devices. The patch validates the value of vblk_size. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Timo Warns Cc: Eugene Teo Cc: Harvey Harrison Cc: Richard Russon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/partitions/ldm.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/fs/partitions/ldm.c b/fs/partitions/ldm.c index b10e3540d5b71..ce4f624404255 100644 --- a/fs/partitions/ldm.c +++ b/fs/partitions/ldm.c @@ -1299,6 +1299,11 @@ static bool ldm_frag_add (const u8 *data, int size, struct list_head *frags) BUG_ON (!data || !frags); + if (size < 2 * VBLK_SIZE_HEAD) { + ldm_error("Value of size is to small."); + return false; + } + group = get_unaligned_be32(data + 0x08); rec = get_unaligned_be16(data + 0x0C); num = get_unaligned_be16(data + 0x0E); @@ -1306,6 +1311,10 @@ static bool ldm_frag_add (const u8 *data, int size, struct list_head *frags) ldm_error ("A VBLK claims to have %d parts.", num); return false; } + if (rec >= num) { + ldm_error("REC value (%d) exceeds NUM value (%d)", rec, num); + return false; + } list_for_each (item, frags) { f = list_entry (item, struct frag, list); @@ -1334,10 +1343,9 @@ static bool ldm_frag_add (const u8 *data, int size, struct list_head *frags) f->map |= (1 << rec); - if (num > 0) { - data += VBLK_SIZE_HEAD; - size -= VBLK_SIZE_HEAD; - } + data += VBLK_SIZE_HEAD; + size -= VBLK_SIZE_HEAD; + memcpy (f->data+rec*(size-VBLK_SIZE_HEAD)+VBLK_SIZE_HEAD, data, size); return true; From 4c55fa77c7b971dc4a7bea058b7a9eb38a0f2a29 Mon Sep 17 00:00:00 2001 From: Lawrence Rust Date: Wed, 4 May 2011 23:25:10 -0300 Subject: [PATCH 1659/2556] cx88: Fix HVR4000 IR keymap [fixed in .39 in a much different way that is too big to backport to .38 - gregkh] Fixes the RC key input for Nova-S plus, HVR1100, HVR3000 and HVR4000 in the 2.6.38 kernel. Signed-off-by: Lawrence Rust Acked-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx88/cx88-input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index 06f7d1d009440..ac4f8e0750ef1 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -283,7 +283,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_PCHDTV_HD3000: case CX88_BOARD_PCHDTV_HD5500: case CX88_BOARD_HAUPPAUGE_IRONLY: - ir_codes = RC_MAP_HAUPPAUGE_NEW; + ir_codes = RC_MAP_RC5_HAUPPAUGE_NEW; ir->sampling = 1; break; case CX88_BOARD_WINFAST_DTV2000H: From b5c1caefa89cb9541e9a0d9a1defaeaba42ba27a Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Thu, 3 Feb 2011 15:29:52 +0200 Subject: [PATCH 1660/2556] KVM: SVM: check for progress after IRET interception commit bd3d1ec3d26b61120bb4f60b18ee99aa81839e6b upstream. When we enable an NMI window, we ask for an IRET intercept, since the IRET re-enables NMIs. However, the IRET intercept happens before the instruction executes, while the NMI window architecturally opens afterwards. To compensate for this mismatch, we only open the NMI window in the following exit, assuming that the IRET has by then executed; however, this assumption is not always correct; we may exit due to a host interrupt or page fault, without having executed the instruction. Fix by checking for forward progress by recording and comparing the IRET's rip. This is somewhat of a hack, since an unchaging rip does not mean that no forward progress has been made, but is the simplest fix for now. Signed-off-by: Avi Kivity Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 63fec1531e89b..d8a15a17d7671 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -135,6 +135,8 @@ struct vcpu_svm { u32 *msrpm; + ulong nmi_iret_rip; + struct nested_state nested; bool nmi_singlestep; @@ -2653,6 +2655,7 @@ static int iret_interception(struct vcpu_svm *svm) ++svm->vcpu.stat.nmi_window_exits; clr_intercept(svm, INTERCEPT_IRET); svm->vcpu.arch.hflags |= HF_IRET_MASK; + svm->nmi_iret_rip = kvm_rip_read(&svm->vcpu); return 1; } @@ -3474,7 +3477,12 @@ static void svm_complete_interrupts(struct vcpu_svm *svm) svm->int3_injected = 0; - if (svm->vcpu.arch.hflags & HF_IRET_MASK) { + /* + * If we've made progress since setting HF_IRET_MASK, we've + * executed an IRET and can allow NMI injection. + */ + if ((svm->vcpu.arch.hflags & HF_IRET_MASK) + && kvm_rip_read(&svm->vcpu) != svm->nmi_iret_rip) { svm->vcpu.arch.hflags &= ~(HF_NMI_MASK | HF_IRET_MASK); kvm_make_request(KVM_REQ_EVENT, &svm->vcpu); } From e124fd120e64c9a71dd6fb6e5ef805e3f35ffa8b Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 3 May 2011 15:15:55 -0400 Subject: [PATCH 1661/2556] drm/radeon/kms: add some new pci ids commit e2c85d8e3974c9041ad7b080846b28d2243e771b upstream. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- include/drm/drm_pciids.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index 6724bf3c1ff11..e893a90a74c55 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -153,6 +153,7 @@ {0x1002, 0x6729, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6738, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6739, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x673e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BARTS|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6740, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6741, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6742, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TURKS|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ @@ -185,6 +186,7 @@ {0x1002, 0x688D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6898, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6899, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x689b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \ {0x1002, 0x689c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HEMLOCK|RADEON_NEW_MEMMAP}, \ {0x1002, 0x689d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HEMLOCK|RADEON_NEW_MEMMAP}, \ {0x1002, 0x689e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYPRESS|RADEON_NEW_MEMMAP}, \ @@ -195,7 +197,9 @@ {0x1002, 0x68b0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x68b8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \ {0x1002, 0x68b9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x68ba, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \ {0x1002, 0x68be, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x68bf, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_JUNIPER|RADEON_NEW_MEMMAP}, \ {0x1002, 0x68c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x68c1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x68c7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_REDWOOD|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ From a6c3399277966a32016a294f1f439f2ef89a9574 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Wed, 20 Apr 2011 01:57:15 +0000 Subject: [PATCH 1662/2556] can: add missing socket check in can/raw release commit 10022a6c66e199d8f61d9044543f38785713cbbd upstream. v2: added space after 'if' according code style. We can get here with a NULL socket argument passed from userspace, so we need to handle it accordingly. Thanks to Dave Jones pointing at this issue in net/can/bcm.c Signed-off-by: Oliver Hartkopp Signed-off-by: David S. Miller Cc: Chuck Ebbert Signed-off-by: Greg Kroah-Hartman --- net/can/raw.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/can/raw.c b/net/can/raw.c index 883e9d74fddf6..241b2b60c7ee8 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -305,7 +305,12 @@ static int raw_init(struct sock *sk) static int raw_release(struct socket *sock) { struct sock *sk = sock->sk; - struct raw_sock *ro = raw_sk(sk); + struct raw_sock *ro; + + if (!sk) + return 0; + + ro = raw_sk(sk); unregister_netdevice_notifier(&ro->notifier); From af751c217b215b21cb29aa3329a86902beba6d8b Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sun, 1 May 2011 09:42:07 -0500 Subject: [PATCH 1663/2556] fix oops in scsi_run_queue() commit c055f5b2614b4f758ae6cc86733f31fa4c2c5844 upstream. The recent commit closing the race window in device teardown: commit 86cbfb5607d4b81b1a993ff689bbd2addd5d3a9b Author: James Bottomley Date: Fri Apr 22 10:39:59 2011 -0500 [SCSI] put stricter guards on queue dead checks is causing a potential NULL deref in scsi_run_queue() because the q->queuedata may already be NULL by the time this function is called. Since we shouldn't be running a queue that is being torn down, simply add a NULL check in scsi_run_queue() to forestall this. Tested-by: Jim Schutt Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_lib.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index fb2bb35c62cbf..415fdf2bd9462 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -400,10 +400,15 @@ static inline int scsi_host_is_busy(struct Scsi_Host *shost) static void scsi_run_queue(struct request_queue *q) { struct scsi_device *sdev = q->queuedata; - struct Scsi_Host *shost = sdev->host; + struct Scsi_Host *shost; LIST_HEAD(starved_list); unsigned long flags; + /* if the device is dead, sdev will be NULL, so no queue to run */ + if (!sdev) + return; + + shost = sdev->host; if (scsi_target(sdev)->single_lun) scsi_single_lun_run(sdev); From 597d087acc7cc72aa238ae5f87a1bdf72b6139d3 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 4 May 2011 21:30:28 -0700 Subject: [PATCH 1664/2556] VM: skip the stack guard page lookup in get_user_pages only for mlock commit a1fde08c74e90accd62d4cfdbf580d2ede938fe7 upstream. The logic in __get_user_pages() used to skip the stack guard page lookup whenever the caller wasn't interested in seeing what the actual page was. But Michel Lespinasse points out that there are cases where we don't care about the physical page itself (so 'pages' may be NULL), but do want to make sure a page is mapped into the virtual address space. So using the existence of the "pages" array as an indication of whether to look up the guard page or not isn't actually so great, and we really should just use the FOLL_MLOCK bit. But because that bit was only set for the VM_LOCKED case (and not all vma's necessarily have it, even for mlock()), we couldn't do that originally. Fix that by moving the VM_LOCKED check deeper into the call-chain, which actually simplifies many things. Now mlock() gets simpler, and we can also check for FOLL_MLOCK in __get_user_pages() and the code ends up much more straightforward. Reported-and-reviewed-by: Michel Lespinasse Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/memory.c | 7 +++---- mm/mlock.c | 5 +---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/mm/memory.c b/mm/memory.c index ab88d09c4a817..8b2bb7dcdae5e 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1359,7 +1359,7 @@ struct page *follow_page(struct vm_area_struct *vma, unsigned long address, */ mark_page_accessed(page); } - if (flags & FOLL_MLOCK) { + if ((flags & FOLL_MLOCK) && (vma->vm_flags & VM_LOCKED)) { /* * The preliminary mapping check is mainly to avoid the * pointless overhead of lock_page on the ZERO_PAGE @@ -1503,10 +1503,9 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, } /* - * If we don't actually want the page itself, - * and it's the stack guard page, just skip it. + * For mlock, just skip the stack guard page. */ - if (!pages && stack_guard_page(vma, start)) + if ((gup_flags & FOLL_MLOCK) && stack_guard_page(vma, start)) goto next_page; do { diff --git a/mm/mlock.c b/mm/mlock.c index da23be42c9067..c8e77909c04fe 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -162,7 +162,7 @@ static long __mlock_vma_pages_range(struct vm_area_struct *vma, VM_BUG_ON(end > vma->vm_end); VM_BUG_ON(!rwsem_is_locked(&mm->mmap_sem)); - gup_flags = FOLL_TOUCH; + gup_flags = FOLL_TOUCH | FOLL_MLOCK; /* * We want to touch writable mappings with a write fault in order * to break COW, except for shared mappings because these don't COW @@ -178,9 +178,6 @@ static long __mlock_vma_pages_range(struct vm_area_struct *vma, if (vma->vm_flags & (VM_READ | VM_WRITE | VM_EXEC)) gup_flags |= FOLL_FORCE; - if (vma->vm_flags & VM_LOCKED) - gup_flags |= FOLL_MLOCK; - return __get_user_pages(current, mm, addr, nr_pages, gup_flags, NULL, NULL, nonblocking); } From dece357b038ecbe098520db0eeb35905220a1a20 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 9 May 2011 15:16:23 -0700 Subject: [PATCH 1665/2556] Linux 2.6.38.6 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ae65f6d1568f0..152ed5cea5efa 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 38 -EXTRAVERSION = .5 +EXTRAVERSION = .6 NAME = Flesh-Eating Bats with Fangs # *DOCUMENTATION* From c5d57aef46f4af92838df632b27e387a4d21dc1b Mon Sep 17 00:00:00 2001 From: Ian Fijolek Date: Wed, 16 Mar 2011 13:48:05 -0400 Subject: [PATCH 1666/2556] Initial attempt at fixing indexes as well as parsing of video blocks and skipping audio blocks --- drivers/video/msm/hdmi/edid.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/video/msm/hdmi/edid.c b/drivers/video/msm/hdmi/edid.c index 101afa1d95eaa..93f4cc3431324 100644 --- a/drivers/video/msm/hdmi/edid.c +++ b/drivers/video/msm/hdmi/edid.c @@ -521,13 +521,29 @@ u8 edid_simple_parsing(struct hdmi_info *hdmi) if (extensions == 1) { EDID_DBG("Block-1: additional timing\n"); /* Block-1: additional timing */ - for( i = 0; i < (edid_buf[128 + 4] & 0x1f); i++) { - index = edid_buf[128 + 5 + i] & 0x7f; - additional_timing_db[index-1].supported = true; - EDID_DBG("%s\n", additional_timing_db[index-1].descrption); + /* edid_buf[127] is last byte of standard edid data + edid_buf[128 + 00] should be byte 00 of eedid + edid_buf[128 + 01] byte 01 rev number + edid_buf[128 + 02] byte 02 nonstandard DTD locations, not being parsed yet + edid_buf[128 + 03] byte 03 version info and number of blocks present + edid_buf[128 + 04] byte 04 start of standard DTD block collection + */ + for( i = 0; i < (edid_buf[128 + 3] & 0x0f); i++) { // Change by Ian: only bits 3-0 show length of blocks + // Check if byte type is Video (2) + if ((edid_buf[128 + 4 + i] & 0xe0) == 2){ + index = edid_buf[128 + 4 + i] & 0x7f; + additional_timing_db[index-1].supported = true; + EDID_DBG("%s\n", additional_timing_db[index-1].descrption); + } else { + // Byte is NOT video + int blockLen = (edid_buf[128 + 4 + i] & 0x1f); + i = i + (blockLen - 1); // -1 since loop has i++ alread + } + } edid_check_hdmi_sink(hdmi, 1); } else { + // Probably also contaions errors corrected above EDID_DBG("Block-2: additional timing\n"); for( i = 0; i < (edid_buf[384 + 4] & 0x1f); i++) { index = edid_buf[384 + 5 + i] & 0x7f; @@ -537,6 +553,7 @@ u8 edid_simple_parsing(struct hdmi_info *hdmi) edid_check_hdmi_sink(hdmi, 3); } + // Why are these overwritten? edid_buf[35] = 0; edid_buf[36] = 0; edid_buf[37] = 0; From c6a563e5ab6c7de8097d39de04681dca5b9bda3b Mon Sep 17 00:00:00 2001 From: Ian Fijolek Date: Thu, 17 Mar 2011 15:57:38 -0400 Subject: [PATCH 1667/2556] comment changes --- .gitignore | 1 + drivers/video/msm/hdmi/edid.c | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 5d56a3fd0de6b..40429c84e9e80 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ modules.builtin *.lzo *.patch *.gcno +vmlinux # # Top-level generic files diff --git a/drivers/video/msm/hdmi/edid.c b/drivers/video/msm/hdmi/edid.c index 93f4cc3431324..7a38db2235337 100644 --- a/drivers/video/msm/hdmi/edid.c +++ b/drivers/video/msm/hdmi/edid.c @@ -521,7 +521,8 @@ u8 edid_simple_parsing(struct hdmi_info *hdmi) if (extensions == 1) { EDID_DBG("Block-1: additional timing\n"); /* Block-1: additional timing */ - /* edid_buf[127] is last byte of standard edid data + /* + edid_buf[127] is last byte of standard edid data edid_buf[128 + 00] should be byte 00 of eedid edid_buf[128 + 01] byte 01 rev number edid_buf[128 + 02] byte 02 nonstandard DTD locations, not being parsed yet @@ -531,7 +532,8 @@ u8 edid_simple_parsing(struct hdmi_info *hdmi) for( i = 0; i < (edid_buf[128 + 3] & 0x0f); i++) { // Change by Ian: only bits 3-0 show length of blocks // Check if byte type is Video (2) if ((edid_buf[128 + 4 + i] & 0xe0) == 2){ - index = edid_buf[128 + 4 + i] & 0x7f; + // Byte block IS video + index = edid_buf[128 + 4 + i] & 0x7f; additional_timing_db[index-1].supported = true; EDID_DBG("%s\n", additional_timing_db[index-1].descrption); } else { @@ -543,7 +545,7 @@ u8 edid_simple_parsing(struct hdmi_info *hdmi) } edid_check_hdmi_sink(hdmi, 1); } else { - // Probably also contaions errors corrected above + // Probably also contaions errors corrected above but probably not used EDID_DBG("Block-2: additional timing\n"); for( i = 0; i < (edid_buf[384 + 4] & 0x1f); i++) { index = edid_buf[384 + 5 + i] & 0x7f; From 61e7aa278c32026a83cf412c7e8cb36230ae76d9 Mon Sep 17 00:00:00 2001 From: Kevin Bruckert Date: Mon, 21 Mar 2011 10:51:12 -0500 Subject: [PATCH 1668/2556] Fix EDID logic --- drivers/video/msm/hdmi/edid.c | 824 ++++++++++++++++++---------------- 1 file changed, 428 insertions(+), 396 deletions(-) diff --git a/drivers/video/msm/hdmi/edid.c b/drivers/video/msm/hdmi/edid.c index 7a38db2235337..36f629c2be43b 100644 --- a/drivers/video/msm/hdmi/edid.c +++ b/drivers/video/msm/hdmi/edid.c @@ -12,7 +12,7 @@ * Common function for accessing/debugging EDID data. * Reference: - http://en.wikipedia.org/wiki/Extended_display_identification_data + http://en.wikipedia.org/wiki/Extended_display_identification_data */ #include @@ -29,181 +29,180 @@ #endif static struct video_mode established_timing_db[] = { - {800, 600, 60, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 60 Hz"}, - {800, 600, 56, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 56 Hz"}, - {640, 480, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 75 Hz"}, - {640, 480, 72, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 72 Hz"}, - {640, 480, 67, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 67 Hz"}, - {640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 60 Hz"}, - {720, 400, 88, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 88 Hz"}, - {720, 400, 70, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 70 Hz"}, - - {1280, 1024, 75, ASPECT(4, 3), PROGRESSIVE, false, "1280x1024@75 Hz"}, - {1024, 768, 75, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@75 Hz"}, - {1024, 768, 70, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@70 Hz"}, - {1024, 768, 60, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@60 Hz"}, - {1024, 768, 87, ASPECT(4, 3), INTERLACE, false, - "1024x768@87 Hz (Interlaced)"}, - {832, 624, 75, ASPECT(4, 3), PROGRESSIVE, false, "832x624@75 Hz"}, - {800, 600, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600@75 Hz"}, - {800, 600, 72, ASPECT(4, 3), PROGRESSIVE, false, "800x600@72 Hz"}, - - {1152, 870, 75, ASPECT(4, 3), PROGRESSIVE, false, "1152x870 @ 75 Hz"}, + {800, 600, 60, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 60 Hz"}, + {800, 600, 56, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 56 Hz"}, + {640, 480, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 75 Hz"}, + {640, 480, 72, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 72 Hz"}, + {640, 480, 67, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 67 Hz"}, + {640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 60 Hz"}, + {720, 400, 88, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 88 Hz"}, + {720, 400, 70, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 70 Hz"}, + + {1280, 1024, 75, ASPECT(4, 3), PROGRESSIVE, false, "1280x1024@75 Hz"}, + {1024, 768, 75, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@75 Hz"}, + {1024, 768, 70, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@70 Hz"}, + {1024, 768, 60, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@60 Hz"}, + {1024, 768, 87, ASPECT(4, 3), INTERLACE, false, "1024x768@87 Hz (Interlaced)"}, + {832, 624, 75, ASPECT(4, 3), PROGRESSIVE, false, "832x624@75 Hz"}, + {800, 600, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600@75 Hz"}, + {800, 600, 72, ASPECT(4, 3), PROGRESSIVE, false, "800x600@72 Hz"}, + + {1152, 870, 75, ASPECT(4, 3), PROGRESSIVE, false, "1152x870 @ 75 Hz"}, }; static struct video_mode standard_timing_db[8]; static struct video_mode additional_timing_db[] = { - {640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, - " 1 DMT0659 4:3 640x480p @ 59.94/60Hz"}, - {720, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, - " 2 480p 4:3 720x480p @ 59.94/60Hz"}, - {720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, - " 3 480pH 16:9 720x480p @ 59.94/60Hz"}, - {1280, 720, 60, ASPECT(16, 9), PROGRESSIVE, false, - " 4 720p 16:9 1280x720p @ 59.94/60Hz"}, - {1920, 1080, 60, ASPECT(4, 3), INTERLACE, false, - " 5 1080i 16:9 1920x1080i @ 59.94/60Hz"}, - {720, 480, 60, ASPECT(4, 3), INTERLACE, false, - " 6 480i 4:3 720(1440)x480i @ 59.94/60Hz"}, - {720, 480, 60, ASPECT(16, 9), INTERLACE, false, - " 7 480iH 16:9 720(1440)x480i @ 59.94/60Hz"}, - {720, 240, 60, ASPECT(4, 3), PROGRESSIVE, false, - " 8 240p 4:3 720(1440)x240p @ 59.94/60Hz"}, - {720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, - " 9 240pH 16:9 720(1440)x240p @ 59.94/60Hz"}, - {2880, 480, 60, ASPECT(4, 3), INTERLACE, false, - "10 480i4x 4:3 (2880)x480i @ 59.94/60Hz"}, - {2880, 480, 60, ASPECT(16, 9), INTERLACE, false, - "11 480i4xH 16:9 (2880)x480i @ 59.94/60Hz"}, - {2880, 240, 60, ASPECT(4, 3), PROGRESSIVE, false, - "12 240p4x 4:3 (2880)x240p @ 59.94/60Hz"}, - {2880, 240, 60, ASPECT(16, 9), PROGRESSIVE, false, - "13 240p4xH 16:9 (2880)x240p @ 59.94/60Hz"}, - {1440, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, - "14 480p2x 4:3 1440x480p @ 59.94/60Hz"}, - {1440, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, - "15 480p2xH 16:9 1440x480p @ 59.94/60Hz"}, - {1920, 1080, 60, ASPECT(16, 9), PROGRESSIVE, false, - "16 1080p 16:9 1920x1080p @ 59.94/60Hz"}, - {720, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, - "17 576p 4:3 720x576p @ 50Hz"}, - {720, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, - "18 576pH 16:9 720x576p @ 50Hz"}, - {1280, 720, 50, ASPECT(16, 9), PROGRESSIVE, false, - "19 720p50 16:9 1280x720p @ 50Hz"}, - {1920, 1080, 50, ASPECT(16, 9), INTERLACE, false, - "20 1080i25 16:9 1920x1080i @ 50Hz*"}, - {1440, 576, 50, ASPECT(4, 3), INTERLACE, false, - "21 576i 4:3 720(1440)x576i @ 50Hz"}, - {1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, - "22 576iH 16:9 720(1440)x576i @ 50Hz"}, - {720, 288, 50, ASPECT(4, 3), PROGRESSIVE, false, - "23 288p 4:3 720(1440)x288p @ 50Hz"}, - {720, 288, 50, ASPECT(16, 9), PROGRESSIVE, false, - "24 288pH 16:9 720(1440)x288p @ 50Hz"}, - {2880, 576, 50, ASPECT(4, 3), INTERLACE, false, - "25 576i4x 4:3 (2880)x576i @ 50Hz"}, - {2880, 576, 50, ASPECT(16, 9), INTERLACE, false, - "26 576i4xH 16:9 (2880)x576i @ 50Hz"}, - {2880, 288, 50, ASPECT(4, 3), PROGRESSIVE, false, - "27 288p4x 4:3 (2880)x288p @ 50Hz"}, - {2880, 288, 50, ASPECT(16, 9), PROGRESSIVE, false, - "28 288p4xH 16:9 (2880)x288p @ 50Hz"}, - {1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, - "29 576p2x 4:3 1440x576p @ 50Hz"}, - {1440, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, - "30 576p2xH 16:9 1440x576p @ 50Hz"}, - {1920, 1080, 50, ASPECT(16, 9), PROGRESSIVE, false, - "31 1080p50 16:9 1920x1080p @ 50Hz"}, - {1920, 1080, 24, ASPECT(16, 9), PROGRESSIVE, false, - "32 1080p24 16:9 1920x1080p @ 23.98/24Hz"}, - {1920, 1080, 25, ASPECT(16, 9), PROGRESSIVE, false, - "33 1080p25 16:9 1920x1080p @ 25Hz"}, - {1920, 1080, 30, ASPECT(16, 9), PROGRESSIVE, false, - "34 1080p30 16:9 1920x1080p @ 29.97/30Hz"}, - {2880, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, - "35 480p4x 4:3 (2880)x480p @ 59.94/60Hz"}, - {2880, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, - "36 480p4xH 16:9 (2880)x480p @ 59.94/60Hz"}, - {2880, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, - "37 576p4x 4:3 (2880)x576p @ 50Hz"}, - {2880, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, - "38 576p4xH 16:9 (2880)x576p @ 50Hz"}, - {1920, 1080, 50, ASPECT(16, 9), INTERLACE, false, - "39 108Oi25 16:9 1920x1080i(1250 Total) @ 50Hz*"}, - {1920, 1080, 100, ASPECT(16, 9), INTERLACE, false, - "40 1080i50 16:9 1920x1080i @ 100Hz"}, - {1280, 720, 100, ASPECT(16, 9), PROGRESSIVE, false, - "41 720p100 16:9 1280x720p @ 100Hz"}, - {720, 576, 100, ASPECT(4, 3), PROGRESSIVE, false, - "42 576p100 4:3 720x576p @ 100Hz"}, - {720, 576, 100, ASPECT(16, 9), PROGRESSIVE, false, - "43 576p100H 16:9 720x576p @ 100Hz"}, - {720, 576, 100, ASPECT(4, 3), INTERLACE, false, - "44 576i50 4:3 720(1440)x576i @ 100Hz"}, - {720, 576, 100, ASPECT(16, 9), INTERLACE, false, - "45 576i50H 16:9 720(1440)x576i @ 100Hz"}, - {1920, 1080, 120, ASPECT(16, 9), INTERLACE, false, - "46 1080i60 16:9 1920x1080i @ 119.88/120Hz"}, - {1280, 720, 120, ASPECT(16, 9), PROGRESSIVE, false, - "47 720p120 16:9 1280x720p @ 119.88/120Hz"}, - {720, 480, 120, ASPECT(4, 3), PROGRESSIVE, false, - "48 480p119 4:3 720x480p @ 119.88/120Hz"}, - {720, 480, 120, ASPECT(16, 9), PROGRESSIVE, false, - "49 480p119H 16:9 720x480p @ 119.88/120Hz"}, - {720, 480, 120, ASPECT(4, 3), INTERLACE, false, - "50 480i59 4:3 720(1440)x480i @ 119.88/120Hz"}, - {720, 480, 120, ASPECT(16, 9), INTERLACE, false, - "51 480i59H 16:9 720(1440)x480i @ 119.88/120Hz"}, - {720, 576, 200, ASPECT(4, 3), PROGRESSIVE, false, - "52 576p200 4:3 720x576p @ 200Hz"}, - {720, 576, 200, ASPECT(16, 9), PROGRESSIVE, false, - "53 576p200H 16:9 720x576p @ 200Hz"}, - {720, 576, 200, ASPECT(4, 3), INTERLACE, false, - "54 576i100 4:3 720(1440)x576i @ 200Hz"}, - {720, 576, 200, ASPECT(16, 9), INTERLACE, false, - "55 576i100H 16:9 720(1440)x576i @ 200Hz"}, - {720, 480, 240, ASPECT(4, 3), PROGRESSIVE, false, - "56 480p239 4:3 720x480p @ 239.76/240Hz"}, - {720, 480, 240, ASPECT(16, 9), PROGRESSIVE, false, - "57 480p239H 16:9 720x480p @ 239.76/240Hz"}, - {720, 480, 240, ASPECT(4, 3), INTERLACE, false, - "58 480i119 4:3 720(1440)x480i @ 239.76/240Hz"}, - {720, 480, 240, ASPECT(16, 9), INTERLACE, false, - "59 480i119H 16:9 720(1440)x480i @ 239.76/240Hz"}, - {1280, 720, 24, ASPECT(16, 9), PROGRESSIVE, false, - "60 720p24 16:9 1280x720p @ 23.98/24Hz"}, - {1280, 720, 25, ASPECT(16, 9), PROGRESSIVE, false, - "61 720p25 16:9 1280x720p @ 25Hz"}, - {1280, 720, 30, ASPECT(16, 9), PROGRESSIVE, false, - "62 720p30 16:9 1280x720p @ 29.97/30Hz"}, - {1920, 1080, 120, ASPECT(16, 9), PROGRESSIVE, false, - "63 1080p120 16:9 1920x1080 @ 119.88/120Hz"}, + {640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, + " 1 DMT0659 4:3 640x480p @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, + " 2 480p 4:3 720x480p @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, + " 3 480pH 16:9 720x480p @ 59.94/60Hz"}, + {1280, 720, 60, ASPECT(16, 9), PROGRESSIVE, false, + " 4 720p 16:9 1280x720p @ 59.94/60Hz"}, + {1920, 1080, 60, ASPECT(4, 3), INTERLACE, false, + " 5 1080i 16:9 1920x1080i @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(4, 3), INTERLACE, false, + " 6 480i 4:3 720(1440)x480i @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(16, 9), INTERLACE, false, + " 7 480iH 16:9 720(1440)x480i @ 59.94/60Hz"}, + {720, 240, 60, ASPECT(4, 3), PROGRESSIVE, false, + " 8 240p 4:3 720(1440)x240p @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, + " 9 240pH 16:9 720(1440)x240p @ 59.94/60Hz"}, + {2880, 480, 60, ASPECT(4, 3), INTERLACE, false, + "10 480i4x 4:3 (2880)x480i @ 59.94/60Hz"}, + {2880, 480, 60, ASPECT(16, 9), INTERLACE, false, + "11 480i4xH 16:9 (2880)x480i @ 59.94/60Hz"}, + {2880, 240, 60, ASPECT(4, 3), PROGRESSIVE, false, + "12 240p4x 4:3 (2880)x240p @ 59.94/60Hz"}, + {2880, 240, 60, ASPECT(16, 9), PROGRESSIVE, false, + "13 240p4xH 16:9 (2880)x240p @ 59.94/60Hz"}, + {1440, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, + "14 480p2x 4:3 1440x480p @ 59.94/60Hz"}, + {1440, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, + "15 480p2xH 16:9 1440x480p @ 59.94/60Hz"}, + {1920, 1080, 60, ASPECT(16, 9), PROGRESSIVE, false, + "16 1080p 16:9 1920x1080p @ 59.94/60Hz"}, + {720, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, + "17 576p 4:3 720x576p @ 50Hz"}, + {720, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, + "18 576pH 16:9 720x576p @ 50Hz"}, + {1280, 720, 50, ASPECT(16, 9), PROGRESSIVE, false, + "19 720p50 16:9 1280x720p @ 50Hz"}, + {1920, 1080, 50, ASPECT(16, 9), INTERLACE, false, + "20 1080i25 16:9 1920x1080i @ 50Hz*"}, + {1440, 576, 50, ASPECT(4, 3), INTERLACE, false, + "21 576i 4:3 720(1440)x576i @ 50Hz"}, + {1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, + "22 576iH 16:9 720(1440)x576i @ 50Hz"}, + {720, 288, 50, ASPECT(4, 3), PROGRESSIVE, false, + "23 288p 4:3 720(1440)x288p @ 50Hz"}, + {720, 288, 50, ASPECT(16, 9), PROGRESSIVE, false, + "24 288pH 16:9 720(1440)x288p @ 50Hz"}, + {2880, 576, 50, ASPECT(4, 3), INTERLACE, false, + "25 576i4x 4:3 (2880)x576i @ 50Hz"}, + {2880, 576, 50, ASPECT(16, 9), INTERLACE, false, + "26 576i4xH 16:9 (2880)x576i @ 50Hz"}, + {2880, 288, 50, ASPECT(4, 3), PROGRESSIVE, false, + "27 288p4x 4:3 (2880)x288p @ 50Hz"}, + {2880, 288, 50, ASPECT(16, 9), PROGRESSIVE, false, + "28 288p4xH 16:9 (2880)x288p @ 50Hz"}, + {1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, + "29 576p2x 4:3 1440x576p @ 50Hz"}, + {1440, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, + "30 576p2xH 16:9 1440x576p @ 50Hz"}, + {1920, 1080, 50, ASPECT(16, 9), PROGRESSIVE, false, + "31 1080p50 16:9 1920x1080p @ 50Hz"}, + {1920, 1080, 24, ASPECT(16, 9), PROGRESSIVE, false, + "32 1080p24 16:9 1920x1080p @ 23.98/24Hz"}, + {1920, 1080, 25, ASPECT(16, 9), PROGRESSIVE, false, + "33 1080p25 16:9 1920x1080p @ 25Hz"}, + {1920, 1080, 30, ASPECT(16, 9), PROGRESSIVE, false, + "34 1080p30 16:9 1920x1080p @ 29.97/30Hz"}, + {2880, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, + "35 480p4x 4:3 (2880)x480p @ 59.94/60Hz"}, + {2880, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, + "36 480p4xH 16:9 (2880)x480p @ 59.94/60Hz"}, + {2880, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, + "37 576p4x 4:3 (2880)x576p @ 50Hz"}, + {2880, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, + "38 576p4xH 16:9 (2880)x576p @ 50Hz"}, + {1920, 1080, 50, ASPECT(16, 9), INTERLACE, false, + "39 108Oi25 16:9 1920x1080i(1250 Total) @ 50Hz*"}, + {1920, 1080, 100, ASPECT(16, 9), INTERLACE, false, + "40 1080i50 16:9 1920x1080i @ 100Hz"}, + {1280, 720, 100, ASPECT(16, 9), PROGRESSIVE, false, + "41 720p100 16:9 1280x720p @ 100Hz"}, + {720, 576, 100, ASPECT(4, 3), PROGRESSIVE, false, + "42 576p100 4:3 720x576p @ 100Hz"}, + {720, 576, 100, ASPECT(16, 9), PROGRESSIVE, false, + "43 576p100H 16:9 720x576p @ 100Hz"}, + {720, 576, 100, ASPECT(4, 3), INTERLACE, false, + "44 576i50 4:3 720(1440)x576i @ 100Hz"}, + {720, 576, 100, ASPECT(16, 9), INTERLACE, false, + "45 576i50H 16:9 720(1440)x576i @ 100Hz"}, + {1920, 1080, 120, ASPECT(16, 9), INTERLACE, false, + "46 1080i60 16:9 1920x1080i @ 119.88/120Hz"}, + {1280, 720, 120, ASPECT(16, 9), PROGRESSIVE, false, + "47 720p120 16:9 1280x720p @ 119.88/120Hz"}, + {720, 480, 120, ASPECT(4, 3), PROGRESSIVE, false, + "48 480p119 4:3 720x480p @ 119.88/120Hz"}, + {720, 480, 120, ASPECT(16, 9), PROGRESSIVE, false, + "49 480p119H 16:9 720x480p @ 119.88/120Hz"}, + {720, 480, 120, ASPECT(4, 3), INTERLACE, false, + "50 480i59 4:3 720(1440)x480i @ 119.88/120Hz"}, + {720, 480, 120, ASPECT(16, 9), INTERLACE, false, + "51 480i59H 16:9 720(1440)x480i @ 119.88/120Hz"}, + {720, 576, 200, ASPECT(4, 3), PROGRESSIVE, false, + "52 576p200 4:3 720x576p @ 200Hz"}, + {720, 576, 200, ASPECT(16, 9), PROGRESSIVE, false, + "53 576p200H 16:9 720x576p @ 200Hz"}, + {720, 576, 200, ASPECT(4, 3), INTERLACE, false, + "54 576i100 4:3 720(1440)x576i @ 200Hz"}, + {720, 576, 200, ASPECT(16, 9), INTERLACE, false, + "55 576i100H 16:9 720(1440)x576i @ 200Hz"}, + {720, 480, 240, ASPECT(4, 3), PROGRESSIVE, false, + "56 480p239 4:3 720x480p @ 239.76/240Hz"}, + {720, 480, 240, ASPECT(16, 9), PROGRESSIVE, false, + "57 480p239H 16:9 720x480p @ 239.76/240Hz"}, + {720, 480, 240, ASPECT(4, 3), INTERLACE, false, + "58 480i119 4:3 720(1440)x480i @ 239.76/240Hz"}, + {720, 480, 240, ASPECT(16, 9), INTERLACE, false, + "59 480i119H 16:9 720(1440)x480i @ 239.76/240Hz"}, + {1280, 720, 24, ASPECT(16, 9), PROGRESSIVE, false, + "60 720p24 16:9 1280x720p @ 23.98/24Hz"}, + {1280, 720, 25, ASPECT(16, 9), PROGRESSIVE, false, + "61 720p25 16:9 1280x720p @ 25Hz"}, + {1280, 720, 30, ASPECT(16, 9), PROGRESSIVE, false, + "62 720p30 16:9 1280x720p @ 29.97/30Hz"}, + {1920, 1080, 120, ASPECT(16, 9), PROGRESSIVE, false, + "63 1080p120 16:9 1920x1080 @ 119.88/120Hz"}, }; /* device supported modes in CEA */ enum { - CEA_MODE_640X480P_60HZ_4_3 = 0, - CEA_MODE_720X480P_60HZ_4_3 = 1, - CEA_MODE_720X480P_60HZ_16_9 = 2, - CEA_MODE_1280X720P_60HZ_16_9 = 3, - CEA_MODE_720X576P_50HZ_4_3 = 16, - CEA_MODE_720X576P_50HZ_16_9 = 17, + CEA_MODE_640X480P_60HZ_4_3 = 0, + CEA_MODE_720X480P_60HZ_4_3 = 1, + CEA_MODE_720X480P_60HZ_16_9 = 2, + CEA_MODE_1280X720P_60HZ_16_9 = 3, + CEA_MODE_720X576P_50HZ_4_3 = 16, + CEA_MODE_720X576P_50HZ_16_9 = 17, }; /* device supported modes in established timing */ enum { - ESTABLISHED_MODE_800X600_60HZ = 0, - ESTABLISHED_MODE_640X480_60HZ = 5, + ESTABLISHED_MODE_800X600_60HZ = 0, + ESTABLISHED_MODE_640X480_60HZ = 5, }; int init_edid_info(struct edid_info_struct *edid_info) { - edid_info->is_valid = false; - mutex_init(&edid_info->access_lock); + edid_info->is_valid = false; + mutex_init(&edid_info->access_lock); - return 0; + return 0; } /* Byte 35-37 of block-0 */ @@ -356,65 +355,71 @@ int edid_dump_video_modes(u8 *edid_buf) bool edid_do_checksum(u8 *data) { - int i; - u8 sum = 0; + int i; + u8 sum = 0; - for (i = 0; i < EDID_BLOCK_SIZE; i++) - sum += data[i]; - EDID_DBG("%s: result=%s\n", __func__, sum ? "fail" : "pass"); - return sum ? false : true; + for (i = 0; i < EDID_BLOCK_SIZE; i++) + sum += data[i]; + EDID_DBG("%s: result=%s\n", __func__, sum ? "fail" : "pass"); + return sum ? false : true; } static bool edid_check_header(u8 *data) { - int ret, i = 0; - /* EDID 8 bytes header */ - static u8 header[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}; + int ret = true; - for (i = 0; i < ARRAY_SIZE(header); i++) - if (data[i] != header[i]) - break; - ret = (i == ARRAY_SIZE(header)) ? true : false; - EDID_DBG("%s: result=%s\n", __func__, ret ? "pass" : "fail"); + /* EDID 8 bytes header */ + static const u8 header[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}; - return ret; + if (memcmp(data, header, 8) != 0) + ret = false; + + EDID_DBG("%s: result=%s\n", __func__, ret ? "pass" : "fail"); + if (!edid_do_checksum(data)) + { + pr_err("%s: checksum error\n", __func__); + + // Not all monitors have a proper checksum, so we'll ignore this error and just log the problem. + // ret = false; + } + return ret; } bool edid_check_hdmi_sink(struct hdmi_info *hdmi, int block) { - int ret = false, index = 4; - u8 *data = &hdmi->edid_buf[block * 128]; - /* block offset where long descriptors start */ - int long_desc_offset = data[LONG_DESCR_PTR_IDX]; - int tag_code, data_block_len; - - while (index < long_desc_offset) { - tag_code = (data[index] >> 5) & 0x7; - data_block_len = data[index++] & 0x1f; - if (tag_code == VENDOR_SPEC_D_BLOCK && - data[index] == 0x03 && - data[index + 1] == 0x0c && - data[index + 2] == 0x00) { - ret = true; - break; - } else - index += data_block_len; - } - hdmi->edid_info.hdmi_sink = ret; - EDID_DBG("%s: ret=%s\n", __func__, ret ? "yes" : "no"); - return ret; + int ret = false, index = 4; + u8 *data = &hdmi->edid_buf[block * 128]; + /* block offset where long descriptors start */ + int long_desc_offset = data[LONG_DESCR_PTR_IDX]; + int tag_code, data_block_len; + + while (index < long_desc_offset) { + tag_code = (data[index] >> 5) & 0x7; + data_block_len = data[index++] & 0x1f; + if (tag_code == VENDOR_SPEC_D_BLOCK && + data[index] == 0x03 && + data[index + 1] == 0x0c && + data[index + 2] == 0x00) { + ret = true; + break; + } else + index += data_block_len; + } + hdmi->edid_info.hdmi_sink = ret; + EDID_DBG("%s: ret=%s\n", __func__, ret ? "yes" : "no"); + return ret; } struct edid_black_list_info { - u8 mfr_model[4]; - u8 prefer_modes[3]; + u8 mfr_model[4]; + u8 prefer_modes[3]; }; struct edid_black_list_info edid_black_list[] = { - { {0x4c, 0x2d, 0xa5, 0x02}, {0, 0, 0x40} }, // 720p only - { {0x4c, 0x2d, 0x0d, 0x05}, {0, 0, 0x40} }, // 720p only + { {0x4c, 0x2d, 0xa5, 0x02}, {0, 0, 0x40} }, // 720p only + { {0x4c, 0x2d, 0x0d, 0x05}, {0, 0, 0x40} }, // 720p only - //{ {0x5a, 0x63, 0x20, 0x2b}, {0, 0, 0x40} }, // Viewsonic test + //{ {0x5a, 0x63, 0x20, 0x2b}, {0, 0, 0x40} }, // Viewsonic test }; /* By comparing the Manufacture(0x8, 0x9) and Model field(0xa, 0xb) of EDID, @@ -422,208 +427,241 @@ struct edid_black_list_info edid_black_list[] = { */ int edid_fixup_compatibility_list(struct hdmi_info *hdmi) { - int i, ret = -1; + int i, ret = -1; - /* FIXME: magic numbers...*/ - for (i = 0; i < ARRAY_SIZE(edid_black_list); i++) { - if (!memcmp(hdmi->edid_buf + 8, edid_black_list[i].mfr_model, 4)){ + /* FIXME: magic numbers...*/ + for (i = 0; i < ARRAY_SIZE(edid_black_list); i++) { + if (!memcmp(hdmi->edid_buf + 8, edid_black_list[i].mfr_model, 4)){ #if 0 - EDID_DBG("%s: found in blacklist %d\n", __func__, i); - EDID_DBG("%s: old timing = {%02x, %02x, %02x}\n", - __func__, - hdmi->edid_buf[35], hdmi->edid_buf[36], - hdmi->edid_buf[37]); - memcpy(hdmi->edid_buf + 35, - edid_black_list[i].prefer_modes, 3); - EDID_DBG("%s: new timing = {%02x, %02x, %02x}\n", - __func__, - hdmi->edid_buf[35], hdmi->edid_buf[36], - hdmi->edid_buf[37]); + EDID_DBG("%s: found in blacklist %d\n", __func__, i); + EDID_DBG("%s: old timing = {%02x, %02x, %02x}\n", + __func__, + hdmi->edid_buf[35], hdmi->edid_buf[36], + hdmi->edid_buf[37]); + memcpy(hdmi->edid_buf + 35, + edid_black_list[i].prefer_modes, 3); + EDID_DBG("%s: new timing = {%02x, %02x, %02x}\n", + __func__, + hdmi->edid_buf[35], hdmi->edid_buf[36], + hdmi->edid_buf[37]); #else - EDID_DBG("%s: found in compatibility %d\n", __func__, i); - memcpy(hdmi->edid_buf + 35, - edid_black_list[i].prefer_modes, 3); + EDID_DBG("%s: found in compatibility %d\n", __func__, i); + memcpy(hdmi->edid_buf + 35, + edid_black_list[i].prefer_modes, 3); #endif - ret = i; - break; - } - } + ret = i; + break; + } + } - return ret; + return ret; } u8 edid_simple_parsing(struct hdmi_info *hdmi) { - u8 *edid_buf = hdmi->edid_buf; - int i, index, ret = -EINVAL; - struct edid_info_struct *edid_info = &hdmi->edid_info; - unsigned v1, width, height, aspect; - unsigned extensions; - - EDID_DBG("%s\n", __func__); - // FIXME: integrate with edid_check() - if (!edid_do_checksum(edid_buf)) { - pr_err("%s: checksum error\n", __func__); - //return EDID_CHECKSUM_ERROR; - } - if (!edid_check_header(edid_buf)) { - pr_err("%s: incorrect header\n", __func__); - return INCORRECT_EDID_HEADER; - } - edid_info->under_scan = ((edid_buf[MISC_SUPPORT_IDX]) >> 7) & 0x1; - edid_info->basic_audio = ((edid_buf[MISC_SUPPORT_IDX]) >> 6) & 0x1; - edid_info->ycbcr_4_4_4 = ((edid_buf[MISC_SUPPORT_IDX]) >> 5) & 0x1; - edid_info->ycbcr_4_2_2 = ((edid_buf[MISC_SUPPORT_IDX]) >> 4) & 0x1; - - // FIXME: 0x7e - extensions = edid_buf[0x7e]; - EDID_DBG("%s: extensions=%d\n", __func__, extensions); - if (!extensions) { - hdmi->edid_info.hdmi_sink = false; - return NO_861_EXTENSIONS; - } - //return; - - /* reset all supported */ - for (i = 0 ; i < ARRAY_SIZE(additional_timing_db); i++) - additional_timing_db[i].supported = false; - for (i = 0 ; i < ARRAY_SIZE(established_timing_db); i++) - established_timing_db[i].supported = false; - - /* Block 0: established timing */ - pr_info("established timing: {%02x, %02x, %02x}\n", - edid_buf[35], edid_buf[36], edid_buf[37]); - - v1 = edid_buf[35] | edid_buf[36] << 8 | (!!edid_buf[37]) << 16; - - for (i = 0 ; i < 17; i++ ) // 17 bits defined in established timing - established_timing_db[i].supported = ((v1 >>= 1) & 1) ; + u8 *edid_buf = hdmi->edid_buf; + int i, index, ret = -EINVAL; + struct edid_info_struct *edid_info = &hdmi->edid_info; + unsigned v1, width, height, aspect; + unsigned extensions; + unsigned extension; + + EDID_DBG("%s\n", __func__); + if (!edid_check_header(edid_buf)) + { + pr_err("%s: incorrect header\n", __func__); + return INCORRECT_EDID_HEADER; + } + + // Retrieve the number of extensions in this EDID + extensions = edid_buf[126]; + EDID_DBG("%s: extensions=%d\n", __func__, extensions); + if (!extensions) + { + hdmi->edid_info.hdmi_sink = false; + return NO_861_EXTENSIONS; + } + + /* reset all supported */ + for (i = 0 ; i < ARRAY_SIZE(additional_timing_db); i++) + additional_timing_db[i].supported = false; + for (i = 0 ; i < ARRAY_SIZE(established_timing_db); i++) + established_timing_db[i].supported = false; + + /* Block 0: established timing */ + pr_info("established timing: {%02x, %02x, %02x}\n", + edid_buf[35], edid_buf[36], edid_buf[37]); + + v1 = edid_buf[35] | edid_buf[36] << 8; + if (edid_buf[37] & 0x80) + v1 |= 0x00010000; + + for (i = 0 ; i < 17; i++ ) // 17 bits defined in established timing + established_timing_db[i].supported = ((v1 >>= 1) & 1) ; + + /* standard timing identification */ + for (i = 0; i < 8; i++) { + width = (edid_buf[38 + (i * 2)] * 8) + 248; + v1 = edid_buf[38 + (i * 2) + 1]; + switch (v1 >> 6) { + case 0: height = width * 10 / 16; aspect = ASPECT(16, 10); break; + case 1: height = width * 3 / 4; aspect = ASPECT(4, 3); break; + case 2: height = width * 4 / 5; aspect = ASPECT(5, 4); break; + case 3: height = width * 9 / 16; aspect = ASPECT(16, 9); break; + } + standard_timing_db[i].width = width; + standard_timing_db[i].height = height; + standard_timing_db[i].aspect = aspect; + standard_timing_db[i].refresh_rate = (v1 & 0x3F) + 60; + standard_timing_db[i].supported = true; + } + + for (extension = 0; extension < extensions; extension++) + { + unsigned baseOffset = (extension + 1) * 0x80; + unsigned dtdStart = 0; + unsigned nativeFormats = 0; + unsigned dbcOffset; + + EDID_DBG("Extension %d\n", extension +1); + if (edid_buf[baseOffset] != 0x02) + { + EDID_DBG(" Extension is not CEA EDID format. Skipping.\n"); + continue; + } -#if 0 - /* standard timing identification */ - for (i = 0; i < 8; i++) { - width = edid_buf[38 + i * 2] * 8 + 248; - v1 = edid_buf[38 + i * 2 + 1]; - switch (v1 >> 6) { - case 0: height = width * 10 / 16; aspect = ASPECT(16, 10); break; - case 1: height = width * 3 / 4; aspect = ASPECT(4, 3); break; - case 2: height = width * 4 / 5; aspect = ASPECT(5, 4); break; - case 3: height = width * 9 / 16; aspect = ASPECT(16, 9); break; - } - standard_timing_db[i].width = width; - standard_timing_db[i].height = height; - standard_timing_db[i].aspect = aspect; - standard_timing_db[i].refresh_rate = (v1 & ~(3 << 6)) + 60; - standard_timing_db[i].supported = true; - } -#endif + if (edid_buf[baseOffset+1] < 0x03) + { + EDID_DBG(" CEA EDID Extension is below version 3. Skipping.\n"); + continue; + } + + dtdStart = edid_buf[baseOffset+2]; + if (dtdStart != 0) dtdStart += baseOffset; - if (extensions == 1) { - EDID_DBG("Block-1: additional timing\n"); - /* Block-1: additional timing */ - /* - edid_buf[127] is last byte of standard edid data - edid_buf[128 + 00] should be byte 00 of eedid - edid_buf[128 + 01] byte 01 rev number - edid_buf[128 + 02] byte 02 nonstandard DTD locations, not being parsed yet - edid_buf[128 + 03] byte 03 version info and number of blocks present - edid_buf[128 + 04] byte 04 start of standard DTD block collection - */ - for( i = 0; i < (edid_buf[128 + 3] & 0x0f); i++) { // Change by Ian: only bits 3-0 show length of blocks - // Check if byte type is Video (2) - if ((edid_buf[128 + 4 + i] & 0xe0) == 2){ - // Byte block IS video - index = edid_buf[128 + 4 + i] & 0x7f; - additional_timing_db[index-1].supported = true; - EDID_DBG("%s\n", additional_timing_db[index-1].descrption); - } else { - // Byte is NOT video - int blockLen = (edid_buf[128 + 4 + i] & 0x1f); - i = i + (blockLen - 1); // -1 since loop has i++ alread - } - - } - edid_check_hdmi_sink(hdmi, 1); - } else { - // Probably also contaions errors corrected above but probably not used - EDID_DBG("Block-2: additional timing\n"); - for( i = 0; i < (edid_buf[384 + 4] & 0x1f); i++) { - index = edid_buf[384 + 5 + i] & 0x7f; - additional_timing_db[index-1].supported = true; - EDID_DBG("%s\n", additional_timing_db[index-1].descrption); - } - edid_check_hdmi_sink(hdmi, 3); - } - - // Why are these overwritten? - edid_buf[35] = 0; - edid_buf[36] = 0; - edid_buf[37] = 0; - - /* edid_buf[37] bit4: 480p, bit5: 576p, bit6: 720p */ - if (additional_timing_db[CEA_MODE_720X480P_60HZ_4_3].supported || - additional_timing_db[CEA_MODE_720X480P_60HZ_16_9].supported) { - EDID_DBG("decide to support 480P\n"); - edid_buf[37] |= (1<<4); - } - - if (additional_timing_db[CEA_MODE_720X576P_50HZ_4_3].supported || - additional_timing_db[CEA_MODE_720X576P_50HZ_16_9].supported) { - EDID_DBG("decide to support 576P\n"); - edid_buf[37] |= (1<<5); - } - - if (additional_timing_db[CEA_MODE_1280X720P_60HZ_16_9].supported) { - EDID_DBG("decide to support 720P\n"); - edid_buf[37] |= (1<<6); - } - - if (established_timing_db[ESTABLISHED_MODE_800X600_60HZ].supported) { - EDID_DBG("decide to support 800x600\n"); - edid_buf[36] |= (1<<6); - } - - if (established_timing_db[ESTABLISHED_MODE_640X480_60HZ].supported) { - EDID_DBG("decide to support 640x480\n"); - edid_buf[35] |= (1<<5); - } - edid_fixup_compatibility_list(hdmi); - - return ret; + edid_info->under_scan = edid_buf[baseOffset+3] & EDID_BIT(7); + edid_info->basic_audio = edid_buf[baseOffset+3] & EDID_BIT(6); + edid_info->ycbcr_4_4_4 = edid_buf[baseOffset+3] & EDID_BIT(5); + edid_info->ycbcr_4_2_2 = edid_buf[baseOffset+3] & EDID_BIT(4); + + nativeFormats = edid_buf[baseOffset+3] & 0x04; + dbcOffset = 4; + + for (i = 0; i < (edid_buf[baseOffset+3] & 0x0f); i++) // Change by Ian: only bits 3-0 show length of blocks + { + unsigned blockType = edid_buf[baseOffset + dbcOffset] >> 5; + unsigned blockLen = edid_buf[baseOffset + dbcOffset] & 0x1f; + unsigned byte; + + // Skip any block which isn't video + if (blockType == 3) + { + // We may have found the HDMI sink + if (edid_buf[baseOffset + dbcOffset + 1] == 0x00 && + edid_buf[baseOffset + dbcOffset + 2] == 0x0C && + edid_buf[baseOffset + dbcOffset + 3] == 0x03 ) + { + // We found the HDMI block + edid_check_hdmi_sink(hdmi, i + 1); + } + continue; + } + if (blockType != 2) + { + dbcOffset += blockLen; + continue; + } + + // The block will be an array of indexes + for (byte = 1; byte < blockLen; byte++) + { + index = edid_buf[baseOffset + dbcOffset] & 0x7f; + + if (index > 63) + { + EDID_DBG("Invalid index in EDID Video block. Ignoring.\n"); + } + else + { + additional_timing_db[index-1].supported = true; + EDID_DBG("%s\n", additional_timing_db[index-1].descrption); + } + } + + dbcOffset += blockLen; + } + } + + // As a cheat, we're replacing the existing timings with our own "custom" + // definition of these bytes. + edid_buf[35] = 0; + edid_buf[36] = 0; + edid_buf[37] = 0; + + /* edid_buf[37] bit4: 480p, bit5: 576p, bit6: 720p */ + if (additional_timing_db[CEA_MODE_720X480P_60HZ_4_3].supported || + additional_timing_db[CEA_MODE_720X480P_60HZ_16_9].supported) { + EDID_DBG("decide to support 480P\n"); + edid_buf[37] |= (1<<4); + } + + if (additional_timing_db[CEA_MODE_720X576P_50HZ_4_3].supported || + additional_timing_db[CEA_MODE_720X576P_50HZ_16_9].supported) { + EDID_DBG("decide to support 576P\n"); + edid_buf[37] |= (1<<5); + } + + if (additional_timing_db[CEA_MODE_1280X720P_60HZ_16_9].supported) { + EDID_DBG("decide to support 720P\n"); + edid_buf[37] |= (1<<6); + } + + if (established_timing_db[ESTABLISHED_MODE_800X600_60HZ].supported) { + EDID_DBG("decide to support 800x600\n"); + edid_buf[36] |= (1<<6); + } + + if (established_timing_db[ESTABLISHED_MODE_640X480_60HZ].supported) { + EDID_DBG("decide to support 640x480\n"); + edid_buf[35] |= (1<<5); + } + edid_fixup_compatibility_list(hdmi); + + return ret; } // FIXME: modify the checking routines into inline function. bool edid_is_video_mode_supported(struct video_mode *vmode) { - int i; - struct video_mode *vmode_db; - - vmode_db = established_timing_db; - for (i = 0, vmode_db = established_timing_db; - i < ARRAY_SIZE(established_timing_db); i++) - if ( (vmode->width == vmode_db[i].width) && - (vmode->height == vmode_db[i].height) && - (vmode->refresh_rate == vmode_db[i].refresh_rate ) && - (vmode_db[i].interlaced == PROGRESSIVE) && - (vmode_db[i].supported == true )) - return true; - for (i = 0, vmode_db = standard_timing_db; - i < ARRAY_SIZE(standard_timing_db); i++) - if ( (vmode->width == vmode_db[i].width) && - (vmode->height == vmode_db[i].height) && - (vmode->refresh_rate == vmode_db[i].refresh_rate ) && - (vmode_db[i].interlaced == PROGRESSIVE) && - (vmode_db[i].supported == true )) - return true; + int i; + struct video_mode *vmode_db; + + vmode_db = established_timing_db; + for (i = 0, vmode_db = established_timing_db; + i < ARRAY_SIZE(established_timing_db); i++) + if ( (vmode->width == vmode_db[i].width) && + (vmode->height == vmode_db[i].height) && + (vmode->refresh_rate == vmode_db[i].refresh_rate ) && + (vmode_db[i].interlaced == PROGRESSIVE) && + (vmode_db[i].supported == true )) + return true; + for (i = 0, vmode_db = standard_timing_db; + i < ARRAY_SIZE(standard_timing_db); i++) + if ( (vmode->width == vmode_db[i].width) && + (vmode->height == vmode_db[i].height) && + (vmode->refresh_rate == vmode_db[i].refresh_rate ) && + (vmode_db[i].interlaced == PROGRESSIVE) && + (vmode_db[i].supported == true )) + return true; for (i = 0, vmode_db = additional_timing_db; i < ARRAY_SIZE(additional_timing_db); i++) if ( (vmode->width == vmode_db[i].width) && (vmode->height == vmode_db[i].height) && (vmode->refresh_rate == vmode_db[i].refresh_rate ) && (vmode_db[i].interlaced == PROGRESSIVE) && - (vmode_db[i].supported == true )) + (vmode_db[i].supported == true )) return true; - return false; + return false; } bool edid_check_sink_type(struct hdmi_info *hdmi) @@ -652,10 +690,10 @@ int edid_dump_hex(u8 *src, int src_size, char *output, int output_size) } line[line_size - 1] = '\n'; strncpy(output+ n, line, line_size); - if ((n + line_size) > output_size) - break; - else - n += line_size; + if ((n + line_size) > output_size) + break; + else + n += line_size; } return n; @@ -669,22 +707,16 @@ static ssize_t edid_dbg_open(struct inode *inode, struct file *file) } static char hex_buff[2048]; + static ssize_t edid_buffered_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { - int n; - int extensions; - struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; - - extensions = hdmi->edid_buf[0x7e] + 1; - edid_dump_video_modes(hdmi->edid_buf);// fixme: crashed - if (extensions == 1) - edid_dump_video_modes(hdmi->edid_buf + 128); - else - edid_dump_video_modes(hdmi->edid_buf + 384); - //edid_simple_parsing(hdmi); // FIXME: crashed... - n = edid_dump_hex(hdmi->edid_buf, (hdmi->edid_buf[0x7e] + 1) * 128, - hex_buff, 2048); + int n; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + edid_simple_parsing(hdmi); + n = edid_dump_hex(hdmi->edid_buf, (hdmi->edid_buf[0x7e] + 1) * 128, + hex_buff, 2048); return simple_read_from_buffer(buf, count, ppos, hex_buff, n); } @@ -700,7 +732,7 @@ static struct file_operations edid_debugfs_fops[] = { int edid_debugfs_init(struct hdmi_info *hdmi) { //int ret; - struct dentry *edid_dent; + struct dentry *edid_dent; edid_dent = debugfs_create_dir("edid", hdmi->debug_dir); if (IS_ERR(edid_dent)) From 837c9eab95cdbdf96d297a30da913ea125041480 Mon Sep 17 00:00:00 2001 From: Kevin Bruckert Date: Tue, 22 Mar 2011 14:07:56 -0500 Subject: [PATCH 1669/2556] Missing file --- drivers/video/msm/hdmi/include/edid.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/video/msm/hdmi/include/edid.h b/drivers/video/msm/hdmi/include/edid.h index 1b7e4587e5c6e..4a6f9ac436c52 100644 --- a/drivers/video/msm/hdmi/include/edid.h +++ b/drivers/video/msm/hdmi/include/edid.h @@ -79,4 +79,7 @@ struct edid_info_struct { #define HDMI_SIGNATURE_LEN 0x03 #define CEC_PHYS_ADDR_LEN 0x02 + +#define EDID_BIT(b) (1 << b) + #endif From 2766ba173c80ce800c73ada5a6b3179690f872af Mon Sep 17 00:00:00 2001 From: Kevin Bruckert Date: Tue, 22 Mar 2011 14:24:44 -0500 Subject: [PATCH 1670/2556] Fix minor bug in EDID code --- drivers/video/msm/hdmi/edid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/msm/hdmi/edid.c b/drivers/video/msm/hdmi/edid.c index 36f629c2be43b..642a000701296 100644 --- a/drivers/video/msm/hdmi/edid.c +++ b/drivers/video/msm/hdmi/edid.c @@ -575,7 +575,7 @@ u8 edid_simple_parsing(struct hdmi_info *hdmi) // The block will be an array of indexes for (byte = 1; byte < blockLen; byte++) { - index = edid_buf[baseOffset + dbcOffset] & 0x7f; + index = edid_buf[baseOffset + dbcOffset + byte] & 0x7f; if (index > 63) { From acad9e28079367c9a126116ae3b71da1a1c8b9ee Mon Sep 17 00:00:00 2001 From: mikeandroid Date: Sun, 1 May 2011 21:24:41 -0400 Subject: [PATCH 1671/2556] [ARM] usb: msm72k_udc: Fix handling of OUT packets on ep0 with length > 0 The gadget level completion function was getting called for the ACK packet instead of the data packet, making it impossible to read the data from the host. Signed-off-by: Mike Lockwood --- drivers/usb/gadget/msm72k_udc_htc.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/msm72k_udc_htc.c b/drivers/usb/gadget/msm72k_udc_htc.c index 5b7915f2353ce..fc7c71f2ca52a 100644 --- a/drivers/usb/gadget/msm72k_udc_htc.c +++ b/drivers/usb/gadget/msm72k_udc_htc.c @@ -64,6 +64,7 @@ static const char driver_name[] = "msm72k_udc"; #define SETUP_BUF_SIZE 4096 +typedef void (*completion_func)(struct usb_ep *ep, struct usb_request *req); static const char *const ep_name[] = { "ep0out", "ep1out", "ep2out", "ep3out", @@ -108,8 +109,7 @@ struct msm_request { struct usb_request req; /* saved copy of req.complete */ - void (*gadget_complete)(struct usb_ep *ep, - struct usb_request *req); + completion_func gadget_complete; struct usb_info *ui; @@ -656,7 +656,7 @@ static void ep0_complete(struct usb_ep *ep, struct usb_request *req) struct usb_info *ui = ept->ui; req->complete = r->gadget_complete; - r->gadget_complete = 0; + r->gadget_complete = NULL; if (req->complete) req->complete(&ui->ep0in.ep, req); } @@ -664,10 +664,16 @@ static void ep0_complete(struct usb_ep *ep, struct usb_request *req) static void ep0_queue_ack_complete(struct usb_ep *ep, struct usb_request *_req) { - struct msm_request *r = to_msm_request(_req); struct msm_endpoint *ept = to_msm_endpoint(ep); struct usb_info *ui = ept->ui; struct usb_request *req = ui->setup_req; + struct msm_request *r = to_msm_request(req); + completion_func gadget_complete = r->gadget_complete; + + if (gadget_complete) { + r->gadget_complete = NULL; + gadget_complete(ep, req); + } /* queue up the receive of the ACK response from the host */ if (_req->status == 0 && _req->actual == _req->length) { @@ -677,7 +683,7 @@ static void ep0_queue_ack_complete(struct usb_ep *ep, else usb_ept_queue_xfer(&ui->ep0in, req); _req->complete = r->gadget_complete; - r->gadget_complete = 0; + r->gadget_complete = NULL; if (_req->complete) _req->complete(&ui->ep0in.ep, _req); } else @@ -741,7 +747,7 @@ static void ep0_setup_send(struct usb_info *ui, unsigned length) req->length = length; req->complete = ep0_queue_ack_complete; - r->gadget_complete = 0; + r->gadget_complete = NULL; usb_ept_queue_xfer(ept, req); } From e541532c394c0cabd4dfac0de43f30fdd77efeaa Mon Sep 17 00:00:00 2001 From: mikeandroid Date: Sun, 1 May 2011 21:31:00 -0400 Subject: [PATCH 1672/2556] [ARM] msm: mahimahi: Add platform data for USB accessory function --- arch/arm/mach-msm/board-supersonic.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index be1a22ec802af..537241638db40 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -24,6 +24,7 @@ #include #include #include +#include #include // #include #include @@ -346,6 +347,11 @@ static char *usb_functions_rndis_adb[] = { "adb", }; +#ifdef CONFIG_USB_ANDROID_ACCESSORY +static char *usb_functions_accessory[] = { "accessory" }; +static char *usb_functions_accessory_adb[] = { "accessory", "adb" }; +#endif + #ifdef CONFIG_USB_ANDROID_DIAG static char *usb_functions_adb_diag[] = { "usb_mass_storage", @@ -366,6 +372,9 @@ static char *usb_functions_all[] = { #ifdef CONFIG_USB_ANDROID_DIAG "diag", #endif +#ifdef CONFIG_USB_ANDROID_ACCESSORY + "accessory", +#endif }; static struct android_usb_product usb_products[] = { @@ -389,6 +398,18 @@ static struct android_usb_product usb_products[] = { .num_functions = ARRAY_SIZE(usb_functions_rndis_adb), .functions = usb_functions_rndis_adb, }, +#ifdef CONFIG_USB_ANDROID_ACCESSORY + { + .product_id = USB_ACCESSORY_PRODUCT_ID, + .num_functions = ARRAY_SIZE(usb_functions_accessory), + .functions = usb_functions_accessory, + }, + { + .product_id = USB_ACCESSORY_ADB_PRODUCT_ID, + .num_functions = ARRAY_SIZE(usb_functions_accessory_adb), + .functions = usb_functions_accessory_adb, + }, +#endif #ifdef CONFIG_USB_ANDROID_DIAG { .product_id = 0x0c07, @@ -396,6 +417,7 @@ static struct android_usb_product usb_products[] = { .functions = usb_functions_adb_diag, }, #endif + }; static struct usb_mass_storage_platform_data mass_storage_pdata = { From ebf71e3fa83e2664bee491d06670142450c11cfc Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sun, 1 May 2011 21:33:29 -0400 Subject: [PATCH 1673/2556] drivers: input: touchscreen: atmel: fix todo comment --- drivers/input/touchscreen/atmel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel.c b/drivers/input/touchscreen/atmel.c index b9ca36673fc20..a7c03097f761f 100644 --- a/drivers/input/touchscreen/atmel.c +++ b/drivers/input/touchscreen/atmel.c @@ -1520,7 +1520,7 @@ static int atmel_ts_probe(struct i2c_client *client, dev_info(&client->dev, "Start touchscreen %s in interrupt mode\n", ts->input_dev->name); -//fix me TODO usb_register_notifier(&cable_status_handler); + usb_register_notifier(&cable_status_handler); return 0; From c978257734d079849d1279b09a60e19d28611321 Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sun, 1 May 2011 21:36:01 -0400 Subject: [PATCH 1674/2556] drivers: usb: gadget: Makefile: add ifdef for mainline msm72k_udc driver --- drivers/usb/gadget/Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index a965eb8b27377..27fba29e570af 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -24,7 +24,11 @@ obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o -obj-$(CONFIG_USB_MSM_72K_HTC) += msm72k_udc_htc.o +ifeq ($(CONFIG_USB_MSM_72K_HTC),y) +obj-$(CONFIG_USB_MSM_72K_HTC) += msm72k_udc_htc.o +else +obj-$(CONFIG_USB_MSM_72K) += msm72k_udc.o +endif # # USB gadget drivers From 3ea1db49c2f8b7a18f947399d26d312c9191997b Mon Sep 17 00:00:00 2001 From: toastcfh Date: Sun, 1 May 2011 21:42:40 -0400 Subject: [PATCH 1675/2556] arm: configs: supersonic: update defconfig --- .../arm/configs/cyanogen_supersonic_defconfig | 76 ++++++++++++------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/arch/arm/configs/cyanogen_supersonic_defconfig b/arch/arm/configs/cyanogen_supersonic_defconfig index 710b72e47c39c..7f3d6899d6a82 100644 --- a/arch/arm/configs/cyanogen_supersonic_defconfig +++ b/arch/arm/configs/cyanogen_supersonic_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.4 Kernel Configuration -# Sat Apr 30 18:43:34 2011 +# Sun May 1 20:26:43 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -31,10 +31,11 @@ CONFIG_HAVE_IRQ_WORK=y # CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" -CONFIG_LOCALVERSION="-cyanogenmod" -CONFIG_LOCALVERSION_AUTO=y +CONFIG_LOCALVERSION="-GoDm0dE" +# CONFIG_LOCALVERSION_AUTO is not set CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y CONFIG_HAVE_KERNEL_LZO=y @@ -64,28 +65,37 @@ CONFIG_HAVE_SPARSE_IRQ=y # RCU Subsystem # # CONFIG_TREE_PREEMPT_RCU is not set -CONFIG_TINY_RCU=y -# CONFIG_TINY_PREEMPT_RCU is not set -# CONFIG_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +CONFIG_TINY_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y # CONFIG_RCU_TRACE is not set # CONFIG_TREE_RCU_TRACE is not set +CONFIG_RCU_BOOST=y +CONFIG_RCU_BOOST_PRIO=1 +CONFIG_RCU_BOOST_DELAY=500 CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=17 CONFIG_CGROUPS=y -# CONFIG_CGROUP_DEBUG is not set -# CONFIG_CGROUP_NS is not set -# CONFIG_CGROUP_FREEZER is not set -# CONFIG_CGROUP_DEVICE is not set +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_NS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y # CONFIG_CPUSETS is not set CONFIG_CGROUP_CPUACCT=y -# CONFIG_RESOURCE_COUNTERS is not set +CONFIG_RESOURCE_COUNTERS=y +# CONFIG_CGROUP_MEM_RES_CTLR is not set CONFIG_CGROUP_SCHED=y CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y -# CONFIG_BLK_CGROUP is not set -# CONFIG_NAMESPACES is not set -# CONFIG_SCHED_AUTOGROUP is not set +CONFIG_BLK_CGROUP=y +# CONFIG_DEBUG_BLK_CGROUP is not set +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_USER_NS=y +CONFIG_PID_NS=y +# CONFIG_NET_NS is not set +CONFIG_SCHED_AUTOGROUP=y # CONFIG_SYSFS_DEPRECATED is not set # CONFIG_RELAY is not set CONFIG_BLK_DEV_INITRD=y @@ -157,19 +167,18 @@ CONFIG_MODULE_FORCE_UNLOAD=y # CONFIG_MODULE_SRCVERSION_ALL is not set CONFIG_BLOCK=y CONFIG_LBDAF=y -# CONFIG_BLK_DEV_BSG is not set -# CONFIG_BLK_DEV_INTEGRITY is not set +CONFIG_BLK_DEV_BSG=y +CONFIG_BLK_DEV_INTEGRITY=y +CONFIG_BLK_DEV_THROTTLING=y # # IO Schedulers # CONFIG_IOSCHED_NOOP=y -CONFIG_IOSCHED_DEADLINE=y -CONFIG_IOSCHED_CFQ=y +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set CONFIG_IOSCHED_BFQ=y -# CONFIG_CGROUP_BFQIO is not set -# CONFIG_DEFAULT_DEADLINE is not set -# CONFIG_DEFAULT_CFQ is not set +CONFIG_CGROUP_BFQIO=y CONFIG_DEFAULT_BFQ=y # CONFIG_DEFAULT_NOOP is not set CONFIG_DEFAULT_IOSCHED="bfq" @@ -327,14 +336,14 @@ CONFIG_MSM_DAL=y CONFIG_MSM_ONCRPCROUTER=y CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y CONFIG_MSM_CPU_FREQ_MAX=998400 -CONFIG_MSM_CPU_FREQ_MIN=245760 +CONFIG_MSM_CPU_FREQ_MIN=245000 # CONFIG_AXI_SCREEN_POLICY is not set -# CONFIG_MSM_HW3D is not set +CONFIG_MSM_HW3D=y CONFIG_HTC_ACOUSTIC_QSD=y CONFIG_MSM_QDSP6=y CONFIG_WIFI_CONTROL_FUNC=y -# CONFIG_WIFI_MEM_PREALLOC is not set -CONFIG_ARCH_MSM_FLASHLIGHT=n +CONFIG_WIFI_MEM_PREALLOC=y +# CONFIG_ARCH_MSM_FLASHLIGHT is not set CONFIG_MICROP_COMMON=y CONFIG_HTC_HEADSET_MGR=y CONFIG_HTC_HEADSET_GPIO=y @@ -725,6 +734,9 @@ CONFIG_BRIDGE_IGMP_SNOOPING=y # CONFIG_DECNET is not set CONFIG_LLC=y # CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set # CONFIG_LAPB is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set @@ -942,7 +954,7 @@ CONFIG_MTD_NAND_IDS=y CONFIG_BLK_DEV=y # CONFIG_BLK_DEV_COW_COMMON is not set CONFIG_BLK_DEV_LOOP=y -# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_CRYPTOLOOP=y # # DRBD disabled because PROC_FS, INET or CONNECTOR not selected @@ -1146,6 +1158,7 @@ CONFIG_INPUT_GPIO=y # CONFIG_INPUT_ADXL34X is not set # CONFIG_INPUT_CMA3000 is not set # CONFIG_INPUT_CAPELLA_CM3602 is not set +CONFIG_LIGHTSENSOR_MICROP=y # # Hardware I/O ports @@ -1685,6 +1698,7 @@ CONFIG_ANDROID_TIMED_OUTPUT=y CONFIG_ANDROID_TIMED_GPIO=y CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_POHMELFS is not set +# CONFIG_AUTOFS_FS is not set # CONFIG_IIO is not set # CONFIG_ZRAM is not set # CONFIG_FB_SM7XX is not set @@ -1693,6 +1707,7 @@ CONFIG_ANDROID_LOW_MEMORY_KILLER=y # Texas Instruments shared transport line discipline # # CONFIG_ST_BT is not set +# CONFIG_SMB_FS is not set CONFIG_MACH_NO_WESTBRIDGE=y # CONFIG_ATH6K_LEGACY is not set # CONFIG_FT1000 is not set @@ -1768,6 +1783,7 @@ CONFIG_TMPFS=y # CONFIG_HUGETLB_PAGE is not set # CONFIG_CONFIGFS_FS is not set CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_HFSPLUS_FS is not set @@ -1793,9 +1809,11 @@ CONFIG_YAFFS_XATTR=y # CONFIG_VXFS_FS is not set # CONFIG_MINIX_FS is not set # CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set # CONFIG_QNX4FS_FS is not set # CONFIG_ROMFS_FS is not set # CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set CONFIG_NETWORK_FILESYSTEMS=y # CONFIG_NFS_FS is not set # CONFIG_NFSD is not set @@ -1887,8 +1905,8 @@ CONFIG_TIMER_STATS=y # CONFIG_DEBUG_RT_MUTEXES is not set # CONFIG_RT_MUTEX_TESTER is not set # CONFIG_DEBUG_SPINLOCK is not set -# CONFIG_DEBUG_MUTEXES is not set -# CONFIG_BKL is not set +CONFIG_DEBUG_MUTEXES=y +CONFIG_BKL=y # CONFIG_DEBUG_LOCK_ALLOC is not set # CONFIG_PROVE_LOCKING is not set # CONFIG_SPARSE_RCU_POINTER is not set @@ -2050,7 +2068,7 @@ CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_ANSI_CPRNG is not set # CONFIG_CRYPTO_USER_API_HASH is not set # CONFIG_CRYPTO_USER_API_SKCIPHER is not set -# CONFIG_CRYPTO_HW is not set +CONFIG_CRYPTO_HW=y # CONFIG_BINARY_PRINTF is not set # From 49365d67bc9f939b5bbf8115f3c72a88bbe73971 Mon Sep 17 00:00:00 2001 From: Tony Layher Date: Sun, 6 Feb 2011 01:37:11 -0500 Subject: [PATCH 1676/2556] Incrediblec: New .38 kernel based on toastcfh's repo. * Includes all imported files from incrediblec source * Modified some files for proper includes and functions. --- .../configs/cyanogen_incrediblec_defconfig | 2112 +++++++++++++++++ arch/arm/include/asm/mach/mmc.h | 2 + arch/arm/mach-msm/Kconfig | 35 + arch/arm/mach-msm/Makefile | 30 +- arch/arm/mach-msm/board-incrediblec-audio.c | 277 +++ arch/arm/mach-msm/board-incrediblec-keypad.c | 137 ++ arch/arm/mach-msm/board-incrediblec-microp.c | 454 ++++ arch/arm/mach-msm/board-incrediblec-mmc.c | 314 +++ arch/arm/mach-msm/board-incrediblec-panel.c | 1211 ++++++++++ arch/arm/mach-msm/board-incrediblec-rfkill.c | 126 + .../mach-msm/board-incrediblec-tpa2018d1.c | 368 +++ .../mach-msm/board-incrediblec-tpa2018d1.h | 35 + arch/arm/mach-msm/board-incrediblec-tpa6130.c | 185 ++ arch/arm/mach-msm/board-incrediblec-tv.c | 125 + arch/arm/mach-msm/board-incrediblec-wifi.c | 140 ++ arch/arm/mach-msm/board-incrediblec.c | 1611 +++++++++++++ arch/arm/mach-msm/board-incrediblec.h | 201 ++ arch/arm/mach-msm/devices-qsd8x50.c | 5 + arch/arm/mach-msm/include/mach/irqs.h | 2 +- arch/arm/mach-msm/include/mach/msm_panel.h | 38 + arch/arm/mach-msm/include/mach/smem_log.h | 232 ++ arch/arm/mach-msm/include/mach/system.h | 1 + arch/arm/mach-msm/proc_engineerid.c | 70 + arch/arm/mach-msm/smem_log.c | 2034 ++++++++++++++++ drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/msm72k_udc_htc.c | 16 +- include/linux/i2c-msm.h | 26 + 27 files changed, 9771 insertions(+), 17 deletions(-) create mode 100644 arch/arm/configs/cyanogen_incrediblec_defconfig create mode 100644 arch/arm/mach-msm/board-incrediblec-audio.c create mode 100644 arch/arm/mach-msm/board-incrediblec-keypad.c create mode 100644 arch/arm/mach-msm/board-incrediblec-microp.c create mode 100644 arch/arm/mach-msm/board-incrediblec-mmc.c create mode 100644 arch/arm/mach-msm/board-incrediblec-panel.c create mode 100644 arch/arm/mach-msm/board-incrediblec-rfkill.c create mode 100644 arch/arm/mach-msm/board-incrediblec-tpa2018d1.c create mode 100644 arch/arm/mach-msm/board-incrediblec-tpa2018d1.h create mode 100644 arch/arm/mach-msm/board-incrediblec-tpa6130.c create mode 100644 arch/arm/mach-msm/board-incrediblec-tv.c create mode 100644 arch/arm/mach-msm/board-incrediblec-wifi.c create mode 100644 arch/arm/mach-msm/board-incrediblec.c create mode 100644 arch/arm/mach-msm/board-incrediblec.h create mode 100644 arch/arm/mach-msm/include/mach/msm_panel.h create mode 100644 arch/arm/mach-msm/include/mach/smem_log.h create mode 100644 arch/arm/mach-msm/proc_engineerid.c create mode 100644 arch/arm/mach-msm/smem_log.c create mode 100644 include/linux/i2c-msm.h diff --git a/arch/arm/configs/cyanogen_incrediblec_defconfig b/arch/arm/configs/cyanogen_incrediblec_defconfig new file mode 100644 index 0000000000000..4ebbf9ccf7006 --- /dev/null +++ b/arch/arm/configs/cyanogen_incrediblec_defconfig @@ -0,0 +1,2112 @@ +# +# Automatically generated make config: don't edit +# Linux/arm 2.6.37 Kernel Configuration +# Sat Feb 5 23:41:40 2011 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y +CONFIG_HAVE_IRQ_WORK=y +CONFIG_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="arm-eabi-" +CONFIG_LOCALVERSION="-cyanogenmod" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +CONFIG_KERNEL_LZMA=y +# CONFIG_KERNEL_LZO is not set +# CONFIG_SWAP is not set +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +# CONFIG_HAVE_GENERIC_HARDIRQS is not set +# CONFIG_SPARSE_IRQ is not set + +# +# RCU Subsystem +# +# CONFIG_TREE_PREEMPT_RCU is not set +CONFIG_TINY_RCU=y +# CONFIG_TINY_PREEMPT_RCU is not set +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_CGROUPS=y +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CGROUP_NS is not set +# CONFIG_CGROUP_FREEZER is not set +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +# CONFIG_RESOURCE_COUNTERS is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +# CONFIG_BLK_CGROUP is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_ASHMEM=y +CONFIG_AIO=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +CONFIG_PERF_EVENTS=y +# CONFIG_PERF_COUNTERS is not set +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_HW_BREAKPOINT=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +CONFIG_ARCH_MSM=y +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_S5PV310 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_TCC_926 is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_ARCH_MSM7X00A is not set +# CONFIG_ARCH_MSM7X30 is not set +CONFIG_ARCH_QSD8X50=y +# CONFIG_ARCH_MSM8X60 is not set +CONFIG_ARCH_MSM_SCORPION=y +CONFIG_HAS_MSM_DEBUG_UART_PHYS=y +CONFIG_MSM_MDP31=y +# CONFIG_PERFLOCK is not set + +# +# Qualcomm MSM Board Type +# +# CONFIG_MACH_SWORDFISH is not set +CONFIG_MACH_QSD8X50_SURF=y +# CONFIG_MACH_QSD8X50A_ST1_5 is not set +CONFIG_MSM_DEBUG_UART_NONE=y +# CONFIG_MSM_DEBUG_UART1 is not set +# CONFIG_MSM_DEBUG_UART2 is not set +# CONFIG_MSM_DEBUG_UART3 is not set +CONFIG_MSM_PROC_COMM=y +# CONFIG_MACH_MAHIMAHI is not set +CONFIG_MACH_BRAVO_NONE=y +# CONFIG_MACH_BRAVO is not set +# CONFIG_MACH_BRAVOC is not set +# CONFIG_MACH_INCREDIBLE is not set +CONFIG_MACH_INCREDIBLEC=y +# CONFIG_MACH_SUPERSONIC is not set +# CONFIG_MACH_QSD8X50_FFA is not set +# CONFIG_JESUS_PHONE is not set +# CONFIG_HTC_HEADSET is not set +# CONFIG_HTC_35MM_JACK is not set +CONFIG_HTC_BATTCHG=y +CONFIG_HTC_BATTCHG_SMEM=y +# CONFIG_HTC_PWRSPLY is not set +# CONFIG_HTC_PWRSINK is not set +# CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set +# CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_MSM7X00A_USE_GP_TIMER=y +# CONFIG_MSM7X00A_USE_DG_TIMER is not set +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=2 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=20000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_FIQ_SUPPORT=y +CONFIG_MSM_SERIAL_DEBUGGER=y +CONFIG_MSM_SERIAL_DEBUGGER_NO_SLEEP=y +# CONFIG_MSM_SERIAL_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON is not set +# CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_DAL=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y +CONFIG_MSM_CPU_FREQ_MAX=998000 +CONFIG_MSM_CPU_FREQ_MIN=245760 +# CONFIG_AXI_SCREEN_POLICY is not set +# CONFIG_MSM_HW3D is not set +# CONFIG_HTC_ACOUSTIC is not set +CONFIG_HTC_ACOUSTIC_QSD=y +CONFIG_MSM_QDSP6=y +CONFIG_WIFI_CONTROL_FUNC=y +# CONFIG_WIFI_MEM_PREALLOC is not set +CONFIG_ARCH_MSM_FLASHLIGHT=y +CONFIG_MICROP_COMMON=y +CONFIG_HTC_HEADSET_MGR=y +CONFIG_HTC_HEADSET_GPIO=y +CONFIG_HTC_HEADSET_MICROP=y +# CONFIG_VIRTUAL_KPANIC_PARTITION is not set + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_VERIFY_PERMISSION_FAULT=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_CPU_HAS_PMU=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_HW_PERF_EVENTS=y +CONFIG_VMALLOC_RESERVE=0x08000000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_NEED_PER_CPU_KM=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +# CONFIG_CPU_FREQ_DEBUG is not set +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND_NVS=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM_OPS=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_ASK_IP_FIB_HASH=y +# CONFIG_IP_FIB_TRIE is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_IP_ROUTE_MULTIPATH is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_XFRM_TUNNEL=y +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +# CONFIG_NF_CONNTRACK_H323 is not set +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_LED=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +CONFIG_NETFILTER_XT_MATCH_OWNER=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +CONFIG_NETFILTER_XT_MATCH_RECENT=y +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +# CONFIG_NF_NAT_SNMP_BASIC is not set +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +# CONFIG_NF_NAT_H323 is not set +CONFIG_NF_NAT_SIP=y +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_RAW is not set +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV6 is not set +# CONFIG_NF_CONNTRACK_IPV6 is not set +# CONFIG_IP6_NF_QUEUE is not set +# CONFIG_IP6_NF_IPTABLES is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +CONFIG_NET_SCH_INGRESS=y + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +# CONFIG_GACT_PROB is not set +CONFIG_NET_ACT_MIRRED=y +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +CONFIG_DNS_RESOLVER=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_PRIV=y +# CONFIG_CFG80211 is not set +CONFIG_WIRELESS_EXT_SYSFS=y +# CONFIG_LIB80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# + +# +# Some wireless drivers require a rate control algorithm +# +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_MSM_NAND=y +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_CRYPTOLOOP=y + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set +CONFIG_MISC_DEVICES=y +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_KERNEL_DEBUGGER_CORE=y +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +CONFIG_SENSORS_AKM8973=y +# CONFIG_SENSORS_AKM8976 is not set +CONFIG_VP_A1026=y +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_SENSORS_BMA150_SPI=y +# CONFIG_APANIC is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_IFB=m +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set +CONFIG_MII=y +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set +# CONFIG_ENC28J60 is not set +# CONFIG_ETHOC is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +CONFIG_WLAN=y +CONFIG_BCM4329=m +CONFIG_BCM4329_FW_PATH="/system/etc/firmware/fw_bcm4329.bin" +CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" +# CONFIG_HOSTAP is not set +# CONFIG_TIWLAN1251 is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set + +# +# CAIF transport drivers +# +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +# CONFIG_PPPOE is not set +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_GAN_ETH is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL=y +CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT=y +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELAN_I2C_8232 is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_QT602240 is not set +# CONFIG_TOUCHSCREEN_MSM is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CAPELLA_CM3602 is not set +# CONFIG_INPUT_CAPELLA_CM3602_HTC is not set +# CONFIG_LIGHTSENSOR_MICROP is not set +CONFIG_INPUT_OPTICALJOYSTICK=y +CONFIG_OPTICALJOYSTICK_CRUCIAL=y +CONFIG_OPTICALJOYSTICK_CRUCIAL_uP=y +# CONFIG_OPTICALJOYSTICK_CRUCIAL_SPI is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_MSM=y +# CONFIG_SERIAL_MSM_CONSOLE is not set +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_BCM_BT_LPM=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_MSM=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +CONFIG_SPI_QSD=y +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set + +# +# PPS support +# +# CONFIG_PPS is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_BASIC_MMIO is not set +# CONFIG_GPIO_IT8761E is not set +# CONFIG_GPIO_VX855 is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_DS2784 is not set +# CONFIG_BATTERY_BQ20Z75 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_MFD_SUPPORT=y +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_TPS65200 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC35892 is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_TPS6586X is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +CONFIG_REGULATOR_TPS65023=y +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_AD5398 is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +CONFIG_IR_CORE=y +CONFIG_VIDEO_IR=y +CONFIG_LIRC=y +CONFIG_RC_MAP=y +CONFIG_IR_NEC_DECODER=y +CONFIG_IR_RC5_DECODER=y +CONFIG_IR_RC6_DECODER=y +CONFIG_IR_JVC_DECODER=y +CONFIG_IR_SONY_DECODER=y +CONFIG_IR_RC5_SZ_DECODER=y +CONFIG_IR_LIRC_CODEC=y +# CONFIG_IR_IMON is not set +# CONFIG_IR_MCEUSB is not set +# CONFIG_IR_STREAMZAP is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +CONFIG_720P_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set + +# +# Camera Sensor Selection +# +# CONFIG_MT9T013 is not set +# CONFIG_MT9D112 is not set +# CONFIG_MT9P012 is not set +# CONFIG_S5K3E2FX is not set +# CONFIG_S5K6AAFX is not set +# CONFIG_OV8810 is not set +# CONFIG_OV9665 is not set +# CONFIG_S5K3H1GX is not set +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_LEGACY_MDP=y +CONFIG_FB_MSM_MDP_PPP=y +CONFIG_FB_MSM_LCDC=y +CONFIG_FB_MSM_MDDI=y +# CONFIG_FB_MSM_MDDI_EPSON is not set +# CONFIG_FB_MSM_MDDI_NOVTEC is not set +CONFIG_GPU_MSM_KGSL=y +CONFIG_MSM_KGSL_MMU=y +# CONFIG_MSM_HDMI is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set +# CONFIG_HID_PID is not set + +# +# Special HID drivers +# +CONFIG_HID_APPLE=y +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_WACOM is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_M66592 is not set +CONFIG_USB_GADGET_MSM_72K=y +# CONFIG_USB_MSM_72K is not set +CONFIG_USB_MSM_72K_HTC=y +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_ANDROID=y +# CONFIG_USB_ANDROID_ACM is not set +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_DIAG=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +# CONFIG_USB_ANDROID_MTP is not set +CONFIG_USB_ANDROID_RNDIS=y +# CONFIG_USB_ANDROID_RNDIS_WCEIS is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_MSM=y +# CONFIG_MMC_SPI is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +CONFIG_LEDS_CPLD=y +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +CONFIG_LEDS_TRIGGER_SLEEP=y + +# +# iptables trigger is under Netfilter config (LED target) +# +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_MSM7X00A=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_ECHO is not set +# CONFIG_BRCM80211 is not set +# CONFIG_COMEDI is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE=128 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE=16 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE=8 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_POHMELFS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_IIO is not set +# CONFIG_ZRAM is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_FB_SM7XX is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_ST_BT is not set +# CONFIG_ADIS16255 is not set +# CONFIG_LIRC_STAGING is not set +# CONFIG_SMB_FS is not set +CONFIG_MACH_NO_WESTBRIDGE=y +# CONFIG_ATH6K_LEGACY is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# + +# +# File systems +# +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT3_FS_XATTR=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +# CONFIG_EXT4_FS is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +CONFIG_FS_POSIX_ACL=y +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +# CONFIG_DNOTIFY is not set +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_ECRYPT_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=m +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +# CONFIG_NFS_V4_1 is not set +# CONFIG_NFS_USE_LEGACY_DNS is not set +CONFIG_NFS_USE_KERNEL_DNS=y +# CONFIG_NFS_USE_NEW_IDMAPPER is not set +# CONFIG_NFSD is not set +CONFIG_LOCKD=m +CONFIG_LOCKD_V4=y +CONFIG_NFS_ACL_SUPPORT=m +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=m +CONFIG_SUNRPC_GSS=m +CONFIG_RPCSEC_GSS_KRB5=m +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=m +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_WEAK_PW_HASH=y +# CONFIG_CIFS_UPCALL is not set +# CONFIG_CIFS_XATTR is not set +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_DFS_UPCALL is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +CONFIG_SCHED_DEBUG=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +CONFIG_DEBUG_PREEMPT=y +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +CONFIG_DEBUG_MUTEXES=y +CONFIG_BKL=y +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +CONFIG_DEBUG_SPINLOCK_SLEEP=y +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED is not set +CONFIG_DEBUG_VM=y +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +CONFIG_DEBUG_SG=y +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_ENABLE_DEFAULT_TRACERS is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_ARM_UNWIND=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +CONFIG_KEYS=y +# CONFIG_KEYS_DEBUG_PROC_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h index f8d391ad92037..a0ca136364d4b 100644 --- a/arch/arm/include/asm/mach/mmc.h +++ b/arch/arm/include/asm/mach/mmc.h @@ -22,6 +22,8 @@ struct mmc_platform_data { unsigned int (*status)(struct device *); struct embedded_sdio_data *embedded_sdio; int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); + unsigned int *slot_type; + unsigned dat0_gpio; }; #endif diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index a76e2321a82b5..e23fb2cc1ee3a 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -293,6 +293,41 @@ config MACH_MAHIMAHI help Select this to support the Mahi-Mahi device +choice + depends on ARCH_QSD8X50 + prompt "Bravo" + default MACH_BRAVO_NONE + help + Select this to support the Bravo GSM or CDMA device + + config MACH_BRAVO_NONE + bool "None" + + config MACH_BRAVO + bool "GSM" + help + Select this to support the Bravo GSM device + + config MACH_BRAVOC + bool "CDMA" + help + Select this to support the Bravo CDMA device +endchoice + +config MACH_INCREDIBLE + depends on ARCH_QSD8X50 + default n + bool "Incredible" + help + Select this to support the Incredible device + +config MACH_INCREDIBLEC + depends on ARCH_QSD8X50 + default y + bool "IncredibleC" + help + Select this to support the IncredibleC device + config MACH_SUPERSONIC depends on ARCH_QSD8X50 default n diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index d16bb101fecd3..8edf2b9f9a195 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -25,7 +25,7 @@ obj-$(CONFIG_MSM_PROC_COMM) += proc_comm.o clock-pcom.o vreg.o obj-$(CONFIG_MSM_PROC_COMM) += clock.o obj-$(CONFIG_ARCH_QSD8X50) += sirc.o obj-$(CONFIG_MSM_FIQ_SUPPORT) += fiq_glue.o -obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o +obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o smem_log.o obj-$(CONFIG_MSM_SMD) += smd_tty.o smd_qmi.o obj-$(CONFIG_MSM_SMD) += last_radio_log.o obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o @@ -109,6 +109,34 @@ obj-$(CONFIG_MACH_SUPERSONIC) += htc_wifi_nvs.o htc_bluetooth.o obj-$(CONFIG_MACH_SUPERSONIC) += htc_acoustic_qsd.o obj-$(CONFIG_MACH_SUPERSONIC) += board-supersonic-flashlight.o +obj-$(CONFIG_MACH_INCREDIBLEC) += board-incrediblec.o board-incrediblec-panel.o board-incrediblec-tv.o +obj-$(CONFIG_MACH_INCREDIBLEC) += board-incrediblec-keypad.o board-incrediblec-mmc.o +obj-$(CONFIG_MACH_INCREDIBLEC) += board-incrediblec-rfkill.o htc_wifi_nvs.o board-incrediblec-audio.o +obj-$(CONFIG_MACH_INCREDIBLEC) += board-incrediblec-wifi.o htc_awb_cal.o +obj-$(CONFIG_MACH_INCREDIBLEC) += board-incrediblec-microp.o htc_bluetooth.o +obj-$(CONFIG_MACH_INCREDIBLEC) += msm_vibrator.o board-incrediblec-tpa2018d1.o +obj-$(CONFIG_MACH_INCREDIBLEC) += proc_engineerid.o board-incrediblec-tpa6130.o +obj-$(CONFIG_MACH_INCREDIBLEC) += htc_acoustic_qsd.o + + +obj-$(CONFIG_MACH_BRAVO) += board-bravo.o board-bravo-panel.o +obj-$(CONFIG_MACH_BRAVO) += board-bravo-keypad.o board-bravo-mmc.o +obj-$(CONFIG_MACH_BRAVO) += board-bravo-rfkill.o htc_wifi_nvs.o htc_awb_cal.o +obj-$(CONFIG_MACH_BRAVO) += board-bravo-wifi.o board-bravo-audio.o +obj-$(CONFIG_MACH_BRAVO) += msm_vibrator.o +obj-$(CONFIG_MACH_BRAVO) += board-bravo-microp.o +obj-$(CONFIG_MACH_BRAVO) += board-bravo-tpa2018d1.o +obj-$(CONFIG_MACH_BRAVO) += board-bravo-smb329.o + +obj-$(CONFIG_MACH_BRAVOC) += board-bravo.o board-bravo-panel.o +obj-$(CONFIG_MACH_BRAVOC) += board-bravo-keypad.o board-bravo-mmc.o +obj-$(CONFIG_MACH_BRAVOC) += board-bravo-rfkill.o htc_wifi_nvs.o htc_awb_cal.o +obj-$(CONFIG_MACH_BRAVOC) += board-bravo-wifi.o board-bravo-audio.o +obj-$(CONFIG_MACH_BRAVOC) += msm_vibrator.o +obj-$(CONFIG_MACH_BRAVOC) += board-bravo-microp.o +obj-$(CONFIG_MACH_BRAVOC) += board-bravo-tpa2018d1.o +obj-$(CONFIG_MACH_BRAVOC) += board-bravo-smb329.o + obj-$(CONFIG_HTC_BATTCHG) += htc_battery.o obj-$(CONFIG_HTC_PWRSINK) += htc_pwrsink.o obj-$(CONFIG_HTC_PWRSPLY) += htc_power_supply.o diff --git a/arch/arm/mach-msm/board-incrediblec-audio.c b/arch/arm/mach-msm/board-incrediblec-audio.c new file mode 100644 index 0000000000000..ed9b9d7245b6d --- /dev/null +++ b/arch/arm/mach-msm/board-incrediblec-audio.c @@ -0,0 +1,277 @@ +/* arch/arm/mach-msm/board-incrediblec-audio.c + * + * Copyright (C) 2009 HTC Corporation + * Copyright (C) 2009 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 +#include +#include + +#include "board-incrediblec.h" +#include "proc_comm.h" +#include "pmic.h" +#include "board-incrediblec-tpa2018d1.h" + +#if 1 +#define D(fmt, args...) printk(KERN_INFO "Audio: "fmt, ##args) +#else +#define D(fmt, args...) do {} while (0) +#endif + +static struct mutex mic_lock; +static struct mutex bt_sco_lock; +static int headset_status = 0; + +static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { + [Q6_HW_HANDSET] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_HEADSET] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_SPEAKER] = { + .min_gain = -1500, + .max_gain = 0, + }, + [Q6_HW_TTY] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_BT_SCO] = { + .min_gain = -2000, + .max_gain = 0, + }, + [Q6_HW_BT_A2DP] = { + .min_gain = -2000, + .max_gain = 0, + }, +}; + +void incrediblec_headset_enable(int en) +{ + D("%s %d\n", __func__, en); + /* enable audio amp */ + if (en != headset_status) { + headset_status = en; + if(en) { + gpio_set_value(INCREDIBLEC_AUD_JACKHP_EN, 1); + mdelay(10); + set_headset_amp(1); + } else { + set_headset_amp(0); + gpio_set_value(INCREDIBLEC_AUD_JACKHP_EN, 0); + } + } +} + +void incrediblec_speaker_enable(int en) +{ + struct spkr_config_mode scm; + memset(&scm, 0, sizeof(scm)); + + D("%s %d\n", __func__, en); + if (en) { + scm.is_right_chan_en = 0; + scm.is_left_chan_en = 1; + scm.is_stereo_en = 0; + scm.is_hpf_en = 1; + pmic_spkr_en_mute(LEFT_SPKR, 0); + pmic_set_spkr_configuration(&scm); + pmic_spkr_en(LEFT_SPKR, 1); + + /* unmute */ + pmic_spkr_en_mute(LEFT_SPKR, 1); + } else { + pmic_spkr_en_mute(LEFT_SPKR, 0); + + pmic_spkr_en(LEFT_SPKR, 0); + + pmic_set_spkr_configuration(&scm); + } + tpa2018d1_set_speaker_amp(en); +} + +void incrediblec_receiver_enable(int en) +{ + /* After XB*/ + if (system_rev >= 1) { + struct spkr_config_mode scm; + memset(&scm, 0, sizeof(scm)); + + D("%s %d\n", __func__, en); + if (en) { + scm.is_right_chan_en = 1; + scm.is_left_chan_en = 0; + scm.is_stereo_en = 0; + scm.is_hpf_en = 1; + pmic_spkr_en_mute(RIGHT_SPKR, 0); + pmic_set_spkr_configuration(&scm); + pmic_spkr_en(RIGHT_SPKR, 1); + + /* unmute */ + pmic_spkr_en_mute(RIGHT_SPKR, 1); + } else { + pmic_spkr_en_mute(RIGHT_SPKR, 0); + + pmic_spkr_en(RIGHT_SPKR, 0); + + pmic_set_spkr_configuration(&scm); + } + } +} + +static uint32_t bt_sco_enable[] = { + PCOM_GPIO_CFG(INCREDIBLEC_BT_PCM_OUT, 1, GPIO_INPUT, + GPIO_PULL_DOWN, GPIO_2MA), + PCOM_GPIO_CFG(INCREDIBLEC_BT_PCM_IN, 1, GPIO_INPUT, + GPIO_PULL_DOWN, GPIO_2MA), + PCOM_GPIO_CFG(INCREDIBLEC_BT_PCM_SYNC, 2, GPIO_INPUT, + GPIO_PULL_DOWN, GPIO_2MA), + PCOM_GPIO_CFG(INCREDIBLEC_BT_PCM_CLK, 2, GPIO_INPUT, + GPIO_PULL_DOWN, GPIO_2MA), +}; + +static uint32_t bt_sco_disable[] = { + PCOM_GPIO_CFG(INCREDIBLEC_BT_PCM_OUT, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(INCREDIBLEC_BT_PCM_IN, 0, GPIO_INPUT, + GPIO_PULL_UP, GPIO_2MA), + PCOM_GPIO_CFG(INCREDIBLEC_BT_PCM_SYNC, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(INCREDIBLEC_BT_PCM_CLK, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), +}; + +void incrediblec_bt_sco_enable(int en) +{ + static int bt_sco_refcount; + D("%s %d\n", __func__, en); + mutex_lock(&bt_sco_lock); + if (en) { + if (++bt_sco_refcount == 1) + config_gpio_table(bt_sco_enable, + ARRAY_SIZE(bt_sco_enable)); + } else { + if (--bt_sco_refcount == 0) { + config_gpio_table(bt_sco_disable, ARRAY_SIZE(bt_sco_disable)); + gpio_set_value(INCREDIBLEC_BT_PCM_OUT, 0); + gpio_set_value(INCREDIBLEC_BT_PCM_SYNC,0); + gpio_set_value(INCREDIBLEC_BT_PCM_CLK,0); + } + } + mutex_unlock(&bt_sco_lock); +} + +void incrediblec_int_mic_enable(int en) +{ + D("%s %d\n", __func__, en); + if (en) + pmic_mic_en(ON_CMD); + else + pmic_mic_en(OFF_CMD); +} + +void incrediblec_ext_mic_enable(int en) +{ + static int old_state = 0, new_state = 0; + + D("%s %d\n", __func__, en); + + mutex_lock(&mic_lock); + if (!!en) + new_state++; + else + new_state--; + + if (new_state == 1 && old_state == 0) { + gpio_set_value(INCREDIBLEC_AUD_2V5_EN, 1); + } else if (new_state == 0 && old_state == 1) + gpio_set_value(INCREDIBLEC_AUD_2V5_EN, 0); + else + D("%s: do nothing %d %d\n", __func__, old_state, new_state); + + old_state = new_state; + mutex_unlock(&mic_lock); +} + +void incrediblec_analog_init(void) +{ + D("%s\n", __func__); + /* stereo pmic init */ + pmic_spkr_set_gain(LEFT_SPKR, SPKR_GAIN_PLUS12DB); + pmic_spkr_set_gain(RIGHT_SPKR, SPKR_GAIN_00DB); + pmic_spkr_en_right_chan(OFF_CMD); + pmic_spkr_en_left_chan(OFF_CMD); + pmic_spkr_add_right_left_chan(OFF_CMD); + pmic_spkr_en_stereo(OFF_CMD); + pmic_spkr_select_usb_with_hpf_20hz(OFF_CMD); + pmic_spkr_bypass_mux(OFF_CMD); + pmic_spkr_en_hpf(ON_CMD); + pmic_spkr_en_sink_curr_from_ref_volt_cir(OFF_CMD); + pmic_spkr_set_mux_hpf_corner_freq(SPKR_FREQ_0_73KHZ); + pmic_mic_set_volt(MIC_VOLT_1_80V); + pmic_set_speaker_delay(SPKR_DLY_100MS); + + gpio_request(INCREDIBLEC_AUD_JACKHP_EN, "aud_jackhp_en"); + gpio_direction_output(INCREDIBLEC_AUD_JACKHP_EN, 1); + gpio_set_value(INCREDIBLEC_AUD_JACKHP_EN, 0); + + mutex_lock(&bt_sco_lock); + config_gpio_table(bt_sco_disable, ARRAY_SIZE(bt_sco_disable)); + gpio_set_value(INCREDIBLEC_BT_PCM_OUT, 0); + gpio_set_value(INCREDIBLEC_BT_PCM_SYNC,0); + gpio_set_value(INCREDIBLEC_BT_PCM_CLK,0); + mutex_unlock(&bt_sco_lock); +} + +int incrediblec_get_rx_vol(uint8_t hw, int level) +{ + struct q6_hw_info *info; + int vol; + + info = &q6_audio_hw[hw]; + vol = info->min_gain + ((info->max_gain - info->min_gain) * level) / 100; + D("%s %d\n", __func__, vol); + return vol; +} + +static struct qsd_acoustic_ops acoustic = { + .enable_mic_bias = incrediblec_ext_mic_enable, +}; + +static struct q6audio_analog_ops ops = { + .init = incrediblec_analog_init, + .speaker_enable = incrediblec_speaker_enable, + .headset_enable = incrediblec_headset_enable, + .receiver_enable = incrediblec_receiver_enable, + .bt_sco_enable = incrediblec_bt_sco_enable, + .int_mic_enable = incrediblec_int_mic_enable, + .ext_mic_enable = incrediblec_ext_mic_enable, + .get_rx_vol = incrediblec_get_rx_vol, +}; + +void __init incrediblec_audio_init(void) +{ + mutex_init(&mic_lock); + mutex_init(&bt_sco_lock); + q6audio_register_analog_ops(&ops); + acoustic_register_ops(&acoustic); +} + diff --git a/arch/arm/mach-msm/board-incrediblec-keypad.c b/arch/arm/mach-msm/board-incrediblec-keypad.c new file mode 100644 index 0000000000000..0880ae6737978 --- /dev/null +++ b/arch/arm/mach-msm/board-incrediblec-keypad.c @@ -0,0 +1,137 @@ +/* arch/arm/mach-msm/board-incrediblec-keypad.c + * + * Copyright (C) 2009 Google, Inc + * Copyright (C) 2009 HTC Corporation. + * + * Author: Dima Zavin + * + * 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 "board-incrediblec.h" + + +const struct gpio_event_direct_entry incrediblec_keypad_nav_map_x0[] = { + { + .gpio = INCREDIBLEC_GPIO_POWER_KEY, + .code = KEY_POWER + }, + { + .gpio = INCREDIBLEC_GPIO_VOLUME_UP, + .code = KEY_VOLUMEUP + }, + { + .gpio = INCREDIBLEC_GPIO_VOLUME_DOWN, + .code = KEY_VOLUMEDOWN + }, +}; + +const struct gpio_event_direct_entry incrediblec_keypad_nav_map_x1[] = { + { + .gpio = INCREDIBLEC_GPIO_POWER_KEY, + .code = KEY_POWER + }, + { + .gpio = INCREDIBLEC_GPIO_VOLUME_UP, + .code = KEY_VOLUMEUP + }, + { + .gpio = INCREDIBLEC_GPIO_VOLUME_DOWN, + .code = KEY_VOLUMEDOWN + }, + { + .gpio = INCREDIBLEC_GPIO_OJ_ACTION_XB, + .code = BTN_MOUSE + }, +}; + +static struct gpio_event_input_info incrediblec_keypad_nav_info = { + .info.func = gpio_event_input_func, + .info.oj_btn = true, + .flags = GPIOEDF_PRINT_KEYS, + .type = EV_KEY, + .keymap = incrediblec_keypad_nav_map_x1, + .debounce_time.tv.nsec = 5 * NSEC_PER_MSEC, + .keymap_size = ARRAY_SIZE(incrediblec_keypad_nav_map_x1) +}; + +static struct gpio_event_info *incrediblec_keypad_info[] = { + &incrediblec_keypad_nav_info.info, +}; + +static struct gpio_event_platform_data incrediblec_keypad_data = { + .name = "incrediblec-keypad", + .info = incrediblec_keypad_info, + .info_count = ARRAY_SIZE(incrediblec_keypad_info) +}; + +static struct platform_device incrediblec_keypad_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = 0, + .dev = { + .platform_data = &incrediblec_keypad_data, + }, +}; + +static int incrediblec_reset_keys_up[] = { + KEY_VOLUMEUP, + 0 +}; + +static struct keyreset_platform_data incrediblec_reset_keys_pdata = { + .keys_up = incrediblec_reset_keys_up, + .keys_down = { + KEY_POWER, + KEY_VOLUMEDOWN, + BTN_MOUSE, + 0 + }, +}; + +static struct platform_device incrediblec_reset_keys_device = { + .name = KEYRESET_NAME, + .dev.platform_data = &incrediblec_reset_keys_pdata, +}; +static int __init incrediblec_init_keypad(void) +{ + int ret; + + if (!machine_is_incrediblec()) + return 0; + + if (system_rev < 2) { + incrediblec_keypad_nav_info.keymap = + incrediblec_keypad_nav_map_x0; + incrediblec_keypad_nav_info.keymap_size = + ARRAY_SIZE(incrediblec_keypad_nav_map_x0); + } + + if (platform_device_register(&incrediblec_reset_keys_device)) + printk(KERN_WARNING "%s: register reset key fail\n", __func__); + + ret = platform_device_register(&incrediblec_keypad_device); + if (ret != 0) + return ret; + + return 0; +} + +device_initcall(incrediblec_init_keypad); + + diff --git a/arch/arm/mach-msm/board-incrediblec-microp.c b/arch/arm/mach-msm/board-incrediblec-microp.c new file mode 100644 index 0000000000000..c535981dab3f5 --- /dev/null +++ b/arch/arm/mach-msm/board-incrediblec-microp.c @@ -0,0 +1,454 @@ +/* arch/arm/mach-msm/board-incrediblec-microp.c + * Copyright (C) 2009 HTC Corporation. + * + * 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. +*/ +#ifdef CONFIG_MICROP_COMMON +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-incrediblec.h" + + +#define INT_PSENSOR (1<<11) + +static int misc_opened; +static struct i2c_client *incrediblec_microp_client; + +static void p_sensor_do_work(struct work_struct *w); +static DECLARE_WORK(p_sensor_work, p_sensor_do_work); + +struct wake_lock proximity_wake_lock; + +static struct capella_cm3602_data { + struct input_dev *input_dev; + struct capella_cm3602_platform_data *pdata; + int enabled; + struct workqueue_struct *p_sensor_wq; +} the_data; + +static int psensor_intr_enable(uint8_t enable) +{ + int ret; + uint8_t addr, data[2]; + + if (enable) + addr = MICROP_I2C_WCMD_GPI_INT_CTL_EN; + else + addr = MICROP_I2C_WCMD_GPI_INT_CTL_DIS; + + data[0] = INT_PSENSOR >> 8; + data[1] = INT_PSENSOR & 0xFF; + ret = microp_i2c_write(addr, data, 2); + if (ret < 0) + pr_err("%s: %s p-sensor interrupt failed\n", + __func__, (enable ? "enable" : "disable")); + + return ret; +} + +static int incrediblec_microp_function_init(struct i2c_client *client) +{ + struct microp_i2c_platform_data *pdata; + struct microp_i2c_client_data *cdata; + uint8_t data[20]; + int i, j; + int ret; + + incrediblec_microp_client = client; + pdata = client->dev.platform_data; + cdata = i2c_get_clientdata(client); + + /* Headset remote key */ + ret = microp_function_check(client, MICROP_FUNCTION_REMOTEKEY); + if (ret >= 0) { + i = ret; + pdata->function_node[MICROP_FUNCTION_REMOTEKEY] = i; + cdata->int_pin.int_remotekey = + pdata->microp_function[i].int_pin; + + for (j = 0; j < 6; j++) { + data[j] = (uint8_t)(pdata->microp_function[i].levels[j] >> 8); + data[j + 6] = (uint8_t)(pdata->microp_function[i].levels[j]); + } + ret = microp_i2c_write(MICROP_I2C_WCMD_REMOTEKEY_TABLE, + data, 12); + if (ret) + goto exit; + } + + /* Reset button interrupt */ + data[0] = 0x08; + ret = microp_i2c_write(MICROP_I2C_WCMD_MISC, data, 1); + if (ret) + goto exit; + + /* OJ interrupt */ + ret = microp_function_check(client, MICROP_FUNCTION_OJ); + if (ret >= 0) { + i = ret; + cdata->int_pin.int_oj = pdata->microp_function[i].int_pin; + + ret = microp_write_interrupt(client, cdata->int_pin.int_oj, 1); + if (ret) + goto exit; + } + + /* Proximity interrupt */ + ret = microp_function_check(client, MICROP_FUNCTION_P); + if (ret >= 0) { + i = ret; + cdata->int_pin.int_psensor = pdata->microp_function[i].int_pin; + cdata->gpio.psensor = pdata->microp_function[i].mask_r[0] << 16 + | pdata->microp_function[i].mask_r[1] << 8 + | pdata->microp_function[i].mask_r[2]; + cdata->fnode.psensor = i; + } + + return 0; + +exit: + return ret; +} + +static int report_psensor_data(void) +{ + int ret, ps_data = 0; + uint8_t data[3] = {0, 0, 0}; + + ret = microp_i2c_read(MICROP_I2C_RCMD_GPIO_STATUS, data, 3); + if (ret < 0) + pr_err("%s: read data failed\n", __func__); + else { + ps_data = (data[2] & 0x10) ? 1 : 0; + pr_info("proximity %s\n", ps_data ? "FAR" : "NEAR"); + + /* 0 is close, 1 is far */ + input_report_abs(the_data.input_dev, ABS_DISTANCE, ps_data); + input_sync(the_data.input_dev); + + wake_lock_timeout(&proximity_wake_lock, 2*HZ); + } + + return ret; +} + +static int capella_cm3602_enable(struct capella_cm3602_data *data) +{ + int rc; + pr_info("%s\n", __func__); + if (data->enabled) { + pr_info("%s: already enabled\n", __func__); + return 0; + } + + /* dummy report */ + input_report_abs(data->input_dev, ABS_DISTANCE, -1); + input_sync(data->input_dev); + + rc = data->pdata->power(PS_PWR_ON, 1); + if (rc < 0) + return -EIO; + + rc = gpio_direction_output(data->pdata->p_en, 0); + if (rc < 0) { + pr_err("%s: set psesnor enable failed!!", + __func__); + return -EIO; + } + msleep(220); + rc = psensor_intr_enable(1); + if (rc < 0) + return -EIO; + + data->enabled = 1; + report_psensor_data(); + + return rc; +} + +static int capella_cm3602_disable(struct capella_cm3602_data *data) +{ + int rc = -EIO; + pr_info("%s\n", __func__); + if (!data->enabled) { + pr_info("%s: already disabled\n", __func__); + return 0; + } + + rc = psensor_intr_enable(0); + if (rc < 0) + return -EIO; + + rc = gpio_direction_output(data->pdata->p_en, 1); + if (rc < 0) { + pr_err("%s: set GPIO failed!!", __func__); + return -EIO; + } + + rc = data->pdata->power(PS_PWR_ON, 0); + if (rc < 0) + return -EIO; + + data->enabled = 0; + return rc; +} + +static ssize_t capella_cm3602_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + + ret = sprintf(buf, "proximity enabled = %d\n", the_data.enabled); + + return ret; +} + +static ssize_t capella_cm3602_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count + ) +{ + ssize_t val; + + val = -1; + sscanf(buf, "%u", &val); + if (val < 0 || val > 1) + return -EINVAL; + + /* Enable capella_cm3602*/ + if (val == 1) + capella_cm3602_enable(&the_data); + + /* Disable capella_cm3602*/ + if (val == 0) + capella_cm3602_disable(&the_data); + + return count; +} + +static DEVICE_ATTR(proximity, 0644, capella_cm3602_show, capella_cm3602_store); + +static int capella_cm3602_open(struct inode *inode, struct file *file) +{ + pr_info("%s\n", __func__); + if (misc_opened) + return -EBUSY; + misc_opened = 1; + return 0; +} + +static int capella_cm3602_release(struct inode *inode, struct file *file) +{ + pr_info("%s\n", __func__); + misc_opened = 0; + return capella_cm3602_disable(&the_data); +} + +static long capella_cm3602_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int val; + pr_info("%s cmd %d\n", __func__, _IOC_NR(cmd)); + switch (cmd) { + case CAPELLA_CM3602_IOCTL_ENABLE: + if (get_user(val, (unsigned long __user *)arg)) + return -EFAULT; + if (val) + return capella_cm3602_enable(&the_data); + else + return capella_cm3602_disable(&the_data); + break; + case CAPELLA_CM3602_IOCTL_GET_ENABLED: + return put_user(the_data.enabled, (unsigned long __user *)arg); + break; + default: + pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd)); + return -EINVAL; + } +} +static void p_sensor_do_work(struct work_struct *w) +{ + report_psensor_data(); +} + +static irqreturn_t p_sensor_irq_handler(int irq, void *data) +{ + struct capella_cm3602_data *ip = data; + queue_work(ip->p_sensor_wq, &p_sensor_work); + + return IRQ_HANDLED; +} + +static struct file_operations capella_cm3602_fops = { + .owner = THIS_MODULE, + .open = capella_cm3602_open, + .release = capella_cm3602_release, + .unlocked_ioctl = capella_cm3602_ioctl +}; + +static struct miscdevice capella_cm3602_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "cm3602", + .fops = &capella_cm3602_fops +}; + +static int capella_cm3602_probe(struct platform_device *pdev) +{ + int rc = -1; + struct input_dev *input_dev; + struct capella_cm3602_data *ip; + struct capella_cm3602_platform_data *pdata; + + struct class *proximity_attr_class; + struct device *proximity_attr_dev; + + pr_info("%s: probe\n", __func__); + + pdata = dev_get_platdata(&pdev->dev); + + ip = &the_data; + platform_set_drvdata(pdev, ip); + + input_dev = input_allocate_device(); + if (!input_dev) { + pr_err("%s: could not allocate input device\n", __func__); + rc = -ENOMEM; + goto done; + } + ip->input_dev = input_dev; + ip->pdata = pdata; + input_set_drvdata(input_dev, ip); + + input_dev->name = "proximity"; + + set_bit(EV_ABS, input_dev->evbit); + input_set_abs_params(input_dev, ABS_DISTANCE, 0, 1, 0, 0); + + rc = input_register_device(input_dev); + if (rc < 0) { + pr_err("%s: could not register input device\n", __func__); + goto err_free_input_device; + } + + rc = misc_register(&capella_cm3602_misc); + if (rc < 0) { + pr_err("%s: could not register misc device\n", __func__); + goto err_unregister_input_device; + } + + wake_lock_init(&proximity_wake_lock, WAKE_LOCK_SUSPEND, "proximity"); + + proximity_attr_class = class_create(THIS_MODULE, "sensors"); + if (IS_ERR(proximity_attr_class)) { + pr_err("%s: class_create failed\n", __func__); + rc = PTR_ERR(proximity_attr_class); + proximity_attr_class = NULL; + goto err_create_class; + } + + proximity_attr_dev = device_create(proximity_attr_class, + NULL, 0, "%s", "proximity_sensor"); + if (unlikely(IS_ERR(proximity_attr_dev))) { + pr_err("%s: device create failed\n", __func__); + rc = PTR_ERR(proximity_attr_dev); + proximity_attr_dev = NULL; + goto err_create_proximity_attr_device; + } + + rc = device_create_file(proximity_attr_dev, &dev_attr_proximity); + if (rc) { + pr_err("%s: device_create_file failed\n", __func__); + goto err_create_proximity_device_file; + } + + ip->p_sensor_wq = create_workqueue("p-sensor_microp_wq"); + if (ip->p_sensor_wq == NULL) { + pr_err("%s: create_workqueue failed\n", __func__); + goto err_create_workqueue; + } + + rc = gpio_request(pdata->p_en, "gpio_proximity_en"); + if (rc < 0) { + pr_err("%s: gpio %d request failed (%d)\n", + __func__, pdata->p_en, rc); + goto err_request_proximity_en; + } + + rc = request_irq(pdata->p_out, p_sensor_irq_handler, + IRQF_TRIGGER_NONE, "p-sensor_microp", ip); + if (rc < 0) { + pr_err("%s: request_irq(%d) failed for (%d)\n", + __func__, pdata->p_out, rc); + goto err_request_proximity_irq; + } + + + goto done; + +err_request_proximity_irq: + gpio_free(pdata->p_en); +err_request_proximity_en: + destroy_workqueue(ip->p_sensor_wq); +err_create_workqueue: + device_remove_file(proximity_attr_dev, &dev_attr_proximity); +err_create_proximity_device_file: + device_unregister(proximity_attr_dev); +err_create_proximity_attr_device: + class_destroy(proximity_attr_class); +err_create_class: + misc_deregister(&capella_cm3602_misc); +err_unregister_input_device: + input_unregister_device(input_dev); +err_free_input_device: + input_free_device(input_dev); +done: + return rc; +} + +static struct microp_ops ops = { + .init_microp_func = incrediblec_microp_function_init, +}; + +void __init incrediblec_microp_init(void) +{ + microp_register_ops(&ops); +} + +static struct platform_driver capella_cm3602_driver = { + .probe = capella_cm3602_probe, + .driver = { + .name = "incrediblec_proximity", + .owner = THIS_MODULE + }, +}; + +static int __init incrediblec_capella_cm3602_init(void) +{ + if (!machine_is_incrediblec()) + return 0; + + return platform_driver_register(&capella_cm3602_driver); +} + +device_initcall(incrediblec_capella_cm3602_init); +#endif diff --git a/arch/arm/mach-msm/board-incrediblec-mmc.c b/arch/arm/mach-msm/board-incrediblec-mmc.c new file mode 100644 index 0000000000000..18e2e489535e1 --- /dev/null +++ b/arch/arm/mach-msm/board-incrediblec-mmc.c @@ -0,0 +1,314 @@ +/* linux/arch/arm/mach-msm/board-incrediblec-mmc.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * + * 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 "board-incrediblec.h" +#include "devices.h" +#include "proc_comm.h" + +#define DEBUG_SDSLOT_VDD 1 + +static bool opt_disable_sdcard; +static int __init incrediblec_disablesdcard_setup(char *str) +{ + opt_disable_sdcard = (bool)simple_strtol(str, NULL, 0); + return 1; +} + +__setup("board_incrediblec.disable_sdcard=", incrediblec_disablesdcard_setup); + +static uint32_t sdcard_on_gpio_table[] = { + PCOM_GPIO_CFG(62, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(63, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(64, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT0 */ +}; + +static uint32_t sdcard_off_gpio_table[] = { + PCOM_GPIO_CFG(62, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(63, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(64, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ +}; + +static uint32_t movinand_on_gpio_table[] = { + PCOM_GPIO_CFG(88, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(89, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(90, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT3 */ + PCOM_GPIO_CFG(91, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT2 */ + PCOM_GPIO_CFG(92, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT1 */ + PCOM_GPIO_CFG(93, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT0 */ + PCOM_GPIO_CFG(158, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT4 */ + PCOM_GPIO_CFG(159, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT5 */ + PCOM_GPIO_CFG(160, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT6 */ + PCOM_GPIO_CFG(161, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* DAT7 */ +}; + +static struct vreg *sdslot_vreg; +static uint32_t sdslot_vdd = 0xffffffff; +static uint32_t sdslot_vreg_enabled; + +static struct { + int mask; + int level; +} mmc_vdd_table[] = { + { MMC_VDD_28_29, 2850 }, + { MMC_VDD_29_30, 2900 }, +}; + +static uint32_t incrediblec_sdslot_switchvdd(struct device *dev, unsigned int vdd) +{ + int i; + int ret; + + if (vdd == sdslot_vdd) + return 0; + + sdslot_vdd = vdd; + + if (vdd == 0) { +#if DEBUG_SDSLOT_VDD + printk(KERN_INFO "%s: Disabling SD slot power\n", __func__); +#endif + config_gpio_table(sdcard_off_gpio_table, + ARRAY_SIZE(sdcard_off_gpio_table)); + vreg_disable(sdslot_vreg); + sdslot_vreg_enabled = 0; + return 0; + } + + if (!sdslot_vreg_enabled) { + mdelay(5); + ret = vreg_enable(sdslot_vreg); + if (ret) + pr_err("%s: Error enabling vreg (%d)\n", __func__, ret); + udelay(500); + config_gpio_table(sdcard_on_gpio_table, + ARRAY_SIZE(sdcard_on_gpio_table)); + sdslot_vreg_enabled = 1; + } + + for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) { + if (mmc_vdd_table[i].mask != (1 << vdd)) + continue; + ret = vreg_set_level(sdslot_vreg, mmc_vdd_table[i].level); + if (ret) + pr_err("%s: Error setting level (%d)\n", __func__, ret); +#if DEBUG_SDSLOT_VDD + printk(KERN_INFO "%s: Setting level to %u (%s)\n", + __func__, mmc_vdd_table[i].level, + ret?"Failed":"Success"); +#endif + return 0; + } + + pr_err("%s: Invalid VDD (%d) specified\n", __func__, vdd); + return 0; +} + +static unsigned int incrediblec_sdslot_status(struct device *dev) +{ + return !gpio_get_value(INCREDIBLEC_GPIO_SDMC_CD_N); +} + +#define INCREDIBLEC_MMC_VDD (MMC_VDD_28_29 | MMC_VDD_29_30) + +static unsigned int incrediblec_sdslot_type = MMC_TYPE_SD; + +static struct mmc_platform_data incrediblec_sdslot_data = { + .ocr_mask = INCREDIBLEC_MMC_VDD, + .status = incrediblec_sdslot_status, + .translate_vdd = incrediblec_sdslot_switchvdd, + //.slot_type = &incrediblec_sdslot_type, +}; + +static unsigned int incrediblec_mmc_type = MMC_TYPE_MMC; + +static struct mmc_platform_data incrediblec_movinand_data = { + .ocr_mask = INCREDIBLEC_MMC_VDD, + //.slot_type = &incrediblec_mmc_type, +}; + +/* int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, + unsigned int stat_irq, unsigned long stat_irq_flags); */ + + + +/* ---- WIFI ---- */ + +static uint32_t wifi_on_gpio_table[] = { + PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(152, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +static uint32_t wifi_off_gpio_table[] = { + PCOM_GPIO_CFG(51, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(56, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(152, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +/* BCM4329 returns wrong sdio_vsn(1) when we read cccr, + * we use predefined value (sdio_vsn=2) here to initial sdio driver well + */ +static struct embedded_sdio_data incrediblec_wifi_emb_data = { + .cccr = { + .sdio_vsn = 2, + .multi_block = 1, + .low_speed = 0, + .wide_bus = 0, + .high_power = 1, + .high_speed = 1, + }, +}; + +static void (*wifi_status_cb)(int card_present, void *dev_id); +static void *wifi_status_cb_devid; + +static int +incrediblec_wifi_status_register(void (*callback)(int card_present, void *dev_id), + void *dev_id) +{ + if (wifi_status_cb) + return -EAGAIN; + wifi_status_cb = callback; + wifi_status_cb_devid = dev_id; + return 0; +} + +static int incrediblec_wifi_cd; /* WiFi virtual 'card detect' status */ + +static unsigned int incrediblec_wifi_status(struct device *dev) +{ + return incrediblec_wifi_cd; +} + +static struct mmc_platform_data incrediblec_wifi_data = { + .ocr_mask = MMC_VDD_28_29, + .status = incrediblec_wifi_status, + .register_status_notify = incrediblec_wifi_status_register, + .embedded_sdio = &incrediblec_wifi_emb_data, +}; + +int incrediblec_wifi_set_carddetect(int val) +{ + printk(KERN_INFO "%s: %d\n", __func__, val); + incrediblec_wifi_cd = val; + if (wifi_status_cb) + wifi_status_cb(val, wifi_status_cb_devid); + else + printk(KERN_WARNING "%s: Nobody to notify\n", __func__); + return 0; +} +EXPORT_SYMBOL(incrediblec_wifi_set_carddetect); + +int incrediblec_wifi_power(int on) +{ + int rc = 0; + + printk(KERN_INFO "%s: %d\n", __func__, on); + + if (on) { + config_gpio_table(wifi_on_gpio_table, + ARRAY_SIZE(wifi_on_gpio_table)); + mdelay(50); + if (rc) + return rc; + } else { + config_gpio_table(wifi_off_gpio_table, + ARRAY_SIZE(wifi_off_gpio_table)); + } + + mdelay(100); + gpio_set_value(INCREDIBLEC_GPIO_WIFI_SHUTDOWN_N, on); /* WIFI_SHUTDOWN */ + mdelay(100); + return 0; +} +EXPORT_SYMBOL(incrediblec_wifi_power); + +int incrediblec_wifi_reset(int on) +{ + printk(KERN_INFO "%s: do nothing\n", __func__); + return 0; +} + + +int __init incrediblec_init_mmc(unsigned int sys_rev) +{ + uint32_t id; + + wifi_status_cb = NULL; + + printk(KERN_INFO "%s()+\n", __func__); + + /* initial WIFI_SHUTDOWN# */ + id = PCOM_GPIO_CFG(INCREDIBLEC_GPIO_WIFI_SHUTDOWN_N, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + gpio_set_value(INCREDIBLEC_GPIO_WIFI_SHUTDOWN_N, 0); + + msm_add_sdcc(1, &incrediblec_wifi_data, 0, 0); + + if (opt_disable_sdcard) { + pr_info("%s: sdcard disabled on cmdline\n", __func__); + goto done; + } + + sdslot_vreg_enabled = 0; + + sdslot_vreg = vreg_get(0, "gp6"); + if (IS_ERR(sdslot_vreg)) + return PTR_ERR(sdslot_vreg); + + set_irq_wake(MSM_GPIO_TO_INT(INCREDIBLEC_GPIO_SDMC_CD_N), 1); + + msm_add_sdcc(2, &incrediblec_sdslot_data, + MSM_GPIO_TO_INT(INCREDIBLEC_GPIO_SDMC_CD_N), + IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE); + +done: + config_gpio_table(movinand_on_gpio_table, + ARRAY_SIZE(movinand_on_gpio_table)); + msm_add_sdcc(3, &incrediblec_movinand_data, 0, 0); /* SDC3: MoviNAND */ + + printk(KERN_INFO "%s()-\n", __func__); + return 0; +} diff --git a/arch/arm/mach-msm/board-incrediblec-panel.c b/arch/arm/mach-msm/board-incrediblec-panel.c new file mode 100644 index 0000000000000..368d0c9e9f762 --- /dev/null +++ b/arch/arm/mach-msm/board-incrediblec-panel.c @@ -0,0 +1,1211 @@ +/* arch/arm/mach-msm/board-incrediblec-panel.c + * + * Copyright (c) 2009 Google Inc. + * Author: Dima Zavin + * + * 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 "proc_comm.h" + +#include "board-incrediblec.h" +#include "devices.h" + +#define SPI_CONFIG (0x00000000) +#define SPI_IO_CONTROL (0x00000004) +#define SPI_OPERATIONAL (0x00000030) +#define SPI_ERROR_FLAGS_EN (0x00000038) +#define SPI_ERROR_FLAGS (0x00000038) +#define SPI_OUTPUT_FIFO (0x00000100) + +static void __iomem *spi_base; +static struct clk *spi_clk ; +static struct vreg *vreg_lcm_rftx_2v6; +static struct vreg *vreg_lcm_aux_2v6; + +#define SAMSUNG_PANEL 0 +/*Bitwise mask for SONY PANEL ONLY*/ +#define SONY_PANEL 0x1 /*Set bit 0 as 1 when it is SONY PANEL*/ +#define SONY_PWM_SPI 0x2 /*Set bit 1 as 1 as PWM_SPI mode, otherwise it is PWM_MICROP mode*/ +#define SONY_GAMMA 0x4 /*Set bit 2 as 1 when panel contains GAMMA table in its NVM*/ +#define SONY_RGB666 0x8 /*Set bit 3 as 1 when panel is 18 bit, otherwise it is 16 bit*/ + +extern int panel_type; + +static int is_sony_spi(void) +{ + return (panel_type & SONY_PWM_SPI ? 1 : 0); +} + +static int is_sony_with_gamma(void) +{ + return (panel_type & SONY_GAMMA ? 1 : 0); +} + +static int is_sony_RGB666(void) +{ + return (panel_type & SONY_RGB666 ? 1 : 0); +} + +static int qspi_send(uint32_t id, uint8_t data) +{ + uint32_t err; + + /* bit-5: OUTPUT_FIFO_NOT_EMPTY */ + while (readl(spi_base + SPI_OPERATIONAL) & (1<<5)) { + if ((err = readl(spi_base + SPI_ERROR_FLAGS))) { + pr_err("%s: ERROR: SPI_ERROR_FLAGS=0x%08x\n", __func__, + err); + return -EIO; + } + } + writel((0x7000 | (id << 9) | data) << 16, spi_base + SPI_OUTPUT_FIFO); + udelay(100); + + return 0; +} + +static int qspi_send_9bit(uint32_t id, uint8_t data) +{ + uint32_t err; + + while (readl(spi_base + SPI_OPERATIONAL) & (1<<5)) { + err = readl(spi_base + SPI_ERROR_FLAGS); + if (err) { + pr_err("%s: ERROR: SPI_ERROR_FLAGS=0x%08x\n", __func__, + err); + return -EIO; + } + } + writel(((id << 8) | data) << 23, spi_base + SPI_OUTPUT_FIFO); + udelay(100); + + return 0; +} + +static int lcm_writeb(uint8_t reg, uint8_t val) +{ + qspi_send(0x0, reg); + qspi_send(0x1, val); + return 0; +} + +static int lcm_writew(uint8_t reg, uint16_t val) +{ + qspi_send(0x0, reg); + qspi_send(0x1, val >> 8); + qspi_send(0x1, val & 0xff); + return 0; +} + +static struct resource resources_msm_fb[] = { + { + .start = MSM_FB_BASE, + .end = MSM_FB_BASE + MSM_FB_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct lcm_tbl { + uint8_t reg; + uint8_t val; +}; + +static struct lcm_tbl samsung_oled_rgb565_init_table[] = { + { 0x31, 0x08 }, + { 0x32, 0x14 }, + { 0x30, 0x2 }, + { 0x27, 0x1 }, + { 0x12, 0x8 }, + { 0x13, 0x8 }, + { 0x15, 0x0 }, + { 0x16, 0x02 }, + { 0x39, 0x44 }, + { 0x17, 0x22 }, + { 0x18, 0x33 }, + { 0x19, 0x3 }, + { 0x1A, 0x1 }, + { 0x22, 0xA4 }, + { 0x23, 0x0 }, + { 0x26, 0xA0 }, + { 0x1D, 0xA0 }, +}; + +static struct lcm_tbl samsung_oled_rgb666_init_table[] = { + { 0x31, 0x08 }, + { 0x32, 0x14 }, + { 0x30, 0x2 }, + { 0x27, 0x1 }, + { 0x12, 0x8 }, + { 0x13, 0x8 }, + { 0x15, 0x0 }, + { 0x16, 0x01 }, + { 0x16, 0x01 }, + { 0x39, 0x44 }, + { 0x17, 0x22 }, + { 0x18, 0x33 }, + { 0x19, 0x3 }, + { 0x1A, 0x1 }, + { 0x22, 0xA4 }, + { 0x23, 0x0 }, + { 0x26, 0xA0 }, + { 0x1D, 0xA0 }, +}; + +static struct lcm_tbl *init_tablep = samsung_oled_rgb565_init_table; +static size_t init_table_sz = ARRAY_SIZE(samsung_oled_rgb565_init_table); + +#define OLED_GAMMA_TABLE_SIZE (7 * 3) +static struct lcm_tbl samsung_oled_gamma_table[][OLED_GAMMA_TABLE_SIZE] = { + /* level 10 */ + { + {0x40, 0x0}, + {0x41, 0x3}, + {0x42, 0x40}, + {0x43, 0x39}, + {0x44, 0x32}, + {0x45, 0x2e}, + {0x46, 0xc }, + {0x50, 0x0 }, + {0x51, 0x0 }, + {0x52, 0x0 }, + {0x53, 0x00}, + {0x54, 0x26}, + {0x55, 0x2d}, + {0x56, 0xb }, + {0x60, 0x0 }, + {0x61, 0x3f}, + {0x62, 0x40}, + {0x63, 0x38}, + {0x64, 0x31}, + {0x65, 0x2d}, + {0x66, 0x12}, + }, + + /*level 40*/ + { + {0x40, 0x0 }, + {0x41, 0x3f}, + {0x42, 0x3e}, + {0x43, 0x2e}, + {0x44, 0x2d}, + {0x45, 0x28}, + {0x46, 0x21}, + {0x50, 0x0 }, + {0x51, 0x0 }, + {0x52, 0x0 }, + {0x53, 0x21}, + {0x54, 0x2a}, + {0x55, 0x28}, + {0x56, 0x20}, + {0x60, 0x0 }, + {0x61, 0x3f}, + {0x62, 0x3e}, + {0x63, 0x2d}, + {0x64, 0x2b}, + {0x65, 0x26}, + {0x66, 0x2d}, + }, + + /*level 70*/ + { + {0x40, 0x0 }, + {0x41, 0x3f}, + {0x42, 0x35}, + {0x43, 0x2c}, + {0x44, 0x2b}, + {0x45, 0x26}, + {0x46, 0x29}, + {0x50, 0x0 }, + {0x51, 0x0 }, + {0x52, 0x0 }, + {0x53, 0x25}, + {0x54, 0x29}, + {0x55, 0x26}, + {0x56, 0x28}, + {0x60, 0x0 }, + {0x61, 0x3f}, + {0x62, 0x34}, + {0x63, 0x2b}, + {0x64, 0x2a}, + {0x65, 0x23}, + {0x66, 0x37}, + }, + + /*level 100*/ + { + {0x40, 0x0 }, + {0x41, 0x3f}, + {0x42, 0x30}, + {0x43, 0x2a}, + {0x44, 0x2b}, + {0x45, 0x24}, + {0x46, 0x2f}, + {0x50, 0x0 }, + {0x51, 0x0 }, + {0x52, 0x0 }, + {0x53, 0x25}, + {0x54, 0x29}, + {0x55, 0x24}, + {0x56, 0x2e}, + {0x60, 0x0 }, + {0x61, 0x3f}, + {0x62, 0x2f}, + {0x63, 0x29}, + {0x64, 0x29}, + {0x65, 0x21}, + {0x66, 0x3f}, + }, + + /*level 130*/ + { + {0x40, 0x0 }, + {0x41, 0x3f}, + {0x42, 0x2e}, + {0x43, 0x29}, + {0x44, 0x2a}, + {0x45, 0x23}, + {0x46, 0x34}, + {0x50, 0x0 }, + {0x51, 0x0 }, + {0x52, 0xa }, + {0x53, 0x25}, + {0x54, 0x28}, + {0x55, 0x23}, + {0x56, 0x33}, + {0x60, 0x0 }, + {0x61, 0x3f}, + {0x62, 0x2d}, + {0x63, 0x28}, + {0x64, 0x27}, + {0x65, 0x20}, + {0x66, 0x46}, + }, + + /*level 160*/ + { + {0x40, 0x0 }, + {0x41, 0x3f}, + {0x42, 0x2b}, + {0x43, 0x29}, + {0x44, 0x28}, + {0x45, 0x23}, + {0x46, 0x38}, + {0x50, 0x0 }, + {0x51, 0x0 }, + {0x52, 0xb }, + {0x53, 0x25}, + {0x54, 0x27}, + {0x55, 0x23}, + {0x56, 0x37}, + {0x60, 0x0 }, + {0x61, 0x3f}, + {0x62, 0x29}, + {0x63, 0x28}, + {0x64, 0x25}, + {0x65, 0x20}, + {0x66, 0x4b}, + }, + + /*level 190*/ + { + {0x40, 0x0 }, + {0x41, 0x3f}, + {0x42, 0x29}, + {0x43, 0x29}, + {0x44, 0x27}, + {0x45, 0x22}, + {0x46, 0x3c}, + {0x50, 0x0 }, + {0x51, 0x0 }, + {0x52, 0x10}, + {0x53, 0x26}, + {0x54, 0x26}, + {0x55, 0x22}, + {0x56, 0x3b}, + {0x60, 0x0 }, + {0x61, 0x3f}, + {0x62, 0x28}, + {0x63, 0x28}, + {0x64, 0x24}, + {0x65, 0x1f}, + {0x66, 0x50}, + }, + + /*level 220*/ + { + {0x40, 0x0 }, + {0x41, 0x3f}, + {0x42, 0x28}, + {0x43, 0x28}, + {0x44, 0x28}, + {0x45, 0x20}, + {0x46, 0x40}, + {0x50, 0x0 }, + {0x51, 0x0 }, + {0x52, 0x11}, + {0x53, 0x25}, + {0x54, 0x27}, + {0x55, 0x20}, + {0x56, 0x3f}, + {0x60, 0x0 }, + {0x61, 0x3f}, + {0x62, 0x27}, + {0x63, 0x26}, + {0x64, 0x26}, + {0x65, 0x1c}, + {0x66, 0x56}, + }, + + /*level 250*/ + { + {0x40, 0x0 }, + {0x41, 0x3f}, + {0x42, 0x2a}, + {0x43, 0x27}, + {0x44, 0x27}, + {0x45, 0x1f}, + {0x46, 0x44}, + {0x50, 0x0 }, + {0x51, 0x0 }, + {0x52, 0x17}, + {0x53, 0x24}, + {0x54, 0x26}, + {0x55, 0x1f}, + {0x56, 0x43}, + {0x60, 0x0 }, + {0x61, 0x3f}, + {0x62, 0x2a}, + {0x63, 0x25}, + {0x64, 0x24}, + {0x65, 0x1b}, + {0x66, 0x5c}, + }, +}; + +#define SAMSUNG_OLED_NUM_LEVELS ARRAY_SIZE(samsung_oled_gamma_table) + +#define SAMSUNG_OLED_MIN_VAL 10 +#define SAMSUNG_OLED_MAX_VAL 250 +#define SAMSUNG_OLED_DEFAULT_VAL (SAMSUNG_OLED_MIN_VAL + \ + (SAMSUNG_OLED_MAX_VAL - \ + SAMSUNG_OLED_MIN_VAL) / 2) + +#define SAMSUNG_OLED_LEVEL_STEP ((SAMSUNG_OLED_MAX_VAL - \ + SAMSUNG_OLED_MIN_VAL) / \ + (SAMSUNG_OLED_NUM_LEVELS - 1)) + +#define LCM_GPIO_CFG(gpio, func) \ +PCOM_GPIO_CFG(gpio, func, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA) +static uint32_t samsung_oled_on_gpio_table[] = { + LCM_GPIO_CFG(INCREDIBLEC_LCD_R0, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R1, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R2, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R3, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R4, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R5, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G0, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G1, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G2, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G3, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G4, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G5, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B0, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B1, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B2, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B3, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B4, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B5, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_PCLK, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_VSYNC, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_HSYNC, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_DE, 1), +}; + +static uint32_t samsung_oled_off_gpio_table[] = { + LCM_GPIO_CFG(INCREDIBLEC_LCD_R0, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R1, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R2, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R3, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R4, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R5, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G0, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G1, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G2, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G3, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G4, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G5, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B0, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B1, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B2, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B3, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B4, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B5, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_PCLK, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_VSYNC, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_HSYNC, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_DE, 0), +}; +#undef LCM_GPIO_CFG + +#define SONY_TFT_DEF_USER_VAL 102 +#define SONY_TFT_MIN_USER_VAL 30 +#define SONY_TFT_MAX_USER_VAL 255 +#define SONY_TFT_DEF_PANEL_VAL 120 +#define SONY_TFT_MIN_PANEL_VAL 8 +#define SONY_TFT_MAX_PANEL_VAL 255 +#define SONY_TFT_DEF_PANEL_UP_VAL 132 +#define SONY_TFT_MIN_PANEL_UP_VAL 9 +#define SONY_TFT_MAX_PANEL_UP_VAL 255 + +static DEFINE_MUTEX(panel_lock); +static struct work_struct brightness_delayed_work; +static DEFINE_SPINLOCK(brightness_lock); +static uint8_t new_val = SAMSUNG_OLED_DEFAULT_VAL; +static uint8_t last_val = SAMSUNG_OLED_DEFAULT_VAL; +static uint8_t table_sel_vals[] = { 0x43, 0x34 }; +static int table_sel_idx = 0; +static uint8_t tft_panel_on; + +static void gamma_table_bank_select(void) +{ + lcm_writeb(0x39, table_sel_vals[table_sel_idx]); + table_sel_idx ^= 1; +} + +static void samsung_oled_set_gamma_val(int val) +{ + int i; + int level; + int frac; + + val = clamp(val, SAMSUNG_OLED_MIN_VAL, SAMSUNG_OLED_MAX_VAL); + val = (val / 2) * 2; + + level = (val - SAMSUNG_OLED_MIN_VAL) / SAMSUNG_OLED_LEVEL_STEP; + frac = (val - SAMSUNG_OLED_MIN_VAL) % SAMSUNG_OLED_LEVEL_STEP; + + clk_enable(spi_clk); + + for (i = 0; i < OLED_GAMMA_TABLE_SIZE; ++i) { + unsigned int v1; + unsigned int v2 = 0; + u8 v; + if (frac == 0) { + v = samsung_oled_gamma_table[level][i].val; + } else { + + v1 = samsung_oled_gamma_table[level][i].val; + v2 = samsung_oled_gamma_table[level+1][i].val; + v = (v1 * (SAMSUNG_OLED_LEVEL_STEP - frac) + + v2 * frac) / SAMSUNG_OLED_LEVEL_STEP; + } + lcm_writeb(samsung_oled_gamma_table[level][i].reg, v); + } + + gamma_table_bank_select(); + clk_disable(spi_clk); + last_val = val; +} + +static void samsung_oled_panel_config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for (n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +static int samsung_oled_panel_gpio_switch (int on) +{ + samsung_oled_panel_config_gpio_table ( + !!on ? samsung_oled_on_gpio_table : samsung_oled_off_gpio_table, + ARRAY_SIZE(samsung_oled_on_gpio_table)); + + return 0; +} + +static int samsung_oled_panel_init(struct msm_lcdc_panel_ops *ops) +{ + pr_info("%s: +()\n", __func__); + mutex_lock(&panel_lock); + + clk_enable(spi_clk); + /* Set the gamma write target to 4, leave the current gamma set at 2 */ + lcm_writeb(0x39, 0x24); + clk_disable(spi_clk); + + mutex_unlock(&panel_lock); + pr_info("%s: -()\n", __func__); + return 0; +} + +static int samsung_oled_panel_unblank(struct msm_lcdc_panel_ops *ops) +{ + int i; + + pr_info("%s: +()\n", __func__); + + mutex_lock(&panel_lock); + samsung_oled_panel_gpio_switch(1); + + gpio_set_value(INCREDIBLEC_LCD_RST_ID1, 1); + udelay(50); + gpio_set_value(INCREDIBLEC_LCD_RST_ID1, 0); + udelay(20); + gpio_set_value(INCREDIBLEC_LCD_RST_ID1, 1); + msleep(20); + + clk_enable(spi_clk); + + for (i = 0; i < init_table_sz; i++) + lcm_writeb(init_tablep[i].reg, init_tablep[i].val); + + lcm_writew(0xef, 0xd0e8); + lcm_writeb(0x1d, 0xa0); + table_sel_idx = 0; + gamma_table_bank_select(); + samsung_oled_set_gamma_val(last_val); + msleep(250); + lcm_writeb(0x14, 0x03); + clk_disable(spi_clk); + + mutex_unlock(&panel_lock); + + pr_info("%s: -()\n", __func__); + return 0; +} + +static int samsung_oled_panel_blank(struct msm_lcdc_panel_ops *ops) +{ + pr_info("%s: +()\n", __func__); + mutex_lock(&panel_lock); + + clk_enable(spi_clk); + lcm_writeb(0x14, 0x0); + mdelay(1); + lcm_writeb(0x1d, 0xa1); + clk_disable(spi_clk); + msleep(200); + + gpio_set_value(INCREDIBLEC_LCD_RST_ID1, 0); + samsung_oled_panel_gpio_switch(0); + + mutex_unlock(&panel_lock); + pr_info("%s: -()\n", __func__); + return 0; +} + +#define LCM_GPIO_CFG(gpio, func, str) \ + PCOM_GPIO_CFG(gpio, func, GPIO_OUTPUT, GPIO_NO_PULL, str) + +static uint32_t sony_tft_display_on_gpio_table[] = { + LCM_GPIO_CFG(INCREDIBLEC_SPI_CLK, 1, GPIO_4MA), + LCM_GPIO_CFG(INCREDIBLEC_SPI_CS, 1, GPIO_4MA), + LCM_GPIO_CFG(INCREDIBLEC_LCD_ID0, 1, GPIO_4MA), + LCM_GPIO_CFG(INCREDIBLEC_SPI_DO, 1, GPIO_4MA), +}; + +static uint32_t sony_tft_display_off_gpio_table[] = { + LCM_GPIO_CFG(INCREDIBLEC_SPI_CLK, 0, GPIO_4MA), + LCM_GPIO_CFG(INCREDIBLEC_SPI_CS, 0, GPIO_4MA), + LCM_GPIO_CFG(INCREDIBLEC_LCD_ID0, 0, GPIO_4MA), + LCM_GPIO_CFG(INCREDIBLEC_SPI_DO, 0, GPIO_4MA), + +}; + +#undef LCM_GPIO_CFG + +#define SONY_TFT_DEF_PANEL_DELTA \ + (SONY_TFT_DEF_PANEL_VAL - SONY_TFT_MIN_PANEL_VAL) +#define SONY_TFT_DEF_USER_DELTA \ + (SONY_TFT_DEF_USER_VAL - SONY_TFT_MIN_USER_VAL) + +static void sony_tft_set_pwm_val(int val) +{ + uint8_t data[4] = {0,0,0,0}; + unsigned int min_pwm, def_pwm, max_pwm; + + pr_info("%s: %d\n", __func__, val); + + last_val = val; + + if (!tft_panel_on) + return; + + if(!is_sony_spi()) { + min_pwm = SONY_TFT_MIN_PANEL_UP_VAL; + def_pwm = SONY_TFT_DEF_PANEL_UP_VAL; + max_pwm = SONY_TFT_MAX_PANEL_UP_VAL; + } else { + min_pwm = SONY_TFT_MIN_PANEL_VAL; + def_pwm = SONY_TFT_DEF_PANEL_VAL; + max_pwm = SONY_TFT_MAX_PANEL_VAL; + } + + if (val <= SONY_TFT_DEF_USER_VAL) { + if (val <= SONY_TFT_MIN_USER_VAL) + val = min_pwm; + else + val = (def_pwm - min_pwm) * + (val - SONY_TFT_MIN_USER_VAL) / + SONY_TFT_DEF_USER_DELTA + + min_pwm; + } else { + val = (max_pwm - def_pwm) * + (val - SONY_TFT_DEF_USER_VAL) / + (SONY_TFT_MAX_USER_VAL - SONY_TFT_DEF_USER_VAL) + + def_pwm; + } + + if (!is_sony_spi()) { + data[0] = 5; + data[1] = val; + data[3] = 1; + microp_i2c_write(0x25, data, 4); + } else { + clk_enable(spi_clk); + qspi_send_9bit(0x0, 0x51); + qspi_send_9bit(0x1, val); + qspi_send_9bit(0x0, 0x53); + qspi_send_9bit(0x1, 0x24); + clk_disable(spi_clk); + } +} + +#undef SONY_TFT_DEF_PANEL_DELTA +#undef SONY_TFT_DEF_USER_DELTA + +static void sony_tft_panel_config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for (n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +static int sony_tft_panel_power(int on) +{ + unsigned id, on_off; + + if (on) { + on_off = 0; + + vreg_enable(vreg_lcm_aux_2v6); + vreg_enable(vreg_lcm_rftx_2v6); + + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + + id = PM_VREG_PDOWN_RFTX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + + gpio_set_value(INCREDIBLEC_LCD_RST_ID1, 1); + mdelay(10); + gpio_set_value(INCREDIBLEC_LCD_RST_ID1, 0); + udelay(500); + gpio_set_value(INCREDIBLEC_LCD_RST_ID1, 1); + mdelay(10); + sony_tft_panel_config_gpio_table( + sony_tft_display_on_gpio_table, + ARRAY_SIZE(sony_tft_display_on_gpio_table)); + } else { + on_off = 1; + + gpio_set_value(INCREDIBLEC_LCD_RST_ID1, 0); + + mdelay(120); + + vreg_disable(vreg_lcm_rftx_2v6); + vreg_disable(vreg_lcm_aux_2v6); + + id = PM_VREG_PDOWN_RFTX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + sony_tft_panel_config_gpio_table( + sony_tft_display_off_gpio_table, + ARRAY_SIZE(sony_tft_display_off_gpio_table)); + } + return 0; +} + +static int sony_tft_panel_init(struct msm_lcdc_panel_ops *ops) +{ + return 0; +} + +static void sony_tft_panel_without_gamma_init(void) +{ + pr_info("%s: init gamma setting", __func__); + + qspi_send_9bit(0x0, 0xF1); + qspi_send_9bit(0x1, 0x5A); + qspi_send_9bit(0x1, 0x5A); + // FAh RGB + qspi_send_9bit(0x0, 0xFA); + // Red + qspi_send_9bit(0x1, 0x32); + qspi_send_9bit(0x1, 0x3F); + qspi_send_9bit(0x1, 0x3F); + qspi_send_9bit(0x1, 0x29); + qspi_send_9bit(0x1, 0x3E); + qspi_send_9bit(0x1, 0x3C); + qspi_send_9bit(0x1, 0x3D); + qspi_send_9bit(0x1, 0x2C); + qspi_send_9bit(0x1, 0x27); + qspi_send_9bit(0x1, 0x3D); + qspi_send_9bit(0x1, 0x2E); + qspi_send_9bit(0x1, 0x31); + qspi_send_9bit(0x1, 0x3A); + qspi_send_9bit(0x1, 0x34); + qspi_send_9bit(0x1, 0x36); + // Green + qspi_send_9bit(0x1, 0x1A); + qspi_send_9bit(0x1, 0x3F); + qspi_send_9bit(0x1, 0x3F); + qspi_send_9bit(0x1, 0x2E); + qspi_send_9bit(0x1, 0x40); + qspi_send_9bit(0x1, 0x3C); + qspi_send_9bit(0x1, 0x3C); + qspi_send_9bit(0x1, 0x2B); + qspi_send_9bit(0x1, 0x25); + qspi_send_9bit(0x1, 0x39); + qspi_send_9bit(0x1, 0x25); + qspi_send_9bit(0x1, 0x23); + qspi_send_9bit(0x1, 0x2A); + qspi_send_9bit(0x1, 0x20); + qspi_send_9bit(0x1, 0x22); + // Blue + qspi_send_9bit(0x1, 0x00); + qspi_send_9bit(0x1, 0x3F); + qspi_send_9bit(0x1, 0x3F); + qspi_send_9bit(0x1, 0x2F); + qspi_send_9bit(0x1, 0x3E); + qspi_send_9bit(0x1, 0x3C); + qspi_send_9bit(0x1, 0x3C); + qspi_send_9bit(0x1, 0x2A); + qspi_send_9bit(0x1, 0x23); + qspi_send_9bit(0x1, 0x35); + qspi_send_9bit(0x1, 0x1E); + qspi_send_9bit(0x1, 0x18); + qspi_send_9bit(0x1, 0x1C); + qspi_send_9bit(0x1, 0x0C); + qspi_send_9bit(0x1, 0x0E); + // FBh RGB + qspi_send_9bit(0x0, 0xFB); + // Red + qspi_send_9bit(0x1, 0x00); + qspi_send_9bit(0x1, 0x0D); + qspi_send_9bit(0x1, 0x09); + qspi_send_9bit(0x1, 0x0C); + qspi_send_9bit(0x1, 0x26); + qspi_send_9bit(0x1, 0x2E); + qspi_send_9bit(0x1, 0x31); + qspi_send_9bit(0x1, 0x22); + qspi_send_9bit(0x1, 0x19); + qspi_send_9bit(0x1, 0x33); + qspi_send_9bit(0x1, 0x22); + qspi_send_9bit(0x1, 0x23); + qspi_send_9bit(0x1, 0x21); + qspi_send_9bit(0x1, 0x17); + qspi_send_9bit(0x1, 0x00); + // Green + qspi_send_9bit(0x1, 0x00); + qspi_send_9bit(0x1, 0x25); + qspi_send_9bit(0x1, 0x1D); + qspi_send_9bit(0x1, 0x1F); + qspi_send_9bit(0x1, 0x35); + qspi_send_9bit(0x1, 0x3C); + qspi_send_9bit(0x1, 0x3A); + qspi_send_9bit(0x1, 0x26); + qspi_send_9bit(0x1, 0x1B); + qspi_send_9bit(0x1, 0x34); + qspi_send_9bit(0x1, 0x23); + qspi_send_9bit(0x1, 0x23); + qspi_send_9bit(0x1, 0x1F); + qspi_send_9bit(0x1, 0x12); + qspi_send_9bit(0x1, 0x00); + // Blue + qspi_send_9bit(0x1, 0x00); + qspi_send_9bit(0x1, 0x3F); + qspi_send_9bit(0x1, 0x31); + qspi_send_9bit(0x1, 0x33); + qspi_send_9bit(0x1, 0x43); + qspi_send_9bit(0x1, 0x48); + qspi_send_9bit(0x1, 0x41); + qspi_send_9bit(0x1, 0x2A); + qspi_send_9bit(0x1, 0x1D); + qspi_send_9bit(0x1, 0x35); + qspi_send_9bit(0x1, 0x23); + qspi_send_9bit(0x1, 0x23); + qspi_send_9bit(0x1, 0x21); + qspi_send_9bit(0x1, 0x10); + qspi_send_9bit(0x1, 0x00); + // F3h Power control + qspi_send_9bit(0x0, 0xF3); + qspi_send_9bit(0x1, 0x00); + qspi_send_9bit(0x1, 0x10); + qspi_send_9bit(0x1, 0x25); + qspi_send_9bit(0x1, 0x01); + qspi_send_9bit(0x1, 0x2D); + qspi_send_9bit(0x1, 0x2D); + qspi_send_9bit(0x1, 0x24); + qspi_send_9bit(0x1, 0x2D); + qspi_send_9bit(0x1, 0x10); + qspi_send_9bit(0x1, 0x10); + qspi_send_9bit(0x1, 0x0A); + qspi_send_9bit(0x1, 0x37); + // F4h VCOM Control + qspi_send_9bit(0x0, 0xF4); + qspi_send_9bit(0x1, 0x88); + qspi_send_9bit(0x1, 0x20); + qspi_send_9bit(0x1, 0x00); + qspi_send_9bit(0x1, 0xAF); + qspi_send_9bit(0x1, 0x64); + qspi_send_9bit(0x1, 0x00); + qspi_send_9bit(0x1, 0xAA); + qspi_send_9bit(0x1, 0x64); + qspi_send_9bit(0x1, 0x00); + qspi_send_9bit(0x1, 0x00); + //Change to level 1 + qspi_send_9bit(0x0, 0xF0); + qspi_send_9bit(0x1, 0x5A); + qspi_send_9bit(0x1, 0x5A); +} + +static int sony_tft_panel_unblank(struct msm_lcdc_panel_ops *ops) +{ + pr_info("%s: +()\n", __func__); + + mutex_lock(&panel_lock); + + if (tft_panel_on) { + pr_info("%s: -() already unblanked\n", __func__); + goto done; + } + + sony_tft_panel_power(1); + msleep(45); + + clk_enable(spi_clk); + qspi_send_9bit(0x0, 0x11); + msleep(5); + qspi_send_9bit(0x0, 0x3a); + if (is_sony_RGB666()) + qspi_send_9bit(0x1, 0x06); + else + qspi_send_9bit(0x1, 0x05); + msleep(100); + qspi_send_9bit(0x0, 0x29); + msleep(20); + + //init gamma setting + if(!is_sony_with_gamma()) + sony_tft_panel_without_gamma_init(); + + /* unlock register page for pwm setting */ + if (is_sony_spi()) { + qspi_send_9bit(0x0, 0xf0); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x0, 0xf1); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x0, 0xd0); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x1, 0x5a); + + qspi_send_9bit(0x0, 0xc2); + qspi_send_9bit(0x1, 0x53); + qspi_send_9bit(0x1, 0x12); + } + clk_disable(spi_clk); + msleep(100); + tft_panel_on = 1; + sony_tft_set_pwm_val(last_val); + + pr_info("%s: -()\n", __func__); +done: + mutex_unlock(&panel_lock); + return 0; +} + +static int sony_tft_panel_blank(struct msm_lcdc_panel_ops *ops) +{ + uint8_t data[4] = {0, 0, 0, 0}; + pr_info("%s: +()\n", __func__); + + mutex_lock(&panel_lock); + + clk_enable(spi_clk); + qspi_send_9bit(0x0, 0x28); + qspi_send_9bit(0x0, 0x10); + clk_disable(spi_clk); + + msleep(40); + sony_tft_panel_power(0); + tft_panel_on = 0; + + mutex_unlock(&panel_lock); + + if (!is_sony_spi()) { + data[0] = 5; + data[1] = 0; + data[3] = 1; + microp_i2c_write(0x25, data, 4); + } + + pr_info("%s: -()\n", __func__); + return 0; +} + +static struct msm_lcdc_panel_ops incrediblec_lcdc_amoled_panel_ops = { + .init = samsung_oled_panel_init, + .blank = samsung_oled_panel_blank, + .unblank = samsung_oled_panel_unblank, +}; + +static struct msm_lcdc_panel_ops incrediblec_lcdc_tft_panel_ops = { + .init = sony_tft_panel_init, + .blank = sony_tft_panel_blank, + .unblank = sony_tft_panel_unblank, +}; + + +static struct msm_lcdc_timing incrediblec_lcdc_amoled_timing = { + .clk_rate = 24576000, + .hsync_pulse_width = 4, + .hsync_back_porch = 8, + .hsync_front_porch = 8, + .hsync_skew = 0, + .vsync_pulse_width = 2, + .vsync_back_porch = 8, + .vsync_front_porch = 8, + .vsync_act_low = 1, + .hsync_act_low = 1, + .den_act_low = 1, +}; + +static struct msm_lcdc_timing incrediblec_lcdc_tft_timing = { + .clk_rate = 24576000, + .hsync_pulse_width = 2, + .hsync_back_porch = 20, + .hsync_front_porch = 20, + .hsync_skew = 0, + .vsync_pulse_width = 2, + .vsync_back_porch = 6, + .vsync_front_porch = 4, + .vsync_act_low = 1, + .hsync_act_low = 1, + .den_act_low = 0, +}; + +static struct msm_fb_data incrediblec_lcdc_fb_data = { + .xres = 480, + .yres = 800, + .width = 48, + .height = 80, + .output_format = MSM_MDP_OUT_IF_FMT_RGB565, +}; + +static struct msm_lcdc_platform_data incrediblec_lcdc_amoled_platform_data = { + .panel_ops = &incrediblec_lcdc_amoled_panel_ops, + .timing = &incrediblec_lcdc_amoled_timing, + .fb_id = 0, + .fb_data = &incrediblec_lcdc_fb_data, + .fb_resource = &resources_msm_fb[0], +}; + +static struct msm_lcdc_platform_data incrediblec_lcdc_tft_platform_data = { + .panel_ops = &incrediblec_lcdc_tft_panel_ops, + .timing = &incrediblec_lcdc_tft_timing, + .fb_id = 0, + .fb_data = &incrediblec_lcdc_fb_data, + .fb_resource = &resources_msm_fb[0], +}; + +static struct platform_device incrediblec_lcdc_amoled_device = { + .name = "msm_mdp_lcdc", + .id = -1, + .dev = { + .platform_data = &incrediblec_lcdc_amoled_platform_data, + }, +}; + +static struct platform_device incrediblec_lcdc_tft_device = { + .name = "msm_mdp_lcdc", + .id = -1, + .dev = { + .platform_data = &incrediblec_lcdc_tft_platform_data, + }, +}; + +static int incrediblec_init_spi_hack(void) +{ + int ret; + + spi_base = ioremap(MSM_SPI_PHYS, MSM_SPI_SIZE); + if (!spi_base) + return -1; + + spi_clk = clk_get(&msm_device_spi.dev, "spi_clk"); + if (IS_ERR(spi_clk)) { + pr_err("%s: unable to get spi_clk\n", __func__); + ret = PTR_ERR(spi_clk); + goto err_clk_get; + } + + clk_enable(spi_clk); + + printk("spi: SPI_CONFIG=%x\n", readl(spi_base + SPI_CONFIG)); + printk("spi: SPI_IO_CONTROL=%x\n", readl(spi_base + SPI_IO_CONTROL)); + printk("spi: SPI_OPERATIONAL=%x\n", readl(spi_base + SPI_OPERATIONAL)); + printk("spi: SPI_ERROR_FLAGS_EN=%x\n", + readl(spi_base + SPI_ERROR_FLAGS_EN)); + printk("spi: SPI_ERROR_FLAGS=%x\n", readl(spi_base + SPI_ERROR_FLAGS)); + printk("-%s()\n", __FUNCTION__); + clk_disable(spi_clk); + + return 0; + +err_clk_get: + iounmap(spi_base); + return ret; +} + +static void incrediblec_brightness_set(struct led_classdev *led_cdev, + enum led_brightness val) +{ + unsigned long flags; + led_cdev->brightness = val; + + spin_lock_irqsave(&brightness_lock, flags); + new_val = val; + spin_unlock_irqrestore(&brightness_lock, flags); + + schedule_work(&brightness_delayed_work); +} + +static void incrediblec_brightness_amoled_set_work(struct work_struct *work_ptr) +{ + unsigned long flags; + uint8_t val; + + spin_lock_irqsave(&brightness_lock, flags); + val = new_val; + spin_unlock_irqrestore(&brightness_lock, flags); + + mutex_lock(&panel_lock); + samsung_oled_set_gamma_val(val); + mutex_unlock(&panel_lock); +} + +static void incrediblec_brightness_tft_set_work(struct work_struct *work_ptr) +{ + unsigned long flags; + uint8_t val; + + spin_lock_irqsave(&brightness_lock, flags); + val = new_val; + spin_unlock_irqrestore(&brightness_lock, flags); + + mutex_lock(&panel_lock); + sony_tft_set_pwm_val(val); + mutex_unlock(&panel_lock); +} + +static struct led_classdev incrediblec_brightness_led = { + .name = "lcd-backlight", + .brightness = LED_FULL, + .brightness_set = incrediblec_brightness_set, +}; + +int __init incrediblec_init_panel(void) +{ + int ret; + + if (system_rev <= 2) { + /* CDMA version (except for EVT1) supports RGB666 */ + init_tablep = samsung_oled_rgb666_init_table; + init_table_sz = ARRAY_SIZE(samsung_oled_rgb666_init_table); + incrediblec_lcdc_fb_data.output_format = MSM_MDP_OUT_IF_FMT_RGB666; + } + + ret = platform_device_register(&msm_device_mdp); + if (ret != 0) + return ret; + + ret = incrediblec_init_spi_hack(); + if (ret != 0) + return ret; + + if (gpio_get_value(INCREDIBLEC_LCD_ID0)) { + pr_info("%s: tft panel\n", __func__); + vreg_lcm_rftx_2v6 = vreg_get(0, "rftx"); + if (IS_ERR(vreg_lcm_rftx_2v6)) + return PTR_ERR(vreg_lcm_rftx_2v6); + vreg_set_level(vreg_lcm_rftx_2v6, 2600); + +#ifdef CONFIG_MACH_INCREDIBLEC + vreg_lcm_aux_2v6 = vreg_get(0, "gp4"); +#else + vreg_lcm_aux_2v6 = vreg_get(0, "gp6"); +#endif + if (IS_ERR(vreg_lcm_aux_2v6)) + return PTR_ERR(vreg_lcm_aux_2v6); + vreg_set_level(vreg_lcm_aux_2v6, 2600); + + if (gpio_get_value(INCREDIBLEC_LCD_RST_ID1)) + tft_panel_on = 1; + ret = platform_device_register(&incrediblec_lcdc_tft_device); + INIT_WORK(&brightness_delayed_work, incrediblec_brightness_tft_set_work); + } else { + pr_info("%s: amoled panel\n", __func__); + ret = platform_device_register(&incrediblec_lcdc_amoled_device); + INIT_WORK(&brightness_delayed_work, incrediblec_brightness_amoled_set_work); + } + + if (ret != 0) + return ret; + + ret = led_classdev_register(NULL, &incrediblec_brightness_led); + if (ret != 0) { + pr_err("%s: Cannot register brightness led\n", __func__); + return ret; + } + + return 0; +} + +device_initcall(incrediblec_init_panel); diff --git a/arch/arm/mach-msm/board-incrediblec-rfkill.c b/arch/arm/mach-msm/board-incrediblec-rfkill.c new file mode 100644 index 0000000000000..c42f4b4a7f0c0 --- /dev/null +++ b/arch/arm/mach-msm/board-incrediblec-rfkill.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation. + * + * 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. + * + */ + +/* Control bluetooth power for incrediblec platform */ + +#include +#include +#include +#include +#include +#include +#include + +#include "board-incrediblec.h" + + +static struct rfkill *bt_rfk; +static const char bt_name[] = "bcm4329"; + +static int bluetooth_set_power(void *data, bool blocked) +{ + if (!blocked) { + gpio_direction_output(INCREDIBLEC_GPIO_BT_RESET_N, 1); + gpio_direction_output(INCREDIBLEC_GPIO_BT_SHUTDOWN_N, 1); + } else { + gpio_direction_output(INCREDIBLEC_GPIO_BT_SHUTDOWN_N, 0); + gpio_direction_output(INCREDIBLEC_GPIO_BT_RESET_N, 0); + } + return 0; +} + +static struct rfkill_ops incrediblec_rfkill_ops = { + .set_block = bluetooth_set_power, +}; + +static int incrediblec_rfkill_probe(struct platform_device *pdev) +{ + int rc = 0; + bool default_state = true; /* off */ + + rc = gpio_request(INCREDIBLEC_GPIO_BT_RESET_N, "bt_reset"); + if (rc) + goto err_gpio_reset; + rc = gpio_request(INCREDIBLEC_GPIO_BT_SHUTDOWN_N, "bt_shutdown"); + if (rc) + goto err_gpio_shutdown; + + bluetooth_set_power(NULL, default_state); + + bt_rfk = rfkill_alloc(bt_name, &pdev->dev, RFKILL_TYPE_BLUETOOTH, + &incrediblec_rfkill_ops, NULL); + if (!bt_rfk) { + rc = -ENOMEM; + goto err_rfkill_alloc; + } + + rfkill_set_states(bt_rfk, default_state, false); + + /* userspace cannot take exclusive control */ + + rc = rfkill_register(bt_rfk); + if (rc) + goto err_rfkill_reg; + + return 0; + +err_rfkill_reg: + rfkill_destroy(bt_rfk); +err_rfkill_alloc: + gpio_free(INCREDIBLEC_GPIO_BT_SHUTDOWN_N); +err_gpio_shutdown: + gpio_free(INCREDIBLEC_GPIO_BT_RESET_N); +err_gpio_reset: + return rc; +} + +static int incrediblec_rfkill_remove(struct platform_device *dev) +{ + rfkill_unregister(bt_rfk); + rfkill_destroy(bt_rfk); + gpio_free(INCREDIBLEC_GPIO_BT_SHUTDOWN_N); + gpio_free(INCREDIBLEC_GPIO_BT_RESET_N); + + return 0; +} + +static struct platform_driver incrediblec_rfkill_driver = { + .probe = incrediblec_rfkill_probe, + .remove = incrediblec_rfkill_remove, + .driver = { + .name = "incrediblec_rfkill", + .owner = THIS_MODULE, + }, +}; + +static int __init incrediblec_rfkill_init(void) +{ + if (!machine_is_incrediblec()) + return 0; + + return platform_driver_register(&incrediblec_rfkill_driver); +} + +static void __exit incrediblec_rfkill_exit(void) +{ + platform_driver_unregister(&incrediblec_rfkill_driver); +} + + +module_init(incrediblec_rfkill_init); +module_exit(incrediblec_rfkill_exit); +MODULE_DESCRIPTION("incrediblec rfkill"); +MODULE_AUTHOR("Nick Pelly "); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-incrediblec-tpa2018d1.c b/arch/arm/mach-msm/board-incrediblec-tpa2018d1.c new file mode 100644 index 0000000000000..26f12dde308de --- /dev/null +++ b/arch/arm/mach-msm/board-incrediblec-tpa2018d1.c @@ -0,0 +1,368 @@ +/* drivers/i2c/chips/tpa2018d1.c + * + * TI TPA2018D1 Speaker Amplifier + * + * Copyright (C) 2009 HTC Corporation + * + * 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. + * + */ + +/* TODO: content validation in TPA2018_SET_CONFIG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-incrediblec-tpa2018d1.h" + +static struct i2c_client *this_client; +static struct tpa2018d1_platform_data *pdata; +static int is_on; +static char spk_amp_cfg[8]; +static const char spk_amp_on[8] = { /* same length as spk_amp_cfg */ + 0x01, 0xc3, 0x20, 0x01, 0x00, 0x08, 0x1a, 0x21 +}; +static const char spk_amp_off[] = {0x01, 0xa2}; + +static DEFINE_MUTEX(spk_amp_lock); +static int tpa2018d1_opened; +static char *config_data; +static int tpa2018d1_num_modes; + +#define DEBUG 0 + +static int tpa2018_i2c_write(const char *txData, int length) +{ + struct i2c_msg msg[] = { + { + .addr = this_client->addr, + .flags = 0, + .len = length, + .buf = txData, + }, + }; + + if (i2c_transfer(this_client->adapter, msg, 1) < 0) { + pr_err("%s: I2C transfer error\n", __func__); + return -EIO; + } else + return 0; +} + +static int tpa2018_i2c_read(char *rxData, int length) +{ + struct i2c_msg msgs[] = { + { + .addr = this_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = rxData, + }, + }; + + if (i2c_transfer(this_client->adapter, msgs, 1) < 0) { + pr_err("%s: I2C transfer error\n", __func__); + return -EIO; + } + +#if DEBUG + do { + int i = 0; + for (i = 0; i < length; i++) + pr_info("%s: rx[%d] = %2x\n", + __func__, i, rxData[i]); + } while(0); +#endif + + return 0; +} + +static int tpa2018d1_open(struct inode *inode, struct file *file) +{ + int rc = 0; + + mutex_lock(&spk_amp_lock); + + if (tpa2018d1_opened) { + pr_err("%s: busy\n", __func__); + rc = -EBUSY; + goto done; + } + + tpa2018d1_opened = 1; +done: + mutex_unlock(&spk_amp_lock); + return rc; +} + +static int tpa2018d1_release(struct inode *inode, struct file *file) +{ + mutex_lock(&spk_amp_lock); + tpa2018d1_opened = 0; + mutex_unlock(&spk_amp_lock); + + return 0; +} + +static int tpa2018d1_read_config(void __user *argp) +{ + int rc = 0; + unsigned char reg_idx = 0x01; + unsigned char tmp[7]; + + if (!is_on) { + gpio_set_value(pdata->gpio_tpa2018_spk_en, 1); + msleep(5); /* According to TPA2018D1 Spec */ + } + + rc = tpa2018_i2c_write(®_idx, sizeof(reg_idx)); + if (rc < 0) + goto err; + + rc = tpa2018_i2c_read(tmp, sizeof(tmp)); + if (rc < 0) + goto err; + + if (copy_to_user(argp, &tmp, sizeof(tmp))) + rc = -EFAULT; + +err: + if (!is_on) + gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); + return rc; +} + +static int tpa2018d1_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int rc = 0; + int mode = -1; + int offset = 0; + struct tpa2018d1_config_data cfg; + + mutex_lock(&spk_amp_lock); + + switch (cmd) { + case TPA2018_SET_CONFIG: + if (copy_from_user(spk_amp_cfg, argp, sizeof(spk_amp_cfg))) + rc = -EFAULT; + break; + + case TPA2018_READ_CONFIG: + rc = tpa2018d1_read_config(argp); + break; + + case TPA2018_SET_MODE: + if (copy_from_user(&mode, argp, sizeof(mode))) { + rc = -EFAULT; + break; + } + if (mode >= tpa2018d1_num_modes || mode < 0) { + pr_err("%s: unsupported tpa2018d1 mode %d\n", + __func__, mode); + rc = -EINVAL; + break; + } + if (!config_data) { + pr_err("%s: no config data!\n", __func__); + rc = -EIO; + break; + } + memcpy(spk_amp_cfg, config_data + mode * TPA2018D1_CMD_LEN, + TPA2018D1_CMD_LEN); + break; + + case TPA2018_SET_PARAM: + if (copy_from_user(&cfg, argp, sizeof(cfg))) { + pr_err("%s: copy from user failed.\n", __func__); + rc = -EFAULT; + break; + } + tpa2018d1_num_modes = cfg.mode_num; + if (tpa2018d1_num_modes > TPA2018_NUM_MODES) { + pr_err("%s: invalid number of modes %d\n", __func__, + tpa2018d1_num_modes); + rc = -EINVAL; + break; + } + if (cfg.data_len != tpa2018d1_num_modes*TPA2018D1_CMD_LEN) { + pr_err("%s: invalid data length %d, expecting %d\n", + __func__, cfg.data_len, + tpa2018d1_num_modes * TPA2018D1_CMD_LEN); + rc = -EINVAL; + break; + } + /* Free the old data */ + if (config_data) + kfree(config_data); + config_data = kmalloc(cfg.data_len, GFP_KERNEL); + if (!config_data) { + pr_err("%s: out of memory\n", __func__); + rc = -ENOMEM; + break; + } + if (copy_from_user(config_data, cfg.cmd_data, cfg.data_len)) { + pr_err("%s: copy data from user failed.\n", __func__); + kfree(config_data); + config_data = NULL; + rc = -EFAULT; + break; + } + /* replace default setting with playback setting */ + if (tpa2018d1_num_modes >= TPA2018_MODE_PLAYBACK) { + offset = TPA2018_MODE_PLAYBACK * TPA2018D1_CMD_LEN; + memcpy(spk_amp_cfg, config_data + offset, + TPA2018D1_CMD_LEN); + } + break; + + default: + pr_err("%s: invalid command %d\n", __func__, _IOC_NR(cmd)); + rc = -EINVAL; + break; + } + mutex_unlock(&spk_amp_lock); + return rc; +} + +static struct file_operations tpa2018d1_fops = { + .owner = THIS_MODULE, + .open = tpa2018d1_open, + .release = tpa2018d1_release, + .unlocked_ioctl = tpa2018d1_ioctl, +}; + +static struct miscdevice tpa2018d1_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "tpa2018d1", + .fops = &tpa2018d1_fops, +}; + +void tpa2018d1_set_speaker_amp(int on) +{ + if (!pdata) { + pr_err("%s: no platform data!\n", __func__); + return; + } + mutex_lock(&spk_amp_lock); + if (on && !is_on) { + gpio_set_value(pdata->gpio_tpa2018_spk_en, 1); + msleep(5); /* According to TPA2018D1 Spec */ + + if (tpa2018_i2c_write(spk_amp_cfg, sizeof(spk_amp_cfg)) == 0) { + is_on = 1; + pr_info("%s: ON\n", __func__); + } + } else if (!on && is_on) { + if (tpa2018_i2c_write(spk_amp_off, sizeof(spk_amp_off)) == 0) { + is_on = 0; + msleep(2); + gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); + pr_info("%s: OFF\n", __func__); + } + } + mutex_unlock(&spk_amp_lock); +} + +static int tpa2018d1_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + + pdata = client->dev.platform_data; + + if (!pdata) { + ret = -EINVAL; + pr_err("%s: platform data is NULL\n", __func__); + goto err_no_pdata; + } + + this_client = client; + + ret = gpio_request(pdata->gpio_tpa2018_spk_en, "tpa2018"); + if (ret < 0) { + pr_err("%s: gpio request aud_spk_en pin failed\n", __func__); + goto err_free_gpio; + } + + ret = gpio_direction_output(pdata->gpio_tpa2018_spk_en, 1); + if (ret < 0) { + pr_err("%s: request aud_spk_en gpio direction failed\n", + __func__); + goto err_free_gpio; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s: i2c check functionality error\n", __func__); + ret = -ENODEV; + goto err_free_gpio; + } + + gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); /* Default Low */ + + ret = misc_register(&tpa2018d1_device); + if (ret) { + pr_err("%s: tpa2018d1_device register failed\n", __func__); + goto err_free_gpio; + } + memcpy(spk_amp_cfg, spk_amp_on, sizeof(spk_amp_on)); + return 0; + +err_free_gpio: + gpio_free(pdata->gpio_tpa2018_spk_en); +err_no_pdata: + return ret; +} + +static int tpa2018d1_suspend(struct i2c_client *client, pm_message_t mesg) +{ + return 0; +} + +static int tpa2018d1_resume(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id tpa2018d1_id[] = { + { TPA2018D1_I2C_NAME, 0 }, + { } +}; + +static struct i2c_driver tpa2018d1_driver = { + .probe = tpa2018d1_probe, + .suspend = tpa2018d1_suspend, + .resume = tpa2018d1_resume, + .id_table = tpa2018d1_id, + .driver = { + .name = TPA2018D1_I2C_NAME, + }, +}; + +static int __init tpa2018d1_init(void) +{ + pr_info("%s\n", __func__); + return i2c_add_driver(&tpa2018d1_driver); +} + +module_init(tpa2018d1_init); + +MODULE_DESCRIPTION("tpa2018d1 speaker amp driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-incrediblec-tpa2018d1.h b/arch/arm/mach-msm/board-incrediblec-tpa2018d1.h new file mode 100644 index 0000000000000..dc11012209454 --- /dev/null +++ b/arch/arm/mach-msm/board-incrediblec-tpa2018d1.h @@ -0,0 +1,35 @@ +/* include/linux/tpa2018d1.h - tpa2018d1 speaker amplifier driver + * + * Copyright (C) 2009 HTC Corporation. + * + * 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 __ASM_ARM_ARCH_TPA2018D1_H +#define __ASM_ARM_ARCH_TPA2018D1_H + +#define TPA2018D1_I2C_NAME "tpa2018d1" +#define TPA2018D1_CMD_LEN 8 + +struct tpa2018d1_platform_data { + uint32_t gpio_tpa2018_spk_en; +}; + +struct tpa2018d1_config_data { + unsigned char *cmd_data; /* [mode][cmd_len][cmds..] */ + unsigned int mode_num; + unsigned int data_len; +}; + +extern void tpa2018d1_set_speaker_amp(int on); + +#endif /* __ASM_ARM_ARCH_TPA2018D1_H */ diff --git a/arch/arm/mach-msm/board-incrediblec-tpa6130.c b/arch/arm/mach-msm/board-incrediblec-tpa6130.c new file mode 100644 index 0000000000000..bd91aeae5f587 --- /dev/null +++ b/arch/arm/mach-msm/board-incrediblec-tpa6130.c @@ -0,0 +1,185 @@ +/* driver/i2c/chip/tap6130.c + * + * TI TPA6130 Headset Amp + * + * Copyright (C) 2009 HTC Corporation + * + * 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 + +#define HEADSET_MTOA_PROG 0x30100003 +#define HEADSET_MTOA_VERS 0 +#define HTC_HEADSET_NULL_PROC 0 +#define HTC_HEADSET_CTL_PROC 1 + +static struct i2c_client *this_client; +struct mutex amp_mutex; +static struct tpa6130_platform_data *pdata; + +static int i2c_on; +char buffer[2]; + +static int I2C_TxData(char *txData, int length) +{ + struct i2c_msg msg[] = { + { + .addr = this_client->addr, + .flags = 0, + .len = length, + .buf = txData, + }, + }; + + if (i2c_transfer(this_client->adapter, msg, 1) < 0) { + pr_err("tpa6130 :I2C transfer error\n"); + return -EIO; + } else + return 0; +} + +void set_headset_amp(int on) +{ + mutex_lock(&_mutex); + if (on && !i2c_on) { + buffer[0] = 0x01; + buffer[1] = 0xC0; + buffer[2] = 0x3E; + if (I2C_TxData(buffer, 3) == 0) { + i2c_on = 1; + pr_err("tpa6130: turn on headset amp !\n"); + } + } else if (!on && i2c_on) { + buffer[0] = 0x01; + buffer[1] = 0xC1; + if (I2C_TxData(buffer, 2) == 0) { + i2c_on = 0; + pr_err("tpa6130: turn off headset amp !\n"); + } + } + mutex_unlock(&_mutex); +} + +static int handle_headset_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + struct rpc_headset_amp_ctl_args *args; + + if (!pdata->enable_rpc_server) + return 0; + + switch (req->procedure) { + case HTC_HEADSET_NULL_PROC: + return 0; + case HTC_HEADSET_CTL_PROC: + args = (struct rpc_headset_amp_ctl_args *)(req + 1); + args->on = be32_to_cpu(args->on); + if (args->on) { + gpio_set_value(pdata->gpio_hp_sd, 1); + msleep(10); + set_headset_amp(args->on); + } else if (!args->on) { + set_headset_amp(args->on); + gpio_set_value(pdata->gpio_hp_sd, 0); + } + return 0; + default: + pr_err("tpa6130a: the wrong proc for headset server\n"); + } + return -ENODEV; +} + +static struct msm_rpc_server headset_server = { + .prog = HEADSET_MTOA_PROG, + .vers = HEADSET_MTOA_VERS, + .rpc_call = handle_headset_call +}; + +int tpa6130_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + + pdata = client->dev.platform_data; + if (pdata == NULL) { + pr_err("tpa6130: platform data is NULL\n"); + goto fault; + } + + if (pdata->enable_rpc_server) { + msm_rpc_create_server(&headset_server); + + ret = gpio_request(pdata->gpio_hp_sd, "tpa6130"); + if (ret < 0) { + pr_err("tap6130a : gpio request failed\n"); + goto fault; + } + + ret = gpio_direction_output(pdata->gpio_hp_sd, 1); + if (ret < 0) { + pr_err("tap6130a: request reset gpio failed\n"); + goto fault; + } + gpio_set_value(pdata->gpio_hp_sd, 0); + } + + this_client = client; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("tpa6130a: i2c check functionality error\n"); + goto fault; + } + + return 0; +fault: + return -ENODEV; +} + +static int tpa6130_remove(struct i2c_client *client) +{ + return 0; +} +static const struct i2c_device_id tpa6130_id[] = { + { TPA6130_I2C_NAME, 0 }, + { } +}; + +static struct i2c_driver tpa6130_driver = { + .probe = tpa6130_probe, + .remove = tpa6130_remove, + .id_table = tpa6130_id, + .driver = { + .name = TPA6130_I2C_NAME, + }, +}; + +static int __init tpa6130_init(void) +{ + pr_err("tpa6130 HP AMP: init\n"); + mutex_init(&_mutex); + return i2c_add_driver(&tpa6130_driver); +} + +static void __exit tpa6130_exit(void) +{ + i2c_del_driver(&tpa6130_driver); +} + +module_init(tpa6130_init); +module_exit(tpa6130_exit); diff --git a/arch/arm/mach-msm/board-incrediblec-tv.c b/arch/arm/mach-msm/board-incrediblec-tv.c new file mode 100644 index 0000000000000..8cb9197d47a93 --- /dev/null +++ b/arch/arm/mach-msm/board-incrediblec-tv.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2008 HTC Corporation. + * + * 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 "board-incrediblec.h" +#include "devices.h" +#include "proc_comm.h" + +static struct resource msm_tvenc_resources[] = { + { + .name = "msm_tv", + .start = 0xaa400000, + .end = 0xaa400000 + 0x1000 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = MSM_TV_FB_BASE, + .end = MSM_TV_FB_BASE + MSM_TV_FB_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct msm_fb_data incrediblec_tvenc_fb_data = { + .xres = 480, + .yres = 720, + /* Typical geometry of 17" CRT */ + .width = 338, + .height = 270, + .output_format = MDP_YCRYCB_H2V1, +}; + +static int incrediblec_tv_video_relay(int on_off) +{ + pr_info("[tv]: %s(%d)\n", __func__, on_off); + on_off = !!on_off; + gpio_set_value(INCREDIBLEC_VIDEO_SHDN_N, on_off); + if (system_rev < 2) + gpio_set_value(INCREDIBLEC_AV_SWITCH, on_off); + + return 0; +} + +static struct msm_tvenc_platform_data incrediblec_tvenc_platform_data = { + .fb_id = 1, + .fb_data = &incrediblec_tvenc_fb_data, + .fb_resource = &msm_tvenc_resources[1], + .video_relay = &incrediblec_tv_video_relay, +}; + +static struct platform_device msm_tvenc_device = { + .name = "msm_tv", + .id = 0, + .num_resources = ARRAY_SIZE(msm_tvenc_resources), + .resource = msm_tvenc_resources, + .dev = { + .platform_data = &incrediblec_tvenc_platform_data, + }, +}; + +int __init incrediblec_init_tv(void) +{ + int ret, engid; + uint32_t config; + + if (!machine_is_incrediblec()) + return 0; + + engid = incrediblec_get_engineerid(); + if (0 == engid || 0xF == engid) { + msm_tvenc_resources[1].start = MSM_TV_FB_XA_BASE; + msm_tvenc_resources[1].end = msm_tvenc_resources[1].start + + MSM_TV_FB_SIZE - 1; + } else if (engid >= 3) { + msm_tvenc_resources[1].start = + MSM_TV_FB_BASE + MSM_MEM_128MB_OFFSET; + msm_tvenc_resources[1].end = msm_tvenc_resources[1].start + + MSM_TV_FB_SIZE - 1; + } + + config = PCOM_GPIO_CFG(INCREDIBLEC_VIDEO_SHDN_N, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_16MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, 0); + gpio_set_value(INCREDIBLEC_VIDEO_SHDN_N, 1); + config = PCOM_GPIO_CFG(INCREDIBLEC_TV_LOAD_DET, 0, GPIO_INPUT, + GPIO_PULL_DOWN, GPIO_16MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, 0); + + if (system_rev < 2) { + config = PCOM_GPIO_CFG(INCREDIBLEC_AV_SWITCH, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_16MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, 0); + } + + if ((ret = platform_device_register(&msm_tvenc_device)) != 0) + return ret; + + return 0; +} + +device_initcall(incrediblec_init_tv); diff --git a/arch/arm/mach-msm/board-incrediblec-wifi.c b/arch/arm/mach-msm/board-incrediblec-wifi.c new file mode 100644 index 0000000000000..57c61262b73af --- /dev/null +++ b/arch/arm/mach-msm/board-incrediblec-wifi.c @@ -0,0 +1,140 @@ +/* linux/arch/arm/mach-msm/board-incrediblec-wifi.c +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-incrediblec.h" + +int incrediblec_wifi_power(int on); +int incrediblec_wifi_reset(int on); +int incrediblec_wifi_set_carddetect(int on); + +#define PREALLOC_WLAN_NUMBER_OF_SECTIONS 4 +#define PREALLOC_WLAN_NUMBER_OF_BUFFERS 160 +#define PREALLOC_WLAN_SECTION_HEADER 24 + +#define WLAN_SECTION_SIZE_0 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128) +#define WLAN_SECTION_SIZE_1 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128) +#define WLAN_SECTION_SIZE_2 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 512) +#define WLAN_SECTION_SIZE_3 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 1024) + +#define WLAN_SKB_BUF_NUM 16 + +static struct sk_buff *wlan_static_skb[WLAN_SKB_BUF_NUM]; + +typedef struct wifi_mem_prealloc_struct { + void *mem_ptr; + unsigned long size; +} wifi_mem_prealloc_t; + +static wifi_mem_prealloc_t wifi_mem_array[PREALLOC_WLAN_NUMBER_OF_SECTIONS] = { + { NULL, (WLAN_SECTION_SIZE_0 + PREALLOC_WLAN_SECTION_HEADER) }, + { NULL, (WLAN_SECTION_SIZE_1 + PREALLOC_WLAN_SECTION_HEADER) }, + { NULL, (WLAN_SECTION_SIZE_2 + PREALLOC_WLAN_SECTION_HEADER) }, + { NULL, (WLAN_SECTION_SIZE_3 + PREALLOC_WLAN_SECTION_HEADER) } +}; + +static void *incrediblec_wifi_mem_prealloc(int section, unsigned long size) +{ + if (section == PREALLOC_WLAN_NUMBER_OF_SECTIONS) + return wlan_static_skb; + if ((section < 0) || (section > PREALLOC_WLAN_NUMBER_OF_SECTIONS)) + return NULL; + if (wifi_mem_array[section].size < size) + return NULL; + return wifi_mem_array[section].mem_ptr; +} + +int __init incrediblec_init_wifi_mem(void) +{ + int i; + + for(i=0;( i < WLAN_SKB_BUF_NUM );i++) { + if (i < (WLAN_SKB_BUF_NUM/2)) + wlan_static_skb[i] = dev_alloc_skb(4096); + else + wlan_static_skb[i] = dev_alloc_skb(8192); + } + for(i=0;( i < PREALLOC_WLAN_NUMBER_OF_SECTIONS );i++) { + wifi_mem_array[i].mem_ptr = kmalloc(wifi_mem_array[i].size, + GFP_KERNEL); + if (wifi_mem_array[i].mem_ptr == NULL) + return -ENOMEM; + } + return 0; +} + +static struct resource incrediblec_wifi_resources[] = { + [0] = { + .name = "bcm4329_wlan_irq", + .start = MSM_GPIO_TO_INT(INCREDIBLEC_GPIO_WIFI_IRQ), + .end = MSM_GPIO_TO_INT(INCREDIBLEC_GPIO_WIFI_IRQ), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL, + }, +}; + +static struct wifi_platform_data incrediblec_wifi_control = { + .set_power = incrediblec_wifi_power, + .set_reset = incrediblec_wifi_reset, + .set_carddetect = incrediblec_wifi_set_carddetect, + .mem_prealloc = incrediblec_wifi_mem_prealloc, +}; + +static struct platform_device incrediblec_wifi_device = { + .name = "bcm4329_wlan", + .id = 1, + .num_resources = ARRAY_SIZE(incrediblec_wifi_resources), + .resource = incrediblec_wifi_resources, + .dev = { + .platform_data = &incrediblec_wifi_control, + }, +}; + +extern unsigned char *get_wifi_nvs_ram(void); + +static unsigned incrediblec_wifi_update_nvs(char *str) +{ +#define NVS_LEN_OFFSET 0x0C +#define NVS_DATA_OFFSET 0x40 + unsigned char *ptr; + unsigned len; + + if (!str) + return -EINVAL; + ptr = get_wifi_nvs_ram(); + /* Size in format LE assumed */ + memcpy(&len, ptr + NVS_LEN_OFFSET, sizeof(len)); + + /* the last bye in NVRAM is 0, trim it */ + if (ptr[NVS_DATA_OFFSET + len -1] == 0) + len -= 1; + + strcpy(ptr + NVS_DATA_OFFSET + len, str); + len += strlen(str); + memcpy(ptr + NVS_LEN_OFFSET, &len, sizeof(len)); + return 0; +} + +static int __init incrediblec_wifi_init(void) +{ + int ret; + + if (!machine_is_incrediblec()) + return 0; + + printk("%s: start\n", __func__); + incrediblec_wifi_update_nvs("sd_oobonly=1\r\n"); + incrediblec_init_wifi_mem(); + ret = platform_device_register(&incrediblec_wifi_device); + return ret; +} + +late_initcall(incrediblec_wifi_init); diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c new file mode 100644 index 0000000000000..4c1ceaab15104 --- /dev/null +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -0,0 +1,1611 @@ +/* linux/arch/arm/mach-msm/board-incrediblec.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation. + * Author: Dima Zavin + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +// #include +#include +#include +#include +#include +#include +#include +#include "board-incrediblec.h" +#include "devices.h" +#include "proc_comm.h" +#include "smd_private.h" +#if 1 /*allenou, bt for bcm, 2009/7/8 */ +#include +#endif +#include +#include +#include +#include +/* #include */ +#include +#include + +#define SMEM_SPINLOCK_I2C 6 +#define INCREDIBLEC_MICROP_VER 0x04 + +static uint opt_usb_h2w_sw; +module_param_named(usb_h2w_sw, opt_usb_h2w_sw, uint, 0); + +void msm_init_pmic_vibrator(void); +extern void __init incrediblec_audio_init(void); +#ifdef CONFIG_MICROP_COMMON +void __init incrediblec_microp_init(void); +#endif + +unsigned int engineerid; + +static struct htc_battery_platform_data htc_battery_pdev_data = { +/* .gpio_mbat_in = INCREDIBLEC_GPIO_MBAT_IN,*/ +/* .gpio_mchg_en_n = INCREDIBLEC_GPIO_MCHG_EN_N,*/ +/* .gpio_iset = INCREDIBLEC_GPIO_ISET,*/ + .guage_driver = GUAGE_MODEM, + .m2a_cable_detect = 1, + .charger = SWITCH_CHARGER, +}; + +static struct platform_device htc_battery_pdev = { + .name = "htc_battery", + .id = -1, + .dev = { + .platform_data = &htc_battery_pdev_data, + }, +}; +static int capella_cm3602_power(int pwr_device, uint8_t enable); +/*XA, XB*/ +static struct microp_function_config microp_functions[] = { + { + .name = "microp_intrrupt", + .category = MICROP_FUNCTION_INTR, + }, + { + .name = "reset-int", + .category = MICROP_FUNCTION_RESET_INT, + .int_pin = 1 << 8, + }, + { + .name = "oj", + .category = MICROP_FUNCTION_OJ, + .int_pin = 1 << 12, + }, + { + .name = "proximity", + .category = MICROP_FUNCTION_P, + .int_pin = 1 << 11, + .mask_r = {0x00, 0x00, 0x10}, + .mask_w = {0x00, 0x00, 0x04}, + }, +}; + +/*For XC: Change ALS chip from CM3602 to CM3605*/ +static struct microp_function_config microp_functions_1[] = { + { + .name = "remote-key", + .category = MICROP_FUNCTION_REMOTEKEY, + .levels = {0, 33, 50, 110, 160, 220}, + .channel = 1, + .int_pin = 1 << 5, + }, + { + .name = "microp_intrrupt", + .category = MICROP_FUNCTION_INTR, + }, + { + .name = "reset-int", + .category = MICROP_FUNCTION_RESET_INT, + .int_pin = 1 << 8, + }, + { + .name = "oj", + .category = MICROP_FUNCTION_OJ, + .int_pin = 1 << 12, + }, + { + .name = "proximity", + .category = MICROP_FUNCTION_P, + .int_pin = 1 << 11, + .mask_r = {0x00, 0x00, 0x10}, + .mask_w = {0x00, 0x00, 0x04}, + }, +}; + +static struct microp_function_config microp_lightsensor = { + .name = "light_sensor", + .category = MICROP_FUNCTION_LSENSOR, + .levels = { 0, 11, 16, 22, 75, 209, 362, 488, 560, 0x3FF }, + .channel = 3, + .int_pin = 1 << 9, + .golden_adc = 0xD2, + .mask_w = {0x00, 0x00, 0x04}, + .ls_power = capella_cm3602_power, +}; + +static struct lightsensor_platform_data lightsensor_data = { + .config = µp_lightsensor, + .irq = MSM_uP_TO_INT(9), +}; + +static struct microp_led_config led_config[] = { + { + .name = "amber", + .type = LED_RGB, + }, + { + .name = "green", + .type = LED_RGB, + }, +}; + +static struct microp_led_platform_data microp_leds_data = { + .num_leds = ARRAY_SIZE(led_config), + .led_config = led_config, +}; + +static struct bma150_platform_data incrediblec_g_sensor_pdata = { + .microp_new_cmd = 1, +}; + +/* Proximity Sensor (Capella_CM3602)*/ + +static int __capella_cm3602_power(int on) +{ + uint8_t data[3], addr; + int ret; + + printk(KERN_DEBUG "%s: Turn the capella_cm3602 power %s\n", + __func__, (on) ? "on" : "off"); + if (on) + gpio_direction_output(INCREDIBLEC_GPIO_PROXIMITY_EN_N, 1); + + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x04; + addr = on ? MICROP_I2C_WCMD_GPO_LED_STATUS_EN : + MICROP_I2C_WCMD_GPO_LED_STATUS_DIS; + ret = microp_i2c_write(addr, data, 3); + if (ret < 0) + pr_err("%s: %s capella power failed\n", + __func__, (on ? "enable" : "disable")); + + if (!on) + gpio_direction_output(INCREDIBLEC_GPIO_PROXIMITY_EN_N, 0); + + return ret; +} + +static DEFINE_MUTEX(capella_cm3602_lock); +static unsigned int als_power_control; + +static int capella_cm3602_power(int pwr_device, uint8_t enable) +{ + unsigned int old_status = 0; + int ret = 0, on = 0; + mutex_lock(&capella_cm3602_lock); + + old_status = als_power_control; + if (enable) + als_power_control |= pwr_device; + else + als_power_control &= ~pwr_device; + + on = als_power_control ? 1 : 0; + if (old_status == 0 && on) + ret = __capella_cm3602_power(1); + else if (!on) + ret = __capella_cm3602_power(0); + + mutex_unlock(&capella_cm3602_lock); + return ret; +} + +static struct capella_cm3602_platform_data capella_cm3602_pdata = { + .power = capella_cm3602_power, + .p_en = INCREDIBLEC_GPIO_PROXIMITY_EN_N, + .p_out = MSM_uP_TO_INT(11), +}; +/* End Proximity Sensor (Capella_CM3602)*/ + +static struct htc_headset_microp_platform_data htc_headset_microp_data = { + .remote_int = 1 << 5, + .remote_irq = MSM_uP_TO_INT(5), + .remote_enable_pin = NULL, + .adc_channel = 0x01, + .adc_remote = {0, 33, 50, 110, 160, 220}, +}; + +static struct platform_device microp_devices[] = { + { + .name = "lightsensor_microp", + .dev = { + .platform_data = &lightsensor_data, + }, + }, + { + .name = "leds-microp", + .id = -1, + .dev = { + .platform_data = µp_leds_data, + }, + }, + { + .name = BMA150_G_SENSOR_NAME, + .dev = { + .platform_data = &incrediblec_g_sensor_pdata, + }, + }, + { + .name = "incrediblec_proximity", + .id = -1, + .dev = { + .platform_data = &capella_cm3602_pdata, + }, + }, + { + .name = "HTC_HEADSET_MICROP", + .id = -1, + .dev = { + .platform_data = &htc_headset_microp_data, + }, + }, +}; + +static struct microp_i2c_platform_data microp_data = { + .num_functions = ARRAY_SIZE(microp_functions), + .microp_function = microp_functions, + .num_devices = ARRAY_SIZE(microp_devices), + .microp_devices = microp_devices, + .gpio_reset = INCREDIBLEC_GPIO_UP_RESET_N, + .microp_ls_on = LS_PWR_ON | PS_PWR_ON, + .spi_devices = SPI_OJ | SPI_GSENSOR, +}; + +static struct gpio_led incrediblec_led_list[] = { + { + .name = "button-backlight", + .gpio = INCREDIBLEC_AP_KEY_LED_EN, + .active_low = 0, + }, +}; + +static struct gpio_led_platform_data incrediblec_leds_data = { + .num_leds = ARRAY_SIZE(incrediblec_led_list), + .leds = incrediblec_led_list, +}; + +static struct platform_device incrediblec_leds = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &incrediblec_leds_data, + }, +}; + +static int incrediblec_phy_init_seq[] = { 0x1D, 0x0D, 0x1D, 0x10, -1 }; + +extern void msm_hsusb_8x50_phy_reset(void); + +static struct msm_hsusb_platform_data msm_hsusb_pdata = { + .phy_init_seq = incrediblec_phy_init_seq, + .phy_reset = msm_hsusb_8x50_phy_reset, + .usb_id_pin_gpio = INCREDIBLEC_GPIO_USB_ID_PIN, + .accessory_detect = 1, /* detect by ID pin gpio */ +}; + +static char *usb_functions_ums[] = { + "usb_mass_storage", +}; + +static char *usb_functions_ums_adb[] = { + "usb_mass_storage", + "adb", +}; + +static char *usb_functions_rndis[] = { + "rndis", +}; + +static char *usb_functions_rndis_adb[] = { + "rndis", + "adb", +}; + +#ifdef CONFIG_USB_ANDROID_DIAG +static char *usb_functions_adb_diag[] = { + "usb_mass_storage", + "adb", + "diag", +}; +#endif + +static char *usb_functions_all[] = { +#ifdef CONFIG_USB_ANDROID_RNDIS + "rndis", +#endif + "usb_mass_storage", + "adb", +#ifdef CONFIG_USB_ANDROID_ACM + "acm", +#endif +#ifdef CONFIG_USB_ANDROID_DIAG + "diag", +#endif +}; + +static struct android_usb_product usb_products[] = { + { + .product_id = 0x0ff9, + .num_functions = ARRAY_SIZE(usb_functions_ums), + .functions = usb_functions_ums, + }, + { + .product_id = 0x0c8d, + .num_functions = ARRAY_SIZE(usb_functions_ums_adb), + .functions = usb_functions_ums_adb, + }, + { + .product_id = 0x0c03, + .num_functions = ARRAY_SIZE(usb_functions_rndis), + .functions = usb_functions_rndis, + }, + { + .product_id = 0x0c04, + .num_functions = ARRAY_SIZE(usb_functions_rndis_adb), + .functions = usb_functions_rndis_adb, + }, +#ifdef CONFIG_USB_ANDROID_DIAG + { + .product_id = 0x0c07, + .num_functions = ARRAY_SIZE(usb_functions_adb_diag), + .functions = usb_functions_adb_diag, + }, +#endif +}; + +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 3, + .vendor = "HTC", + .product = "Incredible", + .release = 0x0100, +}; + +static struct platform_device usb_mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &mass_storage_pdata, + }, +}; + +#ifdef CONFIG_USB_ANDROID_RNDIS +static struct usb_ether_platform_data rndis_pdata = { + /* ethaddr is filled by board_serialno_setup */ + .vendorID = 0x18d1, + .vendorDescr = "Google, Inc.", +}; + +static struct platform_device rndis_device = { + .name = "rndis", + .id = -1, + .dev = { + .platform_data = &rndis_pdata, + }, +}; +#endif + +static struct android_usb_platform_data android_usb_pdata = { + .vendor_id = 0x0bb4, + .product_id = 0x0c9e, + .version = 0x0100, + .product_name = "Android Phone", + .manufacturer_name = "HTC", + .num_products = ARRAY_SIZE(usb_products), + .products = usb_products, + .num_functions = ARRAY_SIZE(usb_functions_all), + .functions = usb_functions_all, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; + + +static struct platform_device incrediblec_rfkill = { + .name = "incrediblec_rfkill", + .id = -1, +}; + +static struct resource qsd_spi_resources[] = { + { + .name = "spi_irq_in", + .start = INT_SPI_INPUT, + .end = INT_SPI_INPUT, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spi_irq_out", + .start = INT_SPI_OUTPUT, + .end = INT_SPI_OUTPUT, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spi_irq_err", + .start = INT_SPI_ERROR, + .end = INT_SPI_ERROR, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spi_base", + .start = 0xA1200000, + .end = 0xA1200000 + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "spi_clk", + .start = 17, + .end = 1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spi_mosi", + .start = 18, + .end = 1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spi_miso", + .start = 19, + .end = 1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spi_cs0", + .start = 20, + .end = 1, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spi_pwr", + .start = 21, + .end = 0, + .flags = IORESOURCE_IRQ, + }, + { + .name = "spi_irq_cs0", + .start = 22, + .end = 0, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device qsd_device_spi = { + .name = "spi_qsd", + .id = 0, + .num_resources = ARRAY_SIZE(qsd_spi_resources), + .resource = qsd_spi_resources, +}; + +static struct resource msm_kgsl_resources[] = { + { + .name = "kgsl_reg_memory", + .start = MSM_GPU_REG_PHYS, + .end = MSM_GPU_REG_PHYS + MSM_GPU_REG_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "kgsl_phys_memory", + .start = MSM_GPU_MEM_BASE, + .end = MSM_GPU_MEM_BASE + MSM_GPU_MEM_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_GRAPHICS, + .end = INT_GRAPHICS, + .flags = IORESOURCE_IRQ, + }, +}; + +#define PWR_RAIL_GRP_CLK 8 +static int incrediblec_kgsl_power_rail_mode(int follow_clk) +{ + int mode = follow_clk ? 0 : 1; + int rail_id = PWR_RAIL_GRP_CLK; + + return msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &rail_id, &mode); +} + +static int incrediblec_kgsl_power(bool on) +{ + int cmd; + int rail_id = PWR_RAIL_GRP_CLK; + + cmd = on ? PCOM_CLKCTL_RPC_RAIL_ENABLE : PCOM_CLKCTL_RPC_RAIL_DISABLE; + return msm_proc_comm(cmd, &rail_id, NULL); +} + +static struct platform_device msm_kgsl_device = { + .name = "kgsl", + .id = -1, + .resource = msm_kgsl_resources, + .num_resources = ARRAY_SIZE(msm_kgsl_resources), +}; + +static struct android_pmem_platform_data mdp_pmem_pdata = { + .name = "pmem", + .start = MSM_PMEM_MDP_BASE, + .size = MSM_PMEM_MDP_SIZE, + .no_allocator = 0, + .cached = 1, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .start = MSM_PMEM_ADSP_BASE, + .size = MSM_PMEM_ADSP_SIZE, + .no_allocator = 0, + .cached = 1, +}; + +#ifdef CONFIG_720P_CAMERA +static struct android_pmem_platform_data android_pmem_venc_pdata = { + .name = "pmem_venc", + .start = MSM_PMEM_VENC_BASE, + .size = MSM_PMEM_VENC_SIZE, + .no_allocator = 0, + .cached = 1, +}; +#else +static struct android_pmem_platform_data android_pmem_camera_pdata = { + .name = "pmem_camera", + .start = MSM_PMEM_CAMERA_BASE, + .size = MSM_PMEM_CAMERA_SIZE, + .no_allocator = 1, + .cached = 1, +}; +#endif + +static struct platform_device android_pmem_mdp_device = { + .name = "android_pmem", + .id = 0, + .dev = { + .platform_data = &mdp_pmem_pdata + }, +}; + +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 4, + .dev = { + .platform_data = &android_pmem_adsp_pdata, + }, +}; + +#ifdef CONFIG_720P_CAMERA +static struct platform_device android_pmem_venc_device = { + .name = "android_pmem", + .id = 5, + .dev = { + .platform_data = &android_pmem_venc_pdata, + }, +}; +#else +static struct platform_device android_pmem_camera_device = { + .name = "android_pmem", + .id = 5, + .dev = { + .platform_data = &android_pmem_camera_pdata, + }, +}; + +#endif + +static struct resource ram_console_resources[] = { + { + .start = MSM_RAM_CONSOLE_BASE, + .end = MSM_RAM_CONSOLE_BASE + MSM_RAM_CONSOLE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device ram_console_device = { + .name = "ram_console", + .id = -1, + .num_resources = ARRAY_SIZE(ram_console_resources), + .resource = ram_console_resources, +}; + +static int incrediblec_atmel_ts_power(int on) +{ + printk(KERN_INFO "incrediblec_atmel_ts_power(%d)\n", on); + if (on) { + gpio_set_value(INCREDIBLEC_GPIO_TP_RST, 0); + msleep(5); + gpio_set_value(INCREDIBLEC_GPIO_TP_EN, 1); + msleep(5); + gpio_set_value(INCREDIBLEC_GPIO_TP_RST, 1); + msleep(40); + } else { + gpio_set_value(INCREDIBLEC_GPIO_TP_EN, 0); + msleep(2); + } + return 0; +} + +struct atmel_i2c_platform_data incrediblec_atmel_ts_data[] = { + { + .version = 0x016, + .abs_x_min = 1, + .abs_x_max = 1023, + .abs_y_min = 2, + .abs_y_max = 966, + .abs_pressure_min = 0, + .abs_pressure_max = 255, + .abs_width_min = 0, + .abs_width_max = 20, + .gpio_irq = INCREDIBLEC_GPIO_TP_INT_N, + .power = incrediblec_atmel_ts_power, + .config_T6 = {0, 0, 0, 0, 0, 0}, + .config_T7 = {50, 15, 25}, + .config_T8 = {10, 0, 20, 10, 0, 0, 5, 15}, + .config_T9 = {139, 0, 0, 18, 12, 0, 16, 38, 3, 7, 0, 5, 2, 15, 2, 10, 25, 5, 0, 0, 0, 0, 0, 0, 0, 0, 159, 47, 149, 81, 40}, + .config_T15 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T19 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T20 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T22 = {15, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 0, 7, 18, 25, 30, 0}, + .config_T23 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T24 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T25 = {3, 0, 200, 50, 64, 31, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T27 = {0, 0, 0, 0, 0, 0, 0}, + .config_T28 = {0, 0, 2, 4, 8, 60}, + .object_crc = {0xDB, 0xBF, 0x60}, + .cable_config = {35, 30, 8, 16}, + .GCAF_level = {20, 24, 28, 40, 63}, + .filter_level = {15, 60, 963, 1008}, + }, + { + .version = 0x015, + .abs_x_min = 13, + .abs_x_max = 1009, + .abs_y_min = 15, + .abs_y_max = 960, + .abs_pressure_min = 0, + .abs_pressure_max = 255, + .abs_width_min = 0, + .abs_width_max = 20, + .gpio_irq = INCREDIBLEC_GPIO_TP_INT_N, + .power = incrediblec_atmel_ts_power, + .config_T6 = {0, 0, 0, 0, 0, 0}, + .config_T7 = {50, 15, 25}, + .config_T8 = {12, 0, 20, 20, 0, 0, 20, 0}, + .config_T9 = {139, 0, 0, 18, 12, 0, 32, 40, 2, 7, 0, 5, 2, 0, 2, 10, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 159, 47, 149, 81}, + .config_T15 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T19 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T20 = {7, 0, 0, 0, 0, 0, 0, 30, 20, 4, 15, 5}, + .config_T22 = {7, 0, 0, 25, 0, -25, 255, 4, 50, 0, 1, 10, 15, 20, 25, 30, 4}, + .config_T23 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T24 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T25 = {3, 0, 200, 50, 64, 31, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T27 = {0, 0, 0, 0, 0, 0, 0}, + .config_T28 = {0, 0, 2, 4, 8, 60}, + .object_crc = {0x19, 0x87, 0x7E}, + }, + { + .version = 0x014, + .abs_x_min = 13, + .abs_x_max = 1009, + .abs_y_min = 15, + .abs_y_max = 960, + .abs_pressure_min = 0, + .abs_pressure_max = 255, + .abs_width_min = 0, + .abs_width_max = 20, + .gpio_irq = INCREDIBLEC_GPIO_TP_INT_N, + .power = incrediblec_atmel_ts_power, + .config_T6 = {0, 0, 0, 0, 0, 0}, + .config_T7 = {50, 15, 25}, + .config_T8 = {12, 0, 20, 20, 0, 0, 10, 15}, + .config_T9 = {3, 0, 0, 18, 12, 0, 48, 45, 2, 7, 0, 0, 0, 0, 2, 10, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 47, 143, 81}, + .config_T15 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T19 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T20 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T22 = {5, 0, 0, 25, 0, -25, 255, 4, 50, 0, 1, 10, 15, 20, 25, 30, 4}, + .config_T23 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T24 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T25 = {3, 0, 200, 50, 64, 31, 0, 0, 0, 0, 0, 0, 0, 0}, + .config_T27 = {0, 0, 0, 0, 0, 0, 0}, + .config_T28 = {0, 0, 2, 4, 8, 60}, + } +}; + +static struct regulator_consumer_supply tps65023_dcdc1_supplies[] = { + { + .supply = "acpu_vcore", + }, +}; + +static struct regulator_init_data tps65023_data[5] = { + { + .constraints = { + .name = "dcdc1", /* VREG_MSMC2_1V29 */ + .min_uV = 925000, + .max_uV = 1350000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + }, + .consumer_supplies = tps65023_dcdc1_supplies, + .num_consumer_supplies = ARRAY_SIZE(tps65023_dcdc1_supplies), + }, + /* dummy values for unused regulators to not crash driver: */ + { + .constraints = { + .name = "dcdc2", /* VREG_MSMC1_1V26 */ + .min_uV = 1260000, + .max_uV = 1260000, + }, + }, + { + .constraints = { + .name = "dcdc3", /* unused */ + .min_uV = 800000, + .max_uV = 3300000, + }, + }, + { + .constraints = { + .name = "ldo1", /* unused */ + .min_uV = 1000000, + .max_uV = 3150000, + }, + }, + { + .constraints = { + .name = "ldo2", /* V_USBPHY_3V3 */ + .min_uV = 3300000, + .max_uV = 3300000, + }, + }, +}; + +static void set_h2w_dat(int n) +{ + gpio_set_value(INCREDIBLEC_GPIO_H2W_DATA, n); +} + +static void set_h2w_clk(int n) +{ + gpio_set_value(INCREDIBLEC_GPIO_H2W_CLK, n); +} + +static int get_h2w_dat(void) +{ + return gpio_get_value(INCREDIBLEC_GPIO_H2W_DATA); +} + +static int get_h2w_clk(void) +{ + return gpio_get_value(INCREDIBLEC_GPIO_H2W_CLK); +} + +static void h2w_dev_power_on(int on) +{ + printk(KERN_INFO "Not support H2W power\n"); +} + +/* default TX,RX to GPI */ +static uint32_t uart3_off_gpi_table[] = { + /* RX, H2W DATA */ + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_H2W_DATA, 0, + GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), + /* TX, H2W CLK */ + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_H2W_CLK, 0, + GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), +}; + +/* set TX,RX to GPO */ +static uint32_t uart3_off_gpo_table[] = { + /* RX, H2W DATA */ + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_H2W_DATA, 0, + GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + /* TX, H2W CLK */ + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_H2W_CLK, 0, + GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), +}; + +static void set_h2w_dat_dir(int n) +{ +#if 0 + if (n == 0) /* input */ + gpio_direction_input(INCREDIBLEC_GPIO_H2W_DATA); + else + gpio_configure(INCREDIBLEC_GPIO_H2W_DATA, GPIOF_DRIVE_OUTPUT); +#else + if (n == 0) /* input */ + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart3_off_gpi_table + 0, 0); + else + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart3_off_gpo_table + 0, 0); +#endif +} + +static void set_h2w_clk_dir(int n) +{ +#if 0 + if (n == 0) /* input */ + gpio_direction_input(INCREDIBLEC_GPIO_H2W_CLK); + else + gpio_configure(INCREDIBLEC_GPIO_H2W_CLK, GPIOF_DRIVE_OUTPUT); +#else + if (n == 0) /* input */ + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart3_off_gpi_table + 1, 0); + else + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart3_off_gpo_table + 1, 0); +#endif +} + + +static void incrediblec_config_serial_debug_gpios(void); + +static void h2w_configure(int route) +{ + printk(KERN_INFO "H2W route = %d \n", route); + switch (route) { + case H2W_UART3: + incrediblec_config_serial_debug_gpios(); + printk(KERN_INFO "H2W -> UART3\n"); + break; + case H2W_GPIO: + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart3_off_gpi_table + 0, 0); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart3_off_gpi_table + 1, 0); + printk(KERN_INFO "H2W -> GPIO\n"); + break; + } +} + +static struct htc_headset_mgr_platform_data htc_headset_mgr_data = { +}; + +static struct platform_device htc_headset_mgr = { + .name = "HTC_HEADSET_MGR", + .id = -1, + .dev = { + .platform_data = &htc_headset_mgr_data, + }, +}; + +static struct htc_headset_gpio_platform_data htc_headset_gpio_data = { + .hpin_gpio = INCREDIBLEC_GPIO_35MM_HEADSET_DET, + .key_enable_gpio = NULL, + .mic_select_gpio = NULL, +}; + +static struct platform_device htc_headset_gpio = { + .name = "HTC_HEADSET_GPIO", + .id = -1, + .dev = { + .platform_data = &htc_headset_gpio_data, + }, +}; + +static struct akm8973_platform_data compass_platform_data = { + .layouts = INCREDIBLEC_LAYOUTS, + .project_name = INCREDIBLEC_PROJECT_NAME, + .reset = INCREDIBLEC_GPIO_COMPASS_RST_N, + .intr = INCREDIBLEC_GPIO_COMPASS_INT_N, +}; + +static struct tpa6130_platform_data headset_amp_platform_data = { + .enable_rpc_server = 0, +}; + +static struct i2c_board_info i2c_devices[] = { + { + I2C_BOARD_INFO(ATMEL_QT602240_NAME, 0x94 >> 1), + .platform_data = &incrediblec_atmel_ts_data, + .irq = MSM_GPIO_TO_INT(INCREDIBLEC_GPIO_TP_INT_N) + }, + { + I2C_BOARD_INFO(MICROP_I2C_NAME, 0xCC >> 1), + .platform_data = µp_data, + .irq = MSM_GPIO_TO_INT(INCREDIBLEC_GPIO_UP_INT_N) + }, + { + I2C_BOARD_INFO("ds2482", 0x30 >> 1), + /*.platform_data = µp_data,*/ + /*.irq = MSM_GPIO_TO_INT(PASSION_GPIO_UP_INT_N)*/ + }, + { + I2C_BOARD_INFO("smb329", 0x6E >> 1), + }, + { + I2C_BOARD_INFO("akm8973", 0x1C), + .platform_data = &compass_platform_data, + .irq = MSM_GPIO_TO_INT(INCREDIBLEC_GPIO_COMPASS_INT_N), + }, +#ifdef CONFIG_MSM_CAMERA +#ifdef CONFIG_OV8810 + { + I2C_BOARD_INFO("ov8810", 0x6C >> 1), + }, +#endif +#endif/*CONIFIG_MSM_CAMERA*/ + + { + I2C_BOARD_INFO(TPA6130_I2C_NAME, 0xC0 >> 1), + .platform_data = &headset_amp_platform_data, + }, + { + I2C_BOARD_INFO("tps65023", 0x48), + .platform_data = tps65023_data, + }, +}; + +static uint32_t camera_off_gpio_table[] = { + +#if 0 /* CAMERA OFF*/ + PCOM_GPIO_CFG(0, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(1, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(2, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* PCLK */ + PCOM_GPIO_CFG(13, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* HSYNC */ + PCOM_GPIO_CFG(14, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* VSYNC */ + PCOM_GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* MCLK */ +#endif + /* CAMERA SUSPEND*/ + PCOM_GPIO_CFG(0, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(1, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(2, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* PCLK */ + PCOM_GPIO_CFG(13, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* HSYNC */ + PCOM_GPIO_CFG(14, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* VSYNC */ + PCOM_GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* MCLK */ + PCOM_GPIO_CFG(99, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), /* CAM1_RST */ + PCOM_GPIO_CFG(INCREDIBLEC_CAM_PWD, + 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), /* CAM1_PWD */ +}; + +static uint32_t camera_on_gpio_table[] = { + /* CAMERA ON */ + PCOM_GPIO_CFG(0, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT0 */ + PCOM_GPIO_CFG(1, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT1 */ + PCOM_GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_16MA), /* PCLK */ + PCOM_GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* HSYNC */ + PCOM_GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* VSYNC */ + PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_16MA), /* MCLK */ +}; + +static void config_camera_on_gpios(void) +{ + config_gpio_table(camera_on_gpio_table, + ARRAY_SIZE(camera_on_gpio_table)); +} + +static void config_camera_off_gpios(void) +{ + config_gpio_table(camera_off_gpio_table, + ARRAY_SIZE(camera_off_gpio_table)); +} + +static struct resource msm_camera_resources[] = { + { + .start = MSM_VFE_PHYS, + .end = MSM_VFE_PHYS + MSM_VFE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_VFE, + INT_VFE, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct msm_camera_device_platform_data msm_camera_device_data = { + .camera_gpio_on = config_camera_on_gpios, + .camera_gpio_off = config_camera_off_gpios, + .ioext.mdcphy = MSM_MDC_PHYS, + .ioext.mdcsz = MSM_MDC_SIZE, + .ioext.appphy = MSM_CLK_CTL_PHYS, + .ioext.appsz = MSM_CLK_CTL_SIZE, +}; + +static struct camera_flash_cfg msm_camera_sensor_flash_cfg = { + .camera_flash = flashlight_control, + .num_flash_levels = FLASHLIGHT_NUM, + .low_temp_limit = 10, + .low_cap_limit = 15, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_ov8810_data = { + .sensor_name = "ov8810", + .sensor_reset = INCREDIBLEC_CAM_RST, /* CAM1_RST */ + .sensor_pwd = INCREDIBLEC_CAM_PWD, /* CAM1_PWDN, enabled in a9 */ + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .waked_up = 0, + .need_suspend = 0, + .flash_cfg = &msm_camera_sensor_flash_cfg, +}; + +static struct platform_device msm_camera_sensor_ov8810 = { + .name = "msm_camera_ov8810", + .dev = { + .platform_data = &msm_camera_sensor_ov8810_data, + }, +}; + +static void config_incrediblec_flashlight_gpios(void) +{ + static uint32_t flashlight_gpio_table[] = { + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_FLASHLIGHT_TORCH, 0, + GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_FLASHLIGHT_FLASH, 0, + GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_FLASHLIGHT_FLASH_ADJ, 0, + GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + }; + + config_gpio_table(flashlight_gpio_table, + ARRAY_SIZE(flashlight_gpio_table)); +} + +static struct flashlight_platform_data incrediblec_flashlight_data = { + .gpio_init = config_incrediblec_flashlight_gpios, + .torch = INCREDIBLEC_GPIO_FLASHLIGHT_TORCH, + .flash = INCREDIBLEC_GPIO_FLASHLIGHT_FLASH, + .flash_adj = INCREDIBLEC_GPIO_FLASHLIGHT_FLASH_ADJ, + .flash_duration_ms = 600, + .led_count = 1, +}; + +static struct platform_device incrediblec_flashlight_device = { + .name = "flashlight", + .dev = { + .platform_data = &incrediblec_flashlight_data, + }, +}; + +static void curcial_oj_shutdown(int enable) +{ + uint8_t cmd[3]; + memset(cmd, 0, sizeof(uint8_t)*3); + + cmd[2] = 0x80; + if (enable) + microp_i2c_write(0x91, cmd, 3); + else + microp_i2c_write(0x90, cmd, 3); +} + +static int curcial_oj_poweron(int on) +{ + struct vreg *oj_power = vreg_get(0, "synt"); + if (IS_ERR(oj_power)) { + printk(KERN_ERR "%s: Error power domain\n", __func__); + return 0; + } + + if (on) { + vreg_set_level(oj_power, 2750); + vreg_enable(oj_power); + } else + vreg_disable(oj_power); + + printk(KERN_INFO "%s: OJ power enable(%d)\n", __func__, on); + return 1; +}; +static void curcial_oj_adjust_xy(uint8_t *data, int16_t *mSumDeltaX, int16_t *mSumDeltaY) +{ + int8_t deltaX; + int8_t deltaY; + + + if (data[2] == 0x80) + data[2] = 0x81; + if (data[1] == 0x80) + data[1] = 0x81; + if (0) { + deltaX = (1)*((int8_t) data[2]); /*X=2*/ + deltaY = (1)*((int8_t) data[1]); /*Y=1*/ + } else { + deltaX = (1)*((int8_t) data[1]); + deltaY = (1)*((int8_t) data[2]); + } + *mSumDeltaX += -((int16_t)deltaX); + *mSumDeltaY += -((int16_t)deltaY); +} +static struct curcial_oj_platform_data incrediblec_oj_data = { + .oj_poweron = curcial_oj_poweron, + .oj_shutdown = curcial_oj_shutdown, + .oj_adjust_xy = curcial_oj_adjust_xy, + .microp_version = INCREDIBLEC_MICROP_VER, + .debugflag = 0, + .mdelay_time = 0, + .normal_th = 8, + .xy_ratio = 15, + .interval = 20, + .swap = true, + .ap_code = false, + .x = 1, + .y = 1, + .share_power = true, + .Xsteps = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9}, + .Ysteps = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9}, + .sht_tbl = {0, 2000, 2250, 2500, 2750, 3000}, + .pxsum_tbl = {0, 0, 40, 50, 60, 70}, + .degree = 6, + .irq = MSM_uP_TO_INT(12), +}; + +static struct platform_device incrediblec_oj = { + .name = CURCIAL_OJ_NAME, + .id = -1, + .dev = { + .platform_data = &incrediblec_oj_data, + } +}; + +static struct resource resources_msm_fb[] = { + { + .start = MSM_FB_BASE, + .end = MSM_FB_BASE + MSM_FB_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { + .rx_wakeup_irq = -1, + .inject_rx_on_wakeup = 0, + .exit_lpm_cb = bcm_bt_lpm_exit_lpm_locked, +}; + +static struct bcm_bt_lpm_platform_data bcm_bt_lpm_pdata = { + .gpio_wake = INCREDIBLEC_GPIO_BT_CHIP_WAKE, + .gpio_host_wake = INCREDIBLEC_GPIO_BT_HOST_WAKE, + .request_clock_off_locked = msm_hs_request_clock_off_locked, + .request_clock_on_locked = msm_hs_request_clock_on_locked, +}; + +struct platform_device bcm_bt_lpm_device = { + .name = "bcm_bt_lpm", + .id = 0, + .dev = { + .platform_data = &bcm_bt_lpm_pdata, + }, +}; + +static struct platform_device *devices[] __initdata = { + &msm_device_uart1, + &bcm_bt_lpm_device, + &msm_device_uart_dm1, + &htc_battery_pdev, + &htc_headset_mgr, + &htc_headset_gpio, + &ram_console_device, + &incrediblec_rfkill, + &msm_device_smd, + &msm_device_nand, + &msm_device_hsusb, + &usb_mass_storage_device, +#ifdef CONFIG_USB_ANDROID_RNDIS + &rndis_device, +#endif + &android_usb_device, + &android_pmem_mdp_device, + &android_pmem_adsp_device, +#ifdef CONFIG_720P_CAMERA + &android_pmem_venc_device, +#else + &android_pmem_camera_device, +#endif + &msm_camera_sensor_ov8810, + &msm_kgsl_device, + &msm_device_i2c, + &incrediblec_flashlight_device, + &incrediblec_leds, + +#if defined(CONFIG_SPI_QSD) + &qsd_device_spi, +#endif + &incrediblec_oj, +}; + +static uint32_t bt_gpio_table_rev_CX[] = { + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_BT_UART1_RTS, 2, GPIO_OUTPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_BT_UART1_CTS, 2, GPIO_INPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_BT_UART1_RX, 2, GPIO_INPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_BT_UART1_TX, 2, GPIO_OUTPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_BT_RESET_N, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_4MA), + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_BT_SHUTDOWN_N, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_4MA), + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_BT_CHIP_WAKE, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_4MA), + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_BT_HOST_WAKE, 0, GPIO_INPUT, + GPIO_PULL_DOWN, GPIO_4MA), +}; + + +static uint32_t usb_phy_3v3_table[] = { + PCOM_GPIO_CFG(INCREDIBLEC_USB_PHY_3V3_ENABLE, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA) +}; + +static uint32_t usb_ID_PIN_table[] = { + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_USB_ID_PIN, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), +}; + +static uint32_t incrediblec_serial_debug_table[] = { + /* RX */ + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_UART3_RX, 3, GPIO_INPUT, GPIO_NO_PULL, + GPIO_4MA), + /* TX */ + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_UART3_TX, 3, GPIO_OUTPUT, GPIO_NO_PULL, + GPIO_4MA), +}; + +static uint32_t incrediblec_uart_gpio_table[] = { + /* RX */ + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_UART3_RX, 3, GPIO_INPUT, GPIO_NO_PULL, + GPIO_4MA), + /* TX */ + PCOM_GPIO_CFG(INCREDIBLEC_GPIO_UART3_TX, 3, GPIO_INPUT, GPIO_NO_PULL, + GPIO_4MA), +}; + +static void incrediblec_config_serial_debug_gpios(void) +{ + config_gpio_table(incrediblec_serial_debug_table, + ARRAY_SIZE(incrediblec_serial_debug_table)); +} + +static void incrediblec_config_uart_gpios(void) +{ + config_gpio_table(incrediblec_uart_gpio_table, + ARRAY_SIZE(incrediblec_uart_gpio_table)); +} + +static struct msm_i2c_device_platform_data msm_i2c_pdata = { + .i2c_clock = 100000, + .clock_strength = GPIO_8MA, + .data_strength = GPIO_8MA, +}; + +static void __init msm_device_i2c_init(void) +{ + msm_i2c_gpio_init(); + msm_device_i2c.dev.platform_data = &msm_i2c_pdata; +} + + +#define ATAG_BDADDR 0x43294329 +#define ATAG_BDADDR_SIZE 4 +#define BDADDR_STR_SIZE 18 + +static char bdaddr[BDADDR_STR_SIZE]; + +module_param_string(bdaddr, bdaddr, sizeof(bdaddr), 0400); +MODULE_PARM_DESC(bdaddr, "bluetooth address"); + +static int __init parse_tag_bdaddr(const struct tag *tag) +{ + unsigned char *b = (unsigned char *)&tag->u; + + if (tag->hdr.size != ATAG_BDADDR_SIZE) + return -EINVAL; + + snprintf(bdaddr, BDADDR_STR_SIZE, "%02X:%02X:%02X:%02X:%02X:%02X", + b[0], b[1], b[2], b[3], b[4], b[5]); + + return 0; +} + +__tagtable(ATAG_BDADDR, parse_tag_bdaddr); + +static struct msm_acpu_clock_platform_data incrediblec_clock_data = { + .acpu_switch_time_us = 20, + .max_speed_delta_khz = 256000, + .vdd_switch_time_us = 62, + .power_collapse_khz = 245000, + .wait_for_irq_khz = 245000, +}; + +static unsigned incrediblec_perf_acpu_table[] = { + 245000000, + 576000000, + 998400000, +}; + +static struct perflock_platform_data incrediblec_perflock_data = { + .perf_acpu_table = incrediblec_perf_acpu_table, + .table_size = ARRAY_SIZE(incrediblec_perf_acpu_table), +}; + +int incrediblec_init_mmc(int sysrev); + +static int OJ_BMA_power(void) +{ + int ret; + struct vreg *vreg = vreg_get(0, "synt"); + + if (!vreg) { + printk(KERN_ERR "%s: vreg error\n", __func__); + return -EIO; + } + ret = vreg_set_level(vreg, 2850); + + ret = vreg_enable(vreg); + if (ret < 0) + printk(KERN_ERR "%s: vreg enable failed\n", __func__); + + return 0; +} + +unsigned int incrediblec_get_engineerid(void) +{ + return engineerid; +} + +static ssize_t incrediblec_virtual_keys_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + if (engineerid > 1 && system_rev > 1) { + /* center: x: home: 45, menu: 152, back: 318, search 422, y: 830 */ + return sprintf(buf, + __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":47:830:74:50" + ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":155:830:80:50" + ":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":337:830:90:50" + ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":434:830:60:50" + "\n"); + } else { + /* center: x: home: 50, menu: 184, back: 315, search 435, y: 830*/ + return sprintf(buf, + __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":50:830:98:50" + ":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":184:830:120:50" + ":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":315:830:100:50" + ":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":435:830:88:50" + "\n"); + } + +} + +static struct kobj_attribute incrediblec_virtual_keys_attr = { + .attr = { + .name = "virtualkeys.atmel-touchscreen", + .mode = S_IRUGO, + }, + .show = &incrediblec_virtual_keys_show, +}; + +static struct attribute *incrediblec_properties_attrs[] = { + &incrediblec_virtual_keys_attr.attr, + NULL +}; + +static struct attribute_group incrediblec_properties_attr_group = { + .attrs = incrediblec_properties_attrs, +}; + +static void incrediblec_reset(void) +{ + gpio_set_value(INCREDIBLEC_GPIO_PS_HOLD, 0); +} + +static void __init incrediblec_init(void) +{ + int ret; + struct kobject *properties_kobj; + + printk("incrediblec_init() revision=%d, engineerid=%d\n", system_rev, engineerid); + + msm_hw_reset_hook = incrediblec_reset; + + if (0 == engineerid || 0xF == engineerid) { + mdp_pmem_pdata.start = MSM_PMEM_MDP_XA_BASE; + android_pmem_adsp_pdata.start = MSM_PMEM_ADSP_XA_BASE; + msm_kgsl_resources[1].start = MSM_GPU_MEM_XA_BASE; + msm_kgsl_resources[1].end = MSM_GPU_MEM_XA_BASE + MSM_GPU_MEM_SIZE - 1; + } else if (engineerid >= 3) { + mdp_pmem_pdata.start = MSM_PMEM_MDP_BASE + MSM_MEM_128MB_OFFSET; + android_pmem_adsp_pdata.start = MSM_PMEM_ADSP_BASE + MSM_MEM_128MB_OFFSET; + msm_kgsl_resources[1].start = MSM_GPU_MEM_BASE; + msm_kgsl_resources[1].end = msm_kgsl_resources[1].start + MSM_GPU_MEM_SIZE - 1; + } + + OJ_BMA_power(); + + msm_acpu_clock_init(&incrediblec_clock_data); + + perflock_init(&incrediblec_perflock_data); + +#if defined(CONFIG_MSM_SERIAL_DEBUGGER) + msm_serial_debug_init(MSM_UART1_PHYS, INT_UART1, + &msm_device_uart1.dev, 1, INT_UART1_RX); +#endif + + bcm_bt_lpm_pdata.gpio_wake = INCREDIBLEC_GPIO_BT_CHIP_WAKE; + config_gpio_table(bt_gpio_table_rev_CX, ARRAY_SIZE(bt_gpio_table_rev_CX)); + + + /* set the gpu power rail to manual mode so clk en/dis will not + * turn off gpu power, and hang it on resume */ + incrediblec_kgsl_power_rail_mode(0); + incrediblec_kgsl_power(true); + + #ifdef CONFIG_SERIAL_MSM_HS + msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; + msm_device_uart_dm1.name = "msm_serial_hs"; /* for bcm */ + #endif + + incrediblec_config_uart_gpios(); + config_gpio_table(usb_phy_3v3_table, ARRAY_SIZE(usb_phy_3v3_table)); + config_gpio_table(camera_off_gpio_table, + ARRAY_SIZE(camera_off_gpio_table)); + /*gpio_direction_output(INCREDIBLEC_GPIO_TP_LS_EN, 0);*/ + gpio_direction_output(INCREDIBLEC_GPIO_TP_EN, 0); + + incrediblec_audio_init(); + msm_device_i2c_init(); +#ifdef CONFIG_MICROP_COMMON + incrediblec_microp_init(); +#endif + + if (system_rev >= 2) { + microp_data.num_functions = ARRAY_SIZE(microp_functions_1); + microp_data.microp_function = microp_functions_1; + } + + platform_add_devices(devices, ARRAY_SIZE(devices)); + if (!opt_usb_h2w_sw) { + msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; + } + if (system_rev > 2) { + incrediblec_atmel_ts_data[0].config_T9[7] = 33; + incrediblec_atmel_ts_data[0].object_crc[0] = 0x2E; + incrediblec_atmel_ts_data[0].object_crc[1] = 0x80; + incrediblec_atmel_ts_data[0].object_crc[2] = 0xE0; + } + i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); + + ret = incrediblec_init_mmc(system_rev); + if (ret != 0) + pr_crit("%s: Unable to initialize MMC\n", __func__); + + properties_kobj = kobject_create_and_add("board_properties", NULL); + if (properties_kobj) + ret = sysfs_create_group(properties_kobj, + &incrediblec_properties_attr_group); + if (!properties_kobj || ret) + pr_err("failed to create board_properties\n"); + + msm_init_pmic_vibrator(); + +} + +static void __init incrediblec_fixup(struct machine_desc *desc, struct tag *tags, + char **cmdline, struct meminfo *mi) +{ + engineerid = parse_tag_engineerid(tags); + mi->nr_banks = 1; + mi->bank[0].start = PHYS_OFFSET; + if (0 == engineerid || 0xF == engineerid) + mi->bank[0].size = (MSM_LINUX_XA_SIZE); + else if (engineerid <= 2) { /* 4G3G */ + mi->bank[0].size = MSM_EBI1_BANK0_SIZE; + mi->nr_banks++; + mi->bank[1].start = MSM_EBI1_BANK1_BASE; + mi->bank[1].size = MSM_EBI1_BANK1_SIZE; + } else { + mi->bank[0].size = MSM_EBI1_BANK0_SIZE; + mi->nr_banks++; + mi->bank[1].start = MSM_EBI1_BANK1_BASE; + mi->bank[1].size = MSM_EBI1_BANK1_SIZE + MSM_MEM_128MB_OFFSET; + } +} + +static void __init incrediblec_map_io(void) +{ + msm_map_qsd8x50_io(); + msm_clock_init(msm_clocks_8x50, msm_num_clocks_8x50); +} + +extern struct sys_timer msm_timer; + +MACHINE_START(INCREDIBLEC, "incrediblec") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x20000100, + .fixup = incrediblec_fixup, + .map_io = incrediblec_map_io, + .init_irq = msm_init_irq, + .init_machine = incrediblec_init, + .timer = &msm_timer, +MACHINE_END diff --git a/arch/arm/mach-msm/board-incrediblec.h b/arch/arm/mach-msm/board-incrediblec.h new file mode 100644 index 0000000000000..7f1c3d3443386 --- /dev/null +++ b/arch/arm/mach-msm/board-incrediblec.h @@ -0,0 +1,201 @@ +/* arch/arm/mach-msm/board-incrediblec.h + * + * Copyright (C) 2009 HTC Corporation. + * Author: Haley Teng + * + * 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 __ARCH_ARM_MACH_MSM_BOARD_INCREDIBLEC_H +#define __ARCH_ARM_MACH_MSM_BOARD_INCREDIBLEC_H + +#include + + +#define MSM_SMI_BASE 0x02B00000 +#define MSM_SMI_SIZE 0x01500000 + +#ifdef CONFIG_720P_CAMERA +#define MSM_PMEM_VENC_BASE 0x02B00000 +#define MSM_PMEM_VENC_SIZE 0x00800000 +/* rest 4MB SMI */ +#else +#define MSM_PMEM_CAMERA_BASE 0x02B00000 +#define MSM_PMEM_CAMERA_SIZE 0x00C00000 +#endif + +#define MSM_GPU_MEM_BASE 0x03700000 +#define MSM_GPU_MEM_SIZE 0x00300000 + +#define MSM_RAM_CONSOLE_BASE 0x03A00000 +#define MSM_RAM_CONSOLE_SIZE 0x00040000 + +#define MSM_FB_BASE 0x03B00000 +#define MSM_FB_SIZE 0x00300000 + +#define MSM_EBI1_BANK0_BASE 0x20000000 +#define MSM_EBI1_BANK0_SIZE 0x0E800000 + +#define MSM_EBI1_BANK1_BASE 0x30000000 +#define MSM_EBI1_BANK1_SIZE 0x03E00000 + +#define MSM_PMEM_MDP_BASE 0x33E00000 +#define MSM_PMEM_MDP_SIZE 0x02000000 + +#define MSM_PMEM_ADSP_BASE 0x35E00000 +#define MSM_PMEM_ADSP_SIZE 0x02000000 + +#define MSM_TV_FB_BASE 0x37E00000 +#define MSM_TV_FB_SIZE 0x00200000 + +#define MSM_MEM_128MB_OFFSET 0x08000000 + +/* 4G2G MCP */ +#define MSM_PMEM_ADSP_XA_BASE 0x29000000 + +#define MSM_TV_FB_XA_BASE 0x2B900000 + +#define MSM_PMEM_MDP_XA_BASE 0x2BB00000 + +#define MSM_GPU_MEM_XA_BASE 0x03700000 + +#define MSM_LINUX_XA_SIZE 0x09000000 +/* 4G2G END */ + +#define INCREDIBLEC_GPIO_UP_INT_N 35 +#define INCREDIBLEC_GPIO_UP_RESET_N 108 + +#define INCREDIBLEC_GPIO_TP_RST 34 +#define INCREDIBLEC_GPIO_TP_INT_N 145 +/*#define INCREDIBLEC_GPIO_TP_LS_EN 93*/ +#define INCREDIBLEC_GPIO_TP_EN 98 + +#define INCREDIBLEC_GPIO_SDMC_CD_N 28 + +/* BT */ +#define INCREDIBLEC_GPIO_BT_UART1_RTS (43) +#define INCREDIBLEC_GPIO_BT_UART1_CTS (44) +#define INCREDIBLEC_GPIO_BT_UART1_RX (45) +#define INCREDIBLEC_GPIO_BT_UART1_TX (46) +#define INCREDIBLEC_GPIO_BT_RESET_N (146) +#define INCREDIBLEC_GPIO_BT_HOST_WAKE (86) +#define INCREDIBLEC_GPIO_BT_CHIP_WAKE (87) +#define INCREDIBLEC_GPIO_BT_SHUTDOWN_N (128) + +#define INCREDIBLEC_GPIO_COMPASS_RST_N 107 +#define INCREDIBLEC_GPIO_COMPASS_INT_N 36 +#define INCREDIBLEC_PROJECT_NAME "incrediblec" +#define INCREDIBLEC_LAYOUTS { \ + { { 0, 1, 0}, { -1, 0, 0}, {0, 0, 1} }, \ + { { 0, -1, 0}, { -1, 0, 0}, {0, 0, 1} }, \ + { { -1, 0, 0}, { 0, -1, 0}, {0, 0, 1} }, \ + { { 1, 0, 0}, { 0, 0, 1}, {0, 1, 0} } \ + } + +/* Proximity */ +#define INCREDIBLEC_GPIO_PROXIMITY_EN_N 120 + +/* Battery */ +#define INCREDIBLEC_GPIO_MBAT_IN 39 +#define INCREDIBLEC_GPIO_MCHG_EN_N 22 +#define INCREDIBLEC_GPIO_ISET 16 + +/*Audio */ +#define INCREDIBLEC_AUD_JACKHP_EN 157 +#define INCREDIBLEC_AUD_2V5_EN 26 + +/* Bluetooth PCM */ +#define INCREDIBLEC_BT_PCM_OUT 68 +#define INCREDIBLEC_BT_PCM_IN 69 +#define INCREDIBLEC_BT_PCM_SYNC 70 +#define INCREDIBLEC_BT_PCM_CLK 71 + +#define INCREDIBLEC_GPIO_MENU_KEY 40 +#define INCREDIBLEC_GPIO_VOLUME_UP 41 +#define INCREDIBLEC_GPIO_VOLUME_DOWN 42 +#define INCREDIBLEC_GPIO_POWER_KEY 94 +#define INCREDIBLEC_GPIO_OJ_ACTION_XB 33 + +/* flash light */ +#define INCREDIBLEC_GPIO_FLASHLIGHT_FLASH (84) +#define INCREDIBLEC_GPIO_FLASHLIGHT_TORCH (85) +#define INCREDIBLEC_GPIO_FLASHLIGHT_FLASH_ADJ (31) + +/* 35mm headset */ +#define INCREDIBLEC_GPIO_35MM_HEADSET_DET (153) +#define INCREDIBLEC_GPIO_CABLE_IN1 (38) +#define INCREDIBLEC_GPIO_CABLE_IN2 (37) +#define INCREDIBLEC_GPIO_H2W_DATA (139) +#define INCREDIBLEC_GPIO_H2W_CLK (140) +#define INCREDIBLEC_GPIO_UART3_RX (139) +#define INCREDIBLEC_GPIO_UART3_TX (140) + +/* Wifi */ +#define INCREDIBLEC_GPIO_WIFI_SHUTDOWN_N 127 +#define INCREDIBLEC_GPIO_WIFI_IRQ 152 + +/* SPI */ +#define INCREDIBLEC_SPI_CLK (17) +#define INCREDIBLEC_SPI_DO (18) +#define INCREDIBLEC_SPI_CS (20) +#define INCREDIBLEC_LCD_RST_ID1 (29) +#define INCREDIBLEC_LCD_ID0 (32) +#define INCREDIBLEC_GPIO_LCD_RST_N 29 +#define INCREDIBLEC_GPIO_LCD_ID0 147 +/* TV-out */ +#define INCREDIBLEC_TV_LOAD_DET (82) +#define INCREDIBLEC_VIDEO_SHDN_N (109) +#define INCREDIBLEC_AV_SWITCH (119) + +/* LCD */ +#define INCREDIBLEC_LCD_SPI_CLK (17) +#define INCREDIBLEC_LCD_SPI_DO (18) +#define INCREDIBLEC_LCD_SPI_CSz (20) +#define INCREDIBLEC_LCD_RSTz (29) +#define INCREDIBLEC_LCD_R0 (113) +#define INCREDIBLEC_LCD_R1 (114) +#define INCREDIBLEC_LCD_R2 (115) +#define INCREDIBLEC_LCD_R3 (116) +#define INCREDIBLEC_LCD_R4 (117) +#define INCREDIBLEC_LCD_R5 (118) +#define INCREDIBLEC_LCD_G0 (121) +#define INCREDIBLEC_LCD_G1 (122) +#define INCREDIBLEC_LCD_G2 (123) +#define INCREDIBLEC_LCD_G3 (124) +#define INCREDIBLEC_LCD_G4 (125) +#define INCREDIBLEC_LCD_G5 (126) +#define INCREDIBLEC_LCD_B0 (129) +#define INCREDIBLEC_LCD_B1 (130) +#define INCREDIBLEC_LCD_B2 (131) +#define INCREDIBLEC_LCD_B3 (132) +#define INCREDIBLEC_LCD_B4 (133) +#define INCREDIBLEC_LCD_B5 (134) +#define INCREDIBLEC_LCD_PCLK (135) +#define INCREDIBLEC_LCD_VSYNC (136) +#define INCREDIBLEC_LCD_HSYNC (137) +#define INCREDIBLEC_LCD_DE (138) + +/* USB PHY 3V3 enable*/ +#define INCREDIBLEC_USB_PHY_3V3_ENABLE (104) +#define INCREDIBLEC_GPIO_USB_CABLE_IN_PIN (144) +#define INCREDIBLEC_GPIO_USB_ID_PIN (112) + +/* AP Key Led turn on*/ +#define INCREDIBLEC_AP_KEY_LED_EN (143) + +/*Camera*/ +#define INCREDIBLEC_CAM_PWD (100) +#define INCREDIBLEC_CAM_RST (99) + +#define INCREDIBLEC_GPIO_PS_HOLD (25) + +unsigned int incrediblec_get_engineerid(void); + +#endif /* __ARCH_ARM_MACH_MSM_BOARD_INCREDIBLEC_H */ diff --git a/arch/arm/mach-msm/devices-qsd8x50.c b/arch/arm/mach-msm/devices-qsd8x50.c index 05079116c031c..2aed947569932 100644 --- a/arch/arm/mach-msm/devices-qsd8x50.c +++ b/arch/arm/mach-msm/devices-qsd8x50.c @@ -969,6 +969,11 @@ struct clk msm_clocks_8x50[] = { }; +void msm_i2c_gpio_init(void) +{ + gpio_request(GPIO_I2C_CLK, "i2c_clk"); + gpio_request(GPIO_I2C_DAT, "i2c_data"); +} unsigned msm_num_clocks_8x50 = ARRAY_SIZE(msm_clocks_8x50); diff --git a/arch/arm/mach-msm/include/mach/irqs.h b/arch/arm/mach-msm/include/mach/irqs.h index 1fa4f73321312..f32831bc6f96f 100644 --- a/arch/arm/mach-msm/include/mach/irqs.h +++ b/arch/arm/mach-msm/include/mach/irqs.h @@ -40,7 +40,7 @@ #error "Unknown architecture specification" #endif -#if defined(CONFIG_MACH_SUPERSONIC) +#if defined(CONFIG_MACH_BRAVO) || defined(CONFIG_MACH_BRAVOC) || defined(CONFIG_MACH_INCREDIBLEC) || defined(CONFIG_MACH_SUPERSONIC) #define NR_IRQS (NR_MSM_IRQS + NR_SIRC_IRQS + NR_GPIO_IRQS + NR_BOARD_IRQS \ + NR_MICROP_IRQS) #define MSM_INT_TO_GPIO(n) ((n) - NR_MSM_IRQS) diff --git a/arch/arm/mach-msm/include/mach/msm_panel.h b/arch/arm/mach-msm/include/mach/msm_panel.h new file mode 100644 index 0000000000000..1a0dac675928f --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_panel.h @@ -0,0 +1,38 @@ +#ifndef _MSM_PANEL_H_ +#define _MSM_PANEL_H_ + +struct panel_platform_data { + struct resource *fb_res; + int (*power)(int on); + int (*gpio_switch)(int on); +}; + +/* For those MDDI clients with variadic length of parameter, such as Samsung + * S6D series controllers. + */ +struct lcm_va_cmd { + uint32_t reg, delay, size; + uint8_t *value; +}; +#define CMD_VECT(r, d, ...) {r, d, sizeof((uint8_t[]){__VA_ARGS__}), \ + (uint8_t []){__VA_ARGS__} } + + struct cabc_platform_data { + int (*change_cabcmode)(struct msm_mddi_client_data *client_data, + int mode, u8 dimming); + }; + +struct cabc_config { + int panel; + int shrink; + uint8_t *pwm_data; + int min_level; + int default_br; + struct msm_mddi_client_data *client; + int (*bl_handle)(struct platform_device *, int); + int (*shrink_br)(int brightness); + int (*change_cabcmode)(struct msm_mddi_client_data *client_data, + int mode, u8 dimming); +}; + +#endif diff --git a/arch/arm/mach-msm/include/mach/smem_log.h b/arch/arm/mach-msm/include/mach/smem_log.h new file mode 100644 index 0000000000000..65a42a27f76f4 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/smem_log.h @@ -0,0 +1,232 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#define SMEM_LOG_BASE 0x30 + +#define SMIOC_SETMODE _IOW(SMEM_LOG_BASE, 1, int) +#define SMIOC_SETLOG _IOW(SMEM_LOG_BASE, 2, int) + +#define SMIOC_TEXT 0x00000001 +#define SMIOC_BINARY 0x00000002 +#define SMIOC_LOG 0x00000003 +#define SMIOC_STATIC_LOG 0x00000004 + +/* Event indentifier format: + * bit 31-28 is processor ID 8 => apps, 4 => Q6, 0 => modem + * bits 27-16 are subsystem id (event base) + * bits 15-0 are event id + */ + +#define PROC 0xF0000000 +#define SUB 0x0FFF0000 +#define ID 0x0000FFFF + +#define SMEM_LOG_PROC_ID_MODEM 0x00000000 +#define SMEM_LOG_PROC_ID_Q6 0x40000000 +#define SMEM_LOG_PROC_ID_APPS 0x80000000 + +#define SMEM_LOG_CONT 0x10000000 + +#define SMEM_LOG_DEBUG_EVENT_BASE 0x00000000 +#define SMEM_LOG_ONCRPC_EVENT_BASE 0x00010000 +#define SMEM_LOG_SMEM_EVENT_BASE 0x00020000 +#define SMEM_LOG_TMC_EVENT_BASE 0x00030000 +#define SMEM_LOG_TIMETICK_EVENT_BASE 0x00040000 +#define SMEM_LOG_DEM_EVENT_BASE 0x00050000 +#define SMEM_LOG_ERROR_EVENT_BASE 0x00060000 +#define SMEM_LOG_DCVS_EVENT_BASE 0x00070000 +#define SMEM_LOG_SLEEP_EVENT_BASE 0x00080000 +#define SMEM_LOG_RPC_ROUTER_EVENT_BASE 0x00090000 +#if defined(CONFIG_MSM_N_WAY_SMSM) +#define DEM_SMSM_ISR (SMEM_LOG_DEM_EVENT_BASE + 0x1) +#define DEM_STATE_CHANGE (SMEM_LOG_DEM_EVENT_BASE + 0x2) +#define DEM_STATE_MACHINE_ENTER (SMEM_LOG_DEM_EVENT_BASE + 0x3) +#define DEM_ENTER_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 0x4) +#define DEM_END_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 0x5) +#define DEM_SETUP_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 0x6) +#define DEM_SETUP_POWER_COLLAPSE (SMEM_LOG_DEM_EVENT_BASE + 0x7) +#define DEM_SETUP_SUSPEND (SMEM_LOG_DEM_EVENT_BASE + 0x8) +#define DEM_EARLY_EXIT (SMEM_LOG_DEM_EVENT_BASE + 0x9) +#define DEM_WAKEUP_REASON (SMEM_LOG_DEM_EVENT_BASE + 0xA) +#define DEM_DETECT_WAKEUP (SMEM_LOG_DEM_EVENT_BASE + 0xB) +#define DEM_DETECT_RESET (SMEM_LOG_DEM_EVENT_BASE + 0xC) +#define DEM_DETECT_SLEEPEXIT (SMEM_LOG_DEM_EVENT_BASE + 0xD) +#define DEM_DETECT_RUN (SMEM_LOG_DEM_EVENT_BASE + 0xE) +#define DEM_APPS_SWFI (SMEM_LOG_DEM_EVENT_BASE + 0xF) +#define DEM_SEND_WAKEUP (SMEM_LOG_DEM_EVENT_BASE + 0x10) +#define DEM_ASSERT_OKTS (SMEM_LOG_DEM_EVENT_BASE + 0x11) +#define DEM_NEGATE_OKTS (SMEM_LOG_DEM_EVENT_BASE + 0x12) +#define DEM_PROC_COMM_CMD (SMEM_LOG_DEM_EVENT_BASE + 0x13) +#define DEM_REMOVE_PROC_PWR (SMEM_LOG_DEM_EVENT_BASE + 0x14) +#define DEM_RESTORE_PROC_PWR (SMEM_LOG_DEM_EVENT_BASE + 0x15) +#define DEM_SMI_CLK_DISABLED (SMEM_LOG_DEM_EVENT_BASE + 0x16) +#define DEM_SMI_CLK_ENABLED (SMEM_LOG_DEM_EVENT_BASE + 0x17) +#define DEM_MAO_INTS (SMEM_LOG_DEM_EVENT_BASE + 0x18) +#define DEM_APPS_WAKEUP_INT (SMEM_LOG_DEM_EVENT_BASE + 0x19) +#define DEM_PROC_WAKEUP (SMEM_LOG_DEM_EVENT_BASE + 0x1A) +#define DEM_PROC_POWERUP (SMEM_LOG_DEM_EVENT_BASE + 0x1B) +#define DEM_TIMER_EXPIRED (SMEM_LOG_DEM_EVENT_BASE + 0x1C) +#define DEM_SEND_BATTERY_INFO (SMEM_LOG_DEM_EVENT_BASE + 0x1D) +#define DEM_REMOTE_PWR_CB (SMEM_LOG_DEM_EVENT_BASE + 0x24) +#define DEM_TIME_SYNC_START (SMEM_LOG_DEM_EVENT_BASE + 0x1E) +#define DEM_TIME_SYNC_SEND_VALUE (SMEM_LOG_DEM_EVENT_BASE + 0x1F) +#define DEM_TIME_SYNC_DONE (SMEM_LOG_DEM_EVENT_BASE + 0x20) +#define DEM_TIME_SYNC_REQUEST (SMEM_LOG_DEM_EVENT_BASE + 0x21) +#define DEM_TIME_SYNC_POLL (SMEM_LOG_DEM_EVENT_BASE + 0x22) +#define DEM_TIME_SYNC_INIT (SMEM_LOG_DEM_EVENT_BASE + 0x23) +#define DEM_INIT (SMEM_LOG_DEM_EVENT_BASE + 0x25) +#else +#define DEM_NO_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 1) +#define DEM_INSUF_TIME (SMEM_LOG_DEM_EVENT_BASE + 2) +#define DEMAPPS_ENTER_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 3) +#define DEMAPPS_DETECT_WAKEUP (SMEM_LOG_DEM_EVENT_BASE + 4) +#define DEMAPPS_END_APPS_TCXO (SMEM_LOG_DEM_EVENT_BASE + 5) +#define DEMAPPS_ENTER_SLEEPEXIT (SMEM_LOG_DEM_EVENT_BASE + 6) +#define DEMAPPS_END_APPS_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 7) +#define DEMAPPS_SETUP_APPS_PWRCLPS (SMEM_LOG_DEM_EVENT_BASE + 8) +#define DEMAPPS_PWRCLPS_EARLY_EXIT (SMEM_LOG_DEM_EVENT_BASE + 9) +#define DEMMOD_SEND_WAKEUP (SMEM_LOG_DEM_EVENT_BASE + 0xA) +#define DEMMOD_NO_APPS_VOTE (SMEM_LOG_DEM_EVENT_BASE + 0xB) +#define DEMMOD_NO_TCXO_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 0xC) +#define DEMMOD_BT_CLOCK (SMEM_LOG_DEM_EVENT_BASE + 0xD) +#define DEMMOD_UART_CLOCK (SMEM_LOG_DEM_EVENT_BASE + 0xE) +#define DEMMOD_OKTS (SMEM_LOG_DEM_EVENT_BASE + 0xF) +#define DEM_SLEEP_INFO (SMEM_LOG_DEM_EVENT_BASE + 0x10) +#define DEMMOD_TCXO_END (SMEM_LOG_DEM_EVENT_BASE + 0x11) +#define DEMMOD_END_SLEEP_SIG (SMEM_LOG_DEM_EVENT_BASE + 0x12) +#define DEMMOD_SETUP_APPSSLEEP (SMEM_LOG_DEM_EVENT_BASE + 0x13) +#define DEMMOD_ENTER_TCXO (SMEM_LOG_DEM_EVENT_BASE + 0x14) +#define DEMMOD_WAKE_APPS (SMEM_LOG_DEM_EVENT_BASE + 0x15) +#define DEMMOD_POWER_COLLAPSE_APPS (SMEM_LOG_DEM_EVENT_BASE + 0x16) +#define DEMMOD_RESTORE_APPS_PWR (SMEM_LOG_DEM_EVENT_BASE + 0x17) +#define DEMAPPS_ASSERT_OKTS (SMEM_LOG_DEM_EVENT_BASE + 0x18) +#define DEMAPPS_RESTART_START_TIMER (SMEM_LOG_DEM_EVENT_BASE + 0x19) +#define DEMAPPS_ENTER_RUN (SMEM_LOG_DEM_EVENT_BASE + 0x1A) +#define DEMMOD_MAO_INTS (SMEM_LOG_DEM_EVENT_BASE + 0x1B) +#define DEMMOD_POWERUP_APPS_CALLED (SMEM_LOG_DEM_EVENT_BASE + 0x1C) +#define DEMMOD_PC_TIMER_EXPIRED (SMEM_LOG_DEM_EVENT_BASE + 0x1D) +#define DEM_DETECT_SLEEPEXIT (SMEM_LOG_DEM_EVENT_BASE + 0x1E) +#define DEM_DETECT_RUN (SMEM_LOG_DEM_EVENT_BASE + 0x1F) +#define DEM_SET_APPS_TIMER (SMEM_LOG_DEM_EVENT_BASE + 0x20) +#define DEM_NEGATE_OKTS (SMEM_LOG_DEM_EVENT_BASE + 0x21) +#define DEMMOD_APPS_WAKEUP_INT (SMEM_LOG_DEM_EVENT_BASE + 0x22) +#define DEMMOD_APPS_SWFI (SMEM_LOG_DEM_EVENT_BASE + 0x23) +#define DEM_SEND_BATTERY_INFO (SMEM_LOG_DEM_EVENT_BASE + 0x24) +#define DEM_SMI_CLK_DISABLED (SMEM_LOG_DEM_EVENT_BASE + 0x25) +#define DEM_SMI_CLK_ENABLED (SMEM_LOG_DEM_EVENT_BASE + 0x26) +#define DEMAPPS_SETUP_APPS_SUSPEND (SMEM_LOG_DEM_EVENT_BASE + 0x27) +#define DEM_RPC_EARLY_EXIT (SMEM_LOG_DEM_EVENT_BASE + 0x28) +#define DEMAPPS_WAKEUP_REASON (SMEM_LOG_DEM_EVENT_BASE + 0x29) +#define DEM_INIT (SMEM_LOG_DEM_EVENT_BASE + 0x30) +#endif +#define DEMMOD_UMTS_BASE (SMEM_LOG_DEM_EVENT_BASE + 0x8000) +#define DEMMOD_GL1_GO_TO_SLEEP (DEMMOD_UMTS_BASE + 0x0000) +#define DEMMOD_GL1_SLEEP_START (DEMMOD_UMTS_BASE + 0x0001) +#define DEMMOD_GL1_AFTER_GSM_CLK_ON (DEMMOD_UMTS_BASE + 0x0002) +#define DEMMOD_GL1_BEFORE_RF_ON (DEMMOD_UMTS_BASE + 0x0003) +#define DEMMOD_GL1_AFTER_RF_ON (DEMMOD_UMTS_BASE + 0x0004) +#define DEMMOD_GL1_FRAME_TICK (DEMMOD_UMTS_BASE + 0x0005) +#define DEMMOD_GL1_WCDMA_START (DEMMOD_UMTS_BASE + 0x0006) +#define DEMMOD_GL1_WCDMA_ENDING (DEMMOD_UMTS_BASE + 0x0007) +#define DEMMOD_UMTS_NOT_OKTS (DEMMOD_UMTS_BASE + 0x0008) +#define DEMMOD_UMTS_START_TCXO_SHUTDOWN (DEMMOD_UMTS_BASE + 0x0009) +#define DEMMOD_UMTS_END_TCXO_SHUTDOWN (DEMMOD_UMTS_BASE + 0x000A) +#define DEMMOD_UMTS_START_ARM_HALT (DEMMOD_UMTS_BASE + 0x000B) +#define DEMMOD_UMTS_END_ARM_HALT (DEMMOD_UMTS_BASE + 0x000C) +#define DEMMOD_UMTS_NEXT_WAKEUP_SCLK (DEMMOD_UMTS_BASE + 0x000D) +#define TIME_REMOTE_LOG_EVENT_START (SMEM_LOG_TIMETICK_EVENT_BASE + 0) +#define TIME_REMOTE_LOG_EVENT_GOTO_WAIT (SMEM_LOG_TIMETICK_EVENT_BASE + 1) +#define TIME_REMOTE_LOG_EVENT_GOTO_INIT (SMEM_LOG_TIMETICK_EVENT_BASE + 2) +#define ERR_ERROR_FATAL (SMEM_LOG_ERROR_EVENT_BASE + 1) +#define ERR_ERROR_FATAL_TASK (SMEM_LOG_ERROR_EVENT_BASE + 2) +#define DCVSAPPS_LOG_IDLE (SMEM_LOG_DCVS_EVENT_BASE + 0x0) +#define DCVSAPPS_LOG_ERR (SMEM_LOG_DCVS_EVENT_BASE + 0x1) +#define DCVSAPPS_LOG_CHG (SMEM_LOG_DCVS_EVENT_BASE + 0x2) +#define DCVSAPPS_LOG_REG (SMEM_LOG_DCVS_EVENT_BASE + 0x3) +#define DCVSAPPS_LOG_DEREG (SMEM_LOG_DCVS_EVENT_BASE + 0x4) +#define SMEM_LOG_EVENT_CB (SMEM_LOG_SMEM_EVENT_BASE + 0) +#define SMEM_LOG_EVENT_START (SMEM_LOG_SMEM_EVENT_BASE + 1) +#define SMEM_LOG_EVENT_INIT (SMEM_LOG_SMEM_EVENT_BASE + 2) +#define SMEM_LOG_EVENT_RUNNING (SMEM_LOG_SMEM_EVENT_BASE + 3) +#define SMEM_LOG_EVENT_STOP (SMEM_LOG_SMEM_EVENT_BASE + 4) +#define SMEM_LOG_EVENT_RESTART (SMEM_LOG_SMEM_EVENT_BASE + 5) +#define SMEM_LOG_EVENT_SS (SMEM_LOG_SMEM_EVENT_BASE + 6) +#define SMEM_LOG_EVENT_READ (SMEM_LOG_SMEM_EVENT_BASE + 7) +#define SMEM_LOG_EVENT_WRITE (SMEM_LOG_SMEM_EVENT_BASE + 8) +#define SMEM_LOG_EVENT_SIGS1 (SMEM_LOG_SMEM_EVENT_BASE + 9) +#define SMEM_LOG_EVENT_SIGS2 (SMEM_LOG_SMEM_EVENT_BASE + 10) +#define SMEM_LOG_EVENT_WRITE_DM (SMEM_LOG_SMEM_EVENT_BASE + 11) +#define SMEM_LOG_EVENT_READ_DM (SMEM_LOG_SMEM_EVENT_BASE + 12) +#define SMEM_LOG_EVENT_SKIP_DM (SMEM_LOG_SMEM_EVENT_BASE + 13) +#define SMEM_LOG_EVENT_STOP_DM (SMEM_LOG_SMEM_EVENT_BASE + 14) +#define SMEM_LOG_EVENT_ISR (SMEM_LOG_SMEM_EVENT_BASE + 15) +#define SMEM_LOG_EVENT_TASK (SMEM_LOG_SMEM_EVENT_BASE + 16) +#define SMEM_LOG_EVENT_RS (SMEM_LOG_SMEM_EVENT_BASE + 17) +#define ONCRPC_LOG_EVENT_SMD_WAIT (SMEM_LOG_ONCRPC_EVENT_BASE + 0) +#define ONCRPC_LOG_EVENT_RPC_WAIT (SMEM_LOG_ONCRPC_EVENT_BASE + 1) +#define ONCRPC_LOG_EVENT_RPC_BOTH_WAIT (SMEM_LOG_ONCRPC_EVENT_BASE + 2) +#define ONCRPC_LOG_EVENT_RPC_INIT (SMEM_LOG_ONCRPC_EVENT_BASE + 3) +#define ONCRPC_LOG_EVENT_RUNNING (SMEM_LOG_ONCRPC_EVENT_BASE + 4) +#define ONCRPC_LOG_EVENT_APIS_INITED (SMEM_LOG_ONCRPC_EVENT_BASE + 5) +#define ONCRPC_LOG_EVENT_AMSS_RESET (SMEM_LOG_ONCRPC_EVENT_BASE + 6) +#define ONCRPC_LOG_EVENT_SMD_RESET (SMEM_LOG_ONCRPC_EVENT_BASE + 7) +#define ONCRPC_LOG_EVENT_ONCRPC_RESET (SMEM_LOG_ONCRPC_EVENT_BASE + 8) +#define ONCRPC_LOG_EVENT_CB (SMEM_LOG_ONCRPC_EVENT_BASE + 9) +#define ONCRPC_LOG_EVENT_STD_CALL (SMEM_LOG_ONCRPC_EVENT_BASE + 10) +#define ONCRPC_LOG_EVENT_STD_REPLY (SMEM_LOG_ONCRPC_EVENT_BASE + 11) +#define ONCRPC_LOG_EVENT_STD_CALL_ASYNC (SMEM_LOG_ONCRPC_EVENT_BASE + 12) +#define NO_SLEEP_OLD (SMEM_LOG_SLEEP_EVENT_BASE + 0x1) +#define INSUF_TIME (SMEM_LOG_SLEEP_EVENT_BASE + 0x2) +#define MOD_UART_CLOCK (SMEM_LOG_SLEEP_EVENT_BASE + 0x3) +#define SLEEP_INFO (SMEM_LOG_SLEEP_EVENT_BASE + 0x4) +#define MOD_TCXO_END (SMEM_LOG_SLEEP_EVENT_BASE + 0x5) +#define MOD_ENTER_TCXO (SMEM_LOG_SLEEP_EVENT_BASE + 0x6) +#define NO_SLEEP_NEW (SMEM_LOG_SLEEP_EVENT_BASE + 0x7) +#define RPC_ROUTER_LOG_EVENT_UNKNOWN (SMEM_LOG_RPC_ROUTER_EVENT_BASE) +#define RPC_ROUTER_LOG_EVENT_MSG_READ (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 1) +#define RPC_ROUTER_LOG_EVENT_MSG_WRITTEN (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 2) +#define RPC_ROUTER_LOG_EVENT_MSG_CFM_REQ (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 3) +#define RPC_ROUTER_LOG_EVENT_MSG_CFM_SNT (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 4) +#define RPC_ROUTER_LOG_EVENT_MID_READ (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 5) +#define RPC_ROUTER_LOG_EVENT_MID_WRITTEN (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 6) +#define RPC_ROUTER_LOG_EVENT_MID_CFM_REQ (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 7) + +void smem_log_event(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3); +void smem_log_event6(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6); +void smem_log_event_to_static(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3); +void smem_log_event6_to_static(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6); + diff --git a/arch/arm/mach-msm/include/mach/system.h b/arch/arm/mach-msm/include/mach/system.h index cdb049d74aa72..23de62af3302c 100644 --- a/arch/arm/mach-msm/include/mach/system.h +++ b/arch/arm/mach-msm/include/mach/system.h @@ -29,3 +29,4 @@ extern void (*msm_hw_reset_hook)(void); void msm_set_i2c_mux(bool gpio, int *gpio_clk, int *gpio_dat); +void msm_i2c_gpio_init(void); diff --git a/arch/arm/mach-msm/proc_engineerid.c b/arch/arm/mach-msm/proc_engineerid.c new file mode 100644 index 0000000000000..7da8c978d525f --- /dev/null +++ b/arch/arm/mach-msm/proc_engineerid.c @@ -0,0 +1,70 @@ +/* arch/arm/mach-msm/proc_engineerid.c + * + * Copyright (C) 2009 HTC Corporation. + * + * 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 "devices.h" + +static int c_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%d\n"); + + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < 1 ? (void *)1 : NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return NULL; +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +const struct seq_operations engineerid_op = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = c_show +}; + +extern const struct seq_operations engineerid_op; +static int engineerid_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &engineerid_op); +} + +static const struct file_operations proc_engineerid_operations = { + .open = engineerid_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __init proc_engineerid_init(void) +{ + proc_create("engineerid", 0, NULL, &proc_engineerid_operations); + return 0; +} +module_init(proc_engineerid_init); + diff --git a/arch/arm/mach-msm/smem_log.c b/arch/arm/mach-msm/smem_log.c new file mode 100644 index 0000000000000..8f1bf00b3e9d1 --- /dev/null +++ b/arch/arm/mach-msm/smem_log.c @@ -0,0 +1,2034 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * Shared memory logging implementation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smd_private.h" + +#define DEBUG +#undef DEBUG + +#ifdef DEBUG +#define D_DUMP_BUFFER(prestr, cnt, buf) \ +do { \ + int i; \ + printk(KERN_ERR "%s", prestr); \ + for (i = 0; i < cnt; i++) \ + printk(KERN_ERR "%.2x", buf[i]); \ + printk(KERN_ERR "\n"); \ +} while (0) +#else +#define D_DUMP_BUFFER(prestr, cnt, buf) +#endif + +#ifdef DEBUG +#define D(x...) printk(x) +#else +#define D(x...) do {} while (0) +#endif + +#define TIMESTAMP_ADDR (MSM_CSR_BASE + 0x04) + +struct smem_log_item { + uint32_t identifier; + uint32_t timetick; + uint32_t data1; + uint32_t data2; + uint32_t data3; +}; + +#define SMEM_LOG_NUM_ENTRIES 2000 +#define SMEM_LOG_EVENTS_SIZE (sizeof(struct smem_log_item) * \ + SMEM_LOG_NUM_ENTRIES) + +#define SMEM_LOG_NUM_STATIC_ENTRIES 150 +#define SMEM_STATIC_LOG_EVENTS_SIZE (sizeof(struct smem_log_item) * \ + SMEM_LOG_NUM_STATIC_ENTRIES) + +#define SMEM_LOG_NUM_POWER_ENTRIES 2000 +#define SMEM_POWER_LOG_EVENTS_SIZE (sizeof(struct smem_log_item) * \ + SMEM_LOG_NUM_POWER_ENTRIES) + +#if defined(CONFIG_ARCH_MSM7X30) +#define SMEM_SPINLOCK_SMEM_LOG "S:2" +#define SMEM_SPINLOCK_STATIC_LOG "S:5" +#else +#define SMEM_SPINLOCK_SMEM_LOG 2 +#define SMEM_SPINLOCK_STATIC_LOG 5 +#endif +/* POWER shares with SMEM_SPINLOCK_SMEM_LOG */ + +static remote_spinlock_t remote_spinlock; +static remote_spinlock_t remote_spinlock_static; + +struct smem_log_inst { + int which_log; + struct smem_log_item __iomem *events; + uint32_t __iomem *idx; + int num; + remote_spinlock_t *remote_spinlock; +}; + +enum smem_logs { + GEN = 0, + STA, + POW, + NUM +}; + +static struct smem_log_inst inst[NUM]; + +#if defined(CONFIG_DEBUG_FS) + +#define HSIZE 13 + +struct sym { + uint32_t val; + char *str; + struct hlist_node node; +}; + +struct sym id_syms[] = { + { SMEM_LOG_PROC_ID_MODEM, "MODM" }, + { SMEM_LOG_PROC_ID_Q6, "QDSP" }, + { SMEM_LOG_PROC_ID_APPS, "APPS" }, +}; + +struct sym base_syms[] = { + { SMEM_LOG_ONCRPC_EVENT_BASE, "ONCRPC" }, + { SMEM_LOG_SMEM_EVENT_BASE, "SMEM" }, + { SMEM_LOG_TMC_EVENT_BASE, "TMC" }, + { SMEM_LOG_TIMETICK_EVENT_BASE, "TIMETICK" }, + { SMEM_LOG_DEM_EVENT_BASE, "DEM" }, + { SMEM_LOG_ERROR_EVENT_BASE, "ERROR" }, + { SMEM_LOG_DCVS_EVENT_BASE, "DCVS" }, + { SMEM_LOG_SLEEP_EVENT_BASE, "SLEEP" }, + { SMEM_LOG_RPC_ROUTER_EVENT_BASE, "ROUTER" }, +}; + +struct sym event_syms[] = { +#if defined(CONFIG_MSM_N_WAY_SMSM) + { DEM_SMSM_ISR, "SMSM_ISR" }, + { DEM_STATE_CHANGE, "STATE_CHANGE" }, + { DEM_STATE_MACHINE_ENTER, "STATE_MACHINE_ENTER" }, + { DEM_ENTER_SLEEP, "ENTER_SLEEP" }, + { DEM_END_SLEEP, "END_SLEEP" }, + { DEM_SETUP_SLEEP, "SETUP_SLEEP" }, + { DEM_SETUP_POWER_COLLAPSE, "SETUP_POWER_COLLAPSE" }, + { DEM_SETUP_SUSPEND, "SETUP_SUSPEND" }, + { DEM_EARLY_EXIT, "EARLY_EXIT" }, + { DEM_WAKEUP_REASON, "WAKEUP_REASON" }, + { DEM_DETECT_WAKEUP, "DETECT_WAKEUP" }, + { DEM_DETECT_RESET, "DETECT_RESET" }, + { DEM_DETECT_SLEEPEXIT, "DETECT_SLEEPEXIT" }, + { DEM_DETECT_RUN, "DETECT_RUN" }, + { DEM_APPS_SWFI, "APPS_SWFI" }, + { DEM_SEND_WAKEUP, "SEND_WAKEUP" }, + { DEM_ASSERT_OKTS, "ASSERT_OKTS" }, + { DEM_NEGATE_OKTS, "NEGATE_OKTS" }, + { DEM_PROC_COMM_CMD, "PROC_COMM_CMD" }, + { DEM_REMOVE_PROC_PWR, "REMOVE_PROC_PWR" }, + { DEM_RESTORE_PROC_PWR, "RESTORE_PROC_PWR" }, + { DEM_SMI_CLK_DISABLED, "SMI_CLK_DISABLED" }, + { DEM_SMI_CLK_ENABLED, "SMI_CLK_ENABLED" }, + { DEM_MAO_INTS, "MAO_INTS" }, + { DEM_APPS_WAKEUP_INT, "APPS_WAKEUP_INT" }, + { DEM_PROC_WAKEUP, "PROC_WAKEUP" }, + { DEM_PROC_POWERUP, "PROC_POWERUP" }, + { DEM_TIMER_EXPIRED, "TIMER_EXPIRED" }, + { DEM_SEND_BATTERY_INFO, "SEND_BATTERY_INFO" }, + { DEM_REMOTE_PWR_CB, "REMOTE_PWR_CB" }, + { DEM_TIME_SYNC_START, "TIME_SYNC_START" }, + { DEM_TIME_SYNC_SEND_VALUE, "TIME_SYNC_SEND_VALUE" }, + { DEM_TIME_SYNC_DONE, "TIME_SYNC_DONE" }, + { DEM_TIME_SYNC_REQUEST, "TIME_SYNC_REQUEST" }, + { DEM_TIME_SYNC_POLL, "TIME_SYNC_POLL" }, + { DEM_TIME_SYNC_INIT, "TIME_SYNC_INIT" }, + { DEM_INIT, "INIT" }, +#else + + { DEM_NO_SLEEP, "NO_SLEEP" }, + { DEM_INSUF_TIME, "INSUF_TIME" }, + { DEMAPPS_ENTER_SLEEP, "APPS_ENTER_SLEEP" }, + { DEMAPPS_DETECT_WAKEUP, "APPS_DETECT_WAKEUP" }, + { DEMAPPS_END_APPS_TCXO, "APPS_END_APPS_TCXO" }, + { DEMAPPS_ENTER_SLEEPEXIT, "APPS_ENTER_SLEEPEXIT" }, + { DEMAPPS_END_APPS_SLEEP, "APPS_END_APPS_SLEEP" }, + { DEMAPPS_SETUP_APPS_PWRCLPS, "APPS_SETUP_APPS_PWRCLPS" }, + { DEMAPPS_PWRCLPS_EARLY_EXIT, "APPS_PWRCLPS_EARLY_EXIT" }, + { DEMMOD_SEND_WAKEUP, "MOD_SEND_WAKEUP" }, + { DEMMOD_NO_APPS_VOTE, "MOD_NO_APPS_VOTE" }, + { DEMMOD_NO_TCXO_SLEEP, "MOD_NO_TCXO_SLEEP" }, + { DEMMOD_BT_CLOCK, "MOD_BT_CLOCK" }, + { DEMMOD_UART_CLOCK, "MOD_UART_CLOCK" }, + { DEMMOD_OKTS, "MOD_OKTS" }, + { DEM_SLEEP_INFO, "SLEEP_INFO" }, + { DEMMOD_TCXO_END, "MOD_TCXO_END" }, + { DEMMOD_END_SLEEP_SIG, "MOD_END_SLEEP_SIG" }, + { DEMMOD_SETUP_APPSSLEEP, "MOD_SETUP_APPSSLEEP" }, + { DEMMOD_ENTER_TCXO, "MOD_ENTER_TCXO" }, + { DEMMOD_WAKE_APPS, "MOD_WAKE_APPS" }, + { DEMMOD_POWER_COLLAPSE_APPS, "MOD_POWER_COLLAPSE_APPS" }, + { DEMMOD_RESTORE_APPS_PWR, "MOD_RESTORE_APPS_PWR" }, + { DEMAPPS_ASSERT_OKTS, "APPS_ASSERT_OKTS" }, + { DEMAPPS_RESTART_START_TIMER, "APPS_RESTART_START_TIMER" }, + { DEMAPPS_ENTER_RUN, "APPS_ENTER_RUN" }, + { DEMMOD_MAO_INTS, "MOD_MAO_INTS" }, + { DEMMOD_POWERUP_APPS_CALLED, "MOD_POWERUP_APPS_CALLED" }, + { DEMMOD_PC_TIMER_EXPIRED, "MOD_PC_TIMER_EXPIRED" }, + { DEM_DETECT_SLEEPEXIT, "_DETECT_SLEEPEXIT" }, + { DEM_DETECT_RUN, "DETECT_RUN" }, + { DEM_SET_APPS_TIMER, "SET_APPS_TIMER" }, + { DEM_NEGATE_OKTS, "NEGATE_OKTS" }, + { DEMMOD_APPS_WAKEUP_INT, "MOD_APPS_WAKEUP_INT" }, + { DEMMOD_APPS_SWFI, "MOD_APPS_SWFI" }, + { DEM_SEND_BATTERY_INFO, "SEND_BATTERY_INFO" }, + { DEM_SMI_CLK_DISABLED, "SMI_CLK_DISABLED" }, + { DEM_SMI_CLK_ENABLED, "SMI_CLK_ENABLED" }, + { DEMAPPS_SETUP_APPS_SUSPEND, "APPS_SETUP_APPS_SUSPEND" }, + { DEM_RPC_EARLY_EXIT, "RPC_EARLY_EXIT" }, + { DEMAPPS_WAKEUP_REASON, "APPS_WAKEUP_REASON" }, + { DEM_INIT, "INIT" }, +#endif + { DEMMOD_UMTS_BASE, "MOD_UMTS_BASE" }, + { DEMMOD_GL1_GO_TO_SLEEP, "GL1_GO_TO_SLEEP" }, + { DEMMOD_GL1_SLEEP_START, "GL1_SLEEP_START" }, + { DEMMOD_GL1_AFTER_GSM_CLK_ON, "GL1_AFTER_GSM_CLK_ON" }, + { DEMMOD_GL1_BEFORE_RF_ON, "GL1_BEFORE_RF_ON" }, + { DEMMOD_GL1_AFTER_RF_ON, "GL1_AFTER_RF_ON" }, + { DEMMOD_GL1_FRAME_TICK, "GL1_FRAME_TICK" }, + { DEMMOD_GL1_WCDMA_START, "GL1_WCDMA_START" }, + { DEMMOD_GL1_WCDMA_ENDING, "GL1_WCDMA_ENDING" }, + { DEMMOD_UMTS_NOT_OKTS, "UMTS_NOT_OKTS" }, + { DEMMOD_UMTS_START_TCXO_SHUTDOWN, "UMTS_START_TCXO_SHUTDOWN" }, + { DEMMOD_UMTS_END_TCXO_SHUTDOWN, "UMTS_END_TCXO_SHUTDOWN" }, + { DEMMOD_UMTS_START_ARM_HALT, "UMTS_START_ARM_HALT" }, + { DEMMOD_UMTS_END_ARM_HALT, "UMTS_END_ARM_HALT" }, + { DEMMOD_UMTS_NEXT_WAKEUP_SCLK, "UMTS_NEXT_WAKEUP_SCLK" }, + { TIME_REMOTE_LOG_EVENT_START, "START" }, + { TIME_REMOTE_LOG_EVENT_GOTO_WAIT, + "GOTO_WAIT" }, + { TIME_REMOTE_LOG_EVENT_GOTO_INIT, + "GOTO_INIT" }, + { ERR_ERROR_FATAL, "ERR_ERROR_FATAL" }, + { ERR_ERROR_FATAL_TASK, "ERR_ERROR_FATAL_TASK" }, + { DCVSAPPS_LOG_IDLE, "DCVSAPPS_LOG_IDLE" }, + { DCVSAPPS_LOG_ERR, "DCVSAPPS_LOG_ERR" }, + { DCVSAPPS_LOG_CHG, "DCVSAPPS_LOG_CHG" }, + { DCVSAPPS_LOG_REG, "DCVSAPPS_LOG_REG" }, + { DCVSAPPS_LOG_DEREG, "DCVSAPPS_LOG_DEREG" }, + { SMEM_LOG_EVENT_CB, "CB" }, + { SMEM_LOG_EVENT_START, "START" }, + { SMEM_LOG_EVENT_INIT, "INIT" }, + { SMEM_LOG_EVENT_RUNNING, "RUNNING" }, + { SMEM_LOG_EVENT_STOP, "STOP" }, + { SMEM_LOG_EVENT_RESTART, "RESTART" }, + { SMEM_LOG_EVENT_SS, "SS" }, + { SMEM_LOG_EVENT_READ, "READ" }, + { SMEM_LOG_EVENT_WRITE, "WRITE" }, + { SMEM_LOG_EVENT_SIGS1, "SIGS1" }, + { SMEM_LOG_EVENT_SIGS2, "SIGS2" }, + { SMEM_LOG_EVENT_WRITE_DM, "WRITE_DM" }, + { SMEM_LOG_EVENT_READ_DM, "READ_DM" }, + { SMEM_LOG_EVENT_SKIP_DM, "SKIP_DM" }, + { SMEM_LOG_EVENT_STOP_DM, "STOP_DM" }, + { SMEM_LOG_EVENT_ISR, "ISR" }, + { SMEM_LOG_EVENT_TASK, "TASK" }, + { SMEM_LOG_EVENT_RS, "RS" }, + { ONCRPC_LOG_EVENT_SMD_WAIT, "SMD_WAIT" }, + { ONCRPC_LOG_EVENT_RPC_WAIT, "RPC_WAIT" }, + { ONCRPC_LOG_EVENT_RPC_BOTH_WAIT, "RPC_BOTH_WAIT" }, + { ONCRPC_LOG_EVENT_RPC_INIT, "RPC_INIT" }, + { ONCRPC_LOG_EVENT_RUNNING, "RUNNING" }, + { ONCRPC_LOG_EVENT_APIS_INITED, "APIS_INITED" }, + { ONCRPC_LOG_EVENT_AMSS_RESET, "AMSS_RESET" }, + { ONCRPC_LOG_EVENT_SMD_RESET, "SMD_RESET" }, + { ONCRPC_LOG_EVENT_ONCRPC_RESET, "ONCRPC_RESET" }, + { ONCRPC_LOG_EVENT_CB, "CB" }, + { ONCRPC_LOG_EVENT_STD_CALL, "STD_CALL" }, + { ONCRPC_LOG_EVENT_STD_REPLY, "STD_REPLY" }, + { ONCRPC_LOG_EVENT_STD_CALL_ASYNC, "STD_CALL_ASYNC" }, + { NO_SLEEP_OLD, "NO_SLEEP_OLD" }, + { INSUF_TIME, "INSUF_TIME" }, + { MOD_UART_CLOCK, "MOD_UART_CLOCK" }, + { SLEEP_INFO, "SLEEP_INFO" }, + { MOD_TCXO_END, "MOD_TCXO_END" }, + { MOD_ENTER_TCXO, "MOD_ENTER_TCXO" }, + { NO_SLEEP_NEW, "NO_SLEEP_NEW" }, + { RPC_ROUTER_LOG_EVENT_UNKNOWN, "UNKNOWN" }, + { RPC_ROUTER_LOG_EVENT_MSG_READ, "MSG_READ" }, + { RPC_ROUTER_LOG_EVENT_MSG_WRITTEN, "MSG_WRITTEN" }, + { RPC_ROUTER_LOG_EVENT_MSG_CFM_REQ, "MSG_CFM_REQ" }, + { RPC_ROUTER_LOG_EVENT_MSG_CFM_SNT, "MSG_CFM_SNT" }, + { RPC_ROUTER_LOG_EVENT_MID_READ, "MID_READ" }, + { RPC_ROUTER_LOG_EVENT_MID_WRITTEN, "MID_WRITTEN" }, + { RPC_ROUTER_LOG_EVENT_MID_CFM_REQ, "MID_CFM_REQ" }, + +}; + +struct sym oncrpc_syms[] = { + { 0x30000000, "CM" }, + { 0x30000001, "DB" }, + { 0x30000002, "SND" }, + { 0x30000003, "WMS" }, + { 0x30000004, "PDSM" }, + { 0x30000005, "MISC_MODEM_APIS" }, + { 0x30000006, "MISC_APPS_APIS" }, + { 0x30000007, "JOYST" }, + { 0x30000008, "VJOY" }, + { 0x30000009, "JOYSTC" }, + { 0x3000000a, "ADSPRTOSATOM" }, + { 0x3000000b, "ADSPRTOSMTOA" }, + { 0x3000000c, "I2C" }, + { 0x3000000d, "TIME_REMOTE" }, + { 0x3000000e, "NV" }, + { 0x3000000f, "CLKRGM_SEC" }, + { 0x30000010, "RDEVMAP" }, + { 0x30000011, "FS_RAPI" }, + { 0x30000012, "PBMLIB" }, + { 0x30000013, "AUDMGR" }, + { 0x30000014, "MVS" }, + { 0x30000015, "DOG_KEEPALIVE" }, + { 0x30000016, "GSDI_EXP" }, + { 0x30000017, "AUTH" }, + { 0x30000018, "NVRUIMI" }, + { 0x30000019, "MMGSDILIB" }, + { 0x3000001a, "CHARGER" }, + { 0x3000001b, "UIM" }, + { 0x3000001C, "ONCRPCTEST" }, + { 0x3000001d, "PDSM_ATL" }, + { 0x3000001e, "FS_XMOUNT" }, + { 0x3000001f, "SECUTIL " }, + { 0x30000020, "MCCMEID" }, + { 0x30000021, "PM_STROBE_FLASH" }, + { 0x30000022, "DS707_EXTIF" }, + { 0x30000023, "SMD BRIDGE_MODEM" }, + { 0x30000024, "SMD PORT_MGR" }, + { 0x30000025, "BUS_PERF" }, + { 0x30000026, "BUS_MON" }, + { 0x30000027, "MC" }, + { 0x30000028, "MCCAP" }, + { 0x30000029, "MCCDMA" }, + { 0x3000002a, "MCCDS" }, + { 0x3000002b, "MCCSCH" }, + { 0x3000002c, "MCCSRID" }, + { 0x3000002d, "SNM" }, + { 0x3000002e, "MCCSYOBJ" }, + { 0x3000002f, "DS707_APIS" }, + { 0x30000030, "DS_MP_SHIM_APPS_ASYNC" }, + { 0x30000031, "DSRLP_APIS" }, + { 0x30000032, "RLP_APIS" }, + { 0x30000033, "DS_MP_SHIM_MODEM" }, + { 0x30000034, "DSHDR_APIS" }, + { 0x30000035, "DSHDR_MDM_APIS" }, + { 0x30000036, "DS_MP_SHIM_APPS" }, + { 0x30000037, "HDRMC_APIS" }, + { 0x30000038, "SMD_BRIDGE_MTOA" }, + { 0x30000039, "SMD_BRIDGE_ATOM" }, + { 0x3000003a, "DPMAPP_OTG" }, + { 0x3000003b, "DIAG" }, + { 0x3000003c, "GSTK_EXP" }, + { 0x3000003d, "DSBC_MDM_APIS" }, + { 0x3000003e, "HDRMRLP_MDM_APIS" }, + { 0x3000003f, "HDRMRLP_APPS_APIS" }, + { 0x30000040, "HDRMC_MRLP_APIS" }, + { 0x30000041, "PDCOMM_APP_API" }, + { 0x30000042, "DSAT_APIS" }, + { 0x30000043, "MISC_RF_APIS" }, + { 0x30000044, "CMIPAPP" }, + { 0x30000045, "DSMP_UMTS_MODEM_APIS" }, + { 0x30000046, "DSMP_UMTS_APPS_APIS" }, + { 0x30000047, "DSUCSDMPSHIM" }, + { 0x30000048, "TIME_REMOTE_ATOM" }, + { 0x3000004a, "SD" }, + { 0x3000004b, "MMOC" }, + { 0x3000004c, "WLAN_ADP_FTM" }, + { 0x3000004d, "WLAN_CP_CM" }, + { 0x3000004e, "FTM_WLAN" }, + { 0x3000004f, "SDCC_CPRM" }, + { 0x30000050, "CPRMINTERFACE" }, + { 0x30000051, "DATA_ON_MODEM_MTOA_APIS" }, + { 0x30000052, "DATA_ON_APPS_ATOM_APIS" }, + { 0x30000053, "MISC_MODEM_APIS_NONWINMOB" }, + { 0x30000054, "MISC_APPS_APIS_NONWINMOB" }, + { 0x30000055, "PMEM_REMOTE" }, + { 0x30000056, "TCXOMGR" }, + { 0x30000057, "DSUCSDAPPIF_APIS" }, + { 0x30000058, "BT" }, + { 0x30000059, "PD_COMMS_API" }, + { 0x3000005a, "PD_COMMS_CLIENT_API" }, + { 0x3000005b, "PDAPI" }, + { 0x3000005c, "LSA_SUPL_DSM" }, + { 0x3000005d, "TIME_REMOTE_MTOA" }, + { 0x3000005e, "FTM_BT" }, + { 0X3000005f, "DSUCSDAPPIF_APIS" }, + { 0X30000060, "PMAPP_GEN" }, + { 0X30000061, "PM_LIB" }, + { 0X30000062, "KEYPAD" }, + { 0X30000063, "HSU_APP_APIS" }, + { 0X30000064, "HSU_MDM_APIS" }, + { 0X30000065, "ADIE_ADC_REMOTE_ATOM " }, + { 0X30000066, "TLMM_REMOTE_ATOM" }, + { 0X30000067, "UI_CALLCTRL" }, + { 0X30000068, "UIUTILS" }, + { 0X30000069, "PRL" }, + { 0X3000006a, "HW" }, + { 0X3000006b, "OEM_RAPI" }, + { 0X3000006c, "WMSPM" }, + { 0X3000006d, "BTPF" }, + { 0X3000006e, "CLKRGM_SYNC_EVENT" }, + { 0X3000006f, "USB_APPS_RPC" }, + { 0X30000070, "USB_MODEM_RPC" }, + { 0X30000071, "ADC" }, + { 0X30000072, "CAMERAREMOTED" }, + { 0X30000073, "SECAPIREMOTED" }, + { 0X30000074, "DSATAPI" }, + { 0X30000075, "CLKCTL_RPC" }, + { 0X30000076, "BREWAPPCOORD" }, + { 0X30000077, "ALTENVSHELL" }, + { 0X30000078, "WLAN_TRP_UTILS" }, + { 0X30000079, "GPIO_RPC" }, + { 0X3000007a, "PING_RPC" }, + { 0X3000007b, "DSC_DCM_API" }, + { 0X3000007c, "L1_DS" }, + { 0X3000007d, "QCHATPK_APIS" }, + { 0X3000007e, "GPS_API" }, + { 0X3000007f, "OSS_RRCASN_REMOTE" }, + { 0X30000080, "PMAPP_OTG_REMOTE" }, + { 0X30000081, "PING_MDM_RPC" }, + { 0X30000082, "PING_KERNEL_RPC" }, + { 0X30000083, "TIMETICK" }, + { 0X30000084, "WM_BTHCI_FTM " }, + { 0X30000085, "WM_BT_PF" }, + { 0X30000086, "IPA_IPC_APIS" }, + { 0X30000087, "UKCC_IPC_APIS" }, + { 0X30000088, "CMIPSMS " }, + { 0X30000089, "VBATT_REMOTE" }, + { 0X3000008a, "MFPAL" }, + { 0X3000008b, "DSUMTSPDPREG" }, + { 0X3000fe00, "RESTART_DAEMON NUMBER 0" }, + { 0X3000fe01, "RESTART_DAEMON NUMBER 1" }, + { 0X3000feff, "RESTART_DAEMON NUMBER 255" }, + { 0X3000fffe, "BACKWARDS_COMPATIBILITY_IN_RPC_CLNT_LOOKUP" }, + { 0X3000ffff, "RPC_ROUTER_SERVER_PROGRAM" }, + { 0x31000000, "CM CB" }, + { 0x31000001, "DB CB" }, + { 0x31000002, "SND CB" }, + { 0x31000003, "WMS CB" }, + { 0x31000004, "PDSM CB" }, + { 0x31000005, "MISC_MODEM_APIS CB" }, + { 0x31000006, "MISC_APPS_APIS CB" }, + { 0x31000007, "JOYST CB" }, + { 0x31000008, "VJOY CB" }, + { 0x31000009, "JOYSTC CB" }, + { 0x3100000a, "ADSPRTOSATOM CB" }, + { 0x3100000b, "ADSPRTOSMTOA CB" }, + { 0x3100000c, "I2C CB" }, + { 0x3100000d, "TIME_REMOTE CB" }, + { 0x3100000e, "NV CB" }, + { 0x3100000f, "CLKRGM_SEC CB" }, + { 0x31000010, "RDEVMAP CB" }, + { 0x31000011, "FS_RAPI CB" }, + { 0x31000012, "PBMLIB CB" }, + { 0x31000013, "AUDMGR CB" }, + { 0x31000014, "MVS CB" }, + { 0x31000015, "DOG_KEEPALIVE CB" }, + { 0x31000016, "GSDI_EXP CB" }, + { 0x31000017, "AUTH CB" }, + { 0x31000018, "NVRUIMI CB" }, + { 0x31000019, "MMGSDILIB CB" }, + { 0x3100001a, "CHARGER CB" }, + { 0x3100001b, "UIM CB" }, + { 0x3100001C, "ONCRPCTEST CB" }, + { 0x3100001d, "PDSM_ATL CB" }, + { 0x3100001e, "FS_XMOUNT CB" }, + { 0x3100001f, "SECUTIL CB" }, + { 0x31000020, "MCCMEID" }, + { 0x31000021, "PM_STROBE_FLASH CB" }, + { 0x31000022, "DS707_EXTIF CB" }, + { 0x31000023, "SMD BRIDGE_MODEM CB" }, + { 0x31000024, "SMD PORT_MGR CB" }, + { 0x31000025, "BUS_PERF CB" }, + { 0x31000026, "BUS_MON CB" }, + { 0x31000027, "MC CB" }, + { 0x31000028, "MCCAP CB" }, + { 0x31000029, "MCCDMA CB" }, + { 0x3100002a, "MCCDS CB" }, + { 0x3100002b, "MCCSCH CB" }, + { 0x3100002c, "MCCSRID CB" }, + { 0x3100002d, "SNM CB" }, + { 0x3100002e, "MCCSYOBJ CB" }, + { 0x3100002f, "DS707_APIS CB" }, + { 0x31000030, "DS_MP_SHIM_APPS_ASYNC CB" }, + { 0x31000031, "DSRLP_APIS CB" }, + { 0x31000032, "RLP_APIS CB" }, + { 0x31000033, "DS_MP_SHIM_MODEM CB" }, + { 0x31000034, "DSHDR_APIS CB" }, + { 0x31000035, "DSHDR_MDM_APIS CB" }, + { 0x31000036, "DS_MP_SHIM_APPS CB" }, + { 0x31000037, "HDRMC_APIS CB" }, + { 0x31000038, "SMD_BRIDGE_MTOA CB" }, + { 0x31000039, "SMD_BRIDGE_ATOM CB" }, + { 0x3100003a, "DPMAPP_OTG CB" }, + { 0x3100003b, "DIAG CB" }, + { 0x3100003c, "GSTK_EXP CB" }, + { 0x3100003d, "DSBC_MDM_APIS CB" }, + { 0x3100003e, "HDRMRLP_MDM_APIS CB" }, + { 0x3100003f, "HDRMRLP_APPS_APIS CB" }, + { 0x31000040, "HDRMC_MRLP_APIS CB" }, + { 0x31000041, "PDCOMM_APP_API CB" }, + { 0x31000042, "DSAT_APIS CB" }, + { 0x31000043, "MISC_RF_APIS CB" }, + { 0x31000044, "CMIPAPP CB" }, + { 0x31000045, "DSMP_UMTS_MODEM_APIS CB" }, + { 0x31000046, "DSMP_UMTS_APPS_APIS CB" }, + { 0x31000047, "DSUCSDMPSHIM CB" }, + { 0x31000048, "TIME_REMOTE_ATOM CB" }, + { 0x3100004a, "SD CB" }, + { 0x3100004b, "MMOC CB" }, + { 0x3100004c, "WLAN_ADP_FTM CB" }, + { 0x3100004d, "WLAN_CP_CM CB" }, + { 0x3100004e, "FTM_WLAN CB" }, + { 0x3100004f, "SDCC_CPRM CB" }, + { 0x31000050, "CPRMINTERFACE CB" }, + { 0x31000051, "DATA_ON_MODEM_MTOA_APIS CB" }, + { 0x31000052, "DATA_ON_APPS_ATOM_APIS CB" }, + { 0x31000053, "MISC_APIS_NONWINMOB CB" }, + { 0x31000054, "MISC_APPS_APIS_NONWINMOB CB" }, + { 0x31000055, "PMEM_REMOTE CB" }, + { 0x31000056, "TCXOMGR CB" }, + { 0x31000057, "DSUCSDAPPIF_APIS CB" }, + { 0x31000058, "BT CB" }, + { 0x31000059, "PD_COMMS_API CB" }, + { 0x3100005a, "PD_COMMS_CLIENT_API CB" }, + { 0x3100005b, "PDAPI CB" }, + { 0x3100005c, "LSA_SUPL_DSM CB" }, + { 0x3100005d, "TIME_REMOTE_MTOA CB" }, + { 0x3100005e, "FTM_BT CB" }, + { 0X3100005f, "DSUCSDAPPIF_APIS CB" }, + { 0X31000060, "PMAPP_GEN CB" }, + { 0X31000061, "PM_LIB CB" }, + { 0X31000062, "KEYPAD CB" }, + { 0X31000063, "HSU_APP_APIS CB" }, + { 0X31000064, "HSU_MDM_APIS CB" }, + { 0X31000065, "ADIE_ADC_REMOTE_ATOM CB" }, + { 0X31000066, "TLMM_REMOTE_ATOM CB" }, + { 0X31000067, "UI_CALLCTRL CB" }, + { 0X31000068, "UIUTILS CB" }, + { 0X31000069, "PRL CB" }, + { 0X3100006a, "HW CB" }, + { 0X3100006b, "OEM_RAPI CB" }, + { 0X3100006c, "WMSPM CB" }, + { 0X3100006d, "BTPF CB" }, + { 0X3100006e, "CLKRGM_SYNC_EVENT CB" }, + { 0X3100006f, "USB_APPS_RPC CB" }, + { 0X31000070, "USB_MODEM_RPC CB" }, + { 0X31000071, "ADC CB" }, + { 0X31000072, "CAMERAREMOTED CB" }, + { 0X31000073, "SECAPIREMOTED CB" }, + { 0X31000074, "DSATAPI CB" }, + { 0X31000075, "CLKCTL_RPC CB" }, + { 0X31000076, "BREWAPPCOORD CB" }, + { 0X31000077, "ALTENVSHELL CB" }, + { 0X31000078, "WLAN_TRP_UTILS CB" }, + { 0X31000079, "GPIO_RPC CB" }, + { 0X3100007a, "PING_RPC CB" }, + { 0X3100007b, "DSC_DCM_API CB" }, + { 0X3100007c, "L1_DS CB" }, + { 0X3100007d, "QCHATPK_APIS CB" }, + { 0X3100007e, "GPS_API CB" }, + { 0X3100007f, "OSS_RRCASN_REMOTE CB" }, + { 0X31000080, "PMAPP_OTG_REMOTE CB" }, + { 0X31000081, "PING_MDM_RPC CB" }, + { 0X31000082, "PING_KERNEL_RPC CB" }, + { 0X31000083, "TIMETICK CB" }, + { 0X31000084, "WM_BTHCI_FTM CB" }, + { 0X31000085, "WM_BT_PF CB" }, + { 0X31000086, "IPA_IPC_APIS CB" }, + { 0X31000087, "UKCC_IPC_APIS CB" }, + { 0X31000088, "CMIPSMS CB" }, + { 0X31000089, "VBATT_REMOTE CB" }, + { 0X3100008a, "MFPAL CB" }, + { 0X3100008b, "DSUMTSPDPREG CB" }, + { 0X3100fe00, "RESTART_DAEMON NUMBER 0 CB" }, + { 0X3100fe01, "RESTART_DAEMON NUMBER 1 CB" }, + { 0X3100feff, "RESTART_DAEMON NUMBER 255 CB" }, + { 0X3100fffe, "BACKWARDS_COMPATIBILITY_IN_RPC_CLNT_LOOKUP CB" }, + { 0X3100ffff, "RPC_ROUTER_SERVER_PROGRAM CB" }, +}; + +struct sym wakeup_syms[] = { + { 0x00000040, "OTHER" }, + { 0x00000020, "RESET" }, + { 0x00000010, "ALARM" }, + { 0x00000008, "TIMER" }, + { 0x00000004, "GPIO" }, + { 0x00000002, "INT" }, + { 0x00000001, "RPC" }, + { 0x00000000, "NONE" }, +}; + +struct sym wakeup_int_syms[] = { + { 0, "MDDI_EXT" }, + { 1, "MDDI_PRI" }, + { 2, "MDDI_CLIENT"}, + { 3, "USB_OTG" }, + { 4, "I2CC" }, + { 5, "SDC1_0" }, + { 6, "SDC1_1" }, + { 7, "SDC2_0" }, + { 8, "SDC2_1" }, + { 9, "ADSP_A9A11" }, + { 10, "UART1" }, + { 11, "UART2" }, + { 12, "UART3" }, + { 13, "DP_RX_DATA" }, + { 14, "DP_RX_DATA2" }, + { 15, "DP_RX_DATA3" }, + { 16, "DM_UART" }, + { 17, "DM_DP_RX_DATA" }, + { 18, "KEYSENSE" }, + { 19, "HSSD" }, + { 20, "NAND_WR_ER_DONE" }, + { 21, "NAND_OP_DONE" }, + { 22, "TCHSCRN1" }, + { 23, "TCHSCRN2" }, + { 24, "TCHSCRN_SSBI" }, + { 25, "USB_HS" }, + { 26, "UART2_DM_RX" }, + { 27, "UART2_DM" }, + { 28, "SDC4_1" }, + { 29, "SDC4_0" }, + { 30, "SDC3_1" }, + { 31, "SDC3_0" }, +}; + +struct sym smsm_syms[] = { + { 0x80000000, "UN" }, + { 0x7F000000, "ERR" }, + { 0x00800000, "SMLP" }, + { 0x00400000, "ADWN" }, + { 0x00200000, "PWRS" }, + { 0x00100000, "DWLD" }, + { 0x00080000, "SRBT" }, + { 0x00040000, "SDWN" }, + { 0x00020000, "ARBT" }, + { 0x00010000, "REL" }, + { 0x00008000, "SLE" }, + { 0x00004000, "SLP" }, + { 0x00002000, "WFPI" }, + { 0x00001000, "EEX" }, + { 0x00000800, "TIN" }, + { 0x00000400, "TWT" }, + { 0x00000200, "PWRC" }, + { 0x00000100, "RUN" }, + { 0x00000080, "SA" }, + { 0x00000040, "RES" }, + { 0x00000020, "RIN" }, + { 0x00000010, "RWT" }, + { 0x00000008, "SIN" }, + { 0x00000004, "SWT" }, + { 0x00000002, "OE" }, + { 0x00000001, "I" }, +}; + +/* never reorder */ +struct sym voter_d2_syms[] = { + { 0x00000001, NULL }, + { 0x00000002, NULL }, + { 0x00000004, NULL }, + { 0x00000008, NULL }, + { 0x00000010, NULL }, + { 0x00000020, NULL }, + { 0x00000040, NULL }, + { 0x00000080, NULL }, + { 0x00000100, NULL }, + { 0x00000200, NULL }, + { 0x00000400, NULL }, + { 0x00000800, NULL }, + { 0x00001000, NULL }, + { 0x00002000, NULL }, + { 0x00004000, NULL }, + { 0x00008000, NULL }, + { 0x00010000, NULL }, + { 0x00020000, NULL }, + { 0x00040000, NULL }, + { 0x00080000, NULL }, + { 0x00100000, NULL }, + { 0x00200000, NULL }, + { 0x00400000, NULL }, + { 0x00800000, NULL }, + { 0x01000000, NULL }, + { 0x02000000, NULL }, + { 0x04000000, NULL }, + { 0x08000000, NULL }, + { 0x10000000, NULL }, + { 0x20000000, NULL }, + { 0x40000000, NULL }, + { 0x80000000, NULL }, +}; + +/* never reorder */ +struct sym voter_d3_syms[] = { + { 0x00000001, NULL }, + { 0x00000002, NULL }, + { 0x00000004, NULL }, + { 0x00000008, NULL }, + { 0x00000010, NULL }, + { 0x00000020, NULL }, + { 0x00000040, NULL }, + { 0x00000080, NULL }, + { 0x00000100, NULL }, + { 0x00000200, NULL }, + { 0x00000400, NULL }, + { 0x00000800, NULL }, + { 0x00001000, NULL }, + { 0x00002000, NULL }, + { 0x00004000, NULL }, + { 0x00008000, NULL }, + { 0x00010000, NULL }, + { 0x00020000, NULL }, + { 0x00040000, NULL }, + { 0x00080000, NULL }, + { 0x00100000, NULL }, + { 0x00200000, NULL }, + { 0x00400000, NULL }, + { 0x00800000, NULL }, + { 0x01000000, NULL }, + { 0x02000000, NULL }, + { 0x04000000, NULL }, + { 0x08000000, NULL }, + { 0x10000000, NULL }, + { 0x20000000, NULL }, + { 0x40000000, NULL }, + { 0x80000000, NULL }, +}; + +struct sym dem_state_master_syms[] = { + { 0, "INIT" }, + { 1, "RUN" }, + { 2, "SLEEP_WAIT" }, + { 3, "SLEEP_CONFIRMED" }, + { 4, "SLEEP_EXIT" }, + { 5, "RSA" }, + { 6, "EARLY_EXIT" }, + { 7, "RSA_DELAYED" }, + { 8, "RSA_CHECK_INTS" }, + { 9, "RSA_CONFIRMED" }, + { 10, "RSA_WAKING" }, + { 11, "RSA_RESTORE" }, + { 12, "RESET" }, +}; + +struct sym dem_state_slave_syms[] = { + { 0, "INIT" }, + { 1, "RUN" }, + { 2, "SLEEP_WAIT" }, + { 3, "SLEEP_EXIT" }, + { 4, "SLEEP_RUN_PENDING" }, + { 5, "POWER_COLLAPSE" }, + { 6, "CHECK_INTERRUPTS" }, + { 7, "SWFI" }, + { 8, "WFPI" }, + { 9, "EARLY_EXIT" }, + { 10, "RESET_RECOVER" }, + { 11, "RESET_ACKNOWLEDGE" }, + { 12, "ERROR" }, +}; + +struct sym smsm_entry_type_syms[] = { + { 0, "SMSM_APPS_STATE" }, + { 1, "SMSM_MODEM_STATE" }, + { 2, "SMSM_Q6_STATE" }, + { 3, "SMSM_APPS_DEM" }, + { 4, "SMSM_MODEM_DEM" }, + { 5, "SMSM_Q6_DEM" }, + { 6, "SMSM_POWER_MASTER_DEM" }, + { 7, "SMSM_TIME_MASTER_DEM" }, +}; + +struct sym smsm_state_syms[] = { + { 0x00000001, "INIT" }, + { 0x00000002, "OSENTERED" }, + { 0x00000004, "SMDWAIT" }, + { 0x00000008, "SMDINIT" }, + { 0x00000010, "RPCWAIT" }, + { 0x00000020, "RPCINIT" }, + { 0x00000040, "RESET" }, + { 0x00000080, "RSA" }, + { 0x00000100, "RUN" }, + { 0x00000200, "PWRC" }, + { 0x00000400, "TIMEWAIT" }, + { 0x00000800, "TIMEINIT" }, + { 0x00001000, "PWRC_EARLY_EXIT" }, + { 0x00002000, "WFPI" }, + { 0x00004000, "SLEEP" }, + { 0x00008000, "SLEEPEXIT" }, + { 0x00010000, "OEMSBL_RELEASE" }, + { 0x00020000, "APPS_REBOOT" }, + { 0x00040000, "SYSTEM_POWER_DOWN" }, + { 0x00080000, "SYSTEM_REBOOT" }, + { 0x00100000, "SYSTEM_DOWNLOAD" }, + { 0x00200000, "PWRC_SUSPEND" }, + { 0x00400000, "APPS_SHUTDOWN" }, + { 0x00800000, "SMD_LOOPBACK" }, + { 0x01000000, "RUN_QUIET" }, + { 0x02000000, "MODEM_WAIT" }, + { 0x04000000, "MODEM_BREAK" }, + { 0x08000000, "MODEM_CONTINUE" }, + { 0x80000000, "UNKNOWN" }, +}; + +#define ID_SYM 0 +#define BASE_SYM 1 +#define EVENT_SYM 2 +#define ONCRPC_SYM 3 +#define WAKEUP_SYM 4 +#define WAKEUP_INT_SYM 5 +#define SMSM_SYM 6 +#define VOTER_D2_SYM 7 +#define VOTER_D3_SYM 8 +#define DEM_STATE_MASTER_SYM 9 +#define DEM_STATE_SLAVE_SYM 10 +#define SMSM_ENTRY_TYPE_SYM 11 +#define SMSM_STATE_SYM 12 + +static struct sym_tbl { + struct sym *data; + int size; + struct hlist_head hlist[HSIZE]; +} tbl[] = { + { id_syms, ARRAY_SIZE(id_syms) }, + { base_syms, ARRAY_SIZE(base_syms) }, + { event_syms, ARRAY_SIZE(event_syms) }, + { oncrpc_syms, ARRAY_SIZE(oncrpc_syms) }, + { wakeup_syms, ARRAY_SIZE(wakeup_syms) }, + { wakeup_int_syms, ARRAY_SIZE(wakeup_int_syms) }, + { smsm_syms, ARRAY_SIZE(smsm_syms) }, + { voter_d2_syms, ARRAY_SIZE(voter_d2_syms) }, + { voter_d3_syms, ARRAY_SIZE(voter_d3_syms) }, + { dem_state_master_syms, ARRAY_SIZE(dem_state_master_syms) }, + { dem_state_slave_syms, ARRAY_SIZE(dem_state_slave_syms) }, + { smsm_entry_type_syms, ARRAY_SIZE(smsm_entry_type_syms) }, + { smsm_state_syms, ARRAY_SIZE(smsm_state_syms) }, +}; + +static void find_voters(void) +{ + void *x, *next; + unsigned size; + int i = 0, j = 0; + + x = smem_item(SMEM_SLEEP_STATIC, &size); + next = x; + while (next && (next < (x + size)) && + ((i + j) < (ARRAY_SIZE(voter_d3_syms) + + ARRAY_SIZE(voter_d2_syms)))) { + + if (i < ARRAY_SIZE(voter_d3_syms)) { + voter_d3_syms[i].str = (char *) next; + i++; + } else if (i >= ARRAY_SIZE(voter_d3_syms) && + j < ARRAY_SIZE(voter_d2_syms)) { + voter_d2_syms[j].str = (char *) next; + j++; + } + + next += 9; + } +} + +#define hash(val) (val % HSIZE) + +static void init_syms(void) +{ + int i; + int j; + + for (i = 0; i < ARRAY_SIZE(tbl); ++i) + for (j = 0; j < HSIZE; ++j) + INIT_HLIST_HEAD(&tbl[i].hlist[j]); + + for (i = 0; i < ARRAY_SIZE(tbl); ++i) + for (j = 0; j < tbl[i].size; ++j) { + INIT_HLIST_NODE(&tbl[i].data[j].node); + hlist_add_head(&tbl[i].data[j].node, + &tbl[i].hlist[hash(tbl[i].data[j].val)]); + } +} + +static char *find_sym(uint32_t id, uint32_t val) +{ + struct hlist_node *n; + struct sym *s; + + hlist_for_each(n, &tbl[id].hlist[hash(val)]) { + s = hlist_entry(n, struct sym, node); + if (s->val == val) + return s->str; + } + + return 0; +} + +#else +static void init_syms(void) {} +#endif + +static inline unsigned int read_timestamp(void) +{ + unsigned int tick; + + do { + tick = readl(TIMESTAMP_ADDR); + } while (tick != (tick = readl(TIMESTAMP_ADDR))); + + return tick; +} + +static void smem_log_event_from_user(struct smem_log_inst *inst, + const char __user *buf, int size, int num) +{ + uint32_t idx; + uint32_t next_idx; + unsigned long flags; + uint32_t identifier = 0; + uint32_t timetick = 0; + int first = 1; + int ret; + + remote_spin_lock_irqsave(inst->remote_spinlock, flags); + + while (num--) { + idx = *inst->idx; + + if (idx < inst->num) { + ret = copy_from_user(&inst->events[idx], + buf, size); + if (ret) { + printk("ERROR %s:%i tried to write " + "%i got ret %i", + __func__, __LINE__, + size, size - ret); + goto out; + } + + if (first) { + identifier = + inst->events[idx]. + identifier; + timetick = read_timestamp(); + first = 0; + } else { + identifier |= SMEM_LOG_CONT; + } + inst->events[idx].identifier = + identifier; + inst->events[idx].timetick = + timetick; + } + + next_idx = idx + 1; + if (next_idx >= inst->num) + next_idx = 0; + *inst->idx = next_idx; + + buf += sizeof(struct smem_log_item); + } + + out: + remote_spin_unlock_irqrestore(inst->remote_spinlock, flags); +} + +static void _smem_log_event( + struct smem_log_item __iomem *events, + uint32_t __iomem *_idx, + remote_spinlock_t *lock, + int num, + uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3) +{ + struct smem_log_item item; + uint32_t idx; + uint32_t next_idx; + unsigned long flags; + + item.timetick = read_timestamp(); + item.identifier = id; + item.data1 = data1; + item.data2 = data2; + item.data3 = data3; + + remote_spin_lock_irqsave(lock, flags); + + idx = *_idx; + + if (idx < num) { + memcpy(&events[idx], + &item, sizeof(item)); + } + + next_idx = idx + 1; + if (next_idx >= num) + next_idx = 0; + *_idx = next_idx; + + remote_spin_unlock_irqrestore(lock, flags); +} + +static void _smem_log_event6( + struct smem_log_item __iomem *events, + uint32_t __iomem *_idx, + remote_spinlock_t *lock, + int num, + uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6) +{ + struct smem_log_item item[2]; + uint32_t idx; + uint32_t next_idx; + unsigned long flags; + + item[0].timetick = read_timestamp(); + item[0].identifier = id; + item[0].data1 = data1; + item[0].data2 = data2; + item[0].data3 = data3; + item[1].identifier = item[0].identifier; + item[1].timetick = item[0].timetick; + item[1].data1 = data4; + item[1].data2 = data5; + item[1].data3 = data6; + + remote_spin_lock_irqsave(lock, flags); + + idx = *_idx; + + if (idx < (num-1)) { + memcpy(&events[idx], + &item, sizeof(item)); + } + + next_idx = idx + 2; + if (next_idx >= num) + next_idx = 0; + *_idx = next_idx; + + remote_spin_unlock_irqrestore(lock, flags); +} + +void smem_log_event(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3) +{ + _smem_log_event(inst[GEN].events, inst[GEN].idx, + inst[GEN].remote_spinlock, SMEM_LOG_NUM_ENTRIES, + id, data1, data2, data3); +} + +void smem_log_event6(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6) +{ + _smem_log_event6(inst[GEN].events, inst[GEN].idx, + inst[GEN].remote_spinlock, SMEM_LOG_NUM_ENTRIES, + id, data1, data2, data3, data4, data5, data6); +} + +void smem_log_event_to_static(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3) +{ + _smem_log_event(inst[STA].events, inst[STA].idx, + inst[STA].remote_spinlock, SMEM_LOG_NUM_STATIC_ENTRIES, + id, data1, data2, data3); +} + +void smem_log_event6_to_static(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6) +{ + _smem_log_event6(inst[STA].events, inst[STA].idx, + inst[STA].remote_spinlock, SMEM_LOG_NUM_STATIC_ENTRIES, + id, data1, data2, data3, data4, data5, data6); +} + +static int _smem_log_init(void) +{ + inst[GEN].which_log = GEN; + inst[GEN].events = + (struct smem_log_item *)smem_alloc(SMEM_SMEM_LOG_EVENTS, + SMEM_LOG_EVENTS_SIZE); + inst[GEN].idx = (uint32_t *)smem_alloc(SMEM_SMEM_LOG_IDX, + sizeof(uint32_t)); + if (!inst[GEN].events || !inst[GEN].idx) { + pr_err("%s: no log or log_idx allocated, " + "smem_log disabled\n", __func__); + } + inst[GEN].num = SMEM_LOG_NUM_ENTRIES; + inst[GEN].remote_spinlock = &remote_spinlock; + + inst[STA].which_log = STA; + inst[STA].events = + (struct smem_log_item *) + smem_alloc(SMEM_SMEM_STATIC_LOG_EVENTS, + SMEM_STATIC_LOG_EVENTS_SIZE); + inst[STA].idx = (uint32_t *)smem_alloc(SMEM_SMEM_STATIC_LOG_IDX, + sizeof(uint32_t)); + if (!inst[STA].events || !inst[STA].idx) { + pr_err("%s: no static log or log_idx " + "allocated, smem_log disabled\n", __func__); + } + inst[STA].num = SMEM_LOG_NUM_STATIC_ENTRIES; + inst[STA].remote_spinlock = &remote_spinlock_static; + + inst[POW].which_log = POW; +#ifdef CONFIG_MSM_N_WAY_SMD + + inst[POW].events = + (struct smem_log_item *) + smem_alloc(SMEM_SMEM_LOG_POWER_EVENTS, + SMEM_POWER_LOG_EVENTS_SIZE); + inst[POW].idx = (uint32_t *)smem_alloc(SMEM_SMEM_LOG_POWER_IDX, + sizeof(uint32_t)); +#else + inst[POW].events = NULL; + inst[POW].idx = NULL; +#endif + if (!inst[POW].events || !inst[POW].idx) { + pr_err("%s: no power log or log_idx " + "allocated, smem_log disabled\n", __func__); + } + inst[POW].num = SMEM_LOG_NUM_POWER_ENTRIES; + inst[POW].remote_spinlock = &remote_spinlock; + + remote_spin_lock_init(&remote_spinlock, + SMEM_SPINLOCK_SMEM_LOG); + remote_spin_lock_init(&remote_spinlock_static, + SMEM_SPINLOCK_STATIC_LOG); + + init_syms(); + + return 0; +} + +static ssize_t smem_log_read_bin(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + int idx; + int orig_idx; + unsigned long flags; + int ret; + int tot_bytes = 0; + struct smem_log_inst *inst; + + inst = fp->private_data; + + remote_spin_lock_irqsave(inst->remote_spinlock, flags); + + orig_idx = *inst->idx; + idx = orig_idx; + + while (1) { + idx--; + if (idx < 0) + idx = inst->num - 1; + if (idx == orig_idx) { + ret = tot_bytes; + break; + } + + if ((tot_bytes + sizeof(struct smem_log_item)) > count) { + ret = tot_bytes; + break; + } + + ret = copy_to_user(buf, &inst[GEN].events[idx], + sizeof(struct smem_log_item)); + if (ret) { + ret = -EIO; + break; + } + + tot_bytes += sizeof(struct smem_log_item); + + buf += sizeof(struct smem_log_item); + } + + remote_spin_unlock_irqrestore(inst->remote_spinlock, flags); + + return ret; +} + +static ssize_t smem_log_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + char loc_buf[128]; + int i; + int idx; + int orig_idx; + unsigned long flags; + int ret; + int tot_bytes = 0; + struct smem_log_inst *inst; + + inst = fp->private_data; + + remote_spin_lock_irqsave(inst->remote_spinlock, flags); + + orig_idx = *inst->idx; + idx = orig_idx; + + while (1) { + idx--; + if (idx < 0) + idx = inst->num - 1; + if (idx == orig_idx) { + ret = tot_bytes; + break; + } + + i = scnprintf(loc_buf, 128, + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + inst->events[idx].identifier, + inst->events[idx].timetick, + inst->events[idx].data1, + inst->events[idx].data2, + inst->events[idx].data3); + if (i == 0) { + ret = -EIO; + break; + } + + if ((tot_bytes + i) > count) { + ret = tot_bytes; + break; + } + + tot_bytes += i; + + ret = copy_to_user(buf, loc_buf, i); + if (ret) { + ret = -EIO; + break; + } + + buf += i; + } + + remote_spin_unlock_irqrestore(inst->remote_spinlock, flags); + + return ret; +} + +static ssize_t smem_log_write_bin(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + if (count < sizeof(struct smem_log_item)) + return -EINVAL; + + smem_log_event_from_user(fp->private_data, buf, + sizeof(struct smem_log_item), + count / sizeof(struct smem_log_item)); + + return count; +} + +static ssize_t smem_log_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + int ret; + const char delimiters[] = " ,;"; + char locbuf[256] = {0}; + uint32_t val[10]; + int vals = 0; + char *token; + char *running; + struct smem_log_inst *inst; + unsigned long res; + + inst = fp->private_data; + + if (count < 0) { + printk(KERN_ERR "ERROR: %s passed neg count = %i\n", + __func__, count); + return -EINVAL; + } + + count = count > 255 ? 255 : count; + + locbuf[count] = '\0'; + + ret = copy_from_user(locbuf, buf, count); + if (ret != 0) { + printk(KERN_ERR "ERROR: %s could not copy %i bytes\n", + __func__, ret); + return -EINVAL; + } + + D(KERN_ERR "%s: ", __func__); + D_DUMP_BUFFER("We got", len, locbuf); + + running = locbuf; + + token = strsep(&running, delimiters); + while (token && vals < ARRAY_SIZE(val)) { + if (*token != '\0') { + D(KERN_ERR "%s: ", __func__); + D_DUMP_BUFFER("", strlen(token), token); + ret = strict_strtoul(token, 0, &res); + if (ret) { + printk(KERN_ERR "ERROR: %s:%i got bad char " + "at strict_strtoul\n", + __func__, __LINE__-4); + return -EINVAL; + } + val[vals++] = res; + } + token = strsep(&running, delimiters); + } + + if (vals > 5) { + if (inst->which_log == GEN) + smem_log_event6(val[0], val[2], val[3], val[4], + val[7], val[8], val[9]); + else if (inst->which_log == STA) + smem_log_event6_to_static(val[0], + val[2], val[3], val[4], + val[7], val[8], val[9]); + else + return -1; + } else { + if (inst->which_log == GEN) + smem_log_event(val[0], val[2], val[3], val[4]); + else if (inst->which_log == STA) + smem_log_event_to_static(val[0], + val[2], val[3], val[4]); + else + return -1; + } + + return count; +} + +static int smem_log_open(struct inode *ip, struct file *fp) +{ + fp->private_data = &inst[GEN]; + + return 0; +} + + +static int smem_log_release(struct inode *ip, struct file *fp) +{ + return 0; +} + +static int smem_log_ioctl(struct inode *ip, struct file *fp, + unsigned int cmd, unsigned long arg); + +static const struct file_operations smem_log_fops = { + .owner = THIS_MODULE, + .read = smem_log_read, + .write = smem_log_write, + .open = smem_log_open, + .release = smem_log_release, + .unlocked_ioctl = smem_log_ioctl, +}; + +static const struct file_operations smem_log_bin_fops = { + .owner = THIS_MODULE, + .read = smem_log_read_bin, + .write = smem_log_write_bin, + .open = smem_log_open, + .release = smem_log_release, + .unlocked_ioctl = smem_log_ioctl, +}; + +static int smem_log_ioctl(struct inode *ip, struct file *fp, + unsigned int cmd, unsigned long arg) +{ + struct smem_log_inst *inst; + + inst = fp->private_data; + + switch (cmd) { + default: + return -ENOTTY; + + case SMIOC_SETMODE: + if (arg == SMIOC_TEXT) { + D("%s set text mode\n", __func__); + fp->f_op = &smem_log_fops; + } else if (arg == SMIOC_BINARY) { + D("%s set bin mode\n", __func__); + fp->f_op = &smem_log_bin_fops; + } else { + return -EINVAL; + } + break; + case SMIOC_SETLOG: + if (arg == SMIOC_LOG) + fp->private_data = &inst[GEN]; + else if (arg == SMIOC_STATIC_LOG) + fp->private_data = &inst[STA]; + else + return -EINVAL; + break; + } + + return 0; +} + +static struct miscdevice smem_log_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "smem_log", + .fops = &smem_log_fops, +}; + +#if defined(CONFIG_DEBUG_FS) + +static int _debug_dump(int log, char *buf, int max) +{ + unsigned int idx; + int orig_idx; + unsigned long flags; + int i = 0; + + if (!inst[log].events) + return 0; + + remote_spin_lock_irqsave(inst[log].remote_spinlock, flags); + + orig_idx = *inst[log].idx; + idx = orig_idx; + + while (1) { + idx++; + if (idx > inst[log].num - 1) + idx = 0; + if (idx == orig_idx) + break; + + if (idx < inst[log].num) { + if (!inst[log].events[idx].identifier) + continue; + + i += scnprintf(buf + i, max - i, + "%08x %08x %08x %08x %08x\n", + inst[log].events[idx].identifier, + inst[log].events[idx].timetick, + inst[log].events[idx].data1, + inst[log].events[idx].data2, + inst[log].events[idx].data3); + } + } + + remote_spin_unlock_irqrestore(inst[log].remote_spinlock, flags); + + return i; +} + +static int _debug_dump_sym(int log, char *buf, int max) +{ + unsigned int idx; + int orig_idx; + unsigned long flags; + int i = 0; + + char *proc; + char *sub; + char *id; + char *sym = NULL; + + uint32_t data[3]; + + uint32_t proc_val = 0; + uint32_t sub_val = 0; + uint32_t id_val = 0; + uint32_t id_only_val = 0; + uint32_t data1 = 0; + uint32_t data2 = 0; + uint32_t data3 = 0; + + int k; + + if (!inst[log].events) + return 0; + + find_voters(); /* need to call each time in case voters come and go */ + + i += scnprintf(buf + i, max - i, "Voters:\n"); + for (k = 0; k < ARRAY_SIZE(voter_d3_syms); ++k) + if (voter_d3_syms[k].str) + i += scnprintf(buf + i, max - i, "%s ", + voter_d3_syms[k].str); + for (k = 0; k < ARRAY_SIZE(voter_d2_syms); ++k) + if (voter_d2_syms[k].str) + i += scnprintf(buf + i, max - i, "%s ", + voter_d2_syms[k].str); + i += scnprintf(buf + i, max - i, "\n"); + + remote_spin_lock_irqsave(inst[log].remote_spinlock, flags); + + orig_idx = *inst[log].idx; + idx = orig_idx; + + while (1) { + idx++; + if (idx > inst[log].num - 1) + idx = 0; + if (idx == orig_idx) { + i += scnprintf(buf + i, max - i, "\n"); + break; + } + if (idx < inst[log].num) { + if (!inst[log].events[idx].identifier) + continue; + + proc_val = PROC & inst[log].events[idx].identifier; + sub_val = SUB & inst[log].events[idx].identifier; + id_val = (SUB | ID) & inst[log].events[idx].identifier; + id_only_val = ID & inst[log].events[idx].identifier; + data1 = inst[log].events[idx].data1; + data2 = inst[log].events[idx].data2; + data3 = inst[log].events[idx].data3; + + if (!(proc_val & SMEM_LOG_CONT)) { + i += scnprintf(buf + i, max - i, "\n"); + + proc = find_sym(ID_SYM, proc_val); + + if (proc) + i += scnprintf(buf + i, max - i, + "%4s: ", + proc); + else + i += scnprintf(buf + i, max - i, + "%04x: ", + PROC & + inst[log].events[idx]. + identifier); + + i += scnprintf(buf + i, max - i, + "%10u ", + inst[log].events[idx].timetick); + + sub = find_sym(BASE_SYM, sub_val); + + if (sub) + i += scnprintf(buf + i, max - i, + "%9s: ", + sub); + else + i += scnprintf(buf + i, max - i, + "%08x: ", + sub_val); + + id = find_sym(EVENT_SYM, id_val); + + if (id) + i += scnprintf(buf + i, max - i, + "%11s: ", + id); + else + i += scnprintf(buf + i, max - i, + "%08x: ", + id_only_val); + } + + if ((proc_val & SMEM_LOG_CONT) && + (id_val == ONCRPC_LOG_EVENT_STD_CALL || + id_val == ONCRPC_LOG_EVENT_STD_REPLY)) { + data[0] = data1; + data[1] = data2; + data[2] = data3; + i += scnprintf(buf + i, max - i, + " %.16s", + (char *) data); + } else if (proc_val & SMEM_LOG_CONT) { + i += scnprintf(buf + i, max - i, + " %08x %08x %08x", + data1, + data2, + data3); + } else if (id_val == ONCRPC_LOG_EVENT_STD_CALL) { + sym = find_sym(ONCRPC_SYM, data2); + + if (sym) + i += scnprintf(buf + i, max - i, + "xid:%4i %8s proc:%3i", + data1, + sym, + data3); + else + i += scnprintf(buf + i, max - i, + "xid:%4i %08x proc:%3i", + data1, + data2, + data3); +#if defined(CONFIG_MSM_N_WAY_SMSM) + } else if (id_val == DEM_STATE_CHANGE) { + if (data1 == 1) { + i += scnprintf(buf + i, + max - i, + "MASTER: "); + sym = find_sym(DEM_STATE_MASTER_SYM, + data2); + } else if (data1 == 0) { + i += scnprintf(buf + i, + max - i, + " SLAVE: "); + sym = find_sym(DEM_STATE_SLAVE_SYM, + data2); + } else { + i += scnprintf(buf + i, + max - i, + "%x: ", + data1); + sym = NULL; + } + if (sym) + i += scnprintf(buf + i, + max - i, + "from:%s ", + sym); + else + i += scnprintf(buf + i, + max - i, + "from:0x%x ", + data2); + + if (data1 == 1) + sym = find_sym(DEM_STATE_MASTER_SYM, + data3); + else if (data1 == 0) + sym = find_sym(DEM_STATE_SLAVE_SYM, + data3); + else + sym = NULL; + if (sym) + i += scnprintf(buf + i, + max - i, + "to:%s ", + sym); + else + i += scnprintf(buf + i, + max - i, + "to:0x%x ", + data3); + + } else if (id_val == DEM_STATE_MACHINE_ENTER) { + i += scnprintf(buf + i, + max - i, + "swfi:%i timer:%i manexit:%i", + data1, data2, data3); + + } else if (id_val == DEM_TIME_SYNC_REQUEST || + id_val == DEM_TIME_SYNC_POLL || + id_val == DEM_TIME_SYNC_INIT) { + sym = find_sym(SMSM_ENTRY_TYPE_SYM, + data1); + if (sym) + i += scnprintf(buf + i, + max - i, + "hostid:%s", + sym); + else + i += scnprintf(buf + i, + max - i, + "hostid:%x", + data1); + + } else if (id_val == DEM_TIME_SYNC_START || + id_val == DEM_TIME_SYNC_SEND_VALUE) { + unsigned mask = 0x1; + unsigned tmp = 0; + if (id_val == DEM_TIME_SYNC_START) + i += scnprintf(buf + i, + max - i, + "req:"); + else + i += scnprintf(buf + i, + max - i, + "pol:"); + while (mask) { + if (mask & data1) { + sym = find_sym( + SMSM_ENTRY_TYPE_SYM, + tmp); + if (sym) + i += scnprintf(buf + i, + max - i, + "%s ", + sym); + else + i += scnprintf(buf + i, + max - i, + "%i ", + tmp); + } + mask <<= 1; + tmp++; + } + if (id_val == DEM_TIME_SYNC_SEND_VALUE) + i += scnprintf(buf + i, + max - i, + "tick:%x", + data2); + } else if (id_val == DEM_SMSM_ISR) { + unsigned vals[] = {data2, data3}; + unsigned j; + unsigned mask; + unsigned tmp; + unsigned once; + sym = find_sym(SMSM_ENTRY_TYPE_SYM, + data1); + if (sym) + i += scnprintf(buf + i, + max - i, + "%s ", + sym); + else + i += scnprintf(buf + i, + max - i, + "%x ", + data1); + + for (j = 0; j < ARRAY_SIZE(vals); ++j) { + i += scnprintf(buf + i, max - i, "["); + mask = 0x80000000; + once = 0; + while (mask) { + tmp = vals[j] & mask; + mask >>= 1; + if (!tmp) + continue; + sym = find_sym(SMSM_STATE_SYM, + tmp); + + if (once) + i += scnprintf(buf + i, + max - i, + " "); + if (sym) + i += scnprintf(buf + i, + max - i, + "%s", + sym); + else + i += scnprintf(buf + i, + max - i, + "0x%x", + tmp); + once = 1; + } + i += scnprintf(buf + i, max - i, "] "); + } +#else + } else if (id_val == DEMAPPS_WAKEUP_REASON) { + unsigned mask = 0x80000000; + unsigned tmp = 0; + while (mask) { + tmp = data1 & mask; + mask >>= 1; + if (!tmp) + continue; + sym = find_sym(WAKEUP_SYM, tmp); + if (sym) + i += scnprintf(buf + i, + max - i, + "%s ", + sym); + else + i += scnprintf(buf + i, + max - i, + "%08x ", + tmp); + } + i += scnprintf(buf + i, max - i, + "%08x %08x", + data2, + data3); + } else if (id_val == DEMMOD_APPS_WAKEUP_INT) { + sym = find_sym(WAKEUP_INT_SYM, data1); + + if (sym) + i += scnprintf(buf + i, max - i, + "%s %08x %08x", + sym, + data2, + data3); + else + i += scnprintf(buf + i, max - i, + "%08x %08x %08x", + data1, + data2, + data3); + } else if (id_val == DEM_NO_SLEEP || + id_val == NO_SLEEP_NEW) { + unsigned vals[] = {data3, data2}; + unsigned j; + unsigned mask; + unsigned tmp; + unsigned once; + i += scnprintf(buf + i, max - i, "%08x ", + data1); + i += scnprintf(buf + i, max - i, "["); + once = 0; + for (j = 0; j < ARRAY_SIZE(vals); ++j) { + mask = 0x00000001; + while (mask) { + tmp = vals[j] & mask; + mask <<= 1; + if (!tmp) + continue; + if (j == 0) + sym = find_sym( + VOTER_D3_SYM, + tmp); + else + sym = find_sym( + VOTER_D2_SYM, + tmp); + + if (once) + i += scnprintf(buf + i, + max - i, + " "); + if (sym) + i += scnprintf(buf + i, + max - i, + "%s", + sym); + else + i += scnprintf(buf + i, + max - i, + "%08x", + tmp); + once = 1; + } + } + i += scnprintf(buf + i, max - i, "] "); +#endif + } else if (id_val == SMEM_LOG_EVENT_CB) { + unsigned vals[] = {data2, data3}; + unsigned j; + unsigned mask; + unsigned tmp; + unsigned once; + i += scnprintf(buf + i, max - i, "%08x ", + data1); + for (j = 0; j < ARRAY_SIZE(vals); ++j) { + i += scnprintf(buf + i, max - i, "["); + mask = 0x80000000; + once = 0; + while (mask) { + tmp = vals[j] & mask; + mask >>= 1; + if (!tmp) + continue; + sym = find_sym(SMSM_SYM, tmp); + + if (once) + i += scnprintf(buf + i, + max - i, + " "); + if (sym) + i += scnprintf(buf + i, + max - i, + "%s", + sym); + else + i += scnprintf(buf + i, + max - i, + "%08x", + tmp); + once = 1; + } + i += scnprintf(buf + i, max - i, "] "); + } + } else { + i += scnprintf(buf + i, max - i, + "%08x %08x %08x", + data1, + data2, + data3); + } + } + } + + remote_spin_unlock_irqrestore(inst[log].remote_spinlock, flags); + + return i; +} + +static int debug_dump(char *buf, int max) +{ + return _debug_dump(GEN, buf, max); +} + +static int debug_dump_sym(char *buf, int max) +{ + return _debug_dump_sym(GEN, buf, max); +} + +static int debug_dump_static(char *buf, int max) +{ + return _debug_dump(STA, buf, max); +} + +static int debug_dump_static_sym(char *buf, int max) +{ + return _debug_dump_sym(STA, buf, max); +} + +static int debug_dump_power(char *buf, int max) +{ + return _debug_dump(POW, buf, max); +} + +static int debug_dump_power_sym(char *buf, int max) +{ + return _debug_dump_sym(POW, buf, max); +} + +#define SMEM_LOG_ITEM_PRINT_SIZE 160 + +#define EVENTS_PRINT_SIZE \ +(SMEM_LOG_ITEM_PRINT_SIZE * SMEM_LOG_NUM_ENTRIES) + +static char debug_buffer[EVENTS_PRINT_SIZE]; + +static ssize_t debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int (*fill)(char *buf, int max) = file->private_data; + int bsize = fill(debug_buffer, EVENTS_PRINT_SIZE); + return simple_read_from_buffer(buf, count, ppos, debug_buffer, + bsize); +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations debug_ops = { + .read = debug_read, + .open = debug_open, +}; + +static void debug_create(const char *name, mode_t mode, + struct dentry *dent, + int (*fill)(char *buf, int max)) +{ + debugfs_create_file(name, mode, dent, fill, &debug_ops); +} + +static void smem_log_debugfs_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("smem_log", 0); + if (IS_ERR(dent)) + return; + + debug_create("dump", 0444, dent, debug_dump); + debug_create("dump_sym", 0444, dent, debug_dump_sym); + debug_create("dump_static", 0444, dent, debug_dump_static); + debug_create("dump_static_sym", 0444, dent, debug_dump_static_sym); + debug_create("dump_power", 0444, dent, debug_dump_power); + debug_create("dump_power_sym", 0444, dent, debug_dump_power_sym); +} +#else +static void smem_log_debugfs_init(void) {} +#endif + +static int __init smem_log_init(void) +{ + int ret; + + ret = _smem_log_init(); + if (ret < 0) + return ret; + + smem_log_debugfs_init(); + + return misc_register(&smem_log_dev); +} + + +module_init(smem_log_init); + +MODULE_DESCRIPTION("smem log"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 27fba29e570af..d516ade38e33f 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -30,6 +30,7 @@ else obj-$(CONFIG_USB_MSM_72K) += msm72k_udc.o endif + # # USB gadget drivers # diff --git a/drivers/usb/gadget/msm72k_udc_htc.c b/drivers/usb/gadget/msm72k_udc_htc.c index fc7c71f2ca52a..80905c54b1463 100644 --- a/drivers/usb/gadget/msm72k_udc_htc.c +++ b/drivers/usb/gadget/msm72k_udc_htc.c @@ -1717,13 +1717,6 @@ static void usb_lpm_enter(struct usb_info *ui) clk_set_rate(ui->ebi1clk, 0); ui->in_lpm = 1; spin_unlock_irqrestore(&ui->lock, iflags); - - if (board_mfg_mode() == 1) {/*for MFG adb unstable in FROYO ROM*/ - printk(KERN_INFO "usb: idle_wake_unlock and perf unlock\n"); - wake_unlock(&vbus_idle_wake_lock); - if (is_perf_lock_active(&usb_perf_lock)) - perf_unlock(&usb_perf_lock); - } } static void usb_lpm_exit(struct usb_info *ui) @@ -1741,13 +1734,6 @@ static void usb_lpm_exit(struct usb_info *ui) clk_enable(ui->otgclk); usb_wakeup_phy(ui); ui->in_lpm = 0; - - if (board_mfg_mode() == 1) {/*for MFG adb unstable in FROYO ROM*/ - printk(KERN_INFO "usb: idle_wake_lock and perf lock\n"); - wake_lock(&vbus_idle_wake_lock); - if (!is_perf_lock_active(&usb_perf_lock)) - perf_lock(&usb_perf_lock); - } } #ifdef CONFIG_DOCK_ACCESSORY_DETECT @@ -2760,7 +2746,7 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, int (*bind)(struct if (!driver || driver->speed < USB_SPEED_FULL - || !bind + || !bind || !driver->disconnect || !driver->setup) return -EINVAL; diff --git a/include/linux/i2c-msm.h b/include/linux/i2c-msm.h new file mode 100644 index 0000000000000..14bf3eb5a1e5a --- /dev/null +++ b/include/linux/i2c-msm.h @@ -0,0 +1,26 @@ +/* + * include/linux/i2c-msm.h - platform data structure for i2c controller + * + * Copyright (C) 2009 HTC. + * + * 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 _I2C_MSM_H +#define _I2C_MSM_H + +struct msm_i2c_device_platform_data { + int i2c_clock; + int clock_strength; + int data_strength; +}; + +#endif /* _I2C_MSM_H */ From 5a0857e0d8fe212e57f8fba3eaa7363199bcf81b Mon Sep 17 00:00:00 2001 From: Tony Layher Date: Sun, 6 Feb 2011 02:10:35 -0500 Subject: [PATCH 1677/2556] Fix Makefile Fail and UMS eMMC mounting. --- drivers/usb/gadget/Makefile | 7 +++---- drivers/usb/gadget/f_mass_storage.c | 11 +++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index d516ade38e33f..2d00a918b5718 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -24,13 +24,12 @@ obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o -ifeq ($(CONFIG_USB_MSM_72K_HTC),y) -obj-$(CONFIG_USB_MSM_72K_HTC) += msm72k_udc_htc.o +ifdef CONFIG_USB_MSM_72K_HTC + obj-$(CONFIG_USB_MSM_72K_HTC) += msm72k_udc_htc.o else -obj-$(CONFIG_USB_MSM_72K) += msm72k_udc.o + obj-$(CONFIG_USB_MSM_72K) += msm72k_udc.o endif - # # USB gadget drivers # diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 5c81b6a7c85e5..4e4c899473529 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -3033,6 +3033,17 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) fsg_intf_desc.bInterfaceNumber = i; fsg->interface_number = i; +#ifdef CONFIG_USB_ANDROID_MASS_STORAGE + /* HACK!! Android doesn't rebind on new configurations, instead it + * separates functionality in different products. Thus config_buf() + * in composite.c is set to rewrite bInterfaceNumber to match the + * actual function configuration of the active product. Since that + * number is checked in fsg_setup, we need to know it. So we cheat, + * knowing that UMS is the first function in all of our "products". + */ + fsg->interface_number = 0; +#endif + /* Find all the endpoints we will use */ ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); if (!ep) From 406e6a99b3f19026496bce8b4390f458d8f1c1a5 Mon Sep 17 00:00:00 2001 From: Tony Layher Date: Sun, 6 Feb 2011 02:24:56 -0500 Subject: [PATCH 1678/2556] Fix Camera Updated with newer ov8810.c from HTC --- .../configs/cyanogen_incrediblec_defconfig | 11 +- drivers/input/misc/Makefile | 2 +- .../input/misc/cm3602_lightsensor_microp.c | 640 ++++++++++++++++++ drivers/media/video/msm/msm_vfe8x_proc.c | 2 + drivers/media/video/msm/ov8810.c | 17 +- 5 files changed, 659 insertions(+), 13 deletions(-) create mode 100644 drivers/input/misc/cm3602_lightsensor_microp.c diff --git a/arch/arm/configs/cyanogen_incrediblec_defconfig b/arch/arm/configs/cyanogen_incrediblec_defconfig index 4ebbf9ccf7006..8ff11fe74abfa 100644 --- a/arch/arm/configs/cyanogen_incrediblec_defconfig +++ b/arch/arm/configs/cyanogen_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.37 Kernel Configuration -# Sat Feb 5 23:41:40 2011 +# Sun Feb 6 01:31:53 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -1150,7 +1150,7 @@ CONFIG_INPUT_GPIO=y # CONFIG_INPUT_ADXL34X is not set # CONFIG_INPUT_CAPELLA_CM3602 is not set # CONFIG_INPUT_CAPELLA_CM3602_HTC is not set -# CONFIG_LIGHTSENSOR_MICROP is not set +CONFIG_LIGHTSENSOR_MICROP=y CONFIG_INPUT_OPTICALJOYSTICK=y CONFIG_OPTICALJOYSTICK_CRUCIAL=y CONFIG_OPTICALJOYSTICK_CRUCIAL_uP=y @@ -1410,7 +1410,7 @@ CONFIG_720P_CAMERA=y # CONFIG_MT9P012 is not set # CONFIG_S5K3E2FX is not set # CONFIG_S5K6AAFX is not set -# CONFIG_OV8810 is not set +CONFIG_OV8810=y # CONFIG_OV9665 is not set # CONFIG_S5K3H1GX is not set # CONFIG_DAB is not set @@ -1499,7 +1499,7 @@ CONFIG_USB_GADGET_SELECTED=y # CONFIG_USB_GADGET_R8A66597 is not set # CONFIG_USB_GADGET_M66592 is not set CONFIG_USB_GADGET_MSM_72K=y -# CONFIG_USB_MSM_72K is not set +CONFIG_USB_MSM_72K=y CONFIG_USB_MSM_72K_HTC=y CONFIG_USB_GADGET_DUALSPEED=y # CONFIG_USB_ZERO is not set @@ -1522,6 +1522,9 @@ CONFIG_USB_ANDROID_RNDIS=y # CONFIG_USB_G_MULTI is not set # CONFIG_USB_G_HID is not set # CONFIG_USB_G_DBGP is not set +# CONFIG_USB_ACCESSORY_DETECT is not set +# CONFIG_DOCK_ACCESSORY_DETECT is not set +# CONFIG_USB_BYPASS_VBUS_NOTIFY is not set # # OTG and related infrastructure diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 90a757e1b99d6..a68ccaa0b64c5 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -46,7 +46,7 @@ obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o -ifeq ($(CONFIG_MICROP_COMMON),y) +ifdef CONFIG_MICROP_COMMON obj-$(CONFIG_LIGHTSENSOR_MICROP) += cm3602_lightsensor_microp_htc.o else obj-$(CONFIG_INPUT_CAPELLA_CM3602) += capella_cm3602.o diff --git a/drivers/input/misc/cm3602_lightsensor_microp.c b/drivers/input/misc/cm3602_lightsensor_microp.c new file mode 100644 index 0000000000000..10c72f5675cde --- /dev/null +++ b/drivers/input/misc/cm3602_lightsensor_microp.c @@ -0,0 +1,640 @@ +/* + * + * Copyright (C) 2009 HTC, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct microp_ls_info { + struct microp_function_config *ls_config; + struct input_dev *ls_input_dev; + struct early_suspend early_suspend; + struct i2c_client *client; + struct workqueue_struct *ls_wq; + + uint32_t als_func; + uint32_t als_kadc; + uint32_t als_gadc; + uint8_t als_calibrating; + int als_intr_enabled; + int is_suspend; + int old_intr_cmd; +}; + +struct microp_ls_info *ls_info; +static int ls_enable_flag; +static int ls_enable_num; +static uint32_t als_kadc; + +static void enable_intr_do_work(struct work_struct *w); +static DECLARE_DELAYED_WORK(enable_intr_work, enable_intr_do_work); + +static void lightsensor_do_work(struct work_struct *w); +static DECLARE_WORK(lightsensor_work, lightsensor_do_work); + +void set_ls_kvalue(struct microp_ls_info *li) +{ + if (!li) { + pr_err("%s: ls_info is empty\n", __func__); + return; + } + + printk(KERN_INFO "%s: ALS calibrated als_kadc=0x%x\n", + __func__, als_kadc); + if (als_kadc >> 16 == ALS_CALIBRATED) + li->als_kadc = als_kadc & 0xFFFF; + else { + li->als_kadc = 0; + printk(KERN_INFO "%s: no ALS calibrated\n", __func__); + } + + if (li->als_kadc && li->ls_config->golden_adc) { + li->als_kadc = (li->als_kadc > 0 && li->als_kadc < 0x400) ? + li->als_kadc : li->ls_config->golden_adc; + li->als_gadc = (li->ls_config->golden_adc > 0) + ? li->ls_config->golden_adc : li->als_kadc; + } else { + li->als_kadc = 1; + li->als_gadc = 1; + } + printk(KERN_INFO "%s: als_kadc=0x%x, als_gadc=0x%x\n", + __func__, li->als_kadc, li->als_gadc); +} + +static int upload_ls_table(struct microp_ls_info *li) +{ + uint8_t data[20]; + int i; + for (i = 0; i < 10; i++) { + if (li->ls_config->levels[i] < 0x3FF) { + data[i] = (uint8_t)(li->ls_config->levels[i] + * li->als_kadc / li->als_gadc >> 8); + data[i + 10] = (uint8_t)(li->ls_config->levels[i] + * li->als_kadc / li->als_gadc); + } else { + data[i] = (uint8_t)(li->ls_config->levels[i] >> 8); + data[i + 10] = (uint8_t)(li->ls_config->levels[i] & 0xFF); + } + printk("ls_table: data[%d] , data[%d] = %x, %x\n", i, i, data[i], data[i+10]); + } + + return microp_i2c_write(MICROP_I2C_WCMD_ADC_TABLE, data, 20); +} + +static int get_ls_adc_level(uint8_t *data) +{ + struct microp_ls_info *li = ls_info; + uint8_t i, adc_level = 0; + uint16_t adc_value = 0; + +/* From HTC + data[0] = 0x00; + data[1] = li->ls_config->channel; + if (microp_read_adc(data)) + return -1; + + adc_value = data[0]<<8 | data[1]; +*/ + /* new */ + if (microp_read_adc(li->ls_config->channel, &adc_value)) + return -1; + + if (adc_value > 0x3FF) { + printk(KERN_WARNING "%s: get wrong value: 0x%X\n", + __func__, adc_value); + return -1; + } else { + if (!li->als_calibrating) { + adc_value = adc_value * li->als_gadc / li->als_kadc; + if (adc_value > 0x3FF) + adc_value = 0x3FF; + data[0] = adc_value >> 8; + data[1] = adc_value & 0xFF; + } + for (i = 0; i < 10; i++) { + if (adc_value <= + li->ls_config->levels[i]) { + adc_level = i; +// if (li->ls_config->levels[i]) + break; + } + } + printk(KERN_DEBUG "ALS value: 0x%X, level: %d #\n", + adc_value, adc_level); + data[2] = adc_level; + } + + return 0; +} + +void report_lightsensor_data(void) +{ + uint8_t data[3]; + int ret; + struct microp_ls_info *li = ls_info; + + ret = get_ls_adc_level(data); + if (!ret) { + input_report_abs(li->ls_input_dev, + ABS_MISC, (int)data[2]); + input_sync(li->ls_input_dev); + } +} + +static int ls_microp_intr_enable(uint8_t enable) +{ + int ret; + uint8_t data[2]; + struct microp_ls_info *li = ls_info; + + if (li->old_intr_cmd) { + data[0] = 0; + if (enable) + data[1] = 1; + else + data[1] = 0; + + ret = microp_i2c_write(MICROP_I2C_WCMD_AUTO_BL_CTL, data, 2); + } else { + ret = microp_write_interrupt(li->client, + li->ls_config->int_pin, enable); + } + + return ret; +} + +static void enable_intr_do_work(struct work_struct *w) +{ + struct microp_ls_info *li = ls_info; + int ret; + + if (ls_enable_flag) { + ret = ls_microp_intr_enable(1); + if (ret < 0) + pr_err("%s error\n", __func__); + else { + li->als_intr_enabled = 1; + ls_enable_flag = 0; + input_report_abs(li->ls_input_dev, ABS_MISC, -1); + input_sync(li->ls_input_dev); + } + } + + report_lightseneor_data(); +} + +static void lightsensor_do_work(struct work_struct *w) +{ + /* Wait for Framework event polling ready */ + if (ls_enable_num == 0) { + ls_enable_num = 1; + msleep(300); + } + + report_lightseneor_data(); +} + +static irqreturn_t lightsensor_irq_handler(int irq, void *data) +{ + struct microp_ls_info *li = ls_info; + queue_work(li->ls_wq, &lightsensor_work); + + return IRQ_HANDLED; +} + +static int ls_power(int enable) +{ + struct microp_ls_info *li = ls_info; + + if (li->ls_config->ls_gpio_on) + gpio_set_value(li->ls_config->ls_gpio_on, enable ? 0 : 1); + + if (li->ls_config->ls_power) + li->ls_config->ls_power(LS_PWR_ON, enable); + + return 0; +} + +static int lightsensor_enable(void) +{ + int ret; + struct microp_ls_info *li = ls_info; + + pr_info("%s\n", __func__); + + ls_enable_flag = 1; + if (li->is_suspend) { + li->als_intr_enabled = 1; + pr_err("%s: microp is suspended\n", __func__); + return 0; + } + if (!li->als_intr_enabled) { + ret = ls_microp_intr_enable(1); + if (ret < 0) + pr_err("%s: set auto light sensor fail\n", __func__); + else { + li->als_intr_enabled = 1; + /* report an invalid value first to ensure we trigger an event + * when adc_level is zero. + */ + input_report_abs(li->ls_input_dev, ABS_MISC, -1); + input_sync(li->ls_input_dev); + } + } + return 0; +} + +static int lightsensor_disable(void) +{ + /* update trigger data when done */ + struct microp_ls_info *li = ls_info; + int ret; + + pr_info("%s\n", __func__); + ls_enable_flag = 0; + if (li->is_suspend) { + li->als_intr_enabled = 0; + pr_err("%s: microp is suspended\n", __func__); + return 0; + } + + if (li->als_intr_enabled) { + ret = ls_microp_intr_enable(0); + if (ret < 0) + pr_err("%s: disable auto light sensor fail\n", + __func__); + else + li->als_intr_enabled = 0; + } + return 0; +} + +DEFINE_MUTEX(ls_i2c_api_lock); +static int lightsensor_opened; + +static int lightsensor_open(struct inode *inode, struct file *file) +{ + int rc = 0; + pr_debug("%s\n", __func__); + mutex_lock(&ls_i2c_api_lock); + if (lightsensor_opened) { + pr_err("%s: already opened\n", __func__); + rc = -EBUSY; + } + lightsensor_opened = 1; + mutex_unlock(&ls_i2c_api_lock); + return rc; +} + +static int lightsensor_release(struct inode *inode, struct file *file) +{ + pr_debug("%s\n", __func__); + mutex_lock(&ls_i2c_api_lock); + lightsensor_opened = 0; + mutex_unlock(&ls_i2c_api_lock); + return 0; +} + +static long lightsensor_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int rc, val; + struct microp_ls_info *li = ls_info; + mutex_lock(&ls_i2c_api_lock); + pr_debug("%s cmd %d\n", __func__, _IOC_NR(cmd)); + + switch (cmd) { + case LIGHTSENSOR_IOCTL_ENABLE: + if (get_user(val, (unsigned long __user *)arg)) { + rc = -EFAULT; + break; + } + pr_info("%s set value = %d\n", __func__, val); + rc = val ? lightsensor_enable() : lightsensor_disable(); + break; + case LIGHTSENSOR_IOCTL_GET_ENABLED: + val = li->als_intr_enabled; + pr_info("%s get enabled status: %d\n", __func__, val); + rc = put_user(val, (unsigned long __user *)arg); + break; + default: + pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd)); + rc = -EINVAL; + } + + mutex_unlock(&ls_i2c_api_lock); + return rc; +} + +static struct file_operations lightsensor_fops = { + .owner = THIS_MODULE, + .open = lightsensor_open, + .release = lightsensor_release, + .unlocked_ioctl = lightsensor_ioctl +}; + +static struct miscdevice lightsensor_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "lightsensor", + .fops = &lightsensor_fops +}; + +static ssize_t ls_adc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + uint8_t data[3]; + int ret; + + ret = get_ls_adc_level(data); + + ret = sprintf(buf, + "ADC[0x%03X] => level %d\n", + (data[0] << 8 | data[1]), data[2]); + + return ret; +} + +static DEVICE_ATTR(ls_adc, 0644, ls_adc_show, NULL); + +static ssize_t ls_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint8_t data[2] = {0, 0}; + int ret; + + microp_i2c_read(MICROP_I2C_RCMD_SPI_BL_STATUS, data, 2); + ret = sprintf(buf, "Light sensor Auto = %d, SPI enable = %d\n", + data[0], data[1]); + + return ret; +} + +static ssize_t ls_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct microp_ls_info *li = ls_info; + uint8_t enable = 0; + int ls_auto; + int ret; + + ls_auto = -1; + sscanf(buf, "%d", &ls_auto); + + if (ls_auto != 0 && ls_auto != 1 && ls_auto != 147) + return -EINVAL; + + if (ls_auto) { + enable = 1; + li->als_calibrating = (ls_auto == 147) ? 1 : 0; + li->als_intr_enabled = 1; + } else { + enable = 0; + li->als_calibrating = 0; + li->als_intr_enabled = 0; + } + + ret = ls_microp_intr_enable(enable); + if (ret < 0) + pr_err("%s: ls intr enable fail\n", __func__); + + return count; +} + +static DEVICE_ATTR(ls_auto, 0644, ls_enable_show, ls_enable_store); + +static ssize_t ls_kadc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct microp_ls_info *li = ls_info; + int ret; + + ret = sprintf(buf, "kadc = 0x%x, gadc = 0x%x, real kadc = 0x%x\n", + li->als_kadc, li->als_gadc, als_kadc); + + return ret; +} + +static ssize_t ls_kadc_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct microp_ls_info *li = ls_info; + int kadc_temp = 0; + + sscanf(buf, "%d", &kadc_temp); + if (kadc_temp <= 0 || li->ls_config->golden_adc <= 0) { + printk(KERN_ERR "%s: kadc_temp=0x%x, als_gadc=0x%x\n", + __func__, + kadc_temp, + li->ls_config->golden_adc); + return -EINVAL; + } + + li->als_kadc = kadc_temp; + li->als_gadc = li->ls_config->golden_adc; + printk(KERN_INFO "%s: als_kadc=0x%x, als_gadc=0x%x\n", + __func__, li->als_kadc, li->als_gadc); + + if (upload_ls_table(li) < 0) + printk(KERN_ERR "%s: upload ls table fail\n", __func__); + + return count; +} + +static DEVICE_ATTR(ls_kadc, 0644, ls_kadc_show, ls_kadc_store); + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void light_sensor_suspend(struct early_suspend *h) +{ + struct microp_ls_info *li = ls_info; + int ret; + + li->is_suspend = 1; + cancel_delayed_work(&enable_intr_work); + if (li->als_intr_enabled) { + ret = ls_microp_intr_enable(0); + if (ret < 0) + pr_err("%s: disable auto light sensor fail\n", + __func__); + else + li->als_intr_enabled = 0; + } + ls_power(0); +} + +static void light_sensor_resume(struct early_suspend *h) +{ + struct microp_ls_info *li = ls_info; + + ls_power(1); + queue_delayed_work(li->ls_wq, &enable_intr_work, msecs_to_jiffies(800)); + li->is_suspend = 0; +} +#endif + +static int lightsensor_probe(struct platform_device *pdev) +{ + int ret, irq; + struct microp_ls_info *li; + struct lightsensor_platform_data *pdata = pdev->dev.platform_data; + + li = kzalloc(sizeof(struct microp_ls_info), GFP_KERNEL); + if (!li) + return -ENOMEM; + ls_info = li; + +/* From HTC + li->client = dev_get_drvdata(&pdev->dev); +*/ + /* new */ + li->client = get_microp_client(); + + if (!li->client) { + pr_err("%s: can't get microp i2c client\n", __func__); + return -1; + } + li->ls_input_dev = input_allocate_device(); + if (!li->ls_input_dev) { + pr_err("%s: could not allocate input device\n", __func__); + return -ENOMEM; + } + li->ls_input_dev->name = "lightsensor-level"; + set_bit(EV_ABS, li->ls_input_dev->evbit); + input_set_abs_params(li->ls_input_dev, ABS_MISC, 0, 9, 0, 0); + + ret = input_register_device(li->ls_input_dev); + if (ret < 0) { + pr_err("%s: can not register input device\n", + __func__); + return ret; + } + + ret = misc_register(&lightsensor_misc); + if (ret < 0) { + pr_err("%s: can not register misc device\n", + __func__); + return ret; + } + + li->ls_config = pdata->config; + irq = pdata->irq; + li->old_intr_cmd = pdata->old_intr_cmd; + ret = request_irq(irq, lightsensor_irq_handler, IRQF_TRIGGER_NONE, "lightsensor_microp", li); + if (ret < 0) { + pr_err("%s: request_irq(%d) failed for (%d)\n", + __func__, irq, ret); + return ret; + } + + set_ls_kvalue(li); + ret = upload_ls_table(li); + if (ret < 0) { + pr_err("%s: upload ls table fail\n", + __func__); + return ret; + } + + li->ls_wq = create_workqueue("ls_wq"); + if (li->ls_wq == NULL) + return -ENOMEM; + + if (li->ls_config->ls_gpio_on) { + ret = gpio_request(li->ls_config->ls_gpio_on, + "microp_i2c"); + if (ret < 0) { + pr_err("request gpio ls failed\n"); + return ret; + } + ret = gpio_direction_output(li->ls_config->ls_gpio_on, 0); + if (ret < 0) { + pr_err("gpio_direction_output ls failed\n"); + return ret; + } + } + ls_power(1); +#ifdef CONFIG_HAS_EARLYSUSPEND + li->early_suspend.level = + EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + li->early_suspend.suspend = light_sensor_suspend; + li->early_suspend.resume = light_sensor_resume; + register_early_suspend(&li->early_suspend); +#endif + ret = device_create_file(&li->client->dev, &dev_attr_ls_adc); + ret = device_create_file(&li->client->dev, &dev_attr_ls_auto); + ret = device_create_file(&li->client->dev, &dev_attr_ls_kadc); + + return 0; + +} + +static struct platform_driver lightsensor_driver = { + .probe = lightsensor_probe, + .driver = { .name = "lightsensor_microp", }, +}; + +static int __init parse_tag_als_kadc(const struct tag *tags) +{ + int found = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_ALS) { + found = 1; + break; + } + } + + if (found) + als_kadc = t->u.revision.rev; + pr_debug("%s: als_kadc = 0x%x\n", __func__, als_kadc); + return 0; +} +__tagtable(ATAG_ALS, parse_tag_als_kadc); + +static int __init light_sensor_init(void) +{ + return platform_driver_register(&lightsensor_driver); +} + +static void __exit light_sensor_exit(void) +{ + platform_driver_unregister(&lightsensor_driver); +} + +module_init(light_sensor_init); +module_exit(light_sensor_exit); + +MODULE_DESCRIPTION("HTC LIGHT SENSOR"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/msm/msm_vfe8x_proc.c b/drivers/media/video/msm/msm_vfe8x_proc.c index 7d6f83d95e99a..58f9afd533a15 100644 --- a/drivers/media/video/msm/msm_vfe8x_proc.c +++ b/drivers/media/video/msm/msm_vfe8x_proc.c @@ -776,6 +776,7 @@ static void vfe_proc_ops(enum VFE_MESSAGE_ID id, void *data) { struct msm_vfe_resp *rp; struct vfe_message *msg; + struct msm_sync *sync = (struct msm_sync *)ctrl->syncdata; CDBG("ctrl->vfeOperationMode = %d, msgId = %d\n", ctrl->vfeOperationMode, id); @@ -919,6 +920,7 @@ static void vfe_process_error_irq(struct isr_queue_cmd *qcmd) static void vfe_process_camif_epoch1_irq(void) { /* Turn on the flash. */ + struct msm_sync *sync = (struct msm_sync *)ctrl->syncdata; /*remove google flashlight*/ /*ctrl->resp->flash_ctrl(sync, MSM_CAMERA_LED_HIGH);*/ diff --git a/drivers/media/video/msm/ov8810.c b/drivers/media/video/msm/ov8810.c index e0f1b6adef579..fc7fb80993d2f 100644 --- a/drivers/media/video/msm/ov8810.c +++ b/drivers/media/video/msm/ov8810.c @@ -114,8 +114,8 @@ const int ov8810_ver_qtr_blk_lines_array[] = {44, 44, 365}; /* Registers*/ /* PLL Registers */ #define REG_PRE_PLL_CLK_DIV 0x3011 /*0x0305*/ -#define REG_PLL_MULTIPLIER 0x3010 -#define REG_VT_CLK_DIV 0x300E /*[7:4]VT_SYS_DIV, [3-0]VT_PIX_DIV*/ +#define REG_PLL_MULTIPLIER 0x3010 +#define REG_VT_CLK_DIV 0x300E /*[7:4]VT_SYS_DIV, [3-0]VT_PIX_DIV*/ #define REG_OP_CLK_DIV 0x300F /*[7:4]OP_SYS_DIV, [3-0]OP_PIX_DIV*/ /* ISP Enable Control */ @@ -2053,6 +2053,10 @@ static int ov8810_sensor_open_init(struct msm_camera_sensor_info *data) pr_info("[CAM]doing clk switch (ov8810)\n"); if(data->camera_clk_switch != NULL) data->camera_clk_switch(); + + /* enable mclk first */ + msm_camio_clk_rate_set(OV8810_DEFAULT_CLOCK_RATE); + msleep(20); msm_camio_camif_pad_reg_reset(); msleep(20); @@ -2067,11 +2071,6 @@ static int ov8810_sensor_open_init(struct msm_camera_sensor_info *data) gpio_free(data->sensor_pwd); msleep(5); - /* enable mclk first */ - msm_camio_clk_rate_set(OV8810_DEFAULT_CLOCK_RATE); - msm_camio_camif_pad_reg_reset(); - msleep(3); - /*Pull reset*/ rc = gpio_request(data->sensor_reset, "ov8810"); if (!rc) gpio_direction_output(data->sensor_reset, 1); @@ -2100,7 +2099,7 @@ static int ov8810_sensor_open_init(struct msm_camera_sensor_info *data) /* enable AF actuator */ rc = vreg_enable(vreg_af_actuator); if (!rc) { - + rc = vreg_set_level(vreg_af_actuator, 2800); /*2v8*/ if (rc) { @@ -2631,6 +2630,7 @@ static int ov8810_sensor_release(void) pr_info("[CAM]vreg_af_actuator vreg_disable\n"); vreg_disable(vreg_af_actuator); + msleep(20); pr_info("[CAM]%s, %d\n", __func__, __LINE__); @@ -2762,6 +2762,7 @@ static int ov8810_vreg_disable(struct platform_device *pdev) static int __ov8810_probe(struct platform_device *pdev) { + struct msm_camera_sensor_info *sdata = pdev->dev.platform_data; printk("[CAM]__ov8810_probe\n"); ov8810_pdev = pdev; From fd390be0b3feae5c52a7a937b0546adfc729ec9b Mon Sep 17 00:00:00 2001 From: Tony Layher Date: Mon, 7 Feb 2011 15:29:02 -0500 Subject: [PATCH 1679/2556] Changes to Attempt to get SLCD working. * Updated GPIO table for sony_tft to include The display on and display off values. * Updated System_rev to coincide with RGB_666 values from Stock HTC source. * Cleaned up other un-needed code. --- arch/arm/mach-msm/board-incrediblec-mmc.c | 9 +- arch/arm/mach-msm/board-incrediblec-panel.c | 66 ++++++++++++--- arch/arm/mach-msm/board-incrediblec.c | 91 ++------------------- arch/arm/mach-msm/include/mach/msm_fb.h | 2 + 4 files changed, 67 insertions(+), 101 deletions(-) diff --git a/arch/arm/mach-msm/board-incrediblec-mmc.c b/arch/arm/mach-msm/board-incrediblec-mmc.c index 18e2e489535e1..d1b85bb1228fe 100644 --- a/arch/arm/mach-msm/board-incrediblec-mmc.c +++ b/arch/arm/mach-msm/board-incrediblec-mmc.c @@ -150,21 +150,16 @@ static struct mmc_platform_data incrediblec_sdslot_data = { .ocr_mask = INCREDIBLEC_MMC_VDD, .status = incrediblec_sdslot_status, .translate_vdd = incrediblec_sdslot_switchvdd, - //.slot_type = &incrediblec_sdslot_type, + .slot_type = &incrediblec_sdslot_type, }; static unsigned int incrediblec_mmc_type = MMC_TYPE_MMC; static struct mmc_platform_data incrediblec_movinand_data = { .ocr_mask = INCREDIBLEC_MMC_VDD, - //.slot_type = &incrediblec_mmc_type, + .slot_type = &incrediblec_mmc_type, }; -/* int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, - unsigned int stat_irq, unsigned long stat_irq_flags); */ - - - /* ---- WIFI ---- */ static uint32_t wifi_on_gpio_table[] = { diff --git a/arch/arm/mach-msm/board-incrediblec-panel.c b/arch/arm/mach-msm/board-incrediblec-panel.c index 368d0c9e9f762..d3a81eba81a8b 100644 --- a/arch/arm/mach-msm/board-incrediblec-panel.c +++ b/arch/arm/mach-msm/board-incrediblec-panel.c @@ -623,21 +623,65 @@ static int samsung_oled_panel_blank(struct msm_lcdc_panel_ops *ops) return 0; } -#define LCM_GPIO_CFG(gpio, func, str) \ - PCOM_GPIO_CFG(gpio, func, GPIO_OUTPUT, GPIO_NO_PULL, str) +#define LCM_GPIO_CFG(gpio, func) \ + PCOM_GPIO_CFG(gpio, func, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA) static uint32_t sony_tft_display_on_gpio_table[] = { - LCM_GPIO_CFG(INCREDIBLEC_SPI_CLK, 1, GPIO_4MA), - LCM_GPIO_CFG(INCREDIBLEC_SPI_CS, 1, GPIO_4MA), - LCM_GPIO_CFG(INCREDIBLEC_LCD_ID0, 1, GPIO_4MA), - LCM_GPIO_CFG(INCREDIBLEC_SPI_DO, 1, GPIO_4MA), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R0, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R1, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R2, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R3, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R4, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R5, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G0, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G1, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G2, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G3, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G4, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G5, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B0, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B1, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B2, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B3, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B4, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B5, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_PCLK, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_VSYNC, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_HSYNC, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_DE, 1), + LCM_GPIO_CFG(INCREDIBLEC_SPI_CLK, 1), + LCM_GPIO_CFG(INCREDIBLEC_SPI_CS, 1), + LCM_GPIO_CFG(INCREDIBLEC_LCD_ID0, 1), + LCM_GPIO_CFG(INCREDIBLEC_SPI_DO, 1), }; static uint32_t sony_tft_display_off_gpio_table[] = { - LCM_GPIO_CFG(INCREDIBLEC_SPI_CLK, 0, GPIO_4MA), - LCM_GPIO_CFG(INCREDIBLEC_SPI_CS, 0, GPIO_4MA), - LCM_GPIO_CFG(INCREDIBLEC_LCD_ID0, 0, GPIO_4MA), - LCM_GPIO_CFG(INCREDIBLEC_SPI_DO, 0, GPIO_4MA), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R0, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R1, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R2, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R3, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R4, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_R5, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G0, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G1, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G2, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G3, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G4, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_G5, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B0, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B1, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B2, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B3, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B4, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_B5, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_PCLK, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_VSYNC, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_HSYNC, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_DE, 0), + LCM_GPIO_CFG(INCREDIBLEC_SPI_CLK, 0), + LCM_GPIO_CFG(INCREDIBLEC_SPI_CS, 0), + LCM_GPIO_CFG(INCREDIBLEC_LCD_ID0, 0), + LCM_GPIO_CFG(INCREDIBLEC_SPI_DO, 0), }; @@ -1155,7 +1199,7 @@ int __init incrediblec_init_panel(void) { int ret; - if (system_rev <= 2) { + if (system_rev >= 1) { /* CDMA version (except for EVT1) supports RGB666 */ init_tablep = samsung_oled_rgb666_init_table; init_table_sz = ARRAY_SIZE(samsung_oled_rgb666_init_table); diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index 4c1ceaab15104..eb5c81e935986 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -44,7 +44,6 @@ #include #include #include -// #include #include #include #include @@ -62,7 +61,6 @@ #include #include #include -/* #include */ #include #include @@ -463,75 +461,10 @@ static struct platform_device incrediblec_rfkill = { .id = -1, }; -static struct resource qsd_spi_resources[] = { - { - .name = "spi_irq_in", - .start = INT_SPI_INPUT, - .end = INT_SPI_INPUT, - .flags = IORESOURCE_IRQ, - }, - { - .name = "spi_irq_out", - .start = INT_SPI_OUTPUT, - .end = INT_SPI_OUTPUT, - .flags = IORESOURCE_IRQ, - }, - { - .name = "spi_irq_err", - .start = INT_SPI_ERROR, - .end = INT_SPI_ERROR, - .flags = IORESOURCE_IRQ, - }, - { - .name = "spi_base", - .start = 0xA1200000, - .end = 0xA1200000 + SZ_4K - 1, - .flags = IORESOURCE_MEM, - }, - { - .name = "spi_clk", - .start = 17, - .end = 1, - .flags = IORESOURCE_IRQ, - }, - { - .name = "spi_mosi", - .start = 18, - .end = 1, - .flags = IORESOURCE_IRQ, - }, - { - .name = "spi_miso", - .start = 19, - .end = 1, - .flags = IORESOURCE_IRQ, - }, - { - .name = "spi_cs0", - .start = 20, - .end = 1, - .flags = IORESOURCE_IRQ, - }, - { - .name = "spi_pwr", - .start = 21, - .end = 0, - .flags = IORESOURCE_IRQ, - }, - { - .name = "spi_irq_cs0", - .start = 22, - .end = 0, - .flags = IORESOURCE_IRQ, - }, +static struct spi_platform_data incrediblec_spi_pdata = { + .clk_rate = 1200000, }; -static struct platform_device qsd_device_spi = { - .name = "spi_qsd", - .id = 0, - .num_resources = ARRAY_SIZE(qsd_spi_resources), - .resource = qsd_spi_resources, -}; static struct resource msm_kgsl_resources[] = { { @@ -1288,7 +1221,7 @@ static struct platform_device *devices[] __initdata = { &incrediblec_leds, #if defined(CONFIG_SPI_QSD) - &qsd_device_spi, + &msm_device_spi, #endif &incrediblec_oj, }; @@ -1351,18 +1284,6 @@ static void incrediblec_config_uart_gpios(void) ARRAY_SIZE(incrediblec_uart_gpio_table)); } -static struct msm_i2c_device_platform_data msm_i2c_pdata = { - .i2c_clock = 100000, - .clock_strength = GPIO_8MA, - .data_strength = GPIO_8MA, -}; - -static void __init msm_device_i2c_init(void) -{ - msm_i2c_gpio_init(); - msm_device_i2c.dev.platform_data = &msm_i2c_pdata; -} - #define ATAG_BDADDR 0x43294329 #define ATAG_BDADDR_SIZE 4 @@ -1518,6 +1439,11 @@ static void __init incrediblec_init(void) incrediblec_kgsl_power_rail_mode(0); incrediblec_kgsl_power(true); + +#ifdef CONFIG_SPI_QSD + msm_device_spi.dev.platform_data = &incrediblec_spi_pdata; +#endif + #ifdef CONFIG_SERIAL_MSM_HS msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; msm_device_uart_dm1.name = "msm_serial_hs"; /* for bcm */ @@ -1531,7 +1457,6 @@ static void __init incrediblec_init(void) gpio_direction_output(INCREDIBLEC_GPIO_TP_EN, 0); incrediblec_audio_init(); - msm_device_i2c_init(); #ifdef CONFIG_MICROP_COMMON incrediblec_microp_init(); #endif diff --git a/arch/arm/mach-msm/include/mach/msm_fb.h b/arch/arm/mach-msm/include/mach/msm_fb.h index cad39e53756c3..a75f4a7b1f7ce 100644 --- a/arch/arm/mach-msm/include/mach/msm_fb.h +++ b/arch/arm/mach-msm/include/mach/msm_fb.h @@ -158,6 +158,8 @@ struct msm_mddi_platform_data { /* fixup the mfr name, product id */ void (*fixup)(uint16_t *mfr_name, uint16_t *product_id); + int vsync_irq; + struct resource *fb_resource; /*optional*/ /* number of clients in the list that follows */ int num_clients; From 2cc513f0fc9b5d7150a408f69a592dae8701e78f Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 7 Feb 2011 14:56:39 -0800 Subject: [PATCH 1680/2556] inc: Disable CONFIG_PERF_EVENTS We were getting a strange panic: Unhandled prefetch abort: breakpoint debug exception ... We don't need perf though, and removing perf also clears HW_BREAKPOINT, which lets me boot successfully. --- arch/arm/configs/cyanogen_incrediblec_defconfig | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/arch/arm/configs/cyanogen_incrediblec_defconfig b/arch/arm/configs/cyanogen_incrediblec_defconfig index 8ff11fe74abfa..83df4bee59688 100644 --- a/arch/arm/configs/cyanogen_incrediblec_defconfig +++ b/arch/arm/configs/cyanogen_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.37 Kernel Configuration -# Sun Feb 6 01:31:53 2011 +# Mon Feb 7 14:15:00 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -27,7 +27,6 @@ CONFIG_VECTORS_BASE=0xffff0000 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" CONFIG_CONSTRUCTORS=y CONFIG_HAVE_IRQ_WORK=y -CONFIG_IRQ_WORK=y # # General setup @@ -115,9 +114,8 @@ CONFIG_PERF_USE_VMALLOC=y # # Kernel Performance Events And Counters # -CONFIG_PERF_EVENTS=y +# CONFIG_PERF_EVENTS is not set # CONFIG_PERF_COUNTERS is not set -# CONFIG_DEBUG_PERF_USE_VMALLOC is not set CONFIG_VM_EVENT_COUNTERS=y CONFIG_COMPAT_BRK=y CONFIG_SLAB=y @@ -130,7 +128,6 @@ CONFIG_HAVE_KPROBES=y CONFIG_HAVE_KRETPROBES=y CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y CONFIG_HAVE_CLK=y -CONFIG_HAVE_HW_BREAKPOINT=y # # GCOV-based kernel profiling @@ -399,7 +396,6 @@ CONFIG_AEABI=y # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set # CONFIG_HIGHMEM is not set -CONFIG_HW_PERF_EVENTS=y CONFIG_VMALLOC_RESERVE=0x08000000 CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y From 1a4990490e997b548da6f6990e407effb45f43a8 Mon Sep 17 00:00:00 2001 From: Tony Layher Date: Mon, 7 Feb 2011 20:32:40 -0500 Subject: [PATCH 1681/2556] Fix screen tearing and Hsync issues with SLCD. * Added missing Hsync stuff. --- drivers/video/msm/mdp_lcdc.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/video/msm/mdp_lcdc.c b/drivers/video/msm/mdp_lcdc.c index b99dceba8822b..b0b2f3787f7ef 100644 --- a/drivers/video/msm/mdp_lcdc.c +++ b/drivers/video/msm/mdp_lcdc.c @@ -550,6 +550,7 @@ static void precompute_timing_parms(struct mdp_lcdc_info *lcdc) unsigned int display_vstart; unsigned int display_vend; +#ifdef CONFIG_MACH_SUPERSONIC hsync_period = (timing->hsync_pulse_width + timing->hsync_back_porch + fb_data->xres + timing->hsync_front_porch); hsync_start_x = (timing->hsync_pulse_width + timing->hsync_back_porch); @@ -565,6 +566,23 @@ static void precompute_timing_parms(struct mdp_lcdc_info *lcdc) display_vend = (timing->vsync_pulse_width + timing->vsync_back_porch + fb_data->yres) * hsync_period; +#else + hsync_period = (timing->hsync_back_porch + + fb_data->xres + timing->hsync_front_porch); + hsync_start_x = timing->hsync_back_porch; + hsync_end_x = hsync_start_x + fb_data->xres - 1; + + vsync_period = (timing->vsync_back_porch + + fb_data->yres + timing->vsync_front_porch); + vsync_period *= hsync_period; + + display_vstart = timing->vsync_back_porch; + display_vstart *= hsync_period; + display_vstart += timing->hsync_skew; + + display_vend = (timing->vsync_back_porch + fb_data->yres) * + hsync_period; +#endif display_vend += timing->hsync_skew - 1; /* register values we pre-compute at init time from the timing From 39b586d1fd27e4bee390a4ebb90cc33fd3a61424 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 9 Feb 2011 22:28:45 -0800 Subject: [PATCH 1682/2556] inc: Disable many config DEBUG flags --- .../configs/cyanogen_incrediblec_defconfig | 78 ++----------------- 1 file changed, 7 insertions(+), 71 deletions(-) diff --git a/arch/arm/configs/cyanogen_incrediblec_defconfig b/arch/arm/configs/cyanogen_incrediblec_defconfig index 83df4bee59688..eb5d4df8aeb8c 100644 --- a/arch/arm/configs/cyanogen_incrediblec_defconfig +++ b/arch/arm/configs/cyanogen_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.37 Kernel Configuration -# Mon Feb 7 14:15:00 2011 +# Tue Feb 8 22:27:38 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -93,7 +93,6 @@ CONFIG_EMBEDDED=y CONFIG_UID16=y # CONFIG_SYSCTL_SYSCALL is not set CONFIG_KALLSYMS=y -# CONFIG_KALLSYMS_ALL is not set # CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_HOTPLUG=y CONFIG_PRINTK=y @@ -132,7 +131,6 @@ CONFIG_HAVE_CLK=y # # GCOV-based kernel profiling # -# CONFIG_GCOV_KERNEL is not set CONFIG_HAVE_GENERIC_DMA_COHERENT=y CONFIG_SLABINFO=y CONFIG_RT_MUTEXES=y @@ -847,8 +845,6 @@ CONFIG_PREVENT_FIRMWARE_BUILD=y CONFIG_FW_LOADER=y # CONFIG_FIRMWARE_IN_KERNEL is not set CONFIG_EXTRA_FIRMWARE="" -# CONFIG_DEBUG_DRIVER is not set -# CONFIG_DEBUG_DEVRES is not set # CONFIG_SYS_HYPERVISOR is not set # CONFIG_CONNECTOR is not set CONFIG_MTD=y @@ -998,7 +994,7 @@ CONFIG_SCSI_MOD=y CONFIG_MD=y # CONFIG_BLK_DEV_MD is not set CONFIG_BLK_DEV_DM=y -CONFIG_DM_DEBUG=y +# CONFIG_DM_DEBUG is not set CONFIG_DM_CRYPT=y # CONFIG_DM_SNAPSHOT is not set # CONFIG_DM_MIRROR is not set @@ -1233,7 +1229,6 @@ CONFIG_I2C_MSM=y # CONFIG_I2C_DEBUG_ALGO is not set # CONFIG_I2C_DEBUG_BUS is not set CONFIG_SPI=y -# CONFIG_SPI_DEBUG is not set CONFIG_SPI_MASTER=y # @@ -1257,7 +1252,6 @@ CONFIG_SPI_QSD=y # CONFIG_PPS is not set CONFIG_ARCH_REQUIRE_GPIOLIB=y CONFIG_GPIOLIB=y -# CONFIG_DEBUG_GPIO is not set # CONFIG_GPIO_SYSFS is not set # @@ -1348,7 +1342,7 @@ CONFIG_MFD_SUPPORT=y # CONFIG_EZX_PCAP is not set # CONFIG_MFD_TPS6586X is not set CONFIG_REGULATOR=y -CONFIG_REGULATOR_DEBUG=y +# CONFIG_REGULATOR_DEBUG is not set # CONFIG_REGULATOR_DUMMY is not set # CONFIG_REGULATOR_FIXED_VOLTAGE is not set # CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set @@ -1487,9 +1481,7 @@ CONFIG_USB_ARCH_HAS_HCD=y # NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may # CONFIG_USB_GADGET=y -# CONFIG_USB_GADGET_DEBUG is not set # CONFIG_USB_GADGET_DEBUG_FILES is not set -# CONFIG_USB_GADGET_DEBUG_FS is not set CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_USB_GADGET_SELECTED=y # CONFIG_USB_GADGET_R8A66597 is not set @@ -1720,7 +1712,6 @@ CONFIG_EXT3_FS_POSIX_ACL=y CONFIG_EXT3_FS_SECURITY=y # CONFIG_EXT4_FS is not set CONFIG_JBD=y -# CONFIG_JBD_DEBUG is not set CONFIG_FS_MBCACHE=y # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set @@ -1892,82 +1883,27 @@ CONFIG_FRAME_WARN=1024 CONFIG_MAGIC_SYSRQ=y # CONFIG_STRIP_ASM_SYMS is not set # CONFIG_UNUSED_SYMBOLS is not set -CONFIG_DEBUG_FS=y +# CONFIG_DEBUG_FS is not set # CONFIG_HEADERS_CHECK is not set -CONFIG_DEBUG_KERNEL=y -# CONFIG_DEBUG_SHIRQ is not set -# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_DEBUG_KERNEL is not set # CONFIG_HARDLOCKUP_DETECTOR is not set -CONFIG_DETECT_HUNG_TASK=y -# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set -CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 -CONFIG_SCHED_DEBUG=y -CONFIG_SCHEDSTATS=y -CONFIG_TIMER_STATS=y -# CONFIG_DEBUG_OBJECTS is not set -# CONFIG_DEBUG_SLAB is not set -# CONFIG_DEBUG_KMEMLEAK is not set -CONFIG_DEBUG_PREEMPT=y -# CONFIG_DEBUG_RT_MUTEXES is not set -# CONFIG_RT_MUTEX_TESTER is not set -# CONFIG_DEBUG_SPINLOCK is not set -CONFIG_DEBUG_MUTEXES=y CONFIG_BKL=y -# CONFIG_DEBUG_LOCK_ALLOC is not set -# CONFIG_PROVE_LOCKING is not set # CONFIG_SPARSE_RCU_POINTER is not set -# CONFIG_LOCK_STAT is not set -CONFIG_DEBUG_SPINLOCK_SLEEP=y -# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set CONFIG_STACKTRACE=y -# CONFIG_DEBUG_KOBJECT is not set -# CONFIG_DEBUG_BUGVERBOSE is not set -CONFIG_DEBUG_INFO=y -# CONFIG_DEBUG_INFO_REDUCED is not set -CONFIG_DEBUG_VM=y -# CONFIG_DEBUG_WRITECOUNT is not set +CONFIG_DEBUG_BUGVERBOSE=y # CONFIG_DEBUG_MEMORY_INIT is not set -# CONFIG_DEBUG_LIST is not set -# CONFIG_TEST_LIST_SORT is not set -CONFIG_DEBUG_SG=y -# CONFIG_DEBUG_NOTIFIERS is not set -# CONFIG_DEBUG_CREDENTIALS is not set -# CONFIG_BOOT_PRINTK_DELAY is not set -# CONFIG_RCU_TORTURE_TEST is not set -# CONFIG_BACKTRACE_SELF_TEST is not set -# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set -# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set -# CONFIG_LKDTM is not set -# CONFIG_FAULT_INJECTION is not set -# CONFIG_LATENCYTOP is not set # CONFIG_SYSCTL_SYSCALL_CHECK is not set -# CONFIG_PAGE_POISONING is not set CONFIG_HAVE_FUNCTION_TRACER=y CONFIG_HAVE_DYNAMIC_FTRACE=y CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y CONFIG_TRACING_SUPPORT=y -CONFIG_FTRACE=y -# CONFIG_FUNCTION_TRACER is not set -# CONFIG_IRQSOFF_TRACER is not set -# CONFIG_PREEMPT_TRACER is not set -# CONFIG_SCHED_TRACER is not set -# CONFIG_ENABLE_DEFAULT_TRACERS is not set -CONFIG_BRANCH_PROFILE_NONE=y -# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set -# CONFIG_PROFILE_ALL_BRANCHES is not set -# CONFIG_STACK_TRACER is not set -# CONFIG_BLK_DEV_IO_TRACE is not set -# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_FTRACE is not set # CONFIG_ATOMIC64_SELFTEST is not set # CONFIG_SAMPLES is not set CONFIG_HAVE_ARCH_KGDB=y -# CONFIG_KGDB is not set # CONFIG_STRICT_DEVMEM is not set CONFIG_ARM_UNWIND=y # CONFIG_DEBUG_USER is not set -# CONFIG_DEBUG_ERRORS is not set -# CONFIG_DEBUG_STACK_USAGE is not set -# CONFIG_DEBUG_LL is not set # CONFIG_OC_ETM is not set # From ee2ea044a0a9f2bebd0273f7828152e4dc3f8ec4 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 9 Feb 2011 22:34:42 -0800 Subject: [PATCH 1683/2556] inc: Fill in /proc/engineerid properly --- arch/arm/mach-msm/proc_engineerid.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/proc_engineerid.c b/arch/arm/mach-msm/proc_engineerid.c index 7da8c978d525f..36367b5ff3478 100644 --- a/arch/arm/mach-msm/proc_engineerid.c +++ b/arch/arm/mach-msm/proc_engineerid.c @@ -19,9 +19,11 @@ #include #include "devices.h" +extern unsigned engineer_id; + static int c_show(struct seq_file *m, void *v) { - seq_printf(m, "%d\n"); + seq_printf(m, "%u\n", engineer_id); return 0; } From a8449646a5a50e82ffe82215f846334d73173203 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 9 Feb 2011 22:38:54 -0800 Subject: [PATCH 1684/2556] inc: Use msm_mmc_platform_data directly - The mmc.h under mach-msm has the right platform struct for us, so just use that by the name msm_mmc_platform_data. - Undo the modifications to mmc_platform_data. --- arch/arm/include/asm/mach/mmc.h | 2 -- arch/arm/mach-msm/board-incrediblec-mmc.c | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h index a0ca136364d4b..f8d391ad92037 100644 --- a/arch/arm/include/asm/mach/mmc.h +++ b/arch/arm/include/asm/mach/mmc.h @@ -22,8 +22,6 @@ struct mmc_platform_data { unsigned int (*status)(struct device *); struct embedded_sdio_data *embedded_sdio; int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); - unsigned int *slot_type; - unsigned dat0_gpio; }; #endif diff --git a/arch/arm/mach-msm/board-incrediblec-mmc.c b/arch/arm/mach-msm/board-incrediblec-mmc.c index d1b85bb1228fe..d93260045de14 100644 --- a/arch/arm/mach-msm/board-incrediblec-mmc.c +++ b/arch/arm/mach-msm/board-incrediblec-mmc.c @@ -25,8 +25,8 @@ #include #include -#include +#include #include #include "board-incrediblec.h" @@ -146,7 +146,7 @@ static unsigned int incrediblec_sdslot_status(struct device *dev) static unsigned int incrediblec_sdslot_type = MMC_TYPE_SD; -static struct mmc_platform_data incrediblec_sdslot_data = { +static struct msm_mmc_platform_data incrediblec_sdslot_data = { .ocr_mask = INCREDIBLEC_MMC_VDD, .status = incrediblec_sdslot_status, .translate_vdd = incrediblec_sdslot_switchvdd, @@ -155,7 +155,7 @@ static struct mmc_platform_data incrediblec_sdslot_data = { static unsigned int incrediblec_mmc_type = MMC_TYPE_MMC; -static struct mmc_platform_data incrediblec_movinand_data = { +static struct msm_mmc_platform_data incrediblec_movinand_data = { .ocr_mask = INCREDIBLEC_MMC_VDD, .slot_type = &incrediblec_mmc_type, }; @@ -217,7 +217,7 @@ static unsigned int incrediblec_wifi_status(struct device *dev) return incrediblec_wifi_cd; } -static struct mmc_platform_data incrediblec_wifi_data = { +static struct msm_mmc_platform_data incrediblec_wifi_data = { .ocr_mask = MMC_VDD_28_29, .status = incrediblec_wifi_status, .register_status_notify = incrediblec_wifi_status_register, From f3cea4358c8ab682eecddcda96a365f41af40433 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 9 Feb 2011 22:43:22 -0800 Subject: [PATCH 1685/2556] inc: Fix ioctl callbacks for newer file_operations The .unlocked_ioctl function signature no longer has an inode parameter. The functions we added for tpa2018d1 and smem_log don't use that one anyway, so it's an easy fix to just remove it. --- arch/arm/mach-msm/board-incrediblec-tpa2018d1.c | 2 +- arch/arm/mach-msm/smem_log.c | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-msm/board-incrediblec-tpa2018d1.c b/arch/arm/mach-msm/board-incrediblec-tpa2018d1.c index 26f12dde308de..9c9e1f51ac1cb 100644 --- a/arch/arm/mach-msm/board-incrediblec-tpa2018d1.c +++ b/arch/arm/mach-msm/board-incrediblec-tpa2018d1.c @@ -149,7 +149,7 @@ static int tpa2018d1_read_config(void __user *argp) return rc; } -static int tpa2018d1_ioctl(struct inode *inode, struct file *file, +static long tpa2018d1_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; diff --git a/arch/arm/mach-msm/smem_log.c b/arch/arm/mach-msm/smem_log.c index 8f1bf00b3e9d1..f569e1904bad5 100644 --- a/arch/arm/mach-msm/smem_log.c +++ b/arch/arm/mach-msm/smem_log.c @@ -1392,8 +1392,7 @@ static int smem_log_release(struct inode *ip, struct file *fp) return 0; } -static int smem_log_ioctl(struct inode *ip, struct file *fp, - unsigned int cmd, unsigned long arg); +static long smem_log_ioctl(struct file *fp, unsigned int cmd, unsigned long arg); static const struct file_operations smem_log_fops = { .owner = THIS_MODULE, @@ -1413,8 +1412,7 @@ static const struct file_operations smem_log_bin_fops = { .unlocked_ioctl = smem_log_ioctl, }; -static int smem_log_ioctl(struct inode *ip, struct file *fp, - unsigned int cmd, unsigned long arg) +static long smem_log_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { struct smem_log_inst *inst; From 769f970dce720eaaafba468d5ee17a8943e55f1a Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 9 Feb 2011 22:47:36 -0800 Subject: [PATCH 1686/2556] inc: Copy wifi setup changes from bravo - wifi_on_gpio_table now sets all as NO_PULL OUTPUT. - NVS flags are updated to match as well. --- arch/arm/mach-msm/board-incrediblec-mmc.c | 12 +++++----- arch/arm/mach-msm/board-incrediblec-wifi.c | 26 +++++++++++++--------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/arch/arm/mach-msm/board-incrediblec-mmc.c b/arch/arm/mach-msm/board-incrediblec-mmc.c index d93260045de14..138e2f0d87fe8 100644 --- a/arch/arm/mach-msm/board-incrediblec-mmc.c +++ b/arch/arm/mach-msm/board-incrediblec-mmc.c @@ -173,13 +173,13 @@ static uint32_t wifi_on_gpio_table[] = { }; static uint32_t wifi_off_gpio_table[] = { - PCOM_GPIO_CFG(51, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */ - PCOM_GPIO_CFG(52, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */ - PCOM_GPIO_CFG(53, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ - PCOM_GPIO_CFG(54, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ - PCOM_GPIO_CFG(55, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(51, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ PCOM_GPIO_CFG(56, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ - PCOM_GPIO_CFG(152, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ + PCOM_GPIO_CFG(152, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ }; /* BCM4329 returns wrong sdio_vsn(1) when we read cccr, diff --git a/arch/arm/mach-msm/board-incrediblec-wifi.c b/arch/arm/mach-msm/board-incrediblec-wifi.c index 57c61262b73af..a9ad4ae575c67 100644 --- a/arch/arm/mach-msm/board-incrediblec-wifi.c +++ b/arch/arm/mach-msm/board-incrediblec-wifi.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include "board-incrediblec.h" @@ -77,7 +77,7 @@ static struct resource incrediblec_wifi_resources[] = { .name = "bcm4329_wlan_irq", .start = MSM_GPIO_TO_INT(INCREDIBLEC_GPIO_WIFI_IRQ), .end = MSM_GPIO_TO_INT(INCREDIBLEC_GPIO_WIFI_IRQ), - .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL, + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE, }, }; @@ -99,8 +99,9 @@ static struct platform_device incrediblec_wifi_device = { }; extern unsigned char *get_wifi_nvs_ram(void); +extern int wifi_calibration_size_set(void); -static unsigned incrediblec_wifi_update_nvs(char *str) +static unsigned incrediblec_wifi_update_nvs(char *str, int add_flag) { #define NVS_LEN_OFFSET 0x0C #define NVS_DATA_OFFSET 0x40 @@ -112,14 +113,18 @@ static unsigned incrediblec_wifi_update_nvs(char *str) ptr = get_wifi_nvs_ram(); /* Size in format LE assumed */ memcpy(&len, ptr + NVS_LEN_OFFSET, sizeof(len)); - - /* the last bye in NVRAM is 0, trim it */ - if (ptr[NVS_DATA_OFFSET + len -1] == 0) + /* if the last byte in NVRAM is 0, trim it */ + if (ptr[NVS_DATA_OFFSET + len - 1] == 0) len -= 1; - - strcpy(ptr + NVS_DATA_OFFSET + len, str); - len += strlen(str); + if (add_flag) { + strcpy(ptr + NVS_DATA_OFFSET + len, str); + len += strlen(str); + } else { + if (strnstr(ptr + NVS_DATA_OFFSET, str, len)) + len -= strlen(str); + } memcpy(ptr + NVS_LEN_OFFSET, &len, sizeof(len)); + wifi_calibration_size_set(); return 0; } @@ -131,7 +136,8 @@ static int __init incrediblec_wifi_init(void) return 0; printk("%s: start\n", __func__); - incrediblec_wifi_update_nvs("sd_oobonly=1\r\n"); + incrediblec_wifi_update_nvs("sd_oobonly=1\r\n", 0); + incrediblec_wifi_update_nvs("btc_params70=0x32\r\n", 1); incrediblec_init_wifi_mem(); ret = platform_device_register(&incrediblec_wifi_device); return ret; From 6be9481270b549040c77b38db775591d919dbd44 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 9 Feb 2011 23:06:44 -0800 Subject: [PATCH 1687/2556] inc: Remove unused defines from board-incrediblec.h This makes the file identical to HTC's again. --- arch/arm/mach-msm/board-incrediblec.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-msm/board-incrediblec.h b/arch/arm/mach-msm/board-incrediblec.h index 7f1c3d3443386..14fb432ad8ecf 100644 --- a/arch/arm/mach-msm/board-incrediblec.h +++ b/arch/arm/mach-msm/board-incrediblec.h @@ -145,20 +145,16 @@ #define INCREDIBLEC_SPI_CLK (17) #define INCREDIBLEC_SPI_DO (18) #define INCREDIBLEC_SPI_CS (20) + #define INCREDIBLEC_LCD_RST_ID1 (29) #define INCREDIBLEC_LCD_ID0 (32) -#define INCREDIBLEC_GPIO_LCD_RST_N 29 -#define INCREDIBLEC_GPIO_LCD_ID0 147 + /* TV-out */ #define INCREDIBLEC_TV_LOAD_DET (82) #define INCREDIBLEC_VIDEO_SHDN_N (109) #define INCREDIBLEC_AV_SWITCH (119) /* LCD */ -#define INCREDIBLEC_LCD_SPI_CLK (17) -#define INCREDIBLEC_LCD_SPI_DO (18) -#define INCREDIBLEC_LCD_SPI_CSz (20) -#define INCREDIBLEC_LCD_RSTz (29) #define INCREDIBLEC_LCD_R0 (113) #define INCREDIBLEC_LCD_R1 (114) #define INCREDIBLEC_LCD_R2 (115) From 9562295ce20adf780df7e323e5a2ce0292932133 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 9 Feb 2011 23:08:14 -0800 Subject: [PATCH 1688/2556] inc: Clean up warnings in board-incredible.c - Initialize ints with 0, NULL is for pointers. - Remove unreferenced functions and global structs. --- arch/arm/mach-msm/board-incrediblec.c | 132 +------------------------- 1 file changed, 3 insertions(+), 129 deletions(-) diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index eb5c81e935986..c6de5ec42c929 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -251,7 +251,7 @@ static struct capella_cm3602_platform_data capella_cm3602_pdata = { static struct htc_headset_microp_platform_data htc_headset_microp_data = { .remote_int = 1 << 5, .remote_irq = MSM_uP_TO_INT(5), - .remote_enable_pin = NULL, + .remote_enable_pin = 0, .adc_channel = 0x01, .adc_remote = {0, 33, 50, 110, 160, 220}, }; @@ -746,105 +746,6 @@ static struct regulator_init_data tps65023_data[5] = { }, }; -static void set_h2w_dat(int n) -{ - gpio_set_value(INCREDIBLEC_GPIO_H2W_DATA, n); -} - -static void set_h2w_clk(int n) -{ - gpio_set_value(INCREDIBLEC_GPIO_H2W_CLK, n); -} - -static int get_h2w_dat(void) -{ - return gpio_get_value(INCREDIBLEC_GPIO_H2W_DATA); -} - -static int get_h2w_clk(void) -{ - return gpio_get_value(INCREDIBLEC_GPIO_H2W_CLK); -} - -static void h2w_dev_power_on(int on) -{ - printk(KERN_INFO "Not support H2W power\n"); -} - -/* default TX,RX to GPI */ -static uint32_t uart3_off_gpi_table[] = { - /* RX, H2W DATA */ - PCOM_GPIO_CFG(INCREDIBLEC_GPIO_H2W_DATA, 0, - GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), - /* TX, H2W CLK */ - PCOM_GPIO_CFG(INCREDIBLEC_GPIO_H2W_CLK, 0, - GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), -}; - -/* set TX,RX to GPO */ -static uint32_t uart3_off_gpo_table[] = { - /* RX, H2W DATA */ - PCOM_GPIO_CFG(INCREDIBLEC_GPIO_H2W_DATA, 0, - GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), - /* TX, H2W CLK */ - PCOM_GPIO_CFG(INCREDIBLEC_GPIO_H2W_CLK, 0, - GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), -}; - -static void set_h2w_dat_dir(int n) -{ -#if 0 - if (n == 0) /* input */ - gpio_direction_input(INCREDIBLEC_GPIO_H2W_DATA); - else - gpio_configure(INCREDIBLEC_GPIO_H2W_DATA, GPIOF_DRIVE_OUTPUT); -#else - if (n == 0) /* input */ - msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, - uart3_off_gpi_table + 0, 0); - else - msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, - uart3_off_gpo_table + 0, 0); -#endif -} - -static void set_h2w_clk_dir(int n) -{ -#if 0 - if (n == 0) /* input */ - gpio_direction_input(INCREDIBLEC_GPIO_H2W_CLK); - else - gpio_configure(INCREDIBLEC_GPIO_H2W_CLK, GPIOF_DRIVE_OUTPUT); -#else - if (n == 0) /* input */ - msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, - uart3_off_gpi_table + 1, 0); - else - msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, - uart3_off_gpo_table + 1, 0); -#endif -} - - -static void incrediblec_config_serial_debug_gpios(void); - -static void h2w_configure(int route) -{ - printk(KERN_INFO "H2W route = %d \n", route); - switch (route) { - case H2W_UART3: - incrediblec_config_serial_debug_gpios(); - printk(KERN_INFO "H2W -> UART3\n"); - break; - case H2W_GPIO: - msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, - uart3_off_gpi_table + 0, 0); - msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, - uart3_off_gpi_table + 1, 0); - printk(KERN_INFO "H2W -> GPIO\n"); - break; - } -} static struct htc_headset_mgr_platform_data htc_headset_mgr_data = { }; @@ -859,8 +760,8 @@ static struct platform_device htc_headset_mgr = { static struct htc_headset_gpio_platform_data htc_headset_gpio_data = { .hpin_gpio = INCREDIBLEC_GPIO_35MM_HEADSET_DET, - .key_enable_gpio = NULL, - .mic_select_gpio = NULL, + .key_enable_gpio = 0, + .mic_select_gpio = 0, }; static struct platform_device htc_headset_gpio = { @@ -1161,14 +1062,6 @@ static struct platform_device incrediblec_oj = { } }; -static struct resource resources_msm_fb[] = { - { - .start = MSM_FB_BASE, - .end = MSM_FB_BASE + MSM_FB_SIZE - 1, - .flags = IORESOURCE_MEM, - }, -}; - static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { .rx_wakeup_irq = -1, .inject_rx_on_wakeup = 0, @@ -1250,19 +1143,6 @@ static uint32_t usb_phy_3v3_table[] = { PCOM_GPIO_CFG(INCREDIBLEC_USB_PHY_3V3_ENABLE, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA) }; -static uint32_t usb_ID_PIN_table[] = { - PCOM_GPIO_CFG(INCREDIBLEC_GPIO_USB_ID_PIN, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), -}; - -static uint32_t incrediblec_serial_debug_table[] = { - /* RX */ - PCOM_GPIO_CFG(INCREDIBLEC_GPIO_UART3_RX, 3, GPIO_INPUT, GPIO_NO_PULL, - GPIO_4MA), - /* TX */ - PCOM_GPIO_CFG(INCREDIBLEC_GPIO_UART3_TX, 3, GPIO_OUTPUT, GPIO_NO_PULL, - GPIO_4MA), -}; - static uint32_t incrediblec_uart_gpio_table[] = { /* RX */ PCOM_GPIO_CFG(INCREDIBLEC_GPIO_UART3_RX, 3, GPIO_INPUT, GPIO_NO_PULL, @@ -1272,12 +1152,6 @@ static uint32_t incrediblec_uart_gpio_table[] = { GPIO_4MA), }; -static void incrediblec_config_serial_debug_gpios(void) -{ - config_gpio_table(incrediblec_serial_debug_table, - ARRAY_SIZE(incrediblec_serial_debug_table)); -} - static void incrediblec_config_uart_gpios(void) { config_gpio_table(incrediblec_uart_gpio_table, From 42d6db4a0db3315c7a9e56d475e1d7dde62039f6 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 9 Feb 2011 23:10:42 -0800 Subject: [PATCH 1689/2556] Remove the unused i2c-msm.h --- arch/arm/mach-msm/board-incrediblec.c | 1 - include/linux/i2c-msm.h | 26 -------------------------- 2 files changed, 27 deletions(-) delete mode 100644 include/linux/i2c-msm.h diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index c6de5ec42c929..1af5ddb60610e 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -17,7 +17,6 @@ #include #include -#include #include #include #include diff --git a/include/linux/i2c-msm.h b/include/linux/i2c-msm.h deleted file mode 100644 index 14bf3eb5a1e5a..0000000000000 --- a/include/linux/i2c-msm.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * include/linux/i2c-msm.h - platform data structure for i2c controller - * - * Copyright (C) 2009 HTC. - * - * 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 _I2C_MSM_H -#define _I2C_MSM_H - -struct msm_i2c_device_platform_data { - int i2c_clock; - int clock_strength; - int data_strength; -}; - -#endif /* _I2C_MSM_H */ From 67b5b10113455511d8915b5c6d4d635ca2d54aed Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 9 Feb 2011 23:10:54 -0800 Subject: [PATCH 1690/2556] Remove the unused msm_panel.h --- arch/arm/mach-msm/board-incrediblec.c | 1 - arch/arm/mach-msm/include/mach/msm_panel.h | 38 ---------------------- 2 files changed, 39 deletions(-) delete mode 100644 arch/arm/mach-msm/include/mach/msm_panel.h diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index 1af5ddb60610e..f4d6bc2c6d8c0 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -48,7 +48,6 @@ #include #include #include -#include #include "board-incrediblec.h" #include "devices.h" #include "proc_comm.h" diff --git a/arch/arm/mach-msm/include/mach/msm_panel.h b/arch/arm/mach-msm/include/mach/msm_panel.h deleted file mode 100644 index 1a0dac675928f..0000000000000 --- a/arch/arm/mach-msm/include/mach/msm_panel.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef _MSM_PANEL_H_ -#define _MSM_PANEL_H_ - -struct panel_platform_data { - struct resource *fb_res; - int (*power)(int on); - int (*gpio_switch)(int on); -}; - -/* For those MDDI clients with variadic length of parameter, such as Samsung - * S6D series controllers. - */ -struct lcm_va_cmd { - uint32_t reg, delay, size; - uint8_t *value; -}; -#define CMD_VECT(r, d, ...) {r, d, sizeof((uint8_t[]){__VA_ARGS__}), \ - (uint8_t []){__VA_ARGS__} } - - struct cabc_platform_data { - int (*change_cabcmode)(struct msm_mddi_client_data *client_data, - int mode, u8 dimming); - }; - -struct cabc_config { - int panel; - int shrink; - uint8_t *pwm_data; - int min_level; - int default_br; - struct msm_mddi_client_data *client; - int (*bl_handle)(struct platform_device *, int); - int (*shrink_br)(int brightness); - int (*change_cabcmode)(struct msm_mddi_client_data *client_data, - int mode, u8 dimming); -}; - -#endif From 8c41eb9df0aea508d33a6d29a1d0230dff3ae9e3 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 9 Feb 2011 23:13:36 -0800 Subject: [PATCH 1691/2556] inc: Enable EXT4, and let it handle EXT2 and EXT3 too --- .../configs/cyanogen_incrediblec_defconfig | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/arch/arm/configs/cyanogen_incrediblec_defconfig b/arch/arm/configs/cyanogen_incrediblec_defconfig index eb5d4df8aeb8c..6ddb19b89697c 100644 --- a/arch/arm/configs/cyanogen_incrediblec_defconfig +++ b/arch/arm/configs/cyanogen_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.37 Kernel Configuration -# Tue Feb 8 22:27:38 2011 +# Wed Feb 9 18:57:13 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -1700,18 +1700,15 @@ CONFIG_MACH_NO_WESTBRIDGE=y # # File systems # -CONFIG_EXT2_FS=y -CONFIG_EXT2_FS_XATTR=y -CONFIG_EXT2_FS_POSIX_ACL=y -CONFIG_EXT2_FS_SECURITY=y -# CONFIG_EXT2_FS_XIP is not set -CONFIG_EXT3_FS=y -# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set -CONFIG_EXT3_FS_XATTR=y -CONFIG_EXT3_FS_POSIX_ACL=y -CONFIG_EXT3_FS_SECURITY=y -# CONFIG_EXT4_FS is not set -CONFIG_JBD=y +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y CONFIG_FS_MBCACHE=y # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set From fbf69cbb8cbcc66e216b1798911ecb229f5b6328 Mon Sep 17 00:00:00 2001 From: Tony Layher Date: Thu, 10 Feb 2011 18:26:42 -0500 Subject: [PATCH 1692/2556] inc: Use proper microp include for the inc device. --- drivers/input/opticaljoystick/curcial.c | 773 ++++++++++++++++++++++++ 1 file changed, 773 insertions(+) create mode 100644 drivers/input/opticaljoystick/curcial.c diff --git a/drivers/input/opticaljoystick/curcial.c b/drivers/input/opticaljoystick/curcial.c new file mode 100644 index 0000000000000..7af4050602041 --- /dev/null +++ b/drivers/input/opticaljoystick/curcial.c @@ -0,0 +1,773 @@ +/* drivers/input/opticaljoystick/curcial.c + * + * Copyright (C) 2009 HTC Corporation. + * + * 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 "curcial.h" + +#define OJ_POWERON 1 +#define OJ_POWEROFF 0 +#define CURCIAL_OJ_POWER 85 +#define BURST_DATA_SIZE 7 +#define OJ_DEVICE_ID 0x0D +#define OJ_REGISTER_WRITE 0x7B +#define OJ_REGISTER_REQUEST 0x7C +#define OJ_REGISTER_READ 0x7D +#define OJ_REGISTER_BURST_REQUEST 0x7E +#define OJ_REGISTER_BURST_READ 0x7F +#define OJ_REGISTER_OJ_POLLING 0xA8 +#define OJ_MOTION 0x02 +#define OJ_DELTA_Y 0x03 +#define OJ_DELTA_X 0x04 +#define OJ_SQUAL 0x05 +#define OJ_SHU_HIGH 0x06 +#define OJ_SHU_LOW 0x07 +#define OJ_OBSERVATION 0x2E +#define OJ_POLLING_ENABLE 1 +#define OJ_POLLING_DISABLE 0 +#define OJ_POLLING_INTERVAL 10 +#define OJ_POLLING_COUNT 10 + +#define DELTA_SUM_TIME 40 +#define DELTA_SUM_CP 0 +#define OJ_RETRY 5 +static const unsigned short keymap[] = { + KEY_RIGHT, + KEY_LEFT, + KEY_UP, + KEY_DOWN /*, + KEY_REPLY*/ +}; + +enum { + MOTION = 0, + Y, + X, + SQUAL, + SHUTTER_UPPER, + SHUTTER_LOWER, + MAXIMUM_PIXEL +}; +extern unsigned int system_rev; +static struct proc_dir_entry *oj_proc_entry; +static struct workqueue_struct *curcial_wq; +static struct curcial_oj_platform_data *my_oj; +static uint8_t polling_delay;/* use msleep*/ +static uint8_t interval; +static uint8_t debugflag; +static uint8_t ap_code; +static int16_t mSumDeltaX; +static int16_t mSumDeltaY; +static int8_t DeltaX[64]; +static int8_t DeltaY[64]; +static int16_t mDeltaX; +static int16_t mDeltaY; +static int8_t normal_th; +static int8_t xy_ratio; + +static atomic_t suspend_flag = ATOMIC_INIT(0); +static uint16_t index; + +static int __devinit curcial_oj_probe(struct platform_device *pdev); +static int __devexit curcial_oj_remove(struct platform_device *pdev); + +static struct platform_driver curcial_oj_device_driver = { + .probe = curcial_oj_probe, + .remove = __devexit_p(curcial_oj_remove), + .driver = { + .name = CURCIAL_OJ_NAME, + .owner = THIS_MODULE, + } +}; + +static uint8_t curcial_oj_register_read(uint8_t reg) +{ + uint8_t cmd[2]; + + cmd[0] = 0; + cmd[1] = reg; + microp_i2c_write(OJ_REGISTER_REQUEST, cmd, 2); + microp_i2c_read(OJ_REGISTER_READ, cmd, 2); + + return cmd[1]; +} + +static void curcial_oj_burst_read(uint8_t *data) +{ + uint8_t cmd[2]; + + cmd[0] = 0x01; + microp_i2c_write(OJ_REGISTER_BURST_REQUEST, cmd, 1); + microp_i2c_read(OJ_REGISTER_BURST_READ, data, BURST_DATA_SIZE); +} + +static void curcial_oj_polling_mode(uint8_t mode) +{ + uint8_t cmd[2]; + + cmd[0] = mode; + microp_i2c_write(OJ_REGISTER_OJ_POLLING, cmd, 1); +} + +static irqreturn_t curcial_oj_irq_handler(int irq, void *data) +{ + queue_work(curcial_wq, &my_oj->work); + return IRQ_HANDLED; +} + +static int curcial_oj_init(void) +{ + uint8_t data[BURST_DATA_SIZE]; + uint8_t id; + uint8_t version; + uint8_t i; + + microp_i2c_read(MICROP_I2C_RCMD_VERSION, data, 2); + version = my_oj->microp_version; + + + if (data[0] < version) { + printk("Microp firmware version:%d have to large than %d !\n\ + Stop OJ driver loading!\n", data[0], version); + return 0; + } + + if (!my_oj->oj_poweron(OJ_POWERON)) + return 0; + + mdelay(10); + microp_spi_vote_enable(SPI_OJ, 1); + + /*microp_i2c_read(0x24, data, 2);*/ + my_oj->oj_shutdown(0); + /* Write 0x5a to register 0x3a */ + data[0] = 0x3a; + data[1] = 0x5a; + microp_i2c_write(OJ_REGISTER_WRITE, data, 2); + mdelay(23); + + /* Read from register 0x02,0x03 and 0x04 one time regardless the state of the motion pin */ + curcial_oj_register_read(OJ_MOTION); + curcial_oj_register_read(OJ_DELTA_Y); + curcial_oj_register_read(OJ_DELTA_X); + + for (i = 0;i < OJ_RETRY; i++ ) { + id = curcial_oj_register_read(0x00); + if (id == OJ_DEVICE_ID) { + printk(KERN_INFO"OpticalJoystick Device ID: %02x\n", OJ_DEVICE_ID); + id = curcial_oj_register_read(0x01); + printk(KERN_INFO"OJ Driver: Revision : %02x\n", id); + break; + } else { + printk("probe OpticalJoystick Device:retry =%d\n", i); + } + } + if (i == OJ_RETRY) { + printk("Can't probe OpticalJoystick Device: %02x!\n", id); + return 0; + } + + /* Write 0x10 to register 0x1C. This will activate burst mode. */ + data[0] = 0x1C; + data[1] = 0x10; + microp_i2c_write(OJ_REGISTER_WRITE, data, 2); + + curcial_oj_polling_mode(OJ_POLLING_ENABLE); + + return 1; +} +static OJKeyEvt_T OJ_ProcessNavi(int Ratio, int DeltaMin, int16_t SumDeltaX, int16_t SumDeltaY) +{ + OJKeyEvt_T tmpKey; + + if ((10*abs(SumDeltaY) > (Ratio*abs(SumDeltaX))) + && (abs(SumDeltaY) > DeltaMin)) { + if (SumDeltaY > 0) + tmpKey = OJ_KEY_UP; + else + tmpKey = OJ_KEY_DOWN; + } else if (abs(SumDeltaX) > DeltaMin) { + if (SumDeltaX > 0) + tmpKey = OJ_KEY_RIGHT; + else + tmpKey = OJ_KEY_LEFT; + } else + tmpKey = OJ_KEY_NONE; + + return tmpKey; +} + +static void curcial_oj_work_func(struct work_struct *work) +{ + struct curcial_oj_platform_data *oj = container_of(work, struct curcial_oj_platform_data, work); + OJData_T OJData; + uint16_t i, j; + uint8_t data[BURST_DATA_SIZE]; + uint32_t click_time = 0; + uint32_t delta_time = 0; + uint32_t entry_time = 0; + OJKeyEvt_T evtKey = OJ_KEY_NONE; + uint8_t x_count = 0; + uint8_t y_count = 0; + bool out = false; + uint8_t pxsum; + uint16_t sht; + int16_t x_sum; + int16_t y_sum; + + curcial_oj_polling_mode(OJ_POLLING_DISABLE); + + mDeltaX = 0; + mDeltaY = 0; + oj->interval = interval; + entry_time = jiffies_to_msecs(jiffies); + x_sum = 0; + y_sum = 0; + + do { + memset(data, 0x00, sizeof(data)); + out = false; + curcial_oj_burst_read(data); + OJData.squal = data[SQUAL]; + pxsum = curcial_oj_register_read(0x09); + sht = ((data[SHUTTER_UPPER] << 8)|data[SHUTTER_LOWER]); + if (debugflag) { + printk(KERN_INFO"OJ1:M=0x%02x Y=0x%02x X=0x%02x SQUAL=0x%02x " + "SHU_U=0x%02x SHU_L=0x%02x pxsum=%d sht=%d \n", data[MOTION], data[Y], data[X], + data[SQUAL], data[SHUTTER_UPPER], data[SHUTTER_LOWER], pxsum, sht); + } + if (ap_code) { + for (i = 1; i < oj->degree; i++) { + if (((oj->sht_tbl[i-1] < sht) && (sht <= oj->sht_tbl[i])) && (oj->pxsum_tbl[i] < pxsum)) { + if (debugflag) + printk("OJ:A.code_condition:%d\n", i); + out = true; + break; + } + } + if (!out) + goto exit; + } + oj->oj_adjust_xy(data, &mDeltaX, &mDeltaY); + + + DeltaX[index] = (int8_t)mDeltaX; + DeltaY[index] = (int8_t)mDeltaY; + /*printk(KERN_INFO"index=%d: DeltaX[] = %d DeltaY[] = %d \n",index, DeltaX[index] , DeltaY[index]);*/ + if (++index == 64) + index = 0; + + x_sum = x_sum + mDeltaX; + y_sum = y_sum + mDeltaY; + mSumDeltaX = mSumDeltaX + mDeltaX; + mSumDeltaY = mSumDeltaY + mDeltaY; + if (debugflag) + printk(KERN_INFO"check:OJ:mSumDeltaX = %d mSumDeltaY = %d \n", mSumDeltaX, mSumDeltaY); + + evtKey = OJ_ProcessNavi(xy_ratio, normal_th, x_sum, y_sum); + + if (evtKey != OJ_KEY_NONE) { + click_time = jiffies_to_msecs(jiffies); + if (debugflag) + printk(KERN_INFO"click_time=%x last_click_time=%x, %x\n", click_time, oj->last_click_time, click_time-oj->last_click_time); + + if (oj->last_click_time == 0) { + oj->last_click_time = entry_time - oj->interval; + oj->key = evtKey; + } + + delta_time = click_time - entry_time; + + /*printk(KERN_INFO"x_sum=%d y_sum=%d, delta time=%dms\n", x_sum, y_sum, delta_time);*/ + + if (click_time - oj->last_click_time < oj->interval) { + evtKey = OJ_KEY_NONE; + + if (debugflag) + printk(KERN_INFO"interval blocking < %d\n", oj->interval); + }else if (click_time - oj->last_click_time < 80 && evtKey != oj->key) { + evtKey = OJ_KEY_NONE; + printk(KERN_INFO"sudden key ignore \n"); + } + } + + x_count = oj->Xsteps[abs(x_sum) / normal_th]; + y_count = oj->Ysteps[abs(y_sum) / normal_th]; + if (evtKey == OJ_KEY_LEFT) { + for (j = 0; j < x_count; j++) { + input_report_rel(oj->input_dev, REL_X, -1); + input_sync(oj->input_dev); + } + if (debugflag) + printk(KERN_INFO"OJ:KEY_LEFT:%d\n", x_count); + + } else if (evtKey == OJ_KEY_RIGHT) { + for (j = 0; j < x_count; j++) { + input_report_rel(oj->input_dev, REL_X, 1); + input_sync(oj->input_dev); + } + if (debugflag) + printk(KERN_INFO"OJ:KEY_RIGHT:%d\n", x_count); + + } else if (evtKey == OJ_KEY_DOWN) { + for (j = 0; j < y_count; j++) { + input_report_rel(oj->input_dev, REL_Y, 1); + input_sync(oj->input_dev); + } + if (debugflag) + printk(KERN_INFO"OJ:KEY_DOWN:%d\n", y_count); + + } else if (evtKey == OJ_KEY_UP) { + for (j = 0; j < y_count; j++) { + input_report_rel(oj->input_dev, REL_Y, -1); + input_sync(oj->input_dev); + } + if (debugflag) + printk(KERN_INFO"OJ:KEY_UP:%d\n", y_count); + } + + if (evtKey != OJ_KEY_NONE) { + oj->key = evtKey; + oj->last_click_time = click_time; + x_sum = 0; + y_sum = 0; + /*goto exit;*/ + } + mDeltaX = 0; + mDeltaY = 0; + if (polling_delay) + msleep(polling_delay);/*hr_msleep(polling_delay);*/ + } while ((data[0] & 0x80) && (!atomic_read(&suspend_flag))); + + +exit: + + if (debugflag) + printk(KERN_INFO"%s:-\n", __func__); + if (!atomic_read(&suspend_flag)) + curcial_oj_polling_mode(OJ_POLLING_ENABLE); + else + curcial_oj_polling_mode(OJ_POLLING_DISABLE); +} + + +static ssize_t oj_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + return sprintf(buf, + "interval=%d normal_th=%d system_rev=%d" + " debugflag=%d polling_delay=%d xy_ratio=%d ap_code=%d", + interval, normal_th, system_rev, debugflag, + polling_delay, xy_ratio, ap_code); + +} + +static ssize_t oj_ap_code_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + + ap_code = simple_strtoull(buf, NULL, 10); + + return count; +} +static ssize_t oj_interval_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + + interval = simple_strtoull(buf, NULL, 10); + + return count; +} + +static ssize_t oj_normal_th_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + + normal_th = simple_strtoull(buf, NULL, 10); + + return count; +} +static ssize_t oj_polling_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + + polling_delay = simple_strtoull(buf, NULL, 10); + + return count; +} + +static ssize_t oj_xy_ratio_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + + xy_ratio = simple_strtoull(buf, NULL, 10); + + return count; +} +static ssize_t oj_debugflag_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + + debugflag = simple_strtoull(buf, NULL, 10); + + return count; +} + + +static ssize_t oj_xtable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + + char *buffer,*endptr; + int i; + buffer = (char *)buf; + + i= simple_strtoull(buffer, &endptr, 10); + buffer = endptr+1; + + if (i <= 30) + my_oj->Xsteps[i-1] = simple_strtoull(buffer, &endptr, 10); + + return count; +} +static ssize_t oj_ytable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char *buffer,*endptr; + int i; + buffer = (char *)buf;; + + + i= simple_strtoull(buffer, &endptr, 10); + buffer = endptr+1; + + if (i <= 30) + my_oj->Ysteps[i-1] = simple_strtoull(buffer, &endptr, 10); + + return count; +} +static ssize_t oj_xtable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char log[128]; + int i,p; + + for (i = 0, p = 0; i < 30 ; i++) { + p += sprintf(log+p, "%d,", my_oj->Xsteps[i]); + } + return sprintf(buf,"X_table:%s\n",log); + +} +static ssize_t oj_ytable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char log[128]; + int i,p; + + for (i = 0, p = 0; i < 30 ; i++) { + p += sprintf(log+p, "%d,", my_oj->Ysteps[i]); + } + return sprintf(buf,"Y_table:%s\n",log); + +} +static ssize_t oj_deltax_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char log[512]; + uint8_t i,p; + + for (i = 0, p = 0; i < 64 ; i++) { + if (i == 63) + p += sprintf(log+p, "%d", DeltaX[i]); + else + p += sprintf(log+p, "%d,", DeltaX[i]); + } + + return sprintf(buf,"%s\n", log); + +} +static ssize_t oj_deltay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char log[512]; + uint8_t i,p; + + for (i = 0, p = 0; i < 64 ; i++) { + if (i == 63) + p += sprintf(log+p, "%d", DeltaY[i]); + else + p += sprintf(log+p, "%d,", DeltaY[i]); + } + + return sprintf(buf,"%s\n", log); + +} +static ssize_t oj_SumDeltaX_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + return sprintf(buf,"%d\n", mSumDeltaX); + +} +static ssize_t oj_SumDeltaY_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + return sprintf(buf,"%d\n", mSumDeltaY); + +} +static ssize_t oj_reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + index = 0; + mSumDeltaX = 0; + mSumDeltaY = 0; + memset(DeltaX, 0x00, sizeof(DeltaX)); + memset(DeltaY, 0x00, sizeof(DeltaY)); + + return count; +} +static DEVICE_ATTR(reset, 0666, oj_show, oj_reset_store); +static DEVICE_ATTR(deltax, 0444, oj_deltax_show, NULL); +static DEVICE_ATTR(deltay, 0444, oj_deltay_show, NULL); +static DEVICE_ATTR(SumDeltaX, 0444, oj_SumDeltaX_show, NULL); +static DEVICE_ATTR(SumDeltaY, 0444, oj_SumDeltaY_show, NULL); +static DEVICE_ATTR(ap_code, 0644, oj_show, oj_ap_code_store); +static DEVICE_ATTR(interval, 0644, oj_show, oj_interval_store); +static DEVICE_ATTR(normal_th, 0644, oj_show, oj_normal_th_store); +static DEVICE_ATTR(polling_delay, 0644, oj_show, oj_polling_delay_store); +static DEVICE_ATTR(xy_ratio, 0644, oj_show, oj_xy_ratio_store); +static DEVICE_ATTR(debugflag, 0644, oj_show, oj_debugflag_store); +static DEVICE_ATTR(xtable, 0644, oj_xtable_show, oj_xtable_store); +static DEVICE_ATTR(ytable, 0644, oj_ytable_show, oj_ytable_store); + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void curcial_oj_early_suspend(struct early_suspend *h) +{ + struct curcial_oj_platform_data *oj; + atomic_set(&suspend_flag, 1); + oj = container_of(h, struct curcial_oj_platform_data, early_suspend); + printk(KERN_ERR"%s: enter\n", __func__); + oj->oj_shutdown(1); + curcial_oj_polling_mode(OJ_POLLING_DISABLE); + if (oj->share_power == false) { + oj->oj_poweron(OJ_POWEROFF); + } + microp_spi_vote_enable(SPI_OJ, 0); + +} + +static void curcial_oj_late_resume(struct early_suspend *h) +{ + struct curcial_oj_platform_data *oj; + atomic_set(&suspend_flag, 0); + oj = container_of(h, struct curcial_oj_platform_data, early_suspend); + printk(KERN_ERR"%s: enter\n", __func__); + if (!curcial_oj_init()) + microp_spi_vote_enable(SPI_OJ, 0); +} +#endif + +static int __devinit curcial_oj_probe(struct platform_device *pdev) +{ + struct curcial_oj_platform_data *oj = pdev->dev.platform_data; + int err; + int i; + + err = -ENOMEM; + my_oj = oj; + + + INIT_WORK(&oj->work, curcial_oj_work_func); + + curcial_wq = create_singlethread_workqueue("curcial_wq"); + if (!curcial_wq) { + err = -ENOMEM; + goto fail; + } + + oj->input_dev = input_allocate_device(); + if (!oj->input_dev) { + printk(KERN_ERR "Unable to allocate device for OJ\n"); + err = -ENOMEM; + goto fail; + } + + oj->input_dev->name = "curcial-oj"; + + + oj->input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + input_set_capability(oj->input_dev, EV_KEY, BTN_MOUSE); + oj->input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); + + + for(i = 0; i < ARRAY_SIZE(keymap); i++) + set_bit(keymap[i], oj->input_dev->keybit); + + err = input_register_device(oj->input_dev); + if (err) { + printk(KERN_ERR "Unable to register %s input device\n", oj->input_dev->name); + goto fail; + } + + if (!curcial_oj_init()) + goto fail; + + err = request_irq(my_oj->irq, curcial_oj_irq_handler, + IRQF_TRIGGER_NONE, CURCIAL_OJ_NAME, oj); + if (err < 0) { + err = -ENOMEM; + printk(KERN_ERR "request_irq failed\n"); + goto fail; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + oj->early_suspend.suspend = curcial_oj_early_suspend; + oj->early_suspend.resume = curcial_oj_late_resume; +/* oj->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1;*/ + register_early_suspend(&oj->early_suspend); +#endif + err = device_create_file(&(pdev->dev), &dev_attr_reset); + err = device_create_file(&(pdev->dev), &dev_attr_deltax); + err = device_create_file(&(pdev->dev), &dev_attr_deltay); + err = device_create_file(&(pdev->dev), &dev_attr_SumDeltaX); + err = device_create_file(&(pdev->dev), &dev_attr_SumDeltaY); + err = device_create_file(&(pdev->dev), &dev_attr_ap_code); + err = device_create_file(&(pdev->dev), &dev_attr_interval); + err = device_create_file(&(pdev->dev), &dev_attr_normal_th); + err = device_create_file(&(pdev->dev), &dev_attr_polling_delay); + err = device_create_file(&(pdev->dev), &dev_attr_xy_ratio); + err = device_create_file(&(pdev->dev), &dev_attr_debugflag); + err = device_create_file(&(pdev->dev), &dev_attr_xtable); + err = device_create_file(&(pdev->dev), &dev_attr_ytable); + + normal_th = my_oj->normal_th; + xy_ratio = my_oj->xy_ratio; + interval = my_oj->interval; + polling_delay = my_oj->mdelay_time; + debugflag = my_oj->debugflag; + ap_code = my_oj->ap_code; + + printk(KERN_INFO "OJ: driver loaded\n"); + return 0; + +fail: + microp_spi_vote_enable(SPI_OJ, 0); + + if (oj->share_power == false) { + oj->oj_poweron(OJ_POWEROFF); + } + + if (oj->input_dev) { + input_free_device(oj->input_dev); + } + + if (curcial_wq) + destroy_workqueue(curcial_wq); + + if (oj_proc_entry) + remove_proc_entry("oj", NULL); + + return err; +} + +static int __devexit curcial_oj_remove(struct platform_device *pdev) +{ + struct curcial_oj_platform_data *oj = pdev->dev.platform_data; + +#ifdef CONFIG_HAS_EARLYSUSPEND + if (oj->early_suspend.suspend && oj->early_suspend.resume) + unregister_early_suspend(&oj->early_suspend); +#endif + if (oj->share_power == false) { + oj->oj_poweron(OJ_POWEROFF); + } + microp_spi_vote_enable(SPI_OJ, 0); + + if (oj->input_dev) { + input_unregister_device(oj->input_dev); + input_free_device(oj->input_dev); + } + + if (curcial_wq) + destroy_workqueue(curcial_wq); + + if (oj_proc_entry) + remove_proc_entry("oj", NULL); + + device_remove_file(&(pdev->dev), &dev_attr_reset); + device_remove_file(&(pdev->dev), &dev_attr_deltax); + device_remove_file(&(pdev->dev), &dev_attr_deltay); + device_remove_file(&(pdev->dev), &dev_attr_SumDeltaX); + device_remove_file(&(pdev->dev), &dev_attr_SumDeltaY); + device_remove_file(&(pdev->dev), &dev_attr_ap_code); + device_remove_file(&(pdev->dev), &dev_attr_interval); + device_remove_file(&(pdev->dev), &dev_attr_normal_th); + device_remove_file(&(pdev->dev), &dev_attr_polling_delay); + device_remove_file(&(pdev->dev), &dev_attr_xy_ratio); + device_remove_file(&(pdev->dev), &dev_attr_debugflag); + device_remove_file(&(pdev->dev), &dev_attr_xtable); + device_remove_file(&(pdev->dev), &dev_attr_ytable); + printk(KERN_INFO "OJ: driver unloaded\n"); + return 0; +} + +static int __init curcial_oj_module_init(void) +{ + return platform_driver_register(&curcial_oj_device_driver); +} + +static void __exit curcial_oj_module_exit(void) +{ + platform_driver_unregister(&curcial_oj_device_driver); +} + +module_init(curcial_oj_module_init); +module_exit(curcial_oj_module_exit); + +void curcial_oj_send_key(unsigned int code, int value) +{ + if ((my_oj != NULL) && (my_oj->input_dev != NULL)) + input_report_key(my_oj->input_dev, code, value); + else + printk(KERN_WARNING "%s: device not ready...\n", __func__); +} + +MODULE_DESCRIPTION("Crucial OpticalJoystick Driver"); +MODULE_LICENSE("GPL"); From 3589ed60dabace5fd0bde6b776cfe5c7ba8701c4 Mon Sep 17 00:00:00 2001 From: Tony Layher Date: Sat, 12 Feb 2011 12:58:30 -0500 Subject: [PATCH 1693/2556] inc: Fix wifi sleep. * Changed the wifi section of board-incrediblec-mmc.c to match more closely to the bravo. --- arch/arm/mach-msm/board-incrediblec-mmc.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-msm/board-incrediblec-mmc.c b/arch/arm/mach-msm/board-incrediblec-mmc.c index 138e2f0d87fe8..81532d8a5bce9 100644 --- a/arch/arm/mach-msm/board-incrediblec-mmc.c +++ b/arch/arm/mach-msm/board-incrediblec-mmc.c @@ -219,6 +219,7 @@ static unsigned int incrediblec_wifi_status(struct device *dev) static struct msm_mmc_platform_data incrediblec_wifi_data = { .ocr_mask = MMC_VDD_28_29, + .built_in = 1, .status = incrediblec_wifi_status, .register_status_notify = incrediblec_wifi_status_register, .embedded_sdio = &incrediblec_wifi_emb_data, @@ -236,18 +237,16 @@ int incrediblec_wifi_set_carddetect(int val) } EXPORT_SYMBOL(incrediblec_wifi_set_carddetect); +static int incrediblec_wifi_power_state; + int incrediblec_wifi_power(int on) { - int rc = 0; - printk(KERN_INFO "%s: %d\n", __func__, on); if (on) { config_gpio_table(wifi_on_gpio_table, ARRAY_SIZE(wifi_on_gpio_table)); mdelay(50); - if (rc) - return rc; } else { config_gpio_table(wifi_off_gpio_table, ARRAY_SIZE(wifi_off_gpio_table)); @@ -255,14 +254,18 @@ int incrediblec_wifi_power(int on) mdelay(100); gpio_set_value(INCREDIBLEC_GPIO_WIFI_SHUTDOWN_N, on); /* WIFI_SHUTDOWN */ - mdelay(100); + mdelay(200); + + incrediblec_wifi_power_state = on; return 0; } -EXPORT_SYMBOL(incrediblec_wifi_power); + +static int incrediblec_wifi_reset_state; int incrediblec_wifi_reset(int on) { printk(KERN_INFO "%s: do nothing\n", __func__); + incrediblec_wifi_reset_state = on; return 0; } @@ -271,8 +274,6 @@ int __init incrediblec_init_mmc(unsigned int sys_rev) { uint32_t id; - wifi_status_cb = NULL; - printk(KERN_INFO "%s()+\n", __func__); /* initial WIFI_SHUTDOWN# */ From cbf80f48b57e13010ade3f47ae3a35e76d432b76 Mon Sep 17 00:00:00 2001 From: Tony Layher Date: Sat, 12 Feb 2011 13:45:30 -0500 Subject: [PATCH 1694/2556] inc: Update defconfig to use /vendor/firmware. --- arch/arm/configs/cyanogen_incrediblec_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/cyanogen_incrediblec_defconfig b/arch/arm/configs/cyanogen_incrediblec_defconfig index 6ddb19b89697c..af69e40d85b3a 100644 --- a/arch/arm/configs/cyanogen_incrediblec_defconfig +++ b/arch/arm/configs/cyanogen_incrediblec_defconfig @@ -1035,7 +1035,7 @@ CONFIG_SMC91X=y # CONFIG_NETDEV_10000 is not set CONFIG_WLAN=y CONFIG_BCM4329=m -CONFIG_BCM4329_FW_PATH="/system/etc/firmware/fw_bcm4329.bin" +CONFIG_BCM4329_FW_PATH="/vendor/firmware/fw_bcm4329.bin" CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" # CONFIG_HOSTAP is not set # CONFIG_TIWLAN1251 is not set From 745cb022c18318769b679a4338974e65881fccdf Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Sat, 12 Feb 2011 11:15:39 -0800 Subject: [PATCH 1695/2556] inc: Add a few DEBUG options back to the defconfig - SCHED_DEBUG & SCHEDSTATS: useful for diagnostics, low overhead - TIMER_STATS: necessary for powertop, and also low overhead - DEBUG_INFO: useful for debugging, no runtime overhead --- .../configs/cyanogen_incrediblec_defconfig | 51 ++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/arch/arm/configs/cyanogen_incrediblec_defconfig b/arch/arm/configs/cyanogen_incrediblec_defconfig index af69e40d85b3a..30aeeedc31791 100644 --- a/arch/arm/configs/cyanogen_incrediblec_defconfig +++ b/arch/arm/configs/cyanogen_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.37 Kernel Configuration -# Wed Feb 9 18:57:13 2011 +# Fri Feb 11 17:13:49 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -93,6 +93,7 @@ CONFIG_EMBEDDED=y CONFIG_UID16=y # CONFIG_SYSCTL_SYSCALL is not set CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set # CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_HOTPLUG=y CONFIG_PRINTK=y @@ -845,6 +846,8 @@ CONFIG_PREVENT_FIRMWARE_BUILD=y CONFIG_FW_LOADER=y # CONFIG_FIRMWARE_IN_KERNEL is not set CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set # CONFIG_SYS_HYPERVISOR is not set # CONFIG_CONNECTOR is not set CONFIG_MTD=y @@ -1229,6 +1232,7 @@ CONFIG_I2C_MSM=y # CONFIG_I2C_DEBUG_ALGO is not set # CONFIG_I2C_DEBUG_BUS is not set CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set CONFIG_SPI_MASTER=y # @@ -1252,6 +1256,7 @@ CONFIG_SPI_QSD=y # CONFIG_PPS is not set CONFIG_ARCH_REQUIRE_GPIOLIB=y CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set # CONFIG_GPIO_SYSFS is not set # @@ -1481,6 +1486,7 @@ CONFIG_USB_ARCH_HAS_HCD=y # NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may # CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set # CONFIG_USB_GADGET_DEBUG_FILES is not set CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_USB_GADGET_SELECTED=y @@ -1882,14 +1888,51 @@ CONFIG_MAGIC_SYSRQ=y # CONFIG_UNUSED_SYMBOLS is not set # CONFIG_DEBUG_FS is not set # CONFIG_HEADERS_CHECK is not set -# CONFIG_DEBUG_KERNEL is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set # CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +CONFIG_SCHED_DEBUG=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set CONFIG_BKL=y +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set # CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set # CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set # CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set CONFIG_HAVE_FUNCTION_TRACER=y CONFIG_HAVE_DYNAMIC_FTRACE=y CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y @@ -1898,9 +1941,13 @@ CONFIG_TRACING_SUPPORT=y # CONFIG_ATOMIC64_SELFTEST is not set # CONFIG_SAMPLES is not set CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set # CONFIG_STRICT_DEVMEM is not set CONFIG_ARM_UNWIND=y # CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_LL is not set # CONFIG_OC_ETM is not set # From b2cfcee1e97184d8de142d55d12b94f92dc964b9 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Sat, 12 Feb 2011 13:26:19 -0800 Subject: [PATCH 1696/2556] inc: Update the correct USB serial number Since we're not currently using the usb_pdata in devices_htc, we were missing the serial_number update. This adds our own update for that, which also does the rndis ethaddr generation like other boards. --- arch/arm/mach-msm/board-incrediblec.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index f4d6bc2c6d8c0..06e611ccd6afd 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -419,7 +419,7 @@ static struct platform_device usb_mass_storage_device = { #ifdef CONFIG_USB_ANDROID_RNDIS static struct usb_ether_platform_data rndis_pdata = { - /* ethaddr is filled by board_serialno_setup */ + /* ethaddr is filled by incrediblec_board_serialno_setup */ .vendorID = 0x18d1, .vendorDescr = "Google, Inc.", }; @@ -1181,6 +1181,27 @@ static int __init parse_tag_bdaddr(const struct tag *tag) __tagtable(ATAG_BDADDR, parse_tag_bdaddr); +static int __init incrediblec_board_serialno_setup(char *serialno) +{ +#ifdef CONFIG_USB_ANDROID_RNDIS + int i; + char *src = serialno; + + /* create a fake MAC address from our serial number. + * first byte is 0x02 to signify locally administered. + */ + rndis_pdata.ethaddr[0] = 0x02; + for (i = 0; *src; i++) { + /* XOR the USB serial across the remaining bytes */ + rndis_pdata.ethaddr[i % (ETH_ALEN - 1) + 1] ^= *src++; + } +#endif + + android_usb_pdata.serial_number = serialno; + msm_hsusb_pdata.serial_number = serialno; + return 1; +} + static struct msm_acpu_clock_platform_data incrediblec_clock_data = { .acpu_switch_time_us = 20, .max_speed_delta_khz = 256000, @@ -1291,6 +1312,8 @@ static void __init incrediblec_init(void) msm_kgsl_resources[1].end = msm_kgsl_resources[1].start + MSM_GPU_MEM_SIZE - 1; } + incrediblec_board_serialno_setup(board_serialno()); + OJ_BMA_power(); msm_acpu_clock_init(&incrediblec_clock_data); From 60d39734a843eb0b184afb11802c1b1f0d8bffc8 Mon Sep 17 00:00:00 2001 From: Tony Layher Date: Sun, 13 Feb 2011 13:01:52 -0500 Subject: [PATCH 1697/2556] INC: Update defconfig. We dont have the a1026 --- arch/arm/configs/cyanogen_incrediblec_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/cyanogen_incrediblec_defconfig b/arch/arm/configs/cyanogen_incrediblec_defconfig index 30aeeedc31791..8f17d12561467 100644 --- a/arch/arm/configs/cyanogen_incrediblec_defconfig +++ b/arch/arm/configs/cyanogen_incrediblec_defconfig @@ -958,7 +958,7 @@ CONFIG_KERNEL_DEBUGGER_CORE=y # CONFIG_SENSORS_AK8975 is not set CONFIG_SENSORS_AKM8973=y # CONFIG_SENSORS_AKM8976 is not set -CONFIG_VP_A1026=y +# CONFIG_VP_A1026 is not set # CONFIG_DS1682 is not set # CONFIG_TI_DAC7512 is not set CONFIG_UID_STAT=y From 4cee2d987ef909727e7ef9e16933d0a0ab64799d Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 14 Feb 2011 19:07:53 -0800 Subject: [PATCH 1698/2556] inc: Remove the private tpa2018d1 driver We don't have platform data for that chip, even in HTC's kernel, so calling tpa2018d1_set_speaker_amp in incrediblec_speaker_enable was causing a lot of dmesg spam. Kill it. --- arch/arm/mach-msm/Makefile | 2 +- arch/arm/mach-msm/board-incrediblec-audio.c | 2 - .../mach-msm/board-incrediblec-tpa2018d1.c | 368 ------------------ .../mach-msm/board-incrediblec-tpa2018d1.h | 35 -- 4 files changed, 1 insertion(+), 406 deletions(-) delete mode 100644 arch/arm/mach-msm/board-incrediblec-tpa2018d1.c delete mode 100644 arch/arm/mach-msm/board-incrediblec-tpa2018d1.h diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 8edf2b9f9a195..7b37214ca39df 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -114,7 +114,7 @@ obj-$(CONFIG_MACH_INCREDIBLEC) += board-incrediblec-keypad.o board-incrediblec-m obj-$(CONFIG_MACH_INCREDIBLEC) += board-incrediblec-rfkill.o htc_wifi_nvs.o board-incrediblec-audio.o obj-$(CONFIG_MACH_INCREDIBLEC) += board-incrediblec-wifi.o htc_awb_cal.o obj-$(CONFIG_MACH_INCREDIBLEC) += board-incrediblec-microp.o htc_bluetooth.o -obj-$(CONFIG_MACH_INCREDIBLEC) += msm_vibrator.o board-incrediblec-tpa2018d1.o +obj-$(CONFIG_MACH_INCREDIBLEC) += msm_vibrator.o obj-$(CONFIG_MACH_INCREDIBLEC) += proc_engineerid.o board-incrediblec-tpa6130.o obj-$(CONFIG_MACH_INCREDIBLEC) += htc_acoustic_qsd.o diff --git a/arch/arm/mach-msm/board-incrediblec-audio.c b/arch/arm/mach-msm/board-incrediblec-audio.c index ed9b9d7245b6d..622564bd29bb8 100644 --- a/arch/arm/mach-msm/board-incrediblec-audio.c +++ b/arch/arm/mach-msm/board-incrediblec-audio.c @@ -25,7 +25,6 @@ #include "board-incrediblec.h" #include "proc_comm.h" #include "pmic.h" -#include "board-incrediblec-tpa2018d1.h" #if 1 #define D(fmt, args...) printk(KERN_INFO "Audio: "fmt, ##args) @@ -105,7 +104,6 @@ void incrediblec_speaker_enable(int en) pmic_set_spkr_configuration(&scm); } - tpa2018d1_set_speaker_amp(en); } void incrediblec_receiver_enable(int en) diff --git a/arch/arm/mach-msm/board-incrediblec-tpa2018d1.c b/arch/arm/mach-msm/board-incrediblec-tpa2018d1.c deleted file mode 100644 index 9c9e1f51ac1cb..0000000000000 --- a/arch/arm/mach-msm/board-incrediblec-tpa2018d1.c +++ /dev/null @@ -1,368 +0,0 @@ -/* drivers/i2c/chips/tpa2018d1.c - * - * TI TPA2018D1 Speaker Amplifier - * - * Copyright (C) 2009 HTC Corporation - * - * 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. - * - */ - -/* TODO: content validation in TPA2018_SET_CONFIG */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "board-incrediblec-tpa2018d1.h" - -static struct i2c_client *this_client; -static struct tpa2018d1_platform_data *pdata; -static int is_on; -static char spk_amp_cfg[8]; -static const char spk_amp_on[8] = { /* same length as spk_amp_cfg */ - 0x01, 0xc3, 0x20, 0x01, 0x00, 0x08, 0x1a, 0x21 -}; -static const char spk_amp_off[] = {0x01, 0xa2}; - -static DEFINE_MUTEX(spk_amp_lock); -static int tpa2018d1_opened; -static char *config_data; -static int tpa2018d1_num_modes; - -#define DEBUG 0 - -static int tpa2018_i2c_write(const char *txData, int length) -{ - struct i2c_msg msg[] = { - { - .addr = this_client->addr, - .flags = 0, - .len = length, - .buf = txData, - }, - }; - - if (i2c_transfer(this_client->adapter, msg, 1) < 0) { - pr_err("%s: I2C transfer error\n", __func__); - return -EIO; - } else - return 0; -} - -static int tpa2018_i2c_read(char *rxData, int length) -{ - struct i2c_msg msgs[] = { - { - .addr = this_client->addr, - .flags = I2C_M_RD, - .len = length, - .buf = rxData, - }, - }; - - if (i2c_transfer(this_client->adapter, msgs, 1) < 0) { - pr_err("%s: I2C transfer error\n", __func__); - return -EIO; - } - -#if DEBUG - do { - int i = 0; - for (i = 0; i < length; i++) - pr_info("%s: rx[%d] = %2x\n", - __func__, i, rxData[i]); - } while(0); -#endif - - return 0; -} - -static int tpa2018d1_open(struct inode *inode, struct file *file) -{ - int rc = 0; - - mutex_lock(&spk_amp_lock); - - if (tpa2018d1_opened) { - pr_err("%s: busy\n", __func__); - rc = -EBUSY; - goto done; - } - - tpa2018d1_opened = 1; -done: - mutex_unlock(&spk_amp_lock); - return rc; -} - -static int tpa2018d1_release(struct inode *inode, struct file *file) -{ - mutex_lock(&spk_amp_lock); - tpa2018d1_opened = 0; - mutex_unlock(&spk_amp_lock); - - return 0; -} - -static int tpa2018d1_read_config(void __user *argp) -{ - int rc = 0; - unsigned char reg_idx = 0x01; - unsigned char tmp[7]; - - if (!is_on) { - gpio_set_value(pdata->gpio_tpa2018_spk_en, 1); - msleep(5); /* According to TPA2018D1 Spec */ - } - - rc = tpa2018_i2c_write(®_idx, sizeof(reg_idx)); - if (rc < 0) - goto err; - - rc = tpa2018_i2c_read(tmp, sizeof(tmp)); - if (rc < 0) - goto err; - - if (copy_to_user(argp, &tmp, sizeof(tmp))) - rc = -EFAULT; - -err: - if (!is_on) - gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); - return rc; -} - -static long tpa2018d1_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *)arg; - int rc = 0; - int mode = -1; - int offset = 0; - struct tpa2018d1_config_data cfg; - - mutex_lock(&spk_amp_lock); - - switch (cmd) { - case TPA2018_SET_CONFIG: - if (copy_from_user(spk_amp_cfg, argp, sizeof(spk_amp_cfg))) - rc = -EFAULT; - break; - - case TPA2018_READ_CONFIG: - rc = tpa2018d1_read_config(argp); - break; - - case TPA2018_SET_MODE: - if (copy_from_user(&mode, argp, sizeof(mode))) { - rc = -EFAULT; - break; - } - if (mode >= tpa2018d1_num_modes || mode < 0) { - pr_err("%s: unsupported tpa2018d1 mode %d\n", - __func__, mode); - rc = -EINVAL; - break; - } - if (!config_data) { - pr_err("%s: no config data!\n", __func__); - rc = -EIO; - break; - } - memcpy(spk_amp_cfg, config_data + mode * TPA2018D1_CMD_LEN, - TPA2018D1_CMD_LEN); - break; - - case TPA2018_SET_PARAM: - if (copy_from_user(&cfg, argp, sizeof(cfg))) { - pr_err("%s: copy from user failed.\n", __func__); - rc = -EFAULT; - break; - } - tpa2018d1_num_modes = cfg.mode_num; - if (tpa2018d1_num_modes > TPA2018_NUM_MODES) { - pr_err("%s: invalid number of modes %d\n", __func__, - tpa2018d1_num_modes); - rc = -EINVAL; - break; - } - if (cfg.data_len != tpa2018d1_num_modes*TPA2018D1_CMD_LEN) { - pr_err("%s: invalid data length %d, expecting %d\n", - __func__, cfg.data_len, - tpa2018d1_num_modes * TPA2018D1_CMD_LEN); - rc = -EINVAL; - break; - } - /* Free the old data */ - if (config_data) - kfree(config_data); - config_data = kmalloc(cfg.data_len, GFP_KERNEL); - if (!config_data) { - pr_err("%s: out of memory\n", __func__); - rc = -ENOMEM; - break; - } - if (copy_from_user(config_data, cfg.cmd_data, cfg.data_len)) { - pr_err("%s: copy data from user failed.\n", __func__); - kfree(config_data); - config_data = NULL; - rc = -EFAULT; - break; - } - /* replace default setting with playback setting */ - if (tpa2018d1_num_modes >= TPA2018_MODE_PLAYBACK) { - offset = TPA2018_MODE_PLAYBACK * TPA2018D1_CMD_LEN; - memcpy(spk_amp_cfg, config_data + offset, - TPA2018D1_CMD_LEN); - } - break; - - default: - pr_err("%s: invalid command %d\n", __func__, _IOC_NR(cmd)); - rc = -EINVAL; - break; - } - mutex_unlock(&spk_amp_lock); - return rc; -} - -static struct file_operations tpa2018d1_fops = { - .owner = THIS_MODULE, - .open = tpa2018d1_open, - .release = tpa2018d1_release, - .unlocked_ioctl = tpa2018d1_ioctl, -}; - -static struct miscdevice tpa2018d1_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = "tpa2018d1", - .fops = &tpa2018d1_fops, -}; - -void tpa2018d1_set_speaker_amp(int on) -{ - if (!pdata) { - pr_err("%s: no platform data!\n", __func__); - return; - } - mutex_lock(&spk_amp_lock); - if (on && !is_on) { - gpio_set_value(pdata->gpio_tpa2018_spk_en, 1); - msleep(5); /* According to TPA2018D1 Spec */ - - if (tpa2018_i2c_write(spk_amp_cfg, sizeof(spk_amp_cfg)) == 0) { - is_on = 1; - pr_info("%s: ON\n", __func__); - } - } else if (!on && is_on) { - if (tpa2018_i2c_write(spk_amp_off, sizeof(spk_amp_off)) == 0) { - is_on = 0; - msleep(2); - gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); - pr_info("%s: OFF\n", __func__); - } - } - mutex_unlock(&spk_amp_lock); -} - -static int tpa2018d1_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - int ret = 0; - - pdata = client->dev.platform_data; - - if (!pdata) { - ret = -EINVAL; - pr_err("%s: platform data is NULL\n", __func__); - goto err_no_pdata; - } - - this_client = client; - - ret = gpio_request(pdata->gpio_tpa2018_spk_en, "tpa2018"); - if (ret < 0) { - pr_err("%s: gpio request aud_spk_en pin failed\n", __func__); - goto err_free_gpio; - } - - ret = gpio_direction_output(pdata->gpio_tpa2018_spk_en, 1); - if (ret < 0) { - pr_err("%s: request aud_spk_en gpio direction failed\n", - __func__); - goto err_free_gpio; - } - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - pr_err("%s: i2c check functionality error\n", __func__); - ret = -ENODEV; - goto err_free_gpio; - } - - gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); /* Default Low */ - - ret = misc_register(&tpa2018d1_device); - if (ret) { - pr_err("%s: tpa2018d1_device register failed\n", __func__); - goto err_free_gpio; - } - memcpy(spk_amp_cfg, spk_amp_on, sizeof(spk_amp_on)); - return 0; - -err_free_gpio: - gpio_free(pdata->gpio_tpa2018_spk_en); -err_no_pdata: - return ret; -} - -static int tpa2018d1_suspend(struct i2c_client *client, pm_message_t mesg) -{ - return 0; -} - -static int tpa2018d1_resume(struct i2c_client *client) -{ - return 0; -} - -static const struct i2c_device_id tpa2018d1_id[] = { - { TPA2018D1_I2C_NAME, 0 }, - { } -}; - -static struct i2c_driver tpa2018d1_driver = { - .probe = tpa2018d1_probe, - .suspend = tpa2018d1_suspend, - .resume = tpa2018d1_resume, - .id_table = tpa2018d1_id, - .driver = { - .name = TPA2018D1_I2C_NAME, - }, -}; - -static int __init tpa2018d1_init(void) -{ - pr_info("%s\n", __func__); - return i2c_add_driver(&tpa2018d1_driver); -} - -module_init(tpa2018d1_init); - -MODULE_DESCRIPTION("tpa2018d1 speaker amp driver"); -MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-incrediblec-tpa2018d1.h b/arch/arm/mach-msm/board-incrediblec-tpa2018d1.h deleted file mode 100644 index dc11012209454..0000000000000 --- a/arch/arm/mach-msm/board-incrediblec-tpa2018d1.h +++ /dev/null @@ -1,35 +0,0 @@ -/* include/linux/tpa2018d1.h - tpa2018d1 speaker amplifier driver - * - * Copyright (C) 2009 HTC Corporation. - * - * 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 __ASM_ARM_ARCH_TPA2018D1_H -#define __ASM_ARM_ARCH_TPA2018D1_H - -#define TPA2018D1_I2C_NAME "tpa2018d1" -#define TPA2018D1_CMD_LEN 8 - -struct tpa2018d1_platform_data { - uint32_t gpio_tpa2018_spk_en; -}; - -struct tpa2018d1_config_data { - unsigned char *cmd_data; /* [mode][cmd_len][cmds..] */ - unsigned int mode_num; - unsigned int data_len; -}; - -extern void tpa2018d1_set_speaker_amp(int on); - -#endif /* __ASM_ARM_ARCH_TPA2018D1_H */ From ff36330f703868c2ef2ee577d8f89a5c380576a6 Mon Sep 17 00:00:00 2001 From: Tony Layher Date: Wed, 16 Feb 2011 20:52:27 -0500 Subject: [PATCH 1699/2556] inc: update defconfig. * enable CIFS support directly in the kernel. * disable NFS related options. --- .../configs/cyanogen_incrediblec_defconfig | 28 ++++++------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/arch/arm/configs/cyanogen_incrediblec_defconfig b/arch/arm/configs/cyanogen_incrediblec_defconfig index 8f17d12561467..a83c1167e7b4a 100644 --- a/arch/arm/configs/cyanogen_incrediblec_defconfig +++ b/arch/arm/configs/cyanogen_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.37 Kernel Configuration -# Fri Feb 11 17:13:49 2011 +# Wed Feb 16 20:35:08 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -153,8 +153,10 @@ CONFIG_LBDAF=y CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y +# CONFIG_IOSCHED_BFQ is not set # CONFIG_DEFAULT_DEADLINE is not set CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_BFQ is not set # CONFIG_DEFAULT_NOOP is not set CONFIG_DEFAULT_IOSCHED="cfq" # CONFIG_INLINE_SPIN_TRYLOCK is not set @@ -1718,7 +1720,7 @@ CONFIG_JBD2=y CONFIG_FS_MBCACHE=y # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set -CONFIG_FS_POSIX_ACL=y +# CONFIG_FS_POSIX_ACL is not set # CONFIG_XFS_FS is not set # CONFIG_GFS2_FS is not set # CONFIG_OCFS2_FS is not set @@ -1800,30 +1802,18 @@ CONFIG_YAFFS_XATTR=y # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set CONFIG_NETWORK_FILESYSTEMS=y -CONFIG_NFS_FS=m -CONFIG_NFS_V3=y -CONFIG_NFS_V3_ACL=y -CONFIG_NFS_V4=y -# CONFIG_NFS_V4_1 is not set -# CONFIG_NFS_USE_LEGACY_DNS is not set -CONFIG_NFS_USE_KERNEL_DNS=y -# CONFIG_NFS_USE_NEW_IDMAPPER is not set +# CONFIG_NFS_FS is not set # CONFIG_NFSD is not set -CONFIG_LOCKD=m -CONFIG_LOCKD_V4=y -CONFIG_NFS_ACL_SUPPORT=m -CONFIG_NFS_COMMON=y -CONFIG_SUNRPC=m -CONFIG_SUNRPC_GSS=m -CONFIG_RPCSEC_GSS_KRB5=m # CONFIG_CEPH_FS is not set -CONFIG_CIFS=m +CONFIG_CIFS=y # CONFIG_CIFS_STATS is not set CONFIG_CIFS_WEAK_PW_HASH=y # CONFIG_CIFS_UPCALL is not set -# CONFIG_CIFS_XATTR is not set +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y # CONFIG_CIFS_DEBUG2 is not set # CONFIG_CIFS_DFS_UPCALL is not set +# CONFIG_CIFS_ACL is not set # CONFIG_CIFS_EXPERIMENTAL is not set # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set From 9bbcf21d60a1a08c33019ee45bea37bca0e29577 Mon Sep 17 00:00:00 2001 From: Tony Layher Date: Sun, 13 Mar 2011 18:21:03 -0400 Subject: [PATCH 1700/2556] INC: Raise gains for handset and speaker volumes. --- arch/arm/mach-msm/board-incrediblec-audio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/board-incrediblec-audio.c b/arch/arm/mach-msm/board-incrediblec-audio.c index 622564bd29bb8..9ae8729a6669b 100644 --- a/arch/arm/mach-msm/board-incrediblec-audio.c +++ b/arch/arm/mach-msm/board-incrediblec-audio.c @@ -38,8 +38,8 @@ static int headset_status = 0; static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { [Q6_HW_HANDSET] = { - .min_gain = -2000, - .max_gain = 0, + .min_gain = -1600, + .max_gain = 400, }, [Q6_HW_HEADSET] = { .min_gain = -2000, @@ -47,7 +47,7 @@ static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { }, [Q6_HW_SPEAKER] = { .min_gain = -1500, - .max_gain = 0, + .max_gain = 400, }, [Q6_HW_TTY] = { .min_gain = -2000, From 3e0f2e9544e5ddd7d16101077220805f69c79e89 Mon Sep 17 00:00:00 2001 From: Tony Layher Date: Tue, 22 Feb 2011 23:47:22 -0500 Subject: [PATCH 1701/2556] INC: update defconfig to use Classic RCU --- arch/arm/configs/cyanogen_incrediblec_defconfig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/configs/cyanogen_incrediblec_defconfig b/arch/arm/configs/cyanogen_incrediblec_defconfig index a83c1167e7b4a..beafa1ea47539 100644 --- a/arch/arm/configs/cyanogen_incrediblec_defconfig +++ b/arch/arm/configs/cyanogen_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux/arm 2.6.37 Kernel Configuration -# Wed Feb 16 20:35:08 2011 +# Linux/arm 2.6.37.1 Kernel Configuration +# Tue Feb 22 23:27:44 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -56,8 +56,9 @@ CONFIG_KERNEL_LZMA=y # # RCU Subsystem # +CONFIG_CLASSIC_RCU=y # CONFIG_TREE_PREEMPT_RCU is not set -CONFIG_TINY_RCU=y +# CONFIG_TINY_RCU is not set # CONFIG_TINY_PREEMPT_RCU is not set # CONFIG_PREEMPT_RCU is not set # CONFIG_TREE_RCU_TRACE is not set @@ -156,7 +157,6 @@ CONFIG_IOSCHED_CFQ=y # CONFIG_IOSCHED_BFQ is not set # CONFIG_DEFAULT_DEADLINE is not set CONFIG_DEFAULT_CFQ=y -# CONFIG_DEFAULT_BFQ is not set # CONFIG_DEFAULT_NOOP is not set CONFIG_DEFAULT_IOSCHED="cfq" # CONFIG_INLINE_SPIN_TRYLOCK is not set From 2db5137560f2606b78403d811e3eeb85ae007e1c Mon Sep 17 00:00:00 2001 From: Tony Layher Date: Thu, 17 Mar 2011 08:51:33 -0400 Subject: [PATCH 1702/2556] INC: update defconfig to use Tree Preempt RCU --- .../configs/cyanogen_incrediblec_defconfig | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/arch/arm/configs/cyanogen_incrediblec_defconfig b/arch/arm/configs/cyanogen_incrediblec_defconfig index beafa1ea47539..7774c77ece5a3 100644 --- a/arch/arm/configs/cyanogen_incrediblec_defconfig +++ b/arch/arm/configs/cyanogen_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux/arm 2.6.37.1 Kernel Configuration -# Tue Feb 22 23:27:44 2011 +# Linux/arm 2.6.37.3 Kernel Configuration +# Mon Mar 14 19:12:22 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -33,7 +33,6 @@ CONFIG_HAVE_IRQ_WORK=y # CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y -CONFIG_LOCK_KERNEL=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="arm-eabi-" CONFIG_LOCALVERSION="-cyanogenmod" @@ -56,11 +55,14 @@ CONFIG_KERNEL_LZMA=y # # RCU Subsystem # -CONFIG_CLASSIC_RCU=y -# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_CLASSIC_RCU is not set +CONFIG_TREE_PREEMPT_RCU=y # CONFIG_TINY_RCU is not set # CONFIG_TINY_PREEMPT_RCU is not set -# CONFIG_PREEMPT_RCU is not set +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_TRACE is not set +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set # CONFIG_TREE_RCU_TRACE is not set CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y @@ -713,9 +715,6 @@ CONFIG_BRIDGE_IGMP_SNOOPING=y # CONFIG_DECNET is not set CONFIG_LLC=y # CONFIG_LLC2 is not set -# CONFIG_IPX is not set -# CONFIG_ATALK is not set -# CONFIG_X25 is not set # CONFIG_LAPB is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set @@ -1533,6 +1532,7 @@ CONFIG_MMC=y CONFIG_MMC_UNSAFE_RESUME=y CONFIG_MMC_EMBEDDED_SDIO=y CONFIG_MMC_PARANOID_SD_INIT=y +# CONFIG_MMC_USE_ONLY_HOST_DEFINED_FREQUENCY is not set # # MMC/SD/SDIO Card Drivers @@ -1684,7 +1684,6 @@ CONFIG_ANDROID_TIMED_OUTPUT=y CONFIG_ANDROID_TIMED_GPIO=y CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_POHMELFS is not set -# CONFIG_AUTOFS_FS is not set # CONFIG_IIO is not set # CONFIG_ZRAM is not set # CONFIG_BATMAN_ADV is not set @@ -1696,7 +1695,6 @@ CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_ST_BT is not set # CONFIG_ADIS16255 is not set # CONFIG_LIRC_STAGING is not set -# CONFIG_SMB_FS is not set CONFIG_MACH_NO_WESTBRIDGE=y # CONFIG_ATH6K_LEGACY is not set # CONFIG_FT1000 is not set @@ -1745,7 +1743,6 @@ CONFIG_INOTIFY_USER=y # CD-ROM/DVD Filesystems # # CONFIG_ISO9660_FS is not set -# CONFIG_UDF_FS is not set # # DOS/FAT/NT Filesystems @@ -1769,7 +1766,6 @@ CONFIG_TMPFS=y # CONFIG_HUGETLB_PAGE is not set # CONFIG_CONFIGFS_FS is not set CONFIG_MISC_FILESYSTEMS=y -# CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set # CONFIG_ECRYPT_FS is not set # CONFIG_HFS_FS is not set @@ -1796,11 +1792,9 @@ CONFIG_YAFFS_XATTR=y # CONFIG_VXFS_FS is not set # CONFIG_MINIX_FS is not set # CONFIG_OMFS_FS is not set -# CONFIG_HPFS_FS is not set # CONFIG_QNX4FS_FS is not set # CONFIG_ROMFS_FS is not set # CONFIG_SYSV_FS is not set -# CONFIG_UFS_FS is not set CONFIG_NETWORK_FILESYSTEMS=y # CONFIG_NFS_FS is not set # CONFIG_NFSD is not set @@ -1894,7 +1888,7 @@ CONFIG_TIMER_STATS=y # CONFIG_RT_MUTEX_TESTER is not set # CONFIG_DEBUG_SPINLOCK is not set # CONFIG_DEBUG_MUTEXES is not set -CONFIG_BKL=y +# CONFIG_BKL is not set # CONFIG_DEBUG_LOCK_ALLOC is not set # CONFIG_PROVE_LOCKING is not set # CONFIG_SPARSE_RCU_POINTER is not set @@ -1916,6 +1910,10 @@ CONFIG_DEBUG_INFO=y # CONFIG_DEBUG_CREDENTIALS is not set # CONFIG_BOOT_PRINTK_DELAY is not set # CONFIG_RCU_TORTURE_TEST is not set +CONFIG_RCU_CPU_STALL_DETECTOR=y +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_RCU_CPU_STALL_DETECTOR_RUNNABLE=y +CONFIG_RCU_CPU_STALL_VERBOSE=y # CONFIG_BACKTRACE_SELF_TEST is not set # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set # CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set From 460e14c19efdb17e9207f75ac41371c8a5863a73 Mon Sep 17 00:00:00 2001 From: Tony Layher Date: Fri, 18 Mar 2011 20:57:13 -0400 Subject: [PATCH 1703/2556] INC: adjust lightsensor values to allow userspace to scale correctly. --- arch/arm/mach-msm/board-incrediblec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index 06e611ccd6afd..d17288861ee14 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -153,7 +153,7 @@ static struct microp_function_config microp_functions_1[] = { static struct microp_function_config microp_lightsensor = { .name = "light_sensor", .category = MICROP_FUNCTION_LSENSOR, - .levels = { 0, 11, 16, 22, 75, 209, 362, 488, 560, 0x3FF }, + .levels = { 1, 11, 16, 22, 75, 209, 362, 488, 560, 0x3FF }, .channel = 3, .int_pin = 1 << 9, .golden_adc = 0xD2, From 34cc1a1b674e3cb3b35e6ad92577e1945767d59e Mon Sep 17 00:00:00 2001 From: Tony Layher Date: Fri, 18 Mar 2011 22:04:52 -0400 Subject: [PATCH 1704/2556] INC: update defconfig. Use Tiny preempt rcu with boost, vice tree. --- .../configs/cyanogen_incrediblec_defconfig | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/arch/arm/configs/cyanogen_incrediblec_defconfig b/arch/arm/configs/cyanogen_incrediblec_defconfig index 7774c77ece5a3..c2a2c089b8901 100644 --- a/arch/arm/configs/cyanogen_incrediblec_defconfig +++ b/arch/arm/configs/cyanogen_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux/arm 2.6.37.3 Kernel Configuration -# Mon Mar 14 19:12:22 2011 +# Linux/arm 2.6.37.4 Kernel Configuration +# Fri Mar 18 20:26:57 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -55,15 +55,15 @@ CONFIG_KERNEL_LZMA=y # # RCU Subsystem # -# CONFIG_CLASSIC_RCU is not set -CONFIG_TREE_PREEMPT_RCU=y +# CONFIG_TREE_PREEMPT_RCU is not set # CONFIG_TINY_RCU is not set -# CONFIG_TINY_PREEMPT_RCU is not set +CONFIG_TINY_PREEMPT_RCU=y CONFIG_PREEMPT_RCU=y # CONFIG_RCU_TRACE is not set -CONFIG_RCU_FANOUT=32 -# CONFIG_RCU_FANOUT_EXACT is not set # CONFIG_TREE_RCU_TRACE is not set +CONFIG_RCU_BOOST=y +CONFIG_RCU_BOOST_PRIO=1 +CONFIG_RCU_BOOST_DELAY=500 CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=17 @@ -156,9 +156,11 @@ CONFIG_LBDAF=y CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y -# CONFIG_IOSCHED_BFQ is not set +CONFIG_IOSCHED_BFQ=y +# CONFIG_CGROUP_BFQIO is not set # CONFIG_DEFAULT_DEADLINE is not set CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_BFQ is not set # CONFIG_DEFAULT_NOOP is not set CONFIG_DEFAULT_IOSCHED="cfq" # CONFIG_INLINE_SPIN_TRYLOCK is not set @@ -1910,10 +1912,6 @@ CONFIG_DEBUG_INFO=y # CONFIG_DEBUG_CREDENTIALS is not set # CONFIG_BOOT_PRINTK_DELAY is not set # CONFIG_RCU_TORTURE_TEST is not set -CONFIG_RCU_CPU_STALL_DETECTOR=y -CONFIG_RCU_CPU_STALL_TIMEOUT=60 -CONFIG_RCU_CPU_STALL_DETECTOR_RUNNABLE=y -CONFIG_RCU_CPU_STALL_VERBOSE=y # CONFIG_BACKTRACE_SELF_TEST is not set # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set # CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set From 8291b83c983c714822a3f33d971b95bfddc69416 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Sun, 10 Apr 2011 21:19:15 -0400 Subject: [PATCH 1705/2556] cm: inc: Update defconfig --- .../configs/cyanogen_incrediblec_defconfig | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/arch/arm/configs/cyanogen_incrediblec_defconfig b/arch/arm/configs/cyanogen_incrediblec_defconfig index c2a2c089b8901..6d5dcdeb6bbab 100644 --- a/arch/arm/configs/cyanogen_incrediblec_defconfig +++ b/arch/arm/configs/cyanogen_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.37.4 Kernel Configuration -# Fri Mar 18 20:26:57 2011 +# Sun Apr 10 21:21:14 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -327,7 +327,6 @@ CONFIG_MSM_CPU_FREQ_MAX=998000 CONFIG_MSM_CPU_FREQ_MIN=245760 # CONFIG_AXI_SCREEN_POLICY is not set # CONFIG_MSM_HW3D is not set -# CONFIG_HTC_ACOUSTIC is not set CONFIG_HTC_ACOUSTIC_QSD=y CONFIG_MSM_QDSP6=y CONFIG_WIFI_CONTROL_FUNC=y @@ -1378,20 +1377,7 @@ CONFIG_MEDIA_SUPPORT=y # # Multimedia drivers # -CONFIG_IR_CORE=y -CONFIG_VIDEO_IR=y -CONFIG_LIRC=y -CONFIG_RC_MAP=y -CONFIG_IR_NEC_DECODER=y -CONFIG_IR_RC5_DECODER=y -CONFIG_IR_RC6_DECODER=y -CONFIG_IR_JVC_DECODER=y -CONFIG_IR_SONY_DECODER=y -CONFIG_IR_RC5_SZ_DECODER=y -CONFIG_IR_LIRC_CODEC=y -# CONFIG_IR_IMON is not set -# CONFIG_IR_MCEUSB is not set -# CONFIG_IR_STREAMZAP is not set +# CONFIG_IR_CORE is not set # # Qualcomm MSM Camera And Video @@ -1696,7 +1682,6 @@ CONFIG_ANDROID_LOW_MEMORY_KILLER=y # # CONFIG_ST_BT is not set # CONFIG_ADIS16255 is not set -# CONFIG_LIRC_STAGING is not set CONFIG_MACH_NO_WESTBRIDGE=y # CONFIG_ATH6K_LEGACY is not set # CONFIG_FT1000 is not set From d9abba4e026d762179a5fbf765f1f0920c4fbac8 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Sun, 10 Apr 2011 21:22:59 -0400 Subject: [PATCH 1706/2556] msm: inc: Fix include for cm3602 --- arch/arm/mach-msm/board-incrediblec-microp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-incrediblec-microp.c b/arch/arm/mach-msm/board-incrediblec-microp.c index c535981dab3f5..5f386156609e2 100644 --- a/arch/arm/mach-msm/board-incrediblec-microp.c +++ b/arch/arm/mach-msm/board-incrediblec-microp.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include From 0fc0caa75c3c9104a347dd869e5998be54c4ac97 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Sun, 10 Apr 2011 21:25:15 -0400 Subject: [PATCH 1707/2556] msm: Disable MACH_INCREDIBLEC by default --- arch/arm/mach-msm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index e23fb2cc1ee3a..db55b41def890 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -323,7 +323,7 @@ config MACH_INCREDIBLE config MACH_INCREDIBLEC depends on ARCH_QSD8X50 - default y + default n bool "IncredibleC" help Select this to support the IncredibleC device From 22e26ebbf3964fc2df0c336e6f22fd64bbbc7245 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Mon, 11 Apr 2011 19:15:54 -0400 Subject: [PATCH 1708/2556] drivers:input: Fix typo in cm3602 microp driver (via Kali-) --- drivers/input/misc/cm3602_lightsensor_microp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/cm3602_lightsensor_microp.c b/drivers/input/misc/cm3602_lightsensor_microp.c index 10c72f5675cde..ee6b9e86eb57d 100644 --- a/drivers/input/misc/cm3602_lightsensor_microp.c +++ b/drivers/input/misc/cm3602_lightsensor_microp.c @@ -210,7 +210,7 @@ static void enable_intr_do_work(struct work_struct *w) } } - report_lightseneor_data(); + report_lightsensor_data(); } static void lightsensor_do_work(struct work_struct *w) @@ -221,7 +221,7 @@ static void lightsensor_do_work(struct work_struct *w) msleep(300); } - report_lightseneor_data(); + report_lightsensor_data(); } static irqreturn_t lightsensor_irq_handler(int irq, void *data) From 3a5fdf47190546a179219c980757619ef9b0c2aa Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Sat, 16 Apr 2011 00:18:58 -0700 Subject: [PATCH 1709/2556] inc: Unified fixes for camera and proximity - Include capella_cm3602_htc.h to get the right structure and callbacks for the driver we're actually using. - Define a dummy camera_set_source for the ov8810 driver so it doesn't call a NULL function pointer. --- arch/arm/mach-msm/board-incrediblec.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index d17288861ee14..160e37fc8185a 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -897,6 +897,12 @@ static void config_camera_off_gpios(void) ARRAY_SIZE(camera_off_gpio_table)); } +enum msm_camera_source camera_source; +static void incrediblec_camera_set_source(enum msm_camera_source source) +{ + camera_source = source; +} + static struct resource msm_camera_resources[] = { { .start = MSM_VFE_PHYS, @@ -930,6 +936,7 @@ static struct msm_camera_sensor_info msm_camera_sensor_ov8810_data = { .sensor_name = "ov8810", .sensor_reset = INCREDIBLEC_CAM_RST, /* CAM1_RST */ .sensor_pwd = INCREDIBLEC_CAM_PWD, /* CAM1_PWDN, enabled in a9 */ + .camera_set_source = incrediblec_camera_set_source, .pdata = &msm_camera_device_data, .resource = msm_camera_resources, .num_resources = ARRAY_SIZE(msm_camera_resources), From 9debf461bae5946434b1b4fa81399a52e2614bc2 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sun, 1 May 2011 20:22:04 -0400 Subject: [PATCH 1710/2556] INC: add optical joystick support --- drivers/input/Kconfig | 2 + drivers/input/Makefile | 2 + drivers/input/misc/gpio_input.c | 43 +++++++++++++++++ drivers/input/misc/gpio_matrix.c | 23 +++++++++ drivers/input/opticaljoystick/Kconfig | 34 ++++++++++++++ drivers/input/opticaljoystick/Makefile | 7 +++ drivers/input/opticaljoystick/curcial.h | 62 +++++++++++++++++++++++++ include/linux/curcial_oj.h | 43 +++++++++++++++++ include/linux/gpio_event.h | 3 ++ 9 files changed, 219 insertions(+) create mode 100644 drivers/input/opticaljoystick/Kconfig create mode 100644 drivers/input/opticaljoystick/Makefile create mode 100644 drivers/input/opticaljoystick/curcial.h create mode 100644 include/linux/curcial_oj.h diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 47e8bd49d388d..17e0dea65b3b8 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -194,6 +194,8 @@ source "drivers/input/touchscreen/Kconfig" source "drivers/input/misc/Kconfig" +source "drivers/input/opticaljoystick/Kconfig" + endif menu "Hardware I/O ports" diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 867badd4b47f7..8b4d6fca68acd 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -27,3 +27,5 @@ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o obj-$(CONFIG_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o + +obj-$(CONFIG_INPUT_OPTICALJOYSTICK) += opticaljoystick/ diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c index 758df480600ba..23c81937897d3 100644 --- a/drivers/input/misc/gpio_input.c +++ b/drivers/input/misc/gpio_input.c @@ -21,6 +21,9 @@ #include #include #include +#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL +#include +#endif enum { DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */ @@ -127,6 +130,14 @@ static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer) pr_info("gpio_keys_scan_keys: key %x-%x, %d (%d) " "changed to %d\n", ds->info->type, key_entry->code, i, key_entry->gpio, pressed); +#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL + if (key_entry->code == BTN_MOUSE) { + pr_info("gpio_keys_scan_keys: OJ action key %x-%x, %d (%d) " + "changed to %d\n", ds->info->type, + key_entry->code, i, key_entry->gpio, pressed); + curcial_oj_send_key(BTN_MOUSE, pressed); + } else +#endif input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, key_entry->code, pressed); } @@ -152,6 +163,35 @@ static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer) return HRTIMER_NORESTART; } +#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL +void keypad_reprort_keycode(struct gpio_key_state *ks){ + struct gpio_input_state *ds = ks->ds; + int keymap_index = ks - ds->key_state; + const struct gpio_event_direct_entry *key_entry; + int pressed; + + key_entry = &ds->info->keymap[keymap_index]; + + pressed = gpio_get_value(key_entry->gpio) ^ + !(ds->info->flags & GPIOEDF_ACTIVE_HIGH); + if (ds->info->flags & GPIOEDF_PRINT_KEYS) + pr_info("keypad_reprort_keycode: key %x-%x, %d " + "(%d) changed to %d\n", + ds->info->type, key_entry->code, keymap_index, + key_entry->gpio, pressed); + + if (ds->info->info.oj_btn && key_entry->code == BTN_MOUSE){ + curcial_oj_send_key(BTN_MOUSE, pressed); + pr_info("keypad_reprort_keycode: OJ key %x-%x, %d " + "(%d) changed to %d\n", + ds->info->type, key_entry->code, keymap_index, + key_entry->gpio, pressed); + } else + input_event(ds->input_devs->dev[key_entry->dev], + ds->info->type, key_entry->code, pressed); +} +#endif + static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id) { struct gpio_key_state *ks = dev_id; @@ -196,6 +236,9 @@ static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id) key_entry->gpio, pressed); input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, key_entry->code, pressed); +#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL + keypad_reprort_keycode(ks); +#endif } return IRQ_HANDLED; } diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c index 227eb8fe3c09f..96276cd1270f5 100644 --- a/drivers/input/misc/gpio_matrix.c +++ b/drivers/input/misc/gpio_matrix.c @@ -21,6 +21,11 @@ #include #include +#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL +#include +#include +#endif + struct gpio_kp { struct gpio_event_input_devs *input_devs; struct gpio_event_matrix_info *keypad_info; @@ -111,6 +116,9 @@ static void report_key(struct gpio_kp *kp, int key_index, int out, int in) unsigned short keyentry = mi->keymap[key_index]; unsigned short keycode = keyentry & MATRIX_KEY_MASK; unsigned short dev = keyentry >> MATRIX_CODE_BITS; +#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL + static unsigned need_send_spec_key = 1; +#endif if (pressed != test_bit(keycode, kp->input_devs->dev[dev]->key)) { if (keycode == KEY_RESERVED) { @@ -125,9 +133,24 @@ static void report_key(struct gpio_kp *kp, int key_index, int out, int in) "changed to %d\n", keycode, out, in, mi->output_gpios[out], mi->input_gpios[in], pressed); +#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL + if (mi->info.oj_btn && keycode == BTN_MOUSE) + ; + else +#endif input_report_key(kp->input_devs->dev[dev], keycode, pressed); } } +#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL + if (mi->info.oj_btn && keycode == BTN_MOUSE) { + if (need_send_spec_key == pressed) { + curcial_oj_send_key(keycode, pressed); + need_send_spec_key = !pressed; + printk(KERN_INFO "%s: send OJ action key, pressed: %d\n", + __func__, need_send_spec_key); + } + } +#endif } static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer) diff --git a/drivers/input/opticaljoystick/Kconfig b/drivers/input/opticaljoystick/Kconfig new file mode 100644 index 0000000000000..cd9c812c0a37d --- /dev/null +++ b/drivers/input/opticaljoystick/Kconfig @@ -0,0 +1,34 @@ +# +# Touchscreen driver configuration +# +menuconfig INPUT_OPTICALJOYSTICK + bool "Opticaljoystick" + help + Say Y here, and a list of supported optical joystick will be displayed. + This option doesn't affect the kernel. + + If unsure, say Y. + +if INPUT_OPTICALJOYSTICK + +config OPTICALJOYSTICK_CRUCIAL + boolean + +choice + prompt "Interface" + +config OPTICALJOYSTICK_CRUCIAL_uP + boolean "Crucial Optical Joystick (microP)" +# depends on MICROP_COMMON + select OPTICALJOYSTICK_CRUCIAL + help + +config OPTICALJOYSTICK_CRUCIAL_SPI + boolean "Crucial Optical Joystick (SPI)" +# depends on SPI_CRUCIAL_OJ + select OPTICALJOYSTICK_CRUCIAL + help + +endchoice + +endif diff --git a/drivers/input/opticaljoystick/Makefile b/drivers/input/opticaljoystick/Makefile new file mode 100644 index 0000000000000..c91575771e4bd --- /dev/null +++ b/drivers/input/opticaljoystick/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the touchscreen drivers. +# + +# Each configuration option enables a list of files. +obj-$(CONFIG_OPTICALJOYSTICK_CRUCIAL_uP) += curcial.o +obj-$(CONFIG_OPTICALJOYSTICK_CRUCIAL_SPI) += curcial_spi.o diff --git a/drivers/input/opticaljoystick/curcial.h b/drivers/input/opticaljoystick/curcial.h new file mode 100644 index 0000000000000..c5f7edae8f533 --- /dev/null +++ b/drivers/input/opticaljoystick/curcial.h @@ -0,0 +1,62 @@ +/* drivers/input/opticaljoystick/curcial.h + * + * Copyright (C) 2009 HTC Corporation. + * + * 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 _CURCIAL_H +#define _CURCIAL_H + +#include + +typedef enum { + OJ_KEY_RIGHT = KEY_RIGHT, + OJ_KEY_LEFT = KEY_LEFT, + OJ_KEY_UP = KEY_UP, + OJ_KEY_DOWN = KEY_DOWN, +/* OJ_KEY_CLICK = KEY_REPLY,*/ + OJ_KEY_NONE +}OJKeyEvt_T; + +typedef enum { + OJ_TOUCH_NONE_EVT = 0, + OJ_TOUCH_PRESS_EVT, + OJ_TOUCH_RELEASE_EVT, + OJ_TOUCH_CLICK_EVT +}OJTouchEvt_T; + +typedef struct { + int8_t deltaX; + int8_t deltaY; + int8_t shtHi; + int8_t shtLo; + uint8_t squal; + uint16_t key; +}OJData_T; + +enum { + OJ_QUEUE_01 = 0, + OJ_QUEUE_02, + OJ_QUEUE_03, + OJ_QUEUE_04, + OJ_QUEUE_05, + OJ_QUEUE_MAX +}; + +extern OJTouchEvt_T OJ_SoftClick_Event(OJData_T* OJData); +extern OJTouchEvt_T gTouchEvt; +extern uint8_t gSqRatio; +extern uint8_t gdeltamod; +extern uint8_t gPressBufCnt; +extern uint8_t softclick; +extern uint8_t gDeltaU; +extern uint8_t gDeltaL; +#endif diff --git a/include/linux/curcial_oj.h b/include/linux/curcial_oj.h new file mode 100644 index 0000000000000..0a65cb7b6c501 --- /dev/null +++ b/include/linux/curcial_oj.h @@ -0,0 +1,43 @@ +#ifndef _CURCIAL_OJ_H +#define _CURCIAL_OJ_H +#include + +#define CURCIAL_OJ_NAME "curcial_oj" + +struct curcial_oj_platform_data { + struct input_dev *input_dev; + struct work_struct work; + bool click; + uint8_t key; + uint32_t last_key_time; + bool ap_code; + uint8_t degree; + uint8_t debugflag; + uint32_t last_click_time; + uint16_t interval; + uint8_t mdelay_time; + int8_t normal_th; + int8_t xy_ratio; + void (*oj_shutdown)(int); + int (*oj_poweron)(int); + void(*oj_adjust_xy)(uint8_t *, int16_t *, int16_t *); + int microp_version; + bool share_power; + bool swap; + int x; + int y; + uint8_t Xsteps[30]; + uint8_t Ysteps[30]; + uint16_t sht_tbl[10]; + uint8_t pxsum_tbl[10]; + int irq; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif + + unsigned irq_gpio; + +}; +void curcial_oj_send_key(unsigned int code, int value); + +#endif \ No newline at end of file diff --git a/include/linux/gpio_event.h b/include/linux/gpio_event.h index 360b4ddb46a6b..fc6aa099d8314 100644 --- a/include/linux/gpio_event.h +++ b/include/linux/gpio_event.h @@ -37,6 +37,9 @@ struct gpio_event_info { void **data, unsigned int dev, unsigned int type, unsigned int code, int value); /* out events */ bool no_suspend; +#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL + bool oj_btn; +#endif }; struct gpio_event_platform_data { From 11fec07baa1f7932dd29c6842f5f29953ff517e0 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sat, 7 May 2011 00:27:39 -0400 Subject: [PATCH 1711/2556] [msm] update Makefile via pershoot to fix n1 build error --- arch/arm/mach-msm/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 7b37214ca39df..fa74c63547a51 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -67,7 +67,7 @@ obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-rfkill.o htc_wifi_nvs.o htc_awb_ca obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-wifi.o board-mahimahi-audio.o obj-$(CONFIG_MACH_MAHIMAHI) += msm_vibrator.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-microp.o - +obj-$(CONFIG_MACH_MAHIMAHI) += htc_acoustic_qsd.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-panel.o fish_battery.o From 7d54d591db59b60c2a97fc4453d32df48499c791 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Tue, 10 May 2011 21:59:30 -0400 Subject: [PATCH 1712/2556] [ARM] msm: mahimahi: Add platform data for USB accessory function Port of commit from mikeandroid to the Incredible --- arch/arm/mach-msm/board-incrediblec.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index 160e37fc8185a..0e31cd5f54429 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -350,6 +351,11 @@ static char *usb_functions_rndis_adb[] = { "adb", }; +#ifdef CONFIG_USB_ANDROID_ACCESSORY +static char *usb_functions_accessory[] = { "accessory" }; +static char *usb_functions_accessory_adb[] = { "accessory", "adb" }; +#endif + #ifdef CONFIG_USB_ANDROID_DIAG static char *usb_functions_adb_diag[] = { "usb_mass_storage", @@ -370,6 +376,9 @@ static char *usb_functions_all[] = { #ifdef CONFIG_USB_ANDROID_DIAG "diag", #endif +#ifdef CONFIG_USB_ANDROID_ACCESSORY + "accessory", +#endif }; static struct android_usb_product usb_products[] = { @@ -393,6 +402,18 @@ static struct android_usb_product usb_products[] = { .num_functions = ARRAY_SIZE(usb_functions_rndis_adb), .functions = usb_functions_rndis_adb, }, +#ifdef CONFIG_USB_ANDROID_ACCESSORY + { + .product_id = USB_ACCESSORY_PRODUCT_ID, + .num_functions = ARRAY_SIZE(usb_functions_accessory), + .functions = usb_functions_accessory, + }, + { + .product_id = USB_ACCESSORY_ADB_PRODUCT_ID, + .num_functions = ARRAY_SIZE(usb_functions_accessory_adb), + .functions = usb_functions_accessory_adb, + }, +#endif #ifdef CONFIG_USB_ANDROID_DIAG { .product_id = 0x0c07, From e9f5c5438dd658946aa587dd3171e673f963b862 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sat, 14 May 2011 13:21:33 -0400 Subject: [PATCH 1713/2556] add support for the HTC Desire(s) via cm-kernel:android-msm-2.6.37 --- arch/arm/configs/cyanogen_bravo_defconfig | 2008 +++++++++++++++++ arch/arm/configs/cyanogen_bravoc_defconfig | 2008 +++++++++++++++++ arch/arm/mach-msm/Makefile | 2 + arch/arm/mach-msm/board-bravo-audio.c | 270 +++ arch/arm/mach-msm/board-bravo-keypad.c | 159 ++ arch/arm/mach-msm/board-bravo-microp.c | 1885 ++++++++++++++++ arch/arm/mach-msm/board-bravo-mmc.c | 442 ++++ arch/arm/mach-msm/board-bravo-panel.c | 1271 +++++++++++ arch/arm/mach-msm/board-bravo-rfkill.c | 122 + arch/arm/mach-msm/board-bravo-smb329.c | 177 ++ arch/arm/mach-msm/board-bravo-smb329.h | 32 + arch/arm/mach-msm/board-bravo-tpa2018d1.c | 368 +++ arch/arm/mach-msm/board-bravo-tpa2018d1.h | 35 + arch/arm/mach-msm/board-bravo-wifi.c | 146 ++ arch/arm/mach-msm/board-bravo.c | 1284 +++++++++++ arch/arm/mach-msm/board-bravo.h | 185 ++ .../include/mach/board-bravo-microp-common.h | 166 ++ drivers/input/misc/Kconfig | 6 + drivers/input/misc/Makefile | 2 + drivers/input/misc/capella_cm3602_htc.c | 352 +++ 20 files changed, 10920 insertions(+) create mode 100644 arch/arm/configs/cyanogen_bravo_defconfig create mode 100644 arch/arm/configs/cyanogen_bravoc_defconfig create mode 100644 arch/arm/mach-msm/board-bravo-audio.c create mode 100644 arch/arm/mach-msm/board-bravo-keypad.c create mode 100644 arch/arm/mach-msm/board-bravo-microp.c create mode 100644 arch/arm/mach-msm/board-bravo-mmc.c create mode 100644 arch/arm/mach-msm/board-bravo-panel.c create mode 100644 arch/arm/mach-msm/board-bravo-rfkill.c create mode 100755 arch/arm/mach-msm/board-bravo-smb329.c create mode 100644 arch/arm/mach-msm/board-bravo-smb329.h create mode 100644 arch/arm/mach-msm/board-bravo-tpa2018d1.c create mode 100644 arch/arm/mach-msm/board-bravo-tpa2018d1.h create mode 100644 arch/arm/mach-msm/board-bravo-wifi.c create mode 100644 arch/arm/mach-msm/board-bravo.c create mode 100644 arch/arm/mach-msm/board-bravo.h create mode 100644 arch/arm/mach-msm/include/mach/board-bravo-microp-common.h create mode 100644 drivers/input/misc/capella_cm3602_htc.c diff --git a/arch/arm/configs/cyanogen_bravo_defconfig b/arch/arm/configs/cyanogen_bravo_defconfig new file mode 100644 index 0000000000000..a9ae3e7bac7d1 --- /dev/null +++ b/arch/arm/configs/cyanogen_bravo_defconfig @@ -0,0 +1,2008 @@ +# +# Automatically generated make config: don't edit +# Linux/arm 2.6.37.6 Kernel Configuration +# Sat Apr 30 10:30:59 2011 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="-cyanogenmod" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +CONFIG_KERNEL_LZMA=y +# CONFIG_KERNEL_LZO is not set +# CONFIG_SWAP is not set +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +# CONFIG_HAVE_GENERIC_HARDIRQS is not set +# CONFIG_SPARSE_IRQ is not set + +# +# RCU Subsystem +# +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +CONFIG_TINY_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_TRACE is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_RCU_BOOST=y +CONFIG_RCU_BOOST_PRIO=1 +CONFIG_RCU_BOOST_DELAY=500 +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +# CONFIG_CGROUP_NS is not set +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +# CONFIG_CGROUP_MEM_RES_CTLR is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +# CONFIG_BLK_CGROUP is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_ASHMEM=y +CONFIG_AIO=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_IOSCHED_BFQ=y +# CONFIG_CGROUP_BFQIO is not set +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +CONFIG_DEFAULT_BFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="bfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +CONFIG_ARCH_MSM=y +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_S5PV310 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_TCC_926 is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_ARCH_MSM7X00A is not set +# CONFIG_ARCH_MSM7X30 is not set +CONFIG_ARCH_QSD8X50=y +# CONFIG_ARCH_MSM8X60 is not set +CONFIG_ARCH_MSM_SCORPION=y +CONFIG_HAS_MSM_DEBUG_UART_PHYS=y +CONFIG_MSM_MDP31=y +# CONFIG_PERFLOCK is not set + +# +# Qualcomm MSM Board Type +# +# CONFIG_MACH_SWORDFISH is not set +CONFIG_MACH_QSD8X50_SURF=y +# CONFIG_MACH_QSD8X50A_ST1_5 is not set +CONFIG_MSM_DEBUG_UART=1 +# CONFIG_MSM_DEBUG_UART_NONE is not set +CONFIG_MSM_DEBUG_UART1=y +# CONFIG_MSM_DEBUG_UART2 is not set +# CONFIG_MSM_DEBUG_UART3 is not set +CONFIG_MSM_PROC_COMM=y +# CONFIG_MACH_MAHIMAHI is not set +# CONFIG_MACH_BRAVO_NONE is not set +CONFIG_MACH_BRAVO=y +# CONFIG_MACH_BRAVOC is not set +# CONFIG_MACH_INCREDIBLE is not set +# CONFIG_MACH_INCREDIBLEC is not set +# CONFIG_MACH_SUPERSONIC is not set +# CONFIG_MACH_QSD8X50_FFA is not set +# CONFIG_JESUS_PHONE is not set +# CONFIG_HTC_HEADSET is not set +CONFIG_HTC_35MM_JACK=y +# CONFIG_HTC_BATTCHG is not set +CONFIG_HTC_PWRSPLY=y +# CONFIG_HTC_PWRSINK is not set +# CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set +# CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_MSM7X00A_USE_GP_TIMER=y +# CONFIG_MSM7X00A_USE_DG_TIMER is not set +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=1 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_FIQ_SUPPORT=y +# CONFIG_MSM_SERIAL_DEBUGGER is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_DAL=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y +CONFIG_MSM_CPU_FREQ_MAX=998400 +CONFIG_MSM_CPU_FREQ_MIN=245000 +# CONFIG_AXI_SCREEN_POLICY is not set +# CONFIG_MSM_HW3D is not set +CONFIG_HTC_ACOUSTIC_QSD=y +CONFIG_MSM_QDSP6=y +CONFIG_WIFI_CONTROL_FUNC=y +# CONFIG_WIFI_MEM_PREALLOC is not set +CONFIG_ARCH_MSM_FLASHLIGHT=y +# CONFIG_MICROP_COMMON is not set +# CONFIG_HTC_HEADSET_MGR is not set +# CONFIG_VIRTUAL_KPANIC_PARTITION is not set + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_VERIFY_PERMISSION_FAULT=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_CPU_HAS_PMU=y +CONFIG_ARM_ERRATA_430973=y +CONFIG_ARM_ERRATA_458693=y +CONFIG_ARM_ERRATA_460075=y +CONFIG_ARM_ERRATA_743622=y +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_VMALLOC_RESERVE=0x08000000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_NEED_PER_CPU_KM=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +# CONFIG_CPU_FREQ_DEBUG is not set +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND_NVS=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM_OPS=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +CONFIG_INET_ESP=y +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_LED=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +CONFIG_NETFILTER_XT_MATCH_OWNER=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +# CONFIG_NF_NAT_SNMP_BASIC is not set +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +CONFIG_NF_NAT_H323=y +CONFIG_NF_NAT_SIP=y +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_RAW is not set +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV6 is not set +# CONFIG_NF_CONNTRACK_IPV6 is not set +# CONFIG_IP6_NF_QUEUE is not set +# CONFIG_IP6_NF_IPTABLES is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +CONFIG_NET_SCH_INGRESS=y + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +# CONFIG_GACT_PROB is not set +CONFIG_NET_ACT_MIRRED=y +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_PRIV=y +# CONFIG_CFG80211 is not set +CONFIG_WIRELESS_EXT_SYSFS=y +# CONFIG_LIB80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# + +# +# Some wireless drivers require a rate control algorithm +# +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_MSM_NAND=y +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set +CONFIG_MISC_DEVICES=y +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_KERNEL_DEBUGGER_CORE=y +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +CONFIG_SENSORS_AKM8973=y +# CONFIG_SENSORS_AKM8976 is not set +# CONFIG_VP_A1026 is not set +# CONFIG_DS1682 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_WL127X_RFKILL is not set +# CONFIG_APANIC is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_IFB=y +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set +# CONFIG_MII is not set +# CONFIG_PHYLIB is not set +# CONFIG_NET_ETHERNET is not set +CONFIG_NETDEV_1000=y +# CONFIG_STMMAC_ETH is not set +CONFIG_NETDEV_10000=y +CONFIG_WLAN=y +CONFIG_BCM4329=m +CONFIG_BCM4329_FW_PATH="/vendor/firmware/fw_bcm4329.bin" +CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" +# CONFIG_HOSTAP is not set +# CONFIG_TIWLAN1251 is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set + +# +# CAIF transport drivers +# +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +# CONFIG_PPPOE is not set +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_GAN_ETH is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_ATMEL is not set +# CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELAN_I2C_8232 is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_QT602240 is not set +# CONFIG_TOUCHSCREEN_MSM is not set +CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y +CONFIG_TOUCHSCREEN_DUPLICATED_FILTER=y +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CAPELLA_CM3602 is not set +CONFIG_INPUT_CAPELLA_CM3602_HTC=y +CONFIG_LIGHTSENSOR_MICROP=y +CONFIG_INPUT_OPTICALJOYSTICK=y +CONFIG_OPTICALJOYSTICK_CRUCIAL=y +CONFIG_OPTICALJOYSTICK_CRUCIAL_uP=y +# CONFIG_OPTICALJOYSTICK_CRUCIAL_SPI is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_BCM_BT_LPM=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_MSM=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_SPI is not set + +# +# PPS support +# +# CONFIG_PPS is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_BASIC_MMIO is not set +# CONFIG_GPIO_IT8761E is not set +# CONFIG_GPIO_VX855 is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +CONFIG_W1=y + +# +# 1-wire Bus Masters +# +CONFIG_W1_MASTER_DS2482=y +# CONFIG_W1_MASTER_DS1WM is not set +# CONFIG_W1_MASTER_GPIO is not set + +# +# 1-wire Slaves +# +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_DS2431 is not set +# CONFIG_W1_SLAVE_DS2433 is not set +# CONFIG_W1_SLAVE_DS2760 is not set +# CONFIG_W1_SLAVE_BQ27000 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2782 is not set +CONFIG_BATTERY_DS2784=y +# CONFIG_BATTERY_BQ20Z75 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_MFD_SUPPORT=y +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_TPS65200 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC35892 is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_TPS6586X is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +CONFIG_REGULATOR_TPS65023=y +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_AD5398 is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +# CONFIG_IR_CORE is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +CONFIG_720P_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set + +# +# Camera Sensor Selection +# +# CONFIG_MT9T013 is not set +# CONFIG_MT9D112 is not set +# CONFIG_MT9P012 is not set +CONFIG_S5K3E2FX=y +# CONFIG_S5K6AAFX is not set +# CONFIG_OV8810 is not set +# CONFIG_OV9665 is not set +# CONFIG_S5K3H1GX is not set +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_LEGACY_MDP=y +CONFIG_FB_MSM_MDP_PPP=y +CONFIG_FB_MSM_LCDC=y +# CONFIG_FB_MSM_MDDI is not set +CONFIG_GPU_MSM_KGSL=y +CONFIG_MSM_KGSL_MMU=y +# CONFIG_MSM_HDMI is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set +# CONFIG_HID_PID is not set + +# +# Special HID drivers +# +CONFIG_HID_APPLE=y +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_WACOM is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_M66592 is not set +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_MSM_72K=y +# CONFIG_USB_MSM_72K_HTC is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_ANDROID=y +# CONFIG_USB_ANDROID_ACM is not set +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_DIAG=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +# CONFIG_USB_ANDROID_MTP is not set +CONFIG_USB_ANDROID_RNDIS=y +CONFIG_USB_ANDROID_RNDIS_WCEIS=y +CONFIG_USB_ANDROID_ACCESSORY=y +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y +# CONFIG_MMC_USE_ONLY_HOST_DEFINED_FREQUENCY is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_MSM=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +CONFIG_LEDS_CPLD=y +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +CONFIG_LEDS_TRIGGER_SLEEP=y + +# +# iptables trigger is under Netfilter config (LED target) +# +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_MSM7X00A=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_ECHO is not set +# CONFIG_BRCM80211 is not set +# CONFIG_COMEDI is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE=128 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE=16 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE=8 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_POHMELFS is not set +# CONFIG_IIO is not set +# CONFIG_ZRAM is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_FB_SM7XX is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_ST_BT is not set +CONFIG_MACH_NO_WESTBRIDGE=y +# CONFIG_ATH6K_LEGACY is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +# CONFIG_DNOTIFY is not set +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=y +# CONFIG_CUSE is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=y +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_WEAK_PW_HASH=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_ACL is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_BKL is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_ARM_UNWIND is not set +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/arch/arm/configs/cyanogen_bravoc_defconfig b/arch/arm/configs/cyanogen_bravoc_defconfig new file mode 100644 index 0000000000000..8922ea4d2ecd4 --- /dev/null +++ b/arch/arm/configs/cyanogen_bravoc_defconfig @@ -0,0 +1,2008 @@ +# +# Automatically generated make config: don't edit +# Linux/arm 2.6.37.6 Kernel Configuration +# Sat Apr 30 10:30:59 2011 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="-cyanogenmod" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +CONFIG_KERNEL_LZMA=y +# CONFIG_KERNEL_LZO is not set +# CONFIG_SWAP is not set +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +# CONFIG_HAVE_GENERIC_HARDIRQS is not set +# CONFIG_SPARSE_IRQ is not set + +# +# RCU Subsystem +# +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +CONFIG_TINY_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_TRACE is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_RCU_BOOST=y +CONFIG_RCU_BOOST_PRIO=1 +CONFIG_RCU_BOOST_DELAY=500 +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +# CONFIG_CGROUP_NS is not set +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +# CONFIG_CGROUP_MEM_RES_CTLR is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +# CONFIG_BLK_CGROUP is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_ASHMEM=y +CONFIG_AIO=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_IOSCHED_BFQ=y +# CONFIG_CGROUP_BFQIO is not set +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +CONFIG_DEFAULT_BFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="bfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +CONFIG_ARCH_MSM=y +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_S5PV310 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_TCC_926 is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_ARCH_MSM7X00A is not set +# CONFIG_ARCH_MSM7X30 is not set +CONFIG_ARCH_QSD8X50=y +# CONFIG_ARCH_MSM8X60 is not set +CONFIG_ARCH_MSM_SCORPION=y +CONFIG_HAS_MSM_DEBUG_UART_PHYS=y +CONFIG_MSM_MDP31=y +# CONFIG_PERFLOCK is not set + +# +# Qualcomm MSM Board Type +# +# CONFIG_MACH_SWORDFISH is not set +CONFIG_MACH_QSD8X50_SURF=y +# CONFIG_MACH_QSD8X50A_ST1_5 is not set +CONFIG_MSM_DEBUG_UART=1 +# CONFIG_MSM_DEBUG_UART_NONE is not set +CONFIG_MSM_DEBUG_UART1=y +# CONFIG_MSM_DEBUG_UART2 is not set +# CONFIG_MSM_DEBUG_UART3 is not set +CONFIG_MSM_PROC_COMM=y +# CONFIG_MACH_MAHIMAHI is not set +# CONFIG_MACH_BRAVO_NONE is not set +# CONFIG_MACH_BRAVO is not set +CONFIG_MACH_BRAVOC=y +# CONFIG_MACH_INCREDIBLE is not set +# CONFIG_MACH_INCREDIBLEC is not set +# CONFIG_MACH_SUPERSONIC is not set +# CONFIG_MACH_QSD8X50_FFA is not set +# CONFIG_JESUS_PHONE is not set +# CONFIG_HTC_HEADSET is not set +CONFIG_HTC_35MM_JACK=y +# CONFIG_HTC_BATTCHG is not set +CONFIG_HTC_PWRSPLY=y +# CONFIG_HTC_PWRSINK is not set +# CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set +# CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_MSM7X00A_USE_GP_TIMER=y +# CONFIG_MSM7X00A_USE_DG_TIMER is not set +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=1 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_FIQ_SUPPORT=y +# CONFIG_MSM_SERIAL_DEBUGGER is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_DAL=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y +CONFIG_MSM_CPU_FREQ_MAX=998400 +CONFIG_MSM_CPU_FREQ_MIN=245000 +# CONFIG_AXI_SCREEN_POLICY is not set +# CONFIG_MSM_HW3D is not set +CONFIG_HTC_ACOUSTIC_QSD=y +CONFIG_MSM_QDSP6=y +CONFIG_WIFI_CONTROL_FUNC=y +# CONFIG_WIFI_MEM_PREALLOC is not set +CONFIG_ARCH_MSM_FLASHLIGHT=y +# CONFIG_MICROP_COMMON is not set +# CONFIG_HTC_HEADSET_MGR is not set +# CONFIG_VIRTUAL_KPANIC_PARTITION is not set + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_VERIFY_PERMISSION_FAULT=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_CPU_HAS_PMU=y +CONFIG_ARM_ERRATA_430973=y +CONFIG_ARM_ERRATA_458693=y +CONFIG_ARM_ERRATA_460075=y +CONFIG_ARM_ERRATA_743622=y +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_VMALLOC_RESERVE=0x08000000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_NEED_PER_CPU_KM=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +# CONFIG_CPU_FREQ_DEBUG is not set +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND_NVS=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM_OPS=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +CONFIG_INET_ESP=y +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_LED=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +CONFIG_NETFILTER_XT_MATCH_OWNER=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +# CONFIG_NF_NAT_SNMP_BASIC is not set +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +CONFIG_NF_NAT_H323=y +CONFIG_NF_NAT_SIP=y +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_RAW is not set +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV6 is not set +# CONFIG_NF_CONNTRACK_IPV6 is not set +# CONFIG_IP6_NF_QUEUE is not set +# CONFIG_IP6_NF_IPTABLES is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +CONFIG_NET_SCH_INGRESS=y + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +# CONFIG_GACT_PROB is not set +CONFIG_NET_ACT_MIRRED=y +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_PRIV=y +# CONFIG_CFG80211 is not set +CONFIG_WIRELESS_EXT_SYSFS=y +# CONFIG_LIB80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# + +# +# Some wireless drivers require a rate control algorithm +# +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_MSM_NAND=y +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set +CONFIG_MISC_DEVICES=y +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_KERNEL_DEBUGGER_CORE=y +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +CONFIG_SENSORS_AKM8973=y +# CONFIG_SENSORS_AKM8976 is not set +# CONFIG_VP_A1026 is not set +# CONFIG_DS1682 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_WL127X_RFKILL is not set +# CONFIG_APANIC is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_IFB=y +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set +# CONFIG_MII is not set +# CONFIG_PHYLIB is not set +# CONFIG_NET_ETHERNET is not set +CONFIG_NETDEV_1000=y +# CONFIG_STMMAC_ETH is not set +CONFIG_NETDEV_10000=y +CONFIG_WLAN=y +CONFIG_BCM4329=m +CONFIG_BCM4329_FW_PATH="/vendor/firmware/fw_bcm4329.bin" +CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" +# CONFIG_HOSTAP is not set +# CONFIG_TIWLAN1251 is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set + +# +# CAIF transport drivers +# +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +# CONFIG_PPPOE is not set +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_GAN_ETH is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_ATMEL is not set +# CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELAN_I2C_8232 is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_QT602240 is not set +# CONFIG_TOUCHSCREEN_MSM is not set +CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y +CONFIG_TOUCHSCREEN_DUPLICATED_FILTER=y +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CAPELLA_CM3602 is not set +CONFIG_INPUT_CAPELLA_CM3602_HTC=y +CONFIG_LIGHTSENSOR_MICROP=y +CONFIG_INPUT_OPTICALJOYSTICK=y +CONFIG_OPTICALJOYSTICK_CRUCIAL=y +CONFIG_OPTICALJOYSTICK_CRUCIAL_uP=y +# CONFIG_OPTICALJOYSTICK_CRUCIAL_SPI is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_BCM_BT_LPM=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_MSM=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_SPI is not set + +# +# PPS support +# +# CONFIG_PPS is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_BASIC_MMIO is not set +# CONFIG_GPIO_IT8761E is not set +# CONFIG_GPIO_VX855 is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +CONFIG_W1=y + +# +# 1-wire Bus Masters +# +CONFIG_W1_MASTER_DS2482=y +# CONFIG_W1_MASTER_DS1WM is not set +# CONFIG_W1_MASTER_GPIO is not set + +# +# 1-wire Slaves +# +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_DS2431 is not set +# CONFIG_W1_SLAVE_DS2433 is not set +# CONFIG_W1_SLAVE_DS2760 is not set +# CONFIG_W1_SLAVE_BQ27000 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2782 is not set +CONFIG_BATTERY_DS2784=y +# CONFIG_BATTERY_BQ20Z75 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_MFD_SUPPORT=y +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_TPS65200 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC35892 is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_TPS6586X is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +CONFIG_REGULATOR_TPS65023=y +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_AD5398 is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +# CONFIG_IR_CORE is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +CONFIG_720P_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set + +# +# Camera Sensor Selection +# +# CONFIG_MT9T013 is not set +# CONFIG_MT9D112 is not set +# CONFIG_MT9P012 is not set +CONFIG_S5K3E2FX=y +# CONFIG_S5K6AAFX is not set +# CONFIG_OV8810 is not set +# CONFIG_OV9665 is not set +# CONFIG_S5K3H1GX is not set +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_LEGACY_MDP=y +CONFIG_FB_MSM_MDP_PPP=y +CONFIG_FB_MSM_LCDC=y +# CONFIG_FB_MSM_MDDI is not set +CONFIG_GPU_MSM_KGSL=y +CONFIG_MSM_KGSL_MMU=y +# CONFIG_MSM_HDMI is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set +# CONFIG_HID_PID is not set + +# +# Special HID drivers +# +CONFIG_HID_APPLE=y +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_WACOM is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_M66592 is not set +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_MSM_72K=y +# CONFIG_USB_MSM_72K_HTC is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_ANDROID=y +# CONFIG_USB_ANDROID_ACM is not set +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_DIAG=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +# CONFIG_USB_ANDROID_MTP is not set +CONFIG_USB_ANDROID_RNDIS=y +CONFIG_USB_ANDROID_RNDIS_WCEIS=y +CONFIG_USB_ANDROID_ACCESSORY=y +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y +# CONFIG_MMC_USE_ONLY_HOST_DEFINED_FREQUENCY is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_MSM=y +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +CONFIG_LEDS_CPLD=y +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +CONFIG_LEDS_TRIGGER_SLEEP=y + +# +# iptables trigger is under Netfilter config (LED target) +# +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_MSM7X00A=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_ECHO is not set +# CONFIG_BRCM80211 is not set +# CONFIG_COMEDI is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE=128 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE=16 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE=8 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_POHMELFS is not set +# CONFIG_IIO is not set +# CONFIG_ZRAM is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_FB_SM7XX is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_ST_BT is not set +CONFIG_MACH_NO_WESTBRIDGE=y +# CONFIG_ATH6K_LEGACY is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +# CONFIG_DNOTIFY is not set +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=y +# CONFIG_CUSE is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=y +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_WEAK_PW_HASH=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_ACL is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_BKL is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_ARM_UNWIND is not set +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index fa74c63547a51..35b45d361e3eb 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -48,6 +48,8 @@ obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o obj-$(CONFIG_MSM_REMOTE_SPINLOCK) += remote_spinlock.o obj-$(CONFIG_MSM_SSBI) += ssbi.o +obj-$(CONFIG_HTC_ACOUSTIC) += htc_acoustic.o +obj-$(CONFIG_HTC_ACOUSTIC_QSD) += htc_acoustic_qsd.o obj-$(CONFIG_MACH_TROUT) += board-trout-rfkill.o obj-$(CONFIG_MACH_TROUT) += board-trout-wifi.o diff --git a/arch/arm/mach-msm/board-bravo-audio.c b/arch/arm/mach-msm/board-bravo-audio.c new file mode 100644 index 0000000000000..716c38a37f626 --- /dev/null +++ b/arch/arm/mach-msm/board-bravo-audio.c @@ -0,0 +1,270 @@ +/* arch/arm/mach-msm/board-bravo-audio.c + * + * Copyright (C) 2009 HTC Corporation + * Copyright (C) 2009 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 "board-bravo.h" +#include "proc_comm.h" +#include "pmic.h" +#include "board-bravo-tpa2018d1.h" + +#if 0 +#define D(fmt, args...) printk(KERN_INFO "Audio: "fmt, ##args) +#else +#define D(fmt, args...) do {} while (0) +#endif + +static struct mutex mic_lock; +static struct mutex bt_sco_lock; + +static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { + [Q6_HW_HANDSET] = { + .min_gain = -1600, + .max_gain = 400, + }, + [Q6_HW_HEADSET] = { + .min_gain = -1600, + .max_gain = 400, + }, + [Q6_HW_SPEAKER] = { + .min_gain = -1100, + .max_gain = 400, + }, + [Q6_HW_TTY] = { + .min_gain = -1600, + .max_gain = 400, + }, + [Q6_HW_BT_SCO] = { + .min_gain = -1600, + .max_gain = 400, + }, + [Q6_HW_BT_A2DP] = { + .min_gain = -1600, + .max_gain = 400, + }, +}; + +void bravo_headset_enable(int en) +{ + D("%s %d\n", __func__, en); + /* enable audio amp */ + if (en) mdelay(15); + gpio_set_value(BRAVO_AUD_JACKHP_EN, !!en); +} + +void bravo_speaker_enable(int en) +{ + struct spkr_config_mode scm; + memset(&scm, 0, sizeof(scm)); + + D("%s %d\n", __func__, en); + if (en) { + scm.is_right_chan_en = 0; + scm.is_left_chan_en = 1; + scm.is_stereo_en = 0; + scm.is_hpf_en = 1; + pmic_spkr_en_mute(LEFT_SPKR, 0); + pmic_spkr_en_mute(RIGHT_SPKR, 0); + pmic_set_spkr_configuration(&scm); + pmic_spkr_en(LEFT_SPKR, 1); + pmic_spkr_en(RIGHT_SPKR, 0); + + /* unmute */ + pmic_spkr_en_mute(LEFT_SPKR, 1); + } else { + pmic_spkr_en_mute(LEFT_SPKR, 0); + + pmic_spkr_en(LEFT_SPKR, 0); + pmic_spkr_en(RIGHT_SPKR, 0); + + pmic_set_spkr_configuration(&scm); + } + if (is_cdma_version(system_rev)) + tpa2018d1_set_speaker_amp(en); +} + +void bravo_receiver_enable(int en) +{ + if (is_cdma_version(system_rev)) { + struct spkr_config_mode scm; + memset(&scm, 0, sizeof(scm)); + + D("%s %d\n", __func__, en); + if (en) { + scm.is_right_chan_en = 1; + scm.is_left_chan_en = 0; + scm.is_stereo_en = 0; + scm.is_hpf_en = 1; + pmic_spkr_en_mute(RIGHT_SPKR, 0); + pmic_set_spkr_configuration(&scm); + pmic_spkr_en(RIGHT_SPKR, 1); + + /* unmute */ + pmic_spkr_en_mute(RIGHT_SPKR, 1); + } else { + pmic_spkr_en_mute(RIGHT_SPKR, 0); + + pmic_spkr_en(RIGHT_SPKR, 0); + + pmic_set_spkr_configuration(&scm); + } + } +} + +static uint32_t bt_sco_enable[] = { + PCOM_GPIO_CFG(BRAVO_BT_PCM_OUT, 1, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(BRAVO_BT_PCM_IN, 1, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(BRAVO_BT_PCM_SYNC, 2, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(BRAVO_BT_PCM_CLK, 2, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), +}; + +static uint32_t bt_sco_disable[] = { + PCOM_GPIO_CFG(BRAVO_BT_PCM_OUT, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(BRAVO_BT_PCM_IN, 0, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(BRAVO_BT_PCM_SYNC, 0, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(BRAVO_BT_PCM_CLK, 0, GPIO_INPUT, + GPIO_NO_PULL, GPIO_2MA), +}; + +void bravo_bt_sco_enable(int en) +{ + static int bt_sco_refcount; + D("%s %d\n", __func__, en); + + mutex_lock(&bt_sco_lock); + if (en) { + if (++bt_sco_refcount == 1) + config_gpio_table(bt_sco_enable, + ARRAY_SIZE(bt_sco_enable)); + } else { + if (--bt_sco_refcount == 0) { + config_gpio_table(bt_sco_disable, + ARRAY_SIZE(bt_sco_disable)); + gpio_set_value(BRAVO_BT_PCM_OUT, 0); + } + } + mutex_unlock(&bt_sco_lock); +} + +void bravo_mic_enable(int en) +{ + static int old_state = 0, new_state = 0; + + D("%s %d\n", __func__, en); + + mutex_lock(&mic_lock); + if (!!en) + new_state++; + else + new_state--; + + if (new_state == 1 && old_state == 0) { + gpio_set_value(BRAVO_AUD_2V5_EN, 1); + mdelay(60); + } else if (new_state == 0 && old_state == 1) + gpio_set_value(BRAVO_AUD_2V5_EN, 0); + else + D("%s: do nothing %d %d\n", __func__, old_state, new_state); + + old_state = new_state; + mutex_unlock(&mic_lock); +} + +void bravo_analog_init(void) +{ + D("%s\n", __func__); + /* stereo pmic init */ + pmic_spkr_set_gain(LEFT_SPKR, SPKR_GAIN_PLUS12DB); + pmic_spkr_set_gain(RIGHT_SPKR, SPKR_GAIN_PLUS12DB); + pmic_spkr_en_right_chan(OFF_CMD); + pmic_spkr_en_left_chan(OFF_CMD); + pmic_spkr_add_right_left_chan(OFF_CMD); + pmic_spkr_en_stereo(OFF_CMD); + pmic_spkr_select_usb_with_hpf_20hz(OFF_CMD); + pmic_spkr_bypass_mux(OFF_CMD); + pmic_spkr_en_hpf(ON_CMD); + pmic_spkr_en_sink_curr_from_ref_volt_cir(OFF_CMD); + pmic_spkr_set_mux_hpf_corner_freq(SPKR_FREQ_0_73KHZ); + pmic_mic_set_volt(MIC_VOLT_1_80V); + + gpio_request(BRAVO_AUD_JACKHP_EN, "aud_jackhp_en"); + gpio_request(BRAVO_BT_PCM_OUT, "bt_pcm_out"); + + gpio_direction_output(BRAVO_AUD_JACKHP_EN, 0); + + mutex_lock(&bt_sco_lock); + config_gpio_table(bt_sco_disable, + ARRAY_SIZE(bt_sco_disable)); + gpio_direction_output(BRAVO_BT_PCM_OUT, 0); + mutex_unlock(&bt_sco_lock); +} + +int bravo_get_rx_vol(uint8_t hw, int level) +{ + int vol; + struct q6_hw_info *info; + + if (level > 100) + level = 100; + else if (level < 0) + level = 0; + + if (is_cdma_version(system_rev) && hw == Q6_HW_HANDSET) { + int handset_volume[6] = { -1600, -1300, -1000, -600, -300, 0 }; + vol = handset_volume[5 * level / 100]; + } else { + info = &q6_audio_hw[hw]; + vol = info->min_gain + ((info->max_gain - info->min_gain) * level) / 100; + } + + D("%s %d\n", __func__, vol); + return vol; +} + +static struct qsd_acoustic_ops acoustic = { + .enable_mic_bias = bravo_mic_enable, +}; + +static struct q6audio_analog_ops ops = { + .init = bravo_analog_init, + .speaker_enable = bravo_speaker_enable, + .headset_enable = bravo_headset_enable, + .receiver_enable = bravo_receiver_enable, + .bt_sco_enable = bravo_bt_sco_enable, + .int_mic_enable = bravo_mic_enable, + .ext_mic_enable = bravo_mic_enable, + .get_rx_vol = bravo_get_rx_vol, +}; + +void __init bravo_audio_init(void) +{ + mutex_init(&mic_lock); + mutex_init(&bt_sco_lock); + q6audio_register_analog_ops(&ops); + acoustic_register_ops(&acoustic); + if (is_cdma_version(system_rev)) + q6audio_set_acdb_file("default_mos.acdb"); +} diff --git a/arch/arm/mach-msm/board-bravo-keypad.c b/arch/arm/mach-msm/board-bravo-keypad.c new file mode 100644 index 0000000000000..8208f20cdc8fc --- /dev/null +++ b/arch/arm/mach-msm/board-bravo-keypad.c @@ -0,0 +1,159 @@ +/* arch/arm/mach-msm/board-bravo-keypad.c + * + * Copyright (C) 2009 Google, Inc + * Copyright (C) 2009 HTC Corporation. + * Copyright (C) 2010 Giulio Cervera + * Copyright (C) 2010 Diogo Ferreira + * + * Author: Dima Zavin + * + * 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 "board-bravo.h" + +static unsigned int bravo_col_gpios[] = { + BRAVO_GPIO_KP_MKOUT0, + BRAVO_GPIO_KP_MKOUT1, + BRAVO_GPIO_KP_MKOUT2, +}; + +static unsigned int bravo_row_gpios[] = { + BRAVO_GPIO_KP_MPIN0, + BRAVO_GPIO_KP_MPIN1, + BRAVO_GPIO_KP_MPIN2, +}; + +#define KEYMAP_INDEX(col, row) ((col)*ARRAY_SIZE(bravo_row_gpios) + (row)) +#define KEYMAP_SIZE (ARRAY_SIZE(bravo_col_gpios) * \ + ARRAY_SIZE(bravo_row_gpios)) + +/* keypad */ +static const unsigned short bravo_keymap[KEYMAP_SIZE] = { + [KEYMAP_INDEX(0, 0)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(0, 1)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(1, 1)] = BTN_MOUSE, /* OJ Action key */ + [KEYMAP_INDEX(1, 0)] = KEY_MENU, + [KEYMAP_INDEX(1, 2)] = KEY_SEARCH, + [KEYMAP_INDEX(2, 0)] = KEY_HOME, + [KEYMAP_INDEX(2, 2)] = KEY_BACK, +}; + +static struct gpio_event_matrix_info bravo_keypad_matrix_info = { + .info.func = gpio_event_matrix_func, + .keymap = bravo_keymap, + .output_gpios = bravo_col_gpios, + .input_gpios = bravo_row_gpios, + .noutputs = ARRAY_SIZE(bravo_col_gpios), + .ninputs = ARRAY_SIZE(bravo_row_gpios), + .settle_time.tv.nsec = 40 * NSEC_PER_USEC, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .debounce_delay.tv.nsec = 5 * NSEC_PER_MSEC, + .flags = (GPIOKPF_LEVEL_TRIGGERED_IRQ | + GPIOKPF_REMOVE_PHANTOM_KEYS | + GPIOKPF_PRINT_UNMAPPED_KEYS), +#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL + .info.oj_btn = true, +#endif +}; + +static struct gpio_event_direct_entry bravo_keypad_key_map[] = { + { + .gpio = BRAVO_GPIO_POWER_KEY, + .code = KEY_POWER, + }, +}; + +static struct gpio_event_input_info bravo_keypad_key_info = { + .info.func = gpio_event_input_func, + .info.no_suspend = true, + .flags = GPIOEDF_PRINT_KEYS, + .type = EV_KEY, + .debounce_time.tv.nsec = 5 * NSEC_PER_MSEC, + .keymap = bravo_keypad_key_map, + .keymap_size = ARRAY_SIZE(bravo_keypad_key_map) +}; + +static struct gpio_event_info *bravo_input_info[] = { + &bravo_keypad_matrix_info.info, + &bravo_keypad_key_info.info, +}; + +static struct gpio_event_platform_data bravo_input_data = { + .names = { +#ifdef CONFIG_MACH_BRAVO + "bravo-keypad", +#else + "bravoc-keypad", +#endif + NULL, + }, + .info = bravo_input_info, + .info_count = ARRAY_SIZE(bravo_input_info), +}; + +static struct platform_device bravo_input_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = 0, + .dev = { + .platform_data = &bravo_input_data, + }, +}; + +static int bravo_reset_keys_up[] = { + KEY_VOLUMEUP, + 0, +}; + +static struct keyreset_platform_data bravo_reset_keys_pdata = { + .keys_up = bravo_reset_keys_up, + .keys_down = { + KEY_POWER, + KEY_VOLUMEDOWN, + BTN_MOUSE, + 0 + }, +}; + +struct platform_device bravo_reset_keys_device = { + .name = KEYRESET_NAME, + .dev = { + .platform_data = &bravo_reset_keys_pdata, + }, +}; + +static int __init bravo_init_keypad(void) +{ + int ret; + + if (!machine_is_bravo() && !machine_is_bravoc()) + return 0; + + ret = platform_device_register(&bravo_reset_keys_device); + if (ret != 0) + return ret; + + ret = platform_device_register(&bravo_input_device); + if (ret != 0) + return ret; + + return 0; +} + +device_initcall(bravo_init_keypad); diff --git a/arch/arm/mach-msm/board-bravo-microp.c b/arch/arm/mach-msm/board-bravo-microp.c new file mode 100644 index 0000000000000..9fc4fbf44c65b --- /dev/null +++ b/arch/arm/mach-msm/board-bravo-microp.c @@ -0,0 +1,1885 @@ +/* board-bravo-microp.c + * + * Copyright (C) 2009 Google. + * Copyright (C) 2009 HTC Corporation. + * Copyright (C) 2010 Giulio Cervera + * Copyright (C) 2010 Diogo Ferreira + * + * The Microp on bravo is an i2c device that supports + * the following functions + * - LEDs (Green, Amber, Blue, Button-backlight) + * - Lightsensor + * - Headset & Remotekeys + * - G-sensor + * - Interrupts + * + * 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 +#include +//#include +#include +#include +#include +#include +#include +#include + +#include +#include "board-bravo.h" + +#define READ_GPI_STATE_HPIN (1<<2) +#define READ_GPI_STATE_SDCARD (1<<0) + +/*#define DEBUG_BMA150 */ +#ifdef DEBUG_BMA150 +/* Debug logging of accelleration data */ +#define GSENSOR_LOG_MAX 2048 /* needs to be power of 2 */ +#define GSENSOR_LOG_MASK (GSENSOR_LOG_MAX - 1) + +struct gsensor_log { + ktime_t timestamp; + short x; + short y; + short z; +}; + +static DEFINE_MUTEX(gsensor_log_lock); +static struct gsensor_log gsensor_log[GSENSOR_LOG_MAX]; +static unsigned gsensor_log_head; +static unsigned gsensor_log_tail; + +void gsensor_log_status(ktime_t time, short x, short y, short z) +{ + unsigned n; + mutex_lock(&gsensor_log_lock); + n = gsensor_log_head; + gsensor_log[n].timestamp = time; + gsensor_log[n].x = x; + gsensor_log[n].y = y; + gsensor_log[n].z = z; + n = (n + 1) & GSENSOR_LOG_MASK; + if (n == gsensor_log_tail) + gsensor_log_tail = (gsensor_log_tail + 1) & GSENSOR_LOG_MASK; + gsensor_log_head = n; + mutex_unlock(&gsensor_log_lock); +} + +static int gsensor_log_print(struct seq_file *sf, void *private) +{ + unsigned n; + + mutex_lock(&gsensor_log_lock); + seq_printf(sf, "timestamp X Y Z\n"); + for (n = gsensor_log_tail; + n != gsensor_log_head; + n = (n + 1) & GSENSOR_LOG_MASK) { + seq_printf(sf, "%10d.%010d %6d %6d %6d\n", + gsensor_log[n].timestamp.tv.sec, + gsensor_log[n].timestamp.tv.nsec, + gsensor_log[n].x, gsensor_log[n].y, + gsensor_log[n].z); + } + mutex_unlock(&gsensor_log_lock); + return 0; +} + +static int gsensor_log_open(struct inode *inode, struct file *file) +{ + return single_open(file, gsensor_log_print, NULL); +} + +static struct file_operations gsensor_log_fops = { + .open = gsensor_log_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif /* def DEBUG_BMA150 */ + +static struct mutex gsensor_RW_mutex; +static struct mutex gsensor_set_mode_mutex; + +static int microp_headset_has_mic(void); +static int microp_enable_headset_plug_event(void); +static int microp_enable_key_event(void); +static int microp_disable_key_event(void); + +static struct h35mm_platform_data bravo_h35mm_data = { + .plug_event_enable = microp_enable_headset_plug_event, + .headset_has_mic = microp_headset_has_mic, + .key_event_enable = microp_enable_key_event, + .key_event_disable = microp_disable_key_event, +}; + +static struct platform_device bravo_h35mm = { + .name = "htc_headset", + .id = -1, + .dev = { + .platform_data = &bravo_h35mm_data, + }, +}; + +enum led_type { + GREEN_LED, + AMBER_LED, + BLUE_LED, + BUTTONS_LED, + NUM_LEDS, +}; + +static uint16_t remote_key_adc_table[6] = { + 0, 33, 43, 110, 129, 220 +}; + +static struct wake_lock microp_i2c_wakelock; + +static struct i2c_client *private_microp_client; + +struct microp_int_pin { + uint16_t int_gsensor; + uint16_t int_lsensor; + uint16_t int_reset; + uint16_t int_simcard; + uint16_t int_hpin; + uint16_t int_remotekey; +}; + +struct microp_led_data { + int type; + struct led_classdev ldev; + struct mutex led_data_mutex; + struct work_struct brightness_work; + spinlock_t brightness_lock; + enum led_brightness brightness; + uint8_t mode; + uint8_t blink; +}; + +struct microp_i2c_work { + struct work_struct work; + struct i2c_client *client; + int (*intr_debounce)(uint8_t *pin_status); + void (*intr_function)(uint8_t *pin_status); +}; + +struct microp_i2c_client_data { + struct microp_led_data leds[NUM_LEDS]; + uint16_t version; + struct microp_i2c_work work; + struct delayed_work hpin_debounce_work; + struct delayed_work ls_read_work; + struct early_suspend early_suspend; + uint8_t enable_early_suspend; + uint8_t enable_reset_button; + int microp_is_suspend; + int auto_backlight_enabled; + uint8_t button_led_value; + int headset_is_in; + int is_hpin_pin_stable; + uint32_t spi_devices_vote; + uint32_t spi_devices; + struct mutex microp_i2c_rw_mutex; + struct mutex microp_adc_mutex; + struct hrtimer gen_irq_timer; + uint16_t intr_status; +}; + +static char *hex2string(uint8_t *data, int len) +{ + static char buf[101]; + int i; + + i = (sizeof(buf) - 1) / 4; + if (len > i) + len = i; + + for (i = 0; i < len; i++) + sprintf(buf + i * 4, "[%02X]", data[i]); + + return buf; +} + +#define I2C_READ_RETRY_TIMES 10 +#define I2C_WRITE_RETRY_TIMES 10 +#define MICROP_I2C_WRITE_BLOCK_SIZE 80 + +static int i2c_read_block(struct i2c_client *client, uint8_t addr, + uint8_t *data, int length) +{ + int retry; + struct microp_i2c_client_data *cdata; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + } + }; + + cdata = i2c_get_clientdata(client); + mutex_lock(&cdata->microp_i2c_rw_mutex); + mdelay(1); + for (retry = 0; retry <= I2C_READ_RETRY_TIMES; retry++) { + if (i2c_transfer(client->adapter, msgs, 2) == 2) + break; + msleep(10); + } + mutex_unlock(&cdata->microp_i2c_rw_mutex); + dev_dbg(&client->dev, "R [%02X] = %s\n", + addr, hex2string(data, length)); + + if (retry > I2C_READ_RETRY_TIMES) { + dev_err(&client->dev, "i2c_read_block retry over %d\n", + I2C_READ_RETRY_TIMES); + return -EIO; + } + + return 0; +} + +static int i2c_write_block(struct i2c_client *client, uint8_t addr, + uint8_t *data, int length) +{ + int retry; + uint8_t buf[MICROP_I2C_WRITE_BLOCK_SIZE]; + int i; + struct microp_i2c_client_data *cdata; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length + 1, + .buf = buf, + } + }; + + dev_dbg(&client->dev, "W [%02X] = %s\n", + addr, hex2string(data, length)); + + cdata = i2c_get_clientdata(client); + if (length + 1 > MICROP_I2C_WRITE_BLOCK_SIZE) { + dev_err(&client->dev, "i2c_write_block length too long\n"); + return -E2BIG; + } + + buf[0] = addr; + for (i = 0; i < length; i++) + buf[i+1] = data[i]; + + mutex_lock(&cdata->microp_i2c_rw_mutex); + mdelay(1); + for (retry = 0; retry <= I2C_WRITE_RETRY_TIMES; retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) + break; + msleep(10); + } + if (retry > I2C_WRITE_RETRY_TIMES) { + dev_err(&client->dev, "i2c_write_block retry over %d\n", + I2C_WRITE_RETRY_TIMES); + mutex_unlock(&cdata->microp_i2c_rw_mutex); + return -EIO; + } + mutex_unlock(&cdata->microp_i2c_rw_mutex); + + return 0; +} + +struct i2c_client *get_microp_client(void) +{ + return private_microp_client; +} + +int microp_i2c_read(uint8_t addr, uint8_t *data, int length) +{ + struct i2c_client *client = private_microp_client; + + if (!client) { + printk(KERN_ERR "%s: dataset: client is empty\n", __func__); + return -EIO; + } + + if (i2c_read_block(client, addr, data, length) < 0) { + dev_err(&client->dev, "%s: write microp i2c fail\n", __func__); + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL(microp_i2c_read); + +int microp_i2c_write(uint8_t addr, uint8_t *data, int length) +{ + struct i2c_client *client = private_microp_client; + + if (!client) { + printk(KERN_ERR "%s: dataset: client is empty\n", __func__); + return -EIO; + } + + if (i2c_write_block(client, addr, data, length) < 0) { + dev_err(&client->dev, "%s: write microp i2c fail\n", __func__); + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL(microp_i2c_write); + +int microp_read_adc(uint8_t channel, uint16_t *value) +{ + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + int ret; + uint8_t cmd[2], data[2]; + + client = private_microp_client; + cdata = i2c_get_clientdata(client); + cmd[0] = 0; + cmd[1] = channel; + mutex_lock(&cdata->microp_adc_mutex); + ret = i2c_write_block(client, MICROP_I2C_WCMD_READ_ADC_VALUE_REQ, + cmd, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: request adc fail\n", __func__); + mutex_unlock(&cdata->microp_adc_mutex); + return -EIO; + } + + ret = i2c_read_block(client, MICROP_I2C_RCMD_ADC_VALUE, data, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: read adc fail\n", __func__); + mutex_unlock(&cdata->microp_adc_mutex); + return -EIO; + } + + *value = data[0] << 8 | data[1]; + mutex_unlock(&cdata->microp_adc_mutex); + return 0; +} +EXPORT_SYMBOL(microp_read_adc); + +static int microp_read_gpi_status(struct i2c_client *client, uint16_t *status) +{ + uint8_t data[3]; + int ret; + + ret = i2c_read_block(client, MICROP_I2C_RCMD_GPI_STATUS, data, 3); + if (ret < 0) { + dev_err(&client->dev, "%s: read failed\n", __func__); + return -EIO; + } + *status = (data[0] << 16 | data[1] << 8 | data[2]); + return 0; +} + +static int microp_interrupt_enable(struct i2c_client *client, + uint16_t interrupt_mask) +{ + uint8_t data[2]; + int ret = -1; + + data[0] = interrupt_mask >> 8; + data[1] = interrupt_mask & 0xFF; + ret = i2c_write_block(client, MICROP_I2C_WCMD_GPI_INT_CTL_EN, data, 2); + + if (ret < 0) + dev_err(&client->dev, "%s: enable 0x%x interrupt failed\n", + __func__, interrupt_mask); + return ret; +} + +static int microp_interrupt_disable(struct i2c_client *client, + uint16_t interrupt_mask) +{ + uint8_t data[2]; + int ret = -1; + + data[0] = interrupt_mask >> 8; + data[1] = interrupt_mask & 0xFF; + ret = i2c_write_block(client, MICROP_I2C_WCMD_GPI_INT_CTL_DIS, data, 2); + + if (ret < 0) + dev_err(&client->dev, "%s: disable 0x%x interrupt failed\n", + __func__, interrupt_mask); + return ret; +} + +int microp_write_interrupt(struct i2c_client *client, + uint16_t interrupt, uint8_t enable) +{ + int ret; + + if (enable) { + ret = microp_interrupt_enable(client, interrupt); + printk("%s: microp_interrupt_enable called\n", __func__ ); + } else { + ret = microp_interrupt_disable(client, interrupt); + printk("%s: microp_interrupt_disable called\n", __func__ ); + } + + return ret; +} +EXPORT_SYMBOL(microp_write_interrupt); + +/* + * SD slot card-detect support + */ +static unsigned int sdslot_cd = 0; +static void (*sdslot_status_cb)(int card_present, void *dev_id); +static void *sdslot_mmc_dev; + +int bravo_microp_sdslot_status_register( + void (*cb)(int card_present, void *dev_id), + void *dev_id) +{ + if (sdslot_status_cb) + return -EBUSY; + sdslot_status_cb = cb; + sdslot_mmc_dev = dev_id; + return 0; +} + +unsigned int bravo_microp_sdslot_status(struct device *dev) +{ + return sdslot_cd; +} + +static void bravo_microp_sdslot_update_status(int status) +{ + sdslot_cd = !(status & READ_GPI_STATE_SDCARD); + if (sdslot_status_cb) + sdslot_status_cb(sdslot_cd, sdslot_mmc_dev); +} + +/* + *Headset Support +*/ +static void hpin_debounce_do_work(struct work_struct *work) +{ + uint16_t gpi_status = 0; + struct microp_i2c_client_data *cdata; + int insert = 0; + struct i2c_client *client; + + client = private_microp_client; + cdata = i2c_get_clientdata(client); + + microp_read_gpi_status(client, &gpi_status); + insert = (gpi_status & READ_GPI_STATE_HPIN) ? 0 : 1; + if (insert != cdata->headset_is_in) { + cdata->headset_is_in = insert; + pr_debug("headset %s\n", insert ? "inserted" : "removed"); + htc_35mm_jack_plug_event(cdata->headset_is_in, + &cdata->is_hpin_pin_stable); + } +} + +static int microp_enable_headset_plug_event(void) +{ + int ret; + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + uint16_t stat; + + client = private_microp_client; + cdata = i2c_get_clientdata(client); + + /* enable microp interrupt to detect changes */ + ret = microp_interrupt_enable(client, IRQ_HEADSETIN); + if (ret < 0) { + dev_err(&client->dev, "%s: failed to enable irqs\n", + __func__); + return 0; + } + /* see if headset state has changed */ + microp_read_gpi_status(client, &stat); + stat = !(stat & READ_GPI_STATE_HPIN); + if(cdata->headset_is_in != stat) { + cdata->headset_is_in = stat; + pr_debug("Headset state changed\n"); + htc_35mm_jack_plug_event(stat, &cdata->is_hpin_pin_stable); + } + + return 1; +} + +static int microp_headset_detect_mic(void) +{ + uint16_t data; + + microp_read_adc(MICROP_REMOTE_KEY_ADC_CHAN, &data); + if (data >= 200) + return 1; + else + return 0; +} + +static int microp_headset_has_mic(void) +{ + int mic1 = -1; + int mic2 = -1; + int count = 0; + + mic2 = microp_headset_detect_mic(); + + /* debounce the detection wait until 2 consecutive read are equal */ + while ((mic1 != mic2) && (count < 10)) { + mic1 = mic2; + msleep(600); + mic2 = microp_headset_detect_mic(); + count++; + } + + pr_info("%s: microphone (%d) %s\n", __func__, count, + mic1 ? "present" : "not present"); + + return mic1; +} + +static int microp_enable_key_event(void) +{ + int ret; + struct i2c_client *client; + + client = private_microp_client; + + if (!is_cdma_version(system_rev)) + gpio_set_value(BRAVO_GPIO_35MM_KEY_INT_SHUTDOWN, 1); + + /* turn on key interrupt */ + /* enable microp interrupt to detect changes */ + ret = microp_interrupt_enable(client, IRQ_REMOTEKEY); + if (ret < 0) { + dev_err(&client->dev, "%s: failed to enable irqs\n", + __func__); + return ret; + } + return 0; +} + +static int microp_disable_key_event(void) +{ + int ret; + struct i2c_client *client; + + client = private_microp_client; + + /* shutdown key interrupt */ + if (!is_cdma_version(system_rev)) + gpio_set_value(BRAVO_GPIO_35MM_KEY_INT_SHUTDOWN, 0); + + /* disable microp interrupt to detect changes */ + ret = microp_interrupt_disable(client, IRQ_REMOTEKEY); + if (ret < 0) { + dev_err(&client->dev, "%s: failed to disable irqs\n", + __func__); + return ret; + } + return 0; +} + +static int get_remote_keycode(int *keycode) +{ + struct i2c_client *client = private_microp_client; + int ret; + uint8_t data[2]; + + ret = i2c_read_block(client, MICROP_I2C_RCMD_REMOTE_KEYCODE, data, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: read remote keycode fail\n", + __func__); + return -EIO; + } + pr_debug("%s: key = 0x%x\n", __func__, data[1]); + if (!data[1]) { + *keycode = 0; + return 1; /* no keycode */ + } else { + *keycode = data[1]; + } + return 0; +} + +static ssize_t microp_i2c_remotekey_adc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client; + uint16_t value; + int i, button = 0; + int ret; + + client = to_i2c_client(dev); + + microp_read_adc(MICROP_REMOTE_KEY_ADC_CHAN, &value); + + for (i = 0; i < 3; i++) { + if ((value >= remote_key_adc_table[2 * i]) && + (value <= remote_key_adc_table[2 * i + 1])) { + button = i + 1; + } + + } + + ret = sprintf(buf, "Remote Key[0x%03X] => button %d\n", + value, button); + + return ret; +} + +static DEVICE_ATTR(key_adc, 0644, microp_i2c_remotekey_adc_show, NULL); + +/* + * LED support +*/ +static int microp_i2c_write_led_mode(struct i2c_client *client, + struct led_classdev *led_cdev, + uint8_t mode, uint16_t off_timer) +{ + struct microp_i2c_client_data *cdata; + struct microp_led_data *ldata; + uint8_t data[7]; + int ret; + + cdata = i2c_get_clientdata(client); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + + if (ldata->type == GREEN_LED) { + data[0] = 0x01; + data[1] = mode; + data[2] = off_timer >> 8; + data[3] = off_timer & 0xFF; + data[4] = 0x00; + data[5] = 0x00; + data[6] = 0x00; + } else if (ldata->type == AMBER_LED) { + data[0] = 0x02; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + data[4] = mode; + data[5] = off_timer >> 8; + data[6] = off_timer & 0xFF; + } else if (ldata->type == BLUE_LED) { + data[0] = 0x04; + data[1] = mode; + data[2] = off_timer >> 8; + data[3] = off_timer & 0xFF; + data[4] = 0x00; + data[5] = 0x00; + data[6] = 0x00; + } + + ret = i2c_write_block(client, MICROP_I2C_WCMD_LED_MODE, data, 7); + if (ret == 0) { + mutex_lock(&ldata->led_data_mutex); + if (mode > 1) + ldata->blink = mode; + else + ldata->mode = mode; + mutex_unlock(&ldata->led_data_mutex); + } + return ret; +} + +static ssize_t microp_i2c_led_blink_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + int ret; + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + + mutex_lock(&ldata->led_data_mutex); + ret = sprintf(buf, "%d\n", ldata->blink ? ldata->blink - 1 : 0); + mutex_unlock(&ldata->led_data_mutex); + + return ret; +} + +static ssize_t microp_i2c_led_blink_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + struct i2c_client *client; + int val, ret; + uint8_t mode; + + val = -1; + sscanf(buf, "%u", &val); + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + client = to_i2c_client(dev->parent); + + mutex_lock(&ldata->led_data_mutex); + switch (val) { + case 0: /* stop flashing */ + mode = ldata->mode; + ldata->blink = 0; + break; + case 1: + case 2: + case 3: + mode = val + 1; + break; + + default: + mutex_unlock(&ldata->led_data_mutex); + return -EINVAL; + } + mutex_unlock(&ldata->led_data_mutex); + + ret = microp_i2c_write_led_mode(client, led_cdev, mode, 0xffff); + if (ret) + dev_err(&client->dev, "%s set blink failed\n", led_cdev->name); + + return count; +} + +static DEVICE_ATTR(blink, 0644, microp_i2c_led_blink_show, + microp_i2c_led_blink_store); + +static ssize_t microp_i2c_led_off_timer_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct microp_i2c_client_data *cdata; + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + struct i2c_client *client; + uint8_t data[2]; + int ret, offtime; + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + client = to_i2c_client(dev->parent); + cdata = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "Getting %s remaining time\n", led_cdev->name); + + if (ldata->type == GREEN_LED) { + ret = i2c_read_block(client, + MICROP_I2C_RCMD_GREEN_LED_REMAIN_TIME, data, 2); + } else if (ldata->type == AMBER_LED) { + ret = i2c_read_block(client, + MICROP_I2C_RCMD_AMBER_LED_REMAIN_TIME, + data, 2); + } else if (ldata->type == BLUE_LED) { + ret = i2c_read_block(client, + MICROP_I2C_RCMD_BLUE_LED_REMAIN_TIME, data, 2); + } else { + dev_err(&client->dev, "Unknown led %s\n", ldata->ldev.name); + return -EINVAL; + } + + if (ret) { + dev_err(&client->dev, + "%s get off_timer failed\n", led_cdev->name); + } + offtime = (int)((data[1] | data[0] << 8) * 2); + + ret = sprintf(buf, "Time remains %d:%d\n", offtime / 60, offtime % 60); + return ret; +} + +static ssize_t microp_i2c_led_off_timer_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct led_classdev *led_cdev; + struct microp_led_data *ldata; + struct i2c_client *client; + int min, sec, ret; + uint16_t off_timer; + + min = -1; + sec = -1; + sscanf(buf, "%d %d", &min, &sec); + + if (min < 0 || min > 255) + return -EINVAL; + if (sec < 0 || sec > 255) + return -EINVAL; + + led_cdev = (struct led_classdev *)dev_get_drvdata(dev); + ldata = container_of(led_cdev, struct microp_led_data, ldev); + client = to_i2c_client(dev->parent); + + dev_dbg(&client->dev, "Setting %s off_timer to %d min %d sec\n", + led_cdev->name, min, sec); + + if (!min && !sec) + off_timer = 0xFFFF; + else + off_timer = (min * 60 + sec) / 2; + + ret = microp_i2c_write_led_mode(client, led_cdev, + ldata->mode, off_timer); + if (ret) { + dev_err(&client->dev, + "%s set off_timer %d min %d sec failed\n", + led_cdev->name, min, sec); + } + return count; +} + +static DEVICE_ATTR(off_timer, 0644, microp_i2c_led_off_timer_show, + microp_i2c_led_off_timer_store); + +static void microp_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + unsigned long flags; + struct i2c_client *client = to_i2c_client(led_cdev->dev->parent); + struct microp_led_data *ldata = + container_of(led_cdev, struct microp_led_data, ldev); + + dev_dbg(&client->dev, "Setting %s brightness current %d new %d\n", + led_cdev->name, led_cdev->brightness, brightness); + + if (brightness > 255) + brightness = 255; + led_cdev->brightness = brightness; + + spin_lock_irqsave(&ldata->brightness_lock, flags); + ldata->brightness = brightness; + spin_unlock_irqrestore(&ldata->brightness_lock, flags); + + schedule_work(&ldata->brightness_work); +} + +static void microp_led_brightness_set_work(struct work_struct *work) +{ + unsigned long flags; + struct microp_led_data *ldata = + container_of(work, struct microp_led_data, brightness_work); + struct led_classdev *led_cdev = &ldata->ldev; + + struct i2c_client *client = to_i2c_client(led_cdev->dev->parent); + + enum led_brightness brightness; + int ret; + uint8_t mode; + + spin_lock_irqsave(&ldata->brightness_lock, flags); + brightness = ldata->brightness; + spin_unlock_irqrestore(&ldata->brightness_lock, flags); + + if (brightness) + mode = 1; + else + mode = 0; + + ret = microp_i2c_write_led_mode(client, led_cdev, mode, 0xffff); + if (ret) { + dev_err(&client->dev, + "led_brightness_set failed to set mode\n"); + } +} + +static void microp_led_brightness_gpo_set_work(struct work_struct *work) +{ + unsigned long flags; + struct microp_led_data *ldata = + container_of(work, struct microp_led_data, brightness_work); + + enum led_brightness brightness; + int ret; + uint8_t addr, data[3] = {0x00,0x02,0x00}, enable; + + spin_lock_irqsave(&ldata->brightness_lock, flags); + brightness = ldata->brightness; + spin_unlock_irqrestore(&ldata->brightness_lock, flags); + + enable = brightness ? 1 : 0; + if (enable) + addr = MICROP_I2C_WCMD_GPO_LED_STATUS_EN; + else + addr = MICROP_I2C_WCMD_GPO_LED_STATUS_DIS; + + ret = microp_i2c_write (addr, data, 3); + if (ret < 0) + pr_err("%s failed on set gpo led mode:%d\n", __func__, brightness); +} + +struct device_attribute *green_amber_attrs[] = { + &dev_attr_blink, + &dev_attr_off_timer, +}; + +static void microp_led_buttons_brightness_set_work(struct work_struct *work) +{ + + unsigned long flags; + struct microp_led_data *ldata = + container_of(work, struct microp_led_data, brightness_work); + struct led_classdev *led_cdev = &ldata->ldev; + + struct i2c_client *client = to_i2c_client(led_cdev->dev->parent); + struct microp_i2c_client_data *cdata = i2c_get_clientdata(client); + + uint8_t data[4] = {0, 0, 0}; + int ret = 0; + enum led_brightness brightness; + uint8_t value; + + spin_lock_irqsave(&ldata->brightness_lock, flags); + brightness = ldata->brightness; + spin_unlock_irqrestore(&ldata->brightness_lock, flags); + + value = brightness >= 255 ? 0xFF : 0; + + /* avoid a flicker that can occur when writing the same value */ + if (cdata->button_led_value == value) + return; + cdata->button_led_value = value; + + /* in 40ms */ + data[0] = 0x05; + /* duty cycle 0-255 */ + data[1] = value; + /* bit2 == change brightness */ + data[3] = 0x04; + + ret = i2c_write_block(client, MICROP_I2C_WCMD_BUTTONS_LED_CTRL, + data, 4); + if (ret < 0) + dev_err(&client->dev, "%s failed on set buttons\n", __func__); +} + +static int microp_oj_interrupt_mode(struct i2c_client *client, uint8_t enable) +{ + int ret; + + if (enable) { + ret = microp_interrupt_enable(client, IRQ_OJ); + printk("%s: microp_interrupt_enable called\n", __func__ ); + } else { + ret = microp_interrupt_disable(client, IRQ_OJ); + printk("%s: microp_interrupt_disable called\n", __func__ ); + } + + return ret; +} + +static int microp_spi_enable(uint8_t on) +{ + struct i2c_client *client; + int ret; + + client = private_microp_client; + ret = i2c_write_block(client, MICROP_I2C_WCMD_SPI_EN, &on, 1); + if (ret < 0) { + dev_err(&client->dev,"%s: i2c_write_block fail\n", __func__); + return ret; + } + + msleep(10); + return ret; +} + +/* Lookup active SPI devices and only turn it off when no device + * is using it + * */ +int microp_spi_vote_enable(int spi_device, uint8_t enable) { + //XXX need to check that all that crap in the HTC kernel is needed + struct i2c_client *client = private_microp_client; + struct microp_i2c_client_data *cdata = i2c_get_clientdata(client); + uint8_t data[2] = {0, 0}; + int ret = 0; + + if (!client) { + printk(KERN_ERR "%s: dataset: client is empty\n", __func__); + return -EIO; + } + + if (spi_device == SPI_OJ) { + microp_oj_interrupt_mode(client, enable); + printk(KERN_ERR "%s: Changing OJ interrupt mode [%d]\n", __func__, enable); + } + + mutex_lock(&cdata->microp_adc_mutex); + /* Add/remove it from the poll */ + if (enable) + cdata->spi_devices_vote |= spi_device; + else + cdata->spi_devices_vote &= ~spi_device; + + ret = i2c_read_block(client, MICROP_I2C_RCMD_SPI_BL_STATUS, data, 2); + if (ret != 0) { + printk(KERN_ERR "%s: read SPI/BL status fail\n", __func__); + mutex_unlock(&cdata->microp_adc_mutex); + return ret; + } + + if ((data[1] & 0x01) == + ((cdata->spi_devices & cdata->spi_devices_vote) ? 1 : 0)) { + printk(KERN_ERR "%s: already in voted state, [spi_device %d,enable %d], [spi_status %d, spi_devices_vote %d]\n", __func__, spi_device, enable, data[1]&0x01, cdata->spi_devices_vote); + mutex_unlock(&cdata->microp_adc_mutex); + return ret; + } + + if (cdata->spi_devices & cdata->spi_devices_vote) + enable = 1; + else + enable = 0; + + printk(KERN_ERR "%s: Changing SPI [%d]\n", __func__, enable); + + mutex_unlock(&cdata->microp_adc_mutex); + ret = microp_spi_enable(enable); + return ret; +} +EXPORT_SYMBOL(microp_spi_vote_enable); + +/* + * G-sensor + */ +static int gsensor_read_reg(uint8_t reg, uint8_t *data) +{ + struct i2c_client *client = private_microp_client; + int ret; + uint8_t tmp[2]; + + mutex_lock(&gsensor_RW_mutex); + + ret = i2c_write_block(client, MICROP_I2C_WCMD_GSENSOR_REG_DATA_REQ, + ®, 1); + if (ret < 0) { + dev_err(&client->dev,"%s: i2c_write_block fail\n", __func__); + mutex_unlock(&gsensor_RW_mutex); + return ret; + } + msleep(10); + + ret = i2c_read_block(client, MICROP_I2C_RCMD_GSENSOR_REG_DATA, tmp, 2); + if (ret < 0) { + dev_err(&client->dev,"%s: i2c_read_block fail\n", __func__); + mutex_unlock(&gsensor_RW_mutex); + return ret; + } + *data = tmp[1]; + + mutex_unlock(&gsensor_RW_mutex); + + return ret; +} + +static int gsensor_write_reg(uint8_t reg, uint8_t data) +{ + struct i2c_client *client = private_microp_client; + int ret; + uint8_t tmp[2]; + + mutex_lock(&gsensor_RW_mutex); + + tmp[0] = reg; + tmp[1] = data; + ret = i2c_write_block(client, MICROP_I2C_WCMD_GSENSOR_REG, tmp, 2); + if (ret < 0) { + dev_err(&client->dev,"%s: i2c_write_block fail\n", __func__); + mutex_unlock(&gsensor_RW_mutex); + return ret; + } + + mutex_unlock(&gsensor_RW_mutex); + + return ret; +} + +static int gsensor_read_acceleration(short *buf) +{ + struct i2c_client *client = private_microp_client; + int ret; + uint8_t tmp[6]; + struct microp_i2c_client_data *cdata; + + mutex_lock(&gsensor_RW_mutex); + + cdata = i2c_get_clientdata(client); + + tmp[0] = 1; + ret = i2c_write_block(client, MICROP_I2C_WCMD_GSENSOR_DATA_REQ, + tmp, 1); + if (ret < 0) { + dev_err(&client->dev,"%s: i2c_write_block fail\n", __func__); + mutex_unlock(&gsensor_RW_mutex); + return ret; + } + + msleep(10); + + ret = i2c_read_block(client, MICROP_I2C_RCMD_GSENSOR_DATA, + tmp, 6); + if (ret < 0) { + dev_err(&client->dev, "%s: i2c_read_block fail\n", + __func__); + mutex_unlock(&gsensor_RW_mutex); + return ret; + } + buf[0] = (short)(tmp[0] << 8 | tmp[1]); + buf[0] >>= 6; + buf[1] = (short)(tmp[2] << 8 | tmp[3]); + buf[1] >>= 6; + buf[2] = (short)(tmp[4] << 8 | tmp[5]); + buf[2] >>= 6; + +#ifdef DEBUG_BMA150 + /* Log this to debugfs */ + gsensor_log_status(ktime_get(), buf[0], buf[1], buf[2]); +#endif + + mutex_unlock(&gsensor_RW_mutex); + + return 1; +} + +static int gsensor_init_hw(void) +{ + uint8_t reg; + int ret; + + pr_debug("%s\n", __func__); + + ret = gsensor_read_reg(RANGE_BWIDTH_REG, ®); + if (ret < 0 ) + return -EIO; + reg &= 0xe0; + ret = gsensor_write_reg(RANGE_BWIDTH_REG, reg); + if (ret < 0 ) + return -EIO; + + ret = gsensor_read_reg(SMB150_CONF2_REG, ®); + if (ret < 0 ) + return -EIO; + reg |= (1 << 3); + ret = gsensor_write_reg(SMB150_CONF2_REG, reg); + + return ret; +} + +static int bma150_set_mode(char mode) +{ + uint8_t reg; + int ret; + + mutex_lock(&gsensor_set_mode_mutex); + + pr_debug("%s mode = %d\n", __func__, mode); + if (mode == BMA_MODE_NORMAL) + microp_spi_vote_enable(SPI_GSENSOR, 1); + + ret = gsensor_read_reg(SMB150_CTRL_REG, ®); + if (ret < 0 ) { + mutex_unlock(&gsensor_set_mode_mutex); + return -EIO; + } + reg = (reg & 0xfe) | mode; + ret = gsensor_write_reg(SMB150_CTRL_REG, reg); + + if (mode == BMA_MODE_SLEEP) + microp_spi_vote_enable(SPI_GSENSOR, 0); + + mutex_unlock(&gsensor_set_mode_mutex); + + return ret; +} + +static int gsensor_read(uint8_t *data) +{ + int ret; + uint8_t reg = data[0]; + + ret = gsensor_read_reg(reg, &data[1]); + pr_debug("%s reg = %x data = %x\n", __func__, reg, data[1]); + return ret; +} + +static int gsensor_write(uint8_t *data) +{ + int ret; + uint8_t reg = data[0]; + + pr_debug("%s reg = %x data = %x\n", __func__, reg, data[1]); + ret = gsensor_write_reg(reg, data[1]); + return ret; +} + +static DEFINE_MUTEX(bma150_lock); + +static int bma150_open(struct inode *inode, struct file *file) +{ + pr_debug("%s\n", __func__); + return nonseekable_open(inode, file); +} + +static int bma150_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static long bma150_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + char rwbuf[8]; + int ret = -1; + short buf[8], temp; + + switch (cmd) { + case BMA_IOCTL_READ: + case BMA_IOCTL_WRITE: + case BMA_IOCTL_SET_MODE: + if (copy_from_user(&rwbuf, argp, sizeof(rwbuf))) + return -EFAULT; + break; + case BMA_IOCTL_READ_ACCELERATION: + if (copy_from_user(&buf, argp, sizeof(buf))) + return -EFAULT; + break; + default: + break; + } + + mutex_lock(&bma150_lock); + switch (cmd) { + case BMA_IOCTL_INIT: + ret = gsensor_init_hw(); + if (ret < 0) + goto err; + break; + + case BMA_IOCTL_READ: + if (rwbuf[0] < 1) { + ret = -EINVAL; + goto err; + } + ret = gsensor_read(rwbuf); + if (ret < 0) + goto err; + break; + case BMA_IOCTL_WRITE: + if (rwbuf[0] < 2) { + ret = -EINVAL; + goto err; + } + ret = gsensor_write(rwbuf); + if (ret < 0) + goto err; + break; + case BMA_IOCTL_READ_ACCELERATION: + ret = gsensor_read_acceleration(&buf[0]); + if (ret < 0) + goto err; + break; + case BMA_IOCTL_SET_MODE: + bma150_set_mode(rwbuf[0]); + break; + case BMA_IOCTL_GET_INT: + temp = 0; + break; + default: + ret = -ENOTTY; + goto err; + } + mutex_unlock(&bma150_lock); + + switch (cmd) { + case BMA_IOCTL_READ: + if (copy_to_user(argp, &rwbuf, sizeof(rwbuf))) + return -EFAULT; + break; + case BMA_IOCTL_READ_ACCELERATION: + if (copy_to_user(argp, &buf, sizeof(buf))) + return -EFAULT; + break; + case BMA_IOCTL_GET_INT: + if (copy_to_user(argp, &temp, sizeof(temp))) + return -EFAULT; + break; + default: + break; + } + + return 0; +err: + mutex_unlock(&bma150_lock); + return ret; +} + +static struct file_operations bma_fops = { + .owner = THIS_MODULE, + .open = bma150_open, + .release = bma150_release, + .unlocked_ioctl = bma150_ioctl, +}; + +static struct miscdevice spi_bma_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = BMA150_G_SENSOR_NAME, + .fops = &bma_fops, +}; + +/* + * Interrupt + */ +static irqreturn_t microp_i2c_intr_irq_handler(int irq, void *dev_id) +{ + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + + client = to_i2c_client(dev_id); + cdata = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "intr_irq_handler\n"); + + disable_irq_nosync(client->irq); + schedule_work(&cdata->work.work); + return IRQ_HANDLED; +} + +static void microp_int_dispatch(u32 status) +{ + unsigned int mask; + int irq; + + while (status) { + mask = status & -status; + irq = fls(mask) - 1; + status &= ~mask; + generic_handle_irq(FIRST_MICROP_IRQ + irq); + } +} + +static enum hrtimer_restart hr_dispath_irq_func(struct hrtimer *data) +{ + struct i2c_client *client = private_microp_client; + struct microp_i2c_client_data *cdata; + + cdata = i2c_get_clientdata(client); + microp_int_dispatch(cdata->intr_status); + cdata->intr_status = 0; + return HRTIMER_NORESTART; +} + +static void microp_i2c_intr_work_func(struct work_struct *work) +{ + struct microp_i2c_work *up_work; + struct i2c_client *client; + struct microp_i2c_client_data *cdata; + uint8_t data[3]; + uint16_t intr_status = 0, gpi_status = 0; + int keycode = 0, ret = 0; + ktime_t zero_debounce; + + up_work = container_of(work, struct microp_i2c_work, work); + client = up_work->client; + cdata = i2c_get_clientdata(client); + + ret = i2c_read_block(client, MICROP_I2C_RCMD_GPI_INT_STATUS, data, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: read interrupt status fail\n", + __func__); + } + + intr_status = data[0]<<8 | data[1]; + ret = i2c_write_block(client, MICROP_I2C_WCMD_GPI_INT_STATUS_CLR, + data, 2); + if (ret < 0) { + dev_err(&client->dev, "%s: clear interrupt status fail\n", + __func__); + } + pr_debug("intr_status=0x%02x\n", intr_status); + + if (intr_status & IRQ_SDCARD) { + microp_read_gpi_status(client, &gpi_status); + bravo_microp_sdslot_update_status(gpi_status); + } + + if (intr_status & IRQ_HEADSETIN) { + cdata->is_hpin_pin_stable = 0; + wake_lock_timeout(µp_i2c_wakelock, 3*HZ); + if (!cdata->headset_is_in) + schedule_delayed_work(&cdata->hpin_debounce_work, + msecs_to_jiffies(500)); + else + schedule_delayed_work(&cdata->hpin_debounce_work, + msecs_to_jiffies(300)); + } + + if (intr_status & IRQ_REMOTEKEY) { + if ((get_remote_keycode(&keycode) == 0) && + (cdata->is_hpin_pin_stable)) { + htc_35mm_key_event(keycode, &cdata->is_hpin_pin_stable); + } + } + + cdata->intr_status = intr_status; + zero_debounce = ktime_set(0, 0); /* No debounce time */ + hrtimer_start(&cdata->gen_irq_timer, zero_debounce, HRTIMER_MODE_REL); + + enable_irq(client->irq); +} + +static int microp_function_initialize(struct i2c_client *client) +{ + struct microp_i2c_client_data *cdata; + uint8_t data[20]; + uint16_t stat, interrupts = 0; + int i; + int ret; + struct led_classdev *led_cdev; + + cdata = i2c_get_clientdata(client); + + /* Headset */ + for (i = 0; i < 6; i++) { + data[i] = (uint8_t)(remote_key_adc_table[i] >> 8); + data[i + 6] = (uint8_t)(remote_key_adc_table[i]); + } + ret = i2c_write_block(client, + MICROP_I2C_WCMD_REMOTEKEY_TABLE, data, 12); + if (ret) + goto exit; + + INIT_DELAYED_WORK( + &cdata->hpin_debounce_work, hpin_debounce_do_work); + + /* SD Card */ + interrupts |= IRQ_SDCARD; + + /* set LED initial state */ + for (i = 0; i < BLUE_LED; i++) { + led_cdev = &cdata->leds[i].ldev; + microp_i2c_write_led_mode(client, led_cdev, 0, 0xffff); + } + +#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL + /* OJ interrupt */ + interrupts |= IRQ_OJ; +#endif + + /* enable the interrupts */ + ret = microp_interrupt_enable(client, interrupts); + if (ret < 0) { + dev_err(&client->dev, "%s: failed to enable gpi irqs\n", + __func__); + goto err_irq_en; + } + + microp_read_gpi_status(client, &stat); + bravo_microp_sdslot_update_status(stat); + + return 0; + +err_irq_en: + gpio_free(BRAVO_GPIO_LS_EN_N); +exit: + return ret; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +void microp_early_suspend(struct early_suspend *h) +{ + struct microp_i2c_client_data *cdata; + struct i2c_client *client = private_microp_client; + int ret; + + if (!client) { + pr_err("%s: dataset: client is empty\n", __func__); + return; + } + cdata = i2c_get_clientdata(client); + + cdata->microp_is_suspend = 1; + + disable_irq(client->irq); + ret = cancel_work_sync(&cdata->work.work); + if (ret != 0) { + enable_irq(client->irq); + } +} + +void microp_early_resume(struct early_suspend *h) +{ + struct i2c_client *client = private_microp_client; + struct microp_i2c_client_data *cdata; + + if (!client) { + pr_err("%s: dataset: client is empty\n", __func__); + return; + } + cdata = i2c_get_clientdata(client); + + cdata->microp_is_suspend = 0; + enable_irq(client->irq); +} +#endif + +static int microp_i2c_suspend(struct i2c_client *client, + pm_message_t mesg) +{ + return 0; +} + +static int microp_i2c_resume(struct i2c_client *client) +{ + return 0; +} + +static void register_microp_devices(struct platform_device *devices, int num) +{ + int i; + for (i = 0; i < num; i++) + platform_device_register((devices + i)); +} + +static struct { + const char *name; + void (*led_set_work)(struct work_struct *); + struct device_attribute **attrs; + int attr_cnt; +} microp_leds[] = { + [GREEN_LED] = { + .name = "green", + .led_set_work = microp_led_brightness_set_work, + .attrs = green_amber_attrs, + .attr_cnt = ARRAY_SIZE(green_amber_attrs) + }, + [AMBER_LED] = { + .name = "amber", + .led_set_work = microp_led_brightness_set_work, + .attrs = green_amber_attrs, + .attr_cnt = ARRAY_SIZE(green_amber_attrs) + }, + [BLUE_LED] = { + .name = "blue", + .led_set_work = microp_led_brightness_gpo_set_work, + .attrs = NULL, + .attr_cnt = 0 + }, + [BUTTONS_LED] = { + .name = "button-backlight", + .led_set_work = microp_led_buttons_brightness_set_work + }, +}; + +static int microp_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct microp_i2c_client_data *cdata; + struct microp_i2c_platform_data *pdata; + uint8_t data[6]; + int ret; + int i; + int j; + + private_microp_client = client; + cdata = kzalloc(sizeof(struct microp_i2c_client_data), GFP_KERNEL); + if (!cdata) { + ret = -ENOMEM; + dev_err(&client->dev, "failed on allocat cdata\n"); + goto err_cdata; + } + + i2c_set_clientdata(client, cdata); + + mutex_init(&cdata->microp_adc_mutex); + mutex_init(&cdata->microp_i2c_rw_mutex); + + pdata = client->dev.platform_data; + if (!pdata) { + ret = -EBUSY; + dev_err(&client->dev, "failed on get pdata\n"); + goto err_exit; + } + pdata->dev_id = (void *)&client->dev; + + ret = i2c_read_block(client, MICROP_I2C_RCMD_VERSION, data, 2); + if (ret || !(data[0] && data[1])) { + ret = -ENODEV; + dev_err(&client->dev, "failed on get microp version\n"); + goto err_exit; + } + dev_info(&client->dev, "microp version [%02X][%02X]\n", + data[0], data[1]); + + ret = gpio_request(BRAVO_GPIO_UP_RESET_N, "microp_i2c_wm"); + if (ret < 0) { + dev_err(&client->dev, "failed on request gpio reset\n"); + goto err_exit; + } + ret = gpio_direction_output(BRAVO_GPIO_UP_RESET_N, 1); + if (ret < 0) { + dev_err(&client->dev, + "failed on gpio_direction_output reset\n"); + goto err_gpio_reset; + } + + cdata->version = data[0] << 8 | data[1]; + cdata->microp_is_suspend = 0; + cdata->spi_devices_vote = 0; + cdata->spi_devices = SPI_OJ | SPI_GSENSOR; + + cdata->intr_status = 0; + hrtimer_init(&cdata->gen_irq_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + cdata->gen_irq_timer.function = hr_dispath_irq_func; + + wake_lock_init(µp_i2c_wakelock, WAKE_LOCK_SUSPEND, + "microp_i2c_present"); + + /* LEDs */ + ret = 0; + for (i = 0; i < ARRAY_SIZE(microp_leds) && !ret; ++i) { + struct microp_led_data *ldata = &cdata->leds[i]; + + ldata->type = i; + ldata->ldev.name = microp_leds[i].name; + ldata->ldev.brightness_set = microp_brightness_set; + mutex_init(&ldata->led_data_mutex); + INIT_WORK(&ldata->brightness_work, microp_leds[i].led_set_work); + spin_lock_init(&ldata->brightness_lock); + ret = led_classdev_register(&client->dev, &ldata->ldev); + if (ret) { + ldata->ldev.name = NULL; + break; + } + + for (j = 0; j < microp_leds[i].attr_cnt && !ret; ++j) + ret = device_create_file(ldata->ldev.dev, + microp_leds[i].attrs[j]); + } + if (ret) { + dev_err(&client->dev, "failed to add leds\n"); + goto err_add_leds; + } + + /* Headset */ + cdata->headset_is_in = 0; + cdata->is_hpin_pin_stable = 1; + platform_device_register(&bravo_h35mm); + + ret = device_create_file(&client->dev, &dev_attr_key_adc); + + /* G-sensor */ + ret = misc_register(&spi_bma_device); + if (ret < 0) { + pr_err("%s: init bma150 misc_register fail\n", + __func__); + goto err_register_bma150; + } + + mutex_init(&gsensor_RW_mutex); + mutex_init(&gsensor_set_mode_mutex); + + microp_spi_vote_enable(SPI_GSENSOR, 1); + +#ifdef DEBUG_BMA150 + debugfs_create_file("gsensor_log", 0444, NULL, NULL, &gsensor_log_fops); +#endif + /* Setup IRQ handler */ + INIT_WORK(&cdata->work.work, microp_i2c_intr_work_func); + cdata->work.client = client; + + ret = request_irq(client->irq, + microp_i2c_intr_irq_handler, + IRQF_TRIGGER_LOW, + "microp_interrupt", + &client->dev); + if (ret) { + dev_err(&client->dev, "request_irq failed\n"); + goto err_intr; + } + ret = set_irq_wake(client->irq, 1); + if (ret) { + dev_err(&client->dev, "set_irq_wake failed\n"); + goto err_intr; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + if (cdata->enable_early_suspend) { + cdata->early_suspend.level = + EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + cdata->early_suspend.suspend = microp_early_suspend; + cdata->early_suspend.resume = microp_early_resume; + register_early_suspend(&cdata->early_suspend); + } +#endif + + ret = microp_function_initialize(client); + if (ret) { + dev_err(&client->dev, "failed on microp function initialize\n"); + goto err_fun_init; + } + + register_microp_devices(pdata->microp_devices, pdata->num_devices); + + return 0; + +err_fun_init: +err_intr: + misc_deregister(&spi_bma_device); + +err_register_bma150: + platform_device_unregister(&bravo_h35mm); + device_remove_file(&client->dev, &dev_attr_key_adc); + +err_add_leds: + for (i = 0; i < ARRAY_SIZE(microp_leds); ++i) { + if (!cdata->leds[i].ldev.name) + continue; + led_classdev_unregister(&cdata->leds[i].ldev); + for (j = 0; j < microp_leds[i].attr_cnt; ++j) + device_remove_file(cdata->leds[i].ldev.dev, + microp_leds[i].attrs[j]); + } + + wake_lock_destroy(µp_i2c_wakelock); + kfree(cdata); + i2c_set_clientdata(client, NULL); + +err_cdata: +err_gpio_reset: + gpio_free(BRAVO_GPIO_UP_RESET_N); +err_exit: + return ret; +} + +static int __devexit microp_i2c_remove(struct i2c_client *client) +{ + struct microp_i2c_client_data *cdata; + int i; + int j; + + cdata = i2c_get_clientdata(client); + + for (i = 0; i < ARRAY_SIZE(microp_leds); ++i) { + struct microp_led_data *ldata = &cdata->leds[i]; + cancel_work_sync(&ldata->brightness_work); + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + if (cdata->enable_early_suspend) { + unregister_early_suspend(&cdata->early_suspend); + } +#endif + + for (i = 0; i < ARRAY_SIZE(microp_leds); ++i) { + if (!cdata->leds[i].ldev.name) + continue; + led_classdev_unregister(&cdata->leds[i].ldev); + for (j = 0; j < microp_leds[i].attr_cnt; ++j) + device_remove_file(cdata->leds[i].ldev.dev, + microp_leds[i].attrs[j]); + } + + free_irq(client->irq, &client->dev); + + gpio_free(BRAVO_GPIO_UP_RESET_N); + + platform_device_unregister(&bravo_h35mm); + + /* G-sensor */ + misc_deregister(&spi_bma_device); + + kfree(cdata); + + return 0; +} + +static const struct i2c_device_id microp_i2c_id[] = { + { MICROP_I2C_NAME, 0 }, + { } +}; + +static struct i2c_driver microp_i2c_driver = { + .driver = { + .name = MICROP_I2C_NAME, + }, + .id_table = microp_i2c_id, + .probe = microp_i2c_probe, + .suspend = microp_i2c_suspend, + .resume = microp_i2c_resume, + .remove = __devexit_p(microp_i2c_remove), +}; + +static void microp_irq_ack(unsigned int irq) +{ + ; +} + +static void microp_irq_mask(unsigned int irq) +{ + ; +} + +static void microp_irq_unmask(unsigned int irq) +{ + ; +} + +static struct irq_chip microp_irq_chip = { + .name = "microp", + .disable = microp_irq_mask, + .ack = microp_irq_ack, + .mask = microp_irq_mask, + .unmask = microp_irq_unmask, +}; + +static int __init microp_i2c_init(void) +{ + int n, MICROP_IRQ_END = FIRST_MICROP_IRQ + NR_MICROP_IRQS; + for (n = FIRST_MICROP_IRQ; n < MICROP_IRQ_END; n++) { + set_irq_chip(n, µp_irq_chip); + set_irq_handler(n, handle_level_irq); + set_irq_flags(n, IRQF_VALID); + } + + return i2c_add_driver(µp_i2c_driver); +} + +static void __exit microp_i2c_exit(void) +{ + i2c_del_driver(µp_i2c_driver); +} + +module_init(microp_i2c_init); +module_exit(microp_i2c_exit); + +MODULE_AUTHOR("Eric Olsen "); +MODULE_DESCRIPTION("MicroP I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-bravo-mmc.c b/arch/arm/mach-msm/board-bravo-mmc.c new file mode 100644 index 0000000000000..8838f4574ab8e --- /dev/null +++ b/arch/arm/mach-msm/board-bravo-mmc.c @@ -0,0 +1,442 @@ +/* linux/arch/arm/mach-msm/board-bravo-mmc.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation + * + * 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 "board-bravo.h" +#include "devices.h" +#include "proc_comm.h" + +#undef BRAVO_DEBUG_MMC + +static bool opt_disable_sdcard; +static int __init bravo_disablesdcard_setup(char *str) +{ + opt_disable_sdcard = (bool)simple_strtol(str, NULL, 0); + return 1; +} + +__setup("board_bravo.disable_sdcard=", bravo_disablesdcard_setup); + +static uint32_t sdcard_on_gpio_table[] = { + PCOM_GPIO_CFG(62, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(63, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(64, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ +}; + +static uint32_t sdcard_off_gpio_table[] = { + PCOM_GPIO_CFG(62, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(63, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(64, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ +}; + +static struct vreg *sdslot_vreg; +static uint32_t sdslot_vdd = 0xffffffff; +static uint32_t sdslot_vreg_enabled; + +static struct { + int mask; + int level; +} mmc_vdd_table[] = { + { MMC_VDD_165_195, 1800 }, + { MMC_VDD_20_21, 2050 }, + { MMC_VDD_21_22, 2150 }, + { MMC_VDD_22_23, 2250 }, + { MMC_VDD_23_24, 2350 }, + { MMC_VDD_24_25, 2450 }, + { MMC_VDD_25_26, 2550 }, + { MMC_VDD_26_27, 2650 }, + { MMC_VDD_27_28, 2750 }, + { MMC_VDD_28_29, 2850 }, + { MMC_VDD_29_30, 2950 }, +}; + +static uint32_t bravo_sdslot_switchvdd(struct device *dev, unsigned int vdd) +{ + int i; + int ret; + + if (vdd == sdslot_vdd) + return 0; + + sdslot_vdd = vdd; + + if (vdd == 0) { + config_gpio_table(sdcard_off_gpio_table, + ARRAY_SIZE(sdcard_off_gpio_table)); + vreg_disable(sdslot_vreg); + sdslot_vreg_enabled = 0; + return 0; + } + + if (!sdslot_vreg_enabled) { + ret = vreg_enable(sdslot_vreg); + if (ret) + pr_err("%s: Error enabling vreg (%d)\n", __func__, ret); + config_gpio_table(sdcard_on_gpio_table, + ARRAY_SIZE(sdcard_on_gpio_table)); + sdslot_vreg_enabled = 1; + } + + for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) { + if (mmc_vdd_table[i].mask != (1 << vdd)) + continue; + ret = vreg_set_level(sdslot_vreg, mmc_vdd_table[i].level); + if (ret) + pr_err("%s: Error setting level (%d)\n", __func__, ret); + return 0; + } + + pr_err("%s: Invalid VDD (%d) specified\n", __func__, vdd); + return 0; +} + +static uint32_t bravo_cdma_sdslot_switchvdd(struct device *dev, unsigned int vdd) +{ + if (!vdd == !sdslot_vdd) + return 0; + + /* In CDMA version, the vdd of sdslot is not configurable, and it is + * fixed in 2.85V by hardware design. + */ + + sdslot_vdd = vdd ? MMC_VDD_28_29 : 0; + + if (vdd) { + gpio_set_value(BRAVO_CDMA_SD_2V85_EN, 1); + config_gpio_table(sdcard_on_gpio_table, + ARRAY_SIZE(sdcard_on_gpio_table)); + } else { + config_gpio_table(sdcard_off_gpio_table, + ARRAY_SIZE(sdcard_off_gpio_table)); + gpio_set_value(BRAVO_CDMA_SD_2V85_EN, 0); + } + + sdslot_vreg_enabled = !!vdd; + + return 0; +} + +static unsigned int bravo_sdslot_status(struct device *dev) +{ + return !gpio_get_value(BRAVO_GPIO_SDMC_CD_N); +} + +#define BRAVO_MMC_VDD (MMC_VDD_165_195 | MMC_VDD_20_21 | \ + MMC_VDD_21_22 | MMC_VDD_22_23 | \ + MMC_VDD_23_24 | MMC_VDD_24_25 | \ + MMC_VDD_25_26 | MMC_VDD_26_27 | \ + MMC_VDD_27_28 | MMC_VDD_28_29 | \ + MMC_VDD_29_30) + +int bravo_microp_sdslot_status_register(void (*cb)(int, void *), void *); +unsigned int bravo_microp_sdslot_status(struct device *); + +static struct msm_mmc_platform_data bravo_sdslot_data = { + .ocr_mask = BRAVO_MMC_VDD, + .status = bravo_microp_sdslot_status, + .register_status_notify = bravo_microp_sdslot_status_register, + .translate_vdd = bravo_sdslot_switchvdd, +}; + +static uint32_t wifi_on_gpio_table[] = { + PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(152, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +static uint32_t wifi_off_gpio_table[] = { + PCOM_GPIO_CFG(51, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(56, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(152, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +/* BCM4329 returns wrong sdio_vsn(1) when we read cccr, + * we use predefined value (sdio_vsn=2) here to initial sdio driver well + */ +static struct embedded_sdio_data bravo_wifi_emb_data = { + .cccr = { + .sdio_vsn = 2, + .multi_block = 1, + .low_speed = 0, + .wide_bus = 0, + .high_power = 1, + .high_speed = 1, + }, +}; + +static int bravo_wifi_cd = 0; /* WIFI virtual 'card detect' status */ +static void (*wifi_status_cb)(int card_present, void *dev_id); +static void *wifi_status_cb_devid; + +static int bravo_wifi_status_register( + void (*callback)(int card_present, void *dev_id), + void *dev_id) +{ + if (wifi_status_cb) + return -EAGAIN; + wifi_status_cb = callback; + wifi_status_cb_devid = dev_id; + return 0; +} + +static unsigned int bravo_wifi_status(struct device *dev) +{ + return bravo_wifi_cd; +} + +static struct msm_mmc_platform_data bravo_wifi_data = { + .ocr_mask = MMC_VDD_28_29, + .built_in = 1, + .status = bravo_wifi_status, + .register_status_notify = bravo_wifi_status_register, + .embedded_sdio = &bravo_wifi_emb_data, +}; + +int bravo_wifi_set_carddetect(int val) +{ + pr_info("%s: %d\n", __func__, val); + bravo_wifi_cd = val; + if (wifi_status_cb) { + wifi_status_cb(val, wifi_status_cb_devid); + } else + pr_warning("%s: Nobody to notify\n", __func__); + return 0; +} + +static int bravo_wifi_power_state; + +int bravo_wifi_power(int on) +{ + printk("%s: %d\n", __func__, on); + + if (on) { + config_gpio_table(wifi_on_gpio_table, + ARRAY_SIZE(wifi_on_gpio_table)); + mdelay(50); + } else { + config_gpio_table(wifi_off_gpio_table, + ARRAY_SIZE(wifi_off_gpio_table)); + } + + mdelay(100); + gpio_set_value(BRAVO_GPIO_WIFI_SHUTDOWN_N, on); /* WIFI_SHUTDOWN */ + mdelay(200); + + bravo_wifi_power_state = on; + return 0; +} + +static int bravo_wifi_reset_state; + +int bravo_wifi_reset(int on) +{ + printk("%s: do nothing\n", __func__); + bravo_wifi_reset_state = on; + return 0; +} + +int __init bravo_init_mmc(unsigned int sys_rev, unsigned debug_uart) +{ + uint32_t id; + + printk("%s()+\n", __func__); + + /* initial WIFI_SHUTDOWN# */ + id = PCOM_GPIO_CFG(BRAVO_GPIO_WIFI_SHUTDOWN_N, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + + msm_add_sdcc(1, &bravo_wifi_data, 0, 0); + + if (debug_uart) { + pr_info("%s: sdcard disabled due to debug uart\n", __func__); + goto done; + } + if (opt_disable_sdcard) { + pr_info("%s: sdcard disabled on cmdline\n", __func__); + goto done; + } + + sdslot_vreg_enabled = 0; + + if (is_cdma_version(sys_rev)) { + /* In the CDMA version, sdslot is supplied by a gpio. */ + int rc = gpio_request(BRAVO_CDMA_SD_2V85_EN, "sdslot_en"); + if (rc < 0) { + pr_err("%s: gpio_request(%d) failed: %d\n", __func__, + BRAVO_CDMA_SD_2V85_EN, rc); + return rc; + } + bravo_sdslot_data.translate_vdd = bravo_cdma_sdslot_switchvdd; + } else { + /* in UMTS version, sdslot is supplied by pmic */ + sdslot_vreg = vreg_get(0, "gp6"); + if (IS_ERR(sdslot_vreg)) + return PTR_ERR(sdslot_vreg); + } + +// if (system_rev > 0) + bravo_sdslot_data.status = bravo_sdslot_status; + msm_add_sdcc(2, &bravo_sdslot_data, 0, 0); +// else { +// bravo_sdslot_data.status = bravo_sdslot_status; +// bravo_sdslot_data.register_status_notify = NULL; +// set_irq_wake(MSM_GPIO_TO_INT(BRAVO_GPIO_SDMC_CD_N), 1); +// msm_add_sdcc(2, &bravo_sdslot_data, +// MSM_GPIO_TO_INT(BRAVO_GPIO_SDMC_CD_N), +// IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE); +// } + +done: + printk("%s()-\n", __func__); + return 0; +} + +#if defined(BRAVO_DEBUG_MMC) && defined(CONFIG_DEBUG_FS) + +static int bravommc_dbg_wifi_reset_set(void *data, u64 val) +{ + bravo_wifi_reset((int) val); + return 0; +} + +static int bravommc_dbg_wifi_reset_get(void *data, u64 *val) +{ + *val = bravo_wifi_reset_state; + return 0; +} + +static int bravommc_dbg_wifi_cd_set(void *data, u64 val) +{ + bravo_wifi_set_carddetect((int) val); + return 0; +} + +static int bravommc_dbg_wifi_cd_get(void *data, u64 *val) +{ + *val = bravo_wifi_cd; + return 0; +} + +static int bravommc_dbg_wifi_pwr_set(void *data, u64 val) +{ + bravo_wifi_power((int) val); + return 0; +} + +static int bravommc_dbg_wifi_pwr_get(void *data, u64 *val) +{ + *val = bravo_wifi_power_state; + return 0; +} + +static int bravommc_dbg_sd_pwr_set(void *data, u64 val) +{ + bravo_sdslot_switchvdd(NULL, (unsigned int) val); + return 0; +} + +static int bravommc_dbg_sd_pwr_get(void *data, u64 *val) +{ + *val = sdslot_vdd; + return 0; +} + +static int bravommc_dbg_sd_cd_set(void *data, u64 val) +{ + return -ENOSYS; +} + +static int bravommc_dbg_sd_cd_get(void *data, u64 *val) +{ + *val = bravo_sdslot_data.status(NULL); + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(bravommc_dbg_wifi_reset_fops, + bravommc_dbg_wifi_reset_get, + bravommc_dbg_wifi_reset_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(bravommc_dbg_wifi_cd_fops, + bravommc_dbg_wifi_cd_get, + bravommc_dbg_wifi_cd_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(bravommc_dbg_wifi_pwr_fops, + bravommc_dbg_wifi_pwr_get, + bravommc_dbg_wifi_pwr_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(bravommc_dbg_sd_pwr_fops, + bravommc_dbg_sd_pwr_get, + bravommc_dbg_sd_pwr_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(bravommc_dbg_sd_cd_fops, + bravommc_dbg_sd_cd_get, + bravommc_dbg_sd_cd_set, "%llu\n"); + +static int __init bravommc_dbg_init(void) +{ + struct dentry *dent; + + if (!machine_is_bravo() && !machine_is_bravoc()) + return 0; + + dent = debugfs_create_dir("bravo_mmc_dbg", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("wifi_reset", 0644, dent, NULL, + &bravommc_dbg_wifi_reset_fops); + debugfs_create_file("wifi_cd", 0644, dent, NULL, + &bravommc_dbg_wifi_cd_fops); + debugfs_create_file("wifi_pwr", 0644, dent, NULL, + &bravommc_dbg_wifi_pwr_fops); + debugfs_create_file("sd_pwr", 0644, dent, NULL, + &bravommc_dbg_sd_pwr_fops); + debugfs_create_file("sd_cd", 0644, dent, NULL, + &bravommc_dbg_sd_cd_fops); + return 0; +} + +device_initcall(bravommc_dbg_init); +#endif diff --git a/arch/arm/mach-msm/board-bravo-panel.c b/arch/arm/mach-msm/board-bravo-panel.c new file mode 100644 index 0000000000000..76b39f0ea5663 --- /dev/null +++ b/arch/arm/mach-msm/board-bravo-panel.c @@ -0,0 +1,1271 @@ +/* arch/arm/mach-msm/board-bravo-panel.c + * + * Copyright (c) 2009 Google Inc. + * Author: Dima Zavin + * + * 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 "proc_comm.h" + +#include "board-bravo.h" +#include "devices.h" + +#define SPI_CONFIG (0x00000000) +#define SPI_IO_CONTROL (0x00000004) +#define SPI_OPERATIONAL (0x00000030) +#define SPI_ERROR_FLAGS_EN (0x00000038) +#define SPI_ERROR_FLAGS (0x00000038) +#define SPI_OUTPUT_FIFO (0x00000100) + +static void __iomem *spi_base; +static struct clk *spi_clk ; +static struct vreg *vreg_lcm_rftx_2v6; +static struct vreg *vreg_lcm_aux_2v6; + +#define SAMSUNG_PANEL 0 +/*Bitwise mask for SONY PANEL ONLY*/ +#define SONY_PANEL 0x1 /*Set bit 0 as 1 when it is SONY PANEL*/ +#define SONY_PWM_SPI 0x2 /*Set bit 1 as 1 as PWM_SPI mode, otherwise it is PWM_MICROP mode*/ +#define SONY_GAMMA 0x4 /*Set bit 2 as 1 when panel contains GAMMA table in its NVM*/ +#define SONY_RGB666 0x8 /*Set bit 3 as 1 when panel is 18 bit, otherwise it is 16 bit*/ + +extern int panel_type; + +static int is_sony_spi(void) +{ + return (panel_type & SONY_PWM_SPI ? 1 : 0); +} + +static int is_sony_with_gamma(void) +{ + return (panel_type & SONY_GAMMA ? 1 : 0); +} + +static int is_sony_RGB666(void) +{ + return (panel_type & SONY_RGB666 ? 1 : 0); +} + +static int qspi_send(uint32_t id, uint8_t data) +{ + uint32_t err; + + /* bit-5: OUTPUT_FIFO_NOT_EMPTY */ + while (readl(spi_base + SPI_OPERATIONAL) & (1<<5)) { + if ((err = readl(spi_base + SPI_ERROR_FLAGS))) { + pr_err("%s: ERROR: SPI_ERROR_FLAGS=0x%08x\n", __func__, + err); + return -EIO; + } + } + writel((0x7000 | (id << 9) | data) << 16, spi_base + SPI_OUTPUT_FIFO); + udelay(100); + + return 0; +} + +static int qspi_send_9bit(uint32_t id, uint8_t data) +{ + uint32_t err; + + while (readl(spi_base + SPI_OPERATIONAL) & (1<<5)) { + err = readl(spi_base + SPI_ERROR_FLAGS); + if (err) { + pr_err("%s: ERROR: SPI_ERROR_FLAGS=0x%08x\n", __func__, + err); + return -EIO; + } + } + writel(((id << 8) | data) << 23, spi_base + SPI_OUTPUT_FIFO); + udelay(100); + + return 0; +} + +static int lcm_writeb(uint8_t reg, uint8_t val) +{ + qspi_send(0x0, reg); + qspi_send(0x1, val); + return 0; +} + +static int lcm_writew(uint8_t reg, uint16_t val) +{ + qspi_send(0x0, reg); + qspi_send(0x1, val >> 8); + qspi_send(0x1, val & 0xff); + return 0; +} + +static struct resource resources_msm_fb[] = { + { + .start = MSM_FB_BASE, + .end = MSM_FB_BASE + MSM_FB_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +struct lcm_tbl { + uint8_t reg; + uint8_t val; +}; + +static struct lcm_tbl samsung_oled_rgb565_init_table[] = { + { 0x31, 0x08 }, + { 0x32, 0x14 }, + { 0x30, 0x2 }, + { 0x27, 0x1 }, + { 0x12, 0x8 }, + { 0x13, 0x8 }, + { 0x15, 0x0 }, + { 0x16, 0x02 }, + { 0x39, 0x24 }, + { 0x17, 0x22 }, + { 0x18, 0x33 }, + { 0x19, 0x3 }, + { 0x1A, 0x1 }, + { 0x22, 0xA4 }, + { 0x23, 0x0 }, + { 0x26, 0xA0 }, +}; + +static struct lcm_tbl samsung_oled_rgb666_init_table[] = { + { 0x31, 0x08 }, + { 0x32, 0x14 }, + { 0x30, 0x2 }, + { 0x27, 0x1 }, + { 0x12, 0x8 }, + { 0x13, 0x8 }, + { 0x15, 0x0 }, + { 0x16, 0x01 }, + { 0x39, 0x24 }, + { 0x17, 0x22 }, + { 0x18, 0x33 }, + { 0x19, 0x3 }, + { 0x1A, 0x1 }, + { 0x22, 0xA4 }, + { 0x23, 0x0 }, + { 0x26, 0xA0 }, +}; + +static struct lcm_tbl *init_tablep = samsung_oled_rgb565_init_table; +static size_t init_table_sz = ARRAY_SIZE(samsung_oled_rgb565_init_table); + +#define OLED_GAMMA_TABLE_SIZE (7 * 3) +static struct lcm_tbl samsung_oled_gamma_table[][OLED_GAMMA_TABLE_SIZE] = { + /* level 10 */ + { + /* Gamma-R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x3f }, + { 0x43, 0x35 }, + { 0x44, 0x30 }, + { 0x45, 0x2c }, + { 0x46, 0x13 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x0 }, + { 0x53, 0x0 }, + { 0x54, 0x27 }, + { 0x55, 0x2b }, + { 0x56, 0x12 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x3f }, + { 0x63, 0x34 }, + { 0x64, 0x2f }, + { 0x65, 0x2b }, + { 0x66, 0x1b }, + }, + + /* level 40 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x3e }, + { 0x43, 0x2e }, + { 0x44, 0x2d }, + { 0x45, 0x28 }, + { 0x46, 0x21 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x0 }, + { 0x53, 0x21 }, + { 0x54, 0x2a }, + { 0x55, 0x28 }, + { 0x56, 0x20 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x3e }, + { 0x63, 0x2d }, + { 0x64, 0x2b }, + { 0x65, 0x26 }, + { 0x66, 0x2d }, + }, + + /* level 70 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x35 }, + { 0x43, 0x2c }, + { 0x44, 0x2b }, + { 0x45, 0x26 }, + { 0x46, 0x29 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x0 }, + { 0x53, 0x25 }, + { 0x54, 0x29 }, + { 0x55, 0x26 }, + { 0x56, 0x28 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x34 }, + { 0x63, 0x2b }, + { 0x64, 0x2a }, + { 0x65, 0x23 }, + { 0x66, 0x37 }, + }, + + /* level 100 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x30 }, + { 0x43, 0x2a }, + { 0x44, 0x2b }, + { 0x45, 0x24 }, + { 0x46, 0x2f }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x0 }, + { 0x53, 0x25 }, + { 0x54, 0x29 }, + { 0x55, 0x24 }, + { 0x56, 0x2e }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x2f }, + { 0x63, 0x29 }, + { 0x64, 0x29 }, + { 0x65, 0x21 }, + { 0x66, 0x3f }, + }, + + /* level 130 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x2e }, + { 0x43, 0x29 }, + { 0x44, 0x2a }, + { 0x45, 0x23 }, + { 0x46, 0x34 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0xa }, + { 0x53, 0x25 }, + { 0x54, 0x28 }, + { 0x55, 0x23 }, + { 0x56, 0x33 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x2d }, + { 0x63, 0x28 }, + { 0x64, 0x27 }, + { 0x65, 0x20 }, + { 0x66, 0x46 }, + }, + + /* level 160 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x2b }, + { 0x43, 0x29 }, + { 0x44, 0x28 }, + { 0x45, 0x23 }, + { 0x46, 0x38 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0xb }, + { 0x53, 0x25 }, + { 0x54, 0x27 }, + { 0x55, 0x23 }, + { 0x56, 0x37 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x29 }, + { 0x63, 0x28 }, + { 0x64, 0x25 }, + { 0x65, 0x20 }, + { 0x66, 0x4b }, + }, + + /* level 190 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x29 }, + { 0x43, 0x29 }, + { 0x44, 0x27 }, + { 0x45, 0x22 }, + { 0x46, 0x3c }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x10 }, + { 0x53, 0x26 }, + { 0x54, 0x26 }, + { 0x55, 0x22 }, + { 0x56, 0x3b }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x28 }, + { 0x63, 0x28 }, + { 0x64, 0x24 }, + { 0x65, 0x1f }, + { 0x66, 0x50 }, + }, + + /* level 220 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x28 }, + { 0x43, 0x28 }, + { 0x44, 0x28 }, + { 0x45, 0x20 }, + { 0x46, 0x40 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x11 }, + { 0x53, 0x25 }, + { 0x54, 0x27 }, + { 0x55, 0x20 }, + { 0x56, 0x3f }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x27 }, + { 0x63, 0x26 }, + { 0x64, 0x26 }, + { 0x65, 0x1c }, + { 0x66, 0x56 }, + }, + + /* level 250 */ + { + /* Gamma -R */ + { 0x40, 0x0 }, + { 0x41, 0x3f }, + { 0x42, 0x2a }, + { 0x43, 0x27 }, + { 0x44, 0x27 }, + { 0x45, 0x1f }, + { 0x46, 0x44 }, + /* Gamma -G */ + { 0x50, 0x0 }, + { 0x51, 0x0 }, + { 0x52, 0x17 }, + { 0x53, 0x24 }, + { 0x54, 0x26 }, + { 0x55, 0x1f }, + { 0x56, 0x43 }, + /* Gamma -B */ + { 0x60, 0x0 }, + { 0x61, 0x3f }, + { 0x62, 0x2a }, + { 0x63, 0x25 }, + { 0x64, 0x24 }, + { 0x65, 0x1b }, + { 0x66, 0x5c }, + }, +}; +#define SAMSUNG_OLED_NUM_LEVELS ARRAY_SIZE(samsung_oled_gamma_table) + +#define SAMSUNG_OLED_MIN_VAL 10 +#define SAMSUNG_OLED_MAX_VAL 250 +#define SAMSUNG_OLED_DEFAULT_VAL (SAMSUNG_OLED_MIN_VAL + \ + (SAMSUNG_OLED_MAX_VAL - \ + SAMSUNG_OLED_MIN_VAL) / 2) + +#define SAMSUNG_OLED_LEVEL_STEP ((SAMSUNG_OLED_MAX_VAL - \ + SAMSUNG_OLED_MIN_VAL) / \ + (SAMSUNG_OLED_NUM_LEVELS - 1)) + +#define LCM_GPIO_CFG(gpio, func) \ +PCOM_GPIO_CFG(gpio, func, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA) +static uint32_t samsung_oled_on_gpio_table[] = { + LCM_GPIO_CFG(BRAVO_LCD_R1, 1), + LCM_GPIO_CFG(BRAVO_LCD_R2, 1), + LCM_GPIO_CFG(BRAVO_LCD_R3, 1), + LCM_GPIO_CFG(BRAVO_LCD_R4, 1), + LCM_GPIO_CFG(BRAVO_LCD_R5, 1), + LCM_GPIO_CFG(BRAVO_LCD_G0, 1), + LCM_GPIO_CFG(BRAVO_LCD_G1, 1), + LCM_GPIO_CFG(BRAVO_LCD_G2, 1), + LCM_GPIO_CFG(BRAVO_LCD_G3, 1), + LCM_GPIO_CFG(BRAVO_LCD_G4, 1), + LCM_GPIO_CFG(BRAVO_LCD_G5, 1), + LCM_GPIO_CFG(BRAVO_LCD_B1, 1), + LCM_GPIO_CFG(BRAVO_LCD_B2, 1), + LCM_GPIO_CFG(BRAVO_LCD_B3, 1), + LCM_GPIO_CFG(BRAVO_LCD_B4, 1), + LCM_GPIO_CFG(BRAVO_LCD_B5, 1), + LCM_GPIO_CFG(BRAVO_LCD_PCLK, 1), + LCM_GPIO_CFG(BRAVO_LCD_VSYNC, 1), + LCM_GPIO_CFG(BRAVO_LCD_HSYNC, 1), + LCM_GPIO_CFG(BRAVO_LCD_DE, 1), +}; + +static uint32_t samsung_oled_off_gpio_table[] = { + LCM_GPIO_CFG(BRAVO_LCD_R1, 0), + LCM_GPIO_CFG(BRAVO_LCD_R2, 0), + LCM_GPIO_CFG(BRAVO_LCD_R3, 0), + LCM_GPIO_CFG(BRAVO_LCD_R4, 0), + LCM_GPIO_CFG(BRAVO_LCD_R5, 0), + LCM_GPIO_CFG(BRAVO_LCD_G0, 0), + LCM_GPIO_CFG(BRAVO_LCD_G1, 0), + LCM_GPIO_CFG(BRAVO_LCD_G2, 0), + LCM_GPIO_CFG(BRAVO_LCD_G3, 0), + LCM_GPIO_CFG(BRAVO_LCD_G4, 0), + LCM_GPIO_CFG(BRAVO_LCD_G5, 0), + LCM_GPIO_CFG(BRAVO_LCD_B1, 0), + LCM_GPIO_CFG(BRAVO_LCD_B2, 0), + LCM_GPIO_CFG(BRAVO_LCD_B3, 0), + LCM_GPIO_CFG(BRAVO_LCD_B4, 0), + LCM_GPIO_CFG(BRAVO_LCD_B5, 0), + LCM_GPIO_CFG(BRAVO_LCD_PCLK, 0), + LCM_GPIO_CFG(BRAVO_LCD_VSYNC, 0), + LCM_GPIO_CFG(BRAVO_LCD_HSYNC, 0), + LCM_GPIO_CFG(BRAVO_LCD_DE, 0), +}; +#undef LCM_GPIO_CFG + +#define SONY_TFT_DEF_USER_VAL 102 +#define SONY_TFT_MIN_USER_VAL 30 +#define SONY_TFT_MAX_USER_VAL 255 +#define SONY_TFT_DEF_PANEL_VAL 120 +#define SONY_TFT_MIN_PANEL_VAL 8 +#define SONY_TFT_MAX_PANEL_VAL 255 +#define SONY_TFT_DEF_PANEL_UP_VAL 132 +#define SONY_TFT_MIN_PANEL_UP_VAL 9 +#define SONY_TFT_MAX_PANEL_UP_VAL 255 + +static DEFINE_MUTEX(panel_lock); +static struct work_struct brightness_delayed_work; +static DEFINE_SPINLOCK(brightness_lock); +static uint8_t new_val = SAMSUNG_OLED_DEFAULT_VAL; +static uint8_t last_val = SAMSUNG_OLED_DEFAULT_VAL; +static uint8_t table_sel_vals[] = { 0x43, 0x34 }; +static int table_sel_idx = 0; +static uint8_t tft_panel_on; + +static void gamma_table_bank_select(void) +{ + lcm_writeb(0x39, table_sel_vals[table_sel_idx]); + table_sel_idx ^= 1; +} + +static void samsung_oled_set_gamma_val(int val) +{ + int i; + int level; + int frac; + + val = clamp(val, SAMSUNG_OLED_MIN_VAL, SAMSUNG_OLED_MAX_VAL); + val = (val / 2) * 2; + + level = (val - SAMSUNG_OLED_MIN_VAL) / SAMSUNG_OLED_LEVEL_STEP; + frac = (val - SAMSUNG_OLED_MIN_VAL) % SAMSUNG_OLED_LEVEL_STEP; + + clk_enable(spi_clk); + + for (i = 0; i < OLED_GAMMA_TABLE_SIZE; ++i) { + unsigned int v1; + unsigned int v2 = 0; + u8 v; + if (frac == 0) { + v = samsung_oled_gamma_table[level][i].val; + } else { + + v1 = samsung_oled_gamma_table[level][i].val; + v2 = samsung_oled_gamma_table[level+1][i].val; + v = (v1 * (SAMSUNG_OLED_LEVEL_STEP - frac) + + v2 * frac) / SAMSUNG_OLED_LEVEL_STEP; + } + lcm_writeb(samsung_oled_gamma_table[level][i].reg, v); + } + + gamma_table_bank_select(); + clk_disable(spi_clk); + last_val = val; +} + +static void samsung_oled_panel_config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for (n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +static int samsung_oled_panel_gpio_switch (int on) +{ + samsung_oled_panel_config_gpio_table ( + !!on ? samsung_oled_on_gpio_table : samsung_oled_off_gpio_table, + ARRAY_SIZE(samsung_oled_on_gpio_table)); + + return 0; +} + +static int samsung_oled_panel_init(struct msm_lcdc_panel_ops *ops) +{ + pr_info("%s: +()\n", __func__); + mutex_lock(&panel_lock); + + clk_enable(spi_clk); + /* Set the gamma write target to 4, leave the current gamma set at 2 */ + lcm_writeb(0x39, 0x24); + clk_disable(spi_clk); + + mutex_unlock(&panel_lock); + pr_info("%s: -()\n", __func__); + return 0; +} + +static int samsung_oled_panel_unblank(struct msm_lcdc_panel_ops *ops) +{ + int i; + + pr_info("%s: +()\n", __func__); + + mutex_lock(&panel_lock); + samsung_oled_panel_gpio_switch(1); + + gpio_set_value(BRAVO_GPIO_LCD_RST_N, 1); + udelay(50); + gpio_set_value(BRAVO_GPIO_LCD_RST_N, 0); + udelay(20); + gpio_set_value(BRAVO_GPIO_LCD_RST_N, 1); + msleep(20); + + clk_enable(spi_clk); + + for (i = 0; i < init_table_sz; i++) + lcm_writeb(init_tablep[i].reg, init_tablep[i].val); + + lcm_writew(0xef, 0xd0e8); + lcm_writeb(0x1d, 0xa0); + table_sel_idx = 0; + gamma_table_bank_select(); + samsung_oled_set_gamma_val(last_val); + msleep(250); + lcm_writeb(0x14, 0x03); + clk_disable(spi_clk); + + mutex_unlock(&panel_lock); + + pr_info("%s: -()\n", __func__); + return 0; +} + +static int samsung_oled_panel_blank(struct msm_lcdc_panel_ops *ops) +{ + pr_info("%s: +()\n", __func__); + mutex_lock(&panel_lock); + + clk_enable(spi_clk); + lcm_writeb(0x14, 0x0); + mdelay(1); + lcm_writeb(0x1d, 0xa1); + clk_disable(spi_clk); + msleep(200); + + gpio_set_value(BRAVO_GPIO_LCD_RST_N, 0); + samsung_oled_panel_gpio_switch(0); + + mutex_unlock(&panel_lock); + pr_info("%s: -()\n", __func__); + return 0; +} + +#define LCM_GPIO_CFG(gpio, func, str) \ + PCOM_GPIO_CFG(gpio, func, GPIO_OUTPUT, GPIO_NO_PULL, str) + +static uint32_t sony_tft_display_on_gpio_table[] = { + LCM_GPIO_CFG(BRAVO_LCD_R1, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_R2, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_R3, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_R4, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_R5, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_G0, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_G1, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_G2, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_G3, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_G4, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_G5, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_B1, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_B2, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_B3, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_B4, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_B5, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_PCLK, 1, GPIO_4MA), + LCM_GPIO_CFG(BRAVO_LCD_VSYNC, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_HSYNC, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_DE, 1, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_SPI_CLK, 1, GPIO_4MA), + LCM_GPIO_CFG(BRAVO_LCD_SPI_DO, 1, GPIO_4MA), + LCM_GPIO_CFG(BRAVO_LCD_SPI_CSz, 1, GPIO_4MA), +}; + +static uint32_t sony_tft_display_off_gpio_table[] = { + LCM_GPIO_CFG(BRAVO_LCD_R1, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_R2, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_R3, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_R4, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_R5, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_G0, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_G1, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_G2, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_G3, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_G4, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_G5, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_B1, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_B2, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_B3, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_B4, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_B5, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_PCLK, 0, GPIO_4MA), + LCM_GPIO_CFG(BRAVO_LCD_VSYNC, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_HSYNC, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_DE, 0, GPIO_8MA), + LCM_GPIO_CFG(BRAVO_LCD_SPI_CLK, 0, GPIO_4MA), + LCM_GPIO_CFG(BRAVO_LCD_SPI_DO, 0, GPIO_4MA), + LCM_GPIO_CFG(BRAVO_LCD_SPI_CSz, 0, GPIO_4MA), +}; + +#undef LCM_GPIO_CFG + +#define SONY_TFT_DEF_PANEL_DELTA \ + (SONY_TFT_DEF_PANEL_VAL - SONY_TFT_MIN_PANEL_VAL) +#define SONY_TFT_DEF_USER_DELTA \ + (SONY_TFT_DEF_USER_VAL - SONY_TFT_MIN_USER_VAL) + +static void sony_tft_set_pwm_val(int val) +{ + uint8_t data[4] = {0,0,0,0}; + unsigned int min_pwm, def_pwm, max_pwm; + + pr_info("%s: %d\n", __func__, val); + + last_val = val; + + if (!tft_panel_on) + return; + + if(!is_sony_spi()) { + min_pwm = SONY_TFT_MIN_PANEL_UP_VAL; + def_pwm = SONY_TFT_DEF_PANEL_UP_VAL; + max_pwm = SONY_TFT_MAX_PANEL_UP_VAL; + } else { + min_pwm = SONY_TFT_MIN_PANEL_VAL; + def_pwm = SONY_TFT_DEF_PANEL_VAL; + max_pwm = SONY_TFT_MAX_PANEL_VAL; + } + + if (val <= SONY_TFT_DEF_USER_VAL) { + if (val <= SONY_TFT_MIN_USER_VAL) + val = min_pwm; + else + val = (def_pwm - min_pwm) * + (val - SONY_TFT_MIN_USER_VAL) / + SONY_TFT_DEF_USER_DELTA + + min_pwm; + } else { + val = (max_pwm - def_pwm) * + (val - SONY_TFT_DEF_USER_VAL) / + (SONY_TFT_MAX_USER_VAL - SONY_TFT_DEF_USER_VAL) + + def_pwm; + } + + if (!is_sony_spi()) { + data[0] = 5; + data[1] = val; + data[3] = 1; + microp_i2c_write(0x25, data, 4); + } else { + clk_enable(spi_clk); + qspi_send_9bit(0x0, 0x51); + qspi_send_9bit(0x1, val); + qspi_send_9bit(0x0, 0x53); + qspi_send_9bit(0x1, 0x24); + clk_disable(spi_clk); + } +} + +#undef SONY_TFT_DEF_PANEL_DELTA +#undef SONY_TFT_DEF_USER_DELTA + +static void sony_tft_panel_config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for (n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +static int sony_tft_panel_power(int on) +{ + unsigned id, on_off; + + if (on) { + on_off = 0; + + vreg_enable(vreg_lcm_aux_2v6); + vreg_enable(vreg_lcm_rftx_2v6); + + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + + id = PM_VREG_PDOWN_RFTX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + + gpio_set_value(BRAVO_GPIO_LCD_RST_N, 1); + mdelay(10); + gpio_set_value(BRAVO_GPIO_LCD_RST_N, 0); + udelay(500); + gpio_set_value(BRAVO_GPIO_LCD_RST_N, 1); + mdelay(10); + sony_tft_panel_config_gpio_table( + sony_tft_display_on_gpio_table, + ARRAY_SIZE(sony_tft_display_on_gpio_table)); + } else { + on_off = 1; + + gpio_set_value(BRAVO_GPIO_LCD_RST_N, 0); + + mdelay(120); + + vreg_disable(vreg_lcm_rftx_2v6); + vreg_disable(vreg_lcm_aux_2v6); + + id = PM_VREG_PDOWN_RFTX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + sony_tft_panel_config_gpio_table( + sony_tft_display_off_gpio_table, + ARRAY_SIZE(sony_tft_display_off_gpio_table)); + } + return 0; +} + +static int sony_tft_panel_init(struct msm_lcdc_panel_ops *ops) +{ + return 0; +} + +static void sony_tft_panel_without_gamma_init(void) +{ + pr_info("%s: init gamma setting", __func__); + + qspi_send_9bit(0x0, 0xF1); + qspi_send_9bit(0x1, 0x5A); + qspi_send_9bit(0x1, 0x5A); + // FAh RGB + qspi_send_9bit(0x0, 0xFA); + // Red + qspi_send_9bit(0x1, 0x32); + qspi_send_9bit(0x1, 0x3F); + qspi_send_9bit(0x1, 0x3F); + qspi_send_9bit(0x1, 0x29); + qspi_send_9bit(0x1, 0x3E); + qspi_send_9bit(0x1, 0x3C); + qspi_send_9bit(0x1, 0x3D); + qspi_send_9bit(0x1, 0x2C); + qspi_send_9bit(0x1, 0x27); + qspi_send_9bit(0x1, 0x3D); + qspi_send_9bit(0x1, 0x2E); + qspi_send_9bit(0x1, 0x31); + qspi_send_9bit(0x1, 0x3A); + qspi_send_9bit(0x1, 0x34); + qspi_send_9bit(0x1, 0x36); + // Green + qspi_send_9bit(0x1, 0x1A); + qspi_send_9bit(0x1, 0x3F); + qspi_send_9bit(0x1, 0x3F); + qspi_send_9bit(0x1, 0x2E); + qspi_send_9bit(0x1, 0x40); + qspi_send_9bit(0x1, 0x3C); + qspi_send_9bit(0x1, 0x3C); + qspi_send_9bit(0x1, 0x2B); + qspi_send_9bit(0x1, 0x25); + qspi_send_9bit(0x1, 0x39); + qspi_send_9bit(0x1, 0x25); + qspi_send_9bit(0x1, 0x23); + qspi_send_9bit(0x1, 0x2A); + qspi_send_9bit(0x1, 0x20); + qspi_send_9bit(0x1, 0x22); + // Blue + qspi_send_9bit(0x1, 0x00); + qspi_send_9bit(0x1, 0x3F); + qspi_send_9bit(0x1, 0x3F); + qspi_send_9bit(0x1, 0x2F); + qspi_send_9bit(0x1, 0x3E); + qspi_send_9bit(0x1, 0x3C); + qspi_send_9bit(0x1, 0x3C); + qspi_send_9bit(0x1, 0x2A); + qspi_send_9bit(0x1, 0x23); + qspi_send_9bit(0x1, 0x35); + qspi_send_9bit(0x1, 0x1E); + qspi_send_9bit(0x1, 0x18); + qspi_send_9bit(0x1, 0x1C); + qspi_send_9bit(0x1, 0x0C); + qspi_send_9bit(0x1, 0x0E); + // FBh RGB + qspi_send_9bit(0x0, 0xFB); + // Red + qspi_send_9bit(0x1, 0x00); + qspi_send_9bit(0x1, 0x0D); + qspi_send_9bit(0x1, 0x09); + qspi_send_9bit(0x1, 0x0C); + qspi_send_9bit(0x1, 0x26); + qspi_send_9bit(0x1, 0x2E); + qspi_send_9bit(0x1, 0x31); + qspi_send_9bit(0x1, 0x22); + qspi_send_9bit(0x1, 0x19); + qspi_send_9bit(0x1, 0x33); + qspi_send_9bit(0x1, 0x22); + qspi_send_9bit(0x1, 0x23); + qspi_send_9bit(0x1, 0x21); + qspi_send_9bit(0x1, 0x17); + qspi_send_9bit(0x1, 0x00); + // Green + qspi_send_9bit(0x1, 0x00); + qspi_send_9bit(0x1, 0x25); + qspi_send_9bit(0x1, 0x1D); + qspi_send_9bit(0x1, 0x1F); + qspi_send_9bit(0x1, 0x35); + qspi_send_9bit(0x1, 0x3C); + qspi_send_9bit(0x1, 0x3A); + qspi_send_9bit(0x1, 0x26); + qspi_send_9bit(0x1, 0x1B); + qspi_send_9bit(0x1, 0x34); + qspi_send_9bit(0x1, 0x23); + qspi_send_9bit(0x1, 0x23); + qspi_send_9bit(0x1, 0x1F); + qspi_send_9bit(0x1, 0x12); + qspi_send_9bit(0x1, 0x00); + // Blue + qspi_send_9bit(0x1, 0x00); + qspi_send_9bit(0x1, 0x3F); + qspi_send_9bit(0x1, 0x31); + qspi_send_9bit(0x1, 0x33); + qspi_send_9bit(0x1, 0x43); + qspi_send_9bit(0x1, 0x48); + qspi_send_9bit(0x1, 0x41); + qspi_send_9bit(0x1, 0x2A); + qspi_send_9bit(0x1, 0x1D); + qspi_send_9bit(0x1, 0x35); + qspi_send_9bit(0x1, 0x23); + qspi_send_9bit(0x1, 0x23); + qspi_send_9bit(0x1, 0x21); + qspi_send_9bit(0x1, 0x10); + qspi_send_9bit(0x1, 0x00); + // F3h Power control + qspi_send_9bit(0x0, 0xF3); + qspi_send_9bit(0x1, 0x00); + qspi_send_9bit(0x1, 0x10); + qspi_send_9bit(0x1, 0x25); + qspi_send_9bit(0x1, 0x01); + qspi_send_9bit(0x1, 0x2D); + qspi_send_9bit(0x1, 0x2D); + qspi_send_9bit(0x1, 0x24); + qspi_send_9bit(0x1, 0x2D); + qspi_send_9bit(0x1, 0x10); + qspi_send_9bit(0x1, 0x10); + qspi_send_9bit(0x1, 0x0A); + qspi_send_9bit(0x1, 0x37); + // F4h VCOM Control + qspi_send_9bit(0x0, 0xF4); + qspi_send_9bit(0x1, 0x88); + qspi_send_9bit(0x1, 0x20); + qspi_send_9bit(0x1, 0x00); + qspi_send_9bit(0x1, 0xAF); + qspi_send_9bit(0x1, 0x64); + qspi_send_9bit(0x1, 0x00); + qspi_send_9bit(0x1, 0xAA); + qspi_send_9bit(0x1, 0x64); + qspi_send_9bit(0x1, 0x00); + qspi_send_9bit(0x1, 0x00); + //Change to level 1 + qspi_send_9bit(0x0, 0xF0); + qspi_send_9bit(0x1, 0x5A); + qspi_send_9bit(0x1, 0x5A); +} + +static int sony_tft_panel_unblank(struct msm_lcdc_panel_ops *ops) +{ + pr_info("%s: +()\n", __func__); + + mutex_lock(&panel_lock); + + if (tft_panel_on) { + pr_info("%s: -() already unblanked\n", __func__); + goto done; + } + + sony_tft_panel_power(1); + msleep(45); + + clk_enable(spi_clk); + qspi_send_9bit(0x0, 0x11); + msleep(5); + qspi_send_9bit(0x0, 0x3a); + if (is_sony_RGB666()) + qspi_send_9bit(0x1, 0x06); + else + qspi_send_9bit(0x1, 0x05); + msleep(100); + qspi_send_9bit(0x0, 0x29); + msleep(20); + + //init gamma setting + if(!is_sony_with_gamma()) + sony_tft_panel_without_gamma_init(); + + /* unlock register page for pwm setting */ + if (is_sony_spi()) { + qspi_send_9bit(0x0, 0xf0); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x0, 0xf1); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x0, 0xd0); + qspi_send_9bit(0x1, 0x5a); + qspi_send_9bit(0x1, 0x5a); + + qspi_send_9bit(0x0, 0xc2); + qspi_send_9bit(0x1, 0x53); + qspi_send_9bit(0x1, 0x12); + } + clk_disable(spi_clk); + msleep(100); + tft_panel_on = 1; + sony_tft_set_pwm_val(last_val); + + pr_info("%s: -()\n", __func__); +done: + mutex_unlock(&panel_lock); + return 0; +} + +static int sony_tft_panel_blank(struct msm_lcdc_panel_ops *ops) +{ + uint8_t data[4] = {0, 0, 0, 0}; + pr_info("%s: +()\n", __func__); + + mutex_lock(&panel_lock); + + clk_enable(spi_clk); + qspi_send_9bit(0x0, 0x28); + qspi_send_9bit(0x0, 0x10); + clk_disable(spi_clk); + + msleep(40); + sony_tft_panel_power(0); + tft_panel_on = 0; + + mutex_unlock(&panel_lock); + + if (!is_sony_spi()) { + data[0] = 5; + data[1] = 0; + data[3] = 1; + microp_i2c_write(0x25, data, 4); + } + + pr_info("%s: -()\n", __func__); + return 0; +} + +static struct msm_lcdc_panel_ops bravo_lcdc_amoled_panel_ops = { + .init = samsung_oled_panel_init, + .blank = samsung_oled_panel_blank, + .unblank = samsung_oled_panel_unblank, +}; + +static struct msm_lcdc_panel_ops bravo_lcdc_tft_panel_ops = { + .init = sony_tft_panel_init, + .blank = sony_tft_panel_blank, + .unblank = sony_tft_panel_unblank, +}; + + +static struct msm_lcdc_timing bravo_lcdc_amoled_timing = { + .clk_rate = 24576000, + .hsync_pulse_width = 4, + .hsync_back_porch = 8, + .hsync_front_porch = 8, + .hsync_skew = 0, + .vsync_pulse_width = 2, + .vsync_back_porch = 8, + .vsync_front_porch = 8, + .vsync_act_low = 1, + .hsync_act_low = 1, + .den_act_low = 1, +}; + +static struct msm_lcdc_timing bravo_lcdc_tft_timing = { + .clk_rate = 24576000, + .hsync_pulse_width = 2, + .hsync_back_porch = 20, + .hsync_front_porch = 20, + .hsync_skew = 0, + .vsync_pulse_width = 2, + .vsync_back_porch = 6, + .vsync_front_porch = 4, + .vsync_act_low = 1, + .hsync_act_low = 1, + .den_act_low = 0, +}; + +static struct msm_fb_data bravo_lcdc_fb_data = { + .xres = 480, + .yres = 800, + .width = 48, + .height = 80, + .output_format = MSM_MDP_OUT_IF_FMT_RGB565, +}; + +static struct msm_lcdc_platform_data bravo_lcdc_amoled_platform_data = { + .panel_ops = &bravo_lcdc_amoled_panel_ops, + .timing = &bravo_lcdc_amoled_timing, + .fb_id = 0, + .fb_data = &bravo_lcdc_fb_data, + .fb_resource = &resources_msm_fb[0], +}; + +static struct msm_lcdc_platform_data bravo_lcdc_tft_platform_data = { + .panel_ops = &bravo_lcdc_tft_panel_ops, + .timing = &bravo_lcdc_tft_timing, + .fb_id = 0, + .fb_data = &bravo_lcdc_fb_data, + .fb_resource = &resources_msm_fb[0], +}; + +static struct platform_device bravo_lcdc_amoled_device = { + .name = "msm_mdp_lcdc", + .id = -1, + .dev = { + .platform_data = &bravo_lcdc_amoled_platform_data, + }, +}; + +static struct platform_device bravo_lcdc_tft_device = { + .name = "msm_mdp_lcdc", + .id = -1, + .dev = { + .platform_data = &bravo_lcdc_tft_platform_data, + }, +}; + +static int bravo_init_spi_hack(void) +{ + int ret; + + spi_base = ioremap(MSM_SPI_PHYS, MSM_SPI_SIZE); + if (!spi_base) + return -1; + + spi_clk = clk_get(&msm_device_spi.dev, "spi_clk"); + if (IS_ERR(spi_clk)) { + pr_err("%s: unable to get spi_clk\n", __func__); + ret = PTR_ERR(spi_clk); + goto err_clk_get; + } + + clk_enable(spi_clk); + + printk("spi: SPI_CONFIG=%x\n", readl(spi_base + SPI_CONFIG)); + printk("spi: SPI_IO_CONTROL=%x\n", readl(spi_base + SPI_IO_CONTROL)); + printk("spi: SPI_OPERATIONAL=%x\n", readl(spi_base + SPI_OPERATIONAL)); + printk("spi: SPI_ERROR_FLAGS_EN=%x\n", + readl(spi_base + SPI_ERROR_FLAGS_EN)); + printk("spi: SPI_ERROR_FLAGS=%x\n", readl(spi_base + SPI_ERROR_FLAGS)); + printk("-%s()\n", __FUNCTION__); + clk_disable(spi_clk); + + return 0; + +err_clk_get: + iounmap(spi_base); + return ret; +} + +static void bravo_brightness_set(struct led_classdev *led_cdev, + enum led_brightness val) +{ + unsigned long flags; + led_cdev->brightness = val; + + spin_lock_irqsave(&brightness_lock, flags); + new_val = val; + spin_unlock_irqrestore(&brightness_lock, flags); + + schedule_work(&brightness_delayed_work); +} + +static void bravo_brightness_amoled_set_work(struct work_struct *work_ptr) +{ + unsigned long flags; + uint8_t val; + + spin_lock_irqsave(&brightness_lock, flags); + val = new_val; + spin_unlock_irqrestore(&brightness_lock, flags); + + mutex_lock(&panel_lock); + samsung_oled_set_gamma_val(val); + mutex_unlock(&panel_lock); +} + +static void bravo_brightness_tft_set_work(struct work_struct *work_ptr) +{ + unsigned long flags; + uint8_t val; + + spin_lock_irqsave(&brightness_lock, flags); + val = new_val; + spin_unlock_irqrestore(&brightness_lock, flags); + + mutex_lock(&panel_lock); + sony_tft_set_pwm_val(val); + mutex_unlock(&panel_lock); +} + +static struct led_classdev bravo_brightness_led = { + .name = "lcd-backlight", + .brightness = LED_FULL, + .brightness_set = bravo_brightness_set, +}; + +int __init bravo_init_panel(void) +{ + int ret; + + if (!machine_is_bravo() && !machine_is_bravoc()) + return 0; + + if (system_rev > 0xC0) { + /* CDMA version (except for EVT1) supports RGB666 */ + init_tablep = samsung_oled_rgb666_init_table; + init_table_sz = ARRAY_SIZE(samsung_oled_rgb666_init_table); + bravo_lcdc_fb_data.output_format = MSM_MDP_OUT_IF_FMT_RGB666; + } + + ret = platform_device_register(&msm_device_mdp); + if (ret != 0) + return ret; + + ret = bravo_init_spi_hack(); + if (ret != 0) + return ret; + + if (gpio_get_value(BRAVO_GPIO_LCD_ID0)) { + pr_info("%s: tft panel\n", __func__); + vreg_lcm_rftx_2v6 = vreg_get(0, "rftx"); + if (IS_ERR(vreg_lcm_rftx_2v6)) + return PTR_ERR(vreg_lcm_rftx_2v6); + vreg_set_level(vreg_lcm_rftx_2v6, 2600); + +#ifdef CONFIG_MACH_BRAVO + vreg_lcm_aux_2v6 = vreg_get(0, "gp4"); +#else + vreg_lcm_aux_2v6 = vreg_get(0, "gp6"); +#endif + if (IS_ERR(vreg_lcm_aux_2v6)) + return PTR_ERR(vreg_lcm_aux_2v6); + vreg_set_level(vreg_lcm_aux_2v6, 2600); + + if (gpio_get_value(BRAVO_GPIO_LCD_RST_N)) + tft_panel_on = 1; + ret = platform_device_register(&bravo_lcdc_tft_device); + INIT_WORK(&brightness_delayed_work, bravo_brightness_tft_set_work); + } else { + pr_info("%s: amoled panel\n", __func__); + ret = platform_device_register(&bravo_lcdc_amoled_device); + INIT_WORK(&brightness_delayed_work, bravo_brightness_amoled_set_work); + } + + if (ret != 0) + return ret; + + ret = led_classdev_register(NULL, &bravo_brightness_led); + if (ret != 0) { + pr_err("%s: Cannot register brightness led\n", __func__); + return ret; + } + + return 0; +} + +device_initcall(bravo_init_panel); diff --git a/arch/arm/mach-msm/board-bravo-rfkill.c b/arch/arm/mach-msm/board-bravo-rfkill.c new file mode 100644 index 0000000000000..f8f24f4d11f27 --- /dev/null +++ b/arch/arm/mach-msm/board-bravo-rfkill.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation. + * + * 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 "board-bravo.h" + +static struct rfkill *bt_rfk; +static const char bt_name[] = "bcm4329"; + +static int bluetooth_set_power(void *data, bool blocked) +{ + if (!blocked) { + gpio_direction_output(BRAVO_GPIO_BT_RESET_N, 1); + gpio_direction_output(BRAVO_GPIO_BT_SHUTDOWN_N, 1); + } else { + gpio_direction_output(BRAVO_GPIO_BT_SHUTDOWN_N, 0); + gpio_direction_output(BRAVO_GPIO_BT_RESET_N, 0); + } + return 0; +} + +static struct rfkill_ops bravo_rfkill_ops = { + .set_block = bluetooth_set_power, +}; + +static int bravo_rfkill_probe(struct platform_device *pdev) +{ + int rc = 0; + bool default_state = true; /* off */ + + rc = gpio_request(BRAVO_GPIO_BT_RESET_N, "bt_reset"); + if (rc) + goto err_gpio_reset; + rc = gpio_request(BRAVO_GPIO_BT_SHUTDOWN_N, "bt_shutdown"); + if (rc) + goto err_gpio_shutdown; + + bluetooth_set_power(NULL, default_state); + + bt_rfk = rfkill_alloc(bt_name, &pdev->dev, RFKILL_TYPE_BLUETOOTH, + &bravo_rfkill_ops, NULL); + if (!bt_rfk) { + rc = -ENOMEM; + goto err_rfkill_alloc; + } + + rfkill_set_states(bt_rfk, default_state, false); + + /* userspace cannot take exclusive control */ + + rc = rfkill_register(bt_rfk); + if (rc) + goto err_rfkill_reg; + + return 0; + +err_rfkill_reg: + rfkill_destroy(bt_rfk); +err_rfkill_alloc: + gpio_free(BRAVO_GPIO_BT_SHUTDOWN_N); +err_gpio_shutdown: + gpio_free(BRAVO_GPIO_BT_RESET_N); +err_gpio_reset: + return rc; +} + +static int bravo_rfkill_remove(struct platform_device *dev) +{ + rfkill_unregister(bt_rfk); + rfkill_destroy(bt_rfk); + gpio_free(BRAVO_GPIO_BT_SHUTDOWN_N); + gpio_free(BRAVO_GPIO_BT_RESET_N); + + return 0; +} + +static struct platform_driver bravo_rfkill_driver = { + .probe = bravo_rfkill_probe, + .remove = bravo_rfkill_remove, + .driver = { + .name = "bravo_rfkill", + .owner = THIS_MODULE, + }, +}; + +static int __init bravo_rfkill_init(void) +{ + if (!machine_is_bravo() && !machine_is_bravoc()) + return 0; + + return platform_driver_register(&bravo_rfkill_driver); +} + +static void __exit bravo_rfkill_exit(void) +{ + platform_driver_unregister(&bravo_rfkill_driver); +} + +module_init(bravo_rfkill_init); +module_exit(bravo_rfkill_exit); +MODULE_DESCRIPTION("bravo rfkill"); +MODULE_AUTHOR("Nick Pelly "); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-bravo-smb329.c b/arch/arm/mach-msm/board-bravo-smb329.c new file mode 100755 index 0000000000000..9d51005e2bce1 --- /dev/null +++ b/arch/arm/mach-msm/board-bravo-smb329.c @@ -0,0 +1,177 @@ +/* drivers/i2c/chips/smb329.c + * + * SMB329B Switch Charger (SUMMIT Microelectronics) + * + * Copyright (C) 2009 HTC Corporation + * Author: Justin Lin + * + * 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 "board-bravo-smb329.h" + +static struct smb329_data { + struct i2c_client *client; + uint8_t version; + struct work_struct work; + struct mutex state_lock; + int chg_state; +} smb329; + +static int smb329_i2c_write(uint8_t *value, uint8_t reg, uint8_t num_bytes) +{ + int ret; + struct i2c_msg msg; + + /* write the first byte of buffer as the register address */ + value[0] = reg; + msg.addr = smb329.client->addr; + msg.len = num_bytes + 1; + msg.flags = 0; + msg.buf = value; + + ret = i2c_transfer(smb329.client->adapter, &msg, 1); + + return (ret >= 0) ? 0 : ret; +} + +static int smb329_i2c_read(uint8_t *value, uint8_t reg, uint8_t num_bytes) +{ + int ret; + struct i2c_msg msg[2]; + + /* setup the address to read */ + msg[0].addr = smb329.client->addr; + msg[0].len = 1; + msg[0].flags = 0; + msg[0].buf = ® + + /* setup the read buffer */ + msg[1].addr = smb329.client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = num_bytes; + msg[1].buf = value; + + ret = i2c_transfer(smb329.client->adapter, msg, 2); + + return (ret >= 0) ? 0 : ret; +} + +static int smb329_i2c_write_byte(uint8_t value, uint8_t reg) +{ + int ret; + uint8_t buf[2] = { 0 }; + + buf[1] = value; + ret = smb329_i2c_write(buf, reg, 1); + if (ret) + pr_err("smb329: write byte error (%d)\n", ret); + + return ret; +} + +static int smb329_i2c_read_byte(uint8_t *value, uint8_t reg) +{ + int ret = smb329_i2c_read(value, reg, 1); + if (ret) + pr_err("smb329: read byte error (%d)\n", ret); + + return ret; +} + +int smb329_set_charger_ctrl(uint32_t ctl) +{ + mutex_lock(&smb329.state_lock); + smb329.chg_state = ctl; + schedule_work(&smb329.work); + mutex_unlock(&smb329.state_lock); + return 0; +} + +static void smb329_work_func(struct work_struct *work) +{ + mutex_lock(&smb329.state_lock); + + switch (smb329.chg_state) { + case SMB329_ENABLE_FAST_CHG: + pr_info("smb329: charger on (fast)\n"); + smb329_i2c_write_byte(0x84, 0x31); + smb329_i2c_write_byte(0x08, 0x05); + if ((smb329.version & 0x18) == 0x0) + smb329_i2c_write_byte(0xA9, 0x00); + break; + + case SMB329_DISABLE_CHG: + case SMB329_ENABLE_SLOW_CHG: + pr_info("smb329: charger off/slow\n"); + smb329_i2c_write_byte(0x88, 0x31); + smb329_i2c_write_byte(0x08, 0x05); + break; + default: + pr_err("smb329: unknown charger state %d\n", + smb329.chg_state); + } + + mutex_unlock(&smb329.state_lock); +} + +static int smb329_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { + dev_dbg(&client->dev, "[SMB329]:I2C fail\n"); + return -EIO; + } + + smb329.client = client; + mutex_init(&smb329.state_lock); + INIT_WORK(&smb329.work, smb329_work_func); + + smb329_i2c_read_byte(&smb329.version, 0x3B); + pr_info("smb329 version: 0x%02x\n", smb329.version); + + return 0; +} + +static const struct i2c_device_id smb329_id[] = { + { "smb329", 0 }, + { }, +}; + +static struct i2c_driver smb329_driver = { + .driver.name = "smb329", + .id_table = smb329_id, + .probe = smb329_probe, +}; + +static int __init smb329_init(void) +{ + int ret = i2c_add_driver(&smb329_driver); + if (ret) + pr_err("smb329_init: failed\n"); + + return ret; +} + +module_init(smb329_init); + +MODULE_AUTHOR("Justin Lin "); +MODULE_DESCRIPTION("SUMMIT Microelectronics SMB329B switch charger"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-bravo-smb329.h b/arch/arm/mach-msm/board-bravo-smb329.h new file mode 100644 index 0000000000000..13b326fa71dfa --- /dev/null +++ b/arch/arm/mach-msm/board-bravo-smb329.h @@ -0,0 +1,32 @@ +/* include/linux/smb329.h - smb329 switch charger driver + * + * Copyright (C) 2009 HTC Corporation. + * + * 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_SMB329_H +#define _LINUX_SMB329_H + +#ifdef __KERNEL__ + +enum { + SMB329_DISABLE_CHG, + SMB329_ENABLE_SLOW_CHG, + SMB329_ENABLE_FAST_CHG, +}; + +extern int smb329_set_charger_ctrl(uint32_t ctl); + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_SMB329_H */ + diff --git a/arch/arm/mach-msm/board-bravo-tpa2018d1.c b/arch/arm/mach-msm/board-bravo-tpa2018d1.c new file mode 100644 index 0000000000000..afabb7966aecf --- /dev/null +++ b/arch/arm/mach-msm/board-bravo-tpa2018d1.c @@ -0,0 +1,368 @@ +/* drivers/i2c/chips/tpa2018d1.c + * + * TI TPA2018D1 Speaker Amplifier + * + * Copyright (C) 2009 HTC Corporation + * + * 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. + * + */ + +/* TODO: content validation in TPA2018_SET_CONFIG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-bravo-tpa2018d1.h" + +static struct i2c_client *this_client; +static struct tpa2018d1_platform_data *pdata; +static int is_on; +static char spk_amp_cfg[8]; +static const char spk_amp_on[8] = { /* same length as spk_amp_cfg */ + 0x01, 0xc3, 0x20, 0x01, 0x00, 0x08, 0x1a, 0x21 +}; +static const char spk_amp_off[] = {0x01, 0xa2}; + +static DEFINE_MUTEX(spk_amp_lock); +static int tpa2018d1_opened; +static char *config_data; +static int tpa2018d1_num_modes; + +#define DEBUG 0 + +static int tpa2018_i2c_write(const char *txData, int length) +{ + struct i2c_msg msg[] = { + { + .addr = this_client->addr, + .flags = 0, + .len = length, + .buf = txData, + }, + }; + + if (i2c_transfer(this_client->adapter, msg, 1) < 0) { + pr_err("%s: I2C transfer error\n", __func__); + return -EIO; + } else + return 0; +} + +static int tpa2018_i2c_read(char *rxData, int length) +{ + struct i2c_msg msgs[] = { + { + .addr = this_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = rxData, + }, + }; + + if (i2c_transfer(this_client->adapter, msgs, 1) < 0) { + pr_err("%s: I2C transfer error\n", __func__); + return -EIO; + } + +#if DEBUG + do { + int i = 0; + for (i = 0; i < length; i++) + pr_info("%s: rx[%d] = %2x\n", + __func__, i, rxData[i]); + } while(0); +#endif + + return 0; +} + +static int tpa2018d1_open(struct inode *inode, struct file *file) +{ + int rc = 0; + + mutex_lock(&spk_amp_lock); + + if (tpa2018d1_opened) { + pr_err("%s: busy\n", __func__); + rc = -EBUSY; + goto done; + } + + tpa2018d1_opened = 1; +done: + mutex_unlock(&spk_amp_lock); + return rc; +} + +static int tpa2018d1_release(struct inode *inode, struct file *file) +{ + mutex_lock(&spk_amp_lock); + tpa2018d1_opened = 0; + mutex_unlock(&spk_amp_lock); + + return 0; +} + +static int tpa2018d1_read_config(void __user *argp) +{ + int rc = 0; + unsigned char reg_idx = 0x01; + unsigned char tmp[7]; + + if (!is_on) { + gpio_set_value(pdata->gpio_tpa2018_spk_en, 1); + msleep(5); /* According to TPA2018D1 Spec */ + } + + rc = tpa2018_i2c_write(®_idx, sizeof(reg_idx)); + if (rc < 0) + goto err; + + rc = tpa2018_i2c_read(tmp, sizeof(tmp)); + if (rc < 0) + goto err; + + if (copy_to_user(argp, &tmp, sizeof(tmp))) + rc = -EFAULT; + +err: + if (!is_on) + gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); + return rc; +} + +static long tpa2018d1_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int rc = 0; + int mode = -1; + int offset = 0; + struct tpa2018d1_config_data cfg; + + mutex_lock(&spk_amp_lock); + + switch (cmd) { + case TPA2018_SET_CONFIG: + if (copy_from_user(spk_amp_cfg, argp, sizeof(spk_amp_cfg))) + rc = -EFAULT; + break; + + case TPA2018_READ_CONFIG: + rc = tpa2018d1_read_config(argp); + break; + + case TPA2018_SET_MODE: + if (copy_from_user(&mode, argp, sizeof(mode))) { + rc = -EFAULT; + break; + } + if (mode >= tpa2018d1_num_modes || mode < 0) { + pr_err("%s: unsupported tpa2018d1 mode %d\n", + __func__, mode); + rc = -EINVAL; + break; + } + if (!config_data) { + pr_err("%s: no config data!\n", __func__); + rc = -EIO; + break; + } + memcpy(spk_amp_cfg, config_data + mode * TPA2018D1_CMD_LEN, + TPA2018D1_CMD_LEN); + break; + + case TPA2018_SET_PARAM: + if (copy_from_user(&cfg, argp, sizeof(cfg))) { + pr_err("%s: copy from user failed.\n", __func__); + rc = -EFAULT; + break; + } + tpa2018d1_num_modes = cfg.mode_num; + if (tpa2018d1_num_modes > TPA2018_NUM_MODES) { + pr_err("%s: invalid number of modes %d\n", __func__, + tpa2018d1_num_modes); + rc = -EINVAL; + break; + } + if (cfg.data_len != tpa2018d1_num_modes*TPA2018D1_CMD_LEN) { + pr_err("%s: invalid data length %d, expecting %d\n", + __func__, cfg.data_len, + tpa2018d1_num_modes * TPA2018D1_CMD_LEN); + rc = -EINVAL; + break; + } + /* Free the old data */ + if (config_data) + kfree(config_data); + config_data = kmalloc(cfg.data_len, GFP_KERNEL); + if (!config_data) { + pr_err("%s: out of memory\n", __func__); + rc = -ENOMEM; + break; + } + if (copy_from_user(config_data, cfg.cmd_data, cfg.data_len)) { + pr_err("%s: copy data from user failed.\n", __func__); + kfree(config_data); + config_data = NULL; + rc = -EFAULT; + break; + } + /* replace default setting with playback setting */ + if (tpa2018d1_num_modes >= TPA2018_MODE_PLAYBACK) { + offset = TPA2018_MODE_PLAYBACK * TPA2018D1_CMD_LEN; + memcpy(spk_amp_cfg, config_data + offset, + TPA2018D1_CMD_LEN); + } + break; + + default: + pr_err("%s: invalid command %d\n", __func__, _IOC_NR(cmd)); + rc = -EINVAL; + break; + } + mutex_unlock(&spk_amp_lock); + return rc; +} + +static struct file_operations tpa2018d1_fops = { + .owner = THIS_MODULE, + .open = tpa2018d1_open, + .release = tpa2018d1_release, + .unlocked_ioctl = tpa2018d1_ioctl, +}; + +static struct miscdevice tpa2018d1_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "tpa2018d1", + .fops = &tpa2018d1_fops, +}; + +void tpa2018d1_set_speaker_amp(int on) +{ + if (!pdata) { + pr_err("%s: no platform data!\n", __func__); + return; + } + mutex_lock(&spk_amp_lock); + if (on && !is_on) { + gpio_set_value(pdata->gpio_tpa2018_spk_en, 1); + msleep(5); /* According to TPA2018D1 Spec */ + + if (tpa2018_i2c_write(spk_amp_cfg, sizeof(spk_amp_cfg)) == 0) { + is_on = 1; + pr_info("%s: ON\n", __func__); + } + } else if (!on && is_on) { + if (tpa2018_i2c_write(spk_amp_off, sizeof(spk_amp_off)) == 0) { + is_on = 0; + msleep(2); + gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); + pr_info("%s: OFF\n", __func__); + } + } + mutex_unlock(&spk_amp_lock); +} + +static int tpa2018d1_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + + pdata = client->dev.platform_data; + + if (!pdata) { + ret = -EINVAL; + pr_err("%s: platform data is NULL\n", __func__); + goto err_no_pdata; + } + + this_client = client; + + ret = gpio_request(pdata->gpio_tpa2018_spk_en, "tpa2018"); + if (ret < 0) { + pr_err("%s: gpio request aud_spk_en pin failed\n", __func__); + goto err_free_gpio; + } + + ret = gpio_direction_output(pdata->gpio_tpa2018_spk_en, 1); + if (ret < 0) { + pr_err("%s: request aud_spk_en gpio direction failed\n", + __func__); + goto err_free_gpio; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s: i2c check functionality error\n", __func__); + ret = -ENODEV; + goto err_free_gpio; + } + + gpio_set_value(pdata->gpio_tpa2018_spk_en, 0); /* Default Low */ + + ret = misc_register(&tpa2018d1_device); + if (ret) { + pr_err("%s: tpa2018d1_device register failed\n", __func__); + goto err_free_gpio; + } + memcpy(spk_amp_cfg, spk_amp_on, sizeof(spk_amp_on)); + return 0; + +err_free_gpio: + gpio_free(pdata->gpio_tpa2018_spk_en); +err_no_pdata: + return ret; +} + +static int tpa2018d1_suspend(struct i2c_client *client, pm_message_t mesg) +{ + return 0; +} + +static int tpa2018d1_resume(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id tpa2018d1_id[] = { + { TPA2018D1_I2C_NAME, 0 }, + { } +}; + +static struct i2c_driver tpa2018d1_driver = { + .probe = tpa2018d1_probe, + .suspend = tpa2018d1_suspend, + .resume = tpa2018d1_resume, + .id_table = tpa2018d1_id, + .driver = { + .name = TPA2018D1_I2C_NAME, + }, +}; + +static int __init tpa2018d1_init(void) +{ + pr_info("%s\n", __func__); + return i2c_add_driver(&tpa2018d1_driver); +} + +module_init(tpa2018d1_init); + +MODULE_DESCRIPTION("tpa2018d1 speaker amp driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-bravo-tpa2018d1.h b/arch/arm/mach-msm/board-bravo-tpa2018d1.h new file mode 100644 index 0000000000000..dc11012209454 --- /dev/null +++ b/arch/arm/mach-msm/board-bravo-tpa2018d1.h @@ -0,0 +1,35 @@ +/* include/linux/tpa2018d1.h - tpa2018d1 speaker amplifier driver + * + * Copyright (C) 2009 HTC Corporation. + * + * 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 __ASM_ARM_ARCH_TPA2018D1_H +#define __ASM_ARM_ARCH_TPA2018D1_H + +#define TPA2018D1_I2C_NAME "tpa2018d1" +#define TPA2018D1_CMD_LEN 8 + +struct tpa2018d1_platform_data { + uint32_t gpio_tpa2018_spk_en; +}; + +struct tpa2018d1_config_data { + unsigned char *cmd_data; /* [mode][cmd_len][cmds..] */ + unsigned int mode_num; + unsigned int data_len; +}; + +extern void tpa2018d1_set_speaker_amp(int on); + +#endif /* __ASM_ARM_ARCH_TPA2018D1_H */ diff --git a/arch/arm/mach-msm/board-bravo-wifi.c b/arch/arm/mach-msm/board-bravo-wifi.c new file mode 100644 index 0000000000000..5b08341120ac6 --- /dev/null +++ b/arch/arm/mach-msm/board-bravo-wifi.c @@ -0,0 +1,146 @@ +/* linux/arch/arm/mach-msm/board-bravo-wifi.c +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board-bravo.h" + +int bravo_wifi_power(int on); +int bravo_wifi_reset(int on); +int bravo_wifi_set_carddetect(int on); + +#define PREALLOC_WLAN_NUMBER_OF_SECTIONS 4 +#define PREALLOC_WLAN_NUMBER_OF_BUFFERS 160 +#define PREALLOC_WLAN_SECTION_HEADER 24 + +#define WLAN_SECTION_SIZE_0 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128) +#define WLAN_SECTION_SIZE_1 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128) +#define WLAN_SECTION_SIZE_2 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 512) +#define WLAN_SECTION_SIZE_3 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 1024) + +#define WLAN_SKB_BUF_NUM 16 + +static struct sk_buff *wlan_static_skb[WLAN_SKB_BUF_NUM]; + +typedef struct wifi_mem_prealloc_struct { + void *mem_ptr; + unsigned long size; +} wifi_mem_prealloc_t; + +static wifi_mem_prealloc_t wifi_mem_array[PREALLOC_WLAN_NUMBER_OF_SECTIONS] = { + { NULL, (WLAN_SECTION_SIZE_0 + PREALLOC_WLAN_SECTION_HEADER) }, + { NULL, (WLAN_SECTION_SIZE_1 + PREALLOC_WLAN_SECTION_HEADER) }, + { NULL, (WLAN_SECTION_SIZE_2 + PREALLOC_WLAN_SECTION_HEADER) }, + { NULL, (WLAN_SECTION_SIZE_3 + PREALLOC_WLAN_SECTION_HEADER) } +}; + +static void *bravo_wifi_mem_prealloc(int section, unsigned long size) +{ + if (section == PREALLOC_WLAN_NUMBER_OF_SECTIONS) + return wlan_static_skb; + if ((section < 0) || (section > PREALLOC_WLAN_NUMBER_OF_SECTIONS)) + return NULL; + if (wifi_mem_array[section].size < size) + return NULL; + return wifi_mem_array[section].mem_ptr; +} + +int __init bravo_init_wifi_mem(void) +{ + int i; + + for(i=0;( i < WLAN_SKB_BUF_NUM );i++) { + if (i < (WLAN_SKB_BUF_NUM/2)) + wlan_static_skb[i] = dev_alloc_skb(4096); + else + wlan_static_skb[i] = dev_alloc_skb(8192); + } + for(i=0;( i < PREALLOC_WLAN_NUMBER_OF_SECTIONS );i++) { + wifi_mem_array[i].mem_ptr = kmalloc(wifi_mem_array[i].size, + GFP_KERNEL); + if (wifi_mem_array[i].mem_ptr == NULL) + return -ENOMEM; + } + return 0; +} + +static struct resource bravo_wifi_resources[] = { + [0] = { + .name = "bcm4329_wlan_irq", + .start = MSM_GPIO_TO_INT(BRAVO_GPIO_WIFI_IRQ), + .end = MSM_GPIO_TO_INT(BRAVO_GPIO_WIFI_IRQ), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE, + }, +}; + +static struct wifi_platform_data bravo_wifi_control = { + .set_power = bravo_wifi_power, + .set_reset = bravo_wifi_reset, + .set_carddetect = bravo_wifi_set_carddetect, + .mem_prealloc = bravo_wifi_mem_prealloc, +}; + +static struct platform_device bravo_wifi_device = { + .name = "bcm4329_wlan", + .id = 1, + .num_resources = ARRAY_SIZE(bravo_wifi_resources), + .resource = bravo_wifi_resources, + .dev = { + .platform_data = &bravo_wifi_control, + }, +}; + +extern unsigned char *get_wifi_nvs_ram(void); +extern int wifi_calibration_size_set(void); + +static unsigned bravo_wifi_update_nvs(char *str, int add_flag) +{ +#define NVS_LEN_OFFSET 0x0C +#define NVS_DATA_OFFSET 0x40 + unsigned char *ptr; + unsigned len; + + if (!str) + return -EINVAL; + ptr = get_wifi_nvs_ram(); + /* Size in format LE assumed */ + memcpy(&len, ptr + NVS_LEN_OFFSET, sizeof(len)); + /* if the last byte in NVRAM is 0, trim it */ + if (ptr[NVS_DATA_OFFSET + len - 1] == 0) + len -= 1; + if (add_flag) { + strcpy(ptr + NVS_DATA_OFFSET + len, str); + len += strlen(str); + } else { + if (strnstr(ptr + NVS_DATA_OFFSET, str, len)) + len -= strlen(str); + } + memcpy(ptr + NVS_LEN_OFFSET, &len, sizeof(len)); + wifi_calibration_size_set(); + return 0; +} + +static int __init bravo_wifi_init(void) +{ + int ret; + + if (!machine_is_bravo() && !machine_is_bravoc()) + return 0; + + printk("%s: start\n", __func__); + bravo_wifi_update_nvs("sd_oobonly=1\r\n", 0); + bravo_wifi_update_nvs("btc_params70=0x32\r\n", 1); + bravo_init_wifi_mem(); + ret = platform_device_register(&bravo_wifi_device); + return ret; +} + +late_initcall(bravo_wifi_init); diff --git a/arch/arm/mach-msm/board-bravo.c b/arch/arm/mach-msm/board-bravo.c new file mode 100644 index 0000000000000..7d0e7314c3a0c --- /dev/null +++ b/arch/arm/mach-msm/board-bravo.c @@ -0,0 +1,1284 @@ +/* arch/arm/mach-msm/board-bravo.c + * + * Copyright (C) 2009 Google, Inc. + * Copyright (C) 2009 HTC Corporation. + * Author: Dima Zavin + * Copyright (C) 2010 Giulio Cervera + * Copyright (C) 2010 Diogo Ferreira + * + * 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 <../../../drivers/staging/android/timed_gpio.h> +#include <../../../drivers/w1/w1.h> +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PERFLOCK +#include +#endif +#include +#include + +#include "board-bravo.h" +#include "devices.h" +#include "proc_comm.h" +#include "board-bravo-tpa2018d1.h" +#include "board-bravo-smb329.h" + +#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL +#include +#endif + +static uint debug_uart; + +module_param_named(debug_uart, debug_uart, uint, 0); + +extern void notify_usb_connected(int); +extern void msm_init_pmic_vibrator(void); +extern void __init bravo_audio_init(void); + +extern int microp_headset_has_mic(void); + +static int bravo_phy_init_seq[] = { + 0x0C, 0x31, + 0x31, 0x32, + 0x1D, 0x0D, + 0x1D, 0x10, + -1 +}; + +static void bravo_usb_phy_reset(void) +{ + u32 id; + int ret; + + id = PCOM_CLKRGM_APPS_RESET_USB_PHY; + ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_ASSERT, &id, NULL); + if (ret) { + pr_err("%s: Cannot assert (%d)\n", __func__, ret); + return; + } + + msleep(1); + + id = PCOM_CLKRGM_APPS_RESET_USB_PHY; + ret = msm_proc_comm(PCOM_CLK_REGIME_SEC_RESET_DEASSERT, &id, NULL); + if (ret) { + pr_err("%s: Cannot assert (%d)\n", __func__, ret); + return; + } +} + +static void bravo_usb_hw_reset(bool enable) +{ + u32 id; + int ret; + u32 func; + + id = PCOM_CLKRGM_APPS_RESET_USBH; + if (enable) + func = PCOM_CLK_REGIME_SEC_RESET_ASSERT; + else + func = PCOM_CLK_REGIME_SEC_RESET_DEASSERT; + ret = msm_proc_comm(func, &id, NULL); + if (ret) + pr_err("%s: Cannot set reset to %d (%d)\n", __func__, enable, + ret); +} + +static struct msm_hsusb_platform_data msm_hsusb_pdata = { + .phy_init_seq = bravo_phy_init_seq, + .phy_reset = bravo_usb_phy_reset, + .hw_reset = bravo_usb_hw_reset, + .usb_connected = notify_usb_connected, +}; + +static char *usb_functions_ums[] = { + "usb_mass_storage", +}; + +static char *usb_functions_ums_adb[] = { + "usb_mass_storage", + "adb", +}; + +static char *usb_functions_rndis[] = { + "rndis", +}; + +static char *usb_functions_rndis_adb[] = { + "rndis", + "adb", +}; + +#ifdef CONFIG_USB_ANDROID_ACCESSORY +static char *usb_functions_accessory[] = { "accessory" }; +static char *usb_functions_accessory_adb[] = { "accessory", "adb" }; +#endif + +#ifdef CONFIG_USB_ANDROID_DIAG +static char *usb_functions_adb_diag[] = { + "usb_mass_storage", + "adb", + "diag", +}; +#endif + +static char *usb_functions_all[] = { +#ifdef CONFIG_USB_ANDROID_RNDIS + "rndis", +#endif +#ifdef CONFIG_USB_ANDROID_ACCESSORY + "accessory", +#endif + "usb_mass_storage", + "adb", +#ifdef CONFIG_USB_ANDROID_ACM + "acm", +#endif +#ifdef CONFIG_USB_ANDROID_DIAG + "diag", +#endif +}; + +static struct android_usb_product usb_products[] = { + { + .product_id = 0x0ff9, + .num_functions = ARRAY_SIZE(usb_functions_ums), + .functions = usb_functions_ums, + }, + { + .product_id = 0x0c87, + .num_functions = ARRAY_SIZE(usb_functions_ums_adb), + .functions = usb_functions_ums_adb, + }, + { + .product_id = 0x0FFE, + .num_functions = ARRAY_SIZE(usb_functions_rndis), + .functions = usb_functions_rndis, + }, + /* + XXX: there isn't a equivalent in htc's kernel + { + .product_id = 0x4e14, + .num_functions = ARRAY_SIZE(usb_functions_rndis_adb), + .functions = usb_functions_rndis_adb, + }, */ +#ifdef CONFIG_USB_ANDROID_ACCESSORY + { + .vendor_id = USB_ACCESSORY_VENDOR_ID, + .product_id = USB_ACCESSORY_PRODUCT_ID, + .num_functions = ARRAY_SIZE(usb_functions_accessory), + .functions = usb_functions_accessory, + }, + { + .vendor_id = USB_ACCESSORY_VENDOR_ID, + .product_id = USB_ACCESSORY_ADB_PRODUCT_ID, + .num_functions = ARRAY_SIZE(usb_functions_accessory_adb), + .functions = usb_functions_accessory_adb, + }, +#endif +#ifdef CONFIG_USB_ANDROID_DIAG + { + .product_id = 0x0c07, + .num_functions = ARRAY_SIZE(usb_functions_adb_diag), + .functions = usb_functions_adb_diag, + }, +#endif +}; + +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .vendor = "HTC", + .product = "Desire", + .release = 0x0100, +}; + +static struct platform_device usb_mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &mass_storage_pdata, + }, +}; + +#ifdef CONFIG_USB_ANDROID_RNDIS +static struct usb_ether_platform_data rndis_pdata = { + /* ethaddr is filled by board_serialno_setup */ + .vendorID = 0x0bb4, + .vendorDescr = "HTC", +}; + +static struct platform_device rndis_device = { + .name = "rndis", + .id = -1, + .dev = { + .platform_data = &rndis_pdata, + }, +}; +#endif + +static struct android_usb_platform_data android_usb_pdata = { + .vendor_id = 0x0bb4, + .product_id = 0x0c02, + .version = 0x0100, + .product_name = "Android Phone", + .manufacturer_name = "HTC", + .num_products = ARRAY_SIZE(usb_products), + .products = usb_products, + .num_functions = ARRAY_SIZE(usb_functions_all), + .functions = usb_functions_all, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; + +static struct platform_device bravo_rfkill = { + .name = "bravo_rfkill", + .id = -1, +}; + +static struct resource msm_kgsl_resources[] = { + { + .name = "kgsl_reg_memory", + .start = MSM_GPU_REG_PHYS, + .end = MSM_GPU_REG_PHYS + MSM_GPU_REG_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .name = "kgsl_phys_memory", + .start = MSM_GPU_MEM_BASE, + .end = MSM_GPU_MEM_BASE + MSM_GPU_MEM_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_GRAPHICS, + .end = INT_GRAPHICS, + .flags = IORESOURCE_IRQ, + }, +}; + +#define PWR_RAIL_GRP_CLK 8 +static int bravo_kgsl_power_rail_mode(int follow_clk) +{ + int mode = follow_clk ? 0 : 1; + int rail_id = PWR_RAIL_GRP_CLK; + + return msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &rail_id, &mode); +} + +static int bravo_kgsl_power(bool on) +{ + int cmd; + int rail_id = PWR_RAIL_GRP_CLK; + + cmd = on ? PCOM_CLKCTL_RPC_RAIL_ENABLE : PCOM_CLKCTL_RPC_RAIL_DISABLE; + return msm_proc_comm(cmd, &rail_id, NULL); +} + +static struct platform_device msm_kgsl_device = { + .name = "kgsl", + .id = -1, + .resource = msm_kgsl_resources, + .num_resources = ARRAY_SIZE(msm_kgsl_resources), +}; + +static struct android_pmem_platform_data mdp_pmem_pdata = { + .name = "pmem", + .start = MSM_PMEM_MDP_BASE, + .size = MSM_PMEM_MDP_SIZE, + .no_allocator = 0, + .cached = 1, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .start = MSM_PMEM_ADSP_BASE, + .size = MSM_PMEM_ADSP_SIZE, + .no_allocator = 0, + .cached = 1, +}; + +static struct android_pmem_platform_data android_pmem_venc_pdata = { + .name = "pmem_venc", + .start = MSM_PMEM_VENC_BASE, + .size = MSM_PMEM_VENC_SIZE, + .no_allocator = 0, + .cached = 1, +}; + +static struct platform_device android_pmem_mdp_device = { + .name = "android_pmem", + .id = 0, + .dev = { + .platform_data = &mdp_pmem_pdata + }, +}; + +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 1, + .dev = { + .platform_data = &android_pmem_adsp_pdata, + }, +}; + +static struct platform_device android_pmem_venc_device = { + .name = "android_pmem", + .id = 3, + .dev = { + .platform_data = &android_pmem_venc_pdata, + }, +}; + +static struct resource ram_console_resources[] = { + { + .start = MSM_RAM_CONSOLE_BASE, + .end = MSM_RAM_CONSOLE_BASE + MSM_RAM_CONSOLE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device ram_console_device = { + .name = "ram_console", + .id = -1, + .num_resources = ARRAY_SIZE(ram_console_resources), + .resource = ram_console_resources, +}; + +static int bravo_ts_power(int on) +{ + pr_info("%s: power %d\n", __func__, on); + + if (on) { + /* level shifter should be off */ + gpio_set_value(BRAVO_GPIO_TP_EN, 1); + msleep(120); + /* enable touch panel level shift */ + gpio_set_value(BRAVO_GPIO_TP_LS_EN, 1); + msleep(3); + } else { + gpio_set_value(BRAVO_GPIO_TP_LS_EN, 0); + gpio_set_value(BRAVO_GPIO_TP_EN, 0); + udelay(50); + } + + return 0; +} + +static struct synaptics_i2c_rmi_platform_data bravo_synaptics_ts_data[] = { + { + .version = 0x100, + .power = bravo_ts_power, + .flags = SYNAPTICS_FLIP_Y | SYNAPTICS_SNAP_TO_INACTIVE_EDGE, + .inactive_left = -1 * 0x10000 / 480, + .inactive_right = -1 * 0x10000 / 480, + .inactive_top = -5 * 0x10000 / 800, + .inactive_bottom = -5 * 0x10000 / 800, + .sensitivity_adjust = 12, + } +}; + +static struct akm8973_platform_data compass_platform_data = { + .layouts = BRAVO_LAYOUTS, + .project_name = BRAVO_PROJECT_NAME, + .reset = BRAVO_GPIO_COMPASS_RST_N, + .intr = BRAVO_GPIO_COMPASS_INT_N, +}; + +static struct regulator_consumer_supply tps65023_dcdc1_supplies[] = { + { + .supply = "acpu_vcore", + }, +}; + +static struct regulator_init_data tps65023_data[5] = { + { + .constraints = { + .name = "dcdc1", /* VREG_MSMC2_1V29 */ + .min_uV = 975000, +#ifdef CONFIG_JESUS_PHONE + .max_uV = 1350000, +#else + .max_uV = 1275000, +#endif + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + }, + .consumer_supplies = tps65023_dcdc1_supplies, + .num_consumer_supplies = ARRAY_SIZE(tps65023_dcdc1_supplies), + }, + /* dummy values for unused regulators to not crash driver: */ + { + .constraints = { + .name = "dcdc2", /* VREG_MSMC1_1V26 */ + .min_uV = 1260000, + .max_uV = 1260000, + }, + }, + { + .constraints = { + .name = "dcdc3", /* unused */ + .min_uV = 800000, + .max_uV = 3300000, + }, + }, + { + .constraints = { + .name = "ldo1", /* unused */ + .min_uV = 1000000, + .max_uV = 3150000, + }, + }, + { + .constraints = { + .name = "ldo2", /* V_USBPHY_3V3 */ + .min_uV = 3300000, + .max_uV = 3300000, + }, + }, +}; + +static void ds2482_set_slp_n(unsigned n) +{ + gpio_direction_output(BRAVO_GPIO_DS2482_SLP_N, n); +} + +static int capella_cm3602_power(int pwr_device, uint8_t enable); +static struct microp_function_config microp_functions[] = { + { + .name = "light_sensor", + .category = MICROP_FUNCTION_LSENSOR, + .levels = { 0x000, 0x001, 0x00F, 0x01E, 0x03C, 0x121, 0x190, 0x2BA, 0x35C, 0x3FF }, + .channel = 6, + .int_pin = IRQ_LSENSOR, + .golden_adc = 0xC0, + .ls_power = capella_cm3602_power, + }, +}; + +static struct lightsensor_platform_data lightsensor_data = { + .config = µp_functions[0], + .irq = MSM_uP_TO_INT(9), +}; + +static struct platform_device microp_devices[] = { + { + .name = "lightsensor_microp", + .dev = { + .platform_data = &lightsensor_data, + }, + }, +}; + +static struct microp_i2c_platform_data microp_data = { + .num_functions = ARRAY_SIZE(microp_functions), + .microp_function = microp_functions, + .num_devices = ARRAY_SIZE(microp_devices), + .microp_devices = microp_devices, + .gpio_reset = BRAVO_GPIO_UP_RESET_N, + .spi_devices = SPI_OJ | SPI_GSENSOR, +}; + +static struct tpa2018d1_platform_data tpa2018_data = { + .gpio_tpa2018_spk_en = BRAVO_CDMA_GPIO_AUD_SPK_AMP_EN, +}; + +static struct i2c_board_info base_i2c_devices[] = { + { + I2C_BOARD_INFO(SYNAPTICS_I2C_RMI_NAME, 0x40), + .platform_data = bravo_synaptics_ts_data, + .irq = MSM_GPIO_TO_INT(BRAVO_GPIO_TP_INT_N) + }, + { + I2C_BOARD_INFO("bravo-microp", 0xCC >> 1), + .platform_data = µp_data, + .irq = MSM_GPIO_TO_INT(BRAVO_GPIO_UP_INT_N) + }, + { + I2C_BOARD_INFO("ds2482", 0x30 >> 1), + .platform_data = ds2482_set_slp_n, + }, + { + I2C_BOARD_INFO(AKM8973_I2C_NAME, 0x1C), + .platform_data = &compass_platform_data, + .irq = MSM_GPIO_TO_INT(BRAVO_GPIO_COMPASS_INT_N), + }, + { + I2C_BOARD_INFO("s5k3e2fx", 0x20 >> 1), + }, + { + I2C_BOARD_INFO("tps65023", 0x48), + .platform_data = tps65023_data, + }, +}; + +static struct i2c_board_info rev_CX_i2c_devices[] = { + { + I2C_BOARD_INFO("tpa2018d1", 0x58), + .platform_data = &tpa2018_data, + }, + { + I2C_BOARD_INFO("smb329", 0x6E >> 1), + }, +}; + +static uint32_t camera_off_gpio_table[] = { + /* CAMERA */ + PCOM_GPIO_CFG(0, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(1, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(2, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* PCLK */ + PCOM_GPIO_CFG(13, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* HSYNC */ + PCOM_GPIO_CFG(14, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), /* VSYNC */ + PCOM_GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* MCLK */ +}; + +static uint32_t camera_on_gpio_table[] = { + /* CAMERA */ + PCOM_GPIO_CFG(0, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT0 */ + PCOM_GPIO_CFG(1, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT1 */ + PCOM_GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_16MA), /* PCLK */ + PCOM_GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* HSYNC */ + PCOM_GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA), /* VSYNC */ + PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* MCLK */ +}; + +void config_camera_on_gpios(void) +{ + config_gpio_table(camera_on_gpio_table, + ARRAY_SIZE(camera_on_gpio_table)); +} + +void config_camera_off_gpios(void) +{ + config_gpio_table(camera_off_gpio_table, + ARRAY_SIZE(camera_off_gpio_table)); +} + +static struct resource msm_camera_resources[] = { + { + .start = MSM_VFE_PHYS, + .end = MSM_VFE_PHYS + MSM_VFE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_VFE, + INT_VFE, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct msm_camera_device_platform_data msm_camera_device_data = { + .camera_gpio_on = config_camera_on_gpios, + .camera_gpio_off = config_camera_off_gpios, + .ioext.mdcphy = MSM_MDC_PHYS, + .ioext.mdcsz = MSM_MDC_SIZE, + .ioext.appphy = MSM_CLK_CTL_PHYS, + .ioext.appsz = MSM_CLK_CTL_SIZE, +}; + +static struct camera_flash_cfg msm_camera_sensor_flash_cfg = { + .camera_flash = flashlight_control, + .num_flash_levels = FLASHLIGHT_NUM, + .low_temp_limit = 5, + .low_cap_limit = 15, +}; + +static struct msm_camera_sensor_info msm_camera_sensor_s5k3e2fx_data = { + .sensor_name = "s5k3e2fx", + .sensor_reset = 144, /* CAM1_RST */ + .sensor_pwd = 143, /* CAM1_PWDN, enabled in a9 */ + /*.vcm_pwd = 31,*/ /* CAM1_VCM_EN, enabled in a9 */ + .pdata = &msm_camera_device_data, + .resource = msm_camera_resources, + .num_resources = ARRAY_SIZE(msm_camera_resources), + .flash_cfg = &msm_camera_sensor_flash_cfg, +}; + +static struct platform_device msm_camera_sensor_s5k3e2fx = { + .name = "msm_camera_s5k3e2fx", + .dev = { + .platform_data = &msm_camera_sensor_s5k3e2fx_data, + }, +}; + +static int __capella_cm3602_power(int on) +{ + printk(KERN_DEBUG "%s: Turn the capella_cm3602 power %s\n", + __func__, (on) ? "on" : "off"); + if (on) { + gpio_direction_output(BRAVO_GPIO_LS_EN_N, 0); + gpio_direction_output(BRAVO_GPIO_PROXIMITY_EN, 1); + } else { + gpio_direction_output(BRAVO_GPIO_LS_EN_N, 1); + } + return 0; +}; + +static DEFINE_MUTEX(capella_cm3602_lock); +static int als_power_control; + +static int capella_cm3602_power(int pwr_device, uint8_t enable) +{ + /* TODO eolsen Add Voltage reg control */ + unsigned int old_status = 0; + int ret = 0, on = 0; + mutex_lock(&capella_cm3602_lock); + + old_status = als_power_control; + if (enable) + als_power_control |= pwr_device; + else + als_power_control &= ~pwr_device; + + on = als_power_control ? 1 : 0; + if (old_status == 0 && on) + ret = __capella_cm3602_power(1); + else if (!on) + ret = __capella_cm3602_power(0); + + mutex_unlock(&capella_cm3602_lock); + return ret; +}; + +static struct capella_cm3602_platform_data capella_cm3602_pdata = { + .power = capella_cm3602_power, + .p_en = BRAVO_GPIO_PROXIMITY_EN, + .p_out = BRAVO_GPIO_PROXIMITY_INT_N, + .irq = MSM_GPIO_TO_INT(BRAVO_GPIO_PROXIMITY_INT_N), +}; + +static struct platform_device capella_cm3602 = { + .name = CAPELLA_CM3602, + .id = -1, + .dev = { + .platform_data = &capella_cm3602_pdata + } +}; + +static uint32_t flashlight_gpio_table[] = { + PCOM_GPIO_CFG(BRAVO_GPIO_FLASHLIGHT_TORCH, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(BRAVO_GPIO_FLASHLIGHT_FLASH, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), +}; + +static uint32_t flashlight_gpio_table_rev_CX[] = { + PCOM_GPIO_CFG(BRAVO_CDMA_GPIO_FLASHLIGHT_TORCH, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(BRAVO_GPIO_FLASHLIGHT_FLASH, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), +}; + +static int config_bravo_flashlight_gpios(void) +{ + if (is_cdma_version(system_rev)) { + config_gpio_table(flashlight_gpio_table_rev_CX, + ARRAY_SIZE(flashlight_gpio_table_rev_CX)); + } else { + config_gpio_table(flashlight_gpio_table, + ARRAY_SIZE(flashlight_gpio_table)); + } + return 0; +} + +static struct flashlight_platform_data bravo_flashlight_data = { + .gpio_init = config_bravo_flashlight_gpios, + .torch = BRAVO_GPIO_FLASHLIGHT_TORCH, + .flash = BRAVO_GPIO_FLASHLIGHT_FLASH, + .flash_duration_ms = 600 +}; + +static struct platform_device bravo_flashlight_device = { + .name = "flashlight", + .dev = { + .platform_data = &bravo_flashlight_data, + }, +}; + +static struct timed_gpio timed_gpios[] = { + { + .name = "vibrator", + .gpio = BRAVO_GPIO_VIBRATOR_ON, + .max_timeout = 15000, + }, +}; + +static struct timed_gpio_platform_data timed_gpio_data = { + .num_gpios = ARRAY_SIZE(timed_gpios), + .gpios = timed_gpios, +}; + +static struct platform_device bravo_timed_gpios = { + .name = "timed-gpio", + .id = -1, + .dev = { + .platform_data = &timed_gpio_data, + }, +}; + +static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { + .rx_wakeup_irq = -1, + .inject_rx_on_wakeup = 0, + .exit_lpm_cb = bcm_bt_lpm_exit_lpm_locked, +}; + +static struct bcm_bt_lpm_platform_data bcm_bt_lpm_pdata = { + .gpio_wake = BRAVO_GPIO_BT_WAKE, + .gpio_host_wake = BRAVO_GPIO_BT_HOST_WAKE, + .request_clock_off_locked = msm_hs_request_clock_off_locked, + .request_clock_on_locked = msm_hs_request_clock_on_locked, +}; + +struct platform_device bcm_bt_lpm_device = { + .name = "bcm_bt_lpm", + .id = 0, + .dev = { + .platform_data = &bcm_bt_lpm_pdata, + }, +}; + +static int ds2784_charge(int on, int fast) +{ + if (is_cdma_version(system_rev)) { + if (!on) + smb329_set_charger_ctrl(SMB329_DISABLE_CHG); + else + smb329_set_charger_ctrl(fast ? SMB329_ENABLE_FAST_CHG : SMB329_ENABLE_SLOW_CHG); + } + else + gpio_direction_output(BRAVO_GPIO_BATTERY_CHARGER_CURRENT, !!fast); + gpio_direction_output(BRAVO_GPIO_BATTERY_CHARGER_EN, !on); + return 0; +} + +static int w1_ds2784_add_slave(struct w1_slave *sl) +{ + struct dd { + struct platform_device pdev; + struct ds2784_platform_data pdata; + } *p; + + int rc; + + p = kzalloc(sizeof(struct dd), GFP_KERNEL); + if (!p) { + pr_err("%s: out of memory\n", __func__); + return -ENOMEM; + } + + rc = gpio_request(BRAVO_GPIO_BATTERY_CHARGER_EN, "charger_en"); + if (rc < 0) { + pr_err("%s: gpio_request(%d) failed: %d\n", __func__, + BRAVO_GPIO_BATTERY_CHARGER_EN, rc); + kfree(p); + return rc; + } + + if (!is_cdma_version(system_rev)) { + rc = gpio_request(BRAVO_GPIO_BATTERY_CHARGER_CURRENT, "charger_current"); + if (rc < 0) { + pr_err("%s: gpio_request(%d) failed: %d\n", __func__, + BRAVO_GPIO_BATTERY_CHARGER_CURRENT, rc); + gpio_free(BRAVO_GPIO_BATTERY_CHARGER_EN); + kfree(p); + return rc; + } + } + + p->pdev.name = "ds2784-battery"; + p->pdev.id = -1; + p->pdev.dev.platform_data = &p->pdata; + p->pdata.charge = ds2784_charge; + p->pdata.w1_slave = sl; + + platform_device_register(&p->pdev); + + return 0; +} + +static struct w1_family_ops w1_ds2784_fops = { + .add_slave = w1_ds2784_add_slave, +}; + +static struct w1_family w1_ds2784_family = { + .fid = W1_FAMILY_DS2784, + .fops = &w1_ds2784_fops, +}; + +static int __init ds2784_battery_init(void) +{ + return w1_register_family(&w1_ds2784_family); +} + +#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL +static void curcial_oj_shutdown(int enable) +{ + uint8_t cmd[3]; + + memset(cmd, 0x00, sizeof(uint8_t)*3); + cmd[2] = 0x20; + // microp firmware(v04) non-shutdown by default + microp_i2c_write(0x90, cmd, 3); + pr_err("%s\n", __func__); +} + +#define CURCIAL_OJ_POWER 150 +static int curcial_oj_poweron(int on) +{ + uint8_t data[2]; + +#ifdef CONFIG_MACH_BRAVO + struct vreg *oj_power = vreg_get(0, "gp2"); + if (IS_ERR(oj_power)) { + pr_err("%s: Error power domain\n", __func__); + return 0; + } + + if (on) { + vreg_set_level(oj_power, 2750); + vreg_enable(oj_power); + } else { + /* for microp firmware(v04) setting*/ + microp_i2c_read(MICROP_I2C_RCMD_VERSION, data, 2); + if (data[0] < 4) { + printk("Microp firmware version: %d\n", data[0]); + return 1; + } + vreg_disable(oj_power); + } + pr_err("%s: OJ power enable(%d)\n", __func__, on); +#else + /* for microp firmware(v04) setting*/ + if (on == 0) { + microp_i2c_read(MICROP_I2C_RCMD_VERSION, data, 2); + if (data[0] < 4) { + printk("Microp firmware version:%d\n",data[0]); + return 1; + } + } + + gpio_set_value(CURCIAL_OJ_POWER, on); + + if (gpio_get_value(CURCIAL_OJ_POWER) != on) { + printk(KERN_ERR "%s:OJ:power status fail \n", __func__); + return 0; + } + printk(KERN_ERR "%s:OJ:power status ok \n", __func__); +#endif + return 1; +} + +static void curcial_oj_adjust_xy(uint8_t *data, int16_t *mSumDeltaX, int16_t *mSumDeltaY) +{ + int8_t deltaX; + int8_t deltaY; + + if (data[2] == 0x80) + data[2] = 0x81; + if (data[1] == 0x80) + data[1] = 0x81; + if (1) { + deltaX = (1)*((int8_t) data[2]); /*X=2*/ + deltaY = (-1)*((int8_t) data[1]); /*Y=1*/ + } else { + deltaX = (-1)*((int8_t) data[1]); + deltaY = (1)*((int8_t) data[2]); + } + *mSumDeltaX += -((int16_t)deltaX); + *mSumDeltaY += -((int16_t)deltaY); +} + +#define BRAVO_MICROP_VER 0x03 +static struct curcial_oj_platform_data bravo_oj_data = { + .oj_poweron = curcial_oj_poweron, + .oj_shutdown = curcial_oj_shutdown, + .oj_adjust_xy = curcial_oj_adjust_xy, + .microp_version = BRAVO_MICROP_VER, + .mdelay_time = 0, + .normal_th = 8, + .xy_ratio = 15, +#ifdef CONFIG_MACH_BRAVO + .interval = 0, + .swap = false, + .y = -1, +#else + .interval = 10, + .swap = true, + .y = 1, +#endif + .x = 1, + .share_power = false, + .debugflag = 0, + .ap_code = false, + .sht_tbl = {0, 1000, 1250, 1500, 1750, 2000, 3000}, + .pxsum_tbl = {0, 0, 90, 100, 110, 120, 130}, + .degree = 7, + .Xsteps = {0, 1, 2, 3, 4, 5, 6, 8, 10, 12, + 14, 16, 18, 20, 22, 24, 26, 27, 28, 29, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9}, + .Ysteps = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9}, + .irq = MSM_uP_TO_INT(12), +}; + +static struct platform_device bravo_oj = { + .name = CURCIAL_OJ_NAME, + .id = -1, + .dev = { + .platform_data = &bravo_oj_data, + } +}; +#endif + +static struct platform_device *devices[] __initdata = { +#if !defined(CONFIG_MSM_SERIAL_DEBUGGER) + &msm_device_uart1, +#endif + &bcm_bt_lpm_device, + &msm_device_uart_dm1, + &ram_console_device, + &bravo_rfkill, + &msm_device_smd, + &msm_device_nand, + &msm_device_hsusb, + &usb_mass_storage_device, +#ifdef CONFIG_USB_ANDROID_RNDIS + &rndis_device, +#endif + &android_usb_device, + &android_pmem_mdp_device, + &android_pmem_adsp_device, +#ifdef CONFIG_720P_CAMERA + &android_pmem_venc_device, +#endif + &msm_kgsl_device, + &msm_device_i2c, + &msm_camera_sensor_s5k3e2fx, + &bravo_flashlight_device, +#ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL + &bravo_oj, +#endif + &capella_cm3602, +}; + +static uint32_t bt_gpio_table[] = { + PCOM_GPIO_CFG(BRAVO_GPIO_BT_UART1_RTS, 2, GPIO_OUTPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(BRAVO_GPIO_BT_UART1_CTS, 2, GPIO_INPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(BRAVO_GPIO_BT_UART1_RX, 2, GPIO_INPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(BRAVO_GPIO_BT_UART1_TX, 2, GPIO_OUTPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(BRAVO_GPIO_BT_RESET_N, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_4MA), + PCOM_GPIO_CFG(BRAVO_GPIO_BT_SHUTDOWN_N, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_4MA), + PCOM_GPIO_CFG(BRAVO_GPIO_BT_WAKE, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_4MA), + PCOM_GPIO_CFG(BRAVO_GPIO_BT_HOST_WAKE, 0, GPIO_INPUT, + GPIO_PULL_DOWN, GPIO_4MA), +}; + +static uint32_t bt_gpio_table_rev_CX[] = { + PCOM_GPIO_CFG(BRAVO_GPIO_BT_UART1_RTS, 2, GPIO_OUTPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(BRAVO_GPIO_BT_UART1_CTS, 2, GPIO_INPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(BRAVO_GPIO_BT_UART1_RX, 2, GPIO_INPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(BRAVO_GPIO_BT_UART1_TX, 2, GPIO_OUTPUT, + GPIO_PULL_UP, GPIO_8MA), + PCOM_GPIO_CFG(BRAVO_GPIO_BT_RESET_N, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_4MA), + PCOM_GPIO_CFG(BRAVO_GPIO_BT_SHUTDOWN_N, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_4MA), + PCOM_GPIO_CFG(BRAVO_CDMA_GPIO_BT_WAKE, 0, GPIO_OUTPUT, + GPIO_PULL_DOWN, GPIO_4MA), + PCOM_GPIO_CFG(BRAVO_GPIO_BT_HOST_WAKE, 0, GPIO_INPUT, + GPIO_PULL_DOWN, GPIO_4MA), +}; + +static uint32_t misc_gpio_table[] = { + PCOM_GPIO_CFG(BRAVO_GPIO_LCD_RST_N, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(BRAVO_GPIO_LED_3V3_EN, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), + PCOM_GPIO_CFG(BRAVO_GPIO_DOCK, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_4MA), +}; + +static uint32_t key_int_shutdown_gpio_table[] = { + PCOM_GPIO_CFG(BRAVO_GPIO_35MM_KEY_INT_SHUTDOWN, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_2MA), +}; + +static void bravo_headset_init(void) +{ + if (is_cdma_version(system_rev)) + return; + config_gpio_table(key_int_shutdown_gpio_table, + ARRAY_SIZE(key_int_shutdown_gpio_table)); + gpio_set_value(BRAVO_GPIO_35MM_KEY_INT_SHUTDOWN, 0); +} + +#define ATAG_BDADDR 0x43294329 /* bravo bluetooth address tag */ +#define ATAG_BDADDR_SIZE 4 +#define BDADDR_STR_SIZE 18 + +static char bdaddr[BDADDR_STR_SIZE]; + +module_param_string(bdaddr, bdaddr, sizeof(bdaddr), 0400); +MODULE_PARM_DESC(bdaddr, "bluetooth address"); + +static int __init parse_tag_bdaddr(const struct tag *tag) +{ + unsigned char *b = (unsigned char *)&tag->u; + + if (tag->hdr.size != ATAG_BDADDR_SIZE) + return -EINVAL; + + snprintf(bdaddr, BDADDR_STR_SIZE, "%02X:%02X:%02X:%02X:%02X:%02X", + b[0], b[1], b[2], b[3], b[4], b[5]); + + return 0; +} + +__tagtable(ATAG_BDADDR, parse_tag_bdaddr); + +static int __init board_serialno_setup(char *serialno) +{ +#ifdef CONFIG_USB_ANDROID_RNDIS + int i; + char *src = serialno; + + /* create a fake MAC address from our serial number. + * first byte is 0x02 to signify locally administered. + */ + rndis_pdata.ethaddr[0] = 0x02; + for (i = 0; *src; i++) { + /* XOR the USB serial across the remaining bytes */ + rndis_pdata.ethaddr[i % (ETH_ALEN - 1) + 1] ^= *src++; + } +#endif + + android_usb_pdata.serial_number = serialno; + return 1; +} +__setup("androidboot.serialno=", board_serialno_setup); + +static struct msm_acpu_clock_platform_data bravo_clock_data = { + .acpu_switch_time_us = 20, + .max_speed_delta_khz = 256000, + .vdd_switch_time_us = 62, + .power_collapse_khz = 245000, + .wait_for_irq_khz = 245000, + .mpll_khz = 245000 +}; + +static struct msm_acpu_clock_platform_data bravo_cdma_clock_data = { + .acpu_switch_time_us = 20, + .max_speed_delta_khz = 256000, + .vdd_switch_time_us = 62, + .power_collapse_khz = 235930, + .wait_for_irq_khz = 235930, + .mpll_khz = 235930 +}; + +#ifdef CONFIG_PERFLOCK +static unsigned bravo_perf_acpu_table[] = { + 245000000, + 576000000, + 998400000, +}; + +static struct perflock_platform_data bravo_perflock_data = { + .perf_acpu_table = bravo_perf_acpu_table, + .table_size = ARRAY_SIZE(bravo_perf_acpu_table), +}; +#endif + +static void bravo_reset(void) +{ + gpio_set_value(BRAVO_GPIO_PS_HOLD, 0); +}; + +int bravo_init_mmc(int sysrev, unsigned debug_uart); + +static const struct smd_tty_channel_desc smd_cdma_default_channels[] = { + { .id = 0, .name = "SMD_DS" }, + { .id = 19, .name = "SMD_DATA3" }, + { .id = 27, .name = "SMD_GPSNMEA" } +}; + +static void __init bravo_init(void) +{ + int ret; + + printk("bravo_init() revision=%d\n", system_rev); + + if (is_cdma_version(system_rev)) + smd_set_channel_list(smd_cdma_default_channels, + ARRAY_SIZE(smd_cdma_default_channels)); + + msm_hw_reset_hook = bravo_reset; + + if (is_cdma_version(system_rev)) + msm_acpu_clock_init(&bravo_cdma_clock_data); + else + msm_acpu_clock_init(&bravo_clock_data); + +#ifdef CONFIG_PERFLOCK + perflock_init(&bravo_perflock_data); +#endif + + msm_serial_debug_init(MSM_UART1_PHYS, INT_UART1, + &msm_device_uart1.dev, 1, MSM_GPIO_TO_INT(139)); + + config_gpio_table(misc_gpio_table, ARRAY_SIZE(misc_gpio_table)); + + if (is_cdma_version(system_rev)) { + bcm_bt_lpm_pdata.gpio_wake = BRAVO_CDMA_GPIO_BT_WAKE; + bravo_flashlight_data.torch = BRAVO_CDMA_GPIO_FLASHLIGHT_TORCH; + config_gpio_table(bt_gpio_table_rev_CX, ARRAY_SIZE(bt_gpio_table_rev_CX)); + } else { + config_gpio_table(bt_gpio_table, ARRAY_SIZE(bt_gpio_table)); + } + + gpio_request(BRAVO_GPIO_TP_LS_EN, "tp_ls_en"); + gpio_direction_output(BRAVO_GPIO_TP_LS_EN, 0); + gpio_request(BRAVO_GPIO_TP_EN, "tp_en"); + gpio_direction_output(BRAVO_GPIO_TP_EN, 0); +// gpio_request(BRAVO_GPIO_PROXIMITY_EN, "proximity_en"); +// gpio_direction_output(BRAVO_GPIO_PROXIMITY_EN, 1); + gpio_request(BRAVO_GPIO_LS_EN_N, "ls_en"); + gpio_request(BRAVO_GPIO_COMPASS_RST_N, "compass_rst"); + gpio_direction_output(BRAVO_GPIO_COMPASS_RST_N, 1); + gpio_request(BRAVO_GPIO_COMPASS_INT_N, "compass_int"); + gpio_direction_input(BRAVO_GPIO_COMPASS_INT_N); + + gpio_request(BRAVO_GPIO_DS2482_SLP_N, "ds2482_slp_n"); + + /* set the gpu power rail to manual mode so clk en/dis will not + * turn off gpu power, and hang it on resume */ + bravo_kgsl_power_rail_mode(0); + bravo_kgsl_power(true); + + msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; + msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; + + platform_add_devices(devices, ARRAY_SIZE(devices)); + + i2c_register_board_info(0, base_i2c_devices, + ARRAY_SIZE(base_i2c_devices)); + + if (is_cdma_version(system_rev)) { + i2c_register_board_info(0, rev_CX_i2c_devices, + ARRAY_SIZE(rev_CX_i2c_devices)); + } + + ret = bravo_init_mmc(system_rev, debug_uart); + if (ret != 0) + pr_crit("%s: Unable to initialize MMC\n", __func__); + + bravo_audio_init(); + bravo_headset_init(); + + platform_device_register(&bravo_timed_gpios); + + ds2784_battery_init(); +} + +static void __init bravo_fixup(struct machine_desc *desc, struct tag *tags, + char **cmdline, struct meminfo *mi) +{ + mi->nr_banks = 2; + mi->bank[0].start = PHYS_OFFSET; + mi->bank[0].size = MSM_EBI1_BANK0_SIZE; + mi->bank[1].start = MSM_EBI1_BANK1_BASE; + mi->bank[1].size = MSM_EBI1_BANK1_SIZE; +} + +static void __init bravo_map_io(void) +{ + msm_map_qsd8x50_io(); + msm_clock_init(msm_clocks_8x50, msm_num_clocks_8x50); +} + +extern struct sys_timer msm_timer; + +#ifdef CONFIG_MACH_BRAVO +MACHINE_START(BRAVO, "bravo") +#else +MACHINE_START(BRAVOC, "bravoc") +#endif + .boot_params = 0x20000100, + .fixup = bravo_fixup, + .map_io = bravo_map_io, + .init_irq = msm_init_irq, + .init_machine = bravo_init, + .timer = &msm_timer, +MACHINE_END diff --git a/arch/arm/mach-msm/board-bravo.h b/arch/arm/mach-msm/board-bravo.h new file mode 100644 index 0000000000000..f3e95cf8785c0 --- /dev/null +++ b/arch/arm/mach-msm/board-bravo.h @@ -0,0 +1,185 @@ +/* arch/arm/mach-msm/board-bravo.h + * + * Copyright (C) 2009 HTC Corporation. + * Author: Haley Teng + * Copyright (C) 2010 Giulio Cervera + * Copyright (C) 2010 Diogo Ferreira + * + * 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 __ARCH_ARM_MACH_MSM_BOARD_BRAVO_H +#define __ARCH_ARM_MACH_MSM_BOARD_BRAVO_H + +#include + +#define MSM_SMI_BASE 0x02B00000 +#define MSM_SMI_SIZE 0x01500000 + +#define MSM_PMEM_VENC_BASE 0x02B00000 +#define MSM_PMEM_VENC_SIZE 0x00800000 + +#define MSM_GPU_MEM_BASE 0x03300000 +#define MSM_GPU_MEM_SIZE 0x00500000 + +#define MSM_RAM_CONSOLE_BASE 0x03A00000 +#define MSM_RAM_CONSOLE_SIZE 0x00040000 + +#define MSM_FB_BASE 0x03B00000 +#define MSM_FB_SIZE 0x00300000 + +#define MSM_EBI1_BANK0_BASE 0x20000000 +#define MSM_EBI1_BANK0_SIZE 0x0E800000 + +#define MSM_EBI1_BANK1_BASE 0x30000000 +#define MSM_EBI1_BANK1_SIZE 0x0B700000 + +#define MSM_PMEM_MDP_BASE 0x3B700000 +#define MSM_PMEM_MDP_SIZE 0x02000000 + +#define MSM_PMEM_ADSP_BASE 0x3D700000 +#define MSM_PMEM_ADSP_SIZE 0x02900000 + +#define BRAVO_GPIO_PS_HOLD 25 + +#define BRAVO_GPIO_OJ_ACTION_XB 33 + +#define BRAVO_GPIO_UP_INT_N 35 +#define BRAVO_GPIO_UP_RESET_N 82 +#define BRAVO_GPIO_LS_EN_N 119 + +#define BRAVO_GPIO_TP_INT_N 92 +#define BRAVO_GPIO_TP_LS_EN 93 +#define BRAVO_GPIO_TP_EN 160 + +#define BRAVO_GPIO_POWER_KEY 94 +#define BRAVO_GPIO_SDMC_CD_N 153 + +#define BRAVO_GPIO_WIFI_SHUTDOWN_N 127 +#define BRAVO_GPIO_WIFI_IRQ 152 + +#define BRAVO_GPIO_BALL_UP 38 +#define BRAVO_GPIO_BALL_DOWN 37 +#define BRAVO_GPIO_BALL_LEFT 145 +#define BRAVO_GPIO_BALL_RIGHT 21 + +#define BRAVO_GPIO_BT_UART1_RTS 43 +#define BRAVO_GPIO_BT_UART1_CTS 44 +#define BRAVO_GPIO_BT_UART1_RX 45 +#define BRAVO_GPIO_BT_UART1_TX 46 +#define BRAVO_GPIO_BT_RESET_N 146 +#define BRAVO_GPIO_BT_SHUTDOWN_N 128 + +#define BRAVO_GPIO_BT_WAKE 57 +#define BRAVO_GPIO_BT_HOST_WAKE 86 + +#define BRAVO_GPIO_PROXIMITY_INT_N 90 +#define BRAVO_GPIO_PROXIMITY_EN 120 + +#define BRAVO_GPIO_DS2482_SLP_N 87 +#define BRAVO_GPIO_VIBRATOR_ON 89 + +#define BRAVO_CDMA_GPIO_BT_WAKE 28 +#define BRAVO_CDMA_GPIO_FLASHLIGHT_TORCH 26 + +#define BRAVO_CDMA_SD_2V85_EN 100 +#define BRAVO_CDMA_JOG_2V6_EN 150 + +/* Compass */ +#define BRAVO_GPIO_COMPASS_INT_N 153 +#define BRAVO_GPIO_COMPASS_RST_N 107 +#ifdef CONFIG_MACH_BRAVO +#define BRAVO_PROJECT_NAME "bravo" +#else +#define BRAVO_PROJECT_NAME "bravoc" +#endif +#define BRAVO_LAYOUTS { \ + { {-1, 0, 0}, { 0, -1, 0}, {0, 0, 1} }, \ + { { 0, -1, 0}, { 1, 0, 0}, {0, 0, -1} }, \ + { { 0, -1, 0}, { 1, 0, 0}, {0, 0, 1} }, \ + { {-1, 0, 0}, { 0, 0, -1}, {0, 1, 0} } \ +} + +/* Audio */ +#define BRAVO_AUD_JACKHP_EN 157 +#define BRAVO_AUD_2V5_EN 158 +#define BRAVO_AUD_MICPATH_SEL 111 + +#define BRAVO_GPIO_AUD_SPK_AMP_EN 104 + +/* Bluetooth PCM */ +#define BRAVO_BT_PCM_OUT 68 +#define BRAVO_BT_PCM_IN 69 +#define BRAVO_BT_PCM_SYNC 70 +#define BRAVO_BT_PCM_CLK 71 +/* flash light */ +#define BRAVO_GPIO_FLASHLIGHT_TORCH 58 +#define BRAVO_GPIO_FLASHLIGHT_FLASH 84 + +/* keypad */ +#define BRAVO_GPIO_KP_MKOUT0 33 +#define BRAVO_GPIO_KP_MKOUT1 32 +#define BRAVO_GPIO_KP_MKOUT2 31 +#define BRAVO_GPIO_KP_MPIN0 42 +#define BRAVO_GPIO_KP_MPIN1 41 +#define BRAVO_GPIO_KP_MPIN2 40 + +#define BRAVO_GPIO_LED_3V3_EN 85 +#define BRAVO_GPIO_LCD_RST_N 29 +#define BRAVO_GPIO_LCD_ID0 147 + +/* 3.5mm remote control key interrupt shutdown signal */ +#define BRAVO_GPIO_35MM_KEY_INT_SHUTDOWN 19 + +#define BRAVO_GPIO_DOCK 106 + +#define BRAVO_CDMA_GPIO_AUD_SPK_AMP_EN 104 + +#define BRAVO_GPIO_BATTERY_DETECTION 39 +#define BRAVO_GPIO_BATTERY_CHARGER_EN 22 +#define BRAVO_GPIO_BATTERY_CHARGER_CURRENT 16 + +/* display relative */ +#define BRAVO_LCD_SPI_CLK (17) +#define BRAVO_LCD_SPI_DO (18) +#define BRAVO_LCD_SPI_CSz (20) +#define BRAVO_LCD_RSTz (29) +#define BRAVO_LCD_R1 (114) +#define BRAVO_LCD_R2 (115) +#define BRAVO_LCD_R3 (116) +#define BRAVO_LCD_R4 (117) +#define BRAVO_LCD_R5 (118) +#define BRAVO_LCD_G0 (121) +#define BRAVO_LCD_G1 (122) +#define BRAVO_LCD_G2 (123) +#define BRAVO_LCD_G3 (124) +#define BRAVO_LCD_G4 (125) +#define BRAVO_LCD_G5 (126) +#define BRAVO_LCD_B1 (130) +#define BRAVO_LCD_B2 (131) +#define BRAVO_LCD_B3 (132) +#define BRAVO_LCD_B4 (133) +#define BRAVO_LCD_B5 (134) +#define BRAVO_LCD_PCLK (135) +#define BRAVO_LCD_VSYNC (136) +#define BRAVO_LCD_HSYNC (137) +#define BRAVO_LCD_DE (138) + +/* know revision + 0x02 = GSM amoled (dev phone) + 0x05 = CDMA lcd + 0x81 = GSM amoled + 0x83 = GSM lcd + 0x84 = GSM lcd +*/ + +#define is_cdma_version(rev) (rev == 0x05) + +#endif /* __ARCH_ARM_MACH_MSM_BOARD_BRAVO_H */ diff --git a/arch/arm/mach-msm/include/mach/board-bravo-microp-common.h b/arch/arm/mach-msm/include/mach/board-bravo-microp-common.h new file mode 100644 index 0000000000000..112121521d8fe --- /dev/null +++ b/arch/arm/mach-msm/include/mach/board-bravo-microp-common.h @@ -0,0 +1,166 @@ +/* arch/arm/mach-msm/board-bravo.h + * + * Copyright (C) 2009 HTC Corporation. + * Author: Haley Teng + * Copyright (C) 2010 Kali- + * Copyright (C) 2010 Diogo Ferreira + * + * 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_BOARD_BRAVO_MICROP_COMMON_H +#define _LINUX_BOARD_BRAVO_MICROP_COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MICROP_I2C_NAME "bravo-microp" + +#define MICROP_LSENSOR_ADC_CHAN 6 +#define MICROP_REMOTE_KEY_ADC_CHAN 7 + +#define MICROP_I2C_WCMD_MISC 0x20 +#define MICROP_I2C_WCMD_SPI_EN 0x21 +#define MICROP_I2C_WCMD_AUTO_BL_CTL 0x23 +#define MICROP_I2C_RCMD_SPI_BL_STATUS 0x24 +#define MICROP_I2C_WCMD_BUTTONS_LED_CTRL 0x25 +#define MICROP_I2C_RCMD_VERSION 0x30 +#define MICROP_I2C_WCMD_ADC_TABLE 0x42 +#define MICROP_I2C_WCMD_LED_MODE 0x53 +#define MICROP_I2C_RCMD_GREEN_LED_REMAIN_TIME 0x54 +#define MICROP_I2C_RCMD_AMBER_LED_REMAIN_TIME 0x55 +#define MICROP_I2C_RCMD_BLUE_LED_REMAIN_TIME 0x57 +#define MICROP_I2C_WCMD_READ_ADC_VALUE_REQ 0x60 +#define MICROP_I2C_RCMD_ADC_VALUE 0x62 +#define MICROP_I2C_WCMD_REMOTEKEY_TABLE 0x63 +#define MICROP_I2C_WCMD_LCM_REGISTER 0x70 +#define MICROP_I2C_WCMD_GSENSOR_REG 0x73 +#define MICROP_I2C_WCMD_GSENSOR_REG_DATA_REQ 0x74 +#define MICROP_I2C_RCMD_GSENSOR_REG_DATA 0x75 +#define MICROP_I2C_WCMD_GSENSOR_DATA_REQ 0x76 +#define MICROP_I2C_RCMD_GSENSOR_X_DATA 0x77 +#define MICROP_I2C_RCMD_GSENSOR_Y_DATA 0x78 +#define MICROP_I2C_RCMD_GSENSOR_Z_DATA 0x79 +#define MICROP_I2C_RCMD_GSENSOR_DATA 0x7A +#define MICROP_I2C_WCMD_OJ_REG 0x7B +#define MICROP_I2C_WCMD_OJ_REG_DATA_REQ 0x7C +#define MICROP_I2C_RCMD_OJ_REG_DATA 0x7D +#define MICROP_I2C_WCMD_OJ_POS_DATA_REQ 0x7E +#define MICROP_I2C_RCMD_OJ_POS_DATA 0x7F +#define MICROP_I2C_WCMD_GPI_INT_CTL_EN 0x80 +#define MICROP_I2C_WCMD_GPI_INT_CTL_DIS 0x81 +#define MICROP_I2C_RCMD_GPI_INT_STATUS 0x82 +#define MICROP_I2C_RCMD_GPI_STATUS 0x83 +#define MICROP_I2C_WCMD_GPI_INT_STATUS_CLR 0x84 +#define MICROP_I2C_RCMD_GPI_INT_SETTING 0x85 +#define MICROP_I2C_RCMD_REMOTE_KEYCODE 0x87 +#define MICROP_I2C_WCMD_REMOTE_KEY_DEBN_TIME 0x88 +#define MICROP_I2C_WCMD_REMOTE_PLUG_DEBN_TIME 0x89 +#define MICROP_I2C_WCMD_SIMCARD_DEBN_TIME 0x8A +#define MICROP_I2C_WCMD_GPO_LED_STATUS_EN 0x90 +#define MICROP_I2C_WCMD_GPO_LED_STATUS_DIS 0x91 +#define MICROP_I2C_WCMD_OJ_INT_STATUS 0xA8 + +#define IRQ_OJ (1<<12) +#define IRQ_GSENSOR (1<<10) +#define IRQ_LSENSOR (1<<9) +#define IRQ_REMOTEKEY (1<<7) +#define IRQ_HEADSETIN (1<<2) +#define IRQ_SDCARD (1<<0) + +#define SPI_GSENSOR (1 << 0) +#define SPI_LCM (1 << 1) +#define SPI_OJ (1 << 2) + +#define MICROP_FUNCTION_LSENSOR 1 +#define MICROP_FUNCTION_REMOTEKEY 2 +#define MICROP_FUNCTION_LCD_BL 3 +#define MICROP_FUNCTION_RMK_VALUE 4 +#define MICROP_FUNCTION_INTR 11 +#define MICROP_FUNCTION_GSENSOR 12 +#define MICROP_FUNCTION_LED 13 +#define MICROP_FUNCTION_HPIN 14 +#define MICROP_FUNCTION_RESET_INT 15 +#define MICROP_FUNCTION_SIM_CARD 16 +#define MICROP_FUNCTION_SDCARD 17 +#define MICROP_FUNCTION_OJ 18 +#define MICROP_FUNCTION_P 19 + +#define LS_PWR_ON (1 << 0) +#define ALS_CALIBRATED 0x6DA5 +#define ATAG_ALS 0x5441001b + +/* I2C functions for drivers */ +int microp_i2c_read(uint8_t addr, uint8_t *data, int length); +int microp_i2c_write(uint8_t addr, uint8_t *data, int length); +int microp_read_adc(uint8_t channel, uint16_t *value); +int microp_spi_vote_enable(int spi_device, uint8_t enable); +int microp_write_interrupt(struct i2c_client *client, + uint16_t interrupt, uint8_t enable); +struct i2c_client *get_microp_client(void); + +struct microp_function_config { + const char *name; + uint8_t category; + uint8_t init_value; + uint8_t channel; + uint8_t fade_time; + uint32_t sub_categ; + uint16_t levels[10]; + uint16_t dutys[10]; + uint16_t int_pin; + uint16_t golden_adc; + uint8_t mask_r[3]; + uint8_t mask_w[3]; + uint32_t ls_gpio_on; + int (*ls_power)(int, uint8_t); +}; + +struct microp_i2c_platform_data { + struct microp_function_config *microp_function; + struct platform_device *microp_devices; + int num_devices; + int num_functions; + uint32_t gpio_reset; + uint32_t microp_ls_on; + void *dev_id; + uint8_t microp_mic_status; + uint8_t function_node[20]; + uint32_t cmd_diff; + uint32_t spi_devices; + uint32_t spi_devices_init; +}; + +struct lightsensor_platform_data{ + struct i2c_client *client; + struct microp_function_config *config; + int irq; + int old_intr_cmd; +}; + +struct microp_ops { + int (*init_microp_func)(struct i2c_client *); + int (*als_pwr_enable)(int pwr_device, uint8_t en); + int (*als_intr_enable)(struct i2c_client *, + uint32_t als_func, uint8_t en); + void (*als_level_change)(struct i2c_client *, uint8_t *data); + void (*headset_enable)(int en); + void (*spi_enable)(int en); +}; + +#endif /* _LINUX_BOARD_BRAVO_MICROP_COMMON_H */ diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 1e70baa4a6197..fb5ee47be7095 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -476,6 +476,12 @@ config INPUT_CAPELLA_CM3602 Say Y here to enable the Capella CM3602 Short Distance Proximity Sensor with Ambient Light Sensor. +config INPUT_CAPELLA_CM3602_HTC + tristate "Capella CM3602 proximity and light sensor (HTC)" + help + Say Y here to enable the Capella CM3602 Short Distance Proximity + Sensor with Ambient Light Sensor. + config LIGHTSENSOR_MICROP tristate "LIGHTSENSOR MICROP Driver" help diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index a68ccaa0b64c5..5815c2a27159d 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -17,6 +17,8 @@ obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o +obj-$(CONFIG_INPUT_CAPELLA_CM3602) += capella_cm3602.o +obj-$(CONFIG_INPUT_CAPELLA_CM3602_HTC) += capella_cm3602_htc.o obj-$(CONFIG_INPUT_CM109) += cm109.o obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o diff --git a/drivers/input/misc/capella_cm3602_htc.c b/drivers/input/misc/capella_cm3602_htc.c new file mode 100644 index 0000000000000..985bce83cc7ad --- /dev/null +++ b/drivers/input/misc/capella_cm3602_htc.c @@ -0,0 +1,352 @@ +/* drivers/input/misc/capella_cm3602.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Iliyan Malchev + * + * 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 + +#define D(x...) pr_info(x) + +struct wake_lock proximity_wake_lock; + +static void ps_irq_do_work(struct work_struct *work); +static DECLARE_WORK(ps_irq_work, ps_irq_do_work); + +static struct capella_cm3602_data { + struct input_dev *input_dev; + struct capella_cm3602_platform_data *pdata; + struct workqueue_struct *ps_wq; + int enabled; +} the_data; + +static int misc_opened; + +static int capella_cm3602_report(struct capella_cm3602_data *data) +{ + int val = gpio_get_value(data->pdata->p_out); + int value1, value2; + int retry_limit = 10; + int irq = data->pdata->irq; + + do { + value1 = gpio_get_value(data->pdata->p_out); + set_irq_type(irq, value1 ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(data->pdata->p_out); + } while (value1 != value2 && retry_limit-- > 0); + + if (val < 0) { + pr_err("%s: gpio_get_value error %d\n", __func__, val); + return val; + } + + D("proximity %s\n", val ? "FAR" : "NEAR"); + + /* 0 is close, 1 is far */ + input_report_abs(data->input_dev, ABS_DISTANCE, val); + input_sync(data->input_dev); + + wake_lock_timeout(&proximity_wake_lock, 2*HZ); + + return val; +} + +static void ps_irq_do_work(struct work_struct *work) +{ + capella_cm3602_report(&the_data); + enable_irq(the_data.pdata->irq); +} + +static irqreturn_t capella_cm3602_irq_handler(int irq, void *data) +{ + disable_irq_nosync(irq); + + queue_work(the_data.ps_wq, &ps_irq_work); + + return IRQ_HANDLED; +} + +static int capella_cm3602_enable(struct capella_cm3602_data *data) +{ + int rc; + int irq = data->pdata->irq; + + D("%s\n", __func__); + if (data->enabled) { + D("%s: already enabled\n", __func__); + return 0; + } + + /* dummy report */ + input_report_abs(data->input_dev, ABS_DISTANCE, -1); + input_sync(data->input_dev); + + data->pdata->power(PS_PWR_ON, 1); + + rc = gpio_direction_output(data->pdata->p_en, 0); + + msleep(220); + + data->enabled = !rc; + if (!rc) + capella_cm3602_report(data); + + enable_irq(irq); + rc = set_irq_wake(irq, 1); + if (rc < 0) + pr_err("%s: failed to set irq %d as a wake interrupt\n", + __func__, irq); + + return rc; +} + +static int capella_cm3602_disable(struct capella_cm3602_data *data) +{ + int rc = -EIO; + int irq = data->pdata->irq; + + D("%s\n", __func__); + if (!data->enabled) { + D("%s: already disabled\n", __func__); + return 0; + } + disable_irq(irq); + rc = set_irq_wake(irq, 0); + if (rc < 0) + pr_err("%s: failed to set irq %d as a non-wake interrupt\n", + __func__, irq); + + rc = gpio_direction_output(data->pdata->p_en, 1); + + if (rc < 0) + return rc; + data->pdata->power(PS_PWR_ON, 0); + data->enabled = 0; + + input_event(data->input_dev, EV_SYN, SYN_CONFIG, 0); + return rc; +} + +static int capella_cm3602_setup(struct capella_cm3602_data *ip) +{ + int rc = -EIO; + struct capella_cm3602_platform_data *pdata = ip->pdata; + int irq = pdata->irq; + + D("%s\n", __func__); + + if (pdata->p_out == 0 || pdata->p_en == 0) { + pr_err("%s: gpio == 0!!\n", __func__); + rc = -1; + goto done; + } + + rc = gpio_request(pdata->p_out, "gpio_proximity_out"); + if (rc < 0) { + pr_err("%s: gpio %d request failed (%d)\n", + __func__, pdata->p_out, rc); + goto done; + } + + rc = gpio_request(pdata->p_en, "gpio_proximity_en"); + if (rc < 0) { + pr_err("%s: gpio %d request failed (%d)\n", + __func__, pdata->p_en, rc); + goto fail_free_p_out; + } + + rc = gpio_direction_input(pdata->p_out); + if (rc < 0) { + pr_err("%s: failed to set gpio %d as input (%d)\n", + __func__, pdata->p_out, rc); + goto fail_free_p_en; + } + + the_data.ps_wq = create_singlethread_workqueue("proximity_wq"); + if (!the_data.ps_wq) { + pr_err("%s: can't create workqueue\n", __func__); + rc = -ENOMEM; + goto fail_free_p_en; + } + + rc = request_irq(irq, + capella_cm3602_irq_handler, + IRQF_TRIGGER_LOW | IRQF_TRIGGER_HIGH, + "capella_cm3602", + ip); + if (rc < 0) { + pr_err("%s: request_irq(%d) failed for gpio %d (%d)\n", + __func__, irq, + pdata->p_out, rc); + goto fail_free_wq; + } + + goto done; + +fail_free_wq: + destroy_workqueue(the_data.ps_wq); +fail_free_p_en: + gpio_free(pdata->p_en); +fail_free_p_out: + gpio_free(pdata->p_out); +done: + return rc; +} + +static int capella_cm3602_open(struct inode *inode, struct file *file) +{ + D("%s\n", __func__); + if (misc_opened) + return -EBUSY; + misc_opened = 1; + return 0; +} + +static int capella_cm3602_release(struct inode *inode, struct file *file) +{ + D("%s\n", __func__); + misc_opened = 0; + return capella_cm3602_disable(&the_data); +} + +static long capella_cm3602_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int val; + D("%s cmd %d\n", __func__, _IOC_NR(cmd)); + switch (cmd) { + case CAPELLA_CM3602_IOCTL_ENABLE: + if (get_user(val, (unsigned long __user *)arg)) + return -EFAULT; + if (val) + return capella_cm3602_enable(&the_data); + else + return capella_cm3602_disable(&the_data); + break; + case CAPELLA_CM3602_IOCTL_GET_ENABLED: + return put_user(the_data.enabled, (unsigned long __user *)arg); + break; + default: + pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd)); + return -EINVAL; + } +} + +static struct file_operations capella_cm3602_fops = { + .owner = THIS_MODULE, + .open = capella_cm3602_open, + .release = capella_cm3602_release, + .unlocked_ioctl = capella_cm3602_ioctl +}; + +struct miscdevice capella_cm3602_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "cm3602", + .fops = &capella_cm3602_fops +}; + +static int capella_cm3602_probe(struct platform_device *pdev) +{ + int rc = -EIO; + struct input_dev *input_dev; + struct capella_cm3602_data *ip; + struct capella_cm3602_platform_data *pdata; + + D("%s: probe\n", __func__); + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + pr_err("%s: missing pdata!\n", __func__); + goto done; + } + if (!pdata->power) { + pr_err("%s: incomplete pdata!\n", __func__); + goto done; + } + + ip = &the_data; + platform_set_drvdata(pdev, ip); + + /*D("%s: allocating input device\n", __func__);*/ + input_dev = input_allocate_device(); + if (!input_dev) { + pr_err("%s: could not allocate input device\n", __func__); + rc = -ENOMEM; + goto done; + } + ip->input_dev = input_dev; + ip->pdata = pdata; + input_set_drvdata(input_dev, ip); + + input_dev->name = "proximity"; + + set_bit(EV_ABS, input_dev->evbit); + input_set_abs_params(input_dev, ABS_DISTANCE, 0, 1, 0, 0); + + /*D("%s: registering input device\n", __func__);*/ + rc = input_register_device(input_dev); + if (rc < 0) { + pr_err("%s: could not register input device\n", __func__); + goto err_free_input_device; + } + + /*D("%s: registering misc device\n", __func__);*/ + rc = misc_register(&capella_cm3602_misc); + if (rc < 0) { + pr_err("%s: could not register misc device\n", __func__); + goto err_unregister_input_device; + } + + wake_lock_init(&proximity_wake_lock, WAKE_LOCK_SUSPEND, "proximity"); + + rc = capella_cm3602_setup(ip); + if (!rc) + goto done; + + misc_deregister(&capella_cm3602_misc); +err_unregister_input_device: + input_unregister_device(input_dev); +err_free_input_device: + input_free_device(input_dev); +done: + if (ip->pdata && ip->pdata->irq) + disable_irq(ip->pdata->irq); + return rc; +} + +static struct platform_driver capella_cm3602_driver = { + .probe = capella_cm3602_probe, + .driver = { + .name = CAPELLA_CM3602, + .owner = THIS_MODULE + }, +}; + +static int __init capella_cm3602_init(void) +{ + return platform_driver_register(&capella_cm3602_driver); +} + +device_initcall(capella_cm3602_init); From ee2c364c74b1cafa337d93c43b9263cb93a97bd1 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 10 May 2011 16:29:51 -0700 Subject: [PATCH 1714/2556] net: wireless: bcm4329: Add new and default wifi locale support Signed-off-by: Dmitry Shmidt --- .../net/wireless/bcm4329/dhd_custom_gpio.c | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/bcm4329/dhd_custom_gpio.c b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c index 4739b97b48bcf..161ed22afe5c3 100644 --- a/drivers/net/wireless/bcm4329/dhd_custom_gpio.c +++ b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c @@ -183,18 +183,22 @@ dhd_custom_get_mac_address(unsigned char *buf) const struct cntry_locales_custom translate_custom_table[] = { /* Table should be filled out based on custom platform regulatory requirement */ #ifdef EXAMPLE_TABLE + {"", "XY", 4} /* universal */ {"US", "US", 69}, /* input ISO "US" to : US regrev 69 */ {"CA", "US", 69}, /* input ISO "CA" to : US regrev 69 */ - {"EU", "EU", 05}, /* input ISO "EU" to : EU regrev 05 */ - {"FR", "EU", 05}, - {"DE", "EU", 05}, - {"IR", "EU", 05}, - {"UK", "EU", 05}, /* input ISO "UK" to : EU regrev 05 */ - {"KR", "XY", 03}, - {"AU", "XY", 03}, - {"CN", "XY", 03}, /* input ISO "CN" to : XY regrev 03 */ - {"TW", "XY", 03}, - {"AR", "XY", 03} + {"EU", "EU", 5}, /* input ISO "EU" to : EU regrev 05 */ + {"FR", "EU", 5}, + {"DE", "EU", 5}, + {"IR", "EU", 5}, + {"UK", "EU", 5}, /* input ISO "UK" to : EU regrev 05 */ + {"KR", "XY", 3}, + {"AU", "XY", 3}, + {"CN", "XY", 3}, /* input ISO "CN" to : XY regrev 03 */ + {"HK", "XY", 3}, + {"TW", "XY", 3}, + {"BR", "XY", 3}, + {"MX", "XY", 3}, + {"AR", "XY", 3} #endif /* EXAMPLE_TABLE */ }; @@ -219,8 +223,10 @@ void get_customized_country_code(char *country_iso_code, wl_country_t *cspec) if (strcmp(country_iso_code, translate_custom_table[i].iso_abbrev) == 0) { memcpy(cspec->ccode, translate_custom_table[i].custom_locale, WLC_CNTRY_BUF_SZ); cspec->rev = translate_custom_table[i].custom_locale_rev; - break; + return; } } + memcpy(cspec->ccode, translate_custom_table[0].custom_locale, WLC_CNTRY_BUF_SZ); + cspec->rev = translate_custom_table[0].custom_locale_rev; return; } From 3fe48911f7dd881d7eef765bb1b93c6557c0bf4f Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Fri, 13 May 2011 13:32:53 -0400 Subject: [PATCH 1715/2556] net: wireless: bcm4329: add some differences from vision-2632 -remove EXAMPLE_TABLE define -add CUSTOMER_HW2 condition to get_customized_country_code -add wifi_get_country_code -add status != WLC_E_STATUS_SUCCESS block, to WLC_E_ROAM case -add get_country_code to platform --- drivers/net/wireless/bcm4329/dhd_custom_gpio.c | 16 +++++++++++++++- drivers/net/wireless/bcm4329/dhd_linux.c | 11 +++++++++++ drivers/net/wireless/bcm4329/wl_iw.c | 9 +++++++++ include/linux/wlan_plat.h | 1 + 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcm4329/dhd_custom_gpio.c b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c index 161ed22afe5c3..5aafd13f6d305 100644 --- a/drivers/net/wireless/bcm4329/dhd_custom_gpio.c +++ b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c @@ -47,6 +47,7 @@ int wifi_set_carddetect(int on); int wifi_set_power(int on, unsigned long msec); int wifi_get_irq_number(unsigned long *irq_flags_ptr); int wifi_get_mac_addr(unsigned char *buf); +void *wifi_get_country_code(char *ccode); #endif #if defined(OOB_INTR_ONLY) @@ -178,7 +179,6 @@ dhd_custom_get_mac_address(unsigned char *buf) } #endif /* GET_CUSTOM_MAC_ENABLE */ -#define EXAMPLE_TABLE /* Customized Locale table : OPTIONAL feature */ const struct cntry_locales_custom translate_custom_table[] = { /* Table should be filled out based on custom platform regulatory requirement */ @@ -209,6 +209,19 @@ const struct cntry_locales_custom translate_custom_table[] = { */ void get_customized_country_code(char *country_iso_code, wl_country_t *cspec) { +#ifdef CUSTOMER_HW2 + struct cntry_locales_custom *cloc_ptr; + + if (!cspec) + return; + + cloc_ptr = wifi_get_country_code(country_iso_code); + if (cloc_ptr) { + strlcpy(cspec->ccode, cloc_ptr->custom_locale, WLC_CNTRY_BUF_SZ); + cspec->rev = cloc_ptr->custom_locale_rev; + } + return; +#else int size, i; size = ARRAYSIZE(translate_custom_table); @@ -229,4 +242,5 @@ void get_customized_country_code(char *country_iso_code, wl_country_t *cspec) memcpy(cspec->ccode, translate_custom_table[0].custom_locale, WLC_CNTRY_BUF_SZ); cspec->rev = translate_custom_table[0].custom_locale_rev; return; +#endif } diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index 3262bf8ea704d..4f719e8de1d2b 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -129,6 +129,17 @@ int wifi_get_mac_addr(unsigned char *buf) return -EOPNOTSUPP; } +void *wifi_get_country_code(char *ccode) +{ + DHD_TRACE(("%s\n", __FUNCTION__)); + if (!ccode) + return NULL; + if (wifi_control_data && wifi_control_data->get_country_code) { + return wifi_control_data->get_country_code(ccode); + } + return NULL; +} + static int wifi_probe(struct platform_device *pdev) { struct wifi_platform_data *wifi_ctrl = diff --git a/drivers/net/wireless/bcm4329/wl_iw.c b/drivers/net/wireless/bcm4329/wl_iw.c index cc860e3a03a1e..0c417a47a12c3 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.c +++ b/drivers/net/wireless/bcm4329/wl_iw.c @@ -7851,6 +7851,15 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) cmd = IWEVREGISTERED; break; case WLC_E_ROAM: + if (status != WLC_E_STATUS_SUCCESS) { + WL_ERROR(("ROAMING did not succeeded, keep status Quo\n")); + goto wl_iw_event_end; + } + + memcpy(wrqu.addr.sa_data, &e->addr.octet, ETHER_ADDR_LEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + cmd = SIOCGIWAP; + if (status == WLC_E_STATUS_SUCCESS) { memcpy(wrqu.addr.sa_data, &e->addr.octet, ETHER_ADDR_LEN); wrqu.addr.sa_family = ARPHRD_ETHER; diff --git a/include/linux/wlan_plat.h b/include/linux/wlan_plat.h index 3b1e2e054fd52..40ec3482d1efb 100644 --- a/include/linux/wlan_plat.h +++ b/include/linux/wlan_plat.h @@ -21,6 +21,7 @@ struct wifi_platform_data { int (*set_carddetect)(int val); void *(*mem_prealloc)(int section, unsigned long size); int (*get_mac_addr)(unsigned char *buf); + void *(*get_country_code)(char *ccode); }; #endif From 6ff5a8a18377802f5709ed328b122817988e4718 Mon Sep 17 00:00:00 2001 From: Dmitry ORNATSKYY Date: Wed, 11 May 2011 09:44:21 -0700 Subject: [PATCH 1716/2556] net: wireless: bcm4329: Reduce listen interval to 10 (from 20) Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/include/wlioctl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcm4329/include/wlioctl.h b/drivers/net/wireless/bcm4329/include/wlioctl.h index 94518205d3d8a..00c61f10782f6 100644 --- a/drivers/net/wireless/bcm4329/include/wlioctl.h +++ b/drivers/net/wireless/bcm4329/include/wlioctl.h @@ -862,7 +862,7 @@ typedef struct wl_ioctl { #define PM_MAX 1 #define PM_FAST 2 -#define LISTEN_INTERVAL 20 +#define LISTEN_INTERVAL 10 #define INTERFERE_NONE 0 #define NON_WLAN 1 From 8e6c4c545ece04981162bea74584b9277cbda927 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sun, 15 May 2011 15:50:30 -0400 Subject: [PATCH 1717/2556] [configs] regen defconfigs --- arch/arm/configs/cyanogen_bravo_defconfig | 97 +- arch/arm/configs/cyanogen_bravoc_defconfig | 97 +- .../configs/cyanogen_incrediblec_defconfig | 101 +- arch/arm/configs/cyanogen_mahimahi_defconfig | 2064 +++++++++++++++++ .../arm/configs/cyanogen_supersonic_defconfig | 11 +- 5 files changed, 2307 insertions(+), 63 deletions(-) create mode 100644 arch/arm/configs/cyanogen_mahimahi_defconfig diff --git a/arch/arm/configs/cyanogen_bravo_defconfig b/arch/arm/configs/cyanogen_bravo_defconfig index a9ae3e7bac7d1..eee6d6f32fd26 100644 --- a/arch/arm/configs/cyanogen_bravo_defconfig +++ b/arch/arm/configs/cyanogen_bravo_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux/arm 2.6.37.6 Kernel Configuration -# Sat Apr 30 10:30:59 2011 +# Linux/arm 2.6.38.6 Kernel Configuration +# Sun May 15 15:47:56 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -9,7 +9,6 @@ CONFIG_GENERIC_GPIO=y # CONFIG_ARCH_USES_GETTIMEOFFSET is not set CONFIG_GENERIC_CLOCKEVENTS=y CONFIG_HAVE_PROC_CPU=y -CONFIG_GENERIC_HARDIRQS=y CONFIG_STACKTRACE_SUPPORT=y CONFIG_HAVE_LATENCYTOP_SUPPORT=y CONFIG_LOCKDEP_SUPPORT=y @@ -22,7 +21,6 @@ CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y CONFIG_GENERIC_HWEIGHT=y CONFIG_GENERIC_CALIBRATE_DELAY=y CONFIG_NEED_DMA_MAP_STATE=y -CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y CONFIG_VECTORS_BASE=0xffff0000 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" CONFIG_CONSTRUCTORS=y @@ -49,7 +47,17 @@ CONFIG_KERNEL_LZMA=y # CONFIG_BSD_PROCESS_ACCT is not set # CONFIG_TASKSTATS is not set # CONFIG_AUDIT is not set -# CONFIG_HAVE_GENERIC_HARDIRQS is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED is not set +CONFIG_HAVE_SPARSE_IRQ=y +# CONFIG_GENERIC_PENDING_IRQ is not set +# CONFIG_AUTO_IRQ_AFFINITY is not set +# CONFIG_IRQ_PER_CPU is not set # CONFIG_SPARSE_IRQ is not set # @@ -81,6 +89,7 @@ CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y # CONFIG_BLK_CGROUP is not set # CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set # CONFIG_SYSFS_DEPRECATED is not set # CONFIG_RELAY is not set CONFIG_BLK_DEV_INITRD=y @@ -88,11 +97,13 @@ CONFIG_INITRAMFS_SOURCE="" CONFIG_RD_GZIP=y # CONFIG_RD_BZIP2 is not set # CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_SYSCTL=y CONFIG_ANON_INODES=y CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y CONFIG_EMBEDDED=y CONFIG_UID16=y # CONFIG_SYSCTL_SYSCALL is not set @@ -132,6 +143,7 @@ CONFIG_HAVE_KPROBES=y CONFIG_HAVE_KRETPROBES=y CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y # # GCOV-based kernel profiling @@ -214,6 +226,7 @@ CONFIG_MMU=y # CONFIG_ARCH_EP93XX is not set # CONFIG_ARCH_FOOTBRIDGE is not set # CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set # CONFIG_ARCH_STMP3XXX is not set # CONFIG_ARCH_NETX is not set # CONFIG_ARCH_H720X is not set @@ -257,6 +270,7 @@ CONFIG_ARCH_MSM=y # CONFIG_ARCH_DAVINCI is not set # CONFIG_ARCH_OMAP is not set # CONFIG_PLAT_SPEAR is not set +# CONFIG_GPIO_PCA953X is not set # CONFIG_ARCH_MSM7X00A is not set # CONFIG_ARCH_MSM7X30 is not set CONFIG_ARCH_QSD8X50=y @@ -286,7 +300,6 @@ CONFIG_MACH_BRAVO=y # CONFIG_MACH_INCREDIBLEC is not set # CONFIG_MACH_SUPERSONIC is not set # CONFIG_MACH_QSD8X50_FFA is not set -# CONFIG_JESUS_PHONE is not set # CONFIG_HTC_HEADSET is not set CONFIG_HTC_35MM_JACK=y # CONFIG_HTC_BATTCHG is not set @@ -335,6 +348,10 @@ CONFIG_ARCH_MSM_FLASHLIGHT=y # CONFIG_HTC_HEADSET_MGR is not set # CONFIG_VIRTUAL_KPANIC_PARTITION is not set +# +# System MMU +# + # # Processor Type # @@ -357,6 +374,7 @@ CONFIG_CPU_CP15_MMU=y # CONFIG_ARM_THUMB=y CONFIG_ARM_THUMBEE=y +# CONFIG_SWP_EMULATE is not set # CONFIG_CPU_ICACHE_DISABLE is not set # CONFIG_CPU_DCACHE_DISABLE is not set # CONFIG_CPU_BPREDICT_DISABLE is not set @@ -405,6 +423,7 @@ CONFIG_FLAT_NODE_MEM_MAP=y CONFIG_HAVE_MEMBLOCK=y CONFIG_PAGEFLAGS_EXTENDED=y CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set # CONFIG_PHYS_ADDR_T_64BIT is not set CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y @@ -427,6 +446,7 @@ CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" # CONFIG_CMDLINE_FORCE is not set # CONFIG_XIP_KERNEL is not set # CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set # CONFIG_AUTO_ZRELADDR is not set # @@ -477,7 +497,6 @@ CONFIG_HAVE_AOUT=y CONFIG_PM=y # CONFIG_PM_DEBUG is not set CONFIG_PM_SLEEP=y -CONFIG_SUSPEND_NVS=y CONFIG_SUSPEND=y CONFIG_SUSPEND_FREEZER=y CONFIG_HAS_WAKELOCK=y @@ -769,6 +788,7 @@ CONFIG_NET_ACT_MIRRED=y # CONFIG_NET_CLS_IND is not set CONFIG_NET_SCH_FIFO=y # CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set # # Network testing @@ -988,6 +1008,7 @@ CONFIG_DM_DEBUG=y CONFIG_DM_CRYPT=y # CONFIG_DM_SNAPSHOT is not set # CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set # CONFIG_DM_ZERO is not set # CONFIG_DM_MULTIPATH is not set # CONFIG_DM_DELAY is not set @@ -1011,7 +1032,6 @@ CONFIG_BCM4329=m CONFIG_BCM4329_FW_PATH="/vendor/firmware/fw_bcm4329.bin" CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" # CONFIG_HOSTAP is not set -# CONFIG_TIWLAN1251 is not set # # Enable WiMAX (Networking options) to see the WiMAX drivers @@ -1097,6 +1117,7 @@ CONFIG_TOUCHSCREEN_DUPLICATED_FILTER=y # CONFIG_TOUCHSCREEN_TOUCHIT213 is not set # CONFIG_TOUCHSCREEN_TSC2007 is not set # CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set # CONFIG_TOUCHSCREEN_TPS6507X is not set CONFIG_INPUT_MISC=y # CONFIG_INPUT_AD714X is not set @@ -1112,6 +1133,7 @@ CONFIG_INPUT_GPIO=y # CONFIG_INPUT_PCF8574 is not set # CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set # CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set # CONFIG_INPUT_CAPELLA_CM3602 is not set CONFIG_INPUT_CAPELLA_CM3602_HTC=y CONFIG_LIGHTSENSOR_MICROP=y @@ -1157,6 +1179,7 @@ CONFIG_UNIX98_PTYS=y # CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set # CONFIG_LEGACY_PTYS is not set # CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set # CONFIG_IPMI_HANDLER is not set # CONFIG_HW_RANDOM is not set # CONFIG_R3964 is not set @@ -1205,6 +1228,10 @@ CONFIG_I2C_MSM=y # PPS support # # CONFIG_PPS is not set + +# +# PPS generators support +# CONFIG_ARCH_REQUIRE_GPIOLIB=y CONFIG_GPIOLIB=y # CONFIG_DEBUG_GPIO is not set @@ -1215,14 +1242,12 @@ CONFIG_GPIOLIB=y # # CONFIG_GPIO_BASIC_MMIO is not set # CONFIG_GPIO_IT8761E is not set -# CONFIG_GPIO_VX855 is not set # # I2C GPIO expanders: # # CONFIG_GPIO_MAX7300 is not set # CONFIG_GPIO_MAX732X is not set -# CONFIG_GPIO_PCA953X is not set # CONFIG_GPIO_PCF857X is not set # CONFIG_GPIO_SX150X is not set # CONFIG_GPIO_ADP5588 is not set @@ -1256,6 +1281,7 @@ CONFIG_W1_MASTER_DS2482=y # # CONFIG_W1_SLAVE_THERM is not set # CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_DS2423 is not set # CONFIG_W1_SLAVE_DS2431 is not set # CONFIG_W1_SLAVE_DS2433 is not set # CONFIG_W1_SLAVE_DS2760 is not set @@ -1269,6 +1295,8 @@ CONFIG_BATTERY_DS2784=y # CONFIG_BATTERY_BQ20Z75 is not set # CONFIG_BATTERY_BQ27x00 is not set # CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_GPIO is not set # CONFIG_HWMON is not set # CONFIG_THERMAL is not set # CONFIG_WATCHDOG is not set @@ -1291,7 +1319,7 @@ CONFIG_MFD_SUPPORT=y # CONFIG_TPS65200 is not set # CONFIG_TWL4030_CORE is not set # CONFIG_MFD_STMPE is not set -# CONFIG_MFD_TC35892 is not set +# CONFIG_MFD_TC3589X is not set # CONFIG_MFD_TMIO is not set # CONFIG_MFD_T7L66XB is not set # CONFIG_MFD_TC6387XB is not set @@ -1307,6 +1335,7 @@ CONFIG_MFD_SUPPORT=y # CONFIG_MFD_PCF50633 is not set # CONFIG_ABX500_CORE is not set # CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_WL1273_CORE is not set CONFIG_REGULATOR=y CONFIG_REGULATOR_DEBUG=y # CONFIG_REGULATOR_DUMMY is not set @@ -1336,7 +1365,20 @@ CONFIG_MEDIA_SUPPORT=y # # Multimedia drivers # -# CONFIG_IR_CORE is not set +CONFIG_RC_CORE=y +CONFIG_LIRC=y +CONFIG_RC_MAP=y +CONFIG_IR_NEC_DECODER=y +CONFIG_IR_RC5_DECODER=y +CONFIG_IR_RC6_DECODER=y +CONFIG_IR_JVC_DECODER=y +CONFIG_IR_SONY_DECODER=y +CONFIG_IR_RC5_SZ_DECODER=y +CONFIG_IR_LIRC_CODEC=y +# CONFIG_IR_IMON is not set +# CONFIG_IR_MCEUSB is not set +# CONFIG_IR_STREAMZAP is not set +# CONFIG_RC_LOOPBACK is not set # # Qualcomm MSM Camera And Video @@ -1356,7 +1398,6 @@ CONFIG_S5K3E2FX=y # CONFIG_OV8810 is not set # CONFIG_OV9665 is not set # CONFIG_S5K3H1GX is not set -# CONFIG_DAB is not set # # Graphics support @@ -1377,6 +1418,7 @@ CONFIG_FB_CFB_IMAGEBLIT=y # CONFIG_FB_SYS_IMAGEBLIT is not set # CONFIG_FB_FOREIGN_ENDIAN is not set # CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set # CONFIG_FB_SVGALIB is not set # CONFIG_FB_MACMODES is not set # CONFIG_FB_BACKLIGHT is not set @@ -1422,7 +1464,7 @@ CONFIG_HID_APPLE=y CONFIG_USB_SUPPORT=y CONFIG_USB_ARCH_HAS_HCD=y # CONFIG_USB_ARCH_HAS_OHCI is not set -# CONFIG_USB_ARCH_HAS_EHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y # CONFIG_USB is not set # CONFIG_USB_OTG_WHITELIST is not set # CONFIG_USB_OTG_BLACKLIST_HUB is not set @@ -1438,13 +1480,16 @@ CONFIG_USB_GADGET=y CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_USB_GADGET_SELECTED=y # CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA_U2O is not set # CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_CI13XXX_MSM is not set CONFIG_USB_GADGET_MSM_72K=y CONFIG_USB_MSM_72K=y # CONFIG_USB_MSM_72K_HTC is not set CONFIG_USB_GADGET_DUALSPEED=y # CONFIG_USB_ZERO is not set # CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set # CONFIG_USB_GADGETFS is not set # CONFIG_USB_FUNCTIONFS is not set # CONFIG_USB_FILE_STORAGE is not set @@ -1471,12 +1516,13 @@ CONFIG_USB_ANDROID_ACCESSORY=y # CONFIG_USB_GPIO_VBUS is not set # CONFIG_USB_ULPI is not set # CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG_72K is not set CONFIG_MMC=y # CONFIG_MMC_DEBUG is not set CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_CLKGATE is not set CONFIG_MMC_EMBEDDED_SDIO=y CONFIG_MMC_PARANOID_SD_INIT=y -# CONFIG_MMC_USE_ONLY_HOST_DEFINED_FREQUENCY is not set # # MMC/SD/SDIO Card Drivers @@ -1493,6 +1539,7 @@ CONFIG_MMC_BLOCK_DEFERRED_RESUME=y # # CONFIG_MMC_SDHCI is not set CONFIG_MMC_MSM=y +# CONFIG_MMC_DW is not set # CONFIG_MEMSTICK is not set CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y @@ -1526,6 +1573,7 @@ CONFIG_LEDS_TRIGGER_SLEEP=y # # iptables trigger is under Netfilter config (LED target) # +# CONFIG_NFC_DEVICES is not set CONFIG_SWITCH=y CONFIG_SWITCH_GPIO=y # CONFIG_ACCESSIBILITY is not set @@ -1620,13 +1668,13 @@ CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_POHMELFS is not set # CONFIG_IIO is not set # CONFIG_ZRAM is not set -# CONFIG_BATMAN_ADV is not set # CONFIG_FB_SM7XX is not set # # Texas Instruments shared transport line discipline # # CONFIG_ST_BT is not set +# CONFIG_LIRC_STAGING is not set CONFIG_MACH_NO_WESTBRIDGE=y # CONFIG_ATH6K_LEGACY is not set # CONFIG_FT1000 is not set @@ -1634,6 +1682,8 @@ CONFIG_MACH_NO_WESTBRIDGE=y # # Speakup console speech # +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set # # File systems @@ -1651,12 +1701,11 @@ CONFIG_JBD2=y CONFIG_FS_MBCACHE=y # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set -# CONFIG_FS_POSIX_ACL is not set # CONFIG_XFS_FS is not set # CONFIG_GFS2_FS is not set -# CONFIG_OCFS2_FS is not set # CONFIG_BTRFS_FS is not set # CONFIG_NILFS2_FS is not set +# CONFIG_FS_POSIX_ACL is not set CONFIG_FILE_LOCKING=y CONFIG_FSNOTIFY=y # CONFIG_DNOTIFY is not set @@ -1677,6 +1726,7 @@ CONFIG_FUSE_FS=y # CD-ROM/DVD Filesystems # # CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set # # DOS/FAT/NT Filesystems @@ -1789,7 +1839,6 @@ CONFIG_NLS_ISO8859_1=y # CONFIG_NLS_KOI8_R is not set # CONFIG_NLS_KOI8_U is not set CONFIG_NLS_UTF8=y -# CONFIG_DLM is not set # # Kernel hacking @@ -1851,15 +1900,19 @@ CONFIG_FRAME_POINTER=y # CONFIG_SYSCTL_SYSCALL_CHECK is not set # CONFIG_PAGE_POISONING is not set CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y CONFIG_HAVE_DYNAMIC_FTRACE=y CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y CONFIG_TRACING_SUPPORT=y # CONFIG_FTRACE is not set # CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set # CONFIG_ATOMIC64_SELFTEST is not set # CONFIG_SAMPLES is not set CONFIG_HAVE_ARCH_KGDB=y # CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set # CONFIG_STRICT_DEVMEM is not set # CONFIG_ARM_UNWIND is not set # CONFIG_DEBUG_USER is not set @@ -1932,7 +1985,7 @@ CONFIG_CRYPTO_HMAC=y # CONFIG_CRYPTO_CRC32C=y # CONFIG_CRYPTO_GHASH is not set -# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD4=y CONFIG_CRYPTO_MD5=y # CONFIG_CRYPTO_MICHAEL_MIC is not set # CONFIG_CRYPTO_RMD128 is not set @@ -1976,6 +2029,8 @@ CONFIG_CRYPTO_DEFLATE=y # Random Number Generation # # CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set CONFIG_CRYPTO_HW=y # CONFIG_BINARY_PRINTF is not set @@ -1993,6 +2048,8 @@ CONFIG_CRC32=y CONFIG_LIBCRC32C=y CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set CONFIG_DECOMPRESS_GZIP=y CONFIG_GENERIC_ALLOCATOR=y CONFIG_REED_SOLOMON=y diff --git a/arch/arm/configs/cyanogen_bravoc_defconfig b/arch/arm/configs/cyanogen_bravoc_defconfig index 8922ea4d2ecd4..4a7c41ad36e45 100644 --- a/arch/arm/configs/cyanogen_bravoc_defconfig +++ b/arch/arm/configs/cyanogen_bravoc_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux/arm 2.6.37.6 Kernel Configuration -# Sat Apr 30 10:30:59 2011 +# Linux/arm 2.6.38.6 Kernel Configuration +# Sun May 15 15:48:38 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -9,7 +9,6 @@ CONFIG_GENERIC_GPIO=y # CONFIG_ARCH_USES_GETTIMEOFFSET is not set CONFIG_GENERIC_CLOCKEVENTS=y CONFIG_HAVE_PROC_CPU=y -CONFIG_GENERIC_HARDIRQS=y CONFIG_STACKTRACE_SUPPORT=y CONFIG_HAVE_LATENCYTOP_SUPPORT=y CONFIG_LOCKDEP_SUPPORT=y @@ -22,7 +21,6 @@ CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y CONFIG_GENERIC_HWEIGHT=y CONFIG_GENERIC_CALIBRATE_DELAY=y CONFIG_NEED_DMA_MAP_STATE=y -CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y CONFIG_VECTORS_BASE=0xffff0000 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" CONFIG_CONSTRUCTORS=y @@ -49,7 +47,17 @@ CONFIG_KERNEL_LZMA=y # CONFIG_BSD_PROCESS_ACCT is not set # CONFIG_TASKSTATS is not set # CONFIG_AUDIT is not set -# CONFIG_HAVE_GENERIC_HARDIRQS is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED is not set +CONFIG_HAVE_SPARSE_IRQ=y +# CONFIG_GENERIC_PENDING_IRQ is not set +# CONFIG_AUTO_IRQ_AFFINITY is not set +# CONFIG_IRQ_PER_CPU is not set # CONFIG_SPARSE_IRQ is not set # @@ -81,6 +89,7 @@ CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y # CONFIG_BLK_CGROUP is not set # CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set # CONFIG_SYSFS_DEPRECATED is not set # CONFIG_RELAY is not set CONFIG_BLK_DEV_INITRD=y @@ -88,11 +97,13 @@ CONFIG_INITRAMFS_SOURCE="" CONFIG_RD_GZIP=y # CONFIG_RD_BZIP2 is not set # CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_SYSCTL=y CONFIG_ANON_INODES=y CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y CONFIG_EMBEDDED=y CONFIG_UID16=y # CONFIG_SYSCTL_SYSCALL is not set @@ -132,6 +143,7 @@ CONFIG_HAVE_KPROBES=y CONFIG_HAVE_KRETPROBES=y CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y # # GCOV-based kernel profiling @@ -214,6 +226,7 @@ CONFIG_MMU=y # CONFIG_ARCH_EP93XX is not set # CONFIG_ARCH_FOOTBRIDGE is not set # CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set # CONFIG_ARCH_STMP3XXX is not set # CONFIG_ARCH_NETX is not set # CONFIG_ARCH_H720X is not set @@ -257,6 +270,7 @@ CONFIG_ARCH_MSM=y # CONFIG_ARCH_DAVINCI is not set # CONFIG_ARCH_OMAP is not set # CONFIG_PLAT_SPEAR is not set +# CONFIG_GPIO_PCA953X is not set # CONFIG_ARCH_MSM7X00A is not set # CONFIG_ARCH_MSM7X30 is not set CONFIG_ARCH_QSD8X50=y @@ -286,7 +300,6 @@ CONFIG_MACH_BRAVOC=y # CONFIG_MACH_INCREDIBLEC is not set # CONFIG_MACH_SUPERSONIC is not set # CONFIG_MACH_QSD8X50_FFA is not set -# CONFIG_JESUS_PHONE is not set # CONFIG_HTC_HEADSET is not set CONFIG_HTC_35MM_JACK=y # CONFIG_HTC_BATTCHG is not set @@ -335,6 +348,10 @@ CONFIG_ARCH_MSM_FLASHLIGHT=y # CONFIG_HTC_HEADSET_MGR is not set # CONFIG_VIRTUAL_KPANIC_PARTITION is not set +# +# System MMU +# + # # Processor Type # @@ -357,6 +374,7 @@ CONFIG_CPU_CP15_MMU=y # CONFIG_ARM_THUMB=y CONFIG_ARM_THUMBEE=y +# CONFIG_SWP_EMULATE is not set # CONFIG_CPU_ICACHE_DISABLE is not set # CONFIG_CPU_DCACHE_DISABLE is not set # CONFIG_CPU_BPREDICT_DISABLE is not set @@ -405,6 +423,7 @@ CONFIG_FLAT_NODE_MEM_MAP=y CONFIG_HAVE_MEMBLOCK=y CONFIG_PAGEFLAGS_EXTENDED=y CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set # CONFIG_PHYS_ADDR_T_64BIT is not set CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y @@ -427,6 +446,7 @@ CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" # CONFIG_CMDLINE_FORCE is not set # CONFIG_XIP_KERNEL is not set # CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set # CONFIG_AUTO_ZRELADDR is not set # @@ -477,7 +497,6 @@ CONFIG_HAVE_AOUT=y CONFIG_PM=y # CONFIG_PM_DEBUG is not set CONFIG_PM_SLEEP=y -CONFIG_SUSPEND_NVS=y CONFIG_SUSPEND=y CONFIG_SUSPEND_FREEZER=y CONFIG_HAS_WAKELOCK=y @@ -769,6 +788,7 @@ CONFIG_NET_ACT_MIRRED=y # CONFIG_NET_CLS_IND is not set CONFIG_NET_SCH_FIFO=y # CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set # # Network testing @@ -988,6 +1008,7 @@ CONFIG_DM_DEBUG=y CONFIG_DM_CRYPT=y # CONFIG_DM_SNAPSHOT is not set # CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set # CONFIG_DM_ZERO is not set # CONFIG_DM_MULTIPATH is not set # CONFIG_DM_DELAY is not set @@ -1011,7 +1032,6 @@ CONFIG_BCM4329=m CONFIG_BCM4329_FW_PATH="/vendor/firmware/fw_bcm4329.bin" CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" # CONFIG_HOSTAP is not set -# CONFIG_TIWLAN1251 is not set # # Enable WiMAX (Networking options) to see the WiMAX drivers @@ -1097,6 +1117,7 @@ CONFIG_TOUCHSCREEN_DUPLICATED_FILTER=y # CONFIG_TOUCHSCREEN_TOUCHIT213 is not set # CONFIG_TOUCHSCREEN_TSC2007 is not set # CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set # CONFIG_TOUCHSCREEN_TPS6507X is not set CONFIG_INPUT_MISC=y # CONFIG_INPUT_AD714X is not set @@ -1112,6 +1133,7 @@ CONFIG_INPUT_GPIO=y # CONFIG_INPUT_PCF8574 is not set # CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set # CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set # CONFIG_INPUT_CAPELLA_CM3602 is not set CONFIG_INPUT_CAPELLA_CM3602_HTC=y CONFIG_LIGHTSENSOR_MICROP=y @@ -1157,6 +1179,7 @@ CONFIG_UNIX98_PTYS=y # CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set # CONFIG_LEGACY_PTYS is not set # CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set # CONFIG_IPMI_HANDLER is not set # CONFIG_HW_RANDOM is not set # CONFIG_R3964 is not set @@ -1205,6 +1228,10 @@ CONFIG_I2C_MSM=y # PPS support # # CONFIG_PPS is not set + +# +# PPS generators support +# CONFIG_ARCH_REQUIRE_GPIOLIB=y CONFIG_GPIOLIB=y # CONFIG_DEBUG_GPIO is not set @@ -1215,14 +1242,12 @@ CONFIG_GPIOLIB=y # # CONFIG_GPIO_BASIC_MMIO is not set # CONFIG_GPIO_IT8761E is not set -# CONFIG_GPIO_VX855 is not set # # I2C GPIO expanders: # # CONFIG_GPIO_MAX7300 is not set # CONFIG_GPIO_MAX732X is not set -# CONFIG_GPIO_PCA953X is not set # CONFIG_GPIO_PCF857X is not set # CONFIG_GPIO_SX150X is not set # CONFIG_GPIO_ADP5588 is not set @@ -1256,6 +1281,7 @@ CONFIG_W1_MASTER_DS2482=y # # CONFIG_W1_SLAVE_THERM is not set # CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_DS2423 is not set # CONFIG_W1_SLAVE_DS2431 is not set # CONFIG_W1_SLAVE_DS2433 is not set # CONFIG_W1_SLAVE_DS2760 is not set @@ -1269,6 +1295,8 @@ CONFIG_BATTERY_DS2784=y # CONFIG_BATTERY_BQ20Z75 is not set # CONFIG_BATTERY_BQ27x00 is not set # CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_GPIO is not set # CONFIG_HWMON is not set # CONFIG_THERMAL is not set # CONFIG_WATCHDOG is not set @@ -1291,7 +1319,7 @@ CONFIG_MFD_SUPPORT=y # CONFIG_TPS65200 is not set # CONFIG_TWL4030_CORE is not set # CONFIG_MFD_STMPE is not set -# CONFIG_MFD_TC35892 is not set +# CONFIG_MFD_TC3589X is not set # CONFIG_MFD_TMIO is not set # CONFIG_MFD_T7L66XB is not set # CONFIG_MFD_TC6387XB is not set @@ -1307,6 +1335,7 @@ CONFIG_MFD_SUPPORT=y # CONFIG_MFD_PCF50633 is not set # CONFIG_ABX500_CORE is not set # CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_WL1273_CORE is not set CONFIG_REGULATOR=y CONFIG_REGULATOR_DEBUG=y # CONFIG_REGULATOR_DUMMY is not set @@ -1336,7 +1365,20 @@ CONFIG_MEDIA_SUPPORT=y # # Multimedia drivers # -# CONFIG_IR_CORE is not set +CONFIG_RC_CORE=y +CONFIG_LIRC=y +CONFIG_RC_MAP=y +CONFIG_IR_NEC_DECODER=y +CONFIG_IR_RC5_DECODER=y +CONFIG_IR_RC6_DECODER=y +CONFIG_IR_JVC_DECODER=y +CONFIG_IR_SONY_DECODER=y +CONFIG_IR_RC5_SZ_DECODER=y +CONFIG_IR_LIRC_CODEC=y +# CONFIG_IR_IMON is not set +# CONFIG_IR_MCEUSB is not set +# CONFIG_IR_STREAMZAP is not set +# CONFIG_RC_LOOPBACK is not set # # Qualcomm MSM Camera And Video @@ -1356,7 +1398,6 @@ CONFIG_S5K3E2FX=y # CONFIG_OV8810 is not set # CONFIG_OV9665 is not set # CONFIG_S5K3H1GX is not set -# CONFIG_DAB is not set # # Graphics support @@ -1377,6 +1418,7 @@ CONFIG_FB_CFB_IMAGEBLIT=y # CONFIG_FB_SYS_IMAGEBLIT is not set # CONFIG_FB_FOREIGN_ENDIAN is not set # CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set # CONFIG_FB_SVGALIB is not set # CONFIG_FB_MACMODES is not set # CONFIG_FB_BACKLIGHT is not set @@ -1422,7 +1464,7 @@ CONFIG_HID_APPLE=y CONFIG_USB_SUPPORT=y CONFIG_USB_ARCH_HAS_HCD=y # CONFIG_USB_ARCH_HAS_OHCI is not set -# CONFIG_USB_ARCH_HAS_EHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y # CONFIG_USB is not set # CONFIG_USB_OTG_WHITELIST is not set # CONFIG_USB_OTG_BLACKLIST_HUB is not set @@ -1438,13 +1480,16 @@ CONFIG_USB_GADGET=y CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_USB_GADGET_SELECTED=y # CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA_U2O is not set # CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_CI13XXX_MSM is not set CONFIG_USB_GADGET_MSM_72K=y CONFIG_USB_MSM_72K=y # CONFIG_USB_MSM_72K_HTC is not set CONFIG_USB_GADGET_DUALSPEED=y # CONFIG_USB_ZERO is not set # CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set # CONFIG_USB_GADGETFS is not set # CONFIG_USB_FUNCTIONFS is not set # CONFIG_USB_FILE_STORAGE is not set @@ -1471,12 +1516,13 @@ CONFIG_USB_ANDROID_ACCESSORY=y # CONFIG_USB_GPIO_VBUS is not set # CONFIG_USB_ULPI is not set # CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG_72K is not set CONFIG_MMC=y # CONFIG_MMC_DEBUG is not set CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_CLKGATE is not set CONFIG_MMC_EMBEDDED_SDIO=y CONFIG_MMC_PARANOID_SD_INIT=y -# CONFIG_MMC_USE_ONLY_HOST_DEFINED_FREQUENCY is not set # # MMC/SD/SDIO Card Drivers @@ -1493,6 +1539,7 @@ CONFIG_MMC_BLOCK_DEFERRED_RESUME=y # # CONFIG_MMC_SDHCI is not set CONFIG_MMC_MSM=y +# CONFIG_MMC_DW is not set # CONFIG_MEMSTICK is not set CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y @@ -1526,6 +1573,7 @@ CONFIG_LEDS_TRIGGER_SLEEP=y # # iptables trigger is under Netfilter config (LED target) # +# CONFIG_NFC_DEVICES is not set CONFIG_SWITCH=y CONFIG_SWITCH_GPIO=y # CONFIG_ACCESSIBILITY is not set @@ -1620,13 +1668,13 @@ CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_POHMELFS is not set # CONFIG_IIO is not set # CONFIG_ZRAM is not set -# CONFIG_BATMAN_ADV is not set # CONFIG_FB_SM7XX is not set # # Texas Instruments shared transport line discipline # # CONFIG_ST_BT is not set +# CONFIG_LIRC_STAGING is not set CONFIG_MACH_NO_WESTBRIDGE=y # CONFIG_ATH6K_LEGACY is not set # CONFIG_FT1000 is not set @@ -1634,6 +1682,8 @@ CONFIG_MACH_NO_WESTBRIDGE=y # # Speakup console speech # +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set # # File systems @@ -1651,12 +1701,11 @@ CONFIG_JBD2=y CONFIG_FS_MBCACHE=y # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set -# CONFIG_FS_POSIX_ACL is not set # CONFIG_XFS_FS is not set # CONFIG_GFS2_FS is not set -# CONFIG_OCFS2_FS is not set # CONFIG_BTRFS_FS is not set # CONFIG_NILFS2_FS is not set +# CONFIG_FS_POSIX_ACL is not set CONFIG_FILE_LOCKING=y CONFIG_FSNOTIFY=y # CONFIG_DNOTIFY is not set @@ -1677,6 +1726,7 @@ CONFIG_FUSE_FS=y # CD-ROM/DVD Filesystems # # CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set # # DOS/FAT/NT Filesystems @@ -1789,7 +1839,6 @@ CONFIG_NLS_ISO8859_1=y # CONFIG_NLS_KOI8_R is not set # CONFIG_NLS_KOI8_U is not set CONFIG_NLS_UTF8=y -# CONFIG_DLM is not set # # Kernel hacking @@ -1851,15 +1900,19 @@ CONFIG_FRAME_POINTER=y # CONFIG_SYSCTL_SYSCALL_CHECK is not set # CONFIG_PAGE_POISONING is not set CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y CONFIG_HAVE_DYNAMIC_FTRACE=y CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y CONFIG_TRACING_SUPPORT=y # CONFIG_FTRACE is not set # CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set # CONFIG_ATOMIC64_SELFTEST is not set # CONFIG_SAMPLES is not set CONFIG_HAVE_ARCH_KGDB=y # CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set # CONFIG_STRICT_DEVMEM is not set # CONFIG_ARM_UNWIND is not set # CONFIG_DEBUG_USER is not set @@ -1932,7 +1985,7 @@ CONFIG_CRYPTO_HMAC=y # CONFIG_CRYPTO_CRC32C=y # CONFIG_CRYPTO_GHASH is not set -# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD4=y CONFIG_CRYPTO_MD5=y # CONFIG_CRYPTO_MICHAEL_MIC is not set # CONFIG_CRYPTO_RMD128 is not set @@ -1976,6 +2029,8 @@ CONFIG_CRYPTO_DEFLATE=y # Random Number Generation # # CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set CONFIG_CRYPTO_HW=y # CONFIG_BINARY_PRINTF is not set @@ -1993,6 +2048,8 @@ CONFIG_CRC32=y CONFIG_LIBCRC32C=y CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set CONFIG_DECOMPRESS_GZIP=y CONFIG_GENERIC_ALLOCATOR=y CONFIG_REED_SOLOMON=y diff --git a/arch/arm/configs/cyanogen_incrediblec_defconfig b/arch/arm/configs/cyanogen_incrediblec_defconfig index 6d5dcdeb6bbab..632b30e769e58 100644 --- a/arch/arm/configs/cyanogen_incrediblec_defconfig +++ b/arch/arm/configs/cyanogen_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux/arm 2.6.37.4 Kernel Configuration -# Sun Apr 10 21:21:14 2011 +# Linux/arm 2.6.38.6 Kernel Configuration +# Sun May 15 15:45:45 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -9,7 +9,6 @@ CONFIG_GENERIC_GPIO=y # CONFIG_ARCH_USES_GETTIMEOFFSET is not set CONFIG_GENERIC_CLOCKEVENTS=y CONFIG_HAVE_PROC_CPU=y -CONFIG_GENERIC_HARDIRQS=y CONFIG_STACKTRACE_SUPPORT=y CONFIG_HAVE_LATENCYTOP_SUPPORT=y CONFIG_LOCKDEP_SUPPORT=y @@ -22,7 +21,6 @@ CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y CONFIG_GENERIC_HWEIGHT=y CONFIG_GENERIC_CALIBRATE_DELAY=y CONFIG_NEED_DMA_MAP_STATE=y -CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y CONFIG_VECTORS_BASE=0xffff0000 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" CONFIG_CONSTRUCTORS=y @@ -49,7 +47,17 @@ CONFIG_KERNEL_LZMA=y # CONFIG_BSD_PROCESS_ACCT is not set # CONFIG_TASKSTATS is not set # CONFIG_AUDIT is not set -# CONFIG_HAVE_GENERIC_HARDIRQS is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED is not set +CONFIG_HAVE_SPARSE_IRQ=y +# CONFIG_GENERIC_PENDING_IRQ is not set +# CONFIG_AUTO_IRQ_AFFINITY is not set +# CONFIG_IRQ_PER_CPU is not set # CONFIG_SPARSE_IRQ is not set # @@ -80,6 +88,7 @@ CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y # CONFIG_BLK_CGROUP is not set # CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set # CONFIG_SYSFS_DEPRECATED is not set # CONFIG_RELAY is not set CONFIG_BLK_DEV_INITRD=y @@ -87,11 +96,13 @@ CONFIG_INITRAMFS_SOURCE="" CONFIG_RD_GZIP=y # CONFIG_RD_BZIP2 is not set # CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_SYSCTL=y CONFIG_ANON_INODES=y CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y CONFIG_EMBEDDED=y CONFIG_UID16=y # CONFIG_SYSCTL_SYSCALL is not set @@ -131,6 +142,7 @@ CONFIG_HAVE_KPROBES=y CONFIG_HAVE_KRETPROBES=y CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y # # GCOV-based kernel profiling @@ -212,6 +224,7 @@ CONFIG_MMU=y # CONFIG_ARCH_EP93XX is not set # CONFIG_ARCH_FOOTBRIDGE is not set # CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set # CONFIG_ARCH_STMP3XXX is not set # CONFIG_ARCH_NETX is not set # CONFIG_ARCH_H720X is not set @@ -255,6 +268,7 @@ CONFIG_ARCH_MSM=y # CONFIG_ARCH_DAVINCI is not set # CONFIG_ARCH_OMAP is not set # CONFIG_PLAT_SPEAR is not set +# CONFIG_GPIO_PCA953X is not set # CONFIG_ARCH_MSM7X00A is not set # CONFIG_ARCH_MSM7X30 is not set CONFIG_ARCH_QSD8X50=y @@ -283,7 +297,6 @@ CONFIG_MACH_BRAVO_NONE=y CONFIG_MACH_INCREDIBLEC=y # CONFIG_MACH_SUPERSONIC is not set # CONFIG_MACH_QSD8X50_FFA is not set -# CONFIG_JESUS_PHONE is not set # CONFIG_HTC_HEADSET is not set # CONFIG_HTC_35MM_JACK is not set CONFIG_HTC_BATTCHG=y @@ -338,6 +351,10 @@ CONFIG_HTC_HEADSET_GPIO=y CONFIG_HTC_HEADSET_MICROP=y # CONFIG_VIRTUAL_KPANIC_PARTITION is not set +# +# System MMU +# + # # Processor Type # @@ -360,6 +377,7 @@ CONFIG_CPU_CP15_MMU=y # CONFIG_ARM_THUMB=y CONFIG_ARM_THUMBEE=y +# CONFIG_SWP_EMULATE is not set # CONFIG_CPU_ICACHE_DISABLE is not set # CONFIG_CPU_DCACHE_DISABLE is not set # CONFIG_CPU_BPREDICT_DISABLE is not set @@ -408,6 +426,7 @@ CONFIG_FLAT_NODE_MEM_MAP=y CONFIG_HAVE_MEMBLOCK=y CONFIG_PAGEFLAGS_EXTENDED=y CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set # CONFIG_PHYS_ADDR_T_64BIT is not set CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y @@ -430,6 +449,7 @@ CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" # CONFIG_CMDLINE_FORCE is not set # CONFIG_XIP_KERNEL is not set # CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set # CONFIG_AUTO_ZRELADDR is not set # @@ -480,7 +500,6 @@ CONFIG_HAVE_AOUT=y CONFIG_PM=y # CONFIG_PM_DEBUG is not set CONFIG_PM_SLEEP=y -CONFIG_SUSPEND_NVS=y CONFIG_SUSPEND=y CONFIG_SUSPEND_FREEZER=y CONFIG_HAS_WAKELOCK=y @@ -778,6 +797,7 @@ CONFIG_NET_ACT_MIRRED=y CONFIG_NET_SCH_FIFO=y # CONFIG_DCB is not set CONFIG_DNS_RESOLVER=y +# CONFIG_BATMAN_ADV is not set # # Network testing @@ -1003,6 +1023,7 @@ CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=y # CONFIG_DM_SNAPSHOT is not set # CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set # CONFIG_DM_ZERO is not set # CONFIG_DM_MULTIPATH is not set # CONFIG_DM_DELAY is not set @@ -1043,7 +1064,6 @@ CONFIG_BCM4329=m CONFIG_BCM4329_FW_PATH="/vendor/firmware/fw_bcm4329.bin" CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" # CONFIG_HOSTAP is not set -# CONFIG_TIWLAN1251 is not set # # Enable WiMAX (Networking options) to see the WiMAX drivers @@ -1130,6 +1150,7 @@ CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT=y # CONFIG_TOUCHSCREEN_TOUCHIT213 is not set # CONFIG_TOUCHSCREEN_TSC2007 is not set # CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set # CONFIG_TOUCHSCREEN_TPS6507X is not set CONFIG_INPUT_MISC=y # CONFIG_INPUT_AD714X is not set @@ -1145,6 +1166,7 @@ CONFIG_INPUT_GPIO=y # CONFIG_INPUT_PCF8574 is not set # CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set # CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set # CONFIG_INPUT_CAPELLA_CM3602 is not set # CONFIG_INPUT_CAPELLA_CM3602_HTC is not set CONFIG_LIGHTSENSOR_MICROP=y @@ -1187,10 +1209,12 @@ CONFIG_SERIAL_BCM_BT_LPM=y # CONFIG_SERIAL_TIMBERDALE is not set # CONFIG_SERIAL_ALTERA_JTAGUART is not set # CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_IFX6X60 is not set CONFIG_UNIX98_PTYS=y # CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set # CONFIG_LEGACY_PTYS is not set # CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set # CONFIG_IPMI_HANDLER is not set # CONFIG_HW_RANDOM is not set # CONFIG_R3964 is not set @@ -1242,6 +1266,7 @@ CONFIG_SPI_MASTER=y # # CONFIG_SPI_BITBANG is not set # CONFIG_SPI_GPIO is not set +# CONFIG_SPI_PXA2XX_PCI is not set CONFIG_SPI_QSD=y # CONFIG_SPI_XILINX is not set # CONFIG_SPI_DESIGNWARE is not set @@ -1256,6 +1281,10 @@ CONFIG_SPI_QSD=y # PPS support # # CONFIG_PPS is not set + +# +# PPS generators support +# CONFIG_ARCH_REQUIRE_GPIOLIB=y CONFIG_GPIOLIB=y # CONFIG_DEBUG_GPIO is not set @@ -1266,14 +1295,12 @@ CONFIG_GPIOLIB=y # # CONFIG_GPIO_BASIC_MMIO is not set # CONFIG_GPIO_IT8761E is not set -# CONFIG_GPIO_VX855 is not set # # I2C GPIO expanders: # # CONFIG_GPIO_MAX7300 is not set # CONFIG_GPIO_MAX732X is not set -# CONFIG_GPIO_PCA953X is not set # CONFIG_GPIO_PCF857X is not set # CONFIG_GPIO_SX150X is not set # CONFIG_GPIO_ADP5588 is not set @@ -1307,6 +1334,8 @@ CONFIG_POWER_SUPPLY=y # CONFIG_BATTERY_BQ20Z75 is not set # CONFIG_BATTERY_BQ27x00 is not set # CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_GPIO is not set # CONFIG_HWMON is not set # CONFIG_THERMAL is not set # CONFIG_WATCHDOG is not set @@ -1329,7 +1358,7 @@ CONFIG_MFD_SUPPORT=y # CONFIG_TPS65200 is not set # CONFIG_TWL4030_CORE is not set # CONFIG_MFD_STMPE is not set -# CONFIG_MFD_TC35892 is not set +# CONFIG_MFD_TC3589X is not set # CONFIG_MFD_TMIO is not set # CONFIG_MFD_T7L66XB is not set # CONFIG_MFD_TC6387XB is not set @@ -1348,6 +1377,7 @@ CONFIG_MFD_SUPPORT=y # CONFIG_ABX500_CORE is not set # CONFIG_EZX_PCAP is not set # CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_WL1273_CORE is not set CONFIG_REGULATOR=y # CONFIG_REGULATOR_DEBUG is not set # CONFIG_REGULATOR_DUMMY is not set @@ -1365,6 +1395,7 @@ CONFIG_REGULATOR_TPS65023=y # CONFIG_REGULATOR_TPS6507X is not set # CONFIG_REGULATOR_ISL6271A is not set # CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_TPS6524X is not set CONFIG_MEDIA_SUPPORT=y # @@ -1377,7 +1408,20 @@ CONFIG_MEDIA_SUPPORT=y # # Multimedia drivers # -# CONFIG_IR_CORE is not set +CONFIG_RC_CORE=y +CONFIG_LIRC=y +CONFIG_RC_MAP=y +CONFIG_IR_NEC_DECODER=y +CONFIG_IR_RC5_DECODER=y +CONFIG_IR_RC6_DECODER=y +CONFIG_IR_JVC_DECODER=y +CONFIG_IR_SONY_DECODER=y +CONFIG_IR_RC5_SZ_DECODER=y +CONFIG_IR_LIRC_CODEC=y +# CONFIG_IR_IMON is not set +# CONFIG_IR_MCEUSB is not set +# CONFIG_IR_STREAMZAP is not set +# CONFIG_RC_LOOPBACK is not set # # Qualcomm MSM Camera And Video @@ -1397,7 +1441,6 @@ CONFIG_720P_CAMERA=y CONFIG_OV8810=y # CONFIG_OV9665 is not set # CONFIG_S5K3H1GX is not set -# CONFIG_DAB is not set # # Graphics support @@ -1418,6 +1461,7 @@ CONFIG_FB_CFB_IMAGEBLIT=y # CONFIG_FB_SYS_IMAGEBLIT is not set # CONFIG_FB_FOREIGN_ENDIAN is not set # CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set # CONFIG_FB_SVGALIB is not set # CONFIG_FB_MACMODES is not set # CONFIG_FB_BACKLIGHT is not set @@ -1465,7 +1509,7 @@ CONFIG_HID_APPLE=y CONFIG_USB_SUPPORT=y CONFIG_USB_ARCH_HAS_HCD=y # CONFIG_USB_ARCH_HAS_OHCI is not set -# CONFIG_USB_ARCH_HAS_EHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y # CONFIG_USB is not set # CONFIG_USB_OTG_WHITELIST is not set # CONFIG_USB_OTG_BLACKLIST_HUB is not set @@ -1480,13 +1524,16 @@ CONFIG_USB_GADGET=y CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_USB_GADGET_SELECTED=y # CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA_U2O is not set # CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_CI13XXX_MSM is not set CONFIG_USB_GADGET_MSM_72K=y CONFIG_USB_MSM_72K=y CONFIG_USB_MSM_72K_HTC=y CONFIG_USB_GADGET_DUALSPEED=y # CONFIG_USB_ZERO is not set # CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set # CONFIG_USB_GADGETFS is not set # CONFIG_USB_FUNCTIONFS is not set # CONFIG_USB_FILE_STORAGE is not set @@ -1501,6 +1548,7 @@ CONFIG_USB_ANDROID_MASS_STORAGE=y # CONFIG_USB_ANDROID_MTP is not set CONFIG_USB_ANDROID_RNDIS=y # CONFIG_USB_ANDROID_RNDIS_WCEIS is not set +# CONFIG_USB_ANDROID_ACCESSORY is not set # CONFIG_USB_CDC_COMPOSITE is not set # CONFIG_USB_G_MULTI is not set # CONFIG_USB_G_HID is not set @@ -1515,12 +1563,13 @@ CONFIG_USB_ANDROID_RNDIS=y # CONFIG_USB_GPIO_VBUS is not set # CONFIG_USB_ULPI is not set # CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG_72K is not set CONFIG_MMC=y # CONFIG_MMC_DEBUG is not set CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_CLKGATE is not set CONFIG_MMC_EMBEDDED_SDIO=y CONFIG_MMC_PARANOID_SD_INIT=y -# CONFIG_MMC_USE_ONLY_HOST_DEFINED_FREQUENCY is not set # # MMC/SD/SDIO Card Drivers @@ -1538,6 +1587,7 @@ CONFIG_MMC_BLOCK_DEFERRED_RESUME=y # CONFIG_MMC_SDHCI is not set CONFIG_MMC_MSM=y # CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set # CONFIG_MEMSTICK is not set CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y @@ -1572,6 +1622,7 @@ CONFIG_LEDS_TRIGGER_SLEEP=y # # iptables trigger is under Netfilter config (LED target) # +# CONFIG_NFC_DEVICES is not set CONFIG_SWITCH=y CONFIG_SWITCH_GPIO=y # CONFIG_ACCESSIBILITY is not set @@ -1674,14 +1725,13 @@ CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_POHMELFS is not set # CONFIG_IIO is not set # CONFIG_ZRAM is not set -# CONFIG_BATMAN_ADV is not set # CONFIG_FB_SM7XX is not set # # Texas Instruments shared transport line discipline # # CONFIG_ST_BT is not set -# CONFIG_ADIS16255 is not set +# CONFIG_LIRC_STAGING is not set CONFIG_MACH_NO_WESTBRIDGE=y # CONFIG_ATH6K_LEGACY is not set # CONFIG_FT1000 is not set @@ -1689,6 +1739,8 @@ CONFIG_MACH_NO_WESTBRIDGE=y # # Speakup console speech # +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set # # File systems @@ -1705,12 +1757,11 @@ CONFIG_JBD2=y CONFIG_FS_MBCACHE=y # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set -# CONFIG_FS_POSIX_ACL is not set # CONFIG_XFS_FS is not set # CONFIG_GFS2_FS is not set -# CONFIG_OCFS2_FS is not set # CONFIG_BTRFS_FS is not set # CONFIG_NILFS2_FS is not set +# CONFIG_FS_POSIX_ACL is not set CONFIG_FILE_LOCKING=y CONFIG_FSNOTIFY=y # CONFIG_DNOTIFY is not set @@ -1730,6 +1781,7 @@ CONFIG_INOTIFY_USER=y # CD-ROM/DVD Filesystems # # CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set # # DOS/FAT/NT Filesystems @@ -1845,7 +1897,6 @@ CONFIG_NLS_ISO8859_1=y # CONFIG_NLS_KOI8_R is not set # CONFIG_NLS_KOI8_U is not set # CONFIG_NLS_UTF8 is not set -# CONFIG_DLM is not set # # Kernel hacking @@ -1905,14 +1956,18 @@ CONFIG_DEBUG_INFO=y # CONFIG_SYSCTL_SYSCALL_CHECK is not set # CONFIG_PAGE_POISONING is not set CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y CONFIG_HAVE_DYNAMIC_FTRACE=y CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y CONFIG_TRACING_SUPPORT=y # CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set # CONFIG_ATOMIC64_SELFTEST is not set # CONFIG_SAMPLES is not set CONFIG_HAVE_ARCH_KGDB=y # CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set # CONFIG_STRICT_DEVMEM is not set CONFIG_ARM_UNWIND=y # CONFIG_DEBUG_USER is not set @@ -1986,7 +2041,7 @@ CONFIG_CRYPTO_HMAC=y # CONFIG_CRYPTO_CRC32C=y # CONFIG_CRYPTO_GHASH is not set -# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD4=y CONFIG_CRYPTO_MD5=y # CONFIG_CRYPTO_MICHAEL_MIC is not set # CONFIG_CRYPTO_RMD128 is not set @@ -2030,6 +2085,8 @@ CONFIG_CRYPTO_DEFLATE=y # Random Number Generation # # CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set CONFIG_CRYPTO_HW=y # CONFIG_BINARY_PRINTF is not set @@ -2047,6 +2104,8 @@ CONFIG_CRC32=y CONFIG_LIBCRC32C=y CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set CONFIG_DECOMPRESS_GZIP=y CONFIG_GENERIC_ALLOCATOR=y CONFIG_REED_SOLOMON=y diff --git a/arch/arm/configs/cyanogen_mahimahi_defconfig b/arch/arm/configs/cyanogen_mahimahi_defconfig new file mode 100644 index 0000000000000..debd4c7282dde --- /dev/null +++ b/arch/arm/configs/cyanogen_mahimahi_defconfig @@ -0,0 +1,2064 @@ +# +# Automatically generated make config: don't edit +# Linux/arm 2.6.38.6 Kernel Configuration +# Sun May 15 15:47:00 2011 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="-cyanogenmod" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +CONFIG_KERNEL_LZMA=y +# CONFIG_KERNEL_LZO is not set +# CONFIG_SWAP is not set +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED is not set +CONFIG_HAVE_SPARSE_IRQ=y +# CONFIG_GENERIC_PENDING_IRQ is not set +# CONFIG_AUTO_IRQ_AFFINITY is not set +# CONFIG_IRQ_PER_CPU is not set +# CONFIG_SPARSE_IRQ is not set + +# +# RCU Subsystem +# +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +CONFIG_TINY_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_TRACE is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_RCU_BOOST=y +CONFIG_RCU_BOOST_PRIO=1 +CONFIG_RCU_BOOST_DELAY=500 +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +# CONFIG_CGROUP_NS is not set +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +# CONFIG_CGROUP_MEM_RES_CTLR is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +# CONFIG_BLK_CGROUP is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_ASHMEM=y +CONFIG_AIO=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_IOSCHED_BFQ=y +# CONFIG_CGROUP_BFQIO is not set +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +CONFIG_DEFAULT_BFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="bfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +CONFIG_ARCH_MSM=y +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_S5PV310 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_TCC_926 is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_ARCH_MSM7X00A is not set +# CONFIG_ARCH_MSM7X30 is not set +CONFIG_ARCH_QSD8X50=y +# CONFIG_ARCH_MSM8X60 is not set +CONFIG_ARCH_MSM_SCORPION=y +CONFIG_HAS_MSM_DEBUG_UART_PHYS=y +CONFIG_MSM_MDP31=y +# CONFIG_PERFLOCK is not set + +# +# Qualcomm MSM Board Type +# +# CONFIG_MACH_SWORDFISH is not set +CONFIG_MACH_QSD8X50_SURF=y +# CONFIG_MACH_QSD8X50A_ST1_5 is not set +CONFIG_MSM_DEBUG_UART=1 +# CONFIG_MSM_DEBUG_UART_NONE is not set +CONFIG_MSM_DEBUG_UART1=y +# CONFIG_MSM_DEBUG_UART2 is not set +# CONFIG_MSM_DEBUG_UART3 is not set +CONFIG_MSM_PROC_COMM=y +CONFIG_MACH_MAHIMAHI=y +CONFIG_MACH_BRAVO_NONE=y +# CONFIG_MACH_BRAVO is not set +# CONFIG_MACH_BRAVOC is not set +# CONFIG_MACH_INCREDIBLE is not set +# CONFIG_MACH_INCREDIBLEC is not set +# CONFIG_MACH_SUPERSONIC is not set +# CONFIG_MACH_QSD8X50_FFA is not set +# CONFIG_HTC_HEADSET is not set +CONFIG_HTC_35MM_JACK=y +# CONFIG_HTC_BATTCHG is not set +CONFIG_HTC_PWRSPLY=y +# CONFIG_HTC_PWRSINK is not set +# CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set +# CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_MSM7X00A_USE_GP_TIMER=y +# CONFIG_MSM7X00A_USE_DG_TIMER is not set +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=1 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_FIQ_SUPPORT=y +# CONFIG_MSM_SERIAL_DEBUGGER is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_DAL=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y +CONFIG_MSM_CPU_FREQ_MAX=998400 +CONFIG_MSM_CPU_FREQ_MIN=245000 +# CONFIG_AXI_SCREEN_POLICY is not set +# CONFIG_MSM_HW3D is not set +CONFIG_HTC_ACOUSTIC_QSD=y +CONFIG_MSM_QDSP6=y +CONFIG_WIFI_CONTROL_FUNC=y +# CONFIG_WIFI_MEM_PREALLOC is not set +CONFIG_ARCH_MSM_FLASHLIGHT=y +# CONFIG_MICROP_COMMON is not set +# CONFIG_HTC_HEADSET_MGR is not set +# CONFIG_VIRTUAL_KPANIC_PARTITION is not set + +# +# System MMU +# + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_VERIFY_PERMISSION_FAULT=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_CPU_HAS_PMU=y +CONFIG_ARM_ERRATA_430973=y +CONFIG_ARM_ERRATA_458693=y +CONFIG_ARM_ERRATA_460075=y +CONFIG_ARM_ERRATA_743622=y +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_VMALLOC_RESERVE=0x08000000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_NEED_PER_CPU_KM=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +# CONFIG_CPU_FREQ_DEBUG is not set +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM_OPS=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +CONFIG_INET_ESP=y +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_LED=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +CONFIG_NETFILTER_XT_MATCH_OWNER=y +CONFIG_NETFILTER_XT_MATCH_POLICY=y +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +# CONFIG_NF_NAT_SNMP_BASIC is not set +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +CONFIG_NF_NAT_H323=y +CONFIG_NF_NAT_SIP=y +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_RAW is not set +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV6 is not set +# CONFIG_NF_CONNTRACK_IPV6 is not set +# CONFIG_IP6_NF_QUEUE is not set +# CONFIG_IP6_NF_IPTABLES is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +CONFIG_NET_SCH_INGRESS=y + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +# CONFIG_GACT_PROB is not set +CONFIG_NET_ACT_MIRRED=y +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_PRIV=y +# CONFIG_CFG80211 is not set +CONFIG_WIRELESS_EXT_SYSFS=y +# CONFIG_LIB80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# + +# +# Some wireless drivers require a rate control algorithm +# +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_MSM_NAND=y +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set +CONFIG_MISC_DEVICES=y +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_KERNEL_DEBUGGER_CORE=y +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +CONFIG_SENSORS_AKM8973=y +# CONFIG_SENSORS_AKM8976 is not set +CONFIG_VP_A1026=y +# CONFIG_DS1682 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_WL127X_RFKILL is not set +# CONFIG_APANIC is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_IFB=y +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set +# CONFIG_MII is not set +# CONFIG_PHYLIB is not set +# CONFIG_NET_ETHERNET is not set +CONFIG_NETDEV_1000=y +# CONFIG_STMMAC_ETH is not set +CONFIG_NETDEV_10000=y +CONFIG_WLAN=y +CONFIG_BCM4329=m +CONFIG_BCM4329_FW_PATH="/system/etc/firmware/fw_bcm4329.bin" +CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" +# CONFIG_HOSTAP is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set + +# +# CAIF transport drivers +# +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +# CONFIG_PPPOE is not set +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_GAN_ETH=y +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_ATMEL is not set +# CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELAN_I2C_8232 is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_QT602240 is not set +# CONFIG_TOUCHSCREEN_MSM is not set +CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y +CONFIG_TOUCHSCREEN_DUPLICATED_FILTER=y +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set +CONFIG_INPUT_CAPELLA_CM3602=y +# CONFIG_INPUT_CAPELLA_CM3602_HTC is not set +# CONFIG_LIGHTSENSOR_MICROP is not set +# CONFIG_INPUT_OPTICALJOYSTICK is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_BCM_BT_LPM=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_MSM=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_SPI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_BASIC_MMIO is not set +# CONFIG_GPIO_IT8761E is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +CONFIG_W1=y + +# +# 1-wire Bus Masters +# +CONFIG_W1_MASTER_DS2482=y +# CONFIG_W1_MASTER_DS1WM is not set +# CONFIG_W1_MASTER_GPIO is not set + +# +# 1-wire Slaves +# +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_DS2423 is not set +# CONFIG_W1_SLAVE_DS2431 is not set +# CONFIG_W1_SLAVE_DS2433 is not set +# CONFIG_W1_SLAVE_DS2760 is not set +# CONFIG_W1_SLAVE_BQ27000 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2782 is not set +CONFIG_BATTERY_DS2784=y +# CONFIG_BATTERY_BQ20Z75 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_MFD_SUPPORT=y +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_TPS65200 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_WL1273_CORE is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +CONFIG_REGULATOR_TPS65023=y +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_AD5398 is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +CONFIG_RC_CORE=y +CONFIG_LIRC=y +CONFIG_RC_MAP=y +CONFIG_IR_NEC_DECODER=y +CONFIG_IR_RC5_DECODER=y +CONFIG_IR_RC6_DECODER=y +CONFIG_IR_JVC_DECODER=y +CONFIG_IR_SONY_DECODER=y +CONFIG_IR_RC5_SZ_DECODER=y +CONFIG_IR_LIRC_CODEC=y +# CONFIG_IR_IMON is not set +# CONFIG_IR_MCEUSB is not set +# CONFIG_IR_STREAMZAP is not set +# CONFIG_RC_LOOPBACK is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +CONFIG_720P_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set + +# +# Camera Sensor Selection +# +# CONFIG_MT9T013 is not set +# CONFIG_MT9D112 is not set +# CONFIG_MT9P012 is not set +CONFIG_S5K3E2FX=y +# CONFIG_S5K6AAFX is not set +# CONFIG_OV8810 is not set +# CONFIG_OV9665 is not set +# CONFIG_S5K3H1GX is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_LEGACY_MDP=y +CONFIG_FB_MSM_MDP_PPP=y +CONFIG_FB_MSM_LCDC=y +# CONFIG_FB_MSM_MDDI is not set +CONFIG_GPU_MSM_KGSL=y +CONFIG_MSM_KGSL_MMU=y +# CONFIG_MSM_HDMI is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set +# CONFIG_HID_PID is not set + +# +# Special HID drivers +# +CONFIG_HID_APPLE=y +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_WACOM is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +# CONFIG_USB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA_U2O is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_CI13XXX_MSM is not set +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_MSM_72K=y +# CONFIG_USB_MSM_72K_HTC is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_ANDROID=y +# CONFIG_USB_ANDROID_ACM is not set +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_DIAG=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +# CONFIG_USB_ANDROID_MTP is not set +CONFIG_USB_ANDROID_RNDIS=y +CONFIG_USB_ANDROID_RNDIS_WCEIS=y +CONFIG_USB_ANDROID_ACCESSORY=y +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG_72K is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_CLKGATE is not set +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_MSM=y +# CONFIG_MMC_DW is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +CONFIG_LEDS_CPLD=y +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +CONFIG_LEDS_TRIGGER_SLEEP=y + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_NFC_DEVICES is not set +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_MSM7X00A=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_ECHO is not set +# CONFIG_BRCM80211 is not set +# CONFIG_COMEDI is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE=128 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE=16 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE=8 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_POHMELFS is not set +# CONFIG_IIO is not set +# CONFIG_ZRAM is not set +# CONFIG_FB_SM7XX is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_ST_BT is not set +# CONFIG_LIRC_STAGING is not set +CONFIG_MACH_NO_WESTBRIDGE=y +# CONFIG_ATH6K_LEGACY is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +# CONFIG_DNOTIFY is not set +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=y +# CONFIG_CUSE is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=y +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_WEAK_PW_HASH=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_ACL is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +CONFIG_SCHED_DEBUG=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_BKL is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_ARM_UNWIND is not set +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_ERRORS=y +CONFIG_DEBUG_STACK_USAGE=y +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/arch/arm/configs/cyanogen_supersonic_defconfig b/arch/arm/configs/cyanogen_supersonic_defconfig index 7f3d6899d6a82..b58a6ece7a390 100644 --- a/arch/arm/configs/cyanogen_supersonic_defconfig +++ b/arch/arm/configs/cyanogen_supersonic_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux/arm 2.6.38.4 Kernel Configuration -# Sun May 1 20:26:43 2011 +# Linux/arm 2.6.38.6 Kernel Configuration +# Sun May 15 15:49:12 2011 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -297,6 +297,11 @@ CONFIG_MSM_DEBUG_UART_NONE=y # CONFIG_MSM_DEBUG_UART3 is not set CONFIG_MSM_PROC_COMM=y # CONFIG_MACH_MAHIMAHI is not set +CONFIG_MACH_BRAVO_NONE=y +# CONFIG_MACH_BRAVO is not set +# CONFIG_MACH_BRAVOC is not set +# CONFIG_MACH_INCREDIBLE is not set +# CONFIG_MACH_INCREDIBLEC is not set CONFIG_MACH_SUPERSONIC=y # CONFIG_MACH_QSD8X50_FFA is not set # CONFIG_HTC_HEADSET is not set @@ -1158,7 +1163,9 @@ CONFIG_INPUT_GPIO=y # CONFIG_INPUT_ADXL34X is not set # CONFIG_INPUT_CMA3000 is not set # CONFIG_INPUT_CAPELLA_CM3602 is not set +# CONFIG_INPUT_CAPELLA_CM3602_HTC is not set CONFIG_LIGHTSENSOR_MICROP=y +# CONFIG_INPUT_OPTICALJOYSTICK is not set # # Hardware I/O ports From 573d5b4419e270ce051ea5d10a6b8d88659e1c10 Mon Sep 17 00:00:00 2001 From: Dmitry ORNATSKYY Date: Wed, 18 May 2011 13:58:44 -0700 Subject: [PATCH 1718/2556] net: wireless: bcm4329: Update example locale table Signed-off-by: Dmitry Shmidt --- .../net/wireless/bcm4329/dhd_custom_gpio.c | 54 ++++++++++++++----- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/bcm4329/dhd_custom_gpio.c b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c index 5aafd13f6d305..8c4b186656007 100644 --- a/drivers/net/wireless/bcm4329/dhd_custom_gpio.c +++ b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c @@ -183,22 +183,48 @@ dhd_custom_get_mac_address(unsigned char *buf) const struct cntry_locales_custom translate_custom_table[] = { /* Table should be filled out based on custom platform regulatory requirement */ #ifdef EXAMPLE_TABLE - {"", "XY", 4} /* universal */ + {"", "XY", 4}, /* universal */ {"US", "US", 69}, /* input ISO "US" to : US regrev 69 */ {"CA", "US", 69}, /* input ISO "CA" to : US regrev 69 */ - {"EU", "EU", 5}, /* input ISO "EU" to : EU regrev 05 */ - {"FR", "EU", 5}, - {"DE", "EU", 5}, - {"IR", "EU", 5}, - {"UK", "EU", 5}, /* input ISO "UK" to : EU regrev 05 */ - {"KR", "XY", 3}, - {"AU", "XY", 3}, - {"CN", "XY", 3}, /* input ISO "CN" to : XY regrev 03 */ - {"HK", "XY", 3}, - {"TW", "XY", 3}, - {"BR", "XY", 3}, - {"MX", "XY", 3}, - {"AR", "XY", 3} + {"EU", "EU", 5}, /* European union countries */ + {"AT", "EU", 5}, + {"BE", "EU", 5}, + {"BG", "EU", 5}, + {"CY", "EU", 5}, + {"CZ", "EU", 5}, + {"DK", "EU", 5}, + {"EE", "EU", 5}, + {"FI", "EU", 5}, + {"FR", "EU", 5}, + {"DE", "EU", 5}, + {"GR", "EU", 5}, + {"HU", "EU", 5}, + {"IE", "EU", 5}, + {"IT", "EU", 5}, + {"LV", "EU", 5}, + {"LI", "EU", 5}, + {"LT", "EU", 5}, + {"LU", "EU", 5}, + {"MT", "EU", 5}, + {"NL", "EU", 5}, + {"PL", "EU", 5}, + {"PT", "EU", 5}, + {"RO", "EU", 5}, + {"SK", "EU", 5}, + {"SI", "EU", 5}, + {"ES", "EU", 5}, + {"SE", "EU", 5}, + {"GB", "EU", 5}, /* input ISO "GB" to : EU regrev 05 */ + {"IL", "IL", 0}, + {"CH", "CH", 0}, + {"TR", "TR", 0}, + {"NO", "NO", 0}, + {"KR", "XY", 3}, + {"AU", "XY", 3}, + {"CN", "XY", 3}, /* input ISO "CN" to : XY regrev 03 */ + {"TW", "XY", 3}, + {"AR", "XY", 3}, + {"MX", "XY", 3} #endif /* EXAMPLE_TABLE */ }; From b9c8360b42534c6762a2782dca07511b932cf4c3 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Thu, 19 May 2011 22:17:58 -0400 Subject: [PATCH 1719/2556] drivers: media: video: msm: fix cam for mahimahi --- drivers/media/video/msm/ov8810.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/msm/ov8810.c b/drivers/media/video/msm/ov8810.c index fc7fb80993d2f..e719399f89a80 100644 --- a/drivers/media/video/msm/ov8810.c +++ b/drivers/media/video/msm/ov8810.c @@ -2053,10 +2053,6 @@ static int ov8810_sensor_open_init(struct msm_camera_sensor_info *data) pr_info("[CAM]doing clk switch (ov8810)\n"); if(data->camera_clk_switch != NULL) data->camera_clk_switch(); - - /* enable mclk first */ - msm_camio_clk_rate_set(OV8810_DEFAULT_CLOCK_RATE); - msleep(20); msm_camio_camif_pad_reg_reset(); msleep(20); @@ -2071,6 +2067,11 @@ static int ov8810_sensor_open_init(struct msm_camera_sensor_info *data) gpio_free(data->sensor_pwd); msleep(5); + /* enable mclk first */ + msm_camio_clk_rate_set(OV8810_DEFAULT_CLOCK_RATE); + msm_camio_camif_pad_reg_reset(); + msleep(3); + /*Pull reset*/ rc = gpio_request(data->sensor_reset, "ov8810"); if (!rc) gpio_direction_output(data->sensor_reset, 1); From 96bea36718ae2f617dbbab2587acd6c7c0c9d3df Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 27 Apr 2011 13:25:51 -0400 Subject: [PATCH 1720/2556] cifs: change bleft in decode_unicode_ssetup back to signed type commit bfacf2225a955bea9c41c707fc72ba16009674a0 upstream. The buffer length checks in this function depend on this value being a signed data type, but 690c522fa converted it to an unsigned type. Also, eliminate a problem with the null termination check in the same function. cifs_strndup_from_ucs handles that situation correctly already, and the existing check could potentially lead to a buffer overrun since it increments bleft without checking to see whether it falls off the end of the buffer. Reported-and-Acked-by: David Howells Signed-off-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/sess.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 16765703131be..7e2dc59bf0ff5 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -277,7 +277,7 @@ static void ascii_ssetup_strings(char **pbcc_area, struct cifsSesInfo *ses, } static void -decode_unicode_ssetup(char **pbcc_area, __u16 bleft, struct cifsSesInfo *ses, +decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifsSesInfo *ses, const struct nls_table *nls_cp) { int len; @@ -285,19 +285,6 @@ decode_unicode_ssetup(char **pbcc_area, __u16 bleft, struct cifsSesInfo *ses, cFYI(1, "bleft %d", bleft); - /* - * Windows servers do not always double null terminate their final - * Unicode string. Check to see if there are an uneven number of bytes - * left. If so, then add an extra NULL pad byte to the end of the - * response. - * - * See section 2.7.2 in "Implementing CIFS" for details - */ - if (bleft % 2) { - data[bleft] = 0; - ++bleft; - } - kfree(ses->serverOS); ses->serverOS = cifs_strndup_from_ucs(data, bleft, true, nls_cp); cFYI(1, "serverOS=%s", ses->serverOS); From 61e16b8c3e8f5657403acabbb5d4eb42e2e6b0a1 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 27 Apr 2011 13:25:51 -0400 Subject: [PATCH 1721/2556] cifs: check for bytes_remaining going to zero in CIFS_SessSetup commit fcda7f4578bbf9717444ca6da8a421d21489d078 upstream. It's possible that when we go to decode the string area in the SESSION_SETUP response, that bytes_remaining will be 0. Decrementing it at that point will mean that it can go "negative" and wrap. Check for a bytes_remaining value of 0, and don't try to decode the string area if that's the case. Reported-and-Acked-by: David Howells Signed-off-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/sess.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 7e2dc59bf0ff5..894076fbb76c8 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -917,7 +917,9 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, } /* BB check if Unicode and decode strings */ - if (smb_buf->Flags2 & SMBFLG2_UNICODE) { + if (bytes_remaining == 0) { + /* no string area to decode, do nothing */ + } else if (smb_buf->Flags2 & SMBFLG2_UNICODE) { /* unicode string area must be word-aligned */ if (((unsigned long) bcc_ptr - (unsigned long) smb_buf) % 2) { ++bcc_ptr; From e025b48277c11706feb296d535b15ded500fbfdd Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 27 Apr 2011 13:29:49 -0400 Subject: [PATCH 1722/2556] cifs: sanitize length checking in coalesce_t2 (try #3) commit 2a2047bc94d0efc316401170c3d078d9edc20dc4 upstream. There are a couple of places in this code where these values can wrap or go negative, and that could potentially end up overflowing the buffer. Ensure that that doesn't happen. Do all of the length calculation and checks first, and only perform the memcpy after they pass. Also, increase some stack variables to 32 bits to ensure that they don't wrap without being detected. Finally, change the error codes to be a bit more descriptive of any problems detected. -EINVAL isn't very accurate. Reported-and-Acked-by: David Howells Signed-off-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/connect.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index d3d836d9c5a29..d3433fc8e9413 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -275,7 +275,8 @@ static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) char *data_area_of_target; char *data_area_of_buf2; int remaining; - __u16 byte_count, total_data_size, total_in_buf, total_in_buf2; + unsigned int byte_count, total_in_buf; + __u16 total_data_size, total_in_buf2; total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); @@ -288,7 +289,7 @@ static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) remaining = total_data_size - total_in_buf; if (remaining < 0) - return -EINVAL; + return -EPROTO; if (remaining == 0) /* nothing to do, ignore */ return 0; @@ -309,20 +310,29 @@ static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) data_area_of_target += total_in_buf; /* copy second buffer into end of first buffer */ - memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2); total_in_buf += total_in_buf2; + /* is the result too big for the field? */ + if (total_in_buf > USHRT_MAX) + return -EPROTO; put_unaligned_le16(total_in_buf, &pSMBt->t2_rsp.DataCount); + + /* fix up the BCC */ byte_count = get_bcc_le(pTargetSMB); byte_count += total_in_buf2; + /* is the result too big for the field? */ + if (byte_count > USHRT_MAX) + return -EPROTO; put_bcc_le(byte_count, pTargetSMB); byte_count = pTargetSMB->smb_buf_length; byte_count += total_in_buf2; - - /* BB also add check that we are not beyond maximum buffer size */ - + /* don't allow buffer to overflow */ + if (byte_count > CIFSMaxBufSize) + return -ENOBUFS; pTargetSMB->smb_buf_length = byte_count; + memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2); + if (remaining == total_in_buf2) { cFYI(1, "found the last secondary response"); return 0; /* we are done */ From 8004af443bb5537018555febbc93de467461abf6 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 29 Apr 2011 06:52:43 -0400 Subject: [PATCH 1723/2556] cifs: refactor mid finding loop in cifs_demultiplex_thread commit 146f9f65bd13f56665205aed7205d531c810cb35 upstream. ...to reduce the extreme indentation. This should introduce no behavioral changes. Acked-by: David Howells Signed-off-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/connect.c | 92 +++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index d3433fc8e9413..073db5d786201 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -618,59 +618,59 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); - if ((mid_entry->mid == smb_buffer->Mid) && - (mid_entry->midState == MID_REQUEST_SUBMITTED) && - (mid_entry->command == smb_buffer->Command)) { - if (length == 0 && - check2ndT2(smb_buffer, server->maxBuf) > 0) { - /* We have a multipart transact2 resp */ - isMultiRsp = true; - if (mid_entry->resp_buf) { - /* merge response - fix up 1st*/ - if (coalesce_t2(smb_buffer, + if (mid_entry->mid != smb_buffer->Mid || + mid_entry->midState != MID_REQUEST_SUBMITTED || + mid_entry->command != smb_buffer->Command) { + mid_entry = NULL; + continue; + } + + if (length == 0 && + check2ndT2(smb_buffer, server->maxBuf) > 0) { + /* We have a multipart transact2 resp */ + isMultiRsp = true; + if (mid_entry->resp_buf) { + /* merge response - fix up 1st*/ + if (coalesce_t2(smb_buffer, mid_entry->resp_buf)) { - mid_entry->multiRsp = - true; - break; - } else { - /* all parts received */ - mid_entry->multiEnd = - true; - goto multi_t2_fnd; - } + mid_entry->multiRsp = true; + break; } else { - if (!isLargeBuf) { - cERROR(1, "1st trans2 resp needs bigbuf"); - /* BB maybe we can fix this up, switch - to already allocated large buffer? */ - } else { - /* Have first buffer */ - mid_entry->resp_buf = - smb_buffer; - mid_entry->largeBuf = - true; - bigbuf = NULL; - } + /* all parts received */ + mid_entry->multiEnd = true; + goto multi_t2_fnd; + } + } else { + if (!isLargeBuf) { + /* + * FIXME: switch to already + * allocated largebuf? + */ + cERROR(1, "1st trans2 resp " + "needs bigbuf"); + } else { + /* Have first buffer */ + mid_entry->resp_buf = + smb_buffer; + mid_entry->largeBuf = true; + bigbuf = NULL; } - break; } - mid_entry->resp_buf = smb_buffer; - mid_entry->largeBuf = isLargeBuf; + break; + } + mid_entry->resp_buf = smb_buffer; + mid_entry->largeBuf = isLargeBuf; multi_t2_fnd: - if (length == 0) - mid_entry->midState = - MID_RESPONSE_RECEIVED; - else - mid_entry->midState = - MID_RESPONSE_MALFORMED; + if (length == 0) + mid_entry->midState = MID_RESPONSE_RECEIVED; + else + mid_entry->midState = MID_RESPONSE_MALFORMED; #ifdef CONFIG_CIFS_STATS2 - mid_entry->when_received = jiffies; + mid_entry->when_received = jiffies; #endif - list_del_init(&mid_entry->qhead); - mid_entry->callback(mid_entry); - break; - } - mid_entry = NULL; + list_del_init(&mid_entry->qhead); + mid_entry->callback(mid_entry); + break; } spin_unlock(&GlobalMid_Lock); From ef65b13bc0be7792c8982264209d782f8ee1dd3c Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 29 Apr 2011 06:52:44 -0400 Subject: [PATCH 1724/2556] cifs: handle errors from coalesce_t2 commit 16541ba11c4f04ffe94b073e301f00b749fb84a1 upstream. cifs_demultiplex_thread calls coalesce_t2 to try and merge follow-on t2 responses into the original mid buffer. coalesce_t2 however can return errors, but the caller doesn't handle that situation properly. Fix the thread to treat such a case as it would a malformed packet. Mark the mid as being malformed and issue the callback. Acked-by: David Howells Signed-off-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/connect.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 073db5d786201..0fd3855a161ab 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -631,12 +631,16 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) isMultiRsp = true; if (mid_entry->resp_buf) { /* merge response - fix up 1st*/ - if (coalesce_t2(smb_buffer, - mid_entry->resp_buf)) { + length = coalesce_t2(smb_buffer, + mid_entry->resp_buf); + if (length > 0) { + length = 0; mid_entry->multiRsp = true; break; } else { - /* all parts received */ + /* all parts received or + * packet is malformed + */ mid_entry->multiEnd = true; goto multi_t2_fnd; } From f8ee0a32d68faa762ff0c6a5b8510370108e74e5 Mon Sep 17 00:00:00 2001 From: Timo Warns Date: Fri, 6 May 2011 13:47:35 +0200 Subject: [PATCH 1725/2556] Validate size of EFI GUID partition entries. commit fa039d5f6b126fbd65eefa05db2f67e44df8f121 upstream. Otherwise corrupted EFI partition tables can cause total confusion. Signed-off-by: Timo Warns Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/partitions/efi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/partitions/efi.c b/fs/partitions/efi.c index ac0ccb5026a2d..19d6750d1d6ce 100644 --- a/fs/partitions/efi.c +++ b/fs/partitions/efi.c @@ -348,6 +348,12 @@ static int is_gpt_valid(struct parsed_partitions *state, u64 lba, goto fail; } + /* Check that sizeof_partition_entry has the correct value */ + if (le32_to_cpu((*gpt)->sizeof_partition_entry) != sizeof(gpt_entry)) { + pr_debug("GUID Partitition Entry Size check failed.\n"); + goto fail; + } + if (!(*ptes = alloc_read_gpt_entries(state, *gpt))) goto fail; From c03aaf2e0b910accc1acb201a57bdddb113f4f62 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 4 May 2011 11:41:47 -0400 Subject: [PATCH 1726/2556] drm/radeon/kms: add pci id to acer travelmate quirk for 5730 commit 4f87af46107499415afd238be104587b5a9d7ac3 upstream. Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=34082 Reported by: Sampo Laaksonen Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_atombios.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index a73f0e62a0782..53df8f98e1cd3 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -431,7 +431,7 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev, } } - /* Acer laptop (Acer TravelMate 5730G) has an HDMI port + /* Acer laptop (Acer TravelMate 5730/5730G) has an HDMI port * on the laptop and a DVI port on the docking station and * both share the same encoder, hpd pin, and ddc line. * So while the bios table is technically correct, @@ -440,7 +440,7 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev, * with different crtcs which isn't possible on the hardware * side and leaves no crtcs for LVDS or VGA. */ - if ((dev->pdev->device == 0x95c4) && + if (((dev->pdev->device == 0x95c4) || (dev->pdev->device == 0x9591)) && (dev->pdev->subsystem_vendor == 0x1025) && (dev->pdev->subsystem_device == 0x013c)) { if ((*connector_type == DRM_MODE_CONNECTOR_DVII) && From fe516b2b16d0b13d6c07c63e7aac5c71b438da8f Mon Sep 17 00:00:00 2001 From: Manoj Iyer Date: Sun, 8 May 2011 18:04:29 -0400 Subject: [PATCH 1727/2556] thinkpad-acpi: module autoloading for newer Lenovo ThinkPads. commit 9fbdaeb4f4dd14a0caa9fc35c496d5440c251a3a upstream. The newer Lenovo ThinkPads have HKEY HID of LEN0068 instead of IBM0068. Added new HID so that thinkpad_acpi module will auto load on these newer Lenovo ThinkPads. Acked-by: Henrique de Moraes Holschuh Signed-off-by: Manoj Iyer Signed-off-by: Andy Lutomirski Signed-off-by: Matthew Garrett Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/thinkpad_acpi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index eb9922385ef8f..125d8912ed8a0 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -128,7 +128,8 @@ enum { }; /* ACPI HIDs */ -#define TPACPI_ACPI_HKEY_HID "IBM0068" +#define TPACPI_ACPI_IBM_HKEY_HID "IBM0068" +#define TPACPI_ACPI_LENOVO_HKEY_HID "LEN0068" #define TPACPI_ACPI_EC_HID "PNP0C09" /* Input IDs */ @@ -3879,7 +3880,8 @@ static int hotkey_write(char *buf) } static const struct acpi_device_id ibm_htk_device_ids[] = { - {TPACPI_ACPI_HKEY_HID, 0}, + {TPACPI_ACPI_IBM_HKEY_HID, 0}, + {TPACPI_ACPI_LENOVO_HKEY_HID, 0}, {"", 0}, }; From ecff5f1a67ee54409b65a7621ba0c5709f2b79b7 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 8 Apr 2011 17:29:36 +0200 Subject: [PATCH 1728/2556] x86, hw_breakpoints: Fix racy access to ptrace breakpoints commit 87dc669ba25777b67796d7262c569429e58b1ed4 upstream. While the tracer accesses ptrace breakpoints, the child task may concurrently exit due to a SIGKILL and thus release its breakpoints at the same time. We can then dereference some freed pointers. To fix this, hold a reference on the child breakpoints before manipulating them. Reported-by: Oleg Nesterov Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Will Deacon Cc: Prasad Cc: Paul Mundt Link: http://lkml.kernel.org/r/1302284067-7860-3-git-send-email-fweisbec@gmail.com Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/ptrace.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 45892dc4b72a3..f65e5b521dbd4 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -608,6 +608,9 @@ static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data) unsigned len, type; struct perf_event *bp; + if (ptrace_get_breakpoints(tsk) < 0) + return -ESRCH; + data &= ~DR_CONTROL_RESERVED; old_dr7 = ptrace_get_dr7(thread->ptrace_bps); restore: @@ -655,6 +658,9 @@ static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data) } goto restore; } + + ptrace_put_breakpoints(tsk); + return ((orig_ret < 0) ? orig_ret : rc); } @@ -668,10 +674,17 @@ static unsigned long ptrace_get_debugreg(struct task_struct *tsk, int n) if (n < HBP_NUM) { struct perf_event *bp; + + if (ptrace_get_breakpoints(tsk) < 0) + return -ESRCH; + bp = thread->ptrace_bps[n]; if (!bp) - return 0; - val = bp->hw.info.address; + val = 0; + else + val = bp->hw.info.address; + + ptrace_put_breakpoints(tsk); } else if (n == 6) { val = thread->debugreg6; } else if (n == 7) { @@ -686,6 +699,10 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr, struct perf_event *bp; struct thread_struct *t = &tsk->thread; struct perf_event_attr attr; + int err = 0; + + if (ptrace_get_breakpoints(tsk) < 0) + return -ESRCH; if (!t->ptrace_bps[nr]) { ptrace_breakpoint_init(&attr); @@ -709,24 +726,23 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr, * writing for the user. And anyway this is the previous * behaviour. */ - if (IS_ERR(bp)) - return PTR_ERR(bp); + if (IS_ERR(bp)) { + err = PTR_ERR(bp); + goto put; + } t->ptrace_bps[nr] = bp; } else { - int err; - bp = t->ptrace_bps[nr]; attr = bp->attr; attr.bp_addr = addr; err = modify_user_hw_breakpoint(bp, &attr); - if (err) - return err; } - - return 0; +put: + ptrace_put_breakpoints(tsk); + return err; } /* From f372134eed7fd6ab754cbb1e671fc39fd5dd16b6 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 7 Apr 2011 16:53:20 +0200 Subject: [PATCH 1729/2556] ptrace: Prepare to fix racy accesses on task breakpoints commit bf26c018490c2fce7fe9b629083b96ce0e6ad019 upstream. When a task is traced and is in a stopped state, the tracer may execute a ptrace request to examine the tracee state and get its task struct. Right after, the tracee can be killed and thus its breakpoints released. This can happen concurrently when the tracer is in the middle of reading or modifying these breakpoints, leading to dereferencing a freed pointer. Hence, to prepare the fix, create a generic breakpoint reference holding API. When a reference on the breakpoints of a task is held, the breakpoints won't be released until the last reference is dropped. After that, no more ptrace request on the task's breakpoints can be serviced for the tracer. Reported-by: Oleg Nesterov Signed-off-by: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Will Deacon Cc: Prasad Cc: Paul Mundt Link: http://lkml.kernel.org/r/1302284067-7860-2-git-send-email-fweisbec@gmail.com Signed-off-by: Greg Kroah-Hartman --- include/linux/ptrace.h | 13 ++++++++++++- include/linux/sched.h | 3 +++ kernel/exit.c | 2 +- kernel/ptrace.c | 17 +++++++++++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index a1147e5dd245e..9178d5cc0b014 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -189,6 +189,10 @@ static inline void ptrace_init_task(struct task_struct *child, bool ptrace) child->ptrace = current->ptrace; __ptrace_link(child, current->parent); } + +#ifdef CONFIG_HAVE_HW_BREAKPOINT + atomic_set(&child->ptrace_bp_refcnt, 1); +#endif } /** @@ -350,6 +354,13 @@ extern int task_current_syscall(struct task_struct *target, long *callno, unsigned long args[6], unsigned int maxargs, unsigned long *sp, unsigned long *pc); -#endif +#ifdef CONFIG_HAVE_HW_BREAKPOINT +extern int ptrace_get_breakpoints(struct task_struct *tsk); +extern void ptrace_put_breakpoints(struct task_struct *tsk); +#else +static inline void ptrace_put_breakpoints(struct task_struct *tsk) { } +#endif /* CONFIG_HAVE_HW_BREAKPOINT */ + +#endif /* __KERNEL */ #endif diff --git a/include/linux/sched.h b/include/linux/sched.h index ed6c384ba6c89..d2a5da917ed20 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1531,6 +1531,9 @@ struct task_struct { unsigned long memsw_bytes; /* uncharged mem+swap usage */ } memcg_batch; #endif +#ifdef CONFIG_HAVE_HW_BREAKPOINT + atomic_t ptrace_bp_refcnt; +#endif }; /* Future-safe accessor for struct task_struct's cpus_allowed. */ diff --git a/kernel/exit.c b/kernel/exit.c index f9a45ebcc7b17..557a348f68286 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -1015,7 +1015,7 @@ NORET_TYPE void do_exit(long code) /* * FIXME: do that only when needed, using sched_exit tracepoint */ - flush_ptrace_hw_breakpoint(tsk); + ptrace_put_breakpoints(tsk); exit_notify(tsk, group_dead); #ifdef CONFIG_NUMA diff --git a/kernel/ptrace.c b/kernel/ptrace.c index e2302e40b3600..254ad5b525123 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -22,6 +22,7 @@ #include #include #include +#include /* @@ -876,3 +877,19 @@ asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid, return ret; } #endif /* CONFIG_COMPAT */ + +#ifdef CONFIG_HAVE_HW_BREAKPOINT +int ptrace_get_breakpoints(struct task_struct *tsk) +{ + if (atomic_inc_not_zero(&tsk->ptrace_bp_refcnt)) + return 0; + + return -1; +} + +void ptrace_put_breakpoints(struct task_struct *tsk) +{ + if (atomic_dec_and_test(&tsk->ptrace_bp_refcnt)) + flush_ptrace_hw_breakpoint(tsk); +} +#endif /* CONFIG_HAVE_HW_BREAKPOINT */ From 7eb30b2d301d5f2d92d35038105a382b571a5fd7 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 6 May 2011 01:53:18 +0200 Subject: [PATCH 1730/2556] hw_breakpoints, powerpc: Fix CONFIG_HAVE_HW_BREAKPOINT off-case in ptrace_set_debugreg() commit 925f83c085e1bb08435556c5b4844a60de002e31 upstream. We make use of ptrace_get_breakpoints() / ptrace_put_breakpoints() to protect ptrace_set_debugreg() even if CONFIG_HAVE_HW_BREAKPOINT if off. However in this case, these APIs are not implemented. To fix this, push the protection down inside the relevant ifdef. Best would be to export the code inside CONFIG_HAVE_HW_BREAKPOINT into a standalone function to cleanup the ifdefury there and call the breakpoint ref API inside. But as it is more invasive, this should be rather made in an -rc1. Fixes this build error: arch/powerpc/kernel/ptrace.c:1594: error: implicit declaration of function 'ptrace_get_breakpoints' make[2]: *** Reported-by: Ingo Molnar Signed-off-by: Frederic Weisbecker Cc: LPPC Cc: Prasad Link: http://lkml.kernel.org/r/1304639598-4707-1-git-send-email-fweisbec@gmail.com Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/ptrace.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 9065369982911..85012abc779e7 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -924,12 +924,16 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, if (data && !(data & DABR_TRANSLATION)) return -EIO; #ifdef CONFIG_HAVE_HW_BREAKPOINT + if (ptrace_get_breakpoints(task) < 0) + return -ESRCH; + bp = thread->ptrace_bps[0]; if ((!data) || !(data & (DABR_DATA_WRITE | DABR_DATA_READ))) { if (bp) { unregister_hw_breakpoint(bp); thread->ptrace_bps[0] = NULL; } + ptrace_put_breakpoints(task); return 0; } if (bp) { @@ -939,9 +943,12 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, (DABR_DATA_WRITE | DABR_DATA_READ), &attr.bp_type); ret = modify_user_hw_breakpoint(bp, &attr); - if (ret) + if (ret) { + ptrace_put_breakpoints(task); return ret; + } thread->ptrace_bps[0] = bp; + ptrace_put_breakpoints(task); thread->dabr = data; return 0; } @@ -956,9 +963,12 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, ptrace_triggered, task); if (IS_ERR(bp)) { thread->ptrace_bps[0] = NULL; + ptrace_put_breakpoints(task); return PTR_ERR(bp); } + ptrace_put_breakpoints(task); + #endif /* CONFIG_HAVE_HW_BREAKPOINT */ /* Move contents to the DABR register */ From bbe09ed280944c9ff9e3764b20131ff39f6954f2 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Mon, 9 May 2011 21:27:30 +0200 Subject: [PATCH 1731/2556] iwlwifi: add {ack, plpc}_check module parameters commit b7977ffaab5187ad75edaf04ac854615cea93828 upstream. Add module ack_check, and plcp_check parameters. Ack_check is disabled by default since is proved that check ack health can cause troubles. Plcp_check is enabled by default. This prevent connection hangs with "low ack count detected" messages. Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=666646 Signed-off-by: Stanislaw Gruszka Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 1 + drivers/net/wireless/iwlwifi/iwl-agn.c | 6 ++++++ drivers/net/wireless/iwlwifi/iwl-core.h | 2 ++ drivers/net/wireless/iwlwifi/iwl-rx.c | 8 ++++++-- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index 3dee87e8f55db..d9c87b3227eb3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c @@ -604,6 +604,7 @@ const u8 *iwlagn_eeprom_query_addr(const struct iwl_priv *priv, struct iwl_mod_params iwlagn_mod_params = { .amsdu_size_8K = 1, .restart_fw = 1, + .plcp_check = true, /* the rest are 0 by default */ }; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 35239f04927c0..be076439cc91e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -4586,3 +4586,9 @@ MODULE_PARM_DESC(antenna_coupling, module_param_named(bt_ch_inhibition, iwlagn_bt_ch_announce, bool, S_IRUGO); MODULE_PARM_DESC(bt_ch_inhibition, "Disable BT channel inhibition (default: enable)"); + +module_param_named(plcp_check, iwlagn_mod_params.plcp_check, bool, S_IRUGO); +MODULE_PARM_DESC(plcp_check, "Check plcp health (default: 1 [enabled])"); + +module_param_named(ack_check, iwlagn_mod_params.ack_check, bool, S_IRUGO); +MODULE_PARM_DESC(ack_check, "Check ack health (default: 0 [disabled])"); diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index a3474376fdbc7..5c0d5f72cbac5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -263,6 +263,8 @@ struct iwl_mod_params { int amsdu_size_8K; /* def: 1 = enable 8K amsdu size */ int antenna; /* def: 0 = both antennas (use diversity) */ int restart_fw; /* def: 1 = restart firmware */ + bool plcp_check; /* def: true = enable plcp health check */ + bool ack_check; /* def: false = disable ack health check */ }; /* diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index 87a6fd84d4d25..b7076173436ac 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -234,10 +234,13 @@ EXPORT_SYMBOL(iwl_rx_spectrum_measure_notif); void iwl_recover_from_statistics(struct iwl_priv *priv, struct iwl_rx_packet *pkt) { + const struct iwl_mod_params *mod_params = priv->cfg->mod_params; + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; if (iwl_is_any_associated(priv)) { - if (priv->cfg->ops->lib->check_ack_health) { + if (mod_params->ack_check && + priv->cfg->ops->lib->check_ack_health) { if (!priv->cfg->ops->lib->check_ack_health( priv, pkt)) { /* @@ -250,7 +253,8 @@ void iwl_recover_from_statistics(struct iwl_priv *priv, return; } } - if (priv->cfg->ops->lib->check_plcp_health) { + if (mod_params->plcp_check && + priv->cfg->ops->lib->check_plcp_health) { if (!priv->cfg->ops->lib->check_plcp_health( priv, pkt)) { /* From 72e85d8d556b3d5106432d60b26b7e242507a1af Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 6 May 2011 14:29:55 -0400 Subject: [PATCH 1732/2556] drm/radeon/kms: fix gart setup on fusion parts (v2) backport Backport of 8aeb96f80232e9a701b5c4715504f4c9173978bd (drm/radeon/kms: fix gart setup on fusion parts (v2)) to the stable tree. Out of the entire GART/VM subsystem, the hw designers changed the location of 3 regs. v2: airlied: add parameter for userspace to work from. Signed-off-by: Alex Deucher Signed-off-by: Jerome Glisse Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/evergreen.c | 17 +++++++++-------- drivers/gpu/drm/radeon/evergreend.h | 5 +++++ drivers/gpu/drm/radeon/radeon_kms.c | 3 +++ include/drm/radeon_drm.h | 1 + 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 6140ea1de45a6..627ba8610def1 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -869,9 +869,15 @@ int evergreen_pcie_gart_enable(struct radeon_device *rdev) SYSTEM_ACCESS_MODE_NOT_IN_SYS | SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU | EFFECTIVE_L1_TLB_SIZE(5) | EFFECTIVE_L1_QUEUE_SIZE(5); - WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp); - WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp); - WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp); + if (rdev->flags & RADEON_IS_IGP) { + WREG32(FUS_MC_VM_MD_L1_TLB0_CNTL, tmp); + WREG32(FUS_MC_VM_MD_L1_TLB1_CNTL, tmp); + WREG32(FUS_MC_VM_MD_L1_TLB2_CNTL, tmp); + } else { + WREG32(MC_VM_MD_L1_TLB0_CNTL, tmp); + WREG32(MC_VM_MD_L1_TLB1_CNTL, tmp); + WREG32(MC_VM_MD_L1_TLB2_CNTL, tmp); + } WREG32(MC_VM_MB_L1_TLB0_CNTL, tmp); WREG32(MC_VM_MB_L1_TLB1_CNTL, tmp); WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp); @@ -2930,11 +2936,6 @@ static int evergreen_startup(struct radeon_device *rdev) rdev->asic->copy = NULL; dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r); } - /* XXX: ontario has problems blitting to gart at the moment */ - if (rdev->family == CHIP_PALM) { - rdev->asic->copy = NULL; - radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size); - } /* allocate wb buffer */ r = radeon_wb_init(rdev); diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h index eb4acf4528ff4..447b622ae62ae 100644 --- a/drivers/gpu/drm/radeon/evergreend.h +++ b/drivers/gpu/drm/radeon/evergreend.h @@ -221,6 +221,11 @@ #define MC_VM_MD_L1_TLB0_CNTL 0x2654 #define MC_VM_MD_L1_TLB1_CNTL 0x2658 #define MC_VM_MD_L1_TLB2_CNTL 0x265C + +#define FUS_MC_VM_MD_L1_TLB0_CNTL 0x265C +#define FUS_MC_VM_MD_L1_TLB1_CNTL 0x2660 +#define FUS_MC_VM_MD_L1_TLB2_CNTL 0x2664 + #define MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x203C #define MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2038 #define MC_VM_SYSTEM_APERTURE_LOW_ADDR 0x2034 diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 8387d32caaa76..a5f463bbc4702 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -205,6 +205,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) /* return clock value in KHz */ value = rdev->clock.spll.reference_freq * 10; break; + case RADEON_INFO_FUSION_GART_WORKING: + value = 1; + break; default: DRM_DEBUG_KMS("Invalid request %d\n", info->request); return -EINVAL; diff --git a/include/drm/radeon_drm.h b/include/drm/radeon_drm.h index e5c607a02d57c..53f9ea216774d 100644 --- a/include/drm/radeon_drm.h +++ b/include/drm/radeon_drm.h @@ -908,6 +908,7 @@ struct drm_radeon_cs { #define RADEON_INFO_WANT_HYPERZ 0x07 #define RADEON_INFO_WANT_CMASK 0x08 /* get access to CMASK on r300 */ #define RADEON_INFO_CLOCK_CRYSTAL_FREQ 0x09 /* clock crystal frequency */ +#define RADEON_INFO_FUSION_GART_WORKING 0x0c /* fusion writes to GTT were broken before this */ struct drm_radeon_info { uint32_t request; From 3dead919f4cf95df59d8df06c1fa559c55571384 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Mon, 9 May 2011 17:44:42 -0700 Subject: [PATCH 1733/2556] vm: fix vm_pgoff wrap in upward expansion commit 42c36f63ac1366ab0ecc2d5717821362c259f517 upstream. Commit a626ca6a6564 ("vm: fix vm_pgoff wrap in stack expansion") fixed the case of an expanding mapping causing vm_pgoff wrapping when you had downward stack expansion. But there was another case where IA64 and PA-RISC expand mappings: upward expansion. This fixes that case too. Signed-off-by: Hugh Dickins Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/mmap.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/mm/mmap.c b/mm/mmap.c index e27e0cf0de03c..772140c53ab18 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1767,10 +1767,13 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address) size = address - vma->vm_start; grow = (address - vma->vm_end) >> PAGE_SHIFT; - error = acct_stack_growth(vma, size, grow); - if (!error) { - vma->vm_end = address; - perf_event_mmap(vma); + error = -ENOMEM; + if (vma->vm_pgoff + (size >> PAGE_SHIFT) >= vma->vm_pgoff) { + error = acct_stack_growth(vma, size, grow); + if (!error) { + vma->vm_end = address; + perf_event_mmap(vma); + } } } vma_unlock_anon_vma(vma); From e277c750ea81076d8dc68694acc7994a024d83b2 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Mon, 9 May 2011 13:01:09 +0200 Subject: [PATCH 1734/2556] Don't lock guardpage if the stack is growing up commit a09a79f66874c905af35d5bb5e5f2fdc7b6b894d upstream. Linux kernel excludes guard page when performing mlock on a VMA with down-growing stack. However, some architectures have up-growing stack and locking the guard page should be excluded in this case too. This patch fixes lvm2 on PA-RISC (and possibly other architectures with up-growing stack). lvm2 calculates number of used pages when locking and when unlocking and reports an internal error if the numbers mismatch. [ Patch changed fairly extensively to also fix /proc//maps for the grows-up case, and to move things around a bit to clean it all up and share the infrstructure with the /proc bits. Tested on ia64 that has both grow-up and grow-down segments - Linus ] Signed-off-by: Mikulas Patocka Tested-by: Tony Luck Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/proc/task_mmu.c | 12 +++++++----- include/linux/mm.h | 24 +++++++++++++++++++++++- mm/memory.c | 16 +++++++--------- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index f269ee673c8bf..c4bec29cf7d11 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -211,7 +211,7 @@ static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma) int flags = vma->vm_flags; unsigned long ino = 0; unsigned long long pgoff = 0; - unsigned long start; + unsigned long start, end; dev_t dev = 0; int len; @@ -224,13 +224,15 @@ static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma) /* We don't show the stack guard page in /proc/maps */ start = vma->vm_start; - if (vma->vm_flags & VM_GROWSDOWN) - if (!vma_stack_continue(vma->vm_prev, vma->vm_start)) - start += PAGE_SIZE; + if (stack_guard_page_start(vma, start)) + start += PAGE_SIZE; + end = vma->vm_end; + if (stack_guard_page_end(vma, end)) + end -= PAGE_SIZE; seq_printf(m, "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu %n", start, - vma->vm_end, + end, flags & VM_READ ? 'r' : '-', flags & VM_WRITE ? 'w' : '-', flags & VM_EXEC ? 'x' : '-', diff --git a/include/linux/mm.h b/include/linux/mm.h index 248c94685037b..957a90b962f07 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -994,11 +994,33 @@ int set_page_dirty_lock(struct page *page); int clear_page_dirty_for_io(struct page *page); /* Is the vma a continuation of the stack vma above it? */ -static inline int vma_stack_continue(struct vm_area_struct *vma, unsigned long addr) +static inline int vma_growsdown(struct vm_area_struct *vma, unsigned long addr) { return vma && (vma->vm_end == addr) && (vma->vm_flags & VM_GROWSDOWN); } +static inline int stack_guard_page_start(struct vm_area_struct *vma, + unsigned long addr) +{ + return (vma->vm_flags & VM_GROWSDOWN) && + (vma->vm_start == addr) && + !vma_growsdown(vma->vm_prev, addr); +} + +/* Is the vma a continuation of the stack vma below it? */ +static inline int vma_growsup(struct vm_area_struct *vma, unsigned long addr) +{ + return vma && (vma->vm_start == addr) && (vma->vm_flags & VM_GROWSUP); +} + +static inline int stack_guard_page_end(struct vm_area_struct *vma, + unsigned long addr) +{ + return (vma->vm_flags & VM_GROWSUP) && + (vma->vm_end == addr) && + !vma_growsup(vma->vm_next, addr); +} + extern unsigned long move_page_tables(struct vm_area_struct *vma, unsigned long old_addr, struct vm_area_struct *new_vma, unsigned long new_addr, unsigned long len); diff --git a/mm/memory.c b/mm/memory.c index 8b2bb7dcdae5e..c8fff70277428 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1412,9 +1412,8 @@ struct page *follow_page(struct vm_area_struct *vma, unsigned long address, static inline int stack_guard_page(struct vm_area_struct *vma, unsigned long addr) { - return (vma->vm_flags & VM_GROWSDOWN) && - (vma->vm_start == addr) && - !vma_stack_continue(vma->vm_prev, addr); + return stack_guard_page_start(vma, addr) || + stack_guard_page_end(vma, addr+PAGE_SIZE); } int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, @@ -1502,12 +1501,6 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, continue; } - /* - * For mlock, just skip the stack guard page. - */ - if ((gup_flags & FOLL_MLOCK) && stack_guard_page(vma, start)) - goto next_page; - do { struct page *page; unsigned int foll_flags = gup_flags; @@ -1524,6 +1517,11 @@ int __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, int ret; unsigned int fault_flags = 0; + /* For mlock, just skip the stack guard page. */ + if (foll_flags & FOLL_MLOCK) { + if (stack_guard_page(vma, start)) + goto next_page; + } if (foll_flags & FOLL_WRITE) fault_flags |= FAULT_FLAG_WRITE; if (nonblocking) From 9e532c331c378b6ba7a0886b8410bcb523663b28 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 17 Apr 2011 06:38:35 +0100 Subject: [PATCH 1735/2556] drm/i915/dp: Be paranoid in case we disable a DP before it is attached commit 31acbcc408f412d1ba73765b846c38642be553c3 upstream. Given that the hardware may be left in a random condition by the BIOS, it is conceivable that we then attempt to clear the DP_PIPEB_SELECT bit without us ever enabling/attaching the DP encoder to a pipe. Thus causing a NULL deference when we attempt to wait for a vblank on that crtc. Reported-and-tested-by: Bryan Christ Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=36314 Signed-off-by: Chris Wilson Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=36456 Reported-and-tested-by: Bo Wang Signed-off-by: Keith Packard Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_dp.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 51cb4e36997f7..8f3a02bc1cf5f 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1455,7 +1455,8 @@ intel_dp_link_down(struct intel_dp *intel_dp) if (!HAS_PCH_CPT(dev) && I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) { - struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.base.crtc); + struct drm_crtc *crtc = intel_dp->base.base.crtc; + /* Hardware workaround: leaving our transcoder select * set to transcoder B while it's off will prevent the * corresponding HDMI output on transcoder A. @@ -1470,7 +1471,19 @@ intel_dp_link_down(struct intel_dp *intel_dp) /* Changes to enable or select take place the vblank * after being written. */ - intel_wait_for_vblank(dev, intel_crtc->pipe); + if (crtc == NULL) { + /* We can arrive here never having been attached + * to a CRTC, for instance, due to inheriting + * random state from the BIOS. + * + * If the pipe is not running, play safe and + * wait for the clocks to stabilise before + * continuing. + */ + POSTING_READ(intel_dp->output_reg); + msleep(50); + } else + intel_wait_for_vblank(dev, to_intel_crtc(crtc)->pipe); } I915_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN); From 911c1616880507b32b0b6d89db7e7055dd803079 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 21 Apr 2011 16:08:14 -0600 Subject: [PATCH 1736/2556] drm/i915/lvds: Only act on lid notify when the device is on commit 2fb4e61d9471867677c97bf11dba8f1e9dfa7f7c upstream. If we're using vga switcheroo, the device may be turned off and poking it can return random state. This provokes an OOPS fixed separately by 8ff887c847 (drm/i915/dp: Be paranoid in case we disable a DP before it is attached). Trying to use and respond to events on a device that has been turned off by the user is in principle a silly thing to do. Signed-off-by: Alex Williamson Signed-off-by: Chris Wilson Signed-off-by: Keith Packard Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_lvds.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index bcdba7bd5cfaf..b902192c4647c 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -540,6 +540,9 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val, struct drm_device *dev = dev_priv->dev; struct drm_connector *connector = dev_priv->int_lvds_connector; + if (dev->switch_power_state != DRM_SWITCH_POWER_ON) + return NOTIFY_OK; + /* * check and update the status of LVDS connector after receiving * the LID nofication event. From 10bb564c39e90bb0a23bd925490e8cf1a7af9372 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 16 Apr 2011 10:23:51 +0100 Subject: [PATCH 1737/2556] drm/i915: Release object along create user fb error path commit 2dd251f0a294300a1cf8f4b63768145fa6153c4d upstream. Reported-by: Alan Cox Signed-off-by: Chris Wilson Signed-off-by: Keith Packard Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_display.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ecf8f947b4b79..841558bc91cc2 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6005,8 +6005,10 @@ intel_user_framebuffer_create(struct drm_device *dev, return ERR_PTR(-ENOENT); intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL); - if (!intel_fb) + if (!intel_fb) { + drm_gem_object_unreference_unlocked(&obj->base); return ERR_PTR(-ENOMEM); + } ret = intel_framebuffer_init(dev, intel_fb, mode_cmd, obj); if (ret) { From a7d9222022375081576a7909167869a0bf1ed44c Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Fri, 6 May 2011 03:27:18 +0000 Subject: [PATCH 1738/2556] dccp: handle invalid feature options length commit a294865978b701e4d0d90135672749531b9a900d upstream. A length of zero (after subtracting two for the type and len fields) for the DCCPO_{CHANGE,CONFIRM}_{L,R} options will cause an underflow due to the subtraction. The subsequent code may read past the end of the options value buffer when parsing. I'm unsure of what the consequences of this might be, but it's probably not good. Signed-off-by: Dan Rosenberg Acked-by: Gerrit Renker Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/dccp/options.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/dccp/options.c b/net/dccp/options.c index f06ffcfc8d712..4b2ab657ac8e6 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c @@ -123,6 +123,8 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, case DCCPO_CHANGE_L ... DCCPO_CONFIRM_R: if (pkt_type == DCCP_PKT_DATA) /* RFC 4340, 6 */ break; + if (len == 0) + goto out_invalid_option; rc = dccp_feat_parse_options(sk, dreq, mandatory, opt, *value, value + 1, len - 1); if (rc) From 0d88dc66b6ca8011870d03b7fb1e65500a02bc4b Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Thu, 14 Apr 2011 22:00:56 +0400 Subject: [PATCH 1739/2556] CIFS: Fix memory over bound bug in cifs_parse_mount_options commit 4906e50b37e6f6c264e7ee4237343eb2b7f8d16d upstream. While password processing we can get out of options array bound if the next character after array is delimiter. The patch adds a check if we reach the end. Signed-off-by: Pavel Shilovsky Reviewed-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/connect.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 0fd3855a161ab..bd7e61fbd27e3 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -822,8 +822,7 @@ static int cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol) { - char *value; - char *data; + char *value, *data, *end; unsigned int temp_len, i, j; char separator[2]; short int override_uid = -1; @@ -866,6 +865,7 @@ cifs_parse_mount_options(char *options, const char *devname, if (!options) return 1; + end = options + strlen(options); if (strncmp(options, "sep=", 4) == 0) { if (options[4] != 0) { separator[0] = options[4]; @@ -930,6 +930,7 @@ cifs_parse_mount_options(char *options, const char *devname, the only illegal character in a password is null */ if ((value[temp_len] == 0) && + (value + temp_len < end) && (value[temp_len+1] == separator[0])) { /* reinsert comma */ value[temp_len] = separator[0]; From b3ec0162bf3d8de20df658daac7abee0dcaf98d0 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 11 May 2011 15:13:28 -0700 Subject: [PATCH 1740/2556] drivers/rtc/rtc-s3c.c: fixup wake support for rtc commit 52cd4e5c620af9e21b5298bf01844b98573505a7 upstream. The driver is not balancing set_irq and disable_irq_wake() calls, so ensure that it keeps track of whether the wake is enabled. The fixes the following error on S3C6410 devices: WARNING: at kernel/irq/manage.c:382 set_irq_wake+0x84/0xec() Unbalanced IRQ 92 wake disable Signed-off-by: Ben Dooks Signed-off-by: Mark Brown Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/rtc/rtc-s3c.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index b80fa28824083..637b012007aa7 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -46,6 +46,7 @@ static struct clk *rtc_clk; static void __iomem *s3c_rtc_base; static int s3c_rtc_alarmno = NO_IRQ; static int s3c_rtc_tickno = NO_IRQ; +static bool wake_en; static enum s3c_cpu_type s3c_rtc_cpu_type; static DEFINE_SPINLOCK(s3c_rtc_pie_lock); @@ -597,8 +598,12 @@ static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state) } s3c_rtc_enable(pdev, 0); - if (device_may_wakeup(&pdev->dev)) - enable_irq_wake(s3c_rtc_alarmno); + if (device_may_wakeup(&pdev->dev) && !wake_en) { + if (enable_irq_wake(s3c_rtc_alarmno) == 0) + wake_en = true; + else + dev_err(&pdev->dev, "enable_irq_wake failed\n"); + } return 0; } @@ -614,8 +619,10 @@ static int s3c_rtc_resume(struct platform_device *pdev) writew(tmp | ticnt_en_save, s3c_rtc_base + S3C2410_RTCCON); } - if (device_may_wakeup(&pdev->dev)) + if (device_may_wakeup(&pdev->dev) && wake_en) { disable_irq_wake(s3c_rtc_alarmno); + wake_en = false; + } return 0; } From 52db907b9df439c6dbee0856e8bf48be62f81027 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Wed, 11 May 2011 15:13:32 -0700 Subject: [PATCH 1741/2556] mm: use alloc_bootmem_node_nopanic() on really needed path commit 8f389a99b652aab5b42297280bd94d95933ad12f upstream. Stefan found nobootmem does not work on his system that has only 8M of RAM. This causes an early panic: BIOS-provided physical RAM map: BIOS-88: 0000000000000000 - 000000000009f000 (usable) BIOS-88: 0000000000100000 - 0000000000840000 (usable) bootconsole [earlyser0] enabled Notice: NX (Execute Disable) protection missing in CPU or disabled in BIOS! DMI not present or invalid. last_pfn = 0x840 max_arch_pfn = 0x100000 init_memory_mapping: 0000000000000000-0000000000840000 8MB LOWMEM available. mapped low ram: 0 - 00840000 low ram: 0 - 00840000 Zone PFN ranges: DMA 0x00000001 -> 0x00001000 Normal empty Movable zone start PFN for each node early_node_map[2] active PFN ranges 0: 0x00000001 -> 0x0000009f 0: 0x00000100 -> 0x00000840 BUG: Int 6: CR2 (null) EDI c034663c ESI (null) EBP c0329f38 ESP c0329ef4 EBX c0346380 EDX 00000006 ECX ffffffff EAX fffffff4 err (null) EIP c0353191 CS c0320060 flg 00010082 Stack: (null) c030c533 000007cd (null) c030c533 00000001 (null) (null) 00000003 0000083f 00000018 00000002 00000002 c0329f6c c03534d6 (null) (null) 00000100 00000840 (null) c0329f64 00000001 00001000 (null) Pid: 0, comm: swapper Not tainted 2.6.36 #5 Call Trace: [] ? 0xc02e3707 [] 0xc035e6e5 [] ? 0xc0353191 [] 0xc03534d6 [] 0xc034f1cd [] 0xc034a824 [] ? 0xc03513cb [] 0xc0349432 [] 0xc0349066 It turns out that we should ignore the low limit of 16M. Use alloc_bootmem_node_nopanic() in this case. [akpm@linux-foundation.org: less mess] Signed-off-by: Yinghai LU Reported-by: Stefan Hellermann Tested-by: Stefan Hellermann Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Thomas Gleixner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- include/linux/bootmem.h | 2 ++ mm/page_alloc.c | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 499dfe982a0e8..552875fae0f04 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -115,6 +115,8 @@ extern void *__alloc_bootmem_low_node(pg_data_t *pgdat, __alloc_bootmem_nopanic(x, PAGE_SIZE, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_node(pgdat, x) \ __alloc_bootmem_node(pgdat, x, SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) +#define alloc_bootmem_node_nopanic(pgdat, x) \ + __alloc_bootmem_node_nopanic(pgdat, x, SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_pages_node(pgdat, x) \ __alloc_bootmem_node(pgdat, x, PAGE_SIZE, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_pages_node_nopanic(pgdat, x) \ diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 28280375327f7..e607de5c138fe 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3498,7 +3498,7 @@ int zone_wait_table_init(struct zone *zone, unsigned long zone_size_pages) if (!slab_is_available()) { zone->wait_table = (wait_queue_head_t *) - alloc_bootmem_node(pgdat, alloc_size); + alloc_bootmem_node_nopanic(pgdat, alloc_size); } else { /* * This case means that a zone whose size was 0 gets new memory @@ -4071,7 +4071,8 @@ static void __init setup_usemap(struct pglist_data *pgdat, unsigned long usemapsize = usemap_size(zonesize); zone->pageblock_flags = NULL; if (usemapsize) - zone->pageblock_flags = alloc_bootmem_node(pgdat, usemapsize); + zone->pageblock_flags = alloc_bootmem_node_nopanic(pgdat, + usemapsize); } #else static inline void setup_usemap(struct pglist_data *pgdat, @@ -4237,7 +4238,7 @@ static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat) size = (end - start) * sizeof(struct page); map = alloc_remap(pgdat->node_id, size); if (!map) - map = alloc_bootmem_node(pgdat, size); + map = alloc_bootmem_node_nopanic(pgdat, size); pgdat->node_mem_map = map + (pgdat->node_start_pfn - start); } #ifndef CONFIG_NEED_MULTIPLE_NODES From fe27581573a4901c83b9c3dadf6f1f2221d9df50 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 11 May 2011 15:13:37 -0700 Subject: [PATCH 1742/2556] tmpfs: fix race between umount and swapoff commit 778dd893ae785c5fd505dac30b5fc40aae188bf1 upstream. The use of igrab() in swapoff's shmem_unuse_inode() is just as vulnerable to umount as that in shmem_writepage(). Fix this instance by extending the protection of shmem_swaplist_mutex right across shmem_unuse_inode(): while it's on the list, the inode cannot be evicted (and the filesystem cannot be unmounted) without shmem_evict_inode() taking that mutex to remove it from the list. But since shmem_writepage() might take that mutex, we should avoid making memory allocations or memcg charges while holding it: prepare them at the outer level in shmem_unuse(). When mem_cgroup_cache_charge() was originally placed, we didn't know until that point that the page from swap was actually a shmem page; but nowadays it's noted in the swap_map, so we're safe to charge upfront. For the radix_tree, do as is done in shmem_getpage(): preload upfront, but don't pin to the cpu; so we make a habit of refreshing the node pool, but might dip into GFP_NOWAIT reserves on occasion if subsequently preempted. With the allocation and charge moved out from shmem_unuse_inode(), we can also hold index map and info->lock over from finding the entry. Signed-off-by: Hugh Dickins Cc: Konstantin Khlebnikov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/shmem.c | 88 ++++++++++++++++++++++++++---------------------------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index 5ac23d570f965..8ac1684d79086 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -852,7 +852,7 @@ static inline int shmem_find_swp(swp_entry_t entry, swp_entry_t *dir, swp_entry_ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, struct page *page) { - struct inode *inode; + struct address_space *mapping; unsigned long idx; unsigned long size; unsigned long limit; @@ -875,8 +875,10 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, s if (size > SHMEM_NR_DIRECT) size = SHMEM_NR_DIRECT; offset = shmem_find_swp(entry, ptr, ptr+size); - if (offset >= 0) + if (offset >= 0) { + shmem_swp_balance_unmap(); goto found; + } if (!info->i_indirect) goto lost2; @@ -914,11 +916,11 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, s if (size > ENTRIES_PER_PAGE) size = ENTRIES_PER_PAGE; offset = shmem_find_swp(entry, ptr, ptr+size); - shmem_swp_unmap(ptr); if (offset >= 0) { shmem_dir_unmap(dir); goto found; } + shmem_swp_unmap(ptr); } } lost1: @@ -928,8 +930,7 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, s return 0; found: idx += offset; - inode = igrab(&info->vfs_inode); - spin_unlock(&info->lock); + ptr += offset; /* * Move _head_ to start search for next from here. @@ -940,37 +941,18 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, s */ if (shmem_swaplist.next != &info->swaplist) list_move_tail(&shmem_swaplist, &info->swaplist); - mutex_unlock(&shmem_swaplist_mutex); - error = 1; - if (!inode) - goto out; /* - * Charge page using GFP_KERNEL while we can wait. - * Charged back to the user(not to caller) when swap account is used. - * add_to_page_cache() will be called with GFP_NOWAIT. + * We rely on shmem_swaplist_mutex, not only to protect the swaplist, + * but also to hold up shmem_evict_inode(): so inode cannot be freed + * beneath us (pagelock doesn't help until the page is in pagecache). */ - error = mem_cgroup_cache_charge(page, current->mm, GFP_KERNEL); - if (error) - goto out; - error = radix_tree_preload(GFP_KERNEL); - if (error) { - mem_cgroup_uncharge_cache_page(page); - goto out; - } - error = 1; - - spin_lock(&info->lock); - ptr = shmem_swp_entry(info, idx, NULL); - if (ptr && ptr->val == entry.val) { - error = add_to_page_cache_locked(page, inode->i_mapping, - idx, GFP_NOWAIT); - /* does mem_cgroup_uncharge_cache_page on error */ - } else /* we must compensate for our precharge above */ - mem_cgroup_uncharge_cache_page(page); + mapping = info->vfs_inode.i_mapping; + error = add_to_page_cache_locked(page, mapping, idx, GFP_NOWAIT); + /* which does mem_cgroup_uncharge_cache_page on error */ if (error == -EEXIST) { - struct page *filepage = find_get_page(inode->i_mapping, idx); + struct page *filepage = find_get_page(mapping, idx); error = 1; if (filepage) { /* @@ -990,14 +972,8 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, s swap_free(entry); error = 1; /* not an error, but entry was found */ } - if (ptr) - shmem_swp_unmap(ptr); + shmem_swp_unmap(ptr); spin_unlock(&info->lock); - radix_tree_preload_end(); -out: - unlock_page(page); - page_cache_release(page); - iput(inode); /* allows for NULL */ return error; } @@ -1009,6 +985,26 @@ int shmem_unuse(swp_entry_t entry, struct page *page) struct list_head *p, *next; struct shmem_inode_info *info; int found = 0; + int error; + + /* + * Charge page using GFP_KERNEL while we can wait, before taking + * the shmem_swaplist_mutex which might hold up shmem_writepage(). + * Charged back to the user (not to caller) when swap account is used. + * add_to_page_cache() will be called with GFP_NOWAIT. + */ + error = mem_cgroup_cache_charge(page, current->mm, GFP_KERNEL); + if (error) + goto out; + /* + * Try to preload while we can wait, to not make a habit of + * draining atomic reserves; but don't latch on to this cpu, + * it's okay if sometimes we get rescheduled after this. + */ + error = radix_tree_preload(GFP_KERNEL); + if (error) + goto uncharge; + radix_tree_preload_end(); mutex_lock(&shmem_swaplist_mutex); list_for_each_safe(p, next, &shmem_swaplist) { @@ -1016,17 +1012,19 @@ int shmem_unuse(swp_entry_t entry, struct page *page) found = shmem_unuse_inode(info, entry, page); cond_resched(); if (found) - goto out; + break; } mutex_unlock(&shmem_swaplist_mutex); - /* - * Can some race bring us here? We've been holding page lock, - * so I think not; but would rather try again later than BUG() - */ + +uncharge: + if (!found) + mem_cgroup_uncharge_cache_page(page); + if (found < 0) + error = found; +out: unlock_page(page); page_cache_release(page); -out: - return (found < 0) ? found : 0; + return error; } /* From 1da3e2d35573b23ae38aa0f1b096cb978ecef445 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 21 Apr 2011 21:45:08 -0400 Subject: [PATCH 1743/2556] ARM: zImage: make sure the stack is 64-bit aligned commit 3bd2cbb95543acf44fe123eb9f038de54e655eb4 upstream. With ARMv5+ and EABI, the compiler expects a 64-bit aligned stack so instructions like STRD and LDRD can be used. Without this, mysterious boot failures were seen semi randomly with the LZMA decompressor. While at it, let's align .bss as well. Signed-off-by: Nicolas Pitre Tested-by: Shawn Guo Acked-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/compressed/Makefile | 2 +- arch/arm/boot/compressed/vmlinux.lds.in | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index 0a8f748e506ad..e1bea82284ae5 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -59,7 +59,7 @@ ZTEXTADDR := $(CONFIG_ZBOOT_ROM_TEXT) ZBSSADDR := $(CONFIG_ZBOOT_ROM_BSS) else ZTEXTADDR := 0 -ZBSSADDR := ALIGN(4) +ZBSSADDR := ALIGN(8) endif SEDFLAGS = s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/ diff --git a/arch/arm/boot/compressed/vmlinux.lds.in b/arch/arm/boot/compressed/vmlinux.lds.in index 366a924019ac6..7ece7056ba0ea 100644 --- a/arch/arm/boot/compressed/vmlinux.lds.in +++ b/arch/arm/boot/compressed/vmlinux.lds.in @@ -57,6 +57,7 @@ SECTIONS .bss : { *(.bss) } _end = .; + . = ALIGN(8); /* the stack must be 64-bit aligned */ .stack : { *(.stack) } .stab 0 : { *(.stab) } From b869799eec5145afe463368afb679445d8e4bc3f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 10 May 2011 21:09:53 +0200 Subject: [PATCH 1744/2556] PM: Fix warning in pm_restrict_gfp_mask() during SNAPSHOT_S2RAM ioctl commit 87186475a402391a1ca7d42a675c9b35a18dc348 upstream. A warning is printed by pm_restrict_gfp_mask() while the SNAPSHOT_S2RAM ioctl is being executed after creating a hibernation image, because pm_restrict_gfp_mask() has been called once already before the image creation and suspend_devices_and_enter() calls it once again. This happens after commit 452aa6999e6703ffbddd7f6ea124d3 (mm/pm: force GFP_NOIO during suspend/hibernation and resume). To avoid this issue, move pm_restrict_gfp_mask() and pm_restore_gfp_mask() from suspend_devices_and_enter() to its caller in kernel/power/suspend.c. Reported-by: Alexandre Felipe Muller de Souza Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- kernel/power/suspend.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index de6f86bfa3038..08f62e890aa5c 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -209,7 +209,6 @@ int suspend_devices_and_enter(suspend_state_t state) goto Close; } suspend_console(); - pm_restrict_gfp_mask(); suspend_test_start(); error = dpm_suspend_start(PMSG_SUSPEND); if (error) { @@ -226,7 +225,6 @@ int suspend_devices_and_enter(suspend_state_t state) suspend_test_start(); dpm_resume_end(PMSG_RESUME); suspend_test_finish("resume devices"); - pm_restore_gfp_mask(); resume_console(); Close: if (suspend_ops->end) @@ -287,7 +285,9 @@ int enter_state(suspend_state_t state) goto Finish; pr_debug("PM: Entering %s sleep\n", pm_states[state]); + pm_restrict_gfp_mask(); error = suspend_devices_and_enter(state); + pm_restore_gfp_mask(); Finish: pr_debug("PM: Finishing wakeup.\n"); From 29639f09f1f93fec0b6a53aff7cf8927bbd3b783 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 10 May 2011 21:10:01 +0200 Subject: [PATCH 1745/2556] PM / Hibernate: Make snapshot_release() restore GFP mask commit 9744997a8a2280e67984d4bffd87221d24f3b6b1 upstream. If the process using the hibernate user space interface closes /dev/snapshot after creating a hibernation image without thawing tasks, snapshot_release() should call pm_restore_gfp_mask() to restore the GFP mask used before the creation of the image. Make that happen. Tested-by: Alexandre Felipe Muller de Souza Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- kernel/power/user.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/power/user.c b/kernel/power/user.c index c36c3b9e8a84f..6522be913ac1b 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c @@ -135,8 +135,10 @@ static int snapshot_release(struct inode *inode, struct file *filp) free_basic_memory_bitmaps(); data = filp->private_data; free_all_swap_pages(data->swap); - if (data->frozen) + if (data->frozen) { + pm_restore_gfp_mask(); thaw_processes(); + } pm_notifier_call_chain(data->mode == O_RDONLY ? PM_POST_HIBERNATION : PM_POST_RESTORE); atomic_inc(&snapshot_device_available); From 782cfad3ae0722283b31c747a7f02b974e273f06 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 10 May 2011 21:10:13 +0200 Subject: [PATCH 1746/2556] PM / Hibernate: Fix ioctl SNAPSHOT_S2RAM commit 36cb7035ea0c11ef2c7fa2bbe0cd181b23569b29 upstream. The SNAPSHOT_S2RAM ioctl used for implementing the feature allowing one to suspend to RAM after creating a hibernation image is currently broken, because it doesn't clear the "ready" flag in the struct snapshot_data object handled by it. As a result, the SNAPSHOT_UNFREEZE doesn't work correctly after SNAPSHOT_S2RAM has returned and the user space hibernate task cannot thaw the other processes as appropriate. Make SNAPSHOT_S2RAM clear data->ready to fix this problem. Tested-by: Alexandre Felipe Muller de Souza Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- kernel/power/user.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/power/user.c b/kernel/power/user.c index 6522be913ac1b..7d02d33be699f 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c @@ -381,6 +381,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, * PM_HIBERNATION_PREPARE */ error = suspend_devices_and_enter(PM_SUSPEND_MEM); + data->ready = 0; break; case SNAPSHOT_PLATFORM_SUPPORT: From 4e239472b3bcbc83a4e3115dab5489168d8086fd Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 4 May 2011 10:02:26 +0000 Subject: [PATCH 1747/2556] net: ip_expire() must revalidate route commit 64f3b9e203bd06855072e295557dca1485a2ecba upstream. Commit 4a94445c9a5c (net: Use ip_route_input_noref() in input path) added a bug in IP defragmentation handling, in case timeout is fired. When a frame is defragmented, we use last skb dst field when building final skb. Its dst is valid, since we are in rcu read section. But if a timeout occurs, we take first queued fragment to build one ICMP TIME EXCEEDED message. Problem is all queued skb have weak dst pointers, since we escaped RCU critical section after their queueing. icmp_send() might dereference a now freed (and possibly reused) part of memory. Calling skb_dst_drop() and ip_route_input_noref() to revalidate route is the only possible choice. Reported-by: Denys Fedoryshchenko Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ip_fragment.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index a1151b8adf3c6..b1d282f11be7e 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -223,31 +223,30 @@ static void ip_expire(unsigned long arg) if ((qp->q.last_in & INET_FRAG_FIRST_IN) && qp->q.fragments != NULL) { struct sk_buff *head = qp->q.fragments; + const struct iphdr *iph; + int err; rcu_read_lock(); head->dev = dev_get_by_index_rcu(net, qp->iif); if (!head->dev) goto out_rcu_unlock; + /* skb dst is stale, drop it, and perform route lookup again */ + skb_dst_drop(head); + iph = ip_hdr(head); + err = ip_route_input_noref(head, iph->daddr, iph->saddr, + iph->tos, head->dev); + if (err) + goto out_rcu_unlock; + /* - * Only search router table for the head fragment, - * when defraging timeout at PRE_ROUTING HOOK. + * Only an end host needs to send an ICMP + * "Fragment Reassembly Timeout" message, per RFC792. */ - if (qp->user == IP_DEFRAG_CONNTRACK_IN && !skb_dst(head)) { - const struct iphdr *iph = ip_hdr(head); - int err = ip_route_input(head, iph->daddr, iph->saddr, - iph->tos, head->dev); - if (unlikely(err)) - goto out_rcu_unlock; - - /* - * Only an end host needs to send an ICMP - * "Fragment Reassembly Timeout" message, per RFC792. - */ - if (skb_rtable(head)->rt_type != RTN_LOCAL) - goto out_rcu_unlock; + if (qp->user == IP_DEFRAG_CONNTRACK_IN && + skb_rtable(head)->rt_type != RTN_LOCAL) + goto out_rcu_unlock; - } /* Send an ICMP "Fragment Reassembly Timeout" message. */ icmp_send(head, ICMP_TIME_EXCEEDED, ICMP_EXC_FRAGTIME, 0); From ce850224f5ead68a9773decd16dbb794c23bb44e Mon Sep 17 00:00:00 2001 From: Kurt Van Dijck Date: Mon, 2 May 2011 04:50:48 +0000 Subject: [PATCH 1748/2556] can: fix SJA1000 dlc for RTR packets commit 87e9af6cc67d842cd92b52b81f3f14e665e7ab05 upstream. RTR frames do have a valid data length code on CAN. The driver for SJA1000 did not handle that situation properly. Signed-off-by: Kurt Van Dijck Acked-by: Marc Kleine-Budde Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/sja1000/sja1000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c index 0a8de01d52f7a..a616658868d14 100644 --- a/drivers/net/can/sja1000/sja1000.c +++ b/drivers/net/can/sja1000/sja1000.c @@ -346,10 +346,10 @@ static void sja1000_rx(struct net_device *dev) | (priv->read_reg(priv, REG_ID2) >> 5); } + cf->can_dlc = get_can_dlc(fi & 0x0F); if (fi & FI_RTR) { id |= CAN_RTR_FLAG; } else { - cf->can_dlc = get_can_dlc(fi & 0x0F); for (i = 0; i < cf->can_dlc; i++) cf->data[i] = priv->read_reg(priv, dreg++); } From dd784ee217b6fb84a1f6ad43edc15abff4dfad52 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 3 May 2011 07:49:25 +0000 Subject: [PATCH 1749/2556] ipheth: Properly distinguish length and alignment in URBs and skbs commit 9c412942a0bb19ba18f7bd939d42eff1e132a901 upstream. The USB protocol this driver implements appears to require 2 bytes of padding in front of each received packet. This used to be equal to the value of NET_IP_ALIGN on x86, so the driver abused that constant and mostly worked, but this is no longer the case. The driver also mixed up the URB and packet lengths, resulting in 2 bytes of junk at the end of the skb. Introduce a private constant for the 2 bytes of padding; fix this confusion and check for the under-length case. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/ipheth.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c index 7d42f9a2c0686..81126ff85e057 100644 --- a/drivers/net/usb/ipheth.c +++ b/drivers/net/usb/ipheth.c @@ -65,6 +65,7 @@ #define IPHETH_USBINTF_PROTO 1 #define IPHETH_BUF_SIZE 1516 +#define IPHETH_IP_ALIGN 2 /* padding at front of URB */ #define IPHETH_TX_TIMEOUT (5 * HZ) #define IPHETH_INTFNUM 2 @@ -202,18 +203,21 @@ static void ipheth_rcvbulk_callback(struct urb *urb) return; } - len = urb->actual_length; - buf = urb->transfer_buffer; + if (urb->actual_length <= IPHETH_IP_ALIGN) { + dev->net->stats.rx_length_errors++; + return; + } + len = urb->actual_length - IPHETH_IP_ALIGN; + buf = urb->transfer_buffer + IPHETH_IP_ALIGN; - skb = dev_alloc_skb(NET_IP_ALIGN + len); + skb = dev_alloc_skb(len); if (!skb) { err("%s: dev_alloc_skb: -ENOMEM", __func__); dev->net->stats.rx_dropped++; return; } - skb_reserve(skb, NET_IP_ALIGN); - memcpy(skb_put(skb, len), buf + NET_IP_ALIGN, len - NET_IP_ALIGN); + memcpy(skb_put(skb, len), buf, len); skb->dev = dev->net; skb->protocol = eth_type_trans(skb, dev->net); From b1d3059e72b8086ed4e7201af52ae6d43ef881cb Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Fri, 6 May 2011 08:32:53 +0000 Subject: [PATCH 1750/2556] vmxnet3: Consistently disable irqs when taking adapter->cmd_lock commit e328d410826d52e9ee348aff9064c4a207f2adb1 upstream. Using the vmxnet3 driver produces a lockdep warning because vmxnet3_set_mc(), which is called with mc->mca_lock held, takes adapter->cmd_lock. However, there are a couple of places where adapter->cmd_lock is taken with softirqs enabled, lockdep warns that a softirq that tries to take mc->mca_lock could happen while adapter->cmd_lock is held, leading to an AB-BA deadlock. I'm not sure if this is a real potential deadlock or not, but the simplest and best fix seems to be simply to make sure we take cmd_lock with spin_lock_irqsave() everywhere -- the places with plain spin_lock just look like oversights. The full enormous lockdep warning is: ========================================================= [ INFO: possible irq lock inversion dependency detected ] 2.6.39-rc6+ #1 --------------------------------------------------------- ifconfig/567 just changed the state of lock: (&(&mc->mca_lock)->rlock){+.-...}, at: [] mld_ifc_timer_expire+0xff/0x280 but this lock took another, SOFTIRQ-unsafe lock in the past: (&(&adapter->cmd_lock)->rlock){+.+...} and interrupts could create inverse lock ordering between them. other info that might help us debug this: 4 locks held by ifconfig/567: #0: (rtnl_mutex){+.+.+.}, at: [] rtnl_lock+0x17/0x20 #1: ((inetaddr_chain).rwsem){.+.+.+}, at: [] __blocking_notifier_call_chain+0x5f/0xb0 #2: (&idev->mc_ifc_timer){+.-...}, at: [] run_timer_softirq+0xeb/0x3f0 #3: (&ndev->lock){++.-..}, at: [] mld_ifc_timer_expire+0x32/0x280 the shortest dependencies between 2nd lock and 1st lock: -> (&(&adapter->cmd_lock)->rlock){+.+...} ops: 11 { HARDIRQ-ON-W at: [] __lock_acquire+0x7f6/0x1e10 [] lock_acquire+0x9d/0x130 [] _raw_spin_lock+0x36/0x70 [] vmxnet3_alloc_intr_resources+0x22/0x230 [vmxnet3] [] vmxnet3_probe_device+0x5f6/0x15c5 [vmxnet3] [] local_pci_probe+0x5f/0xd0 [] pci_device_probe+0x119/0x120 [] driver_probe_device+0x96/0x1c0 [] __driver_attach+0xab/0xb0 [] bus_for_each_dev+0x5e/0x90 [] driver_attach+0x1e/0x20 [] bus_add_driver+0xc8/0x290 [] driver_register+0x76/0x140 [] __pci_register_driver+0x66/0xe0 [] serio_raw_poll+0x3a/0x60 [serio_raw] [] do_one_initcall+0x45/0x190 [] sys_init_module+0xfb/0x250 [] system_call_fastpath+0x16/0x1b SOFTIRQ-ON-W at: [] __lock_acquire+0x827/0x1e10 [] lock_acquire+0x9d/0x130 [] _raw_spin_lock+0x36/0x70 [] vmxnet3_alloc_intr_resources+0x22/0x230 [vmxnet3] [] vmxnet3_probe_device+0x5f6/0x15c5 [vmxnet3] [] local_pci_probe+0x5f/0xd0 [] pci_device_probe+0x119/0x120 [] driver_probe_device+0x96/0x1c0 [] __driver_attach+0xab/0xb0 [] bus_for_each_dev+0x5e/0x90 [] driver_attach+0x1e/0x20 [] bus_add_driver+0xc8/0x290 [] driver_register+0x76/0x140 [] __pci_register_driver+0x66/0xe0 [] serio_raw_poll+0x3a/0x60 [serio_raw] [] do_one_initcall+0x45/0x190 [] sys_init_module+0xfb/0x250 [] system_call_fastpath+0x16/0x1b INITIAL USE at: [] __lock_acquire+0x459/0x1e10 [] lock_acquire+0x9d/0x130 [] _raw_spin_lock+0x36/0x70 [] vmxnet3_alloc_intr_resources+0x22/0x230 [vmxnet3] [] vmxnet3_probe_device+0x5f6/0x15c5 [vmxnet3] [] local_pci_probe+0x5f/0xd0 [] pci_device_probe+0x119/0x120 [] driver_probe_device+0x96/0x1c0 [] __driver_attach+0xab/0xb0 [] bus_for_each_dev+0x5e/0x90 [] driver_attach+0x1e/0x20 [] bus_add_driver+0xc8/0x290 [] driver_register+0x76/0x140 [] __pci_register_driver+0x66/0xe0 [] serio_raw_poll+0x3a/0x60 [serio_raw] [] do_one_initcall+0x45/0x190 [] sys_init_module+0xfb/0x250 [] system_call_fastpath+0x16/0x1b } ... key at: [] __key.42516+0x0/0xffffffffffffda70 [vmxnet3] ... acquired at: [] lock_acquire+0x9d/0x130 [] _raw_spin_lock_irqsave+0x55/0xa0 [] vmxnet3_set_mc+0x97/0x1a0 [vmxnet3] [] __dev_set_rx_mode+0x40/0xb0 [] dev_set_rx_mode+0x30/0x50 [] __dev_open+0xc7/0x100 [] __dev_change_flags+0xa1/0x180 [] dev_change_flags+0x28/0x70 [] devinet_ioctl+0x730/0x800 [] inet_ioctl+0x88/0xa0 [] sock_do_ioctl+0x30/0x70 [] sock_ioctl+0x79/0x2f0 [] do_vfs_ioctl+0x98/0x570 [] sys_ioctl+0x91/0xa0 [] system_call_fastpath+0x16/0x1b -> (_xmit_ETHER){+.....} ops: 6 { HARDIRQ-ON-W at: [] __lock_acquire+0x7f6/0x1e10 [] lock_acquire+0x9d/0x130 [] _raw_spin_lock_bh+0x3b/0x70 [] __dev_mc_add+0x38/0x90 [] dev_mc_add+0x10/0x20 [] igmp6_group_added+0x10e/0x1b0 [] ipv6_dev_mc_inc+0x2cd/0x430 [] ipv6_add_dev+0x357/0x450 [] addrconf_notify+0x2f7/0xb10 [] notifier_call_chain+0x8c/0xc0 [] raw_notifier_call_chain+0x16/0x20 [] call_netdevice_notifiers+0x37/0x70 [] register_netdevice+0x244/0x2d0 [] register_netdev+0x3f/0x60 [] vmxnet3_probe_device+0x760/0x15c5 [vmxnet3] [] local_pci_probe+0x5f/0xd0 [] pci_device_probe+0x119/0x120 [] driver_probe_device+0x96/0x1c0 [] __driver_attach+0xab/0xb0 [] bus_for_each_dev+0x5e/0x90 [] driver_attach+0x1e/0x20 [] bus_add_driver+0xc8/0x290 [] driver_register+0x76/0x140 [] __pci_register_driver+0x66/0xe0 [] serio_raw_poll+0x3a/0x60 [serio_raw] [] do_one_initcall+0x45/0x190 [] sys_init_module+0xfb/0x250 [] system_call_fastpath+0x16/0x1b INITIAL USE at: [] __lock_acquire+0x459/0x1e10 [] lock_acquire+0x9d/0x130 [] _raw_spin_lock_bh+0x3b/0x70 [] __dev_mc_add+0x38/0x90 [] dev_mc_add+0x10/0x20 [] igmp6_group_added+0x10e/0x1b0 [] ipv6_dev_mc_inc+0x2cd/0x430 [] ipv6_add_dev+0x357/0x450 [] addrconf_notify+0x2f7/0xb10 [] notifier_call_chain+0x8c/0xc0 [] raw_notifier_call_chain+0x16/0x20 [] call_netdevice_notifiers+0x37/0x70 [] register_netdevice+0x244/0x2d0 [] register_netdev+0x3f/0x60 [] vmxnet3_probe_device+0x760/0x15c5 [vmxnet3] [] local_pci_probe+0x5f/0xd0 [] pci_device_probe+0x119/0x120 [] driver_probe_device+0x96/0x1c0 [] __driver_attach+0xab/0xb0 [] bus_for_each_dev+0x5e/0x90 [] driver_attach+0x1e/0x20 [] bus_add_driver+0xc8/0x290 [] driver_register+0x76/0x140 [] __pci_register_driver+0x66/0xe0 [] serio_raw_poll+0x3a/0x60 [serio_raw] [] do_one_initcall+0x45/0x190 [] sys_init_module+0xfb/0x250 [] system_call_fastpath+0x16/0x1b } ... key at: [] netdev_addr_lock_key+0x8/0x1e0 ... acquired at: [] lock_acquire+0x9d/0x130 [] _raw_spin_lock_bh+0x3b/0x70 [] __dev_mc_add+0x38/0x90 [] dev_mc_add+0x10/0x20 [] igmp6_group_added+0x10e/0x1b0 [] ipv6_dev_mc_inc+0x2cd/0x430 [] ipv6_add_dev+0x357/0x450 [] addrconf_notify+0x2f7/0xb10 [] notifier_call_chain+0x8c/0xc0 [] raw_notifier_call_chain+0x16/0x20 [] call_netdevice_notifiers+0x37/0x70 [] register_netdevice+0x244/0x2d0 [] register_netdev+0x3f/0x60 [] vmxnet3_probe_device+0x760/0x15c5 [vmxnet3] [] local_pci_probe+0x5f/0xd0 [] pci_device_probe+0x119/0x120 [] driver_probe_device+0x96/0x1c0 [] __driver_attach+0xab/0xb0 [] bus_for_each_dev+0x5e/0x90 [] driver_attach+0x1e/0x20 [] bus_add_driver+0xc8/0x290 [] driver_register+0x76/0x140 [] __pci_register_driver+0x66/0xe0 [] serio_raw_poll+0x3a/0x60 [serio_raw] [] do_one_initcall+0x45/0x190 [] sys_init_module+0xfb/0x250 [] system_call_fastpath+0x16/0x1b -> (&(&mc->mca_lock)->rlock){+.-...} ops: 6 { HARDIRQ-ON-W at: [] __lock_acquire+0x7f6/0x1e10 [] lock_acquire+0x9d/0x130 [] _raw_spin_lock_bh+0x3b/0x70 [] igmp6_group_added+0x45/0x1b0 [] ipv6_dev_mc_inc+0x2cd/0x430 [] ipv6_add_dev+0x357/0x450 [] addrconf_init+0x4e/0x183 [] inet6_init+0x191/0x2a6 [] do_one_initcall+0x45/0x190 [] kernel_init+0xe3/0x168 [] kernel_thread_helper+0x4/0x10 IN-SOFTIRQ-W at: [] __lock_acquire+0x7ce/0x1e10 [] lock_acquire+0x9d/0x130 [] _raw_spin_lock_bh+0x3b/0x70 [] mld_ifc_timer_expire+0xff/0x280 [] run_timer_softirq+0x179/0x3f0 [] __do_softirq+0xc0/0x210 [] call_softirq+0x1c/0x30 [] do_softirq+0xad/0xe0 [] irq_exit+0x9e/0xb0 [] smp_apic_timer_interrupt+0x70/0x9b [] apic_timer_interrupt+0x13/0x20 [] rt_do_flush+0x87/0x2a0 [] rt_cache_flush+0x46/0x60 [] fib_disable_ip+0x40/0x60 [] fib_inetaddr_event+0xd7/0xe0 [] notifier_call_chain+0x8c/0xc0 [] __blocking_notifier_call_chain+0x78/0xb0 [] blocking_notifier_call_chain+0x16/0x20 [] __inet_del_ifa+0xf1/0x2e0 [] inet_del_ifa+0x13/0x20 [] devinet_ioctl+0x501/0x800 [] inet_ioctl+0x88/0xa0 [] sock_do_ioctl+0x30/0x70 [] sock_ioctl+0x79/0x2f0 [] do_vfs_ioctl+0x98/0x570 [] sys_ioctl+0x91/0xa0 [] system_call_fastpath+0x16/0x1b INITIAL USE at: [] __lock_acquire+0x459/0x1e10 [] lock_acquire+0x9d/0x130 [] _raw_spin_lock_bh+0x3b/0x70 [] igmp6_group_added+0x45/0x1b0 [] ipv6_dev_mc_inc+0x2cd/0x430 [] ipv6_add_dev+0x357/0x450 [] addrconf_init+0x4e/0x183 [] inet6_init+0x191/0x2a6 [] do_one_initcall+0x45/0x190 [] kernel_init+0xe3/0x168 [] kernel_thread_helper+0x4/0x10 } ... key at: [] __key.40877+0x0/0x8 ... acquired at: [] check_usage_forwards+0x9c/0x110 [] mark_lock+0x19c/0x400 [] __lock_acquire+0x7ce/0x1e10 [] lock_acquire+0x9d/0x130 [] _raw_spin_lock_bh+0x3b/0x70 [] mld_ifc_timer_expire+0xff/0x280 [] run_timer_softirq+0x179/0x3f0 [] __do_softirq+0xc0/0x210 [] call_softirq+0x1c/0x30 [] do_softirq+0xad/0xe0 [] irq_exit+0x9e/0xb0 [] smp_apic_timer_interrupt+0x70/0x9b [] apic_timer_interrupt+0x13/0x20 [] rt_do_flush+0x87/0x2a0 [] rt_cache_flush+0x46/0x60 [] fib_disable_ip+0x40/0x60 [] fib_inetaddr_event+0xd7/0xe0 [] notifier_call_chain+0x8c/0xc0 [] __blocking_notifier_call_chain+0x78/0xb0 [] blocking_notifier_call_chain+0x16/0x20 [] __inet_del_ifa+0xf1/0x2e0 [] inet_del_ifa+0x13/0x20 [] devinet_ioctl+0x501/0x800 [] inet_ioctl+0x88/0xa0 [] sock_do_ioctl+0x30/0x70 [] sock_ioctl+0x79/0x2f0 [] do_vfs_ioctl+0x98/0x570 [] sys_ioctl+0x91/0xa0 [] system_call_fastpath+0x16/0x1b stack backtrace: Pid: 567, comm: ifconfig Not tainted 2.6.39-rc6+ #1 Call Trace: [] print_irq_inversion_bug+0x146/0x170 [] ? print_irq_inversion_bug+0x170/0x170 [] check_usage_forwards+0x9c/0x110 [] mark_lock+0x19c/0x400 [] __lock_acquire+0x7ce/0x1e10 [] ? mark_lock+0x1f3/0x400 [] ? __lock_acquire+0xf07/0x1e10 [] ? native_sched_clock+0x15/0x70 [] lock_acquire+0x9d/0x130 [] ? mld_ifc_timer_expire+0xff/0x280 [] ? lock_release_holdtime+0x3d/0x1a0 [] _raw_spin_lock_bh+0x3b/0x70 [] ? mld_ifc_timer_expire+0xff/0x280 [] ? _raw_spin_unlock+0x2b/0x40 [] mld_ifc_timer_expire+0xff/0x280 [] run_timer_softirq+0x179/0x3f0 [] ? run_timer_softirq+0xeb/0x3f0 [] ? sched_clock+0x9/0x10 [] ? mld_gq_timer_expire+0x30/0x30 [] __do_softirq+0xc0/0x210 [] ? tick_program_event+0x1f/0x30 [] call_softirq+0x1c/0x30 [] do_softirq+0xad/0xe0 [] irq_exit+0x9e/0xb0 [] smp_apic_timer_interrupt+0x70/0x9b [] apic_timer_interrupt+0x13/0x20 [] ? retint_restore_args+0x13/0x13 [] ? lock_is_held+0x17/0xd0 [] rt_do_flush+0x87/0x2a0 [] rt_cache_flush+0x46/0x60 [] fib_disable_ip+0x40/0x60 [] fib_inetaddr_event+0xd7/0xe0 [] notifier_call_chain+0x8c/0xc0 [] __blocking_notifier_call_chain+0x78/0xb0 [] blocking_notifier_call_chain+0x16/0x20 [] __inet_del_ifa+0xf1/0x2e0 [] inet_del_ifa+0x13/0x20 [] devinet_ioctl+0x501/0x800 [] ? local_clock+0x6f/0x80 [] ? do_page_fault+0x268/0x560 [] inet_ioctl+0x88/0xa0 [] sock_do_ioctl+0x30/0x70 [] sock_ioctl+0x79/0x2f0 [] ? __call_rcu+0xa7/0x190 [] do_vfs_ioctl+0x98/0x570 [] ? fget_light+0x33e/0x430 [] ? retint_swapgs+0x13/0x1b [] sys_ioctl+0x91/0xa0 [] system_call_fastpath+0x16/0x1b Signed-off-by: Roland Dreier Signed-off-by: Shreyas N Bhatewara Signed-off-by: Scott J. Goldman Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/vmxnet3/vmxnet3_drv.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index cc14b4a75048c..cab2846f8311c 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -178,6 +178,7 @@ static void vmxnet3_process_events(struct vmxnet3_adapter *adapter) { int i; + unsigned long flags; u32 events = le32_to_cpu(adapter->shared->ecr); if (!events) return; @@ -190,10 +191,10 @@ vmxnet3_process_events(struct vmxnet3_adapter *adapter) /* Check if there is an error on xmit/recv queues */ if (events & (VMXNET3_ECR_TQERR | VMXNET3_ECR_RQERR)) { - spin_lock(&adapter->cmd_lock); + spin_lock_irqsave(&adapter->cmd_lock, flags); VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_QUEUE_STATUS); - spin_unlock(&adapter->cmd_lock); + spin_unlock_irqrestore(&adapter->cmd_lock, flags); for (i = 0; i < adapter->num_tx_queues; i++) if (adapter->tqd_start[i].status.stopped) @@ -2733,13 +2734,14 @@ static void vmxnet3_alloc_intr_resources(struct vmxnet3_adapter *adapter) { u32 cfg; + unsigned long flags; /* intr settings */ - spin_lock(&adapter->cmd_lock); + spin_lock_irqsave(&adapter->cmd_lock, flags); VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, VMXNET3_CMD_GET_CONF_INTR); cfg = VMXNET3_READ_BAR1_REG(adapter, VMXNET3_REG_CMD); - spin_unlock(&adapter->cmd_lock); + spin_unlock_irqrestore(&adapter->cmd_lock, flags); adapter->intr.type = cfg & 0x3; adapter->intr.mask_mode = (cfg >> 2) & 0x3; From 0249262d6296a5744ced1945c92dec7c2914a688 Mon Sep 17 00:00:00 2001 From: Kleber Sacilotto de Souza Date: Wed, 4 May 2011 13:05:11 +0000 Subject: [PATCH 1751/2556] ehea: fix wrongly reported speed and port commit dcbe14b91a920657ff3a9ba0efb7c5b5562f956a upstream. Currently EHEA reports to ethtool as supporting 10M, 100M, 1G and 10G and connected to FIBRE independent of the hardware configuration. However, when connected to FIBRE the only supported speed is 10G full-duplex, and the other speeds and modes are only supported when connected to twisted pair. Signed-off-by: Kleber Sacilotto de Souza Acked-by: Breno Leitao Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ehea/ehea_ethtool.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/net/ehea/ehea_ethtool.c b/drivers/net/ehea/ehea_ethtool.c index 3e2e734fecb73..f3bbdcef338cf 100644 --- a/drivers/net/ehea/ehea_ethtool.c +++ b/drivers/net/ehea/ehea_ethtool.c @@ -55,15 +55,20 @@ static int ehea_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) cmd->duplex = -1; } - cmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_1000baseT_Full - | SUPPORTED_100baseT_Full | SUPPORTED_100baseT_Half - | SUPPORTED_10baseT_Full | SUPPORTED_10baseT_Half - | SUPPORTED_Autoneg | SUPPORTED_FIBRE); - - cmd->advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_Autoneg - | ADVERTISED_FIBRE); + if (cmd->speed == SPEED_10000) { + cmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE); + cmd->advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE); + cmd->port = PORT_FIBRE; + } else { + cmd->supported = (SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full + | SUPPORTED_100baseT_Half | SUPPORTED_10baseT_Full + | SUPPORTED_10baseT_Half | SUPPORTED_Autoneg + | SUPPORTED_TP); + cmd->advertising = (ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg + | ADVERTISED_TP); + cmd->port = PORT_TP; + } - cmd->port = PORT_FIBRE; cmd->autoneg = port->autoneg == 1 ? AUTONEG_ENABLE : AUTONEG_DISABLE; return 0; From 1b0b0acbe1d20b296ba9a9cc5a9d9b877e4c6eb5 Mon Sep 17 00:00:00 2001 From: Matvejchikov Ilya Date: Fri, 6 May 2011 06:23:09 +0000 Subject: [PATCH 1752/2556] NET: slip, fix ldisc->open retval commit 057bef938896e6266ae24ec4266d24792d27c29a upstream. TTY layer expects 0 if the ldisc->open operation succeeded. Signed-off-by : Matvejchikov Ilya Acked-by: Oliver Hartkopp Acked-by: Alan Cox Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/slip.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/slip.c b/drivers/net/slip.c index 86cbb9ea2f269..8ec1a9a0bb9ae 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -853,7 +853,9 @@ static int slip_open(struct tty_struct *tty) /* Done. We have linked the TTY line to a channel. */ rtnl_unlock(); tty->receive_room = 65536; /* We don't flow control */ - return sl->dev->base_addr; + + /* TTY layer expects 0 on success */ + return 0; err_free_bufs: sl_free_bufs(sl); From b9f596db8cefc8e4f0abd804edde9fde96ec0d01 Mon Sep 17 00:00:00 2001 From: Toshiharu Okada Date: Fri, 6 May 2011 02:53:51 +0000 Subject: [PATCH 1753/2556] PCH_GbE : Fixed the issue of collision detection commit ce3dad0f74e6b240f0b1dedbd8ea268a3f298d82 upstream. The collision detection setting was invalid. When collision occurred, because data was not resent, there was an issue to which a transmitting throughput falls. This patch enables the collision detection. Signed-off-by: Toshiharu Okada Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/pch_gbe/pch_gbe_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/pch_gbe/pch_gbe_main.c b/drivers/net/pch_gbe/pch_gbe_main.c index b99e90aca37dc..a2e7d63d7fd11 100644 --- a/drivers/net/pch_gbe/pch_gbe_main.c +++ b/drivers/net/pch_gbe/pch_gbe_main.c @@ -43,8 +43,7 @@ const char pch_driver_version[] = DRV_VERSION; #define PCH_GBE_MAC_RGMII_CTRL_SETTING ( \ PCH_GBE_CHIP_TYPE_INTERNAL | \ - PCH_GBE_RGMII_MODE_RGMII | \ - PCH_GBE_CRS_SEL \ + PCH_GBE_RGMII_MODE_RGMII \ ) /* Ethertype field values */ From b121770076ed5c378f4ac78891f4fb07da978c05 Mon Sep 17 00:00:00 2001 From: Toshiharu Okada Date: Fri, 6 May 2011 02:53:56 +0000 Subject: [PATCH 1754/2556] PCH_GbE : Fixed the issue of checksum judgment commit 5d05a04d283061b586e8dc819cfa6f4b8cfd5948 upstream. The checksum judgment was mistaken. Judgment result 0:Correct 1:Wrong This patch fixes the issue. Signed-off-by: Toshiharu Okada Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/pch_gbe/pch_gbe_main.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/pch_gbe/pch_gbe_main.c b/drivers/net/pch_gbe/pch_gbe_main.c index a2e7d63d7fd11..bd3ef37fb51e8 100644 --- a/drivers/net/pch_gbe/pch_gbe_main.c +++ b/drivers/net/pch_gbe/pch_gbe_main.c @@ -1493,12 +1493,11 @@ pch_gbe_clean_rx(struct pch_gbe_adapter *adapter, /* Write meta date of skb */ skb_put(skb, length); skb->protocol = eth_type_trans(skb, netdev); - if ((tcp_ip_status & PCH_GBE_RXD_ACC_STAT_TCPIPOK) == - PCH_GBE_RXD_ACC_STAT_TCPIPOK) { - skb->ip_summed = CHECKSUM_UNNECESSARY; - } else { + if (tcp_ip_status & PCH_GBE_RXD_ACC_STAT_TCPIPOK) skb->ip_summed = CHECKSUM_NONE; - } + else + skb->ip_summed = CHECKSUM_UNNECESSARY; + napi_gro_receive(&adapter->napi, skb); (*work_done)++; pr_debug("Receive skb->ip_summed: %d length: %d\n", From fc4e1025211914e13b9c5a8268d52fb2b9d90032 Mon Sep 17 00:00:00 2001 From: Tomoya Date: Mon, 9 May 2011 01:19:37 +0000 Subject: [PATCH 1755/2556] pch_gbe: support ML7223 IOH commit b0e6baf5619a6fa3eaf43b55fdb4daa362c3c916 upstream. Support new device OKI SEMICONDUCTOR ML7223 IOH(Input/Output Hub). The ML7223 IOH is for MP(Media Phone) use. The ML7223 is companion chip for Intel Atom E6xx series. The ML7223 is completely compatible for Intel EG20T PCH. Signed-off-by: Tomoya MORINAGA Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/Kconfig | 8 +++++++- drivers/net/pch_gbe/pch_gbe_main.c | 11 +++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 03823327db252..5bcb395e4f0c7 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2533,7 +2533,7 @@ config S6GMAC source "drivers/net/stmmac/Kconfig" config PCH_GBE - tristate "PCH Gigabit Ethernet" + tristate "Intel EG20T PCH / OKI SEMICONDUCTOR ML7223 IOH GbE" depends on PCI select MII ---help--- @@ -2545,6 +2545,12 @@ config PCH_GBE to Gigabit Ethernet. This driver enables Gigabit Ethernet function. + This driver also can be used for OKI SEMICONDUCTOR IOH(Input/ + Output Hub), ML7223. + ML7223 IOH is for MP(Media Phone) use. + ML7223 is companion chip for Intel Atom E6xx series. + ML7223 is completely compatible for Intel EG20T PCH. + endif # NETDEV_1000 # diff --git a/drivers/net/pch_gbe/pch_gbe_main.c b/drivers/net/pch_gbe/pch_gbe_main.c index bd3ef37fb51e8..d95e527cd3209 100644 --- a/drivers/net/pch_gbe/pch_gbe_main.c +++ b/drivers/net/pch_gbe/pch_gbe_main.c @@ -34,6 +34,10 @@ const char pch_driver_version[] = DRV_VERSION; #define PCH_GBE_COPYBREAK_DEFAULT 256 #define PCH_GBE_PCI_BAR 1 +/* Macros for ML7223 */ +#define PCI_VENDOR_ID_ROHM 0x10db +#define PCI_DEVICE_ID_ROHM_ML7223_GBE 0x8013 + #define PCH_GBE_TX_WEIGHT 64 #define PCH_GBE_RX_WEIGHT 64 #define PCH_GBE_RX_BUFFER_WRITE 16 @@ -2418,6 +2422,13 @@ static DEFINE_PCI_DEVICE_TABLE(pch_gbe_pcidev_id) = { .class = (PCI_CLASS_NETWORK_ETHERNET << 8), .class_mask = (0xFFFF00) }, + {.vendor = PCI_VENDOR_ID_ROHM, + .device = PCI_DEVICE_ID_ROHM_ML7223_GBE, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .class = (PCI_CLASS_NETWORK_ETHERNET << 8), + .class_mask = (0xFFFF00) + }, /* required last entry */ {0} }; From 8970cb9186550711878378d7091f64b177d350d9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 10 May 2011 12:26:06 -0700 Subject: [PATCH 1756/2556] net: dev_close() should check IFF_UP commit e14a599335427f81bbb0008963e59aa9c6449dce upstream. Commit 443457242beb (factorize sync-rcu call in unregister_netdevice_many) mistakenly removed one test from dev_close() Following actions trigger a BUG : modprobe bonding modprobe dummy ifconfig bond0 up ifenslave bond0 dummy0 rmmod dummy dev_close() must not close a non IFF_UP device. With help from Frank Blaschka and Einar EL Lueck Reported-by: Frank Blaschka Reported-by: Einar EL Lueck Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/core/dev.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 6561021d22d1f..af0995df7114a 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1332,11 +1332,13 @@ int dev_close_many(struct list_head *head) */ int dev_close(struct net_device *dev) { - LIST_HEAD(single); + if (dev->flags & IFF_UP) { + LIST_HEAD(single); - list_add(&dev->unreg_list, &single); - dev_close_many(&single); - list_del(&single); + list_add(&dev->unreg_list, &single); + dev_close_many(&single); + list_del(&single); + } return 0; } EXPORT_SYMBOL(dev_close); From da7f9c20fda9b1775def5b4087c51169e13cc0c4 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 10 May 2011 13:12:30 -0700 Subject: [PATCH 1757/2556] slcan: fix ldisc->open retval commit 0d4420a90b51abdea71585f571bad6d789ff8eb7 upstream. TTY layer expects 0 if the ldisc->open operation succeeded. Reported-by: Matvejchikov Ilya Signed-off-by: Oliver Hartkopp Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/slcan.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c index b423965a78d16..1b49df6b24708 100644 --- a/drivers/net/can/slcan.c +++ b/drivers/net/can/slcan.c @@ -583,7 +583,9 @@ static int slcan_open(struct tty_struct *tty) /* Done. We have linked the TTY line to a channel. */ rtnl_unlock(); tty->receive_room = 65536; /* We don't flow control */ - return sl->dev->base_addr; + + /* TTY layer expects 0 on success */ + return 0; err_free_chan: sl->tty = NULL; From db257178dc770728f2866a8bafef33e6a96978b6 Mon Sep 17 00:00:00 2001 From: Marek Belisko Date: Tue, 3 May 2011 14:46:32 +0200 Subject: [PATCH 1758/2556] ASoC: UDA134x: Remove POWER_OFF_ON_STANDBY define. commit bf707de21fec7bb203dace2d0a2bbd124d1b36ca upstream. Define POWER_OFF_ON_STANDBY cause trobles when trying to get some sound from codec because code for bias setup was not compiled (define wasn't defined). This define was removed in commit: cc3202f5 but again introduced by commit: f0fba2ad1 which then completely break codec functionality so remove it again. Signed-off-by: Marek Belisko Acked-by: Liam Girdwood Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/uda134x.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index 48ffd406a71d1..a7b8f301bad39 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -601,9 +601,7 @@ static struct snd_soc_codec_driver soc_codec_dev_uda134x = { .reg_cache_step = 1, .read = uda134x_read_reg_cache, .write = uda134x_write, -#ifdef POWER_OFF_ON_STANDBY .set_bias_level = uda134x_set_bias_level, -#endif }; static int __devinit uda134x_codec_probe(struct platform_device *pdev) From b4c2a9797cd113ed9022341d3990303b3c7a2d7a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 5 May 2011 16:59:16 +0200 Subject: [PATCH 1759/2556] ASoC: SSM2602: Fix 'Mic Boost2' control commit 36c90ab33feabbd63da775bd92ad356e5bd5cf56 upstream. The 'Mic Boost2' control's shift was off by one and thus was not working. Signed-off-by: Lars-Peter Clausen Acked-by: Liam Girdwood Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/ssm2602.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 2727befd158ec..977d0b64e9c43 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -139,7 +139,7 @@ SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0), SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1), SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0), -SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 7, 1, 0), +SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0), SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1), SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1), From a0790b74a28e55380220f8afc732d3cc97c3c113 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 12 May 2011 09:11:40 +0000 Subject: [PATCH 1760/2556] ne-h8300: Fix regression caused during net_device_ops conversion commit 2592a7354092afd304a8c067319b15ab1e441e35 upstream. Changeset dcd39c90290297f6e6ed8a04bb20da7ac2b043c5 ("ne-h8300: convert to net_device_ops") broke ne-h8300 by adding 8390.o to the link. That meant that lib8390.c was included twice, once in ne-h8300.c and once in 8390.c, subject to different macros. This patch reverts that by avoiding the wrappers in 8390.c. Fix based on commits 217cbfa856dc1cbc2890781626c4032d9e3ec59f ("mac8390: fix regression caused during net_device_ops conversion") and 4e0168fa4842e27795a75b205a510f25b62181d9 ("mac8390: fix build with NET_POLL_CONTROLLER"). Signed-off-by: Geert Uytterhoeven Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/Makefile | 2 +- drivers/net/ne-h8300.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/net/Makefile b/drivers/net/Makefile index b90738d139943..9607d87cfc439 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -144,7 +144,7 @@ obj-$(CONFIG_NE3210) += ne3210.o 8390.o obj-$(CONFIG_SB1250_MAC) += sb1250-mac.o obj-$(CONFIG_B44) += b44.o obj-$(CONFIG_FORCEDETH) += forcedeth.o -obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o +obj-$(CONFIG_NE_H8300) += ne-h8300.o obj-$(CONFIG_AX88796) += ax88796.o obj-$(CONFIG_BCM63XX_ENET) += bcm63xx_enet.o diff --git a/drivers/net/ne-h8300.c b/drivers/net/ne-h8300.c index 30be8c634ebdd..7298a34bc7951 100644 --- a/drivers/net/ne-h8300.c +++ b/drivers/net/ne-h8300.c @@ -167,7 +167,7 @@ static void cleanup_card(struct net_device *dev) #ifndef MODULE struct net_device * __init ne_probe(int unit) { - struct net_device *dev = alloc_ei_netdev(); + struct net_device *dev = ____alloc_ei_netdev(0); int err; if (!dev) @@ -197,15 +197,15 @@ static const struct net_device_ops ne_netdev_ops = { .ndo_open = ne_open, .ndo_stop = ne_close, - .ndo_start_xmit = ei_start_xmit, - .ndo_tx_timeout = ei_tx_timeout, - .ndo_get_stats = ei_get_stats, - .ndo_set_multicast_list = ei_set_multicast_list, + .ndo_start_xmit = __ei_start_xmit, + .ndo_tx_timeout = __ei_tx_timeout, + .ndo_get_stats = __ei_get_stats, + .ndo_set_multicast_list = __ei_set_multicast_list, .ndo_validate_addr = eth_validate_addr, - .ndo_set_mac_address = eth_mac_addr, + .ndo_set_mac_address = eth_mac_addr, .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = ei_poll, + .ndo_poll_controller = __ei_poll, #endif }; @@ -637,7 +637,7 @@ int init_module(void) int err; for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) { - struct net_device *dev = alloc_ei_netdev(); + struct net_device *dev = ____alloc_ei_netdev(0); if (!dev) break; if (io[this_dev]) { From d37cd3a2ab0377ba7176ca289c51950def44c9b8 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 12 May 2011 09:11:39 +0000 Subject: [PATCH 1761/2556] hydra: Fix regression caused during net_device_ops conversion commit 0b25e0157dfa236a0629c16c8ad6f222f633f682 upstream. Changeset 5618f0d1193d6b051da9b59b0e32ad24397f06a4 ("hydra: convert to net_device_ops") broke hydra by adding 8390.o to the link. That meant that lib8390.c was included twice, once in hydra.c and once in 8390.c, subject to different macros. This patch reverts that by avoiding the wrappers in 8390.c. Fix based on commits 217cbfa856dc1cbc2890781626c4032d9e3ec59f ("mac8390: fix regression caused during net_device_ops conversion") and 4e0168fa4842e27795a75b205a510f25b62181d9 ("mac8390: fix build with NET_POLL_CONTROLLER"). Signed-off-by: Geert Uytterhoeven Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/Makefile | 2 +- drivers/net/hydra.c | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 9607d87cfc439..c5fc18f15a7f8 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -229,7 +229,7 @@ obj-$(CONFIG_SGI_IOC3_ETH) += ioc3-eth.o obj-$(CONFIG_DECLANCE) += declance.o obj-$(CONFIG_ATARILANCE) += atarilance.o obj-$(CONFIG_A2065) += a2065.o -obj-$(CONFIG_HYDRA) += hydra.o 8390.o +obj-$(CONFIG_HYDRA) += hydra.o obj-$(CONFIG_ARIADNE) += ariadne.o obj-$(CONFIG_CS89x0) += cs89x0.o obj-$(CONFIG_MACSONIC) += macsonic.o diff --git a/drivers/net/hydra.c b/drivers/net/hydra.c index c5ef62ceb8403..1cd481c04202f 100644 --- a/drivers/net/hydra.c +++ b/drivers/net/hydra.c @@ -98,15 +98,15 @@ static const struct net_device_ops hydra_netdev_ops = { .ndo_open = hydra_open, .ndo_stop = hydra_close, - .ndo_start_xmit = ei_start_xmit, - .ndo_tx_timeout = ei_tx_timeout, - .ndo_get_stats = ei_get_stats, - .ndo_set_multicast_list = ei_set_multicast_list, + .ndo_start_xmit = __ei_start_xmit, + .ndo_tx_timeout = __ei_tx_timeout, + .ndo_get_stats = __ei_get_stats, + .ndo_set_multicast_list = __ei_set_multicast_list, .ndo_validate_addr = eth_validate_addr, - .ndo_set_mac_address = eth_mac_addr, + .ndo_set_mac_address = eth_mac_addr, .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = ei_poll, + .ndo_poll_controller = __ei_poll, #endif }; @@ -125,7 +125,7 @@ static int __devinit hydra_init(struct zorro_dev *z) 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, }; - dev = alloc_ei_netdev(); + dev = ____alloc_ei_netdev(0); if (!dev) return -ENOMEM; From cc6267bbbedd8caca5293ad2208dfae76b65a575 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 10 May 2011 16:17:10 +0000 Subject: [PATCH 1762/2556] ehea: Fix memory hotplug oops commit 21ccc7936dac5ca9b3e2838bbc112a60f34e18b3 upstream. The ehea driver oopses during memory hotplug if the ports are not up. A simple testcase: # ifconfig ethX down # echo offline > /sys/devices/system/memory/memory32/state Oops: Kernel access of bad area, sig: 11 [#1] last sysfs file: /sys/devices/system/memory/memory32/state REGS: c000000709393110 TRAP: 0300 Not tainted (2.6.39-rc2-01385-g7ef73bc-dirty) DAR: 0000000000000000, DSISR: 40000000 ... NIP [c000000000067c98] .__wake_up_common+0x48/0xf0 LR [c00000000006d034] .__wake_up+0x54/0x90 Call Trace: [c00000000006d034] .__wake_up+0x54/0x90 [d000000006bb6270] .ehea_rereg_mrs+0x140/0x730 [ehea] [d000000006bb69c4] .ehea_mem_notifier+0x164/0x170 [ehea] [c0000000006fc8a8] .notifier_call_chain+0x78/0xf0 [c0000000000b3d70] .__blocking_notifier_call_chain+0x70/0xb0 [c000000000458d78] .memory_notify+0x28/0x40 [c0000000001871d8] .remove_memory+0x208/0x6d0 [c000000000458264] .memory_section_action+0x94/0x140 [c0000000004583ec] .memory_block_change_state+0xdc/0x1d0 [c0000000004585cc] .store_mem_state+0xec/0x160 [c00000000044768c] .sysdev_store+0x3c/0x50 [c00000000020b48c] .sysfs_write_file+0xec/0x1f0 [c00000000018f86c] .vfs_write+0xec/0x1e0 [c00000000018fa88] .SyS_write+0x58/0xd0 To fix this, initialise the waitqueues during port probe instead of port open. Signed-off-by: Anton Blanchard Acked-by: Breno Leitao Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/ehea/ehea_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c index f75d3144b8a50..0bd6d30a1ec7c 100644 --- a/drivers/net/ehea/ehea_main.c +++ b/drivers/net/ehea/ehea_main.c @@ -2688,9 +2688,6 @@ static int ehea_open(struct net_device *dev) netif_start_queue(dev); } - init_waitqueue_head(&port->swqe_avail_wq); - init_waitqueue_head(&port->restart_wq); - mutex_unlock(&port->port_lock); return ret; @@ -3273,6 +3270,9 @@ struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter, INIT_WORK(&port->reset_task, ehea_reset_port); + init_waitqueue_head(&port->swqe_avail_wq); + init_waitqueue_head(&port->restart_wq); + ret = register_netdev(dev); if (ret) { pr_err("register_netdev failed. ret=%d\n", ret); From 86ef111cb2e9961fd92795f9ac5cafbcaf4dcbe1 Mon Sep 17 00:00:00 2001 From: Paul Fox Date: Mon, 9 May 2011 10:40:42 +0100 Subject: [PATCH 1763/2556] libertas: fix cmdpendingq locking commit 2ae1b8b35faba31a59b153cbad07f9c15de99740 upstream. We occasionally see list corruption using libertas. While we haven't been able to diagnose this precisely, we have spotted a possible cause: cmdpendingq is generally modified with driver_lock held. However, there are a couple of points where this is not the case. Fix up those operations to execute under the lock, it seems like the correct thing to do and will hopefully improve the situation. Signed-off-by: Paul Fox Signed-off-by: Daniel Drake Acked-by: Dan Williams Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/libertas/cmd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index 78c4da150a745..b9b0a0cec7966 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -1335,8 +1335,8 @@ int lbs_execute_next_command(struct lbs_private *priv) cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { lbs_deb_host( "EXEC_NEXT_CMD: ignore ENTER_PS cmd\n"); - list_del(&cmdnode->list); spin_lock_irqsave(&priv->driver_lock, flags); + list_del(&cmdnode->list); lbs_complete_command(priv, cmdnode, 0); spin_unlock_irqrestore(&priv->driver_lock, flags); @@ -1348,8 +1348,8 @@ int lbs_execute_next_command(struct lbs_private *priv) (priv->psstate == PS_STATE_PRE_SLEEP)) { lbs_deb_host( "EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n"); - list_del(&cmdnode->list); spin_lock_irqsave(&priv->driver_lock, flags); + list_del(&cmdnode->list); lbs_complete_command(priv, cmdnode, 0); spin_unlock_irqrestore(&priv->driver_lock, flags); priv->needtowakeup = 1; @@ -1362,7 +1362,9 @@ int lbs_execute_next_command(struct lbs_private *priv) "EXEC_NEXT_CMD: sending EXIT_PS\n"); } } + spin_lock_irqsave(&priv->driver_lock, flags); list_del(&cmdnode->list); + spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n", le16_to_cpu(cmd->command)); lbs_submit_command(priv, cmdnode); From 6e8a1b8955ad234e6e52d8f3725ce03cbc7940ab Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 12 May 2011 09:11:38 +0000 Subject: [PATCH 1764/2556] zorro8390: Fix regression caused during net_device_ops conversion commit cf7e032fc87d59c475df26c4d40bf45d401b2adb upstream. Changeset b6114794a1c394534659f4a17420e48cf23aa922 ("zorro8390: convert to net_device_ops") broke zorro8390 by adding 8390.o to the link. That meant that lib8390.c was included twice, once in zorro8390.c and once in 8390.c, subject to different macros. This patch reverts that by avoiding the wrappers in 8390.c. Fix based on commits 217cbfa856dc1cbc2890781626c4032d9e3ec59f ("mac8390: fix regression caused during net_device_ops conversion") and 4e0168fa4842e27795a75b205a510f25b62181d9 ("mac8390: fix build with NET_POLL_CONTROLLER"). Reported-by: Christian T. Steigies Suggested-by: Finn Thain Signed-off-by: Geert Uytterhoeven Tested-by: Christian T. Steigies Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/Makefile | 2 +- drivers/net/zorro8390.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/Makefile b/drivers/net/Makefile index c5fc18f15a7f8..94ba9affb8345 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -217,7 +217,7 @@ obj-$(CONFIG_SC92031) += sc92031.o obj-$(CONFIG_LP486E) += lp486e.o obj-$(CONFIG_ETH16I) += eth16i.o -obj-$(CONFIG_ZORRO8390) += zorro8390.o 8390.o +obj-$(CONFIG_ZORRO8390) += zorro8390.o obj-$(CONFIG_HPLANCE) += hplance.o 7990.o obj-$(CONFIG_MVME147_NET) += mvme147.o 7990.o obj-$(CONFIG_EQUALIZER) += eql.o diff --git a/drivers/net/zorro8390.c b/drivers/net/zorro8390.c index b78a38d9172a5..8c7c522a056ac 100644 --- a/drivers/net/zorro8390.c +++ b/drivers/net/zorro8390.c @@ -126,7 +126,7 @@ static int __devinit zorro8390_init_one(struct zorro_dev *z, board = z->resource.start; ioaddr = board+cards[i].offset; - dev = alloc_ei_netdev(); + dev = ____alloc_ei_netdev(0); if (!dev) return -ENOMEM; if (!request_mem_region(ioaddr, NE_IO_EXTENT*2, DRV_NAME)) { @@ -146,15 +146,15 @@ static int __devinit zorro8390_init_one(struct zorro_dev *z, static const struct net_device_ops zorro8390_netdev_ops = { .ndo_open = zorro8390_open, .ndo_stop = zorro8390_close, - .ndo_start_xmit = ei_start_xmit, - .ndo_tx_timeout = ei_tx_timeout, - .ndo_get_stats = ei_get_stats, - .ndo_set_multicast_list = ei_set_multicast_list, + .ndo_start_xmit = __ei_start_xmit, + .ndo_tx_timeout = __ei_tx_timeout, + .ndo_get_stats = __ei_get_stats, + .ndo_set_multicast_list = __ei_set_multicast_list, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, .ndo_change_mtu = eth_change_mtu, #ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = ei_poll, + .ndo_poll_controller = __ei_poll, #endif }; From a621088213d3272950a85875afc25cc87b01b560 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 11 May 2011 15:13:36 -0700 Subject: [PATCH 1765/2556] tmpfs: fix race between umount and writepage commit b1dea800ac39599301d4bb8dcf2b1d29c2558211 upstream. Konstanin Khlebnikov reports that a dangerous race between umount and shmem_writepage can be reproduced by this script: for i in {1..300} ; do mkdir $i while true ; do mount -t tmpfs none $i dd if=/dev/zero of=$i/test bs=1M count=$(($RANDOM % 100)) umount $i done & done on a 6xCPU node with 8Gb RAM: kernel very unstable after this accident. =) Kernel log: VFS: Busy inodes after unmount of tmpfs. Self-destruct in 5 seconds. Have a nice day... WARNING: at lib/list_debug.c:53 __list_del_entry+0x8d/0x98() list_del corruption. prev->next should be ffff880222fdaac8, but was (null) Pid: 11222, comm: mount.tmpfs Not tainted 2.6.39-rc2+ #4 Call Trace: warn_slowpath_common+0x80/0x98 warn_slowpath_fmt+0x41/0x43 __list_del_entry+0x8d/0x98 evict+0x50/0x113 iput+0x138/0x141 ... BUG: unable to handle kernel paging request at ffffffffffffffff IP: shmem_free_blocks+0x18/0x4c Pid: 10422, comm: dd Tainted: G W 2.6.39-rc2+ #4 Call Trace: shmem_recalc_inode+0x61/0x66 shmem_writepage+0xba/0x1dc pageout+0x13c/0x24c shrink_page_list+0x28e/0x4be shrink_inactive_list+0x21f/0x382 ... shmem_writepage() calls igrab() on the inode for the page which came from page reclaim, to add it later into shmem_swaplist for swapoff operation. This igrab() can race with super-block deactivating process: shrink_inactive_list() deactivate_super() pageout() tmpfs_fs_type->kill_sb() shmem_writepage() kill_litter_super() generic_shutdown_super() evict_inodes() igrab() atomic_read(&inode->i_count) skip-inode iput() if (!list_empty(&sb->s_inodes)) printk("VFS: Busy inodes after... This igrap-iput pair was added in commit 1b1b32f2c6f6 "tmpfs: fix shmem_swaplist races" based on incorrect assumptions: igrab() protects the inode from concurrent eviction by deletion, but it does nothing to protect it from concurrent unmounting, which goes ahead despite the raised i_count. So this use of igrab() was wrong all along, but the race made much worse in 2.6.37 when commit 63997e98a3be "split invalidate_inodes()" replaced two attempts at invalidate_inodes() by a single evict_inodes(). Konstantin posted a plausible patch, raising sb->s_active too: I'm unsure whether it was correct or not; but burnt once by igrab(), I am sure that we don't want to rely more deeply upon externals here. Fix it by adding the inode to shmem_swaplist earlier, while the page lock on page in page cache still secures the inode against eviction, without artifically raising i_count. It was originally added later because shmem_unuse_inode() is liable to remove an inode from the list while it's unswapped; but we can guard against that by taking spinlock before dropping mutex. Reported-by: Konstantin Khlebnikov Signed-off-by: Hugh Dickins Tested-by: Konstantin Khlebnikov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/shmem.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index 8ac1684d79086..bd376ba6fd3cd 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1037,6 +1037,7 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc) struct address_space *mapping; unsigned long index; struct inode *inode; + bool unlock_mutex = false; BUG_ON(!PageLocked(page)); mapping = page->mapping; @@ -1062,7 +1063,26 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc) else swap.val = 0; + /* + * Add inode to shmem_unuse()'s list of swapped-out inodes, + * if it's not already there. Do it now because we cannot take + * mutex while holding spinlock, and must do so before the page + * is moved to swap cache, when its pagelock no longer protects + * the inode from eviction. But don't unlock the mutex until + * we've taken the spinlock, because shmem_unuse_inode() will + * prune a !swapped inode from the swaplist under both locks. + */ + if (swap.val && list_empty(&info->swaplist)) { + mutex_lock(&shmem_swaplist_mutex); + /* move instead of add in case we're racing */ + list_move_tail(&info->swaplist, &shmem_swaplist); + unlock_mutex = true; + } + spin_lock(&info->lock); + if (unlock_mutex) + mutex_unlock(&shmem_swaplist_mutex); + if (index >= info->next_index) { BUG_ON(!(info->flags & SHMEM_TRUNCATE)); goto unlock; @@ -1082,22 +1102,11 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc) remove_from_page_cache(page); shmem_swp_set(info, entry, swap.val); shmem_swp_unmap(entry); - if (list_empty(&info->swaplist)) - inode = igrab(inode); - else - inode = NULL; spin_unlock(&info->lock); swap_shmem_alloc(swap); BUG_ON(page_mapped(page)); page_cache_release(page); /* pagecache ref */ swap_writepage(page, wbc); - if (inode) { - mutex_lock(&shmem_swaplist_mutex); - /* move instead of add in case we're racing */ - list_move_tail(&info->swaplist, &shmem_swaplist); - mutex_unlock(&shmem_swaplist_mutex); - iput(inode); - } return 0; } From 3c249016aed25db602d4849c2690521cf5e1ff2f Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Sat, 14 May 2011 12:06:42 -0700 Subject: [PATCH 1766/2556] tmpfs: fix race between swapoff and writepage commit 05bf86b4ccfd0f197da61c67bd372111d15a6620 upstream. Shame on me! Commit b1dea800ac39 "tmpfs: fix race between umount and writepage" fixed the advertized race, but introduced another: as even its comment makes clear, we cannot safely rely on a peek at list_empty() while holding no lock - until info->swapped is set, shmem_unuse_inode() may delete any formerly-swapped inode from the shmem_swaplist, which in this case would leave a swap area impossible to swapoff. Although I don't relish taking the mutex every time, I don't care much for the alternatives either; and at least the peek at list_empty() in shmem_evict_inode() (a hotter path since most inodes would never have been swapped) remains safe, because we already truncated the whole file. Signed-off-by: Hugh Dickins Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/shmem.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index bd376ba6fd3cd..436fef08356ef 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1037,7 +1037,6 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc) struct address_space *mapping; unsigned long index; struct inode *inode; - bool unlock_mutex = false; BUG_ON(!PageLocked(page)); mapping = page->mapping; @@ -1072,15 +1071,14 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc) * we've taken the spinlock, because shmem_unuse_inode() will * prune a !swapped inode from the swaplist under both locks. */ - if (swap.val && list_empty(&info->swaplist)) { + if (swap.val) { mutex_lock(&shmem_swaplist_mutex); - /* move instead of add in case we're racing */ - list_move_tail(&info->swaplist, &shmem_swaplist); - unlock_mutex = true; + if (list_empty(&info->swaplist)) + list_add_tail(&info->swaplist, &shmem_swaplist); } spin_lock(&info->lock); - if (unlock_mutex) + if (swap.val) mutex_unlock(&shmem_swaplist_mutex); if (index >= info->next_index) { From a1cae297f2bfede2268dee6ce994ef3d861db9ac Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Thu, 14 Apr 2011 15:22:07 -0700 Subject: [PATCH 1767/2556] tmpfs: fix off-by-one in max_blocks checks commit fc5da22ae35d4720be59af8787a8a6d5e4da9517 upstream. If you fill up a tmpfs, df was showing tmpfs 460800 - - - /tmp because of an off-by-one in the max_blocks checks. Fix it so df shows tmpfs 460800 460800 0 100% /tmp Signed-off-by: Hugh Dickins Cc: Tim Chen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/shmem.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index 436fef08356ef..b44006dc00217 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -422,7 +422,8 @@ static swp_entry_t *shmem_swp_alloc(struct shmem_inode_info *info, unsigned long * a waste to allocate index if we cannot allocate data. */ if (sbinfo->max_blocks) { - if (percpu_counter_compare(&sbinfo->used_blocks, (sbinfo->max_blocks - 1)) > 0) + if (percpu_counter_compare(&sbinfo->used_blocks, + sbinfo->max_blocks - 1) >= 0) return ERR_PTR(-ENOSPC); percpu_counter_inc(&sbinfo->used_blocks); spin_lock(&inode->i_lock); @@ -1404,7 +1405,8 @@ static int shmem_getpage(struct inode *inode, unsigned long idx, shmem_swp_unmap(entry); sbinfo = SHMEM_SB(inode->i_sb); if (sbinfo->max_blocks) { - if ((percpu_counter_compare(&sbinfo->used_blocks, sbinfo->max_blocks) > 0) || + if (percpu_counter_compare(&sbinfo->used_blocks, + sbinfo->max_blocks) >= 0 || shmem_acct_block(info->flags)) { spin_unlock(&info->lock); error = -ENOSPC; From e69ce03f62c57295465701bb86e0bca560944764 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 11 May 2011 15:13:38 -0700 Subject: [PATCH 1768/2556] tmpfs: fix spurious ENOSPC when racing with unswap commit 59a16ead572330deb38e5848151d30ed1af754bc upstream. Testing the shmem_swaplist replacements for igrab() revealed another bug: writes to /dev/loop0 on a tmpfs file which fills its filesystem were sometimes failing with "Buffer I/O error"s. These came from ENOSPC failures of shmem_getpage(), when racing with swapoff: the same could happen when racing with another shmem_getpage(), pulling the page in from swap in between our find_lock_page() and our taking the info->lock (though not in the single-threaded loop case). This is unacceptable, and surprising that I've not noticed it before: it dates back many years, but (presumably) was made a lot easier to reproduce in 2.6.36, which sited a page preallocation in the race window. Fix it by rechecking the page cache before settling on an ENOSPC error. Signed-off-by: Hugh Dickins Cc: Konstantin Khlebnikov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/shmem.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index b44006dc00217..3ca835061291f 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1407,20 +1407,14 @@ static int shmem_getpage(struct inode *inode, unsigned long idx, if (sbinfo->max_blocks) { if (percpu_counter_compare(&sbinfo->used_blocks, sbinfo->max_blocks) >= 0 || - shmem_acct_block(info->flags)) { - spin_unlock(&info->lock); - error = -ENOSPC; - goto failed; - } + shmem_acct_block(info->flags)) + goto nospace; percpu_counter_inc(&sbinfo->used_blocks); spin_lock(&inode->i_lock); inode->i_blocks += BLOCKS_PER_PAGE; spin_unlock(&inode->i_lock); - } else if (shmem_acct_block(info->flags)) { - spin_unlock(&info->lock); - error = -ENOSPC; - goto failed; - } + } else if (shmem_acct_block(info->flags)) + goto nospace; if (!filepage) { int ret; @@ -1500,6 +1494,24 @@ static int shmem_getpage(struct inode *inode, unsigned long idx, error = 0; goto out; +nospace: + /* + * Perhaps the page was brought in from swap between find_lock_page + * and taking info->lock? We allow for that at add_to_page_cache_lru, + * but must also avoid reporting a spurious ENOSPC while working on a + * full tmpfs. (When filepage has been passed in to shmem_getpage, it + * is already in page cache, which prevents this race from occurring.) + */ + if (!filepage) { + struct page *page = find_get_page(mapping, idx); + if (page) { + spin_unlock(&info->lock); + page_cache_release(page); + goto repeat; + } + } + spin_unlock(&info->lock); + error = -ENOSPC; failed: if (*pagep != filepage) { unlock_page(filepage); From 911b30ea52d3fb8e15e1cbba5a081ed3fab4f421 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 9 May 2011 16:04:11 +0200 Subject: [PATCH 1769/2556] libata: fix oops when LPM is used with PMP commit 5f6f12ccf3aa42cfc0c5bde9228df0c843dd63f7 upstream. ae01b2493c (libata: Implement ATA_FLAG_NO_DIPM and apply it to mcp65) added ATA_FLAG_NO_DIPM and made ata_eh_set_lpm() check the flag. However, @ap is NULL if @link points to a PMP link and thus the unconditional @ap->flags dereference leads to the following oops. BUG: unable to handle kernel NULL pointer dereference at 0000000000000018 IP: [] ata_eh_recover+0x9a1/0x1510 ... Pid: 295, comm: scsi_eh_4 Tainted: P 2.6.38.5-core2 #1 System76, Inc. Serval Professional/Serval Professional RIP: 0010:[] [] ata_eh_recover+0x9a1/0x1510 RSP: 0018:ffff880132defbf0 EFLAGS: 00010246 RAX: 0000000000000000 RBX: ffff880132f40000 RCX: 0000000000000000 RDX: ffff88013377c000 RSI: ffff880132f40000 RDI: 0000000000000000 RBP: ffff880132defce0 R08: ffff88013377dc58 R09: ffff880132defd98 R10: 0000000000000000 R11: 00000000ffffffff R12: 0000000000000000 R13: 0000000000000000 R14: ffff88013377c000 R15: 0000000000000000 FS: 0000000000000000(0000) GS:ffff8800bf700000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000018 CR3: 0000000001a03000 CR4: 00000000000406e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process scsi_eh_4 (pid: 295, threadinfo ffff880132dee000, task ffff880133b416c0) Stack: 0000000000000000 ffff880132defcc0 0000000000000000 ffff880132f42738 ffffffff813ee8f0 ffffffff813eefe0 ffff880132defd98 ffff88013377f190 ffffffffa00b3e30 ffffffff813ef030 0000000032defc60 ffff880100000000 Call Trace: [] sata_pmp_error_handler+0x607/0xc30 [] ahci_error_handler+0x1f/0x70 [libahci] [] ata_scsi_error+0x5be/0x900 [] scsi_error_handler+0x124/0x650 [] kthread+0x96/0xa0 [] kernel_thread_helper+0x4/0x10 Code: 8b 95 70 ff ff ff b8 00 00 00 00 48 3b 9a 10 2e 00 00 48 0f 44 c2 48 89 85 70 ff ff ff 48 8b 8d 70 ff ff ff f6 83 69 02 00 00 01 <48> 8b 41 18 0f 85 48 01 00 00 48 85 c9 74 12 48 8b 51 08 48 83 RIP [] ata_eh_recover+0x9a1/0x1510 RSP CR2: 0000000000000018 Fix it by testing @link->ap->flags instead. stable: ATA_FLAG_NO_DIPM was added during 2.6.39 cycle but was backported to 2.6.37 and 38. This is a fix for that and thus also applicable to 2.6.37 and 38. Signed-off-by: Tejun Heo Reported-by: "Nathan A. Mourey II" LKML-Reference: <1304555277.2059.2.camel@localhost.localdomain> Cc: Connor H Signed-off-by: Jeff Garzik Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-eh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index fe18c2de41ba1..09329a1eea62c 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -3276,7 +3276,7 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, struct ata_eh_context *ehc = &link->eh_context; struct ata_device *dev, *link_dev = NULL, *lpm_dev = NULL; enum ata_lpm_policy old_policy = link->lpm_policy; - bool no_dipm = ap->flags & ATA_FLAG_NO_DIPM; + bool no_dipm = link->ap->flags & ATA_FLAG_NO_DIPM; unsigned int hints = ATA_LPM_EMPTY | ATA_LPM_HIPM; unsigned int err_mask; int rc; From 75059cf1749d3e92a5119789ff33f951fbbd1c31 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 11 May 2011 14:02:07 -0400 Subject: [PATCH 1770/2556] drm/radeon/kms: fix extended lvds info parsing commit 05fa7ea7d23980de0014417a0e0af2048a0f9fc1 upstream. On rev <= 1.1 tables, the offset is absolute, on newer tables, it's relative. Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=700326 Signed-off-by: Alex Deucher Reviewed-by: Jerome Glisse Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_atombios.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 53df8f98e1cd3..ad11a057a9f25 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -1574,9 +1574,17 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct ATOM_FAKE_EDID_PATCH_RECORD *fake_edid_record; ATOM_PANEL_RESOLUTION_PATCH_RECORD *panel_res_record; bool bad_record = false; - u8 *record = (u8 *)(mode_info->atom_context->bios + - data_offset + - le16_to_cpu(lvds_info->info.usModePatchTableOffset)); + u8 *record; + + if ((frev == 1) && (crev < 2)) + /* absolute */ + record = (u8 *)(mode_info->atom_context->bios + + le16_to_cpu(lvds_info->info.usModePatchTableOffset)); + else + /* relative */ + record = (u8 *)(mode_info->atom_context->bios + + data_offset + + le16_to_cpu(lvds_info->info.usModePatchTableOffset)); while (*record != ATOM_RECORD_END_TYPE) { switch (*record) { case LCD_MODE_PATCH_RECORD_MODE_TYPE: From 758b8cc02fae512d6748a87d1c8656a94b1fecaa Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Mon, 16 May 2011 11:32:26 -0400 Subject: [PATCH 1771/2556] Revert "mmc: fix a race between card-detect rescan and clock-gate work instances" commit 86f315bbb2374f1f077500ad131dd9b71856e697 upstream. This reverts commit 26fc8775b51484d8c0a671198639c6d5ae60533e, which has been reported to cause boot/resume-time crashes for some users: https://bbs.archlinux.org/viewtopic.php?id=118751. Signed-off-by: Chris Ball Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/core/host.c | 9 +++++---- include/linux/mmc/host.h | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 9ae3dbfba3661..b3ac6c5bc5c62 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -94,7 +94,7 @@ static void mmc_host_clk_gate_delayed(struct mmc_host *host) spin_unlock_irqrestore(&host->clk_lock, flags); return; } - mmc_claim_host(host); + mutex_lock(&host->clk_gate_mutex); spin_lock_irqsave(&host->clk_lock, flags); if (!host->clk_requests) { spin_unlock_irqrestore(&host->clk_lock, flags); @@ -104,7 +104,7 @@ static void mmc_host_clk_gate_delayed(struct mmc_host *host) pr_debug("%s: gated MCI clock\n", mmc_hostname(host)); } spin_unlock_irqrestore(&host->clk_lock, flags); - mmc_release_host(host); + mutex_unlock(&host->clk_gate_mutex); } /* @@ -130,7 +130,7 @@ void mmc_host_clk_ungate(struct mmc_host *host) { unsigned long flags; - mmc_claim_host(host); + mutex_lock(&host->clk_gate_mutex); spin_lock_irqsave(&host->clk_lock, flags); if (host->clk_gated) { spin_unlock_irqrestore(&host->clk_lock, flags); @@ -140,7 +140,7 @@ void mmc_host_clk_ungate(struct mmc_host *host) } host->clk_requests++; spin_unlock_irqrestore(&host->clk_lock, flags); - mmc_release_host(host); + mutex_unlock(&host->clk_gate_mutex); } /** @@ -218,6 +218,7 @@ static inline void mmc_host_clk_init(struct mmc_host *host) host->clk_gated = false; INIT_WORK(&host->clk_gate_work, mmc_host_clk_gate_work); spin_lock_init(&host->clk_lock); + mutex_init(&host->clk_gate_mutex); } /** diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index eb792cb6d745a..bcb793ec7374f 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -183,6 +183,7 @@ struct mmc_host { struct work_struct clk_gate_work; /* delayed clock gate */ unsigned int clk_old; /* old clock value cache */ spinlock_t clk_lock; /* lock for clk fields */ + struct mutex clk_gate_mutex; /* mutex for clock gating */ #endif /* host specific block data */ From ba0bb0c9b289b919dc7b106a26ee05e1516171a8 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 17 May 2011 06:40:30 -0400 Subject: [PATCH 1772/2556] cifs: add fallback in is_path_accessible for old servers commit 221d1d797202984cb874e3ed9f1388593d34ee22 upstream. The is_path_accessible check uses a QPathInfo call, which isn't supported by ancient win9x era servers. Fall back to an older SMBQueryInfo call if it fails with the magic error codes. Reported-and-Tested-by: Sandro Bonazzola Signed-off-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/connect.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index bd7e61fbd27e3..1f4db2cca3370 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2657,6 +2657,11 @@ is_path_accessible(int xid, struct cifsTconInfo *tcon, 0 /* not legacy */, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + + if (rc == -EOPNOTSUPP || rc == -EINVAL) + rc = SMBQueryInformation(xid, tcon, full_path, pfile_info, + cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); kfree(pfile_info); return rc; } From 1f3696ee34df25a119a6efc225a90af5864a982f Mon Sep 17 00:00:00 2001 From: Alexandre Bounine Date: Tue, 17 May 2011 15:44:08 -0700 Subject: [PATCH 1773/2556] rapidio: fix default routing initialization commit 0bf2461fdd9008290cf429e50e4f362dafab4249 upstream. Fix switch initialization to ensure that all switches have default routing disabled. This guarantees that no unexpected RapidIO packets arrive to the default port set by reset and there is no default routing destination until it is properly configured by software. This update also unifies handling of unmapped destinations by tsi57x, IDT Gen1 and IDT Gen2 switches. Signed-off-by: Alexandre Bounine Cc: Kumar Gala Cc: Matt Porter Cc: Li Yang Cc: Thomas Moll Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/rapidio/switches/idt_gen2.c | 9 +++++++++ drivers/rapidio/switches/idtcps.c | 6 ++++++ drivers/rapidio/switches/tsi57x.c | 6 ++++++ 3 files changed, 21 insertions(+) diff --git a/drivers/rapidio/switches/idt_gen2.c b/drivers/rapidio/switches/idt_gen2.c index 095016a9dec16..a70f37c4f49ff 100644 --- a/drivers/rapidio/switches/idt_gen2.c +++ b/drivers/rapidio/switches/idt_gen2.c @@ -95,6 +95,9 @@ idtg2_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, else table++; + if (route_port == RIO_INVALID_ROUTE) + route_port = IDT_DEFAULT_ROUTE; + rio_mport_write_config_32(mport, destid, hopcount, LOCAL_RTE_CONF_DESTID_SEL, table); @@ -411,6 +414,12 @@ static int idtg2_switch_init(struct rio_dev *rdev, int do_enum) rdev->rswitch->em_handle = idtg2_em_handler; rdev->rswitch->sw_sysfs = idtg2_sysfs; + if (do_enum) { + /* Ensure that default routing is disabled on startup */ + rio_write_config_32(rdev, + RIO_STD_RTE_DEFAULT_PORT, IDT_NO_ROUTE); + } + return 0; } diff --git a/drivers/rapidio/switches/idtcps.c b/drivers/rapidio/switches/idtcps.c index 3a971077e7bfc..d06ee2d44b447 100644 --- a/drivers/rapidio/switches/idtcps.c +++ b/drivers/rapidio/switches/idtcps.c @@ -26,6 +26,9 @@ idtcps_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, { u32 result; + if (route_port == RIO_INVALID_ROUTE) + route_port = CPS_DEFAULT_ROUTE; + if (table == RIO_GLOBAL_TABLE) { rio_mport_write_config_32(mport, destid, hopcount, RIO_STD_RTE_CONF_DESTID_SEL_CSR, route_destid); @@ -130,6 +133,9 @@ static int idtcps_switch_init(struct rio_dev *rdev, int do_enum) /* set TVAL = ~50us */ rio_write_config_32(rdev, rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x8e << 8); + /* Ensure that default routing is disabled on startup */ + rio_write_config_32(rdev, + RIO_STD_RTE_DEFAULT_PORT, CPS_NO_ROUTE); } return 0; diff --git a/drivers/rapidio/switches/tsi57x.c b/drivers/rapidio/switches/tsi57x.c index 1a62934bfebc5..db8b8028988d3 100644 --- a/drivers/rapidio/switches/tsi57x.c +++ b/drivers/rapidio/switches/tsi57x.c @@ -303,6 +303,12 @@ static int tsi57x_switch_init(struct rio_dev *rdev, int do_enum) rdev->rswitch->em_init = tsi57x_em_init; rdev->rswitch->em_handle = tsi57x_em_handler; + if (do_enum) { + /* Ensure that default routing is disabled on startup */ + rio_write_config_32(rdev, RIO_STD_RTE_DEFAULT_PORT, + RIO_INVALID_ROUTE); + } + return 0; } From a6fab0cc993f6de06991c054c287e902063a928b Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 17 May 2011 14:55:18 +0200 Subject: [PATCH 1774/2556] Revert "x86, AMD: Fix APIC timer erratum 400 affecting K8 Rev.A-E processors" commit 328935e6348c6a7cb34798a68c326f4b8372e68a upstream. This reverts commit e20a2d205c05cef6b5783df339a7d54adeb50962, as it crashes certain boxes with specific AMD CPU models. Moving the lower endpoint of the Erratum 400 check to accomodate earlier K8 revisions (A-E) opens a can of worms which is simply not worth to fix properly by tweaking the errata checking framework: * missing IntPenging MSR on revisions < CG cause #GP: http://marc.info/?l=linux-kernel&m=130541471818831 * makes earlier revisions use the LAPIC timer instead of the C1E idle routine which switches to HPET, thus not waking up in deeper C-states: http://lkml.org/lkml/2011/4/24/20 Therefore, leave the original boundary starting with K8-revF. Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/amd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index b65c707716fa3..a2b9c7d413230 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -681,7 +681,7 @@ cpu_dev_register(amd_cpu_dev); */ const int amd_erratum_400[] = - AMD_OSVW_ERRATUM(1, AMD_MODEL_RANGE(0x0f, 0x4, 0x2, 0xff, 0xf), + AMD_OSVW_ERRATUM(1, AMD_MODEL_RANGE(0xf, 0x41, 0x2, 0xff, 0xf), AMD_MODEL_RANGE(0x10, 0x2, 0x1, 0xff, 0xf)); EXPORT_SYMBOL_GPL(amd_erratum_400); From bf2c1e165ad118dd67f06742cbb435007726b059 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 17 May 2011 14:55:19 +0200 Subject: [PATCH 1775/2556] x86, AMD: Fix ARAT feature setting again commit 14fb57dccb6e1defe9f89a66f548fcb24c374c1d upstream. Trying to enable the local APIC timer on early K8 revisions uncovers a number of other issues with it, in conjunction with the C1E enter path on AMD. Fixing those causes much more churn and troubles than the benefit of using that timer brings so don't enable it on K8 at all, falling back to the original functionality the kernel had wrt to that. Reported-and-bisected-by: Nick Bowler Cc: Boris Ostrovsky Cc: Andreas Herrmann Cc: Greg Kroah-Hartman Cc: Hans Rosenfeld Cc: Nick Bowler Cc: Joerg-Volker-Peetz Signed-off-by: Borislav Petkov Link: http://lkml.kernel.org/r/1305636919-31165-3-git-send-email-bp@amd64.org Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/amd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index a2b9c7d413230..ff32eb37be6da 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -596,7 +596,7 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c) #endif /* As a rule processors have APIC timer running in deep C states */ - if (c->x86 >= 0xf && !cpu_has_amd_erratum(amd_erratum_400)) + if (c->x86 > 0xf && !cpu_has_amd_erratum(amd_erratum_400)) set_cpu_cap(c, X86_FEATURE_ARAT); /* From f8c47371b566131cf60263ce8fe7a593da151387 Mon Sep 17 00:00:00 2001 From: john stultz Date: Wed, 4 May 2011 18:16:50 -0700 Subject: [PATCH 1776/2556] clocksource: Install completely before selecting commit e05b2efb82596905ebfe88e8612ee81dec9b6592 upstream. Christian Hoffmann reported that the command line clocksource override with acpi_pm timer fails: Kernel command line: clocksource=acpi_pm hpet clockevent registered Switching to clocksource hpet Override clocksource acpi_pm is not HRT compatible. Cannot switch while in HRT/NOHZ mode. The watchdog code is what enables CLOCK_SOURCE_VALID_FOR_HRES, but we actually end up selecting the clocksource before we enqueue it into the watchdog list, so that's why we see the warning and fail to switch to acpi_pm timer as requested. That's particularly bad when we want to debug timekeeping related problems in early boot. Put the selection call last. Reported-by: Christian Hoffmann Signed-off-by: John Stultz Link: http://lkml.kernel.org/r/%3C1304558210.2943.24.camel%40work-vm%3E Signed-off-by: Thomas Gleixner Signed-off-by: Greg Kroah-Hartman --- kernel/time/clocksource.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 6519cf62d9cd9..0e17c10f8a9da 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -685,8 +685,8 @@ int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq) /* Add clocksource to the clcoksource list */ mutex_lock(&clocksource_mutex); clocksource_enqueue(cs); - clocksource_select(); clocksource_enqueue_watchdog(cs); + clocksource_select(); mutex_unlock(&clocksource_mutex); return 0; } @@ -706,8 +706,8 @@ int clocksource_register(struct clocksource *cs) mutex_lock(&clocksource_mutex); clocksource_enqueue(cs); - clocksource_select(); clocksource_enqueue_watchdog(cs); + clocksource_select(); mutex_unlock(&clocksource_mutex); return 0; } From 9652c2691df16ae7251510d81a1c730ce67c756d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 16 May 2011 11:07:48 +0200 Subject: [PATCH 1777/2556] tick: Clear broadcast active bit when switching to oneshot commit 07f4beb0b5bbfaf36a64aa00d59e670ec578a95a upstream. The first cpu which switches from periodic to oneshot mode switches also the broadcast device into oneshot mode. The broadcast device serves as a backup for per cpu timers which stop in deeper C-states. To avoid starvation of the cpus which might be in idle and depend on broadcast mode it marks the other cpus as broadcast active and sets the brodcast expiry value of those cpus to the next tick. The oneshot mode broadcast bit for the other cpus is sticky and gets only cleared when those cpus exit idle. If a cpu was not idle while the bit got set in consequence the bit prevents that the broadcast device is armed on behalf of that cpu when it enters idle for the first time after it switched to oneshot mode. In most cases that goes unnoticed as one of the other cpus has usually a timer pending which keeps the broadcast device armed with a short timeout. Now if the only cpu which has a short timer active has the bit set then the broadcast device will not be armed on behalf of that cpu and will fire way after the expected timer expiry. In the case of Christians bug report it took ~145 seconds which is about half of the wrap around time of HPET (the limit for that device) due to the fact that all other cpus had no timers armed which expired before the 145 seconds timeframe. The solution is simply to clear the broadcast active bit unconditionally when a cpu switches to oneshot mode after the first cpu switched the broadcast device over. It's not idle at that point otherwise it would not be executing that code. [ I fundamentally hate that broadcast crap. Why the heck thought some folks that when going into deep idle it's a brilliant concept to switch off the last device which brings the cpu back from that state? ] Thanks to Christian for providing all the valuable debug information! Reported-and-tested-by: Christian Hoffmann Cc: John Stultz Link: http://lkml.kernel.org/r/%3Calpine.LFD.2.02.1105161105170.3078%40ionos%3E Signed-off-by: Thomas Gleixner Signed-off-by: Greg Kroah-Hartman --- kernel/time/tick-broadcast.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index a3b5aff626064..2bb742c964950 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -523,10 +523,11 @@ static void tick_broadcast_init_next_event(struct cpumask *mask, */ void tick_broadcast_setup_oneshot(struct clock_event_device *bc) { + int cpu = smp_processor_id(); + /* Set it up only once ! */ if (bc->event_handler != tick_handle_oneshot_broadcast) { int was_periodic = bc->mode == CLOCK_EVT_MODE_PERIODIC; - int cpu = smp_processor_id(); bc->event_handler = tick_handle_oneshot_broadcast; clockevents_set_mode(bc, CLOCK_EVT_MODE_ONESHOT); @@ -552,6 +553,15 @@ void tick_broadcast_setup_oneshot(struct clock_event_device *bc) tick_broadcast_set_event(tick_next_period, 1); } else bc->next_event.tv64 = KTIME_MAX; + } else { + /* + * The first cpu which switches to oneshot mode sets + * the bit for all other cpus which are in the general + * (periodic) broadcast mask. So the bit is set and + * would prevent the first broadcast enter after this + * to program the bc device. + */ + tick_broadcast_clear_oneshot(cpu); } } From c0f0a12212ed64f082b3700dab1f8e72f6227004 Mon Sep 17 00:00:00 2001 From: Youquan Song Date: Fri, 22 Apr 2011 00:22:43 +0800 Subject: [PATCH 1778/2556] x86, apic: Fix spurious error interrupts triggering on all non-boot APs commit e503f9e4b092e2349a9477a333543de8f3c7f5d9 upstream. This patch fixes a bug reported by a customer, who found that many unreasonable error interrupts reported on all non-boot CPUs (APs) during the system boot stage. According to Chapter 10 of Intel Software Developer Manual Volume 3A, Local APIC may signal an illegal vector error when an LVT entry is set as an illegal vector value (0~15) under FIXED delivery mode (bits 8-11 is 0), regardless of whether the mask bit is set or an interrupt actually happen. These errors are seen as error interrupts. The initial value of thermal LVT entries on all APs always reads 0x10000 because APs are woken up by BSP issuing INIT-SIPI-SIPI sequence to them and LVT registers are reset to 0s except for the mask bits which are set to 1s when APs receive INIT IPI. When the BIOS takes over the thermal throttling interrupt, the LVT thermal deliver mode should be SMI and it is required from the kernel to keep AP's LVT thermal monitoring register programmed as such as well. This issue happens when BIOS does not take over thermal throttling interrupt, AP's LVT thermal monitor register will be restored to 0x10000 which means vector 0 and fixed deliver mode, so all APs will signal illegal vector error interrupts. This patch check if interrupt delivery mode is not fixed mode before restoring AP's LVT thermal monitor register. Signed-off-by: Youquan Song Acked-by: Suresh Siddha Acked-by: Yong Wang Cc: hpa@linux.intel.com Cc: joe@perches.com Cc: jbaron@redhat.com Cc: trenn@suse.de Cc: kent.liu@intel.com Cc: chaohong.guo@intel.com Link: http://lkml.kernel.org/r/1303402963-17738-1-git-send-email-youquan.song@intel.com Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/apicdef.h | 1 + arch/x86/kernel/cpu/mcheck/therm_throt.c | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/arch/x86/include/asm/apicdef.h b/arch/x86/include/asm/apicdef.h index 47a30ff8e5178..8ac7695c15f7e 100644 --- a/arch/x86/include/asm/apicdef.h +++ b/arch/x86/include/asm/apicdef.h @@ -78,6 +78,7 @@ #define APIC_DEST_LOGICAL 0x00800 #define APIC_DEST_PHYSICAL 0x00000 #define APIC_DM_FIXED 0x00000 +#define APIC_DM_FIXED_MASK 0x00700 #define APIC_DM_LOWEST 0x00100 #define APIC_DM_SMI 0x00200 #define APIC_DM_REMRD 0x00300 diff --git a/arch/x86/kernel/cpu/mcheck/therm_throt.c b/arch/x86/kernel/cpu/mcheck/therm_throt.c index 6f8c5e9da97f0..0f034460260d5 100644 --- a/arch/x86/kernel/cpu/mcheck/therm_throt.c +++ b/arch/x86/kernel/cpu/mcheck/therm_throt.c @@ -446,18 +446,20 @@ void intel_init_thermal(struct cpuinfo_x86 *c) */ rdmsr(MSR_IA32_MISC_ENABLE, l, h); + h = lvtthmr_init; /* * The initial value of thermal LVT entries on all APs always reads * 0x10000 because APs are woken up by BSP issuing INIT-SIPI-SIPI * sequence to them and LVT registers are reset to 0s except for * the mask bits which are set to 1s when APs receive INIT IPI. - * Always restore the value that BIOS has programmed on AP based on - * BSP's info we saved since BIOS is always setting the same value - * for all threads/cores + * If BIOS takes over the thermal interrupt and sets its interrupt + * delivery mode to SMI (not fixed), it restores the value that the + * BIOS has programmed on AP based on BSP's info we saved since BIOS + * is always setting the same value for all threads/cores. */ - apic_write(APIC_LVTTHMR, lvtthmr_init); + if ((h & APIC_DM_FIXED_MASK) != APIC_DM_FIXED) + apic_write(APIC_LVTTHMR, lvtthmr_init); - h = lvtthmr_init; if ((l & MSR_IA32_MISC_ENABLE_TM1) && (h & APIC_DM_SMI)) { printk(KERN_DEBUG From 55285e0fd90b9cf00276f66bb491e28ecdee11d9 Mon Sep 17 00:00:00 2001 From: Lawrence Rust Date: Fri, 8 Apr 2011 09:50:45 -0300 Subject: [PATCH 1779/2556] Fix cx88 remote control input commit 2a164d02dd34c6b49a3f0995900e0f8af102b804 upstream. In the IR interrupt handler of cx88-input.c there's a 32-bit multiply overflow which causes IR pulse durations to be incorrectly calculated. This is a regression caused by commit 2997137be8eba. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/video/cx88/cx88-input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index ac4f8e0750ef1..67a2b086101ad 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -523,7 +523,7 @@ void cx88_ir_irq(struct cx88_core *core) for (todo = 32; todo > 0; todo -= bits) { ev.pulse = samples & 0x80000000 ? false : true; bits = min(todo, 32U - fls(ev.pulse ? samples : ~samples)); - ev.duration = (bits * NSEC_PER_SEC) / (1000 * ir_samplerate); + ev.duration = (bits * (NSEC_PER_SEC / 1000)) / ir_samplerate; ir_raw_event_store_with_filter(ir->dev, &ev); samples <<= bits; } From 9e7b09d09931b8ef2d1e46335bc41eff5ff0d23a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 30 Apr 2011 10:34:05 -0300 Subject: [PATCH 1780/2556] v4l: Release module if subdev registration fails commit b7534f002d3c81d18abfbf57179d07d3ec763bb5 upstream. If v4l2_device_register_subdev() fails, the reference to the subdev module taken by the function isn't released. Fix this. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/video/v4l2-device.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index ce64fe16bc604..3504fc6d7b713 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -131,14 +131,17 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, sd->v4l2_dev = v4l2_dev; if (sd->internal_ops && sd->internal_ops->registered) { err = sd->internal_ops->registered(sd); - if (err) + if (err) { + module_put(sd->owner); return err; + } } /* This just returns 0 if either of the two args is NULL */ err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler); if (err) { if (sd->internal_ops && sd->internal_ops->unregistered) sd->internal_ops->unregistered(sd); + module_put(sd->owner); return err; } spin_lock(&v4l2_dev->lock); From 9af9f92080401b2f68e88ef2df266f2eaeca73e0 Mon Sep 17 00:00:00 2001 From: Cliff Wickman Date: Tue, 10 May 2011 08:26:43 -0500 Subject: [PATCH 1781/2556] x86: Fix UV BAU for non-consecutive nasids commit 77ed23f8d995a01cd8101d84351b567bf5177a30 upstream. This is a fix for the SGI Altix-UV Broadcast Assist Unit code, which is used for TLB flushing. Certain hardware configurations (that customers are ordering) cause nasids (numa address space id's) to be non-consecutive. Specifically, once you have more than 4 blades in a IRU (Individual Rack Unit - or 1/2 rack) but less than the maximum of 16, the nasid numbering becomes non-consecutive. This currently results in a 'catastrophic error' (CATERR) detected by the firmware during OS boot. The BAU is generating an 'INTD' request that is targeting a non-existent nasid value. Such configurations may also occur when a blade is configured off because of hardware errors. (There is one UV hub per blade.) This patch is required to support such configurations. The problem with the tlb_uv.c code is that is using the consecutive hub numbers as indices to the BAU distribution bit map. These are simply the ordinal position of the hub or blade within its partition. It should be using physical node numbers (pnodes), which correspond to the physical nasid values. Use of the hub number only works as long as the nasids in the partition are consecutive and increase with a stride of 1. This patch changes the index to be the pnode number, thus allowing nasids to be non-consecutive. It also provides a table in local memory for each cpu to translate target cpu number to target pnode and nasid. And it improves naming to properly reflect 'node' and 'uvhub' versus 'nasid'. Signed-off-by: Cliff Wickman Link: http://lkml.kernel.org/r/E1QJmxX-0002Mz-Fk@eag09.americas.sgi.com Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/uv/uv_bau.h | 17 ++++-- arch/x86/platform/uv/tlb_uv.c | 92 ++++++++++++++++++++++---------- 2 files changed, 76 insertions(+), 33 deletions(-) diff --git a/arch/x86/include/asm/uv/uv_bau.h b/arch/x86/include/asm/uv/uv_bau.h index 3e094af443c39..130f1eeee5fed 100644 --- a/arch/x86/include/asm/uv/uv_bau.h +++ b/arch/x86/include/asm/uv/uv_bau.h @@ -94,6 +94,8 @@ /* after this # consecutive successes, bump up the throttle if it was lowered */ #define COMPLETE_THRESHOLD 5 +#define UV_LB_SUBNODEID 0x10 + /* * number of entries in the destination side payload queue */ @@ -124,7 +126,7 @@ * The distribution specification (32 bytes) is interpreted as a 256-bit * distribution vector. Adjacent bits correspond to consecutive even numbered * nodeIDs. The result of adding the index of a given bit to the 15-bit - * 'base_dest_nodeid' field of the header corresponds to the + * 'base_dest_nasid' field of the header corresponds to the * destination nodeID associated with that specified bit. */ struct bau_target_uvhubmask { @@ -176,7 +178,7 @@ struct bau_msg_payload { struct bau_msg_header { unsigned int dest_subnodeid:6; /* must be 0x10, for the LB */ /* bits 5:0 */ - unsigned int base_dest_nodeid:15; /* nasid of the */ + unsigned int base_dest_nasid:15; /* nasid of the */ /* bits 20:6 */ /* first bit in uvhub map */ unsigned int command:8; /* message type */ /* bits 28:21 */ @@ -378,6 +380,10 @@ struct ptc_stats { unsigned long d_rcanceled; /* number of messages canceled by resets */ }; +struct hub_and_pnode { + short uvhub; + short pnode; +}; /* * one per-cpu; to locate the software tables */ @@ -399,10 +405,12 @@ struct bau_control { int baudisabled; int set_bau_off; short cpu; + short osnode; short uvhub_cpu; short uvhub; short cpus_in_socket; short cpus_in_uvhub; + short partition_base_pnode; unsigned short message_number; unsigned short uvhub_quiesce; short socket_acknowledge_count[DEST_Q_SIZE]; @@ -422,15 +430,16 @@ struct bau_control { int congested_period; cycles_t period_time; long period_requests; + struct hub_and_pnode *target_hub_and_pnode; }; static inline int bau_uvhub_isset(int uvhub, struct bau_target_uvhubmask *dstp) { return constant_test_bit(uvhub, &dstp->bits[0]); } -static inline void bau_uvhub_set(int uvhub, struct bau_target_uvhubmask *dstp) +static inline void bau_uvhub_set(int pnode, struct bau_target_uvhubmask *dstp) { - __set_bit(uvhub, &dstp->bits[0]); + __set_bit(pnode, &dstp->bits[0]); } static inline void bau_uvhubs_clear(struct bau_target_uvhubmask *dstp, int nbits) diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index a7b38d35c29a1..3796f99d7bf0c 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -698,16 +698,17 @@ const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask, struct mm_struct *mm, unsigned long va, unsigned int cpu) { - int tcpu; - int uvhub; int locals = 0; int remotes = 0; int hubs = 0; + int tcpu; + int tpnode; struct bau_desc *bau_desc; struct cpumask *flush_mask; struct ptc_stats *stat; struct bau_control *bcp; struct bau_control *tbcp; + struct hub_and_pnode *hpp; /* kernel was booted 'nobau' */ if (nobau) @@ -749,11 +750,18 @@ const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask, bau_desc += UV_ITEMS_PER_DESCRIPTOR * bcp->uvhub_cpu; bau_uvhubs_clear(&bau_desc->distribution, UV_DISTRIBUTION_SIZE); - /* cpu statistics */ for_each_cpu(tcpu, flush_mask) { - uvhub = uv_cpu_to_blade_id(tcpu); - bau_uvhub_set(uvhub, &bau_desc->distribution); - if (uvhub == bcp->uvhub) + /* + * The distribution vector is a bit map of pnodes, relative + * to the partition base pnode (and the partition base nasid + * in the header). + * Translate cpu to pnode and hub using an array stored + * in local memory. + */ + hpp = &bcp->socket_master->target_hub_and_pnode[tcpu]; + tpnode = hpp->pnode - bcp->partition_base_pnode; + bau_uvhub_set(tpnode, &bau_desc->distribution); + if (hpp->uvhub == bcp->uvhub) locals++; else remotes++; @@ -854,7 +862,7 @@ void uv_bau_message_interrupt(struct pt_regs *regs) * an interrupt, but causes an error message to be returned to * the sender. */ -static void uv_enable_timeouts(void) +static void __init uv_enable_timeouts(void) { int uvhub; int nuvhubs; @@ -1325,10 +1333,10 @@ static int __init uv_ptc_init(void) } /* - * initialize the sending side's sending buffers + * Initialize the sending side's sending buffers. */ static void -uv_activation_descriptor_init(int node, int pnode) +uv_activation_descriptor_init(int node, int pnode, int base_pnode) { int i; int cpu; @@ -1351,11 +1359,11 @@ uv_activation_descriptor_init(int node, int pnode) n = pa >> uv_nshift; m = pa & uv_mmask; + /* the 14-bit pnode */ uv_write_global_mmr64(pnode, UVH_LB_BAU_SB_DESCRIPTOR_BASE, (n << UV_DESC_BASE_PNODE_SHIFT | m)); - /* - * initializing all 8 (UV_ITEMS_PER_DESCRIPTOR) descriptors for each + * Initializing all 8 (UV_ITEMS_PER_DESCRIPTOR) descriptors for each * cpu even though we only use the first one; one descriptor can * describe a broadcast to 256 uv hubs. */ @@ -1364,12 +1372,13 @@ uv_activation_descriptor_init(int node, int pnode) memset(bd2, 0, sizeof(struct bau_desc)); bd2->header.sw_ack_flag = 1; /* - * base_dest_nodeid is the nasid of the first uvhub - * in the partition. The bit map will indicate uvhub numbers, - * which are 0-N in a partition. Pnodes are unique system-wide. + * The base_dest_nasid set in the message header is the nasid + * of the first uvhub in the partition. The bit map will + * indicate destination pnode numbers relative to that base. + * They may not be consecutive if nasid striding is being used. */ - bd2->header.base_dest_nodeid = UV_PNODE_TO_NASID(uv_partition_base_pnode); - bd2->header.dest_subnodeid = 0x10; /* the LB */ + bd2->header.base_dest_nasid = UV_PNODE_TO_NASID(base_pnode); + bd2->header.dest_subnodeid = UV_LB_SUBNODEID; bd2->header.command = UV_NET_ENDPOINT_INTD; bd2->header.int_both = 1; /* @@ -1441,7 +1450,7 @@ uv_payload_queue_init(int node, int pnode) /* * Initialization of each UV hub's structures */ -static void __init uv_init_uvhub(int uvhub, int vector) +static void __init uv_init_uvhub(int uvhub, int vector, int base_pnode) { int node; int pnode; @@ -1449,11 +1458,11 @@ static void __init uv_init_uvhub(int uvhub, int vector) node = uvhub_to_first_node(uvhub); pnode = uv_blade_to_pnode(uvhub); - uv_activation_descriptor_init(node, pnode); + uv_activation_descriptor_init(node, pnode, base_pnode); uv_payload_queue_init(node, pnode); /* - * the below initialization can't be in firmware because the - * messaging IRQ will be determined by the OS + * The below initialization can't be in firmware because the + * messaging IRQ will be determined by the OS. */ apicid = uvhub_to_first_apicid(uvhub) | uv_apicid_hibits; uv_write_global_mmr64(pnode, UVH_BAU_DATA_CONFIG, @@ -1490,10 +1499,11 @@ calculate_destination_timeout(void) /* * initialize the bau_control structure for each cpu */ -static int __init uv_init_per_cpu(int nuvhubs) +static int __init uv_init_per_cpu(int nuvhubs, int base_part_pnode) { int i; int cpu; + int tcpu; int pnode; int uvhub; int have_hmaster; @@ -1527,6 +1537,15 @@ static int __init uv_init_per_cpu(int nuvhubs) bcp = &per_cpu(bau_control, cpu); memset(bcp, 0, sizeof(struct bau_control)); pnode = uv_cpu_hub_info(cpu)->pnode; + if ((pnode - base_part_pnode) >= UV_DISTRIBUTION_SIZE) { + printk(KERN_EMERG + "cpu %d pnode %d-%d beyond %d; BAU disabled\n", + cpu, pnode, base_part_pnode, + UV_DISTRIBUTION_SIZE); + return 1; + } + bcp->osnode = cpu_to_node(cpu); + bcp->partition_base_pnode = uv_partition_base_pnode; uvhub = uv_cpu_hub_info(cpu)->numa_blade_id; *(uvhub_mask + (uvhub/8)) |= (1 << (uvhub%8)); bdp = &uvhub_descs[uvhub]; @@ -1535,7 +1554,7 @@ static int __init uv_init_per_cpu(int nuvhubs) bdp->pnode = pnode; /* kludge: 'assuming' one node per socket, and assuming that disabling a socket just leaves a gap in node numbers */ - socket = (cpu_to_node(cpu) & 1); + socket = bcp->osnode & 1; bdp->socket_mask |= (1 << socket); sdp = &bdp->socket[socket]; sdp->cpu_number[sdp->num_cpus] = cpu; @@ -1584,6 +1603,20 @@ static int __init uv_init_per_cpu(int nuvhubs) nextsocket: socket++; socket_mask = (socket_mask >> 1); + /* each socket gets a local array of pnodes/hubs */ + bcp = smaster; + bcp->target_hub_and_pnode = kmalloc_node( + sizeof(struct hub_and_pnode) * + num_possible_cpus(), GFP_KERNEL, bcp->osnode); + memset(bcp->target_hub_and_pnode, 0, + sizeof(struct hub_and_pnode) * + num_possible_cpus()); + for_each_present_cpu(tcpu) { + bcp->target_hub_and_pnode[tcpu].pnode = + uv_cpu_hub_info(tcpu)->pnode; + bcp->target_hub_and_pnode[tcpu].uvhub = + uv_cpu_hub_info(tcpu)->numa_blade_id; + } } } kfree(uvhub_descs); @@ -1636,21 +1669,22 @@ static int __init uv_bau_init(void) spin_lock_init(&disable_lock); congested_cycles = microsec_2_cycles(congested_response_us); - if (uv_init_per_cpu(nuvhubs)) { - nobau = 1; - return 0; - } - uv_partition_base_pnode = 0x7fffffff; - for (uvhub = 0; uvhub < nuvhubs; uvhub++) + for (uvhub = 0; uvhub < nuvhubs; uvhub++) { if (uv_blade_nr_possible_cpus(uvhub) && (uv_blade_to_pnode(uvhub) < uv_partition_base_pnode)) uv_partition_base_pnode = uv_blade_to_pnode(uvhub); + } + + if (uv_init_per_cpu(nuvhubs, uv_partition_base_pnode)) { + nobau = 1; + return 0; + } vector = UV_BAU_MESSAGE; for_each_possible_blade(uvhub) if (uv_blade_nr_possible_cpus(uvhub)) - uv_init_uvhub(uvhub, vector); + uv_init_uvhub(uvhub, vector, uv_partition_base_pnode); uv_enable_timeouts(); alloc_intr_gate(vector, uv_bau_message_intr1); From ec03ea41ad7c4a453486eefd71c7a7fda43d52f9 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 13 May 2011 15:52:09 +0200 Subject: [PATCH 1782/2556] x86, mce, AMD: Fix leaving freed data in a list commit d9a5ac9ef306eb5cc874f285185a15c303c50009 upstream. b may be added to a list, but is not removed before being freed in the case of an error. This is done in the corresponding deallocation function, so the code here has been changed to follow that. The sematic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression E,E1,E2; identifier l; @@ *list_add(&E->l,E1); ... when != E1 when != list_del(&E->l) when != list_del_init(&E->l) when != E = E2 *kfree(E);// Signed-off-by: Julia Lawall Cc: Borislav Petkov Cc: Robert Richter Cc: Yinghai Lu Cc: Andreas Herrmann Link: http://lkml.kernel.org/r/1305294731-12127-1-git-send-email-julia@diku.dk Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/mcheck/mce_amd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c index 5bf2fac52aca7..ca46a3a595599 100644 --- a/arch/x86/kernel/cpu/mcheck/mce_amd.c +++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c @@ -509,6 +509,7 @@ static __cpuinit int allocate_threshold_blocks(unsigned int cpu, out_free: if (b) { kobject_put(&b->kobj); + list_del(&b->miscj); kfree(b); } return err; From b106c56573f9656e3cf863781dd5c0895d0f120d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Wed, 19 Jan 2011 10:01:14 +0100 Subject: [PATCH 1783/2556] megaraid_sas: Sanity check user supplied length before passing it to dma_alloc_coherent() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 98cb7e4413d189cd2b54daf993a4667d9788c0bb upstream. The ioc->sgl[i].iov_len value is supplied by the ioctl caller, and can be zero in some cases. Assume that's valid and continue without error. Fixes (multiple individual reports of the same problem for quite a while): http://marc.info/?l=linux-ide&m=128941801715301 http://bugs.debian.org/604627 http://www.mail-archive.com/linux-poweredge@dell.com/msg02575.html megasas: Failed to alloc kernel SGL buffer for IOCTL and [ 69.162538] ------------[ cut here ]------------ [ 69.162806] kernel BUG at /build/buildd/linux-2.6.32/lib/swiotlb.c:368! [ 69.163134] invalid opcode: 0000 [#1] SMP [ 69.163570] last sysfs file: /sys/devices/system/cpu/cpu3/cache/index2/shared_cpu_map [ 69.163975] CPU 0 [ 69.164227] Modules linked in: fbcon tileblit font bitblit softcursor vga16fb vgastate ioatdma radeon ttm drm_kms_helper shpchp drm i2c_algo_bit lp parport floppy pata_jmicron megaraid_sas igb dca [ 69.167419] Pid: 1206, comm: smartctl Tainted: G W 2.6.32-25-server #45-Ubuntu X8DTN [ 69.167843] RIP: 0010:[] [] map_single+0x255/0x260 [ 69.168370] RSP: 0018:ffff88081c0ebc58 EFLAGS: 00010246 [ 69.168655] RAX: 000000000003bffc RBX: 00000000ffffffff RCX: 0000000000000002 [ 69.169000] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff88001dffe000 [ 69.169346] RBP: ffff88081c0ebcb8 R08: 0000000000000000 R09: ffff880000030840 [ 69.169691] R10: 0000000000100000 R11: 0000000000000000 R12: 0000000000000000 [ 69.170036] R13: 00000000ffffffff R14: 0000000000000001 R15: 0000000000200000 [ 69.170382] FS: 00007fb8de189720(0000) GS:ffff88001de00000(0000) knlGS:0000000000000000 [ 69.170794] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 69.171094] CR2: 00007fb8dd59237c CR3: 000000081a790000 CR4: 00000000000006f0 [ 69.171439] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 69.171784] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 69.172130] Process smartctl (pid: 1206, threadinfo ffff88081c0ea000, task ffff88081a760000) [ 69.194513] Stack: [ 69.205788] 0000000000000034 00000002817e3390 0000000000000000 ffff88081c0ebe00 [ 69.217739] <0> 0000000000000000 000000000003bffc 0000000000000000 0000000000000000 [ 69.241250] <0> 0000000000000000 00000000ffffffff ffff88081c5b4080 ffff88081c0ebe00 [ 69.277310] Call Trace: [ 69.289278] [] swiotlb_alloc_coherent+0xec/0x130 [ 69.301118] [] x86_swiotlb_alloc_coherent+0x61/0x70 [ 69.313045] [] megasas_mgmt_fw_ioctl+0x1ae/0x690 [megaraid_sas] [ 69.336399] [] megasas_mgmt_ioctl_fw+0x198/0x240 [megaraid_sas] [ 69.359346] [] megasas_mgmt_ioctl+0x35/0x50 [megaraid_sas] [ 69.370902] [] vfs_ioctl+0x22/0xa0 [ 69.382322] [] ? alloc_fd+0x10a/0x150 [ 69.393622] [] do_vfs_ioctl+0x81/0x410 [ 69.404696] [] ? do_page_fault+0x153/0x3b0 [ 69.415761] [] sys_ioctl+0x81/0xa0 [ 69.426640] [] system_call_fastpath+0x16/0x1b [ 69.437491] Code: fe ff ff 48 8b 3d 74 38 76 00 41 bf 00 00 20 00 e8 51 f5 d7 ff 83 e0 ff 48 05 ff 07 00 00 48 c1 e8 0b 48 89 45 c8 e9 13 fe ff ff <0f> 0b eb fe 0f 1f 80 00 00 00 00 55 48 89 e5 48 83 ec 20 4c 89 [ 69.478216] RIP [] map_single+0x255/0x260 [ 69.489668] RSP [ 69.500975] ---[ end trace 6a2181b634e2abc7 ]--- Reported-by: Bokhan Artem Reported by: Marc-Christian Petersen Signed-off-by: Bjørn Mork Cc: Michael Benz Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/megaraid/megaraid_sas_base.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 5d6d07bd1cd05..cee1d3bd68dcd 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -4611,6 +4611,9 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, * For each user buffer, create a mirror buffer and copy in */ for (i = 0; i < ioc->sge_count; i++) { + if (!ioc->sgl[i].iov_len) + continue; + kbuff_arr[i] = dma_alloc_coherent(&instance->pdev->dev, ioc->sgl[i].iov_len, &buf_handle, GFP_KERNEL); From 9f5cc5c89537184cf45a7e069330c85428a7fc09 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 29 Apr 2011 10:15:14 +0200 Subject: [PATCH 1784/2556] cdrom: always check_disk_change() on open commit bf2253a6f00e8fea5b026e471e9f0d0a1b3621f2 upstream. cdrom_open() called check_disk_change() after the rest of open path succeeded which leads to the following bizarre behavior. * After media change, if the device opened without O_NONBLOCK, open_for_data() naturally fails with -ENOMEDIA and check_disk_change() is never called. The media is known to be gone and the open failure makes it obvious to the userland but device invalidation never happens. * But if the device is opened with O_NONBLOCK, all the checks are bypassed and cdrom_open() doesn't notice that the media is not there and check_disk_change() is called and invalidation happens. There's nothing to be gained by avoiding calling check_disk_change() on open failure. Common cases end up calling check_disk_change() anyway. All we get is inconsistent behavior. Fix it by moving check_disk_change() invocation to the top of cdrom_open() so that it always gets called regardless of how the rest of open proceeds. Stable: 2.6.38 Signed-off-by: Tejun Heo Reported-by: Amit Shah Tested-by: Amit Shah Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/cdrom/cdrom.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index e2c48a7eccffe..5ade78a01c4dc 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -986,6 +986,9 @@ int cdrom_open(struct cdrom_device_info *cdi, struct block_device *bdev, fmode_t cdinfo(CD_OPEN, "entering cdrom_open\n"); + /* open is event synchronization point, check events first */ + check_disk_change(bdev); + /* if this was a O_NONBLOCK open and we should honor the flags, * do a quick open without drive/disc integrity checks. */ cdi->use_count++; @@ -1012,9 +1015,6 @@ int cdrom_open(struct cdrom_device_info *cdi, struct block_device *bdev, fmode_t cdinfo(CD_OPEN, "Use count for \"/dev/%s\" now %d\n", cdi->name, cdi->use_count); - /* Do this on open. Don't wait for mount, because they might - not be mounting, but opening with O_NONBLOCK */ - check_disk_change(bdev); return 0; err_release: if (CDROM_CAN(CDC_LOCK) && cdi->options & CDO_LOCK) { From 67de934b0b0c242cb95ce97c78fc6e0ca4e904f7 Mon Sep 17 00:00:00 2001 From: Thomas Jarosch Date: Mon, 16 May 2011 06:28:15 +0000 Subject: [PATCH 1785/2556] vmxnet3: Fix inconsistent LRO state after initialization commit ebde6f8acba92abfc203585198a54f47e83e2cd0 upstream. During initialization of vmxnet3, the state of LRO gets out of sync with netdev->features. This leads to very poor TCP performance in a IP forwarding setup and is hitting many VMware users. Simplified call sequence: 1. vmxnet3_declare_features() initializes "adapter->lro" to true. 2. The kernel automatically disables LRO if IP forwarding is enabled, so vmxnet3_set_flags() gets called. This also updates netdev->features. 3. Now vmxnet3_setup_driver_shared() is called. "adapter->lro" is still set to true and LRO gets enabled again, even though netdev->features shows it's disabled. Fix it by updating "adapter->lro", too. The private vmxnet3 adapter flags are scheduled for removal in net-next, see commit a0d2730c9571aeba793cb5d3009094ee1d8fda35 "net: vmxnet3: convert to hw_features". Patch applies to 2.6.37 / 2.6.38 and 2.6.39-rc6. Please CC: comments. Signed-off-by: Thomas Jarosch Acked-by: Stephen Hemminger Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/vmxnet3/vmxnet3_ethtool.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c index 51f2ef142a5b6..976467253d200 100644 --- a/drivers/net/vmxnet3/vmxnet3_ethtool.c +++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c @@ -311,6 +311,9 @@ vmxnet3_set_flags(struct net_device *netdev, u32 data) /* toggle the LRO feature*/ netdev->features ^= NETIF_F_LRO; + /* Update private LRO flag */ + adapter->lro = lro_requested; + /* update harware LRO capability accordingly */ if (lro_requested) adapter->shared->devRead.misc.uptFeatures |= From 3ea80459be84832670d6888ca06b3aaffe32e6a4 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 23 Mar 2011 09:58:28 -0500 Subject: [PATCH 1786/2556] Revert "[SCSI] Retrieve the Caching mode page" commit 3dea642afd9187728d119fce5c82a7ed9faa9b6a upstream. This reverts commit 24d720b726c1a85f1962831ac30ad4d2ef8276b1. Previously we thought there was little possibility that devices would crash with this, but some have been found. Reported-by: Alan Stern Cc: Luben Tuikov Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/sd.c | 63 ++++++++++++----------------------------------- 1 file changed, 16 insertions(+), 47 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index e56730214c05e..a63b94c0fba62 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1910,14 +1910,10 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) int old_rcd = sdkp->RCD; int old_dpofua = sdkp->DPOFUA; - if (sdp->skip_ms_page_8) { - if (sdp->type == TYPE_RBC) - goto defaults; - else { - modepage = 0x3F; - dbd = 0; - } - } else if (sdp->type == TYPE_RBC) { + if (sdp->skip_ms_page_8) + goto defaults; + + if (sdp->type == TYPE_RBC) { modepage = 6; dbd = 8; } else { @@ -1945,11 +1941,13 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) */ if (len < 3) goto bad_sense; - else if (len > SD_BUF_SIZE) { - sd_printk(KERN_NOTICE, sdkp, "Truncating mode parameter " - "data from %d to %d bytes\n", len, SD_BUF_SIZE); - len = SD_BUF_SIZE; - } + if (len > 20) + len = 20; + + /* Take headers and block descriptors into account */ + len += data.header_length + data.block_descriptor_length; + if (len > SD_BUF_SIZE) + goto bad_sense; /* Get the data */ res = sd_do_mode_sense(sdp, dbd, modepage, buffer, len, &data, &sshdr); @@ -1957,45 +1955,16 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) if (scsi_status_is_good(res)) { int offset = data.header_length + data.block_descriptor_length; - while (offset < len) { - u8 page_code = buffer[offset] & 0x3F; - u8 spf = buffer[offset] & 0x40; - - if (page_code == 8 || page_code == 6) { - /* We're interested only in the first 3 bytes. - */ - if (len - offset <= 2) { - sd_printk(KERN_ERR, sdkp, "Incomplete " - "mode parameter data\n"); - goto defaults; - } else { - modepage = page_code; - goto Page_found; - } - } else { - /* Go to the next page */ - if (spf && len - offset > 3) - offset += 4 + (buffer[offset+2] << 8) + - buffer[offset+3]; - else if (!spf && len - offset > 1) - offset += 2 + buffer[offset+1]; - else { - sd_printk(KERN_ERR, sdkp, "Incomplete " - "mode parameter data\n"); - goto defaults; - } - } + if (offset >= SD_BUF_SIZE - 2) { + sd_printk(KERN_ERR, sdkp, "Malformed MODE SENSE response\n"); + goto defaults; } - if (modepage == 0x3F) { - sd_printk(KERN_ERR, sdkp, "No Caching mode page " - "present\n"); - goto defaults; - } else if ((buffer[offset] & 0x3f) != modepage) { + if ((buffer[offset] & 0x3f) != modepage) { sd_printk(KERN_ERR, sdkp, "Got wrong page\n"); goto defaults; } - Page_found: + if (modepage == 8) { sdkp->WCE = ((buffer[offset + 2] & 0x04) != 0); sdkp->RCD = ((buffer[offset + 2] & 0x01) != 0); From 8f18c3721794569b3ac31f30f5e3f0ac983033a5 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 5 Apr 2011 15:02:37 -0400 Subject: [PATCH 1787/2556] cifs: clean up various nits in unicode routines (try #2) commit 581ade4d1c025eb10421eda0d0c0a2f04447d7c5 upstream. Minor revision to the original patch. Don't abuse the __le16 variable on the stack by casting it to wchar_t and handing it off to char2uni. Declare an actual wchar_t on the stack instead. This fixes a valid sparse warning. Fix the spelling of UNI_ASTERISK. Eliminate the unneeded len_remaining variable in cifsConvertToUCS. Also, as David Howells points out. We were better off making cifsConvertToUCS *not* use put_unaligned_le16 since it means that we can't optimize the mapped characters at compile time. Switch them instead to use cpu_to_le16, and simply use put_unaligned to set them in the string. Reported-and-acked-by: David Howells Signed-off-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/cifs_unicode.c | 35 +++++++++++++++++------------------ fs/cifs/cifs_unicode.h | 2 +- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index fc0fd4fde306b..23d43cde43060 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -90,7 +90,7 @@ cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, case UNI_COLON: *target = ':'; break; - case UNI_ASTERIK: + case UNI_ASTERISK: *target = '*'; break; case UNI_QUESTION: @@ -264,40 +264,40 @@ cifs_strndup_from_ucs(const char *src, const int maxlen, const bool is_unicode, * names are little endian 16 bit Unicode on the wire */ int -cifsConvertToUCS(__le16 *target, const char *source, int maxlen, +cifsConvertToUCS(__le16 *target, const char *source, int srclen, const struct nls_table *cp, int mapChars) { int i, j, charlen; - int len_remaining = maxlen; char src_char; - __u16 temp; + __le16 dst_char; + wchar_t tmp; if (!mapChars) return cifs_strtoUCS(target, source, PATH_MAX, cp); - for (i = 0, j = 0; i < maxlen; j++) { + for (i = 0, j = 0; i < srclen; j++) { src_char = source[i]; switch (src_char) { case 0: - put_unaligned_le16(0, &target[j]); + put_unaligned(0, &target[j]); goto ctoUCS_out; case ':': - temp = UNI_COLON; + dst_char = cpu_to_le16(UNI_COLON); break; case '*': - temp = UNI_ASTERIK; + dst_char = cpu_to_le16(UNI_ASTERISK); break; case '?': - temp = UNI_QUESTION; + dst_char = cpu_to_le16(UNI_QUESTION); break; case '<': - temp = UNI_LESSTHAN; + dst_char = cpu_to_le16(UNI_LESSTHAN); break; case '>': - temp = UNI_GRTRTHAN; + dst_char = cpu_to_le16(UNI_GRTRTHAN); break; case '|': - temp = UNI_PIPE; + dst_char = cpu_to_le16(UNI_PIPE); break; /* * FIXME: We can not handle remapping backslash (UNI_SLASH) @@ -305,17 +305,17 @@ cifsConvertToUCS(__le16 *target, const char *source, int maxlen, * as they use backslash as separator. */ default: - charlen = cp->char2uni(source+i, len_remaining, - &temp); + charlen = cp->char2uni(source + i, srclen - i, &tmp); + dst_char = cpu_to_le16(tmp); + /* * if no match, use question mark, which at least in * some cases serves as wild card */ if (charlen < 1) { - temp = 0x003f; + dst_char = cpu_to_le16(0x003f); charlen = 1; } - len_remaining -= charlen; /* * character may take more than one byte in the source * string, but will take exactly two bytes in the @@ -324,9 +324,8 @@ cifsConvertToUCS(__le16 *target, const char *source, int maxlen, i += charlen; continue; } - put_unaligned_le16(temp, &target[j]); + put_unaligned(dst_char, &target[j]); i++; /* move to next char in source string */ - len_remaining--; } ctoUCS_out: diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h index 7fe6b52df5076..644dd882a5604 100644 --- a/fs/cifs/cifs_unicode.h +++ b/fs/cifs/cifs_unicode.h @@ -44,7 +44,7 @@ * reserved symbols (along with \ and /), otherwise illegal to store * in filenames in NTFS */ -#define UNI_ASTERIK (__u16) ('*' + 0xF000) +#define UNI_ASTERISK (__u16) ('*' + 0xF000) #define UNI_QUESTION (__u16) ('?' + 0xF000) #define UNI_COLON (__u16) (':' + 0xF000) #define UNI_GRTRTHAN (__u16) ('>' + 0xF000) From abe43277b747dc3820678a5a919ca758013017f3 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 17 May 2011 15:28:21 -0400 Subject: [PATCH 1788/2556] cifs: fix cifsConvertToUCS() for the mapchars case commit 11379b5e33950048ad66825da7f462b0d0da9d73 upstream. As Metze pointed out, commit 84cdf74e broke mapchars option: Commit "cifs: fix unaligned accesses in cifsConvertToUCS" (84cdf74e8096a10dd6acbb870dd404b92f07a756) does multiple steps in just one commit (moving the function and changing it without testing). put_unaligned_le16(temp, &target[j]); is never called for any codepoint the goes via the 'default' switch statement. As a result we put just zero (or maybe uninitialized) bytes into the target buffer. His proposed patch looks correct, but doesn't apply to the current head of the tree. This patch should also fix it. Reported-by: Stefan Metzmacher Signed-off-by: Jeff Layton Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/cifs_unicode.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index 23d43cde43060..1b2e180b018dd 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -277,6 +277,7 @@ cifsConvertToUCS(__le16 *target, const char *source, int srclen, for (i = 0, j = 0; i < srclen; j++) { src_char = source[i]; + charlen = 1; switch (src_char) { case 0: put_unaligned(0, &target[j]); @@ -316,16 +317,13 @@ cifsConvertToUCS(__le16 *target, const char *source, int srclen, dst_char = cpu_to_le16(0x003f); charlen = 1; } - /* - * character may take more than one byte in the source - * string, but will take exactly two bytes in the - * target string - */ - i += charlen; - continue; } + /* + * character may take more than one byte in the source string, + * but will take exactly two bytes in the target string + */ + i += charlen; put_unaligned(dst_char, &target[j]); - i++; /* move to next char in source string */ } ctoUCS_out: From 23181925de608dea571fb5aba4a34900df54b973 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 7 May 2011 17:46:21 +0200 Subject: [PATCH 1789/2556] iwlegacy: fix IBSS mode crashes commit eb85de3f84868ca85703a23617b4079ce79a801e upstream. We should not switch to non-IBSS channels when working in IBSS mode, otherwise there are microcode errors, and after some time system crashes. This bug is only observable when software scan is used in IBSS mode, so should be considered as regression after: commit 0263aa45293838b514b8af674a03faf040991a90 Author: Stanislaw Gruszka Date: Tue Mar 29 11:24:21 2011 +0200 iwl3945: disable hw scan by default However IBSS mode check, which this patch add again, was removed by commit b2f30e8bdd8ef5f3b5a7ef9146509585a15347d3 Author: Johannes Berg Date: Thu Jan 21 07:32:20 2010 -0800 iwlwifi: remove IBSS channel sanity check That commit claim that mac80211 will not use non-IBSS channel in IBSS mode, what definitely is not true. Bug probably should be fixed in mac80211, but that will require more work, so better to apply that patch temporally, and provide proper mac80211 fix latter. Resolves: https://bugzilla.kernel.org/show_bug.cgi?id=34452 Reported-and-tested-by: Mikko Rapeli Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl-legacy.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/iwl-legacy.c b/drivers/net/wireless/iwlwifi/iwl-legacy.c index bb1a742a98a0e..7f4905bc16316 100644 --- a/drivers/net/wireless/iwlwifi/iwl-legacy.c +++ b/drivers/net/wireless/iwlwifi/iwl-legacy.c @@ -123,6 +123,13 @@ int iwl_legacy_mac_config(struct ieee80211_hw *hw, u32 changed) goto set_ch_out; } + if (priv->iw_mode == NL80211_IFTYPE_ADHOC && + !is_channel_ibss(ch_info)) { + IWL_DEBUG_MAC80211(priv, "leave - not IBSS channel\n"); + ret = -EINVAL; + goto set_ch_out; + } + spin_lock_irqsave(&priv->lock, flags); for_each_context(priv, ctx) { From 10a22931499f4f1f579fb37b1ab0562f3ccfbe0d Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Fri, 20 May 2011 15:47:33 -0700 Subject: [PATCH 1790/2556] tmpfs: fix highmem swapoff crash regression commit e6c9366b2adb52cba64b359b3050200743c7568c upstream. Commit 778dd893ae78 ("tmpfs: fix race between umount and swapoff") forgot the new rules for strict atomic kmap nesting, causing WARNING: at arch/x86/mm/highmem_32.c:81 from __kunmap_atomic(), then BUG: unable to handle kernel paging request at fffb9000 from shmem_swp_set() when shmem_unuse_inode() is handling swapoff with highmem in use. My disgrace again. See https://bugzilla.kernel.org/show_bug.cgi?id=35352 Reported-by: Witold Baryluk Signed-off-by: Hugh Dickins Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/shmem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mm/shmem.c b/mm/shmem.c index 3ca835061291f..21bdaf8fd21c6 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -917,11 +917,12 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, s if (size > ENTRIES_PER_PAGE) size = ENTRIES_PER_PAGE; offset = shmem_find_swp(entry, ptr, ptr+size); + shmem_swp_unmap(ptr); if (offset >= 0) { shmem_dir_unmap(dir); + ptr = shmem_swp_map(subdir); goto found; } - shmem_swp_unmap(ptr); } } lost1: From 1c6dfdb912ee90ce3d20db20fec032e06a4315dd Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 21 May 2011 15:13:59 -0700 Subject: [PATCH 1791/2556] Linux 2.6.38.7 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c74c308507ef7..785cac8897907 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 38 -EXTRAVERSION = .6 +EXTRAVERSION = .7 NAME = Flesh-Eating Bats with Fangs # *DOCUMENTATION* From 3e768cc47e031b7b28cd0f70257d403aedc8d2c5 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Wed, 27 Apr 2011 16:44:26 +0100 Subject: [PATCH 1792/2556] kmemleak: Do not return a pointer to an object that kmemleak did not get commit 52c3ce4ec5601ee383a14f1485f6bac7b278896e upstream. The kmemleak_seq_next() function tries to get an object (and increment its use count) before returning it. If it could not get the last object during list traversal (because it may have been freed), the function should return NULL rather than a pointer to such object that it did not get. Signed-off-by: Catalin Marinas Reported-by: Phil Carmody Acked-by: Phil Carmody Signed-off-by: Greg Kroah-Hartman --- mm/kmemleak.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mm/kmemleak.c b/mm/kmemleak.c index 84225f3b71905..a351b680acbd3 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -1414,9 +1414,12 @@ static void *kmemleak_seq_next(struct seq_file *seq, void *v, loff_t *pos) ++(*pos); list_for_each_continue_rcu(n, &object_list) { - next_obj = list_entry(n, struct kmemleak_object, object_list); - if (get_object(next_obj)) + struct kmemleak_object *obj = + list_entry(n, struct kmemleak_object, object_list); + if (get_object(obj)) { + next_obj = obj; break; + } } put_object(prev_obj); From 3f21d6a86ecd7a6cf28df5b3fba5b6d243dc3d54 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Thu, 19 May 2011 16:25:30 +0100 Subject: [PATCH 1793/2556] kmemleak: Initialise kmemleak after debug_objects_mem_init() commit 9b090f2da85bd0df5e1a1ecfe4120b7b50358f48 upstream. Kmemleak frees objects via RCU and when CONFIG_DEBUG_OBJECTS_RCU_HEAD is enabled, the RCU callback triggers a call to free_object() in lib/debugobjects.c. Since kmemleak is initialised before debug objects initialisation, it may result in a kernel panic during booting. This patch moves the kmemleak_init() call after debug_objects_mem_init(). Reported-by: Marcin Slusarz Tested-by: Tejun Heo Signed-off-by: Catalin Marinas Signed-off-by: Greg Kroah-Hartman --- init/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/init/main.c b/init/main.c index 33c37c379e964..a7fcde2913776 100644 --- a/init/main.c +++ b/init/main.c @@ -668,8 +668,8 @@ asmlinkage void __init start_kernel(void) #endif page_cgroup_init(); enable_debug_pagealloc(); - kmemleak_init(); debug_objects_mem_init(); + kmemleak_init(); setup_per_cpu_pageset(); numa_policy_init(); if (late_time_init) From d52cffae9cadf97c5551058a150ec39e2f5affe3 Mon Sep 17 00:00:00 2001 From: Naga Chumbalkar Date: Tue, 26 Apr 2011 17:05:18 +0000 Subject: [PATCH 1794/2556] Fix _OSC UUID in pcc-cpufreq commit 904cc1e637a00dba1b58e7752f485f90ebf2a568 upstream. UUID needs to be written out the way it is described in Sec 18.5.124 of ACPI 4.0a Specification. Platform firmware's use of this UUID/_OSC is optional, which is why we didn't notice this bug earlier. Signed-off-by: Naga Chumbalkar Signed-off-by: Dave Jones Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/cpufreq/pcc-cpufreq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/cpu/cpufreq/pcc-cpufreq.c b/arch/x86/kernel/cpu/cpufreq/pcc-cpufreq.c index 4a5a42b842adf..500a242853e7b 100644 --- a/arch/x86/kernel/cpu/cpufreq/pcc-cpufreq.c +++ b/arch/x86/kernel/cpu/cpufreq/pcc-cpufreq.c @@ -39,7 +39,7 @@ #include -#define PCC_VERSION "1.00.00" +#define PCC_VERSION "1.10.00" #define POLL_LOOPS 300 #define CMD_COMPLETE 0x1 @@ -102,7 +102,7 @@ static struct acpi_generic_address doorbell; static u64 doorbell_preserve; static u64 doorbell_write; -static u8 OSC_UUID[16] = {0x63, 0x9B, 0x2C, 0x9F, 0x70, 0x91, 0x49, 0x1f, +static u8 OSC_UUID[16] = {0x9F, 0x2C, 0x9B, 0x63, 0x91, 0x70, 0x1f, 0x49, 0xBB, 0x4F, 0xA5, 0x98, 0x2F, 0xA1, 0xB5, 0x46}; struct pcc_cpu { From 15ae4738537b75bb8f9ba737bcb18c8cb0cb1e07 Mon Sep 17 00:00:00 2001 From: Jacob Shin Date: Wed, 27 Apr 2011 13:32:11 -0500 Subject: [PATCH 1795/2556] CPU hotplug, re-create sysfs directory and symlinks commit 27ecddc2a9f99ce4ac9a59a0acd77f7100b6d034 upstream. When we discover CPUs that are affected by each other's frequency/voltage transitions, the first CPU gets a sysfs directory created, and rest of the siblings get symlinks. Currently, when we hotplug off only the first CPU, all of the symlinks and the sysfs directory gets removed. Even though rest of the siblings are still online and functional, they are orphaned, and no longer governed by cpufreq. This patch, given the above scenario, creates a sysfs directory for the first sibling and symlinks for the rest of the siblings. Please note the recursive call, it was rather too ugly to roll it out. And the removal of redundant NULL setting (it is already taken care of near the top of the function). Signed-off-by: Jacob Shin Acked-by: Mark Langsdorf Reviewed-by: Thomas Renninger Signed-off-by: Dave Jones Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/cpufreq.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 5cb4d09919d67..f06a3b9b5f167 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1207,12 +1207,28 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) cpufreq_driver->exit(data); unlock_policy_rwsem_write(cpu); + cpufreq_debug_enable_ratelimit(); + +#ifdef CONFIG_HOTPLUG_CPU + /* when the CPU which is the parent of the kobj is hotplugged + * offline, check for siblings, and create cpufreq sysfs interface + * and symlinks + */ + if (unlikely(cpumask_weight(data->cpus) > 1)) { + /* first sibling now owns the new sysfs dir */ + cpumask_clear_cpu(cpu, data->cpus); + cpufreq_add_dev(get_cpu_sysdev(cpumask_first(data->cpus))); + + /* finally remove our own symlink */ + lock_policy_rwsem_write(cpu); + __cpufreq_remove_dev(sys_dev); + } +#endif + free_cpumask_var(data->related_cpus); free_cpumask_var(data->cpus); kfree(data); - per_cpu(cpufreq_cpu_data, cpu) = NULL; - cpufreq_debug_enable_ratelimit(); return 0; } From a5ba67df5b332a1e9bd73f6954285a3284f86e71 Mon Sep 17 00:00:00 2001 From: steven finney Date: Mon, 2 May 2011 11:29:17 -0700 Subject: [PATCH 1796/2556] Fix memory leak in cpufreq_stat commit 98586ed8b8878e10691203687e89a42fa3355300 upstream. When a CPU is taken offline in an SMP system, cpufreq_remove_dev() nulls out the per-cpu policy before cpufreq_stats_free_table() can make use of it. cpufreq_stats_free_table() then skips the call to sysfs_remove_group(), leaving about 100 bytes of sysfs-related memory unclaimed each time a CPU-removal occurs. Break up cpu_stats_free_table into sysfs and table portions, and call the sysfs portion early. Signed-off-by: Steven Finney Signed-off-by: Dave Jones Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/cpufreq_stats.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 00d73fc8e4e28..4f1b8de2c9f39 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -165,17 +165,27 @@ static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq) return -1; } +/* should be called late in the CPU removal sequence so that the stats + * memory is still available in case someone tries to use it. + */ static void cpufreq_stats_free_table(unsigned int cpu) { struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, cpu); - struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); - if (policy && policy->cpu == cpu) - sysfs_remove_group(&policy->kobj, &stats_attr_group); if (stat) { kfree(stat->time_in_state); kfree(stat); } per_cpu(cpufreq_stats_table, cpu) = NULL; +} + +/* must be called early in the CPU removal sequence (before + * cpufreq_remove_dev) so that policy is still valid. + */ +static void cpufreq_stats_free_sysfs(unsigned int cpu) +{ + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); + if (policy && policy->cpu == cpu) + sysfs_remove_group(&policy->kobj, &stats_attr_group); if (policy) cpufreq_cpu_put(policy); } @@ -316,6 +326,9 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, case CPU_ONLINE_FROZEN: cpufreq_update_policy(cpu); break; + case CPU_DOWN_PREPARE: + cpufreq_stats_free_sysfs(cpu); + break; case CPU_DEAD: case CPU_DEAD_FROZEN: cpufreq_stats_free_table(cpu); @@ -324,9 +337,11 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, return NOTIFY_OK; } +/* priority=1 so this will get called before cpufreq_remove_dev */ static struct notifier_block cpufreq_stat_cpu_notifier __refdata = { .notifier_call = cpufreq_stat_cpu_callback, + .priority = 1, }; static struct notifier_block notifier_policy_block = { From ad8ba6a764a5bb3a1e6f878b489b7842081e33fc Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 17 May 2011 15:38:57 -0400 Subject: [PATCH 1797/2556] net: recvmmsg: Strip MSG_WAITFORONE when calling recvmsg commit b9eb8b8752804cecbacdb4d24b52e823cf07f107 upstream. recvmmsg fails on a raw socket with EINVAL. The reason for this is packet_recvmsg checks the incoming flags: err = -EINVAL; if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT|MSG_ERRQUEUE)) goto out; This patch strips out MSG_WAITFORONE when calling recvmmsg which fixes the issue. Signed-off-by: Anton Blanchard Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/socket.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/socket.c b/net/socket.c index 29c7df0ed3f8f..1204afdfac6ee 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2117,14 +2117,16 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, */ if (MSG_CMSG_COMPAT & flags) { err = __sys_recvmsg(sock, (struct msghdr __user *)compat_entry, - &msg_sys, flags, datagrams); + &msg_sys, flags & ~MSG_WAITFORONE, + datagrams); if (err < 0) break; err = __put_user(err, &compat_entry->msg_len); ++compat_entry; } else { err = __sys_recvmsg(sock, (struct msghdr __user *)entry, - &msg_sys, flags, datagrams); + &msg_sys, flags & ~MSG_WAITFORONE, + datagrams); if (err < 0) break; err = put_user(err, &entry->msg_len); From e5664c3da9affbcca581c9a9d4a7a5dae3ca8826 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 29 Apr 2011 22:35:33 -0400 Subject: [PATCH 1798/2556] ftrace: Only update the function code on write to filter files commit 058e297d34a404caaa5ed277de15698d8dc43000 upstream. If function tracing is enabled, a read of the filter files will cause the call to stop_machine to update the function trace sites. It should only call stop_machine on write. Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/ftrace.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 888b611897d37..9803b68dbc310 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -2414,14 +2414,16 @@ ftrace_regex_release(struct inode *inode, struct file *file, int enable) ftrace_match_records(parser->buffer, parser->idx, enable); } - mutex_lock(&ftrace_lock); - if (ftrace_start_up && ftrace_enabled) - ftrace_run_update_code(FTRACE_ENABLE_CALLS); - mutex_unlock(&ftrace_lock); - trace_parser_put(parser); kfree(iter); + if (file->f_mode & FMODE_WRITE) { + mutex_lock(&ftrace_lock); + if (ftrace_start_up && ftrace_enabled) + ftrace_run_update_code(FTRACE_ENABLE_CALLS); + mutex_unlock(&ftrace_lock); + } + mutex_unlock(&ftrace_regex_lock); return 0; } From 88d3fa3d42d6758cab35373536ec1fc9be94e597 Mon Sep 17 00:00:00 2001 From: Arun Easi Date: Tue, 10 May 2011 11:18:16 -0700 Subject: [PATCH 1799/2556] qla2xxx: Fix hang during driver unload when vport is active. commit 43ebf16d762b082663976b679b813e1b546548d1 upstream. Bumping ref count during fc_vport_terminate() was the cause. vport delete would wait for ref count to drop to zero and that would never happen. Signed-off-by: Arun Easi Signed-off-by: Madhuranath Iyengar Signed-off-by: James Bottomley Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/qla2xxx/qla_os.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index f27724d76cf66..3010dd7815972 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -2359,21 +2359,26 @@ qla2x00_remove_one(struct pci_dev *pdev) base_vha = pci_get_drvdata(pdev); ha = base_vha->hw; - spin_lock_irqsave(&ha->vport_slock, flags); - list_for_each_entry(vha, &ha->vp_list, list) { - atomic_inc(&vha->vref_count); + mutex_lock(&ha->vport_lock); + while (ha->cur_vport_count) { + struct Scsi_Host *scsi_host; - if (vha->fc_vport) { - spin_unlock_irqrestore(&ha->vport_slock, flags); + spin_lock_irqsave(&ha->vport_slock, flags); - fc_vport_terminate(vha->fc_vport); + BUG_ON(base_vha->list.next == &ha->vp_list); + /* This assumes first entry in ha->vp_list is always base vha */ + vha = list_first_entry(&base_vha->list, scsi_qla_host_t, list); + scsi_host = scsi_host_get(vha->host); - spin_lock_irqsave(&ha->vport_slock, flags); - } + spin_unlock_irqrestore(&ha->vport_slock, flags); + mutex_unlock(&ha->vport_lock); + + fc_vport_terminate(vha->fc_vport); + scsi_host_put(vha->host); - atomic_dec(&vha->vref_count); + mutex_lock(&ha->vport_lock); } - spin_unlock_irqrestore(&ha->vport_slock, flags); + mutex_unlock(&ha->vport_lock); set_bit(UNLOADING, &base_vha->dpc_flags); From 2ce89b71185b877d6ffd8d39aa0e088405719408 Mon Sep 17 00:00:00 2001 From: Saurav Kashyap Date: Tue, 10 May 2011 11:18:18 -0700 Subject: [PATCH 1800/2556] qla2xxx: Fix virtual port failing to login after chip reset. commit cefcaba67ab97fb756b3a6af5139c94d861b660d upstream. This patch ensures qla82xx_watchdog is not being run for the vport. It also makes sure that beacon ON is not done for the vport, as it will lead to the waking up of the dpc thread again and again. Signed-off-by: Saurav Kashyap Signed-off-by: Madhuranath Iyengar Signed-off-by: James Bottomley Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/qla2xxx/qla_os.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 3010dd7815972..8f823bd9f027b 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -3608,7 +3608,8 @@ qla2x00_timer(scsi_qla_host_t *vha) if (!pci_channel_offline(ha->pdev)) pci_read_config_word(ha->pdev, PCI_VENDOR_ID, &w); - if (IS_QLA82XX(ha)) { + /* Make sure qla82xx_watchdog is run only for physical port */ + if (!vha->vp_idx && IS_QLA82XX(ha)) { if (test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags)) start_dpc++; qla82xx_watchdog(vha); @@ -3679,8 +3680,8 @@ qla2x00_timer(scsi_qla_host_t *vha) atomic_read(&vha->loop_down_timer))); } - /* Check if beacon LED needs to be blinked */ - if (ha->beacon_blink_led == 1) { + /* Check if beacon LED needs to be blinked for physical host only */ + if (!vha->vp_idx && (ha->beacon_blink_led == 1)) { set_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags); start_dpc++; } From 8a72111ee11db1030a9291bf05d87ffc1dd9cff5 Mon Sep 17 00:00:00 2001 From: Arun Easi Date: Tue, 10 May 2011 11:18:17 -0700 Subject: [PATCH 1801/2556] qla2xxx: Fix vport delete hang when logins are outstanding. commit 9f40682e2857a3c2ddb80a87b185af3c6a708346 upstream. Timer is required to flush out entries that may be present in work queues. Signed-off-by: Arun Easi Signed-off-by: Madhuranath Iyengar Signed-off-by: James Bottomley Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/qla2xxx/qla_attr.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index d3e58d763b434..c52a0a26f2a7d 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -1877,14 +1877,15 @@ qla24xx_vport_delete(struct fc_vport *fc_vport) scsi_remove_host(vha->host); + /* Allow timer to run to drain queued items, when removing vp */ + qla24xx_deallocate_vp_id(vha); + if (vha->timer_active) { qla2x00_vp_stop_timer(vha); DEBUG15(printk(KERN_INFO "scsi(%ld): timer for the vport[%d]" " = %p has stopped\n", vha->host_no, vha->vp_idx, vha)); } - qla24xx_deallocate_vp_id(vha); - /* No pending activities shall be there on the vha now */ DEBUG(msleep(random32()%10)); /* Just to see if something falls on * the net we have placed below */ From 2752ce97f6a85284c1859ca932c654f4deceeba6 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Tue, 10 May 2011 19:28:41 +0000 Subject: [PATCH 1802/2556] powerpc/kdump64: Don't reference freed memory as pacas commit bd9e5eefecb3d69018bb95796298019d309cbec8 upstream. Starting with 1426d5a3bd07589534286375998c0c8c6fdc5260 (powerpc: Dynamically allocate pacas) the space for pacas beyond cpu_possible is freed, but we failed to update the loop in crash.c. Since c1854e00727f50f7ac99e98d26ece04c087ef785 (powerpc: Set nr_cpu_ids early and use it to free PACAs) the number of pacas allocated is always nr_cpu_ids. Signed-off-by: Milton Miller Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/crash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/crash.c b/arch/powerpc/kernel/crash.c index 3d569e2aff18e..8ee4c7c739f89 100644 --- a/arch/powerpc/kernel/crash.c +++ b/arch/powerpc/kernel/crash.c @@ -170,7 +170,7 @@ static void crash_kexec_wait_realmode(int cpu) int i; msecs = 10000; - for (i=0; i < NR_CPUS && msecs > 0; i++) { + for (i=0; i < nr_cpu_ids && msecs > 0; i++) { if (i == cpu) continue; From ce19c2986d069294f5409430746069102603aa82 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Tue, 10 May 2011 19:28:33 +0000 Subject: [PATCH 1803/2556] powerpc/kexec: Fix memory corruption from unallocated slaves commit 3d2cea732d68aa270c360f55d8669820ebce188a upstream. Commit 1fc711f7ffb01089efc58042cfdbac8573d1b59a (powerpc/kexec: Fix race in kexec shutdown) moved the write to signal the cpu had exited the kernel from before the transition to real mode in kexec_smp_wait to kexec_wait. Unfornately it missed that kexec_wait is used both by cpus leaving the kernel and by secondary slave cpus that were not allocated a paca for what ever reason -- they could be beyond nr_cpus or not described in the current device tree for whatever reason (for example, kexec-load was not refreshed after a cpu hotplug operation). Cpus coming through that path they will write to paca[NR_CPUS] which is beyond the space allocated for the paca data and overwrite memory not allocated to pacas but very likely still real mode accessable). Move the write back to kexec_smp_wait, which is used only by cpus that found their paca, but after the transition to real mode. Signed-off-by: Milton Miller Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/misc_64.S | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/kernel/misc_64.S b/arch/powerpc/kernel/misc_64.S index 206a321a71d35..e89df59cdc5a5 100644 --- a/arch/powerpc/kernel/misc_64.S +++ b/arch/powerpc/kernel/misc_64.S @@ -462,7 +462,8 @@ _GLOBAL(disable_kernel_fp) * wait for the flag to change, indicating this kernel is going away but * the slave code for the next one is at addresses 0 to 100. * - * This is used by all slaves. + * This is used by all slaves, even those that did not find a matching + * paca in the secondary startup code. * * Physical (hardware) cpu id should be in r3. */ @@ -471,10 +472,6 @@ _GLOBAL(kexec_wait) 1: mflr r5 addi r5,r5,kexec_flag-1b - li r4,KEXEC_STATE_REAL_MODE - stb r4,PACAKEXECSTATE(r13) - SYNC - 99: HMT_LOW #ifdef CONFIG_KEXEC /* use no memory without kexec */ lwz r4,0(r5) @@ -499,11 +496,17 @@ kexec_flag: * * get phys id from paca * switch to real mode + * mark the paca as no longer used * join other cpus in kexec_wait(phys_id) */ _GLOBAL(kexec_smp_wait) lhz r3,PACAHWCPUID(r13) bl real_mode + + li r4,KEXEC_STATE_REAL_MODE + stb r4,PACAKEXECSTATE(r13) + SYNC + b .kexec_wait /* From 09ea34e414df2d89f1eb7877c6f678729bdef67e Mon Sep 17 00:00:00 2001 From: Fenghua Yu Date: Tue, 17 May 2011 12:33:26 -0700 Subject: [PATCH 1804/2556] x86, cpufeature: Fix cpuid leaf 7 feature detection commit 2494b030ba9334c7dd7df9b9f7abe4eacc950ec5 upstream. CPUID leaf 7, subleaf 0 returns the maximum subleaf in EAX, not the number of subleaves. Since so far only subleaf 0 is defined (and only the EBX bitfield) we do not need to qualify the test. Signed-off-by: Fenghua Yu Link: http://lkml.kernel.org/r/1305660806-17519-1-git-send-email-fenghua.yu@intel.com Signed-off-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/common.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 1d59834396bdc..a9c7d7d7733a5 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -565,8 +565,7 @@ void __cpuinit get_cpu_cap(struct cpuinfo_x86 *c) cpuid_count(0x00000007, 0, &eax, &ebx, &ecx, &edx); - if (eax > 0) - c->x86_capability[9] = ebx; + c->x86_capability[9] = ebx; } /* AMD-defined flags: level 0x80000001 */ From 8c2d23af8ae2f5990a0df92a19a220ea5804f623 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 4 May 2011 19:37:17 +0530 Subject: [PATCH 1805/2556] ath9k_hw: do noise floor calibration only on required chains commit 28ef6450f0182f95c4f50aaa0ab2043a09c72b0a upstream. At present the noise floor calibration is processed in supported control and extension chains rather than required chains. Unnccesarily doing nfcal in all supported chains leads to invalid nf readings on extn chains and these invalid values got updated into history buffer. While loading those values from history buffer is moving the chip to deaf state. This issue was observed in AR9002/AR9003 chips while doing associate/dissociate in HT40 mode and interface up/down in iterative manner. After some iterations, the chip was moved to deaf state. Somehow the pci devices are recovered by poll work after chip reset. Raading the nf values in all supported extension chains when the hw is not yet configured in HT40 mode results invalid values. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/calib.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index b68a1acbddd01..8482eebd11679 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -69,15 +69,21 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, int16_t *nfarray) { struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_conf *conf = &common->hw->conf; struct ath_nf_limits *limit; struct ath9k_nfcal_hist *h; bool high_nf_mid = false; + u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; int i; h = cal->nfCalHist; limit = ath9k_hw_get_nf_limits(ah, ah->curchan); for (i = 0; i < NUM_NF_READINGS; i++) { + if (!(chainmask & (1 << i)) || + ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf))) + continue; + h[i].nfCalBuffer[h[i].currIndex] = nfarray[i]; if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX) @@ -225,6 +231,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) int32_t val; u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_conf *conf = &common->hw->conf; s16 default_nf = ath9k_hw_get_default_nf(ah, chan); if (ah->caldata) @@ -234,6 +241,9 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) if (chainmask & (1 << i)) { s16 nfval; + if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)) + continue; + if (h) nfval = h[i].privNF; else @@ -293,6 +303,9 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) ENABLE_REGWRITE_BUFFER(ah); for (i = 0; i < NUM_NF_READINGS; i++) { if (chainmask & (1 << i)) { + if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)) + continue; + val = REG_READ(ah, ah->nf_regs[i]); val &= 0xFFFFFE00; val |= (((u32) (-50) << 1) & 0x1ff); From 64219cd0732330e377e066dd30d9db06c02209f3 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 4 May 2011 14:01:26 -0700 Subject: [PATCH 1806/2556] ath9k_hw: fix power for the HT40 duplicate frames commit cf3a03b9c99a0b2715741d116f50f513f545bb2d upstream. With AR9003 at about ~ 10 feet from an AP that uses RTS / CTS you will be able to associate but not not get data through given that the power for the rates used was set too low. This increases the power and permits data connectivity at longer distances from access points when connected with HT40. Without this you will not get any data through when associated to APs configured in HT40 at about more than 10 feet away. Cc: Fiona Cain Cc: Zhen Xie Cc: Kathy Giori Cc: Neha Choksi Cc: Wayne Daniel Cc: Gaurav Jauhar Cc: Samira Naraghi CC: Ashok Chennupati Cc: Lance Zimmerman Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index 4819747fa4c3a..c4d492d635332 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -3994,6 +3994,16 @@ static int ar9003_hw_tx_power_regwrite(struct ath_hw *ah, u8 * pPwrArray) POW_SM(pPwrArray[ALL_TARGET_LEGACY_1L_5L], 0) ); + /* Write the power for duplicated frames - HT40 */ + + /* dup40_cck (LSB), dup40_ofdm, ext20_cck, ext20_ofdm (MSB) */ + REG_WRITE(ah, 0xa3e0, + POW_SM(pPwrArray[ALL_TARGET_LEGACY_6_24], 24) | + POW_SM(pPwrArray[ALL_TARGET_LEGACY_1L_5L], 16) | + POW_SM(pPwrArray[ALL_TARGET_LEGACY_6_24], 8) | + POW_SM(pPwrArray[ALL_TARGET_LEGACY_1L_5L], 0) + ); + /* Write the HT20 power per rate set */ /* 0/8/16 (LSB), 1-3/9-11/17-19, 4, 5 (MSB) */ From b5dfd8ea8b054826a0e12621dcc41f43b2e85932 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 11 May 2011 14:57:26 -0700 Subject: [PATCH 1807/2556] ath9k_hw: fix dual band assumption for XB113 commit 9ba7f4f5eba5f4b44c7796bbad29f8ec3a7d5864 upstream. The XB113 cards are single band, 5 GHz-only, but the default settings were configured to assume it was dual band. Users of these cards then would see 2.4 GHz channels but you would never get any scan results from these channels given that the radio is not present. Cc: Fiona Cain Cc: Ray Li Cc: Kathy Giori Cc: Aeolus Yang Cc: Dan Friedman Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index c4d492d635332..12b7a1231ea28 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -652,7 +652,7 @@ static const struct ar9300_eeprom ar9300_x113 = { .regDmn = { LE16(0), LE16(0x1f) }, .txrxMask = 0x77, /* 4 bits tx and 4 bits rx */ .opCapFlags = { - .opFlags = AR5416_OPFLAGS_11G | AR5416_OPFLAGS_11A, + .opFlags = AR5416_OPFLAGS_11A, .eepMisc = 0, }, .rfSilent = 0, From 00914170ac616eef890065c518a749ad66899f09 Mon Sep 17 00:00:00 2001 From: Senthil Balasubramanian Date: Thu, 12 May 2011 16:24:28 +0530 Subject: [PATCH 1808/2556] ath9k_hw: Fix STA connection issues with AR9380 (XB113). commit be0e6aa5a0c487a2a0880dda8bc70f7f1860fc39 upstream. XB113 (AR9380) 3x3 SB 5G only cards were failing to connect to APs due to incorrect xpabiaslevel configuration. fix it. Cc: Ray Li Cc: Kathy Giori Cc: Aeolus Yang Cc: compat@orbit-lab.org Signed-off-by: Senthil Balasubramanian Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index 12b7a1231ea28..80f0dee83843e 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -922,7 +922,7 @@ static const struct ar9300_eeprom ar9300_x113 = { .db_stage2 = {3, 3, 3}, /* 3 chain */ .db_stage3 = {3, 3, 3}, /* doesn't exist for 2G */ .db_stage4 = {3, 3, 3}, /* don't exist for 2G */ - .xpaBiasLvl = 0, + .xpaBiasLvl = 0xf, .txFrameToDataStart = 0x0e, .txFrameToPaOn = 0x0e, .txClip = 3, /* 4 bits tx_clip, 4 bits dac_scale_cck */ From f003fed4410b5da934d4663db9dfb8693dd8dca8 Mon Sep 17 00:00:00 2001 From: Ryan Grimm Date: Thu, 31 Mar 2011 19:33:02 +0000 Subject: [PATCH 1809/2556] powerpc: Set nr_cpu_ids early and use it to free PACAs commit c1854e00727f50f7ac99e98d26ece04c087ef785 upstream. Without this, "holes" in the CPU numbering can cause us to free too many PACAs Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/paca.c | 2 +- arch/powerpc/kernel/setup-common.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/paca.c b/arch/powerpc/kernel/paca.c index f4adf89d76141..10f0aadee95b9 100644 --- a/arch/powerpc/kernel/paca.c +++ b/arch/powerpc/kernel/paca.c @@ -203,7 +203,7 @@ void __init free_unused_pacas(void) { int new_size; - new_size = PAGE_ALIGN(sizeof(struct paca_struct) * num_possible_cpus()); + new_size = PAGE_ALIGN(sizeof(struct paca_struct) * nr_cpu_ids); if (new_size >= paca_size) return; diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index 9d4882a466471..21f30cb68077f 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -509,6 +509,9 @@ void __init smp_setup_cpu_maps(void) */ cpu_init_thread_core_maps(nthreads); + /* Now that possible cpus are set, set nr_cpu_ids for later use */ + nr_cpu_ids = find_last_bit(cpumask_bits(cpu_possible_mask),NR_CPUS) + 1; + free_unused_pacas(); } #endif /* CONFIG_SMP */ From 148f054048b33a4e506710837eecce266d7523b3 Mon Sep 17 00:00:00 2001 From: Eric B Munson Date: Mon, 23 May 2011 04:22:40 +0000 Subject: [PATCH 1810/2556] powerpc/oprofile: Handle events that raise an exception without overflowing commit ad5d5292f16c6c1d7d3e257c4c7407594286b97e upstream. Commit 0837e3242c73566fc1c0196b4ec61779c25ffc93 fixes a situation on POWER7 where events can roll back if a specualtive event doesn't actually complete. This can raise a performance monitor exception. We need to catch this to ensure that we reset the PMC. In all cases the PMC will be less than 256 cycles from overflow. This patch lifts Anton's fix for the problem in perf and applies it to oprofile as well. Signed-off-by: Eric B Munson Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/oprofile/op_model_power4.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/oprofile/op_model_power4.c b/arch/powerpc/oprofile/op_model_power4.c index 80774092db77f..93636ca48d3d5 100644 --- a/arch/powerpc/oprofile/op_model_power4.c +++ b/arch/powerpc/oprofile/op_model_power4.c @@ -261,6 +261,28 @@ static int get_kernel(unsigned long pc, unsigned long mmcra) return is_kernel; } +static bool pmc_overflow(unsigned long val) +{ + if ((int)val < 0) + return true; + + /* + * Events on POWER7 can roll back if a speculative event doesn't + * eventually complete. Unfortunately in some rare cases they will + * raise a performance monitor exception. We need to catch this to + * ensure we reset the PMC. In all cases the PMC will be 256 or less + * cycles from overflow. + * + * We only do this if the first pass fails to find any overflowing + * PMCs because a user might set a period of less than 256 and we + * don't want to mistakenly reset them. + */ + if (__is_processor(PV_POWER7) && ((0x80000000 - val) <= 256)) + return true; + + return false; +} + static void power4_handle_interrupt(struct pt_regs *regs, struct op_counter_config *ctr) { @@ -281,7 +303,7 @@ static void power4_handle_interrupt(struct pt_regs *regs, for (i = 0; i < cur_cpu_spec->num_pmcs; ++i) { val = classic_ctr_read(i); - if (val < 0) { + if (pmc_overflow(val)) { if (oprofile_running && ctr[i].enabled) { oprofile_add_ext_sample(pc, regs, i, is_kernel); classic_ctr_write(i, reset_value[i]); From 05d0b1401071aff5e886533e424e1c7b4421a3a1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 6 May 2011 11:11:20 -0700 Subject: [PATCH 1811/2556] iwlagn: fix iwl_is_any_associated commit 054ec924944912413e4ee927b8cf02f476d08783 upstream. The function iwl_is_any_associated() was intended to check both contexts, but due to an oversight it only checks the BSS context. This leads to a problem with scanning since the passive dwell time isn't restricted appropriately and a scan that includes passive channels will never finish if only the PAN context is associated since the default dwell time of 120ms won't fit into the normal 100 TU DTIM interval. Fix the function by using for_each_context() and also reorganise the other functions a bit to take advantage of each other making the code easier to read. Signed-off-by: Johannes Berg Signed-off-by: Wey-Yi Guy Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl-dev.h | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 8dda67850af45..e4872b13f93f0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -1604,21 +1604,24 @@ iwl_rxon_ctx_from_vif(struct ieee80211_vif *vif) ctx < &priv->contexts[NUM_IWL_RXON_CTX]; ctx++) \ if (priv->valid_contexts & BIT(ctx->ctxid)) -static inline int iwl_is_associated(struct iwl_priv *priv, - enum iwl_rxon_context_id ctxid) +static inline int iwl_is_associated_ctx(struct iwl_rxon_context *ctx) { - return (priv->contexts[ctxid].active.filter_flags & - RXON_FILTER_ASSOC_MSK) ? 1 : 0; + return (ctx->active.filter_flags & RXON_FILTER_ASSOC_MSK) ? 1 : 0; } -static inline int iwl_is_any_associated(struct iwl_priv *priv) +static inline int iwl_is_associated(struct iwl_priv *priv, + enum iwl_rxon_context_id ctxid) { - return iwl_is_associated(priv, IWL_RXON_CTX_BSS); + return iwl_is_associated_ctx(&priv->contexts[ctxid]); } -static inline int iwl_is_associated_ctx(struct iwl_rxon_context *ctx) +static inline int iwl_is_any_associated(struct iwl_priv *priv) { - return (ctx->active.filter_flags & RXON_FILTER_ASSOC_MSK) ? 1 : 0; + struct iwl_rxon_context *ctx; + for_each_context(priv, ctx) + if (iwl_is_associated_ctx(ctx)) + return true; + return false; } static inline int is_channel_valid(const struct iwl_channel_info *ch_info) From 1b42653c2a64cd81723b19d011639c07c54909e6 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 29 Apr 2011 10:15:20 +0200 Subject: [PATCH 1812/2556] block: rescan partitions on invalidated devices on -ENOMEDIA too commit 02e352287a40bd456eb78df705bf888bc3161d3f upstream. __blkdev_get() doesn't rescan partitions if disk->fops->open() fails, which leads to ghost partition devices lingering after medimum removal is known to both the kernel and userland. The behavior also creates a subtle inconsistency where O_NONBLOCK open, which doesn't fail even if there's no medium, clears the ghots partitions, which is exploited to work around the problem from userland. Fix it by updating __blkdev_get() to issue partition rescan after -ENOMEDIA too. This was reported in the following bz. https://bugzilla.kernel.org/show_bug.cgi?id=13029 Stable: 2.6.38 Signed-off-by: Tejun Heo Reported-by: David Zeuthen Reported-by: Martin Pitt Reported-by: Kay Sievers Tested-by: Kay Sievers Cc: Alan Cox Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- fs/block_dev.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/fs/block_dev.c b/fs/block_dev.c index 889287019599a..b6b09cc88a7dd 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1099,6 +1099,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) if (!bdev->bd_part) goto out_clear; + ret = 0; if (disk->fops->open) { ret = disk->fops->open(bdev, mode); if (ret == -ERESTARTSYS) { @@ -1114,9 +1115,18 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) mutex_unlock(&bdev->bd_mutex); goto restart; } - if (ret) - goto out_clear; } + /* + * If the device is invalidated, rescan partition + * if open succeeded or failed with -ENOMEDIUM. + * The latter is necessary to prevent ghost + * partitions on a removed medium. + */ + if (bdev->bd_invalidated && (!ret || ret == -ENOMEDIUM)) + rescan_partitions(disk, bdev); + if (ret) + goto out_clear; + if (!bdev->bd_openers) { bd_set_size(bdev,(loff_t)get_capacity(disk)<<9); bdi = blk_get_backing_dev_info(bdev); @@ -1124,8 +1134,6 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) bdi = &default_backing_dev_info; bdev_inode_switch_bdi(bdev->bd_inode, bdi); } - if (bdev->bd_invalidated) - rescan_partitions(disk, bdev); } else { struct block_device *whole; whole = bdget_disk(disk, 0); @@ -1152,13 +1160,14 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) put_disk(disk); disk = NULL; if (bdev->bd_contains == bdev) { - if (bdev->bd_disk->fops->open) { + ret = 0; + if (bdev->bd_disk->fops->open) ret = bdev->bd_disk->fops->open(bdev, mode); - if (ret) - goto out_unlock_bdev; - } - if (bdev->bd_invalidated) + /* the same as first opener case, read comment there */ + if (bdev->bd_invalidated && (!ret || ret == -ENOMEDIUM)) rescan_partitions(bdev->bd_disk, bdev); + if (ret) + goto out_unlock_bdev; } } bdev->bd_openers++; From 9d7e15702edac354221e82cfd64e21b99590e81c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 23 May 2011 13:26:07 +0200 Subject: [PATCH 1813/2556] block: move bd_set_size() above rescan_partitions() in __blkdev_get() commit 7e69723fef8771a9d57bd27d36281d756130b4b5 upstream. 02e352287a4 (block: rescan partitions on invalidated devices on -ENOMEDIA too) relocated partition rescan above explicit bd_set_size() to simplify condition check. As rescan_partitions() does its own bdev size setting, this doesn't break anything; however, rescan_partitions() prints out the following messages when adjusting bdev size, which can be confusing. sda: detected capacity change from 0 to 146815737856 sdb: detected capacity change from 0 to 146815737856 This patch restores the original order and remove the warning messages. stable: Please apply together with 02e352287a4 (block: rescan partitions on invalidated devices on -ENOMEDIA too). Signed-off-by: Tejun Heo Reported-by: Tony Luck Tested-by: Tony Luck Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- fs/block_dev.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/fs/block_dev.c b/fs/block_dev.c index b6b09cc88a7dd..0920e7cfde5ac 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1116,6 +1116,15 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) goto restart; } } + + if (!ret && !bdev->bd_openers) { + bd_set_size(bdev,(loff_t)get_capacity(disk)<<9); + bdi = blk_get_backing_dev_info(bdev); + if (bdi == NULL) + bdi = &default_backing_dev_info; + bdev_inode_switch_bdi(bdev->bd_inode, bdi); + } + /* * If the device is invalidated, rescan partition * if open succeeded or failed with -ENOMEDIUM. @@ -1126,14 +1135,6 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) rescan_partitions(disk, bdev); if (ret) goto out_clear; - - if (!bdev->bd_openers) { - bd_set_size(bdev,(loff_t)get_capacity(disk)<<9); - bdi = blk_get_backing_dev_info(bdev); - if (bdi == NULL) - bdi = &default_backing_dev_info; - bdev_inode_switch_bdi(bdev->bd_inode, bdi); - } } else { struct block_device *whole; whole = bdget_disk(disk, 0); From fa9e1619451a71b98de8c050fb2950bb4b2bce60 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Mar 2011 19:54:28 +0100 Subject: [PATCH 1814/2556] paride: Convert to bdops->check_events() commit b1b56b93f331bd61492fdb99e7986f7a528ca730 upstream. Convert paride drivers from ->media_changed() to ->check_events(). pcd and pd buffer and clear events after reporting; however, pf unconditionally reports MEDIA_CHANGE and will generate spurious events when polled. Signed-off-by: Tejun Heo Cc: Jens Axboe Cc: Kay Sievers Cc: Tim Waugh Signed-off-by: Greg Kroah-Hartman --- drivers/block/paride/pcd.c | 18 +++++++++++------- drivers/block/paride/pd.c | 7 ++++--- drivers/block/paride/pf.c | 10 ++++++---- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c index 62cec6afd7adf..2f2ccf6862519 100644 --- a/drivers/block/paride/pcd.c +++ b/drivers/block/paride/pcd.c @@ -172,7 +172,8 @@ module_param_array(drive3, int, NULL, 0); static int pcd_open(struct cdrom_device_info *cdi, int purpose); static void pcd_release(struct cdrom_device_info *cdi); static int pcd_drive_status(struct cdrom_device_info *cdi, int slot_nr); -static int pcd_media_changed(struct cdrom_device_info *cdi, int slot_nr); +static unsigned int pcd_check_events(struct cdrom_device_info *cdi, + unsigned int clearing, int slot_nr); static int pcd_tray_move(struct cdrom_device_info *cdi, int position); static int pcd_lock_door(struct cdrom_device_info *cdi, int lock); static int pcd_drive_reset(struct cdrom_device_info *cdi); @@ -257,10 +258,11 @@ static int pcd_block_ioctl(struct block_device *bdev, fmode_t mode, return ret; } -static int pcd_block_media_changed(struct gendisk *disk) +static unsigned int pcd_block_check_events(struct gendisk *disk, + unsigned int clearing) { struct pcd_unit *cd = disk->private_data; - return cdrom_media_changed(&cd->info); + return cdrom_check_events(&cd->info, clearing); } static const struct block_device_operations pcd_bdops = { @@ -268,14 +270,14 @@ static const struct block_device_operations pcd_bdops = { .open = pcd_block_open, .release = pcd_block_release, .ioctl = pcd_block_ioctl, - .media_changed = pcd_block_media_changed, + .check_events = pcd_block_check_events, }; static struct cdrom_device_ops pcd_dops = { .open = pcd_open, .release = pcd_release, .drive_status = pcd_drive_status, - .media_changed = pcd_media_changed, + .check_events = pcd_check_events, .tray_move = pcd_tray_move, .lock_door = pcd_lock_door, .get_mcn = pcd_get_mcn, @@ -318,6 +320,7 @@ static void pcd_init_units(void) disk->first_minor = unit; strcpy(disk->disk_name, cd->name); /* umm... */ disk->fops = &pcd_bdops; + disk->events = DISK_EVENT_MEDIA_CHANGE; } } @@ -502,13 +505,14 @@ static int pcd_packet(struct cdrom_device_info *cdi, struct packet_command *cgc) #define DBMSG(msg) ((verbose>1)?(msg):NULL) -static int pcd_media_changed(struct cdrom_device_info *cdi, int slot_nr) +static unsigned int pcd_check_events(struct cdrom_device_info *cdi, + unsigned int clearing, int slot_nr) { struct pcd_unit *cd = cdi->handle; int res = cd->changed; if (res) cd->changed = 0; - return res; + return res ? DISK_EVENT_MEDIA_CHANGE : 0; } static int pcd_lock_door(struct cdrom_device_info *cdi, int lock) diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c index c0ee1558b9bba..21dfdb7768695 100644 --- a/drivers/block/paride/pd.c +++ b/drivers/block/paride/pd.c @@ -794,7 +794,7 @@ static int pd_release(struct gendisk *p, fmode_t mode) return 0; } -static int pd_check_media(struct gendisk *p) +static unsigned int pd_check_events(struct gendisk *p, unsigned int clearing) { struct pd_unit *disk = p->private_data; int r; @@ -803,7 +803,7 @@ static int pd_check_media(struct gendisk *p) pd_special_command(disk, pd_media_check); r = disk->changed; disk->changed = 0; - return r; + return r ? DISK_EVENT_MEDIA_CHANGE : 0; } static int pd_revalidate(struct gendisk *p) @@ -822,7 +822,7 @@ static const struct block_device_operations pd_fops = { .release = pd_release, .ioctl = pd_ioctl, .getgeo = pd_getgeo, - .media_changed = pd_check_media, + .check_events = pd_check_events, .revalidate_disk= pd_revalidate }; @@ -837,6 +837,7 @@ static void pd_probe_drive(struct pd_unit *disk) p->fops = &pd_fops; p->major = major; p->first_minor = (disk - pd) << PD_BITS; + p->events = DISK_EVENT_MEDIA_CHANGE; disk->gd = p; p->private_data = disk; p->queue = pd_queue; diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c index 635f25dd9e108..7adeb1edbf43f 100644 --- a/drivers/block/paride/pf.c +++ b/drivers/block/paride/pf.c @@ -243,7 +243,8 @@ static struct pf_unit units[PF_UNITS]; static int pf_identify(struct pf_unit *pf); static void pf_lock(struct pf_unit *pf, int func); static void pf_eject(struct pf_unit *pf); -static int pf_check_media(struct gendisk *disk); +static unsigned int pf_check_events(struct gendisk *disk, + unsigned int clearing); static char pf_scratch[512]; /* scratch block buffer */ @@ -270,7 +271,7 @@ static const struct block_device_operations pf_fops = { .release = pf_release, .ioctl = pf_ioctl, .getgeo = pf_getgeo, - .media_changed = pf_check_media, + .check_events = pf_check_events, }; static void __init pf_init_units(void) @@ -293,6 +294,7 @@ static void __init pf_init_units(void) disk->first_minor = unit; strcpy(disk->disk_name, pf->name); disk->fops = &pf_fops; + disk->events = DISK_EVENT_MEDIA_CHANGE; if (!(*drives[unit])[D_PRT]) pf_drive_count++; } @@ -377,9 +379,9 @@ static int pf_release(struct gendisk *disk, fmode_t mode) } -static int pf_check_media(struct gendisk *disk) +static unsigned int pf_check_events(struct gendisk *disk, unsigned int clearing) { - return 1; + return DISK_EVENT_MEDIA_CHANGE; } static inline int status_reg(struct pf_unit *pf) From 620f10854dc3d00f0bf93d3a803384e342bafada Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Mar 2011 19:54:28 +0100 Subject: [PATCH 1815/2556] gdrom,viocd: Convert to bdops->check_events() commit 1c27030bd21e7e2c68ef5be9f28c63778cf4b27f upstream. Convert gdrom and viocd from ->media_changed() to ->check_events(). It's unclear how the conditions are cleared and it's possible that it may generate spurious events when polled. Signed-off-by: Tejun Heo Cc: Jens Axboe Cc: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/cdrom/gdrom.c | 16 ++++++++++------ drivers/cdrom/viocd.c | 17 ++++++++++------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c index 64a21461c408f..b2b034fea34e6 100644 --- a/drivers/cdrom/gdrom.c +++ b/drivers/cdrom/gdrom.c @@ -395,10 +395,12 @@ static int gdrom_drivestatus(struct cdrom_device_info *cd_info, int ignore) return CDS_NO_INFO; } -static int gdrom_mediachanged(struct cdrom_device_info *cd_info, int ignore) +static unsigned int gdrom_check_events(struct cdrom_device_info *cd_info, + unsigned int clearing, int ignore) { /* check the sense key */ - return (__raw_readb(GDROM_ERROR_REG) & 0xF0) == 0x60; + return (__raw_readb(GDROM_ERROR_REG) & 0xF0) == 0x60 ? + DISK_EVENT_MEDIA_CHANGE : 0; } /* reset the G1 bus */ @@ -483,7 +485,7 @@ static struct cdrom_device_ops gdrom_ops = { .open = gdrom_open, .release = gdrom_release, .drive_status = gdrom_drivestatus, - .media_changed = gdrom_mediachanged, + .check_events = gdrom_check_events, .get_last_session = gdrom_get_last_session, .reset = gdrom_hardreset, .audio_ioctl = gdrom_audio_ioctl, @@ -509,9 +511,10 @@ static int gdrom_bdops_release(struct gendisk *disk, fmode_t mode) return 0; } -static int gdrom_bdops_mediachanged(struct gendisk *disk) +static unsigned int gdrom_bdops_check_events(struct gendisk *disk, + unsigned int clearing) { - return cdrom_media_changed(gd.cd_info); + return cdrom_check_events(gd.cd_info, clearing); } static int gdrom_bdops_ioctl(struct block_device *bdev, fmode_t mode, @@ -530,7 +533,7 @@ static const struct block_device_operations gdrom_bdops = { .owner = THIS_MODULE, .open = gdrom_bdops_open, .release = gdrom_bdops_release, - .media_changed = gdrom_bdops_mediachanged, + .check_events = gdrom_bdops_check_events, .ioctl = gdrom_bdops_ioctl, }; @@ -800,6 +803,7 @@ static int __devinit probe_gdrom(struct platform_device *devptr) goto probe_fail_cdrom_register; } gd.disk->fops = &gdrom_bdops; + gd.disk->events = DISK_EVENT_MEDIA_CHANGE; /* latch on to the interrupt */ err = gdrom_set_interrupt_handlers(); if (err) diff --git a/drivers/cdrom/viocd.c b/drivers/cdrom/viocd.c index be73a9b493a69..4e874c5fa6059 100644 --- a/drivers/cdrom/viocd.c +++ b/drivers/cdrom/viocd.c @@ -186,10 +186,11 @@ static int viocd_blk_ioctl(struct block_device *bdev, fmode_t mode, return ret; } -static int viocd_blk_media_changed(struct gendisk *disk) +static unsigned int viocd_blk_check_events(struct gendisk *disk, + unsigned int clearing) { struct disk_info *di = disk->private_data; - return cdrom_media_changed(&di->viocd_info); + return cdrom_check_events(&di->viocd_info, clearing); } static const struct block_device_operations viocd_fops = { @@ -197,7 +198,7 @@ static const struct block_device_operations viocd_fops = { .open = viocd_blk_open, .release = viocd_blk_release, .ioctl = viocd_blk_ioctl, - .media_changed = viocd_blk_media_changed, + .check_events = viocd_blk_check_events, }; static int viocd_open(struct cdrom_device_info *cdi, int purpose) @@ -320,7 +321,8 @@ static void do_viocd_request(struct request_queue *q) } } -static int viocd_media_changed(struct cdrom_device_info *cdi, int disc_nr) +static unsigned int viocd_check_events(struct cdrom_device_info *cdi, + unsigned int clearing, int disc_nr) { struct viocd_waitevent we; HvLpEvent_Rc hvrc; @@ -340,7 +342,7 @@ static int viocd_media_changed(struct cdrom_device_info *cdi, int disc_nr) if (hvrc != 0) { pr_warning("bad rc on HvCallEvent_signalLpEventFast %d\n", (int)hvrc); - return -EIO; + return 0; } wait_for_completion(&we.com); @@ -354,7 +356,7 @@ static int viocd_media_changed(struct cdrom_device_info *cdi, int disc_nr) return 0; } - return we.changed; + return we.changed ? DISK_EVENT_MEDIA_CHANGE : 0; } static int viocd_lock_door(struct cdrom_device_info *cdi, int locking) @@ -550,7 +552,7 @@ static int viocd_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, static struct cdrom_device_ops viocd_dops = { .open = viocd_open, .release = viocd_release, - .media_changed = viocd_media_changed, + .check_events = viocd_check_events, .lock_door = viocd_lock_door, .generic_packet = viocd_packet, .audio_ioctl = viocd_audio_ioctl, @@ -624,6 +626,7 @@ static int viocd_probe(struct vio_dev *vdev, const struct vio_device_id *id) gendisk->queue = q; gendisk->fops = &viocd_fops; gendisk->flags = GENHD_FL_CD|GENHD_FL_REMOVABLE; + gendisk->events = DISK_EVENT_MEDIA_CHANGE; set_capacity(gendisk, 0); gendisk->private_data = d; d->viocd_disk = gendisk; From 5fb46ae7b8c51b05a12c6a66108e8d398c20ee09 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Mar 2011 19:54:27 +0100 Subject: [PATCH 1816/2556] ide: Convert to bdops->check_events() commit 5b03a1b140e13a28ff6be1526892a9dc538ddef6 upstream. Convert ->media_changed() to the new ->check_events() method. The conversion is mostly mechanical. The only notable change is that cdrom now doesn't generate any event if @slot_nr isn't CDSL_CURRENT. It used to return -EINVAL which would be treated as media changed. As media changer isn't supported anyway, this doesn't make any difference. This makes ide emit the standard disk events and allows kernel event polling. Currently, only MEDIA_CHANGE event is implemented. Adding support for EJECT_REQUEST shouldn't be difficult; however, given that ide driver is already deprecated, it probably is best to leave it alone. Signed-off-by: Tejun Heo Acked-by: Jens Axboe Cc: Kay Sievers Cc: "David S. Miller" Cc: linux-ide@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/ide/ide-cd.c | 10 ++++++---- drivers/ide/ide-cd.h | 3 ++- drivers/ide/ide-cd_ioctl.c | 8 ++++---- drivers/ide/ide-gd.c | 14 ++++++++------ 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 0c73fe39a236b..a2e29099ee0c1 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1177,7 +1177,7 @@ static struct cdrom_device_ops ide_cdrom_dops = { .open = ide_cdrom_open_real, .release = ide_cdrom_release_real, .drive_status = ide_cdrom_drive_status, - .media_changed = ide_cdrom_check_media_change_real, + .check_events = ide_cdrom_check_events_real, .tray_move = ide_cdrom_tray_move, .lock_door = ide_cdrom_lock_door, .select_speed = ide_cdrom_select_speed, @@ -1702,10 +1702,11 @@ static int idecd_ioctl(struct block_device *bdev, fmode_t mode, } -static int idecd_media_changed(struct gendisk *disk) +static unsigned int idecd_check_events(struct gendisk *disk, + unsigned int clearing) { struct cdrom_info *info = ide_drv_g(disk, cdrom_info); - return cdrom_media_changed(&info->devinfo); + return cdrom_check_events(&info->devinfo, clearing); } static int idecd_revalidate_disk(struct gendisk *disk) @@ -1723,7 +1724,7 @@ static const struct block_device_operations idecd_ops = { .open = idecd_open, .release = idecd_release, .ioctl = idecd_ioctl, - .media_changed = idecd_media_changed, + .check_events = idecd_check_events, .revalidate_disk = idecd_revalidate_disk }; @@ -1790,6 +1791,7 @@ static int ide_cd_probe(ide_drive_t *drive) ide_cd_read_toc(drive, &sense); g->fops = &idecd_ops; g->flags |= GENHD_FL_REMOVABLE; + g->events = DISK_EVENT_MEDIA_CHANGE; add_disk(g); return 0; diff --git a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h index 93a3cf1b0f3f8..1efc936f5b667 100644 --- a/drivers/ide/ide-cd.h +++ b/drivers/ide/ide-cd.h @@ -111,7 +111,8 @@ int cdrom_check_status(ide_drive_t *, struct request_sense *); int ide_cdrom_open_real(struct cdrom_device_info *, int); void ide_cdrom_release_real(struct cdrom_device_info *); int ide_cdrom_drive_status(struct cdrom_device_info *, int); -int ide_cdrom_check_media_change_real(struct cdrom_device_info *, int); +unsigned int ide_cdrom_check_events_real(struct cdrom_device_info *, + unsigned int clearing, int slot_nr); int ide_cdrom_tray_move(struct cdrom_device_info *, int); int ide_cdrom_lock_door(struct cdrom_device_info *, int); int ide_cdrom_select_speed(struct cdrom_device_info *, int); diff --git a/drivers/ide/ide-cd_ioctl.c b/drivers/ide/ide-cd_ioctl.c index 766b3deeb23c7..2a6bc50e8a41e 100644 --- a/drivers/ide/ide-cd_ioctl.c +++ b/drivers/ide/ide-cd_ioctl.c @@ -79,8 +79,8 @@ int ide_cdrom_drive_status(struct cdrom_device_info *cdi, int slot_nr) return CDS_DRIVE_NOT_READY; } -int ide_cdrom_check_media_change_real(struct cdrom_device_info *cdi, - int slot_nr) +unsigned int ide_cdrom_check_events_real(struct cdrom_device_info *cdi, + unsigned int clearing, int slot_nr) { ide_drive_t *drive = cdi->handle; int retval; @@ -89,9 +89,9 @@ int ide_cdrom_check_media_change_real(struct cdrom_device_info *cdi, (void) cdrom_check_status(drive, NULL); retval = (drive->dev_flags & IDE_DFLAG_MEDIA_CHANGED) ? 1 : 0; drive->dev_flags &= ~IDE_DFLAG_MEDIA_CHANGED; - return retval; + return retval ? DISK_EVENT_MEDIA_CHANGE : 0; } else { - return -EINVAL; + return 0; } } diff --git a/drivers/ide/ide-gd.c b/drivers/ide/ide-gd.c index 35c4b43585e34..c4ffd4888939a 100644 --- a/drivers/ide/ide-gd.c +++ b/drivers/ide/ide-gd.c @@ -285,11 +285,12 @@ static int ide_gd_getgeo(struct block_device *bdev, struct hd_geometry *geo) return 0; } -static int ide_gd_media_changed(struct gendisk *disk) +static unsigned int ide_gd_check_events(struct gendisk *disk, + unsigned int clearing) { struct ide_disk_obj *idkp = ide_drv_g(disk, ide_disk_obj); ide_drive_t *drive = idkp->drive; - int ret; + bool ret; /* do not scan partitions twice if this is a removable device */ if (drive->dev_flags & IDE_DFLAG_ATTACH) { @@ -297,10 +298,10 @@ static int ide_gd_media_changed(struct gendisk *disk) return 0; } - ret = !!(drive->dev_flags & IDE_DFLAG_MEDIA_CHANGED); + ret = drive->dev_flags & IDE_DFLAG_MEDIA_CHANGED; drive->dev_flags &= ~IDE_DFLAG_MEDIA_CHANGED; - return ret; + return ret ? DISK_EVENT_MEDIA_CHANGE : 0; } static void ide_gd_unlock_native_capacity(struct gendisk *disk) @@ -318,7 +319,7 @@ static int ide_gd_revalidate_disk(struct gendisk *disk) struct ide_disk_obj *idkp = ide_drv_g(disk, ide_disk_obj); ide_drive_t *drive = idkp->drive; - if (ide_gd_media_changed(disk)) + if (ide_gd_check_events(disk, 0)) drive->disk_ops->get_capacity(drive); set_capacity(disk, ide_gd_capacity(drive)); @@ -340,7 +341,7 @@ static const struct block_device_operations ide_gd_ops = { .release = ide_gd_release, .ioctl = ide_gd_ioctl, .getgeo = ide_gd_getgeo, - .media_changed = ide_gd_media_changed, + .check_events = ide_gd_check_events, .unlock_native_capacity = ide_gd_unlock_native_capacity, .revalidate_disk = ide_gd_revalidate_disk }; @@ -412,6 +413,7 @@ static int ide_gd_probe(ide_drive_t *drive) if (drive->dev_flags & IDE_DFLAG_REMOVABLE) g->flags = GENHD_FL_REMOVABLE; g->fops = &ide_gd_ops; + g->events = DISK_EVENT_MEDIA_CHANGE; add_disk(g); return 0; From c63356c3ed12f1242ec7fb4b14a6a28b0e0c72a2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 21 Apr 2011 20:54:46 +0200 Subject: [PATCH 1817/2556] block: don't block events on excl write for non-optical devices commit d4dc210f69bcb0b4bef5a83b1c323817be89bad1 upstream. Disk event code automatically blocks events on excl write. This is primarily to avoid issuing polling commands while burning is in progress. This behavior doesn't fit other types of devices with removeable media where polling commands don't have adverse side effects and door locking usually doesn't exist. This patch introduces new genhd flag which controls the auto-blocking behavior and uses it to enable auto-blocking only on optical devices. Note for stable: 2.6.38 and later only Signed-off-by: Tejun Heo Reported-by: Kay Sievers Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/block/paride/pcd.c | 1 + drivers/cdrom/viocd.c | 3 ++- drivers/ide/ide-cd.c | 2 +- drivers/scsi/sr.c | 2 +- fs/block_dev.c | 17 ++++++++++------- include/linux/genhd.h | 1 + 6 files changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c index 2f2ccf6862519..a0aabd904a512 100644 --- a/drivers/block/paride/pcd.c +++ b/drivers/block/paride/pcd.c @@ -320,6 +320,7 @@ static void pcd_init_units(void) disk->first_minor = unit; strcpy(disk->disk_name, cd->name); /* umm... */ disk->fops = &pcd_bdops; + disk->flags = GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE; disk->events = DISK_EVENT_MEDIA_CHANGE; } } diff --git a/drivers/cdrom/viocd.c b/drivers/cdrom/viocd.c index 4e874c5fa6059..ae15a4ddaa9b0 100644 --- a/drivers/cdrom/viocd.c +++ b/drivers/cdrom/viocd.c @@ -625,7 +625,8 @@ static int viocd_probe(struct vio_dev *vdev, const struct vio_device_id *id) blk_queue_max_hw_sectors(q, 4096 / 512); gendisk->queue = q; gendisk->fops = &viocd_fops; - gendisk->flags = GENHD_FL_CD|GENHD_FL_REMOVABLE; + gendisk->flags = GENHD_FL_CD | GENHD_FL_REMOVABLE | + GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE; gendisk->events = DISK_EVENT_MEDIA_CHANGE; set_capacity(gendisk, 0); gendisk->private_data = d; diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index a2e29099ee0c1..413e4ef60356e 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1790,7 +1790,7 @@ static int ide_cd_probe(ide_drive_t *drive) ide_cd_read_toc(drive, &sense); g->fops = &idecd_ops; - g->flags |= GENHD_FL_REMOVABLE; + g->flags |= GENHD_FL_REMOVABLE | GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE; g->events = DISK_EVENT_MEDIA_CHANGE; add_disk(g); return 0; diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index aefadc6a16072..464ee7ba95b7e 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -636,7 +636,7 @@ static int sr_probe(struct device *dev) disk->first_minor = minor; sprintf(disk->disk_name, "sr%d", minor); disk->fops = &sr_bdops; - disk->flags = GENHD_FL_CD; + disk->flags = GENHD_FL_CD | GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE; disk->events = DISK_EVENT_MEDIA_CHANGE | DISK_EVENT_EJECT_REQUEST; blk_queue_rq_timeout(sdev->request_queue, SR_TIMEOUT); diff --git a/fs/block_dev.c b/fs/block_dev.c index 0920e7cfde5ac..59277ba8d2903 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1233,6 +1233,8 @@ int blkdev_get(struct block_device *bdev, fmode_t mode, void *holder) res = __blkdev_get(bdev, mode, 0); if (whole) { + struct gendisk *disk = whole->bd_disk; + /* finish claiming */ mutex_lock(&bdev->bd_mutex); spin_lock(&bdev_lock); @@ -1259,15 +1261,16 @@ int blkdev_get(struct block_device *bdev, fmode_t mode, void *holder) spin_unlock(&bdev_lock); /* - * Block event polling for write claims. Any write - * holder makes the write_holder state stick until all - * are released. This is good enough and tracking - * individual writeable reference is too fragile given - * the way @mode is used in blkdev_get/put(). + * Block event polling for write claims if requested. Any + * write holder makes the write_holder state stick until + * all are released. This is good enough and tracking + * individual writeable reference is too fragile given the + * way @mode is used in blkdev_get/put(). */ - if (!res && (mode & FMODE_WRITE) && !bdev->bd_write_holder) { + if ((disk->flags & GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE) && + !res && (mode & FMODE_WRITE) && !bdev->bd_write_holder) { bdev->bd_write_holder = true; - disk_block_events(bdev->bd_disk); + disk_block_events(disk); } mutex_unlock(&bdev->bd_mutex); diff --git a/include/linux/genhd.h b/include/linux/genhd.h index c0d5f6945c1eb..035bc67051ce5 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -127,6 +127,7 @@ struct hd_struct { #define GENHD_FL_SUPPRESS_PARTITION_INFO 32 #define GENHD_FL_EXT_DEVT 64 /* allow extended devt */ #define GENHD_FL_NATIVE_CAPACITY 128 +#define GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE 256 enum { DISK_EVENT_MEDIA_CHANGE = 1 << 0, /* media changed */ From 5d8ddba27676c7f753b01ac75f90ea90be0ffcea Mon Sep 17 00:00:00 2001 From: "Martin K. Petersen" Date: Wed, 18 May 2011 10:37:35 +0200 Subject: [PATCH 1818/2556] block: Fix discard topology stacking and reporting commit a934a00a69e940b126b9bdbf83e630ef5fe43523 upstream. In some cases we would end up stacking discard_zeroes_data incorrectly. Fix this by enabling the feature by default for stacking drivers and clearing it for low-level drivers. Incorporating a device that does not support dzd will then cause the feature to be disabled in the stacking driver. Also ensure that the maximum discard value does not overflow when exported in sysfs and return 0 in the alignment and dzd fields for devices that don't support discard. Reported-by: Lukas Czerner Signed-off-by: Martin K. Petersen Acked-by: Mike Snitzer Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/blk-settings.c | 3 ++- block/blk-sysfs.c | 3 ++- include/linux/blkdev.h | 7 +++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/block/blk-settings.c b/block/blk-settings.c index 36c8c1f2af180..0aef26e6dc2f4 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -120,7 +120,7 @@ void blk_set_default_limits(struct queue_limits *lim) lim->discard_granularity = 0; lim->discard_alignment = 0; lim->discard_misaligned = 0; - lim->discard_zeroes_data = -1; + lim->discard_zeroes_data = 1; lim->logical_block_size = lim->physical_block_size = lim->io_min = 512; lim->bounce_pfn = (unsigned long)(BLK_BOUNCE_ANY >> PAGE_SHIFT); lim->alignment_offset = 0; @@ -174,6 +174,7 @@ void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn) blk_set_default_limits(&q->limits); blk_queue_max_hw_sectors(q, BLK_SAFE_MAX_SECTORS); + q->limits.discard_zeroes_data = 0; /* * If the caller didn't supply a lock, fall back to our embedded diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 3655e193e60ea..629070309f0d3 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -152,7 +152,8 @@ static ssize_t queue_discard_granularity_show(struct request_queue *q, char *pag static ssize_t queue_discard_max_show(struct request_queue *q, char *page) { - return queue_var_show(q->limits.max_discard_sectors << 9, page); + return sprintf(page, "%llu\n", + (unsigned long long)q->limits.max_discard_sectors << 9); } static ssize_t queue_discard_zeroes_data_show(struct request_queue *q, char *page) diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index d5063e1b55559..65661479a232c 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -252,7 +252,7 @@ struct queue_limits { unsigned char misaligned; unsigned char discard_misaligned; unsigned char cluster; - signed char discard_zeroes_data; + unsigned char discard_zeroes_data; }; struct request_queue @@ -1032,13 +1032,16 @@ static inline int queue_limit_discard_alignment(struct queue_limits *lim, sector { unsigned int alignment = (sector << 9) & (lim->discard_granularity - 1); + if (!lim->max_discard_sectors) + return 0; + return (lim->discard_granularity + lim->discard_alignment - alignment) & (lim->discard_granularity - 1); } static inline unsigned int queue_discard_zeroes_data(struct request_queue *q) { - if (q->limits.discard_zeroes_data == 1) + if (q->limits.max_discard_sectors && q->limits.discard_zeroes_data == 1) return 1; return 0; From f47c98ae14f369c03c71aaac4bd05f0b098a4632 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 18 May 2011 16:20:10 +0200 Subject: [PATCH 1819/2556] block: add proper state guards to __elv_next_request commit 0a58e077eb600d1efd7e54ad9926a75a39d7f8ae upstream. blk_cleanup_queue() calls elevator_exit() and after this, we can't touch the elevator without oopsing. __elv_next_request() must check for this state because in the refcounted queue model, we can still call it after blk_cleanup_queue() has been called. This was reported as causing an oops attributable to scsi. Signed-off-by: James Bottomley Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/blk.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/block/blk.h b/block/blk.h index 2db8f32838e73..e03adf82761a9 100644 --- a/block/blk.h +++ b/block/blk.h @@ -68,7 +68,8 @@ static inline struct request *__elv_next_request(struct request_queue *q) return rq; } - if (!q->elevator->ops->elevator_dispatch_fn(q, 0)) + if (test_bit(QUEUE_FLAG_DEAD, &q->queue_flags) || + !q->elevator->ops->elevator_dispatch_fn(q, 0)) return NULL; } } From 0c3edf8b455dfdf2d7013e5df6a75838f126f863 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 26 May 2011 21:06:50 +0200 Subject: [PATCH 1820/2556] block: always allocate genhd->ev if check_events is implemented commit 75e3f3ee3c64968d42f4843ec49e579f84b5aa0c upstream. 9fd097b149 (block: unexport DISK_EVENT_MEDIA_CHANGE for legacy/fringe drivers) removed DISK_EVENT_MEDIA_CHANGE from legacy/fringe block drivers which have inadequate ->check_events(). Combined with earlier change 7c88a168da (block: don't propagate unlisted DISK_EVENTs to userland), this enables using ->check_events() for internal processing while avoiding enabling in-kernel block event polling which can lead to infinite event loop. Unfortunately, this made many drivers including floppy without any bit set in disk->events and ->async_events in which case disk_add_events() simply skipped allocation of disk->ev, which disables whole event handling. As ->check_events() is still used during open processing for revalidation, this can lead to open failure. This patch always allocates disk->ev if ->check_events is implemented. In the long term, it would make sense to simply include the event structure inline into genhd as it's now used by virtually all block devices. Signed-off-by: Tejun Heo Reported-by: Ondrej Zary Reported-by: Alex Villacis Lasso Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- block/genhd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/genhd.c b/block/genhd.c index cbf1112a885c0..d333f9687eff3 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1724,7 +1724,7 @@ static void disk_add_events(struct gendisk *disk) { struct disk_events *ev; - if (!disk->fops->check_events || !(disk->events | disk->async_events)) + if (!disk->fops->check_events) return; ev = kzalloc(sizeof(*ev), GFP_KERNEL); From 4e82eb6905adb9d7aeb1b6954b4a788c902d0bcf Mon Sep 17 00:00:00 2001 From: Felix Radensky Date: Mon, 25 Apr 2011 01:57:12 +0300 Subject: [PATCH 1821/2556] mtd: mtdconcat: fix NAND OOB write commit 431e1ecabddcd7cbba237182ddf431771f98bb4c upstream. Currently mtdconcat is broken for NAND. An attemtpt to create JFFS2 filesystem on concatenation of several NAND devices fails with OOB write errors. This patch fixes that problem. Signed-off-by: Felix Radensky Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/mtdconcat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 5f5777bd3f75f..c2c93e9206dbb 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -319,7 +319,7 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; - ops->retlen = 0; + ops->retlen = ops->oobretlen = 0; for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; @@ -334,7 +334,7 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) devops.len = subdev->size - to; err = subdev->write_oob(subdev, to, &devops); - ops->retlen += devops.retlen; + ops->retlen += devops.oobretlen; if (err) return err; From 836cea37e901e91122293714af287a407ad242bf Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 28 Apr 2011 20:26:59 +0300 Subject: [PATCH 1822/2556] mtd: return badblockbits back commit 26d9be11485ea8c1102c3e8eaa7667412eef4950 upstream. In commit c7b28e25cb9beb943aead770ff14551b55fa8c79 the initialization of the backblockbits was accidentally removed. This patch returns it back, because otherwise some NAND drivers are broken. This problem was reported by "Saxena, Parth " here: http://lists.infradead.org/pipermail/linux-mtd/2011-April/035221.html Reported-by: Parth Saxena Signed-off-by: Artem Bityutskiy Tested-by: Parth Saxena Acked-by: Parth Saxena Acked-by: Brian Norris Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/nand/nand_base.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index a9c6ce745767a..c4c2b7326a059 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3111,6 +3111,8 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->chip_shift += 32 - 1; } + chip->badblockbits = 8; + /* Set the bad block position */ if (mtd->writesize > 512 || (busw & NAND_BUSWIDTH_16)) chip->badblockpos = NAND_LARGE_BADBLOCK_POS; From aa0ff541cfa5935a5d52f3807c8a4993c46a2799 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 12 May 2011 16:30:30 +0200 Subject: [PATCH 1823/2556] x86, 64-bit: Fix copy_[to/from]_user() checks for the userspace address limit commit 26afb7c661080ae3f1f13ddf7f0c58c4f931c22b upstream. As reported in BZ #30352: https://bugzilla.kernel.org/show_bug.cgi?id=30352 there's a kernel bug related to reading the last allowed page on x86_64. The _copy_to_user() and _copy_from_user() functions use the following check for address limit: if (buf + size >= limit) fail(); while it should be more permissive: if (buf + size > limit) fail(); That's because the size represents the number of bytes being read/write from/to buf address AND including the buf address. So the copy function will actually never touch the limit address even if "buf + size == limit". Following program fails to use the last page as buffer due to the wrong limit check: #include #include #include #define PAGE_SIZE (4096) #define LAST_PAGE ((void*)(0x7fffffffe000)) int main() { int fds[2], err; void * ptr = mmap(LAST_PAGE, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); assert(ptr == LAST_PAGE); err = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds); assert(err == 0); err = send(fds[0], ptr, PAGE_SIZE, 0); perror("send"); assert(err == PAGE_SIZE); err = recv(fds[1], ptr, PAGE_SIZE, MSG_WAITALL); perror("recv"); assert(err == PAGE_SIZE); return 0; } The other place checking the addr limit is the access_ok() function, which is working properly. There's just a misleading comment for the __range_not_ok() macro - which this patch fixes as well. The last page of the user-space address range is a guard page and Brian Gerst observed that the guard page itself due to an erratum on K8 cpus (#121 Sequential Execution Across Non-Canonical Boundary Causes Processor Hang). However, the test code is using the last valid page before the guard page. The bug is that the last byte before the guard page can't be read because of the off-by-one error. The guard page is left in place. This bug would normally not show up because the last page is part of the process stack and never accessed via syscalls. Signed-off-by: Jiri Olsa Acked-by: Brian Gerst Acked-by: Linus Torvalds Link: http://lkml.kernel.org/r/1305210630-7136-1-git-send-email-jolsa@redhat.com Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/uaccess.h | 2 +- arch/x86/lib/copy_user_64.S | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h index abd3e0ea762ac..99f0ad753f32c 100644 --- a/arch/x86/include/asm/uaccess.h +++ b/arch/x86/include/asm/uaccess.h @@ -42,7 +42,7 @@ * Returns 0 if the range is valid, nonzero otherwise. * * This is equivalent to the following test: - * (u33)addr + (u33)size >= (u33)current->addr_limit.seg (u65 for x86_64) + * (u33)addr + (u33)size > (u33)current->addr_limit.seg (u65 for x86_64) * * This needs 33-bit (65-bit for x86_64) arithmetic. We have a carry... */ diff --git a/arch/x86/lib/copy_user_64.S b/arch/x86/lib/copy_user_64.S index a460158b5ac5b..cfd3ca4ac4a74 100644 --- a/arch/x86/lib/copy_user_64.S +++ b/arch/x86/lib/copy_user_64.S @@ -72,7 +72,7 @@ ENTRY(_copy_to_user) addq %rdx,%rcx jc bad_to_user cmpq TI_addr_limit(%rax),%rcx - jae bad_to_user + ja bad_to_user ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,copy_user_generic_unrolled,copy_user_generic_string CFI_ENDPROC ENDPROC(_copy_to_user) @@ -85,7 +85,7 @@ ENTRY(_copy_from_user) addq %rdx,%rcx jc bad_from_user cmpq TI_addr_limit(%rax),%rcx - jae bad_from_user + ja bad_from_user ALTERNATIVE_JUMP X86_FEATURE_REP_GOOD,copy_user_generic_unrolled,copy_user_generic_string CFI_ENDPROC ENDPROC(_copy_from_user) From 1db58ae0a6e032378326412bd446ea23591ff50c Mon Sep 17 00:00:00 2001 From: Lukas Czerner Date: Fri, 20 May 2011 13:55:29 -0400 Subject: [PATCH 1824/2556] ext4: fix possible use-after-free in ext4_remove_li_request() commit 1bb933fb1fa8e4cb337a0d5dfd2ff4c0dc2073e8 upstream. We need to take reference to the s_li_request after we take a mutex, because it might be freed since then, hence result in accessing old already freed memory. Also we should protect the whole ext4_remove_li_request() because ext4_li_info might be in the process of being freed in ext4_lazyinit_thread(). Signed-off-by: Lukas Czerner Signed-off-by: "Theodore Ts'o" Reviewed-by: Eric Sandeen Signed-off-by: Greg Kroah-Hartman --- fs/ext4/super.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 243deb021e7b7..f3e1b18867775 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2716,14 +2716,16 @@ static void ext4_remove_li_request(struct ext4_li_request *elr) static void ext4_unregister_li_request(struct super_block *sb) { - struct ext4_li_request *elr = EXT4_SB(sb)->s_li_request; - - if (!ext4_li_info) + mutex_lock(&ext4_li_mtx); + if (!ext4_li_info) { + mutex_unlock(&ext4_li_mtx); return; + } mutex_lock(&ext4_li_info->li_list_mtx); - ext4_remove_li_request(elr); + ext4_remove_li_request(EXT4_SB(sb)->s_li_request); mutex_unlock(&ext4_li_info->li_list_mtx); + mutex_unlock(&ext4_li_mtx); } static struct task_struct *ext4_lazyinit_task; From c5c970a5a59305869358cf1e5d2bce710b0e10c7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 29 Mar 2011 06:29:37 -0700 Subject: [PATCH 1825/2556] iwlwifi: fix bugs in change_interface commit a2b76b3b31568da9d281a393845f17689594ccdf upstream. If change_interface gets invoked during a firmware restart, it may crash; prevent that from happening by checking if ctx->vif is assigned. Additionally, in my initial commit I forgot to set the vif->p2p variable correctly, so fix that too. Signed-off-by: Johannes Berg Signed-off-by: Wey-Yi Guy Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/iwlwifi/iwl-core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 294e9fcb7eef8..c4c8417153dc0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -1879,6 +1879,15 @@ int iwl_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mutex_lock(&priv->mutex); + if (!ctx->vif || !iwl_is_ready_rf(priv)) { + /* + * Huh? But wait ... this can maybe happen when + * we're in the middle of a firmware restart! + */ + err = -EBUSY; + goto out; + } + interface_modes = ctx->interface_modes | ctx->exclusive_interface_modes; if (!(interface_modes & BIT(newtype))) { @@ -1906,6 +1915,7 @@ int iwl_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, /* success */ iwl_teardown_interface(priv, vif, true); vif->type = newtype; + vif->p2p = newp2p; err = iwl_setup_interface(priv, ctx); WARN_ON(err); /* From 8a2b75b1bc777133e2d70e560e5e49bc1b536b36 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 3 May 2011 22:45:16 -0700 Subject: [PATCH 1826/2556] nl80211: Fix set_key regression with some drivers commit 0e579d6a8f4aea346da818f13ee71401c125e639 upstream. Commit dbd2fd656f2060abfd3a16257f8b51ec60f6d2ed added a mechanism for user space to indicate whether a default key is being configured for only unicast or only multicast frames instead of all frames. This commit added a driver capability flag for indicating whether separate default keys are supported and validation of the set_key command based on that capability. However, this single capability flag is not enough to cover possible difference based on mode (AP/IBSS/STA) and the way this change was introduced resulted in a regression with drivers that do not indicate the new capability (i.e.., more or less any non-mac80211 driver using cfg80211) when using a recent wpa_supplicant snapshot. Fix the regression by removing the new check which is not strictly speaking needed. The new separate default key functionality is needed only for RSN IBSS which has a separate capability indication. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- net/wireless/nl80211.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9b62710891a2b..47d9a875612db 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1679,14 +1679,6 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) if (err) goto out; - if (!(rdev->wiphy.flags & - WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS)) { - if (!key.def_uni || !key.def_multi) { - err = -EOPNOTSUPP; - goto out; - } - } - err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx, key.def_uni, key.def_multi); From 4eb0c775fbb6af5d34681f9a8a68aed7c837fd2e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 May 2011 15:11:37 +0200 Subject: [PATCH 1827/2556] mac80211: fix a few RCU issues commit a3836e02ba4c50db958d32d710b226f2408623dc upstream. A few configuration functions correctly do rcu_read_lock() but don't correctly reference some pointers protected by RCU. Fix that. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- net/mac80211/cfg.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 40f7357c26998..c35dcf79a52da 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -228,11 +228,11 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, goto out; if (pairwise) - key = sta->ptk; + key = rcu_dereference(sta->ptk); else if (key_idx < NUM_DEFAULT_KEYS) - key = sta->gtk[key_idx]; + key = rcu_dereference(sta->gtk[key_idx]); } else - key = sdata->keys[key_idx]; + key = rcu_dereference(sdata->keys[key_idx]); if (!key) goto out; @@ -904,8 +904,10 @@ static int ieee80211_change_mpath(struct wiphy *wiphy, static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop, struct mpath_info *pinfo) { - if (mpath->next_hop) - memcpy(next_hop, mpath->next_hop->sta.addr, ETH_ALEN); + struct sta_info *next_hop_sta = rcu_dereference(mpath->next_hop); + + if (next_hop_sta) + memcpy(next_hop, next_hop_sta->sta.addr, ETH_ALEN); else memset(next_hop, 0, ETH_ALEN); From 352e844e4670bbdc878c0c05301d105fb38c9fe6 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 15 Apr 2011 08:55:44 -0700 Subject: [PATCH 1828/2556] wire up fanotify syscalls commit 1824074b07ee66fa0f714e08579ad85075132d7b upstream. Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- arch/parisc/include/asm/unistd.h | 4 +++- arch/parisc/kernel/sys_parisc32.c | 8 ++++++++ arch/parisc/kernel/syscall_table.S | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/arch/parisc/include/asm/unistd.h b/arch/parisc/include/asm/unistd.h index 3eb82c2a5ec33..09f62a6eb0696 100644 --- a/arch/parisc/include/asm/unistd.h +++ b/arch/parisc/include/asm/unistd.h @@ -814,8 +814,10 @@ #define __NR_recvmmsg (__NR_Linux + 319) #define __NR_accept4 (__NR_Linux + 320) #define __NR_prlimit64 (__NR_Linux + 321) +#define __NR_fanotify_init (__NR_Linux + 322) +#define __NR_fanotify_mark (__NR_Linux + 323) -#define __NR_Linux_syscalls (__NR_prlimit64 + 1) +#define __NR_Linux_syscalls (__NR_fanotify_mark + 1) #define __IGNORE_select /* newselect */ diff --git a/arch/parisc/kernel/sys_parisc32.c b/arch/parisc/kernel/sys_parisc32.c index 88a0ad14a9c99..dc9a624623233 100644 --- a/arch/parisc/kernel/sys_parisc32.c +++ b/arch/parisc/kernel/sys_parisc32.c @@ -228,3 +228,11 @@ asmlinkage long compat_sys_fallocate(int fd, int mode, u32 offhi, u32 offlo, return sys_fallocate(fd, mode, ((loff_t)offhi << 32) | offlo, ((loff_t)lenhi << 32) | lenlo); } + +asmlinkage long compat_sys_fanotify_mark(int fan_fd, int flags, u32 mask_hi, + u32 mask_lo, int fd, + const char __user *pathname) +{ + return sys_fanotify_mark(fan_fd, flags, ((u64)mask_hi << 32) | mask_lo, + fd, pathname); +} diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S index 74867dfdabe57..6ae4b3969bc02 100644 --- a/arch/parisc/kernel/syscall_table.S +++ b/arch/parisc/kernel/syscall_table.S @@ -420,6 +420,8 @@ ENTRY_COMP(recvmmsg) ENTRY_SAME(accept4) /* 320 */ ENTRY_SAME(prlimit64) + ENTRY_SAME(fanotify_init) + ENTRY_COMP(fanotify_mark) /* Nothing yet */ From baa950b85e4a7e6965da1cf618801735584ad65e Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 15 Apr 2011 08:55:45 -0700 Subject: [PATCH 1829/2556] wire up clock_adjtime syscall commit c3f957a22eca106bd28136943305b390b4337ebf upstream. Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- arch/parisc/include/asm/unistd.h | 3 ++- arch/parisc/kernel/syscall_table.S | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/parisc/include/asm/unistd.h b/arch/parisc/include/asm/unistd.h index 09f62a6eb0696..9af5fab2befc5 100644 --- a/arch/parisc/include/asm/unistd.h +++ b/arch/parisc/include/asm/unistd.h @@ -816,8 +816,9 @@ #define __NR_prlimit64 (__NR_Linux + 321) #define __NR_fanotify_init (__NR_Linux + 322) #define __NR_fanotify_mark (__NR_Linux + 323) +#define __NR_clock_adjtime (__NR_Linux + 324) -#define __NR_Linux_syscalls (__NR_fanotify_mark + 1) +#define __NR_Linux_syscalls (__NR_clock_adjtime + 1) #define __IGNORE_select /* newselect */ diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S index 6ae4b3969bc02..759323b2469b7 100644 --- a/arch/parisc/kernel/syscall_table.S +++ b/arch/parisc/kernel/syscall_table.S @@ -422,6 +422,7 @@ ENTRY_SAME(prlimit64) ENTRY_SAME(fanotify_init) ENTRY_COMP(fanotify_mark) + ENTRY_COMP(clock_adjtime) /* Nothing yet */ From eb83cf2f0101005e98c65dbe04d8d83361e93a21 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Wed, 27 Apr 2011 16:10:57 +1000 Subject: [PATCH 1830/2556] drm: Send pending vblank events before disabling vblank. commit 498548ec69c6897fe4376b2ca90758762fa0b817 upstream. This is the least-bad behaviour. It means that we signal the vblank event before it actually happens, but since we're disabling vblanks there's no guarantee that it will *ever* happen otherwise. This prevents GL applications which use WaitMSC from hanging indefinitely. Signed-off-by: Christopher James Halse Rogers Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/drm_irq.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 28d1d3c24d65e..1d4afbc3c8fa2 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -942,11 +942,34 @@ EXPORT_SYMBOL(drm_vblank_put); void drm_vblank_off(struct drm_device *dev, int crtc) { + struct drm_pending_vblank_event *e, *t; + struct timeval now; unsigned long irqflags; + unsigned int seq; spin_lock_irqsave(&dev->vbl_lock, irqflags); vblank_disable_and_save(dev, crtc); DRM_WAKEUP(&dev->vbl_queue[crtc]); + + /* Send any queued vblank events, lest the natives grow disquiet */ + seq = drm_vblank_count_and_time(dev, crtc, &now); + list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { + if (e->pipe != crtc) + continue; + DRM_DEBUG("Sending premature vblank event on disable: \ + wanted %d, current %d\n", + e->event.sequence, seq); + + e->event.sequence = seq; + e->event.tv_sec = now.tv_sec; + e->event.tv_usec = now.tv_usec; + drm_vblank_put(dev, e->pipe); + list_move_tail(&e->base.link, &e->base.file_priv->event_list); + wake_up_interruptible(&e->base.file_priv->event_wait); + trace_drm_vblank_event_delivered(e->base.pid, e->pipe, + e->event.sequence); + } + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); } EXPORT_SYMBOL(drm_vblank_off); From 811dfd3f3d8304443b9dc5a74a953f1b907e6129 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sun, 24 Apr 2011 14:30:14 -0500 Subject: [PATCH 1831/2556] pata_cm64x: fix boot crash on parisc commit 9281b16caac1276817b77033c5b8a1f5ca30102c upstream. The old IDE cmd64x checks the status of the CNTRL register to see if the ports are enabled before probing them. pata_cmd64x doesn't do this, which causes a HPMC on parisc when it tries to poke at the secondary port because apparently the BAR isn't wired up (and a non-responding piece of memory causes a HPMC). Fix this by porting the CNTRL register port detection logic from IDE cmd64x. In addition, following converns from Alan Cox, add a check to see if a mobility electronics bridge is the immediate parent and forgo the check if it is (prevents problems on hotplug controllers). Signed-off-by: James Bottomley Signed-off-by: Jeff Garzik Signed-off-by: Greg Kroah-Hartman --- drivers/ata/pata_cmd64x.c | 42 +++++++++++++++++++++++++++++++++++---- include/linux/pci_ids.h | 2 ++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c index 905ff76d3cbbc..635a759cca607 100644 --- a/drivers/ata/pata_cmd64x.c +++ b/drivers/ata/pata_cmd64x.c @@ -41,6 +41,9 @@ enum { CFR = 0x50, CFR_INTR_CH0 = 0x04, + CNTRL = 0x51, + CNTRL_CH0 = 0x04, + CNTRL_CH1 = 0x08, CMDTIM = 0x52, ARTTIM0 = 0x53, DRWTIM0 = 0x54, @@ -328,9 +331,19 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) .port_ops = &cmd648_port_ops } }; - const struct ata_port_info *ppi[] = { &cmd_info[id->driver_data], NULL }; - u8 mrdmode; + const struct ata_port_info *ppi[] = { + &cmd_info[id->driver_data], + &cmd_info[id->driver_data], + NULL + }; + u8 mrdmode, reg; int rc; + struct pci_dev *bridge = pdev->bus->self; + /* mobility split bridges don't report enabled ports correctly */ + int port_ok = !(bridge && bridge->vendor == + PCI_VENDOR_ID_MOBILITY_ELECTRONICS); + /* all (with exceptions below) apart from 643 have CNTRL_CH0 bit */ + int cntrl_ch0_ok = (id->driver_data != 0); rc = pcim_enable_device(pdev); if (rc) @@ -341,11 +354,18 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) if (pdev->device == PCI_DEVICE_ID_CMD_646) { /* Does UDMA work ? */ - if (pdev->revision > 4) + if (pdev->revision > 4) { ppi[0] = &cmd_info[2]; + ppi[1] = &cmd_info[2]; + } /* Early rev with other problems ? */ - else if (pdev->revision == 1) + else if (pdev->revision == 1) { ppi[0] = &cmd_info[3]; + ppi[1] = &cmd_info[3]; + } + /* revs 1,2 have no CNTRL_CH0 */ + if (pdev->revision < 3) + cntrl_ch0_ok = 0; } pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64); @@ -354,6 +374,20 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) mrdmode |= 0x02; /* Memory read line enable */ pci_write_config_byte(pdev, MRDMODE, mrdmode); + /* check for enabled ports */ + pci_read_config_byte(pdev, CNTRL, ®); + if (!port_ok) + dev_printk(KERN_NOTICE, &pdev->dev, "Mobility Bridge detected, ignoring CNTRL port enable/disable\n"); + if (port_ok && cntrl_ch0_ok && !(reg & CNTRL_CH0)) { + dev_printk(KERN_NOTICE, &pdev->dev, "Primary port is disabled\n"); + ppi[0] = &ata_dummy_port_info; + + } + if (port_ok && !(reg & CNTRL_CH1)) { + dev_printk(KERN_NOTICE, &pdev->dev, "Secondary port is disabled\n"); + ppi[1] = &ata_dummy_port_info; + } + /* Force PIO 0 here.. */ /* PPC specific fixup copied from old driver */ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 3adb06ebf8418..9f36491a18eb0 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -607,6 +607,8 @@ #define PCI_DEVICE_ID_MATROX_G550 0x2527 #define PCI_DEVICE_ID_MATROX_VIA 0x4536 +#define PCI_VENDOR_ID_MOBILITY_ELECTRONICS 0x14f2 + #define PCI_VENDOR_ID_CT 0x102c #define PCI_DEVICE_ID_CT_69000 0x00c0 #define PCI_DEVICE_ID_CT_65545 0x00d8 From d5994d14b3697d22117b2e3c91317c4bff87803a Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 27 Apr 2011 18:20:44 +0200 Subject: [PATCH 1832/2556] ext3: Fix fs corruption when make_indexed_dir() fails commit 86c4f6d85595cd7da635dc6985d27bfa43b1ae10 upstream. When make_indexed_dir() fails (e.g. because of ENOSPC) after it has allocated block for index tree root, we did not properly mark all changed buffers dirty. This lead to only some of these buffers being written out and thus effectively corrupting the directory. Fix the issue by marking all changed data dirty even in the error failure case. Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/ext3/namei.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index 75c968eaf90d2..d5d35d7d2bd0b 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -1416,10 +1416,19 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry, frame->at = entries; frame->bh = bh; bh = bh2; + /* + * Mark buffers dirty here so that if do_split() fails we write a + * consistent set of buffers to disk. + */ + ext3_journal_dirty_metadata(handle, frame->bh); + ext3_journal_dirty_metadata(handle, bh); de = do_split(handle,dir, &bh, frame, &hinfo, &retval); - dx_release (frames); - if (!(de)) + if (!de) { + ext3_mark_inode_dirty(handle, dir); + dx_release(frames); return retval; + } + dx_release(frames); return add_dirent_to_buf(handle, dentry, inode, de, bh); } From 1ddeaea5338e280bf6ad194838a040ff2a13a9eb Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 5 May 2011 13:59:35 +0200 Subject: [PATCH 1833/2556] jbd: Fix forever sleeping process in do_get_write_access() commit 2842bb20eed2e25cde5114298edc62c8883a1d9a upstream. In do_get_write_access() we wait on BH_Unshadow bit for buffer to get from shadow state. The waking code in journal_commit_transaction() has a bug because it does not issue a memory barrier after the buffer is moved from the shadow state and before wake_up_bit() is called. Thus a waitqueue check can happen before the buffer is actually moved from the shadow state and waiting process may never be woken. Fix the problem by issuing proper barrier. Reported-by: Tao Ma Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/jbd/commit.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c index 34a4861c14b85..eea335e5323f0 100644 --- a/fs/jbd/commit.c +++ b/fs/jbd/commit.c @@ -722,8 +722,13 @@ void journal_commit_transaction(journal_t *journal) required. */ JBUFFER_TRACE(jh, "file as BJ_Forget"); journal_file_buffer(jh, commit_transaction, BJ_Forget); - /* Wake up any transactions which were waiting for this - IO to complete */ + /* + * Wake up any transactions which were waiting for this + * IO to complete. The barrier must be here so that changes + * by journal_file_buffer() take effect before wake_up_bit() + * does the waitqueue check. + */ + smp_mb(); wake_up_bit(&bh->b_state, BH_Unshadow); JBUFFER_TRACE(jh, "brelse shadowed buffer"); __brelse(bh); From 271f27554f70be8444aaf632dca5f91d33816753 Mon Sep 17 00:00:00 2001 From: Ted Ts'o Date: Sat, 30 Apr 2011 13:17:11 -0400 Subject: [PATCH 1834/2556] jbd: fix fsync() tid wraparound bug commit d9b01934d56a96d9f4ae2d6204d4ea78a36f5f36 upstream. If an application program does not make any changes to the indirect blocks or extent tree, i_datasync_tid will not get updated. If there are enough commits (i.e., 2**31) such that tid_geq()'s calculations wrap, and there isn't a currently active transaction at the time of the fdatasync() call, this can end up triggering a BUG_ON in fs/jbd/commit.c: J_ASSERT(journal->j_running_transaction != NULL); It's pretty rare that this can happen, since it requires the use of fdatasync() plus *very* frequent and excessive use of fsync(). But with the right workload, it can. We fix this by replacing the use of tid_geq() with an equality test, since there's only one valid transaction id that is valid for us to start: namely, the currently running transaction (if it exists). Reported-by: Martin_Zielinski@McAfee.com Signed-off-by: "Theodore Ts'o" Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/jbd/journal.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/fs/jbd/journal.c b/fs/jbd/journal.c index da1b5e4ffce12..f23188c30ba5a 100644 --- a/fs/jbd/journal.c +++ b/fs/jbd/journal.c @@ -437,9 +437,12 @@ int __log_space_left(journal_t *journal) int __log_start_commit(journal_t *journal, tid_t target) { /* - * Are we already doing a recent enough commit? + * The only transaction we can possibly wait upon is the + * currently running transaction (if it exists). Otherwise, + * the target tid must be an old one. */ - if (!tid_geq(journal->j_commit_request, target)) { + if (journal->j_running_transaction && + journal->j_running_transaction->t_tid == target) { /* * We want a new commit: OK, mark the request and wakeup the * commit thread. We do _not_ do the commit ourselves. @@ -451,7 +454,14 @@ int __log_start_commit(journal_t *journal, tid_t target) journal->j_commit_sequence); wake_up(&journal->j_wait_commit); return 1; - } + } else if (!tid_geq(journal->j_commit_request, target)) + /* This should never happen, but if it does, preserve + the evidence before kjournald goes into a loop and + increments j_commit_sequence beyond all recognition. */ + WARN_ONCE(1, "jbd: bad log_start_commit: %u %u %u %u\n", + journal->j_commit_request, journal->j_commit_sequence, + target, journal->j_running_transaction ? + journal->j_running_transaction->t_tid : 0); return 0; } From af2d3c5a7d5bf8305a6930f866b865284e3db3f4 Mon Sep 17 00:00:00 2001 From: Yang Ruirui Date: Sat, 16 Apr 2011 19:17:48 -0400 Subject: [PATCH 1835/2556] ext4: release page cache in ext4_mb_load_buddy error path commit 26626f1172fb4f3f323239a6a5cf4e082643fa46 upstream. Add missing page_cache_release in the error path of ext4_mb_load_buddy Signed-off-by: Yang Ruirui Signed-off-by: "Theodore Ts'o" Signed-off-by: Greg Kroah-Hartman --- fs/ext4/mballoc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index d1fe09aea73dc..1738236a51060 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -1268,6 +1268,8 @@ ext4_mb_load_buddy(struct super_block *sb, ext4_group_t group, return 0; err: + if (page) + page_cache_release(page); if (e4b->bd_bitmap_page) page_cache_release(e4b->bd_bitmap_page); if (e4b->bd_buddy_page) From 86ea9b89a0e49eba68dc3b8e322f63763806573d Mon Sep 17 00:00:00 2001 From: Jiri Bohac Date: Tue, 19 Apr 2011 02:09:55 +0000 Subject: [PATCH 1836/2556] bonding: 802.3ad - fix agg_device_up [ Upstream commit 2430af8b7fa37ac0be102c77f9dc6ee669d24ba9 ] The slave member of struct aggregator does not necessarily point to a slave which is part of the aggregator. It points to the slave structure containing the aggregator structure, while completely different slaves (or no slaves at all) may be part of the aggregator. The agg_device_up() function wrongly uses agg->slave to find the state of the aggregator. Use agg->lag_ports->slave instead. The bug has been introduced by commit 4cd6fe1c6483cde93e2ec91f58b7af9c9eea51ad ("bonding: fix link down handling in 802.3ad mode"). Signed-off-by: Jiri Bohac Signed-off-by: Jay Vosburgh Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/bonding/bond_3ad.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index a5d5d0b5b1558..28ea364994245 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -1482,8 +1482,11 @@ static struct aggregator *ad_agg_selection_test(struct aggregator *best, static int agg_device_up(const struct aggregator *agg) { - return (netif_running(agg->slave->dev) && - netif_carrier_ok(agg->slave->dev)); + struct port *port = agg->lag_ports; + if (!port) + return 0; + return (netif_running(port->slave->dev) && + netif_carrier_ok(port->slave->dev)); } /** From 3b7219a8c7a581033008ffbf60d6529a24291718 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 13 May 2011 16:03:24 -0400 Subject: [PATCH 1837/2556] bridge: fix forwarding of IPv6 [ Upstream commit cb68552858c64db302771469b1202ea09e696329 ] The commit 6b1e960fdbd75dcd9bcc3ba5ff8898ff1ad30b6e bridge: Reset IPCB when entering IP stack on NF_FORWARD broke forwarding of IPV6 packets in bridge because it would call bp_parse_ip_options with an IPV6 packet. Reported-by: Noah Meyerhans Signed-off-by: Stephen Hemminger Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/bridge/br_netfilter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 333bcaa1377c6..e54990e4448b3 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -739,7 +739,7 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb, nf_bridge->mask |= BRNF_PKT_TYPE; } - if (br_parse_ip_options(skb)) + if (pf == PF_INET && br_parse_ip_options(skb)) return NF_DROP; /* The physdev module checks on this */ From 2d2cd498698d0718644b387bb5b1b4240b47a5d5 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 12 Apr 2011 15:33:23 -0700 Subject: [PATCH 1838/2556] ieee802154: Remove hacked CFLAGS in net/ieee802154/Makefile [ Upstream commit bfac3693c426d280b026f6a1b77dc2294ea43fea ] It adds -Wall (which the kernel carefully controls already) and of all things -DDEBUG (which should be set by other means if desired, please we have dynamic-debug these days). Kill this noise. Reported-by: Dave Jones Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ieee802154/Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile index ce2d33582859d..5761185f884e8 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile @@ -1,5 +1,3 @@ obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o af_802154-y := af_ieee802154.o raw.o dgram.o - -ccflags-y += -Wall -DDEBUG From 90958a1a96c93aed6d2ae5d7401447cc1a4158f7 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Tue, 12 Apr 2011 15:29:54 -0700 Subject: [PATCH 1839/2556] irda: fix locking unbalance in irda_sendmsg [ Upstream commit 020318d0d2af51e0fd59ba654ede9b2171558720 ] 5b40964eadea40509d353318d2c82e8b7bf5e8a5 ("irda: Remove BKL instances from af_irda.c") introduced a path where we have a locking unbalance. If we pass invalid flags, we unlock a socket we never locked, resulting in this... ===================================== [ BUG: bad unlock balance detected! ] ------------------------------------- trinity/20101 is trying to release lock (sk_lock-AF_IRDA) at: [] irda_sendmsg+0x207/0x21d [irda] but there are no more locks to release! other info that might help us debug this: no locks held by trinity/20101. stack backtrace: Pid: 20101, comm: trinity Not tainted 2.6.39-rc3+ #3 Call Trace: [] ? irda_sendmsg+0x207/0x21d [irda] [] print_unlock_inbalance_bug+0xc7/0xd2 [] ? irda_sendmsg+0x207/0x21d [irda] [] lock_release+0xcf/0x18e [] release_sock+0x2d/0x155 [] irda_sendmsg+0x207/0x21d [irda] [] __sock_sendmsg+0x69/0x75 [] sock_sendmsg+0xa1/0xb6 [] ? might_fault+0x5c/0xac [] ? lock_release+0x181/0x18e [] ? might_fault+0xa5/0xac [] ? might_fault+0x5c/0xac [] ? fcheck_files+0xb9/0xf0 [] ? copy_from_user+0x2f/0x31 [] ? verify_iovec+0x52/0xa6 [] sys_sendmsg+0x23a/0x2b8 [] ? lock_release+0x181/0x18e [] ? up_read+0x28/0x2c [] ? do_page_fault+0x360/0x3b4 [] ? trace_hardirqs_on_caller+0x10b/0x12f [] ? finish_task_switch+0xb2/0xe3 [] ? finish_task_switch+0x46/0xe3 [] ? trace_hardirqs_off_caller+0x33/0x90 [] ? retint_swapgs+0x13/0x1b [] ? trace_hardirqs_on_caller+0x10b/0x12f [] ? audit_syscall_entry+0x11c/0x148 [] ? trace_hardirqs_on_thunk+0x3a/0x3f [] system_call_fastpath+0x16/0x1b Signed-off-by: Dave Jones Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/irda/af_irda.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c index c9890e25cd4c9..cc616974a447d 100644 --- a/net/irda/af_irda.c +++ b/net/irda/af_irda.c @@ -1297,8 +1297,7 @@ static int irda_sendmsg(struct kiocb *iocb, struct socket *sock, /* Note : socket.c set MSG_EOR on SEQPACKET sockets */ if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_EOR | MSG_CMSG_COMPAT | MSG_NOSIGNAL)) { - err = -EINVAL; - goto out; + return -EINVAL; } lock_sock(sk); From 73503196fb0362a35b9b05c0625f80805f793e29 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 11 Apr 2011 22:39:40 +0000 Subject: [PATCH 1840/2556] inetpeer: reduce stack usage [ Upstream commit 66944e1c5797562cebe2d1857d46dff60bf9a69e ] On 64bit arches, we use 752 bytes of stack when cleanup_once() is called from inet_getpeer(). Lets share the avl stack to save ~376 bytes. Before patch : # objdump -d net/ipv4/inetpeer.o | scripts/checkstack.pl 0x000006c3 unlink_from_pool [inetpeer.o]: 376 0x00000721 unlink_from_pool [inetpeer.o]: 376 0x00000cb1 inet_getpeer [inetpeer.o]: 376 0x00000e6d inet_getpeer [inetpeer.o]: 376 0x0004 inet_initpeers [inetpeer.o]: 112 # size net/ipv4/inetpeer.o text data bss dec hex filename 5320 432 21 5773 168d net/ipv4/inetpeer.o After patch : objdump -d net/ipv4/inetpeer.o | scripts/checkstack.pl 0x00000c11 inet_getpeer [inetpeer.o]: 376 0x00000dcd inet_getpeer [inetpeer.o]: 376 0x00000ab9 peer_check_expire [inetpeer.o]: 328 0x00000b7f peer_check_expire [inetpeer.o]: 328 0x0004 inet_initpeers [inetpeer.o]: 112 # size net/ipv4/inetpeer.o text data bss dec hex filename 5163 432 21 5616 15f0 net/ipv4/inetpeer.o Signed-off-by: Eric Dumazet Cc: Scot Doyle Cc: Stephen Hemminger Cc: Hiroaki SHIMODA Reviewed-by: Hiroaki SHIMODA Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/inetpeer.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index a96e65674ac3e..14af1b44121e4 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -366,7 +366,8 @@ static void inetpeer_free_rcu(struct rcu_head *head) } /* May be called with local BH enabled. */ -static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base) +static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base, + struct inet_peer __rcu **stack[PEER_MAXDEPTH]) { int do_free; @@ -380,7 +381,6 @@ static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base) * We use refcnt=-1 to alert lockless readers this entry is deleted. */ if (atomic_cmpxchg(&p->refcnt, 1, -1) == 1) { - struct inet_peer __rcu **stack[PEER_MAXDEPTH]; struct inet_peer __rcu ***stackptr, ***delp; if (lookup(&p->daddr, stack, base) != p) BUG(); @@ -435,7 +435,7 @@ static struct inet_peer_base *peer_to_base(struct inet_peer *p) } /* May be called with local BH enabled. */ -static int cleanup_once(unsigned long ttl) +static int cleanup_once(unsigned long ttl, struct inet_peer __rcu **stack[PEER_MAXDEPTH]) { struct inet_peer *p = NULL; @@ -467,7 +467,7 @@ static int cleanup_once(unsigned long ttl) * happen because of entry limits in route cache. */ return -1; - unlink_from_pool(p, peer_to_base(p)); + unlink_from_pool(p, peer_to_base(p), stack); return 0; } @@ -523,7 +523,7 @@ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create) if (base->total >= inet_peer_threshold) /* Remove one less-recently-used entry. */ - cleanup_once(0); + cleanup_once(0, stack); return p; } @@ -539,6 +539,7 @@ static void peer_check_expire(unsigned long dummy) { unsigned long now = jiffies; int ttl, total; + struct inet_peer __rcu **stack[PEER_MAXDEPTH]; total = compute_total(); if (total >= inet_peer_threshold) @@ -547,7 +548,7 @@ static void peer_check_expire(unsigned long dummy) ttl = inet_peer_maxttl - (inet_peer_maxttl - inet_peer_minttl) / HZ * total / inet_peer_threshold * HZ; - while (!cleanup_once(ttl)) { + while (!cleanup_once(ttl, stack)) { if (jiffies != now) break; } From 124035df30f4a57658d744d2720a92711355bd6b Mon Sep 17 00:00:00 2001 From: Thomas Egerer Date: Wed, 20 Apr 2011 22:56:02 +0000 Subject: [PATCH 1841/2556] ipv6: Remove hoplimit initialization to -1 [ Upstream commit e965c05dabdabb85af0187952ccd75e43995c4b3 ] The changes introduced with git-commit a02e4b7d ("ipv6: Demark default hoplimit as zero.") missed to remove the hoplimit initialization. As a result, ipv6_get_mtu interprets the return value of dst_metric_raw (-1) as 255 and answers ping6 with this hoplimit. This patche removes the line such that ping6 is answered with the hoplimit value configured via sysctl. Signed-off-by: Thomas Egerer Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/route.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index e7db7014e89f9..57c20dc41ccb4 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1970,7 +1970,6 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, rt->dst.output = ip6_output; rt->rt6i_dev = net->loopback_dev; rt->rt6i_idev = idev; - dst_metric_set(&rt->dst, RTAX_HOPLIMIT, -1); rt->dst.obsolete = -1; rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; From 039b2d693940cec4fec438a8731720141d755f79 Mon Sep 17 00:00:00 2001 From: Shan Wei Date: Tue, 19 Apr 2011 22:52:49 +0000 Subject: [PATCH 1842/2556] ipv6: udp: fix the wrong headroom check [ Upstream commit a9cf73ea7ff78f52662c8658d93c226effbbedde ] At this point, skb->data points to skb_transport_header. So, headroom check is wrong. For some case:bridge(UFO is on) + eth device(UFO is off), there is no enough headroom for IPv6 frag head. But headroom check is always false. This will bring about data be moved to there prior to skb->head, when adding IPv6 frag header to skb. Signed-off-by: Shan Wei Acked-by: Herbert Xu Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv6/udp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 9a009c66c8a3d..6703f8b949267 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1339,7 +1339,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, int features) skb->ip_summed = CHECKSUM_NONE; /* Check if there is enough headroom to insert fragment header. */ - if ((skb_headroom(skb) < frag_hdr_sz) && + if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) && pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC)) goto out; From 39d073d0ce6fb9c45bbaeb314100c92c0c350552 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 20 May 2011 14:59:23 -0400 Subject: [PATCH 1843/2556] macvlan: fix panic if lowerdev in a bond [ Upstream commit d93515611bbc70c2fe4db232e5feb448ed8e4cc9 ] commit a35e2c1b6d905 (macvlan: use rx_handler_data pointer to store macvlan_port pointer V2) added a bug in macvlan_port_create() Steps to reproduce the bug: # ifenslave bond0 eth0 eth1 # ip link add link eth0 up name eth0#1 type macvlan ->error EBUSY # ip link add link eth0 up name eth0#1 type macvlan ->panic Fix: Dont set IFF_MACVLAN_PORT in error case. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/macvlan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 6ed577b065df1..47fc7d1810bc2 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -586,8 +586,8 @@ static int macvlan_port_create(struct net_device *dev) err = netdev_rx_handler_register(dev, macvlan_handle_frame, port); if (err) kfree(port); - - dev->priv_flags |= IFF_MACVLAN_PORT; + else + dev->priv_flags |= IFF_MACVLAN_PORT; return err; } From 2e14c286d94922cd9f4bf3a2cb110ae0f24f9d85 Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Tue, 12 Apr 2011 13:59:33 -0700 Subject: [PATCH 1844/2556] net: Do not wrap sysctl igmp_max_memberships in IP_MULTICAST [ Upstream commit 192910a6cca5e50e5bd6cbd1da0e7376c7adfe62 ] controlling igmp_max_membership is useful even when IP_MULTICAST is off. Quagga(an OSPF deamon) uses multicast addresses for all interfaces using a single socket and hits igmp_max_membership limit when there are 20 interfaces or more. Always export sysctl igmp_max_memberships in proc, just like igmp_max_msf Signed-off-by: Joakim Tjernlund Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/sysctl_net_ipv4.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 1a456652086b7..321e6e84dbccb 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -311,7 +311,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_do_large_bitmap, }, -#ifdef CONFIG_IP_MULTICAST { .procname = "igmp_max_memberships", .data = &sysctl_igmp_max_memberships, @@ -319,8 +318,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - -#endif { .procname = "igmp_max_msf", .data = &sysctl_igmp_max_msf, From ad033b20a2cc45cf88e48646d21bab1afc7024c6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 17 May 2011 13:56:59 -0400 Subject: [PATCH 1845/2556] net: use hlist_del_rcu() in dev_change_name() [ Upstream commit 372b2312010bece1e36f577d6c99a6193ec54cbd ] Using plain hlist_del() in dev_change_name() is wrong since a concurrent reader can crash trying to dereference LIST_POISON1. Bug introduced in commit 72c9528bab94 (net: Introduce dev_get_by_name_rcu()) Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/core/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/dev.c b/net/core/dev.c index af0995df7114a..2bb4aa68466e5 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1006,7 +1006,7 @@ int dev_change_name(struct net_device *dev, const char *newname) } write_lock_bh(&dev_base_lock); - hlist_del(&dev->name_hlist); + hlist_del_rcu(&dev->name_hlist); write_unlock_bh(&dev_base_lock); synchronize_rcu(); From e165f370fe28e09a5a1ad1acc250f8b5b7f72c22 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 21 Apr 2011 21:17:25 -0700 Subject: [PATCH 1846/2556] Revert "bridge: Forward reserved group addresses if !STP" [ Upstream commit f01cb5fbea1c1613621f9f32f385e12c1a29dde0 ] This reverts commit 1e253c3b8a1aeed51eef6fc366812f219b97de65. It breaks 802.3ad bonding inside of a bridge. The commit was meant to support transport bridging, and specifically virtual machines bridged to an ethernet interface connected to a switch port wiht 802.1x enabled. But this isn't the way to do it, it breaks too many other things. Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/bridge/br_input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 88e4aa9cb1f9a..90e985b99f31f 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -163,7 +163,7 @@ struct sk_buff *br_handle_frame(struct sk_buff *skb) goto drop; /* If STP is turned off, then forward */ - if (p->br->stp_enabled == BR_NO_STP) + if (p->br->stp_enabled == BR_NO_STP && dest[5] == 0) goto forward; if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev, From b89f9a09353e9382646babf92e6fe5e9068661de Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 13 Apr 2011 12:01:14 -0700 Subject: [PATCH 1847/2556] Revert "tcp: disallow bind() to reuse addr/port" [ Upstream commit 3e8c806a08c7beecd972e7ce15c570b9aba64baa ] This reverts commit c191a836a908d1dd6b40c503741f91b914de3348. It causes known regressions for programs that expect to be able to use SO_REUSEADDR to shutdown a socket, then successfully rebind another socket to the same ID. Programs such as haproxy and amavisd expect this to work. This should fix kernel bugzilla 32832. Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/inet_connection_sock.c | 5 ++--- net/ipv6/inet6_connection_sock.c | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 97e5fb7652650..25e318153f143 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -73,7 +73,7 @@ int inet_csk_bind_conflict(const struct sock *sk, !sk2->sk_bound_dev_if || sk->sk_bound_dev_if == sk2->sk_bound_dev_if)) { if (!reuse || !sk2->sk_reuse || - ((1 << sk2->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) { + sk2->sk_state == TCP_LISTEN) { const __be32 sk2_rcv_saddr = sk_rcv_saddr(sk2); if (!sk2_rcv_saddr || !sk_rcv_saddr(sk) || sk2_rcv_saddr == sk_rcv_saddr(sk)) @@ -122,8 +122,7 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum) (tb->num_owners < smallest_size || smallest_size == -1)) { smallest_size = tb->num_owners; smallest_rover = rover; - if (atomic_read(&hashinfo->bsockets) > (high - low) + 1 && - !inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb)) { + if (atomic_read(&hashinfo->bsockets) > (high - low) + 1) { spin_unlock(&head->lock); snum = smallest_rover; goto have_snum; diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index d144e629d2b43..e46305d1815ae 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -44,7 +44,7 @@ int inet6_csk_bind_conflict(const struct sock *sk, !sk2->sk_bound_dev_if || sk->sk_bound_dev_if == sk2->sk_bound_dev_if) && (!sk->sk_reuse || !sk2->sk_reuse || - ((1 << sk2->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) && + sk2->sk_state == TCP_LISTEN) && ipv6_rcv_saddr_equal(sk, sk2)) break; } From 29ad44a2c7365a4094772be7bbc81aff279295fc Mon Sep 17 00:00:00 2001 From: Jacek Luczak Date: Thu, 19 May 2011 09:55:13 +0000 Subject: [PATCH 1848/2556] SCTP: fix race between sctp_bind_addr_free() and sctp_bind_addr_conflict() [ Upstream commit c182f90bc1f22ce5039b8722e45621d5f96862c2 ] During the sctp_close() call, we do not use rcu primitives to destroy the address list attached to the endpoint. At the same time, we do the removal of addresses from this list before attempting to remove the socket from the port hash As a result, it is possible for another process to find the socket in the port hash that is in the process of being closed. It then proceeds to traverse the address list to find the conflict, only to have that address list suddenly disappear without rcu() critical section. Fix issue by closing address list removal inside RCU critical section. Race can result in a kernel crash with general protection fault or kernel NULL pointer dereference: kernel: general protection fault: 0000 [#1] SMP kernel: RIP: 0010:[] [] sctp_bind_addr_conflict+0x64/0x82 [sctp] kernel: Call Trace: kernel: [] ? sctp_get_port_local+0x17b/0x2a3 [sctp] kernel: [] ? sctp_bind_addr_match+0x33/0x68 [sctp] kernel: [] ? sctp_do_bind+0xd3/0x141 [sctp] kernel: [] ? sctp_bindx_add+0x4d/0x8e [sctp] kernel: [] ? sctp_setsockopt_bindx+0x112/0x4a4 [sctp] kernel: [] ? generic_file_aio_write+0x7f/0x9b kernel: [] ? sctp_setsockopt+0x14f/0xfee [sctp] kernel: [] ? do_sync_write+0xab/0xeb kernel: [] ? fsnotify+0x239/0x282 kernel: [] ? alloc_file+0x18/0xb1 kernel: [] ? compat_sys_setsockopt+0x1a5/0x1d9 kernel: [] ? compat_sys_socketcall+0x143/0x1a4 kernel: [] ? sysenter_dispatch+0x7/0x32 Signed-off-by: Jacek Luczak Acked-by: Vlad Yasevich CC: Eric Dumazet Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sctp/bind_addr.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index faf71d179e464..6150ac5cf5ddf 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -140,14 +140,12 @@ void sctp_bind_addr_init(struct sctp_bind_addr *bp, __u16 port) /* Dispose of the address list. */ static void sctp_bind_addr_clean(struct sctp_bind_addr *bp) { - struct sctp_sockaddr_entry *addr; - struct list_head *pos, *temp; + struct sctp_sockaddr_entry *addr, *temp; /* Empty the bind address list. */ - list_for_each_safe(pos, temp, &bp->address_list) { - addr = list_entry(pos, struct sctp_sockaddr_entry, list); - list_del(pos); - kfree(addr); + list_for_each_entry_safe(addr, temp, &bp->address_list, list) { + list_del_rcu(&addr->list); + call_rcu(&addr->rcu, sctp_local_addr_free); SCTP_DBG_OBJCNT_DEC(addr); } } From 21fdc47e2e3238412559703663e5a04cbebb4309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Fri, 1 Apr 2011 21:47:41 -0700 Subject: [PATCH 1849/2556] tcp: len check is unnecessarily devastating, change to WARN_ON MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 2fceec13375e5d98ef033c6b0ee03943fc460950 ] All callers are prepared for alloc failures anyway, so this error can safely be boomeranged to the callers domain without super bad consequences. ...At worst the connection might go into a state where each RTO tries to (unsuccessfully) re-fragment with such a mis-sized value and eventually dies. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/tcp_output.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index dfa5beb0c1c8c..8b0d0167e44aa 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1003,7 +1003,8 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, int nlen; u8 flags; - BUG_ON(len > skb->len); + if (WARN_ON(len > skb->len)) + return -EINVAL; nsize = skb_headlen(skb) - len; if (nsize < 0) From b404cdd3aaa697187fcac36157aed7e4a596a8ef Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 10 May 2011 12:22:54 -0700 Subject: [PATCH 1850/2556] vlan: fix GVRP at dismantle time MIME-Version: 1.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 0442277740ec56109c5b5f7bcfded299cf9e72bd ] ip link add link eth2 eth2.103 type vlan id 103 gvrp on loose_binding on ip link set eth2.103 up rmmod tg3 # driver providing eth2 BUG: unable to handle kernel NULL pointer dereference at (null) IP: [] garp_request_leave+0x3e/0xc0 [garp] PGD 11d251067 PUD 11b9e0067 PMD 0 Oops: 0000 [#1] SMP last sysfs file: /sys/devices/virtual/net/eth2.104/ifindex CPU 0 Modules linked in: tg3(-) 8021q garp nfsd lockd auth_rpcgss sunrpc libphy sg [last unloaded: x_tables] Pid: 11494, comm: rmmod Tainted: G W 2.6.39-rc6-00261-gfd71257-dirty #580 HP ProLiant BL460c G6 RIP: 0010:[] [] garp_request_leave+0x3e/0xc0 [garp] RSP: 0018:ffff88007a19bae8 EFLAGS: 00010286 RAX: 0000000000000000 RBX: ffff88011b5e2000 RCX: 0000000000000002 RDX: 0000000000000000 RSI: 0000000000000175 RDI: ffffffffa0030d5b RBP: ffff88007a19bb18 R08: 0000000000000001 R09: ffff88011bd64a00 R10: ffff88011d34ec00 R11: 0000000000000000 R12: 0000000000000002 R13: ffff88007a19bc48 R14: ffff88007a19bb88 R15: 0000000000000001 FS: 0000000000000000(0000) GS:ffff88011fc00000(0063) knlGS:00000000f77d76c0 CS: 0010 DS: 002b ES: 002b CR0: 000000008005003b CR2: 0000000000000000 CR3: 000000011a675000 CR4: 00000000000006f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process rmmod (pid: 11494, threadinfo ffff88007a19a000, task ffff8800798595c0) Stack: ffff88007a19bb36 ffff88011c84b800 ffff88011b5e2000 ffff88007a19bc48 ffff88007a19bb88 0000000000000006 ffff88007a19bb38 ffffffffa003a5f6 ffff88007a19bb38 670088007a19bba8 ffff88007a19bb58 ffffffffa00397e7 Call Trace: [] vlan_gvrp_request_leave+0x46/0x50 [8021q] [] vlan_dev_stop+0xb7/0xc0 [8021q] [] __dev_close_many+0x87/0xe0 [] dev_close_many+0x87/0x110 [] rollback_registered_many+0xa0/0x240 [] unregister_netdevice_many+0x19/0x60 [] vlan_device_event+0x53b/0x550 [8021q] [] ? ip6mr_device_event+0xa8/0xd0 [] notifier_call_chain+0x53/0x80 [] __raw_notifier_call_chain+0x9/0x10 [] raw_notifier_call_chain+0x11/0x20 [] call_netdevice_notifiers+0x32/0x60 [] rollback_registered_many+0x10f/0x240 [] rollback_registered+0x2f/0x40 [] unregister_netdevice_queue+0x58/0x90 [] unregister_netdev+0x1b/0x30 [] tg3_remove_one+0x6f/0x10b [tg3] We should call vlan_gvrp_request_leave() from unregister_vlan_dev(), not from vlan_dev_stop(), because vlan_gvrp_uninit_applicant() is called right after unregister_netdevice_queue(). In batch mode, unregister_netdevice_queue() doesn’t immediately call vlan_dev_stop(). Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/8021q/vlan.c | 3 +++ net/8021q/vlan_dev.c | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 6e64f7c6a2e92..8a7de0f13e673 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -124,6 +124,9 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) grp->nr_vlans--; + if (vlan->flags & VLAN_FLAG_GVRP) + vlan_gvrp_request_leave(dev); + vlan_group_set_device(grp, vlan_id, NULL); if (!grp->killall) synchronize_net(); diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index ed68d0722e531..a330b9e83d0b8 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -487,9 +487,6 @@ static int vlan_dev_stop(struct net_device *dev) struct vlan_dev_info *vlan = vlan_dev_info(dev); struct net_device *real_dev = vlan->real_dev; - if (vlan->flags & VLAN_FLAG_GVRP) - vlan_gvrp_request_leave(dev); - dev_mc_unsync(real_dev, dev); dev_uc_unsync(real_dev, dev); if (dev->flags & IFF_ALLMULTI) From 7c4acccbb07cddc422a1283f8569e48613f328c2 Mon Sep 17 00:00:00 2001 From: Veaceslav Falico Date: Mon, 23 May 2011 23:15:05 +0000 Subject: [PATCH 1851/2556] igmp: call ip_mc_clear_src() only when we have no users of ip_mc_list [ Upstream commit 24cf3af3fed5edcf90bc2a0ed181e6ce1513d2dc ] In igmp_group_dropped() we call ip_mc_clear_src(), which resets the number of source filters per mulitcast. However, igmp_group_dropped() is also called on NETDEV_DOWN, NETDEV_PRE_TYPE_CHANGE and NETDEV_UNREGISTER, which means that the group might get added back on NETDEV_UP, NETDEV_REGISTER and NETDEV_POST_TYPE_CHANGE respectively, leaving us with broken source filters. To fix that, we must clear the source filters only when there are no users in the ip_mc_list, i.e. in ip_mc_dec_group() and on device destroy. Acked-by: David L Stevens Signed-off-by: Veaceslav Falico Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/ipv4/igmp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index e0e77e297de32..d9d5130a91227 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1172,20 +1172,18 @@ static void igmp_group_dropped(struct ip_mc_list *im) if (!in_dev->dead) { if (IGMP_V1_SEEN(in_dev)) - goto done; + return; if (IGMP_V2_SEEN(in_dev)) { if (reporter) igmp_send_report(in_dev, im, IGMP_HOST_LEAVE_MESSAGE); - goto done; + return; } /* IGMPv3 */ igmpv3_add_delrec(in_dev, im); igmp_ifc_event(in_dev); } -done: #endif - ip_mc_clear_src(im); } static void igmp_group_added(struct ip_mc_list *im) @@ -1322,6 +1320,7 @@ void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) *ip = i->next_rcu; in_dev->mc_count--; igmp_group_dropped(i); + ip_mc_clear_src(i); if (!in_dev->dead) ip_rt_multicast_event(in_dev); @@ -1431,7 +1430,8 @@ void ip_mc_destroy_dev(struct in_device *in_dev) in_dev->mc_list = i->next_rcu; in_dev->mc_count--; - igmp_group_dropped(i); + /* We've dropped the groups in ip_mc_down already */ + ip_mc_clear_src(i); ip_ma_put(i); } } From 6c063e6ac38b809e6039ebe59d50d28d1db502c6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 18 May 2011 02:21:31 -0400 Subject: [PATCH 1852/2556] net: add skb_dst_force() in sock_queue_err_skb() [ Upstream commit abb57ea48fd9431fa320a5c55f73e6b5a44c2efb ] Commit 7fee226ad239 (add a noref bit on skb dst) forgot to use skb_dst_force() on packets queued in sk_error_queue This triggers following warning, for applications using IP_CMSG_PKTINFO receiving one error status ------------[ cut here ]------------ WARNING: at include/linux/skbuff.h:457 ip_cmsg_recv_pktinfo+0xa6/0xb0() Hardware name: 2669UYD Modules linked in: isofs vboxnetadp vboxnetflt nfsd ebtable_nat ebtables lib80211_crypt_ccmp uinput xcbc hdaps tp_smapi thinkpad_ec radeonfb fb_ddc radeon ttm drm_kms_helper drm ipw2200 intel_agp intel_gtt libipw i2c_algo_bit i2c_i801 agpgart rng_core cfbfillrect cfbcopyarea cfbimgblt video raid10 raid1 raid0 linear md_mod vboxdrv Pid: 4697, comm: miredo Not tainted 2.6.39-rc6-00569-g5895198-dirty #22 Call Trace: [] ? printk+0x1d/0x1f [] warn_slowpath_common+0x72/0xa0 [] ? ip_cmsg_recv_pktinfo+0xa6/0xb0 [] ? ip_cmsg_recv_pktinfo+0xa6/0xb0 [] warn_slowpath_null+0x20/0x30 [] ip_cmsg_recv_pktinfo+0xa6/0xb0 [] ip_cmsg_recv+0x127/0x260 [] ? skb_dequeue+0x4d/0x70 [] ? skb_copy_datagram_iovec+0x53/0x300 [] ? sub_preempt_count+0x24/0x50 [] ip_recv_error+0x23d/0x270 [] udp_recvmsg+0x264/0x2b0 [] inet_recvmsg+0xd9/0x130 [] sock_recvmsg+0xf2/0x120 [] ? might_fault+0x4b/0xa0 [] ? verify_iovec+0x4c/0xc0 [] ? sock_recvmsg_nosec+0x100/0x100 [] __sys_recvmsg+0x114/0x1e0 [] ? __lock_acquire+0x365/0x780 [] ? fget_light+0xa6/0x3e0 [] ? fget_light+0xbf/0x3e0 [] ? fget_light+0x2e/0x3e0 [] sys_recvmsg+0x39/0x60 Close bug https://bugzilla.kernel.org/show_bug.cgi?id=34622 Reported-by: Witold Baryluk Signed-off-by: Eric Dumazet CC: Stephen Hemminger Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/core/skbuff.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index d883dcc78b6b6..e9f924898e6b0 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2997,6 +2997,9 @@ int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb) skb->destructor = sock_rmem_free; atomic_add(skb->truesize, &sk->sk_rmem_alloc); + /* before exiting rcu section, make sure dst is refcounted */ + skb_dst_force(skb); + skb_queue_tail(&sk->sk_error_queue, skb); if (!sock_flag(sk, SOCK_DEAD)) sk->sk_data_ready(sk, skb->len); From 1003a81b92683e72019b8160ac59c7a2651a74e5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 23 May 2011 11:02:42 +0000 Subject: [PATCH 1853/2556] sch_sfq: avoid giving spurious NET_XMIT_CN signals [ Upstream commit 8efa885406359af300d46910642b50ca82c0fe47 ] While chasing a possible net_sched bug, I found that IP fragments have litle chance to pass a congestioned SFQ qdisc : - Say SFQ qdisc is full because one flow is non responsive. - ip_fragment() wants to send two fragments belonging to an idle flow. - sfq_enqueue() queues first packet, but see queue limit reached : - sfq_enqueue() drops one packet from 'big consumer', and returns NET_XMIT_CN. - ip_fragment() cancel remaining fragments. This patch restores fairness, making sure we return NET_XMIT_CN only if we dropped a packet from the same flow. Signed-off-by: Eric Dumazet CC: Patrick McHardy CC: Jarek Poplawski CC: Jamal Hadi Salim CC: Stephen Hemminger Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sched/sch_sfq.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index edea8cefec6c9..17289d7068684 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -361,7 +361,7 @@ sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) { struct sfq_sched_data *q = qdisc_priv(sch); unsigned int hash; - sfq_index x; + sfq_index x, qlen; struct sfq_slot *slot; int uninitialized_var(ret); @@ -405,8 +405,12 @@ sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) if (++sch->q.qlen <= q->limit) return NET_XMIT_SUCCESS; + qlen = slot->qlen; sfq_drop(sch); - return NET_XMIT_CN; + /* Return Congestion Notification only if we dropped a packet + * from this flow. + */ + return (qlen != slot->qlen) ? NET_XMIT_CN : NET_XMIT_SUCCESS; } static struct sk_buff * From 696789e6e980e3f11860a34a57b4724d041f02ab Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 24 May 2011 21:48:02 +0000 Subject: [PATCH 1854/2556] sctp: fix memory leak of the ASCONF queue when free asoc [ Upstream commit 8b4472cc13136d04727e399c6fdadf58d2218b0a ] If an ASCONF chunk is outstanding, then the following ASCONF chunk will be queued for later transmission. But when we free the asoc, we forget to free the ASCONF queue at the same time, this will cause memory leak. Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sctp/associola.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 5f1fb8bd862de..490f003da84d8 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -64,6 +64,7 @@ /* Forward declarations for internal functions. */ static void sctp_assoc_bh_rcv(struct work_struct *work); static void sctp_assoc_free_asconf_acks(struct sctp_association *asoc); +static void sctp_assoc_free_asconf_queue(struct sctp_association *asoc); /* Keep track of the new idr low so that we don't re-use association id * numbers too fast. It is protected by they idr spin lock is in the @@ -446,6 +447,9 @@ void sctp_association_free(struct sctp_association *asoc) /* Free any cached ASCONF_ACK chunk. */ sctp_assoc_free_asconf_acks(asoc); + /* Free the ASCONF queue. */ + sctp_assoc_free_asconf_queue(asoc); + /* Free any cached ASCONF chunk. */ if (asoc->addip_last_asconf) sctp_chunk_free(asoc->addip_last_asconf); @@ -1576,6 +1580,18 @@ int sctp_assoc_set_id(struct sctp_association *asoc, gfp_t gfp) return error; } +/* Free the ASCONF queue */ +static void sctp_assoc_free_asconf_queue(struct sctp_association *asoc) +{ + struct sctp_chunk *asconf; + struct sctp_chunk *tmp; + + list_for_each_entry_safe(asconf, tmp, &asoc->addip_chunk_list, list) { + list_del_init(&asconf->list); + sctp_chunk_free(asconf); + } +} + /* Free asconf_ack cache */ static void sctp_assoc_free_asconf_acks(struct sctp_association *asoc) { From 53280ba65f9fad14cf52bfb3c5ffae09a8c2ea51 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 25 May 2011 04:40:11 +0000 Subject: [PATCH 1855/2556] sch_sfq: fix peek() implementation [ Upstream commit 07bd8df5df4369487812bf85a237322ff3569b77 ] Since commit eeaeb068f139 (sch_sfq: allow big packets and be fair), sfq_peek() can return a different skb that would be normally dequeued by sfq_dequeue() [ if current slot->allot is negative ] Use generic qdisc_peek_dequeued() instead of custom implementation, to get consistent result. Signed-off-by: Eric Dumazet CC: Jarek Poplawski CC: Patrick McHardy CC: Jesper Dangaard Brouer Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/sched/sch_sfq.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 17289d7068684..e852bb1f64296 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -413,18 +413,6 @@ sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) return (qlen != slot->qlen) ? NET_XMIT_CN : NET_XMIT_SUCCESS; } -static struct sk_buff * -sfq_peek(struct Qdisc *sch) -{ - struct sfq_sched_data *q = qdisc_priv(sch); - - /* No active slots */ - if (q->tail == NULL) - return NULL; - - return q->slots[q->tail->next].skblist_next; -} - static struct sk_buff * sfq_dequeue(struct Qdisc *sch) { @@ -683,7 +671,7 @@ static struct Qdisc_ops sfq_qdisc_ops __read_mostly = { .priv_size = sizeof(struct sfq_sched_data), .enqueue = sfq_enqueue, .dequeue = sfq_dequeue, - .peek = sfq_peek, + .peek = qdisc_peek_dequeued, .drop = sfq_drop, .init = sfq_init, .reset = sfq_reset, From 63771f4d72eb7ec242795fa6904fb7b48d955136 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Wed, 25 May 2011 08:13:01 +0000 Subject: [PATCH 1856/2556] bonding: prevent deadlock on slave store with alb mode (v3) [ Upstream commit 9fe0617d9b6d21f700ee9e658e1c9fe3be2fb402 ] This soft lockup was recently reported: [root@dell-per715-01 ~]# echo +bond5 > /sys/class/net/bonding_masters [root@dell-per715-01 ~]# echo +eth1 > /sys/class/net/bond5/bonding/slaves bonding: bond5: doing slave updates when interface is down. bonding bond5: master_dev is not up in bond_enslave [root@dell-per715-01 ~]# echo -eth1 > /sys/class/net/bond5/bonding/slaves bonding: bond5: doing slave updates when interface is down. BUG: soft lockup - CPU#12 stuck for 60s! [bash:6444] CPU 12: Modules linked in: bonding autofs4 hidp rfcomm l2cap bluetooth lockd sunrpc be2d Pid: 6444, comm: bash Not tainted 2.6.18-262.el5 #1 RIP: 0010:[] [] .text.lock.spinlock+0x26/00 RSP: 0018:ffff810113167da8 EFLAGS: 00000286 RAX: ffff810113167fd8 RBX: ffff810123a47800 RCX: 0000000000ff1025 RDX: 0000000000000000 RSI: ffff810123a47800 RDI: ffff81021b57f6f8 RBP: ffff81021b57f500 R08: 0000000000000000 R09: 000000000000000c R10: 00000000ffffffff R11: ffff81011d41c000 R12: ffff81021b57f000 R13: 0000000000000000 R14: 0000000000000282 R15: 0000000000000282 FS: 00002b3b41ef3f50(0000) GS:ffff810123b27940(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 00002b3b456dd000 CR3: 000000031fc60000 CR4: 00000000000006e0 Call Trace: [] _spin_lock_bh+0x9/0x14 [] :bonding:tlb_clear_slave+0x22/0xa1 [] :bonding:bond_alb_deinit_slave+0xba/0xf0 [] :bonding:bond_release+0x1b4/0x450 [] __down_write_nested+0x12/0x92 [] :bonding:bonding_store_slaves+0x25c/0x2f7 [] sysfs_write_file+0xb9/0xe8 [] vfs_write+0xce/0x174 [] sys_write+0x45/0x6e [] tracesys+0xd5/0xe0 It occurs because we are able to change the slave configuarion of a bond while the bond interface is down. The bonding driver initializes some data structures only after its ndo_open routine is called. Among them is the initalization of the alb tx and rx hash locks. So if we add or remove a slave without first opening the bond master device, we run the risk of trying to lock/unlock a spinlock that has garbage for data in it, which results in our above softlock. Note that sometimes this works, because in many cases an unlocked spinlock has the raw_lock parameter initialized to zero (meaning that the kzalloc of the net_device private data is equivalent to calling spin_lock_init), but thats not true in all cases, and we aren't guaranteed that condition, so we need to pass the relevant spinlocks through the spin_lock_init function. Fix it by moving the spin_lock_init calls for the tx and rx hashtable locks to the ndo_init path, so they are ready for use by the bond_store_slaves path. Change notes: v2) Based on conversation with Jay and Nicolas it seems that the ability to enslave devices while the bond master is down should be safe to do. As such this is an outlier bug, and so instead we'll just initalize the errant spinlocks in the init path rather than the open path, solving the problem. We'll also remove the warnings about the bond being down during enslave operations, since it should be safe v3) Fix spelling error Signed-off-by: Neil Horman Reported-by: jtluka@redhat.com CC: Jay Vosburgh CC: Andy Gospodarek CC: nicolas.2p.debian@gmail.com CC: "David S. Miller" Signed-off-by: Jay Vosburgh Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/bonding/bond_alb.c | 4 ---- drivers/net/bonding/bond_main.c | 16 ++++++++++------ drivers/net/bonding/bond_sysfs.c | 6 ------ 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 5c6fba802f2b7..11ebd8f353caa 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -163,8 +163,6 @@ static int tlb_initialize(struct bonding *bond) struct tlb_client_info *new_hashtbl; int i; - spin_lock_init(&(bond_info->tx_hashtbl_lock)); - new_hashtbl = kzalloc(size, GFP_KERNEL); if (!new_hashtbl) { pr_err("%s: Error: Failed to allocate TLB hash table\n", @@ -764,8 +762,6 @@ static int rlb_initialize(struct bonding *bond) int size = RLB_HASH_TABLE_SIZE * sizeof(struct rlb_client_info); int i; - spin_lock_init(&(bond_info->rx_hashtbl_lock)); - new_hashtbl = kmalloc(size, GFP_KERNEL); if (!new_hashtbl) { pr_err("%s: Error: Failed to allocate RLB hash table\n", diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 163e0b06eaa5d..ac8dce5545a6a 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1441,12 +1441,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) bond_dev->name, slave_dev->name); } - /* bond must be initialized by bond_open() before enslaving */ - if (!(bond_dev->flags & IFF_UP)) { - pr_warning("%s: master_dev is not up in bond_enslave\n", - bond_dev->name); - } - /* already enslaved */ if (slave_dev->flags & IFF_SLAVE) { pr_debug("Error, Device was already enslaved\n"); @@ -5157,9 +5151,19 @@ static int bond_init(struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); struct bond_net *bn = net_generic(dev_net(bond_dev), bond_net_id); + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); pr_debug("Begin bond_init for %s\n", bond_dev->name); + /* + * Initialize locks that may be required during + * en/deslave operations. All of the bond_open work + * (of which this is part) should really be moved to + * a phase prior to dev_open + */ + spin_lock_init(&(bond_info->tx_hashtbl_lock)); + spin_lock_init(&(bond_info->rx_hashtbl_lock)); + bond->wq = create_singlethread_workqueue(bond_dev->name); if (!bond->wq) return -ENOMEM; diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index 8fd0174c53804..ddc316500fa8a 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -224,12 +224,6 @@ static ssize_t bonding_store_slaves(struct device *d, struct net_device *dev; struct bonding *bond = to_bond(d); - /* Quick sanity check -- is the bond interface up? */ - if (!(bond->dev->flags & IFF_UP)) { - pr_warning("%s: doing slave updates when interface is down.\n", - bond->dev->name); - } - if (!rtnl_trylock()) return restart_syscall(); From 04a7e30aa079cdc2249dbc091060137bb496c4e6 Mon Sep 17 00:00:00 2001 From: "Kashyap, Desai" Date: Wed, 4 May 2011 16:35:58 +0530 Subject: [PATCH 1857/2556] mpt2sas: move even handling of MPT2SAS_TURN_ON_FAULT_LED into process context commit 3ace8e052be5293ebb3e00f819effccc64108a38 upstream. Driver was a sending a SEP request during interrupt context which required to go to sleep. The fix is to rearrange the code so a fake event MPT2SAS_TURN_ON_FAULT_LED is fired from interrupt context, then later during the kernel worker threads processing, the SEP request is issued to firmware. Signed-off-by: Kashyap Desai Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/mpt2sas/mpt2sas_scsih.c | 94 ++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 26 deletions(-) diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index 5ded3db6e316f..20bcd1ec14cae 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -113,6 +113,7 @@ struct sense_info { }; +#define MPT2SAS_TURN_ON_FAULT_LED (0xFFFC) #define MPT2SAS_RESCAN_AFTER_HOST_RESET (0xFFFF) /** @@ -121,6 +122,7 @@ struct sense_info { * @work: work object (ioc->fault_reset_work_q) * @cancel_pending_work: flag set during reset handling * @ioc: per adapter object + * @device_handle: device handle * @VF_ID: virtual function id * @VP_ID: virtual port id * @ignore: flag meaning this event has been marked to ignore @@ -134,6 +136,7 @@ struct fw_event_work { u8 cancel_pending_work; struct delayed_work delayed_work; struct MPT2SAS_ADAPTER *ioc; + u16 device_handle; u8 VF_ID; u8 VP_ID; u8 ignore; @@ -3708,17 +3711,75 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, #endif /** - * _scsih_smart_predicted_fault - illuminate Fault LED + * _scsih_turn_on_fault_led - illuminate Fault LED * @ioc: per adapter object * @handle: device handle + * Context: process * * Return nothing. */ static void -_scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) +_scsih_turn_on_fault_led(struct MPT2SAS_ADAPTER *ioc, u16 handle) { Mpi2SepReply_t mpi_reply; Mpi2SepRequest_t mpi_request; + + memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t)); + mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR; + mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS; + mpi_request.SlotStatus = + cpu_to_le32(MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT); + mpi_request.DevHandle = cpu_to_le16(handle); + mpi_request.Flags = MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS; + if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply, + &mpi_request)) != 0) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, + __FILE__, __LINE__, __func__); + return; + } + + if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "enclosure_processor: " + "ioc_status (0x%04x), loginfo(0x%08x)\n", ioc->name, + le16_to_cpu(mpi_reply.IOCStatus), + le32_to_cpu(mpi_reply.IOCLogInfo))); + return; + } +} + +/** + * _scsih_send_event_to_turn_on_fault_led - fire delayed event + * @ioc: per adapter object + * @handle: device handle + * Context: interrupt. + * + * Return nothing. + */ +static void +_scsih_send_event_to_turn_on_fault_led(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct fw_event_work *fw_event; + + fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); + if (!fw_event) + return; + fw_event->event = MPT2SAS_TURN_ON_FAULT_LED; + fw_event->device_handle = handle; + fw_event->ioc = ioc; + _scsih_fw_event_add(ioc, fw_event); +} + +/** + * _scsih_smart_predicted_fault - process smart errors + * @ioc: per adapter object + * @handle: device handle + * Context: interrupt. + * + * Return nothing. + */ +static void +_scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ struct scsi_target *starget; struct MPT2SAS_TARGET *sas_target_priv_data; Mpi2EventNotificationReply_t *event_reply; @@ -3745,30 +3806,8 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) starget_printk(KERN_WARNING, starget, "predicted fault\n"); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) { - memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t)); - mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR; - mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS; - mpi_request.SlotStatus = - cpu_to_le32(MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT); - mpi_request.DevHandle = cpu_to_le16(handle); - mpi_request.Flags = MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS; - if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply, - &mpi_request)) != 0) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - return; - } - - if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "enclosure_processor: ioc_status (0x%04x), " - "loginfo(0x%08x)\n", ioc->name, - le16_to_cpu(mpi_reply.IOCStatus), - le32_to_cpu(mpi_reply.IOCLogInfo))); - return; - } - } + if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) + _scsih_send_event_to_turn_on_fault_led(ioc, handle); /* insert into event log */ sz = offsetof(Mpi2EventNotificationReply_t, EventData) + @@ -6330,6 +6369,9 @@ _firmware_event_work(struct work_struct *work) } switch (fw_event->event) { + case MPT2SAS_TURN_ON_FAULT_LED: + _scsih_turn_on_fault_led(ioc, fw_event->device_handle); + break; case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: _scsih_sas_topology_change_event(ioc, fw_event); break; From 88c8999a00d5b407dcd7df8734c345c64564275c Mon Sep 17 00:00:00 2001 From: Eddie Wai Date: Mon, 16 May 2011 11:13:18 -0700 Subject: [PATCH 1858/2556] bnx2i: Fixed packet error created when the sq_size is set to 16 commit 7287c63e986fe1a51a89f4bb1327320274a7a741 upstream. The number of chip's internal command cell, which is use to generate SCSI cmd packets to the target, was not initialized correctly by the driver when the sq_size is changed from the default 128. This, in turn, will create a problem where the chip's transmit pipe will erroneously reuse an old command cell that is no longer valid. The fix is to correctly initialize the chip's command cell upon setup. Signed-off-by: Eddie Wai Reviewed-by: Mike Christie Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/bnx2i/bnx2i_hwi.c | 1 + drivers/scsi/bnx2i/bnx2i_iscsi.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c index 96505e3ab9861..34f28fea22284 100644 --- a/drivers/scsi/bnx2i/bnx2i_hwi.c +++ b/drivers/scsi/bnx2i/bnx2i_hwi.c @@ -1221,6 +1221,7 @@ int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba) iscsi_init.dummy_buffer_addr_hi = (u32) ((u64) hba->dummy_buf_dma >> 32); + hba->num_ccell = hba->max_sqes >> 1; hba->ctx_ccell_tasks = ((hba->num_ccell & 0xFFFF) | (hba->max_sqes << 16)); iscsi_init.num_ccells_per_conn = hba->num_ccell; diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c index f0dce26593eb9..204d9629600d9 100644 --- a/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -1205,6 +1205,9 @@ static int bnx2i_task_xmit(struct iscsi_task *task) struct bnx2i_cmd *cmd = task->dd_data; struct iscsi_cmd *hdr = (struct iscsi_cmd *) task->hdr; + if (bnx2i_conn->ep->num_active_cmds + 1 > hba->max_sqes) + return -ENOMEM; + /* * If there is no scsi_cmnd this must be a mgmt task */ From 75faeb1a62cf83c7661c4fbfb5c56f44d1029091 Mon Sep 17 00:00:00 2001 From: Eddie Wai Date: Mon, 16 May 2011 11:13:19 -0700 Subject: [PATCH 1859/2556] bnx2i: Updated the connection shutdown/cleanup timeout commit d5307a078bb0288945c900c6f4a2fd77ba6d0817 upstream. Modified the 10s wait time for inflight offload connections to advance to the next state to 2s based on test result. Modified the 20s shutdown timeout to 30s based on test result. Signed-off-by: Eddie Wai Reviewed-by: Mike Christie Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/bnx2i/bnx2i_init.c | 2 +- drivers/scsi/bnx2i/bnx2i_iscsi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/bnx2i/bnx2i_init.c b/drivers/scsi/bnx2i/bnx2i_init.c index 72a7b2d4a439e..dd4622eca5639 100644 --- a/drivers/scsi/bnx2i/bnx2i_init.c +++ b/drivers/scsi/bnx2i/bnx2i_init.c @@ -227,7 +227,7 @@ void bnx2i_stop(void *handle) wait_event_interruptible_timeout(hba->eh_wait, (list_empty(&hba->ep_ofld_list) && list_empty(&hba->ep_destroy_list)), - 10 * HZ); + 2 * HZ); /* Wait for all endpoints to be torn down, Chip will be reset once * control returns to network driver. So it is required to cleanup and * release all connection resources before returning from this routine. diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c index 204d9629600d9..0a46832f55c2c 100644 --- a/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -858,7 +858,7 @@ struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic) mutex_init(&hba->net_dev_lock); init_waitqueue_head(&hba->eh_wait); if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type)) { - hba->hba_shutdown_tmo = 20 * HZ; + hba->hba_shutdown_tmo = 30 * HZ; hba->conn_teardown_tmo = 20 * HZ; hba->conn_ctx_destroy_tmo = 6 * HZ; } else { /* 5706/5708/5709 */ From 50d170e2c3707fbe3ca31f42e7c8d69a4a41e29c Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Wed, 18 May 2011 17:06:05 +0200 Subject: [PATCH 1860/2556] Fix Ultrastor asm snippet commit fad4dab5e44e10acf6b0235e469cb8e773b58e31 upstream. Commit 1292500b replaced "=m" (*field) : "1" (*field) with "=m" (*field) : with comment "The following patch fixes it by using the '+' operator on the (*field) operand, marking it as read-write to gcc." '+' was actually forgotten. This really puts it. Signed-off-by: Samuel Thibault Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/ultrastor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ultrastor.c b/drivers/scsi/ultrastor.c index 0571ef9639cbc..dc076e028a3ca 100644 --- a/drivers/scsi/ultrastor.c +++ b/drivers/scsi/ultrastor.c @@ -306,7 +306,7 @@ static inline int find_and_clear_bit_16(unsigned long *field) "0: bsfw %1,%w0\n\t" "btr %0,%1\n\t" "jnc 0b" - : "=&r" (rv), "=m" (*field) :); + : "=&r" (rv), "+m" (*field) :); return rv; } From baee56a2ef25317d47dbbb65eddaea25a1c46b99 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Thu, 19 May 2011 20:19:09 -0700 Subject: [PATCH 1861/2556] target: Fix multi task->task_sg[] chaining logic bug commit 97868c8905a1537153d406c4a3aa39a503a5c299 upstream. This patch fixes a bug in transport_do_task_sg_chain() used by HW target mode modules with sg_chain() to provide a single sg_next() walkable memory layout for use with pci_map_sg() and friends. This patch addresses an issue with mapping multiple small block max_sector tasks across multiple struct se_task->task_sg[] mappings for HW target mode operation. This was causing OOPs with (cmd->t_task->t_tasks_no > 1) I/O traffic for HW target drivers using transport_do_task_sg_chain(), and has been tested so far with tcm_fc(openfcoe), tcm_qla2xxx, and ib_srpt fabrics with t_tasks_no > 1 IBLOCK backends using a smaller max_sectors to trigger the original issue. Signed-off-by: Nicholas Bellinger Acked-by: Kiran Patil Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_transport.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 4bbf6c147f896..a0ad2f41a2f01 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -4777,18 +4777,20 @@ void transport_do_task_sg_chain(struct se_cmd *cmd) sg_end_cur->page_link &= ~0x02; sg_chain(sg_head, task_sg_num, sg_head_cur); - sg_count += (task->task_sg_num + 1); - } else sg_count += task->task_sg_num; + task_sg_num = (task->task_sg_num + 1); + } else { + sg_chain(sg_head, task_sg_num, sg_head_cur); + sg_count += task->task_sg_num; + task_sg_num = task->task_sg_num; + } sg_head = sg_head_cur; sg_link = sg_link_cur; - task_sg_num = task->task_sg_num; continue; } sg_head = sg_first = &task->task_sg[0]; sg_link = &task->task_sg[task->task_sg_num]; - task_sg_num = task->task_sg_num; /* * Check for single task.. */ @@ -4799,9 +4801,12 @@ void transport_do_task_sg_chain(struct se_cmd *cmd) */ sg_end = &task->task_sg[task->task_sg_num - 1]; sg_end->page_link &= ~0x02; - sg_count += (task->task_sg_num + 1); - } else sg_count += task->task_sg_num; + task_sg_num = (task->task_sg_num + 1); + } else { + sg_count += task->task_sg_num; + task_sg_num = task->task_sg_num; + } } /* * Setup the starting pointer and total t_tasks_sg_linked_no including @@ -4810,21 +4815,20 @@ void transport_do_task_sg_chain(struct se_cmd *cmd) T_TASK(cmd)->t_tasks_sg_chained = sg_first; T_TASK(cmd)->t_tasks_sg_chained_no = sg_count; - DEBUG_CMD_M("Setup T_TASK(cmd)->t_tasks_sg_chained: %p and" - " t_tasks_sg_chained_no: %u\n", T_TASK(cmd)->t_tasks_sg_chained, + DEBUG_CMD_M("Setup cmd: %p T_TASK(cmd)->t_tasks_sg_chained: %p and" + " t_tasks_sg_chained_no: %u\n", cmd, T_TASK(cmd)->t_tasks_sg_chained, T_TASK(cmd)->t_tasks_sg_chained_no); for_each_sg(T_TASK(cmd)->t_tasks_sg_chained, sg, T_TASK(cmd)->t_tasks_sg_chained_no, i) { - DEBUG_CMD_M("SG: %p page: %p length: %d offset: %d\n", - sg, sg_page(sg), sg->length, sg->offset); + DEBUG_CMD_M("SG[%d]: %p page: %p length: %d offset: %d, magic: 0x%08x\n", + i, sg, sg_page(sg), sg->length, sg->offset, sg->sg_magic); if (sg_is_chain(sg)) DEBUG_CMD_M("SG: %p sg_is_chain=1\n", sg); if (sg_is_last(sg)) DEBUG_CMD_M("SG: %p sg_is_last=1\n", sg); } - } EXPORT_SYMBOL(transport_do_task_sg_chain); From c690d5d2e4f68f2203071118a423357ed108f2ea Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Thu, 19 May 2011 20:19:10 -0700 Subject: [PATCH 1862/2556] target: Fix interrupt context bug with stats_lock and core_tmr_alloc_req commit 53ab6709b4d35b1924240854d794482fd7d33d4a upstream. This patch fixes two bugs wrt to the interrupt context usage of target core with HW target mode drivers. It first converts the usage of struct se_device->stats_lock in transport_get_lun_for_cmd() and core_tmr_lun_reset() to properly use spin_lock_irq() to address an BUG with CONFIG_LOCKDEP_SUPPORT=y enabled. This patch also adds a 'in_interrupt()' check to allow GFP_ATOMIC usage from core_tmr_alloc_req() to fix a 'sleeping in interrupt context' BUG with HW target fabrics that require this logic to function. Signed-off-by: Nicholas Bellinger Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_device.c | 4 ++-- drivers/target/target_core_tmr.c | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 5da051a07fa30..0e0257bf5b78d 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -151,13 +151,13 @@ int transport_get_lun_for_cmd( { struct se_device *dev = se_lun->lun_se_dev; - spin_lock(&dev->stats_lock); + spin_lock_irq(&dev->stats_lock); dev->num_cmds++; if (se_cmd->data_direction == DMA_TO_DEVICE) dev->write_bytes += se_cmd->data_length; else if (se_cmd->data_direction == DMA_FROM_DEVICE) dev->read_bytes += se_cmd->data_length; - spin_unlock(&dev->stats_lock); + spin_unlock_irq(&dev->stats_lock); } /* diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index 4a109835e4203..59b8b9c5ad72a 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -55,7 +55,8 @@ struct se_tmr_req *core_tmr_alloc_req( { struct se_tmr_req *tmr; - tmr = kmem_cache_zalloc(se_tmr_req_cache, GFP_KERNEL); + tmr = kmem_cache_zalloc(se_tmr_req_cache, (in_interrupt()) ? + GFP_ATOMIC : GFP_KERNEL); if (!(tmr)) { printk(KERN_ERR "Unable to allocate struct se_tmr_req\n"); return ERR_PTR(-ENOMEM); @@ -398,9 +399,9 @@ int core_tmr_lun_reset( printk(KERN_INFO "LUN_RESET: SCSI-2 Released reservation\n"); } - spin_lock(&dev->stats_lock); + spin_lock_irq(&dev->stats_lock); dev->num_resets++; - spin_unlock(&dev->stats_lock); + spin_unlock_irq(&dev->stats_lock); DEBUG_LR("LUN_RESET: %s for [%s] Complete\n", (preempt_and_abort_list) ? "Preempt" : "TMR", From 572346cd175e27eb59fcd9508305ee46666e00eb Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Thu, 19 May 2011 20:19:11 -0700 Subject: [PATCH 1863/2556] target: Fix bug with task_sg chained transport_free_dev_tasks release commit f436677262a5b524ac87675014c6d4e8ee153029 upstream. This patch addresses a bug in the target core release path for HW operation where transport_free_dev_tasks() was incorrectly being called from transport_lun_remove_cmd() while releasing a se_cmd reference and calling struct target_core_fabric_ops->queue_data_in(). This would result in a OOPs with HW target mode when the release of se_task->task_sg[] would happen before pci_unmap_sg() can be called in HW target mode fabric module code. This patch addresses the issue by moving transport_free_dev_tasks() from transport_lun_remove_cmd() into transport_generic_free_cmd(), and adding TRANSPORT_FREE_CMD_INTR and transport_generic_free_cmd_intr() to allow se_cmd descriptor release to happen fromfrom within transport_processing_thread() process context when release of se_cmd is not possible from HW interrupt context. Signed-off-by: Nicholas Bellinger Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_transport.c | 13 ++++++++++++- include/target/target_core_base.h | 1 + include/target/target_core_transport.h | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index a0ad2f41a2f01..0ddcc264ef316 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -765,7 +765,6 @@ static void transport_lun_remove_cmd(struct se_cmd *cmd) transport_all_task_dev_remove_state(cmd); spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags); - transport_free_dev_tasks(cmd); check_lun: spin_lock_irqsave(&lun->lun_cmd_lock, flags); @@ -2062,6 +2061,13 @@ int transport_generic_handle_tmr( } EXPORT_SYMBOL(transport_generic_handle_tmr); +void transport_generic_free_cmd_intr( + struct se_cmd *cmd) +{ + transport_add_cmd_to_queue(cmd, TRANSPORT_FREE_CMD_INTR); +} +EXPORT_SYMBOL(transport_generic_free_cmd_intr); + static int transport_stop_tasks_for_cmd(struct se_cmd *cmd) { struct se_task *task, *task_tmp; @@ -5302,6 +5308,8 @@ void transport_generic_free_cmd( if (wait_for_tasks && cmd->transport_wait_for_tasks) cmd->transport_wait_for_tasks(cmd, 0, 0); + transport_free_dev_tasks(cmd); + transport_generic_remove(cmd, release_to_pool, session_reinstatement); } @@ -6142,6 +6150,9 @@ static int transport_processing_thread(void *param) case TRANSPORT_REMOVE: transport_generic_remove(cmd, 1, 0); break; + case TRANSPORT_FREE_CMD_INTR: + transport_generic_free_cmd(cmd, 0, 1, 0); + break; case TRANSPORT_PROCESS_TMR: transport_generic_do_tmr(cmd); break; diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 0828b6c8610ae..95c9d9810dc2f 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -98,6 +98,7 @@ enum transport_state_table { TRANSPORT_REMOVE = 14, TRANSPORT_FREE = 15, TRANSPORT_NEW_CMD_MAP = 16, + TRANSPORT_FREE_CMD_INTR = 17, }; /* Used for struct se_cmd->se_cmd_flags */ diff --git a/include/target/target_core_transport.h b/include/target/target_core_transport.h index 2e8ec51f06155..379ae61912699 100644 --- a/include/target/target_core_transport.h +++ b/include/target/target_core_transport.h @@ -170,6 +170,7 @@ extern int transport_generic_handle_cdb_map(struct se_cmd *); extern int transport_generic_handle_data(struct se_cmd *); extern void transport_new_cmd_failure(struct se_cmd *); extern int transport_generic_handle_tmr(struct se_cmd *); +extern void transport_generic_free_cmd_intr(struct se_cmd *); extern void __transport_stop_task_timer(struct se_task *, unsigned long *); extern unsigned char transport_asciihex_to_binaryhex(unsigned char val[2]); extern int transport_generic_map_mem_to_cmd(struct se_cmd *cmd, struct scatterlist *, u32, From 6a50d53922bcf9185e5b0fef94936577916a6b8d Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Thu, 19 May 2011 20:19:12 -0700 Subject: [PATCH 1864/2556] target: Fix task->task_execute_queue=1 clear bug + LUN_RESET OOPs commit af57c3ac9947990da2608561b71f4799eb7795c6 upstream. This patch fixes a bug where task->task_execute_queue=1 was not being cleared once se_task had been removed from se_device->execute_task_list, resulting in an OOPs in core_tmr_lun_reset() for the task->task_active=0 case where transport_remove_task_from_execute_queue() was incorrectly being called. This patch fixes two cases in transport_get_task_from_execute_queue() and transport_remove_task_from_execute_queue() to properly clear task->task_execute_queue=0 once list_del(&task->t_execute_list) has been called. It also adds an explict check in transport_remove_task_from_execute_queue() to dump_stack + return if called with task->task_execute_queue=0. Signed-off-by: Nicholas Bellinger Signed-off-by: James Bottomley Signed-off-by: Greg Kroah-Hartman --- drivers/target/target_core_transport.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 0ddcc264ef316..d2a5768a5d93e 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1197,6 +1197,7 @@ transport_get_task_from_execute_queue(struct se_device *dev) break; list_del(&task->t_execute_list); + atomic_set(&task->task_execute_queue, 0); atomic_dec(&dev->execute_tasks); return task; @@ -1212,8 +1213,14 @@ void transport_remove_task_from_execute_queue( { unsigned long flags; + if (atomic_read(&task->task_execute_queue) == 0) { + dump_stack(); + return; + } + spin_lock_irqsave(&dev->execute_task_lock, flags); list_del(&task->t_execute_list); + atomic_set(&task->task_execute_queue, 0); atomic_dec(&dev->execute_tasks); spin_unlock_irqrestore(&dev->execute_task_lock, flags); } From 2e05fd68447a2ab4205dbd316f086f0861fbc37c Mon Sep 17 00:00:00 2001 From: Daniel J Blueman Date: Wed, 18 May 2011 16:31:31 -0700 Subject: [PATCH 1865/2556] x86, ioapic: Fix potential resume deadlock commit b64ce24daffb634b5b3133a2e411bd4de50654e8 upstream. Fix a potential deadlock when resuming; here the calling function has disabled interrupts, so we cannot sleep. Change the memory allocation flag from GFP_KERNEL to GFP_ATOMIC. TODO: We can do away with this memory allocation during resume by reusing the ioapic suspend/resume code that uses boot time allocated buffers, but we want to keep this -stable patch simple. Signed-off-by: Daniel J Blueman Signed-off-by: Suresh Siddha Link: http://lkml.kernel.org/r/20110518233157.385970138@sbsiddha-MOBL3.sc.intel.com Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/apic/io_apic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c index ca9e2a3545a9b..e43777835f6de 100644 --- a/arch/x86/kernel/apic/io_apic.c +++ b/arch/x86/kernel/apic/io_apic.c @@ -615,14 +615,14 @@ struct IO_APIC_route_entry **alloc_ioapic_entries(void) struct IO_APIC_route_entry **ioapic_entries; ioapic_entries = kzalloc(sizeof(*ioapic_entries) * nr_ioapics, - GFP_KERNEL); + GFP_ATOMIC); if (!ioapic_entries) return 0; for (apic = 0; apic < nr_ioapics; apic++) { ioapic_entries[apic] = kzalloc(sizeof(struct IO_APIC_route_entry) * - nr_ioapic_registers[apic], GFP_KERNEL); + nr_ioapic_registers[apic], GFP_ATOMIC); if (!ioapic_entries[apic]) goto nomem; } From 82a56dc10641d8ab447ea3a693c36f7e167a423f Mon Sep 17 00:00:00 2001 From: Boris Ostrovsky Date: Thu, 26 May 2011 11:19:52 -0400 Subject: [PATCH 1866/2556] x86, amd: Do not enable ARAT feature on AMD processors below family 0x12 commit e9cdd343a5e42c43bcda01e609fa23089e026470 upstream. Commit b87cf80af3ba4b4c008b4face3c68d604e1715c6 added support for ARAT (Always Running APIC timer) on AMD processors that are not affected by erratum 400. This erratum is present on certain processor families and prevents APIC timer from waking up the CPU when it is in a deep C state, including C1E state. Determining whether a processor is affected by this erratum may have some corner cases and handling these cases is somewhat complicated. In the interest of simplicity we won't claim ARAT support on processor families below 0x12 and will go back to broadcasting timer when going idle. Signed-off-by: Boris Ostrovsky Link: http://lkml.kernel.org/r/1306423192-19774-1-git-send-email-ostr@amd64.org Tested-by: Boris Petkov Cc: Hans Rosenfeld Cc: Andreas Herrmann Cc: Chuck Ebbert Signed-off-by: H. Peter Anvin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/amd.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index ff32eb37be6da..ae2be9a253396 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -595,8 +595,11 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c) } #endif - /* As a rule processors have APIC timer running in deep C states */ - if (c->x86 > 0xf && !cpu_has_amd_erratum(amd_erratum_400)) + /* + * Family 0x12 and above processors have APIC timer + * running in deep C states. + */ + if (c->x86 > 0x11) set_cpu_cap(c, X86_FEATURE_ARAT); /* From 497ce1c280e962b3fd87d8c84d015a18127a0d79 Mon Sep 17 00:00:00 2001 From: "Roedel, Joerg" Date: Thu, 19 May 2011 11:13:39 +0200 Subject: [PATCH 1867/2556] x86, amd: Use _safe() msr access for GartTlbWlk disable code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d47cc0db8fd6011de2248df505fc34990b7451bf upstream. The workaround for Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=33012 introduced a read and a write to the MC4 mask msr. Unfortunatly this MSR is not emulated by the KVM hypervisor so that the kernel will get a #GP and crashes when applying this workaround when running inside KVM. This issue was reported as: https://bugzilla.kernel.org/show_bug.cgi?id=35132 and is fixed with this patch. The change just let the kernel ignore any #GP it gets while accessing this MSR by using the _safe msr access methods. Reported-by: Török Edwin Signed-off-by: Joerg Roedel Cc: Rafael J. Wysocki Cc: Maciej Rutecki Cc: Avi Kivity Cc: Andrew Morton Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/amd.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index ae2be9a253396..e19776f65948d 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -615,10 +615,13 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c) * Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=33012 */ u64 mask; + int err; - rdmsrl(MSR_AMD64_MCx_MASK(4), mask); - mask |= (1 << 10); - wrmsrl(MSR_AMD64_MCx_MASK(4), mask); + err = rdmsrl_safe(MSR_AMD64_MCx_MASK(4), &mask); + if (err == 0) { + mask |= (1 << 10); + checking_wrmsrl(MSR_AMD64_MCx_MASK(4), mask); + } } } From 8da0d5755ac88a4027e0c511a2be7c0ef0c70634 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 24 May 2011 16:29:26 -0700 Subject: [PATCH 1868/2556] x86, cpufeature: Update CPU feature RDRND to RDRAND commit 7ccafc5f75c87853f3c49845d5a884f2376e03ce upstream. The Intel manual changed the name of the CPUID bit to match the instruction name. We should follow suit for sanity's sake. (See Intel SDM Volume 2, Table 3-20 "Feature Information Returned in the ECX Register".) [ hpa: we can only do this at this time because there are currently no CPUs with this feature on the market, hence this is pre-hardware enabling. However, Cc:'ing stable so that stable can present a consistent ABI. ] Signed-off-by: Kees Cook Link: http://lkml.kernel.org/r/20110524232926.GA27728@outflux.net Signed-off-by: H. Peter Anvin Cc: Fenghua Yu Signed-off-by: Greg Kroah-Hartman --- arch/x86/include/asm/cpufeature.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h index 220e2ea08e80b..3b98f78ad63ff 100644 --- a/arch/x86/include/asm/cpufeature.h +++ b/arch/x86/include/asm/cpufeature.h @@ -125,7 +125,7 @@ #define X86_FEATURE_OSXSAVE (4*32+27) /* "" XSAVE enabled in the OS */ #define X86_FEATURE_AVX (4*32+28) /* Advanced Vector Extensions */ #define X86_FEATURE_F16C (4*32+29) /* 16-bit fp conversions */ -#define X86_FEATURE_RDRND (4*32+30) /* The RDRAND instruction */ +#define X86_FEATURE_RDRAND (4*32+30) /* The RDRAND instruction */ #define X86_FEATURE_HYPERVISOR (4*32+31) /* Running on a hypervisor */ /* VIA/Cyrix/Centaur-defined CPU features, CPUID level 0xC0000001, word 5 */ From cc5b828d1335a8652026d1ade17cfe96faf0676f Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Fri, 20 May 2011 09:46:54 +0200 Subject: [PATCH 1869/2556] oprofile, x86: Enable preemption during pci device setup in IBS init commit 3d2606f42984613d324ad3047cf503bcddc3880a upstream. IBS initialization is a mix of per-core register access and per-node pci device setup. Register access should be pinned to the cpu, but pci setup must run with preemption enabled. This patch better separates the code into non-/preemptible sections and fixes sleeping with preemption disabled. See bug message below. Fixes also freeing the eilvt entry by introducing put_eilvt(). BUG: sleeping function called from invalid context at mm/slub.c:824 in_atomic(): 1, irqs_disabled(): 0, pid: 32357, name: modprobe INFO: lockdep is turned off. Pid: 32357, comm: modprobe Not tainted 2.6.39-rc7+ #14 Call Trace: [] __might_sleep+0x112/0x117 [] kmem_cache_alloc_trace+0x4b/0xe7 [] kzalloc.constprop.0+0x29/0x2b [] pci_get_subsys+0x36/0x78 [] ? setup_APIC_eilvt+0xfb/0x139 [] pci_get_device+0x16/0x18 [] op_amd_init+0xd3/0x211 [oprofile] [] ? 0xffffffffa064cfff [] op_nmi_init+0x21e/0x26a [oprofile] [] oprofile_arch_init+0xe/0x26 [oprofile] [] oprofile_init+0x10/0x42 [oprofile] [] do_one_initcall+0x7f/0x13a [] sys_init_module+0x132/0x281 [] system_call_fastpath+0x16/0x1b Reported-by: Dave Jones Signed-off-by: Robert Richter Signed-off-by: Greg Kroah-Hartman --- arch/x86/oprofile/op_model_amd.c | 95 ++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 41 deletions(-) diff --git a/arch/x86/oprofile/op_model_amd.c b/arch/x86/oprofile/op_model_amd.c index c3b8e24f2b16f..9fd8a567fe1e4 100644 --- a/arch/x86/oprofile/op_model_amd.c +++ b/arch/x86/oprofile/op_model_amd.c @@ -316,16 +316,23 @@ static void op_amd_stop_ibs(void) wrmsrl(MSR_AMD64_IBSOPCTL, 0); } -static inline int eilvt_is_available(int offset) +static inline int get_eilvt(int offset) { - /* check if we may assign a vector */ return !setup_APIC_eilvt(offset, 0, APIC_EILVT_MSG_NMI, 1); } +static inline int put_eilvt(int offset) +{ + return !setup_APIC_eilvt(offset, 0, 0, 1); +} + static inline int ibs_eilvt_valid(void) { int offset; u64 val; + int valid = 0; + + preempt_disable(); rdmsrl(MSR_AMD64_IBSCTL, val); offset = val & IBSCTL_LVT_OFFSET_MASK; @@ -333,16 +340,20 @@ static inline int ibs_eilvt_valid(void) if (!(val & IBSCTL_LVT_OFFSET_VALID)) { pr_err(FW_BUG "cpu %d, invalid IBS interrupt offset %d (MSR%08X=0x%016llx)\n", smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); - return 0; + goto out; } - if (!eilvt_is_available(offset)) { + if (!get_eilvt(offset)) { pr_err(FW_BUG "cpu %d, IBS interrupt offset %d not available (MSR%08X=0x%016llx)\n", smp_processor_id(), offset, MSR_AMD64_IBSCTL, val); - return 0; + goto out; } - return 1; + valid = 1; +out: + preempt_enable(); + + return valid; } static inline int get_ibs_offset(void) @@ -600,67 +611,69 @@ static int setup_ibs_ctl(int ibs_eilvt_off) static int force_ibs_eilvt_setup(void) { - int i; + int offset; int ret; - /* find the next free available EILVT entry */ - for (i = 1; i < 4; i++) { - if (!eilvt_is_available(i)) - continue; - ret = setup_ibs_ctl(i); - if (ret) - return ret; - pr_err(FW_BUG "using offset %d for IBS interrupts\n", i); - return 0; + /* + * find the next free available EILVT entry, skip offset 0, + * pin search to this cpu + */ + preempt_disable(); + for (offset = 1; offset < APIC_EILVT_NR_MAX; offset++) { + if (get_eilvt(offset)) + break; } + preempt_enable(); - printk(KERN_DEBUG "No EILVT entry available\n"); - - return -EBUSY; -} - -static int __init_ibs_nmi(void) -{ - int ret; - - if (ibs_eilvt_valid()) - return 0; + if (offset == APIC_EILVT_NR_MAX) { + printk(KERN_DEBUG "No EILVT entry available\n"); + return -EBUSY; + } - ret = force_ibs_eilvt_setup(); + ret = setup_ibs_ctl(offset); if (ret) - return ret; + goto out; - if (!ibs_eilvt_valid()) - return -EFAULT; + if (!ibs_eilvt_valid()) { + ret = -EFAULT; + goto out; + } + pr_err(FW_BUG "using offset %d for IBS interrupts\n", offset); pr_err(FW_BUG "workaround enabled for IBS LVT offset\n"); return 0; +out: + preempt_disable(); + put_eilvt(offset); + preempt_enable(); + return ret; } /* * check and reserve APIC extended interrupt LVT offset for IBS if * available - * - * init_ibs() preforms implicitly cpu-local operations, so pin this - * thread to its current CPU */ static void init_ibs(void) { - preempt_disable(); - ibs_caps = get_ibs_caps(); + if (!ibs_caps) + return; + + if (ibs_eilvt_valid()) goto out; - if (__init_ibs_nmi() < 0) - ibs_caps = 0; - else - printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps); + if (!force_ibs_eilvt_setup()) + goto out; + + /* Failed to setup ibs */ + ibs_caps = 0; + return; out: - preempt_enable(); + printk(KERN_INFO "oprofile: AMD IBS detected (0x%08x)\n", ibs_caps); } static int (*create_arch_files)(struct super_block *sb, struct dentry *root); From 8574041ad2ecac425ea510f2db86eeed27a6decb Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 20 May 2011 02:09:54 +0200 Subject: [PATCH 1870/2556] rcu: Fix unpaired rcu_irq_enter() from locking selftests commit ba9f207c9f82115aba4ce04b22e0081af0ae300f upstream. HARDIRQ_ENTER() maps to irq_enter() which calls rcu_irq_enter(). But HARDIRQ_EXIT() maps to __irq_exit() which doesn't call rcu_irq_exit(). So for every locking selftest that simulates hardirq disabled, we create an imbalance in the rcu extended quiescent state internal state. As a result, after the first missing rcu_irq_exit(), subsequent irqs won't exit dyntick-idle mode after leaving the interrupt handler. This means that RCU won't see the affected CPU as being in an extended quiescent state, resulting in long grace-period delays (as in grace periods extending for hours). To fix this, just use __irq_enter() to simulate the hardirq context. This is sufficient for the locking selftests as we don't need to exit any extended quiescent state or perform any check that irqs normally do when they wake up from idle. As a side effect, this patch makes it possible to restore "rcu: Decrease memory-barrier usage based on semi-formal proof", which eventually helped finding this bug. Reported-and-tested-by: Yinghai Lu Signed-off-by: Frederic Weisbecker Cc: Paul E. McKenney Cc: Ingo Molnar Cc: Peter Zijlstra Signed-off-by: Paul E. McKenney Signed-off-by: Greg Kroah-Hartman --- lib/locking-selftest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c index 619313ed6c46d..507a22fab7380 100644 --- a/lib/locking-selftest.c +++ b/lib/locking-selftest.c @@ -144,7 +144,7 @@ static void init_shared_classes(void) #define HARDIRQ_ENTER() \ local_irq_disable(); \ - irq_enter(); \ + __irq_enter(); \ WARN_ON(!in_irq()); #define HARDIRQ_EXIT() \ From f1ed16123ae7b2b87b20d9bf60d179f635dba3d5 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 26 May 2011 18:38:54 +0000 Subject: [PATCH 1871/2556] When mandatory encryption on share, fail mount commit 6848b7334b24b47aa3d0e70342ff839ffa95d5fa upstream. When mandatory encryption is configured in samba server on a share (smb.conf parameter "smb encrypt = mandatory") the server will hang up the tcp session when we try to send the first frame after the tree connect if it is not a QueryFSUnixInfo, this causes cifs mount to hang (it must be killed with ctl-c). Move the QueryFSUnixInfo call earlier in the mount sequence, and check whether the SetFSUnixInfo fails due to mandatory encryption so we can return a sensible error (EACCES) on mount. In a future patch (for 2.6.40) we will support mandatory encryption. Reviewed-by: Pavel Shilovsky Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/connect.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 1f4db2cca3370..fbe54a4f36328 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2431,7 +2431,7 @@ void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon, if (!CIFSSMBQFSUnixInfo(xid, tcon)) { __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability); - + cFYI(1, "unix caps which server supports %lld", cap); /* check for reconnect case in which we do not want to change the mount behavior if we can avoid it */ if (vol_info == NULL) { @@ -2449,6 +2449,9 @@ void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon, } } + if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP) + cERROR(1, "per-share encryption not supported yet"); + cap &= CIFS_UNIX_CAP_MASK; if (vol_info && vol_info->no_psx_acl) cap &= ~CIFS_UNIX_POSIX_ACL_CAP; @@ -2497,6 +2500,10 @@ void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon, cFYI(1, "very large read cap"); if (cap & CIFS_UNIX_LARGE_WRITE_CAP) cFYI(1, "very large write cap"); + if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP) + cFYI(1, "transport encryption cap"); + if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP) + cFYI(1, "mandatory transport encryption cap"); #endif /* CIFS_DEBUG2 */ if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) { if (vol_info == NULL) { @@ -2815,20 +2822,26 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, goto remote_path_check; } - /* do not care if following two calls succeed - informational */ - if (!tcon->ipc) { - CIFSSMBQFSDeviceInfo(xid, tcon); - CIFSSMBQFSAttributeInfo(xid, tcon); - } - /* tell server which Unix caps we support */ - if (tcon->ses->capabilities & CAP_UNIX) + if (tcon->ses->capabilities & CAP_UNIX) { /* reset of caps checks mount to see if unix extensions disabled for just this mount */ reset_cifs_unix_caps(xid, tcon, sb, volume_info); - else + if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) && + (le64_to_cpu(tcon->fsUnixInfo.Capability) & + CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) { + rc = -EACCES; + goto mount_fail_check; + } + } else tcon->unix_ext = 0; /* server does not support them */ + /* do not care if following two calls succeed - informational */ + if (!tcon->ipc) { + CIFSSMBQFSDeviceInfo(xid, tcon); + CIFSSMBQFSAttributeInfo(xid, tcon); + } + /* convert forward to back slashes in prepath here if needed */ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0) convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb)); From 1063a9dec1316a46e264d7a86059ee6c7af09a26 Mon Sep 17 00:00:00 2001 From: David Chang Date: Thu, 12 May 2011 18:31:11 +0800 Subject: [PATCH 1872/2556] staging: usbip: fix wrong endian conversion commit cacd18a8476ce145ca5dcd46dc5b75585fd1289c upstream. Fix number_of_packets wrong endian conversion in function correct_endian_ret_submit() Signed-off-by: David Chang Acked-by: Arjan Mels Signed-off-by: Greg Kroah-Hartman --- drivers/staging/usbip/usbip_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c index 2108ca15542e6..69aa496b19317 100644 --- a/drivers/staging/usbip/usbip_common.c +++ b/drivers/staging/usbip/usbip_common.c @@ -709,7 +709,7 @@ static void correct_endian_ret_submit(struct usbip_header_ret_submit *pdu, be32_to_cpus(&pdu->status); be32_to_cpus(&pdu->actual_length); be32_to_cpus(&pdu->start_frame); - cpu_to_be32s(&pdu->number_of_packets); + be32_to_cpus(&pdu->number_of_packets); be32_to_cpus(&pdu->error_count); } } From ea1e05a4044e3ef488ee43098f89b4a34c034837 Mon Sep 17 00:00:00 2001 From: Jeff Chua Date: Wed, 27 Apr 2011 11:25:14 -0500 Subject: [PATCH 1873/2556] staging: r8712u: Fix driver to support ad-hoc mode commit 62819fd9481021db7f87d5f61f2e2fd2be1dfcfa upstream. Driver r8712u is unable to handle ad-hoc mode. The issue is that when the driver first starts, there will not be an SSID for association. The fix is to always call the "select and join from scan" routine when in ad-hoc mode. Note: Ad-hoc mode worked intermittently before. If the driver had previously been associated, then things were OK. Signed-off-by: Jeff Chua Signed-off-by: Larry Finger Signed-off-by: Greg Kroah-Hartman --- drivers/staging/rtl8712/rtl871x_ioctl_set.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/staging/rtl8712/rtl871x_ioctl_set.c b/drivers/staging/rtl8712/rtl871x_ioctl_set.c index 8b1451d030698..8486eb1503cc0 100644 --- a/drivers/staging/rtl8712/rtl871x_ioctl_set.c +++ b/drivers/staging/rtl8712/rtl871x_ioctl_set.c @@ -68,7 +68,10 @@ static u8 do_join(struct _adapter *padapter) pmlmepriv->fw_state |= _FW_UNDER_LINKING; pmlmepriv->pscanned = plist; pmlmepriv->to_join = true; - if (_queue_empty(queue) == true) { + + /* adhoc mode will start with an empty queue, but skip checking */ + if (!check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) && + _queue_empty(queue)) { if (pmlmepriv->fw_state & _FW_UNDER_LINKING) pmlmepriv->fw_state ^= _FW_UNDER_LINKING; /* when set_ssid/set_bssid for do_join(), but scanning queue From 2f026bcd6c57ca6cae4032fd0748abf6fd24c19c Mon Sep 17 00:00:00 2001 From: Timo Warns Date: Thu, 19 May 2011 09:24:17 +0200 Subject: [PATCH 1874/2556] Fix for buffer overflow in ldm_frag_add not sufficient commit cae13fe4cc3f24820ffb990c09110626837e85d4 upstream. As Ben Hutchings discovered [1], the patch for CVE-2011-1017 (buffer overflow in ldm_frag_add) is not sufficient. The original patch in commit c340b1d64000 ("fs/partitions/ldm.c: fix oops caused by corrupted partition table") does not consider that, for subsequent fragments, previously allocated memory is used. [1] http://lkml.org/lkml/2011/5/6/407 Reported-by: Ben Hutchings Signed-off-by: Timo Warns Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/partitions/ldm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/partitions/ldm.c b/fs/partitions/ldm.c index ce4f624404255..a29d5ccf3d54a 100644 --- a/fs/partitions/ldm.c +++ b/fs/partitions/ldm.c @@ -1335,6 +1335,11 @@ static bool ldm_frag_add (const u8 *data, int size, struct list_head *frags) list_add_tail (&f->list, frags); found: + if (rec >= f->num) { + ldm_error("REC value (%d) exceeds NUM value (%d)", rec, f->num); + return false; + } + if (f->map & (1 << rec)) { ldm_error ("Duplicate VBLK, part %d.", rec); f->map &= 0x7F; /* Mark the group as broken */ From 8a5167a5fc4e06cc1b4281eff1b3f884003bb7e2 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Thu, 12 May 2011 04:13:54 -0500 Subject: [PATCH 1875/2556] seqlock: Don't smp_rmb in seqlock reader spin loop commit 5db1256a5131d3b133946fa02ac9770a784e6eb2 upstream. Move the smp_rmb after cpu_relax loop in read_seqlock and add ACCESS_ONCE to make sure the test and return are consistent. A multi-threaded core in the lab didn't like the update from 2.6.35 to 2.6.36, to the point it would hang during boot when multiple threads were active. Bisection showed af5ab277ded04bd9bc6b048c5a2f0e7d70ef0867 (clockevents: Remove the per cpu tick skew) as the culprit and it is supported with stack traces showing xtime_lock waits including tick_do_update_jiffies64 and/or update_vsyscall. Experimentation showed the combination of cpu_relax and smp_rmb was significantly slowing the progress of other threads sharing the core, and this patch is effective in avoiding the hang. A theory is the rmb is affecting the whole core while the cpu_relax is causing a resource rebalance flush, together they cause an interfernce cadance that is unbroken when the seqlock reader has interrupts disabled. At first I was confused why the refactor in 3c22cd5709e8143444a6d08682a87f4c57902df3 (kernel: optimise seqlock) didn't affect this patch application, but after some study that affected seqcount not seqlock. The new seqcount was not factored back into the seqlock. I defer that the future. While the removal of the timer interrupt offset created contention for the xtime lock while a cpu does the additonal work to update the system clock, the seqlock implementation with the tight rmb spin loop goes back much further, and is just waiting for the right trigger. Signed-off-by: Milton Miller Cc: Cc: Linus Torvalds Cc: Andi Kleen Cc: Nick Piggin Cc: Benjamin Herrenschmidt Cc: Anton Blanchard Cc: Paul McKenney Acked-by: Eric Dumazet Link: http://lkml.kernel.org/r/%3Cseqlock-rmb%40mdm.bga.com%3E Signed-off-by: Thomas Gleixner Signed-off-by: Greg Kroah-Hartman --- include/linux/seqlock.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index e98cd2e571944..06d69648fc86c 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -88,12 +88,12 @@ static __always_inline unsigned read_seqbegin(const seqlock_t *sl) unsigned ret; repeat: - ret = sl->sequence; - smp_rmb(); + ret = ACCESS_ONCE(sl->sequence); if (unlikely(ret & 1)) { cpu_relax(); goto repeat; } + smp_rmb(); return ret; } From ff7c37c2f964d7e1f14966b3a1e5532705c7adee Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 10 May 2011 17:49:01 +1000 Subject: [PATCH 1876/2556] md: Fix race when creating a new md device. commit b0140891a8cea36469f58d23859e599b1122bd37 upstream. There is a race when creating an md device by opening /dev/mdXX. If two processes do this at much the same time they will follow the call path __blkdev_get -> get_gendisk -> kobj_lookup The first will call -> md_probe -> md_alloc -> add_disk -> blk_register_region and the race happens when the second gets to kobj_lookup after add_disk has called blk_register_region but before it returns to md_alloc. In the case the second will not call md_probe (as the probe is already done) but will get a handle on the gendisk, return to __blkdev_get which will then call md_open (via the ->open) pointer. As mddev->gendisk hasn't been set yet, md_open will think something is wrong an return with ERESTARTSYS. This can loop endlessly while the first thread makes no progress through add_disk. Nothing is blocking it, but due to scheduler behaviour it doesn't get a turn. So this is essentially a live-lock. We fix this by simply moving the assignment to mddev->gendisk before the call the add_disk() so md_open doesn't get confused. Also move blk_queue_flush earlier because add_disk should be as late as possible. To make sure that md_open doesn't complete until md_alloc has done all that is needed, we take mddev->open_mutex during the last part of md_alloc. md_open will wait for this. This can cause a lock-up on boot so Cc:ing for stable. For 2.6.36 and earlier a different patch will be needed as the 'blk_queue_flush' call isn't there. Signed-off-by: NeilBrown Reported-by: Thomas Jarosch Tested-by: Thomas Jarosch Signed-off-by: Greg Kroah-Hartman --- drivers/md/md.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/md/md.c b/drivers/md/md.c index 818313e277e7c..3683a07415fb4 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -4335,13 +4335,19 @@ static int md_alloc(dev_t dev, char *name) disk->fops = &md_fops; disk->private_data = mddev; disk->queue = mddev->queue; + blk_queue_flush(mddev->queue, REQ_FLUSH | REQ_FUA); /* Allow extended partitions. This makes the * 'mdp' device redundant, but we can't really * remove it now. */ disk->flags |= GENHD_FL_EXT_DEVT; - add_disk(disk); mddev->gendisk = disk; + /* As soon as we call add_disk(), another thread could get + * through to md_open, so make sure it doesn't get too far + */ + mutex_lock(&mddev->open_mutex); + add_disk(disk); + error = kobject_init_and_add(&mddev->kobj, &md_ktype, &disk_to_dev(disk)->kobj, "%s", "md"); if (error) { @@ -4355,8 +4361,7 @@ static int md_alloc(dev_t dev, char *name) if (mddev->kobj.sd && sysfs_create_group(&mddev->kobj, &md_bitmap_group)) printk(KERN_DEBUG "pointless warning\n"); - - blk_queue_flush(mddev->queue, REQ_FLUSH | REQ_FUA); + mutex_unlock(&mddev->open_mutex); abort: mutex_unlock(&disks_mutex); if (!error && mddev->kobj.sd) { From 2fdcf29a363c69ec1937b0b1a402622bd99fabeb Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 11 May 2011 14:26:30 +1000 Subject: [PATCH 1877/2556] md/bitmap: fix saving of events_cleared and other state. commit 8258c53208d7a9b7207e7d4dae36d2ea384cb278 upstream. If a bitmap is found to be 'stale' the events_cleared value is set to match 'events'. However if the array is degraded this does not get stored on disk. This can subsequently lead to incorrect behaviour. So change bitmap_update_sb to always update events_cleared in the superblock from the known events_cleared. For neatness also set ->state from ->flags. This requires updating ->state whenever we update ->flags, which makes sense anyway. This is suitable for any active -stable release. Signed-off-by: NeilBrown Signed-off-by: Greg Kroah-Hartman --- drivers/md/bitmap.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index 9a35320fb59f7..9d4406985fe57 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -493,11 +493,11 @@ void bitmap_update_sb(struct bitmap *bitmap) spin_unlock_irqrestore(&bitmap->lock, flags); sb = kmap_atomic(bitmap->sb_page, KM_USER0); sb->events = cpu_to_le64(bitmap->mddev->events); - if (bitmap->mddev->events < bitmap->events_cleared) { + if (bitmap->mddev->events < bitmap->events_cleared) /* rocking back to read-only */ bitmap->events_cleared = bitmap->mddev->events; - sb->events_cleared = cpu_to_le64(bitmap->events_cleared); - } + sb->events_cleared = cpu_to_le64(bitmap->events_cleared); + sb->state = cpu_to_le32(bitmap->flags); /* Just in case these have been changed via sysfs: */ sb->daemon_sleep = cpu_to_le32(bitmap->mddev->bitmap_info.daemon_sleep/HZ); sb->write_behind = cpu_to_le32(bitmap->mddev->bitmap_info.max_write_behind); @@ -618,7 +618,7 @@ static int bitmap_read_sb(struct bitmap *bitmap) if (le32_to_cpu(sb->version) == BITMAP_MAJOR_HOSTENDIAN) bitmap->flags |= BITMAP_HOSTENDIAN; bitmap->events_cleared = le64_to_cpu(sb->events_cleared); - if (sb->state & cpu_to_le32(BITMAP_STALE)) + if (bitmap->flags & BITMAP_STALE) bitmap->events_cleared = bitmap->mddev->events; err = 0; out: @@ -652,9 +652,11 @@ static int bitmap_mask_state(struct bitmap *bitmap, enum bitmap_state bits, switch (op) { case MASK_SET: sb->state |= cpu_to_le32(bits); + bitmap->flags |= bits; break; case MASK_UNSET: sb->state &= cpu_to_le32(~bits); + bitmap->flags &= ~bits; break; default: BUG(); From 28663b64187171a869bf991b20e3dc24f88067d4 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Tue, 24 May 2011 17:11:09 -0700 Subject: [PATCH 1878/2556] mm: vmscan: correct use of pgdat_balanced in sleeping_prematurely commit afc7e326a3f5bafc41324d7926c324414e343ee5 upstream. There are a few reports of people experiencing hangs when copying large amounts of data with kswapd using a large amount of CPU which appear to be due to recent reclaim changes. SLUB using high orders is the trigger but not the root cause as SLUB has been using high orders for a while. The root cause was bugs introduced into reclaim which are addressed by the following two patches. Patch 1 corrects logic introduced by commit 1741c877 ("mm: kswapd: keep kswapd awake for high-order allocations until a percentage of the node is balanced") to allow kswapd to go to sleep when balanced for high orders. Patch 2 notes that it is possible for kswapd to miss every cond_resched() and updates shrink_slab() so it'll at least reach that scheduling point. Chris Wood reports that these two patches in isolation are sufficient to prevent the system hanging. AFAIK, they should also resolve similar hangs experienced by James Bottomley. This patch: Johannes Weiner poined out that the logic in commit 1741c877 ("mm: kswapd: keep kswapd awake for high-order allocations until a percentage of the node is balanced") is backwards. Instead of allowing kswapd to go to sleep when balancing for high order allocations, it keeps it kswapd running uselessly. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Signed-off-by: Johannes Weiner Reviewed-by: Wu Fengguang Cc: James Bottomley Tested-by: Colin King Cc: Raghavendra D Prabhu Cc: Jan Kara Cc: Chris Mason Cc: Christoph Lameter Cc: Pekka Enberg Cc: Rik van Riel Reviewed-by: Minchan Kim Reviewed-by: Wu Fengguang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/vmscan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 06655208884c9..9789faaabdf26 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2286,7 +2286,7 @@ static bool sleeping_prematurely(pg_data_t *pgdat, int order, long remaining, * must be balanced */ if (order) - return pgdat_balanced(pgdat, balanced, classzone_idx); + return !pgdat_balanced(pgdat, balanced, classzone_idx); else return !all_zones_ok; } From 2020aa625c559d371518040290b5476356e7aacf Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Tue, 24 May 2011 17:11:11 -0700 Subject: [PATCH 1879/2556] mm: vmscan: correctly check if reclaimer should schedule during shrink_slab commit f06590bd718ed950c98828e30ef93204028f3210 upstream. It has been reported on some laptops that kswapd is consuming large amounts of CPU and not being scheduled when SLUB is enabled during large amounts of file copying. It is expected that this is due to kswapd missing every cond_resched() point because; shrink_page_list() calls cond_resched() if inactive pages were isolated which in turn may not happen if all_unreclaimable is set in shrink_zones(). If for whatver reason, all_unreclaimable is set on all zones, we can miss calling cond_resched(). balance_pgdat() only calls cond_resched if the zones are not balanced. For a high-order allocation that is balanced, it checks order-0 again. During that window, order-0 might have become unbalanced so it loops again for order-0 and returns that it was reclaiming for order-0 to kswapd(). It can then find that a caller has rewoken kswapd for a high-order and re-enters balance_pgdat() without ever calling cond_resched(). shrink_slab only calls cond_resched() if we are reclaiming slab pages. If there are a large number of direct reclaimers, the shrinker_rwsem can be contended and prevent kswapd calling cond_resched(). This patch modifies the shrink_slab() case. If the semaphore is contended, the caller will still check cond_resched(). After each successful call into a shrinker, the check for cond_resched() remains in case one shrinker is particularly slow. [mgorman@suse.de: preserve call to cond_resched after each call into shrinker] Signed-off-by: Mel Gorman Signed-off-by: Minchan Kim Cc: Rik van Riel Cc: Johannes Weiner Cc: Wu Fengguang Cc: James Bottomley Tested-by: Colin King Cc: Raghavendra D Prabhu Cc: Jan Kara Cc: Chris Mason Cc: Christoph Lameter Cc: Pekka Enberg Cc: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/vmscan.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 9789faaabdf26..a74bf72e5a679 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -230,8 +230,11 @@ unsigned long shrink_slab(unsigned long scanned, gfp_t gfp_mask, if (scanned == 0) scanned = SWAP_CLUSTER_MAX; - if (!down_read_trylock(&shrinker_rwsem)) - return 1; /* Assume we'll be able to shrink next time */ + if (!down_read_trylock(&shrinker_rwsem)) { + /* Assume we'll be able to shrink next time */ + ret = 1; + goto out; + } list_for_each_entry(shrinker, &shrinker_list, list) { unsigned long long delta; @@ -282,6 +285,8 @@ unsigned long shrink_slab(unsigned long scanned, gfp_t gfp_mask, shrinker->nr += total_scan; } up_read(&shrinker_rwsem); +out: + cond_resched(); return ret; } From f6172a0875387c008bebf0fde0bd64ecc0709fe1 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Mon, 28 Mar 2011 06:36:30 -0400 Subject: [PATCH 1880/2556] ips: use interruptible waits in ips-monitor commit a3424216e4935221fdaa5ca3c26e024f11297164 upstream. This is what I intended to do since: 1) the driver handles variable waits just fine, and 2) interruptible waits aren't reported as load in the load avg. Reported-and-tested-by: Andreas Hartmann Signed-off-by: Jesse Barnes Signed-off-by: Matthew Garrett Cc: Leann Ogasawara Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/intel_ips.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index 1294a39373bab..85c8ad43c0c58 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -1111,7 +1111,7 @@ static int ips_monitor(void *data) last_msecs = jiffies_to_msecs(jiffies); expire = jiffies + msecs_to_jiffies(IPS_SAMPLE_PERIOD); - __set_current_state(TASK_UNINTERRUPTIBLE); + __set_current_state(TASK_INTERRUPTIBLE); mod_timer(&timer, expire); schedule(); From 5899dde6559c06d81895416c6d962b02d6c8f241 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Mon, 16 May 2011 12:09:29 +0200 Subject: [PATCH 1881/2556] ALSA: HDA: Use one dmic only for Dell Studio 1558 commit e033ebfb399227e01686260ac271029011bc6b47 upstream. There are no signs of a dmic at node 0x0b, so the user is left with an additional internal mic which does not exist. This commit removes that non-existing mic. BugLink: http://bugs.launchpad.net/bugs/731706 Reported-by: James Page Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_sigmatel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 856611909c413..295a96a4df9a7 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -1634,7 +1634,7 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02fe, "Dell Studio XPS 1645", STAC_DELL_M6_BOTH), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0413, - "Dell Studio 1558", STAC_DELL_M6_BOTH), + "Dell Studio 1558", STAC_DELL_M6_DMIC), {} /* terminator */ }; From f130d60035bb0aedf1c9ba7a536a290a2412ee15 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Mon, 23 May 2011 08:26:16 +0200 Subject: [PATCH 1882/2556] ALSA: HDA: Add quirk for Lenovo U350 commit d2859fd49200f1f3efd8acdb54b6d51d3ab82302 upstream. Add model=asus quirk for Lenovo Ideapad U350 to make internal mic work correctly. BugLink: http://bugs.launchpad.net/bugs/751681 Reported-by: Kent Baxley Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_conexant.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index e33d69eea7989..9fbfd3b0ab59d 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -3132,6 +3132,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT5066_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo U350", CXT5066_ASUS), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS), SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", CXT5066_IDEAPAD), /* Fallback for Lenovos without dock mic */ {} From d5f550c4d9eb10ae1895f837e5dbd0ae6890591d Mon Sep 17 00:00:00 2001 From: Adrian Wilkins Date: Thu, 19 May 2011 21:52:38 +0100 Subject: [PATCH 1883/2556] ALSA: hda - Fix input-src parse in patch_analog.c commit 5a2d227fdc7a02ed1b4cebba391d8fb9ad57caaf upstream. Compare pin type enum to the pin type and not the array index. Fixes bug#0005368. Signed-off-by: Adrian Wilkins Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_analog.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 7aee90044c685..f8363ae869e86 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -3167,6 +3167,7 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec) for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; + int type = cfg->inputs[i].type; switch (nid) { case 0x15: /* port-C */ snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0); @@ -3176,7 +3177,7 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec) break; } snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - i == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN); + type == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN); if (nid != AD1988_PIN_CD_NID) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); From a1dadcf089c94dfc26986f3f801b1ee8ee9ed48d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 20 May 2011 16:29:09 +0200 Subject: [PATCH 1884/2556] ALSA: hda - Use LPIB for ATI/AMD chipsets as default commit 50e3bbf9898840eead86f90a43b3625a2b2f4112 upstream. ATI and AMD chipsets seem not providing the proper position-buffer information, and it also doesn't provide FIFO register required by VIACOMBO fix. It's better to use LPIB for these. Reported-by: David Henningsson Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/hda_intel.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index fcedad9a5feff..1de9f2e9998b8 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2346,9 +2346,16 @@ static int __devinit check_position_fix(struct azx *chip, int fix) /* Check VIA/ATI HD Audio Controller exist */ switch (chip->driver_type) { case AZX_DRIVER_VIA: - case AZX_DRIVER_ATI: /* Use link position directly, avoid any transfer problem. */ return POS_FIX_VIACOMBO; + case AZX_DRIVER_ATI: + /* ATI chipsets don't work well with position-buffer */ + return POS_FIX_LPIB; + case AZX_DRIVER_GENERIC: + /* AMD chipsets also don't work with position-buffer */ + if (chip->pci->vendor == PCI_VENDOR_ID_AMD) + return POS_FIX_LPIB; + break; } return POS_FIX_AUTO; From b46f2b345b5fccbc63f7454fbdc912377f027e48 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 14 May 2011 17:21:28 -0700 Subject: [PATCH 1885/2556] ASoC: Ensure output PGA is enabled for line outputs in wm_hubs commit d0b48af6c2b887354d0893e598d92911ce52620e upstream. Also fix a left/right typo while we're at it. Signed-off-by: Mark Brown Acked-by: Liam Girdwood Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/wm_hubs.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index d365f4344f96d..917dedbbf6518 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -786,17 +786,17 @@ static const struct snd_soc_dapm_route analogue_routes[] = { static const struct snd_soc_dapm_route lineout1_diff_routes[] = { { "LINEOUT1 Mixer", "IN1L Switch", "IN1L PGA" }, { "LINEOUT1 Mixer", "IN1R Switch", "IN1R PGA" }, - { "LINEOUT1 Mixer", "Output Switch", "Left Output Mixer" }, + { "LINEOUT1 Mixer", "Output Switch", "Left Output PGA" }, { "LINEOUT1N Driver", NULL, "LINEOUT1 Mixer" }, { "LINEOUT1P Driver", NULL, "LINEOUT1 Mixer" }, }; static const struct snd_soc_dapm_route lineout1_se_routes[] = { - { "LINEOUT1N Mixer", "Left Output Switch", "Left Output Mixer" }, - { "LINEOUT1N Mixer", "Right Output Switch", "Left Output Mixer" }, + { "LINEOUT1N Mixer", "Left Output Switch", "Left Output PGA" }, + { "LINEOUT1N Mixer", "Right Output Switch", "Right Output PGA" }, - { "LINEOUT1P Mixer", "Left Output Switch", "Left Output Mixer" }, + { "LINEOUT1P Mixer", "Left Output Switch", "Left Output PGA" }, { "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" }, { "LINEOUT1P Driver", NULL, "LINEOUT1P Mixer" }, @@ -805,17 +805,17 @@ static const struct snd_soc_dapm_route lineout1_se_routes[] = { static const struct snd_soc_dapm_route lineout2_diff_routes[] = { { "LINEOUT2 Mixer", "IN2L Switch", "IN2L PGA" }, { "LINEOUT2 Mixer", "IN2R Switch", "IN2R PGA" }, - { "LINEOUT2 Mixer", "Output Switch", "Right Output Mixer" }, + { "LINEOUT2 Mixer", "Output Switch", "Right Output PGA" }, { "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" }, { "LINEOUT2P Driver", NULL, "LINEOUT2 Mixer" }, }; static const struct snd_soc_dapm_route lineout2_se_routes[] = { - { "LINEOUT2N Mixer", "Left Output Switch", "Left Output Mixer" }, - { "LINEOUT2N Mixer", "Right Output Switch", "Left Output Mixer" }, + { "LINEOUT2N Mixer", "Left Output Switch", "Left Output PGA" }, + { "LINEOUT2N Mixer", "Right Output Switch", "Right Output PGA" }, - { "LINEOUT2P Mixer", "Right Output Switch", "Right Output Mixer" }, + { "LINEOUT2P Mixer", "Right Output Switch", "Right Output PGA" }, { "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" }, { "LINEOUT2P Driver", NULL, "LINEOUT2P Mixer" }, From a73155a0ff2253fd51f58f055ed1bf99a8ccfb21 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 15 May 2011 12:18:38 -0700 Subject: [PATCH 1886/2556] ASoC: Add some missing volume update bit sets for wm_hubs devices commit fb5af53d421d80725172427e9076f6e889603df6 upstream. Signed-off-by: Mark Brown Acked-by: Liam Girdwood Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/wm_hubs.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 917dedbbf6518..fce23b3ca1dc5 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -835,17 +835,21 @@ int wm_hubs_add_analogue_controls(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, WM8993_IN2_VU, WM8993_IN2_VU); + snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_LEFT, + WM8993_SPKOUT_VU, WM8993_SPKOUT_VU); snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_RIGHT, WM8993_SPKOUT_VU, WM8993_SPKOUT_VU); snd_soc_update_bits(codec, WM8993_LEFT_OUTPUT_VOLUME, - WM8993_HPOUT1L_ZC, WM8993_HPOUT1L_ZC); + WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC, + WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC); snd_soc_update_bits(codec, WM8993_RIGHT_OUTPUT_VOLUME, WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC, WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC); snd_soc_update_bits(codec, WM8993_LEFT_OPGA_VOLUME, - WM8993_MIXOUTL_ZC, WM8993_MIXOUTL_ZC); + WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU, + WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU); snd_soc_update_bits(codec, WM8993_RIGHT_OPGA_VOLUME, WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU, WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU); From 29dd7782519b0d07f1113f91c2d7a4f51167d88b Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Thu, 19 May 2011 17:58:07 +0200 Subject: [PATCH 1887/2556] HID: magicmouse: ignore 'ivalid report id' while switching modes commit 23746a66d7d9e73402c68ef00d708796b97ebd72 upstream. The device reponds with 'invalid report id' when feature report switching it into multitouch mode is sent to it. This has been silently ignored before 0825411ade ("HID: bt: Wait for ACK on Sent Reports"), but since this commit, it propagates -EIO from the _raw callback . So let the driver ignore -EIO as response to 0xd7,0x01 report, as that's how the device reacts in normal mode. Sad, but following reality. This fixes https://bugzilla.kernel.org/show_bug.cgi?id=35022 Tested-by: Chase Douglas Signed-off-by: Jiri Kosina Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-magicmouse.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 418c399d3ef77..a3972bbfa3664 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -501,9 +501,17 @@ static int magicmouse_probe(struct hid_device *hdev, } report->size = 6; + /* + * The device reponds with 'invalid report id' when feature + * report switching it into multitouch mode is sent to it. + * + * This results in -EIO from the _raw low-level transport callback, + * but there seems to be no other way of switching the mode. + * Thus the super-ugly hacky success check below. + */ ret = hdev->hid_output_raw_report(hdev, feature, sizeof(feature), HID_FEATURE_REPORT); - if (ret != sizeof(feature)) { + if (ret != -EIO) { hid_err(hdev, "unable to request touch data (%d)\n", ret); goto err_stop_hw; } From cb29ff4edcd40f96be17b153c07eb1578f834fdf Mon Sep 17 00:00:00 2001 From: Andrew Barry Date: Tue, 24 May 2011 17:12:52 -0700 Subject: [PATCH 1888/2556] mm/page_alloc.c: prevent unending loop in __alloc_pages_slowpath() commit cfa54a0fcfc1017c6f122b6f21aaba36daa07f71 upstream. I believe I found a problem in __alloc_pages_slowpath, which allows a process to get stuck endlessly looping, even when lots of memory is available. Running an I/O and memory intensive stress-test I see a 0-order page allocation with __GFP_IO and __GFP_WAIT, running on a system with very little free memory. Right about the same time that the stress-test gets killed by the OOM-killer, the utility trying to allocate memory gets stuck in __alloc_pages_slowpath even though most of the systems memory was freed by the oom-kill of the stress-test. The utility ends up looping from the rebalance label down through the wait_iff_congested continiously. Because order=0, __alloc_pages_direct_compact skips the call to get_page_from_freelist. Because all of the reclaimable memory on the system has already been reclaimed, __alloc_pages_direct_reclaim skips the call to get_page_from_freelist. Since there is no __GFP_FS flag, the block with __alloc_pages_may_oom is skipped. The loop hits the wait_iff_congested, then jumps back to rebalance without ever trying to get_page_from_freelist. This loop repeats infinitely. The test case is pretty pathological. Running a mix of I/O stress-tests that do a lot of fork() and consume all of the system memory, I can pretty reliably hit this on 600 nodes, in about 12 hours. 32GB/node. Signed-off-by: Andrew Barry Signed-off-by: Minchan Kim Reviewed-by: Rik van Riel Acked-by: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/page_alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index e607de5c138fe..493b52268e67e 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2044,6 +2044,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, first_zones_zonelist(zonelist, high_zoneidx, NULL, &preferred_zone); +rebalance: /* This is the last chance, in general, before the goto nopage. */ page = get_page_from_freelist(gfp_mask, nodemask, order, zonelist, high_zoneidx, alloc_flags & ~ALLOC_NO_WATERMARKS, @@ -2051,7 +2052,6 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, if (page) goto got_pg; -rebalance: /* Allocate without watermarks if the context allows */ if (alloc_flags & ALLOC_NO_WATERMARKS) { page = __alloc_pages_high_priority(gfp_mask, order, From c439bb66b6dd6a03511246efcc864fa635bda644 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 24 May 2011 16:48:54 +0200 Subject: [PATCH 1889/2556] loop: limit 'max_part' module param to DISK_MAX_PARTS commit 78f4bb367fd147a0e7e3998ba6e47109999d8814 upstream. The 'max_part' parameter controls the number of maximum partition a loop block device can have. However if a user specifies very large value it would exceed the limitation of device minor number and can cause a kernel panic (or, at least, produce invalid device nodes in some cases). On my desktop system, following command kills the kernel. On qemu, it triggers similar oops but the kernel was alive: $ sudo modprobe loop max_part0000 ------------[ cut here ]------------ kernel BUG at /media/Linux_Data/project/linux/fs/sysfs/group.c:65! invalid opcode: 0000 [#1] SMP last sysfs file: CPU 0 Modules linked in: loop(+) Pid: 43, comm: insmod Tainted: G W 2.6.39-qemu+ #155 Bochs Bochs RIP: 0010:[] [] internal_create_group= +0x2a/0x170 RSP: 0018:ffff880007b3fde8 EFLAGS: 00000246 RAX: 00000000ffffffef RBX: ffff880007b3d878 RCX: 00000000000007b4 RDX: ffffffff8152da50 RSI: 0000000000000000 RDI: ffff880007b3d878 RBP: ffff880007b3fe38 R08: ffff880007b3fde8 R09: 0000000000000000 R10: ffff88000783b4a8 R11: ffff880007b3d878 R12: ffffffff8152da50 R13: ffff880007b3d868 R14: 0000000000000000 R15: ffff880007b3d800 FS: 0000000002137880(0063) GS:ffff880007c00000(0000) knlGS:00000000000000= 00 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000422680 CR3: 0000000007b50000 CR4: 00000000000006b0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 0000000000000000 DR7: 0000000000000000 Process insmod (pid: 43, threadinfo ffff880007b3e000, task ffff880007afb9c= 0) Stack: ffff880007b3fe58 ffffffff811e66dd ffff880007b3fe58 ffffffff811e570b 0000000000000010 ffff880007b3d800 ffff880007a7b390 ffff880007b3d868 0000000000400920 ffff880007b3d800 ffff880007b3fe48 ffffffff8113cfc8 Call Trace: [] ? device_add+0x4bc/0x5af [] ? dev_set_name+0x3c/0x3e [] sysfs_create_group+0xe/0x12 [] blk_trace_init_sysfs+0x14/0x16 [] blk_register_queue+0x47/0xf7 [] add_disk+0xdf/0x290 [] loop_init+0xeb/0x1b8 [loop] [] ? 0xffffffffa0005fff [] do_one_initcall+0x7a/0x12e [] sys_init_module+0x9c/0x1e0 [] system_call_fastpath+0x16/0x1b Code: c3 55 48 89 e5 41 57 41 56 41 89 f6 41 55 41 54 49 89 d4 53 48 89 fb= 48 83 ec 28 48 85 ff 74 0b 85 f6 75 0b 48 83 7f 30 00 75 14 <0f> 0b eb fe = 48 83 7f 30 00 b9 ea ff ff ff 0f 84 18 01 00 00 49 RIP [] internal_create_group+0x2a/0x170 RSP ---[ end trace a123eb592043acad ]--- Signed-off-by: Namhyung Kim Cc: Laurent Vivier Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/block/loop.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/block/loop.c b/drivers/block/loop.c index dbf31ec9114db..8684c72de98dc 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1707,6 +1707,9 @@ static int __init loop_init(void) if (max_part > 0) part_shift = fls(max_part); + if ((1UL << part_shift) > DISK_MAX_PARTS) + return -EINVAL; + if (max_loop > 1UL << (MINORBITS - part_shift)) return -EINVAL; From 05ebf3493e8f0b71131a52b3bdc3d2a7e9c1f12b Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 24 May 2011 16:48:55 +0200 Subject: [PATCH 1890/2556] loop: handle on-demand devices correctly commit a1c15c59feee36267c43142a41152fbf7402afb6 upstream. When finding or allocating a loop device, loop_probe() did not take partition numbers into account so that it can result to a different device. Consider following example: $ sudo modprobe loop max_part=15 $ ls -l /dev/loop* brw-rw---- 1 root disk 7, 0 2011-05-24 22:16 /dev/loop0 brw-rw---- 1 root disk 7, 16 2011-05-24 22:16 /dev/loop1 brw-rw---- 1 root disk 7, 32 2011-05-24 22:16 /dev/loop2 brw-rw---- 1 root disk 7, 48 2011-05-24 22:16 /dev/loop3 brw-rw---- 1 root disk 7, 64 2011-05-24 22:16 /dev/loop4 brw-rw---- 1 root disk 7, 80 2011-05-24 22:16 /dev/loop5 brw-rw---- 1 root disk 7, 96 2011-05-24 22:16 /dev/loop6 brw-rw---- 1 root disk 7, 112 2011-05-24 22:16 /dev/loop7 $ sudo mknod /dev/loop8 b 7 128 $ sudo losetup /dev/loop8 ~/temp/disk-with-3-parts.img $ sudo losetup -a /dev/loop128: [0805]:278201 (/home/namhyung/temp/disk-with-3-parts.img) $ ls -l /dev/loop* brw-rw---- 1 root disk 7, 0 2011-05-24 22:16 /dev/loop0 brw-rw---- 1 root disk 7, 16 2011-05-24 22:16 /dev/loop1 brw-rw---- 1 root disk 7, 2048 2011-05-24 22:18 /dev/loop128 brw-rw---- 1 root disk 7, 2049 2011-05-24 22:18 /dev/loop128p1 brw-rw---- 1 root disk 7, 2050 2011-05-24 22:18 /dev/loop128p2 brw-rw---- 1 root disk 7, 2051 2011-05-24 22:18 /dev/loop128p3 brw-rw---- 1 root disk 7, 32 2011-05-24 22:16 /dev/loop2 brw-rw---- 1 root disk 7, 48 2011-05-24 22:16 /dev/loop3 brw-rw---- 1 root disk 7, 64 2011-05-24 22:16 /dev/loop4 brw-rw---- 1 root disk 7, 80 2011-05-24 22:16 /dev/loop5 brw-rw---- 1 root disk 7, 96 2011-05-24 22:16 /dev/loop6 brw-rw---- 1 root disk 7, 112 2011-05-24 22:16 /dev/loop7 brw-r--r-- 1 root root 7, 128 2011-05-24 22:17 /dev/loop8 After this patch, /dev/loop8 - instead of /dev/loop128 - was accessed correctly. In addition, 'range' passed to blk_register_region() should include all range of dev_t that LOOP_MAJOR can address. It does not need to be limited by partition numbers unless 'max_loop' param was specified. Signed-off-by: Namhyung Kim Cc: Laurent Vivier Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/block/loop.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 8684c72de98dc..f49e43fd73d71 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1674,7 +1674,7 @@ static struct kobject *loop_probe(dev_t dev, int *part, void *data) struct kobject *kobj; mutex_lock(&loop_devices_mutex); - lo = loop_init_one(dev & MINORMASK); + lo = loop_init_one(MINOR(dev) >> part_shift); kobj = lo ? get_disk(lo->lo_disk) : ERR_PTR(-ENOMEM); mutex_unlock(&loop_devices_mutex); @@ -1715,10 +1715,10 @@ static int __init loop_init(void) if (max_loop) { nr = max_loop; - range = max_loop; + range = max_loop << part_shift; } else { nr = 8; - range = 1UL << (MINORBITS - part_shift); + range = 1UL << MINORBITS; } if (register_blkdev(LOOP_MAJOR, "loop")) @@ -1757,7 +1757,7 @@ static void __exit loop_exit(void) unsigned long range; struct loop_device *lo, *next; - range = max_loop ? max_loop : 1UL << (MINORBITS - part_shift); + range = max_loop ? max_loop << part_shift : 1UL << MINORBITS; list_for_each_entry_safe(lo, next, &loop_devices, lo_list) loop_del_one(lo); From 2617bae63c8a99987a2eb05739bd1ee087167351 Mon Sep 17 00:00:00 2001 From: Vikram Narayanan Date: Tue, 24 May 2011 20:58:48 +0200 Subject: [PATCH 1891/2556] i2c/writing-clients: Fix foo_driver.id_table commit 3116c86033079a1d4d4e84c40028f96b614843b8 upstream. The i2c_device_id structure variable's name is not used in the i2c_driver structure. Signed-off-by: Vikram Narayanan Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- Documentation/i2c/writing-clients | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/i2c/writing-clients b/Documentation/i2c/writing-clients index 5ebf5af1d7160..5aa53374ea2a8 100644 --- a/Documentation/i2c/writing-clients +++ b/Documentation/i2c/writing-clients @@ -38,7 +38,7 @@ static struct i2c_driver foo_driver = { .name = "foo", }, - .id_table = foo_ids, + .id_table = foo_idtable, .probe = foo_probe, .remove = foo_remove, /* if device autodetection is needed: */ From 6ce06305461d90dbc5b23589127ed1a06f70bb51 Mon Sep 17 00:00:00 2001 From: Craig Shelley Date: Sun, 20 Mar 2011 13:51:13 +0000 Subject: [PATCH 1892/2556] USB: CP210x Add 4 Device IDs for AC-Services Devices commit 4eff0b40a7174896b860312910e0db51f2dcc567 upstream. This patch adds 4 device IDs for CP2102 based devices manufactured by AC-Services. See http://www.ac-services.eu for further info. Signed-off-by: Craig Shelley Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp210x.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 735ea03157aba..66767ce0d7bef 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -113,6 +113,10 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */ { USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */ { USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */ + { USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */ + { USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */ + { USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */ + { USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA71) }, /* Infinity GPS-MIC-1 Radio Monophone */ From 936798e04ac9246256f7a378786c47b3dc24988b Mon Sep 17 00:00:00 2001 From: Elizabeth Jennifer Myers Date: Sat, 16 Apr 2011 14:49:51 -0400 Subject: [PATCH 1893/2556] USB: moto_modem: Add USB identifier for the Motorola VE240. commit 3938a0b32dc12229e76735679b37095bc2bc1578 upstream. Tested on my phone, the ttyUSB device is created and is fully functional. Signed-off-by: Elizabeth Jennifer Myers Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/moto_modem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/moto_modem.c b/drivers/usb/serial/moto_modem.c index 653465f61d4a9..e2bfecc464024 100644 --- a/drivers/usb/serial/moto_modem.c +++ b/drivers/usb/serial/moto_modem.c @@ -25,6 +25,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x05c6, 0x3197) }, /* unknown Motorola phone */ { USB_DEVICE(0x0c44, 0x0022) }, /* unknown Mororola phone */ { USB_DEVICE(0x22b8, 0x2a64) }, /* Motorola KRZR K1m */ + { USB_DEVICE(0x22b8, 0x2c84) }, /* Motorola VE240 phone */ { USB_DEVICE(0x22b8, 0x2c64) }, /* Motorola V950 phone */ { }, }; From c682a5150e502427f882d063cbd3ad3b497bb558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benedek=20L=C3=A1szl=C3=B3?= Date: Wed, 20 Apr 2011 03:22:21 +0200 Subject: [PATCH 1894/2556] USB: serial: ftdi_sio: adding support for TavIR STK500 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 37909fe588c9e09ab57cd267e98678a17ceda64a upstream. Adding support for the TavIR STK500 (id 0403:FA33) Atmel AVR programmer device based on FTDI FT232RL. Signed-off-by: Benedek László Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/ftdi_sio_ids.h | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 31bba6ddfb279..3c2d9d685c7b1 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -566,6 +566,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_IBS_APP70_PID) }, { USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TAVIR_STK500_PID) }, /* * ELV devices: */ diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 4e873ce579bbc..f8e3f0dc34e83 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -491,6 +491,11 @@ /* www.canusb.com Lawicel CANUSB device (FTDI_VID) */ #define FTDI_CANUSB_PID 0xFFA8 /* Product Id */ +/* + * TavIR AVR product ids (FTDI_VID) + */ +#define FTDI_TAVIR_STK500_PID 0xFA33 /* STK500 AVR programmer */ + /********************************/ From 3c9e61a5a06bbcf9be1d015eab2bc928ab619de1 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Tue, 26 Apr 2011 19:08:36 +0200 Subject: [PATCH 1895/2556] USB: gadget: g_multi: fixed vendor and product ID in inf files commit 7701846fd52f86dffe50715e0e63154088b7c982 upstream. Commit 1c6529e92b "USB: gadget: g_multi: fixed vendor and product ID" replaced g_multi's vendor and product ID with proper ID's from Linux Foundation. This commit now updates INF files in the Documentation/usb directory which were omitted in the original commit. Signed-off-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman --- Documentation/usb/linux-cdc-acm.inf | 4 ++-- Documentation/usb/linux.inf | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/usb/linux-cdc-acm.inf b/Documentation/usb/linux-cdc-acm.inf index 612e7220fb295..37a02ce548417 100644 --- a/Documentation/usb/linux-cdc-acm.inf +++ b/Documentation/usb/linux-cdc-acm.inf @@ -90,10 +90,10 @@ ServiceBinary=%12%\USBSER.sys [SourceDisksFiles] [SourceDisksNames] [DeviceList] -%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_0525&PID_A4AB&MI_02 +%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_1D6B&PID_0104&MI_02 [DeviceList.NTamd64] -%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_0525&PID_A4AB&MI_02 +%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_1D6B&PID_0104&MI_02 ;------------------------------------------------------------------------------ diff --git a/Documentation/usb/linux.inf b/Documentation/usb/linux.inf index 4dee95851224a..4ffa715b0ae88 100644 --- a/Documentation/usb/linux.inf +++ b/Documentation/usb/linux.inf @@ -18,15 +18,15 @@ DriverVer = 06/21/2006,6.0.6000.16384 ; Decoration for x86 architecture [LinuxDevices.NTx86] -%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_0525&PID_a4ab&MI_00 +%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_1d6b&PID_0104&MI_00 ; Decoration for x64 architecture [LinuxDevices.NTamd64] -%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_0525&PID_a4ab&MI_00 +%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_1d6b&PID_0104&MI_00 ; Decoration for ia64 architecture [LinuxDevices.NTia64] -%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_0525&PID_a4ab&MI_00 +%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_1d6b&PID_0104&MI_00 ;@@@ This is the common setting for setup [ControlFlags] From 1dc080723321b37c5451f381ffad52e4cf52a89d Mon Sep 17 00:00:00 2001 From: Hermann Kneissel Date: Fri, 29 Apr 2011 08:58:43 +0200 Subject: [PATCH 1896/2556] USB: gamin_gps: Fix for data transfer problems in native mode commit b4026c4584cd70858d4d3450abfb1cd0714d4f32 upstream. This patch fixes a problem where data received from the gps is sometimes transferred incompletely to the serial port. If used in native mode now all data received via the bulk queue will be forwarded to the serial port. Signed-off-by: Hermann Kneissel Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/garmin_gps.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 26710b1899185..456447e03383c 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1,7 +1,7 @@ /* * Garmin GPS driver * - * Copyright (C) 2006-2009 Hermann Kneissel herkne@users.sourceforge.net + * Copyright (C) 2006-2011 Hermann Kneissel herkne@gmx.de * * The latest version of the driver can be found at * http://sourceforge.net/projects/garmin-gps/ @@ -51,7 +51,7 @@ static int debug; */ #define VERSION_MAJOR 0 -#define VERSION_MINOR 33 +#define VERSION_MINOR 36 #define _STR(s) #s #define _DRIVER_VERSION(a, b) "v" _STR(a) "." _STR(b) @@ -410,6 +410,7 @@ static int gsp_send_ack(struct garmin_data *garmin_data_p, __u8 pkt_id) */ static int gsp_rec_packet(struct garmin_data *garmin_data_p, int count) { + unsigned long flags; const __u8 *recpkt = garmin_data_p->inbuffer+GSP_INITIAL_OFFSET; __le32 *usbdata = (__le32 *) garmin_data_p->inbuffer; @@ -458,7 +459,9 @@ static int gsp_rec_packet(struct garmin_data *garmin_data_p, int count) /* if this was an abort-transfer command, flush all queued data. */ if (isAbortTrfCmnd(garmin_data_p->inbuffer)) { + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= FLAGS_DROP_DATA; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); pkt_clear(garmin_data_p); } @@ -943,7 +946,7 @@ static int garmin_open(struct tty_struct *tty, struct usb_serial_port *port) spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->mode = initial_mode; garmin_data_p->count = 0; - garmin_data_p->flags = 0; + garmin_data_p->flags &= FLAGS_SESSION_REPLY1_SEEN; spin_unlock_irqrestore(&garmin_data_p->lock, flags); /* shutdown any bulk reads that might be going on */ @@ -1178,7 +1181,8 @@ static int garmin_write_room(struct tty_struct *tty) static void garmin_read_process(struct garmin_data *garmin_data_p, - unsigned char *data, unsigned data_length) + unsigned char *data, unsigned data_length, + int bulk_data) { unsigned long flags; @@ -1193,7 +1197,8 @@ static void garmin_read_process(struct garmin_data *garmin_data_p, send it directly to the tty port */ if (garmin_data_p->flags & FLAGS_QUEUING) { pkt_add(garmin_data_p, data, data_length); - } else if (getLayerId(data) == GARMIN_LAYERID_APPL) { + } else if (bulk_data || + getLayerId(data) == GARMIN_LAYERID_APPL) { spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= APP_RESP_SEEN; @@ -1237,7 +1242,7 @@ static void garmin_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - garmin_read_process(garmin_data_p, data, urb->actual_length); + garmin_read_process(garmin_data_p, data, urb->actual_length, 1); if (urb->actual_length == 0 && 0 != (garmin_data_p->flags & FLAGS_BULK_IN_RESTART)) { @@ -1346,7 +1351,7 @@ static void garmin_read_int_callback(struct urb *urb) __func__, garmin_data_p->serial_num); } - garmin_read_process(garmin_data_p, data, urb->actual_length); + garmin_read_process(garmin_data_p, data, urb->actual_length, 0); port->interrupt_in_urb->dev = port->serial->dev; retval = usb_submit_urb(urb, GFP_ATOMIC); @@ -1461,6 +1466,7 @@ static int garmin_attach(struct usb_serial *serial) garmin_data_p->timer.function = timeout_handler; garmin_data_p->port = port; garmin_data_p->state = 0; + garmin_data_p->flags = 0; garmin_data_p->count = 0; usb_set_serial_port_data(port, garmin_data_p); From 33730250bc0b5d432e7fb37e5a95ff77e10e1364 Mon Sep 17 00:00:00 2001 From: "Marius B. Kotsbak" Date: Mon, 21 Mar 2011 23:27:21 +0100 Subject: [PATCH 1897/2556] Bind only modem AT command endpoint to option module. commit 15b2f3204a5c878c32939094775fb7349f707263 upstream. Network interface is handled by upcoming gt_b3730 module. Removed "GT-B3710" from comment, it is another modem with another USB ID. Signed-off-by: Marius B. Kotsbak Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index d77ff0435896b..4001f7630a547 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -972,7 +972,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD100) }, { USB_DEVICE(CELOT_VENDOR_ID, CELOT_PRODUCT_CT680M) }, /* CT-650 CDMA 450 1xEVDO modem */ { USB_DEVICE(ONDA_VENDOR_ID, ONDA_MT825UP) }, /* ONDA MT825UP modem */ - { USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_GT_B3730, USB_CLASS_CDC_DATA, 0x00, 0x00) }, /* Samsung GT-B3730/GT-B3710 LTE USB modem.*/ + { USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_GT_B3730, USB_CLASS_CDC_DATA, 0x00, 0x00) }, /* Samsung GT-B3730 LTE USB modem.*/ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); @@ -1109,6 +1109,12 @@ static int option_probe(struct usb_serial *serial, serial->interface->cur_altsetting->desc.bInterfaceNumber == 1) return -ENODEV; + /* Don't bind network interface on Samsung GT-B3730, it is handled by a separate module */ + if (serial->dev->descriptor.idVendor == SAMSUNG_VENDOR_ID && + serial->dev->descriptor.idProduct == SAMSUNG_PRODUCT_GT_B3730 && + serial->interface->cur_altsetting->desc.bInterfaceClass != USB_CLASS_CDC_DATA) + return -ENODEV; + data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); if (!data) From ed25d77e109b1c90e999e77f51f00b6bf6b75bae Mon Sep 17 00:00:00 2001 From: Erik Slagter Date: Wed, 11 May 2011 12:06:55 +0200 Subject: [PATCH 1898/2556] USB: cdc_acm: Fix oops when Droids MuIn LCD is connected commit fd5054c169d29747a44b4e1419ff47f57ae82dbc upstream. The Droids MuIn LCD operates like a serial remote terminal. Data received are displayed directly on the LCD. This patch fixes the kernel null pointer oops when it is plugged in. Add NO_DATA_INTERFACE quirk to tell the driver that "control" and "data" interfaces are not separated for this device, which prevents dereferencing a null pointer in the device probe code. Signed-off-by: Erik Slagter Signed-off-by: Maxin B. John Tested-by: Erik Slagter Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 13 +++++++++++-- drivers/usb/class/cdc-acm.h | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 30bb8d053c2ee..83589f49efde5 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -946,7 +946,7 @@ static int acm_probe(struct usb_interface *intf, u8 ac_management_function = 0; u8 call_management_function = 0; int call_interface_num = -1; - int data_interface_num; + int data_interface_num = -1; unsigned long quirks; int num_rx_buf; int i; @@ -1030,7 +1030,11 @@ static int acm_probe(struct usb_interface *intf, if (!union_header) { if (call_interface_num > 0) { dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n"); - data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); + /* quirks for Droids MuIn LCD */ + if (quirks & NO_DATA_INTERFACE) + data_interface = usb_ifnum_to_if(usb_dev, 0); + else + data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); control_interface = intf; } else { if (intf->cur_altsetting->desc.bNumEndpoints != 3) { @@ -1622,6 +1626,11 @@ static const struct usb_device_id acm_ids[] = { .driver_info = NOT_A_MODEM, }, + /* Support for Droids MuIn LCD */ + { USB_DEVICE(0x04d8, 0x000b), + .driver_info = NO_DATA_INTERFACE, + }, + /* control interfaces without any protocol set */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_PROTO_NONE) }, diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 5eeb570b9a617..a2446d6caf05e 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -137,3 +137,4 @@ struct acm { #define SINGLE_RX_URB 2 #define NO_CAP_LINE 4 #define NOT_A_MODEM 8 +#define NO_DATA_INTERFACE 16 From 0f63996abc2163c56997ade41cb01632a66beb28 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 5 May 2011 19:08:09 -0700 Subject: [PATCH 1899/2556] xhci: Fix bug in control transfer cancellation. commit 3abeca998a44205cfd837fa0bf1f7c24f8294acb upstream. When the xHCI driver attempts to cancel a transfer, it issues a Stop Endpoint command and waits for the host controller to indicate which TRB it was in the middle of processing. The host will put an event TRB with completion code COMP_STOP on the event ring if it stops on a control transfer TRB (or other types of transfer TRBs). The ring handling code is supposed to set ep->stopped_trb to the TRB that the host stopped on when this happens. Unfortunately, there is a long-standing bug in the control transfer completion code. It doesn't actually check to see if COMP_STOP is set before attempting to process the transfer based on which part of the control TD completed. So when we get an event on the data phase of the control TRB with COMP_STOP set, it thinks it's a normal completion of the transfer and doesn't set ep->stopped_td or ep->stopped_trb. When the ring handling code goes on to process the completion of the Stop Endpoint command, it sees that ep->stopped_trb is not a part of the TD it's trying to cancel. It thinks the hardware has its enqueue pointer somewhere further up in the ring, and thinks it's safe to turn the control TRBs into no-op TRBs. Since the hardware was in the middle of the control TRBs to be cancelled, the proper software behavior is to issue a Set TR dequeue pointer command. It turns out that the NEC host controllers can handle active TRBs being set to no-op TRBs after a stop endpoint command, but other host controllers have issues with this out-of-spec software behavior. Fix this behavior. This patch should be backported to kernels as far back as 2.6.31, but it may be a bit challenging, since process_ctrl_td() was introduced in some refactoring done in 2.6.36, and some endian-safe patches added in 2.6.40 that touch the same lines. Signed-off-by: Sarah Sharp Cc: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-ring.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index d3f0406f6f20a..2d7fa995ea39b 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1532,6 +1532,9 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, else *status = 0; break; + case COMP_STOP_INVAL: + case COMP_STOP: + return finish_td(xhci, td, event_trb, event, ep, status, false); default: if (!xhci_requires_manual_halt_cleanup(xhci, ep_ctx, trb_comp_code)) @@ -1576,15 +1579,12 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, } } else { /* Maybe the event was for the data stage? */ - if (trb_comp_code != COMP_STOP_INVAL) { - /* We didn't stop on a link TRB in the middle */ - td->urb->actual_length = - td->urb->transfer_buffer_length - - TRB_LEN(event->transfer_len); - xhci_dbg(xhci, "Waiting for status " - "stage event\n"); - return 0; - } + td->urb->actual_length = + td->urb->transfer_buffer_length - + TRB_LEN(le32_to_cpu(event->transfer_len)); + xhci_dbg(xhci, "Waiting for status " + "stage event\n"); + return 0; } } From 73758f998dc8b4528e031d3428573bb3aef5c77b Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Fri, 13 May 2011 17:03:02 +0200 Subject: [PATCH 1900/2556] usb/gadget: at91sam9g20 fix end point max packet size commit bf1f0a05d472e33dda8e5e69525be1584cdbd03a upstream. on 9g20 they are the same as the 9260 Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/at91_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index bdec36acd0fa4..c3bb5ec393aaf 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1767,7 +1767,7 @@ static int __init at91udc_probe(struct platform_device *pdev) } /* newer chips have more FIFO memory than rm9200 */ - if (cpu_is_at91sam9260()) { + if (cpu_is_at91sam9260() || cpu_is_at91sam9g20()) { udc->ep[0].maxpacket = 64; udc->ep[3].maxpacket = 64; udc->ep[4].maxpacket = 512; From ac57b58b06df77d17383f14e87da85ddc5e7c35b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 13 May 2011 16:53:48 +0300 Subject: [PATCH 1901/2556] usb: gadget: rndis: don't test against req->length commit 472b91274a6c6857877b5caddb875dcb5ecdfcb8 upstream. composite.c always sets req->length to zero and expects function driver's setup handlers to return the amount of bytes to be used on req->length. If we test against req->length w_length will always be greater than req->length thus making us always stall that particular SEND_ENCAPSULATED_COMMAND request. Tested against a Windows XP SP3. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/f_rndis.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 882484a40398b..fa12ec8364eff 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -420,8 +420,7 @@ rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) */ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) | USB_CDC_SEND_ENCAPSULATED_COMMAND: - if (w_length > req->length || w_value - || w_index != rndis->ctrl_id) + if (w_value || w_index != rndis->ctrl_id) goto invalid; /* read the request; process it later */ value = w_length; From b8f653f794ec459132a903c7b7d6aa40bbe5c2a1 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 13 May 2011 13:10:01 -0700 Subject: [PATCH 1902/2556] xhci: Fix full speed bInterval encoding. commit b513d44751bfb609a3c20463f764c8ce822d63e9 upstream. Dmitry's patch dfa49c4ad120a784ef1ff0717168aa79f55a483a USB: xhci - fix math in xhci_get_endpoint_interval() introduced a bug. The USB 2.0 spec says that full speed isochronous endpoints' bInterval must be decoded as an exponent to a power of two (e.g. interval = 2^(bInterval - 1)). Full speed interrupt endpoints, on the other hand, don't use exponents, and the interval in frames is encoded straight into bInterval. Dmitry's patch was supposed to fix up the full speed isochronous to parse bInterval as an exponent, but instead it changed the *interrupt* endpoint bInterval decoding. The isochronous endpoint encoding was the same. This caused full speed devices with interrupt endpoints (including mice, hubs, and USB to ethernet devices) to fail under NEC 0.96 xHCI host controllers: [ 100.909818] xhci_hcd 0000:06:00.0: add ep 0x83, slot id 1, new drop flags = 0x0, new add flags = 0x99, new slot info = 0x38100000 [ 100.909821] xhci_hcd 0000:06:00.0: xhci_check_bandwidth called for udev ffff88011f0ea000 ... [ 100.910187] xhci_hcd 0000:06:00.0: ERROR: unexpected command completion code 0x11. [ 100.910190] xhci_hcd 0000:06:00.0: xhci_reset_bandwidth called for udev ffff88011f0ea000 When the interrupt endpoint was added and a Configure Endpoint command was issued to the host, the host controller would return a very odd error message (0x11 means "Slot Not Enabled", which isn't true because the slot was enabled). Probably the host controller was getting very confused with the bad encoding. Signed-off-by: Sarah Sharp Cc: Dmitry Torokhov Reported-by: Thomas Lindroth Tested-by: Thomas Lindroth Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 0de3100053271..217299869887b 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -992,12 +992,12 @@ static inline unsigned int xhci_get_endpoint_interval(struct usb_device *udev, break; case USB_SPEED_FULL: - if (usb_endpoint_xfer_int(&ep->desc)) { + if (usb_endpoint_xfer_isoc(&ep->desc)) { interval = xhci_parse_exponent_interval(udev, ep); break; } /* - * Fall through for isochronous endpoint interval decoding + * Fall through for interrupt endpoint interval decoding * since it uses the same rules as low speed interrupt * endpoints. */ From a66ccccb5525a5606a2000066889a71e0c8a9867 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Mon, 16 May 2011 13:09:08 -0700 Subject: [PATCH 1903/2556] xhci: Fix memory leak in ring cache deallocation. commit 30f89ca021c3e584b61bc5a14eede89f74b2e826 upstream. When an endpoint ring is freed, it is either cached in a per-device ring cache, or simply freed if the ring cache is full. If the ring was added to the cache, then virt_dev->num_rings_cached is incremented. The cache is designed to hold up to 31 endpoint rings, in array indexes 0 to 30. When the device is freed (when the slot was disabled), xhci_free_virt_device() is called, it would free the cached rings in array indexes 0 to virt_dev->num_rings_cached. Unfortunately, the original code in xhci_free_or_cache_endpoint_ring() would put the first entry into the ring cache in array index 1, instead of array index 0. This was caused by the second assignment to rings_cached: rings_cached = virt_dev->num_rings_cached; if (rings_cached < XHCI_MAX_RINGS_CACHED) { virt_dev->num_rings_cached++; rings_cached = virt_dev->num_rings_cached; virt_dev->ring_cache[rings_cached] = virt_dev->eps[ep_index].ring; This meant that when the device was freed, cached rings with indexes 0 to N would be freed, and the last cached ring in index N+1 would not be freed. When the driver was unloaded, this caused interesting messages like: xhci_hcd 0000:06:00.0: dma_pool_destroy xHCI ring segments, ffff880063040000 busy This should be queued to stable kernels back to 2.6.33. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 217299869887b..175434ddc2168 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -207,14 +207,13 @@ void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci, rings_cached = virt_dev->num_rings_cached; if (rings_cached < XHCI_MAX_RINGS_CACHED) { - virt_dev->num_rings_cached++; - rings_cached = virt_dev->num_rings_cached; virt_dev->ring_cache[rings_cached] = virt_dev->eps[ep_index].ring; + virt_dev->num_rings_cached++; xhci_dbg(xhci, "Cached old ring, " "%d ring%s cached\n", - rings_cached, - (rings_cached > 1) ? "s" : ""); + virt_dev->num_rings_cached, + (virt_dev->num_rings_cached > 1) ? "s" : ""); } else { xhci_ring_free(xhci, virt_dev->eps[ep_index].ring); xhci_dbg(xhci, "Ring cache full (%d rings), " From 6f10f1a521400f591dade45ded73d3573df7778f Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 12 May 2011 18:06:37 -0700 Subject: [PATCH 1904/2556] xhci: Fix memory leak bug when dropping endpoints commit 834cb0fc4712a3b21c6b8c5cb55bd13607191311 upstream. When the USB core wants to change to an alternate interface setting that doesn't include an active endpoint, or de-configuring the device, the xHCI driver needs to issue a Configure Endpoint command to tell the host to drop some endpoints from the schedule. After the command completes, the xHCI driver needs to free rings for any endpoints that were dropped. Unfortunately, the xHCI driver wasn't actually freeing the endpoint rings for dropped endpoints. The rings would be freed if the endpoint's information was simply changed (and a new ring was installed), but dropped endpoints never had their rings freed. This caused errors when the ring segment DMA pool was freed when the xHCI driver was unloaded: [ 5582.883995] xhci_hcd 0000:06:00.0: dma_pool_destroy xHCI ring segments, ffff88003371d000 busy [ 5582.884002] xhci_hcd 0000:06:00.0: dma_pool_destroy xHCI ring segments, ffff880033716000 busy [ 5582.884011] xhci_hcd 0000:06:00.0: dma_pool_destroy xHCI ring segments, ffff880033455000 busy [ 5582.884018] xhci_hcd 0000:06:00.0: Freed segment pool [ 5582.884026] xhci_hcd 0000:06:00.0: Freed device context pool [ 5582.884033] xhci_hcd 0000:06:00.0: Freed small stream array pool [ 5582.884038] xhci_hcd 0000:06:00.0: Freed medium stream array pool [ 5582.884048] xhci_hcd 0000:06:00.0: xhci_stop completed - status = 1 [ 5582.884061] xhci_hcd 0000:06:00.0: USB bus 3 deregistered [ 5582.884193] xhci_hcd 0000:06:00.0: PCI INT A disabled Fix this issue and free endpoint rings when their endpoints are successfully dropped. This patch should be backported to kernels as old as 2.6.31. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 150349d1fdac5..c39f12fe95898 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1636,8 +1636,17 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) xhci_dbg_ctx(xhci, virt_dev->out_ctx, LAST_CTX_TO_EP_NUM(slot_ctx->dev_info)); + /* Free any rings that were dropped, but not changed. */ + for (i = 1; i < 31; ++i) { + if ((ctrl_ctx->drop_flags & (1 << (i + 1))) && + !(ctrl_ctx->add_flags & (1 << (i + 1)))) + xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i); + } xhci_zero_in_ctx(xhci, virt_dev); - /* Install new rings and free or cache any old rings */ + /* + * Install any rings for completely new endpoints or changed endpoints, + * and free or cache any old rings from changed endpoints. + */ for (i = 1; i < 31; ++i) { if (!virt_dev->eps[i].new_ring) continue; From 371b30ea7c1e31126c9775935fa7c9a113a394d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Ga=C5=82czy=C5=84ski?= Date: Sun, 15 May 2011 11:41:54 +0200 Subject: [PATCH 1905/2556] USB: option: add support for Huawei E353 device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 610ba42f29c3dfa46a05ff8c2cadc29f544ff76d upstream. I am sharing patch to the devices/usb/serial/option.c. This allows operation of Huawei E353 broadband modem using the “option” driver. The patch simply adds new constant with proper product ID and an entry to usb_device_id. I worked on the 2.6.38.6 sources. Tested on Dell inspiron 1764 (i3 core cpu) and brand new Huawei E353 modem, Fedora 15 beta. Looking at the type of change, i doubt it has potential to introduce problems in other parts of kernel or the driver itself. Signed-off-by: Marcin Galczynski Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 4001f7630a547..318dd00040a3f 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -149,6 +149,7 @@ static void option_instat_callback(struct urb *urb); #define HUAWEI_PRODUCT_K3765 0x1465 #define HUAWEI_PRODUCT_E14AC 0x14AC #define HUAWEI_PRODUCT_ETS1220 0x1803 +#define HUAWEI_PRODUCT_E353 0x1506 #define QUANTA_VENDOR_ID 0x0408 #define QUANTA_PRODUCT_Q101 0xEA02 @@ -532,6 +533,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ETS1220, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x01) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V740) }, From a6cde63c3d193a021257ce07fe16b4936902a2b7 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 16 May 2011 12:15:19 -0400 Subject: [PATCH 1906/2556] OHCI: fix regression caused by nVidia shutdown workaround commit 2b7aaf503d56216b847c8265421d2a7d9b42df3e upstream. This patch (as1463) fixes a regression caused by commit 3df7169e73fc1d71a39cffeacc969f6840cdf52b (OHCI: work around for nVidia shutdown problem). The original problem encountered by people using NVIDIA chipsets was that USB devices were not turning off when the system shut down. For example, the LED on an optical mouse would remain on, draining a laptop's battery. The problem was caused by a bug in the chipset; an OHCI controller in the Reset state would continue to drive a bus reset signal even after system shutdown. The workaround was to put the controllers into the Suspend state instead. It turns out that later NVIDIA chipsets do not suffer from this bug. Instead some have the opposite bug: If a system is shut down while an OHCI controller is in the Suspend state, USB devices remain powered! On other systems, shutting down with a Suspended controller causes the system to reboot immediately. Thus, working around the original bug on some machines exposes other bugs on other machines. The best solution seems to be to limit the workaround to OHCI controllers with a low-numbered PCI product ID. I don't know exactly at what point NVIDIA changed their chipsets; the value used here is a guess. So far it was worked out okay for all the people who have tested it. This fixes Bugzilla #35032. Signed-off-by: Alan Stern Tested-by: Andre "Osku" Schmidt Tested-by: Yury Siamashka Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-pci.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 36ee9a666e937..702f4c74dc97a 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -207,10 +207,18 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd) */ static int ohci_quirk_nvidia_shutdown(struct usb_hcd *hcd) { + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); struct ohci_hcd *ohci = hcd_to_ohci(hcd); - ohci->flags |= OHCI_QUIRK_SHUTDOWN; - ohci_dbg(ohci, "enabled nVidia shutdown quirk\n"); + /* Evidently nVidia fixed their later hardware; this is a guess at + * the changeover point. + */ +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_USB 0x026d + + if (pdev->device < PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_USB) { + ohci->flags |= OHCI_QUIRK_SHUTDOWN; + ohci_dbg(ohci, "enabled nVidia shutdown quirk\n"); + } return 0; } From d8fe0246330b97a736c7c9986c70eadc7b014581 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 17 May 2011 17:27:12 -0400 Subject: [PATCH 1907/2556] USB: remove remaining usages of hcd->state from usbcore and fix regression commit 69fff59de4d844f8b4c2454c3c23d32b69dcbfd7 upstream. This patch (as1467) removes the last usages of hcd->state from usbcore. We no longer check to see if an interrupt handler finds that a controller has died; instead we rely on host controller drivers to make an explicit call to usb_hc_died(). This fixes a regression introduced by commit 9b37596a2e860404503a3f2a6513db60c296bfdc (USB: move usbcore away from hcd->state). It used to be that when a controller shared an IRQ with another device and an interrupt arrived while hcd->state was set to HC_STATE_HALT, the interrupt handler would be skipped. The commit removed that test; as a result the current code doesn't skip calling the handler and ends up believing the controller has died, even though it's only temporarily stopped. The solution is to ignore HC_STATE_HALT following the handler's return. As a consequence of this change, several of the host controller drivers need to be modified. They can no longer implicitly rely on usbcore realizing that a controller has died because of hcd->state. The patch adds calls to usb_hc_died() in the appropriate places. The patch also changes a few of the interrupt handlers. They don't expect to be called when hcd->state is equal to HC_STATE_HALT, even if the controller is still alive. Early returns were added to avoid any confusion. Signed-off-by: Alan Stern Tested-by: Manuel Lauss CC: Rodolfo Giometti CC: Olav Kongas Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 5 +---- drivers/usb/host/ehci-hcd.c | 4 +++- drivers/usb/host/ehci-sched.c | 8 ++++++-- drivers/usb/host/isp116x-hcd.c | 1 + drivers/usb/host/ohci-hcd.c | 4 +++- drivers/usb/host/oxu210hp-hcd.c | 6 +++++- 6 files changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index fd95b9e94bb10..a27dd223a3334 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -983,7 +983,7 @@ static int register_root_hub(struct usb_hcd *hcd) spin_unlock_irq (&hcd_root_hub_lock); /* Did the HC die before the root hub was registered? */ - if (HCD_DEAD(hcd) || hcd->state == HC_STATE_HALT) + if (HCD_DEAD(hcd)) usb_hc_died (hcd); /* This time clean up */ } @@ -2103,9 +2103,6 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd) rc = IRQ_NONE; } else { set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); - - if (unlikely(hcd->state == HC_STATE_HALT)) - usb_hc_died(hcd); rc = IRQ_HANDLED; } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 74dcf49bd015a..44849262b85e5 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -776,8 +776,9 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) goto dead; } + /* Shared IRQ? */ masked_status = status & INTR_MASK; - if (!masked_status) { /* irq sharing? */ + if (!masked_status || unlikely(hcd->state == HC_STATE_HALT)) { spin_unlock(&ehci->lock); return IRQ_NONE; } @@ -872,6 +873,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) dead: ehci_reset(ehci); ehci_writel(ehci, 0, &ehci->regs->configured_flag); + usb_hc_died(hcd); /* generic layer kills/unlinks all urbs, then * uses ehci_stop to clean up the rest */ diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index aa46f57f9ec8f..9dc7c19a8204e 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -471,8 +471,10 @@ static int enable_periodic (struct ehci_hcd *ehci) */ status = handshake_on_error_set_halt(ehci, &ehci->regs->status, STS_PSS, 0, 9 * 125); - if (status) + if (status) { + usb_hc_died(ehci_to_hcd(ehci)); return status; + } cmd = ehci_readl(ehci, &ehci->regs->command) | CMD_PSE; ehci_writel(ehci, cmd, &ehci->regs->command); @@ -510,8 +512,10 @@ static int disable_periodic (struct ehci_hcd *ehci) */ status = handshake_on_error_set_halt(ehci, &ehci->regs->status, STS_PSS, STS_PSS, 9 * 125); - if (status) + if (status) { + usb_hc_died(ehci_to_hcd(ehci)); return status; + } cmd = ehci_readl(ehci, &ehci->regs->command) & ~CMD_PSE; ehci_writel(ehci, cmd, &ehci->regs->command); diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 0da7fc05f4537..9e3ed9a670630 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -612,6 +612,7 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd) /* IRQ's are off, we do no DMA, perfectly ready to die ... */ hcd->state = HC_STATE_HALT; + usb_hc_died(hcd); ret = IRQ_HANDLED; goto done; } diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 759a12ff8048b..46b884aa3fbae 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -773,6 +773,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) if (ints == ~(u32)0) { disable (ohci); ohci_dbg (ohci, "device removed!\n"); + usb_hc_died(hcd); return IRQ_HANDLED; } @@ -780,7 +781,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) ints &= ohci_readl(ohci, ®s->intrenable); /* interrupt for some other device? */ - if (ints == 0) + if (ints == 0 || unlikely(hcd->state == HC_STATE_HALT)) return IRQ_NOTMINE; if (ints & OHCI_INTR_UE) { @@ -797,6 +798,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) } else { disable (ohci); ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n"); + usb_hc_died(hcd); } ohci_dump (ohci, 1); diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index e0cb12b573f91..6d6e2b38a98a1 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -1884,6 +1884,7 @@ static int enable_periodic(struct oxu_hcd *oxu) status = handshake(oxu, &oxu->regs->status, STS_PSS, 0, 9 * 125); if (status != 0) { oxu_to_hcd(oxu)->state = HC_STATE_HALT; + usb_hc_died(oxu_to_hcd(oxu)); return status; } @@ -1909,6 +1910,7 @@ static int disable_periodic(struct oxu_hcd *oxu) status = handshake(oxu, &oxu->regs->status, STS_PSS, STS_PSS, 9 * 125); if (status != 0) { oxu_to_hcd(oxu)->state = HC_STATE_HALT; + usb_hc_died(oxu_to_hcd(oxu)); return status; } @@ -2449,8 +2451,9 @@ static irqreturn_t oxu210_hcd_irq(struct usb_hcd *hcd) goto dead; } + /* Shared IRQ? */ status &= INTR_MASK; - if (!status) { /* irq sharing? */ + if (!status || unlikely(hcd->state == HC_STATE_HALT)) { spin_unlock(&oxu->lock); return IRQ_NONE; } @@ -2516,6 +2519,7 @@ static irqreturn_t oxu210_hcd_irq(struct usb_hcd *hcd) dead: ehci_reset(oxu); writel(0, &oxu->regs->configured_flag); + usb_hc_died(hcd); /* generic layer kills/unlinks all urbs, then * uses oxu_stop to clean up the rest */ From 18dc0de0c7e41be4fe0ca22c6aa384cc38fa45d5 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Sun, 1 May 2011 06:29:16 -0300 Subject: [PATCH 1908/2556] cx88: protect per-device driver list with device lock commit 8a317a8760cfffa8185b56ff59fb4b6c58488d79 upstream. The BKL conversion of this driver seems to have gone wrong. Various uses of the sub-device and driver lists appear to be subject to race conditions. In particular, some functions access drvlist without a relevant lock held, which will race against removal of drivers. Let's start with that --- clean up by consistently protecting dev->drvlist with dev->core->lock, noting driver functions that require the device lock to be held or not to be held. After this patch, there are still some races --- e.g., cx8802_blackbird_remove can run between the time the blackbird driver is acquired and the time it is used in mpeg_release, and there's a similar race in cx88_dvb_bus_ctrl. Later patches will address the remaining known races and the deadlock noticed by Andi. This patch just makes the semantics clearer in preparation for those later changes. Based on work by Ben Hutchings . Tested-by: Andi Huber Tested-by: Marlon de Boer Signed-off-by: Jonathan Nieder Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/video/cx88/cx88-blackbird.c | 3 ++- drivers/media/video/cx88/cx88-dvb.c | 3 +++ drivers/media/video/cx88/cx88-mpeg.c | 11 +++++++---- drivers/media/video/cx88/cx88.h | 9 ++++++++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index bca307eb1e249..b93fbd39a39e5 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -1122,10 +1122,11 @@ static int mpeg_release(struct file *file) mutex_lock(&dev->core->lock); file->private_data = NULL; kfree(fh); - mutex_unlock(&dev->core->lock); /* Make sure we release the hardware */ drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD); + mutex_unlock(&dev->core->lock); + if (drv) drv->request_release(drv); diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 90717ee944ec5..88a15079ef2f3 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -132,7 +132,10 @@ static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, int acquire) return -EINVAL; } + mutex_lock(&dev->core->lock); drv = cx8802_get_driver(dev, CX88_MPEG_DVB); + mutex_unlock(&dev->core->lock); + if (drv) { if (acquire){ dev->frontends.active_fe_id = fe_id; diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index addf9545e9bf4..918172b871483 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -748,6 +748,8 @@ int cx8802_unregister_driver(struct cx8802_driver *drv) dev->pci->subsystem_device, dev->core->board.name, dev->core->boardnr); + mutex_lock(&dev->core->lock); + list_for_each_entry_safe(d, dtmp, &dev->drvlist, drvlist) { /* only unregister the correct driver type */ if (d->type_id != drv->type_id) @@ -755,15 +757,14 @@ int cx8802_unregister_driver(struct cx8802_driver *drv) err = d->remove(d); if (err == 0) { - mutex_lock(&drv->core->lock); list_del(&d->drvlist); - mutex_unlock(&drv->core->lock); kfree(d); } else printk(KERN_ERR "%s/2: cx8802 driver remove " "failed (%d)\n", dev->core->name, err); } + mutex_unlock(&dev->core->lock); } return err; @@ -827,6 +828,8 @@ static void __devexit cx8802_remove(struct pci_dev *pci_dev) flush_request_modules(dev); + mutex_lock(&dev->core->lock); + if (!list_empty(&dev->drvlist)) { struct cx8802_driver *drv, *tmp; int err; @@ -838,9 +841,7 @@ static void __devexit cx8802_remove(struct pci_dev *pci_dev) list_for_each_entry_safe(drv, tmp, &dev->drvlist, drvlist) { err = drv->remove(drv); if (err == 0) { - mutex_lock(&drv->core->lock); list_del(&drv->drvlist); - mutex_unlock(&drv->core->lock); } else printk(KERN_ERR "%s/2: cx8802 driver remove " "failed (%d)\n", dev->core->name, err); @@ -848,6 +849,8 @@ static void __devexit cx8802_remove(struct pci_dev *pci_dev) } } + mutex_unlock(&dev->core->lock); + /* Destroy any 8802 reference. */ dev->core->dvbdev = NULL; diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index c9981e77416a6..6ff34c711b000 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -496,7 +496,11 @@ struct cx8802_driver { int (*resume)(struct pci_dev *pci_dev); /* MPEG 8802 -> mini driver - Driver probe and configuration */ + + /* Caller must _not_ hold core->lock */ int (*probe)(struct cx8802_driver *drv); + + /* Caller must hold core->lock */ int (*remove)(struct cx8802_driver *drv); /* MPEG 8802 -> mini driver - Access for hardware control */ @@ -551,8 +555,9 @@ struct cx8802_dev { /* for switching modulation types */ unsigned char ts_gen_cntrl; - /* List of attached drivers */ + /* List of attached drivers; must hold core->lock to access */ struct list_head drvlist; + struct work_struct request_module_wk; }; @@ -675,6 +680,8 @@ int cx88_audio_thread(void *data); int cx8802_register_driver(struct cx8802_driver *drv); int cx8802_unregister_driver(struct cx8802_driver *drv); + +/* Caller must hold core->lock */ struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype); /* ----------------------------------------------------------- */ From d47b389e7b2de4086cbf1d811d6803245c3798fc Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Sun, 1 May 2011 06:29:37 -0300 Subject: [PATCH 1909/2556] cx88: fix locking of sub-driver operations commit 1fe70e963028f34ba5e32488a7870ff4b410b19b upstream. The BKL conversion of this driver seems to have gone wrong. Loading the cx88-blackbird driver deadlocks. The cause: mpeg_ops::open in the cx2388x blackbird driver acquires the device lock and calls the sub-driver's request_acquire, which tries to acquire the lock again. Fix it by clarifying the semantics of request_acquire, request_release, advise_acquire, and advise_release: now all will rely on the caller to acquire the device lock. Based on work by Ben Hutchings . Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=31962 Reported-by: Andi Huber Tested-by: Andi Huber Tested-by: Marlon de Boer Signed-off-by: Jonathan Nieder Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/video/cx88/cx88-blackbird.c | 4 ++-- drivers/media/video/cx88/cx88-dvb.c | 3 +-- drivers/media/video/cx88/cx88-mpeg.c | 4 ---- drivers/media/video/cx88/cx88.h | 3 ++- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index b93fbd39a39e5..a6f7d53a4510f 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -1125,13 +1125,13 @@ static int mpeg_release(struct file *file) /* Make sure we release the hardware */ drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD); - mutex_unlock(&dev->core->lock); - if (drv) drv->request_release(drv); atomic_dec(&dev->core->mpeg_users); + mutex_unlock(&dev->core->lock); + return 0; } diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 88a15079ef2f3..5eccd0211e79f 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -134,8 +134,6 @@ static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, int acquire) mutex_lock(&dev->core->lock); drv = cx8802_get_driver(dev, CX88_MPEG_DVB); - mutex_unlock(&dev->core->lock); - if (drv) { if (acquire){ dev->frontends.active_fe_id = fe_id; @@ -145,6 +143,7 @@ static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, int acquire) dev->frontends.active_fe_id = 0; } } + mutex_unlock(&dev->core->lock); return ret; } diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index 918172b871483..9147c16c82afd 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -624,13 +624,11 @@ static int cx8802_request_acquire(struct cx8802_driver *drv) if (drv->advise_acquire) { - mutex_lock(&drv->core->lock); core->active_ref++; if (core->active_type_id == CX88_BOARD_NONE) { core->active_type_id = drv->type_id; drv->advise_acquire(drv); } - mutex_unlock(&drv->core->lock); mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __func__, cx_read(MO_GP0_IO)); } @@ -643,14 +641,12 @@ static int cx8802_request_release(struct cx8802_driver *drv) { struct cx88_core *core = drv->core; - mutex_lock(&drv->core->lock); if (drv->advise_release && --core->active_ref == 0) { drv->advise_release(drv); core->active_type_id = CX88_BOARD_NONE; mpeg_dbg(1,"%s() Post release GPIO=%x\n", __func__, cx_read(MO_GP0_IO)); } - mutex_unlock(&drv->core->lock); return 0; } diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 6ff34c711b000..e912919f15db6 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -500,7 +500,8 @@ struct cx8802_driver { /* Caller must _not_ hold core->lock */ int (*probe)(struct cx8802_driver *drv); - /* Caller must hold core->lock */ + /* Callers to the following functions must hold core->lock */ + int (*remove)(struct cx8802_driver *drv); /* MPEG 8802 -> mini driver - Access for hardware control */ From 80e0c7a68e493495c9006824dc7d476ab2ff3776 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Sun, 1 May 2011 06:29:56 -0300 Subject: [PATCH 1910/2556] cx88: hold device lock during sub-driver initialization commit 1d6213ab995c61f7d1d81cf6cf876acf15d6e714 upstream. cx8802_blackbird_probe makes a device node for the mpeg sub-device before it has been added to dev->drvlist. If the device is opened during that time, the open succeeds but request_acquire cannot be called, so the reference count remains zero. Later, when the device is closed, the reference count becomes negative --- uh oh. Close the race by holding core->lock during probe and not releasing until the device is in drvlist and initialization finished. Previously the BKL prevented this race. Reported-by: Andreas Huber Tested-by: Andi Huber Tested-by: Marlon de Boer Signed-off-by: Jonathan Nieder Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/video/cx88/cx88-blackbird.c | 2 -- drivers/media/video/cx88/cx88-mpeg.c | 5 ++--- drivers/media/video/cx88/cx88.h | 7 ++----- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index a6f7d53a4510f..f637d34d5062e 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -1335,11 +1335,9 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv) blackbird_register_video(dev); /* initial device configuration: needed ? */ - mutex_lock(&dev->core->lock); // init_controls(core); cx88_set_tvnorm(core,core->tvnorm); cx88_video_mux(core,0); - mutex_unlock(&dev->core->lock); return 0; diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index 9147c16c82afd..497f26fd07baa 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -709,18 +709,17 @@ int cx8802_register_driver(struct cx8802_driver *drv) drv->request_release = cx8802_request_release; memcpy(driver, drv, sizeof(*driver)); + mutex_lock(&drv->core->lock); err = drv->probe(driver); if (err == 0) { i++; - mutex_lock(&drv->core->lock); list_add_tail(&driver->drvlist, &dev->drvlist); - mutex_unlock(&drv->core->lock); } else { printk(KERN_ERR "%s/2: cx8802 probe failed, err = %d\n", dev->core->name, err); } - + mutex_unlock(&drv->core->lock); } return i ? 0 : -ENODEV; diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index e912919f15db6..93a94bff471fd 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -495,13 +495,10 @@ struct cx8802_driver { int (*suspend)(struct pci_dev *pci_dev, pm_message_t state); int (*resume)(struct pci_dev *pci_dev); - /* MPEG 8802 -> mini driver - Driver probe and configuration */ - - /* Caller must _not_ hold core->lock */ - int (*probe)(struct cx8802_driver *drv); - /* Callers to the following functions must hold core->lock */ + /* MPEG 8802 -> mini driver - Driver probe and configuration */ + int (*probe)(struct cx8802_driver *drv); int (*remove)(struct cx8802_driver *drv); /* MPEG 8802 -> mini driver - Access for hardware control */ From 4e3ce1d6df72b3b6bf871c9d2ea78711d3a9d724 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 14 Apr 2011 17:13:53 +0900 Subject: [PATCH 1911/2556] sh: clkfwk: fixup clk_rate_table_build parameter in div6 clock commit 52c10ad22b7e317960b4d411c9a9ddeaf3d5ae39 upstream. div6 clock should not use arch_flags for clk_rate_table_build, because SH_CLK_DIV6_EXT doesn't care .arch_flags. clk->freq_table[] will be all CPUFREQ_ENTRY_INVALID without this patch. Signed-off-by: Kuninori Morimoto Signed-off-by: Paul Mundt Signed-off-by: Greg Kroah-Hartman --- drivers/sh/clk/cpg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 6172335ae3234..82dd6fb178386 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -105,7 +105,7 @@ static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) /* Rebuild the frequency table */ clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, - table, &clk->arch_flags); + table, NULL); return 0; } From 3a0d69ad4e806eb4aba6cb5ce167dc28a74f0e4c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 15 Apr 2011 16:44:27 +0900 Subject: [PATCH 1912/2556] sh: fixup fpu.o compile order commit a375b15164dd9264f724ad941825e52c90145151 upstream. arch_ptrace() was modified to reference init_fpu() to fix up xstate initialization, which overlooked the fact that there are configurations that don't enable any of hard FPU support or emulation, resulting in build errors on DSP parts. Given that init_fpu() simply sets up the xstate slab cache and is side-stepped entirely for the DSP case, we can simply always build in the helper and fix up the references. Reported-by: Nobuhiro Iwamatsu Signed-off-by: Kuninori Morimoto Signed-off-by: Paul Mundt Signed-off-by: Greg Kroah-Hartman --- arch/sh/kernel/cpu/Makefile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile index d49c2135fd480..ae95935d93cde 100644 --- a/arch/sh/kernel/cpu/Makefile +++ b/arch/sh/kernel/cpu/Makefile @@ -17,7 +17,5 @@ obj-$(CONFIG_ARCH_SHMOBILE) += shmobile/ obj-$(CONFIG_SH_ADC) += adc.o obj-$(CONFIG_SH_CLK_CPG_LEGACY) += clock-cpg.o -obj-$(CONFIG_SH_FPU) += fpu.o -obj-$(CONFIG_SH_FPU_EMU) += fpu.o -obj-y += irq/ init.o clock.o hwblk.o proc.o +obj-y += irq/ init.o clock.o fpu.o hwblk.o proc.o From d0f7955df4d1fac108e5edfd6a9ebe9135b1597f Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Fri, 13 May 2011 21:47:23 +0200 Subject: [PATCH 1913/2556] p54usb: add zoom 4410 usbid commit 9368a9a2378ab721f82f59430a135b4ce4ff5109 upstream. Reported-by: Mark Davis Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/p54/p54usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index e18358725b698..a8f3bc740dfaf 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -82,6 +82,7 @@ static struct usb_device_id p54u_table[] __devinitdata = { {USB_DEVICE(0x06b9, 0x0121)}, /* Thomson SpeedTouch 121g */ {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */ {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */ + {USB_DEVICE(0x083a, 0xc501)}, /* Zoom Wireless-G 4410 */ {USB_DEVICE(0x083a, 0xf503)}, /* Accton FD7050E ver 1010ec */ {USB_DEVICE(0x0846, 0x4240)}, /* Netgear WG111 (v2) */ {USB_DEVICE(0x0915, 0x2000)}, /* Cohiba Proto board */ From b93e342044640789e34a48893c120875afbcb07d Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Tue, 17 May 2011 00:50:33 -0500 Subject: [PATCH 1914/2556] eCryptfs: Allow 2 scatterlist entries for encrypted filenames commit 8d08dab786ad5cc2aca2bf870de370144b78c85a upstream. The buffers allocated while encrypting and decrypting long filenames can sometimes straddle two pages. In this situation, virt_to_scatterlist() will return -ENOMEM, causing the operation to fail and the user will get scary error messages in their logs: kernel: ecryptfs_write_tag_70_packet: Internal error whilst attempting to convert filename memory to scatterlist; expected rc = 1; got rc = [-12]. block_aligned_filename_size = [272] kernel: ecryptfs_encrypt_filename: Error attempting to generate tag 70 packet; rc = [-12] kernel: ecryptfs_encrypt_and_encode_filename: Error attempting to encrypt filename; rc = [-12] kernel: ecryptfs_lookup: Error attempting to encrypt and encode filename; rc = [-12] The solution is to allow up to 2 scatterlist entries to be used. Signed-off-by: Tyler Hicks Signed-off-by: Greg Kroah-Hartman --- fs/ecryptfs/keystore.c | 46 +++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index 4feb78c23651a..7ed2ef30b8a86 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -492,8 +492,8 @@ struct ecryptfs_write_tag_70_packet_silly_stack { struct mutex *tfm_mutex; char *block_aligned_filename; struct ecryptfs_auth_tok *auth_tok; - struct scatterlist src_sg; - struct scatterlist dst_sg; + struct scatterlist src_sg[2]; + struct scatterlist dst_sg[2]; struct blkcipher_desc desc; char iv[ECRYPTFS_MAX_IV_BYTES]; char hash[ECRYPTFS_TAG_70_DIGEST_SIZE]; @@ -709,23 +709,21 @@ ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes, memcpy(&s->block_aligned_filename[s->num_rand_bytes], filename, filename_size); rc = virt_to_scatterlist(s->block_aligned_filename, - s->block_aligned_filename_size, &s->src_sg, 1); - if (rc != 1) { + s->block_aligned_filename_size, s->src_sg, 2); + if (rc < 1) { printk(KERN_ERR "%s: Internal error whilst attempting to " - "convert filename memory to scatterlist; " - "expected rc = 1; got rc = [%d]. " + "convert filename memory to scatterlist; rc = [%d]. " "block_aligned_filename_size = [%zd]\n", __func__, rc, s->block_aligned_filename_size); goto out_release_free_unlock; } rc = virt_to_scatterlist(&dest[s->i], s->block_aligned_filename_size, - &s->dst_sg, 1); - if (rc != 1) { + s->dst_sg, 2); + if (rc < 1) { printk(KERN_ERR "%s: Internal error whilst attempting to " "convert encrypted filename memory to scatterlist; " - "expected rc = 1; got rc = [%d]. " - "block_aligned_filename_size = [%zd]\n", __func__, rc, - s->block_aligned_filename_size); + "rc = [%d]. block_aligned_filename_size = [%zd]\n", + __func__, rc, s->block_aligned_filename_size); goto out_release_free_unlock; } /* The characters in the first block effectively do the job @@ -748,7 +746,7 @@ ecryptfs_write_tag_70_packet(char *dest, size_t *remaining_bytes, mount_crypt_stat->global_default_fn_cipher_key_bytes); goto out_release_free_unlock; } - rc = crypto_blkcipher_encrypt_iv(&s->desc, &s->dst_sg, &s->src_sg, + rc = crypto_blkcipher_encrypt_iv(&s->desc, s->dst_sg, s->src_sg, s->block_aligned_filename_size); if (rc) { printk(KERN_ERR "%s: Error attempting to encrypt filename; " @@ -782,8 +780,8 @@ struct ecryptfs_parse_tag_70_packet_silly_stack { struct mutex *tfm_mutex; char *decrypted_filename; struct ecryptfs_auth_tok *auth_tok; - struct scatterlist src_sg; - struct scatterlist dst_sg; + struct scatterlist src_sg[2]; + struct scatterlist dst_sg[2]; struct blkcipher_desc desc; char fnek_sig_hex[ECRYPTFS_SIG_SIZE_HEX + 1]; char iv[ECRYPTFS_MAX_IV_BYTES]; @@ -890,13 +888,12 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size, } mutex_lock(s->tfm_mutex); rc = virt_to_scatterlist(&data[(*packet_size)], - s->block_aligned_filename_size, &s->src_sg, 1); - if (rc != 1) { + s->block_aligned_filename_size, s->src_sg, 2); + if (rc < 1) { printk(KERN_ERR "%s: Internal error whilst attempting to " "convert encrypted filename memory to scatterlist; " - "expected rc = 1; got rc = [%d]. " - "block_aligned_filename_size = [%zd]\n", __func__, rc, - s->block_aligned_filename_size); + "rc = [%d]. block_aligned_filename_size = [%zd]\n", + __func__, rc, s->block_aligned_filename_size); goto out_unlock; } (*packet_size) += s->block_aligned_filename_size; @@ -910,13 +907,12 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size, goto out_unlock; } rc = virt_to_scatterlist(s->decrypted_filename, - s->block_aligned_filename_size, &s->dst_sg, 1); - if (rc != 1) { + s->block_aligned_filename_size, s->dst_sg, 2); + if (rc < 1) { printk(KERN_ERR "%s: Internal error whilst attempting to " "convert decrypted filename memory to scatterlist; " - "expected rc = 1; got rc = [%d]. " - "block_aligned_filename_size = [%zd]\n", __func__, rc, - s->block_aligned_filename_size); + "rc = [%d]. block_aligned_filename_size = [%zd]\n", + __func__, rc, s->block_aligned_filename_size); goto out_free_unlock; } /* The characters in the first block effectively do the job of @@ -956,7 +952,7 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size, mount_crypt_stat->global_default_fn_cipher_key_bytes); goto out_free_unlock; } - rc = crypto_blkcipher_decrypt_iv(&s->desc, &s->dst_sg, &s->src_sg, + rc = crypto_blkcipher_decrypt_iv(&s->desc, s->dst_sg, s->src_sg, s->block_aligned_filename_size); if (rc) { printk(KERN_ERR "%s: Error attempting to decrypt filename; " From c02eace342342b3156426e7a5ffe1ddc93552c51 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 6 May 2011 17:08:56 +0300 Subject: [PATCH 1915/2556] UBIFS: fix a rare memory leak in ro to rw remounting path commit eaeee242c531cd4b0a4a46e8b5dd7ef504380c42 upstream. When re-mounting from R/O mode to R/W mode and the LEB count in the superblock is not up-to date, because for the underlying UBI volume became larger, we re-write the superblock. We allocate RAM for these purposes, but never free it. So this is a memory leak, although very rare one. Signed-off-by: Artem Bityutskiy Signed-off-by: Greg Kroah-Hartman --- fs/ubifs/sb.c | 3 ++- fs/ubifs/super.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c index bf31b4729e51c..cad60b51f7c4a 100644 --- a/fs/ubifs/sb.c +++ b/fs/ubifs/sb.c @@ -475,7 +475,8 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup) * @c: UBIFS file-system description object * * This function returns a pointer to the superblock node or a negative error - * code. + * code. Note, the user of this function is responsible of kfree()'ing the + * returned superblock buffer. */ struct ubifs_sb_node *ubifs_read_sb_node(struct ubifs_info *c) { diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index e20cb5a9aee54..38749e76dda56 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1560,6 +1560,7 @@ static int ubifs_remount_rw(struct ubifs_info *c) } sup->leb_cnt = cpu_to_le32(c->leb_cnt); err = ubifs_write_sb_node(c, sup); + kfree(sup); if (err) goto out; } From 77a75aa402d6021f2b0059ef1c2f92cb6b10fbaf Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Tue, 10 May 2011 15:47:16 -0700 Subject: [PATCH 1916/2556] kbuild: Fix GNU make v3.80 compatibility commit 43f67c98161c65f1b2e3af3a9ce6741850072c06 upstream. According to Documentation/Changes, the kernel should be buildable with GNU make 3.80+. Commit 88d7be031f9f975bb3f50a0b5ef3796a671e7edf (kbuild: Use a single clean rule for kernel and external modules) introduced the "$(or" construct, which requires make 3.81. This causes "make clean" to malfunction when it is used with external modules. Replace "$(or" with an equivalent "$(if" expression, to restore backward compatibility. Signed-off-by: Kevin Cernekee Signed-off-by: Michal Marek Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 785cac8897907..23ce04c556587 100644 --- a/Makefile +++ b/Makefile @@ -1368,7 +1368,7 @@ endif # KBUILD_EXTMOD clean: $(clean-dirs) $(call cmd,rmdirs) $(call cmd,rmfiles) - @find $(or $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \ + @find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \ \( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \ -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \ -o -name '*.symtypes' -o -name 'modules.order' \ From f48e373ba0df22c98e0d8e9d8888d888b845e061 Mon Sep 17 00:00:00 2001 From: Luca Tettamanti Date: Wed, 25 May 2011 20:43:31 +0200 Subject: [PATCH 1917/2556] i8k: Avoid lahf in 64-bit code commit bc1f419c76a2d6450413ce4349f4e4a07be011d5 upstream. i8k uses lahf to read the flag register in 64-bit code; early x86-64 CPUs, however, lack this instruction and we get an invalid opcode exception at runtime. Use pushf to load the flag register into the stack instead. Signed-off-by: Luca Tettamanti Reported-by: Jeff Rickman Tested-by: Jeff Rickman Tested-by: Harry G McGavran Jr Cc: Massimo Dal Zotto Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/char/i8k.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index d72433f2d310d..ee017166545eb 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -139,8 +139,8 @@ static int i8k_smm(struct smm_regs *regs) "movl %%edi,20(%%rax)\n\t" "popq %%rdx\n\t" "movl %%edx,0(%%rax)\n\t" - "lahf\n\t" - "shrl $8,%%eax\n\t" + "pushfq\n\t" + "popq %%rax\n\t" "andl $1,%%eax\n" :"=a"(rc) : "a"(regs) From 4a1163dff6592dcee594b2bee597aafd749b93ee Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Thu, 24 Feb 2011 17:19:23 +0200 Subject: [PATCH 1918/2556] cpuidle: menu: fixed wrapping timers at 4.294 seconds commit 7467571f4480b273007517b26297c07154c73924 upstream. Cpuidle menu governor is using u32 as a temporary datatype for storing nanosecond values which wrap around at 4.294 seconds. This causes errors in predicted sleep times resulting in higher than should be C state selection and increased power consumption. This also breaks cpuidle state residency statistics. Signed-off-by: Tero Kristo Signed-off-by: Len Brown Signed-off-by: Greg Kroah-Hartman --- drivers/cpuidle/governors/menu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index f508690eb9585..c47f3d09c1eeb 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -237,6 +237,7 @@ static int menu_select(struct cpuidle_device *dev) unsigned int power_usage = -1; int i; int multiplier; + struct timespec t; if (data->needs_update) { menu_update(dev); @@ -251,8 +252,9 @@ static int menu_select(struct cpuidle_device *dev) return 0; /* determine the expected residency time, round up */ + t = ktime_to_timespec(tick_nohz_get_sleep_length()); data->expected_us = - DIV_ROUND_UP((u32)ktime_to_ns(tick_nohz_get_sleep_length()), 1000); + t.tv_sec * USEC_PER_SEC + t.tv_nsec / NSEC_PER_USEC; data->bucket = which_bucket(data->expected_us); From a1722e9acd8083e85d0ba54409c630ac61dec985 Mon Sep 17 00:00:00 2001 From: Tim Chen Date: Fri, 11 Feb 2011 12:49:04 -0800 Subject: [PATCH 1919/2556] idle governor: Avoid lock acquisition to read pm_qos before entering idle commit 333c5ae9948194428fe6c5ef5c088304fc98263b upstream. Thanks to the reviews and comments by Rafael, James, Mark and Andi. Here's version 2 of the patch incorporating your comments and also some update to my previous patch comments. I noticed that before entering idle state, the menu idle governor will look up the current pm_qos target value according to the list of qos requests received. This look up currently needs the acquisition of a lock to access the list of qos requests to find the qos target value, slowing down the entrance into idle state due to contention by multiple cpus to access this list. The contention is severe when there are a lot of cpus waking and going into idle. For example, for a simple workload that has 32 pair of processes ping ponging messages to each other, where 64 cpu cores are active in test system, I see the following profile with 37.82% of cpu cycles spent in contention of pm_qos_lock: - 37.82% swapper [kernel.kallsyms] [k] _raw_spin_lock_irqsave - _raw_spin_lock_irqsave - 95.65% pm_qos_request menu_select cpuidle_idle_call - cpu_idle 99.98% start_secondary A better approach will be to cache the updated pm_qos target value so reading it does not require lock acquisition as in the patch below. With this patch the contention for pm_qos_lock is removed and I saw a 2.2X increase in throughput for my message passing workload. Signed-off-by: Tim Chen Acked-by: Andi Kleen Acked-by: James Bottomley Acked-by: mark gross Signed-off-by: Len Brown Signed-off-by: Greg Kroah-Hartman --- include/linux/pm_qos_params.h | 4 ++++ kernel/pm_qos_params.c | 37 +++++++++++++++++++++++------------ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/include/linux/pm_qos_params.h b/include/linux/pm_qos_params.h index 77cbddb3784cf..a7d87f911cabb 100644 --- a/include/linux/pm_qos_params.h +++ b/include/linux/pm_qos_params.h @@ -16,6 +16,10 @@ #define PM_QOS_NUM_CLASSES 4 #define PM_QOS_DEFAULT_VALUE -1 +#define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC) +#define PM_QOS_NETWORK_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC) +#define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE 0 + struct pm_qos_request_list { struct plist_node list; int pm_qos_class; diff --git a/kernel/pm_qos_params.c b/kernel/pm_qos_params.c index aeaa7f8468216..6a8fad82a3ad4 100644 --- a/kernel/pm_qos_params.c +++ b/kernel/pm_qos_params.c @@ -53,11 +53,17 @@ enum pm_qos_type { PM_QOS_MIN /* return the smallest value */ }; +/* + * Note: The lockless read path depends on the CPU accessing + * target_value atomically. Atomic access is only guaranteed on all CPU + * types linux supports for 32 bit quantites + */ struct pm_qos_object { struct plist_head requests; struct blocking_notifier_head *notifiers; struct miscdevice pm_qos_power_miscdev; char *name; + s32 target_value; /* Do not change to 64 bit */ s32 default_value; enum pm_qos_type type; }; @@ -70,7 +76,8 @@ static struct pm_qos_object cpu_dma_pm_qos = { .requests = PLIST_HEAD_INIT(cpu_dma_pm_qos.requests, pm_qos_lock), .notifiers = &cpu_dma_lat_notifier, .name = "cpu_dma_latency", - .default_value = 2000 * USEC_PER_SEC, + .target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, + .default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, .type = PM_QOS_MIN, }; @@ -79,7 +86,8 @@ static struct pm_qos_object network_lat_pm_qos = { .requests = PLIST_HEAD_INIT(network_lat_pm_qos.requests, pm_qos_lock), .notifiers = &network_lat_notifier, .name = "network_latency", - .default_value = 2000 * USEC_PER_SEC, + .target_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE, + .default_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE, .type = PM_QOS_MIN }; @@ -89,7 +97,8 @@ static struct pm_qos_object network_throughput_pm_qos = { .requests = PLIST_HEAD_INIT(network_throughput_pm_qos.requests, pm_qos_lock), .notifiers = &network_throughput_notifier, .name = "network_throughput", - .default_value = 0, + .target_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE, + .default_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE, .type = PM_QOS_MAX, }; @@ -132,6 +141,16 @@ static inline int pm_qos_get_value(struct pm_qos_object *o) } } +static inline s32 pm_qos_read_value(struct pm_qos_object *o) +{ + return o->target_value; +} + +static inline void pm_qos_set_value(struct pm_qos_object *o, s32 value) +{ + o->target_value = value; +} + static void update_target(struct pm_qos_object *o, struct plist_node *node, int del, int value) { @@ -156,6 +175,7 @@ static void update_target(struct pm_qos_object *o, struct plist_node *node, plist_add(node, &o->requests); } curr_value = pm_qos_get_value(o); + pm_qos_set_value(o, curr_value); spin_unlock_irqrestore(&pm_qos_lock, flags); if (prev_value != curr_value) @@ -190,18 +210,11 @@ static int find_pm_qos_object_by_minor(int minor) * pm_qos_request - returns current system wide qos expectation * @pm_qos_class: identification of which qos value is requested * - * This function returns the current target value in an atomic manner. + * This function returns the current target value. */ int pm_qos_request(int pm_qos_class) { - unsigned long flags; - int value; - - spin_lock_irqsave(&pm_qos_lock, flags); - value = pm_qos_get_value(pm_qos_array[pm_qos_class]); - spin_unlock_irqrestore(&pm_qos_lock, flags); - - return value; + return pm_qos_read_value(pm_qos_array[pm_qos_class]); } EXPORT_SYMBOL_GPL(pm_qos_request); From 5bbf11bd6cac2d02b6054eb1ecba1ead7dc7191b Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Sun, 29 May 2011 13:02:52 +0100 Subject: [PATCH 1920/2556] dm table: reject devices without request fns commit f4808ca99a203f20b4475601748e44b25a65bdec upstream. This patch adds a check that a block device has a request function defined before it is used. Otherwise, misconfiguration can cause an oops. Because we are allowing devices with zero size e.g. an offline multipath device as in commit 2cd54d9bedb79a97f014e86c0da393416b264eb3 ("dm: allow offline devices") there needs to be an additional check to ensure devices are initialised. Some block devices, like a loop device without a backing file, exist but have no request function. Reproducer is trivial: dm-mirror on unbound loop device (no backing file on loop devices) dmsetup create x --table "0 8 mirror core 2 8 sync 2 /dev/loop0 0 /dev/loop1 0" and mirror resync will immediatelly cause OOps. BUG: unable to handle kernel NULL pointer dereference at (null) ? generic_make_request+0x2bd/0x590 ? kmem_cache_alloc+0xad/0x190 submit_bio+0x53/0xe0 ? bio_add_page+0x3b/0x50 dispatch_io+0x1ca/0x210 [dm_mod] ? read_callback+0x0/0xd0 [dm_mirror] dm_io+0xbb/0x290 [dm_mod] do_mirror+0x1e0/0x748 [dm_mirror] Signed-off-by: Milan Broz Reported-by: Zdenek Kabelac Acked-by: Mike Snitzer Signed-off-by: Alasdair G Kergon Signed-off-by: Greg Kroah-Hartman --- drivers/md/dm-table.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 38e4eb1bb9656..abd23aa34e4f7 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -361,6 +361,7 @@ static void close_dev(struct dm_dev_internal *d, struct mapped_device *md) static int device_area_is_invalid(struct dm_target *ti, struct dm_dev *dev, sector_t start, sector_t len, void *data) { + struct request_queue *q; struct queue_limits *limits = data; struct block_device *bdev = dev->bdev; sector_t dev_size = @@ -369,6 +370,22 @@ static int device_area_is_invalid(struct dm_target *ti, struct dm_dev *dev, limits->logical_block_size >> SECTOR_SHIFT; char b[BDEVNAME_SIZE]; + /* + * Some devices exist without request functions, + * such as loop devices not yet bound to backing files. + * Forbid the use of such devices. + */ + q = bdev_get_queue(bdev); + if (!q || !q->make_request_fn) { + DMWARN("%s: %s is not yet initialised: " + "start=%llu, len=%llu, dev_size=%llu", + dm_device_name(ti->table->md), bdevname(bdev, b), + (unsigned long long)start, + (unsigned long long)len, + (unsigned long long)dev_size); + return 1; + } + if (!dev_size) return 0; From f311af550d6d24b03c1c911572192bc95fe58404 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 26 May 2011 11:20:19 +0100 Subject: [PATCH 1921/2556] ARM: 6941/1: cache: ensure MVA is cacheline aligned in flush_kern_dcache_area commit a248b13b21ae00b97638b4f435c8df3075808b5d upstream. The v6 and v7 implementations of flush_kern_dcache_area do not align the passed MVA to the size of a cacheline in the data cache. If a misaligned address is used, only a subset of the requested area will be flushed. This has been observed to cause failures in SMP boot where the secondary_data initialised by the primary CPU is not cacheline aligned, causing the secondary CPUs to read incorrect values for their pgd and stack pointers. This patch ensures that the base address is cacheline aligned before flushing the d-cache. Acked-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Russell King Signed-off-by: Greg Kroah-Hartman --- arch/arm/mm/cache-v6.S | 1 + arch/arm/mm/cache-v7.S | 2 ++ 2 files changed, 3 insertions(+) diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S index c96fa1b3f49f5..73b4a8b66a575 100644 --- a/arch/arm/mm/cache-v6.S +++ b/arch/arm/mm/cache-v6.S @@ -176,6 +176,7 @@ ENDPROC(v6_coherent_kern_range) */ ENTRY(v6_flush_kern_dcache_area) add r1, r0, r1 + bic r0, r0, #D_CACHE_LINE_SIZE - 1 1: #ifdef HARVARD_CACHE mcr p15, 0, r0, c7, c14, 1 @ clean & invalidate D line diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S index 6136e68ce953d..d9b5cab4afd7b 100644 --- a/arch/arm/mm/cache-v7.S +++ b/arch/arm/mm/cache-v7.S @@ -221,6 +221,8 @@ ENDPROC(v7_coherent_user_range) ENTRY(v7_flush_kern_dcache_area) dcache_line_size r2, r3 add r1, r0, r1 + sub r3, r2, #1 + bic r0, r0, r3 1: mcr p15, 0, r0, c7, c14, 1 @ clean & invalidate D line / unified line add r0, r0, r2 From 213c900d693b87e709e32b046858843fad0332ea Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Sat, 28 May 2011 13:14:09 -0700 Subject: [PATCH 1922/2556] tmpfs: fix race between truncate and writepage commit 826267cf1e6c6899eda1325a19f1b1d15c558b20 upstream. While running fsx on tmpfs with a memhog then swapoff, swapoff was hanging (interruptibly), repeatedly failing to locate the owner of a 0xff entry in the swap_map. Although shmem_writepage() does abandon when it sees incoming page index is beyond eof, there was still a window in which shmem_truncate_range() could come in between writepage's dropping lock and updating swap_map, find the half-completed swap_map entry, and in trying to free it, leave it in a state that swap_shmem_alloc() could not correct. Arguably a bug in __swap_duplicate()'s and swap_entry_free()'s handling of the different cases, but easiest to fix by moving swap_shmem_alloc() under cover of the lock. More interesting than the bug: it's been there since 2.6.33, why could I not see it with earlier kernels? The mmotm of two weeks ago seems to have some magic for generating races, this is just one of three I found. With yesterday's git I first saw this in mainline, bisected in search of that magic, but the easy reproducibility evaporated. Oh well, fix the bug. Signed-off-by: Hugh Dickins Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- mm/shmem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/shmem.c b/mm/shmem.c index 21bdaf8fd21c6..91dd9c3efe871 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1102,8 +1102,8 @@ static int shmem_writepage(struct page *page, struct writeback_control *wbc) remove_from_page_cache(page); shmem_swp_set(info, entry, swap.val); shmem_swp_unmap(entry); - spin_unlock(&info->lock); swap_shmem_alloc(swap); + spin_unlock(&info->lock); BUG_ON(page_mapped(page)); page_cache_release(page); /* pagecache ref */ swap_writepage(page, wbc); From 9854d5778eb446a332fb34ad17ff4d04c6440999 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 27 May 2011 04:51:54 +0000 Subject: [PATCH 1923/2556] atm: expose ATM device index in sysfs commit e7a46b4d0839c2a3aa2e0ae0b145f293f6738498 upstream. It's currently exposed only through /proc which, besides requiring screen-scraping, doesn't allow userspace to distinguish between two identical ATM adapters with different ATM indexes. The ATM device index is required when using PPPoATM on a system with multiple ATM adapters. Signed-off-by: Dan Williams Reviewed-by: Eric Dumazet Tested-by: David Woodhouse Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- net/atm/atm_sysfs.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/atm/atm_sysfs.c b/net/atm/atm_sysfs.c index f7fa67c78766f..f49da5814bc3c 100644 --- a/net/atm/atm_sysfs.c +++ b/net/atm/atm_sysfs.c @@ -59,6 +59,14 @@ static ssize_t show_atmaddress(struct device *cdev, return pos - buf; } +static ssize_t show_atmindex(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct atm_dev *adev = to_atm_dev(cdev); + + return sprintf(buf, "%d\n", adev->number); +} + static ssize_t show_carrier(struct device *cdev, struct device_attribute *attr, char *buf) { @@ -99,6 +107,7 @@ static ssize_t show_link_rate(struct device *cdev, static DEVICE_ATTR(address, S_IRUGO, show_address, NULL); static DEVICE_ATTR(atmaddress, S_IRUGO, show_atmaddress, NULL); +static DEVICE_ATTR(atmindex, S_IRUGO, show_atmindex, NULL); static DEVICE_ATTR(carrier, S_IRUGO, show_carrier, NULL); static DEVICE_ATTR(type, S_IRUGO, show_type, NULL); static DEVICE_ATTR(link_rate, S_IRUGO, show_link_rate, NULL); @@ -106,6 +115,7 @@ static DEVICE_ATTR(link_rate, S_IRUGO, show_link_rate, NULL); static struct device_attribute *atm_attrs[] = { &dev_attr_atmaddress, &dev_attr_address, + &dev_attr_atmindex, &dev_attr_carrier, &dev_attr_type, &dev_attr_link_rate, From 367ecefc8ad6e652b22199403da04742feb96156 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 26 May 2011 21:06:50 +0200 Subject: [PATCH 1924/2556] brd: limit 'max_part' module param to DISK_MAX_PARTS commit 315980c8688c4b06713c1a5fe9d64cdf8ab57a72 upstream. The 'max_part' parameter controls the number of maximum partition a brd device can have. However if a user specifies very large value it would exceed the limitation of device minor number and can cause a kernel panic (or, at least, produce invalid device nodes in some cases). On my desktop system, following command kills the kernel. On qemu, it triggers similar oops but the kernel was alive: $ sudo modprobe brd max_part=100000 BUG: unable to handle kernel NULL pointer dereference at 0000000000000058 IP: [] sysfs_create_dir+0x2d/0xae PGD 7af1067 PUD 7b19067 PMD 0 Oops: 0000 [#1] SMP last sysfs file: CPU 0 Modules linked in: brd(+) Pid: 44, comm: insmod Tainted: G W 2.6.39-qemu+ #158 Bochs Bochs RIP: 0010:[] [] sysfs_create_dir+0x2d/0xae RSP: 0018:ffff880007b15d78 EFLAGS: 00000286 RAX: ffff880007b05478 RBX: ffff880007a52760 RCX: ffff880007b15dc8 RDX: ffff880007a4f900 RSI: ffff880007b15e48 RDI: ffff880007a52760 RBP: ffff880007b15da8 R08: 0000000000000002 R09: 0000000000000000 R10: ffff880007b15e48 R11: ffff880007b05478 R12: 0000000000000000 R13: ffff880007b05478 R14: 0000000000400920 R15: 0000000000000063 FS: 0000000002160880(0063) GS:ffff880007c00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000058 CR3: 0000000007b1c000 CR4: 00000000000006b0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 0000000000000000 DR7: 0000000000000000 Process insmod (pid: 44, threadinfo ffff880007b14000, task ffff880007acb980) Stack: ffff880007b15dc8 ffff880007b05478 ffff880007b15da8 00000000fffffffe ffff880007a52760 ffff880007b05478 ffff880007b15de8 ffffffff81143c0a 0000000000400920 ffff880007a52760 ffff880007b05478 0000000000000000 Call Trace: [] kobject_add_internal+0xdf/0x1a0 [] kobject_add_varg+0x41/0x50 [] kobject_add+0x64/0x66 [] blk_register_queue+0x5f/0xb8 [] add_disk+0xdf/0x289 [] brd_init+0xdf/0x1aa [brd] [] ? 0xffffffffa0003fff [] ? 0xffffffffa0003fff [] do_one_initcall+0x7a/0x12e [] sys_init_module+0x9c/0x1dc [] system_call_fastpath+0x16/0x1b Code: 89 e5 41 55 41 54 53 48 89 fb 48 83 ec 18 48 85 ff 75 04 0f 0b eb fe 48 8b 47 18 49 c7 c4 70 1e 4d 81 48 85 c0 74 04 4c 8b 60 30 8b 44 24 58 45 31 ed 0f b6 c4 85 c0 74 0d 48 8b 43 28 48 89 RIP [] sysfs_create_dir+0x2d/0xae RSP CR2: 0000000000000058 ---[ end trace aebb1175ce1f6739 ]--- Signed-off-by: Namhyung Kim Cc: Laurent Vivier Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/block/brd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/block/brd.c b/drivers/block/brd.c index b7f51e4594f86..7c9939f0303cd 100644 --- a/drivers/block/brd.c +++ b/drivers/block/brd.c @@ -585,6 +585,9 @@ static int __init brd_init(void) if (max_part > 0) part_shift = fls(max_part); + if ((1UL << part_shift) > DISK_MAX_PARTS) + return -EINVAL; + if (rd_nr > 1UL << (MINORBITS - part_shift)) return -EINVAL; From a3a9ec868705f8c00c9ddac569f2a3f2f241b2b6 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 26 May 2011 21:06:50 +0200 Subject: [PATCH 1925/2556] brd: handle on-demand devices correctly commit af46566885a373b0a526932484cd8fef8de7b598 upstream. When finding or allocating a ram disk device, brd_probe() did not take partition numbers into account so that it can result to a different device. Consider following example (I set CONFIG_BLK_DEV_RAM_COUNT=4 for simplicity) : $ sudo modprobe brd max_part=15 $ ls -l /dev/ram* brw-rw---- 1 root disk 1, 0 2011-05-25 15:41 /dev/ram0 brw-rw---- 1 root disk 1, 16 2011-05-25 15:41 /dev/ram1 brw-rw---- 1 root disk 1, 32 2011-05-25 15:41 /dev/ram2 brw-rw---- 1 root disk 1, 48 2011-05-25 15:41 /dev/ram3 $ sudo mknod /dev/ram4 b 1 64 $ sudo dd if=/dev/zero of=/dev/ram4 bs=4k count=256 256+0 records in 256+0 records out 1048576 bytes (1.0 MB) copied, 0.00215578 s, 486 MB/s namhyung@leonhard:linux$ ls -l /dev/ram* brw-rw---- 1 root disk 1, 0 2011-05-25 15:41 /dev/ram0 brw-rw---- 1 root disk 1, 16 2011-05-25 15:41 /dev/ram1 brw-rw---- 1 root disk 1, 32 2011-05-25 15:41 /dev/ram2 brw-rw---- 1 root disk 1, 48 2011-05-25 15:41 /dev/ram3 brw-r--r-- 1 root root 1, 64 2011-05-25 15:45 /dev/ram4 brw-rw---- 1 root disk 1, 1024 2011-05-25 15:44 /dev/ram64 After this patch, /dev/ram4 - instead of /dev/ram64 - was accessed correctly. In addition, 'range' passed to blk_register_region() should include all range of dev_t that RAMDISK_MAJOR can address. It does not need to be limited by partition numbers unless 'rd_nr' param was specified. Signed-off-by: Namhyung Kim Cc: Laurent Vivier Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- drivers/block/brd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/block/brd.c b/drivers/block/brd.c index 7c9939f0303cd..c94bc48d99df7 100644 --- a/drivers/block/brd.c +++ b/drivers/block/brd.c @@ -552,7 +552,7 @@ static struct kobject *brd_probe(dev_t dev, int *part, void *data) struct kobject *kobj; mutex_lock(&brd_devices_mutex); - brd = brd_init_one(dev & MINORMASK); + brd = brd_init_one(MINOR(dev) >> part_shift); kobj = brd ? get_disk(brd->brd_disk) : ERR_PTR(-ENOMEM); mutex_unlock(&brd_devices_mutex); @@ -593,10 +593,10 @@ static int __init brd_init(void) if (rd_nr) { nr = rd_nr; - range = rd_nr; + range = rd_nr << part_shift; } else { nr = CONFIG_BLK_DEV_RAM_COUNT; - range = 1UL << (MINORBITS - part_shift); + range = 1UL << MINORBITS; } if (register_blkdev(RAMDISK_MAJOR, "ramdisk")) @@ -635,7 +635,7 @@ static void __exit brd_exit(void) unsigned long range; struct brd_device *brd, *next; - range = rd_nr ? rd_nr : 1UL << (MINORBITS - part_shift); + range = rd_nr ? rd_nr << part_shift : 1UL << MINORBITS; list_for_each_entry_safe(brd, next, &brd_devices, brd_list) brd_del_one(brd); From cd9c43f0be334f028e2c8e969e6bd90b51fcdebb Mon Sep 17 00:00:00 2001 From: Boqun Feng Date: Mon, 16 May 2011 16:02:39 +0800 Subject: [PATCH 1926/2556] drm/i915: fix user irq miss in BSD ring on g4x commit 5bfa1063a775836a84f97e4df863fc36e1f856ad upstream. On g4x, user interrupt in BSD ring is missed. This is because though g4x and ironlake share the same bsd_ring, their interrupt control interfaces have _two_ differences. 1.different irq enable/disable functions: On g4x are i915_enable_irq and i915_disable_irq. On ironlake are ironlake_enable_irq and ironlake_disable_irq. 2.different irq flag: On g4x user interrupt flag in BSD ring on is I915_BSD_USER_INTERRUPT. On ironlake is GT_BSD_USER_INTERRUPT Old bsd_ring_get/put_irq call ring_get_irq and ring_get_irq. ring_get_irq and ring_put_irq only call ironlake_enable/disable_irq. So comes the irq miss on g4x. To fix this, as other rings' code do, conditionally call different functions(i915_enable/disable_irq and ironlake_enable/disable_irq) and use different interrupt flags in bsd_ring_get/put_irq. Signed-off-by: Boqun Feng Reviewed-by: Xiang, Haihao Signed-off-by: Keith Packard Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_ringbuffer.c | 29 +++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 445f27efe677f..bd087df02b8e5 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -684,12 +684,37 @@ gen6_ring_put_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag) static bool bsd_ring_get_irq(struct intel_ring_buffer *ring) { - return ring_get_irq(ring, GT_BSD_USER_INTERRUPT); + struct drm_device *dev = ring->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + + if (!dev->irq_enabled) + return false; + + spin_lock(&ring->irq_lock); + if (ring->irq_refcount++ == 0) { + if (IS_G4X(dev)) + i915_enable_irq(dev_priv, I915_BSD_USER_INTERRUPT); + else + ironlake_enable_irq(dev_priv, GT_BSD_USER_INTERRUPT); + } + spin_unlock(&ring->irq_lock); + + return true; } static void bsd_ring_put_irq(struct intel_ring_buffer *ring) { - ring_put_irq(ring, GT_BSD_USER_INTERRUPT); + struct drm_device *dev = ring->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + + spin_lock(&ring->irq_lock); + if (--ring->irq_refcount == 0) { + if (IS_G4X(dev)) + i915_disable_irq(dev_priv, I915_BSD_USER_INTERRUPT); + else + ironlake_disable_irq(dev_priv, GT_BSD_USER_INTERRUPT); + } + spin_unlock(&ring->irq_lock); } static int From ee2e1006af9c8ed994adeaa6cb60b76a6650ff7a Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 19 May 2011 11:07:57 -0400 Subject: [PATCH 1927/2556] drm/radeon/evergreen/btc/fusion: setup hdp to invalidate and flush when asked commit f25a5c63bfa017498c9adecb24d649ae96ba5c68 upstream. This needs to be explicitly set on btc. It's set by default on evergreen/fusion, so it fine to just unconditionally enable it for all chips. Signed-off-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/evergreen.c | 6 +++++- drivers/gpu/drm/radeon/evergreend.h | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 627ba8610def1..5d6774ab77284 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -1585,7 +1585,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) u32 sq_stack_resource_mgmt_2; u32 sq_stack_resource_mgmt_3; u32 vgt_cache_invalidation; - u32 hdp_host_path_cntl; + u32 hdp_host_path_cntl, tmp; int i, j, num_shader_engines, ps_thread_count; switch (rdev->family) { @@ -2145,6 +2145,10 @@ static void evergreen_gpu_init(struct radeon_device *rdev) for (i = SQ_ALU_CONST_BUFFER_SIZE_HS_0; i < 0x29000; i += 4) WREG32(i, 0); + tmp = RREG32(HDP_MISC_CNTL); + tmp |= HDP_FLUSH_INVALIDATE_CACHE; + WREG32(HDP_MISC_CNTL, tmp); + hdp_host_path_cntl = RREG32(HDP_HOST_PATH_CNTL); WREG32(HDP_HOST_PATH_CNTL, hdp_host_path_cntl); diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h index 447b622ae62ae..621d61c3cfb9c 100644 --- a/drivers/gpu/drm/radeon/evergreend.h +++ b/drivers/gpu/drm/radeon/evergreend.h @@ -64,6 +64,8 @@ #define GB_BACKEND_MAP 0x98FC #define DMIF_ADDR_CONFIG 0xBD4 #define HDP_ADDR_CONFIG 0x2F48 +#define HDP_MISC_CNTL 0x2F4C +#define HDP_FLUSH_INVALIDATE_CACHE (1 << 0) #define CC_SYS_RB_BACKEND_DISABLE 0x3F88 #define GC_USER_RB_BACKEND_DISABLE 0x9B7C From 00871cab71537bed0eef99e014f690263f6c1286 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 19 May 2011 14:14:43 +1000 Subject: [PATCH 1928/2556] drm/radeon/kms: add wait idle ioctl for eg->cayman commit 97bfd0acd32e9639c9136e03955d574655d5cc2b upstream. None of the latest GPUs had this hooked up, this is necessary for correct operation in a lot of cases, however we should test this on a few GPUs in these families as we've had problems in this area before. Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/radeon/radeon_asic.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 793c5e6026ad7..04152b7c0dd6b 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -782,6 +782,7 @@ static struct radeon_asic evergreen_asic = { .hpd_fini = &evergreen_hpd_fini, .hpd_sense = &evergreen_hpd_sense, .hpd_set_polarity = &evergreen_hpd_set_polarity, + .ioctl_wait_idle = r600_ioctl_wait_idle, .gui_idle = &r600_gui_idle, .pm_misc = &evergreen_pm_misc, .pm_prepare = &evergreen_pm_prepare, @@ -828,6 +829,7 @@ static struct radeon_asic sumo_asic = { .hpd_fini = &evergreen_hpd_fini, .hpd_sense = &evergreen_hpd_sense, .hpd_set_polarity = &evergreen_hpd_set_polarity, + .ioctl_wait_idle = r600_ioctl_wait_idle, .gui_idle = &r600_gui_idle, .pm_misc = &evergreen_pm_misc, .pm_prepare = &evergreen_pm_prepare, @@ -874,6 +876,8 @@ static struct radeon_asic btc_asic = { .hpd_fini = &evergreen_hpd_fini, .hpd_sense = &evergreen_hpd_sense, .hpd_set_polarity = &evergreen_hpd_set_polarity, + .ioctl_wait_idle = r600_ioctl_wait_idle, + .ioctl_wait_idle = r600_ioctl_wait_idle, .gui_idle = &r600_gui_idle, .pm_misc = &evergreen_pm_misc, .pm_prepare = &evergreen_pm_prepare, From a0a238f7cb684c0ea76a5817d1c92e2c7b0b7bd2 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 18 Mar 2011 20:21:23 -0400 Subject: [PATCH 1929/2556] SUNRPC: Deal with the lack of a SYN_SENT sk->sk_state_change callback... commit fe19a96b10032035a35779f42ad59e35d6dd8ffd upstream. The TCP connection state code depends on the state_change() callback being called when the SYN_SENT state is set. However the networking layer doesn't actually call us back in that case. Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/xprtsock.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 1e336a06d3e63..3e0b5f146b059 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -1344,7 +1344,6 @@ static void xs_tcp_state_change(struct sock *sk) case TCP_CLOSE_WAIT: /* The server initiated a shutdown of the socket */ xprt_force_disconnect(xprt); - case TCP_SYN_SENT: xprt->connect_cookie++; case TCP_CLOSING: /* @@ -1758,6 +1757,7 @@ static void xs_tcp_reuse_connection(struct sock_xprt *transport) static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) { struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); + int ret = -ENOTCONN; if (!transport->inet) { struct sock *sk = sock->sk; @@ -1789,12 +1789,22 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) } if (!xprt_bound(xprt)) - return -ENOTCONN; + goto out; /* Tell the socket layer to start connecting... */ xprt->stat.connect_count++; xprt->stat.connect_start = jiffies; - return kernel_connect(sock, xs_addr(xprt), xprt->addrlen, O_NONBLOCK); + ret = kernel_connect(sock, xs_addr(xprt), xprt->addrlen, O_NONBLOCK); + switch (ret) { + case 0: + case -EINPROGRESS: + /* SYN_SENT! */ + xprt->connect_cookie++; + if (xprt->reestablish_timeout < XS_TCP_INIT_REEST_TO) + xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO; + } +out: + return ret; } /** From 6844274a3ca81e5643b53c9844796f3dc11b5518 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 26 May 2011 14:26:35 -0400 Subject: [PATCH 1930/2556] NFSv4: Handle expired stateids when the lease is still valid commit 0ced63d1a245ac11241a5d37932e6d04d9c8040d upstream. Currently, if the server returns NFS4ERR_EXPIRED in reply to a READ or WRITE, but the RENEW test determines that the lease is still active, we fail to recover and end up looping forever in a READ/WRITE + RENEW death spiral. Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- fs/nfs/nfs4proc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 0a07e353a9613..31c1ad7e9f5b3 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -258,9 +258,11 @@ static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, break; nfs4_schedule_stateid_recovery(server, state); goto wait_on_recovery; + case -NFS4ERR_EXPIRED: + if (state != NULL) + nfs4_schedule_stateid_recovery(server, state); case -NFS4ERR_STALE_STATEID: case -NFS4ERR_STALE_CLIENTID: - case -NFS4ERR_EXPIRED: nfs4_schedule_lease_recovery(clp); goto wait_on_recovery; #if defined(CONFIG_NFS_V4_1) @@ -3504,9 +3506,11 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, break; nfs4_schedule_stateid_recovery(server, state); goto wait_on_recovery; + case -NFS4ERR_EXPIRED: + if (state != NULL) + nfs4_schedule_stateid_recovery(server, state); case -NFS4ERR_STALE_STATEID: case -NFS4ERR_STALE_CLIENTID: - case -NFS4ERR_EXPIRED: nfs4_schedule_lease_recovery(clp); goto wait_on_recovery; #if defined(CONFIG_NFS_V4_1) @@ -4397,6 +4401,7 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl) case -ESTALE: goto out; case -NFS4ERR_EXPIRED: + nfs4_schedule_stateid_recovery(server, state); case -NFS4ERR_STALE_CLIENTID: case -NFS4ERR_STALE_STATEID: nfs4_schedule_lease_recovery(server->nfs_client); From 0ab692407602f96ad71d62f9c551fdba4529c803 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 26 May 2011 14:26:35 -0400 Subject: [PATCH 1931/2556] NFSv4.1: Fix the handling of NFS4ERR_SEQ_MISORDERED errors commit 444f72fe7e7b5f4db34cee933fa3546ebb8e9122 upstream. Currently, the call to nfs4_schedule_session_recovery() will actually just result in a test of the lease when what we really want is to force a session reset. Signed-off-by: Trond Myklebust Signed-off-by: Greg Kroah-Hartman --- fs/nfs/nfs4state.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 6221640397dd2..6eea1a62c8d75 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1446,7 +1446,10 @@ static int nfs4_reclaim_lease(struct nfs_client *clp) #ifdef CONFIG_NFS_V4_1 void nfs4_schedule_session_recovery(struct nfs4_session *session) { - nfs4_schedule_lease_recovery(session->clp); + struct nfs_client *clp = session->clp; + + set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); + nfs4_schedule_lease_recovery(clp); } void nfs41_handle_recall_slot(struct nfs_client *clp) @@ -1528,6 +1531,7 @@ static int nfs4_reset_session(struct nfs_client *clp) status = nfs4_recovery_handle_error(clp, status); goto out; } + clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); /* create_session negotiated new slot table */ clear_bit(NFS4CLNT_RECALL_SLOT, &clp->cl_state); From 872b58c1256fa27e17c52b098a55a4d6c92b2584 Mon Sep 17 00:00:00 2001 From: Hemant Pedanekar Date: Tue, 5 Apr 2011 12:32:50 +0530 Subject: [PATCH 1932/2556] PCI: Add quirk for setting valid class for TI816X Endpoint commit 63c4408074cbcc070ac17fc10e524800eb9bd0b0 upstream. TI816X (common name for DM816x/C6A816x/AM389x family) devices configured to boot as PCIe Endpoint have class code = 0. This makes kernel PCI bus code to skip allocating BARs to these devices resulting into following type of error when trying to enable them: "Device 0000:01:00.0 not available because of resource collisions" The device cannot be operated because of the above issue. This patch adds a ID specific (TI VENDOR ID and 816X DEVICE ID based) 'early' fixup quirk to replace class code with PCI_CLASS_MULTIMEDIA_VIDEO as class. Signed-off-by: Hemant Pedanekar Signed-off-by: Jesse Barnes Signed-off-by: Greg Kroah-Hartman --- drivers/pci/quirks.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index bd80f63784635..a1e4f6156913f 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2784,6 +2784,16 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x342e, vtd_mask_spec_errors); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x3c28, vtd_mask_spec_errors); #endif +static void __devinit fixup_ti816x_class(struct pci_dev* dev) +{ + /* TI 816x devices do not have class code set when in PCIe boot mode */ + if (dev->class == PCI_CLASS_NOT_DEFINED) { + dev_info(&dev->dev, "Setting PCI class for 816x PCIe device\n"); + dev->class = PCI_CLASS_MULTIMEDIA_VIDEO; + } +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_TI, 0xb800, fixup_ti816x_class); + static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end) { From abe87a0b0ba5a0cddfab30bbafc6e2b121537b15 Mon Sep 17 00:00:00 2001 From: "Tian, Kevin" Date: Thu, 12 May 2011 10:56:08 +0800 Subject: [PATCH 1933/2556] xen mmu: fix a race window causing leave_mm BUG() commit 7899891c7d161752f29abcc9bc0a9c6c3a3af26c upstream. There's a race window in xen_drop_mm_ref, where remote cpu may exit dirty bitmap between the check on this cpu and the point where remote cpu handles drop request. So in drop_other_mm_ref we need check whether TLB state is still lazy before calling into leave_mm. This bug is rarely observed in earlier kernel, but exaggerated by the commit 831d52bc153971b70e64eccfbed2b232394f22f8 ("x86, mm: avoid possible bogus tlb entries by clearing prev mm_cpumask after switching mm") which clears bitmap after changing the TLB state. the call trace is as below: --------------------------------- kernel BUG at arch/x86/mm/tlb.c:61! invalid opcode: 0000 [#1] SMP last sysfs file: /sys/devices/system/xen_memory/xen_memory0/info/current_kb CPU 1 Modules linked in: 8021q garp xen_netback xen_blkback blktap blkback_pagemap nbd bridge stp llc autofs4 ipmi_devintf ipmi_si ipmi_msghandler lockd sunrpc bonding ipv6 xenfs dm_multipath video output sbs sbshc parport_pc lp parport ses enclosure snd_seq_dummy snd_seq_oss snd_seq_midi_event snd_seq snd_seq_device serio_raw bnx2 snd_pcm_oss snd_mixer_oss snd_pcm snd_timer iTCO_wdt snd soundcore snd_page_alloc i2c_i801 iTCO_vendor_support i2c_core pcs pkr pata_acpi ata_generic ata_piix shpchp mptsas mptscsih mptbase [last unloaded: freq_table] Pid: 25581, comm: khelper Not tainted 2.6.32.36fixxen #1 Tecal RH2285 RIP: e030:[] [] leave_mm+0x15/0x46 RSP: e02b:ffff88002805be48 EFLAGS: 00010046 RAX: 0000000000000000 RBX: 0000000000000001 RCX: ffff88015f8e2da0 RDX: ffff88002805be78 RSI: 0000000000000000 RDI: 0000000000000001 RBP: ffff88002805be48 R08: ffff88009d662000 R09: dead000000200200 R10: dead000000100100 R11: ffffffff814472b2 R12: ffff88009bfc1880 R13: ffff880028063020 R14: 00000000000004f6 R15: 0000000000000000 FS: 00007f62362d66e0(0000) GS:ffff880028058000(0000) knlGS:0000000000000000 CS: e033 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000003aabc11909 CR3: 000000009b8ca000 CR4: 0000000000002660 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 00000000000000 00 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process khelper (pid: 25581, threadinfo ffff88007691e000, task ffff88009b92db40) Stack: ffff88002805be68 ffffffff8100e4ae 0000000000000001 ffff88009d733b88 <0> ffff88002805be98 ffffffff81087224 ffff88002805be78 ffff88002805be78 <0> ffff88015f808360 00000000000004f6 ffff88002805bea8 ffffffff81010108 Call Trace: [] drop_other_mm_ref+0x2a/0x53 [] generic_smp_call_function_single_interrupt+0xd8/0xfc [] xen_call_function_single_interrupt+0x13/0x28 [] handle_IRQ_event+0x66/0x120 [] handle_percpu_irq+0x41/0x6e [] __xen_evtchn_do_upcall+0x1ab/0x27d [] xen_evtchn_do_upcall+0x33/0x46 [] xen_do_hyper visor_callback+0x1e/0x30 [] ? _spin_unlock_irqrestore+0x15/0x17 [] ? xen_restore_fl_direct_end+0x0/0x1 [] ? flush_old_exec+0x3ac/0x500 [] ? load_elf_binary+0x0/0x17ef [] ? load_elf_binary+0x0/0x17ef [] ? load_elf_binary+0x398/0x17ef [] ? need_resched+0x23/0x2d [] ? process_measurement+0xc0/0xd7 [] ? load_elf_binary+0x0/0x17ef [] ? search_binary_handler+0xc8/0x255 [] ? do_execve+0x1c3/0x29e [] ? sys_execve+0x43/0x5d [] ? __call_usermodehelper+0x0/0x6f [] ? kernel_execve+0x68/0xd0 [] ? __call_usermodehelper+0x0/0x6f [] ? xen_restore_fl_direct_end+0x0/0x1 [] ? ____call_usermodehelper+0x113/0x11e [] ? child_rip+0xa/0x20 [] ? __call_usermodehelper+0x0/0x6f [] ? int_ret_from_sys_call+0x7/0x1b [] ? retint_restore_args+0x5/0x6 [] ? child_rip+0x0/0x20 Code: 41 5e 41 5f c9 c3 55 48 89 e5 0f 1f 44 00 00 e8 17 ff ff ff c9 c3 55 48 89 e5 0f 1f 44 00 00 65 8b 04 25 c8 55 01 00 ff c8 75 04 <0f> 0b eb fe 65 48 8b 34 25 c0 55 01 00 48 81 c6 b8 02 00 00 e8 RIP [] leave_mm+0x15/0x46 RSP ---[ end trace ce9cee6832a9c503 ]--- Tested-by: Maoxiaoyun Signed-off-by: Kevin Tian [v1: Fleshed out the git description a bit] Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: Greg Kroah-Hartman --- arch/x86/xen/mmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index 60205625e47e0..d835bc2a6a988 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -1138,7 +1138,7 @@ static void drop_other_mm_ref(void *info) active_mm = percpu_read(cpu_tlbstate.active_mm); - if (active_mm == mm) + if (active_mm == mm && percpu_read(cpu_tlbstate.state) != TLBSTATE_OK) leave_mm(smp_processor_id()); /* If this cpu still has a stale cr3 reference, then make sure From ad106f8c9b2493de31e4b560b9aac000aa81ba10 Mon Sep 17 00:00:00 2001 From: Lukas Czerner Date: Fri, 20 May 2011 13:49:04 -0400 Subject: [PATCH 1934/2556] ext4: Use schedule_timeout_interruptible() for waiting in lazyinit thread commit 4ed5c033c11b33149d993734a6a8de1016e8f03f upstream. In order to make lazyinit eat approx. 10% of io bandwidth at max, we are sleeping between zeroing each single inode table. For that purpose we are using timer which wakes up thread when it expires. It is set via add_timer() and this may cause troubles in the case that thread has been woken up earlier and in next iteration we call add_timer() on still running timer hence hitting BUG_ON in add_timer(). We could fix that by using mod_timer() instead however we can use schedule_timeout_interruptible() for waiting and hence simplifying things a lot. This commit exchange the old "waiting mechanism" with simple schedule_timeout_interruptible(), setting the time to sleep. Hence we do not longer need li_wait_daemon waiting queue and others, so get rid of it. Addresses-Red-Hat-Bugzilla: #699708 Signed-off-by: Lukas Czerner Signed-off-by: "Theodore Ts'o" Reviewed-by: Eric Sandeen Signed-off-by: Greg Kroah-Hartman --- fs/ext4/ext4.h | 4 ---- fs/ext4/super.c | 31 ++++++------------------------- 2 files changed, 6 insertions(+), 29 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 3aa0b72b3b94b..0783b4f272990 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1590,12 +1590,8 @@ void ext4_get_group_no_and_offset(struct super_block *sb, ext4_fsblk_t blocknr, */ struct ext4_lazy_init { unsigned long li_state; - - wait_queue_head_t li_wait_daemon; wait_queue_head_t li_wait_task; - struct timer_list li_timer; struct task_struct *li_task; - struct list_head li_request_list; struct mutex li_list_mtx; }; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index f3e1b18867775..9bada495f3dc3 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2645,12 +2645,6 @@ static void print_daily_error_info(unsigned long arg) mod_timer(&sbi->s_err_report, jiffies + 24*60*60*HZ); /* Once a day */ } -static void ext4_lazyinode_timeout(unsigned long data) -{ - struct task_struct *p = (struct task_struct *)data; - wake_up_process(p); -} - /* Find next suitable group and run ext4_init_inode_table */ static int ext4_run_li_request(struct ext4_li_request *elr) { @@ -2698,7 +2692,7 @@ static int ext4_run_li_request(struct ext4_li_request *elr) /* * Remove lr_request from the list_request and free the - * request tructure. Should be called with li_list_mtx held + * request structure. Should be called with li_list_mtx held */ static void ext4_remove_li_request(struct ext4_li_request *elr) { @@ -2744,14 +2738,10 @@ static int ext4_lazyinit_thread(void *arg) struct ext4_lazy_init *eli = (struct ext4_lazy_init *)arg; struct list_head *pos, *n; struct ext4_li_request *elr; - unsigned long next_wakeup; - DEFINE_WAIT(wait); + unsigned long next_wakeup, cur; BUG_ON(NULL == eli); - eli->li_timer.data = (unsigned long)current; - eli->li_timer.function = ext4_lazyinode_timeout; - eli->li_task = current; wake_up(&eli->li_wait_task); @@ -2785,19 +2775,15 @@ static int ext4_lazyinit_thread(void *arg) if (freezing(current)) refrigerator(); - if ((time_after_eq(jiffies, next_wakeup)) || + cur = jiffies; + if ((time_after_eq(cur, next_wakeup)) || (MAX_JIFFY_OFFSET == next_wakeup)) { cond_resched(); continue; } - eli->li_timer.expires = next_wakeup; - add_timer(&eli->li_timer); - prepare_to_wait(&eli->li_wait_daemon, &wait, - TASK_INTERRUPTIBLE); - if (time_before(jiffies, next_wakeup)) - schedule(); - finish_wait(&eli->li_wait_daemon, &wait); + schedule_timeout_interruptible(next_wakeup - cur); + if (kthread_should_stop()) { ext4_clear_request_list(); goto exit_thread; @@ -2821,12 +2807,10 @@ static int ext4_lazyinit_thread(void *arg) goto cont_thread; } mutex_unlock(&eli->li_list_mtx); - del_timer_sync(&ext4_li_info->li_timer); eli->li_task = NULL; wake_up(&eli->li_wait_task); kfree(ext4_li_info); - ext4_lazyinit_task = NULL; ext4_li_info = NULL; mutex_unlock(&ext4_li_mtx); @@ -2854,7 +2838,6 @@ static int ext4_run_lazyinit_thread(void) if (IS_ERR(ext4_lazyinit_task)) { int err = PTR_ERR(ext4_lazyinit_task); ext4_clear_request_list(); - del_timer_sync(&ext4_li_info->li_timer); kfree(ext4_li_info); ext4_li_info = NULL; printk(KERN_CRIT "EXT4: error %d creating inode table " @@ -2903,9 +2886,7 @@ static int ext4_li_info_new(void) INIT_LIST_HEAD(&eli->li_request_list); mutex_init(&eli->li_list_mtx); - init_waitqueue_head(&eli->li_wait_daemon); init_waitqueue_head(&eli->li_wait_task); - init_timer(&eli->li_timer); eli->li_state |= EXT4_LAZYINIT_QUIT; ext4_li_info = eli; From 035165d752508146f79fe90631fe6b95022d8833 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 31 May 2011 11:31:41 -0700 Subject: [PATCH 1935/2556] AppArmor: fix oops in apparmor_setprocattr commit a5b2c5b2ad5853591a6cac6134cd0f599a720865 upstream. When invalid parameters are passed to apparmor_setprocattr a NULL deref oops occurs when it tries to record an audit message. This is because it is passing NULL for the profile parameter for aa_audit. But aa_audit now requires that the profile passed is not NULL. Fix this by passing the current profile on the task that is trying to setprocattr. Signed-off-by: Kees Cook Signed-off-by: John Johansen Signed-off-by: James Morris Signed-off-by: Greg Kroah-Hartman --- security/apparmor/lsm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index b7106f192b75d..e2e902f39ac2f 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -592,7 +592,8 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, sa.aad.op = OP_SETPROCATTR; sa.aad.info = name; sa.aad.error = -EINVAL; - return aa_audit(AUDIT_APPARMOR_DENIED, NULL, GFP_KERNEL, + return aa_audit(AUDIT_APPARMOR_DENIED, + __aa_current_profile(), GFP_KERNEL, &sa, NULL); } } else if (strcmp(name, "exec") == 0) { From 4b7a6d2528bfb625cc359d89ac16439b0ec744ea Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 3 Jun 2011 10:35:11 +0900 Subject: [PATCH 1936/2556] Linux 2.6.38.8 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 23ce04c556587..66b7e76956cc8 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 38 -EXTRAVERSION = .7 +EXTRAVERSION = .8 NAME = Flesh-Eating Bats with Fangs # *DOCUMENTATION* From 9bb51f3a9b4fabfd5b9ec38c4a66a7a1c1f87809 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sun, 5 Jun 2011 00:49:55 -0400 Subject: [PATCH 1937/2556] clean up merge error --- drivers/cpufreq/cpufreq_stats.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index b6f110a6473d0..ebb536ce7acc7 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -349,10 +349,6 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, break; case CPU_DOWN_PREPARE: case CPU_DOWN_PREPARE_FROZEN: - cpufreq_stats_free_sysfs(cpu); - break; - case CPU_DEAD: - case CPU_DEAD_FROZEN: cpufreq_stats_free_table(cpu); break; case CPU_DOWN_FAILED: From d666293133a02ba2085c72522a045ee15b24f9c2 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sun, 5 Jun 2011 01:06:41 -0400 Subject: [PATCH 1938/2556] Revert "Fix minor bug in EDID code" This reverts commit 2766ba173c80ce800c73ada5a6b3179690f872af. --- drivers/video/msm/hdmi/edid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/msm/hdmi/edid.c b/drivers/video/msm/hdmi/edid.c index 642a000701296..36f629c2be43b 100644 --- a/drivers/video/msm/hdmi/edid.c +++ b/drivers/video/msm/hdmi/edid.c @@ -575,7 +575,7 @@ u8 edid_simple_parsing(struct hdmi_info *hdmi) // The block will be an array of indexes for (byte = 1; byte < blockLen; byte++) { - index = edid_buf[baseOffset + dbcOffset + byte] & 0x7f; + index = edid_buf[baseOffset + dbcOffset] & 0x7f; if (index > 63) { From a6a57e829623400430a7cf74e556fc896ad43404 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sun, 5 Jun 2011 01:06:56 -0400 Subject: [PATCH 1939/2556] Revert "Missing file" This reverts commit 837c9eab95cdbdf96d297a30da913ea125041480. --- drivers/video/msm/hdmi/include/edid.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/video/msm/hdmi/include/edid.h b/drivers/video/msm/hdmi/include/edid.h index 4a6f9ac436c52..1b7e4587e5c6e 100644 --- a/drivers/video/msm/hdmi/include/edid.h +++ b/drivers/video/msm/hdmi/include/edid.h @@ -79,7 +79,4 @@ struct edid_info_struct { #define HDMI_SIGNATURE_LEN 0x03 #define CEC_PHYS_ADDR_LEN 0x02 - -#define EDID_BIT(b) (1 << b) - #endif From 916ccabeb300772857d94885dbe3a139438c3677 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sun, 5 Jun 2011 01:07:13 -0400 Subject: [PATCH 1940/2556] Revert "Fix EDID logic" This reverts commit 61e7aa278c32026a83cf412c7e8cb36230ae76d9. --- drivers/video/msm/hdmi/edid.c | 824 ++++++++++++++++------------------ 1 file changed, 396 insertions(+), 428 deletions(-) diff --git a/drivers/video/msm/hdmi/edid.c b/drivers/video/msm/hdmi/edid.c index 36f629c2be43b..7a38db2235337 100644 --- a/drivers/video/msm/hdmi/edid.c +++ b/drivers/video/msm/hdmi/edid.c @@ -12,7 +12,7 @@ * Common function for accessing/debugging EDID data. * Reference: - http://en.wikipedia.org/wiki/Extended_display_identification_data + http://en.wikipedia.org/wiki/Extended_display_identification_data */ #include @@ -29,180 +29,181 @@ #endif static struct video_mode established_timing_db[] = { - {800, 600, 60, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 60 Hz"}, - {800, 600, 56, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 56 Hz"}, - {640, 480, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 75 Hz"}, - {640, 480, 72, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 72 Hz"}, - {640, 480, 67, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 67 Hz"}, - {640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 60 Hz"}, - {720, 400, 88, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 88 Hz"}, - {720, 400, 70, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 70 Hz"}, - - {1280, 1024, 75, ASPECT(4, 3), PROGRESSIVE, false, "1280x1024@75 Hz"}, - {1024, 768, 75, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@75 Hz"}, - {1024, 768, 70, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@70 Hz"}, - {1024, 768, 60, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@60 Hz"}, - {1024, 768, 87, ASPECT(4, 3), INTERLACE, false, "1024x768@87 Hz (Interlaced)"}, - {832, 624, 75, ASPECT(4, 3), PROGRESSIVE, false, "832x624@75 Hz"}, - {800, 600, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600@75 Hz"}, - {800, 600, 72, ASPECT(4, 3), PROGRESSIVE, false, "800x600@72 Hz"}, - - {1152, 870, 75, ASPECT(4, 3), PROGRESSIVE, false, "1152x870 @ 75 Hz"}, + {800, 600, 60, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 60 Hz"}, + {800, 600, 56, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 56 Hz"}, + {640, 480, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 75 Hz"}, + {640, 480, 72, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 72 Hz"}, + {640, 480, 67, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 67 Hz"}, + {640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 60 Hz"}, + {720, 400, 88, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 88 Hz"}, + {720, 400, 70, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 70 Hz"}, + + {1280, 1024, 75, ASPECT(4, 3), PROGRESSIVE, false, "1280x1024@75 Hz"}, + {1024, 768, 75, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@75 Hz"}, + {1024, 768, 70, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@70 Hz"}, + {1024, 768, 60, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@60 Hz"}, + {1024, 768, 87, ASPECT(4, 3), INTERLACE, false, + "1024x768@87 Hz (Interlaced)"}, + {832, 624, 75, ASPECT(4, 3), PROGRESSIVE, false, "832x624@75 Hz"}, + {800, 600, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600@75 Hz"}, + {800, 600, 72, ASPECT(4, 3), PROGRESSIVE, false, "800x600@72 Hz"}, + + {1152, 870, 75, ASPECT(4, 3), PROGRESSIVE, false, "1152x870 @ 75 Hz"}, }; static struct video_mode standard_timing_db[8]; static struct video_mode additional_timing_db[] = { - {640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, - " 1 DMT0659 4:3 640x480p @ 59.94/60Hz"}, - {720, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, - " 2 480p 4:3 720x480p @ 59.94/60Hz"}, - {720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, - " 3 480pH 16:9 720x480p @ 59.94/60Hz"}, - {1280, 720, 60, ASPECT(16, 9), PROGRESSIVE, false, - " 4 720p 16:9 1280x720p @ 59.94/60Hz"}, - {1920, 1080, 60, ASPECT(4, 3), INTERLACE, false, - " 5 1080i 16:9 1920x1080i @ 59.94/60Hz"}, - {720, 480, 60, ASPECT(4, 3), INTERLACE, false, - " 6 480i 4:3 720(1440)x480i @ 59.94/60Hz"}, - {720, 480, 60, ASPECT(16, 9), INTERLACE, false, - " 7 480iH 16:9 720(1440)x480i @ 59.94/60Hz"}, - {720, 240, 60, ASPECT(4, 3), PROGRESSIVE, false, - " 8 240p 4:3 720(1440)x240p @ 59.94/60Hz"}, - {720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, - " 9 240pH 16:9 720(1440)x240p @ 59.94/60Hz"}, - {2880, 480, 60, ASPECT(4, 3), INTERLACE, false, - "10 480i4x 4:3 (2880)x480i @ 59.94/60Hz"}, - {2880, 480, 60, ASPECT(16, 9), INTERLACE, false, - "11 480i4xH 16:9 (2880)x480i @ 59.94/60Hz"}, - {2880, 240, 60, ASPECT(4, 3), PROGRESSIVE, false, - "12 240p4x 4:3 (2880)x240p @ 59.94/60Hz"}, - {2880, 240, 60, ASPECT(16, 9), PROGRESSIVE, false, - "13 240p4xH 16:9 (2880)x240p @ 59.94/60Hz"}, - {1440, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, - "14 480p2x 4:3 1440x480p @ 59.94/60Hz"}, - {1440, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, - "15 480p2xH 16:9 1440x480p @ 59.94/60Hz"}, - {1920, 1080, 60, ASPECT(16, 9), PROGRESSIVE, false, - "16 1080p 16:9 1920x1080p @ 59.94/60Hz"}, - {720, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, - "17 576p 4:3 720x576p @ 50Hz"}, - {720, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, - "18 576pH 16:9 720x576p @ 50Hz"}, - {1280, 720, 50, ASPECT(16, 9), PROGRESSIVE, false, - "19 720p50 16:9 1280x720p @ 50Hz"}, - {1920, 1080, 50, ASPECT(16, 9), INTERLACE, false, - "20 1080i25 16:9 1920x1080i @ 50Hz*"}, - {1440, 576, 50, ASPECT(4, 3), INTERLACE, false, - "21 576i 4:3 720(1440)x576i @ 50Hz"}, - {1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, - "22 576iH 16:9 720(1440)x576i @ 50Hz"}, - {720, 288, 50, ASPECT(4, 3), PROGRESSIVE, false, - "23 288p 4:3 720(1440)x288p @ 50Hz"}, - {720, 288, 50, ASPECT(16, 9), PROGRESSIVE, false, - "24 288pH 16:9 720(1440)x288p @ 50Hz"}, - {2880, 576, 50, ASPECT(4, 3), INTERLACE, false, - "25 576i4x 4:3 (2880)x576i @ 50Hz"}, - {2880, 576, 50, ASPECT(16, 9), INTERLACE, false, - "26 576i4xH 16:9 (2880)x576i @ 50Hz"}, - {2880, 288, 50, ASPECT(4, 3), PROGRESSIVE, false, - "27 288p4x 4:3 (2880)x288p @ 50Hz"}, - {2880, 288, 50, ASPECT(16, 9), PROGRESSIVE, false, - "28 288p4xH 16:9 (2880)x288p @ 50Hz"}, - {1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, - "29 576p2x 4:3 1440x576p @ 50Hz"}, - {1440, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, - "30 576p2xH 16:9 1440x576p @ 50Hz"}, - {1920, 1080, 50, ASPECT(16, 9), PROGRESSIVE, false, - "31 1080p50 16:9 1920x1080p @ 50Hz"}, - {1920, 1080, 24, ASPECT(16, 9), PROGRESSIVE, false, - "32 1080p24 16:9 1920x1080p @ 23.98/24Hz"}, - {1920, 1080, 25, ASPECT(16, 9), PROGRESSIVE, false, - "33 1080p25 16:9 1920x1080p @ 25Hz"}, - {1920, 1080, 30, ASPECT(16, 9), PROGRESSIVE, false, - "34 1080p30 16:9 1920x1080p @ 29.97/30Hz"}, - {2880, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, - "35 480p4x 4:3 (2880)x480p @ 59.94/60Hz"}, - {2880, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, - "36 480p4xH 16:9 (2880)x480p @ 59.94/60Hz"}, - {2880, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, - "37 576p4x 4:3 (2880)x576p @ 50Hz"}, - {2880, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, - "38 576p4xH 16:9 (2880)x576p @ 50Hz"}, - {1920, 1080, 50, ASPECT(16, 9), INTERLACE, false, - "39 108Oi25 16:9 1920x1080i(1250 Total) @ 50Hz*"}, - {1920, 1080, 100, ASPECT(16, 9), INTERLACE, false, - "40 1080i50 16:9 1920x1080i @ 100Hz"}, - {1280, 720, 100, ASPECT(16, 9), PROGRESSIVE, false, - "41 720p100 16:9 1280x720p @ 100Hz"}, - {720, 576, 100, ASPECT(4, 3), PROGRESSIVE, false, - "42 576p100 4:3 720x576p @ 100Hz"}, - {720, 576, 100, ASPECT(16, 9), PROGRESSIVE, false, - "43 576p100H 16:9 720x576p @ 100Hz"}, - {720, 576, 100, ASPECT(4, 3), INTERLACE, false, - "44 576i50 4:3 720(1440)x576i @ 100Hz"}, - {720, 576, 100, ASPECT(16, 9), INTERLACE, false, - "45 576i50H 16:9 720(1440)x576i @ 100Hz"}, - {1920, 1080, 120, ASPECT(16, 9), INTERLACE, false, - "46 1080i60 16:9 1920x1080i @ 119.88/120Hz"}, - {1280, 720, 120, ASPECT(16, 9), PROGRESSIVE, false, - "47 720p120 16:9 1280x720p @ 119.88/120Hz"}, - {720, 480, 120, ASPECT(4, 3), PROGRESSIVE, false, - "48 480p119 4:3 720x480p @ 119.88/120Hz"}, - {720, 480, 120, ASPECT(16, 9), PROGRESSIVE, false, - "49 480p119H 16:9 720x480p @ 119.88/120Hz"}, - {720, 480, 120, ASPECT(4, 3), INTERLACE, false, - "50 480i59 4:3 720(1440)x480i @ 119.88/120Hz"}, - {720, 480, 120, ASPECT(16, 9), INTERLACE, false, - "51 480i59H 16:9 720(1440)x480i @ 119.88/120Hz"}, - {720, 576, 200, ASPECT(4, 3), PROGRESSIVE, false, - "52 576p200 4:3 720x576p @ 200Hz"}, - {720, 576, 200, ASPECT(16, 9), PROGRESSIVE, false, - "53 576p200H 16:9 720x576p @ 200Hz"}, - {720, 576, 200, ASPECT(4, 3), INTERLACE, false, - "54 576i100 4:3 720(1440)x576i @ 200Hz"}, - {720, 576, 200, ASPECT(16, 9), INTERLACE, false, - "55 576i100H 16:9 720(1440)x576i @ 200Hz"}, - {720, 480, 240, ASPECT(4, 3), PROGRESSIVE, false, - "56 480p239 4:3 720x480p @ 239.76/240Hz"}, - {720, 480, 240, ASPECT(16, 9), PROGRESSIVE, false, - "57 480p239H 16:9 720x480p @ 239.76/240Hz"}, - {720, 480, 240, ASPECT(4, 3), INTERLACE, false, - "58 480i119 4:3 720(1440)x480i @ 239.76/240Hz"}, - {720, 480, 240, ASPECT(16, 9), INTERLACE, false, - "59 480i119H 16:9 720(1440)x480i @ 239.76/240Hz"}, - {1280, 720, 24, ASPECT(16, 9), PROGRESSIVE, false, - "60 720p24 16:9 1280x720p @ 23.98/24Hz"}, - {1280, 720, 25, ASPECT(16, 9), PROGRESSIVE, false, - "61 720p25 16:9 1280x720p @ 25Hz"}, - {1280, 720, 30, ASPECT(16, 9), PROGRESSIVE, false, - "62 720p30 16:9 1280x720p @ 29.97/30Hz"}, - {1920, 1080, 120, ASPECT(16, 9), PROGRESSIVE, false, - "63 1080p120 16:9 1920x1080 @ 119.88/120Hz"}, + {640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, + " 1 DMT0659 4:3 640x480p @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, + " 2 480p 4:3 720x480p @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, + " 3 480pH 16:9 720x480p @ 59.94/60Hz"}, + {1280, 720, 60, ASPECT(16, 9), PROGRESSIVE, false, + " 4 720p 16:9 1280x720p @ 59.94/60Hz"}, + {1920, 1080, 60, ASPECT(4, 3), INTERLACE, false, + " 5 1080i 16:9 1920x1080i @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(4, 3), INTERLACE, false, + " 6 480i 4:3 720(1440)x480i @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(16, 9), INTERLACE, false, + " 7 480iH 16:9 720(1440)x480i @ 59.94/60Hz"}, + {720, 240, 60, ASPECT(4, 3), PROGRESSIVE, false, + " 8 240p 4:3 720(1440)x240p @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, + " 9 240pH 16:9 720(1440)x240p @ 59.94/60Hz"}, + {2880, 480, 60, ASPECT(4, 3), INTERLACE, false, + "10 480i4x 4:3 (2880)x480i @ 59.94/60Hz"}, + {2880, 480, 60, ASPECT(16, 9), INTERLACE, false, + "11 480i4xH 16:9 (2880)x480i @ 59.94/60Hz"}, + {2880, 240, 60, ASPECT(4, 3), PROGRESSIVE, false, + "12 240p4x 4:3 (2880)x240p @ 59.94/60Hz"}, + {2880, 240, 60, ASPECT(16, 9), PROGRESSIVE, false, + "13 240p4xH 16:9 (2880)x240p @ 59.94/60Hz"}, + {1440, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, + "14 480p2x 4:3 1440x480p @ 59.94/60Hz"}, + {1440, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, + "15 480p2xH 16:9 1440x480p @ 59.94/60Hz"}, + {1920, 1080, 60, ASPECT(16, 9), PROGRESSIVE, false, + "16 1080p 16:9 1920x1080p @ 59.94/60Hz"}, + {720, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, + "17 576p 4:3 720x576p @ 50Hz"}, + {720, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, + "18 576pH 16:9 720x576p @ 50Hz"}, + {1280, 720, 50, ASPECT(16, 9), PROGRESSIVE, false, + "19 720p50 16:9 1280x720p @ 50Hz"}, + {1920, 1080, 50, ASPECT(16, 9), INTERLACE, false, + "20 1080i25 16:9 1920x1080i @ 50Hz*"}, + {1440, 576, 50, ASPECT(4, 3), INTERLACE, false, + "21 576i 4:3 720(1440)x576i @ 50Hz"}, + {1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, + "22 576iH 16:9 720(1440)x576i @ 50Hz"}, + {720, 288, 50, ASPECT(4, 3), PROGRESSIVE, false, + "23 288p 4:3 720(1440)x288p @ 50Hz"}, + {720, 288, 50, ASPECT(16, 9), PROGRESSIVE, false, + "24 288pH 16:9 720(1440)x288p @ 50Hz"}, + {2880, 576, 50, ASPECT(4, 3), INTERLACE, false, + "25 576i4x 4:3 (2880)x576i @ 50Hz"}, + {2880, 576, 50, ASPECT(16, 9), INTERLACE, false, + "26 576i4xH 16:9 (2880)x576i @ 50Hz"}, + {2880, 288, 50, ASPECT(4, 3), PROGRESSIVE, false, + "27 288p4x 4:3 (2880)x288p @ 50Hz"}, + {2880, 288, 50, ASPECT(16, 9), PROGRESSIVE, false, + "28 288p4xH 16:9 (2880)x288p @ 50Hz"}, + {1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, + "29 576p2x 4:3 1440x576p @ 50Hz"}, + {1440, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, + "30 576p2xH 16:9 1440x576p @ 50Hz"}, + {1920, 1080, 50, ASPECT(16, 9), PROGRESSIVE, false, + "31 1080p50 16:9 1920x1080p @ 50Hz"}, + {1920, 1080, 24, ASPECT(16, 9), PROGRESSIVE, false, + "32 1080p24 16:9 1920x1080p @ 23.98/24Hz"}, + {1920, 1080, 25, ASPECT(16, 9), PROGRESSIVE, false, + "33 1080p25 16:9 1920x1080p @ 25Hz"}, + {1920, 1080, 30, ASPECT(16, 9), PROGRESSIVE, false, + "34 1080p30 16:9 1920x1080p @ 29.97/30Hz"}, + {2880, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, + "35 480p4x 4:3 (2880)x480p @ 59.94/60Hz"}, + {2880, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, + "36 480p4xH 16:9 (2880)x480p @ 59.94/60Hz"}, + {2880, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, + "37 576p4x 4:3 (2880)x576p @ 50Hz"}, + {2880, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, + "38 576p4xH 16:9 (2880)x576p @ 50Hz"}, + {1920, 1080, 50, ASPECT(16, 9), INTERLACE, false, + "39 108Oi25 16:9 1920x1080i(1250 Total) @ 50Hz*"}, + {1920, 1080, 100, ASPECT(16, 9), INTERLACE, false, + "40 1080i50 16:9 1920x1080i @ 100Hz"}, + {1280, 720, 100, ASPECT(16, 9), PROGRESSIVE, false, + "41 720p100 16:9 1280x720p @ 100Hz"}, + {720, 576, 100, ASPECT(4, 3), PROGRESSIVE, false, + "42 576p100 4:3 720x576p @ 100Hz"}, + {720, 576, 100, ASPECT(16, 9), PROGRESSIVE, false, + "43 576p100H 16:9 720x576p @ 100Hz"}, + {720, 576, 100, ASPECT(4, 3), INTERLACE, false, + "44 576i50 4:3 720(1440)x576i @ 100Hz"}, + {720, 576, 100, ASPECT(16, 9), INTERLACE, false, + "45 576i50H 16:9 720(1440)x576i @ 100Hz"}, + {1920, 1080, 120, ASPECT(16, 9), INTERLACE, false, + "46 1080i60 16:9 1920x1080i @ 119.88/120Hz"}, + {1280, 720, 120, ASPECT(16, 9), PROGRESSIVE, false, + "47 720p120 16:9 1280x720p @ 119.88/120Hz"}, + {720, 480, 120, ASPECT(4, 3), PROGRESSIVE, false, + "48 480p119 4:3 720x480p @ 119.88/120Hz"}, + {720, 480, 120, ASPECT(16, 9), PROGRESSIVE, false, + "49 480p119H 16:9 720x480p @ 119.88/120Hz"}, + {720, 480, 120, ASPECT(4, 3), INTERLACE, false, + "50 480i59 4:3 720(1440)x480i @ 119.88/120Hz"}, + {720, 480, 120, ASPECT(16, 9), INTERLACE, false, + "51 480i59H 16:9 720(1440)x480i @ 119.88/120Hz"}, + {720, 576, 200, ASPECT(4, 3), PROGRESSIVE, false, + "52 576p200 4:3 720x576p @ 200Hz"}, + {720, 576, 200, ASPECT(16, 9), PROGRESSIVE, false, + "53 576p200H 16:9 720x576p @ 200Hz"}, + {720, 576, 200, ASPECT(4, 3), INTERLACE, false, + "54 576i100 4:3 720(1440)x576i @ 200Hz"}, + {720, 576, 200, ASPECT(16, 9), INTERLACE, false, + "55 576i100H 16:9 720(1440)x576i @ 200Hz"}, + {720, 480, 240, ASPECT(4, 3), PROGRESSIVE, false, + "56 480p239 4:3 720x480p @ 239.76/240Hz"}, + {720, 480, 240, ASPECT(16, 9), PROGRESSIVE, false, + "57 480p239H 16:9 720x480p @ 239.76/240Hz"}, + {720, 480, 240, ASPECT(4, 3), INTERLACE, false, + "58 480i119 4:3 720(1440)x480i @ 239.76/240Hz"}, + {720, 480, 240, ASPECT(16, 9), INTERLACE, false, + "59 480i119H 16:9 720(1440)x480i @ 239.76/240Hz"}, + {1280, 720, 24, ASPECT(16, 9), PROGRESSIVE, false, + "60 720p24 16:9 1280x720p @ 23.98/24Hz"}, + {1280, 720, 25, ASPECT(16, 9), PROGRESSIVE, false, + "61 720p25 16:9 1280x720p @ 25Hz"}, + {1280, 720, 30, ASPECT(16, 9), PROGRESSIVE, false, + "62 720p30 16:9 1280x720p @ 29.97/30Hz"}, + {1920, 1080, 120, ASPECT(16, 9), PROGRESSIVE, false, + "63 1080p120 16:9 1920x1080 @ 119.88/120Hz"}, }; /* device supported modes in CEA */ enum { - CEA_MODE_640X480P_60HZ_4_3 = 0, - CEA_MODE_720X480P_60HZ_4_3 = 1, - CEA_MODE_720X480P_60HZ_16_9 = 2, - CEA_MODE_1280X720P_60HZ_16_9 = 3, - CEA_MODE_720X576P_50HZ_4_3 = 16, - CEA_MODE_720X576P_50HZ_16_9 = 17, + CEA_MODE_640X480P_60HZ_4_3 = 0, + CEA_MODE_720X480P_60HZ_4_3 = 1, + CEA_MODE_720X480P_60HZ_16_9 = 2, + CEA_MODE_1280X720P_60HZ_16_9 = 3, + CEA_MODE_720X576P_50HZ_4_3 = 16, + CEA_MODE_720X576P_50HZ_16_9 = 17, }; /* device supported modes in established timing */ enum { - ESTABLISHED_MODE_800X600_60HZ = 0, - ESTABLISHED_MODE_640X480_60HZ = 5, + ESTABLISHED_MODE_800X600_60HZ = 0, + ESTABLISHED_MODE_640X480_60HZ = 5, }; int init_edid_info(struct edid_info_struct *edid_info) { - edid_info->is_valid = false; - mutex_init(&edid_info->access_lock); + edid_info->is_valid = false; + mutex_init(&edid_info->access_lock); - return 0; + return 0; } /* Byte 35-37 of block-0 */ @@ -355,71 +356,65 @@ int edid_dump_video_modes(u8 *edid_buf) bool edid_do_checksum(u8 *data) { - int i; - u8 sum = 0; + int i; + u8 sum = 0; - for (i = 0; i < EDID_BLOCK_SIZE; i++) - sum += data[i]; - EDID_DBG("%s: result=%s\n", __func__, sum ? "fail" : "pass"); - return sum ? false : true; + for (i = 0; i < EDID_BLOCK_SIZE; i++) + sum += data[i]; + EDID_DBG("%s: result=%s\n", __func__, sum ? "fail" : "pass"); + return sum ? false : true; } static bool edid_check_header(u8 *data) { - int ret = true; + int ret, i = 0; + /* EDID 8 bytes header */ + static u8 header[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}; - /* EDID 8 bytes header */ - static const u8 header[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}; + for (i = 0; i < ARRAY_SIZE(header); i++) + if (data[i] != header[i]) + break; + ret = (i == ARRAY_SIZE(header)) ? true : false; + EDID_DBG("%s: result=%s\n", __func__, ret ? "pass" : "fail"); - if (memcmp(data, header, 8) != 0) - ret = false; - - EDID_DBG("%s: result=%s\n", __func__, ret ? "pass" : "fail"); - if (!edid_do_checksum(data)) - { - pr_err("%s: checksum error\n", __func__); - - // Not all monitors have a proper checksum, so we'll ignore this error and just log the problem. - // ret = false; - } - return ret; + return ret; } bool edid_check_hdmi_sink(struct hdmi_info *hdmi, int block) { - int ret = false, index = 4; - u8 *data = &hdmi->edid_buf[block * 128]; - /* block offset where long descriptors start */ - int long_desc_offset = data[LONG_DESCR_PTR_IDX]; - int tag_code, data_block_len; - - while (index < long_desc_offset) { - tag_code = (data[index] >> 5) & 0x7; - data_block_len = data[index++] & 0x1f; - if (tag_code == VENDOR_SPEC_D_BLOCK && - data[index] == 0x03 && - data[index + 1] == 0x0c && - data[index + 2] == 0x00) { - ret = true; - break; - } else - index += data_block_len; - } - hdmi->edid_info.hdmi_sink = ret; - EDID_DBG("%s: ret=%s\n", __func__, ret ? "yes" : "no"); - return ret; + int ret = false, index = 4; + u8 *data = &hdmi->edid_buf[block * 128]; + /* block offset where long descriptors start */ + int long_desc_offset = data[LONG_DESCR_PTR_IDX]; + int tag_code, data_block_len; + + while (index < long_desc_offset) { + tag_code = (data[index] >> 5) & 0x7; + data_block_len = data[index++] & 0x1f; + if (tag_code == VENDOR_SPEC_D_BLOCK && + data[index] == 0x03 && + data[index + 1] == 0x0c && + data[index + 2] == 0x00) { + ret = true; + break; + } else + index += data_block_len; + } + hdmi->edid_info.hdmi_sink = ret; + EDID_DBG("%s: ret=%s\n", __func__, ret ? "yes" : "no"); + return ret; } struct edid_black_list_info { - u8 mfr_model[4]; - u8 prefer_modes[3]; + u8 mfr_model[4]; + u8 prefer_modes[3]; }; struct edid_black_list_info edid_black_list[] = { - { {0x4c, 0x2d, 0xa5, 0x02}, {0, 0, 0x40} }, // 720p only - { {0x4c, 0x2d, 0x0d, 0x05}, {0, 0, 0x40} }, // 720p only + { {0x4c, 0x2d, 0xa5, 0x02}, {0, 0, 0x40} }, // 720p only + { {0x4c, 0x2d, 0x0d, 0x05}, {0, 0, 0x40} }, // 720p only - //{ {0x5a, 0x63, 0x20, 0x2b}, {0, 0, 0x40} }, // Viewsonic test + //{ {0x5a, 0x63, 0x20, 0x2b}, {0, 0, 0x40} }, // Viewsonic test }; /* By comparing the Manufacture(0x8, 0x9) and Model field(0xa, 0xb) of EDID, @@ -427,241 +422,208 @@ struct edid_black_list_info edid_black_list[] = { */ int edid_fixup_compatibility_list(struct hdmi_info *hdmi) { - int i, ret = -1; + int i, ret = -1; - /* FIXME: magic numbers...*/ - for (i = 0; i < ARRAY_SIZE(edid_black_list); i++) { - if (!memcmp(hdmi->edid_buf + 8, edid_black_list[i].mfr_model, 4)){ + /* FIXME: magic numbers...*/ + for (i = 0; i < ARRAY_SIZE(edid_black_list); i++) { + if (!memcmp(hdmi->edid_buf + 8, edid_black_list[i].mfr_model, 4)){ #if 0 - EDID_DBG("%s: found in blacklist %d\n", __func__, i); - EDID_DBG("%s: old timing = {%02x, %02x, %02x}\n", - __func__, - hdmi->edid_buf[35], hdmi->edid_buf[36], - hdmi->edid_buf[37]); - memcpy(hdmi->edid_buf + 35, - edid_black_list[i].prefer_modes, 3); - EDID_DBG("%s: new timing = {%02x, %02x, %02x}\n", - __func__, - hdmi->edid_buf[35], hdmi->edid_buf[36], - hdmi->edid_buf[37]); + EDID_DBG("%s: found in blacklist %d\n", __func__, i); + EDID_DBG("%s: old timing = {%02x, %02x, %02x}\n", + __func__, + hdmi->edid_buf[35], hdmi->edid_buf[36], + hdmi->edid_buf[37]); + memcpy(hdmi->edid_buf + 35, + edid_black_list[i].prefer_modes, 3); + EDID_DBG("%s: new timing = {%02x, %02x, %02x}\n", + __func__, + hdmi->edid_buf[35], hdmi->edid_buf[36], + hdmi->edid_buf[37]); #else - EDID_DBG("%s: found in compatibility %d\n", __func__, i); - memcpy(hdmi->edid_buf + 35, - edid_black_list[i].prefer_modes, 3); + EDID_DBG("%s: found in compatibility %d\n", __func__, i); + memcpy(hdmi->edid_buf + 35, + edid_black_list[i].prefer_modes, 3); #endif - ret = i; - break; - } - } + ret = i; + break; + } + } - return ret; + return ret; } u8 edid_simple_parsing(struct hdmi_info *hdmi) { - u8 *edid_buf = hdmi->edid_buf; - int i, index, ret = -EINVAL; - struct edid_info_struct *edid_info = &hdmi->edid_info; - unsigned v1, width, height, aspect; - unsigned extensions; - unsigned extension; - - EDID_DBG("%s\n", __func__); - if (!edid_check_header(edid_buf)) - { - pr_err("%s: incorrect header\n", __func__); - return INCORRECT_EDID_HEADER; - } - - // Retrieve the number of extensions in this EDID - extensions = edid_buf[126]; - EDID_DBG("%s: extensions=%d\n", __func__, extensions); - if (!extensions) - { - hdmi->edid_info.hdmi_sink = false; - return NO_861_EXTENSIONS; - } - - /* reset all supported */ - for (i = 0 ; i < ARRAY_SIZE(additional_timing_db); i++) - additional_timing_db[i].supported = false; - for (i = 0 ; i < ARRAY_SIZE(established_timing_db); i++) - established_timing_db[i].supported = false; - - /* Block 0: established timing */ - pr_info("established timing: {%02x, %02x, %02x}\n", - edid_buf[35], edid_buf[36], edid_buf[37]); - - v1 = edid_buf[35] | edid_buf[36] << 8; - if (edid_buf[37] & 0x80) - v1 |= 0x00010000; - - for (i = 0 ; i < 17; i++ ) // 17 bits defined in established timing - established_timing_db[i].supported = ((v1 >>= 1) & 1) ; - - /* standard timing identification */ - for (i = 0; i < 8; i++) { - width = (edid_buf[38 + (i * 2)] * 8) + 248; - v1 = edid_buf[38 + (i * 2) + 1]; - switch (v1 >> 6) { - case 0: height = width * 10 / 16; aspect = ASPECT(16, 10); break; - case 1: height = width * 3 / 4; aspect = ASPECT(4, 3); break; - case 2: height = width * 4 / 5; aspect = ASPECT(5, 4); break; - case 3: height = width * 9 / 16; aspect = ASPECT(16, 9); break; - } - standard_timing_db[i].width = width; - standard_timing_db[i].height = height; - standard_timing_db[i].aspect = aspect; - standard_timing_db[i].refresh_rate = (v1 & 0x3F) + 60; - standard_timing_db[i].supported = true; - } - - for (extension = 0; extension < extensions; extension++) - { - unsigned baseOffset = (extension + 1) * 0x80; - unsigned dtdStart = 0; - unsigned nativeFormats = 0; - unsigned dbcOffset; - - EDID_DBG("Extension %d\n", extension +1); - if (edid_buf[baseOffset] != 0x02) - { - EDID_DBG(" Extension is not CEA EDID format. Skipping.\n"); - continue; - } - - if (edid_buf[baseOffset+1] < 0x03) - { - EDID_DBG(" CEA EDID Extension is below version 3. Skipping.\n"); - continue; - } - - dtdStart = edid_buf[baseOffset+2]; - if (dtdStart != 0) dtdStart += baseOffset; - - edid_info->under_scan = edid_buf[baseOffset+3] & EDID_BIT(7); - edid_info->basic_audio = edid_buf[baseOffset+3] & EDID_BIT(6); - edid_info->ycbcr_4_4_4 = edid_buf[baseOffset+3] & EDID_BIT(5); - edid_info->ycbcr_4_2_2 = edid_buf[baseOffset+3] & EDID_BIT(4); + u8 *edid_buf = hdmi->edid_buf; + int i, index, ret = -EINVAL; + struct edid_info_struct *edid_info = &hdmi->edid_info; + unsigned v1, width, height, aspect; + unsigned extensions; + + EDID_DBG("%s\n", __func__); + // FIXME: integrate with edid_check() + if (!edid_do_checksum(edid_buf)) { + pr_err("%s: checksum error\n", __func__); + //return EDID_CHECKSUM_ERROR; + } + if (!edid_check_header(edid_buf)) { + pr_err("%s: incorrect header\n", __func__); + return INCORRECT_EDID_HEADER; + } + edid_info->under_scan = ((edid_buf[MISC_SUPPORT_IDX]) >> 7) & 0x1; + edid_info->basic_audio = ((edid_buf[MISC_SUPPORT_IDX]) >> 6) & 0x1; + edid_info->ycbcr_4_4_4 = ((edid_buf[MISC_SUPPORT_IDX]) >> 5) & 0x1; + edid_info->ycbcr_4_2_2 = ((edid_buf[MISC_SUPPORT_IDX]) >> 4) & 0x1; + + // FIXME: 0x7e + extensions = edid_buf[0x7e]; + EDID_DBG("%s: extensions=%d\n", __func__, extensions); + if (!extensions) { + hdmi->edid_info.hdmi_sink = false; + return NO_861_EXTENSIONS; + } + //return; + + /* reset all supported */ + for (i = 0 ; i < ARRAY_SIZE(additional_timing_db); i++) + additional_timing_db[i].supported = false; + for (i = 0 ; i < ARRAY_SIZE(established_timing_db); i++) + established_timing_db[i].supported = false; + + /* Block 0: established timing */ + pr_info("established timing: {%02x, %02x, %02x}\n", + edid_buf[35], edid_buf[36], edid_buf[37]); + + v1 = edid_buf[35] | edid_buf[36] << 8 | (!!edid_buf[37]) << 16; + + for (i = 0 ; i < 17; i++ ) // 17 bits defined in established timing + established_timing_db[i].supported = ((v1 >>= 1) & 1) ; - nativeFormats = edid_buf[baseOffset+3] & 0x04; - dbcOffset = 4; - - for (i = 0; i < (edid_buf[baseOffset+3] & 0x0f); i++) // Change by Ian: only bits 3-0 show length of blocks - { - unsigned blockType = edid_buf[baseOffset + dbcOffset] >> 5; - unsigned blockLen = edid_buf[baseOffset + dbcOffset] & 0x1f; - unsigned byte; - - // Skip any block which isn't video - if (blockType == 3) - { - // We may have found the HDMI sink - if (edid_buf[baseOffset + dbcOffset + 1] == 0x00 && - edid_buf[baseOffset + dbcOffset + 2] == 0x0C && - edid_buf[baseOffset + dbcOffset + 3] == 0x03 ) - { - // We found the HDMI block - edid_check_hdmi_sink(hdmi, i + 1); - } - continue; - } - if (blockType != 2) - { - dbcOffset += blockLen; - continue; - } - - // The block will be an array of indexes - for (byte = 1; byte < blockLen; byte++) - { - index = edid_buf[baseOffset + dbcOffset] & 0x7f; - - if (index > 63) - { - EDID_DBG("Invalid index in EDID Video block. Ignoring.\n"); - } - else - { - additional_timing_db[index-1].supported = true; - EDID_DBG("%s\n", additional_timing_db[index-1].descrption); - } - } +#if 0 + /* standard timing identification */ + for (i = 0; i < 8; i++) { + width = edid_buf[38 + i * 2] * 8 + 248; + v1 = edid_buf[38 + i * 2 + 1]; + switch (v1 >> 6) { + case 0: height = width * 10 / 16; aspect = ASPECT(16, 10); break; + case 1: height = width * 3 / 4; aspect = ASPECT(4, 3); break; + case 2: height = width * 4 / 5; aspect = ASPECT(5, 4); break; + case 3: height = width * 9 / 16; aspect = ASPECT(16, 9); break; + } + standard_timing_db[i].width = width; + standard_timing_db[i].height = height; + standard_timing_db[i].aspect = aspect; + standard_timing_db[i].refresh_rate = (v1 & ~(3 << 6)) + 60; + standard_timing_db[i].supported = true; + } +#endif - dbcOffset += blockLen; - } - } - - // As a cheat, we're replacing the existing timings with our own "custom" - // definition of these bytes. - edid_buf[35] = 0; - edid_buf[36] = 0; - edid_buf[37] = 0; - - /* edid_buf[37] bit4: 480p, bit5: 576p, bit6: 720p */ - if (additional_timing_db[CEA_MODE_720X480P_60HZ_4_3].supported || - additional_timing_db[CEA_MODE_720X480P_60HZ_16_9].supported) { - EDID_DBG("decide to support 480P\n"); - edid_buf[37] |= (1<<4); - } - - if (additional_timing_db[CEA_MODE_720X576P_50HZ_4_3].supported || - additional_timing_db[CEA_MODE_720X576P_50HZ_16_9].supported) { - EDID_DBG("decide to support 576P\n"); - edid_buf[37] |= (1<<5); - } - - if (additional_timing_db[CEA_MODE_1280X720P_60HZ_16_9].supported) { - EDID_DBG("decide to support 720P\n"); - edid_buf[37] |= (1<<6); - } - - if (established_timing_db[ESTABLISHED_MODE_800X600_60HZ].supported) { - EDID_DBG("decide to support 800x600\n"); - edid_buf[36] |= (1<<6); - } - - if (established_timing_db[ESTABLISHED_MODE_640X480_60HZ].supported) { - EDID_DBG("decide to support 640x480\n"); - edid_buf[35] |= (1<<5); - } - edid_fixup_compatibility_list(hdmi); - - return ret; + if (extensions == 1) { + EDID_DBG("Block-1: additional timing\n"); + /* Block-1: additional timing */ + /* + edid_buf[127] is last byte of standard edid data + edid_buf[128 + 00] should be byte 00 of eedid + edid_buf[128 + 01] byte 01 rev number + edid_buf[128 + 02] byte 02 nonstandard DTD locations, not being parsed yet + edid_buf[128 + 03] byte 03 version info and number of blocks present + edid_buf[128 + 04] byte 04 start of standard DTD block collection + */ + for( i = 0; i < (edid_buf[128 + 3] & 0x0f); i++) { // Change by Ian: only bits 3-0 show length of blocks + // Check if byte type is Video (2) + if ((edid_buf[128 + 4 + i] & 0xe0) == 2){ + // Byte block IS video + index = edid_buf[128 + 4 + i] & 0x7f; + additional_timing_db[index-1].supported = true; + EDID_DBG("%s\n", additional_timing_db[index-1].descrption); + } else { + // Byte is NOT video + int blockLen = (edid_buf[128 + 4 + i] & 0x1f); + i = i + (blockLen - 1); // -1 since loop has i++ alread + } + + } + edid_check_hdmi_sink(hdmi, 1); + } else { + // Probably also contaions errors corrected above but probably not used + EDID_DBG("Block-2: additional timing\n"); + for( i = 0; i < (edid_buf[384 + 4] & 0x1f); i++) { + index = edid_buf[384 + 5 + i] & 0x7f; + additional_timing_db[index-1].supported = true; + EDID_DBG("%s\n", additional_timing_db[index-1].descrption); + } + edid_check_hdmi_sink(hdmi, 3); + } + + // Why are these overwritten? + edid_buf[35] = 0; + edid_buf[36] = 0; + edid_buf[37] = 0; + + /* edid_buf[37] bit4: 480p, bit5: 576p, bit6: 720p */ + if (additional_timing_db[CEA_MODE_720X480P_60HZ_4_3].supported || + additional_timing_db[CEA_MODE_720X480P_60HZ_16_9].supported) { + EDID_DBG("decide to support 480P\n"); + edid_buf[37] |= (1<<4); + } + + if (additional_timing_db[CEA_MODE_720X576P_50HZ_4_3].supported || + additional_timing_db[CEA_MODE_720X576P_50HZ_16_9].supported) { + EDID_DBG("decide to support 576P\n"); + edid_buf[37] |= (1<<5); + } + + if (additional_timing_db[CEA_MODE_1280X720P_60HZ_16_9].supported) { + EDID_DBG("decide to support 720P\n"); + edid_buf[37] |= (1<<6); + } + + if (established_timing_db[ESTABLISHED_MODE_800X600_60HZ].supported) { + EDID_DBG("decide to support 800x600\n"); + edid_buf[36] |= (1<<6); + } + + if (established_timing_db[ESTABLISHED_MODE_640X480_60HZ].supported) { + EDID_DBG("decide to support 640x480\n"); + edid_buf[35] |= (1<<5); + } + edid_fixup_compatibility_list(hdmi); + + return ret; } // FIXME: modify the checking routines into inline function. bool edid_is_video_mode_supported(struct video_mode *vmode) { - int i; - struct video_mode *vmode_db; - - vmode_db = established_timing_db; - for (i = 0, vmode_db = established_timing_db; - i < ARRAY_SIZE(established_timing_db); i++) - if ( (vmode->width == vmode_db[i].width) && - (vmode->height == vmode_db[i].height) && - (vmode->refresh_rate == vmode_db[i].refresh_rate ) && - (vmode_db[i].interlaced == PROGRESSIVE) && - (vmode_db[i].supported == true )) - return true; - for (i = 0, vmode_db = standard_timing_db; - i < ARRAY_SIZE(standard_timing_db); i++) - if ( (vmode->width == vmode_db[i].width) && - (vmode->height == vmode_db[i].height) && - (vmode->refresh_rate == vmode_db[i].refresh_rate ) && - (vmode_db[i].interlaced == PROGRESSIVE) && - (vmode_db[i].supported == true )) - return true; + int i; + struct video_mode *vmode_db; + + vmode_db = established_timing_db; + for (i = 0, vmode_db = established_timing_db; + i < ARRAY_SIZE(established_timing_db); i++) + if ( (vmode->width == vmode_db[i].width) && + (vmode->height == vmode_db[i].height) && + (vmode->refresh_rate == vmode_db[i].refresh_rate ) && + (vmode_db[i].interlaced == PROGRESSIVE) && + (vmode_db[i].supported == true )) + return true; + for (i = 0, vmode_db = standard_timing_db; + i < ARRAY_SIZE(standard_timing_db); i++) + if ( (vmode->width == vmode_db[i].width) && + (vmode->height == vmode_db[i].height) && + (vmode->refresh_rate == vmode_db[i].refresh_rate ) && + (vmode_db[i].interlaced == PROGRESSIVE) && + (vmode_db[i].supported == true )) + return true; for (i = 0, vmode_db = additional_timing_db; i < ARRAY_SIZE(additional_timing_db); i++) if ( (vmode->width == vmode_db[i].width) && (vmode->height == vmode_db[i].height) && (vmode->refresh_rate == vmode_db[i].refresh_rate ) && (vmode_db[i].interlaced == PROGRESSIVE) && - (vmode_db[i].supported == true )) + (vmode_db[i].supported == true )) return true; - return false; + return false; } bool edid_check_sink_type(struct hdmi_info *hdmi) @@ -690,10 +652,10 @@ int edid_dump_hex(u8 *src, int src_size, char *output, int output_size) } line[line_size - 1] = '\n'; strncpy(output+ n, line, line_size); - if ((n + line_size) > output_size) - break; - else - n += line_size; + if ((n + line_size) > output_size) + break; + else + n += line_size; } return n; @@ -707,16 +669,22 @@ static ssize_t edid_dbg_open(struct inode *inode, struct file *file) } static char hex_buff[2048]; - static ssize_t edid_buffered_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { - int n; - struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; - - edid_simple_parsing(hdmi); - n = edid_dump_hex(hdmi->edid_buf, (hdmi->edid_buf[0x7e] + 1) * 128, - hex_buff, 2048); + int n; + int extensions; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + extensions = hdmi->edid_buf[0x7e] + 1; + edid_dump_video_modes(hdmi->edid_buf);// fixme: crashed + if (extensions == 1) + edid_dump_video_modes(hdmi->edid_buf + 128); + else + edid_dump_video_modes(hdmi->edid_buf + 384); + //edid_simple_parsing(hdmi); // FIXME: crashed... + n = edid_dump_hex(hdmi->edid_buf, (hdmi->edid_buf[0x7e] + 1) * 128, + hex_buff, 2048); return simple_read_from_buffer(buf, count, ppos, hex_buff, n); } @@ -732,7 +700,7 @@ static struct file_operations edid_debugfs_fops[] = { int edid_debugfs_init(struct hdmi_info *hdmi) { //int ret; - struct dentry *edid_dent; + struct dentry *edid_dent; edid_dent = debugfs_create_dir("edid", hdmi->debug_dir); if (IS_ERR(edid_dent)) From 36508a164c0ed8c9bf738ef221d6cb61b2ff5201 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sun, 5 Jun 2011 01:07:23 -0400 Subject: [PATCH 1941/2556] Revert "comment changes" This reverts commit c6a563e5ab6c7de8097d39de04681dca5b9bda3b. --- .gitignore | 1 - drivers/video/msm/hdmi/edid.c | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 40429c84e9e80..5d56a3fd0de6b 100644 --- a/.gitignore +++ b/.gitignore @@ -32,7 +32,6 @@ modules.builtin *.lzo *.patch *.gcno -vmlinux # # Top-level generic files diff --git a/drivers/video/msm/hdmi/edid.c b/drivers/video/msm/hdmi/edid.c index 7a38db2235337..93f4cc3431324 100644 --- a/drivers/video/msm/hdmi/edid.c +++ b/drivers/video/msm/hdmi/edid.c @@ -521,8 +521,7 @@ u8 edid_simple_parsing(struct hdmi_info *hdmi) if (extensions == 1) { EDID_DBG("Block-1: additional timing\n"); /* Block-1: additional timing */ - /* - edid_buf[127] is last byte of standard edid data + /* edid_buf[127] is last byte of standard edid data edid_buf[128 + 00] should be byte 00 of eedid edid_buf[128 + 01] byte 01 rev number edid_buf[128 + 02] byte 02 nonstandard DTD locations, not being parsed yet @@ -532,8 +531,7 @@ u8 edid_simple_parsing(struct hdmi_info *hdmi) for( i = 0; i < (edid_buf[128 + 3] & 0x0f); i++) { // Change by Ian: only bits 3-0 show length of blocks // Check if byte type is Video (2) if ((edid_buf[128 + 4 + i] & 0xe0) == 2){ - // Byte block IS video - index = edid_buf[128 + 4 + i] & 0x7f; + index = edid_buf[128 + 4 + i] & 0x7f; additional_timing_db[index-1].supported = true; EDID_DBG("%s\n", additional_timing_db[index-1].descrption); } else { @@ -545,7 +543,7 @@ u8 edid_simple_parsing(struct hdmi_info *hdmi) } edid_check_hdmi_sink(hdmi, 1); } else { - // Probably also contaions errors corrected above but probably not used + // Probably also contaions errors corrected above EDID_DBG("Block-2: additional timing\n"); for( i = 0; i < (edid_buf[384 + 4] & 0x1f); i++) { index = edid_buf[384 + 5 + i] & 0x7f; From 57fbfd726f92f4b5f146e5306833851e405848ad Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sun, 5 Jun 2011 01:07:35 -0400 Subject: [PATCH 1942/2556] Revert "Initial attempt at fixing indexes as well as parsing of video blocks and skipping audio blocks" This reverts commit c5d57aef46f4af92838df632b27e387a4d21dc1b. --- drivers/video/msm/hdmi/edid.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/drivers/video/msm/hdmi/edid.c b/drivers/video/msm/hdmi/edid.c index 93f4cc3431324..101afa1d95eaa 100644 --- a/drivers/video/msm/hdmi/edid.c +++ b/drivers/video/msm/hdmi/edid.c @@ -521,29 +521,13 @@ u8 edid_simple_parsing(struct hdmi_info *hdmi) if (extensions == 1) { EDID_DBG("Block-1: additional timing\n"); /* Block-1: additional timing */ - /* edid_buf[127] is last byte of standard edid data - edid_buf[128 + 00] should be byte 00 of eedid - edid_buf[128 + 01] byte 01 rev number - edid_buf[128 + 02] byte 02 nonstandard DTD locations, not being parsed yet - edid_buf[128 + 03] byte 03 version info and number of blocks present - edid_buf[128 + 04] byte 04 start of standard DTD block collection - */ - for( i = 0; i < (edid_buf[128 + 3] & 0x0f); i++) { // Change by Ian: only bits 3-0 show length of blocks - // Check if byte type is Video (2) - if ((edid_buf[128 + 4 + i] & 0xe0) == 2){ - index = edid_buf[128 + 4 + i] & 0x7f; - additional_timing_db[index-1].supported = true; - EDID_DBG("%s\n", additional_timing_db[index-1].descrption); - } else { - // Byte is NOT video - int blockLen = (edid_buf[128 + 4 + i] & 0x1f); - i = i + (blockLen - 1); // -1 since loop has i++ alread - } - + for( i = 0; i < (edid_buf[128 + 4] & 0x1f); i++) { + index = edid_buf[128 + 5 + i] & 0x7f; + additional_timing_db[index-1].supported = true; + EDID_DBG("%s\n", additional_timing_db[index-1].descrption); } edid_check_hdmi_sink(hdmi, 1); } else { - // Probably also contaions errors corrected above EDID_DBG("Block-2: additional timing\n"); for( i = 0; i < (edid_buf[384 + 4] & 0x1f); i++) { index = edid_buf[384 + 5 + i] & 0x7f; @@ -553,7 +537,6 @@ u8 edid_simple_parsing(struct hdmi_info *hdmi) edid_check_hdmi_sink(hdmi, 3); } - // Why are these overwritten? edid_buf[35] = 0; edid_buf[36] = 0; edid_buf[37] = 0; From 5647f0355d0fa7e66b7a8f1de952f2dd81e4dd35 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sun, 5 Jun 2011 01:32:33 -0400 Subject: [PATCH 1943/2556] [drivers] input: misc: fix light sensor for bravos --- drivers/input/misc/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 5815c2a27159d..b659acac45e27 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -51,5 +51,5 @@ obj-$(CONFIG_INPUT_YEALINK) += yealink.o ifdef CONFIG_MICROP_COMMON obj-$(CONFIG_LIGHTSENSOR_MICROP) += cm3602_lightsensor_microp_htc.o else - obj-$(CONFIG_INPUT_CAPELLA_CM3602) += capella_cm3602.o + obj-$(CONFIG_LIGHTSENSOR_MICROP) += cm3602_lightsensor_microp.o endif From 6c766efff3d0ea68386981dd4e04219cbcce85e2 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sun, 5 Jun 2011 01:42:36 -0400 Subject: [PATCH 1944/2556] [msm] tweak audio gain --- arch/arm/mach-msm/board-bravo-audio.c | 8 ++++---- arch/arm/mach-msm/board-incrediblec-audio.c | 20 +++++++++---------- arch/arm/mach-msm/board-mahimahi-audio.c | 22 ++++++++++----------- arch/arm/mach-msm/board-supersonic-audio.c | 18 ++++++++--------- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/arch/arm/mach-msm/board-bravo-audio.c b/arch/arm/mach-msm/board-bravo-audio.c index 716c38a37f626..d9ee10da2d6cf 100644 --- a/arch/arm/mach-msm/board-bravo-audio.c +++ b/arch/arm/mach-msm/board-bravo-audio.c @@ -35,12 +35,12 @@ static struct mutex bt_sco_lock; static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { [Q6_HW_HANDSET] = { - .min_gain = -1600, - .max_gain = 400, + .min_gain = -1500, + .max_gain = 1199, }, [Q6_HW_HEADSET] = { - .min_gain = -1600, - .max_gain = 400, + .min_gain = -2000, + .max_gain = 1199, }, [Q6_HW_SPEAKER] = { .min_gain = -1100, diff --git a/arch/arm/mach-msm/board-incrediblec-audio.c b/arch/arm/mach-msm/board-incrediblec-audio.c index 9ae8729a6669b..0550afb75431d 100644 --- a/arch/arm/mach-msm/board-incrediblec-audio.c +++ b/arch/arm/mach-msm/board-incrediblec-audio.c @@ -38,28 +38,28 @@ static int headset_status = 0; static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { [Q6_HW_HANDSET] = { - .min_gain = -1600, - .max_gain = 400, + .min_gain = -1500, + .max_gain = 1199, }, [Q6_HW_HEADSET] = { .min_gain = -2000, - .max_gain = 0, + .max_gain = 1199, }, [Q6_HW_SPEAKER] = { - .min_gain = -1500, + .min_gain = -1100, .max_gain = 400, }, [Q6_HW_TTY] = { - .min_gain = -2000, - .max_gain = 0, + .min_gain = -1600, + .max_gain = 400, }, [Q6_HW_BT_SCO] = { - .min_gain = -2000, - .max_gain = 0, + .min_gain = -1600, + .max_gain = 400, }, [Q6_HW_BT_A2DP] = { - .min_gain = -2000, - .max_gain = 0, + .min_gain = -1600, + .max_gain = 400, }, }; diff --git a/arch/arm/mach-msm/board-mahimahi-audio.c b/arch/arm/mach-msm/board-mahimahi-audio.c index 6cb01b11edd48..94d70f507bc70 100644 --- a/arch/arm/mach-msm/board-mahimahi-audio.c +++ b/arch/arm/mach-msm/board-mahimahi-audio.c @@ -36,27 +36,27 @@ static struct mutex bt_sco_lock; static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { [Q6_HW_HANDSET] = { .min_gain = -1500, - .max_gain = 1100, + .max_gain = 1199, }, [Q6_HW_HEADSET] = { - .min_gain = -1700, - .max_gain = 900, + .min_gain = -2000, + .max_gain = 1199, }, [Q6_HW_SPEAKER] = { - .min_gain = -2000, - .max_gain = 600, + .min_gain = -1100, + .max_gain = 400, }, [Q6_HW_TTY] = { - .min_gain = -1500, - .max_gain = 1100, + .min_gain = -1600, + .max_gain = 400, }, [Q6_HW_BT_SCO] = { - .min_gain = -2000, - .max_gain = 600, + .min_gain = -1600, + .max_gain = 400, }, [Q6_HW_BT_A2DP] = { - .min_gain = -2000, - .max_gain = 600, + .min_gain = -1600, + .max_gain = 400, }, }; diff --git a/arch/arm/mach-msm/board-supersonic-audio.c b/arch/arm/mach-msm/board-supersonic-audio.c index eb78de46846ad..d986b6f509192 100644 --- a/arch/arm/mach-msm/board-supersonic-audio.c +++ b/arch/arm/mach-msm/board-supersonic-audio.c @@ -40,24 +40,24 @@ static int headset_status = 0; static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { [Q6_HW_HANDSET] = { - .min_gain = -2000, - .max_gain = 0, + .min_gain = -1500, + .max_gain = 1199, }, [Q6_HW_HEADSET] = { .min_gain = -2000, - .max_gain = 0, + .max_gain = 1199, }, [Q6_HW_SPEAKER] = { - .min_gain = -1500, - .max_gain = 0, + .min_gain = -1100, + .max_gain = 400, }, [Q6_HW_TTY] = { - .min_gain = -2000, - .max_gain = 0, + .min_gain = -1600, + .max_gain = 400, }, [Q6_HW_BT_SCO] = { - .min_gain = -2000, - .max_gain = 0, + .min_gain = -1600, + .max_gain = 400, }, [Q6_HW_BT_A2DP] = { .min_gain = -2000, From 5832fa79446027d1218c77eae54172ab516ef0ee Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Fri, 28 Jan 2011 13:13:47 +0800 Subject: [PATCH 1945/2556] ARM: mm: cache-l2x0: Correct l2x0 initialization This corrects the regression in the msm-2.6.35 version of this file. The initialization routine was not setting L2X0_AUX_CTRL and missing an INV_WAY flush as specified in the tech documents. --- arch/arm/mm/cache-l2x0.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index f2ce38e085d21..770f612e3afbc 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -302,8 +302,9 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask) type = "L2x0 series"; break; } - + writel(aux, l2x0_base + L2X0_AUX_CTRL); l2x0_way_mask = (1 << ways) - 1; + l2x0_inv_all(); /* * L2 cache Size = Way size * Number of ways From eb317b481ecd02d0569c4ece3169ab805d476fa6 Mon Sep 17 00:00:00 2001 From: Stepan Moskovchenko Date: Fri, 25 Feb 2011 18:52:22 -0800 Subject: [PATCH 1946/2556] msm: iommu: Don't read from write-only registers Don't read from V2Pxx command registers when doing iova-to-phys operations. These registers are write-only and reading the value before modifying the VA bits is unnecessary. Change-Id: I9a92e6300bf369d9422eef0fec7b066a4221f244 Signed-off-by: Stepan Moskovchenko --- .../arm/mach-msm/include/mach/iommu_hw-8xxx.h | 30 +------------------ arch/arm/mach-msm/iommu.c | 2 +- 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/iommu_hw-8xxx.h b/arch/arm/mach-msm/include/mach/iommu_hw-8xxx.h index c2c3da9444f48..d2463bf78fea9 100644 --- a/arch/arm/mach-msm/include/mach/iommu_hw-8xxx.h +++ b/arch/arm/mach-msm/include/mach/iommu_hw-8xxx.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -623,20 +623,6 @@ do { \ #define SET_INDEX(b, c, v) SET_CONTEXT_FIELD(b, c, V2PSR, INDEX, v) -/* V2Pxx UW UR PW PR */ -#define SET_V2PUW_INDEX(b, c, v) SET_CONTEXT_FIELD(b, c, V2PUW, V2Pxx_INDEX, v) -#define SET_V2PUW_VA(b, c, v) SET_CONTEXT_FIELD(b, c, V2PUW, V2Pxx_VA, v) - -#define SET_V2PUR_INDEX(b, c, v) SET_CONTEXT_FIELD(b, c, V2PUR, V2Pxx_INDEX, v) -#define SET_V2PUR_VA(b, c, v) SET_CONTEXT_FIELD(b, c, V2PUR, V2Pxx_VA, v) - -#define SET_V2PPW_INDEX(b, c, v) SET_CONTEXT_FIELD(b, c, V2PPW, V2Pxx_INDEX, v) -#define SET_V2PPW_VA(b, c, v) SET_CONTEXT_FIELD(b, c, V2PPW, V2Pxx_VA, v) - -#define SET_V2PPR_INDEX(b, c, v) SET_CONTEXT_FIELD(b, c, V2PPR, V2Pxx_INDEX, v) -#define SET_V2PPR_VA(b, c, v) SET_CONTEXT_FIELD(b, c, V2PPR, V2Pxx_VA, v) - - /* Context Register getters */ /* ACTLR */ #define GET_CFERE(b, c) GET_CONTEXT_FIELD(b, c, ACTLR, CFERE) @@ -824,20 +810,6 @@ do { \ #define GET_INDEX(b, c) GET_CONTEXT_FIELD(b, c, V2PSR, INDEX) -/* V2Pxx UW UR PW PR */ -#define GET_V2PUW_INDEX(b, c) GET_CONTEXT_FIELD(b, c, V2PUW, V2Pxx_INDEX) -#define GET_V2PUW_VA(b, c) GET_CONTEXT_FIELD(b, c, V2PUW, V2Pxx_VA) - -#define GET_V2PUR_INDEX(b, c) GET_CONTEXT_FIELD(b, c, V2PUR, V2Pxx_INDEX) -#define GET_V2PUR_VA(b, c) GET_CONTEXT_FIELD(b, c, V2PUR, V2Pxx_VA) - -#define GET_V2PPW_INDEX(b, c) GET_CONTEXT_FIELD(b, c, V2PPW, V2Pxx_INDEX) -#define GET_V2PPW_VA(b, c) GET_CONTEXT_FIELD(b, c, V2PPW, V2Pxx_VA) - -#define GET_V2PPR_INDEX(b, c) GET_CONTEXT_FIELD(b, c, V2PPR, V2Pxx_INDEX) -#define GET_V2PPR_VA(b, c) GET_CONTEXT_FIELD(b, c, V2PPR, V2Pxx_VA) - - /* Global Registers */ #define M2VCBR_N (0xFF000) #define CBACR_N (0xFF800) diff --git a/arch/arm/mach-msm/iommu.c b/arch/arm/mach-msm/iommu.c index e2d58e4cb0d73..329867f8547ed 100644 --- a/arch/arm/mach-msm/iommu.c +++ b/arch/arm/mach-msm/iommu.c @@ -534,7 +534,7 @@ static phys_addr_t msm_iommu_iova_to_phys(struct iommu_domain *domain, /* Invalidate context TLB */ SET_CTX_TLBIALL(base, ctx, 0); - SET_V2PPR_VA(base, ctx, va >> V2Pxx_VA_SHIFT); + SET_V2PPR(base, ctx, va & V2Pxx_VA); par = GET_PAR(base, ctx); From 663171871bc794738e8deaf96ad3230fd0b098f6 Mon Sep 17 00:00:00 2001 From: Jeff Ohlstein Date: Fri, 25 Feb 2011 21:02:22 -0800 Subject: [PATCH 1947/2556] msm: timer: Only use GPT workarounds on ARM11 The workarounds needed to use the GPT on ARM11 targets are unnecessary on Scorpion. CRs-Fixed: 273610 Change-Id: I8f216b57eb53f44f136a67ee0496c0c8dac6ca6c Signed-off-by: Jeff Ohlstein --- arch/arm/mach-msm/timer.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index 7868db17862c2..ea8bcb2197a84 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -582,9 +582,12 @@ static struct msm_clock msm_clocks[] = { .regbase = MSM_GPT_BASE, .freq = GPT_HZ, .flags = +#ifdef CONFIG_ARCH_MSM_ARM11 MSM_CLOCK_FLAGS_UNSTABLE_COUNT | MSM_CLOCK_FLAGS_ODD_MATCH_WRITE | - MSM_CLOCK_FLAGS_DELAYED_WRITE_POST, + MSM_CLOCK_FLAGS_DELAYED_WRITE_POST | +#endif + 0, .write_delay = 9, }, [MSM_CLOCK_DGT] = { From f5948a5a43a674afa69e4037442425feb7cccf56 Mon Sep 17 00:00:00 2001 From: "Chiranjeevi, Velempati" Date: Mon, 6 Jul 2009 12:46:20 +0530 Subject: [PATCH 1948/2556] USB: android gadget: mass_storage: read/write performance enhancement Implementing the csw hack feature & increasing the number of buffers used in mass storage to 4. As per the mass storage protocol, csw is sent from the device after successfully writing the data on to storage media, where there is a time factor involved. With this csw hack, csw is sent just after receiving the data & before writing the data on the storage media. By, any chance, if the write fails, the corresponding lun is removed to indicate the user of write failure. Buffers has been increased to 4, so that there is enough space for the incoming data, which is usually 64KB per CBW. User has the option of selecting/deselecting this feature from the kernel config. Signed-off-by: Velempati Chiranjeevi --- drivers/usb/gadget/Kconfig | 9 ++++ drivers/usb/gadget/f_mass_storage.c | 68 ++++++++++++++++++++++++++++- drivers/usb/gadget/storage_common.c | 6 ++- 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 9f2d46067969c..3af4afce538ed 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -986,6 +986,15 @@ config USB_ANDROID_ACCESSORY help Provides Android USB Accessory support for android gadget driver. +config USB_CSW_HACK + boolean "USB Mass storage csw hack Feature" + depends on USB_ANDROID + help + This csw hack feature is for increasing the performance of the mass + storage + + default y + config USB_CDC_COMPOSITE tristate "CDC Composite Device (Ethernet and ACM)" depends on NET diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 4e4c899473529..5de5494a184fb 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -318,7 +318,10 @@ static const char fsg_string_interface[] = "Mass Storage"; #include "storage_common.c" - +#ifdef CONFIG_USB_CSW_HACK +static int write_error_after_csw_sent; +static int csw_hack_sent; +#endif /*-------------------------------------------------------------------------*/ struct fsg_dev; @@ -478,6 +481,7 @@ static inline struct fsg_dev *fsg_from_func(struct usb_function *f) } typedef void (*fsg_routine_t)(struct fsg_dev *); +static int send_status(struct fsg_common *common); static int exception_in_progress(struct fsg_common *common) { @@ -885,6 +889,9 @@ static int do_write(struct fsg_common *common) ssize_t nwritten; int rc; +#ifdef CONFIG_USB_CSW_HACK + int i; +#endif if (curlun->ro) { curlun->sense_data = SS_WRITE_PROTECTED; return -EINVAL; @@ -998,7 +1005,17 @@ static int do_write(struct fsg_common *common) bh = common->next_buffhd_to_drain; if (bh->state == BUF_STATE_EMPTY && !get_some_more) break; /* We stopped early */ +#ifdef CONFIG_USB_CSW_HACK + /* + * If the csw packet is already submmitted to the hardware, + * by marking the state of buffer as full, then by checking + * the residue, we make sure that this csw packet is not + * written on to the storage media. + */ + if (bh->state == BUF_STATE_FULL && common->residue) { +#else if (bh->state == BUF_STATE_FULL) { +#endif smp_rmb(); common->next_buffhd_to_drain = bh->next; bh->state = BUF_STATE_EMPTY; @@ -1049,9 +1066,36 @@ static int do_write(struct fsg_common *common) curlun->sense_data = SS_WRITE_ERROR; curlun->sense_data_info = file_offset >> 9; curlun->info_valid = 1; +#ifdef CONFIG_USB_CSW_HACK + write_error_after_csw_sent = 1; + goto write_error; +#endif break; } +#ifdef CONFIG_USB_CSW_HACK +write_error: + if ((nwritten == amount) && !csw_hack_sent) { + if (write_error_after_csw_sent) + break; + /* + * Check if any of the buffer is in the + * busy state, if any buffer is in busy state, + * means the complete data is not received + * yet from the host. So there is no point in + * csw right away without the complete data. + */ + for (i = 0; i < FSG_NUM_BUFFERS; i++) { + if (common->buffhds[i].state == + BUF_STATE_BUSY) + break; + } + if (!amount_left_to_req && i == FSG_NUM_BUFFERS) { + csw_hack_sent = 1; + send_status(common); + } + } +#endif /* Did the host decide to stop early? */ if (bh->outreq->actual != bh->outreq->length) { common->short_packet_received = 1; @@ -1825,7 +1869,19 @@ static int send_status(struct fsg_common *common) csw->Signature = cpu_to_le32(USB_BULK_CS_SIG); csw->Tag = common->tag; +#ifdef CONFIG_USB_CSW_HACK + /* Since csw is being sent early, before + * writing on to storage media, need to set + * residue to zero,assuming that write will succeed. + */ + if (write_error_after_csw_sent) { + write_error_after_csw_sent = 0; + csw->Residue = cpu_to_le32(common->residue); + } else + csw->Residue = 0; +#else csw->Residue = cpu_to_le32(common->residue); +#endif csw->Status = status; bh->inreq->length = USB_BULK_CS_WRAP_LEN; @@ -2688,6 +2744,16 @@ static int fsg_main_thread(void *common_) common->state = FSG_STATE_STATUS_PHASE; spin_unlock_irq(&common->lock); +#ifdef CONFIG_USB_CSW_HACK + /* Since status is already sent for write scsi command, + * need to skip sending status once again if it is a + * write scsi command. + */ + if (csw_hack_sent) { + csw_hack_sent = 0; + continue; + } +#endif if (send_status(common)) continue; diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 1c610a0aeb654..8f836337c3055 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -262,8 +262,12 @@ static struct fsg_lun *fsg_lun_from_dev(struct device *dev) #define EP0_BUFSIZE 256 #define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ -/* Number of buffers we will use. 2 is enough for double-buffering */ +/* Number of buffers for CBW, DATA and CSW */ +#ifdef CONFIG_USB_CSW_HACK +#define FSG_NUM_BUFFERS 4 +#else #define FSG_NUM_BUFFERS 2 +#endif /* Default size of buffer length. */ #define FSG_BUFLEN ((u32)16384) From 25a1a5479287603a2dd4b7ce298addefbd25f87d Mon Sep 17 00:00:00 2001 From: Kevin Bruckert Date: Sun, 5 Jun 2011 21:36:45 -0400 Subject: [PATCH 1949/2556] video: msm: hdmi: fresh poon goodness fix edid code and bugs throughtout hdmi driver --- drivers/video/msm/hdmi/edid.c | 805 +++++++++--------- drivers/video/msm/hdmi/fb-hdmi.c | 28 +- drivers/video/msm/hdmi/include/edid.h | 3 + drivers/video/msm/hdmi/include/sil902x.h | 1 + .../video/msm/hdmi/silicon-image/av_config.c | 16 +- drivers/video/msm/hdmi/silicon-image/tpi.c | 48 +- 6 files changed, 481 insertions(+), 420 deletions(-) diff --git a/drivers/video/msm/hdmi/edid.c b/drivers/video/msm/hdmi/edid.c index 101afa1d95eaa..06ee29af56d6b 100644 --- a/drivers/video/msm/hdmi/edid.c +++ b/drivers/video/msm/hdmi/edid.c @@ -12,7 +12,7 @@ * Common function for accessing/debugging EDID data. * Reference: - http://en.wikipedia.org/wiki/Extended_display_identification_data + http://en.wikipedia.org/wiki/Extended_display_identification_data */ #include @@ -29,181 +29,180 @@ #endif static struct video_mode established_timing_db[] = { - {800, 600, 60, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 60 Hz"}, - {800, 600, 56, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 56 Hz"}, - {640, 480, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 75 Hz"}, - {640, 480, 72, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 72 Hz"}, - {640, 480, 67, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 67 Hz"}, - {640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 60 Hz"}, - {720, 400, 88, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 88 Hz"}, - {720, 400, 70, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 70 Hz"}, - - {1280, 1024, 75, ASPECT(4, 3), PROGRESSIVE, false, "1280x1024@75 Hz"}, - {1024, 768, 75, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@75 Hz"}, - {1024, 768, 70, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@70 Hz"}, - {1024, 768, 60, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@60 Hz"}, - {1024, 768, 87, ASPECT(4, 3), INTERLACE, false, - "1024x768@87 Hz (Interlaced)"}, - {832, 624, 75, ASPECT(4, 3), PROGRESSIVE, false, "832x624@75 Hz"}, - {800, 600, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600@75 Hz"}, - {800, 600, 72, ASPECT(4, 3), PROGRESSIVE, false, "800x600@72 Hz"}, - - {1152, 870, 75, ASPECT(4, 3), PROGRESSIVE, false, "1152x870 @ 75 Hz"}, + {800, 600, 60, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 60 Hz"}, + {800, 600, 56, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 56 Hz"}, + {640, 480, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600 @ 75 Hz"}, + {640, 480, 72, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 72 Hz"}, + {640, 480, 67, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 67 Hz"}, + {640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, "640x480 @ 60 Hz"}, + {720, 400, 88, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 88 Hz"}, + {720, 400, 70, ASPECT(4, 3), PROGRESSIVE, false, "720x400 @ 70 Hz"}, + + {1280, 1024, 75, ASPECT(4, 3), PROGRESSIVE, false, "1280x1024@75 Hz"}, + {1024, 768, 75, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@75 Hz"}, + {1024, 768, 70, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@70 Hz"}, + {1024, 768, 60, ASPECT(4, 3), PROGRESSIVE, false, "1024x768@60 Hz"}, + {1024, 768, 87, ASPECT(4, 3), INTERLACE, false, "1024x768@87 Hz (Interlaced)"}, + {832, 624, 75, ASPECT(4, 3), PROGRESSIVE, false, "832x624@75 Hz"}, + {800, 600, 75, ASPECT(4, 3), PROGRESSIVE, false, "800x600@75 Hz"}, + {800, 600, 72, ASPECT(4, 3), PROGRESSIVE, false, "800x600@72 Hz"}, + + {1152, 870, 75, ASPECT(4, 3), PROGRESSIVE, false, "1152x870 @ 75 Hz"}, }; static struct video_mode standard_timing_db[8]; static struct video_mode additional_timing_db[] = { - {640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, - " 1 DMT0659 4:3 640x480p @ 59.94/60Hz"}, - {720, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, - " 2 480p 4:3 720x480p @ 59.94/60Hz"}, - {720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, - " 3 480pH 16:9 720x480p @ 59.94/60Hz"}, - {1280, 720, 60, ASPECT(16, 9), PROGRESSIVE, false, - " 4 720p 16:9 1280x720p @ 59.94/60Hz"}, - {1920, 1080, 60, ASPECT(4, 3), INTERLACE, false, - " 5 1080i 16:9 1920x1080i @ 59.94/60Hz"}, - {720, 480, 60, ASPECT(4, 3), INTERLACE, false, - " 6 480i 4:3 720(1440)x480i @ 59.94/60Hz"}, - {720, 480, 60, ASPECT(16, 9), INTERLACE, false, - " 7 480iH 16:9 720(1440)x480i @ 59.94/60Hz"}, - {720, 240, 60, ASPECT(4, 3), PROGRESSIVE, false, - " 8 240p 4:3 720(1440)x240p @ 59.94/60Hz"}, - {720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, - " 9 240pH 16:9 720(1440)x240p @ 59.94/60Hz"}, - {2880, 480, 60, ASPECT(4, 3), INTERLACE, false, - "10 480i4x 4:3 (2880)x480i @ 59.94/60Hz"}, - {2880, 480, 60, ASPECT(16, 9), INTERLACE, false, - "11 480i4xH 16:9 (2880)x480i @ 59.94/60Hz"}, - {2880, 240, 60, ASPECT(4, 3), PROGRESSIVE, false, - "12 240p4x 4:3 (2880)x240p @ 59.94/60Hz"}, - {2880, 240, 60, ASPECT(16, 9), PROGRESSIVE, false, - "13 240p4xH 16:9 (2880)x240p @ 59.94/60Hz"}, - {1440, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, - "14 480p2x 4:3 1440x480p @ 59.94/60Hz"}, - {1440, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, - "15 480p2xH 16:9 1440x480p @ 59.94/60Hz"}, - {1920, 1080, 60, ASPECT(16, 9), PROGRESSIVE, false, - "16 1080p 16:9 1920x1080p @ 59.94/60Hz"}, - {720, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, - "17 576p 4:3 720x576p @ 50Hz"}, - {720, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, - "18 576pH 16:9 720x576p @ 50Hz"}, - {1280, 720, 50, ASPECT(16, 9), PROGRESSIVE, false, - "19 720p50 16:9 1280x720p @ 50Hz"}, - {1920, 1080, 50, ASPECT(16, 9), INTERLACE, false, - "20 1080i25 16:9 1920x1080i @ 50Hz*"}, - {1440, 576, 50, ASPECT(4, 3), INTERLACE, false, - "21 576i 4:3 720(1440)x576i @ 50Hz"}, - {1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, - "22 576iH 16:9 720(1440)x576i @ 50Hz"}, - {720, 288, 50, ASPECT(4, 3), PROGRESSIVE, false, - "23 288p 4:3 720(1440)x288p @ 50Hz"}, - {720, 288, 50, ASPECT(16, 9), PROGRESSIVE, false, - "24 288pH 16:9 720(1440)x288p @ 50Hz"}, - {2880, 576, 50, ASPECT(4, 3), INTERLACE, false, - "25 576i4x 4:3 (2880)x576i @ 50Hz"}, - {2880, 576, 50, ASPECT(16, 9), INTERLACE, false, - "26 576i4xH 16:9 (2880)x576i @ 50Hz"}, - {2880, 288, 50, ASPECT(4, 3), PROGRESSIVE, false, - "27 288p4x 4:3 (2880)x288p @ 50Hz"}, - {2880, 288, 50, ASPECT(16, 9), PROGRESSIVE, false, - "28 288p4xH 16:9 (2880)x288p @ 50Hz"}, - {1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, - "29 576p2x 4:3 1440x576p @ 50Hz"}, - {1440, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, - "30 576p2xH 16:9 1440x576p @ 50Hz"}, - {1920, 1080, 50, ASPECT(16, 9), PROGRESSIVE, false, - "31 1080p50 16:9 1920x1080p @ 50Hz"}, - {1920, 1080, 24, ASPECT(16, 9), PROGRESSIVE, false, - "32 1080p24 16:9 1920x1080p @ 23.98/24Hz"}, - {1920, 1080, 25, ASPECT(16, 9), PROGRESSIVE, false, - "33 1080p25 16:9 1920x1080p @ 25Hz"}, - {1920, 1080, 30, ASPECT(16, 9), PROGRESSIVE, false, - "34 1080p30 16:9 1920x1080p @ 29.97/30Hz"}, - {2880, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, - "35 480p4x 4:3 (2880)x480p @ 59.94/60Hz"}, - {2880, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, - "36 480p4xH 16:9 (2880)x480p @ 59.94/60Hz"}, - {2880, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, - "37 576p4x 4:3 (2880)x576p @ 50Hz"}, - {2880, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, - "38 576p4xH 16:9 (2880)x576p @ 50Hz"}, - {1920, 1080, 50, ASPECT(16, 9), INTERLACE, false, - "39 108Oi25 16:9 1920x1080i(1250 Total) @ 50Hz*"}, - {1920, 1080, 100, ASPECT(16, 9), INTERLACE, false, - "40 1080i50 16:9 1920x1080i @ 100Hz"}, - {1280, 720, 100, ASPECT(16, 9), PROGRESSIVE, false, - "41 720p100 16:9 1280x720p @ 100Hz"}, - {720, 576, 100, ASPECT(4, 3), PROGRESSIVE, false, - "42 576p100 4:3 720x576p @ 100Hz"}, - {720, 576, 100, ASPECT(16, 9), PROGRESSIVE, false, - "43 576p100H 16:9 720x576p @ 100Hz"}, - {720, 576, 100, ASPECT(4, 3), INTERLACE, false, - "44 576i50 4:3 720(1440)x576i @ 100Hz"}, - {720, 576, 100, ASPECT(16, 9), INTERLACE, false, - "45 576i50H 16:9 720(1440)x576i @ 100Hz"}, - {1920, 1080, 120, ASPECT(16, 9), INTERLACE, false, - "46 1080i60 16:9 1920x1080i @ 119.88/120Hz"}, - {1280, 720, 120, ASPECT(16, 9), PROGRESSIVE, false, - "47 720p120 16:9 1280x720p @ 119.88/120Hz"}, - {720, 480, 120, ASPECT(4, 3), PROGRESSIVE, false, - "48 480p119 4:3 720x480p @ 119.88/120Hz"}, - {720, 480, 120, ASPECT(16, 9), PROGRESSIVE, false, - "49 480p119H 16:9 720x480p @ 119.88/120Hz"}, - {720, 480, 120, ASPECT(4, 3), INTERLACE, false, - "50 480i59 4:3 720(1440)x480i @ 119.88/120Hz"}, - {720, 480, 120, ASPECT(16, 9), INTERLACE, false, - "51 480i59H 16:9 720(1440)x480i @ 119.88/120Hz"}, - {720, 576, 200, ASPECT(4, 3), PROGRESSIVE, false, - "52 576p200 4:3 720x576p @ 200Hz"}, - {720, 576, 200, ASPECT(16, 9), PROGRESSIVE, false, - "53 576p200H 16:9 720x576p @ 200Hz"}, - {720, 576, 200, ASPECT(4, 3), INTERLACE, false, - "54 576i100 4:3 720(1440)x576i @ 200Hz"}, - {720, 576, 200, ASPECT(16, 9), INTERLACE, false, - "55 576i100H 16:9 720(1440)x576i @ 200Hz"}, - {720, 480, 240, ASPECT(4, 3), PROGRESSIVE, false, - "56 480p239 4:3 720x480p @ 239.76/240Hz"}, - {720, 480, 240, ASPECT(16, 9), PROGRESSIVE, false, - "57 480p239H 16:9 720x480p @ 239.76/240Hz"}, - {720, 480, 240, ASPECT(4, 3), INTERLACE, false, - "58 480i119 4:3 720(1440)x480i @ 239.76/240Hz"}, - {720, 480, 240, ASPECT(16, 9), INTERLACE, false, - "59 480i119H 16:9 720(1440)x480i @ 239.76/240Hz"}, - {1280, 720, 24, ASPECT(16, 9), PROGRESSIVE, false, - "60 720p24 16:9 1280x720p @ 23.98/24Hz"}, - {1280, 720, 25, ASPECT(16, 9), PROGRESSIVE, false, - "61 720p25 16:9 1280x720p @ 25Hz"}, - {1280, 720, 30, ASPECT(16, 9), PROGRESSIVE, false, - "62 720p30 16:9 1280x720p @ 29.97/30Hz"}, - {1920, 1080, 120, ASPECT(16, 9), PROGRESSIVE, false, - "63 1080p120 16:9 1920x1080 @ 119.88/120Hz"}, + {640, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, + " 1 DMT0659 4:3 640x480p @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, + " 2 480p 4:3 720x480p @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, + " 3 480pH 16:9 720x480p @ 59.94/60Hz"}, + {1280, 720, 60, ASPECT(16, 9), PROGRESSIVE, false, + " 4 720p 16:9 1280x720p @ 59.94/60Hz"}, + {1920, 1080, 60, ASPECT(4, 3), INTERLACE, false, + " 5 1080i 16:9 1920x1080i @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(4, 3), INTERLACE, false, + " 6 480i 4:3 720(1440)x480i @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(16, 9), INTERLACE, false, + " 7 480iH 16:9 720(1440)x480i @ 59.94/60Hz"}, + {720, 240, 60, ASPECT(4, 3), PROGRESSIVE, false, + " 8 240p 4:3 720(1440)x240p @ 59.94/60Hz"}, + {720, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, + " 9 240pH 16:9 720(1440)x240p @ 59.94/60Hz"}, + {2880, 480, 60, ASPECT(4, 3), INTERLACE, false, + "10 480i4x 4:3 (2880)x480i @ 59.94/60Hz"}, + {2880, 480, 60, ASPECT(16, 9), INTERLACE, false, + "11 480i4xH 16:9 (2880)x480i @ 59.94/60Hz"}, + {2880, 240, 60, ASPECT(4, 3), PROGRESSIVE, false, + "12 240p4x 4:3 (2880)x240p @ 59.94/60Hz"}, + {2880, 240, 60, ASPECT(16, 9), PROGRESSIVE, false, + "13 240p4xH 16:9 (2880)x240p @ 59.94/60Hz"}, + {1440, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, + "14 480p2x 4:3 1440x480p @ 59.94/60Hz"}, + {1440, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, + "15 480p2xH 16:9 1440x480p @ 59.94/60Hz"}, + {1920, 1080, 60, ASPECT(16, 9), PROGRESSIVE, false, + "16 1080p 16:9 1920x1080p @ 59.94/60Hz"}, + {720, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, + "17 576p 4:3 720x576p @ 50Hz"}, + {720, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, + "18 576pH 16:9 720x576p @ 50Hz"}, + {1280, 720, 50, ASPECT(16, 9), PROGRESSIVE, false, + "19 720p50 16:9 1280x720p @ 50Hz"}, + {1920, 1080, 50, ASPECT(16, 9), INTERLACE, false, + "20 1080i25 16:9 1920x1080i @ 50Hz*"}, + {1440, 576, 50, ASPECT(4, 3), INTERLACE, false, + "21 576i 4:3 720(1440)x576i @ 50Hz"}, + {1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, + "22 576iH 16:9 720(1440)x576i @ 50Hz"}, + {720, 288, 50, ASPECT(4, 3), PROGRESSIVE, false, + "23 288p 4:3 720(1440)x288p @ 50Hz"}, + {720, 288, 50, ASPECT(16, 9), PROGRESSIVE, false, + "24 288pH 16:9 720(1440)x288p @ 50Hz"}, + {2880, 576, 50, ASPECT(4, 3), INTERLACE, false, + "25 576i4x 4:3 (2880)x576i @ 50Hz"}, + {2880, 576, 50, ASPECT(16, 9), INTERLACE, false, + "26 576i4xH 16:9 (2880)x576i @ 50Hz"}, + {2880, 288, 50, ASPECT(4, 3), PROGRESSIVE, false, + "27 288p4x 4:3 (2880)x288p @ 50Hz"}, + {2880, 288, 50, ASPECT(16, 9), PROGRESSIVE, false, + "28 288p4xH 16:9 (2880)x288p @ 50Hz"}, + {1440, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, + "29 576p2x 4:3 1440x576p @ 50Hz"}, + {1440, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, + "30 576p2xH 16:9 1440x576p @ 50Hz"}, + {1920, 1080, 50, ASPECT(16, 9), PROGRESSIVE, false, + "31 1080p50 16:9 1920x1080p @ 50Hz"}, + {1920, 1080, 24, ASPECT(16, 9), PROGRESSIVE, false, + "32 1080p24 16:9 1920x1080p @ 23.98/24Hz"}, + {1920, 1080, 25, ASPECT(16, 9), PROGRESSIVE, false, + "33 1080p25 16:9 1920x1080p @ 25Hz"}, + {1920, 1080, 30, ASPECT(16, 9), PROGRESSIVE, false, + "34 1080p30 16:9 1920x1080p @ 29.97/30Hz"}, + {2880, 480, 60, ASPECT(4, 3), PROGRESSIVE, false, + "35 480p4x 4:3 (2880)x480p @ 59.94/60Hz"}, + {2880, 480, 60, ASPECT(16, 9), PROGRESSIVE, false, + "36 480p4xH 16:9 (2880)x480p @ 59.94/60Hz"}, + {2880, 576, 50, ASPECT(4, 3), PROGRESSIVE, false, + "37 576p4x 4:3 (2880)x576p @ 50Hz"}, + {2880, 576, 50, ASPECT(16, 9), PROGRESSIVE, false, + "38 576p4xH 16:9 (2880)x576p @ 50Hz"}, + {1920, 1080, 50, ASPECT(16, 9), INTERLACE, false, + "39 108Oi25 16:9 1920x1080i(1250 Total) @ 50Hz*"}, + {1920, 1080, 100, ASPECT(16, 9), INTERLACE, false, + "40 1080i50 16:9 1920x1080i @ 100Hz"}, + {1280, 720, 100, ASPECT(16, 9), PROGRESSIVE, false, + "41 720p100 16:9 1280x720p @ 100Hz"}, + {720, 576, 100, ASPECT(4, 3), PROGRESSIVE, false, + "42 576p100 4:3 720x576p @ 100Hz"}, + {720, 576, 100, ASPECT(16, 9), PROGRESSIVE, false, + "43 576p100H 16:9 720x576p @ 100Hz"}, + {720, 576, 100, ASPECT(4, 3), INTERLACE, false, + "44 576i50 4:3 720(1440)x576i @ 100Hz"}, + {720, 576, 100, ASPECT(16, 9), INTERLACE, false, + "45 576i50H 16:9 720(1440)x576i @ 100Hz"}, + {1920, 1080, 120, ASPECT(16, 9), INTERLACE, false, + "46 1080i60 16:9 1920x1080i @ 119.88/120Hz"}, + {1280, 720, 120, ASPECT(16, 9), PROGRESSIVE, false, + "47 720p120 16:9 1280x720p @ 119.88/120Hz"}, + {720, 480, 120, ASPECT(4, 3), PROGRESSIVE, false, + "48 480p119 4:3 720x480p @ 119.88/120Hz"}, + {720, 480, 120, ASPECT(16, 9), PROGRESSIVE, false, + "49 480p119H 16:9 720x480p @ 119.88/120Hz"}, + {720, 480, 120, ASPECT(4, 3), INTERLACE, false, + "50 480i59 4:3 720(1440)x480i @ 119.88/120Hz"}, + {720, 480, 120, ASPECT(16, 9), INTERLACE, false, + "51 480i59H 16:9 720(1440)x480i @ 119.88/120Hz"}, + {720, 576, 200, ASPECT(4, 3), PROGRESSIVE, false, + "52 576p200 4:3 720x576p @ 200Hz"}, + {720, 576, 200, ASPECT(16, 9), PROGRESSIVE, false, + "53 576p200H 16:9 720x576p @ 200Hz"}, + {720, 576, 200, ASPECT(4, 3), INTERLACE, false, + "54 576i100 4:3 720(1440)x576i @ 200Hz"}, + {720, 576, 200, ASPECT(16, 9), INTERLACE, false, + "55 576i100H 16:9 720(1440)x576i @ 200Hz"}, + {720, 480, 240, ASPECT(4, 3), PROGRESSIVE, false, + "56 480p239 4:3 720x480p @ 239.76/240Hz"}, + {720, 480, 240, ASPECT(16, 9), PROGRESSIVE, false, + "57 480p239H 16:9 720x480p @ 239.76/240Hz"}, + {720, 480, 240, ASPECT(4, 3), INTERLACE, false, + "58 480i119 4:3 720(1440)x480i @ 239.76/240Hz"}, + {720, 480, 240, ASPECT(16, 9), INTERLACE, false, + "59 480i119H 16:9 720(1440)x480i @ 239.76/240Hz"}, + {1280, 720, 24, ASPECT(16, 9), PROGRESSIVE, false, + "60 720p24 16:9 1280x720p @ 23.98/24Hz"}, + {1280, 720, 25, ASPECT(16, 9), PROGRESSIVE, false, + "61 720p25 16:9 1280x720p @ 25Hz"}, + {1280, 720, 30, ASPECT(16, 9), PROGRESSIVE, false, + "62 720p30 16:9 1280x720p @ 29.97/30Hz"}, + {1920, 1080, 120, ASPECT(16, 9), PROGRESSIVE, false, + "63 1080p120 16:9 1920x1080 @ 119.88/120Hz"}, }; /* device supported modes in CEA */ enum { - CEA_MODE_640X480P_60HZ_4_3 = 0, - CEA_MODE_720X480P_60HZ_4_3 = 1, - CEA_MODE_720X480P_60HZ_16_9 = 2, - CEA_MODE_1280X720P_60HZ_16_9 = 3, - CEA_MODE_720X576P_50HZ_4_3 = 16, - CEA_MODE_720X576P_50HZ_16_9 = 17, + CEA_MODE_640X480P_60HZ_4_3 = 0, + CEA_MODE_720X480P_60HZ_4_3 = 1, + CEA_MODE_720X480P_60HZ_16_9 = 2, + CEA_MODE_1280X720P_60HZ_16_9 = 3, + CEA_MODE_720X576P_50HZ_4_3 = 16, + CEA_MODE_720X576P_50HZ_16_9 = 17, }; /* device supported modes in established timing */ enum { - ESTABLISHED_MODE_800X600_60HZ = 0, - ESTABLISHED_MODE_640X480_60HZ = 5, + ESTABLISHED_MODE_800X600_60HZ = 0, + ESTABLISHED_MODE_640X480_60HZ = 5, }; int init_edid_info(struct edid_info_struct *edid_info) { - edid_info->is_valid = false; - mutex_init(&edid_info->access_lock); + edid_info->is_valid = false; + mutex_init(&edid_info->access_lock); - return 0; + return 0; } /* Byte 35-37 of block-0 */ @@ -356,65 +355,46 @@ int edid_dump_video_modes(u8 *edid_buf) bool edid_do_checksum(u8 *data) { - int i; - u8 sum = 0; + int i; + u8 sum = 0; - for (i = 0; i < EDID_BLOCK_SIZE; i++) - sum += data[i]; - EDID_DBG("%s: result=%s\n", __func__, sum ? "fail" : "pass"); - return sum ? false : true; + for (i = 0; i < EDID_BLOCK_SIZE; i++) + sum += data[i]; + EDID_DBG("%s: result=%s\n", __func__, sum ? "fail" : "pass"); + return sum ? false : true; } static bool edid_check_header(u8 *data) { - int ret, i = 0; - /* EDID 8 bytes header */ - static u8 header[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}; + int ret = true; - for (i = 0; i < ARRAY_SIZE(header); i++) - if (data[i] != header[i]) - break; - ret = (i == ARRAY_SIZE(header)) ? true : false; - EDID_DBG("%s: result=%s\n", __func__, ret ? "pass" : "fail"); + /* EDID 8 bytes header */ + static const u8 header[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}; - return ret; -} + if (memcmp(data, header, 8) != 0) + ret = false; -bool edid_check_hdmi_sink(struct hdmi_info *hdmi, int block) -{ - int ret = false, index = 4; - u8 *data = &hdmi->edid_buf[block * 128]; - /* block offset where long descriptors start */ - int long_desc_offset = data[LONG_DESCR_PTR_IDX]; - int tag_code, data_block_len; - - while (index < long_desc_offset) { - tag_code = (data[index] >> 5) & 0x7; - data_block_len = data[index++] & 0x1f; - if (tag_code == VENDOR_SPEC_D_BLOCK && - data[index] == 0x03 && - data[index + 1] == 0x0c && - data[index + 2] == 0x00) { - ret = true; - break; - } else - index += data_block_len; - } - hdmi->edid_info.hdmi_sink = ret; - EDID_DBG("%s: ret=%s\n", __func__, ret ? "yes" : "no"); - return ret; + EDID_DBG("%s: result=%s\n", __func__, ret ? "pass" : "fail"); + if (!edid_do_checksum(data)) + { + pr_err("%s: checksum error\n", __func__); + + // Not all monitors have a proper checksum, so we'll ignore this error and just log the problem. + // ret = false; + } + return ret; } struct edid_black_list_info { - u8 mfr_model[4]; - u8 prefer_modes[3]; + u8 mfr_model[4]; + u8 prefer_modes[3]; }; struct edid_black_list_info edid_black_list[] = { - { {0x4c, 0x2d, 0xa5, 0x02}, {0, 0, 0x40} }, // 720p only - { {0x4c, 0x2d, 0x0d, 0x05}, {0, 0, 0x40} }, // 720p only + { {0x4c, 0x2d, 0xa5, 0x02}, {0, 0, 0x40} }, // 720p only + { {0x4c, 0x2d, 0x0d, 0x05}, {0, 0, 0x40} }, // 720p only - //{ {0x5a, 0x63, 0x20, 0x2b}, {0, 0, 0x40} }, // Viewsonic test + //{ {0x5a, 0x63, 0x20, 0x2b}, {0, 0, 0x40} }, // Viewsonic test }; /* By comparing the Manufacture(0x8, 0x9) and Model field(0xa, 0xb) of EDID, @@ -422,189 +402,254 @@ struct edid_black_list_info edid_black_list[] = { */ int edid_fixup_compatibility_list(struct hdmi_info *hdmi) { - int i, ret = -1; + int i, ret = -1; - /* FIXME: magic numbers...*/ - for (i = 0; i < ARRAY_SIZE(edid_black_list); i++) { - if (!memcmp(hdmi->edid_buf + 8, edid_black_list[i].mfr_model, 4)){ + /* FIXME: magic numbers...*/ + for (i = 0; i < ARRAY_SIZE(edid_black_list); i++) { + if (!memcmp(hdmi->edid_buf + 8, edid_black_list[i].mfr_model, 4)){ #if 0 - EDID_DBG("%s: found in blacklist %d\n", __func__, i); - EDID_DBG("%s: old timing = {%02x, %02x, %02x}\n", - __func__, - hdmi->edid_buf[35], hdmi->edid_buf[36], - hdmi->edid_buf[37]); - memcpy(hdmi->edid_buf + 35, - edid_black_list[i].prefer_modes, 3); - EDID_DBG("%s: new timing = {%02x, %02x, %02x}\n", - __func__, - hdmi->edid_buf[35], hdmi->edid_buf[36], - hdmi->edid_buf[37]); + EDID_DBG("%s: found in blacklist %d\n", __func__, i); + EDID_DBG("%s: old timing = {%02x, %02x, %02x}\n", + __func__, + hdmi->edid_buf[35], hdmi->edid_buf[36], + hdmi->edid_buf[37]); + memcpy(hdmi->edid_buf + 35, + edid_black_list[i].prefer_modes, 3); + EDID_DBG("%s: new timing = {%02x, %02x, %02x}\n", + __func__, + hdmi->edid_buf[35], hdmi->edid_buf[36], + hdmi->edid_buf[37]); #else - EDID_DBG("%s: found in compatibility %d\n", __func__, i); - memcpy(hdmi->edid_buf + 35, - edid_black_list[i].prefer_modes, 3); + EDID_DBG("%s: found in compatibility %d\n", __func__, i); + memcpy(hdmi->edid_buf + 35, + edid_black_list[i].prefer_modes, 3); #endif - ret = i; - break; - } - } + ret = i; + break; + } + } - return ret; + return ret; } u8 edid_simple_parsing(struct hdmi_info *hdmi) { - u8 *edid_buf = hdmi->edid_buf; - int i, index, ret = -EINVAL; - struct edid_info_struct *edid_info = &hdmi->edid_info; - unsigned v1, width, height, aspect; - unsigned extensions; - - EDID_DBG("%s\n", __func__); - // FIXME: integrate with edid_check() - if (!edid_do_checksum(edid_buf)) { - pr_err("%s: checksum error\n", __func__); - //return EDID_CHECKSUM_ERROR; - } - if (!edid_check_header(edid_buf)) { - pr_err("%s: incorrect header\n", __func__); - return INCORRECT_EDID_HEADER; - } - edid_info->under_scan = ((edid_buf[MISC_SUPPORT_IDX]) >> 7) & 0x1; - edid_info->basic_audio = ((edid_buf[MISC_SUPPORT_IDX]) >> 6) & 0x1; - edid_info->ycbcr_4_4_4 = ((edid_buf[MISC_SUPPORT_IDX]) >> 5) & 0x1; - edid_info->ycbcr_4_2_2 = ((edid_buf[MISC_SUPPORT_IDX]) >> 4) & 0x1; - - // FIXME: 0x7e - extensions = edid_buf[0x7e]; - EDID_DBG("%s: extensions=%d\n", __func__, extensions); - if (!extensions) { - hdmi->edid_info.hdmi_sink = false; - return NO_861_EXTENSIONS; - } - //return; - - /* reset all supported */ - for (i = 0 ; i < ARRAY_SIZE(additional_timing_db); i++) - additional_timing_db[i].supported = false; - for (i = 0 ; i < ARRAY_SIZE(established_timing_db); i++) - established_timing_db[i].supported = false; - - /* Block 0: established timing */ - pr_info("established timing: {%02x, %02x, %02x}\n", - edid_buf[35], edid_buf[36], edid_buf[37]); - - v1 = edid_buf[35] | edid_buf[36] << 8 | (!!edid_buf[37]) << 16; - - for (i = 0 ; i < 17; i++ ) // 17 bits defined in established timing - established_timing_db[i].supported = ((v1 >>= 1) & 1) ; + u8 *edid_buf = hdmi->edid_buf; + int i, index, ret = -EINVAL; + struct edid_info_struct *edid_info = &hdmi->edid_info; + unsigned v1, width, height, aspect; + unsigned extensions; + unsigned extension; + + EDID_DBG("%s\n", __func__); + if (!edid_check_header(edid_buf)) + { + pr_err("%s: incorrect header\n", __func__); + return INCORRECT_EDID_HEADER; + } + + // Retrieve the number of extensions in this EDID + extensions = edid_buf[126]; + EDID_DBG("%s: extensions=%d\n", __func__, extensions); + if (!extensions) + { + hdmi->edid_info.hdmi_sink = false; + return NO_861_EXTENSIONS; + } + + /* reset all supported */ + for (i = 0 ; i < ARRAY_SIZE(additional_timing_db); i++) + additional_timing_db[i].supported = false; + for (i = 0 ; i < ARRAY_SIZE(established_timing_db); i++) + established_timing_db[i].supported = false; + + /* Block 0: established timing */ + pr_info("established timing: {%02x, %02x, %02x}\n", + edid_buf[35], edid_buf[36], edid_buf[37]); + + v1 = edid_buf[35] | edid_buf[36] << 8; + if (edid_buf[37] & 0x80) + v1 |= 0x00010000; + + for (i = 0 ; i < 17; i++ ) // 17 bits defined in established timing + established_timing_db[i].supported = ((v1 >>= 1) & 1) ; + + /* standard timing identification */ + for (i = 0; i < 8; i++) { + width = (edid_buf[38 + (i * 2)] * 8) + 248; + v1 = edid_buf[38 + (i * 2) + 1]; + switch (v1 >> 6) { + case 0: height = width * 10 / 16; aspect = ASPECT(16, 10); break; + case 1: height = width * 3 / 4; aspect = ASPECT(4, 3); break; + case 2: height = width * 4 / 5; aspect = ASPECT(5, 4); break; + case 3: height = width * 9 / 16; aspect = ASPECT(16, 9); break; + } + standard_timing_db[i].width = width; + standard_timing_db[i].height = height; + standard_timing_db[i].aspect = aspect; + standard_timing_db[i].refresh_rate = (v1 & 0x3F) + 60; + standard_timing_db[i].supported = true; + } + + for (extension = 0; extension < extensions; extension++) + { + unsigned baseOffset = (extension + 1) * 0x80; + unsigned dtdStart = 0; + unsigned nativeFormats = 0; + unsigned dbcOffset; + + EDID_DBG("Extension %d\n", extension +1); + if (edid_buf[baseOffset] != 0x02) + { + EDID_DBG(" Extension is not CEA EDID format. Skipping.\n"); + continue; + } -#if 0 - /* standard timing identification */ - for (i = 0; i < 8; i++) { - width = edid_buf[38 + i * 2] * 8 + 248; - v1 = edid_buf[38 + i * 2 + 1]; - switch (v1 >> 6) { - case 0: height = width * 10 / 16; aspect = ASPECT(16, 10); break; - case 1: height = width * 3 / 4; aspect = ASPECT(4, 3); break; - case 2: height = width * 4 / 5; aspect = ASPECT(5, 4); break; - case 3: height = width * 9 / 16; aspect = ASPECT(16, 9); break; - } - standard_timing_db[i].width = width; - standard_timing_db[i].height = height; - standard_timing_db[i].aspect = aspect; - standard_timing_db[i].refresh_rate = (v1 & ~(3 << 6)) + 60; - standard_timing_db[i].supported = true; - } -#endif + if (edid_buf[baseOffset+1] < 0x03) + { + EDID_DBG(" CEA EDID Extension is below version 3. Skipping.\n"); + continue; + } + + dtdStart = edid_buf[baseOffset+2]; + + edid_info->under_scan = edid_buf[baseOffset+3] & EDID_BIT(7); + edid_info->basic_audio = edid_buf[baseOffset+3] & EDID_BIT(6); + edid_info->ycbcr_4_4_4 = edid_buf[baseOffset+3] & EDID_BIT(5); + edid_info->ycbcr_4_2_2 = edid_buf[baseOffset+3] & EDID_BIT(4); - if (extensions == 1) { - EDID_DBG("Block-1: additional timing\n"); - /* Block-1: additional timing */ - for( i = 0; i < (edid_buf[128 + 4] & 0x1f); i++) { - index = edid_buf[128 + 5 + i] & 0x7f; - additional_timing_db[index-1].supported = true; - EDID_DBG("%s\n", additional_timing_db[index-1].descrption); - } - edid_check_hdmi_sink(hdmi, 1); - } else { - EDID_DBG("Block-2: additional timing\n"); - for( i = 0; i < (edid_buf[384 + 4] & 0x1f); i++) { - index = edid_buf[384 + 5 + i] & 0x7f; - additional_timing_db[index-1].supported = true; - EDID_DBG("%s\n", additional_timing_db[index-1].descrption); - } - edid_check_hdmi_sink(hdmi, 3); - } - - edid_buf[35] = 0; - edid_buf[36] = 0; - edid_buf[37] = 0; - - /* edid_buf[37] bit4: 480p, bit5: 576p, bit6: 720p */ - if (additional_timing_db[CEA_MODE_720X480P_60HZ_4_3].supported || - additional_timing_db[CEA_MODE_720X480P_60HZ_16_9].supported) { - EDID_DBG("decide to support 480P\n"); - edid_buf[37] |= (1<<4); - } - - if (additional_timing_db[CEA_MODE_720X576P_50HZ_4_3].supported || - additional_timing_db[CEA_MODE_720X576P_50HZ_16_9].supported) { - EDID_DBG("decide to support 576P\n"); - edid_buf[37] |= (1<<5); - } - - if (additional_timing_db[CEA_MODE_1280X720P_60HZ_16_9].supported) { - EDID_DBG("decide to support 720P\n"); - edid_buf[37] |= (1<<6); - } - - if (established_timing_db[ESTABLISHED_MODE_800X600_60HZ].supported) { - EDID_DBG("decide to support 800x600\n"); - edid_buf[36] |= (1<<6); - } - - if (established_timing_db[ESTABLISHED_MODE_640X480_60HZ].supported) { - EDID_DBG("decide to support 640x480\n"); - edid_buf[35] |= (1<<5); - } - edid_fixup_compatibility_list(hdmi); - - return ret; + nativeFormats = edid_buf[baseOffset+3] & 0x04; + dbcOffset = 4; + + while (dbcOffset < dtdStart) + { + unsigned blockType = edid_buf[baseOffset + dbcOffset] >> 5; + unsigned blockLen = edid_buf[baseOffset + dbcOffset] & 0x1f; + unsigned byte; + + EDID_DBG(" Block Type: %d Block Length: %d\n", blockType, blockLen); + + // Check for an audio data block + if (blockType == AUDIO_D_BLOCK) + { + edid_info->basic_audio = true; + EDID_DBG(" CEA3 Audio Data Block found.\n"); + dbcOffset += blockLen + 1; + continue; + } + + // Check for a vendor data block + if (blockType == VENDOR_SPEC_D_BLOCK) + { + EDID_DBG(" CEA3 Vendor Block found.\n"); + // This may be an HDMI vendor block, and if so, we need to parse it + if (edid_buf[baseOffset + dbcOffset + 1] == 0x03 && + edid_buf[baseOffset + dbcOffset + 2] == 0x0C && + edid_buf[baseOffset + dbcOffset + 3] == 0x00 ) + { + // We found the HDMI block + EDID_DBG(" CEA3 HDMI Vendor Block found.\n"); + hdmi->edid_info.hdmi_sink = true; + } + dbcOffset += blockLen + 1; + continue; + } + if (blockType != VIDEO_D_BLOCK) + { + dbcOffset += blockLen + 1; + continue; + } + + // The block will be an array of indexes + for (byte = 1; byte < blockLen; byte++) + { + index = edid_buf[baseOffset + dbcOffset + byte] & 0x7f; + + if (index > 63) + { + EDID_DBG("Invalid index in EDID Video block. Ignoring.\n"); + } + else + { + additional_timing_db[index-1].supported = true; + EDID_DBG("%s\n", additional_timing_db[index-1].descrption); + } + } + + dbcOffset += blockLen + 1; + } + } + + // As a cheat, we're replacing the existing timings with our own "custom" + // definition of these bytes. + edid_buf[35] = 0; + edid_buf[36] = 0; + edid_buf[37] = 0; + + /* edid_buf[37] bit4: 480p, bit5: 576p, bit6: 720p */ + if (additional_timing_db[CEA_MODE_720X480P_60HZ_4_3].supported || + additional_timing_db[CEA_MODE_720X480P_60HZ_16_9].supported) { + EDID_DBG("decide to support 480P\n"); + edid_buf[37] |= (1<<4); + } + + if (additional_timing_db[CEA_MODE_720X576P_50HZ_4_3].supported || + additional_timing_db[CEA_MODE_720X576P_50HZ_16_9].supported) { + EDID_DBG("decide to support 576P\n"); + edid_buf[37] |= (1<<5); + } + + if (additional_timing_db[CEA_MODE_1280X720P_60HZ_16_9].supported) { + EDID_DBG("decide to support 720P\n"); + edid_buf[37] |= (1<<6); + } + + if (established_timing_db[ESTABLISHED_MODE_800X600_60HZ].supported) { + EDID_DBG("decide to support 800x600\n"); + edid_buf[36] |= (1<<6); + } + + if (established_timing_db[ESTABLISHED_MODE_640X480_60HZ].supported) { + EDID_DBG("decide to support 640x480\n"); + edid_buf[35] |= (1<<5); + } + edid_fixup_compatibility_list(hdmi); + + return ret; } // FIXME: modify the checking routines into inline function. bool edid_is_video_mode_supported(struct video_mode *vmode) { - int i; - struct video_mode *vmode_db; - - vmode_db = established_timing_db; - for (i = 0, vmode_db = established_timing_db; - i < ARRAY_SIZE(established_timing_db); i++) - if ( (vmode->width == vmode_db[i].width) && - (vmode->height == vmode_db[i].height) && - (vmode->refresh_rate == vmode_db[i].refresh_rate ) && - (vmode_db[i].interlaced == PROGRESSIVE) && - (vmode_db[i].supported == true )) - return true; - for (i = 0, vmode_db = standard_timing_db; - i < ARRAY_SIZE(standard_timing_db); i++) - if ( (vmode->width == vmode_db[i].width) && - (vmode->height == vmode_db[i].height) && - (vmode->refresh_rate == vmode_db[i].refresh_rate ) && - (vmode_db[i].interlaced == PROGRESSIVE) && - (vmode_db[i].supported == true )) - return true; + int i; + struct video_mode *vmode_db; + + vmode_db = established_timing_db; + for (i = 0, vmode_db = established_timing_db; + i < ARRAY_SIZE(established_timing_db); i++) + if ( (vmode->width == vmode_db[i].width) && + (vmode->height == vmode_db[i].height) && + (vmode->refresh_rate == vmode_db[i].refresh_rate ) && + (vmode_db[i].interlaced == PROGRESSIVE) && + (vmode_db[i].supported == true )) + return true; + for (i = 0, vmode_db = standard_timing_db; + i < ARRAY_SIZE(standard_timing_db); i++) + if ( (vmode->width == vmode_db[i].width) && + (vmode->height == vmode_db[i].height) && + (vmode->refresh_rate == vmode_db[i].refresh_rate ) && + (vmode_db[i].interlaced == PROGRESSIVE) && + (vmode_db[i].supported == true )) + return true; for (i = 0, vmode_db = additional_timing_db; i < ARRAY_SIZE(additional_timing_db); i++) if ( (vmode->width == vmode_db[i].width) && (vmode->height == vmode_db[i].height) && (vmode->refresh_rate == vmode_db[i].refresh_rate ) && (vmode_db[i].interlaced == PROGRESSIVE) && - (vmode_db[i].supported == true )) + (vmode_db[i].supported == true )) return true; - return false; + return false; } bool edid_check_sink_type(struct hdmi_info *hdmi) @@ -613,6 +658,12 @@ bool edid_check_sink_type(struct hdmi_info *hdmi) return hdmi->edid_info.hdmi_sink; } +bool edid_check_audio_support(struct hdmi_info *hdmi) +{ + EDID_DBG("%s: ret=%d\n", __func__, hdmi->edid_info.basic_audio); + return hdmi->edid_info.basic_audio; +} + int edid_dump_hex(u8 *src, int src_size, char *output, int output_size) { char line[80]; @@ -633,10 +684,10 @@ int edid_dump_hex(u8 *src, int src_size, char *output, int output_size) } line[line_size - 1] = '\n'; strncpy(output+ n, line, line_size); - if ((n + line_size) > output_size) - break; - else - n += line_size; + if ((n + line_size) > output_size) + break; + else + n += line_size; } return n; @@ -650,22 +701,16 @@ static ssize_t edid_dbg_open(struct inode *inode, struct file *file) } static char hex_buff[2048]; + static ssize_t edid_buffered_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { - int n; - int extensions; - struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; - - extensions = hdmi->edid_buf[0x7e] + 1; - edid_dump_video_modes(hdmi->edid_buf);// fixme: crashed - if (extensions == 1) - edid_dump_video_modes(hdmi->edid_buf + 128); - else - edid_dump_video_modes(hdmi->edid_buf + 384); - //edid_simple_parsing(hdmi); // FIXME: crashed... - n = edid_dump_hex(hdmi->edid_buf, (hdmi->edid_buf[0x7e] + 1) * 128, - hex_buff, 2048); + int n; + struct hdmi_info *hdmi = (struct hdmi_info*)filp->private_data; + + edid_simple_parsing(hdmi); + n = edid_dump_hex(hdmi->edid_buf, (hdmi->edid_buf[0x7e] + 1) * 128, + hex_buff, 2048); return simple_read_from_buffer(buf, count, ppos, hex_buff, n); } @@ -681,7 +726,7 @@ static struct file_operations edid_debugfs_fops[] = { int edid_debugfs_init(struct hdmi_info *hdmi) { //int ret; - struct dentry *edid_dent; + struct dentry *edid_dent; edid_dent = debugfs_create_dir("edid", hdmi->debug_dir); if (IS_ERR(edid_dent)) diff --git a/drivers/video/msm/hdmi/fb-hdmi.c b/drivers/video/msm/hdmi/fb-hdmi.c index 28a5b525b2a46..fe024718f25a8 100644 --- a/drivers/video/msm/hdmi/fb-hdmi.c +++ b/drivers/video/msm/hdmi/fb-hdmi.c @@ -303,6 +303,8 @@ static int hdmifb_blit(struct fb_info *info, void __user *p) /* Copy the requested blit in case we're mirroring */ if (_hdmi_fb->mirroring) { + int previousValue = _hdmi_fb->doubleBuffering; + memcpy(&_hdmi_fb->mirrorReq, &req, sizeof(struct mdp_blit_req)); /* Default double-buffering off */ @@ -316,13 +318,14 @@ static int hdmifb_blit(struct fb_info *info, void __user *p) { _hdmi_fb->doubleBuffering = 1; } - else - { - // Switch us back to buffer 0 - mdp->dma(mdp, _hdmi_fb->fb->fix.smem_start, _hdmi_fb->fb->var.xres * 2, - _hdmi_fb->fb->var.xres, _hdmi_fb->fb->var.yres, 0, 0, - &_hdmi_fb->dma_callback, _hdmi_fb->panel->interface_type); - } + } + + if (previousValue == 1 && _hdmi_fb->doubleBuffering == 0) + { + // Switch us back to buffer 0 + mdp->dma(mdp, _hdmi_fb->fb->fix.smem_start, _hdmi_fb->fb->var.xres * 2, + _hdmi_fb->fb->var.xres, _hdmi_fb->fb->var.yres, 0, 0, + &_hdmi_fb->dma_callback, _hdmi_fb->panel->interface_type); } } @@ -495,7 +498,16 @@ static int hdmifb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg) break; case HDMI_ENABLE: get_user(val, (unsigned __user *) arg); - ret = hdmifb_pause(p, 0); + if (val == 0x03040601) + { + ret = hdmifb_pause(p, 0); + } + else + { + ret = hdmifb_pause(p, 1); + } + // Always disable mirroring if someone sends an HDMI_ENABLE request + hdmi_fb->mirroring = 0; break; case HDMI_GET_STATE: ret = put_user(test_bit(hdmi_enabled, &hdmi_fb->state), diff --git a/drivers/video/msm/hdmi/include/edid.h b/drivers/video/msm/hdmi/include/edid.h index 1b7e4587e5c6e..4a6f9ac436c52 100644 --- a/drivers/video/msm/hdmi/include/edid.h +++ b/drivers/video/msm/hdmi/include/edid.h @@ -79,4 +79,7 @@ struct edid_info_struct { #define HDMI_SIGNATURE_LEN 0x03 #define CEC_PHYS_ADDR_LEN 0x02 + +#define EDID_BIT(b) (1 << b) + #endif diff --git a/drivers/video/msm/hdmi/include/sil902x.h b/drivers/video/msm/hdmi/include/sil902x.h index 9f27a147580a1..17ab291fcfc20 100644 --- a/drivers/video/msm/hdmi/include/sil902x.h +++ b/drivers/video/msm/hdmi/include/sil902x.h @@ -361,6 +361,7 @@ int edid_dump_hex(u8 *src, int src_size, char *output, int output_size); bool edid_is_video_mode_supported(struct video_mode *vmode); int edid_debugfs_init(struct hdmi_info *hdmi); bool edid_check_sink_type(struct hdmi_info *hdmi); +bool edid_check_audio_support(struct hdmi_info *hdmi); int HotPlugServiceLoop(struct hdmi_info *hdmi); int tpi_prepare(struct hdmi_info *hdmi); diff --git a/drivers/video/msm/hdmi/silicon-image/av_config.c b/drivers/video/msm/hdmi/silicon-image/av_config.c index 2be7d9500a727..22be4b67d3953 100644 --- a/drivers/video/msm/hdmi/silicon-image/av_config.c +++ b/drivers/video/msm/hdmi/silicon-image/av_config.c @@ -90,6 +90,12 @@ int hdmi_active9022(struct i2c_client *client) /* HDMI Output */ ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL, OUTPUT_MODE_MASK, OUTPUT_MODE_HDMI); + } else { + ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL, + OUTPUT_MODE_MASK, OUTPUT_MODE_DVI); + } + + if (edid_check_audio_support(info)) { /* audio configuration */ ret = hdmi_write_byte(client, 0x26, 0x91); ret = hdmi_write_byte(client, 0x25, 0x03); @@ -109,8 +115,6 @@ int hdmi_active9022(struct i2c_client *client) ret = hdmi_write_byte(client, 0xbe, 0); ret = hdmi_write_byte(client, 0x26, 0x81); } else { - ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL, - OUTPUT_MODE_MASK, OUTPUT_MODE_DVI); SetAudioMute(info, AUDIO_MUTE_MUTED); } ret = hdmi_write_byte(client, HDMI_PIXEL_DATA, 0x60); @@ -172,6 +176,12 @@ int hdmi_active9022_dup(struct i2c_client *client) /* HDMI Output */ ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL, OUTPUT_MODE_MASK, OUTPUT_MODE_HDMI); + } else { + ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL, + OUTPUT_MODE_MASK, OUTPUT_MODE_DVI); + } + + if (edid_check_audio_support(info)) { /* audio configuration */ ret = hdmi_write_byte(client, 0x26, 0x91); ret = hdmi_write_byte(client, 0x25, 0x03); @@ -191,8 +201,6 @@ int hdmi_active9022_dup(struct i2c_client *client) ret = hdmi_write_byte(client, 0xbe, 0); ret = hdmi_write_byte(client, 0x26, 0x81); } else { - ReadModifyWriteTPI(info, TPI_SYSTEM_CONTROL, - OUTPUT_MODE_MASK, OUTPUT_MODE_DVI); SetAudioMute(info, AUDIO_MUTE_MUTED); } ret = hdmi_write_byte(client, HDMI_PIXEL_DATA, 0x60); diff --git a/drivers/video/msm/hdmi/silicon-image/tpi.c b/drivers/video/msm/hdmi/silicon-image/tpi.c index c8d3cf3f982c3..36ec47ed9d781 100644 --- a/drivers/video/msm/hdmi/silicon-image/tpi.c +++ b/drivers/video/msm/hdmi/silicon-image/tpi.c @@ -364,7 +364,7 @@ int tpi_read_edid(struct hdmi_info *hdmi) msg.addr = 0x50; msg.flags = I2C_M_RD; - msg.len = 128; + msg.len = 256; msg.buf = hdmi->edid_buf; ret = i2c_transfer(hdmi->client->adapter, &msg, 1); if (ret < 0) { @@ -374,24 +374,7 @@ int tpi_read_edid(struct hdmi_info *hdmi) if (hdmi->edid_buf[0x7e] <= 3) edid_blocks = hdmi->edid_buf[0x7e] ; - dev_info(&hdmi->client->dev, "EDID blocks = %d\n", edid_blocks); - - if (edid_blocks == 0 ) { - goto end_read_edid; - } - // Block-1 - msg.addr = 0x50; - msg.flags = 0; - msg.len = 1; - i2c_buff[0] = 128; - msg.buf = i2c_buff; - ret = i2c_transfer(hdmi->client->adapter, &msg, 1); - - msg.addr = 0x50; - msg.flags = I2C_M_RD; - msg.len = 128; - msg.buf = &hdmi->edid_buf[128]; - ret = i2c_transfer(hdmi->client->adapter, &msg, 1); + dev_info(&hdmi->client->dev, "EDID blocks = %d\n", edid_blocks); } if (edid_blocks > 1) { @@ -460,7 +443,7 @@ void HotPlugService (struct hdmi_info *hdmi) EnableTMDS(hdmi); } - if (edid_check_sink_type(hdmi)) + if (edid_check_audio_support(hdmi)) avc_set_basic_audio(hdmi); else SetAudioMute(hdmi, AUDIO_MUTE_MUTED); @@ -565,8 +548,10 @@ void tpi_cable_conn(struct hdmi_info *hdmi) tpi_read_edid(hdmi); memset(edid_hex_buff, 0, 2048); - edid_dump_hex(hdmi->edid_buf, 256, edid_hex_buff, 2048); - printk("EDID data:\n%s\n=====", edid_hex_buff); + edid_dump_hex(hdmi->edid_buf, 128, edid_hex_buff, 2048); + printk("Base EDID:\n%s\n=====\n", edid_hex_buff); + edid_dump_hex(hdmi->edid_buf + 128, 256, edid_hex_buff, 2048); + printk("Extended EDID blocks:\n%s\n=====\n", edid_hex_buff); /* select output mode (HDMI/DVI) according to sink capabilty */ if (edid_check_sink_type(hdmi)) ReadModifyWriteTPI(hdmi, TPI_SYSTEM_CONTROL, OUTPUT_MODE_MASK, OUTPUT_MODE_HDMI); @@ -587,11 +572,14 @@ void tpi_cable_disconn(struct hdmi_info *hdmi, bool into_d3) { HDMI_DBG("%s, into_d3=%d\n", __func__, into_d3); +#if 0 hdmi->cable_connected = false; dsRxPoweredUp = false; edidDataValid = false; hdcp_off(hdmi); DisableTMDS(hdmi); +#endif + #if 1 /* wait for debounce */ msleep(20); @@ -601,7 +589,9 @@ void tpi_cable_disconn(struct hdmi_info *hdmi, bool into_d3) if (!(reg & 0x0c)) tpi_clear_pending_event(hdmi); #endif - if (into_d3) { + +#if 0 + if (into_d3) { mutex_lock(&hdmi->lock); HDMI_DBG("%s, playing=%d\n", __func__, hdmi->user_playing); if (false == hdmi->user_playing) @@ -616,6 +606,7 @@ void tpi_cable_disconn(struct hdmi_info *hdmi, bool into_d3) HDMI_DBG("Cable unplugged.\n"); switch_send_event(BIT_HDMI_CABLE, 0); #endif +#endif } static char *str_debug_interrupt[] = { @@ -674,13 +665,13 @@ static void tpi_poll(struct hdmi_info *hdmi) ReadSetWriteTPI(hdmi, TPI_INTERRUPT_ENABLE_REG, HOT_PLUG_EVENT); // Repeat this loop while cable is bouncing: do { - //DLOG(DBG_POLLING, "TPI: Interrupt status image - 2= %02x\n", status); + DLOG(DBG_POLLING, "TPI: Interrupt status image - 2= %02x\n", status); hdmi_write_byte(hdmi->client, TPI_INTERRUPT_STATUS_REG, HOT_PLUG_EVENT); // Delay for metastability protection and to help filter out connection bouncing mdelay(T_HPD_DELAY); // Read Interrupt status register status = hdmi_read(hdmi->client, TPI_INTERRUPT_STATUS_REG); - //DLOG(DBG_POLLING, "TPI: Interrupt status image - 3= %02x\n", status); + DLOG(DBG_POLLING, "TPI: Interrupt status image - 3= %02x\n", status); if (!retry--) { HDMI_DBG("%s: retry failed\n", __func__); break; @@ -690,11 +681,11 @@ static void tpi_poll(struct hdmi_info *hdmi) DLOG(DBG_POLLING, "int status: %02x, after debouncing: %02x\n", orig_status, status); - //DLOG(DBG_POLLING, "TPI->hdmiCableConnected = %d\n", hdmi->cable_connected); + DLOG(DBG_POLLING, "TPI->hdmiCableConnected = %d\n", hdmi->cable_connected); if (((status & HOT_PLUG_STATE) >> 2) != hdmi->cable_connected) { DLOG(DBG_POLLING, "cable status changed: from %d to %d\n", hdmi->cable_connected, !!(status & HOT_PLUG_STATE)); - //DLOG(DBG_POLLING, "TPI-> CONDITION\n"); + DLOG(DBG_POLLING, "TPI-> CONDITION\n"); if (hdmi->cable_connected == true) tpi_cable_disconn(hdmi, status & 0x8 ? false : true); else { @@ -705,9 +696,10 @@ static void tpi_poll(struct hdmi_info *hdmi) mutex_unlock(&hdmi->polling_lock); return; } - } else if ( false == hdmi->cable_connected) + } else if ( false == hdmi->cable_connected) { /* only occur while booting without cable attached. */ tpi_cable_disconn(hdmi, true); + } } // Check rx power From d94e3ff68f5a4170baae4ed434c18d84fde1e5f0 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Sat, 18 Jun 2011 12:21:20 -0400 Subject: [PATCH 1950/2556] supersonic: Fix serial number and BT MAC --- arch/arm/mach-msm/board-supersonic.c | 41 ++++++++++++++++------------ 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index 537241638db40..d35071751d615 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -485,7 +485,7 @@ XB : GPIO33 = 0 -> USB */ -static int __init board_serialno_setup(char *serialno) +static int __init supersonic_board_serialno_setup(char *serialno) { #ifdef CONFIG_USB_ANDROID_RNDIS int i; @@ -502,9 +502,10 @@ static int __init board_serialno_setup(char *serialno) #endif android_usb_pdata.serial_number = serialno; + msm_hsusb_pdata.serial_number = serialno; return 1; } -__setup("androidboot.serialno=", board_serialno_setup); +__setup("androidboot.serialno=", supersonic_board_serialno_setup); static struct platform_device supersonic_rfkill = { @@ -1119,21 +1120,29 @@ static struct i2c_board_info i2c_devices[] = { }, }; -#ifdef CONFIG_ARCH_QSD8X50 -static char bdaddress[20]; +#define ATAG_BDADDR 0x43294329 +#define ATAG_BDADDR_SIZE 4 +#define BDADDR_STR_SIZE 18 + +static char bdaddr[BDADDR_STR_SIZE]; + +module_param_string(bdaddr, bdaddr, sizeof(bdaddr), 0400); +MODULE_PARM_DESC(bdaddr, "bluetooth address"); + +static int __init parse_tag_bdaddr(const struct tag *tag) +{ + unsigned char *b = (unsigned char *)&tag->u; + + if (tag->hdr.size != ATAG_BDADDR_SIZE) + return -EINVAL; -static void bt_export_bd_address(void) - { - unsigned char cTemp[6]; + snprintf(bdaddr, BDADDR_STR_SIZE, "%02X:%02X:%02X:%02X:%02X:%02X", + b[0], b[1], b[2], b[3], b[4], b[5]); - memcpy(cTemp, get_bt_bd_ram(), 6); - sprintf(bdaddress, "%02x:%02x:%02x:%02x:%02x:%02x", cTemp[0], cTemp[1], cTemp[2], cTemp[3], cTemp[4], cTemp[5]); - printk(KERN_INFO "YoYo--BD_ADDRESS=%s\n", bdaddress); + return 0; } -module_param_string(bdaddress, bdaddress, sizeof(bdaddress), S_IWUSR | S_IRUGO); -MODULE_PARM_DESC(bdaddress, "BT MAC ADDRESS"); -#endif +__tagtable(ATAG_BDADDR, parse_tag_bdaddr); static uint32_t camera_off_gpio_table[] = { /* CAMERA SUSPEND*/ @@ -1559,6 +1568,8 @@ static void __init supersonic_init(void) msm_hw_reset_hook = supersonic_reset; + supersonic_board_serialno_setup(board_serialno()); + OJ_BMA_power(); msm_acpu_clock_init(&supersonic_clock_data); @@ -1568,10 +1579,6 @@ static void __init supersonic_init(void) &msm_device_uart1.dev, 1, MSM_GPIO_TO_INT(SUPERSONIC_GPIO_UART1_RX)); #endif -#ifdef CONFIG_ARCH_QSD8X50 - bt_export_bd_address(); -#endif - /* set the gpu power rail to manual mode so clk en/dis will not * turn off gpu power, and hang it on resume */ supersonic_kgsl_power_rail_mode(0); From 33a63952988c0a54903131355a3adba5ffb7aebf Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Sat, 18 Jun 2011 13:10:23 -0400 Subject: [PATCH 1951/2556] mahimahi: Fix serial number --- arch/arm/mach-msm/board-mahimahi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index b213bff533fb7..ed4cfe46ac212 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -944,7 +944,7 @@ static int __init parse_tag_bdaddr(const struct tag *tag) __tagtable(ATAG_BDADDR, parse_tag_bdaddr); -static int __init board_serialno_setup(char *serialno) +static int __init mahimahi_board_serialno_setup(char *serialno) { #ifdef CONFIG_USB_ANDROID_RNDIS int i; @@ -961,9 +961,10 @@ static int __init board_serialno_setup(char *serialno) #endif android_usb_pdata.serial_number = serialno; + msm_hsusb_pdata.serial_number = serialno; return 1; } -__setup("androidboot.serialno=", board_serialno_setup); +__setup("androidboot.serialno=", mahimahi_board_serialno_setup); static struct msm_acpu_clock_platform_data mahimahi_clock_data = { .acpu_switch_time_us = 20, @@ -1061,6 +1062,8 @@ static void __init mahimahi_init(void) msm_hw_reset_hook = mahimahi_reset; + mahimahi_board_serialno_setup(board_serialno()); + if (is_cdma_version(system_rev)) msm_acpu_clock_init(&mahimahi_cdma_clock_data); else From 73e3d6198c631eb32dcb6b16b67d143ceae7b4b6 Mon Sep 17 00:00:00 2001 From: Giulio Cervera Date: Sat, 18 Jun 2011 15:38:35 -0400 Subject: [PATCH 1952/2556] bravo: Fix serial number --- arch/arm/mach-msm/board-bravo.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/board-bravo.c b/arch/arm/mach-msm/board-bravo.c index 7d0e7314c3a0c..7de13afa7d00b 100644 --- a/arch/arm/mach-msm/board-bravo.c +++ b/arch/arm/mach-msm/board-bravo.c @@ -1109,7 +1109,7 @@ static int __init parse_tag_bdaddr(const struct tag *tag) __tagtable(ATAG_BDADDR, parse_tag_bdaddr); -static int __init board_serialno_setup(char *serialno) +static int __init bravo_board_serialno_setup(char *serialno) { #ifdef CONFIG_USB_ANDROID_RNDIS int i; @@ -1126,9 +1126,10 @@ static int __init board_serialno_setup(char *serialno) #endif android_usb_pdata.serial_number = serialno; + msm_hsusb_pdata.serial_number = serialno; return 1; } -__setup("androidboot.serialno=", board_serialno_setup); +__setup("androidboot.serialno=", bravo_board_serialno_setup); static struct msm_acpu_clock_platform_data bravo_clock_data = { .acpu_switch_time_us = 20, @@ -1186,6 +1187,8 @@ static void __init bravo_init(void) msm_hw_reset_hook = bravo_reset; + bravo_board_serialno_setup(board_serialno()); + if (is_cdma_version(system_rev)) msm_acpu_clock_init(&bravo_cdma_clock_data); else From 5aca43806adf192c973b88baafcbb40cc9f2db65 Mon Sep 17 00:00:00 2001 From: "andrew.boren" Date: Fri, 25 Nov 2011 21:29:16 -0700 Subject: [PATCH 1953/2556] Touchscreen fix for atmel toushcreens on ICS. --- drivers/input/touchscreen/atmel.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/input/touchscreen/atmel.c b/drivers/input/touchscreen/atmel.c index a7c03097f761f..adb3183b31c8a 100644 --- a/drivers/input/touchscreen/atmel.c +++ b/drivers/input/touchscreen/atmel.c @@ -764,13 +764,17 @@ static void msg_process_noisesuppression(struct atmel_ts_data *ts, uint8_t *data static void compatible_input_report(struct input_dev *idev, struct atmel_finger_data *fdata, uint8_t press, uint8_t last) { - if (!press) + if (!press) { + input_report_key(idev, BTN_TOUCH, 0); input_report_abs(idev, ABS_MT_TOUCH_MAJOR, 0); + } else { + input_report_key(idev, BTN_TOUCH, 1); input_report_abs(idev, ABS_MT_TOUCH_MAJOR, fdata->z); input_report_abs(idev, ABS_MT_WIDTH_MAJOR, fdata->w); input_report_abs(idev, ABS_MT_POSITION_X, fdata->x); input_report_abs(idev, ABS_MT_POSITION_Y, fdata->y); + input_report_abs(idev, ABS_MT_PRESSURE, fdata->z); input_mt_sync(idev); } } @@ -780,12 +784,16 @@ static void htc_input_report(struct input_dev *idev, struct atmel_finger_data *fdata, uint8_t press, uint8_t last) { if (!press) { + input_report_key(idev, BTN_TOUCH, 0); input_report_abs(idev, ABS_MT_AMPLITUDE, 0); + input_report_abs(idev, ABS_MT_PRESSURE, 0); input_report_abs(idev, ABS_MT_POSITION, BIT(31)); } else { + input_report_key(idev, BTN_TOUCH, 1); input_report_abs(idev, ABS_MT_AMPLITUDE, fdata->z << 16 | fdata->w); input_report_abs(idev, ABS_MT_POSITION, (last ? BIT(31) : 0) | fdata->x << 16 | fdata->y); + input_report_abs(idev, ABS_MT_PRESSURE, fdata->z); } } #endif @@ -1470,7 +1478,7 @@ static int atmel_ts_probe(struct i2c_client *client, } ts->input_dev->name = "atmel-touchscreen"; set_bit(EV_SYN, ts->input_dev->evbit); - set_bit(EV_KEY, ts->input_dev->evbit); + set_bit(EV_KEY, ts->input_dev->keybit); set_bit(BTN_TOUCH, ts->input_dev->keybit); set_bit(BTN_2, ts->input_dev->keybit); set_bit(EV_ABS, ts->input_dev->evbit); @@ -1479,16 +1487,15 @@ static int atmel_ts_probe(struct i2c_client *client, ts->abs_x_min, ts->abs_x_max, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, ts->abs_y_min, ts->abs_y_max, 0, 0); - input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, - ts->abs_pressure_min, ts->abs_pressure_max, - 0, 0); - input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, - ts->abs_width_min, ts->abs_width_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 30, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0); #ifndef CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT input_set_abs_params(ts->input_dev, ABS_MT_AMPLITUDE, 0, ((ts->abs_pressure_max << 16) | ts->abs_width_max), 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_POSITION, 0, (BIT(31) | (ts->abs_x_max << 16) | ts->abs_y_max), 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0); #endif From d9aa2e309e69295d9f98ae27b530a0b878bc697c Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 20 Jun 2011 12:41:46 -0700 Subject: [PATCH 1954/2556] netfilter: add xt_qtaguid matching module This module allows tracking stats at the socket level for given UIDs. It replaces xt_owner. If the --uid-owner is not specified, it will just count stats based on who the skb belongs to. This will even happen on incoming skbs as it looks into the skb via xt_socket magic to see who owns it. If an skb is lost, it will be assigned to uid=0. To control what sockets of what UIDs are tagged by what, one uses: echo t $sock_fd $accounting_tag $the_billed_uid \ > /proc/net/xt_qtaguid/ctrl So whenever an skb belongs to a sock_fd, it will be accounted against $the_billed_uid and matching stats will show up under the uid with the given $accounting_tag. Because the number of allocations for the stats structs is not that big: ~500 apps * 32 per app we'll just do it atomic. This avoids walking lists many times, and the fancy worker thread handling. Slabs will grow when needed later. It use netdevice and inetaddr notifications instead of hooks in the core dev code to track when a device comes and goes. This removes the need for exposed iface_stat.h. Put procfs dirs in /proc/net/xt_qtaguid/ ctrl stats iface_stat//... The uid stats are obtainable in ./stats. Change-Id: I01af4fd91c8de651668d3decb76d9bdc1e343919 Signed-off-by: JP Abgrall --- include/linux/netfilter/xt_qtaguid.h | 13 + net/netfilter/Kconfig | 18 + net/netfilter/Makefile | 1 + net/netfilter/xt_qtaguid.c | 1221 ++++++++++++++++++++++++++ 4 files changed, 1253 insertions(+) create mode 100644 include/linux/netfilter/xt_qtaguid.h create mode 100644 net/netfilter/xt_qtaguid.c diff --git a/include/linux/netfilter/xt_qtaguid.h b/include/linux/netfilter/xt_qtaguid.h new file mode 100644 index 0000000000000..ca60fbdec2f3b --- /dev/null +++ b/include/linux/netfilter/xt_qtaguid.h @@ -0,0 +1,13 @@ +#ifndef _XT_QTAGUID_MATCH_H +#define _XT_QTAGUID_MATCH_H + +/* For now we just replace the xt_owner. + * FIXME: make iptables aware of qtaguid. */ +#include + +#define XT_QTAGUID_UID XT_OWNER_UID +#define XT_QTAGUID_GID XT_OWNER_GID +#define XT_QTAGUID_SOCKET XT_OWNER_SOCKET +#define xt_qtaguid_match_info xt_owner_match_info + +#endif /* _XT_QTAGUID_MATCH_H */ diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 1534f2b44cafb..c67f793ae382e 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -830,6 +830,8 @@ config NETFILTER_XT_MATCH_OWNER based on who created the socket: the user or group. It is also possible to check whether a socket actually exists. + Conflicts with '"quota, tag, uid" match' + config NETFILTER_XT_MATCH_POLICY tristate 'IPsec "policy" match support' depends on XFRM @@ -863,6 +865,22 @@ config NETFILTER_XT_MATCH_PKTTYPE To compile it as a module, choose M here. If unsure, say N. +config NETFILTER_XT_MATCH_QTAGUID + bool '"quota, tag, owner" match and stats support' + depends on NETFILTER_XT_MATCH_SOCKET + depends on NETFILTER_XT_MATCH_OWNER=n + help + This option replaces the `owner' match. In addition to matching + on uid, it keeps stats based on a tag assigned to a socket. + The full tag is comprised of a UID and an accounting tag. + The tags are assignable to sockets from user space (e.g. a download + manager can assign the socket to another UID for accounting). + Stats and control are done via /proc/net/xt_qtaguid/. + It replaces owner as it takes the same arguments, but should + really be recognized by the iptables tool. + + If unsure, say `N'. + config NETFILTER_XT_MATCH_QUOTA tristate '"quota" match support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 441050f31111a..f111be8a0243d 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_OWNER) += xt_owner.o obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o +obj-$(CONFIG_NETFILTER_XT_MATCH_QTAGUID) += xt_qtaguid.o obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c new file mode 100644 index 0000000000000..a764552cc3871 --- /dev/null +++ b/net/netfilter/xt_qtaguid.c @@ -0,0 +1,1221 @@ +/* + * Kernel iptables module to track stats for packets based on user tags. + * + * (C) 2011 Google, Inc + * + * 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. + */ + +/* TODO: support ipv6 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +/*---------------------------------------------------------------------------*/ +/* + * Tags: + * + * They represent what the data usage counters will be tracked against. + * By default a tag is just based on the UID. + * The UID is used as the base for policying, and can not be ignored. + * So a tag will always at least represent a UID (uid_tag). + * + * A tag can be augmented with an "accounting tag" which is associated + * with a UID. + * User space can set the acct_tag portion of the tag which is then used + * with sockets: all data belong to that socket will be counted against the + * tag. The policing is then based on the tag's uid_tag portion, + * and stats are collected for the acct_tag portion seperately. + * + * There could be + * a: {acct_tag=1, uid_tag=10003} + * b: {acct_tag=2, uid_tag=10003} + * c: {acct_tag=3, uid_tag=10003} + * d: {acct_tag=0, uid_tag=10003} + * (a, b, and c represent tags associated with specific sockets. + * d is for the totals for that uid, including all untagged traffic. + * Typically d is used with policing/quota rules. + * + * We want tag_t big enough to distinguish uid_t and acct_tag. + * It might become a struct if needed. + * Nothing should be using it as an int. + */ +typedef uint64_t tag_t; /* Only used via accessors */ + +static const char *iface_stat_procdirname = "iface_stat"; +static struct proc_dir_entry *iface_stat_procdir; + +enum ifs_tx_rx { + IFS_TX, + IFS_RX, + IFS_MAX_DIRECTIONS +}; + +/* For now, TCP, UDP, the rest */ +enum ifs_proto { + IFS_TCP, + IFS_UDP, + IFS_PROTO_OTHER, + IFS_MAX_PROTOS +}; + +struct byte_packet_counters { + uint64_t bytes; + uint64_t packets; +}; + +struct data_counters { + struct byte_packet_counters bpc[IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS]; +}; + +struct tag_stat { + struct rb_node node; + tag_t tag; + + struct data_counters counters; + /* If this tag is acct_tag based, we need to count against the + * matching parent uid_tag. */ + struct data_counters *parent_counters; + struct proc_dir_entry *proc_ptr; +}; + +static LIST_HEAD(iface_stat_list); +static DEFINE_SPINLOCK(iface_stat_list_lock); + +struct iface_stat { + struct list_head list; + char *ifname; + uint64_t rx_bytes; + uint64_t rx_packets; + uint64_t tx_bytes; + uint64_t tx_packets; + bool active; + struct proc_dir_entry *proc_ptr; + + struct rb_root tag_stat_tree; + spinlock_t tag_stat_list_lock; +}; + + +static struct rb_root sock_tag_tree = RB_ROOT; +static DEFINE_SPINLOCK(sock_tag_list_lock); + +/* + * Track tag that this socket is transferring data for, and not necesseraly + * the uid that owns the socket. + * This is the tag against which tag_stat.counters will be billed. + */ +struct sock_tag { + struct rb_node node; + struct sock *sk; + tag_t tag; +}; + +static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par); + +/*----------------------------------------------*/ +static inline int tag_compare(tag_t t1, tag_t t2) +{ + return t1 < t2 ? -1 : t1 == t2 ? 0 : 1; +} + + +static inline tag_t combine_atag_with_uid(tag_t acct_tag, uid_t uid) +{ + return acct_tag | uid; +} +static inline tag_t make_tag_from_uid(uid_t uid) +{ + return uid; +} +static inline uid_t get_uid_from_tag(tag_t tag) +{ + return tag & 0xFFFFFFFFULL; +} +static inline tag_t get_utag_from_tag(tag_t tag) +{ + return tag & 0xFFFFFFFFULL; +} +static inline tag_t get_atag_from_tag(tag_t tag) +{ + return tag & ~0xFFFFFFFFULL; +} + +static inline bool valid_atag(tag_t tag) +{ + return !(tag & 0xFFFFFFFFULL); +} + +static inline void dc_add_byte_packets(struct data_counters *counters, + enum ifs_tx_rx direction, + enum ifs_proto ifs_proto, + int bytes, + int packets) +{ + counters->bpc[direction][ifs_proto].bytes += bytes; + counters->bpc[direction][ifs_proto].packets += packets; +} + +static inline uint64_t dc_sum_bytes(struct data_counters *counters, + enum ifs_tx_rx direction) +{ + return counters->bpc[direction][IFS_TCP].bytes + + counters->bpc[direction][IFS_UDP].bytes + + counters->bpc[direction][IFS_PROTO_OTHER].bytes; +} + +static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct tag_stat *data = rb_entry(node, struct tag_stat, node); + int result = tag_compare(tag, data->tag); + pr_debug("qtaguid: tag_stat_tree_search(): tag=0x%llx" + " (uid=%d)\n", + data->tag, + get_uid_from_tag(data->tag)); + + if (result < 0) + node = node->rb_left; + else if (result > 0) + node = node->rb_right; + else + return data; + } + return NULL; +} + +static void tag_stat_tree_insert(struct tag_stat *data, struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct tag_stat *this = rb_entry(*new, struct tag_stat, + node); + int result = tag_compare(data->tag, this->tag); + pr_debug("qtaguid: tag_stat_tree_insert(): tag=0x%llx" + " (uid=%d)\n", + this->tag, + get_uid_from_tag(this->tag)); + parent = *new; + if (result < 0) + new = &((*new)->rb_left); + else if (result > 0) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); +} + +static struct sock_tag *sock_tag_tree_search(struct rb_root *root, + const struct sock *sk) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct sock_tag *data = rb_entry(node, struct sock_tag, node); + ptrdiff_t result = sk - data->sk; + if (result < 0) + node = node->rb_left; + else if (result > 0) + node = node->rb_right; + else + return data; + } + return NULL; +} + +static void sock_tag_tree_insert(struct sock_tag *data, struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct sock_tag *this = rb_entry(*new, struct sock_tag, node); + ptrdiff_t result = data->sk - this->sk; + parent = *new; + if (result < 0) + new = &((*new)->rb_left); + else if (result > 0) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); +} + +static int read_proc_u64(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + uint64_t value; + char *p = page; + uint64_t *iface_entry = data; + if (!data) + return 0; + + value = *iface_entry; + p += sprintf(p, "%llu\n", value); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +static int read_proc_bool(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + bool value; + char *p = page; + bool *bool_entry = data; + if (!data) + return 0; + + value = *bool_entry; + p += sprintf(p, "%u\n", value); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +/* Find the entry for tracking the specified interface. */ +static struct iface_stat *get_iface_stat(const char *ifname) +{ + unsigned long flags; + struct iface_stat *iface_entry; + if (!ifname) + return NULL; + + spin_lock_irqsave(&iface_stat_list_lock, flags); + list_for_each_entry(iface_entry, &iface_stat_list, list) { + if (!strcmp(iface_entry->ifname, ifname)) + goto done; + } + iface_entry = NULL; +done: + spin_unlock_irqrestore(&iface_stat_list_lock, flags); + return iface_entry; +} + +/* + * Create a new entry for tracking the specified interface. + * Do nothing if the entry already exists. + * Called when an interface is configured with a valid IP address. + */ +void iface_stat_create(const struct net_device *net_dev) +{ + struct in_device *in_dev; + unsigned long flags; + struct iface_stat *new_iface; + struct proc_dir_entry *proc_entry; + const char *ifname; + struct iface_stat *entry; + __be32 ipaddr = 0; + struct in_ifaddr *ifa = NULL; + + ASSERT_RTNL(); /* No need for separate locking */ + + pr_debug("iface_stat: create(): netdev=%p->name=%s\n", + net_dev, net_dev ? net_dev->name : ""); + if (!net_dev) { + pr_err("iface_stat: create(): no net dev!\n"); + return; + } + + in_dev = __in_dev_get_rtnl(net_dev); + if (!in_dev) { + pr_err("iface_stat: create(): no inet dev!\n"); + return; + } + + pr_debug("iface_stat: create(): in_dev=%p\n", in_dev); + ifname = net_dev->name; + pr_debug("iface_stat: create(): ifname=%p\n", ifname); + for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { + pr_debug("iface_stat: create(): for(): ifa=%p ifname=%p\n", + ifa, ifname); + pr_debug("iface_stat: create(): ifname=%s ifa_label=%s\n", + ifname, ifa->ifa_label ? ifa->ifa_label : "(null)"); + if (ifa->ifa_label && !strcmp(ifname, ifa->ifa_label)) + break; + } + + if (ifa) { + ipaddr = ifa->ifa_local; + } else { + pr_err("iface_stat: create(): dev %s has no matching IP\n", + ifname); + return; + } + + entry = get_iface_stat(net_dev->name); + if (entry != NULL) { + pr_debug("iface_stat: create(): dev %s entry=%p\n", ifname, + entry); + if (ipv4_is_loopback(ipaddr)) { + entry->active = false; + pr_debug("iface_stat: create(): disable tracking of " + "loopback dev %s\n", ifname); + } else { + entry->active = true; + pr_debug("iface_stat: create(): enable tracking of " + "dev %s with ip=%pI4\n", + ifname, &ipaddr); + } + return; + } else if (ipv4_is_loopback(ipaddr)) { + pr_debug("iface_stat: create(): ignore loopback dev %s" + " ip=%pI4\n", ifname, &ipaddr); + return; + } + + new_iface = kmalloc(sizeof(*new_iface), GFP_KERNEL); + if (new_iface == NULL) { + pr_err("iface_stat: create(): failed to alloc iface_stat\n"); + return; + } + memset(new_iface, 0, sizeof(*new_iface)); + new_iface->ifname = kstrdup(ifname, GFP_KERNEL); + if (new_iface->ifname == NULL) { + pr_err("iface_stat: create(): failed to alloc ifname\n"); + kfree(new_iface); + return; + } + spin_lock_init(&new_iface->tag_stat_list_lock); + + new_iface->active = true; + + new_iface->tag_stat_tree = RB_ROOT; + spin_lock_irqsave(&iface_stat_list_lock, flags); + list_add(&new_iface->list, &iface_stat_list); + spin_unlock_irqrestore(&iface_stat_list_lock, flags); + + proc_entry = proc_mkdir(ifname, iface_stat_procdir); + new_iface->proc_ptr = proc_entry; + + /* TODO: make root access only */ + create_proc_read_entry("tx_bytes", S_IRUGO, proc_entry, + read_proc_u64, &new_iface->tx_bytes); + create_proc_read_entry("rx_bytes", S_IRUGO, proc_entry, + read_proc_u64, &new_iface->rx_bytes); + create_proc_read_entry("tx_packets", S_IRUGO, proc_entry, + read_proc_u64, &new_iface->tx_packets); + create_proc_read_entry("rx_packets", S_IRUGO, proc_entry, + read_proc_u64, &new_iface->rx_packets); + create_proc_read_entry("active", S_IRUGO, proc_entry, + read_proc_bool, &new_iface->active); + + pr_debug("iface_stat: create(): done entry=%p dev=%s ip=%pI4\n", + new_iface, ifname, &ipaddr); +} + +static struct sock_tag *get_sock_stat_nl(const struct sock *sk) +{ + pr_debug("xt_qtaguid: get_sock_stat_nl(sk=%p)\n", sk); + return sock_tag_tree_search(&sock_tag_tree, sk); +} + +static struct sock_tag *get_sock_stat(const struct sock *sk) +{ + unsigned long flags; + struct sock_tag *sock_tag_entry; + pr_debug("xt_qtaguid: get_sock_stat(sk=%p)\n", sk); + if (!sk) + return NULL; + spin_lock_irqsave(&sock_tag_list_lock, flags); + sock_tag_entry = get_sock_stat_nl(sk); + spin_unlock_irqrestore(&sock_tag_list_lock, flags); + return sock_tag_entry; +} + +static void +data_counters_update(struct data_counters *dc, enum ifs_tx_rx direction, + int proto, int bytes) +{ + switch (proto) { + case IPPROTO_TCP: + dc_add_byte_packets(dc, direction, IFS_TCP, bytes, 1); + break; + case IPPROTO_UDP: + dc_add_byte_packets(dc, direction, IFS_UDP, bytes, 1); + break; + case IPPROTO_IP: + default: + dc_add_byte_packets(dc, direction, IFS_PROTO_OTHER, bytes, 1); + break; + } +} + + +/* + * Update stats for the specified interface. Do nothing if the entry + * does not exist (when a device was never configured with an IP address). + * Called when an device is being unregistered. + */ +void iface_stat_update(struct net_device *dev) +{ + struct rtnl_link_stats64 dev_stats, *stats; + struct iface_stat *entry; + stats = dev_get_stats(dev, &dev_stats); + ASSERT_RTNL(); + + entry = get_iface_stat(dev->name); + if (entry == NULL) { + pr_debug("iface_stat: dev %s monitor not found\n", dev->name); + return; + } + if (entry->active) { + entry->tx_bytes += stats->tx_bytes; + entry->tx_packets += stats->tx_packets; + entry->rx_bytes += stats->rx_bytes; + entry->rx_packets += stats->rx_packets; + entry->active = false; + pr_debug("iface_stat: Updating stats for " + "dev %s which went down\n", dev->name); + } else { + pr_debug("iface_stat: Did not update stats for " + "dev %s which went down\n", dev->name); + } +} + + +static void tag_stat_update(struct tag_stat *tag_entry, + enum ifs_tx_rx direction, int proto, int bytes) +{ + pr_debug("xt_qtaguid: tag_stat_update(tag=0x%llx (uid=%d) dir=%d " + "proto=%d bytes=%d)\n", + tag_entry->tag, get_uid_from_tag(tag_entry->tag), direction, + proto, bytes); + data_counters_update(&tag_entry->counters, direction, proto, bytes); + if (tag_entry->parent_counters) + data_counters_update(tag_entry->parent_counters, direction, + proto, bytes); +} + + +/* Create a new entry for tracking the specified {acct_tag,uid_tag} within + * the interface. + * iface_entry->tag_stat_list_lock should be held. */ +static struct tag_stat *create_if_tag_stat(struct iface_stat *iface_entry, + tag_t tag) +{ + struct tag_stat *new_tag_stat_entry = NULL; + pr_debug("iface_stat: create_if_tag_stat(): ife=%p tag=0x%llx" + " (uid=%d)\n", + iface_entry, tag, get_uid_from_tag(tag)); + new_tag_stat_entry = kmalloc(sizeof(*new_tag_stat_entry), GFP_ATOMIC); + if (!new_tag_stat_entry) { + pr_err("iface_stat: failed to alloc new tag entry\n"); + goto done; + } + memset(new_tag_stat_entry, 0, sizeof(*new_tag_stat_entry)); + new_tag_stat_entry->tag = tag; + tag_stat_tree_insert(new_tag_stat_entry, &iface_entry->tag_stat_tree); +done: + return new_tag_stat_entry; +} + +static struct iface_stat *get_iface_entry(const char *ifname) +{ + struct iface_stat *iface_entry; + unsigned long flags; + + /* Find the entry for tracking the specified tag within the interface */ + if (ifname == NULL) { + pr_info("iface_stat: NULL device name\n"); + return NULL; + } + + + /* Iterate over interfaces */ + spin_lock_irqsave(&iface_stat_list_lock, flags); + list_for_each_entry(iface_entry, &iface_stat_list, list) { + if (!strcmp(ifname, iface_entry->ifname)) + goto done; + } + iface_entry = NULL; +done: + spin_unlock_irqrestore(&iface_stat_list_lock, flags); + return iface_entry; +} + +static void if_tag_stat_update(const char *ifname, uid_t uid, + const struct sock *sk, enum ifs_tx_rx direction, + int proto, int bytes) +{ + struct tag_stat *tag_stat_entry; + tag_t tag, acct_tag; + tag_t uid_tag; + struct data_counters *uid_tag_counters; + struct sock_tag *sock_tag_entry; + struct iface_stat *iface_entry; + unsigned long flags; + struct tag_stat *new_tag_stat; + pr_debug("xt_qtaguid: if_tag_stat_update(ifname=%s " + "uid=%d sk=%p dir=%d proto=%d bytes=%d)\n", + ifname, uid, sk, direction, proto, bytes); + + + iface_entry = get_iface_entry(ifname); + if (!iface_entry) { + pr_err("iface_stat: interface %s not found\n", ifname); + return; + } + /* else { If the iface_entry becomes inactive, it is still ok + * to process the data. } */ + + pr_debug("iface_stat: stat_update() got entry=%p\n", iface_entry); + + /* Look for a tagged sock. + * It will have an acct_uid. */ + sock_tag_entry = get_sock_stat(sk); + if (sock_tag_entry) { + tag = sock_tag_entry->tag; + acct_tag = get_atag_from_tag(tag); + uid_tag = get_utag_from_tag(tag); + } else { + uid_tag = make_tag_from_uid(uid); + acct_tag = 0; + tag = combine_atag_with_uid(acct_tag, uid); + } + pr_debug("iface_stat: stat_update(): looking for tag=0x%llx (uid=%d)" + " in ife=%p\n", + tag, get_uid_from_tag(tag), iface_entry); + /* Loop over tag list under this interface for {acct_tag,uid_tag} */ + spin_lock_irqsave(&iface_entry->tag_stat_list_lock, flags); + + tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree, + tag); + if (tag_stat_entry) { + /* Updating the {acct_tag, uid_tag} entry handles both stats: + * {0, uid_tag} will also get updated. */ + tag_stat_update(tag_stat_entry, direction, proto, bytes); + spin_unlock_irqrestore(&iface_entry->tag_stat_list_lock, flags); + return; + } + + /* Loop over tag list under this interface for {0,uid_tag} */ + tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree, + uid_tag); + if (!tag_stat_entry) { + /* Here: the base uid_tag did not exist */ + /* + * No parent counters. So + * - No {0, uid_tag} stats and no {acc_tag, uid_tag} stats. + */ + new_tag_stat = create_if_tag_stat(iface_entry, uid_tag); + uid_tag_counters = &new_tag_stat->counters; + } else { + uid_tag_counters = &tag_stat_entry->counters; + } + + if (acct_tag) { + new_tag_stat = create_if_tag_stat(iface_entry, tag); + new_tag_stat->parent_counters = uid_tag_counters; + } + spin_unlock_irqrestore(&iface_entry->tag_stat_list_lock, flags); + tag_stat_update(new_tag_stat, direction, proto, bytes); +} + +static int iface_netdev_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) { + struct net_device *dev = ptr; + + pr_debug("iface_stat: netdev_event(): ev=0x%lx netdev=%p->name=%s\n", + event, dev, dev ? dev->name : ""); + + switch (event) { + case NETDEV_UP: + case NETDEV_REBOOT: + case NETDEV_CHANGE: + case NETDEV_REGISTER: /* Most likely no IP */ + case NETDEV_CHANGEADDR: /* MAC addr change */ + case NETDEV_CHANGENAME: + case NETDEV_FEAT_CHANGE: /* Might be usefull when cell type changes */ + iface_stat_create(dev); + break; + case NETDEV_UNREGISTER: + iface_stat_update(dev); + break; + } + return NOTIFY_DONE; +} + +static int iface_inetaddr_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) { + + struct in_ifaddr *ifa = ptr; + struct in_device *in_dev = ifa->ifa_dev; + struct net_device *dev = in_dev->dev; + + pr_debug("iface_stat: inetaddr_event(): ev=0x%lx netdev=%p->name=%s\n", + event, dev, dev ? dev->name : ""); + + switch (event) { + case NETDEV_UP: + iface_stat_create(dev); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block iface_netdev_notifier_blk = { + .notifier_call = iface_netdev_event_handler, +}; + +static struct notifier_block iface_inetaddr_notifier_blk = { + .notifier_call = iface_inetaddr_event_handler, +}; + +static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) +{ + int err; + + iface_stat_procdir = proc_mkdir(iface_stat_procdirname, parent_procdir); + if (!iface_stat_procdir) { + pr_err("iface_stat: failed to create proc entry\n"); + err = -1; + goto err; + } + err = register_netdevice_notifier(&iface_netdev_notifier_blk); + if (err) { + pr_err("iface_stat: failed to register dev event handler\n"); + goto err_unreg_nd; + } + err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); + if (err) { + pr_err("iface_stat: failed to register dev event handler\n"); + goto err_zap_entry; + } + return 0; + +err_unreg_nd: + unregister_netdevice_notifier(&iface_netdev_notifier_blk); +err_zap_entry: + remove_proc_entry(iface_stat_procdirname, parent_procdir); +err: + return err; +} + +static struct sock *qtaguid_find_sk(const struct sk_buff *skb, + struct xt_action_param *par) +{ + struct sock *sk; + + sk = xt_socket_get4_sk(skb, par); + /* TODO: is this fixed? + * Seems to be issues on the file ptr for TCP+TIME_WAIT SKs. + * http://kerneltrap.org/mailarchive/linux-netdev/2010/10/21/6287959 + */ + if (sk) + pr_debug("xt_qtaguid: %p->sk_proto=%u " + "->sk_state=%d\n", sk, sk->sk_protocol, + sk->sk_state); + return sk; +} + +static void account_for_uid(const struct sk_buff *skb, + const struct sock *alternate_sk, uid_t uid, + struct xt_action_param *par) +{ + const struct net_device *el_dev; + + if (!skb->dev) { + pr_debug("xt_qtaguid[%d]: no skb->dev\n", par->hooknum); + el_dev = par->in ? : par->out; + } else { + const struct net_device *other_dev; + el_dev = skb->dev; + other_dev = par->in ? : par->out; + if (el_dev != other_dev) { + pr_debug("xt_qtaguid[%d]: skb->dev=%p %s vs " + "par->(in/out)=%p %s\n", + par->hooknum, el_dev, el_dev->name, other_dev, + other_dev->name); + } + } + + if (unlikely(!el_dev)) { + pr_info("xt_qtaguid[%d]: no par->in/out?!!\n", par->hooknum); + } else if (unlikely(!el_dev->name)) { + pr_info("xt_qtaguid[%d]: no dev->name?!!\n", par->hooknum); + } else { + pr_debug("xt_qtaguid[%d]: dev name=%s type=%d\n", + par->hooknum, + el_dev->name, + el_dev->type); + + if_tag_stat_update(el_dev->name, uid, + skb->sk ? skb->sk : alternate_sk, + par->in ? IFS_RX : IFS_TX, + ip_hdr(skb)->protocol, skb->len); + } +} + +static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_qtaguid_match_info *info = par->matchinfo; + const struct file *filp; + bool got_sock = false; + struct sock *sk; + uid_t sock_uid; + bool res; + pr_debug("xt_qtaguid[%d]: entered skb=%p par->in=%p/out=%p\n", + par->hooknum, skb, par->in, par->out); + if (skb == NULL) { + res = (info->match ^ info->invert) == 0; + goto ret_res; + } + + sk = skb->sk; + + if (sk == NULL) { + /* A missing sk->sk_socket happens when packets are in-flight + * and the matching socket is already closed and gone. + */ + sk = qtaguid_find_sk(skb, par); + /* If we got the socket from the find_sk(), we will need to put + * it back, as nf_tproxy_get_sock_v4() got it. */ + got_sock = sk; + } + pr_debug("xt_qtaguid[%d]: sk=%p got_sock=%d proto=%d\n", + par->hooknum, sk, got_sock, ip_hdr(skb)->protocol); + if (sk != NULL) { + pr_debug("xt_qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", + par->hooknum, sk, sk->sk_socket, + sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); + filp = sk->sk_socket ? sk->sk_socket->file : NULL; + pr_debug("xt_qtaguid[%d]: filp...uid=%d\n", + par->hooknum, filp ? filp->f_cred->fsuid : -1); + } + + if (sk == NULL || sk->sk_socket == NULL) { + /* Here, the qtaguid_find_sk() using connection tracking + * couldn't find the owner, so for now we just count them + * against the system. */ + /* TODO: unhack how to force just accounting. + * For now we only do iface stats when the uid-owner is not + * requested */ + if (!(info->match & XT_QTAGUID_UID)) + account_for_uid(skb, sk, 0, par); + pr_debug("xt_qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n", + par->hooknum, + sk ? sk->sk_socket : NULL); + res = (info->match ^ info->invert) == 0; + goto put_sock_ret_res; + } else if (info->match & info->invert & XT_QTAGUID_SOCKET) { + res = false; + goto put_sock_ret_res; + } + filp = sk->sk_socket->file; + if (filp == NULL) { + pr_debug("xt_qtaguid[%d]: leaving filp=NULL\n", par->hooknum); + res = ((info->match ^ info->invert) & + (XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0; + goto put_sock_ret_res; + } + sock_uid = filp->f_cred->fsuid; + /* TODO: unhack how to force just accounting. + * For now we only do iface stats when the uid-owner is not requested */ + if (!(info->match & XT_QTAGUID_UID)) + account_for_uid(skb, sk, sock_uid, par); + + /* The following two tests fail the match when: + * id not in range AND no inverted condition requested + * or id in range AND inverted condition requested + * Thus (!a && b) || (a && !b) == a ^ b + */ + if (info->match & XT_QTAGUID_UID) + if ((filp->f_cred->fsuid >= info->uid_min && + filp->f_cred->fsuid <= info->uid_max) ^ + !(info->invert & XT_QTAGUID_UID)) { + pr_debug("xt_qtaguid[%d]: leaving uid not matching\n", + par->hooknum); + res = false; + goto put_sock_ret_res; + } + if (info->match & XT_QTAGUID_GID) + if ((filp->f_cred->fsgid >= info->gid_min && + filp->f_cred->fsgid <= info->gid_max) ^ + !(info->invert & XT_QTAGUID_GID)) { + pr_debug("xt_qtaguid[%d]: leaving gid not matching\n", + par->hooknum); + res = false; + goto put_sock_ret_res; + } + + pr_debug("xt_qtaguid[%d]: leaving matched\n", par->hooknum); + res = true; + +put_sock_ret_res: + if (got_sock) + xt_socket_put_sk(sk); +ret_res: + pr_debug("xt_qtaguid[%d]: left %d\n", par->hooknum, res); + return res; +} + +/* TODO: Use Documentation/filesystems/seq_file.txt? */ +static int qtaguid_ctrl_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *out = page + off; + int len; + unsigned long flags; + uid_t uid; + struct sock_tag *sock_tag_entry; + struct rb_node *node; + pr_debug("xt_qtaguid:proc ctrl page=%p off=%ld count=%d eof=%p\n", + page, off, count, eof); + + *eof = 0; + spin_lock_irqsave(&sock_tag_list_lock, flags); + for (node = rb_first(&sock_tag_tree); + node; + node = rb_next(node)) { + sock_tag_entry = rb_entry(node, struct sock_tag, node); + uid = get_uid_from_tag(sock_tag_entry->tag); + pr_debug("xt_qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%d)\n", + sock_tag_entry->sk, + sock_tag_entry->tag, + uid); + len = snprintf(out, count, "sock=%p tag=0x%llx (uid=%u)\n", + sock_tag_entry->sk, sock_tag_entry->tag, uid); + out += len; + count -= len; + if (!count) { + spin_unlock_irqrestore(&sock_tag_list_lock, flags); + return out - page; + } + } + *eof = 1; + spin_unlock_irqrestore(&sock_tag_list_lock, flags); + return out - page; +} + +static int qtaguid_ctrl_parse(const char *input, int count) +{ + char cmd; + int sock_fd = 0; + uid_t uid = 0; + tag_t acct_tag = 0; + struct socket *el_socket; + int res, argc; + struct sock_tag *sock_tag_entry; + unsigned long flags; + + pr_debug("xt_qtaguid: ctrl(%s): entered\n", input); + /* Unassigned args will get defaulted later. */ + /* TODO: get acct_tag_str, keep a list of available tags for the + * uid, use num as acct_tag. */ + argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid); + pr_debug("xt_qtaguid: ctrl(%s): argc=%d cmd=%c sock_fd=%d " + "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd, + acct_tag, uid); + + /* Collect params for commands */ + switch (cmd) { + case 't': + case 'u': + if (argc < 2) { + res = -EINVAL; + goto err; + } + el_socket = sockfd_lookup(sock_fd, &res); + if (!el_socket) { + pr_info("xt_qtaguid: ctrl(%s): failed to lookup" + " sock_fd=%d err=%d\n", input, sock_fd, res); + goto err; + } + spin_lock_irqsave(&sock_tag_list_lock, flags); + /* TODO: optim: pass in the current_fsuid() to do lookups + * as look ups will always be initiated form the same uid. */ + sock_tag_entry = get_sock_stat_nl(el_socket->sk); + if (!sock_tag_entry) + spin_unlock_irqrestore(&sock_tag_list_lock, flags); + break; + default: + res = -EINVAL; + goto err; + } + + /* Process commands */ + switch (cmd) { + + case 't': + if (argc < 2) { + res = -EINVAL; + goto err_unlock; + } + if (argc < 3) { + acct_tag = 0; + } else if (!valid_atag(acct_tag)) { + res = -EINVAL; + goto err_unlock; + } + if (argc < 4) + uid = current_fsuid(); + if (!sock_tag_entry) { + sock_tag_entry = kmalloc(sizeof(*sock_tag_entry), + GFP_KERNEL); + if (!sock_tag_entry) { + res = -ENOMEM; + goto err; + } + memset(sock_tag_entry, 0, sizeof(*sock_tag_entry)); + sock_tag_entry->sk = el_socket->sk; + /* TODO: check that uid==current_fsuid() except + * for special uid/gid. */ + sock_tag_entry->tag = combine_atag_with_uid(acct_tag, + uid); + spin_lock_irqsave(&sock_tag_list_lock, flags); + sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); + spin_unlock_irqrestore(&sock_tag_list_lock, flags); + } else { + /* Just update the acct_tag portion. */ + uid_t orig_uid = get_uid_from_tag(sock_tag_entry->tag); + sock_tag_entry->tag = combine_atag_with_uid(acct_tag, + orig_uid); + } + pr_debug("xt_qtaguid: tag: sock_tag_entry->sk=%p " + "...->tag=0x%llx (uid=%u)\n", + sock_tag_entry->sk, sock_tag_entry->tag, + get_uid_from_tag(sock_tag_entry->tag)); + break; + + case 'u': + if (!sock_tag_entry) { + res = -EINVAL; + goto err; + } + /* TODO: check that the uid==current_fsuid() + * except for special uid/gid. */ + rb_erase(&sock_tag_entry->node, &sock_tag_tree); + spin_unlock_irqrestore(&sock_tag_list_lock, flags); + kfree(sock_tag_entry); + break; + } + + /* All of the input has been processed */ + res = count; + goto ok; + +err_unlock: + if (!sock_tag_entry) + spin_unlock_irqrestore(&sock_tag_list_lock, flags); +err: +ok: + pr_debug("xt_qtaguid: ctrl(%s): res=%d\n", input, res); + return res; +} + +#define MAX_QTAGUID_CTRL_INPUT_LEN 255 +static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + char input_buf[MAX_QTAGUID_CTRL_INPUT_LEN]; + + if (count >= MAX_QTAGUID_CTRL_INPUT_LEN) + return -EINVAL; + + if (copy_from_user(input_buf, buffer, count)) + return -EFAULT; + + input_buf[count] = '\0'; + return qtaguid_ctrl_parse(input_buf, count); +} + +/* + * Procfs reader to get all tag stats using style "1)" as described in + * fs/proc/generic.c + * Groups all protocols tx/rx bytes. + */ +static int qtaguid_stats_proc_read(char *page, char **num_items_returned, + off_t items_to_skip, int char_count, int *eof, + void *data) +{ + char *outp = page; + int len; + unsigned long flags, flags2; + struct iface_stat *iface_entry; + struct tag_stat *ts_entry; + int item_index = 0; + + /* TODO: make root access only */ + + pr_debug("xt_qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " + "char_count=%d *eof=%d\n", page, *num_items_returned, + items_to_skip, char_count, *eof); + + if (*eof) + return 0; + + if (!items_to_skip) { + /* The idx is there to help debug when things go belly up. */ + len = snprintf(outp, char_count, + "idx iface acct_tag_hex uid_tag_int rx_bytes " + "tx_bytes\n"); + /* Don't advance the outp unless the whole line was printed */ + if (len >= char_count) { + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + } + + spin_lock_irqsave(&iface_stat_list_lock, flags); + list_for_each_entry(iface_entry, &iface_stat_list, list) { + struct rb_node *node; + spin_lock_irqsave(&iface_entry->tag_stat_list_lock, flags2); + for (node = rb_first(&iface_entry->tag_stat_tree); + node; + node = rb_next(node)) { + ts_entry = rb_entry(node, struct tag_stat, node); + if (item_index++ < items_to_skip) + continue; + len = snprintf(outp, char_count, + "%d %s 0x%llx %u %llu %llu\n", + item_index, + iface_entry->ifname, + get_atag_from_tag(ts_entry->tag), + get_uid_from_tag(ts_entry->tag), + dc_sum_bytes(&ts_entry->counters, + IFS_RX), + dc_sum_bytes(&ts_entry->counters, + IFS_TX)); + if (len >= char_count) { + spin_unlock_irqrestore( + &iface_entry->tag_stat_list_lock, + flags2); + spin_unlock_irqrestore( + &iface_stat_list_lock, flags); + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*(int *)num_items_returned)++; + } + spin_unlock_irqrestore(&iface_entry->tag_stat_list_lock, + flags2); + } + spin_unlock_irqrestore(&iface_stat_list_lock, flags); + + *eof = 1; + return outp - page; +} + +/*------------------------------------------*/ +static const char *module_procdirname = "xt_qtaguid"; +static struct proc_dir_entry *xt_qtaguid_procdir; +static struct proc_dir_entry *xt_qtaguid_ctrl_file; +static struct proc_dir_entry *xt_qtaguid_stats_file; + +static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) +{ + int ret; + *res_procdir = proc_mkdir(module_procdirname, init_net.proc_net); + if (!*res_procdir) { + pr_err("xt_qtaguid: failed to create proc/.../xt_qtaguid\n"); + ret = -ENOMEM; + goto no_dir; + } + + xt_qtaguid_ctrl_file = create_proc_entry("ctrl", 0666, + *res_procdir); + if (!xt_qtaguid_ctrl_file) { + pr_err("xt_qtaguid: failed to create xt_qtaguid/ctrl " + " file\n"); + ret = -ENOMEM; + goto no_ctrl_entry; + } + xt_qtaguid_ctrl_file->read_proc = qtaguid_ctrl_proc_read; + xt_qtaguid_ctrl_file->write_proc = qtaguid_ctrl_proc_write; + + xt_qtaguid_stats_file = create_proc_entry("stats", 0666, + *res_procdir); + if (!xt_qtaguid_stats_file) { + pr_err("xt_qtaguid: failed to create xt_qtaguid/stats " + "file\n"); + ret = -ENOMEM; + goto no_stats_entry; + } + /* + * TODO: add extra read_proc for full stats with protocol + * breakout + */ + xt_qtaguid_stats_file->read_proc = qtaguid_stats_proc_read; + /* + * TODO: add support counter hacking + * xt_qtaguid_stats_file->write_proc = qtaguid_stats_proc_write; + */ + return 0; + +no_stats_entry: + remove_proc_entry("ctrl", *res_procdir); +no_ctrl_entry: + remove_proc_entry("xt_qtaguid", NULL); +no_dir: + return ret; +} + +static struct xt_match qtaguid_mt_reg __read_mostly = { + /* + * This module masquerades as the "owner" module so that iptables + * tools can deal with it. + */ + .name = "owner", + .revision = 1, + .family = NFPROTO_UNSPEC, + .match = qtaguid_mt, + .matchsize = sizeof(struct xt_qtaguid_match_info), + .me = THIS_MODULE, +}; + +static int __init qtaguid_mt_init(void) +{ + if (qtaguid_proc_register(&xt_qtaguid_procdir) + || iface_stat_init(xt_qtaguid_procdir) + || xt_register_match(&qtaguid_mt_reg)) + return -1; + return 0; +} + +/* TODO: allow unloading of the module. + * For now stats are permanent. + * Kconfig forces'y/n' and never an 'm'. + */ + +module_init(qtaguid_mt_init); +MODULE_AUTHOR("jpa "); +MODULE_DESCRIPTION("Xtables: socket owner+tag matching and associated stats"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_owner"); +MODULE_ALIAS("ip6t_owner"); +MODULE_ALIAS("ipt_qtaguid"); +MODULE_ALIAS("ip6t_qtaguid"); From 98a0c714e125b2843be23f6d655c2f31e4b8e80c Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 27 Jun 2011 21:03:04 -0700 Subject: [PATCH 1955/2556] nf: qtaguid: workaround xt_socket_get_sk() returning bad SKs. (This is a direct cherry pick from 2.6.39: Id2a9912b) * xt_socket_get_sk() returns invalid sockets when the sk_state is TCP_TIME_WAIT. Added detection of time-wait. * Added more constrained usage: qtaguid insures that xt_socket_get*_sk() is not invoked for unexpected hooks or protocols (but I have not seen those active at the point where the returned sk is bad). Signed-off-by: JP Abgrall Change-Id: Id2a9912bb451a3e59d012fc55bbbd40fbb90693f --- net/netfilter/xt_qtaguid.c | 45 ++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index a764552cc3871..320ad84890e89 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -8,7 +8,7 @@ * published by the Free Software Foundation. */ -/* TODO: support ipv6 */ +/* TODO: support ipv6 for iface_stat */ #include #include @@ -22,6 +22,10 @@ #include #include +/* We only use the xt_socket funcs within a similar context to avoid unexpected + * return values. */ +#define XT_SOCKET_SUPPORTED_HOOKS \ + ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN)) /*---------------------------------------------------------------------------*/ @@ -725,16 +729,39 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, struct xt_action_param *par) { struct sock *sk; + unsigned int hook_mask = (1 << par->hooknum); - sk = xt_socket_get4_sk(skb, par); - /* TODO: is this fixed? - * Seems to be issues on the file ptr for TCP+TIME_WAIT SKs. + pr_debug("xt_qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb, + par->hooknum, par->family); + + /* Let's not abuse the the xt_socket_get*_sk(), or else it will + * return garbage SKs. */ + if (!(hook_mask & XT_SOCKET_SUPPORTED_HOOKS)) + return NULL; + + switch (par->family) { + case NFPROTO_IPV6: + sk = xt_socket_get6_sk(skb, par); + break; + case NFPROTO_IPV4: + sk = xt_socket_get4_sk(skb, par); + break; + default: + return NULL; + } + + /* Seems to be issues on the file ptr for TCP_TIME_WAIT SKs. * http://kerneltrap.org/mailarchive/linux-netdev/2010/10/21/6287959 + * Not fixed in 3.0-r3 :( */ - if (sk) + if (sk) { pr_debug("xt_qtaguid: %p->sk_proto=%u " - "->sk_state=%d\n", sk, sk->sk_protocol, - sk->sk_state); + "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state); + if (sk->sk_state == TCP_TIME_WAIT) { + xt_socket_put_sk(sk); + sk = NULL; + } + } return sk; } @@ -784,8 +811,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) struct sock *sk; uid_t sock_uid; bool res; - pr_debug("xt_qtaguid[%d]: entered skb=%p par->in=%p/out=%p\n", - par->hooknum, skb, par->in, par->out); + pr_debug("xt_qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n", + par->hooknum, skb, par->in, par->out, par->family); if (skb == NULL) { res = (info->match ^ info->invert) == 0; goto ret_res; From 62ce031d9edca2140dede01a2ce705453f5979ef Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 27 Jun 2011 23:31:46 -0700 Subject: [PATCH 1956/2556] nf: qtaguid: make procfs entry for ctrl return correct data. (This is a direct cherry-pick from 2.6.39: I3b925802) Fixed procreader for /proc/net/xt_qtaguid/ctrl: it would just fill the output with the same entry. Simplify the **start handling. Signed-off-by: JP Abgrall Change-Id: I3b92580228f2b57795bb2d0d6197fc95ab6be552 --- net/netfilter/xt_qtaguid.c | 45 ++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 320ad84890e89..3b5ab3ff06152 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -906,42 +906,55 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) return res; } -/* TODO: Use Documentation/filesystems/seq_file.txt? */ -static int qtaguid_ctrl_proc_read(char *page, char **start, off_t off, - int count, int *eof, void *data) +/* + * Procfs reader to get all active socket tags using style "1)" as described in + * fs/proc/generic.c + */ +static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, + off_t items_to_skip, int char_count, int *eof, + void *data) { - char *out = page + off; + char *outp = page; int len; unsigned long flags; uid_t uid; struct sock_tag *sock_tag_entry; struct rb_node *node; - pr_debug("xt_qtaguid:proc ctrl page=%p off=%ld count=%d eof=%p\n", - page, off, count, eof); + int item_index = 0; + + pr_debug("xt_qtaguid:proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", + page, items_to_skip, char_count, *eof); + + if (*eof) + return 0; - *eof = 0; spin_lock_irqsave(&sock_tag_list_lock, flags); for (node = rb_first(&sock_tag_tree); node; node = rb_next(node)) { + if (item_index++ < items_to_skip) + continue; sock_tag_entry = rb_entry(node, struct sock_tag, node); uid = get_uid_from_tag(sock_tag_entry->tag); pr_debug("xt_qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%d)\n", sock_tag_entry->sk, sock_tag_entry->tag, uid); - len = snprintf(out, count, "sock=%p tag=0x%llx (uid=%u)\n", - sock_tag_entry->sk, sock_tag_entry->tag, uid); - out += len; - count -= len; - if (!count) { + len = snprintf(outp, char_count, + "sock=%p tag=0x%llx (uid=%u)\n", + sock_tag_entry->sk, sock_tag_entry->tag, uid); + if (len >= char_count) { spin_unlock_irqrestore(&sock_tag_list_lock, flags); - return out - page; + *outp = '\0'; + return outp - page; } + outp += len; + char_count -= len; + (*num_items_returned)++; } - *eof = 1; spin_unlock_irqrestore(&sock_tag_list_lock, flags); - return out - page; + *eof = 1; + return outp - page; } static int qtaguid_ctrl_parse(const char *input, int count) @@ -1146,7 +1159,7 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, } outp += len; char_count -= len; - (*(int *)num_items_returned)++; + (*num_items_returned)++; } spin_unlock_irqrestore(&iface_entry->tag_stat_list_lock, flags2); From 0620c56d9d6d9d1aa06ab2f67c637baaed42ae87 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Wed, 6 Jul 2011 12:05:49 -0700 Subject: [PATCH 1957/2556] netfilter: qtaguid: fix bad-arg handling when tagging socket When processing args passed to the procfs ctrl, if the tag was invalid it would exit without releasing the spin_lock... Bye bye scheduling. Signed-off-by: JP Abgrall Change-Id: Ic1480ae9d37bba687586094cf6d0274db9c5b28a --- net/netfilter/xt_qtaguid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 3b5ab3ff06152..3cacec07fbf72 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1065,7 +1065,7 @@ static int qtaguid_ctrl_parse(const char *input, int count) goto ok; err_unlock: - if (!sock_tag_entry) + if (sock_tag_entry) spin_unlock_irqrestore(&sock_tag_list_lock, flags); err: ok: From 2669b2796fedda7143e48774ca23da4327657d89 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Wed, 6 Jul 2011 20:09:38 -0700 Subject: [PATCH 1958/2556] netfitler: xt_qtaguid: add another missing spin_unlock. This time the symptom is caused by tagging the same socket twice without untagging it in between. This would cause it to not unlock, and return. Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 3cacec07fbf72..49ed432d793c2 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -997,11 +997,13 @@ static int qtaguid_ctrl_parse(const char *input, int count) sock_tag_entry = get_sock_stat_nl(el_socket->sk); if (!sock_tag_entry) spin_unlock_irqrestore(&sock_tag_list_lock, flags); + /* HERE: The lock is held if there was a matching sock tag entry */ break; default: res = -EINVAL; goto err; } + /* HERE: The lock is held if there was a matching sock tag entry */ /* Process commands */ switch (cmd) { @@ -1009,17 +1011,23 @@ static int qtaguid_ctrl_parse(const char *input, int count) case 't': if (argc < 2) { res = -EINVAL; + /* HERE: The lock is held if there was a matching sock + * tag entry */ goto err_unlock; } if (argc < 3) { acct_tag = 0; } else if (!valid_atag(acct_tag)) { res = -EINVAL; + /* HERE: The lock is held if there was a matching sock + * tag entry */ goto err_unlock; } if (argc < 4) uid = current_fsuid(); if (!sock_tag_entry) { + /* HERE: There is no lock held because there was no + * sock tag entry */ sock_tag_entry = kmalloc(sizeof(*sock_tag_entry), GFP_KERNEL); if (!sock_tag_entry) { @@ -1034,13 +1042,15 @@ static int qtaguid_ctrl_parse(const char *input, int count) uid); spin_lock_irqsave(&sock_tag_list_lock, flags); sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); - spin_unlock_irqrestore(&sock_tag_list_lock, flags); } else { + /* HERE: The lock is held because there is a matching + * sock tag entry */ /* Just update the acct_tag portion. */ uid_t orig_uid = get_uid_from_tag(sock_tag_entry->tag); sock_tag_entry->tag = combine_atag_with_uid(acct_tag, orig_uid); } + spin_unlock_irqrestore(&sock_tag_list_lock, flags); pr_debug("xt_qtaguid: tag: sock_tag_entry->sk=%p " "...->tag=0x%llx (uid=%u)\n", sock_tag_entry->sk, sock_tag_entry->tag, From e2319e2040fb8cf078aafc5ae3765628b1c1569d Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 15 Jul 2011 22:27:28 -0700 Subject: [PATCH 1959/2556] netfilter: qtaguid: add tag delete command, expand stats output. * Add a new ctrl command to delete stored data. d [] The uid will default to the running process's. The accounting tag can be 0, in which case all counters and socket tags associated with the uid will be cleared. * Simplify the ctrl command handling at the expense of duplicate code. This should make it easier to maintain. * /proc/net/xt_qtaguid/stats now returns more stats idx iface acct_tag_hex uid_tag_int {rx,tx}_{bytes,packets} {rx,tx}_{tcp,udp,other}_{bytes,packets} the {rx,tx}_{bytes,packets} are the totals. * re-tagging will now allow changing the uid. Change-Id: I9594621543cefeab557caa3d68a22a3eb320466d Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 414 ++++++++++++++++++++++++++----------- 1 file changed, 291 insertions(+), 123 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 49ed432d793c2..0aa33daabedc2 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -8,7 +8,8 @@ * published by the Free Software Foundation. */ -/* TODO: support ipv6 for iface_stat */ +/* TODO: support ipv6 for iface_stat. + * Currently if an iface is only v6 it will not have stats collected. */ #include #include @@ -96,9 +97,6 @@ struct tag_stat { struct proc_dir_entry *proc_ptr; }; -static LIST_HEAD(iface_stat_list); -static DEFINE_SPINLOCK(iface_stat_list_lock); - struct iface_stat { struct list_head list; char *ifname; @@ -113,9 +111,8 @@ struct iface_stat { spinlock_t tag_stat_list_lock; }; - -static struct rb_root sock_tag_tree = RB_ROOT; -static DEFINE_SPINLOCK(sock_tag_list_lock); +static LIST_HEAD(iface_stat_list); +static DEFINE_SPINLOCK(iface_stat_list_lock); /* * Track tag that this socket is transferring data for, and not necesseraly @@ -128,6 +125,9 @@ struct sock_tag { tag_t tag; }; +static struct rb_root sock_tag_tree = RB_ROOT; +static DEFINE_SPINLOCK(sock_tag_list_lock); + static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par); /*----------------------------------------------*/ @@ -181,6 +181,14 @@ static inline uint64_t dc_sum_bytes(struct data_counters *counters, + counters->bpc[direction][IFS_PROTO_OTHER].bytes; } +static inline uint64_t dc_sum_packets(struct data_counters *counters, + enum ifs_tx_rx direction) +{ + return counters->bpc[direction][IFS_TCP].packets + + counters->bpc[direction][IFS_UDP].packets + + counters->bpc[direction][IFS_PROTO_OTHER].packets; +} + static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag) { struct rb_node *node = root->rb_node; @@ -397,12 +405,11 @@ void iface_stat_create(const struct net_device *net_dev) return; } - new_iface = kmalloc(sizeof(*new_iface), GFP_KERNEL); + new_iface = kzalloc(sizeof(*new_iface), GFP_KERNEL); if (new_iface == NULL) { pr_err("iface_stat: create(): failed to alloc iface_stat\n"); return; } - memset(new_iface, 0, sizeof(*new_iface)); new_iface->ifname = kstrdup(ifname, GFP_KERNEL); if (new_iface->ifname == NULL) { pr_err("iface_stat: create(): failed to alloc ifname\n"); @@ -531,12 +538,11 @@ static struct tag_stat *create_if_tag_stat(struct iface_stat *iface_entry, pr_debug("iface_stat: create_if_tag_stat(): ife=%p tag=0x%llx" " (uid=%d)\n", iface_entry, tag, get_uid_from_tag(tag)); - new_tag_stat_entry = kmalloc(sizeof(*new_tag_stat_entry), GFP_ATOMIC); + new_tag_stat_entry = kzalloc(sizeof(*new_tag_stat_entry), GFP_ATOMIC); if (!new_tag_stat_entry) { pr_err("iface_stat: failed to alloc new tag entry\n"); goto done; } - memset(new_tag_stat_entry, 0, sizeof(*new_tag_stat_entry)); new_tag_stat_entry->tag = tag; tag_stat_tree_insert(new_tag_stat_entry, &iface_entry->tag_stat_tree); done: @@ -852,7 +858,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) pr_debug("xt_qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n", par->hooknum, sk ? sk->sk_socket : NULL); - res = (info->match ^ info->invert) == 0; + res = (info->match ^ info->invert) == 0; goto put_sock_ret_res; } else if (info->match & info->invert & XT_QTAGUID_SOCKET) { res = false; @@ -922,7 +928,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, struct rb_node *node; int item_index = 0; - pr_debug("xt_qtaguid:proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", + pr_debug("xt_qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", page, items_to_skip, char_count, *eof); if (*eof) @@ -934,7 +940,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, node = rb_next(node)) { if (item_index++ < items_to_skip) continue; - sock_tag_entry = rb_entry(node, struct sock_tag, node); + sock_tag_entry = rb_entry(node, struct sock_tag, node); uid = get_uid_from_tag(sock_tag_entry->tag); pr_debug("xt_qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%d)\n", sock_tag_entry->sk, @@ -957,7 +963,103 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, return outp - page; } -static int qtaguid_ctrl_parse(const char *input, int count) +/* Delete socket tags, and stat tags associated with a given + * accouting tag and uid. */ +static int ctrl_cmd_delete(const char *input) +{ + char cmd; + uid_t uid = 0; + uid_t entry_uid; + tag_t acct_tag = 0; + tag_t tag; + int res, argc; + unsigned long flags, flags2; + struct iface_stat *iface_entry; + struct rb_node *node; + struct sock_tag *st_entry; + struct tag_stat *ts_entry; + + pr_debug("xt_qtaguid: ctrl_delete(%s): entered\n", input); + argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid); + pr_debug("xt_qtaguid: ctrl_delete(%s): argc=%d cmd=%c " + "acct_tag=0x%llx uid=%u\n", input, argc, cmd, + acct_tag, uid); + if (argc < 2) { + res = -EINVAL; + goto err; + } + if (!valid_atag(acct_tag)) { + pr_info("xt_qtaguid: ctrl_delete(%s): invalid tag\n", input); + res = -EINVAL; + goto err; + } + if (argc < 3) + uid = current_fsuid(); + + /* TODO: check that the uid == current_fsuid() + * except for special uid/gid. */ + + spin_lock_irqsave(&sock_tag_list_lock, flags); + node = rb_first(&sock_tag_tree); + while (node) { + st_entry = rb_entry(node, struct sock_tag, node); + entry_uid = get_uid_from_tag(st_entry->tag); + node = rb_next(node); + if (entry_uid != uid) + continue; + + if (!acct_tag || st_entry->tag == tag) { + pr_debug("xt_qtaguid: ctrl_delete(): " + "erase sk=%p tag=0x%llx (uid=%d)\n", + st_entry->sk, + st_entry->tag, + entry_uid); + rb_erase(&ts_entry->node, &sock_tag_tree); + kfree(st_entry); + } + } + spin_unlock_irqrestore(&sock_tag_list_lock, flags); + + /* If acct_tag is 0, then all entries belonging to uid are + * erased. */ + tag = combine_atag_with_uid(acct_tag, uid); + spin_lock_irqsave(&iface_stat_list_lock, flags); + list_for_each_entry(iface_entry, &iface_stat_list, list) { + + spin_lock_irqsave(&iface_entry->tag_stat_list_lock, flags2); + node = rb_first(&iface_entry->tag_stat_tree); + while (node) { + ts_entry = rb_entry(node, struct tag_stat, node); + entry_uid = get_uid_from_tag(ts_entry->tag); + node = rb_next(node); + if (entry_uid != uid) + continue; + if (!acct_tag || ts_entry->tag == tag) { + pr_debug("xt_qtaguid: ctrl_delete(): erase " + "%s 0x%llx %u\n", + iface_entry->ifname, + get_atag_from_tag(ts_entry->tag), + entry_uid); + rb_erase(&ts_entry->node, + &iface_entry->tag_stat_tree); + kfree(ts_entry); + } + } + spin_unlock_irqrestore(&iface_entry->tag_stat_list_lock, + flags2); + + } + spin_unlock_irqrestore(&iface_stat_list_lock, flags); + + res = 0; + +err: + pr_debug("xt_qtaguid: ctrl_delete(%s) res=%d\n", input, res); + return res; +} + + +static int ctrl_cmd_tag(const char *input) { char cmd; int sock_fd = 0; @@ -968,117 +1070,139 @@ static int qtaguid_ctrl_parse(const char *input, int count) struct sock_tag *sock_tag_entry; unsigned long flags; - pr_debug("xt_qtaguid: ctrl(%s): entered\n", input); /* Unassigned args will get defaulted later. */ - /* TODO: get acct_tag_str, keep a list of available tags for the - * uid, use num as acct_tag. */ argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid); - pr_debug("xt_qtaguid: ctrl(%s): argc=%d cmd=%c sock_fd=%d " - "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd, - acct_tag, uid); + pr_debug("xt_qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d " + "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd, + acct_tag, uid); + if (argc < 2) { + res = -EINVAL; + goto err; + } + el_socket = sockfd_lookup(sock_fd, &res); + if (!el_socket) { + pr_info("xt_qtaguid: ctrl_tag(%s): failed to lookup" + " sock_fd=%d err=%d\n", input, sock_fd, res); + goto err; + } + if (argc < 3) { + acct_tag = 0; + } else if (!valid_atag(acct_tag)) { + pr_info("xt_qtaguid: ctrl_tag(%s): invalid tag\n", input); + res = -EINVAL; + goto err; + } + if (argc < 4) + uid = current_fsuid(); - /* Collect params for commands */ - switch (cmd) { - case 't': - case 'u': - if (argc < 2) { - res = -EINVAL; - goto err; - } - el_socket = sockfd_lookup(sock_fd, &res); - if (!el_socket) { - pr_info("xt_qtaguid: ctrl(%s): failed to lookup" - " sock_fd=%d err=%d\n", input, sock_fd, res); + spin_lock_irqsave(&sock_tag_list_lock, flags); + sock_tag_entry = get_sock_stat_nl(el_socket->sk); + if (sock_tag_entry) { + /* TODO: check that the uid == current_fsuid() + * except for special uid/gid. */ + sock_tag_entry->tag = combine_atag_with_uid(acct_tag, + uid); + } else { + spin_unlock_irqrestore(&sock_tag_list_lock, flags); + sock_tag_entry = kzalloc(sizeof(*sock_tag_entry), + GFP_KERNEL); + if (!sock_tag_entry) { + res = -ENOMEM; goto err; } + sock_tag_entry->sk = el_socket->sk; + /* TODO: check that uid==current_fsuid() except + * for special uid/gid. */ + sock_tag_entry->tag = combine_atag_with_uid(acct_tag, + uid); spin_lock_irqsave(&sock_tag_list_lock, flags); - /* TODO: optim: pass in the current_fsuid() to do lookups - * as look ups will always be initiated form the same uid. */ - sock_tag_entry = get_sock_stat_nl(el_socket->sk); - if (!sock_tag_entry) - spin_unlock_irqrestore(&sock_tag_list_lock, flags); - /* HERE: The lock is held if there was a matching sock tag entry */ - break; - default: + sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); + } + spin_unlock_irqrestore(&sock_tag_list_lock, flags); + + pr_debug("xt_qtaguid: tag: sock_tag_entry->sk=%p " + "...->tag=0x%llx (uid=%u)\n", + sock_tag_entry->sk, sock_tag_entry->tag, + get_uid_from_tag(sock_tag_entry->tag)); + res = 0; + +err: + pr_debug("xt_qtaguid: ctrl_tag(%s) res=%d\n", input, res); + return res; +} + + +static int ctrl_cmd_untag(const char *input) +{ + char cmd; + int sock_fd = 0; + struct socket *el_socket; + int res, argc; + struct sock_tag *sock_tag_entry; + unsigned long flags; + + pr_debug("xt_qtaguid: ctrl_untag(%s): entered\n", input); + argc = sscanf(input, "%c %d", &cmd, &sock_fd); + pr_debug("xt_qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", + input, argc, cmd, sock_fd); + if (argc < 2) { res = -EINVAL; goto err; } - /* HERE: The lock is held if there was a matching sock tag entry */ + el_socket = sockfd_lookup(sock_fd, &res); + if (!el_socket) { + pr_info("xt_qtaguid: ctrl_untag(%s): failed to lookup" + " sock_fd=%d err=%d\n", input, sock_fd, res); + goto err; + } + spin_lock_irqsave(&sock_tag_list_lock, flags); + sock_tag_entry = get_sock_stat_nl(el_socket->sk); + if (!sock_tag_entry) { + spin_unlock_irqrestore(&sock_tag_list_lock, flags); + res = -EINVAL; + goto err; + } + + /* TODO: check that the uid==current_fsuid() + * except for special uid/gid. */ + rb_erase(&sock_tag_entry->node, &sock_tag_tree); + spin_unlock_irqrestore(&sock_tag_list_lock, flags); + kfree(sock_tag_entry); + + res = 0; +err: + pr_debug("xt_qtaguid: ctrl_untag(%s): res=%d\n", input, res); + return res; +} - /* Process commands */ +static int qtaguid_ctrl_parse(const char *input, int count) +{ + char cmd; + int res; + + pr_debug("xt_qtaguid: ctrl(%s): entered\n", input); + cmd = input[0]; + /* Collect params for commands */ switch (cmd) { + case 'd': + res = ctrl_cmd_delete(input); + break; case 't': - if (argc < 2) { - res = -EINVAL; - /* HERE: The lock is held if there was a matching sock - * tag entry */ - goto err_unlock; - } - if (argc < 3) { - acct_tag = 0; - } else if (!valid_atag(acct_tag)) { - res = -EINVAL; - /* HERE: The lock is held if there was a matching sock - * tag entry */ - goto err_unlock; - } - if (argc < 4) - uid = current_fsuid(); - if (!sock_tag_entry) { - /* HERE: There is no lock held because there was no - * sock tag entry */ - sock_tag_entry = kmalloc(sizeof(*sock_tag_entry), - GFP_KERNEL); - if (!sock_tag_entry) { - res = -ENOMEM; - goto err; - } - memset(sock_tag_entry, 0, sizeof(*sock_tag_entry)); - sock_tag_entry->sk = el_socket->sk; - /* TODO: check that uid==current_fsuid() except - * for special uid/gid. */ - sock_tag_entry->tag = combine_atag_with_uid(acct_tag, - uid); - spin_lock_irqsave(&sock_tag_list_lock, flags); - sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); - } else { - /* HERE: The lock is held because there is a matching - * sock tag entry */ - /* Just update the acct_tag portion. */ - uid_t orig_uid = get_uid_from_tag(sock_tag_entry->tag); - sock_tag_entry->tag = combine_atag_with_uid(acct_tag, - orig_uid); - } - spin_unlock_irqrestore(&sock_tag_list_lock, flags); - pr_debug("xt_qtaguid: tag: sock_tag_entry->sk=%p " - "...->tag=0x%llx (uid=%u)\n", - sock_tag_entry->sk, sock_tag_entry->tag, - get_uid_from_tag(sock_tag_entry->tag)); + res = ctrl_cmd_tag(input); break; case 'u': - if (!sock_tag_entry) { - res = -EINVAL; - goto err; - } - /* TODO: check that the uid==current_fsuid() - * except for special uid/gid. */ - rb_erase(&sock_tag_entry->node, &sock_tag_tree); - spin_unlock_irqrestore(&sock_tag_list_lock, flags); - kfree(sock_tag_entry); + res = ctrl_cmd_untag(input); break; - } - - /* All of the input has been processed */ - res = count; - goto ok; -err_unlock: - if (sock_tag_entry) - spin_unlock_irqrestore(&sock_tag_list_lock, flags); + default: + res = -EINVAL; + goto err; + } + if (!res) + res = count; err: -ok: pr_debug("xt_qtaguid: ctrl(%s): res=%d\n", input, res); return res; } @@ -1099,6 +1223,57 @@ static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, return qtaguid_ctrl_parse(input_buf, count); } +static int print_stats_line(char *outp, int char_count, int item_index, + char *ifname, tag_t tag, + struct data_counters *counters) +{ + int len; + if (!item_index) + len = snprintf(outp, char_count, + "idx iface acct_tag_hex uid_tag_int " + "rx_bytes rx_packets " + "tx_bytes tx_packets " + "rx_tcp_packets rx_tcp_bytes " + "rx_udp_packets rx_udp_bytes " + "rx_other_packets rx_other_bytes " + "tx_tcp_packets tx_tcp_bytes " + "tx_udp_packets tx_udp_bytes " + "tx_other_packets tx_other_bytes\n"); + else + len = snprintf(outp, char_count, + "%d %s 0x%llx %u " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu\n", + item_index, + ifname, + get_atag_from_tag(tag), + get_uid_from_tag(tag), + dc_sum_bytes(counters, IFS_RX), + dc_sum_packets(counters, IFS_RX), + dc_sum_bytes(counters, IFS_TX), + dc_sum_packets(counters, IFS_TX), + counters->bpc[IFS_RX][IFS_TCP].bytes, + counters->bpc[IFS_RX][IFS_TCP].packets, + counters->bpc[IFS_RX][IFS_UDP].bytes, + counters->bpc[IFS_RX][IFS_UDP].packets, + counters->bpc[IFS_RX][IFS_PROTO_OTHER].bytes, + counters->bpc[IFS_RX][IFS_PROTO_OTHER].packets, + counters->bpc[IFS_TX][IFS_TCP].bytes, + counters->bpc[IFS_TX][IFS_TCP].packets, + counters->bpc[IFS_TX][IFS_UDP].bytes, + counters->bpc[IFS_TX][IFS_UDP].packets, + counters->bpc[IFS_TX][IFS_PROTO_OTHER].bytes, + counters->bpc[IFS_TX][IFS_PROTO_OTHER].packets); + return len; +} + + /* * Procfs reader to get all tag stats using style "1)" as described in * fs/proc/generic.c @@ -1126,9 +1301,8 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, if (!items_to_skip) { /* The idx is there to help debug when things go belly up. */ - len = snprintf(outp, char_count, - "idx iface acct_tag_hex uid_tag_int rx_bytes " - "tx_bytes\n"); + len = print_stats_line(outp, char_count, /*index*/0, NULL, + make_tag_from_uid(0), NULL); /* Don't advance the outp unless the whole line was printed */ if (len >= char_count) { *outp = '\0'; @@ -1137,7 +1311,6 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, outp += len; char_count -= len; } - spin_lock_irqsave(&iface_stat_list_lock, flags); list_for_each_entry(iface_entry, &iface_stat_list, list) { struct rb_node *node; @@ -1145,26 +1318,21 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, for (node = rb_first(&iface_entry->tag_stat_tree); node; node = rb_next(node)) { - ts_entry = rb_entry(node, struct tag_stat, node); + ts_entry = rb_entry(node, struct tag_stat, node); if (item_index++ < items_to_skip) continue; - len = snprintf(outp, char_count, - "%d %s 0x%llx %u %llu %llu\n", - item_index, - iface_entry->ifname, - get_atag_from_tag(ts_entry->tag), - get_uid_from_tag(ts_entry->tag), - dc_sum_bytes(&ts_entry->counters, - IFS_RX), - dc_sum_bytes(&ts_entry->counters, - IFS_TX)); + len = print_stats_line(outp, char_count, + item_index, + iface_entry->ifname, + ts_entry->tag, + &ts_entry->counters); if (len >= char_count) { + *outp = '\0'; spin_unlock_irqrestore( &iface_entry->tag_stat_list_lock, flags2); spin_unlock_irqrestore( &iface_stat_list_lock, flags); - *outp = '\0'; return outp - page; } outp += len; From ea23026d6240c662c1b05fbb72c3c86ab253b6fd Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Sun, 17 Jul 2011 16:07:23 -0700 Subject: [PATCH 1960/2556] netfilter: xt_qtaguid: add uid permission checks during ctrl/stats access * uid handling - Limit UID impersonation to processes with a gid in AID_NET_BW_ACCT. This affects socket tagging, and data removal. - Limit stats lookup to own uid or the process gid is in AID_NET_BW_STATS. This affects stats lookup. * allow pacifying the module Setting passive to Y/y will make the module return immediately on external stimulus. No more stats and silent success on ctrl writes. Mainly used when one suspects this module of misbehaving. Change-Id: I83990862d52a9b0922aca103a0f61375cddeb7c4 Signed-off-by: JP Abgrall --- include/linux/android_aid.h | 2 + net/netfilter/xt_qtaguid.c | 170 ++++++++++++++++++++++++++---------- 2 files changed, 128 insertions(+), 44 deletions(-) diff --git a/include/linux/android_aid.h b/include/linux/android_aid.h index 7f16a14c0fe71..0f904b3ba7f07 100644 --- a/include/linux/android_aid.h +++ b/include/linux/android_aid.h @@ -22,5 +22,7 @@ #define AID_INET 3003 #define AID_NET_RAW 3004 #define AID_NET_ADMIN 3005 +#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */ +#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */ #endif diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 0aa33daabedc2..3ef8753bc37b3 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -10,6 +10,7 @@ /* TODO: support ipv6 for iface_stat. * Currently if an iface is only v6 it will not have stats collected. */ +#define DEBUG #include #include @@ -29,6 +30,49 @@ ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN)) +static const char *module_procdirname = "xt_qtaguid"; +static struct proc_dir_entry *xt_qtaguid_procdir; + +static unsigned int proc_iface_perms = S_IRUGO; +module_param_named(iface_perms, proc_iface_perms, uint, S_IRUGO | S_IWUSR); + +static struct proc_dir_entry *xt_qtaguid_stats_file; +static unsigned int proc_stats_perms = S_IRUGO; +module_param_named(stats_perms, proc_stats_perms, uint, S_IRUGO | S_IWUSR); + +static struct proc_dir_entry *xt_qtaguid_ctrl_file; +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUGO; +#else +static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUSR; +#endif +module_param_named(ctrl_perms, proc_ctrl_perms, uint, S_IRUGO | S_IWUSR); + +#ifdef CONFIG_ANDROID_PARANOID_NETWORK +#include +static gid_t proc_stats_readall_gid = AID_NET_BW_STATS; +static gid_t proc_ctrl_write_gid = AID_NET_BW_ACCT; +#else +/* 0 means, don't limit anybody */ +static gid_t proc_stats_readall_gid; +static gid_t proc_ctrl_write_gid; +#endif +module_param_named(stats_readall_gid, proc_stats_readall_gid, uint, + S_IRUGO | S_IWUSR); +module_param_named(ctrl_write_gid, proc_ctrl_write_gid, uint, + S_IRUGO | S_IWUSR); + +/* After the kernel has initiallized this module, it is still possible + * to make it passive: + * - do not register it via iptables. + * the matching code will not be invoked. + * - set passive to 0 + * the iface stats handling will not be act on notifications. + * This is mostly usefull when a bug is suspected. + */ +static bool module_passive; +module_param_named(passive, module_passive, bool, S_IRUGO | S_IWUSR); + /*---------------------------------------------------------------------------*/ /* * Tags: @@ -429,15 +473,15 @@ void iface_stat_create(const struct net_device *net_dev) new_iface->proc_ptr = proc_entry; /* TODO: make root access only */ - create_proc_read_entry("tx_bytes", S_IRUGO, proc_entry, + create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, read_proc_u64, &new_iface->tx_bytes); - create_proc_read_entry("rx_bytes", S_IRUGO, proc_entry, + create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, read_proc_u64, &new_iface->rx_bytes); - create_proc_read_entry("tx_packets", S_IRUGO, proc_entry, + create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, read_proc_u64, &new_iface->tx_packets); - create_proc_read_entry("rx_packets", S_IRUGO, proc_entry, + create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, read_proc_u64, &new_iface->rx_packets); - create_proc_read_entry("active", S_IRUGO, proc_entry, + create_proc_read_entry("active", proc_iface_perms, proc_entry, read_proc_bool, &new_iface->active); pr_debug("iface_stat: create(): done entry=%p dev=%s ip=%pI4\n", @@ -655,6 +699,9 @@ static int iface_netdev_event_handler(struct notifier_block *nb, unsigned long event, void *ptr) { struct net_device *dev = ptr; + if (unlikely(module_passive)) + return NOTIFY_DONE; + pr_debug("iface_stat: netdev_event(): ev=0x%lx netdev=%p->name=%s\n", event, dev, dev ? dev->name : ""); @@ -682,6 +729,9 @@ static int iface_inetaddr_event_handler(struct notifier_block *nb, struct in_device *in_dev = ifa->ifa_dev; struct net_device *dev = in_dev->dev; + if (unlikely(module_passive)) + return NOTIFY_DONE; + pr_debug("iface_stat: inetaddr_event(): ev=0x%lx netdev=%p->name=%s\n", event, dev, dev ? dev->name : ""); @@ -817,8 +867,10 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) struct sock *sk; uid_t sock_uid; bool res; + pr_debug("xt_qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n", par->hooknum, skb, par->in, par->out, par->family); + if (skb == NULL) { res = (info->match ^ info->invert) == 0; goto ret_res; @@ -928,6 +980,11 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, struct rb_node *node; int item_index = 0; + if (unlikely(module_passive)) { + *eof = 1; + return 0; + } + pr_debug("xt_qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", page, items_to_skip, char_count, *eof); @@ -963,6 +1020,20 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, return outp - page; } +int can_impersonate_uid(uid_t uid) +{ + return uid == current_fsuid() + || !proc_ctrl_write_gid + || in_egroup_p(proc_ctrl_write_gid); +} + +int can_read_other_uid_stats(uid_t uid) +{ + return uid == current_fsuid() + || !proc_ctrl_write_gid + || in_egroup_p(proc_stats_readall_gid); +} + /* Delete socket tags, and stat tags associated with a given * accouting tag and uid. */ static int ctrl_cmd_delete(const char *input) @@ -993,11 +1064,14 @@ static int ctrl_cmd_delete(const char *input) res = -EINVAL; goto err; } - if (argc < 3) + if (argc < 3) { uid = current_fsuid(); - - /* TODO: check that the uid == current_fsuid() - * except for special uid/gid. */ + } else if (!can_impersonate_uid(uid)) { + pr_info("xt_qtaguid: ctrl_delete(%s): insuficient priv\n", + input); + res = -EPERM; + goto err; + } spin_lock_irqsave(&sock_tag_list_lock, flags); node = rb_first(&sock_tag_tree); @@ -1092,14 +1166,18 @@ static int ctrl_cmd_tag(const char *input) res = -EINVAL; goto err; } - if (argc < 4) + if (argc < 4) { uid = current_fsuid(); + } else if (!can_impersonate_uid(uid)) { + pr_info("xt_qtaguid: ctrl_tag(%s): insuficient priv\n", + input); + res = -EPERM; + goto err; + } spin_lock_irqsave(&sock_tag_list_lock, flags); sock_tag_entry = get_sock_stat_nl(el_socket->sk); if (sock_tag_entry) { - /* TODO: check that the uid == current_fsuid() - * except for special uid/gid. */ sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid); } else { @@ -1111,8 +1189,6 @@ static int ctrl_cmd_tag(const char *input) goto err; } sock_tag_entry->sk = el_socket->sk; - /* TODO: check that uid==current_fsuid() except - * for special uid/gid. */ sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid); spin_lock_irqsave(&sock_tag_list_lock, flags); @@ -1162,9 +1238,8 @@ static int ctrl_cmd_untag(const char *input) res = -EINVAL; goto err; } - - /* TODO: check that the uid==current_fsuid() - * except for special uid/gid. */ + /* The socket already belongs to the current process + * so it can do whatever it wants to it. */ rb_erase(&sock_tag_entry->node, &sock_tag_tree); spin_unlock_irqrestore(&sock_tag_list_lock, flags); kfree(sock_tag_entry); @@ -1213,6 +1288,9 @@ static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, { char input_buf[MAX_QTAGUID_CTRL_INPUT_LEN]; + if (unlikely(module_passive)) + return count; + if (count >= MAX_QTAGUID_CTRL_INPUT_LEN) return -EINVAL; @@ -1228,18 +1306,25 @@ static int print_stats_line(char *outp, int char_count, int item_index, struct data_counters *counters) { int len; - if (!item_index) + if (!item_index) { len = snprintf(outp, char_count, - "idx iface acct_tag_hex uid_tag_int " - "rx_bytes rx_packets " - "tx_bytes tx_packets " - "rx_tcp_packets rx_tcp_bytes " - "rx_udp_packets rx_udp_bytes " - "rx_other_packets rx_other_bytes " - "tx_tcp_packets tx_tcp_bytes " - "tx_udp_packets tx_udp_bytes " - "tx_other_packets tx_other_bytes\n"); - else + "idx iface acct_tag_hex uid_tag_int " + "rx_bytes rx_packets " + "tx_bytes tx_packets " + "rx_tcp_packets rx_tcp_bytes " + "rx_udp_packets rx_udp_bytes " + "rx_other_packets rx_other_bytes " + "tx_tcp_packets tx_tcp_bytes " + "tx_udp_packets tx_udp_bytes " + "tx_other_packets tx_other_bytes\n"); + } else { + uid_t stat_uid = get_uid_from_tag(tag); + if (!can_read_other_uid_stats(stat_uid)) { + pr_debug("xt_qtaguid: insufficient priv for stat line:" + "%s 0x%llx %u\n", + ifname, get_atag_from_tag(tag), stat_uid); + return 0; + } len = snprintf(outp, char_count, "%d %s 0x%llx %u " "%llu %llu " @@ -1253,7 +1338,7 @@ static int print_stats_line(char *outp, int char_count, int item_index, item_index, ifname, get_atag_from_tag(tag), - get_uid_from_tag(tag), + stat_uid, dc_sum_bytes(counters, IFS_RX), dc_sum_packets(counters, IFS_RX), dc_sum_bytes(counters, IFS_TX), @@ -1270,6 +1355,7 @@ static int print_stats_line(char *outp, int char_count, int item_index, counters->bpc[IFS_TX][IFS_UDP].packets, counters->bpc[IFS_TX][IFS_PROTO_OTHER].bytes, counters->bpc[IFS_TX][IFS_PROTO_OTHER].packets); + } return len; } @@ -1290,7 +1376,10 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, struct tag_stat *ts_entry; int item_index = 0; - /* TODO: make root access only */ + if (unlikely(module_passive)) { + *eof = 1; + return 0; + } pr_debug("xt_qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " "char_count=%d *eof=%d\n", page, *num_items_returned, @@ -1335,9 +1424,11 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, &iface_stat_list_lock, flags); return outp - page; } - outp += len; - char_count -= len; - (*num_items_returned)++; + if (len) { + outp += len; + char_count -= len; + (*num_items_returned)++; + } } spin_unlock_irqrestore(&iface_entry->tag_stat_list_lock, flags2); @@ -1349,11 +1440,6 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, } /*------------------------------------------*/ -static const char *module_procdirname = "xt_qtaguid"; -static struct proc_dir_entry *xt_qtaguid_procdir; -static struct proc_dir_entry *xt_qtaguid_ctrl_file; -static struct proc_dir_entry *xt_qtaguid_stats_file; - static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) { int ret; @@ -1364,7 +1450,7 @@ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) goto no_dir; } - xt_qtaguid_ctrl_file = create_proc_entry("ctrl", 0666, + xt_qtaguid_ctrl_file = create_proc_entry("ctrl", proc_ctrl_perms, *res_procdir); if (!xt_qtaguid_ctrl_file) { pr_err("xt_qtaguid: failed to create xt_qtaguid/ctrl " @@ -1375,7 +1461,7 @@ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) xt_qtaguid_ctrl_file->read_proc = qtaguid_ctrl_proc_read; xt_qtaguid_ctrl_file->write_proc = qtaguid_ctrl_proc_write; - xt_qtaguid_stats_file = create_proc_entry("stats", 0666, + xt_qtaguid_stats_file = create_proc_entry("stats", proc_stats_perms, *res_procdir); if (!xt_qtaguid_stats_file) { pr_err("xt_qtaguid: failed to create xt_qtaguid/stats " @@ -1383,10 +1469,6 @@ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) ret = -ENOMEM; goto no_stats_entry; } - /* - * TODO: add extra read_proc for full stats with protocol - * breakout - */ xt_qtaguid_stats_file->read_proc = qtaguid_stats_proc_read; /* * TODO: add support counter hacking From 0572dbeb95f1cab07385d1e3934b2d8bc012de82 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 22 Jul 2011 10:34:22 -0700 Subject: [PATCH 1961/2556] netfilter: qtaguid: disable #define DEBUG This would cause log spam to the point of slowing down the system. Change-Id: I5655f0207935004b0198f43ad0d3c9ea25466e4e Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 3ef8753bc37b3..22552c9b81cf2 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -10,7 +10,6 @@ /* TODO: support ipv6 for iface_stat. * Currently if an iface is only v6 it will not have stats collected. */ -#define DEBUG #include #include From 75544a1f82e092894e7671cfda6b4fd861fe6058 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 9 Aug 2011 11:49:50 -0700 Subject: [PATCH 1962/2556] netfilter: xt_qtaguid: add counter sets and matching control * Added support for sets of counters. By default set 0 is active. Userspace can control which set is active for a given UID by writing to .../ctrl s Changing the active set is only permitted for processes in the AID_NET_BW_ACCT group. The active set tracking is reset when the uid tag is deleted with the .../ctrl command d 0 * New output format for the proc .../stats - Now has cnt_set in the list. """ idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_packets rx_tcp_bytes rx_udp_packets rx_udp_bytes rx_other_packets rx_other_bytes tx_tcp_packets tx_tcp_bytes tx_udp_packets tx_udp_bytes tx_other_packets tx_other_bytes ... 2 rmnet0 0x0 1000 0 27729 29 1477 27 27501 26 228 3 0 0 1249 24 228 3 0 0 2 rmnet0 0x0 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 rmnet0 0x0 10005 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 rmnet0 0x0 10005 1 46407 57 8008 64 46407 57 0 0 0 0 8008 64 0 0 0 0 ... 6 rmnet0 0x7fff000100000000 10005 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 rmnet0 0x7fff000100000000 10005 1 27493 24 1564 22 27493 24 0 0 0 0 1564 22 0 0 0 0 """ * Refactored for proc stats output code. * Silenced some of the per packet debug output. * Reworded some of the debug messages. * Replaced all the spin_lock_irqsave/irqrestore with *_bh(): netfilter handling is done in softirq. Change-Id: Ibe89f9d754579fd97335617186c614b43333cfd3 Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 821 +++++++++++++++++++++++-------------- 1 file changed, 509 insertions(+), 312 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 22552c9b81cf2..842d4d763e1ec 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -8,8 +8,34 @@ * published by the Free Software Foundation. */ -/* TODO: support ipv6 for iface_stat. - * Currently if an iface is only v6 it will not have stats collected. */ +/* + * TODO: support ipv6 for iface_stat. + * Currently if an iface is only v6 it will not have stats collected. + */ + +/* #define DEBUG */ +/* #define IDEBUG */ +/* #define MDEBUG */ +/* #define RDEBUG */ + +/* Iface handling */ +#ifdef IDEBUG +#define IF_DEBUG(...) pr_debug(__VA_ARGS__) +#else +#define IF_DEBUG(...) no_printk(__VA_ARGS__) +#endif +/* Iptable Matching */ +#ifdef MDEBUG +#define MT_DEBUG(...) pr_debug(__VA_ARGS__) +#else +#define MT_DEBUG(...) no_printk(__VA_ARGS__) +#endif +/* Red-black tree handling */ +#ifdef RDEBUG +#define RB_DEBUG(...) pr_debug(__VA_ARGS__) +#else +#define RB_DEBUG(...) no_printk(__VA_ARGS__) +#endif #include #include @@ -23,8 +49,10 @@ #include #include -/* We only use the xt_socket funcs within a similar context to avoid unexpected - * return values. */ +/* + * We only use the xt_socket funcs within a similar context to avoid unexpected + * return values. + */ #define XT_SOCKET_SUPPORTED_HOOKS \ ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN)) @@ -61,7 +89,8 @@ module_param_named(stats_readall_gid, proc_stats_readall_gid, uint, module_param_named(ctrl_write_gid, proc_ctrl_write_gid, uint, S_IRUGO | S_IWUSR); -/* After the kernel has initiallized this module, it is still possible +/* + * After the kernel has initiallized this module, it is still possible * to make it passive: * - do not register it via iptables. * the matching code will not be invoked. @@ -106,6 +135,14 @@ typedef uint64_t tag_t; /* Only used via accessors */ static const char *iface_stat_procdirname = "iface_stat"; static struct proc_dir_entry *iface_stat_procdir; + +/* + * For now we only track 2 sets of counters. + * The default set is 0. + * Userspace can activate another set for a given uid being tracked. + */ +#define IFS_MAX_COUNTER_SETS 2 + enum ifs_tx_rx { IFS_TX, IFS_RX, @@ -126,16 +163,22 @@ struct byte_packet_counters { }; struct data_counters { - struct byte_packet_counters bpc[IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS]; + struct byte_packet_counters bpc[IFS_MAX_COUNTER_SETS][IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS]; }; -struct tag_stat { +/* Generic tag based node used as a base for rb_tree ops. */ +struct tag_node { struct rb_node node; tag_t tag; +}; +struct tag_stat { + struct tag_node tn; struct data_counters counters; - /* If this tag is acct_tag based, we need to count against the - * matching parent uid_tag. */ + /* + * If this tag is acct_tag based, we need to count against the + * matching parent uid_tag. + */ struct data_counters *parent_counters; struct proc_dir_entry *proc_ptr; }; @@ -171,6 +214,15 @@ struct sock_tag { static struct rb_root sock_tag_tree = RB_ROOT; static DEFINE_SPINLOCK(sock_tag_list_lock); +/* Track the set active_set for the given tag. */ +struct tag_counter_set { + struct tag_node tn; + int active_set; +}; + +static struct rb_root tag_counter_set_tree = RB_ROOT; +static DEFINE_SPINLOCK(tag_counter_set_list_lock); + static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par); /*----------------------------------------------*/ @@ -179,7 +231,6 @@ static inline int tag_compare(tag_t t1, tag_t t2) return t1 < t2 ? -1 : t1 == t2 ? 0 : 1; } - static inline tag_t combine_atag_with_uid(tag_t acct_tag, uid_t uid) { return acct_tag | uid; @@ -206,40 +257,42 @@ static inline bool valid_atag(tag_t tag) return !(tag & 0xFFFFFFFFULL); } -static inline void dc_add_byte_packets(struct data_counters *counters, +static inline void dc_add_byte_packets(struct data_counters *counters, int set, enum ifs_tx_rx direction, enum ifs_proto ifs_proto, int bytes, int packets) { - counters->bpc[direction][ifs_proto].bytes += bytes; - counters->bpc[direction][ifs_proto].packets += packets; + counters->bpc[set][direction][ifs_proto].bytes += bytes; + counters->bpc[set][direction][ifs_proto].packets += packets; } static inline uint64_t dc_sum_bytes(struct data_counters *counters, + int set, enum ifs_tx_rx direction) { - return counters->bpc[direction][IFS_TCP].bytes - + counters->bpc[direction][IFS_UDP].bytes - + counters->bpc[direction][IFS_PROTO_OTHER].bytes; + return counters->bpc[set][direction][IFS_TCP].bytes + + counters->bpc[set][direction][IFS_UDP].bytes + + counters->bpc[set][direction][IFS_PROTO_OTHER].bytes; } static inline uint64_t dc_sum_packets(struct data_counters *counters, + int set, enum ifs_tx_rx direction) { - return counters->bpc[direction][IFS_TCP].packets - + counters->bpc[direction][IFS_UDP].packets - + counters->bpc[direction][IFS_PROTO_OTHER].packets; + return counters->bpc[set][direction][IFS_TCP].packets + + counters->bpc[set][direction][IFS_UDP].packets + + counters->bpc[set][direction][IFS_PROTO_OTHER].packets; } -static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag) +static struct tag_node *tag_node_tree_search(struct rb_root *root, tag_t tag) { struct rb_node *node = root->rb_node; while (node) { - struct tag_stat *data = rb_entry(node, struct tag_stat, node); + struct tag_node *data = rb_entry(node, struct tag_node, node); int result = tag_compare(tag, data->tag); - pr_debug("qtaguid: tag_stat_tree_search(): tag=0x%llx" + RB_DEBUG("qtaguid: tag_node_tree_search(): tag=0x%llx" " (uid=%d)\n", data->tag, get_uid_from_tag(data->tag)); @@ -254,16 +307,16 @@ static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag) return NULL; } -static void tag_stat_tree_insert(struct tag_stat *data, struct rb_root *root) +static void tag_node_tree_insert(struct tag_node *data, struct rb_root *root) { struct rb_node **new = &(root->rb_node), *parent = NULL; /* Figure out where to put new node */ while (*new) { - struct tag_stat *this = rb_entry(*new, struct tag_stat, + struct tag_node *this = rb_entry(*new, struct tag_node, node); int result = tag_compare(data->tag, this->tag); - pr_debug("qtaguid: tag_stat_tree_insert(): tag=0x%llx" + RB_DEBUG("qtaguid: tag_node_tree_insert(): tag=0x%llx" " (uid=%d)\n", this->tag, get_uid_from_tag(this->tag)); @@ -281,6 +334,28 @@ static void tag_stat_tree_insert(struct tag_stat *data, struct rb_root *root) rb_insert_color(&data->node, root); } +static void tag_stat_tree_insert(struct tag_stat *data, struct rb_root *root) +{ + tag_node_tree_insert((struct tag_node *)data, root); +} + +static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag) +{ + return (struct tag_stat *)tag_node_tree_search(root, tag); +} + +static void tag_counter_set_tree_insert(struct tag_counter_set *data, + struct rb_root *root) +{ + tag_node_tree_insert((struct tag_node *)data, root); +} + +static struct tag_counter_set *tag_counter_set_tree_search(struct rb_root *root, + tag_t tag) +{ + return (struct tag_counter_set *)tag_node_tree_search(root, tag); +} + static struct sock_tag *sock_tag_tree_search(struct rb_root *root, const struct sock *sk) { @@ -357,22 +432,45 @@ static int read_proc_bool(char *page, char **start, off_t off, return len; } +static int get_active_counter_set(tag_t tag) +{ + int active_set = 0; + struct tag_counter_set *tcs; + + MT_DEBUG("qtaguid: get_active_counter_set(tag=0x%llx)" + " (uid=%d)\n", + tag, get_uid_from_tag(tag)); + /* For now we only handle UID tags for active sets */ + tag = get_utag_from_tag(tag); + spin_lock_bh(&tag_counter_set_list_lock); + tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (tcs) + active_set = tcs->active_set; + spin_unlock_bh(&tag_counter_set_list_lock); + return active_set; +} + /* Find the entry for tracking the specified interface. */ -static struct iface_stat *get_iface_stat(const char *ifname) +static struct iface_stat *get_iface_entry(const char *ifname) { - unsigned long flags; struct iface_stat *iface_entry; - if (!ifname) + + /* Find the entry for tracking the specified tag within the interface */ + if (ifname == NULL) { + pr_info("iface_stat: NULL device name\n"); return NULL; + } + - spin_lock_irqsave(&iface_stat_list_lock, flags); + /* Iterate over interfaces */ + spin_lock_bh(&iface_stat_list_lock); list_for_each_entry(iface_entry, &iface_stat_list, list) { - if (!strcmp(iface_entry->ifname, ifname)) + if (!strcmp(ifname, iface_entry->ifname)) goto done; } iface_entry = NULL; done: - spin_unlock_irqrestore(&iface_stat_list_lock, flags); + spin_unlock_bh(&iface_stat_list_lock); return iface_entry; } @@ -384,7 +482,6 @@ static struct iface_stat *get_iface_stat(const char *ifname) void iface_stat_create(const struct net_device *net_dev) { struct in_device *in_dev; - unsigned long flags; struct iface_stat *new_iface; struct proc_dir_entry *proc_entry; const char *ifname; @@ -394,26 +491,27 @@ void iface_stat_create(const struct net_device *net_dev) ASSERT_RTNL(); /* No need for separate locking */ - pr_debug("iface_stat: create(): netdev=%p->name=%s\n", + IF_DEBUG("qtaguid: iface_stat: create(): netdev=%p->name=%s\n", net_dev, net_dev ? net_dev->name : ""); if (!net_dev) { - pr_err("iface_stat: create(): no net dev!\n"); + pr_err("qtaguid: iface_stat: create(): no net dev!\n"); return; } in_dev = __in_dev_get_rtnl(net_dev); if (!in_dev) { - pr_err("iface_stat: create(): no inet dev!\n"); + pr_err("qtaguid: iface_stat: create(): no inet dev!\n"); return; } - pr_debug("iface_stat: create(): in_dev=%p\n", in_dev); + IF_DEBUG("qtaguid: iface_stat: create(): in_dev=%p\n", in_dev); ifname = net_dev->name; - pr_debug("iface_stat: create(): ifname=%p\n", ifname); + IF_DEBUG("qtaguid: iface_stat: create(): ifname=%p\n", ifname); for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { - pr_debug("iface_stat: create(): for(): ifa=%p ifname=%p\n", - ifa, ifname); - pr_debug("iface_stat: create(): ifname=%s ifa_label=%s\n", + IF_DEBUG("qtaguid: iface_stat: create(): for(): " + "ifa=%p ifname=%p\n", ifa, ifname); + IF_DEBUG("qtaguid: iface_stat: create(): " + "ifname=%s ifa_label=%s\n", ifname, ifa->ifa_label ? ifa->ifa_label : "(null)"); if (ifa->ifa_label && !strcmp(ifname, ifa->ifa_label)) break; @@ -422,40 +520,42 @@ void iface_stat_create(const struct net_device *net_dev) if (ifa) { ipaddr = ifa->ifa_local; } else { - pr_err("iface_stat: create(): dev %s has no matching IP\n", - ifname); + IF_DEBUG("qtaguid: iface_stat: create(): " + "dev %s has no matching IP\n", + ifname); return; } - entry = get_iface_stat(net_dev->name); + entry = get_iface_entry(net_dev->name); if (entry != NULL) { - pr_debug("iface_stat: create(): dev %s entry=%p\n", ifname, - entry); + IF_DEBUG("qtaguid: iface_stat: create(): dev %s entry=%p\n", + ifname, entry); if (ipv4_is_loopback(ipaddr)) { entry->active = false; - pr_debug("iface_stat: create(): disable tracking of " - "loopback dev %s\n", ifname); + IF_DEBUG("qtaguid: iface_stat: create(): " + "disable tracking of loopback dev %s\n", + ifname); } else { entry->active = true; - pr_debug("iface_stat: create(): enable tracking of " - "dev %s with ip=%pI4\n", + IF_DEBUG("qtaguid: iface_stat: create(): " + "enable tracking of dev %s with ip=%pI4\n", ifname, &ipaddr); } return; } else if (ipv4_is_loopback(ipaddr)) { - pr_debug("iface_stat: create(): ignore loopback dev %s" + IF_DEBUG("qtaguid: iface_stat: create(): ignore loopback dev %s" " ip=%pI4\n", ifname, &ipaddr); return; } new_iface = kzalloc(sizeof(*new_iface), GFP_KERNEL); if (new_iface == NULL) { - pr_err("iface_stat: create(): failed to alloc iface_stat\n"); + pr_err("qtaguid: iface_stat: create(): failed to alloc iface_stat\n"); return; } new_iface->ifname = kstrdup(ifname, GFP_KERNEL); if (new_iface->ifname == NULL) { - pr_err("iface_stat: create(): failed to alloc ifname\n"); + pr_err("qtaguid: iface_stat: create(): failed to alloc ifname\n"); kfree(new_iface); return; } @@ -464,14 +564,13 @@ void iface_stat_create(const struct net_device *net_dev) new_iface->active = true; new_iface->tag_stat_tree = RB_ROOT; - spin_lock_irqsave(&iface_stat_list_lock, flags); + spin_lock_bh(&iface_stat_list_lock); list_add(&new_iface->list, &iface_stat_list); - spin_unlock_irqrestore(&iface_stat_list_lock, flags); + spin_unlock_bh(&iface_stat_list_lock); proc_entry = proc_mkdir(ifname, iface_stat_procdir); new_iface->proc_ptr = proc_entry; - /* TODO: make root access only */ create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, read_proc_u64, &new_iface->tx_bytes); create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, @@ -483,63 +582,63 @@ void iface_stat_create(const struct net_device *net_dev) create_proc_read_entry("active", proc_iface_perms, proc_entry, read_proc_bool, &new_iface->active); - pr_debug("iface_stat: create(): done entry=%p dev=%s ip=%pI4\n", - new_iface, ifname, &ipaddr); + IF_DEBUG("qtaguid: iface_stat: create(): done " + "entry=%p dev=%s ip=%pI4\n", new_iface, ifname, &ipaddr); } static struct sock_tag *get_sock_stat_nl(const struct sock *sk) { - pr_debug("xt_qtaguid: get_sock_stat_nl(sk=%p)\n", sk); + MT_DEBUG("qtaguid: get_sock_stat_nl(sk=%p)\n", sk); return sock_tag_tree_search(&sock_tag_tree, sk); } static struct sock_tag *get_sock_stat(const struct sock *sk) { - unsigned long flags; struct sock_tag *sock_tag_entry; - pr_debug("xt_qtaguid: get_sock_stat(sk=%p)\n", sk); + MT_DEBUG("qtaguid: get_sock_stat(sk=%p)\n", sk); if (!sk) return NULL; - spin_lock_irqsave(&sock_tag_list_lock, flags); + spin_lock_bh(&sock_tag_list_lock); sock_tag_entry = get_sock_stat_nl(sk); - spin_unlock_irqrestore(&sock_tag_list_lock, flags); + spin_unlock_bh(&sock_tag_list_lock); return sock_tag_entry; } static void -data_counters_update(struct data_counters *dc, enum ifs_tx_rx direction, - int proto, int bytes) +data_counters_update(struct data_counters *dc, int set, + enum ifs_tx_rx direction, int proto, int bytes) { switch (proto) { case IPPROTO_TCP: - dc_add_byte_packets(dc, direction, IFS_TCP, bytes, 1); + dc_add_byte_packets(dc, set, direction, IFS_TCP, bytes, 1); break; case IPPROTO_UDP: - dc_add_byte_packets(dc, direction, IFS_UDP, bytes, 1); + dc_add_byte_packets(dc, set, direction, IFS_UDP, bytes, 1); break; case IPPROTO_IP: default: - dc_add_byte_packets(dc, direction, IFS_PROTO_OTHER, bytes, 1); + dc_add_byte_packets(dc, set, direction, IFS_PROTO_OTHER, bytes, + 1); break; } } - /* * Update stats for the specified interface. Do nothing if the entry * does not exist (when a device was never configured with an IP address). * Called when an device is being unregistered. */ -void iface_stat_update(struct net_device *dev) +static void iface_stat_update(struct net_device *dev) { struct rtnl_link_stats64 dev_stats, *stats; struct iface_stat *entry; stats = dev_get_stats(dev, &dev_stats); ASSERT_RTNL(); - entry = get_iface_stat(dev->name); + entry = get_iface_entry(dev->name); if (entry == NULL) { - pr_debug("iface_stat: dev %s monitor not found\n", dev->name); + IF_DEBUG("qtaguid: iface_stat: dev %s monitor not found\n", + dev->name); return; } if (entry->active) { @@ -548,74 +647,53 @@ void iface_stat_update(struct net_device *dev) entry->rx_bytes += stats->rx_bytes; entry->rx_packets += stats->rx_packets; entry->active = false; - pr_debug("iface_stat: Updating stats for " + IF_DEBUG("qtaguid: iface_stat: Updating stats for " "dev %s which went down\n", dev->name); } else { - pr_debug("iface_stat: Did not update stats for " + IF_DEBUG("qtaguid: iface_stat: Did not update stats for " "dev %s which went down\n", dev->name); } } - static void tag_stat_update(struct tag_stat *tag_entry, enum ifs_tx_rx direction, int proto, int bytes) { - pr_debug("xt_qtaguid: tag_stat_update(tag=0x%llx (uid=%d) dir=%d " - "proto=%d bytes=%d)\n", - tag_entry->tag, get_uid_from_tag(tag_entry->tag), direction, - proto, bytes); - data_counters_update(&tag_entry->counters, direction, proto, bytes); + int active_set; + active_set = get_active_counter_set(tag_entry->tn.tag); + MT_DEBUG("qtaguid: tag_stat_update(tag=0x%llx (uid=%d) set=%d " + "dir=%d proto=%d bytes=%d)\n", + tag_entry->tn.tag, get_uid_from_tag(tag_entry->tn.tag), + active_set, direction, proto, bytes); + data_counters_update(&tag_entry->counters, active_set, direction, + proto, bytes); if (tag_entry->parent_counters) - data_counters_update(tag_entry->parent_counters, direction, - proto, bytes); + data_counters_update(tag_entry->parent_counters, active_set, + direction, proto, bytes); } - -/* Create a new entry for tracking the specified {acct_tag,uid_tag} within +/* + * Create a new entry for tracking the specified {acct_tag,uid_tag} within * the interface. - * iface_entry->tag_stat_list_lock should be held. */ + * iface_entry->tag_stat_list_lock should be held. + */ static struct tag_stat *create_if_tag_stat(struct iface_stat *iface_entry, tag_t tag) { struct tag_stat *new_tag_stat_entry = NULL; - pr_debug("iface_stat: create_if_tag_stat(): ife=%p tag=0x%llx" + IF_DEBUG("qtaguid: iface_stat: create_if_tag_stat(): ife=%p tag=0x%llx" " (uid=%d)\n", iface_entry, tag, get_uid_from_tag(tag)); new_tag_stat_entry = kzalloc(sizeof(*new_tag_stat_entry), GFP_ATOMIC); if (!new_tag_stat_entry) { - pr_err("iface_stat: failed to alloc new tag entry\n"); + pr_err("qtaguid: iface_stat: failed to alloc new tag entry\n"); goto done; } - new_tag_stat_entry->tag = tag; + new_tag_stat_entry->tn.tag = tag; tag_stat_tree_insert(new_tag_stat_entry, &iface_entry->tag_stat_tree); done: return new_tag_stat_entry; } -static struct iface_stat *get_iface_entry(const char *ifname) -{ - struct iface_stat *iface_entry; - unsigned long flags; - - /* Find the entry for tracking the specified tag within the interface */ - if (ifname == NULL) { - pr_info("iface_stat: NULL device name\n"); - return NULL; - } - - - /* Iterate over interfaces */ - spin_lock_irqsave(&iface_stat_list_lock, flags); - list_for_each_entry(iface_entry, &iface_stat_list, list) { - if (!strcmp(ifname, iface_entry->ifname)) - goto done; - } - iface_entry = NULL; -done: - spin_unlock_irqrestore(&iface_stat_list_lock, flags); - return iface_entry; -} - static void if_tag_stat_update(const char *ifname, uid_t uid, const struct sock *sk, enum ifs_tx_rx direction, int proto, int bytes) @@ -626,25 +704,29 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, struct data_counters *uid_tag_counters; struct sock_tag *sock_tag_entry; struct iface_stat *iface_entry; - unsigned long flags; struct tag_stat *new_tag_stat; - pr_debug("xt_qtaguid: if_tag_stat_update(ifname=%s " + MT_DEBUG("qtaguid: if_tag_stat_update(ifname=%s " "uid=%d sk=%p dir=%d proto=%d bytes=%d)\n", ifname, uid, sk, direction, proto, bytes); iface_entry = get_iface_entry(ifname); if (!iface_entry) { - pr_err("iface_stat: interface %s not found\n", ifname); + pr_err("qtaguid: iface_stat: interface %s not found\n", ifname); return; } - /* else { If the iface_entry becomes inactive, it is still ok - * to process the data. } */ + /* + * else { If the iface_entry becomes inactive, it is still ok + * to process the data. } + */ - pr_debug("iface_stat: stat_update() got entry=%p\n", iface_entry); + MT_DEBUG("qtaguid: iface_stat: stat_update() got entry=%p\n", + iface_entry); - /* Look for a tagged sock. - * It will have an acct_uid. */ + /* + * Look for a tagged sock. + * It will have an acct_uid. + */ sock_tag_entry = get_sock_stat(sk); if (sock_tag_entry) { tag = sock_tag_entry->tag; @@ -655,19 +737,21 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, acct_tag = 0; tag = combine_atag_with_uid(acct_tag, uid); } - pr_debug("iface_stat: stat_update(): looking for tag=0x%llx (uid=%d)" - " in ife=%p\n", + MT_DEBUG("qtaguid: iface_stat: stat_update(): " + " looking for tag=0x%llx (uid=%d) in ife=%p\n", tag, get_uid_from_tag(tag), iface_entry); /* Loop over tag list under this interface for {acct_tag,uid_tag} */ - spin_lock_irqsave(&iface_entry->tag_stat_list_lock, flags); + spin_lock_bh(&iface_entry->tag_stat_list_lock); tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree, tag); if (tag_stat_entry) { - /* Updating the {acct_tag, uid_tag} entry handles both stats: - * {0, uid_tag} will also get updated. */ + /* + * Updating the {acct_tag, uid_tag} entry handles both stats: + * {0, uid_tag} will also get updated. + */ tag_stat_update(tag_stat_entry, direction, proto, bytes); - spin_unlock_irqrestore(&iface_entry->tag_stat_list_lock, flags); + spin_unlock_bh(&iface_entry->tag_stat_list_lock); return; } @@ -690,7 +774,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, new_tag_stat = create_if_tag_stat(iface_entry, tag); new_tag_stat->parent_counters = uid_tag_counters; } - spin_unlock_irqrestore(&iface_entry->tag_stat_list_lock, flags); + spin_unlock_bh(&iface_entry->tag_stat_list_lock); tag_stat_update(new_tag_stat, direction, proto, bytes); } @@ -701,7 +785,8 @@ static int iface_netdev_event_handler(struct notifier_block *nb, if (unlikely(module_passive)) return NOTIFY_DONE; - pr_debug("iface_stat: netdev_event(): ev=0x%lx netdev=%p->name=%s\n", + IF_DEBUG("qtaguid: iface_stat: netdev_event(): " + "ev=0x%lx netdev=%p->name=%s\n", event, dev, dev ? dev->name : ""); switch (event) { @@ -731,7 +816,8 @@ static int iface_inetaddr_event_handler(struct notifier_block *nb, if (unlikely(module_passive)) return NOTIFY_DONE; - pr_debug("iface_stat: inetaddr_event(): ev=0x%lx netdev=%p->name=%s\n", + IF_DEBUG("qtaguid: iface_stat: inetaddr_event(): " + "ev=0x%lx netdev=%p->name=%s\n", event, dev, dev ? dev->name : ""); switch (event) { @@ -756,18 +842,18 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) iface_stat_procdir = proc_mkdir(iface_stat_procdirname, parent_procdir); if (!iface_stat_procdir) { - pr_err("iface_stat: failed to create proc entry\n"); + pr_err("qtaguid: iface_stat: failed to create proc entry\n"); err = -1; goto err; } err = register_netdevice_notifier(&iface_netdev_notifier_blk); if (err) { - pr_err("iface_stat: failed to register dev event handler\n"); + pr_err("qtaguid: iface_stat: failed to register dev event handler\n"); goto err_unreg_nd; } err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); if (err) { - pr_err("iface_stat: failed to register dev event handler\n"); + pr_err("qtaguid: iface_stat: failed to register dev event handler\n"); goto err_zap_entry; } return 0; @@ -786,11 +872,13 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, struct sock *sk; unsigned int hook_mask = (1 << par->hooknum); - pr_debug("xt_qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb, + MT_DEBUG("qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb, par->hooknum, par->family); - /* Let's not abuse the the xt_socket_get*_sk(), or else it will - * return garbage SKs. */ + /* + * Let's not abuse the the xt_socket_get*_sk(), or else it will + * return garbage SKs. + */ if (!(hook_mask & XT_SOCKET_SUPPORTED_HOOKS)) return NULL; @@ -805,12 +893,13 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, return NULL; } - /* Seems to be issues on the file ptr for TCP_TIME_WAIT SKs. + /* + * Seems to be issues on the file ptr for TCP_TIME_WAIT SKs. * http://kerneltrap.org/mailarchive/linux-netdev/2010/10/21/6287959 * Not fixed in 3.0-r3 :( */ if (sk) { - pr_debug("xt_qtaguid: %p->sk_proto=%u " + MT_DEBUG("qtaguid: %p->sk_proto=%u " "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state); if (sk->sk_state == TCP_TIME_WAIT) { xt_socket_put_sk(sk); @@ -827,14 +916,14 @@ static void account_for_uid(const struct sk_buff *skb, const struct net_device *el_dev; if (!skb->dev) { - pr_debug("xt_qtaguid[%d]: no skb->dev\n", par->hooknum); + MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); el_dev = par->in ? : par->out; } else { const struct net_device *other_dev; el_dev = skb->dev; other_dev = par->in ? : par->out; if (el_dev != other_dev) { - pr_debug("xt_qtaguid[%d]: skb->dev=%p %s vs " + MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " "par->(in/out)=%p %s\n", par->hooknum, el_dev, el_dev->name, other_dev, other_dev->name); @@ -842,14 +931,14 @@ static void account_for_uid(const struct sk_buff *skb, } if (unlikely(!el_dev)) { - pr_info("xt_qtaguid[%d]: no par->in/out?!!\n", par->hooknum); + pr_info("qtaguid[%d]: no par->in/out?!!\n", par->hooknum); } else if (unlikely(!el_dev->name)) { - pr_info("xt_qtaguid[%d]: no dev->name?!!\n", par->hooknum); + pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum); } else { - pr_debug("xt_qtaguid[%d]: dev name=%s type=%d\n", - par->hooknum, - el_dev->name, - el_dev->type); + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d\n", + par->hooknum, + el_dev->name, + el_dev->type); if_tag_stat_update(el_dev->name, uid, skb->sk ? skb->sk : alternate_sk, @@ -867,7 +956,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) uid_t sock_uid; bool res; - pr_debug("xt_qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n", + MT_DEBUG("qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n", par->hooknum, skb, par->in, par->out, par->family); if (skb == NULL) { @@ -878,35 +967,42 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) sk = skb->sk; if (sk == NULL) { - /* A missing sk->sk_socket happens when packets are in-flight + /* + * A missing sk->sk_socket happens when packets are in-flight * and the matching socket is already closed and gone. */ sk = qtaguid_find_sk(skb, par); - /* If we got the socket from the find_sk(), we will need to put - * it back, as nf_tproxy_get_sock_v4() got it. */ + /* + * If we got the socket from the find_sk(), we will need to put + * it back, as nf_tproxy_get_sock_v4() got it. + */ got_sock = sk; } - pr_debug("xt_qtaguid[%d]: sk=%p got_sock=%d proto=%d\n", + MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d proto=%d\n", par->hooknum, sk, got_sock, ip_hdr(skb)->protocol); if (sk != NULL) { - pr_debug("xt_qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", + MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", par->hooknum, sk, sk->sk_socket, sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); filp = sk->sk_socket ? sk->sk_socket->file : NULL; - pr_debug("xt_qtaguid[%d]: filp...uid=%d\n", + MT_DEBUG("qtaguid[%d]: filp...uid=%d\n", par->hooknum, filp ? filp->f_cred->fsuid : -1); } if (sk == NULL || sk->sk_socket == NULL) { - /* Here, the qtaguid_find_sk() using connection tracking + /* + * Here, the qtaguid_find_sk() using connection tracking * couldn't find the owner, so for now we just count them - * against the system. */ - /* TODO: unhack how to force just accounting. + * against the system. + */ + /* + * TODO: unhack how to force just accounting. * For now we only do iface stats when the uid-owner is not - * requested */ + * requested. + */ if (!(info->match & XT_QTAGUID_UID)) account_for_uid(skb, sk, 0, par); - pr_debug("xt_qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n", + MT_DEBUG("qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n", par->hooknum, sk ? sk->sk_socket : NULL); res = (info->match ^ info->invert) == 0; @@ -917,18 +1013,21 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) } filp = sk->sk_socket->file; if (filp == NULL) { - pr_debug("xt_qtaguid[%d]: leaving filp=NULL\n", par->hooknum); + MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum); res = ((info->match ^ info->invert) & (XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0; goto put_sock_ret_res; } sock_uid = filp->f_cred->fsuid; - /* TODO: unhack how to force just accounting. - * For now we only do iface stats when the uid-owner is not requested */ + /* + * TODO: unhack how to force just accounting. + * For now we only do iface stats when the uid-owner is not requested + */ if (!(info->match & XT_QTAGUID_UID)) account_for_uid(skb, sk, sock_uid, par); - /* The following two tests fail the match when: + /* + * The following two tests fail the match when: * id not in range AND no inverted condition requested * or id in range AND inverted condition requested * Thus (!a && b) || (a && !b) == a ^ b @@ -937,7 +1036,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) if ((filp->f_cred->fsuid >= info->uid_min && filp->f_cred->fsuid <= info->uid_max) ^ !(info->invert & XT_QTAGUID_UID)) { - pr_debug("xt_qtaguid[%d]: leaving uid not matching\n", + MT_DEBUG("qtaguid[%d]: leaving uid not matching\n", par->hooknum); res = false; goto put_sock_ret_res; @@ -946,20 +1045,20 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) if ((filp->f_cred->fsgid >= info->gid_min && filp->f_cred->fsgid <= info->gid_max) ^ !(info->invert & XT_QTAGUID_GID)) { - pr_debug("xt_qtaguid[%d]: leaving gid not matching\n", + MT_DEBUG("qtaguid[%d]: leaving gid not matching\n", par->hooknum); res = false; goto put_sock_ret_res; } - pr_debug("xt_qtaguid[%d]: leaving matched\n", par->hooknum); + MT_DEBUG("qtaguid[%d]: leaving matched\n", par->hooknum); res = true; put_sock_ret_res: if (got_sock) xt_socket_put_sk(sk); ret_res: - pr_debug("xt_qtaguid[%d]: left %d\n", par->hooknum, res); + MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res); return res; } @@ -973,7 +1072,6 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, { char *outp = page; int len; - unsigned long flags; uid_t uid; struct sock_tag *sock_tag_entry; struct rb_node *node; @@ -984,13 +1082,13 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, return 0; } - pr_debug("xt_qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", + pr_debug("qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", page, items_to_skip, char_count, *eof); if (*eof) return 0; - spin_lock_irqsave(&sock_tag_list_lock, flags); + spin_lock_bh(&sock_tag_list_lock); for (node = rb_first(&sock_tag_tree); node; node = rb_next(node)) { @@ -998,7 +1096,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, continue; sock_tag_entry = rb_entry(node, struct sock_tag, node); uid = get_uid_from_tag(sock_tag_entry->tag); - pr_debug("xt_qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%d)\n", + pr_debug("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%d)\n", sock_tag_entry->sk, sock_tag_entry->tag, uid); @@ -1006,7 +1104,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, "sock=%p tag=0x%llx (uid=%u)\n", sock_tag_entry->sk, sock_tag_entry->tag, uid); if (len >= char_count) { - spin_unlock_irqrestore(&sock_tag_list_lock, flags); + spin_unlock_bh(&sock_tag_list_lock); *outp = '\0'; return outp - page; } @@ -1014,44 +1112,48 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, char_count -= len; (*num_items_returned)++; } - spin_unlock_irqrestore(&sock_tag_list_lock, flags); + spin_unlock_bh(&sock_tag_list_lock); *eof = 1; return outp - page; } -int can_impersonate_uid(uid_t uid) +static bool can_manipulate_uids(void) { - return uid == current_fsuid() - || !proc_ctrl_write_gid - || in_egroup_p(proc_ctrl_write_gid); + return !proc_ctrl_write_gid || in_egroup_p(proc_ctrl_write_gid); } -int can_read_other_uid_stats(uid_t uid) +static bool can_impersonate_uid(uid_t uid) { - return uid == current_fsuid() - || !proc_ctrl_write_gid + return uid == current_fsuid() || can_manipulate_uids(); +} + +static bool can_read_other_uid_stats(uid_t uid) +{ + return uid == current_fsuid() || !proc_ctrl_write_gid || in_egroup_p(proc_stats_readall_gid); } -/* Delete socket tags, and stat tags associated with a given - * accouting tag and uid. */ +/* + * Delete socket tags, and stat tags associated with a given + * accouting tag and uid. + */ static int ctrl_cmd_delete(const char *input) { char cmd; - uid_t uid = 0; + uid_t uid; uid_t entry_uid; - tag_t acct_tag = 0; + tag_t acct_tag; tag_t tag; int res, argc; - unsigned long flags, flags2; struct iface_stat *iface_entry; struct rb_node *node; struct sock_tag *st_entry; struct tag_stat *ts_entry; + struct tag_counter_set *tcs_entry; - pr_debug("xt_qtaguid: ctrl_delete(%s): entered\n", input); + pr_debug("qtaguid: ctrl_delete(%s): entered\n", input); argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid); - pr_debug("xt_qtaguid: ctrl_delete(%s): argc=%d cmd=%c " + pr_debug("qtaguid: ctrl_delete(%s): argc=%d cmd=%c " "acct_tag=0x%llx uid=%u\n", input, argc, cmd, acct_tag, uid); if (argc < 2) { @@ -1059,20 +1161,21 @@ static int ctrl_cmd_delete(const char *input) goto err; } if (!valid_atag(acct_tag)) { - pr_info("xt_qtaguid: ctrl_delete(%s): invalid tag\n", input); + pr_info("qtaguid: ctrl_delete(%s): invalid tag\n", input); res = -EINVAL; goto err; } if (argc < 3) { uid = current_fsuid(); } else if (!can_impersonate_uid(uid)) { - pr_info("xt_qtaguid: ctrl_delete(%s): insuficient priv\n", + pr_info("qtaguid: ctrl_delete(%s): insuficient priv\n", input); res = -EPERM; goto err; } - spin_lock_irqsave(&sock_tag_list_lock, flags); + /* Delete socket tags */ + spin_lock_bh(&sock_tag_list_lock); node = rb_first(&sock_tag_tree); while (node) { st_entry = rb_entry(node, struct sock_tag, node); @@ -1082,55 +1185,130 @@ static int ctrl_cmd_delete(const char *input) continue; if (!acct_tag || st_entry->tag == tag) { - pr_debug("xt_qtaguid: ctrl_delete(): " - "erase sk=%p tag=0x%llx (uid=%d)\n", + pr_debug("qtaguid: ctrl_delete(): " + "erase st: sk=%p tag=0x%llx (uid=%d)\n", st_entry->sk, st_entry->tag, entry_uid); - rb_erase(&ts_entry->node, &sock_tag_tree); + rb_erase(&st_entry->node, &sock_tag_tree); kfree(st_entry); } } - spin_unlock_irqrestore(&sock_tag_list_lock, flags); + spin_unlock_bh(&sock_tag_list_lock); - /* If acct_tag is 0, then all entries belonging to uid are - * erased. */ tag = combine_atag_with_uid(acct_tag, uid); - spin_lock_irqsave(&iface_stat_list_lock, flags); + + /* Delete tag counter-sets */ + spin_lock_bh(&tag_counter_set_list_lock); + tcs_entry = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (tcs_entry) { + pr_debug("qtaguid: ctrl_delete(): " + "erase tcs: tag=0x%llx (uid=%d) set=%d\n", + tcs_entry->tn.tag, + get_uid_from_tag(tcs_entry->tn.tag), + tcs_entry->active_set); + rb_erase(&tcs_entry->tn.node, &tag_counter_set_tree); + kfree(tcs_entry); + } + spin_unlock_bh(&tag_counter_set_list_lock); + + /* + * If acct_tag is 0, then all entries belonging to uid are + * erased. + */ + spin_lock_bh(&iface_stat_list_lock); list_for_each_entry(iface_entry, &iface_stat_list, list) { - spin_lock_irqsave(&iface_entry->tag_stat_list_lock, flags2); + spin_lock_bh(&iface_entry->tag_stat_list_lock); node = rb_first(&iface_entry->tag_stat_tree); while (node) { - ts_entry = rb_entry(node, struct tag_stat, node); - entry_uid = get_uid_from_tag(ts_entry->tag); + ts_entry = rb_entry(node, struct tag_stat, tn.node); + entry_uid = get_uid_from_tag(ts_entry->tn.tag); node = rb_next(node); if (entry_uid != uid) continue; - if (!acct_tag || ts_entry->tag == tag) { - pr_debug("xt_qtaguid: ctrl_delete(): erase " - "%s 0x%llx %u\n", + if (!acct_tag || ts_entry->tn.tag == tag) { + pr_debug("qtaguid: ctrl_delete(): " + "erase ts: %s 0x%llx %u\n", iface_entry->ifname, - get_atag_from_tag(ts_entry->tag), + get_atag_from_tag(ts_entry->tn.tag), entry_uid); - rb_erase(&ts_entry->node, + rb_erase(&ts_entry->tn.node, &iface_entry->tag_stat_tree); kfree(ts_entry); } } - spin_unlock_irqrestore(&iface_entry->tag_stat_list_lock, - flags2); + spin_unlock_bh(&iface_entry->tag_stat_list_lock); } - spin_unlock_irqrestore(&iface_stat_list_lock, flags); + spin_unlock_bh(&iface_stat_list_lock); res = 0; err: - pr_debug("xt_qtaguid: ctrl_delete(%s) res=%d\n", input, res); + pr_debug("qtaguid: ctrl_delete(%s) res=%d\n", input, res); return res; } +static int ctrl_cmd_counter_set(const char *input) +{ + char cmd; + uid_t uid = 0; + tag_t tag; + int res, argc; + struct tag_counter_set *tcs; + int counter_set; + + pr_debug("qtaguid: ctrl_counterset(%s): entered\n", input); + argc = sscanf(input, "%c %d %u", &cmd, &counter_set, &uid); + pr_debug("qtaguid: ctrl_counterset(%s): argc=%d cmd=%c " + "set=%d uid=%u\n", input, argc, cmd, + counter_set, uid); + if (argc != 3) { + res = -EINVAL; + goto err; + } + if (counter_set < 0 || counter_set >= IFS_MAX_COUNTER_SETS) { + pr_info("qtaguid: ctrl_counterset(%s): invalid counter_set range\n", + input); + res = -EINVAL; + goto err; + } + if (!can_manipulate_uids()) { + pr_info("qtaguid: ctrl_counterset(%s): insufficient priv\n", + input); + res = -EPERM; + goto err; + } + + tag = make_tag_from_uid(uid); + spin_lock_bh(&tag_counter_set_list_lock); + tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (!tcs) { + tcs = kzalloc(sizeof(*tcs), GFP_ATOMIC); + if (!tcs) { + spin_unlock_bh(&tag_counter_set_list_lock); + pr_err("qtaguid: ctrl_counterset(%s): " + "failed to alloc counter set\n", + input); + res = -ENOMEM; + goto err; + } + tcs->tn.tag = tag; + tag_counter_set_tree_insert(tcs, &tag_counter_set_tree); + pr_debug("qtaguid: ctrl_counterset(%s): added tcs tag=0x%llx " + "(uid=%d) set=%d\n", + input, tag, get_uid_from_tag(tag), counter_set); + } + tcs->active_set = counter_set; + spin_unlock_bh(&tag_counter_set_list_lock); + + res = 0; + +err: + pr_debug("qtaguid: ctrl_counterset(%s) res=%d\n", input, res); + return res; +} static int ctrl_cmd_tag(const char *input) { @@ -1141,11 +1319,10 @@ static int ctrl_cmd_tag(const char *input) struct socket *el_socket; int res, argc; struct sock_tag *sock_tag_entry; - unsigned long flags; /* Unassigned args will get defaulted later. */ argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid); - pr_debug("xt_qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d " + pr_debug("qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d " "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd, acct_tag, uid); if (argc < 2) { @@ -1154,35 +1331,34 @@ static int ctrl_cmd_tag(const char *input) } el_socket = sockfd_lookup(sock_fd, &res); if (!el_socket) { - pr_info("xt_qtaguid: ctrl_tag(%s): failed to lookup" + pr_info("qtaguid: ctrl_tag(%s): failed to lookup" " sock_fd=%d err=%d\n", input, sock_fd, res); goto err; } if (argc < 3) { acct_tag = 0; } else if (!valid_atag(acct_tag)) { - pr_info("xt_qtaguid: ctrl_tag(%s): invalid tag\n", input); + pr_info("qtaguid: ctrl_tag(%s): invalid tag\n", input); res = -EINVAL; goto err; } if (argc < 4) { uid = current_fsuid(); } else if (!can_impersonate_uid(uid)) { - pr_info("xt_qtaguid: ctrl_tag(%s): insuficient priv\n", + pr_info("qtaguid: ctrl_tag(%s): insuficient priv\n", input); res = -EPERM; goto err; } - spin_lock_irqsave(&sock_tag_list_lock, flags); + spin_lock_bh(&sock_tag_list_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); if (sock_tag_entry) { sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid); } else { - spin_unlock_irqrestore(&sock_tag_list_lock, flags); sock_tag_entry = kzalloc(sizeof(*sock_tag_entry), - GFP_KERNEL); + GFP_ATOMIC); if (!sock_tag_entry) { res = -ENOMEM; goto err; @@ -1190,23 +1366,21 @@ static int ctrl_cmd_tag(const char *input) sock_tag_entry->sk = el_socket->sk; sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid); - spin_lock_irqsave(&sock_tag_list_lock, flags); sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); } - spin_unlock_irqrestore(&sock_tag_list_lock, flags); + spin_unlock_bh(&sock_tag_list_lock); - pr_debug("xt_qtaguid: tag: sock_tag_entry->sk=%p " + pr_debug("qtaguid: tag: sock_tag_entry->sk=%p " "...->tag=0x%llx (uid=%u)\n", sock_tag_entry->sk, sock_tag_entry->tag, get_uid_from_tag(sock_tag_entry->tag)); res = 0; err: - pr_debug("xt_qtaguid: ctrl_tag(%s) res=%d\n", input, res); + pr_debug("qtaguid: ctrl_tag(%s) res=%d\n", input, res); return res; } - static int ctrl_cmd_untag(const char *input) { char cmd; @@ -1214,11 +1388,10 @@ static int ctrl_cmd_untag(const char *input) struct socket *el_socket; int res, argc; struct sock_tag *sock_tag_entry; - unsigned long flags; - pr_debug("xt_qtaguid: ctrl_untag(%s): entered\n", input); + pr_debug("qtaguid: ctrl_untag(%s): entered\n", input); argc = sscanf(input, "%c %d", &cmd, &sock_fd); - pr_debug("xt_qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", + pr_debug("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", input, argc, cmd, sock_fd); if (argc < 2) { res = -EINVAL; @@ -1226,26 +1399,26 @@ static int ctrl_cmd_untag(const char *input) } el_socket = sockfd_lookup(sock_fd, &res); if (!el_socket) { - pr_info("xt_qtaguid: ctrl_untag(%s): failed to lookup" + pr_info("qtaguid: ctrl_untag(%s): failed to lookup" " sock_fd=%d err=%d\n", input, sock_fd, res); goto err; } - spin_lock_irqsave(&sock_tag_list_lock, flags); + spin_lock_bh(&sock_tag_list_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); if (!sock_tag_entry) { - spin_unlock_irqrestore(&sock_tag_list_lock, flags); + spin_unlock_bh(&sock_tag_list_lock); res = -EINVAL; goto err; } /* The socket already belongs to the current process * so it can do whatever it wants to it. */ rb_erase(&sock_tag_entry->node, &sock_tag_tree); - spin_unlock_irqrestore(&sock_tag_list_lock, flags); + spin_unlock_bh(&sock_tag_list_lock); kfree(sock_tag_entry); res = 0; err: - pr_debug("xt_qtaguid: ctrl_untag(%s): res=%d\n", input, res); + pr_debug("qtaguid: ctrl_untag(%s): res=%d\n", input, res); return res; } @@ -1254,7 +1427,7 @@ static int qtaguid_ctrl_parse(const char *input, int count) char cmd; int res; - pr_debug("xt_qtaguid: ctrl(%s): entered\n", input); + pr_debug("qtaguid: ctrl(%s): entered\n", input); cmd = input[0]; /* Collect params for commands */ switch (cmd) { @@ -1262,6 +1435,10 @@ static int qtaguid_ctrl_parse(const char *input, int count) res = ctrl_cmd_delete(input); break; + case 's': + res = ctrl_cmd_counter_set(input); + break; + case 't': res = ctrl_cmd_tag(input); break; @@ -1277,7 +1454,7 @@ static int qtaguid_ctrl_parse(const char *input, int count) if (!res) res = count; err: - pr_debug("xt_qtaguid: ctrl(%s): res=%d\n", input, res); + pr_debug("qtaguid: ctrl(%s): res=%d\n", input, res); return res; } @@ -1300,14 +1477,22 @@ static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, return qtaguid_ctrl_parse(input_buf, count); } -static int print_stats_line(char *outp, int char_count, int item_index, - char *ifname, tag_t tag, - struct data_counters *counters) +struct proc_print_info { + char *outp; + char **num_items_returned; + struct iface_stat *iface_entry; + struct tag_stat *ts_entry; + int item_index; + int char_count; +}; + +static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) { int len; - if (!item_index) { - len = snprintf(outp, char_count, - "idx iface acct_tag_hex uid_tag_int " + struct data_counters *cnts; + if (!ppi->item_index) { + len = snprintf(ppi->outp, ppi->char_count, + "idx iface acct_tag_hex uid_tag_int cnt_set " "rx_bytes rx_packets " "tx_bytes tx_packets " "rx_tcp_packets rx_tcp_bytes " @@ -1317,47 +1502,71 @@ static int print_stats_line(char *outp, int char_count, int item_index, "tx_udp_packets tx_udp_bytes " "tx_other_packets tx_other_bytes\n"); } else { + tag_t tag = ppi->ts_entry->tn.tag; uid_t stat_uid = get_uid_from_tag(tag); if (!can_read_other_uid_stats(stat_uid)) { - pr_debug("xt_qtaguid: insufficient priv for stat line:" + pr_debug("qtaguid: insufficient priv for stat line:" "%s 0x%llx %u\n", - ifname, get_atag_from_tag(tag), stat_uid); + ppi->iface_entry->ifname, + get_atag_from_tag(tag), stat_uid); return 0; } - len = snprintf(outp, char_count, - "%d %s 0x%llx %u " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu\n", - item_index, - ifname, - get_atag_from_tag(tag), - stat_uid, - dc_sum_bytes(counters, IFS_RX), - dc_sum_packets(counters, IFS_RX), - dc_sum_bytes(counters, IFS_TX), - dc_sum_packets(counters, IFS_TX), - counters->bpc[IFS_RX][IFS_TCP].bytes, - counters->bpc[IFS_RX][IFS_TCP].packets, - counters->bpc[IFS_RX][IFS_UDP].bytes, - counters->bpc[IFS_RX][IFS_UDP].packets, - counters->bpc[IFS_RX][IFS_PROTO_OTHER].bytes, - counters->bpc[IFS_RX][IFS_PROTO_OTHER].packets, - counters->bpc[IFS_TX][IFS_TCP].bytes, - counters->bpc[IFS_TX][IFS_TCP].packets, - counters->bpc[IFS_TX][IFS_UDP].bytes, - counters->bpc[IFS_TX][IFS_UDP].packets, - counters->bpc[IFS_TX][IFS_PROTO_OTHER].bytes, - counters->bpc[IFS_TX][IFS_PROTO_OTHER].packets); + cnts = &ppi->ts_entry->counters; + len = snprintf( + ppi->outp, ppi->char_count, + "%d %s 0x%llx %u %u " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu\n", + ppi->item_index, + ppi->iface_entry->ifname, + get_atag_from_tag(tag), + stat_uid, + cnt_set, + dc_sum_bytes(cnts, cnt_set, IFS_RX), + dc_sum_packets(cnts, cnt_set, IFS_RX), + dc_sum_bytes(cnts, cnt_set, IFS_TX), + dc_sum_packets(cnts, cnt_set, IFS_TX), + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); } return len; } +bool pp_sets(struct proc_print_info *ppi) +{ + int len; + int counter_set; + for (counter_set = 0; counter_set < IFS_MAX_COUNTER_SETS; + counter_set++) { + len = pp_stats_line(ppi, counter_set); + if (len >= ppi->char_count) { + *ppi->outp = '\0'; + return false; + } + if (len) { + ppi->outp += len; + ppi->char_count -= len; + (*ppi->num_items_returned)++; + } + } + return true; +} /* * Procfs reader to get all tag stats using style "1)" as described in @@ -1368,19 +1577,20 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, off_t items_to_skip, int char_count, int *eof, void *data) { - char *outp = page; + struct proc_print_info ppi; int len; - unsigned long flags, flags2; - struct iface_stat *iface_entry; - struct tag_stat *ts_entry; - int item_index = 0; + + ppi.outp = page; + ppi.item_index = 0; + ppi.char_count = char_count; + ppi.num_items_returned = num_items_returned; if (unlikely(module_passive)) { *eof = 1; return 0; } - pr_debug("xt_qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " + pr_debug("qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " "char_count=%d *eof=%d\n", page, *num_items_returned, items_to_skip, char_count, *eof); @@ -1389,53 +1599,39 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, if (!items_to_skip) { /* The idx is there to help debug when things go belly up. */ - len = print_stats_line(outp, char_count, /*index*/0, NULL, - make_tag_from_uid(0), NULL); + len = pp_stats_line(&ppi, 0); /* Don't advance the outp unless the whole line was printed */ - if (len >= char_count) { - *outp = '\0'; - return outp - page; + if (len >= ppi.char_count) { + *ppi.outp = '\0'; + return ppi.outp - page; } - outp += len; - char_count -= len; + ppi.outp += len; + ppi.char_count -= len; } - spin_lock_irqsave(&iface_stat_list_lock, flags); - list_for_each_entry(iface_entry, &iface_stat_list, list) { + + spin_lock_bh(&iface_stat_list_lock); + list_for_each_entry(ppi.iface_entry, &iface_stat_list, list) { struct rb_node *node; - spin_lock_irqsave(&iface_entry->tag_stat_list_lock, flags2); - for (node = rb_first(&iface_entry->tag_stat_tree); + spin_lock_bh(&ppi.iface_entry->tag_stat_list_lock); + for (node = rb_first(&ppi.iface_entry->tag_stat_tree); node; node = rb_next(node)) { - ts_entry = rb_entry(node, struct tag_stat, node); - if (item_index++ < items_to_skip) + ppi.ts_entry = rb_entry(node, struct tag_stat, tn.node); + if (ppi.item_index++ < items_to_skip) continue; - len = print_stats_line(outp, char_count, - item_index, - iface_entry->ifname, - ts_entry->tag, - &ts_entry->counters); - if (len >= char_count) { - *outp = '\0'; - spin_unlock_irqrestore( - &iface_entry->tag_stat_list_lock, - flags2); - spin_unlock_irqrestore( - &iface_stat_list_lock, flags); - return outp - page; - } - if (len) { - outp += len; - char_count -= len; - (*num_items_returned)++; + if (!pp_sets(&ppi)) { + spin_unlock_bh( + &ppi.iface_entry->tag_stat_list_lock); + spin_unlock_bh(&iface_stat_list_lock); + return ppi.outp - page; } } - spin_unlock_irqrestore(&iface_entry->tag_stat_list_lock, - flags2); + spin_unlock_bh(&ppi.iface_entry->tag_stat_list_lock); } - spin_unlock_irqrestore(&iface_stat_list_lock, flags); + spin_unlock_bh(&iface_stat_list_lock); *eof = 1; - return outp - page; + return ppi.outp - page; } /*------------------------------------------*/ @@ -1444,7 +1640,7 @@ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) int ret; *res_procdir = proc_mkdir(module_procdirname, init_net.proc_net); if (!*res_procdir) { - pr_err("xt_qtaguid: failed to create proc/.../xt_qtaguid\n"); + pr_err("qtaguid: failed to create proc/.../xt_qtaguid\n"); ret = -ENOMEM; goto no_dir; } @@ -1452,7 +1648,7 @@ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) xt_qtaguid_ctrl_file = create_proc_entry("ctrl", proc_ctrl_perms, *res_procdir); if (!xt_qtaguid_ctrl_file) { - pr_err("xt_qtaguid: failed to create xt_qtaguid/ctrl " + pr_err("qtaguid: failed to create xt_qtaguid/ctrl " " file\n"); ret = -ENOMEM; goto no_ctrl_entry; @@ -1463,7 +1659,7 @@ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) xt_qtaguid_stats_file = create_proc_entry("stats", proc_stats_perms, *res_procdir); if (!xt_qtaguid_stats_file) { - pr_err("xt_qtaguid: failed to create xt_qtaguid/stats " + pr_err("qtaguid: failed to create xt_qtaguid/stats " "file\n"); ret = -ENOMEM; goto no_stats_entry; @@ -1505,7 +1701,8 @@ static int __init qtaguid_mt_init(void) return 0; } -/* TODO: allow unloading of the module. +/* + * TODO: allow unloading of the module. * For now stats are permanent. * Kconfig forces'y/n' and never an 'm'. */ From 77092f7579571ae4bd009dc7251a3bd47ea9d551 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 9 Aug 2011 11:53:11 -0700 Subject: [PATCH 1963/2556] netfilter: xt_qtaguid: recognize IPV6 interfaces. root is procfs privileged. * Allow tracking interfaces that only have an ipv6 address. Deal with ipv6 notifier chains that do NETDEV_UP without the rtnl_lock() * Allow root all access to procfs ctrl/stats. To disable all checks: echo 0 > /sys/module/xt_qtaguid/parameters/ctrl_write_gid echo 0 > /sys/module/xt_qtaguid/parameters/stats_readall_gid * Add CDEBUG define to enable pr_debug output specific to procfs ctrl/stats access. Change-Id: I9a469511d92fe42734daff6ea2326701312a161b Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 384 ++++++++++++++++++++++++++----------- 1 file changed, 273 insertions(+), 111 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 842d4d763e1ec..b04b4710574e4 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -8,15 +8,11 @@ * published by the Free Software Foundation. */ -/* - * TODO: support ipv6 for iface_stat. - * Currently if an iface is only v6 it will not have stats collected. - */ - /* #define DEBUG */ /* #define IDEBUG */ /* #define MDEBUG */ /* #define RDEBUG */ +/* #define CDEBUG */ /* Iface handling */ #ifdef IDEBUG @@ -36,6 +32,12 @@ #else #define RB_DEBUG(...) no_printk(__VA_ARGS__) #endif +/* procfs ctrl/stats handling */ +#ifdef CDEBUG +#define CT_DEBUG(...) pr_debug(__VA_ARGS__) +#else +#define CT_DEBUG(...) no_printk(__VA_ARGS__) +#endif #include #include @@ -44,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -200,8 +203,14 @@ struct iface_stat { static LIST_HEAD(iface_stat_list); static DEFINE_SPINLOCK(iface_stat_list_lock); +/* This is needed to create proc_dir_entries from atomic context. */ +struct iface_stat_work { + struct work_struct iface_work; + struct iface_stat *iface_entry; +}; + /* - * Track tag that this socket is transferring data for, and not necesseraly + * Track tag that this socket is transferring data for, and not necessarily * the uid that owns the socket. * This is the tag against which tag_stat.counters will be billed. */ @@ -450,7 +459,10 @@ static int get_active_counter_set(tag_t tag) return active_set; } -/* Find the entry for tracking the specified interface. */ +/* + * Find the entry for tracking the specified interface. + * Caller must hold iface_stat_list_lock + */ static struct iface_stat *get_iface_entry(const char *ifname) { struct iface_stat *iface_entry; @@ -463,70 +475,139 @@ static struct iface_stat *get_iface_entry(const char *ifname) /* Iterate over interfaces */ - spin_lock_bh(&iface_stat_list_lock); list_for_each_entry(iface_entry, &iface_stat_list, list) { if (!strcmp(ifname, iface_entry->ifname)) goto done; } iface_entry = NULL; done: - spin_unlock_bh(&iface_stat_list_lock); return iface_entry; } +static void iface_create_proc_worker(struct work_struct *work) +{ + struct proc_dir_entry *proc_entry; + struct iface_stat_work *isw = container_of(work, struct iface_stat_work, + iface_work); + struct iface_stat *new_iface = isw->iface_entry; + + /* iface_entries are not deleted, so safe to manipulate. */ + proc_entry = proc_mkdir(new_iface->ifname, iface_stat_procdir); + if (IS_ERR_OR_NULL(proc_entry)) { + pr_err("qtaguid: iface_stat: create_proc(): alloc failed.\n"); + kfree(isw); + return; + } + + new_iface->proc_ptr = proc_entry; + + create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->tx_bytes); + create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->rx_bytes); + create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->tx_packets); + create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->rx_packets); + create_proc_read_entry("active", proc_iface_perms, proc_entry, + read_proc_bool, &new_iface->active); + + IF_DEBUG("qtaguid: iface_stat: create_proc(): done " + "entry=%p dev=%s\n", new_iface, new_iface->ifname); + kfree(isw); +} + +/* Caller must hold iface_stat_list_lock */ +static struct iface_stat *iface_alloc(const char *ifname) +{ + struct iface_stat *new_iface; + struct iface_stat_work *isw; + + new_iface = kzalloc(sizeof(*new_iface), GFP_ATOMIC); + if (new_iface == NULL) { + pr_err("qtaguid: iface_stat: create(): failed to alloc iface_stat\n"); + return NULL; + } + new_iface->ifname = kstrdup(ifname, GFP_ATOMIC); + if (new_iface->ifname == NULL) { + pr_err("qtaguid: iface_stat: create(): failed to alloc ifname\n"); + kfree(new_iface); + return NULL; + } + spin_lock_init(&new_iface->tag_stat_list_lock); + new_iface->active = true; + new_iface->tag_stat_tree = RB_ROOT; + + /* + * ipv6 notifier chains are atomic :( + * No create_proc_read_entry() for you! + */ + isw = kmalloc(sizeof(*isw), GFP_ATOMIC); + if (!isw) { + pr_err("qtaguid: iface_stat: create(): " + "failed to alloc work for %s\n", new_iface->ifname); + kfree(new_iface->ifname); + kfree(new_iface); + return NULL; + } + isw->iface_entry = new_iface; + INIT_WORK(&isw->iface_work, iface_create_proc_worker); + schedule_work(&isw->iface_work); + list_add(&new_iface->list, &iface_stat_list); + return new_iface; +} + /* * Create a new entry for tracking the specified interface. * Do nothing if the entry already exists. * Called when an interface is configured with a valid IP address. */ -void iface_stat_create(const struct net_device *net_dev) +void iface_stat_create(const struct net_device *net_dev, + struct in_ifaddr *ifa) { - struct in_device *in_dev; - struct iface_stat *new_iface; - struct proc_dir_entry *proc_entry; + struct in_device *in_dev = NULL; const char *ifname; struct iface_stat *entry; __be32 ipaddr = 0; - struct in_ifaddr *ifa = NULL; - - ASSERT_RTNL(); /* No need for separate locking */ + struct iface_stat *new_iface; - IF_DEBUG("qtaguid: iface_stat: create(): netdev=%p->name=%s\n", - net_dev, net_dev ? net_dev->name : ""); + IF_DEBUG("qtaguid: iface_stat: create(): ifa=%p netdev=%p->name=%s\n", + ifa, net_dev, net_dev ? net_dev->name : ""); if (!net_dev) { pr_err("qtaguid: iface_stat: create(): no net dev!\n"); return; } - in_dev = __in_dev_get_rtnl(net_dev); - if (!in_dev) { - pr_err("qtaguid: iface_stat: create(): no inet dev!\n"); - return; - } - - IF_DEBUG("qtaguid: iface_stat: create(): in_dev=%p\n", in_dev); ifname = net_dev->name; - IF_DEBUG("qtaguid: iface_stat: create(): ifname=%p\n", ifname); - for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { - IF_DEBUG("qtaguid: iface_stat: create(): for(): " - "ifa=%p ifname=%p\n", ifa, ifname); - IF_DEBUG("qtaguid: iface_stat: create(): " - "ifname=%s ifa_label=%s\n", - ifname, ifa->ifa_label ? ifa->ifa_label : "(null)"); - if (ifa->ifa_label && !strcmp(ifname, ifa->ifa_label)) - break; + if (!ifa) { + in_dev = in_dev_get(net_dev); + if (!in_dev) { + pr_err("qtaguid: iface_stat: create(): " + "no inet dev for %s!\n", ifname); + return; + } + IF_DEBUG("qtaguid: iface_stat: create(): in_dev=%p ifname=%p\n", + in_dev, ifname); + for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { + IF_DEBUG("qtaguid: iface_stat: create(): " + "ifa=%p ifname=%s ifa_label=%s\n", + ifa, ifname, + ifa->ifa_label ? ifa->ifa_label : "(null)"); + if (ifa->ifa_label && !strcmp(ifname, ifa->ifa_label)) + break; + } } - if (ifa) { - ipaddr = ifa->ifa_local; - } else { + if (!ifa) { IF_DEBUG("qtaguid: iface_stat: create(): " "dev %s has no matching IP\n", ifname); - return; + goto done_put; } + ipaddr = ifa->ifa_local; - entry = get_iface_entry(net_dev->name); + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(ifname); if (entry != NULL) { IF_DEBUG("qtaguid: iface_stat: create(): dev %s entry=%p\n", ifname, entry); @@ -541,49 +622,91 @@ void iface_stat_create(const struct net_device *net_dev) "enable tracking of dev %s with ip=%pI4\n", ifname, &ipaddr); } - return; + goto done_unlock_put; } else if (ipv4_is_loopback(ipaddr)) { IF_DEBUG("qtaguid: iface_stat: create(): ignore loopback dev %s" " ip=%pI4\n", ifname, &ipaddr); - return; + goto done_unlock_put; } - new_iface = kzalloc(sizeof(*new_iface), GFP_KERNEL); - if (new_iface == NULL) { - pr_err("qtaguid: iface_stat: create(): failed to alloc iface_stat\n"); + new_iface = iface_alloc(ifname); + IF_DEBUG("qtaguid: iface_stat: create(): done " + "entry=%p dev=%s ip=%pI4\n", new_iface, ifname, &ipaddr); + +done_unlock_put: + spin_unlock_bh(&iface_stat_list_lock); +done_put: + if (in_dev) + in_dev_put(in_dev); +} + +void iface_stat_create_ipv6(const struct net_device *net_dev, + struct inet6_ifaddr *ifa) +{ + struct in_device *in_dev; + const char *ifname; + struct iface_stat *entry; + struct iface_stat *new_iface; + int addr_type; + + IF_DEBUG("qtaguid: iface_stat: create6(): ifa=%p netdev=%p->name=%s\n", + ifa, net_dev, net_dev ? net_dev->name : ""); + if (!net_dev) { + pr_err("qtaguid: iface_stat: create6(): no net dev!\n"); return; } - new_iface->ifname = kstrdup(ifname, GFP_KERNEL); - if (new_iface->ifname == NULL) { - pr_err("qtaguid: iface_stat: create(): failed to alloc ifname\n"); - kfree(new_iface); + ifname = net_dev->name; + + in_dev = in_dev_get(net_dev); + if (!in_dev) { + pr_err("qtaguid: iface_stat: create6(): no inet dev for %s!\n", + ifname); return; } - spin_lock_init(&new_iface->tag_stat_list_lock); - new_iface->active = true; + IF_DEBUG("qtaguid: iface_stat: create6(): in_dev=%p ifname=%p\n", + in_dev, ifname); + + if (!ifa) { + IF_DEBUG("qtaguid: iface_stat: create6(): " + "dev %s has no matching IP\n", + ifname); + goto done_put; + } + addr_type = ipv6_addr_type(&ifa->addr); - new_iface->tag_stat_tree = RB_ROOT; spin_lock_bh(&iface_stat_list_lock); - list_add(&new_iface->list, &iface_stat_list); - spin_unlock_bh(&iface_stat_list_lock); + entry = get_iface_entry(ifname); + if (entry != NULL) { + IF_DEBUG("qtaguid: iface_stat: create6(): dev %s entry=%p\n", + ifname, entry); + if (addr_type & IPV6_ADDR_LOOPBACK) { + entry->active = false; + IF_DEBUG("qtaguid: iface_stat: create6(): " + "disable tracking of loopback dev %s\n", + ifname); + } else { + entry->active = true; + IF_DEBUG("qtaguid: iface_stat: create6(): " + "enable tracking of dev %s with ip=%pI6c\n", + ifname, &ifa->addr); + } + goto done_unlock_put; + } else if (addr_type & IPV6_ADDR_LOOPBACK) { + IF_DEBUG("qtaguid: iface_stat: create6(): " + "ignore loopback dev %s ip=%pI6c\n", + ifname, &ifa->addr); + goto done_unlock_put; + } - proc_entry = proc_mkdir(ifname, iface_stat_procdir); - new_iface->proc_ptr = proc_entry; + new_iface = iface_alloc(ifname); + IF_DEBUG("qtaguid: iface_stat: create6(): done " + "entry=%p dev=%s ip=%pI6c\n", new_iface, ifname, &ifa->addr); - create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->tx_bytes); - create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->rx_bytes); - create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->tx_packets); - create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->rx_packets); - create_proc_read_entry("active", proc_iface_perms, proc_entry, - read_proc_bool, &new_iface->active); - - IF_DEBUG("qtaguid: iface_stat: create(): done " - "entry=%p dev=%s ip=%pI4\n", new_iface, ifname, &ipaddr); +done_unlock_put: + spin_unlock_bh(&iface_stat_list_lock); +done_put: + in_dev_put(in_dev); } static struct sock_tag *get_sock_stat_nl(const struct sock *sk) @@ -632,13 +755,14 @@ static void iface_stat_update(struct net_device *dev) { struct rtnl_link_stats64 dev_stats, *stats; struct iface_stat *entry; - stats = dev_get_stats(dev, &dev_stats); - ASSERT_RTNL(); + stats = dev_get_stats(dev, &dev_stats); + spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(dev->name); if (entry == NULL) { IF_DEBUG("qtaguid: iface_stat: dev %s monitor not found\n", dev->name); + spin_unlock_bh(&iface_stat_list_lock); return; } if (entry->active) { @@ -653,6 +777,7 @@ static void iface_stat_update(struct net_device *dev) IF_DEBUG("qtaguid: iface_stat: Did not update stats for " "dev %s which went down\n", dev->name); } + spin_unlock_bh(&iface_stat_list_lock); } static void tag_stat_update(struct tag_stat *tag_entry, @@ -715,10 +840,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, pr_err("qtaguid: iface_stat: interface %s not found\n", ifname); return; } - /* - * else { If the iface_entry becomes inactive, it is still ok - * to process the data. } - */ + /* It is ok to process data when an iface_entry is inactive */ MT_DEBUG("qtaguid: iface_stat: stat_update() got entry=%p\n", iface_entry); @@ -797,7 +919,7 @@ static int iface_netdev_event_handler(struct notifier_block *nb, case NETDEV_CHANGEADDR: /* MAC addr change */ case NETDEV_CHANGENAME: case NETDEV_FEAT_CHANGE: /* Might be usefull when cell type changes */ - iface_stat_create(dev); + iface_stat_create(dev, NULL); break; case NETDEV_UNREGISTER: iface_stat_update(dev); @@ -806,23 +928,47 @@ static int iface_netdev_event_handler(struct notifier_block *nb, return NOTIFY_DONE; } -static int iface_inetaddr_event_handler(struct notifier_block *nb, - unsigned long event, void *ptr) { +static int iface_inet6addr_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct inet6_ifaddr *ifa = ptr; + struct net_device *dev; + + if (unlikely(module_passive)) + return NOTIFY_DONE; + + IF_DEBUG("qtaguid: iface_stat: inet6addr_event(): " + "ev=0x%lx ifa=%p\n", + event, ifa); + + switch (event) { + case NETDEV_UP: + BUG_ON(!ifa || !ifa->idev); + dev = (struct net_device *)ifa->idev->dev; + iface_stat_create_ipv6(dev, ifa); + break; + } + return NOTIFY_DONE; +} +static int iface_inetaddr_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) +{ struct in_ifaddr *ifa = ptr; - struct in_device *in_dev = ifa->ifa_dev; - struct net_device *dev = in_dev->dev; + struct net_device *dev; if (unlikely(module_passive)) return NOTIFY_DONE; IF_DEBUG("qtaguid: iface_stat: inetaddr_event(): " - "ev=0x%lx netdev=%p->name=%s\n", - event, dev, dev ? dev->name : ""); + "ev=0x%lx ifa=%p\n", + event, ifa); switch (event) { case NETDEV_UP: - iface_stat_create(dev); + BUG_ON(!ifa || !ifa->ifa_dev); + dev = ifa->ifa_dev->dev; + iface_stat_create(dev, ifa); break; } return NOTIFY_DONE; @@ -836,6 +982,10 @@ static struct notifier_block iface_inetaddr_notifier_blk = { .notifier_call = iface_inetaddr_event_handler, }; +static struct notifier_block iface_inet6addr_notifier_blk = { + .notifier_call = iface_inet6addr_event_handler, +}; + static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) { int err; @@ -849,15 +999,25 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) err = register_netdevice_notifier(&iface_netdev_notifier_blk); if (err) { pr_err("qtaguid: iface_stat: failed to register dev event handler\n"); - goto err_unreg_nd; + goto err_zap_entry; } err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); if (err) { - pr_err("qtaguid: iface_stat: failed to register dev event handler\n"); - goto err_zap_entry; + pr_err("qtaguid: iface_stat: " + "failed to register ipv4 dev event handler\n"); + goto err_unreg_nd; + } + + err = register_inet6addr_notifier(&iface_inet6addr_notifier_blk); + if (err) { + pr_err("qtaguid: iface_stat: " + "failed to register ipv6 dev event handler\n"); + goto err_unreg_ip4_addr; } return 0; +err_unreg_ip4_addr: + unregister_inetaddr_notifier(&iface_inetaddr_notifier_blk); err_unreg_nd: unregister_netdevice_notifier(&iface_netdev_notifier_blk); err_zap_entry: @@ -1082,7 +1242,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, return 0; } - pr_debug("qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", + CT_DEBUG("qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", page, items_to_skip, char_count, *eof); if (*eof) @@ -1096,7 +1256,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, continue; sock_tag_entry = rb_entry(node, struct sock_tag, node); uid = get_uid_from_tag(sock_tag_entry->tag); - pr_debug("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%d)\n", + CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%d)\n", sock_tag_entry->sk, sock_tag_entry->tag, uid); @@ -1119,7 +1279,9 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, static bool can_manipulate_uids(void) { - return !proc_ctrl_write_gid || in_egroup_p(proc_ctrl_write_gid); + /* root pwnd */ + return unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_gid) + || in_egroup_p(proc_ctrl_write_gid); } static bool can_impersonate_uid(uid_t uid) @@ -1129,7 +1291,9 @@ static bool can_impersonate_uid(uid_t uid) static bool can_read_other_uid_stats(uid_t uid) { - return uid == current_fsuid() || !proc_ctrl_write_gid + /* root pwnd */ + return unlikely(!current_fsuid()) || uid == current_fsuid() + || unlikely(!proc_stats_readall_gid) || in_egroup_p(proc_stats_readall_gid); } @@ -1151,9 +1315,9 @@ static int ctrl_cmd_delete(const char *input) struct tag_stat *ts_entry; struct tag_counter_set *tcs_entry; - pr_debug("qtaguid: ctrl_delete(%s): entered\n", input); + CT_DEBUG("qtaguid: ctrl_delete(%s): entered\n", input); argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid); - pr_debug("qtaguid: ctrl_delete(%s): argc=%d cmd=%c " + CT_DEBUG("qtaguid: ctrl_delete(%s): argc=%d cmd=%c " "acct_tag=0x%llx uid=%u\n", input, argc, cmd, acct_tag, uid); if (argc < 2) { @@ -1185,7 +1349,7 @@ static int ctrl_cmd_delete(const char *input) continue; if (!acct_tag || st_entry->tag == tag) { - pr_debug("qtaguid: ctrl_delete(): " + CT_DEBUG("qtaguid: ctrl_delete(): " "erase st: sk=%p tag=0x%llx (uid=%d)\n", st_entry->sk, st_entry->tag, @@ -1202,7 +1366,7 @@ static int ctrl_cmd_delete(const char *input) spin_lock_bh(&tag_counter_set_list_lock); tcs_entry = tag_counter_set_tree_search(&tag_counter_set_tree, tag); if (tcs_entry) { - pr_debug("qtaguid: ctrl_delete(): " + CT_DEBUG("qtaguid: ctrl_delete(): " "erase tcs: tag=0x%llx (uid=%d) set=%d\n", tcs_entry->tn.tag, get_uid_from_tag(tcs_entry->tn.tag), @@ -1218,7 +1382,6 @@ static int ctrl_cmd_delete(const char *input) */ spin_lock_bh(&iface_stat_list_lock); list_for_each_entry(iface_entry, &iface_stat_list, list) { - spin_lock_bh(&iface_entry->tag_stat_list_lock); node = rb_first(&iface_entry->tag_stat_tree); while (node) { @@ -1228,7 +1391,7 @@ static int ctrl_cmd_delete(const char *input) if (entry_uid != uid) continue; if (!acct_tag || ts_entry->tn.tag == tag) { - pr_debug("qtaguid: ctrl_delete(): " + CT_DEBUG("qtaguid: ctrl_delete(): " "erase ts: %s 0x%llx %u\n", iface_entry->ifname, get_atag_from_tag(ts_entry->tn.tag), @@ -1239,14 +1402,13 @@ static int ctrl_cmd_delete(const char *input) } } spin_unlock_bh(&iface_entry->tag_stat_list_lock); - } spin_unlock_bh(&iface_stat_list_lock); res = 0; err: - pr_debug("qtaguid: ctrl_delete(%s) res=%d\n", input, res); + CT_DEBUG("qtaguid: ctrl_delete(%s) res=%d\n", input, res); return res; } @@ -1259,9 +1421,9 @@ static int ctrl_cmd_counter_set(const char *input) struct tag_counter_set *tcs; int counter_set; - pr_debug("qtaguid: ctrl_counterset(%s): entered\n", input); + CT_DEBUG("qtaguid: ctrl_counterset(%s): entered\n", input); argc = sscanf(input, "%c %d %u", &cmd, &counter_set, &uid); - pr_debug("qtaguid: ctrl_counterset(%s): argc=%d cmd=%c " + CT_DEBUG("qtaguid: ctrl_counterset(%s): argc=%d cmd=%c " "set=%d uid=%u\n", input, argc, cmd, counter_set, uid); if (argc != 3) { @@ -1296,7 +1458,7 @@ static int ctrl_cmd_counter_set(const char *input) } tcs->tn.tag = tag; tag_counter_set_tree_insert(tcs, &tag_counter_set_tree); - pr_debug("qtaguid: ctrl_counterset(%s): added tcs tag=0x%llx " + CT_DEBUG("qtaguid: ctrl_counterset(%s): added tcs tag=0x%llx " "(uid=%d) set=%d\n", input, tag, get_uid_from_tag(tag), counter_set); } @@ -1306,7 +1468,7 @@ static int ctrl_cmd_counter_set(const char *input) res = 0; err: - pr_debug("qtaguid: ctrl_counterset(%s) res=%d\n", input, res); + CT_DEBUG("qtaguid: ctrl_counterset(%s) res=%d\n", input, res); return res; } @@ -1322,7 +1484,7 @@ static int ctrl_cmd_tag(const char *input) /* Unassigned args will get defaulted later. */ argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid); - pr_debug("qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d " + CT_DEBUG("qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d " "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd, acct_tag, uid); if (argc < 2) { @@ -1370,14 +1532,14 @@ static int ctrl_cmd_tag(const char *input) } spin_unlock_bh(&sock_tag_list_lock); - pr_debug("qtaguid: tag: sock_tag_entry->sk=%p " + CT_DEBUG("qtaguid: tag: sock_tag_entry->sk=%p " "...->tag=0x%llx (uid=%u)\n", sock_tag_entry->sk, sock_tag_entry->tag, get_uid_from_tag(sock_tag_entry->tag)); res = 0; err: - pr_debug("qtaguid: ctrl_tag(%s) res=%d\n", input, res); + CT_DEBUG("qtaguid: ctrl_tag(%s) res=%d\n", input, res); return res; } @@ -1389,9 +1551,9 @@ static int ctrl_cmd_untag(const char *input) int res, argc; struct sock_tag *sock_tag_entry; - pr_debug("qtaguid: ctrl_untag(%s): entered\n", input); + CT_DEBUG("qtaguid: ctrl_untag(%s): entered\n", input); argc = sscanf(input, "%c %d", &cmd, &sock_fd); - pr_debug("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", + CT_DEBUG("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", input, argc, cmd, sock_fd); if (argc < 2) { res = -EINVAL; @@ -1418,7 +1580,7 @@ static int ctrl_cmd_untag(const char *input) res = 0; err: - pr_debug("qtaguid: ctrl_untag(%s): res=%d\n", input, res); + CT_DEBUG("qtaguid: ctrl_untag(%s): res=%d\n", input, res); return res; } @@ -1427,7 +1589,7 @@ static int qtaguid_ctrl_parse(const char *input, int count) char cmd; int res; - pr_debug("qtaguid: ctrl(%s): entered\n", input); + CT_DEBUG("qtaguid: ctrl(%s): entered\n", input); cmd = input[0]; /* Collect params for commands */ switch (cmd) { @@ -1454,7 +1616,7 @@ static int qtaguid_ctrl_parse(const char *input, int count) if (!res) res = count; err: - pr_debug("qtaguid: ctrl(%s): res=%d\n", input, res); + CT_DEBUG("qtaguid: ctrl(%s): res=%d\n", input, res); return res; } @@ -1505,7 +1667,7 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) tag_t tag = ppi->ts_entry->tn.tag; uid_t stat_uid = get_uid_from_tag(tag); if (!can_read_other_uid_stats(stat_uid)) { - pr_debug("qtaguid: insufficient priv for stat line:" + CT_DEBUG("qtaguid: insufficient priv for stat line:" "%s 0x%llx %u\n", ppi->iface_entry->ifname, get_atag_from_tag(tag), stat_uid); @@ -1590,7 +1752,7 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, return 0; } - pr_debug("qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " + CT_DEBUG("qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " "char_count=%d *eof=%d\n", page, *num_items_returned, items_to_skip, char_count, *eof); From d8c0067e0607e9261982de4a3f53f94e0aff8bd5 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Wed, 17 Aug 2011 16:43:00 -0700 Subject: [PATCH 1964/2556] netfilter: xt_qtaguid: Fix socket refcounts when tagging * Don't hold the sockets after tagging. sockfd_lookup() does a get() on the associated file. There was no matching put() so a closed socket could never be freed. * Don't rely on struct member order for tag_node The structs that had a struct tag_node member would work with the *_tree_* routines only because tag_node was 1st. * Improve debug messages Provide info on who the caller is. Use unsigned int for uid. * Only process NETDEV_UP events. * Pacifier: disable netfilter matching. Leave .../stats header. Change-Id: Iccb8ae3cca9608210c417597287a2391010dff2c Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 309 ++++++++++++++++++++++--------------- 1 file changed, 186 insertions(+), 123 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index b04b4710574e4..b9dcfde99762e 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -183,7 +183,6 @@ struct tag_stat { * matching parent uid_tag. */ struct data_counters *parent_counters; - struct proc_dir_entry *proc_ptr; }; struct iface_stat { @@ -215,8 +214,11 @@ struct iface_stat_work { * This is the tag against which tag_stat.counters will be billed. */ struct sock_tag { - struct rb_node node; - struct sock *sk; + struct rb_node sock_node; + struct sock *sk; /* Only used as a number, never dereferenced */ + /* The socket is needed for sockfd_put() */ + struct socket *socket; + tag_t tag; }; @@ -345,24 +347,31 @@ static void tag_node_tree_insert(struct tag_node *data, struct rb_root *root) static void tag_stat_tree_insert(struct tag_stat *data, struct rb_root *root) { - tag_node_tree_insert((struct tag_node *)data, root); + tag_node_tree_insert(&data->tn, root); } static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag) { - return (struct tag_stat *)tag_node_tree_search(root, tag); + struct tag_node *node = tag_node_tree_search(root, tag); + if (!node) + return NULL; + return rb_entry(&node->node, struct tag_stat, tn.node); } static void tag_counter_set_tree_insert(struct tag_counter_set *data, struct rb_root *root) { - tag_node_tree_insert((struct tag_node *)data, root); + tag_node_tree_insert(&data->tn, root); } static struct tag_counter_set *tag_counter_set_tree_search(struct rb_root *root, tag_t tag) { - return (struct tag_counter_set *)tag_node_tree_search(root, tag); + struct tag_node *node = tag_node_tree_search(root, tag); + if (!node) + return NULL; + return rb_entry(&node->node, struct tag_counter_set, tn.node); + } static struct sock_tag *sock_tag_tree_search(struct rb_root *root, @@ -371,7 +380,8 @@ static struct sock_tag *sock_tag_tree_search(struct rb_root *root, struct rb_node *node = root->rb_node; while (node) { - struct sock_tag *data = rb_entry(node, struct sock_tag, node); + struct sock_tag *data = rb_entry(node, struct sock_tag, + sock_node); ptrdiff_t result = sk - data->sk; if (result < 0) node = node->rb_left; @@ -389,7 +399,8 @@ static void sock_tag_tree_insert(struct sock_tag *data, struct rb_root *root) /* Figure out where to put new node */ while (*new) { - struct sock_tag *this = rb_entry(*new, struct sock_tag, node); + struct sock_tag *this = rb_entry(*new, struct sock_tag, + sock_node); ptrdiff_t result = data->sk - this->sk; parent = *new; if (result < 0) @@ -401,8 +412,8 @@ static void sock_tag_tree_insert(struct sock_tag *data, struct rb_root *root) } /* Add new node and rebalance tree. */ - rb_link_node(&data->node, parent, new); - rb_insert_color(&data->node, root); + rb_link_node(&data->sock_node, parent, new); + rb_insert_color(&data->sock_node, root); } static int read_proc_u64(char *page, char **start, off_t off, @@ -412,6 +423,7 @@ static int read_proc_u64(char *page, char **start, off_t off, uint64_t value; char *p = page; uint64_t *iface_entry = data; + if (!data) return 0; @@ -430,6 +442,7 @@ static int read_proc_bool(char *page, char **start, off_t off, bool value; char *p = page; bool *bool_entry = data; + if (!data) return 0; @@ -447,7 +460,7 @@ static int get_active_counter_set(tag_t tag) struct tag_counter_set *tcs; MT_DEBUG("qtaguid: get_active_counter_set(tag=0x%llx)" - " (uid=%d)\n", + " (uid=%u)\n", tag, get_uid_from_tag(tag)); /* For now we only handle UID tags for active sets */ tag = get_utag_from_tag(tag); @@ -469,11 +482,10 @@ static struct iface_stat *get_iface_entry(const char *ifname) /* Find the entry for tracking the specified tag within the interface */ if (ifname == NULL) { - pr_info("iface_stat: NULL device name\n"); + pr_info("qtaguid: iface_stat: get() NULL device name\n"); return NULL; } - /* Iterate over interfaces */ list_for_each_entry(iface_entry, &iface_stat_list, list) { if (!strcmp(ifname, iface_entry->ifname)) @@ -525,12 +537,14 @@ static struct iface_stat *iface_alloc(const char *ifname) new_iface = kzalloc(sizeof(*new_iface), GFP_ATOMIC); if (new_iface == NULL) { - pr_err("qtaguid: iface_stat: create(): failed to alloc iface_stat\n"); + pr_err("qtaguid: iface_stat: create(%s): " + "iface_stat alloc failed\n", ifname); return NULL; } new_iface->ifname = kstrdup(ifname, GFP_ATOMIC); if (new_iface->ifname == NULL) { - pr_err("qtaguid: iface_stat: create(): failed to alloc ifname\n"); + pr_err("qtaguid: iface_stat: create(%s): " + "ifname alloc failed\n", ifname); kfree(new_iface); return NULL; } @@ -544,8 +558,8 @@ static struct iface_stat *iface_alloc(const char *ifname) */ isw = kmalloc(sizeof(*isw), GFP_ATOMIC); if (!isw) { - pr_err("qtaguid: iface_stat: create(): " - "failed to alloc work for %s\n", new_iface->ifname); + pr_err("qtaguid: iface_stat: create(%s): " + "work alloc failed\n", new_iface->ifname); kfree(new_iface->ifname); kfree(new_iface); return NULL; @@ -571,10 +585,11 @@ void iface_stat_create(const struct net_device *net_dev, __be32 ipaddr = 0; struct iface_stat *new_iface; - IF_DEBUG("qtaguid: iface_stat: create(): ifa=%p netdev=%p->name=%s\n", - ifa, net_dev, net_dev ? net_dev->name : ""); + IF_DEBUG("qtaguid: iface_stat: create(%s): ifa=%p netdev=%p\n", + net_dev ? net_dev->name : "?", + ifa, net_dev); if (!net_dev) { - pr_err("qtaguid: iface_stat: create(): no net dev!\n"); + pr_err("qtaguid: iface_stat: create(): no net dev\n"); return; } @@ -582,16 +597,16 @@ void iface_stat_create(const struct net_device *net_dev, if (!ifa) { in_dev = in_dev_get(net_dev); if (!in_dev) { - pr_err("qtaguid: iface_stat: create(): " - "no inet dev for %s!\n", ifname); + pr_err("qtaguid: iface_stat: create(%s): no inet dev\n", + ifname); return; } - IF_DEBUG("qtaguid: iface_stat: create(): in_dev=%p ifname=%p\n", - in_dev, ifname); + IF_DEBUG("qtaguid: iface_stat: create(%s): in_dev=%p\n", + ifname, in_dev); for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { - IF_DEBUG("qtaguid: iface_stat: create(): " - "ifa=%p ifname=%s ifa_label=%s\n", - ifa, ifname, + IF_DEBUG("qtaguid: iface_stat: create(%s): " + "ifa=%p ifa_label=%s\n", + ifname, ifa, ifa->ifa_label ? ifa->ifa_label : "(null)"); if (ifa->ifa_label && !strcmp(ifname, ifa->ifa_label)) break; @@ -599,8 +614,7 @@ void iface_stat_create(const struct net_device *net_dev, } if (!ifa) { - IF_DEBUG("qtaguid: iface_stat: create(): " - "dev %s has no matching IP\n", + IF_DEBUG("qtaguid: iface_stat: create(%s): no matching IP\n", ifname); goto done_put; } @@ -609,29 +623,29 @@ void iface_stat_create(const struct net_device *net_dev, spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(ifname); if (entry != NULL) { - IF_DEBUG("qtaguid: iface_stat: create(): dev %s entry=%p\n", + IF_DEBUG("qtaguid: iface_stat: create(%s): entry=%p\n", ifname, entry); if (ipv4_is_loopback(ipaddr)) { entry->active = false; - IF_DEBUG("qtaguid: iface_stat: create(): " - "disable tracking of loopback dev %s\n", + IF_DEBUG("qtaguid: iface_stat: create(%s): " + "disable tracking of loopback dev\n", ifname); } else { entry->active = true; - IF_DEBUG("qtaguid: iface_stat: create(): " - "enable tracking of dev %s with ip=%pI4\n", + IF_DEBUG("qtaguid: iface_stat: create(%s): " + "enable tracking. ip=%pI4\n", ifname, &ipaddr); } goto done_unlock_put; } else if (ipv4_is_loopback(ipaddr)) { - IF_DEBUG("qtaguid: iface_stat: create(): ignore loopback dev %s" - " ip=%pI4\n", ifname, &ipaddr); + IF_DEBUG("qtaguid: iface_stat: create(%s): " + "ignore loopback dev. ip=%pI4\n", ifname, &ipaddr); goto done_unlock_put; } new_iface = iface_alloc(ifname); - IF_DEBUG("qtaguid: iface_stat: create(): done " - "entry=%p dev=%s ip=%pI4\n", new_iface, ifname, &ipaddr); + IF_DEBUG("qtaguid: iface_stat: create(%s): done " + "entry=%p ip=%pI4\n", ifname, new_iface, &ipaddr); done_unlock_put: spin_unlock_bh(&iface_stat_list_lock); @@ -659,17 +673,16 @@ void iface_stat_create_ipv6(const struct net_device *net_dev, in_dev = in_dev_get(net_dev); if (!in_dev) { - pr_err("qtaguid: iface_stat: create6(): no inet dev for %s!\n", + pr_err("qtaguid: iface_stat: create6(%s): no inet dev\n", ifname); return; } - IF_DEBUG("qtaguid: iface_stat: create6(): in_dev=%p ifname=%p\n", - in_dev, ifname); + IF_DEBUG("qtaguid: iface_stat: create6(%s): in_dev=%p\n", + ifname, in_dev); if (!ifa) { - IF_DEBUG("qtaguid: iface_stat: create6(): " - "dev %s has no matching IP\n", + IF_DEBUG("qtaguid: iface_stat: create6(%s): no matching IP\n", ifname); goto done_put; } @@ -678,30 +691,30 @@ void iface_stat_create_ipv6(const struct net_device *net_dev, spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(ifname); if (entry != NULL) { - IF_DEBUG("qtaguid: iface_stat: create6(): dev %s entry=%p\n", + IF_DEBUG("qtaguid: iface_stat: create6(%s): entry=%p\n", ifname, entry); if (addr_type & IPV6_ADDR_LOOPBACK) { entry->active = false; - IF_DEBUG("qtaguid: iface_stat: create6(): " - "disable tracking of loopback dev %s\n", + IF_DEBUG("qtaguid: iface_stat: create6(%s): " + "disable tracking of loopback dev\n", ifname); } else { entry->active = true; - IF_DEBUG("qtaguid: iface_stat: create6(): " - "enable tracking of dev %s with ip=%pI6c\n", + IF_DEBUG("qtaguid: iface_stat: create6(%s): " + "enable tracking. ip=%pI6c\n", ifname, &ifa->addr); } goto done_unlock_put; } else if (addr_type & IPV6_ADDR_LOOPBACK) { - IF_DEBUG("qtaguid: iface_stat: create6(): " - "ignore loopback dev %s ip=%pI6c\n", + IF_DEBUG("qtaguid: iface_stat: create6(%s): " + "ignore loopback dev. ip=%pI6c\n", ifname, &ifa->addr); goto done_unlock_put; } new_iface = iface_alloc(ifname); - IF_DEBUG("qtaguid: iface_stat: create6(): done " - "entry=%p dev=%s ip=%pI6c\n", new_iface, ifname, &ifa->addr); + IF_DEBUG("qtaguid: iface_stat: create6(%s): done " + "entry=%p ip=%pI6c\n", ifname, new_iface, &ifa->addr); done_unlock_put: spin_unlock_bh(&iface_stat_list_lock); @@ -760,22 +773,22 @@ static void iface_stat_update(struct net_device *dev) spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(dev->name); if (entry == NULL) { - IF_DEBUG("qtaguid: iface_stat: dev %s monitor not found\n", + IF_DEBUG("qtaguid: iface_stat_update: dev=%s not tracked\n", dev->name); spin_unlock_bh(&iface_stat_list_lock); return; } + IF_DEBUG("qtaguid: iface_stat_update: dev=%s entry=%p\n", + dev->name, entry); if (entry->active) { entry->tx_bytes += stats->tx_bytes; entry->tx_packets += stats->tx_packets; entry->rx_bytes += stats->rx_bytes; entry->rx_packets += stats->rx_packets; entry->active = false; - IF_DEBUG("qtaguid: iface_stat: Updating stats for " - "dev %s which went down\n", dev->name); } else { - IF_DEBUG("qtaguid: iface_stat: Did not update stats for " - "dev %s which went down\n", dev->name); + IF_DEBUG("qtaguid: iface_stat_update: dev=%s inactive\n", + dev->name); } spin_unlock_bh(&iface_stat_list_lock); } @@ -785,7 +798,7 @@ static void tag_stat_update(struct tag_stat *tag_entry, { int active_set; active_set = get_active_counter_set(tag_entry->tn.tag); - MT_DEBUG("qtaguid: tag_stat_update(tag=0x%llx (uid=%d) set=%d " + MT_DEBUG("qtaguid: tag_stat_update(tag=0x%llx (uid=%u) set=%d " "dir=%d proto=%d bytes=%d)\n", tag_entry->tn.tag, get_uid_from_tag(tag_entry->tn.tag), active_set, direction, proto, bytes); @@ -806,11 +819,11 @@ static struct tag_stat *create_if_tag_stat(struct iface_stat *iface_entry, { struct tag_stat *new_tag_stat_entry = NULL; IF_DEBUG("qtaguid: iface_stat: create_if_tag_stat(): ife=%p tag=0x%llx" - " (uid=%d)\n", + " (uid=%u)\n", iface_entry, tag, get_uid_from_tag(tag)); new_tag_stat_entry = kzalloc(sizeof(*new_tag_stat_entry), GFP_ATOMIC); if (!new_tag_stat_entry) { - pr_err("qtaguid: iface_stat: failed to alloc new tag entry\n"); + pr_err("qtaguid: iface_stat: tag stat alloc failed\n"); goto done; } new_tag_stat_entry->tn.tag = tag; @@ -831,19 +844,20 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, struct iface_stat *iface_entry; struct tag_stat *new_tag_stat; MT_DEBUG("qtaguid: if_tag_stat_update(ifname=%s " - "uid=%d sk=%p dir=%d proto=%d bytes=%d)\n", + "uid=%u sk=%p dir=%d proto=%d bytes=%d)\n", ifname, uid, sk, direction, proto, bytes); iface_entry = get_iface_entry(ifname); if (!iface_entry) { - pr_err("qtaguid: iface_stat: interface %s not found\n", ifname); + pr_err("qtaguid: iface_stat: stat_update() %s not found\n", + ifname); return; } /* It is ok to process data when an iface_entry is inactive */ - MT_DEBUG("qtaguid: iface_stat: stat_update() got entry=%p\n", - iface_entry); + MT_DEBUG("qtaguid: iface_stat: stat_update() dev=%s entry=%p\n", + ifname, iface_entry); /* * Look for a tagged sock. @@ -860,7 +874,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, tag = combine_atag_with_uid(acct_tag, uid); } MT_DEBUG("qtaguid: iface_stat: stat_update(): " - " looking for tag=0x%llx (uid=%d) in ife=%p\n", + " looking for tag=0x%llx (uid=%u) in ife=%p\n", tag, get_uid_from_tag(tag), iface_entry); /* Loop over tag list under this interface for {acct_tag,uid_tag} */ spin_lock_bh(&iface_entry->tag_stat_list_lock); @@ -913,12 +927,6 @@ static int iface_netdev_event_handler(struct notifier_block *nb, switch (event) { case NETDEV_UP: - case NETDEV_REBOOT: - case NETDEV_CHANGE: - case NETDEV_REGISTER: /* Most likely no IP */ - case NETDEV_CHANGEADDR: /* MAC addr change */ - case NETDEV_CHANGENAME: - case NETDEV_FEAT_CHANGE: /* Might be usefull when cell type changes */ iface_stat_create(dev, NULL); break; case NETDEV_UNREGISTER: @@ -992,25 +1000,26 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) iface_stat_procdir = proc_mkdir(iface_stat_procdirname, parent_procdir); if (!iface_stat_procdir) { - pr_err("qtaguid: iface_stat: failed to create proc entry\n"); + pr_err("qtaguid: iface_stat: init failed to create proc entry\n"); err = -1; goto err; } err = register_netdevice_notifier(&iface_netdev_notifier_blk); if (err) { - pr_err("qtaguid: iface_stat: failed to register dev event handler\n"); + pr_err("qtaguid: iface_stat: init " + "failed to register dev event handler\n"); goto err_zap_entry; } err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); if (err) { - pr_err("qtaguid: iface_stat: " + pr_err("qtaguid: iface_stat: init " "failed to register ipv4 dev event handler\n"); goto err_unreg_nd; } err = register_inet6addr_notifier(&iface_inet6addr_notifier_blk); if (err) { - pr_err("qtaguid: iface_stat: " + pr_err("qtaguid: iface_stat: init " "failed to register ipv6 dev event handler\n"); goto err_unreg_ip4_addr; } @@ -1116,6 +1125,9 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) uid_t sock_uid; bool res; + if (unlikely(module_passive)) + return (info->match ^ info->invert) == 0; + MT_DEBUG("qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n", par->hooknum, skb, par->in, par->out, par->family); @@ -1145,7 +1157,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) par->hooknum, sk, sk->sk_socket, sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); filp = sk->sk_socket ? sk->sk_socket->file : NULL; - MT_DEBUG("qtaguid[%d]: filp...uid=%d\n", + MT_DEBUG("qtaguid[%d]: filp...uid=%u\n", par->hooknum, filp ? filp->f_cred->fsuid : -1); } @@ -1254,12 +1266,13 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, node = rb_next(node)) { if (item_index++ < items_to_skip) continue; - sock_tag_entry = rb_entry(node, struct sock_tag, node); + sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); uid = get_uid_from_tag(sock_tag_entry->tag); - CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%d)\n", - sock_tag_entry->sk, - sock_tag_entry->tag, - uid); + CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u)\n", + sock_tag_entry->sk, + sock_tag_entry->tag, + uid + ); len = snprintf(outp, char_count, "sock=%p tag=0x%llx (uid=%u)\n", sock_tag_entry->sk, sock_tag_entry->tag, uid); @@ -1315,10 +1328,9 @@ static int ctrl_cmd_delete(const char *input) struct tag_stat *ts_entry; struct tag_counter_set *tcs_entry; - CT_DEBUG("qtaguid: ctrl_delete(%s): entered\n", input); argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid); CT_DEBUG("qtaguid: ctrl_delete(%s): argc=%d cmd=%c " - "acct_tag=0x%llx uid=%u\n", input, argc, cmd, + "user_tag=0x%llx uid=%u\n", input, argc, cmd, acct_tag, uid); if (argc < 2) { res = -EINVAL; @@ -1332,8 +1344,9 @@ static int ctrl_cmd_delete(const char *input) if (argc < 3) { uid = current_fsuid(); } else if (!can_impersonate_uid(uid)) { - pr_info("qtaguid: ctrl_delete(%s): insuficient priv\n", - input); + pr_info("qtaguid: ctrl_delete(%s): " + "insufficient priv from pid=%u uid=%u\n", + input, current->pid, current_fsuid()); res = -EPERM; goto err; } @@ -1342,7 +1355,7 @@ static int ctrl_cmd_delete(const char *input) spin_lock_bh(&sock_tag_list_lock); node = rb_first(&sock_tag_tree); while (node) { - st_entry = rb_entry(node, struct sock_tag, node); + st_entry = rb_entry(node, struct sock_tag, sock_node); entry_uid = get_uid_from_tag(st_entry->tag); node = rb_next(node); if (entry_uid != uid) @@ -1350,11 +1363,12 @@ static int ctrl_cmd_delete(const char *input) if (!acct_tag || st_entry->tag == tag) { CT_DEBUG("qtaguid: ctrl_delete(): " - "erase st: sk=%p tag=0x%llx (uid=%d)\n", + "erase st: sk=%p tag=0x%llx (uid=%u)\n", st_entry->sk, st_entry->tag, entry_uid); - rb_erase(&st_entry->node, &sock_tag_tree); + rb_erase(&st_entry->sock_node, &sock_tag_tree); + sockfd_put(st_entry->socket); kfree(st_entry); } } @@ -1367,7 +1381,7 @@ static int ctrl_cmd_delete(const char *input) tcs_entry = tag_counter_set_tree_search(&tag_counter_set_tree, tag); if (tcs_entry) { CT_DEBUG("qtaguid: ctrl_delete(): " - "erase tcs: tag=0x%llx (uid=%d) set=%d\n", + "erase tcs: tag=0x%llx (uid=%u) set=%d\n", tcs_entry->tn.tag, get_uid_from_tag(tcs_entry->tn.tag), tcs_entry->active_set); @@ -1408,7 +1422,6 @@ static int ctrl_cmd_delete(const char *input) res = 0; err: - CT_DEBUG("qtaguid: ctrl_delete(%s) res=%d\n", input, res); return res; } @@ -1421,7 +1434,6 @@ static int ctrl_cmd_counter_set(const char *input) struct tag_counter_set *tcs; int counter_set; - CT_DEBUG("qtaguid: ctrl_counterset(%s): entered\n", input); argc = sscanf(input, "%c %d %u", &cmd, &counter_set, &uid); CT_DEBUG("qtaguid: ctrl_counterset(%s): argc=%d cmd=%c " "set=%d uid=%u\n", input, argc, cmd, @@ -1437,8 +1449,9 @@ static int ctrl_cmd_counter_set(const char *input) goto err; } if (!can_manipulate_uids()) { - pr_info("qtaguid: ctrl_counterset(%s): insufficient priv\n", - input); + pr_info("qtaguid: ctrl_counterset(%s): " + "insufficient priv from pid=%u uid=%u\n", + input, current->pid, current_fsuid()); res = -EPERM; goto err; } @@ -1459,7 +1472,7 @@ static int ctrl_cmd_counter_set(const char *input) tcs->tn.tag = tag; tag_counter_set_tree_insert(tcs, &tag_counter_set_tree); CT_DEBUG("qtaguid: ctrl_counterset(%s): added tcs tag=0x%llx " - "(uid=%d) set=%d\n", + "(uid=%u) set=%d\n", input, tag, get_uid_from_tag(tag), counter_set); } tcs->active_set = counter_set; @@ -1468,7 +1481,6 @@ static int ctrl_cmd_counter_set(const char *input) res = 0; err: - CT_DEBUG("qtaguid: ctrl_counterset(%s) res=%d\n", input, res); return res; } @@ -1479,6 +1491,7 @@ static int ctrl_cmd_tag(const char *input) uid_t uid = 0; tag_t acct_tag = 0; struct socket *el_socket; + int refcnt = -1; int res, argc; struct sock_tag *sock_tag_entry; @@ -1491,55 +1504,80 @@ static int ctrl_cmd_tag(const char *input) res = -EINVAL; goto err; } - el_socket = sockfd_lookup(sock_fd, &res); + el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ if (!el_socket) { pr_info("qtaguid: ctrl_tag(%s): failed to lookup" " sock_fd=%d err=%d\n", input, sock_fd, res); goto err; } + refcnt = atomic_read(&el_socket->file->f_count); + CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%d\n", + input, refcnt); if (argc < 3) { acct_tag = 0; } else if (!valid_atag(acct_tag)) { pr_info("qtaguid: ctrl_tag(%s): invalid tag\n", input); res = -EINVAL; - goto err; - } + goto err_put; + } + CT_DEBUG("qtaguid: ctrl_tag(%s): " + "uid=%u euid=%u fsuid=%u " + "in_group=%d in_egroup=%d\n", + input, current_uid(), current_euid(), current_fsuid(), + in_group_p(proc_stats_readall_gid), + in_egroup_p(proc_stats_readall_gid)); if (argc < 4) { uid = current_fsuid(); } else if (!can_impersonate_uid(uid)) { - pr_info("qtaguid: ctrl_tag(%s): insuficient priv\n", - input); + pr_info("qtaguid: ctrl_tag(%s): " + "insufficient priv from pid=%u uid=%u\n", + input, current->pid, current_fsuid()); res = -EPERM; - goto err; + goto err_put; } spin_lock_bh(&sock_tag_list_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); if (sock_tag_entry) { + /* + * This is a re-tagging, so release the sock_fd that was + * locked at the time of the 1st tagging. + */ + sockfd_put(sock_tag_entry->socket); + refcnt--; sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid); } else { sock_tag_entry = kzalloc(sizeof(*sock_tag_entry), GFP_ATOMIC); if (!sock_tag_entry) { + pr_err("qtaguid: ctrl_tag(%s): " + "socket tag alloc failed\n", + input); + spin_unlock_bh(&sock_tag_list_lock); res = -ENOMEM; - goto err; + goto err_put; } sock_tag_entry->sk = el_socket->sk; + sock_tag_entry->socket = el_socket; sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid); sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); } spin_unlock_bh(&sock_tag_list_lock); + /* We keep the ref to the socket (file) until it is untagged */ + CT_DEBUG("qtaguid: ctrl_tag(%s): done. socket->...->f_count=%d\n", + input, + el_socket ? atomic_read(&el_socket->file->f_count) : -1); + return 0; - CT_DEBUG("qtaguid: tag: sock_tag_entry->sk=%p " - "...->tag=0x%llx (uid=%u)\n", - sock_tag_entry->sk, sock_tag_entry->tag, - get_uid_from_tag(sock_tag_entry->tag)); - res = 0; - +err_put: + /* Release the sock_fd that was grabbed by sockfd_lookup(). */ + sockfd_put(el_socket); + refcnt--; err: - CT_DEBUG("qtaguid: ctrl_tag(%s) res=%d\n", input, res); + CT_DEBUG("qtaguid: ctrl_tag(%s): done. socket->...->f_count=%d\n", + input, refcnt); return res; } @@ -1548,10 +1586,10 @@ static int ctrl_cmd_untag(const char *input) char cmd; int sock_fd = 0; struct socket *el_socket; + int refcnt = -1; int res, argc; struct sock_tag *sock_tag_entry; - CT_DEBUG("qtaguid: ctrl_untag(%s): entered\n", input); argc = sscanf(input, "%c %d", &cmd, &sock_fd); CT_DEBUG("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", input, argc, cmd, sock_fd); @@ -1559,28 +1597,49 @@ static int ctrl_cmd_untag(const char *input) res = -EINVAL; goto err; } - el_socket = sockfd_lookup(sock_fd, &res); + el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ if (!el_socket) { pr_info("qtaguid: ctrl_untag(%s): failed to lookup" " sock_fd=%d err=%d\n", input, sock_fd, res); goto err; } + refcnt = atomic_read(&el_socket->file->f_count); + CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%d\n", + input, refcnt); spin_lock_bh(&sock_tag_list_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); if (!sock_tag_entry) { spin_unlock_bh(&sock_tag_list_lock); res = -EINVAL; - goto err; + goto err_put; } - /* The socket already belongs to the current process - * so it can do whatever it wants to it. */ - rb_erase(&sock_tag_entry->node, &sock_tag_tree); + /* + * The socket already belongs to the current process + * so it can do whatever it wants to it. + */ + rb_erase(&sock_tag_entry->sock_node, &sock_tag_tree); + + /* + * Release the sock_fd that was grabbed at tag time, + * and once more for the sockfd_lookup() here. + */ + sockfd_put(sock_tag_entry->socket); spin_unlock_bh(&sock_tag_list_lock); + sockfd_put(el_socket); + refcnt -= 2; kfree(sock_tag_entry); + CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%d\n", + input, refcnt); - res = 0; + return 0; + +err_put: + /* Release the sock_fd that was grabbed by sockfd_lookup(). */ + sockfd_put(el_socket); + refcnt--; err: - CT_DEBUG("qtaguid: ctrl_untag(%s): res=%d\n", input, res); + CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%d\n", + input, refcnt); return res; } @@ -1589,7 +1648,6 @@ static int qtaguid_ctrl_parse(const char *input, int count) char cmd; int res; - CT_DEBUG("qtaguid: ctrl(%s): entered\n", input); cmd = input[0]; /* Collect params for commands */ switch (cmd) { @@ -1667,10 +1725,12 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) tag_t tag = ppi->ts_entry->tn.tag; uid_t stat_uid = get_uid_from_tag(tag); if (!can_read_other_uid_stats(stat_uid)) { - CT_DEBUG("qtaguid: insufficient priv for stat line:" - "%s 0x%llx %u\n", + CT_DEBUG("qtaguid: stats line: " + "%s 0x%llx %u: " + "insufficient priv from pid=%u uid=%u\n", ppi->iface_entry->ifname, - get_atag_from_tag(tag), stat_uid); + get_atag_from_tag(tag), stat_uid, + current->pid, current_fsuid()); return 0; } cnts = &ppi->ts_entry->counters; @@ -1748,8 +1808,11 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, ppi.num_items_returned = num_items_returned; if (unlikely(module_passive)) { + len = pp_stats_line(&ppi, 0); + /* The header should always be shorter than the buffer. */ + WARN_ON(len >= ppi.char_count); *eof = 1; - return 0; + return len; } CT_DEBUG("qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " From b5c9a798dae8ecc6debb7b91f6af0367c30f4249 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Thu, 18 Aug 2011 15:05:47 -0700 Subject: [PATCH 1965/2556] netfilter: xt_qtaguid: Fix sockfd_put() call within spinlock sockfd_put() risks sleeping. So when doing a delete ctrl command, defer the sockfd_put() and kfree() to outside of the spinlock. Change-Id: I5f8ab51d05888d885b2fbb035f61efa5b7abb88a Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index b9dcfde99762e..968693cb1bc07 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1325,6 +1325,7 @@ static int ctrl_cmd_delete(const char *input) struct iface_stat *iface_entry; struct rb_node *node; struct sock_tag *st_entry; + struct rb_root st_to_free_tree = RB_ROOT; struct tag_stat *ts_entry; struct tag_counter_set *tcs_entry; @@ -1362,18 +1363,27 @@ static int ctrl_cmd_delete(const char *input) continue; if (!acct_tag || st_entry->tag == tag) { - CT_DEBUG("qtaguid: ctrl_delete(): " - "erase st: sk=%p tag=0x%llx (uid=%u)\n", - st_entry->sk, - st_entry->tag, - entry_uid); rb_erase(&st_entry->sock_node, &sock_tag_tree); - sockfd_put(st_entry->socket); - kfree(st_entry); + /* Can't sockfd_put() within spinlock, do it later. */ + sock_tag_tree_insert(st_entry, &st_to_free_tree); } } spin_unlock_bh(&sock_tag_list_lock); + node = rb_first(&st_to_free_tree); + while (node) { + st_entry = rb_entry(node, struct sock_tag, sock_node); + node = rb_next(node); + CT_DEBUG("qtaguid: ctrl_delete(): " + "erase st: sk=%p tag=0x%llx (uid=%u)\n", + st_entry->sk, + st_entry->tag, + entry_uid); + rb_erase(&st_entry->sock_node, &st_to_free_tree); + sockfd_put(st_entry->socket); + kfree(st_entry); + } + tag = combine_atag_with_uid(acct_tag, uid); /* Delete tag counter-sets */ From c097b6c1785c81c020a7d533423f6eb5a3d550ea Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 19 Aug 2011 20:21:06 -0700 Subject: [PATCH 1966/2556] netfilter: xt_qtaguid: add some tagging/matching stats /proc/net/xt_qtaguid/ctrl will now show: active tagged sockets: lines of "sock=%p tag=0x%llx (uid=%u)" sockets_tagged, : the number of sockets successfully tagged. sockets_untagged: the number of sockets successfully untagged. counter_set_changes: ctrl counter set change requests. delete_cmds: ctrl delete commands completed. iface_events: number of NETDEV_* events handled. match_found_sk: sk found in skbuff without ct assist. match_found_sk_in_ct: the number of times the connection tracker found a socket for us. This happens when the skbuff didn't have info. match_found_sk_none: the number of times no sk could be determined successfully looked up. This indicates we don't know who the data actually belongs to. This could be unsolicited traffic. Change-Id: I3a65613bb24852e1eea768ab0320a6a7073ab9be Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 64 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 968693cb1bc07..5afd4192f3534 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -222,6 +222,28 @@ struct sock_tag { tag_t tag; }; +struct qtaguid_event_counts { + /* Various successful events */ + atomic64_t sockets_tagged; + atomic64_t sockets_untagged; + atomic64_t counter_set_changes; + atomic64_t delete_cmds; + atomic64_t iface_events; /* Number of NETDEV_* events handled */ + /* + * match_found_sk_*: numbers related to the netfilter matching + * function finding a sock for the sk_buff. + */ + atomic64_t match_found_sk; /* An sk was already in the sk_buff. */ + /* The connection tracker had the sk. */ + atomic64_t match_found_sk_in_ct; + /* + * No sk could be found. No apparent owner. Could happen with + * unsolicited traffic. + */ + atomic64_t match_found_sk_none; +}; +static struct qtaguid_event_counts qtu_events; + static struct rb_root sock_tag_tree = RB_ROOT; static DEFINE_SPINLOCK(sock_tag_list_lock); @@ -954,6 +976,7 @@ static int iface_inet6addr_event_handler(struct notifier_block *nb, BUG_ON(!ifa || !ifa->idev); dev = (struct net_device *)ifa->idev->dev; iface_stat_create_ipv6(dev, ifa); + atomic64_inc(&qtu_events.iface_events); break; } return NOTIFY_DONE; @@ -977,6 +1000,7 @@ static int iface_inetaddr_event_handler(struct notifier_block *nb, BUG_ON(!ifa || !ifa->ifa_dev); dev = ifa->ifa_dev->dev; iface_stat_create(dev, ifa); + atomic64_inc(&qtu_events.iface_events); break; } return NOTIFY_DONE; @@ -1149,6 +1173,10 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) * it back, as nf_tproxy_get_sock_v4() got it. */ got_sock = sk; + if (sk) + atomic64_inc(&qtu_events.match_found_sk_in_ct); + } else { + atomic64_inc(&qtu_events.match_found_sk); } MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d proto=%d\n", par->hooknum, sk, got_sock, ip_hdr(skb)->protocol); @@ -1178,6 +1206,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) par->hooknum, sk ? sk->sk_socket : NULL); res = (info->match ^ info->invert) == 0; + atomic64_inc(&qtu_events.match_found_sk_none); goto put_sock_ret_res; } else if (info->match & info->invert & XT_QTAGUID_SOCKET) { res = false; @@ -1254,6 +1283,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, return 0; } + /* TODO: support skipping num_items_returned on entry. */ CT_DEBUG("qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", page, items_to_skip, char_count, *eof); @@ -1286,6 +1316,34 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, (*num_items_returned)++; } spin_unlock_bh(&sock_tag_list_lock); + + if (item_index++ >= items_to_skip) { + len = snprintf(outp, char_count, + "events: sockets_tagged=%llu " + "sockets_untagged=%llu " + "counter_set_changes=%llu " + "delete_cmds=%llu " + "iface_events=%llu " + "match_found_sk=%llu " + "match_found_sk_in_ct=%llu " + "match_found_sk_none=%llu\n", + atomic64_read(&qtu_events.sockets_tagged), + atomic64_read(&qtu_events.sockets_untagged), + atomic64_read(&qtu_events.counter_set_changes), + atomic64_read(&qtu_events.delete_cmds), + atomic64_read(&qtu_events.iface_events), + atomic64_read(&qtu_events.match_found_sk), + atomic64_read(&qtu_events.match_found_sk_in_ct), + atomic64_read(&qtu_events.match_found_sk_none)); + if (len >= char_count) { + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + *eof = 1; return outp - page; } @@ -1428,7 +1486,7 @@ static int ctrl_cmd_delete(const char *input) spin_unlock_bh(&iface_entry->tag_stat_list_lock); } spin_unlock_bh(&iface_stat_list_lock); - + atomic64_inc(&qtu_events.delete_cmds); res = 0; err: @@ -1487,7 +1545,7 @@ static int ctrl_cmd_counter_set(const char *input) } tcs->active_set = counter_set; spin_unlock_bh(&tag_counter_set_list_lock); - + atomic64_inc(&qtu_events.counter_set_changes); res = 0; err: @@ -1573,6 +1631,7 @@ static int ctrl_cmd_tag(const char *input) sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid); sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); + atomic64_inc(&qtu_events.sockets_tagged); } spin_unlock_bh(&sock_tag_list_lock); /* We keep the ref to the socket (file) until it is untagged */ @@ -1638,6 +1697,7 @@ static int ctrl_cmd_untag(const char *input) sockfd_put(el_socket); refcnt -= 2; kfree(sock_tag_entry); + atomic64_inc(&qtu_events.sockets_untagged); CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%d\n", input, refcnt); From 183144f7dcd47f4f571152dfc26aa3612c301638 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Thu, 25 Aug 2011 17:10:52 -0700 Subject: [PATCH 1967/2556] netfilter: xt_qtaguid: fix dev_stats for missing NETDEV_UNREGISTER Turns out that some devices don't call the notifier chains with NETDEV_UNREGISTER. So now we only track up/down as the points for tracking active/inactive transitions and saving the get_dev_stats(). Change-Id: I948755962b4c64150b4d04f294fb4889f151e42b Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 5afd4192f3534..80b5990045bc1 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -795,12 +795,12 @@ static void iface_stat_update(struct net_device *dev) spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(dev->name); if (entry == NULL) { - IF_DEBUG("qtaguid: iface_stat_update: dev=%s not tracked\n", + IF_DEBUG("qtaguid: iface_stat: update(%s): not tracked\n", dev->name); spin_unlock_bh(&iface_stat_list_lock); return; } - IF_DEBUG("qtaguid: iface_stat_update: dev=%s entry=%p\n", + IF_DEBUG("qtaguid: iface_stat: update(%s): entry=%p\n", dev->name, entry); if (entry->active) { entry->tx_bytes += stats->tx_bytes; @@ -808,8 +808,11 @@ static void iface_stat_update(struct net_device *dev) entry->rx_bytes += stats->rx_bytes; entry->rx_packets += stats->rx_packets; entry->active = false; + IF_DEBUG("qtaguid: iface_stat: update(%s): " + " disable tracking. rx/tx=%llu/%llu\n", + dev->name, stats->rx_bytes, stats->tx_bytes); } else { - IF_DEBUG("qtaguid: iface_stat_update: dev=%s inactive\n", + IF_DEBUG("qtaguid: iface_stat: update(%s): disabled\n", dev->name); } spin_unlock_bh(&iface_stat_list_lock); @@ -951,7 +954,7 @@ static int iface_netdev_event_handler(struct notifier_block *nb, case NETDEV_UP: iface_stat_create(dev, NULL); break; - case NETDEV_UNREGISTER: + case NETDEV_DOWN: iface_stat_update(dev); break; } @@ -978,6 +981,12 @@ static int iface_inet6addr_event_handler(struct notifier_block *nb, iface_stat_create_ipv6(dev, ifa); atomic64_inc(&qtu_events.iface_events); break; + case NETDEV_DOWN: + BUG_ON(!ifa || !ifa->idev); + dev = (struct net_device *)ifa->idev->dev; + iface_stat_update(dev); + atomic64_inc(&qtu_events.iface_events); + break; } return NOTIFY_DONE; } @@ -1002,6 +1011,12 @@ static int iface_inetaddr_event_handler(struct notifier_block *nb, iface_stat_create(dev, ifa); atomic64_inc(&qtu_events.iface_events); break; + case NETDEV_DOWN: + BUG_ON(!ifa || !ifa->ifa_dev); + dev = ifa->ifa_dev->dev; + iface_stat_update(dev); + atomic64_inc(&qtu_events.iface_events); + break; } return NOTIFY_DONE; } From 53c566b0fd6bbe7d50afe0ec1eaf1e413c05acab Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Wed, 31 Aug 2011 13:50:15 -0700 Subject: [PATCH 1968/2556] netfilter: qtaguid: fix proc/.../stats uid filtered output "cat /proc/net/xt_qtaguid/stats" for a non-priviledged UID would output multiple twice its own stats. The fix tweaks the way lines are counted. Non-root: idx iface acct_tag_hex uid_tag_int cnt_set ... 2 wlan0 0x0 10022 0 ... 3 wlan0 0x0 10022 1 ... 4 wlan0 0x3010000000000000 10022 0 ... 5 wlan0 0x3010000000000000 10022 1 ... Root: idx iface acct_tag_hex uid_tag_int cnt_set 2 wlan0 0x0 0 0 ... 3 wlan0 0x0 0 1 ... 4 wlan0 0x0 1000 0 ... ... 12 wlan0 0x0 10022 0 ... 13 wlan0 0x0 10022 1 ... ... 18 wlan0 0x3010000000000000 10022 0 ... 19 wlan0 0x3010000000000000 10022 1 ... Change-Id: I3cae1f4fee616bc897831350374656b0c718c45b Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 80b5990045bc1..b145f5a4b45d2 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1788,6 +1788,7 @@ struct proc_print_info { struct iface_stat *iface_entry; struct tag_stat *ts_entry; int item_index; + int items_to_skip; int char_count; }; @@ -1795,7 +1796,10 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) { int len; struct data_counters *cnts; + if (!ppi->item_index) { + if (ppi->item_index++ < ppi->items_to_skip) + return 0; len = snprintf(ppi->outp, ppi->char_count, "idx iface acct_tag_hex uid_tag_int cnt_set " "rx_bytes rx_packets " @@ -1809,6 +1813,7 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) } else { tag_t tag = ppi->ts_entry->tn.tag; uid_t stat_uid = get_uid_from_tag(tag); + if (!can_read_other_uid_stats(stat_uid)) { CT_DEBUG("qtaguid: stats line: " "%s 0x%llx %u: " @@ -1818,6 +1823,8 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) current->pid, current_fsuid()); return 0; } + if (ppi->item_index++ < ppi->items_to_skip) + return 0; cnts = &ppi->ts_entry->counters; len = snprintf( ppi->outp, ppi->char_count, @@ -1891,11 +1898,13 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, ppi.item_index = 0; ppi.char_count = char_count; ppi.num_items_returned = num_items_returned; + ppi.items_to_skip = items_to_skip; if (unlikely(module_passive)) { len = pp_stats_line(&ppi, 0); /* The header should always be shorter than the buffer. */ WARN_ON(len >= ppi.char_count); + (*num_items_returned)++; *eof = 1; return len; } @@ -1907,16 +1916,17 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, if (*eof) return 0; - if (!items_to_skip) { - /* The idx is there to help debug when things go belly up. */ - len = pp_stats_line(&ppi, 0); - /* Don't advance the outp unless the whole line was printed */ - if (len >= ppi.char_count) { - *ppi.outp = '\0'; - return ppi.outp - page; - } + /* The idx is there to help debug when things go belly up. */ + len = pp_stats_line(&ppi, 0); + /* Don't advance the outp unless the whole line was printed */ + if (len >= ppi.char_count) { + *ppi.outp = '\0'; + return ppi.outp - page; + } + if (len) { ppi.outp += len; ppi.char_count -= len; + (*num_items_returned)++; } spin_lock_bh(&iface_stat_list_lock); @@ -1927,8 +1937,6 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, node; node = rb_next(node)) { ppi.ts_entry = rb_entry(node, struct tag_stat, tn.node); - if (ppi.item_index++ < items_to_skip) - continue; if (!pp_sets(&ppi)) { spin_unlock_bh( &ppi.iface_entry->tag_stat_list_lock); From 7481f71f233fa431cd1c0587c0e9ecc8c9595161 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 9 Sep 2011 01:55:24 -0700 Subject: [PATCH 1969/2556] netfilter: xt_qtaguid: 1st pass at tracking tag based data resources * Added global resource tracking based on tags. - Can be put into passive mode via /sys/modules/xt_qtaguid/params/tag_tracking_passive - The number of socket tags per UID is now limited - Adding /dev/xt_qtaguid that each process should open before starting to tag sockets. A later change will make it a "must". - A process should not create new tags unless it has the dev open. A later change will make it a must. - On qtaguid_resources release, the process' matching socket tag info is deleted. * Support run-time debug mask via /sys/modules parameter "debug_mask". * split module into prettyprinting code, includes, main. * Removed ptrdiff_t usage which didn't work in all cases. Change-Id: I4a21d3bea55d23c1c3747253904e2a79f7d555d9 Signed-off-by: JP Abgrall --- net/netfilter/Makefile | 2 +- net/netfilter/xt_qtaguid.c | 1087 +++++++++++++++++++-------- net/netfilter/xt_qtaguid_internal.h | 305 ++++++++ net/netfilter/xt_qtaguid_print.c | 397 ++++++++++ net/netfilter/xt_qtaguid_print.h | 39 + 5 files changed, 1533 insertions(+), 297 deletions(-) create mode 100644 net/netfilter/xt_qtaguid_internal.h create mode 100644 net/netfilter/xt_qtaguid_print.c create mode 100644 net/netfilter/xt_qtaguid_print.h diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index f111be8a0243d..e9552d4abb869 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -88,7 +88,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_OWNER) += xt_owner.o obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o -obj-$(CONFIG_NETFILTER_XT_MATCH_QTAGUID) += xt_qtaguid.o +obj-$(CONFIG_NETFILTER_XT_MATCH_QTAGUID) += xt_qtaguid_print.o xt_qtaguid.o obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index b145f5a4b45d2..dff2ff3fd7cc8 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -8,36 +8,11 @@ * published by the Free Software Foundation. */ -/* #define DEBUG */ -/* #define IDEBUG */ -/* #define MDEBUG */ -/* #define RDEBUG */ -/* #define CDEBUG */ - -/* Iface handling */ -#ifdef IDEBUG -#define IF_DEBUG(...) pr_debug(__VA_ARGS__) -#else -#define IF_DEBUG(...) no_printk(__VA_ARGS__) -#endif -/* Iptable Matching */ -#ifdef MDEBUG -#define MT_DEBUG(...) pr_debug(__VA_ARGS__) -#else -#define MT_DEBUG(...) no_printk(__VA_ARGS__) -#endif -/* Red-black tree handling */ -#ifdef RDEBUG -#define RB_DEBUG(...) pr_debug(__VA_ARGS__) -#else -#define RB_DEBUG(...) no_printk(__VA_ARGS__) -#endif -/* procfs ctrl/stats handling */ -#ifdef CDEBUG -#define CT_DEBUG(...) pr_debug(__VA_ARGS__) -#else -#define CT_DEBUG(...) no_printk(__VA_ARGS__) -#endif +/* + * There are run-time debug flags enabled via the debug_mask module param, or + * via the DEFAULT_DEBUG_MASK. See xt_qtaguid_internal.h. + */ +#define DEBUG #include #include @@ -52,6 +27,9 @@ #include #include +#include "xt_qtaguid_internal.h" +#include "xt_qtaguid_print.h" + /* * We only use the xt_socket funcs within a similar context to avoid unexpected * return values. @@ -92,202 +70,159 @@ module_param_named(stats_readall_gid, proc_stats_readall_gid, uint, module_param_named(ctrl_write_gid, proc_ctrl_write_gid, uint, S_IRUGO | S_IWUSR); +/* + * Limit the number of active tags (via socket tags) for a given UID. + * Multiple processes could share the UID. + */ +static int max_sock_tags = DEFAULT_MAX_SOCK_TAGS; +module_param(max_sock_tags, int, S_IRUGO | S_IWUSR); + /* * After the kernel has initiallized this module, it is still possible - * to make it passive: - * - do not register it via iptables. - * the matching code will not be invoked. - * - set passive to 0 - * the iface stats handling will not be act on notifications. + * to make it passive. + * Setting passive to Y: + * - the iface stats handling will not act on notifications. + * - iptables matches will never match. + * - ctrl commands silently succeed. + * - stats are always empty. * This is mostly usefull when a bug is suspected. */ static bool module_passive; module_param_named(passive, module_passive, bool, S_IRUGO | S_IWUSR); -/*---------------------------------------------------------------------------*/ /* - * Tags: - * - * They represent what the data usage counters will be tracked against. - * By default a tag is just based on the UID. - * The UID is used as the base for policying, and can not be ignored. - * So a tag will always at least represent a UID (uid_tag). - * - * A tag can be augmented with an "accounting tag" which is associated - * with a UID. - * User space can set the acct_tag portion of the tag which is then used - * with sockets: all data belong to that socket will be counted against the - * tag. The policing is then based on the tag's uid_tag portion, - * and stats are collected for the acct_tag portion seperately. - * - * There could be - * a: {acct_tag=1, uid_tag=10003} - * b: {acct_tag=2, uid_tag=10003} - * c: {acct_tag=3, uid_tag=10003} - * d: {acct_tag=0, uid_tag=10003} - * (a, b, and c represent tags associated with specific sockets. - * d is for the totals for that uid, including all untagged traffic. - * Typically d is used with policing/quota rules. - * - * We want tag_t big enough to distinguish uid_t and acct_tag. - * It might become a struct if needed. - * Nothing should be using it as an int. + * Control how qtaguid data is tracked per proc/uid. + * Setting tag_tracking_passive to Y: + * - don't create proc specific structs to track tags + * - don't check that active tag stats exceed some limits. + * - don't clean up socket tags on process exits. + * This is mostly usefull when a bug is suspected. */ -typedef uint64_t tag_t; /* Only used via accessors */ +static bool qtu_proc_handling_passive; +module_param_named(tag_tracking_passive, qtu_proc_handling_passive, bool, + S_IRUGO | S_IWUSR); + +#define QTU_DEV_NAME "xt_qtaguid" + +uint debug_mask = DEFAULT_DEBUG_MASK; +module_param(debug_mask, uint, S_IRUGO | S_IWUSR); + +/*---------------------------------------------------------------------------*/ static const char *iface_stat_procdirname = "iface_stat"; static struct proc_dir_entry *iface_stat_procdir; - /* - * For now we only track 2 sets of counters. - * The default set is 0. - * Userspace can activate another set for a given uid being tracked. + * Ordering of locks: + * outer locks: + * iface_stat_list_lock + * sock_tag_list_lock + * inner locks: + * uid_tag_data_tree_lock + * tag_counter_set_list_lock + * Notice how sock_tag_list_lock is held sometimes when uid_tag_data_tree_lock + * is acquired. + * + * Call tree with all lock holders as of 2011-09-06: + * + * qtaguid_ctrl_parse() + * ctrl_cmd_delete() + * sock_tag_list_lock + * tag_counter_set_list_lock + * iface_stat_list_lock + * iface_entry->tag_stat_list_lock + * uid_tag_data_tree_lock + * ctrl_cmd_counter_set() + * tag_counter_set_list_lock + * ctrl_cmd_tag() + * sock_tag_list_lock + * get_tag_ref() + * uid_tag_data_tree_lock + * uid_tag_data_tree_lock + * ctrl_cmd_untag() + * sock_tag_list_lock + * uid_tag_data_tree_lock + * + * qtaguid_mt() + * account_for_uid() + * if_tag_stat_update() + * get_sock_stat() + * sock_tag_list_lock + * iface_entry->tag_stat_list_lock + * tag_stat_update() + * get_active_counter_set() + * tag_counter_set_list_lock + * + * iface_netdev_event_handler() + * iface_stat_create() + * iface_stat_list_lock + * iface_stat_update() + * iface_stat_list_lock + * + * iface_inet6addr_event_handler() + * iface_stat_create_ipv6() + * iface_stat_list_lock + * iface_stat_update() + * iface_stat_list_lock + * + * iface_inetaddr_event_handler() + * iface_stat_create() + * iface_stat_list_lock + * iface_stat_update() + * iface_stat_list_lock + * + * qtaguid_ctrl_proc_read() + * sock_tag_list_lock + * sock_tag_list_lock + * uid_tag_data_tree_lock + * iface_stat_list_lock + * + * qtaguid_stats_proc_read() + * iface_stat_list_lock + * iface_entry->tag_stat_list_lock + * + * qtudev_open() + * uid_tag_data_tree_lock + * + * qtud_dev_release() + * sock_tag_list_lock + * uid_tag_data_tree_lock */ -#define IFS_MAX_COUNTER_SETS 2 - -enum ifs_tx_rx { - IFS_TX, - IFS_RX, - IFS_MAX_DIRECTIONS -}; - -/* For now, TCP, UDP, the rest */ -enum ifs_proto { - IFS_TCP, - IFS_UDP, - IFS_PROTO_OTHER, - IFS_MAX_PROTOS -}; - -struct byte_packet_counters { - uint64_t bytes; - uint64_t packets; -}; - -struct data_counters { - struct byte_packet_counters bpc[IFS_MAX_COUNTER_SETS][IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS]; -}; - -/* Generic tag based node used as a base for rb_tree ops. */ -struct tag_node { - struct rb_node node; - tag_t tag; -}; - -struct tag_stat { - struct tag_node tn; - struct data_counters counters; - /* - * If this tag is acct_tag based, we need to count against the - * matching parent uid_tag. - */ - struct data_counters *parent_counters; -}; - -struct iface_stat { - struct list_head list; - char *ifname; - uint64_t rx_bytes; - uint64_t rx_packets; - uint64_t tx_bytes; - uint64_t tx_packets; - bool active; - struct proc_dir_entry *proc_ptr; - - struct rb_root tag_stat_tree; - spinlock_t tag_stat_list_lock; -}; - static LIST_HEAD(iface_stat_list); static DEFINE_SPINLOCK(iface_stat_list_lock); -/* This is needed to create proc_dir_entries from atomic context. */ -struct iface_stat_work { - struct work_struct iface_work; - struct iface_stat *iface_entry; -}; - -/* - * Track tag that this socket is transferring data for, and not necessarily - * the uid that owns the socket. - * This is the tag against which tag_stat.counters will be billed. - */ -struct sock_tag { - struct rb_node sock_node; - struct sock *sk; /* Only used as a number, never dereferenced */ - /* The socket is needed for sockfd_put() */ - struct socket *socket; - - tag_t tag; -}; - -struct qtaguid_event_counts { - /* Various successful events */ - atomic64_t sockets_tagged; - atomic64_t sockets_untagged; - atomic64_t counter_set_changes; - atomic64_t delete_cmds; - atomic64_t iface_events; /* Number of NETDEV_* events handled */ - /* - * match_found_sk_*: numbers related to the netfilter matching - * function finding a sock for the sk_buff. - */ - atomic64_t match_found_sk; /* An sk was already in the sk_buff. */ - /* The connection tracker had the sk. */ - atomic64_t match_found_sk_in_ct; - /* - * No sk could be found. No apparent owner. Could happen with - * unsolicited traffic. - */ - atomic64_t match_found_sk_none; -}; -static struct qtaguid_event_counts qtu_events; - static struct rb_root sock_tag_tree = RB_ROOT; static DEFINE_SPINLOCK(sock_tag_list_lock); -/* Track the set active_set for the given tag. */ -struct tag_counter_set { - struct tag_node tn; - int active_set; -}; - static struct rb_root tag_counter_set_tree = RB_ROOT; static DEFINE_SPINLOCK(tag_counter_set_list_lock); -static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par); +static struct rb_root uid_tag_data_tree = RB_ROOT; +static DEFINE_SPINLOCK(uid_tag_data_tree_lock); +static struct rb_root proc_qtu_data_tree = RB_ROOT; +/* No proc_qtu_data_tree_lock; use uid_tag_data_tree_lock */ + +static struct qtaguid_event_counts qtu_events; /*----------------------------------------------*/ -static inline int tag_compare(tag_t t1, tag_t t2) +static bool can_manipulate_uids(void) { - return t1 < t2 ? -1 : t1 == t2 ? 0 : 1; + /* root pwnd */ + return unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_gid) + || in_egroup_p(proc_ctrl_write_gid); } -static inline tag_t combine_atag_with_uid(tag_t acct_tag, uid_t uid) -{ - return acct_tag | uid; -} -static inline tag_t make_tag_from_uid(uid_t uid) -{ - return uid; -} -static inline uid_t get_uid_from_tag(tag_t tag) -{ - return tag & 0xFFFFFFFFULL; -} -static inline tag_t get_utag_from_tag(tag_t tag) -{ - return tag & 0xFFFFFFFFULL; -} -static inline tag_t get_atag_from_tag(tag_t tag) +static bool can_impersonate_uid(uid_t uid) { - return tag & ~0xFFFFFFFFULL; + return uid == current_fsuid() || can_manipulate_uids(); } -static inline bool valid_atag(tag_t tag) +static bool can_read_other_uid_stats(uid_t uid) { - return !(tag & 0xFFFFFFFFULL); + /* root pwnd */ + return unlikely(!current_fsuid()) || uid == current_fsuid() + || unlikely(!proc_stats_readall_gid) + || in_egroup_p(proc_stats_readall_gid); } static inline void dc_add_byte_packets(struct data_counters *counters, int set, @@ -324,12 +259,13 @@ static struct tag_node *tag_node_tree_search(struct rb_root *root, tag_t tag) while (node) { struct tag_node *data = rb_entry(node, struct tag_node, node); - int result = tag_compare(tag, data->tag); - RB_DEBUG("qtaguid: tag_node_tree_search(): tag=0x%llx" - " (uid=%d)\n", - data->tag, - get_uid_from_tag(data->tag)); - + int result; + RB_DEBUG("qtaguid: tag_node_tree_search(0x%llx): " + " node=%p data=%p\n", tag, node, data); + result = tag_compare(tag, data->tag); + RB_DEBUG("qtaguid: tag_node_tree_search(0x%llx): " + " data.tag=0x%llx (uid=%u) res=%d\n", + tag, data->tag, get_uid_from_tag(data->tag), result); if (result < 0) node = node->rb_left; else if (result > 0) @@ -349,8 +285,8 @@ static void tag_node_tree_insert(struct tag_node *data, struct rb_root *root) struct tag_node *this = rb_entry(*new, struct tag_node, node); int result = tag_compare(data->tag, this->tag); - RB_DEBUG("qtaguid: tag_node_tree_insert(): tag=0x%llx" - " (uid=%d)\n", + RB_DEBUG("qtaguid: %s(): tag=0x%llx" + " (uid=%u)\n", __func__, this->tag, get_uid_from_tag(this->tag)); parent = *new; @@ -396,6 +332,19 @@ static struct tag_counter_set *tag_counter_set_tree_search(struct rb_root *root, } +static void tag_ref_tree_insert(struct tag_ref *data, struct rb_root *root) +{ + tag_node_tree_insert(&data->tn, root); +} + +static struct tag_ref *tag_ref_tree_search(struct rb_root *root, tag_t tag) +{ + struct tag_node *node = tag_node_tree_search(root, tag); + if (!node) + return NULL; + return rb_entry(&node->node, struct tag_ref, tn.node); +} + static struct sock_tag *sock_tag_tree_search(struct rb_root *root, const struct sock *sk) { @@ -404,10 +353,9 @@ static struct sock_tag *sock_tag_tree_search(struct rb_root *root, while (node) { struct sock_tag *data = rb_entry(node, struct sock_tag, sock_node); - ptrdiff_t result = sk - data->sk; - if (result < 0) + if (sk < data->sk) node = node->rb_left; - else if (result > 0) + else if (sk > data->sk) node = node->rb_right; else return data; @@ -423,11 +371,10 @@ static void sock_tag_tree_insert(struct sock_tag *data, struct rb_root *root) while (*new) { struct sock_tag *this = rb_entry(*new, struct sock_tag, sock_node); - ptrdiff_t result = data->sk - this->sk; parent = *new; - if (result < 0) + if (data->sk < this->sk) new = &((*new)->rb_left); - else if (result > 0) + else if (data->sk > this->sk) new = &((*new)->rb_right); else BUG(); @@ -438,6 +385,292 @@ static void sock_tag_tree_insert(struct sock_tag *data, struct rb_root *root) rb_insert_color(&data->sock_node, root); } +static void sock_tag_tree_erase(struct rb_root *st_to_free_tree) +{ + struct rb_node *node; + struct sock_tag *st_entry; + + node = rb_first(st_to_free_tree); + while (node) { + st_entry = rb_entry(node, struct sock_tag, sock_node); + node = rb_next(node); + CT_DEBUG("qtaguid: %s(): " + "erase st: sk=%p tag=0x%llx (uid=%u)\n", __func__, + st_entry->sk, + st_entry->tag, + get_uid_from_tag(st_entry->tag)); + rb_erase(&st_entry->sock_node, st_to_free_tree); + sockfd_put(st_entry->socket); + kfree(st_entry); + } +} + +static struct proc_qtu_data *proc_qtu_data_tree_search(struct rb_root *root, + const pid_t pid) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct proc_qtu_data *data = rb_entry(node, + struct proc_qtu_data, + node); + if (pid < data->pid) + node = node->rb_left; + else if (pid > data->pid) + node = node->rb_right; + else + return data; + } + return NULL; +} + +static void proc_qtu_data_tree_insert(struct proc_qtu_data *data, + struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct proc_qtu_data *this = rb_entry(*new, + struct proc_qtu_data, + node); + parent = *new; + if (data->pid < this->pid) + new = &((*new)->rb_left); + else if (data->pid > this->pid) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); +} + +static void uid_tag_data_tree_insert(struct uid_tag_data *data, + struct rb_root *root) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct uid_tag_data *this = rb_entry(*new, + struct uid_tag_data, + node); + parent = *new; + if (data->uid < this->uid) + new = &((*new)->rb_left); + else if (data->uid > this->uid) + new = &((*new)->rb_right); + else + BUG(); + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); +} + +static struct uid_tag_data *uid_tag_data_tree_search(struct rb_root *root, + uid_t uid) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct uid_tag_data *data = rb_entry(node, + struct uid_tag_data, + node); + if (uid < data->uid) + node = node->rb_left; + else if (uid > data->uid) + node = node->rb_right; + else + return data; + } + return NULL; +} + +/* + * Allocates a new uid_tag_data struct if needed. + * Returns a pointer to the found or allocated uid_tag_data. + * Returns a PTR_ERR on failures, and lock is not held. + * If found is not NULL: + * sets *found to true if not allocated. + * sets *found to false if allocated. + */ +struct uid_tag_data *get_uid_data(uid_t uid, bool *found_res) +{ + struct uid_tag_data *utd_entry; + + /* Look for top level uid_tag_data for the UID */ + utd_entry = uid_tag_data_tree_search(&uid_tag_data_tree, uid); + DR_DEBUG("qtaguid: get_uid_data(%u) utd=%p\n", uid, utd_entry); + + if (found_res) + *found_res = utd_entry; + if (utd_entry) + return utd_entry; + + utd_entry = kzalloc(sizeof(*utd_entry), GFP_ATOMIC); + if (!utd_entry) { + pr_err("qtaguid: get_uid_data(%u): " + "tag data alloc failed\n", uid); + return ERR_PTR(-ENOMEM); + } + + utd_entry->uid = uid; + utd_entry->tag_ref_tree = RB_ROOT; + uid_tag_data_tree_insert(utd_entry, &uid_tag_data_tree); + DR_DEBUG("qtaguid: get_uid_data(%u) new utd=%p\n", uid, utd_entry); + return utd_entry; +} + +/* Never returns NULL. Either PTR_ERR or a valid ptr. */ +static struct tag_ref *new_tag_ref(tag_t new_tag, + struct uid_tag_data *utd_entry) +{ + struct tag_ref *tr_entry; + int res; + + if (utd_entry->num_active_tags + 1 > max_sock_tags) { + pr_info("qtaguid: new_tag_ref(0x%llx): " + "tag ref alloc quota exceeded. max=%d\n", + new_tag, max_sock_tags); + res = -EMFILE; + goto err_res; + + } + + tr_entry = kzalloc(sizeof(*tr_entry), GFP_ATOMIC); + if (!tr_entry) { + pr_err("qtaguid: new_tag_ref(0x%llx): " + "tag ref alloc failed\n", + new_tag); + res = -ENOMEM; + goto err_res; + } + tr_entry->tn.tag = new_tag; + /* tr_entry->num_sock_tags handled by caller */ + utd_entry->num_active_tags++; + tag_ref_tree_insert(tr_entry, &utd_entry->tag_ref_tree); + DR_DEBUG("qtaguid: new_tag_ref(0x%llx): " + " inserted new tag ref\n", + new_tag); + return tr_entry; + +err_res: + return ERR_PTR(res); +} + +static struct tag_ref *lookup_tag_ref(tag_t full_tag, + struct uid_tag_data **utd_res) +{ + struct uid_tag_data *utd_entry; + struct tag_ref *tr_entry; + bool found_utd; + uid_t uid = get_uid_from_tag(full_tag); + + DR_DEBUG("qtaguid: lookup_tag_ref(tag=0x%llx (uid=%u))\n", + full_tag, uid); + + utd_entry = get_uid_data(uid, &found_utd); + if (IS_ERR_OR_NULL(utd_entry)) { + if (utd_res) + *utd_res = utd_entry; + return NULL; + } + + tr_entry = tag_ref_tree_search(&utd_entry->tag_ref_tree, full_tag); + if (utd_res) + *utd_res = utd_entry; + DR_DEBUG("qtaguid: lookup_tag_ref(0x%llx) utd_entry=%p tr_entry=%p\n", + full_tag, utd_entry, tr_entry); + return tr_entry; +} + +/* Never returns NULL. Either PTR_ERR or a valid ptr. */ +static struct tag_ref *get_tag_ref(tag_t full_tag, + struct uid_tag_data **utd_res) +{ + struct uid_tag_data *utd_entry; + struct tag_ref *tr_entry; + + DR_DEBUG("qtaguid: get_tag_ref(0x%llx)\n", + full_tag); + spin_lock_bh(&uid_tag_data_tree_lock); + tr_entry = lookup_tag_ref(full_tag, &utd_entry); + BUG_ON(IS_ERR_OR_NULL(utd_entry)); + if (!tr_entry) + tr_entry = new_tag_ref(full_tag, utd_entry); + + spin_unlock_bh(&uid_tag_data_tree_lock); + if (utd_res) + *utd_res = utd_entry; + DR_DEBUG("qtaguid: get_tag_ref(0x%llx) utd=%p tr=%p\n", + full_tag, utd_entry, tr_entry); + return tr_entry; +} + +/* Checks and maybe frees the UID Tag Data entry */ +static void put_utd_entry(struct uid_tag_data *utd_entry) +{ + /* Are we done with the UID tag data entry? */ + if (RB_EMPTY_ROOT(&utd_entry->tag_ref_tree)) { + DR_DEBUG("qtaguid: %s(): " + "erase utd_entry=%p uid=%u " + "by pid=%u tgid=%u uid=%u\n", __func__, + utd_entry, utd_entry->uid, + current->pid, current->tgid, current_fsuid()); + BUG_ON(utd_entry->num_active_tags); + rb_erase(&utd_entry->node, &uid_tag_data_tree); + kfree(utd_entry); + } else { + DR_DEBUG("qtaguid: %s(): " + "utd_entry=%p still has %d tags\n", __func__, + utd_entry, utd_entry->num_active_tags); + BUG_ON(!utd_entry->num_active_tags); + } +} + +/* + * If no sock_tags are using this tag_ref, + * decrements refcount of utd_entry, removes tr_entry + * from utd_entry->tag_ref_tree and frees. + */ +static void free_tag_ref_from_utd_entry(struct tag_ref *tr_entry, + struct uid_tag_data *utd_entry) +{ + DR_DEBUG("qtaguid: %s(): %p tag=0x%llx (uid=%u)\n", __func__, + tr_entry, tr_entry->tn.tag, + get_uid_from_tag(tr_entry->tn.tag)); + if (!tr_entry->num_sock_tags) { + BUG_ON(!utd_entry->num_active_tags); + utd_entry->num_active_tags--; + rb_erase(&tr_entry->tn.node, &utd_entry->tag_ref_tree); + DR_DEBUG("qtaguid: %s(): erased %p\n", __func__, tr_entry); + kfree(tr_entry); + } +} + +static void put_tag_ref_tree(tag_t full_tag, struct uid_tag_data *utd_entry) +{ + struct rb_node *node; + struct tag_ref *tr_entry; + tag_t acct_tag; + + DR_DEBUG("qtaguid: %s(tag=0x%llx (uid=%u))\n", __func__, + full_tag, get_uid_from_tag(full_tag)); + acct_tag = get_atag_from_tag(full_tag); + node = rb_first(&utd_entry->tag_ref_tree); + while (node) { + tr_entry = rb_entry(node, struct tag_ref, tn.node); + node = rb_next(node); + if (!acct_tag || tr_entry->tn.tag == full_tag) + free_tag_ref_from_utd_entry(tr_entry, utd_entry); + } +} + static int read_proc_u64(char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -843,8 +1076,8 @@ static struct tag_stat *create_if_tag_stat(struct iface_stat *iface_entry, tag_t tag) { struct tag_stat *new_tag_stat_entry = NULL; - IF_DEBUG("qtaguid: iface_stat: create_if_tag_stat(): ife=%p tag=0x%llx" - " (uid=%u)\n", + IF_DEBUG("qtaguid: iface_stat: %s(): ife=%p tag=0x%llx" + " (uid=%u)\n", __func__, iface_entry, tag, get_uid_from_tag(tag)); new_tag_stat_entry = kzalloc(sizeof(*new_tag_stat_entry), GFP_ATOMIC); if (!new_tag_stat_entry) { @@ -894,9 +1127,9 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, acct_tag = get_atag_from_tag(tag); uid_tag = get_utag_from_tag(tag); } else { - uid_tag = make_tag_from_uid(uid); - acct_tag = 0; + acct_tag = make_atag_from_value(0); tag = combine_atag_with_uid(acct_tag, uid); + uid_tag = make_tag_from_uid(uid); } MT_DEBUG("qtaguid: iface_stat: stat_update(): " " looking for tag=0x%llx (uid=%u) in ife=%p\n", @@ -1289,22 +1522,23 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, char *outp = page; int len; uid_t uid; - struct sock_tag *sock_tag_entry; struct rb_node *node; + struct sock_tag *sock_tag_entry; int item_index = 0; + int indent_level = 0; + long f_count; if (unlikely(module_passive)) { *eof = 1; return 0; } - /* TODO: support skipping num_items_returned on entry. */ - CT_DEBUG("qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", - page, items_to_skip, char_count, *eof); - if (*eof) return 0; + CT_DEBUG("qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", + page, items_to_skip, char_count, *eof); + spin_lock_bh(&sock_tag_list_lock); for (node = rb_first(&sock_tag_tree); node; @@ -1313,14 +1547,21 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, continue; sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); uid = get_uid_from_tag(sock_tag_entry->tag); - CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u)\n", + CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u) " + "pid=%u\n", sock_tag_entry->sk, sock_tag_entry->tag, - uid + uid, + sock_tag_entry->pid ); + f_count = atomic_long_read( + &sock_tag_entry->socket->file->f_count); len = snprintf(outp, char_count, - "sock=%p tag=0x%llx (uid=%u)\n", - sock_tag_entry->sk, sock_tag_entry->tag, uid); + "sock=%p tag=0x%llx (uid=%u) pid=%u " + "f_count=%lu\n", + sock_tag_entry->sk, + sock_tag_entry->tag, uid, + sock_tag_entry->pid, f_count); if (len >= char_count) { spin_unlock_bh(&sock_tag_list_lock); *outp = '\0'; @@ -1359,28 +1600,31 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, (*num_items_returned)++; } - *eof = 1; - return outp - page; -} +#ifdef CDEBUG + /* Count the following as part of the last item_index */ + if (item_index > items_to_skip) { + CT_DEBUG("qtaguid: proc ctrl state debug {\n"); + spin_lock_bh(&sock_tag_list_lock); + prdebug_sock_tag_tree(indent_level, &sock_tag_tree); + spin_unlock_bh(&sock_tag_list_lock); -static bool can_manipulate_uids(void) -{ - /* root pwnd */ - return unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_gid) - || in_egroup_p(proc_ctrl_write_gid); -} + spin_lock_bh(&uid_tag_data_tree_lock); + prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree); + prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree); + spin_unlock_bh(&uid_tag_data_tree_lock); -static bool can_impersonate_uid(uid_t uid) -{ - return uid == current_fsuid() || can_manipulate_uids(); -} + spin_lock_bh(&iface_stat_list_lock); + prdebug_iface_stat_list(indent_level, &iface_stat_list); + spin_unlock_bh(&iface_stat_list_lock); -static bool can_read_other_uid_stats(uid_t uid) -{ - /* root pwnd */ - return unlikely(!current_fsuid()) || uid == current_fsuid() - || unlikely(!proc_stats_readall_gid) - || in_egroup_p(proc_stats_readall_gid); + CT_DEBUG("qtaguid: proc ctrl state debug }\n"); + + + } +#endif + + *eof = 1; + return outp - page; } /* @@ -1401,6 +1645,8 @@ static int ctrl_cmd_delete(const char *input) struct rb_root st_to_free_tree = RB_ROOT; struct tag_stat *ts_entry; struct tag_counter_set *tcs_entry; + struct tag_ref *tr_entry; + struct uid_tag_data *utd_entry; argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid); CT_DEBUG("qtaguid: ctrl_delete(%s): argc=%d cmd=%c " @@ -1419,12 +1665,17 @@ static int ctrl_cmd_delete(const char *input) uid = current_fsuid(); } else if (!can_impersonate_uid(uid)) { pr_info("qtaguid: ctrl_delete(%s): " - "insufficient priv from pid=%u uid=%u\n", - input, current->pid, current_fsuid()); + "insufficient priv from pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); res = -EPERM; goto err; } + tag = combine_atag_with_uid(acct_tag, uid); + CT_DEBUG("qtaguid: ctrl_delete(): " + "looking for tag=0x%llx (uid=%u)\n", + tag, uid); + /* Delete socket tags */ spin_lock_bh(&sock_tag_list_lock); node = rb_first(&sock_tag_tree); @@ -1435,32 +1686,25 @@ static int ctrl_cmd_delete(const char *input) if (entry_uid != uid) continue; + CT_DEBUG("qtaguid: ctrl_delete(): st tag=0x%llx (uid=%u)\n", + st_entry->tag, entry_uid); + if (!acct_tag || st_entry->tag == tag) { rb_erase(&st_entry->sock_node, &sock_tag_tree); /* Can't sockfd_put() within spinlock, do it later. */ sock_tag_tree_insert(st_entry, &st_to_free_tree); + tr_entry = lookup_tag_ref(st_entry->tag, NULL); + BUG_ON(tr_entry->num_sock_tags <= 0); + tr_entry->num_sock_tags--; } } spin_unlock_bh(&sock_tag_list_lock); - node = rb_first(&st_to_free_tree); - while (node) { - st_entry = rb_entry(node, struct sock_tag, sock_node); - node = rb_next(node); - CT_DEBUG("qtaguid: ctrl_delete(): " - "erase st: sk=%p tag=0x%llx (uid=%u)\n", - st_entry->sk, - st_entry->tag, - entry_uid); - rb_erase(&st_entry->sock_node, &st_to_free_tree); - sockfd_put(st_entry->socket); - kfree(st_entry); - } - - tag = combine_atag_with_uid(acct_tag, uid); + sock_tag_tree_erase(&st_to_free_tree); /* Delete tag counter-sets */ spin_lock_bh(&tag_counter_set_list_lock); + /* Counter sets are only on the uid tag, not full tag */ tcs_entry = tag_counter_set_tree_search(&tag_counter_set_tree, tag); if (tcs_entry) { CT_DEBUG("qtaguid: ctrl_delete(): " @@ -1485,6 +1729,11 @@ static int ctrl_cmd_delete(const char *input) ts_entry = rb_entry(node, struct tag_stat, tn.node); entry_uid = get_uid_from_tag(ts_entry->tn.tag); node = rb_next(node); + + CT_DEBUG("qtaguid: ctrl_delete(): " + "ts tag=0x%llx (uid=%u)\n", + ts_entry->tn.tag, entry_uid); + if (entry_uid != uid) continue; if (!acct_tag || ts_entry->tn.tag == tag) { @@ -1501,6 +1750,30 @@ static int ctrl_cmd_delete(const char *input) spin_unlock_bh(&iface_entry->tag_stat_list_lock); } spin_unlock_bh(&iface_stat_list_lock); + + /* Cleanup the uid_tag_data */ + spin_lock_bh(&uid_tag_data_tree_lock); + node = rb_first(&uid_tag_data_tree); + while (node) { + utd_entry = rb_entry(node, struct uid_tag_data, node); + entry_uid = utd_entry->uid; + node = rb_next(node); + + CT_DEBUG("qtaguid: ctrl_delete(): " + "utd uid=%u\n", + entry_uid); + + if (entry_uid != uid) + continue; + /* + * Go over the tag_refs, and those that don't have + * sock_tags using them are freed. + */ + put_tag_ref_tree(tag, utd_entry); + put_utd_entry(utd_entry); + } + spin_unlock_bh(&uid_tag_data_tree_lock); + atomic64_inc(&qtu_events.delete_cmds); res = 0; @@ -1533,8 +1806,8 @@ static int ctrl_cmd_counter_set(const char *input) } if (!can_manipulate_uids()) { pr_info("qtaguid: ctrl_counterset(%s): " - "insufficient priv from pid=%u uid=%u\n", - input, current->pid, current_fsuid()); + "insufficient priv from pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); res = -EPERM; goto err; } @@ -1572,11 +1845,14 @@ static int ctrl_cmd_tag(const char *input) char cmd; int sock_fd = 0; uid_t uid = 0; - tag_t acct_tag = 0; + tag_t acct_tag = make_atag_from_value(0); + tag_t full_tag; struct socket *el_socket; - int refcnt = -1; int res, argc; struct sock_tag *sock_tag_entry; + struct tag_ref *tag_ref_entry; + struct uid_tag_data *uid_tag_data_entry; + struct proc_qtu_data *pqd_entry; /* Unassigned args will get defaulted later. */ argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid); @@ -1593,44 +1869,66 @@ static int ctrl_cmd_tag(const char *input) " sock_fd=%d err=%d\n", input, sock_fd, res); goto err; } - refcnt = atomic_read(&el_socket->file->f_count); - CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%d\n", - input, refcnt); + CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n", + input, atomic_long_read(&el_socket->file->f_count), + el_socket->sk); if (argc < 3) { - acct_tag = 0; + acct_tag = make_atag_from_value(0); } else if (!valid_atag(acct_tag)) { pr_info("qtaguid: ctrl_tag(%s): invalid tag\n", input); res = -EINVAL; goto err_put; } CT_DEBUG("qtaguid: ctrl_tag(%s): " - "uid=%u euid=%u fsuid=%u " + "pid=%u tgid=%u uid=%u euid=%u fsuid=%u " "in_group=%d in_egroup=%d\n", - input, current_uid(), current_euid(), current_fsuid(), - in_group_p(proc_stats_readall_gid), - in_egroup_p(proc_stats_readall_gid)); + input, current->pid, current->tgid, current_uid(), + current_euid(), current_fsuid(), + in_group_p(proc_ctrl_write_gid), + in_egroup_p(proc_ctrl_write_gid)); if (argc < 4) { uid = current_fsuid(); } else if (!can_impersonate_uid(uid)) { pr_info("qtaguid: ctrl_tag(%s): " - "insufficient priv from pid=%u uid=%u\n", - input, current->pid, current_fsuid()); + "insufficient priv from pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); res = -EPERM; goto err_put; } + full_tag = combine_atag_with_uid(acct_tag, uid); spin_lock_bh(&sock_tag_list_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); + tag_ref_entry = get_tag_ref(full_tag, &uid_tag_data_entry); + if (IS_ERR(tag_ref_entry)) { + res = PTR_ERR(tag_ref_entry); + spin_unlock_bh(&sock_tag_list_lock); + goto err_put; + } + tag_ref_entry->num_sock_tags++; if (sock_tag_entry) { + struct tag_ref *prev_tag_ref_entry; + + CT_DEBUG("qtaguid: ctrl_tag(%s): retag for sk=%p " + "st@%p ...->f_count=%ld\n", + input, el_socket->sk, sock_tag_entry, + atomic_long_read(&el_socket->file->f_count)); /* * This is a re-tagging, so release the sock_fd that was * locked at the time of the 1st tagging. + * There is still the ref from this call's sockfd_lookup() so + * it can be done within the spinlock. */ sockfd_put(sock_tag_entry->socket); - refcnt--; - sock_tag_entry->tag = combine_atag_with_uid(acct_tag, - uid); + prev_tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, + &uid_tag_data_entry); + BUG_ON(IS_ERR_OR_NULL(prev_tag_ref_entry)); + BUG_ON(prev_tag_ref_entry->num_sock_tags <= 0); + prev_tag_ref_entry->num_sock_tags--; + sock_tag_entry->tag = full_tag; } else { + CT_DEBUG("qtaguid: ctrl_tag(%s): newtag for sk=%p\n", + input, el_socket->sk); sock_tag_entry = kzalloc(sizeof(*sock_tag_entry), GFP_ATOMIC); if (!sock_tag_entry) { @@ -1639,29 +1937,47 @@ static int ctrl_cmd_tag(const char *input) input); spin_unlock_bh(&sock_tag_list_lock); res = -ENOMEM; - goto err_put; + goto err_tag_unref_put; } sock_tag_entry->sk = el_socket->sk; sock_tag_entry->socket = el_socket; + sock_tag_entry->pid = current->tgid; sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid); + spin_lock_bh(&uid_tag_data_tree_lock); + pqd_entry = proc_qtu_data_tree_search( + &proc_qtu_data_tree, current->tgid); + /* TODO: remove if() test, do BUG_ON() */ + WARN_ON(IS_ERR_OR_NULL(pqd_entry)); + if (!IS_ERR_OR_NULL(pqd_entry)) { + list_add(&sock_tag_entry->list, + &pqd_entry->sock_tag_list); + } + spin_unlock_bh(&uid_tag_data_tree_lock); + sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); atomic64_inc(&qtu_events.sockets_tagged); } spin_unlock_bh(&sock_tag_list_lock); /* We keep the ref to the socket (file) until it is untagged */ - CT_DEBUG("qtaguid: ctrl_tag(%s): done. socket->...->f_count=%d\n", - input, - el_socket ? atomic_read(&el_socket->file->f_count) : -1); + CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->f_count=%ld\n", + input, sock_tag_entry, + atomic_long_read(&el_socket->file->f_count)); return 0; +err_tag_unref_put: + BUG_ON(tag_ref_entry->num_sock_tags <= 0); + tag_ref_entry->num_sock_tags--; + free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry); err_put: + CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->f_count=%ld\n", + input, atomic_long_read(&el_socket->file->f_count) - 1); /* Release the sock_fd that was grabbed by sockfd_lookup(). */ sockfd_put(el_socket); - refcnt--; + return res; + err: - CT_DEBUG("qtaguid: ctrl_tag(%s): done. socket->...->f_count=%d\n", - input, refcnt); + CT_DEBUG("qtaguid: ctrl_tag(%s): done.\n", input); return res; } @@ -1670,9 +1986,11 @@ static int ctrl_cmd_untag(const char *input) char cmd; int sock_fd = 0; struct socket *el_socket; - int refcnt = -1; int res, argc; struct sock_tag *sock_tag_entry; + struct tag_ref *tag_ref_entry; + struct uid_tag_data *utd_entry; + struct proc_qtu_data *pqd_entry; argc = sscanf(input, "%c %d", &cmd, &sock_fd); CT_DEBUG("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", @@ -1687,9 +2005,9 @@ static int ctrl_cmd_untag(const char *input) " sock_fd=%d err=%d\n", input, sock_fd, res); goto err; } - refcnt = atomic_read(&el_socket->file->f_count); - CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%d\n", - input, refcnt); + CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n", + input, atomic_long_read(&el_socket->file->f_count), + el_socket->sk); spin_lock_bh(&sock_tag_list_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); if (!sock_tag_entry) { @@ -1703,28 +2021,47 @@ static int ctrl_cmd_untag(const char *input) */ rb_erase(&sock_tag_entry->sock_node, &sock_tag_tree); + tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag, &utd_entry); + BUG_ON(!tag_ref_entry); + BUG_ON(tag_ref_entry->num_sock_tags <= 0); + spin_lock_bh(&uid_tag_data_tree_lock); + pqd_entry = proc_qtu_data_tree_search( + &proc_qtu_data_tree, current->tgid); + /* TODO: remove if() test, do BUG_ON() */ + WARN_ON(IS_ERR_OR_NULL(pqd_entry)); + if (!IS_ERR_OR_NULL(pqd_entry)) + list_del(&sock_tag_entry->list); + spin_unlock_bh(&uid_tag_data_tree_lock); + /* + * We don't free tag_ref from the utd_entry here, + * only during a cmd_delete(). + */ + tag_ref_entry->num_sock_tags--; + spin_unlock_bh(&sock_tag_list_lock); /* * Release the sock_fd that was grabbed at tag time, * and once more for the sockfd_lookup() here. */ sockfd_put(sock_tag_entry->socket); - spin_unlock_bh(&sock_tag_list_lock); + CT_DEBUG("qtaguid: ctrl_untag(%s): done. st@%p ...->f_count=%ld\n", + input, sock_tag_entry, + atomic_long_read(&el_socket->file->f_count) - 1); sockfd_put(el_socket); - refcnt -= 2; + kfree(sock_tag_entry); atomic64_inc(&qtu_events.sockets_untagged); - CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%d\n", - input, refcnt); return 0; err_put: + CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%ld\n", + input, atomic_long_read(&el_socket->file->f_count) - 1); /* Release the sock_fd that was grabbed by sockfd_lookup(). */ sockfd_put(el_socket); - refcnt--; + return res; + err: - CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%d\n", - input, refcnt); + CT_DEBUG("qtaguid: ctrl_untag(%s): done.\n", input); return res; } @@ -1816,11 +2153,11 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) if (!can_read_other_uid_stats(stat_uid)) { CT_DEBUG("qtaguid: stats line: " - "%s 0x%llx %u: " - "insufficient priv from pid=%u uid=%u\n", + "%s 0x%llx %u: insufficient priv " + "from pid=%u tgid=%u uid=%u\n", ppi->iface_entry->ifname, get_atag_from_tag(tag), stat_uid, - current->pid, current_fsuid()); + current->pid, current->tgid, current_fsuid()); return 0; } if (ppi->item_index++ < ppi->items_to_skip) @@ -1903,7 +2240,7 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, if (unlikely(module_passive)) { len = pp_stats_line(&ppi, 0); /* The header should always be shorter than the buffer. */ - WARN_ON(len >= ppi.char_count); + BUG_ON(len >= ppi.char_count); (*num_items_returned)++; *eof = 1; return len; @@ -1952,6 +2289,163 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, return ppi.outp - page; } +/*------------------------------------------*/ +static int qtudev_open(struct inode *inode, struct file *file) +{ + struct uid_tag_data *utd_entry; + struct proc_qtu_data *pqd_entry; + struct proc_qtu_data *new_pqd_entry = 0; + int res; + bool utd_entry_found; + + if (unlikely(qtu_proc_handling_passive)) + return 0; + + DR_DEBUG("qtaguid: qtudev_open(): pid=%u tgid=%u uid=%u\n", + current->pid, current->tgid, current_fsuid()); + + spin_lock_bh(&uid_tag_data_tree_lock); + + /* Look for existing uid data, or alloc one. */ + utd_entry = get_uid_data(current_fsuid(), &utd_entry_found); + if (IS_ERR_OR_NULL(utd_entry)) { + res = PTR_ERR(utd_entry); + goto err; + } + + /* Look for existing PID based proc_data */ + pqd_entry = proc_qtu_data_tree_search(&proc_qtu_data_tree, + current->tgid); + if (pqd_entry) { + pr_err("qtaguid: qtudev_open(): %u/%u %u " + "%s already opened\n", + current->pid, current->tgid, current_fsuid(), + QTU_DEV_NAME); + res = -EBUSY; + goto err_unlock_free_utd; + } + + new_pqd_entry = kzalloc(sizeof(*new_pqd_entry), GFP_ATOMIC); + if (!new_pqd_entry) { + pr_err("qtaguid: qtudev_open(): %u/%u %u: " + "proc data alloc failed\n", + current->pid, current->tgid, current_fsuid()); + res = -ENOMEM; + goto err_unlock_free_utd; + } + new_pqd_entry->pid = current->tgid; + INIT_LIST_HEAD(&new_pqd_entry->sock_tag_list); + new_pqd_entry->parent_tag_data = utd_entry; + + proc_qtu_data_tree_insert(new_pqd_entry, + &proc_qtu_data_tree); + + spin_unlock_bh(&uid_tag_data_tree_lock); + DR_DEBUG("qtaguid: tracking data for uid=%u\n", current_fsuid()); + file->private_data = new_pqd_entry; + return 0; + +err_unlock_free_utd: + if (!utd_entry_found) { + rb_erase(&utd_entry->node, &uid_tag_data_tree); + kfree(utd_entry); + } + spin_unlock_bh(&uid_tag_data_tree_lock); +err: + return res; +} + +static int qtudev_release(struct inode *inode, struct file *file) +{ + struct proc_qtu_data *pqd_entry = file->private_data; + struct uid_tag_data *utd_entry = pqd_entry->parent_tag_data; + struct sock_tag *st_entry; + struct rb_root st_to_free_tree = RB_ROOT; + struct list_head *entry, *next; + struct tag_ref *tr; + + if (unlikely(qtu_proc_handling_passive)) + return 0; + + /* + * Do not trust the current->pid, it might just be a kworker cleaning + * up after a dead proc. + */ + DR_DEBUG("qtaguid: qtudev_release(): " + "pid=%u tgid=%u uid=%u " + "pqd_entry=%p->pid=%u utd_entry=%p->active_tags=%d\n", + current->pid, current->tgid, pqd_entry->parent_tag_data->uid, + pqd_entry, pqd_entry->pid, utd_entry, + utd_entry->num_active_tags); + + spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); + + /* + * If this proc didn't actually tag anything for itself, or has already + * willingly cleaned up itself ... + */ + put_utd_entry(utd_entry); + + list_for_each_safe(entry, next, &pqd_entry->sock_tag_list) { + st_entry = list_entry(entry, struct sock_tag, list); + DR_DEBUG("qtaguid: %s(): " + "erase sock_tag=%p->sk=%p pid=%u tgid=%u uid=%u\n", + __func__, + st_entry, st_entry->sk, + current->pid, current->tgid, + pqd_entry->parent_tag_data->uid); + + utd_entry = uid_tag_data_tree_search( + &uid_tag_data_tree, + get_uid_from_tag(st_entry->tag)); + BUG_ON(IS_ERR_OR_NULL(utd_entry)); + DR_DEBUG("qtaguid: %s(): " + "looking for tag=0x%llx in utd_entry=%p\n", __func__, + st_entry->tag, utd_entry); + tr = tag_ref_tree_search(&utd_entry->tag_ref_tree, + st_entry->tag); + BUG_ON(!tr); + BUG_ON(tr->num_sock_tags <= 0); + tr->num_sock_tags--; + free_tag_ref_from_utd_entry(tr, utd_entry); + + rb_erase(&st_entry->sock_node, &sock_tag_tree); + list_del(&st_entry->list); + /* Can't sockfd_put() within spinlock, do it later. */ + sock_tag_tree_insert(st_entry, &st_to_free_tree); + + /* Do not put_utd_entry(utd_entry) someone elses utd_entry */ + } + + rb_erase(&pqd_entry->node, &proc_qtu_data_tree); + kfree(pqd_entry); + file->private_data = NULL; + + spin_unlock_bh(&uid_tag_data_tree_lock); + spin_unlock_bh(&sock_tag_list_lock); + + + sock_tag_tree_erase(&st_to_free_tree); + + + return 0; +} + +/*------------------------------------------*/ +static const struct file_operations qtudev_fops = { + .owner = THIS_MODULE, + .open = qtudev_open, + .release = qtudev_release, +}; + +static struct miscdevice qtu_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = QTU_DEV_NAME, + .fops = &qtudev_fops, + /* How sad it doesn't allow for defaults: .mode = S_IRUGO | S_IWUSR */ +}; + /*------------------------------------------*/ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) { @@ -2014,7 +2508,8 @@ static int __init qtaguid_mt_init(void) { if (qtaguid_proc_register(&xt_qtaguid_procdir) || iface_stat_init(xt_qtaguid_procdir) - || xt_register_match(&qtaguid_mt_reg)) + || xt_register_match(&qtaguid_mt_reg) + || misc_register(&qtu_device)) return -1; return 0; } diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h new file mode 100644 index 0000000000000..752e196e2e4c0 --- /dev/null +++ b/net/netfilter/xt_qtaguid_internal.h @@ -0,0 +1,305 @@ +/* + * Kernel iptables module to track stats for packets based on user tags. + * + * (C) 2011 Google, Inc + * + * 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. + */ +#ifndef __XT_QTAGUID_INTERNAL_H__ +#define __XT_QTAGUID_INTERNAL_H__ + +#include +#include +#include +#include + +/* Define/comment out these *DEBUG to compile in/out the pr_debug calls. */ +/* Iface handling */ +#define IDEBUG +/* Iptable Matching. Per packet. */ +#define MDEBUG +/* Red-black tree handling. Per packet. */ +#define RDEBUG +/* procfs ctrl/stats handling */ +#define CDEBUG +/* dev and resource tracking */ +#define DDEBUG + +/* E.g (IDEBUG_MASK | CDEBUG_MASK | DDEBUG_MASK) */ +#define DEFAULT_DEBUG_MASK 0 + + +#define IDEBUG_MASK (1<<0) +#define MDEBUG_MASK (1<<1) +#define RDEBUG_MASK (1<<2) +#define CDEBUG_MASK (1<<3) +#define DDEBUG_MASK (1<<4) + +#define MSK_DEBUG(mask, ...) do { \ + if (unlikely(debug_mask & (mask))) \ + pr_debug(__VA_ARGS__); \ + } while (0) +#ifdef IDEBUG +#define IF_DEBUG(...) MSK_DEBUG(IDEBUG_MASK, __VA_ARGS__) +#else +#define IF_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef MDEBUG +#define MT_DEBUG(...) MSK_DEBUG(MDEBUG_MASK, __VA_ARGS__) +#else +#define MT_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef RDEBUG +#define RB_DEBUG(...) MSK_DEBUG(RDEBUG_MASK, __VA_ARGS__) +#else +#define RB_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef CDEBUG +#define CT_DEBUG(...) MSK_DEBUG(CDEBUG_MASK, __VA_ARGS__) +#else +#define CT_DEBUG(...) no_printk(__VA_ARGS__) +#endif +#ifdef DDEBUG +#define DR_DEBUG(...) MSK_DEBUG(DDEBUG_MASK, __VA_ARGS__) +#else +#define DR_DEBUG(...) no_printk(__VA_ARGS__) +#endif + +extern uint debug_mask; + +/*---------------------------------------------------------------------------*/ +/* + * Tags: + * + * They represent what the data usage counters will be tracked against. + * By default a tag is just based on the UID. + * The UID is used as the base for policing, and can not be ignored. + * So a tag will always at least represent a UID (uid_tag). + * + * A tag can be augmented with an "accounting tag" which is associated + * with a UID. + * User space can set the acct_tag portion of the tag which is then used + * with sockets: all data belonging to that socket will be counted against the + * tag. The policing is then based on the tag's uid_tag portion, + * and stats are collected for the acct_tag portion separately. + * + * There could be + * a: {acct_tag=1, uid_tag=10003} + * b: {acct_tag=2, uid_tag=10003} + * c: {acct_tag=3, uid_tag=10003} + * d: {acct_tag=0, uid_tag=10003} + * a, b, and c represent tags associated with specific sockets. + * d is for the totals for that uid, including all untagged traffic. + * Typically d is used with policing/quota rules. + * + * We want tag_t big enough to distinguish uid_t and acct_tag. + * It might become a struct if needed. + * Nothing should be using it as an int. + */ +typedef uint64_t tag_t; /* Only used via accessors */ + +#define TAG_UID_MASK 0xFFFFFFFFULL +#define TAG_ACCT_MASK (~0xFFFFFFFFULL) + +static inline int tag_compare(tag_t t1, tag_t t2) +{ + return t1 < t2 ? -1 : t1 == t2 ? 0 : 1; +} + +static inline tag_t combine_atag_with_uid(tag_t acct_tag, uid_t uid) +{ + return acct_tag | uid; +} +static inline tag_t make_tag_from_uid(uid_t uid) +{ + return uid; +} +static inline uid_t get_uid_from_tag(tag_t tag) +{ + return tag & TAG_UID_MASK; +} +static inline tag_t get_utag_from_tag(tag_t tag) +{ + return tag & TAG_UID_MASK; +} +static inline tag_t get_atag_from_tag(tag_t tag) +{ + return tag & TAG_ACCT_MASK; +} + +static inline bool valid_atag(tag_t tag) +{ + return !(tag & TAG_UID_MASK); +} +static inline tag_t make_atag_from_value(uint32_t value) +{ + return (uint64_t)value << 32; +} +/*---------------------------------------------------------------------------*/ + +/* + * Maximum number of socket tags that a UID is allowed to have active. + * Multiple processes belonging to the same UID contribute towards this limit. + * Special UIDs that can impersonate a UID also contribute (e.g. download + * manager, ...) + */ +#define DEFAULT_MAX_SOCK_TAGS 1024 + +/* + * For now we only track 2 sets of counters. + * The default set is 0. + * Userspace can activate another set for a given uid being tracked. + */ +#define IFS_MAX_COUNTER_SETS 2 + +enum ifs_tx_rx { + IFS_TX, + IFS_RX, + IFS_MAX_DIRECTIONS +}; + +/* For now, TCP, UDP, the rest */ +enum ifs_proto { + IFS_TCP, + IFS_UDP, + IFS_PROTO_OTHER, + IFS_MAX_PROTOS +}; + +struct byte_packet_counters { + uint64_t bytes; + uint64_t packets; +}; + +struct data_counters { + struct byte_packet_counters bpc[IFS_MAX_COUNTER_SETS][IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS]; +}; + +/* Generic X based nodes used as a base for rb_tree ops */ +struct tag_node { + struct rb_node node; + tag_t tag; +}; + +struct tag_stat { + struct tag_node tn; + struct data_counters counters; + /* + * If this tag is acct_tag based, we need to count against the + * matching parent uid_tag. + */ + struct data_counters *parent_counters; +}; + +struct iface_stat { + struct list_head list; /* in iface_stat_list */ + char *ifname; + uint64_t rx_bytes; + uint64_t rx_packets; + uint64_t tx_bytes; + uint64_t tx_packets; + bool active; + struct proc_dir_entry *proc_ptr; + + struct rb_root tag_stat_tree; + spinlock_t tag_stat_list_lock; +}; + +/* This is needed to create proc_dir_entries from atomic context. */ +struct iface_stat_work { + struct work_struct iface_work; + struct iface_stat *iface_entry; +}; + +/* + * Track tag that this socket is transferring data for, and not necessarily + * the uid that owns the socket. + * This is the tag against which tag_stat.counters will be billed. + * These structs need to be looked up by sock and pid. + */ +struct sock_tag { + struct rb_node sock_node; + struct sock *sk; /* Only used as a number, never dereferenced */ + /* The socket is needed for sockfd_put() */ + struct socket *socket; + /* Used to associate with a given pid */ + struct list_head list; /* in proc_qtu_data.sock_tag_list */ + pid_t pid; + + tag_t tag; +}; + +struct qtaguid_event_counts { + /* Various successful events */ + atomic64_t sockets_tagged; + atomic64_t sockets_untagged; + atomic64_t counter_set_changes; + atomic64_t delete_cmds; + atomic64_t iface_events; /* Number of NETDEV_* events handled */ + /* + * match_found_sk_*: numbers related to the netfilter matching + * function finding a sock for the sk_buff. + */ + atomic64_t match_found_sk; /* An sk was already in the sk_buff. */ + /* The connection tracker had the sk. */ + atomic64_t match_found_sk_in_ct; + /* + * No sk could be found. No apparent owner. Could happen with + * unsolicited traffic. + */ + atomic64_t match_found_sk_none; +}; + +/* Track the set active_set for the given tag. */ +struct tag_counter_set { + struct tag_node tn; + int active_set; +}; + +/*----------------------------------------------*/ +/* + * The qtu uid data is used to track resources that are created directly or + * indirectly by processes (uid tracked). + * It is shared by the processes with the same uid. + * Some of the resource will be counted to prevent further rogue allocations, + * some will need freeing once the owner process (uid) exits. + */ +struct uid_tag_data { + struct rb_node node; + uid_t uid; + + /* + * For the uid, how many accounting tags have been set. + */ + int num_active_tags; + struct rb_root tag_ref_tree; + /* No tag_node_tree_lock; use uid_tag_data_tree_lock */ +}; + +struct tag_ref { + struct tag_node tn; + + /* + * This tracks the number of active sockets that have a tag on them + * which matches this tag_ref.tn.tag. + * A tag ref can live on after the sockets are untagged. + * A tag ref can only be removed during a tag delete command. + */ + int num_sock_tags; +}; + +struct proc_qtu_data { + struct rb_node node; + pid_t pid; + + struct uid_tag_data *parent_tag_data; + + /* Tracks the sock_tags that need freeing upon this proc's death */ + struct list_head sock_tag_list; + /* No spinlock_t sock_tag_list_lock; use the global one. */ +}; + +/*----------------------------------------------*/ +#endif /* ifndef __XT_QTAGUID_INTERNAL_H__ */ diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c new file mode 100644 index 0000000000000..3d054474f9a2a --- /dev/null +++ b/net/netfilter/xt_qtaguid_print.c @@ -0,0 +1,397 @@ +/* + * Pretty printing Support for iptables xt_qtaguid module. + * + * (C) 2011 Google, Inc + * + * 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. + */ + +/* + * There are run-time debug flags enabled via the debug_mask module param, or + * via the DEFAULT_DEBUG_MASK. See xt_qtaguid_internal.h. + */ +#define DEBUG + +#include +#include +#include +#include +#include +#include + + +#include "xt_qtaguid_internal.h" +#include "xt_qtaguid_print.h" + +char *pp_tag_t(tag_t *tag) +{ + if (!tag) + return kasprintf(GFP_ATOMIC, "tag_t@null{}"); + return kasprintf(GFP_ATOMIC, + "tag_t@%p{tag=0x%llx, uid=%u}", + tag, *tag, get_uid_from_tag(*tag)); +} + +char *pp_data_counters(struct data_counters *dc, bool showValues) +{ + if (!dc) + return kasprintf(GFP_ATOMIC, "data_counters@null{}"); + if (showValues) + return kasprintf( + GFP_ATOMIC, "data_counters@%p{" + "set0{" + "rx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}, " + "tx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}}, " + "set1{" + "rx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}, " + "tx{" + "tcp{b=%llu, p=%llu}, " + "udp{b=%llu, p=%llu}," + "other{b=%llu, p=%llu}}}}", + dc, + dc->bpc[0][IFS_RX][IFS_TCP].bytes, + dc->bpc[0][IFS_RX][IFS_TCP].packets, + dc->bpc[0][IFS_RX][IFS_UDP].bytes, + dc->bpc[0][IFS_RX][IFS_UDP].packets, + dc->bpc[0][IFS_RX][IFS_PROTO_OTHER].bytes, + dc->bpc[0][IFS_RX][IFS_PROTO_OTHER].packets, + dc->bpc[0][IFS_TX][IFS_TCP].bytes, + dc->bpc[0][IFS_TX][IFS_TCP].packets, + dc->bpc[0][IFS_TX][IFS_UDP].bytes, + dc->bpc[0][IFS_TX][IFS_UDP].packets, + dc->bpc[0][IFS_TX][IFS_PROTO_OTHER].bytes, + dc->bpc[0][IFS_TX][IFS_PROTO_OTHER].packets, + dc->bpc[1][IFS_RX][IFS_TCP].bytes, + dc->bpc[1][IFS_RX][IFS_TCP].packets, + dc->bpc[1][IFS_RX][IFS_UDP].bytes, + dc->bpc[1][IFS_RX][IFS_UDP].packets, + dc->bpc[1][IFS_RX][IFS_PROTO_OTHER].bytes, + dc->bpc[1][IFS_RX][IFS_PROTO_OTHER].packets, + dc->bpc[1][IFS_TX][IFS_TCP].bytes, + dc->bpc[1][IFS_TX][IFS_TCP].packets, + dc->bpc[1][IFS_TX][IFS_UDP].bytes, + dc->bpc[1][IFS_TX][IFS_UDP].packets, + dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].bytes, + dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].packets); + else + return kasprintf(GFP_ATOMIC, "data_counters@%p{...}", dc); +} + +char *pp_tag_node(struct tag_node *tn) +{ + char *tag_str; + char *res; + + if (!tn) + return kasprintf(GFP_ATOMIC, "tag_node@null{}"); + tag_str = pp_tag_t(&tn->tag); + res = kasprintf(GFP_ATOMIC, + "tag_node@%p{tag=%s}", + tn, tag_str); + kfree(tag_str); + return res; +} + +char *pp_tag_ref(struct tag_ref *tr) +{ + char *tn_str; + char *res; + + if (!tr) + return kasprintf(GFP_ATOMIC, "tag_ref@null{}"); + tn_str = pp_tag_node(&tr->tn); + res = kasprintf(GFP_ATOMIC, + "tag_ref@%p{%s, num_sock_tags=%d}", + tr, tn_str, tr->num_sock_tags); + kfree(tn_str); + return res; +} + +char *pp_tag_stat(struct tag_stat *ts) +{ + char *tn_str; + char *counters_str; + char *parent_counters_str; + char *res; + + if (!ts) + return kasprintf(GFP_ATOMIC, "tag_stat@null{}"); + tn_str = pp_tag_node(&ts->tn); + counters_str = pp_data_counters(&ts->counters, true); + parent_counters_str = pp_data_counters(ts->parent_counters, false); + res = kasprintf(GFP_ATOMIC, + "tag_stat@%p{%s, counters=%s, parent_counters=%s}", + ts, tn_str, counters_str, parent_counters_str); + kfree(tn_str); + kfree(counters_str); + kfree(parent_counters_str); + return res; +} + +char *pp_iface_stat(struct iface_stat *is) +{ + if (!is) + return kasprintf(GFP_ATOMIC, "iface_stat@null{}"); + return kasprintf(GFP_ATOMIC, "iface_stat@%p{" + "list=list_head{...}, " + "ifname=%s, " + "rx_bytes=%llu, " + "rx_packets=%llu, " + "tx_bytes=%llu, " + "tx_packets=%llu, " + "active=%d, " + "proc_ptr=%p, " + "tag_stat_tree=rb_root{...}}", + is, + is->ifname, + is->rx_bytes, + is->rx_packets, + is->tx_bytes, + is->tx_packets, + is->active, + is->proc_ptr); +} + +char *pp_sock_tag(struct sock_tag *st) +{ + char *tag_str; + char *res; + + if (!st) + return kasprintf(GFP_ATOMIC, "sock_tag@null{}"); + tag_str = pp_tag_t(&st->tag); + res = kasprintf(GFP_ATOMIC, "sock_tag@%p{" + "sock_node=rb_node{...}, " + "sk=%p socket=%p (f_count=%lu), list=list_head{...}, " + "pid=%u, tag=%s}", + st, st->sk, st->socket, atomic_long_read( + &st->socket->file->f_count), + st->pid, tag_str); + kfree(tag_str); + return res; +} + +char *pp_uid_tag_data(struct uid_tag_data *utd) +{ + char *res; + + if (!utd) + return kasprintf(GFP_ATOMIC, "uid_tag_data@null{}"); + res = kasprintf(GFP_ATOMIC, "uid_tag_data@%p{" + "uid=%u, num_active_acct_tags=%d, " + "tag_node_tree=rb_root{...}, " + "proc_qtu_data_tree=rb_root{...}}", + utd, utd->uid, + utd->num_active_tags); + return res; +} + +char *pp_proc_qtu_data(struct proc_qtu_data *pqd) +{ + char *parent_tag_data_str; + char *res; + + if (!pqd) + return kasprintf(GFP_ATOMIC, "proc_qtu_data@null{}"); + parent_tag_data_str = pp_uid_tag_data(pqd->parent_tag_data); + res = kasprintf(GFP_ATOMIC, "proc_qtu_data@%p{" + "node=rb_node{...}, pid=%u, " + "parent_tag_data=%s, " + "sock_tag_list=list_head{...}}", + pqd, pqd->pid, parent_tag_data_str + ); + kfree(parent_tag_data_str); + return res; +} + +/*------------------------------------------*/ +void prdebug_sock_tag_tree(int indent_level, + struct rb_root *sock_tag_tree) +{ + struct rb_node *node; + struct sock_tag *sock_tag_entry; + char *str; + + str = "sock_tag_tree=rb_root{"; + CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(sock_tag_tree); + node; + node = rb_next(node)) { + sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); + str = pp_sock_tag(sock_tag_entry); + CT_DEBUG("%*d: %s,\n", indent_level*2, indent_level, str); + kfree(str); + } + indent_level--; + str = "}"; + CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_sock_tag_list(int indent_level, + struct list_head *sock_tag_list) +{ + struct sock_tag *sock_tag_entry; + char *str; + + str = "sock_tag_list=list_head{"; + CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + list_for_each_entry(sock_tag_entry, sock_tag_list, list) { + str = pp_sock_tag(sock_tag_entry); + CT_DEBUG("%*d: %s,\n", indent_level*2, indent_level, str); + kfree(str); + } + indent_level--; + str = "}"; + CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_proc_qtu_data_tree(int indent_level, + struct rb_root *proc_qtu_data_tree) +{ + char *str; + struct rb_node *node; + struct proc_qtu_data *proc_qtu_data_entry; + + str = "proc_qtu_data_tree=rb_root{"; + CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(proc_qtu_data_tree); + node; + node = rb_next(node)) { + proc_qtu_data_entry = rb_entry(node, + struct proc_qtu_data, + node); + str = pp_proc_qtu_data(proc_qtu_data_entry); + CT_DEBUG("%*d: %s,\n", indent_level*2, indent_level, + str); + kfree(str); + indent_level++; + prdebug_sock_tag_list(indent_level, + &proc_qtu_data_entry->sock_tag_list); + indent_level--; + + } + indent_level--; + str = "}"; + CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree) +{ + char *str; + struct rb_node *node; + struct tag_ref *tag_ref_entry; + + str = "tag_ref_tree{"; + CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(tag_ref_tree); + node; + node = rb_next(node)) { + tag_ref_entry = rb_entry(node, + struct tag_ref, + tn.node); + str = pp_tag_ref(tag_ref_entry); + CT_DEBUG("%*d: %s,\n", indent_level*2, indent_level, + str); + kfree(str); + } + indent_level--; + str = "}"; + CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_uid_tag_data_tree(int indent_level, + struct rb_root *uid_tag_data_tree) +{ + char *str; + struct rb_node *node; + struct uid_tag_data *uid_tag_data_entry; + + str = "uid_tag_data_tree=rb_root{"; + CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(uid_tag_data_tree); + node; + node = rb_next(node)) { + uid_tag_data_entry = rb_entry(node, struct uid_tag_data, + node); + str = pp_uid_tag_data(uid_tag_data_entry); + CT_DEBUG("%*d: %s,\n", indent_level*2, indent_level, str); + kfree(str); + if (!RB_EMPTY_ROOT(&uid_tag_data_entry->tag_ref_tree)) { + indent_level++; + prdebug_tag_ref_tree(indent_level, + &uid_tag_data_entry->tag_ref_tree); + indent_level--; + } + } + indent_level--; + str = "}"; + CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_tag_stat_tree(int indent_level, + struct rb_root *tag_stat_tree) +{ + char *str; + struct rb_node *node; + struct tag_stat *ts_entry; + + str = "tag_stat_tree{"; + CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + for (node = rb_first(tag_stat_tree); + node; + node = rb_next(node)) { + ts_entry = rb_entry(node, struct tag_stat, tn.node); + str = pp_tag_stat(ts_entry); + CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, + str); + kfree(str); + } + indent_level--; + str = "}"; + CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); +} + +void prdebug_iface_stat_list(int indent_level, + struct list_head *iface_stat_list) +{ + char *str; + struct iface_stat *iface_entry; + + str = "iface_stat_list=list_head{"; + CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + indent_level++; + list_for_each_entry(iface_entry, iface_stat_list, list) { + str = pp_iface_stat(iface_entry); + CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + kfree(str); + + spin_lock_bh(&iface_entry->tag_stat_list_lock); + if (!RB_EMPTY_ROOT(&iface_entry->tag_stat_tree)) { + indent_level++; + prdebug_tag_stat_tree(indent_level, + &iface_entry->tag_stat_tree); + indent_level--; + } + spin_unlock_bh(&iface_entry->tag_stat_list_lock); + } + indent_level--; + str = "}"; + CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); +} diff --git a/net/netfilter/xt_qtaguid_print.h b/net/netfilter/xt_qtaguid_print.h new file mode 100644 index 0000000000000..e26020c91dfc2 --- /dev/null +++ b/net/netfilter/xt_qtaguid_print.h @@ -0,0 +1,39 @@ +/* + * Pretty printing Support for iptables xt_qtaguid module. + * + * (C) 2011 Google, Inc + * + * 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. + */ +#ifndef __XT_QTAGUID_PRINT_H__ +#define __XT_QTAGUID_PRINT_H__ + +#include "xt_qtaguid_internal.h" + +char *pp_tag_t(tag_t *tag); +char *pp_data_counters(struct data_counters *dc, bool showValues); +char *pp_tag_node(struct tag_node *tn); +char *pp_tag_ref(struct tag_ref *tr); +char *pp_tag_stat(struct tag_stat *ts); +char *pp_iface_stat(struct iface_stat *is); +char *pp_sock_tag(struct sock_tag *st); +char *pp_uid_tag_data(struct uid_tag_data *qtd); +char *pp_proc_qtu_data(struct proc_qtu_data *pqd); + +/*------------------------------------------*/ +void prdebug_sock_tag_list(int indent_level, + struct list_head *sock_tag_list); +void prdebug_sock_tag_tree(int indent_level, + struct rb_root *sock_tag_tree); +void prdebug_proc_qtu_data_tree(int indent_level, + struct rb_root *proc_qtu_data_tree); +void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree); +void prdebug_uid_tag_data_tree(int indent_level, + struct rb_root *uid_tag_data_tree); +void prdebug_tag_stat_tree(int indent_level, + struct rb_root *tag_stat_tree); +void prdebug_iface_stat_list(int indent_level, + struct list_head *iface_stat_list); +#endif /* ifndef __XT_QTAGUID_PRINT_H__ */ From 28c8582040e5d022067f12dfddc5f3de0993e827 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Sun, 11 Sep 2011 12:13:41 -0700 Subject: [PATCH 1970/2556] netfilter: xt_qtaguid: warn only once for missing proc qtaguid data When a process doesn't have /dev/xt_qtaguid open, only warn once instead of for every ctrl access. Change-Id: I98a462a8731254ddc3bf6d2fefeef9823659b1f0 Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index dff2ff3fd7cc8..66eab3a9c4a47 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1947,8 +1947,15 @@ static int ctrl_cmd_tag(const char *input) spin_lock_bh(&uid_tag_data_tree_lock); pqd_entry = proc_qtu_data_tree_search( &proc_qtu_data_tree, current->tgid); - /* TODO: remove if() test, do BUG_ON() */ - WARN_ON(IS_ERR_OR_NULL(pqd_entry)); + /* + * TODO: remove if, and start failing. + * At first, we want to catch user-space code that is not + * opening the /dev/xt_qtaguid. + */ + WARN_ONCE(IS_ERR_OR_NULL(pqd_entry), + "qtaguid: User space forgot to open /dev/xt_qtaguid? " + "pid=%u tgid=%u uid=%u\n", + current->pid, current->tgid, current_fsuid()); if (!IS_ERR_OR_NULL(pqd_entry)) { list_add(&sock_tag_entry->list, &pqd_entry->sock_tag_list); @@ -2027,8 +2034,15 @@ static int ctrl_cmd_untag(const char *input) spin_lock_bh(&uid_tag_data_tree_lock); pqd_entry = proc_qtu_data_tree_search( &proc_qtu_data_tree, current->tgid); - /* TODO: remove if() test, do BUG_ON() */ - WARN_ON(IS_ERR_OR_NULL(pqd_entry)); + /* + * TODO: remove if, and start failing. + * At first, we want to catch user-space code that is not + * opening the /dev/xt_qtaguid. + */ + WARN_ONCE(IS_ERR_OR_NULL(pqd_entry), + "qtaguid: User space forgot to open /dev/xt_qtaguid? " + "pid=%u tgid=%u uid=%u\n", + current->pid, current->tgid, current_fsuid()); if (!IS_ERR_OR_NULL(pqd_entry)) list_del(&sock_tag_entry->list); spin_unlock_bh(&uid_tag_data_tree_lock); From 10df60ad0b756345e96468a9ca925df75ce57f20 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Thu, 15 Sep 2011 00:56:20 -0700 Subject: [PATCH 1971/2556] netfilter: xt_qtaguid: work around devices that reset their stats Most net devs will not reset their stats when just going down/up, unless a NETDEV_UNREGISTER was notified. But some devs will not send out a NETDEV_UNREGISTER but still reset their stats just before a NETDEV_UP. Now we just track the dev stats during NETDEV_DOWN... just in case. Then on NETDEV_UP we check the stats: if the device didn't do a NETDEV_UNREGISTER and a prior NETDEV_DOWN captured stats, then we treat it as an UNREGISTER and save the totals from the stashed values. Added extra netdev event debugging. Change-Id: Iec79e74bfd40269aa3e5892f161be71e09de6946 Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 120 +++++++++++++++++++++------- net/netfilter/xt_qtaguid_internal.h | 17 +++- net/netfilter/xt_qtaguid_print.c | 59 ++++++++++++-- net/netfilter/xt_qtaguid_print.h | 3 + 4 files changed, 159 insertions(+), 40 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 66eab3a9c4a47..3cdf1d8a26da5 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -769,13 +769,13 @@ static void iface_create_proc_worker(struct work_struct *work) new_iface->proc_ptr = proc_entry; create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->tx_bytes); + read_proc_u64, &new_iface->totals[IFS_TX].bytes); create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->rx_bytes); + read_proc_u64, &new_iface->totals[IFS_RX].bytes); create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->tx_packets); + read_proc_u64, &new_iface->totals[IFS_TX].packets); create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->rx_packets); + read_proc_u64, &new_iface->totals[IFS_RX].packets); create_proc_read_entry("active", proc_iface_perms, proc_entry, read_proc_bool, &new_iface->active); @@ -826,13 +826,53 @@ static struct iface_stat *iface_alloc(const char *ifname) return new_iface; } +static void iface_check_stats_reset_and_adjust(struct net_device *net_dev, + struct iface_stat *iface) +{ + struct rtnl_link_stats64 dev_stats, *stats; + bool stats_rewound; + + stats = dev_get_stats(net_dev, &dev_stats); + /* No empty packets */ + stats_rewound = + (stats->rx_bytes < iface->last_known[IFS_RX].bytes) + || (stats->tx_bytes < iface->last_known[IFS_TX].bytes); + + IF_DEBUG("qtaguid: %s(%s): iface=%p netdev=%p " + "bytes rx/tx=%llu/%llu " + "active=%d last_known=%d " + "stats_rewound=%d\n", __func__, + net_dev ? net_dev->name : "?", + iface, net_dev, + stats->rx_bytes, stats->tx_bytes, + iface->active, iface->last_known_valid, stats_rewound); + + if (iface->active && iface->last_known_valid && stats_rewound) { + pr_warn_once("qtaguid: iface_stat: %s(%s): " + "iface reset its stats unexpectedly\n", __func__, + net_dev->name); + + iface->totals[IFS_TX].bytes += iface->last_known[IFS_TX].bytes; + iface->totals[IFS_TX].packets += + iface->last_known[IFS_TX].packets; + iface->totals[IFS_RX].bytes += iface->last_known[IFS_RX].bytes; + iface->totals[IFS_RX].packets += + iface->last_known[IFS_RX].packets; + iface->last_known_valid = false; + IF_DEBUG("qtaguid: %s(%s): iface=%p " + "used last known bytes rx/tx=%llu/%llu\n", __func__, + iface->ifname, iface, iface->last_known[IFS_RX].bytes, + iface->last_known[IFS_TX].bytes); + } +} + /* * Create a new entry for tracking the specified interface. * Do nothing if the entry already exists. * Called when an interface is configured with a valid IP address. */ -void iface_stat_create(const struct net_device *net_dev, - struct in_ifaddr *ifa) +static void iface_stat_create(struct net_device *net_dev, + struct in_ifaddr *ifa) { struct in_device *in_dev = NULL; const char *ifname; @@ -880,6 +920,7 @@ void iface_stat_create(const struct net_device *net_dev, if (entry != NULL) { IF_DEBUG("qtaguid: iface_stat: create(%s): entry=%p\n", ifname, entry); + iface_check_stats_reset_and_adjust(net_dev, entry); if (ipv4_is_loopback(ipaddr)) { entry->active = false; IF_DEBUG("qtaguid: iface_stat: create(%s): " @@ -909,8 +950,8 @@ void iface_stat_create(const struct net_device *net_dev, in_dev_put(in_dev); } -void iface_stat_create_ipv6(const struct net_device *net_dev, - struct inet6_ifaddr *ifa) +static void iface_stat_create_ipv6(struct net_device *net_dev, + struct inet6_ifaddr *ifa) { struct in_device *in_dev; const char *ifname; @@ -948,6 +989,7 @@ void iface_stat_create_ipv6(const struct net_device *net_dev, if (entry != NULL) { IF_DEBUG("qtaguid: iface_stat: create6(%s): entry=%p\n", ifname, entry); + iface_check_stats_reset_and_adjust(net_dev, entry); if (addr_type & IPV6_ADDR_LOOPBACK) { entry->active = false; IF_DEBUG("qtaguid: iface_stat: create6(%s): " @@ -1019,7 +1061,7 @@ data_counters_update(struct data_counters *dc, int set, * does not exist (when a device was never configured with an IP address). * Called when an device is being unregistered. */ -static void iface_stat_update(struct net_device *dev) +static void iface_stat_update(struct net_device *dev, bool stash_only) { struct rtnl_link_stats64 dev_stats, *stats; struct iface_stat *entry; @@ -1033,21 +1075,38 @@ static void iface_stat_update(struct net_device *dev) spin_unlock_bh(&iface_stat_list_lock); return; } + IF_DEBUG("qtaguid: iface_stat: update(%s): entry=%p\n", dev->name, entry); - if (entry->active) { - entry->tx_bytes += stats->tx_bytes; - entry->tx_packets += stats->tx_packets; - entry->rx_bytes += stats->rx_bytes; - entry->rx_packets += stats->rx_packets; - entry->active = false; + if (!entry->active) { + IF_DEBUG("qtaguid: iface_stat: update(%s): already disabled\n", + dev->name); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + + if (stash_only) { + entry->last_known[IFS_TX].bytes = stats->tx_bytes; + entry->last_known[IFS_TX].packets = stats->tx_packets; + entry->last_known[IFS_RX].bytes = stats->rx_bytes; + entry->last_known[IFS_RX].packets = stats->rx_packets; + entry->last_known_valid = true; IF_DEBUG("qtaguid: iface_stat: update(%s): " - " disable tracking. rx/tx=%llu/%llu\n", + "dev stats stashed rx/tx=%llu/%llu\n", dev->name, stats->rx_bytes, stats->tx_bytes); - } else { - IF_DEBUG("qtaguid: iface_stat: update(%s): disabled\n", - dev->name); + spin_unlock_bh(&iface_stat_list_lock); + return; } + entry->totals[IFS_TX].bytes += stats->tx_bytes; + entry->totals[IFS_TX].packets += stats->tx_packets; + entry->totals[IFS_RX].bytes += stats->rx_bytes; + entry->totals[IFS_RX].packets += stats->rx_packets; + /* We don't need the last_known[] anymore */ + entry->last_known_valid = false; + entry->active = false; + IF_DEBUG("qtaguid: iface_stat: update(%s): " + "disable tracking. rx/tx=%llu/%llu\n", + dev->name, stats->rx_bytes, stats->tx_bytes); spin_unlock_bh(&iface_stat_list_lock); } @@ -1180,15 +1239,18 @@ static int iface_netdev_event_handler(struct notifier_block *nb, return NOTIFY_DONE; IF_DEBUG("qtaguid: iface_stat: netdev_event(): " - "ev=0x%lx netdev=%p->name=%s\n", - event, dev, dev ? dev->name : ""); + "ev=0x%lx/%s netdev=%p->name=%s\n", + event, netdev_evt_str(event), dev, dev ? dev->name : ""); switch (event) { case NETDEV_UP: iface_stat_create(dev, NULL); + atomic64_inc(&qtu_events.iface_events); break; case NETDEV_DOWN: - iface_stat_update(dev); + case NETDEV_UNREGISTER: + iface_stat_update(dev, event == NETDEV_DOWN); + atomic64_inc(&qtu_events.iface_events); break; } return NOTIFY_DONE; @@ -1204,8 +1266,8 @@ static int iface_inet6addr_event_handler(struct notifier_block *nb, return NOTIFY_DONE; IF_DEBUG("qtaguid: iface_stat: inet6addr_event(): " - "ev=0x%lx ifa=%p\n", - event, ifa); + "ev=0x%lx/%s ifa=%p\n", + event, netdev_evt_str(event), ifa); switch (event) { case NETDEV_UP: @@ -1215,9 +1277,10 @@ static int iface_inet6addr_event_handler(struct notifier_block *nb, atomic64_inc(&qtu_events.iface_events); break; case NETDEV_DOWN: + case NETDEV_UNREGISTER: BUG_ON(!ifa || !ifa->idev); dev = (struct net_device *)ifa->idev->dev; - iface_stat_update(dev); + iface_stat_update(dev, event == NETDEV_DOWN); atomic64_inc(&qtu_events.iface_events); break; } @@ -1234,8 +1297,8 @@ static int iface_inetaddr_event_handler(struct notifier_block *nb, return NOTIFY_DONE; IF_DEBUG("qtaguid: iface_stat: inetaddr_event(): " - "ev=0x%lx ifa=%p\n", - event, ifa); + "ev=0x%lx/%s ifa=%p\n", + event, netdev_evt_str(event), ifa); switch (event) { case NETDEV_UP: @@ -1245,9 +1308,10 @@ static int iface_inetaddr_event_handler(struct notifier_block *nb, atomic64_inc(&qtu_events.iface_events); break; case NETDEV_DOWN: + case NETDEV_UNREGISTER: BUG_ON(!ifa || !ifa->ifa_dev); dev = ifa->ifa_dev->dev; - iface_stat_update(dev); + iface_stat_update(dev, event == NETDEV_DOWN); atomic64_inc(&qtu_events.iface_events); break; } diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h index 752e196e2e4c0..f762704639195 100644 --- a/net/netfilter/xt_qtaguid_internal.h +++ b/net/netfilter/xt_qtaguid_internal.h @@ -196,11 +196,20 @@ struct tag_stat { struct iface_stat { struct list_head list; /* in iface_stat_list */ char *ifname; - uint64_t rx_bytes; - uint64_t rx_packets; - uint64_t tx_bytes; - uint64_t tx_packets; bool active; + struct byte_packet_counters totals[IFS_MAX_DIRECTIONS]; + /* + * We keep the last_known, because some devices reset their counters + * just before NETDEV_UP, while some will reset just before + * NETDEV_REGISTER (which is more normal). + * So now, if the device didn't do a NETDEV_UNREGISTER and we see + * its current dev stats smaller that what was previously known, we + * assume an UNREGISTER and just use the last_known. + */ + struct byte_packet_counters last_known[IFS_MAX_DIRECTIONS]; + /* last_known is usable when last_known_valid is true */ + bool last_known_valid; + struct proc_dir_entry *proc_ptr; struct rb_root tag_stat_tree; diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c index 3d054474f9a2a..7fef3a3f212a0 100644 --- a/net/netfilter/xt_qtaguid_print.c +++ b/net/netfilter/xt_qtaguid_print.c @@ -146,19 +146,29 @@ char *pp_iface_stat(struct iface_stat *is) return kasprintf(GFP_ATOMIC, "iface_stat@%p{" "list=list_head{...}, " "ifname=%s, " - "rx_bytes=%llu, " - "rx_packets=%llu, " - "tx_bytes=%llu, " - "tx_packets=%llu, " + "total={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " + "last_known_valid=%d, " + "last_known={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " "active=%d, " "proc_ptr=%p, " "tag_stat_tree=rb_root{...}}", is, is->ifname, - is->rx_bytes, - is->rx_packets, - is->tx_bytes, - is->tx_packets, + is->totals[IFS_RX].bytes, + is->totals[IFS_RX].packets, + is->totals[IFS_TX].bytes, + is->totals[IFS_TX].packets, + is->last_known_valid, + is->last_known[IFS_RX].bytes, + is->last_known[IFS_RX].packets, + is->last_known[IFS_TX].bytes, + is->last_known[IFS_TX].packets, is->active, is->proc_ptr); } @@ -395,3 +405,36 @@ void prdebug_iface_stat_list(int indent_level, str = "}"; CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); } + +/*------------------------------------------*/ +static const char * const netdev_event_strings[] = { + "netdev_unknown", + "NETDEV_UP", + "NETDEV_DOWN", + "NETDEV_REBOOT", + "NETDEV_CHANGE", + "NETDEV_REGISTER", + "NETDEV_UNREGISTER", + "NETDEV_CHANGEMTU", + "NETDEV_CHANGEADDR", + "NETDEV_GOING_DOWN", + "NETDEV_CHANGENAME", + "NETDEV_FEAT_CHANGE", + "NETDEV_BONDING_FAILOVER", + "NETDEV_PRE_UP", + "NETDEV_PRE_TYPE_CHANGE", + "NETDEV_POST_TYPE_CHANGE", + "NETDEV_POST_INIT", + "NETDEV_UNREGISTER_BATCH", + "NETDEV_RELEASE", + "NETDEV_NOTIFY_PEERS", + "NETDEV_JOIN", +}; + +const char *netdev_evt_str(int netdev_event) +{ + if (netdev_event < 0 + || netdev_event >= ARRAY_SIZE(netdev_event_strings)) + return "bad event num"; + return netdev_event_strings[netdev_event]; +} diff --git a/net/netfilter/xt_qtaguid_print.h b/net/netfilter/xt_qtaguid_print.h index e26020c91dfc2..388622860ea04 100644 --- a/net/netfilter/xt_qtaguid_print.h +++ b/net/netfilter/xt_qtaguid_print.h @@ -36,4 +36,7 @@ void prdebug_tag_stat_tree(int indent_level, struct rb_root *tag_stat_tree); void prdebug_iface_stat_list(int indent_level, struct list_head *iface_stat_list); + +/*------------------------------------------*/ +const char *netdev_evt_str(int netdev_event); #endif /* ifndef __XT_QTAGUID_PRINT_H__ */ From 195e9adbf90a1f79319086b060b1dd7567f634cd Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 19 Sep 2011 22:54:51 -0700 Subject: [PATCH 1972/2556] netfilter: xt_qtaguid: provide an iface_stat_all proc entry There is a /proc/net/xt_qtaguid/iface//{rx_bytes,rx_packets,tx_bytes,...} but for better convenience and to avoid getting overly stale net/dev stats we now have /proc/net/xt_qtaguid/iface_stat_all which outputs lines of: iface_name active rx_bytes rx_packets tx_bytes tx_packets net_dev_rx_bytes net_dev_rx_packets net_dev_tx_bytes net_dev_tx_packets Change-Id: I12cc10d2d123b86b56d4eb489b1d77b2ce72ebcf Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 195 +++++++++++++++++++++------- net/netfilter/xt_qtaguid_internal.h | 3 + net/netfilter/xt_qtaguid_print.c | 2 + 3 files changed, 151 insertions(+), 49 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 3cdf1d8a26da5..9b9b809b9960a 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -111,6 +111,8 @@ module_param(debug_mask, uint, S_IRUGO | S_IWUSR); /*---------------------------------------------------------------------------*/ static const char *iface_stat_procdirname = "iface_stat"; static struct proc_dir_entry *iface_stat_procdir; +static const char *iface_stat_all_procfilename = "iface_stat_all"; +static struct proc_dir_entry *iface_stat_all_procfile; /* * Ordering of locks: @@ -751,6 +753,72 @@ static struct iface_stat *get_iface_entry(const char *ifname) return iface_entry; } +static int iface_stat_all_proc_read(char *page, char **num_items_returned, + off_t items_to_skip, int char_count, + int *eof, void *data) +{ + char *outp = page; + int item_index = 0; + int len; + struct iface_stat *iface_entry; + struct rtnl_link_stats64 dev_stats, *stats; + struct rtnl_link_stats64 no_dev_stats = {0}; + + if (unlikely(module_passive)) { + *eof = 1; + return 0; + } + + CT_DEBUG("qtaguid:proc iface_stat_all " + "page=%p *num_items_returned=%p off=%ld " + "char_count=%d *eof=%d\n", page, *num_items_returned, + items_to_skip, char_count, *eof); + + if (*eof) + return 0; + + /* + * This lock will prevent iface_stat_update() from changing active, + * and in turn prevent an interface from unregistering itself. + */ + spin_lock_bh(&iface_stat_list_lock); + list_for_each_entry(iface_entry, &iface_stat_list, list) { + if (item_index++ < items_to_skip) + continue; + + if (iface_entry->active) { + stats = dev_get_stats(iface_entry->net_dev, + &dev_stats); + } else { + stats = &no_dev_stats; + } + len = snprintf(outp, char_count, + "%s %d " + "%llu %llu %llu %llu " + "%llu %llu %llu %llu\n", + iface_entry->ifname, + iface_entry->active, + iface_entry->totals[IFS_RX].bytes, + iface_entry->totals[IFS_RX].packets, + iface_entry->totals[IFS_TX].bytes, + iface_entry->totals[IFS_TX].packets, + stats->rx_bytes, stats->rx_packets, + stats->tx_bytes, stats->tx_packets); + if (len >= char_count) { + spin_unlock_bh(&iface_stat_list_lock); + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + spin_unlock_bh(&iface_stat_list_lock); + + *eof = 1; + return outp - page; +} + static void iface_create_proc_worker(struct work_struct *work) { struct proc_dir_entry *proc_entry; @@ -784,8 +852,34 @@ static void iface_create_proc_worker(struct work_struct *work) kfree(isw); } +/* + * Will set the entry's active state, and + * update the net_dev accordingly also. + */ +static void _iface_stat_set_active(struct iface_stat *entry, + struct net_device *net_dev, + bool activate) +{ + if (activate) { + entry->net_dev = net_dev; + entry->active = true; + IF_DEBUG("qtaguid: %s(%s): " + "enable tracking. rfcnt=%d\n", __func__, + entry->ifname, + percpu_read(*net_dev->pcpu_refcnt)); + } else { + entry->active = false; + entry->net_dev = NULL; + IF_DEBUG("qtaguid: %s(%s): " + "disable tracking. rfcnt=%d\n", __func__, + entry->ifname, + percpu_read(*net_dev->pcpu_refcnt)); + + } +} + /* Caller must hold iface_stat_list_lock */ -static struct iface_stat *iface_alloc(const char *ifname) +static struct iface_stat *iface_alloc(struct net_device *net_dev) { struct iface_stat *new_iface; struct iface_stat_work *isw; @@ -793,19 +887,19 @@ static struct iface_stat *iface_alloc(const char *ifname) new_iface = kzalloc(sizeof(*new_iface), GFP_ATOMIC); if (new_iface == NULL) { pr_err("qtaguid: iface_stat: create(%s): " - "iface_stat alloc failed\n", ifname); + "iface_stat alloc failed\n", net_dev->name); return NULL; } - new_iface->ifname = kstrdup(ifname, GFP_ATOMIC); + new_iface->ifname = kstrdup(net_dev->name, GFP_ATOMIC); if (new_iface->ifname == NULL) { pr_err("qtaguid: iface_stat: create(%s): " - "ifname alloc failed\n", ifname); + "ifname alloc failed\n", net_dev->name); kfree(new_iface); return NULL; } spin_lock_init(&new_iface->tag_stat_list_lock); - new_iface->active = true; new_iface->tag_stat_tree = RB_ROOT; + _iface_stat_set_active(new_iface, net_dev, true); /* * ipv6 notifier chains are atomic :( @@ -815,6 +909,7 @@ static struct iface_stat *iface_alloc(const char *ifname) if (!isw) { pr_err("qtaguid: iface_stat: create(%s): " "work alloc failed\n", new_iface->ifname); + _iface_stat_set_active(new_iface, net_dev, false); kfree(new_iface->ifname); kfree(new_iface); return NULL; @@ -918,20 +1013,14 @@ static void iface_stat_create(struct net_device *net_dev, spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(ifname); if (entry != NULL) { + bool activate = !ipv4_is_loopback(ipaddr); IF_DEBUG("qtaguid: iface_stat: create(%s): entry=%p\n", ifname, entry); iface_check_stats_reset_and_adjust(net_dev, entry); - if (ipv4_is_loopback(ipaddr)) { - entry->active = false; - IF_DEBUG("qtaguid: iface_stat: create(%s): " - "disable tracking of loopback dev\n", - ifname); - } else { - entry->active = true; - IF_DEBUG("qtaguid: iface_stat: create(%s): " - "enable tracking. ip=%pI4\n", - ifname, &ipaddr); - } + _iface_stat_set_active(entry, net_dev, activate); + IF_DEBUG("qtaguid: %s(%s): " + "tracking now %d on ip=%pI4\n", __func__, + entry->ifname, activate, &ipaddr); goto done_unlock_put; } else if (ipv4_is_loopback(ipaddr)) { IF_DEBUG("qtaguid: iface_stat: create(%s): " @@ -939,10 +1028,9 @@ static void iface_stat_create(struct net_device *net_dev, goto done_unlock_put; } - new_iface = iface_alloc(ifname); + new_iface = iface_alloc(net_dev); IF_DEBUG("qtaguid: iface_stat: create(%s): done " "entry=%p ip=%pI4\n", ifname, new_iface, &ipaddr); - done_unlock_put: spin_unlock_bh(&iface_stat_list_lock); done_put: @@ -987,29 +1075,23 @@ static void iface_stat_create_ipv6(struct net_device *net_dev, spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(ifname); if (entry != NULL) { - IF_DEBUG("qtaguid: iface_stat: create6(%s): entry=%p\n", + bool activate = !(addr_type & IPV6_ADDR_LOOPBACK); + IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, ifname, entry); iface_check_stats_reset_and_adjust(net_dev, entry); - if (addr_type & IPV6_ADDR_LOOPBACK) { - entry->active = false; - IF_DEBUG("qtaguid: iface_stat: create6(%s): " - "disable tracking of loopback dev\n", - ifname); - } else { - entry->active = true; - IF_DEBUG("qtaguid: iface_stat: create6(%s): " - "enable tracking. ip=%pI6c\n", - ifname, &ifa->addr); - } + _iface_stat_set_active(entry, net_dev, activate); + IF_DEBUG("qtaguid: %s(%s): " + "tracking now %d on ip=%pI6c\n", __func__, + entry->ifname, activate, &ifa->addr); goto done_unlock_put; } else if (addr_type & IPV6_ADDR_LOOPBACK) { - IF_DEBUG("qtaguid: iface_stat: create6(%s): " - "ignore loopback dev. ip=%pI6c\n", + IF_DEBUG("qtaguid: %s(%s): " + "ignore loopback dev. ip=%pI6c\n", __func__, ifname, &ifa->addr); goto done_unlock_put; } - new_iface = iface_alloc(ifname); + new_iface = iface_alloc(net_dev); IF_DEBUG("qtaguid: iface_stat: create6(%s): done " "entry=%p ip=%pI6c\n", ifname, new_iface, &ifa->addr); @@ -1061,26 +1143,26 @@ data_counters_update(struct data_counters *dc, int set, * does not exist (when a device was never configured with an IP address). * Called when an device is being unregistered. */ -static void iface_stat_update(struct net_device *dev, bool stash_only) +static void iface_stat_update(struct net_device *net_dev, bool stash_only) { struct rtnl_link_stats64 dev_stats, *stats; struct iface_stat *entry; - stats = dev_get_stats(dev, &dev_stats); + stats = dev_get_stats(net_dev, &dev_stats); spin_lock_bh(&iface_stat_list_lock); - entry = get_iface_entry(dev->name); + entry = get_iface_entry(net_dev->name); if (entry == NULL) { IF_DEBUG("qtaguid: iface_stat: update(%s): not tracked\n", - dev->name); + net_dev->name); spin_unlock_bh(&iface_stat_list_lock); return; } - IF_DEBUG("qtaguid: iface_stat: update(%s): entry=%p\n", - dev->name, entry); + IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, + net_dev->name, entry); if (!entry->active) { - IF_DEBUG("qtaguid: iface_stat: update(%s): already disabled\n", - dev->name); + IF_DEBUG("qtaguid: %s(%s): already disabled\n", __func__, + net_dev->name); spin_unlock_bh(&iface_stat_list_lock); return; } @@ -1091,9 +1173,9 @@ static void iface_stat_update(struct net_device *dev, bool stash_only) entry->last_known[IFS_RX].bytes = stats->rx_bytes; entry->last_known[IFS_RX].packets = stats->rx_packets; entry->last_known_valid = true; - IF_DEBUG("qtaguid: iface_stat: update(%s): " - "dev stats stashed rx/tx=%llu/%llu\n", - dev->name, stats->rx_bytes, stats->tx_bytes); + IF_DEBUG("qtaguid: %s(%s): " + "dev stats stashed rx/tx=%llu/%llu\n", __func__, + net_dev->name, stats->rx_bytes, stats->tx_bytes); spin_unlock_bh(&iface_stat_list_lock); return; } @@ -1103,10 +1185,10 @@ static void iface_stat_update(struct net_device *dev, bool stash_only) entry->totals[IFS_RX].packets += stats->rx_packets; /* We don't need the last_known[] anymore */ entry->last_known_valid = false; - entry->active = false; - IF_DEBUG("qtaguid: iface_stat: update(%s): " - "disable tracking. rx/tx=%llu/%llu\n", - dev->name, stats->rx_bytes, stats->tx_bytes); + _iface_stat_set_active(entry, net_dev, false); + IF_DEBUG("qtaguid: %s(%s): " + "disable tracking. rx/tx=%llu/%llu\n", __func__, + net_dev->name, stats->rx_bytes, stats->tx_bytes); spin_unlock_bh(&iface_stat_list_lock); } @@ -1340,11 +1422,24 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) err = -1; goto err; } + + iface_stat_all_procfile = create_proc_entry(iface_stat_all_procfilename, + proc_iface_perms, + parent_procdir); + if (!iface_stat_all_procfile) { + pr_err("qtaguid: iface_stat: init " + " failed to create stat_all proc entry\n"); + err = -1; + goto err_zap_entry; + } + iface_stat_all_procfile->read_proc = iface_stat_all_proc_read; + + err = register_netdevice_notifier(&iface_netdev_notifier_blk); if (err) { pr_err("qtaguid: iface_stat: init " "failed to register dev event handler\n"); - goto err_zap_entry; + goto err_zap_all_stats_entry; } err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); if (err) { @@ -1365,6 +1460,8 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) unregister_inetaddr_notifier(&iface_inetaddr_notifier_blk); err_unreg_nd: unregister_netdevice_notifier(&iface_netdev_notifier_blk); +err_zap_all_stats_entry: + remove_proc_entry(iface_stat_all_procfilename, parent_procdir); err_zap_entry: remove_proc_entry(iface_stat_procdirname, parent_procdir); err: diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h index f762704639195..d39ee89a9a2c5 100644 --- a/net/netfilter/xt_qtaguid_internal.h +++ b/net/netfilter/xt_qtaguid_internal.h @@ -197,6 +197,9 @@ struct iface_stat { struct list_head list; /* in iface_stat_list */ char *ifname; bool active; + /* net_dev is only valid for active iface_stat */ + struct net_device *net_dev; + struct byte_packet_counters totals[IFS_MAX_DIRECTIONS]; /* * We keep the last_known, because some devices reset their counters diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c index 7fef3a3f212a0..45d717ff3653b 100644 --- a/net/netfilter/xt_qtaguid_print.c +++ b/net/netfilter/xt_qtaguid_print.c @@ -156,6 +156,7 @@ char *pp_iface_stat(struct iface_stat *is) "tx={bytes=%llu, " "packets=%llu}}, " "active=%d, " + "net_dev=%p, " "proc_ptr=%p, " "tag_stat_tree=rb_root{...}}", is, @@ -170,6 +171,7 @@ char *pp_iface_stat(struct iface_stat *is) is->last_known[IFS_TX].bytes, is->last_known[IFS_TX].packets, is->active, + is->net_dev, is->proc_ptr); } From 2d65286101381bc3c264cb516e4b5dd276c6d055 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 20 Sep 2011 14:23:39 -0700 Subject: [PATCH 1973/2556] netfilter: xt_qtaguid: change WARN_ONCE into pr_warn_once Make the warning less scary. Change-Id: I0276c5413e37ec991f24db57aeb90333fb1b5a65 Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 9b9b809b9960a..c42e486c5cbe0 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -2113,14 +2113,16 @@ static int ctrl_cmd_tag(const char *input) * At first, we want to catch user-space code that is not * opening the /dev/xt_qtaguid. */ - WARN_ONCE(IS_ERR_OR_NULL(pqd_entry), - "qtaguid: User space forgot to open /dev/xt_qtaguid? " - "pid=%u tgid=%u uid=%u\n", - current->pid, current->tgid, current_fsuid()); - if (!IS_ERR_OR_NULL(pqd_entry)) { + if (IS_ERR_OR_NULL(pqd_entry)) + pr_warn_once( + "qtaguid: %s(): " + "User space forgot to open /dev/xt_qtaguid? " + "pid=%u tgid=%u uid=%u\n", __func__, + current->pid, current->tgid, + current_fsuid()); + else list_add(&sock_tag_entry->list, &pqd_entry->sock_tag_list); - } spin_unlock_bh(&uid_tag_data_tree_lock); sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); @@ -2200,11 +2202,12 @@ static int ctrl_cmd_untag(const char *input) * At first, we want to catch user-space code that is not * opening the /dev/xt_qtaguid. */ - WARN_ONCE(IS_ERR_OR_NULL(pqd_entry), - "qtaguid: User space forgot to open /dev/xt_qtaguid? " - "pid=%u tgid=%u uid=%u\n", - current->pid, current->tgid, current_fsuid()); - if (!IS_ERR_OR_NULL(pqd_entry)) + if (IS_ERR_OR_NULL(pqd_entry)) + pr_warn_once("qtaguid: %s(): " + "User space forgot to open /dev/xt_qtaguid? " + "pid=%u tgid=%u uid=%u\n", __func__, + current->pid, current->tgid, current_fsuid()); + else list_del(&sock_tag_entry->list); spin_unlock_bh(&uid_tag_data_tree_lock); /* From 2e8927c30480982c07f0f7234d0d26aa627d71b6 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Sun, 25 Sep 2011 19:24:02 -0700 Subject: [PATCH 1974/2556] netfilter: xt_qtaguid: fix crash after using delete ctrl command * Crash fix The delete command would delete a socket tag entry without removing it from the proc_qtu_data { ..., sock_tag_list, }. This in turn would cause an exiting process to crash while cleaning up its matching proc_qtu_data. * Added more aggressive tracking/cleanup of proc_qtu_data This should allow one process to cleanup qtu_tag_data{} left around from processes that didn't use resource tracking via /dev/xt_qtaguid. * Debug printing tweaks Better code inclusion/exclusion handling, and extra debug out of full state. Change-Id: I735965af2962ffcd7f3021cdc0068b3ab21245c2 Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 259 +++++++++++++++----------- net/netfilter/xt_qtaguid_internal.h | 36 ++-- net/netfilter/xt_qtaguid_print.c | 272 ++++++++++++++++++++-------- net/netfilter/xt_qtaguid_print.h | 78 ++++++++ 4 files changed, 449 insertions(+), 196 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index c42e486c5cbe0..32d855b1b6d24 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -102,11 +102,10 @@ static bool qtu_proc_handling_passive; module_param_named(tag_tracking_passive, qtu_proc_handling_passive, bool, S_IRUGO | S_IWUSR); - #define QTU_DEV_NAME "xt_qtaguid" -uint debug_mask = DEFAULT_DEBUG_MASK; -module_param(debug_mask, uint, S_IRUGO | S_IWUSR); +uint qtaguid_debug_mask = DEFAULT_DEBUG_MASK; +module_param_named(debug_mask, qtaguid_debug_mask, uint, S_IRUGO | S_IWUSR); /*---------------------------------------------------------------------------*/ static const char *iface_stat_procdirname = "iface_stat"; @@ -125,70 +124,92 @@ static struct proc_dir_entry *iface_stat_all_procfile; * Notice how sock_tag_list_lock is held sometimes when uid_tag_data_tree_lock * is acquired. * - * Call tree with all lock holders as of 2011-09-06: - * - * qtaguid_ctrl_parse() - * ctrl_cmd_delete() - * sock_tag_list_lock - * tag_counter_set_list_lock - * iface_stat_list_lock - * iface_entry->tag_stat_list_lock - * uid_tag_data_tree_lock - * ctrl_cmd_counter_set() - * tag_counter_set_list_lock - * ctrl_cmd_tag() - * sock_tag_list_lock - * get_tag_ref() - * uid_tag_data_tree_lock - * uid_tag_data_tree_lock - * ctrl_cmd_untag() - * sock_tag_list_lock - * uid_tag_data_tree_lock + * Call tree with all lock holders as of 2011-09-25: * - * qtaguid_mt() - * account_for_uid() - * if_tag_stat_update() - * get_sock_stat() - * sock_tag_list_lock - * iface_entry->tag_stat_list_lock - * tag_stat_update() - * get_active_counter_set() - * tag_counter_set_list_lock + * iface_stat_all_proc_read() + * iface_stat_list_lock + * (struct iface_stat) * - * iface_netdev_event_handler() - * iface_stat_create() - * iface_stat_list_lock - * iface_stat_update() - * iface_stat_list_lock + * qtaguid_ctrl_proc_read() + * sock_tag_list_lock + * (sock_tag_tree) + * (struct proc_qtu_data->sock_tag_list) + * prdebug_full_state() + * sock_tag_list_lock + * (sock_tag_tree) + * uid_tag_data_tree_lock + * (uid_tag_data_tree) + * (proc_qtu_data_tree) + * iface_stat_list_lock * - * iface_inet6addr_event_handler() - * iface_stat_create_ipv6() - * iface_stat_list_lock - * iface_stat_update() - * iface_stat_list_lock + * qtaguid_stats_proc_read() + * iface_stat_list_lock + * struct iface_stat->tag_stat_list_lock * - * iface_inetaddr_event_handler() - * iface_stat_create() - * iface_stat_list_lock - * iface_stat_update() - * iface_stat_list_lock + * qtudev_open() + * uid_tag_data_tree_lock * - * qtaguid_ctrl_proc_read() - * sock_tag_list_lock + * qtudev_release() + * sock_tag_data_list_lock + * uid_tag_data_tree_lock + * prdebug_full_state() * sock_tag_list_lock * uid_tag_data_tree_lock * iface_stat_list_lock * - * qtaguid_stats_proc_read() + * iface_netdev_event_handler() + * iface_stat_create() + * iface_stat_list_lock + * iface_stat_update() * iface_stat_list_lock - * iface_entry->tag_stat_list_lock * - * qtudev_open() - * uid_tag_data_tree_lock + * iface_inetaddr_event_handler() + * iface_stat_create() + * iface_stat_list_lock + * iface_stat_update() + * iface_stat_list_lock + * + * iface_inet6addr_event_handler() + * iface_stat_create_ipv6() + * iface_stat_list_lock + * iface_stat_update() + * iface_stat_list_lock * - * qtud_dev_release() + * qtaguid_mt() + * account_for_uid() + * if_tag_stat_update() + * get_sock_stat() + * sock_tag_list_lock + * struct iface_stat->tag_stat_list_lock + * tag_stat_update() + * get_active_counter_set() + * tag_counter_set_list_lock + * tag_stat_update() + * get_active_counter_set() + * tag_counter_set_list_lock + * + * + * qtaguid_ctrl_parse() + * ctrl_cmd_delete() + * sock_tag_list_lock + * tag_counter_set_list_lock + * iface_stat_list_lock + * struct iface_stat->tag_stat_list_lock + * uid_tag_data_tree_lock + * ctrl_cmd_counter_set() + * tag_counter_set_list_lock + * ctrl_cmd_tag() * sock_tag_list_lock + * (sock_tag_tree) + * get_tag_ref() + * uid_tag_data_tree_lock + * (uid_tag_data_tree) * uid_tag_data_tree_lock + * (proc_qtu_data_tree) + * ctrl_cmd_untag() + * sock_tag_list_lock + * uid_tag_data_tree_lock + * */ static LIST_HEAD(iface_stat_list); static DEFINE_SPINLOCK(iface_stat_list_lock); @@ -557,8 +578,8 @@ static struct tag_ref *new_tag_ref(tag_t new_tag, utd_entry->num_active_tags++; tag_ref_tree_insert(tr_entry, &utd_entry->tag_ref_tree); DR_DEBUG("qtaguid: new_tag_ref(0x%llx): " - " inserted new tag ref\n", - new_tag); + " inserted new tag ref %p\n", + new_tag, tr_entry); return tr_entry; err_res: @@ -618,7 +639,8 @@ static struct tag_ref *get_tag_ref(tag_t full_tag, static void put_utd_entry(struct uid_tag_data *utd_entry) { /* Are we done with the UID tag data entry? */ - if (RB_EMPTY_ROOT(&utd_entry->tag_ref_tree)) { + if (RB_EMPTY_ROOT(&utd_entry->tag_ref_tree) && + !utd_entry->num_pqd) { DR_DEBUG("qtaguid: %s(): " "erase utd_entry=%p uid=%u " "by pid=%u tgid=%u uid=%u\n", __func__, @@ -629,9 +651,11 @@ static void put_utd_entry(struct uid_tag_data *utd_entry) kfree(utd_entry); } else { DR_DEBUG("qtaguid: %s(): " - "utd_entry=%p still has %d tags\n", __func__, - utd_entry, utd_entry->num_active_tags); - BUG_ON(!utd_entry->num_active_tags); + "utd_entry=%p still has %d tags %d proc_qtu_data\n", + __func__, utd_entry, utd_entry->num_active_tags, + utd_entry->num_pqd); + BUG_ON(!(utd_entry->num_active_tags || + utd_entry->num_pqd)); } } @@ -1309,8 +1333,8 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, new_tag_stat = create_if_tag_stat(iface_entry, tag); new_tag_stat->parent_counters = uid_tag_counters; } - spin_unlock_bh(&iface_entry->tag_stat_list_lock); tag_stat_update(new_tag_stat, direction, proto, bytes); + spin_unlock_bh(&iface_entry->tag_stat_list_lock); } static int iface_netdev_event_handler(struct notifier_block *nb, @@ -1672,6 +1696,50 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) return res; } +#ifdef DDEBUG +/* This function is not in xt_qtaguid_print.c because of locks visibility */ +static void prdebug_full_state(int indent_level, const char *fmt, ...) +{ + va_list args; + char *fmt_buff; + char *buff; + + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + fmt_buff = kasprintf(GFP_ATOMIC, + "qtaguid: %s(): %s {\n", __func__, fmt); + BUG_ON(!fmt_buff); + va_start(args, fmt); + buff = kvasprintf(GFP_ATOMIC, + fmt_buff, args); + BUG_ON(!buff); + pr_debug("%s", buff); + kfree(fmt_buff); + kfree(buff); + va_end(args); + + spin_lock_bh(&sock_tag_list_lock); + prdebug_sock_tag_tree(indent_level, &sock_tag_tree); + spin_unlock_bh(&sock_tag_list_lock); + + spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); + prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree); + prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree); + spin_unlock_bh(&uid_tag_data_tree_lock); + spin_unlock_bh(&sock_tag_list_lock); + + spin_lock_bh(&iface_stat_list_lock); + prdebug_iface_stat_list(indent_level, &iface_stat_list); + spin_unlock_bh(&iface_stat_list_lock); + + pr_debug("qtaguid: %s(): }\n", __func__); +} +#else +static void prdebug_full_state(int indent_level, const char *fmt, ...) {} +#endif + /* * Procfs reader to get all active socket tags using style "1)" as described in * fs/proc/generic.c @@ -1761,28 +1829,10 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, (*num_items_returned)++; } -#ifdef CDEBUG /* Count the following as part of the last item_index */ if (item_index > items_to_skip) { - CT_DEBUG("qtaguid: proc ctrl state debug {\n"); - spin_lock_bh(&sock_tag_list_lock); - prdebug_sock_tag_tree(indent_level, &sock_tag_tree); - spin_unlock_bh(&sock_tag_list_lock); - - spin_lock_bh(&uid_tag_data_tree_lock); - prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree); - prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree); - spin_unlock_bh(&uid_tag_data_tree_lock); - - spin_lock_bh(&iface_stat_list_lock); - prdebug_iface_stat_list(indent_level, &iface_stat_list); - spin_unlock_bh(&iface_stat_list_lock); - - CT_DEBUG("qtaguid: proc ctrl state debug }\n"); - - + prdebug_full_state(indent_level, "proc ctrl"); } -#endif *eof = 1; return outp - page; @@ -1833,9 +1883,9 @@ static int ctrl_cmd_delete(const char *input) } tag = combine_atag_with_uid(acct_tag, uid); - CT_DEBUG("qtaguid: ctrl_delete(): " + CT_DEBUG("qtaguid: ctrl_delete(%s): " "looking for tag=0x%llx (uid=%u)\n", - tag, uid); + input, tag, uid); /* Delete socket tags */ spin_lock_bh(&sock_tag_list_lock); @@ -1847,8 +1897,8 @@ static int ctrl_cmd_delete(const char *input) if (entry_uid != uid) continue; - CT_DEBUG("qtaguid: ctrl_delete(): st tag=0x%llx (uid=%u)\n", - st_entry->tag, entry_uid); + CT_DEBUG("qtaguid: ctrl_delete(%s): st tag=0x%llx (uid=%u)\n", + input, st_entry->tag, entry_uid); if (!acct_tag || st_entry->tag == tag) { rb_erase(&st_entry->sock_node, &sock_tag_tree); @@ -1857,6 +1907,7 @@ static int ctrl_cmd_delete(const char *input) tr_entry = lookup_tag_ref(st_entry->tag, NULL); BUG_ON(tr_entry->num_sock_tags <= 0); tr_entry->num_sock_tags--; + list_del(&st_entry->list); } } spin_unlock_bh(&sock_tag_list_lock); @@ -1868,8 +1919,9 @@ static int ctrl_cmd_delete(const char *input) /* Counter sets are only on the uid tag, not full tag */ tcs_entry = tag_counter_set_tree_search(&tag_counter_set_tree, tag); if (tcs_entry) { - CT_DEBUG("qtaguid: ctrl_delete(): " + CT_DEBUG("qtaguid: ctrl_delete(%s): " "erase tcs: tag=0x%llx (uid=%u) set=%d\n", + input, tcs_entry->tn.tag, get_uid_from_tag(tcs_entry->tn.tag), tcs_entry->active_set); @@ -1891,16 +1943,16 @@ static int ctrl_cmd_delete(const char *input) entry_uid = get_uid_from_tag(ts_entry->tn.tag); node = rb_next(node); - CT_DEBUG("qtaguid: ctrl_delete(): " + CT_DEBUG("qtaguid: ctrl_delete(%s): " "ts tag=0x%llx (uid=%u)\n", - ts_entry->tn.tag, entry_uid); + input, ts_entry->tn.tag, entry_uid); if (entry_uid != uid) continue; if (!acct_tag || ts_entry->tn.tag == tag) { - CT_DEBUG("qtaguid: ctrl_delete(): " + CT_DEBUG("qtaguid: ctrl_delete(%s): " "erase ts: %s 0x%llx %u\n", - iface_entry->ifname, + input, iface_entry->ifname, get_atag_from_tag(ts_entry->tn.tag), entry_uid); rb_erase(&ts_entry->tn.node, @@ -1920,9 +1972,9 @@ static int ctrl_cmd_delete(const char *input) entry_uid = utd_entry->uid; node = rb_next(node); - CT_DEBUG("qtaguid: ctrl_delete(): " + CT_DEBUG("qtaguid: ctrl_delete(%s): " "utd uid=%u\n", - entry_uid); + input, entry_uid); if (entry_uid != uid) continue; @@ -2377,7 +2429,7 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) return len; } -bool pp_sets(struct proc_print_info *ppi) +static bool pp_sets(struct proc_print_info *ppi) { int len; int counter_set; @@ -2472,7 +2524,7 @@ static int qtudev_open(struct inode *inode, struct file *file) { struct uid_tag_data *utd_entry; struct proc_qtu_data *pqd_entry; - struct proc_qtu_data *new_pqd_entry = 0; + struct proc_qtu_data *new_pqd_entry; int res; bool utd_entry_found; @@ -2514,12 +2566,14 @@ static int qtudev_open(struct inode *inode, struct file *file) new_pqd_entry->pid = current->tgid; INIT_LIST_HEAD(&new_pqd_entry->sock_tag_list); new_pqd_entry->parent_tag_data = utd_entry; + utd_entry->num_pqd++; proc_qtu_data_tree_insert(new_pqd_entry, &proc_qtu_data_tree); spin_unlock_bh(&uid_tag_data_tree_lock); - DR_DEBUG("qtaguid: tracking data for uid=%u\n", current_fsuid()); + DR_DEBUG("qtaguid: tracking data for uid=%u in pqd=%p\n", + current_fsuid(), new_pqd_entry); file->private_data = new_pqd_entry; return 0; @@ -2559,12 +2613,6 @@ static int qtudev_release(struct inode *inode, struct file *file) spin_lock_bh(&sock_tag_list_lock); spin_lock_bh(&uid_tag_data_tree_lock); - /* - * If this proc didn't actually tag anything for itself, or has already - * willingly cleaned up itself ... - */ - put_utd_entry(utd_entry); - list_for_each_safe(entry, next, &pqd_entry->sock_tag_list) { st_entry = list_entry(entry, struct sock_tag, list); DR_DEBUG("qtaguid: %s(): " @@ -2593,10 +2641,18 @@ static int qtudev_release(struct inode *inode, struct file *file) /* Can't sockfd_put() within spinlock, do it later. */ sock_tag_tree_insert(st_entry, &st_to_free_tree); - /* Do not put_utd_entry(utd_entry) someone elses utd_entry */ + /* + * Try to free the utd_entry if no other proc_qtu_data is + * using it (num_pqd is 0) and it doesn't have active tags + * (num_active_tags is 0). + */ + put_utd_entry(utd_entry); } rb_erase(&pqd_entry->node, &proc_qtu_data_tree); + BUG_ON(pqd_entry->parent_tag_data->num_pqd < 1); + pqd_entry->parent_tag_data->num_pqd--; + put_utd_entry(pqd_entry->parent_tag_data); kfree(pqd_entry); file->private_data = NULL; @@ -2606,7 +2662,8 @@ static int qtudev_release(struct inode *inode, struct file *file) sock_tag_tree_erase(&st_to_free_tree); - + prdebug_full_state(0, "%s(): pid=%u tgid=%u", __func__, + current->pid, current->tgid); return 0; } diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h index d39ee89a9a2c5..fdce0d006d301 100644 --- a/net/netfilter/xt_qtaguid_internal.h +++ b/net/netfilter/xt_qtaguid_internal.h @@ -15,31 +15,33 @@ #include #include -/* Define/comment out these *DEBUG to compile in/out the pr_debug calls. */ /* Iface handling */ -#define IDEBUG +#define IDEBUG_MASK (1<<0) /* Iptable Matching. Per packet. */ -#define MDEBUG +#define MDEBUG_MASK (1<<1) /* Red-black tree handling. Per packet. */ -#define RDEBUG +#define RDEBUG_MASK (1<<2) /* procfs ctrl/stats handling */ -#define CDEBUG +#define CDEBUG_MASK (1<<3) /* dev and resource tracking */ -#define DDEBUG +#define DDEBUG_MASK (1<<4) /* E.g (IDEBUG_MASK | CDEBUG_MASK | DDEBUG_MASK) */ #define DEFAULT_DEBUG_MASK 0 +/* + * (Un)Define these *DEBUG to compile out/in the pr_debug calls. + * All undef: text size ~ 0x3030; all def: ~ 0x4404. + */ +#define IDEBUG +#define MDEBUG +#define RDEBUG +#define CDEBUG +#define DDEBUG -#define IDEBUG_MASK (1<<0) -#define MDEBUG_MASK (1<<1) -#define RDEBUG_MASK (1<<2) -#define CDEBUG_MASK (1<<3) -#define DDEBUG_MASK (1<<4) - -#define MSK_DEBUG(mask, ...) do { \ - if (unlikely(debug_mask & (mask))) \ - pr_debug(__VA_ARGS__); \ +#define MSK_DEBUG(mask, ...) do { \ + if (unlikely(qtaguid_debug_mask & (mask))) \ + pr_debug(__VA_ARGS__); \ } while (0) #ifdef IDEBUG #define IF_DEBUG(...) MSK_DEBUG(IDEBUG_MASK, __VA_ARGS__) @@ -67,7 +69,7 @@ #define DR_DEBUG(...) no_printk(__VA_ARGS__) #endif -extern uint debug_mask; +extern uint qtaguid_debug_mask; /*---------------------------------------------------------------------------*/ /* @@ -286,6 +288,8 @@ struct uid_tag_data { * For the uid, how many accounting tags have been set. */ int num_active_tags; + /* Track the number of proc_qtu_data that reference it */ + int num_pqd; struct rb_root tag_ref_tree; /* No tag_node_tree_lock; use uid_tag_data_tree_lock */ }; diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c index 45d717ff3653b..39176785c91f8 100644 --- a/net/netfilter/xt_qtaguid_print.c +++ b/net/netfilter/xt_qtaguid_print.c @@ -9,9 +9,13 @@ */ /* - * There are run-time debug flags enabled via the debug_mask module param, or - * via the DEFAULT_DEBUG_MASK. See xt_qtaguid_internal.h. + * Most of the functions in this file just waste time if DEBUG is not defined. + * The matching xt_qtaguid_print.h will static inline empty funcs if the needed + * debug flags ore not defined. + * Those funcs that fail to allocate memory will panic as there is no need to + * hobble allong just pretending to do the requested work. */ + #define DEBUG #include @@ -25,21 +29,38 @@ #include "xt_qtaguid_internal.h" #include "xt_qtaguid_print.h" +#ifdef DDEBUG + +static void _bug_on_err_or_null(void *ptr) +{ + if (IS_ERR_OR_NULL(ptr)) { + pr_err("qtaguid: kmalloc failed\n"); + BUG(); + } +} + char *pp_tag_t(tag_t *tag) { + char *res; + if (!tag) - return kasprintf(GFP_ATOMIC, "tag_t@null{}"); - return kasprintf(GFP_ATOMIC, - "tag_t@%p{tag=0x%llx, uid=%u}", - tag, *tag, get_uid_from_tag(*tag)); + res = kasprintf(GFP_ATOMIC, "tag_t@null{}"); + else + res = kasprintf(GFP_ATOMIC, + "tag_t@%p{tag=0x%llx, uid=%u}", + tag, *tag, get_uid_from_tag(*tag)); + _bug_on_err_or_null(res); + return res; } char *pp_data_counters(struct data_counters *dc, bool showValues) { + char *res; + if (!dc) - return kasprintf(GFP_ATOMIC, "data_counters@null{}"); - if (showValues) - return kasprintf( + res = kasprintf(GFP_ATOMIC, "data_counters@null{}"); + else if (showValues) + res = kasprintf( GFP_ATOMIC, "data_counters@%p{" "set0{" "rx{" @@ -85,7 +106,9 @@ char *pp_data_counters(struct data_counters *dc, bool showValues) dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].bytes, dc->bpc[1][IFS_TX][IFS_PROTO_OTHER].packets); else - return kasprintf(GFP_ATOMIC, "data_counters@%p{...}", dc); + res = kasprintf(GFP_ATOMIC, "data_counters@%p{...}", dc); + _bug_on_err_or_null(res); + return res; } char *pp_tag_node(struct tag_node *tn) @@ -93,12 +116,16 @@ char *pp_tag_node(struct tag_node *tn) char *tag_str; char *res; - if (!tn) - return kasprintf(GFP_ATOMIC, "tag_node@null{}"); + if (!tn) { + res = kasprintf(GFP_ATOMIC, "tag_node@null{}"); + _bug_on_err_or_null(res); + return res; + } tag_str = pp_tag_t(&tn->tag); res = kasprintf(GFP_ATOMIC, "tag_node@%p{tag=%s}", tn, tag_str); + _bug_on_err_or_null(res); kfree(tag_str); return res; } @@ -108,12 +135,16 @@ char *pp_tag_ref(struct tag_ref *tr) char *tn_str; char *res; - if (!tr) - return kasprintf(GFP_ATOMIC, "tag_ref@null{}"); + if (!tr) { + res = kasprintf(GFP_ATOMIC, "tag_ref@null{}"); + _bug_on_err_or_null(res); + return res; + } tn_str = pp_tag_node(&tr->tn); res = kasprintf(GFP_ATOMIC, "tag_ref@%p{%s, num_sock_tags=%d}", tr, tn_str, tr->num_sock_tags); + _bug_on_err_or_null(res); kfree(tn_str); return res; } @@ -125,14 +156,18 @@ char *pp_tag_stat(struct tag_stat *ts) char *parent_counters_str; char *res; - if (!ts) - return kasprintf(GFP_ATOMIC, "tag_stat@null{}"); + if (!ts) { + res = kasprintf(GFP_ATOMIC, "tag_stat@null{}"); + _bug_on_err_or_null(res); + return res; + } tn_str = pp_tag_node(&ts->tn); counters_str = pp_data_counters(&ts->counters, true); parent_counters_str = pp_data_counters(ts->parent_counters, false); res = kasprintf(GFP_ATOMIC, "tag_stat@%p{%s, counters=%s, parent_counters=%s}", ts, tn_str, counters_str, parent_counters_str); + _bug_on_err_or_null(res); kfree(tn_str); kfree(counters_str); kfree(parent_counters_str); @@ -141,38 +176,42 @@ char *pp_tag_stat(struct tag_stat *ts) char *pp_iface_stat(struct iface_stat *is) { + char *res; if (!is) - return kasprintf(GFP_ATOMIC, "iface_stat@null{}"); - return kasprintf(GFP_ATOMIC, "iface_stat@%p{" - "list=list_head{...}, " - "ifname=%s, " - "total={rx={bytes=%llu, " - "packets=%llu}, " - "tx={bytes=%llu, " - "packets=%llu}}, " - "last_known_valid=%d, " - "last_known={rx={bytes=%llu, " - "packets=%llu}, " - "tx={bytes=%llu, " - "packets=%llu}}, " - "active=%d, " - "net_dev=%p, " - "proc_ptr=%p, " - "tag_stat_tree=rb_root{...}}", - is, - is->ifname, - is->totals[IFS_RX].bytes, - is->totals[IFS_RX].packets, - is->totals[IFS_TX].bytes, - is->totals[IFS_TX].packets, - is->last_known_valid, - is->last_known[IFS_RX].bytes, - is->last_known[IFS_RX].packets, - is->last_known[IFS_TX].bytes, - is->last_known[IFS_TX].packets, - is->active, - is->net_dev, - is->proc_ptr); + res = kasprintf(GFP_ATOMIC, "iface_stat@null{}"); + else + res = kasprintf(GFP_ATOMIC, "iface_stat@%p{" + "list=list_head{...}, " + "ifname=%s, " + "total={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " + "last_known_valid=%d, " + "last_known={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " + "active=%d, " + "net_dev=%p, " + "proc_ptr=%p, " + "tag_stat_tree=rb_root{...}}", + is, + is->ifname, + is->totals[IFS_RX].bytes, + is->totals[IFS_RX].packets, + is->totals[IFS_TX].bytes, + is->totals[IFS_TX].packets, + is->last_known_valid, + is->last_known[IFS_RX].bytes, + is->last_known[IFS_RX].packets, + is->last_known[IFS_TX].bytes, + is->last_known[IFS_TX].packets, + is->active, + is->net_dev, + is->proc_ptr); + _bug_on_err_or_null(res); + return res; } char *pp_sock_tag(struct sock_tag *st) @@ -180,8 +219,11 @@ char *pp_sock_tag(struct sock_tag *st) char *tag_str; char *res; - if (!st) - return kasprintf(GFP_ATOMIC, "sock_tag@null{}"); + if (!st) { + res = kasprintf(GFP_ATOMIC, "sock_tag@null{}"); + _bug_on_err_or_null(res); + return res; + } tag_str = pp_tag_t(&st->tag); res = kasprintf(GFP_ATOMIC, "sock_tag@%p{" "sock_node=rb_node{...}, " @@ -190,6 +232,7 @@ char *pp_sock_tag(struct sock_tag *st) st, st->sk, st->socket, atomic_long_read( &st->socket->file->f_count), st->pid, tag_str); + _bug_on_err_or_null(res); kfree(tag_str); return res; } @@ -199,13 +242,16 @@ char *pp_uid_tag_data(struct uid_tag_data *utd) char *res; if (!utd) - return kasprintf(GFP_ATOMIC, "uid_tag_data@null{}"); - res = kasprintf(GFP_ATOMIC, "uid_tag_data@%p{" - "uid=%u, num_active_acct_tags=%d, " - "tag_node_tree=rb_root{...}, " - "proc_qtu_data_tree=rb_root{...}}", - utd, utd->uid, - utd->num_active_tags); + res = kasprintf(GFP_ATOMIC, "uid_tag_data@null{}"); + else + res = kasprintf(GFP_ATOMIC, "uid_tag_data@%p{" + "uid=%u, num_active_acct_tags=%d, " + "num_pqd=%d, " + "tag_node_tree=rb_root{...}, " + "proc_qtu_data_tree=rb_root{...}}", + utd, utd->uid, + utd->num_active_tags, utd->num_pqd); + _bug_on_err_or_null(res); return res; } @@ -214,8 +260,11 @@ char *pp_proc_qtu_data(struct proc_qtu_data *pqd) char *parent_tag_data_str; char *res; - if (!pqd) - return kasprintf(GFP_ATOMIC, "proc_qtu_data@null{}"); + if (!pqd) { + res = kasprintf(GFP_ATOMIC, "proc_qtu_data@null{}"); + _bug_on_err_or_null(res); + return res; + } parent_tag_data_str = pp_uid_tag_data(pqd->parent_tag_data); res = kasprintf(GFP_ATOMIC, "proc_qtu_data@%p{" "node=rb_node{...}, pid=%u, " @@ -223,6 +272,7 @@ char *pp_proc_qtu_data(struct proc_qtu_data *pqd) "sock_tag_list=list_head{...}}", pqd, pqd->pid, parent_tag_data_str ); + _bug_on_err_or_null(res); kfree(parent_tag_data_str); return res; } @@ -235,20 +285,29 @@ void prdebug_sock_tag_tree(int indent_level, struct sock_tag *sock_tag_entry; char *str; + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(sock_tag_tree)) { + str = "sock_tag_tree=rb_root{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + str = "sock_tag_tree=rb_root{"; - CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); indent_level++; for (node = rb_first(sock_tag_tree); node; node = rb_next(node)) { sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); str = pp_sock_tag(sock_tag_entry); - CT_DEBUG("%*d: %s,\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); kfree(str); } indent_level--; str = "}"; - CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); } void prdebug_sock_tag_list(int indent_level, @@ -257,17 +316,26 @@ void prdebug_sock_tag_list(int indent_level, struct sock_tag *sock_tag_entry; char *str; + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (list_empty(sock_tag_list)) { + str = "sock_tag_list=list_head{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + str = "sock_tag_list=list_head{"; - CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); indent_level++; list_for_each_entry(sock_tag_entry, sock_tag_list, list) { str = pp_sock_tag(sock_tag_entry); - CT_DEBUG("%*d: %s,\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); kfree(str); } indent_level--; str = "}"; - CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); } void prdebug_proc_qtu_data_tree(int indent_level, @@ -277,8 +345,17 @@ void prdebug_proc_qtu_data_tree(int indent_level, struct rb_node *node; struct proc_qtu_data *proc_qtu_data_entry; + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(proc_qtu_data_tree)) { + str = "proc_qtu_data_tree=rb_root{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + str = "proc_qtu_data_tree=rb_root{"; - CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); indent_level++; for (node = rb_first(proc_qtu_data_tree); node; @@ -287,7 +364,7 @@ void prdebug_proc_qtu_data_tree(int indent_level, struct proc_qtu_data, node); str = pp_proc_qtu_data(proc_qtu_data_entry); - CT_DEBUG("%*d: %s,\n", indent_level*2, indent_level, + pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); kfree(str); indent_level++; @@ -298,7 +375,7 @@ void prdebug_proc_qtu_data_tree(int indent_level, } indent_level--; str = "}"; - CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); } void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree) @@ -307,8 +384,17 @@ void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree) struct rb_node *node; struct tag_ref *tag_ref_entry; + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(tag_ref_tree)) { + str = "tag_ref_tree{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + str = "tag_ref_tree{"; - CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); indent_level++; for (node = rb_first(tag_ref_tree); node; @@ -317,13 +403,13 @@ void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree) struct tag_ref, tn.node); str = pp_tag_ref(tag_ref_entry); - CT_DEBUG("%*d: %s,\n", indent_level*2, indent_level, + pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); kfree(str); } indent_level--; str = "}"; - CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); } void prdebug_uid_tag_data_tree(int indent_level, @@ -333,8 +419,17 @@ void prdebug_uid_tag_data_tree(int indent_level, struct rb_node *node; struct uid_tag_data *uid_tag_data_entry; + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(uid_tag_data_tree)) { + str = "uid_tag_data_tree=rb_root{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + str = "uid_tag_data_tree=rb_root{"; - CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); indent_level++; for (node = rb_first(uid_tag_data_tree); node; @@ -342,7 +437,7 @@ void prdebug_uid_tag_data_tree(int indent_level, uid_tag_data_entry = rb_entry(node, struct uid_tag_data, node); str = pp_uid_tag_data(uid_tag_data_entry); - CT_DEBUG("%*d: %s,\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s,\n", indent_level*2, indent_level, str); kfree(str); if (!RB_EMPTY_ROOT(&uid_tag_data_entry->tag_ref_tree)) { indent_level++; @@ -353,7 +448,7 @@ void prdebug_uid_tag_data_tree(int indent_level, } indent_level--; str = "}"; - CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); } void prdebug_tag_stat_tree(int indent_level, @@ -363,21 +458,30 @@ void prdebug_tag_stat_tree(int indent_level, struct rb_node *node; struct tag_stat *ts_entry; + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (RB_EMPTY_ROOT(tag_stat_tree)) { + str = "tag_stat_tree{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + str = "tag_stat_tree{"; - CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); indent_level++; for (node = rb_first(tag_stat_tree); node; node = rb_next(node)) { ts_entry = rb_entry(node, struct tag_stat, tn.node); str = pp_tag_stat(ts_entry); - CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); kfree(str); } indent_level--; str = "}"; - CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); } void prdebug_iface_stat_list(int indent_level, @@ -386,12 +490,21 @@ void prdebug_iface_stat_list(int indent_level, char *str; struct iface_stat *iface_entry; + if (!unlikely(qtaguid_debug_mask & DDEBUG_MASK)) + return; + + if (list_empty(iface_stat_list)) { + str = "iface_stat_list=list_head{}"; + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); + return; + } + str = "iface_stat_list=list_head{"; - CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); indent_level++; list_for_each_entry(iface_entry, iface_stat_list, list) { str = pp_iface_stat(iface_entry); - CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); kfree(str); spin_lock_bh(&iface_entry->tag_stat_list_lock); @@ -405,9 +518,10 @@ void prdebug_iface_stat_list(int indent_level, } indent_level--; str = "}"; - CT_DEBUG("%*d: %s\n", indent_level*2, indent_level, str); + pr_debug("%*d: %s\n", indent_level*2, indent_level, str); } +#endif /* ifdef DDEBUG */ /*------------------------------------------*/ static const char * const netdev_event_strings[] = { "netdev_unknown", diff --git a/net/netfilter/xt_qtaguid_print.h b/net/netfilter/xt_qtaguid_print.h index 388622860ea04..b63871a0be5a7 100644 --- a/net/netfilter/xt_qtaguid_print.h +++ b/net/netfilter/xt_qtaguid_print.h @@ -12,6 +12,8 @@ #include "xt_qtaguid_internal.h" +#ifdef DDEBUG + char *pp_tag_t(tag_t *tag); char *pp_data_counters(struct data_counters *dc, bool showValues); char *pp_tag_node(struct tag_node *tn); @@ -37,6 +39,82 @@ void prdebug_tag_stat_tree(int indent_level, void prdebug_iface_stat_list(int indent_level, struct list_head *iface_stat_list); +#else + +/*------------------------------------------*/ +static inline char *pp_tag_t(tag_t *tag) +{ + return NULL; +} +static inline char *pp_data_counters(struct data_counters *dc, bool showValues) +{ + return NULL; +} +static inline char *pp_tag_node(struct tag_node *tn) +{ + return NULL; +} +static inline char *pp_tag_ref(struct tag_ref *tr) +{ + return NULL; +} +static inline char *pp_tag_stat(struct tag_stat *ts) +{ + return NULL; +} +static inline char *pp_iface_stat(struct iface_stat *is) +{ + return NULL; +} +static inline char *pp_sock_tag(struct sock_tag *st) +{ + return NULL; +} +static inline char *pp_uid_tag_data(struct uid_tag_data *qtd) +{ + return NULL; +} +static inline char *pp_proc_qtu_data(struct proc_qtu_data *pqd) +{ + return NULL; +} + +/*------------------------------------------*/ +static inline +void prdebug_sock_tag_list(int indent_level, + struct list_head *sock_tag_list) +{ +} +static inline +void prdebug_sock_tag_tree(int indent_level, + struct rb_root *sock_tag_tree) +{ +} +static inline +void prdebug_proc_qtu_data_tree(int indent_level, + struct rb_root *proc_qtu_data_tree) +{ +} +static inline +void prdebug_tag_ref_tree(int indent_level, struct rb_root *tag_ref_tree) +{ +} +static inline +void prdebug_uid_tag_data_tree(int indent_level, + struct rb_root *uid_tag_data_tree) +{ +} +static inline +void prdebug_tag_stat_tree(int indent_level, + struct rb_root *tag_stat_tree) +{ +} +static inline +void prdebug_iface_stat_list(int indent_level, + struct list_head *iface_stat_list) +{ +} +#endif /*------------------------------------------*/ const char *netdev_evt_str(int netdev_event); #endif /* ifndef __XT_QTAGUID_PRINT_H__ */ From 0ceaf1c36bf0ea3c3314c5243ce65ddd9c838f37 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Thu, 29 Sep 2011 16:29:53 -0700 Subject: [PATCH 1975/2556] netfilter: xt_qtaguid: add missing tracking for no filp case In cases where the skb would have an sk_socket but no file, that skb would not be counted at all. Assigning to uid 0 now. Adding extra counters to track skb counts. Change-Id: If049b4b525e1fbd5afc9c72b4a174c0a435f2ca7 Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 18 +++++++++++++++--- net/netfilter/xt_qtaguid_internal.h | 13 +++++++++++-- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 32d855b1b6d24..b6b95c395e875 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1588,6 +1588,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) MT_DEBUG("qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n", par->hooknum, skb, par->in, par->out, par->family); + atomic64_inc(&qtu_events.match_calls); if (skb == NULL) { res = (info->match ^ info->invert) == 0; goto ret_res; @@ -1608,6 +1609,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) got_sock = sk; if (sk) atomic64_inc(&qtu_events.match_found_sk_in_ct); + else + atomic64_inc(&qtu_events.match_found_no_sk_in_ct); } else { atomic64_inc(&qtu_events.match_found_sk); } @@ -1639,7 +1642,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) par->hooknum, sk ? sk->sk_socket : NULL); res = (info->match ^ info->invert) == 0; - atomic64_inc(&qtu_events.match_found_sk_none); + atomic64_inc(&qtu_events.match_no_sk); goto put_sock_ret_res; } else if (info->match & info->invert & XT_QTAGUID_SOCKET) { res = false; @@ -1648,8 +1651,10 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) filp = sk->sk_socket->file; if (filp == NULL) { MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum); + account_for_uid(skb, sk, 0, par); res = ((info->match ^ info->invert) & (XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0; + atomic64_inc(&qtu_events.match_no_sk_file); goto put_sock_ret_res; } sock_uid = filp->f_cred->fsuid; @@ -1809,17 +1814,24 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, "counter_set_changes=%llu " "delete_cmds=%llu " "iface_events=%llu " + "match_calls=%llu " "match_found_sk=%llu " "match_found_sk_in_ct=%llu " - "match_found_sk_none=%llu\n", + "match_found_no_sk_in_ct=%llu " + "match_no_sk=%llu " + "match_no_sk_file=%llu\n", atomic64_read(&qtu_events.sockets_tagged), atomic64_read(&qtu_events.sockets_untagged), atomic64_read(&qtu_events.counter_set_changes), atomic64_read(&qtu_events.delete_cmds), atomic64_read(&qtu_events.iface_events), + atomic64_read(&qtu_events.match_calls), atomic64_read(&qtu_events.match_found_sk), atomic64_read(&qtu_events.match_found_sk_in_ct), - atomic64_read(&qtu_events.match_found_sk_none)); + atomic64_read( + &qtu_events.match_found_no_sk_in_ct), + atomic64_read(&qtu_events.match_no_sk), + atomic64_read(&qtu_events.match_no_sk_file)); if (len >= char_count) { *outp = '\0'; return outp - page; diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h index fdce0d006d301..02479d6d317d5 100644 --- a/net/netfilter/xt_qtaguid_internal.h +++ b/net/netfilter/xt_qtaguid_internal.h @@ -252,18 +252,27 @@ struct qtaguid_event_counts { atomic64_t counter_set_changes; atomic64_t delete_cmds; atomic64_t iface_events; /* Number of NETDEV_* events handled */ + + atomic64_t match_calls; /* Number of times iptables called mt */ /* * match_found_sk_*: numbers related to the netfilter matching * function finding a sock for the sk_buff. + * Total skbs processed is sum(match_found*). */ atomic64_t match_found_sk; /* An sk was already in the sk_buff. */ - /* The connection tracker had the sk. */ + /* The connection tracker had or didn't have the sk. */ atomic64_t match_found_sk_in_ct; + atomic64_t match_found_no_sk_in_ct; /* * No sk could be found. No apparent owner. Could happen with * unsolicited traffic. */ - atomic64_t match_found_sk_none; + atomic64_t match_no_sk; + /* + * The file ptr in the sk_socket wasn't there. + * This might happen for traffic while the socket is being closed. + */ + atomic64_t match_no_sk_file; }; /* Track the set active_set for the given tag. */ From 51472045aa3387a7e74015beca2d42596f069d96 Mon Sep 17 00:00:00 2001 From: Ashish Sharma Date: Tue, 4 Oct 2011 13:11:47 -0700 Subject: [PATCH 1976/2556] netfilter: xt_qtaguid: Fix the stats info display order Change-Id: I3bf165c31f35a6c7dc212f23df5eefaeb8129d0d Signed-off-by: Ashish Sharma --- net/netfilter/xt_qtaguid.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index b6b95c395e875..5d73ecaf540a5 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -2383,12 +2383,12 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) "idx iface acct_tag_hex uid_tag_int cnt_set " "rx_bytes rx_packets " "tx_bytes tx_packets " - "rx_tcp_packets rx_tcp_bytes " - "rx_udp_packets rx_udp_bytes " - "rx_other_packets rx_other_bytes " - "tx_tcp_packets tx_tcp_bytes " - "tx_udp_packets tx_udp_bytes " - "tx_other_packets tx_other_bytes\n"); + "rx_tcp_bytes rx_tcp_packets " + "rx_udp_bytes rx_udp_packets " + "rx_other_bytes rx_other_packets " + "tx_tcp_bytes tx_tcp_packets " + "tx_udp_bytes tx_udp_packets " + "tx_other_bytes tx_other_packets\n"); } else { tag_t tag = ppi->ts_entry->tn.tag; uid_t stat_uid = get_uid_from_tag(tag); From bf90d0ee83dcc071049363d970ed8fa744706742 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Fri, 7 Oct 2011 22:14:24 -0700 Subject: [PATCH 1977/2556] netfilter: xt_qtaguid: fix crash on ctrl delete command Because for now the xt_qtaguid module allows procs to use tags without having /dev/xt_qtaguid open, there was a case where it would try to delete a resources from a list that was proc specific. But that resource was never added to that list which is only used when /dev/xt_qtaguid has been opened by the proc. Once our userspace is fully updated, we won't need those exceptions. Change-Id: Idd4bfea926627190c74645142916e10832eb2504 Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 5d73ecaf540a5..08086d680c2c2 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1919,7 +1919,15 @@ static int ctrl_cmd_delete(const char *input) tr_entry = lookup_tag_ref(st_entry->tag, NULL); BUG_ON(tr_entry->num_sock_tags <= 0); tr_entry->num_sock_tags--; - list_del(&st_entry->list); + /* + * TODO: remove if, and start failing. + * This is a hack to work around the fact that in some + * places we have "if (IS_ERR_OR_NULL(pqd_entry))" + * and are trying to work around apps + * that didn't open the /dev/xt_qtaguid. + */ + if (st_entry->list.next && st_entry->list.prev) + list_del(&st_entry->list); } } spin_unlock_bh(&sock_tag_list_lock); From 21ee05c945f98d9dea431646d0e5326d1fa0a91a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 19 May 2011 15:44:27 +0200 Subject: [PATCH 1978/2556] netfilter: add more values to enum ip_conntrack_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Following error is raised (and other similar ones) : net/ipv4/netfilter/nf_nat_standalone.c: In function ‘nf_nat_fn’: net/ipv4/netfilter/nf_nat_standalone.c:119:2: warning: case value ‘4’ not in enumerated type ‘enum ip_conntrack_info’ gcc barfs on adding two enum values and getting a not enumerated result : case IP_CT_RELATED+IP_CT_IS_REPLY: Add missing enum values Signed-off-by: Eric Dumazet CC: David Miller Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nf_conntrack_common.h | 3 +++ net/ipv4/netfilter/ipt_CLUSTERIP.c | 6 +++--- net/ipv4/netfilter/ipt_MASQUERADE.c | 2 +- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 2 +- net/ipv4/netfilter/nf_nat_core.c | 2 +- net/ipv4/netfilter/nf_nat_rule.c | 2 +- net/ipv4/netfilter/nf_nat_standalone.c | 4 ++-- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 2 +- net/netfilter/nf_conntrack_core.c | 4 ++-- net/netfilter/nf_conntrack_ftp.c | 2 +- net/netfilter/nf_conntrack_h323_main.c | 10 ++++------ net/netfilter/nf_conntrack_irc.c | 3 +-- net/netfilter/nf_conntrack_pptp.c | 3 +-- net/netfilter/nf_conntrack_sane.c | 2 +- net/netfilter/nf_conntrack_sip.c | 2 +- net/netfilter/xt_socket.c | 4 ++-- 16 files changed, 26 insertions(+), 27 deletions(-) diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h index 50cdc2559a5aa..0d3dd66322ecb 100644 --- a/include/linux/netfilter/nf_conntrack_common.h +++ b/include/linux/netfilter/nf_conntrack_common.h @@ -18,6 +18,9 @@ enum ip_conntrack_info { /* >= this indicates reply direction */ IP_CT_IS_REPLY, + IP_CT_ESTABLISHED_REPLY = IP_CT_ESTABLISHED + IP_CT_IS_REPLY, + IP_CT_RELATED_REPLY = IP_CT_RELATED + IP_CT_IS_REPLY, + IP_CT_NEW_REPLY = IP_CT_NEW + IP_CT_IS_REPLY, /* Number of distinct IP_CT types (no NEW in reply dirn). */ IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1 }; diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index af7dec683e104..e07341db19246 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -312,7 +312,7 @@ clusterip_tg(struct sk_buff *skb, const struct xt_action_param *par) * error messages (RELATED) and information requests (see below) */ if (ip_hdr(skb)->protocol == IPPROTO_ICMP && (ctinfo == IP_CT_RELATED || - ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)) + ctinfo == IP_CT_RELATED_REPLY)) return XT_CONTINUE; /* ip_conntrack_icmp guarantees us that we only have ICMP_ECHO, @@ -326,12 +326,12 @@ clusterip_tg(struct sk_buff *skb, const struct xt_action_param *par) ct->mark = hash; break; case IP_CT_RELATED: - case IP_CT_RELATED+IP_CT_IS_REPLY: + case IP_CT_RELATED_REPLY: /* FIXME: we don't handle expectations at the * moment. they can arrive on a different node than * the master connection (e.g. FTP passive mode) */ case IP_CT_ESTABLISHED: - case IP_CT_ESTABLISHED+IP_CT_IS_REPLY: + case IP_CT_ESTABLISHED_REPLY: break; default: break; diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index d2ed9dc74ebc3..9931152a78b54 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -60,7 +60,7 @@ masquerade_tg(struct sk_buff *skb, const struct xt_action_param *par) nat = nfct_nat(ct); NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || - ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)); + ctinfo == IP_CT_RELATED_REPLY)); /* Source address is 0.0.0.0 - locally generated packet that is * probably not supposed to be masqueraded. diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 5a03c02af999a..db10075dd88e4 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -101,7 +101,7 @@ static unsigned int ipv4_confirm(unsigned int hooknum, /* This is where we call the helper: as the packet goes out. */ ct = nf_ct_get(skb, &ctinfo); - if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY) + if (!ct || ctinfo == IP_CT_RELATED_REPLY) goto out; help = nfct_help(ct); diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c index c04787ce1a712..f9ff19ffeebb7 100644 --- a/net/ipv4/netfilter/nf_nat_core.c +++ b/net/ipv4/netfilter/nf_nat_core.c @@ -428,7 +428,7 @@ int nf_nat_icmp_reply_translation(struct nf_conn *ct, /* Must be RELATED */ NF_CT_ASSERT(skb->nfctinfo == IP_CT_RELATED || - skb->nfctinfo == IP_CT_RELATED+IP_CT_IS_REPLY); + skb->nfctinfo == IP_CT_RELATED_REPLY); /* Redirects on non-null nats must be dropped, else they'll start talking to each other without our translation, and be diff --git a/net/ipv4/netfilter/nf_nat_rule.c b/net/ipv4/netfilter/nf_nat_rule.c index 21c30426480b0..733c9abc1cbd9 100644 --- a/net/ipv4/netfilter/nf_nat_rule.c +++ b/net/ipv4/netfilter/nf_nat_rule.c @@ -53,7 +53,7 @@ ipt_snat_target(struct sk_buff *skb, const struct xt_action_param *par) /* Connection must be valid and new. */ NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || - ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)); + ctinfo == IP_CT_RELATED_REPLY)); NF_CT_ASSERT(par->out != NULL); return nf_nat_setup_info(ct, &mr->range[0], IP_NAT_MANIP_SRC); diff --git a/net/ipv4/netfilter/nf_nat_standalone.c b/net/ipv4/netfilter/nf_nat_standalone.c index 95481fee8bdbf..771f5a5efbfd4 100644 --- a/net/ipv4/netfilter/nf_nat_standalone.c +++ b/net/ipv4/netfilter/nf_nat_standalone.c @@ -115,7 +115,7 @@ nf_nat_fn(unsigned int hooknum, switch (ctinfo) { case IP_CT_RELATED: - case IP_CT_RELATED+IP_CT_IS_REPLY: + case IP_CT_RELATED_REPLY: if (ip_hdr(skb)->protocol == IPPROTO_ICMP) { if (!nf_nat_icmp_reply_translation(ct, ctinfo, hooknum, skb)) @@ -143,7 +143,7 @@ nf_nat_fn(unsigned int hooknum, default: /* ESTABLISHED */ NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || - ctinfo == (IP_CT_ESTABLISHED+IP_CT_IS_REPLY)); + ctinfo == IP_CT_ESTABLISHED_REPLY); } return nf_nat_packet(ct, ctinfo, hooknum, skb); diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index c8af58b225620..4111050a9fc52 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -160,7 +160,7 @@ static unsigned int ipv6_confirm(unsigned int hooknum, /* This is where we call the helper: as the packet goes out. */ ct = nf_ct_get(skb, &ctinfo); - if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY) + if (!ct || ctinfo == IP_CT_RELATED_REPLY) goto out; help = nfct_help(ct); diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 84f4fcc5884be..842f53081f579 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -833,7 +833,7 @@ resolve_normal_ct(struct net *net, struct nf_conn *tmpl, /* It exists; we have (non-exclusive) reference. */ if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) { - *ctinfo = IP_CT_ESTABLISHED + IP_CT_IS_REPLY; + *ctinfo = IP_CT_ESTABLISHED_REPLY; /* Please set reply bit if this packet OK */ *set_reply = 1; } else { @@ -1126,7 +1126,7 @@ static void nf_conntrack_attach(struct sk_buff *nskb, struct sk_buff *skb) /* This ICMP is in reverse direction to the packet which caused it */ ct = nf_ct_get(skb, &ctinfo); if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) - ctinfo = IP_CT_RELATED + IP_CT_IS_REPLY; + ctinfo = IP_CT_RELATED_REPLY; else ctinfo = IP_CT_RELATED; diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index e17cb7c7dd8fd..6f5801eac9992 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -368,7 +368,7 @@ static int help(struct sk_buff *skb, /* Until there's been traffic both ways, don't look in packets. */ if (ctinfo != IP_CT_ESTABLISHED && - ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + ctinfo != IP_CT_ESTABLISHED_REPLY) { pr_debug("ftp: Conntrackinfo = %u\n", ctinfo); return NF_ACCEPT; } diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c index b969025cf82fe..547c05be7f5f7 100644 --- a/net/netfilter/nf_conntrack_h323_main.c +++ b/net/netfilter/nf_conntrack_h323_main.c @@ -571,10 +571,9 @@ static int h245_help(struct sk_buff *skb, unsigned int protoff, int ret; /* Until there's been traffic both ways, don't look in packets. */ - if (ctinfo != IP_CT_ESTABLISHED && - ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) return NF_ACCEPT; - } + pr_debug("nf_ct_h245: skblen = %u\n", skb->len); spin_lock_bh(&nf_h323_lock); @@ -1117,10 +1116,9 @@ static int q931_help(struct sk_buff *skb, unsigned int protoff, int ret; /* Until there's been traffic both ways, don't look in packets. */ - if (ctinfo != IP_CT_ESTABLISHED && - ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { + if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) return NF_ACCEPT; - } + pr_debug("nf_ct_q931: skblen = %u\n", skb->len); spin_lock_bh(&nf_h323_lock); diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c index b394aa3187764..4f9390b98697e 100644 --- a/net/netfilter/nf_conntrack_irc.c +++ b/net/netfilter/nf_conntrack_irc.c @@ -125,8 +125,7 @@ static int help(struct sk_buff *skb, unsigned int protoff, return NF_ACCEPT; /* Until there's been traffic both ways, don't look in packets. */ - if (ctinfo != IP_CT_ESTABLISHED && - ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) + if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) return NF_ACCEPT; /* Not a full tcp header? */ diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index 088944824e135..2fd4565144def 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -519,8 +519,7 @@ conntrack_pptp_help(struct sk_buff *skb, unsigned int protoff, u_int16_t msg; /* don't do any tracking before tcp handshake complete */ - if (ctinfo != IP_CT_ESTABLISHED && - ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) + if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) return NF_ACCEPT; nexthdr_off = protoff; diff --git a/net/netfilter/nf_conntrack_sane.c b/net/netfilter/nf_conntrack_sane.c index d9e27734b2a22..8501823b3f9b0 100644 --- a/net/netfilter/nf_conntrack_sane.c +++ b/net/netfilter/nf_conntrack_sane.c @@ -78,7 +78,7 @@ static int help(struct sk_buff *skb, ct_sane_info = &nfct_help(ct)->help.ct_sane_info; /* Until there's been traffic both ways, don't look in packets. */ if (ctinfo != IP_CT_ESTABLISHED && - ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) + ctinfo != IP_CT_ESTABLISHED_REPLY) return NF_ACCEPT; /* Not a full tcp header? */ diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index bcf47eb518eff..95a046a548fd0 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1422,7 +1422,7 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, typeof(nf_nat_sip_seq_adjust_hook) nf_nat_sip_seq_adjust; if (ctinfo != IP_CT_ESTABLISHED && - ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) + ctinfo != IP_CT_ESTABLISHED_REPLY) return NF_ACCEPT; /* No Data ? */ diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 9cc46356b5773..fe39f7e913dff 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -143,9 +143,9 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, ct = nf_ct_get(skb, &ctinfo); if (ct && !nf_ct_is_untracked(ct) && ((iph->protocol != IPPROTO_ICMP && - ctinfo == IP_CT_IS_REPLY + IP_CT_ESTABLISHED) || + ctinfo == IP_CT_ESTABLISHED_REPLY) || (iph->protocol == IPPROTO_ICMP && - ctinfo == IP_CT_IS_REPLY + IP_CT_RELATED)) && + ctinfo == IP_CT_RELATED_REPLY)) && (ct->status & IPS_SRC_NAT_DONE)) { daddr = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; From 4e774c2129b0b6415305c02e29a8ee875225b259 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Wed, 15 Jun 2011 16:52:40 -0700 Subject: [PATCH 1979/2556] nf: xt_socket: export the fancy sock finder code The socket matching function has some nifty logic to get the struct sock from the skb or from the connection tracker. We export this so other xt_* can use it, similarly to ho how xt_socket uses nf_tproxy_get_sock. Change-Id: I11c58f59087e7f7ae09e4abd4b937cd3370fa2fd Signed-off-by: JP Abgrall --- include/linux/netfilter/xt_socket.h | 6 +++ net/netfilter/xt_socket.c | 70 +++++++++++++++++++---------- 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/include/linux/netfilter/xt_socket.h b/include/linux/netfilter/xt_socket.h index 6f475b8ff34be..b21f2c23cd24f 100644 --- a/include/linux/netfilter/xt_socket.h +++ b/include/linux/netfilter/xt_socket.h @@ -9,4 +9,10 @@ struct xt_socket_mtinfo1 { __u8 flags; }; +void xt_socket_put_sk(struct sock *sk); +struct sock *xt_socket_get4_sk(const struct sk_buff *skb, + struct xt_action_param *par); +struct sock *xt_socket_get6_sk(const struct sk_buff *skb, + struct xt_action_param *par); + #endif /* _XT_SOCKET_H */ diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index fe39f7e913dff..ddf5e0507f5f5 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -35,7 +35,7 @@ #include #endif -static void +void xt_socket_put_sk(struct sock *sk) { if (sk->sk_state == TCP_TIME_WAIT) @@ -43,6 +43,7 @@ xt_socket_put_sk(struct sock *sk) else sock_put(sk); } +EXPORT_SYMBOL(xt_socket_put_sk); static int extract_icmp4_fields(const struct sk_buff *skb, @@ -101,9 +102,8 @@ extract_icmp4_fields(const struct sk_buff *skb, return 0; } -static bool -socket_match(const struct sk_buff *skb, struct xt_action_param *par, - const struct xt_socket_mtinfo1 *info) +struct sock* +xt_socket_get4_sk(const struct sk_buff *skb, struct xt_action_param *par) { const struct iphdr *iph = ip_hdr(skb); struct udphdr _hdr, *hp = NULL; @@ -120,7 +120,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr); if (hp == NULL) - return false; + return NULL; protocol = iph->protocol; saddr = iph->saddr; @@ -131,9 +131,9 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, } else if (iph->protocol == IPPROTO_ICMP) { if (extract_icmp4_fields(skb, &protocol, &saddr, &daddr, &sport, &dport)) - return false; + return NULL; } else { - return false; + return NULL; } #ifdef XT_SOCKET_HAVE_CONNTRACK @@ -157,6 +157,23 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol, saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY); + + pr_debug("proto %hhu %pI4:%hu -> %pI4:%hu (orig %pI4:%hu) sock %p\n", + protocol, &saddr, ntohs(sport), + &daddr, ntohs(dport), + &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); + + return sk; +} +EXPORT_SYMBOL(xt_socket_get4_sk); + +static bool +socket_match(const struct sk_buff *skb, struct xt_action_param *par, + const struct xt_socket_mtinfo1 *info) +{ + struct sock *sk; + + sk = xt_socket_get4_sk(skb, par); if (sk != NULL) { bool wildcard; bool transparent = true; @@ -179,11 +196,6 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, sk = NULL; } - pr_debug("proto %hhu %pI4:%hu -> %pI4:%hu (orig %pI4:%hu) sock %p\n", - protocol, &saddr, ntohs(sport), - &daddr, ntohs(dport), - &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); - return (sk != NULL); } @@ -253,8 +265,8 @@ extract_icmp6_fields(const struct sk_buff *skb, return 0; } -static bool -socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) +struct sock* +xt_socket_get6_sk(const struct sk_buff *skb, struct xt_action_param *par) { struct ipv6hdr *iph = ipv6_hdr(skb); struct udphdr _hdr, *hp = NULL; @@ -262,7 +274,6 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) struct in6_addr *daddr, *saddr; __be16 dport, sport; int thoff, tproto; - const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo; tproto = ipv6_find_hdr(skb, &thoff, -1, NULL); if (tproto < 0) { @@ -274,7 +285,7 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr); if (hp == NULL) - return false; + return NULL; saddr = &iph->saddr; sport = hp->source; @@ -284,13 +295,30 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) } else if (tproto == IPPROTO_ICMPV6) { if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr, &sport, &dport)) - return false; + return NULL; } else { - return false; + return NULL; } sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY); + pr_debug("proto %hhd %pI6:%hu -> %pI6:%hu " + "(orig %pI6:%hu) sock %p\n", + tproto, saddr, ntohs(sport), + daddr, ntohs(dport), + &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); + return sk; +} +EXPORT_SYMBOL(xt_socket_get6_sk); + +static bool +socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) +{ + struct sock *sk; + const struct xt_socket_mtinfo1 *info; + + info = (struct xt_socket_mtinfo1 *) par->matchinfo; + sk = xt_socket_get6_sk(skb, par); if (sk != NULL) { bool wildcard; bool transparent = true; @@ -313,12 +341,6 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) sk = NULL; } - pr_debug("proto %hhd %pI6:%hu -> %pI6:%hu " - "(orig %pI6:%hu) sock %p\n", - tproto, saddr, ntohs(sport), - daddr, ntohs(dport), - &iph->daddr, hp ? ntohs(hp->dest) : 0, sk); - return (sk != NULL); } #endif From 2739285d8f7e5039a9072f58d286257b14242d52 Mon Sep 17 00:00:00 2001 From: Ricardo Cerqueira Date: Thu, 17 Nov 2011 03:12:43 +0000 Subject: [PATCH 1980/2556] nf: xt_qtaguid: 2.6.35 compat --- net/netfilter/xt_qtaguid.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 08086d680c2c2..b19fa949f9920 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -30,6 +30,7 @@ #include "xt_qtaguid_internal.h" #include "xt_qtaguid_print.h" +#define pr_warn_once printk /* * We only use the xt_socket funcs within a similar context to avoid unexpected * return values. @@ -785,7 +786,7 @@ static int iface_stat_all_proc_read(char *page, char **num_items_returned, int item_index = 0; int len; struct iface_stat *iface_entry; - struct rtnl_link_stats64 dev_stats, *stats; + const struct net_device_stats *stats; struct rtnl_link_stats64 no_dev_stats = {0}; if (unlikely(module_passive)) { @@ -811,15 +812,14 @@ static int iface_stat_all_proc_read(char *page, char **num_items_returned, continue; if (iface_entry->active) { - stats = dev_get_stats(iface_entry->net_dev, - &dev_stats); + stats = dev_get_stats(iface_entry->net_dev); } else { stats = &no_dev_stats; } len = snprintf(outp, char_count, "%s %d " "%llu %llu %llu %llu " - "%llu %llu %llu %llu\n", + "%lu %lu %lu %lu\n", iface_entry->ifname, iface_entry->active, iface_entry->totals[IFS_RX].bytes, @@ -948,17 +948,17 @@ static struct iface_stat *iface_alloc(struct net_device *net_dev) static void iface_check_stats_reset_and_adjust(struct net_device *net_dev, struct iface_stat *iface) { - struct rtnl_link_stats64 dev_stats, *stats; + const struct net_device_stats *stats; bool stats_rewound; - stats = dev_get_stats(net_dev, &dev_stats); + stats = dev_get_stats(net_dev); /* No empty packets */ stats_rewound = (stats->rx_bytes < iface->last_known[IFS_RX].bytes) || (stats->tx_bytes < iface->last_known[IFS_TX].bytes); IF_DEBUG("qtaguid: %s(%s): iface=%p netdev=%p " - "bytes rx/tx=%llu/%llu " + "bytes rx/tx=%lu/%lu " "active=%d last_known=%d " "stats_rewound=%d\n", __func__, net_dev ? net_dev->name : "?", @@ -1169,10 +1169,10 @@ data_counters_update(struct data_counters *dc, int set, */ static void iface_stat_update(struct net_device *net_dev, bool stash_only) { - struct rtnl_link_stats64 dev_stats, *stats; + const struct net_device_stats *stats; struct iface_stat *entry; - stats = dev_get_stats(net_dev, &dev_stats); + stats = dev_get_stats(net_dev); spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(net_dev->name); if (entry == NULL) { @@ -1198,7 +1198,7 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only) entry->last_known[IFS_RX].packets = stats->rx_packets; entry->last_known_valid = true; IF_DEBUG("qtaguid: %s(%s): " - "dev stats stashed rx/tx=%llu/%llu\n", __func__, + "dev stats stashed rx/tx=%lu/%lu\n", __func__, net_dev->name, stats->rx_bytes, stats->tx_bytes); spin_unlock_bh(&iface_stat_list_lock); return; @@ -1211,7 +1211,7 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only) entry->last_known_valid = false; _iface_stat_set_active(entry, net_dev, false); IF_DEBUG("qtaguid: %s(%s): " - "disable tracking. rx/tx=%llu/%llu\n", __func__, + "disable tracking. rx/tx=%lu/%lu\n", __func__, net_dev->name, stats->rx_bytes, stats->tx_bytes); spin_unlock_bh(&iface_stat_list_lock); } @@ -1509,9 +1509,11 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, return NULL; switch (par->family) { +#ifdef XT_SOCKET_HAVE_IPV6 case NFPROTO_IPV6: sk = xt_socket_get6_sk(skb, par); break; +#endif case NFPROTO_IPV4: sk = xt_socket_get4_sk(skb, par); break; From 6637d5d5c6debf78e1771ec5347bcb83c1feb187 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 21 Jun 2011 11:14:49 -0700 Subject: [PATCH 1981/2556] netfilter: adding the original quota2 from xtables-addons (Backport from common 3.0: Change I19d49858) The original xt_quota in the kernel is plain broken: - counts quota at a per CPU level (was written back when ubiquitous SMP was just a dream) - provides no way to count across IPV4/IPV6. This patch is the original unaltered code from: http://sourceforge.net/projects/xtables-addons at commit e84391ce665cef046967f796dd91026851d6bbf3 Change-Id: I19d49858840effee9ecf6cff03c23b45a97efdeb Signed-off-by: JP Abgrall --- include/linux/netfilter/xt_quota2.h | 25 +++ net/netfilter/xt_quota2.c | 274 ++++++++++++++++++++++++++++ 2 files changed, 299 insertions(+) create mode 100644 include/linux/netfilter/xt_quota2.h create mode 100644 net/netfilter/xt_quota2.c diff --git a/include/linux/netfilter/xt_quota2.h b/include/linux/netfilter/xt_quota2.h new file mode 100644 index 0000000000000..eadc6903314e7 --- /dev/null +++ b/include/linux/netfilter/xt_quota2.h @@ -0,0 +1,25 @@ +#ifndef _XT_QUOTA_H +#define _XT_QUOTA_H + +enum xt_quota_flags { + XT_QUOTA_INVERT = 1 << 0, + XT_QUOTA_GROW = 1 << 1, + XT_QUOTA_PACKET = 1 << 2, + XT_QUOTA_NO_CHANGE = 1 << 3, + XT_QUOTA_MASK = 0x0F, +}; + +struct xt_quota_counter; + +struct xt_quota_mtinfo2 { + char name[15]; + u_int8_t flags; + + /* Comparison-invariant */ + aligned_u64 quota; + + /* Used internally by the kernel */ + struct xt_quota_counter *master __attribute__((aligned(8))); +}; + +#endif /* _XT_QUOTA_H */ diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c new file mode 100644 index 0000000000000..4857008f1eb04 --- /dev/null +++ b/net/netfilter/xt_quota2.c @@ -0,0 +1,274 @@ +/* + * xt_quota2 - enhanced xt_quota that can count upwards and in packets + * as a minimal accounting match. + * by Jan Engelhardt , 2008 + * + * Originally based on xt_quota.c: + * netfilter module to enforce network quotas + * Sam Johnston + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License; either + * version 2 of the License, as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include + +#include +#include "xt_quota2.h" +#include "compat_xtables.h" + +/** + * @lock: lock to protect quota writers from each other + */ +struct xt_quota_counter { + u_int64_t quota; + spinlock_t lock; + struct list_head list; + atomic_t ref; + char name[sizeof(((struct xt_quota_mtinfo2 *)NULL)->name)]; + struct proc_dir_entry *procfs_entry; +}; + +static LIST_HEAD(counter_list); +static DEFINE_SPINLOCK(counter_list_lock); + +static struct proc_dir_entry *proc_xt_quota; +static unsigned int quota_list_perms = S_IRUGO | S_IWUSR; +static unsigned int quota_list_uid = 0; +static unsigned int quota_list_gid = 0; +module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR); +module_param_named(uid, quota_list_uid, uint, S_IRUGO | S_IWUSR); +module_param_named(gid, quota_list_gid, uint, S_IRUGO | S_IWUSR); + +static int quota_proc_read(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + struct xt_quota_counter *e = data; + int ret; + + spin_lock_bh(&e->lock); + ret = snprintf(page, PAGE_SIZE, "%llu\n", e->quota); + spin_unlock_bh(&e->lock); + return ret; +} + +static int quota_proc_write(struct file *file, const char __user *input, + unsigned long size, void *data) +{ + struct xt_quota_counter *e = data; + char buf[sizeof("18446744073709551616")]; + + if (size > sizeof(buf)) + size = sizeof(buf); + if (copy_from_user(buf, input, size) != 0) + return -EFAULT; + buf[sizeof(buf)-1] = '\0'; + + spin_lock_bh(&e->lock); + e->quota = simple_strtoull(buf, NULL, 0); + spin_unlock_bh(&e->lock); + return size; +} + +static struct xt_quota_counter * +q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon) +{ + struct xt_quota_counter *e; + unsigned int size; + + /* Do not need all the procfs things for anonymous counters. */ + size = anon ? offsetof(typeof(*e), list) : sizeof(*e); + e = kmalloc(size, GFP_KERNEL); + if (e == NULL) + return NULL; + + e->quota = q->quota; + spin_lock_init(&e->lock); + if (!anon) { + INIT_LIST_HEAD(&e->list); + atomic_set(&e->ref, 1); + strncpy(e->name, q->name, sizeof(e->name)); + } + return e; +} + +/** + * q2_get_counter - get ref to counter or create new + * @name: name of counter + */ +static struct xt_quota_counter * +q2_get_counter(const struct xt_quota_mtinfo2 *q) +{ + struct proc_dir_entry *p; + struct xt_quota_counter *e; + + if (*q->name == '\0') + return q2_new_counter(q, true); + + spin_lock_bh(&counter_list_lock); + list_for_each_entry(e, &counter_list, list) + if (strcmp(e->name, q->name) == 0) { + atomic_inc(&e->ref); + spin_unlock_bh(&counter_list_lock); + return e; + } + + e = q2_new_counter(q, false); + if (e == NULL) + goto out; + + p = e->procfs_entry = create_proc_entry(e->name, quota_list_perms, + proc_xt_quota); + if (p == NULL || IS_ERR(p)) + goto out; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) + p->owner = THIS_MODULE; +#endif + p->data = e; + p->read_proc = quota_proc_read; + p->write_proc = quota_proc_write; + p->uid = quota_list_uid; + p->gid = quota_list_gid; + list_add_tail(&e->list, &counter_list); + spin_unlock_bh(&counter_list_lock); + return e; + + out: + spin_unlock_bh(&counter_list_lock); + kfree(e); + return NULL; +} + +static int quota_mt2_check(const struct xt_mtchk_param *par) +{ + struct xt_quota_mtinfo2 *q = par->matchinfo; + + if (q->flags & ~XT_QUOTA_MASK) + return -EINVAL; + + q->name[sizeof(q->name)-1] = '\0'; + if (*q->name == '.' || strchr(q->name, '/') != NULL) { + printk(KERN_ERR "xt_quota.3: illegal name\n"); + return -EINVAL; + } + + q->master = q2_get_counter(q); + if (q->master == NULL) { + printk(KERN_ERR "xt_quota.3: memory alloc failure\n"); + return -ENOMEM; + } + + return 0; +} + +static void quota_mt2_destroy(const struct xt_mtdtor_param *par) +{ + struct xt_quota_mtinfo2 *q = par->matchinfo; + struct xt_quota_counter *e = q->master; + + if (*q->name == '\0') { + kfree(e); + return; + } + + spin_lock_bh(&counter_list_lock); + if (!atomic_dec_and_test(&e->ref)) { + spin_unlock_bh(&counter_list_lock); + return; + } + + list_del(&e->list); + remove_proc_entry(e->name, proc_xt_quota); + spin_unlock_bh(&counter_list_lock); + kfree(e); +} + +static bool +quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) +{ + struct xt_quota_mtinfo2 *q = (void *)par->matchinfo; + struct xt_quota_counter *e = q->master; + bool ret = q->flags & XT_QUOTA_INVERT; + + spin_lock_bh(&e->lock); + if (q->flags & XT_QUOTA_GROW) { + /* + * While no_change is pointless in "grow" mode, we will + * implement it here simply to have a consistent behavior. + */ + if (!(q->flags & XT_QUOTA_NO_CHANGE)) { + e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; + q->quota = e->quota; + } + ret = true; + } else { + if (e->quota >= skb->len) { + if (!(q->flags & XT_QUOTA_NO_CHANGE)) + e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; + ret = !ret; + } else { + /* we do not allow even small packets from now on */ + e->quota = 0; + } + q->quota = e->quota; + } + spin_unlock_bh(&e->lock); + return ret; +} + +static struct xt_match quota_mt2_reg[] __read_mostly = { + { + .name = "quota2", + .revision = 3, + .family = NFPROTO_IPV4, + .checkentry = quota_mt2_check, + .match = quota_mt2, + .destroy = quota_mt2_destroy, + .matchsize = sizeof(struct xt_quota_mtinfo2), + .me = THIS_MODULE, + }, + { + .name = "quota2", + .revision = 3, + .family = NFPROTO_IPV6, + .checkentry = quota_mt2_check, + .match = quota_mt2, + .destroy = quota_mt2_destroy, + .matchsize = sizeof(struct xt_quota_mtinfo2), + .me = THIS_MODULE, + }, +}; + +static int __init quota_mt2_init(void) +{ + int ret; + + proc_xt_quota = proc_mkdir("xt_quota", init_net__proc_net); + if (proc_xt_quota == NULL) + return -EACCES; + + ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); + if (ret < 0) + remove_proc_entry("xt_quota", init_net__proc_net); + return ret; +} + +static void __exit quota_mt2_exit(void) +{ + xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); + remove_proc_entry("xt_quota", init_net__proc_net); +} + +module_init(quota_mt2_init); +module_exit(quota_mt2_exit); +MODULE_DESCRIPTION("Xtables: countdown quota match; up counter"); +MODULE_AUTHOR("Sam Johnston "); +MODULE_AUTHOR("Jan Engelhardt "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_quota2"); +MODULE_ALIAS("ip6t_quota2"); From f6705cf585e15bbb64f1f56f52b85bc88fcdca35 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 12 Jul 2011 12:02:59 -0700 Subject: [PATCH 1982/2556] netfitler: fixup the quota2, and enable. (Backport from common 3.0: Change I021d3b74) The xt_quota2 came from http://sourceforge.net/projects/xtables-addons/develop It needed tweaking for it to compile within the kernel tree. Fixed kmalloc() and create_proc_entry() invocations within a non-interruptible context. Removed useless copying of current quota back to the iptable's struct matchinfo: - those are per CPU: they will change randomly based on which cpu gets to update the value. - they prevent matching a rule: e.g. -A chain -m quota2 --name q1 --quota 123 can't be followed by -D chain -m quota2 --name q1 --quota 123 as the 123 will be compared to the struct matchinfo's quota member. Change-Id: I021d3b743db3b22158cc49acb5c94d905b501492 Signed-off-by: JP Abgrall --- net/netfilter/Kconfig | 12 ++++++++ net/netfilter/Makefile | 1 + net/netfilter/xt_quota2.c | 61 ++++++++++++++++++++++++--------------- 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index c67f793ae382e..325bfda8dbd2c 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -891,6 +891,18 @@ config NETFILTER_XT_MATCH_QUOTA If you want to compile it as a module, say M here and read . If unsure, say `N'. +config NETFILTER_XT_MATCH_QUOTA2 + tristate '"quota2" match support' + depends on NETFILTER_ADVANCED + help + This option adds a `quota2' match, which allows to match on a + byte counter correctly and not per CPU. + It allows naming the quotas. + This is based on http://xtables-addons.git.sourceforge.net + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + config NETFILTER_XT_MATCH_RATEEST tristate '"rateest" match support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index e9552d4abb869..210e607491a2f 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -90,6 +90,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o obj-$(CONFIG_NETFILTER_XT_MATCH_QTAGUID) += xt_qtaguid_print.o xt_qtaguid.o obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o +obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA2) += xt_quota2.o obj-$(CONFIG_NETFILTER_XT_MATCH_RATEEST) += xt_rateest.o obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o obj-$(CONFIG_NETFILTER_XT_MATCH_RECENT) += xt_recent.o diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c index 4857008f1eb04..454ce10c8cdc8 100644 --- a/net/netfilter/xt_quota2.c +++ b/net/netfilter/xt_quota2.c @@ -18,8 +18,7 @@ #include #include -#include "xt_quota2.h" -#include "compat_xtables.h" +#include /** * @lock: lock to protect quota writers from each other @@ -91,7 +90,7 @@ q2_new_counter(const struct xt_quota_mtinfo2 *q, bool anon) if (!anon) { INIT_LIST_HEAD(&e->list); atomic_set(&e->ref, 1); - strncpy(e->name, q->name, sizeof(e->name)); + strlcpy(e->name, q->name, sizeof(e->name)); } return e; } @@ -104,42 +103,56 @@ static struct xt_quota_counter * q2_get_counter(const struct xt_quota_mtinfo2 *q) { struct proc_dir_entry *p; - struct xt_quota_counter *e; + struct xt_quota_counter *e = NULL; + struct xt_quota_counter *new_e; if (*q->name == '\0') return q2_new_counter(q, true); + /* No need to hold a lock while getting a new counter */ + new_e = q2_new_counter(q, false); + if (new_e == NULL) + goto out; + spin_lock_bh(&counter_list_lock); list_for_each_entry(e, &counter_list, list) if (strcmp(e->name, q->name) == 0) { atomic_inc(&e->ref); spin_unlock_bh(&counter_list_lock); + kfree(new_e); + pr_debug("xt_quota2: old counter name=%s", e->name); return e; } + e = new_e; + pr_debug("xt_quota2: new_counter name=%s", e->name); + list_add_tail(&e->list, &counter_list); + /* The entry having a refcount of 1 is not directly destructible. + * This func has not yet returned the new entry, thus iptables + * has not references for destroying this entry. + * For another rule to try to destroy it, it would 1st need for this + * func* to be re-invoked, acquire a new ref for the same named quota. + * Nobody will access the e->procfs_entry either. + * So release the lock. */ + spin_unlock_bh(&counter_list_lock); - e = q2_new_counter(q, false); - if (e == NULL) - goto out; - + /* create_proc_entry() is not spin_lock happy */ p = e->procfs_entry = create_proc_entry(e->name, quota_list_perms, proc_xt_quota); - if (p == NULL || IS_ERR(p)) - goto out; -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) - p->owner = THIS_MODULE; -#endif + if (IS_ERR_OR_NULL(p)) { + spin_lock_bh(&counter_list_lock); + list_del(&e->list); + spin_unlock_bh(&counter_list_lock); + goto out; + } p->data = e; p->read_proc = quota_proc_read; p->write_proc = quota_proc_write; p->uid = quota_list_uid; p->gid = quota_list_gid; - list_add_tail(&e->list, &counter_list); - spin_unlock_bh(&counter_list_lock); return e; out: - spin_unlock_bh(&counter_list_lock); kfree(e); return NULL; } @@ -148,6 +161,8 @@ static int quota_mt2_check(const struct xt_mtchk_param *par) { struct xt_quota_mtinfo2 *q = par->matchinfo; + pr_debug("xt_quota2: check() flags=0x%04x", q->flags); + if (q->flags & ~XT_QUOTA_MASK) return -EINVAL; @@ -203,7 +218,6 @@ quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) */ if (!(q->flags & XT_QUOTA_NO_CHANGE)) { e->quota += (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; - q->quota = e->quota; } ret = true; } else { @@ -215,7 +229,6 @@ quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) /* we do not allow even small packets from now on */ e->quota = 0; } - q->quota = e->quota; } spin_unlock_bh(&e->lock); return ret; @@ -228,7 +241,7 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { .family = NFPROTO_IPV4, .checkentry = quota_mt2_check, .match = quota_mt2, - .destroy = quota_mt2_destroy, + .destroy = quota_mt2_destroy, .matchsize = sizeof(struct xt_quota_mtinfo2), .me = THIS_MODULE, }, @@ -238,7 +251,7 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { .family = NFPROTO_IPV6, .checkentry = quota_mt2_check, .match = quota_mt2, - .destroy = quota_mt2_destroy, + .destroy = quota_mt2_destroy, .matchsize = sizeof(struct xt_quota_mtinfo2), .me = THIS_MODULE, }, @@ -247,21 +260,23 @@ static struct xt_match quota_mt2_reg[] __read_mostly = { static int __init quota_mt2_init(void) { int ret; + pr_debug("xt_quota2: init()"); - proc_xt_quota = proc_mkdir("xt_quota", init_net__proc_net); + proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net); if (proc_xt_quota == NULL) return -EACCES; ret = xt_register_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); if (ret < 0) - remove_proc_entry("xt_quota", init_net__proc_net); + remove_proc_entry("xt_quota", init_net.proc_net); + pr_debug("xt_quota2: init() %d", ret); return ret; } static void __exit quota_mt2_exit(void) { xt_unregister_matches(quota_mt2_reg, ARRAY_SIZE(quota_mt2_reg)); - remove_proc_entry("xt_quota", init_net__proc_net); + remove_proc_entry("xt_quota", init_net.proc_net); } module_init(quota_mt2_init); From 67128774f8b1715f8ad5dc61d5c6b9e893b2983b Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Wed, 13 Jul 2011 16:02:31 -0700 Subject: [PATCH 1983/2556] netfilter: quota2: add support to log quota limit reached. (backport from 3.0: I6f31736b568bb31a4ff0b9ac2ee58380e6b675ca) This uses the NETLINK NETLINK_NFLOG family to log a single message when the quota limit is reached. It uses the same packet type as ipt_ULOG, but - never copies skb data, - uses 112 as the event number (ULOG's +1) It doesn't log if the module param "event_num" is 0. Change-Id: I6f31736b568bb31a4ff0b9ac2ee58380e6b675ca Signed-off-by: JP Abgrall --- net/netfilter/Kconfig | 12 +++++ net/netfilter/xt_quota2.c | 92 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 325bfda8dbd2c..a776cc65bc3eb 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -903,6 +903,18 @@ config NETFILTER_XT_MATCH_QUOTA2 If you want to compile it as a module, say M here and read . If unsure, say `N'. +config NETFILTER_XT_MATCH_QUOTA2_LOG + bool '"quota2" Netfilter LOG support' + depends on NETFILTER_XT_MATCH_QUOTA2 + depends on IP_NF_TARGET_ULOG=n # not yes, not module, just no + default n + help + This option allows `quota2' to log ONCE when a quota limit + is passed. It logs via NETLINK using the NETLINK_NFLOG family. + It logs similarly to how ipt_ULOG would without data. + + If unsure, say `N'. + config NETFILTER_XT_MATCH_RATEEST tristate '"rateest" match support' depends on NETFILTER_ADVANCED diff --git a/net/netfilter/xt_quota2.c b/net/netfilter/xt_quota2.c index 454ce10c8cdc8..3c72bea2dd698 100644 --- a/net/netfilter/xt_quota2.c +++ b/net/netfilter/xt_quota2.c @@ -19,6 +19,9 @@ #include #include +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +#include +#endif /** * @lock: lock to protect quota writers from each other @@ -32,6 +35,16 @@ struct xt_quota_counter { struct proc_dir_entry *procfs_entry; }; +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +/* Harald's favorite number +1 :D From ipt_ULOG.C */ +static int qlog_nl_event = 112; +module_param_named(event_num, qlog_nl_event, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(event_num, + "Event number for NETLINK_NFLOG message. 0 disables log." + "111 is what ipt_ULOG uses."); +static struct sock *nflognl; +#endif + static LIST_HEAD(counter_list); static DEFINE_SPINLOCK(counter_list_lock); @@ -43,6 +56,69 @@ module_param_named(perms, quota_list_perms, uint, S_IRUGO | S_IWUSR); module_param_named(uid, quota_list_uid, uint, S_IRUGO | S_IWUSR); module_param_named(gid, quota_list_gid, uint, S_IRUGO | S_IWUSR); + +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG +static void quota2_log(unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const char *prefix) +{ + ulog_packet_msg_t *pm; + struct sk_buff *log_skb; + size_t size; + struct nlmsghdr *nlh; + + if (!qlog_nl_event) + return; + + size = NLMSG_SPACE(sizeof(*pm)); + size = max(size, (size_t)NLMSG_GOODSIZE); + log_skb = alloc_skb(size, GFP_ATOMIC); + if (!log_skb) { + pr_err("xt_quota2: cannot alloc skb for logging\n"); + return; + } + + /* NLMSG_PUT() uses "goto nlmsg_failure" */ + nlh = NLMSG_PUT(log_skb, /*pid*/0, /*seq*/0, qlog_nl_event, + sizeof(*pm)); + pm = NLMSG_DATA(nlh); + if (skb->tstamp.tv64 == 0) + __net_timestamp((struct sk_buff *)skb); + pm->data_len = 0; + pm->hook = hooknum; + if (prefix != NULL) + strlcpy(pm->prefix, prefix, sizeof(pm->prefix)); + else + *(pm->prefix) = '\0'; + if (in) + strlcpy(pm->indev_name, in->name, sizeof(pm->indev_name)); + else + pm->indev_name[0] = '\0'; + + if (out) + strlcpy(pm->outdev_name, out->name, sizeof(pm->outdev_name)); + else + pm->outdev_name[0] = '\0'; + + NETLINK_CB(log_skb).dst_group = 1; + pr_debug("throwing 1 packets to netlink group 1\n"); + netlink_broadcast(nflognl, log_skb, 0, 1, GFP_ATOMIC); + +nlmsg_failure: /* Used within NLMSG_PUT() */ + pr_debug("xt_quota2: error during NLMSG_PUT\n"); +} +#else +static void quota2_log(unsigned int hooknum, + const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const char *prefix) +{ +} +#endif /* if+else CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG */ + static int quota_proc_read(char *page, char **start, off_t offset, int count, int *eof, void *data) { @@ -226,6 +302,14 @@ quota_mt2(const struct sk_buff *skb, struct xt_action_param *par) e->quota -= (q->flags & XT_QUOTA_PACKET) ? 1 : skb->len; ret = !ret; } else { + /* We are transitioning, log that fact. */ + if (e->quota) { + quota2_log(par->hooknum, + skb, + par->in, + par->out, + q->name); + } /* we do not allow even small packets from now on */ e->quota = 0; } @@ -262,6 +346,14 @@ static int __init quota_mt2_init(void) int ret; pr_debug("xt_quota2: init()"); +#ifdef CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG + nflognl = netlink_kernel_create(&init_net, + NETLINK_NFLOG, 1, NULL, + NULL, THIS_MODULE); + if (!nflognl) + return -ENOMEM; +#endif + proc_xt_quota = proc_mkdir("xt_quota", init_net.proc_net); if (proc_xt_quota == NULL) return -EACCES; From 76f5bda56399c6e978800ceea8819bcf3b295b88 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Fri, 25 Nov 2011 23:19:01 -0600 Subject: [PATCH 1984/2556] Revert "nf: xt_qtaguid: 2.6.35 compat" This reverts commit 84d640ca9906da81910b0cc7cb331351cbe71b67. --- net/netfilter/xt_qtaguid.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index b19fa949f9920..08086d680c2c2 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -30,7 +30,6 @@ #include "xt_qtaguid_internal.h" #include "xt_qtaguid_print.h" -#define pr_warn_once printk /* * We only use the xt_socket funcs within a similar context to avoid unexpected * return values. @@ -786,7 +785,7 @@ static int iface_stat_all_proc_read(char *page, char **num_items_returned, int item_index = 0; int len; struct iface_stat *iface_entry; - const struct net_device_stats *stats; + struct rtnl_link_stats64 dev_stats, *stats; struct rtnl_link_stats64 no_dev_stats = {0}; if (unlikely(module_passive)) { @@ -812,14 +811,15 @@ static int iface_stat_all_proc_read(char *page, char **num_items_returned, continue; if (iface_entry->active) { - stats = dev_get_stats(iface_entry->net_dev); + stats = dev_get_stats(iface_entry->net_dev, + &dev_stats); } else { stats = &no_dev_stats; } len = snprintf(outp, char_count, "%s %d " "%llu %llu %llu %llu " - "%lu %lu %lu %lu\n", + "%llu %llu %llu %llu\n", iface_entry->ifname, iface_entry->active, iface_entry->totals[IFS_RX].bytes, @@ -948,17 +948,17 @@ static struct iface_stat *iface_alloc(struct net_device *net_dev) static void iface_check_stats_reset_and_adjust(struct net_device *net_dev, struct iface_stat *iface) { - const struct net_device_stats *stats; + struct rtnl_link_stats64 dev_stats, *stats; bool stats_rewound; - stats = dev_get_stats(net_dev); + stats = dev_get_stats(net_dev, &dev_stats); /* No empty packets */ stats_rewound = (stats->rx_bytes < iface->last_known[IFS_RX].bytes) || (stats->tx_bytes < iface->last_known[IFS_TX].bytes); IF_DEBUG("qtaguid: %s(%s): iface=%p netdev=%p " - "bytes rx/tx=%lu/%lu " + "bytes rx/tx=%llu/%llu " "active=%d last_known=%d " "stats_rewound=%d\n", __func__, net_dev ? net_dev->name : "?", @@ -1169,10 +1169,10 @@ data_counters_update(struct data_counters *dc, int set, */ static void iface_stat_update(struct net_device *net_dev, bool stash_only) { - const struct net_device_stats *stats; + struct rtnl_link_stats64 dev_stats, *stats; struct iface_stat *entry; - stats = dev_get_stats(net_dev); + stats = dev_get_stats(net_dev, &dev_stats); spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(net_dev->name); if (entry == NULL) { @@ -1198,7 +1198,7 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only) entry->last_known[IFS_RX].packets = stats->rx_packets; entry->last_known_valid = true; IF_DEBUG("qtaguid: %s(%s): " - "dev stats stashed rx/tx=%lu/%lu\n", __func__, + "dev stats stashed rx/tx=%llu/%llu\n", __func__, net_dev->name, stats->rx_bytes, stats->tx_bytes); spin_unlock_bh(&iface_stat_list_lock); return; @@ -1211,7 +1211,7 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only) entry->last_known_valid = false; _iface_stat_set_active(entry, net_dev, false); IF_DEBUG("qtaguid: %s(%s): " - "disable tracking. rx/tx=%lu/%lu\n", __func__, + "disable tracking. rx/tx=%llu/%llu\n", __func__, net_dev->name, stats->rx_bytes, stats->tx_bytes); spin_unlock_bh(&iface_stat_list_lock); } @@ -1509,11 +1509,9 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, return NULL; switch (par->family) { -#ifdef XT_SOCKET_HAVE_IPV6 case NFPROTO_IPV6: sk = xt_socket_get6_sk(skb, par); break; -#endif case NFPROTO_IPV4: sk = xt_socket_get4_sk(skb, par); break; From 431faf3f55c33cb69fdf1c0fe465cff187a6e8d4 Mon Sep 17 00:00:00 2001 From: plattypus Date: Thu, 1 Dec 2011 22:38:36 -0700 Subject: [PATCH 1985/2556] msm: htc_headset_mgr: fix headphones for ICS --- arch/arm/mach-msm/htc_headset_mgr.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-msm/htc_headset_mgr.c b/arch/arm/mach-msm/htc_headset_mgr.c index f7d40069e00a6..e16f46a158882 100644 --- a/arch/arm/mach-msm/htc_headset_mgr.c +++ b/arch/arm/mach-msm/htc_headset_mgr.c @@ -372,12 +372,12 @@ static void remove_35mm_do_work(struct work_struct *work) if (hi->usb_dev_type == USB_HEADSET) { hi->usb_dev_status = STATUS_CONNECTED_ENABLED; - state &= ~(BIT_35MM_HEADSET | BIT_HEADSET); + state &= ~BIT_HEADSET; state |= BIT_HEADSET_NO_MIC; switch_set_state(&hi->sdev, state); mutex_unlock(&hi->mutex_lock); } else if (hi->usb_dev_type == H2W_TVOUT) { - state &= ~(BIT_HEADSET | BIT_35MM_HEADSET); + state &= ~BIT_HEADSET; state |= BIT_HEADSET_NO_MIC; switch_set_state(&hi->sdev, state); #if 0 @@ -388,8 +388,7 @@ static void remove_35mm_do_work(struct work_struct *work) HS_DELAY_ZERO_JIFFIES); #endif } else { - state &= ~(BIT_HEADSET | BIT_HEADSET_NO_MIC | - BIT_35MM_HEADSET); + state &= ~(BIT_HEADSET | BIT_HEADSET_NO_MIC); switch_set_state(&hi->sdev, state); } @@ -452,7 +451,7 @@ static void insert_35mm_do_work(struct work_struct *work) printk(KERN_INFO "3.5mm_headset with microphone\n"); } - state |= BIT_35MM_HEADSET; + switch_set_state(&hi->sdev, state); if (state & BIT_HEADSET_NO_MIC) hi->ext_35mm_status = HTC_35MM_NO_MIC; From 37315df6e9451c212baa45c6723eddf3c2d6d3c3 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 1 Jan 2012 04:02:46 -0600 Subject: [PATCH 1986/2556] msm: htc_35mm_jack: fix headphones for ICS (part 2) --- arch/arm/mach-msm/htc_35mm_jack.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-msm/htc_35mm_jack.c b/arch/arm/mach-msm/htc_35mm_jack.c index a956472789449..60d407a2f0a82 100644 --- a/arch/arm/mach-msm/htc_35mm_jack.c +++ b/arch/arm/mach-msm/htc_35mm_jack.c @@ -221,7 +221,6 @@ static void insert_35mm_do_work(struct work_struct *work) pr_info("3.5mm without microphone\n"); hi->ext_35mm_status = BIT_HEADSET_NO_MIC; } - hi->ext_35mm_status |= BIT_35MM_HEADSET; /* Notify framework via switch class */ mutex_lock(&hi->mutex_lock); From 258c6646bebb096a58caae26c2fa120cb5768566 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Tue, 13 Dec 2011 19:48:22 -0500 Subject: [PATCH 1987/2556] drivers: base: squashed commit: Add generic cross-process locking API Add a generic locking API for situations where multiple user-space processes and/or kernel drivers need to cooordinate access to a shared resource such as a graphics buffer. Change-Id: Ic0dedbad74b970d7bd1a6624a845b5b1b9847443 Signed-off-by: Jordan Crouse base: genlock: Properly destroy handle resources The resources allocated for a handle were not being freed on device release. This included releasing the lock. Unfortunately, the lock release had a fput() too many which was screwing up the reference counting on the file descriptors. When attaching a lock, we only need the file pointer for a short time so fput() it back immediately. That way, only there will only be one reference to the lock per process, and the process will be responsible for closing the fd directly and the fput() in genlock_release_lock is no longer needed. CRs-fixed: 322645 Change-Id: Ic0dedbad55300a2f8463c800cf8361719583b0b8 Signed-off-by: Jordan Crouse --- Documentation/genlock.txt | 161 ++++++++++ drivers/base/Kconfig | 15 + drivers/base/Makefile | 1 + drivers/base/genlock.c | 640 ++++++++++++++++++++++++++++++++++++++ include/linux/Kbuild | 1 + include/linux/genlock.h | 45 +++ 6 files changed, 863 insertions(+) create mode 100644 Documentation/genlock.txt create mode 100644 drivers/base/genlock.c create mode 100644 include/linux/genlock.h diff --git a/Documentation/genlock.txt b/Documentation/genlock.txt new file mode 100644 index 0000000000000..d3a44e2ffa725 --- /dev/null +++ b/Documentation/genlock.txt @@ -0,0 +1,161 @@ +Introduction + +'genlock' is an in-kernel API and optional userspace interface for a generic +cross-process locking mechanism. The API is designed for situations where +multiple user space processes and/or kernel drivers need to coordinate access +to a shared resource, such as a graphics buffer. The API was designed with +graphics buffers in mind, but is sufficiently generic to allow it to be +independently used with different types of resources. The chief advantage +of genlock over other cross-process locking mechanisms is that the resources +can be accessed by both userspace and kernel drivers which allows resources +to be locked or unlocked by asynchronous events in the kernel without the +intervention of user space. + +As an example, consider a graphics buffer that is shared between a rendering +application and a compositing window manager. The application renders into a +buffer. That buffer is reused by the compositing window manager as a texture. +To avoid corruption, access to the buffer needs to be restricted so that one +is not drawing on the surface while the other is reading. Locks can be +explicitly added between the rendering stages in the processes, but explicit +locks require that the application wait for rendering and purposely release the +lock. An implicit release triggered by an asynchronous event from the GPU +kernel driver, however, will let execution continue without requiring the +intercession of user space. + +SW Goals + +The genlock API implements exclusive write locks and shared read locks meaning +that there can only be one writer at a time, but multiple readers. Processes +that are unable to acquire a lock can be optionally blocked until the resource +becomes available. + +Locks are shared between processes. Each process will have its own private +instance for a lock known as a handle. Handles can be shared between user +space and kernel space to allow a kernel driver to unlock or lock a buffer +on behalf of a user process. + +Kernel API + +Access to the genlock API can either be via the in-kernel API or via an +optional character device (/dev/genlock). The character device is primarily +to be used for legacy resource sharing APIs that cannot be easily changed. +New resource sharing APIs from this point should implement a scheme specific +wrapper for locking. + +To create or attach to an existing lock, a process or kernel driver must first +create a handle. Each handle is linked to a single lock at any time. An entityi +may have multiple handles, each associated with a different lock. Once a handle +has been created, the owner may create a new lock or attach an existing lock +that has been exported from a different handle. + +Once the handle has a lock attached, the owning process may attempt to lock the +buffer for read or write. Write locks are exclusive, meaning that only one +process may acquire it at any given time. Read locks are shared, meaning that +multiple readers can hold the lock at the same time. Attempts to acquire a read +lock with a writer active or a write lock with one or more readers or writers +active will typically cause the process to block until the lock is acquired. +When the lock is released, all waiting processes will be woken up. Ownership +of the lock is reference counted, meaning that any one owner can "lock" +multiple times. The lock will only be released from the owner when all the +references to the lock are released via unlock. + +The owner of a write lock may atomically convert the lock into a read lock +(which will wake up other processes waiting for a read lock) without first +releasing the lock. The owner would simply issue a new request for a read lock. +However, the owner of a read lock cannot convert it into a write lock in the +same manner. To switch from a read lock to a write lock, the owner must +release the lock and then try to reacquire it. + +These are the in-kernel API calls that drivers can use to create and +manipulate handles and locks. Handles can either be created and managed +completely inside of kernel space, or shared from user space via a file +descriptor. + +* struct genlock_handle *genlock_get_handle(void) +Create a new handle. + +* struct genlock_handle * genlock_get_handle_fd(int fd) +Given a valid file descriptor, return the handle associated with that +descriptor. + +* void genlock_put_handle(struct genlock_handle *) +Release a handle. + +* struct genlock * genlock_create_lock(struct genlock_handle *) +Create a new lock and attach it to the handle. + +* struct genlock * genlock_attach_lock(struct genlock_handle *handle, int fd) +Given a valid file descriptor, get the lock associated with it and attach it to +the handle. + +* void genlock_release_lock(struct genlock_handle *) +Release a lock attached to a handle. + +* int genlock_lock(struct genlock_handle *, int op, int flags, u32 timeout) +Lock or unlock the lock attached to the handle. A zero timeout value will +be treated just like if the GENOCK_NOBLOCK flag is passed; if the lock +can be acquired without blocking then do so otherwise return -EAGAIN. +Function returns -ETIMEDOUT if the timeout expired or 0 if the lock was +acquired. + +* int genlock_wait(struct genloc_handle *, u32 timeout) +Wait for a lock held by the handle to go to the unlocked state. A non-zero +timeout value must be passed. Returns -ETIMEDOUT if the timeout expired or +0 if the lock is in an unlocked state. + +Character Device + +Opening an instance to the /dev/genlock character device will automatically +create a new handle. All ioctl functions with the exception of NEW and +RELEASE use the following parameter structure: + +struct genlock_lock { + int fd; /* Returned by EXPORT, used by ATTACH */ + int op; /* Used by LOCK */ + int flags; /* used by LOCK */ + u32 timeout; /* Used by LOCK and WAIT */ +} + +*GENLOCK_IOC_NEW +Create a new lock and attaches it to the handle. Returns -EINVAL if the handle +already has a lock attached (use GENLOCK_IOC_RELEASE to remove it). Returns +-ENOMEM if the memory for the lock can not be allocated. No data is passed +from the user for this ioctl. + +*GENLOCK_IOC_EXPORT +Export the currently attached lock to a file descriptor. The file descriptor +is returned in genlock_lock.fd. + +*GENLOCK_IOC_ATTACH +Attach an exported lock file descriptor to the current handle. Return -EINVAL +if the handle already has a lock attached (use GENLOCK_IOC_RELEASE to remove +it). Pass the file descriptor in genlock_lock.fd. + +*GENLOCK_IOC_LOCK +Lock or unlock the attached lock. Pass the desired operation in +genlock_lock.op: + * GENLOCK_WRLOCK - write lock + * GENLOCK_RDLOCK - read lock + * GENLOCK_UNLOCK - unlock an existing lock + +Pass flags in genlock_lock.flags: + * GENLOCK_NOBLOCK - Do not block if the lock is already taken + +Pass a timeout value in milliseconds in genlock_lock.timeout. +genlock_lock.flags and genlock_lock.timeout are not used for UNLOCK. +Returns -EINVAL if no lock is attached, -EAGAIN if the lock is taken and +NOBLOCK is specified or if the timeout value is zero, -ETIMEDOUT if the timeout +expires or 0 if the lock was successful. + +* GENLOCK_IOC_WAIT +Wait for the lock attached to the handle to be released (i.e. goes to unlock). +This is mainly used for a thread that needs to wait for a peer to release a +lock on the same shared handle. A non-zero timeout value in milliseconds is +passed in genlock_lock.timeout. Returns 0 when the lock has been released, +-EINVAL if a zero timeout is passed, or -ETIMEDOUT if the timeout expires. + +* GENLOCK_IOC_RELEASE +Use this to release an existing lock. This is useful if you wish to attach a +different lock to the same handle. You do not need to call this under normal +circumstances; when the handle is closed the reference to the lock is released. +No data is passed from the user for this ioctl. diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index d57e8d0fb8235..ad6fb3a3d98a9 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -168,4 +168,19 @@ config SYS_HYPERVISOR bool default n +config GENLOCK + bool "Enable a generic cross-process locking mechanism" + depends on ANON_INODES + help + Enable a generic cross-process locking API to provide protection + for shared memory objects such as graphics buffers. + +config GENLOCK_MISCDEVICE + bool "Enable a misc-device for userspace to access the genlock engine" + depends on GENLOCK + help + Create a miscdevice for the purposes of allowing userspace to create + and interact with locks created using genlock. + + endmenu diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 5f51c3b4451e2..d1bb577ebebb8 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_DEVTMPFS) += devtmpfs.o obj-y += power/ obj-$(CONFIG_HAS_DMA) += dma-mapping.o obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o +obj-$(CONFIG_GENLOCK) += genlock.o obj-$(CONFIG_ISA) += isa.o obj-$(CONFIG_FW_LOADER) += firmware_class.o obj-$(CONFIG_NUMA) += node.o diff --git a/drivers/base/genlock.c b/drivers/base/genlock.c new file mode 100644 index 0000000000000..afe8eb1c74d9a --- /dev/null +++ b/drivers/base/genlock.c @@ -0,0 +1,640 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for in_interrupt() */ + +/* Lock states - can either be unlocked, held as an exclusive write lock or a + * shared read lock + */ + +#define _UNLOCKED 0 +#define _RDLOCK GENLOCK_RDLOCK +#define _WRLOCK GENLOCK_WRLOCK + +struct genlock { + struct list_head active; /* List of handles holding lock */ + spinlock_t lock; /* Spinlock to protect the lock internals */ + wait_queue_head_t queue; /* Holding pen for processes pending lock */ + struct file *file; /* File structure for exported lock */ + int state; /* Current state of the lock */ + struct kref refcount; +}; + +struct genlock_handle { + struct genlock *lock; /* Lock currently attached to the handle */ + struct list_head entry; /* List node for attaching to a lock */ + struct file *file; /* File structure associated with handle */ + int active; /* Number of times the active lock has been + taken */ +}; + +static void genlock_destroy(struct kref *kref) +{ + struct genlock *lock = container_of(kref, struct genlock, + refcount); + + kfree(lock); +} + +/* + * Release the genlock object. Called when all the references to + * the genlock file descriptor are released + */ + +static int genlock_release(struct inode *inodep, struct file *file) +{ + return 0; +} + +static const struct file_operations genlock_fops = { + .release = genlock_release, +}; + +/** + * genlock_create_lock - Create a new lock + * @handle - genlock handle to attach the lock to + * + * Returns: a pointer to the genlock + */ + +struct genlock *genlock_create_lock(struct genlock_handle *handle) +{ + struct genlock *lock; + + if (handle->lock != NULL) + return ERR_PTR(-EINVAL); + + lock = kzalloc(sizeof(*lock), GFP_KERNEL); + if (lock == NULL) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&lock->active); + init_waitqueue_head(&lock->queue); + spin_lock_init(&lock->lock); + + lock->state = _UNLOCKED; + + /* + * Create an anonyonmous inode for the object that can exported to + * other processes + */ + + lock->file = anon_inode_getfile("genlock", &genlock_fops, + lock, O_RDWR); + + /* Attach the new lock to the handle */ + handle->lock = lock; + kref_init(&lock->refcount); + + return lock; +} +EXPORT_SYMBOL(genlock_create_lock); + +/* + * Get a file descriptor reference to a lock suitable for sharing with + * other processes + */ + +static int genlock_get_fd(struct genlock *lock) +{ + int ret; + + if (!lock->file) + return -EINVAL; + + ret = get_unused_fd_flags(0); + if (ret < 0) + return ret; + fd_install(ret, lock->file); + return ret; +} + +/** + * genlock_attach_lock - Attach an existing lock to a handle + * @handle - Pointer to a genlock handle to attach the lock to + * @fd - file descriptor for the exported lock + * + * Returns: A pointer to the attached lock structure + */ + +struct genlock *genlock_attach_lock(struct genlock_handle *handle, int fd) +{ + struct file *file; + struct genlock *lock; + + if (handle->lock != NULL) + return ERR_PTR(-EINVAL); + + file = fget(fd); + if (file == NULL) + return ERR_PTR(-EBADF); + + lock = file->private_data; + + fput(file); + + if (lock == NULL) + return ERR_PTR(-EINVAL); + + handle->lock = lock; + kref_get(&lock->refcount); + + return lock; +} +EXPORT_SYMBOL(genlock_attach_lock); + +/* Helper function that returns 1 if the specified handle holds the lock */ + +static int handle_has_lock(struct genlock *lock, struct genlock_handle *handle) +{ + struct genlock_handle *h; + + list_for_each_entry(h, &lock->active, entry) { + if (h == handle) + return 1; + } + + return 0; +} + +/* If the lock just became available, signal the next entity waiting for it */ + +static void _genlock_signal(struct genlock *lock) +{ + if (list_empty(&lock->active)) { + /* If the list is empty, then the lock is free */ + lock->state = _UNLOCKED; + /* Wake up the first process sitting in the queue */ + wake_up(&lock->queue); + } +} + +/* Attempt to release the handle's ownership of the lock */ + +static int _genlock_unlock(struct genlock *lock, struct genlock_handle *handle) +{ + int ret = -EINVAL; + unsigned long irqflags; + + spin_lock_irqsave(&lock->lock, irqflags); + + if (lock->state == _UNLOCKED) + goto done; + + /* Make sure this handle is an owner of the lock */ + if (!handle_has_lock(lock, handle)) + goto done; + + /* If the handle holds no more references to the lock then + release it (maybe) */ + + if (--handle->active == 0) { + list_del(&handle->entry); + _genlock_signal(lock); + } + + ret = 0; + +done: + spin_unlock_irqrestore(&lock->lock, irqflags); + return ret; +} + +/* Attempt to acquire the lock for the handle */ + +static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle, + int op, int flags, uint32_t timeout) +{ + unsigned long irqflags; + int ret = 0; + unsigned int ticks = msecs_to_jiffies(timeout); + + spin_lock_irqsave(&lock->lock, irqflags); + + /* Sanity check - no blocking locks in a debug context. Even if it + * succeed to not block, the mere idea is too dangerous to continue + */ + + if (in_interrupt() && !(flags & GENLOCK_NOBLOCK)) + BUG(); + + /* Fast path - the lock is unlocked, so go do the needful */ + + if (lock->state == _UNLOCKED) + goto dolock; + + if (handle_has_lock(lock, handle)) { + + /* + * If the handle already holds the lock and the type matches, + * then just increment the active pointer. This allows the + * handle to do recursive locks + */ + + if (lock->state == op) { + handle->active++; + goto done; + } + + /* + * If the handle holds a write lock then the owner can switch + * to a read lock if they want. Do the transition atomically + * then wake up any pending waiters in case they want a read + * lock too. + */ + + if (op == _RDLOCK && handle->active == 1) { + lock->state = _RDLOCK; + wake_up(&lock->queue); + goto done; + } + + /* + * Otherwise the user tried to turn a read into a write, and we + * don't allow that. + */ + + ret = -EINVAL; + goto done; + } + + /* + * If we request a read and the lock is held by a read, then go + * ahead and share the lock + */ + + if (op == GENLOCK_RDLOCK && lock->state == _RDLOCK) + goto dolock; + + /* Treat timeout 0 just like a NOBLOCK flag and return if the + lock cannot be aquired without blocking */ + + if (flags & GENLOCK_NOBLOCK || timeout == 0) { + ret = -EAGAIN; + goto done; + } + + /* Wait while the lock remains in an incompatible state */ + + while (lock->state != _UNLOCKED) { + unsigned int elapsed; + + spin_unlock_irqrestore(&lock->lock, irqflags); + + elapsed = wait_event_interruptible_timeout(lock->queue, + lock->state == _UNLOCKED, ticks); + + spin_lock_irqsave(&lock->lock, irqflags); + + if (elapsed <= 0) { + ret = (elapsed < 0) ? elapsed : -ETIMEDOUT; + goto done; + } + + ticks = elapsed; + } + +dolock: + /* We can now get the lock, add ourselves to the list of owners */ + + list_add_tail(&handle->entry, &lock->active); + lock->state = op; + handle->active = 1; + +done: + spin_unlock_irqrestore(&lock->lock, irqflags); + return ret; + +} + +/** + * genlock_lock - Acquire or release a lock + * @handle - pointer to the genlock handle that is requesting the lock + * @op - the operation to perform (RDLOCK, WRLOCK, UNLOCK) + * @flags - flags to control the operation + * @timeout - optional timeout to wait for the lock to come free + * + * Returns: 0 on success or error code on failure + */ + +int genlock_lock(struct genlock_handle *handle, int op, int flags, + uint32_t timeout) +{ + struct genlock *lock = handle->lock; + int ret = 0; + + if (lock == NULL) + return -EINVAL; + + switch (op) { + case GENLOCK_UNLOCK: + ret = _genlock_unlock(lock, handle); + break; + case GENLOCK_RDLOCK: + case GENLOCK_WRLOCK: + ret = _genlock_lock(lock, handle, op, flags, timeout); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} +EXPORT_SYMBOL(genlock_lock); + +/** + * genlock_wait - Wait for the lock to be released + * @handle - pointer to the genlock handle that is waiting for the lock + * @timeout - optional timeout to wait for the lock to get released + */ + +int genlock_wait(struct genlock_handle *handle, uint32_t timeout) +{ + struct genlock *lock = handle->lock; + unsigned long irqflags; + int ret = 0; + unsigned int ticks = msecs_to_jiffies(timeout); + + if (lock == NULL) + return -EINVAL; + + spin_lock_irqsave(&lock->lock, irqflags); + + /* + * if timeout is 0 and the lock is already unlocked, then success + * otherwise return -EAGAIN + */ + + if (timeout == 0) { + ret = (lock->state == _UNLOCKED) ? 0 : -EAGAIN; + goto done; + } + + while (lock->state != _UNLOCKED) { + unsigned int elapsed; + + spin_unlock_irqrestore(&lock->lock, irqflags); + + elapsed = wait_event_interruptible_timeout(lock->queue, + lock->state == _UNLOCKED, ticks); + + spin_lock_irqsave(&lock->lock, irqflags); + + if (elapsed <= 0) { + ret = (elapsed < 0) ? elapsed : -ETIMEDOUT; + break; + } + + ticks = elapsed; + } + +done: + spin_unlock_irqrestore(&lock->lock, irqflags); + return ret; +} + +/** + * genlock_release_lock - Release a lock attached to a handle + * @handle - Pointer to the handle holding the lock + */ + +void genlock_release_lock(struct genlock_handle *handle) +{ + unsigned long flags; + + if (handle == NULL || handle->lock == NULL) + return; + + spin_lock_irqsave(&handle->lock->lock, flags); + + /* If the handle is holding the lock, then force it closed */ + + if (handle_has_lock(handle->lock, handle)) { + list_del(&handle->entry); + _genlock_signal(handle->lock); + } + spin_unlock_irqrestore(&handle->lock->lock, flags); + + kref_put(&handle->lock->refcount, genlock_destroy); + handle->lock = NULL; + handle->active = 0; +} +EXPORT_SYMBOL(genlock_release_lock); + +/* + * Release function called when all references to a handle are released + */ + +static int genlock_handle_release(struct inode *inodep, struct file *file) +{ + struct genlock_handle *handle = file->private_data; + + genlock_release_lock(handle); + kfree(handle); + + return 0; +} + +static const struct file_operations genlock_handle_fops = { + .release = genlock_handle_release +}; + +/* + * Allocate a new genlock handle + */ + +static struct genlock_handle *_genlock_get_handle(void) +{ + struct genlock_handle *handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (handle == NULL) + return ERR_PTR(-ENOMEM); + + return handle; +} + +/** + * genlock_get_handle - Create a new genlock handle + * + * Returns: A pointer to a new genlock handle + */ + +struct genlock_handle *genlock_get_handle(void) +{ + struct genlock_handle *handle = _genlock_get_handle(); + if (IS_ERR(handle)) + return handle; + + handle->file = anon_inode_getfile("genlock-handle", + &genlock_handle_fops, handle, O_RDWR); + + return handle; +} +EXPORT_SYMBOL(genlock_get_handle); + +/** + * genlock_put_handle - release a reference to a genlock handle + * @handle - A pointer to the handle to release + */ + +void genlock_put_handle(struct genlock_handle *handle) +{ + if (handle) + fput(handle->file); +} +EXPORT_SYMBOL(genlock_put_handle); + +/** + * genlock_get_handle_fd - Get a handle reference from a file descriptor + * @fd - The file descriptor for a genlock handle + */ + +struct genlock_handle *genlock_get_handle_fd(int fd) +{ + struct file *file = fget(fd); + + if (file == NULL) + return ERR_PTR(-EINVAL); + + return file->private_data; +} +EXPORT_SYMBOL(genlock_get_handle_fd); + +#ifdef CONFIG_GENLOCK_MISCDEVICE + +static long genlock_dev_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct genlock_lock param; + struct genlock_handle *handle = filep->private_data; + struct genlock *lock; + int ret; + + switch (cmd) { + case GENLOCK_IOC_NEW: { + lock = genlock_create_lock(handle); + if (IS_ERR(lock)) + return PTR_ERR(lock); + + return 0; + } + case GENLOCK_IOC_EXPORT: { + if (handle->lock == NULL) + return -EINVAL; + + ret = genlock_get_fd(handle->lock); + if (ret < 0) + return ret; + + param.fd = ret; + + if (copy_to_user((void __user *) arg, ¶m, + sizeof(param))) + return -EFAULT; + + return 0; + } + case GENLOCK_IOC_ATTACH: { + if (copy_from_user(¶m, (void __user *) arg, + sizeof(param))) + return -EFAULT; + + lock = genlock_attach_lock(handle, param.fd); + if (IS_ERR(lock)) + return PTR_ERR(lock); + + return 0; + } + case GENLOCK_IOC_LOCK: { + if (copy_from_user(¶m, (void __user *) arg, + sizeof(param))) + return -EFAULT; + + return genlock_lock(handle, param.op, param.flags, + param.timeout); + } + case GENLOCK_IOC_WAIT: { + if (copy_from_user(¶m, (void __user *) arg, + sizeof(param))) + return -EFAULT; + + return genlock_wait(handle, param.timeout); + } + case GENLOCK_IOC_RELEASE: { + genlock_release_lock(handle); + return 0; + } + default: + return -EINVAL; + } +} + +static int genlock_dev_release(struct inode *inodep, struct file *file) +{ + struct genlock_handle *handle = file->private_data; + + genlock_release_lock(handle); + kfree(handle); + + return 0; +} + +static int genlock_dev_open(struct inode *inodep, struct file *file) +{ + struct genlock_handle *handle = _genlock_get_handle(); + if (IS_ERR(handle)) + return PTR_ERR(handle); + + handle->file = file; + file->private_data = handle; + return 0; +} + +static const struct file_operations genlock_dev_fops = { + .open = genlock_dev_open, + .release = genlock_dev_release, + .unlocked_ioctl = genlock_dev_ioctl, +}; + +static struct miscdevice genlock_dev; + +static int genlock_dev_init(void) +{ + genlock_dev.minor = MISC_DYNAMIC_MINOR; + genlock_dev.name = "genlock"; + genlock_dev.fops = &genlock_dev_fops; + genlock_dev.parent = NULL; + + return misc_register(&genlock_dev); +} + +static void genlock_dev_close(void) +{ + misc_deregister(&genlock_dev); +} + +module_init(genlock_dev_init); +module_exit(genlock_dev_close); + +#endif diff --git a/include/linux/Kbuild b/include/linux/Kbuild index b0ada6f37dd65..086e7eeb1d08f 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -17,6 +17,7 @@ header-y += netfilter_ipv4/ header-y += netfilter_ipv6/ header-y += usb/ header-y += wimax/ +header-y += genlock.h objhdr-y += version.h diff --git a/include/linux/genlock.h b/include/linux/genlock.h new file mode 100644 index 0000000000000..2e9f9d682a357 --- /dev/null +++ b/include/linux/genlock.h @@ -0,0 +1,45 @@ +#ifndef _GENLOCK_H_ +#define _GENLOCK_H_ + +#ifdef __KERNEL__ + +struct genlock; +struct genlock_handle; + +struct genlock_handle *genlock_get_handle(void); +struct genlock_handle *genlock_get_handle_fd(int fd); +void genlock_put_handle(struct genlock_handle *handle); +struct genlock *genlock_create_lock(struct genlock_handle *); +struct genlock *genlock_attach_lock(struct genlock_handle *, int fd); +int genlock_wait(struct genlock_handle *handle, u32 timeout); +void genlock_release_lock(struct genlock_handle *); +int genlock_lock(struct genlock_handle *handle, int op, int flags, + u32 timeout); +#endif + +#define GENLOCK_UNLOCK 0 +#define GENLOCK_WRLOCK 1 +#define GENLOCK_RDLOCK 2 + +#define GENLOCK_NOBLOCK (1 << 0) + +struct genlock_lock { + int fd; + int op; + int flags; + int timeout; +}; + +#define GENLOCK_IOC_MAGIC 'G' + +#define GENLOCK_IOC_NEW _IO(GENLOCK_IOC_MAGIC, 0) +#define GENLOCK_IOC_EXPORT _IOR(GENLOCK_IOC_MAGIC, 1, \ + struct genlock_lock) +#define GENLOCK_IOC_ATTACH _IOW(GENLOCK_IOC_MAGIC, 2, \ + struct genlock_lock) +#define GENLOCK_IOC_LOCK _IOW(GENLOCK_IOC_MAGIC, 3, \ + struct genlock_lock) +#define GENLOCK_IOC_RELEASE _IO(GENLOCK_IOC_MAGIC, 4) +#define GENLOCK_IOC_WAIT _IOW(GENLOCK_IOC_MAGIC, 5, \ + struct genlock_lock) +#endif From f33fddcdec28c6aef1c79738d1e03844284c7284 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sat, 7 Jan 2012 22:34:59 -0500 Subject: [PATCH 1988/2556] configs: update configs for new features --- arch/arm/configs/cyanogen_bravo_defconfig | 47 +++++++++++++--- arch/arm/configs/cyanogen_bravoc_defconfig | 47 +++++++++++++--- .../configs/cyanogen_incrediblec_defconfig | 49 ++++++++++++++--- arch/arm/configs/cyanogen_mahimahi_defconfig | 53 +++++++++++++++---- .../arm/configs/cyanogen_supersonic_defconfig | 49 ++++++++++++++--- 5 files changed, 205 insertions(+), 40 deletions(-) diff --git a/arch/arm/configs/cyanogen_bravo_defconfig b/arch/arm/configs/cyanogen_bravo_defconfig index eee6d6f32fd26..1df9b7fb3ad8d 100644 --- a/arch/arm/configs/cyanogen_bravo_defconfig +++ b/arch/arm/configs/cyanogen_bravo_defconfig @@ -604,6 +604,7 @@ CONFIG_NF_CONNTRACK_SANE=y CONFIG_NF_CONNTRACK_SIP=y CONFIG_NF_CONNTRACK_TFTP=y CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y CONFIG_NETFILTER_XTABLES=y # @@ -615,16 +616,24 @@ CONFIG_NETFILTER_XT_CONNMARK=y # # Xtables targets # +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NETFILTER_XT_TARGET_HL=y # CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set # CONFIG_NETFILTER_XT_TARGET_RATEEST is not set # CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set # CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set # # Xtables matches @@ -649,15 +658,19 @@ CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y # CONFIG_NETFILTER_XT_MATCH_OSF is not set -CONFIG_NETFILTER_XT_MATCH_OWNER=y +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set CONFIG_NETFILTER_XT_MATCH_POLICY=y # CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y # CONFIG_NETFILTER_XT_MATCH_RATEEST is not set # CONFIG_NETFILTER_XT_MATCH_REALM is not set -# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +CONFIG_NETFILTER_XT_MATCH_RECENT=y # CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y @@ -699,8 +712,11 @@ CONFIG_NF_NAT_AMANDA=y CONFIG_NF_NAT_PPTP=y CONFIG_NF_NAT_H323=y CONFIG_NF_NAT_SIP=y -# CONFIG_IP_NF_MANGLE is not set -# CONFIG_IP_NF_RAW is not set +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y @@ -708,10 +724,24 @@ CONFIG_IP_NF_ARP_MANGLE=y # # IPv6: Netfilter Configuration # -# CONFIG_NF_DEFRAG_IPV6 is not set -# CONFIG_NF_CONNTRACK_IPV6 is not set +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK_IPV6=y # CONFIG_IP6_NF_QUEUE is not set -# CONFIG_IP6_NF_IPTABLES is not set +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_TARGET_HL=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +# CONFIG_IP6_NF_RAW is not set # CONFIG_BRIDGE_NF_EBTABLES is not set # CONFIG_IP_DCCP is not set # CONFIG_IP_SCTP is not set @@ -862,6 +892,8 @@ CONFIG_EXTRA_FIRMWARE="" # CONFIG_DEBUG_DRIVER is not set # CONFIG_DEBUG_DEVRES is not set # CONFIG_SYS_HYPERVISOR is not set +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set @@ -1505,6 +1537,7 @@ CONFIG_USB_ANDROID_MASS_STORAGE=y CONFIG_USB_ANDROID_RNDIS=y CONFIG_USB_ANDROID_RNDIS_WCEIS=y CONFIG_USB_ANDROID_ACCESSORY=y +CONFIG_USB_CSW_HACK=y # CONFIG_USB_CDC_COMPOSITE is not set # CONFIG_USB_G_MULTI is not set # CONFIG_USB_G_HID is not set diff --git a/arch/arm/configs/cyanogen_bravoc_defconfig b/arch/arm/configs/cyanogen_bravoc_defconfig index 4a7c41ad36e45..c688a13ec9b2e 100644 --- a/arch/arm/configs/cyanogen_bravoc_defconfig +++ b/arch/arm/configs/cyanogen_bravoc_defconfig @@ -604,6 +604,7 @@ CONFIG_NF_CONNTRACK_SANE=y CONFIG_NF_CONNTRACK_SIP=y CONFIG_NF_CONNTRACK_TFTP=y CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y CONFIG_NETFILTER_XTABLES=y # @@ -615,16 +616,24 @@ CONFIG_NETFILTER_XT_CONNMARK=y # # Xtables targets # +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NETFILTER_XT_TARGET_HL=y # CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set # CONFIG_NETFILTER_XT_TARGET_RATEEST is not set # CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set # CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set # # Xtables matches @@ -649,15 +658,19 @@ CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y # CONFIG_NETFILTER_XT_MATCH_OSF is not set -CONFIG_NETFILTER_XT_MATCH_OWNER=y +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set CONFIG_NETFILTER_XT_MATCH_POLICY=y # CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y # CONFIG_NETFILTER_XT_MATCH_RATEEST is not set # CONFIG_NETFILTER_XT_MATCH_REALM is not set -# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +CONFIG_NETFILTER_XT_MATCH_RECENT=y # CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y @@ -699,8 +712,11 @@ CONFIG_NF_NAT_AMANDA=y CONFIG_NF_NAT_PPTP=y CONFIG_NF_NAT_H323=y CONFIG_NF_NAT_SIP=y -# CONFIG_IP_NF_MANGLE is not set -# CONFIG_IP_NF_RAW is not set +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y @@ -708,10 +724,24 @@ CONFIG_IP_NF_ARP_MANGLE=y # # IPv6: Netfilter Configuration # -# CONFIG_NF_DEFRAG_IPV6 is not set -# CONFIG_NF_CONNTRACK_IPV6 is not set +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK_IPV6=y # CONFIG_IP6_NF_QUEUE is not set -# CONFIG_IP6_NF_IPTABLES is not set +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_TARGET_HL=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +# CONFIG_IP6_NF_RAW is not set # CONFIG_BRIDGE_NF_EBTABLES is not set # CONFIG_IP_DCCP is not set # CONFIG_IP_SCTP is not set @@ -862,6 +892,8 @@ CONFIG_EXTRA_FIRMWARE="" # CONFIG_DEBUG_DRIVER is not set # CONFIG_DEBUG_DEVRES is not set # CONFIG_SYS_HYPERVISOR is not set +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set @@ -1505,6 +1537,7 @@ CONFIG_USB_ANDROID_MASS_STORAGE=y CONFIG_USB_ANDROID_RNDIS=y CONFIG_USB_ANDROID_RNDIS_WCEIS=y CONFIG_USB_ANDROID_ACCESSORY=y +CONFIG_USB_CSW_HACK=y # CONFIG_USB_CDC_COMPOSITE is not set # CONFIG_USB_G_MULTI is not set # CONFIG_USB_G_HID is not set diff --git a/arch/arm/configs/cyanogen_incrediblec_defconfig b/arch/arm/configs/cyanogen_incrediblec_defconfig index 632b30e769e58..9b8cf2e1a1c67 100644 --- a/arch/arm/configs/cyanogen_incrediblec_defconfig +++ b/arch/arm/configs/cyanogen_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux/arm 2.6.38.6 Kernel Configuration -# Sun May 15 15:45:45 2011 +# Linux/arm 2.6.38.8 Kernel Configuration +# Sat Jan 7 22:31:58 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -612,6 +612,7 @@ CONFIG_NF_CONNTRACK_SANE=y CONFIG_NF_CONNTRACK_SIP=y CONFIG_NF_CONNTRACK_TFTP=y CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y CONFIG_NETFILTER_XTABLES=y # @@ -623,16 +624,24 @@ CONFIG_NETFILTER_XT_CONNMARK=y # # Xtables targets # +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NETFILTER_XT_TARGET_HL=y # CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set # CONFIG_NETFILTER_XT_TARGET_RATEEST is not set # CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set # CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set # # Xtables matches @@ -657,15 +666,19 @@ CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y # CONFIG_NETFILTER_XT_MATCH_OSF is not set -CONFIG_NETFILTER_XT_MATCH_OWNER=y +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set CONFIG_NETFILTER_XT_MATCH_POLICY=y # CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y # CONFIG_NETFILTER_XT_MATCH_RATEEST is not set # CONFIG_NETFILTER_XT_MATCH_REALM is not set CONFIG_NETFILTER_XT_MATCH_RECENT=y # CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y @@ -707,8 +720,11 @@ CONFIG_NF_NAT_AMANDA=y CONFIG_NF_NAT_PPTP=y # CONFIG_NF_NAT_H323 is not set CONFIG_NF_NAT_SIP=y -# CONFIG_IP_NF_MANGLE is not set -# CONFIG_IP_NF_RAW is not set +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y @@ -716,10 +732,24 @@ CONFIG_IP_NF_ARP_MANGLE=y # # IPv6: Netfilter Configuration # -# CONFIG_NF_DEFRAG_IPV6 is not set -# CONFIG_NF_CONNTRACK_IPV6 is not set +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK_IPV6=y # CONFIG_IP6_NF_QUEUE is not set -# CONFIG_IP6_NF_IPTABLES is not set +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_TARGET_HL=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +# CONFIG_IP6_NF_RAW is not set # CONFIG_BRIDGE_NF_EBTABLES is not set # CONFIG_IP_DCCP is not set # CONFIG_IP_SCTP is not set @@ -871,6 +901,8 @@ CONFIG_EXTRA_FIRMWARE="" # CONFIG_DEBUG_DRIVER is not set # CONFIG_DEBUG_DEVRES is not set # CONFIG_SYS_HYPERVISOR is not set +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set @@ -1549,6 +1581,7 @@ CONFIG_USB_ANDROID_MASS_STORAGE=y CONFIG_USB_ANDROID_RNDIS=y # CONFIG_USB_ANDROID_RNDIS_WCEIS is not set # CONFIG_USB_ANDROID_ACCESSORY is not set +CONFIG_USB_CSW_HACK=y # CONFIG_USB_CDC_COMPOSITE is not set # CONFIG_USB_G_MULTI is not set # CONFIG_USB_G_HID is not set diff --git a/arch/arm/configs/cyanogen_mahimahi_defconfig b/arch/arm/configs/cyanogen_mahimahi_defconfig index debd4c7282dde..3d72f52e620b9 100644 --- a/arch/arm/configs/cyanogen_mahimahi_defconfig +++ b/arch/arm/configs/cyanogen_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux/arm 2.6.38.6 Kernel Configuration -# Sun May 15 15:47:00 2011 +# Linux/arm 2.6.38.8 Kernel Configuration +# Sat Jan 7 22:30:19 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -604,6 +604,7 @@ CONFIG_NF_CONNTRACK_SANE=y CONFIG_NF_CONNTRACK_SIP=y CONFIG_NF_CONNTRACK_TFTP=y CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y CONFIG_NETFILTER_XTABLES=y # @@ -615,16 +616,24 @@ CONFIG_NETFILTER_XT_CONNMARK=y # # Xtables targets # +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NETFILTER_XT_TARGET_HL=y # CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set # CONFIG_NETFILTER_XT_TARGET_RATEEST is not set # CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set # CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set # # Xtables matches @@ -647,17 +656,21 @@ CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y -# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y # CONFIG_NETFILTER_XT_MATCH_OSF is not set -CONFIG_NETFILTER_XT_MATCH_OWNER=y +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set CONFIG_NETFILTER_XT_MATCH_POLICY=y # CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y # CONFIG_NETFILTER_XT_MATCH_RATEEST is not set # CONFIG_NETFILTER_XT_MATCH_REALM is not set -# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +CONFIG_NETFILTER_XT_MATCH_RECENT=y # CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y @@ -699,8 +712,11 @@ CONFIG_NF_NAT_AMANDA=y CONFIG_NF_NAT_PPTP=y CONFIG_NF_NAT_H323=y CONFIG_NF_NAT_SIP=y -# CONFIG_IP_NF_MANGLE is not set -# CONFIG_IP_NF_RAW is not set +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y @@ -708,10 +724,24 @@ CONFIG_IP_NF_ARP_MANGLE=y # # IPv6: Netfilter Configuration # -# CONFIG_NF_DEFRAG_IPV6 is not set -# CONFIG_NF_CONNTRACK_IPV6 is not set +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK_IPV6=y # CONFIG_IP6_NF_QUEUE is not set -# CONFIG_IP6_NF_IPTABLES is not set +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_TARGET_HL=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +# CONFIG_IP6_NF_RAW is not set # CONFIG_BRIDGE_NF_EBTABLES is not set # CONFIG_IP_DCCP is not set # CONFIG_IP_SCTP is not set @@ -862,6 +892,8 @@ CONFIG_EXTRA_FIRMWARE="" # CONFIG_DEBUG_DRIVER is not set # CONFIG_DEBUG_DEVRES is not set # CONFIG_SYS_HYPERVISOR is not set +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set @@ -1502,6 +1534,7 @@ CONFIG_USB_ANDROID_MASS_STORAGE=y CONFIG_USB_ANDROID_RNDIS=y CONFIG_USB_ANDROID_RNDIS_WCEIS=y CONFIG_USB_ANDROID_ACCESSORY=y +CONFIG_USB_CSW_HACK=y # CONFIG_USB_CDC_COMPOSITE is not set # CONFIG_USB_G_MULTI is not set # CONFIG_USB_G_HID is not set diff --git a/arch/arm/configs/cyanogen_supersonic_defconfig b/arch/arm/configs/cyanogen_supersonic_defconfig index b58a6ece7a390..b159d9c84ff4d 100644 --- a/arch/arm/configs/cyanogen_supersonic_defconfig +++ b/arch/arm/configs/cyanogen_supersonic_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux/arm 2.6.38.6 Kernel Configuration -# Sun May 15 15:49:12 2011 +# Linux/arm 2.6.38.8 Kernel Configuration +# Sat Jan 7 22:25:33 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -616,6 +616,7 @@ CONFIG_NF_CONNTRACK_SANE=y CONFIG_NF_CONNTRACK_SIP=y CONFIG_NF_CONNTRACK_TFTP=y CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y CONFIG_NETFILTER_XTABLES=y # @@ -627,16 +628,24 @@ CONFIG_NETFILTER_XT_CONNMARK=y # # Xtables targets # +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NETFILTER_XT_TARGET_HL=y # CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set # CONFIG_NETFILTER_XT_TARGET_RATEEST is not set # CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set # CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set # # Xtables matches @@ -661,15 +670,19 @@ CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y # CONFIG_NETFILTER_XT_MATCH_OSF is not set -CONFIG_NETFILTER_XT_MATCH_OWNER=y +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set CONFIG_NETFILTER_XT_MATCH_POLICY=y # CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y # CONFIG_NETFILTER_XT_MATCH_RATEEST is not set # CONFIG_NETFILTER_XT_MATCH_REALM is not set CONFIG_NETFILTER_XT_MATCH_RECENT=y # CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y @@ -711,8 +724,11 @@ CONFIG_NF_NAT_AMANDA=y CONFIG_NF_NAT_PPTP=y # CONFIG_NF_NAT_H323 is not set CONFIG_NF_NAT_SIP=y -# CONFIG_IP_NF_MANGLE is not set -# CONFIG_IP_NF_RAW is not set +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y @@ -720,10 +736,24 @@ CONFIG_IP_NF_ARP_MANGLE=y # # IPv6: Netfilter Configuration # -# CONFIG_NF_DEFRAG_IPV6 is not set -# CONFIG_NF_CONNTRACK_IPV6 is not set +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK_IPV6=y # CONFIG_IP6_NF_QUEUE is not set -# CONFIG_IP6_NF_IPTABLES is not set +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_TARGET_HL=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +# CONFIG_IP6_NF_RAW is not set # CONFIG_BRIDGE_NF_EBTABLES is not set # CONFIG_IP_DCCP is not set # CONFIG_IP_SCTP is not set @@ -878,6 +908,8 @@ CONFIG_EXTRA_FIRMWARE="" # CONFIG_DEBUG_DRIVER is not set # CONFIG_DEBUG_DEVRES is not set # CONFIG_SYS_HYPERVISOR is not set +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set @@ -1530,6 +1562,7 @@ CONFIG_USB_ANDROID_MASS_STORAGE=y CONFIG_USB_ANDROID_RNDIS=y CONFIG_USB_ANDROID_RNDIS_WCEIS=y CONFIG_USB_ANDROID_ACCESSORY=y +CONFIG_USB_CSW_HACK=y # CONFIG_USB_CDC_COMPOSITE is not set # CONFIG_USB_G_MULTI is not set # CONFIG_USB_G_HID is not set From 6062bcf4629c27ed5f2cb075b461353fa1d247b5 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 1 Feb 2012 00:05:40 -0600 Subject: [PATCH 1989/2556] Revert "net: wireless: bcm4329: Update example locale table" This reverts commit cbd54c694382d06ef68afbd0cf9d80ebfb7f8b8d. --- .../net/wireless/bcm4329/dhd_custom_gpio.c | 54 +++++-------------- 1 file changed, 14 insertions(+), 40 deletions(-) diff --git a/drivers/net/wireless/bcm4329/dhd_custom_gpio.c b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c index 8c4b186656007..5aafd13f6d305 100644 --- a/drivers/net/wireless/bcm4329/dhd_custom_gpio.c +++ b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c @@ -183,48 +183,22 @@ dhd_custom_get_mac_address(unsigned char *buf) const struct cntry_locales_custom translate_custom_table[] = { /* Table should be filled out based on custom platform regulatory requirement */ #ifdef EXAMPLE_TABLE - {"", "XY", 4}, /* universal */ + {"", "XY", 4} /* universal */ {"US", "US", 69}, /* input ISO "US" to : US regrev 69 */ {"CA", "US", 69}, /* input ISO "CA" to : US regrev 69 */ - {"EU", "EU", 5}, /* European union countries */ - {"AT", "EU", 5}, - {"BE", "EU", 5}, - {"BG", "EU", 5}, - {"CY", "EU", 5}, - {"CZ", "EU", 5}, - {"DK", "EU", 5}, - {"EE", "EU", 5}, - {"FI", "EU", 5}, - {"FR", "EU", 5}, - {"DE", "EU", 5}, - {"GR", "EU", 5}, - {"HU", "EU", 5}, - {"IE", "EU", 5}, - {"IT", "EU", 5}, - {"LV", "EU", 5}, - {"LI", "EU", 5}, - {"LT", "EU", 5}, - {"LU", "EU", 5}, - {"MT", "EU", 5}, - {"NL", "EU", 5}, - {"PL", "EU", 5}, - {"PT", "EU", 5}, - {"RO", "EU", 5}, - {"SK", "EU", 5}, - {"SI", "EU", 5}, - {"ES", "EU", 5}, - {"SE", "EU", 5}, - {"GB", "EU", 5}, /* input ISO "GB" to : EU regrev 05 */ - {"IL", "IL", 0}, - {"CH", "CH", 0}, - {"TR", "TR", 0}, - {"NO", "NO", 0}, - {"KR", "XY", 3}, - {"AU", "XY", 3}, - {"CN", "XY", 3}, /* input ISO "CN" to : XY regrev 03 */ - {"TW", "XY", 3}, - {"AR", "XY", 3}, - {"MX", "XY", 3} + {"EU", "EU", 5}, /* input ISO "EU" to : EU regrev 05 */ + {"FR", "EU", 5}, + {"DE", "EU", 5}, + {"IR", "EU", 5}, + {"UK", "EU", 5}, /* input ISO "UK" to : EU regrev 05 */ + {"KR", "XY", 3}, + {"AU", "XY", 3}, + {"CN", "XY", 3}, /* input ISO "CN" to : XY regrev 03 */ + {"HK", "XY", 3}, + {"TW", "XY", 3}, + {"BR", "XY", 3}, + {"MX", "XY", 3}, + {"AR", "XY", 3} #endif /* EXAMPLE_TABLE */ }; From af65d2caa4223f1bc2067470fc78bf115608c539 Mon Sep 17 00:00:00 2001 From: Dmitry ORNATSKYY Date: Thu, 2 Jun 2011 21:25:57 -0400 Subject: [PATCH 1990/2556] net: wireless: bcm4329: Update example locale table Signed-off-by: Dmitry Shmidt --- .../net/wireless/bcm4329/dhd_custom_gpio.c | 54 ++++++++++++++----- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/bcm4329/dhd_custom_gpio.c b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c index 5aafd13f6d305..2252031d9fd6c 100644 --- a/drivers/net/wireless/bcm4329/dhd_custom_gpio.c +++ b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c @@ -183,22 +183,48 @@ dhd_custom_get_mac_address(unsigned char *buf) const struct cntry_locales_custom translate_custom_table[] = { /* Table should be filled out based on custom platform regulatory requirement */ #ifdef EXAMPLE_TABLE - {"", "XY", 4} /* universal */ + {"", "XY", 4}, /* universal */ {"US", "US", 69}, /* input ISO "US" to : US regrev 69 */ {"CA", "US", 69}, /* input ISO "CA" to : US regrev 69 */ - {"EU", "EU", 5}, /* input ISO "EU" to : EU regrev 05 */ - {"FR", "EU", 5}, - {"DE", "EU", 5}, - {"IR", "EU", 5}, - {"UK", "EU", 5}, /* input ISO "UK" to : EU regrev 05 */ - {"KR", "XY", 3}, - {"AU", "XY", 3}, - {"CN", "XY", 3}, /* input ISO "CN" to : XY regrev 03 */ - {"HK", "XY", 3}, - {"TW", "XY", 3}, - {"BR", "XY", 3}, - {"MX", "XY", 3}, - {"AR", "XY", 3} + {"EU", "EU", 5}, /* European union countries */ + {"AT", "EU", 5}, + {"BE", "EU", 5}, + {"BG", "EU", 5}, + {"CY", "EU", 5}, + {"CZ", "EU", 5}, + {"DK", "EU", 5}, + {"EE", "EU", 5}, + {"FI", "EU", 5}, + {"FR", "EU", 5}, + {"DE", "EU", 5}, + {"GR", "EU", 5}, + {"HU", "EU", 5}, + {"IE", "EU", 5}, + {"IT", "EU", 5}, + {"LV", "EU", 5}, + {"LI", "EU", 5}, + {"LT", "EU", 5}, + {"LU", "EU", 5}, + {"MT", "EU", 5}, + {"NL", "EU", 5}, + {"PL", "EU", 5}, + {"PT", "EU", 5}, + {"RO", "EU", 5}, + {"SK", "EU", 5}, + {"SI", "EU", 5}, + {"ES", "EU", 5}, + {"SE", "EU", 5}, + {"GB", "EU", 5}, /* input ISO "GB" to : EU regrev 05 */ + {"IL", "IL", 0}, + {"CH", "CH", 0}, + {"TR", "TR", 0}, + {"NO", "NO", 0}, + {"KR", "XY", 3}, + {"AU", "XY", 3}, + {"CN", "XY", 3}, /* input ISO "CN" to : XY regrev 03 */ + {"TW", "XY", 3}, + {"AR", "XY", 3}, + {"MX", "XY", 3} #endif /* EXAMPLE_TABLE */ }; From b4b9875537b434ee6c0072f54d54191ed167f3ae Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Mon, 2 May 2011 19:55:36 -0400 Subject: [PATCH 1991/2556] Revert (Partial): net: wireless: bcm4329: newer country_code/custom country_code logic --- drivers/net/wireless/bcm4329/dhd.h | 2 +- drivers/net/wireless/bcm4329/dhd_common.c | 7 +- .../net/wireless/bcm4329/dhd_custom_gpio.c | 92 ------------------- drivers/net/wireless/bcm4329/dhd_linux.c | 8 +- .../net/wireless/bcm4329/include/wlioctl.h | 6 -- drivers/net/wireless/bcm4329/wl_iw.c | 60 ++++++++---- drivers/net/wireless/bcm4329/wl_iw.h | 9 +- 7 files changed, 49 insertions(+), 135 deletions(-) diff --git a/drivers/net/wireless/bcm4329/dhd.h b/drivers/net/wireless/bcm4329/dhd.h index 9aeca7e4f30ab..7cec5b6c5183d 100644 --- a/drivers/net/wireless/bcm4329/dhd.h +++ b/drivers/net/wireless/bcm4329/dhd.h @@ -165,7 +165,7 @@ typedef struct dhd_pub { char * pktfilter[100]; int pktfilter_count; - wl_country_t dhd_cspec; /* Current Locale info */ + uint8 country_code[WLC_CNTRY_BUF_SZ]; char eventmask[WL_EVENTING_MASK_LEN]; } dhd_pub_t; diff --git a/drivers/net/wireless/bcm4329/dhd_common.c b/drivers/net/wireless/bcm4329/dhd_common.c index e50da1414c9e7..0d6a06acfddbf 100644 --- a/drivers/net/wireless/bcm4329/dhd_common.c +++ b/drivers/net/wireless/bcm4329/dhd_common.c @@ -1363,10 +1363,9 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif /* SET_RANDOM_MAC_SOFTAP */ /* Set Country code */ - if (dhd->dhd_cspec.ccode[0] != 0) { - bcm_mkiovar("country", (char *)&dhd->dhd_cspec, \ - sizeof(wl_country_t), iovbuf, sizeof(iovbuf)); - if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf))) < 0) { + if (dhd->country_code[0] != 0) { + if (dhdcdc_set_ioctl(dhd, 0, WLC_SET_COUNTRY, + dhd->country_code, sizeof(dhd->country_code)) < 0) { DHD_ERROR(("%s: country code setting failed\n", __FUNCTION__)); } } diff --git a/drivers/net/wireless/bcm4329/dhd_custom_gpio.c b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c index 2252031d9fd6c..a1a4297f05162 100644 --- a/drivers/net/wireless/bcm4329/dhd_custom_gpio.c +++ b/drivers/net/wireless/bcm4329/dhd_custom_gpio.c @@ -178,95 +178,3 @@ dhd_custom_get_mac_address(unsigned char *buf) return ret; } #endif /* GET_CUSTOM_MAC_ENABLE */ - -/* Customized Locale table : OPTIONAL feature */ -const struct cntry_locales_custom translate_custom_table[] = { -/* Table should be filled out based on custom platform regulatory requirement */ -#ifdef EXAMPLE_TABLE - {"", "XY", 4}, /* universal */ - {"US", "US", 69}, /* input ISO "US" to : US regrev 69 */ - {"CA", "US", 69}, /* input ISO "CA" to : US regrev 69 */ - {"EU", "EU", 5}, /* European union countries */ - {"AT", "EU", 5}, - {"BE", "EU", 5}, - {"BG", "EU", 5}, - {"CY", "EU", 5}, - {"CZ", "EU", 5}, - {"DK", "EU", 5}, - {"EE", "EU", 5}, - {"FI", "EU", 5}, - {"FR", "EU", 5}, - {"DE", "EU", 5}, - {"GR", "EU", 5}, - {"HU", "EU", 5}, - {"IE", "EU", 5}, - {"IT", "EU", 5}, - {"LV", "EU", 5}, - {"LI", "EU", 5}, - {"LT", "EU", 5}, - {"LU", "EU", 5}, - {"MT", "EU", 5}, - {"NL", "EU", 5}, - {"PL", "EU", 5}, - {"PT", "EU", 5}, - {"RO", "EU", 5}, - {"SK", "EU", 5}, - {"SI", "EU", 5}, - {"ES", "EU", 5}, - {"SE", "EU", 5}, - {"GB", "EU", 5}, /* input ISO "GB" to : EU regrev 05 */ - {"IL", "IL", 0}, - {"CH", "CH", 0}, - {"TR", "TR", 0}, - {"NO", "NO", 0}, - {"KR", "XY", 3}, - {"AU", "XY", 3}, - {"CN", "XY", 3}, /* input ISO "CN" to : XY regrev 03 */ - {"TW", "XY", 3}, - {"AR", "XY", 3}, - {"MX", "XY", 3} -#endif /* EXAMPLE_TABLE */ -}; - - -/* Customized Locale convertor -* input : ISO 3166-1 country abbreviation -* output: customized cspec -*/ -void get_customized_country_code(char *country_iso_code, wl_country_t *cspec) -{ -#ifdef CUSTOMER_HW2 - struct cntry_locales_custom *cloc_ptr; - - if (!cspec) - return; - - cloc_ptr = wifi_get_country_code(country_iso_code); - if (cloc_ptr) { - strlcpy(cspec->ccode, cloc_ptr->custom_locale, WLC_CNTRY_BUF_SZ); - cspec->rev = cloc_ptr->custom_locale_rev; - } - return; -#else - int size, i; - - size = ARRAYSIZE(translate_custom_table); - - if (cspec == 0) - return; - - if (size == 0) - return; - - for (i = 0; i < size; i++) { - if (strcmp(country_iso_code, translate_custom_table[i].iso_abbrev) == 0) { - memcpy(cspec->ccode, translate_custom_table[i].custom_locale, WLC_CNTRY_BUF_SZ); - cspec->rev = translate_custom_table[i].custom_locale_rev; - return; - } - } - memcpy(cspec->ccode, translate_custom_table[0].custom_locale, WLC_CNTRY_BUF_SZ); - cspec->rev = translate_custom_table[0].custom_locale_rev; - return; -#endif -} diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index 4f719e8de1d2b..e17291f9698d3 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -3153,20 +3153,20 @@ int net_os_send_hang_message(struct net_device *dev) return ret; } -void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec) +void dhd_bus_country_set(struct net_device *dev, char *country_code) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); if (dhd && dhd->pub.up) - memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); + strncpy(dhd->pub.country_code, country_code, WLC_CNTRY_BUF_SZ); } char *dhd_bus_country_get(struct net_device *dev) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); - if (dhd && (dhd->pub.dhd_cspec.ccode[0] != 0)) - return dhd->pub.dhd_cspec.ccode; + if (dhd && (dhd->pub.country_code[0] != 0)) + return dhd->pub.country_code; return NULL; } diff --git a/drivers/net/wireless/bcm4329/include/wlioctl.h b/drivers/net/wireless/bcm4329/include/wlioctl.h index 00c61f10782f6..078484830dc78 100644 --- a/drivers/net/wireless/bcm4329/include/wlioctl.h +++ b/drivers/net/wireless/bcm4329/include/wlioctl.h @@ -254,12 +254,6 @@ typedef struct wl_join_params { #define WLC_CNTRY_BUF_SZ 4 -typedef struct wl_country { - char country_abbrev[WLC_CNTRY_BUF_SZ]; - int32 rev; - char ccode[WLC_CNTRY_BUF_SZ]; -} wl_country_t; - typedef enum sup_auth_status { WLC_SUP_DISCONNECTED = 0, diff --git a/drivers/net/wireless/bcm4329/wl_iw.c b/drivers/net/wireless/bcm4329/wl_iw.c index 0c417a47a12c3..9289aee034bf3 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.c +++ b/drivers/net/wireless/bcm4329/wl_iw.c @@ -647,6 +647,31 @@ wl_iw_get_macaddr( return error; } +static int +wl_iw_set_country_code(struct net_device *dev, char *ccode) +{ + char country_code[WLC_CNTRY_BUF_SZ]; + int ret = -1; + + WL_TRACE(("%s\n", __FUNCTION__)); + if (!ccode) + ccode = dhd_bus_country_get(dev); + strncpy(country_code, ccode, sizeof(country_code)); + if (ccode && (country_code[0] != 0)) { +#ifdef CONFIG_US_NON_DFS_CHANNELS_ONLY + if (use_non_dfs_channels && !strncmp(country_code, "US", 2)) + strncpy(country_code, "Q2", WLC_CNTRY_BUF_SZ); + if (!use_non_dfs_channels && !strncmp(country_code, "Q2", 2)) + strncpy(country_code, "US", WLC_CNTRY_BUF_SZ); +#endif + ret = dev_wlc_ioctl(dev, WLC_SET_COUNTRY, &country_code, sizeof(country_code)); + if (ret >= 0) { + WL_TRACE(("%s: set country %s OK\n", __FUNCTION__, country_code)); + dhd_bus_country_set(dev, &country_code[0]); + } + } + return ret; +} static int wl_iw_set_country( @@ -661,12 +686,9 @@ wl_iw_set_country( char *p = extra; int country_offset; int country_code_size; - wl_country_t cspec = {{0}, 0, {0}}; - char smbuf[WLC_IOCTL_SMLEN]; - cspec.rev = -1; + WL_TRACE(("%s\n", __FUNCTION__)); memset(country_code, 0, sizeof(country_code)); - memset(smbuf, 0, sizeof(smbuf)); country_offset = strcspn(extra, " "); country_code_size = strlen(extra) - country_offset; @@ -674,25 +696,15 @@ wl_iw_set_country( if (country_offset != 0) { strncpy(country_code, extra + country_offset +1, MIN(country_code_size, sizeof(country_code))); - - - memcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ); - memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ); - - get_customized_country_code((char *)&cspec.country_abbrev, &cspec); - - if ((error = dev_iw_iovar_setbuf(dev, "country", &cspec, \ - sizeof(cspec), smbuf, sizeof(smbuf))) >= 0) { + error = wl_iw_set_country_code(dev, country_code); + if (error >= 0) { p += snprintf(p, MAX_WX_STRING, "OK"); - WL_ERROR(("%s: set country for %s as %s rev %d is OK\n", \ - __FUNCTION__, country_code, cspec.ccode, cspec.rev)); - dhd_bus_country_set(dev, &cspec); + WL_TRACE(("%s: set country %s OK\n", __FUNCTION__, country_code)); goto exit; } } - WL_ERROR(("%s: set country for %s as %s rev %d failed\n", \ - __FUNCTION__, country_code, cspec.ccode, cspec.rev)); + WL_ERROR(("%s: set country %s failed code %d\n", __FUNCTION__, country_code, error)); p += snprintf(p, MAX_WX_STRING, "FAIL"); @@ -6414,8 +6426,16 @@ static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap) } if (strlen(ap->country_code)) { - WL_ERROR(("%s: Igonored: Country MUST be specified \ - COUNTRY command with \n", __FUNCTION__)); + int error = 0; + if ((error = dev_wlc_ioctl(dev, WLC_SET_COUNTRY, + ap->country_code, sizeof(ap->country_code))) >= 0) { + WL_SOFTAP(("%s: set country %s OK\n", + __FUNCTION__, ap->country_code)); + dhd_bus_country_set(dev, &ap->country_code[0]); + } else { + WL_ERROR(("%s: ERROR:%d setting country %s\n", + __FUNCTION__, error, ap->country_code)); + } } else { WL_SOFTAP(("%s: Country code is not specified," " will use Radio's default\n", diff --git a/drivers/net/wireless/bcm4329/wl_iw.h b/drivers/net/wireless/bcm4329/wl_iw.h index d94bdb4327233..e5eda6ad5cfb6 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.h +++ b/drivers/net/wireless/bcm4329/wl_iw.h @@ -62,12 +62,6 @@ typedef struct wl_iw_extra_params { int target_channel; } wl_iw_extra_params_t; -struct cntry_locales_custom { - char iso_abbrev[WLC_CNTRY_BUF_SZ]; - char custom_locale[WLC_CNTRY_BUF_SZ]; - int32 custom_locale_rev; -}; - #define WL_IW_RSSI_MINVAL -200 #define WL_IW_RSSI_NO_SIGNAL -91 #define WL_IW_RSSI_VERY_LOW -80 @@ -205,7 +199,7 @@ extern int net_os_set_suspend_disable(struct net_device *dev, int val); extern int net_os_set_suspend(struct net_device *dev, int val); extern int net_os_set_dtim_skip(struct net_device *dev, int val); extern int net_os_set_packet_filter(struct net_device *dev, int val); -extern void get_customized_country_code(char *country_iso_code, wl_country_t *cspec); +extern void dhd_bus_country_set(struct net_device *dev, char *country_code); extern char *dhd_bus_country_get(struct net_device *dev); extern int dhd_get_dtim_skip(dhd_pub_t *dhd); @@ -235,7 +229,6 @@ extern int dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, \ int nssid, ushort scan_fr, int pno_repeat, int pno_freq_expo_max); extern int dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled); extern int dhd_dev_get_pno_status(struct net_device *dev); -extern void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec); #define PNO_TLV_PREFIX 'S' #define PNO_TLV_VERSION '1' From 717f223365d616b16f2f8830bab1e17479d13326 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 3 Aug 2011 12:16:06 +0200 Subject: [PATCH 1992/2556] net: wireless: bcm4329: Remove obsolete file --- drivers/net/wireless/bcm4329/bcmspibrcm.c | 1726 --------------------- 1 file changed, 1726 deletions(-) delete mode 100644 drivers/net/wireless/bcm4329/bcmspibrcm.c diff --git a/drivers/net/wireless/bcm4329/bcmspibrcm.c b/drivers/net/wireless/bcm4329/bcmspibrcm.c deleted file mode 100644 index 0f131a40f4b8e..0000000000000 --- a/drivers/net/wireless/bcm4329/bcmspibrcm.c +++ /dev/null @@ -1,1726 +0,0 @@ -/* - * Broadcom BCMSDH to gSPI Protocol Conversion Layer - * - * Copyright (C) 2010, Broadcom Corporation - * All Rights Reserved. - * - * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation; - * the contents of this file may not be disclosed to third parties, copied - * or duplicated in any form, in whole or in part, without the prior - * written permission of Broadcom Corporation. - * - * $Id: bcmspibrcm.c,v 1.11.2.10.2.9.6.11 2009/05/21 13:21:57 Exp $ - */ - -#define HSMODE - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include /* bcmsdh to/from specific controller APIs */ -#include /* ioctl/iovars */ -#include - -#include - - -#include -#include - -#define F0_RESPONSE_DELAY 16 -#define F1_RESPONSE_DELAY 16 -#define F2_RESPONSE_DELAY F0_RESPONSE_DELAY - -#define CMDLEN 4 - -#define DWORDMODE_ON (sd->chip == BCM4329_CHIP_ID) && (sd->chiprev == 2) && (sd->dwordmode == TRUE) - -/* Globals */ -uint sd_msglevel = 0; - -uint sd_hiok = FALSE; /* Use hi-speed mode if available? */ -uint sd_sdmode = SDIOH_MODE_SPI; /* Use SD4 mode by default */ -uint sd_f2_blocksize = 64; /* Default blocksize */ - - -uint sd_divisor = 2; -uint sd_power = 1; /* Default to SD Slot powered ON */ -uint sd_clock = 1; /* Default to SD Clock turned ON */ -uint sd_crc = 0; /* Default to SPI CRC Check turned OFF */ - -uint8 spi_outbuf[SPI_MAX_PKT_LEN]; -uint8 spi_inbuf[SPI_MAX_PKT_LEN]; - -/* 128bytes buffer is enough to clear data-not-available and program response-delay F0 bits - * assuming we will not exceed F0 response delay > 100 bytes at 48MHz. - */ -#define BUF2_PKT_LEN 128 -uint8 spi_outbuf2[BUF2_PKT_LEN]; -uint8 spi_inbuf2[BUF2_PKT_LEN]; - -/* Prototypes */ -static bool bcmspi_test_card(sdioh_info_t *sd); -static bool bcmspi_host_device_init_adapt(sdioh_info_t *sd); -static int bcmspi_set_highspeed_mode(sdioh_info_t *sd, bool hsmode); -static int bcmspi_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd_arg, - uint32 *data, uint32 datalen); -static int bcmspi_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, - int regsize, uint32 *data); -static int bcmspi_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, - int regsize, uint32 data); -static int bcmspi_card_bytewrite(sdioh_info_t *sd, int func, uint32 regaddr, - uint8 *data); -static int bcmspi_driver_init(sdioh_info_t *sd); -static int bcmspi_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo, - uint32 addr, int nbytes, uint32 *data); -static int bcmspi_card_regread_fixedaddr(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, - uint32 *data); -static void bcmspi_cmd_getdstatus(sdioh_info_t *sd, uint32 *dstatus_buffer); -static int bcmspi_update_stats(sdioh_info_t *sd, uint32 cmd_arg); - -/* - * Public entry points & extern's - */ -extern sdioh_info_t * -sdioh_attach(osl_t *osh, void *bar0, uint irq) -{ - sdioh_info_t *sd; - - sd_trace(("%s\n", __FUNCTION__)); - if ((sd = (sdioh_info_t *)MALLOC(osh, sizeof(sdioh_info_t))) == NULL) { - sd_err(("%s: out of memory, malloced %d bytes\n", __FUNCTION__, MALLOCED(osh))); - return NULL; - } - bzero((char *)sd, sizeof(sdioh_info_t)); - sd->osh = osh; - if (spi_osinit(sd) != 0) { - sd_err(("%s: spi_osinit() failed\n", __FUNCTION__)); - MFREE(sd->osh, sd, sizeof(sdioh_info_t)); - return NULL; - } - - sd->bar0 = bar0; - sd->irq = irq; - sd->intr_handler = NULL; - sd->intr_handler_arg = NULL; - sd->intr_handler_valid = FALSE; - - /* Set defaults */ - sd->use_client_ints = TRUE; - sd->sd_use_dma = FALSE; /* DMA Not supported */ - - /* Spi device default is 16bit mode, change to 4 when device is changed to 32bit - * mode - */ - sd->wordlen = 2; - - if (!spi_hw_attach(sd)) { - sd_err(("%s: spi_hw_attach() failed\n", __FUNCTION__)); - spi_osfree(sd); - MFREE(sd->osh, sd, sizeof(sdioh_info_t)); - return (NULL); - } - - if (bcmspi_driver_init(sd) != SUCCESS) { - sd_err(("%s: bcmspi_driver_init() failed()\n", __FUNCTION__)); - spi_hw_detach(sd); - spi_osfree(sd); - MFREE(sd->osh, sd, sizeof(sdioh_info_t)); - return (NULL); - } - - if (spi_register_irq(sd, irq) != SUCCESS) { - sd_err(("%s: spi_register_irq() failed for irq = %d\n", __FUNCTION__, irq)); - spi_hw_detach(sd); - spi_osfree(sd); - MFREE(sd->osh, sd, sizeof(sdioh_info_t)); - return (NULL); - } - - sd_trace(("%s: Done\n", __FUNCTION__)); - - return sd; -} - -extern SDIOH_API_RC -sdioh_detach(osl_t *osh, sdioh_info_t *sd) -{ - sd_trace(("%s\n", __FUNCTION__)); - if (sd) { - sd_err(("%s: detaching from hardware\n", __FUNCTION__)); - spi_free_irq(sd->irq, sd); - spi_hw_detach(sd); - spi_osfree(sd); - MFREE(sd->osh, sd, sizeof(sdioh_info_t)); - } - return SDIOH_API_RC_SUCCESS; -} - -/* Configure callback to client when we recieve client interrupt */ -extern SDIOH_API_RC -sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh) -{ - sd_trace(("%s: Entering\n", __FUNCTION__)); - sd->intr_handler = fn; - sd->intr_handler_arg = argh; - sd->intr_handler_valid = TRUE; - return SDIOH_API_RC_SUCCESS; -} - -extern SDIOH_API_RC -sdioh_interrupt_deregister(sdioh_info_t *sd) -{ - sd_trace(("%s: Entering\n", __FUNCTION__)); - sd->intr_handler_valid = FALSE; - sd->intr_handler = NULL; - sd->intr_handler_arg = NULL; - return SDIOH_API_RC_SUCCESS; -} - -extern SDIOH_API_RC -sdioh_interrupt_query(sdioh_info_t *sd, bool *onoff) -{ - sd_trace(("%s: Entering\n", __FUNCTION__)); - *onoff = sd->client_intr_enabled; - return SDIOH_API_RC_SUCCESS; -} - -#if defined(DHD_DEBUG) -extern bool -sdioh_interrupt_pending(sdioh_info_t *sd) -{ - return 0; -} -#endif - -extern SDIOH_API_RC -sdioh_query_device(sdioh_info_t *sd) -{ - /* Return a BRCM ID appropriate to the dongle class */ - return (sd->num_funcs > 1) ? BCM4329_D11NDUAL_ID : BCM4318_D11G_ID; -} - -/* Provide dstatus bits of spi-transaction for dhd layers. */ -extern uint32 -sdioh_get_dstatus(sdioh_info_t *sd) -{ - return sd->card_dstatus; -} - -extern void -sdioh_chipinfo(sdioh_info_t *sd, uint32 chip, uint32 chiprev) -{ - sd->chip = chip; - sd->chiprev = chiprev; -} - -extern void -sdioh_dwordmode(sdioh_info_t *sd, bool set) -{ - uint8 reg = 0; - int status; - - if ((status = sdioh_request_byte(sd, SDIOH_READ, SPI_FUNC_0, SPID_STATUS_ENABLE, ®)) != - SUCCESS) { - sd_err(("%s: Failed to set dwordmode in gSPI\n", __FUNCTION__)); - return; - } - - if (set) { - reg |= DWORD_PKT_LEN_EN; - sd->dwordmode = TRUE; - sd->client_block_size[SPI_FUNC_2] = 4096; /* h2spi's limit is 4KB, we support 8KB */ - } else { - reg &= ~DWORD_PKT_LEN_EN; - sd->dwordmode = FALSE; - sd->client_block_size[SPI_FUNC_2] = 2048; - } - - if ((status = sdioh_request_byte(sd, SDIOH_WRITE, SPI_FUNC_0, SPID_STATUS_ENABLE, ®)) != - SUCCESS) { - sd_err(("%s: Failed to set dwordmode in gSPI\n", __FUNCTION__)); - return; - } -} - - -uint -sdioh_query_iofnum(sdioh_info_t *sd) -{ - return sd->num_funcs; -} - -/* IOVar table */ -enum { - IOV_MSGLEVEL = 1, - IOV_BLOCKMODE, - IOV_BLOCKSIZE, - IOV_DMA, - IOV_USEINTS, - IOV_NUMINTS, - IOV_NUMLOCALINTS, - IOV_HOSTREG, - IOV_DEVREG, - IOV_DIVISOR, - IOV_SDMODE, - IOV_HISPEED, - IOV_HCIREGS, - IOV_POWER, - IOV_CLOCK, - IOV_SPIERRSTATS, - IOV_RESP_DELAY_ALL -}; - -const bcm_iovar_t sdioh_iovars[] = { - {"sd_msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 }, - {"sd_blocksize", IOV_BLOCKSIZE, 0, IOVT_UINT32, 0 }, /* ((fn << 16) | size) */ - {"sd_dma", IOV_DMA, 0, IOVT_BOOL, 0 }, - {"sd_ints", IOV_USEINTS, 0, IOVT_BOOL, 0 }, - {"sd_numints", IOV_NUMINTS, 0, IOVT_UINT32, 0 }, - {"sd_numlocalints", IOV_NUMLOCALINTS, 0, IOVT_UINT32, 0 }, - {"sd_hostreg", IOV_HOSTREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, - {"sd_devreg", IOV_DEVREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, - {"sd_divisor", IOV_DIVISOR, 0, IOVT_UINT32, 0 }, - {"sd_power", IOV_POWER, 0, IOVT_UINT32, 0 }, - {"sd_clock", IOV_CLOCK, 0, IOVT_UINT32, 0 }, - {"sd_mode", IOV_SDMODE, 0, IOVT_UINT32, 100}, - {"sd_highspeed", IOV_HISPEED, 0, IOVT_UINT32, 0}, - {"spi_errstats", IOV_SPIERRSTATS, 0, IOVT_BUFFER, sizeof(struct spierrstats_t) }, - {"spi_respdelay", IOV_RESP_DELAY_ALL, 0, IOVT_BOOL, 0 }, - {NULL, 0, 0, 0, 0 } -}; - -int -sdioh_iovar_op(sdioh_info_t *si, const char *name, - void *params, int plen, void *arg, int len, bool set) -{ - const bcm_iovar_t *vi = NULL; - int bcmerror = 0; - int val_size; - int32 int_val = 0; - bool bool_val; - uint32 actionid; -/* - sdioh_regs_t *regs; -*/ - - ASSERT(name); - ASSERT(len >= 0); - - /* Get must have return space; Set does not take qualifiers */ - ASSERT(set || (arg && len)); - ASSERT(!set || (!params && !plen)); - - sd_trace(("%s: Enter (%s %s)\n", __FUNCTION__, (set ? "set" : "get"), name)); - - if ((vi = bcm_iovar_lookup(sdioh_iovars, name)) == NULL) { - bcmerror = BCME_UNSUPPORTED; - goto exit; - } - - if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, set)) != 0) - goto exit; - - /* Set up params so get and set can share the convenience variables */ - if (params == NULL) { - params = arg; - plen = len; - } - - if (vi->type == IOVT_VOID) - val_size = 0; - else if (vi->type == IOVT_BUFFER) - val_size = len; - else - val_size = sizeof(int); - - if (plen >= (int)sizeof(int_val)) - bcopy(params, &int_val, sizeof(int_val)); - - bool_val = (int_val != 0) ? TRUE : FALSE; - - actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid); - switch (actionid) { - case IOV_GVAL(IOV_MSGLEVEL): - int_val = (int32)sd_msglevel; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_MSGLEVEL): - sd_msglevel = int_val; - break; - - case IOV_GVAL(IOV_BLOCKSIZE): - if ((uint32)int_val > si->num_funcs) { - bcmerror = BCME_BADARG; - break; - } - int_val = (int32)si->client_block_size[int_val]; - bcopy(&int_val, arg, val_size); - break; - - case IOV_GVAL(IOV_DMA): - int_val = (int32)si->sd_use_dma; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_DMA): - si->sd_use_dma = (bool)int_val; - break; - - case IOV_GVAL(IOV_USEINTS): - int_val = (int32)si->use_client_ints; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_USEINTS): - break; - - case IOV_GVAL(IOV_DIVISOR): - int_val = (uint32)sd_divisor; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_DIVISOR): - sd_divisor = int_val; - if (!spi_start_clock(si, (uint16)sd_divisor)) { - sd_err(("%s: set clock failed\n", __FUNCTION__)); - bcmerror = BCME_ERROR; - } - break; - - case IOV_GVAL(IOV_POWER): - int_val = (uint32)sd_power; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_POWER): - sd_power = int_val; - break; - - case IOV_GVAL(IOV_CLOCK): - int_val = (uint32)sd_clock; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_CLOCK): - sd_clock = int_val; - break; - - case IOV_GVAL(IOV_SDMODE): - int_val = (uint32)sd_sdmode; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_SDMODE): - sd_sdmode = int_val; - break; - - case IOV_GVAL(IOV_HISPEED): - int_val = (uint32)sd_hiok; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_HISPEED): - sd_hiok = int_val; - - if (!bcmspi_set_highspeed_mode(si, (bool)sd_hiok)) { - sd_err(("%s: Failed changing highspeed mode to %d.\n", - __FUNCTION__, sd_hiok)); - bcmerror = BCME_ERROR; - return ERROR; - } - break; - - case IOV_GVAL(IOV_NUMINTS): - int_val = (int32)si->intrcount; - bcopy(&int_val, arg, val_size); - break; - - case IOV_GVAL(IOV_NUMLOCALINTS): - int_val = (int32)si->local_intrcount; - bcopy(&int_val, arg, val_size); - break; - case IOV_GVAL(IOV_DEVREG): - { - sdreg_t *sd_ptr = (sdreg_t *)params; - uint8 data; - - if (sdioh_cfg_read(si, sd_ptr->func, sd_ptr->offset, &data)) { - bcmerror = BCME_SDIO_ERROR; - break; - } - - int_val = (int)data; - bcopy(&int_val, arg, sizeof(int_val)); - break; - } - - case IOV_SVAL(IOV_DEVREG): - { - sdreg_t *sd_ptr = (sdreg_t *)params; - uint8 data = (uint8)sd_ptr->value; - - if (sdioh_cfg_write(si, sd_ptr->func, sd_ptr->offset, &data)) { - bcmerror = BCME_SDIO_ERROR; - break; - } - break; - } - - - case IOV_GVAL(IOV_SPIERRSTATS): - { - bcopy(&si->spierrstats, arg, sizeof(struct spierrstats_t)); - break; - } - - case IOV_SVAL(IOV_SPIERRSTATS): - { - bzero(&si->spierrstats, sizeof(struct spierrstats_t)); - break; - } - - case IOV_GVAL(IOV_RESP_DELAY_ALL): - int_val = (int32)si->resp_delay_all; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_RESP_DELAY_ALL): - si->resp_delay_all = (bool)int_val; - int_val = STATUS_ENABLE|INTR_WITH_STATUS; - if (si->resp_delay_all) - int_val |= RESP_DELAY_ALL; - else { - if (bcmspi_card_regwrite(si, SPI_FUNC_0, SPID_RESPONSE_DELAY, 1, - F1_RESPONSE_DELAY) != SUCCESS) { - sd_err(("%s: Unable to set response delay.\n", __FUNCTION__)); - bcmerror = BCME_SDIO_ERROR; - break; - } - } - - if (bcmspi_card_regwrite(si, SPI_FUNC_0, SPID_STATUS_ENABLE, 1, int_val) - != SUCCESS) { - sd_err(("%s: Unable to set response delay.\n", __FUNCTION__)); - bcmerror = BCME_SDIO_ERROR; - break; - } - break; - - default: - bcmerror = BCME_UNSUPPORTED; - break; - } -exit: - - return bcmerror; -} - -extern SDIOH_API_RC -sdioh_cfg_read(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data) -{ - SDIOH_API_RC status; - /* No lock needed since sdioh_request_byte does locking */ - status = sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data); - return status; -} - -extern SDIOH_API_RC -sdioh_cfg_write(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data) -{ - /* No lock needed since sdioh_request_byte does locking */ - SDIOH_API_RC status; - - if ((fnc_num == SPI_FUNC_1) && (addr == SBSDIO_FUNC1_FRAMECTRL)) { - uint8 dummy_data; - status = sdioh_cfg_read(sd, fnc_num, addr, &dummy_data); - if (status) { - sd_err(("sdioh_cfg_read() failed.\n")); - return status; - } - } - - status = sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data); - return status; -} - -extern SDIOH_API_RC -sdioh_cis_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 length) -{ - uint32 count; - int offset; - uint32 cis_byte; - uint16 *cis = (uint16 *)cisd; - uint bar0 = SI_ENUM_BASE; - int status; - uint8 data; - - sd_trace(("%s: Func %d\n", __FUNCTION__, func)); - - spi_lock(sd); - - /* Set sb window address to 0x18000000 */ - data = (bar0 >> 8) & SBSDIO_SBADDRLOW_MASK; - status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW, &data); - if (status == SUCCESS) { - data = (bar0 >> 16) & SBSDIO_SBADDRMID_MASK; - status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID, &data); - } else { - sd_err(("%s: Unable to set sb-addr-windows\n", __FUNCTION__)); - spi_unlock(sd); - return (BCME_ERROR); - } - if (status == SUCCESS) { - data = (bar0 >> 24) & SBSDIO_SBADDRHIGH_MASK; - status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH, &data); - } else { - sd_err(("%s: Unable to set sb-addr-windows\n", __FUNCTION__)); - spi_unlock(sd); - return (BCME_ERROR); - } - - offset = CC_OTP; /* OTP offset in chipcommon. */ - for (count = 0; count < length/2; count++) { - if (bcmspi_card_regread (sd, SDIO_FUNC_1, offset, 2, &cis_byte) < 0) { - sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__)); - spi_unlock(sd); - return (BCME_ERROR); - } - - *cis = (uint16)cis_byte; - cis++; - offset += 2; - } - - spi_unlock(sd); - - return (BCME_OK); -} - -extern SDIOH_API_RC -sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *byte) -{ - int status; - uint32 cmd_arg; - uint32 dstatus; - uint32 data = (uint32)(*byte); - - spi_lock(sd); - - cmd_arg = 0; - cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); - cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */ - cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); - cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, rw == SDIOH_READ ? 0 : 1); - cmd_arg = SFIELD(cmd_arg, SPI_LEN, 1); - - sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); - sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, rw, func, - regaddr, data)); - - if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, - cmd_arg, &data, 1)) != SUCCESS) { - spi_unlock(sd); - return status; - } - - if (rw == SDIOH_READ) - *byte = (uint8)data; - - bcmspi_cmd_getdstatus(sd, &dstatus); - if (dstatus) - sd_trace(("dstatus =0x%x\n", dstatus)); - - spi_unlock(sd); - return SDIOH_API_RC_SUCCESS; -} - -extern SDIOH_API_RC -sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr, - uint32 *word, uint nbytes) -{ - int status; - - spi_lock(sd); - - if (rw == SDIOH_READ) - status = bcmspi_card_regread(sd, func, addr, nbytes, word); - else - status = bcmspi_card_regwrite(sd, func, addr, nbytes, *word); - - spi_unlock(sd); - return (status == SUCCESS ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); -} - -extern SDIOH_API_RC -sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint rw, uint func, - uint addr, uint reg_width, uint buflen_u, uint8 *buffer, void *pkt) -{ - int len; - int buflen = (int)buflen_u; - bool fifo = (fix_inc == SDIOH_DATA_FIX); - - spi_lock(sd); - - ASSERT(reg_width == 4); - ASSERT(buflen_u < (1 << 30)); - ASSERT(sd->client_block_size[func]); - - sd_data(("%s: %c len %d r_cnt %d t_cnt %d, pkt @0x%p\n", - __FUNCTION__, rw == SDIOH_READ ? 'R' : 'W', - buflen_u, sd->r_cnt, sd->t_cnt, pkt)); - - /* Break buffer down into blocksize chunks. */ - while (buflen > 0) { - len = MIN(sd->client_block_size[func], buflen); - if (bcmspi_card_buf(sd, rw, func, fifo, addr, len, (uint32 *)buffer) != SUCCESS) { - sd_err(("%s: bcmspi_card_buf %s failed\n", - __FUNCTION__, rw == SDIOH_READ ? "Read" : "Write")); - spi_unlock(sd); - return SDIOH_API_RC_FAIL; - } - buffer += len; - buflen -= len; - if (!fifo) - addr += len; - } - spi_unlock(sd); - return SDIOH_API_RC_SUCCESS; -} - -/* This function allows write to gspi bus when another rd/wr function is deep down the call stack. - * Its main aim is to have simpler spi writes rather than recursive writes. - * e.g. When there is a need to program response delay on the fly after detecting the SPI-func - * this call will allow to program the response delay. - */ -static int -bcmspi_card_byterewrite(sdioh_info_t *sd, int func, uint32 regaddr, uint8 byte) -{ - uint32 cmd_arg; - uint32 datalen = 1; - uint32 hostlen; - - cmd_arg = 0; - - cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1); - cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */ - cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); - cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); - cmd_arg = SFIELD(cmd_arg, SPI_LEN, datalen); - - sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); - - - /* Set up and issue the SPI command. MSByte goes out on bus first. Increase datalen - * according to the wordlen mode(16/32bit) the device is in. - */ - ASSERT(sd->wordlen == 4 || sd->wordlen == 2); - datalen = ROUNDUP(datalen, sd->wordlen); - - /* Start by copying command in the spi-outbuffer */ - if (sd->wordlen == 4) { /* 32bit spid */ - *(uint32 *)spi_outbuf2 = bcmswap32(cmd_arg); - if (datalen & 0x3) - datalen += (4 - (datalen & 0x3)); - } else if (sd->wordlen == 2) { /* 16bit spid */ - *(uint16 *)spi_outbuf2 = bcmswap16(cmd_arg & 0xffff); - *(uint16 *)&spi_outbuf2[2] = bcmswap16((cmd_arg & 0xffff0000) >> 16); - if (datalen & 0x1) - datalen++; - } else { - sd_err(("%s: Host is %d bit spid, could not create SPI command.\n", - __FUNCTION__, 8 * sd->wordlen)); - return ERROR; - } - - /* for Write, put the data into the output buffer */ - if (datalen != 0) { - if (sd->wordlen == 4) { /* 32bit spid */ - *(uint32 *)&spi_outbuf2[CMDLEN] = bcmswap32(byte); - } else if (sd->wordlen == 2) { /* 16bit spid */ - *(uint16 *)&spi_outbuf2[CMDLEN] = bcmswap16(byte & 0xffff); - *(uint16 *)&spi_outbuf2[CMDLEN + 2] = - bcmswap16((byte & 0xffff0000) >> 16); - } - } - - /* +4 for cmd, +4 for dstatus */ - hostlen = datalen + 8; - hostlen += (4 - (hostlen & 0x3)); - spi_sendrecv(sd, spi_outbuf2, spi_inbuf2, hostlen); - - /* Last 4bytes are dstatus. Device is configured to return status bits. */ - if (sd->wordlen == 4) { /* 32bit spid */ - sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf2[datalen + CMDLEN ]); - } else if (sd->wordlen == 2) { /* 16bit spid */ - sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN ]) | - (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN + 2]) << 16)); - } else { - sd_err(("%s: Host is %d bit machine, could not read SPI dstatus.\n", - __FUNCTION__, 8 * sd->wordlen)); - return ERROR; - } - - if (sd->card_dstatus) - sd_trace(("dstatus after byte rewrite = 0x%x\n", sd->card_dstatus)); - - return (BCME_OK); -} - -/* Program the response delay corresponding to the spi function */ -static int -bcmspi_prog_resp_delay(sdioh_info_t *sd, int func, uint8 resp_delay) -{ - if (sd->resp_delay_all == FALSE) - return (BCME_OK); - - if (sd->prev_fun == func) - return (BCME_OK); - - if (F0_RESPONSE_DELAY == F1_RESPONSE_DELAY) - return (BCME_OK); - - bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_RESPONSE_DELAY, resp_delay); - - /* Remember function for which to avoid reprogramming resp-delay in next iteration */ - sd->prev_fun = func; - - return (BCME_OK); - -} - -#define GSPI_RESYNC_PATTERN 0x0 - -/* A resync pattern is a 32bit MOSI line with all zeros. Its a special command in gSPI. - * It resets the spi-bkplane logic so that all F1 related ping-pong buffer logic is - * synchronised and all queued resuests are cancelled. - */ -static int -bcmspi_resync_f1(sdioh_info_t *sd) -{ - uint32 cmd_arg = GSPI_RESYNC_PATTERN, data = 0, datalen = 0; - - - /* Set up and issue the SPI command. MSByte goes out on bus first. Increase datalen - * according to the wordlen mode(16/32bit) the device is in. - */ - ASSERT(sd->wordlen == 4 || sd->wordlen == 2); - datalen = ROUNDUP(datalen, sd->wordlen); - - /* Start by copying command in the spi-outbuffer */ - *(uint32 *)spi_outbuf2 = cmd_arg; - - /* for Write, put the data into the output buffer */ - *(uint32 *)&spi_outbuf2[CMDLEN] = data; - - /* +4 for cmd, +4 for dstatus */ - spi_sendrecv(sd, spi_outbuf2, spi_inbuf2, datalen + 8); - - /* Last 4bytes are dstatus. Device is configured to return status bits. */ - if (sd->wordlen == 4) { /* 32bit spid */ - sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf2[datalen + CMDLEN ]); - } else if (sd->wordlen == 2) { /* 16bit spid */ - sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN ]) | - (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN + 2]) << 16)); - } else { - sd_err(("%s: Host is %d bit machine, could not read SPI dstatus.\n", - __FUNCTION__, 8 * sd->wordlen)); - return ERROR; - } - - if (sd->card_dstatus) - sd_trace(("dstatus after resync pattern write = 0x%x\n", sd->card_dstatus)); - - return (BCME_OK); -} - -uint32 dstatus_count = 0; - -static int -bcmspi_update_stats(sdioh_info_t *sd, uint32 cmd_arg) -{ - uint32 dstatus = sd->card_dstatus; - struct spierrstats_t *spierrstats = &sd->spierrstats; - int err = SUCCESS; - - sd_trace(("cmd = 0x%x, dstatus = 0x%x\n", cmd_arg, dstatus)); - - /* Store dstatus of last few gSPI transactions */ - spierrstats->dstatus[dstatus_count % NUM_PREV_TRANSACTIONS] = dstatus; - spierrstats->spicmd[dstatus_count % NUM_PREV_TRANSACTIONS] = cmd_arg; - dstatus_count++; - - if (sd->card_init_done == FALSE) - return err; - - if (dstatus & STATUS_DATA_NOT_AVAILABLE) { - spierrstats->dna++; - sd_trace(("Read data not available on F1 addr = 0x%x\n", - GFIELD(cmd_arg, SPI_REG_ADDR))); - /* Clear dna bit */ - bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_INTR_REG, DATA_UNAVAILABLE); - } - - if (dstatus & STATUS_UNDERFLOW) { - spierrstats->rdunderflow++; - sd_err(("FIFO underflow happened due to current F2 read command.\n")); - } - - if (dstatus & STATUS_OVERFLOW) { - spierrstats->wroverflow++; - sd_err(("FIFO overflow happened due to current (F1/F2) write command.\n")); - if ((sd->chip == BCM4329_CHIP_ID) && (sd->chiprev == 0)) { - bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_INTR_REG, F1_OVERFLOW); - bcmspi_resync_f1(sd); - sd_err(("Recovering from F1 FIFO overflow.\n")); - } else { - err = ERROR_OF; - } - } - - if (dstatus & STATUS_F2_INTR) { - spierrstats->f2interrupt++; - sd_trace(("Interrupt from F2. SW should clear corresponding IntStatus bits\n")); - } - - if (dstatus & STATUS_F3_INTR) { - spierrstats->f3interrupt++; - sd_err(("Interrupt from F3. SW should clear corresponding IntStatus bits\n")); - } - - if (dstatus & STATUS_HOST_CMD_DATA_ERR) { - spierrstats->hostcmddataerr++; - sd_err(("Error in CMD or Host data, detected by CRC/Checksum (optional)\n")); - } - - if (dstatus & STATUS_F2_PKT_AVAILABLE) { - spierrstats->f2pktavailable++; - sd_trace(("Packet is available/ready in F2 TX FIFO\n")); - sd_trace(("Packet length = %d\n", sd->dwordmode ? - ((dstatus & STATUS_F2_PKT_LEN_MASK) >> (STATUS_F2_PKT_LEN_SHIFT - 2)) : - ((dstatus & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT))); - } - - if (dstatus & STATUS_F3_PKT_AVAILABLE) { - spierrstats->f3pktavailable++; - sd_err(("Packet is available/ready in F3 TX FIFO\n")); - sd_err(("Packet length = %d\n", - (dstatus & STATUS_F3_PKT_LEN_MASK) >> STATUS_F3_PKT_LEN_SHIFT)); - } - - return err; -} - -extern int -sdioh_abort(sdioh_info_t *sd, uint func) -{ - return 0; -} - -int -sdioh_start(sdioh_info_t *sd, int stage) -{ - return SUCCESS; -} - -int -sdioh_stop(sdioh_info_t *sd) -{ - return SUCCESS; -} - - - -/* - * Private/Static work routines - */ -static int -bcmspi_host_init(sdioh_info_t *sd) -{ - - /* Default power on mode */ - sd->sd_mode = SDIOH_MODE_SPI; - sd->polled_mode = TRUE; - sd->host_init_done = TRUE; - sd->card_init_done = FALSE; - sd->adapter_slot = 1; - - return (SUCCESS); -} - -static int -get_client_blocksize(sdioh_info_t *sd) -{ - uint32 regdata[2]; - int status; - - /* Find F1/F2/F3 max packet size */ - if ((status = bcmspi_card_regread(sd, 0, SPID_F1_INFO_REG, - 8, regdata)) != SUCCESS) { - return status; - } - - sd_trace(("pkt_size regdata[0] = 0x%x, regdata[1] = 0x%x\n", - regdata[0], regdata[1])); - - sd->client_block_size[1] = (regdata[0] & F1_MAX_PKT_SIZE) >> 2; - sd_trace(("Func1 blocksize = %d\n", sd->client_block_size[1])); - ASSERT(sd->client_block_size[1] == BLOCK_SIZE_F1); - - sd->client_block_size[2] = ((regdata[0] >> 16) & F2_MAX_PKT_SIZE) >> 2; - sd_trace(("Func2 blocksize = %d\n", sd->client_block_size[2])); - ASSERT(sd->client_block_size[2] == BLOCK_SIZE_F2); - - sd->client_block_size[3] = (regdata[1] & F3_MAX_PKT_SIZE) >> 2; - sd_trace(("Func3 blocksize = %d\n", sd->client_block_size[3])); - ASSERT(sd->client_block_size[3] == BLOCK_SIZE_F3); - - return 0; -} - -static int -bcmspi_client_init(sdioh_info_t *sd) -{ - uint32 status_en_reg = 0; - sd_trace(("%s: Powering up slot %d\n", __FUNCTION__, sd->adapter_slot)); - -#ifdef HSMODE - if (!spi_start_clock(sd, (uint16)sd_divisor)) { - sd_err(("spi_start_clock failed\n")); - return ERROR; - } -#else - /* Start at ~400KHz clock rate for initialization */ - if (!spi_start_clock(sd, 128)) { - sd_err(("spi_start_clock failed\n")); - return ERROR; - } -#endif /* HSMODE */ - - if (!bcmspi_host_device_init_adapt(sd)) { - sd_err(("bcmspi_host_device_init_adapt failed\n")); - return ERROR; - } - - if (!bcmspi_test_card(sd)) { - sd_err(("bcmspi_test_card failed\n")); - return ERROR; - } - - sd->num_funcs = SPI_MAX_IOFUNCS; - - get_client_blocksize(sd); - - /* Apply resync pattern cmd with all zeros to reset spi-bkplane F1 logic */ - bcmspi_resync_f1(sd); - - sd->dwordmode = FALSE; - - bcmspi_card_regread(sd, 0, SPID_STATUS_ENABLE, 1, &status_en_reg); - - sd_trace(("%s: Enabling interrupt with dstatus \n", __FUNCTION__)); - status_en_reg |= INTR_WITH_STATUS; - - - if (bcmspi_card_regwrite(sd, SPI_FUNC_0, SPID_STATUS_ENABLE, 1, - status_en_reg & 0xff) != SUCCESS) { - sd_err(("%s: Unable to set response delay for all fun's.\n", __FUNCTION__)); - return ERROR; - } - - -#ifndef HSMODE - /* After configuring for High-Speed mode, set the desired clock rate. */ - if (!spi_start_clock(sd, 4)) { - sd_err(("spi_start_clock failed\n")); - return ERROR; - } -#endif /* HSMODE */ - - sd->card_init_done = TRUE; - - - return SUCCESS; -} - -static int -bcmspi_set_highspeed_mode(sdioh_info_t *sd, bool hsmode) -{ - uint32 regdata; - int status; - - if ((status = bcmspi_card_regread(sd, 0, SPID_CONFIG, - 4, ®data)) != SUCCESS) - return status; - - sd_trace(("In %s spih-ctrl = 0x%x \n", __FUNCTION__, regdata)); - - - if (hsmode == TRUE) { - sd_trace(("Attempting to enable High-Speed mode.\n")); - - if (regdata & HIGH_SPEED_MODE) { - sd_trace(("Device is already in High-Speed mode.\n")); - return status; - } else { - regdata |= HIGH_SPEED_MODE; - sd_trace(("Writing %08x to device at %08x\n", regdata, SPID_CONFIG)); - if ((status = bcmspi_card_regwrite(sd, 0, SPID_CONFIG, - 4, regdata)) != SUCCESS) { - return status; - } - } - } else { - sd_trace(("Attempting to disable High-Speed mode.\n")); - - if (regdata & HIGH_SPEED_MODE) { - regdata &= ~HIGH_SPEED_MODE; - sd_trace(("Writing %08x to device at %08x\n", regdata, SPID_CONFIG)); - if ((status = bcmspi_card_regwrite(sd, 0, SPID_CONFIG, - 4, regdata)) != SUCCESS) - return status; - } - else { - sd_trace(("Device is already in Low-Speed mode.\n")); - return status; - } - } - - spi_controller_highspeed_mode(sd, hsmode); - - return TRUE; -} - -#define bcmspi_find_curr_mode(sd) { \ - sd->wordlen = 2; \ - status = bcmspi_card_regread_fixedaddr(sd, 0, SPID_TEST_READ, 4, ®data); \ - regdata &= 0xff; \ - if ((regdata == 0xad) || (regdata == 0x5b) || \ - (regdata == 0x5d) || (regdata == 0x5a)) \ - break; \ - sd->wordlen = 4; \ - status = bcmspi_card_regread_fixedaddr(sd, 0, SPID_TEST_READ, 4, ®data); \ - regdata &= 0xff; \ - if ((regdata == 0xad) || (regdata == 0x5b) || \ - (regdata == 0x5d) || (regdata == 0x5a)) \ - break; \ - sd_trace(("Silicon testability issue: regdata = 0x%x." \ - " Expected 0xad, 0x5a, 0x5b or 0x5d.\n", regdata)); \ - OSL_DELAY(100000); \ -} - -#define INIT_ADAPT_LOOP 100 - -/* Adapt clock-phase-speed-bitwidth between host and device */ -static bool -bcmspi_host_device_init_adapt(sdioh_info_t *sd) -{ - uint32 wrregdata, regdata = 0; - int status; - int i; - - /* Due to a silicon testability issue, the first command from the Host - * to the device will get corrupted (first bit will be lost). So the - * Host should poll the device with a safe read request. ie: The Host - * should try to read F0 addr 0x14 using the Fixed address mode - * (This will prevent a unintended write command to be detected by device) - */ - for (i = 0; i < INIT_ADAPT_LOOP; i++) { - /* If device was not power-cycled it will stay in 32bit mode with - * response-delay-all bit set. Alternate the iteration so that - * read either with or without response-delay for F0 to succeed. - */ - bcmspi_find_curr_mode(sd); - sd->resp_delay_all = (i & 0x1) ? TRUE : FALSE; - - bcmspi_find_curr_mode(sd); - sd->dwordmode = TRUE; - - bcmspi_find_curr_mode(sd); - sd->dwordmode = FALSE; - } - - /* Bail out, device not detected */ - if (i == INIT_ADAPT_LOOP) - return FALSE; - - /* Softreset the spid logic */ - if ((sd->dwordmode) || (sd->wordlen == 4)) { - bcmspi_card_regwrite(sd, 0, SPID_RESET_BP, 1, RESET_ON_WLAN_BP_RESET|RESET_SPI); - bcmspi_card_regread(sd, 0, SPID_RESET_BP, 1, ®data); - sd_trace(("reset reg read = 0x%x\n", regdata)); - sd_trace(("dwordmode = %d, wordlen = %d, resp_delay_all = %d\n", sd->dwordmode, - sd->wordlen, sd->resp_delay_all)); - /* Restore default state after softreset */ - sd->wordlen = 2; - sd->dwordmode = FALSE; - } - - if (sd->wordlen == 4) { - if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, ®data)) != - SUCCESS) - return FALSE; - if (regdata == TEST_RO_DATA_32BIT_LE) { - sd_trace(("Spid is already in 32bit LE mode. Value read = 0x%x\n", - regdata)); - sd_trace(("Spid power was left on.\n")); - } else { - sd_err(("Spid power was left on but signature read failed." - " Value read = 0x%x\n", regdata)); - return FALSE; - } - } else { - sd->wordlen = 2; - -#define CTRL_REG_DEFAULT 0x00010430 /* according to the host m/c */ - - wrregdata = (CTRL_REG_DEFAULT); - sd->resp_delay_all = TRUE; - if (sd->resp_delay_all == TRUE) { - /* Enable response delay for all */ - wrregdata |= (RESP_DELAY_ALL << 16); - /* Program response delay value */ - wrregdata &= 0xffff00ff; - wrregdata |= (F1_RESPONSE_DELAY << 8); - sd->prev_fun = SPI_FUNC_1; - bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata); - } - - if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, ®data)) != SUCCESS) - return FALSE; - sd_trace(("(we are still in 16bit mode) 32bit READ LE regdata = 0x%x\n", regdata)); - -#ifndef HSMODE - wrregdata |= (CLOCK_PHASE | CLOCK_POLARITY); - wrregdata &= ~HIGH_SPEED_MODE; - bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata); -#endif /* HSMODE */ - - for (i = 0; i < INIT_ADAPT_LOOP; i++) { - if ((regdata == 0xfdda7d5b) || (regdata == 0xfdda7d5a)) { - sd_trace(("0xfeedbead was leftshifted by 1-bit.\n")); - if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, - ®data)) != SUCCESS) - return FALSE; - } - OSL_DELAY(1000); - } - - - /* Change to host controller intr-polarity of active-low */ - wrregdata &= ~INTR_POLARITY; - sd_trace(("(we are still in 16bit mode) 32bit Write LE reg-ctrl-data = 0x%x\n", - wrregdata)); - /* Change to 32bit mode */ - wrregdata |= WORD_LENGTH_32; - bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata); - - /* Change command/data packaging in 32bit LE mode */ - sd->wordlen = 4; - - if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, ®data)) != SUCCESS) - return FALSE; - - if (regdata == TEST_RO_DATA_32BIT_LE) { - sd_trace(("Read spid passed. Value read = 0x%x\n", regdata)); - sd_trace(("Spid had power-on cycle OR spi was soft-resetted \n")); - } else { - sd_err(("Stale spid reg values read as it was kept powered. Value read =" - "0x%x\n", regdata)); - return FALSE; - } - } - - - return TRUE; -} - -static bool -bcmspi_test_card(sdioh_info_t *sd) -{ - uint32 regdata; - int status; - - if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, ®data)) != SUCCESS) - return FALSE; - - if (regdata == (TEST_RO_DATA_32BIT_LE)) - sd_trace(("32bit LE regdata = 0x%x\n", regdata)); - else { - sd_trace(("Incorrect 32bit LE regdata = 0x%x\n", regdata)); - return FALSE; - } - - -#define RW_PATTERN1 0xA0A1A2A3 -#define RW_PATTERN2 0x4B5B6B7B - - regdata = RW_PATTERN1; - if ((status = bcmspi_card_regwrite(sd, 0, SPID_TEST_RW, 4, regdata)) != SUCCESS) - return FALSE; - regdata = 0; - if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_RW, 4, ®data)) != SUCCESS) - return FALSE; - if (regdata != RW_PATTERN1) { - sd_err(("Write-Read spid failed. Value wrote = 0x%x, Value read = 0x%x\n", - RW_PATTERN1, regdata)); - return FALSE; - } else - sd_trace(("R/W spid passed. Value read = 0x%x\n", regdata)); - - regdata = RW_PATTERN2; - if ((status = bcmspi_card_regwrite(sd, 0, SPID_TEST_RW, 4, regdata)) != SUCCESS) - return FALSE; - regdata = 0; - if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_RW, 4, ®data)) != SUCCESS) - return FALSE; - if (regdata != RW_PATTERN2) { - sd_err(("Write-Read spid failed. Value wrote = 0x%x, Value read = 0x%x\n", - RW_PATTERN2, regdata)); - return FALSE; - } else - sd_trace(("R/W spid passed. Value read = 0x%x\n", regdata)); - - return TRUE; -} - -static int -bcmspi_driver_init(sdioh_info_t *sd) -{ - sd_trace(("%s\n", __FUNCTION__)); - if ((bcmspi_host_init(sd)) != SUCCESS) { - return ERROR; - } - - if (bcmspi_client_init(sd) != SUCCESS) { - return ERROR; - } - - return SUCCESS; -} - -/* Read device reg */ -static int -bcmspi_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data) -{ - int status; - uint32 cmd_arg, dstatus; - - ASSERT(regsize); - - if (func == 2) - sd_trace(("Reg access on F2 will generate error indication in dstatus bits.\n")); - - cmd_arg = 0; - cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 0); - cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */ - cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); - cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); - cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize == BLOCK_SIZE_F2 ? 0 : regsize); - - sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); - sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 0, func, - regaddr, *data)); - - if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, data, regsize)) - != SUCCESS) - return status; - - bcmspi_cmd_getdstatus(sd, &dstatus); - if (dstatus) - sd_trace(("dstatus =0x%x\n", dstatus)); - - return SUCCESS; -} - -static int -bcmspi_card_regread_fixedaddr(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data) -{ - - int status; - uint32 cmd_arg; - uint32 dstatus; - - ASSERT(regsize); - - if (func == 2) - sd_trace(("Reg access on F2 will generate error indication in dstatus bits.\n")); - - cmd_arg = 0; - cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 0); - cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 0); /* Fixed access */ - cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); - cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); - cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize); - - sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); - - if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, data, regsize)) - != SUCCESS) - return status; - - sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 0, func, - regaddr, *data)); - - bcmspi_cmd_getdstatus(sd, &dstatus); - sd_trace(("dstatus =0x%x\n", dstatus)); - return SUCCESS; -} - -/* write a device register */ -static int -bcmspi_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 data) -{ - int status; - uint32 cmd_arg, dstatus; - - ASSERT(regsize); - - cmd_arg = 0; - - cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1); - cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */ - cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); - cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); - cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize == BLOCK_SIZE_F2 ? 0 : regsize); - - sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); - sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 1, func, - regaddr, data)); - - - if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, &data, regsize)) - != SUCCESS) - return status; - - bcmspi_cmd_getdstatus(sd, &dstatus); - if (dstatus) - sd_trace(("dstatus =0x%x\n", dstatus)); - - return SUCCESS; -} - -/* write a device register - 1 byte */ -static int -bcmspi_card_bytewrite(sdioh_info_t *sd, int func, uint32 regaddr, uint8 *byte) -{ - int status; - uint32 cmd_arg; - uint32 dstatus; - uint32 data = (uint32)(*byte); - - cmd_arg = 0; - cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); - cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */ - cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); - cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1); - cmd_arg = SFIELD(cmd_arg, SPI_LEN, 1); - - sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); - sd_trace(("%s: func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, func, - regaddr, data)); - - if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, - cmd_arg, &data, 1)) != SUCCESS) { - return status; - } - - bcmspi_cmd_getdstatus(sd, &dstatus); - if (dstatus) - sd_trace(("dstatus =0x%x\n", dstatus)); - - return SUCCESS; -} - -void -bcmspi_cmd_getdstatus(sdioh_info_t *sd, uint32 *dstatus_buffer) -{ - *dstatus_buffer = sd->card_dstatus; -} - -/* 'data' is of type uint32 whereas other buffers are of type uint8 */ -static int -bcmspi_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd_arg, - uint32 *data, uint32 datalen) -{ - uint32 i, j; - uint8 resp_delay = 0; - int err = SUCCESS; - uint32 hostlen; - uint32 spilen = 0; - uint32 dstatus_idx = 0; - uint16 templen, buslen, len, *ptr = NULL; - - sd_trace(("spi cmd = 0x%x\n", cmd_arg)); - - if (DWORDMODE_ON) { - spilen = GFIELD(cmd_arg, SPI_LEN); - if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_0) || - (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_1)) - dstatus_idx = spilen * 3; - - if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) && - (GFIELD(cmd_arg, SPI_RW_FLAG) == 1)) { - spilen = spilen << 2; - dstatus_idx = (spilen % 16) ? (16 - (spilen % 16)) : 0; - /* convert len to mod16 size */ - spilen = ROUNDUP(spilen, 16); - cmd_arg = SFIELD(cmd_arg, SPI_LEN, (spilen >> 2)); - } - } - - /* Set up and issue the SPI command. MSByte goes out on bus first. Increase datalen - * according to the wordlen mode(16/32bit) the device is in. - */ - if (sd->wordlen == 4) { /* 32bit spid */ - *(uint32 *)spi_outbuf = bcmswap32(cmd_arg); - if (datalen & 0x3) - datalen += (4 - (datalen & 0x3)); - } else if (sd->wordlen == 2) { /* 16bit spid */ - *(uint16 *)spi_outbuf = bcmswap16(cmd_arg & 0xffff); - *(uint16 *)&spi_outbuf[2] = bcmswap16((cmd_arg & 0xffff0000) >> 16); - if (datalen & 0x1) - datalen++; - if (datalen < 4) - datalen = ROUNDUP(datalen, 4); - } else { - sd_err(("Host is %d bit spid, could not create SPI command.\n", - 8 * sd->wordlen)); - return ERROR; - } - - /* for Write, put the data into the output buffer */ - if (GFIELD(cmd_arg, SPI_RW_FLAG) == 1) { - /* We send len field of hw-header always a mod16 size, both from host and dongle */ - if (DWORDMODE_ON) { - if (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) { - ptr = (uint16 *)&data[0]; - templen = *ptr; - /* ASSERT(*ptr == ~*(ptr + 1)); */ - templen = ROUNDUP(templen, 16); - *ptr = templen; - sd_trace(("actual tx len = %d\n", (uint16)(~*(ptr+1)))); - } - } - - if (datalen != 0) { - for (i = 0; i < datalen/4; i++) { - if (sd->wordlen == 4) { /* 32bit spid */ - *(uint32 *)&spi_outbuf[i * 4 + CMDLEN] = - bcmswap32(data[i]); - } else if (sd->wordlen == 2) { /* 16bit spid */ - *(uint16 *)&spi_outbuf[i * 4 + CMDLEN] = - bcmswap16(data[i] & 0xffff); - *(uint16 *)&spi_outbuf[i * 4 + CMDLEN + 2] = - bcmswap16((data[i] & 0xffff0000) >> 16); - } - } - } - } - - /* Append resp-delay number of bytes and clock them out for F0/1/2 reads. */ - if (GFIELD(cmd_arg, SPI_RW_FLAG) == 0) { - int func = GFIELD(cmd_arg, SPI_FUNCTION); - switch (func) { - case 0: - resp_delay = sd->resp_delay_all ? F0_RESPONSE_DELAY : 0; - break; - case 1: - resp_delay = F1_RESPONSE_DELAY; - break; - case 2: - resp_delay = sd->resp_delay_all ? F2_RESPONSE_DELAY : 0; - break; - default: - ASSERT(0); - break; - } - /* Program response delay */ - bcmspi_prog_resp_delay(sd, func, resp_delay); - } - - /* +4 for cmd and +4 for dstatus */ - hostlen = datalen + 8 + resp_delay; - hostlen += dstatus_idx; - hostlen += (4 - (hostlen & 0x3)); - spi_sendrecv(sd, spi_outbuf, spi_inbuf, hostlen); - - /* for Read, get the data into the input buffer */ - if (datalen != 0) { - if (GFIELD(cmd_arg, SPI_RW_FLAG) == 0) { /* if read cmd */ - for (j = 0; j < datalen/4; j++) { - if (sd->wordlen == 4) { /* 32bit spid */ - data[j] = bcmswap32(*(uint32 *)&spi_inbuf[j * 4 + - CMDLEN + resp_delay]); - } else if (sd->wordlen == 2) { /* 16bit spid */ - data[j] = (bcmswap16(*(uint16 *)&spi_inbuf[j * 4 + - CMDLEN + resp_delay])) | - ((bcmswap16(*(uint16 *)&spi_inbuf[j * 4 + - CMDLEN + resp_delay + 2])) << 16); - } - } - - if ((DWORDMODE_ON) && (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2)) { - ptr = (uint16 *)&data[0]; - templen = *ptr; - buslen = len = ~(*(ptr + 1)); - buslen = ROUNDUP(buslen, 16); - /* populate actual len in hw-header */ - if (templen == buslen) - *ptr = len; - } - } - } - - /* Restore back the len field of the hw header */ - if (DWORDMODE_ON) { - if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) && - (GFIELD(cmd_arg, SPI_RW_FLAG) == 1)) { - ptr = (uint16 *)&data[0]; - *ptr = (uint16)(~*(ptr+1)); - } - } - - dstatus_idx += (datalen + CMDLEN + resp_delay); - /* Last 4bytes are dstatus. Device is configured to return status bits. */ - if (sd->wordlen == 4) { /* 32bit spid */ - sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf[dstatus_idx]); - } else if (sd->wordlen == 2) { /* 16bit spid */ - sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf[dstatus_idx]) | - (bcmswap16(*(uint16 *)&spi_inbuf[dstatus_idx + 2]) << 16)); - } else { - sd_err(("Host is %d bit machine, could not read SPI dstatus.\n", - 8 * sd->wordlen)); - return ERROR; - } - if (sd->card_dstatus == 0xffffffff) { - sd_err(("looks like not a GSPI device or device is not powered.\n")); - } - - err = bcmspi_update_stats(sd, cmd_arg); - - return err; - -} - -static int -bcmspi_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo, - uint32 addr, int nbytes, uint32 *data) -{ - int status; - uint32 cmd_arg; - bool write = rw == SDIOH_READ ? 0 : 1; - uint retries = 0; - - bool enable; - uint32 spilen; - - cmd_arg = 0; - - ASSERT(nbytes); - ASSERT(nbytes <= sd->client_block_size[func]); - - if (write) sd->t_cnt++; else sd->r_cnt++; - - if (func == 2) { - /* Frame len check limited by gSPI. */ - if ((nbytes > 2000) && write) { - sd_trace((">2KB write: F2 wr of %d bytes\n", nbytes)); - } - /* ASSERT(nbytes <= 2048); Fix bigger len gspi issue and uncomment. */ - /* If F2 fifo on device is not ready to receive data, don't do F2 transfer */ - if (write) { - uint32 dstatus; - /* check F2 ready with cached one */ - bcmspi_cmd_getdstatus(sd, &dstatus); - if ((dstatus & STATUS_F2_RX_READY) == 0) { - retries = WAIT_F2RXFIFORDY; - enable = 0; - while (retries-- && !enable) { - OSL_DELAY(WAIT_F2RXFIFORDY_DELAY * 1000); - bcmspi_card_regread(sd, SPI_FUNC_0, SPID_STATUS_REG, 4, - &dstatus); - if (dstatus & STATUS_F2_RX_READY) - enable = TRUE; - } - if (!enable) { - struct spierrstats_t *spierrstats = &sd->spierrstats; - spierrstats->f2rxnotready++; - sd_err(("F2 FIFO is not ready to receive data.\n")); - return ERROR; - } - sd_trace(("No of retries on F2 ready %d\n", - (WAIT_F2RXFIFORDY - retries))); - } - } - } - - /* F2 transfers happen on 0 addr */ - addr = (func == 2) ? 0 : addr; - - /* In pio mode buffer is read using fixed address fifo in func 1 */ - if ((func == 1) && (fifo)) - cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 0); - else - cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); - - cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); - cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, addr); - cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, write); - spilen = sd->data_xfer_count = MIN(sd->client_block_size[func], nbytes); - if ((sd->dwordmode == TRUE) && (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2)) { - /* convert len to mod4 size */ - spilen = spilen + ((spilen & 0x3) ? (4 - (spilen & 0x3)): 0); - cmd_arg = SFIELD(cmd_arg, SPI_LEN, (spilen >> 2)); - } else - cmd_arg = SFIELD(cmd_arg, SPI_LEN, spilen); - - if ((func == 2) && (fifo == 1)) { - sd_data(("%s: %s func %d, %s, addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n", - __FUNCTION__, write ? "Wr" : "Rd", func, "INCR", - addr, nbytes, sd->r_cnt, sd->t_cnt)); - } - - sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); - sd_data(("%s: %s func %d, %s, addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n", - __FUNCTION__, write ? "Wd" : "Rd", func, "INCR", - addr, nbytes, sd->r_cnt, sd->t_cnt)); - - - if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, - data, nbytes)) != SUCCESS) { - sd_err(("%s: cmd_issue failed for %s\n", __FUNCTION__, - (write ? "write" : "read"))); - return status; - } - - /* gSPI expects that hw-header-len is equal to spi-command-len */ - if ((func == 2) && (rw == SDIOH_WRITE) && (sd->dwordmode == FALSE)) { - ASSERT((uint16)sd->data_xfer_count == (uint16)(*data & 0xffff)); - ASSERT((uint16)sd->data_xfer_count == (uint16)(~((*data & 0xffff0000) >> 16))); - } - - if ((nbytes > 2000) && !write) { - sd_trace((">2KB read: F2 rd of %d bytes\n", nbytes)); - } - - return SUCCESS; -} - -/* Reset and re-initialize the device */ -int -sdioh_sdio_reset(sdioh_info_t *si) -{ - si->card_init_done = FALSE; - return bcmspi_client_init(si); -} From 26fd0c293435b4d978c2ad21f2db6c5a1129736e Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 11 Nov 2011 19:36:57 +0100 Subject: [PATCH 1993/2556] net: wireless: bcm4329: Add packet filtering commands --- drivers/net/wireless/bcm4329/dhd.h | 7 +++++ drivers/net/wireless/bcm4329/dhd_common.c | 6 ++++ drivers/net/wireless/bcm4329/dhd_linux.c | 34 ++++++++++++++++++++++- drivers/net/wireless/bcm4329/wl_iw.c | 22 +++++++++++---- drivers/net/wireless/bcm4329/wl_iw.h | 5 +++- 5 files changed, 67 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/bcm4329/dhd.h b/drivers/net/wireless/bcm4329/dhd.h index 7cec5b6c5183d..c38e47d5ffbd3 100644 --- a/drivers/net/wireless/bcm4329/dhd.h +++ b/drivers/net/wireless/bcm4329/dhd.h @@ -454,4 +454,11 @@ extern void dhd_arp_cleanup(dhd_pub_t *dhd); int dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen); void dhd_arp_offload_add_ip(dhd_pub_t *dhd, u32 ipaddr); +#define DHD_UNICAST_FILTER_NUM 0 +#define DHD_BROADCAST_FILTER_NUM 1 +#define DHD_MULTICAST4_FILTER_NUM 2 +#define DHD_MULTICAST6_FILTER_NUM 3 +extern int net_os_set_packet_filter(struct net_device *dev, int val); +extern int net_os_rxfilter_add_remove(struct net_device *dev, int val, int num); + #endif /* _dhd_h_ */ diff --git a/drivers/net/wireless/bcm4329/dhd_common.c b/drivers/net/wireless/bcm4329/dhd_common.c index 0d6a06acfddbf..3af1c33cea3a4 100644 --- a/drivers/net/wireless/bcm4329/dhd_common.c +++ b/drivers/net/wireless/bcm4329/dhd_common.c @@ -992,6 +992,9 @@ dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_ wl_pkt_filter_enable_t enable_parm; wl_pkt_filter_enable_t * pkt_filterp; + if (!arg) + return; + if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) { DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); goto fail; @@ -1065,6 +1068,9 @@ dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg) char *arg_save = 0, *arg_org = 0; #define BUF_SIZE 2048 + if (!arg) + return; + if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) { DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); goto fail; diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index e17291f9698d3..2aab9f0b83222 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -2294,9 +2294,12 @@ dhd_bus_start(dhd_pub_t *dhdp) /* enable dongle roaming event */ setbit(dhdp->eventmask, WLC_E_ROAM); - dhdp->pktfilter_count = 1; + dhdp->pktfilter_count = 4; /* Setup filter to allow only unicast */ dhdp->pktfilter[0] = "100 0 0 0 0x01 0x00"; + dhdp->pktfilter[1] = NULL; + dhdp->pktfilter[2] = NULL; + dhdp->pktfilter[3] = NULL; #endif /* EMBEDDED_PLATFORM */ /* Bus is ready, do any protocol initialization */ @@ -3067,6 +3070,35 @@ int net_os_set_dtim_skip(struct net_device *dev, int val) return 0; } +int net_os_rxfilter_add_remove(struct net_device *dev, int add_remove, int num) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + char *filterp = NULL; + int ret = 0; + + if (!dhd || (num == DHD_UNICAST_FILTER_NUM)) + return ret; + if (num >= dhd->pub.pktfilter_count) + return -EINVAL; + if (add_remove) { + switch (num) { + case DHD_BROADCAST_FILTER_NUM: + filterp = "101 0 0 0 0xFFFFFFFFFFFF 0xFFFFFFFFFFFF"; + break; + case DHD_MULTICAST4_FILTER_NUM: + filterp = "102 0 0 0 0xFFFFFF 0x01005E"; + break; + case DHD_MULTICAST6_FILTER_NUM: + filterp = "103 0 0 0 0xFFFF 0x3333"; + break; + default: + return -EINVAL; + } + } + dhd->pub.pktfilter[num] = filterp; + return ret; +} + int net_os_set_packet_filter(struct net_device *dev, int val) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); diff --git a/drivers/net/wireless/bcm4329/wl_iw.c b/drivers/net/wireless/bcm4329/wl_iw.c index 9289aee034bf3..7fcdd05dfcb14 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.c +++ b/drivers/net/wireless/bcm4329/wl_iw.c @@ -7256,27 +7256,39 @@ static int wl_iw_set_priv( ret = wl_iw_set_pno_enable(dev, info, (union iwreq_data *)dwrq, extra); #endif #if defined(CSCAN) - else if (strnicmp(extra, CSCAN_COMMAND, strlen(CSCAN_COMMAND)) == 0) + else if (strnicmp(extra, CSCAN_COMMAND, strlen(CSCAN_COMMAND)) == 0) ret = wl_iw_set_cscan(dev, info, (union iwreq_data *)dwrq, extra); -#endif +#endif #ifdef CUSTOMER_HW2 else if (strnicmp(extra, "POWERMODE", strlen("POWERMODE")) == 0) ret = wl_iw_set_power_mode(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, "BTCOEXMODE", strlen("BTCOEXMODE")) == 0) { + else if (strnicmp(extra, "BTCOEXMODE", strlen("BTCOEXMODE")) == 0) { WL_TRACE_COEX(("%s:got Framwrork cmd: 'BTCOEXMODE'\n", __FUNCTION__)); ret = wl_iw_set_btcoex_dhcp(dev, info, (union iwreq_data *)dwrq, extra); - } + } #else else if (strnicmp(extra, "POWERMODE", strlen("POWERMODE")) == 0) ret = wl_iw_set_btcoex_dhcp(dev, info, (union iwreq_data *)dwrq, extra); #endif else if (strnicmp(extra, "GETPOWER", strlen("GETPOWER")) == 0) ret = wl_iw_get_power_mode(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, RXFILTER_START_CMD, strlen(RXFILTER_START_CMD)) == 0) + ret = net_os_set_packet_filter(dev, 1); + else if (strnicmp(extra, RXFILTER_STOP_CMD, strlen(RXFILTER_STOP_CMD)) == 0) + ret = net_os_set_packet_filter(dev, 0); + else if (strnicmp(extra, RXFILTER_ADD_CMD, strlen(RXFILTER_ADD_CMD)) == 0) { + int filter_num = *(extra + strlen(RXFILTER_ADD_CMD) + 1) - '0'; + ret = net_os_rxfilter_add_remove(dev, TRUE, filter_num); + } + else if (strnicmp(extra, RXFILTER_REMOVE_CMD, strlen(RXFILTER_REMOVE_CMD)) == 0) { + int filter_num = *(extra + strlen(RXFILTER_REMOVE_CMD) + 1) - '0'; + ret = net_os_rxfilter_add_remove(dev, FALSE, filter_num); + } #ifdef SOFTAP #ifdef SOFTAP_TLV_CFG else if (strnicmp(extra, SOFTAP_SET_CMD, strlen(SOFTAP_SET_CMD)) == 0) { wl_iw_softap_cfg_tlv(dev, info, (union iwreq_data *)dwrq, extra); - } + } #endif else if (strnicmp(extra, "ASCII_CMD", strlen("ASCII_CMD")) == 0) { wl_iw_process_private_ascii_cmd(dev, info, (union iwreq_data *)dwrq, extra); diff --git a/drivers/net/wireless/bcm4329/wl_iw.h b/drivers/net/wireless/bcm4329/wl_iw.h index e5eda6ad5cfb6..86613e4c84433 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.h +++ b/drivers/net/wireless/bcm4329/wl_iw.h @@ -53,6 +53,10 @@ #define PNOENABLE_SET_CMD "PNOFORCE" #define PNODEBUG_SET_CMD "PNODEBUG" #define TXPOWER_SET_CMD "TXPOWER" +#define RXFILTER_START_CMD "RXFILTER-START" +#define RXFILTER_STOP_CMD "RXFILTER-STOP" +#define RXFILTER_ADD_CMD "RXFILTER-ADD" +#define RXFILTER_REMOVE_CMD "RXFILTER-REMOVE" #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" @@ -198,7 +202,6 @@ extern int net_os_wake_lock_timeout_enable(struct net_device *dev); extern int net_os_set_suspend_disable(struct net_device *dev, int val); extern int net_os_set_suspend(struct net_device *dev, int val); extern int net_os_set_dtim_skip(struct net_device *dev, int val); -extern int net_os_set_packet_filter(struct net_device *dev, int val); extern void dhd_bus_country_set(struct net_device *dev, char *country_code); extern char *dhd_bus_country_get(struct net_device *dev); extern int dhd_get_dtim_skip(dhd_pub_t *dhd); From 794c16cce1454eab3931980b9b344c3f2c414b5d Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 11 Nov 2011 19:37:32 +0100 Subject: [PATCH 1994/2556] net: wireless: bcm4329: Prohibit FW access in case of FW crash --- drivers/net/wireless/bcm4329/dhd.h | 3 ++- drivers/net/wireless/bcm4329/dhd_cdc.c | 17 +++++++++++++++-- drivers/net/wireless/bcm4329/dhd_linux.c | 15 +++++++++++---- drivers/net/wireless/bcm4329/dhd_sdio.c | 3 ++- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/bcm4329/dhd.h b/drivers/net/wireless/bcm4329/dhd.h index c38e47d5ffbd3..c18a858e7d33c 100644 --- a/drivers/net/wireless/bcm4329/dhd.h +++ b/drivers/net/wireless/bcm4329/dhd.h @@ -144,7 +144,7 @@ typedef struct dhd_pub { ulong rx_readahead_cnt; /* Number of packets where header read-ahead was used. */ ulong tx_realloc; /* Number of tx packets we had to realloc for headroom */ - ulong fc_packets; /* Number of flow control pkts recvd */ + ulong fc_packets; /* Number of flow control pkts recvd */ /* Last error return */ int bcmerror; @@ -156,6 +156,7 @@ typedef struct dhd_pub { /* Suspend disable flag and "in suspend" flag */ int suspend_disable_flag; /* "1" to disable all extra powersaving during suspend */ int in_suspend; /* flag set to 1 when early suspend called */ + int hang_was_sent; /* flag that message was send at least once */ #ifdef PNO_SUPPORT int pno_enable; /* pno status : "1" is pno enable */ #endif /* PNO_SUPPORT */ diff --git a/drivers/net/wireless/bcm4329/dhd_cdc.c b/drivers/net/wireless/bcm4329/dhd_cdc.c index 61f6a6f393a97..4bec0b606dc9d 100644 --- a/drivers/net/wireless/bcm4329/dhd_cdc.c +++ b/drivers/net/wireless/bcm4329/dhd_cdc.c @@ -150,7 +150,8 @@ dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len) memcpy(prot->buf, buf, len); if ((ret = dhdcdc_msg(dhd)) < 0) { - DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret)); + if (!dhd->hang_was_sent) + DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret)); goto done; } @@ -205,6 +206,18 @@ dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len) DHD_TRACE(("%s: Enter\n", __FUNCTION__)); DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len)); + if (dhd->busstate == DHD_BUS_DOWN) { + DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__)); + return -EIO; + } + + /* don't talk to the dongle if fw is about to be reloaded */ + if (dhd->hang_was_sent) { + DHD_ERROR(("%s: HANG was sent up earlier. Not talking to the chip\n", + __FUNCTION__)); + return -EIO; + } + memset(msg, 0, sizeof(cdc_ioctl_t)); msg->cmd = htol32(cmd); @@ -251,7 +264,7 @@ dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len) dhd_prot_t *prot = dhd->prot; int ret = -1; - if (dhd->busstate == DHD_BUS_DOWN) { + if ((dhd->busstate == DHD_BUS_DOWN) || dhd->hang_was_sent) { DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__)); return ret; } diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index 2aab9f0b83222..ba9ef2aee0e92 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -309,7 +309,6 @@ typedef struct dhd_info { int wl_count; int wl_packet; - int hang_was_sent; /* flag that message was send at least once */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) struct mutex wl_start_lock; /* mutex when START called to prevent any other Linux calls */ #endif @@ -1768,6 +1767,14 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) dhd_os_wake_lock(&dhd->pub); + /* send to dongle only if we are not waiting for reload already */ + if (dhd->pub.hang_was_sent) { + DHD_ERROR(("%s: HANG was sent up earlier\n", __FUNCTION__)); + dhd_os_wake_lock_timeout_enable(&dhd->pub); + dhd_os_wake_unlock(&dhd->pub); + return OSL_ERROR(BCME_DONGLE_DOWN); + } + ifidx = dhd_net2idx(dhd, net); DHD_TRACE(("%s: ifidx %d, cmd 0x%04x\n", __FUNCTION__, ifidx, cmd)); @@ -1911,7 +1918,7 @@ dhd_stop(struct net_device *net) #else DHD_ERROR(("BYPASS %s:due to BRCM compilation : under investigation ...\n", __FUNCTION__)); #endif /* !defined(IGNORE_ETH0_DOWN) */ - + dhd->pub.hang_was_sent = 0; OLD_MOD_DEC_USE_COUNT; return 0; } @@ -3177,8 +3184,8 @@ int net_os_send_hang_message(struct net_device *dev) int ret = 0; if (dhd) { - if (!dhd->hang_was_sent) { - dhd->hang_was_sent = 1; + if (!dhd->pub.hang_was_sent) { + dhd->pub.hang_was_sent = 1; ret = wl_iw_send_priv_event(dev, "HANG"); } } diff --git a/drivers/net/wireless/bcm4329/dhd_sdio.c b/drivers/net/wireless/bcm4329/dhd_sdio.c index 1380dd389cf62..e9093e8106244 100644 --- a/drivers/net/wireless/bcm4329/dhd_sdio.c +++ b/drivers/net/wireless/bcm4329/dhd_sdio.c @@ -1281,7 +1281,8 @@ dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) DHD_INFO(("%s: ctrl_frame_stat == FALSE\n", __FUNCTION__)); ret = 0; } else { - DHD_INFO(("%s: ctrl_frame_stat == TRUE\n", __FUNCTION__)); + if (!bus->dhd->hang_was_sent) + DHD_ERROR(("%s: ctrl_frame_stat == TRUE\n", __FUNCTION__)); ret = -1; } } From c6d93f154cc1711b4966ea53a9209e2c76dd7381 Mon Sep 17 00:00:00 2001 From: "Choi, Jong-Hwan" Date: Fri, 11 Nov 2011 20:37:04 +0100 Subject: [PATCH 1995/2556] net: wireless: bcm4329: fix array subscript is below array bounds --- drivers/net/wireless/bcm4329/dhd_linux.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index ba9ef2aee0e92..7b4a099d54ec5 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -1938,6 +1938,9 @@ dhd_open(struct net_device *net) ifidx = dhd_net2idx(dhd, net); DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx)); + if (ifidx == DHD_BAD_IF) + return -1; + if ((dhd->iflist[ifidx]) && (dhd->iflist[ifidx]->state == WLC_E_IF_DEL)) { DHD_ERROR(("%s: Error: called when IF already deleted\n", __FUNCTION__)); return -1; From 368ef42ec21f99404d785a3906c7b23ca321a115 Mon Sep 17 00:00:00 2001 From: "Choi, Jong-Hwan" Date: Fri, 11 Nov 2011 20:38:54 +0100 Subject: [PATCH 1996/2556] net: wireless: bcm4329: Convert kmalloc() + bzero() to kzalloc() --- drivers/net/wireless/bcm4329/linux_osl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/bcm4329/linux_osl.c b/drivers/net/wireless/bcm4329/linux_osl.c index cf72a077bd908..c5c94457ce837 100644 --- a/drivers/net/wireless/bcm4329/linux_osl.c +++ b/drivers/net/wireless/bcm4329/linux_osl.c @@ -155,10 +155,9 @@ osl_attach(void *pdev, uint bustype, bool pkttag) gfp_t flags; flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; - osh = kmalloc(sizeof(osl_t), flags); + osh = kzalloc(sizeof(osl_t), flags); ASSERT(osh); - bzero(osh, sizeof(osl_t)); ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(linuxbcmerrormap) - 1)); From d8d83dcd45cf0bc29ba66e6eb1c09d588e8c91f0 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Sun, 13 Nov 2011 14:10:41 +0100 Subject: [PATCH 1997/2556] net: wireless: bcm4329: Skip dhd_bus_stop() if bus is already down --- drivers/net/wireless/bcm4329/dhd_linux.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index 7b4a099d54ec5..7b13c7668c137 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -1509,7 +1509,8 @@ dhd_dpc_thread(void *data) dhd_os_wake_unlock(&dhd->pub); } } else { - dhd_bus_stop(dhd->pub.bus, TRUE); + if (dhd->pub.up) + dhd_bus_stop(dhd->pub.bus, TRUE); dhd_os_wake_unlock(&dhd->pub); } } From 43abf5992b65b4943b5230118e5717b67471c9d8 Mon Sep 17 00:00:00 2001 From: "Choi, Jong-Hwan" Date: Tue, 15 Nov 2011 15:23:00 +0100 Subject: [PATCH 1998/2556] net: wireless:bcm4329: Fix Unknown escape '%' Change-Id: Icd859a5b7ab4784f718009d7e5b54690b2ef0aef --- drivers/net/wireless/bcm4329/wl_iw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcm4329/wl_iw.c b/drivers/net/wireless/bcm4329/wl_iw.c index 7fcdd05dfcb14..b98392665d831 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.c +++ b/drivers/net/wireless/bcm4329/wl_iw.c @@ -5885,7 +5885,7 @@ static int iwpriv_set_cscan(struct net_device *dev, struct iw_request_info *info int nssid = 0; int nchan = 0; - WL_TRACE(("\%s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n", + WL_TRACE(("%s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n", __FUNCTION__, info->cmd, info->flags, wrqu->data.pointer, wrqu->data.length)); From c8ebb06a108e6e7fbc6cf619ea32f444b221c06d Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 20 Dec 2011 16:40:28 +0100 Subject: [PATCH 1999/2556] net: wireless: bcm4329: Fix pno_enable if disassociated Conflicts: drivers/net/wireless/bcm4329/dhd_common.c --- drivers/net/wireless/bcm4329/dhd_common.c | 43 +++++++++++++++++++---- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/bcm4329/dhd_common.c b/drivers/net/wireless/bcm4329/dhd_common.c index 3af1c33cea3a4..7235a9e56dbaf 100644 --- a/drivers/net/wireless/bcm4329/dhd_common.c +++ b/drivers/net/wireless/bcm4329/dhd_common.c @@ -1895,6 +1895,41 @@ dhd_iscan_get_partial_result(void *dhdp, uint *scan_count) #endif +/* + * returns = TRUE if associated, FALSE if not associated + */ +bool is_associated(dhd_pub_t *dhd, void *bss_buf) +{ + char bssid[ETHER_ADDR_LEN], zbuf[ETHER_ADDR_LEN]; + int ret = -1; + + bzero(bssid, ETHER_ADDR_LEN); + bzero(zbuf, ETHER_ADDR_LEN); + + ret = dhdcdc_set_ioctl(dhd, 0, WLC_GET_BSSID, (char *)bssid, ETHER_ADDR_LEN); + DHD_TRACE((" %s WLC_GET_BSSID ioctl res = %d\n", __FUNCTION__, ret)); + + if (ret == BCME_NOTASSOCIATED) { + DHD_TRACE(("%s: not associated! res:%d\n", __FUNCTION__, ret)); + } + + if (ret < 0) + return FALSE; + + if ((memcmp(bssid, zbuf, ETHER_ADDR_LEN) != 0)) { + /* STA is assocoated BSSID is non zero */ + + if (bss_buf) { + /* return bss if caller provided buf */ + memcpy(bss_buf, bssid, ETHER_ADDR_LEN); + } + return TRUE; + } else { + DHD_TRACE(("%s: WLC_GET_BSSID ioctl returned zero bssid\n", __FUNCTION__)); + return FALSE; + } +} + /* Function to estimate possible DTIM_SKIP value */ int dhd_get_dtim_skip(dhd_pub_t *dhd) { @@ -1978,7 +2013,6 @@ int dhd_pno_clean(dhd_pub_t *dhd) int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled) { char iovbuf[128]; - uint8 bssid[6]; int ret = -1; if ((!dhd) && ((pfn_enabled != 0) || (pfn_enabled != 1))) { @@ -1989,12 +2023,7 @@ int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled) memset(iovbuf, 0, sizeof(iovbuf)); /* Check if disassoc to enable pno */ - if ((pfn_enabled) && \ - ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_GET_BSSID, \ - (char *)&bssid, ETHER_ADDR_LEN)) == BCME_NOTASSOCIATED)) { - DHD_TRACE(("%s pno enable called in disassoc mode\n", __FUNCTION__)); - } - else if (pfn_enabled) { + if (pfn_enabled && (is_associated(dhd, NULL) == TRUE)) { DHD_ERROR(("%s pno enable called in assoc mode ret=%d\n", \ __FUNCTION__, ret)); return ret; From 66d3b0219ffdf18f6ca7ed0c874588418040f9d0 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 20 Dec 2011 16:41:08 +0100 Subject: [PATCH 2000/2556] wireless: Protect regdomain change by mutex --- net/wireless/reg.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 37693b6ef23a6..6560d13902da4 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1747,6 +1747,7 @@ static void restore_alpha2(char *alpha2, bool reset_user) static void restore_regulatory_settings(bool reset_user) { char alpha2[2]; + char world_alpha2[2]; struct reg_beacon *reg_beacon, *btmp; mutex_lock(&cfg80211_mutex); @@ -1776,11 +1777,13 @@ static void restore_regulatory_settings(bool reset_user) /* First restore to the basic regulatory settings */ cfg80211_regdomain = cfg80211_world_regdom; + world_alpha2[0] = cfg80211_regdomain->alpha2[0]; + world_alpha2[1] = cfg80211_regdomain->alpha2[1]; mutex_unlock(®_mutex); mutex_unlock(&cfg80211_mutex); - regulatory_hint_core(cfg80211_regdomain->alpha2); + regulatory_hint_core(world_alpha2); /* * This restores the ieee80211_regdom module parameter From 91d12c145d84aae336ee874147cf378a859fae20 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Mon, 18 Apr 2011 17:29:14 -0400 Subject: [PATCH 2001/2556] ext4: check for ext[23] file system features when mounting as ext[23] Provide better emulation for ext[23] mode by enforcing that the file system does not have any unsupported file system features as defined by ext[23] when emulating the ext[23] file system driver when CONFIG_EXT4_USE_FOR_EXT23 is defined. This causes the file system type information in /proc/mounts to be correct for the automatically mounted root file system. This also means that "mount -t ext2 /dev/sda /mnt" will fail if /dev/sda contains an ext3 or ext4 file system, just as one would expect if the original ext2 file system driver were in use. Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 15 ++++++++++ fs/ext4/super.c | 74 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 80 insertions(+), 9 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 0783b4f272990..92ad5a9d6ea53 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1351,6 +1351,21 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei) #define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400 /* EA in inode */ #define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000 /* data in dirent */ +#define EXT2_FEATURE_COMPAT_SUPP EXT4_FEATURE_COMPAT_EXT_ATTR +#define EXT2_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \ + EXT4_FEATURE_INCOMPAT_META_BG) +#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT4_FEATURE_RO_COMPAT_BTREE_DIR) + +#define EXT3_FEATURE_COMPAT_SUPP EXT4_FEATURE_COMPAT_EXT_ATTR +#define EXT3_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \ + EXT4_FEATURE_INCOMPAT_RECOVER| \ + EXT4_FEATURE_INCOMPAT_META_BG) +#define EXT3_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT4_FEATURE_RO_COMPAT_BTREE_DIR) + #define EXT4_FEATURE_COMPAT_SUPP EXT2_FEATURE_COMPAT_EXT_ATTR #define EXT4_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \ EXT4_FEATURE_INCOMPAT_RECOVER| \ diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 9bada495f3dc3..b8c891cbf1b75 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -75,11 +75,27 @@ static void ext4_write_super(struct super_block *sb); static int ext4_freeze(struct super_block *sb); static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data); +static inline int ext2_feature_set_ok(struct super_block *sb); +static inline int ext3_feature_set_ok(struct super_block *sb); static int ext4_feature_set_ok(struct super_block *sb, int readonly); static void ext4_destroy_lazyinit_thread(void); static void ext4_unregister_li_request(struct super_block *sb); static void ext4_clear_request_list(void); +#if !defined(CONFIG_EXT2_FS) && !defined(CONFIG_EXT2_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT23) +static struct file_system_type ext2_fs_type = { + .owner = THIS_MODULE, + .name = "ext2", + .mount = ext4_mount, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; +#define IS_EXT2_SB(sb) ((sb)->s_bdev->bd_holder == &ext2_fs_type) +#else +#define IS_EXT2_SB(sb) (0) +#endif + + #if !defined(CONFIG_EXT3_FS) && !defined(CONFIG_EXT3_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT23) static struct file_system_type ext3_fs_type = { .owner = THIS_MODULE, @@ -3156,6 +3172,28 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) "feature flags set on rev 0 fs, " "running e2fsck is recommended"); + if (IS_EXT2_SB(sb)) { + if (ext2_feature_set_ok(sb)) + ext4_msg(sb, KERN_INFO, "mounting ext2 file system " + "using the ext4 subsystem"); + else { + ext4_msg(sb, KERN_ERR, "couldn't mount as ext2 due " + "to feature incompatibilities"); + goto failed_mount; + } + } + + if (IS_EXT3_SB(sb)) { + if (ext3_feature_set_ok(sb)) + ext4_msg(sb, KERN_INFO, "mounting ext3 file system " + "using the ext4 subsystem"); + else { + ext4_msg(sb, KERN_ERR, "couldn't mount as ext3 due " + "to feature incompatibilities"); + goto failed_mount; + } + } + /* * Check feature flags regardless of the revision level, since we * previously didn't change the revision level when setting the flags, @@ -4714,14 +4752,6 @@ static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags, } #if !defined(CONFIG_EXT2_FS) && !defined(CONFIG_EXT2_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT23) -static struct file_system_type ext2_fs_type = { - .owner = THIS_MODULE, - .name = "ext2", - .mount = ext4_mount, - .kill_sb = kill_block_super, - .fs_flags = FS_REQUIRES_DEV, -}; - static inline void register_as_ext2(void) { int err = register_filesystem(&ext2_fs_type); @@ -4734,10 +4764,22 @@ static inline void unregister_as_ext2(void) { unregister_filesystem(&ext2_fs_type); } + +static inline int ext2_feature_set_ok(struct super_block *sb) +{ + if (EXT4_HAS_INCOMPAT_FEATURE(sb, ~EXT2_FEATURE_INCOMPAT_SUPP)) + return 0; + if (sb->s_flags & MS_RDONLY) + return 1; + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, ~EXT2_FEATURE_RO_COMPAT_SUPP)) + return 0; + return 1; +} MODULE_ALIAS("ext2"); #else static inline void register_as_ext2(void) { } static inline void unregister_as_ext2(void) { } +static inline int ext2_feature_set_ok(struct super_block *sb) { return 0; } #endif #if !defined(CONFIG_EXT3_FS) && !defined(CONFIG_EXT3_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT23) @@ -4753,10 +4795,24 @@ static inline void unregister_as_ext3(void) { unregister_filesystem(&ext3_fs_type); } + +static inline int ext3_feature_set_ok(struct super_block *sb) +{ + if (EXT4_HAS_INCOMPAT_FEATURE(sb, ~EXT3_FEATURE_INCOMPAT_SUPP)) + return 0; + if (!EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_HAS_JOURNAL)) + return 0; + if (sb->s_flags & MS_RDONLY) + return 1; + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, ~EXT3_FEATURE_RO_COMPAT_SUPP)) + return 0; + return 1; +} MODULE_ALIAS("ext3"); #else static inline void register_as_ext3(void) { } static inline void unregister_as_ext3(void) { } +static inline int ext3_feature_set_ok(struct super_block *sb) { return 0; } #endif static struct file_system_type ext4_fs_type = { @@ -4840,8 +4896,8 @@ static int __init ext4_init_fs(void) err = init_inodecache(); if (err) goto out1; - register_as_ext2(); register_as_ext3(); + register_as_ext2(); err = register_filesystem(&ext4_fs_type); if (err) goto out; From c4413fe7b6190d727496937ec9c205423ee8f372 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Wed, 27 Jul 2011 21:20:00 -0400 Subject: [PATCH 2002/2556] msm: Add support for voice call recording --- arch/arm/mach-msm/qdsp6/pcm_in.c | 105 ++++++++++++++++++------------ arch/arm/mach-msm/qdsp6/q6audio.c | 98 +++++++++++++++++++++++++++- 2 files changed, 161 insertions(+), 42 deletions(-) diff --git a/arch/arm/mach-msm/qdsp6/pcm_in.c b/arch/arm/mach-msm/qdsp6/pcm_in.c index d5a8f7716eced..501ac682e73b9 100644 --- a/arch/arm/mach-msm/qdsp6/pcm_in.c +++ b/arch/arm/mach-msm/qdsp6/pcm_in.c @@ -21,23 +21,36 @@ #include #include #include +#include #include #include -#define BUFSZ (256) -static DEFINE_MUTEX(pcm_in_lock); -static uint32_t sample_rate = 8000; -static uint32_t channel_count = 1; -static uint32_t buffer_size = BUFSZ; -static int pcm_in_opened = 0; +struct msm_voicerec_mode { + uint32_t rec_mode; +}; + + +#define AUDIO_SET_INCALL _IOW(AUDIO_IOCTL_MAGIC, 19, struct msm_voicerec_mode) +#define AUDIO_FLAG_INCALL_MIXED 2 + +struct pcm { + struct audio_client *ac; + uint32_t sample_rate; + uint32_t channel_count; + uint32_t buffer_size; + uint32_t rec_mode; +}; + +#define BUFSZ (256) void audio_client_dump(struct audio_client *ac); static long q6_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + struct pcm *pcm = file->private_data; int rc = 0; switch (cmd) { @@ -61,17 +74,15 @@ static long q6_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } - mutex_lock(&pcm_in_lock); - if (file->private_data) { + if (pcm->ac) { rc = -EBUSY; } else { - file->private_data = q6audio_open_pcm( - buffer_size, sample_rate, channel_count, - AUDIO_FLAG_READ, acdb_id); - if (!file->private_data) + pcm->ac = q6audio_open_pcm(pcm->buffer_size, + pcm->sample_rate, pcm->channel_count, + pcm->rec_mode, acdb_id); + if (!pcm->ac) rc = -ENOMEM; } - mutex_unlock(&pcm_in_lock); break; } case AUDIO_STOP: @@ -96,17 +107,32 @@ static long q6_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg) rc = -EINVAL; break; } - sample_rate = config.sample_rate; - channel_count = config.channel_count; - buffer_size = config.buffer_size; + + pcm->sample_rate = config.sample_rate; + pcm->channel_count = config.channel_count; + pcm->buffer_size = config.buffer_size; + break; + } + case AUDIO_SET_INCALL: { + struct msm_voicerec_mode voicerec_mode; + if (copy_from_user(&voicerec_mode, (void *)arg, + sizeof(struct msm_voicerec_mode))) + return -EFAULT; + if (voicerec_mode.rec_mode != AUDIO_FLAG_READ && + voicerec_mode.rec_mode != AUDIO_FLAG_INCALL_MIXED) { + pcm->rec_mode = AUDIO_FLAG_READ; + pr_err("invalid rec_mode\n"); + rc = -EINVAL; + } else + pcm->rec_mode = voicerec_mode.rec_mode; break; } case AUDIO_GET_CONFIG: { struct msm_audio_config config; - config.buffer_size = buffer_size; + config.buffer_size = pcm->buffer_size; config.buffer_count = 2; - config.sample_rate = sample_rate; - config.channel_count = channel_count; + config.sample_rate = pcm->sample_rate; + config.channel_count = pcm->channel_count; config.unused[0] = 0; config.unused[1] = 0; config.unused[2] = 0; @@ -123,32 +149,33 @@ static long q6_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static int q6_in_open(struct inode *inode, struct file *file) { - int rc; + struct pcm *pcm; pr_info("pcm_in: open\n"); - mutex_lock(&pcm_in_lock); - if (pcm_in_opened) { - pr_err("pcm_in: busy\n"); - rc = -EBUSY; - } else { - pcm_in_opened = 1; - rc = 0; - } - mutex_unlock(&pcm_in_lock); - return rc; + pcm = kzalloc(sizeof(struct pcm), GFP_KERNEL); + + if (!pcm) + return -ENOMEM; + + pcm->channel_count = 1; + pcm->sample_rate = 8000; + pcm->buffer_size = BUFSZ; + pcm->rec_mode = AUDIO_FLAG_READ; + file->private_data = pcm; + return 0; } static ssize_t q6_in_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { + struct pcm *pcm = file->private_data; struct audio_client *ac; struct audio_buffer *ab; const char __user *start = buf; int xfer; int res; - mutex_lock(&pcm_in_lock); - ac = file->private_data; + ac = pcm->ac; if (!ac) { res = -ENODEV; goto fail; @@ -181,20 +208,18 @@ static ssize_t q6_in_read(struct file *file, char __user *buf, } fail: res = buf - start; - mutex_unlock(&pcm_in_lock); - return res; } static int q6_in_release(struct inode *inode, struct file *file) { + int rc = 0; - mutex_lock(&pcm_in_lock); - if (file->private_data) - rc = q6audio_close(file->private_data); - pcm_in_opened = 0; - mutex_unlock(&pcm_in_lock); - pr_info("pcm_in: release\n"); + struct pcm *pcm = file->private_data; + if (pcm->ac) + rc = q6audio_close(pcm->ac); + kfree(pcm); + pr_info("pcm_out: release\n"); return rc; } diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c index 5568fb6b201ad..d45e7be69e399 100644 --- a/arch/arm/mach-msm/qdsp6/q6audio.c +++ b/arch/arm/mach-msm/qdsp6/q6audio.c @@ -394,6 +394,7 @@ static int audio_out_open(struct audio_client *ac, uint32_t bufsz, return audio_ioctl(ac, &rpc, sizeof(rpc)); } +#if 0 static int audio_in_open(struct audio_client *ac, uint32_t bufsz, uint32_t rate, uint32_t channels) { @@ -416,6 +417,34 @@ static int audio_in_open(struct audio_client *ac, uint32_t bufsz, TRACE("%p: open in\n", ac); return audio_ioctl(ac, &rpc, sizeof(rpc)); } +#else +static int audio_in_open(struct audio_client *ac, uint32_t bufsz, + uint32_t flags, uint32_t rate, uint32_t channels) +{ + struct adsp_open_command rpc; + + memset(&rpc, 0, sizeof(rpc)); + + rpc.format.standard.format = ADSP_AUDIO_FORMAT_PCM; + rpc.format.standard.channels = channels; + rpc.format.standard.bits_per_sample = 16; + rpc.format.standard.sampling_rate = rate; + rpc.format.standard.is_signed = 1; + rpc.format.standard.is_interleaved = 1; + + rpc.hdr.opcode = ADSP_AUDIO_IOCTL_CMD_OPEN_READ; + rpc.device = ADSP_AUDIO_DEVICE_ID_DEFAULT; + if (flags == AUDIO_FLAG_READ) + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; + else + rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_MIXED_RECORD; + + rpc.buf_max_size = bufsz; + + TRACE("%p: open in\n", ac); + return audio_ioctl(ac, &rpc, sizeof(rpc)); +} +#endif static int audio_mp3_open(struct audio_client *ac, uint32_t bufsz, uint32_t rate, uint32_t channels) @@ -1419,6 +1448,31 @@ int q6audio_set_route(const char *name) return 0; } +static void adie_rx_path_enable(uint32_t acdb_id) +{ + if (audio_rx_path_id) { + adie_enable(); + adie_set_path(adie, audio_rx_path_id, ADIE_PATH_RX); + adie_set_path_freq_plan(adie, ADIE_PATH_RX, 48000); + + adie_proceed_to_stage(adie, ADIE_PATH_RX, + ADIE_STAGE_DIGITAL_READY); + adie_proceed_to_stage(adie, ADIE_PATH_RX, + ADIE_STAGE_DIGITAL_ANALOG_READY); + } +} + +static void q6_rx_path_enable(int reconf, uint32_t acdb_id) +{ + audio_update_acdb(audio_rx_device_id, acdb_id); + if (!reconf) + qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, audio_rx_device_id); + qdsp6_standby(ac_control); + qdsp6_start(ac_control); +} + + + struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, uint32_t channels, uint32_t flags, uint32_t acdb_id) { @@ -1435,7 +1489,7 @@ struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, ac->flags = flags; mutex_lock(&audio_path_lock); - +#if 0 if (ac->flags & AUDIO_FLAG_WRITE) { audio_rx_path_refcount++; if (audio_rx_path_refcount == 1) { @@ -1480,7 +1534,47 @@ struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, audio_rx_analog_enable(1); } } - +#else + if (ac->flags & AUDIO_FLAG_WRITE) { + audio_rx_path_refcount++; + if (audio_rx_path_refcount == 1) { + _audio_rx_clk_enable(); + q6_rx_path_enable(0, acdb_id); + adie_rx_path_enable(acdb_id); + } + } else { + /* TODO: consider concurrency with voice call */ + if (audio_tx_path_refcount > 0) { + tx_clk_freq = 8000; + } else { + tx_clk_freq = rate; + } + audio_tx_path_refcount++; + if (audio_tx_path_refcount == 1) { + tx_clk_freq = rate; + _audio_tx_clk_enable(); + _audio_tx_path_enable(0, acdb_id); + } + } + + for (retry = 5;;retry--) { + if (ac->flags & AUDIO_FLAG_WRITE) + rc = audio_out_open(ac, bufsz, rate, channels); + else + rc = audio_in_open(ac, bufsz, flags, rate, channels); + if (rc == 0) + break; + if (retry == 0) + BUG(); + pr_err("q6audio: open pcm error %d, retrying\n", rc); + msleep(1); + } + + if (ac->flags & AUDIO_FLAG_WRITE) { + if (audio_rx_path_refcount == 1) + audio_rx_analog_enable(1); + } +#endif mutex_unlock(&audio_path_lock); for (retry = 5;;retry--) { From ba67c6bf96699c6b3986be9d69a216f76d49a112 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 2 Nov 2011 21:31:14 -0700 Subject: [PATCH 2003/2556] qdsp6: Allow audio_rx_path_id to be zero, fixes Bluetooth In the refactoring done by commit 8913be80, the new adie_rx_path_enable added a check for audio_rx_path_id!=0 that didn't exist before. It becomes 0 for example when using Bluetooth voice dial. I found that CAF's kernels also do this, but they make the same check around every adie_proceed_to_stage() call throughout. Since we only have it in one place, the audio can get into a confused state and stop working. This patch removes the audio_rx_path_id check, so the logic more closely matches what we had before, and now BT is fine again. Change-Id: I9a4f437597b1a5f509517b88f8fd9369e6f7ad7d --- arch/arm/mach-msm/qdsp6/q6audio.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-msm/qdsp6/q6audio.c b/arch/arm/mach-msm/qdsp6/q6audio.c index d45e7be69e399..5882953a44366 100644 --- a/arch/arm/mach-msm/qdsp6/q6audio.c +++ b/arch/arm/mach-msm/qdsp6/q6audio.c @@ -1450,16 +1450,14 @@ int q6audio_set_route(const char *name) static void adie_rx_path_enable(uint32_t acdb_id) { - if (audio_rx_path_id) { - adie_enable(); - adie_set_path(adie, audio_rx_path_id, ADIE_PATH_RX); - adie_set_path_freq_plan(adie, ADIE_PATH_RX, 48000); - - adie_proceed_to_stage(adie, ADIE_PATH_RX, - ADIE_STAGE_DIGITAL_READY); - adie_proceed_to_stage(adie, ADIE_PATH_RX, - ADIE_STAGE_DIGITAL_ANALOG_READY); - } + adie_enable(); + adie_set_path(adie, audio_rx_path_id, ADIE_PATH_RX); + adie_set_path_freq_plan(adie, ADIE_PATH_RX, 48000); + + adie_proceed_to_stage(adie, ADIE_PATH_RX, + ADIE_STAGE_DIGITAL_READY); + adie_proceed_to_stage(adie, ADIE_PATH_RX, + ADIE_STAGE_DIGITAL_ANALOG_READY); } static void q6_rx_path_enable(int reconf, uint32_t acdb_id) From 78929837dfbb647a27249e3b63f4e1f4f157b11e Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 20 Dec 2011 11:25:41 +0100 Subject: [PATCH 2004/2556] mmc: Set suspend/resume bus operations if CONFIG_PM_RUNTIME is used --- drivers/mmc/core/bus.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 238ef08acd83e..3c4ebc97402cc 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -125,18 +125,19 @@ static int mmc_bus_remove(struct device *dev) return 0; } -static int mmc_bus_suspend(struct device *dev, pm_message_t state) +static int mmc_bus_pm_suspend(struct device *dev) { struct mmc_driver *drv = to_mmc_driver(dev->driver); struct mmc_card *card = mmc_dev_to_card(dev); int ret = 0; + pm_message_t state = { PM_EVENT_SUSPEND }; if (dev->driver && drv->suspend) ret = drv->suspend(card, state); return ret; } -static int mmc_bus_resume(struct device *dev) +static int mmc_bus_pm_resume(struct device *dev) { struct mmc_driver *drv = to_mmc_driver(dev->driver); struct mmc_card *card = mmc_dev_to_card(dev); @@ -148,7 +149,6 @@ static int mmc_bus_resume(struct device *dev) } #ifdef CONFIG_PM_RUNTIME - static int mmc_runtime_suspend(struct device *dev) { struct mmc_card *card = mmc_dev_to_card(dev); @@ -167,21 +167,13 @@ static int mmc_runtime_idle(struct device *dev) { return pm_runtime_suspend(dev); } +#endif /* CONFIG_PM_RUNTIME */ static const struct dev_pm_ops mmc_bus_pm_ops = { - .runtime_suspend = mmc_runtime_suspend, - .runtime_resume = mmc_runtime_resume, - .runtime_idle = mmc_runtime_idle, + SET_SYSTEM_SLEEP_PM_OPS(mmc_bus_pm_suspend, mmc_bus_pm_resume) + SET_RUNTIME_PM_OPS(mmc_runtime_suspend, mmc_runtime_resume, mmc_runtime_idle) }; -#define MMC_PM_OPS_PTR (&mmc_bus_pm_ops) - -#else /* !CONFIG_PM_RUNTIME */ - -#define MMC_PM_OPS_PTR NULL - -#endif /* !CONFIG_PM_RUNTIME */ - static struct bus_type mmc_bus_type = { .name = "mmc", .dev_attrs = mmc_dev_attrs, @@ -189,9 +181,7 @@ static struct bus_type mmc_bus_type = { .uevent = mmc_bus_uevent, .probe = mmc_bus_probe, .remove = mmc_bus_remove, - .suspend = mmc_bus_suspend, - .resume = mmc_bus_resume, - .pm = MMC_PM_OPS_PTR, + .pm = &mmc_bus_pm_ops, }; int mmc_register_bus(void) From b8c9229fcd1acfefa0119ef5d3741e993647299e Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Mon, 2 Jan 2012 11:00:14 -0500 Subject: [PATCH 2005/2556] arch: arm: msm: acpuclock-8x50: increase the low end by +.025 --- arch/arm/mach-msm/acpuclock-qsd8x50.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-msm/acpuclock-qsd8x50.c b/arch/arm/mach-msm/acpuclock-qsd8x50.c index e7d5f2efd3ea9..3978c60b25fb5 100644 --- a/arch/arm/mach-msm/acpuclock-qsd8x50.c +++ b/arch/arm/mach-msm/acpuclock-qsd8x50.c @@ -74,11 +74,11 @@ static unsigned long max_axi_rate; #define SRC_PLL1 3 /* 768 MHz */ struct clkctl_acpu_speed acpu_freq_tbl[] = { - { 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 975, 14000 }, - { 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 975, 14000 }, - { 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 1000, 29000 }, + { 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 1000, 14000 }, + { 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 1000, 14000 }, + { 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 1025, 29000 }, /* Work around for acpu resume hung, GPLL is turn off by arm9 */ - /*{ 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 1000, 29000 },*/ + /*{ 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 1025, 29000 },*/ { 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 1025, 58000 }, { 422400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0B, 0, 1050, 117000 }, { 460800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0C, 0, 1050, 117000 }, From 5b845fa758547d934db4bb22ef2f227abb2a55fa Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 1 Feb 2012 08:55:57 -0600 Subject: [PATCH 2006/2556] arm: asm: cacheflush: add invalidate/clean range --- arch/arm/include/asm/cacheflush.h | 23 +++++++++++++++++++++++ arch/arm/mm/cache-v7.S | 6 ++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h index f533a6a71924e..f572b36cf7beb 100644 --- a/arch/arm/include/asm/cacheflush.h +++ b/arch/arm/include/asm/cacheflush.h @@ -203,6 +203,21 @@ * * DMA Cache Coherency * =================== + * + * dma_inv_range(start, end) + * + * Invalidate (discard) the specified virtual address range. + * May not write back any entries. If 'start' or 'end' + * are not cache line aligned, those lines must be written + * back. + * - start - virtual start address + * - end - virtual end address + * + * dma_clean_range(start, end) + * + * Clean (write back) the specified virtual address range. + * - start - virtual start address + * - end - virtual end address * * dma_flush_range(start, end) * @@ -224,6 +239,8 @@ struct cpu_cache_fns { void (*dma_map_area)(const void *, size_t, int); void (*dma_unmap_area)(const void *, size_t, int); + void (*dma_inv_range)(const void *, const void *); + void (*dma_clean_range)(const void *, const void *); void (*dma_flush_range)(const void *, const void *); }; @@ -250,6 +267,8 @@ extern struct cpu_cache_fns cpu_cache; */ #define dmac_map_area cpu_cache.dma_map_area #define dmac_unmap_area cpu_cache.dma_unmap_area +#define dmac_inv_range cpu_cache.dma_inv_range +#define dmac_clean_range cpu_cache.dma_clean_range #define dmac_flush_range cpu_cache.dma_flush_range #else @@ -278,10 +297,14 @@ extern void __cpuc_flush_dcache_area(void *, size_t); */ #define dmac_map_area __glue(_CACHE,_dma_map_area) #define dmac_unmap_area __glue(_CACHE,_dma_unmap_area) +#define dmac_inv_range __glue(_CACHE,_dma_inv_range) +#define dmac_clean_range __glue(_CACHE,_dma_clean_range) #define dmac_flush_range __glue(_CACHE,_dma_flush_range) extern void dmac_map_area(const void *, size_t, int); extern void dmac_unmap_area(const void *, size_t, int); +extern void dmac_inv_range(const void *, const void *); +extern void dmac_clean_range(const void *, const void *); extern void dmac_flush_range(const void *, const void *); #endif diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S index d9b5cab4afd7b..3b97ac4fbe045 100644 --- a/arch/arm/mm/cache-v7.S +++ b/arch/arm/mm/cache-v7.S @@ -242,7 +242,7 @@ ENDPROC(v7_flush_kern_dcache_area) * - start - virtual start address of region * - end - virtual end address of region */ -v7_dma_inv_range: +ENTRY(v7_dma_inv_range) dcache_line_size r2, r3 sub r3, r2, #1 tst r0, r3 @@ -266,7 +266,7 @@ ENDPROC(v7_dma_inv_range) * - start - virtual start address of region * - end - virtual end address of region */ -v7_dma_clean_range: +ENTRY(v7_dma_clean_range) dcache_line_size r2, r3 sub r3, r2, #1 bic r0, r0, r3 @@ -336,5 +336,7 @@ ENTRY(v7_cache_fns) .long v7_flush_kern_dcache_area .long v7_dma_map_area .long v7_dma_unmap_area + .long v7_dma_inv_range + .long v7_dma_clean_range .long v7_dma_flush_range .size v7_cache_fns, . - v7_cache_fns From aedd47ae5e1194ec0896168eec22fdedce6a0d21 Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Fri, 15 Jul 2011 14:05:57 -0600 Subject: [PATCH 2007/2556] Kbuild: Export ashmem header file to user space CRs-fixed: 291124 Signed-off-by: Shubhraprakash Das --- include/linux/Kbuild | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 086e7eeb1d08f..d02cb077d5df5 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -45,6 +45,7 @@ header-y += agpgart.h header-y += aio_abi.h header-y += apm_bios.h header-y += arcfb.h +header-y += ashmem.h header-y += atalk.h header-y += atm.h header-y += atm_eni.h From 9307c4ed03ce3e950f8402333c6e75c7e0cb2ddf Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 1 Feb 2012 09:40:24 -0600 Subject: [PATCH 2008/2556] mm: ashmem: get/put file and flush/clean/inv range support --- include/linux/ashmem.h | 7 +++ mm/ashmem.c | 129 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) diff --git a/include/linux/ashmem.h b/include/linux/ashmem.h index 1976b10ef93eb..25a190e12ab79 100644 --- a/include/linux/ashmem.h +++ b/include/linux/ashmem.h @@ -44,5 +44,12 @@ struct ashmem_pin { #define ASHMEM_UNPIN _IOW(__ASHMEMIOC, 8, struct ashmem_pin) #define ASHMEM_GET_PIN_STATUS _IO(__ASHMEMIOC, 9) #define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10) +#define ASHMEM_CACHE_FLUSH_RANGE _IO(__ASHMEMIOC, 11) +#define ASHMEM_CACHE_CLEAN_RANGE _IO(__ASHMEMIOC, 12) +#define ASHMEM_CACHE_INV_RANGE _IO(__ASHMEMIOC, 13) + +int get_ashmem_file(int fd, struct file **filp, struct file **vm_file, + unsigned long *len); +void put_ashmem_file(struct file *file); #endif /* _LINUX_ASHMEM_H */ diff --git a/mm/ashmem.c b/mm/ashmem.c index f92eb34a14826..a16f3f7cfa2d2 100644 --- a/mm/ashmem.c +++ b/mm/ashmem.c @@ -29,6 +29,7 @@ #include #include #include +#include #define ASHMEM_NAME_PREFIX "dev/ashmem/" #define ASHMEM_NAME_PREFIX_LEN (sizeof(ASHMEM_NAME_PREFIX) - 1) @@ -45,6 +46,8 @@ struct ashmem_area { struct list_head unpinned_list; /* list of all ashmem areas */ struct file *file; /* the shmem-based backing file */ size_t size; /* size of the mapping, in bytes */ + unsigned long vm_start; /* Start address of vm_area + * which maps this ashmem */ unsigned long prot_mask; /* allowed prot bits, as vm_flags */ }; @@ -326,6 +329,7 @@ static int ashmem_mmap(struct file *file, struct vm_area_struct *vma) vma->vm_file = asma->file; } vma->vm_flags |= VM_CAN_NONLINEAR; + asma->vm_start = vma->vm_start; out: mutex_unlock(&ashmem_mutex); @@ -626,6 +630,69 @@ static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd, return ret; } +#ifdef CONFIG_OUTER_CACHE +static unsigned int virtaddr_to_physaddr(unsigned int virtaddr) +{ + unsigned int physaddr = 0; + pgd_t *pgd_ptr = NULL; + pmd_t *pmd_ptr = NULL; + pte_t *pte_ptr = NULL, pte; + + spin_lock(¤t->mm->page_table_lock); + pgd_ptr = pgd_offset(current->mm, virtaddr); + if (pgd_none(*pgd) || pgd_bad(*pgd)) { + pr_err("Failed to convert virtaddr %x to pgd_ptr\n", + virtaddr); + goto done; + } + + pmd_ptr = pmd_offset(pgd_ptr, virtaddr); + if (pmd_none(*pmd_ptr) || pmd_bad(*pmd_ptr)) { + pr_err("Failed to convert pgd_ptr %p to pmd_ptr\n", + (void *)pgd_ptr); + goto done; + } + + pte_ptr = pte_offset_map(pmd_ptr, virtaddr); + if (!pte_ptr) { + pr_err("Failed to convert pmd_ptr %p to pte_ptr\n", + (void *)pmd_ptr); + goto done; + } + pte = *pte_ptr; + physaddr = pte_pfn(pte); + pte_unmap(pte_ptr); +done: + spin_unlock(¤t->mm->page_table_lock); + physaddr <<= PAGE_SHIFT; + return physaddr; +} +#endif + +static int ashmem_cache_op(struct ashmem_area *asma, + void (*cache_func)(unsigned long vstart, unsigned long length, + unsigned long pstart)) +{ +#ifdef CONFIG_OUTER_CACHE + unsigned long vaddr; +#endif + mutex_lock(&ashmem_mutex); +#ifndef CONFIG_OUTER_CACHE + cache_func(asma->vm_start, asma->size, 0); +#else + for (vaddr = asma->vm_start; vaddr < asma->vm_start + asma->size; + vaddr += PAGE_SIZE) { + unsigned long physaddr; + physaddr = virtaddr_to_physaddr(vaddr); + if (!physaddr) + return -EINVAL; + cache_func(vaddr, PAGE_SIZE, physaddr); + } +#endif + mutex_unlock(&ashmem_mutex); + return 0; +} + static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct ashmem_area *asma = file->private_data; @@ -666,11 +733,73 @@ static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ashmem_shrink(&ashmem_shrinker, ret, GFP_KERNEL); } break; + case ASHMEM_CACHE_FLUSH_RANGE: + ret = ashmem_cache_op(asma, &clean_and_invalidate_caches); + break; + case ASHMEM_CACHE_CLEAN_RANGE: + ret = ashmem_cache_op(asma, &clean_caches); + break; + case ASHMEM_CACHE_INV_RANGE: + ret = ashmem_cache_op(asma, &invalidate_caches); + break; } return ret; } +static int is_ashmem_file(struct file *file) +{ + char fname[256], *name; + name = dentry_path(file->f_dentry, fname, 256); + return strcmp(name, "/ashmem") ? 0 : 1; +} + +int get_ashmem_file(int fd, struct file **filp, struct file **vm_file, + unsigned long *len) +{ + int ret = -1; + struct file *file = fget(fd); + *filp = NULL; + *vm_file = NULL; + if (unlikely(file == NULL)) { + pr_err("ashmem: %s: requested data from file " + "descriptor that doesn't exist.\n", __func__); + } else { + char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1]; + pr_debug("filp %p rdev %d pid %u(%s) file %p(%ld)" + " dev id: %d\n", filp, + file->f_dentry->d_inode->i_rdev, + current->pid, get_task_comm(currtask_name, current), + file, file_count(file), + MINOR(file->f_dentry->d_inode->i_rdev)); + if (is_ashmem_file(file)) { + struct ashmem_area *asma = file->private_data; + *filp = file; + *vm_file = asma->file; + *len = asma->size; + ret = 0; + } else { + pr_err("file descriptor is not an ashmem " + "region fd: %d\n", fd); + fput(file); + } + } + return ret; +} +EXPORT_SYMBOL(get_ashmem_file); + +void put_ashmem_file(struct file *file) +{ + char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1]; + pr_debug("rdev %d pid %u(%s) file %p(%ld)" " dev id: %d\n", + file->f_dentry->d_inode->i_rdev, current->pid, + get_task_comm(currtask_name, current), file, + file_count(file), MINOR(file->f_dentry->d_inode->i_rdev)); + if (file && is_ashmem_file(file)) + fput(file); +} +EXPORT_SYMBOL(put_ashmem_file); + static struct file_operations ashmem_fops = { .owner = THIS_MODULE, .open = ashmem_open, From ad45d83abc03a76e6d6a65e4a08972aef2e8c3f1 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 1 Feb 2012 09:41:03 -0600 Subject: [PATCH 2009/2556] arm: msm: memory: clean/inv cache support --- arch/arm/mach-msm/include/mach/memory.h | 6 ++++ arch/arm/mach-msm/memory.c | 48 +++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h index 6e202fdfefd86..a9f235799fb8b 100644 --- a/arch/arm/mach-msm/include/mach/memory.h +++ b/arch/arm/mach-msm/include/mach/memory.h @@ -40,6 +40,12 @@ #define HAS_ARCH_IO_REMAP_PFN_RANGE +#ifndef __ASSEMBLY__ +void clean_and_invalidate_caches(unsigned long, unsigned long, unsigned long); +void clean_caches(unsigned long, unsigned long, unsigned long); +void invalidate_caches(unsigned long, unsigned long, unsigned long); +#endif + #define CONSISTENT_DMA_SIZE (4*SZ_1M) #ifdef CONFIG_ARCH_MSM_SCORPION diff --git a/arch/arm/mach-msm/memory.c b/arch/arm/mach-msm/memory.c index 3be79d8f9de78..4157211eb6119 100644 --- a/arch/arm/mach-msm/memory.c +++ b/arch/arm/mach-msm/memory.c @@ -27,3 +27,51 @@ int arch_io_remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, } return remap_pfn_range(vma, addr, pfn, size, prot); } + +#define CACHE_LINE_SIZE 32 + +/* These cache related routines make the assumption that the associated + * physical memory is contiguous. They will operate on all (L1 + * and L2 if present) caches. + */ +void clean_and_invalidate_caches(unsigned long vstart, + unsigned long length, unsigned long pstart) +{ + unsigned long vaddr; + + for (vaddr = vstart; vaddr < vstart + length; vaddr += CACHE_LINE_SIZE) + asm ("mcr p15, 0, %0, c7, c14, 1" : : "r" (vaddr)); +#ifdef CONFIG_OUTER_CACHE + outer_flush_range(pstart, pstart + length); +#endif + asm ("mcr p15, 0, %0, c7, c10, 4" : : "r" (0)); + asm ("mcr p15, 0, %0, c7, c5, 0" : : "r" (0)); +} + +void clean_caches(unsigned long vstart, + unsigned long length, unsigned long pstart) +{ + unsigned long vaddr; + + for (vaddr = vstart; vaddr < vstart + length; vaddr += CACHE_LINE_SIZE) + asm ("mcr p15, 0, %0, c7, c10, 1" : : "r" (vaddr)); +#ifdef CONFIG_OUTER_CACHE + outer_clean_range(pstart, pstart + length); +#endif + asm ("mcr p15, 0, %0, c7, c10, 4" : : "r" (0)); + asm ("mcr p15, 0, %0, c7, c5, 0" : : "r" (0)); +} + +void invalidate_caches(unsigned long vstart, + unsigned long length, unsigned long pstart) +{ + unsigned long vaddr; + + for (vaddr = vstart; vaddr < vstart + length; vaddr += CACHE_LINE_SIZE) + asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (vaddr)); +#ifdef CONFIG_OUTER_CACHE + outer_inv_range(pstart, pstart + length); +#endif + asm ("mcr p15, 0, %0, c7, c10, 4" : : "r" (0)); + asm ("mcr p15, 0, %0, c7, c5, 0" : : "r" (0)); +} From f3747d82cb1bdeb21bd3596bf41cc06a28c1aa8b Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 1 Feb 2012 10:09:01 -0600 Subject: [PATCH 2010/2556] lib: bitmap/genalloc: backport from cafs 3.0 --- include/linux/bitmap.h | 29 ++++-- include/linux/genalloc.h | 68 ++++++++----- lib/bitmap.c | 135 ++++++++++++++++++++----- lib/genalloc.c | 208 +++++++++++++++++++++++++-------------- 4 files changed, 314 insertions(+), 126 deletions(-) diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h index daf8c480c7867..50e2c16b20f64 100644 --- a/include/linux/bitmap.h +++ b/include/linux/bitmap.h @@ -45,6 +45,7 @@ * bitmap_set(dst, pos, nbits) Set specified bit area * bitmap_clear(dst, pos, nbits) Clear specified bit area * bitmap_find_next_zero_area(buf, len, pos, n, mask) Find bit free area + * bitmap_find_next_zero_area_off(buf, len, pos, n, mask) as above * bitmap_shift_right(dst, src, n, nbits) *dst = *src >> n * bitmap_shift_left(dst, src, n, nbits) *dst = *src << n * bitmap_remap(dst, src, old, new, nbits) *dst = map(old, new)(src) @@ -55,7 +56,8 @@ * bitmap_parse(buf, buflen, dst, nbits) Parse bitmap dst from kernel buf * bitmap_parse_user(ubuf, ulen, dst, nbits) Parse bitmap dst from user buf * bitmap_scnlistprintf(buf, len, src, nbits) Print bitmap src as list to buf - * bitmap_parselist(buf, dst, nbits) Parse bitmap dst from list + * bitmap_parselist(buf, dst, nbits) Parse bitmap dst from kernel buf + * bitmap_parselist_user(buf, dst, nbits) Parse bitmap dst from user buf * bitmap_find_free_region(bitmap, bits, order) Find and allocate bit region * bitmap_release_region(bitmap, pos, order) Free specified bit region * bitmap_allocate_region(bitmap, pos, order) Allocate specified bit region @@ -113,11 +115,24 @@ extern int __bitmap_weight(const unsigned long *bitmap, int bits); extern void bitmap_set(unsigned long *map, int i, int len); extern void bitmap_clear(unsigned long *map, int start, int nr); -extern unsigned long bitmap_find_next_zero_area(unsigned long *map, - unsigned long size, - unsigned long start, - unsigned int nr, - unsigned long align_mask); + +extern unsigned long bitmap_find_next_zero_area_off(unsigned long *map, + unsigned long size, + unsigned long start, + unsigned int nr, + unsigned long align_mask, + unsigned long align_offset); + +static inline unsigned long +bitmap_find_next_zero_area(unsigned long *map, + unsigned long size, + unsigned long start, + unsigned int nr, + unsigned long align_mask) +{ + return bitmap_find_next_zero_area_off(map, size, start, nr, + align_mask, 0); +} extern int bitmap_scnprintf(char *buf, unsigned int len, const unsigned long *src, int nbits); @@ -129,6 +144,8 @@ extern int bitmap_scnlistprintf(char *buf, unsigned int len, const unsigned long *src, int nbits); extern int bitmap_parselist(const char *buf, unsigned long *maskp, int nmaskbits); +extern int bitmap_parselist_user(const char __user *ubuf, unsigned int ulen, + unsigned long *dst, int nbits); extern void bitmap_remap(unsigned long *dst, const unsigned long *src, const unsigned long *old, const unsigned long *new, int bits); extern int bitmap_bitremap(int oldbit, diff --git a/include/linux/genalloc.h b/include/linux/genalloc.h index 9869ef3674acb..3bc7dd7394bf0 100644 --- a/include/linux/genalloc.h +++ b/include/linux/genalloc.h @@ -8,29 +8,53 @@ * Version 2. See the file COPYING for more details. */ +#ifndef __GENALLOC_H__ +#define __GENALLOC_H__ -/* - * General purpose special memory pool descriptor. +struct gen_pool; + +struct gen_pool *__must_check gen_pool_create(unsigned order, int nid); + +void gen_pool_destroy(struct gen_pool *pool); + +unsigned long __must_check +gen_pool_alloc_aligned(struct gen_pool *pool, size_t size, + unsigned alignment_order); + +/** + * gen_pool_alloc() - allocate special memory from the pool + * @pool: Pool to allocate from. + * @size: Number of bytes to allocate from the pool. + * + * Allocate the requested number of bytes from the specified pool. + * Uses a first-fit algorithm. */ -struct gen_pool { - rwlock_t lock; - struct list_head chunks; /* list of chunks in this pool */ - int min_alloc_order; /* minimum allocation order */ -}; +static inline unsigned long __must_check +gen_pool_alloc(struct gen_pool *pool, size_t size) +{ + return gen_pool_alloc_aligned(pool, size, 0); +} -/* - * General purpose special memory pool chunk descriptor. +void gen_pool_free(struct gen_pool *pool, unsigned long addr, size_t size); + +extern phys_addr_t gen_pool_virt_to_phys(struct gen_pool *pool, unsigned long); +extern int gen_pool_add_virt(struct gen_pool *, unsigned long, phys_addr_t, + size_t, int); +/** + * gen_pool_add - add a new chunk of special memory to the pool + * @pool: pool to add new memory chunk to + * @addr: starting address of memory chunk to add to pool + * @size: size in bytes of the memory chunk to add to pool + * @nid: node id of the node the chunk structure and bitmap should be + * allocated on, or -1 + * + * Add a new chunk of special memory to the specified pool. + * + * Returns 0 on success or a -ve errno on failure. */ -struct gen_pool_chunk { - spinlock_t lock; - struct list_head next_chunk; /* next chunk in pool */ - unsigned long start_addr; /* starting address of memory chunk */ - unsigned long end_addr; /* ending address of memory chunk */ - unsigned long bits[0]; /* bitmap for allocating memory chunk */ -}; - -extern struct gen_pool *gen_pool_create(int, int); -extern int gen_pool_add(struct gen_pool *, unsigned long, size_t, int); -extern void gen_pool_destroy(struct gen_pool *); -extern unsigned long gen_pool_alloc(struct gen_pool *, size_t); -extern void gen_pool_free(struct gen_pool *, unsigned long, size_t); +static inline int __must_check gen_pool_add(struct gen_pool *pool, unsigned long addr, + size_t size, int nid) +{ + return gen_pool_add_virt(pool, addr, -1, size, nid); +} +#endif /* __GENALLOC_H__ */ diff --git a/lib/bitmap.c b/lib/bitmap.c index 741fae905ae3b..cf12bb86d7c20 100644 --- a/lib/bitmap.c +++ b/lib/bitmap.c @@ -315,30 +315,32 @@ void bitmap_clear(unsigned long *map, int start, int nr) } EXPORT_SYMBOL(bitmap_clear); -/* +/** * bitmap_find_next_zero_area - find a contiguous aligned zero area * @map: The address to base the search on * @size: The bitmap size in bits * @start: The bitnumber to start searching at * @nr: The number of zeroed bits we're looking for * @align_mask: Alignment mask for zero area + * @align_offset: Alignment offset for zero area. * * The @align_mask should be one less than a power of 2; the effect is that - * the bit offset of all zero areas this function finds is multiples of that - * power of 2. A @align_mask of 0 means no alignment is required. + * the bit offset of all zero areas this function finds plus @align_offset + * is multiple of that power of 2. */ -unsigned long bitmap_find_next_zero_area(unsigned long *map, - unsigned long size, - unsigned long start, - unsigned int nr, - unsigned long align_mask) +unsigned long bitmap_find_next_zero_area_off(unsigned long *map, + unsigned long size, + unsigned long start, + unsigned int nr, + unsigned long align_mask, + unsigned long align_offset) { unsigned long index, end, i; again: index = find_next_zero_bit(map, size, start); /* Align allocation */ - index = __ALIGN_MASK(index, align_mask); + index = __ALIGN_MASK(index + align_offset, align_mask) - align_offset; end = index + nr; if (end > size) @@ -350,7 +352,7 @@ unsigned long bitmap_find_next_zero_area(unsigned long *map, } return index; } -EXPORT_SYMBOL(bitmap_find_next_zero_area); +EXPORT_SYMBOL(bitmap_find_next_zero_area_off); /* * Bitmap printing & parsing functions: first version by Bill Irwin, @@ -571,8 +573,11 @@ int bitmap_scnlistprintf(char *buf, unsigned int buflen, EXPORT_SYMBOL(bitmap_scnlistprintf); /** - * bitmap_parselist - convert list format ASCII string to bitmap - * @bp: read nul-terminated user string from this buffer + * __bitmap_parselist - convert list format ASCII string to bitmap + * @buf: read nul-terminated user string from this buffer + * @buflen: buffer size in bytes. If string is smaller than this + * then it must be terminated with a \0. + * @is_user: location of buffer, 0 indicates kernel space * @maskp: write resulting mask here * @nmaskbits: number of bits in mask to be written * @@ -587,20 +592,63 @@ EXPORT_SYMBOL(bitmap_scnlistprintf); * %-EINVAL: invalid character in string * %-ERANGE: bit number specified too large for mask */ -int bitmap_parselist(const char *bp, unsigned long *maskp, int nmaskbits) +static int __bitmap_parselist(const char *buf, unsigned int buflen, + int is_user, unsigned long *maskp, + int nmaskbits) { unsigned a, b; + int c, old_c, totaldigits; + const char __user *ubuf = buf; + int exp_digit, in_range; + totaldigits = c = 0; bitmap_zero(maskp, nmaskbits); do { - if (!isdigit(*bp)) - return -EINVAL; - b = a = simple_strtoul(bp, (char **)&bp, BASEDEC); - if (*bp == '-') { - bp++; - if (!isdigit(*bp)) + exp_digit = 1; + in_range = 0; + a = b = 0; + + /* Get the next cpu# or a range of cpu#'s */ + while (buflen) { + old_c = c; + if (is_user) { + if (__get_user(c, ubuf++)) + return -EFAULT; + } else + c = *buf++; + buflen--; + if (isspace(c)) + continue; + + /* + * If the last character was a space and the current + * character isn't '\0', we've got embedded whitespace. + * This is a no-no, so throw an error. + */ + if (totaldigits && c && isspace(old_c)) + return -EINVAL; + + /* A '\0' or a ',' signal the end of a cpu# or range */ + if (c == '\0' || c == ',') + break; + + if (c == '-') { + if (exp_digit || in_range) + return -EINVAL; + b = 0; + in_range = 1; + exp_digit = 1; + continue; + } + + if (!isdigit(c)) return -EINVAL; - b = simple_strtoul(bp, (char **)&bp, BASEDEC); + + b = b * 10 + (c - '0'); + if (!in_range) + a = b; + exp_digit = 0; + totaldigits++; } if (!(a <= b)) return -EINVAL; @@ -610,13 +658,52 @@ int bitmap_parselist(const char *bp, unsigned long *maskp, int nmaskbits) set_bit(a, maskp); a++; } - if (*bp == ',') - bp++; - } while (*bp != '\0' && *bp != '\n'); + } while (buflen && c == ','); return 0; } + +int bitmap_parselist(const char *bp, unsigned long *maskp, int nmaskbits) +{ + char *nl = strchr(bp, '\n'); + int len; + + if (nl) + len = nl - bp; + else + len = strlen(bp); + + return __bitmap_parselist(bp, len, 0, maskp, nmaskbits); +} EXPORT_SYMBOL(bitmap_parselist); + +/** + * bitmap_parselist_user() + * + * @ubuf: pointer to user buffer containing string. + * @ulen: buffer size in bytes. If string is smaller than this + * then it must be terminated with a \0. + * @maskp: pointer to bitmap array that will contain result. + * @nmaskbits: size of bitmap, in bits. + * + * Wrapper for bitmap_parselist(), providing it with user buffer. + * + * We cannot have this as an inline function in bitmap.h because it needs + * linux/uaccess.h to get the access_ok() declaration and this causes + * cyclic dependencies. + */ +int bitmap_parselist_user(const char __user *ubuf, + unsigned int ulen, unsigned long *maskp, + int nmaskbits) +{ + if (!access_ok(VERIFY_READ, ubuf, ulen)) + return -EFAULT; + return __bitmap_parselist((const char *)ubuf, + ulen, 1, maskp, nmaskbits); +} +EXPORT_SYMBOL(bitmap_parselist_user); + + /** * bitmap_pos_to_ord - find ordinal of set bit at given position in bitmap * @buf: pointer to a bitmap @@ -830,7 +917,7 @@ EXPORT_SYMBOL(bitmap_bitremap); * @orig (i.e. bits 3, 5, 7 and 9) were also set. * * When bit 11 is set in @orig, it means turn on the bit in - * @dst corresponding to whatever is the twelth bit that is + * @dst corresponding to whatever is the twelfth bit that is * turned on in @relmap. In the above example, there were * only ten bits turned on in @relmap (30..39), so that bit * 11 was set in @orig had no affect on @dst. diff --git a/lib/genalloc.c b/lib/genalloc.c index 1923f1490e726..c7b9b9c41b780 100644 --- a/lib/genalloc.c +++ b/lib/genalloc.c @@ -16,53 +16,86 @@ #include +/* General purpose special memory pool descriptor. */ +struct gen_pool { + rwlock_t lock; /* protects chunks list */ + struct list_head chunks; /* list of chunks in this pool */ + unsigned order; /* minimum allocation order */ +}; + +/* General purpose special memory pool chunk descriptor. */ +struct gen_pool_chunk { + spinlock_t lock; /* protects bits */ + struct list_head next_chunk; /* next chunk in pool */ + phys_addr_t phys_addr; /* physical starting address of memory chunk */ + unsigned long start; /* start of memory chunk */ + unsigned long size; /* number of bits */ + unsigned long bits[0]; /* bitmap for allocating memory chunk */ +}; + /** - * gen_pool_create - create a new special memory pool - * @min_alloc_order: log base 2 of number of bytes each bitmap bit represents - * @nid: node id of the node the pool structure should be allocated on, or -1 + * gen_pool_create() - create a new special memory pool + * @order: Log base 2 of number of bytes each bitmap bit + * represents. + * @nid: Node id of the node the pool structure should be allocated + * on, or -1. This will be also used for other allocations. * * Create a new special memory pool that can be used to manage special purpose * memory not managed by the regular kmalloc/kfree interface. */ -struct gen_pool *gen_pool_create(int min_alloc_order, int nid) +struct gen_pool *__must_check gen_pool_create(unsigned order, int nid) { struct gen_pool *pool; - pool = kmalloc_node(sizeof(struct gen_pool), GFP_KERNEL, nid); - if (pool != NULL) { + if (WARN_ON(order >= BITS_PER_LONG)) + return NULL; + + pool = kmalloc_node(sizeof *pool, GFP_KERNEL, nid); + if (pool) { rwlock_init(&pool->lock); INIT_LIST_HEAD(&pool->chunks); - pool->min_alloc_order = min_alloc_order; + pool->order = order; } return pool; } EXPORT_SYMBOL(gen_pool_create); /** - * gen_pool_add - add a new chunk of special memory to the pool + * gen_pool_add_virt - add a new chunk of special memory to the pool * @pool: pool to add new memory chunk to - * @addr: starting address of memory chunk to add to pool + * @virt: virtual starting address of memory chunk to add to pool + * @phys: physical starting address of memory chunk to add to pool * @size: size in bytes of the memory chunk to add to pool * @nid: node id of the node the chunk structure and bitmap should be * allocated on, or -1 * * Add a new chunk of special memory to the specified pool. + * + * Returns 0 on success or a -ve errno on failure. */ -int gen_pool_add(struct gen_pool *pool, unsigned long addr, size_t size, - int nid) +int __must_check gen_pool_add_virt(struct gen_pool *pool, unsigned long virt, phys_addr_t phys, + size_t size, int nid) { struct gen_pool_chunk *chunk; - int nbits = size >> pool->min_alloc_order; - int nbytes = sizeof(struct gen_pool_chunk) + - (nbits + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + size_t nbytes; - chunk = kmalloc_node(nbytes, GFP_KERNEL | __GFP_ZERO, nid); - if (unlikely(chunk == NULL)) - return -1; + if (WARN_ON(!virt || virt + size < virt || + (virt & ((1 << pool->order) - 1)))) + return -EINVAL; + + size = size >> pool->order; + if (WARN_ON(!size)) + return -EINVAL; + + nbytes = sizeof *chunk + BITS_TO_LONGS(size) * sizeof *chunk->bits; + chunk = kzalloc_node(nbytes, GFP_KERNEL, nid); + if (!chunk) + return -ENOMEM; spin_lock_init(&chunk->lock); - chunk->start_addr = addr; - chunk->end_addr = addr + size; + chunk->phys_addr = phys; + chunk->start = virt >> pool->order; + chunk->size = size; write_lock(&pool->lock); list_add(&chunk->next_chunk, &pool->chunks); @@ -70,118 +103,145 @@ int gen_pool_add(struct gen_pool *pool, unsigned long addr, size_t size, return 0; } -EXPORT_SYMBOL(gen_pool_add); +EXPORT_SYMBOL(gen_pool_add_virt); /** - * gen_pool_destroy - destroy a special memory pool - * @pool: pool to destroy + * gen_pool_virt_to_phys - return the physical address of memory + * @pool: pool to allocate from + * @addr: starting address of memory + * + * Returns the physical address on success, or -1 on error. + */ +phys_addr_t gen_pool_virt_to_phys(struct gen_pool *pool, unsigned long addr) +{ + struct list_head *_chunk; + struct gen_pool_chunk *chunk; + + read_lock(&pool->lock); + list_for_each(_chunk, &pool->chunks) { + chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk); + + if (addr >= chunk->start && + addr < (chunk->start + chunk->size)) + return chunk->phys_addr + addr - chunk->start; + } + read_unlock(&pool->lock); + + return -1; +} +EXPORT_SYMBOL(gen_pool_virt_to_phys); + +/** + * gen_pool_destroy() - destroy a special memory pool + * @pool: Pool to destroy. * * Destroy the specified special memory pool. Verifies that there are no * outstanding allocations. */ void gen_pool_destroy(struct gen_pool *pool) { - struct list_head *_chunk, *_next_chunk; struct gen_pool_chunk *chunk; - int order = pool->min_alloc_order; - int bit, end_bit; - + int bit; - list_for_each_safe(_chunk, _next_chunk, &pool->chunks) { - chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk); + while (!list_empty(&pool->chunks)) { + chunk = list_entry(pool->chunks.next, struct gen_pool_chunk, + next_chunk); list_del(&chunk->next_chunk); - end_bit = (chunk->end_addr - chunk->start_addr) >> order; - bit = find_next_bit(chunk->bits, end_bit, 0); - BUG_ON(bit < end_bit); + bit = find_next_bit(chunk->bits, chunk->size, 0); + BUG_ON(bit < chunk->size); kfree(chunk); } kfree(pool); - return; } EXPORT_SYMBOL(gen_pool_destroy); /** - * gen_pool_alloc - allocate special memory from the pool - * @pool: pool to allocate from - * @size: number of bytes to allocate from the pool + * gen_pool_alloc_aligned() - allocate special memory from the pool + * @pool: Pool to allocate from. + * @size: Number of bytes to allocate from the pool. + * @alignment_order: Order the allocated space should be + * aligned to (eg. 20 means allocated space + * must be aligned to 1MiB). * * Allocate the requested number of bytes from the specified pool. * Uses a first-fit algorithm. */ -unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size) +unsigned long __must_check +gen_pool_alloc_aligned(struct gen_pool *pool, size_t size, + unsigned alignment_order) { - struct list_head *_chunk; + unsigned long addr, align_mask = 0, flags, start; struct gen_pool_chunk *chunk; - unsigned long addr, flags; - int order = pool->min_alloc_order; - int nbits, start_bit, end_bit; if (size == 0) return 0; - nbits = (size + (1UL << order) - 1) >> order; + if (alignment_order > pool->order) + align_mask = (1 << (alignment_order - pool->order)) - 1; - read_lock(&pool->lock); - list_for_each(_chunk, &pool->chunks) { - chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk); + size = (size + (1UL << pool->order) - 1) >> pool->order; - end_bit = (chunk->end_addr - chunk->start_addr) >> order; + read_lock(&pool->lock); + list_for_each_entry(chunk, &pool->chunks, next_chunk) { + if (chunk->size < size) + continue; spin_lock_irqsave(&chunk->lock, flags); - start_bit = bitmap_find_next_zero_area(chunk->bits, end_bit, 0, - nbits, 0); - if (start_bit >= end_bit) { + start = bitmap_find_next_zero_area_off(chunk->bits, chunk->size, + 0, size, align_mask, + chunk->start); + if (start >= chunk->size) { spin_unlock_irqrestore(&chunk->lock, flags); continue; } - addr = chunk->start_addr + ((unsigned long)start_bit << order); - - bitmap_set(chunk->bits, start_bit, nbits); + bitmap_set(chunk->bits, start, size); spin_unlock_irqrestore(&chunk->lock, flags); - read_unlock(&pool->lock); - return addr; + addr = (chunk->start + start) << pool->order; + goto done; } + + addr = 0; +done: read_unlock(&pool->lock); - return 0; + return addr; } -EXPORT_SYMBOL(gen_pool_alloc); +EXPORT_SYMBOL(gen_pool_alloc_aligned); /** - * gen_pool_free - free allocated special memory back to the pool - * @pool: pool to free to - * @addr: starting address of memory to free back to pool - * @size: size in bytes of memory to free + * gen_pool_free() - free allocated special memory back to the pool + * @pool: Pool to free to. + * @addr: Starting address of memory to free back to pool. + * @size: Size in bytes of memory to free. * * Free previously allocated special memory back to the specified pool. */ void gen_pool_free(struct gen_pool *pool, unsigned long addr, size_t size) { - struct list_head *_chunk; struct gen_pool_chunk *chunk; unsigned long flags; - int order = pool->min_alloc_order; - int bit, nbits; - nbits = (size + (1UL << order) - 1) >> order; + if (!size) + return; - read_lock(&pool->lock); - list_for_each(_chunk, &pool->chunks) { - chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk); + addr = addr >> pool->order; + size = (size + (1UL << pool->order) - 1) >> pool->order; + + BUG_ON(addr + size < addr); - if (addr >= chunk->start_addr && addr < chunk->end_addr) { - BUG_ON(addr + size > chunk->end_addr); + read_lock(&pool->lock); + list_for_each_entry(chunk, &pool->chunks, next_chunk) + if (addr >= chunk->start && + addr + size <= chunk->start + chunk->size) { spin_lock_irqsave(&chunk->lock, flags); - bit = (addr - chunk->start_addr) >> order; - while (nbits--) - __clear_bit(bit++, chunk->bits); + bitmap_clear(chunk->bits, addr - chunk->start, size); spin_unlock_irqrestore(&chunk->lock, flags); - break; + goto done; } - } - BUG_ON(nbits > 0); + BUG_ON(1); +done: read_unlock(&pool->lock); } EXPORT_SYMBOL(gen_pool_free); From 89ca98f4fcffc1f9e387df00221bcb81bf589ab1 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 1 Feb 2012 10:09:29 -0600 Subject: [PATCH 2011/2556] lib: backport memory_alloc from cafs 3.0 --- include/linux/memory_alloc.h | 58 +++++ lib/Makefile | 3 +- lib/memory_alloc.c | 425 +++++++++++++++++++++++++++++++++++ 3 files changed, 485 insertions(+), 1 deletion(-) create mode 100644 include/linux/memory_alloc.h create mode 100644 lib/memory_alloc.c diff --git a/include/linux/memory_alloc.h b/include/linux/memory_alloc.h new file mode 100644 index 0000000000000..e7049f8a355ea --- /dev/null +++ b/include/linux/memory_alloc.h @@ -0,0 +1,58 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_MEMALLOC_H +#define _LINUX_MEMALLOC_H + +#include +#include +#include + +struct mem_pool { + struct mutex pool_mutex; + struct gen_pool *gpool; + unsigned long paddr; + unsigned long size; + unsigned long free; + unsigned int id; +}; + +struct alloc { + struct rb_node rb_node; + void *vaddr; + unsigned long paddr; + struct mem_pool *mpool; + unsigned long len; + void *caller; +}; + +struct mem_pool *initialize_memory_pool(unsigned long start, + unsigned long size, int mem_type); + +void *allocate_contiguous_memory(unsigned long size, + int mem_type, unsigned long align, int cached); + +unsigned long _allocate_contiguous_memory_nomap(unsigned long size, + int mem_type, unsigned long align, void *caller); + +unsigned long allocate_contiguous_memory_nomap(unsigned long size, + int mem_type, unsigned long align); + +void free_contiguous_memory(void *addr); +void free_contiguous_memory_by_paddr(unsigned long paddr); + +unsigned long memory_pool_node_paddr(void *vaddr); + +unsigned long memory_pool_node_len(void *vaddr); + +int memory_pool_init(void); +#endif /* _LINUX_MEMALLOC_H */ diff --git a/lib/Makefile b/lib/Makefile index dfbf7d82ce98b..2990d70ebe872 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -12,7 +12,8 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \ idr.o int_sqrt.o extable.o prio_tree.o \ sha1.o irq_regs.o reciprocal_div.o argv_split.o \ proportions.o prio_heap.o ratelimit.o show_mem.o \ - is_single_threaded.o plist.o decompress.o + is_single_threaded.o plist.o decompress.o \ + memory_alloc.o lib-$(CONFIG_MMU) += ioremap.o lib-$(CONFIG_SMP) += cpumask.o diff --git a/lib/memory_alloc.c b/lib/memory_alloc.c new file mode 100644 index 0000000000000..d931e148e5b05 --- /dev/null +++ b/lib/memory_alloc.c @@ -0,0 +1,425 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define MAX_MEMPOOLS 8 + +struct mem_pool mpools[MAX_MEMPOOLS]; + +/* The tree contains all allocations over all memory pools */ +static struct rb_root alloc_root; +static struct mutex alloc_mutex; + +static void *s_start(struct seq_file *m, loff_t *pos) + __acquires(&alloc_mutex) +{ + loff_t n = *pos; + struct rb_node *r; + + mutex_lock(&alloc_mutex); + r = rb_first(&alloc_root); + + while (n > 0 && r) { + n--; + r = rb_next(r); + } + if (!n) + return r; + return NULL; +} + +static void *s_next(struct seq_file *m, void *p, loff_t *pos) +{ + struct rb_node *r = p; + ++*pos; + return rb_next(r); +} + +static void s_stop(struct seq_file *m, void *p) + __releases(&alloc_mutex) +{ + mutex_unlock(&alloc_mutex); +} + +static int s_show(struct seq_file *m, void *p) +{ + struct rb_node *r = p; + struct alloc *node = rb_entry(r, struct alloc, rb_node); + + seq_printf(m, "0x%lx 0x%p %ld %u %pS\n", node->paddr, node->vaddr, + node->len, node->mpool->id, node->caller); + return 0; +} + +static const struct seq_operations mempool_op = { + .start = s_start, + .next = s_next, + .stop = s_stop, + .show = s_show, +}; + +static int mempool_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &mempool_op); +} + +static struct alloc *find_alloc(void *addr) +{ + struct rb_root *root = &alloc_root; + struct rb_node *p = root->rb_node; + + mutex_lock(&alloc_mutex); + + while (p) { + struct alloc *node; + + node = rb_entry(p, struct alloc, rb_node); + if (addr < node->vaddr) + p = p->rb_left; + else if (addr > node->vaddr) + p = p->rb_right; + else { + mutex_unlock(&alloc_mutex); + return node; + } + } + mutex_unlock(&alloc_mutex); + return NULL; +} + +static int add_alloc(struct alloc *node) +{ + struct rb_root *root = &alloc_root; + struct rb_node **p = &root->rb_node; + struct rb_node *parent = NULL; + + mutex_lock(&alloc_mutex); + while (*p) { + struct alloc *tmp; + parent = *p; + + tmp = rb_entry(parent, struct alloc, rb_node); + + if (node->vaddr < tmp->vaddr) + p = &(*p)->rb_left; + else if (node->vaddr > tmp->vaddr) + p = &(*p)->rb_right; + else { + WARN(1, "memory at %p already allocated", tmp->vaddr); + mutex_unlock(&alloc_mutex); + return -EINVAL; + } + } + rb_link_node(&node->rb_node, parent, p); + rb_insert_color(&node->rb_node, root); + mutex_unlock(&alloc_mutex); + return 0; +} + +static int remove_alloc(struct alloc *victim_node) +{ + struct rb_root *root = &alloc_root; + if (!victim_node) + return -EINVAL; + + mutex_lock(&alloc_mutex); + rb_erase(&victim_node->rb_node, root); + mutex_unlock(&alloc_mutex); + return 0; +} + +static struct gen_pool *initialize_gpool(unsigned long start, + unsigned long size) +{ + struct gen_pool *gpool; + + gpool = gen_pool_create(PAGE_SHIFT, -1); + + if (!gpool) + return NULL; + if (gen_pool_add(gpool, start, size, -1)) { + gen_pool_destroy(gpool); + return NULL; + } + + return gpool; +} + +static void *__alloc(struct mem_pool *mpool, unsigned long size, + unsigned long align, int cached, void *caller) +{ + unsigned long paddr; + void __iomem *vaddr; + + unsigned long aligned_size; + int log_align = ilog2(align); + + struct alloc *node; + + aligned_size = PFN_ALIGN(size); + paddr = gen_pool_alloc_aligned(mpool->gpool, aligned_size, log_align); + if (!paddr) + return NULL; + + node = kmalloc(sizeof(struct alloc), GFP_KERNEL); + if (!node) + goto out; + + if (cached) + vaddr = ioremap_cached(paddr, aligned_size); + else + vaddr = ioremap(paddr, aligned_size); + + if (!vaddr) + goto out_kfree; + + node->vaddr = vaddr; + node->paddr = paddr; + node->len = aligned_size; + node->mpool = mpool; + node->caller = caller; + if (add_alloc(node)) + goto out_kfree; + + mpool->free -= aligned_size; + + return vaddr; +out_kfree: + if (vaddr) + iounmap(vaddr); + kfree(node); +out: + gen_pool_free(mpool->gpool, paddr, aligned_size); + return NULL; +} + +static void __free(void *vaddr, bool unmap) +{ + struct alloc *node = find_alloc(vaddr); + + if (!node) + return; + + if (unmap) + iounmap(node->vaddr); + + gen_pool_free(node->mpool->gpool, node->paddr, node->len); + node->mpool->free += node->len; + + remove_alloc(node); + kfree(node); +} + +static struct mem_pool *mem_type_to_memory_pool(int mem_type) +{ + struct mem_pool *mpool = &mpools[mem_type]; + + if (!mpool->size) + return NULL; + + mutex_lock(&mpool->pool_mutex); + if (!mpool->gpool) + mpool->gpool = initialize_gpool(mpool->paddr, mpool->size); + mutex_unlock(&mpool->pool_mutex); + if (!mpool->gpool) + return NULL; + + return mpool; +} + +struct mem_pool *initialize_memory_pool(unsigned long start, + unsigned long size, int mem_type) +{ + int id = mem_type; + + if (id >= MAX_MEMPOOLS || size <= PAGE_SIZE || size % PAGE_SIZE) + return NULL; + + mutex_lock(&mpools[id].pool_mutex); + + mpools[id].paddr = start; + mpools[id].size = size; + mpools[id].free = size; + mpools[id].id = id; + mutex_unlock(&mpools[id].pool_mutex); + + pr_info("memory pool %d (start %lx size %lx) initialized\n", + id, start, size); + return &mpools[id]; +} +EXPORT_SYMBOL_GPL(initialize_memory_pool); + +void *allocate_contiguous_memory(unsigned long size, + int mem_type, unsigned long align, int cached) +{ + unsigned long aligned_size = PFN_ALIGN(size); + struct mem_pool *mpool; + + mpool = mem_type_to_memory_pool(mem_type); + if (!mpool) + return NULL; + return __alloc(mpool, aligned_size, align, cached, + __builtin_return_address(0)); + +} +EXPORT_SYMBOL_GPL(allocate_contiguous_memory); + +unsigned long _allocate_contiguous_memory_nomap(unsigned long size, + int mem_type, unsigned long align, void *caller) +{ + unsigned long paddr; + unsigned long aligned_size; + + struct alloc *node; + struct mem_pool *mpool; + int log_align = ilog2(align); + + mpool = mem_type_to_memory_pool(mem_type); + if (!mpool || !mpool->gpool) + return 0; + + aligned_size = PFN_ALIGN(size); + paddr = gen_pool_alloc_aligned(mpool->gpool, aligned_size, log_align); + if (!paddr) + return 0; + + node = kmalloc(sizeof(struct alloc), GFP_KERNEL); + if (!node) + goto out; + + node->paddr = paddr; + + /* We search the tree using node->vaddr, so set + * it to something unique even though we don't + * use it for physical allocation nodes. + * The virtual and physical address ranges + * are disjoint, so there won't be any chance of + * a duplicate node->vaddr value. + */ + node->vaddr = (void *)paddr; + node->len = aligned_size; + node->mpool = mpool; + node->caller = caller; + if (add_alloc(node)) + goto out_kfree; + + mpool->free -= aligned_size; + return paddr; +out_kfree: + kfree(node); +out: + gen_pool_free(mpool->gpool, paddr, aligned_size); + return 0; +} +EXPORT_SYMBOL_GPL(_allocate_contiguous_memory_nomap); + +unsigned long allocate_contiguous_memory_nomap(unsigned long size, + int mem_type, unsigned long align) +{ + return _allocate_contiguous_memory_nomap(size, mem_type, align, + __builtin_return_address(0)); +} +EXPORT_SYMBOL_GPL(allocate_contiguous_memory_nomap); + +void free_contiguous_memory(void *addr) +{ + if (!addr) + return; + __free(addr, true); + return; +} +EXPORT_SYMBOL_GPL(free_contiguous_memory); + +void free_contiguous_memory_by_paddr(unsigned long paddr) +{ + if (!paddr) + return; + __free((void *)paddr, false); + return; +} +EXPORT_SYMBOL_GPL(free_contiguous_memory_by_paddr); + +unsigned long memory_pool_node_paddr(void *vaddr) +{ + struct alloc *node = find_alloc(vaddr); + + if (!node) + return -EINVAL; + + return node->paddr; +} +EXPORT_SYMBOL_GPL(memory_pool_node_paddr); + +unsigned long memory_pool_node_len(void *vaddr) +{ + struct alloc *node = find_alloc(vaddr); + + if (!node) + return -EINVAL; + + return node->len; +} +EXPORT_SYMBOL_GPL(memory_pool_node_len); + +static const struct file_operations mempool_operations = { + .owner = THIS_MODULE, + .open = mempool_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; + +int __init memory_pool_init(void) +{ + int i; + + alloc_root = RB_ROOT; + mutex_init(&alloc_mutex); + for (i = 0; i < ARRAY_SIZE(mpools); i++) { + mutex_init(&mpools[i].pool_mutex); + mpools[i].gpool = NULL; + } + + return 0; +} + +static int __init debugfs_mempool_init(void) +{ + struct dentry *entry, *dir = debugfs_create_dir("mempool", NULL); + + if (!dir) { + pr_err("Cannot create /sys/kernel/debug/mempool"); + return -EINVAL; + } + + entry = debugfs_create_file("map", S_IRUSR, dir, + NULL, &mempool_operations); + + if (!entry) + pr_err("Cannot create /sys/kernel/debug/mempool/map"); + + return entry ? 0 : -EINVAL; +} + +module_init(debugfs_mempool_init); From 7fa1d78ee36e0d2243b8811125cb81e52c8f7ad3 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 24 Jan 2012 02:44:17 -0600 Subject: [PATCH 2012/2556] msm: add support for socinfo and internal_power_rail --- arch/arm/mach-msm/Makefile | 3 +- .../include/mach/internal_power_rail.h | 68 ++ arch/arm/mach-msm/include/mach/socinfo.h | 138 ++++ arch/arm/mach-msm/internal_power_rail.c | 103 +++ arch/arm/mach-msm/socinfo.c | 655 ++++++++++++++++++ 5 files changed, 966 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-msm/include/mach/internal_power_rail.h create mode 100644 arch/arm/mach-msm/include/mach/socinfo.h create mode 100644 arch/arm/mach-msm/internal_power_rail.c create mode 100644 arch/arm/mach-msm/socinfo.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 35b45d361e3eb..ece3635ddc61a 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -8,6 +8,7 @@ obj-y += nand_partitions.o obj-y += pmic.o obj-y += drv_callback.o obj-y += htc_board_tags.o +obj-y += socinfo.o obj-$(CONFIG_ARCH_MSM_ARM11) += acpuclock-arm11.o idle.o obj-$(CONFIG_ARCH_MSM_SCORPION) += idle-v7.o @@ -22,7 +23,7 @@ endif obj-$(CONFIG_ARCH_MSM8X60) += clock-dummy.o iommu.o iommu_dev.o devices-msm8x60-iommu.o obj-$(CONFIG_MSM_PROC_COMM) += proc_comm.o clock-pcom.o vreg.o -obj-$(CONFIG_MSM_PROC_COMM) += clock.o +obj-$(CONFIG_MSM_PROC_COMM) += clock.o internal_power_rail.o obj-$(CONFIG_ARCH_QSD8X50) += sirc.o obj-$(CONFIG_MSM_FIQ_SUPPORT) += fiq_glue.o obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o smem_log.o diff --git a/arch/arm/mach-msm/include/mach/internal_power_rail.h b/arch/arm/mach-msm/include/mach/internal_power_rail.h new file mode 100644 index 0000000000000..be3c404c8bbf7 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/internal_power_rail.h @@ -0,0 +1,68 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _INTERNAL_POWER_RAIL_H +#define _INTERNAL_POWER_RAIL_H + +/* Clock power rail IDs */ +#define PWR_RAIL_GRP_CLK 8 +#define PWR_RAIL_GRP_2D_CLK 58 +#define PWR_RAIL_MDP_CLK 14 +#define PWR_RAIL_MFC_CLK 68 +#define PWR_RAIL_ROTATOR_CLK 90 +#define PWR_RAIL_VDC_CLK 39 +#define PWR_RAIL_VFE_CLK 41 +#define PWR_RAIL_VPE_CLK 76 + +enum rail_ctl_mode { + PWR_RAIL_CTL_AUTO = 0, + PWR_RAIL_CTL_MANUAL, +}; + +#if defined(CONFIG_ARCH_MSM8X60) || defined(CONFIG_ARCH_MSM8960) +static inline int __maybe_unused internal_pwr_rail_ctl(unsigned rail_id, + bool enable) +{ + /* Not yet implemented. */ + return 0; +} +static inline int __maybe_unused internal_pwr_rail_mode(unsigned rail_id, + enum rail_ctl_mode mode) +{ + /* Not yet implemented. */ + return 0; +} +#else +int internal_pwr_rail_ctl(unsigned rail_id, bool enable); +int internal_pwr_rail_mode(unsigned rail_id, enum rail_ctl_mode mode); +#endif + +int internal_pwr_rail_ctl_auto(unsigned rail_id, bool enable); + +#endif /* _INTERNAL_POWER_RAIL_H */ + diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h new file mode 100644 index 0000000000000..5040212a0e93d --- /dev/null +++ b/arch/arm/mach-msm/include/mach/socinfo.h @@ -0,0 +1,138 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_SOCINFO_H_ +#define _ARCH_ARM_MACH_MSM_SOCINFO_H_ + +#include + +/* + * SOC version type with major number in the upper 16 bits and minor + * number in the lower 16 bits. For example: + * 1.0 -> 0x00010000 + * 2.3 -> 0x00020003 + */ +#define SOCINFO_VERSION_MAJOR(ver) ((ver & 0xffff0000) >> 16) +#define SOCINFO_VERSION_MINOR(ver) (ver & 0x0000ffff) + +enum msm_cpu { + MSM_CPU_UNKNOWN = 0, + MSM_CPU_7X01, + MSM_CPU_7X25, + MSM_CPU_7X27, + MSM_CPU_8X50, + MSM_CPU_8X50A, + MSM_CPU_7X30, + MSM_CPU_8X55, + MSM_CPU_8X60, + MSM_CPU_8960, + MSM_CPU_7X27A, +}; + +enum msm_cpu socinfo_get_msm_cpu(void); +uint32_t socinfo_get_id(void); +uint32_t socinfo_get_version(void); +char *socinfo_get_build_id(void); +uint32_t socinfo_get_platform_type(void); +uint32_t socinfo_get_platform_subtype(void); +uint32_t socinfo_get_platform_version(void); +int __init socinfo_init(void) __must_check; + +static inline int cpu_is_msm7x01(void) +{ + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X01; +} + +static inline int cpu_is_msm7x25(void) +{ + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X25; +} + +static inline int cpu_is_msm7x27(void) +{ + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X27; +} + +static inline int cpu_is_msm7x27a(void) +{ + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X27A; +} + +static inline int cpu_is_msm7x30(void) +{ + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X30; +} + +static inline int cpu_is_qsd8x50(void) +{ + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_8X50; +} + +static inline int cpu_is_msm8x55(void) +{ + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_8X55; +} + +static inline int cpu_is_msm8x60(void) +{ + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_8X60; +} + +static inline int cpu_is_msm8960(void) +{ + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_8960; +} +#endif diff --git a/arch/arm/mach-msm/internal_power_rail.c b/arch/arm/mach-msm/internal_power_rail.c new file mode 100644 index 0000000000000..8e04319397921 --- /dev/null +++ b/arch/arm/mach-msm/internal_power_rail.c @@ -0,0 +1,103 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +#include + +#include "proc_comm.h" + +static DEFINE_SPINLOCK(power_rail_lock); + +static struct internal_rail { + uint32_t id; + uint32_t mode; +} rails[] = { + { PWR_RAIL_GRP_CLK, PWR_RAIL_CTL_AUTO }, + { PWR_RAIL_GRP_2D_CLK, PWR_RAIL_CTL_AUTO }, + { PWR_RAIL_MDP_CLK, PWR_RAIL_CTL_MANUAL }, + { PWR_RAIL_MFC_CLK, PWR_RAIL_CTL_AUTO }, + { PWR_RAIL_ROTATOR_CLK, PWR_RAIL_CTL_AUTO }, + { PWR_RAIL_VDC_CLK, PWR_RAIL_CTL_AUTO }, + { PWR_RAIL_VFE_CLK, PWR_RAIL_CTL_AUTO }, + { PWR_RAIL_VPE_CLK, PWR_RAIL_CTL_AUTO }, +}; + +static struct internal_rail *find_rail(unsigned rail_id) +{ + int i; + for (i = 0; i < ARRAY_SIZE(rails); i++) + if (rails[i].id == rail_id) + return rails + i; + + return NULL; +} + +/* Enable or disable an internal power rail */ +int internal_pwr_rail_ctl(unsigned rail_id, bool enable) +{ + int cmd, rc; + + cmd = enable ? PCOM_CLKCTL_RPC_RAIL_ENABLE : + PCOM_CLKCTL_RPC_RAIL_DISABLE; + + rc = msm_proc_comm(cmd, &rail_id, NULL); + + return rc; + +} +EXPORT_SYMBOL(internal_pwr_rail_ctl); + +/* Enable or disable a rail if the rail is in auto mode. */ +int internal_pwr_rail_ctl_auto(unsigned rail_id, bool enable) +{ + int rc = 0; + unsigned long flags; + struct internal_rail *rail = find_rail(rail_id); + + BUG_ON(!rail); + + spin_lock_irqsave(&power_rail_lock, flags); + if (rail->mode == PWR_RAIL_CTL_AUTO) + rc = internal_pwr_rail_ctl(rail_id, enable); + spin_unlock_irqrestore(&power_rail_lock, flags); + + return rc; +} + +/* Specify an internal power rail control mode (ex. auto, manual) */ +int internal_pwr_rail_mode(unsigned rail_id, enum rail_ctl_mode mode) +{ + int rc; + unsigned long flags; + struct internal_rail *rail = find_rail(rail_id); + + spin_lock_irqsave(&power_rail_lock, flags); + rc = msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &rail_id, &mode); + if (rc) + goto out; + if (rail_id) { + rc = -EINVAL; + goto out; + } + + if (rail) + rail->mode = mode; +out: + spin_unlock_irqrestore(&power_rail_lock, flags); + return rc; +} +EXPORT_SYMBOL(internal_pwr_rail_mode); + diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c new file mode 100644 index 0000000000000..313ef99bde9ba --- /dev/null +++ b/arch/arm/mach-msm/socinfo.c @@ -0,0 +1,655 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * SOC Info Routines + * + */ + +#include +#include +#include +#include + +#include "smd_private.h" + +#define BUILD_ID_LENGTH 32 + +enum { + HW_PLATFORM_UNKNOWN = 0, + HW_PLATFORM_SURF = 1, + HW_PLATFORM_FFA = 2, + HW_PLATFORM_FLUID = 3, + HW_PLATFORM_SVLTE_FFA = 4, + HW_PLATFORM_SVLTE_SURF = 5, + HW_PLATFORM_INVALID +}; + +const char *hw_platform[] = { + [HW_PLATFORM_UNKNOWN] = "Unknown", + [HW_PLATFORM_SURF] = "Surf", + [HW_PLATFORM_FFA] = "FFA", + [HW_PLATFORM_FLUID] = "Fluid", + [HW_PLATFORM_SVLTE_FFA] = "SVLTE_FFA", + [HW_PLATFORM_SVLTE_SURF] = "SLVTE_SURF" +}; + +enum { + ACCESSORY_CHIP_UNKNOWN = 0, + ACCESSORY_CHIP_CHARM = 58, +}; + +enum { + PLATFORM_SUBTYPE_UNKNOWN = 0x0, + PLATFORM_SUBTYPE_CHARM = 0x1, + PLATFORM_SUBTYPE_STRANGE = 0x2, + PLATFORM_SUBTYPE_STRANGE_2A = 0x3, + PLATFORM_SUBTYPE_INVALID, +}; + +const char *hw_platform_subtype[] = { + [PLATFORM_SUBTYPE_UNKNOWN] = "Unknown", + [PLATFORM_SUBTYPE_CHARM] = "charm", + [PLATFORM_SUBTYPE_STRANGE] = "strange", + [PLATFORM_SUBTYPE_STRANGE_2A] = "strange_2a," +}; + +/* Used to parse shared memory. Must match the modem. */ +struct socinfo_v1 { + uint32_t format; + uint32_t id; + uint32_t version; + char build_id[BUILD_ID_LENGTH]; +}; + +struct socinfo_v2 { + struct socinfo_v1 v1; + + /* only valid when format==2 */ + uint32_t raw_id; + uint32_t raw_version; +}; + +struct socinfo_v3 { + struct socinfo_v2 v2; + + /* only valid when format==3 */ + uint32_t hw_platform; +}; + +struct socinfo_v4 { + struct socinfo_v3 v3; + + /* only valid when format==4 */ + uint32_t platform_version; +}; + +struct socinfo_v5 { + struct socinfo_v4 v4; + + /* only valid when format==5 */ + uint32_t accessory_chip; +}; + +struct socinfo_v6 { + struct socinfo_v5 v5; + + /* only valid when format==6 */ + uint32_t hw_platform_subtype; +}; + +static union { + struct socinfo_v1 v1; + struct socinfo_v2 v2; + struct socinfo_v3 v3; + struct socinfo_v4 v4; + struct socinfo_v5 v5; + struct socinfo_v6 v6; +} *socinfo; + +static enum msm_cpu cpu_of_id[] = { + + /* 7x01 IDs */ + [1] = MSM_CPU_7X01, + [16] = MSM_CPU_7X01, + [17] = MSM_CPU_7X01, + [18] = MSM_CPU_7X01, + [19] = MSM_CPU_7X01, + [23] = MSM_CPU_7X01, + [25] = MSM_CPU_7X01, + [26] = MSM_CPU_7X01, + [32] = MSM_CPU_7X01, + [33] = MSM_CPU_7X01, + [34] = MSM_CPU_7X01, + [35] = MSM_CPU_7X01, + + /* 7x25 IDs */ + [20] = MSM_CPU_7X25, + [21] = MSM_CPU_7X25, /* 7225 */ + [24] = MSM_CPU_7X25, /* 7525 */ + [27] = MSM_CPU_7X25, /* 7625 */ + [39] = MSM_CPU_7X25, + [40] = MSM_CPU_7X25, + [41] = MSM_CPU_7X25, + [42] = MSM_CPU_7X25, + [62] = MSM_CPU_7X25, /* 7625-1 */ + [63] = MSM_CPU_7X25, /* 7225-1 */ + [66] = MSM_CPU_7X25, /* 7225-2 */ + + + /* 7x27 IDs */ + [43] = MSM_CPU_7X27, + [44] = MSM_CPU_7X27, + [61] = MSM_CPU_7X27, + [67] = MSM_CPU_7X27, /* 7227-1 */ + [68] = MSM_CPU_7X27, /* 7627-1 */ + [69] = MSM_CPU_7X27, /* 7627-2 */ + + + /* 8x50 IDs */ + [30] = MSM_CPU_8X50, + [36] = MSM_CPU_8X50, + [37] = MSM_CPU_8X50, + [38] = MSM_CPU_8X50, + + /* 8x50A IDs */ + [64] = MSM_CPU_8X50A, + [65] = MSM_CPU_8X50A, + + /* 7x30 IDs */ + [59] = MSM_CPU_7X30, + [60] = MSM_CPU_7X30, + + /* 8x55 IDs */ + [74] = MSM_CPU_8X55, + [75] = MSM_CPU_8X55, + [85] = MSM_CPU_8X55, + + /* 8x60 IDs */ + [70] = MSM_CPU_8X60, + [71] = MSM_CPU_8X60, + [86] = MSM_CPU_8X60, + + /* 8960 IDs */ + [87] = MSM_CPU_8960, + + /* 7x27A IDs */ + [90] = MSM_CPU_7X27A, + [91] = MSM_CPU_7X27A, + [92] = MSM_CPU_7X27A, + + /* Uninitialized IDs are not known to run Linux. + MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are + considered as unknown CPU. */ +}; + +static enum msm_cpu cur_cpu; + +static struct socinfo_v1 dummy_socinfo = { + .format = 1, + .version = 1, + .build_id = "Dummy socinfo placeholder" +}; + +uint32_t socinfo_get_id(void) +{ + return (socinfo) ? socinfo->v1.id : 0; +} +EXPORT_SYMBOL_GPL(socinfo_get_id); + +uint32_t socinfo_get_version(void) +{ + return (socinfo) ? socinfo->v1.version : 0; +} + +char *socinfo_get_build_id(void) +{ + return (socinfo) ? socinfo->v1.build_id : NULL; +} + +uint32_t socinfo_get_raw_id(void) +{ + return socinfo ? + (socinfo->v1.format >= 2 ? socinfo->v2.raw_id : 0) + : 0; +} + +uint32_t socinfo_get_raw_version(void) +{ + return socinfo ? + (socinfo->v1.format >= 2 ? socinfo->v2.raw_version : 0) + : 0; +} + +uint32_t socinfo_get_platform_type(void) +{ + return socinfo ? + (socinfo->v1.format >= 3 ? socinfo->v3.hw_platform : 0) + : 0; +} + + +uint32_t socinfo_get_platform_version(void) +{ + return socinfo ? + (socinfo->v1.format >= 4 ? socinfo->v4.platform_version : 0) + : 0; +} + +/* This information is directly encoded by the machine id */ +/* Thus no external callers rely on this information at the moment */ +static uint32_t socinfo_get_accessory_chip(void) +{ + return socinfo ? + (socinfo->v1.format >= 5 ? socinfo->v5.accessory_chip : 0) + : 0; +} + +uint32_t socinfo_get_platform_subtype(void) +{ + return socinfo ? + (socinfo->v1.format >= 6 ? socinfo->v6.hw_platform_subtype : 0) + : 0; +} + +enum msm_cpu socinfo_get_msm_cpu(void) +{ + return cur_cpu; +} +EXPORT_SYMBOL_GPL(socinfo_get_msm_cpu); + +static ssize_t +socinfo_show_id(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", socinfo_get_id()); +} + +static ssize_t +socinfo_show_version(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + uint32_t version; + + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + + version = socinfo_get_version(); + return snprintf(buf, PAGE_SIZE, "%u.%u\n", + SOCINFO_VERSION_MAJOR(version), + SOCINFO_VERSION_MINOR(version)); +} + +static ssize_t +socinfo_show_build_id(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%-.32s\n", socinfo_get_build_id()); +} + +static ssize_t +socinfo_show_raw_id(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 2) { + pr_err("%s: Raw ID not available!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", socinfo_get_raw_id()); +} + +static ssize_t +socinfo_show_raw_version(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 2) { + pr_err("%s: Raw version not available!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", socinfo_get_raw_version()); +} + +static ssize_t +socinfo_show_platform_type(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + uint32_t hw_type; + + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 3) { + pr_err("%s: platform type not available!\n", __func__); + return 0; + } + + hw_type = socinfo_get_platform_type(); + if (hw_type >= HW_PLATFORM_INVALID) { + pr_err("%s: Invalid hardware platform type found\n", + __func__); + hw_type = HW_PLATFORM_UNKNOWN; + } + + return snprintf(buf, PAGE_SIZE, "%-.32s\n", hw_platform[hw_type]); +} + +static ssize_t +socinfo_show_platform_version(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 4) { + pr_err("%s: platform version not available!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", + socinfo_get_platform_version()); +} + +static ssize_t +socinfo_show_accessory_chip(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 5) { + pr_err("%s: accessory chip not available!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", + socinfo_get_accessory_chip()); +} + +static ssize_t +socinfo_show_platform_subtype(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + uint32_t hw_subtype; + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 6) { + pr_err("%s: platform subtype not available!\n", __func__); + return 0; + } + + hw_subtype = socinfo_get_platform_subtype(); + if (hw_subtype >= PLATFORM_SUBTYPE_INVALID) { + pr_err("%s: Invalid hardware platform sub type found\n", + __func__); + hw_subtype = PLATFORM_SUBTYPE_UNKNOWN; + } + return snprintf(buf, PAGE_SIZE, "%-.32s\n", + hw_platform_subtype[hw_subtype]); +} + +static struct sysdev_attribute socinfo_v1_files[] = { + _SYSDEV_ATTR(id, 0444, socinfo_show_id, NULL), + _SYSDEV_ATTR(version, 0444, socinfo_show_version, NULL), + _SYSDEV_ATTR(build_id, 0444, socinfo_show_build_id, NULL), +}; + +static struct sysdev_attribute socinfo_v2_files[] = { + _SYSDEV_ATTR(raw_id, 0444, socinfo_show_raw_id, NULL), + _SYSDEV_ATTR(raw_version, 0444, socinfo_show_raw_version, NULL), +}; + +static struct sysdev_attribute socinfo_v3_files[] = { + _SYSDEV_ATTR(hw_platform, 0444, socinfo_show_platform_type, NULL), +}; + +static struct sysdev_attribute socinfo_v4_files[] = { + _SYSDEV_ATTR(platform_version, 0444, + socinfo_show_platform_version, NULL), +}; + +static struct sysdev_attribute socinfo_v5_files[] = { + _SYSDEV_ATTR(accessory_chip, 0444, + socinfo_show_accessory_chip, NULL), +}; + +static struct sysdev_attribute socinfo_v6_files[] = { + _SYSDEV_ATTR(platform_subtype, 0444, + socinfo_show_platform_subtype, NULL), +}; + +static struct sysdev_class soc_sysdev_class = { + .name = "soc", +}; + +static struct sys_device soc_sys_device = { + .id = 0, + .cls = &soc_sysdev_class, +}; + +static int __init socinfo_create_files(struct sys_device *dev, + struct sysdev_attribute files[], + int size) +{ + int i; + for (i = 0; i < size; i++) { + int err = sysdev_create_file(dev, &files[i]); + if (err) { + pr_err("%s: sysdev_create_file(%s)=%d\n", + __func__, files[i].attr.name, err); + return err; + } + } + return 0; +} + +static int __init socinfo_init_sysdev(void) +{ + int err; + + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return -ENODEV; + } + + err = sysdev_class_register(&soc_sysdev_class); + if (err) { + pr_err("%s: sysdev_class_register fail (%d)\n", + __func__, err); + return err; + } + err = sysdev_register(&soc_sys_device); + if (err) { + pr_err("%s: sysdev_register fail (%d)\n", + __func__, err); + return err; + } + socinfo_create_files(&soc_sys_device, socinfo_v1_files, + ARRAY_SIZE(socinfo_v1_files)); + if (socinfo->v1.format < 2) + return err; + socinfo_create_files(&soc_sys_device, socinfo_v2_files, + ARRAY_SIZE(socinfo_v2_files)); + + if (socinfo->v1.format < 3) + return err; + + socinfo_create_files(&soc_sys_device, socinfo_v3_files, + ARRAY_SIZE(socinfo_v3_files)); + + if (socinfo->v1.format < 4) + return err; + + socinfo_create_files(&soc_sys_device, socinfo_v4_files, + ARRAY_SIZE(socinfo_v4_files)); + + if (socinfo->v1.format < 5) + return err; + + socinfo_create_files(&soc_sys_device, socinfo_v5_files, + ARRAY_SIZE(socinfo_v5_files)); + + if (socinfo->v1.format < 6) + return err; + + return socinfo_create_files(&soc_sys_device, socinfo_v6_files, + ARRAY_SIZE(socinfo_v6_files)); + +} + +arch_initcall(socinfo_init_sysdev); + +void *setup_dummy_socinfo(void) +{ + if (machine_is_msm8960_rumi3() || machine_is_msm8960_sim())/* || + machine_is_msm8960_cdp())*/ + dummy_socinfo.id = 87; + return (void *) &dummy_socinfo; +} + +int __init socinfo_init(void) +{ + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, sizeof(struct socinfo_v6)); + + if (!socinfo) + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, + sizeof(struct socinfo_v5)); + + if (!socinfo) + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, + sizeof(struct socinfo_v4)); + + if (!socinfo) + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, + sizeof(struct socinfo_v3)); + + if (!socinfo) + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, + sizeof(struct socinfo_v2)); + + if (!socinfo) + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, + sizeof(struct socinfo_v1)); + + if (!socinfo) { + pr_warn("%s: Can't find SMEM_HW_SW_BUILD_ID; falling back on " + "dummy values.\n", __func__); + socinfo = setup_dummy_socinfo(); + } + + WARN(!socinfo_get_id(), "Unknown SOC ID!\n"); + WARN(socinfo_get_id() >= ARRAY_SIZE(cpu_of_id), + "New IDs added! ID => CPU mapping might need an update.\n"); + + if (socinfo->v1.id < ARRAY_SIZE(cpu_of_id)) + cur_cpu = cpu_of_id[socinfo->v1.id]; + + switch (socinfo->v1.format) { + case 1: + pr_info("%s: v%u, id=%u, ver=%u.%u\n", + __func__, socinfo->v1.format, socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version)); + break; + case 2: + pr_info("%s: v%u, id=%u, ver=%u.%u, " + "raw_id=%u, raw_ver=%u\n", + __func__, socinfo->v1.format, socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version), + socinfo->v2.raw_id, socinfo->v2.raw_version); + break; + case 3: + pr_info("%s: v%u, id=%u, ver=%u.%u, " + "raw_id=%u, raw_ver=%u, hw_plat=%u\n", + __func__, socinfo->v1.format, socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version), + socinfo->v2.raw_id, socinfo->v2.raw_version, + socinfo->v3.hw_platform); + break; + case 4: + pr_info("%s: v%u, id=%u, ver=%u.%u, " + "raw_id=%u, raw_ver=%u, hw_plat=%u, hw_plat_ver=%u\n", + __func__, socinfo->v1.format, socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version), + socinfo->v2.raw_id, socinfo->v2.raw_version, + socinfo->v3.hw_platform, socinfo->v4.platform_version); + break; + case 5: + pr_info("%s: v%u, id=%u, ver=%u.%u, " + "raw_id=%u, raw_ver=%u, hw_plat=%u, hw_plat_ver=%u\n" + " accessory_chip=%u\n", __func__, socinfo->v1.format, + socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version), + socinfo->v2.raw_id, socinfo->v2.raw_version, + socinfo->v3.hw_platform, socinfo->v4.platform_version, + socinfo->v5.accessory_chip); + break; + case 6: + pr_info("%s: v%u, id=%u, ver=%u.%u, " + "raw_id=%u, raw_ver=%u, hw_plat=%u, hw_plat_ver=%u\n" + " accessory_chip=%u hw_plat_subtype=%u\n", __func__, + socinfo->v1.format, + socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version), + socinfo->v2.raw_id, socinfo->v2.raw_version, + socinfo->v3.hw_platform, socinfo->v4.platform_version, + socinfo->v5.accessory_chip, + socinfo->v6.hw_platform_subtype); + break; + default: + pr_err("%s: Unknown format found\n", __func__); + break; + } + + return 0; +} From cc7b2d360eb16ce85f61fd2f7e7db655b11f4b10 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Mon, 23 Jan 2012 07:31:33 -0600 Subject: [PATCH 2013/2556] msm: kgsl: patch: import kgsl v3.3 driver from CAF msm.38 - part 1 --- drivers/gpu/Makefile | 1 + drivers/gpu/msm/Kconfig | 106 ++ drivers/gpu/msm/Makefile | 29 + drivers/gpu/msm/a200_reg.h | 448 ++++++ drivers/gpu/msm/a220_reg.h | 39 + drivers/gpu/msm/adreno.c | 1366 ++++++++++++++++++ drivers/gpu/msm/adreno.h | 77 + drivers/gpu/msm/adreno_debugfs.c | 450 ++++++ drivers/gpu/msm/adreno_debugfs.h | 56 + drivers/gpu/msm/adreno_drawctxt.c | 1680 +++++++++++++++++++++ drivers/gpu/msm/adreno_drawctxt.h | 113 ++ drivers/gpu/msm/adreno_pm4types.h | 193 +++ drivers/gpu/msm/adreno_postmortem.c | 865 +++++++++++ drivers/gpu/msm/adreno_postmortem.h | 37 + drivers/gpu/msm/adreno_ringbuffer.c | 915 ++++++++++++ drivers/gpu/msm/adreno_ringbuffer.h | 184 +++ drivers/gpu/msm/kgsl.c | 2081 +++++++++++++++++++++++++++ drivers/gpu/msm/kgsl.h | 272 ++++ drivers/gpu/msm/kgsl_cffdump.c | 711 +++++++++ drivers/gpu/msm/kgsl_cffdump.h | 73 + drivers/gpu/msm/kgsl_debugfs.c | 81 ++ drivers/gpu/msm/kgsl_debugfs.h | 37 + drivers/gpu/msm/kgsl_device.h | 242 ++++ drivers/gpu/msm/kgsl_drm.c | 1690 ++++++++++++++++++++++ drivers/gpu/msm/kgsl_log.h | 118 ++ drivers/gpu/msm/kgsl_mmu.c | 1133 +++++++++++++++ drivers/gpu/msm/kgsl_mmu.h | 267 ++++ drivers/gpu/msm/kgsl_pwrctrl.c | 723 ++++++++++ drivers/gpu/msm/kgsl_pwrctrl.h | 93 ++ drivers/gpu/msm/kgsl_sharedmem.c | 646 +++++++++ drivers/gpu/msm/kgsl_sharedmem.h | 116 ++ drivers/gpu/msm/z180.c | 1076 ++++++++++++++ drivers/gpu/msm/z180.h | 49 + drivers/gpu/msm/z180_reg.h | 93 ++ 34 files changed, 16060 insertions(+) create mode 100644 drivers/gpu/msm/Kconfig create mode 100644 drivers/gpu/msm/Makefile create mode 100644 drivers/gpu/msm/a200_reg.h create mode 100644 drivers/gpu/msm/a220_reg.h create mode 100644 drivers/gpu/msm/adreno.c create mode 100644 drivers/gpu/msm/adreno.h create mode 100644 drivers/gpu/msm/adreno_debugfs.c create mode 100644 drivers/gpu/msm/adreno_debugfs.h create mode 100644 drivers/gpu/msm/adreno_drawctxt.c create mode 100644 drivers/gpu/msm/adreno_drawctxt.h create mode 100644 drivers/gpu/msm/adreno_pm4types.h create mode 100644 drivers/gpu/msm/adreno_postmortem.c create mode 100644 drivers/gpu/msm/adreno_postmortem.h create mode 100644 drivers/gpu/msm/adreno_ringbuffer.c create mode 100644 drivers/gpu/msm/adreno_ringbuffer.h create mode 100644 drivers/gpu/msm/kgsl.c create mode 100644 drivers/gpu/msm/kgsl.h create mode 100644 drivers/gpu/msm/kgsl_cffdump.c create mode 100644 drivers/gpu/msm/kgsl_cffdump.h create mode 100644 drivers/gpu/msm/kgsl_debugfs.c create mode 100644 drivers/gpu/msm/kgsl_debugfs.h create mode 100644 drivers/gpu/msm/kgsl_device.h create mode 100644 drivers/gpu/msm/kgsl_drm.c create mode 100644 drivers/gpu/msm/kgsl_log.h create mode 100644 drivers/gpu/msm/kgsl_mmu.c create mode 100644 drivers/gpu/msm/kgsl_mmu.h create mode 100644 drivers/gpu/msm/kgsl_pwrctrl.c create mode 100644 drivers/gpu/msm/kgsl_pwrctrl.h create mode 100644 drivers/gpu/msm/kgsl_sharedmem.c create mode 100644 drivers/gpu/msm/kgsl_sharedmem.h create mode 100644 drivers/gpu/msm/z180.c create mode 100644 drivers/gpu/msm/z180.h create mode 100644 drivers/gpu/msm/z180_reg.h diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile index cc9277885dd07..5cde134d19db6 100644 --- a/drivers/gpu/Makefile +++ b/drivers/gpu/Makefile @@ -1 +1,2 @@ obj-y += drm/ vga/ stub/ +obj-$(CONFIG_MSM_KGSL) += msm/ diff --git a/drivers/gpu/msm/Kconfig b/drivers/gpu/msm/Kconfig new file mode 100644 index 0000000000000..e17bbbb64998a --- /dev/null +++ b/drivers/gpu/msm/Kconfig @@ -0,0 +1,106 @@ +config MSM_KGSL + tristate "MSM 3D Graphics driver" + default n + depends on FB && ARM && ARCH_MSM && !MSM_HW3D && ANDROID_PMEM + select GENERIC_ALLOCATOR + select FW_LOADER + select RELAY + ---help--- + 3D graphics driver. Required to use hardware accelerated + OpenGL ES 2.0 and 1.1. + +config MSM_KGSL_CFF_DUMP + bool "Enable KGSL Common File Format (CFF) Dump Feature [Use with caution]" + default n + depends on MSM_KGSL + select RELAY + ---help--- + This is an analysis and diagnostic feature only, and should only be + turned on during KGSL GPU diagnostics and will slow down the KGSL + performance sigificantly, hence *do not use in production builds*. + When enabled, CFF Dump is on at boot. It can be turned off at runtime + via 'echo 0 > /d/kgsl/cff_dump'. The log can be captured via + /d/kgsl-cff/cpu[0|1]. + +config MSM_KGSL_CFF_DUMP_NO_CONTEXT_MEM_DUMP + bool "When selected will disable KGSL CFF Dump for context switches" + default n + depends on MSM_KGSL_CFF_DUMP + ---help--- + Dumping all the memory for every context switch can produce quite + huge log files, to reduce this, turn this feature on. + +config MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL + bool "Disable human readable CP_STAT fields in post-mortem dump" + default n + depends on MSM_KGSL + ---help--- + For a more compact kernel log the human readable output of + CP_STAT can be turned off with this option. + +config MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP + bool "Disable dumping current IB1 and IB2 in post-mortem dump" + default n + depends on MSM_KGSL + ---help--- + For a more compact kernel log the IB1 and IB2 embedded dump + can be turned off with this option. Some IB dumps take up + so much space that vital other information gets cut from the + post-mortem dump. + +config MSM_KGSL_PSTMRTMDMP_RB_HEX + bool "Use hex version for ring-buffer in post-mortem dump" + default n + depends on MSM_KGSL + ---help--- + Use hex version for the ring-buffer in the post-mortem dump, instead + of the human readable version. + +config MSM_KGSL_2D + bool "Enable the 2D core. Required for OpenVG" + default n + depends on MSM_KGSL && !ARCH_MSM7X27 + +config MSM_KGSL_DRM + bool "Build a DRM interface for the MSM_KGSL driver" + depends on MSM_KGSL && DRM + +config MSM_KGSL_MMU + bool "Enable the GPU MMU in the MSM_KGSL driver" + depends on MSM_KGSL && MMU && !MSM_KGSL_CFF_DUMP + default y + +config KGSL_PER_PROCESS_PAGE_TABLE + bool "Enable Per Process page tables for the KGSL driver" + default n + depends on MSM_KGSL_MMU && !MSM_KGSL_DRM + ---help--- + The MMU will use per process pagetables when enabled. + +config MSM_KGSL_PAGE_TABLE_SIZE + hex "Size of pagetables" + default 0xFFF0000 + depends on MSM_KGSL_MMU + ---help--- + Sets the pagetable size used by the MMU. The max value + is 0xFFF0000 or (256M - 64K). + +config MSM_KGSL_PAGE_TABLE_COUNT + int "Minimum of concurrent pagetables to support" + default 8 + depends on KGSL_PER_PROCESS_PAGE_TABLE + ---help--- + Specify the number of pagetables to allocate at init time + This is the number of concurrent processes that are guaranteed to + to run at any time. Additional processes can be created dynamically + assuming there is enough contiguous memory to allocate the pagetable. + +config MSM_KGSL_MMU_PAGE_FAULT + bool "Force the GPU MMU to page fault for unmapped regions" + default n + depends on MSM_KGSL_MMU + +config MSM_KGSL_DISABLE_SHADOW_WRITES + bool "Disable register shadow writes for context switches" + default n + depends on MSM_KGSL diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile new file mode 100644 index 0000000000000..edbb92ea21fd8 --- /dev/null +++ b/drivers/gpu/msm/Makefile @@ -0,0 +1,29 @@ +ccflags-y := -Iinclude/drm + +msm_kgsl_core-$(CONFIG_MSM_KGSL) = \ + kgsl.o \ + kgsl_sharedmem.o \ + kgsl_pwrctrl.o + +msm_kgsl_core-$(CONFIG_DEBUG_FS) += kgsl_debugfs.o +msm_kgsl_core-$(CONFIG_MSM_KGSL_MMU) += kgsl_mmu.o +msm_kgsl_core-$(CONFIG_MSM_KGSL_CFF_DUMP) += kgsl_cffdump.o +msm_kgsl_core-$(CONFIG_MSM_KGSL_DRM) += kgsl_drm.o + +msm_adreno-$(CONFIG_MSM_KGSL) += \ + adreno_ringbuffer.o \ + adreno_drawctxt.o \ + adreno_postmortem.o \ + adreno.o + +msm_adreno-$(CONFIG_DEBUG_FS) += adreno_debugfs.o + +msm_z180-$(CONFIG_MSM_KGSL_2D) += z180.o + +msm_kgsl_core-objs = $(msm_kgsl_core-y) +msm_adreno-objs = $(msm_adreno-y) +msm_z180-objs = $(msm_z180-y) + +obj-$(CONFIG_MSM_KGSL) += msm_kgsl_core.o +obj-$(CONFIG_MSM_KGSL) += msm_adreno.o +obj-$(CONFIG_MSM_KGSL_2D) += msm_z180.o diff --git a/drivers/gpu/msm/a200_reg.h b/drivers/gpu/msm/a200_reg.h new file mode 100644 index 0000000000000..4df6e14cf4dfc --- /dev/null +++ b/drivers/gpu/msm/a200_reg.h @@ -0,0 +1,448 @@ +/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __A200_REG_H +#define __A200_REG_H + +enum VGT_EVENT_TYPE { + VS_DEALLOC = 0, + PS_DEALLOC = 1, + VS_DONE_TS = 2, + PS_DONE_TS = 3, + CACHE_FLUSH_TS = 4, + CONTEXT_DONE = 5, + CACHE_FLUSH = 6, + VIZQUERY_START = 7, + VIZQUERY_END = 8, + SC_WAIT_WC = 9, + RST_PIX_CNT = 13, + RST_VTX_CNT = 14, + TILE_FLUSH = 15, + CACHE_FLUSH_AND_INV_TS_EVENT = 20, + ZPASS_DONE = 21, + CACHE_FLUSH_AND_INV_EVENT = 22, + PERFCOUNTER_START = 23, + PERFCOUNTER_STOP = 24, + VS_FETCH_DONE = 27, + FACENESS_FLUSH = 28, +}; + +enum COLORFORMATX { + COLORX_4_4_4_4 = 0, + COLORX_1_5_5_5 = 1, + COLORX_5_6_5 = 2, + COLORX_8 = 3, + COLORX_8_8 = 4, + COLORX_8_8_8_8 = 5, + COLORX_S8_8_8_8 = 6, + COLORX_16_FLOAT = 7, + COLORX_16_16_FLOAT = 8, + COLORX_16_16_16_16_FLOAT = 9, + COLORX_32_FLOAT = 10, + COLORX_32_32_FLOAT = 11, + COLORX_32_32_32_32_FLOAT = 12, + COLORX_2_3_3 = 13, + COLORX_8_8_8 = 14, +}; + +enum SURFACEFORMAT { + FMT_1_REVERSE = 0, + FMT_1 = 1, + FMT_8 = 2, + FMT_1_5_5_5 = 3, + FMT_5_6_5 = 4, + FMT_6_5_5 = 5, + FMT_8_8_8_8 = 6, + FMT_2_10_10_10 = 7, + FMT_8_A = 8, + FMT_8_B = 9, + FMT_8_8 = 10, + FMT_Cr_Y1_Cb_Y0 = 11, + FMT_Y1_Cr_Y0_Cb = 12, + FMT_5_5_5_1 = 13, + FMT_8_8_8_8_A = 14, + FMT_4_4_4_4 = 15, + FMT_10_11_11 = 16, + FMT_11_11_10 = 17, + FMT_DXT1 = 18, + FMT_DXT2_3 = 19, + FMT_DXT4_5 = 20, + FMT_24_8 = 22, + FMT_24_8_FLOAT = 23, + FMT_16 = 24, + FMT_16_16 = 25, + FMT_16_16_16_16 = 26, + FMT_16_EXPAND = 27, + FMT_16_16_EXPAND = 28, + FMT_16_16_16_16_EXPAND = 29, + FMT_16_FLOAT = 30, + FMT_16_16_FLOAT = 31, + FMT_16_16_16_16_FLOAT = 32, + FMT_32 = 33, + FMT_32_32 = 34, + FMT_32_32_32_32 = 35, + FMT_32_FLOAT = 36, + FMT_32_32_FLOAT = 37, + FMT_32_32_32_32_FLOAT = 38, + FMT_32_AS_8 = 39, + FMT_32_AS_8_8 = 40, + FMT_16_MPEG = 41, + FMT_16_16_MPEG = 42, + FMT_8_INTERLACED = 43, + FMT_32_AS_8_INTERLACED = 44, + FMT_32_AS_8_8_INTERLACED = 45, + FMT_16_INTERLACED = 46, + FMT_16_MPEG_INTERLACED = 47, + FMT_16_16_MPEG_INTERLACED = 48, + FMT_DXN = 49, + FMT_8_8_8_8_AS_16_16_16_16 = 50, + FMT_DXT1_AS_16_16_16_16 = 51, + FMT_DXT2_3_AS_16_16_16_16 = 52, + FMT_DXT4_5_AS_16_16_16_16 = 53, + FMT_2_10_10_10_AS_16_16_16_16 = 54, + FMT_10_11_11_AS_16_16_16_16 = 55, + FMT_11_11_10_AS_16_16_16_16 = 56, + FMT_32_32_32_FLOAT = 57, + FMT_DXT3A = 58, + FMT_DXT5A = 59, + FMT_CTX1 = 60, + FMT_DXT3A_AS_1_1_1_1 = 61 +}; + +#define REG_PERF_MODE_CNT 0x0 +#define REG_PERF_STATE_RESET 0x0 +#define REG_PERF_STATE_ENABLE 0x1 +#define REG_PERF_STATE_FREEZE 0x2 + +#define RB_EDRAM_INFO_EDRAM_SIZE_SIZE 4 +#define RB_EDRAM_INFO_EDRAM_MAPPING_MODE_SIZE 2 +#define RB_EDRAM_INFO_UNUSED0_SIZE 8 +#define RB_EDRAM_INFO_EDRAM_RANGE_SIZE 18 + +struct rb_edram_info_t { + unsigned int edram_size:RB_EDRAM_INFO_EDRAM_SIZE_SIZE; + unsigned int edram_mapping_mode:RB_EDRAM_INFO_EDRAM_MAPPING_MODE_SIZE; + unsigned int unused0:RB_EDRAM_INFO_UNUSED0_SIZE; + unsigned int edram_range:RB_EDRAM_INFO_EDRAM_RANGE_SIZE; +}; + +union reg_rb_edram_info { + unsigned int val; + struct rb_edram_info_t f; +}; + +#define RBBM_READ_ERROR_UNUSED0_SIZE 2 +#define RBBM_READ_ERROR_READ_ADDRESS_SIZE 15 +#define RBBM_READ_ERROR_UNUSED1_SIZE 13 +#define RBBM_READ_ERROR_READ_REQUESTER_SIZE 1 +#define RBBM_READ_ERROR_READ_ERROR_SIZE 1 + +struct rbbm_read_error_t { + unsigned int unused0:RBBM_READ_ERROR_UNUSED0_SIZE; + unsigned int read_address:RBBM_READ_ERROR_READ_ADDRESS_SIZE; + unsigned int unused1:RBBM_READ_ERROR_UNUSED1_SIZE; + unsigned int read_requester:RBBM_READ_ERROR_READ_REQUESTER_SIZE; + unsigned int read_error:RBBM_READ_ERROR_READ_ERROR_SIZE; +}; + +union rbbm_read_error_u { + unsigned int val:32; + struct rbbm_read_error_t f; +}; + +#define CP_RB_CNTL_RB_BUFSZ_SIZE 6 +#define CP_RB_CNTL_UNUSED0_SIZE 2 +#define CP_RB_CNTL_RB_BLKSZ_SIZE 6 +#define CP_RB_CNTL_UNUSED1_SIZE 2 +#define CP_RB_CNTL_BUF_SWAP_SIZE 2 +#define CP_RB_CNTL_UNUSED2_SIZE 2 +#define CP_RB_CNTL_RB_POLL_EN_SIZE 1 +#define CP_RB_CNTL_UNUSED3_SIZE 6 +#define CP_RB_CNTL_RB_NO_UPDATE_SIZE 1 +#define CP_RB_CNTL_UNUSED4_SIZE 3 +#define CP_RB_CNTL_RB_RPTR_WR_ENA_SIZE 1 + +struct cp_rb_cntl_t { + unsigned int rb_bufsz:CP_RB_CNTL_RB_BUFSZ_SIZE; + unsigned int unused0:CP_RB_CNTL_UNUSED0_SIZE; + unsigned int rb_blksz:CP_RB_CNTL_RB_BLKSZ_SIZE; + unsigned int unused1:CP_RB_CNTL_UNUSED1_SIZE; + unsigned int buf_swap:CP_RB_CNTL_BUF_SWAP_SIZE; + unsigned int unused2:CP_RB_CNTL_UNUSED2_SIZE; + unsigned int rb_poll_en:CP_RB_CNTL_RB_POLL_EN_SIZE; + unsigned int unused3:CP_RB_CNTL_UNUSED3_SIZE; + unsigned int rb_no_update:CP_RB_CNTL_RB_NO_UPDATE_SIZE; + unsigned int unused4:CP_RB_CNTL_UNUSED4_SIZE; + unsigned int rb_rptr_wr_ena:CP_RB_CNTL_RB_RPTR_WR_ENA_SIZE; +}; + +union reg_cp_rb_cntl { + unsigned int val:32; + struct cp_rb_cntl_t f; +}; + +#define RB_COLOR_INFO__COLOR_FORMAT_MASK 0x0000000fL +#define RB_COPY_DEST_INFO__COPY_DEST_FORMAT__SHIFT 0x00000004 + + +#define SQ_INT_CNTL__PS_WATCHDOG_MASK 0x00000001L +#define SQ_INT_CNTL__VS_WATCHDOG_MASK 0x00000002L + +#define RBBM_INT_CNTL__RDERR_INT_MASK 0x00000001L +#define RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK 0x00000002L +#define RBBM_INT_CNTL__GUI_IDLE_INT_MASK 0x00080000L + +#define RBBM_STATUS__CMDFIFO_AVAIL_MASK 0x0000001fL +#define RBBM_STATUS__TC_BUSY_MASK 0x00000020L +#define RBBM_STATUS__HIRQ_PENDING_MASK 0x00000100L +#define RBBM_STATUS__CPRQ_PENDING_MASK 0x00000200L +#define RBBM_STATUS__CFRQ_PENDING_MASK 0x00000400L +#define RBBM_STATUS__PFRQ_PENDING_MASK 0x00000800L +#define RBBM_STATUS__VGT_BUSY_NO_DMA_MASK 0x00001000L +#define RBBM_STATUS__RBBM_WU_BUSY_MASK 0x00004000L +#define RBBM_STATUS__CP_NRT_BUSY_MASK 0x00010000L +#define RBBM_STATUS__MH_BUSY_MASK 0x00040000L +#define RBBM_STATUS__MH_COHERENCY_BUSY_MASK 0x00080000L +#define RBBM_STATUS__SX_BUSY_MASK 0x00200000L +#define RBBM_STATUS__TPC_BUSY_MASK 0x00400000L +#define RBBM_STATUS__SC_CNTX_BUSY_MASK 0x01000000L +#define RBBM_STATUS__PA_BUSY_MASK 0x02000000L +#define RBBM_STATUS__VGT_BUSY_MASK 0x04000000L +#define RBBM_STATUS__SQ_CNTX17_BUSY_MASK 0x08000000L +#define RBBM_STATUS__SQ_CNTX0_BUSY_MASK 0x10000000L +#define RBBM_STATUS__RB_CNTX_BUSY_MASK 0x40000000L +#define RBBM_STATUS__GUI_ACTIVE_MASK 0x80000000L + +#define CP_INT_CNTL__SW_INT_MASK 0x00080000L +#define CP_INT_CNTL__T0_PACKET_IN_IB_MASK 0x00800000L +#define CP_INT_CNTL__OPCODE_ERROR_MASK 0x01000000L +#define CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK 0x02000000L +#define CP_INT_CNTL__RESERVED_BIT_ERROR_MASK 0x04000000L +#define CP_INT_CNTL__IB_ERROR_MASK 0x08000000L +#define CP_INT_CNTL__IB2_INT_MASK 0x20000000L +#define CP_INT_CNTL__IB1_INT_MASK 0x40000000L +#define CP_INT_CNTL__RB_INT_MASK 0x80000000L + +#define MASTER_INT_SIGNAL__MH_INT_STAT 0x00000020L +#define MASTER_INT_SIGNAL__SQ_INT_STAT 0x04000000L +#define MASTER_INT_SIGNAL__CP_INT_STAT 0x40000000L +#define MASTER_INT_SIGNAL__RBBM_INT_STAT 0x80000000L + +#define RB_EDRAM_INFO__EDRAM_SIZE_MASK 0x0000000fL +#define RB_EDRAM_INFO__EDRAM_RANGE_MASK 0xffffc000L + +#define MH_ARBITER_CONFIG__SAME_PAGE_GRANULARITY__SHIFT 0x00000006 +#define MH_ARBITER_CONFIG__L1_ARB_ENABLE__SHIFT 0x00000007 +#define MH_ARBITER_CONFIG__L1_ARB_HOLD_ENABLE__SHIFT 0x00000008 +#define MH_ARBITER_CONFIG__L2_ARB_CONTROL__SHIFT 0x00000009 +#define MH_ARBITER_CONFIG__PAGE_SIZE__SHIFT 0x0000000a +#define MH_ARBITER_CONFIG__TC_REORDER_ENABLE__SHIFT 0x0000000d +#define MH_ARBITER_CONFIG__TC_ARB_HOLD_ENABLE__SHIFT 0x0000000e +#define MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT_ENABLE__SHIFT 0x0000000f +#define MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT__SHIFT 0x00000010 +#define MH_ARBITER_CONFIG__CP_CLNT_ENABLE__SHIFT 0x00000016 +#define MH_ARBITER_CONFIG__VGT_CLNT_ENABLE__SHIFT 0x00000017 +#define MH_ARBITER_CONFIG__TC_CLNT_ENABLE__SHIFT 0x00000018 +#define MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT 0x00000019 +#define MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT 0x0000001a + +#define MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT 0x00000004 +#define MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT 0x00000006 +#define MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT 0x00000008 +#define MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT 0x0000000a +#define MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT 0x0000000c +#define MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT 0x0000000e +#define MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT 0x00000010 +#define MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT 0x00000012 +#define MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT 0x00000014 +#define MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT 0x00000016 +#define MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT 0x00000018 + +#define CP_RB_CNTL__RB_BUFSZ__SHIFT 0x00000000 +#define CP_RB_CNTL__RB_BLKSZ__SHIFT 0x00000008 +#define CP_RB_CNTL__RB_POLL_EN__SHIFT 0x00000014 +#define CP_RB_CNTL__RB_NO_UPDATE__SHIFT 0x0000001b + +#define RB_COLOR_INFO__COLOR_FORMAT__SHIFT 0x00000000 +#define RB_EDRAM_INFO__EDRAM_MAPPING_MODE__SHIFT 0x00000004 +#define RB_EDRAM_INFO__EDRAM_RANGE__SHIFT 0x0000000e + +#define REG_CP_CSQ_IB1_STAT 0x01FE +#define REG_CP_CSQ_IB2_STAT 0x01FF +#define REG_CP_CSQ_RB_STAT 0x01FD +#define REG_CP_DEBUG 0x01FC +#define REG_CP_IB1_BASE 0x0458 +#define REG_CP_IB1_BUFSZ 0x0459 +#define REG_CP_IB2_BASE 0x045A +#define REG_CP_IB2_BUFSZ 0x045B +#define REG_CP_INT_ACK 0x01F4 +#define REG_CP_INT_CNTL 0x01F2 +#define REG_CP_INT_STATUS 0x01F3 +#define REG_CP_ME_CNTL 0x01F6 +#define REG_CP_ME_RAM_DATA 0x01FA +#define REG_CP_ME_RAM_WADDR 0x01F8 +#define REG_CP_ME_STATUS 0x01F7 +#define REG_CP_PFP_UCODE_ADDR 0x00C0 +#define REG_CP_PFP_UCODE_DATA 0x00C1 +#define REG_CP_QUEUE_THRESHOLDS 0x01D5 +#define REG_CP_RB_BASE 0x01C0 +#define REG_CP_RB_CNTL 0x01C1 +#define REG_CP_RB_RPTR 0x01C4 +#define REG_CP_RB_RPTR_ADDR 0x01C3 +#define REG_CP_RB_RPTR_WR 0x01C7 +#define REG_CP_RB_WPTR 0x01C5 +#define REG_CP_RB_WPTR_BASE 0x01C8 +#define REG_CP_RB_WPTR_DELAY 0x01C6 +#define REG_CP_STAT 0x047F +#define REG_CP_STATE_DEBUG_DATA 0x01ED +#define REG_CP_STATE_DEBUG_INDEX 0x01EC +#define REG_CP_ST_BASE 0x044D +#define REG_CP_ST_BUFSZ 0x044E + +#define REG_CP_PERFMON_CNTL 0x0444 +#define REG_CP_PERFCOUNTER_SELECT 0x0445 +#define REG_CP_PERFCOUNTER_LO 0x0446 +#define REG_CP_PERFCOUNTER_HI 0x0447 + +#define REG_RBBM_PERFCOUNTER1_SELECT 0x0395 +#define REG_RBBM_PERFCOUNTER1_HI 0x0398 +#define REG_RBBM_PERFCOUNTER1_LO 0x0397 + +#define REG_MASTER_INT_SIGNAL 0x03B7 + +#define REG_MH_ARBITER_CONFIG 0x0A40 +#define REG_MH_INTERRUPT_CLEAR 0x0A44 +#define REG_MH_INTERRUPT_MASK 0x0A42 +#define REG_MH_INTERRUPT_STATUS 0x0A43 +#define REG_MH_MMU_CONFIG 0x0040 +#define REG_MH_MMU_INVALIDATE 0x0045 +#define REG_MH_MMU_MPU_BASE 0x0046 +#define REG_MH_MMU_MPU_END 0x0047 +#define REG_MH_MMU_PAGE_FAULT 0x0043 +#define REG_MH_MMU_PT_BASE 0x0042 +#define REG_MH_MMU_TRAN_ERROR 0x0044 +#define REG_MH_MMU_VA_RANGE 0x0041 +#define REG_MH_CLNT_INTF_CTRL_CONFIG1 0x0A54 +#define REG_MH_CLNT_INTF_CTRL_CONFIG2 0x0A55 + +#define REG_PA_CL_VPORT_XSCALE 0x210F +#define REG_PA_CL_VPORT_ZOFFSET 0x2114 +#define REG_PA_CL_VPORT_ZSCALE 0x2113 +#define REG_PA_CL_CLIP_CNTL 0x2204 +#define REG_PA_CL_VTE_CNTL 0x2206 +#define REG_PA_SC_AA_MASK 0x2312 +#define REG_PA_SC_LINE_CNTL 0x2300 +#define REG_PA_SC_SCREEN_SCISSOR_BR 0x200F +#define REG_PA_SC_SCREEN_SCISSOR_TL 0x200E +#define REG_PA_SC_VIZ_QUERY 0x2293 +#define REG_PA_SC_VIZ_QUERY_STATUS 0x0C44 +#define REG_PA_SC_WINDOW_OFFSET 0x2080 +#define REG_PA_SC_WINDOW_SCISSOR_BR 0x2082 +#define REG_PA_SC_WINDOW_SCISSOR_TL 0x2081 +#define REG_PA_SU_FACE_DATA 0x0C86 +#define REG_PA_SU_POINT_SIZE 0x2280 +#define REG_PA_SU_LINE_CNTL 0x2282 +#define REG_PA_SU_POLY_OFFSET_BACK_OFFSET 0x2383 +#define REG_PA_SU_POLY_OFFSET_FRONT_SCALE 0x2380 +#define REG_PA_SU_SC_MODE_CNTL 0x2205 + +#define REG_PC_INDEX_OFFSET 0x2102 + +#define REG_RBBM_CNTL 0x003B +#define REG_RBBM_INT_ACK 0x03B6 +#define REG_RBBM_INT_CNTL 0x03B4 +#define REG_RBBM_INT_STATUS 0x03B5 +#define REG_RBBM_PATCH_RELEASE 0x0001 +#define REG_RBBM_PERIPHID1 0x03F9 +#define REG_RBBM_PERIPHID2 0x03FA +#define REG_RBBM_DEBUG 0x039B +#define REG_RBBM_DEBUG_OUT 0x03A0 +#define REG_RBBM_DEBUG_CNTL 0x03A1 +#define REG_RBBM_PM_OVERRIDE1 0x039C +#define REG_RBBM_PM_OVERRIDE2 0x039D +#define REG_RBBM_READ_ERROR 0x03B3 +#define REG_RBBM_SOFT_RESET 0x003C +#define REG_RBBM_STATUS 0x05D0 + +#define REG_RB_COLORCONTROL 0x2202 +#define REG_RB_COLOR_DEST_MASK 0x2326 +#define REG_RB_COLOR_MASK 0x2104 +#define REG_RB_COPY_CONTROL 0x2318 +#define REG_RB_DEPTHCONTROL 0x2200 +#define REG_RB_EDRAM_INFO 0x0F02 +#define REG_RB_MODECONTROL 0x2208 +#define REG_RB_SURFACE_INFO 0x2000 +#define REG_RB_SAMPLE_POS 0x220a + +#define REG_SCRATCH_ADDR 0x01DD +#define REG_SCRATCH_REG0 0x0578 +#define REG_SCRATCH_REG2 0x057A +#define REG_SCRATCH_UMSK 0x01DC + +#define REG_SQ_CF_BOOLEANS 0x4900 +#define REG_SQ_CF_LOOP 0x4908 +#define REG_SQ_GPR_MANAGEMENT 0x0D00 +#define REG_SQ_INST_STORE_MANAGMENT 0x0D02 +#define REG_SQ_INT_ACK 0x0D36 +#define REG_SQ_INT_CNTL 0x0D34 +#define REG_SQ_INT_STATUS 0x0D35 +#define REG_SQ_PROGRAM_CNTL 0x2180 +#define REG_SQ_PS_PROGRAM 0x21F6 +#define REG_SQ_VS_PROGRAM 0x21F7 +#define REG_SQ_WRAPPING_0 0x2183 +#define REG_SQ_WRAPPING_1 0x2184 + +#define REG_VGT_ENHANCE 0x2294 +#define REG_VGT_INDX_OFFSET 0x2102 +#define REG_VGT_MAX_VTX_INDX 0x2100 +#define REG_VGT_MIN_VTX_INDX 0x2101 + +#define REG_TP0_CHICKEN 0x0E1E +#define REG_TC_CNTL_STATUS 0x0E00 +#define REG_PA_SC_AA_CONFIG 0x2301 +#define REG_VGT_VERTEX_REUSE_BLOCK_CNTL 0x2316 +#define REG_SQ_INTERPOLATOR_CNTL 0x2182 +#define REG_RB_DEPTH_INFO 0x2002 +#define REG_COHER_DEST_BASE_0 0x2006 +#define REG_RB_FOG_COLOR 0x2109 +#define REG_RB_STENCILREFMASK_BF 0x210C +#define REG_PA_SC_LINE_STIPPLE 0x2283 +#define REG_SQ_PS_CONST 0x2308 +#define REG_RB_DEPTH_CLEAR 0x231D +#define REG_RB_SAMPLE_COUNT_CTL 0x2324 +#define REG_SQ_CONSTANT_0 0x4000 +#define REG_SQ_FETCH_0 0x4800 + +#define REG_MH_AXI_ERROR 0xA45 +#define REG_MH_DEBUG_CTRL 0xA4E +#define REG_MH_DEBUG_DATA 0xA4F +#define REG_COHER_BASE_PM4 0xA2A +#define REG_COHER_STATUS_PM4 0xA2B +#define REG_COHER_SIZE_PM4 0xA29 + +#endif /* __A200_REG_H */ diff --git a/drivers/gpu/msm/a220_reg.h b/drivers/gpu/msm/a220_reg.h new file mode 100644 index 0000000000000..9542a9bae10c7 --- /dev/null +++ b/drivers/gpu/msm/a220_reg.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __A205_REG_H +#define __A205_REG_H + +#define REG_LEIA_PC_INDX_OFFSET REG_VGT_INDX_OFFSET +#define REG_LEIA_PC_VERTEX_REUSE_BLOCK_CNTL REG_VGT_VERTEX_REUSE_BLOCK_CNTL +#define REG_LEIA_PC_MAX_VTX_INDX REG_VGT_MAX_VTX_INDX +#define REG_LEIA_GRAS_CONTROL 0x2210 +#define REG_LEIA_VSC_BIN_SIZE 0x0C01 +#define REG_LEIA_VSC_PIPE_DATA_LENGTH_7 0x0C1D + +#endif /*__A205_REG_H */ diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c new file mode 100644 index 0000000000000..8462ea0f6ef4a --- /dev/null +++ b/drivers/gpu/msm/adreno.c @@ -0,0 +1,1366 @@ +/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include + +#include "kgsl.h" +#include "kgsl_cffdump.h" + +#include "adreno.h" +#include "adreno_pm4types.h" +#include "adreno_debugfs.h" +#include "adreno_postmortem.h" + +#include "a200_reg.h" + +#define DRIVER_VERSION_MAJOR 3 +#define DRIVER_VERSION_MINOR 1 + +#define GSL_RBBM_INT_MASK \ + (RBBM_INT_CNTL__RDERR_INT_MASK | \ + RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK) + +#define GSL_SQ_INT_MASK \ + (SQ_INT_CNTL__PS_WATCHDOG_MASK | \ + SQ_INT_CNTL__VS_WATCHDOG_MASK) + +/* Adreno MH arbiter config*/ +#define ADRENO_CFG_MHARB \ + (0x10 \ + | (0 << MH_ARBITER_CONFIG__SAME_PAGE_GRANULARITY__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__L1_ARB_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__L1_ARB_HOLD_ENABLE__SHIFT) \ + | (0 << MH_ARBITER_CONFIG__L2_ARB_CONTROL__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__PAGE_SIZE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__TC_REORDER_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__TC_ARB_HOLD_ENABLE__SHIFT) \ + | (0 << MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT_ENABLE__SHIFT) \ + | (0x8 << MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__CP_CLNT_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__VGT_CLNT_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__TC_CLNT_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT)) + +#define ADRENO_MMU_CONFIG \ + (0x01 \ + | (MMU_CONFIG << MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT)) + +/* max msecs to wait for gpu to finish its operation(s) */ +#define MAX_WAITGPU_SECS (HZ + HZ/2) + +static struct adreno_device device_3d0 = { + .dev = { + .name = DEVICE_3D0_NAME, + .id = KGSL_DEVICE_3D0, + .ver_major = DRIVER_VERSION_MAJOR, + .ver_minor = DRIVER_VERSION_MINOR, + .mmu = { + .config = ADRENO_MMU_CONFIG, + /* turn off memory protection unit by setting + acceptable physical address range to include + all pages. */ + .mpu_base = 0x00000000, + .mpu_range = 0xFFFFF000, + .reg = { + .config = REG_MH_MMU_CONFIG, + .mpu_base = REG_MH_MMU_MPU_BASE, + .mpu_end = REG_MH_MMU_MPU_END, + .va_range = REG_MH_MMU_VA_RANGE, + .pt_page = REG_MH_MMU_PT_BASE, + .page_fault = REG_MH_MMU_PAGE_FAULT, + .tran_error = REG_MH_MMU_TRAN_ERROR, + .invalidate = REG_MH_MMU_INVALIDATE, + .interrupt_mask = REG_MH_INTERRUPT_MASK, + .interrupt_status = REG_MH_INTERRUPT_STATUS, + .interrupt_clear = REG_MH_INTERRUPT_CLEAR, + .axi_error = REG_MH_AXI_ERROR, + }, + }, + .pwrctrl = { + .pwr_rail = PWR_RAIL_GRP_CLK, + .regulator_name = "fs_gfx3d", + .irq_name = KGSL_3D0_IRQ, + .src_clk_name = "grp_src_clk", + }, + .mutex = __MUTEX_INITIALIZER(device_3d0.dev.mutex), + .state = KGSL_STATE_INIT, + .active_cnt = 0, + .iomemname = KGSL_3D0_REG_MEMORY, + }, + .gmemspace = { + .gpu_base = 0, + .sizebytes = SZ_256K, + }, + .pfp_fw = NULL, + .pm4_fw = NULL, +}; + +static void __devinit adreno_getfunctable(struct kgsl_functable *ftbl); + +static int adreno_gmeminit(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = &adreno_dev->dev; + union reg_rb_edram_info rb_edram_info; + unsigned int gmem_size; + unsigned int edram_value = 0; + + /* make sure edram range is aligned to size */ + BUG_ON(adreno_dev->gmemspace.gpu_base & + (adreno_dev->gmemspace.sizebytes - 1)); + + /* get edram_size value equivalent */ + gmem_size = (adreno_dev->gmemspace.sizebytes >> 14); + while (gmem_size >>= 1) + edram_value++; + + rb_edram_info.val = 0; + + rb_edram_info.f.edram_size = edram_value; + if (device->chip_id != KGSL_CHIPID_LEIA_REV470) + rb_edram_info.f.edram_mapping_mode = 0; /* EDRAM_MAP_UPPER */ + + /* must be aligned to size */ + rb_edram_info.f.edram_range = (adreno_dev->gmemspace.gpu_base >> 14); + + adreno_regwrite(device, REG_RB_EDRAM_INFO, rb_edram_info.val); + + return 0; +} + +static int adreno_gmemclose(struct kgsl_device *device) +{ + adreno_regwrite(device, REG_RB_EDRAM_INFO, 0x00000000); + + return 0; +} + +static void adreno_rbbm_intrcallback(struct kgsl_device *device) +{ + unsigned int status = 0; + unsigned int rderr = 0; + + adreno_regread_isr(device, REG_RBBM_INT_STATUS, &status); + + if (status & RBBM_INT_CNTL__RDERR_INT_MASK) { + union rbbm_read_error_u rerr; + adreno_regread_isr(device, REG_RBBM_READ_ERROR, &rderr); + rerr.val = rderr; + if (rerr.f.read_address == REG_CP_INT_STATUS && + rerr.f.read_error && + rerr.f.read_requester) + KGSL_DRV_WARN(device, + "rbbm read error interrupt: %08x\n", rderr); + else + KGSL_DRV_CRIT(device, + "rbbm read error interrupt: %08x\n", rderr); + } else if (status & RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK) { + KGSL_DRV_INFO(device, "rbbm display update interrupt\n"); + } else if (status & RBBM_INT_CNTL__GUI_IDLE_INT_MASK) { + KGSL_DRV_INFO(device, "rbbm gui idle interrupt\n"); + } else { + KGSL_CMD_WARN(device, + "bad bits in REG_CP_INT_STATUS %08x\n", status); + } + + status &= GSL_RBBM_INT_MASK; + adreno_regwrite_isr(device, REG_RBBM_INT_ACK, status); +} + +static void adreno_sq_intrcallback(struct kgsl_device *device) +{ + unsigned int status = 0; + + adreno_regread_isr(device, REG_SQ_INT_STATUS, &status); + + if (status & SQ_INT_CNTL__PS_WATCHDOG_MASK) + KGSL_DRV_INFO(device, "sq ps watchdog interrupt\n"); + else if (status & SQ_INT_CNTL__VS_WATCHDOG_MASK) + KGSL_DRV_INFO(device, "sq vs watchdog interrupt\n"); + else + KGSL_DRV_WARN(device, + "bad bits in REG_SQ_INT_STATUS %08x\n", status); + + + status &= GSL_SQ_INT_MASK; + adreno_regwrite_isr(device, REG_SQ_INT_ACK, status); +} + +irqreturn_t adreno_isr(int irq, void *data) +{ + irqreturn_t result = IRQ_NONE; + struct kgsl_device *device; + unsigned int status; + + device = (struct kgsl_device *) data; + + BUG_ON(device == NULL); + BUG_ON(device->regspace.sizebytes == 0); + BUG_ON(device->regspace.mmio_virt_base == 0); + + adreno_regread_isr(device, REG_MASTER_INT_SIGNAL, &status); + + if (status & MASTER_INT_SIGNAL__MH_INT_STAT) { + kgsl_mh_intrcallback(device); + result = IRQ_HANDLED; + } + + if (status & MASTER_INT_SIGNAL__CP_INT_STAT) { + kgsl_cp_intrcallback(device); + result = IRQ_HANDLED; + } + + if (status & MASTER_INT_SIGNAL__RBBM_INT_STAT) { + adreno_rbbm_intrcallback(device); + result = IRQ_HANDLED; + } + + if (status & MASTER_INT_SIGNAL__SQ_INT_STAT) { + adreno_sq_intrcallback(device); + result = IRQ_HANDLED; + } + + if (device->pwrctrl.nap_allowed == true) { + device->requested_state = KGSL_STATE_NAP; + schedule_work(&device->idle_check_ws); + } else if (device->pwrctrl.idle_pass == true) { + schedule_work(&device->idle_check_ws); + } + /* Reset the time-out in our idle timer */ + mod_timer(&device->idle_timer, + jiffies + device->pwrctrl.interval_timeout); + return result; +} + +static int adreno_cleanup_pt(struct kgsl_device *device, + struct kgsl_pagetable *pagetable) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; + + kgsl_mmu_unmap(pagetable, &rb->buffer_desc); + + kgsl_mmu_unmap(pagetable, &rb->memptrs_desc); + + kgsl_mmu_unmap(pagetable, &device->memstore); + + kgsl_mmu_unmap(pagetable, &device->mmu.dummyspace); + + return 0; +} + +static int adreno_setup_pt(struct kgsl_device *device, + struct kgsl_pagetable *pagetable) +{ + int result = 0; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; + + BUG_ON(rb->buffer_desc.physaddr == 0); + BUG_ON(rb->memptrs_desc.physaddr == 0); + BUG_ON(device->memstore.physaddr == 0); +#ifdef CONFIG_MSM_KGSL_MMU + BUG_ON(device->mmu.dummyspace.physaddr == 0); +#endif + result = kgsl_mmu_map_global(pagetable, &rb->buffer_desc, + GSL_PT_PAGE_RV); + if (result) + goto error; + + result = kgsl_mmu_map_global(pagetable, &rb->memptrs_desc, + GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); + if (result) + goto unmap_buffer_desc; + + result = kgsl_mmu_map_global(pagetable, &device->memstore, + GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); + if (result) + goto unmap_memptrs_desc; + + result = kgsl_mmu_map_global(pagetable, &device->mmu.dummyspace, + GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); + if (result) + goto unmap_memstore_desc; + + return result; + +unmap_memstore_desc: + kgsl_mmu_unmap(pagetable, &device->memstore); + +unmap_memptrs_desc: + kgsl_mmu_unmap(pagetable, &rb->memptrs_desc); + +unmap_buffer_desc: + kgsl_mmu_unmap(pagetable, &rb->buffer_desc); + +error: + return result; +} + +static int adreno_setstate(struct kgsl_device *device, uint32_t flags) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + unsigned int link[32]; + unsigned int *cmds = &link[0]; + int sizedwords = 0; + unsigned int mh_mmu_invalidate = 0x00000003; /*invalidate all and tc */ + +#ifndef CONFIG_MSM_KGSL_MMU + return 0; +#endif + /* if possible, set via command stream, + * otherwise set via direct register writes + */ + if (adreno_dev->drawctxt_active) { + if (flags & KGSL_MMUFLAGS_PTUPDATE) { + /* wait for graphics pipe to be idle */ + *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmds++ = 0x00000000; + + /* set page table base */ + *cmds++ = pm4_type0_packet(REG_MH_MMU_PT_BASE, 1); + *cmds++ = device->mmu.hwpagetable->base.gpuaddr; + sizedwords += 4; + } + + if (flags & KGSL_MMUFLAGS_TLBFLUSH) { + if (!(flags & KGSL_MMUFLAGS_PTUPDATE)) { + *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, + 1); + *cmds++ = 0x00000000; + sizedwords += 2; + } + *cmds++ = pm4_type0_packet(REG_MH_MMU_INVALIDATE, 1); + *cmds++ = mh_mmu_invalidate; + sizedwords += 2; + } + + if (flags & KGSL_MMUFLAGS_PTUPDATE && + device->chip_id != KGSL_CHIPID_LEIA_REV470) { + /* HW workaround: to resolve MMU page fault interrupts + * caused by the VGT.It prevents the CP PFP from filling + * the VGT DMA request fifo too early,thereby ensuring + * that the VGT will not fetch vertex/bin data until + * after the page table base register has been updated. + * + * Two null DRAW_INDX_BIN packets are inserted right + * after the page table base update, followed by a + * wait for idle. The null packets will fill up the + * VGT DMA request fifo and prevent any further + * vertex/bin updates from occurring until the wait + * has finished. */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = (0x4 << 16) | + (REG_PA_SU_SC_MODE_CNTL - 0x2000); + *cmds++ = 0; /* disable faceness generation */ + *cmds++ = pm4_type3_packet(PM4_SET_BIN_BASE_OFFSET, 1); + *cmds++ = device->mmu.dummyspace.gpuaddr; + *cmds++ = pm4_type3_packet(PM4_DRAW_INDX_BIN, 6); + *cmds++ = 0; /* viz query info */ + *cmds++ = 0x0003C004; /* draw indicator */ + *cmds++ = 0; /* bin base */ + *cmds++ = 3; /* bin size */ + *cmds++ = device->mmu.dummyspace.gpuaddr; /* dma base */ + *cmds++ = 6; /* dma size */ + *cmds++ = pm4_type3_packet(PM4_DRAW_INDX_BIN, 6); + *cmds++ = 0; /* viz query info */ + *cmds++ = 0x0003C004; /* draw indicator */ + *cmds++ = 0; /* bin base */ + *cmds++ = 3; /* bin size */ + /* dma base */ + *cmds++ = device->mmu.dummyspace.gpuaddr; + *cmds++ = 6; /* dma size */ + *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmds++ = 0x00000000; + sizedwords += 21; + } + + if (flags & (KGSL_MMUFLAGS_PTUPDATE | KGSL_MMUFLAGS_TLBFLUSH)) { + *cmds++ = pm4_type3_packet(PM4_INVALIDATE_STATE, 1); + *cmds++ = 0x7fff; /* invalidate all base pointers */ + sizedwords += 2; + } + + adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, + &link[0], sizedwords); + } else { + if (flags & KGSL_MMUFLAGS_PTUPDATE) { + adreno_idle(device, KGSL_TIMEOUT_DEFAULT); + adreno_regwrite(device, REG_MH_MMU_PT_BASE, + device->mmu.hwpagetable->base.gpuaddr); + } + + if (flags & KGSL_MMUFLAGS_TLBFLUSH) { + adreno_regwrite(device, REG_MH_MMU_INVALIDATE, + mh_mmu_invalidate); + } + } + + return 0; +} + +static unsigned int +adreno_getchipid(struct kgsl_device *device) +{ + unsigned int chipid; + unsigned int coreid, majorid, minorid, patchid, revid; + + /* YDX */ + adreno_regread(device, REG_RBBM_PERIPHID1, &coreid); + coreid &= 0xF; + + adreno_regread(device, REG_RBBM_PERIPHID2, &majorid); + majorid = (majorid >> 4) & 0xF; + + adreno_regread(device, REG_RBBM_PATCH_RELEASE, &revid); + /* this is a 16bit field, but extremely unlikely it would ever get + * this high + */ + minorid = ((revid >> 0) & 0xFF); + + + patchid = ((revid >> 16) & 0xFF); + + chipid = ((coreid << 24) | (majorid << 16) | + (minorid << 8) | (patchid << 0)); + + /* Hardware revision 211 (8650) returns the wrong chip ID */ + if (chipid == KGSL_CHIPID_YAMATODX_REV21) + chipid = KGSL_CHIPID_YAMATODX_REV211; + + /* Workaround Hardware revision issue of Z470 */ + if (chipid == KGSL_CHIPID_LEIA_REV470_TEMP) + chipid = KGSL_CHIPID_LEIA_REV470; + + + return chipid; +} + +static int __devinit +adreno_probe(struct platform_device *pdev) +{ + struct kgsl_device *device; + struct adreno_device *adreno_dev; + int status = -EINVAL; + + device = (struct kgsl_device *)pdev->id_entry->driver_data; + adreno_dev = ADRENO_DEVICE(device); + device->pdev = pdev; + + init_completion(&device->recovery_gate); + + adreno_getfunctable(&device->ftbl); + + status = adreno_ringbuffer_init(device); + if (status != 0) + goto error; + + status = kgsl_device_platform_probe(device, adreno_isr); + if (status) + goto error_close_rb; + + adreno_debugfs_init(device); + + device->flags &= ~KGSL_FLAGS_SOFT_RESET; + return 0; + +error_close_rb: + adreno_ringbuffer_close(&adreno_dev->ringbuffer); +error: + device->pdev = NULL; + return status; +} + +static int __devexit adreno_remove(struct platform_device *pdev) +{ + struct kgsl_device *device; + struct adreno_device *adreno_dev; + + device = (struct kgsl_device *)pdev->id_entry->driver_data; + adreno_dev = ADRENO_DEVICE(device); + + adreno_ringbuffer_close(&adreno_dev->ringbuffer); + kgsl_device_platform_remove(device); + + return 0; +} + +static int adreno_start(struct kgsl_device *device, unsigned int init_ram) +{ + int status = -EINVAL; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + int init_reftimestamp = 0x7fffffff; + + device->state = KGSL_STATE_INIT; + device->requested_state = KGSL_STATE_NONE; + /* Order pwrrail/clk sequence based upon platform. */ + if (device->pwrctrl.pwrrail_first) + kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_ON); + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_ON); + kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_ON); + if (!device->pwrctrl.pwrrail_first) + kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_ON); + + if (kgsl_mmu_start(device)) + goto error_clk_off; + + /*We need to make sure all blocks are powered up and clocked before + *issuing a soft reset. The overrides will then be turned off (set to 0) + */ + adreno_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0xfffffffe); + device->chip_id = adreno_getchipid(device); + + if (device->chip_id == CHIP_REV_251) + adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0x000000ff); + else + adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xffffffff); + + /* Only reset CP block if all blocks have previously been reset */ + if (!(device->flags & KGSL_FLAGS_SOFT_RESET) || + (device->chip_id != KGSL_CHIPID_LEIA_REV470)) { + adreno_regwrite(device, REG_RBBM_SOFT_RESET, 0xFFFFFFFF); + device->flags |= KGSL_FLAGS_SOFT_RESET; + } else + adreno_regwrite(device, REG_RBBM_SOFT_RESET, 0x00000001); + + /* The core is in an indeterminate state until the reset completes + * after 30ms. + */ + msleep(30); + + adreno_regwrite(device, REG_RBBM_SOFT_RESET, 0x00000000); + + adreno_regwrite(device, REG_RBBM_CNTL, 0x00004442); + + adreno_regwrite(device, REG_MH_ARBITER_CONFIG, + ADRENO_CFG_MHARB); + + if (device->chip_id != KGSL_CHIPID_LEIA_REV470) { + adreno_regwrite(device, + REG_MH_CLNT_INTF_CTRL_CONFIG1, 0x00030f27); + adreno_regwrite(device, + REG_MH_CLNT_INTF_CTRL_CONFIG2, 0x00472747); + } + + /* Remove 1k boundary check in z470 to avoid GPU hang. + Notice that, this solution won't work if both EBI and SMI are used */ + if (device->chip_id == KGSL_CHIPID_LEIA_REV470) { + adreno_regwrite(device, REG_MH_CLNT_INTF_CTRL_CONFIG1, + 0x00032f07); + } + + adreno_regwrite(device, REG_SQ_VS_PROGRAM, 0x00000000); + adreno_regwrite(device, REG_SQ_PS_PROGRAM, 0x00000000); + + adreno_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0); + if (device->chip_id != KGSL_CHIPID_LEIA_REV470) + adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0); + else + adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0x80); + + kgsl_sharedmem_writel(&device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), + init_reftimestamp); + + adreno_regwrite(device, REG_RBBM_DEBUG, 0x00080000); + + adreno_regwrite(device, REG_RBBM_INT_CNTL, GSL_RBBM_INT_MASK); + + /* make sure SQ interrupts are disabled */ + adreno_regwrite(device, REG_SQ_INT_CNTL, 0); + + if (device->chip_id == KGSL_CHIPID_LEIA_REV470) + adreno_dev->gmemspace.sizebytes = SZ_512K; + else + adreno_dev->gmemspace.sizebytes = SZ_256K; + adreno_gmeminit(adreno_dev); + + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_ON); + + status = adreno_ringbuffer_start(&adreno_dev->ringbuffer, init_ram); + if (status != 0) + goto error_irq_off; + + mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT); + return status; + +error_irq_off: + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); +error_clk_off: + kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_OFF); + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_OFF); + + kgsl_mmu_stop(device); + return status; +} + +static int adreno_stop(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + del_timer(&device->idle_timer); + adreno_regwrite(device, REG_RBBM_INT_CNTL, 0); + + adreno_regwrite(device, REG_SQ_INT_CNTL, 0); + + adreno_dev->drawctxt_active = NULL; + + adreno_ringbuffer_stop(&adreno_dev->ringbuffer); + + adreno_gmemclose(device); + + kgsl_mmu_stop(device); + + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); + if (!device->pwrctrl.pwrrail_first) + kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_OFF); + kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_OFF); + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_OFF); + if (device->pwrctrl.pwrrail_first) + kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_OFF); + + return 0; +} + +static int +adreno_recover_hang(struct kgsl_device *device) +{ + int ret; + unsigned int *rb_buffer; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; + unsigned int timestamp; + unsigned int num_rb_contents; + unsigned int bad_context; + unsigned int reftimestamp; + unsigned int enable_ts; + unsigned int soptimestamp; + unsigned int eoptimestamp; + struct adreno_context *drawctxt; + + KGSL_DRV_ERR(device, "Starting recovery from 3D GPU hang....\n"); + rb_buffer = vmalloc(rb->buffer_desc.size); + if (!rb_buffer) { + KGSL_MEM_ERR(device, + "Failed to allocate memory for recovery: %x\n", + rb->buffer_desc.size); + return -ENOMEM; + } + /* Extract valid contents from rb which can stil be executed after + * hang */ + ret = adreno_ringbuffer_extract(rb, rb_buffer, &num_rb_contents); + if (ret) + goto done; + timestamp = rb->timestamp; + KGSL_DRV_ERR(device, "Last issued timestamp: %x\n", timestamp); + kgsl_sharedmem_readl(&device->memstore, &bad_context, + KGSL_DEVICE_MEMSTORE_OFFSET(current_context)); + kgsl_sharedmem_readl(&device->memstore, &reftimestamp, + KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts)); + kgsl_sharedmem_readl(&device->memstore, &enable_ts, + KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable)); + kgsl_sharedmem_readl(&device->memstore, &soptimestamp, + KGSL_DEVICE_MEMSTORE_OFFSET(soptimestamp)); + kgsl_sharedmem_readl(&device->memstore, &eoptimestamp, + KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp)); + rmb(); + KGSL_CTXT_ERR(device, + "Context that caused a GPU hang: %x\n", bad_context); + /* restart device */ + ret = adreno_stop(device); + if (ret) + goto done; + ret = adreno_start(device, true); + if (ret) + goto done; + KGSL_DRV_ERR(device, "Device has been restarted after hang\n"); + /* Restore timestamp states */ + kgsl_sharedmem_writel(&device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(soptimestamp), + soptimestamp); + kgsl_sharedmem_writel(&device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp), + eoptimestamp); + kgsl_sharedmem_writel(&device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(soptimestamp), + soptimestamp); + if (num_rb_contents) { + kgsl_sharedmem_writel(&device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), + reftimestamp); + kgsl_sharedmem_writel(&device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable), + enable_ts); + } + wmb(); + /* Mark the invalid context so no more commands are accepted from + * that context */ + + drawctxt = (struct adreno_context *) bad_context; + + KGSL_CTXT_ERR(device, + "Context that caused a GPU hang: %x\n", bad_context); + + drawctxt->flags |= CTXT_FLAGS_GPU_HANG; + + /* Restore valid commands in ringbuffer */ + adreno_ringbuffer_restore(rb, rb_buffer, num_rb_contents); + rb->timestamp = timestamp; +done: + vfree(rb_buffer); + return ret; +} + +static int +adreno_dump_and_recover(struct kgsl_device *device) +{ + static int recovery; + int result = -ETIMEDOUT; + + if (device->state == KGSL_STATE_HUNG) + goto done; + if (device->state == KGSL_STATE_DUMP_AND_RECOVER && !recovery) { + mutex_unlock(&device->mutex); + wait_for_completion(&device->recovery_gate); + mutex_lock(&device->mutex); + if (!(device->state & KGSL_STATE_HUNG)) + /* recovery success */ + result = 0; + } else { + INIT_COMPLETION(device->recovery_gate); + /* Detected a hang - trigger an automatic dump */ + adreno_postmortem_dump(device, 0); + if (!recovery) { + recovery = 1; + result = adreno_recover_hang(device); + if (result) + device->state = KGSL_STATE_HUNG; + recovery = 0; + complete_all(&device->recovery_gate); + } else + KGSL_DRV_ERR(device, + "Cannot recover from another hang while " + "recovering from a hang\n"); + } +done: + return result; +} + +static int adreno_getproperty(struct kgsl_device *device, + enum kgsl_property_type type, + void *value, + unsigned int sizebytes) +{ + int status = -EINVAL; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + + switch (type) { + case KGSL_PROP_DEVICE_INFO: + { + struct kgsl_devinfo devinfo; + + if (sizebytes != sizeof(devinfo)) { + status = -EINVAL; + break; + } + + memset(&devinfo, 0, sizeof(devinfo)); + devinfo.device_id = device->id+1; + devinfo.chip_id = device->chip_id; + devinfo.mmu_enabled = kgsl_mmu_enabled(); + devinfo.gmem_hostbaseaddr = (unsigned int) + adreno_dev->gmemspace.mmio_virt_base; + devinfo.gmem_gpubaseaddr = adreno_dev->gmemspace. + gpu_base; + devinfo.gmem_sizebytes = adreno_dev->gmemspace. + sizebytes; + + if (copy_to_user(value, &devinfo, sizeof(devinfo)) != + 0) { + status = -EFAULT; + break; + } + status = 0; + } + break; + case KGSL_PROP_DEVICE_SHADOW: + { + struct kgsl_shadowprop shadowprop; + + if (sizebytes != sizeof(shadowprop)) { + status = -EINVAL; + break; + } + memset(&shadowprop, 0, sizeof(shadowprop)); + if (device->memstore.hostptr) { + /*NOTE: with mmu enabled, gpuaddr doesn't mean + * anything to mmap(). + */ + shadowprop.gpuaddr = device->memstore.physaddr; + shadowprop.size = device->memstore.size; + /* GSL needs this to be set, even if it + appears to be meaningless */ + shadowprop.flags = KGSL_FLAGS_INITIALIZED; + } + if (copy_to_user(value, &shadowprop, + sizeof(shadowprop))) { + status = -EFAULT; + break; + } + status = 0; + } + break; + case KGSL_PROP_MMU_ENABLE: + { +#ifdef CONFIG_MSM_KGSL_MMU + int mmuProp = 1; +#else + int mmuProp = 0; +#endif + if (sizebytes != sizeof(int)) { + status = -EINVAL; + break; + } + if (copy_to_user(value, &mmuProp, sizeof(mmuProp))) { + status = -EFAULT; + break; + } + status = 0; + } + break; + case KGSL_PROP_INTERRUPT_WAITS: + { + int int_waits = 1; + if (sizebytes != sizeof(int)) { + status = -EINVAL; + break; + } + if (copy_to_user(value, &int_waits, sizeof(int))) { + status = -EFAULT; + break; + } + status = 0; + } + break; + default: + status = -EINVAL; + } + + return status; +} + +/* Caller must hold the device mutex. */ +int adreno_idle(struct kgsl_device *device, unsigned int timeout) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; + unsigned int rbbm_status; + unsigned long wait_time = jiffies + MAX_WAITGPU_SECS; + + kgsl_cffdump_regpoll(device->id, REG_RBBM_STATUS << 2, + 0x00000000, 0x80000000); + /* first, wait until the CP has consumed all the commands in + * the ring buffer + */ +retry: + if (rb->flags & KGSL_FLAGS_STARTED) { + do { + GSL_RB_GET_READPTR(rb, &rb->rptr); + if (time_after(jiffies, wait_time)) { + KGSL_DRV_ERR(device, "rptr: %x, wptr: %x\n", + rb->rptr, rb->wptr); + goto err; + } + } while (rb->rptr != rb->wptr); + } + + /* now, wait for the GPU to finish its operations */ + wait_time = jiffies + MAX_WAITGPU_SECS; + while (time_before(jiffies, wait_time)) { + adreno_regread(device, REG_RBBM_STATUS, &rbbm_status); + if (rbbm_status == 0x110) + return 0; + } + +err: + KGSL_DRV_ERR(device, "spun too long waiting for RB to idle\n"); + if (!adreno_dump_and_recover(device)) { + wait_time = jiffies + MAX_WAITGPU_SECS; + goto retry; + } + return -ETIMEDOUT; +} + +static unsigned int adreno_isidle(struct kgsl_device *device) +{ + int status = false; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; + unsigned int rbbm_status; + + if (rb->flags & KGSL_FLAGS_STARTED) { + /* Is the ring buffer is empty? */ + GSL_RB_GET_READPTR(rb, &rb->rptr); + if (!device->active_cnt && (rb->rptr == rb->wptr)) { + /* Is the core idle? */ + adreno_regread(device, REG_RBBM_STATUS, + &rbbm_status); + if (rbbm_status == 0x110) + status = true; + } + } else { + KGSL_DRV_ERR(device, "ringbuffer not started\n"); + BUG(); + } + return status; +} + + +/******************************************************************/ +/* Caller must hold the driver mutex. */ +static int adreno_resume_context(struct kgsl_device *device) +{ + int status = 0; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + + if (device->pwrctrl.suspended_ctxt != NULL) { + adreno_drawctxt_switch(adreno_dev, + device->pwrctrl.suspended_ctxt, 0); + status = adreno_idle(device, 0); + + } + + return status; +} + +/******************************************************************/ +/* Caller must hold the device mutex. */ +static int adreno_suspend_context(struct kgsl_device *device) +{ + int status = 0; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + + /* save ctxt ptr and switch to NULL ctxt */ + device->pwrctrl.suspended_ctxt = adreno_dev->drawctxt_active; + if (device->pwrctrl.suspended_ctxt != NULL) { + adreno_drawctxt_switch(adreno_dev, NULL, 0); + status = adreno_idle(device, KGSL_TIMEOUT_DEFAULT); + } + + return status; +} + +uint8_t *kgsl_sharedmem_convertaddr(struct kgsl_device *device, + unsigned int pt_base, unsigned int gpuaddr, unsigned int *size) +{ + uint8_t *result = NULL; + struct kgsl_mem_entry *entry; + struct kgsl_process_private *priv; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_ringbuffer *ringbuffer = &adreno_dev->ringbuffer; + + if (kgsl_gpuaddr_in_memdesc(&ringbuffer->buffer_desc, gpuaddr)) { + return kgsl_gpuaddr_to_vaddr(&ringbuffer->buffer_desc, + gpuaddr, size); + } + + if (kgsl_gpuaddr_in_memdesc(&ringbuffer->memptrs_desc, gpuaddr)) { + return kgsl_gpuaddr_to_vaddr(&ringbuffer->memptrs_desc, + gpuaddr, size); + } + + if (kgsl_gpuaddr_in_memdesc(&device->memstore, gpuaddr)) { + return kgsl_gpuaddr_to_vaddr(&device->memstore, + gpuaddr, size); + } + + mutex_lock(&kgsl_driver.process_mutex); + list_for_each_entry(priv, &kgsl_driver.process_list, list) { + if (pt_base != 0 + && priv->pagetable + && priv->pagetable->base.gpuaddr != pt_base) { + continue; + } + + spin_lock(&priv->mem_lock); + entry = kgsl_sharedmem_find_region(priv, gpuaddr, + sizeof(unsigned int)); + if (entry) { + result = kgsl_gpuaddr_to_vaddr(&entry->memdesc, + gpuaddr, size); + spin_unlock(&priv->mem_lock); + mutex_unlock(&kgsl_driver.process_mutex); + return result; + } + spin_unlock(&priv->mem_lock); + } + mutex_unlock(&kgsl_driver.process_mutex); + + BUG_ON(!mutex_is_locked(&device->mutex)); + list_for_each_entry(entry, &device->memqueue, list) { + if (kgsl_gpuaddr_in_memdesc(&entry->memdesc, gpuaddr)) { + result = kgsl_gpuaddr_to_vaddr(&entry->memdesc, + gpuaddr, size); + break; + } + + } + return result; +} + +static void _adreno_regread(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int *value) +{ + unsigned int *reg; + BUG_ON(offsetwords*sizeof(uint32_t) >= device->regspace.sizebytes); + reg = (unsigned int *)(device->regspace.mmio_virt_base + + (offsetwords << 2)); + /*ensure this read finishes before the next one. + * i.e. act like normal readl() */ + *value = __raw_readl(reg); + rmb(); +} + +void adreno_regread(struct kgsl_device *device, unsigned int offsetwords, + unsigned int *value) +{ + kgsl_pre_hwaccess(device); + _adreno_regread(device, offsetwords, value); +} + +void adreno_regread_isr(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int *value) +{ + _adreno_regread(device, offsetwords, value); +} + +static void _adreno_regwrite(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int value) +{ + unsigned int *reg; + + BUG_ON(offsetwords*sizeof(uint32_t) >= device->regspace.sizebytes); + + kgsl_cffdump_regwrite(device->id, offsetwords << 2, value); + reg = (unsigned int *)(device->regspace.mmio_virt_base + + (offsetwords << 2)); + + /*ensure previous writes post before this one, + * i.e. act like normal writel() */ + wmb(); + __raw_writel(value, reg); +} + +void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords, + unsigned int value) +{ + kgsl_pre_hwaccess(device); + _adreno_regwrite(device, offsetwords, value); +} + +void adreno_regwrite_isr(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int value) +{ + _adreno_regwrite(device, offsetwords, value); +} + +static int kgsl_check_interrupt_timestamp(struct kgsl_device *device, + unsigned int timestamp) +{ + int status; + unsigned int ref_ts, enableflag; + + status = kgsl_check_timestamp(device, timestamp); + if (!status) { + mutex_lock(&device->mutex); + kgsl_sharedmem_readl(&device->memstore, &enableflag, + KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable)); + mb(); + + if (enableflag) { + kgsl_sharedmem_readl(&device->memstore, &ref_ts, + KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts)); + mb(); + if (timestamp_cmp(ref_ts, timestamp)) { + kgsl_sharedmem_writel(&device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), + timestamp); + wmb(); + } + } else { + unsigned int cmds[2]; + kgsl_sharedmem_writel(&device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), + timestamp); + enableflag = 1; + kgsl_sharedmem_writel(&device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable), + enableflag); + wmb(); + /* submit a dummy packet so that even if all + * commands upto timestamp get executed we will still + * get an interrupt */ + cmds[0] = pm4_type3_packet(PM4_NOP, 1); + cmds[1] = 0; + adreno_ringbuffer_issuecmds(device, 0, &cmds[0], 2); + } + mutex_unlock(&device->mutex); + } + + return status; +} + +/* + wait_io_event_interruptible_timeout checks for the exit condition before + placing a process in wait q. For conditional interrupts we expect the + process to already be in its wait q when its exit condition checking + function is called. +*/ +#define kgsl_wait_io_event_interruptible_timeout(wq, condition, timeout)\ +({ \ + long __ret = timeout; \ + __wait_io_event_interruptible_timeout(wq, condition, __ret); \ + __ret; \ +}) + +/* MUST be called with the device mutex held */ +static int adreno_waittimestamp(struct kgsl_device *device, + unsigned int timestamp, + unsigned int msecs) +{ + long status = 0; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + + if (timestamp != adreno_dev->ringbuffer.timestamp && + timestamp_cmp(timestamp, + adreno_dev->ringbuffer.timestamp)) { + KGSL_DRV_ERR(device, "Cannot wait for invalid ts: %x, " + "rb->timestamp: %x\n", + timestamp, adreno_dev->ringbuffer.timestamp); + status = -EINVAL; + goto done; + } + if (!kgsl_check_timestamp(device, timestamp)) { + mutex_unlock(&device->mutex); + /* We need to make sure that the process is placed in wait-q + * before its condition is called */ + status = kgsl_wait_io_event_interruptible_timeout( + device->wait_queue, + kgsl_check_interrupt_timestamp(device, + timestamp), msecs_to_jiffies(msecs)); + mutex_lock(&device->mutex); + + if (status > 0) + status = 0; + else if (status == 0) { + if (!kgsl_check_timestamp(device, timestamp)) { + status = -ETIMEDOUT; + KGSL_DRV_ERR(device, + "Device hang detected while waiting " + "for timestamp: %x, last " + "submitted(rb->timestamp): %x, wptr: " + "%x\n", timestamp, + adreno_dev->ringbuffer.timestamp, + adreno_dev->ringbuffer.wptr); + if (!adreno_dump_and_recover(device)) { + /* wait for idle after recovery as the + * timestamp that this process wanted + * to wait on may be invalid */ + if (!adreno_idle(device, + KGSL_TIMEOUT_DEFAULT)) + status = 0; + } + } + } + } + +done: + return (int)status; +} + +static unsigned int adreno_readtimestamp(struct kgsl_device *device, + enum kgsl_timestamp_type type) +{ + unsigned int timestamp = 0; + + if (type == KGSL_TIMESTAMP_CONSUMED) + adreno_regread(device, REG_CP_TIMESTAMP, ×tamp); + else if (type == KGSL_TIMESTAMP_RETIRED) + kgsl_sharedmem_readl(&device->memstore, ×tamp, + KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp)); + rmb(); + + return timestamp; +} + +static long adreno_ioctl(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + int result = 0; + struct kgsl_drawctxt_set_bin_base_offset *binbase; + struct kgsl_context *context; + + switch (cmd) { + case IOCTL_KGSL_DRAWCTXT_SET_BIN_BASE_OFFSET: + binbase = data; + + context = kgsl_find_context(dev_priv, binbase->drawctxt_id); + if (context) { + result = adreno_drawctxt_set_bin_base_offset( + dev_priv->device, + context, + binbase->offset); + } else { + result = -EINVAL; + KGSL_DRV_ERR(dev_priv->device, + "invalid drawctxt drawctxt_id %d " + "device_id=%d\n", + binbase->drawctxt_id, dev_priv->device->id); + } + break; + + default: + KGSL_DRV_INFO(dev_priv->device, + "invalid ioctl code %08x\n", cmd); + result = -EINVAL; + break; + } + return result; + +} + +static inline s64 adreno_ticks_to_us(u32 ticks, u32 gpu_freq) +{ + gpu_freq /= 1000000; + return ticks / gpu_freq; +} + +static unsigned int adreno_idle_calc(struct kgsl_device *device) +{ + unsigned int ret, reg; + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + + /* In order to calculate idle you have to have run the algorithm * + * at least once to get a start time. */ + if (pwr->time != 0) { + s64 total_time, busy_time, tmp; + /* Stop the performance moniter and read the current * + * busy cycles. */ + adreno_regwrite(device, + REG_CP_PERFMON_CNTL, + REG_PERF_MODE_CNT | + REG_PERF_STATE_FREEZE); + adreno_regread(device, REG_RBBM_PERFCOUNTER1_LO, ®); + tmp = ktime_to_us(ktime_get()); + total_time = tmp - pwr->time; + pwr->time = tmp; + busy_time = adreno_ticks_to_us(reg, device->pwrctrl. + pwrlevels[device->pwrctrl.active_pwrlevel]. + gpu_freq); + ret = total_time - busy_time; + adreno_regwrite(device, + REG_CP_PERFMON_CNTL, + REG_PERF_MODE_CNT | + REG_PERF_STATE_RESET); + } else { + pwr->time = ktime_to_us(ktime_get()); + ret = 0; + } + + /* re-enable the performance moniters */ + adreno_regread(device, REG_RBBM_PM_OVERRIDE2, ®); + adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, (reg | 0x40)); + adreno_regwrite(device, REG_RBBM_PERFCOUNTER1_SELECT, 0x1); + adreno_regwrite(device, + REG_CP_PERFMON_CNTL, + REG_PERF_MODE_CNT | REG_PERF_STATE_ENABLE); + return ret; +} + +static void __devinit adreno_getfunctable(struct kgsl_functable *ftbl) +{ + if (ftbl == NULL) + return; + ftbl->device_regread = adreno_regread; + ftbl->device_regwrite = adreno_regwrite; + ftbl->device_regread_isr = adreno_regread_isr; + ftbl->device_regwrite_isr = adreno_regwrite_isr; + ftbl->device_setstate = adreno_setstate; + ftbl->device_idle = adreno_idle; + ftbl->device_isidle = adreno_isidle; + ftbl->device_suspend_context = adreno_suspend_context; + ftbl->device_resume_context = adreno_resume_context; + ftbl->device_start = adreno_start; + ftbl->device_stop = adreno_stop; + ftbl->device_getproperty = adreno_getproperty; + ftbl->device_waittimestamp = adreno_waittimestamp; + ftbl->device_readtimestamp = adreno_readtimestamp; + ftbl->device_issueibcmds = adreno_ringbuffer_issueibcmds; + ftbl->device_drawctxt_create = adreno_drawctxt_create; + ftbl->device_drawctxt_destroy = adreno_drawctxt_destroy; + ftbl->device_ioctl = adreno_ioctl; + ftbl->device_setup_pt = adreno_setup_pt; + ftbl->device_cleanup_pt = adreno_cleanup_pt; + ftbl->device_idle_calc = adreno_idle_calc; +} + +static struct platform_device_id adreno_id_table[] = { + { DEVICE_3D0_NAME, (kernel_ulong_t)&device_3d0.dev, }, + { }, +}; +MODULE_DEVICE_TABLE(platform, adreno_id_table); + +static struct platform_driver adreno_platform_driver = { + .probe = adreno_probe, + .remove = __devexit_p(adreno_remove), + .suspend = kgsl_suspend_driver, + .resume = kgsl_resume_driver, + .id_table = adreno_id_table, + .driver = { + .owner = THIS_MODULE, + .name = DEVICE_3D_NAME, + .pm = &kgsl_pm_ops, + } +}; + +static int __init kgsl_3d_init(void) +{ + return platform_driver_register(&adreno_platform_driver); +} + +static void __exit kgsl_3d_exit(void) +{ + platform_driver_unregister(&adreno_platform_driver); +} + +module_init(kgsl_3d_init); +module_exit(kgsl_3d_exit); + +MODULE_DESCRIPTION("3D Graphics driver"); +MODULE_VERSION("1.2"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:kgsl_3d"); diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h new file mode 100644 index 0000000000000..ba155cd2af0b8 --- /dev/null +++ b/drivers/gpu/msm/adreno.h @@ -0,0 +1,77 @@ +/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __ADRENO_H +#define __ADRENO_H + +#include "adreno_drawctxt.h" +#include "adreno_ringbuffer.h" + +#define DEVICE_3D_NAME "kgsl-3d" +#define DEVICE_3D0_NAME "kgsl-3d0" + +#define ADRENO_DEVICE(device) \ + KGSL_CONTAINER_OF(device, struct adreno_device, dev) + +/* Flags to control command packet settings */ +#define KGSL_CMD_FLAGS_PMODE 0x00000001 +#define KGSL_CMD_FLAGS_NO_TS_CMP 0x00000002 +#define KGSL_CMD_FLAGS_NOT_KERNEL_CMD 0x00000004 + +/* Command identifiers */ +#define KGSL_CONTEXT_TO_MEM_IDENTIFIER 0xDEADBEEF +#define KGSL_CMD_IDENTIFIER 0xFEEDFACE + +struct adreno_device { + struct kgsl_device dev; /* Must be first field in this struct */ + struct kgsl_memregion gmemspace; + struct adreno_context *drawctxt_active; + wait_queue_head_t ib1_wq; + unsigned int *pfp_fw; + size_t pfp_fw_size; + unsigned int *pm4_fw; + size_t pm4_fw_size; + struct adreno_ringbuffer ringbuffer; +}; + +int adreno_idle(struct kgsl_device *device, unsigned int timeout); +void adreno_regread(struct kgsl_device *device, unsigned int offsetwords, + unsigned int *value); +void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords, + unsigned int value); +void adreno_regread_isr(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int *value); +void adreno_regwrite_isr(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int value); + +uint8_t *kgsl_sharedmem_convertaddr(struct kgsl_device *device, + unsigned int pt_base, unsigned int gpuaddr, unsigned int *size); + +#endif /*__ADRENO_H */ diff --git a/drivers/gpu/msm/adreno_debugfs.c b/drivers/gpu/msm/adreno_debugfs.c new file mode 100644 index 0000000000000..882ba5032d1f0 --- /dev/null +++ b/drivers/gpu/msm/adreno_debugfs.c @@ -0,0 +1,450 @@ +/* Copyright (c) 2002,2008-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +#include "kgsl.h" +#include "adreno_postmortem.h" +#include "adreno.h" + +#include "a200_reg.h" + +unsigned int kgsl_cff_dump_enable; +int kgsl_pm_regs_enabled; + +static uint32_t kgsl_ib_base; +static uint32_t kgsl_ib_size; + +static struct dentry *pm_d_debugfs; + +static int pm_dump_set(void *data, u64 val) +{ + struct kgsl_device *device = data; + + if (val) { + mutex_lock(&device->mutex); + adreno_postmortem_dump(device, 1); + mutex_unlock(&device->mutex); + } + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(pm_dump_fops, + NULL, + pm_dump_set, "%llu\n"); + +static int pm_regs_enabled_set(void *data, u64 val) +{ + kgsl_pm_regs_enabled = val ? 1 : 0; + return 0; +} + +static int pm_regs_enabled_get(void *data, u64 *val) +{ + *val = kgsl_pm_regs_enabled; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(pm_regs_enabled_fops, + pm_regs_enabled_get, + pm_regs_enabled_set, "%llu\n"); + + +static int kgsl_cff_dump_enable_set(void *data, u64 val) +{ +#ifdef CONFIG_MSM_KGSL_CFF_DUMP + kgsl_cff_dump_enable = (val != 0); + return 0; +#else + return -EINVAL; +#endif +} + +static int kgsl_cff_dump_enable_get(void *data, u64 *val) +{ + *val = kgsl_cff_dump_enable; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(kgsl_cff_dump_enable_fops, kgsl_cff_dump_enable_get, + kgsl_cff_dump_enable_set, "%llu\n"); + +static int kgsl_dbgfs_open(struct inode *inode, struct file *file) +{ + file->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE); + file->private_data = inode->i_private; + return 0; +} + +static int kgsl_dbgfs_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int kgsl_hex_dump(const char *prefix, int c, uint8_t *data, + int rowc, int linec, char __user *buff) +{ + int ss; + /* Prefix of 20 chars max, 32 bytes per row, in groups of four - that's + * 8 groups at 8 chars per group plus a space, plus new-line, plus + * ending character */ + char linebuf[20 + 64 + 1 + 1]; + + ss = snprintf(linebuf, sizeof(linebuf), prefix, c); + hex_dump_to_buffer(data, linec, rowc, 4, linebuf+ss, + sizeof(linebuf)-ss, 0); + strncat(linebuf, "\n", sizeof(linebuf)); + linebuf[sizeof(linebuf)-1] = 0; + ss = strlen(linebuf); + if (copy_to_user(buff, linebuf, ss+1)) + return -EFAULT; + return ss; +} + +static ssize_t kgsl_ib_dump_read( + struct file *file, + char __user *buff, + size_t buff_count, + loff_t *ppos) +{ + int i, count = kgsl_ib_size, remaining, pos = 0, tot = 0, ss; + struct kgsl_device *device = file->private_data; + const int rowc = 32; + unsigned int pt_base, ib_memsize; + uint8_t *base_addr; + char linebuf[80]; + + if (!ppos || !device || !kgsl_ib_base) + return 0; + + kgsl_regread(device, REG_MH_MMU_PT_BASE, &pt_base); + base_addr = kgsl_sharedmem_convertaddr(device, pt_base, kgsl_ib_base, + &ib_memsize); + + if (!base_addr) + return 0; + + pr_info("%s ppos=%ld, buff_count=%d, count=%d\n", __func__, (long)*ppos, + buff_count, count); + ss = snprintf(linebuf, sizeof(linebuf), "IB: base=%08x(%08x" + "), size=%d, memsize=%d\n", kgsl_ib_base, + (uint32_t)base_addr, kgsl_ib_size, ib_memsize); + if (*ppos == 0) { + if (copy_to_user(buff, linebuf, ss+1)) + return -EFAULT; + tot += ss; + buff += ss; + *ppos += ss; + } + pos += ss; + remaining = count; + for (i = 0; i < count; i += rowc) { + int linec = min(remaining, rowc); + + remaining -= rowc; + ss = kgsl_hex_dump("IB: %05x: ", i, base_addr, rowc, linec, + buff); + if (ss < 0) + return ss; + + if (pos >= *ppos) { + if (tot+ss >= buff_count) { + ss = copy_to_user(buff, "", 1); + return tot; + } + tot += ss; + buff += ss; + *ppos += ss; + } + pos += ss; + base_addr += linec; + } + + return tot; +} + +static ssize_t kgsl_ib_dump_write( + struct file *file, + const char __user *buff, + size_t count, + loff_t *ppos) +{ + char local_buff[64]; + + if (count >= sizeof(local_buff)) + return -EFAULT; + + if (copy_from_user(local_buff, buff, count)) + return -EFAULT; + + local_buff[count] = 0; /* end of string */ + sscanf(local_buff, "%x %d", &kgsl_ib_base, &kgsl_ib_size); + + pr_info("%s: base=%08X size=%d\n", __func__, kgsl_ib_base, + kgsl_ib_size); + + return count; +} + +static const struct file_operations kgsl_ib_dump_fops = { + .open = kgsl_dbgfs_open, + .release = kgsl_dbgfs_release, + .read = kgsl_ib_dump_read, + .write = kgsl_ib_dump_write, +}; + +static int kgsl_regread_nolock(struct kgsl_device *device, + unsigned int offsetwords, unsigned int *value) +{ + unsigned int *reg; + + if (offsetwords*sizeof(uint32_t) >= device->regspace.sizebytes) { + KGSL_DRV_ERR(device, "invalid offset %d\n", offsetwords); + return -ERANGE; + } + + reg = (unsigned int *)(device->regspace.mmio_virt_base + + (offsetwords << 2)); + *value = readl(reg); + return 0; +} + +#define KGSL_ISTORE_START 0x5000 +#define KGSL_ISTORE_LENGTH 0x600 +static ssize_t kgsl_istore_read( + struct file *file, + char __user *buff, + size_t buff_count, + loff_t *ppos) +{ + int i, count = KGSL_ISTORE_LENGTH, remaining, pos = 0, tot = 0; + struct kgsl_device *device = file->private_data; + const int rowc = 8; + + if (!ppos || !device) + return 0; + + remaining = count; + for (i = 0; i < count; i += rowc) { + unsigned int vals[rowc]; + int j, ss; + int linec = min(remaining, rowc); + remaining -= rowc; + + if (pos >= *ppos) { + for (j = 0; j < linec; ++j) + kgsl_regread_nolock(device, + KGSL_ISTORE_START+i+j, vals+j); + } else + memset(vals, 0, sizeof(vals)); + + ss = kgsl_hex_dump("IS: %04x: ", i, (uint8_t *)vals, rowc*4, + linec*4, buff); + if (ss < 0) + return ss; + + if (pos >= *ppos) { + if (tot+ss >= buff_count) + return tot; + tot += ss; + buff += ss; + *ppos += ss; + } + pos += ss; + } + + return tot; +} + +static const struct file_operations kgsl_istore_fops = { + .open = kgsl_dbgfs_open, + .release = kgsl_dbgfs_release, + .read = kgsl_istore_read, + .llseek = default_llseek, +}; + +typedef void (*reg_read_init_t)(struct kgsl_device *device); +typedef void (*reg_read_fill_t)(struct kgsl_device *device, int i, + unsigned int *vals, int linec); +static ssize_t kgsl_reg_read(struct kgsl_device *device, int count, + reg_read_init_t reg_read_init, + reg_read_fill_t reg_read_fill, const char *prefix, char __user *buff, + loff_t *ppos) +{ + int i, remaining; + const int rowc = 8; + + if (!ppos || *ppos || !device) + return 0; + + mutex_lock(&device->mutex); + reg_read_init(device); + remaining = count; + for (i = 0; i < count; i += rowc) { + unsigned int vals[rowc]; + int ss; + int linec = min(remaining, rowc); + remaining -= rowc; + + reg_read_fill(device, i, vals, linec); + ss = kgsl_hex_dump(prefix, i, (uint8_t *)vals, rowc*4, linec*4, + buff); + if (ss < 0) { + mutex_unlock(&device->mutex); + return ss; + } + buff += ss; + *ppos += ss; + } + mutex_unlock(&device->mutex); + + return *ppos; +} + + +static void kgsl_sx_reg_read_init(struct kgsl_device *device) +{ + kgsl_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xFF); + kgsl_regwrite(device, REG_RBBM_DEBUG_CNTL, 0); +} + +static void kgsl_sx_reg_read_fill(struct kgsl_device *device, int i, + unsigned int *vals, int linec) +{ + int j; + + for (j = 0; j < linec; ++j) { + kgsl_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1B00 | i); + kgsl_regread(device, REG_RBBM_DEBUG_OUT, vals+j); + } +} + +static ssize_t kgsl_sx_debug_read( + struct file *file, + char __user *buff, + size_t buff_count, + loff_t *ppos) +{ + struct kgsl_device *device = file->private_data; + return kgsl_reg_read(device, 0x1B, kgsl_sx_reg_read_init, + kgsl_sx_reg_read_fill, "SX: %02x: ", buff, ppos); +} + +static const struct file_operations kgsl_sx_debug_fops = { + .open = kgsl_dbgfs_open, + .release = kgsl_dbgfs_release, + .read = kgsl_sx_debug_read, +}; + +static void kgsl_cp_reg_read_init(struct kgsl_device *device) +{ + kgsl_regwrite(device, REG_RBBM_DEBUG_CNTL, 0); +} + +static void kgsl_cp_reg_read_fill(struct kgsl_device *device, int i, + unsigned int *vals, int linec) +{ + int j; + + for (j = 0; j < linec; ++j) { + kgsl_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1628); + kgsl_regread(device, REG_RBBM_DEBUG_OUT, vals+j); + msleep(100); + } +} + +static ssize_t kgsl_cp_debug_read( + struct file *file, + char __user *buff, + size_t buff_count, + loff_t *ppos) +{ + struct kgsl_device *device = file->private_data; + return kgsl_reg_read(device, 20, kgsl_cp_reg_read_init, + kgsl_cp_reg_read_fill, + "CP: %02x: ", buff, ppos); +} + +static const struct file_operations kgsl_cp_debug_fops = { + .open = kgsl_dbgfs_open, + .release = kgsl_dbgfs_release, + .read = kgsl_cp_debug_read, +}; + +static void kgsl_mh_reg_read_init(struct kgsl_device *device) +{ + kgsl_regwrite(device, REG_RBBM_DEBUG_CNTL, 0); +} + +static void kgsl_mh_reg_read_fill(struct kgsl_device *device, int i, + unsigned int *vals, int linec) +{ + int j; + + for (j = 0; j < linec; ++j) { + kgsl_regwrite(device, REG_MH_DEBUG_CTRL, i+j); + kgsl_regread(device, REG_MH_DEBUG_DATA, vals+j); + } +} + +static ssize_t kgsl_mh_debug_read( + struct file *file, + char __user *buff, + size_t buff_count, + loff_t *ppos) +{ + struct kgsl_device *device = file->private_data; + return kgsl_reg_read(device, 0x40, kgsl_mh_reg_read_init, + kgsl_mh_reg_read_fill, + "MH: %02x: ", buff, ppos); +} + +static const struct file_operations kgsl_mh_debug_fops = { + .open = kgsl_dbgfs_open, + .release = kgsl_dbgfs_release, + .read = kgsl_mh_debug_read, +}; + +void adreno_debugfs_init(struct kgsl_device *device) +{ + if (!device->d_debugfs || IS_ERR(device->d_debugfs)) + return; + + debugfs_create_file("ib_dump", 0600, device->d_debugfs, device, + &kgsl_ib_dump_fops); + debugfs_create_file("istore", 0400, device->d_debugfs, device, + &kgsl_istore_fops); + debugfs_create_file("sx_debug", 0400, device->d_debugfs, device, + &kgsl_sx_debug_fops); + debugfs_create_file("cp_debug", 0400, device->d_debugfs, device, + &kgsl_cp_debug_fops); + debugfs_create_file("mh_debug", 0400, device->d_debugfs, device, + &kgsl_mh_debug_fops); + debugfs_create_file("cff_dump", 0644, device->d_debugfs, device, + &kgsl_cff_dump_enable_fops); + + /* Create post mortem control files */ + + pm_d_debugfs = debugfs_create_dir("postmortem", device->d_debugfs); + + if (IS_ERR(pm_d_debugfs)) + return; + + debugfs_create_file("dump", 0600, pm_d_debugfs, device, + &pm_dump_fops); + debugfs_create_file("regs_enabled", 0644, pm_d_debugfs, device, + &pm_regs_enabled_fops); +} diff --git a/drivers/gpu/msm/adreno_debugfs.h b/drivers/gpu/msm/adreno_debugfs.h new file mode 100644 index 0000000000000..680eb849fc4ce --- /dev/null +++ b/drivers/gpu/msm/adreno_debugfs.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2002,2008-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __ADRENO_DEBUGFS_H +#define __ADRENO_DEBUGFS_H + +#ifdef CONFIG_DEBUG_FS + +int adreno_debugfs_init(struct kgsl_device *device); + +extern int kgsl_pm_regs_enabled; + +static inline int kgsl_pmregs_enabled(void) +{ + return kgsl_pm_regs_enabled; +} + +#else +static inline int adreno_debugfs_init(struct kgsl_device *device) +{ + return 0; +} + +static inline int kgsl_pmregs_enabled(void) +{ + /* If debugfs is turned off, then always print registers */ + return 1; +} +#endif + +#endif /* __ADRENO_DEBUGFS_H */ diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c new file mode 100644 index 0000000000000..27a246e3a10a5 --- /dev/null +++ b/drivers/gpu/msm/adreno_drawctxt.c @@ -0,0 +1,1680 @@ +/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include + +#include "kgsl.h" + +#include "adreno.h" +#include "adreno_pm4types.h" +#include "adreno_drawctxt.h" + +/* + * + * Memory Map for Register, Constant & Instruction Shadow, and Command Buffers + * (34.5KB) + * + * +---------------------+------------+-------------+---+---------------------+ + * | ALU Constant Shadow | Reg Shadow | C&V Buffers |Tex| Shader Instr Shadow | + * +---------------------+------------+-------------+---+---------------------+ + * ________________________________/ \____________________ + * / | + * +--------------+-----------+------+-----------+------------------------+ + * | Restore Regs | Save Regs | Quad | Gmem Save | Gmem Restore | unused | + * +--------------+-----------+------+-----------+------------------------+ + * + * 8K - ALU Constant Shadow (8K aligned) + * 4K - H/W Register Shadow (8K aligned) + * 4K - Command and Vertex Buffers + * - Indirect command buffer : Const/Reg restore + * - includes Loop & Bool const shadows + * - Indirect command buffer : Const/Reg save + * - Quad vertices & texture coordinates + * - Indirect command buffer : Gmem save + * - Indirect command buffer : Gmem restore + * - Unused (padding to 8KB boundary) + * <1K - Texture Constant Shadow (768 bytes) (8K aligned) + * 18K - Shader Instruction Shadow + * - 6K vertex (32 byte aligned) + * - 6K pixel (32 byte aligned) + * - 6K shared (32 byte aligned) + * + * Note: Reading constants into a shadow, one at a time using REG_TO_MEM, takes + * 3 DWORDS per DWORD transfered, plus 1 DWORD for the shadow, for a total of + * 16 bytes per constant. If the texture constants were transfered this way, + * the Command & Vertex Buffers section would extend past the 16K boundary. + * By moving the texture constant shadow area to start at 16KB boundary, we + * only require approximately 40 bytes more memory, but are able to use the + * LOAD_CONSTANT_CONTEXT shadowing feature for the textures, speeding up + * context switching. + * + * [Using LOAD_CONSTANT_CONTEXT shadowing feature for the Loop and/or Bool + * constants would require an additional 8KB each, for alignment.] + * + */ + +/* Constants */ + +#define ALU_CONSTANTS 2048 /* DWORDS */ +#define NUM_REGISTERS 1024 /* DWORDS */ +#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES +#define CMD_BUFFER_LEN 9216 /* DWORDS */ +#else +#define CMD_BUFFER_LEN 3072 /* DWORDS */ +#endif +#define TEX_CONSTANTS (32*6) /* DWORDS */ +#define BOOL_CONSTANTS 8 /* DWORDS */ +#define LOOP_CONSTANTS 56 /* DWORDS */ +#define SHADER_INSTRUCT_LOG2 9U /* 2^n == SHADER_INSTRUCTIONS */ + +#if defined(PM4_IM_STORE) +/* 96-bit instructions */ +#define SHADER_INSTRUCT (1<= 0x10000) { + exp += 16; + u >>= 16; + } + if (u >= 0x100) { + exp += 8; + u >>= 8; + } + if (u >= 0x10) { + exp += 4; + u >>= 4; + } + if (u >= 0x4) { + exp += 2; + u >>= 2; + } + if (u >= 0x2) { + exp += 1; + u >>= 1; + } + + /* Calculate fraction */ + if (23 > exp) + frac = (uintval & (~(1 << exp))) << (23 - exp); + + /* Exp is biased by 127 and shifted 23 bits */ + exp = (exp + 127) << 23; + + return exp | frac; +} + +/* context save (gmem -> sys) */ + +/* pre-compiled vertex shader program +* +* attribute vec4 P; +* void main(void) +* { +* gl_Position = P; +* } +*/ +#define GMEM2SYS_VTX_PGM_LEN 0x12 + +static unsigned int gmem2sys_vtx_pgm[GMEM2SYS_VTX_PGM_LEN] = { + 0x00011003, 0x00001000, 0xc2000000, + 0x00001004, 0x00001000, 0xc4000000, + 0x00001005, 0x00002000, 0x00000000, + 0x1cb81000, 0x00398a88, 0x00000003, + 0x140f803e, 0x00000000, 0xe2010100, + 0x14000000, 0x00000000, 0xe2000000 +}; + +/* pre-compiled fragment shader program +* +* precision highp float; +* uniform vec4 clear_color; +* void main(void) +* { +* gl_FragColor = clear_color; +* } +*/ + +#define GMEM2SYS_FRAG_PGM_LEN 0x0c + +static unsigned int gmem2sys_frag_pgm[GMEM2SYS_FRAG_PGM_LEN] = { + 0x00000000, 0x1002c400, 0x10000000, + 0x00001003, 0x00002000, 0x00000000, + 0x140f8000, 0x00000000, 0x22000000, + 0x14000000, 0x00000000, 0xe2000000 +}; + +/* context restore (sys -> gmem) */ +/* pre-compiled vertex shader program +* +* attribute vec4 position; +* attribute vec4 texcoord; +* varying vec4 texcoord0; +* void main() +* { +* gl_Position = position; +* texcoord0 = texcoord; +* } +*/ + +#define SYS2GMEM_VTX_PGM_LEN 0x18 + +static unsigned int sys2gmem_vtx_pgm[SYS2GMEM_VTX_PGM_LEN] = { + 0x00052003, 0x00001000, 0xc2000000, 0x00001005, + 0x00001000, 0xc4000000, 0x00001006, 0x10071000, + 0x20000000, 0x18981000, 0x0039ba88, 0x00000003, + 0x12982000, 0x40257b08, 0x00000002, 0x140f803e, + 0x00000000, 0xe2010100, 0x140f8000, 0x00000000, + 0xe2020200, 0x14000000, 0x00000000, 0xe2000000 +}; + +/* pre-compiled fragment shader program +* +* precision mediump float; +* uniform sampler2D tex0; +* varying vec4 texcoord0; +* void main() +* { +* gl_FragColor = texture2D(tex0, texcoord0.xy); +* } +*/ + +#define SYS2GMEM_FRAG_PGM_LEN 0x0f + +static unsigned int sys2gmem_frag_pgm[SYS2GMEM_FRAG_PGM_LEN] = { + 0x00011002, 0x00001000, 0xc4000000, 0x00001003, + 0x10041000, 0x20000000, 0x10000001, 0x1ffff688, + 0x00000002, 0x140f8000, 0x00000000, 0xe2000000, + 0x14000000, 0x00000000, 0xe2000000 +}; + +/* shader texture constants (sysmem -> gmem) */ +#define SYS2GMEM_TEX_CONST_LEN 6 + +static unsigned int sys2gmem_tex_const[SYS2GMEM_TEX_CONST_LEN] = { + /* Texture, FormatXYZW=Unsigned, ClampXYZ=Wrap/Repeat, + * RFMode=ZeroClamp-1, Dim=1:2d + */ + 0x00000002, /* Pitch = TBD */ + + /* Format=6:8888_WZYX, EndianSwap=0:None, ReqSize=0:256bit, DimHi=0, + * NearestClamp=1:OGL Mode + */ + 0x00000800, /* Address[31:12] = TBD */ + + /* Width, Height, EndianSwap=0:None */ + 0, /* Width & Height = TBD */ + + /* NumFormat=0:RF, DstSelXYZW=XYZW, ExpAdj=0, MagFilt=MinFilt=0:Point, + * Mip=2:BaseMap + */ + 0 << 1 | 1 << 4 | 2 << 7 | 3 << 10 | 2 << 23, + + /* VolMag=VolMin=0:Point, MinMipLvl=0, MaxMipLvl=1, LodBiasH=V=0, + * Dim3d=0 + */ + 0, + + /* BorderColor=0:ABGRBlack, ForceBC=0:diable, TriJuice=0, Aniso=0, + * Dim=1:2d, MipPacking=0 + */ + 1 << 9 /* Mip Address[31:12] = TBD */ +}; + +/* quad for copying GMEM to context shadow */ +#define QUAD_LEN 12 + +static unsigned int gmem_copy_quad[QUAD_LEN] = { + 0x00000000, 0x00000000, 0x3f800000, + 0x00000000, 0x00000000, 0x3f800000, + 0x00000000, 0x00000000, 0x3f800000, + 0x00000000, 0x00000000, 0x3f800000 +}; + +#define TEXCOORD_LEN 8 + +static unsigned int gmem_copy_texcoord[TEXCOORD_LEN] = { + 0x00000000, 0x3f800000, + 0x3f800000, 0x3f800000, + 0x00000000, 0x00000000, + 0x3f800000, 0x00000000 +}; + +#define NUM_COLOR_FORMATS 13 + +static enum SURFACEFORMAT surface_format_table[NUM_COLOR_FORMATS] = { + FMT_4_4_4_4, /* COLORX_4_4_4_4 */ + FMT_1_5_5_5, /* COLORX_1_5_5_5 */ + FMT_5_6_5, /* COLORX_5_6_5 */ + FMT_8, /* COLORX_8 */ + FMT_8_8, /* COLORX_8_8 */ + FMT_8_8_8_8, /* COLORX_8_8_8_8 */ + FMT_8_8_8_8, /* COLORX_S8_8_8_8 */ + FMT_16_FLOAT, /* COLORX_16_FLOAT */ + FMT_16_16_FLOAT, /* COLORX_16_16_FLOAT */ + FMT_16_16_16_16_FLOAT, /* COLORX_16_16_16_16_FLOAT */ + FMT_32_FLOAT, /* COLORX_32_FLOAT */ + FMT_32_32_FLOAT, /* COLORX_32_32_FLOAT */ + FMT_32_32_32_32_FLOAT, /* COLORX_32_32_32_32_FLOAT */ +}; + +static unsigned int format2bytesperpixel[NUM_COLOR_FORMATS] = { + 2, /* COLORX_4_4_4_4 */ + 2, /* COLORX_1_5_5_5 */ + 2, /* COLORX_5_6_5 */ + 1, /* COLORX_8 */ + 2, /* COLORX_8_8 8*/ + 4, /* COLORX_8_8_8_8 */ + 4, /* COLORX_S8_8_8_8 */ + 2, /* COLORX_16_FLOAT */ + 4, /* COLORX_16_16_FLOAT */ + 8, /* COLORX_16_16_16_16_FLOAT */ + 4, /* COLORX_32_FLOAT */ + 8, /* COLORX_32_32_FLOAT */ + 16, /* COLORX_32_32_32_32_FLOAT */ +}; + +/* shader linkage info */ +#define SHADER_CONST_ADDR (11 * 6 + 3) + +/* gmem command buffer length */ +#define PM4_REG(reg) ((0x4 << 16) | (GSL_HAL_SUBBLOCK_OFFSET(reg))) + +/* functions */ +static void config_gmemsize(struct gmem_shadow_t *shadow, int gmem_size) +{ + int w = 64, h = 64; /* 16KB surface, minimum */ + + shadow->format = COLORX_8_8_8_8; + /* convert from bytes to 32-bit words */ + gmem_size = (gmem_size + 3) / 4; + + /* find the right surface size, close to a square. */ + while (w * h < gmem_size) + if (w < h) + w *= 2; + else + h *= 2; + + shadow->width = w; + shadow->pitch = w; + shadow->height = h; + shadow->gmem_pitch = shadow->pitch; + + shadow->size = shadow->pitch * shadow->height * 4; +} + +static unsigned int gpuaddr(unsigned int *cmd, struct kgsl_memdesc *memdesc) +{ + return memdesc->gpuaddr + ((char *)cmd - (char *)memdesc->hostptr); +} + +static void +create_ib1(struct adreno_context *drawctxt, unsigned int *cmd, + unsigned int *start, unsigned int *end) +{ + cmd[0] = PM4_HDR_INDIRECT_BUFFER_PFD; + cmd[1] = gpuaddr(start, &drawctxt->gpustate); + cmd[2] = end - start; +} + +static unsigned int *program_shader(unsigned int *cmds, int vtxfrag, + unsigned int *shader_pgm, int dwords) +{ + /* load the patched vertex shader stream */ + *cmds++ = pm4_type3_packet(PM4_IM_LOAD_IMMEDIATE, 2 + dwords); + /* 0=vertex shader, 1=fragment shader */ + *cmds++ = vtxfrag; + /* instruction start & size (in 32-bit words) */ + *cmds++ = ((0 << 16) | dwords); + + memcpy(cmds, shader_pgm, dwords << 2); + cmds += dwords; + + return cmds; +} + +static unsigned int *reg_to_mem(unsigned int *cmds, uint32_t dst, + uint32_t src, int dwords) +{ + while (dwords-- > 0) { + *cmds++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmds++ = src++; + *cmds++ = dst; + dst += 4; + } + + return cmds; +} + +#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES + +static void build_reg_to_mem_range(unsigned int start, unsigned int end, + unsigned int **cmd, + struct adreno_context *drawctxt) +{ + unsigned int i = start; + + for (i = start; i <= end; i++) { + *(*cmd)++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *(*cmd)++ = i; + *(*cmd)++ = + ((drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000) + + (i - 0x2000) * 4; + } +} + +#endif + +/* chicken restore */ +static unsigned int *build_chicken_restore_cmds( + struct adreno_context *drawctxt, + struct tmp_ctx *ctx) +{ + unsigned int *start = ctx->cmd; + unsigned int *cmds = start; + + *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmds++ = 0; + + *cmds++ = pm4_type0_packet(REG_TP0_CHICKEN, 1); + ctx->chicken_restore = gpuaddr(cmds, &drawctxt->gpustate); + *cmds++ = 0x00000000; + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->chicken_restore, start, cmds); + + return cmds; +} + +/* save h/w regs, alu constants, texture contants, etc. ... +* requires: bool_shadow_gpuaddr, loop_shadow_gpuaddr +*/ +static void build_regsave_cmds(struct kgsl_device *device, + struct adreno_context *drawctxt, + struct tmp_ctx *ctx) +{ + unsigned int *start = ctx->cmd; + unsigned int *cmd = start; + + *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + +#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES + /* Make sure the HW context has the correct register values + * before reading them. */ + *cmd++ = pm4_type3_packet(PM4_CONTEXT_UPDATE, 1); + *cmd++ = 0; +#endif + +#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES + /* Write HW registers into shadow */ + build_reg_to_mem_range(REG_RB_SURFACE_INFO, REG_RB_DEPTH_INFO, + &cmd, drawctxt); + build_reg_to_mem_range(REG_COHER_DEST_BASE_0, + REG_PA_SC_SCREEN_SCISSOR_BR, + &cmd, drawctxt); + build_reg_to_mem_range(REG_PA_SC_WINDOW_OFFSET, + REG_PA_SC_WINDOW_SCISSOR_BR, + &cmd, drawctxt); + if (device->chip_id != KGSL_CHIPID_LEIA_REV470) { + build_reg_to_mem_range(REG_VGT_MAX_VTX_INDX, REG_RB_FOG_COLOR, + &cmd, drawctxt); + } else { + build_reg_to_mem_range(REG_LEIA_PC_MAX_VTX_INDX, + REG_LEIA_PC_INDX_OFFSET, + &cmd, drawctxt); + build_reg_to_mem_range(REG_RB_COLOR_MASK, + REG_RB_FOG_COLOR, + &cmd, drawctxt); + } + build_reg_to_mem_range(REG_RB_STENCILREFMASK_BF, + REG_PA_CL_VPORT_ZOFFSET, + &cmd, drawctxt); + build_reg_to_mem_range(REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1, + &cmd, drawctxt); + if (device->chip_id != KGSL_CHIPID_LEIA_REV470) { + build_reg_to_mem_range(REG_RB_DEPTHCONTROL, REG_RB_MODECONTROL, + &cmd, drawctxt); + build_reg_to_mem_range(REG_PA_SU_POINT_SIZE, + REG_PA_SC_LINE_STIPPLE, + &cmd, drawctxt); + build_reg_to_mem_range(REG_PA_SC_VIZ_QUERY, REG_PA_SC_VIZ_QUERY, + &cmd, drawctxt); + } else { + build_reg_to_mem_range(REG_RB_DEPTHCONTROL, + REG_RB_COLORCONTROL, + &cmd, drawctxt); + build_reg_to_mem_range(REG_PA_CL_CLIP_CNTL, + REG_PA_CL_VTE_CNTL, + &cmd, drawctxt); + build_reg_to_mem_range(REG_RB_MODECONTROL, + REG_LEIA_GRAS_CONTROL, + &cmd, drawctxt); + build_reg_to_mem_range(REG_PA_SU_POINT_SIZE, + REG_PA_SU_LINE_CNTL, + &cmd, drawctxt); + } + build_reg_to_mem_range(REG_PA_SC_LINE_CNTL, REG_SQ_PS_CONST, + &cmd, drawctxt); + build_reg_to_mem_range(REG_PA_SC_AA_MASK, REG_PA_SC_AA_MASK, + &cmd, drawctxt); + if (device->chip_id != KGSL_CHIPID_LEIA_REV470) { + build_reg_to_mem_range(REG_VGT_VERTEX_REUSE_BLOCK_CNTL, + REG_RB_DEPTH_CLEAR, + &cmd, drawctxt); + } else { + build_reg_to_mem_range(REG_LEIA_PC_VERTEX_REUSE_BLOCK_CNTL, + REG_LEIA_PC_VERTEX_REUSE_BLOCK_CNTL, + &cmd, drawctxt); + build_reg_to_mem_range(REG_RB_COPY_CONTROL, + REG_RB_DEPTH_CLEAR, + &cmd, drawctxt); + } + build_reg_to_mem_range(REG_RB_SAMPLE_COUNT_CTL, + REG_RB_COLOR_DEST_MASK, + &cmd, drawctxt); + build_reg_to_mem_range(REG_PA_SU_POLY_OFFSET_FRONT_SCALE, + REG_PA_SU_POLY_OFFSET_BACK_OFFSET, + &cmd, drawctxt); + + /* Copy ALU constants */ + cmd = + reg_to_mem(cmd, (drawctxt->gpustate.gpuaddr) & 0xFFFFE000, + REG_SQ_CONSTANT_0, ALU_CONSTANTS); + + /* Copy Tex constants */ + cmd = + reg_to_mem(cmd, + (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000, + REG_SQ_FETCH_0, TEX_CONSTANTS); +#else + + /* Insert a wait for idle packet before reading the registers. + * This is to fix a hang/reset seen during stress testing. In this + * hang, CP encountered a timeout reading SQ's boolean constant + * register. There is logic in the HW that blocks reading of this + * register when the SQ block is not idle, which we believe is + * contributing to the hang.*/ + *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + + /* H/w registers are already shadowed; just need to disable shadowing + * to prevent corruption. + */ + *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); + *cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000; + *cmd++ = 4 << 16; /* regs, start=0 */ + *cmd++ = 0x0; /* count = 0 */ + + /* ALU constants are already shadowed; just need to disable shadowing + * to prevent corruption. + */ + *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); + *cmd++ = drawctxt->gpustate.gpuaddr & 0xFFFFE000; + *cmd++ = 0 << 16; /* ALU, start=0 */ + *cmd++ = 0x0; /* count = 0 */ + + /* Tex constants are already shadowed; just need to disable shadowing + * to prevent corruption. + */ + *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); + *cmd++ = (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000; + *cmd++ = 1 << 16; /* Tex, start=0 */ + *cmd++ = 0x0; /* count = 0 */ +#endif + + /* Need to handle some of the registers separately */ + *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmd++ = REG_SQ_GPR_MANAGEMENT; + *cmd++ = ctx->reg_values[0]; + + *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmd++ = REG_TP0_CHICKEN; + *cmd++ = ctx->reg_values[1]; + + *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmd++ = REG_RBBM_PM_OVERRIDE2; + *cmd++ = ctx->reg_values[2]; + + if (device->chip_id == KGSL_CHIPID_LEIA_REV470) { + unsigned int i; + unsigned int j = 3; + for (i = REG_LEIA_VSC_BIN_SIZE; i <= + REG_LEIA_VSC_PIPE_DATA_LENGTH_7; i++) { + *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmd++ = i; + *cmd++ = ctx->reg_values[j]; + j++; + } + } + + /* Copy Boolean constants */ + cmd = reg_to_mem(cmd, ctx->bool_shadow, REG_SQ_CF_BOOLEANS, + BOOL_CONSTANTS); + + /* Copy Loop constants */ + cmd = reg_to_mem(cmd, ctx->loop_shadow, REG_SQ_CF_LOOP, LOOP_CONSTANTS); + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->reg_save, start, cmd); + + ctx->cmd = cmd; +} + +/*copy colour, depth, & stencil buffers from graphics memory to system memory*/ +static unsigned int *build_gmem2sys_cmds(struct kgsl_device *device, + struct adreno_context *drawctxt, + struct tmp_ctx *ctx, + struct gmem_shadow_t *shadow) +{ + unsigned int *cmds = shadow->gmem_save_commands; + unsigned int *start = cmds; + /* Calculate the new offset based on the adjusted base */ + unsigned int bytesperpixel = format2bytesperpixel[shadow->format]; + unsigned int addr = shadow->gmemshadow.gpuaddr; + unsigned int offset = (addr - (addr & 0xfffff000)) / bytesperpixel; + + /* Store TP0_CHICKEN register */ + *cmds++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmds++ = REG_TP0_CHICKEN; + if (ctx) + *cmds++ = ctx->chicken_restore; + else + cmds++; + + *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmds++ = 0; + + /* Set TP0_CHICKEN to zero */ + *cmds++ = pm4_type0_packet(REG_TP0_CHICKEN, 1); + *cmds++ = 0x00000000; + + /* Set PA_SC_AA_CONFIG to 0 */ + *cmds++ = pm4_type0_packet(REG_PA_SC_AA_CONFIG, 1); + *cmds++ = 0x00000000; + + /* program shader */ + + /* load shader vtx constants ... 5 dwords */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 4); + *cmds++ = (0x1 << 16) | SHADER_CONST_ADDR; + *cmds++ = 0; + /* valid(?) vtx constant flag & addr */ + *cmds++ = shadow->quad_vertices.gpuaddr | 0x3; + /* limit = 12 dwords */ + *cmds++ = 0x00000030; + + /* Invalidate L2 cache to make sure vertices are updated */ + *cmds++ = pm4_type0_packet(REG_TC_CNTL_STATUS, 1); + *cmds++ = 0x1; + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 4); + *cmds++ = PM4_REG(REG_VGT_MAX_VTX_INDX); + *cmds++ = 0x00ffffff; /* REG_VGT_MAX_VTX_INDX */ + *cmds++ = 0x0; /* REG_VGT_MIN_VTX_INDX */ + *cmds++ = 0x00000000; /* REG_VGT_INDX_OFFSET */ + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_PA_SC_AA_MASK); + *cmds++ = 0x0000ffff; /* REG_PA_SC_AA_MASK */ + + /* load the patched vertex shader stream */ + cmds = program_shader(cmds, 0, gmem2sys_vtx_pgm, GMEM2SYS_VTX_PGM_LEN); + + /* Load the patched fragment shader stream */ + cmds = + program_shader(cmds, 1, gmem2sys_frag_pgm, GMEM2SYS_FRAG_PGM_LEN); + + /* SQ_PROGRAM_CNTL / SQ_CONTEXT_MISC */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_SQ_PROGRAM_CNTL); + if (device->chip_id == KGSL_CHIPID_LEIA_REV470) + *cmds++ = 0x10018001; + else + *cmds++ = 0x10010001; + *cmds++ = 0x00000008; + + /* resolve */ + + /* PA_CL_VTE_CNTL */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_PA_CL_VTE_CNTL); + /* disable X/Y/Z transforms, X/Y/Z are premultiplied by W */ + *cmds++ = 0x00000b00; + + /* program surface info */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_RB_SURFACE_INFO); + *cmds++ = shadow->gmem_pitch; /* pitch, MSAA = 1 */ + + /* RB_COLOR_INFO Endian=none, Linear, Format=RGBA8888, Swap=0, + * Base=gmem_base + */ + /* gmem base assumed 4K aligned. */ + if (ctx) { + BUG_ON(ctx->gmem_base & 0xFFF); + *cmds++ = + (shadow-> + format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT) | ctx-> + gmem_base; + } else { + unsigned int temp = *cmds; + *cmds++ = (temp & ~RB_COLOR_INFO__COLOR_FORMAT_MASK) | + (shadow->format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT); + } + + /* disable Z */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_RB_DEPTHCONTROL); + if (device->chip_id == KGSL_CHIPID_LEIA_REV470) + *cmds++ = 0x08; + else + *cmds++ = 0; + + /* set REG_PA_SU_SC_MODE_CNTL + * Front_ptype = draw triangles + * Back_ptype = draw triangles + * Provoking vertex = last + */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_PA_SU_SC_MODE_CNTL); + *cmds++ = 0x00080240; + + /* Use maximum scissor values -- quad vertices already have the + * correct bounds */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_PA_SC_SCREEN_SCISSOR_TL); + *cmds++ = (0 << 16) | 0; + *cmds++ = (0x1fff << 16) | (0x1fff); + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_PA_SC_WINDOW_SCISSOR_TL); + *cmds++ = (unsigned int)((1U << 31) | (0 << 16) | 0); + *cmds++ = (0x1fff << 16) | (0x1fff); + + /* load the viewport so that z scale = clear depth and + * z offset = 0.0f + */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_PA_CL_VPORT_ZSCALE); + *cmds++ = 0xbf800000; /* -1.0f */ + *cmds++ = 0x0; + + /* load the stencil ref value + * $AAM - do this later + */ + + /* load the COPY state */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 6); + *cmds++ = PM4_REG(REG_RB_COPY_CONTROL); + *cmds++ = 0; /* RB_COPY_CONTROL */ + *cmds++ = addr & 0xfffff000; /* RB_COPY_DEST_BASE */ + *cmds++ = shadow->pitch >> 5; /* RB_COPY_DEST_PITCH */ + + /* Endian=none, Linear, Format=RGBA8888,Swap=0,!Dither, + * MaskWrite:R=G=B=A=1 + */ + *cmds++ = 0x0003c008 | + (shadow->format << RB_COPY_DEST_INFO__COPY_DEST_FORMAT__SHIFT); + /* Make sure we stay in offsetx field. */ + BUG_ON(offset & 0xfffff000); + *cmds++ = offset; + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_RB_MODECONTROL); + *cmds++ = 0x6; /* EDRAM copy */ + + if (device->chip_id == KGSL_CHIPID_LEIA_REV470) { + *cmds++ = 0xc0043600; /* packet 3 3D_DRAW_INDX_2 */ + *cmds++ = 0x0; + *cmds++ = 0x00004046; /* tristrip */ + *cmds++ = 0x00000004; /* NUM_INDICES */ + *cmds++ = 0x00010000; /* index: 0x00, 0x01 */ + *cmds++ = 0x00030002; /* index: 0x02, 0x03 */ + } else { + /* queue the draw packet */ + *cmds++ = pm4_type3_packet(PM4_DRAW_INDX, 2); + *cmds++ = 0; /* viz query info. */ + /* PrimType=RectList, NumIndices=3, SrcSel=AutoIndex */ + *cmds++ = 0x00030088; + } + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, shadow->gmem_save, start, cmds); + + return cmds; +} + +/* context restore */ + +/*copy colour, depth, & stencil buffers from system memory to graphics memory*/ +static unsigned int *build_sys2gmem_cmds(struct kgsl_device *device, + struct adreno_context *drawctxt, + struct tmp_ctx *ctx, + struct gmem_shadow_t *shadow) +{ + unsigned int *cmds = shadow->gmem_restore_commands; + unsigned int *start = cmds; + + /* Store TP0_CHICKEN register */ + *cmds++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmds++ = REG_TP0_CHICKEN; + if (ctx) + *cmds++ = ctx->chicken_restore; + else + cmds++; + + *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmds++ = 0; + + /* Set TP0_CHICKEN to zero */ + *cmds++ = pm4_type0_packet(REG_TP0_CHICKEN, 1); + *cmds++ = 0x00000000; + + /* Set PA_SC_AA_CONFIG to 0 */ + *cmds++ = pm4_type0_packet(REG_PA_SC_AA_CONFIG, 1); + *cmds++ = 0x00000000; + /* shader constants */ + + /* vertex buffer constants */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 7); + + *cmds++ = (0x1 << 16) | (9 * 6); + /* valid(?) vtx constant flag & addr */ + *cmds++ = shadow->quad_vertices.gpuaddr | 0x3; + /* limit = 12 dwords */ + *cmds++ = 0x00000030; + /* valid(?) vtx constant flag & addr */ + *cmds++ = shadow->quad_texcoords.gpuaddr | 0x3; + /* limit = 8 dwords */ + *cmds++ = 0x00000020; + *cmds++ = 0; + *cmds++ = 0; + + /* Invalidate L2 cache to make sure vertices are updated */ + *cmds++ = pm4_type0_packet(REG_TC_CNTL_STATUS, 1); + *cmds++ = 0x1; + + cmds = program_shader(cmds, 0, sys2gmem_vtx_pgm, SYS2GMEM_VTX_PGM_LEN); + + /* Load the patched fragment shader stream */ + cmds = + program_shader(cmds, 1, sys2gmem_frag_pgm, SYS2GMEM_FRAG_PGM_LEN); + + /* SQ_PROGRAM_CNTL / SQ_CONTEXT_MISC */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_SQ_PROGRAM_CNTL); + *cmds++ = 0x10030002; + *cmds++ = 0x00000008; + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_PA_SC_AA_MASK); + *cmds++ = 0x0000ffff; /* REG_PA_SC_AA_MASK */ + + if (device->chip_id != KGSL_CHIPID_LEIA_REV470) { + /* PA_SC_VIZ_QUERY */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_PA_SC_VIZ_QUERY); + *cmds++ = 0x0; /*REG_PA_SC_VIZ_QUERY */ + } + + /* RB_COLORCONTROL */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_RB_COLORCONTROL); + *cmds++ = 0x00000c20; + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 4); + *cmds++ = PM4_REG(REG_VGT_MAX_VTX_INDX); + *cmds++ = 0x00ffffff; /* mmVGT_MAX_VTX_INDX */ + *cmds++ = 0x0; /* mmVGT_MIN_VTX_INDX */ + *cmds++ = 0x00000000; /* mmVGT_INDX_OFFSET */ + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_VGT_VERTEX_REUSE_BLOCK_CNTL); + *cmds++ = 0x00000002; /* mmVGT_VERTEX_REUSE_BLOCK_CNTL */ + *cmds++ = 0x00000002; /* mmVGT_OUT_DEALLOC_CNTL */ + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_SQ_INTERPOLATOR_CNTL); + *cmds++ = 0xffffffff; /* mmSQ_INTERPOLATOR_CNTL */ + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_PA_SC_AA_CONFIG); + *cmds++ = 0x00000000; /* REG_PA_SC_AA_CONFIG */ + + /* set REG_PA_SU_SC_MODE_CNTL + * Front_ptype = draw triangles + * Back_ptype = draw triangles + * Provoking vertex = last + */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_PA_SU_SC_MODE_CNTL); + *cmds++ = 0x00080240; + + /* texture constants */ + *cmds++ = + pm4_type3_packet(PM4_SET_CONSTANT, (SYS2GMEM_TEX_CONST_LEN + 1)); + *cmds++ = (0x1 << 16) | (0 * 6); + memcpy(cmds, sys2gmem_tex_const, SYS2GMEM_TEX_CONST_LEN << 2); + cmds[0] |= (shadow->pitch >> 5) << 22; + cmds[1] |= + shadow->gmemshadow.gpuaddr | surface_format_table[shadow->format]; + cmds[2] |= (shadow->width - 1) | (shadow->height - 1) << 13; + cmds += SYS2GMEM_TEX_CONST_LEN; + + /* program surface info */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_RB_SURFACE_INFO); + *cmds++ = shadow->gmem_pitch; /* pitch, MSAA = 1 */ + + /* RB_COLOR_INFO Endian=none, Linear, Format=RGBA8888, Swap=0, + * Base=gmem_base + */ + if (ctx) + *cmds++ = + (shadow-> + format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT) | ctx-> + gmem_base; + else { + unsigned int temp = *cmds; + *cmds++ = (temp & ~RB_COLOR_INFO__COLOR_FORMAT_MASK) | + (shadow->format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT); + } + + /* RB_DEPTHCONTROL */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_RB_DEPTHCONTROL); + + if (device->chip_id == KGSL_CHIPID_LEIA_REV470) + *cmds++ = 8; /* disable Z */ + else + *cmds++ = 0; /* disable Z */ + + /* Use maximum scissor values -- quad vertices already + * have the correct bounds */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_PA_SC_SCREEN_SCISSOR_TL); + *cmds++ = (0 << 16) | 0; + *cmds++ = ((0x1fff) << 16) | 0x1fff; + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_PA_SC_WINDOW_SCISSOR_TL); + *cmds++ = (unsigned int)((1U << 31) | (0 << 16) | 0); + *cmds++ = ((0x1fff) << 16) | 0x1fff; + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_PA_CL_VTE_CNTL); + /* disable X/Y/Z transforms, X/Y/Z are premultiplied by W */ + *cmds++ = 0x00000b00; + + /*load the viewport so that z scale = clear depth and z offset = 0.0f */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_PA_CL_VPORT_ZSCALE); + *cmds++ = 0xbf800000; + *cmds++ = 0x0; + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_RB_COLOR_MASK); + *cmds++ = 0x0000000f; /* R = G = B = 1:enabled */ + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_RB_COLOR_DEST_MASK); + *cmds++ = 0xffffffff; + + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); + *cmds++ = PM4_REG(REG_SQ_WRAPPING_0); + *cmds++ = 0x00000000; + *cmds++ = 0x00000000; + + /* load the stencil ref value + * $AAM - do this later + */ + *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = PM4_REG(REG_RB_MODECONTROL); + /* draw pixels with color and depth/stencil component */ + *cmds++ = 0x4; + + if (device->chip_id == KGSL_CHIPID_LEIA_REV470) { + *cmds++ = 0xc0043600; /* packet 3 3D_DRAW_INDX_2 */ + *cmds++ = 0x0; + *cmds++ = 0x00004046; /* tristrip */ + *cmds++ = 0x00000004; /* NUM_INDICES */ + *cmds++ = 0x00010000; /* index: 0x00, 0x01 */ + *cmds++ = 0x00030002; /* index: 0x02, 0x03 */ + } else { + /* queue the draw packet */ + *cmds++ = pm4_type3_packet(PM4_DRAW_INDX, 2); + *cmds++ = 0; /* viz query info. */ + /* PrimType=RectList, NumIndices=3, SrcSel=AutoIndex */ + *cmds++ = 0x00030088; + } + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, shadow->gmem_restore, start, cmds); + + return cmds; +} + +/* restore h/w regs, alu constants, texture constants, etc. ... */ +static unsigned *reg_range(unsigned int *cmd, unsigned int start, + unsigned int end) +{ + *cmd++ = PM4_REG(start); /* h/w regs, start addr */ + *cmd++ = end - start + 1; /* count */ + return cmd; +} + +static void build_regrestore_cmds(struct kgsl_device *device, + struct adreno_context *drawctxt, + struct tmp_ctx *ctx) +{ + unsigned int *start = ctx->cmd; + unsigned int *cmd = start; + + *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + + /* H/W Registers */ + /* deferred pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, ???); */ + cmd++; +#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES + /* Force mismatch */ + *cmd++ = ((drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000) | 1; +#else + *cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000; +#endif + + if (device->chip_id != KGSL_CHIPID_LEIA_REV470) { + cmd = reg_range(cmd, REG_RB_SURFACE_INFO, + REG_PA_SC_SCREEN_SCISSOR_BR); + } else { + cmd = reg_range(cmd, REG_RB_SURFACE_INFO, REG_RB_DEPTH_INFO); + cmd = reg_range(cmd, REG_COHER_DEST_BASE_0, + REG_PA_SC_SCREEN_SCISSOR_BR); + } + cmd = reg_range(cmd, REG_PA_SC_WINDOW_OFFSET, + REG_PA_SC_WINDOW_SCISSOR_BR); + if (device->chip_id != KGSL_CHIPID_LEIA_REV470) { + cmd = reg_range(cmd, REG_VGT_MAX_VTX_INDX, + REG_PA_CL_VPORT_ZOFFSET); + } else { + cmd = reg_range(cmd, REG_LEIA_PC_MAX_VTX_INDX, + REG_LEIA_PC_INDX_OFFSET); + cmd = reg_range(cmd, REG_RB_COLOR_MASK, REG_RB_FOG_COLOR); + cmd = reg_range(cmd, REG_RB_STENCILREFMASK_BF, + REG_PA_CL_VPORT_ZOFFSET); + } + cmd = reg_range(cmd, REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1); + if (device->chip_id != KGSL_CHIPID_LEIA_REV470) { + cmd = reg_range(cmd, REG_RB_DEPTHCONTROL, REG_RB_MODECONTROL); + cmd = reg_range(cmd, REG_PA_SU_POINT_SIZE, + REG_PA_SC_VIZ_QUERY); /*REG_VGT_ENHANCE */ + cmd = reg_range(cmd, REG_PA_SC_LINE_CNTL, + REG_RB_COLOR_DEST_MASK); + } else { + cmd = reg_range(cmd, REG_RB_DEPTHCONTROL, REG_RB_COLORCONTROL); + cmd = reg_range(cmd, REG_PA_CL_CLIP_CNTL, REG_PA_CL_VTE_CNTL); + cmd = reg_range(cmd, REG_RB_MODECONTROL, REG_LEIA_GRAS_CONTROL); + cmd = reg_range(cmd, REG_PA_SU_POINT_SIZE, REG_PA_SU_LINE_CNTL); + cmd = reg_range(cmd, REG_PA_SC_LINE_CNTL, REG_SQ_PS_CONST); + cmd = reg_range(cmd, REG_PA_SC_AA_MASK, REG_PA_SC_AA_MASK); + cmd = reg_range(cmd, REG_LEIA_PC_VERTEX_REUSE_BLOCK_CNTL, + REG_LEIA_PC_VERTEX_REUSE_BLOCK_CNTL); + cmd = reg_range(cmd, REG_RB_COPY_CONTROL, REG_RB_DEPTH_CLEAR); + cmd = reg_range(cmd, REG_RB_SAMPLE_COUNT_CTL, + REG_RB_COLOR_DEST_MASK); + } + cmd = reg_range(cmd, REG_PA_SU_POLY_OFFSET_FRONT_SCALE, + REG_PA_SU_POLY_OFFSET_BACK_OFFSET); + + /* Now we know how many register blocks we have, we can compute command + * length + */ + start[2] = + pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, (cmd - start) - 3); + /* Enable shadowing for the entire register block. */ +#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES + start[4] |= (0 << 24) | (4 << 16); /* Disable shadowing. */ +#else + start[4] |= (1 << 24) | (4 << 16); +#endif + + /* Need to handle some of the registers separately */ + *cmd++ = pm4_type0_packet(REG_SQ_GPR_MANAGEMENT, 1); + ctx->reg_values[0] = gpuaddr(cmd, &drawctxt->gpustate); + *cmd++ = 0x00040400; + + *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + *cmd++ = pm4_type0_packet(REG_TP0_CHICKEN, 1); + ctx->reg_values[1] = gpuaddr(cmd, &drawctxt->gpustate); + *cmd++ = 0x00000000; + + *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE2, 1); + ctx->reg_values[2] = gpuaddr(cmd, &drawctxt->gpustate); + if (device->chip_id != KGSL_CHIPID_LEIA_REV470) + *cmd++ = 0x00000000; + else + *cmd++ = 0x80; + + if (device->chip_id == KGSL_CHIPID_LEIA_REV470) { + unsigned int i; + unsigned int j = 3; + for (i = REG_LEIA_VSC_BIN_SIZE; i <= + REG_LEIA_VSC_PIPE_DATA_LENGTH_7; i++) { + *cmd++ = pm4_type0_packet(i, 1); + ctx->reg_values[j] = gpuaddr(cmd, &drawctxt->gpustate); + *cmd++ = 0x00000000; + j++; + } + } + + /* ALU Constants */ + *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); + *cmd++ = drawctxt->gpustate.gpuaddr & 0xFFFFE000; +#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES + *cmd++ = (0 << 24) | (0 << 16) | 0; /* Disable shadowing */ +#else + *cmd++ = (1 << 24) | (0 << 16) | 0; +#endif + *cmd++ = ALU_CONSTANTS; + + /* Texture Constants */ + *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); + *cmd++ = (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000; +#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES + /* Disable shadowing */ + *cmd++ = (0 << 24) | (1 << 16) | 0; +#else + *cmd++ = (1 << 24) | (1 << 16) | 0; +#endif + *cmd++ = TEX_CONSTANTS; + + /* Boolean Constants */ + *cmd++ = pm4_type3_packet(PM4_SET_CONSTANT, 1 + BOOL_CONSTANTS); + *cmd++ = (2 << 16) | 0; + + /* the next BOOL_CONSTANT dwords is the shadow area for + * boolean constants. + */ + ctx->bool_shadow = gpuaddr(cmd, &drawctxt->gpustate); + cmd += BOOL_CONSTANTS; + + /* Loop Constants */ + *cmd++ = pm4_type3_packet(PM4_SET_CONSTANT, 1 + LOOP_CONSTANTS); + *cmd++ = (3 << 16) | 0; + + /* the next LOOP_CONSTANTS dwords is the shadow area for + * loop constants. + */ + ctx->loop_shadow = gpuaddr(cmd, &drawctxt->gpustate); + cmd += LOOP_CONSTANTS; + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->reg_restore, start, cmd); + + ctx->cmd = cmd; +} + +/* quad for saving/restoring gmem */ +static void set_gmem_copy_quad(struct gmem_shadow_t *shadow) +{ + /* set vertex buffer values */ + gmem_copy_quad[1] = uint2float(shadow->height); + gmem_copy_quad[3] = uint2float(shadow->width); + gmem_copy_quad[4] = uint2float(shadow->height); + gmem_copy_quad[9] = uint2float(shadow->width); + + gmem_copy_quad[0] = uint2float(0); + gmem_copy_quad[6] = uint2float(0); + gmem_copy_quad[7] = uint2float(0); + gmem_copy_quad[10] = uint2float(0); + + memcpy(shadow->quad_vertices.hostptr, gmem_copy_quad, QUAD_LEN << 2); + + memcpy(shadow->quad_texcoords.hostptr, gmem_copy_texcoord, + TEXCOORD_LEN << 2); +} + +/* quad for saving/restoring gmem */ +static void build_quad_vtxbuff(struct adreno_context *drawctxt, + struct tmp_ctx *ctx, struct gmem_shadow_t *shadow) +{ + unsigned int *cmd = ctx->cmd; + + /* quad vertex buffer location (in GPU space) */ + shadow->quad_vertices.hostptr = cmd; + shadow->quad_vertices.gpuaddr = gpuaddr(cmd, &drawctxt->gpustate); + + cmd += QUAD_LEN; + + /* tex coord buffer location (in GPU space) */ + shadow->quad_texcoords.hostptr = cmd; + shadow->quad_texcoords.gpuaddr = gpuaddr(cmd, &drawctxt->gpustate); + + cmd += TEXCOORD_LEN; + + set_gmem_copy_quad(shadow); + + ctx->cmd = cmd; +} + +static void +build_shader_save_restore_cmds(struct adreno_context *drawctxt, + struct tmp_ctx *ctx) +{ + unsigned int *cmd = ctx->cmd; + unsigned int *save, *restore, *fixup; +#if defined(PM4_IM_STORE) + unsigned int *startSizeVtx, *startSizePix, *startSizeShared; +#endif + unsigned int *partition1; + unsigned int *shaderBases, *partition2; + +#if defined(PM4_IM_STORE) + /* compute vertex, pixel and shared instruction shadow GPU addresses */ + ctx->shader_vertex = drawctxt->gpustate.gpuaddr + SHADER_OFFSET; + ctx->shader_pixel = ctx->shader_vertex + SHADER_SHADOW_SIZE; + ctx->shader_shared = ctx->shader_pixel + SHADER_SHADOW_SIZE; +#endif + + /* restore shader partitioning and instructions */ + + restore = cmd; /* start address */ + + /* Invalidate Vertex & Pixel instruction code address and sizes */ + *cmd++ = pm4_type3_packet(PM4_INVALIDATE_STATE, 1); + *cmd++ = 0x00000300; /* 0x100 = Vertex, 0x200 = Pixel */ + + /* Restore previous shader vertex & pixel instruction bases. */ + *cmd++ = pm4_type3_packet(PM4_SET_SHADER_BASES, 1); + shaderBases = cmd++; /* TBD #5: shader bases (from fixup) */ + + /* write the shader partition information to a scratch register */ + *cmd++ = pm4_type0_packet(REG_SQ_INST_STORE_MANAGMENT, 1); + partition1 = cmd++; /* TBD #4a: partition info (from save) */ + +#if defined(PM4_IM_STORE) + /* load vertex shader instructions from the shadow. */ + *cmd++ = pm4_type3_packet(PM4_IM_LOAD, 2); + *cmd++ = ctx->shader_vertex + 0x0; /* 0x0 = Vertex */ + startSizeVtx = cmd++; /* TBD #1: start/size (from save) */ + + /* load pixel shader instructions from the shadow. */ + *cmd++ = pm4_type3_packet(PM4_IM_LOAD, 2); + *cmd++ = ctx->shader_pixel + 0x1; /* 0x1 = Pixel */ + startSizePix = cmd++; /* TBD #2: start/size (from save) */ + + /* load shared shader instructions from the shadow. */ + *cmd++ = pm4_type3_packet(PM4_IM_LOAD, 2); + *cmd++ = ctx->shader_shared + 0x2; /* 0x2 = Shared */ + startSizeShared = cmd++; /* TBD #3: start/size (from save) */ +#endif + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->shader_restore, restore, cmd); + + /* + * fixup SET_SHADER_BASES data + * + * since self-modifying PM4 code is being used here, a seperate + * command buffer is used for this fixup operation, to ensure the + * commands are not read by the PM4 engine before the data fields + * have been written. + */ + + fixup = cmd; /* start address */ + + /* write the shader partition information to a scratch register */ + *cmd++ = pm4_type0_packet(REG_SCRATCH_REG2, 1); + partition2 = cmd++; /* TBD #4b: partition info (from save) */ + + /* mask off unused bits, then OR with shader instruction memory size */ + *cmd++ = pm4_type3_packet(PM4_REG_RMW, 3); + *cmd++ = REG_SCRATCH_REG2; + /* AND off invalid bits. */ + *cmd++ = 0x0FFF0FFF; + /* OR in instruction memory size */ + *cmd++ = (unsigned int)((SHADER_INSTRUCT_LOG2 - 5U) << 29); + + /* write the computed value to the SET_SHADER_BASES data field */ + *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmd++ = REG_SCRATCH_REG2; + /* TBD #5: shader bases (to restore) */ + *cmd++ = gpuaddr(shaderBases, &drawctxt->gpustate); + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->shader_fixup, fixup, cmd); + + /* save shader partitioning and instructions */ + + save = cmd; /* start address */ + + *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + + /* fetch the SQ_INST_STORE_MANAGMENT register value, + * store the value in the data fields of the SET_CONSTANT commands + * above. + */ + *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmd++ = REG_SQ_INST_STORE_MANAGMENT; + /* TBD #4a: partition info (to restore) */ + *cmd++ = gpuaddr(partition1, &drawctxt->gpustate); + *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); + *cmd++ = REG_SQ_INST_STORE_MANAGMENT; + /* TBD #4b: partition info (to fixup) */ + *cmd++ = gpuaddr(partition2, &drawctxt->gpustate); + +#if defined(PM4_IM_STORE) + + /* store the vertex shader instructions */ + *cmd++ = pm4_type3_packet(PM4_IM_STORE, 2); + *cmd++ = ctx->shader_vertex + 0x0; /* 0x0 = Vertex */ + /* TBD #1: start/size (to restore) */ + *cmd++ = gpuaddr(startSizeVtx, &drawctxt->gpustate); + + /* store the pixel shader instructions */ + *cmd++ = pm4_type3_packet(PM4_IM_STORE, 2); + *cmd++ = ctx->shader_pixel + 0x1; /* 0x1 = Pixel */ + /* TBD #2: start/size (to restore) */ + *cmd++ = gpuaddr(startSizePix, &drawctxt->gpustate); + + /* store the shared shader instructions if vertex base is nonzero */ + + *cmd++ = pm4_type3_packet(PM4_IM_STORE, 2); + *cmd++ = ctx->shader_shared + 0x2; /* 0x2 = Shared */ + /* TBD #3: start/size (to restore) */ + *cmd++ = gpuaddr(startSizeShared, &drawctxt->gpustate); + +#endif + + *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->shader_save, save, cmd); + + ctx->cmd = cmd; +} + +/* create buffers for saving/restoring registers, constants, & GMEM */ +static int +create_gpustate_shadow(struct kgsl_device *device, + struct adreno_context *drawctxt, + struct tmp_ctx *ctx) +{ + int result; + + /* Allocate vmalloc memory to store the gpustate */ + result = kgsl_sharedmem_vmalloc(&drawctxt->gpustate, + drawctxt->pagetable, CONTEXT_SIZE); + + if (result) + return result; + + drawctxt->flags |= CTXT_FLAGS_STATE_SHADOW; + + /* Blank out h/w register, constant, and command buffer shadows. */ + kgsl_sharedmem_set(&drawctxt->gpustate, 0, 0, CONTEXT_SIZE); + + /* set-up command and vertex buffer pointers */ + ctx->cmd = ctx->start + = (unsigned int *)((char *)drawctxt->gpustate.hostptr + CMD_OFFSET); + + /* build indirect command buffers to save & restore regs/constants */ + adreno_idle(device, KGSL_TIMEOUT_DEFAULT); + build_regrestore_cmds(device, drawctxt, ctx); + build_regsave_cmds(device, drawctxt, ctx); + + build_shader_save_restore_cmds(drawctxt, ctx); + + kgsl_cache_range_op(&drawctxt->gpustate, + KGSL_CACHE_OP_FLUSH); + + return 0; +} + +/* create buffers for saving/restoring registers, constants, & GMEM */ +static int +create_gmem_shadow(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt, + struct tmp_ctx *ctx) +{ + struct kgsl_device *device = &adreno_dev->dev; + int result; + + config_gmemsize(&drawctxt->context_gmem_shadow, + adreno_dev->gmemspace.sizebytes); + ctx->gmem_base = adreno_dev->gmemspace.gpu_base; + + result = kgsl_sharedmem_vmalloc( + &drawctxt->context_gmem_shadow.gmemshadow, + drawctxt->pagetable, + drawctxt->context_gmem_shadow.size); + + if (result) + return result; + + /* we've allocated the shadow, when swapped out, GMEM must be saved. */ + drawctxt->flags |= CTXT_FLAGS_GMEM_SHADOW | CTXT_FLAGS_GMEM_SAVE; + + /* blank out gmem shadow. */ + kgsl_sharedmem_set(&drawctxt->context_gmem_shadow.gmemshadow, 0, 0, + drawctxt->context_gmem_shadow.size); + + /* build quad vertex buffer */ + build_quad_vtxbuff(drawctxt, ctx, &drawctxt->context_gmem_shadow); + + /* build TP0_CHICKEN register restore command buffer */ + ctx->cmd = build_chicken_restore_cmds(drawctxt, ctx); + + /* build indirect command buffers to save & restore gmem */ + /* Idle because we are reading PM override registers */ + adreno_idle(device, KGSL_TIMEOUT_DEFAULT); + drawctxt->context_gmem_shadow.gmem_save_commands = ctx->cmd; + ctx->cmd = + build_gmem2sys_cmds(device, drawctxt, ctx, + &drawctxt->context_gmem_shadow); + drawctxt->context_gmem_shadow.gmem_restore_commands = ctx->cmd; + ctx->cmd = + build_sys2gmem_cmds(device, drawctxt, ctx, + &drawctxt->context_gmem_shadow); + + kgsl_cache_range_op(&drawctxt->context_gmem_shadow.gmemshadow, + KGSL_CACHE_OP_FLUSH); + + return 0; +} + +/* create a new drawing context */ + +int +adreno_drawctxt_create(struct kgsl_device_private *dev_priv, uint32_t flags, + struct kgsl_context *context) +{ + struct adreno_context *drawctxt; + struct kgsl_device *device = dev_priv->device; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct kgsl_pagetable *pagetable = dev_priv->process_priv->pagetable; + struct tmp_ctx ctx; + int ret; + + drawctxt = kzalloc(sizeof(struct adreno_context), GFP_KERNEL); + + if (drawctxt == NULL) + return -ENOMEM; + + drawctxt->pagetable = pagetable; + drawctxt->bin_base_offset = 0; + + ret = create_gpustate_shadow(device, drawctxt, &ctx); + if (ret) + goto err; + + /* Save the shader instruction memory on context switching */ + drawctxt->flags |= CTXT_FLAGS_SHADER_SAVE; + + memset(&drawctxt->context_gmem_shadow.gmemshadow, + 0, sizeof(struct kgsl_memdesc)); + + if (!(flags & KGSL_CONTEXT_NO_GMEM_ALLOC)) { + /* create gmem shadow */ + ret = create_gmem_shadow(adreno_dev, drawctxt, &ctx); + if (ret != 0) + goto err; + } + + BUG_ON(ctx.cmd - ctx.start > CMD_BUFFER_LEN); + + context->devctxt = drawctxt; + return 0; +err: + kgsl_sharedmem_free(&drawctxt->gpustate); + kfree(drawctxt); + return ret; +} + +/* destroy a drawing context */ + +int adreno_drawctxt_destroy(struct kgsl_device *device, + struct kgsl_context *context) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_context *drawctxt = context->devctxt; + + if (drawctxt == NULL) + return -EINVAL; + + /* deactivate context */ + if (adreno_dev->drawctxt_active == drawctxt) { + /* no need to save GMEM or shader, the context is + * being destroyed. + */ + drawctxt->flags &= ~(CTXT_FLAGS_GMEM_SAVE | + CTXT_FLAGS_SHADER_SAVE | + CTXT_FLAGS_GMEM_SHADOW | + CTXT_FLAGS_STATE_SHADOW); + + adreno_drawctxt_switch(adreno_dev, NULL, 0); + } + + adreno_idle(device, KGSL_TIMEOUT_DEFAULT); + + kgsl_sharedmem_free(&drawctxt->gpustate); + kgsl_sharedmem_free(&drawctxt->context_gmem_shadow.gmemshadow); + + kfree(drawctxt); + context->devctxt = NULL; + + return 0; +} + +/* set bin base offset */ +int adreno_drawctxt_set_bin_base_offset(struct kgsl_device *device, + struct kgsl_context *context, + unsigned int offset) +{ + struct adreno_context *drawctxt = context->devctxt; + + if (drawctxt == NULL) + return -EINVAL; + + drawctxt->bin_base_offset = offset; + + return 0; +} + +/* switch drawing contexts */ +void +adreno_drawctxt_switch(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt, + unsigned int flags) +{ + struct adreno_context *active_ctxt = + adreno_dev->drawctxt_active; + struct kgsl_device *device = &adreno_dev->dev; + unsigned int cmds[5]; + + if (drawctxt) { + if (flags & KGSL_CONTEXT_SAVE_GMEM) + /* Set the flag in context so that the save is done + * when this context is switched out. */ + drawctxt->flags |= CTXT_FLAGS_GMEM_SAVE; + else + /* Remove GMEM saving flag from the context */ + drawctxt->flags &= ~CTXT_FLAGS_GMEM_SAVE; + } + /* already current? */ + if (active_ctxt == drawctxt) + return; + + KGSL_CTXT_INFO(device, "from %p to %p flags %d\n", + adreno_dev->drawctxt_active, drawctxt, flags); + /* save old context*/ + if (active_ctxt && active_ctxt->flags & CTXT_FLAGS_GPU_HANG) + KGSL_CTXT_WARN(device, + "Current active context has caused gpu hang\n"); + + if (active_ctxt != NULL) { + KGSL_CTXT_INFO(device, + "active_ctxt flags %08x\n", active_ctxt->flags); + /* save registers and constants. */ + adreno_ringbuffer_issuecmds(device, 0, + active_ctxt->reg_save, 3); + + if (active_ctxt->flags & CTXT_FLAGS_SHADER_SAVE) { + /* save shader partitioning and instructions. */ + adreno_ringbuffer_issuecmds(device, + KGSL_CMD_FLAGS_PMODE, + active_ctxt->shader_save, 3); + + /* fixup shader partitioning parameter for + * SET_SHADER_BASES. + */ + adreno_ringbuffer_issuecmds(device, 0, + active_ctxt->shader_fixup, 3); + + active_ctxt->flags |= CTXT_FLAGS_SHADER_RESTORE; + } + + if (active_ctxt->flags & CTXT_FLAGS_GMEM_SAVE + && active_ctxt->flags & CTXT_FLAGS_GMEM_SHADOW) { + /* save gmem. + * (note: changes shader. shader must already be saved.) + */ + adreno_ringbuffer_issuecmds(device, + KGSL_CMD_FLAGS_PMODE, + active_ctxt->context_gmem_shadow.gmem_save, 3); + + /* Restore TP0_CHICKEN */ + adreno_ringbuffer_issuecmds(device, 0, + active_ctxt->chicken_restore, 3); + + active_ctxt->flags |= CTXT_FLAGS_GMEM_RESTORE; + } + } + + adreno_dev->drawctxt_active = drawctxt; + + /* restore new context */ + if (drawctxt != NULL) { + + KGSL_CTXT_INFO(device, + "drawctxt flags %08x\n", drawctxt->flags); + kgsl_mmu_setstate(device, drawctxt->pagetable); + +#ifndef CONFIG_MSM_KGSL_CFF_DUMP_NO_CONTEXT_MEM_DUMP + kgsl_cffdump_syncmem(NULL, &drawctxt->gpustate, + drawctxt->gpustate.gpuaddr, LCC_SHADOW_SIZE + + REG_SHADOW_SIZE + CMD_BUFFER_SIZE + TEX_SHADOW_SIZE, + false); +#endif + cmds[0] = pm4_nop_packet(1); + cmds[1] = KGSL_CONTEXT_TO_MEM_IDENTIFIER; + cmds[2] = pm4_type3_packet(PM4_MEM_WRITE, 2); + cmds[3] = device->memstore.gpuaddr + + KGSL_DEVICE_MEMSTORE_OFFSET(current_context); + cmds[4] = (unsigned int)adreno_dev->drawctxt_active; + adreno_ringbuffer_issuecmds(device, 0, cmds, 5); + + /* restore gmem. + * (note: changes shader. shader must not already be restored.) + */ + if (drawctxt->flags & CTXT_FLAGS_GMEM_RESTORE) { + adreno_ringbuffer_issuecmds(device, + KGSL_CMD_FLAGS_PMODE, + drawctxt->context_gmem_shadow.gmem_restore, 3); + + /* Restore TP0_CHICKEN */ + adreno_ringbuffer_issuecmds(device, 0, + drawctxt->chicken_restore, 3); + + drawctxt->flags &= ~CTXT_FLAGS_GMEM_RESTORE; + } + + /* restore registers and constants. */ + adreno_ringbuffer_issuecmds(device, 0, + drawctxt->reg_restore, 3); + + /* restore shader instructions & partitioning. */ + if (drawctxt->flags & CTXT_FLAGS_SHADER_RESTORE) { + adreno_ringbuffer_issuecmds(device, 0, + drawctxt->shader_restore, 3); + } + + cmds[0] = pm4_type3_packet(PM4_SET_BIN_BASE_OFFSET, 1); + cmds[1] = drawctxt->bin_base_offset; + if (device->chip_id != KGSL_CHIPID_LEIA_REV470) + adreno_ringbuffer_issuecmds(device, 0, cmds, 2); + + } else + kgsl_mmu_setstate(device, device->mmu.defaultpagetable); +} diff --git a/drivers/gpu/msm/adreno_drawctxt.h b/drivers/gpu/msm/adreno_drawctxt.h new file mode 100644 index 0000000000000..8ea4043623a49 --- /dev/null +++ b/drivers/gpu/msm/adreno_drawctxt.h @@ -0,0 +1,113 @@ +/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __ADRENO_DRAWCTXT_H +#define __ADRENO_DRAWCTXT_H + +#include "a200_reg.h" +#include "a220_reg.h" + +/* Flags */ + +#define CTXT_FLAGS_NOT_IN_USE 0x00000000 +#define CTXT_FLAGS_IN_USE 0x00000001 + +/* state shadow memory allocated */ +#define CTXT_FLAGS_STATE_SHADOW 0x00000010 + +/* gmem shadow memory allocated */ +#define CTXT_FLAGS_GMEM_SHADOW 0x00000100 +/* gmem must be copied to shadow */ +#define CTXT_FLAGS_GMEM_SAVE 0x00000200 +/* gmem can be restored from shadow */ +#define CTXT_FLAGS_GMEM_RESTORE 0x00000400 +/* shader must be copied to shadow */ +#define CTXT_FLAGS_SHADER_SAVE 0x00002000 +/* shader can be restored from shadow */ +#define CTXT_FLAGS_SHADER_RESTORE 0x00004000 +/* Context has caused a GPU hang */ +#define CTXT_FLAGS_GPU_HANG 0x00008000 + +struct kgsl_device; +struct adreno_device; +struct kgsl_device_private; +struct kgsl_context; + +/* draw context */ +struct gmem_shadow_t { + struct kgsl_memdesc gmemshadow; /* Shadow buffer address */ + + /* 256 KB GMEM surface = 4 bytes-per-pixel x 256 pixels/row x + * 256 rows. */ + /* width & height must be a multiples of 32, in case tiled textures + * are used. */ + enum COLORFORMATX format; + unsigned int size; /* Size of surface used to store GMEM */ + unsigned int width; /* Width of surface used to store GMEM */ + unsigned int height; /* Height of surface used to store GMEM */ + unsigned int pitch; /* Pitch of surface used to store GMEM */ + unsigned int gmem_pitch; /* Pitch value used for GMEM */ + unsigned int *gmem_save_commands; + unsigned int *gmem_restore_commands; + unsigned int gmem_save[3]; + unsigned int gmem_restore[3]; + struct kgsl_memdesc quad_vertices; + struct kgsl_memdesc quad_texcoords; +}; + +struct adreno_context { + uint32_t flags; + struct kgsl_pagetable *pagetable; + struct kgsl_memdesc gpustate; + unsigned int reg_save[3]; + unsigned int reg_restore[3]; + unsigned int shader_save[3]; + unsigned int shader_fixup[3]; + unsigned int shader_restore[3]; + unsigned int chicken_restore[3]; + unsigned int bin_base_offset; + /* Information of the GMEM shadow that is created in context create */ + struct gmem_shadow_t context_gmem_shadow; +}; + + +int adreno_drawctxt_create(struct kgsl_device_private *dev_priv, + uint32_t flags, + struct kgsl_context *context); + +int adreno_drawctxt_destroy(struct kgsl_device *device, + struct kgsl_context *context); + +void adreno_drawctxt_switch(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt, + unsigned int flags); +int adreno_drawctxt_set_bin_base_offset(struct kgsl_device *device, + struct kgsl_context *context, + unsigned int offset); + +#endif /* __ADRENO_DRAWCTXT_H */ diff --git a/drivers/gpu/msm/adreno_pm4types.h b/drivers/gpu/msm/adreno_pm4types.h new file mode 100644 index 0000000000000..246315b66095f --- /dev/null +++ b/drivers/gpu/msm/adreno_pm4types.h @@ -0,0 +1,193 @@ +/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __ADRENO_PM4TYPES_H +#define __ADRENO_PM4TYPES_H + + +#define PM4_PKT_MASK 0xc0000000 + +#define PM4_TYPE0_PKT ((unsigned int)0 << 30) +#define PM4_TYPE1_PKT ((unsigned int)1 << 30) +#define PM4_TYPE2_PKT ((unsigned int)2 << 30) +#define PM4_TYPE3_PKT ((unsigned int)3 << 30) + + +/* type3 packets */ +/* initialize CP's micro-engine */ +#define PM4_ME_INIT 0x48 + +/* skip N 32-bit words to get to the next packet */ +#define PM4_NOP 0x10 + +/* indirect buffer dispatch. prefetch parser uses this packet type to determine +* whether to pre-fetch the IB +*/ +#define PM4_INDIRECT_BUFFER 0x3f + +/* indirect buffer dispatch. same as IB, but init is pipelined */ +#define PM4_INDIRECT_BUFFER_PFD 0x37 + +/* wait for the IDLE state of the engine */ +#define PM4_WAIT_FOR_IDLE 0x26 + +/* wait until a register or memory location is a specific value */ +#define PM4_WAIT_REG_MEM 0x3c + +/* wait until a register location is equal to a specific value */ +#define PM4_WAIT_REG_EQ 0x52 + +/* wait until a register location is >= a specific value */ +#define PM4_WAT_REG_GTE 0x53 + +/* wait until a read completes */ +#define PM4_WAIT_UNTIL_READ 0x5c + +/* wait until all base/size writes from an IB_PFD packet have completed */ +#define PM4_WAIT_IB_PFD_COMPLETE 0x5d + +/* register read/modify/write */ +#define PM4_REG_RMW 0x21 + +/* reads register in chip and writes to memory */ +#define PM4_REG_TO_MEM 0x3e + +/* write N 32-bit words to memory */ +#define PM4_MEM_WRITE 0x3d + +/* write CP_PROG_COUNTER value to memory */ +#define PM4_MEM_WRITE_CNTR 0x4f + +/* conditional execution of a sequence of packets */ +#define PM4_COND_EXEC 0x44 + +/* conditional write to memory or register */ +#define PM4_COND_WRITE 0x45 + +/* generate an event that creates a write to memory when completed */ +#define PM4_EVENT_WRITE 0x46 + +/* generate a VS|PS_done event */ +#define PM4_EVENT_WRITE_SHD 0x58 + +/* generate a cache flush done event */ +#define PM4_EVENT_WRITE_CFL 0x59 + +/* generate a z_pass done event */ +#define PM4_EVENT_WRITE_ZPD 0x5b + + +/* initiate fetch of index buffer and draw */ +#define PM4_DRAW_INDX 0x22 + +/* draw using supplied indices in packet */ +#define PM4_DRAW_INDX_2 0x36 + +/* initiate fetch of index buffer and binIDs and draw */ +#define PM4_DRAW_INDX_BIN 0x34 + +/* initiate fetch of bin IDs and draw using supplied indices */ +#define PM4_DRAW_INDX_2_BIN 0x35 + + +/* begin/end initiator for viz query extent processing */ +#define PM4_VIZ_QUERY 0x23 + +/* fetch state sub-blocks and initiate shader code DMAs */ +#define PM4_SET_STATE 0x25 + +/* load constant into chip and to memory */ +#define PM4_SET_CONSTANT 0x2d + +/* load sequencer instruction memory (pointer-based) */ +#define PM4_IM_LOAD 0x27 + +/* load sequencer instruction memory (code embedded in packet) */ +#define PM4_IM_LOAD_IMMEDIATE 0x2b + +/* load constants from a location in memory */ +#define PM4_LOAD_CONSTANT_CONTEXT 0x2e + +/* selective invalidation of state pointers */ +#define PM4_INVALIDATE_STATE 0x3b + + +/* dynamically changes shader instruction memory partition */ +#define PM4_SET_SHADER_BASES 0x4A + +/* sets the 64-bit BIN_MASK register in the PFP */ +#define PM4_SET_BIN_MASK 0x50 + +/* sets the 64-bit BIN_SELECT register in the PFP */ +#define PM4_SET_BIN_SELECT 0x51 + + +/* updates the current context, if needed */ +#define PM4_CONTEXT_UPDATE 0x5e + +/* generate interrupt from the command stream */ +#define PM4_INTERRUPT 0x40 + + +/* copy sequencer instruction memory to system memory */ +#define PM4_IM_STORE 0x2c + +/* program an offset that will added to the BIN_BASE value of + * the 3D_DRAW_INDX_BIN packet */ +#define PM4_SET_BIN_BASE_OFFSET 0x4B + +#define PM4_SET_PROTECTED_MODE 0x5f /* sets the register protection mode */ + + +/* packet header building macros */ +#define pm4_type0_packet(regindx, cnt) \ + (PM4_TYPE0_PKT | (((cnt)-1) << 16) | ((regindx) & 0x7FFF)) + +#define pm4_type0_packet_for_sameregister(regindx, cnt) \ + ((PM4_TYPE0_PKT | (((cnt)-1) << 16) | ((1 << 15) | \ + ((regindx) & 0x7FFF))) + +#define pm4_type1_packet(reg0, reg1) \ + (PM4_TYPE1_PKT | ((reg1) << 12) | (reg0)) + +#define pm4_type3_packet(opcode, cnt) \ + (PM4_TYPE3_PKT | (((cnt)-1) << 16) | (((opcode) & 0xFF) << 8)) + +#define pm4_predicated_type3_packet(opcode, cnt) \ + (PM4_TYPE3_PKT | (((cnt)-1) << 16) | (((opcode) & 0xFF) << 8) | 0x1) + +#define pm4_nop_packet(cnt) \ + (PM4_TYPE3_PKT | (((cnt)-1) << 16) | (PM4_NOP << 8)) + + +/* packet headers */ +#define PM4_HDR_ME_INIT pm4_type3_packet(PM4_ME_INIT, 18) +#define PM4_HDR_INDIRECT_BUFFER_PFD pm4_type3_packet(PM4_INDIRECT_BUFFER_PFD, 2) +#define PM4_HDR_INDIRECT_BUFFER pm4_type3_packet(PM4_INDIRECT_BUFFER, 2) + +#endif /* __ADRENO_PM4TYPES_H */ diff --git a/drivers/gpu/msm/adreno_postmortem.c b/drivers/gpu/msm/adreno_postmortem.c new file mode 100644 index 0000000000000..11dc48298e27c --- /dev/null +++ b/drivers/gpu/msm/adreno_postmortem.c @@ -0,0 +1,865 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include + +#include "kgsl.h" + +#include "adreno.h" +#include "adreno_pm4types.h" +#include "adreno_ringbuffer.h" +#include "adreno_postmortem.h" +#include "adreno_debugfs.h" + +#include "a200_reg.h" + +#define INVALID_RB_CMD 0xaaaaaaaa + +struct pm_id_name { + uint32_t id; + char name[9]; +}; + +static const struct pm_id_name pm0_types[] = { + {REG_PA_SC_AA_CONFIG, "RPASCAAC"}, + {REG_RBBM_PM_OVERRIDE2, "RRBBPMO2"}, + {REG_SCRATCH_REG2, "RSCRTRG2"}, + {REG_SQ_GPR_MANAGEMENT, "RSQGPRMN"}, + {REG_SQ_INST_STORE_MANAGMENT, "RSQINSTS"}, + {REG_TC_CNTL_STATUS, "RTCCNTLS"}, + {REG_TP0_CHICKEN, "RTP0CHCK"}, + {REG_CP_TIMESTAMP, "CP_TM_ST"}, +}; + +static const struct pm_id_name pm3_types[] = { + {PM4_COND_EXEC, "CND_EXEC"}, + {PM4_CONTEXT_UPDATE, "CX__UPDT"}, + {PM4_DRAW_INDX, "DRW_NDX_"}, + {PM4_DRAW_INDX_BIN, "DRW_NDXB"}, + {PM4_EVENT_WRITE, "EVENT_WT"}, + {PM4_IM_LOAD, "IN__LOAD"}, + {PM4_IM_LOAD_IMMEDIATE, "IM_LOADI"}, + {PM4_IM_STORE, "IM_STORE"}, + {PM4_INDIRECT_BUFFER, "IND_BUF_"}, + {PM4_INDIRECT_BUFFER_PFD, "IND_BUFP"}, + {PM4_INTERRUPT, "PM4_INTR"}, + {PM4_INVALIDATE_STATE, "INV_STAT"}, + {PM4_LOAD_CONSTANT_CONTEXT, "LD_CN_CX"}, + {PM4_ME_INIT, "ME__INIT"}, + {PM4_NOP, "PM4__NOP"}, + {PM4_REG_RMW, "REG__RMW"}, + {PM4_REG_TO_MEM, "REG2_MEM"}, + {PM4_SET_BIN_BASE_OFFSET, "ST_BIN_O"}, + {PM4_SET_CONSTANT, "ST_CONST"}, + {PM4_SET_PROTECTED_MODE, "ST_PRT_M"}, + {PM4_SET_SHADER_BASES, "ST_SHD_B"}, + {PM4_WAIT_FOR_IDLE, "WAIT4IDL"}, +}; + +/* Offset address pairs: start, end of range to dump (inclusive) */ + +/* GPU < Z470 */ + +static const int a200_registers[] = { + 0x0000, 0x0008, 0x0010, 0x002c, 0x00ec, 0x00f4, + 0x0100, 0x0110, 0x0118, 0x011c, + 0x0700, 0x0704, 0x070c, 0x0720, 0x0754, 0x0764, + 0x0770, 0x0774, 0x07a8, 0x07a8, 0x07b8, 0x07cc, + 0x07d8, 0x07dc, 0x07f0, 0x07fc, 0x0e44, 0x0e48, + 0x0e6c, 0x0e78, 0x0ec8, 0x0ed4, 0x0edc, 0x0edc, + 0x0fe0, 0x0fec, 0x1100, 0x1100, + + 0x110c, 0x1110, 0x112c, 0x112c, 0x1134, 0x113c, + 0x1148, 0x1148, 0x1150, 0x116c, 0x11fc, 0x11fc, + 0x15e0, 0x161c, 0x1724, 0x1724, 0x1740, 0x1740, + 0x1804, 0x1810, 0x1818, 0x1824, 0x182c, 0x1838, + 0x184c, 0x1850, 0x28a4, 0x28ac, 0x28bc, 0x28c4, + 0x2900, 0x290c, 0x2914, 0x2914, 0x2938, 0x293c, + 0x30b0, 0x30b0, 0x30c0, 0x30c0, 0x30e0, 0x30f0, + 0x3100, 0x3100, 0x3110, 0x3110, 0x3200, 0x3218, + 0x3220, 0x3250, 0x3264, 0x3268, 0x3290, 0x3294, + 0x3400, 0x340c, 0x3418, 0x3418, 0x3420, 0x342c, + 0x34d0, 0x34d4, 0x36b8, 0x3704, 0x3720, 0x3750, + 0x3760, 0x3764, 0x3800, 0x3800, 0x3808, 0x3810, + 0x385c, 0x3878, 0x3b00, 0x3b24, 0x3b2c, 0x3b30, + 0x3b40, 0x3b40, 0x3b50, 0x3b5c, 0x3b80, 0x3b88, + 0x3c04, 0x3c08, 0x3c30, 0x3c30, 0x3c38, 0x3c48, + 0x3c98, 0x3ca8, 0x3cb0, 0x3cb0, + + 0x8000, 0x8008, 0x8018, 0x803c, 0x8200, 0x8208, + 0x8400, 0x8424, 0x8430, 0x8450, 0x8600, 0x8610, + 0x87d4, 0x87dc, 0x8800, 0x8820, 0x8a00, 0x8a0c, + 0x8a4c, 0x8a50, 0x8c00, 0x8c20, 0x8c48, 0x8c48, + 0x8c58, 0x8c74, 0x8c90, 0x8c98, 0x8e00, 0x8e0c, + + 0x9000, 0x9008, 0x9018, 0x903c, 0x9200, 0x9208, + 0x9400, 0x9424, 0x9430, 0x9450, 0x9600, 0x9610, + 0x97d4, 0x97dc, 0x9800, 0x9820, 0x9a00, 0x9a0c, + 0x9a4c, 0x9a50, 0x9c00, 0x9c20, 0x9c48, 0x9c48, + 0x9c58, 0x9c74, 0x9c90, 0x9c98, 0x9e00, 0x9e0c, + + 0x10000, 0x1000c, 0x12000, 0x12014, + 0x12400, 0x12400, 0x12420, 0x12420 +}; + +/* GPU = Z470 */ + +static const int a220_registers[] = { + 0x0000, 0x0008, 0x0010, 0x002c, 0x00ec, 0x00f4, + 0x0100, 0x0110, 0x0118, 0x011c, + 0x0700, 0x0704, 0x070c, 0x0720, 0x0754, 0x0764, + 0x0770, 0x0774, 0x07a8, 0x07a8, 0x07b8, 0x07cc, + 0x07d8, 0x07dc, 0x07f0, 0x07fc, 0x0e44, 0x0e48, + 0x0e6c, 0x0e78, 0x0ec8, 0x0ed4, 0x0edc, 0x0edc, + 0x0fe0, 0x0fec, 0x1100, 0x1100, + + 0x110c, 0x1110, 0x112c, 0x112c, 0x1134, 0x113c, + 0x1148, 0x1148, 0x1150, 0x116c, 0x11fc, 0x11fc, + 0x15e0, 0x161c, 0x1724, 0x1724, 0x1740, 0x1740, + 0x1804, 0x1810, 0x1818, 0x1824, 0x182c, 0x1838, + 0x184c, 0x1850, 0x28a4, 0x28ac, 0x28bc, 0x28c4, + 0x2900, 0x2900, 0x2908, 0x290c, 0x2914, 0x2914, + 0x2938, 0x293c, 0x30c0, 0x30c0, 0x30e0, 0x30e4, + 0x30f0, 0x30f0, 0x3200, 0x3204, 0x3220, 0x324c, + 0x3400, 0x340c, 0x3414, 0x3418, 0x3420, 0x342c, + 0x34d0, 0x34d4, 0x36b8, 0x3704, 0x3720, 0x3750, + 0x3760, 0x3764, 0x3800, 0x3800, 0x3808, 0x3810, + 0x385c, 0x3878, 0x3b00, 0x3b24, 0x3b2c, 0x3b30, + 0x3b40, 0x3b40, 0x3b50, 0x3b5c, 0x3b80, 0x3b88, + 0x3c04, 0x3c08, 0x8000, 0x8008, 0x8018, 0x803c, + 0x8200, 0x8208, 0x8400, 0x8408, 0x8410, 0x8424, + 0x8430, 0x8450, 0x8600, 0x8610, 0x87d4, 0x87dc, + 0x8800, 0x8808, 0x8810, 0x8810, 0x8820, 0x8820, + 0x8a00, 0x8a08, 0x8a50, 0x8a50, + 0x8c00, 0x8c20, 0x8c24, 0x8c28, 0x8c48, 0x8c48, + 0x8c58, 0x8c58, 0x8c60, 0x8c74, 0x8c90, 0x8c98, + 0x8e00, 0x8e0c, 0x9000, 0x9008, 0x9018, 0x903c, + 0x9200, 0x9208, 0x9400, 0x9408, 0x9410, 0x9424, + 0x9430, 0x9450, 0x9600, 0x9610, 0x97d4, 0x97dc, + 0x9800, 0x9808, 0x9810, 0x9818, 0x9820, 0x9820, + 0x9a00, 0x9a08, 0x9a50, 0x9a50, 0x9c00, 0x9c20, + 0x9c48, 0x9c48, 0x9c58, 0x9c58, 0x9c60, 0x9c74, + 0x9c90, 0x9c98, 0x9e00, 0x9e0c, + + 0x10000, 0x1000c, 0x12000, 0x12014, + 0x12400, 0x12400, 0x12420, 0x12420 +}; + +static struct { + int id; + const int *registers; + int len; +} kgsl_registers[] = { + { KGSL_CHIPID_LEIA_REV470, a220_registers, + ARRAY_SIZE(a220_registers) / 2 }, + { KGSL_CHIPID_LEIA_REV470_TEMP, a220_registers, + ARRAY_SIZE(a220_registers) / 2 }, + { KGSL_CHIPID_YAMATODX_REV21, a200_registers, + ARRAY_SIZE(a200_registers) / 2 }, + { KGSL_CHIPID_YAMATODX_REV211, a200_registers, + ARRAY_SIZE(a200_registers) / 2 }, + { 0x0, NULL, 0}, +}; + +static uint32_t adreno_is_pm4_len(uint32_t word) +{ + if (word == INVALID_RB_CMD) + return 0; + + return (word >> 16) & 0x3FFF; +} + +static bool adreno_is_pm4_type(uint32_t word) +{ + int i; + + if (word == INVALID_RB_CMD) + return 1; + + if (adreno_is_pm4_len(word) > 16) + return 0; + + if ((word & (3<<30)) == PM4_TYPE0_PKT) { + for (i = 0; i < ARRAY_SIZE(pm0_types); ++i) { + if ((word & 0x7FFF) == pm0_types[i].id) + return 1; + } + return 0; + } + if ((word & (3<<30)) == PM4_TYPE3_PKT) { + for (i = 0; i < ARRAY_SIZE(pm3_types); ++i) { + if ((word & 0xFFFF) == (pm3_types[i].id << 8)) + return 1; + } + return 0; + } + return 0; +} + +static const char *adreno_pm4_name(uint32_t word) +{ + int i; + + if (word == INVALID_RB_CMD) + return "--------"; + + if ((word & (3<<30)) == PM4_TYPE0_PKT) { + for (i = 0; i < ARRAY_SIZE(pm0_types); ++i) { + if ((word & 0x7FFF) == pm0_types[i].id) + return pm0_types[i].name; + } + return "????????"; + } + if ((word & (3<<30)) == PM4_TYPE3_PKT) { + for (i = 0; i < ARRAY_SIZE(pm3_types); ++i) { + if ((word & 0xFFFF) == (pm3_types[i].id << 8)) + return pm3_types[i].name; + } + return "????????"; + } + return "????????"; +} + +static void adreno_dump_regs(struct kgsl_device *device, + const int *registers, int size) +{ + int range = 0, offset = 0; + + for (range = 0; range < size; range++) { + /* start and end are in dword offsets */ + int start = registers[range * 2] / 4; + int end = registers[range * 2 + 1] / 4; + + unsigned char linebuf[32 * 3 + 2 + 32 + 1]; + int linelen, i; + + for (offset = start; offset <= end; offset += linelen) { + unsigned int regvals[32/4]; + linelen = min(end+1-offset, 32/4); + + for (i = 0; i < linelen; ++i) + kgsl_regread(device, offset+i, regvals+i); + + hex_dump_to_buffer(regvals, linelen*4, 32, 4, + linebuf, sizeof(linebuf), 0); + KGSL_LOG_DUMP(device, + "REG: %5.5X: %s\n", offset<<2, linebuf); + } + } +} + +static void dump_ib(struct kgsl_device *device, char* buffId, uint32_t pt_base, + uint32_t base_offset, uint32_t ib_base, uint32_t ib_size, bool dump) +{ + unsigned int memsize; + uint8_t *base_addr = kgsl_sharedmem_convertaddr(device, pt_base, + ib_base, &memsize); + + if (base_addr && dump) + print_hex_dump(KERN_ERR, buffId, DUMP_PREFIX_OFFSET, + 32, 4, base_addr, ib_size*4, 0); + else + KGSL_LOG_DUMP(device, "%s base:%8.8X ib_size:%d " + "offset:%5.5X%s\n", + buffId, ib_base, ib_size*4, base_offset, + base_addr ? "" : " [Invalid]"); +} + +#define IB_LIST_SIZE 64 +struct ib_list { + int count; + uint32_t bases[IB_LIST_SIZE]; + uint32_t sizes[IB_LIST_SIZE]; + uint32_t offsets[IB_LIST_SIZE]; +}; + +static void dump_ib1(struct kgsl_device *device, uint32_t pt_base, + uint32_t base_offset, + uint32_t ib1_base, uint32_t ib1_size, + struct ib_list *ib_list, bool dump) +{ + int i, j; + uint32_t value; + uint32_t *ib1_addr; + unsigned int memsize; + + dump_ib(device, "IB1:", pt_base, base_offset, ib1_base, + ib1_size, dump); + + /* fetch virtual address for given IB base */ + ib1_addr = (uint32_t *)kgsl_sharedmem_convertaddr(device, pt_base, + ib1_base, &memsize); + if (!ib1_addr) + return; + + for (i = 0; i+3 < ib1_size; ) { + value = ib1_addr[i++]; + if (value == pm4_type3_packet(PM4_INDIRECT_BUFFER_PFD, 2)) { + uint32_t ib2_base = ib1_addr[i++]; + uint32_t ib2_size = ib1_addr[i++]; + + /* find previous match */ + for (j = 0; j < ib_list->count; ++j) + if (ib_list->sizes[j] == ib2_size + && ib_list->bases[j] == ib2_base) + break; + + if (j < ib_list->count || ib_list->count + >= IB_LIST_SIZE) + continue; + + /* store match */ + ib_list->sizes[ib_list->count] = ib2_size; + ib_list->bases[ib_list->count] = ib2_base; + ib_list->offsets[ib_list->count] = i<<2; + ++ib_list->count; + } + } +} + +static void adreno_dump_rb_buffer(const void *buf, size_t len, + char *linebuf, size_t linebuflen, int *argp) +{ + const u32 *ptr4 = buf; + const int ngroups = len; + int lx = 0, j; + bool nxsp = 1; + + for (j = 0; j < ngroups; j++) { + if (*argp < 0) { + lx += scnprintf(linebuf + lx, linebuflen - lx, " <"); + *argp = -*argp; + } else if (nxsp) + lx += scnprintf(linebuf + lx, linebuflen - lx, " "); + else + nxsp = 1; + if (!*argp && adreno_is_pm4_type(ptr4[j])) { + lx += scnprintf(linebuf + lx, linebuflen - lx, + "%s", adreno_pm4_name(ptr4[j])); + *argp = -(adreno_is_pm4_len(ptr4[j])+1); + } else { + lx += scnprintf(linebuf + lx, linebuflen - lx, + "%8.8X", ptr4[j]); + if (*argp > 1) + --*argp; + else if (*argp == 1) { + *argp = 0; + nxsp = 0; + lx += scnprintf(linebuf + lx, linebuflen - lx, + "> "); + } + } + } + linebuf[lx] = '\0'; +} + +static bool adreno_rb_use_hex(void) +{ +#ifdef CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX + return 1; +#else + return 0; +#endif +} + +static void adreno_dump_rb(struct kgsl_device *device, const void *buf, + size_t len, int start, int size) +{ + const uint32_t *ptr = buf; + int i, remaining, args = 0; + unsigned char linebuf[32 * 3 + 2 + 32 + 1]; + const int rowsize = 8; + + len >>= 2; + remaining = len; + for (i = 0; i < len; i += rowsize) { + int linelen = min(remaining, rowsize); + remaining -= rowsize; + + if (adreno_rb_use_hex()) + hex_dump_to_buffer(ptr+i, linelen*4, rowsize*4, 4, + linebuf, sizeof(linebuf), 0); + else + adreno_dump_rb_buffer(ptr+i, linelen, linebuf, + sizeof(linebuf), &args); + KGSL_LOG_DUMP(device, + "RB: %4.4X:%s\n", (start+i)%size, linebuf); + } +} + +static bool adreno_ib_dump_enabled(void) +{ +#ifdef CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP + return 0; +#else + return 1; +#endif +} + +struct log_field { + bool show; + const char *display; +}; + +static int adreno_dump_fields_line(struct kgsl_device *device, + const char *start, char *str, int slen, + const struct log_field **lines, + int num) +{ + const struct log_field *l = *lines; + int sptr, count = 0; + + sptr = snprintf(str, slen, "%s", start); + + for ( ; num && sptr < slen; num--, l++) { + int ilen = strlen(l->display); + + if (count) + ilen += strlen(" | "); + + if (ilen > (slen - sptr)) + break; + + if (count++) + sptr += snprintf(str + sptr, slen - sptr, " | "); + + sptr += snprintf(str + sptr, slen - sptr, "%s", l->display); + } + + KGSL_LOG_DUMP(device, "%s\n", str); + + *lines = l; + return num; +} + +static void adreno_dump_fields(struct kgsl_device *device, + const char *start, const struct log_field *lines, + int num) +{ + char lb[90]; + const char *sstr = start; + + lb[sizeof(lb) - 1] = '\0'; + + while (num) { + int ret = adreno_dump_fields_line(device, sstr, lb, + sizeof(lb) - 1, &lines, num); + + if (ret == num) + break; + + num = ret; + sstr = " "; + } +} + +static int adreno_dump(struct kgsl_device *device) +{ + unsigned int r1, r2, r3, rbbm_status; + unsigned int cp_ib1_base, cp_ib1_bufsz, cp_stat; + unsigned int cp_ib2_base, cp_ib2_bufsz; + unsigned int pt_base; + unsigned int cp_rb_base, rb_count; + unsigned int cp_rb_wptr, cp_rb_rptr; + unsigned int i; + int result = 0; + uint32_t *rb_copy; + const uint32_t *rb_vaddr; + int num_item = 0; + int read_idx, write_idx; + unsigned int ts_processed, rb_memsize; + + static struct ib_list ib_list; + + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + + mb(); + + KGSL_LOG_DUMP(device, "POWER: FLAGS = %08X | ACTIVE POWERLEVEL = %08X", + pwr->power_flags, pwr->active_pwrlevel); + + KGSL_LOG_DUMP(device, "POWER: INTERVAL TIMEOUT = %08X ", + pwr->interval_timeout); + + KGSL_LOG_DUMP(device, "GRP_CLK = %lu ", + kgsl_get_clkrate(pwr->grp_clks[0])); + + KGSL_LOG_DUMP(device, "BUS CLK = %lu ", + kgsl_get_clkrate(pwr->ebi1_clk)); + + + kgsl_regread(device, REG_RBBM_STATUS, &rbbm_status); + kgsl_regread(device, REG_RBBM_PM_OVERRIDE1, &r2); + kgsl_regread(device, REG_RBBM_PM_OVERRIDE2, &r3); + KGSL_LOG_DUMP(device, "RBBM: STATUS = %08X | PM_OVERRIDE1 = %08X | " + "PM_OVERRIDE2 = %08X\n", rbbm_status, r2, r3); + + kgsl_regread(device, REG_RBBM_INT_CNTL, &r1); + kgsl_regread(device, REG_RBBM_INT_STATUS, &r2); + kgsl_regread(device, REG_RBBM_READ_ERROR, &r3); + KGSL_LOG_DUMP(device, " INT_CNTL = %08X | INT_STATUS = %08X | " + "READ_ERROR = %08X\n", r1, r2, r3); + + { + char cmdFifo[16]; + struct log_field lines[] = { + {rbbm_status & 0x000F, cmdFifo}, + {rbbm_status & BIT(5), "TC busy "}, + {rbbm_status & BIT(8), "HIRQ pending"}, + {rbbm_status & BIT(9), "CPRQ pending"}, + {rbbm_status & BIT(10), "CFRQ pending"}, + {rbbm_status & BIT(11), "PFRQ pending"}, + {rbbm_status & BIT(12), "VGT 0DMA bsy"}, + {rbbm_status & BIT(14), "RBBM WU busy"}, + {rbbm_status & BIT(16), "CP NRT busy "}, + {rbbm_status & BIT(18), "MH busy "}, + {rbbm_status & BIT(19), "MH chncy bsy"}, + {rbbm_status & BIT(21), "SX busy "}, + {rbbm_status & BIT(22), "TPC busy "}, + {rbbm_status & BIT(24), "SC CNTX busy"}, + {rbbm_status & BIT(25), "PA busy "}, + {rbbm_status & BIT(26), "VGT busy "}, + {rbbm_status & BIT(27), "SQ cntx1 bsy"}, + {rbbm_status & BIT(28), "SQ cntx0 bsy"}, + {rbbm_status & BIT(30), "RB busy "}, + {rbbm_status & BIT(31), "Grphs pp bsy"}, + }; + snprintf(cmdFifo, sizeof(cmdFifo), "CMD FIFO=%01X ", + rbbm_status & 0xf); + adreno_dump_fields(device, " STATUS=", lines, + ARRAY_SIZE(lines)); + } + + kgsl_regread(device, REG_CP_RB_BASE, &cp_rb_base); + kgsl_regread(device, REG_CP_RB_CNTL, &r2); + rb_count = 2 << (r2 & (BIT(6)-1)); + kgsl_regread(device, REG_CP_RB_RPTR_ADDR, &r3); + KGSL_LOG_DUMP(device, + "CP_RB: BASE = %08X | CNTL = %08X | RPTR_ADDR = %08X" + "\n", cp_rb_base, r2, r3); + + kgsl_regread(device, REG_CP_RB_RPTR, &cp_rb_rptr); + kgsl_regread(device, REG_CP_RB_WPTR, &cp_rb_wptr); + kgsl_regread(device, REG_CP_RB_RPTR_WR, &r3); + KGSL_LOG_DUMP(device, + " RPTR = %08X | WPTR = %08X | RPTR_WR = %08X" + "\n", cp_rb_rptr, cp_rb_wptr, r3); + + kgsl_regread(device, REG_CP_IB1_BASE, &cp_ib1_base); + kgsl_regread(device, REG_CP_IB1_BUFSZ, &cp_ib1_bufsz); + KGSL_LOG_DUMP(device, + "CP_IB1: BASE = %08X | BUFSZ = %d\n", cp_ib1_base, + cp_ib1_bufsz); + + kgsl_regread(device, REG_CP_IB2_BASE, &cp_ib2_base); + kgsl_regread(device, REG_CP_IB2_BUFSZ, &cp_ib2_bufsz); + KGSL_LOG_DUMP(device, + "CP_IB2: BASE = %08X | BUFSZ = %d\n", cp_ib2_base, + cp_ib2_bufsz); + + kgsl_regread(device, REG_CP_INT_CNTL, &r1); + kgsl_regread(device, REG_CP_INT_STATUS, &r2); + KGSL_LOG_DUMP(device, "CP_INT: CNTL = %08X | STATUS = %08X\n", r1, r2); + + kgsl_regread(device, REG_CP_ME_CNTL, &r1); + kgsl_regread(device, REG_CP_ME_STATUS, &r2); + kgsl_regread(device, REG_MASTER_INT_SIGNAL, &r3); + KGSL_LOG_DUMP(device, + "CP_ME: CNTL = %08X | STATUS = %08X | MSTR_INT_SGNL = " + "%08X\n", r1, r2, r3); + + kgsl_regread(device, REG_CP_STAT, &cp_stat); + KGSL_LOG_DUMP(device, "CP_STAT = %08X\n", cp_stat); +#ifndef CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL + { + struct log_field lns[] = { + {cp_stat & BIT(0), "WR_BSY 0"}, + {cp_stat & BIT(1), "RD_RQ_BSY 1"}, + {cp_stat & BIT(2), "RD_RTN_BSY 2"}, + }; + adreno_dump_fields(device, " MIU=", lns, ARRAY_SIZE(lns)); + } + { + struct log_field lns[] = { + {cp_stat & BIT(5), "RING_BUSY 5"}, + {cp_stat & BIT(6), "NDRCTS_BSY 6"}, + {cp_stat & BIT(7), "NDRCT2_BSY 7"}, + {cp_stat & BIT(9), "ST_BUSY 9"}, + {cp_stat & BIT(10), "BUSY 10"}, + }; + adreno_dump_fields(device, " CSF=", lns, ARRAY_SIZE(lns)); + } + { + struct log_field lns[] = { + {cp_stat & BIT(11), "RNG_Q_BSY 11"}, + {cp_stat & BIT(12), "NDRCTS_Q_B12"}, + {cp_stat & BIT(13), "NDRCT2_Q_B13"}, + {cp_stat & BIT(16), "ST_QUEUE_B16"}, + {cp_stat & BIT(17), "PFP_BUSY 17"}, + }; + adreno_dump_fields(device, " RING=", lns, ARRAY_SIZE(lns)); + } + { + struct log_field lns[] = { + {cp_stat & BIT(3), "RBIU_BUSY 3"}, + {cp_stat & BIT(4), "RCIU_BUSY 4"}, + {cp_stat & BIT(18), "MQ_RG_BSY 18"}, + {cp_stat & BIT(19), "MQ_NDRS_BS19"}, + {cp_stat & BIT(20), "MQ_NDR2_BS20"}, + {cp_stat & BIT(21), "MIU_WC_STL21"}, + {cp_stat & BIT(22), "CP_NRT_BSY22"}, + {cp_stat & BIT(23), "3D_BUSY 23"}, + {cp_stat & BIT(26), "ME_BUSY 26"}, + {cp_stat & BIT(29), "ME_WC_BSY 29"}, + {cp_stat & BIT(30), "MIU_FF EM 30"}, + {cp_stat & BIT(31), "CP_BUSY 31"}, + }; + adreno_dump_fields(device, " CP_STT=", lns, ARRAY_SIZE(lns)); + } +#endif + + kgsl_regread(device, REG_SCRATCH_REG0, &r1); + KGSL_LOG_DUMP(device, "SCRATCH_REG0 = %08X\n", r1); + + kgsl_regread(device, REG_COHER_SIZE_PM4, &r1); + kgsl_regread(device, REG_COHER_BASE_PM4, &r2); + kgsl_regread(device, REG_COHER_STATUS_PM4, &r3); + KGSL_LOG_DUMP(device, + "COHER: SIZE_PM4 = %08X | BASE_PM4 = %08X | STATUS_PM4" + " = %08X\n", r1, r2, r3); + + kgsl_regread(device, REG_MH_AXI_ERROR, &r1); + KGSL_LOG_DUMP(device, "MH: AXI_ERROR = %08X\n", r1); + + kgsl_regread(device, REG_MH_MMU_PAGE_FAULT, &r1); + kgsl_regread(device, REG_MH_MMU_CONFIG, &r2); + kgsl_regread(device, REG_MH_MMU_MPU_BASE, &r3); + KGSL_LOG_DUMP(device, + "MH_MMU: PAGE_FAULT = %08X | CONFIG = %08X | MPU_BASE =" + " %08X\n", r1, r2, r3); + + kgsl_regread(device, REG_MH_MMU_MPU_END, &r1); + kgsl_regread(device, REG_MH_MMU_VA_RANGE, &r2); + kgsl_regread(device, REG_MH_MMU_PT_BASE, &pt_base); + KGSL_LOG_DUMP(device, + " MPU_END = %08X | VA_RANGE = %08X | PT_BASE =" + " %08X\n", r1, r2, pt_base); + + KGSL_LOG_DUMP(device, "PAGETABLE SIZE: %08X ", KGSL_PAGETABLE_SIZE); + + kgsl_regread(device, REG_MH_MMU_TRAN_ERROR, &r1); + KGSL_LOG_DUMP(device, " TRAN_ERROR = %08X\n", r1); + + kgsl_regread(device, REG_MH_INTERRUPT_MASK, &r1); + kgsl_regread(device, REG_MH_INTERRUPT_STATUS, &r2); + KGSL_LOG_DUMP(device, + "MH_INTERRUPT: MASK = %08X | STATUS = %08X\n", r1, r2); + + if (device->ftbl.device_readtimestamp != NULL) { + ts_processed = device->ftbl.device_readtimestamp( + device, KGSL_TIMESTAMP_RETIRED); + KGSL_LOG_DUMP(device, "TIMESTM RTRD: %08X\n", ts_processed); + } + + num_item = adreno_ringbuffer_count(&adreno_dev->ringbuffer, + cp_rb_rptr); + if (num_item <= 0) + KGSL_LOG_POSTMORTEM_WRITE(device, "Ringbuffer is Empty.\n"); + + rb_copy = vmalloc(rb_count<<2); + if (!rb_copy) { + KGSL_LOG_POSTMORTEM_WRITE(device, + "vmalloc(%d) failed\n", rb_count << 2); + result = -ENOMEM; + goto end; + } + + KGSL_LOG_DUMP(device, "RB: rd_addr:%8.8x rb_size:%d num_item:%d\n", + cp_rb_base, rb_count<<2, num_item); + rb_vaddr = (const uint32_t *)kgsl_sharedmem_convertaddr(device, pt_base, + cp_rb_base, &rb_memsize); + if (!rb_vaddr) { + KGSL_LOG_POSTMORTEM_WRITE(device, + "Can't fetch vaddr for CP_RB_BASE\n"); + goto error_vfree; + } + + read_idx = (int)cp_rb_rptr - 64; + if (read_idx < 0) + read_idx += rb_count; + write_idx = (int)cp_rb_wptr + 16; + if (write_idx > rb_count) + write_idx -= rb_count; + num_item += 64+16; + if (num_item > rb_count) + num_item = rb_count; + if (write_idx >= read_idx) + memcpy(rb_copy, rb_vaddr+read_idx, num_item<<2); + else { + int part1_c = rb_count-read_idx; + memcpy(rb_copy, rb_vaddr+read_idx, part1_c<<2); + memcpy(rb_copy+part1_c, rb_vaddr, (num_item-part1_c)<<2); + } + + /* extract the latest ib commands from the buffer */ + ib_list.count = 0; + i = 0; + for (read_idx = 0; read_idx < num_item; ) { + uint32_t this_cmd = rb_copy[read_idx++]; + if (this_cmd == pm4_type3_packet(PM4_INDIRECT_BUFFER_PFD, 2)) { + uint32_t ib_addr = rb_copy[read_idx++]; + uint32_t ib_size = rb_copy[read_idx++]; + dump_ib1(device, pt_base, (read_idx-3)<<2, ib_addr, + ib_size, &ib_list, 0); + for (; i < ib_list.count; ++i) + dump_ib(device, "IB2:", pt_base, + ib_list.offsets[i], + ib_list.bases[i], + ib_list.sizes[i], 0); + } + } + + read_idx = (int)cp_rb_rptr - 64; + if (read_idx < 0) + read_idx += rb_count; + KGSL_LOG_DUMP(device, + "RB: addr=%8.8x window:%4.4x-%4.4x, start:%4.4x\n", + cp_rb_base, cp_rb_rptr, cp_rb_wptr, read_idx); + adreno_dump_rb(device, rb_copy, num_item<<2, read_idx, rb_count); + + if (adreno_ib_dump_enabled()) { + for (read_idx = 64; read_idx >= 0; --read_idx) { + uint32_t this_cmd = rb_copy[read_idx]; + if (this_cmd == pm4_type3_packet( + PM4_INDIRECT_BUFFER_PFD, 2)) { + uint32_t ib_addr = rb_copy[read_idx+1]; + uint32_t ib_size = rb_copy[read_idx+2]; + if (cp_ib1_bufsz && cp_ib1_base == ib_addr) { + KGSL_LOG_DUMP(device, + "IB1: base:%8.8X " + "count:%d\n", ib_addr, ib_size); + dump_ib(device, "IB1: ", pt_base, + read_idx<<2, ib_addr, ib_size, + 1); + } + } + } + for (i = 0; i < ib_list.count; ++i) { + if (cp_ib2_bufsz && cp_ib2_base == ib_list.bases[i]) { + uint32_t ib_size = ib_list.sizes[i]; + uint32_t ib_offset = ib_list.offsets[i]; + KGSL_LOG_DUMP(device, + "IB2: base:%8.8X count:%d\n", + cp_ib2_base, ib_size); + dump_ib(device, "IB2: ", pt_base, ib_offset, + ib_list.bases[i], ib_size, 1); + } + } + } + + /* Dump the registers if the user asked for it */ + + for (i = 0; kgsl_pmregs_enabled() && kgsl_registers[i].id; i++) { + if (kgsl_registers[i].id == device->chip_id) { + adreno_dump_regs(device, kgsl_registers[i].registers, + kgsl_registers[i].len); + break; + } + } + +error_vfree: + vfree(rb_copy); +end: + return result; +} + +/** + * adreno_postmortem_dump - Dump the current GPU state + * @device - A pointer to the KGSL device to dump + * @manual - A flag that indicates if this was a manually triggered + * dump (from debugfs). If zero, then this is assumed to be a + * dump automaticlaly triggered from a hang +*/ + +int adreno_postmortem_dump(struct kgsl_device *device, int manual) +{ + bool saved_nap; + + BUG_ON(device == NULL); + + /* For a manual dump, make sure that the system is idle */ + + if (manual) { + if (device->active_cnt != 0) { + mutex_unlock(&device->mutex); + wait_for_completion(&device->suspend_gate); + mutex_lock(&device->mutex); + } + + if (device->state == KGSL_STATE_ACTIVE) + kgsl_idle(device, KGSL_TIMEOUT_DEFAULT); + + } + /* Disable the idle timer so we don't get interrupted */ + del_timer(&device->idle_timer); + + /* Turn off napping to make sure we have the clocks full + attention through the following process */ + saved_nap = device->pwrctrl.nap_allowed; + device->pwrctrl.nap_allowed = false; + + /* Force on the clocks */ + kgsl_pwrctrl_wake(device); + + /* Disable the irq */ + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); + + /* If this is not a manual trigger, then set up the + state to try to recover */ + + if (!manual) { + device->state = KGSL_STATE_DUMP_AND_RECOVER; + KGSL_PWR_WARN(device, + "state -> DUMP_AND_RECOVER, device %d\n", + device->id); + } + + KGSL_DRV_ERR(device, + "wait for work in workqueue to complete\n"); + mutex_unlock(&device->mutex); + flush_workqueue(device->work_queue); + mutex_lock(&device->mutex); + adreno_dump(device); + + /* Restore nap mode */ + device->pwrctrl.nap_allowed = saved_nap; + + /* On a manual trigger, turn on the interrupts and put + the clocks to sleep. They will recover themselves + on the next event. For a hang, leave things as they + are until recovery kicks in. */ + + if (manual) { + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_ON); + + /* try to go into a sleep mode until the next event */ + device->requested_state = KGSL_STATE_SLEEP; + kgsl_pwrctrl_sleep(device); + } + + KGSL_DRV_ERR(device, "Dump Finished\n"); + + return 0; +} diff --git a/drivers/gpu/msm/adreno_postmortem.h b/drivers/gpu/msm/adreno_postmortem.h new file mode 100644 index 0000000000000..1a432489ba8e7 --- /dev/null +++ b/drivers/gpu/msm/adreno_postmortem.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __ADRENO_POSTMORTEM_H +#define __ADRENO_POSTMORTEM_H + +struct kgsl_device; + +int adreno_postmortem_dump(struct kgsl_device *device, int manual); + +#endif /* __ADRENO_POSTMORTEM_H */ diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c new file mode 100644 index 0000000000000..624503e31f2f7 --- /dev/null +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -0,0 +1,915 @@ +/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include + +#include "kgsl.h" + +#include "adreno.h" +#include "adreno_pm4types.h" +#include "adreno_ringbuffer.h" + +#include "a200_reg.h" + +#define VALID_STATUS_COUNT_MAX 10 +#define GSL_RB_NOP_SIZEDWORDS 2 +/* protected mode error checking below register address 0x800 +* note: if CP_INTERRUPT packet is used then checking needs +* to change to below register address 0x7C8 +*/ +#define GSL_RB_PROTECTED_MODE_CONTROL 0x200001F2 + +#define GSL_CP_INT_MASK \ + (CP_INT_CNTL__SW_INT_MASK | \ + CP_INT_CNTL__T0_PACKET_IN_IB_MASK | \ + CP_INT_CNTL__OPCODE_ERROR_MASK | \ + CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK | \ + CP_INT_CNTL__RESERVED_BIT_ERROR_MASK | \ + CP_INT_CNTL__IB_ERROR_MASK | \ + CP_INT_CNTL__IB2_INT_MASK | \ + CP_INT_CNTL__IB1_INT_MASK | \ + CP_INT_CNTL__RB_INT_MASK) + +/* Firmware file names + * Legacy names must remain but replacing macro names to + * match current kgsl model. + * a200 is yamato + * a220 is leia + */ +#define A200_PFP_FW "yamato_pfp.fw" +#define A200_PM4_FW "yamato_pm4.fw" +#define A220_PFP_470_FW "leia_pfp_470.fw" +#define A220_PM4_470_FW "leia_pm4_470.fw" + +/* ringbuffer size log2 quadwords equivalent */ +inline unsigned int adreno_ringbuffer_sizelog2quadwords(unsigned int sizedwords) +{ + unsigned int sizelog2quadwords = 0; + int i = sizedwords >> 1; + + while (i >>= 1) + sizelog2quadwords++; + + return sizelog2quadwords; +} + + +/* functions */ +void kgsl_cp_intrcallback(struct kgsl_device *device) +{ + unsigned int status = 0, num_reads = 0, master_status = 0; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; + + adreno_regread_isr(device, REG_MASTER_INT_SIGNAL, &master_status); + while (!status && (num_reads < VALID_STATUS_COUNT_MAX) && + (master_status & MASTER_INT_SIGNAL__CP_INT_STAT)) { + adreno_regread_isr(device, REG_CP_INT_STATUS, &status); + adreno_regread_isr(device, REG_MASTER_INT_SIGNAL, + &master_status); + num_reads++; + } + if (num_reads > 1) + KGSL_DRV_WARN(device, + "Looped %d times to read REG_CP_INT_STATUS\n", + num_reads); + if (!status) { + if (master_status & MASTER_INT_SIGNAL__CP_INT_STAT) { + /* This indicates that we could not read CP_INT_STAT. + * As a precaution just wake up processes so + * they can check their timestamps. Since, we + * did not ack any interrupts this interrupt will + * be generated again */ + KGSL_DRV_WARN(device, "Unable to read CP_INT_STATUS\n"); + wake_up_interruptible_all(&device->wait_queue); + } else + KGSL_DRV_WARN(device, "Spurious interrput detected\n"); + return; + } + + if (status & CP_INT_CNTL__RB_INT_MASK) { + /* signal intr completion event */ + unsigned int enableflag = 0; + kgsl_sharedmem_writel(&rb->device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable), + enableflag); + wmb(); + KGSL_CMD_WARN(rb->device, "ringbuffer rb interrupt\n"); + } + + if (status & CP_INT_CNTL__T0_PACKET_IN_IB_MASK) { + KGSL_CMD_CRIT(rb->device, + "ringbuffer TO packet in IB interrupt\n"); + adreno_regwrite_isr(rb->device, REG_CP_INT_CNTL, 0); + } + if (status & CP_INT_CNTL__OPCODE_ERROR_MASK) { + KGSL_CMD_CRIT(rb->device, + "ringbuffer opcode error interrupt\n"); + adreno_regwrite_isr(rb->device, REG_CP_INT_CNTL, 0); + } + if (status & CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK) { + KGSL_CMD_CRIT(rb->device, + "ringbuffer protected mode error interrupt\n"); + adreno_regwrite_isr(rb->device, REG_CP_INT_CNTL, 0); + } + if (status & CP_INT_CNTL__RESERVED_BIT_ERROR_MASK) { + KGSL_CMD_CRIT(rb->device, + "ringbuffer reserved bit error interrupt\n"); + adreno_regwrite_isr(rb->device, REG_CP_INT_CNTL, 0); + } + if (status & CP_INT_CNTL__IB_ERROR_MASK) { + KGSL_CMD_CRIT(rb->device, + "ringbuffer IB error interrupt\n"); + adreno_regwrite_isr(rb->device, REG_CP_INT_CNTL, 0); + } + if (status & CP_INT_CNTL__SW_INT_MASK) + KGSL_CMD_INFO(rb->device, "ringbuffer software interrupt\n"); + + if (status & CP_INT_CNTL__IB2_INT_MASK) + KGSL_CMD_INFO(rb->device, "ringbuffer ib2 interrupt\n"); + + if (status & (~GSL_CP_INT_MASK)) + KGSL_CMD_WARN(rb->device, + "bad bits in REG_CP_INT_STATUS %08x\n", status); + + /* only ack bits we understand */ + status &= GSL_CP_INT_MASK; + adreno_regwrite_isr(device, REG_CP_INT_ACK, status); + + if (status & (CP_INT_CNTL__IB1_INT_MASK | CP_INT_CNTL__RB_INT_MASK)) { + KGSL_CMD_WARN(rb->device, "ringbuffer ib1/rb interrupt\n"); + wake_up_interruptible_all(&device->wait_queue); + atomic_notifier_call_chain(&(device->ts_notifier_list), + device->id, + NULL); + } +} + +static void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb) +{ + BUG_ON(rb->wptr == 0); + + /*synchronize memory before informing the hardware of the + *new commands. + */ + mb(); + + adreno_regwrite(rb->device, REG_CP_RB_WPTR, rb->wptr); + + rb->flags |= KGSL_FLAGS_ACTIVE; +} + +static int +adreno_ringbuffer_waitspace(struct adreno_ringbuffer *rb, unsigned int numcmds, + int wptr_ahead) +{ + int nopcount; + unsigned int freecmds; + unsigned int *cmds; + uint cmds_gpu; + + /* if wptr ahead, fill the remaining with NOPs */ + if (wptr_ahead) { + /* -1 for header */ + nopcount = rb->sizedwords - rb->wptr - 1; + + cmds = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr; + cmds_gpu = rb->buffer_desc.gpuaddr + sizeof(uint)*rb->wptr; + + GSL_RB_WRITE(cmds, cmds_gpu, pm4_nop_packet(nopcount)); + + /* Make sure that rptr is not 0 before submitting + * commands at the end of ringbuffer. We do not + * want the rptr and wptr to become equal when + * the ringbuffer is not empty */ + do { + GSL_RB_GET_READPTR(rb, &rb->rptr); + } while (!rb->rptr); + + rb->wptr++; + + adreno_ringbuffer_submit(rb); + + rb->wptr = 0; + } + + /* wait for space in ringbuffer */ + do { + GSL_RB_GET_READPTR(rb, &rb->rptr); + + freecmds = rb->rptr - rb->wptr; + + } while ((freecmds != 0) && (freecmds <= numcmds)); + + return 0; +} + + +static unsigned int *adreno_ringbuffer_allocspace(struct adreno_ringbuffer *rb, + unsigned int numcmds) +{ + unsigned int *ptr = NULL; + int status = 0; + + BUG_ON(numcmds >= rb->sizedwords); + + GSL_RB_GET_READPTR(rb, &rb->rptr); + /* check for available space */ + if (rb->wptr >= rb->rptr) { + /* wptr ahead or equal to rptr */ + /* reserve dwords for nop packet */ + if ((rb->wptr + numcmds) > (rb->sizedwords - + GSL_RB_NOP_SIZEDWORDS)) + status = adreno_ringbuffer_waitspace(rb, numcmds, 1); + } else { + /* wptr behind rptr */ + if ((rb->wptr + numcmds) >= rb->rptr) + status = adreno_ringbuffer_waitspace(rb, numcmds, 0); + /* check for remaining space */ + /* reserve dwords for nop packet */ + if ((rb->wptr + numcmds) > (rb->sizedwords - + GSL_RB_NOP_SIZEDWORDS)) + status = adreno_ringbuffer_waitspace(rb, numcmds, 1); + } + + if (status == 0) { + ptr = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr; + rb->wptr += numcmds; + } + + return ptr; +} + +static int _load_firmware(struct kgsl_device *device, const char *fwfile, + void **data, int *len) +{ + const struct firmware *fw = NULL; + int ret; + + ret = request_firmware(&fw, fwfile, device->dev); + + if (ret) { + KGSL_DRV_ERR(device, "request_firmware(%s) failed: %d\n", + fwfile, ret); + return ret; + } + + *data = kmalloc(fw->size, GFP_KERNEL); + + if (*data) { + memcpy(*data, fw->data, fw->size); + *len = fw->size; + } else + KGSL_MEM_ERR(device, "kmalloc(%d) failed\n", fw->size); + + release_firmware(fw); + return (*data != NULL) ? 0 : -ENOMEM; +} + +static int adreno_ringbuffer_load_pm4_ucode(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + const char *fwfile; + int i, ret = 0; + + if (device->chip_id == KGSL_CHIPID_LEIA_REV470) + fwfile = A220_PM4_470_FW; + else + fwfile = A200_PM4_FW; + + if (adreno_dev->pm4_fw == NULL) { + int len; + unsigned int *ptr; + + ret = _load_firmware(device, fwfile, (void *) &ptr, &len); + if (ret) + goto err; + + /* PM4 size is 3 dword aligned plus 1 dword of version */ + if (len % ((sizeof(uint32_t) * 3)) != sizeof(uint32_t)) { + KGSL_DRV_ERR(device, "Bad firmware size: %d\n", len); + ret = -EINVAL; + goto err; + } + + adreno_dev->pm4_fw_size = len / sizeof(uint32_t); + adreno_dev->pm4_fw = ptr; + } + + KGSL_DRV_INFO(device, "loading pm4 ucode version: %d\n", + adreno_dev->pm4_fw[0]); + + adreno_regwrite(device, REG_CP_DEBUG, 0x02000000); + adreno_regwrite(device, REG_CP_ME_RAM_WADDR, 0); + for (i = 1; i < adreno_dev->pm4_fw_size; i++) + adreno_regwrite(device, REG_CP_ME_RAM_DATA, + adreno_dev->pm4_fw[i]); +err: + return ret; +} + +static int adreno_ringbuffer_load_pfp_ucode(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + const char *fwfile; + int i, ret = 0; + + if (device->chip_id == KGSL_CHIPID_LEIA_REV470) + fwfile = A220_PFP_470_FW; + else + fwfile = A200_PFP_FW; + + if (adreno_dev->pfp_fw == NULL) { + int len; + unsigned int *ptr; + + ret = _load_firmware(device, fwfile, (void *) &ptr, &len); + if (ret) + goto err; + + /* PFP size shold be dword aligned */ + if (len % sizeof(uint32_t) != 0) { + KGSL_DRV_ERR(device, "Bad firmware size: %d\n", len); + ret = -EINVAL; + goto err; + } + + adreno_dev->pfp_fw_size = len / sizeof(uint32_t); + adreno_dev->pfp_fw = ptr; + } + + KGSL_DRV_INFO(device, "loading pfp ucode version: %d\n", + adreno_dev->pfp_fw[0]); + + adreno_regwrite(device, REG_CP_PFP_UCODE_ADDR, 0); + for (i = 1; i < adreno_dev->pfp_fw_size; i++) + adreno_regwrite(device, REG_CP_PFP_UCODE_DATA, + adreno_dev->pfp_fw[i]); +err: + return ret; +} + +int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, unsigned int init_ram) +{ + int status; + /*cp_rb_cntl_u cp_rb_cntl; */ + union reg_cp_rb_cntl cp_rb_cntl; + unsigned int *cmds, rb_cntl; + struct kgsl_device *device = rb->device; + uint cmds_gpu; + + if (rb->flags & KGSL_FLAGS_STARTED) + return 0; + + if (init_ram) { + rb->timestamp = 0; + GSL_RB_INIT_TIMESTAMP(rb); + } + + kgsl_sharedmem_set(&rb->memptrs_desc, 0, 0, + sizeof(struct kgsl_rbmemptrs)); + + kgsl_sharedmem_set(&rb->buffer_desc, 0, 0xAA, + (rb->sizedwords << 2)); + + adreno_regwrite(device, REG_CP_RB_WPTR_BASE, + (rb->memptrs_desc.gpuaddr + + GSL_RB_MEMPTRS_WPTRPOLL_OFFSET)); + + /* setup WPTR delay */ + adreno_regwrite(device, REG_CP_RB_WPTR_DELAY, 0 /*0x70000010 */); + + /*setup REG_CP_RB_CNTL */ + adreno_regread(device, REG_CP_RB_CNTL, &rb_cntl); + cp_rb_cntl.val = rb_cntl; + /* size of ringbuffer */ + cp_rb_cntl.f.rb_bufsz = + adreno_ringbuffer_sizelog2quadwords(rb->sizedwords); + /* quadwords to read before updating mem RPTR */ + cp_rb_cntl.f.rb_blksz = rb->blksizequadwords; + cp_rb_cntl.f.rb_poll_en = GSL_RB_CNTL_POLL_EN; /* WPTR polling */ + /* mem RPTR writebacks */ + cp_rb_cntl.f.rb_no_update = GSL_RB_CNTL_NO_UPDATE; + + adreno_regwrite(device, REG_CP_RB_CNTL, cp_rb_cntl.val); + + adreno_regwrite(device, REG_CP_RB_BASE, rb->buffer_desc.gpuaddr); + + adreno_regwrite(device, REG_CP_RB_RPTR_ADDR, + rb->memptrs_desc.gpuaddr + + GSL_RB_MEMPTRS_RPTR_OFFSET); + + /* explicitly clear all cp interrupts */ + adreno_regwrite(device, REG_CP_INT_ACK, 0xFFFFFFFF); + + /* setup scratch/timestamp */ + adreno_regwrite(device, REG_SCRATCH_ADDR, + device->memstore.gpuaddr + + KGSL_DEVICE_MEMSTORE_OFFSET(soptimestamp)); + + adreno_regwrite(device, REG_SCRATCH_UMSK, + GSL_RB_MEMPTRS_SCRATCH_MASK); + + /* load the CP ucode */ + + status = adreno_ringbuffer_load_pm4_ucode(device); + if (status != 0) + return status; + + /* load the prefetch parser ucode */ + status = adreno_ringbuffer_load_pfp_ucode(device); + if (status != 0) + return status; + + adreno_regwrite(device, REG_CP_QUEUE_THRESHOLDS, 0x000C0804); + + rb->rptr = 0; + rb->wptr = 0; + + /* clear ME_HALT to start micro engine */ + adreno_regwrite(device, REG_CP_ME_CNTL, 0); + + /* ME_INIT */ + cmds = adreno_ringbuffer_allocspace(rb, 19); + cmds_gpu = rb->buffer_desc.gpuaddr + sizeof(uint)*(rb->wptr-19); + + GSL_RB_WRITE(cmds, cmds_gpu, PM4_HDR_ME_INIT); + /* All fields present (bits 9:0) */ + GSL_RB_WRITE(cmds, cmds_gpu, 0x000003ff); + /* Disable/Enable Real-Time Stream processing (present but ignored) */ + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); + /* Enable (2D <-> 3D) implicit synchronization (present but ignored) */ + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); + + GSL_RB_WRITE(cmds, cmds_gpu, + GSL_HAL_SUBBLOCK_OFFSET(REG_RB_SURFACE_INFO)); + GSL_RB_WRITE(cmds, cmds_gpu, + GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SC_WINDOW_OFFSET)); + GSL_RB_WRITE(cmds, cmds_gpu, + GSL_HAL_SUBBLOCK_OFFSET(REG_VGT_MAX_VTX_INDX)); + GSL_RB_WRITE(cmds, cmds_gpu, + GSL_HAL_SUBBLOCK_OFFSET(REG_SQ_PROGRAM_CNTL)); + GSL_RB_WRITE(cmds, cmds_gpu, + GSL_HAL_SUBBLOCK_OFFSET(REG_RB_DEPTHCONTROL)); + GSL_RB_WRITE(cmds, cmds_gpu, + GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SU_POINT_SIZE)); + GSL_RB_WRITE(cmds, cmds_gpu, + GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SC_LINE_CNTL)); + GSL_RB_WRITE(cmds, cmds_gpu, + GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SU_POLY_OFFSET_FRONT_SCALE)); + + /* Vertex and Pixel Shader Start Addresses in instructions + * (3 DWORDS per instruction) */ + GSL_RB_WRITE(cmds, cmds_gpu, 0x80000180); + /* Maximum Contexts */ + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000001); + /* Write Confirm Interval and The CP will wait the + * wait_interval * 16 clocks between polling */ + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); + + /* NQ and External Memory Swap */ + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); + /* Protected mode error checking */ + GSL_RB_WRITE(cmds, cmds_gpu, GSL_RB_PROTECTED_MODE_CONTROL); + /* Disable header dumping and Header dump address */ + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); + /* Header dump size */ + GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); + + adreno_ringbuffer_submit(rb); + + /* idle device to validate ME INIT */ + status = adreno_idle(device, KGSL_TIMEOUT_DEFAULT); + + adreno_regwrite(rb->device, REG_CP_INT_CNTL, GSL_CP_INT_MASK); + if (status == 0) + rb->flags |= KGSL_FLAGS_STARTED; + + return status; +} + +int adreno_ringbuffer_stop(struct adreno_ringbuffer *rb) +{ + if (rb->flags & KGSL_FLAGS_STARTED) { + adreno_regwrite(rb->device, REG_CP_INT_CNTL, 0); + + /* ME_HALT */ + adreno_regwrite(rb->device, REG_CP_ME_CNTL, 0x10000000); + + rb->flags &= ~KGSL_FLAGS_STARTED; + } + + return 0; +} + +int adreno_ringbuffer_init(struct kgsl_device *device) +{ + int status; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; + + rb->device = device; + rb->sizedwords = (2 << kgsl_cfg_rb_sizelog2quadwords); + rb->blksizequadwords = kgsl_cfg_rb_blksizequadwords; + + /* allocate memory for ringbuffer */ + status = kgsl_allocate_contig(&rb->buffer_desc, (rb->sizedwords << 2)); + + if (status != 0) { + adreno_ringbuffer_close(rb); + return status; + } + + /* allocate memory for polling and timestamps */ + /* This really can be at 4 byte alignment boundry but for using MMU + * we need to make it at page boundary */ + status = kgsl_allocate_contig(&rb->memptrs_desc, + sizeof(struct kgsl_rbmemptrs)); + + if (status != 0) { + adreno_ringbuffer_close(rb); + return status; + } + + /* overlay structure on memptrs memory */ + rb->memptrs = (struct kgsl_rbmemptrs *) rb->memptrs_desc.hostptr; + + return 0; +} + +int adreno_ringbuffer_close(struct adreno_ringbuffer *rb) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(rb->device); + + if (rb->buffer_desc.hostptr) + kgsl_sharedmem_free(&rb->buffer_desc); + + if (rb->memptrs_desc.hostptr) + kgsl_sharedmem_free(&rb->memptrs_desc); + + if (adreno_dev->pfp_fw != NULL) + kfree(adreno_dev->pfp_fw); + if (adreno_dev->pm4_fw != NULL) + kfree(adreno_dev->pm4_fw); + adreno_dev->pfp_fw = NULL; + adreno_dev->pm4_fw = NULL; + + memset(rb, 0, sizeof(struct adreno_ringbuffer)); + + return 0; +} + +static uint32_t +adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, + unsigned int flags, unsigned int *cmds, + int sizedwords) +{ + unsigned int *ringcmds; + unsigned int timestamp; + unsigned int total_sizedwords = sizedwords + 6; + unsigned int i; + unsigned int rcmd_gpu; + + /* reserve space to temporarily turn off protected mode + * error checking if needed + */ + total_sizedwords += flags & KGSL_CMD_FLAGS_PMODE ? 4 : 0; + total_sizedwords += !(flags & KGSL_CMD_FLAGS_NO_TS_CMP) ? 7 : 0; + total_sizedwords += !(flags & KGSL_CMD_FLAGS_NOT_KERNEL_CMD) ? 2 : 0; + + ringcmds = adreno_ringbuffer_allocspace(rb, total_sizedwords); + rcmd_gpu = rb->buffer_desc.gpuaddr + + sizeof(uint)*(rb->wptr-total_sizedwords); + + if (!(flags & KGSL_CMD_FLAGS_NOT_KERNEL_CMD)) { + GSL_RB_WRITE(ringcmds, rcmd_gpu, pm4_nop_packet(1)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, KGSL_CMD_IDENTIFIER); + } + if (flags & KGSL_CMD_FLAGS_PMODE) { + /* disable protected mode error checking */ + GSL_RB_WRITE(ringcmds, rcmd_gpu, + pm4_type3_packet(PM4_SET_PROTECTED_MODE, 1)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, 0); + } + + for (i = 0; i < sizedwords; i++) { + GSL_RB_WRITE(ringcmds, rcmd_gpu, *cmds); + cmds++; + } + + if (flags & KGSL_CMD_FLAGS_PMODE) { + /* re-enable protected mode error checking */ + GSL_RB_WRITE(ringcmds, rcmd_gpu, + pm4_type3_packet(PM4_SET_PROTECTED_MODE, 1)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, 1); + } + + rb->timestamp++; + timestamp = rb->timestamp; + + /* start-of-pipeline and end-of-pipeline timestamps */ + GSL_RB_WRITE(ringcmds, rcmd_gpu, pm4_type0_packet(REG_CP_TIMESTAMP, 1)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, rb->timestamp); + GSL_RB_WRITE(ringcmds, rcmd_gpu, pm4_type3_packet(PM4_EVENT_WRITE, 3)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, CACHE_FLUSH_TS); + GSL_RB_WRITE(ringcmds, rcmd_gpu, + (rb->device->memstore.gpuaddr + + KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp))); + GSL_RB_WRITE(ringcmds, rcmd_gpu, rb->timestamp); + + if (!(flags & KGSL_CMD_FLAGS_NO_TS_CMP)) { + /* Conditional execution based on memory values */ + GSL_RB_WRITE(ringcmds, rcmd_gpu, + pm4_type3_packet(PM4_COND_EXEC, 4)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, (rb->device->memstore.gpuaddr + + KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable)) >> 2); + GSL_RB_WRITE(ringcmds, rcmd_gpu, (rb->device->memstore.gpuaddr + + KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts)) >> 2); + GSL_RB_WRITE(ringcmds, rcmd_gpu, rb->timestamp); + /* # of conditional command DWORDs */ + GSL_RB_WRITE(ringcmds, rcmd_gpu, 2); + GSL_RB_WRITE(ringcmds, rcmd_gpu, + pm4_type3_packet(PM4_INTERRUPT, 1)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, CP_INT_CNTL__RB_INT_MASK); + } + + adreno_ringbuffer_submit(rb); + + /* return timestamp of issued coREG_ands */ + return timestamp; +} + +void +adreno_ringbuffer_issuecmds(struct kgsl_device *device, + unsigned int flags, + unsigned int *cmds, + int sizedwords) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; + + if (device->state & KGSL_STATE_HUNG) + return; + adreno_ringbuffer_addcmds(rb, flags, cmds, sizedwords); +} + +int +adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv, + struct kgsl_context *context, + struct kgsl_ibdesc *ibdesc, + unsigned int numibs, + uint32_t *timestamp, + unsigned int flags) +{ + struct kgsl_device *device = dev_priv->device; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + unsigned int *link; + unsigned int *cmds; + unsigned int i; + struct adreno_context *drawctxt = context->devctxt; + + if (device->state & KGSL_STATE_HUNG) + return -EBUSY; + if (!(adreno_dev->ringbuffer.flags & KGSL_FLAGS_STARTED) || + context == NULL) + return -EINVAL; + + BUG_ON(ibdesc == 0); + BUG_ON(numibs == 0); + + if (drawctxt->flags & CTXT_FLAGS_GPU_HANG) { + KGSL_CTXT_WARN(device, "Context %p caused a gpu hang.." + " will not accept commands for this context\n", + drawctxt); + return -EDEADLK; + } + link = kzalloc(sizeof(unsigned int) * numibs * 3, GFP_KERNEL); + cmds = link; + if (!link) { + KGSL_MEM_ERR(device, "Failed to allocate memory for for command" + " submission, size %x\n", numibs * 3); + return -ENOMEM; + } + for (i = 0; i < numibs; i++) { + (void)kgsl_cffdump_parse_ibs(dev_priv, NULL, + ibdesc[i].gpuaddr, ibdesc[i].sizedwords, false); + + *cmds++ = PM4_HDR_INDIRECT_BUFFER_PFD; + *cmds++ = ibdesc[i].gpuaddr; + *cmds++ = ibdesc[i].sizedwords; + } + + kgsl_setstate(device, + kgsl_pt_get_flags(device->mmu.hwpagetable, + device->id)); + + adreno_drawctxt_switch(adreno_dev, drawctxt, flags); + + *timestamp = adreno_ringbuffer_addcmds(&adreno_dev->ringbuffer, + KGSL_CMD_FLAGS_NOT_KERNEL_CMD, + &link[0], (cmds - link)); + + KGSL_CMD_INFO(device, "ctxt %d g %08x numibs %d ts %d\n", + context->id, (unsigned int)ibdesc, numibs, *timestamp); + + kfree(link); + +#ifdef CONFIG_MSM_KGSL_CFF_DUMP + /* + * insert wait for idle after every IB1 + * this is conservative but works reliably and is ok + * even for performance simulations + */ + adreno_idle(device, KGSL_TIMEOUT_DEFAULT); +#endif + + return 0; +} + +int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, + unsigned int *temp_rb_buffer, + int *rb_size) +{ + struct kgsl_device *device = rb->device; + unsigned int rb_rptr; + unsigned int retired_timestamp; + unsigned int temp_idx = 0; + unsigned int value; + unsigned int val1; + unsigned int val2; + unsigned int val3; + unsigned int copy_rb_contents = 0; + unsigned int cur_context; + unsigned int j; + + GSL_RB_GET_READPTR(rb, &rb->rptr); + + retired_timestamp = device->ftbl.device_readtimestamp( + device, KGSL_TIMESTAMP_RETIRED); + rmb(); + KGSL_DRV_ERR(device, "GPU successfully executed till ts: %x\n", + retired_timestamp); + rb_rptr = (rb->rptr - 4) * sizeof(unsigned int); + /* Read the rb contents going backwards to locate end of last + * sucessfully executed command */ + while ((rb_rptr / sizeof(unsigned int)) != rb->wptr) { + kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); + rmb(); + if (value == retired_timestamp) { + rb_rptr += sizeof(unsigned int); + kgsl_sharedmem_readl(&rb->buffer_desc, &val1, rb_rptr); + rb_rptr += sizeof(unsigned int); + kgsl_sharedmem_readl(&rb->buffer_desc, &val2, rb_rptr); + rb_rptr += sizeof(unsigned int); + kgsl_sharedmem_readl(&rb->buffer_desc, &val3, rb_rptr); + rmb(); + /* match the pattern found at the end of a command */ + if ((val1 == 2 && + val2 == pm4_type3_packet(PM4_INTERRUPT, 1) + && val3 == CP_INT_CNTL__RB_INT_MASK) || + (val1 == pm4_type3_packet(PM4_EVENT_WRITE, 3) + && val2 == CACHE_FLUSH_TS && + val3 == (rb->device->memstore.gpuaddr + + KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp)))) { + rb_rptr += sizeof(unsigned int); + KGSL_DRV_ERR(device, + "Found end of last executed " + "command at offset: %x\n", + rb_rptr / sizeof(unsigned int)); + break; + } else { + rb_rptr -= (3 * sizeof(unsigned int)); + } + } + + rb_rptr -= sizeof(unsigned int); + if (rb_rptr < 0) + rb_rptr = rb->buffer_desc.size - sizeof(unsigned int); + } + + if ((rb_rptr / sizeof(unsigned int)) == rb->wptr) { + KGSL_DRV_ERR(device, + "GPU recovery from hang not possible because last" + " successful timestamp is overwritten\n"); + return -EINVAL; + } + /* rb_rptr is now pointing to the first dword of the command following + * the last sucessfully executed command sequence. Assumption is that + * GPU is hung in the command sequence pointed by rb_rptr */ + /* make sure the GPU is not hung in a command submitted by kgsl + * itself */ + kgsl_sharedmem_readl(&rb->buffer_desc, &val1, rb_rptr); + kgsl_sharedmem_readl(&rb->buffer_desc, &val2, + rb_rptr + sizeof(unsigned int)); + rmb(); + if (val1 == pm4_nop_packet(1) && val2 == KGSL_CMD_IDENTIFIER) { + KGSL_DRV_ERR(device, + "GPU recovery from hang not possible because " + "of hang in kgsl command\n"); + return -EINVAL; + } + + /* current_context is the context that is presently active in the + * GPU, i.e the context in which the hang is caused */ + kgsl_sharedmem_readl(&device->memstore, &cur_context, + KGSL_DEVICE_MEMSTORE_OFFSET(current_context)); + while ((rb_rptr / sizeof(unsigned int)) != rb->wptr) { + kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); + rb_rptr = (rb_rptr + sizeof(unsigned int)) % + rb->buffer_desc.size; + rmb(); + /* check for context switch indicator */ + if (value == KGSL_CONTEXT_TO_MEM_IDENTIFIER) { + kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); + rb_rptr = (rb_rptr + sizeof(unsigned int)) % + rb->buffer_desc.size; + rmb(); + BUG_ON(value != pm4_type3_packet(PM4_MEM_WRITE, 2)); + kgsl_sharedmem_readl(&rb->buffer_desc, &val1, rb_rptr); + rb_rptr = (rb_rptr + sizeof(unsigned int)) % + rb->buffer_desc.size; + rmb(); + BUG_ON(val1 != (device->memstore.gpuaddr + + KGSL_DEVICE_MEMSTORE_OFFSET(current_context))); + kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); + rb_rptr = (rb_rptr + sizeof(unsigned int)) % + rb->buffer_desc.size; + rmb(); + BUG_ON((copy_rb_contents == 0) && + (value == cur_context)); + /* if context switches to a context that did not cause + * hang then start saving the rb contents as those + * commands can be executed */ + if (value != cur_context) { + copy_rb_contents = 1; + temp_rb_buffer[temp_idx++] = pm4_nop_packet(1); + temp_rb_buffer[temp_idx++] = + KGSL_CMD_IDENTIFIER; + temp_rb_buffer[temp_idx++] = pm4_nop_packet(1); + temp_rb_buffer[temp_idx++] = + KGSL_CONTEXT_TO_MEM_IDENTIFIER; + temp_rb_buffer[temp_idx++] = + pm4_type3_packet(PM4_MEM_WRITE, 2); + temp_rb_buffer[temp_idx++] = val1; + temp_rb_buffer[temp_idx++] = value; + } else { + /* if temp_idx is not 0 then we do not need to + * copy extra dwords indicating a kernel cmd */ + if (temp_idx) + temp_idx -= 3; + copy_rb_contents = 0; + } + } else if (copy_rb_contents) + temp_rb_buffer[temp_idx++] = value; + } + + *rb_size = temp_idx; + KGSL_DRV_ERR(device, "Extracted rb contents, size: %x\n", *rb_size); + for (temp_idx = 0; temp_idx < *rb_size;) { + char str[80]; + int idx = 0; + if ((temp_idx + 8) <= *rb_size) + j = 8; + else + j = *rb_size - temp_idx; + for (; j != 0; j--) + idx += scnprintf(str + idx, 80 - idx, + "%8.8X ", temp_rb_buffer[temp_idx++]); + printk(KERN_ALERT "%s", str); + } + return 0; +} + +void +adreno_ringbuffer_restore(struct adreno_ringbuffer *rb, unsigned int *rb_buff, + int num_rb_contents) +{ + int i; + unsigned int *ringcmds; + unsigned int rcmd_gpu; + + if (!num_rb_contents) + return; + + if (num_rb_contents > (rb->buffer_desc.size - rb->wptr)) { + adreno_regwrite(rb->device, REG_CP_RB_RPTR, 0); + rb->rptr = 0; + BUG_ON(num_rb_contents > rb->buffer_desc.size); + } + ringcmds = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr; + rcmd_gpu = rb->buffer_desc.gpuaddr + sizeof(unsigned int) * rb->wptr; + for (i = 0; i < num_rb_contents; i++) + GSL_RB_WRITE(ringcmds, rcmd_gpu, rb_buff[i]); + rb->wptr += num_rb_contents; + adreno_ringbuffer_submit(rb); +} diff --git a/drivers/gpu/msm/adreno_ringbuffer.h b/drivers/gpu/msm/adreno_ringbuffer.h new file mode 100644 index 0000000000000..d1919e65de444 --- /dev/null +++ b/drivers/gpu/msm/adreno_ringbuffer.h @@ -0,0 +1,184 @@ +/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __ADRENO_RINGBUFFER_H +#define __ADRENO_RINGBUFFER_H + +#define GSL_RB_USE_MEM_RPTR +#define GSL_RB_USE_MEM_TIMESTAMP +#define GSL_DEVICE_SHADOW_MEMSTORE_TO_USER + +/* ringbuffer sizes log2quadword */ +#define GSL_RB_SIZE_8 0 +#define GSL_RB_SIZE_16 1 +#define GSL_RB_SIZE_32 2 +#define GSL_RB_SIZE_64 3 +#define GSL_RB_SIZE_128 4 +#define GSL_RB_SIZE_256 5 +#define GSL_RB_SIZE_512 6 +#define GSL_RB_SIZE_1K 7 +#define GSL_RB_SIZE_2K 8 +#define GSL_RB_SIZE_4K 9 +#define GSL_RB_SIZE_8K 10 +#define GSL_RB_SIZE_16K 11 +#define GSL_RB_SIZE_32K 12 +#define GSL_RB_SIZE_64K 13 +#define GSL_RB_SIZE_128K 14 +#define GSL_RB_SIZE_256K 15 +#define GSL_RB_SIZE_512K 16 +#define GSL_RB_SIZE_1M 17 +#define GSL_RB_SIZE_2M 18 +#define GSL_RB_SIZE_4M 19 + +/* Adreno ringbuffer config*/ +static const unsigned int kgsl_cfg_rb_sizelog2quadwords = GSL_RB_SIZE_32K; +static const unsigned int kgsl_cfg_rb_blksizequadwords = GSL_RB_SIZE_16; + +/* CP timestamp register */ +#define REG_CP_TIMESTAMP REG_SCRATCH_REG0 + + +struct kgsl_device; +struct kgsl_device_private; + +#define GSL_RB_MEMPTRS_SCRATCH_COUNT 8 +struct kgsl_rbmemptrs { + int rptr; + int wptr_poll; +}; + +#define GSL_RB_MEMPTRS_RPTR_OFFSET \ + (offsetof(struct kgsl_rbmemptrs, rptr)) + +#define GSL_RB_MEMPTRS_WPTRPOLL_OFFSET \ + (offsetof(struct kgsl_rbmemptrs, wptr_poll)) + +struct adreno_ringbuffer { + struct kgsl_device *device; + uint32_t flags; + + struct kgsl_memdesc buffer_desc; + + struct kgsl_memdesc memptrs_desc; + struct kgsl_rbmemptrs *memptrs; + + /*ringbuffer size */ + unsigned int sizedwords; + unsigned int blksizequadwords; + + unsigned int wptr; /* write pointer offset in dwords from baseaddr */ + unsigned int rptr; /* read pointer offset in dwords from baseaddr */ + uint32_t timestamp; +}; + +/* dword base address of the GFX decode space */ +#define GSL_HAL_SUBBLOCK_OFFSET(reg) ((unsigned int)((reg) - (0x2000))) + +#define GSL_RB_WRITE(ring, gpuaddr, data) \ + do { \ + writel_relaxed(data, ring); \ + wmb(); \ + kgsl_cffdump_setmem(gpuaddr, data, 4); \ + ring++; \ + gpuaddr += sizeof(uint); \ + } while (0) + +/* timestamp */ +#ifdef GSL_DEVICE_SHADOW_MEMSTORE_TO_USER +#define GSL_RB_USE_MEM_TIMESTAMP +#endif /* GSL_DEVICE_SHADOW_MEMSTORE_TO_USER */ + +#ifdef GSL_RB_USE_MEM_TIMESTAMP +/* enable timestamp (...scratch0) memory shadowing */ +#define GSL_RB_MEMPTRS_SCRATCH_MASK 0x1 +#define GSL_RB_INIT_TIMESTAMP(rb) + +#else +#define GSL_RB_MEMPTRS_SCRATCH_MASK 0x0 +#define GSL_RB_INIT_TIMESTAMP(rb) \ + adreno_regwrite((rb)->device->id, REG_CP_TIMESTAMP, 0) + +#endif /* GSL_RB_USE_MEMTIMESTAMP */ + +/* mem rptr */ +#ifdef GSL_RB_USE_MEM_RPTR +#define GSL_RB_CNTL_NO_UPDATE 0x0 /* enable */ +#define GSL_RB_GET_READPTR(rb, data) \ + do { \ + *(data) = readl_relaxed(&(rb)->memptrs->rptr); \ + } while (0) +#else +#define GSL_RB_CNTL_NO_UPDATE 0x1 /* disable */ +#define GSL_RB_GET_READPTR(rb, data) \ + do { \ + adreno_regread((rb)->device->id, REG_CP_RB_RPTR, (data)); \ + } while (0) +#endif /* GSL_RB_USE_MEMRPTR */ + +#define GSL_RB_CNTL_POLL_EN 0x0 /* disable */ + +int adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv, + struct kgsl_context *context, + struct kgsl_ibdesc *ibdesc, + unsigned int numibs, + uint32_t *timestamp, + unsigned int flags); + +int adreno_ringbuffer_init(struct kgsl_device *device); + +int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, + unsigned int init_ram); + +int adreno_ringbuffer_stop(struct adreno_ringbuffer *rb); + +int adreno_ringbuffer_close(struct adreno_ringbuffer *rb); + +void adreno_ringbuffer_issuecmds(struct kgsl_device *device, + unsigned int flags, + unsigned int *cmdaddr, + int sizedwords); + +void kgsl_cp_intrcallback(struct kgsl_device *device); + +int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, + unsigned int *temp_rb_buffer, + int *rb_size); + +void +adreno_ringbuffer_restore(struct adreno_ringbuffer *rb, unsigned int *rb_buff, + int num_rb_contents); + +static inline int adreno_ringbuffer_count(struct adreno_ringbuffer *rb, + unsigned int rptr) +{ + if (rb->wptr >= rptr) + return rb->wptr - rptr; + return rb->wptr + rb->sizedwords - rptr; +} + +#endif /* __ADRENO_RINGBUFFER_H */ diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c new file mode 100644 index 0000000000000..cdfc4e58eed80 --- /dev/null +++ b/drivers/gpu/msm/kgsl.c @@ -0,0 +1,2081 @@ +/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "kgsl.h" +#include "kgsl_debugfs.h" +#include "kgsl_cffdump.h" + +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "kgsl." + +static int kgsl_pagetable_count = KGSL_PAGETABLE_COUNT; +module_param_named(ptcount, kgsl_pagetable_count, int, 0); +MODULE_PARM_DESC(kgsl_pagetable_count, +"Minimum number of pagetables for KGSL to allocate at initialization time"); + +static inline struct kgsl_mem_entry * +kgsl_mem_entry_create(void) +{ + struct kgsl_mem_entry *entry = kzalloc(sizeof(*entry), GFP_KERNEL); + + if (!entry) + KGSL_CORE_ERR("kzalloc(%d) failed\n", sizeof(*entry)); + else + kref_init(&entry->refcount); + + return entry; +} + +void +kgsl_mem_entry_destroy(struct kref *kref) +{ + struct kgsl_mem_entry *entry = container_of(kref, + struct kgsl_mem_entry, + refcount); + size_t size = entry->memdesc.size; + + kgsl_sharedmem_free(&entry->memdesc); + + if (entry->memtype == KGSL_VMALLOC_MEMORY) + entry->priv->stats.vmalloc -= size; + else { + if (entry->file_ptr) + fput(entry->file_ptr); + + entry->priv->stats.exmem -= size; + } + + kfree(entry); +} +EXPORT_SYMBOL(kgsl_mem_entry_destroy); + +static +void kgsl_mem_entry_attach_process(struct kgsl_mem_entry *entry, + struct kgsl_process_private *process) +{ + spin_lock(&process->mem_lock); + list_add(&entry->list, &process->mem_list); + spin_unlock(&process->mem_lock); + + entry->priv = process; +} + +/* Allocate a new context id */ + +static struct kgsl_context * +kgsl_create_context(struct kgsl_device_private *dev_priv) +{ + struct kgsl_context *context; + int ret, id; + + context = kzalloc(sizeof(*context), GFP_KERNEL); + + if (context == NULL) + return NULL; + + while (1) { + if (idr_pre_get(&dev_priv->device->context_idr, + GFP_KERNEL) == 0) { + kfree(context); + return NULL; + } + + ret = idr_get_new(&dev_priv->device->context_idr, + context, &id); + + if (ret != -EAGAIN) + break; + } + + if (ret) { + kfree(context); + return NULL; + } + + context->id = id; + context->dev_priv = dev_priv; + + return context; +} + +static void +kgsl_destroy_context(struct kgsl_device_private *dev_priv, + struct kgsl_context *context) +{ + int id; + + if (context == NULL) + return; + + /* Fire a bug if the devctxt hasn't been freed */ + BUG_ON(context->devctxt); + + id = context->id; + kfree(context); + + idr_remove(&dev_priv->device->context_idr, id); +} + +/* to be called when a process is destroyed, this walks the memqueue and + * frees any entryies that belong to the dying process + */ +static void kgsl_memqueue_cleanup(struct kgsl_device *device, + struct kgsl_process_private *private) +{ + struct kgsl_mem_entry *entry, *entry_tmp; + + if (!private) + return; + + BUG_ON(!mutex_is_locked(&device->mutex)); + + list_for_each_entry_safe(entry, entry_tmp, &device->memqueue, list) { + if (entry->priv == private) { + list_del(&entry->list); + kgsl_mem_entry_put(entry); + } + } +} + +static void kgsl_memqueue_freememontimestamp(struct kgsl_device *device, + struct kgsl_mem_entry *entry, + uint32_t timestamp, + enum kgsl_timestamp_type type) +{ + BUG_ON(!mutex_is_locked(&device->mutex)); + + entry->free_timestamp = timestamp; + + list_add_tail(&entry->list, &device->memqueue); +} + +static void kgsl_memqueue_drain(struct kgsl_device *device) +{ + struct kgsl_mem_entry *entry, *entry_tmp; + uint32_t ts_processed; + + BUG_ON(!mutex_is_locked(&device->mutex)); + + /* get current EOP timestamp */ + ts_processed = device->ftbl.device_readtimestamp( + device, + KGSL_TIMESTAMP_RETIRED); + + list_for_each_entry_safe(entry, entry_tmp, &device->memqueue, list) { + KGSL_MEM_INFO(device, + "ts_processed %d ts_free %d gpuaddr %x)\n", + ts_processed, entry->free_timestamp, + entry->memdesc.gpuaddr); + if (!timestamp_cmp(ts_processed, entry->free_timestamp)) + break; + + list_del(&entry->list); + kgsl_mem_entry_put(entry); + } +} + +static void kgsl_memqueue_drain_unlocked(struct kgsl_device *device) +{ + mutex_lock(&device->mutex); + kgsl_check_suspended(device); + kgsl_memqueue_drain(device); + mutex_unlock(&device->mutex); +} + +static void kgsl_check_idle_locked(struct kgsl_device *device) +{ + if (device->pwrctrl.nap_allowed == true && + device->state == KGSL_STATE_ACTIVE && + device->requested_state == KGSL_STATE_NONE) { + device->requested_state = KGSL_STATE_NAP; + if (kgsl_pwrctrl_sleep(device) != 0) + mod_timer(&device->idle_timer, + jiffies + + device->pwrctrl.interval_timeout); + } +} + +static void kgsl_check_idle(struct kgsl_device *device) +{ + mutex_lock(&device->mutex); + kgsl_check_idle_locked(device); + mutex_unlock(&device->mutex); +} + +struct kgsl_device *kgsl_get_device(int dev_idx) +{ + int i; + struct kgsl_device *ret = NULL; + + mutex_lock(&kgsl_driver.devlock); + + for (i = 0; i < KGSL_DEVICE_MAX; i++) { + if (kgsl_driver.devp[i] && kgsl_driver.devp[i]->id == dev_idx) { + ret = kgsl_driver.devp[i]; + break; + } + } + + mutex_unlock(&kgsl_driver.devlock); + return ret; +} +EXPORT_SYMBOL(kgsl_get_device); + +static struct kgsl_device *kgsl_get_minor(int minor) +{ + struct kgsl_device *ret = NULL; + + if (minor < 0 || minor >= KGSL_DEVICE_MAX) + return NULL; + + mutex_lock(&kgsl_driver.devlock); + ret = kgsl_driver.devp[minor]; + mutex_unlock(&kgsl_driver.devlock); + + return ret; +} + +int kgsl_register_ts_notifier(struct kgsl_device *device, + struct notifier_block *nb) +{ + BUG_ON(device == NULL); + return atomic_notifier_chain_register(&device->ts_notifier_list, + nb); +} +EXPORT_SYMBOL(kgsl_register_ts_notifier); + +int kgsl_unregister_ts_notifier(struct kgsl_device *device, + struct notifier_block *nb) +{ + BUG_ON(device == NULL); + return atomic_notifier_chain_unregister(&device->ts_notifier_list, + nb); +} +EXPORT_SYMBOL(kgsl_unregister_ts_notifier); + +int kgsl_check_timestamp(struct kgsl_device *device, unsigned int timestamp) +{ + unsigned int ts_processed; + BUG_ON(device->ftbl.device_readtimestamp == NULL); + + ts_processed = device->ftbl.device_readtimestamp( + device, KGSL_TIMESTAMP_RETIRED); + + return timestamp_cmp(ts_processed, timestamp); +} +EXPORT_SYMBOL(kgsl_check_timestamp); + +int kgsl_setstate(struct kgsl_device *device, uint32_t flags) +{ + int status = -ENXIO; + + if (flags && device->ftbl.device_setstate) { + status = device->ftbl.device_setstate(device, flags); + } else + status = 0; + + return status; +} +EXPORT_SYMBOL(kgsl_setstate); + +int kgsl_idle(struct kgsl_device *device, unsigned int timeout) +{ + int status = -ENXIO; + + if (device->ftbl.device_idle) + status = device->ftbl.device_idle(device, timeout); + + return status; +} +EXPORT_SYMBOL(kgsl_idle); + +static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state) +{ + int status = -EINVAL; + unsigned int nap_allowed_saved; + + if (!device) + return -EINVAL; + + KGSL_PWR_WARN(device, "suspend start\n"); + + mutex_lock(&device->mutex); + nap_allowed_saved = device->pwrctrl.nap_allowed; + device->pwrctrl.nap_allowed = false; + device->requested_state = KGSL_STATE_SUSPEND; + /* Make sure no user process is waiting for a timestamp * + * before supending */ + if (device->active_cnt != 0) { + mutex_unlock(&device->mutex); + wait_for_completion(&device->suspend_gate); + mutex_lock(&device->mutex); + } + /* Don't let the timer wake us during suspended sleep. */ + del_timer(&device->idle_timer); + switch (device->state) { + case KGSL_STATE_INIT: + break; + case KGSL_STATE_ACTIVE: + /* Wait for the device to become idle */ + device->ftbl.device_idle(device, KGSL_TIMEOUT_DEFAULT); + case KGSL_STATE_NAP: + case KGSL_STATE_SLEEP: + /* Get the completion ready to be waited upon. */ + INIT_COMPLETION(device->hwaccess_gate); + device->ftbl.device_suspend_context(device); + device->ftbl.device_stop(device); + device->state = KGSL_STATE_SUSPEND; + KGSL_PWR_WARN(device, "state -> SUSPEND, device %d\n", + device->id); + break; + default: + KGSL_PWR_ERR(device, "suspend fail, device %d\n", + device->id); + goto end; + } + device->requested_state = KGSL_STATE_NONE; + device->pwrctrl.nap_allowed = nap_allowed_saved; + status = 0; + +end: + mutex_unlock(&device->mutex); + KGSL_PWR_WARN(device, "suspend end\n"); + return status; +} + +static int kgsl_resume_device(struct kgsl_device *device) +{ + int status = -EINVAL; + + if (!device) + return -EINVAL; + + KGSL_PWR_WARN(device, "resume start\n"); + mutex_lock(&device->mutex); + if (device->state == KGSL_STATE_SUSPEND) { + device->requested_state = KGSL_STATE_ACTIVE; + status = device->ftbl.device_start(device, 0); + if (status == 0) { + device->state = KGSL_STATE_ACTIVE; + KGSL_PWR_WARN(device, + "state -> ACTIVE, device %d\n", + device->id); + } else { + KGSL_PWR_ERR(device, + "resume failed, device %d\n", + device->id); + device->state = KGSL_STATE_INIT; + goto end; + } + status = device->ftbl.device_resume_context(device); + complete_all(&device->hwaccess_gate); + } + device->requested_state = KGSL_STATE_NONE; + +end: + mutex_unlock(&device->mutex); + KGSL_PWR_WARN(device, "resume end\n"); + return status; +} + +static int kgsl_suspend(struct device *dev) +{ + + pm_message_t arg = {0}; + struct kgsl_device *device = dev_get_drvdata(dev); + return kgsl_suspend_device(device, arg); +} + +static int kgsl_resume(struct device *dev) +{ + struct kgsl_device *device = dev_get_drvdata(dev); + return kgsl_resume_device(device); +} + +static int kgsl_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int kgsl_runtime_resume(struct device *dev) +{ + return 0; +} + +const struct dev_pm_ops kgsl_pm_ops = { + .suspend = kgsl_suspend, + .resume = kgsl_resume, + .runtime_suspend = kgsl_runtime_suspend, + .runtime_resume = kgsl_runtime_resume, +}; +EXPORT_SYMBOL(kgsl_pm_ops); + +int kgsl_suspend_driver(struct platform_device *pdev, + pm_message_t state) +{ + struct kgsl_device *device = dev_get_drvdata(&pdev->dev); + return kgsl_suspend_device(device, state); +} +EXPORT_SYMBOL(kgsl_suspend_driver); + +int kgsl_resume_driver(struct platform_device *pdev) +{ + struct kgsl_device *device = dev_get_drvdata(&pdev->dev); + return kgsl_resume_device(device); +} +EXPORT_SYMBOL(kgsl_resume_driver); + +/* file operations */ +static struct kgsl_process_private * +kgsl_get_process_private(struct kgsl_device_private *cur_dev_priv) +{ + struct kgsl_process_private *private; + + mutex_lock(&kgsl_driver.process_mutex); + list_for_each_entry(private, &kgsl_driver.process_list, list) { + if (private->pid == task_tgid_nr(current)) { + private->refcnt++; + goto out; + } + } + + /* no existing process private found for this dev_priv, create one */ + private = kzalloc(sizeof(struct kgsl_process_private), GFP_KERNEL); + if (private == NULL) { + KGSL_DRV_ERR(cur_dev_priv->device, "kzalloc(%d) failed\n", + sizeof(struct kgsl_process_private)); + goto out; + } + + spin_lock_init(&private->mem_lock); + private->refcnt = 1; + private->pid = task_tgid_nr(current); + + INIT_LIST_HEAD(&private->mem_list); + +#ifdef CONFIG_MSM_KGSL_MMU + { + unsigned long pt_name; + +#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE + pt_name = task_tgid_nr(current); +#else + pt_name = KGSL_MMU_GLOBAL_PT; +#endif + private->pagetable = kgsl_mmu_getpagetable(pt_name); + if (private->pagetable == NULL) { + kfree(private); + private = NULL; + goto out; + } + } +#endif + + list_add(&private->list, &kgsl_driver.process_list); + + kgsl_process_init_sysfs(private); + +out: + mutex_unlock(&kgsl_driver.process_mutex); + return private; +} + +static void +kgsl_put_process_private(struct kgsl_device *device, + struct kgsl_process_private *private) +{ + struct kgsl_mem_entry *entry = NULL; + struct kgsl_mem_entry *entry_tmp = NULL; + + if (!private) + return; + + mutex_lock(&kgsl_driver.process_mutex); + + if (--private->refcnt) + goto unlock; + + KGSL_MEM_INFO(device, + "Memory usage: vmalloc (%d/%d) exmem (%d/%d)\n", + private->stats.vmalloc, private->stats.vmalloc_max, + private->stats.exmem, private->stats.exmem_max); + + kgsl_process_uninit_sysfs(private); + + list_del(&private->list); + + list_for_each_entry_safe(entry, entry_tmp, &private->mem_list, list) { + list_del(&entry->list); + kgsl_mem_entry_put(entry); + } + + kgsl_mmu_putpagetable(private->pagetable); + kfree(private); +unlock: + mutex_unlock(&kgsl_driver.process_mutex); +} + +static int kgsl_release(struct inode *inodep, struct file *filep) +{ + int result = 0; + struct kgsl_device_private *dev_priv = NULL; + struct kgsl_process_private *private = NULL; + struct kgsl_device *device; + struct kgsl_context *context; + int next = 0; + + device = kgsl_driver.devp[iminor(inodep)]; + BUG_ON(device == NULL); + + dev_priv = (struct kgsl_device_private *) filep->private_data; + BUG_ON(dev_priv == NULL); + BUG_ON(device != dev_priv->device); + /* private could be null if kgsl_open is not successful */ + private = dev_priv->process_priv; + filep->private_data = NULL; + + mutex_lock(&device->mutex); + kgsl_check_suspended(device); + + while (1) { + context = idr_get_next(&dev_priv->device->context_idr, &next); + if (context == NULL) + break; + + if (context->dev_priv == dev_priv) { + device->ftbl.device_drawctxt_destroy(device, context); + kgsl_destroy_context(dev_priv, context); + } + + next = next + 1; + } + + device->open_count--; + if (device->open_count == 0) { + result = device->ftbl.device_stop(device); + device->state = KGSL_STATE_INIT; + KGSL_PWR_WARN(device, "state -> INIT, device %d\n", device->id); + } + /* clean up any to-be-freed entries that belong to this + * process and this device + */ + kgsl_memqueue_cleanup(device, private); + + mutex_unlock(&device->mutex); + kfree(dev_priv); + + kgsl_put_process_private(device, private); + + pm_runtime_put(&device->pdev->dev); + return result; +} + +static int kgsl_open(struct inode *inodep, struct file *filep) +{ + int result; + struct kgsl_device_private *dev_priv; + struct kgsl_device *device; + unsigned int minor = iminor(inodep); + struct device *dev; + + device = kgsl_get_minor(minor); + BUG_ON(device == NULL); + + if (filep->f_flags & O_EXCL) { + KGSL_DRV_ERR(device, "O_EXCL not allowed\n"); + return -EBUSY; + } + + dev = &device->pdev->dev; + + result = pm_runtime_get_sync(dev); + if (result < 0) { + KGSL_DRV_ERR(device, + "Runtime PM: Unable to wake up the device, rc = %d\n", + result); + return result; + } + result = 0; + + dev_priv = kzalloc(sizeof(struct kgsl_device_private), GFP_KERNEL); + if (dev_priv == NULL) { + KGSL_DRV_ERR(device, "kzalloc failed(%d)\n", + sizeof(struct kgsl_device_private)); + result = -ENOMEM; + goto err_pmruntime; + } + + dev_priv->device = device; + filep->private_data = dev_priv; + + /* Get file (per process) private struct */ + dev_priv->process_priv = kgsl_get_process_private(dev_priv); + if (dev_priv->process_priv == NULL) { + result = -ENOMEM; + goto err_freedevpriv; + } + + mutex_lock(&device->mutex); + kgsl_check_suspended(device); + + if (device->open_count == 0) { + result = device->ftbl.device_start(device, true); + + if (result) { + mutex_unlock(&device->mutex); + goto err_putprocess; + } + device->state = KGSL_STATE_ACTIVE; + KGSL_PWR_WARN(device, + "state -> ACTIVE, device %d\n", minor); + } + device->open_count++; + mutex_unlock(&device->mutex); + + KGSL_DRV_INFO(device, "Initialized %s: mmu=%s pagetable_count=%d\n", + device->name, kgsl_mmu_enabled() ? "on" : "off", + KGSL_PAGETABLE_COUNT); + + return result; + +err_putprocess: + kgsl_put_process_private(device, dev_priv->process_priv); +err_freedevpriv: + filep->private_data = NULL; + kfree(dev_priv); +err_pmruntime: + pm_runtime_put(&device->pdev->dev); + return result; +} + + +/*call with private->mem_lock locked */ +static struct kgsl_mem_entry * +kgsl_sharedmem_find(struct kgsl_process_private *private, unsigned int gpuaddr) +{ + struct kgsl_mem_entry *entry = NULL, *result = NULL; + + BUG_ON(private == NULL); + + list_for_each_entry(entry, &private->mem_list, list) { + if (entry->memdesc.gpuaddr == gpuaddr) { + result = entry; + break; + } + } + return result; +} + +/*call with private->mem_lock locked */ +struct kgsl_mem_entry * +kgsl_sharedmem_find_region(struct kgsl_process_private *private, + unsigned int gpuaddr, + size_t size) +{ + struct kgsl_mem_entry *entry = NULL, *result = NULL; + + BUG_ON(private == NULL); + + list_for_each_entry(entry, &private->mem_list, list) { + if (gpuaddr >= entry->memdesc.gpuaddr && + ((gpuaddr + size) <= + (entry->memdesc.gpuaddr + entry->memdesc.size))) { + result = entry; + break; + } + } + + return result; +} +EXPORT_SYMBOL(kgsl_sharedmem_find_region); + +uint8_t *kgsl_gpuaddr_to_vaddr(const struct kgsl_memdesc *memdesc, + unsigned int gpuaddr, unsigned int *size) +{ + BUG_ON(memdesc->hostptr == NULL); + + if (memdesc->gpuaddr == 0 || (gpuaddr < memdesc->gpuaddr || + gpuaddr >= memdesc->gpuaddr + memdesc->size)) + return NULL; + + *size = memdesc->size - (memdesc->gpuaddr - gpuaddr); + return memdesc->hostptr + (memdesc->gpuaddr - gpuaddr); +} +EXPORT_SYMBOL(kgsl_gpuaddr_to_vaddr); + +/*call all ioctl sub functions with driver locked*/ +static long kgsl_ioctl_device_getproperty(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + int result = 0; + struct kgsl_device_getproperty *param = data; + + switch (param->type) { + case KGSL_PROP_VERSION: + { + struct kgsl_version version; + if (param->sizebytes != sizeof(version)) { + result = -EINVAL; + break; + } + + version.drv_major = KGSL_VERSION_MAJOR; + version.drv_minor = KGSL_VERSION_MINOR; + version.dev_major = dev_priv->device->ver_major; + version.dev_minor = dev_priv->device->ver_minor; + + if (copy_to_user(param->value, &version, sizeof(version))) + result = -EFAULT; + + break; + } + default: + result = dev_priv->device->ftbl.device_getproperty( + dev_priv->device, param->type, + param->value, param->sizebytes); + } + + + return result; +} + +static long kgsl_ioctl_device_regread(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + struct kgsl_device_regread *param = data; + + if (param->offsetwords*sizeof(uint32_t) >= + dev_priv->device->regspace.sizebytes) { + KGSL_DRV_ERR(dev_priv->device, "invalid offset %d\n", + param->offsetwords); + return -ERANGE; + } + + kgsl_regread(dev_priv->device, param->offsetwords, ¶m->value); + + return 0; +} + + +static long kgsl_ioctl_device_waittimestamp(struct kgsl_device_private + *dev_priv, unsigned int cmd, + void *data) +{ + int result = 0; + struct kgsl_device_waittimestamp *param = data; + + /* Set the active count so that suspend doesn't do the + wrong thing */ + + dev_priv->device->active_cnt++; + + /* Don't wait forever, set a max value for now */ + if (param->timeout == -1) + param->timeout = 10 * MSEC_PER_SEC; + + result = dev_priv->device->ftbl.device_waittimestamp(dev_priv->device, + param->timestamp, + param->timeout); + + kgsl_memqueue_drain(dev_priv->device); + + /* Fire off any pending suspend operations that are in flight */ + + INIT_COMPLETION(dev_priv->device->suspend_gate); + dev_priv->device->active_cnt--; + complete(&dev_priv->device->suspend_gate); + + return result; +} +static bool check_ibdesc(struct kgsl_device_private *dev_priv, + struct kgsl_ibdesc *ibdesc, unsigned int numibs, + bool parse) +{ + bool result = true; + unsigned int i; + for (i = 0; i < numibs; i++) { + struct kgsl_mem_entry *entry; + spin_lock(&dev_priv->process_priv->mem_lock); + entry = kgsl_sharedmem_find_region(dev_priv->process_priv, + ibdesc[i].gpuaddr, ibdesc[i].sizedwords * sizeof(uint)); + spin_unlock(&dev_priv->process_priv->mem_lock); + if (entry == NULL) { + KGSL_DRV_ERR(dev_priv->device, + "invalid cmd buffer gpuaddr %08x " \ + "sizedwords %d\n", ibdesc[i].gpuaddr, + ibdesc[i].sizedwords); + result = false; + break; + } + + if (parse && !kgsl_cffdump_parse_ibs(dev_priv, &entry->memdesc, + ibdesc[i].gpuaddr, ibdesc[i].sizedwords, true)) { + KGSL_DRV_ERR(dev_priv->device, + "invalid cmd buffer gpuaddr %08x " \ + "sizedwords %d numibs %d/%d\n", + ibdesc[i].gpuaddr, + ibdesc[i].sizedwords, i+1, numibs); + result = false; + break; + } + } + return result; +} + +static long kgsl_ioctl_rb_issueibcmds(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + int result = 0; + struct kgsl_ringbuffer_issueibcmds *param = data; + struct kgsl_ibdesc *ibdesc; + struct kgsl_context *context; + +#ifdef CONFIG_MSM_KGSL_DRM + kgsl_gpu_mem_flush(DRM_KGSL_GEM_CACHE_OP_TO_DEV); +#endif + + context = kgsl_find_context(dev_priv, param->drawctxt_id); + if (context == NULL) { + result = -EINVAL; + KGSL_DRV_ERR(dev_priv->device, + "invalid drawctxt drawctxt_id %d\n", + param->drawctxt_id); + goto done; + } + + if (param->flags & KGSL_CONTEXT_SUBMIT_IB_LIST) { + KGSL_DRV_INFO(dev_priv->device, + "Using IB list mode for ib submission, numibs: %d\n", + param->numibs); + if (!param->numibs) { + KGSL_DRV_ERR(dev_priv->device, + "Invalid numibs as parameter: %d\n", + param->numibs); + result = -EINVAL; + goto done; + } + + ibdesc = kzalloc(sizeof(struct kgsl_ibdesc) * param->numibs, + GFP_KERNEL); + if (!ibdesc) { + KGSL_MEM_ERR(dev_priv->device, + "kzalloc(%d) failed\n", + sizeof(struct kgsl_ibdesc) * param->numibs); + result = -ENOMEM; + goto done; + } + + if (copy_from_user(ibdesc, (void *)param->ibdesc_addr, + sizeof(struct kgsl_ibdesc) * param->numibs)) { + result = -EFAULT; + KGSL_DRV_ERR(dev_priv->device, + "copy_from_user failed\n"); + goto free_ibdesc; + } + } else { + KGSL_DRV_INFO(dev_priv->device, + "Using single IB submission mode for ib submission\n"); + /* If user space driver is still using the old mode of + * submitting single ib then we need to support that as well */ + ibdesc = kzalloc(sizeof(struct kgsl_ibdesc), GFP_KERNEL); + if (!ibdesc) { + KGSL_MEM_ERR(dev_priv->device, + "kzalloc(%d) failed\n", + sizeof(struct kgsl_ibdesc)); + result = -ENOMEM; + goto done; + } + ibdesc[0].gpuaddr = param->ibdesc_addr; + ibdesc[0].sizedwords = param->numibs; + param->numibs = 1; + } + + if (!check_ibdesc(dev_priv, ibdesc, param->numibs, true)) { + KGSL_DRV_ERR(dev_priv->device, "bad ibdesc"); + result = -EINVAL; + goto free_ibdesc; + } + + result = dev_priv->device->ftbl.device_issueibcmds(dev_priv, + context, + ibdesc, + param->numibs, + ¶m->timestamp, + param->flags); + + if (result != 0) + goto free_ibdesc; + + /* this is a check to try to detect if a command buffer was freed + * during issueibcmds(). + */ + if (!check_ibdesc(dev_priv, ibdesc, param->numibs, false)) { + KGSL_DRV_ERR(dev_priv->device, "bad ibdesc AFTER issue"); + result = -EINVAL; + goto free_ibdesc; + } + +free_ibdesc: + kfree(ibdesc); +done: + +#ifdef CONFIG_MSM_KGSL_DRM + kgsl_gpu_mem_flush(DRM_KGSL_GEM_CACHE_OP_FROM_DEV); +#endif + + return result; +} + +static long kgsl_ioctl_cmdstream_readtimestamp(struct kgsl_device_private + *dev_priv, unsigned int cmd, + void *data) +{ + struct kgsl_cmdstream_readtimestamp *param = data; + + param->timestamp = + dev_priv->device->ftbl.device_readtimestamp( + dev_priv->device, param->type); + + return 0; +} + +static long kgsl_ioctl_cmdstream_freememontimestamp(struct kgsl_device_private + *dev_priv, unsigned int cmd, + void *data) +{ + int result = 0; + struct kgsl_cmdstream_freememontimestamp *param = data; + struct kgsl_mem_entry *entry = NULL; + + spin_lock(&dev_priv->process_priv->mem_lock); + entry = kgsl_sharedmem_find(dev_priv->process_priv, param->gpuaddr); + if (entry) + list_del(&entry->list); + spin_unlock(&dev_priv->process_priv->mem_lock); + + if (entry) { + kgsl_memqueue_freememontimestamp(dev_priv->device, entry, + param->timestamp, param->type); + kgsl_memqueue_drain(dev_priv->device); + } else { + KGSL_DRV_ERR(dev_priv->device, + "invalid gpuaddr %08x\n", param->gpuaddr); + result = -EINVAL; + } + + return result; +} + +static long kgsl_ioctl_drawctxt_create(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + int result = 0; + struct kgsl_drawctxt_create *param = data; + struct kgsl_context *context = NULL; + + context = kgsl_create_context(dev_priv); + + if (context == NULL) { + result = -ENOMEM; + goto done; + } + + if (dev_priv->device->ftbl.device_drawctxt_create != NULL) + result = dev_priv->device->ftbl.device_drawctxt_create(dev_priv, + param->flags, + context); + + param->drawctxt_id = context->id; + +done: + if (result && context) + kgsl_destroy_context(dev_priv, context); + + return result; +} + +static long kgsl_ioctl_drawctxt_destroy(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + int result = 0; + struct kgsl_drawctxt_destroy *param = data; + struct kgsl_context *context; + + context = kgsl_find_context(dev_priv, param->drawctxt_id); + + if (context == NULL) { + result = -EINVAL; + goto done; + } + + result = dev_priv->device->ftbl.device_drawctxt_destroy( + dev_priv->device, + context); + + kgsl_destroy_context(dev_priv, context); + +done: + return result; +} + +static long kgsl_ioctl_sharedmem_free(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + int result = 0; + struct kgsl_sharedmem_free *param = data; + struct kgsl_process_private *private = dev_priv->process_priv; + struct kgsl_mem_entry *entry = NULL; + + spin_lock(&private->mem_lock); + entry = kgsl_sharedmem_find(private, param->gpuaddr); + if (entry) + list_del(&entry->list); + spin_unlock(&private->mem_lock); + + if (entry) { + kgsl_mem_entry_put(entry); + } else { + KGSL_CORE_ERR("invalid gpuaddr %08x\n", param->gpuaddr); + result = -EINVAL; + } + + return result; +} + +static struct vm_area_struct *kgsl_get_vma_from_start_addr(unsigned int addr) +{ + struct vm_area_struct *vma; + int len; + + down_read(¤t->mm->mmap_sem); + vma = find_vma(current->mm, addr); + up_read(¤t->mm->mmap_sem); + if (!vma) { + KGSL_CORE_ERR("find_vma(%x) failed\n", addr); + return NULL; + } + len = vma->vm_end - vma->vm_start; + if (vma->vm_pgoff || !KGSL_IS_PAGE_ALIGNED(len) || + !KGSL_IS_PAGE_ALIGNED(vma->vm_start)) { + KGSL_CORE_ERR("address %x is not aligned\n", addr); + return NULL; + } + if (vma->vm_start != addr) { + KGSL_CORE_ERR("vma address does not match mmap address\n"); + return NULL; + } + return vma; +} + +static long +kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + int result = 0, len = 0; + struct kgsl_process_private *private = dev_priv->process_priv; + struct kgsl_sharedmem_from_vmalloc *param = data; + struct kgsl_mem_entry *entry = NULL; + struct vm_area_struct *vma; + + if (!kgsl_mmu_enabled()) + return -ENODEV; + + /* Make sure all pending freed memory is collected */ + kgsl_memqueue_drain_unlocked(dev_priv->device); + + if (!param->hostptr) { + KGSL_CORE_ERR("invalid hostptr %x\n", param->hostptr); + result = -EINVAL; + goto error; + } + + vma = kgsl_get_vma_from_start_addr(param->hostptr); + if (!vma) { + result = -EINVAL; + goto error; + } + len = vma->vm_end - vma->vm_start; + if (len == 0) { + KGSL_CORE_ERR("Invalid vma region length %d\n", len); + result = -EINVAL; + goto error; + } + + entry = kgsl_mem_entry_create(); + if (entry == NULL) { + result = -ENOMEM; + goto error; + } + + result = kgsl_sharedmem_vmalloc_user(&entry->memdesc, + private->pagetable, len, + param->flags); + if (result != 0) + goto error_free_entry; + + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + result = remap_vmalloc_range(vma, (void *) entry->memdesc.hostptr, 0); + if (result) { + KGSL_CORE_ERR("remap_vmalloc_range failed: %d\n", result); + goto error_free_vmalloc; + } + + param->gpuaddr = entry->memdesc.gpuaddr; + + entry->memtype = KGSL_VMALLOC_MEMORY; + + kgsl_mem_entry_attach_process(entry, private); + + /* Process specific statistics */ + KGSL_STATS_ADD(len, private->stats.vmalloc, + private->stats.vmalloc_max); + + kgsl_check_idle(dev_priv->device); + return 0; + +error_free_vmalloc: + kgsl_sharedmem_free(&entry->memdesc); + +error_free_entry: + kfree(entry); + +error: + kgsl_check_idle(dev_priv->device); + return result; +} + +static int kgsl_get_phys_file(int fd, unsigned long *start, unsigned long *len, + unsigned long *vstart, struct file **filep) +{ + struct file *fbfile; + int ret = 0; + dev_t rdev; + struct fb_info *info; + + *filep = NULL; + if (!get_pmem_file(fd, start, vstart, len, filep)) + return 0; + + fbfile = fget(fd); + if (fbfile == NULL) { + KGSL_CORE_ERR("fget_light failed\n"); + return -1; + } + + rdev = fbfile->f_dentry->d_inode->i_rdev; + info = MAJOR(rdev) == FB_MAJOR ? registered_fb[MINOR(rdev)] : NULL; + if (info) { + *start = info->fix.smem_start; + *len = info->fix.smem_len; + *vstart = (unsigned long)__va(info->fix.smem_start); + ret = 0; + } else { + KGSL_CORE_ERR("framebuffer minor %d not found\n", + MINOR(rdev)); + ret = -1; + } + + fput(fbfile); + + return ret; +} + +static inline int _check_region(unsigned long start, unsigned long size, + uint64_t len) +{ + uint64_t end = start + size; + return (end > len); +} + +static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, + struct kgsl_pagetable *pagetable, + unsigned int fd, unsigned int offset, + size_t size) +{ + int ret; + unsigned long phys, virt, len; + struct file *filep; + + ret = kgsl_get_phys_file(fd, &phys, &len, &virt, &filep); + if (ret) + return ret; + + if (size == 0) + size = len; + + size = ALIGN(size, PAGE_SIZE); + + if (_check_region(offset & PAGE_MASK, size, len)) { + KGSL_CORE_ERR("Offset (%ld) + size (%d) is larger" + "than pmem region length %ld\n", + offset & PAGE_MASK, size, len); + put_pmem_file(filep); + return -EINVAL; + } + + entry->file_ptr = filep; + + entry->memdesc.pagetable = pagetable; + entry->memdesc.size = size; + entry->memdesc.physaddr = phys + (offset & PAGE_MASK); + entry->memdesc.hostptr = (void *) (virt + (offset & PAGE_MASK)); + entry->memdesc.ops = &kgsl_contig_ops; + + return 0; +} + +static int kgsl_setup_hostptr(struct kgsl_mem_entry *entry, + struct kgsl_pagetable *pagetable, + void *hostptr, unsigned int offset, + size_t size) +{ + struct vm_area_struct *vma; + unsigned int len; + + down_read(¤t->mm->mmap_sem); + vma = find_vma(current->mm, (unsigned int) hostptr); + up_read(¤t->mm->mmap_sem); + + if (!vma) { + KGSL_CORE_ERR("find_vma(%p) failed\n", hostptr); + return -EINVAL; + } + + /* We don't necessarily start at vma->vm_start */ + len = vma->vm_end - (unsigned long) hostptr; + + if (!KGSL_IS_PAGE_ALIGNED((unsigned long) hostptr) || + !KGSL_IS_PAGE_ALIGNED(len)) { + KGSL_CORE_ERR("user address len(%u)" + "and start(%p) must be page" + "aligned\n", len, hostptr); + return -EINVAL; + } + + if (size == 0) + size = len; + + size = ALIGN(size, PAGE_SIZE); + + if (_check_region(offset & PAGE_MASK, size, len)) { + KGSL_CORE_ERR("Offset (%ld) + size (%d) is larger" + "than region length %d\n", + offset & PAGE_MASK, size, len); + return -EINVAL; + } + + entry->memdesc.pagetable = pagetable; + entry->memdesc.size = size; + entry->memdesc.hostptr = hostptr + (offset & PAGE_MASK); + entry->memdesc.ops = &kgsl_userptr_ops; + + return 0; +} + +static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry, + struct kgsl_pagetable *pagetable, + int fd, void *hostptr, size_t size) +{ + int ret; + struct vm_area_struct *vma; + struct file *filep, *vmfile; + unsigned long len; + + vma = kgsl_get_vma_from_start_addr((unsigned long) hostptr); + if (vma == NULL) + return -EINVAL; + + len = vma->vm_end - vma->vm_start; + + if (size == 0) + size = len; + + if (size != len) { + KGSL_CORE_ERR("Invalid size %d for vma region %p\n", + size, hostptr); + return -EINVAL; + } + + ret = get_ashmem_file(fd, &filep, &vmfile, &len); + + if (ret) { + KGSL_CORE_ERR("get_ashmem_file failed\n"); + return ret; + } + + if (vmfile != vma->vm_file) { + KGSL_CORE_ERR("ashmem shmem file does not match vma\n"); + ret = -EINVAL; + goto err; + } + + entry->file_ptr = filep; + + entry->memdesc.pagetable = pagetable; + entry->memdesc.size = ALIGN(size, PAGE_SIZE); + entry->memdesc.hostptr = hostptr; + entry->memdesc.ops = &kgsl_userptr_ops; + + return 0; + +err: + put_ashmem_file(filep); + return ret; +} + +static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + int result = -EINVAL; + struct kgsl_map_user_mem *param = data; + struct kgsl_mem_entry *entry = NULL; + struct kgsl_process_private *private = dev_priv->process_priv; + + entry = kgsl_mem_entry_create(); + + if (entry == NULL) + return -ENOMEM; + + kgsl_memqueue_drain_unlocked(dev_priv->device); + + switch (param->memtype) { + case KGSL_USER_MEM_TYPE_PMEM: + if (param->fd == 0 || param->len == 0) + break; + + result = kgsl_setup_phys_file(entry, private->pagetable, + param->fd, param->offset, + param->len); + break; + + case KGSL_USER_MEM_TYPE_ADDR: + if (!kgsl_mmu_enabled()) { + KGSL_DRV_ERR(dev_priv->device, + "Cannot map paged memory with the " + "MMU disabled\n"); + break; + } + + if (param->hostptr == 0) + break; + + result = kgsl_setup_hostptr(entry, private->pagetable, + (void *) param->hostptr, + param->offset, param->len); + break; + + case KGSL_USER_MEM_TYPE_ASHMEM: + if (!kgsl_mmu_enabled()) { + KGSL_DRV_ERR(dev_priv->device, + "Cannot map paged memory with the " + "MMU disabled\n"); + break; + } + + if (param->hostptr == 0) + break; + + result = kgsl_setup_ashmem(entry, private->pagetable, + param->fd, (void *) param->hostptr, + param->len); + break; + default: + KGSL_CORE_ERR("Invalid memory type: %x\n", param->memtype); + break; + } + + if (result) + goto error; + + result = kgsl_mmu_map(private->pagetable, + &entry->memdesc, + GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); + + if (result) + goto error_put_file_ptr; + + param->gpuaddr = entry->memdesc.gpuaddr; + + entry->memtype = KGSL_EXTERNAL_MEMORY; + + /* Statistics */ + KGSL_STATS_ADD(param->len, private->stats.exmem, + private->stats.exmem_max); + + kgsl_mem_entry_attach_process(entry, private); + + kgsl_check_idle(dev_priv->device); + return result; + + error_put_file_ptr: + if (entry->file_ptr) + fput(entry->file_ptr); + +error: + kfree(entry); + kgsl_check_idle(dev_priv->device); + return result; +} + +/*This function flushes a graphics memory allocation from CPU cache + *when caching is enabled with MMU*/ +static long +kgsl_ioctl_sharedmem_flush_cache(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + int result = 0; + struct kgsl_mem_entry *entry; + struct kgsl_sharedmem_free *param = data; + struct kgsl_process_private *private = dev_priv->process_priv; + + spin_lock(&private->mem_lock); + entry = kgsl_sharedmem_find(private, param->gpuaddr); + if (!entry) { + KGSL_CORE_ERR("invalid gpuaddr %08x\n", param->gpuaddr); + result = -EINVAL; + } else { + if (!entry->memdesc.hostptr) + entry->memdesc.hostptr = + kgsl_gpuaddr_to_vaddr(&entry->memdesc, + param->gpuaddr, &entry->memdesc.size); + + if (!entry->memdesc.hostptr) { + KGSL_CORE_ERR("invalid hostptr with gpuaddr %08x\n", + param->gpuaddr); + goto done; + } + + kgsl_cache_range_op(&entry->memdesc, KGSL_CACHE_OP_CLEAN); + + /* Statistics - keep track of how many flushes each process + does */ + private->stats.flushes++; + } + spin_unlock(&private->mem_lock); +done: + return result; +} + +static long +kgsl_ioctl_gpumem_alloc(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + struct kgsl_process_private *private = dev_priv->process_priv; + struct kgsl_gpumem_alloc *param = data; + struct kgsl_mem_entry *entry; + int result; + + entry = kgsl_mem_entry_create(); + if (entry == NULL) + return -ENOMEM; + + /* Make sure all pending freed memory is collected */ + kgsl_memqueue_drain_unlocked(dev_priv->device); + + result = kgsl_allocate_user(&entry->memdesc, private->pagetable, + param->size, param->flags); + + if (result == 0) { + kgsl_mem_entry_attach_process(entry, private); + param->gpuaddr = entry->memdesc.gpuaddr; + } else + kfree(entry); + + kgsl_check_idle(dev_priv->device); + return result; +} + +typedef long (*kgsl_ioctl_func_t)(struct kgsl_device_private *, + unsigned int, void *); + +#define KGSL_IOCTL_FUNC(_cmd, _func, _lock) \ + [_IOC_NR(_cmd)] = { .cmd = _cmd, .func = _func, .lock = _lock } + +static const struct { + unsigned int cmd; + kgsl_ioctl_func_t func; + int lock; +} kgsl_ioctl_funcs[] = { + KGSL_IOCTL_FUNC(IOCTL_KGSL_DEVICE_GETPROPERTY, + kgsl_ioctl_device_getproperty, 1), + KGSL_IOCTL_FUNC(IOCTL_KGSL_DEVICE_REGREAD, + kgsl_ioctl_device_regread, 1), + KGSL_IOCTL_FUNC(IOCTL_KGSL_DEVICE_WAITTIMESTAMP, + kgsl_ioctl_device_waittimestamp, 1), + KGSL_IOCTL_FUNC(IOCTL_KGSL_RINGBUFFER_ISSUEIBCMDS, + kgsl_ioctl_rb_issueibcmds, 1), + KGSL_IOCTL_FUNC(IOCTL_KGSL_CMDSTREAM_READTIMESTAMP, + kgsl_ioctl_cmdstream_readtimestamp, 1), + KGSL_IOCTL_FUNC(IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP, + kgsl_ioctl_cmdstream_freememontimestamp, 1), + KGSL_IOCTL_FUNC(IOCTL_KGSL_DRAWCTXT_CREATE, + kgsl_ioctl_drawctxt_create, 1), + KGSL_IOCTL_FUNC(IOCTL_KGSL_DRAWCTXT_DESTROY, + kgsl_ioctl_drawctxt_destroy, 1), + KGSL_IOCTL_FUNC(IOCTL_KGSL_MAP_USER_MEM, + kgsl_ioctl_map_user_mem, 0), + KGSL_IOCTL_FUNC(IOCTL_KGSL_SHAREDMEM_FROM_PMEM, + kgsl_ioctl_map_user_mem, 0), + KGSL_IOCTL_FUNC(IOCTL_KGSL_SHAREDMEM_FREE, + kgsl_ioctl_sharedmem_free, 0), + KGSL_IOCTL_FUNC(IOCTL_KGSL_SHAREDMEM_FROM_VMALLOC, + kgsl_ioctl_sharedmem_from_vmalloc, 0), + KGSL_IOCTL_FUNC(IOCTL_KGSL_SHAREDMEM_FLUSH_CACHE, + kgsl_ioctl_sharedmem_flush_cache, 0), + KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_ALLOC, + kgsl_ioctl_gpumem_alloc, 0), +}; + +static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + struct kgsl_device_private *dev_priv = filep->private_data; + unsigned int nr = _IOC_NR(cmd); + kgsl_ioctl_func_t func; + int lock, ret; + char ustack[64]; + void *uptr = NULL; + + BUG_ON(dev_priv == NULL); + + /* Workaround for an previously incorrectly defined ioctl code. + This helps ensure binary compatability */ + + if (cmd == IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP_OLD) + cmd = IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP; + + if (cmd & (IOC_IN | IOC_OUT)) { + if (_IOC_SIZE(cmd) < sizeof(ustack)) + uptr = ustack; + else { + uptr = kzalloc(_IOC_SIZE(cmd), GFP_KERNEL); + if (uptr == NULL) { + KGSL_MEM_ERR(dev_priv->device, + "kzalloc(%d) failed\n", _IOC_SIZE(cmd)); + ret = -ENOMEM; + goto done; + } + } + + if (cmd & IOC_IN) { + if (copy_from_user(uptr, (void __user *) arg, + _IOC_SIZE(cmd))) { + ret = -EFAULT; + goto done; + } + } else + memset(uptr, 0, _IOC_SIZE(cmd)); + } + + if (nr < ARRAY_SIZE(kgsl_ioctl_funcs) && + kgsl_ioctl_funcs[nr].func != NULL) { + func = kgsl_ioctl_funcs[nr].func; + lock = kgsl_ioctl_funcs[nr].lock; + } else { + func = dev_priv->device->ftbl.device_ioctl; + lock = 1; + } + + if (lock) { + mutex_lock(&dev_priv->device->mutex); + kgsl_check_suspended(dev_priv->device); + } + + ret = func(dev_priv, cmd, uptr); + + if (lock) { + kgsl_check_idle_locked(dev_priv->device); + mutex_unlock(&dev_priv->device->mutex); + } + + if (ret == 0 && (cmd & IOC_OUT)) { + if (copy_to_user((void __user *) arg, uptr, _IOC_SIZE(cmd))) + ret = -EFAULT; + } + +done: + if (_IOC_SIZE(cmd) >= sizeof(ustack)) + kfree(uptr); + + return ret; +} + +static int +kgsl_mmap_memstore(struct kgsl_device *device, struct vm_area_struct *vma) +{ + struct kgsl_memdesc *memdesc = &device->memstore; + int result; + unsigned int vma_size = vma->vm_end - vma->vm_start; + + /* The memstore can only be mapped as read only */ + + if (vma->vm_flags & VM_WRITE) + return -EPERM; + + if (memdesc->size != vma_size) { + KGSL_MEM_ERR(device, "memstore bad size: %d should be %d\n", + vma_size, memdesc->size); + return -EINVAL; + } + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + result = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma_size, vma->vm_page_prot); + if (result != 0) + KGSL_MEM_ERR(device, "remap_pfn_range failed: %d\n", + result); + + return result; +} + +static int +kgsl_gpumem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct kgsl_mem_entry *entry = vma->vm_private_data; + + if (!entry->memdesc.ops->vmfault) + return VM_FAULT_SIGBUS; + + return entry->memdesc.ops->vmfault(&entry->memdesc, vma, vmf); +} + +static void +kgsl_gpumem_vm_close(struct vm_area_struct *vma) +{ + struct kgsl_mem_entry *entry = vma->vm_private_data; + kgsl_mem_entry_put(entry); +} + +static struct vm_operations_struct kgsl_gpumem_vm_ops = { + .fault = kgsl_gpumem_vm_fault, + .close = kgsl_gpumem_vm_close, +}; + +static int kgsl_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long vma_offset = vma->vm_pgoff << PAGE_SHIFT; + struct inode *inodep = file->f_path.dentry->d_inode; + struct kgsl_device_private *dev_priv = file->private_data; + struct kgsl_process_private *private = dev_priv->process_priv; + struct kgsl_mem_entry *entry; + struct kgsl_device *device; + + device = kgsl_driver.devp[iminor(inodep)]; + BUG_ON(device == NULL); + + /* Handle leagacy behavior for memstore */ + + if (vma_offset == device->memstore.physaddr) + return kgsl_mmap_memstore(device, vma); + + /* Find a chunk of GPU memory */ + + spin_lock(&private->mem_lock); + list_for_each_entry(entry, &private->mem_list, list) { + if (vma_offset == entry->memdesc.gpuaddr) { + kgsl_mem_entry_get(entry); + break; + } + } + spin_unlock(&private->mem_lock); + + if (entry == NULL) + return -EINVAL; + + if (!entry->memdesc.ops->vmflags || !entry->memdesc.ops->vmfault) + return -EINVAL; + + vma->vm_flags |= entry->memdesc.ops->vmflags(&entry->memdesc); + + vma->vm_private_data = entry; + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_ops = &kgsl_gpumem_vm_ops; + vma->vm_file = file; + + return 0; +} + +static const struct file_operations kgsl_fops = { + .owner = THIS_MODULE, + .release = kgsl_release, + .open = kgsl_open, + .mmap = kgsl_mmap, + .unlocked_ioctl = kgsl_ioctl, +}; + +struct kgsl_driver kgsl_driver = { + .process_mutex = __MUTEX_INITIALIZER(kgsl_driver.process_mutex), + .pt_mutex = __MUTEX_INITIALIZER(kgsl_driver.pt_mutex), + .devlock = __MUTEX_INITIALIZER(kgsl_driver.devlock), +}; +EXPORT_SYMBOL(kgsl_driver); + +void kgsl_unregister_device(struct kgsl_device *device) +{ + int minor; + + mutex_lock(&kgsl_driver.devlock); + for (minor = 0; minor < KGSL_DEVICE_MAX; minor++) { + if (device == kgsl_driver.devp[minor]) + break; + } + + mutex_unlock(&kgsl_driver.devlock); + + if (minor == KGSL_DEVICE_MAX) + return; + + kgsl_cffdump_close(device->id); + kgsl_pwrctrl_uninit_sysfs(device); + + wake_lock_destroy(&device->idle_wakelock); + idr_destroy(&device->context_idr); + + if (device->memstore.hostptr) + kgsl_sharedmem_free(&device->memstore); + + kgsl_mmu_close(device); + + if (device->work_queue) { + destroy_workqueue(device->work_queue); + device->work_queue = NULL; + } + + device_destroy(kgsl_driver.class, + MKDEV(MAJOR(kgsl_driver.major), minor)); + + mutex_lock(&kgsl_driver.devlock); + kgsl_driver.devp[minor] = NULL; + mutex_unlock(&kgsl_driver.devlock); + + atomic_dec(&kgsl_driver.device_count); +} +EXPORT_SYMBOL(kgsl_unregister_device); + +int +kgsl_register_device(struct kgsl_device *device) +{ + int minor, ret; + dev_t dev; + + /* Find a minor for the device */ + + mutex_lock(&kgsl_driver.devlock); + for (minor = 0; minor < KGSL_DEVICE_MAX; minor++) { + if (kgsl_driver.devp[minor] == NULL) { + kgsl_driver.devp[minor] = device; + break; + } + } + + mutex_unlock(&kgsl_driver.devlock); + + if (minor == KGSL_DEVICE_MAX) { + KGSL_CORE_ERR("minor devices exhausted\n"); + return -ENODEV; + } + + /* Create the device */ + dev = MKDEV(MAJOR(kgsl_driver.major), minor); + device->dev = device_create(kgsl_driver.class, + &device->pdev->dev, + dev, device, + device->name); + + if (IS_ERR(device->dev)) { + ret = PTR_ERR(device->dev); + KGSL_CORE_ERR("device_create(%s): %d\n", device->name, ret); + goto err_devlist; + } + + dev_set_drvdata(&device->pdev->dev, device); + + /* Generic device initialization */ + atomic_inc(&kgsl_driver.device_count); + + init_waitqueue_head(&device->wait_queue); + + kgsl_cffdump_open(device->id); + + init_completion(&device->hwaccess_gate); + init_completion(&device->suspend_gate); + + ATOMIC_INIT_NOTIFIER_HEAD(&device->ts_notifier_list); + + setup_timer(&device->idle_timer, kgsl_timer, (unsigned long) device); + ret = kgsl_create_device_workqueue(device); + if (ret) + goto err_devlist; + + INIT_WORK(&device->idle_check_ws, kgsl_idle_check); + + INIT_LIST_HEAD(&device->memqueue); + + ret = kgsl_mmu_init(device); + if (ret != 0) + goto err_dest_work_q; + + ret = kgsl_allocate_contig(&device->memstore, + sizeof(struct kgsl_devmemstore)); + + if (ret != 0) + goto err_close_mmu; + + kgsl_sharedmem_set(&device->memstore, 0, 0, device->memstore.size); + + wake_lock_init(&device->idle_wakelock, WAKE_LOCK_IDLE, device->name); + idr_init(&device->context_idr); + + /* sysfs and debugfs initalization - failure here is non fatal */ + + /* Initialize logging */ + kgsl_device_debugfs_init(device); + + /* Initialize common sysfs entries */ + kgsl_pwrctrl_init_sysfs(device); + + return 0; + +err_close_mmu: + kgsl_mmu_close(device); +err_dest_work_q: + destroy_workqueue(device->work_queue); + device->work_queue = NULL; +err_devlist: + mutex_lock(&kgsl_driver.devlock); + kgsl_driver.devp[minor] = NULL; + mutex_unlock(&kgsl_driver.devlock); + + return ret; +} +EXPORT_SYMBOL(kgsl_register_device); + +int kgsl_device_platform_probe(struct kgsl_device *device, + irqreturn_t (*dev_isr) (int, void*)) +{ + int status = -EINVAL; + struct kgsl_memregion *regspace = NULL; + struct resource *res; + struct platform_device *pdev = device->pdev; + + pm_runtime_enable(&pdev->dev); + + status = kgsl_pwrctrl_init(device); + if (status) + goto error; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + device->iomemname); + if (res == NULL) { + KGSL_DRV_ERR(device, "platform_get_resource_byname failed\n"); + status = -EINVAL; + goto error_pwrctrl_close; + } + if (res->start == 0 || resource_size(res) == 0) { + KGSL_DRV_ERR(device, "dev %d invalid regspace\n", device->id); + status = -EINVAL; + goto error_pwrctrl_close; + } + + regspace = &device->regspace; + regspace->mmio_phys_base = res->start; + regspace->sizebytes = resource_size(res); + + if (!request_mem_region(regspace->mmio_phys_base, + regspace->sizebytes, device->name)) { + KGSL_DRV_ERR(device, "request_mem_region failed\n"); + status = -ENODEV; + goto error_pwrctrl_close; + } + + regspace->mmio_virt_base = ioremap(regspace->mmio_phys_base, + regspace->sizebytes); + + if (regspace->mmio_virt_base == NULL) { + KGSL_DRV_ERR(device, "ioremap failed\n"); + status = -ENODEV; + goto error_release_mem; + } + + status = request_irq(device->pwrctrl.interrupt_num, dev_isr, + IRQF_TRIGGER_HIGH, device->name, device); + if (status) { + KGSL_DRV_ERR(device, "request_irq(%d) failed: %d\n", + device->pwrctrl.interrupt_num, status); + goto error_iounmap; + } + device->pwrctrl.have_irq = 1; + disable_irq(device->pwrctrl.interrupt_num); + + KGSL_DRV_INFO(device, + "dev_id %d regs phys 0x%08x size 0x%08x virt %p\n", + device->id, regspace->mmio_phys_base, + regspace->sizebytes, regspace->mmio_virt_base); + + + status = kgsl_register_device(device); + if (!status) + return status; + + free_irq(device->pwrctrl.interrupt_num, NULL); + device->pwrctrl.have_irq = 0; +error_iounmap: + iounmap(regspace->mmio_virt_base); + regspace->mmio_virt_base = NULL; +error_release_mem: + release_mem_region(regspace->mmio_phys_base, regspace->sizebytes); +error_pwrctrl_close: + kgsl_pwrctrl_close(device); +error: + return status; +} +EXPORT_SYMBOL(kgsl_device_platform_probe); + +void kgsl_device_platform_remove(struct kgsl_device *device) +{ + struct kgsl_memregion *regspace = &device->regspace; + + kgsl_unregister_device(device); + + if (regspace->mmio_virt_base != NULL) { + iounmap(regspace->mmio_virt_base); + regspace->mmio_virt_base = NULL; + release_mem_region(regspace->mmio_phys_base, + regspace->sizebytes); + } + kgsl_pwrctrl_close(device); + + pm_runtime_disable(&device->pdev->dev); +} +EXPORT_SYMBOL(kgsl_device_platform_remove); + +static int __devinit +kgsl_ptdata_init(void) +{ + INIT_LIST_HEAD(&kgsl_driver.pagetable_list); + + return kgsl_ptpool_init(&kgsl_driver.ptpool, KGSL_PAGETABLE_SIZE, + kgsl_pagetable_count); +} + +static void kgsl_core_exit(void) +{ + unregister_chrdev_region(kgsl_driver.major, KGSL_DEVICE_MAX); + + kgsl_ptpool_destroy(&kgsl_driver.ptpool); + + device_unregister(&kgsl_driver.virtdev); + + if (kgsl_driver.class) { + class_destroy(kgsl_driver.class); + kgsl_driver.class = NULL; + } + + kgsl_drm_exit(); + kgsl_cffdump_destroy(); +} + +static int __init kgsl_core_init(void) +{ + int result = 0; + + /* alloc major and minor device numbers */ + result = alloc_chrdev_region(&kgsl_driver.major, 0, KGSL_DEVICE_MAX, + KGSL_NAME); + if (result < 0) { + KGSL_CORE_ERR("alloc_chrdev_region failed err = %d\n", result); + goto err; + } + + cdev_init(&kgsl_driver.cdev, &kgsl_fops); + kgsl_driver.cdev.owner = THIS_MODULE; + kgsl_driver.cdev.ops = &kgsl_fops; + result = cdev_add(&kgsl_driver.cdev, MKDEV(MAJOR(kgsl_driver.major), 0), + KGSL_DEVICE_MAX); + + if (result) { + KGSL_CORE_ERR("kgsl: cdev_add() failed, dev_num= %d," + " result= %d\n", kgsl_driver.major, result); + goto err; + } + + kgsl_driver.class = class_create(THIS_MODULE, KGSL_NAME); + + if (IS_ERR(kgsl_driver.class)) { + result = PTR_ERR(kgsl_driver.class); + KGSL_CORE_ERR("failed to create class %s", KGSL_NAME); + goto err; + } + + /* Make a virtual device for managing core related things + in sysfs */ + kgsl_driver.virtdev.class = kgsl_driver.class; + dev_set_name(&kgsl_driver.virtdev, "kgsl"); + result = device_register(&kgsl_driver.virtdev); + if (result) { + KGSL_CORE_ERR("driver_register failed\n"); + goto err; + } + + /* Make kobjects in the virtual device for storing statistics */ + + kgsl_driver.ptkobj = + kobject_create_and_add("pagetables", + &kgsl_driver.virtdev.kobj); + + kgsl_driver.prockobj = + kobject_create_and_add("proc", + &kgsl_driver.virtdev.kobj); + + kgsl_core_debugfs_init(); + + kgsl_sharedmem_init_sysfs(); + kgsl_cffdump_init(); + + /* Generic device initialization */ + atomic_set(&kgsl_driver.device_count, -1); + + INIT_LIST_HEAD(&kgsl_driver.process_list); + + result = kgsl_ptdata_init(); + if (result) + goto err; + + result = kgsl_drm_init(NULL); + + if (result) + goto err; + + return 0; + +err: + kgsl_core_exit(); + return result; +} + +device_initcall(kgsl_core_init); + diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h new file mode 100644 index 0000000000000..25a56b49faa62 --- /dev/null +++ b/drivers/gpu/msm/kgsl.h @@ -0,0 +1,272 @@ +/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __KGSL_H +#define __KGSL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "kgsl_device.h" +#include "kgsl_pwrctrl.h" +#include "kgsl_sharedmem.h" +#include "kgsl_log.h" +#include "kgsl_cffdump.h" + +#define KGSL_NAME "kgsl" + +#define CHIP_REV_251 0x020501 + +/* Flags to control whether to flush or invalidate a cached memory range */ +#define KGSL_CACHE_INV 0x00000000 +#define KGSL_CACHE_CLEAN 0x00000001 +#define KGSL_CACHE_FLUSH 0x00000002 + +#define KGSL_CACHE_USER_ADDR 0x00000010 +#define KGSL_CACHE_VMALLOC_ADDR 0x00000020 + +/*cache coherency ops */ +#define DRM_KGSL_GEM_CACHE_OP_TO_DEV 0x0001 +#define DRM_KGSL_GEM_CACHE_OP_FROM_DEV 0x0002 + +/* The size of each entry in a page table */ +#define KGSL_PAGETABLE_ENTRY_SIZE 4 + +/* Pagetable Virtual Address base */ +#define KGSL_PAGETABLE_BASE 0x66000000 + +/* Extra accounting entries needed in the pagetable */ +#define KGSL_PT_EXTRA_ENTRIES 16 + +#define KGSL_PAGETABLE_ENTRIES(_sz) (((_sz) >> PAGE_SHIFT) + \ + KGSL_PT_EXTRA_ENTRIES) + +#ifdef CONFIG_MSM_KGSL_MMU +#define KGSL_PAGETABLE_SIZE \ +ALIGN(KGSL_PAGETABLE_ENTRIES(CONFIG_MSM_KGSL_PAGE_TABLE_SIZE) * \ +KGSL_PAGETABLE_ENTRY_SIZE, PAGE_SIZE) +#else +#define KGSL_PAGETABLE_SIZE 0 +#endif + +#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE +#define KGSL_PAGETABLE_COUNT (CONFIG_MSM_KGSL_PAGE_TABLE_COUNT) +#else +#define KGSL_PAGETABLE_COUNT 1 +#endif + +/* Casting using container_of() for structures that kgsl owns. */ +#define KGSL_CONTAINER_OF(ptr, type, member) \ + container_of(ptr, type, member) + +/* A macro for memory statistics - add the new size to the stat and if + the statisic is greater then _max, set _max +*/ + +#define KGSL_STATS_ADD(_size, _stat, _max) \ + do { _stat += (_size); if (_stat > _max) _max = _stat; } while (0) + +struct kgsl_driver { + struct cdev cdev; + dev_t major; + struct class *class; + /* Virtual device for managing the core */ + struct device virtdev; + /* Kobjects for storing pagetable and process statistics */ + struct kobject *ptkobj; + struct kobject *prockobj; + atomic_t device_count; + struct kgsl_device *devp[KGSL_DEVICE_MAX]; + + uint32_t flags_debug; + + /* Global lilst of open processes */ + struct list_head process_list; + /* Global list of pagetables */ + struct list_head pagetable_list; + /* Mutex for accessing the pagetable list */ + struct mutex pt_mutex; + /* Mutex for accessing the process list */ + struct mutex process_mutex; + + /* Mutex for protecting the device list */ + struct mutex devlock; + + struct kgsl_ptpool ptpool; + + struct { + unsigned int vmalloc; + unsigned int vmalloc_max; + unsigned int coherent; + unsigned int coherent_max; + unsigned int histogram[16]; + } stats; +}; + +extern struct kgsl_driver kgsl_driver; + +#define KGSL_VMALLOC_MEMORY 0 +#define KGSL_EXTERNAL_MEMORY 1 + +struct kgsl_mem_entry { + struct kref refcount; + struct kgsl_memdesc memdesc; + int memtype; + struct file *file_ptr; + struct list_head list; + uint32_t free_timestamp; + /* back pointer to private structure under whose context this + * allocation is made */ + struct kgsl_process_private *priv; +}; + +#ifdef CONFIG_MSM_KGSL_MMU_PAGE_FAULT +#define MMU_CONFIG 2 +#else +#define MMU_CONFIG 1 +#endif + +void kgsl_mem_entry_destroy(struct kref *kref); +uint8_t *kgsl_gpuaddr_to_vaddr(const struct kgsl_memdesc *memdesc, + unsigned int gpuaddr, unsigned int *size); +struct kgsl_mem_entry *kgsl_sharedmem_find_region( + struct kgsl_process_private *private, unsigned int gpuaddr, + size_t size); +int kgsl_idle(struct kgsl_device *device, unsigned int timeout); +int kgsl_setstate(struct kgsl_device *device, uint32_t flags); + +static inline void kgsl_regread(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int *value) +{ + device->ftbl.device_regread(device, offsetwords, value); +} + +static inline void kgsl_regwrite(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int value) +{ + device->ftbl.device_regwrite(device, offsetwords, value); +} + +static inline void kgsl_regread_isr(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int *value) +{ + device->ftbl.device_regread_isr(device, offsetwords, value); +} + +static inline void kgsl_regwrite_isr(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int value) +{ + device->ftbl.device_regwrite_isr(device, offsetwords, value); +} + +int kgsl_check_timestamp(struct kgsl_device *device, unsigned int timestamp); + +int kgsl_register_ts_notifier(struct kgsl_device *device, + struct notifier_block *nb); + +int kgsl_unregister_ts_notifier(struct kgsl_device *device, + struct notifier_block *nb); + +int kgsl_device_platform_probe(struct kgsl_device *device, + irqreturn_t (*dev_isr) (int, void*)); +void kgsl_device_platform_remove(struct kgsl_device *device); + +extern const struct dev_pm_ops kgsl_pm_ops; + +int kgsl_suspend_driver(struct platform_device *pdev, pm_message_t state); +int kgsl_resume_driver(struct platform_device *pdev); + +#ifdef CONFIG_MSM_KGSL_DRM +extern int kgsl_drm_init(struct platform_device *dev); +extern void kgsl_drm_exit(void); +extern void kgsl_gpu_mem_flush(int op); +#else +static inline int kgsl_drm_init(struct platform_device *dev) +{ + return 0; +} + +static inline void kgsl_drm_exit(void) +{ +} +#endif + +static inline int kgsl_gpuaddr_in_memdesc(const struct kgsl_memdesc *memdesc, + unsigned int gpuaddr) +{ + if (gpuaddr >= memdesc->gpuaddr && (gpuaddr + sizeof(unsigned int)) <= + (memdesc->gpuaddr + memdesc->size)) { + return 1; + } + return 0; +} + +static inline struct kgsl_device *kgsl_device_from_dev(struct device *dev) +{ + int i; + + for (i = 0; i < KGSL_DEVICE_MAX; i++) { + if (kgsl_driver.devp[i] && kgsl_driver.devp[i]->dev == dev) + return kgsl_driver.devp[i]; + } + + return NULL; +} + +static inline bool timestamp_cmp(unsigned int new, unsigned int old) +{ + int ts_diff = new - old; + return (ts_diff >= 0) || (ts_diff < -20000); +} + +static inline void +kgsl_mem_entry_get(struct kgsl_mem_entry *entry) +{ + kref_get(&entry->refcount); +} + +static inline void +kgsl_mem_entry_put(struct kgsl_mem_entry *entry) +{ + kref_put(&entry->refcount, kgsl_mem_entry_destroy); +} + +#endif /* __KGSL_H */ diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c new file mode 100644 index 0000000000000..6da93487f4cef --- /dev/null +++ b/drivers/gpu/msm/kgsl_cffdump.c @@ -0,0 +1,711 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* #define DEBUG */ +#define ALIGN_CPU + +#include +#include +#include +#include +#include +#include + +#include "kgsl.h" +#include "kgsl_cffdump.h" +#include "kgsl_debugfs.h" + +static struct rchan *chan; +static struct dentry *dir; +static int suspended; +static size_t dropped; +static size_t subbuf_size = 256*1024; +static size_t n_subbufs = 64; + +/* forward declarations */ +static void destroy_channel(void); +static struct rchan *create_channel(unsigned subbuf_size, unsigned n_subbufs); + +static spinlock_t cffdump_lock; +static ulong serial_nr; +static ulong total_bytes; +static ulong total_syncmem; +static long last_sec; + +#define MEMBUF_SIZE 64 + +#define CFF_OP_WRITE_REG 0x00000002 +struct cff_op_write_reg { + unsigned char op; + uint addr; + uint value; +} __attribute__((packed)); + +#define CFF_OP_POLL_REG 0x00000004 +struct cff_op_poll_reg { + unsigned char op; + uint addr; + uint value; + uint mask; +} __attribute__((packed)); + +#define CFF_OP_WAIT_IRQ 0x00000005 +struct cff_op_wait_irq { + unsigned char op; +} __attribute__((packed)); + +#define CFF_OP_VERIFY_MEM_FILE 0x00000007 +#define CFF_OP_RMW 0x0000000a + +#define CFF_OP_WRITE_MEM 0x0000000b +struct cff_op_write_mem { + unsigned char op; + uint addr; + uint value; +} __attribute__((packed)); + +#define CFF_OP_WRITE_MEMBUF 0x0000000c +struct cff_op_write_membuf { + unsigned char op; + uint addr; + ushort count; + uint buffer[MEMBUF_SIZE]; +} __attribute__((packed)); + +#define CFF_OP_EOF 0xffffffff +struct cff_op_eof { + unsigned char op; +} __attribute__((packed)); + + +static void b64_encodeblock(unsigned char in[3], unsigned char out[4], int len) +{ + static const char tob64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmno" + "pqrstuvwxyz0123456789+/"; + + out[0] = tob64[in[0] >> 2]; + out[1] = tob64[((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4)]; + out[2] = (unsigned char) (len > 1 ? tob64[((in[1] & 0x0f) << 2) + | ((in[2] & 0xc0) >> 6)] : '='); + out[3] = (unsigned char) (len > 2 ? tob64[in[2] & 0x3f] : '='); +} + +static void b64_encode(const unsigned char *in_buf, int in_size, + unsigned char *out_buf, int out_bufsize, int *out_size) +{ + unsigned char in[3], out[4]; + int i, len; + + *out_size = 0; + while (in_size > 0) { + len = 0; + for (i = 0; i < 3; ++i) { + if (in_size-- > 0) { + in[i] = *in_buf++; + ++len; + } else + in[i] = 0; + } + if (len) { + b64_encodeblock(in, out, len); + if (out_bufsize < 4) { + pr_warn("kgsl: cffdump: %s: out of buffer\n", + __func__); + return; + } + for (i = 0; i < 4; ++i) + *out_buf++ = out[i]; + *out_size += 4; + out_bufsize -= 4; + } + } +} + +#define KLOG_TMPBUF_SIZE (1024) +static void klog_printk(const char *fmt, ...) +{ + /* per-cpu klog formatting temporary buffer */ + static char klog_buf[NR_CPUS][KLOG_TMPBUF_SIZE]; + + va_list args; + int len; + char *cbuf; + unsigned long flags; + + local_irq_save(flags); + cbuf = klog_buf[smp_processor_id()]; + va_start(args, fmt); + len = vsnprintf(cbuf, KLOG_TMPBUF_SIZE, fmt, args); + total_bytes += len; + va_end(args); + relay_write(chan, cbuf, len); + local_irq_restore(flags); +} + +static struct cff_op_write_membuf cff_op_write_membuf; +static void cffdump_membuf(int id, unsigned char *out_buf, int out_bufsize) +{ + void *data; + int len, out_size; + struct cff_op_write_mem cff_op_write_mem; + + uint addr = cff_op_write_membuf.addr + - sizeof(uint)*cff_op_write_membuf.count; + + if (!cff_op_write_membuf.count) { + pr_warn("kgsl: cffdump: membuf: count == 0, skipping"); + return; + } + + if (cff_op_write_membuf.count != 1) { + cff_op_write_membuf.op = CFF_OP_WRITE_MEMBUF; + cff_op_write_membuf.addr = addr; + len = sizeof(cff_op_write_membuf) - + sizeof(uint)*(MEMBUF_SIZE - cff_op_write_membuf.count); + data = &cff_op_write_membuf; + } else { + cff_op_write_mem.op = CFF_OP_WRITE_MEM; + cff_op_write_mem.addr = addr; + cff_op_write_mem.value = cff_op_write_membuf.buffer[0]; + data = &cff_op_write_mem; + len = sizeof(cff_op_write_mem); + } + b64_encode(data, len, out_buf, out_bufsize, &out_size); + out_buf[out_size] = 0; + klog_printk("%ld:%d;%s\n", ++serial_nr, id, out_buf); + cff_op_write_membuf.count = 0; + cff_op_write_membuf.addr = 0; +} + +static void cffdump_printline(int id, uint opcode, uint op1, uint op2, + uint op3) +{ + struct cff_op_write_reg cff_op_write_reg; + struct cff_op_poll_reg cff_op_poll_reg; + struct cff_op_wait_irq cff_op_wait_irq; + struct cff_op_eof cff_op_eof; + unsigned char out_buf[sizeof(cff_op_write_membuf)/3*4 + 16]; + void *data; + int len = 0, out_size; + long cur_secs; + + spin_lock(&cffdump_lock); + if (opcode == CFF_OP_WRITE_MEM) { + if (op1 < 0x40000000 || op1 >= 0x60000000) + KGSL_CORE_ERR("addr out-of-range: op1=%08x", op1); + if ((cff_op_write_membuf.addr != op1 && + cff_op_write_membuf.count) + || (cff_op_write_membuf.count == MEMBUF_SIZE)) + cffdump_membuf(id, out_buf, sizeof(out_buf)); + + cff_op_write_membuf.buffer[cff_op_write_membuf.count++] = op2; + cff_op_write_membuf.addr = op1 + sizeof(uint); + spin_unlock(&cffdump_lock); + return; + } else if (cff_op_write_membuf.count) + cffdump_membuf(id, out_buf, sizeof(out_buf)); + spin_unlock(&cffdump_lock); + + switch (opcode) { + case CFF_OP_WRITE_REG: + cff_op_write_reg.op = opcode; + cff_op_write_reg.addr = op1; + cff_op_write_reg.value = op2; + data = &cff_op_write_reg; + len = sizeof(cff_op_write_reg); + break; + + case CFF_OP_POLL_REG: + cff_op_poll_reg.op = opcode; + cff_op_poll_reg.addr = op1; + cff_op_poll_reg.value = op2; + cff_op_poll_reg.mask = op3; + data = &cff_op_poll_reg; + len = sizeof(cff_op_poll_reg); + break; + + case CFF_OP_WAIT_IRQ: + cff_op_wait_irq.op = opcode; + data = &cff_op_wait_irq; + len = sizeof(cff_op_wait_irq); + break; + + case CFF_OP_EOF: + cff_op_eof.op = opcode; + data = &cff_op_eof; + len = sizeof(cff_op_eof); + break; + } + + if (len) { + b64_encode(data, len, out_buf, sizeof(out_buf), &out_size); + out_buf[out_size] = 0; + klog_printk("%ld:%d;%s\n", ++serial_nr, id, out_buf); + } else + pr_warn("kgsl: cffdump: unhandled opcode: %d\n", opcode); + + cur_secs = get_seconds(); + if ((cur_secs - last_sec) > 10 || (last_sec - cur_secs) > 10) { + pr_info("kgsl: cffdump: total [bytes:%lu kB, syncmem:%lu kB], " + "seq#: %lu\n", total_bytes/1024, total_syncmem/1024, + serial_nr); + last_sec = cur_secs; + } +} + +void kgsl_cffdump_init() +{ + struct dentry *debugfs_dir = kgsl_get_debugfs_dir(); + +#ifdef ALIGN_CPU + cpumask_t mask; + + cpumask_clear(&mask); + cpumask_set_cpu(1, &mask); + sched_setaffinity(0, &mask); +#endif + if (!debugfs_dir || IS_ERR(debugfs_dir)) { + KGSL_CORE_ERR("Debugfs directory is bad\n"); + return; + } + + kgsl_cff_dump_enable = 1; + + spin_lock_init(&cffdump_lock); + + dir = debugfs_create_dir("cff", debugfs_dir); + if (!dir) { + KGSL_CORE_ERR("debugfs_create_dir failed\n"); + return; + } + + chan = create_channel(subbuf_size, n_subbufs); +} + +void kgsl_cffdump_destroy() +{ + if (chan) + relay_flush(chan); + destroy_channel(); + if (dir) + debugfs_remove(dir); +} + +void kgsl_cffdump_open(enum kgsl_deviceid device_id) +{ +} + +void kgsl_cffdump_close(enum kgsl_deviceid device_id) +{ + cffdump_printline(device_id, CFF_OP_EOF, 0, 0, 0); +} + +void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv, + const struct kgsl_memdesc *memdesc, uint gpuaddr, uint sizebytes, + bool clean_cache) +{ + const void *src; + uint host_size; + uint physaddr; + + if (!kgsl_cff_dump_enable) + return; + + total_syncmem += sizebytes; + + if (memdesc == NULL) { + struct kgsl_mem_entry *entry; + spin_lock(&dev_priv->process_priv->mem_lock); + entry = kgsl_sharedmem_find_region(dev_priv->process_priv, + gpuaddr, sizebytes); + spin_unlock(&dev_priv->process_priv->mem_lock); + if (entry == NULL) { + KGSL_CORE_ERR("did not find mapping " + "for gpuaddr: 0x%08x\n", gpuaddr); + return; + } + memdesc = &entry->memdesc; + } + BUG_ON(memdesc->gpuaddr == 0); + BUG_ON(gpuaddr == 0); + physaddr = kgsl_get_realaddr(memdesc) + (gpuaddr - memdesc->gpuaddr); + + src = kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr, &host_size); + if (src == NULL || host_size < sizebytes) { + KGSL_CORE_ERR(("did not find mapping for " + "gpuaddr: 0x%08x, m->host: 0x%p, phys: 0x%08x\n", + gpuaddr, memdesc->hostptr, memdesc->physaddr); + return; + } + + if (clean_cache) { + /* Ensure that this memory region is not read from the + * cache but fetched fresh */ + + dsb(); wmb(); mb(); + + kgsl_cache_range_op(memdesc->hostptr, memdesc->size, + memdesc->type, KGSL_CACHE_OP_INV); + } + + BUG_ON(physaddr > 0x66000000 && physaddr < 0x66ffffff); + while (sizebytes > 3) { + cffdump_printline(-1, CFF_OP_WRITE_MEM, physaddr, *(uint *)src, + 0); + physaddr += 4; + src += 4; + sizebytes -= 4; + } + if (sizebytes > 0) + cffdump_printline(-1, CFF_OP_WRITE_MEM, physaddr, *(uint *)src, + 0); +} + +void kgsl_cffdump_setmem(uint addr, uint value, uint sizebytes) +{ + if (!kgsl_cff_dump_enable) + return; + + BUG_ON(addr > 0x66000000 && addr < 0x66ffffff); + while (sizebytes > 3) { + /* Use 32bit memory writes as long as there's at least + * 4 bytes left */ + cffdump_printline(-1, CFF_OP_WRITE_MEM, addr, value, 0); + addr += 4; + sizebytes -= 4; + } + if (sizebytes > 0) + cffdump_printline(-1, CFF_OP_WRITE_MEM, addr, value, 0); +} + +void kgsl_cffdump_regwrite(enum kgsl_deviceid device_id, uint addr, + uint value) +{ + if (!kgsl_cff_dump_enable) + return; + + cffdump_printline(device_id, CFF_OP_WRITE_REG, addr, value, 0); +} + +void kgsl_cffdump_regpoll(enum kgsl_deviceid device_id, uint addr, + uint value, uint mask) +{ + if (!kgsl_cff_dump_enable) + return; + + cffdump_printline(device_id, CFF_OP_POLL_REG, addr, value, mask); +} + +void kgsl_cffdump_slavewrite(uint addr, uint value) +{ + if (!kgsl_cff_dump_enable) + return; + + cffdump_printline(-1, CFF_OP_WRITE_REG, addr, value, 0); +} + +int kgsl_cffdump_waitirq(void) +{ + if (!kgsl_cff_dump_enable) + return 0; + + cffdump_printline(-1, CFF_OP_WAIT_IRQ, 0, 0, 0); + + return 1; +} +EXPORT_SYMBOL(kgsl_cffdump_waitirq); + +#define ADDRESS_STACK_SIZE 256 +#define GET_PM4_TYPE3_OPCODE(x) ((*(x) >> 8) & 0xFF) +static unsigned int kgsl_cffdump_addr_count; + +static bool kgsl_cffdump_handle_type3(struct kgsl_device_private *dev_priv, + uint *hostaddr, bool check_only) +{ + static uint addr_stack[ADDRESS_STACK_SIZE]; + static uint size_stack[ADDRESS_STACK_SIZE]; + + switch (GET_PM4_TYPE3_OPCODE(hostaddr)) { + case PM4_INDIRECT_BUFFER_PFD: + case PM4_INDIRECT_BUFFER: + { + /* traverse indirect buffers */ + int i; + uint ibaddr = hostaddr[1]; + uint ibsize = hostaddr[2]; + + /* is this address already in encountered? */ + for (i = 0; + i < kgsl_cffdump_addr_count && addr_stack[i] != ibaddr; + ++i) + ; + + if (kgsl_cffdump_addr_count == i) { + addr_stack[kgsl_cffdump_addr_count] = ibaddr; + size_stack[kgsl_cffdump_addr_count++] = ibsize; + + if (kgsl_cffdump_addr_count >= ADDRESS_STACK_SIZE) { + KGSL_CORE_ERR("stack overflow\n"); + return false; + } + + return kgsl_cffdump_parse_ibs(dev_priv, NULL, + ibaddr, ibsize, check_only); + } else if (size_stack[i] != ibsize) { + KGSL_CORE_ERR("gpuaddr: 0x%08x, " + "wc: %u, with size wc: %u already on the " + "stack\n", ibaddr, ibsize, size_stack[i]); + return false; + } + } + break; + } + + return true; +} + +/* + * Traverse IBs and dump them to test vector. Detect swap by inspecting + * register writes, keeping note of the current state, and dump + * framebuffer config to test vector + */ +bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv, + const struct kgsl_memdesc *memdesc, uint gpuaddr, int sizedwords, + bool check_only) +{ + static uint level; /* recursion level */ + bool ret = true; + uint host_size; + uint *hostaddr, *hoststart; + int dwords_left = sizedwords; /* dwords left in the current command + buffer */ + + if (level == 0) + kgsl_cffdump_addr_count = 0; + + if (memdesc == NULL) { + struct kgsl_mem_entry *entry; + spin_lock(&dev_priv->process_priv->mem_lock); + entry = kgsl_sharedmem_find_region(dev_priv->process_priv, + gpuaddr, sizedwords * sizeof(uint)); + spin_unlock(&dev_priv->process_priv->mem_lock); + if (entry == NULL) { + KGSL_CORE_ERR("did not find mapping " + "for gpuaddr: 0x%08x\n", gpuaddr); + return true; + } + memdesc = &entry->memdesc; + } + + hostaddr = (uint *)kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr, &host_size); + if (hostaddr == NULL) { + KGSL_CORE_ERR("did not find mapping for " + "gpuaddr: 0x%08x\n", gpuaddr); + return true; + } + + hoststart = hostaddr; + + level++; + + if (!memdesc->physaddr) { + KGSL_CORE_ERR("no physaddr"); + return true; + else { + dsb(); wmb(); mb(); + kgsl_cache_range_op(memdesc->hostptr, memdesc->size, + memdesc->type, KGSL_CACHE_OP_INV); + } + +#ifdef DEBUG + pr_info("kgsl: cffdump: ib: gpuaddr:0x%08x, wc:%d, hptr:%p\n", + gpuaddr, sizedwords, hostaddr); +#endif + + while (dwords_left > 0) { + int count = 0; /* dword count including packet header */ + bool cur_ret = true; + + switch (*hostaddr >> 30) { + case 0x0: /* type-0 */ + count = (*hostaddr >> 16)+2; + break; + case 0x1: /* type-1 */ + count = 2; + break; + case 0x3: /* type-3 */ + count = ((*hostaddr >> 16) & 0x3fff) + 2; + cur_ret = kgsl_cffdump_handle_type3(dev_priv, + hostaddr, check_only); + break; + default: + pr_warn("kgsl: cffdump: parse-ib: unexpected type: " + "type:%d, word:0x%08x @ 0x%p, gpu:0x%08x\n", + *hostaddr >> 30, *hostaddr, hostaddr, + gpuaddr+4*(sizedwords-dwords_left)); + cur_ret = false; + count = dwords_left; + break; + } + +#ifdef DEBUG + if (!cur_ret) { + pr_info("kgsl: cffdump: bad sub-type: #:%d/%d, v:0x%08x" + " @ 0x%p[gb:0x%08x], level:%d\n", + sizedwords-dwords_left, sizedwords, *hostaddr, + hostaddr, gpuaddr+4*(sizedwords-dwords_left), + level); + + print_hex_dump(KERN_ERR, level == 1 ? "IB1:" : "IB2:", + DUMP_PREFIX_OFFSET, 32, 4, hoststart, + sizedwords*4, 0); + } +#endif + ret = ret && cur_ret; + + /* jump to next packet */ + dwords_left -= count; + hostaddr += count; + cur_ret = dwords_left >= 0; + +#ifdef DEBUG + if (!cur_ret) { + pr_info("kgsl: cffdump: bad count: c:%d, #:%d/%d, " + "v:0x%08x @ 0x%p[gb:0x%08x], level:%d\n", + count, sizedwords-(dwords_left+count), + sizedwords, *(hostaddr-count), hostaddr-count, + gpuaddr+4*(sizedwords-(dwords_left+count)), + level); + + print_hex_dump(KERN_ERR, level == 1 ? "IB1:" : "IB2:", + DUMP_PREFIX_OFFSET, 32, 4, hoststart, + sizedwords*4, 0); + } +#endif + + ret = ret && cur_ret; + } + + if (!ret) + pr_info("kgsl: cffdump: parsing failed: gpuaddr:0x%08x, " + "host:0x%p, wc:%d\n", gpuaddr, hoststart, sizedwords); + + if (!check_only) { +#ifdef DEBUG + uint offset = gpuaddr - memdesc->gpuaddr; + pr_info("kgsl: cffdump: ib-dump: hostptr:%p, gpuaddr:%08x, " + "physaddr:%08x, offset:%d, size:%d", hoststart, + gpuaddr, memdesc->physaddr + offset, offset, + sizedwords*4); +#endif + kgsl_cffdump_syncmem(dev_priv, memdesc, gpuaddr, sizedwords*4, + false); + } + + level--; + + return ret; +} + +static int subbuf_start_handler(struct rchan_buf *buf, + void *subbuf, void *prev_subbuf, uint prev_padding) +{ + pr_debug("kgsl: cffdump: subbuf_start_handler(subbuf=%p, prev_subbuf" + "=%p, prev_padding=%08x)\n", subbuf, prev_subbuf, prev_padding); + + if (relay_buf_full(buf)) { + if (!suspended) { + suspended = 1; + pr_warn("kgsl: cffdump: relay: cpu %d buffer full!!!\n", + smp_processor_id()); + } + dropped++; + return 0; + } else if (suspended) { + suspended = 0; + pr_warn("kgsl: cffdump: relay: cpu %d buffer no longer full.\n", + smp_processor_id()); + } + + subbuf_start_reserve(buf, 0); + return 1; +} + +static struct dentry *create_buf_file_handler(const char *filename, + struct dentry *parent, int mode, struct rchan_buf *buf, + int *is_global) +{ + return debugfs_create_file(filename, mode, parent, buf, + &relay_file_operations); +} + +/* + * file_remove() default callback. Removes relay file in debugfs. + */ +static int remove_buf_file_handler(struct dentry *dentry) +{ + pr_info("kgsl: cffdump: %s()\n", __func__); + debugfs_remove(dentry); + return 0; +} + +/* + * relay callbacks + */ +static struct rchan_callbacks relay_callbacks = { + .subbuf_start = subbuf_start_handler, + .create_buf_file = create_buf_file_handler, + .remove_buf_file = remove_buf_file_handler, +}; + +/** + * create_channel - creates channel /debug/klog/cpuXXX + * + * Creates channel along with associated produced/consumed control files + * + * Returns channel on success, NULL otherwise + */ +static struct rchan *create_channel(unsigned subbuf_size, unsigned n_subbufs) +{ + struct rchan *chan; + + pr_info("kgsl: cffdump: relay: create_channel: subbuf_size %u, " + "n_subbufs %u, dir 0x%p\n", subbuf_size, n_subbufs, dir); + + chan = relay_open("cpu", dir, subbuf_size, + n_subbufs, &relay_callbacks, NULL); + if (!chan) { + KGSL_CORE_ERR("relay_open failed\n"); + return NULL; + } + + suspended = 0; + dropped = 0; + + return chan; +} + +/** + * destroy_channel - destroys channel /debug/kgsl/cff/cpuXXX + * + * Destroys channel along with associated produced/consumed control files + */ +static void destroy_channel(void) +{ + pr_info("kgsl: cffdump: relay: destroy_channel\n"); + if (chan) { + relay_close(chan); + chan = NULL; + } +} + diff --git a/drivers/gpu/msm/kgsl_cffdump.h b/drivers/gpu/msm/kgsl_cffdump.h new file mode 100644 index 0000000000000..147dda92c16ae --- /dev/null +++ b/drivers/gpu/msm/kgsl_cffdump.h @@ -0,0 +1,73 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __KGSL_CFFDUMP_H +#define __KGSL_CFFDUMP_H + +#ifdef CONFIG_MSM_KGSL_CFF_DUMP + +#include + +#include "kgsl_device.h" + +void kgsl_cffdump_init(void); +void kgsl_cffdump_destroy(void); +void kgsl_cffdump_open(enum kgsl_deviceid device_id); +void kgsl_cffdump_close(enum kgsl_deviceid device_id); +void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv, + const struct kgsl_memdesc *memdesc, uint physaddr, uint sizebytes, + bool clean_cache); +void kgsl_cffdump_setmem(uint addr, uint value, uint sizebytes); +void kgsl_cffdump_regwrite(enum kgsl_deviceid device_id, uint addr, + uint value); +void kgsl_cffdump_regpoll(enum kgsl_deviceid device_id, uint addr, + uint value, uint mask); +bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv, + const struct kgsl_memdesc *memdesc, uint gpuaddr, int sizedwords, + bool check_only); +static inline bool kgsl_cffdump_flags_no_memzero(void) { return true; } + +#else + +#define kgsl_cffdump_init() (void)0 +#define kgsl_cffdump_destroy() (void)0 +#define kgsl_cffdump_open(device_id) (void)0 +#define kgsl_cffdump_close(device_id) (void)0 +#define kgsl_cffdump_syncmem(dev_priv, memdesc, addr, sizebytes, clean_cache) \ + (void) 0 +#define kgsl_cffdump_setmem(addr, value, sizebytes) (void)0 +#define kgsl_cffdump_regwrite(device_id, addr, value) (void)0 +#define kgsl_cffdump_regpoll(device_id, addr, value, mask) (void)0 +#define kgsl_cffdump_parse_ibs(dev_priv, memdesc, gpuaddr, \ + sizedwords, check_only) true +#define kgsl_cffdump_flags_no_memzero() true + +#endif /* CONFIG_MSM_KGSL_CFF_DUMP */ + +#endif /* __KGSL_CFFDUMP_H */ diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c new file mode 100644 index 0000000000000..364ab1dcea7cd --- /dev/null +++ b/drivers/gpu/msm/kgsl_debugfs.c @@ -0,0 +1,81 @@ +/* Copyright (c) 2002,2008-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include + +#include "kgsl.h" + +/*default log levels is error for everything*/ +#define KGSL_LOG_LEVEL_DEFAULT 3 +#define KGSL_LOG_LEVEL_MAX 7 + +struct dentry *kgsl_debugfs_dir; + +static inline int kgsl_log_set(unsigned int *log_val, void *data, u64 val) +{ + *log_val = min((unsigned int)val, (unsigned int)KGSL_LOG_LEVEL_MAX); + return 0; +} + +#define KGSL_DEBUGFS_LOG(__log) \ +static int __log ## _set(void *data, u64 val) \ +{ \ + struct kgsl_device *device = data; \ + return kgsl_log_set(&device->__log, data, val); \ +} \ +static int __log ## _get(void *data, u64 *val) \ +{ \ + struct kgsl_device *device = data; \ + *val = device->__log; \ + return 0; \ +} \ +DEFINE_SIMPLE_ATTRIBUTE(__log ## _fops, \ +__log ## _get, __log ## _set, "%llu\n"); \ + +KGSL_DEBUGFS_LOG(drv_log); +KGSL_DEBUGFS_LOG(cmd_log); +KGSL_DEBUGFS_LOG(ctxt_log); +KGSL_DEBUGFS_LOG(mem_log); +KGSL_DEBUGFS_LOG(pwr_log); + +void kgsl_device_debugfs_init(struct kgsl_device *device) +{ + if (kgsl_debugfs_dir && !IS_ERR(kgsl_debugfs_dir)) + device->d_debugfs = debugfs_create_dir(device->name, + kgsl_debugfs_dir); + + if (!device->d_debugfs || IS_ERR(device->d_debugfs)) + return; + + device->cmd_log = KGSL_LOG_LEVEL_DEFAULT; + device->ctxt_log = KGSL_LOG_LEVEL_DEFAULT; + device->drv_log = KGSL_LOG_LEVEL_DEFAULT; + device->mem_log = KGSL_LOG_LEVEL_DEFAULT; + device->pwr_log = KGSL_LOG_LEVEL_DEFAULT; + + debugfs_create_file("log_level_cmd", 0644, device->d_debugfs, device, + &cmd_log_fops); + debugfs_create_file("log_level_ctxt", 0644, device->d_debugfs, device, + &ctxt_log_fops); + debugfs_create_file("log_level_drv", 0644, device->d_debugfs, device, + &drv_log_fops); + debugfs_create_file("log_level_mem", 0644, device->d_debugfs, device, + &mem_log_fops); + debugfs_create_file("log_level_pwr", 0644, device->d_debugfs, device, + &pwr_log_fops); +} + +void kgsl_core_debugfs_init(void) +{ + kgsl_debugfs_dir = debugfs_create_dir("kgsl", 0); +} diff --git a/drivers/gpu/msm/kgsl_debugfs.h b/drivers/gpu/msm/kgsl_debugfs.h new file mode 100644 index 0000000000000..1e36fabba65d2 --- /dev/null +++ b/drivers/gpu/msm/kgsl_debugfs.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2002,2008-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _KGSL_DEBUGFS_H +#define _KGSL_DEBUGFS_H + +struct kgsl_device; + +#ifdef CONFIG_DEBUG_FS +void kgsl_core_debugfs_init(void); +void kgsl_device_debugfs_init(struct kgsl_device *device); + +extern struct dentry *kgsl_debugfs_dir; +static inline struct dentry *kgsl_get_debugfs_dir(void) +{ + return kgsl_debugfs_dir; +} + +#else +static inline void kgsl_core_debugfs_init(void) { } +static inline void kgsl_device_debugfs_init(struct kgsl_device *device) { } + +static inline struct dentry *kgsl_get_debugfs_dir(void) { return NULL; } + +#endif + +#endif diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h new file mode 100644 index 0000000000000..1a515d230e1ef --- /dev/null +++ b/drivers/gpu/msm/kgsl_device.h @@ -0,0 +1,242 @@ +/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __KGSL_DEVICE_H +#define __KGSL_DEVICE_H + +#include +#include + +#include "kgsl_mmu.h" +#include "kgsl_pwrctrl.h" +#include "kgsl_log.h" + +#define KGSL_TIMEOUT_NONE 0 +#define KGSL_TIMEOUT_DEFAULT 0xFFFFFFFF + +#define FIRST_TIMEOUT (HZ / 2) + +#define KGSL_CHIPID_YAMATODX_REV21 0x20100 +#define KGSL_CHIPID_YAMATODX_REV211 0x20101 +#define KGSL_CHIPID_LEIA_REV470_TEMP 0x10001 +#define KGSL_CHIPID_LEIA_REV470 0x2010000 + +/* KGSL device state is initialized to INIT when platform_probe * + * sucessfully initialized the device. Once a device has been opened * + * (started) it becomes active. NAP implies that only low latency * + * resources (for now clocks on some platforms) are off. SLEEP implies * + * that the KGSL module believes a device is idle (has been inactive * + * past its timer) and all system resources are released. SUSPEND is * + * requested by the kernel and will be enforced upon all open devices. */ + +#define KGSL_STATE_NONE 0x00000000 +#define KGSL_STATE_INIT 0x00000001 +#define KGSL_STATE_ACTIVE 0x00000002 +#define KGSL_STATE_NAP 0x00000004 +#define KGSL_STATE_SLEEP 0x00000008 +#define KGSL_STATE_SUSPEND 0x00000010 +#define KGSL_STATE_HUNG 0x00000020 +#define KGSL_STATE_DUMP_AND_RECOVER 0x00000040 + +#define KGSL_GRAPHICS_MEMORY_LOW_WATERMARK 0x1000000 + +#define KGSL_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK))) + +struct kgsl_device; +struct platform_device; +struct kgsl_device_private; +struct kgsl_context; + +struct kgsl_functable { + void (*device_regread) (struct kgsl_device *device, + unsigned int offsetwords, + unsigned int *value); + void (*device_regwrite) (struct kgsl_device *device, + unsigned int offsetwords, + unsigned int value); + void (*device_regread_isr) (struct kgsl_device *device, + unsigned int offsetwords, + unsigned int *value); + void (*device_regwrite_isr) (struct kgsl_device *device, + unsigned int offsetwords, + unsigned int value); + int (*device_setstate) (struct kgsl_device *device, uint32_t flags); + int (*device_idle) (struct kgsl_device *device, unsigned int timeout); + unsigned int (*device_isidle) (struct kgsl_device *device); + int (*device_suspend_context) (struct kgsl_device *device); + int (*device_resume_context) (struct kgsl_device *device); + int (*device_start) (struct kgsl_device *device, unsigned int init_ram); + int (*device_stop) (struct kgsl_device *device); + int (*device_getproperty) (struct kgsl_device *device, + enum kgsl_property_type type, + void *value, + unsigned int sizebytes); + int (*device_waittimestamp) (struct kgsl_device *device, + unsigned int timestamp, + unsigned int msecs); + unsigned int (*device_readtimestamp) ( + struct kgsl_device *device, + enum kgsl_timestamp_type type); + int (*device_issueibcmds) (struct kgsl_device_private *dev_priv, + struct kgsl_context *context, + struct kgsl_ibdesc *ibdesc, + unsigned int sizedwords, + uint32_t *timestamp, + unsigned int flags); + int (*device_drawctxt_create) (struct kgsl_device_private *dev_priv, + uint32_t flags, + struct kgsl_context *context); + int (*device_drawctxt_destroy) (struct kgsl_device *device, + struct kgsl_context *context); + long (*device_ioctl) (struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data); + int (*device_setup_pt)(struct kgsl_device *device, + struct kgsl_pagetable *pagetable); + + int (*device_cleanup_pt)(struct kgsl_device *device, + struct kgsl_pagetable *pagetable); + unsigned int (*device_idle_calc)(struct kgsl_device *device); +}; + +struct kgsl_memregion { + unsigned char *mmio_virt_base; + unsigned int mmio_phys_base; + uint32_t gpu_base; + unsigned int sizebytes; +}; + +struct kgsl_device { + struct device *dev; + const char *name; + unsigned int ver_major; + unsigned int ver_minor; + uint32_t flags; + enum kgsl_deviceid id; + unsigned int chip_id; + struct kgsl_memregion regspace; + struct kgsl_memdesc memstore; + const char *iomemname; + + struct kgsl_mmu mmu; + struct completion hwaccess_gate; + struct kgsl_functable ftbl; + struct work_struct idle_check_ws; + struct timer_list idle_timer; + struct kgsl_pwrctrl pwrctrl; + int open_count; + + struct atomic_notifier_head ts_notifier_list; + struct mutex mutex; + uint32_t state; + uint32_t requested_state; + + struct list_head memqueue; + unsigned int active_cnt; + struct completion suspend_gate; + + wait_queue_head_t wait_queue; + struct workqueue_struct *work_queue; + struct platform_device *pdev; + struct completion recovery_gate; + struct dentry *d_debugfs; + struct idr context_idr; + + /* Logging levels */ + int cmd_log; + int ctxt_log; + int drv_log; + int mem_log; + int pwr_log; + struct wake_lock idle_wakelock; +}; + +struct kgsl_context { + uint32_t id; + + /* Pointer to the owning device instance */ + struct kgsl_device_private *dev_priv; + + /* Pointer to the device specific context information */ + void *devctxt; +}; + +struct kgsl_process_private { + unsigned int refcnt; + pid_t pid; + spinlock_t mem_lock; + struct list_head mem_list; + struct kgsl_pagetable *pagetable; + struct list_head list; + struct kobject *kobj; + + struct { + unsigned int vmalloc; + unsigned int vmalloc_max; + unsigned int exmem; + unsigned int exmem_max; + unsigned int flushes; + } stats; +}; + +struct kgsl_device_private { + struct kgsl_device *device; + struct kgsl_process_private *process_priv; +}; + +struct kgsl_device *kgsl_get_device(int dev_idx); + +static inline struct kgsl_mmu * +kgsl_get_mmu(struct kgsl_device *device) +{ + return (struct kgsl_mmu *) (device ? &device->mmu : NULL); +} + +static inline int kgsl_create_device_workqueue(struct kgsl_device *device) +{ + device->work_queue = create_workqueue(device->name); + if (!device->work_queue) { + KGSL_DRV_ERR(device, "create_workqueue(%s) failed\n", + device->name); + return -EINVAL; + } + return 0; +} + +static inline struct kgsl_context * +kgsl_find_context(struct kgsl_device_private *dev_priv, uint32_t id) +{ + struct kgsl_context *ctxt = + idr_find(&dev_priv->device->context_idr, id); + + /* Make sure that the context belongs to the current instance so + that other processes can't guess context IDs and mess things up */ + + return (ctxt && ctxt->dev_priv == dev_priv) ? ctxt : NULL; +} + +#endif /* __KGSL_DEVICE_H */ diff --git a/drivers/gpu/msm/kgsl_drm.c b/drivers/gpu/msm/kgsl_drm.c new file mode 100644 index 0000000000000..1e878e158d0a0 --- /dev/null +++ b/drivers/gpu/msm/kgsl_drm.c @@ -0,0 +1,1690 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* Implements an interface between KGSL and the DRM subsystem. For now this + * is pretty simple, but it will take on more of the workload as time goes + * on + */ +#include "drmP.h" +#include "drm.h" +#include +#include + +#include "kgsl.h" +#include "kgsl_device.h" +#include "kgsl_drm.h" +#include "kgsl_mmu.h" +#include "kgsl_sharedmem.h" + +#define DRIVER_AUTHOR "Qualcomm" +#define DRIVER_NAME "kgsl" +#define DRIVER_DESC "KGSL DRM" +#define DRIVER_DATE "20100127" + +#define DRIVER_MAJOR 2 +#define DRIVER_MINOR 1 +#define DRIVER_PATCHLEVEL 1 + +#define DRM_KGSL_GEM_FLAG_MAPPED (1 << 0) + +#define ENTRY_EMPTY -1 +#define ENTRY_NEEDS_CLEANUP -2 + +#define DRM_KGSL_NUM_FENCE_ENTRIES (DRM_KGSL_HANDLE_WAIT_ENTRIES << 2) +#define DRM_KGSL_HANDLE_WAIT_ENTRIES 5 + +/* Returns true if the memory type is in PMEM */ + +#ifdef CONFIG_KERNEL_PMEM_SMI_REGION +#define TYPE_IS_PMEM(_t) \ + (((_t & DRM_KGSL_GEM_TYPE_MEM_MASK) == DRM_KGSL_GEM_TYPE_EBI) || \ + ((_t & DRM_KGSL_GEM_TYPE_MEM_MASK) == DRM_KGSL_GEM_TYPE_SMI) || \ + ((_t) & DRM_KGSL_GEM_TYPE_PMEM)) +#else +#define TYPE_IS_PMEM(_t) \ + (((_t & DRM_KGSL_GEM_TYPE_MEM_MASK) == DRM_KGSL_GEM_TYPE_EBI) || \ + ((_t) & (DRM_KGSL_GEM_TYPE_PMEM | DRM_KGSL_GEM_PMEM_EBI))) +#endif + +/* Returns true if the memory type is regular */ + +#define TYPE_IS_MEM(_t) \ + (((_t & DRM_KGSL_GEM_TYPE_MEM_MASK) == DRM_KGSL_GEM_TYPE_KMEM) || \ + ((_t & DRM_KGSL_GEM_TYPE_MEM_MASK) == DRM_KGSL_GEM_TYPE_KMEM_NOCACHE) || \ + ((_t) & DRM_KGSL_GEM_TYPE_MEM)) + +#define TYPE_IS_FD(_t) ((_t) & DRM_KGSL_GEM_TYPE_FD_MASK) + +/* Returns true if KMEM region is uncached */ + +#define IS_MEM_UNCACHED(_t) \ + ((_t == DRM_KGSL_GEM_TYPE_KMEM_NOCACHE) || \ + (_t == DRM_KGSL_GEM_TYPE_KMEM) || \ + (TYPE_IS_MEM(_t) && (_t & DRM_KGSL_GEM_CACHE_WCOMBINE))) + +struct drm_kgsl_gem_object_wait_list_entry { + struct list_head list; + int pid; + int in_use; + wait_queue_head_t process_wait_q; +}; + +struct drm_kgsl_gem_object_fence { + int32_t fence_id; + unsigned int num_buffers; + int ts_valid; + unsigned int timestamp; + int ts_device; + int lockpid; + struct list_head buffers_in_fence; +}; + +struct drm_kgsl_gem_object_fence_list_entry { + struct list_head list; + int in_use; + struct drm_gem_object *gem_obj; +}; + +static int32_t fence_id = 0x1; + +static struct drm_kgsl_gem_object_fence + gem_buf_fence[DRM_KGSL_NUM_FENCE_ENTRIES]; + +struct drm_kgsl_gem_object { + struct drm_gem_object *obj; + uint32_t type; + struct kgsl_memdesc memdesc; + struct kgsl_pagetable *pagetable; + uint64_t mmap_offset; + int bufcount; + int flags; + struct list_head list; + int active; + + struct { + uint32_t offset; + uint32_t gpuaddr; + } bufs[DRM_KGSL_GEM_MAX_BUFFERS]; + + int bound; + int lockpid; + /* Put these here to avoid allocing all the time */ + struct drm_kgsl_gem_object_wait_list_entry + wait_entries[DRM_KGSL_HANDLE_WAIT_ENTRIES]; + /* Each object can only appear in a single fence */ + struct drm_kgsl_gem_object_fence_list_entry + fence_entries[DRM_KGSL_NUM_FENCE_ENTRIES]; + + struct list_head wait_list; +}; + +/* This is a global list of all the memory currently mapped in the MMU */ +static struct list_head kgsl_mem_list; + +static void kgsl_gem_mem_flush(struct kgsl_memdesc *memdesc, int type, int op) +{ + int cacheop = 0; + + switch (op) { + case DRM_KGSL_GEM_CACHE_OP_TO_DEV: + if (type & (DRM_KGSL_GEM_CACHE_WBACK | + DRM_KGSL_GEM_CACHE_WBACKWA)) + cacheop = KGSL_CACHE_OP_CLEAN; + + break; + + case DRM_KGSL_GEM_CACHE_OP_FROM_DEV: + if (type & (DRM_KGSL_GEM_CACHE_WBACK | + DRM_KGSL_GEM_CACHE_WBACKWA | + DRM_KGSL_GEM_CACHE_WTHROUGH)) + cacheop = KGSL_CACHE_OP_INV; + } + + kgsl_cache_range_op(memdesc, cacheop); +} + +/* Flush all the memory mapped in the MMU */ + +void kgsl_gpu_mem_flush(int op) +{ + struct drm_kgsl_gem_object *entry; + + list_for_each_entry(entry, &kgsl_mem_list, list) { + kgsl_gem_mem_flush(&entry->memdesc, entry->type, op); + } + + /* Takes care of WT/WC case. + * More useful when we go barrierless + */ + dmb(); +} + +/* TODO: + * Add vsync wait */ + +static int kgsl_drm_load(struct drm_device *dev, unsigned long flags) +{ + return 0; +} + +static int kgsl_drm_unload(struct drm_device *dev) +{ + return 0; +} + +struct kgsl_drm_device_priv { + struct kgsl_device *device[KGSL_DEVICE_MAX]; + struct kgsl_device_private *devpriv[KGSL_DEVICE_MAX]; +}; + +static int kgsl_ts_notifier_cb(struct notifier_block *blk, + unsigned long code, void *_param); + +static struct notifier_block kgsl_ts_nb[KGSL_DEVICE_MAX]; + +static int kgsl_drm_firstopen(struct drm_device *dev) +{ + int i; + + for (i = 0; i < KGSL_DEVICE_MAX; i++) { + struct kgsl_device *device = kgsl_get_device(i); + + if (device == NULL) + continue; + + kgsl_ts_nb[i].notifier_call = kgsl_ts_notifier_cb; + kgsl_register_ts_notifier(device, &kgsl_ts_nb[i]); + } + + return 0; +} + +void kgsl_drm_lastclose(struct drm_device *dev) +{ + int i; + + for (i = 0; i < KGSL_DEVICE_MAX; i++) { + struct kgsl_device *device = kgsl_get_device(i); + if (device == NULL) + continue; + + kgsl_unregister_ts_notifier(device, &kgsl_ts_nb[i]); + } +} + +void kgsl_drm_preclose(struct drm_device *dev, struct drm_file *file_priv) +{ +} + +static int kgsl_drm_suspend(struct drm_device *dev, pm_message_t state) +{ + return 0; +} + +static int kgsl_drm_resume(struct drm_device *dev) +{ + return 0; +} + +static void +kgsl_gem_free_mmap_offset(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + struct drm_gem_mm *mm = dev->mm_private; + struct drm_kgsl_gem_object *priv = obj->driver_private; + struct drm_map_list *list; + + list = &obj->map_list; + drm_ht_remove_item(&mm->offset_hash, &list->hash); + if (list->file_offset_node) { + drm_mm_put_block(list->file_offset_node); + list->file_offset_node = NULL; + } + + kfree(list->map); + list->map = NULL; + + priv->mmap_offset = 0; +} + +static int +kgsl_gem_memory_allocated(struct drm_gem_object *obj) +{ + struct drm_kgsl_gem_object *priv = obj->driver_private; + return priv->memdesc.size ? 1 : 0; +} + +static int +kgsl_gem_alloc_memory(struct drm_gem_object *obj) +{ + struct drm_kgsl_gem_object *priv = obj->driver_private; + int index; + + /* Return if the memory is already allocated */ + + if (kgsl_gem_memory_allocated(obj) || TYPE_IS_FD(priv->type)) + return 0; + + if (TYPE_IS_PMEM(priv->type)) { + int type; + + if (priv->type == DRM_KGSL_GEM_TYPE_EBI || + priv->type & DRM_KGSL_GEM_PMEM_EBI) + type = PMEM_MEMTYPE_EBI1; + else + type = PMEM_MEMTYPE_SMI; + + priv->memdesc.physaddr = + pmem_kalloc(obj->size * priv->bufcount, + type | PMEM_ALIGNMENT_4K); + + if (IS_ERR((void *) priv->memdesc.physaddr)) { + DRM_ERROR("Unable to allocate PMEM memory\n"); + return -ENOMEM; + } + + priv->memdesc.size = obj->size * priv->bufcount; + priv->memdesc.ops = &kgsl_contig_ops; + + } else if (TYPE_IS_MEM(priv->type)) { + priv->memdesc.hostptr = + vmalloc_user(obj->size * priv->bufcount); + + if (priv->memdesc.hostptr == NULL) { + DRM_ERROR("Unable to allocate vmalloc memory\n"); + return -ENOMEM; + } + + priv->memdesc.size = obj->size * priv->bufcount; + priv->memdesc.ops = &kgsl_vmalloc_ops; + } else + return -EINVAL; + + for (index = 0; index < priv->bufcount; index++) + priv->bufs[index].offset = index * obj->size; + + + return 0; +} + +#ifdef CONFIG_MSM_KGSL_MMU +static void +kgsl_gem_unmap(struct drm_gem_object *obj) +{ + struct drm_kgsl_gem_object *priv = obj->driver_private; + + if (!priv->flags & DRM_KGSL_GEM_FLAG_MAPPED) + return; + + kgsl_mmu_unmap(priv->pagetable, &priv->memdesc); + + kgsl_mmu_putpagetable(priv->pagetable); + priv->pagetable = NULL; + + if ((priv->type == DRM_KGSL_GEM_TYPE_KMEM) || + (priv->type & DRM_KGSL_GEM_CACHE_MASK)) + list_del(&priv->list); + + priv->flags &= ~DRM_KGSL_GEM_FLAG_MAPPED; +} +#else +static void +kgsl_gem_unmap(struct drm_gem_object *obj) +{ +} +#endif + +static void +kgsl_gem_free_memory(struct drm_gem_object *obj) +{ + struct drm_kgsl_gem_object *priv = obj->driver_private; + + if (!kgsl_gem_memory_allocated(obj) || TYPE_IS_FD(priv->type)) + return; + + kgsl_gem_mem_flush(&priv->memdesc, priv->type, + DRM_KGSL_GEM_CACHE_OP_FROM_DEV); + + kgsl_gem_unmap(obj); + + if (TYPE_IS_PMEM(priv->type)) + pmem_kfree(priv->memdesc.physaddr); + + kgsl_sharedmem_free(&priv->memdesc); +} + +int +kgsl_gem_init_object(struct drm_gem_object *obj) +{ + struct drm_kgsl_gem_object *priv; + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (priv == NULL) { + DRM_ERROR("Unable to create GEM object\n"); + return -ENOMEM; + } + + obj->driver_private = priv; + priv->obj = obj; + + return 0; +} + +void +kgsl_gem_free_object(struct drm_gem_object *obj) +{ + kgsl_gem_free_memory(obj); + kgsl_gem_free_mmap_offset(obj); + drm_gem_object_release(obj); + kfree(obj->driver_private); +} + +static int +kgsl_gem_create_mmap_offset(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + struct drm_gem_mm *mm = dev->mm_private; + struct drm_kgsl_gem_object *priv = obj->driver_private; + struct drm_map_list *list; + int msize; + + list = &obj->map_list; + list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL); + if (list->map == NULL) { + DRM_ERROR("Unable to allocate drm_map_list\n"); + return -ENOMEM; + } + + msize = obj->size * priv->bufcount; + + list->map->type = _DRM_GEM; + list->map->size = msize; + list->map->handle = obj; + + /* Allocate a mmap offset */ + list->file_offset_node = drm_mm_search_free(&mm->offset_manager, + msize / PAGE_SIZE, + 0, 0); + + if (!list->file_offset_node) { + DRM_ERROR("Failed to allocate offset for %d\n", obj->name); + kfree(list->map); + return -ENOMEM; + } + + list->file_offset_node = drm_mm_get_block(list->file_offset_node, + msize / PAGE_SIZE, 0); + + if (!list->file_offset_node) { + DRM_ERROR("Unable to create the file_offset_node\n"); + kfree(list->map); + return -ENOMEM; + } + + list->hash.key = list->file_offset_node->start; + if (drm_ht_insert_item(&mm->offset_hash, &list->hash)) { + DRM_ERROR("Failed to add to map hash\n"); + drm_mm_put_block(list->file_offset_node); + kfree(list->map); + return -ENOMEM; + } + + priv->mmap_offset = ((uint64_t) list->hash.key) << PAGE_SHIFT; + + return 0; +} + +int +kgsl_gem_obj_addr(int drm_fd, int handle, unsigned long *start, + unsigned long *len) +{ + struct file *filp; + struct drm_device *dev; + struct drm_file *file_priv; + struct drm_gem_object *obj; + struct drm_kgsl_gem_object *priv; + int ret = 0; + + filp = fget(drm_fd); + if (unlikely(filp == NULL)) { + DRM_ERROR("Unable to ghet the DRM file descriptor\n"); + return -EINVAL; + } + file_priv = filp->private_data; + if (unlikely(file_priv == NULL)) { + DRM_ERROR("Unable to get the file private data\n"); + fput(filp); + return -EINVAL; + } + dev = file_priv->minor->dev; + if (unlikely(dev == NULL)) { + DRM_ERROR("Unable to get the minor device\n"); + fput(filp); + return -EINVAL; + } + + obj = drm_gem_object_lookup(dev, file_priv, handle); + if (unlikely(obj == NULL)) { + DRM_ERROR("Invalid GEM handle %x\n", handle); + fput(filp); + return -EBADF; + } + + mutex_lock(&dev->struct_mutex); + priv = obj->driver_private; + + /* We can only use the MDP for PMEM regions */ + + if (TYPE_IS_PMEM(priv->type)) { + *start = priv->memdesc.physaddr + + priv->bufs[priv->active].offset; + + *len = priv->memdesc.size; + + kgsl_gem_mem_flush(&priv->memdesc, + priv->type, DRM_KGSL_GEM_CACHE_OP_TO_DEV); + } else { + *start = 0; + *len = 0; + ret = -EINVAL; + } + + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + + fput(filp); + return ret; +} + +static int +kgsl_gem_init_obj(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_gem_object *obj, + int *handle) +{ + struct drm_kgsl_gem_object *priv; + int ret, i; + + mutex_lock(&dev->struct_mutex); + priv = obj->driver_private; + + memset(&priv->memdesc, 0, sizeof(priv->memdesc)); + priv->bufcount = 1; + priv->active = 0; + priv->bound = 0; + + /* To preserve backwards compatability, the default memory source + is EBI */ + + priv->type = DRM_KGSL_GEM_TYPE_PMEM | DRM_KGSL_GEM_PMEM_EBI; + + ret = drm_gem_handle_create(file_priv, obj, handle); + + drm_gem_object_handle_unreference(obj); + INIT_LIST_HEAD(&priv->wait_list); + + for (i = 0; i < DRM_KGSL_HANDLE_WAIT_ENTRIES; i++) { + INIT_LIST_HEAD((struct list_head *) &priv->wait_entries[i]); + priv->wait_entries[i].pid = 0; + init_waitqueue_head(&priv->wait_entries[i].process_wait_q); + } + + for (i = 0; i < DRM_KGSL_NUM_FENCE_ENTRIES; i++) { + INIT_LIST_HEAD((struct list_head *) &priv->fence_entries[i]); + priv->fence_entries[i].in_use = 0; + priv->fence_entries[i].gem_obj = obj; + } + + mutex_unlock(&dev->struct_mutex); + return ret; +} + +int +kgsl_gem_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_kgsl_gem_create *create = data; + struct drm_gem_object *obj; + int ret, handle; + + /* Page align the size so we can allocate multiple buffers */ + create->size = ALIGN(create->size, 4096); + + obj = drm_gem_object_alloc(dev, create->size); + + if (obj == NULL) { + DRM_ERROR("Unable to allocate the GEM object\n"); + return -ENOMEM; + } + + ret = kgsl_gem_init_obj(dev, file_priv, obj, &handle); + if (ret) + return ret; + + create->handle = handle; + return 0; +} + +int +kgsl_gem_create_fd_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_kgsl_gem_create_fd *args = data; + struct file *file; + dev_t rdev; + struct fb_info *info; + struct drm_gem_object *obj; + struct drm_kgsl_gem_object *priv; + int ret, put_needed, handle; + + file = fget_light(args->fd, &put_needed); + + if (file == NULL) { + DRM_ERROR("Unable to get the file object\n"); + return -EBADF; + } + + rdev = file->f_dentry->d_inode->i_rdev; + + /* Only framebuffer objects are supported ATM */ + + if (MAJOR(rdev) != FB_MAJOR) { + DRM_ERROR("File descriptor is not a framebuffer\n"); + ret = -EBADF; + goto error_fput; + } + + info = registered_fb[MINOR(rdev)]; + + if (info == NULL) { + DRM_ERROR("Framebuffer minor %d is not registered\n", + MINOR(rdev)); + ret = -EBADF; + goto error_fput; + } + + obj = drm_gem_object_alloc(dev, info->fix.smem_len); + + if (obj == NULL) { + DRM_ERROR("Unable to allocate GEM object\n"); + ret = -ENOMEM; + goto error_fput; + } + + ret = kgsl_gem_init_obj(dev, file_priv, obj, &handle); + + if (ret) + goto error_fput; + + mutex_lock(&dev->struct_mutex); + + priv = obj->driver_private; + priv->memdesc.physaddr = info->fix.smem_start; + priv->type = DRM_KGSL_GEM_TYPE_FD_FBMEM; + + mutex_unlock(&dev->struct_mutex); + args->handle = handle; + +error_fput: + fput_light(file, put_needed); + + return ret; +} + +int +kgsl_gem_setmemtype_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_kgsl_gem_memtype *args = data; + struct drm_gem_object *obj; + struct drm_kgsl_gem_object *priv; + int ret = 0; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + + if (obj == NULL) { + DRM_ERROR("Invalid GEM handle %x\n", args->handle); + return -EBADF; + } + + mutex_lock(&dev->struct_mutex); + priv = obj->driver_private; + + if (TYPE_IS_FD(priv->type)) + ret = -EINVAL; + else { + if (TYPE_IS_PMEM(args->type) || TYPE_IS_MEM(args->type)) + priv->type = args->type; + else + ret = -EINVAL; + } + + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + + return ret; +} + +int +kgsl_gem_getmemtype_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_kgsl_gem_memtype *args = data; + struct drm_gem_object *obj; + struct drm_kgsl_gem_object *priv; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + + if (obj == NULL) { + DRM_ERROR("Invalid GEM handle %x\n", args->handle); + return -EBADF; + } + + mutex_lock(&dev->struct_mutex); + priv = obj->driver_private; + + args->type = priv->type; + + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + + return 0; +} + +int +kgsl_gem_unbind_gpu_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_kgsl_gem_bind_gpu *args = data; + struct drm_gem_object *obj; + struct drm_kgsl_gem_object *priv; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + + if (obj == NULL) { + DRM_ERROR("Invalid GEM handle %x\n", args->handle); + return -EBADF; + } + + mutex_lock(&dev->struct_mutex); + priv = obj->driver_private; + + if (--priv->bound == 0) + kgsl_gem_unmap(obj); + + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + return 0; +} + +#ifdef CONFIG_MSM_KGSL_MMU +static int +kgsl_gem_map(struct drm_gem_object *obj) +{ + struct drm_kgsl_gem_object *priv = obj->driver_private; + int index; + int ret = -EINVAL; + + if (priv->flags & DRM_KGSL_GEM_FLAG_MAPPED) + return 0; + + /* Get the global page table */ + + if (priv->pagetable == NULL) { + priv->pagetable = kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT); + + if (priv->pagetable == NULL) { + DRM_ERROR("Unable to get the GPU MMU pagetable\n"); + return -EINVAL; + } + } + + priv->memdesc.pagetable = priv->pagetable; + + ret = kgsl_mmu_map(priv->pagetable, &priv->memdesc, + GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); + + if (!ret) { + for (index = 0; index < priv->bufcount; index++) { + priv->bufs[index].gpuaddr = + priv->memdesc.gpuaddr + + priv->bufs[index].offset; + } + } + + /* Add cached memory to the list to be cached */ + + if (priv->type == DRM_KGSL_GEM_TYPE_KMEM || + priv->type & DRM_KGSL_GEM_CACHE_MASK) + list_add(&priv->list, &kgsl_mem_list); + + priv->flags |= DRM_KGSL_GEM_FLAG_MAPPED; + + return ret; +} +#else +static int +kgsl_gem_map(struct drm_gem_object *obj) +{ + struct drm_kgsl_gem_object *priv = obj->driver_private; + int index; + + if (TYPE_IS_PMEM(priv->type)) { + for (index = 0; index < priv->bufcount; index++) + priv->bufs[index].gpuaddr = + priv->memdesc.physaddr + priv->bufs[index].offset; + + return 0; + } + + return -EINVAL; +} +#endif + +int +kgsl_gem_bind_gpu_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_kgsl_gem_bind_gpu *args = data; + struct drm_gem_object *obj; + struct drm_kgsl_gem_object *priv; + int ret = 0; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + + if (obj == NULL) { + DRM_ERROR("Invalid GEM handle %x\n", args->handle); + return -EBADF; + } + + mutex_lock(&dev->struct_mutex); + priv = obj->driver_private; + + if (priv->bound++ == 0) { + + if (!kgsl_gem_memory_allocated(obj)) { + DRM_ERROR("Memory not allocated for this object\n"); + ret = -ENOMEM; + goto out; + } + + ret = kgsl_gem_map(obj); + + /* This is legacy behavior - use GET_BUFFERINFO instead */ + args->gpuptr = priv->bufs[0].gpuaddr; + } +out: + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + return ret; +} + +/* Allocate the memory and prepare it for CPU mapping */ + +int +kgsl_gem_alloc_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_kgsl_gem_alloc *args = data; + struct drm_gem_object *obj; + struct drm_kgsl_gem_object *priv; + int ret; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + + if (obj == NULL) { + DRM_ERROR("Invalid GEM handle %x\n", args->handle); + return -EBADF; + } + + mutex_lock(&dev->struct_mutex); + priv = obj->driver_private; + + ret = kgsl_gem_alloc_memory(obj); + + if (ret) { + DRM_ERROR("Unable to allocate object memory\n"); + } else if (!priv->mmap_offset) { + ret = kgsl_gem_create_mmap_offset(obj); + if (ret) + DRM_ERROR("Unable to create a mmap offset\n"); + } + + args->offset = priv->mmap_offset; + + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + + return ret; +} + +int +kgsl_gem_mmap_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_kgsl_gem_mmap *args = data; + struct drm_gem_object *obj; + unsigned long addr; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + + if (obj == NULL) { + DRM_ERROR("Invalid GEM handle %x\n", args->handle); + return -EBADF; + } + + down_write(¤t->mm->mmap_sem); + + addr = do_mmap(obj->filp, 0, args->size, + PROT_READ | PROT_WRITE, MAP_SHARED, + args->offset); + + up_write(¤t->mm->mmap_sem); + + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + + if (IS_ERR((void *) addr)) + return addr; + + args->hostptr = (uint32_t) addr; + return 0; +} + +/* This function is deprecated */ + +int +kgsl_gem_prep_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_kgsl_gem_prep *args = data; + struct drm_gem_object *obj; + struct drm_kgsl_gem_object *priv; + int ret; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + + if (obj == NULL) { + DRM_ERROR("Invalid GEM handle %x\n", args->handle); + return -EBADF; + } + + mutex_lock(&dev->struct_mutex); + priv = obj->driver_private; + + ret = kgsl_gem_alloc_memory(obj); + if (ret) { + DRM_ERROR("Unable to allocate object memory\n"); + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + return ret; + } + + if (priv->mmap_offset == 0) { + ret = kgsl_gem_create_mmap_offset(obj); + if (ret) { + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + return ret; + } + } + + args->offset = priv->mmap_offset; + args->phys = priv->memdesc.physaddr; + + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + + return 0; +} + +int +kgsl_gem_get_bufinfo_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_kgsl_gem_bufinfo *args = data; + struct drm_gem_object *obj; + struct drm_kgsl_gem_object *priv; + int ret = -EINVAL; + int index; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + + if (obj == NULL) { + DRM_ERROR("Invalid GEM handle %x\n", args->handle); + return -EBADF; + } + + mutex_lock(&dev->struct_mutex); + priv = obj->driver_private; + + if (!kgsl_gem_memory_allocated(obj)) { + DRM_ERROR("Memory not allocated for this object\n"); + goto out; + } + + for (index = 0; index < priv->bufcount; index++) { + args->offset[index] = priv->bufs[index].offset; + args->gpuaddr[index] = priv->bufs[index].gpuaddr; + } + + args->count = priv->bufcount; + args->active = priv->active; + + ret = 0; + +out: + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + + return ret; +} + +int +kgsl_gem_set_bufcount_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_kgsl_gem_bufcount *args = data; + struct drm_gem_object *obj; + struct drm_kgsl_gem_object *priv; + int ret = -EINVAL; + + if (args->bufcount < 1 || args->bufcount > DRM_KGSL_GEM_MAX_BUFFERS) + return -EINVAL; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + + if (obj == NULL) { + DRM_ERROR("Invalid GEM handle %x\n", args->handle); + return -EBADF; + } + + mutex_lock(&dev->struct_mutex); + priv = obj->driver_private; + + /* It is too much math to worry about what happens if we are already + allocated, so just bail if we are */ + + if (kgsl_gem_memory_allocated(obj)) { + DRM_ERROR("Memory already allocated - cannot change" + "number of buffers\n"); + goto out; + } + + priv->bufcount = args->bufcount; + ret = 0; + +out: + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + + return ret; +} + +int +kgsl_gem_set_active_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_kgsl_gem_active *args = data; + struct drm_gem_object *obj; + struct drm_kgsl_gem_object *priv; + int ret = -EINVAL; + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + + if (obj == NULL) { + DRM_ERROR("Invalid GEM handle %x\n", args->handle); + return -EBADF; + } + + mutex_lock(&dev->struct_mutex); + priv = obj->driver_private; + + if (args->active < 0 || args->active >= priv->bufcount) { + DRM_ERROR("Invalid active buffer %d\n", args->active); + goto out; + } + + priv->active = args->active; + ret = 0; + +out: + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + + return ret; +} + +int kgsl_gem_kmem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct drm_gem_object *obj = vma->vm_private_data; + struct drm_device *dev = obj->dev; + struct drm_kgsl_gem_object *priv; + unsigned long offset, pg; + struct page *page; + + mutex_lock(&dev->struct_mutex); + + priv = obj->driver_private; + + offset = (unsigned long) vmf->virtual_address - vma->vm_start; + pg = (unsigned long) priv->memdesc.hostptr + offset; + + page = vmalloc_to_page((void *) pg); + if (!page) { + mutex_unlock(&dev->struct_mutex); + return VM_FAULT_SIGBUS; + } + + get_page(page); + vmf->page = page; + + mutex_unlock(&dev->struct_mutex); + return 0; +} + +int kgsl_gem_phys_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct drm_gem_object *obj = vma->vm_private_data; + struct drm_device *dev = obj->dev; + struct drm_kgsl_gem_object *priv; + unsigned long offset, pfn; + int ret = 0; + + offset = ((unsigned long) vmf->virtual_address - vma->vm_start) >> + PAGE_SHIFT; + + mutex_lock(&dev->struct_mutex); + + priv = obj->driver_private; + + pfn = (priv->memdesc.physaddr >> PAGE_SHIFT) + offset; + ret = vm_insert_pfn(vma, + (unsigned long) vmf->virtual_address, pfn); + mutex_unlock(&dev->struct_mutex); + + switch (ret) { + case -ENOMEM: + case -EAGAIN: + return VM_FAULT_OOM; + case -EFAULT: + return VM_FAULT_SIGBUS; + default: + return VM_FAULT_NOPAGE; + } +} + +static struct vm_operations_struct kgsl_gem_kmem_vm_ops = { + .fault = kgsl_gem_kmem_fault, + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; + +static struct vm_operations_struct kgsl_gem_phys_vm_ops = { + .fault = kgsl_gem_phys_fault, + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; + +/* This is a clone of the standard drm_gem_mmap function modified to allow + us to properly map KMEM regions as well as the PMEM regions */ + +int msm_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *priv = filp->private_data; + struct drm_device *dev = priv->minor->dev; + struct drm_gem_mm *mm = dev->mm_private; + struct drm_local_map *map = NULL; + struct drm_gem_object *obj; + struct drm_hash_item *hash; + struct drm_kgsl_gem_object *gpriv; + int ret = 0; + + mutex_lock(&dev->struct_mutex); + + if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &hash)) { + mutex_unlock(&dev->struct_mutex); + return drm_mmap(filp, vma); + } + + map = drm_hash_entry(hash, struct drm_map_list, hash)->map; + if (!map || + ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) { + ret = -EPERM; + goto out_unlock; + } + + /* Check for valid size. */ + if (map->size < vma->vm_end - vma->vm_start) { + ret = -EINVAL; + goto out_unlock; + } + + obj = map->handle; + + gpriv = obj->driver_private; + + /* VM_PFNMAP is only for memory that doesn't use struct page + * in other words, not "normal" memory. If you try to use it + * with "normal" memory then the mappings don't get flushed. */ + + if (TYPE_IS_MEM(gpriv->type)) { + vma->vm_flags |= VM_RESERVED | VM_DONTEXPAND; + vma->vm_ops = &kgsl_gem_kmem_vm_ops; + } else { + vma->vm_flags |= VM_RESERVED | VM_IO | VM_PFNMAP | + VM_DONTEXPAND; + vma->vm_ops = &kgsl_gem_phys_vm_ops; + } + + vma->vm_private_data = map->handle; + + + /* Take care of requested caching policy */ + if (gpriv->type == DRM_KGSL_GEM_TYPE_KMEM || + gpriv->type & DRM_KGSL_GEM_CACHE_MASK) { + if (gpriv->type & DRM_KGSL_GEM_CACHE_WBACKWA) + vma->vm_page_prot = + pgprot_writebackwacache(vma->vm_page_prot); + else if (gpriv->type & DRM_KGSL_GEM_CACHE_WBACK) + vma->vm_page_prot = + pgprot_writebackcache(vma->vm_page_prot); + else if (gpriv->type & DRM_KGSL_GEM_CACHE_WTHROUGH) + vma->vm_page_prot = + pgprot_writethroughcache(vma->vm_page_prot); + else + vma->vm_page_prot = + pgprot_writecombine(vma->vm_page_prot); + } else { + if (gpriv->type == DRM_KGSL_GEM_TYPE_KMEM_NOCACHE) + vma->vm_page_prot = + pgprot_noncached(vma->vm_page_prot); + else + /* default pmem is WC */ + vma->vm_page_prot = + pgprot_writecombine(vma->vm_page_prot); + } + + /* flush out existing KMEM cached mappings if new ones are + * of uncached type */ + if (IS_MEM_UNCACHED(gpriv->type)) + kgsl_cache_range_op(&gpriv->memdesc, + KGSL_CACHE_OP_FLUSH); + + /* Add the other memory types here */ + + /* Take a ref for this mapping of the object, so that the fault + * handler can dereference the mmap offset's pointer to the object. + * This reference is cleaned up by the corresponding vm_close + * (which should happen whether the vma was created by this call, or + * by a vm_open due to mremap or partial unmap or whatever). + */ + drm_gem_object_reference(obj); + + vma->vm_file = filp; /* Needed for drm_vm_open() */ + drm_vm_open_locked(vma); + +out_unlock: + mutex_unlock(&dev->struct_mutex); + + return ret; +} + +void +cleanup_fence(struct drm_kgsl_gem_object_fence *fence, int check_waiting) +{ + int j; + struct drm_kgsl_gem_object_fence_list_entry *this_fence_entry = NULL; + struct drm_kgsl_gem_object *unlock_obj; + struct drm_gem_object *obj; + struct drm_kgsl_gem_object_wait_list_entry *lock_next; + + fence->ts_valid = 0; + fence->timestamp = -1; + fence->ts_device = -1; + + /* Walk the list of buffers in this fence and clean up the */ + /* references. Note that this can cause memory allocations */ + /* to be freed */ + for (j = fence->num_buffers; j > 0; j--) { + this_fence_entry = + (struct drm_kgsl_gem_object_fence_list_entry *) + fence->buffers_in_fence.prev; + + this_fence_entry->in_use = 0; + obj = this_fence_entry->gem_obj; + unlock_obj = obj->driver_private; + + /* Delete it from the list */ + + list_del(&this_fence_entry->list); + + /* we are unlocking - see if there are other pids waiting */ + if (check_waiting) { + if (!list_empty(&unlock_obj->wait_list)) { + lock_next = + (struct drm_kgsl_gem_object_wait_list_entry *) + unlock_obj->wait_list.prev; + + list_del((struct list_head *)&lock_next->list); + + unlock_obj->lockpid = 0; + wake_up_interruptible( + &lock_next->process_wait_q); + lock_next->pid = 0; + + } else { + /* List is empty so set pid to 0 */ + unlock_obj->lockpid = 0; + } + } + + drm_gem_object_unreference(obj); + } + /* here all the buffers in the fence are released */ + /* clear the fence entry */ + fence->fence_id = ENTRY_EMPTY; +} + +int +find_empty_fence(void) +{ + int i; + + for (i = 0; i < DRM_KGSL_NUM_FENCE_ENTRIES; i++) { + if (gem_buf_fence[i].fence_id == ENTRY_EMPTY) { + gem_buf_fence[i].fence_id = fence_id++; + gem_buf_fence[i].ts_valid = 0; + INIT_LIST_HEAD(&(gem_buf_fence[i].buffers_in_fence)); + if (fence_id == 0xFFFFFFF0) + fence_id = 1; + return i; + } else { + + /* Look for entries to be cleaned up */ + if (gem_buf_fence[i].fence_id == ENTRY_NEEDS_CLEANUP) + cleanup_fence(&gem_buf_fence[i], 0); + } + } + + return ENTRY_EMPTY; +} + +int +find_fence(int index) +{ + int i; + + for (i = 0; i < DRM_KGSL_NUM_FENCE_ENTRIES; i++) { + if (gem_buf_fence[i].fence_id == index) + return i; + } + + return ENTRY_EMPTY; +} + +void +wakeup_fence_entries(struct drm_kgsl_gem_object_fence *fence) +{ + struct drm_kgsl_gem_object_fence_list_entry *this_fence_entry = NULL; + struct drm_kgsl_gem_object_wait_list_entry *lock_next; + struct drm_kgsl_gem_object *unlock_obj; + struct drm_gem_object *obj; + + /* TS has expired when we get here */ + fence->ts_valid = 0; + fence->timestamp = -1; + fence->ts_device = -1; + + list_for_each_entry(this_fence_entry, &fence->buffers_in_fence, list) { + obj = this_fence_entry->gem_obj; + unlock_obj = obj->driver_private; + + if (!list_empty(&unlock_obj->wait_list)) { + lock_next = + (struct drm_kgsl_gem_object_wait_list_entry *) + unlock_obj->wait_list.prev; + + /* Unblock the pid */ + lock_next->pid = 0; + + /* Delete it from the list */ + list_del((struct list_head *)&lock_next->list); + + unlock_obj->lockpid = 0; + wake_up_interruptible(&lock_next->process_wait_q); + + } else { + /* List is empty so set pid to 0 */ + unlock_obj->lockpid = 0; + } + } + fence->fence_id = ENTRY_NEEDS_CLEANUP; /* Mark it as needing cleanup */ +} + +static int kgsl_ts_notifier_cb(struct notifier_block *blk, + unsigned long code, void *_param) +{ + struct drm_kgsl_gem_object_fence *fence; + struct kgsl_device *device = kgsl_get_device(code); + int i; + + /* loop through the fences to see what things can be processed */ + + for (i = 0; i < DRM_KGSL_NUM_FENCE_ENTRIES; i++) { + fence = &gem_buf_fence[i]; + if (!fence->ts_valid || fence->ts_device != code) + continue; + + if (kgsl_check_timestamp(device, fence->timestamp)) + wakeup_fence_entries(fence); + } + + return 0; +} + +int +kgsl_gem_lock_handle_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + /* The purpose of this function is to lock a given set of handles. */ + /* The driver will maintain a list of locked handles. */ + /* If a request comes in for a handle that's locked the thread will */ + /* block until it's no longer in use. */ + + struct drm_kgsl_gem_lock_handles *args = data; + struct drm_gem_object *obj; + struct drm_kgsl_gem_object *priv; + struct drm_kgsl_gem_object_fence_list_entry *this_fence_entry = NULL; + struct drm_kgsl_gem_object_fence *fence; + struct drm_kgsl_gem_object_wait_list_entry *lock_item; + int i, j; + int result = 0; + uint32_t *lock_list; + uint32_t *work_list = NULL; + int32_t fence_index; + + /* copy in the data from user space */ + lock_list = kzalloc(sizeof(uint32_t) * args->num_handles, GFP_KERNEL); + if (!lock_list) { + DRM_ERROR("Unable allocate memory for lock list\n"); + result = -ENOMEM; + goto error; + } + + if (copy_from_user(lock_list, args->handle_list, + sizeof(uint32_t) * args->num_handles)) { + DRM_ERROR("Unable to copy the lock list from the user\n"); + result = -EFAULT; + goto free_handle_list; + } + + + work_list = lock_list; + mutex_lock(&dev->struct_mutex); + + /* build the fence for this group of handles */ + fence_index = find_empty_fence(); + if (fence_index == ENTRY_EMPTY) { + DRM_ERROR("Unable to find a empty fence\n"); + args->lock_id = 0xDEADBEEF; + result = -EFAULT; + goto out_unlock; + } + + fence = &gem_buf_fence[fence_index]; + gem_buf_fence[fence_index].num_buffers = args->num_handles; + args->lock_id = gem_buf_fence[fence_index].fence_id; + + for (j = args->num_handles; j > 0; j--, lock_list++) { + obj = drm_gem_object_lookup(dev, file_priv, *lock_list); + + if (obj == NULL) { + DRM_ERROR("Invalid GEM handle %x\n", *lock_list); + result = -EBADF; + goto out_unlock; + } + + priv = obj->driver_private; + this_fence_entry = NULL; + + /* get a fence entry to hook into the fence */ + for (i = 0; i < DRM_KGSL_NUM_FENCE_ENTRIES; i++) { + if (!priv->fence_entries[i].in_use) { + this_fence_entry = &priv->fence_entries[i]; + this_fence_entry->in_use = 1; + break; + } + } + + if (this_fence_entry == NULL) { + fence->num_buffers = 0; + fence->fence_id = ENTRY_EMPTY; + args->lock_id = 0xDEADBEAD; + result = -EFAULT; + drm_gem_object_unreference(obj); + goto out_unlock; + } + + /* We're trying to lock - add to a fence */ + list_add((struct list_head *)this_fence_entry, + &gem_buf_fence[fence_index].buffers_in_fence); + if (priv->lockpid) { + + if (priv->lockpid == args->pid) { + /* now that things are running async this */ + /* happens when an op isn't done */ + /* so it's already locked by the calling pid */ + continue; + } + + + /* if a pid already had it locked */ + /* create and add to wait list */ + for (i = 0; i < DRM_KGSL_HANDLE_WAIT_ENTRIES; i++) { + if (priv->wait_entries[i].in_use == 0) { + /* this one is empty */ + lock_item = &priv->wait_entries[i]; + lock_item->in_use = 1; + lock_item->pid = args->pid; + INIT_LIST_HEAD((struct list_head *) + &priv->wait_entries[i]); + break; + } + } + + if (i == DRM_KGSL_HANDLE_WAIT_ENTRIES) { + + result = -EFAULT; + drm_gem_object_unreference(obj); + goto out_unlock; + } + + list_add_tail((struct list_head *)&lock_item->list, + &priv->wait_list); + mutex_unlock(&dev->struct_mutex); + /* here we need to block */ + wait_event_interruptible_timeout( + priv->wait_entries[i].process_wait_q, + (priv->lockpid == 0), + msecs_to_jiffies(64)); + mutex_lock(&dev->struct_mutex); + lock_item->in_use = 0; + } + + /* Getting here means no one currently holds the lock */ + priv->lockpid = args->pid; + + args->lock_id = gem_buf_fence[fence_index].fence_id; + } + fence->lockpid = args->pid; + +out_unlock: + mutex_unlock(&dev->struct_mutex); + +free_handle_list: + kfree(work_list); + +error: + return result; +} + +int +kgsl_gem_unlock_handle_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_kgsl_gem_unlock_handles *args = data; + int result = 0; + int32_t fence_index; + + mutex_lock(&dev->struct_mutex); + fence_index = find_fence(args->lock_id); + if (fence_index == ENTRY_EMPTY) { + DRM_ERROR("Invalid lock ID: %x\n", args->lock_id); + result = -EFAULT; + goto out_unlock; + } + + cleanup_fence(&gem_buf_fence[fence_index], 1); + +out_unlock: + mutex_unlock(&dev->struct_mutex); + + return result; +} + + +int +kgsl_gem_unlock_on_ts_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_kgsl_gem_unlock_on_ts *args = data; + int result = 0; + int ts_done = 0; + int32_t fence_index, ts_device; + struct drm_kgsl_gem_object_fence *fence; + struct kgsl_device *device; + + if (args->type == DRM_KGSL_GEM_TS_3D) + ts_device = KGSL_DEVICE_3D0; + else if (args->type == DRM_KGSL_GEM_TS_2D) + ts_device = KGSL_DEVICE_2D0; + else { + result = -EINVAL; + goto error; + } + + device = kgsl_get_device(ts_device); + ts_done = kgsl_check_timestamp(device, args->timestamp); + + mutex_lock(&dev->struct_mutex); + + fence_index = find_fence(args->lock_id); + if (fence_index == ENTRY_EMPTY) { + DRM_ERROR("Invalid lock ID: %x\n", args->lock_id); + result = -EFAULT; + goto out_unlock; + } + + fence = &gem_buf_fence[fence_index]; + fence->ts_device = ts_device; + + if (!ts_done) + fence->ts_valid = 1; + else + cleanup_fence(fence, 1); + + +out_unlock: + mutex_unlock(&dev->struct_mutex); + +error: + return result; +} + +struct drm_ioctl_desc kgsl_drm_ioctls[] = { + DRM_IOCTL_DEF_DRV(KGSL_GEM_CREATE, kgsl_gem_create_ioctl, 0), + DRM_IOCTL_DEF_DRV(KGSL_GEM_PREP, kgsl_gem_prep_ioctl, 0), + DRM_IOCTL_DEF_DRV(KGSL_GEM_SETMEMTYPE, kgsl_gem_setmemtype_ioctl, 0), + DRM_IOCTL_DEF_DRV(KGSL_GEM_GETMEMTYPE, kgsl_gem_getmemtype_ioctl, 0), + DRM_IOCTL_DEF_DRV(KGSL_GEM_BIND_GPU, kgsl_gem_bind_gpu_ioctl, 0), + DRM_IOCTL_DEF_DRV(KGSL_GEM_UNBIND_GPU, kgsl_gem_unbind_gpu_ioctl, 0), + DRM_IOCTL_DEF_DRV(KGSL_GEM_ALLOC, kgsl_gem_alloc_ioctl, 0), + DRM_IOCTL_DEF_DRV(KGSL_GEM_MMAP, kgsl_gem_mmap_ioctl, 0), + DRM_IOCTL_DEF_DRV(KGSL_GEM_GET_BUFINFO, kgsl_gem_get_bufinfo_ioctl, 0), + DRM_IOCTL_DEF_DRV(KGSL_GEM_SET_BUFCOUNT, + kgsl_gem_set_bufcount_ioctl, 0), + DRM_IOCTL_DEF_DRV(KGSL_GEM_SET_ACTIVE, kgsl_gem_set_active_ioctl, 0), + DRM_IOCTL_DEF_DRV(KGSL_GEM_LOCK_HANDLE, + kgsl_gem_lock_handle_ioctl, 0), + DRM_IOCTL_DEF_DRV(KGSL_GEM_UNLOCK_HANDLE, + kgsl_gem_unlock_handle_ioctl, 0), + DRM_IOCTL_DEF_DRV(KGSL_GEM_UNLOCK_ON_TS, + kgsl_gem_unlock_on_ts_ioctl, 0), + DRM_IOCTL_DEF_DRV(KGSL_GEM_CREATE_FD, kgsl_gem_create_fd_ioctl, + DRM_MASTER), +}; + +static struct drm_driver driver = { + .driver_features = DRIVER_USE_PLATFORM_DEVICE | DRIVER_GEM, + .load = kgsl_drm_load, + .unload = kgsl_drm_unload, + .firstopen = kgsl_drm_firstopen, + .lastclose = kgsl_drm_lastclose, + .preclose = kgsl_drm_preclose, + .suspend = kgsl_drm_suspend, + .resume = kgsl_drm_resume, + .reclaim_buffers = drm_core_reclaim_buffers, + .gem_init_object = kgsl_gem_init_object, + .gem_free_object = kgsl_gem_free_object, + .ioctls = kgsl_drm_ioctls, + + .fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = msm_drm_gem_mmap, + .poll = drm_poll, + .fasync = drm_fasync, + }, + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +}; + +int kgsl_drm_init(struct platform_device *dev) +{ + int i; + + driver.num_ioctls = DRM_ARRAY_SIZE(kgsl_drm_ioctls); + driver.platform_device = dev; + + INIT_LIST_HEAD(&kgsl_mem_list); + + for (i = 0; i < DRM_KGSL_NUM_FENCE_ENTRIES; i++) { + gem_buf_fence[i].num_buffers = 0; + gem_buf_fence[i].ts_valid = 0; + gem_buf_fence[i].fence_id = ENTRY_EMPTY; + } + + return drm_init(&driver); +} + +void kgsl_drm_exit(void) +{ + drm_exit(&driver); +} diff --git a/drivers/gpu/msm/kgsl_log.h b/drivers/gpu/msm/kgsl_log.h new file mode 100644 index 0000000000000..e816e568d4629 --- /dev/null +++ b/drivers/gpu/msm/kgsl_log.h @@ -0,0 +1,118 @@ +/* Copyright (c) 2002,2008-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __KGSL_LOG_H +#define __KGSL_LOG_H + +extern unsigned int kgsl_cff_dump_enable; + +#define KGSL_LOG_INFO(dev, lvl, fmt, args...) \ + do { \ + if ((lvl) >= 6) \ + dev_info(dev, "|%s| " fmt, \ + __func__, ##args);\ + } while (0) + +#define KGSL_LOG_WARN(dev, lvl, fmt, args...) \ + do { \ + if ((lvl) >= 4) \ + dev_warn(dev, "|%s| " fmt, \ + __func__, ##args);\ + } while (0) + +#define KGSL_LOG_ERR(dev, lvl, fmt, args...) \ + do { \ + if ((lvl) >= 3) \ + dev_err(dev, "|%s| " fmt, \ + __func__, ##args);\ + } while (0) + +#define KGSL_LOG_CRIT(dev, lvl, fmt, args...) \ + do { \ + if ((lvl) >= 2) \ + dev_crit(dev, "|%s| " fmt, \ + __func__, ##args);\ + } while (0) + +#define KGSL_LOG_POSTMORTEM_WRITE(_dev, fmt, args...) \ + do { dev_crit(_dev->dev, fmt, ##args); } while (0) + +#define KGSL_LOG_DUMP(_dev, fmt, args...) dev_err(_dev->dev, fmt, ##args) + +#define KGSL_DRV_INFO(_dev, fmt, args...) \ +KGSL_LOG_INFO(_dev->dev, _dev->drv_log, fmt, ##args) +#define KGSL_DRV_WARN(_dev, fmt, args...) \ +KGSL_LOG_WARN(_dev->dev, _dev->drv_log, fmt, ##args) +#define KGSL_DRV_ERR(_dev, fmt, args...) \ +KGSL_LOG_ERR(_dev->dev, _dev->drv_log, fmt, ##args) +#define KGSL_DRV_CRIT(_dev, fmt, args...) \ +KGSL_LOG_CRIT(_dev->dev, _dev->drv_log, fmt, ##args) + +#define KGSL_CMD_INFO(_dev, fmt, args...) \ +KGSL_LOG_INFO(_dev->dev, _dev->cmd_log, fmt, ##args) +#define KGSL_CMD_WARN(_dev, fmt, args...) \ +KGSL_LOG_WARN(_dev->dev, _dev->cmd_log, fmt, ##args) +#define KGSL_CMD_ERR(_dev, fmt, args...) \ +KGSL_LOG_ERR(_dev->dev, _dev->cmd_log, fmt, ##args) +#define KGSL_CMD_CRIT(_dev, fmt, args...) \ +KGSL_LOG_CRIT(_dev->dev, _dev->cmd_log, fmt, ##args) + +#define KGSL_CTXT_INFO(_dev, fmt, args...) \ +KGSL_LOG_INFO(_dev->dev, _dev->ctxt_log, fmt, ##args) +#define KGSL_CTXT_WARN(_dev, fmt, args...) \ +KGSL_LOG_WARN(_dev->dev, _dev->ctxt_log, fmt, ##args) +#define KGSL_CTXT_ERR(_dev, fmt, args...) \ +KGSL_LOG_ERR(_dev->dev, _dev->ctxt_log, fmt, ##args) +#define KGSL_CTXT_CRIT(_dev, fmt, args...) \ +KGSL_LOG_CRIT(_dev->dev, _dev->ctxt_log, fmt, ##args) + +#define KGSL_MEM_INFO(_dev, fmt, args...) \ +KGSL_LOG_INFO(_dev->dev, _dev->mem_log, fmt, ##args) +#define KGSL_MEM_WARN(_dev, fmt, args...) \ +KGSL_LOG_WARN(_dev->dev, _dev->mem_log, fmt, ##args) +#define KGSL_MEM_ERR(_dev, fmt, args...) \ +KGSL_LOG_ERR(_dev->dev, _dev->mem_log, fmt, ##args) +#define KGSL_MEM_CRIT(_dev, fmt, args...) \ +KGSL_LOG_CRIT(_dev->dev, _dev->mem_log, fmt, ##args) + +#define KGSL_PWR_INFO(_dev, fmt, args...) \ +KGSL_LOG_INFO(_dev->dev, _dev->pwr_log, fmt, ##args) +#define KGSL_PWR_WARN(_dev, fmt, args...) \ +KGSL_LOG_WARN(_dev->dev, _dev->pwr_log, fmt, ##args) +#define KGSL_PWR_ERR(_dev, fmt, args...) \ +KGSL_LOG_ERR(_dev->dev, _dev->pwr_log, fmt, ##args) +#define KGSL_PWR_CRIT(_dev, fmt, args...) \ +KGSL_LOG_CRIT(_dev->dev, _dev->pwr_log, fmt, ##args) + +/* Core error messages - these are for core KGSL functions that have + no device associated with them (such as memory) */ + +#define KGSL_CORE_ERR(fmt, args...) \ +pr_err("kgsl: %s: " fmt, __func__, ##args) + +#endif /* __KGSL_LOG_H */ diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c new file mode 100644 index 0000000000000..6cb768b0825eb --- /dev/null +++ b/drivers/gpu/msm/kgsl_mmu.c @@ -0,0 +1,1133 @@ +/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include + +#include "kgsl.h" +#include "kgsl_mmu.h" + +#define KGSL_MMU_ALIGN_SHIFT 13 +#define KGSL_MMU_ALIGN_MASK (~((1 << KGSL_MMU_ALIGN_SHIFT) - 1)) + +#define GSL_PT_PAGE_BITS_MASK 0x00000007 +#define GSL_PT_PAGE_ADDR_MASK PAGE_MASK + +#define GSL_MMU_INT_MASK \ + (MH_INTERRUPT_MASK__AXI_READ_ERROR | \ + MH_INTERRUPT_MASK__AXI_WRITE_ERROR) + +static ssize_t +sysfs_show_ptpool_entries(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", kgsl_driver.ptpool.entries); +} + +static ssize_t +sysfs_show_ptpool_min(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", kgsl_driver.ptpool.static_entries); +} + +static ssize_t +sysfs_show_ptpool_chunks(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", kgsl_driver.ptpool.chunks); +} + +static ssize_t +sysfs_show_ptpool_ptsize(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", kgsl_driver.ptpool.ptsize); +} + +static struct kobj_attribute attr_ptpool_entries = { + .attr = { .name = "ptpool_entries", .mode = 0444 }, + .show = sysfs_show_ptpool_entries, + .store = NULL, +}; + +static struct kobj_attribute attr_ptpool_min = { + .attr = { .name = "ptpool_min", .mode = 0444 }, + .show = sysfs_show_ptpool_min, + .store = NULL, +}; + +static struct kobj_attribute attr_ptpool_chunks = { + .attr = { .name = "ptpool_chunks", .mode = 0444 }, + .show = sysfs_show_ptpool_chunks, + .store = NULL, +}; + +static struct kobj_attribute attr_ptpool_ptsize = { + .attr = { .name = "ptpool_ptsize", .mode = 0444 }, + .show = sysfs_show_ptpool_ptsize, + .store = NULL, +}; + +static struct attribute *ptpool_attrs[] = { + &attr_ptpool_entries.attr, + &attr_ptpool_min.attr, + &attr_ptpool_chunks.attr, + &attr_ptpool_ptsize.attr, + NULL, +}; + +static struct attribute_group ptpool_attr_group = { + .attrs = ptpool_attrs, +}; + +static int +_kgsl_ptpool_add_entries(struct kgsl_ptpool *pool, int count, int dynamic) +{ + struct kgsl_ptpool_chunk *chunk; + size_t size = ALIGN(count * pool->ptsize, PAGE_SIZE); + + BUG_ON(count == 0); + + if (get_order(size) >= MAX_ORDER) { + KGSL_CORE_ERR("ptpool allocation is too big: %d\n", size); + return -EINVAL; + } + + chunk = kzalloc(sizeof(*chunk), GFP_KERNEL); + if (chunk == NULL) { + KGSL_CORE_ERR("kzalloc(%d) failed\n", sizeof(*chunk)); + return -ENOMEM; + } + + chunk->size = size; + chunk->count = count; + chunk->dynamic = dynamic; + + chunk->data = dma_alloc_coherent(NULL, size, + &chunk->phys, GFP_KERNEL); + + if (chunk->data == NULL) { + KGSL_CORE_ERR("dma_alloc_coherent(%d) failed\n", size); + goto err; + } + + chunk->bitmap = kzalloc(BITS_TO_LONGS(count) * 4, GFP_KERNEL); + + if (chunk->bitmap == NULL) { + KGSL_CORE_ERR("kzalloc(%d) failed\n", + BITS_TO_LONGS(count) * 4); + goto err_dma; + } + + list_add_tail(&chunk->list, &pool->list); + + pool->chunks++; + pool->entries += count; + + if (!dynamic) + pool->static_entries += count; + + return 0; + +err_dma: + dma_free_coherent(NULL, chunk->size, chunk->data, chunk->phys); +err: + kfree(chunk); + return -ENOMEM; +} + +static void * +_kgsl_ptpool_get_entry(struct kgsl_ptpool *pool, unsigned int *physaddr) +{ + struct kgsl_ptpool_chunk *chunk; + + list_for_each_entry(chunk, &pool->list, list) { + int bit = find_first_zero_bit(chunk->bitmap, chunk->count); + + if (bit >= chunk->count) + continue; + + set_bit(bit, chunk->bitmap); + *physaddr = chunk->phys + (bit * pool->ptsize); + + return chunk->data + (bit * pool->ptsize); + } + + return NULL; +} + +/** + * kgsl_ptpool_add + * @pool: A pointer to a ptpool structure + * @entries: Number of entries to add + * + * Add static entries to the pagetable pool. + */ + +int +kgsl_ptpool_add(struct kgsl_ptpool *pool, int count) +{ + int ret = 0; + BUG_ON(count == 0); + + mutex_lock(&pool->lock); + + /* Only 4MB can be allocated in one chunk, so larger allocations + need to be split into multiple sections */ + + while (count) { + int entries = ((count * pool->ptsize) > SZ_4M) ? + SZ_4M / pool->ptsize : count; + + /* Add the entries as static, i.e. they don't ever stand + a chance of being removed */ + + ret = _kgsl_ptpool_add_entries(pool, entries, 0); + if (ret) + break; + + count -= entries; + } + + mutex_unlock(&pool->lock); + return ret; +} + +/** + * kgsl_ptpool_alloc + * @pool: A pointer to a ptpool structure + * @addr: A pointer to store the physical address of the chunk + * + * Allocate a pagetable from the pool. Returns the virtual address + * of the pagetable, the physical address is returned in physaddr + */ + +void *kgsl_ptpool_alloc(struct kgsl_ptpool *pool, unsigned int *physaddr) +{ + void *addr = NULL; + int ret; + + mutex_lock(&pool->lock); + addr = _kgsl_ptpool_get_entry(pool, physaddr); + if (addr) + goto done; + + /* Add a chunk for 1 more pagetable and mark it as dynamic */ + ret = _kgsl_ptpool_add_entries(pool, 1, 1); + + if (ret) + goto done; + + addr = _kgsl_ptpool_get_entry(pool, physaddr); +done: + mutex_unlock(&pool->lock); + return addr; +} + +static inline void _kgsl_ptpool_rm_chunk(struct kgsl_ptpool_chunk *chunk) +{ + list_del(&chunk->list); + + if (chunk->data) + dma_free_coherent(NULL, chunk->size, chunk->data, + chunk->phys); + kfree(chunk->bitmap); + kfree(chunk); +} + +/** + * kgsl_ptpool_free + * @pool: A pointer to a ptpool structure + * @addr: A pointer to the virtual address to free + * + * Free a pagetable allocated from the pool + */ + +void kgsl_ptpool_free(struct kgsl_ptpool *pool, void *addr) +{ + struct kgsl_ptpool_chunk *chunk, *tmp; + + if (pool == NULL || addr == NULL) + return; + + mutex_lock(&pool->lock); + list_for_each_entry_safe(chunk, tmp, &pool->list, list) { + if (addr >= chunk->data && + addr < chunk->data + chunk->size) { + int bit = ((unsigned long) (addr - chunk->data)) / + pool->ptsize; + + clear_bit(bit, chunk->bitmap); + memset(addr, 0, pool->ptsize); + + if (chunk->dynamic && + bitmap_empty(chunk->bitmap, chunk->count)) + _kgsl_ptpool_rm_chunk(chunk); + + break; + } + } + + mutex_unlock(&pool->lock); +} + +void kgsl_ptpool_destroy(struct kgsl_ptpool *pool) +{ + struct kgsl_ptpool_chunk *chunk, *tmp; + + if (pool == NULL) + return; + + mutex_lock(&pool->lock); + list_for_each_entry_safe(chunk, tmp, &pool->list, list) + _kgsl_ptpool_rm_chunk(chunk); + mutex_unlock(&pool->lock); + + memset(pool, 0, sizeof(*pool)); +} + +/** + * kgsl_ptpool_init + * @pool: A pointer to a ptpool structure to initialize + * @ptsize: The size of each pagetable entry + * @entries: The number of inital entries to add to the pool + * + * Initalize a pool and allocate an initial chunk of entries. + */ + +int kgsl_ptpool_init(struct kgsl_ptpool *pool, int ptsize, int entries) +{ + int ret = 0; + BUG_ON(ptsize == 0); + + pool->ptsize = ptsize; + mutex_init(&pool->lock); + INIT_LIST_HEAD(&pool->list); + + if (entries) { + ret = kgsl_ptpool_add(pool, entries); + if (ret) + return ret; + } + + return sysfs_create_group(kgsl_driver.ptkobj, &ptpool_attr_group); +} + +/* pt_mutex needs to be held in this function */ + +static struct kgsl_pagetable * +kgsl_get_pagetable(unsigned long name) +{ + struct kgsl_pagetable *pt; + + list_for_each_entry(pt, &kgsl_driver.pagetable_list, list) { + if (pt->name == name) + return pt; + } + + return NULL; +} + +static struct kgsl_pagetable * +_get_pt_from_kobj(struct kobject *kobj) +{ + unsigned long ptname; + + if (!kobj) + return NULL; + + if (sscanf(kobj->name, "%ld", &ptname) != 1) + return NULL; + + return kgsl_get_pagetable(ptname); +} + +static ssize_t +sysfs_show_entries(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct kgsl_pagetable *pt; + int ret = 0; + + mutex_lock(&kgsl_driver.pt_mutex); + pt = _get_pt_from_kobj(kobj); + + if (pt) + ret += sprintf(buf, "%d\n", pt->stats.entries); + + mutex_unlock(&kgsl_driver.pt_mutex); + return ret; +} + +static ssize_t +sysfs_show_mapped(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct kgsl_pagetable *pt; + int ret = 0; + + mutex_lock(&kgsl_driver.pt_mutex); + pt = _get_pt_from_kobj(kobj); + + if (pt) + ret += sprintf(buf, "%d\n", pt->stats.mapped); + + mutex_unlock(&kgsl_driver.pt_mutex); + return ret; +} + +static ssize_t +sysfs_show_va_range(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct kgsl_pagetable *pt; + int ret = 0; + + mutex_lock(&kgsl_driver.pt_mutex); + pt = _get_pt_from_kobj(kobj); + + if (pt) + ret += sprintf(buf, "0x%x\n", pt->va_range); + + mutex_unlock(&kgsl_driver.pt_mutex); + return ret; +} + +static ssize_t +sysfs_show_max_mapped(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct kgsl_pagetable *pt; + int ret = 0; + + mutex_lock(&kgsl_driver.pt_mutex); + pt = _get_pt_from_kobj(kobj); + + if (pt) + ret += sprintf(buf, "%d\n", pt->stats.max_mapped); + + mutex_unlock(&kgsl_driver.pt_mutex); + return ret; +} + +static ssize_t +sysfs_show_max_entries(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct kgsl_pagetable *pt; + int ret = 0; + + mutex_lock(&kgsl_driver.pt_mutex); + pt = _get_pt_from_kobj(kobj); + + if (pt) + ret += sprintf(buf, "%d\n", pt->stats.max_entries); + + mutex_unlock(&kgsl_driver.pt_mutex); + return ret; +} + +static struct kobj_attribute attr_entries = { + .attr = { .name = "entries", .mode = 0444 }, + .show = sysfs_show_entries, + .store = NULL, +}; + +static struct kobj_attribute attr_mapped = { + .attr = { .name = "mapped", .mode = 0444 }, + .show = sysfs_show_mapped, + .store = NULL, +}; + +static struct kobj_attribute attr_va_range = { + .attr = { .name = "va_range", .mode = 0444 }, + .show = sysfs_show_va_range, + .store = NULL, +}; + +static struct kobj_attribute attr_max_mapped = { + .attr = { .name = "max_mapped", .mode = 0444 }, + .show = sysfs_show_max_mapped, + .store = NULL, +}; + +static struct kobj_attribute attr_max_entries = { + .attr = { .name = "max_entries", .mode = 0444 }, + .show = sysfs_show_max_entries, + .store = NULL, +}; + +static struct attribute *pagetable_attrs[] = { + &attr_entries.attr, + &attr_mapped.attr, + &attr_va_range.attr, + &attr_max_mapped.attr, + &attr_max_entries.attr, + NULL, +}; + +static struct attribute_group pagetable_attr_group = { + .attrs = pagetable_attrs, +}; + +static void +pagetable_remove_sysfs_objects(struct kgsl_pagetable *pagetable) +{ + if (pagetable->kobj) + sysfs_remove_group(pagetable->kobj, + &pagetable_attr_group); + + kobject_put(pagetable->kobj); +} + +static int +pagetable_add_sysfs_objects(struct kgsl_pagetable *pagetable) +{ + char ptname[16]; + int ret = -ENOMEM; + + snprintf(ptname, sizeof(ptname), "%d", pagetable->name); + pagetable->kobj = kobject_create_and_add(ptname, + kgsl_driver.ptkobj); + if (pagetable->kobj == NULL) + goto err; + + ret = sysfs_create_group(pagetable->kobj, &pagetable_attr_group); + +err: + if (ret) { + if (pagetable->kobj) + kobject_put(pagetable->kobj); + + pagetable->kobj = NULL; + } + + return ret; +} + +static inline uint32_t +kgsl_pt_entry_get(struct kgsl_pagetable *pt, uint32_t va) +{ + return (va - pt->va_base) >> PAGE_SHIFT; +} + +static inline void +kgsl_pt_map_set(struct kgsl_pagetable *pt, uint32_t pte, uint32_t val) +{ + uint32_t *baseptr = (uint32_t *)pt->base.hostptr; + + writel_relaxed(val, &baseptr[pte]); +} + +static inline uint32_t +kgsl_pt_map_getaddr(struct kgsl_pagetable *pt, uint32_t pte) +{ + uint32_t *baseptr = (uint32_t *)pt->base.hostptr; + uint32_t ret = readl_relaxed(&baseptr[pte]) & GSL_PT_PAGE_ADDR_MASK; + return ret; +} + +void kgsl_mh_intrcallback(struct kgsl_device *device) +{ + unsigned int status = 0; + unsigned int reg; + + kgsl_regread_isr(device, device->mmu.reg.interrupt_status, &status); + + if (status & MH_INTERRUPT_MASK__AXI_READ_ERROR) { + kgsl_regread_isr(device, device->mmu.reg.axi_error, ®); + KGSL_MEM_CRIT(device, "axi read error interrupt: %08x\n", reg); + } else if (status & MH_INTERRUPT_MASK__AXI_WRITE_ERROR) { + kgsl_regread_isr(device, device->mmu.reg.axi_error, ®); + KGSL_MEM_CRIT(device, "axi write error interrupt: %08x\n", reg); + } else if (status & MH_INTERRUPT_MASK__MMU_PAGE_FAULT) { + kgsl_regread_isr(device, device->mmu.reg.page_fault, ®); + KGSL_MEM_CRIT(device, "mmu page fault interrupt: %08x\n", reg); + } else { + KGSL_MEM_WARN(device, + "bad bits in REG_MH_INTERRUPT_STATUS %08x\n", status); + } + + kgsl_regwrite_isr(device, device->mmu.reg.interrupt_clear, status); + + /*TODO: figure out how to handle errror interupts. + * specifically, page faults should probably nuke the client that + * caused them, but we don't have enough info to figure that out yet. + */ +} + +static int kgsl_setup_pt(struct kgsl_pagetable *pt) +{ + int i = 0; + int status = 0; + + for (i = 0; i < KGSL_DEVICE_MAX; i++) { + struct kgsl_device *device = kgsl_driver.devp[i]; + if (device) { + status = device->ftbl.device_setup_pt(device, pt); + if (status) + goto error_pt; + } + } + return status; +error_pt: + while (i >= 0) { + struct kgsl_device *device = kgsl_driver.devp[i]; + if (device) + device->ftbl.device_cleanup_pt(device, pt); + i--; + } + return status; +} + +static int kgsl_cleanup_pt(struct kgsl_pagetable *pt) +{ + int i; + for (i = 0; i < KGSL_DEVICE_MAX; i++) { + struct kgsl_device *device = kgsl_driver.devp[i]; + if (device) + device->ftbl.device_cleanup_pt(device, pt); + } + return 0; +} + +static struct kgsl_pagetable *kgsl_mmu_createpagetableobject( + unsigned int name) +{ + int status = 0; + struct kgsl_pagetable *pagetable = NULL; + + pagetable = kzalloc(sizeof(struct kgsl_pagetable), GFP_KERNEL); + if (pagetable == NULL) { + KGSL_CORE_ERR("kzalloc(%d) failed\n", + sizeof(struct kgsl_pagetable)); + return NULL; + } + + pagetable->refcnt = 1; + + spin_lock_init(&pagetable->lock); + pagetable->tlb_flags = 0; + pagetable->name = name; + pagetable->va_base = KGSL_PAGETABLE_BASE; + pagetable->va_range = CONFIG_MSM_KGSL_PAGE_TABLE_SIZE; + pagetable->last_superpte = 0; + pagetable->max_entries = KGSL_PAGETABLE_ENTRIES(pagetable->va_range); + + pagetable->tlbflushfilter.size = (pagetable->va_range / + (PAGE_SIZE * GSL_PT_SUPER_PTE * 8)) + 1; + pagetable->tlbflushfilter.base = (unsigned int *) + kzalloc(pagetable->tlbflushfilter.size, GFP_KERNEL); + if (!pagetable->tlbflushfilter.base) { + KGSL_CORE_ERR("kzalloc(%d) failed\n", + pagetable->tlbflushfilter.size); + goto err_alloc; + } + GSL_TLBFLUSH_FILTER_RESET(); + + pagetable->pool = gen_pool_create(PAGE_SHIFT, -1); + if (pagetable->pool == NULL) { + KGSL_CORE_ERR("gen_pool_create(%d) failed\n", PAGE_SHIFT); + goto err_flushfilter; + } + + if (gen_pool_add(pagetable->pool, pagetable->va_base, + pagetable->va_range, -1)) { + KGSL_CORE_ERR("gen_pool_add failed\n"); + goto err_pool; + } + + pagetable->base.hostptr = kgsl_ptpool_alloc(&kgsl_driver.ptpool, + &pagetable->base.physaddr); + + if (pagetable->base.hostptr == NULL) + goto err_pool; + + /* ptpool allocations are from coherent memory, so update the + device statistics acordingly */ + + KGSL_STATS_ADD(KGSL_PAGETABLE_SIZE, kgsl_driver.stats.coherent, + kgsl_driver.stats.coherent_max); + + pagetable->base.gpuaddr = pagetable->base.physaddr; + pagetable->base.size = KGSL_PAGETABLE_SIZE; + + status = kgsl_setup_pt(pagetable); + if (status) + goto err_free_sharedmem; + + list_add(&pagetable->list, &kgsl_driver.pagetable_list); + + /* Create the sysfs entries */ + pagetable_add_sysfs_objects(pagetable); + + return pagetable; + +err_free_sharedmem: + kgsl_ptpool_free(&kgsl_driver.ptpool, &pagetable->base.hostptr); +err_pool: + gen_pool_destroy(pagetable->pool); +err_flushfilter: + kfree(pagetable->tlbflushfilter.base); +err_alloc: + kfree(pagetable); + + return NULL; +} + +static void kgsl_mmu_destroypagetable(struct kgsl_pagetable *pagetable) +{ + list_del(&pagetable->list); + + pagetable_remove_sysfs_objects(pagetable); + + kgsl_cleanup_pt(pagetable); + + kgsl_ptpool_free(&kgsl_driver.ptpool, pagetable->base.hostptr); + + kgsl_driver.stats.coherent -= KGSL_PAGETABLE_SIZE; + + if (pagetable->pool) { + gen_pool_destroy(pagetable->pool); + pagetable->pool = NULL; + } + + if (pagetable->tlbflushfilter.base) { + pagetable->tlbflushfilter.size = 0; + kfree(pagetable->tlbflushfilter.base); + pagetable->tlbflushfilter.base = NULL; + } + + kfree(pagetable); +} + +struct kgsl_pagetable *kgsl_mmu_getpagetable(unsigned long name) +{ + struct kgsl_pagetable *pt; + + mutex_lock(&kgsl_driver.pt_mutex); + + pt = kgsl_get_pagetable(name); + + if (pt) { + spin_lock(&pt->lock); + pt->refcnt++; + spin_unlock(&pt->lock); + goto done; + } + + pt = kgsl_mmu_createpagetableobject(name); + +done: + mutex_unlock(&kgsl_driver.pt_mutex); + return pt; +} + +void kgsl_mmu_putpagetable(struct kgsl_pagetable *pagetable) +{ + bool dead; + if (pagetable == NULL) + return; + + mutex_lock(&kgsl_driver.pt_mutex); + + spin_lock(&pagetable->lock); + dead = (--pagetable->refcnt) == 0; + spin_unlock(&pagetable->lock); + + if (dead) + kgsl_mmu_destroypagetable(pagetable); + + mutex_unlock(&kgsl_driver.pt_mutex); +} + +int kgsl_mmu_setstate(struct kgsl_device *device, + struct kgsl_pagetable *pagetable) +{ + int status = 0; + struct kgsl_mmu *mmu = &device->mmu; + + if (mmu->flags & KGSL_FLAGS_STARTED) { + /* page table not current, then setup mmu to use new + * specified page table + */ + if (mmu->hwpagetable != pagetable) { + mmu->hwpagetable = pagetable; + spin_lock(&mmu->hwpagetable->lock); + mmu->hwpagetable->tlb_flags &= ~(1<id); + spin_unlock(&mmu->hwpagetable->lock); + + /* call device specific set page table */ + status = kgsl_setstate(mmu->device, + KGSL_MMUFLAGS_TLBFLUSH | + KGSL_MMUFLAGS_PTUPDATE); + + } + } + + return status; +} + +int kgsl_mmu_init(struct kgsl_device *device) +{ + /* + * intialize device mmu + * + * call this with the global lock held + */ + int status = 0; + struct kgsl_mmu *mmu = &device->mmu; + + mmu->device = device; + + /* make sure aligned to pagesize */ + BUG_ON(mmu->mpu_base & (PAGE_SIZE - 1)); + BUG_ON((mmu->mpu_base + mmu->mpu_range) & (PAGE_SIZE - 1)); + + /* sub-client MMU lookups require address translation */ + if ((mmu->config & ~0x1) > 0) { + /*make sure virtual address range is a multiple of 64Kb */ + BUG_ON(CONFIG_MSM_KGSL_PAGE_TABLE_SIZE & ((1 << 16) - 1)); + + /* allocate memory used for completing r/w operations that + * cannot be mapped by the MMU + */ + status = kgsl_allocate_contig(&mmu->dummyspace, 64); + if (!status) + kgsl_sharedmem_set(&mmu->dummyspace, 0, 0, + mmu->dummyspace.size); + } + + return status; +} + +int kgsl_mmu_start(struct kgsl_device *device) +{ + /* + * intialize device mmu + * + * call this with the global lock held + */ + int status; + struct kgsl_mmu *mmu = &device->mmu; + + if (mmu->flags & KGSL_FLAGS_STARTED) + return 0; + + /* MMU not enabled */ + if ((mmu->config & 0x1) == 0) + return 0; + + mmu->flags |= KGSL_FLAGS_STARTED; + + /* setup MMU and sub-client behavior */ + kgsl_regwrite(device, device->mmu.reg.config, mmu->config); + + /* enable axi interrupts */ + kgsl_regwrite(device, device->mmu.reg.interrupt_mask, + GSL_MMU_INT_MASK); + + /* idle device */ + kgsl_idle(device, KGSL_TIMEOUT_DEFAULT); + + /* define physical memory range accessible by the core */ + kgsl_regwrite(device, device->mmu.reg.mpu_base, mmu->mpu_base); + kgsl_regwrite(device, device->mmu.reg.mpu_end, + mmu->mpu_base + mmu->mpu_range); + + /* enable axi interrupts */ + kgsl_regwrite(device, device->mmu.reg.interrupt_mask, + GSL_MMU_INT_MASK | MH_INTERRUPT_MASK__MMU_PAGE_FAULT); + + /* sub-client MMU lookups require address translation */ + if ((mmu->config & ~0x1) > 0) { + + kgsl_sharedmem_set(&mmu->dummyspace, 0, 0, + mmu->dummyspace.size); + + /* TRAN_ERROR needs a 32 byte (32 byte aligned) chunk of memory + * to complete transactions in case of an MMU fault. Note that + * we'll leave the bottom 32 bytes of the dummyspace for other + * purposes (e.g. use it when dummy read cycles are needed + * for other blocks */ + kgsl_regwrite(device, device->mmu.reg.tran_error, + mmu->dummyspace.physaddr + 32); + + if (mmu->defaultpagetable == NULL) + mmu->defaultpagetable = + kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT); + mmu->hwpagetable = mmu->defaultpagetable; + + kgsl_regwrite(device, device->mmu.reg.pt_page, + mmu->hwpagetable->base.gpuaddr); + kgsl_regwrite(device, device->mmu.reg.va_range, + (mmu->hwpagetable->va_base | + (mmu->hwpagetable->va_range >> 16))); + status = kgsl_setstate(device, KGSL_MMUFLAGS_TLBFLUSH); + if (status) { + KGSL_MEM_ERR(device, "Failed to setstate TLBFLUSH\n"); + goto error; + } + } + + return 0; +error: + /* disable MMU */ + kgsl_regwrite(device, device->mmu.reg.interrupt_mask, 0); + kgsl_regwrite(device, device->mmu.reg.config, 0x00000000); + return status; +} + +unsigned int kgsl_virtaddr_to_physaddr(void *virtaddr) +{ + unsigned int physaddr = 0; + pgd_t *pgd_ptr = NULL; + pmd_t *pmd_ptr = NULL; + pte_t *pte_ptr = NULL, pte; + + pgd_ptr = pgd_offset(current->mm, (unsigned long) virtaddr); + if (pgd_none(*pgd) || pgd_bad(*pgd)) { + KGSL_CORE_ERR("Invalid pgd entry\n"); + return 0; + } + + pmd_ptr = pmd_offset(pgd_ptr, (unsigned long) virtaddr); + if (pmd_none(*pmd_ptr) || pmd_bad(*pmd_ptr)) { + KGSL_CORE_ERR("Invalid pmd entry\n"); + return 0; + } + + pte_ptr = pte_offset_map(pmd_ptr, (unsigned long) virtaddr); + if (!pte_ptr) { + KGSL_CORE_ERR("pt_offset_map failed\n"); + return 0; + } + pte = *pte_ptr; + physaddr = pte_pfn(pte); + pte_unmap(pte_ptr); + physaddr <<= PAGE_SHIFT; + return physaddr; +} + +int +kgsl_mmu_map(struct kgsl_pagetable *pagetable, + struct kgsl_memdesc *memdesc, + unsigned int protflags) +{ + int numpages; + unsigned int pte, ptefirst, ptelast, physaddr; + int flushtlb; + unsigned int offset = 0; + + BUG_ON(protflags & ~(GSL_PT_PAGE_RV | GSL_PT_PAGE_WV)); + BUG_ON(protflags == 0); + + memdesc->gpuaddr = gen_pool_alloc_aligned(pagetable->pool, + memdesc->size, KGSL_MMU_ALIGN_SHIFT); + + if (memdesc->gpuaddr == 0) { + KGSL_CORE_ERR("gen_pool_alloc(%d) failed\n", memdesc->size); + KGSL_CORE_ERR(" [%d] allocated=%d, entries=%d\n", + pagetable->name, pagetable->stats.mapped, + pagetable->stats.entries); + return -ENOMEM; + } + + numpages = (memdesc->size >> PAGE_SHIFT); + + ptefirst = kgsl_pt_entry_get(pagetable, memdesc->gpuaddr); + ptelast = ptefirst + numpages; + + pte = ptefirst; + flushtlb = 0; + + /* tlb needs to be flushed when the first and last pte are not at + * superpte boundaries */ + if ((ptefirst & (GSL_PT_SUPER_PTE - 1)) != 0 || + ((ptelast + 1) & (GSL_PT_SUPER_PTE-1)) != 0) + flushtlb = 1; + + spin_lock(&pagetable->lock); + for (pte = ptefirst; pte < ptelast; pte++, offset += PAGE_SIZE) { +#ifdef VERBOSE_DEBUG + /* check if PTE exists */ + uint32_t val = kgsl_pt_map_getaddr(pagetable, pte); + BUG_ON(val != 0 && val != GSL_PT_PAGE_DIRTY); +#endif + if ((pte & (GSL_PT_SUPER_PTE-1)) == 0) + if (GSL_TLBFLUSH_FILTER_ISDIRTY(pte / GSL_PT_SUPER_PTE)) + flushtlb = 1; + /* mark pte as in use */ + + physaddr = memdesc->ops->physaddr(memdesc, offset); + BUG_ON(physaddr == 0); + kgsl_pt_map_set(pagetable, pte, physaddr | protflags); + } + + /* Keep track of the statistics for the sysfs files */ + + KGSL_STATS_ADD(1, pagetable->stats.entries, + pagetable->stats.max_entries); + + KGSL_STATS_ADD(memdesc->size, pagetable->stats.mapped, + pagetable->stats.max_mapped); + + /* Post all writes to the pagetable */ + wmb(); + + /* Invalidate tlb only if current page table used by GPU is the + * pagetable that we used to allocate */ + if (flushtlb) { + /*set all devices as needing flushing*/ + pagetable->tlb_flags = UINT_MAX; + GSL_TLBFLUSH_FILTER_RESET(); + } + spin_unlock(&pagetable->lock); + + return 0; +} + +int +kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, + struct kgsl_memdesc *memdesc) +{ + unsigned int numpages; + unsigned int pte, ptefirst, ptelast, superpte; + unsigned int range = memdesc->size; + + /* All GPU addresses as assigned are page aligned, but some + functions purturb the gpuaddr with an offset, so apply the + mask here to make sure we have the right address */ + + unsigned int gpuaddr = memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK; + + if (range == 0 || gpuaddr == 0) + return 0; + + numpages = (range >> PAGE_SHIFT); + if (range & (PAGE_SIZE - 1)) + numpages++; + + ptefirst = kgsl_pt_entry_get(pagetable, gpuaddr); + ptelast = ptefirst + numpages; + + spin_lock(&pagetable->lock); + superpte = ptefirst - (ptefirst & (GSL_PT_SUPER_PTE-1)); + GSL_TLBFLUSH_FILTER_SETDIRTY(superpte / GSL_PT_SUPER_PTE); + for (pte = ptefirst; pte < ptelast; pte++) { +#ifdef VERBOSE_DEBUG + /* check if PTE exists */ + BUG_ON(!kgsl_pt_map_getaddr(pagetable, pte)); +#endif + kgsl_pt_map_set(pagetable, pte, GSL_PT_PAGE_DIRTY); + superpte = pte - (pte & (GSL_PT_SUPER_PTE - 1)); + if (pte == superpte) + GSL_TLBFLUSH_FILTER_SETDIRTY(superpte / + GSL_PT_SUPER_PTE); + } + + /* Remove the statistics */ + pagetable->stats.entries--; + pagetable->stats.mapped -= range; + + /* Post all writes to the pagetable */ + wmb(); + + spin_unlock(&pagetable->lock); + + gen_pool_free(pagetable->pool, gpuaddr, range); + + return 0; +} + +int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable, + struct kgsl_memdesc *memdesc, unsigned int protflags) +{ + int result = -EINVAL; + unsigned int gpuaddr = 0; + + if (memdesc == NULL) { + KGSL_CORE_ERR("invalid memdesc\n"); + goto error; + } + + gpuaddr = memdesc->gpuaddr; + + result = kgsl_mmu_map(pagetable, memdesc, protflags); + if (result) + goto error; + + /*global mappings must have the same gpu address in all pagetables*/ + if (gpuaddr && gpuaddr != memdesc->gpuaddr) { + KGSL_CORE_ERR("pt %p addr mismatch phys 0x%08x" + "gpu 0x%0x 0x%08x", pagetable, memdesc->physaddr, + gpuaddr, memdesc->gpuaddr); + goto error_unmap; + } + return result; +error_unmap: + kgsl_mmu_unmap(pagetable, memdesc); +error: + return result; +} + +int kgsl_mmu_stop(struct kgsl_device *device) +{ + /* + * stop device mmu + * + * call this with the global lock held + */ + struct kgsl_mmu *mmu = &device->mmu; + + if (mmu->flags & KGSL_FLAGS_STARTED) { + /* disable mh interrupts */ + /* disable MMU */ + kgsl_regwrite(device, device->mmu.reg.interrupt_mask, 0); + kgsl_regwrite(device, device->mmu.reg.config, 0x00000000); + + mmu->flags &= ~KGSL_FLAGS_STARTED; + } + + return 0; +} + +int kgsl_mmu_close(struct kgsl_device *device) +{ + /* + * close device mmu + * + * call this with the global lock held + */ + struct kgsl_mmu *mmu = &device->mmu; + + if (mmu->dummyspace.gpuaddr) + kgsl_sharedmem_free(&mmu->dummyspace); + + if (mmu->defaultpagetable) + kgsl_mmu_putpagetable(mmu->defaultpagetable); + + return 0; +} diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h new file mode 100644 index 0000000000000..4a67ea6794541 --- /dev/null +++ b/drivers/gpu/msm/kgsl_mmu.h @@ -0,0 +1,267 @@ +/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __KGSL_MMU_H +#define __KGSL_MMU_H +#include "kgsl_sharedmem.h" + +/* Identifier for the global page table */ +/* Per process page tables will probably pass in the thread group + as an identifier */ + +#define KGSL_MMU_GLOBAL_PT 0 + +#define GSL_PT_SUPER_PTE 8 +#define GSL_PT_PAGE_WV 0x00000001 +#define GSL_PT_PAGE_RV 0x00000002 +#define GSL_PT_PAGE_DIRTY 0x00000004 +/* MMU Flags */ +#define KGSL_MMUFLAGS_TLBFLUSH 0x10000000 +#define KGSL_MMUFLAGS_PTUPDATE 0x20000000 + +#define MH_INTERRUPT_MASK__AXI_READ_ERROR 0x00000001L +#define MH_INTERRUPT_MASK__AXI_WRITE_ERROR 0x00000002L +#define MH_INTERRUPT_MASK__MMU_PAGE_FAULT 0x00000004L + +/* Macros to manage TLB flushing */ +#define GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS (sizeof(unsigned char) * 8) +#define GSL_TLBFLUSH_FILTER_GET(superpte) \ + (*((unsigned char *) \ + (((unsigned int)pagetable->tlbflushfilter.base) \ + + (superpte / GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS)))) +#define GSL_TLBFLUSH_FILTER_SETDIRTY(superpte) \ + (GSL_TLBFLUSH_FILTER_GET((superpte)) |= 1 << \ + (superpte % GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS)) +#define GSL_TLBFLUSH_FILTER_ISDIRTY(superpte) \ + (GSL_TLBFLUSH_FILTER_GET((superpte)) & \ + (1 << (superpte % GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS))) +#define GSL_TLBFLUSH_FILTER_RESET() memset(pagetable->tlbflushfilter.base,\ + 0, pagetable->tlbflushfilter.size) + + +struct kgsl_device; + +struct kgsl_tlbflushfilter { + unsigned int *base; + unsigned int size; +}; + +struct kgsl_pagetable { + spinlock_t lock; + unsigned int refcnt; + struct kgsl_memdesc base; + uint32_t va_base; + unsigned int va_range; + unsigned int last_superpte; + unsigned int max_entries; + struct gen_pool *pool; + struct list_head list; + unsigned int name; + /* Maintain filter to manage tlb flushing */ + struct kgsl_tlbflushfilter tlbflushfilter; + unsigned int tlb_flags; + struct kobject *kobj; + + struct { + unsigned int entries; + unsigned int mapped; + unsigned int max_mapped; + unsigned int max_entries; + } stats; +}; + +struct kgsl_mmu_reg { + + uint32_t config; + uint32_t mpu_base; + uint32_t mpu_end; + uint32_t va_range; + uint32_t pt_page; + uint32_t page_fault; + uint32_t tran_error; + uint32_t invalidate; + uint32_t interrupt_mask; + uint32_t interrupt_status; + uint32_t interrupt_clear; + uint32_t axi_error; +}; + +struct kgsl_mmu { + unsigned int refcnt; + uint32_t flags; + struct kgsl_device *device; + unsigned int config; + uint32_t mpu_base; + int mpu_range; + struct kgsl_memdesc dummyspace; + struct kgsl_mmu_reg reg; + /* current page table object being used by device mmu */ + struct kgsl_pagetable *defaultpagetable; + struct kgsl_pagetable *hwpagetable; +}; + +struct kgsl_ptpool_chunk { + size_t size; + unsigned int count; + int dynamic; + + void *data; + unsigned int phys; + + unsigned long *bitmap; + struct list_head list; +}; + +struct kgsl_ptpool { + size_t ptsize; + struct mutex lock; + struct list_head list; + int entries; + int static_entries; + int chunks; +}; + +struct kgsl_pagetable *kgsl_mmu_getpagetable(unsigned long name); + +#ifdef CONFIG_MSM_KGSL_MMU + +int kgsl_mmu_init(struct kgsl_device *device); +int kgsl_mmu_start(struct kgsl_device *device); +int kgsl_mmu_stop(struct kgsl_device *device); +int kgsl_mmu_close(struct kgsl_device *device); +int kgsl_mmu_setstate(struct kgsl_device *device, + struct kgsl_pagetable *pagetable); +int kgsl_mmu_map(struct kgsl_pagetable *pagetable, + struct kgsl_memdesc *memdesc, + unsigned int protflags); +int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable, + struct kgsl_memdesc *memdesc, unsigned int protflags); +int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, + struct kgsl_memdesc *memdesc); +void kgsl_ptpool_destroy(struct kgsl_ptpool *pool); +int kgsl_ptpool_init(struct kgsl_ptpool *pool, int ptsize, int entries); +void kgsl_mh_intrcallback(struct kgsl_device *device); +void kgsl_mmu_putpagetable(struct kgsl_pagetable *pagetable); +unsigned int kgsl_virtaddr_to_physaddr(void *virtaddr); + +static inline int kgsl_mmu_enabled(void) +{ + return 1; +} + +#else + +static inline int kgsl_mmu_enabled(void) +{ + return 0; +} + +static inline int kgsl_mmu_init(struct kgsl_device *device) +{ + return 0; +} + +static inline int kgsl_mmu_start(struct kgsl_device *device) +{ + return 0; +} + +static inline int kgsl_mmu_stop(struct kgsl_device *device) +{ + return 0; +} + +static inline int kgsl_mmu_close(struct kgsl_device *device) +{ + return 0; +} + +static inline int kgsl_mmu_setstate(struct kgsl_device *device, + struct kgsl_pagetable *pagetable) +{ + return 0; +} + +static inline int kgsl_mmu_map(struct kgsl_pagetable *pagetable, + struct kgsl_memdesc *memdesc, + unsigned int protflags) +{ + memdesc->gpuaddr = memdesc->physaddr; + return 0; +} + +static inline int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, + struct kgsl_memdesc *memdesc) +{ + return 0; +} + +static inline int kgsl_ptpool_init(struct kgsl_ptpool *pool, int ptsize, + int entries) +{ + return 0; +} + +static inline int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable, + struct kgsl_memdesc *memdesc, unsigned int protflags) +{ + /* gpuaddr is the same that gets passed in */ + return 0; +} + +static inline void kgsl_ptpool_destroy(struct kgsl_ptpool *pool) { } + +static inline void kgsl_mh_intrcallback(struct kgsl_device *device) { } + +static inline void kgsl_mmu_putpagetable(struct kgsl_pagetable *pagetable) { } + +static inline unsigned int kgsl_virtaddr_to_physaddr(void *virtaddr) +{ + return 0; +} + +#endif + +static inline unsigned int kgsl_pt_get_flags(struct kgsl_pagetable *pt, + enum kgsl_deviceid id) +{ + unsigned int result = 0; + + if (pt == NULL) + return 0; + + spin_lock(&pt->lock); + if (pt->tlb_flags && (1<tlb_flags &= ~(1<lock); + return result; +} + +#endif /* __KGSL_MMU_H */ diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c new file mode 100644 index 0000000000000..d039dddbd6034 --- /dev/null +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -0,0 +1,723 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include + +#include "kgsl.h" + +#define SWITCH_OFF 200 +#define TZ_UPDATE_ID 0x01404000 +#define TZ_RESET_ID 0x01403000 + +#ifdef CONFIG_MSM_SECURE_IO +/* Trap into the TrustZone, and call funcs there. */ +static int __secure_tz_entry(u32 cmd, u32 val) +{ + register u32 r0 asm("r0") = cmd; + register u32 r1 asm("r1") = 0x0; + register u32 r2 asm("r2") = val; + + __iowmb(); + asm( + __asmeq("%0", "r0") + __asmeq("%1", "r0") + __asmeq("%2", "r1") + __asmeq("%3", "r2") + "smc #0 @ switch to secure world\n" + : "=r" (r0) + : "r" (r0), "r" (r1), "r" (r2) + ); + return r0; +} +#else +static int __secure_tz_entry(u32 cmd, u32 val) +{ + return 0; +} +#endif /* CONFIG_MSM_SECURE_IO */ + +/* Returns the requested update to our power level. * + * Either up/down (-1/1) a level, or stay the same (0). */ +static inline int kgsl_pwrctrl_tz_update(u32 idle) +{ + return __secure_tz_entry(TZ_UPDATE_ID, idle); +} + +static inline void kgsl_pwrctrl_tz_reset(void) +{ + __secure_tz_entry(TZ_RESET_ID, 0); +} + +static void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, + unsigned int new_level) +{ + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + if (new_level < (pwr->num_pwrlevels - 1) && + new_level >= pwr->thermal_pwrlevel && + new_level != pwr->active_pwrlevel) { + pwr->active_pwrlevel = new_level; + if (pwr->power_flags & KGSL_PWRFLAGS_CLK_ON) + clk_set_rate(pwr->grp_clks[0], + pwr->pwrlevels[pwr->active_pwrlevel]. + gpu_freq); + if (pwr->power_flags & KGSL_PWRFLAGS_AXI_ON) + if (pwr->pcl) + msm_bus_scale_client_update_request(pwr->pcl, + pwr->pwrlevels[pwr->active_pwrlevel]. + bus_freq); + KGSL_PWR_WARN(device, "pwr level changed to %d\n", + pwr->active_pwrlevel); + } +} + +static int kgsl_pwrctrl_gpuclk_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char temp[20]; + int rc, i, delta = 5000000; + unsigned long val; + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + + snprintf(temp, sizeof(temp), "%.*s", + (int)min(count, sizeof(temp) - 1), buf); + rc = strict_strtoul(temp, 0, &val); + if (rc) + return rc; + + mutex_lock(&device->mutex); + /* Find the best match for the requested freq, if it exists */ + + for (i = 0; i < pwr->num_pwrlevels; i++) + if (abs(pwr->pwrlevels[i].gpu_freq - val) < delta) { + pwr->thermal_pwrlevel = i; + break; + } + + kgsl_pwrctrl_pwrlevel_change(device, i); + + mutex_unlock(&device->mutex); + + return count; +} + +static int kgsl_pwrctrl_gpuclk_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + return sprintf(buf, "%d\n", + pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq); +} + +static int kgsl_pwrctrl_pwrnap_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char temp[20]; + unsigned long val; + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + int rc; + + snprintf(temp, sizeof(temp), "%.*s", + (int)min(count, sizeof(temp) - 1), buf); + rc = strict_strtoul(temp, 0, &val); + if (rc) + return rc; + + mutex_lock(&device->mutex); + + if (val == 1) + pwr->nap_allowed = true; + else if (val == 0) + pwr->nap_allowed = false; + + mutex_unlock(&device->mutex); + + return count; +} + +static int kgsl_pwrctrl_pwrnap_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + return sprintf(buf, "%d\n", pwr->nap_allowed); +} + + +static int kgsl_pwrctrl_idle_timer_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char temp[20]; + unsigned long val; + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + const long div = 1000/HZ; + static unsigned int org_interval_timeout = 1; + int rc; + + snprintf(temp, sizeof(temp), "%.*s", + (int)min(count, sizeof(temp) - 1), buf); + rc = strict_strtoul(temp, 0, &val); + if (rc) + return rc; + + if (org_interval_timeout == 1) + org_interval_timeout = pwr->interval_timeout; + + mutex_lock(&device->mutex); + + /* Let the timeout be requested in ms, but convert to jiffies. */ + val /= div; + if (val >= org_interval_timeout) + pwr->interval_timeout = val; + + mutex_unlock(&device->mutex); + + return count; +} + +static int kgsl_pwrctrl_idle_timer_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + return sprintf(buf, "%d\n", pwr->interval_timeout); +} + +static struct device_attribute gpuclk_attr = { + .attr = { .name = "gpuclk", .mode = 0644, }, + .show = kgsl_pwrctrl_gpuclk_show, + .store = kgsl_pwrctrl_gpuclk_store, +}; + +static struct device_attribute pwrnap_attr = { + .attr = { .name = "pwrnap", .mode = 0644, }, + .show = kgsl_pwrctrl_pwrnap_show, + .store = kgsl_pwrctrl_pwrnap_store, +}; + +static struct device_attribute idle_timer_attr = { + .attr = { .name = "idle_timer", .mode = 0644, }, + .show = kgsl_pwrctrl_idle_timer_show, + .store = kgsl_pwrctrl_idle_timer_store, +}; + +int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device) +{ + int ret = 0; + ret = device_create_file(device->dev, &pwrnap_attr); + if (ret == 0) + ret = device_create_file(device->dev, &gpuclk_attr); + if (ret == 0) + ret = device_create_file(device->dev, &idle_timer_attr); + return ret; +} + +unsigned long kgsl_get_clkrate(struct clk *clk) +{ + if (clk != NULL) { + return clk_get_rate(clk); + } else { + return 0; + } +} + +void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device) +{ + device_remove_file(device->dev, &gpuclk_attr); + device_remove_file(device->dev, &pwrnap_attr); + device_remove_file(device->dev, &idle_timer_attr); +} + +static void kgsl_pwrctrl_idle_calc(struct kgsl_device *device) +{ + int idle, val; + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + + idle = device->ftbl.device_idle_calc(device); + if (!idle) + return; + + /* If the GPU has stayed in turbo mode for a while, * + * stop writing out values. */ + if (pwr->active_pwrlevel) + pwr->no_switch_cnt = 0; + else if (pwr->no_switch_cnt > SWITCH_OFF) + return; + pwr->no_switch_cnt++; + val = kgsl_pwrctrl_tz_update(idle); + if (val) + kgsl_pwrctrl_pwrlevel_change(device, + pwr->active_pwrlevel + val); +} + +void kgsl_pwrctrl_clk(struct kgsl_device *device, unsigned int pwrflag) +{ + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + int i = 0; + switch (pwrflag) { + case KGSL_PWRFLAGS_CLK_OFF: + if (pwr->power_flags & KGSL_PWRFLAGS_CLK_ON) { + KGSL_PWR_INFO(device, + "clocks off, device %d\n", device->id); + for (i = KGSL_MAX_CLKS - 1; i > 0; i--) + if (pwr->grp_clks[i]) + clk_disable(pwr->grp_clks[i]); + if ((pwr->pwrlevels[0].gpu_freq > 0) && + (device->requested_state != KGSL_STATE_NAP)) + clk_set_rate(pwr->grp_clks[0], + pwr->pwrlevels[pwr->num_pwrlevels - 1]. + gpu_freq); + pwr->power_flags &= + ~(KGSL_PWRFLAGS_CLK_ON); + pwr->power_flags |= KGSL_PWRFLAGS_CLK_OFF; + } + return; + case KGSL_PWRFLAGS_CLK_ON: + if (pwr->power_flags & KGSL_PWRFLAGS_CLK_OFF) { + KGSL_PWR_INFO(device, + "clocks on, device %d\n", device->id); + + pwr->power_flags &= + ~(KGSL_PWRFLAGS_CLK_OFF); + pwr->power_flags |= KGSL_PWRFLAGS_CLK_ON; + + if ((pwr->pwrlevels[0].gpu_freq > 0) && + (device->state != KGSL_STATE_NAP)) + clk_set_rate(pwr->grp_clks[0], + pwr->pwrlevels[pwr->active_pwrlevel]. + gpu_freq); + + /* as last step, enable grp_clk + this is to let GPU interrupt to come */ + for (i = KGSL_MAX_CLKS - 1; i > 0; i--) + if (pwr->grp_clks[i]) + clk_enable(pwr->grp_clks[i]); + } + return; + default: + return; + } +} + +void kgsl_pwrctrl_axi(struct kgsl_device *device, unsigned int pwrflag) +{ + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + + switch (pwrflag) { + case KGSL_PWRFLAGS_AXI_OFF: + if (pwr->power_flags & KGSL_PWRFLAGS_AXI_ON) { + KGSL_PWR_INFO(device, + "axi off, device %d\n", device->id); + if (pwr->ebi1_clk) + clk_disable(pwr->ebi1_clk); + if (pwr->pcl) + msm_bus_scale_client_update_request(pwr->pcl, + 0); + pwr->power_flags &= + ~(KGSL_PWRFLAGS_AXI_ON); + pwr->power_flags |= KGSL_PWRFLAGS_AXI_OFF; + } + return; + case KGSL_PWRFLAGS_AXI_ON: + if (pwr->power_flags & KGSL_PWRFLAGS_AXI_OFF) { + KGSL_PWR_INFO(device, + "axi on, device %d\n", device->id); + if (pwr->ebi1_clk) + clk_enable(pwr->ebi1_clk); + if (pwr->pcl) + msm_bus_scale_client_update_request(pwr->pcl, + pwr->pwrlevels[pwr->active_pwrlevel]. + bus_freq); + pwr->power_flags &= + ~(KGSL_PWRFLAGS_AXI_OFF); + pwr->power_flags |= KGSL_PWRFLAGS_AXI_ON; + } + return; + default: + return; + } +} + + +void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, unsigned int pwrflag) +{ + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + + switch (pwrflag) { + case KGSL_PWRFLAGS_POWER_OFF: + if (pwr->power_flags & KGSL_PWRFLAGS_POWER_ON) { + KGSL_PWR_INFO(device, + "power off, device %d\n", device->id); + if (internal_pwr_rail_ctl(pwr->pwr_rail, false)) { + KGSL_DRV_ERR(device, + "call internal_pwr_rail_ctl failed\n"); + return; + } + if (pwr->gpu_reg) + regulator_disable(pwr->gpu_reg); + pwr->power_flags &= + ~(KGSL_PWRFLAGS_POWER_ON); + pwr->power_flags |= + KGSL_PWRFLAGS_POWER_OFF; + } + return; + case KGSL_PWRFLAGS_POWER_ON: + if (pwr->power_flags & KGSL_PWRFLAGS_POWER_OFF) { + KGSL_PWR_INFO(device, + "power on, device %d\n", device->id); + if (internal_pwr_rail_ctl(pwr->pwr_rail, true)) { + KGSL_PWR_ERR(device, + "call internal_pwr_rail_ctl failed\n"); + return; + } + + if (pwr->gpu_reg) + regulator_enable(pwr->gpu_reg); + pwr->power_flags &= + ~(KGSL_PWRFLAGS_POWER_OFF); + pwr->power_flags |= + KGSL_PWRFLAGS_POWER_ON; + } + return; + default: + return; + } +} + + +void kgsl_pwrctrl_irq(struct kgsl_device *device, unsigned int pwrflag) +{ + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + switch (pwrflag) { + case KGSL_PWRFLAGS_IRQ_ON: + if (pwr->power_flags & KGSL_PWRFLAGS_IRQ_OFF) { + KGSL_PWR_INFO(device, + "irq on, device %d\n", device->id); + pwr->power_flags &= + ~(KGSL_PWRFLAGS_IRQ_OFF); + pwr->power_flags |= KGSL_PWRFLAGS_IRQ_ON; + enable_irq(pwr->interrupt_num); + } + return; + case KGSL_PWRFLAGS_IRQ_OFF: + if (pwr->power_flags & KGSL_PWRFLAGS_IRQ_ON) { + KGSL_PWR_INFO(device, + "irq off, device %d\n", device->id); + disable_irq(pwr->interrupt_num); + pwr->power_flags &= + ~(KGSL_PWRFLAGS_IRQ_ON); + pwr->power_flags |= KGSL_PWRFLAGS_IRQ_OFF; + } + return; + default: + return; + } +} + +int kgsl_pwrctrl_init(struct kgsl_device *device) +{ + int i, result = 0; + struct clk *clk; + struct platform_device *pdev = device->pdev; + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data; + struct kgsl_device_pwr_data *pdata_pwr = &pdata_dev->pwr_data; + const char *clk_names[KGSL_MAX_CLKS] = {pwr->src_clk_name, + pdata_dev->clk.name.clk, + pdata_dev->clk.name.pclk, + pdata_dev->imem_clk_name.clk, + pdata_dev->imem_clk_name.pclk}; + + /*acquire clocks */ + for (i = 1; i < KGSL_MAX_CLKS; i++) { + if (clk_names[i]) { + clk = clk_get(&pdev->dev, clk_names[i]); + if (IS_ERR(clk)) + goto clk_err; + pwr->grp_clks[i] = clk; + } + } + /* Make sure we have a source clk for freq setting */ + clk = clk_get(&pdev->dev, clk_names[0]); + pwr->grp_clks[0] = (IS_ERR(clk)) ? pwr->grp_clks[1] : clk; + + /* put the AXI bus into asynchronous mode with the graphics cores */ + if (pdata_pwr->set_grp_async != NULL) + pdata_pwr->set_grp_async(); + + if (pdata_pwr->num_levels > KGSL_MAX_PWRLEVELS) { + KGSL_PWR_ERR(device, "invalid power level count: %d\n", + pdata_pwr->num_levels); + result = -EINVAL; + goto done; + } + pwr->num_pwrlevels = pdata_pwr->num_levels; + pwr->active_pwrlevel = pdata_pwr->init_level; + for (i = 0; i < pdata_pwr->num_levels; i++) { + pwr->pwrlevels[i].gpu_freq = + (pdata_pwr->pwrlevel[i].gpu_freq > 0) ? + clk_round_rate(pwr->grp_clks[0], + pdata_pwr->pwrlevel[i]. + gpu_freq) : 0; + pwr->pwrlevels[i].bus_freq = + pdata_pwr->pwrlevel[i].bus_freq; + } + /* Do not set_rate for targets in sync with AXI */ + if (pwr->pwrlevels[0].gpu_freq > 0) + clk_set_rate(pwr->grp_clks[0], pwr-> + pwrlevels[pwr->num_pwrlevels - 1].gpu_freq); + + pwr->gpu_reg = regulator_get(NULL, pwr->regulator_name); + if (IS_ERR(pwr->gpu_reg)) + pwr->gpu_reg = NULL; + if (internal_pwr_rail_mode(device->pwrctrl.pwr_rail, + PWR_RAIL_CTL_MANUAL)) { + KGSL_PWR_ERR(device, "internal_pwr_rail_mode failed\n"); + result = -EINVAL; + goto done; + } + + pwr->power_flags = KGSL_PWRFLAGS_CLK_OFF | + KGSL_PWRFLAGS_AXI_OFF | KGSL_PWRFLAGS_POWER_OFF | + KGSL_PWRFLAGS_IRQ_OFF; + pwr->nap_allowed = pdata_pwr->nap_allowed; + pwr->pwrrail_first = pdata_pwr->pwrrail_first; + pwr->idle_pass = pdata_pwr->idle_pass; + pwr->interval_timeout = pdata_pwr->idle_timeout; + pwr->ebi1_clk = clk_get(NULL, "ebi1_kgsl_clk"); + if (IS_ERR(pwr->ebi1_clk)) + pwr->ebi1_clk = NULL; + else + clk_set_rate(pwr->ebi1_clk, + pwr->pwrlevels[pwr->active_pwrlevel]. + bus_freq); + if (pdata_dev->clk.bus_scale_table != NULL) { + pwr->pcl = + msm_bus_scale_register_client(pdata_dev->clk. + bus_scale_table); + if (!pwr->pcl) { + KGSL_PWR_ERR(device, + "msm_bus_scale_register_client failed: " + "id %d table %p", device->id, + pdata_dev->clk.bus_scale_table); + result = -EINVAL; + goto done; + } + } + + /*acquire interrupt */ + pwr->interrupt_num = + platform_get_irq_byname(pdev, pwr->irq_name); + + if (pwr->interrupt_num <= 0) { + KGSL_PWR_ERR(device, "platform_get_irq_byname failed: %d\n", + pwr->interrupt_num); + result = -EINVAL; + goto done; + } + return result; + +clk_err: + result = PTR_ERR(clk); + KGSL_PWR_ERR(device, "clk_get(%s) failed: %d\n", + clk_names[i], result); + +done: + return result; +} + +void kgsl_pwrctrl_close(struct kgsl_device *device) +{ + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + int i; + + KGSL_PWR_INFO(device, "close device %d\n", device->id); + + if (pwr->interrupt_num > 0) { + if (pwr->have_irq) { + free_irq(pwr->interrupt_num, NULL); + pwr->have_irq = 0; + } + pwr->interrupt_num = 0; + } + + clk_put(pwr->ebi1_clk); + + if (pwr->pcl) + msm_bus_scale_unregister_client(pwr->pcl); + + pwr->pcl = 0; + + if (pwr->gpu_reg) { + regulator_put(pwr->gpu_reg); + pwr->gpu_reg = NULL; + } + + for (i = 1; i < KGSL_MAX_CLKS; i++) + if (pwr->grp_clks[i]) { + clk_put(pwr->grp_clks[i]); + pwr->grp_clks[i] = NULL; + } + + pwr->grp_clks[0] = NULL; + pwr->power_flags = 0; +} + +void kgsl_idle_check(struct work_struct *work) +{ + struct kgsl_device *device = container_of(work, struct kgsl_device, + idle_check_ws); + + mutex_lock(&device->mutex); + if ((device->pwrctrl.idle_pass) && + (device->requested_state != KGSL_STATE_SLEEP)) + kgsl_pwrctrl_idle_calc(device); + + if (device->state & (KGSL_STATE_ACTIVE | KGSL_STATE_NAP) && + device->pwrctrl.nap_allowed) { + if (kgsl_pwrctrl_sleep(device) != 0) + mod_timer(&device->idle_timer, + jiffies + + device->pwrctrl.interval_timeout); + } else if (device->state & (KGSL_STATE_HUNG | + KGSL_STATE_DUMP_AND_RECOVER)) { + device->requested_state = KGSL_STATE_NONE; + } + + mutex_unlock(&device->mutex); +} + +void kgsl_timer(unsigned long data) +{ + struct kgsl_device *device = (struct kgsl_device *) data; + + KGSL_PWR_INFO(device, "idle timer expired device %d\n", device->id); + if (device->requested_state != KGSL_STATE_SUSPEND) { + device->requested_state = KGSL_STATE_SLEEP; + /* Have work run in a non-interrupt context. */ + queue_work(device->work_queue, &device->idle_check_ws); + } +} + +void kgsl_pre_hwaccess(struct kgsl_device *device) +{ + BUG_ON(!mutex_is_locked(&device->mutex)); + if (device->state & (KGSL_STATE_SLEEP | KGSL_STATE_NAP)) + kgsl_pwrctrl_wake(device); +} + +void kgsl_check_suspended(struct kgsl_device *device) +{ + if (device->requested_state == KGSL_STATE_SUSPEND || + device->state == KGSL_STATE_SUSPEND) { + mutex_unlock(&device->mutex); + wait_for_completion(&device->hwaccess_gate); + mutex_lock(&device->mutex); + } + if (device->state == KGSL_STATE_DUMP_AND_RECOVER) { + mutex_unlock(&device->mutex); + wait_for_completion(&device->recovery_gate); + mutex_lock(&device->mutex); + } + } + + +/******************************************************************/ +/* Caller must hold the device mutex. */ +int kgsl_pwrctrl_sleep(struct kgsl_device *device) +{ + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + KGSL_PWR_INFO(device, "sleep device %d\n", device->id); + + /* Work through the legal state transitions */ + if (device->requested_state == KGSL_STATE_NAP) { + if (device->ftbl.device_isidle(device)) + goto nap; + } else if (device->requested_state == KGSL_STATE_SLEEP) { + if (device->state == KGSL_STATE_NAP || + device->ftbl.device_isidle(device)) + goto sleep; + } + + device->requested_state = KGSL_STATE_NONE; + return -EBUSY; + +sleep: + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); + /*In 7x27a, do not turn off axi and gpu clocks*/ + if (cpu_is_msm7x27a()) + goto end; + kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_OFF); + if (pwr->pwrlevels[0].gpu_freq > 0) + clk_set_rate(pwr->grp_clks[0], + pwr->pwrlevels[pwr->num_pwrlevels - 1]. + gpu_freq); + device->pwrctrl.no_switch_cnt = 0; + device->pwrctrl.time = 0; + kgsl_pwrctrl_tz_reset(); + goto clk_off; + +nap: + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); +clk_off: + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_OFF); + +end: + device->state = device->requested_state; + device->requested_state = KGSL_STATE_NONE; + wake_unlock(&device->idle_wakelock); + KGSL_PWR_WARN(device, "state -> NAP/SLEEP(%d), device %d\n", + device->state, device->id); + + return 0; +} + + +/******************************************************************/ +/* Caller must hold the device mutex. */ +void kgsl_pwrctrl_wake(struct kgsl_device *device) +{ + if (device->state == KGSL_STATE_SUSPEND) + return; + + if (device->state != KGSL_STATE_NAP) { + if (device->pwrctrl.idle_pass) + kgsl_pwrctrl_pwrlevel_change(device, + device->pwrctrl.thermal_pwrlevel); + kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_ON); + } + /* Turn on the core clocks */ + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_ON); + + /* Enable state before turning on irq */ + device->state = KGSL_STATE_ACTIVE; + KGSL_PWR_WARN(device, "state -> ACTIVE, device %d\n", device->id); + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_ON); + + /* Re-enable HW access */ + mod_timer(&device->idle_timer, + jiffies + device->pwrctrl.interval_timeout); + + wake_lock(&device->idle_wakelock); + KGSL_PWR_INFO(device, "wake return for device %d\n", device->id); +} + diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h new file mode 100644 index 0000000000000..3277b6a5d6c15 --- /dev/null +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -0,0 +1,93 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __KGSL_PWRCTRL_H +#define __KGSL_PWRCTRL_H + +#include + +/***************************************************************************** +** power flags +*****************************************************************************/ +#define KGSL_PWRFLAGS_POWER_OFF 0x00000001 +#define KGSL_PWRFLAGS_POWER_ON 0x00000002 +#define KGSL_PWRFLAGS_CLK_ON 0x00000004 +#define KGSL_PWRFLAGS_CLK_OFF 0x00000008 +#define KGSL_PWRFLAGS_AXI_ON 0x00000010 +#define KGSL_PWRFLAGS_AXI_OFF 0x00000020 +#define KGSL_PWRFLAGS_IRQ_ON 0x00000040 +#define KGSL_PWRFLAGS_IRQ_OFF 0x00000080 + +#define KGSL_DEFAULT_PWRLEVEL 1 +#define KGSL_MAX_CLKS 5 + +struct platform_device; + +struct kgsl_pwrctrl { + int interrupt_num; + int have_irq; + unsigned int pwr_rail; + struct clk *ebi1_clk; + struct clk *grp_clks[KGSL_MAX_CLKS]; + unsigned int power_flags; + struct kgsl_pwrlevel pwrlevels[KGSL_MAX_PWRLEVELS]; + unsigned int active_pwrlevel; + int thermal_pwrlevel; + unsigned int num_pwrlevels; + unsigned int interval_timeout; + struct regulator *gpu_reg; + uint32_t pcl; + unsigned int nap_allowed; + struct adreno_context *suspended_ctxt; + const char *regulator_name; + const char *irq_name; + const char *src_clk_name; + bool pwrrail_first; + s64 time; + unsigned int no_switch_cnt; + unsigned int idle_pass; +}; + +void kgsl_pwrctrl_clk(struct kgsl_device *device, unsigned int pwrflag); +void kgsl_pwrctrl_axi(struct kgsl_device *device, unsigned int pwrflag); +void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, unsigned int pwrflag); +void kgsl_pwrctrl_irq(struct kgsl_device *device, unsigned int pwrflag); +int kgsl_pwrctrl_init(struct kgsl_device *device); +void kgsl_pwrctrl_close(struct kgsl_device *device); +void kgsl_timer(unsigned long data); +void kgsl_idle_check(struct work_struct *work); +void kgsl_pre_hwaccess(struct kgsl_device *device); +void kgsl_check_suspended(struct kgsl_device *device); +int kgsl_pwrctrl_sleep(struct kgsl_device *device); +void kgsl_pwrctrl_wake(struct kgsl_device *device); +unsigned long kgsl_get_clkrate(struct clk *clk); + +int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device); +void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device); + +#endif /* __KGSL_PWRCTRL_H */ diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c new file mode 100644 index 0000000000000..15e6b4fd38cf1 --- /dev/null +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -0,0 +1,646 @@ +/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include + +#include "kgsl.h" +#include "kgsl_sharedmem.h" +#include "kgsl_cffdump.h" + +static struct kgsl_process_private * +_get_priv_from_kobj(struct kobject *kobj) +{ + struct kgsl_process_private *private; + unsigned long name; + + if (!kobj) + return NULL; + + if (sscanf(kobj->name, "%ld", &name) != 1) + return NULL; + + list_for_each_entry(private, &kgsl_driver.process_list, list) { + if (private->pid == name) + return private; + } + + return NULL; +} + +/* sharedmem / memory sysfs files */ + +static ssize_t +process_show_vmalloc(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + + struct kgsl_process_private *priv; + int ret = 0; + + mutex_lock(&kgsl_driver.process_mutex); + priv = _get_priv_from_kobj(kobj); + + if (priv) + ret += sprintf(buf, "%d\n", priv->stats.vmalloc); + + mutex_unlock(&kgsl_driver.process_mutex); + return ret; +} + +static ssize_t +process_show_vmalloc_max(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + + struct kgsl_process_private *priv; + int ret = 0; + + mutex_lock(&kgsl_driver.process_mutex); + priv = _get_priv_from_kobj(kobj); + + if (priv) + ret += sprintf(buf, "%d\n", priv->stats.vmalloc_max); + + mutex_unlock(&kgsl_driver.process_mutex); + return ret; +} + +static ssize_t +process_show_exmem(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + + struct kgsl_process_private *priv; + int ret = 0; + + mutex_lock(&kgsl_driver.process_mutex); + priv = _get_priv_from_kobj(kobj); + + if (priv) + ret += sprintf(buf, "%d\n", priv->stats.exmem); + + mutex_unlock(&kgsl_driver.process_mutex); + return ret; +} + +static ssize_t +process_show_exmem_max(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + + struct kgsl_process_private *priv; + int ret = 0; + + mutex_lock(&kgsl_driver.process_mutex); + priv = _get_priv_from_kobj(kobj); + + if (priv) + ret += sprintf(buf, "%d\n", priv->stats.exmem_max); + + mutex_unlock(&kgsl_driver.process_mutex); + return ret; +} + +static ssize_t +process_show_flushes(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct kgsl_process_private *priv; + int ret = 0; + + mutex_lock(&kgsl_driver.process_mutex); + priv = _get_priv_from_kobj(kobj); + + if (priv) + ret += sprintf(buf, "%d\n", priv->stats.flushes); + + mutex_unlock(&kgsl_driver.process_mutex); + return ret; +} + +static struct kobj_attribute attr_vmalloc = { + .attr = { .name = "vmalloc", .mode = 0444 }, + .show = process_show_vmalloc, + .store = NULL, +}; + +static struct kobj_attribute attr_vmalloc_max = { + .attr = { .name = "vmalloc_max", .mode = 0444 }, + .show = process_show_vmalloc_max, + .store = NULL, +}; + +static struct kobj_attribute attr_exmem = { + .attr = { .name = "exmem", .mode = 0444 }, + .show = process_show_exmem, + .store = NULL, +}; + +static struct kobj_attribute attr_exmem_max = { + .attr = { .name = "exmem_max", .mode = 0444 }, + .show = process_show_exmem_max, + .store = NULL, +}; + +static struct kobj_attribute attr_flushes = { + .attr = { .name = "flushes", .mode = 0444 }, + .show = process_show_flushes, + .store = NULL, +}; + +static struct attribute *process_attrs[] = { + &attr_vmalloc.attr, + &attr_vmalloc_max.attr, + &attr_exmem.attr, + &attr_exmem_max.attr, + &attr_flushes.attr, + NULL +}; + +static struct attribute_group process_attr_group = { + .attrs = process_attrs, +}; + +void +kgsl_process_uninit_sysfs(struct kgsl_process_private *private) +{ + /* Remove the sysfs entry */ + if (private->kobj) { + sysfs_remove_group(private->kobj, &process_attr_group); + kobject_put(private->kobj); + } +} + +void +kgsl_process_init_sysfs(struct kgsl_process_private *private) +{ + unsigned char name[16]; + + /* Add a entry to the sysfs device */ + snprintf(name, sizeof(name), "%d", private->pid); + private->kobj = kobject_create_and_add(name, kgsl_driver.prockobj); + + /* sysfs failure isn't fatal, just annoying */ + if (private->kobj != NULL) { + if (sysfs_create_group(private->kobj, &process_attr_group)) { + kobject_put(private->kobj); + private->kobj = NULL; + } + } +} + +static int kgsl_drv_vmalloc_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", kgsl_driver.stats.vmalloc); +} + +static int kgsl_drv_vmalloc_max_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", kgsl_driver.stats.vmalloc_max); +} + +static int kgsl_drv_coherent_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", kgsl_driver.stats.coherent); +} + +static int kgsl_drv_coherent_max_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", kgsl_driver.stats.coherent_max); +} + +static int kgsl_drv_histogram_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int len = 0; + int i; + + for (i = 0; i < 16; i++) + len += sprintf(buf + len, "%d ", + kgsl_driver.stats.histogram[i]); + + len += sprintf(buf + len, "\n"); + return len; +} + +static struct device_attribute drv_vmalloc_attr = { + .attr = { .name = "vmalloc", .mode = 0444, }, + .show = kgsl_drv_vmalloc_show, + .store = NULL, +}; + +static struct device_attribute drv_vmalloc_max_attr = { + .attr = { .name = "vmalloc_max", .mode = 0444, }, + .show = kgsl_drv_vmalloc_max_show, + .store = NULL, +}; + +static struct device_attribute drv_coherent_attr = { + .attr = { .name = "coherent", .mode = 0444, }, + .show = kgsl_drv_coherent_show, + .store = NULL, +}; + +static struct device_attribute drv_coherent_max_attr = { + .attr = { .name = "coherent_max", .mode = 0444, }, + .show = kgsl_drv_coherent_max_show, + .store = NULL, +}; + +static struct device_attribute drv_histogram_attr = { + .attr = { .name = "histogram", .mode = 0444, }, + .show = kgsl_drv_histogram_show, + .store = NULL, +}; + +void +kgsl_sharedmem_uninit_sysfs(void) +{ + device_remove_file(&kgsl_driver.virtdev, &drv_vmalloc_attr); + device_remove_file(&kgsl_driver.virtdev, &drv_vmalloc_max_attr); + device_remove_file(&kgsl_driver.virtdev, &drv_coherent_attr); + device_remove_file(&kgsl_driver.virtdev, &drv_coherent_max_attr); + device_remove_file(&kgsl_driver.virtdev, &drv_histogram_attr); +} + +int +kgsl_sharedmem_init_sysfs(void) +{ + int ret; + + ret = device_create_file(&kgsl_driver.virtdev, + &drv_vmalloc_attr); + ret |= device_create_file(&kgsl_driver.virtdev, + &drv_vmalloc_max_attr); + ret |= device_create_file(&kgsl_driver.virtdev, + &drv_coherent_attr); + ret |= device_create_file(&kgsl_driver.virtdev, + &drv_coherent_max_attr); + ret |= device_create_file(&kgsl_driver.virtdev, + &drv_histogram_attr); + + return ret; +} + +#ifdef CONFIG_OUTER_CACHE +static void _outer_cache_range_op(int op, unsigned long addr, size_t size) +{ + switch (op) { + case KGSL_CACHE_OP_FLUSH: + outer_flush_range(addr, addr + size); + break; + case KGSL_CACHE_OP_CLEAN: + outer_clean_range(addr, addr + size); + break; + case KGSL_CACHE_OP_INV: + outer_inv_range(addr, addr + size); + break; + } + + mb(); +} +#endif + +static unsigned long kgsl_vmalloc_physaddr(struct kgsl_memdesc *memdesc, + unsigned int offset) +{ + unsigned int addr; + + if (offset > memdesc->size) + return 0; + + addr = vmalloc_to_pfn(memdesc->hostptr + offset); + return addr << PAGE_SHIFT; +} + +#ifdef CONFIG_OUTER_CACHE +static void kgsl_vmalloc_outer_cache(struct kgsl_memdesc *memdesc, int op) +{ + void *vaddr = memdesc->hostptr; + for (; vaddr < (memdesc->hostptr + memdesc->size); vaddr += PAGE_SIZE) { + unsigned long paddr = page_to_phys(vmalloc_to_page(vaddr)); + _outer_cache_range_op(op, paddr, PAGE_SIZE); + } +} +#endif + +static int kgsl_vmalloc_vmfault(struct kgsl_memdesc *memdesc, + struct vm_area_struct *vma, + struct vm_fault *vmf) +{ + unsigned long offset, pg; + struct page *page; + + offset = (unsigned long) vmf->virtual_address - vma->vm_start; + pg = (unsigned long) memdesc->hostptr + offset; + + page = vmalloc_to_page((void *) pg); + if (page == NULL) + return VM_FAULT_SIGBUS; + + get_page(page); + + vmf->page = page; + return 0; +} + +static int kgsl_vmalloc_vmflags(struct kgsl_memdesc *memdesc) +{ + return VM_RESERVED | VM_DONTEXPAND; +} + +static void kgsl_vmalloc_free(struct kgsl_memdesc *memdesc) +{ + kgsl_driver.stats.vmalloc -= memdesc->size; + vfree(memdesc->hostptr); +} + +static void kgsl_coherent_free(struct kgsl_memdesc *memdesc) +{ + kgsl_driver.stats.coherent -= memdesc->size; + dma_free_coherent(NULL, memdesc->size, + memdesc->hostptr, memdesc->physaddr); +} + +static unsigned long kgsl_contig_physaddr(struct kgsl_memdesc *memdesc, + unsigned int offset) +{ + if (offset > memdesc->size) + return 0; + + return memdesc->physaddr + offset; +} + +#ifdef CONFIG_OUTER_CACHE +static void kgsl_contig_outer_cache(struct kgsl_memdesc *memdesc, int op) +{ + _outer_cache_range_op(op, memdesc->physaddr, memdesc->size); +} +#endif + +#ifdef CONFIG_OUTER_CACHE +static void kgsl_userptr_outer_cache(struct kgsl_memdesc *memdesc, int op) +{ + void *vaddr = memdesc->hostptr; + for (; vaddr < (memdesc->hostptr + memdesc->size); vaddr += PAGE_SIZE) { + unsigned long paddr = kgsl_virtaddr_to_physaddr(vaddr); + if (paddr) + _outer_cache_range_op(op, paddr, PAGE_SIZE); + } +} +#endif + +static unsigned long kgsl_userptr_physaddr(struct kgsl_memdesc *memdesc, + unsigned int offset) +{ + return kgsl_virtaddr_to_physaddr(memdesc->hostptr + offset); +} + +/* Global - also used by kgsl_drm.c */ +struct kgsl_memdesc_ops kgsl_vmalloc_ops = { + .physaddr = kgsl_vmalloc_physaddr, + .free = kgsl_vmalloc_free, + .vmflags = kgsl_vmalloc_vmflags, + .vmfault = kgsl_vmalloc_vmfault, +#ifdef CONFIG_OUTER_CACHE + .outer_cache = kgsl_vmalloc_outer_cache, +#endif +}; +EXPORT_SYMBOL(kgsl_vmalloc_ops); + +static struct kgsl_memdesc_ops kgsl_coherent_ops = { + .physaddr = kgsl_contig_physaddr, + .free = kgsl_coherent_free, +#ifdef CONFIG_OUTER_CACHE + .outer_cache = kgsl_contig_outer_cache, +#endif +}; + +/* Global - also used by kgsl.c and kgsl_drm.c */ +struct kgsl_memdesc_ops kgsl_contig_ops = { + .physaddr = kgsl_contig_physaddr, +#ifdef CONFIG_OUTER_CACHE + .outer_cache = kgsl_contig_outer_cache +#endif +}; +EXPORT_SYMBOL(kgsl_contig_ops); + +/* Global - also used by kgsl.c */ +struct kgsl_memdesc_ops kgsl_userptr_ops = { + .physaddr = kgsl_userptr_physaddr, +#ifdef CONFIG_OUTER_CACHE + .outer_cache = kgsl_userptr_outer_cache, +#endif +}; +EXPORT_SYMBOL(kgsl_userptr_ops); + +void kgsl_cache_range_op(struct kgsl_memdesc *memdesc, int op) +{ + void *addr = memdesc->hostptr; + int size = memdesc->size; + + switch (op) { + case KGSL_CACHE_OP_FLUSH: + dmac_flush_range(addr, addr + size); + break; + case KGSL_CACHE_OP_CLEAN: + dmac_clean_range(addr, addr + size); + break; + case KGSL_CACHE_OP_INV: + dmac_inv_range(addr, addr + size); + break; + } + + if (memdesc->ops->outer_cache) + memdesc->ops->outer_cache(memdesc, op); +} +EXPORT_SYMBOL(kgsl_cache_range_op); + +static int +_kgsl_sharedmem_vmalloc(struct kgsl_memdesc *memdesc, + struct kgsl_pagetable *pagetable, + void *ptr, size_t size, unsigned int protflags) +{ + int result; + + memdesc->size = size; + memdesc->pagetable = pagetable; + memdesc->priv = KGSL_MEMFLAGS_CACHED; + memdesc->ops = &kgsl_vmalloc_ops; + memdesc->hostptr = (void *) ptr; + + kgsl_cache_range_op(memdesc, KGSL_CACHE_OP_INV); + + result = kgsl_mmu_map(pagetable, memdesc, protflags); + + if (result) { + kgsl_sharedmem_free(memdesc); + } else { + int order; + + KGSL_STATS_ADD(size, kgsl_driver.stats.vmalloc, + kgsl_driver.stats.vmalloc_max); + + order = get_order(size); + + if (order < 16) + kgsl_driver.stats.histogram[order]++; + } + + return result; +} + +int +kgsl_sharedmem_vmalloc(struct kgsl_memdesc *memdesc, + struct kgsl_pagetable *pagetable, size_t size) +{ + void *ptr; + + BUG_ON(size == 0); + + size = ALIGN(size, PAGE_SIZE * 2); + ptr = vmalloc(size); + + if (ptr == NULL) { + KGSL_CORE_ERR("vmalloc(%d) failed\n", size); + return -ENOMEM; + } + + return _kgsl_sharedmem_vmalloc(memdesc, pagetable, ptr, size, + GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); +} +EXPORT_SYMBOL(kgsl_sharedmem_vmalloc); + +int +kgsl_sharedmem_vmalloc_user(struct kgsl_memdesc *memdesc, + struct kgsl_pagetable *pagetable, + size_t size, int flags) +{ + void *ptr; + unsigned int protflags; + + BUG_ON(size == 0); + ptr = vmalloc_user(size); + + if (ptr == NULL) { + KGSL_CORE_ERR("vmalloc_user(%d) failed: allocated=%d\n", + size, kgsl_driver.stats.vmalloc); + return -ENOMEM; + } + + protflags = GSL_PT_PAGE_RV; + if (!(flags & KGSL_MEMFLAGS_GPUREADONLY)) + protflags |= GSL_PT_PAGE_WV; + + return _kgsl_sharedmem_vmalloc(memdesc, pagetable, ptr, size, + protflags); +} +EXPORT_SYMBOL(kgsl_sharedmem_vmalloc_user); + +int +kgsl_sharedmem_alloc_coherent(struct kgsl_memdesc *memdesc, size_t size) +{ + size = ALIGN(size, PAGE_SIZE); + + memdesc->hostptr = dma_alloc_coherent(NULL, size, &memdesc->physaddr, + GFP_KERNEL); + if (memdesc->hostptr == NULL) { + KGSL_CORE_ERR("dma_alloc_coherent(%d) failed\n", size); + return -ENOMEM; + } + + memdesc->size = size; + memdesc->ops = &kgsl_coherent_ops; + + /* Record statistics */ + + KGSL_STATS_ADD(size, kgsl_driver.stats.coherent, + kgsl_driver.stats.coherent_max); + + return 0; +} +EXPORT_SYMBOL(kgsl_sharedmem_alloc_coherent); + +void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc) +{ + if (memdesc == NULL || memdesc->size == 0) + return; + + if (memdesc->gpuaddr) + kgsl_mmu_unmap(memdesc->pagetable, memdesc); + + if (memdesc->ops->free) + memdesc->ops->free(memdesc); + + memset(memdesc, 0, sizeof(*memdesc)); +} +EXPORT_SYMBOL(kgsl_sharedmem_free); + +int +kgsl_sharedmem_readl(const struct kgsl_memdesc *memdesc, + uint32_t *dst, + unsigned int offsetbytes) +{ + BUG_ON(memdesc == NULL || memdesc->hostptr == NULL || dst == NULL); + WARN_ON(offsetbytes + sizeof(unsigned int) > memdesc->size); + + if (offsetbytes + sizeof(unsigned int) > memdesc->size) + return -ERANGE; + + *dst = readl(memdesc->hostptr + offsetbytes); + return 0; +} +EXPORT_SYMBOL(kgsl_sharedmem_readl); + +int +kgsl_sharedmem_writel(const struct kgsl_memdesc *memdesc, + unsigned int offsetbytes, + uint32_t src) +{ + BUG_ON(memdesc == NULL || memdesc->hostptr == NULL); + BUG_ON(offsetbytes + sizeof(unsigned int) > memdesc->size); + + kgsl_cffdump_setmem(memdesc->physaddr + offsetbytes, + src, sizeof(uint)); + writel(src, memdesc->hostptr + offsetbytes); + return 0; +} +EXPORT_SYMBOL(kgsl_sharedmem_writel); + +int +kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc, unsigned int offsetbytes, + unsigned int value, unsigned int sizebytes) +{ + BUG_ON(memdesc == NULL || memdesc->hostptr == NULL); + BUG_ON(offsetbytes + sizebytes > memdesc->size); + + kgsl_cffdump_setmem(memdesc->physaddr + offsetbytes, value, + sizebytes); + memset(memdesc->hostptr + offsetbytes, value, sizebytes); + return 0; +} +EXPORT_SYMBOL(kgsl_sharedmem_set); diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h new file mode 100644 index 0000000000000..d0070584a3b11 --- /dev/null +++ b/drivers/gpu/msm/kgsl_sharedmem.h @@ -0,0 +1,116 @@ +/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __KGSL_SHAREDMEM_H +#define __KGSL_SHAREDMEM_H + +#include + +struct kgsl_pagetable; +struct kgsl_device; +struct kgsl_process_private; + +#define KGSL_CACHE_OP_INV 0x01 +#define KGSL_CACHE_OP_FLUSH 0x02 +#define KGSL_CACHE_OP_CLEAN 0x03 + +/** Set if the memdesc describes cached memory */ +#define KGSL_MEMFLAGS_CACHED 0x00000001 + +struct kgsl_memdesc; + +struct kgsl_memdesc_ops { + unsigned long (*physaddr)(struct kgsl_memdesc *, unsigned int); + void (*outer_cache)(struct kgsl_memdesc *, int); + int (*vmflags)(struct kgsl_memdesc *); + int (*vmfault)(struct kgsl_memdesc *, struct vm_area_struct *, + struct vm_fault *); + void (*free)(struct kgsl_memdesc *memdesc); +}; + +/* shared memory allocation */ +struct kgsl_memdesc { + struct kgsl_pagetable *pagetable; + void *hostptr; + unsigned int gpuaddr; + unsigned int physaddr; + unsigned int size; + unsigned int priv; + struct kgsl_memdesc_ops *ops; +}; + +extern struct kgsl_memdesc_ops kgsl_vmalloc_ops; +extern struct kgsl_memdesc_ops kgsl_contig_ops; +extern struct kgsl_memdesc_ops kgsl_userptr_ops; + +int kgsl_sharedmem_vmalloc(struct kgsl_memdesc *memdesc, + struct kgsl_pagetable *pagetable, size_t size); + +int kgsl_sharedmem_vmalloc_user(struct kgsl_memdesc *memdesc, + struct kgsl_pagetable *pagetable, + size_t size, int flags); + +int kgsl_sharedmem_alloc_coherent(struct kgsl_memdesc *memdesc, size_t size); + +void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc); + +int kgsl_sharedmem_readl(const struct kgsl_memdesc *memdesc, + uint32_t *dst, + unsigned int offsetbytes); + +int kgsl_sharedmem_writel(const struct kgsl_memdesc *memdesc, + unsigned int offsetbytes, + uint32_t src); + +int kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc, + unsigned int offsetbytes, unsigned int value, + unsigned int sizebytes); + +void kgsl_cache_range_op(struct kgsl_memdesc *memdesc, int op); + +void kgsl_process_init_sysfs(struct kgsl_process_private *private); +void kgsl_process_uninit_sysfs(struct kgsl_process_private *private); + +int kgsl_sharedmem_init_sysfs(void); +void kgsl_sharedmem_uninit_sysfs(void); + +static inline int +kgsl_allocate_user(struct kgsl_memdesc *memdesc, + struct kgsl_pagetable *pagetable, + size_t size, unsigned int flags) +{ + return kgsl_sharedmem_vmalloc_user(memdesc, pagetable, size, flags); +} + +static inline int +kgsl_allocate_contig(struct kgsl_memdesc *memdesc, size_t size) +{ + return kgsl_sharedmem_alloc_coherent(memdesc, size); +} + +#endif /* __KGSL_SHAREDMEM_H */ diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c new file mode 100644 index 0000000000000..1d8ada3f41beb --- /dev/null +++ b/drivers/gpu/msm/z180.c @@ -0,0 +1,1076 @@ +/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include + +#include "kgsl.h" +#include "kgsl_cffdump.h" + +#include "z180.h" +#include "z180_reg.h" + +#define DRIVER_VERSION_MAJOR 3 +#define DRIVER_VERSION_MINOR 1 + +#define Z180_DEVICE(device) \ + KGSL_CONTAINER_OF(device, struct z180_device, dev) + +#define GSL_VGC_INT_MASK \ + (REG_VGC_IRQSTATUS__MH_MASK | \ + REG_VGC_IRQSTATUS__G2D_MASK | \ + REG_VGC_IRQSTATUS__FIFO_MASK) + +#define VGV3_NEXTCMD_JUMP 0x01 + +#define VGV3_NEXTCMD_NEXTCMD_FSHIFT 12 +#define VGV3_NEXTCMD_NEXTCMD_FMASK 0x7 + +#define VGV3_CONTROL_MARKADD_FSHIFT 0 +#define VGV3_CONTROL_MARKADD_FMASK 0xfff + +#define Z180_PACKET_SIZE 15 +#define Z180_MARKER_SIZE 10 +#define Z180_CALL_CMD 0x1000 +#define Z180_MARKER_CMD 0x8000 +#define Z180_STREAM_END_CMD 0x9000 +#define Z180_STREAM_PACKET 0x7C000176 +#define Z180_STREAM_PACKET_CALL 0x7C000275 +#define Z180_PACKET_COUNT 8 +#define Z180_RB_SIZE (Z180_PACKET_SIZE*Z180_PACKET_COUNT \ + *sizeof(uint32_t)) + +#define NUMTEXUNITS 4 +#define TEXUNITREGCOUNT 25 +#define VG_REGCOUNT 0x39 + +#define PACKETSIZE_BEGIN 3 +#define PACKETSIZE_G2DCOLOR 2 +#define PACKETSIZE_TEXUNIT (TEXUNITREGCOUNT * 2) +#define PACKETSIZE_REG (VG_REGCOUNT * 2) +#define PACKETSIZE_STATE (PACKETSIZE_TEXUNIT * NUMTEXUNITS + \ + PACKETSIZE_REG + PACKETSIZE_BEGIN + \ + PACKETSIZE_G2DCOLOR) +#define PACKETSIZE_STATESTREAM (ALIGN((PACKETSIZE_STATE * \ + sizeof(unsigned int)), 32) / \ + sizeof(unsigned int)) + +#define Z180_INVALID_CONTEXT UINT_MAX + +/* z180 MH arbiter config*/ +#define Z180_CFG_MHARB \ + (0x10 \ + | (0 << MH_ARBITER_CONFIG__SAME_PAGE_GRANULARITY__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__L1_ARB_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__L1_ARB_HOLD_ENABLE__SHIFT) \ + | (0 << MH_ARBITER_CONFIG__L2_ARB_CONTROL__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__PAGE_SIZE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__TC_REORDER_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__TC_ARB_HOLD_ENABLE__SHIFT) \ + | (0 << MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT_ENABLE__SHIFT) \ + | (0x8 << MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__CP_CLNT_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__VGT_CLNT_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__TC_CLNT_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT) \ + | (1 << MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT)) + +#define Z180_TIMESTAMP_EPSILON 20000 +#define Z180_IDLE_COUNT_MAX 1000000 + +#define Z180_CMDWINDOW_TARGET_MASK 0x000000FF +#define Z180_CMDWINDOW_ADDR_MASK 0x00FFFF00 +#define Z180_CMDWINDOW_TARGET_SHIFT 0 +#define Z180_CMDWINDOW_ADDR_SHIFT 8 + +static int z180_start(struct kgsl_device *device, unsigned int init_ram); +static int z180_stop(struct kgsl_device *device); +static int z180_wait(struct kgsl_device *device, + unsigned int timestamp, + unsigned int msecs); +static void z180_regread(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int *value); +static void z180_regwrite(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int value); +static int z180_cmdwindow_write(struct kgsl_device *device, + enum kgsl_cmdwindow_type target, + unsigned int addr, + unsigned int data); +static void z180_regread_isr(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int *value); +static void z180_regwrite_isr(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int value); +static void __devinit z180_getfunctable(struct kgsl_functable *ftbl); + +#define Z180_MMU_CONFIG \ + (0x01 \ + | (MMU_CONFIG << MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT) \ + | (MMU_CONFIG << MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT)) + +static struct z180_device device_2d0 = { + .dev = { + .name = DEVICE_2D0_NAME, + .id = KGSL_DEVICE_2D0, + .ver_major = DRIVER_VERSION_MAJOR, + .ver_minor = DRIVER_VERSION_MINOR, + .mmu = { + .config = Z180_MMU_CONFIG, + /* turn off memory protection unit by setting + acceptable physical address range to include + all pages. */ + .mpu_base = 0x00000000, + .mpu_range = 0xFFFFF000, + .reg = { + .config = ADDR_MH_MMU_CONFIG, + .mpu_base = ADDR_MH_MMU_MPU_BASE, + .mpu_end = ADDR_MH_MMU_MPU_END, + .va_range = ADDR_MH_MMU_VA_RANGE, + .pt_page = ADDR_MH_MMU_PT_BASE, + .page_fault = ADDR_MH_MMU_PAGE_FAULT, + .tran_error = ADDR_MH_MMU_TRAN_ERROR, + .invalidate = ADDR_MH_MMU_INVALIDATE, + .interrupt_mask = ADDR_MH_INTERRUPT_MASK, + .interrupt_status = ADDR_MH_INTERRUPT_STATUS, + .interrupt_clear = ADDR_MH_INTERRUPT_CLEAR, + .axi_error = ADDR_MH_AXI_ERROR, + }, + }, + .pwrctrl = { + .pwr_rail = PWR_RAIL_GRP_2D_CLK, + .regulator_name = "fs_gfx2d0", + .irq_name = KGSL_2D0_IRQ, + }, + .mutex = __MUTEX_INITIALIZER(device_2d0.dev.mutex), + .state = KGSL_STATE_INIT, + .active_cnt = 0, + .iomemname = KGSL_2D0_REG_MEMORY, + }, +}; + +static struct z180_device device_2d1 = { + .dev = { + .name = DEVICE_2D1_NAME, + .id = KGSL_DEVICE_2D1, + .ver_major = DRIVER_VERSION_MAJOR, + .ver_minor = DRIVER_VERSION_MINOR, + .mmu = { + .config = Z180_MMU_CONFIG, + /* turn off memory protection unit by setting + acceptable physical address range to include + all pages. */ + .mpu_base = 0x00000000, + .mpu_range = 0xFFFFF000, + .reg = { + .config = ADDR_MH_MMU_CONFIG, + .mpu_base = ADDR_MH_MMU_MPU_BASE, + .mpu_end = ADDR_MH_MMU_MPU_END, + .va_range = ADDR_MH_MMU_VA_RANGE, + .pt_page = ADDR_MH_MMU_PT_BASE, + .page_fault = ADDR_MH_MMU_PAGE_FAULT, + .tran_error = ADDR_MH_MMU_TRAN_ERROR, + .invalidate = ADDR_MH_MMU_INVALIDATE, + .interrupt_mask = ADDR_MH_INTERRUPT_MASK, + .interrupt_status = ADDR_MH_INTERRUPT_STATUS, + .interrupt_clear = ADDR_MH_INTERRUPT_CLEAR, + .axi_error = ADDR_MH_AXI_ERROR, + }, + }, + .pwrctrl = { + .pwr_rail = PWR_RAIL_GRP_2D_CLK, + .regulator_name = "fs_gfx2d1", + .irq_name = KGSL_2D1_IRQ, + }, + .mutex = __MUTEX_INITIALIZER(device_2d1.dev.mutex), + .state = KGSL_STATE_INIT, + .active_cnt = 0, + .iomemname = KGSL_2D1_REG_MEMORY, + }, +}; + +static irqreturn_t z180_isr(int irq, void *data) +{ + irqreturn_t result = IRQ_NONE; + unsigned int status; + struct kgsl_device *device = (struct kgsl_device *) data; + struct z180_device *z180_dev = Z180_DEVICE(device); + + z180_regread_isr(device, ADDR_VGC_IRQSTATUS >> 2, &status); + + if (status & GSL_VGC_INT_MASK) { + z180_regwrite_isr(device, + ADDR_VGC_IRQSTATUS >> 2, status & GSL_VGC_INT_MASK); + + result = IRQ_HANDLED; + + if (status & REG_VGC_IRQSTATUS__FIFO_MASK) + KGSL_DRV_ERR(device, "z180 fifo interrupt\n"); + if (status & REG_VGC_IRQSTATUS__MH_MASK) + kgsl_mh_intrcallback(device); + if (status & REG_VGC_IRQSTATUS__G2D_MASK) { + int count; + + z180_regread_isr(device, + ADDR_VGC_IRQ_ACTIVE_CNT >> 2, + &count); + + count >>= 8; + count &= 255; + z180_dev->timestamp += count; + + wake_up_interruptible(&device->wait_queue); + + atomic_notifier_call_chain( + &(device->ts_notifier_list), + device->id, NULL); + } + } + + if (device->pwrctrl.nap_allowed == true) { + device->requested_state = KGSL_STATE_NAP; + schedule_work(&device->idle_check_ws); + } + mod_timer(&device->idle_timer, + jiffies + device->pwrctrl.interval_timeout); + + return result; +} + +static int z180_cleanup_pt(struct kgsl_device *device, + struct kgsl_pagetable *pagetable) +{ + struct z180_device *z180_dev = Z180_DEVICE(device); + + kgsl_mmu_unmap(pagetable, &device->mmu.dummyspace); + + kgsl_mmu_unmap(pagetable, &device->memstore); + + kgsl_mmu_unmap(pagetable, &z180_dev->ringbuffer.cmdbufdesc); + + return 0; +} + +static int z180_setup_pt(struct kgsl_device *device, + struct kgsl_pagetable *pagetable) +{ + int result = 0; + struct z180_device *z180_dev = Z180_DEVICE(device); + + result = kgsl_mmu_map_global(pagetable, &device->mmu.dummyspace, + GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); + + if (result) + goto error; + + result = kgsl_mmu_map_global(pagetable, &device->memstore, + GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); + if (result) + goto error_unmap_dummy; + + result = kgsl_mmu_map_global(pagetable, + &z180_dev->ringbuffer.cmdbufdesc, + GSL_PT_PAGE_RV); + if (result) + goto error_unmap_memstore; + return result; + +error_unmap_dummy: + kgsl_mmu_unmap(pagetable, &device->mmu.dummyspace); + +error_unmap_memstore: + kgsl_mmu_unmap(pagetable, &device->memstore); + +error: + return result; +} + +static inline unsigned int rb_offset(unsigned int index) +{ + return index*sizeof(unsigned int)*(Z180_PACKET_SIZE); +} + +static void addmarker(struct z180_ringbuffer *rb, unsigned int index) +{ + char *ptr = (char *)(rb->cmdbufdesc.hostptr); + unsigned int *p = (unsigned int *)(ptr + rb_offset(index)); + + *p++ = Z180_STREAM_PACKET; + *p++ = (Z180_MARKER_CMD | 5); + *p++ = ADDR_VGV3_LAST << 24; + *p++ = ADDR_VGV3_LAST << 24; + *p++ = ADDR_VGV3_LAST << 24; + *p++ = Z180_STREAM_PACKET; + *p++ = 5; + *p++ = ADDR_VGV3_LAST << 24; + *p++ = ADDR_VGV3_LAST << 24; + *p++ = ADDR_VGV3_LAST << 24; +} + +static void addcmd(struct z180_ringbuffer *rb, unsigned int index, + unsigned int cmd, unsigned int nextcnt) +{ + char * ptr = (char *)(rb->cmdbufdesc.hostptr); + unsigned int *p = (unsigned int *)(ptr + (rb_offset(index) + + (Z180_MARKER_SIZE * sizeof(unsigned int)))); + + *p++ = Z180_STREAM_PACKET_CALL; + *p++ = cmd; + *p++ = Z180_CALL_CMD | nextcnt; + *p++ = ADDR_VGV3_LAST << 24; + *p++ = ADDR_VGV3_LAST << 24; +} + +static int z180_cmdstream_start(struct kgsl_device *device) +{ + struct z180_device *z180_dev = Z180_DEVICE(device); + int result; + unsigned int cmd = VGV3_NEXTCMD_JUMP << VGV3_NEXTCMD_NEXTCMD_FSHIFT; + + z180_dev->timestamp = 0; + z180_dev->current_timestamp = 0; + + addmarker(&z180_dev->ringbuffer, 0); + + result = z180_cmdwindow_write(device, KGSL_CMDWINDOW_2D, + ADDR_VGV3_MODE, 4); + if (result != 0) + return result; + + result = z180_cmdwindow_write(device, KGSL_CMDWINDOW_2D, + ADDR_VGV3_NEXTADDR, + z180_dev->ringbuffer.cmdbufdesc.gpuaddr); + if (result != 0) + return result; + + result = z180_cmdwindow_write(device, KGSL_CMDWINDOW_2D, + ADDR_VGV3_NEXTCMD, cmd | 5); + if (result != 0) + return result; + + result = z180_cmdwindow_write(device, KGSL_CMDWINDOW_2D, + ADDR_VGV3_WRITEADDR, device->memstore.gpuaddr); + + if (result != 0) + return result; + + cmd = (int)(((1) & VGV3_CONTROL_MARKADD_FMASK) + << VGV3_CONTROL_MARKADD_FSHIFT); + + result = z180_cmdwindow_write(device, KGSL_CMDWINDOW_2D, + ADDR_VGV3_CONTROL, cmd); + + if (result != 0) + return result; + + result = z180_cmdwindow_write(device, KGSL_CMDWINDOW_2D, + ADDR_VGV3_CONTROL, 0); + if (result != 0) + return result; + + return result; +} + +static int room_in_rb(struct z180_device *device) +{ + int ts_diff; + + ts_diff = device->current_timestamp - device->timestamp; + + return ts_diff < Z180_PACKET_COUNT; +} + +static int z180_idle(struct kgsl_device *device, unsigned int timeout) +{ + int status = 0; + struct z180_device *z180_dev = Z180_DEVICE(device); + + if (z180_dev->current_timestamp > z180_dev->timestamp) + status = z180_wait(device, z180_dev->current_timestamp, + timeout); + + if (status) + KGSL_DRV_ERR(device, "z180_waittimestamp() timed out\n"); + + return status; +} + +static int z180_setstate(struct kgsl_device *device, uint32_t flags) +{ +#ifdef CONFIG_MSM_KGSL_MMU + unsigned int mh_mmu_invalidate = 0x00000003; /*invalidate all and tc */ + + if (flags & KGSL_MMUFLAGS_PTUPDATE) { + z180_idle(device, KGSL_TIMEOUT_DEFAULT); + z180_regwrite(device, ADDR_MH_MMU_PT_BASE, + device->mmu.hwpagetable->base.gpuaddr); + z180_regwrite(device, ADDR_MH_MMU_VA_RANGE, + (device->mmu.hwpagetable-> + va_base | (device->mmu.hwpagetable-> + va_range >> 16))); + z180_regwrite(device, ADDR_MH_MMU_INVALIDATE, + mh_mmu_invalidate); + } + + if (flags & KGSL_MMUFLAGS_TLBFLUSH) + z180_regwrite(device, ADDR_MH_MMU_INVALIDATE, + mh_mmu_invalidate); +#endif + return 0; +} + +int +z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv, + struct kgsl_context *context, + struct kgsl_ibdesc *ibdesc, + unsigned int numibs, + uint32_t *timestamp, + unsigned int ctrl) +{ + unsigned int result = 0; + unsigned int ofs = PACKETSIZE_STATESTREAM * sizeof(unsigned int); + unsigned int cnt = 5; + unsigned int nextaddr = 0; + unsigned int index = 0; + unsigned int nextindex; + unsigned int nextcnt = Z180_STREAM_END_CMD | 5; + struct kgsl_memdesc tmp = {0}; + unsigned int cmd; + struct kgsl_device *device = dev_priv->device; + struct kgsl_pagetable *pagetable = dev_priv->process_priv->pagetable; + struct z180_device *z180_dev = Z180_DEVICE(device); + unsigned int sizedwords; + + if (device->state & KGSL_STATE_HUNG) { + return -EINVAL; + goto error; + } + if (numibs != 1) { + KGSL_DRV_ERR(device, "Invalid number of ibs: %d\n", numibs); + result = -EINVAL; + goto error; + } + cmd = ibdesc[0].gpuaddr; + sizedwords = ibdesc[0].sizedwords; + + tmp.hostptr = (void *)*timestamp; + + KGSL_CMD_INFO(device, "ctxt %d ibaddr 0x%08x sizedwords %d\n", + context->id, cmd, sizedwords); + /* context switch */ + if ((context->id != (int)z180_dev->ringbuffer.prevctx) || + (ctrl & KGSL_CONTEXT_CTX_SWITCH)) { + KGSL_CMD_INFO(device, "context switch %d -> %d\n", + context->id, z180_dev->ringbuffer.prevctx); + kgsl_mmu_setstate(device, pagetable); + cnt = PACKETSIZE_STATESTREAM; + ofs = 0; + } + z180_setstate(device, kgsl_pt_get_flags(device->mmu.hwpagetable, + device->id)); + + result = wait_event_interruptible_timeout(device->wait_queue, + room_in_rb(z180_dev), + msecs_to_jiffies(KGSL_TIMEOUT_DEFAULT)); + if (result < 0) { + KGSL_CMD_ERR(device, "wait_event_interruptible_timeout " + "failed: %d\n", result); + goto error; + } + result = 0; + + index = z180_dev->current_timestamp % Z180_PACKET_COUNT; + z180_dev->current_timestamp++; + nextindex = z180_dev->current_timestamp % Z180_PACKET_COUNT; + *timestamp = z180_dev->current_timestamp; + + z180_dev->ringbuffer.prevctx = context->id; + + addcmd(&z180_dev->ringbuffer, index, cmd + ofs, cnt); + + /* Make sure the next ringbuffer entry has a marker */ + addmarker(&z180_dev->ringbuffer, nextindex); + + nextaddr = z180_dev->ringbuffer.cmdbufdesc.gpuaddr + + rb_offset(nextindex); + + tmp.hostptr = (void *)(tmp.hostptr + + (sizedwords * sizeof(unsigned int))); + tmp.size = 12; + + kgsl_sharedmem_writel(&tmp, 4, nextaddr); + kgsl_sharedmem_writel(&tmp, 8, nextcnt); + + /* sync memory before activating the hardware for the new command*/ + mb(); + + cmd = (int)(((2) & VGV3_CONTROL_MARKADD_FMASK) + << VGV3_CONTROL_MARKADD_FSHIFT); + + z180_cmdwindow_write(device, + KGSL_CMDWINDOW_2D, ADDR_VGV3_CONTROL, cmd); + z180_cmdwindow_write(device, + KGSL_CMDWINDOW_2D, ADDR_VGV3_CONTROL, 0); +error: + return result; +} + +static int z180_ringbuffer_init(struct kgsl_device *device) +{ + struct z180_device *z180_dev = Z180_DEVICE(device); + memset(&z180_dev->ringbuffer, 0, sizeof(struct z180_ringbuffer)); + z180_dev->ringbuffer.prevctx = Z180_INVALID_CONTEXT; + return kgsl_sharedmem_alloc_coherent( + &z180_dev->ringbuffer.cmdbufdesc, + Z180_RB_SIZE); +} + +static void z180_ringbuffer_close(struct kgsl_device *device) +{ + struct z180_device *z180_dev = Z180_DEVICE(device); + kgsl_sharedmem_free(&z180_dev->ringbuffer.cmdbufdesc); + memset(&z180_dev->ringbuffer, 0, sizeof(struct z180_ringbuffer)); +} + +static int __devinit z180_probe(struct platform_device *pdev) +{ + int status = -EINVAL; + struct kgsl_device *device = NULL; + struct z180_device *z180_dev; + + device = (struct kgsl_device *)pdev->id_entry->driver_data; + device->pdev = pdev; + + z180_getfunctable(&device->ftbl); + + z180_dev = Z180_DEVICE(device); + spin_lock_init(&z180_dev->cmdwin_lock); + + status = z180_ringbuffer_init(device); + if (status != 0) + goto error; + + status = kgsl_device_platform_probe(device, z180_isr); + if (status) + goto error_close_ringbuffer; + + return status; + +error_close_ringbuffer: + z180_ringbuffer_close(device); +error: + device->pdev = NULL; + return status; +} + +static int __devexit z180_remove(struct platform_device *pdev) +{ + struct kgsl_device *device = NULL; + + device = (struct kgsl_device *)pdev->id_entry->driver_data; + + kgsl_device_platform_remove(device); + + z180_ringbuffer_close(device); + + return 0; +} + +static int z180_start(struct kgsl_device *device, unsigned int init_ram) +{ + int status = 0; + + device->state = KGSL_STATE_INIT; + device->requested_state = KGSL_STATE_NONE; + KGSL_PWR_WARN(device, "state -> INIT, device %d\n", device->id); + + /* Order pwrrail/clk sequence based upon platform. */ + if (device->pwrctrl.pwrrail_first) + kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_ON); + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_ON); + kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_ON); + if (!device->pwrctrl.pwrrail_first) + kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_ON); + + /* Set up MH arbiter. MH offsets are considered to be dword + * based, therefore no down shift. */ + z180_regwrite(device, ADDR_MH_ARBITER_CONFIG, Z180_CFG_MHARB); + + z180_regwrite(device, ADDR_MH_CLNT_INTF_CTRL_CONFIG1, 0x00030F27); + z180_regwrite(device, ADDR_MH_CLNT_INTF_CTRL_CONFIG2, 0x004B274F); + + z180_regwrite(device, (ADDR_VGC_IRQENABLE >> 2), 0x3); + + status = kgsl_mmu_start(device); + if (status) + goto error_clk_off; + + status = z180_cmdstream_start(device); + if (status) + goto error_mmu_stop; + + mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT); + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_ON); + return 0; +error_clk_off: + z180_regwrite(device, (ADDR_VGC_IRQENABLE >> 2), 0); + kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_OFF); + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_OFF); +error_mmu_stop: + kgsl_mmu_stop(device); + return status; +} + +static int z180_stop(struct kgsl_device *device) +{ + z180_idle(device, KGSL_TIMEOUT_DEFAULT); + + del_timer(&device->idle_timer); + + kgsl_mmu_stop(device); + + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); + if (!device->pwrctrl.pwrrail_first) + kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_OFF); + kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_OFF); + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_OFF); + if (device->pwrctrl.pwrrail_first) + kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_OFF); + + return 0; +} + +static int z180_getproperty(struct kgsl_device *device, + enum kgsl_property_type type, + void *value, + unsigned int sizebytes) +{ + int status = -EINVAL; + + switch (type) { + case KGSL_PROP_DEVICE_INFO: + { + struct kgsl_devinfo devinfo; + + if (sizebytes != sizeof(devinfo)) { + status = -EINVAL; + break; + } + + memset(&devinfo, 0, sizeof(devinfo)); + devinfo.device_id = device->id+1; + devinfo.chip_id = device->chip_id; + devinfo.mmu_enabled = kgsl_mmu_enabled(); + + if (copy_to_user(value, &devinfo, sizeof(devinfo)) != + 0) { + status = -EFAULT; + break; + } + status = 0; + } + break; + case KGSL_PROP_MMU_ENABLE: + { +#ifdef CONFIG_MSM_KGSL_MMU + int mmuProp = 1; +#else + int mmuProp = 0; +#endif + if (sizebytes != sizeof(int)) { + status = -EINVAL; + break; + } + if (copy_to_user(value, &mmuProp, sizeof(mmuProp))) { + status = -EFAULT; + break; + } + status = 0; + } + break; + + default: + KGSL_DRV_ERR(device, "invalid property: %d\n", type); + status = -EINVAL; + } + return status; +} + +static unsigned int z180_isidle(struct kgsl_device *device) +{ + int status = false; + struct z180_device *z180_dev = Z180_DEVICE(device); + + int timestamp = z180_dev->timestamp; + + if (timestamp == z180_dev->current_timestamp) + status = true; + + return status; +} + +static int z180_resume_context(struct kgsl_device *device) +{ + /* Context is in the pre-amble, automatically restored. */ + + return 0; +} + +static int z180_suspend_context(struct kgsl_device *device) +{ + struct z180_device *z180_dev = Z180_DEVICE(device); + + z180_dev->ringbuffer.prevctx = Z180_INVALID_CONTEXT; + + return 0; +} + +/* Not all Z180 registers are directly accessible. + * The _z180_(read|write)_simple functions below handle the ones that are. + */ +static void _z180_regread_simple(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int *value) +{ + unsigned int *reg; + + BUG_ON(offsetwords * sizeof(uint32_t) >= device->regspace.sizebytes); + + reg = (unsigned int *)(device->regspace.mmio_virt_base + + (offsetwords << 2)); + + /*ensure this read finishes before the next one. + * i.e. act like normal readl() */ + *value = __raw_readl(reg); + rmb(); + +} + +static void _z180_regwrite_simple(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int value) +{ + unsigned int *reg; + + BUG_ON(offsetwords*sizeof(uint32_t) >= device->regspace.sizebytes); + + reg = (unsigned int *)(device->regspace.mmio_virt_base + + (offsetwords << 2)); + kgsl_cffdump_regwrite(device->id, offsetwords << 2, value); + /*ensure previous writes post before this one, + * i.e. act like normal writel() */ + wmb(); + __raw_writel(value, reg); +} + + +/* The MH registers must be accessed through via a 2 step write, (read|write) + * process. These registers may be accessed from interrupt context during + * the handling of MH or MMU error interrupts. Therefore a spin lock is used + * to ensure that the 2 step sequence is not interrupted. + */ +static void _z180_regread_mmu(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int *value) +{ + struct z180_device *z180_dev = Z180_DEVICE(device); + unsigned long flags; + + spin_lock_irqsave(&z180_dev->cmdwin_lock, flags); + _z180_regwrite_simple(device, (ADDR_VGC_MH_READ_ADDR >> 2), + offsetwords); + _z180_regread_simple(device, (ADDR_VGC_MH_DATA_ADDR >> 2), value); + spin_unlock_irqrestore(&z180_dev->cmdwin_lock, flags); +} + + +static void _z180_regwrite_mmu(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int value) +{ + struct z180_device *z180_dev = Z180_DEVICE(device); + unsigned int cmdwinaddr; + unsigned long flags; + + cmdwinaddr = ((KGSL_CMDWINDOW_MMU << Z180_CMDWINDOW_TARGET_SHIFT) & + Z180_CMDWINDOW_TARGET_MASK); + cmdwinaddr |= ((offsetwords << Z180_CMDWINDOW_ADDR_SHIFT) & + Z180_CMDWINDOW_ADDR_MASK); + + spin_lock_irqsave(&z180_dev->cmdwin_lock, flags); + _z180_regwrite_simple(device, ADDR_VGC_MMUCOMMANDSTREAM >> 2, + cmdwinaddr); + _z180_regwrite_simple(device, ADDR_VGC_MMUCOMMANDSTREAM >> 2, value); + spin_unlock_irqrestore(&z180_dev->cmdwin_lock, flags); +} + +/* the rest of the code doesn't want to think about if it is writing mmu + * registers or normal registers so handle it here + */ +static void _z180_regread(struct kgsl_device *device, unsigned int offsetwords, + unsigned int *value) +{ + if ((offsetwords >= ADDR_MH_ARBITER_CONFIG && + offsetwords <= ADDR_MH_AXI_HALT_CONTROL) || + (offsetwords >= ADDR_MH_MMU_CONFIG && + offsetwords <= ADDR_MH_MMU_MPU_END)) { + _z180_regread_mmu(device, offsetwords, value); + } else { + _z180_regread_simple(device, offsetwords, value); + } +} + +static void _z180_regwrite(struct kgsl_device *device, unsigned int offsetwords, + unsigned int value) +{ + if ((offsetwords >= ADDR_MH_ARBITER_CONFIG && + offsetwords <= ADDR_MH_CLNT_INTF_CTRL_CONFIG2) || + (offsetwords >= ADDR_MH_MMU_CONFIG && + offsetwords <= ADDR_MH_MMU_MPU_END)) { + _z180_regwrite_mmu(device, offsetwords, value); + + } else { + _z180_regwrite_simple(device, offsetwords, value); + } +} + + +static void z180_regread(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int *value) +{ + kgsl_pre_hwaccess(device); + _z180_regread(device, offsetwords, value); +} + +static void z180_regread_isr(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int *value) +{ + _z180_regread(device, offsetwords, value); +} + +static void z180_regwrite(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int value) +{ + kgsl_pre_hwaccess(device); + _z180_regwrite(device, offsetwords, value); +} + +static void z180_regwrite_isr(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int value) +{ + _z180_regwrite(device, offsetwords, value); +} + +static int z180_cmdwindow_write(struct kgsl_device *device, + enum kgsl_cmdwindow_type target, unsigned int addr, + unsigned int data) +{ + unsigned int cmdwinaddr; + unsigned int cmdstream; + + if (target < KGSL_CMDWINDOW_MIN || + target > KGSL_CMDWINDOW_MAX) { + KGSL_DRV_ERR(device, "invalid target\n"); + return -EINVAL; + } + + if (target == KGSL_CMDWINDOW_MMU) + cmdstream = ADDR_VGC_MMUCOMMANDSTREAM; + else + cmdstream = ADDR_VGC_COMMANDSTREAM; + + cmdwinaddr = ((target << Z180_CMDWINDOW_TARGET_SHIFT) & + Z180_CMDWINDOW_TARGET_MASK); + cmdwinaddr |= ((addr << Z180_CMDWINDOW_ADDR_SHIFT) & + Z180_CMDWINDOW_ADDR_MASK); + + z180_regwrite(device, cmdstream >> 2, cmdwinaddr); + z180_regwrite(device, cmdstream >> 2, data); + + return 0; +} + +static unsigned int z180_readtimestamp(struct kgsl_device *device, + enum kgsl_timestamp_type type) +{ + struct z180_device *z180_dev = Z180_DEVICE(device); + /* get current EOP timestamp */ + return z180_dev->timestamp; +} + +static int z180_waittimestamp(struct kgsl_device *device, + unsigned int timestamp, + unsigned int msecs) +{ + int status = -EINVAL; + mutex_unlock(&device->mutex); + status = z180_wait(device, timestamp, msecs); + mutex_lock(&device->mutex); + + return status; +} + +static int z180_wait(struct kgsl_device *device, + unsigned int timestamp, + unsigned int msecs) +{ + int status = -EINVAL; + long timeout = 0; + + timeout = wait_io_event_interruptible_timeout( + device->wait_queue, + kgsl_check_timestamp(device, timestamp), + msecs_to_jiffies(msecs)); + + if (timeout > 0) + status = 0; + else if (timeout == 0) { + status = -ETIMEDOUT; + device->state = KGSL_STATE_HUNG; + KGSL_PWR_WARN(device, "state -> HUNG, device %d\n", device->id); + } else + status = timeout; + + return status; +} + +static long +z180_ioctl_cmdwindow_write(struct kgsl_device_private *dev_priv, + void *data) +{ + struct kgsl_cmdwindow_write *param = data; + + return z180_cmdwindow_write(dev_priv->device, + param->target, + param->addr, + param->data); +} + +static int +z180_drawctxt_destroy(struct kgsl_device *device, + struct kgsl_context *context) +{ + struct z180_device *z180_dev = Z180_DEVICE(device); + + z180_idle(device, KGSL_TIMEOUT_DEFAULT); + + if (z180_dev->ringbuffer.prevctx == context->id) { + z180_dev->ringbuffer.prevctx = Z180_INVALID_CONTEXT; + device->mmu.hwpagetable = device->mmu.defaultpagetable; + kgsl_setstate(device, KGSL_MMUFLAGS_PTUPDATE); + } + + return 0; +} + +static long z180_ioctl(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + int result = 0; + + switch (cmd) { + case IOCTL_KGSL_CMDWINDOW_WRITE: + result = z180_ioctl_cmdwindow_write(dev_priv, data); + break; + default: + KGSL_DRV_INFO(dev_priv->device, + "invalid ioctl code %08x\n", cmd); + result = -EINVAL; + break; + } + return result; + +} + +static unsigned int z180_idle_calc(struct kgsl_device *device) +{ + return device->pwrctrl.time; +} + +static void __devinit z180_getfunctable(struct kgsl_functable *ftbl) +{ + if (ftbl == NULL) + return; + ftbl->device_regread = z180_regread; + ftbl->device_regwrite = z180_regwrite; + ftbl->device_regread_isr = z180_regread_isr; + ftbl->device_regwrite_isr = z180_regwrite_isr; + ftbl->device_setstate = z180_setstate; + ftbl->device_idle = z180_idle; + ftbl->device_isidle = z180_isidle; + ftbl->device_suspend_context = z180_suspend_context; + ftbl->device_resume_context = z180_resume_context; + ftbl->device_start = z180_start; + ftbl->device_stop = z180_stop; + ftbl->device_getproperty = z180_getproperty; + ftbl->device_waittimestamp = z180_waittimestamp; + ftbl->device_readtimestamp = z180_readtimestamp; + ftbl->device_issueibcmds = z180_cmdstream_issueibcmds; + ftbl->device_drawctxt_create = NULL; + ftbl->device_drawctxt_destroy = z180_drawctxt_destroy; + ftbl->device_ioctl = z180_ioctl; + ftbl->device_setup_pt = z180_setup_pt; + ftbl->device_cleanup_pt = z180_cleanup_pt; + ftbl->device_idle_calc = z180_idle_calc; +} + +static struct platform_device_id z180_id_table[] = { + { DEVICE_2D0_NAME, (kernel_ulong_t)&device_2d0.dev, }, + { DEVICE_2D1_NAME, (kernel_ulong_t)&device_2d1.dev, }, + { }, +}; +MODULE_DEVICE_TABLE(platform, z180_id_table); + +static struct platform_driver z180_platform_driver = { + .probe = z180_probe, + .remove = __devexit_p(z180_remove), + .suspend = kgsl_suspend_driver, + .resume = kgsl_resume_driver, + .id_table = z180_id_table, + .driver = { + .owner = THIS_MODULE, + .name = DEVICE_2D_NAME, + .pm = &kgsl_pm_ops, + } +}; + +static int __init kgsl_2d_init(void) +{ + return platform_driver_register(&z180_platform_driver); +} + +static void __exit kgsl_2d_exit(void) +{ + platform_driver_unregister(&z180_platform_driver); +} + +module_init(kgsl_2d_init); +module_exit(kgsl_2d_exit); + +MODULE_DESCRIPTION("2D Graphics driver"); +MODULE_VERSION("1.2"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:kgsl_2d"); diff --git a/drivers/gpu/msm/z180.h b/drivers/gpu/msm/z180.h new file mode 100644 index 0000000000000..c62398a523f68 --- /dev/null +++ b/drivers/gpu/msm/z180.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __Z180_H +#define __Z180_H + +#define DEVICE_2D_NAME "kgsl-2d" +#define DEVICE_2D0_NAME "kgsl-2d0" +#define DEVICE_2D1_NAME "kgsl-2d1" + +struct z180_ringbuffer { + unsigned int prevctx; + struct kgsl_memdesc cmdbufdesc; +}; + +struct z180_device { + struct kgsl_device dev; /* Must be first field in this struct */ + int current_timestamp; + int timestamp; + struct z180_ringbuffer ringbuffer; + spinlock_t cmdwin_lock; +}; + +#endif /* __Z180_H */ diff --git a/drivers/gpu/msm/z180_reg.h b/drivers/gpu/msm/z180_reg.h new file mode 100644 index 0000000000000..f5625535f10c4 --- /dev/null +++ b/drivers/gpu/msm/z180_reg.h @@ -0,0 +1,93 @@ +/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __Z80_REG_H +#define __Z80_REG_H + +#define REG_VGC_IRQSTATUS__MH_MASK 0x00000001L +#define REG_VGC_IRQSTATUS__G2D_MASK 0x00000002L +#define REG_VGC_IRQSTATUS__FIFO_MASK 0x00000004L + +#define MH_ARBITER_CONFIG__SAME_PAGE_GRANULARITY__SHIFT 0x00000006 +#define MH_ARBITER_CONFIG__L1_ARB_ENABLE__SHIFT 0x00000007 +#define MH_ARBITER_CONFIG__L1_ARB_HOLD_ENABLE__SHIFT 0x00000008 +#define MH_ARBITER_CONFIG__L2_ARB_CONTROL__SHIFT 0x00000009 +#define MH_ARBITER_CONFIG__PAGE_SIZE__SHIFT 0x0000000a +#define MH_ARBITER_CONFIG__TC_REORDER_ENABLE__SHIFT 0x0000000d +#define MH_ARBITER_CONFIG__TC_ARB_HOLD_ENABLE__SHIFT 0x0000000e +#define MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT_ENABLE__SHIFT 0x0000000f +#define MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT__SHIFT 0x00000010 +#define MH_ARBITER_CONFIG__CP_CLNT_ENABLE__SHIFT 0x00000016 +#define MH_ARBITER_CONFIG__VGT_CLNT_ENABLE__SHIFT 0x00000017 +#define MH_ARBITER_CONFIG__TC_CLNT_ENABLE__SHIFT 0x00000018 +#define MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT 0x00000019 +#define MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT 0x0000001a + +#define MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT 0x00000004 +#define MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT 0x00000006 +#define MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT 0x00000008 +#define MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT 0x0000000a +#define MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT 0x0000000c +#define MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT 0x0000000e +#define MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT 0x00000010 +#define MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT 0x00000012 +#define MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT 0x00000014 +#define MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT 0x00000016 +#define MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT 0x00000018 + +#define ADDR_MH_ARBITER_CONFIG 0x0A40 +#define ADDR_MH_INTERRUPT_CLEAR 0x0A44 +#define ADDR_MH_INTERRUPT_MASK 0x0A42 +#define ADDR_MH_INTERRUPT_STATUS 0x0A43 +#define ADDR_MH_AXI_ERROR 0x0A45 +#define ADDR_MH_AXI_HALT_CONTROL 0x0A50 +#define ADDR_MH_CLNT_INTF_CTRL_CONFIG1 0x0A54 +#define ADDR_MH_CLNT_INTF_CTRL_CONFIG2 0x0A55 +#define ADDR_MH_MMU_CONFIG 0x0040 +#define ADDR_MH_MMU_INVALIDATE 0x0045 +#define ADDR_MH_MMU_MPU_BASE 0x0046 +#define ADDR_MH_MMU_MPU_END 0x0047 +#define ADDR_MH_MMU_PT_BASE 0x0042 +#define ADDR_MH_MMU_TRAN_ERROR 0x0044 +#define ADDR_MH_MMU_VA_RANGE 0x0041 +#define ADDR_VGC_MH_READ_ADDR 0x0510 +#define ADDR_VGC_MH_DATA_ADDR 0x0518 +#define ADDR_MH_MMU_PAGE_FAULT 0x0043 +#define ADDR_VGC_COMMANDSTREAM 0x0000 +#define ADDR_VGC_IRQENABLE 0x0438 +#define ADDR_VGC_IRQSTATUS 0x0418 +#define ADDR_VGC_IRQ_ACTIVE_CNT 0x04E0 +#define ADDR_VGC_MMUCOMMANDSTREAM 0x03FC +#define ADDR_VGV3_CONTROL 0x0070 +#define ADDR_VGV3_LAST 0x007F +#define ADDR_VGV3_MODE 0x0071 +#define ADDR_VGV3_NEXTADDR 0x0075 +#define ADDR_VGV3_NEXTCMD 0x0076 +#define ADDR_VGV3_WRITEADDR 0x0072 + +#endif /* __Z180_REG_H */ From f4b7a18bfc4dba07087fcd9b336cc549aac530bc Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 24 Jan 2012 07:39:27 -0600 Subject: [PATCH 2014/2556] board: mahimahi: add kgsl-3d0 device --- arch/arm/mach-msm/board-mahimahi.c | 82 ++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index ed4cfe46ac212..105ef77ec6cc3 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -60,6 +60,8 @@ #include "board-mahimahi-tpa2018d1.h" #include "board-mahimahi-smb329.h" +#include + static uint debug_uart; module_param_named(debug_uart, debug_uart, uint, 0); @@ -272,26 +274,7 @@ static struct platform_device mahimahi_rfkill = { .id = -1, }; -static struct resource msm_kgsl_resources[] = { - { - .name = "kgsl_reg_memory", - .start = MSM_GPU_REG_PHYS, - .end = MSM_GPU_REG_PHYS + MSM_GPU_REG_SIZE - 1, - .flags = IORESOURCE_MEM, - }, - { - .name = "kgsl_phys_memory", - .start = MSM_GPU_MEM_BASE, - .end = MSM_GPU_MEM_BASE + MSM_GPU_MEM_SIZE - 1, - .flags = IORESOURCE_MEM, - }, - { - .start = INT_GRAPHICS, - .end = INT_GRAPHICS, - .flags = IORESOURCE_IRQ, - }, -}; - +#if 0 #define PWR_RAIL_GRP_CLK 8 static int mahimahi_kgsl_power_rail_mode(int follow_clk) { @@ -309,13 +292,57 @@ static int mahimahi_kgsl_power(bool on) cmd = on ? PCOM_CLKCTL_RPC_RAIL_ENABLE : PCOM_CLKCTL_RPC_RAIL_DISABLE; return msm_proc_comm(cmd, &rail_id, NULL); } +#endif -static struct platform_device msm_kgsl_device = { - .name = "kgsl", - .id = -1, - .resource = msm_kgsl_resources, - .num_resources = ARRAY_SIZE(msm_kgsl_resources), +/* start kgsl */ +static struct resource kgsl_3d0_resources[] = { + { + .name = KGSL_3D0_REG_MEMORY, + .start = 0xA0000000, + .end = 0xA001ffff, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_3D0_IRQ, + .start = INT_GRAPHICS, + .end = INT_GRAPHICS, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct kgsl_device_platform_data kgsl_3d0_pdata = { + .pwr_data = { + .pwrlevel = { + { + .gpu_freq = 0, + .bus_freq = 128000000, + }, + }, + .init_level = 0, + .num_levels = 1, + .set_grp_async = NULL, + .idle_timeout = HZ/5, + }, + .clk = { + .name = { + .clk = "grp_clk", + }, + }, + .imem_clk_name = { + .clk = "imem_clk", + }, +}; + +struct platform_device msm_kgsl_3d0 = { + .name = "kgsl-3d0", + .id = 0, + .num_resources = ARRAY_SIZE(kgsl_3d0_resources), + .resource = kgsl_3d0_resources, + .dev = { + .platform_data = &kgsl_3d0_pdata, + }, }; +/* end kgsl */ static struct android_pmem_platform_data mdp_pmem_pdata = { .name = "pmem", @@ -851,14 +878,13 @@ static struct platform_device *devices[] __initdata = { #ifdef CONFIG_720P_CAMERA &android_pmem_venc_device, #endif - &msm_kgsl_device, + &msm_kgsl_3d0, &msm_device_i2c, &capella_cm3602, &msm_camera_sensor_s5k3e2fx, &mahimahi_flashlight_device, }; - static uint32_t bt_gpio_table[] = { PCOM_GPIO_CFG(MAHIMAHI_GPIO_BT_UART1_RTS, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), @@ -1099,10 +1125,12 @@ static void __init mahimahi_init(void) gpio_request(MAHIMAHI_GPIO_DS2482_SLP_N, "ds2482_slp_n"); +#if 0 /* set the gpu power rail to manual mode so clk en/dis will not * turn off gpu power, and hang it on resume */ mahimahi_kgsl_power_rail_mode(0); mahimahi_kgsl_power(true); +#endif msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; From 6b4fdac02415337e5af488772521dcf151db5376 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 1 Feb 2012 17:02:21 -0600 Subject: [PATCH 2015/2556] msm: kgsl: fixes for kgsl backport --- arch/arm/mach-msm/include/mach/msm_bus.h | 134 ++++++++++++++ drivers/video/Kconfig | 2 + include/linux/msm_kgsl.h | 213 ++++++++++++++++++++--- 3 files changed, 320 insertions(+), 29 deletions(-) create mode 100644 arch/arm/mach-msm/include/mach/msm_bus.h diff --git a/arch/arm/mach-msm/include/mach/msm_bus.h b/arch/arm/mach-msm/include/mach/msm_bus.h new file mode 100644 index 0000000000000..095af3a6853cb --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_bus.h @@ -0,0 +1,134 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU General Public License, version 2, in which case the provisions + * of the GPL version 2 are required INSTEAD OF the BSD license. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#ifndef _ARCH_ARM_MACH_MSM_BUS_H +#define _ARCH_ARM_MACH_MSM_BUS_H + +#include +#include + +/* + * Macros for clients to convert their data to ib and ab + * Ws : Time window over which to transfer the data in SECONDS + * Bs : Size of the data block in bytes + * Per : Recurrence period + * Tb : Throughput bandwidth to prevent stalling + * R : Ratio of actual bandwidth used to Tb + * Ib : Instantaneous bandwidth + * Ab : Arbitrated bandwidth + * + * IB_RECURRBLOCK and AB_RECURRBLOCK: + * These are used if the requirement is to transfer a + * recurring block of data over a known time window. + * + * IB_THROUGHPUTBW and AB_THROUGHPUTBW: + * These are used for CPU style masters. Here the requirement + * is to have minimum throughput bandwidth available to avoid + * stalling. + */ +#define IB_RECURRBLOCK(Ws, Bs) ((Ws) == 0 ? 0 : ((Bs)/(Ws))) +#define AB_RECURRBLOCK(Ws, Per) ((Ws) == 0 ? 0 : ((Bs)/(Per))) +#define IB_THROUGHPUTBW(Tb) (Tb) +#define AB_THROUGHPUTBW(Tb, R) ((Tb) * (R)) + +struct msm_bus_vectors { + int src; /* Master */ + int dst; /* Slave */ + unsigned int ab; /* Arbitrated bandwidth */ + unsigned int ib; /* Instantaneous bandwidth */ +}; + +struct msm_bus_paths { + int num_paths; + struct msm_bus_vectors *vectors; +}; + +struct msm_bus_scale_pdata { + struct msm_bus_paths *usecase; + int num_usecases; + const char *name; + /* + * If the active_only flag is set to 1, the BW request is applied + * only when at least one CPU is active (powered on). If the flag + * is set to 0, then the BW request is always applied irrespective + * of the CPU state. + */ + unsigned int active_only; +}; + +/* Scaling APIs */ + +/* + * This function returns a handle to the client. This should be used to + * call msm_bus_scale_client_update_request. + * The function returns 0 if bus driver is unable to register a client + */ + +#ifdef CONFIG_MSM_BUS_SCALING +uint32_t msm_bus_scale_register_client(struct msm_bus_scale_pdata *pdata); +int msm_bus_scale_client_update_request(uint32_t cl, unsigned int index); +void msm_bus_scale_unregister_client(uint32_t cl); +/* AXI Port configuration APIs */ +int msm_bus_axi_porthalt(int master_port); +int msm_bus_axi_portunhalt(int master_port); + +#else +static inline uint32_t +msm_bus_scale_register_client(struct msm_bus_scale_pdata *pdata) +{ + return 1; +} + +static inline int +msm_bus_scale_client_update_request(uint32_t cl, unsigned int index) +{ + return 0; +} + +static inline void +msm_bus_scale_unregister_client(uint32_t cl) +{ +} + +static inline int msm_bus_axi_porthalt(int master_port) +{ + return 0; +} + +static inline int msm_bus_axi_portunhalt(int master_port) +{ + return 0; +} +#endif + +#endif /*_ARCH_ARM_MACH_MSM_BUS_H*/ diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index d110ba496e5ed..62334c77ca7ef 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -26,6 +26,8 @@ source "drivers/gpu/drm/Kconfig" source "drivers/gpu/stub/Kconfig" +source "drivers/gpu/msm/Kconfig" + config VGASTATE tristate default n diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index 28a1e1e8944f0..118d1f98097ae 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -1,27 +1,50 @@ -/* - * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 - * Copyright (c) 2008-2009 QUALCOMM USA, INC. - * - * All source code in this file is licensed under the following license - * - * 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, you can find it at http://www.fsf.org +/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU General Public License, version 2, in which case the provisions + * of the GPL version 2 are required INSTEAD OF the BSD license. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * */ #ifndef _MSM_KGSL_H #define _MSM_KGSL_H +#define KGSL_VERSION_MAJOR 3 +#define KGSL_VERSION_MINOR 3 + /*context flags */ -#define KGSL_CONTEXT_SAVE_GMEM 1 +#define KGSL_CONTEXT_SAVE_GMEM 1 #define KGSL_CONTEXT_NO_GMEM_ALLOC 2 +#define KGSL_CONTEXT_SUBMIT_IB_LIST 4 +#define KGSL_CONTEXT_CTX_SWITCH 8 + +/* Memory allocayion flags */ +#define KGSL_MEMFLAGS_GPUREADONLY 0x01000000 /* generic flag values */ #define KGSL_FLAGS_NORMALMODE 0x00000000 @@ -33,13 +56,22 @@ #define KGSL_FLAGS_RESERVED0 0x00000020 #define KGSL_FLAGS_RESERVED1 0x00000040 #define KGSL_FLAGS_RESERVED2 0x00000080 +#define KGSL_FLAGS_SOFT_RESET 0x00000100 + +#define KGSL_MAX_PWRLEVELS 5 /* device id */ enum kgsl_deviceid { - KGSL_DEVICE_ANY = 0x00000000, - KGSL_DEVICE_YAMATO = 0x00000001, - KGSL_DEVICE_G12 = 0x00000002, - KGSL_DEVICE_MAX = 0x00000002 + KGSL_DEVICE_3D0 = 0x00000000, + KGSL_DEVICE_2D0 = 0x00000001, + KGSL_DEVICE_2D1 = 0x00000002, + KGSL_DEVICE_MAX = 0x00000003 +}; + +enum kgsl_user_mem_type { + KGSL_USER_MEM_TYPE_PMEM = 0x00000000, + KGSL_USER_MEM_TYPE_ASHMEM = 0x00000001, + KGSL_USER_MEM_TYPE_ADDR = 0x00000002 }; struct kgsl_devinfo { @@ -70,6 +102,8 @@ struct kgsl_devmemstore { unsigned int sbz3; volatile unsigned int ref_wait_ts; unsigned int sbz4; + unsigned int current_context; + unsigned int sbz5; }; #define KGSL_DEVICE_MEMSTORE_OFFSET(field) \ @@ -92,6 +126,7 @@ enum kgsl_property_type { KGSL_PROP_SHMEM_APERTURES = 0x00000005, KGSL_PROP_MMU_ENABLE = 0x00000006, KGSL_PROP_INTERRUPT_WAITS = 0x00000007, + KGSL_PROP_VERSION = 0x00000008, }; struct kgsl_shadowprop { @@ -100,6 +135,66 @@ struct kgsl_shadowprop { unsigned int flags; /* contains KGSL_FLAGS_ values */ }; +struct kgsl_pwrlevel { + unsigned int gpu_freq; + unsigned int bus_freq; +}; + +struct kgsl_version { + unsigned int drv_major; + unsigned int drv_minor; + unsigned int dev_major; + unsigned int dev_minor; +}; + +#ifdef __KERNEL__ +#include + +#define KGSL_3D0_REG_MEMORY "kgsl_3d0_reg_memory" +#define KGSL_3D0_IRQ "kgsl_3d0_irq" +#define KGSL_2D0_REG_MEMORY "kgsl_2d0_reg_memory" +#define KGSL_2D0_IRQ "kgsl_2d0_irq" +#define KGSL_2D1_REG_MEMORY "kgsl_2d1_reg_memory" +#define KGSL_2D1_IRQ "kgsl_2d1_irq" + +struct kgsl_grp_clk_name { + const char *clk; + const char *pclk; +}; + +struct kgsl_device_pwr_data { + struct kgsl_pwrlevel pwrlevel[KGSL_MAX_PWRLEVELS]; + int init_level; + int num_levels; + int (*set_grp_async)(void); + unsigned int idle_timeout; + unsigned int nap_allowed; + bool pwrrail_first; + unsigned int idle_pass; +}; + +struct kgsl_clk_data { + struct kgsl_grp_clk_name name; + struct msm_bus_scale_pdata *bus_scale_table; +}; + +struct kgsl_device_platform_data { + struct kgsl_device_pwr_data pwr_data; + struct kgsl_clk_data clk; + /* imem_clk_name is for 3d only, not used in 2d devices */ + struct kgsl_grp_clk_name imem_clk_name; +}; + +#endif + +/* structure holds list of ibs */ +struct kgsl_ibdesc { + unsigned int gpuaddr; + void *hostptr; + unsigned int sizedwords; + unsigned int ctrl; +}; + /* ioctls */ #define KGSL_IOC_TYPE 0x09 @@ -159,8 +254,8 @@ struct kgsl_device_waittimestamp { */ struct kgsl_ringbuffer_issueibcmds { unsigned int drawctxt_id; - unsigned int ibaddr; - unsigned int sizedwords; + unsigned int ibdesc_addr; + unsigned int numibs; unsigned int timestamp; /*output param */ unsigned int flags; }; @@ -191,6 +286,15 @@ struct kgsl_cmdstream_freememontimestamp { }; #define IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP \ + _IOW(KGSL_IOC_TYPE, 0x12, struct kgsl_cmdstream_freememontimestamp) + +/* Previous versions of this header had incorrectly defined + IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP as a read-only ioctl instead + of a write only ioctl. To ensure binary compatability, the following + #define will be used to intercept the incorrect ioctl +*/ + +#define IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP_OLD \ _IOR(KGSL_IOC_TYPE, 0x12, struct kgsl_cmdstream_freememontimestamp) /* create a draw context, which is used to preserve GPU state. @@ -212,6 +316,22 @@ struct kgsl_drawctxt_destroy { #define IOCTL_KGSL_DRAWCTXT_DESTROY \ _IOW(KGSL_IOC_TYPE, 0x14, struct kgsl_drawctxt_destroy) +/* add a block of pmem, fb, ashmem or user allocated address + * into the GPU address space */ +struct kgsl_map_user_mem { + int fd; + unsigned int gpuaddr; /*output param */ + unsigned int len; + unsigned int offset; + unsigned int hostptr; /*input param */ + enum kgsl_user_mem_type memtype; + unsigned int reserved; /* May be required to add + params for another mem type */ +}; + +#define IOCTL_KGSL_MAP_USER_MEM \ + _IOWR(KGSL_IOC_TYPE, 0x15, struct kgsl_map_user_mem) + /* add a block of pmem or fb into the GPU address space */ struct kgsl_sharedmem_from_pmem { int pmem_fd; @@ -231,6 +351,7 @@ struct kgsl_sharedmem_free { #define IOCTL_KGSL_SHAREDMEM_FREE \ _IOW(KGSL_IOC_TYPE, 0x21, struct kgsl_sharedmem_free) + struct kgsl_gmem_desc { unsigned int x; unsigned int y; @@ -240,9 +361,9 @@ struct kgsl_gmem_desc { }; struct kgsl_buffer_desc { - void *hostptr; + void *hostptr; unsigned int gpuaddr; - int size; + int size; unsigned int format; unsigned int pitch; unsigned int enabled; @@ -264,9 +385,7 @@ struct kgsl_bind_gmem_shadow { struct kgsl_sharedmem_from_vmalloc { unsigned int gpuaddr; /*output param */ unsigned int hostptr; - /* If set from user space then will attempt to - * allocate even if low watermark is crossed */ - int force_no_low_watermark; + unsigned int flags; }; #define IOCTL_KGSL_SHAREDMEM_FROM_VMALLOC \ @@ -283,4 +402,40 @@ struct kgsl_drawctxt_set_bin_base_offset { #define IOCTL_KGSL_DRAWCTXT_SET_BIN_BASE_OFFSET \ _IOW(KGSL_IOC_TYPE, 0x25, struct kgsl_drawctxt_set_bin_base_offset) +enum kgsl_cmdwindow_type { + KGSL_CMDWINDOW_MIN = 0x00000000, + KGSL_CMDWINDOW_2D = 0x00000000, + KGSL_CMDWINDOW_3D = 0x00000001, /* legacy */ + KGSL_CMDWINDOW_MMU = 0x00000002, + KGSL_CMDWINDOW_ARBITER = 0x000000FF, + KGSL_CMDWINDOW_MAX = 0x000000FF, +}; + +/* write to the command window */ +struct kgsl_cmdwindow_write { + enum kgsl_cmdwindow_type target; + unsigned int addr; + unsigned int data; +}; + +#define IOCTL_KGSL_CMDWINDOW_WRITE \ + _IOW(KGSL_IOC_TYPE, 0x2e, struct kgsl_cmdwindow_write) + +struct kgsl_gpumem_alloc { + unsigned long gpuaddr; + size_t size; + unsigned int flags; +}; + +#define IOCTL_KGSL_GPUMEM_ALLOC \ + _IOWR(KGSL_IOC_TYPE, 0x2f, struct kgsl_gpumem_alloc) + +#ifdef __KERNEL__ +#ifdef CONFIG_MSM_KGSL_DRM +int kgsl_gem_obj_addr(int drm_fd, int handle, unsigned long *start, + unsigned long *len); +#else +#define kgsl_gem_obj_addr(...) 0 +#endif +#endif #endif /* _MSM_KGSL_H */ From dd5f94be5049e897343e52e4c05dd0b4deeead46 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 2 Feb 2012 13:59:29 -0600 Subject: [PATCH 2016/2556] drivers: misc: update pmem still pretty old but is at least working --- arch/arm/mach-msm/board-mahimahi.c | 9 +- drivers/misc/pmem.c | 2546 +++++++++++++++++++++++----- include/linux/android_pmem.h | 142 +- 3 files changed, 2234 insertions(+), 463 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 105ef77ec6cc3..9473c3b20340e 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -348,7 +348,8 @@ static struct android_pmem_platform_data mdp_pmem_pdata = { .name = "pmem", .start = MSM_PMEM_MDP_BASE, .size = MSM_PMEM_MDP_SIZE, - .no_allocator = 0, +/* .no_allocator = 0,*/ + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 1, }; @@ -356,7 +357,8 @@ static struct android_pmem_platform_data android_pmem_adsp_pdata = { .name = "pmem_adsp", .start = MSM_PMEM_ADSP_BASE, .size = MSM_PMEM_ADSP_SIZE, - .no_allocator = 0, +/* .no_allocator = 0,*/ + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 1, }; @@ -364,7 +366,8 @@ static struct android_pmem_platform_data android_pmem_venc_pdata = { .name = "pmem_venc", .start = MSM_PMEM_VENC_BASE, .size = MSM_PMEM_VENC_SIZE, - .no_allocator = 0, +/* .no_allocator = 0,*/ + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 1, }; diff --git a/drivers/misc/pmem.c b/drivers/misc/pmem.c index abb73c1431646..813fe6726f17e 100644 --- a/drivers/misc/pmem.c +++ b/drivers/misc/pmem.c @@ -1,6 +1,7 @@ /* drivers/android/pmem.c * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -19,20 +20,38 @@ #include #include #include -#include #include #include #include -#include +#include +#ifdef CONFIG_MEMORY_HOTPLUG +#include +#include +#endif #include #include #include +#include +#include + +#define PMEM_MAX_USER_SPACE_DEVICES (10) +#define PMEM_MAX_KERNEL_SPACE_DEVICES (2) +#define PMEM_MAX_DEVICES \ + (PMEM_MAX_USER_SPACE_DEVICES + PMEM_MAX_KERNEL_SPACE_DEVICES) -#define PMEM_MAX_DEVICES 10 -#define PMEM_MAX_ORDER 128 +#define PMEM_MAX_ORDER (128) #define PMEM_MIN_ALLOC PAGE_SIZE +#define PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS (64) + +#define PMEM_32BIT_WORD_ORDER (5) +#define PMEM_BITS_PER_WORD_MASK (BITS_PER_LONG - 1) + +#ifdef CONFIG_ANDROID_PMEM_DEBUG #define PMEM_DEBUG 1 +#else +#define PMEM_DEBUG 0 +#endif /* indicates that a refernce to this file has been taken via get_pmem_file, * the file should not be released until put_pmem_file is called */ @@ -50,7 +69,6 @@ #define PMEM_FLAGS_SUBMAP 0x1 << 3 #define PMEM_FLAGS_UNSUBMAP 0x1 << 4 - struct pmem_data { /* in alloc mode: an index into the bitmap * in no_alloc mode: the size of the allocation */ @@ -93,13 +111,34 @@ struct pmem_region_node { #define PMEM_DEBUG_MSGS 0 #if PMEM_DEBUG_MSGS #define DLOG(fmt,args...) \ - do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \ + do { pr_debug("[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \ ##args); } \ while (0) #else #define DLOG(x...) do {} while (0) #endif +enum pmem_align { + PMEM_ALIGN_4K, + PMEM_ALIGN_1M, +}; + +#define PMEM_NAME_SIZE 16 + +#define MEMORY_STABLE 0 +#define MEMORY_UNSTABLE_NO_MEMORY_ALLOCATED 1 +#define MEMORY_UNSTABLE_MEMORY_ALLOCATED 2 + +#define NO_UNSTABLE_MEMORY 0 +#define UNSTABLE_UNINITIALIZED 1 +#define UNSTABLE_INITIALIZED 2 + +int unstable_pmem_present; +/* start of unstable PMEM physical memory */ +unsigned long unstable_pmem_start; +/* size of unstable PMEM physical memory */ +unsigned long unstable_pmem_size; + struct pmem_info { struct miscdevice dev; /* physical start address of the remaped pmem space */ @@ -112,62 +151,126 @@ struct pmem_info { unsigned long num_entries; /* pfn of the garbage page in memory */ unsigned long garbage_pfn; + /* memory state (stable/unstable with or without memory */ + int memory_state; + + char name[PMEM_NAME_SIZE]; + /* index of the garbage page in the pmem space */ int garbage_index; - /* the bitmap for the region indicating which entries are allocated - * and which are free */ - struct pmem_bits *bitmap; - /* indicates the region should not be managed with an allocator */ - unsigned no_allocator; + + enum pmem_allocator_type allocator_type; + + int (*allocate)(const int, + const unsigned long, + const unsigned int); + int (*free)(int, int); + int (*free_space)(int, struct pmem_freespace *); + unsigned long (*len)(int, struct pmem_data *); + unsigned long (*start_addr)(int, struct pmem_data *); + int (*kapi_free_index)(const int32_t, int); + + /* actual size of memory element, e.g.: (4 << 10) is 4K */ + unsigned int quantum; + /* indicates maps of this region should be cached, if a mix of * cached and uncached is desired, set this and open the device with * O_SYNC to get an uncached region */ unsigned cached; unsigned buffered; - /* in no_allocator mode the first mapper gets the whole space and sets - * this flag */ - unsigned allocated; + union { + struct { + /* in all_or_nothing allocator mode the first mapper + * gets the whole space and sets this flag */ + unsigned allocated; + } all_or_nothing; + + struct { + /* the buddy allocator bitmap for the region + * indicating which entries are allocated and which + * are free. + */ + + struct pmem_bits *buddy_bitmap; + } buddy_bestfit; + + struct { + unsigned int bitmap_free; /* # of zero bits/quanta */ + uint32_t *bitmap; + int32_t bitmap_allocs; + struct { + short bit; + unsigned short quanta; + } *bitm_alloc; + } bitmap; + } allocator; + + int id; + struct kobject kobj; + /* for debugging, creates a list of pmem file structs, the - * data_list_lock should be taken before pmem_data->sem if both are + * data_list_mutex should be taken before pmem_data->sem if both are * needed */ - struct mutex data_list_lock; + struct mutex data_list_mutex; struct list_head data_list; - /* pmem_sem protects the bitmap array - * a write lock should be held when modifying entries in bitmap - * a read lock should be held when reading data from bits or - * dereferencing a pointer into bitmap - * - * pmem_data->sem protects the pmem data of a particular file - * Many of the function that require the pmem_data->sem have a non- - * locking version for when the caller is already holding that sem. + /* arena_mutex protects the global allocation arena * * IF YOU TAKE BOTH LOCKS TAKE THEM IN THIS ORDER: - * down(pmem_data->sem) => down(bitmap_sem) + * down(pmem_data->sem) => mutex_lock(arena_mutex) */ - struct rw_semaphore bitmap_sem; + struct mutex arena_mutex; long (*ioctl)(struct file *, unsigned int, unsigned long); int (*release)(struct inode *, struct file *); }; +#define to_pmem_info_id(a) (container_of(a, struct pmem_info, kobj)->id) static struct pmem_info pmem[PMEM_MAX_DEVICES]; static int id_count; +static struct { + const char * const name; + const int memtype; + const int fallback_memtype; + int info_id; +} kapi_memtypes[] = { +#ifdef CONFIG_KERNEL_PMEM_SMI_REGION + { PMEM_KERNEL_SMI_DATA_NAME, + PMEM_MEMTYPE_SMI, + PMEM_MEMTYPE_EBI1, /* Fall back to EBI1 automatically */ + -1 }, +#endif + { PMEM_KERNEL_EBI1_DATA_NAME, + PMEM_MEMTYPE_EBI1, + PMEM_INVALID_MEMTYPE, /* MUST be set invalid if no fallback */ + -1 }, +}; -#define PMEM_IS_FREE(id, index) !(pmem[id].bitmap[index].allocated) -#define PMEM_ORDER(id, index) pmem[id].bitmap[index].order -#define PMEM_BUDDY_INDEX(id, index) (index ^ (1 << PMEM_ORDER(id, index))) -#define PMEM_NEXT_INDEX(id, index) (index + (1 << PMEM_ORDER(id, index))) -#define PMEM_OFFSET(index) (index * PMEM_MIN_ALLOC) -#define PMEM_START_ADDR(id, index) (PMEM_OFFSET(index) + pmem[id].base) -#define PMEM_LEN(id, index) ((1 << PMEM_ORDER(id, index)) * PMEM_MIN_ALLOC) -#define PMEM_END_ADDR(id, index) (PMEM_START_ADDR(id, index) + \ - PMEM_LEN(id, index)) -#define PMEM_START_VADDR(id, index) (PMEM_OFFSET(id, index) + pmem[id].vbase) -#define PMEM_END_VADDR(id, index) (PMEM_START_VADDR(id, index) + \ - PMEM_LEN(id, index)) +#define PMEM_SYSFS_DIR_NAME "pmem_regions" /* under /sys/kernel/ */ +static struct kset *pmem_kset; + +#define PMEM_IS_FREE_BUDDY(id, index) \ + (!(pmem[id].allocator.buddy_bestfit.buddy_bitmap[index].allocated)) +#define PMEM_BUDDY_ORDER(id, index) \ + (pmem[id].allocator.buddy_bestfit.buddy_bitmap[index].order) +#define PMEM_BUDDY_INDEX(id, index) \ + (index ^ (1 << PMEM_BUDDY_ORDER(id, index))) +#define PMEM_BUDDY_NEXT_INDEX(id, index) \ + (index + (1 << PMEM_BUDDY_ORDER(id, index))) +#define PMEM_OFFSET(index) (index * pmem[id].quantum) +#define PMEM_START_ADDR(id, index) \ + (PMEM_OFFSET(index) + pmem[id].base) +#define PMEM_BUDDY_LEN(id, index) \ + ((1 << PMEM_BUDDY_ORDER(id, index)) * pmem[id].quantum) +#define PMEM_END_ADDR(id, index) \ + (PMEM_START_ADDR(id, index) + PMEM_LEN(id, index)) +#define PMEM_START_VADDR(id, index) \ + (PMEM_OFFSET(id, index) + pmem[id].vbase) +#define PMEM_END_VADDR(id, index) \ + (PMEM_START_VADDR(id, index) + PMEM_LEN(id, index)) #define PMEM_REVOKED(data) (data->flags & PMEM_FLAGS_REVOKED) #define PMEM_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK))) -#define PMEM_IS_SUBMAP(data) ((data->flags & PMEM_FLAGS_SUBMAP) && \ +#define PMEM_IS_SUBMAP(data) \ + ((data->flags & PMEM_FLAGS_SUBMAP) && \ (!(data->flags & PMEM_FLAGS_UNSUBMAP))) static int pmem_release(struct inode *, struct file *); @@ -182,48 +285,316 @@ struct file_operations pmem_fops = { .unlocked_ioctl = pmem_ioctl, }; +#define PMEM_ATTR(_name, _mode, _show, _store) { \ + .attr = {.name = __stringify(_name), .mode = _mode }, \ + .show = _show, \ + .store = _store, \ +} + +struct pmem_attr { + struct attribute attr; + ssize_t(*show) (const int id, char * const); + ssize_t(*store) (const int id, const char * const, const size_t count); +}; +#define to_pmem_attr(a) container_of(a, struct pmem_attr, attr) + +#define RW_PMEM_ATTR(name) \ +static struct pmem_attr pmem_attr_## name = \ + PMEM_ATTR(name, S_IRUGO | S_IWUSR, show_pmem_## name, store_pmem_## name) + +#define RO_PMEM_ATTR(name) \ +static struct pmem_attr pmem_attr_## name = \ + PMEM_ATTR(name, S_IRUGO, show_pmem_## name, NULL) + +#define WO_PMEM_ATTR(name) \ +static struct pmem_attr pmem_attr_## name = \ + PMEM_ATTR(name, S_IWUSR, NULL, store_pmem_## name) +/*HTC_START*/ +static struct dentry *root = NULL; +u32 misc_msg_pmem_qcom = 0; + +static struct dentry *vidc_debugfs_root; + +static struct dentry *vidc_get_debugfs_root(void) +{ + if (vidc_debugfs_root == NULL) + vidc_debugfs_root = debugfs_create_dir("misc", NULL); + return vidc_debugfs_root; +} + +static void vidc_debugfs_file_create(struct dentry *root, const char *name, + u32 *var) +{ + struct dentry *vidc_debugfs_file = + debugfs_create_u32(name, S_IRUGO | S_IWUSR, root, var); + if (!vidc_debugfs_file) + pr_info("%s(): Error creating/opening file %s\n", __func__, name); +} +/*HTC_END*/ +static ssize_t show_pmem(struct kobject *kobj, + struct attribute *attr, + char *buf) +{ + struct pmem_attr *a = to_pmem_attr(attr); + return a->show ? a->show(to_pmem_info_id(kobj), buf) : -EIO; +} + +static ssize_t store_pmem(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct pmem_attr *a = to_pmem_attr(attr); + return a->store ? a->store(to_pmem_info_id(kobj), buf, count) : -EIO; +} + +static struct sysfs_ops pmem_ops = { + .show = show_pmem, + .store = store_pmem, +}; + +static ssize_t show_pmem_base(int id, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%lu(%#lx)\n", + pmem[id].base, pmem[id].base); +} +RO_PMEM_ATTR(base); + +static ssize_t show_pmem_size(int id, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%lu(%#lx)\n", + pmem[id].size, pmem[id].size); +} +RO_PMEM_ATTR(size); + +static ssize_t show_pmem_allocator_type(int id, char *buf) +{ + switch (pmem[id].allocator_type) { + case PMEM_ALLOCATORTYPE_ALLORNOTHING: + return scnprintf(buf, PAGE_SIZE, "%s\n", "All or Nothing"); + case PMEM_ALLOCATORTYPE_BUDDYBESTFIT: + return scnprintf(buf, PAGE_SIZE, "%s\n", "Buddy Bestfit"); + case PMEM_ALLOCATORTYPE_BITMAP: + return scnprintf(buf, PAGE_SIZE, "%s\n", "Bitmap"); + default: + return scnprintf(buf, PAGE_SIZE, + "??? Invalid allocator type (%d) for this region! " + "Something isn't right.\n", + pmem[id].allocator_type); + } +} +RO_PMEM_ATTR(allocator_type); + +static ssize_t show_pmem_mapped_regions(int id, char *buf) +{ + struct list_head *elt; + int ret; + + ret = scnprintf(buf, PAGE_SIZE, + "pid #: mapped regions (offset, len) (offset,len)...\n"); + + mutex_lock(&pmem[id].data_list_mutex); + list_for_each(elt, &pmem[id].data_list) { + struct pmem_data *data = + list_entry(elt, struct pmem_data, list); + struct list_head *elt2; + + down_read(&data->sem); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, "pid %u:", + data->pid); + list_for_each(elt2, &data->region_list) { + struct pmem_region_node *region_node = list_entry(elt2, + struct pmem_region_node, + list); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "(%lx,%lx) ", + region_node->region.offset, + region_node->region.len); + } + up_read(&data->sem); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n"); + } + mutex_unlock(&pmem[id].data_list_mutex); + return ret; +} +RO_PMEM_ATTR(mapped_regions); + +#define PMEM_COMMON_SYSFS_ATTRS \ + &pmem_attr_base.attr, \ + &pmem_attr_size.attr, \ + &pmem_attr_allocator_type.attr, \ + &pmem_attr_mapped_regions.attr + + +static ssize_t show_pmem_allocated(int id, char *buf) +{ + ssize_t ret; + + mutex_lock(&pmem[id].arena_mutex); + ret = scnprintf(buf, PAGE_SIZE, "%s\n", + pmem[id].allocator.all_or_nothing.allocated ? + "is allocated" : "is NOT allocated"); + mutex_unlock(&pmem[id].arena_mutex); + return ret; +} +RO_PMEM_ATTR(allocated); + +static struct attribute *pmem_allornothing_attrs[] = { + PMEM_COMMON_SYSFS_ATTRS, + + &pmem_attr_allocated.attr, + + NULL +}; + +static struct kobj_type pmem_allornothing_ktype = { + .sysfs_ops = &pmem_ops, + .default_attrs = pmem_allornothing_attrs, +}; + +static ssize_t show_pmem_total_entries(int id, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%lu\n", pmem[id].num_entries); +} +RO_PMEM_ATTR(total_entries); + +static ssize_t show_pmem_quantum_size(int id, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%u (%#x)\n", + pmem[id].quantum, pmem[id].quantum); +} +RO_PMEM_ATTR(quantum_size); + +static ssize_t show_pmem_buddy_bitmap_dump(int id, char *buf) +{ + int ret, i; + + mutex_lock(&pmem[id].data_list_mutex); + ret = scnprintf(buf, PAGE_SIZE, "index\torder\tlength\tallocated\n"); + + for (i = 0; i < pmem[id].num_entries && (PAGE_SIZE - ret); + i = PMEM_BUDDY_NEXT_INDEX(id, i)) + ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%d\t%d\t%d\t%d\n", + i, PMEM_BUDDY_ORDER(id, i), + PMEM_BUDDY_LEN(id, i), + !PMEM_IS_FREE_BUDDY(id, i)); + + mutex_unlock(&pmem[id].data_list_mutex); + return ret; +} +RO_PMEM_ATTR(buddy_bitmap_dump); + +#define PMEM_BITMAP_BUDDY_BESTFIT_COMMON_SYSFS_ATTRS \ + &pmem_attr_quantum_size.attr, \ + &pmem_attr_total_entries.attr + +static struct attribute *pmem_buddy_bestfit_attrs[] = { + PMEM_COMMON_SYSFS_ATTRS, + + PMEM_BITMAP_BUDDY_BESTFIT_COMMON_SYSFS_ATTRS, + + &pmem_attr_buddy_bitmap_dump.attr, + + NULL +}; + +static struct kobj_type pmem_buddy_bestfit_ktype = { + .sysfs_ops = &pmem_ops, + .default_attrs = pmem_buddy_bestfit_attrs, +}; + +static ssize_t show_pmem_free_quanta(int id, char *buf) +{ + ssize_t ret; + + mutex_lock(&pmem[id].arena_mutex); + ret = scnprintf(buf, PAGE_SIZE, "%u\n", + pmem[id].allocator.bitmap.bitmap_free); + mutex_unlock(&pmem[id].arena_mutex); + return ret; +} +RO_PMEM_ATTR(free_quanta); + +static ssize_t show_pmem_bits_allocated(int id, char *buf) +{ + ssize_t ret; + unsigned int i; + + mutex_lock(&pmem[id].arena_mutex); + + ret = scnprintf(buf, PAGE_SIZE, + "id: %d\nbitnum\tindex\tquanta allocated\n", id); + + for (i = 0; i < pmem[id].allocator.bitmap.bitmap_allocs; i++) + if (pmem[id].allocator.bitmap.bitm_alloc[i].bit != -1) + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "%u\t%u\t%u\n", + i, + pmem[id].allocator.bitmap.bitm_alloc[i].bit, + pmem[id].allocator.bitmap.bitm_alloc[i].quanta + ); + + mutex_unlock(&pmem[id].arena_mutex); + return ret; +} +RO_PMEM_ATTR(bits_allocated); + +static struct attribute *pmem_bitmap_attrs[] = { + PMEM_COMMON_SYSFS_ATTRS, + + PMEM_BITMAP_BUDDY_BESTFIT_COMMON_SYSFS_ATTRS, + + &pmem_attr_free_quanta.attr, + &pmem_attr_bits_allocated.attr, + + NULL +}; + +static struct kobj_type pmem_bitmap_ktype = { + .sysfs_ops = &pmem_ops, + .default_attrs = pmem_bitmap_attrs, +}; + static int get_id(struct file *file) { return MINOR(file->f_dentry->d_inode->i_rdev); } -int is_pmem_file(struct file *file) +static char *get_name(struct file *file) +{ + int id = get_id(file); + return pmem[id].name; +} + +static int is_pmem_file(struct file *file) { int id; if (unlikely(!file || !file->f_dentry || !file->f_dentry->d_inode)) return 0; + id = get_id(file); - if (unlikely(id >= PMEM_MAX_DEVICES)) - return 0; - if (unlikely(file->f_dentry->d_inode->i_rdev != - MKDEV(MISC_MAJOR, pmem[id].dev.minor))) - return 0; - return 1; + return (unlikely(id >= PMEM_MAX_DEVICES || + file->f_dentry->d_inode->i_rdev != + MKDEV(MISC_MAJOR, pmem[id].dev.minor))) ? 0 : 1; } static int has_allocation(struct file *file) { - struct pmem_data *data; - /* check is_pmem_file first if not accessed via pmem_file_ops */ - - if (unlikely(!file->private_data)) - return 0; - data = (struct pmem_data *)file->private_data; - if (unlikely(data->index < 0)) - return 0; - return 1; + /* must be called with at least read lock held on + * ((struct pmem_data *)(file->private_data))->sem which + * means that file is guaranteed not to be NULL upon entry!! + * check is_pmem_file first if not accessed via pmem_file_ops */ + struct pmem_data *pdata = file->private_data; + return pdata && pdata->index >= 0; } static int is_master_owner(struct file *file) { struct file *master_file; - struct pmem_data *data; + struct pmem_data *data = file->private_data; int put_needed, ret = 0; - if (!is_pmem_file(file) || !has_allocation(file)) + if (!has_allocation(file)) return 0; - data = (struct pmem_data *)file->private_data; if (PMEM_FLAGS_MASTERMAP & data->flags) return 1; master_file = fget_light(data->master_fd, &put_needed); @@ -233,28 +604,49 @@ static int is_master_owner(struct file *file) return ret; } -static int pmem_free(int id, int index) +static int pmem_free_all_or_nothing(int id, int index) { - /* caller should hold the write lock on pmem_sem! */ - int buddy, curr = index; + /* caller should hold the lock on arena_mutex! */ DLOG("index %d\n", index); - if (pmem[id].no_allocator) { - pmem[id].allocated = 0; - return 0; - } + pmem[id].allocator.all_or_nothing.allocated = 0; + return 0; +} + +static int pmem_free_space_all_or_nothing(int id, + struct pmem_freespace *fs) +{ + /* caller should hold the lock on arena_mutex! */ + fs->total = (unsigned long) + pmem[id].allocator.all_or_nothing.allocated == 0 ? + pmem[id].size : 0; + + fs->largest = fs->total; + return 0; +} + + +static int pmem_free_buddy_bestfit(int id, int index) +{ + /* caller should hold the lock on arena_mutex! */ + int curr = index; + DLOG("index %d\n", index); + + /* clean up the bitmap, merging any buddies */ - pmem[id].bitmap[curr].allocated = 0; + pmem[id].allocator.buddy_bestfit.buddy_bitmap[curr].allocated = 0; /* find a slots buddy Buddy# = Slot# ^ (1 << order) * if the buddy is also free merge them * repeat until the buddy is not free or end of the bitmap is reached */ do { - buddy = PMEM_BUDDY_INDEX(id, curr); - if (PMEM_IS_FREE(id, buddy) && - PMEM_ORDER(id, buddy) == PMEM_ORDER(id, curr)) { - PMEM_ORDER(id, buddy)++; - PMEM_ORDER(id, curr)++; + int buddy = PMEM_BUDDY_INDEX(id, curr); + if (buddy < pmem[id].num_entries && + PMEM_IS_FREE_BUDDY(id, buddy) && + PMEM_BUDDY_ORDER(id, buddy) == + PMEM_BUDDY_ORDER(id, curr)) { + PMEM_BUDDY_ORDER(id, buddy)++; + PMEM_BUDDY_ORDER(id, curr)++; curr = min(buddy, curr); } else { break; @@ -264,43 +656,193 @@ static int pmem_free(int id, int index) return 0; } + +static int pmem_free_space_buddy_bestfit(int id, + struct pmem_freespace *fs) +{ + /* caller should hold the lock on arena_mutex! */ + int curr; + unsigned long size; + fs->total = 0; + fs->largest = 0; + + for (curr = 0; curr < pmem[id].num_entries; + curr = PMEM_BUDDY_NEXT_INDEX(id, curr)) { + if (PMEM_IS_FREE_BUDDY(id, curr)) { + size = PMEM_BUDDY_LEN(id, curr); + if (size > fs->largest) + fs->largest = size; + fs->total += size; + } + } + return 0; +} + + +static inline uint32_t start_mask(int bit_start) +{ + return (uint32_t)(~0) << (bit_start & PMEM_BITS_PER_WORD_MASK); +} + +static inline uint32_t end_mask(int bit_end) +{ + return (uint32_t)(~0) >> + ((BITS_PER_LONG - bit_end) & PMEM_BITS_PER_WORD_MASK); +} + +static inline int compute_total_words(int bit_end, int word_index) +{ + return ((bit_end + BITS_PER_LONG - 1) >> + PMEM_32BIT_WORD_ORDER) - word_index; +} + +static void bitmap_bits_clear_all(uint32_t *bitp, int bit_start, int bit_end) +{ + int word_index = bit_start >> PMEM_32BIT_WORD_ORDER, total_words; + + total_words = compute_total_words(bit_end, word_index); + if (total_words > 0) { + if (total_words == 1) { + bitp[word_index] &= + ~(start_mask(bit_start) & end_mask(bit_end)); + } else { + bitp[word_index++] &= ~start_mask(bit_start); + if (total_words > 2) { + int total_bytes; + + total_words -= 2; + total_bytes = total_words << 2; + + memset(&bitp[word_index], 0, total_bytes); + word_index += total_words; + } + bitp[word_index] &= ~end_mask(bit_end); + } + } +} + +static int pmem_free_bitmap(int id, int bitnum) +{ + /* caller should hold the lock on arena_mutex! */ + int i; + char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1]; +/*HTC_START*/ + if (misc_msg_pmem_qcom) + pr_info("[PME][%s] pmem_free_bitmap, bitnum %d\n", pmem[id].name, bitnum); +/*HTC_END*/ + for (i = 0; i < pmem[id].allocator.bitmap.bitmap_allocs; i++) { + const int curr_bit = + pmem[id].allocator.bitmap.bitm_alloc[i].bit; + + if (curr_bit == bitnum) { + const int curr_quanta = + pmem[id].allocator.bitmap.bitm_alloc[i].quanta; + + bitmap_bits_clear_all(pmem[id].allocator.bitmap.bitmap, + curr_bit, curr_bit + curr_quanta); + pmem[id].allocator.bitmap.bitmap_free += curr_quanta; + pmem[id].allocator.bitmap.bitm_alloc[i].bit = -1; + return 0; + } + } + printk(KERN_ALERT "pmem: %s: Attempt to free unallocated index %d, id" + " %d, pid %d(%s)\n", __func__, bitnum, id, current->pid, + get_task_comm(currtask_name, current)); + + return -1; +} + +static int pmem_free_space_bitmap(int id, struct pmem_freespace *fs) +{ + int i, j; + int max_allocs = pmem[id].allocator.bitmap.bitmap_allocs; + int alloc_start = 0; + int next_alloc; + unsigned long size = 0; + + fs->total = 0; + fs->largest = 0; + + for (i = 0; i < max_allocs; i++) { + + int alloc_quanta = 0; + int alloc_idx = 0; + next_alloc = pmem[id].num_entries; + + /* Look for the lowest bit where next allocation starts */ + for (j = 0; j < max_allocs; j++) { + const int curr_alloc = pmem[id].allocator. + bitmap.bitm_alloc[j].bit; + if (curr_alloc != -1) { + if (alloc_start >= curr_alloc) + continue; + if (curr_alloc < next_alloc) { + next_alloc = curr_alloc; + alloc_idx = j; + } + } + } + alloc_quanta = pmem[id].allocator.bitmap. + bitm_alloc[alloc_idx].quanta; + size = (next_alloc - (alloc_start + alloc_quanta)) * + pmem[id].quantum; + + if (size > fs->largest) + fs->largest = size; + fs->total += size; + + if (next_alloc == pmem[id].num_entries) + break; + else + alloc_start = next_alloc; + } + + return 0; +} + static void pmem_revoke(struct file *file, struct pmem_data *data); static int pmem_release(struct inode *inode, struct file *file) { - struct pmem_data *data = (struct pmem_data *)file->private_data; + struct pmem_data *data = file->private_data; struct pmem_region_node *region_node; struct list_head *elt, *elt2; int id = get_id(file), ret = 0; - - mutex_lock(&pmem[id].data_list_lock); +#if PMEM_DEBUG_MSGS + char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1]; +#endif + DLOG("releasing memory pid %u(%s) file %p(%ld) dev %s(id: %d)\n", + current->pid, get_task_comm(currtask_name, current), + file, file_count(file), get_name(file), id); + mutex_lock(&pmem[id].data_list_mutex); /* if this file is a master, revoke all the memory in the connected * files */ if (PMEM_FLAGS_MASTERMAP & data->flags) { - struct pmem_data *sub_data; list_for_each(elt, &pmem[id].data_list) { - sub_data = list_entry(elt, struct pmem_data, list); + struct pmem_data *sub_data = + list_entry(elt, struct pmem_data, list); + int is_master; + down_read(&sub_data->sem); - if (PMEM_IS_SUBMAP(sub_data) && - file == sub_data->master_file) { - up_read(&sub_data->sem); + is_master = (PMEM_IS_SUBMAP(sub_data) && + file == sub_data->master_file); + up_read(&sub_data->sem); + + if (is_master) pmem_revoke(file, sub_data); - } else - up_read(&sub_data->sem); } } list_del(&data->list); - mutex_unlock(&pmem[id].data_list_lock); - + mutex_unlock(&pmem[id].data_list_mutex); down_write(&data->sem); - /* if its not a conencted file and it has an allocation, free it */ + /* if it is not a connected file and it has an allocation, free it */ if (!(PMEM_FLAGS_CONNECTED & data->flags) && has_allocation(file)) { - down_write(&pmem[id].bitmap_sem); - ret = pmem_free(id, data->index); - up_write(&pmem[id].bitmap_sem); + mutex_lock(&pmem[id].arena_mutex); + ret = pmem[id].free(id, data->index); + mutex_unlock(&pmem[id].arena_mutex); } /* if this file is a submap (mapped, connected file), downref the @@ -333,15 +875,23 @@ static int pmem_open(struct inode *inode, struct file *file) struct pmem_data *data; int id = get_id(file); int ret = 0; +#if PMEM_DEBUG_MSGS + char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1]; +#endif - DLOG("current %u file %p(%d)\n", current->pid, file, file_count(file)); + if (pmem[id].memory_state == MEMORY_UNSTABLE_NO_MEMORY_ALLOCATED) + return -ENODEV; + DLOG("pid %u(%s) file %p(%ld) dev %s(id: %d)\n", + current->pid, get_task_comm(currtask_name, current), + file, file_count(file), get_name(file), id); /* setup file->private_data to indicate its unmapped */ /* you can only open a pmem device one time */ if (file->private_data != NULL) - return -1; + return -EINVAL; data = kmalloc(sizeof(struct pmem_data), GFP_KERNEL); if (!data) { - printk("pmem: unable to allocate memory for pmem metadata."); + printk(KERN_ALERT "pmem: %s: unable to allocate memory for " + "pmem metadata.", __func__); return -1; } data->flags = 0; @@ -359,17 +909,17 @@ static int pmem_open(struct inode *inode, struct file *file) file->private_data = data; INIT_LIST_HEAD(&data->list); - mutex_lock(&pmem[id].data_list_lock); + mutex_lock(&pmem[id].data_list_mutex); list_add(&data->list, &pmem[id].data_list); - mutex_unlock(&pmem[id].data_list_lock); + mutex_unlock(&pmem[id].data_list_mutex); return ret; } -static unsigned long pmem_order(unsigned long len) +static unsigned long pmem_order(unsigned long len, int id) { int i; - len = (len + PMEM_MIN_ALLOC - 1)/PMEM_MIN_ALLOC; + len = (len + pmem[id].quantum - 1)/pmem[id].quantum; len--; for (i = 0; i < sizeof(len)*8; i++) if (len >> i == 0) @@ -377,109 +927,424 @@ static unsigned long pmem_order(unsigned long len) return i; } -static int pmem_allocate(int id, unsigned long len) +static int pmem_allocator_all_or_nothing(const int id, + const unsigned long len, + const unsigned int align) { - /* caller should hold the write lock on pmem_sem! */ - /* return the corresponding pdata[] entry */ - int curr = 0; - int end = pmem[id].num_entries; - int best_fit = -1; - unsigned long order = pmem_order(len); + /* caller should hold the lock on arena_mutex! */ + DLOG("all or nothing\n"); + if ((len > pmem[id].size) || + pmem[id].allocator.all_or_nothing.allocated) + return -1; + pmem[id].allocator.all_or_nothing.allocated = 1; + return len; +} - if (pmem[id].no_allocator) { - DLOG("no allocator"); - if ((len > pmem[id].size) || pmem[id].allocated) - return -1; - pmem[id].allocated = 1; - return len; - } +static int pmem_allocator_buddy_bestfit(const int id, + const unsigned long len, + unsigned int align) +{ + /* caller should hold the lock on arena_mutex! */ + int curr; + int best_fit = -1; + unsigned long order; + DLOG("buddy bestfit\n"); + order = pmem_order(len, id); if (order > PMEM_MAX_ORDER) - return -1; + goto out; + DLOG("order %lx\n", order); - /* look through the bitmap: - * if you find a free slot of the correct order use it - * otherwise, use the best fit (smallest with size > order) slot + /* Look through the bitmap. + * If a free slot of the correct order is found, use it. + * Otherwise, use the best fit (smallest with size > order) slot. */ - while (curr < end) { - if (PMEM_IS_FREE(id, curr)) { - if (PMEM_ORDER(id, curr) == (unsigned char)order) { + for (curr = 0; + curr < pmem[id].num_entries; + curr = PMEM_BUDDY_NEXT_INDEX(id, curr)) + if (PMEM_IS_FREE_BUDDY(id, curr)) { + if (PMEM_BUDDY_ORDER(id, curr) == + (unsigned char)order) { /* set the not free bit and clear others */ best_fit = curr; break; } - if (PMEM_ORDER(id, curr) > (unsigned char)order && + if (PMEM_BUDDY_ORDER(id, curr) > + (unsigned char)order && (best_fit < 0 || - PMEM_ORDER(id, curr) < PMEM_ORDER(id, best_fit))) + PMEM_BUDDY_ORDER(id, curr) < + PMEM_BUDDY_ORDER(id, best_fit))) best_fit = curr; } - curr = PMEM_NEXT_INDEX(id, curr); - } - /* if best_fit < 0, there are no suitable slots, - * return an error - */ + /* if best_fit < 0, there are no suitable slots; return an error */ if (best_fit < 0) { - printk("pmem: no space left to allocate!\n"); - return -1; +#if PMEM_DEBUG + printk(KERN_ALERT "pmem: %s: no space left to allocate!\n", + __func__); +#endif + goto out; } /* now partition the best fit: * split the slot into 2 buddies of order - 1 * repeat until the slot is of the correct order */ - while (PMEM_ORDER(id, best_fit) > (unsigned char)order) { + while (PMEM_BUDDY_ORDER(id, best_fit) > (unsigned char)order) { int buddy; - PMEM_ORDER(id, best_fit) -= 1; + PMEM_BUDDY_ORDER(id, best_fit) -= 1; buddy = PMEM_BUDDY_INDEX(id, best_fit); - PMEM_ORDER(id, buddy) = PMEM_ORDER(id, best_fit); + PMEM_BUDDY_ORDER(id, buddy) = PMEM_BUDDY_ORDER(id, best_fit); } - pmem[id].bitmap[best_fit].allocated = 1; + pmem[id].allocator.buddy_bestfit.buddy_bitmap[best_fit].allocated = 1; +out: return best_fit; } -static pgprot_t pmem_access_prot(struct file *file, pgprot_t vma_prot) -{ - int id = get_id(file); -#ifdef pgprot_noncached - if (pmem[id].cached == 0 || file->f_flags & O_SYNC) - return pgprot_noncached(vma_prot); -#endif -#ifdef pgprot_ext_buffered - else if (pmem[id].buffered) - return pgprot_ext_buffered(vma_prot); -#endif - return vma_prot; -} -static unsigned long pmem_start_addr(int id, struct pmem_data *data) +static inline unsigned long paddr_from_bit(const int id, const int bitnum) { - if (pmem[id].no_allocator) - return PMEM_START_ADDR(id, 0); - else - return PMEM_START_ADDR(id, data->index); - + return pmem[id].base + pmem[id].quantum * bitnum; } -static void *pmem_start_vaddr(int id, struct pmem_data *data) +static inline unsigned long bit_from_paddr(const int id, + const unsigned long paddr) { - return pmem_start_addr(id, data) - pmem[id].base + pmem[id].vbase; + return (paddr - pmem[id].base) / pmem[id].quantum; } -static unsigned long pmem_len(int id, struct pmem_data *data) +static void bitmap_bits_set_all(uint32_t *bitp, int bit_start, int bit_end) { - if (pmem[id].no_allocator) - return data->index; - else - return PMEM_LEN(id, data->index); + int word_index = bit_start >> PMEM_32BIT_WORD_ORDER, total_words; + + total_words = compute_total_words(bit_end, word_index); + if (total_words > 0) { + if (total_words == 1) { + bitp[word_index] |= + (start_mask(bit_start) & end_mask(bit_end)); + } else { + bitp[word_index++] |= start_mask(bit_start); + if (total_words > 2) { + int total_bytes; + + total_words -= 2; + total_bytes = total_words << 2; + + memset(&bitp[word_index], ~0, total_bytes); + word_index += total_words; + } + bitp[word_index] |= end_mask(bit_end); + } + } } -static int pmem_map_garbage(int id, struct vm_area_struct *vma, - struct pmem_data *data, unsigned long offset, - unsigned long len) +static int +bitmap_allocate_contiguous(uint32_t *bitp, int num_bits_to_alloc, + int total_bits, int spacing) { - int i, garbage_pages = len >> PAGE_SHIFT; + int bit_start, last_bit, word_index; + + if (num_bits_to_alloc <= 0) + return -1; + + for (bit_start = 0; ; + bit_start = (last_bit + + (word_index << PMEM_32BIT_WORD_ORDER) + spacing - 1) + & ~(spacing - 1)) { + int bit_end = bit_start + num_bits_to_alloc, total_words; + + if (bit_end > total_bits) + return -1; /* out of contiguous memory */ + + word_index = bit_start >> PMEM_32BIT_WORD_ORDER; + total_words = compute_total_words(bit_end, word_index); + + if (total_words <= 0) + return -1; + + if (total_words == 1) { + last_bit = fls(bitp[word_index] & + (start_mask(bit_start) & + end_mask(bit_end))); + if (last_bit) + continue; + } else { + int end_word = word_index + (total_words - 1); + last_bit = + fls(bitp[word_index] & start_mask(bit_start)); + if (last_bit) + continue; + + for (word_index++; + word_index < end_word; + word_index++) { + last_bit = fls(bitp[word_index]); + if (last_bit) + break; + } + if (last_bit) + continue; + + last_bit = fls(bitp[word_index] & end_mask(bit_end)); + if (last_bit) + continue; + } + bitmap_bits_set_all(bitp, bit_start, bit_end); + return bit_start; + } + return -1; +} + +static int reserve_quanta(const unsigned int quanta_needed, + const int id, + unsigned int align) +{ + /* alignment should be a valid power of 2 */ + int ret = -1, start_bit = 0, spacing = 1; + + /* Sanity check */ + if (quanta_needed > pmem[id].allocator.bitmap.bitmap_free) { +#if PMEM_DEBUG + printk(KERN_ALERT "pmem: %s: request (%d) too big for" + " available free (%d)\n", __func__, quanta_needed, + pmem[id].allocator.bitmap.bitmap_free); +#endif + return -1; + } + + start_bit = bit_from_paddr(id, + (pmem[id].base + align - 1) & ~(align - 1)); + if (start_bit <= -1) { +#if PMEM_DEBUG + printk(KERN_ALERT + "pmem: %s: bit_from_paddr fails for" + " %u alignment.\n", __func__, align); +#endif + return -1; + } + spacing = align / pmem[id].quantum; + spacing = spacing > 1 ? spacing : 1; + + ret = bitmap_allocate_contiguous(pmem[id].allocator.bitmap.bitmap, + quanta_needed, + (pmem[id].size + pmem[id].quantum - 1) / pmem[id].quantum, + spacing); + +#if PMEM_DEBUG + if (ret < 0) + printk(KERN_ALERT "pmem: %s: not enough contiguous bits free " + "in bitmap! Region memory is either too fragmented or" + " request is too large for available memory.\n", + __func__); +#endif + + return ret; +} + +static int pmem_allocator_bitmap(const int id, + const unsigned long len, + const unsigned int align) +{ + /* caller should hold the lock on arena_mutex! */ + int bitnum, i; + unsigned int quanta_needed; +/*HTC_START*/ + if (misc_msg_pmem_qcom) + pr_info("[PME][%s] pmem_allocator_bitmap, len %ld\n", pmem[id].name, len); + + if (!pmem[id].allocator.bitmap.bitm_alloc) { + if (misc_msg_pmem_qcom) { + printk(KERN_ALERT "[PME][%s] bitm_alloc not present! \n", + pmem[id].name); + } +/*HTC_END*/ + bitnum = -1; goto leave; + } + + quanta_needed = (len + pmem[id].quantum - 1) / pmem[id].quantum; +/*HTC_START*/ + if (misc_msg_pmem_qcom) { + pr_info("[PME][%s] quantum size %u quanta needed %u free %u\n", + pmem[id].name, pmem[id].quantum, quanta_needed, + pmem[id].allocator.bitmap.bitmap_free); + } + + if (pmem[id].allocator.bitmap.bitmap_free < quanta_needed) { + if (misc_msg_pmem_qcom) { + printk(KERN_ALERT "[PME][%s] memory allocation failure. " + "PMEM memory region exhausted." + " Unable to comply with allocation request.\n", pmem[id].name); + } +/*HTC_END*/ + bitnum = -1; goto leave; + } + + bitnum = reserve_quanta(quanta_needed, id, align); + if (bitnum == -1) + goto leave; + + for (i = 0; + i < pmem[id].allocator.bitmap.bitmap_allocs && + pmem[id].allocator.bitmap.bitm_alloc[i].bit != -1; + i++) + ; + + if (i >= pmem[id].allocator.bitmap.bitmap_allocs) { + void *temp; + int32_t new_bitmap_allocs = + pmem[id].allocator.bitmap.bitmap_allocs << 1; + int j; + + if (!new_bitmap_allocs) { /* failed sanity check!! */ +/*HTC_START*/ + if (misc_msg_pmem_qcom) { + pr_alert("[PME][%s] pmem: bitmap_allocs number" + " wrapped around to zero! Something " + "is VERY wrong.\n", pmem[id].name); + } + bitnum = -1; goto leave; + } + if (new_bitmap_allocs > pmem[id].num_entries) { + /* failed sanity check!! */ + if (misc_msg_pmem_qcom) { + pr_alert("[PME][%s] pmem: required bitmap_allocs" + " number exceeds maximum entries possible" + " for current quanta\n", pmem[id].name); + } + + bitnum = -1; goto leave; + } + temp = krealloc(pmem[id].allocator.bitmap.bitm_alloc, + new_bitmap_allocs * + sizeof(*pmem[id].allocator.bitmap.bitm_alloc), + GFP_KERNEL); + if (!temp) { + if (misc_msg_pmem_qcom) { + pr_alert("[PME][%s] can't realloc bitmap_allocs," + " current num bitmap allocs %d\n", + pmem[id].name, pmem[id].allocator.bitmap.bitmap_allocs); + } +/*HTC_END*/ + bitnum = -1; goto leave; + } + pmem[id].allocator.bitmap.bitmap_allocs = new_bitmap_allocs; + pmem[id].allocator.bitmap.bitm_alloc = temp; + + for (j = i; j < new_bitmap_allocs; j++) { + pmem[id].allocator.bitmap.bitm_alloc[j].bit = -1; + pmem[id].allocator.bitmap.bitm_alloc[i].quanta = 0; + } +/*HTC_START*/ + if (misc_msg_pmem_qcom) { + pr_info("[PME][%s] increased # of allocated regions to %d for \n", + pmem[id].name, pmem[id].allocator.bitmap.bitmap_allocs); + } + } + if (misc_msg_pmem_qcom) + pr_info("[PME][%s] bitnum %d, bitm_alloc index %d\n", pmem[id].name, bitnum, i); +/*HTC_END*/ + pmem[id].allocator.bitmap.bitmap_free -= quanta_needed; + pmem[id].allocator.bitmap.bitm_alloc[i].bit = bitnum; + pmem[id].allocator.bitmap.bitm_alloc[i].quanta = quanta_needed; +leave: + if (-1 == bitnum) { + pr_err("[PME][%s] error: pmem_allocator_bitmap failed\n", pmem[id].name); + for (i = 0; i < pmem[id].allocator.bitmap.bitmap_allocs; i++) { + if (pmem[id].allocator.bitmap.bitm_alloc[i].bit != -1) { + /*HTC_START*/ + if (misc_msg_pmem_qcom) { + pr_info("[PME][%s] bitm_alloc[%d].bit: %u bitm_alloc[%d].quanta: %u\n", + pmem[id].name, + i, + pmem[id].allocator.bitmap.bitm_alloc[i].bit, + i, + pmem[id].allocator.bitmap.bitm_alloc[i].quanta + ); + } + /*HTC_END*/ + } + } + } + return bitnum; +} + +static pgprot_t pmem_phys_mem_access_prot(struct file *file, pgprot_t vma_prot) +{ + int id = get_id(file); +#ifdef pgprot_writecombine + if (pmem[id].cached == 0 || file->f_flags & O_SYNC) + /* on ARMv6 and ARMv7 this expands to Normal Noncached */ + return pgprot_writecombine(vma_prot); +#endif +#ifdef pgprot_ext_buffered + else if (pmem[id].buffered) + return pgprot_ext_buffered(vma_prot); +#endif + return vma_prot; +} + +static unsigned long pmem_start_addr_all_or_nothing(int id, + struct pmem_data *data) +{ + return PMEM_START_ADDR(id, 0); +} + +static unsigned long pmem_start_addr_buddy_bestfit(int id, + struct pmem_data *data) +{ + return PMEM_START_ADDR(id, data->index); +} + +static unsigned long pmem_start_addr_bitmap(int id, struct pmem_data *data) +{ + return data->index * pmem[id].quantum + pmem[id].base; +} + +static void *pmem_start_vaddr(int id, struct pmem_data *data) +{ + return pmem[id].start_addr(id, data) - pmem[id].base + pmem[id].vbase; +} + +static unsigned long pmem_len_all_or_nothing(int id, struct pmem_data *data) +{ + return data->index; +} + +static unsigned long pmem_len_buddy_bestfit(int id, struct pmem_data *data) +{ + return PMEM_BUDDY_LEN(id, data->index); +} + +static unsigned long pmem_len_bitmap(int id, struct pmem_data *data) +{ + int i; + unsigned long ret = 0; + + mutex_lock(&pmem[id].arena_mutex); + + for (i = 0; i < pmem[id].allocator.bitmap.bitmap_allocs; i++) + if (pmem[id].allocator.bitmap.bitm_alloc[i].bit == + data->index) { + ret = pmem[id].allocator.bitmap.bitm_alloc[i].quanta * + pmem[id].quantum; + break; + } + + mutex_unlock(&pmem[id].arena_mutex); +#if PMEM_DEBUG + if (i >= pmem[id].allocator.bitmap.bitmap_allocs) + pr_alert("pmem: %s: can't find bitnum %d in " + "alloc'd array!\n", __func__, data->index); +#endif + return ret; +} + +static int pmem_map_garbage(int id, struct vm_area_struct *vma, + struct pmem_data *data, unsigned long offset, + unsigned long len) +{ + int i, garbage_pages = len >> PAGE_SHIFT; vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP | VM_SHARED | VM_WRITE; for (i = 0; i < garbage_pages; i++) { @@ -509,18 +1374,25 @@ static int pmem_map_pfn_range(int id, struct vm_area_struct *vma, struct pmem_data *data, unsigned long offset, unsigned long len) { + int ret; DLOG("map offset %lx len %lx\n", offset, len); BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_start)); BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_end)); BUG_ON(!PMEM_IS_PAGE_ALIGNED(len)); BUG_ON(!PMEM_IS_PAGE_ALIGNED(offset)); - if (io_remap_pfn_range(vma, vma->vm_start + offset, - (pmem_start_addr(id, data) + offset) >> PAGE_SHIFT, - len, vma->vm_page_prot)) { - return -EAGAIN; + ret = io_remap_pfn_range(vma, vma->vm_start + offset, + (pmem[id].start_addr(id, data) + offset) >> PAGE_SHIFT, + len, vma->vm_page_prot); + if (ret) { +#if PMEM_DEBUG + pr_alert("pmem: %s: io_remap_pfn_range fails with " + "return value: %d!\n", __func__, ret); +#endif + + ret = -EAGAIN; } - return 0; + return ret; } static int pmem_remap_pfn_range(int id, struct vm_area_struct *vma, @@ -538,13 +1410,21 @@ static void pmem_vma_open(struct vm_area_struct *vma) struct file *file = vma->vm_file; struct pmem_data *data = file->private_data; int id = get_id(file); + +#if PMEM_DEBUG_MSGS + char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1]; +#endif + DLOG("Dev %s(id: %d) pid %u(%s) ppid %u file %p count %ld\n", + get_name(file), id, current->pid, + get_task_comm(currtask_name, current), + current->parent->pid, file, file_count(file)); /* this should never be called as we don't support copying pmem * ranges via fork */ + down_read(&data->sem); BUG_ON(!has_allocation(file)); - down_write(&data->sem); /* remap the garbage pages, forkers don't get access to the data */ pmem_unmap_pfn_range(id, vma, data, 0, vma->vm_start - vma->vm_end); - up_write(&data->sem); + up_read(&data->sem); } static void pmem_vma_close(struct vm_area_struct *vma) @@ -552,15 +1432,29 @@ static void pmem_vma_close(struct vm_area_struct *vma) struct file *file = vma->vm_file; struct pmem_data *data = file->private_data; - DLOG("current %u ppid %u file %p count %d\n", current->pid, - current->parent->pid, file, file_count(file)); - if (unlikely(!is_pmem_file(file) || !has_allocation(file))) { - printk(KERN_WARNING "pmem: something is very wrong, you are " +#if PMEM_DEBUG_MSGS + char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1]; +#endif + DLOG("Dev %s(id: %d) pid %u(%s) ppid %u file %p count %ld\n", + get_name(file), get_id(file), current->pid, + get_task_comm(currtask_name, current), + current->parent->pid, file, file_count(file)); + + if (unlikely(!is_pmem_file(file))) { + pr_warning("pmem: something is very wrong, you are " "closing a vm backing an allocation that doesn't " "exist!\n"); return; } + down_write(&data->sem); + if (unlikely(!has_allocation(file))) { + up_write(&data->sem); + pr_warning("pmem: something is very wrong, you are " + "closing a vm backing an allocation that doesn't " + "exist!\n"); + return; + } if (data->vma == vma) { data->vma = NULL; if ((data->flags & PMEM_FLAGS_CONNECTED) && @@ -578,27 +1472,33 @@ static struct vm_operations_struct vm_ops = { static int pmem_mmap(struct file *file, struct vm_area_struct *vma) { - struct pmem_data *data; + struct pmem_data *data = file->private_data; int index; unsigned long vma_size = vma->vm_end - vma->vm_start; int ret = 0, id = get_id(file); +#if PMEM_DEBUG_MSGS + char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1]; +#endif + DLOG("pid %u(%s) mmap vma_size %lu on dev %s(id: %d)\n", current->pid, + get_task_comm(currtask_name, current), vma_size, + get_name(file), id); if (vma->vm_pgoff || !PMEM_IS_PAGE_ALIGNED(vma_size)) { #if PMEM_DEBUG - printk(KERN_ERR "pmem: mmaps must be at offset zero, aligned" + pr_err("pmem: mmaps must be at offset zero, aligned" " and a multiple of pages_size.\n"); #endif return -EINVAL; } - data = (struct pmem_data *)file->private_data; down_write(&data->sem); /* check this file isn't already mmaped, for submaps check this file * has never been mmaped */ - if ((data->flags & PMEM_FLAGS_SUBMAP) || + if ((data->flags & PMEM_FLAGS_MASTERMAP) || + (data->flags & PMEM_FLAGS_SUBMAP) || (data->flags & PMEM_FLAGS_UNSUBMAP)) { #if PMEM_DEBUG - printk(KERN_ERR "pmem: you can only mmap a pmem file once, " + pr_err("pmem: you can only mmap a pmem file once, " "this file is already mmaped. %x\n", data->flags); #endif ret = -EINVAL; @@ -606,36 +1506,44 @@ static int pmem_mmap(struct file *file, struct vm_area_struct *vma) } /* if file->private_data == unalloced, alloc*/ if (data && data->index == -1) { - down_write(&pmem[id].bitmap_sem); - index = pmem_allocate(id, vma->vm_end - vma->vm_start); - up_write(&pmem[id].bitmap_sem); + mutex_lock(&pmem[id].arena_mutex); + index = pmem[id].allocate(id, + vma->vm_end - vma->vm_start, + SZ_4K); + mutex_unlock(&pmem[id].arena_mutex); data->index = index; + if (data->index < 0) { + pr_err("pmem: mmap unable to allocate memory" + "on %s\n", get_name(file)); + } } + /* either no space was available or an error occured */ if (!has_allocation(file)) { - ret = -EINVAL; - printk("pmem: could not find allocation for map.\n"); + ret = -ENOMEM; + pr_err("pmem: could not find allocation for map.\n"); goto error; } - if (pmem_len(id, data) < vma_size) { + if (pmem[id].len(id, data) < vma_size) { #if PMEM_DEBUG - printk(KERN_WARNING "pmem: mmap size [%lu] does not match" - "size of backing region [%lu].\n", vma_size, - pmem_len(id, data)); + pr_err("pmem: mmap size [%lu] does not match" + " size of backing region [%lu].\n", vma_size, + pmem[id].len(id, data)); #endif ret = -EINVAL; goto error; } - vma->vm_pgoff = pmem_start_addr(id, data) >> PAGE_SHIFT; - vma->vm_page_prot = pmem_access_prot(file, vma->vm_page_prot); + vma->vm_pgoff = pmem[id].start_addr(id, data) >> PAGE_SHIFT; + + vma->vm_page_prot = pmem_phys_mem_access_prot(file, vma->vm_page_prot); if (data->flags & PMEM_FLAGS_CONNECTED) { struct pmem_region_node *region_node; struct list_head *elt; if (pmem_map_garbage(id, vma, data, 0, vma_size)) { - printk("pmem: mmap failed in kernel!\n"); + pr_alert("pmem: mmap failed in kernel!\n"); ret = -EAGAIN; goto error; } @@ -663,7 +1571,7 @@ static int pmem_mmap(struct file *file, struct vm_area_struct *vma) current->pid); } else { if (pmem_map_pfn_range(id, vma, data, 0, vma_size)) { - printk(KERN_INFO "pmem: mmap failed in kernel!\n"); + pr_err("pmem: mmap failed in kernel!\n"); ret = -EAGAIN; goto error; } @@ -681,103 +1589,155 @@ static int pmem_mmap(struct file *file, struct vm_area_struct *vma) int get_pmem_user_addr(struct file *file, unsigned long *start, unsigned long *len) { - struct pmem_data *data; - if (!is_pmem_file(file) || !has_allocation(file)) { + int ret = -1; + + if (is_pmem_file(file)) { + struct pmem_data *data = file->private_data; + + down_read(&data->sem); + if (has_allocation(file)) { + if (data->vma) { + *start = data->vma->vm_start; + *len = data->vma->vm_end - data->vma->vm_start; + } else { + *start = *len = 0; #if PMEM_DEBUG - printk(KERN_INFO "pmem: requested pmem data from invalid" - "file.\n"); + pr_err("pmem: %s: no vma present.\n", + __func__); #endif - return -1; - } - data = (struct pmem_data *)file->private_data; - down_read(&data->sem); - if (data->vma) { - *start = data->vma->vm_start; - *len = data->vma->vm_end - data->vma->vm_start; - } else { - *start = 0; - *len = 0; + } + ret = 0; + } + up_read(&data->sem); } - up_read(&data->sem); - return 0; + +#if PMEM_DEBUG + if (ret) + pr_err("pmem: %s: requested pmem data from invalid" + "file.\n", __func__); +#endif + return ret; } int get_pmem_addr(struct file *file, unsigned long *start, unsigned long *vstart, unsigned long *len) { - struct pmem_data *data; - int id; + int ret = -1; - if (!is_pmem_file(file) || !has_allocation(file)) { - return -1; - } + if (is_pmem_file(file)) { + struct pmem_data *data = file->private_data; - data = (struct pmem_data *)file->private_data; - if (data->index == -1) { -#if PMEM_DEBUG - printk(KERN_INFO "pmem: requested pmem data from file with no " - "allocation.\n"); - return -1; -#endif - } - id = get_id(file); + down_read(&data->sem); + if (has_allocation(file)) { + int id = get_id(file); - down_read(&data->sem); - *start = pmem_start_addr(id, data); - *len = pmem_len(id, data); - *vstart = (unsigned long)pmem_start_vaddr(id, data); - up_read(&data->sem); + *start = pmem[id].start_addr(id, data); + *len = pmem[id].len(id, data); + *vstart = (unsigned long) + pmem_start_vaddr(id, data); + up_read(&data->sem); #if PMEM_DEBUG - down_write(&data->sem); - data->ref++; - up_write(&data->sem); + down_write(&data->sem); + data->ref++; + up_write(&data->sem); #endif - return 0; + DLOG("returning start %#lx len %lu " + "vstart %#lx\n", + *start, *len, *vstart); + ret = 0; + } else { + up_read(&data->sem); + } + } + return ret; } -int get_pmem_file(int fd, unsigned long *start, unsigned long *vstart, +int get_pmem_file(unsigned int fd, unsigned long *start, unsigned long *vstart, unsigned long *len, struct file **filp) { - struct file *file; + int ret = -1; + struct file *file = fget(fd); - file = fget(fd); if (unlikely(file == NULL)) { - printk(KERN_INFO "pmem: requested data from file descriptor " - "that doesn't exist."); - return -1; + pr_err("pmem: %s: requested data from file " + "descriptor that doesn't exist.\n", __func__); + } else { +#if PMEM_DEBUG_MSGS + char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1]; +#endif + DLOG("filp %p rdev %d pid %u(%s) file %p(%ld)" + " dev %s(id: %d)\n", filp, + file->f_dentry->d_inode->i_rdev, + current->pid, get_task_comm(currtask_name, current), + file, file_count(file), get_name(file), get_id(file)); + + if (!get_pmem_addr(file, start, vstart, len)) { + if (filp) + *filp = file; + ret = 0; + } else { + fput(file); + } } + return ret; +} +EXPORT_SYMBOL(get_pmem_file); - if (get_pmem_addr(file, start, vstart, len)) - goto end; - - if (filp) - *filp = file; - return 0; -end: - fput(file); - return -1; +int get_pmem_fd(int fd, unsigned long *start, unsigned long *len) +{ + unsigned long vstart; + return get_pmem_file(fd, start, &vstart, len, NULL); } +EXPORT_SYMBOL(get_pmem_fd); void put_pmem_file(struct file *file) { - struct pmem_data *data; - int id; - - if (!is_pmem_file(file)) - return; - id = get_id(file); - data = (struct pmem_data *)file->private_data; +#if PMEM_DEBUG_MSGS + char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1]; +#endif + DLOG("rdev %d pid %u(%s) file %p(%ld)" " dev %s(id: %d)\n", + file->f_dentry->d_inode->i_rdev, current->pid, + get_task_comm(currtask_name, current), file, + file_count(file), get_name(file), get_id(file)); + if (is_pmem_file(file)) { #if PMEM_DEBUG - down_write(&data->sem); - if (data->ref == 0) { - printk("pmem: pmem_put > pmem_get %s (pid %d)\n", - pmem[id].dev.name, data->pid); - BUG(); - } - data->ref--; - up_write(&data->sem); + struct pmem_data *data = file->private_data; + + down_write(&data->sem); + if (!data->ref--) { + data->ref++; + pr_alert("pmem: pmem_put > pmem_get %s " + "(pid %d)\n", + pmem[get_id(file)].dev.name, data->pid); + BUG(); + } + up_write(&data->sem); #endif - fput(file); + fput(file); + } +} +EXPORT_SYMBOL(put_pmem_file); + +void put_pmem_fd(int fd) +{ + int put_needed; + struct file *file = fget_light(fd, &put_needed); + + if (file) { + put_pmem_file(file); + fput_light(file, put_needed); + } +} + +void flush_pmem_fd(int fd, unsigned long offset, unsigned long len) +{ + int fput_needed; + struct file *file = fget_light(fd, &fput_needed); + + if (file) { + flush_pmem_file(file, offset, len); + fput_light(file, fput_needed); + } } void flush_pmem_file(struct file *file, unsigned long offset, unsigned long len) @@ -788,21 +1748,35 @@ void flush_pmem_file(struct file *file, unsigned long offset, unsigned long len) struct pmem_region_node *region_node; struct list_head *elt; void *flush_start, *flush_end; - - if (!is_pmem_file(file) || !has_allocation(file)) { +#ifdef CONFIG_OUTER_CACHE + unsigned long phy_start, phy_end; +#endif + if (!is_pmem_file(file)) return; - } id = get_id(file); - data = (struct pmem_data *)file->private_data; - if (!pmem[id].cached || file->f_flags & O_SYNC) + if (!pmem[id].cached) return; + /* is_pmem_file fails if !file */ + data = file->private_data; + down_read(&data->sem); + if (!has_allocation(file)) + goto end; + vaddr = pmem_start_vaddr(id, data); /* if this isn't a submmapped file, flush the whole thing */ if (unlikely(!(data->flags & PMEM_FLAGS_CONNECTED))) { - dmac_flush_range(vaddr, vaddr + pmem_len(id, data)); + dmac_flush_range(vaddr, vaddr + pmem[id].len(id, data)); +#ifdef CONFIG_OUTER_CACHE + phy_start = (unsigned long)vaddr - + (unsigned long)pmem[id].vbase + pmem[id].base; + + phy_end = phy_start + pmem[id].len(id, data); + + outer_flush_range(phy_start, phy_end); +#endif goto end; } /* otherwise, flush the region of the file we are drawing */ @@ -814,6 +1788,15 @@ void flush_pmem_file(struct file *file, unsigned long offset, unsigned long len) flush_start = vaddr + region_node->region.offset; flush_end = flush_start + region_node->region.len; dmac_flush_range(flush_start, flush_end); +#ifdef CONFIG_OUTER_CACHE + + phy_start = (unsigned long)flush_start - + (unsigned long)pmem[id].vbase + pmem[id].base; + + phy_end = phy_start + region_node->region.len; + + outer_flush_range(phy_start, phy_end); +#endif break; } } @@ -821,45 +1804,278 @@ void flush_pmem_file(struct file *file, unsigned long offset, unsigned long len) up_read(&data->sem); } +int pmem_cache_maint(struct file *file, unsigned int cmd, + struct pmem_addr *pmem_addr) +{ + struct pmem_data *data; + int id; + unsigned long vaddr, paddr, length, offset, + pmem_len, pmem_start_addr; + + /* Called from kernel-space so file may be NULL */ + if (!file) + return -EBADF; + + data = file->private_data; + id = get_id(file); + + if (!pmem[id].cached) + return 0; + + offset = pmem_addr->offset; + length = pmem_addr->length; + + down_read(&data->sem); + if (!has_allocation(file)) { + up_read(&data->sem); + return -EINVAL; + } + pmem_len = pmem[id].len(id, data); + pmem_start_addr = pmem[id].start_addr(id, data); + up_read(&data->sem); + + if (offset + length > pmem_len) + return -EINVAL; + + vaddr = pmem_addr->vaddr; + paddr = pmem_start_addr + offset; + + DLOG("pmem cache maint on dev %s(id: %d)" + "(vaddr %lx paddr %lx len %lu bytes)\n", + get_name(file), id, vaddr, paddr, length); + if (cmd == PMEM_CLEAN_INV_CACHES) + clean_and_invalidate_caches(vaddr, + length, paddr); + else if (cmd == PMEM_CLEAN_CACHES) + clean_caches(vaddr, length, paddr); + else if (cmd == PMEM_INV_CACHES) + invalidate_caches(vaddr, length, paddr); + + return 0; +} +EXPORT_SYMBOL(pmem_cache_maint); + +int32_t pmem_kalloc(const size_t size, const uint32_t flags) +{ + int info_id, i, memtype, fallback = 0; + unsigned int align; + int32_t index = -1; + + switch (flags & PMEM_ALIGNMENT_MASK) { + case PMEM_ALIGNMENT_4K: + align = SZ_4K; + break; + case PMEM_ALIGNMENT_1M: + align = SZ_1M; + break; + default: + pr_alert("pmem: %s: Invalid alignment %#x\n", + __func__, (flags & PMEM_ALIGNMENT_MASK)); + return -EINVAL; + } + + memtype = flags & PMEM_MEMTYPE_MASK; +retry_memalloc: + info_id = -1; + for (i = 0; i < ARRAY_SIZE(kapi_memtypes); i++) + if (kapi_memtypes[i].memtype == memtype) { + info_id = kapi_memtypes[i].info_id; + break; + } + if (info_id < 0) { + pr_alert("pmem: %s: Kernel %#x memory arena is not " + "initialized. Check board file!\n", + __func__, (flags & PMEM_MEMTYPE_MASK)); + return -EINVAL; + } + + if (!pmem[info_id].allocate) { + pr_alert("pmem: %s: Attempt to allocate size %u, alignment %#x" + " from non-existent PMEM kernel region %d. " + "Driver/board setup is faulty!", + __func__, size, (flags & PMEM_ALIGNMENT_MASK), + info_id); + return -ENOMEM; + } + +#if PMEM_DEBUG + if (align != SZ_4K && + (pmem[info_id].allocator_type == + PMEM_ALLOCATORTYPE_ALLORNOTHING || + pmem[info_id].allocator_type == + PMEM_ALLOCATORTYPE_BUDDYBESTFIT)) + pr_warning("pmem: %s: alignment other than on 4K " + "pages not supported with %s allocator for PMEM " + "memory region '%s'. Memory will be aligned to 4K " + "boundary. Check your board file or allocation " + "invocation.\n", __func__, + (pmem[info_id].allocator_type == + PMEM_ALLOCATORTYPE_ALLORNOTHING ? + "'All Or Nothing'" + : + "'Buddy / Best Fit'"), + pmem[info_id].dev.name); +#endif + + mutex_lock(&pmem[info_id].arena_mutex); + index = pmem[info_id].allocate(info_id, size, align); + mutex_unlock(&pmem[info_id].arena_mutex); + + if (index < 0 && + !fallback && + kapi_memtypes[i].fallback_memtype != PMEM_INVALID_MEMTYPE) { + fallback = 1; + memtype = kapi_memtypes[i].fallback_memtype; + goto retry_memalloc; + } + + return index >= 0 ? + index * pmem[info_id].quantum + pmem[info_id].base : -ENOMEM; +} +EXPORT_SYMBOL(pmem_kalloc); + +static int pmem_kapi_free_index_allornothing(const int32_t physaddr, int id) +{ + return physaddr == pmem[id].base ? 0 : -1; +} + +static int pmem_kapi_free_index_buddybestfit(const int32_t physaddr, int id) +{ + return (physaddr >= pmem[id].base && + physaddr < (pmem[id].base + pmem[id].size && + !(physaddr % pmem[id].quantum))) ? + (physaddr - pmem[id].base) / pmem[id].quantum : -1; +} + +static int pmem_kapi_free_index_bitmap(const int32_t physaddr, int id) +{ + return (physaddr >= pmem[id].base && + physaddr < (pmem[id].base + pmem[id].size)) ? + bit_from_paddr(id, physaddr) : -1; +} + +int pmem_kfree(const int32_t physaddr) +{ + int i; + for (i = 0; i < ARRAY_SIZE(kapi_memtypes); i++) { + int index; + int id = kapi_memtypes[i].info_id; + + if (id < 0) + continue; + + if (!pmem[id].allocate) { +#if PMEM_DEBUG + pr_alert("pmem: %s: " + "Attempt to free physical address %#x " + "from unregistered PMEM kernel region" + " %d. Driver/board setup is faulty!", + __func__, physaddr, id); +#endif + return -EINVAL; + } + + index = pmem[id].kapi_free_index(physaddr, id); + if (index >= 0) + return pmem[id].free(id, index) ? -EINVAL : 0; + } +#if PMEM_DEBUG + pr_alert("pmem: %s: Failed to free physaddr %#x, does not " + "seem be value returned by pmem_kalloc()!", + __func__, physaddr); +#endif + return -EINVAL; +} +EXPORT_SYMBOL(pmem_kfree); + static int pmem_connect(unsigned long connect, struct file *file) { - struct pmem_data *data = (struct pmem_data *)file->private_data; - struct pmem_data *src_data; - struct file *src_file; int ret = 0, put_needed; + struct file *src_file; + + if (!file) { + pr_err("pmem: %s: NULL file pointer passed in, " + "bailing out!\n", __func__); + ret = -EINVAL; + goto leave; + } - down_write(&data->sem); - /* retrieve the src file and check it is a pmem file with an alloc */ src_file = fget_light(connect, &put_needed); - DLOG("connect %p to %p\n", file, src_file); + if (!src_file) { - printk("pmem: src file not found!\n"); - ret = -EINVAL; - goto err_no_file; + pr_err("pmem: %s: src file not found!\n", __func__); + ret = -EBADF; + goto leave; } - if (unlikely(!is_pmem_file(src_file) || !has_allocation(src_file))) { - printk(KERN_INFO "pmem: src file is not a pmem file or has no " - "alloc!\n"); + + if (src_file == file) { /* degenerative case, operator error */ + pr_err("pmem: %s: src_file and passed in file are " + "the same; refusing to connect to self!\n", __func__); ret = -EINVAL; - goto err_bad_file; + goto put_src_file; } - src_data = (struct pmem_data *)src_file->private_data; - if (has_allocation(file) && (data->index != src_data->index)) { - printk("pmem: file is already mapped but doesn't match this" - " src_file!\n"); + if (unlikely(!is_pmem_file(src_file))) { + pr_err("pmem: %s: src file is not a pmem file!\n", + __func__); ret = -EINVAL; - goto err_bad_file; - } - data->index = src_data->index; - data->flags |= PMEM_FLAGS_CONNECTED; - data->master_fd = connect; - data->master_file = src_file; + goto put_src_file; + } else { + struct pmem_data *src_data = src_file->private_data; + + if (!src_data) { + pr_err("pmem: %s: src file pointer has no" + "private data, bailing out!\n", __func__); + ret = -EINVAL; + goto put_src_file; + } + + down_read(&src_data->sem); + + if (unlikely(!has_allocation(src_file))) { + up_read(&src_data->sem); + pr_err("pmem: %s: src file has no allocation!\n", + __func__); + ret = -EINVAL; + } else { + struct pmem_data *data; + int src_index = src_data->index; + + up_read(&src_data->sem); + + data = file->private_data; + if (!data) { + pr_err("pmem: %s: passed in file " + "pointer has no private data, bailing" + " out!\n", __func__); + ret = -EINVAL; + goto put_src_file; + } + + down_write(&data->sem); + if (has_allocation(file) && + (data->index != src_index)) { + up_write(&data->sem); + + pr_err("pmem: %s: file is already " + "mapped but doesn't match this " + "src_file!\n", __func__); + ret = -EINVAL; + } else { + data->index = src_index; + data->flags |= PMEM_FLAGS_CONNECTED; + data->master_fd = connect; + data->master_file = src_file; + + up_write(&data->sem); -err_bad_file: + DLOG("connect %p to %p\n", file, src_file); + } + } + } +put_src_file: fput_light(src_file, put_needed); -err_no_file: - up_write(&data->sem); +leave: return ret; } @@ -878,16 +2094,23 @@ static int pmem_lock_data_and_mm(struct file *file, struct pmem_data *data, { int ret = 0; struct mm_struct *mm = NULL; +#if PMEM_DEBUG_MSGS + char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1]; +#endif + DLOG("pid %u(%s) file %p(%ld)\n", + current->pid, get_task_comm(currtask_name, current), + file, file_count(file)); + *locked_mm = NULL; lock_mm: down_read(&data->sem); if (PMEM_IS_SUBMAP(data)) { mm = get_task_mm(data->task); if (!mm) { + up_read(&data->sem); #if PMEM_DEBUG - printk("pmem: can't remap task is gone!\n"); + pr_alert("pmem: can't remap - task is gone!\n"); #endif - up_read(&data->sem); return -1; } } @@ -902,7 +2125,7 @@ static int pmem_lock_data_and_mm(struct file *file, struct pmem_data *data, * once */ if (PMEM_IS_SUBMAP(data) && !mm) { pmem_unlock_data_and_mm(data, mm); - up_write(&data->sem); + DLOG("mapping contention, repeating mmap op\n"); goto lock_mm; } /* now check that vma.mm is still there, it could have been @@ -916,6 +2139,9 @@ static int pmem_lock_data_and_mm(struct file *file, struct pmem_data *data, data->flags &= ~(PMEM_FLAGS_SUBMAP); } pmem_unlock_data_and_mm(data, mm); +#if PMEM_DEBUG + pr_alert("pmem: vma.mm went away!\n"); +#endif return -1; } *locked_mm = mm; @@ -930,14 +2156,28 @@ int pmem_remap(struct pmem_region *region, struct file *file, struct mm_struct *mm = NULL; struct list_head *elt, *elt2; int id = get_id(file); - struct pmem_data *data = (struct pmem_data *)file->private_data; + struct pmem_data *data; + + DLOG("operation %#x, region offset %ld, region len %ld\n", + operation, region->offset, region->len); + + if (!is_pmem_file(file)) { +#if PMEM_DEBUG + pr_err("pmem: remap request for non-pmem file descriptor\n"); +#endif + return -EINVAL; + } + + /* is_pmem_file fails if !file */ + data = file->private_data; /* pmem region must be aligned on a page boundry */ if (unlikely(!PMEM_IS_PAGE_ALIGNED(region->offset) || !PMEM_IS_PAGE_ALIGNED(region->len))) { #if PMEM_DEBUG - printk("pmem: request for unaligned pmem suballocation " - "%lx %lx\n", region->offset, region->len); + pr_err("pmem: request for unaligned pmem" + "suballocation %lx %lx\n", + region->offset, region->len); #endif return -EINVAL; } @@ -955,18 +2195,18 @@ int pmem_remap(struct pmem_region *region, struct file *file, * that back in it */ if (!is_master_owner(file)) { #if PMEM_DEBUG - printk("pmem: remap requested from non-master process\n"); + pr_err("pmem: remap requested from non-master process\n"); #endif ret = -EINVAL; goto err; } /* check that the requested range is within the src allocation */ - if (unlikely((region->offset > pmem_len(id, data)) || - (region->len > pmem_len(id, data)) || - (region->offset + region->len > pmem_len(id, data)))) { + if (unlikely((region->offset > pmem[id].len(id, data)) || + (region->len > pmem[id].len(id, data)) || + (region->offset + region->len > pmem[id].len(id, data)))) { #if PMEM_DEBUG - printk(KERN_INFO "pmem: suballoc doesn't fit in src_file!\n"); + pr_err("pmem: suballoc doesn't fit in src_file!\n"); #endif ret = -EINVAL; goto err; @@ -978,7 +2218,7 @@ int pmem_remap(struct pmem_region *region, struct file *file, if (!region_node) { ret = -ENOMEM; #if PMEM_DEBUG - printk(KERN_INFO "No space to allocate metadata!"); + pr_alert("pmem: No space to allocate remap metadata!"); #endif goto err; } @@ -999,8 +2239,8 @@ int pmem_remap(struct pmem_region *region, struct file *file, } if (!found) { #if PMEM_DEBUG - printk("pmem: Unmap region does not map any mapped " - "region!"); + pr_err("pmem: Unmap region does not map any" + " mapped region!"); #endif ret = -EINVAL; goto err; @@ -1010,10 +2250,10 @@ int pmem_remap(struct pmem_region *region, struct file *file, if (data->vma && PMEM_IS_SUBMAP(data)) { if (operation == PMEM_MAP) ret = pmem_remap_pfn_range(id, data->vma, data, - region->offset, region->len); + region->offset, region->len); else if (operation == PMEM_UNMAP) ret = pmem_unmap_pfn_range(id, data->vma, data, - region->offset, region->len); + region->offset, region->len); } err: @@ -1054,63 +2294,83 @@ static void pmem_revoke(struct file *file, struct pmem_data *data) static void pmem_get_size(struct pmem_region *region, struct file *file) { - struct pmem_data *data = (struct pmem_data *)file->private_data; + /* called via ioctl file op, so file guaranteed to be not NULL */ + struct pmem_data *data = file->private_data; int id = get_id(file); + down_read(&data->sem); if (!has_allocation(file)) { region->offset = 0; region->len = 0; - return; } else { - region->offset = pmem_start_addr(id, data); - region->len = pmem_len(id, data); + region->offset = pmem[id].start_addr(id, data); + region->len = pmem[id].len(id, data); } - DLOG("offset %lx len %lx\n", region->offset, region->len); + up_read(&data->sem); + DLOG("offset 0x%lx len 0x%lx\n", region->offset, region->len); } static long pmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct pmem_data *data; + /* called from user space as file op, so file guaranteed to be not + * NULL + */ + struct pmem_data *data = file->private_data; int id = get_id(file); +#if PMEM_DEBUG_MSGS + char currtask_name[ + FIELD_SIZEOF(struct task_struct, comm) + 1]; +#endif + + DLOG("pid %u(%s) file %p(%ld) cmd %#x, dev %s(id: %d)\n", + current->pid, get_task_comm(currtask_name, current), + file, file_count(file), cmd, get_name(file), id); switch (cmd) { case PMEM_GET_PHYS: { struct pmem_region region; + DLOG("get_phys\n"); + down_read(&data->sem); if (!has_allocation(file)) { region.offset = 0; region.len = 0; } else { - data = (struct pmem_data *)file->private_data; - region.offset = pmem_start_addr(id, data); - region.len = pmem_len(id, data); + region.offset = pmem[id].start_addr(id, data); + region.len = pmem[id].len(id, data); } - printk(KERN_INFO "pmem: request for physical address of pmem region " - "from process %d.\n", current->pid); + up_read(&data->sem); + if (copy_to_user((void __user *)arg, ®ion, sizeof(struct pmem_region))) return -EFAULT; + + DLOG("pmem: successful request for " + "physical address of pmem region id %d, " + "offset 0x%lx, len 0x%lx\n", + id, region.offset, region.len); + break; } case PMEM_MAP: { struct pmem_region region; + DLOG("map\n"); if (copy_from_user(®ion, (void __user *)arg, sizeof(struct pmem_region))) return -EFAULT; - data = (struct pmem_data *)file->private_data; return pmem_remap(®ion, file, PMEM_MAP); } break; case PMEM_UNMAP: { struct pmem_region region; + DLOG("unmap\n"); if (copy_from_user(®ion, (void __user *)arg, sizeof(struct pmem_region))) return -EFAULT; - data = (struct pmem_data *)file->private_data; return pmem_remap(®ion, file, PMEM_UNMAP); break; } @@ -1136,169 +2396,560 @@ static long pmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return -EFAULT; break; } + case PMEM_GET_FREE_SPACE: + { + struct pmem_freespace fs; + DLOG("get freespace on %s(id: %d)\n", + get_name(file), id); + + mutex_lock(&pmem[id].arena_mutex); + pmem[id].free_space(id, &fs); + mutex_unlock(&pmem[id].arena_mutex); + + DLOG("%s(id: %d) total free %lu, largest %lu\n", + get_name(file), id, fs.total, fs.largest); + + if (copy_to_user((void __user *)arg, &fs, + sizeof(struct pmem_freespace))) + return -EFAULT; + break; + } + case PMEM_ALLOCATE: { - if (has_allocation(file)) + int ret = 0; + DLOG("allocate, id %d\n", id); + down_write(&data->sem); + if (has_allocation(file)) { + pr_err("pmem: Existing allocation found on " + "this file descrpitor\n"); + up_write(&data->sem); return -EINVAL; - data = (struct pmem_data *)file->private_data; - data->index = pmem_allocate(id, arg); - break; + } + + mutex_lock(&pmem[id].arena_mutex); + data->index = pmem[id].allocate(id, + arg, + SZ_4K); + mutex_unlock(&pmem[id].arena_mutex); + ret = data->index == -1 ? -ENOMEM : + data->index; + up_write(&data->sem); + return ret; + } + case PMEM_ALLOCATE_ALIGNED: + { + struct pmem_allocation alloc; + int ret = 0; + + if (copy_from_user(&alloc, (void __user *)arg, + sizeof(struct pmem_allocation))) + return -EFAULT; + DLOG("allocate id align %d %u\n", id, alloc.align); + down_write(&data->sem); + if (has_allocation(file)) { + pr_err("pmem: Existing allocation found on " + "this file descrpitor\n"); + up_write(&data->sem); + return -EINVAL; + } + + if (alloc.align & (alloc.align - 1)) { + pr_err("pmem: Alignment is not a power of 2\n"); + return -EINVAL; + } + + if (alloc.align != SZ_4K && + (pmem[id].allocator_type != + PMEM_ALLOCATORTYPE_BITMAP)) { + pr_err("pmem: Non 4k alignment requires bitmap" + " allocator on %s\n", pmem[id].name); + return -EINVAL; + } + + if (alloc.align > SZ_1M || + alloc.align < SZ_4K) { + pr_err("pmem: Invalid Alignment (%u) " + "specified\n", alloc.align); + return -EINVAL; + } + + mutex_lock(&pmem[id].arena_mutex); + data->index = pmem[id].allocate(id, + alloc.size, + alloc.align); + mutex_unlock(&pmem[id].arena_mutex); + ret = data->index == -1 ? -ENOMEM : + data->index; + up_write(&data->sem); + return ret; } case PMEM_CONNECT: DLOG("connect\n"); return pmem_connect(arg, file); - break; - case PMEM_CACHE_FLUSH: + case PMEM_CLEAN_INV_CACHES: + case PMEM_CLEAN_CACHES: + case PMEM_INV_CACHES: { - struct pmem_region region; - DLOG("flush\n"); - if (copy_from_user(®ion, (void __user *)arg, - sizeof(struct pmem_region))) + struct pmem_addr pmem_addr; + + if (copy_from_user(&pmem_addr, (void __user *)arg, + sizeof(struct pmem_addr))) return -EFAULT; - flush_pmem_file(file, region.offset, region.len); - break; + + return pmem_cache_maint(file, cmd, &pmem_addr); } default: if (pmem[id].ioctl) return pmem[id].ioctl(file, cmd, arg); + + DLOG("ioctl invalid (%#x)\n", cmd); return -EINVAL; } return 0; } -#if PMEM_DEBUG -static ssize_t debug_open(struct inode *inode, struct file *file) +static void ioremap_pmem(int id) +{ + if (pmem[id].cached) + pmem[id].vbase = ioremap_cached(pmem[id].base, pmem[id].size); +#ifdef ioremap_ext_buffered + else if (pmem[id].buffered) + pmem[id].vbase = ioremap_ext_buffered(pmem[id].base, + pmem[id].size); +#endif + else + pmem[id].vbase = ioremap(pmem[id].base, pmem[id].size); +} + +#ifdef CONFIG_MEMORY_HOTPLUG +static int pmem_mapped_regions(int id) { - file->private_data = inode->i_private; + struct list_head *elt; + + mutex_lock(&pmem[id].data_list_mutex); + list_for_each(elt, &pmem[id].data_list) { + struct pmem_data *data = + list_entry(elt, struct pmem_data, list); + + if (data) { + mutex_unlock(&pmem[id].data_list_mutex); + return 1; + } + } + mutex_unlock(&pmem[id].data_list_mutex); return 0; } -static ssize_t debug_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) +static int active_unstable_pmem(void) { - struct list_head *elt, *elt2; - struct pmem_data *data; - struct pmem_region_node *region_node; - int id = (int)file->private_data; - const int debug_bufmax = 4096; - static char buffer[4096]; - int n = 0; + int id; - DLOG("debug open\n"); - n = scnprintf(buffer, debug_bufmax, - "pid #: mapped regions (offset, len) (offset,len)...\n"); + for (id = 0; id < id_count; id++) { + if (pmem[id].memory_state == MEMORY_STABLE) + continue; + if (pmem_mapped_regions(id)) + return 1; + } - mutex_lock(&pmem[id].data_list_lock); - list_for_each(elt, &pmem[id].data_list) { - data = list_entry(elt, struct pmem_data, list); - down_read(&data->sem); - n += scnprintf(buffer + n, debug_bufmax - n, "pid %u:", - data->pid); - list_for_each(elt2, &data->region_list) { - region_node = list_entry(elt2, struct pmem_region_node, - list); - n += scnprintf(buffer + n, debug_bufmax - n, - "(%lx,%lx) ", - region_node->region.offset, - region_node->region.len); + return 0; +} + +static void reserve_unstable_pmem(unsigned long unstable_pmem_start, + unsigned long unstable_pmem_size) +{ + reserve_hotplug_pages(unstable_pmem_start >> PAGE_SHIFT, + unstable_pmem_size >> PAGE_SHIFT); +} + +static void unreserve_unstable_pmem(unsigned long unstable_pmem_start, + unsigned long unstable_pmem_size) +{ + unreserve_hotplug_pages(unstable_pmem_start >> PAGE_SHIFT, + unstable_pmem_size >> PAGE_SHIFT); +} + +static void pmem_setup_unstable_devices(unsigned long start_pfn, + unsigned long nr_pages) +{ + int id; + unsigned long tmp; + + unstable_pmem_start = start_pfn << PAGE_SHIFT; + tmp = unstable_pmem_start; + + for (id = 0; id < id_count; id++) { + if (pmem[id].memory_state == MEMORY_STABLE) + continue; + + pmem[id].base = tmp; + pr_info("reserving %lx bytes unstable memory at %lx \ + for %s\n", pmem[id].size, pmem[id].base, pmem[id].name); + tmp += pmem[id].size; + } + unstable_pmem_size = tmp - unstable_pmem_start; + + for (id = 0; id < id_count; id++) { + if (pmem[id].memory_state == + MEMORY_UNSTABLE_NO_MEMORY_ALLOCATED) { + ioremap_pmem(id); + pmem[id].garbage_pfn = + page_to_pfn(alloc_page(GFP_KERNEL)); + + if (pmem[id].vbase == 0) + continue; + pmem[id].memory_state = + MEMORY_UNSTABLE_MEMORY_ALLOCATED; } - n += scnprintf(buffer + n, debug_bufmax - n, "\n"); - up_read(&data->sem); } - mutex_unlock(&pmem[id].data_list_lock); +} + +static int pmem_mem_going_offline_callback(void *arg) +{ + struct memory_notify *marg = arg; + int id; + + if ((marg->start_pfn << PAGE_SHIFT) != unstable_pmem_start) + return 0; + + if (active_unstable_pmem()) { + pr_alert("unstable PMEM memory device in use \ + prevents memory hotremove!\n"); + return -EAGAIN; + } + + unreserve_unstable_pmem(unstable_pmem_start, unstable_pmem_size); - n++; - buffer[n] = 0; - return simple_read_from_buffer(buf, count, ppos, buffer, n); + for (id = 0; id < id_count; id++) { + if (pmem[id].memory_state == MEMORY_UNSTABLE_MEMORY_ALLOCATED) + pmem[id].memory_state = + MEMORY_UNSTABLE_NO_MEMORY_ALLOCATED; + } + return 0; } -static struct file_operations debug_fops = { - .read = debug_read, - .open = debug_open, -}; -#endif +static int pmem_mem_online_callback(void *arg) +{ + struct memory_notify *marg = arg; + int id; -#if 0 -static struct miscdevice pmem_dev = { - .name = "pmem", - .fops = &pmem_fops, -}; + + if (unstable_pmem_present == UNSTABLE_UNINITIALIZED) { + pmem_setup_unstable_devices(marg->start_pfn, marg->nr_pages); + pr_alert("unstable pmem start %lx size %lx\n", + unstable_pmem_start, unstable_pmem_size); + unstable_pmem_present = UNSTABLE_INITIALIZED; + } + + if ((marg->start_pfn << PAGE_SHIFT) != unstable_pmem_start) + return 0; + + reserve_unstable_pmem(unstable_pmem_start, unstable_pmem_size); + + for (id = 0; id < id_count; id++) { + if (pmem[id].memory_state == + MEMORY_UNSTABLE_NO_MEMORY_ALLOCATED) { + if (pmem[id].vbase == 0) + ioremap_pmem(id); + if (pmem[id].vbase == 0) + continue; + pmem[id].memory_state = + MEMORY_UNSTABLE_MEMORY_ALLOCATED; + } + } + return 0; +} + +static int pmem_memory_callback(struct notifier_block *self, + unsigned long action, void *arg) +{ + int ret = 0; + + if (unstable_pmem_present == NO_UNSTABLE_MEMORY) + return 0; + + switch (action) { + case MEM_ONLINE: + ret = pmem_mem_online_callback(arg); + break; + case MEM_GOING_OFFLINE: + ret = pmem_mem_going_offline_callback(arg); + break; + case MEM_OFFLINE: + case MEM_GOING_ONLINE: + case MEM_CANCEL_ONLINE: + case MEM_CANCEL_OFFLINE: + break; + } + if (ret) + ret = notifier_from_errno(ret); + else + ret = NOTIFY_OK; + return ret; +} #endif int pmem_setup(struct android_pmem_platform_data *pdata, long (*ioctl)(struct file *, unsigned int, unsigned long), int (*release)(struct inode *, struct file *)) { - int err = 0; - int i, index = 0; - int id = id_count; - id_count++; + int i, index = 0, kapi_memtype_idx = -1, id, is_kernel_memtype = 0; + + if (id_count >= PMEM_MAX_DEVICES) { + pr_alert("pmem: %s: unable to register driver(%s) - no more " + "devices available!\n", __func__, pdata->name); + goto err_no_mem; + } + + if (!pdata->size) { + pr_alert("pmem: %s: unable to register pmem driver(%s) - zero " + "size passed in!\n", __func__, pdata->name); + goto err_no_mem; + } + + id = id_count++; + + pmem[id].id = id; + + if (pmem[id].allocate) { + pr_alert("pmem: %s: unable to register pmem driver - " + "duplicate registration of %s!\n", + __func__, pdata->name); + goto err_no_mem; + } + + pmem[id].allocator_type = pdata->allocator_type; + + for (i = 0; i < ARRAY_SIZE(kapi_memtypes); i++) { + if (!strcmp(kapi_memtypes[i].name, pdata->name)) { + if (kapi_memtypes[i].info_id >= 0) { + pr_alert("Unable to register kernel pmem " + "driver - duplicate registration of " + "%s!\n", pdata->name); + goto err_no_mem; + } + if (pdata->cached) { + pr_alert("kernel arena memory must " + "NOT be configured as 'cached'. Check " + "and fix your board file. Failing " + "pmem driver %s registration!", + pdata->name); + goto err_no_mem; + } + + is_kernel_memtype = 1; + kapi_memtypes[i].info_id = id; + kapi_memtype_idx = i; + break; + } + } + + /* 'quantum' is a "hidden" variable that defaults to 0 in the board + * files */ + pmem[id].quantum = pdata->quantum ?: PMEM_MIN_ALLOC; + if (pmem[id].quantum < PMEM_MIN_ALLOC || + !is_power_of_2(pmem[id].quantum)) { + pr_alert("pmem: %s: unable to register pmem driver %s - " + "invalid quantum value (%#x)!\n", + __func__, pdata->name, pmem[id].quantum); + goto err_reset_pmem_info; + } + + if (pdata->start % pmem[id].quantum) { + /* bad alignment for start! */ + pr_alert("pmem: %s: Unable to register driver %s - " + "improperly aligned memory region start address " + "(%#lx) as checked against quantum value of %#x!\n", + __func__, pdata->name, pdata->start, + pmem[id].quantum); + goto err_reset_pmem_info; + } + + if (pdata->size % pmem[id].quantum) { + /* bad alignment for size! */ + pr_alert("pmem: %s: Unable to register driver %s - " + "memory region size (%#lx) is not a multiple of " + "quantum size(%#x)!\n", __func__, pdata->name, + pdata->size, pmem[id].quantum); + goto err_reset_pmem_info; + } - pmem[id].no_allocator = pdata->no_allocator; pmem[id].cached = pdata->cached; pmem[id].buffered = pdata->buffered; pmem[id].base = pdata->start; pmem[id].size = pdata->size; - pmem[id].ioctl = ioctl; - pmem[id].release = release; - init_rwsem(&pmem[id].bitmap_sem); - mutex_init(&pmem[id].data_list_lock); - INIT_LIST_HEAD(&pmem[id].data_list); - pmem[id].dev.name = pdata->name; - pmem[id].dev.minor = id; - pmem[id].dev.fops = &pmem_fops; - printk(KERN_INFO "%s: %d init\n", pdata->name, pdata->cached); + strlcpy(pmem[id].name, pdata->name, PMEM_NAME_SIZE); - err = misc_register(&pmem[id].dev); - if (err) { - printk(KERN_ALERT "Unable to register pmem driver!\n"); - goto err_cant_register_device; + if (pdata->unstable) { + pmem[id].memory_state = MEMORY_UNSTABLE_NO_MEMORY_ALLOCATED; + unstable_pmem_present = UNSTABLE_UNINITIALIZED; } - pmem[id].num_entries = pmem[id].size / PMEM_MIN_ALLOC; - pmem[id].bitmap = kmalloc(pmem[id].num_entries * - sizeof(struct pmem_bits), GFP_KERNEL); - if (!pmem[id].bitmap) - goto err_no_mem_for_metadata; + pmem[id].num_entries = pmem[id].size / pmem[id].quantum; + + memset(&pmem[id].kobj, 0, sizeof(pmem[0].kobj)); + pmem[id].kobj.kset = pmem_kset; + + switch (pmem[id].allocator_type) { + case PMEM_ALLOCATORTYPE_ALLORNOTHING: + pmem[id].allocate = pmem_allocator_all_or_nothing; + pmem[id].free = pmem_free_all_or_nothing; + pmem[id].free_space = pmem_free_space_all_or_nothing; + pmem[id].kapi_free_index = pmem_kapi_free_index_allornothing; + pmem[id].len = pmem_len_all_or_nothing; + pmem[id].start_addr = pmem_start_addr_all_or_nothing; + pmem[id].num_entries = 1; + pmem[id].quantum = pmem[id].size; + pmem[id].allocator.all_or_nothing.allocated = 0; + + if (kobject_init_and_add(&pmem[id].kobj, + &pmem_allornothing_ktype, NULL, + "%s", pdata->name)) + goto out_put_kobj; + + break; + + case PMEM_ALLOCATORTYPE_BUDDYBESTFIT: + pmem[id].allocator.buddy_bestfit.buddy_bitmap = kmalloc( + pmem[id].num_entries * sizeof(struct pmem_bits), + GFP_KERNEL); + if (!pmem[id].allocator.buddy_bestfit.buddy_bitmap) + goto err_reset_pmem_info; + + memset(pmem[id].allocator.buddy_bestfit.buddy_bitmap, 0, + sizeof(struct pmem_bits) * pmem[id].num_entries); + + for (i = sizeof(pmem[id].num_entries) * 8 - 1; i >= 0; i--) + if ((pmem[id].num_entries) & 1<name)) + goto out_put_kobj; + + break; + + case PMEM_ALLOCATORTYPE_BITMAP: /* 0, default if not explicit */ + pmem[id].allocator.bitmap.bitm_alloc = kmalloc( + PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS * + sizeof(*pmem[id].allocator.bitmap.bitm_alloc), + GFP_KERNEL); + if (!pmem[id].allocator.bitmap.bitm_alloc) { + pr_alert("pmem: %s: Unable to register pmem " + "driver %s - can't allocate " + "bitm_alloc!\n", + __func__, pdata->name); + goto err_reset_pmem_info; + } + + if (kobject_init_and_add(&pmem[id].kobj, + &pmem_bitmap_ktype, NULL, + "%s", pdata->name)) + goto out_put_kobj; - memset(pmem[id].bitmap, 0, sizeof(struct pmem_bits) * - pmem[id].num_entries); + for (i = 0; i < PMEM_INITIAL_NUM_BITMAP_ALLOCATIONS; i++) { + pmem[id].allocator.bitmap.bitm_alloc[i].bit = -1; + pmem[id].allocator.bitmap.bitm_alloc[i].quanta = 0; + } - for (i = sizeof(pmem[id].num_entries) * 8 - 1; i >= 0; i--) { - if ((pmem[id].num_entries) & 1<name, pmem[id].allocator.bitmap.bitmap_free, + pmem[id].size, pmem[id].quantum); + break; + + default: + pr_alert("Invalid allocator type (%d) for pmem driver\n", + pdata->allocator_type); + goto err_reset_pmem_info; } - if (pmem[id].cached) - pmem[id].vbase = ioremap_cached(pmem[id].base, - pmem[id].size); -#ifdef ioremap_ext_buffered - else if (pmem[id].buffered) - pmem[id].vbase = ioremap_ext_buffered(pmem[id].base, - pmem[id].size); -#endif - else - pmem[id].vbase = ioremap(pmem[id].base, pmem[id].size); + pmem[id].ioctl = ioctl; + pmem[id].release = release; + mutex_init(&pmem[id].arena_mutex); + mutex_init(&pmem[id].data_list_mutex); + INIT_LIST_HEAD(&pmem[id].data_list); + + pmem[id].dev.name = pdata->name; + if (!is_kernel_memtype) { + pmem[id].dev.minor = id; + pmem[id].dev.fops = &pmem_fops; + pr_info("pmem: Initializing %s (user-space) as %s\n", + pdata->name, pdata->cached ? "cached" : "non-cached"); + + if (misc_register(&pmem[id].dev)) { + pr_alert("Unable to register pmem driver!\n"); + goto err_cant_register_device; + } + } else { /* kernel region, no user accessible device */ + pmem[id].dev.minor = -1; + pr_info("pmem: Initializing %s (in-kernel)\n", pdata->name); + } + + /* do not set up unstable pmem now, wait until first memory hotplug */ + if (pmem[id].memory_state == MEMORY_UNSTABLE_NO_MEMORY_ALLOCATED) + return 0; - if (pmem[id].vbase == 0) - goto error_cant_remap; + if (!is_kernel_memtype) { + ioremap_pmem(id); + if (pmem[id].vbase == 0) { + pr_err("pmem: ioremap failed for device %s\n", + pmem[id].name); + goto error_cant_remap; + } + } pmem[id].garbage_pfn = page_to_pfn(alloc_page(GFP_KERNEL)); - if (pmem[id].no_allocator) - pmem[id].allocated = 0; -#if PMEM_DEBUG - debugfs_create_file(pdata->name, S_IFREG | S_IRUGO, NULL, (void *)id, - &debug_fops); -#endif return 0; + error_cant_remap: - kfree(pmem[id].bitmap); -err_no_mem_for_metadata: - misc_deregister(&pmem[id].dev); + if (!is_kernel_memtype) + misc_deregister(&pmem[id].dev); err_cant_register_device: +out_put_kobj: + kobject_put(&pmem[id].kobj); + if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_BUDDYBESTFIT) + kfree(pmem[id].allocator.buddy_bestfit.buddy_bitmap); + else if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_BITMAP) { + kfree(pmem[id].allocator.bitmap.bitmap); + kfree(pmem[id].allocator.bitmap.bitm_alloc); + } +err_reset_pmem_info: + pmem[id].allocate = 0; + pmem[id].dev.minor = -1; + if (kapi_memtype_idx >= 0) + kapi_memtypes[i].info_id = -1; +err_no_mem: return -1; } @@ -1307,31 +2958,72 @@ static int pmem_probe(struct platform_device *pdev) struct android_pmem_platform_data *pdata; if (!pdev || !pdev->dev.platform_data) { - printk(KERN_ALERT "Unable to probe pmem!\n"); + pr_alert("Unable to probe pmem!\n"); return -1; } pdata = pdev->dev.platform_data; + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + return pmem_setup(pdata, NULL, NULL); } - static int pmem_remove(struct platform_device *pdev) { int id = pdev->id; __free_page(pfn_to_page(pmem[id].garbage_pfn)); + pm_runtime_disable(&pdev->dev); misc_deregister(&pmem[id].dev); return 0; } +static int pmem_runtime_suspend(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: suspending...\n"); + return 0; +} + +static int pmem_runtime_resume(struct device *dev) +{ + dev_dbg(dev, "pm_runtime: resuming...\n"); + return 0; +} + +static const struct dev_pm_ops pmem_dev_pm_ops = { + .runtime_suspend = pmem_runtime_suspend, + .runtime_resume = pmem_runtime_resume, +}; + static struct platform_driver pmem_driver = { .probe = pmem_probe, .remove = pmem_remove, - .driver = { .name = "android_pmem" } + .driver = { .name = "android_pmem", + .pm = &pmem_dev_pm_ops, + } }; static int __init pmem_init(void) { + /* create /sys/kernel/ directory */ + pmem_kset = kset_create_and_add(PMEM_SYSFS_DIR_NAME, + NULL, kernel_kobj); + if (!pmem_kset) { + pr_err("pmem(%s):kset_create_and_add fail\n", __func__); + return -ENOMEM; + } +/*HTC_START*/ + +root = vidc_get_debugfs_root(); + if (root) { + vidc_debugfs_file_create(root, "misc_msg_pmem_qcom", + (u32 *) &misc_msg_pmem_qcom); + } +/*HTC_END*/ +#ifdef CONFIG_MEMORY_HOTPLUG + hotplug_memory_notifier(pmem_memory_callback, 0); +#endif return platform_driver_register(&pmem_driver); } diff --git a/include/linux/android_pmem.h b/include/linux/android_pmem.h index f633621f5be3d..0d1e3e0afbc5a 100644 --- a/include/linux/android_pmem.h +++ b/include/linux/android_pmem.h @@ -1,6 +1,7 @@ /* include/linux/android_pmem.h * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -16,6 +17,20 @@ #ifndef _ANDROID_PMEM_H_ #define _ANDROID_PMEM_H_ +#include + +#define PMEM_KERNEL_TEST_MAGIC 0xc0 +#define PMEM_KERNEL_TEST_NOMINAL_TEST_IOCTL \ + _IO(PMEM_KERNEL_TEST_MAGIC, 1) +#define PMEM_KERNEL_TEST_ADVERSARIAL_TEST_IOCTL \ + _IO(PMEM_KERNEL_TEST_MAGIC, 2) +#define PMEM_KERNEL_TEST_HUGE_ALLOCATION_TEST_IOCTL \ + _IO(PMEM_KERNEL_TEST_MAGIC, 3) +#define PMEM_KERNEL_TEST_FREE_UNALLOCATED_TEST_IOCTL \ + _IO(PMEM_KERNEL_TEST_MAGIC, 4) +#define PMEM_KERNEL_TEST_LARGE_REGION_NUMBER_TEST_IOCTL \ + _IO(PMEM_KERNEL_TEST_MAGIC, 5) + #define PMEM_IOCTL_MAGIC 'p' #define PMEM_GET_PHYS _IOW(PMEM_IOCTL_MAGIC, 1, unsigned int) #define PMEM_MAP _IOW(PMEM_IOCTL_MAGIC, 2, unsigned int) @@ -33,7 +48,89 @@ * struct (with offset set to 0). */ #define PMEM_GET_TOTAL_SIZE _IOW(PMEM_IOCTL_MAGIC, 7, unsigned int) +/* Revokes gpu registers and resets the gpu. Pass a pointer to the + * start of the mapped gpu regs (the vaddr returned by mmap) as the argument. + */ +#define HW3D_REVOKE_GPU _IOW(PMEM_IOCTL_MAGIC, 8, unsigned int) #define PMEM_CACHE_FLUSH _IOW(PMEM_IOCTL_MAGIC, 8, unsigned int) +#define HW3D_GRANT_GPU _IOW(PMEM_IOCTL_MAGIC, 9, unsigned int) + +#define PMEM_CLEAN_INV_CACHES _IOW(PMEM_IOCTL_MAGIC, 11, unsigned int) +#define PMEM_CLEAN_CACHES _IOW(PMEM_IOCTL_MAGIC, 12, unsigned int) +#define PMEM_INV_CACHES _IOW(PMEM_IOCTL_MAGIC, 13, unsigned int) + +#define PMEM_GET_FREE_SPACE _IOW(PMEM_IOCTL_MAGIC, 14, unsigned int) +#define PMEM_ALLOCATE_ALIGNED _IOW(PMEM_IOCTL_MAGIC, 15, unsigned int) +struct pmem_region { + unsigned long offset; + unsigned long len; +}; + +struct pmem_addr { + unsigned long vaddr; + unsigned long offset; + unsigned long length; +}; + +struct pmem_freespace { + unsigned long total; + unsigned long largest; +}; + +struct pmem_allocation { + unsigned long size; + unsigned int align; +}; + +#ifdef __KERNEL__ +int get_pmem_file(unsigned int fd, unsigned long *start, unsigned long *vstart, + unsigned long *end, struct file **filp); +int get_pmem_fd(int fd, unsigned long *start, unsigned long *end); +int get_pmem_user_addr(struct file *file, unsigned long *start, + unsigned long *end); +void put_pmem_file(struct file* file); +void put_pmem_fd(int fd); +void flush_pmem_fd(int fd, unsigned long start, unsigned long len); +void flush_pmem_file(struct file *file, unsigned long start, unsigned long len); +int pmem_cache_maint(struct file *file, unsigned int cmd, + struct pmem_addr *pmem_addr); + +enum pmem_allocator_type { + /* Zero is a default in platform PMEM structures in the board files, + * when the "allocator_type" structure element is not explicitly + * defined + */ + PMEM_ALLOCATORTYPE_BITMAP = 0, /* forced to be zero here */ + + PMEM_ALLOCATORTYPE_ALLORNOTHING, + PMEM_ALLOCATORTYPE_BUDDYBESTFIT, + + PMEM_ALLOCATORTYPE_MAX, +}; + +#define PMEM_MEMTYPE_MASK 0x7 +#define PMEM_INVALID_MEMTYPE 0x0 +#define PMEM_MEMTYPE_EBI1 0x1 +#define PMEM_MEMTYPE_SMI 0x2 +#define PMEM_MEMTYPE_RESERVED_INVALID2 0x3 +#define PMEM_MEMTYPE_RESERVED_INVALID3 0x4 +#define PMEM_MEMTYPE_RESERVED_INVALID4 0x5 +#define PMEM_MEMTYPE_RESERVED_INVALID5 0x6 +#define PMEM_MEMTYPE_RESERVED_INVALID6 0x7 + +#define PMEM_ALIGNMENT_MASK 0x18 +#define PMEM_ALIGNMENT_RESERVED_INVALID1 0x0 +#define PMEM_ALIGNMENT_4K 0x8 /* the default */ +#define PMEM_ALIGNMENT_1M 0x10 +#define PMEM_ALIGNMENT_RESERVED_INVALID2 0x18 + +/* flags in the following function defined as above. */ +int32_t pmem_kalloc(const size_t size, const uint32_t flags); +int32_t pmem_kfree(const int32_t physaddr); + +/* kernel api names for board specific data structures */ +#define PMEM_KERNEL_EBI1_DATA_NAME "pmem_kernel_ebi1" +#define PMEM_KERNEL_SMI_DATA_NAME "pmem_kernel_smi" struct android_pmem_platform_data { @@ -42,52 +139,31 @@ struct android_pmem_platform_data unsigned long start; /* size of memory region */ unsigned long size; - /* set to indicate the region should not be managed with an allocator */ - unsigned no_allocator; + + enum pmem_allocator_type allocator_type; + /* treated as a 'hidden' variable in the board files. Can be + * set, but default is the system init value of 0 which becomes a + * quantum of 4K pages. + */ + unsigned int quantum; + /* set to indicate maps of this region should be cached, if a mix of * cached and uncached is desired, set this and open the device with * O_SYNC to get an uncached region */ unsigned cached; /* The MSM7k has bits to enable a write buffer in the bus controller*/ unsigned buffered; + /* This PMEM is on memory that may be powered off */ + unsigned unstable; }; -struct pmem_region { - unsigned long offset; - unsigned long len; -}; - -#ifdef CONFIG_ANDROID_PMEM -int is_pmem_file(struct file *file); -int get_pmem_file(int fd, unsigned long *start, unsigned long *vstart, - unsigned long *end, struct file **filp); -int get_pmem_user_addr(struct file *file, unsigned long *start, - unsigned long *end); -void put_pmem_file(struct file* file); -void flush_pmem_file(struct file *file, unsigned long start, unsigned long len); int pmem_setup(struct android_pmem_platform_data *pdata, long (*ioctl)(struct file *, unsigned int, unsigned long), int (*release)(struct inode *, struct file *)); + int pmem_remap(struct pmem_region *region, struct file *file, unsigned operation); - -#else -static inline int is_pmem_file(struct file *file) { return 0; } -static inline int get_pmem_file(int fd, unsigned long *start, - unsigned long *vstart, unsigned long *end, - struct file **filp) { return -ENOSYS; } -static inline int get_pmem_user_addr(struct file *file, unsigned long *start, - unsigned long *end) { return -ENOSYS; } -static inline void put_pmem_file(struct file* file) { return; } -static inline void flush_pmem_file(struct file *file, unsigned long start, - unsigned long len) { return; } -static inline int pmem_setup(struct android_pmem_platform_data *pdata, - long (*ioctl)(struct file *, unsigned int, unsigned long), - int (*release)(struct inode *, struct file *)) { return -ENOSYS; } - -static inline int pmem_remap(struct pmem_region *region, struct file *file, - unsigned operation) { return -ENOSYS; } -#endif +#endif /* __KERNEL__ */ #endif //_ANDROID_PPP_H_ From 26bfdadcf93d7b15b9ff3c90e4855ca5f7e88106 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 2 Feb 2012 14:00:12 -0600 Subject: [PATCH 2017/2556] drivers: video: msm: fix build for new pmem --- drivers/video/msm/mdp_ppp.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c index 56ecedfd3fd14..f80ba6a68a7a3 100644 --- a/drivers/video/msm/mdp_ppp.c +++ b/drivers/video/msm/mdp_ppp.c @@ -717,7 +717,7 @@ static int get_img(struct mdp_img *img, struct fb_info *info, return ret; } - +/* static void put_img(struct file *file) { if (file) { @@ -727,6 +727,17 @@ static void put_img(struct file *file) put_msm_hw3d_file(file); } } +*/ +void put_img(struct file *p_src_file) +{ +#ifdef CONFIG_ANDROID_PMEM + if (p_src_file) + put_pmem_file(p_src_file); +#else + if (is_msm_hw3d_file(file)) + put_msm_hw3d_file(file); +#endif +} static void dump_req(struct mdp_blit_req *req, unsigned long src_start, unsigned long src_len, From b354765473b4199603399076c65cbae445b9883d Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 2 Feb 2012 14:21:18 -0600 Subject: [PATCH 2018/2556] drivers: base: genlock: update via CAF --- drivers/base/genlock.c | 132 ++++++++++++++++++++++++++++++++++------- 1 file changed, 111 insertions(+), 21 deletions(-) diff --git a/drivers/base/genlock.c b/drivers/base/genlock.c index afe8eb1c74d9a..27717e0fbdbf4 100644 --- a/drivers/base/genlock.c +++ b/drivers/base/genlock.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -22,7 +22,6 @@ #include #include #include -#include /* for in_interrupt() */ /* Lock states - can either be unlocked, held as an exclusive write lock or a * shared read lock @@ -32,6 +31,9 @@ #define _RDLOCK GENLOCK_RDLOCK #define _WRLOCK GENLOCK_WRLOCK +#define GENLOCK_LOG_ERR(fmt, args...) \ +pr_err("genlock: %s: " fmt, __func__, ##args) + struct genlock { struct list_head active; /* List of handles holding lock */ spinlock_t lock; /* Spinlock to protect the lock internals */ @@ -49,12 +51,29 @@ struct genlock_handle { taken */ }; +/* + * Create a spinlock to protect against a race condition when a lock gets + * released while another process tries to attach it + */ + +static DEFINE_SPINLOCK(genlock_file_lock); + static void genlock_destroy(struct kref *kref) { - struct genlock *lock = container_of(kref, struct genlock, - refcount); + struct genlock *lock = container_of(kref, struct genlock, + refcount); - kfree(lock); + /* + * Clear the private data for the file descriptor in case the fd is + * still active after the lock gets released + */ + + spin_lock(&genlock_file_lock); + if (lock->file) + lock->file->private_data = NULL; + spin_unlock(&genlock_file_lock); + + kfree(lock); } /* @@ -64,6 +83,15 @@ static void genlock_destroy(struct kref *kref) static int genlock_release(struct inode *inodep, struct file *file) { + struct genlock *lock = file->private_data; + /* + * Clear the refrence back to this file structure to avoid + * somehow reusing the lock after the file has been destroyed + */ + + if (lock) + lock->file = NULL; + return 0; } @@ -82,12 +110,21 @@ struct genlock *genlock_create_lock(struct genlock_handle *handle) { struct genlock *lock; - if (handle->lock != NULL) + if (IS_ERR_OR_NULL(handle)) { + GENLOCK_LOG_ERR("Invalid handle\n"); return ERR_PTR(-EINVAL); + } + + if (handle->lock != NULL) { + GENLOCK_LOG_ERR("Handle already has a lock attached\n"); + return ERR_PTR(-EINVAL); + } lock = kzalloc(sizeof(*lock), GFP_KERNEL); - if (lock == NULL) + if (lock == NULL) { + GENLOCK_LOG_ERR("Unable to allocate memory for a lock\n"); return ERR_PTR(-ENOMEM); + } INIT_LIST_HEAD(&lock->active); init_waitqueue_head(&lock->queue); @@ -120,8 +157,10 @@ static int genlock_get_fd(struct genlock *lock) { int ret; - if (!lock->file) + if (!lock->file) { + GENLOCK_LOG_ERR("No file attached to the lock\n"); return -EINVAL; + } ret = get_unused_fd_flags(0); if (ret < 0) @@ -143,19 +182,37 @@ struct genlock *genlock_attach_lock(struct genlock_handle *handle, int fd) struct file *file; struct genlock *lock; - if (handle->lock != NULL) + if (IS_ERR_OR_NULL(handle)) { + GENLOCK_LOG_ERR("Invalid handle\n"); return ERR_PTR(-EINVAL); + } + + if (handle->lock != NULL) { + GENLOCK_LOG_ERR("Handle already has a lock attached\n"); + return ERR_PTR(-EINVAL); + } file = fget(fd); - if (file == NULL) + if (file == NULL) { + GENLOCK_LOG_ERR("Bad file descriptor\n"); return ERR_PTR(-EBADF); + } + /* + * take a spinlock to avoid a race condition if the lock is + * released and then attached + */ + + spin_lock(&genlock_file_lock); lock = file->private_data; + spin_unlock(&genlock_file_lock); fput(file); - if (lock == NULL) + if (lock == NULL) { + GENLOCK_LOG_ERR("File descriptor is invalid\n"); return ERR_PTR(-EINVAL); + } handle->lock = lock; kref_get(&lock->refcount); @@ -199,13 +256,16 @@ static int _genlock_unlock(struct genlock *lock, struct genlock_handle *handle) spin_lock_irqsave(&lock->lock, irqflags); - if (lock->state == _UNLOCKED) + if (lock->state == _UNLOCKED) { + GENLOCK_LOG_ERR("Trying to unlock an unlocked handle\n"); goto done; + } /* Make sure this handle is an owner of the lock */ - if (!handle_has_lock(lock, handle)) + if (!handle_has_lock(lock, handle)) { + GENLOCK_LOG_ERR("handle does not have lock attached to it\n"); goto done; - + } /* If the handle holds no more references to the lock then release it (maybe) */ @@ -274,7 +334,8 @@ static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle, * Otherwise the user tried to turn a read into a write, and we * don't allow that. */ - + GENLOCK_LOG_ERR("Trying to upgrade a read lock to a write" + "lock\n"); ret = -EINVAL; goto done; } @@ -341,11 +402,21 @@ static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle, int genlock_lock(struct genlock_handle *handle, int op, int flags, uint32_t timeout) { - struct genlock *lock = handle->lock; + struct genlock *lock; + int ret = 0; - if (lock == NULL) + if (IS_ERR_OR_NULL(handle)) { + GENLOCK_LOG_ERR("Invalid handle\n"); return -EINVAL; + } + + lock = handle->lock; + + if (lock == NULL) { + GENLOCK_LOG_ERR("Handle does not have a lock attached\n"); + return -EINVAL; + } switch (op) { case GENLOCK_UNLOCK: @@ -356,6 +427,7 @@ int genlock_lock(struct genlock_handle *handle, int op, int flags, ret = _genlock_lock(lock, handle, op, flags, timeout); break; default: + GENLOCK_LOG_ERR("Invalid lock operation\n"); ret = -EINVAL; break; } @@ -372,13 +444,22 @@ EXPORT_SYMBOL(genlock_lock); int genlock_wait(struct genlock_handle *handle, uint32_t timeout) { - struct genlock *lock = handle->lock; + struct genlock *lock; unsigned long irqflags; int ret = 0; unsigned int ticks = msecs_to_jiffies(timeout); - if (lock == NULL) + if (IS_ERR_OR_NULL(handle)) { + GENLOCK_LOG_ERR("Invalid handle\n"); + return -EINVAL; + } + + lock = handle->lock; + + if (lock == NULL) { + GENLOCK_LOG_ERR("Handle does not have a lock attached\n"); return -EINVAL; + } spin_lock_irqsave(&lock->lock, irqflags); @@ -468,8 +549,10 @@ static const struct file_operations genlock_handle_fops = { static struct genlock_handle *_genlock_get_handle(void) { struct genlock_handle *handle = kzalloc(sizeof(*handle), GFP_KERNEL); - if (handle == NULL) + if (handle == NULL) { + GENLOCK_LOG_ERR("Unable to allocate memory for the handle\n"); return ERR_PTR(-ENOMEM); + } return handle; } @@ -531,6 +614,9 @@ static long genlock_dev_ioctl(struct file *filep, unsigned int cmd, struct genlock *lock; int ret; + if (IS_ERR_OR_NULL(handle)) + return -EINVAL; + switch (cmd) { case GENLOCK_IOC_NEW: { lock = genlock_create_lock(handle); @@ -540,8 +626,11 @@ static long genlock_dev_ioctl(struct file *filep, unsigned int cmd, return 0; } case GENLOCK_IOC_EXPORT: { - if (handle->lock == NULL) + if (handle->lock == NULL) { + GENLOCK_LOG_ERR("Handle does not have a lock" + "attached\n"); return -EINVAL; + } ret = genlock_get_fd(handle->lock); if (ret < 0) @@ -586,6 +675,7 @@ static long genlock_dev_ioctl(struct file *filep, unsigned int cmd, return 0; } default: + GENLOCK_LOG_ERR("Invalid ioctl\n"); return -EINVAL; } } From d192c0b1ff71dfcfd31412dd45d950e946a0e1cf Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Tue, 21 Jun 2011 15:51:59 -0600 Subject: [PATCH 2019/2556] msm: kgsl: Remove from msm_kgsl.h msm_kgsl.h doesn't need msm_bus.h included inside of it, and since it is the public facing header, it is preferable to keep it as clean as possible. Remove it, and fix up the missing dependencies in a few of the driver files. Change-Id: Ic0dedbad56ad36d81beaae4ed3dfc0f46581c46c Signed-off-by: Jordan Crouse --- drivers/gpu/msm/adreno.c | 1 + drivers/gpu/msm/kgsl_pwrctrl.c | 1 + include/linux/msm_kgsl.h | 1 - 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 8462ea0f6ef4a..2909a35a8261f 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "kgsl.h" #include "kgsl_cffdump.h" diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index d039dddbd6034..bce098202bc64 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -12,6 +12,7 @@ */ #include #include +#include #include "kgsl.h" diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index 118d1f98097ae..21b90fc23d6b0 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -148,7 +148,6 @@ struct kgsl_version { }; #ifdef __KERNEL__ -#include #define KGSL_3D0_REG_MEMORY "kgsl_3d0_reg_memory" #define KGSL_3D0_IRQ "kgsl_3d0_irq" From 8d4c4ef694dc758e8ba50c3d7c74c7c74c9783f6 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Fri, 3 Feb 2012 13:01:46 -0600 Subject: [PATCH 2020/2556] include: msm: update mdp header per CAF 3.0 this partially fixes copybit. at the least it enables us to use mdp compositon --- drivers/video/msm/mdp_ppp31.c | 16 +- include/linux/msm_mdp.h | 440 +++++++++++++++++++++++++++++++--- 2 files changed, 418 insertions(+), 38 deletions(-) diff --git a/drivers/video/msm/mdp_ppp31.c b/drivers/video/msm/mdp_ppp31.c index da4bb0230b880..fa36002ebe374 100644 --- a/drivers/video/msm/mdp_ppp31.c +++ b/drivers/video/msm/mdp_ppp31.c @@ -388,8 +388,8 @@ static int blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *re } /* blit first region */ - if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_90) || - ((splitreq.flags & MDP_ROT_MASK) == 0x0)) { + if (((splitreq.flags & 0x07) == MDP_ROT_90) || + ((splitreq.flags & 0x07) == 0x0)) { splitreq.src_rect.h = s_h_0; splitreq.src_rect.y = s_y_0; splitreq.dst_rect.h = d_h_0; @@ -415,8 +415,8 @@ static int blit_split_height(struct mdp_info *mdp, const struct mdp_blit_req *re return ret; /* blit second region */ - if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_90) || - ((splitreq.flags & MDP_ROT_MASK) == 0x0)) { + if (((splitreq.flags & 0x07) == MDP_ROT_90) || + ((splitreq.flags & 0x07) == 0x0)) { splitreq.src_rect.h = s_h_1; splitreq.src_rect.y = s_y_1; splitreq.dst_rect.h = d_h_1; @@ -498,8 +498,8 @@ static int blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *req } /* blit first region */ - if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_270) || - ((splitreq.flags & MDP_ROT_MASK) == 0x0)) { + if (((splitreq.flags & 0x07) == MDP_ROT_270) || + ((splitreq.flags & 0x07) == 0x0)) { splitreq.src_rect.h = s_h_0; splitreq.src_rect.y = s_y_0; splitreq.dst_rect.h = d_h_0; @@ -533,8 +533,8 @@ static int blit_split_width(struct mdp_info *mdp, const struct mdp_blit_req *req return ret; /* blit second region */ - if (((splitreq.flags & MDP_ROT_MASK) == MDP_ROT_270) || - ((splitreq.flags & MDP_ROT_MASK) == 0x0)) { + if (((splitreq.flags & 0x07) == MDP_ROT_270) || + ((splitreq.flags & 0x07) == 0x0)) { splitreq.src_rect.h = s_h_1; splitreq.src_rect.y = s_y_1; splitreq.dst_rect.h = d_h_1; diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h index 03d8fdec2a3e6..427465db41676 100644 --- a/include/linux/msm_mdp.h +++ b/include/linux/msm_mdp.h @@ -15,25 +15,88 @@ #define _MSM_MDP_H_ #include +#include #define MSMFB_IOCTL_MAGIC 'm' #define MSMFB_GRP_DISP _IOW(MSMFB_IOCTL_MAGIC, 1, unsigned int) #define MSMFB_BLIT _IOW(MSMFB_IOCTL_MAGIC, 2, unsigned int) +#define MSMFB_SUSPEND_SW_REFRESHER _IOW(MSMFB_IOCTL_MAGIC, 128, unsigned int) +#define MSMFB_RESUME_SW_REFRESHER _IOW(MSMFB_IOCTL_MAGIC, 129, unsigned int) +#define MSMFB_CURSOR _IOW(MSMFB_IOCTL_MAGIC, 130, struct fb_cursor) +#define MSMFB_SET_LUT _IOW(MSMFB_IOCTL_MAGIC, 131, struct fb_cmap) +#define MSMFB_HISTOGRAM _IOWR(MSMFB_IOCTL_MAGIC, 132, struct mdp_histogram) +/* new ioctls's for set/get ccs matrix */ +#define MSMFB_GET_CCS_MATRIX _IOWR(MSMFB_IOCTL_MAGIC, 133, struct mdp_ccs) +#define MSMFB_SET_CCS_MATRIX _IOW(MSMFB_IOCTL_MAGIC, 134, struct mdp_ccs) +#define MSMFB_OVERLAY_SET _IOWR(MSMFB_IOCTL_MAGIC, 135, \ + struct mdp_overlay) +#define MSMFB_OVERLAY_UNSET _IOW(MSMFB_IOCTL_MAGIC, 136, unsigned int) +#define MSMFB_OVERLAY_PLAY _IOW(MSMFB_IOCTL_MAGIC, 137, \ + struct msmfb_overlay_data) +#define MSMFB_GET_PAGE_PROTECTION _IOR(MSMFB_IOCTL_MAGIC, 138, \ + struct mdp_page_protection) +#define MSMFB_SET_PAGE_PROTECTION _IOW(MSMFB_IOCTL_MAGIC, 139, \ + struct mdp_page_protection) +#define MSMFB_OVERLAY_GET _IOR(MSMFB_IOCTL_MAGIC, 140, \ + struct mdp_overlay) +#define MSMFB_OVERLAY_PLAY_ENABLE _IOW(MSMFB_IOCTL_MAGIC, 141, unsigned int) +#define MSMFB_OVERLAY_BLT _IOWR(MSMFB_IOCTL_MAGIC, 142, \ + struct msmfb_overlay_blt) +#define MSMFB_OVERLAY_BLT_OFFSET _IOW(MSMFB_IOCTL_MAGIC, 143, unsigned int) +#define MSMFB_HISTOGRAM_START _IO(MSMFB_IOCTL_MAGIC, 144) +#define MSMFB_HISTOGRAM_STOP _IO(MSMFB_IOCTL_MAGIC, 145) +#define MSMFB_NOTIFY_UPDATE _IOW(MSMFB_IOCTL_MAGIC, 146, unsigned int) + +#define MSMFB_OVERLAY_3D _IOWR(MSMFB_IOCTL_MAGIC, 147, \ + struct msmfb_overlay_3d) + +#define MSMFB_MIXER_INFO _IOWR(MSMFB_IOCTL_MAGIC, 148, \ + struct msmfb_mixer_info_req) +#define MSMFB_OVERLAY_PLAY_WAIT _IOWR(MSMFB_IOCTL_MAGIC, 149, \ + struct msmfb_overlay_data) +#define MSMFB_WRITEBACK_INIT _IO(MSMFB_IOCTL_MAGIC, 150) +#define MSMFB_WRITEBACK_START _IO(MSMFB_IOCTL_MAGIC, 151) +#define MSMFB_WRITEBACK_STOP _IO(MSMFB_IOCTL_MAGIC, 152) +#define MSMFB_WRITEBACK_QUEUE_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 153, \ + struct msmfb_data) +#define MSMFB_WRITEBACK_DEQUEUE_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 154, \ + struct msmfb_data) +#define MSMFB_WRITEBACK_TERMINATE _IO(MSMFB_IOCTL_MAGIC, 155) +#define MSMFB_MDP_PP _IOWR(MSMFB_IOCTL_MAGIC, 156, struct msmfb_mdp_pp) + +#define FB_TYPE_3D_PANEL 0x10101010 +#define MDP_IMGTYPE2_START 0x10000 +#define MSMFB_DRIVER_VERSION 0xF9E8D701 + +enum { + NOTIFY_UPDATE_START, + NOTIFY_UPDATE_STOP, +}; enum { - MDP_RGB_565, /* RGB 565 planar */ - MDP_XRGB_8888, /* RGB 888 padded */ - MDP_Y_CBCR_H2V2, /* Y and CbCr, pseudo planar w/ Cb is in MSB */ - MDP_ARGB_8888, /* ARGB 888 */ - MDP_RGB_888, /* RGB 888 planar */ - MDP_Y_CRCB_H2V2, /* Y and CrCb, pseudo planar w/ Cr is in MSB */ - MDP_YCRYCB_H2V1, /* YCrYCb interleave */ - MDP_Y_CRCB_H2V1, /* Y and CrCb, pseduo planar w/ Cr is in MSB */ - MDP_Y_CBCR_H2V1, /* Y and CrCb, pseduo planar w/ Cr is in MSB */ - MDP_RGBA_8888, /* ARGB 888 */ - MDP_BGRA_8888, /* ABGR 888 */ - MDP_RGBX_8888, /* RGBX 888 */ - MDP_IMGTYPE_LIMIT /* Non valid image type after this enum */ + MDP_RGB_565, /* RGB 565 planer */ + MDP_XRGB_8888, /* RGB 888 padded */ + MDP_Y_CBCR_H2V2, /* Y and CbCr, pseudo planer w/ Cb is in MSB */ + MDP_ARGB_8888, /* ARGB 888 */ + MDP_RGB_888, /* RGB 888 planer */ + MDP_Y_CRCB_H2V2, /* Y and CrCb, pseudo planer w/ Cr is in MSB */ + MDP_YCRYCB_H2V1, /* YCrYCb interleave */ + MDP_Y_CRCB_H2V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */ + MDP_Y_CBCR_H2V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */ + MDP_RGBA_8888, /* ARGB 888 */ + MDP_BGRA_8888, /* ABGR 888 */ + MDP_RGBX_8888, /* RGBX 888 */ + MDP_Y_CRCB_H2V2_TILE, /* Y and CrCb, pseudo planer tile */ + MDP_Y_CBCR_H2V2_TILE, /* Y and CbCr, pseudo planer tile */ + MDP_Y_CR_CB_H2V2, /* Y, Cr and Cb, planar */ + MDP_Y_CR_CB_GH2V2, /* Y, Cr and Cb, planar aligned to Android YV12 */ + MDP_Y_CB_CR_H2V2, /* Y, Cb and Cr, planar */ + MDP_Y_CRCB_H1V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */ + MDP_Y_CBCR_H1V1, /* Y and CbCr, pseduo planer w/ Cb is in MSB */ + MDP_IMGTYPE_LIMIT, + MDP_BGR_565 = MDP_IMGTYPE2_START, /* BGR 565 planer */ + MDP_FB_FORMAT, /* framebuffer format */ + MDP_IMGTYPE_LIMIT2 /* Non valid image type after this enum */ }; enum { @@ -41,41 +104,358 @@ enum { FB_IMG, }; -/* flag values */ -#define MDP_ROT_NOP 0 -#define MDP_FLIP_LR 0x1 -#define MDP_FLIP_UD 0x2 -#define MDP_ROT_90 0x4 -#define MDP_ROT_180 (MDP_FLIP_UD|MDP_FLIP_LR) -#define MDP_ROT_270 (MDP_ROT_90|MDP_FLIP_UD|MDP_FLIP_LR) -#define MDP_ROT_MASK 0x7 -#define MDP_DITHER 0x8 -#define MDP_BLUR 0x10 +enum { + HSIC_HUE = 0, + HSIC_SAT, + HSIC_INT, + HSIC_CON, + NUM_HSIC_PARAM, +}; + +/* mdp_blit_req flag values */ +#define MDP_ROT_NOP 0 +#define MDP_FLIP_LR 0x1 +#define MDP_FLIP_UD 0x2 +#define MDP_ROT_90 0x4 +#define MDP_ROT_180 (MDP_FLIP_UD|MDP_FLIP_LR) +#define MDP_ROT_270 (MDP_ROT_90|MDP_FLIP_UD|MDP_FLIP_LR) +#define MDP_DITHER 0x8 +#define MDP_BLUR 0x10 #define MDP_BLEND_FG_PREMULT 0x20000 +#define MDP_DEINTERLACE 0x80000000 +#define MDP_SHARPENING 0x40000000 +#define MDP_NO_DMA_BARRIER_START 0x20000000 +#define MDP_NO_DMA_BARRIER_END 0x10000000 +#define MDP_NO_BLIT 0x08000000 +#define MDP_BLIT_WITH_DMA_BARRIERS 0x000 +#define MDP_BLIT_WITH_NO_DMA_BARRIERS \ + (MDP_NO_DMA_BARRIER_START | MDP_NO_DMA_BARRIER_END) +#define MDP_BLIT_SRC_GEM 0x04000000 +#define MDP_BLIT_DST_GEM 0x02000000 +#define MDP_BLIT_NON_CACHED 0x01000000 +#define MDP_OV_PIPE_SHARE 0x00800000 +#define MDP_DEINTERLACE_ODD 0x00400000 +#define MDP_OV_PLAY_NOWAIT 0x00200000 +#define MDP_SOURCE_ROTATED_90 0x00100000 +#define MDP_DPP_HSIC 0x00080000 +#define MDP_BORDERFILL_SUPPORTED 0x00010000 +#define MDP_SECURE_OVERLAY_SESSION 0x00008000 +#define MDP_MEMORY_ID_TYPE_FB 0x00001000 -#define MDP_TRANSP_NOP 0xffffffff -#define MDP_ALPHA_NOP 0xff +#define MDP_TRANSP_NOP 0xffffffff +#define MDP_ALPHA_NOP 0xff + +#define MDP_FB_PAGE_PROTECTION_NONCACHED (0) +#define MDP_FB_PAGE_PROTECTION_WRITECOMBINE (1) +#define MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE (2) +#define MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE (3) +#define MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE (4) +/* Sentinel: Don't use! */ +#define MDP_FB_PAGE_PROTECTION_INVALID (5) +/* Count of the number of MDP_FB_PAGE_PROTECTION_... values. */ +#define MDP_NUM_FB_PAGE_PROTECTION_VALUES (5) struct mdp_rect { - u32 x, y, w, h; + uint32_t x; + uint32_t y; + uint32_t w; + uint32_t h; }; struct mdp_img { - u32 width, height, format, offset; + uint32_t width; + uint32_t height; + uint32_t format; + uint32_t offset; int memory_id; /* the file descriptor */ + uint32_t priv; +}; + +/* + * {3x3} + {3} ccs matrix + */ + +#define MDP_CCS_RGB2YUV 0 +#define MDP_CCS_YUV2RGB 1 + +#define MDP_CCS_SIZE 9 +#define MDP_BV_SIZE 3 + +struct mdp_ccs { + int direction; /* MDP_CCS_RGB2YUV or YUV2RGB */ + uint16_t ccs[MDP_CCS_SIZE]; /* 3x3 color coefficients */ + uint16_t bv[MDP_BV_SIZE]; /* 1x3 bias vector */ +}; + +struct mdp_csc { + int id; + uint32_t csc_mv[9]; + uint32_t csc_pre_bv[3]; + uint32_t csc_post_bv[3]; + uint32_t csc_pre_lv[6]; + uint32_t csc_post_lv[6]; }; +/* The version of the mdp_blit_req structure so that + * user applications can selectively decide which functionality + * to include + */ + +#define MDP_BLIT_REQ_VERSION 2 + struct mdp_blit_req { struct mdp_img src; struct mdp_img dst; struct mdp_rect src_rect; struct mdp_rect dst_rect; - u32 alpha, transp_mask, flags; + uint32_t alpha; + uint32_t transp_mask; + uint32_t flags; + int sharpening_strength; /* -127 <--> 127, default 64 */ }; struct mdp_blit_req_list { - u32 count; + uint32_t count; struct mdp_blit_req req[]; }; -#endif /* _MSM_MDP_H_ */ +#define MSMFB_DATA_VERSION 2 + +struct msmfb_data { + uint32_t offset; + int memory_id; + int id; + uint32_t flags; + uint32_t priv; + uint32_t iova; +}; + +#define MSMFB_NEW_REQUEST -1 + +struct msmfb_overlay_data { + uint32_t id; + struct msmfb_data data; + uint32_t version_key; + struct msmfb_data plane1_data; + struct msmfb_data plane2_data; +}; + +struct msmfb_img { + uint32_t width; + uint32_t height; + uint32_t format; +}; + +#define MSMFB_WRITEBACK_DEQUEUE_BLOCKING 0x1 +struct msmfb_writeback_data { + struct msmfb_data buf_info; + struct msmfb_img img; +}; + +struct dpp_ctrl { + /* + *'sharp_strength' has inputs = -128 <-> 127 + * Increasingly positive values correlate with increasingly sharper + * picture. Increasingly negative values correlate with increasingly + * smoothed picture. + */ + int8_t sharp_strength; + int8_t hsic_params[NUM_HSIC_PARAM]; +}; + +struct mdp_overlay { + struct msmfb_img src; + struct mdp_rect src_rect; + struct mdp_rect dst_rect; + uint32_t z_order; /* stage number */ + uint32_t is_fg; /* control alpha & transp */ + uint32_t alpha; + uint32_t transp_mask; + uint32_t flags; + uint32_t id; + uint32_t user_data[8]; + struct dpp_ctrl dpp; +}; + +struct msmfb_overlay_3d { + uint32_t is_3d; + uint32_t width; + uint32_t height; +}; + + +struct msmfb_overlay_blt { + uint32_t enable; + uint32_t offset; + uint32_t width; + uint32_t height; + uint32_t bpp; +}; + +struct mdp_histogram { + uint32_t frame_cnt; + uint32_t bin_cnt; + uint32_t *r; + uint32_t *g; + uint32_t *b; +}; + + +/* + + mdp_block_type defines the identifiers for each of pipes in MDP 4.3 + + MDP_BLOCK_RESERVED is provided for backward compatibility and is + deprecated. It corresponds to DMA_P. So MDP_BLOCK_DMA_P should be used + instead. + +*/ + +enum { + MDP_BLOCK_RESERVED = 0, + MDP_BLOCK_OVERLAY_0, + MDP_BLOCK_OVERLAY_1, + MDP_BLOCK_VG_1, + MDP_BLOCK_VG_2, + MDP_BLOCK_RGB_1, + MDP_BLOCK_RGB_2, + MDP_BLOCK_DMA_P, + MDP_BLOCK_DMA_S, + MDP_BLOCK_DMA_E, + MDP_BLOCK_MAX, +}; + +struct mdp_pcc_coeff { + uint32_t c, r, g, b, rr, gg, bb, rg, gb, rb, rgb_0, rgb_1; +}; + +struct mdp_pcc_cfg_data { + uint32_t block; + uint32_t ops; + struct mdp_pcc_coeff r, g, b; +}; + +#define MDP_CSC_FLAG_ENABLE 0x1 +#define MDP_CSC_FLAG_YUV_IN 0x2 +#define MDP_CSC_FLAG_YUV_OUT 0x4 + +struct mdp_csc_cfg { + /* flags for enable CSC, toggling RGB,YUV input/output */ + uint32_t flags; + uint32_t csc_mv[9]; + uint32_t csc_pre_bv[3]; + uint32_t csc_post_bv[3]; + uint32_t csc_pre_lv[6]; + uint32_t csc_post_lv[6]; +}; + +struct mdp_csc_cfg_data { + uint32_t block; + struct mdp_csc_cfg csc_data; +}; + +enum { + mdp_lut_igc, + mdp_lut_pgc, + mdp_lut_hist, + mdp_lut_max, +}; + + +struct mdp_igc_lut_data { + uint32_t block; + uint32_t len, ops; + uint32_t *c0_c1_data; + uint32_t *c2_data; +}; + +struct mdp_ar_gc_lut_data { + uint32_t x_start; + uint32_t slope; + uint32_t offset; +}; + +struct mdp_pgc_lut_data { + uint32_t block; + uint32_t flags; + uint8_t num_r_stages; + uint8_t num_g_stages; + uint8_t num_b_stages; + struct mdp_ar_gc_lut_data *r_data; + struct mdp_ar_gc_lut_data *g_data; + struct mdp_ar_gc_lut_data *b_data; +}; + + +struct mdp_hist_lut_data { + uint32_t block; + uint32_t ops; + uint32_t len; + uint32_t *data; +}; + + +struct mdp_lut_cfg_data { + uint32_t lut_type; + union { + struct mdp_igc_lut_data igc_lut_data; + struct mdp_pgc_lut_data pgc_lut_data; + struct mdp_hist_lut_data hist_lut_data; + } data; +}; + +enum { + mdp_op_pcc_cfg, + mdp_op_csc_cfg, + mdp_op_lut_cfg, + mdp_op_max, +}; + +struct msmfb_mdp_pp { + uint32_t op; + union { + struct mdp_pcc_cfg_data pcc_cfg_data; + struct mdp_csc_cfg_data csc_cfg_data; + struct mdp_lut_cfg_data lut_cfg_data; + } data; +}; + + +struct mdp_page_protection { + uint32_t page_protection; +}; + + +struct mdp_mixer_info { + int pndx; + int pnum; + int ptype; + int mixer_num; + int z_order; +}; + +#define MAX_PIPE_PER_MIXER 4 + +struct msmfb_mixer_info_req { + int mixer_num; + int cnt; + struct mdp_mixer_info info[MAX_PIPE_PER_MIXER]; +}; + + +#ifdef __KERNEL__ + +/* get the framebuffer physical address information */ +int get_fb_phys_info(unsigned long *start, unsigned long *len, int fb_num); +struct fb_info *msm_fb_get_writeback_fb(void); +int msm_fb_writeback_init(struct fb_info *info); +int msm_fb_writeback_start(struct fb_info *info); +int msm_fb_writeback_queue_buffer(struct fb_info *info, + struct msmfb_data *data); +int msm_fb_writeback_dequeue_buffer(struct fb_info *info, + struct msmfb_data *data); +int msm_fb_writeback_stop(struct fb_info *info); +int msm_fb_writeback_terminate(struct fb_info *info); +#endif + +#endif /*_MSM_MDP_H_*/ From ebd8f5901eddf739119c81427ea8c59213e0650b Mon Sep 17 00:00:00 2001 From: Lucille Sylvester Date: Thu, 19 May 2011 15:30:33 -0600 Subject: [PATCH 2021/2556] msm: kgsl: Do not thrash general workqueue Use the device specific workqueues for idle_check. Do not double schedule idle_checks. Change-Id: Ia1e934aa3592eebf7d3fe7ac39b94f0d3e7f695a CRs-fixed: 288592 Signed-off-by: Lucille Sylvester --- drivers/gpu/msm/adreno.c | 13 ++++++++----- drivers/gpu/msm/z180.c | 5 +++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 2909a35a8261f..da41081d75b3a 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -242,12 +242,15 @@ irqreturn_t adreno_isr(int irq, void *data) result = IRQ_HANDLED; } - if (device->pwrctrl.nap_allowed == true) { - device->requested_state = KGSL_STATE_NAP; - schedule_work(&device->idle_check_ws); - } else if (device->pwrctrl.idle_pass == true) { - schedule_work(&device->idle_check_ws); + if (device->requested_state == KGSL_STATE_NONE) { + if (device->pwrctrl.nap_allowed == true) { + device->requested_state = KGSL_STATE_NAP; + queue_work(device->work_queue, &device->idle_check_ws); + } else if (device->pwrctrl.idle_pass == true) { + queue_work(device->work_queue, &device->idle_check_ws); + } } + /* Reset the time-out in our idle timer */ mod_timer(&device->idle_timer, jiffies + device->pwrctrl.interval_timeout); diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c index 1d8ada3f41beb..a702e4dc289a3 100644 --- a/drivers/gpu/msm/z180.c +++ b/drivers/gpu/msm/z180.c @@ -246,9 +246,10 @@ static irqreturn_t z180_isr(int irq, void *data) } } - if (device->pwrctrl.nap_allowed == true) { + if ((device->pwrctrl.nap_allowed == true) && + (device->requested_state == KGSL_STATE_NONE)) { device->requested_state = KGSL_STATE_NAP; - schedule_work(&device->idle_check_ws); + queue_work(device->work_queue, &device->idle_check_ws); } mod_timer(&device->idle_timer, jiffies + device->pwrctrl.interval_timeout); From 344008634e80a0c313219a9c04c06636f4c8d5fc Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Fri, 20 May 2011 11:56:56 -0600 Subject: [PATCH 2022/2556] msm: kgsl: Make KGSL components buildable as modules Make kgsl-core, adreno and z180 buildable and usable as loadable modules. Export global core symbols for use by devices. Change-Id: Id2fdd9a1ec53cc4ee9a13ae9e13fedec67e46b9b Signed-off-by: Jordan Crouse --- drivers/gpu/msm/Kconfig | 2 +- drivers/gpu/msm/Makefile | 6 +++--- drivers/gpu/msm/kgsl.c | 6 +++++- drivers/gpu/msm/kgsl_mmu.c | 6 ++++++ drivers/gpu/msm/kgsl_pwrctrl.c | 19 +++++++------------ drivers/gpu/msm/kgsl_pwrctrl.h | 6 +++++- 6 files changed, 27 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/msm/Kconfig b/drivers/gpu/msm/Kconfig index e17bbbb64998a..7a183fc140c49 100644 --- a/drivers/gpu/msm/Kconfig +++ b/drivers/gpu/msm/Kconfig @@ -57,7 +57,7 @@ config MSM_KGSL_PSTMRTMDMP_RB_HEX of the human readable version. config MSM_KGSL_2D - bool "Enable the 2D core. Required for OpenVG" + tristate "MSM 2D graphics driver. Required for OpenVG" default n depends on MSM_KGSL && !ARCH_MSM7X27 diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile index edbb92ea21fd8..07f56dcf2f5c6 100644 --- a/drivers/gpu/msm/Makefile +++ b/drivers/gpu/msm/Makefile @@ -1,6 +1,6 @@ ccflags-y := -Iinclude/drm -msm_kgsl_core-$(CONFIG_MSM_KGSL) = \ +msm_kgsl_core-y = \ kgsl.o \ kgsl_sharedmem.o \ kgsl_pwrctrl.o @@ -10,7 +10,7 @@ msm_kgsl_core-$(CONFIG_MSM_KGSL_MMU) += kgsl_mmu.o msm_kgsl_core-$(CONFIG_MSM_KGSL_CFF_DUMP) += kgsl_cffdump.o msm_kgsl_core-$(CONFIG_MSM_KGSL_DRM) += kgsl_drm.o -msm_adreno-$(CONFIG_MSM_KGSL) += \ +msm_adreno-y += \ adreno_ringbuffer.o \ adreno_drawctxt.o \ adreno_postmortem.o \ @@ -18,7 +18,7 @@ msm_adreno-$(CONFIG_MSM_KGSL) += \ msm_adreno-$(CONFIG_DEBUG_FS) += adreno_debugfs.o -msm_z180-$(CONFIG_MSM_KGSL_2D) += z180.o +msm_z180-y += z180.o msm_kgsl_core-objs = $(msm_kgsl_core-y) msm_adreno-objs = $(msm_adreno-y) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index cdfc4e58eed80..ccb538eede41f 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -2077,5 +2077,9 @@ static int __init kgsl_core_init(void) return result; } -device_initcall(kgsl_core_init); +module_init(kgsl_core_init); +module_exit(kgsl_core_exit); +MODULE_AUTHOR("Qualcomm Innovation Center, Inc."); +MODULE_DESCRIPTION("MSM GPU driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c index 6cb768b0825eb..2f18c1b9b8490 100644 --- a/drivers/gpu/msm/kgsl_mmu.c +++ b/drivers/gpu/msm/kgsl_mmu.c @@ -577,6 +577,7 @@ void kgsl_mh_intrcallback(struct kgsl_device *device) * caused them, but we don't have enough info to figure that out yet. */ } +EXPORT_SYMBOL(kgsl_mh_intrcallback); static int kgsl_setup_pt(struct kgsl_pagetable *pt) { @@ -789,6 +790,7 @@ int kgsl_mmu_setstate(struct kgsl_device *device, return status; } +EXPORT_SYMBOL(kgsl_mmu_setstate); int kgsl_mmu_init(struct kgsl_device *device) { @@ -899,6 +901,7 @@ int kgsl_mmu_start(struct kgsl_device *device) kgsl_regwrite(device, device->mmu.reg.config, 0x00000000); return status; } +EXPORT_SYMBOL(kgsl_mmu_start); unsigned int kgsl_virtaddr_to_physaddr(void *virtaddr) { @@ -1061,6 +1064,7 @@ kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, return 0; } +EXPORT_SYMBOL(kgsl_mmu_unmap); int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc, unsigned int protflags) @@ -1092,6 +1096,7 @@ int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable, error: return result; } +EXPORT_SYMBOL(kgsl_mmu_map_global); int kgsl_mmu_stop(struct kgsl_device *device) { @@ -1113,6 +1118,7 @@ int kgsl_mmu_stop(struct kgsl_device *device) return 0; } +EXPORT_SYMBOL(kgsl_mmu_stop); int kgsl_mmu_close(struct kgsl_device *device) { diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index bce098202bc64..bab17cdc2f261 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -232,15 +232,6 @@ int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device) return ret; } -unsigned long kgsl_get_clkrate(struct clk *clk) -{ - if (clk != NULL) { - return clk_get_rate(clk); - } else { - return 0; - } -} - void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device) { device_remove_file(device->dev, &gpuclk_attr); @@ -318,6 +309,7 @@ void kgsl_pwrctrl_clk(struct kgsl_device *device, unsigned int pwrflag) return; } } +EXPORT_SYMBOL(kgsl_pwrctrl_clk); void kgsl_pwrctrl_axi(struct kgsl_device *device, unsigned int pwrflag) { @@ -357,6 +349,7 @@ void kgsl_pwrctrl_axi(struct kgsl_device *device, unsigned int pwrflag) return; } } +EXPORT_SYMBOL(kgsl_pwrctrl_axi); void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, unsigned int pwrflag) @@ -403,7 +396,7 @@ void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, unsigned int pwrflag) return; } } - +EXPORT_SYMBOL(kgsl_pwrctrl_pwrrail); void kgsl_pwrctrl_irq(struct kgsl_device *device, unsigned int pwrflag) { @@ -433,6 +426,7 @@ void kgsl_pwrctrl_irq(struct kgsl_device *device, unsigned int pwrflag) return; } } +EXPORT_SYMBOL(kgsl_pwrctrl_irq); int kgsl_pwrctrl_init(struct kgsl_device *device) { @@ -625,6 +619,7 @@ void kgsl_pre_hwaccess(struct kgsl_device *device) if (device->state & (KGSL_STATE_SLEEP | KGSL_STATE_NAP)) kgsl_pwrctrl_wake(device); } +EXPORT_SYMBOL(kgsl_pre_hwaccess); void kgsl_check_suspended(struct kgsl_device *device) { @@ -691,7 +686,7 @@ int kgsl_pwrctrl_sleep(struct kgsl_device *device) return 0; } - +EXPORT_SYMBOL(kgsl_pwrctrl_sleep); /******************************************************************/ /* Caller must hold the device mutex. */ @@ -721,4 +716,4 @@ void kgsl_pwrctrl_wake(struct kgsl_device *device) wake_lock(&device->idle_wakelock); KGSL_PWR_INFO(device, "wake return for device %d\n", device->id); } - +EXPORT_SYMBOL(kgsl_pwrctrl_wake); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 3277b6a5d6c15..034c5c3d97505 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -85,9 +85,13 @@ void kgsl_pre_hwaccess(struct kgsl_device *device); void kgsl_check_suspended(struct kgsl_device *device); int kgsl_pwrctrl_sleep(struct kgsl_device *device); void kgsl_pwrctrl_wake(struct kgsl_device *device); -unsigned long kgsl_get_clkrate(struct clk *clk); int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device); void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device); +static inline unsigned long kgsl_get_clkrate(struct clk *clk) +{ + return (clk != NULL) ? clk_get_rate(clk) : 0; +} + #endif /* __KGSL_PWRCTRL_H */ From a3b86fb6540be5dd7debbfe390efecaef6d2bfe7 Mon Sep 17 00:00:00 2001 From: Lucille Sylvester Date: Wed, 18 May 2011 18:37:39 -0600 Subject: [PATCH 2023/2556] msm: kgsl: Do not check nap_allowed to go to SLEEP nap_allowed should be checked when setting the requested_state, but not before attempting to sleep. Change-Id: Ia165fecc9c6c816c859bd1c51a00a40a354170e5 CRs-fixed: 288423 Signed-off-by: Lucille Sylvester --- drivers/gpu/msm/kgsl_pwrctrl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index bab17cdc2f261..42a4343e853b6 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -587,8 +587,7 @@ void kgsl_idle_check(struct work_struct *work) (device->requested_state != KGSL_STATE_SLEEP)) kgsl_pwrctrl_idle_calc(device); - if (device->state & (KGSL_STATE_ACTIVE | KGSL_STATE_NAP) && - device->pwrctrl.nap_allowed) { + if (device->state & (KGSL_STATE_ACTIVE | KGSL_STATE_NAP)) { if (kgsl_pwrctrl_sleep(device) != 0) mod_timer(&device->idle_timer, jiffies + From 6eeaeccd64b47d9caea5425d28cc3d525ef9c100 Mon Sep 17 00:00:00 2001 From: Sudhakara Rao Tentu Date: Tue, 24 May 2011 16:20:27 +0530 Subject: [PATCH 2024/2556] Revert "msm: kgsl: Do not turn off GPU clocks on 7x27a" Enable code to turn off GPU clocks in case of sleep. This reverts commit 2947a6dc8c871538d463cf9a4eb3246c6c0b65fe. Change-Id: I6b1a12f4dd81dd3ef0122e4a3ca91f83587e1e7e Signed-off-by: Sudhakara Rao Tentu --- drivers/gpu/msm/kgsl.h | 1 - drivers/gpu/msm/kgsl_pwrctrl.c | 4 ---- 2 files changed, 5 deletions(-) diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 25a56b49faa62..d128ac275c0f8 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -37,7 +37,6 @@ #include #include #include -#include #include diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 42a4343e853b6..1cf05d5291ae9 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -658,9 +658,6 @@ int kgsl_pwrctrl_sleep(struct kgsl_device *device) sleep: kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); - /*In 7x27a, do not turn off axi and gpu clocks*/ - if (cpu_is_msm7x27a()) - goto end; kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_OFF); if (pwr->pwrlevels[0].gpu_freq > 0) clk_set_rate(pwr->grp_clks[0], @@ -676,7 +673,6 @@ int kgsl_pwrctrl_sleep(struct kgsl_device *device) clk_off: kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_OFF); -end: device->state = device->requested_state; device->requested_state = KGSL_STATE_NONE; wake_unlock(&device->idle_wakelock); From 30d63ae6b8924c02cde05aa9e57f8849605c2d15 Mon Sep 17 00:00:00 2001 From: Lucille Sylvester Date: Thu, 26 May 2011 19:12:22 -0600 Subject: [PATCH 2025/2556] msm: kgsl: Only bind kgsl to CPU0, not CPU1 Change-Id: Ib65917e2d3711ef26e057d0f2c86c2d639bae29b Signed-off-by: Lucille Sylvester --- drivers/gpu/msm/kgsl_cffdump.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c index 6da93487f4cef..e5e8398686c41 100644 --- a/drivers/gpu/msm/kgsl_cffdump.c +++ b/drivers/gpu/msm/kgsl_cffdump.c @@ -271,7 +271,7 @@ void kgsl_cffdump_init() cpumask_t mask; cpumask_clear(&mask); - cpumask_set_cpu(1, &mask); + cpumask_set_cpu(0, &mask); sched_setaffinity(0, &mask); #endif if (!debugfs_dir || IS_ERR(debugfs_dir)) { From 75b2d514dd3327e45af9ea9f4d812b0f0e235b33 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Thu, 2 Jun 2011 08:30:11 -0600 Subject: [PATCH 2026/2556] msm: kgsl: Fixup external memory offsets Offsets passed in for external memory chunks are not always 4K aligned, so make sure the differences are accounted for. CRs-fixed: 290267 Change-Id: I3c11b7f853679e00af8ebc9c7f54443d2fe6ec6d Signed-off-by: Jordan Crouse --- drivers/gpu/msm/kgsl.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index ccb538eede41f..576e91b360735 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -680,6 +680,8 @@ kgsl_sharedmem_find(struct kgsl_process_private *private, unsigned int gpuaddr) BUG_ON(private == NULL); + gpuaddr &= PAGE_MASK; + list_for_each_entry(entry, &private->mem_list, list) { if (entry->memdesc.gpuaddr == gpuaddr) { result = entry; @@ -1227,6 +1229,9 @@ static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, if (size == 0) size = len; + /* Adjust the size of the region to account for the offset */ + size += offset & ~PAGE_MASK; + size = ALIGN(size, PAGE_SIZE); if (_check_region(offset & PAGE_MASK, size, len)) { @@ -1279,6 +1284,9 @@ static int kgsl_setup_hostptr(struct kgsl_mem_entry *entry, if (size == 0) size = len; + /* Adjust the size of the region to account for the offset */ + size += offset & ~PAGE_MASK; + size = ALIGN(size, PAGE_SIZE); if (_check_region(offset & PAGE_MASK, size, len)) { @@ -1418,7 +1426,8 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, if (result) goto error_put_file_ptr; - param->gpuaddr = entry->memdesc.gpuaddr; + /* Adjust the returned value for a non 4k aligned offset */ + param->gpuaddr = entry->memdesc.gpuaddr + (param->offset & ~PAGE_MASK); entry->memtype = KGSL_EXTERNAL_MEMORY; From d5d5f47f25283eb1ce327f7290033be29a7e17d6 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Tue, 7 Jun 2011 10:38:57 -0600 Subject: [PATCH 2027/2556] msm: kgsl: Add pwrscale policy infrastructure Add a new infrastructure to allow for pluggable GPU power scaling policies. Change-Id: Ic0dedbade19812f6fd92023bf7d25f6a9046d0c9 Signed-off-by: Jordan Crouse Conflicts: drivers/gpu/msm/kgsl.c --- drivers/gpu/msm/Makefile | 3 +- drivers/gpu/msm/adreno.c | 1 + drivers/gpu/msm/kgsl.c | 8 + drivers/gpu/msm/kgsl_device.h | 3 + drivers/gpu/msm/kgsl_pwrctrl.c | 5 +- drivers/gpu/msm/kgsl_pwrctrl.h | 3 +- drivers/gpu/msm/kgsl_pwrscale.c | 308 ++++++++++++++++++++++++++++++++ drivers/gpu/msm/kgsl_pwrscale.h | 89 +++++++++ 8 files changed, 416 insertions(+), 4 deletions(-) create mode 100644 drivers/gpu/msm/kgsl_pwrscale.c create mode 100644 drivers/gpu/msm/kgsl_pwrscale.h diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile index 07f56dcf2f5c6..c905bfecfc600 100644 --- a/drivers/gpu/msm/Makefile +++ b/drivers/gpu/msm/Makefile @@ -3,7 +3,8 @@ ccflags-y := -Iinclude/drm msm_kgsl_core-y = \ kgsl.o \ kgsl_sharedmem.o \ - kgsl_pwrctrl.o + kgsl_pwrctrl.o \ + kgsl_pwrscale.o msm_kgsl_core-$(CONFIG_DEBUG_FS) += kgsl_debugfs.o msm_kgsl_core-$(CONFIG_MSM_KGSL_MMU) += kgsl_mmu.o diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index da41081d75b3a..88a084d680265 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -16,6 +16,7 @@ #include #include "kgsl.h" +#include "kgsl_pwrscale.h" #include "kgsl_cffdump.h" #include "adreno.h" diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 576e91b360735..a8f00e3896819 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -921,6 +921,14 @@ static long kgsl_ioctl_rb_issueibcmds(struct kgsl_device_private *dev_priv, goto free_ibdesc; } + /* Let the pwrscale policy know that a new command buffer + is being issued */ + + kgsl_pwrscale_busy(dev_priv->device); + +/* drewis: don't know what changed this...diff from cherry-pick + f3c1074d1539be20cecbb82f37705bd16058418e */ +/* result = dev_priv->device->ftbl->issueibcmds(dev_priv,*/ result = dev_priv->device->ftbl.device_issueibcmds(dev_priv, context, ibdesc, diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index 1a515d230e1ef..e24daa094781c 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -35,6 +35,7 @@ #include "kgsl_mmu.h" #include "kgsl_pwrctrl.h" #include "kgsl_log.h" +#include "kgsl_pwrscale.h" #define KGSL_TIMEOUT_NONE 0 #define KGSL_TIMEOUT_DEFAULT 0xFFFFFFFF @@ -173,6 +174,8 @@ struct kgsl_device { int mem_log; int pwr_log; struct wake_lock idle_wakelock; + struct kgsl_pwrscale pwrscale; + struct kobject pwrscale_kobj; }; struct kgsl_context { diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 1cf05d5291ae9..c7e8b8e7e58ca 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -59,8 +59,8 @@ static inline void kgsl_pwrctrl_tz_reset(void) __secure_tz_entry(TZ_RESET_ID, 0); } -static void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, - unsigned int new_level) +void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, + unsigned int new_level) { struct kgsl_pwrctrl *pwr = &device->pwrctrl; if (new_level < (pwr->num_pwrlevels - 1) && @@ -80,6 +80,7 @@ static void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, pwr->active_pwrlevel); } } +EXPORT_SYMBOL(kgsl_pwrctrl_pwrlevel_change); static int kgsl_pwrctrl_gpuclk_store(struct device *dev, struct device_attribute *attr, diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 034c5c3d97505..0bea2337e9ff0 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -85,7 +85,8 @@ void kgsl_pre_hwaccess(struct kgsl_device *device); void kgsl_check_suspended(struct kgsl_device *device); int kgsl_pwrctrl_sleep(struct kgsl_device *device); void kgsl_pwrctrl_wake(struct kgsl_device *device); - +void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, + unsigned int level); int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device); void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device); diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c new file mode 100644 index 0000000000000..f2250283655ef --- /dev/null +++ b/drivers/gpu/msm/kgsl_pwrscale.c @@ -0,0 +1,308 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include + +#include "kgsl.h" +#include "kgsl_pwrscale.h" + +struct kgsl_pwrscale_attribute { + struct attribute attr; + ssize_t (*show)(struct kgsl_device *device, char *buf); + ssize_t (*store)(struct kgsl_device *device, const char *buf, + size_t count); +}; + +#define to_pwrscale(k) container_of(k, struct kgsl_pwrscale, kobj) +#define pwrscale_to_device(p) container_of(p, struct kgsl_device, pwrscale) +#define to_device(k) container_of(k, struct kgsl_device, pwrscale_kobj) +#define to_pwrscale_attr(a) \ +container_of(a, struct kgsl_pwrscale_attribute, attr) +#define to_policy_attr(a) \ +container_of(a, struct kgsl_pwrscale_policy_attribute, attr) + +#define PWRSCALE_ATTR(_name, _mode, _show, _store) \ +struct kgsl_pwrscale_attribute pwrscale_attr_##_name = \ +__ATTR(_name, _mode, _show, _store) + +/* Master list of available policies */ + +static struct kgsl_pwrscale_policy *kgsl_pwrscale_policies[] = { + NULL +}; + +static ssize_t pwrscale_policy_store(struct kgsl_device *device, + const char *buf, size_t count) +{ + int i; + struct kgsl_pwrscale_policy *policy = NULL; + + /* The special keyword none allows the user to detach all + policies */ + if (!strncmp("none", buf, 4)) { + kgsl_pwrscale_detach_policy(device); + return count; + } + + for (i = 0; kgsl_pwrscale_policies[i]; i++) { + if (!strncmp(kgsl_pwrscale_policies[i]->name, buf, + strnlen(kgsl_pwrscale_policies[i]->name, + PAGE_SIZE))) { + policy = kgsl_pwrscale_policies[i]; + break; + } + } + + if (policy) + if (kgsl_pwrscale_attach_policy(device, policy)) + return -EIO; + + return count; +} + +static ssize_t pwrscale_policy_show(struct kgsl_device *device, char *buf) +{ + int ret; + + if (device->pwrscale.policy) + ret = snprintf(buf, PAGE_SIZE, "%s\n", + device->pwrscale.policy->name); + else + ret = snprintf(buf, PAGE_SIZE, "none\n"); + + return ret; +} + +PWRSCALE_ATTR(policy, 0644, pwrscale_policy_show, pwrscale_policy_store); + +static ssize_t pwrscale_avail_policies_show(struct kgsl_device *device, + char *buf) +{ + int i, ret = 0; + + for (i = 0; kgsl_pwrscale_policies[i]; i++) { + ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s ", + kgsl_pwrscale_policies[i]->name); + } + + ret += snprintf(buf + ret, PAGE_SIZE - ret, "none\n"); + return ret; +} +PWRSCALE_ATTR(avail_policies, 0444, pwrscale_avail_policies_show, NULL); + +static struct attribute *pwrscale_attrs[] = { + &pwrscale_attr_policy.attr, + &pwrscale_attr_avail_policies.attr, + NULL +}; + +static ssize_t policy_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct kgsl_pwrscale *pwrscale = to_pwrscale(kobj); + struct kgsl_device *device = pwrscale_to_device(pwrscale); + struct kgsl_pwrscale_policy_attribute *pattr = to_policy_attr(attr); + ssize_t ret; + + if (pattr->show) + ret = pattr->show(device, pwrscale, buf); + else + ret = -EIO; + + return ret; +} + +static ssize_t policy_sysfs_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t count) +{ + struct kgsl_pwrscale *pwrscale = to_pwrscale(kobj); + struct kgsl_device *device = pwrscale_to_device(pwrscale); + struct kgsl_pwrscale_policy_attribute *pattr = to_policy_attr(attr); + ssize_t ret; + + if (pattr->store) + ret = pattr->store(device, pwrscale, buf, count); + else + ret = -EIO; + + return ret; +} + +static void policy_sysfs_release(struct kobject *kobj) +{ + struct kgsl_pwrscale *pwrscale = to_pwrscale(kobj); + + complete(&pwrscale->kobj_unregister); +} + +static ssize_t pwrscale_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct kgsl_device *device = to_device(kobj); + struct kgsl_pwrscale_attribute *pattr = to_pwrscale_attr(attr); + ssize_t ret; + + if (pattr->show) + ret = pattr->show(device, buf); + else + ret = -EIO; + + return ret; +} + +static ssize_t pwrscale_sysfs_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t count) +{ + struct kgsl_device *device = to_device(kobj); + struct kgsl_pwrscale_attribute *pattr = to_pwrscale_attr(attr); + ssize_t ret; + + if (pattr->store) + ret = pattr->store(device, buf, count); + else + ret = -EIO; + + return ret; +} + +static void pwrscale_sysfs_release(struct kobject *kobj) +{ +} + +static const struct sysfs_ops policy_sysfs_ops = { + .show = policy_sysfs_show, + .store = policy_sysfs_store +}; + +static const struct sysfs_ops pwrscale_sysfs_ops = { + .show = pwrscale_sysfs_show, + .store = pwrscale_sysfs_store +}; + +static struct kobj_type ktype_pwrscale_policy = { + .sysfs_ops = &policy_sysfs_ops, + .default_attrs = NULL, + .release = policy_sysfs_release +}; + +static struct kobj_type ktype_pwrscale = { + .sysfs_ops = &pwrscale_sysfs_ops, + .default_attrs = pwrscale_attrs, + .release = pwrscale_sysfs_release +}; + +void kgsl_pwrscale_sleep(struct kgsl_device *device) +{ + if (device->pwrscale.policy && device->pwrscale.policy->sleep) + device->pwrscale.policy->sleep(device, &device->pwrscale); +} +EXPORT_SYMBOL(kgsl_pwrscale_sleep); + +void kgsl_pwrscale_wake(struct kgsl_device *device) +{ + if (device->pwrscale.policy && device->pwrscale.policy->wake) + device->pwrscale.policy->wake(device, &device->pwrscale); +} +EXPORT_SYMBOL(kgsl_pwrscale_wake); + +void kgsl_pwrscale_busy(struct kgsl_device *device) +{ + if (device->pwrscale.policy && device->pwrscale.policy->busy) + device->pwrscale.policy->busy(device, &device->pwrscale); +} + +void kgsl_pwrscale_idle(struct kgsl_device *device) +{ + if (device->pwrscale.policy && device->pwrscale.policy->idle) + device->pwrscale.policy->idle(device, &device->pwrscale); +} +EXPORT_SYMBOL(kgsl_pwrscale_idle); + +int kgsl_pwrscale_policy_add_files(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale, + struct attribute_group *attr_group) +{ + int ret; + + init_completion(&pwrscale->kobj_unregister); + + ret = kobject_init_and_add(&pwrscale->kobj, + &ktype_pwrscale_policy, + &device->pwrscale_kobj, + "%s", pwrscale->policy->name); + + if (ret) + return ret; + + ret = sysfs_create_group(&pwrscale->kobj, attr_group); + + if (ret) { + kobject_put(&pwrscale->kobj); + wait_for_completion(&pwrscale->kobj_unregister); + } + + return ret; +} + +void kgsl_pwrscale_policy_remove_files(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale, + struct attribute_group *attr_group) +{ + sysfs_remove_group(&pwrscale->kobj, attr_group); + kobject_put(&pwrscale->kobj); + wait_for_completion(&pwrscale->kobj_unregister); +} + +void kgsl_pwrscale_detach_policy(struct kgsl_device *device) +{ + mutex_lock(&device->mutex); + if (device->pwrscale.policy != NULL) + device->pwrscale.policy->close(device, &device->pwrscale); + device->pwrscale.policy = NULL; + mutex_unlock(&device->mutex); +} +EXPORT_SYMBOL(kgsl_pwrscale_detach_policy); + +int kgsl_pwrscale_attach_policy(struct kgsl_device *device, + struct kgsl_pwrscale_policy *policy) +{ + int ret; + + if (device->pwrscale.policy != NULL) + kgsl_pwrscale_detach_policy(device); + + mutex_lock(&device->mutex); + device->pwrscale.policy = policy; + ret = device->pwrscale.policy->init(device, &device->pwrscale); + if (ret) + device->pwrscale.policy = NULL; + mutex_unlock(&device->mutex); + + return ret; +} +EXPORT_SYMBOL(kgsl_pwrscale_attach_policy); + +int kgsl_pwrscale_init(struct kgsl_device *device) +{ + return kobject_init_and_add(&device->pwrscale_kobj, &ktype_pwrscale, + &device->dev->kobj, "pwrscale"); +} +EXPORT_SYMBOL(kgsl_pwrscale_init); + +void kgsl_pwrscale_close(struct kgsl_device *device) +{ + kobject_put(&device->pwrscale_kobj); +} +EXPORT_SYMBOL(kgsl_pwrscale_close); diff --git a/drivers/gpu/msm/kgsl_pwrscale.h b/drivers/gpu/msm/kgsl_pwrscale.h new file mode 100644 index 0000000000000..f05adbd5411f0 --- /dev/null +++ b/drivers/gpu/msm/kgsl_pwrscale.h @@ -0,0 +1,89 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __KGSL_PWRSCALE_H +#define __KGSL_PWRSCALE_H + +struct kgsl_pwrscale; + +struct kgsl_pwrscale_policy { + const char *name; + int (*init)(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale); + void (*close)(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale); + void (*idle)(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale); + void (*busy)(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale); + void (*sleep)(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale); + void (*wake)(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale); +}; + +struct kgsl_pwrscale { + struct kgsl_pwrscale_policy *policy; + struct kobject kobj; + struct completion kobj_unregister; + void *priv; +}; + +struct kgsl_pwrscale_policy_attribute { + struct attribute attr; + ssize_t (*show)(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale, char *buf); + ssize_t (*store)(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale, const char *buf, + size_t count); +}; + +#define PWRSCALE_POLICY_ATTR(_name, _mode, _show, _store) \ + struct kgsl_pwrscale_policy_attribute policy_attr_##_name = \ + __ATTR(_name, _mode, _show, _store) + +int kgsl_pwrscale_init(struct kgsl_device *device); +void kgsl_pwrscale_close(struct kgsl_device *device); +int kgsl_pwrscale_attach_policy(struct kgsl_device *device, + struct kgsl_pwrscale_policy *policy); +void kgsl_pwrscale_detach_policy(struct kgsl_device *device); + +void kgsl_pwrscale_idle(struct kgsl_device *device); +void kgsl_pwrscale_busy(struct kgsl_device *device); +void kgsl_pwrscale_sleep(struct kgsl_device *device); +void kgsl_pwrscale_wake(struct kgsl_device *device); + +int kgsl_pwrscale_policy_add_files(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale, + struct attribute_group *attr_group); + +void kgsl_pwrscale_policy_remove_files(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale, + struct attribute_group *attr_group); +#endif From 10b37a81050092c00f8b68f43e261904b71185d1 Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Tue, 26 Apr 2011 11:34:22 -0600 Subject: [PATCH 2028/2556] msm: kgsl: clean up memory barriers in hang recovery Change-Id: I018439f138113e9c074074cefc5d0bace7614f9e Signed-off-by: Jeremy Gebben Conflicts: drivers/gpu/msm/adreno_ringbuffer.c --- drivers/gpu/msm/adreno.c | 4 +++- drivers/gpu/msm/adreno_ringbuffer.c | 13 +++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 88a084d680265..c661435889a3b 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -688,7 +688,8 @@ adreno_recover_hang(struct kgsl_device *device) KGSL_DEVICE_MEMSTORE_OFFSET(soptimestamp)); kgsl_sharedmem_readl(&device->memstore, &eoptimestamp, KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp)); - rmb(); + /* Make sure memory is synchronized before restarting the GPU */ + mb(); KGSL_CTXT_ERR(device, "Context that caused a GPU hang: %x\n", bad_context); /* restart device */ @@ -717,6 +718,7 @@ adreno_recover_hang(struct kgsl_device *device) KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable), enable_ts); } + /* Make sure all writes are posted before the GPU reads them */ wmb(); /* Mark the invalid context so no more commands are accepted from * that context */ diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index 624503e31f2f7..912e2fcf4e9a8 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -754,9 +754,13 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, GSL_RB_GET_READPTR(rb, &rb->rptr); +/* drewis: still not sure where this struct was changed */ +#if 0 + retired_timestamp = device->ftbl->readtimestamp(device, + KGSL_TIMESTAMP_RETIRED); +#endif retired_timestamp = device->ftbl.device_readtimestamp( device, KGSL_TIMESTAMP_RETIRED); - rmb(); KGSL_DRV_ERR(device, "GPU successfully executed till ts: %x\n", retired_timestamp); rb_rptr = (rb->rptr - 4) * sizeof(unsigned int); @@ -764,7 +768,6 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, * sucessfully executed command */ while ((rb_rptr / sizeof(unsigned int)) != rb->wptr) { kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); - rmb(); if (value == retired_timestamp) { rb_rptr += sizeof(unsigned int); kgsl_sharedmem_readl(&rb->buffer_desc, &val1, rb_rptr); @@ -772,7 +775,6 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, kgsl_sharedmem_readl(&rb->buffer_desc, &val2, rb_rptr); rb_rptr += sizeof(unsigned int); kgsl_sharedmem_readl(&rb->buffer_desc, &val3, rb_rptr); - rmb(); /* match the pattern found at the end of a command */ if ((val1 == 2 && val2 == pm4_type3_packet(PM4_INTERRUPT, 1) @@ -811,7 +813,6 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, kgsl_sharedmem_readl(&rb->buffer_desc, &val1, rb_rptr); kgsl_sharedmem_readl(&rb->buffer_desc, &val2, rb_rptr + sizeof(unsigned int)); - rmb(); if (val1 == pm4_nop_packet(1) && val2 == KGSL_CMD_IDENTIFIER) { KGSL_DRV_ERR(device, "GPU recovery from hang not possible because " @@ -827,24 +828,20 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); rb_rptr = (rb_rptr + sizeof(unsigned int)) % rb->buffer_desc.size; - rmb(); /* check for context switch indicator */ if (value == KGSL_CONTEXT_TO_MEM_IDENTIFIER) { kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); rb_rptr = (rb_rptr + sizeof(unsigned int)) % rb->buffer_desc.size; - rmb(); BUG_ON(value != pm4_type3_packet(PM4_MEM_WRITE, 2)); kgsl_sharedmem_readl(&rb->buffer_desc, &val1, rb_rptr); rb_rptr = (rb_rptr + sizeof(unsigned int)) % rb->buffer_desc.size; - rmb(); BUG_ON(val1 != (device->memstore.gpuaddr + KGSL_DEVICE_MEMSTORE_OFFSET(current_context))); kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); rb_rptr = (rb_rptr + sizeof(unsigned int)) % rb->buffer_desc.size; - rmb(); BUG_ON((copy_rb_contents == 0) && (value == cur_context)); /* if context switches to a context that did not cause From 025a0e9e0a32001c4b2fc2652e825cfa9898ce7b Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Tue, 26 Apr 2011 15:51:22 -0600 Subject: [PATCH 2029/2556] msm: kgsl: clean up barriers in cffdump and logging code Change-Id: I5d8af471b2ae3d2d564643410375d988b73395e5 Signed-off-by: Jeremy Gebben --- drivers/gpu/msm/adreno_debugfs.c | 2 +- drivers/gpu/msm/kgsl_cffdump.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/msm/adreno_debugfs.c b/drivers/gpu/msm/adreno_debugfs.c index 882ba5032d1f0..527ada5627962 100644 --- a/drivers/gpu/msm/adreno_debugfs.c +++ b/drivers/gpu/msm/adreno_debugfs.c @@ -218,7 +218,7 @@ static int kgsl_regread_nolock(struct kgsl_device *device, reg = (unsigned int *)(device->regspace.mmio_virt_base + (offsetwords << 2)); - *value = readl(reg); + *value = __raw_readl(reg); return 0; } diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c index e5e8398686c41..138f4eace3bab 100644 --- a/drivers/gpu/msm/kgsl_cffdump.c +++ b/drivers/gpu/msm/kgsl_cffdump.c @@ -352,7 +352,7 @@ void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv, /* Ensure that this memory region is not read from the * cache but fetched fresh */ - dsb(); wmb(); mb(); + mb(); kgsl_cache_range_op(memdesc->hostptr, memdesc->size, memdesc->type, KGSL_CACHE_OP_INV); @@ -521,8 +521,8 @@ bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv, if (!memdesc->physaddr) { KGSL_CORE_ERR("no physaddr"); return true; - else { - dsb(); wmb(); mb(); + } else { + mb(); kgsl_cache_range_op(memdesc->hostptr, memdesc->size, memdesc->type, KGSL_CACHE_OP_INV); } From c381345a13f3d75a3adab7e7b78ef3a48e816d8d Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Tue, 26 Apr 2011 15:54:03 -0600 Subject: [PATCH 2030/2556] msm: kgsl: use relaxed readl and writel for memory read/write Calling code should have barriers where necessary rather than relying on barriers in readl() and writel(). Change-Id: Icfd50ce59a8ea5412f8e95985c27d1d3572de4e6 Signed-off-by: Jeremy Gebben --- drivers/gpu/msm/kgsl_sharedmem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index 15e6b4fd38cf1..a22b0e1e5d23c 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -611,7 +611,7 @@ kgsl_sharedmem_readl(const struct kgsl_memdesc *memdesc, if (offsetbytes + sizeof(unsigned int) > memdesc->size) return -ERANGE; - *dst = readl(memdesc->hostptr + offsetbytes); + *dst = readl_relaxed(memdesc->hostptr + offsetbytes); return 0; } EXPORT_SYMBOL(kgsl_sharedmem_readl); @@ -626,7 +626,7 @@ kgsl_sharedmem_writel(const struct kgsl_memdesc *memdesc, kgsl_cffdump_setmem(memdesc->physaddr + offsetbytes, src, sizeof(uint)); - writel(src, memdesc->hostptr + offsetbytes); + writel_relaxed(src, memdesc->hostptr + offsetbytes); return 0; } EXPORT_SYMBOL(kgsl_sharedmem_writel); From 5cf379b9ca8659f3dd1c3fa1de19aa75448ae6eb Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Mon, 9 May 2011 17:25:10 -0600 Subject: [PATCH 2031/2556] msm: kgsl: remove barrier after outer cache ops This shouldn't be necessary if outer cache calls are implemented correctly. Change-Id: Ia94d981e1b643848c36f4953cb1f9974c1cc568c Signed-off-by: Jeremy Gebben --- drivers/gpu/msm/kgsl_sharedmem.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index a22b0e1e5d23c..1e22ddab14120 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -320,8 +320,6 @@ static void _outer_cache_range_op(int op, unsigned long addr, size_t size) outer_inv_range(addr, addr + size); break; } - - mb(); } #endif From 2f01039ec35cc66acae35a3fbd567614c2f95e5b Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Thu, 19 May 2011 13:44:49 -0600 Subject: [PATCH 2032/2556] msm: kgsl: remove unused handler for sq block interrupts Interrupts from this block have never been enabled. Change-Id: I942560666c0864917f28e418bf2c099d38d2a04e Signed-off-by: Jeremy Gebben Conflicts: drivers/gpu/msm/adreno.c --- drivers/gpu/msm/adreno.c | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index c661435889a3b..40770fca58762 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -33,10 +33,6 @@ (RBBM_INT_CNTL__RDERR_INT_MASK | \ RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK) -#define GSL_SQ_INT_MASK \ - (SQ_INT_CNTL__PS_WATCHDOG_MASK | \ - SQ_INT_CNTL__VS_WATCHDOG_MASK) - /* Adreno MH arbiter config*/ #define ADRENO_CFG_MHARB \ (0x10 \ @@ -190,25 +186,6 @@ static void adreno_rbbm_intrcallback(struct kgsl_device *device) adreno_regwrite_isr(device, REG_RBBM_INT_ACK, status); } -static void adreno_sq_intrcallback(struct kgsl_device *device) -{ - unsigned int status = 0; - - adreno_regread_isr(device, REG_SQ_INT_STATUS, &status); - - if (status & SQ_INT_CNTL__PS_WATCHDOG_MASK) - KGSL_DRV_INFO(device, "sq ps watchdog interrupt\n"); - else if (status & SQ_INT_CNTL__VS_WATCHDOG_MASK) - KGSL_DRV_INFO(device, "sq vs watchdog interrupt\n"); - else - KGSL_DRV_WARN(device, - "bad bits in REG_SQ_INT_STATUS %08x\n", status); - - - status &= GSL_SQ_INT_MASK; - adreno_regwrite_isr(device, REG_SQ_INT_ACK, status); -} - irqreturn_t adreno_isr(int irq, void *data) { irqreturn_t result = IRQ_NONE; @@ -238,11 +215,6 @@ irqreturn_t adreno_isr(int irq, void *data) result = IRQ_HANDLED; } - if (status & MASTER_INT_SIGNAL__SQ_INT_STAT) { - adreno_sq_intrcallback(device); - result = IRQ_HANDLED; - } - if (device->requested_state == KGSL_STATE_NONE) { if (device->pwrctrl.nap_allowed == true) { device->requested_state = KGSL_STATE_NAP; @@ -626,8 +598,6 @@ static int adreno_stop(struct kgsl_device *device) del_timer(&device->idle_timer); adreno_regwrite(device, REG_RBBM_INT_CNTL, 0); - adreno_regwrite(device, REG_SQ_INT_CNTL, 0); - adreno_dev->drawctxt_active = NULL; adreno_ringbuffer_stop(&adreno_dev->ringbuffer); From 9430d4547a1354f77ccc829a16681830fa62a792 Mon Sep 17 00:00:00 2001 From: Matt Wagantall Date: Tue, 21 Jun 2011 11:48:32 -0700 Subject: [PATCH 2033/2556] msm: kgsl: Replace internal_power_rail API calls with regulator APIs Remove use of the msm-specific internal_power_rail driver in favor of the new footswitch-pcom driver, which uses the regulator framework's APIs. While code for several SoCs is modified to accomplish this, this change should only be significant for 7x30. All other SoCs were either already using regulator APIs or do not support footswitch control at all. Change-Id: Ifb3b671889ce61783326368eb7ef307f1a40b1c7 Signed-off-by: Matt Wagantall Conflicts: arch/arm/mach-msm/clock-7x30.c arch/arm/mach-msm/devices-8960.c arch/arm/mach-msm/devices-msm7x30.c arch/arm/mach-msm/devices-msm8x60.c drivers/gpu/msm/kgsl_pwrctrl.c --- drivers/gpu/msm/adreno.c | 14 ++++---------- drivers/gpu/msm/kgsl_pwrctrl.c | 20 ++------------------ drivers/gpu/msm/kgsl_pwrctrl.h | 4 ---- drivers/gpu/msm/z180.c | 15 ++++----------- include/linux/msm_kgsl.h | 1 - 5 files changed, 10 insertions(+), 44 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 40770fca58762..16cc1fa4d63c0 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -97,7 +97,6 @@ static struct adreno_device device_3d0 = { }, }, .pwrctrl = { - .pwr_rail = PWR_RAIL_GRP_CLK, .regulator_name = "fs_gfx3d", .irq_name = KGSL_3D0_IRQ, .src_clk_name = "grp_src_clk", @@ -491,13 +490,10 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) device->state = KGSL_STATE_INIT; device->requested_state = KGSL_STATE_NONE; - /* Order pwrrail/clk sequence based upon platform. */ - if (device->pwrctrl.pwrrail_first) - kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_ON); + /* Enable the power rail before the clocks. */ + kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_ON); kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_ON); kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_ON); - if (!device->pwrctrl.pwrrail_first) - kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_ON); if (kgsl_mmu_start(device)) goto error_clk_off; @@ -606,13 +602,11 @@ static int adreno_stop(struct kgsl_device *device) kgsl_mmu_stop(device); + /* Disable the clocks before the power rail. */ kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); - if (!device->pwrctrl.pwrrail_first) - kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_OFF); kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_OFF); kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_OFF); - if (device->pwrctrl.pwrrail_first) - kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_OFF); + kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_OFF); return 0; } diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index c7e8b8e7e58ca..51982ac462b13 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -362,11 +362,6 @@ void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, unsigned int pwrflag) if (pwr->power_flags & KGSL_PWRFLAGS_POWER_ON) { KGSL_PWR_INFO(device, "power off, device %d\n", device->id); - if (internal_pwr_rail_ctl(pwr->pwr_rail, false)) { - KGSL_DRV_ERR(device, - "call internal_pwr_rail_ctl failed\n"); - return; - } if (pwr->gpu_reg) regulator_disable(pwr->gpu_reg); pwr->power_flags &= @@ -379,12 +374,6 @@ void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, unsigned int pwrflag) if (pwr->power_flags & KGSL_PWRFLAGS_POWER_OFF) { KGSL_PWR_INFO(device, "power on, device %d\n", device->id); - if (internal_pwr_rail_ctl(pwr->pwr_rail, true)) { - KGSL_PWR_ERR(device, - "call internal_pwr_rail_ctl failed\n"); - return; - } - if (pwr->gpu_reg) regulator_enable(pwr->gpu_reg); pwr->power_flags &= @@ -485,19 +474,14 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) pwr->gpu_reg = regulator_get(NULL, pwr->regulator_name); if (IS_ERR(pwr->gpu_reg)) pwr->gpu_reg = NULL; - if (internal_pwr_rail_mode(device->pwrctrl.pwr_rail, - PWR_RAIL_CTL_MANUAL)) { - KGSL_PWR_ERR(device, "internal_pwr_rail_mode failed\n"); - result = -EINVAL; - goto done; - } pwr->power_flags = KGSL_PWRFLAGS_CLK_OFF | KGSL_PWRFLAGS_AXI_OFF | KGSL_PWRFLAGS_POWER_OFF | KGSL_PWRFLAGS_IRQ_OFF; pwr->nap_allowed = pdata_pwr->nap_allowed; - pwr->pwrrail_first = pdata_pwr->pwrrail_first; +/* drewis: below was removed at some point before i cherry-picked the below commit */ pwr->idle_pass = pdata_pwr->idle_pass; +/*dc14311... msm: kgsl: Replace internal_power_rail API calls with regulator APIs*/ pwr->interval_timeout = pdata_pwr->idle_timeout; pwr->ebi1_clk = clk_get(NULL, "ebi1_kgsl_clk"); if (IS_ERR(pwr->ebi1_clk)) diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 0bea2337e9ff0..26b0d3aab6a27 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -29,8 +29,6 @@ #ifndef __KGSL_PWRCTRL_H #define __KGSL_PWRCTRL_H -#include - /***************************************************************************** ** power flags *****************************************************************************/ @@ -51,7 +49,6 @@ struct platform_device; struct kgsl_pwrctrl { int interrupt_num; int have_irq; - unsigned int pwr_rail; struct clk *ebi1_clk; struct clk *grp_clks[KGSL_MAX_CLKS]; unsigned int power_flags; @@ -67,7 +64,6 @@ struct kgsl_pwrctrl { const char *regulator_name; const char *irq_name; const char *src_clk_name; - bool pwrrail_first; s64 time; unsigned int no_switch_cnt; unsigned int idle_pass; diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c index a702e4dc289a3..b4df869a2a8eb 100644 --- a/drivers/gpu/msm/z180.c +++ b/drivers/gpu/msm/z180.c @@ -157,7 +157,6 @@ static struct z180_device device_2d0 = { }, }, .pwrctrl = { - .pwr_rail = PWR_RAIL_GRP_2D_CLK, .regulator_name = "fs_gfx2d0", .irq_name = KGSL_2D0_IRQ, }, @@ -197,7 +196,6 @@ static struct z180_device device_2d1 = { }, }, .pwrctrl = { - .pwr_rail = PWR_RAIL_GRP_2D_CLK, .regulator_name = "fs_gfx2d1", .irq_name = KGSL_2D1_IRQ, }, @@ -604,13 +602,10 @@ static int z180_start(struct kgsl_device *device, unsigned int init_ram) device->requested_state = KGSL_STATE_NONE; KGSL_PWR_WARN(device, "state -> INIT, device %d\n", device->id); - /* Order pwrrail/clk sequence based upon platform. */ - if (device->pwrctrl.pwrrail_first) - kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_ON); + /* Enable the power rail before the clocks. */ + kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_ON); kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_ON); kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_ON); - if (!device->pwrctrl.pwrrail_first) - kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_ON); /* Set up MH arbiter. MH offsets are considered to be dword * based, therefore no down shift. */ @@ -649,13 +644,11 @@ static int z180_stop(struct kgsl_device *device) kgsl_mmu_stop(device); + /* Disable the clocks before the power rail. */ kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); - if (!device->pwrctrl.pwrrail_first) - kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_OFF); kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_OFF); kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_OFF); - if (device->pwrctrl.pwrrail_first) - kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_OFF); + kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_OFF); return 0; } diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index 21b90fc23d6b0..ed14a84efa945 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -168,7 +168,6 @@ struct kgsl_device_pwr_data { int (*set_grp_async)(void); unsigned int idle_timeout; unsigned int nap_allowed; - bool pwrrail_first; unsigned int idle_pass; }; From f292e6f20c878fd1b31a338d8aba826bf9d5e3ba Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 5 Feb 2012 00:57:30 -0600 Subject: [PATCH 2034/2556] msm: replace internal_power_rail with newer footswitch-pcom first attempt at fixing kernel panic caused by gpu hang when put to sleep. --- arch/arm/mach-msm/Kconfig | 1 + arch/arm/mach-msm/Makefile | 3 +- arch/arm/mach-msm/board-mahimahi.c | 12 + arch/arm/mach-msm/devices.h | 2 + arch/arm/mach-msm/footswitch-pcom.c | 332 ++++++++++++++++++ arch/arm/mach-msm/footswitch.h | 66 ++++ .../include/mach/internal_power_rail.h | 68 ---- arch/arm/mach-msm/internal_power_rail.c | 103 ------ 8 files changed, 415 insertions(+), 172 deletions(-) create mode 100644 arch/arm/mach-msm/footswitch-pcom.c create mode 100644 arch/arm/mach-msm/footswitch.h delete mode 100644 arch/arm/mach-msm/include/mach/internal_power_rail.h delete mode 100644 arch/arm/mach-msm/internal_power_rail.c diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index db55b41def890..4bc6e2747f09d 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -36,6 +36,7 @@ config ARCH_QSD8X50 select HAS_MSM_DEBUG_UART_PHYS select VERIFY_PERMISSION_FAULT select MSM_DAL + select REGULATOR config ARCH_MSM8X60 bool "MSM8X60" diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index ece3635ddc61a..606149d557adf 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -23,7 +23,8 @@ endif obj-$(CONFIG_ARCH_MSM8X60) += clock-dummy.o iommu.o iommu_dev.o devices-msm8x60-iommu.o obj-$(CONFIG_MSM_PROC_COMM) += proc_comm.o clock-pcom.o vreg.o -obj-$(CONFIG_MSM_PROC_COMM) += clock.o internal_power_rail.o +obj-$(CONFIG_MSM_PROC_COMM) += clock.o +obj-$(CONFIG_MSM_PROC_COMM) += footswitch-pcom.o obj-$(CONFIG_ARCH_QSD8X50) += sirc.o obj-$(CONFIG_MSM_FIQ_SUPPORT) += fiq_glue.o obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o smem_log.o diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 9473c3b20340e..ebdf653805bda 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -61,6 +61,8 @@ #include "board-mahimahi-smb329.h" #include +#include +#include "footswitch.h" static uint debug_uart; @@ -344,6 +346,13 @@ struct platform_device msm_kgsl_3d0 = { }; /* end kgsl */ +/* start footswitch regulator */ +struct platform_device *msm_footswitch_devices[] = { + FS_PCOM(FS_GFX3D, "fs_gfx3d"), +}; +unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices); +/* end footswitch regulator */ + static struct android_pmem_platform_data mdp_pmem_pdata = { .name = "pmem", .start = MSM_PMEM_MDP_BASE, @@ -1140,6 +1149,9 @@ static void __init mahimahi_init(void) platform_add_devices(devices, ARRAY_SIZE(devices)); + platform_add_devices(msm_footswitch_devices, + msm_num_footswitch_devices); + i2c_register_board_info(0, base_i2c_devices, ARRAY_SIZE(base_i2c_devices)); diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h index 2337fe4c883e0..460590f1b5702 100644 --- a/arch/arm/mach-msm/devices.h +++ b/arch/arm/mach-msm/devices.h @@ -63,4 +63,6 @@ extern struct clk msm_clocks_8x50[]; extern unsigned msm_num_clocks_8x50; extern struct platform_device msm_device_vidc_720p; +extern struct platform_device msm_footswitch; + #endif diff --git a/arch/arm/mach-msm/footswitch-pcom.c b/arch/arm/mach-msm/footswitch-pcom.c new file mode 100644 index 0000000000000..8eec807464f23 --- /dev/null +++ b/arch/arm/mach-msm/footswitch-pcom.c @@ -0,0 +1,332 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include "footswitch.h" +#include "proc_comm.h" + +/* PCOM power rail IDs */ +#define PCOM_FS_GRP 8 +#define PCOM_FS_GRP_2D 58 +#define PCOM_FS_MDP 14 +#define PCOM_FS_MFC 68 +#define PCOM_FS_ROTATOR 90 +#define PCOM_FS_VFE 41 +#define PCOM_FS_VPE 76 + +#define PCOM_RAIL_MODE_AUTO 0 +#define PCOM_RAIL_MODE_MANUAL 1 + +/** + * struct footswitch - Per-footswitch data and state + * @rdev: Regulator framework device + * @desc: Regulator descriptor + * @init_data: Regulator platform data + * @pcom_id: Proc-comm ID of the footswitch + * @is_enabled: Flag set when footswitch is enabled + * @is_manual: Flag set when footswitch is in manual proc-comm mode + * @has_ahb_clk: Flag set if footswitched core has an ahb_clk + * @has_src_clk: Flag set if footswitched core has a src_clk + * @src_clk: Controls the core clock's rate + * @core_clk: Clocks the core + * @ahb_clk: Clocks the core's register interface + * @src_clk_init_rate: Rate to use for src_clk if it has not been set yet + * @is_rate_set: Flag set if core_clk's rate has been set + */ +struct footswitch { + struct regulator_dev *rdev; + struct regulator_desc desc; + struct regulator_init_data init_data; + unsigned pcom_id; + bool is_enabled; + bool is_manual; + struct clk *src_clk; + struct clk *core_clk; + struct clk *ahb_clk; + const bool has_ahb_clk; + const bool has_src_clk; + const int src_clk_init_rate; + bool is_rate_set; +}; + +static inline int set_rail_mode(int pcom_id, int mode) +{ + int rc; + + rc = msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &pcom_id, &mode); + if (!rc && pcom_id) + rc = -EINVAL; + + return rc; +} + +static inline int set_rail_state(int pcom_id, int state) +{ + int rc; + + rc = msm_proc_comm(state, &pcom_id, NULL); + if (!rc && pcom_id) + rc = -EINVAL; + + return rc; +} + +static int enable_clocks(struct footswitch *fs) +{ + fs->is_rate_set = !!(clk_get_rate(fs->src_clk)); + if (!fs->is_rate_set) + clk_set_rate(fs->src_clk, fs->src_clk_init_rate); + clk_enable(fs->core_clk); + + if (fs->ahb_clk) + clk_enable(fs->ahb_clk); + + return 0; +} + +static void disable_clocks(struct footswitch *fs) +{ + if (fs->ahb_clk) + clk_disable(fs->ahb_clk); + clk_disable(fs->core_clk); +} + +static int footswitch_is_enabled(struct regulator_dev *rdev) +{ + struct footswitch *fs = rdev_get_drvdata(rdev); + + return fs->is_enabled; +} + +static int footswitch_enable(struct regulator_dev *rdev) +{ + struct footswitch *fs = rdev_get_drvdata(rdev); + int rc; + + rc = enable_clocks(fs); + if (rc) + return rc; + + rc = set_rail_state(fs->pcom_id, PCOM_CLKCTL_RPC_RAIL_ENABLE); + if (!rc) + fs->is_enabled = true; + + disable_clocks(fs); + + return rc; +} + +static int footswitch_disable(struct regulator_dev *rdev) +{ + struct footswitch *fs = rdev_get_drvdata(rdev); + int rc; + + rc = enable_clocks(fs); + if (rc) + return rc; + + rc = set_rail_state(fs->pcom_id, PCOM_CLKCTL_RPC_RAIL_DISABLE); + if (!rc) + fs->is_enabled = false; + + disable_clocks(fs); + + return rc; +} + +static struct regulator_ops footswitch_ops = { + .is_enabled = footswitch_is_enabled, + .enable = footswitch_enable, + .disable = footswitch_disable, +}; + +#define FOOTSWITCH(_id, _pcom_id, _name, _src_clk, _rate, _ahb_clk) \ + [_id] = { \ + .desc = { \ + .id = _id, \ + .name = _name, \ + .ops = &footswitch_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + .pcom_id = _pcom_id, \ + .has_src_clk = _src_clk, \ + .src_clk_init_rate = _rate, \ + .has_ahb_clk = _ahb_clk, \ + } +static struct footswitch footswitches[] = { + FOOTSWITCH(FS_GFX3D, PCOM_FS_GRP, + "fs_gfx3d", true, 24576000, true), + FOOTSWITCH(FS_GFX2D0, PCOM_FS_GRP_2D, + "fs_gfx2d0", false, 24576000, true), + FOOTSWITCH(FS_MDP, PCOM_FS_MDP, + "fs_mdp", false, 24576000, true), + FOOTSWITCH(FS_MFC, PCOM_FS_MFC, + "fs_mfc", false, 24576000, true), + FOOTSWITCH(FS_ROT, PCOM_FS_ROTATOR, + "fs_rot", false, 0, true), + FOOTSWITCH(FS_VFE, PCOM_FS_VFE, + "fs_vfe", false, 24576000, true), + FOOTSWITCH(FS_VPE, PCOM_FS_VPE, + "fs_vpe", false, 24576000, false), +}; + +static int get_clocks(struct device *dev, struct footswitch *fs) +{ + int rc; + + /* + * Some SoCs may not have a separate rate-settable clock. + * If one can't be found, try to use the core clock for + * rate-setting instead. + */ + if (fs->has_src_clk) { + fs->src_clk = clk_get(dev, "src_clk"); + if (IS_ERR(fs->src_clk)) + fs->src_clk = clk_get(dev, "core_clk"); + } else { + fs->src_clk = clk_get(dev, "core_clk"); + } + if (IS_ERR(fs->src_clk)) { + pr_err("clk_get(src_clk) failed\n"); + rc = PTR_ERR(fs->src_clk); + goto err_src_clk; + } + + fs->core_clk = clk_get(dev, "core_clk"); + if (IS_ERR(fs->core_clk)) { + pr_err("clk_get(core_clk) failed\n"); + rc = PTR_ERR(fs->core_clk); + goto err_core_clk; + } + + if (fs->has_ahb_clk) { + fs->ahb_clk = clk_get(dev, "iface_clk"); + if (IS_ERR(fs->ahb_clk)) { + pr_err("clk_get(iface_clk) failed\n"); + rc = PTR_ERR(fs->ahb_clk); + goto err_ahb_clk; + } + } + + return 0; + +err_ahb_clk: + clk_put(fs->core_clk); +err_core_clk: + clk_put(fs->src_clk); +err_src_clk: + return rc; +} + +static void put_clocks(struct footswitch *fs) +{ + clk_put(fs->src_clk); + clk_put(fs->core_clk); + clk_put(fs->ahb_clk); +} + +static int footswitch_probe(struct platform_device *pdev) +{ + struct footswitch *fs; + struct regulator_init_data *init_data; + int rc; + + if (pdev == NULL) + return -EINVAL; + + if (pdev->id >= MAX_FS) + return -ENODEV; + + fs = &footswitches[pdev->id]; + if (!fs->is_manual) { + pr_err("%s is not in manual mode\n", fs->desc.name); + return -EINVAL; + } + init_data = pdev->dev.platform_data; + + rc = get_clocks(&pdev->dev, fs); + if (rc) + return rc; + + fs->rdev = regulator_register(&fs->desc, &pdev->dev, init_data, fs); + if (IS_ERR(fs->rdev)) { + pr_err("regulator_register(%s) failed\n", fs->desc.name); + rc = PTR_ERR(fs->rdev); + goto err_register; + } + + return 0; + +err_register: + put_clocks(fs); + + return rc; +} + +static int __devexit footswitch_remove(struct platform_device *pdev) +{ + struct footswitch *fs = &footswitches[pdev->id]; + + regulator_unregister(fs->rdev); + set_rail_mode(fs->pcom_id, PCOM_RAIL_MODE_AUTO); + put_clocks(fs); + + return 0; +} + +static struct platform_driver footswitch_driver = { + .probe = footswitch_probe, + .remove = __devexit_p(footswitch_remove), + .driver = { + .name = "footswitch-pcom", + .owner = THIS_MODULE, + }, +}; + +static int __init footswitch_init(void) +{ + struct footswitch *fs; + int ret; + + /* + * Enable all footswitches in manual mode (ie. not controlled along + * with pcom clocks). + */ + for (fs = footswitches; fs < footswitches + ARRAY_SIZE(footswitches); + fs++) { + set_rail_state(fs->pcom_id, PCOM_CLKCTL_RPC_RAIL_ENABLE); + ret = set_rail_mode(fs->pcom_id, PCOM_RAIL_MODE_MANUAL); + if (!ret) + fs->is_manual = 1; + } + + return platform_driver_register(&footswitch_driver); +} +subsys_initcall(footswitch_init); + +static void __exit footswitch_exit(void) +{ + platform_driver_unregister(&footswitch_driver); +} +module_exit(footswitch_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("proc_comm rail footswitch"); +MODULE_ALIAS("platform:footswitch-pcom"); diff --git a/arch/arm/mach-msm/footswitch.h b/arch/arm/mach-msm/footswitch.h new file mode 100644 index 0000000000000..3d9b4795f314d --- /dev/null +++ b/arch/arm/mach-msm/footswitch.h @@ -0,0 +1,66 @@ +/* Copyright (c) 2010-2011 Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __MSM_FOOTSWITCH__ +#define __MSM_FOOTSWITCH__ + +#include + +/* Device IDs */ +#define FS_GFX2D0 0 +#define FS_GFX2D1 1 +#define FS_GFX3D 2 +#define FS_IJPEG 3 +#define FS_MDP 4 +#define FS_MFC 5 +#define FS_ROT 6 +#define FS_VED 7 +#define FS_VFE 8 +#define FS_VPE 9 +#define MAX_FS 10 + +#define FS_GENERIC(_drv_name, _id, _name) (&(struct platform_device){ \ + .name = (_drv_name), \ + .id = (_id), \ + .dev = { \ + .platform_data = &(struct regulator_init_data){ \ + .constraints = { \ + .valid_modes_mask = REGULATOR_MODE_NORMAL, \ + .valid_ops_mask = REGULATOR_CHANGE_STATUS, \ + }, \ + .num_consumer_supplies = 1, \ + .consumer_supplies = \ + &(struct regulator_consumer_supply) \ + REGULATOR_SUPPLY((_name), NULL), \ + } \ + }, \ +}) +#define FS_PCOM(_id, _name) FS_GENERIC("footswitch-pcom", (_id), (_name)) +#define FS_8X60(_id, _name) FS_GENERIC("footswitch-8x60", (_id), (_name)) + +#endif diff --git a/arch/arm/mach-msm/include/mach/internal_power_rail.h b/arch/arm/mach-msm/include/mach/internal_power_rail.h deleted file mode 100644 index be3c404c8bbf7..0000000000000 --- a/arch/arm/mach-msm/include/mach/internal_power_rail.h +++ /dev/null @@ -1,68 +0,0 @@ -/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _INTERNAL_POWER_RAIL_H -#define _INTERNAL_POWER_RAIL_H - -/* Clock power rail IDs */ -#define PWR_RAIL_GRP_CLK 8 -#define PWR_RAIL_GRP_2D_CLK 58 -#define PWR_RAIL_MDP_CLK 14 -#define PWR_RAIL_MFC_CLK 68 -#define PWR_RAIL_ROTATOR_CLK 90 -#define PWR_RAIL_VDC_CLK 39 -#define PWR_RAIL_VFE_CLK 41 -#define PWR_RAIL_VPE_CLK 76 - -enum rail_ctl_mode { - PWR_RAIL_CTL_AUTO = 0, - PWR_RAIL_CTL_MANUAL, -}; - -#if defined(CONFIG_ARCH_MSM8X60) || defined(CONFIG_ARCH_MSM8960) -static inline int __maybe_unused internal_pwr_rail_ctl(unsigned rail_id, - bool enable) -{ - /* Not yet implemented. */ - return 0; -} -static inline int __maybe_unused internal_pwr_rail_mode(unsigned rail_id, - enum rail_ctl_mode mode) -{ - /* Not yet implemented. */ - return 0; -} -#else -int internal_pwr_rail_ctl(unsigned rail_id, bool enable); -int internal_pwr_rail_mode(unsigned rail_id, enum rail_ctl_mode mode); -#endif - -int internal_pwr_rail_ctl_auto(unsigned rail_id, bool enable); - -#endif /* _INTERNAL_POWER_RAIL_H */ - diff --git a/arch/arm/mach-msm/internal_power_rail.c b/arch/arm/mach-msm/internal_power_rail.c deleted file mode 100644 index 8e04319397921..0000000000000 --- a/arch/arm/mach-msm/internal_power_rail.c +++ /dev/null @@ -1,103 +0,0 @@ -/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include - -#include - -#include "proc_comm.h" - -static DEFINE_SPINLOCK(power_rail_lock); - -static struct internal_rail { - uint32_t id; - uint32_t mode; -} rails[] = { - { PWR_RAIL_GRP_CLK, PWR_RAIL_CTL_AUTO }, - { PWR_RAIL_GRP_2D_CLK, PWR_RAIL_CTL_AUTO }, - { PWR_RAIL_MDP_CLK, PWR_RAIL_CTL_MANUAL }, - { PWR_RAIL_MFC_CLK, PWR_RAIL_CTL_AUTO }, - { PWR_RAIL_ROTATOR_CLK, PWR_RAIL_CTL_AUTO }, - { PWR_RAIL_VDC_CLK, PWR_RAIL_CTL_AUTO }, - { PWR_RAIL_VFE_CLK, PWR_RAIL_CTL_AUTO }, - { PWR_RAIL_VPE_CLK, PWR_RAIL_CTL_AUTO }, -}; - -static struct internal_rail *find_rail(unsigned rail_id) -{ - int i; - for (i = 0; i < ARRAY_SIZE(rails); i++) - if (rails[i].id == rail_id) - return rails + i; - - return NULL; -} - -/* Enable or disable an internal power rail */ -int internal_pwr_rail_ctl(unsigned rail_id, bool enable) -{ - int cmd, rc; - - cmd = enable ? PCOM_CLKCTL_RPC_RAIL_ENABLE : - PCOM_CLKCTL_RPC_RAIL_DISABLE; - - rc = msm_proc_comm(cmd, &rail_id, NULL); - - return rc; - -} -EXPORT_SYMBOL(internal_pwr_rail_ctl); - -/* Enable or disable a rail if the rail is in auto mode. */ -int internal_pwr_rail_ctl_auto(unsigned rail_id, bool enable) -{ - int rc = 0; - unsigned long flags; - struct internal_rail *rail = find_rail(rail_id); - - BUG_ON(!rail); - - spin_lock_irqsave(&power_rail_lock, flags); - if (rail->mode == PWR_RAIL_CTL_AUTO) - rc = internal_pwr_rail_ctl(rail_id, enable); - spin_unlock_irqrestore(&power_rail_lock, flags); - - return rc; -} - -/* Specify an internal power rail control mode (ex. auto, manual) */ -int internal_pwr_rail_mode(unsigned rail_id, enum rail_ctl_mode mode) -{ - int rc; - unsigned long flags; - struct internal_rail *rail = find_rail(rail_id); - - spin_lock_irqsave(&power_rail_lock, flags); - rc = msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &rail_id, &mode); - if (rc) - goto out; - if (rail_id) { - rc = -EINVAL; - goto out; - } - - if (rail) - rail->mode = mode; -out: - spin_unlock_irqrestore(&power_rail_lock, flags); - return rc; -} -EXPORT_SYMBOL(internal_pwr_rail_mode); - From 932e215f1d1f48098828e73ada197ad8aaff2bd8 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 5 Feb 2012 23:35:40 -0600 Subject: [PATCH 2035/2556] drivers: msm: hdmi: fix build on supersonic --- drivers/video/msm/hdmi/fb-hdmi.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/video/msm/hdmi/fb-hdmi.c b/drivers/video/msm/hdmi/fb-hdmi.c index fe024718f25a8..ec3bc0ccadcbd 100644 --- a/drivers/video/msm/hdmi/fb-hdmi.c +++ b/drivers/video/msm/hdmi/fb-hdmi.c @@ -311,7 +311,7 @@ static int hdmifb_blit(struct fb_info *info, void __user *p) _hdmi_fb->doubleBuffering = 0; /* Are we rotating? */ - if ((req.flags & MDP_ROT_MASK) == MDP_ROT_90 || (req.flags & MDP_ROT_MASK) == MDP_ROT_270) + if ((req.flags & 0x7) == MDP_ROT_90 || (req.flags & 0x7) == MDP_ROT_270) { /* Are we scaling at the same time? */ if (req.src_rect.w != req.dst_rect.w || req.src_rect.h != req.dst_rect.h) @@ -323,8 +323,8 @@ static int hdmifb_blit(struct fb_info *info, void __user *p) if (previousValue == 1 && _hdmi_fb->doubleBuffering == 0) { // Switch us back to buffer 0 - mdp->dma(mdp, _hdmi_fb->fb->fix.smem_start, _hdmi_fb->fb->var.xres * 2, - _hdmi_fb->fb->var.xres, _hdmi_fb->fb->var.yres, 0, 0, + mdp->dma(mdp, _hdmi_fb->fb->fix.smem_start, _hdmi_fb->fb->var.xres * 2, + _hdmi_fb->fb->var.xres, _hdmi_fb->fb->var.yres, 0, 0, &_hdmi_fb->dma_callback, _hdmi_fb->panel->interface_type); } } @@ -343,7 +343,7 @@ void reportUnderflow(void) int hdmi_usePanelSync(void) { - if (_hdmi_fb->mirroring && + if (_hdmi_fb->mirroring && (_hdmi_fb->vsyncMode == VSYNC_NONE || _hdmi_fb->vsyncMode == VSYNC_HDMI_ONLY)) return 0; return 1; @@ -351,7 +351,7 @@ int hdmi_usePanelSync(void) int hdmi_useHdmiSync(void) { - if (_hdmi_fb->mirroring && + if (_hdmi_fb->mirroring && (_hdmi_fb->vsyncMode == VSYNC_NONE || _hdmi_fb->vsyncMode == VSYNC_PANEL_ONLY)) return 0; return 1; @@ -405,8 +405,8 @@ void hdmi_DoBlit(int fb0Offset) { // Force us back to the primary buffer _hdmi_fb->mirrorReq.dst.offset = 0; - mdp->dma(mdp, _hdmi_fb->fb->fix.smem_start, _hdmi_fb->fb->var.xres * 2, - _hdmi_fb->fb->var.xres, _hdmi_fb->fb->var.yres, 0, 0, + mdp->dma(mdp, _hdmi_fb->fb->fix.smem_start, _hdmi_fb->fb->var.xres * 2, + _hdmi_fb->fb->var.xres, _hdmi_fb->fb->var.yres, 0, 0, &_hdmi_fb->dma_callback, _hdmi_fb->panel->interface_type); } } @@ -425,13 +425,13 @@ void hdmi_DoBlit(int fb0Offset) { if (hdmi_useHdmiSync()) { - hdmifb_pan_update(_hdmi_fb->fb, 0, 0, + hdmifb_pan_update(_hdmi_fb->fb, 0, 0, _hdmi_fb->fb->var.xres, _hdmi_fb->fb->var.yres, yoffset); } else { - mdp->dma(mdp, _hdmi_fb->mirrorReq.dst.offset + _hdmi_fb->fb->fix.smem_start, - _hdmi_fb->fb->var.xres * 2, _hdmi_fb->fb->var.xres, _hdmi_fb->fb->var.yres, 0, 0, + mdp->dma(mdp, _hdmi_fb->mirrorReq.dst.offset + _hdmi_fb->fb->fix.smem_start, + _hdmi_fb->fb->var.xres * 2, _hdmi_fb->fb->var.xres, _hdmi_fb->fb->var.yres, 0, 0, &_hdmi_fb->dma_callback, _hdmi_fb->panel->interface_type); } } From 2622579cf8a37ed26f1aef182e67fa30b89da3fa Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 14 Dec 2011 12:05:29 -0500 Subject: [PATCH 2036/2556] power: Add option to log time spent in suspend Prints the time spent in suspend in the kernel log, and keeps statistics on the time spent in suspend in /sys/kernel/debug/suspend_time Change-Id: Ia6b9ebe4baa0f7f5cd211c6a4f7e813aefd3fa1d Signed-off-by: Colin Cross Signed-off-by: Todd Poynor --- kernel/power/Kconfig | 7 +++ kernel/power/Makefile | 1 + kernel/power/suspend_time.c | 111 ++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 kernel/power/suspend_time.c diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index d4d3d180abb44..871e458e1ffc8 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -325,3 +325,10 @@ config PM_OPP representing individual voltage domains and provides SOC implementations a ready to use framework to manage OPPs. For more information, read + +config SUSPEND_TIME + bool "Log time spent in suspend" + ---help--- + Prints the time spent in suspend in the kernel log, and + keeps statistics on the time spent in suspend in + /sys/kernel/debug/suspend_time diff --git a/kernel/power/Makefile b/kernel/power/Makefile index d128ccea88648..bd4e091c411b8 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -12,5 +12,6 @@ obj-$(CONFIG_USER_WAKELOCK) += userwakelock.o obj-$(CONFIG_EARLYSUSPEND) += earlysuspend.o obj-$(CONFIG_CONSOLE_EARLYSUSPEND) += consoleearlysuspend.o obj-$(CONFIG_FB_EARLYSUSPEND) += fbearlysuspend.o +obj-$(CONFIG_SUSPEND_TIME) += suspend_time.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o diff --git a/kernel/power/suspend_time.c b/kernel/power/suspend_time.c new file mode 100644 index 0000000000000..d2a65da9f22c1 --- /dev/null +++ b/kernel/power/suspend_time.c @@ -0,0 +1,111 @@ +/* + * debugfs file to track time spent in suspend + * + * Copyright (c) 2011, Google, 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct timespec suspend_time_before; +static unsigned int time_in_suspend_bins[32]; + +#ifdef CONFIG_DEBUG_FS +static int suspend_time_debug_show(struct seq_file *s, void *data) +{ + int bin; + seq_printf(s, "time (secs) count\n"); + seq_printf(s, "------------------\n"); + for (bin = 0; bin < 32; bin++) { + if (time_in_suspend_bins[bin] == 0) + continue; + seq_printf(s, "%4d - %4d %4u\n", + bin ? 1 << (bin - 1) : 0, 1 << bin, + time_in_suspend_bins[bin]); + } + return 0; +} + +static int suspend_time_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, suspend_time_debug_show, NULL); +} + +static const struct file_operations suspend_time_debug_fops = { + .open = suspend_time_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init suspend_time_debug_init(void) +{ + struct dentry *d; + + d = debugfs_create_file("suspend_time", 0755, NULL, NULL, + &suspend_time_debug_fops); + if (!d) { + pr_err("Failed to create suspend_time debug file\n"); + return -ENOMEM; + } + + return 0; +} + +late_initcall(suspend_time_debug_init); +#endif + +static int suspend_time_syscore_suspend(void) +{ + read_persistent_clock(&suspend_time_before); + + return 0; +} + +static void suspend_time_syscore_resume(void) +{ + struct timespec after; + + read_persistent_clock(&after); + + after = timespec_sub(after, suspend_time_before); + + time_in_suspend_bins[fls(after.tv_sec)]++; + + pr_info("Suspended for %lu.%03lu seconds\n", after.tv_sec, + after.tv_nsec / NSEC_PER_MSEC); +} + +static struct syscore_ops suspend_time_syscore_ops = { + .suspend = suspend_time_syscore_suspend, + .resume = suspend_time_syscore_resume, +}; + +static int suspend_time_syscore_init(void) +{ + register_syscore_ops(&suspend_time_syscore_ops); + + return 0; +} + +static void suspend_time_syscore_exit(void) +{ + unregister_syscore_ops(&suspend_time_syscore_ops); +} +module_init(suspend_time_syscore_init); +module_exit(suspend_time_syscore_exit); From d3f7a9c0984e99cfa90ad3119fa30e1acc4b9e48 Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Sat, 21 May 2011 08:59:30 -0600 Subject: [PATCH 2037/2556] msm: kgsl: remove implicit dependencies on pmem and ashmem These drivers can be used if present, but they are not mandatory requirements. Change-Id: Id0c4292f73d448bf83cb0874eb7a0b1f78ac9a47 Signed-off-by: Jeremy Gebben --- drivers/gpu/msm/Kconfig | 2 +- drivers/gpu/msm/kgsl.c | 33 ++++++++++++++++++++++++++------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/msm/Kconfig b/drivers/gpu/msm/Kconfig index 7a183fc140c49..620fa763671e5 100644 --- a/drivers/gpu/msm/Kconfig +++ b/drivers/gpu/msm/Kconfig @@ -1,7 +1,7 @@ config MSM_KGSL tristate "MSM 3D Graphics driver" default n - depends on FB && ARM && ARCH_MSM && !MSM_HW3D && ANDROID_PMEM + depends on FB && ARM && ARCH_MSM && !MSM_HW3D select GENERIC_ALLOCATOR select FW_LOADER select RELAY diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index a8f00e3896819..23a5974fb6c55 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1178,6 +1178,14 @@ kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_device_private *dev_priv, return result; } +static inline int _check_region(unsigned long start, unsigned long size, + uint64_t len) +{ + uint64_t end = start + size; + return (end > len); +} + +#ifdef CONFIG_ANDROID_PMEM static int kgsl_get_phys_file(int fd, unsigned long *start, unsigned long *len, unsigned long *vstart, struct file **filep) { @@ -1214,13 +1222,6 @@ static int kgsl_get_phys_file(int fd, unsigned long *start, unsigned long *len, return ret; } -static inline int _check_region(unsigned long start, unsigned long size, - uint64_t len) -{ - uint64_t end = start + size; - return (end > len); -} - static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, struct kgsl_pagetable *pagetable, unsigned int fd, unsigned int offset, @@ -1260,6 +1261,15 @@ static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, return 0; } +#else +static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, + struct kgsl_pagetable *pagetable, + unsigned int fd, unsigned int offset, + size_t size) +{ + return -EINVAL; +} +#endif static int kgsl_setup_hostptr(struct kgsl_mem_entry *entry, struct kgsl_pagetable *pagetable, @@ -1312,6 +1322,7 @@ static int kgsl_setup_hostptr(struct kgsl_mem_entry *entry, return 0; } +#ifdef CONFIG_ASHMEM static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry, struct kgsl_pagetable *pagetable, int fd, void *hostptr, size_t size) @@ -1362,6 +1373,14 @@ static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry, put_ashmem_file(filep); return ret; } +#else +static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry, + struct kgsl_pagetable *pagetable, + int fd, void *hostptr, size_t size) +{ + return -EINVAL; +} +#endif static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, unsigned int cmd, void *data) From fe1b379285a2b3979806e92e62520c8d7105f38c Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Wed, 25 May 2011 12:55:09 -0600 Subject: [PATCH 2038/2556] msm: kgsl: cleanup statistics Rename the sysfs files for the statistics to represent more generic categories, and add a new driver wide statistic to track externally mapped memory. Cleanup and consolidate the sysfs functions and use the proper macros where applicable. Change-Id: Ia30788728dada84a2630b2dd433ac9f26bed0dc4 Signed-off-by: Jordan Crouse Conflicts: drivers/gpu/msm/kgsl_sharedmem.c --- drivers/gpu/msm/kgsl.c | 34 +++-- drivers/gpu/msm/kgsl.h | 6 +- drivers/gpu/msm/kgsl_device.h | 8 +- drivers/gpu/msm/kgsl_sharedmem.c | 250 +++++++++---------------------- 4 files changed, 99 insertions(+), 199 deletions(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 23a5974fb6c55..2aae2a3b3454c 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -59,13 +59,14 @@ kgsl_mem_entry_destroy(struct kref *kref) kgsl_sharedmem_free(&entry->memdesc); - if (entry->memtype == KGSL_VMALLOC_MEMORY) - entry->priv->stats.vmalloc -= size; - else { + if (entry->memtype == KGSL_USER_MEMORY) + entry->priv->stats.user -= size; + else if (entry->memtype == KGSL_MAPPED_MEMORY) { if (entry->file_ptr) fput(entry->file_ptr); - entry->priv->stats.exmem -= size; + kgsl_driver.stats.mapped -= size; + entry->priv->stats.mapped -= size; } kfree(entry); @@ -519,9 +520,9 @@ kgsl_put_process_private(struct kgsl_device *device, goto unlock; KGSL_MEM_INFO(device, - "Memory usage: vmalloc (%d/%d) exmem (%d/%d)\n", - private->stats.vmalloc, private->stats.vmalloc_max, - private->stats.exmem, private->stats.exmem_max); + "Memory usage: user (%d/%d) mapped (%d/%d)\n", + private->stats.user, private->stats.user_max, + private->stats.mapped, private->stats.mapped_max); kgsl_process_uninit_sysfs(private); @@ -1156,13 +1157,13 @@ kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_device_private *dev_priv, param->gpuaddr = entry->memdesc.gpuaddr; - entry->memtype = KGSL_VMALLOC_MEMORY; + entry->memtype = KGSL_USER_MEMORY; kgsl_mem_entry_attach_process(entry, private); /* Process specific statistics */ - KGSL_STATS_ADD(len, private->stats.vmalloc, - private->stats.vmalloc_max); + KGSL_STATS_ADD(len, private->stats.user, + private->stats.user_max); kgsl_check_idle(dev_priv->device); return 0; @@ -1456,11 +1457,14 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, /* Adjust the returned value for a non 4k aligned offset */ param->gpuaddr = entry->memdesc.gpuaddr + (param->offset & ~PAGE_MASK); - entry->memtype = KGSL_EXTERNAL_MEMORY; + entry->memtype = KGSL_MAPPED_MEMORY; + + KGSL_STATS_ADD(param->len, kgsl_driver.stats.mapped, + kgsl_driver.stats.mapped_max); /* Statistics */ - KGSL_STATS_ADD(param->len, private->stats.exmem, - private->stats.exmem_max); + KGSL_STATS_ADD(param->len, private->stats.mapped, + private->stats.mapped_max); kgsl_mem_entry_attach_process(entry, private); @@ -1536,8 +1540,12 @@ kgsl_ioctl_gpumem_alloc(struct kgsl_device_private *dev_priv, param->size, param->flags); if (result == 0) { + entry->memtype = KGSL_USER_MEMORY; kgsl_mem_entry_attach_process(entry, private); param->gpuaddr = entry->memdesc.gpuaddr; + + KGSL_STATS_ADD(entry->memdesc.size, private->stats.user, + private->stats.user_max); } else kfree(entry); diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index d128ac275c0f8..bc43b7bc5e832 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -132,14 +132,16 @@ struct kgsl_driver { unsigned int vmalloc_max; unsigned int coherent; unsigned int coherent_max; + unsigned int mapped; + unsigned int mapped_max; unsigned int histogram[16]; } stats; }; extern struct kgsl_driver kgsl_driver; -#define KGSL_VMALLOC_MEMORY 0 -#define KGSL_EXTERNAL_MEMORY 1 +#define KGSL_USER_MEMORY 1 +#define KGSL_MAPPED_MEMORY 2 struct kgsl_mem_entry { struct kref refcount; diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index e24daa094781c..ddd96328042d3 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -198,10 +198,10 @@ struct kgsl_process_private { struct kobject *kobj; struct { - unsigned int vmalloc; - unsigned int vmalloc_max; - unsigned int exmem; - unsigned int exmem_max; + unsigned int user; + unsigned int user_max; + unsigned int mapped; + unsigned int mapped_max; unsigned int flushes; } stats; }; diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index 1e22ddab14120..47e9ae5af934b 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -40,134 +40,51 @@ _get_priv_from_kobj(struct kobject *kobj) /* sharedmem / memory sysfs files */ static ssize_t -process_show_vmalloc(struct kobject *kobj, - struct kobj_attribute *attr, - char *buf) +process_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) { - - struct kgsl_process_private *priv; - int ret = 0; - - mutex_lock(&kgsl_driver.process_mutex); - priv = _get_priv_from_kobj(kobj); - - if (priv) - ret += sprintf(buf, "%d\n", priv->stats.vmalloc); - - mutex_unlock(&kgsl_driver.process_mutex); - return ret; -} - -static ssize_t -process_show_vmalloc_max(struct kobject *kobj, - struct kobj_attribute *attr, - char *buf) -{ - - struct kgsl_process_private *priv; - int ret = 0; - - mutex_lock(&kgsl_driver.process_mutex); - priv = _get_priv_from_kobj(kobj); - - if (priv) - ret += sprintf(buf, "%d\n", priv->stats.vmalloc_max); - - mutex_unlock(&kgsl_driver.process_mutex); - return ret; -} - -static ssize_t -process_show_exmem(struct kobject *kobj, - struct kobj_attribute *attr, - char *buf) -{ - - struct kgsl_process_private *priv; - int ret = 0; - - mutex_lock(&kgsl_driver.process_mutex); - priv = _get_priv_from_kobj(kobj); - - if (priv) - ret += sprintf(buf, "%d\n", priv->stats.exmem); - - mutex_unlock(&kgsl_driver.process_mutex); - return ret; -} - -static ssize_t -process_show_exmem_max(struct kobject *kobj, - struct kobj_attribute *attr, - char *buf) -{ - struct kgsl_process_private *priv; - int ret = 0; + unsigned int val = 0; mutex_lock(&kgsl_driver.process_mutex); priv = _get_priv_from_kobj(kobj); - if (priv) - ret += sprintf(buf, "%d\n", priv->stats.exmem_max); - - mutex_unlock(&kgsl_driver.process_mutex); - return ret; -} - -static ssize_t -process_show_flushes(struct kobject *kobj, - struct kobj_attribute *attr, - char *buf) -{ - struct kgsl_process_private *priv; - int ret = 0; - - mutex_lock(&kgsl_driver.process_mutex); - priv = _get_priv_from_kobj(kobj); + if (priv == NULL) { + mutex_unlock(&kgsl_driver.process_mutex); + return 0; + } - if (priv) - ret += sprintf(buf, "%d\n", priv->stats.flushes); + if (!strncmp(attr->attr.name, "user", 4)) + val = priv->stats.user; + if (!strncmp(attr->attr.name, "user_max", 8)) + val = priv->stats.user_max; + if (!strncmp(attr->attr.name, "mapped", 6)) + val = priv->stats.mapped; + if (!strncmp(attr->attr.name, "mapped_max", 10)) + val = priv->stats.mapped_max; + if (!strncmp(attr->attr.name, "flushes", 7)) + val = priv->stats.flushes; mutex_unlock(&kgsl_driver.process_mutex); - return ret; + return snprintf(buf, PAGE_SIZE, "%u\n", val); } -static struct kobj_attribute attr_vmalloc = { - .attr = { .name = "vmalloc", .mode = 0444 }, - .show = process_show_vmalloc, - .store = NULL, -}; - -static struct kobj_attribute attr_vmalloc_max = { - .attr = { .name = "vmalloc_max", .mode = 0444 }, - .show = process_show_vmalloc_max, - .store = NULL, -}; +#define KGSL_MEMSTAT_ATTR(_name, _show) \ + static struct kobj_attribute attr_##_name = \ + __ATTR(_name, 0444, _show, NULL) -static struct kobj_attribute attr_exmem = { - .attr = { .name = "exmem", .mode = 0444 }, - .show = process_show_exmem, - .store = NULL, -}; - -static struct kobj_attribute attr_exmem_max = { - .attr = { .name = "exmem_max", .mode = 0444 }, - .show = process_show_exmem_max, - .store = NULL, -}; - -static struct kobj_attribute attr_flushes = { - .attr = { .name = "flushes", .mode = 0444 }, - .show = process_show_flushes, - .store = NULL, -}; +KGSL_MEMSTAT_ATTR(user, process_show); +KGSL_MEMSTAT_ATTR(user_max, process_show); +KGSL_MEMSTAT_ATTR(mapped, process_show); +KGSL_MEMSTAT_ATTR(mapped_max, process_show); +KGSL_MEMSTAT_ATTR(flushes, process_show); static struct attribute *process_attrs[] = { - &attr_vmalloc.attr, - &attr_vmalloc_max.attr, - &attr_exmem.attr, - &attr_exmem_max.attr, + &attr_user.attr, + &attr_user_max.attr, + &attr_mapped.attr, + &attr_mapped_max.attr, &attr_flushes.attr, NULL }; @@ -204,32 +121,26 @@ kgsl_process_init_sysfs(struct kgsl_process_private *private) } } -static int kgsl_drv_vmalloc_show(struct device *dev, +static int kgsl_drv_memstat_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", kgsl_driver.stats.vmalloc); -} - -static int kgsl_drv_vmalloc_max_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%d\n", kgsl_driver.stats.vmalloc_max); -} + unsigned int val = 0; -static int kgsl_drv_coherent_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%d\n", kgsl_driver.stats.coherent); -} + if (!strncmp(attr->attr.name, "vmalloc", 7)) + val = kgsl_driver.stats.vmalloc; + else if (!strncmp(attr->attr.name, "vmalloc_max", 11)) + val = kgsl_driver.stats.vmalloc_max; + else if (!strncmp(attr->attr.name, "coherent", 8)) + val = kgsl_driver.stats.coherent; + else if (!strncmp(attr->attr.name, "coherent_max", 12)) + val = kgsl_driver.stats.coherent_max; + else if (!strncmp(attr->attr.name, "mapped", 6)) + val = kgsl_driver.stats.mapped; + else if (!strncmp(attr->attr.name, "mapped_max", 10)) + val = kgsl_driver.stats.mapped_max; -static int kgsl_drv_coherent_max_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%d\n", kgsl_driver.stats.coherent_max); + return snprintf(buf, PAGE_SIZE, "%u\n", val); } static int kgsl_drv_histogram_show(struct device *dev, @@ -247,61 +158,40 @@ static int kgsl_drv_histogram_show(struct device *dev, return len; } -static struct device_attribute drv_vmalloc_attr = { - .attr = { .name = "vmalloc", .mode = 0444, }, - .show = kgsl_drv_vmalloc_show, - .store = NULL, -}; - -static struct device_attribute drv_vmalloc_max_attr = { - .attr = { .name = "vmalloc_max", .mode = 0444, }, - .show = kgsl_drv_vmalloc_max_show, - .store = NULL, -}; - -static struct device_attribute drv_coherent_attr = { - .attr = { .name = "coherent", .mode = 0444, }, - .show = kgsl_drv_coherent_show, - .store = NULL, -}; - -static struct device_attribute drv_coherent_max_attr = { - .attr = { .name = "coherent_max", .mode = 0444, }, - .show = kgsl_drv_coherent_max_show, - .store = NULL, -}; - -static struct device_attribute drv_histogram_attr = { - .attr = { .name = "histogram", .mode = 0444, }, - .show = kgsl_drv_histogram_show, - .store = NULL, +DEVICE_ATTR(vmalloc, 0444, kgsl_drv_memstat_show, NULL); +DEVICE_ATTR(vmalloc_max, 0444, kgsl_drv_memstat_show, NULL); +DEVICE_ATTR(coherent, 0444, kgsl_drv_memstat_show, NULL); +DEVICE_ATTR(coherent_max, 0444, kgsl_drv_memstat_show, NULL); +DEVICE_ATTR(mapped, 0444, kgsl_drv_memstat_show, NULL); +DEVICE_ATTR(mapped_max, 0444, kgsl_drv_memstat_show, NULL); +DEVICE_ATTR(histogram, 0444, kgsl_drv_histogram_show, NULL); + +static const struct device_attribute *drv_attr_list[] = { + &dev_attr_vmalloc, + &dev_attr_vmalloc_max, + &dev_attr_coherent, + &dev_attr_coherent_max, + &dev_attr_mapped, + &dev_attr_mapped_max, + &dev_attr_histogram, }; void kgsl_sharedmem_uninit_sysfs(void) { - device_remove_file(&kgsl_driver.virtdev, &drv_vmalloc_attr); - device_remove_file(&kgsl_driver.virtdev, &drv_vmalloc_max_attr); - device_remove_file(&kgsl_driver.virtdev, &drv_coherent_attr); - device_remove_file(&kgsl_driver.virtdev, &drv_coherent_max_attr); - device_remove_file(&kgsl_driver.virtdev, &drv_histogram_attr); + int i; + for (i = 0; i < ARRAY_SIZE(drv_attr_list); i++) + device_remove_file(&kgsl_driver.virtdev, drv_attr_list[i]); } int kgsl_sharedmem_init_sysfs(void) { - int ret; - - ret = device_create_file(&kgsl_driver.virtdev, - &drv_vmalloc_attr); - ret |= device_create_file(&kgsl_driver.virtdev, - &drv_vmalloc_max_attr); - ret |= device_create_file(&kgsl_driver.virtdev, - &drv_coherent_attr); - ret |= device_create_file(&kgsl_driver.virtdev, - &drv_coherent_max_attr); - ret |= device_create_file(&kgsl_driver.virtdev, - &drv_histogram_attr); + int ret = 0, i; + + for (i = 0; i < ARRAY_SIZE(drv_attr_list); i++) + ret |= device_create_file(&kgsl_driver.virtdev, + drv_attr_list[i]); return ret; } From d93f11323b853e8b3a3ef708b89493cdd10786c7 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Fri, 27 May 2011 11:05:16 -0600 Subject: [PATCH 2039/2556] msm: kgsl: Rename and expand calc_idle Rather then just calculating the idle time and returning it, there is value in returning the raw statistics and let the caller use them as they see fit. Rename calc_idle to power_stats, and use a structure to pass back the total_time and busy_time. The structure allows for future expansion as well as providing enough information for others to perform interesting power and load related calculations. Change-Id: I5132060fd6c4c6867b39a15fccb2380108394225 Signed-off-by: Jordan Crouse --- drivers/gpu/msm/adreno.c | 19 ++++++++++--------- drivers/gpu/msm/kgsl_device.h | 9 ++++++++- drivers/gpu/msm/kgsl_pwrctrl.c | 10 ++++++---- drivers/gpu/msm/z180.c | 8 +++++--- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 16cc1fa4d63c0..0ea5bfd5ab56f 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1230,15 +1230,16 @@ static inline s64 adreno_ticks_to_us(u32 ticks, u32 gpu_freq) return ticks / gpu_freq; } -static unsigned int adreno_idle_calc(struct kgsl_device *device) +static void adreno_power_stats(struct kgsl_device *device, + struct kgsl_power_stats *stats) { - unsigned int ret, reg; + unsigned int reg; struct kgsl_pwrctrl *pwr = &device->pwrctrl; /* In order to calculate idle you have to have run the algorithm * * at least once to get a start time. */ if (pwr->time != 0) { - s64 total_time, busy_time, tmp; + s64 tmp; /* Stop the performance moniter and read the current * * busy cycles. */ adreno_regwrite(device, @@ -1247,19 +1248,20 @@ static unsigned int adreno_idle_calc(struct kgsl_device *device) REG_PERF_STATE_FREEZE); adreno_regread(device, REG_RBBM_PERFCOUNTER1_LO, ®); tmp = ktime_to_us(ktime_get()); - total_time = tmp - pwr->time; + stats->total_time = tmp - pwr->time; pwr->time = tmp; - busy_time = adreno_ticks_to_us(reg, device->pwrctrl. + stats->busy_time = adreno_ticks_to_us(reg, device->pwrctrl. pwrlevels[device->pwrctrl.active_pwrlevel]. gpu_freq); - ret = total_time - busy_time; + adreno_regwrite(device, REG_CP_PERFMON_CNTL, REG_PERF_MODE_CNT | REG_PERF_STATE_RESET); } else { + stats->total_time = 0; + stats->busy_time = 0; pwr->time = ktime_to_us(ktime_get()); - ret = 0; } /* re-enable the performance moniters */ @@ -1269,7 +1271,6 @@ static unsigned int adreno_idle_calc(struct kgsl_device *device) adreno_regwrite(device, REG_CP_PERFMON_CNTL, REG_PERF_MODE_CNT | REG_PERF_STATE_ENABLE); - return ret; } static void __devinit adreno_getfunctable(struct kgsl_functable *ftbl) @@ -1296,7 +1297,7 @@ static void __devinit adreno_getfunctable(struct kgsl_functable *ftbl) ftbl->device_ioctl = adreno_ioctl; ftbl->device_setup_pt = adreno_setup_pt; ftbl->device_cleanup_pt = adreno_cleanup_pt; - ftbl->device_idle_calc = adreno_idle_calc; + ftbl->device_power_stats = adreno_power_stats; } static struct platform_device_id adreno_id_table[] = { diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index ddd96328042d3..2a0b4f6a8cb29 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -72,6 +72,7 @@ struct kgsl_device; struct platform_device; struct kgsl_device_private; struct kgsl_context; +struct kgsl_power_stats; struct kgsl_functable { void (*device_regread) (struct kgsl_device *device, @@ -121,7 +122,8 @@ struct kgsl_functable { int (*device_cleanup_pt)(struct kgsl_device *device, struct kgsl_pagetable *pagetable); - unsigned int (*device_idle_calc)(struct kgsl_device *device); + void (*device_power_stats)(struct kgsl_device *device, + struct kgsl_power_stats *stats); }; struct kgsl_memregion { @@ -211,6 +213,11 @@ struct kgsl_device_private { struct kgsl_process_private *process_priv; }; +struct kgsl_power_stats { + s64 total_time; + s64 busy_time; +}; + struct kgsl_device *kgsl_get_device(int dev_idx); static inline struct kgsl_mmu * diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 51982ac462b13..0e14d09da4360 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -242,11 +242,13 @@ void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device) static void kgsl_pwrctrl_idle_calc(struct kgsl_device *device) { - int idle, val; + int val; struct kgsl_pwrctrl *pwr = &device->pwrctrl; + struct kgsl_power_stats stats; - idle = device->ftbl.device_idle_calc(device); - if (!idle) + device->ftbl.device_power_stats(device, &stats); + + if (stats.total_time == 0) return; /* If the GPU has stayed in turbo mode for a while, * @@ -256,7 +258,7 @@ static void kgsl_pwrctrl_idle_calc(struct kgsl_device *device) else if (pwr->no_switch_cnt > SWITCH_OFF) return; pwr->no_switch_cnt++; - val = kgsl_pwrctrl_tz_update(idle); + val = kgsl_pwrctrl_tz_update(stats.total_time - stats.busy_time); if (val) kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel + val); diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c index b4df869a2a8eb..4d7a31f80de48 100644 --- a/drivers/gpu/msm/z180.c +++ b/drivers/gpu/msm/z180.c @@ -999,9 +999,11 @@ static long z180_ioctl(struct kgsl_device_private *dev_priv, } -static unsigned int z180_idle_calc(struct kgsl_device *device) +static void z180_power_stats(struct kgsl_device *device, + struct kgsl_power_stats *stats) { - return device->pwrctrl.time; + stats->total_time = 0; + stats->busy_time = 0; } static void __devinit z180_getfunctable(struct kgsl_functable *ftbl) @@ -1028,7 +1030,7 @@ static void __devinit z180_getfunctable(struct kgsl_functable *ftbl) ftbl->device_ioctl = z180_ioctl; ftbl->device_setup_pt = z180_setup_pt; ftbl->device_cleanup_pt = z180_cleanup_pt; - ftbl->device_idle_calc = z180_idle_calc; + ftbl->device_power_stats = z180_power_stats, } static struct platform_device_id z180_id_table[] = { From 63418f77b87428207945bb30ed943007275b599e Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Mon, 6 Jun 2011 09:29:47 -0600 Subject: [PATCH 2040/2556] msm: kgsl: Cleanup sysfs device file creation Take the generic sysfs create/remove functions from kgsl_sharedmem.c and share their creamy goodness with the rest of the driver. Change-Id: Ia5ba94d23ed551289fcb6fbb196c1749e6c43b65 Signed-off-by: Jordan Crouse --- drivers/gpu/msm/kgsl.h | 17 ++++++++ drivers/gpu/msm/kgsl_pwrctrl.c | 74 +++++++++++++++++++++----------- drivers/gpu/msm/kgsl_sharedmem.c | 14 ++---- 3 files changed, 71 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index bc43b7bc5e832..6696c7a7be3a3 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -270,4 +270,21 @@ kgsl_mem_entry_put(struct kgsl_mem_entry *entry) kref_put(&entry->refcount, kgsl_mem_entry_destroy); } +static inline int kgsl_create_device_sysfs_files(struct device *root, + const struct device_attribute **list) +{ + int ret = 0, i; + for (i = 0; list[i] != NULL; i++) + ret |= device_create_file(root, list[i]); + return ret; +} + +static inline void kgsl_remove_device_sysfs_files(struct device *root, + const struct device_attribute **list) +{ + int i; + for (i = 0; list[i] != NULL; i++) + device_remove_file(root, list[i]); +} + #endif /* __KGSL_H */ diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 0e14d09da4360..fae90805f61b0 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -204,40 +204,66 @@ static int kgsl_pwrctrl_idle_timer_show(struct device *dev, return sprintf(buf, "%d\n", pwr->interval_timeout); } -static struct device_attribute gpuclk_attr = { - .attr = { .name = "gpuclk", .mode = 0644, }, - .show = kgsl_pwrctrl_gpuclk_show, - .store = kgsl_pwrctrl_gpuclk_store, -}; +static int kgsl_pwrctrl_scaling_governor_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char temp[20]; + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + unsigned int reset = pwr->idle_pass; -static struct device_attribute pwrnap_attr = { - .attr = { .name = "pwrnap", .mode = 0644, }, - .show = kgsl_pwrctrl_pwrnap_show, - .store = kgsl_pwrctrl_pwrnap_store, -}; + snprintf(temp, sizeof(temp), "%.*s", + (int)min(count, sizeof(temp) - 1), buf); + if (strncmp(temp, "ondemand", 8) == 0) + reset = 1; + else if (strncmp(temp, "performance", 11) == 0) + reset = 0; + + mutex_lock(&device->mutex); + pwr->idle_pass = reset; + if (pwr->idle_pass == 0) + kgsl_pwrctrl_pwrlevel_change(device, pwr->thermal_pwrlevel); + mutex_unlock(&device->mutex); + + return count; +} + +static int kgsl_pwrctrl_scaling_governor_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + if (pwr->idle_pass) + return snprintf(buf, 10, "ondemand\n"); + else + return snprintf(buf, 13, "performance\n"); +} -static struct device_attribute idle_timer_attr = { - .attr = { .name = "idle_timer", .mode = 0644, }, - .show = kgsl_pwrctrl_idle_timer_show, - .store = kgsl_pwrctrl_idle_timer_store, +DEVICE_ATTR(gpuclk, 0644, kgsl_pwrctrl_gpuclk_show, kgsl_pwrctrl_gpuclk_store); +DEVICE_ATTR(pwrnap, 0644, kgsl_pwrctrl_pwrnap_show, kgsl_pwrctrl_pwrnap_store); +DEVICE_ATTR(idle_timer, 0644, kgsl_pwrctrl_idle_timer_show, + kgsl_pwrctrl_idle_timer_store); +DEVICE_ATTR(scaling_governor, 0644, kgsl_pwrctrl_scaling_governor_show, + kgsl_pwrctrl_scaling_governor_store); + +static const struct device_attribute *pwrctrl_attr_list[] = { + &dev_attr_gpuclk, + &dev_attr_pwrnap, + &dev_attr_idle_timer, + &dev_attr_scaling_governor, + NULL }; int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device) { - int ret = 0; - ret = device_create_file(device->dev, &pwrnap_attr); - if (ret == 0) - ret = device_create_file(device->dev, &gpuclk_attr); - if (ret == 0) - ret = device_create_file(device->dev, &idle_timer_attr); - return ret; + return kgsl_create_device_sysfs_files(device->dev, pwrctrl_attr_list); } void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device) { - device_remove_file(device->dev, &gpuclk_attr); - device_remove_file(device->dev, &pwrnap_attr); - device_remove_file(device->dev, &idle_timer_attr); + kgsl_remove_device_sysfs_files(device->dev, pwrctrl_attr_list); } static void kgsl_pwrctrl_idle_calc(struct kgsl_device *device) diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index 47e9ae5af934b..92d9517c4bc1a 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -174,26 +174,20 @@ static const struct device_attribute *drv_attr_list[] = { &dev_attr_mapped, &dev_attr_mapped_max, &dev_attr_histogram, + NULL }; void kgsl_sharedmem_uninit_sysfs(void) { - int i; - for (i = 0; i < ARRAY_SIZE(drv_attr_list); i++) - device_remove_file(&kgsl_driver.virtdev, drv_attr_list[i]); + kgsl_remove_device_sysfs_files(&kgsl_driver.virtdev, drv_attr_list); } int kgsl_sharedmem_init_sysfs(void) { - int ret = 0, i; - - for (i = 0; i < ARRAY_SIZE(drv_attr_list); i++) - ret |= device_create_file(&kgsl_driver.virtdev, - drv_attr_list[i]); - - return ret; + return kgsl_create_device_sysfs_files(&kgsl_driver.virtdev, + drv_attr_list); } #ifdef CONFIG_OUTER_CACHE From e396ea409bc4db2361f94b2b1f2f2d73b111a1f6 Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Mon, 6 Jun 2011 15:59:03 -0600 Subject: [PATCH 2041/2556] msm: kgsl: Fix gpuaddr to vaddr conversion The virtual address for a given gpu address was being calculated incorrectly, instead of adding the offset to the base virtual address the offset was being subtracted. Change-Id: I769d7e9d2cfd0dfe03a8c6cc19558e5455fa9b49 Signed-off-by: Shubhraprakash Das --- drivers/gpu/msm/kgsl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 2aae2a3b3454c..7bb141fc0df14 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -724,8 +724,8 @@ uint8_t *kgsl_gpuaddr_to_vaddr(const struct kgsl_memdesc *memdesc, gpuaddr >= memdesc->gpuaddr + memdesc->size)) return NULL; - *size = memdesc->size - (memdesc->gpuaddr - gpuaddr); - return memdesc->hostptr + (memdesc->gpuaddr - gpuaddr); + *size = memdesc->size - (gpuaddr - memdesc->gpuaddr); + return memdesc->hostptr + (gpuaddr - memdesc->gpuaddr); } EXPORT_SYMBOL(kgsl_gpuaddr_to_vaddr); From 630edb3e1954b7211ab1847e66fce9c62dea8b2d Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Wed, 8 Jun 2011 16:34:29 -0600 Subject: [PATCH 2042/2556] msm: kgsl: Fix mmu pagefaults after recovery Add the context switch identifier code before changing pagetables Change-Id: I4a418c810eea8b788b9d7cde2f7020424d177887 CRs-fixed: 285814 Signed-off-by: Shubhraprakash Das --- drivers/gpu/msm/adreno_drawctxt.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c index 27a246e3a10a5..85b7611e97d4c 100644 --- a/drivers/gpu/msm/adreno_drawctxt.c +++ b/drivers/gpu/msm/adreno_drawctxt.c @@ -1629,6 +1629,13 @@ adreno_drawctxt_switch(struct adreno_device *adreno_dev, KGSL_CTXT_INFO(device, "drawctxt flags %08x\n", drawctxt->flags); + cmds[0] = pm4_nop_packet(1); + cmds[1] = KGSL_CONTEXT_TO_MEM_IDENTIFIER; + cmds[2] = pm4_type3_packet(PM4_MEM_WRITE, 2); + cmds[3] = device->memstore.gpuaddr + + KGSL_DEVICE_MEMSTORE_OFFSET(current_context); + cmds[4] = (unsigned int)adreno_dev->drawctxt_active; + adreno_ringbuffer_issuecmds(device, 0, cmds, 5); kgsl_mmu_setstate(device, drawctxt->pagetable); #ifndef CONFIG_MSM_KGSL_CFF_DUMP_NO_CONTEXT_MEM_DUMP @@ -1637,13 +1644,6 @@ adreno_drawctxt_switch(struct adreno_device *adreno_dev, REG_SHADOW_SIZE + CMD_BUFFER_SIZE + TEX_SHADOW_SIZE, false); #endif - cmds[0] = pm4_nop_packet(1); - cmds[1] = KGSL_CONTEXT_TO_MEM_IDENTIFIER; - cmds[2] = pm4_type3_packet(PM4_MEM_WRITE, 2); - cmds[3] = device->memstore.gpuaddr + - KGSL_DEVICE_MEMSTORE_OFFSET(current_context); - cmds[4] = (unsigned int)adreno_dev->drawctxt_active; - adreno_ringbuffer_issuecmds(device, 0, cmds, 5); /* restore gmem. * (note: changes shader. shader must not already be restored.) From 9fe9da294611207ca86ad88f346dc520a6ba7b4d Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Wed, 8 Jun 2011 17:29:32 -0600 Subject: [PATCH 2043/2556] msm: kgsl: Fix access to ringbuffer in recovery code Whenever the read pointer is used to access the ringbuffer in recovery code looping has been introduced in places where it was missing Change-Id: I7d122f553ac999c73dc5c1c0b2fbbf17e354b18f CRs-fixed: 285120 Signed-off-by: Shubhraprakash Das --- drivers/gpu/msm/adreno_ringbuffer.c | 54 ++++++++++++++++++++--------- drivers/gpu/msm/adreno_ringbuffer.h | 7 ++++ 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index 912e2fcf4e9a8..607b32dd84203 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -763,17 +763,29 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, device, KGSL_TIMESTAMP_RETIRED); KGSL_DRV_ERR(device, "GPU successfully executed till ts: %x\n", retired_timestamp); - rb_rptr = (rb->rptr - 4) * sizeof(unsigned int); + /* + * We need to go back in history by 4 dwords from the current location + * of read pointer as 4 dwords are read to match the end of a command. + * Also, take care of wrap around when moving back + */ + if (rb->rptr >= 4) + rb_rptr = (rb->rptr - 4) * sizeof(unsigned int); + else + rb_rptr = rb->buffer_desc.size - + ((4 - rb->rptr) * sizeof(unsigned int)); /* Read the rb contents going backwards to locate end of last * sucessfully executed command */ while ((rb_rptr / sizeof(unsigned int)) != rb->wptr) { kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); if (value == retired_timestamp) { - rb_rptr += sizeof(unsigned int); + rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, + rb->buffer_desc.size); kgsl_sharedmem_readl(&rb->buffer_desc, &val1, rb_rptr); - rb_rptr += sizeof(unsigned int); + rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, + rb->buffer_desc.size); kgsl_sharedmem_readl(&rb->buffer_desc, &val2, rb_rptr); - rb_rptr += sizeof(unsigned int); + rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, + rb->buffer_desc.size); kgsl_sharedmem_readl(&rb->buffer_desc, &val3, rb_rptr); /* match the pattern found at the end of a command */ if ((val1 == 2 && @@ -783,20 +795,27 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, && val2 == CACHE_FLUSH_TS && val3 == (rb->device->memstore.gpuaddr + KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp)))) { - rb_rptr += sizeof(unsigned int); + rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, + rb->buffer_desc.size); KGSL_DRV_ERR(device, "Found end of last executed " "command at offset: %x\n", rb_rptr / sizeof(unsigned int)); break; } else { - rb_rptr -= (3 * sizeof(unsigned int)); + if (rb_rptr < (3 * sizeof(unsigned int))) + rb_rptr = rb->buffer_desc.size - + (3 * sizeof(unsigned int)) + + rb_rptr; + else + rb_rptr -= (3 * sizeof(unsigned int)); } } - rb_rptr -= sizeof(unsigned int); - if (rb_rptr < 0) + if (rb_rptr == 0) rb_rptr = rb->buffer_desc.size - sizeof(unsigned int); + else + rb_rptr -= sizeof(unsigned int); } if ((rb_rptr / sizeof(unsigned int)) == rb->wptr) { @@ -812,7 +831,8 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, * itself */ kgsl_sharedmem_readl(&rb->buffer_desc, &val1, rb_rptr); kgsl_sharedmem_readl(&rb->buffer_desc, &val2, - rb_rptr + sizeof(unsigned int)); + adreno_ringbuffer_inc_wrapped(rb_rptr, + rb->buffer_desc.size)); if (val1 == pm4_nop_packet(1) && val2 == KGSL_CMD_IDENTIFIER) { KGSL_DRV_ERR(device, "GPU recovery from hang not possible because " @@ -826,22 +846,22 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, KGSL_DEVICE_MEMSTORE_OFFSET(current_context)); while ((rb_rptr / sizeof(unsigned int)) != rb->wptr) { kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); - rb_rptr = (rb_rptr + sizeof(unsigned int)) % - rb->buffer_desc.size; + rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, + rb->buffer_desc.size); /* check for context switch indicator */ if (value == KGSL_CONTEXT_TO_MEM_IDENTIFIER) { kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); - rb_rptr = (rb_rptr + sizeof(unsigned int)) % - rb->buffer_desc.size; + rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, + rb->buffer_desc.size); BUG_ON(value != pm4_type3_packet(PM4_MEM_WRITE, 2)); kgsl_sharedmem_readl(&rb->buffer_desc, &val1, rb_rptr); - rb_rptr = (rb_rptr + sizeof(unsigned int)) % - rb->buffer_desc.size; + rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, + rb->buffer_desc.size); BUG_ON(val1 != (device->memstore.gpuaddr + KGSL_DEVICE_MEMSTORE_OFFSET(current_context))); kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); - rb_rptr = (rb_rptr + sizeof(unsigned int)) % - rb->buffer_desc.size; + rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, + rb->buffer_desc.size); BUG_ON((copy_rb_contents == 0) && (value == cur_context)); /* if context switches to a context that did not cause diff --git a/drivers/gpu/msm/adreno_ringbuffer.h b/drivers/gpu/msm/adreno_ringbuffer.h index d1919e65de444..a4b25c3fab0b6 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.h +++ b/drivers/gpu/msm/adreno_ringbuffer.h @@ -181,4 +181,11 @@ static inline int adreno_ringbuffer_count(struct adreno_ringbuffer *rb, return rb->wptr + rb->sizedwords - rptr; } +/* Increment a value by 4 bytes with wrap-around based on size */ +static inline unsigned int adreno_ringbuffer_inc_wrapped(unsigned int val, + unsigned int size) +{ + return (val + sizeof(unsigned int)) % size; +} + #endif /* __ADRENO_RINGBUFFER_H */ From 4cf8edb6e8a1cfbaf4dec4cff58893cf2af30622 Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Thu, 9 Jun 2011 13:03:41 -0600 Subject: [PATCH 2044/2556] msm: kgsl: remove implementation of IOCTL_KGSL_DEVICE_REGREAD This ioctl isn't used by userspace. Change-Id: If51a949edc6882a92aa925d5c3b2ec961462f7e8 Signed-off-by: Jeremy Gebben --- drivers/gpu/msm/kgsl.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 7bb141fc0df14..80b77ef86abc7 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -765,24 +765,6 @@ static long kgsl_ioctl_device_getproperty(struct kgsl_device_private *dev_priv, return result; } -static long kgsl_ioctl_device_regread(struct kgsl_device_private *dev_priv, - unsigned int cmd, void *data) -{ - struct kgsl_device_regread *param = data; - - if (param->offsetwords*sizeof(uint32_t) >= - dev_priv->device->regspace.sizebytes) { - KGSL_DRV_ERR(dev_priv->device, "invalid offset %d\n", - param->offsetwords); - return -ERANGE; - } - - kgsl_regread(dev_priv->device, param->offsetwords, ¶m->value); - - return 0; -} - - static long kgsl_ioctl_device_waittimestamp(struct kgsl_device_private *dev_priv, unsigned int cmd, void *data) @@ -1566,8 +1548,6 @@ static const struct { } kgsl_ioctl_funcs[] = { KGSL_IOCTL_FUNC(IOCTL_KGSL_DEVICE_GETPROPERTY, kgsl_ioctl_device_getproperty, 1), - KGSL_IOCTL_FUNC(IOCTL_KGSL_DEVICE_REGREAD, - kgsl_ioctl_device_regread, 1), KGSL_IOCTL_FUNC(IOCTL_KGSL_DEVICE_WAITTIMESTAMP, kgsl_ioctl_device_waittimestamp, 1), KGSL_IOCTL_FUNC(IOCTL_KGSL_RINGBUFFER_ISSUEIBCMDS, From 534e0c27142f5765fbb9d7b9d59e9bc5fa7c0ddf Mon Sep 17 00:00:00 2001 From: Shubhraprakash Das Date: Wed, 8 Jun 2011 17:55:53 -0600 Subject: [PATCH 2045/2556] msm: kgsl: Reconstruct the ringbuffer correctly during recovery Make sure while extracting commands the identifier commands do not get added twice. Change-Id: Ic1593ef091489c5bcbdbc3e06fe815f521752600 Signed-off-by: Shubhraprakash Das --- drivers/gpu/msm/adreno_ringbuffer.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index 607b32dd84203..c805a05757071 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -864,6 +864,13 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, rb->buffer_desc.size); BUG_ON((copy_rb_contents == 0) && (value == cur_context)); + /* + * If we were copying the commands and got to this point + * then we need to remove the 3 commands that appear + * before KGSL_CONTEXT_TO_MEM_IDENTIFIER + */ + if (temp_idx) + temp_idx -= 3; /* if context switches to a context that did not cause * hang then start saving the rb contents as those * commands can be executed */ @@ -880,10 +887,6 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, temp_rb_buffer[temp_idx++] = val1; temp_rb_buffer[temp_idx++] = value; } else { - /* if temp_idx is not 0 then we do not need to - * copy extra dwords indicating a kernel cmd */ - if (temp_idx) - temp_idx -= 3; copy_rb_contents = 0; } } else if (copy_rb_contents) From 7243b5491ce883657342908be42ccf14be273300 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Mon, 20 Jun 2011 11:43:12 -0600 Subject: [PATCH 2046/2556] msm: kgsl: Track the parent device for the driver Rather then store the platform_device pointer, remember the generic parent device which gets used in more places and makes the core code just slightly less dependent on platform devices in general. Change-Id: Ic0dedbade7b21732ff61c998dc1de68aa1f372af Signed-off-by: Jordan Crouse --- drivers/gpu/msm/adreno.c | 4 ++-- drivers/gpu/msm/kgsl.c | 20 +++++++++----------- drivers/gpu/msm/kgsl_device.h | 2 +- drivers/gpu/msm/kgsl_pwrctrl.c | 3 ++- drivers/gpu/msm/z180.c | 4 ++-- 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 0ea5bfd5ab56f..98fc9215ae4e6 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -442,7 +442,7 @@ adreno_probe(struct platform_device *pdev) device = (struct kgsl_device *)pdev->id_entry->driver_data; adreno_dev = ADRENO_DEVICE(device); - device->pdev = pdev; + device->parentdev = &pdev->dev; init_completion(&device->recovery_gate); @@ -464,7 +464,7 @@ adreno_probe(struct platform_device *pdev) error_close_rb: adreno_ringbuffer_close(&adreno_dev->ringbuffer); error: - device->pdev = NULL; + device->parentdev = NULL; return status; } diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 80b77ef86abc7..1311de86183c8 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -590,7 +590,7 @@ static int kgsl_release(struct inode *inodep, struct file *filep) kgsl_put_process_private(device, private); - pm_runtime_put(&device->pdev->dev); + pm_runtime_put(device->parentdev); return result; } @@ -600,7 +600,6 @@ static int kgsl_open(struct inode *inodep, struct file *filep) struct kgsl_device_private *dev_priv; struct kgsl_device *device; unsigned int minor = iminor(inodep); - struct device *dev; device = kgsl_get_minor(minor); BUG_ON(device == NULL); @@ -610,9 +609,7 @@ static int kgsl_open(struct inode *inodep, struct file *filep) return -EBUSY; } - dev = &device->pdev->dev; - - result = pm_runtime_get_sync(dev); + result = pm_runtime_get_sync(device->parentdev); if (result < 0) { KGSL_DRV_ERR(device, "Runtime PM: Unable to wake up the device, rc = %d\n", @@ -668,7 +665,7 @@ static int kgsl_open(struct inode *inodep, struct file *filep) filep->private_data = NULL; kfree(dev_priv); err_pmruntime: - pm_runtime_put(&device->pdev->dev); + pm_runtime_put(device->parentdev); return result; } @@ -1826,7 +1823,7 @@ kgsl_register_device(struct kgsl_device *device) /* Create the device */ dev = MKDEV(MAJOR(kgsl_driver.major), minor); device->dev = device_create(kgsl_driver.class, - &device->pdev->dev, + device->parentdev, dev, device, device->name); @@ -1836,7 +1833,7 @@ kgsl_register_device(struct kgsl_device *device) goto err_devlist; } - dev_set_drvdata(&device->pdev->dev, device); + dev_set_drvdata(device->parentdev, device); /* Generic device initialization */ atomic_inc(&kgsl_driver.device_count); @@ -1904,9 +1901,10 @@ int kgsl_device_platform_probe(struct kgsl_device *device, int status = -EINVAL; struct kgsl_memregion *regspace = NULL; struct resource *res; - struct platform_device *pdev = device->pdev; + struct platform_device *pdev = + container_of(device->parentdev, struct platform_device, dev); - pm_runtime_enable(&pdev->dev); + pm_runtime_enable(device->parentdev); status = kgsl_pwrctrl_init(device); if (status) @@ -1993,7 +1991,7 @@ void kgsl_device_platform_remove(struct kgsl_device *device) } kgsl_pwrctrl_close(device); - pm_runtime_disable(&device->pdev->dev); + pm_runtime_disable(device->parentdev); } EXPORT_SYMBOL(kgsl_device_platform_remove); diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index 2a0b4f6a8cb29..df986009b1c3b 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -164,7 +164,7 @@ struct kgsl_device { wait_queue_head_t wait_queue; struct workqueue_struct *work_queue; - struct platform_device *pdev; + struct device *parentdev; struct completion recovery_gate; struct dentry *d_debugfs; struct idr context_idr; diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index fae90805f61b0..45ec638d0610b 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -450,7 +450,8 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) { int i, result = 0; struct clk *clk; - struct platform_device *pdev = device->pdev; + struct platform_device *pdev = + container_of(device->parentdev, struct platform_device, dev); struct kgsl_pwrctrl *pwr = &device->pwrctrl; struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data; struct kgsl_device_pwr_data *pdata_pwr = &pdata_dev->pwr_data; diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c index 4d7a31f80de48..526b7044df101 100644 --- a/drivers/gpu/msm/z180.c +++ b/drivers/gpu/msm/z180.c @@ -557,7 +557,7 @@ static int __devinit z180_probe(struct platform_device *pdev) struct z180_device *z180_dev; device = (struct kgsl_device *)pdev->id_entry->driver_data; - device->pdev = pdev; + device->parentdev = &pdev->dev; z180_getfunctable(&device->ftbl); @@ -577,7 +577,7 @@ static int __devinit z180_probe(struct platform_device *pdev) error_close_ringbuffer: z180_ringbuffer_close(device); error: - device->pdev = NULL; + device->parentdev = NULL; return status; } From 94117bb2197c736277d2f1cc0e164b823b30c3c7 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Mon, 6 Jun 2011 15:45:32 -0600 Subject: [PATCH 2047/2556] msm: kgsl: Add a sysfs entry to set the maximum allowable clock speed Add a new sysfs entry 'max_gpuclk' to set the maximum allowable clock speed under any circumstances. This mainly used for thermal mitigation. Change-Id: I0406f8a9fa847e05de4ac665913ba11fd237e05a Signed-off-by: Lucille Sylvester --- drivers/gpu/msm/kgsl_pwrctrl.c | 71 +++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 45ec638d0610b..015a7d89452e7 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -82,45 +82,77 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, } EXPORT_SYMBOL(kgsl_pwrctrl_pwrlevel_change); -static int kgsl_pwrctrl_gpuclk_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - char temp[20]; - int rc, i, delta = 5000000; +static int __gpuclk_store(int max, struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ int ret, i, delta = 5000000; unsigned long val; struct kgsl_device *device = kgsl_device_from_dev(dev); struct kgsl_pwrctrl *pwr = &device->pwrctrl; - snprintf(temp, sizeof(temp), "%.*s", - (int)min(count, sizeof(temp) - 1), buf); - rc = strict_strtoul(temp, 0, &val); - if (rc) - return rc; + ret = sscanf(buf, "%ld", &val); + if (ret != 1) + return count; mutex_lock(&device->mutex); - /* Find the best match for the requested freq, if it exists */ - - for (i = 0; i < pwr->num_pwrlevels; i++) + for (i = 0; i < pwr->num_pwrlevels; i++) { if (abs(pwr->pwrlevels[i].gpu_freq - val) < delta) { - pwr->thermal_pwrlevel = i; + if (max) + pwr->thermal_pwrlevel = i; break; } + } + + if (i == pwr->num_pwrlevels) + goto done; - kgsl_pwrctrl_pwrlevel_change(device, i); + /* + * If the current or requested clock speed is greater than the + * thermal limit, bump down immediately. + */ - mutex_unlock(&device->mutex); + if (pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq > + pwr->pwrlevels[pwr->thermal_pwrlevel].gpu_freq) + kgsl_pwrctrl_pwrlevel_change(device, pwr->thermal_pwrlevel); + else if (!max) + kgsl_pwrctrl_pwrlevel_change(device, i); +done: + mutex_unlock(&device->mutex); return count; } +static int kgsl_pwrctrl_max_gpuclk_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return __gpuclk_store(1, dev, attr, buf, count); +} + +static int kgsl_pwrctrl_max_gpuclk_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + return snprintf(buf, PAGE_SIZE, "%d\n", + pwr->pwrlevels[pwr->thermal_pwrlevel].gpu_freq); +} + +static int kgsl_pwrctrl_gpuclk_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return __gpuclk_store(0, dev, attr, buf, count); +} + static int kgsl_pwrctrl_gpuclk_show(struct device *dev, struct device_attribute *attr, char *buf) { struct kgsl_device *device = kgsl_device_from_dev(dev); struct kgsl_pwrctrl *pwr = &device->pwrctrl; - return sprintf(buf, "%d\n", + return snprintf(buf, PAGE_SIZE, "%d\n", pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq); } @@ -242,6 +274,8 @@ static int kgsl_pwrctrl_scaling_governor_show(struct device *dev, } DEVICE_ATTR(gpuclk, 0644, kgsl_pwrctrl_gpuclk_show, kgsl_pwrctrl_gpuclk_store); +DEVICE_ATTR(max_gpuclk, 0644, kgsl_pwrctrl_max_gpuclk_show, + kgsl_pwrctrl_max_gpuclk_store); DEVICE_ATTR(pwrnap, 0644, kgsl_pwrctrl_pwrnap_show, kgsl_pwrctrl_pwrnap_store); DEVICE_ATTR(idle_timer, 0644, kgsl_pwrctrl_idle_timer_show, kgsl_pwrctrl_idle_timer_store); @@ -250,6 +284,7 @@ DEVICE_ATTR(scaling_governor, 0644, kgsl_pwrctrl_scaling_governor_show, static const struct device_attribute *pwrctrl_attr_list[] = { &dev_attr_gpuclk, + &dev_attr_max_gpuclk, &dev_attr_pwrnap, &dev_attr_idle_timer, &dev_attr_scaling_governor, From 147ab149f0c3974b2b3fad37921e0ee1cf94824d Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Wed, 8 Jun 2011 11:08:22 -0600 Subject: [PATCH 2048/2556] msm: kgsl: put gpu_id into struct kgsl_devinfo The field gmem_hostbaseaddr has never been used by userspace and has always been set to 0. It is being replaced with the gpu_id field which will contain the revision of the GPU, for example 200 for adreno200. Change-Id: I48347da5d430a3aa1db588b4fe54febe7eb44476 Signed-off-by: Jeremy Gebben --- drivers/gpu/msm/adreno.c | 3 +-- include/linux/msm_kgsl.h | 10 ++++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 98fc9215ae4e6..37756dfeb17bb 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -759,8 +759,7 @@ static int adreno_getproperty(struct kgsl_device *device, devinfo.device_id = device->id+1; devinfo.chip_id = device->chip_id; devinfo.mmu_enabled = kgsl_mmu_enabled(); - devinfo.gmem_hostbaseaddr = (unsigned int) - adreno_dev->gmemspace.mmio_virt_base; + devinfo.gpu_id = adreno_get_rev(adreno_dev); devinfo.gmem_gpubaseaddr = adreno_dev->gmemspace. gpu_base; devinfo.gmem_sizebytes = adreno_dev->gmemspace. diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index ed14a84efa945..ca273223a63a6 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -35,7 +35,7 @@ #define _MSM_KGSL_H #define KGSL_VERSION_MAJOR 3 -#define KGSL_VERSION_MINOR 3 +#define KGSL_VERSION_MINOR 4 /*context flags */ #define KGSL_CONTEXT_SAVE_GMEM 1 @@ -83,9 +83,11 @@ struct kgsl_devinfo { unsigned int chip_id; unsigned int mmu_enabled; unsigned int gmem_gpubaseaddr; - /* if gmem_hostbaseaddr is NULL, we would know its not mapped into - * mmio space */ - unsigned int gmem_hostbaseaddr; + /* + * This field contains the adreno revision + * number 200, 205, 220, etc... + */ + unsigned int gpu_id; unsigned int gmem_sizebytes; }; From c80ec11e53455821635a0efdb8b283761c1d3471 Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Sat, 21 May 2011 16:12:20 -0600 Subject: [PATCH 2049/2556] msm: kgsl: explicitly include These files depend on sched.h which is being implictly included in some kernel versions but not in others. Change-Id: I880b28e8afe3d826154b6f91faf6971bf8d3c44e Signed-off-by: Jeremy Gebben --- drivers/gpu/msm/adreno.c | 1 + drivers/gpu/msm/adreno_ringbuffer.c | 1 + drivers/gpu/msm/kgsl_mmu.c | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 37756dfeb17bb..7bed2868e57fa 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "kgsl.h" #include "kgsl_pwrscale.h" diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index c805a05757071..f30092b11120f 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -12,6 +12,7 @@ */ #include #include +#include #include "kgsl.h" diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c index 2f18c1b9b8490..fe441d723f55a 100644 --- a/drivers/gpu/msm/kgsl_mmu.c +++ b/drivers/gpu/msm/kgsl_mmu.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "kgsl.h" #include "kgsl_mmu.h" From ab3cb224aec9440f4401748162ae62673efce971 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Thu, 23 Jun 2011 16:41:22 -0600 Subject: [PATCH 2050/2556] msm: kgsl: Move MH ARB settings to the adreno_device struture The MH arbriter settings are not always consistent across GPU platforms, so put the value into the adreno_devce structure and individual platforms can change them at will. Change-Id: Ic0dedbad37f14c16432b30c750ca96d6c77e09ca Signed-off-by: Jordan Crouse --- drivers/gpu/msm/adreno.c | 3 ++- drivers/gpu/msm/adreno.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 7bed2868e57fa..c1a2fe48af571 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -113,6 +113,7 @@ static struct adreno_device device_3d0 = { }, .pfp_fw = NULL, .pm4_fw = NULL, + .mharb = ADRENO_CFG_MHARB, }; static void __devinit adreno_getfunctable(struct kgsl_functable *ftbl); @@ -528,7 +529,7 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) adreno_regwrite(device, REG_RBBM_CNTL, 0x00004442); adreno_regwrite(device, REG_MH_ARBITER_CONFIG, - ADRENO_CFG_MHARB); + adreno_dev->mharb); if (device->chip_id != KGSL_CHIPID_LEIA_REV470) { adreno_regwrite(device, diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index ba155cd2af0b8..5cbff5d81d16c 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -57,6 +57,7 @@ struct adreno_device { unsigned int *pm4_fw; size_t pm4_fw_size; struct adreno_ringbuffer ringbuffer; + unsigned int mharb; }; int adreno_idle(struct kgsl_device *device, unsigned int timeout); From a8ae20deab7ff59492982eb755715bf517a7bf15 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Fri, 24 Jun 2011 10:43:49 -0600 Subject: [PATCH 2051/2556] msm: kgsl: Consolidate device power up / power down Consolidate the complex dance for device power up and down into one location to simplify the init code. Change-Id: Ic0dedbada0933c8a9b29bed3eddb3241ea7d5a64 Signed-off-by: Jordan Crouse Conflicts: drivers/gpu/msm/z180.c --- drivers/gpu/msm/adreno.c | 18 ++++++++---------- drivers/gpu/msm/kgsl_pwrctrl.c | 18 ++++++++++++++++++ drivers/gpu/msm/kgsl_pwrctrl.h | 3 ++- drivers/gpu/msm/z180.c | 13 ++++--------- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index c1a2fe48af571..33a598cfe1687 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -492,10 +492,9 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) device->state = KGSL_STATE_INIT; device->requested_state = KGSL_STATE_NONE; - /* Enable the power rail before the clocks. */ - kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_ON); - kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_ON); - kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_ON); + + /* Power up the device */ + kgsl_pwrctrl_enable(device); if (kgsl_mmu_start(device)) goto error_clk_off; @@ -583,10 +582,9 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) error_irq_off: kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); error_clk_off: - kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_OFF); - kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_OFF); - + kgsl_pwrctrl_disable(device); kgsl_mmu_stop(device); + return status; } @@ -606,9 +604,9 @@ static int adreno_stop(struct kgsl_device *device) /* Disable the clocks before the power rail. */ kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); - kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_OFF); - kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_OFF); - kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_OFF); + + /* Power down the device */ + kgsl_pwrctrl_disable(device); return 0; } diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 015a7d89452e7..73297fd92b789 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -761,3 +761,21 @@ void kgsl_pwrctrl_wake(struct kgsl_device *device) KGSL_PWR_INFO(device, "wake return for device %d\n", device->id); } EXPORT_SYMBOL(kgsl_pwrctrl_wake); + +void kgsl_pwrctrl_enable(struct kgsl_device *device) +{ + /* Order pwrrail/clk sequence based upon platform */ + kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_ON); + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_ON); + kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_ON); +} +EXPORT_SYMBOL(kgsl_pwrctrl_enable); + +void kgsl_pwrctrl_disable(struct kgsl_device *device) +{ + /* Order pwrrail/clk sequence based upon platform */ + kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_OFF); + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_OFF); + kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_OFF); +} +EXPORT_SYMBOL(kgsl_pwrctrl_disable); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 26b0d3aab6a27..f4fda3fec683d 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -85,7 +85,8 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, unsigned int level); int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device); void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device); - +void kgsl_pwrctrl_enable(struct kgsl_device *device); +void kgsl_pwrctrl_disable(struct kgsl_device *device); static inline unsigned long kgsl_get_clkrate(struct clk *clk) { return (clk != NULL) ? clk_get_rate(clk) : 0; diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c index 526b7044df101..924eb45d8d647 100644 --- a/drivers/gpu/msm/z180.c +++ b/drivers/gpu/msm/z180.c @@ -602,10 +602,7 @@ static int z180_start(struct kgsl_device *device, unsigned int init_ram) device->requested_state = KGSL_STATE_NONE; KGSL_PWR_WARN(device, "state -> INIT, device %d\n", device->id); - /* Enable the power rail before the clocks. */ - kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_ON); - kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_ON); - kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_ON); + kgsl_pwrctrl_enable(device); /* Set up MH arbiter. MH offsets are considered to be dword * based, therefore no down shift. */ @@ -629,8 +626,7 @@ static int z180_start(struct kgsl_device *device, unsigned int init_ram) return 0; error_clk_off: z180_regwrite(device, (ADDR_VGC_IRQENABLE >> 2), 0); - kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_OFF); - kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_OFF); + kgsl_pwrctrl_disable(device); error_mmu_stop: kgsl_mmu_stop(device); return status; @@ -646,9 +642,8 @@ static int z180_stop(struct kgsl_device *device) /* Disable the clocks before the power rail. */ kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); - kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_OFF); - kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_OFF); - kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_OFF); + + kgsl_pwrctrl_disable(device); return 0; } From 937c732a2063700b625c645ce1d262d1745d6fb5 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 7 Feb 2012 23:56:40 -0600 Subject: [PATCH 2052/2556] Revert "msm: kgsl: put gpu_id into struct kgsl_devinfo" This reverts commit 5ea111de5b7138c0cbf81d913a9e68e80eb4492b. --- drivers/gpu/msm/adreno.c | 3 ++- include/linux/msm_kgsl.h | 10 ++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 33a598cfe1687..ee7e94c0b72ce 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -759,7 +759,8 @@ static int adreno_getproperty(struct kgsl_device *device, devinfo.device_id = device->id+1; devinfo.chip_id = device->chip_id; devinfo.mmu_enabled = kgsl_mmu_enabled(); - devinfo.gpu_id = adreno_get_rev(adreno_dev); + devinfo.gmem_hostbaseaddr = (unsigned int) + adreno_dev->gmemspace.mmio_virt_base; devinfo.gmem_gpubaseaddr = adreno_dev->gmemspace. gpu_base; devinfo.gmem_sizebytes = adreno_dev->gmemspace. diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index ca273223a63a6..ed14a84efa945 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -35,7 +35,7 @@ #define _MSM_KGSL_H #define KGSL_VERSION_MAJOR 3 -#define KGSL_VERSION_MINOR 4 +#define KGSL_VERSION_MINOR 3 /*context flags */ #define KGSL_CONTEXT_SAVE_GMEM 1 @@ -83,11 +83,9 @@ struct kgsl_devinfo { unsigned int chip_id; unsigned int mmu_enabled; unsigned int gmem_gpubaseaddr; - /* - * This field contains the adreno revision - * number 200, 205, 220, etc... - */ - unsigned int gpu_id; + /* if gmem_hostbaseaddr is NULL, we would know its not mapped into + * mmio space */ + unsigned int gmem_hostbaseaddr; unsigned int gmem_sizebytes; }; From 74803202a915aa9361c18fa521286e8727104183 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Mon, 27 Jun 2011 13:20:52 -0600 Subject: [PATCH 2053/2556] msm: kgsl: Remove unneeded and redundant ringbuffer code Replace log2 function with the builtin kernel versions and remove various if statements and unused flags. Change-Id: Ic0dedbadfab29a17569fba4c5c1e24ab4dc2e3a6 Signed-off-by: Jordan Crouse --- drivers/gpu/msm/adreno_ringbuffer.c | 55 +++++++++++++---------------- drivers/gpu/msm/adreno_ringbuffer.h | 33 ++++------------- 2 files changed, 32 insertions(+), 56 deletions(-) diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index f30092b11120f..300f318177813 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "kgsl.h" @@ -52,19 +53,6 @@ #define A220_PFP_470_FW "leia_pfp_470.fw" #define A220_PM4_470_FW "leia_pm4_470.fw" -/* ringbuffer size log2 quadwords equivalent */ -inline unsigned int adreno_ringbuffer_sizelog2quadwords(unsigned int sizedwords) -{ - unsigned int sizelog2quadwords = 0; - int i = sizedwords >> 1; - - while (i >>= 1) - sizelog2quadwords++; - - return sizelog2quadwords; -} - - /* functions */ void kgsl_cp_intrcallback(struct kgsl_device *device) { @@ -166,8 +154,6 @@ static void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb) mb(); adreno_regwrite(rb->device, REG_CP_RB_WPTR, rb->wptr); - - rb->flags |= KGSL_FLAGS_ACTIVE; } static int @@ -393,11 +379,20 @@ int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, unsigned int init_ram) /*setup REG_CP_RB_CNTL */ adreno_regread(device, REG_CP_RB_CNTL, &rb_cntl); cp_rb_cntl.val = rb_cntl; - /* size of ringbuffer */ - cp_rb_cntl.f.rb_bufsz = - adreno_ringbuffer_sizelog2quadwords(rb->sizedwords); - /* quadwords to read before updating mem RPTR */ - cp_rb_cntl.f.rb_blksz = rb->blksizequadwords; + + /* + * The size of the ringbuffer in the hardware is the log2 + * representation of the size in quadwords (sizedwords / 2) + */ + cp_rb_cntl.f.rb_bufsz = ilog2(rb->sizedwords >> 1); + + /* + * Specify the quadwords to read before updating mem RPTR. + * Like above, pass the log2 representation of the blocksize + * in quadwords. + */ + cp_rb_cntl.f.rb_blksz = ilog2(KGSL_RB_BLKSIZE >> 3); + cp_rb_cntl.f.rb_poll_en = GSL_RB_CNTL_POLL_EN; /* WPTR polling */ /* mem RPTR writebacks */ cp_rb_cntl.f.rb_no_update = GSL_RB_CNTL_NO_UPDATE; @@ -520,8 +515,12 @@ int adreno_ringbuffer_init(struct kgsl_device *device) struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; rb->device = device; - rb->sizedwords = (2 << kgsl_cfg_rb_sizelog2quadwords); - rb->blksizequadwords = kgsl_cfg_rb_blksizequadwords; + /* + * It is silly to convert this to words and then back to bytes + * immediately below, but most of the rest of the code deals + * in words, so we might as well only do the math once + */ + rb->sizedwords = KGSL_RB_SIZE >> 2; /* allocate memory for ringbuffer */ status = kgsl_allocate_contig(&rb->buffer_desc, (rb->sizedwords << 2)); @@ -552,16 +551,12 @@ int adreno_ringbuffer_close(struct adreno_ringbuffer *rb) { struct adreno_device *adreno_dev = ADRENO_DEVICE(rb->device); - if (rb->buffer_desc.hostptr) - kgsl_sharedmem_free(&rb->buffer_desc); + kgsl_sharedmem_free(&rb->buffer_desc); + kgsl_sharedmem_free(&rb->memptrs_desc); - if (rb->memptrs_desc.hostptr) - kgsl_sharedmem_free(&rb->memptrs_desc); + kfree(adreno_dev->pfp_fw); + kfree(adreno_dev->pm4_fw); - if (adreno_dev->pfp_fw != NULL) - kfree(adreno_dev->pfp_fw); - if (adreno_dev->pm4_fw != NULL) - kfree(adreno_dev->pm4_fw); adreno_dev->pfp_fw = NULL; adreno_dev->pm4_fw = NULL; diff --git a/drivers/gpu/msm/adreno_ringbuffer.h b/drivers/gpu/msm/adreno_ringbuffer.h index a4b25c3fab0b6..9162dea2cae33 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.h +++ b/drivers/gpu/msm/adreno_ringbuffer.h @@ -33,31 +33,13 @@ #define GSL_RB_USE_MEM_TIMESTAMP #define GSL_DEVICE_SHADOW_MEMSTORE_TO_USER -/* ringbuffer sizes log2quadword */ -#define GSL_RB_SIZE_8 0 -#define GSL_RB_SIZE_16 1 -#define GSL_RB_SIZE_32 2 -#define GSL_RB_SIZE_64 3 -#define GSL_RB_SIZE_128 4 -#define GSL_RB_SIZE_256 5 -#define GSL_RB_SIZE_512 6 -#define GSL_RB_SIZE_1K 7 -#define GSL_RB_SIZE_2K 8 -#define GSL_RB_SIZE_4K 9 -#define GSL_RB_SIZE_8K 10 -#define GSL_RB_SIZE_16K 11 -#define GSL_RB_SIZE_32K 12 -#define GSL_RB_SIZE_64K 13 -#define GSL_RB_SIZE_128K 14 -#define GSL_RB_SIZE_256K 15 -#define GSL_RB_SIZE_512K 16 -#define GSL_RB_SIZE_1M 17 -#define GSL_RB_SIZE_2M 18 -#define GSL_RB_SIZE_4M 19 - -/* Adreno ringbuffer config*/ -static const unsigned int kgsl_cfg_rb_sizelog2quadwords = GSL_RB_SIZE_32K; -static const unsigned int kgsl_cfg_rb_blksizequadwords = GSL_RB_SIZE_16; +/* + * Adreno ringbuffer sizes in bytes - these are converted to + * the appropriate log2 values in the code + */ + +#define KGSL_RB_SIZE (32 * 1024) +#define KGSL_RB_BLKSIZE 16 /* CP timestamp register */ #define REG_CP_TIMESTAMP REG_SCRATCH_REG0 @@ -89,7 +71,6 @@ struct adreno_ringbuffer { /*ringbuffer size */ unsigned int sizedwords; - unsigned int blksizequadwords; unsigned int wptr; /* write pointer offset in dwords from baseaddr */ unsigned int rptr; /* read pointer offset in dwords from baseaddr */ From be1853e32272563a8fdb5881e4703e86ac38bf3a Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Tue, 28 Jun 2011 13:14:49 -0600 Subject: [PATCH 2054/2556] msm: kgsl: Gracefully handle invalid memory map offsets Handle situations where the offset for an external memory mapping is bigger then the region or if it happens to overflow the 32 bit register. Change-Id: Ic0dedbad1cbebb315f3089d5d8dbecd76bc572c2 Signed-off-by: Jordan Crouse --- drivers/gpu/msm/kgsl.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 1311de86183c8..4c6ae3e546b42 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1161,7 +1161,7 @@ kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_device_private *dev_priv, static inline int _check_region(unsigned long start, unsigned long size, uint64_t len) { - uint64_t end = start + size; + uint64_t end = ((uint64_t) start) + size; return (end > len); } @@ -1215,6 +1215,11 @@ static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, if (ret) return ret; + if (offset >= len) { + ret = -EINVAL; + goto err; + } + if (size == 0) size = len; @@ -1227,8 +1232,9 @@ static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, KGSL_CORE_ERR("Offset (%ld) + size (%d) is larger" "than pmem region length %ld\n", offset & PAGE_MASK, size, len); - put_pmem_file(filep); - return -EINVAL; + ret = -EINVAL; + goto err; + } entry->file_ptr = filep; @@ -1240,6 +1246,9 @@ static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, entry->memdesc.ops = &kgsl_contig_ops; return 0; +err: + put_pmem_file(filep); + return ret; } #else static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, @@ -1271,6 +1280,9 @@ static int kgsl_setup_hostptr(struct kgsl_mem_entry *entry, /* We don't necessarily start at vma->vm_start */ len = vma->vm_end - (unsigned long) hostptr; + if (offset >= len) + return -EINVAL; + if (!KGSL_IS_PAGE_ALIGNED((unsigned long) hostptr) || !KGSL_IS_PAGE_ALIGNED(len)) { KGSL_CORE_ERR("user address len(%u)" From d0a1b9e75e7385c3935977e28995e8616e66499b Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Sat, 21 May 2011 16:01:53 -0600 Subject: [PATCH 2055/2556] msm: kgsl: Kconfig cleanup Fix dependencies for the 3d and 2d drivers. Fix defaults for page fault mode and the 2d driver. Change-Id: Idfe331ea932935f21fcdeaff8427ecfd2794a269 Signed-off-by: Jeremy Gebben --- drivers/gpu/msm/Kconfig | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/msm/Kconfig b/drivers/gpu/msm/Kconfig index 620fa763671e5..64cbc304c0968 100644 --- a/drivers/gpu/msm/Kconfig +++ b/drivers/gpu/msm/Kconfig @@ -1,10 +1,9 @@ config MSM_KGSL tristate "MSM 3D Graphics driver" default n - depends on FB && ARM && ARCH_MSM && !MSM_HW3D + depends on ARCH_MSM && !ARCH_MSM7X00A && !ARCH_MSM7X25 select GENERIC_ALLOCATOR select FW_LOADER - select RELAY ---help--- 3D graphics driver. Required to use hardware accelerated OpenGL ES 2.0 and 1.1. @@ -58,8 +57,8 @@ config MSM_KGSL_PSTMRTMDMP_RB_HEX config MSM_KGSL_2D tristate "MSM 2D graphics driver. Required for OpenVG" - default n - depends on MSM_KGSL && !ARCH_MSM7X27 + default y + depends on MSM_KGSL && !ARCH_MSM7X27 && !ARCH_MSM7X27A && !(ARCH_QSD8X50 && !MSM_SOC_REV_A) config MSM_KGSL_DRM bool "Build a DRM interface for the MSM_KGSL driver" @@ -97,7 +96,7 @@ config MSM_KGSL_PAGE_TABLE_COUNT config MSM_KGSL_MMU_PAGE_FAULT bool "Force the GPU MMU to page fault for unmapped regions" - default n + default y depends on MSM_KGSL_MMU config MSM_KGSL_DISABLE_SHADOW_WRITES From f33d9f112f059ea149b4bbdfe787a24aa2416369 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Wed, 6 Jul 2011 10:51:51 -0600 Subject: [PATCH 2056/2556] msm: kgsl: Use atomic operations to set the power flags Use atomic operations to check and set the power flags so they can be modified from within interrupt handlers. Change-Id: Ic0dedbad0f1d8a4aa3d74803d565fd4471e96499 Signed-off-by: Jordan Crouse Conflicts: drivers/gpu/msm/kgsl_pwrctrl.c --- drivers/gpu/msm/adreno.c | 6 +- drivers/gpu/msm/adreno_postmortem.c | 6 +- drivers/gpu/msm/kgsl_pwrctrl.c | 131 ++++++++++------------------ drivers/gpu/msm/kgsl_pwrctrl.h | 25 +++--- drivers/gpu/msm/z180.c | 2 +- 5 files changed, 65 insertions(+), 105 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index ee7e94c0b72ce..40d33a179f92a 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -570,7 +570,7 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) adreno_dev->gmemspace.sizebytes = SZ_256K; adreno_gmeminit(adreno_dev); - kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_ON); + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON); status = adreno_ringbuffer_start(&adreno_dev->ringbuffer, init_ram); if (status != 0) @@ -580,7 +580,7 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) return status; error_irq_off: - kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); error_clk_off: kgsl_pwrctrl_disable(device); kgsl_mmu_stop(device); @@ -603,7 +603,7 @@ static int adreno_stop(struct kgsl_device *device) kgsl_mmu_stop(device); /* Disable the clocks before the power rail. */ - kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); /* Power down the device */ kgsl_pwrctrl_disable(device); diff --git a/drivers/gpu/msm/adreno_postmortem.c b/drivers/gpu/msm/adreno_postmortem.c index 11dc48298e27c..509f25e21e6da 100644 --- a/drivers/gpu/msm/adreno_postmortem.c +++ b/drivers/gpu/msm/adreno_postmortem.c @@ -487,7 +487,7 @@ static int adreno_dump(struct kgsl_device *device) mb(); - KGSL_LOG_DUMP(device, "POWER: FLAGS = %08X | ACTIVE POWERLEVEL = %08X", + KGSL_LOG_DUMP(device, "POWER: FLAGS = %08lX | ACTIVE POWERLEVEL = %08X", pwr->power_flags, pwr->active_pwrlevel); KGSL_LOG_DUMP(device, "POWER: INTERVAL TIMEOUT = %08X ", @@ -824,7 +824,7 @@ int adreno_postmortem_dump(struct kgsl_device *device, int manual) kgsl_pwrctrl_wake(device); /* Disable the irq */ - kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); /* If this is not a manual trigger, then set up the state to try to recover */ @@ -852,7 +852,7 @@ int adreno_postmortem_dump(struct kgsl_device *device, int manual) are until recovery kicks in. */ if (manual) { - kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_ON); + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON); /* try to go into a sleep mode until the next event */ device->requested_state = KGSL_STATE_SLEEP; diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 73297fd92b789..3cac7b18ffec2 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -67,11 +67,11 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, new_level >= pwr->thermal_pwrlevel && new_level != pwr->active_pwrlevel) { pwr->active_pwrlevel = new_level; - if (pwr->power_flags & KGSL_PWRFLAGS_CLK_ON) + if (test_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->power_flags)) clk_set_rate(pwr->grp_clks[0], pwr->pwrlevels[pwr->active_pwrlevel]. gpu_freq); - if (pwr->power_flags & KGSL_PWRFLAGS_AXI_ON) + if (test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)) if (pwr->pcl) msm_bus_scale_client_update_request(pwr->pcl, pwr->pwrlevels[pwr->active_pwrlevel]. @@ -325,13 +325,13 @@ static void kgsl_pwrctrl_idle_calc(struct kgsl_device *device) pwr->active_pwrlevel + val); } -void kgsl_pwrctrl_clk(struct kgsl_device *device, unsigned int pwrflag) +void kgsl_pwrctrl_clk(struct kgsl_device *device, int state) { struct kgsl_pwrctrl *pwr = &device->pwrctrl; int i = 0; - switch (pwrflag) { - case KGSL_PWRFLAGS_CLK_OFF: - if (pwr->power_flags & KGSL_PWRFLAGS_CLK_ON) { + if (state == KGSL_PWRFLAGS_OFF) { + if (test_and_clear_bit(KGSL_PWRFLAGS_CLK_ON, + &pwr->power_flags)) { KGSL_PWR_INFO(device, "clocks off, device %d\n", device->id); for (i = KGSL_MAX_CLKS - 1; i > 0; i--) @@ -342,20 +342,13 @@ void kgsl_pwrctrl_clk(struct kgsl_device *device, unsigned int pwrflag) clk_set_rate(pwr->grp_clks[0], pwr->pwrlevels[pwr->num_pwrlevels - 1]. gpu_freq); - pwr->power_flags &= - ~(KGSL_PWRFLAGS_CLK_ON); - pwr->power_flags |= KGSL_PWRFLAGS_CLK_OFF; } - return; - case KGSL_PWRFLAGS_CLK_ON: - if (pwr->power_flags & KGSL_PWRFLAGS_CLK_OFF) { + } else if (state == KGSL_PWRFLAGS_ON) { + if (!test_and_set_bit(KGSL_PWRFLAGS_CLK_ON, + &pwr->power_flags)) { KGSL_PWR_INFO(device, "clocks on, device %d\n", device->id); - pwr->power_flags &= - ~(KGSL_PWRFLAGS_CLK_OFF); - pwr->power_flags |= KGSL_PWRFLAGS_CLK_ON; - if ((pwr->pwrlevels[0].gpu_freq > 0) && (device->state != KGSL_STATE_NAP)) clk_set_rate(pwr->grp_clks[0], @@ -368,20 +361,17 @@ void kgsl_pwrctrl_clk(struct kgsl_device *device, unsigned int pwrflag) if (pwr->grp_clks[i]) clk_enable(pwr->grp_clks[i]); } - return; - default: - return; } } EXPORT_SYMBOL(kgsl_pwrctrl_clk); -void kgsl_pwrctrl_axi(struct kgsl_device *device, unsigned int pwrflag) +void kgsl_pwrctrl_axi(struct kgsl_device *device, int state) { struct kgsl_pwrctrl *pwr = &device->pwrctrl; - switch (pwrflag) { - case KGSL_PWRFLAGS_AXI_OFF: - if (pwr->power_flags & KGSL_PWRFLAGS_AXI_ON) { + if (state == KGSL_PWRFLAGS_OFF) { + if (test_and_clear_bit(KGSL_PWRFLAGS_AXI_ON, + &pwr->power_flags)) { KGSL_PWR_INFO(device, "axi off, device %d\n", device->id); if (pwr->ebi1_clk) @@ -389,13 +379,10 @@ void kgsl_pwrctrl_axi(struct kgsl_device *device, unsigned int pwrflag) if (pwr->pcl) msm_bus_scale_client_update_request(pwr->pcl, 0); - pwr->power_flags &= - ~(KGSL_PWRFLAGS_AXI_ON); - pwr->power_flags |= KGSL_PWRFLAGS_AXI_OFF; } - return; - case KGSL_PWRFLAGS_AXI_ON: - if (pwr->power_flags & KGSL_PWRFLAGS_AXI_OFF) { + } else if (state == KGSL_PWRFLAGS_ON) { + if (!test_and_set_bit(KGSL_PWRFLAGS_AXI_ON, + &pwr->power_flags)) { KGSL_PWR_INFO(device, "axi on, device %d\n", device->id); if (pwr->ebi1_clk) @@ -404,79 +391,54 @@ void kgsl_pwrctrl_axi(struct kgsl_device *device, unsigned int pwrflag) msm_bus_scale_client_update_request(pwr->pcl, pwr->pwrlevels[pwr->active_pwrlevel]. bus_freq); - pwr->power_flags &= - ~(KGSL_PWRFLAGS_AXI_OFF); - pwr->power_flags |= KGSL_PWRFLAGS_AXI_ON; } - return; - default: - return; } } EXPORT_SYMBOL(kgsl_pwrctrl_axi); -void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, unsigned int pwrflag) +void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, int state) { struct kgsl_pwrctrl *pwr = &device->pwrctrl; - switch (pwrflag) { - case KGSL_PWRFLAGS_POWER_OFF: - if (pwr->power_flags & KGSL_PWRFLAGS_POWER_ON) { + if (state == KGSL_PWRFLAGS_OFF) { + if (test_and_clear_bit(KGSL_PWRFLAGS_POWER_ON, + &pwr->power_flags)) { KGSL_PWR_INFO(device, "power off, device %d\n", device->id); if (pwr->gpu_reg) regulator_disable(pwr->gpu_reg); - pwr->power_flags &= - ~(KGSL_PWRFLAGS_POWER_ON); - pwr->power_flags |= - KGSL_PWRFLAGS_POWER_OFF; } - return; - case KGSL_PWRFLAGS_POWER_ON: - if (pwr->power_flags & KGSL_PWRFLAGS_POWER_OFF) { + } else if (state == KGSL_PWRFLAGS_ON) { + if (!test_and_set_bit(KGSL_PWRFLAGS_POWER_ON, + &pwr->power_flags)) { KGSL_PWR_INFO(device, "power on, device %d\n", device->id); if (pwr->gpu_reg) regulator_enable(pwr->gpu_reg); - pwr->power_flags &= - ~(KGSL_PWRFLAGS_POWER_OFF); - pwr->power_flags |= - KGSL_PWRFLAGS_POWER_ON; } - return; - default: - return; } } EXPORT_SYMBOL(kgsl_pwrctrl_pwrrail); -void kgsl_pwrctrl_irq(struct kgsl_device *device, unsigned int pwrflag) +void kgsl_pwrctrl_irq(struct kgsl_device *device, int state) { struct kgsl_pwrctrl *pwr = &device->pwrctrl; - switch (pwrflag) { - case KGSL_PWRFLAGS_IRQ_ON: - if (pwr->power_flags & KGSL_PWRFLAGS_IRQ_OFF) { + + if (state == KGSL_PWRFLAGS_ON) { + if (!test_and_set_bit(KGSL_PWRFLAGS_IRQ_ON, + &pwr->power_flags)) { KGSL_PWR_INFO(device, "irq on, device %d\n", device->id); - pwr->power_flags &= - ~(KGSL_PWRFLAGS_IRQ_OFF); - pwr->power_flags |= KGSL_PWRFLAGS_IRQ_ON; enable_irq(pwr->interrupt_num); } - return; - case KGSL_PWRFLAGS_IRQ_OFF: - if (pwr->power_flags & KGSL_PWRFLAGS_IRQ_ON) { + } else if (state == KGSL_PWRFLAGS_OFF) { + if (test_and_clear_bit(KGSL_PWRFLAGS_IRQ_ON, + &pwr->power_flags)) { KGSL_PWR_INFO(device, "irq off, device %d\n", device->id); disable_irq(pwr->interrupt_num); - pwr->power_flags &= - ~(KGSL_PWRFLAGS_IRQ_ON); - pwr->power_flags |= KGSL_PWRFLAGS_IRQ_OFF; } - return; - default: - return; } } EXPORT_SYMBOL(kgsl_pwrctrl_irq); @@ -539,9 +501,8 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) if (IS_ERR(pwr->gpu_reg)) pwr->gpu_reg = NULL; - pwr->power_flags = KGSL_PWRFLAGS_CLK_OFF | - KGSL_PWRFLAGS_AXI_OFF | KGSL_PWRFLAGS_POWER_OFF | - KGSL_PWRFLAGS_IRQ_OFF; + pwr->power_flags = 0; + pwr->nap_allowed = pdata_pwr->nap_allowed; /* drewis: below was removed at some point before i cherry-picked the below commit */ pwr->idle_pass = pdata_pwr->idle_pass; @@ -706,8 +667,8 @@ int kgsl_pwrctrl_sleep(struct kgsl_device *device) return -EBUSY; sleep: - kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); - kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_OFF); + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); + kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_OFF); if (pwr->pwrlevels[0].gpu_freq > 0) clk_set_rate(pwr->grp_clks[0], pwr->pwrlevels[pwr->num_pwrlevels - 1]. @@ -718,9 +679,9 @@ int kgsl_pwrctrl_sleep(struct kgsl_device *device) goto clk_off; nap: - kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); clk_off: - kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_OFF); + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF); device->state = device->requested_state; device->requested_state = KGSL_STATE_NONE; @@ -743,15 +704,15 @@ void kgsl_pwrctrl_wake(struct kgsl_device *device) if (device->pwrctrl.idle_pass) kgsl_pwrctrl_pwrlevel_change(device, device->pwrctrl.thermal_pwrlevel); - kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_ON); + kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON); } /* Turn on the core clocks */ - kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_ON); + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON); /* Enable state before turning on irq */ device->state = KGSL_STATE_ACTIVE; KGSL_PWR_WARN(device, "state -> ACTIVE, device %d\n", device->id); - kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_ON); + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON); /* Re-enable HW access */ mod_timer(&device->idle_timer, @@ -765,17 +726,17 @@ EXPORT_SYMBOL(kgsl_pwrctrl_wake); void kgsl_pwrctrl_enable(struct kgsl_device *device) { /* Order pwrrail/clk sequence based upon platform */ - kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_ON); - kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_ON); - kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_ON); + kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_ON); + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON); + kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON); } EXPORT_SYMBOL(kgsl_pwrctrl_enable); void kgsl_pwrctrl_disable(struct kgsl_device *device) { /* Order pwrrail/clk sequence based upon platform */ - kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_AXI_OFF); - kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_CLK_OFF); - kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_POWER_OFF); + kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_OFF); + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF); + kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_OFF); } EXPORT_SYMBOL(kgsl_pwrctrl_disable); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index f4fda3fec683d..108cc3093955a 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -32,14 +32,13 @@ /***************************************************************************** ** power flags *****************************************************************************/ -#define KGSL_PWRFLAGS_POWER_OFF 0x00000001 -#define KGSL_PWRFLAGS_POWER_ON 0x00000002 -#define KGSL_PWRFLAGS_CLK_ON 0x00000004 -#define KGSL_PWRFLAGS_CLK_OFF 0x00000008 -#define KGSL_PWRFLAGS_AXI_ON 0x00000010 -#define KGSL_PWRFLAGS_AXI_OFF 0x00000020 -#define KGSL_PWRFLAGS_IRQ_ON 0x00000040 -#define KGSL_PWRFLAGS_IRQ_OFF 0x00000080 +#define KGSL_PWRFLAGS_POWER_ON 0 +#define KGSL_PWRFLAGS_CLK_ON 1 +#define KGSL_PWRFLAGS_AXI_ON 2 +#define KGSL_PWRFLAGS_IRQ_ON 3 + +#define KGSL_PWRFLAGS_ON 1 +#define KGSL_PWRFLAGS_OFF 0 #define KGSL_DEFAULT_PWRLEVEL 1 #define KGSL_MAX_CLKS 5 @@ -51,7 +50,7 @@ struct kgsl_pwrctrl { int have_irq; struct clk *ebi1_clk; struct clk *grp_clks[KGSL_MAX_CLKS]; - unsigned int power_flags; + unsigned long power_flags; struct kgsl_pwrlevel pwrlevels[KGSL_MAX_PWRLEVELS]; unsigned int active_pwrlevel; int thermal_pwrlevel; @@ -69,10 +68,10 @@ struct kgsl_pwrctrl { unsigned int idle_pass; }; -void kgsl_pwrctrl_clk(struct kgsl_device *device, unsigned int pwrflag); -void kgsl_pwrctrl_axi(struct kgsl_device *device, unsigned int pwrflag); -void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, unsigned int pwrflag); -void kgsl_pwrctrl_irq(struct kgsl_device *device, unsigned int pwrflag); +void kgsl_pwrctrl_clk(struct kgsl_device *device, int state); +void kgsl_pwrctrl_axi(struct kgsl_device *device, int state); +void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, int state); +void kgsl_pwrctrl_irq(struct kgsl_device *device, int state); int kgsl_pwrctrl_init(struct kgsl_device *device); void kgsl_pwrctrl_close(struct kgsl_device *device); void kgsl_timer(unsigned long data); diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c index 924eb45d8d647..f1bc2caad4794 100644 --- a/drivers/gpu/msm/z180.c +++ b/drivers/gpu/msm/z180.c @@ -641,7 +641,7 @@ static int z180_stop(struct kgsl_device *device) kgsl_mmu_stop(device); /* Disable the clocks before the power rail. */ - kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_OFF); + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); kgsl_pwrctrl_disable(device); From 8f21eff29c36b8a85ed26684d35b8a20347584ee Mon Sep 17 00:00:00 2001 From: Jason Varbedian Date: Mon, 11 Jul 2011 17:29:05 -0700 Subject: [PATCH 2057/2556] msm: kgsl: change readtimestamp from IOR to IOWR and keep legacy Change-Id: Ic018e7d8dd1aff7c3b03db08afdee78adf7edd0c Signed-off-by: Jason Varbedian Conflicts: include/linux/msm_kgsl.h --- drivers/gpu/msm/kgsl.c | 2 ++ include/linux/msm_kgsl.h | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 4c6ae3e546b42..a7c4d6469bd85 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1599,6 +1599,8 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) if (cmd == IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP_OLD) cmd = IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP; + else if (cmd == IOCTL_KGSL_CMDSTREAM_READTIMESTAMP_OLD) + cmd = IOCTL_KGSL_CMDSTREAM_READTIMESTAMP; if (cmd & (IOC_IN | IOC_OUT)) { if (_IOC_SIZE(cmd) < sizeof(ustack)) diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index ed14a84efa945..faa6459e273b4 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -35,7 +35,7 @@ #define _MSM_KGSL_H #define KGSL_VERSION_MAJOR 3 -#define KGSL_VERSION_MINOR 3 +#define KGSL_VERSION_MINOR 5 /*context flags */ #define KGSL_CONTEXT_SAVE_GMEM 1 @@ -269,9 +269,12 @@ struct kgsl_cmdstream_readtimestamp { unsigned int timestamp; /*output param */ }; -#define IOCTL_KGSL_CMDSTREAM_READTIMESTAMP \ +#define IOCTL_KGSL_CMDSTREAM_READTIMESTAMP_OLD \ _IOR(KGSL_IOC_TYPE, 0x11, struct kgsl_cmdstream_readtimestamp) +#define IOCTL_KGSL_CMDSTREAM_READTIMESTAMP \ + _IOWR(KGSL_IOC_TYPE, 0x11, struct kgsl_cmdstream_readtimestamp) + /* free memory when the GPU reaches a given timestamp. * gpuaddr specify a memory region created by a * IOCTL_KGSL_SHAREDMEM_FROM_PMEM call From 8b9da871411ac40afd2a838323e728b1f90dc5db Mon Sep 17 00:00:00 2001 From: Wei Zou Date: Fri, 8 Jul 2011 10:24:22 -0700 Subject: [PATCH 2058/2556] msm: kgsl: Check for null physical address in PMEM case CRs-fixed: 294653 Change-Id: Ie6193f5b4702883c52ebde1a2617e400d30ca653 Signed-off-by: Wei Zou --- drivers/gpu/msm/kgsl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index a7c4d6469bd85..3e99f50e4d61a 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1215,6 +1215,11 @@ static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, if (ret) return ret; + if (phys == 0) { + ret = -EINVAL; + goto err; + } + if (offset >= len) { ret = -EINVAL; goto err; From 6017f03486633ef1e22a43a4e96252983ba16a53 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 8 Feb 2012 00:56:07 -0600 Subject: [PATCH 2059/2556] include: linux: msm: try older mdp header --- include/linux/msm_mdp.h | 140 +++------------------------------------- 1 file changed, 10 insertions(+), 130 deletions(-) diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h index 427465db41676..5c674712b1a08 100644 --- a/include/linux/msm_mdp.h +++ b/include/linux/msm_mdp.h @@ -54,15 +54,17 @@ struct msmfb_mixer_info_req) #define MSMFB_OVERLAY_PLAY_WAIT _IOWR(MSMFB_IOCTL_MAGIC, 149, \ struct msmfb_overlay_data) + #define MSMFB_WRITEBACK_INIT _IO(MSMFB_IOCTL_MAGIC, 150) -#define MSMFB_WRITEBACK_START _IO(MSMFB_IOCTL_MAGIC, 151) -#define MSMFB_WRITEBACK_STOP _IO(MSMFB_IOCTL_MAGIC, 152) +#define MSMFB_WRITEBACK_REGISTER_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 151, \ + struct msmfb_writeback_data) +#define MSMFB_WRITEBACK_UNREGISTER_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 152, \ + struct msmfb_writeback_data) #define MSMFB_WRITEBACK_QUEUE_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 153, \ struct msmfb_data) #define MSMFB_WRITEBACK_DEQUEUE_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 154, \ struct msmfb_data) #define MSMFB_WRITEBACK_TERMINATE _IO(MSMFB_IOCTL_MAGIC, 155) -#define MSMFB_MDP_PP _IOWR(MSMFB_IOCTL_MAGIC, 156, struct msmfb_mdp_pp) #define FB_TYPE_3D_PANEL 0x10101010 #define MDP_IMGTYPE2_START 0x10000 @@ -137,10 +139,8 @@ enum { #define MDP_DEINTERLACE_ODD 0x00400000 #define MDP_OV_PLAY_NOWAIT 0x00200000 #define MDP_SOURCE_ROTATED_90 0x00100000 -#define MDP_DPP_HSIC 0x00080000 -#define MDP_BORDERFILL_SUPPORTED 0x00010000 -#define MDP_SECURE_OVERLAY_SESSION 0x00008000 #define MDP_MEMORY_ID_TYPE_FB 0x00001000 +#define MDP_DPP_HSIC 0x00080000 #define MDP_TRANSP_NOP 0xffffffff #define MDP_ALPHA_NOP 0xff @@ -227,7 +227,6 @@ struct msmfb_data { int id; uint32_t flags; uint32_t priv; - uint32_t iova; }; #define MSMFB_NEW_REQUEST -1 @@ -300,127 +299,6 @@ struct mdp_histogram { uint32_t *b; }; - -/* - - mdp_block_type defines the identifiers for each of pipes in MDP 4.3 - - MDP_BLOCK_RESERVED is provided for backward compatibility and is - deprecated. It corresponds to DMA_P. So MDP_BLOCK_DMA_P should be used - instead. - -*/ - -enum { - MDP_BLOCK_RESERVED = 0, - MDP_BLOCK_OVERLAY_0, - MDP_BLOCK_OVERLAY_1, - MDP_BLOCK_VG_1, - MDP_BLOCK_VG_2, - MDP_BLOCK_RGB_1, - MDP_BLOCK_RGB_2, - MDP_BLOCK_DMA_P, - MDP_BLOCK_DMA_S, - MDP_BLOCK_DMA_E, - MDP_BLOCK_MAX, -}; - -struct mdp_pcc_coeff { - uint32_t c, r, g, b, rr, gg, bb, rg, gb, rb, rgb_0, rgb_1; -}; - -struct mdp_pcc_cfg_data { - uint32_t block; - uint32_t ops; - struct mdp_pcc_coeff r, g, b; -}; - -#define MDP_CSC_FLAG_ENABLE 0x1 -#define MDP_CSC_FLAG_YUV_IN 0x2 -#define MDP_CSC_FLAG_YUV_OUT 0x4 - -struct mdp_csc_cfg { - /* flags for enable CSC, toggling RGB,YUV input/output */ - uint32_t flags; - uint32_t csc_mv[9]; - uint32_t csc_pre_bv[3]; - uint32_t csc_post_bv[3]; - uint32_t csc_pre_lv[6]; - uint32_t csc_post_lv[6]; -}; - -struct mdp_csc_cfg_data { - uint32_t block; - struct mdp_csc_cfg csc_data; -}; - -enum { - mdp_lut_igc, - mdp_lut_pgc, - mdp_lut_hist, - mdp_lut_max, -}; - - -struct mdp_igc_lut_data { - uint32_t block; - uint32_t len, ops; - uint32_t *c0_c1_data; - uint32_t *c2_data; -}; - -struct mdp_ar_gc_lut_data { - uint32_t x_start; - uint32_t slope; - uint32_t offset; -}; - -struct mdp_pgc_lut_data { - uint32_t block; - uint32_t flags; - uint8_t num_r_stages; - uint8_t num_g_stages; - uint8_t num_b_stages; - struct mdp_ar_gc_lut_data *r_data; - struct mdp_ar_gc_lut_data *g_data; - struct mdp_ar_gc_lut_data *b_data; -}; - - -struct mdp_hist_lut_data { - uint32_t block; - uint32_t ops; - uint32_t len; - uint32_t *data; -}; - - -struct mdp_lut_cfg_data { - uint32_t lut_type; - union { - struct mdp_igc_lut_data igc_lut_data; - struct mdp_pgc_lut_data pgc_lut_data; - struct mdp_hist_lut_data hist_lut_data; - } data; -}; - -enum { - mdp_op_pcc_cfg, - mdp_op_csc_cfg, - mdp_op_lut_cfg, - mdp_op_max, -}; - -struct msmfb_mdp_pp { - uint32_t op; - union { - struct mdp_pcc_cfg_data pcc_cfg_data; - struct mdp_csc_cfg_data csc_cfg_data; - struct mdp_lut_cfg_data lut_cfg_data; - } data; -}; - - struct mdp_page_protection { uint32_t page_protection; }; @@ -449,12 +327,14 @@ struct msmfb_mixer_info_req { int get_fb_phys_info(unsigned long *start, unsigned long *len, int fb_num); struct fb_info *msm_fb_get_writeback_fb(void); int msm_fb_writeback_init(struct fb_info *info); -int msm_fb_writeback_start(struct fb_info *info); +int msm_fb_writeback_register_buffer(struct fb_info *info, + struct msmfb_writeback_data *data); int msm_fb_writeback_queue_buffer(struct fb_info *info, struct msmfb_data *data); int msm_fb_writeback_dequeue_buffer(struct fb_info *info, struct msmfb_data *data); -int msm_fb_writeback_stop(struct fb_info *info); +int msm_fb_writeback_unregister_buffer(struct fb_info *info, + struct msmfb_writeback_data *data); int msm_fb_writeback_terminate(struct fb_info *info); #endif From c3176f87c1ed597186aa6e8418078dcf2f2199ff Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 12 Jul 2011 10:24:25 -0700 Subject: [PATCH 2060/2556] msm: kgsl: make map_user_mem accept sharedmem_from_pmem struct Change-Id: I280ea82ce6391aa36ee9f9948372e789a3bca0cf Signed-off-by: Jason Varbedian --- drivers/gpu/msm/kgsl.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 3e99f50e4d61a..e641506767c7e 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1386,6 +1386,7 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, struct kgsl_map_user_mem *param = data; struct kgsl_mem_entry *entry = NULL; struct kgsl_process_private *private = dev_priv->process_priv; + enum kgsl_user_mem_type memtype; entry = kgsl_mem_entry_create(); @@ -1394,7 +1395,12 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, kgsl_memqueue_drain_unlocked(dev_priv->device); - switch (param->memtype) { + if (_IOC_SIZE(cmd) == sizeof(struct kgsl_sharedmem_from_pmem)) + memtype = KGSL_USER_MEM_TYPE_PMEM; + else + memtype = param->memtype; + + switch (memtype) { case KGSL_USER_MEM_TYPE_PMEM: if (param->fd == 0 || param->len == 0) break; @@ -1436,7 +1442,7 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, param->len); break; default: - KGSL_CORE_ERR("Invalid memory type: %x\n", param->memtype); + KGSL_CORE_ERR("Invalid memory type: %x\n", memtype); break; } From cff89a593a712727ac9306c1993b14b080a1e6df Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Tue, 26 Jul 2011 08:30:20 -0600 Subject: [PATCH 2061/2556] msm: kgsl: Don't make assumptions about VMA regions We cannot assume that a VMA region created as a result of an mmap belongs exclusively to us. Allow the user to pass the size of the vmalloc region through the 'gpuaddr' member of the ioctl struct and do strict checking on the returned vma to make sure it is valid. Change-Id: Ic0dedbad70d08958c93bfbd601a2ffbb7bb6338d Signed-off-by: Jordan Crouse --- drivers/gpu/msm/kgsl.c | 51 +++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index e641506767c7e..d0511a40a39bb 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1058,25 +1058,13 @@ static long kgsl_ioctl_sharedmem_free(struct kgsl_device_private *dev_priv, static struct vm_area_struct *kgsl_get_vma_from_start_addr(unsigned int addr) { struct vm_area_struct *vma; - int len; down_read(¤t->mm->mmap_sem); vma = find_vma(current->mm, addr); up_read(¤t->mm->mmap_sem); - if (!vma) { + if (!vma) KGSL_CORE_ERR("find_vma(%x) failed\n", addr); - return NULL; - } - len = vma->vm_end - vma->vm_start; - if (vma->vm_pgoff || !KGSL_IS_PAGE_ALIGNED(len) || - !KGSL_IS_PAGE_ALIGNED(vma->vm_start)) { - KGSL_CORE_ERR("address %x is not aligned\n", addr); - return NULL; - } - if (vma->vm_start != addr) { - KGSL_CORE_ERR("vma address does not match mmap address\n"); - return NULL; - } + return vma; } @@ -1107,9 +1095,30 @@ kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_device_private *dev_priv, result = -EINVAL; goto error; } - len = vma->vm_end - vma->vm_start; - if (len == 0) { - KGSL_CORE_ERR("Invalid vma region length %d\n", len); + + /* + * If the user specified a length, use it, otherwise try to + * infer the length if the vma region + */ + if (param->gpuaddr != 0) { + len = param->gpuaddr; + } else { + /* + * For this to work, we have to assume the VMA region is only + * for this single allocation. If it isn't, then bail out + */ + if (vma->vm_pgoff || (param->hostptr != vma->vm_start)) { + KGSL_CORE_ERR("VMA region does not match hostaddr\n"); + result = -EINVAL; + goto error; + } + + len = vma->vm_end - vma->vm_start; + } + + /* Make sure it fits */ + if (len == 0 || param->hostptr + len > vma->vm_end) { + KGSL_CORE_ERR("Invalid memory allocation length %d\n", len); result = -EINVAL; goto error; } @@ -1328,11 +1337,17 @@ static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry, struct vm_area_struct *vma; struct file *filep, *vmfile; unsigned long len; + unsigned int hostaddr = (unsigned int) hostptr; - vma = kgsl_get_vma_from_start_addr((unsigned long) hostptr); + vma = kgsl_get_vma_from_start_addr(hostaddr); if (vma == NULL) return -EINVAL; + if (vma->vm_pgoff || vma->vm_start != hostaddr) { + KGSL_CORE_ERR("Invalid vma region\n"); + return -EINVAL; + } + len = vma->vm_end - vma->vm_start; if (size == 0) From 9acac3c844198f5b5cf43e816d9be63e5a87dbdd Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Mon, 8 Aug 2011 16:09:38 -0600 Subject: [PATCH 2062/2556] msm: kgsl: switch sprintf calls to snprintf Change-Id: I7d3be565db3e0555c94a345995f60e21d3bbe434 Signed-off-by: Jeremy Gebben --- drivers/gpu/msm/kgsl_mmu.c | 19 ++++++++++--------- drivers/gpu/msm/kgsl_pwrctrl.c | 4 ++-- drivers/gpu/msm/kgsl_sharedmem.c | 4 ++-- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c index fe441d723f55a..bc35c411164a8 100644 --- a/drivers/gpu/msm/kgsl_mmu.c +++ b/drivers/gpu/msm/kgsl_mmu.c @@ -35,7 +35,7 @@ sysfs_show_ptpool_entries(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", kgsl_driver.ptpool.entries); + return snprintf(buf, PAGE_SIZE, "%d\n", kgsl_driver.ptpool.entries); } static ssize_t @@ -43,7 +43,8 @@ sysfs_show_ptpool_min(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", kgsl_driver.ptpool.static_entries); + return snprintf(buf, PAGE_SIZE, "%d\n", + kgsl_driver.ptpool.static_entries); } static ssize_t @@ -51,7 +52,7 @@ sysfs_show_ptpool_chunks(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", kgsl_driver.ptpool.chunks); + return snprintf(buf, PAGE_SIZE, "%d\n", kgsl_driver.ptpool.chunks); } static ssize_t @@ -59,7 +60,7 @@ sysfs_show_ptpool_ptsize(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", kgsl_driver.ptpool.ptsize); + return snprintf(buf, PAGE_SIZE, "%d\n", kgsl_driver.ptpool.ptsize); } static struct kobj_attribute attr_ptpool_entries = { @@ -372,7 +373,7 @@ sysfs_show_entries(struct kobject *kobj, pt = _get_pt_from_kobj(kobj); if (pt) - ret += sprintf(buf, "%d\n", pt->stats.entries); + ret += snprintf(buf, PAGE_SIZE, "%d\n", pt->stats.entries); mutex_unlock(&kgsl_driver.pt_mutex); return ret; @@ -390,7 +391,7 @@ sysfs_show_mapped(struct kobject *kobj, pt = _get_pt_from_kobj(kobj); if (pt) - ret += sprintf(buf, "%d\n", pt->stats.mapped); + ret += snprintf(buf, PAGE_SIZE, "%d\n", pt->stats.mapped); mutex_unlock(&kgsl_driver.pt_mutex); return ret; @@ -408,7 +409,7 @@ sysfs_show_va_range(struct kobject *kobj, pt = _get_pt_from_kobj(kobj); if (pt) - ret += sprintf(buf, "0x%x\n", pt->va_range); + ret += snprintf(buf, PAGE_SIZE, "0x%x\n", pt->va_range); mutex_unlock(&kgsl_driver.pt_mutex); return ret; @@ -426,7 +427,7 @@ sysfs_show_max_mapped(struct kobject *kobj, pt = _get_pt_from_kobj(kobj); if (pt) - ret += sprintf(buf, "%d\n", pt->stats.max_mapped); + ret += snprintf(buf, PAGE_SIZE, "%d\n", pt->stats.max_mapped); mutex_unlock(&kgsl_driver.pt_mutex); return ret; @@ -444,7 +445,7 @@ sysfs_show_max_entries(struct kobject *kobj, pt = _get_pt_from_kobj(kobj); if (pt) - ret += sprintf(buf, "%d\n", pt->stats.max_entries); + ret += snprintf(buf, PAGE_SIZE, "%d\n", pt->stats.max_entries); mutex_unlock(&kgsl_driver.pt_mutex); return ret; diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 3cac7b18ffec2..35bbdc9ab5f51 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -190,7 +190,7 @@ static int kgsl_pwrctrl_pwrnap_show(struct device *dev, { struct kgsl_device *device = kgsl_device_from_dev(dev); struct kgsl_pwrctrl *pwr = &device->pwrctrl; - return sprintf(buf, "%d\n", pwr->nap_allowed); + return snprintf(buf, PAGE_SIZE, "%d\n", pwr->nap_allowed); } @@ -233,7 +233,7 @@ static int kgsl_pwrctrl_idle_timer_show(struct device *dev, { struct kgsl_device *device = kgsl_device_from_dev(dev); struct kgsl_pwrctrl *pwr = &device->pwrctrl; - return sprintf(buf, "%d\n", pwr->interval_timeout); + return snprintf(buf, PAGE_SIZE, "%d\n", pwr->interval_timeout); } static int kgsl_pwrctrl_scaling_governor_store(struct device *dev, diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index 92d9517c4bc1a..36ff19c7e8800 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -151,10 +151,10 @@ static int kgsl_drv_histogram_show(struct device *dev, int i; for (i = 0; i < 16; i++) - len += sprintf(buf + len, "%d ", + len += snprintf(buf + len, PAGE_SIZE - len, "%d ", kgsl_driver.stats.histogram[i]); - len += sprintf(buf + len, "\n"); + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); return len; } From 92366a82e3849b84d34e2ff8cbc9e24ef6860260 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Mon, 8 Aug 2011 16:05:09 -0600 Subject: [PATCH 2063/2556] msm: kgsl: Don't lookup the device with inodep if we don't have to kgsl_open does a fine job of setting the private members of struct file so use that in the other fops functions rather then looking up the device via the inode minor every time. Change-Id: Ic0dedbadc56d8a8c5d29f6952a319b93aaa0efd0 Signed-off-by: Jordan Crouse --- drivers/gpu/msm/kgsl.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index d0511a40a39bb..fab56fa1f5b3e 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -542,27 +542,19 @@ kgsl_put_process_private(struct kgsl_device *device, static int kgsl_release(struct inode *inodep, struct file *filep) { int result = 0; - struct kgsl_device_private *dev_priv = NULL; - struct kgsl_process_private *private = NULL; - struct kgsl_device *device; + struct kgsl_device_private *dev_priv = filep->private_data; + struct kgsl_process_private *private = dev_priv->process_priv; + struct kgsl_device *device = dev_priv->device; struct kgsl_context *context; int next = 0; - device = kgsl_driver.devp[iminor(inodep)]; - BUG_ON(device == NULL); - - dev_priv = (struct kgsl_device_private *) filep->private_data; - BUG_ON(dev_priv == NULL); - BUG_ON(device != dev_priv->device); - /* private could be null if kgsl_open is not successful */ - private = dev_priv->process_priv; filep->private_data = NULL; mutex_lock(&device->mutex); kgsl_check_suspended(device); while (1) { - context = idr_get_next(&dev_priv->device->context_idr, &next); + context = idr_get_next(&device->context_idr, &next); if (context == NULL) break; @@ -1739,14 +1731,10 @@ static struct vm_operations_struct kgsl_gpumem_vm_ops = { static int kgsl_mmap(struct file *file, struct vm_area_struct *vma) { unsigned long vma_offset = vma->vm_pgoff << PAGE_SHIFT; - struct inode *inodep = file->f_path.dentry->d_inode; struct kgsl_device_private *dev_priv = file->private_data; struct kgsl_process_private *private = dev_priv->process_priv; struct kgsl_mem_entry *entry; - struct kgsl_device *device; - - device = kgsl_driver.devp[iminor(inodep)]; - BUG_ON(device == NULL); + struct kgsl_device *device = dev_priv->device; /* Handle leagacy behavior for memstore */ From 671fec435e1b725f4e8ca71364c0b99dc13c3fe0 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Mon, 8 Aug 2011 17:00:06 -0600 Subject: [PATCH 2064/2556] msm: kgsl: Check for NULL from kgsl_get_device_from_dev Change-Id: Ic0dedbad8ee97511baad8496e14887479c98ec7c Signed-off-by: Jordan Crouse --- drivers/gpu/msm/kgsl_pwrctrl.c | 39 ++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 35bbdc9ab5f51..dc272f3160106 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -88,7 +88,11 @@ static int __gpuclk_store(int max, struct device *dev, { int ret, i, delta = 5000000; unsigned long val; struct kgsl_device *device = kgsl_device_from_dev(dev); - struct kgsl_pwrctrl *pwr = &device->pwrctrl; + struct kgsl_pwrctrl *pwr; + + if (device == NULL) + return 0; + pwr = &device->pwrctrl; ret = sscanf(buf, "%ld", &val); if (ret != 1) @@ -134,7 +138,10 @@ static int kgsl_pwrctrl_max_gpuclk_show(struct device *dev, char *buf) { struct kgsl_device *device = kgsl_device_from_dev(dev); - struct kgsl_pwrctrl *pwr = &device->pwrctrl; + struct kgsl_pwrctrl *pwr; + if (device == NULL) + return 0; + pwr = &device->pwrctrl; return snprintf(buf, PAGE_SIZE, "%d\n", pwr->pwrlevels[pwr->thermal_pwrlevel].gpu_freq); } @@ -151,7 +158,10 @@ static int kgsl_pwrctrl_gpuclk_show(struct device *dev, char *buf) { struct kgsl_device *device = kgsl_device_from_dev(dev); - struct kgsl_pwrctrl *pwr = &device->pwrctrl; + struct kgsl_pwrctrl *pwr; + if (device == NULL) + return 0; + pwr = &device->pwrctrl; return snprintf(buf, PAGE_SIZE, "%d\n", pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq); } @@ -163,9 +173,13 @@ static int kgsl_pwrctrl_pwrnap_store(struct device *dev, char temp[20]; unsigned long val; struct kgsl_device *device = kgsl_device_from_dev(dev); - struct kgsl_pwrctrl *pwr = &device->pwrctrl; + struct kgsl_pwrctrl *pwr; int rc; + if (device == NULL) + return 0; + pwr = &device->pwrctrl; + snprintf(temp, sizeof(temp), "%.*s", (int)min(count, sizeof(temp) - 1), buf); rc = strict_strtoul(temp, 0, &val); @@ -189,8 +203,9 @@ static int kgsl_pwrctrl_pwrnap_show(struct device *dev, char *buf) { struct kgsl_device *device = kgsl_device_from_dev(dev); - struct kgsl_pwrctrl *pwr = &device->pwrctrl; - return snprintf(buf, PAGE_SIZE, "%d\n", pwr->nap_allowed); + if (device == NULL) + return 0; + return snprintf(buf, PAGE_SIZE, "%d\n", device->pwrctrl.nap_allowed); } @@ -201,11 +216,15 @@ static int kgsl_pwrctrl_idle_timer_store(struct device *dev, char temp[20]; unsigned long val; struct kgsl_device *device = kgsl_device_from_dev(dev); - struct kgsl_pwrctrl *pwr = &device->pwrctrl; + struct kgsl_pwrctrl *pwr; const long div = 1000/HZ; static unsigned int org_interval_timeout = 1; int rc; + if (device == NULL) + return 0; + pwr = &device->pwrctrl; + snprintf(temp, sizeof(temp), "%.*s", (int)min(count, sizeof(temp) - 1), buf); rc = strict_strtoul(temp, 0, &val); @@ -232,8 +251,10 @@ static int kgsl_pwrctrl_idle_timer_show(struct device *dev, char *buf) { struct kgsl_device *device = kgsl_device_from_dev(dev); - struct kgsl_pwrctrl *pwr = &device->pwrctrl; - return snprintf(buf, PAGE_SIZE, "%d\n", pwr->interval_timeout); + if (device == NULL) + return 0; + return snprintf(buf, PAGE_SIZE, "%d\n", + device->pwrctrl.interval_timeout); } static int kgsl_pwrctrl_scaling_governor_store(struct device *dev, From 3f3832ee3192776039769edb4b528be84f8f64ef Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Mon, 8 Aug 2011 16:33:49 -0600 Subject: [PATCH 2065/2556] msm: kgsl: fix error handling in kgsl_ioct_sharedmem_flush_cache For some error conditions it was possible to exit this function without unlocking the spinlock. Change-Id: I9937e3ba45d93e5f33b53921ac44e119ff994a67 Signed-off-by: Jeremy Gebben --- drivers/gpu/msm/kgsl.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index fab56fa1f5b3e..715ff065607c4 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1506,26 +1506,25 @@ kgsl_ioctl_sharedmem_flush_cache(struct kgsl_device_private *dev_priv, if (!entry) { KGSL_CORE_ERR("invalid gpuaddr %08x\n", param->gpuaddr); result = -EINVAL; - } else { - if (!entry->memdesc.hostptr) - entry->memdesc.hostptr = + goto done; + } + if (!entry->memdesc.hostptr) + entry->memdesc.hostptr = kgsl_gpuaddr_to_vaddr(&entry->memdesc, param->gpuaddr, &entry->memdesc.size); - if (!entry->memdesc.hostptr) { - KGSL_CORE_ERR("invalid hostptr with gpuaddr %08x\n", - param->gpuaddr); + if (!entry->memdesc.hostptr) { + KGSL_CORE_ERR("invalid hostptr with gpuaddr %08x\n", + param->gpuaddr); goto done; - } + } - kgsl_cache_range_op(&entry->memdesc, KGSL_CACHE_OP_CLEAN); + kgsl_cache_range_op(&entry->memdesc, KGSL_CACHE_OP_CLEAN); - /* Statistics - keep track of how many flushes each process - does */ - private->stats.flushes++; - } - spin_unlock(&private->mem_lock); + /* Statistics - keep track of how many flushes each process does */ + private->stats.flushes++; done: + spin_unlock(&private->mem_lock); return result; } From 67cfeb3b6ee7b73815fdb9c032f909a4793ecb51 Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Mon, 8 Aug 2011 17:04:11 -0600 Subject: [PATCH 2066/2556] msm: kgsl: fix parameter checking in adreno_ringbuffer_issueibcmds Change-Id: Icdcaad5f1897a1264e13d687644effd48fe542d5 Signed-off-by: Jeremy Gebben --- drivers/gpu/msm/adreno_ringbuffer.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index 300f318177813..2b54b592a49c7 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -672,16 +672,15 @@ adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv, unsigned int *link; unsigned int *cmds; unsigned int i; - struct adreno_context *drawctxt = context->devctxt; + struct adreno_context *drawctxt; if (device->state & KGSL_STATE_HUNG) return -EBUSY; if (!(adreno_dev->ringbuffer.flags & KGSL_FLAGS_STARTED) || - context == NULL) + context == NULL || ibdesc == 0 || numibs == 0) return -EINVAL; - BUG_ON(ibdesc == 0); - BUG_ON(numibs == 0); + drawctxt = context->devctxt; if (drawctxt->flags & CTXT_FLAGS_GPU_HANG) { KGSL_CTXT_WARN(device, "Context %p caused a gpu hang.." From b0f101c933c77152002d3615382bc48b7514d88e Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Mon, 8 Aug 2011 16:44:07 -0600 Subject: [PATCH 2067/2556] msm: kgsl: fix possible memory leak in firmware loading Change-Id: Ia136ea2c1481ed4bcd4068b878b1db3d50e644be Signed-off-by: Jeremy Gebben --- drivers/gpu/msm/adreno_ringbuffer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index 2b54b592a49c7..45dc3f5b3b5b1 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -286,6 +286,7 @@ static int adreno_ringbuffer_load_pm4_ucode(struct kgsl_device *device) if (len % ((sizeof(uint32_t) * 3)) != sizeof(uint32_t)) { KGSL_DRV_ERR(device, "Bad firmware size: %d\n", len); ret = -EINVAL; + kfree(ptr); goto err; } @@ -328,6 +329,7 @@ static int adreno_ringbuffer_load_pfp_ucode(struct kgsl_device *device) if (len % sizeof(uint32_t) != 0) { KGSL_DRV_ERR(device, "Bad firmware size: %d\n", len); ret = -EINVAL; + kfree(ptr); goto err; } From 27e62be433dcd05d85442246b4601c4e1f90693f Mon Sep 17 00:00:00 2001 From: Sushmita Susheelendra Date: Mon, 9 May 2011 16:40:02 -0600 Subject: [PATCH 2068/2556] msm: kgsl: Fix build errors when CONFIG_MSM_KGSL_CFF_DUMP is on Change-Id: I654d12ef1d58bd33d829de9eb375041cf7858d0d Signed-off-by: Sushmita Susheelendra --- drivers/gpu/msm/kgsl_cffdump.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c index 138f4eace3bab..fe24d15c6a83e 100644 --- a/drivers/gpu/msm/kgsl_cffdump.c +++ b/drivers/gpu/msm/kgsl_cffdump.c @@ -24,6 +24,9 @@ #include "kgsl.h" #include "kgsl_cffdump.h" #include "kgsl_debugfs.h" +#include "kgsl_log.h" +#include "kgsl_sharedmem.h" +#include "adreno_pm4types.h" static struct rchan *chan; static struct dentry *dir; @@ -49,7 +52,7 @@ struct cff_op_write_reg { unsigned char op; uint addr; uint value; -} __attribute__((packed)); +} __packed; #define CFF_OP_POLL_REG 0x00000004 struct cff_op_poll_reg { @@ -57,12 +60,12 @@ struct cff_op_poll_reg { uint addr; uint value; uint mask; -} __attribute__((packed)); +} __packed; #define CFF_OP_WAIT_IRQ 0x00000005 struct cff_op_wait_irq { unsigned char op; -} __attribute__((packed)); +} __packed; #define CFF_OP_VERIFY_MEM_FILE 0x00000007 #define CFF_OP_RMW 0x0000000a @@ -72,7 +75,7 @@ struct cff_op_write_mem { unsigned char op; uint addr; uint value; -} __attribute__((packed)); +} __packed; #define CFF_OP_WRITE_MEMBUF 0x0000000c struct cff_op_write_membuf { @@ -80,12 +83,12 @@ struct cff_op_write_membuf { uint addr; ushort count; uint buffer[MEMBUF_SIZE]; -} __attribute__((packed)); +} __packed; #define CFF_OP_EOF 0xffffffff struct cff_op_eof { unsigned char op; -} __attribute__((packed)); +} __packed; static void b64_encodeblock(unsigned char in[3], unsigned char out[4], int len) @@ -342,7 +345,7 @@ void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv, src = kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr, &host_size); if (src == NULL || host_size < sizebytes) { - KGSL_CORE_ERR(("did not find mapping for " + KGSL_CORE_ERR("did not find mapping for " "gpuaddr: 0x%08x, m->host: 0x%p, phys: 0x%08x\n", gpuaddr, memdesc->hostptr, memdesc->physaddr); return; @@ -354,8 +357,8 @@ void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv, mb(); - kgsl_cache_range_op(memdesc->hostptr, memdesc->size, - memdesc->type, KGSL_CACHE_OP_INV); + kgsl_cache_range_op((struct kgsl_memdesc *)memdesc, + KGSL_CACHE_OP_INV); } BUG_ON(physaddr > 0x66000000 && physaddr < 0x66ffffff); @@ -523,8 +526,8 @@ bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv, return true; } else { mb(); - kgsl_cache_range_op(memdesc->hostptr, memdesc->size, - memdesc->type, KGSL_CACHE_OP_INV); + kgsl_cache_range_op((struct kgsl_memdesc *)memdesc, + KGSL_CACHE_OP_INV); } #ifdef DEBUG From e3a4ccd3aabe42b5f10c4942ec9654a0bb01f344 Mon Sep 17 00:00:00 2001 From: Tarun Karra Date: Thu, 18 Aug 2011 13:30:24 -0700 Subject: [PATCH 2069/2556] msm: kgsl: changed strncat to strlcat Changed strncat to strlcat to pervent buffer overflow. Change-Id: Ica6ee3dfcec2fc3a2f39d27921b7c242bbcdbe11 Signed-off-by: Tarun Karra --- drivers/gpu/msm/adreno_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/msm/adreno_debugfs.c b/drivers/gpu/msm/adreno_debugfs.c index 527ada5627962..487e1f7fe10fe 100644 --- a/drivers/gpu/msm/adreno_debugfs.c +++ b/drivers/gpu/msm/adreno_debugfs.c @@ -106,7 +106,7 @@ static int kgsl_hex_dump(const char *prefix, int c, uint8_t *data, ss = snprintf(linebuf, sizeof(linebuf), prefix, c); hex_dump_to_buffer(data, linec, rowc, 4, linebuf+ss, sizeof(linebuf)-ss, 0); - strncat(linebuf, "\n", sizeof(linebuf)); + strlcat(linebuf, "\n", sizeof(linebuf)); linebuf[sizeof(linebuf)-1] = 0; ss = strlen(linebuf); if (copy_to_user(buff, linebuf, ss+1)) From 35741150d2e364d6fd294c863735437148910ead Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Mon, 18 Apr 2011 15:11:21 -0600 Subject: [PATCH 2070/2556] msm: kgsl: cffdump syncmem ioctl This ioctl is needed to track memory writes done from userspace so that this data is correctly captured in the dump. Change-Id: Icc748cf753fe12a2f23a013a29d67f1193e66031 Signed-off-by: Jeremy Gebben --- drivers/gpu/msm/kgsl.c | 20 ++++++++++++++++++++ include/linux/msm_kgsl.h | 11 ++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 715ff065607c4..bf57db6034cbf 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1560,6 +1560,24 @@ kgsl_ioctl_gpumem_alloc(struct kgsl_device_private *dev_priv, kgsl_check_idle(dev_priv->device); return result; } +static long kgsl_ioctl_cff_syncmem(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + int result = 0; + struct kgsl_cff_syncmem *param = data; + struct kgsl_process_private *private = dev_priv->process_priv; + struct kgsl_mem_entry *entry = NULL; + + spin_lock(&private->mem_lock); + entry = kgsl_sharedmem_find_region(private, param->gpuaddr, param->len); + if (entry) + kgsl_cffdump_syncmem(dev_priv, &entry->memdesc, param->gpuaddr, + param->len, true); + else + result = -EINVAL; + spin_unlock(&private->mem_lock); + return result; +} typedef long (*kgsl_ioctl_func_t)(struct kgsl_device_private *, unsigned int, void *); @@ -1598,6 +1616,8 @@ static const struct { kgsl_ioctl_sharedmem_flush_cache, 0), KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_ALLOC, kgsl_ioctl_gpumem_alloc, 0), + KGSL_IOCTL_FUNC(IOCTL_KGSL_CFF_SYNCMEM, + kgsl_ioctl_cff_syncmem, 0), }; static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index faa6459e273b4..06cb2e520ac5f 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -35,7 +35,7 @@ #define _MSM_KGSL_H #define KGSL_VERSION_MAJOR 3 -#define KGSL_VERSION_MINOR 5 +#define KGSL_VERSION_MINOR 6 /*context flags */ #define KGSL_CONTEXT_SAVE_GMEM 1 @@ -431,6 +431,15 @@ struct kgsl_gpumem_alloc { #define IOCTL_KGSL_GPUMEM_ALLOC \ _IOWR(KGSL_IOC_TYPE, 0x2f, struct kgsl_gpumem_alloc) +struct kgsl_cff_syncmem { + unsigned int gpuaddr; + unsigned int len; + unsigned int __pad[2]; /* For future binary compatibility */ +}; + +#define IOCTL_KGSL_CFF_SYNCMEM \ + _IOW(KGSL_IOC_TYPE, 0x30, struct kgsl_cff_syncmem) + #ifdef __KERNEL__ #ifdef CONFIG_MSM_KGSL_DRM int kgsl_gem_obj_addr(int drm_fd, int handle, unsigned long *start, From 9c842e960c9d05509fbb68155e1226bb803f1c27 Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Mon, 18 Apr 2011 15:23:16 -0600 Subject: [PATCH 2071/2556] msm: kgsl: add MEMORY_BASE and HANG packets These are changes to the cffdump output format. MEMORY_BASE reports the address range we're using at the start of the dump, which helps the postprocessors validate memory writes. HANG is emitted at the start of kgsl_postmortem_dump(), and can be used by the postprocessors to stop when the hang occurs. Change-Id: Ia60557d34bcaf3a20b3852f880a27f3605fecad2 Signed-off-by: Jeremy Gebben --- drivers/gpu/msm/adreno_postmortem.c | 3 ++ drivers/gpu/msm/kgsl_cffdump.c | 52 +++++++++++++++++++++++++++++ drivers/gpu/msm/kgsl_cffdump.h | 7 ++++ 3 files changed, 62 insertions(+) diff --git a/drivers/gpu/msm/adreno_postmortem.c b/drivers/gpu/msm/adreno_postmortem.c index 509f25e21e6da..ed08d5d535a81 100644 --- a/drivers/gpu/msm/adreno_postmortem.c +++ b/drivers/gpu/msm/adreno_postmortem.c @@ -20,6 +20,7 @@ #include "adreno_ringbuffer.h" #include "adreno_postmortem.h" #include "adreno_debugfs.h" +#include "kgsl_cffdump.h" #include "a200_reg.h" @@ -799,6 +800,8 @@ int adreno_postmortem_dump(struct kgsl_device *device, int manual) BUG_ON(device == NULL); + kgsl_cffdump_hang(device->id); + /* For a manual dump, make sure that the system is idle */ if (manual) { diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c index fe24d15c6a83e..fbb78117d57fc 100644 --- a/drivers/gpu/msm/kgsl_cffdump.c +++ b/drivers/gpu/msm/kgsl_cffdump.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "kgsl.h" #include "kgsl_cffdump.h" @@ -85,6 +86,19 @@ struct cff_op_write_membuf { uint buffer[MEMBUF_SIZE]; } __packed; +#define CFF_OP_MEMORY_BASE 0x0000000d +struct cff_op_memory_base { + unsigned char op; + uint base; + uint size; + uint gmemsize; +} __packed; + +#define CFF_OP_HANG 0x0000000e +struct cff_op_hang { + unsigned char op; +} __packed; + #define CFF_OP_EOF 0xffffffff struct cff_op_eof { unsigned char op; @@ -196,6 +210,8 @@ static void cffdump_printline(int id, uint opcode, uint op1, uint op2, struct cff_op_write_reg cff_op_write_reg; struct cff_op_poll_reg cff_op_poll_reg; struct cff_op_wait_irq cff_op_wait_irq; + struct cff_op_memory_base cff_op_memory_base; + struct cff_op_hang cff_op_hang; struct cff_op_eof cff_op_eof; unsigned char out_buf[sizeof(cff_op_write_membuf)/3*4 + 16]; void *data; @@ -243,6 +259,21 @@ static void cffdump_printline(int id, uint opcode, uint op1, uint op2, len = sizeof(cff_op_wait_irq); break; + case CFF_OP_MEMORY_BASE: + cff_op_memory_base.op = opcode; + cff_op_memory_base.base = op1; + cff_op_memory_base.size = op2; + cff_op_memory_base.gmemsize = op3; + data = &cff_op_memory_base; + len = sizeof(cff_op_memory_base); + break; + + case CFF_OP_HANG: + cff_op_hang.op = opcode; + data = &cff_op_hang; + len = sizeof(cff_op_hang); + break; + case CFF_OP_EOF: cff_op_eof.op = opcode; data = &cff_op_eof; @@ -306,6 +337,27 @@ void kgsl_cffdump_destroy() void kgsl_cffdump_open(enum kgsl_deviceid device_id) { + /*TODO: move this to where we can report correct gmemsize*/ + unsigned int va_base; + + if (cpu_is_msm8x60() || cpu_is_msm8960()) + va_base = 0x40000000; + else + va_base = 0x20000000; + + kgsl_cffdump_memory_base(device_id, va_base, + CONFIG_MSM_KGSL_PAGE_TABLE_SIZE, SZ_256K); +} + +void kgsl_cffdump_memory_base(enum kgsl_deviceid device_id, unsigned int base, + unsigned int range, unsigned gmemsize) +{ + cffdump_printline(device_id, CFF_OP_MEMORY_BASE, base, range, gmemsize); +} + +void kgsl_cffdump_hang(enum kgsl_deviceid device_id) +{ + cffdump_printline(device_id, CFF_OP_HANG, 0, 0, 0); } void kgsl_cffdump_close(enum kgsl_deviceid device_id) diff --git a/drivers/gpu/msm/kgsl_cffdump.h b/drivers/gpu/msm/kgsl_cffdump.h index 147dda92c16ae..5ddd0035e3baa 100644 --- a/drivers/gpu/msm/kgsl_cffdump.h +++ b/drivers/gpu/msm/kgsl_cffdump.h @@ -53,6 +53,11 @@ bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv, bool check_only); static inline bool kgsl_cffdump_flags_no_memzero(void) { return true; } +void kgsl_cffdump_memory_base(enum kgsl_deviceid device_id, unsigned int base, + unsigned int range, unsigned int gmemsize); + +void kgsl_cffdump_hang(enum kgsl_deviceid device_id); + #else #define kgsl_cffdump_init() (void)0 @@ -67,6 +72,8 @@ static inline bool kgsl_cffdump_flags_no_memzero(void) { return true; } #define kgsl_cffdump_parse_ibs(dev_priv, memdesc, gpuaddr, \ sizedwords, check_only) true #define kgsl_cffdump_flags_no_memzero() true +#define kgsl_cffdump_memory_base(base, range, gmemsize) (void)0 +#define kgsl_cffdump_hang(device_id) (void)0 #endif /* CONFIG_MSM_KGSL_CFF_DUMP */ From 1214e2b22fe01c565ab46b9cddb7d1d3673b06ae Mon Sep 17 00:00:00 2001 From: Sushmita Susheelendra Date: Wed, 11 May 2011 17:15:58 -0600 Subject: [PATCH 2072/2556] msm: kgsl: Add CFF user event ioctl to write data to cff dumps Add the ioctl IOCTL_KGSL_CFF_USER_EVENT to write data to cff dump. Define a new CFF packet cff_op_user_event for this generic user event. Write the surface parameter data packet and verify mem file packet to cff dumps Change-Id: Iaadc2da2c211b2dbb824cb883c06b89bb57ff4a0 Signed-off-by: Sushmita Susheelendra --- drivers/gpu/msm/kgsl.c | 14 +++++++ drivers/gpu/msm/kgsl_cffdump.c | 67 ++++++++++++++++++++++++++-------- drivers/gpu/msm/kgsl_cffdump.h | 5 +++ include/linux/msm_kgsl.h | 14 ++++++- 4 files changed, 83 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index bf57db6034cbf..1873473c95bb3 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1579,6 +1579,18 @@ static long kgsl_ioctl_cff_syncmem(struct kgsl_device_private *dev_priv, return result; } +static long kgsl_ioctl_cff_user_event(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + int result = 0; + struct kgsl_cff_user_event *param = data; + + kgsl_cffdump_user_event(param->cff_opcode, param->op1, param->op2, + param->op3, param->op4, param->op5); + + return result; +} + typedef long (*kgsl_ioctl_func_t)(struct kgsl_device_private *, unsigned int, void *); @@ -1618,6 +1630,8 @@ static const struct { kgsl_ioctl_gpumem_alloc, 0), KGSL_IOCTL_FUNC(IOCTL_KGSL_CFF_SYNCMEM, kgsl_ioctl_cff_syncmem, 0), + KGSL_IOCTL_FUNC(IOCTL_KGSL_CFF_USER_EVENT, + kgsl_ioctl_cff_user_event, 0), }; static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c index fbb78117d57fc..74075bf40eddb 100644 --- a/drivers/gpu/msm/kgsl_cffdump.c +++ b/drivers/gpu/msm/kgsl_cffdump.c @@ -68,7 +68,6 @@ struct cff_op_wait_irq { unsigned char op; } __packed; -#define CFF_OP_VERIFY_MEM_FILE 0x00000007 #define CFF_OP_RMW 0x0000000a #define CFF_OP_WRITE_MEM 0x0000000b @@ -104,6 +103,17 @@ struct cff_op_eof { unsigned char op; } __packed; +#define CFF_OP_VERIFY_MEM_FILE 0x00000007 +#define CFF_OP_WRITE_SURFACE_PARAMS 0x00000011 +struct cff_op_user_event { + unsigned char op; + unsigned int op1; + unsigned int op2; + unsigned int op3; + unsigned int op4; + unsigned int op5; +} __packed; + static void b64_encodeblock(unsigned char in[3], unsigned char out[4], int len) { @@ -205,7 +215,7 @@ static void cffdump_membuf(int id, unsigned char *out_buf, int out_bufsize) } static void cffdump_printline(int id, uint opcode, uint op1, uint op2, - uint op3) + uint op3, uint op4, uint op5) { struct cff_op_write_reg cff_op_write_reg; struct cff_op_poll_reg cff_op_poll_reg; @@ -213,6 +223,7 @@ static void cffdump_printline(int id, uint opcode, uint op1, uint op2, struct cff_op_memory_base cff_op_memory_base; struct cff_op_hang cff_op_hang; struct cff_op_eof cff_op_eof; + struct cff_op_user_event cff_op_user_event; unsigned char out_buf[sizeof(cff_op_write_membuf)/3*4 + 16]; void *data; int len = 0, out_size; @@ -279,6 +290,18 @@ static void cffdump_printline(int id, uint opcode, uint op1, uint op2, data = &cff_op_eof; len = sizeof(cff_op_eof); break; + + case CFF_OP_WRITE_SURFACE_PARAMS: + case CFF_OP_VERIFY_MEM_FILE: + cff_op_user_event.op = opcode; + cff_op_user_event.op1 = op1; + cff_op_user_event.op2 = op2; + cff_op_user_event.op3 = op3; + cff_op_user_event.op4 = op4; + cff_op_user_event.op5 = op5; + data = &cff_op_user_event; + len = sizeof(cff_op_user_event); + break; } if (len) { @@ -352,17 +375,25 @@ void kgsl_cffdump_open(enum kgsl_deviceid device_id) void kgsl_cffdump_memory_base(enum kgsl_deviceid device_id, unsigned int base, unsigned int range, unsigned gmemsize) { - cffdump_printline(device_id, CFF_OP_MEMORY_BASE, base, range, gmemsize); + cffdump_printline(device_id, CFF_OP_MEMORY_BASE, base, + range, gmemsize, 0, 0); } void kgsl_cffdump_hang(enum kgsl_deviceid device_id) { - cffdump_printline(device_id, CFF_OP_HANG, 0, 0, 0); + cffdump_printline(device_id, CFF_OP_HANG, 0, 0, 0, 0, 0); } void kgsl_cffdump_close(enum kgsl_deviceid device_id) { - cffdump_printline(device_id, CFF_OP_EOF, 0, 0, 0); + cffdump_printline(device_id, CFF_OP_EOF, 0, 0, 0, 0, 0); +} + +void kgsl_cffdump_user_event(unsigned int cff_opcode, unsigned int op1, + unsigned int op2, unsigned int op3, + unsigned int op4, unsigned int op5) +{ + cffdump_printline(-1, cff_opcode, op1, op2, op3, op4, op5); } void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv, @@ -415,15 +446,15 @@ void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv, BUG_ON(physaddr > 0x66000000 && physaddr < 0x66ffffff); while (sizebytes > 3) { - cffdump_printline(-1, CFF_OP_WRITE_MEM, physaddr, *(uint *)src, - 0); - physaddr += 4; + cffdump_printline(-1, CFF_OP_WRITE_MEM, gpuaddr, *(uint *)src, + 0, 0, 0); + gpuaddr += 4; src += 4; sizebytes -= 4; } if (sizebytes > 0) - cffdump_printline(-1, CFF_OP_WRITE_MEM, physaddr, *(uint *)src, - 0); + cffdump_printline(-1, CFF_OP_WRITE_MEM, gpuaddr, *(uint *)src, + 0, 0, 0); } void kgsl_cffdump_setmem(uint addr, uint value, uint sizebytes) @@ -435,12 +466,14 @@ void kgsl_cffdump_setmem(uint addr, uint value, uint sizebytes) while (sizebytes > 3) { /* Use 32bit memory writes as long as there's at least * 4 bytes left */ - cffdump_printline(-1, CFF_OP_WRITE_MEM, addr, value, 0); + cffdump_printline(-1, CFF_OP_WRITE_MEM, addr, value, + 0, 0, 0); addr += 4; sizebytes -= 4; } if (sizebytes > 0) - cffdump_printline(-1, CFF_OP_WRITE_MEM, addr, value, 0); + cffdump_printline(-1, CFF_OP_WRITE_MEM, addr, value, + 0, 0, 0); } void kgsl_cffdump_regwrite(enum kgsl_deviceid device_id, uint addr, @@ -449,7 +482,8 @@ void kgsl_cffdump_regwrite(enum kgsl_deviceid device_id, uint addr, if (!kgsl_cff_dump_enable) return; - cffdump_printline(device_id, CFF_OP_WRITE_REG, addr, value, 0); + cffdump_printline(device_id, CFF_OP_WRITE_REG, addr, value, + 0, 0, 0); } void kgsl_cffdump_regpoll(enum kgsl_deviceid device_id, uint addr, @@ -458,7 +492,8 @@ void kgsl_cffdump_regpoll(enum kgsl_deviceid device_id, uint addr, if (!kgsl_cff_dump_enable) return; - cffdump_printline(device_id, CFF_OP_POLL_REG, addr, value, mask); + cffdump_printline(device_id, CFF_OP_POLL_REG, addr, value, + mask, 0, 0); } void kgsl_cffdump_slavewrite(uint addr, uint value) @@ -466,7 +501,7 @@ void kgsl_cffdump_slavewrite(uint addr, uint value) if (!kgsl_cff_dump_enable) return; - cffdump_printline(-1, CFF_OP_WRITE_REG, addr, value, 0); + cffdump_printline(-1, CFF_OP_WRITE_REG, addr, value, 0, 0, 0); } int kgsl_cffdump_waitirq(void) @@ -474,7 +509,7 @@ int kgsl_cffdump_waitirq(void) if (!kgsl_cff_dump_enable) return 0; - cffdump_printline(-1, CFF_OP_WAIT_IRQ, 0, 0, 0); + cffdump_printline(-1, CFF_OP_WAIT_IRQ, 0, 0, 0, 0, 0); return 1; } diff --git a/drivers/gpu/msm/kgsl_cffdump.h b/drivers/gpu/msm/kgsl_cffdump.h index 5ddd0035e3baa..d2f9d172811e6 100644 --- a/drivers/gpu/msm/kgsl_cffdump.h +++ b/drivers/gpu/msm/kgsl_cffdump.h @@ -51,6 +51,9 @@ void kgsl_cffdump_regpoll(enum kgsl_deviceid device_id, uint addr, bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv, const struct kgsl_memdesc *memdesc, uint gpuaddr, int sizedwords, bool check_only); +void kgsl_cffdump_user_event(unsigned int cff_opcode, unsigned int op1, + unsigned int op2, unsigned int op3, + unsigned int op4, unsigned int op5); static inline bool kgsl_cffdump_flags_no_memzero(void) { return true; } void kgsl_cffdump_memory_base(enum kgsl_deviceid device_id, unsigned int base, @@ -74,6 +77,8 @@ void kgsl_cffdump_hang(enum kgsl_deviceid device_id); #define kgsl_cffdump_flags_no_memzero() true #define kgsl_cffdump_memory_base(base, range, gmemsize) (void)0 #define kgsl_cffdump_hang(device_id) (void)0 +#define kgsl_cffdump_user_event(cff_opcode, op1, op2, op3, op4, op5) \ + (void)param #endif /* CONFIG_MSM_KGSL_CFF_DUMP */ diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index 06cb2e520ac5f..9af0605e7ead9 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -35,7 +35,7 @@ #define _MSM_KGSL_H #define KGSL_VERSION_MAJOR 3 -#define KGSL_VERSION_MINOR 6 +#define KGSL_VERSION_MINOR 7 /*context flags */ #define KGSL_CONTEXT_SAVE_GMEM 1 @@ -352,6 +352,18 @@ struct kgsl_sharedmem_free { #define IOCTL_KGSL_SHAREDMEM_FREE \ _IOW(KGSL_IOC_TYPE, 0x21, struct kgsl_sharedmem_free) +struct kgsl_cff_user_event { + unsigned char cff_opcode; + unsigned int op1; + unsigned int op2; + unsigned int op3; + unsigned int op4; + unsigned int op5; + unsigned int __pad[2]; +}; + +#define IOCTL_KGSL_CFF_USER_EVENT \ + _IOW(KGSL_IOC_TYPE, 0x31, struct kgsl_cff_user_event) struct kgsl_gmem_desc { unsigned int x; From 8d08d7fa3c9346d288da356a54d11c1f1e3e4d7e Mon Sep 17 00:00:00 2001 From: Ranjhith Kalisamy Date: Thu, 25 Aug 2011 15:07:26 +0530 Subject: [PATCH 2073/2556] msm: kgsl: Fix bug in postmortem dump register decode Change-Id: I813bf79d11b353a1f4d3ab065498cf17e77c4fc0 Signed-off-by: Ranjhith Kalisamy --- drivers/gpu/msm/adreno_postmortem.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/msm/adreno_postmortem.c b/drivers/gpu/msm/adreno_postmortem.c index ed08d5d535a81..de386f957177c 100644 --- a/drivers/gpu/msm/adreno_postmortem.c +++ b/drivers/gpu/msm/adreno_postmortem.c @@ -425,6 +425,9 @@ static int adreno_dump_fields_line(struct kgsl_device *device, for ( ; num && sptr < slen; num--, l++) { int ilen = strlen(l->display); + if (!l->show) + continue; + if (count) ilen += strlen(" | "); @@ -516,7 +519,7 @@ static int adreno_dump(struct kgsl_device *device) { char cmdFifo[16]; struct log_field lines[] = { - {rbbm_status & 0x000F, cmdFifo}, + {rbbm_status & 0x001F, cmdFifo}, {rbbm_status & BIT(5), "TC busy "}, {rbbm_status & BIT(8), "HIRQ pending"}, {rbbm_status & BIT(9), "CPRQ pending"}, From ec31f23ba817f5952484e27fc9ddc63977e47196 Mon Sep 17 00:00:00 2001 From: Ranjhith Kalisamy Date: Fri, 2 Sep 2011 20:01:06 +0530 Subject: [PATCH 2074/2556] msm: kgsl: Prevent writes to ringbuffer WPTR from being dropped Sometimes, the writes to ringbuffer WPTR register are dropped. This will make the GPU to go to idle before completing all the commands that it is submitted with. This workaround prevents the writes from being dropped. Change-Id: I65b4417dbd5868e589145d4aea3e7c787e366487 CRs-Fixed: 302173 Signed-off-by: Ranjhith Kalisamy --- drivers/gpu/msm/adreno.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 40d33a179f92a..d130607ff151f 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -557,7 +557,7 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), init_reftimestamp); - adreno_regwrite(device, REG_RBBM_DEBUG, 0x00080000); + adreno_regwrite(device, REG_RBBM_DEBUG, 0x000C0000); adreno_regwrite(device, REG_RBBM_INT_CNTL, GSL_RBBM_INT_MASK); From c069c12c5d9ba2ab09c1d1ea02df41bbbe010621 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Fri, 2 Sep 2011 14:34:32 -0600 Subject: [PATCH 2075/2556] msm: kgsl: Fix an over greedy CONFIG_ANDROID_PMEM ifdef kgsl_get_phys_file is used for both PMEM and the framebuffer, so adjust the #ifdef CONFIG_ANDROID_PMEM to only omit the PMEM specific parts. Change-Id: Ic0dedbadd01ed95db06880438ddb606b987525fb Signed-off-by: Jordan Crouse --- drivers/gpu/msm/kgsl.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 1873473c95bb3..229a50ccfa1d2 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1166,7 +1166,6 @@ static inline int _check_region(unsigned long start, unsigned long size, return (end > len); } -#ifdef CONFIG_ANDROID_PMEM static int kgsl_get_phys_file(int fd, unsigned long *start, unsigned long *len, unsigned long *vstart, struct file **filep) { @@ -1176,8 +1175,10 @@ static int kgsl_get_phys_file(int fd, unsigned long *start, unsigned long *len, struct fb_info *info; *filep = NULL; +#ifdef CONFIG_ANDROID_PMEM if (!get_pmem_file(fd, start, vstart, len, filep)) return 0; +#endif fbfile = fget(fd); if (fbfile == NULL) { @@ -1253,18 +1254,11 @@ static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, return 0; err: +#ifdef CONFIG_ANDROID_PMEM put_pmem_file(filep); +#endif return ret; } -#else -static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, - struct kgsl_pagetable *pagetable, - unsigned int fd, unsigned int offset, - size_t size) -{ - return -EINVAL; -} -#endif static int kgsl_setup_hostptr(struct kgsl_mem_entry *entry, struct kgsl_pagetable *pagetable, From 9093814323ffe329a577e1bc874c9760a4ca799e Mon Sep 17 00:00:00 2001 From: Lynus Vaz Date: Thu, 15 Sep 2011 14:43:40 +0530 Subject: [PATCH 2076/2556] msm: kgsl: Add AXI rate request on wakeup Actually vote for AXI rate. If gpuclk sysfs file is changed during device sleep, the previous AXI rate was retained instead of the new AXI rate. Change-Id: Iaf8d3037d6432870fc33a1ea0c26a4d577cd2f2d Signed-off-by: Lynus Vaz --- drivers/gpu/msm/kgsl_pwrctrl.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index dc272f3160106..5840dc00c60fa 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -395,8 +395,10 @@ void kgsl_pwrctrl_axi(struct kgsl_device *device, int state) &pwr->power_flags)) { KGSL_PWR_INFO(device, "axi off, device %d\n", device->id); - if (pwr->ebi1_clk) + if (pwr->ebi1_clk) { + clk_set_rate(pwr->ebi1_clk, 0); clk_disable(pwr->ebi1_clk); + } if (pwr->pcl) msm_bus_scale_client_update_request(pwr->pcl, 0); @@ -406,8 +408,12 @@ void kgsl_pwrctrl_axi(struct kgsl_device *device, int state) &pwr->power_flags)) { KGSL_PWR_INFO(device, "axi on, device %d\n", device->id); - if (pwr->ebi1_clk) + if (pwr->ebi1_clk) { clk_enable(pwr->ebi1_clk); + clk_set_rate(pwr->ebi1_clk, + pwr->pwrlevels[pwr->active_pwrlevel]. + bus_freq); + } if (pwr->pcl) msm_bus_scale_client_update_request(pwr->pcl, pwr->pwrlevels[pwr->active_pwrlevel]. From a16326517320ce2a89d5cb0b9bbf68cd4e7ec3d5 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Mon, 26 Sep 2011 14:45:47 -0600 Subject: [PATCH 2077/2556] msm: kgsl: Properly handle double mmapped memory In some situations, the same block of memory is mapped multiple times. Properly refcount that mapping otherwise we could get into a situation where the mem entry is completely dereferenced before its time. CRs-fixed: 306942 Change-Id: Ic0dedbad3c278dca6f9268e11ed23bb2336da651 Signed-off-by: Jordan Crouse --- drivers/gpu/msm/kgsl.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 229a50ccfa1d2..5373b0bfe5b2a 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1732,6 +1732,17 @@ kgsl_mmap_memstore(struct kgsl_device *device, struct vm_area_struct *vma) return result; } +/* + * kgsl_gpumem_vm_open is called whenever a vma region is copied or split. + * Increase the refcount to make sure that the accounting stays correct + */ + +static void kgsl_gpumem_vm_open(struct vm_area_struct *vma) +{ + struct kgsl_mem_entry *entry = vma->vm_private_data; + kgsl_mem_entry_get(entry); +} + static int kgsl_gpumem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { @@ -1751,6 +1762,7 @@ kgsl_gpumem_vm_close(struct vm_area_struct *vma) } static struct vm_operations_struct kgsl_gpumem_vm_ops = { + .open = kgsl_gpumem_vm_open, .fault = kgsl_gpumem_vm_fault, .close = kgsl_gpumem_vm_close, }; From fcb2bd2f31131e450ea3df8c503722637789454c Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Tue, 4 Oct 2011 09:31:29 -0600 Subject: [PATCH 2078/2556] msm: kgsl: Cleanup virtual files on module exit Make sure that all sysfs and debugfs entries are cleaned up when the driver exits. Change-Id: Ic0dedbad33d0e109de53ef95b8c07d56f2defe95 Signed-off-by: Jordan Crouse --- drivers/gpu/msm/kgsl.c | 2 ++ drivers/gpu/msm/kgsl_debugfs.c | 5 +++++ drivers/gpu/msm/kgsl_debugfs.h | 4 +++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 5373b0bfe5b2a..d8749fa87f52b 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -2086,6 +2086,8 @@ static void kgsl_core_exit(void) kgsl_drm_exit(); kgsl_cffdump_destroy(); + kgsl_core_debugfs_close(); + kgsl_sharedmem_uninit_sysfs(); } static int __init kgsl_core_init(void) diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c index 364ab1dcea7cd..d98586a9a9598 100644 --- a/drivers/gpu/msm/kgsl_debugfs.c +++ b/drivers/gpu/msm/kgsl_debugfs.c @@ -79,3 +79,8 @@ void kgsl_core_debugfs_init(void) { kgsl_debugfs_dir = debugfs_create_dir("kgsl", 0); } + +void kgsl_core_debugfs_close(void) +{ + debugfs_remove_recursive(kgsl_debugfs_dir); +} diff --git a/drivers/gpu/msm/kgsl_debugfs.h b/drivers/gpu/msm/kgsl_debugfs.h index 1e36fabba65d2..5e10988076def 100644 --- a/drivers/gpu/msm/kgsl_debugfs.h +++ b/drivers/gpu/msm/kgsl_debugfs.h @@ -18,6 +18,8 @@ struct kgsl_device; #ifdef CONFIG_DEBUG_FS void kgsl_core_debugfs_init(void); +void kgsl_core_debugfs_close(void); + void kgsl_device_debugfs_init(struct kgsl_device *device); extern struct dentry *kgsl_debugfs_dir; @@ -29,7 +31,7 @@ static inline struct dentry *kgsl_get_debugfs_dir(void) #else static inline void kgsl_core_debugfs_init(void) { } static inline void kgsl_device_debugfs_init(struct kgsl_device *device) { } - +static inline void kgsl_core_debugfs_close(void) { } static inline struct dentry *kgsl_get_debugfs_dir(void) { return NULL; } #endif From d38ae81f8454c45626e450c660dafa4c5833af44 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 8 Feb 2012 01:47:12 -0600 Subject: [PATCH 2079/2556] msm: board: mahimahi: remove commented pwrrail code --- arch/arm/mach-msm/board-mahimahi.c | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index ebdf653805bda..47d4cf925cf9b 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -276,26 +276,6 @@ static struct platform_device mahimahi_rfkill = { .id = -1, }; -#if 0 -#define PWR_RAIL_GRP_CLK 8 -static int mahimahi_kgsl_power_rail_mode(int follow_clk) -{ - int mode = follow_clk ? 0 : 1; - int rail_id = PWR_RAIL_GRP_CLK; - - return msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &rail_id, &mode); -} - -static int mahimahi_kgsl_power(bool on) -{ - int cmd; - int rail_id = PWR_RAIL_GRP_CLK; - - cmd = on ? PCOM_CLKCTL_RPC_RAIL_ENABLE : PCOM_CLKCTL_RPC_RAIL_DISABLE; - return msm_proc_comm(cmd, &rail_id, NULL); -} -#endif - /* start kgsl */ static struct resource kgsl_3d0_resources[] = { { @@ -1137,13 +1117,6 @@ static void __init mahimahi_init(void) gpio_request(MAHIMAHI_GPIO_DS2482_SLP_N, "ds2482_slp_n"); -#if 0 - /* set the gpu power rail to manual mode so clk en/dis will not - * turn off gpu power, and hang it on resume */ - mahimahi_kgsl_power_rail_mode(0); - mahimahi_kgsl_power(true); -#endif - msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; From 278bc60962283e000d28feb0aaf06370ef08d80c Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 8 Feb 2012 02:09:13 -0600 Subject: [PATCH 2080/2556] drivers: video: msm: remove old (unused) kgsl driver --- drivers/video/msm/Kconfig | 25 +- drivers/video/msm/Makefile | 3 - drivers/video/msm/gpu/kgsl/Makefile | 11 - drivers/video/msm/gpu/kgsl/kgsl.c | 1280 ------------ drivers/video/msm/gpu/kgsl/kgsl.h | 84 - drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c | 105 - drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h | 54 - drivers/video/msm/gpu/kgsl/kgsl_device.h | 141 -- drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c | 1824 ------------------ drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h | 120 -- drivers/video/msm/gpu/kgsl/kgsl_log.c | 292 --- drivers/video/msm/gpu/kgsl/kgsl_log.h | 105 - drivers/video/msm/gpu/kgsl/kgsl_mmu.c | 672 ------- drivers/video/msm/gpu/kgsl/kgsl_mmu.h | 169 -- drivers/video/msm/gpu/kgsl/kgsl_pm4types.h | 182 -- drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c | 837 -------- drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.h | 254 --- drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c | 300 --- drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h | 111 -- drivers/video/msm/gpu/kgsl/kgsl_yamato.c | 1004 ---------- drivers/video/msm/gpu/kgsl/yamato_reg.h | 400 ---- 21 files changed, 3 insertions(+), 7970 deletions(-) delete mode 100644 drivers/video/msm/gpu/kgsl/Makefile delete mode 100644 drivers/video/msm/gpu/kgsl/kgsl.c delete mode 100644 drivers/video/msm/gpu/kgsl/kgsl.h delete mode 100644 drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c delete mode 100644 drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h delete mode 100644 drivers/video/msm/gpu/kgsl/kgsl_device.h delete mode 100644 drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c delete mode 100644 drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h delete mode 100644 drivers/video/msm/gpu/kgsl/kgsl_log.c delete mode 100644 drivers/video/msm/gpu/kgsl/kgsl_log.h delete mode 100644 drivers/video/msm/gpu/kgsl/kgsl_mmu.c delete mode 100644 drivers/video/msm/gpu/kgsl/kgsl_mmu.h delete mode 100644 drivers/video/msm/gpu/kgsl/kgsl_pm4types.h delete mode 100644 drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c delete mode 100644 drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.h delete mode 100644 drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c delete mode 100644 drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h delete mode 100644 drivers/video/msm/gpu/kgsl/kgsl_yamato.c delete mode 100644 drivers/video/msm/gpu/kgsl/yamato_reg.h diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig index 8b4c1423749ad..defabe5a65591 100644 --- a/drivers/video/msm/Kconfig +++ b/drivers/video/msm/Kconfig @@ -1,5 +1,5 @@ config FB_MSM - tristate + tristate "MSM Framebuffer" depends on FB && ARCH_MSM select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -7,12 +7,12 @@ config FB_MSM default y config FB_MSM_LEGACY_MDP - bool + bool "MSM Legacy MDP (qsd8k)" depends on FB_MSM && (MSM_MDP31 || MSM_MDP22) default y config FB_MSM_MDP_PPP - bool + bool "MSM MDP PPP" depends on FB_MSM_LEGACY_MDP default y @@ -35,25 +35,6 @@ config FB_MSM_MDDI_NOVTEC bool "Support for Novtec MDDI panels" depends on FB_MSM_MDDI default n - -config GPU_MSM_KGSL - tristate "MSM 3D Graphics driver for Adreno class GPUs" - default n - depends on FB_MSM && (ARCH_QSD8X50 || ARCH_MSM7X30) - select GENERIC_ALLOCATOR - select CONFIG_FW_LOADER - help - 3D graphics driver for QSD8x50 and MSM7x27. Required to - use hardware accelerated OpenGL ES 2.0 and 1.1 on these - chips. - -config MSM_KGSL_MMU - bool "Turn on MMU for graphics driver " - depends on GPU_MSM_KGSL && MMU - default n - help - If enabled, the GPU driver will allocate memory from vmalloc - and enable the use of GPU MMU, instead of using pmem. config MSM_HDMI bool "Support for HDMI in QCT platform" diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile index 1b307877e7f37..696960dbadd82 100644 --- a/drivers/video/msm/Makefile +++ b/drivers/video/msm/Makefile @@ -32,7 +32,4 @@ obj-$(CONFIG_FB_MSM_MDDI_EPSON) += mddi_client_epson.o # MDP LCD controller driver obj-$(CONFIG_FB_MSM_LCDC) += mdp_lcdc.o -# Yamato GL driver -obj-$(CONFIG_GPU_MSM_KGSL) += gpu/kgsl/ - obj-$(CONFIG_MSM_HDMI) += hdmi/ diff --git a/drivers/video/msm/gpu/kgsl/Makefile b/drivers/video/msm/gpu/kgsl/Makefile deleted file mode 100644 index 0290b50c37e3c..0000000000000 --- a/drivers/video/msm/gpu/kgsl/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -msm_kgsl-objs = \ - kgsl_drawctxt.o \ - kgsl_cmdstream.o \ - kgsl.o \ - kgsl_log.o \ - kgsl_mmu.o \ - kgsl_ringbuffer.o \ - kgsl_sharedmem.o \ - kgsl_yamato.o - -obj-$(CONFIG_GPU_MSM_KGSL) += msm_kgsl.o diff --git a/drivers/video/msm/gpu/kgsl/kgsl.c b/drivers/video/msm/gpu/kgsl/kgsl.c deleted file mode 100644 index 884eea74c5599..0000000000000 --- a/drivers/video/msm/gpu/kgsl/kgsl.c +++ /dev/null @@ -1,1280 +0,0 @@ -/* -* Copyright (c) 2008-2009 QUALCOMM USA, INC. -* -* All source code in this file is licensed under the following license -* -* 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, you can find it at http://www.fsf.org -*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "kgsl.h" -#include "kgsl_drawctxt.h" -#include "kgsl_ringbuffer.h" -#include "kgsl_cmdstream.h" -#include "kgsl_log.h" - -#define KGSL_MAX_PRESERVED_BUFFERS 10 -#define KGSL_MAX_SIZE_OF_PRESERVED_BUFFER 0x10000 - -struct kgsl_file_private { - struct list_head list; - struct list_head mem_list; - uint32_t ctxt_id_mask; - struct kgsl_pagetable *pagetable; - unsigned long vmalloc_size; - struct list_head preserve_entry_list; - int preserve_list_size; -}; - -static void kgsl_put_phys_file(struct file *file); - -#ifdef CONFIG_MSM_KGSL_MMU -static long flush_l1_cache_range(unsigned long addr, int size) -{ - struct page *page; - pte_t *pte_ptr; - unsigned long end; - - for (end = addr; end < (addr + size); end += KGSL_PAGESIZE) { - pte_ptr = kgsl_get_pte_from_vaddr(end); - if (!pte_ptr) - return -EINVAL; - - page = pte_page(pte_val(*pte_ptr)); - if (!page) { - KGSL_DRV_ERR("could not find page for pte\n"); - pte_unmap(pte_ptr); - return -EINVAL; - } - - pte_unmap(pte_ptr); - flush_dcache_page(page); - } - - return 0; -} - -static long flush_l1_cache_all(struct kgsl_file_private *private) -{ - int result = 0; - struct kgsl_mem_entry *entry = NULL; - - kgsl_yamato_runpending(&kgsl_driver.yamato_device); - list_for_each_entry(entry, &private->mem_list, list) { - if (KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH & entry->memdesc.priv) { - result = - flush_l1_cache_range((unsigned long)entry-> - memdesc.hostptr, - entry->memdesc.size); - if (result) - goto done; - } - } -done: - return result; -} -#else -static inline long flush_l1_cache_range(unsigned long addr, int size) -{ return 0; } - -static inline long flush_l1_cache_all(struct kgsl_file_private *private) -{ return 0; } -#endif - -/*this is used for logging, so that we can call the dev_printk - functions without export struct kgsl_driver everywhere*/ -struct device *kgsl_driver_getdevnode(void) -{ - BUG_ON(kgsl_driver.pdev == NULL); - return &kgsl_driver.pdev->dev; -} - -/* the hw and clk enable/disable funcs must be either called from softirq or - * with mutex held */ -static void kgsl_clk_enable(void) -{ - clk_set_rate(kgsl_driver.ebi1_clk, 128000000); - clk_enable(kgsl_driver.imem_clk); - if (kgsl_driver.grp_pclk) - clk_enable(kgsl_driver.grp_pclk); - clk_enable(kgsl_driver.grp_clk); -} - -static void kgsl_clk_disable(void) -{ - clk_disable(kgsl_driver.grp_clk); - if (kgsl_driver.grp_pclk) - clk_disable(kgsl_driver.grp_pclk); - clk_disable(kgsl_driver.imem_clk); - clk_set_rate(kgsl_driver.ebi1_clk, 0); -} - -static void kgsl_hw_disable(void) -{ - kgsl_driver.active = false; - disable_irq(kgsl_driver.interrupt_num); - kgsl_clk_disable(); - pr_debug("kgsl: hw disabled\n"); - wake_unlock(&kgsl_driver.wake_lock); -} - -static void kgsl_hw_enable(void) -{ - wake_lock(&kgsl_driver.wake_lock); - kgsl_clk_enable(); - enable_irq(kgsl_driver.interrupt_num); - kgsl_driver.active = true; - pr_debug("kgsl: hw enabled\n"); -} - -static void kgsl_hw_get_locked(void) -{ - /* active_cnt is protected by driver mutex */ - if (kgsl_driver.active_cnt++ == 0) { - if (kgsl_driver.active) { - del_timer_sync(&kgsl_driver.standby_timer); - barrier(); - } - if (!kgsl_driver.active) - kgsl_hw_enable(); - } -} - -static void kgsl_hw_put_locked(bool start_timer) -{ - if ((--kgsl_driver.active_cnt == 0) && start_timer) { - mod_timer(&kgsl_driver.standby_timer, - jiffies + msecs_to_jiffies(20)); - } -} - -static void kgsl_do_standby_timer(unsigned long data) -{ - if (kgsl_yamato_is_idle(&kgsl_driver.yamato_device)) - kgsl_hw_disable(); - else - mod_timer(&kgsl_driver.standby_timer, - jiffies + msecs_to_jiffies(10)); -} - -/* file operations */ -static int kgsl_first_open_locked(void) -{ - int result = 0; - - BUG_ON(kgsl_driver.active); - BUG_ON(kgsl_driver.active_cnt); - - kgsl_clk_enable(); - - /* init devices */ - result = kgsl_yamato_init(&kgsl_driver.yamato_device, - &kgsl_driver.yamato_config); - if (result != 0) - goto done; - - result = kgsl_yamato_start(&kgsl_driver.yamato_device, 0); - if (result != 0) - goto done; - -done: - kgsl_clk_disable(); - return result; -} - -static int kgsl_last_release_locked(void) -{ - BUG_ON(kgsl_driver.active_cnt); - - disable_irq(kgsl_driver.interrupt_num); - - kgsl_yamato_stop(&kgsl_driver.yamato_device); - - /* close devices */ - kgsl_yamato_close(&kgsl_driver.yamato_device); - - kgsl_clk_disable(); - kgsl_driver.active = false; - wake_unlock(&kgsl_driver.wake_lock); - - return 0; -} - -static int kgsl_release(struct inode *inodep, struct file *filep) -{ - int result = 0; - unsigned int i; - struct kgsl_mem_entry *entry, *entry_tmp; - struct kgsl_file_private *private = NULL; - - mutex_lock(&kgsl_driver.mutex); - - private = filep->private_data; - BUG_ON(private == NULL); - filep->private_data = NULL; - list_del(&private->list); - - kgsl_hw_get_locked(); - - for (i = 0; i < KGSL_CONTEXT_MAX; i++) - if (private->ctxt_id_mask & (1 << i)) - kgsl_drawctxt_destroy(&kgsl_driver.yamato_device, i); - - list_for_each_entry_safe(entry, entry_tmp, &private->mem_list, list) - kgsl_remove_mem_entry(entry, false); - - entry = NULL; - entry_tmp = NULL; - list_for_each_entry_safe(entry, entry_tmp, - &private->preserve_entry_list, list) - kgsl_remove_mem_entry(entry, false); - - if (private->pagetable != NULL) { - kgsl_yamato_cleanup_pt(&kgsl_driver.yamato_device, - private->pagetable); - kgsl_mmu_destroypagetableobject(private->pagetable); - private->pagetable = NULL; - } - - kfree(private); - - if (atomic_dec_return(&kgsl_driver.open_count) == 0) { - KGSL_DRV_VDBG("last_release\n"); - kgsl_hw_put_locked(false); - result = kgsl_last_release_locked(); - } else - kgsl_hw_put_locked(true); - - mutex_unlock(&kgsl_driver.mutex); - - return result; -} - -static int kgsl_open(struct inode *inodep, struct file *filep) -{ - int result = 0; - struct kgsl_file_private *private = NULL; - - KGSL_DRV_DBG("file %p pid %d\n", filep, task_pid_nr(current)); - - - if (filep->f_flags & O_EXCL) { - KGSL_DRV_ERR("O_EXCL not allowed\n"); - return -EBUSY; - } - - private = kzalloc(sizeof(*private), GFP_KERNEL); - if (private == NULL) { - KGSL_DRV_ERR("cannot allocate file private data\n"); - return -ENOMEM; - } - - mutex_lock(&kgsl_driver.mutex); - - private->ctxt_id_mask = 0; - INIT_LIST_HEAD(&private->mem_list); - INIT_LIST_HEAD(&private->preserve_entry_list); - private->preserve_list_size = 0; - - filep->private_data = private; - - list_add(&private->list, &kgsl_driver.client_list); - - if (atomic_inc_return(&kgsl_driver.open_count) == 1) { - result = kgsl_first_open_locked(); - if (result != 0) - goto done; - } - - kgsl_hw_get_locked(); - - /*NOTE: this must happen after first_open */ - private->pagetable = - kgsl_mmu_createpagetableobject(&kgsl_driver.yamato_device.mmu); - if (private->pagetable == NULL) { - result = -ENOMEM; - goto done; - } - result = kgsl_yamato_setup_pt(&kgsl_driver.yamato_device, - private->pagetable); - if (result) { - kgsl_mmu_destroypagetableobject(private->pagetable); - private->pagetable = NULL; - goto done; - } - private->vmalloc_size = 0; -done: - kgsl_hw_put_locked(true); - mutex_unlock(&kgsl_driver.mutex); - if (result != 0) - kgsl_release(inodep, filep); - return result; -} - - -/*call with driver locked */ -static struct kgsl_mem_entry * -kgsl_sharedmem_find(struct kgsl_file_private *private, unsigned int gpuaddr) -{ - struct kgsl_mem_entry *entry = NULL, *result = NULL; - - BUG_ON(private == NULL); - - list_for_each_entry(entry, &private->mem_list, list) { - if (entry->memdesc.gpuaddr == gpuaddr) { - result = entry; - break; - } - } - return result; -} - -/*call with driver locked */ -struct kgsl_mem_entry * -kgsl_sharedmem_find_region(struct kgsl_file_private *private, - unsigned int gpuaddr, - size_t size) -{ - struct kgsl_mem_entry *entry = NULL, *result = NULL; - - BUG_ON(private == NULL); - - list_for_each_entry(entry, &private->mem_list, list) { - if (gpuaddr >= entry->memdesc.gpuaddr && - ((gpuaddr + size) <= - (entry->memdesc.gpuaddr + entry->memdesc.size))) { - result = entry; - break; - } - } - - return result; -} - -/*call all ioctl sub functions with driver locked*/ - -static long kgsl_ioctl_device_getproperty(struct kgsl_file_private *private, - void __user *arg) -{ - int result = 0; - struct kgsl_device_getproperty param; - - if (copy_from_user(¶m, arg, sizeof(param))) { - result = -EFAULT; - goto done; - } - result = kgsl_yamato_getproperty(&kgsl_driver.yamato_device, - param.type, - param.value, param.sizebytes); -done: - return result; -} - -static long kgsl_ioctl_device_regread(struct kgsl_file_private *private, - void __user *arg) -{ - int result = 0; - struct kgsl_device_regread param; - - if (copy_from_user(¶m, arg, sizeof(param))) { - result = -EFAULT; - goto done; - } - result = kgsl_yamato_regread(&kgsl_driver.yamato_device, - param.offsetwords, ¶m.value); - if (result != 0) - goto done; - - if (copy_to_user(arg, ¶m, sizeof(param))) { - result = -EFAULT; - goto done; - } -done: - return result; -} - - -static long kgsl_ioctl_device_waittimestamp(struct kgsl_file_private *private, - void __user *arg) -{ - int result = 0; - struct kgsl_device_waittimestamp param; - - if (copy_from_user(¶m, arg, sizeof(param))) { - result = -EFAULT; - goto done; - } - - /* Don't wait forever, set a max value for now */ - if (param.timeout == -1) - param.timeout = 10 * MSEC_PER_SEC; - result = kgsl_yamato_waittimestamp(&kgsl_driver.yamato_device, - param.timestamp, - param.timeout); - - kgsl_yamato_runpending(&kgsl_driver.yamato_device); -done: - return result; -} - -static long kgsl_ioctl_rb_issueibcmds(struct kgsl_file_private *private, - void __user *arg) -{ - int result = 0; - struct kgsl_ringbuffer_issueibcmds param; - - if (copy_from_user(¶m, arg, sizeof(param))) { - result = -EFAULT; - goto done; - } - - if (param.drawctxt_id >= KGSL_CONTEXT_MAX - || (private->ctxt_id_mask & 1 << param.drawctxt_id) == 0) { - result = -EINVAL; - KGSL_DRV_ERR("invalid drawctxt drawctxt_id %d\n", - param.drawctxt_id); - result = -EINVAL; - goto done; - } - - if (kgsl_sharedmem_find_region(private, param.ibaddr, - param.sizedwords*sizeof(uint32_t)) == NULL) { - KGSL_DRV_ERR("invalid cmd buffer ibaddr %08x sizedwords %d\n", - param.ibaddr, param.sizedwords); - result = -EINVAL; - goto done; - - } - - result = kgsl_ringbuffer_issueibcmds(&kgsl_driver.yamato_device, - param.drawctxt_id, - param.ibaddr, - param.sizedwords, - ¶m.timestamp, - param.flags); - if (result != 0) - goto done; - - if (copy_to_user(arg, ¶m, sizeof(param))) { - result = -EFAULT; - goto done; - } -done: - return result; -} - -static long kgsl_ioctl_cmdstream_readtimestamp(struct kgsl_file_private - *private, void __user *arg) -{ - int result = 0; - struct kgsl_cmdstream_readtimestamp param; - - if (copy_from_user(¶m, arg, sizeof(param))) { - result = -EFAULT; - goto done; - } - - param.timestamp = - kgsl_cmdstream_readtimestamp(&kgsl_driver.yamato_device, - param.type); - if (result != 0) - goto done; - - if (copy_to_user(arg, ¶m, sizeof(param))) { - result = -EFAULT; - goto done; - } -done: - return result; -} - -static long kgsl_ioctl_cmdstream_freememontimestamp(struct kgsl_file_private - *private, void __user *arg) -{ - int result = 0; - struct kgsl_cmdstream_freememontimestamp param; - struct kgsl_mem_entry *entry = NULL; - - if (copy_from_user(¶m, arg, sizeof(param))) { - result = -EFAULT; - goto done; - } - - entry = kgsl_sharedmem_find(private, param.gpuaddr); - if (entry == NULL) { - KGSL_DRV_ERR("invalid gpuaddr %08x\n", param.gpuaddr); - result = -EINVAL; - goto done; - } - - if (entry->memdesc.priv & KGSL_MEMFLAGS_VMALLOC_MEM) - entry->memdesc.priv &= ~KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH; - - result = kgsl_cmdstream_freememontimestamp(&kgsl_driver.yamato_device, - entry, - param.timestamp, - param.type); - - kgsl_yamato_runpending(&kgsl_driver.yamato_device); - -done: - return result; -} - -static long kgsl_ioctl_drawctxt_create(struct kgsl_file_private *private, - void __user *arg) -{ - int result = 0; - struct kgsl_drawctxt_create param; - - if (copy_from_user(¶m, arg, sizeof(param))) { - result = -EFAULT; - goto done; - } - - result = kgsl_drawctxt_create(&kgsl_driver.yamato_device, - private->pagetable, - param.flags, - ¶m.drawctxt_id); - if (result != 0) - goto done; - - if (copy_to_user(arg, ¶m, sizeof(param))) { - result = -EFAULT; - goto done; - } - - private->ctxt_id_mask |= 1 << param.drawctxt_id; - -done: - return result; -} - -static long kgsl_ioctl_drawctxt_destroy(struct kgsl_file_private *private, - void __user *arg) -{ - int result = 0; - struct kgsl_drawctxt_destroy param; - - if (copy_from_user(¶m, arg, sizeof(param))) { - result = -EFAULT; - goto done; - } - - if (param.drawctxt_id >= KGSL_CONTEXT_MAX - || (private->ctxt_id_mask & 1 << param.drawctxt_id) == 0) { - result = -EINVAL; - goto done; - } - - result = kgsl_drawctxt_destroy(&kgsl_driver.yamato_device, - param.drawctxt_id); - if (result == 0) - private->ctxt_id_mask &= ~(1 << param.drawctxt_id); - -done: - return result; -} - -void kgsl_remove_mem_entry(struct kgsl_mem_entry *entry, bool preserve) -{ - /* If allocation is vmalloc and preserve is requested then save - * the allocation in a free list to be used later instead of - * freeing it here */ - if (KGSL_MEMFLAGS_VMALLOC_MEM & entry->memdesc.priv && - preserve && - entry->priv->preserve_list_size < KGSL_MAX_PRESERVED_BUFFERS && - entry->memdesc.size <= KGSL_MAX_SIZE_OF_PRESERVED_BUFFER) { - if (entry->free_list.prev) { - list_del(&entry->free_list); - entry->free_list.prev = NULL; - } - if (entry->list.prev) { - list_del(&entry->list); - entry->list.prev = NULL; - } - list_add(&entry->list, &entry->priv->preserve_entry_list); - entry->priv->preserve_list_size++; - return; - } - - kgsl_mmu_unmap(entry->memdesc.pagetable, - entry->memdesc.gpuaddr & KGSL_PAGEMASK, - entry->memdesc.size); - if (KGSL_MEMFLAGS_VMALLOC_MEM & entry->memdesc.priv) { - vfree((void *)entry->memdesc.physaddr); - entry->priv->vmalloc_size -= entry->memdesc.size; - } else - kgsl_put_phys_file(entry->pmem_file); - list_del(&entry->list); - - if (entry->free_list.prev) - list_del(&entry->free_list); - - kfree(entry); - -} - -static long kgsl_ioctl_sharedmem_free(struct kgsl_file_private *private, - void __user *arg) -{ - int result = 0; - struct kgsl_sharedmem_free param; - struct kgsl_mem_entry *entry = NULL; - - if (copy_from_user(¶m, arg, sizeof(param))) { - result = -EFAULT; - goto done; - } - - entry = kgsl_sharedmem_find(private, param.gpuaddr); - if (entry == NULL) { - KGSL_DRV_ERR("invalid gpuaddr %08x\n", param.gpuaddr); - result = -EINVAL; - goto done; - } - - kgsl_remove_mem_entry(entry, false); -done: - return result; -} - -#ifdef CONFIG_MSM_KGSL_MMU -static int kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_file_private *private, - void __user *arg) -{ - int result = 0, len, found = 0; - struct kgsl_sharedmem_from_vmalloc param; - struct kgsl_mem_entry *entry = NULL, *entry_tmp = NULL; - void *vmalloc_area; - struct vm_area_struct *vma; - - if (copy_from_user(¶m, arg, sizeof(param))) { - result = -EFAULT; - goto error; - } - - if (!param.hostptr) { - KGSL_DRV_ERR - ("Invalid host pointer of malloc passed: param.hostptr " - "%08x\n", param.hostptr); - result = -EINVAL; - goto error; - } - - vma = find_vma(current->mm, param.hostptr); - if (!vma) { - KGSL_MEM_ERR("Could not find vma for address %x\n", - param.hostptr); - result = -EINVAL; - goto error; - } - len = vma->vm_end - vma->vm_start; - if (vma->vm_pgoff || !IS_ALIGNED(len, PAGE_SIZE) - || !IS_ALIGNED(vma->vm_start, PAGE_SIZE)) { - KGSL_MEM_ERR - ("kgsl vmalloc mapping must be at offset 0 and page aligned\n"); - result = -EINVAL; - goto error; - } - if (vma->vm_start != param.hostptr) { - KGSL_MEM_ERR - ("vma start address is not equal to mmap address\n"); - result = -EINVAL; - goto error; - } - - if ((private->vmalloc_size + len) > KGSL_GRAPHICS_MEMORY_LOW_WATERMARK - && !param.force_no_low_watermark) { - result = -ENOMEM; - goto error; - } - - list_for_each_entry_safe(entry, entry_tmp, - &private->preserve_entry_list, list) { - if (entry->memdesc.size == len) { - list_del(&entry->list); - found = 1; - break; - } - } - if (!found) { - entry = kzalloc(sizeof(struct kgsl_mem_entry), GFP_KERNEL); - if (entry == NULL) { - result = -ENOMEM; - goto error; - } - - /* allocate memory and map it to user space */ - vmalloc_area = vmalloc_user(len); - if (!vmalloc_area) { - KGSL_MEM_ERR("vmalloc failed\n"); - result = -ENOMEM; - goto error_free_entry; - } - dmac_flush_range(vmalloc_area, vmalloc_area + len); - - result = - kgsl_mmu_map(private->pagetable, - (unsigned long)vmalloc_area, len, - GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, - &entry->memdesc.gpuaddr, KGSL_MEMFLAGS_ALIGN4K); - if (result != 0) - goto error_free_vmalloc; - - entry->memdesc.pagetable = private->pagetable; - entry->memdesc.size = len; - entry->memdesc.priv = KGSL_MEMFLAGS_VMALLOC_MEM | - KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH; - entry->memdesc.physaddr = (unsigned long)vmalloc_area; - entry->priv = private; - private->vmalloc_size += len; - - } else { - KGSL_MEM_INFO("Reusing memory entry: %x, size: %x\n", - (unsigned int)entry, entry->memdesc.size); - entry->priv->preserve_list_size--; - vmalloc_area = (void *)entry->memdesc.physaddr; - } - - if (!kgsl_cache_enable) { - /* If we are going to map non-cached, make sure to flush the - * cache to ensure that previously cached data does not - * overwrite this memory */ -// dmac_flush_range(vmalloc_area, vmalloc_area + len); - KGSL_MEM_INFO("Caching for memory allocation turned off\n"); - vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); - } else { - KGSL_MEM_INFO("Caching for memory allocation turned on\n"); - } - - result = remap_vmalloc_range(vma, vmalloc_area, 0); - if (result) { - KGSL_MEM_ERR("remap_vmalloc_range returned %d\n", result); - goto error_unmap_entry; - } - - entry->memdesc.hostptr = (void *)param.hostptr; - - param.gpuaddr = entry->memdesc.gpuaddr; - - if (copy_to_user(arg, ¶m, sizeof(param))) { - result = -EFAULT; - goto error_unmap_entry; - } - list_add(&entry->list, &private->mem_list); - - return 0; - -error_unmap_entry: - kgsl_mmu_unmap(private->pagetable, entry->memdesc.gpuaddr, - entry->memdesc.size); - -error_free_vmalloc: - vfree(vmalloc_area); - -error_free_entry: - kfree(entry); - -error: - return result; -} -#else -static inline int kgsl_ioctl_sharedmem_from_vmalloc( - struct kgsl_file_private *private, void __user *arg) -{ - return -ENOSYS; -} -#endif - -static int kgsl_get_phys_file(int fd, unsigned long *start, unsigned long *len, - struct file **filep) -{ - struct file *fbfile; - int put_needed; - unsigned long vstart = 0; - int ret = 0; - dev_t rdev; - struct fb_info *info; - - *filep = NULL; - if (!get_pmem_file(fd, start, &vstart, len, filep)) - return 0; - - fbfile = fget_light(fd, &put_needed); - if (fbfile == NULL) - return -1; - - rdev = fbfile->f_dentry->d_inode->i_rdev; - info = MAJOR(rdev) == FB_MAJOR ? registered_fb[MINOR(rdev)] : NULL; - if (info) { - *start = info->fix.smem_start; - *len = info->fix.smem_len; - ret = 0; - } else - ret = -1; - fput_light(fbfile, put_needed); - - return ret; -} - -static void kgsl_put_phys_file(struct file *file) -{ - KGSL_DRV_DBG("put phys file %p\n", file); - if (file) - put_pmem_file(file); -} - -static int kgsl_ioctl_sharedmem_from_pmem(struct kgsl_file_private *private, - void __user *arg) -{ - int result = 0; - struct kgsl_sharedmem_from_pmem param; - struct kgsl_mem_entry *entry = NULL; - unsigned long start = 0, len = 0; - struct file *pmem_file = NULL; - - if (copy_from_user(¶m, arg, sizeof(param))) { - result = -EFAULT; - goto error; - } - - if (kgsl_get_phys_file(param.pmem_fd, &start, &len, &pmem_file)) { - result = -EINVAL; - goto error; - } else if (param.offset + param.len > len) { - KGSL_DRV_ERR("%s: region too large 0x%x + 0x%x >= 0x%lx\n", - __func__, param.offset, param.len, len); - result = -EINVAL; - goto error_put_pmem; - } - - KGSL_MEM_INFO("get phys file %p start 0x%lx len 0x%lx\n", - pmem_file, start, len); - KGSL_DRV_DBG("locked phys file %p\n", pmem_file); - - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (entry == NULL) { - result = -ENOMEM; - goto error_put_pmem; - } - - entry->pmem_file = pmem_file; - - entry->memdesc.pagetable = private->pagetable; - - /* Any MMU mapped memory must have a length in multiple of PAGESIZE */ - entry->memdesc.size = ALIGN(param.len, PAGE_SIZE); - - /*we shouldn't need to write here from kernel mode */ - entry->memdesc.hostptr = NULL; - - /* ensure that MMU mappings are at page boundary */ - entry->memdesc.physaddr = start + (param.offset & KGSL_PAGEMASK); - result = kgsl_mmu_map(private->pagetable, entry->memdesc.physaddr, - entry->memdesc.size, GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, - &entry->memdesc.gpuaddr, - KGSL_MEMFLAGS_ALIGN4K | KGSL_MEMFLAGS_CONPHYS); - if (result) - goto error_free_entry; - - /* If the offset is not at 4K boundary then add the correct offset - * value to gpuaddr */ - entry->memdesc.gpuaddr += (param.offset & ~KGSL_PAGEMASK); - param.gpuaddr = entry->memdesc.gpuaddr; - - if (copy_to_user(arg, ¶m, sizeof(param))) { - result = -EFAULT; - goto error_unmap_entry; - } - list_add(&entry->list, &private->mem_list); - return result; - -error_unmap_entry: - kgsl_mmu_unmap(entry->memdesc.pagetable, - entry->memdesc.gpuaddr & KGSL_PAGEMASK, - entry->memdesc.size); -error_free_entry: - kfree(entry); - -error_put_pmem: - kgsl_put_phys_file(pmem_file); - -error: - return result; -} - -#ifdef CONFIG_MSM_KGSL_MMU -/*This function flushes a graphics memory allocation from CPU cache - *when caching is enabled with MMU*/ -static int kgsl_ioctl_sharedmem_flush_cache(struct kgsl_file_private *private, - void __user *arg) -{ - int result = 0; - struct kgsl_mem_entry *entry; - struct kgsl_sharedmem_free param; - - if (copy_from_user(¶m, arg, sizeof(param))) { - result = -EFAULT; - goto done; - } - - entry = kgsl_sharedmem_find(private, param.gpuaddr); - if (!entry) { - KGSL_DRV_ERR("invalid gpuaddr %08x\n", param.gpuaddr); - result = -EINVAL; - goto done; - } - result = flush_l1_cache_range((unsigned long)entry->memdesc.hostptr, - entry->memdesc.size); - /* Mark memory as being flushed so we don't flush it again */ - entry->memdesc.priv &= ~KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH; -done: - return result; -} -#else -static int kgsl_ioctl_sharedmem_flush_cache(struct kgsl_file_private *private, - void __user *arg) -{ - return -ENOSYS; -} -#endif - - -static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) -{ - int result = 0; - struct kgsl_file_private *private = filep->private_data; - struct kgsl_drawctxt_set_bin_base_offset binbase; - - BUG_ON(private == NULL); - - KGSL_DRV_VDBG("filep %p cmd 0x%08x arg 0x%08lx\n", filep, cmd, arg); - - mutex_lock(&kgsl_driver.mutex); - - kgsl_hw_get_locked(); - - switch (cmd) { - - case IOCTL_KGSL_DEVICE_GETPROPERTY: - result = - kgsl_ioctl_device_getproperty(private, (void __user *)arg); - break; - - case IOCTL_KGSL_DEVICE_REGREAD: - result = kgsl_ioctl_device_regread(private, (void __user *)arg); - break; - - case IOCTL_KGSL_DEVICE_WAITTIMESTAMP: - result = kgsl_ioctl_device_waittimestamp(private, - (void __user *)arg); - break; - - case IOCTL_KGSL_RINGBUFFER_ISSUEIBCMDS: - if (kgsl_cache_enable) - flush_l1_cache_all(private); - result = kgsl_ioctl_rb_issueibcmds(private, (void __user *)arg); - break; - - case IOCTL_KGSL_CMDSTREAM_READTIMESTAMP: - result = - kgsl_ioctl_cmdstream_readtimestamp(private, - (void __user *)arg); - break; - - case IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP: - result = - kgsl_ioctl_cmdstream_freememontimestamp(private, - (void __user *)arg); - break; - - case IOCTL_KGSL_DRAWCTXT_CREATE: - result = kgsl_ioctl_drawctxt_create(private, - (void __user *)arg); - break; - - case IOCTL_KGSL_DRAWCTXT_DESTROY: - result = - kgsl_ioctl_drawctxt_destroy(private, (void __user *)arg); - break; - - case IOCTL_KGSL_SHAREDMEM_FREE: - result = kgsl_ioctl_sharedmem_free(private, (void __user *)arg); - break; - - case IOCTL_KGSL_SHAREDMEM_FROM_VMALLOC: - kgsl_yamato_runpending(&kgsl_driver.yamato_device); - result = kgsl_ioctl_sharedmem_from_vmalloc(private, - (void __user *)arg); - break; - - case IOCTL_KGSL_SHAREDMEM_FLUSH_CACHE: - if (kgsl_cache_enable) - result = kgsl_ioctl_sharedmem_flush_cache(private, - (void __user *)arg); - break; - case IOCTL_KGSL_SHAREDMEM_FROM_PMEM: - kgsl_yamato_runpending(&kgsl_driver.yamato_device); - result = kgsl_ioctl_sharedmem_from_pmem(private, - (void __user *)arg); - break; - - case IOCTL_KGSL_DRAWCTXT_SET_BIN_BASE_OFFSET: - if (copy_from_user(&binbase, (void __user *)arg, - sizeof(binbase))) { - result = -EFAULT; - break; - } - - if (private->ctxt_id_mask & (1 << binbase.drawctxt_id)) { - result = kgsl_drawctxt_set_bin_base_offset( - &kgsl_driver.yamato_device, - binbase.drawctxt_id, - binbase.offset); - } else { - result = -EINVAL; - KGSL_DRV_ERR("invalid drawctxt drawctxt_id %d\n", - binbase.drawctxt_id); - } - break; - - default: - KGSL_DRV_ERR("invalid ioctl code %08x\n", cmd); - result = -EINVAL; - break; - } - - kgsl_hw_put_locked(true); - mutex_unlock(&kgsl_driver.mutex); - KGSL_DRV_VDBG("result %d\n", result); - return result; -} - -static struct file_operations kgsl_fops = { - .owner = THIS_MODULE, - .release = kgsl_release, - .open = kgsl_open, - .unlocked_ioctl = kgsl_ioctl, -}; - - -struct kgsl_driver kgsl_driver = { - .misc = { - .name = DRIVER_NAME, - .minor = MISC_DYNAMIC_MINOR, - .fops = &kgsl_fops, - }, - .open_count = ATOMIC_INIT(0), - .mutex = __MUTEX_INITIALIZER(kgsl_driver.mutex), -}; - -static void kgsl_driver_cleanup(void) -{ - - wake_lock_destroy(&kgsl_driver.wake_lock); - - if (kgsl_driver.interrupt_num > 0) { - if (kgsl_driver.have_irq) { - free_irq(kgsl_driver.interrupt_num, NULL); - kgsl_driver.have_irq = 0; - } - kgsl_driver.interrupt_num = 0; - } - - /* shutdown memory apertures */ - kgsl_sharedmem_close(&kgsl_driver.shmem); - - if (kgsl_driver.grp_clk) { - clk_put(kgsl_driver.grp_clk); - kgsl_driver.grp_clk = NULL; - } - - if (kgsl_driver.imem_clk != NULL) { - clk_put(kgsl_driver.imem_clk); - kgsl_driver.imem_clk = NULL; - } - - if (kgsl_driver.ebi1_clk != NULL) { - clk_put(kgsl_driver.ebi1_clk); - kgsl_driver.ebi1_clk = NULL; - } - - kgsl_driver.pdev = NULL; - -} - - -static int __devinit kgsl_platform_probe(struct platform_device *pdev) -{ - int result = 0; - struct clk *clk; - struct resource *res = NULL; - - kgsl_debug_init(); - - INIT_LIST_HEAD(&kgsl_driver.client_list); - - /*acquire clocks */ - BUG_ON(kgsl_driver.grp_clk != NULL); - BUG_ON(kgsl_driver.imem_clk != NULL); - BUG_ON(kgsl_driver.ebi1_clk != NULL); - - kgsl_driver.pdev = pdev; - - setup_timer(&kgsl_driver.standby_timer, kgsl_do_standby_timer, 0); - wake_lock_init(&kgsl_driver.wake_lock, WAKE_LOCK_SUSPEND, "kgsl"); - - clk = clk_get(&pdev->dev, "grp_clk"); - if (IS_ERR(clk)) { - result = PTR_ERR(clk); - KGSL_DRV_ERR("clk_get(grp_clk) returned %d\n", result); - goto done; - } - kgsl_driver.grp_clk = clk; - - clk = clk_get(&pdev->dev, "grp_pclk"); - if (IS_ERR(clk)) { - KGSL_DRV_ERR("no grp_pclk, continuing\n"); - clk = NULL; - } - kgsl_driver.grp_pclk = clk; - - clk = clk_get(&pdev->dev, "imem_clk"); - if (IS_ERR(clk)) { - result = PTR_ERR(clk); - KGSL_DRV_ERR("clk_get(imem_clk) returned %d\n", result); - goto done; - } - kgsl_driver.imem_clk = clk; - - clk = clk_get(&pdev->dev, "ebi1_clk"); - if (IS_ERR(clk)) { - result = PTR_ERR(clk); - KGSL_DRV_ERR("clk_get(ebi1_clk) returned %d\n", result); - goto done; - } - kgsl_driver.ebi1_clk = clk; - - /*acquire interrupt */ - kgsl_driver.interrupt_num = platform_get_irq(pdev, 0); - if (kgsl_driver.interrupt_num <= 0) { - KGSL_DRV_ERR("platform_get_irq() returned %d\n", - kgsl_driver.interrupt_num); - result = -EINVAL; - goto done; - } - - result = request_irq(kgsl_driver.interrupt_num, kgsl_yamato_isr, - IRQF_TRIGGER_HIGH, DRIVER_NAME, NULL); - if (result) { - KGSL_DRV_ERR("request_irq(%d) returned %d\n", - kgsl_driver.interrupt_num, result); - goto done; - } - kgsl_driver.have_irq = 1; - disable_irq(kgsl_driver.interrupt_num); - - result = kgsl_yamato_config(&kgsl_driver.yamato_config, pdev); - if (result != 0) - goto done; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "kgsl_phys_memory"); - if (res == NULL) { - result = -EINVAL; - goto done; - } - - kgsl_driver.shmem.physbase = res->start; - kgsl_driver.shmem.size = resource_size(res); - - /* init memory apertures */ - result = kgsl_sharedmem_init(&kgsl_driver.shmem); - -done: - if (result) - kgsl_driver_cleanup(); - else - result = misc_register(&kgsl_driver.misc); - - return result; -} - -static int kgsl_platform_remove(struct platform_device *pdev) -{ - - kgsl_driver_cleanup(); - misc_deregister(&kgsl_driver.misc); - - return 0; -} - -static int kgsl_platform_suspend(struct platform_device *pdev, - pm_message_t state) -{ - mutex_lock(&kgsl_driver.mutex); - if (atomic_read(&kgsl_driver.open_count) > 0) { - if (kgsl_driver.active) - pr_err("%s: Suspending while active???\n", __func__); - } - mutex_unlock(&kgsl_driver.mutex); - return 0; -} - -static struct platform_driver kgsl_platform_driver = { - .probe = kgsl_platform_probe, - .remove = __devexit_p(kgsl_platform_remove), - .suspend = kgsl_platform_suspend, - .driver = { - .owner = THIS_MODULE, - .name = DRIVER_NAME - } -}; - -static int __init kgsl_mod_init(void) -{ - return platform_driver_register(&kgsl_platform_driver); -} - -static void __exit kgsl_mod_exit(void) -{ - platform_driver_unregister(&kgsl_platform_driver); -} - -module_init(kgsl_mod_init); -module_exit(kgsl_mod_exit); - -MODULE_AUTHOR("QUALCOMM"); -MODULE_DESCRIPTION("3D graphics driver for QSD8x50 and MSM7x27"); -MODULE_VERSION("1.0"); -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_ALIAS("platform:kgsl"); diff --git a/drivers/video/msm/gpu/kgsl/kgsl.h b/drivers/video/msm/gpu/kgsl/kgsl.h deleted file mode 100644 index dece225fd9d28..0000000000000 --- a/drivers/video/msm/gpu/kgsl/kgsl.h +++ /dev/null @@ -1,84 +0,0 @@ -/* -* Copyright (c) 2008-2009 QUALCOMM USA, INC. -* -* All source code in this file is licensed under the following license -* -* 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, you can find it at http://www.fsf.org -*/ -#ifndef _GSL_DRIVER_H -#define _GSL_DRIVER_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "kgsl_device.h" -#include "kgsl_sharedmem.h" - -#define DRIVER_NAME "kgsl" - -struct kgsl_driver { - struct miscdevice misc; - struct platform_device *pdev; - atomic_t open_count; - struct mutex mutex; - - int interrupt_num; - int have_irq; - - struct clk *grp_clk; - struct clk *grp_pclk; - struct clk *imem_clk; - struct clk *ebi1_clk; - - struct kgsl_devconfig yamato_config; - - uint32_t flags_debug; - - struct kgsl_sharedmem shmem; - struct kgsl_device yamato_device; - - struct list_head client_list; - - bool active; - int active_cnt; - struct timer_list standby_timer; - - struct wake_lock wake_lock; -}; - -extern struct kgsl_driver kgsl_driver; - -struct kgsl_mem_entry { - struct kgsl_memdesc memdesc; - struct file *pmem_file; - struct list_head list; - struct list_head free_list; - uint32_t free_timestamp; - - /* back pointer to private structure under whose context this - * allocation is made */ - struct kgsl_file_private *priv; -}; - -void kgsl_remove_mem_entry(struct kgsl_mem_entry *entry, bool preserve); - -#endif /* _GSL_DRIVER_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c deleted file mode 100644 index 37760be19fe29..0000000000000 --- a/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.c +++ /dev/null @@ -1,105 +0,0 @@ -/* -* Copyright (c) 2008-2009 QUALCOMM USA, INC. -* -* All source code in this file is licensed under the following license -* -* 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, you can find it at http://www.fsf.org -*/ - -#include "kgsl.h" -#include "kgsl_device.h" -#include "kgsl_cmdstream.h" -#include "kgsl_sharedmem.h" - -int kgsl_cmdstream_init(struct kgsl_device *device) -{ - return 0; -} - -int kgsl_cmdstream_close(struct kgsl_device *device) -{ - return 0; -} - -uint32_t -kgsl_cmdstream_readtimestamp(struct kgsl_device *device, - enum kgsl_timestamp_type type) -{ - uint32_t timestamp = 0; - - KGSL_CMD_VDBG("enter (device_id=%d, type=%d)\n", device->id, type); - - if (type == KGSL_TIMESTAMP_CONSUMED) - KGSL_CMDSTREAM_GET_SOP_TIMESTAMP(device, - (unsigned int *)×tamp); - else if (type == KGSL_TIMESTAMP_RETIRED) - KGSL_CMDSTREAM_GET_EOP_TIMESTAMP(device, - (unsigned int *)×tamp); - - rmb(); - - KGSL_CMD_VDBG("return %d\n", timestamp); - - return timestamp; -} - -int kgsl_cmdstream_check_timestamp(struct kgsl_device *device, - unsigned int timestamp) -{ - unsigned int ts_processed; - - ts_processed = kgsl_cmdstream_readtimestamp(device, - KGSL_TIMESTAMP_RETIRED); - return timestamp_cmp(ts_processed, timestamp); -} - -void kgsl_cmdstream_memqueue_drain(struct kgsl_device *device) -{ - struct kgsl_mem_entry *entry, *entry_tmp; - uint32_t ts_processed; - struct kgsl_ringbuffer *rb = &device->ringbuffer; - - /* get current EOP timestamp */ - ts_processed = - kgsl_cmdstream_readtimestamp(device, KGSL_TIMESTAMP_RETIRED); - - list_for_each_entry_safe(entry, entry_tmp, &rb->memqueue, free_list) { - /*NOTE: this assumes that the free list is sorted by - * timestamp, but I'm not yet sure that it is a valid - * assumption - */ - if (!timestamp_cmp(ts_processed, entry->free_timestamp)) - break; - KGSL_MEM_DBG("ts_processed %d ts_free %d gpuaddr %x)\n", - ts_processed, entry->free_timestamp, - entry->memdesc.gpuaddr); - kgsl_remove_mem_entry(entry, true); - } -} - -int -kgsl_cmdstream_freememontimestamp(struct kgsl_device *device, - struct kgsl_mem_entry *entry, - uint32_t timestamp, - enum kgsl_timestamp_type type) -{ - struct kgsl_ringbuffer *rb = &device->ringbuffer; - KGSL_MEM_DBG("enter (dev %p gpuaddr %x ts %d)\n", - device, entry->memdesc.gpuaddr, timestamp); - (void)type; /* unref. For now just use EOP timestamp */ - - list_add_tail(&entry->free_list, &rb->memqueue); - entry->free_timestamp = timestamp; - - return 0; -} diff --git a/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h b/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h deleted file mode 100644 index f81ef54673911..0000000000000 --- a/drivers/video/msm/gpu/kgsl/kgsl_cmdstream.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef __KGSL_CMDSTREAM_H -#define __KGSL_CMDSTREAM_H - -#include -#include "kgsl_device.h" -#include "kgsl_log.h" - -#ifdef KGSL_DEVICE_SHADOW_MEMSTORE_TO_USER -#define KGSL_CMDSTREAM_USE_MEM_TIMESTAMP -#endif /* KGSL_DEVICE_SHADOW_MEMSTORE_TO_USER */ - -#ifdef KGSL_CMDSTREAM_USE_MEM_TIMESTAMP -#define KGSL_CMDSTREAM_GET_SOP_TIMESTAMP(device, data) \ - kgsl_sharedmem_read(&device->memstore, (data), \ - KGSL_DEVICE_MEMSTORE_OFFSET(soptimestamp), 4) -#else -#define KGSL_CMDSTREAM_GET_SOP_TIMESTAMP(device, data) \ - kgsl_yamato_regread(device, REG_CP_TIMESTAMP, (data)) -#endif /* KGSL_CMDSTREAM_USE_MEM_TIMESTAMP */ - -#define KGSL_CMDSTREAM_GET_EOP_TIMESTAMP(device, data) \ - kgsl_sharedmem_read(&device->memstore, (data), \ - KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp), 4) - -/* Flags to control command packet settings */ -#define KGSL_CMD_FLAGS_PMODE 0x00000001 -#define KGSL_CMD_FLAGS_NO_TS_CMP 0x00000002 - -int kgsl_cmdstream_init(struct kgsl_device *device); - -int kgsl_cmdstream_close(struct kgsl_device *device); - -void kgsl_cmdstream_memqueue_drain(struct kgsl_device *device); - -uint32_t -kgsl_cmdstream_readtimestamp(struct kgsl_device *device, - enum kgsl_timestamp_type type); - -int kgsl_cmdstream_check_timestamp(struct kgsl_device *device, - unsigned int timestamp); - -int -kgsl_cmdstream_freememontimestamp(struct kgsl_device *device, - struct kgsl_mem_entry *entry, - uint32_t timestamp, - enum kgsl_timestamp_type type); - -static inline bool timestamp_cmp(unsigned int new, unsigned int old) -{ - int ts_diff = new - old; - return (ts_diff >= 0) || (ts_diff < -20000); -} - -#endif /* __KGSL_CMDSTREAM_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_device.h b/drivers/video/msm/gpu/kgsl/kgsl_device.h deleted file mode 100644 index dcbf4db78574d..0000000000000 --- a/drivers/video/msm/gpu/kgsl/kgsl_device.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 - * Copyright (c) 2008-2009 QUALCOMM USA, INC. - * - * All source code in this file is licensed under the following license - * - * 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, you can find it at http://www.fsf.org - */ -#ifndef _KGSL_DEVICE_H -#define _KGSL_DEVICE_H - -#include - -#include -#include -#include -#include - -#include "kgsl_drawctxt.h" -#include "kgsl_mmu.h" -#include "kgsl_ringbuffer.h" - -#define KGSL_CONTEXT_MAX 8 - -#define KGSL_TIMEOUT_NONE 0 -#define KGSL_TIMEOUT_DEFAULT 0xFFFFFFFF - -#define KGSL_DEV_FLAGS_INITIALIZED0 0x00000001 -#define KGSL_DEV_FLAGS_INITIALIZED 0x00000002 -#define KGSL_DEV_FLAGS_STARTED 0x00000004 -#define KGSL_DEV_FLAGS_ACTIVE 0x00000008 - -#define KGSL_CHIPID_YAMATODX_REV21 0x20100 -#define KGSL_CHIPID_YAMATODX_REV211 0x20101 - -/* Private memory flags for use with memdesc->priv feild */ -#define KGSL_MEMFLAGS_MEM_REQUIRES_FLUSH 0x00000001 -#define KGSL_MEMFLAGS_VMALLOC_MEM 0x00000002 - -#define KGSL_GRAPHICS_MEMORY_LOW_WATERMARK 0x1000000 -#define KGSL_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK))) - -struct kgsl_device; -struct platform_device; - - -struct kgsl_memregion { - unsigned char *mmio_virt_base; - unsigned int mmio_phys_base; - uint32_t gpu_base; - unsigned int sizebytes; -}; - -struct kgsl_device { - - unsigned int refcnt; - uint32_t flags; - enum kgsl_deviceid id; - unsigned int chip_id; - struct kgsl_memregion regspace; - struct kgsl_memdesc memstore; - - struct kgsl_mmu mmu; - struct kgsl_memregion gmemspace; - struct kgsl_ringbuffer ringbuffer; - unsigned int drawctxt_count; - struct kgsl_drawctxt *drawctxt_active; - struct kgsl_drawctxt drawctxt[KGSL_CONTEXT_MAX]; - - wait_queue_head_t ib1_wq; -}; - -struct kgsl_devconfig { - struct kgsl_memregion regspace; - - unsigned int mmu_config; - uint32_t mpu_base; - int mpu_range; - uint32_t va_base; - unsigned int va_range; - - struct kgsl_memregion gmemspace; -}; - -int kgsl_yamato_start(struct kgsl_device *device, uint32_t flags); - -int kgsl_yamato_stop(struct kgsl_device *device); - -bool kgsl_yamato_is_idle(struct kgsl_device *device); - -int kgsl_yamato_idle(struct kgsl_device *device, unsigned int timeout); - -int kgsl_yamato_getproperty(struct kgsl_device *device, - enum kgsl_property_type type, void *value, - unsigned int sizebytes); - -int kgsl_yamato_regread(struct kgsl_device *device, unsigned int offsetwords, - unsigned int *value); - -int kgsl_yamato_regwrite(struct kgsl_device *device, unsigned int offsetwords, - unsigned int value); - -int kgsl_yamato_waittimestamp(struct kgsl_device *device, - unsigned int timestamp, unsigned int timeout); - - -int kgsl_yamato_init(struct kgsl_device *, struct kgsl_devconfig *); - -int kgsl_yamato_close(struct kgsl_device *device); - -int kgsl_yamato_runpending(struct kgsl_device *device); - -int __init kgsl_yamato_config(struct kgsl_devconfig *, - struct platform_device *pdev); - -void kgsl_register_dump(struct kgsl_device *device); - -int kgsl_yamato_setup_pt(struct kgsl_device *device, - struct kgsl_pagetable *pagetable); -int kgsl_yamato_cleanup_pt(struct kgsl_device *device, - struct kgsl_pagetable *pagetable); -#ifdef CONFIG_MSM_KGSL_MMU -int kgsl_yamato_setstate(struct kgsl_device *device, uint32_t flags); -#else -static inline int kgsl_yamato_setstate(struct kgsl_device *device, uint32_t flags) -{ return 0; } -#endif - -irqreturn_t kgsl_yamato_isr(int irq, void *data); - -#endif /* _KGSL_DEVICE_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c deleted file mode 100644 index a057c8c94079b..0000000000000 --- a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.c +++ /dev/null @@ -1,1824 +0,0 @@ -/* - * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 - * Copyright (c) 2008-2009 QUALCOMM USA, INC. - * - * All source code in this file is licensed under the following license - * - * 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, you can find it at http://www.fsf.org - */ -#include -#include -#include - -#include "kgsl_drawctxt.h" - -#include "yamato_reg.h" -#include "kgsl.h" -#include "kgsl_log.h" -#include "kgsl_pm4types.h" -#include "kgsl_cmdstream.h" - -/* -* -* Memory Map for Register, Constant & Instruction Shadow, and Command Buffers -* (34.5KB) -* -* +---------------------+------------+-------------+---+---------------------+ -* | ALU Constant Shadow | Reg Shadow | C&V Buffers |Tex| Shader Instr Shadow | -* +---------------------+------------+-------------+---+---------------------+ -* ________________________________/ \____________________ -* / | -* +--------------+-----------+------+-----------+------------------------+ -* | Restore Regs | Save Regs | Quad | Gmem Save | Gmem Restore | unused | -* +--------------+-----------+------+-----------+------------------------+ -* -* 8K - ALU Constant Shadow (8K aligned) -* 4K - H/W Register Shadow (8K aligned) -* 4K - Command and Vertex Buffers -* - Indirect command buffer : Const/Reg restore -* - includes Loop & Bool const shadows -* - Indirect command buffer : Const/Reg save -* - Quad vertices & texture coordinates -* - Indirect command buffer : Gmem save -* - Indirect command buffer : Gmem restore -* - Unused (padding to 8KB boundary) -* <1K - Texture Constant Shadow (768 bytes) (8K aligned) -* 18K - Shader Instruction Shadow -* - 6K vertex (32 byte aligned) -* - 6K pixel (32 byte aligned) -* - 6K shared (32 byte aligned) -* -* Note: Reading constants into a shadow, one at a time using REG_TO_MEM, takes -* 3 DWORDS per DWORD transfered, plus 1 DWORD for the shadow, for a total of -* 16 bytes per constant. If the texture constants were transfered this way, -* the Command & Vertex Buffers section would extend past the 16K boundary. -* By moving the texture constant shadow area to start at 16KB boundary, we -* only require approximately 40 bytes more memory, but are able to use the -* LOAD_CONSTANT_CONTEXT shadowing feature for the textures, speeding up -* context switching. -* -* [Using LOAD_CONSTANT_CONTEXT shadowing feature for the Loop and/or Bool -* constants would require an additional 8KB each, for alignment.] -* -*/ - -/* Constants */ - -#define ALU_CONSTANTS 2048 /* DWORDS */ -#define NUM_REGISTERS 1024 /* DWORDS */ -#ifdef DISABLE_SHADOW_WRITES -#define CMD_BUFFER_LEN 9216 /* DWORDS */ -#else -#define CMD_BUFFER_LEN 3072 /* DWORDS */ -#endif -#define TEX_CONSTANTS (32*6) /* DWORDS */ -#define BOOL_CONSTANTS 8 /* DWORDS */ -#define LOOP_CONSTANTS 56 /* DWORDS */ -#define SHADER_INSTRUCT_LOG2 9U /* 2^n == SHADER_INSTRUCTIONS */ - -#if defined(PM4_IM_STORE) -/* 96-bit instructions */ -#define SHADER_INSTRUCT (1<= 0x10000) { - exp += 16; - u >>= 16; - } - if (u >= 0x100) { - exp += 8; - u >>= 8; - } - if (u >= 0x10) { - exp += 4; - u >>= 4; - } - if (u >= 0x4) { - exp += 2; - u >>= 2; - } - if (u >= 0x2) { - exp += 1; - u >>= 1; - } - - /* Calculate fraction */ - if (23 >= exp) - frac = (uintval & (~(1 << exp))) << (23 - exp); - - /* Exp is biased by 127 and shifted 23 bits */ - exp = (exp + 127) << 23; - - return exp | frac; -} - -/* context save (gmem -> sys) */ - -/* pre-compiled vertex shader program -* -* attribute vec4 P; -* void main(void) -* { -* gl_Position = P; -* } -*/ -#define GMEM2SYS_VTX_PGM_LEN 0x12 - -static unsigned int gmem2sys_vtx_pgm[GMEM2SYS_VTX_PGM_LEN] = { - 0x00011003, 0x00001000, 0xc2000000, - 0x00001004, 0x00001000, 0xc4000000, - 0x00001005, 0x00002000, 0x00000000, - 0x1cb81000, 0x00398a88, 0x00000003, - 0x140f803e, 0x00000000, 0xe2010100, - 0x14000000, 0x00000000, 0xe2000000 -}; - -/* pre-compiled fragment shader program -* -* precision highp float; -* uniform vec4 clear_color; -* void main(void) -* { -* gl_FragColor = clear_color; -* } -*/ - -#define GMEM2SYS_FRAG_PGM_LEN 0x0c - -static unsigned int gmem2sys_frag_pgm[GMEM2SYS_FRAG_PGM_LEN] = { - 0x00000000, 0x1002c400, 0x10000000, - 0x00001003, 0x00002000, 0x00000000, - 0x140f8000, 0x00000000, 0x22000000, - 0x14000000, 0x00000000, 0xe2000000 -}; - -/* context restore (sys -> gmem) */ -/* pre-compiled vertex shader program -* -* attribute vec4 position; -* attribute vec4 texcoord; -* varying vec4 texcoord0; -* void main() -* { -* gl_Position = position; -* texcoord0 = texcoord; -* } -*/ - -#define SYS2GMEM_VTX_PGM_LEN 0x18 - -static unsigned int sys2gmem_vtx_pgm[SYS2GMEM_VTX_PGM_LEN] = { - 0x00052003, 0x00001000, 0xc2000000, 0x00001005, - 0x00001000, 0xc4000000, 0x00001006, 0x10071000, - 0x20000000, 0x18981000, 0x0039ba88, 0x00000003, - 0x12982000, 0x40257b08, 0x00000002, 0x140f803e, - 0x00000000, 0xe2010100, 0x140f8000, 0x00000000, - 0xe2020200, 0x14000000, 0x00000000, 0xe2000000 -}; - -/* pre-compiled fragment shader program -* -* precision mediump float; -* uniform sampler2D tex0; -* varying vec4 texcoord0; -* void main() -* { -* gl_FragColor = texture2D(tex0, texcoord0.xy); -* } -*/ - -#define SYS2GMEM_FRAG_PGM_LEN 0x0f - -static unsigned int sys2gmem_frag_pgm[SYS2GMEM_FRAG_PGM_LEN] = { - 0x00011002, 0x00001000, 0xc4000000, 0x00001003, - 0x10041000, 0x20000000, 0x10000001, 0x1ffff688, - 0x00000002, 0x140f8000, 0x00000000, 0xe2000000, - 0x14000000, 0x00000000, 0xe2000000 -}; - -/* shader texture constants (sysmem -> gmem) */ -#define SYS2GMEM_TEX_CONST_LEN 6 - -static unsigned int sys2gmem_tex_const[SYS2GMEM_TEX_CONST_LEN] = { - /* Texture, FormatXYZW=Unsigned, ClampXYZ=Wrap/Repeat, - * RFMode=ZeroClamp-1, Dim=1:2d - */ - 0x00000002, /* Pitch = TBD */ - - /* Format=6:8888_WZYX, EndianSwap=0:None, ReqSize=0:256bit, DimHi=0, - * NearestClamp=1:OGL Mode - */ - 0x00000800, /* Address[31:12] = TBD */ - - /* Width, Height, EndianSwap=0:None */ - 0, /* Width & Height = TBD */ - - /* NumFormat=0:RF, DstSelXYZW=XYZW, ExpAdj=0, MagFilt=MinFilt=0:Point, - * Mip=2:BaseMap - */ - 0 << 1 | 1 << 4 | 2 << 7 | 3 << 10 | 2 << 23, - - /* VolMag=VolMin=0:Point, MinMipLvl=0, MaxMipLvl=1, LodBiasH=V=0, - * Dim3d=0 - */ - 0, - - /* BorderColor=0:ABGRBlack, ForceBC=0:diable, TriJuice=0, Aniso=0, - * Dim=1:2d, MipPacking=0 - */ - 1 << 9 /* Mip Address[31:12] = TBD */ -}; - -/* quad for copying GMEM to context shadow */ -#define QUAD_LEN 12 - -static unsigned int gmem_copy_quad[QUAD_LEN] = { - 0x00000000, 0x00000000, 0x3f800000, - 0x00000000, 0x00000000, 0x3f800000, - 0x00000000, 0x00000000, 0x3f800000, - 0x00000000, 0x00000000, 0x3f800000 -}; - -#define TEXCOORD_LEN 8 - -static unsigned int gmem_copy_texcoord[TEXCOORD_LEN] = { - 0x00000000, 0x3f800000, - 0x3f800000, 0x3f800000, - 0x00000000, 0x00000000, - 0x3f800000, 0x00000000 -}; - -#define NUM_COLOR_FORMATS 13 - -static enum SURFACEFORMAT surface_format_table[NUM_COLOR_FORMATS] = { - FMT_4_4_4_4, /* COLORX_4_4_4_4 */ - FMT_1_5_5_5, /* COLORX_1_5_5_5 */ - FMT_5_6_5, /* COLORX_5_6_5 */ - FMT_8, /* COLORX_8 */ - FMT_8_8, /* COLORX_8_8 */ - FMT_8_8_8_8, /* COLORX_8_8_8_8 */ - FMT_8_8_8_8, /* COLORX_S8_8_8_8 */ - FMT_16_FLOAT, /* COLORX_16_FLOAT */ - FMT_16_16_FLOAT, /* COLORX_16_16_FLOAT */ - FMT_16_16_16_16_FLOAT, /* COLORX_16_16_16_16_FLOAT */ - FMT_32_FLOAT, /* COLORX_32_FLOAT */ - FMT_32_32_FLOAT, /* COLORX_32_32_FLOAT */ - FMT_32_32_32_32_FLOAT, /* COLORX_32_32_32_32_FLOAT */ -}; - -static unsigned int format2bytesperpixel[NUM_COLOR_FORMATS] = { - 2, /* COLORX_4_4_4_4 */ - 2, /* COLORX_1_5_5_5 */ - 2, /* COLORX_5_6_5 */ - 1, /* COLORX_8 */ - 2, /* COLORX_8_8 8*/ - 4, /* COLORX_8_8_8_8 */ - 4, /* COLORX_S8_8_8_8 */ - 2, /* COLORX_16_FLOAT */ - 4, /* COLORX_16_16_FLOAT */ - 8, /* COLORX_16_16_16_16_FLOAT */ - 4, /* COLORX_32_FLOAT */ - 8, /* COLORX_32_32_FLOAT */ - 16, /* COLORX_32_32_32_32_FLOAT */ -}; - -/* shader linkage info */ -#define SHADER_CONST_ADDR (11 * 6 + 3) - -/* gmem command buffer length */ -#define PM4_REG(reg) ((0x4 << 16) | (GSL_HAL_SUBBLOCK_OFFSET(reg))) - -/* functions */ -static void config_gmemsize(struct gmem_shadow_t *shadow, int gmem_size) -{ - int w = 64, h = 64; /* 16KB surface, minimum */ - - shadow->format = COLORX_8_8_8_8; - /* convert from bytes to 32-bit words */ - gmem_size = (gmem_size + 3) / 4; - - /* find the right surface size, close to a square. */ - while (w * h < gmem_size) - if (w < h) - w *= 2; - else - h *= 2; - - shadow->width = w; - shadow->pitch = w; - shadow->height = h; - shadow->gmem_pitch = shadow->pitch; - - shadow->size = shadow->pitch * shadow->height * 4; -} - -static unsigned int gpuaddr(unsigned int *cmd, struct kgsl_memdesc *memdesc) -{ - return memdesc->gpuaddr + ((char *)cmd - (char *)memdesc->hostptr); -} - -static void -create_ib1(struct kgsl_drawctxt *drawctxt, unsigned int *cmd, - unsigned int *start, unsigned int *end) -{ - cmd[0] = PM4_HDR_INDIRECT_BUFFER_PFD; - cmd[1] = gpuaddr(start, &drawctxt->gpustate); - cmd[2] = end - start; -} - -static unsigned int *program_shader(unsigned int *cmds, int vtxfrag, - unsigned int *shader_pgm, int dwords) -{ - /* load the patched vertex shader stream */ - *cmds++ = pm4_type3_packet(PM4_IM_LOAD_IMMEDIATE, 2 + dwords); - /* 0=vertex shader, 1=fragment shader */ - *cmds++ = vtxfrag; - /* instruction start & size (in 32-bit words) */ - *cmds++ = ((0 << 16) | dwords); - - memcpy(cmds, shader_pgm, dwords << 2); - cmds += dwords; - - return cmds; -} - -static unsigned int *reg_to_mem(unsigned int *cmds, uint32_t dst, - uint32_t src, int dwords) -{ - while (dwords-- > 0) { - *cmds++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmds++ = src++; - *cmds++ = dst; - dst += 4; - } - - return cmds; -} - -#ifdef DISABLE_SHADOW_WRITES - -static void build_reg_to_mem_range(unsigned int start, unsigned int end, - unsigned int **cmd, - struct kgsl_drawctxt *drawctxt) -{ - unsigned int i = start; - - for (i = start; i <= end; i++) { - *(*cmd)++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *(*cmd)++ = i | (1 << 30); - *(*cmd)++ = - ((drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000) + - (i - 0x2000) * 4; - } -} - -#endif - -/* chicken restore */ -static unsigned int *build_chicken_restore_cmds(struct kgsl_drawctxt *drawctxt, - struct tmp_ctx *ctx) -{ - unsigned int *start = ctx->cmd; - unsigned int *cmds = start; - - *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmds++ = 0; - - *cmds++ = pm4_type0_packet(REG_TP0_CHICKEN, 1); - ctx->chicken_restore = gpuaddr(cmds, &drawctxt->gpustate); - *cmds++ = 0x00000000; - - /* create indirect buffer command for above command sequence */ - create_ib1(drawctxt, drawctxt->chicken_restore, start, cmds); - - return cmds; -} - -/* save h/w regs, alu constants, texture contants, etc. ... -* requires: bool_shadow_gpuaddr, loop_shadow_gpuaddr -*/ -static void build_regsave_cmds(struct kgsl_device *device, - struct kgsl_drawctxt *drawctxt, - struct tmp_ctx *ctx) -{ - unsigned int *start = ctx->cmd; - unsigned int *cmd = start; - unsigned int pm_override1; - - kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE1, &pm_override1); - - *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmd++ = 0; - -#ifdef DISABLE_SHADOW_WRITES - /* Make sure the HW context has the correct register values - * before reading them. */ - *cmd++ = pm4_type3_packet(PM4_CONTEXT_UPDATE, 1); - *cmd++ = 0; -#endif - - /* Enable clock override for REG_FIFOS_SCLK */ - *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); - *cmd++ = pm_override1 | (1 << 6); - -#ifdef DISABLE_SHADOW_WRITES - /* Write HW registers into shadow */ - build_reg_to_mem_range(REG_RB_SURFACE_INFO, REG_RB_DEPTH_INFO, &cmd, - drawctxt); - build_reg_to_mem_range(REG_COHER_DEST_BASE_0, - REG_PA_SC_SCREEN_SCISSOR_BR, &cmd, drawctxt); - build_reg_to_mem_range(REG_PA_SC_WINDOW_OFFSET, - REG_PA_SC_WINDOW_SCISSOR_BR, &cmd, drawctxt); - build_reg_to_mem_range(REG_VGT_MAX_VTX_INDX, REG_RB_FOG_COLOR, &cmd, - drawctxt); - build_reg_to_mem_range(REG_RB_STENCILREFMASK_BF, - REG_PA_CL_VPORT_ZOFFSET, &cmd, drawctxt); - build_reg_to_mem_range(REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1, &cmd, - drawctxt); - build_reg_to_mem_range(REG_RB_DEPTHCONTROL, REG_RB_MODECONTROL, &cmd, - drawctxt); - build_reg_to_mem_range(REG_PA_SU_POINT_SIZE, REG_PA_SC_LINE_STIPPLE, - &cmd, drawctxt); - build_reg_to_mem_range(REG_PA_SC_VIZ_QUERY, REG_PA_SC_VIZ_QUERY, &cmd, - drawctxt); - build_reg_to_mem_range(REG_PA_SC_LINE_CNTL, REG_SQ_PS_CONST, &cmd, - drawctxt); - build_reg_to_mem_range(REG_PA_SC_AA_MASK, REG_PA_SC_AA_MASK, &cmd, - drawctxt); - build_reg_to_mem_range(REG_VGT_VERTEX_REUSE_BLOCK_CNTL, - REG_RB_DEPTH_CLEAR, &cmd, drawctxt); - build_reg_to_mem_range(REG_RB_SAMPLE_COUNT_CTL, REG_RB_COLOR_DEST_MASK, - &cmd, drawctxt); - build_reg_to_mem_range(REG_PA_SU_POLY_OFFSET_FRONT_SCALE, - REG_PA_SU_POLY_OFFSET_BACK_OFFSET, &cmd, - drawctxt); - - /* Copy ALU constants */ - cmd = - reg_to_mem(cmd, (drawctxt->gpustate.gpuaddr) & 0xFFFFE000, - REG_SQ_CONSTANT_0, ALU_CONSTANTS); - - /* Copy Tex constants */ - cmd = - reg_to_mem(cmd, - (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000, - REG_SQ_FETCH_0, TEX_CONSTANTS); -#else - - /* Insert a wait for idle packet before reading the registers. - * This is to fix a hang/reset seen during stress testing. In this - * hang, CP encountered a timeout reading SQ's boolean constant - * register. There is logic in the HW that blocks reading of this - * register when the SQ block is not idle, which we believe is - * contributing to the hang.*/ - *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmd++ = 0; - /* H/w registers are already shadowed; just need to disable shadowing - * to prevent corruption. - */ - *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); - *cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000; - *cmd++ = 4 << 16; /* regs, start=0 */ - *cmd++ = 0x0; /* count = 0 */ - - /* ALU constants are already shadowed; just need to disable shadowing - * to prevent corruption. - */ - *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); - *cmd++ = drawctxt->gpustate.gpuaddr & 0xFFFFE000; - *cmd++ = 0 << 16; /* ALU, start=0 */ - *cmd++ = 0x0; /* count = 0 */ - - /* Tex constants are already shadowed; just need to disable shadowing - * to prevent corruption. - */ - *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); - *cmd++ = (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000; - *cmd++ = 1 << 16; /* Tex, start=0 */ - *cmd++ = 0x0; /* count = 0 */ -#endif - - /* Need to handle some of the registers separately */ - *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmd++ = REG_SQ_GPR_MANAGEMENT; - *cmd++ = ctx->reg_values[0]; - - *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmd++ = REG_TP0_CHICKEN; - *cmd++ = ctx->reg_values[1]; - - *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmd++ = REG_RBBM_PM_OVERRIDE1; - *cmd++ = ctx->reg_values[2]; - - *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmd++ = REG_RBBM_PM_OVERRIDE2; - *cmd++ = ctx->reg_values[3]; - - /* Copy Boolean constants */ - cmd = reg_to_mem(cmd, ctx->bool_shadow, REG_SQ_CF_BOOLEANS, - BOOL_CONSTANTS); - - /* Copy Loop constants */ - cmd = reg_to_mem(cmd, ctx->loop_shadow, REG_SQ_CF_LOOP, LOOP_CONSTANTS); - - /* Restore RBBM_PM_OVERRIDE1 */ - *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmd++ = 0; - *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); - *cmd++ = pm_override1; - - /* create indirect buffer command for above command sequence */ - create_ib1(drawctxt, drawctxt->reg_save, start, cmd); - - ctx->cmd = cmd; -} - -/*copy colour, depth, & stencil buffers from graphics memory to system memory*/ -static unsigned int *build_gmem2sys_cmds(struct kgsl_device *device, - struct kgsl_drawctxt *drawctxt, - struct tmp_ctx *ctx, - struct gmem_shadow_t *shadow) -{ - unsigned int *cmds = shadow->gmem_save_commands; - unsigned int *start = cmds; - unsigned int pm_override1; - /* Calculate the new offset based on the adjusted base */ - unsigned int bytesperpixel = format2bytesperpixel[shadow->format]; - unsigned int addr = - (shadow->gmemshadow.gpuaddr + shadow->offset * bytesperpixel); - unsigned int offset = (addr - (addr & 0xfffff000)) / bytesperpixel; - - kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE1, &pm_override1); - - /* Store TP0_CHICKEN register */ - *cmds++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmds++ = REG_TP0_CHICKEN; - if (ctx) - *cmds++ = ctx->chicken_restore; - else - cmds++; - - *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmds++ = 0; - - /* Enable clock override for REG_FIFOS_SCLK */ - *cmds++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); - *cmds++ = pm_override1 | (1 << 6); - - /* Set TP0_CHICKEN to zero */ - *cmds++ = pm4_type0_packet(REG_TP0_CHICKEN, 1); - *cmds++ = 0x00000000; - - /* Set PA_SC_AA_CONFIG to 0 */ - *cmds++ = pm4_type0_packet(REG_PA_SC_AA_CONFIG, 1); - *cmds++ = 0x00000000; - - /* program shader */ - - /* load shader vtx constants ... 5 dwords */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 4); - *cmds++ = (0x1 << 16) | SHADER_CONST_ADDR; - *cmds++ = 0; - /* valid(?) vtx constant flag & addr */ - *cmds++ = shadow->quad_vertices.gpuaddr | 0x3; - /* limit = 12 dwords */ - *cmds++ = 0x00000030; - - /* Invalidate L2 cache to make sure vertices are updated */ - *cmds++ = pm4_type0_packet(REG_TC_CNTL_STATUS, 1); - *cmds++ = 0x1; - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 4); - *cmds++ = PM4_REG(REG_VGT_MAX_VTX_INDX); - *cmds++ = 0x00ffffff; /* REG_VGT_MAX_VTX_INDX */ - *cmds++ = 0x0; /* REG_VGT_MIN_VTX_INDX */ - *cmds++ = 0x00000000; /* REG_VGT_INDX_OFFSET */ - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_PA_SC_AA_MASK); - *cmds++ = 0x0000ffff; /* REG_PA_SC_AA_MASK */ - - /* load the patched vertex shader stream */ - cmds = program_shader(cmds, 0, gmem2sys_vtx_pgm, GMEM2SYS_VTX_PGM_LEN); - - /* Load the patched fragment shader stream */ - cmds = - program_shader(cmds, 1, gmem2sys_frag_pgm, GMEM2SYS_FRAG_PGM_LEN); - - /* SQ_PROGRAM_CNTL / SQ_CONTEXT_MISC */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_SQ_PROGRAM_CNTL); - *cmds++ = 0x10010001; - *cmds++ = 0x00000008; - - /* resolve */ - - /* PA_CL_VTE_CNTL */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_PA_CL_VTE_CNTL); - /* disable X/Y/Z transforms, X/Y/Z are premultiplied by W */ - *cmds++ = 0x00000b00; - - /* program surface info */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_RB_SURFACE_INFO); - *cmds++ = shadow->gmem_pitch; /* pitch, MSAA = 1 */ - - /* RB_COLOR_INFO Endian=none, Linear, Format=RGBA8888, Swap=0, - * Base=gmem_base - */ - /* gmem base assumed 4K aligned. */ - if (ctx) { - BUG_ON(ctx->gmem_base & 0xFFF); - *cmds++ = - (shadow-> - format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT) | ctx-> - gmem_base; - } else { - unsigned int temp = *cmds; - *cmds++ = (temp & ~RB_COLOR_INFO__COLOR_FORMAT_MASK) | - (shadow->format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT); - } - - /* disable Z */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_RB_DEPTHCONTROL); - *cmds++ = 0; - - /* set REG_PA_SU_SC_MODE_CNTL - * Front_ptype = draw triangles - * Back_ptype = draw triangles - * Provoking vertex = last - */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_PA_SU_SC_MODE_CNTL); - *cmds++ = 0x00080240; - - /* Use maximum scissor values -- quad vertices already have the - * correct bounds */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_PA_SC_SCREEN_SCISSOR_TL); - *cmds++ = (0 << 16) | 0; - *cmds++ = (0x1fff << 16) | (0x1fff); - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_PA_SC_WINDOW_SCISSOR_TL); - *cmds++ = (unsigned int)((1U << 31) | (0 << 16) | 0); - *cmds++ = (0x1fff << 16) | (0x1fff); - - /* load the viewport so that z scale = clear depth and - * z offset = 0.0f - */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_PA_CL_VPORT_ZSCALE); - *cmds++ = 0xbf800000; /* -1.0f */ - *cmds++ = 0x0; - - /* load the stencil ref value - * $AAM - do this later - */ - - /* load the COPY state */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 6); - *cmds++ = PM4_REG(REG_RB_COPY_CONTROL); - *cmds++ = 0; /* RB_COPY_CONTROL */ - *cmds++ = addr & 0xfffff000; /* RB_COPY_DEST_BASE */ - *cmds++ = shadow->pitch >> 5; /* RB_COPY_DEST_PITCH */ - - /* Endian=none, Linear, Format=RGBA8888,Swap=0,!Dither, - * MaskWrite:R=G=B=A=1 - */ - *cmds++ = 0x0003c008 | - (shadow->format << RB_COPY_DEST_INFO__COPY_DEST_FORMAT__SHIFT); - /* Make sure we stay in offsetx field. */ - BUG_ON(offset & 0xfffff000); - *cmds++ = offset; - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_RB_MODECONTROL); - *cmds++ = 0x6; /* EDRAM copy */ - - /* queue the draw packet */ - *cmds++ = pm4_type3_packet(PM4_DRAW_INDX, 2); - *cmds++ = 0; /* viz query info. */ - /* PrimType=RectList, NumIndices=3, SrcSel=AutoIndex */ - *cmds++ = 0x00030088; - - /* Restore RBBM_PM_OVERRIDE1 */ - *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmds++ = 0; - *cmds++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); - *cmds++ = pm_override1; - /* create indirect buffer command for above command sequence */ - create_ib1(drawctxt, shadow->gmem_save, start, cmds); - - return cmds; -} - -/* context restore */ - -/*copy colour, depth, & stencil buffers from system memory to graphics memory*/ -static unsigned int *build_sys2gmem_cmds(struct kgsl_device *device, - struct kgsl_drawctxt *drawctxt, - struct tmp_ctx *ctx, - struct gmem_shadow_t *shadow) -{ - unsigned int *cmds = shadow->gmem_restore_commands; - unsigned int *start = cmds; - unsigned int pm_override1; - - kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE1, &pm_override1); - - /* Store TP0_CHICKEN register */ - *cmds++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmds++ = REG_TP0_CHICKEN; - if (ctx) - *cmds++ = ctx->chicken_restore; - else - cmds++; - - *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmds++ = 0; - - /* Enable clock override for REG_FIFOS_SCLK */ - *cmds++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); - *cmds++ = pm_override1 | (1 << 6); - - /* Set TP0_CHICKEN to zero */ - *cmds++ = pm4_type0_packet(REG_TP0_CHICKEN, 1); - *cmds++ = 0x00000000; - - /* Set PA_SC_AA_CONFIG to 0 */ - *cmds++ = pm4_type0_packet(REG_PA_SC_AA_CONFIG, 1); - *cmds++ = 0x00000000; - /* shader constants */ - - /* vertex buffer constants */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 7); - - *cmds++ = (0x1 << 16) | (9 * 6); - /* valid(?) vtx constant flag & addr */ - *cmds++ = shadow->quad_vertices.gpuaddr | 0x3; - /* limit = 12 dwords */ - *cmds++ = 0x00000030; - /* valid(?) vtx constant flag & addr */ - *cmds++ = shadow->quad_texcoords.gpuaddr | 0x3; - /* limit = 8 dwords */ - *cmds++ = 0x00000020; - *cmds++ = 0; - *cmds++ = 0; - - /* Invalidate L2 cache to make sure vertices are updated */ - *cmds++ = pm4_type0_packet(REG_TC_CNTL_STATUS, 1); - *cmds++ = 0x1; - - cmds = program_shader(cmds, 0, sys2gmem_vtx_pgm, SYS2GMEM_VTX_PGM_LEN); - - /* Load the patched fragment shader stream */ - cmds = - program_shader(cmds, 1, sys2gmem_frag_pgm, SYS2GMEM_FRAG_PGM_LEN); - - /* SQ_PROGRAM_CNTL / SQ_CONTEXT_MISC */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_SQ_PROGRAM_CNTL); - *cmds++ = 0x10030002; - *cmds++ = 0x00000008; - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_PA_SC_AA_MASK); - *cmds++ = 0x0000ffff; /* REG_PA_SC_AA_MASK */ - - /* PA_SC_VIZ_QUERY */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_PA_SC_VIZ_QUERY); - *cmds++ = 0x0; /*REG_PA_SC_VIZ_QUERY */ - - /* RB_COLORCONTROL */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_RB_COLORCONTROL); - *cmds++ = 0x00000c20; - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 4); - *cmds++ = PM4_REG(REG_VGT_MAX_VTX_INDX); - *cmds++ = 0x00ffffff; /* mmVGT_MAX_VTX_INDX */ - *cmds++ = 0x0; /* mmVGT_MIN_VTX_INDX */ - *cmds++ = 0x00000000; /* mmVGT_INDX_OFFSET */ - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_VGT_VERTEX_REUSE_BLOCK_CNTL); - *cmds++ = 0x00000002; /* mmVGT_VERTEX_REUSE_BLOCK_CNTL */ - *cmds++ = 0x00000002; /* mmVGT_OUT_DEALLOC_CNTL */ - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_SQ_INTERPOLATOR_CNTL); - //*cmds++ = 0x0000ffff; //mmSQ_INTERPOLATOR_CNTL - *cmds++ = 0xffffffff; //mmSQ_INTERPOLATOR_CNTL - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_PA_SC_AA_CONFIG); - *cmds++ = 0x00000000; /* REG_PA_SC_AA_CONFIG */ - - /* set REG_PA_SU_SC_MODE_CNTL - * Front_ptype = draw triangles - * Back_ptype = draw triangles - * Provoking vertex = last - */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_PA_SU_SC_MODE_CNTL); - *cmds++ = 0x00080240; - - /* texture constants */ - *cmds++ = - pm4_type3_packet(PM4_SET_CONSTANT, (SYS2GMEM_TEX_CONST_LEN + 1)); - *cmds++ = (0x1 << 16) | (0 * 6); - memcpy(cmds, sys2gmem_tex_const, SYS2GMEM_TEX_CONST_LEN << 2); - cmds[0] |= (shadow->pitch >> 5) << 22; - cmds[1] |= - shadow->gmemshadow.gpuaddr | surface_format_table[shadow->format]; - cmds[2] |= - (shadow->width + shadow->offset_x - 1) | (shadow->height + - shadow->offset_y - - 1) << 13; - cmds += SYS2GMEM_TEX_CONST_LEN; - - /* program surface info */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_RB_SURFACE_INFO); - *cmds++ = shadow->gmem_pitch; /* pitch, MSAA = 1 */ - - /* RB_COLOR_INFO Endian=none, Linear, Format=RGBA8888, Swap=0, - * Base=gmem_base - */ - if (ctx) - *cmds++ = - (shadow-> - format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT) | ctx-> - gmem_base; - else { - unsigned int temp = *cmds; - *cmds++ = (temp & ~RB_COLOR_INFO__COLOR_FORMAT_MASK) | - (shadow->format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT); - } - - /* RB_DEPTHCONTROL */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_RB_DEPTHCONTROL); - *cmds++ = 0; /* disable Z */ - - /* Use maximum scissor values -- quad vertices already - * have the correct bounds */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_PA_SC_SCREEN_SCISSOR_TL); - *cmds++ = (0 << 16) | 0; - *cmds++ = ((0x1fff) << 16) | 0x1fff; - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_PA_SC_WINDOW_SCISSOR_TL); - *cmds++ = (unsigned int)((1U << 31) | (0 << 16) | 0); - *cmds++ = ((0x1fff) << 16) | 0x1fff; - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_PA_CL_VTE_CNTL); - /* disable X/Y/Z transforms, X/Y/Z are premultiplied by W */ - *cmds++ = 0x00000b00; - - /*load the viewport so that z scale = clear depth and z offset = 0.0f */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_PA_CL_VPORT_ZSCALE); - *cmds++ = 0xbf800000; - *cmds++ = 0x0; - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_RB_COLOR_MASK); - *cmds++ = 0x0000000f; /* R = G = B = 1:enabled */ - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_RB_COLOR_DEST_MASK); - *cmds++ = 0xffffffff; - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_SQ_WRAPPING_0); - *cmds++ = 0x00000000; - *cmds++ = 0x00000000; - - /* load the stencil ref value - * $AAM - do this later - */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_RB_MODECONTROL); - /* draw pixels with color and depth/stencil component */ - *cmds++ = 0x4; - - /* queue the draw packet */ - *cmds++ = pm4_type3_packet(PM4_DRAW_INDX, 2); - *cmds++ = 0; /* viz query info. */ - /* PrimType=RectList, NumIndices=3, SrcSel=AutoIndex */ - *cmds++ = 0x00030088; - - /* Restore RBBM_PM_OVERRIDE1 */ - *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmds++ = 0; - *cmds++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); - *cmds++ = pm_override1; - - /* create indirect buffer command for above command sequence */ - create_ib1(drawctxt, shadow->gmem_restore, start, cmds); - - return cmds; -} - -/* restore h/w regs, alu constants, texture constants, etc. ... */ -static unsigned *reg_range(unsigned int *cmd, unsigned int start, - unsigned int end) -{ - *cmd++ = PM4_REG(start); /* h/w regs, start addr */ - *cmd++ = end - start + 1; /* count */ - return cmd; -} - -static void build_regrestore_cmds(struct kgsl_device *device, - struct kgsl_drawctxt *drawctxt, - struct tmp_ctx *ctx) -{ - unsigned int *start = ctx->cmd; - unsigned int *cmd = start; - unsigned int pm_override1; - - kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE1, &pm_override1); - - *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmd++ = 0; - - /* Enable clock override for REG_FIFOS_SCLK */ - *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); - *cmd++ = pm_override1 | (1 << 6); - - /* H/W Registers */ - /* deferred pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, ???); */ - cmd++; -#ifdef DISABLE_SHADOW_WRITES - /* Force mismatch */ - *cmd++ = ((drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000) | 1; -#else - *cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000; -#endif - - cmd = reg_range(cmd, REG_RB_SURFACE_INFO, REG_PA_SC_SCREEN_SCISSOR_BR); - cmd = reg_range(cmd, REG_PA_SC_WINDOW_OFFSET, - REG_PA_SC_WINDOW_SCISSOR_BR); - cmd = reg_range(cmd, REG_VGT_MAX_VTX_INDX, REG_PA_CL_VPORT_ZOFFSET); - cmd = reg_range(cmd, REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1); - cmd = reg_range(cmd, REG_RB_DEPTHCONTROL, REG_RB_MODECONTROL); - cmd = reg_range(cmd, REG_PA_SU_POINT_SIZE, - REG_PA_SC_VIZ_QUERY); /*REG_VGT_ENHANCE */ - cmd = reg_range(cmd, REG_PA_SC_LINE_CNTL, REG_RB_COLOR_DEST_MASK); - cmd = reg_range(cmd, REG_PA_SU_POLY_OFFSET_FRONT_SCALE, - REG_PA_SU_POLY_OFFSET_BACK_OFFSET); - - /* Now we know how many register blocks we have, we can compute command - * length - */ - start[4] = - pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, (cmd - start) - 5); - /* Enable shadowing for the entire register block. */ -#ifdef DISABLE_SHADOW_WRITES - start[6] |= (0 << 24) | (4 << 16); /* Disable shadowing. */ -#else - start[6] |= (1 << 24) | (4 << 16); -#endif - - /* Need to handle some of the registers separately */ - *cmd++ = pm4_type0_packet(REG_SQ_GPR_MANAGEMENT, 1); - ctx->reg_values[0] = gpuaddr(cmd, &drawctxt->gpustate); - *cmd++ = 0x00040400; - - *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmd++ = 0; - *cmd++ = pm4_type0_packet(REG_TP0_CHICKEN, 1); - ctx->reg_values[1] = gpuaddr(cmd, &drawctxt->gpustate); - *cmd++ = 0x00000000; - - *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); - ctx->reg_values[2] = gpuaddr(cmd, &drawctxt->gpustate); - *cmd++ = 0x00000000; - - *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE2, 1); - ctx->reg_values[3] = gpuaddr(cmd, &drawctxt->gpustate); - *cmd++ = 0x00000000; - - /* ALU Constants */ - *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); - *cmd++ = drawctxt->gpustate.gpuaddr & 0xFFFFE000; -#ifdef DISABLE_SHADOW_WRITES - *cmd++ = (0 << 24) | (0 << 16) | 0; /* Disable shadowing */ -#else - *cmd++ = (1 << 24) | (0 << 16) | 0; -#endif - *cmd++ = ALU_CONSTANTS; - - /* Texture Constants */ - *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); - *cmd++ = (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000; -#ifdef DISABLE_SHADOW_WRITES - /* Disable shadowing */ - *cmd++ = (0 << 24) | (1 << 16) | 0; -#else - *cmd++ = (1 << 24) | (1 << 16) | 0; -#endif - *cmd++ = TEX_CONSTANTS; - - /* Boolean Constants */ - *cmd++ = pm4_type3_packet(PM4_SET_CONSTANT, 1 + BOOL_CONSTANTS); - *cmd++ = (2 << 16) | 0; - - /* the next BOOL_CONSTANT dwords is the shadow area for - * boolean constants. - */ - ctx->bool_shadow = gpuaddr(cmd, &drawctxt->gpustate); - cmd += BOOL_CONSTANTS; - - /* Loop Constants */ - *cmd++ = pm4_type3_packet(PM4_SET_CONSTANT, 1 + LOOP_CONSTANTS); - *cmd++ = (3 << 16) | 0; - - /* the next LOOP_CONSTANTS dwords is the shadow area for - * loop constants. - */ - ctx->loop_shadow = gpuaddr(cmd, &drawctxt->gpustate); - cmd += LOOP_CONSTANTS; - - /* Restore RBBM_PM_OVERRIDE1 */ - *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmd++ = 0; - *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE1, 1); - *cmd++ = pm_override1; - - /* create indirect buffer command for above command sequence */ - create_ib1(drawctxt, drawctxt->reg_restore, start, cmd); - - ctx->cmd = cmd; -} - -/* quad for saving/restoring gmem */ -static void set_gmem_copy_quad(struct gmem_shadow_t *shadow) -{ - /* set vertex buffer values */ - gmem_copy_quad[1] = uint2float(shadow->height + shadow->gmem_offset_y); - gmem_copy_quad[3] = uint2float(shadow->width + shadow->gmem_offset_x); - gmem_copy_quad[4] = uint2float(shadow->height + shadow->gmem_offset_y); - gmem_copy_quad[9] = uint2float(shadow->width + shadow->gmem_offset_x); - - gmem_copy_quad[0] = uint2float(shadow->gmem_offset_x); - gmem_copy_quad[6] = uint2float(shadow->gmem_offset_x); - gmem_copy_quad[7] = uint2float(shadow->gmem_offset_y); - gmem_copy_quad[10] = uint2float(shadow->gmem_offset_y); - - BUG_ON(shadow->offset_x); - BUG_ON(shadow->offset_y); - - memcpy(shadow->quad_vertices.hostptr, gmem_copy_quad, QUAD_LEN << 2); - - memcpy(shadow->quad_texcoords.hostptr, gmem_copy_texcoord, - TEXCOORD_LEN << 2); -} - -/* quad for saving/restoring gmem */ -static void build_quad_vtxbuff(struct kgsl_drawctxt *drawctxt, - struct tmp_ctx *ctx, struct gmem_shadow_t *shadow) -{ - unsigned int *cmd = ctx->cmd; - - /* quad vertex buffer location (in GPU space) */ - shadow->quad_vertices.hostptr = cmd; - shadow->quad_vertices.gpuaddr = gpuaddr(cmd, &drawctxt->gpustate); - - cmd += QUAD_LEN; - - /* tex coord buffer location (in GPU space) */ - shadow->quad_texcoords.hostptr = cmd; - shadow->quad_texcoords.gpuaddr = gpuaddr(cmd, &drawctxt->gpustate); - - cmd += TEXCOORD_LEN; - - set_gmem_copy_quad(shadow); - - ctx->cmd = cmd; -} - -static void -build_shader_save_restore_cmds(struct kgsl_drawctxt *drawctxt, - struct tmp_ctx *ctx) -{ - unsigned int *cmd = ctx->cmd; - unsigned int *save, *restore, *fixup; -#if defined(PM4_IM_STORE) - unsigned int *startSizeVtx, *startSizePix, *startSizeShared; -#endif - unsigned int *partition1; - unsigned int *shaderBases, *partition2; - -#if defined(PM4_IM_STORE) - /* compute vertex, pixel and shared instruction shadow GPU addresses */ - ctx->shader_vertex = drawctxt->gpustate.gpuaddr + SHADER_OFFSET; - ctx->shader_pixel = ctx->shader_vertex + SHADER_SHADOW_SIZE; - ctx->shader_shared = ctx->shader_pixel + SHADER_SHADOW_SIZE; -#endif - - /* restore shader partitioning and instructions */ - - restore = cmd; /* start address */ - - /* Invalidate Vertex & Pixel instruction code address and sizes */ - *cmd++ = pm4_type3_packet(PM4_INVALIDATE_STATE, 1); - *cmd++ = 0x00000300; /* 0x100 = Vertex, 0x200 = Pixel */ - - /* Restore previous shader vertex & pixel instruction bases. */ - *cmd++ = pm4_type3_packet(PM4_SET_SHADER_BASES, 1); - shaderBases = cmd++; /* TBD #5: shader bases (from fixup) */ - - /* write the shader partition information to a scratch register */ - *cmd++ = pm4_type0_packet(REG_SQ_INST_STORE_MANAGMENT, 1); - partition1 = cmd++; /* TBD #4a: partition info (from save) */ - -#if defined(PM4_IM_STORE) - /* load vertex shader instructions from the shadow. */ - *cmd++ = pm4_type3_packet(PM4_IM_LOAD, 2); - *cmd++ = ctx->shader_vertex + 0x0; /* 0x0 = Vertex */ - startSizeVtx = cmd++; /* TBD #1: start/size (from save) */ - - /* load pixel shader instructions from the shadow. */ - *cmd++ = pm4_type3_packet(PM4_IM_LOAD, 2); - *cmd++ = ctx->shader_pixel + 0x1; /* 0x1 = Pixel */ - startSizePix = cmd++; /* TBD #2: start/size (from save) */ - - /* load shared shader instructions from the shadow. */ - *cmd++ = pm4_type3_packet(PM4_IM_LOAD, 2); - *cmd++ = ctx->shader_shared + 0x2; /* 0x2 = Shared */ - startSizeShared = cmd++; /* TBD #3: start/size (from save) */ -#endif - - /* create indirect buffer command for above command sequence */ - create_ib1(drawctxt, drawctxt->shader_restore, restore, cmd); - - /* - * fixup SET_SHADER_BASES data - * - * since self-modifying PM4 code is being used here, a seperate - * command buffer is used for this fixup operation, to ensure the - * commands are not read by the PM4 engine before the data fields - * have been written. - */ - - fixup = cmd; /* start address */ - - /* write the shader partition information to a scratch register */ - *cmd++ = pm4_type0_packet(REG_SCRATCH_REG2, 1); - partition2 = cmd++; /* TBD #4b: partition info (from save) */ - - /* mask off unused bits, then OR with shader instruction memory size */ - *cmd++ = pm4_type3_packet(PM4_REG_RMW, 3); - *cmd++ = REG_SCRATCH_REG2; - /* AND off invalid bits. */ - *cmd++ = 0x0FFF0FFF; - /* OR in instruction memory size */ - *cmd++ = (unsigned int)((SHADER_INSTRUCT_LOG2 - 5U) << 29); - - /* write the computed value to the SET_SHADER_BASES data field */ - *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmd++ = REG_SCRATCH_REG2; - /* TBD #5: shader bases (to restore) */ - *cmd++ = gpuaddr(shaderBases, &drawctxt->gpustate); - - /* create indirect buffer command for above command sequence */ - create_ib1(drawctxt, drawctxt->shader_fixup, fixup, cmd); - - /* save shader partitioning and instructions */ - - save = cmd; /* start address */ - - *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmd++ = 0; - - /* fetch the SQ_INST_STORE_MANAGMENT register value, - * store the value in the data fields of the SET_CONSTANT commands - * above. - */ - *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmd++ = REG_SQ_INST_STORE_MANAGMENT; - /* TBD #4a: partition info (to restore) */ - *cmd++ = gpuaddr(partition1, &drawctxt->gpustate); - *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmd++ = REG_SQ_INST_STORE_MANAGMENT; - /* TBD #4b: partition info (to fixup) */ - *cmd++ = gpuaddr(partition2, &drawctxt->gpustate); - -#if defined(PM4_IM_STORE) - - /* store the vertex shader instructions */ - *cmd++ = pm4_type3_packet(PM4_IM_STORE, 2); - *cmd++ = ctx->shader_vertex + 0x0; /* 0x0 = Vertex */ - /* TBD #1: start/size (to restore) */ - *cmd++ = gpuaddr(startSizeVtx, &drawctxt->gpustate); - - /* store the pixel shader instructions */ - *cmd++ = pm4_type3_packet(PM4_IM_STORE, 2); - *cmd++ = ctx->shader_pixel + 0x1; /* 0x1 = Pixel */ - /* TBD #2: start/size (to restore) */ - *cmd++ = gpuaddr(startSizePix, &drawctxt->gpustate); - - /* store the shared shader instructions if vertex base is nonzero */ - - *cmd++ = pm4_type3_packet(PM4_IM_STORE, 2); - *cmd++ = ctx->shader_shared + 0x2; /* 0x2 = Shared */ - /* TBD #3: start/size (to restore) */ - *cmd++ = gpuaddr(startSizeShared, &drawctxt->gpustate); - -#endif - - *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmd++ = 0; - - /* create indirect buffer command for above command sequence */ - create_ib1(drawctxt, drawctxt->shader_save, save, cmd); - - ctx->cmd = cmd; -} - -/* create buffers for saving/restoring registers, constants, & GMEM */ -static int -create_gpustate_shadow(struct kgsl_device *device, - struct kgsl_drawctxt *drawctxt, struct tmp_ctx *ctx) -{ - uint32_t flags; - - flags = (KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN8K); - - /* allocate memory to allow HW to save sub-blocks for efficient context - * save/restore - */ - if (kgsl_sharedmem_alloc(flags, CONTEXT_SIZE, &drawctxt->gpustate) != 0) - return -ENOMEM; - if (kgsl_mmu_map(drawctxt->pagetable, drawctxt->gpustate.physaddr, - drawctxt->gpustate.size, - GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, - &drawctxt->gpustate.gpuaddr, - KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN8K)) - return -EINVAL; - - drawctxt->flags |= CTXT_FLAGS_STATE_SHADOW; - - /* Blank out h/w register, constant, and command buffer shadows. */ - kgsl_sharedmem_set(&drawctxt->gpustate, 0, 0, CONTEXT_SIZE); - - /* set-up command and vertex buffer pointers */ - ctx->cmd = ctx->start - = (unsigned int *)((char *)drawctxt->gpustate.hostptr + CMD_OFFSET); - - /* build indirect command buffers to save & restore regs/constants */ - kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT); - build_regrestore_cmds(device, drawctxt, ctx); - build_regsave_cmds(device, drawctxt, ctx); - - build_shader_save_restore_cmds(drawctxt, ctx); - - return 0; -} - -/* create buffers for saving/restoring registers, constants, & GMEM */ -static int -create_gmem_shadow(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt, - struct tmp_ctx *ctx) -{ - unsigned int flags = KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN8K, i; - - config_gmemsize(&drawctxt->context_gmem_shadow, - device->gmemspace.sizebytes); - ctx->gmem_base = device->gmemspace.gpu_base; - - /* allocate memory for GMEM shadow */ - if (kgsl_sharedmem_alloc(flags, drawctxt->context_gmem_shadow.size, - &drawctxt->context_gmem_shadow.gmemshadow) != - 0) - return -ENOMEM; - if (kgsl_mmu_map(drawctxt->pagetable, - drawctxt->context_gmem_shadow.gmemshadow.physaddr, - drawctxt->context_gmem_shadow.gmemshadow.size, - GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, - &drawctxt->context_gmem_shadow.gmemshadow.gpuaddr, - KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN8K)) - return -EINVAL; - - /* we've allocated the shadow, when swapped out, GMEM must be saved. */ - drawctxt->flags |= CTXT_FLAGS_GMEM_SHADOW | CTXT_FLAGS_GMEM_SAVE; - - /* blank out gmem shadow. */ - kgsl_sharedmem_set(&drawctxt->context_gmem_shadow.gmemshadow, 0, 0, - drawctxt->context_gmem_shadow.size); - - /* build quad vertex buffer */ - build_quad_vtxbuff(drawctxt, ctx, &drawctxt->context_gmem_shadow); - - /* build TP0_CHICKEN register restore command buffer */ - ctx->cmd = build_chicken_restore_cmds(drawctxt, ctx); - - /* build indirect command buffers to save & restore gmem */ - /* Idle because we are reading PM override registers */ - kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT); - drawctxt->context_gmem_shadow.gmem_save_commands = ctx->cmd; - ctx->cmd = - build_gmem2sys_cmds(device, drawctxt, ctx, - &drawctxt->context_gmem_shadow); - drawctxt->context_gmem_shadow.gmem_restore_commands = ctx->cmd; - ctx->cmd = - build_sys2gmem_cmds(device, drawctxt, ctx, - &drawctxt->context_gmem_shadow); - - for (i = 0; i < KGSL_MAX_GMEM_SHADOW_BUFFERS; i++) { - build_quad_vtxbuff(drawctxt, ctx, - &drawctxt->user_gmem_shadow[i]); - - drawctxt->user_gmem_shadow[i].gmem_save_commands = ctx->cmd; - ctx->cmd = - build_gmem2sys_cmds(device, drawctxt, ctx, - &drawctxt->user_gmem_shadow[i]); - - drawctxt->user_gmem_shadow[i].gmem_restore_commands = ctx->cmd; - ctx->cmd = - build_sys2gmem_cmds(device, drawctxt, ctx, - &drawctxt->user_gmem_shadow[i]); - } - - return 0; -} - -/* init draw context */ - -int kgsl_drawctxt_init(struct kgsl_device *device) -{ - return 0; -} - -/* close draw context */ -int kgsl_drawctxt_close(struct kgsl_device *device) -{ - return 0; -} - -/* create a new drawing context */ - -int -kgsl_drawctxt_create(struct kgsl_device *device, - struct kgsl_pagetable *pagetable, - unsigned int flags, unsigned int *drawctxt_id) -{ - struct kgsl_drawctxt *drawctxt; - int index; - struct tmp_ctx ctx; - - KGSL_CTXT_INFO("pt %p flags %08x\n", pagetable, flags); - if (device->drawctxt_count >= KGSL_CONTEXT_MAX) - return -EINVAL; - - /* find a free context slot */ - index = 0; - while (index < KGSL_CONTEXT_MAX) { - if (device->drawctxt[index].flags == CTXT_FLAGS_NOT_IN_USE) - break; - - index++; - } - - if (index >= KGSL_CONTEXT_MAX) - return -EINVAL; - - drawctxt = &device->drawctxt[index]; - drawctxt->pagetable = pagetable; - drawctxt->flags = CTXT_FLAGS_IN_USE; - drawctxt->bin_base_offset = 0; - - device->drawctxt_count++; - - if (create_gpustate_shadow(device, drawctxt, &ctx) - != 0) { - kgsl_drawctxt_destroy(device, index); - return -EINVAL; - } - - /* Save the shader instruction memory on context switching */ - drawctxt->flags |= CTXT_FLAGS_SHADER_SAVE; - - if (!(flags & KGSL_CONTEXT_NO_GMEM_ALLOC)) { - /* create gmem shadow */ - memset(drawctxt->user_gmem_shadow, 0, - sizeof(struct gmem_shadow_t) * - KGSL_MAX_GMEM_SHADOW_BUFFERS); - - if (create_gmem_shadow(device, drawctxt, &ctx) - != 0) { - kgsl_drawctxt_destroy(device, index); - return -EINVAL; - } - } - - BUG_ON(ctx.cmd - ctx.start > CMD_BUFFER_LEN); - - *drawctxt_id = index; - - KGSL_CTXT_INFO("return drawctxt_id %d\n", *drawctxt_id); - return 0; -} - -/* destroy a drawing context */ - -int kgsl_drawctxt_destroy(struct kgsl_device *device, unsigned int drawctxt_id) -{ - struct kgsl_drawctxt *drawctxt; - - drawctxt = &device->drawctxt[drawctxt_id]; - - KGSL_CTXT_INFO("drawctxt_id %d ptr %p\n", drawctxt_id, drawctxt); - if (drawctxt->flags != CTXT_FLAGS_NOT_IN_USE) { - /* deactivate context */ - if (device->drawctxt_active == drawctxt) { - /* no need to save GMEM or shader, the context is - * being destroyed. - */ - drawctxt->flags &= ~(CTXT_FLAGS_GMEM_SAVE | - CTXT_FLAGS_SHADER_SAVE | - CTXT_FLAGS_GMEM_SHADOW | - CTXT_FLAGS_STATE_SHADOW); - - kgsl_drawctxt_switch(device, NULL, 0); - } - - kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT); - - /* destroy state shadow, if allocated */ - if (drawctxt->gpustate.gpuaddr != 0) { - kgsl_mmu_unmap(drawctxt->pagetable, - drawctxt->gpustate.gpuaddr, - drawctxt->gpustate.size); - drawctxt->gpustate.gpuaddr = 0; - } - if (drawctxt->gpustate.physaddr != 0) - kgsl_sharedmem_free(&drawctxt->gpustate); - - /* destroy gmem shadow, if allocated */ - if (drawctxt->context_gmem_shadow.gmemshadow.gpuaddr != 0) { - kgsl_mmu_unmap(drawctxt->pagetable, - drawctxt->context_gmem_shadow.gmemshadow.gpuaddr, - drawctxt->context_gmem_shadow.gmemshadow.size); - drawctxt->context_gmem_shadow.gmemshadow.gpuaddr = 0; - } - - if (drawctxt->context_gmem_shadow.gmemshadow.physaddr != 0) - kgsl_sharedmem_free(&drawctxt->context_gmem_shadow. - gmemshadow); - - drawctxt->flags = CTXT_FLAGS_NOT_IN_USE; - - BUG_ON(device->drawctxt_count == 0); - device->drawctxt_count--; - } - KGSL_CTXT_INFO("return\n"); - return 0; -} - -/* Binds a user specified buffer as GMEM shadow area */ -int kgsl_drawctxt_bind_gmem_shadow(struct kgsl_device *device, - unsigned int drawctxt_id, - const struct kgsl_gmem_desc *gmem_desc, - unsigned int shadow_x, - unsigned int shadow_y, - const struct kgsl_buffer_desc - *shadow_buffer, unsigned int buffer_id) -{ - struct kgsl_drawctxt *drawctxt; - - /* Shadow struct being modified */ - struct gmem_shadow_t *shadow; - unsigned int i; - - if (device->flags & KGSL_FLAGS_SAFEMODE) - /* No need to bind any buffers since safe mode - * skips context switch */ - return 0; - - drawctxt = &device->drawctxt[drawctxt_id]; - - shadow = &drawctxt->user_gmem_shadow[buffer_id]; - - if (!shadow_buffer->enabled) { - /* Disable shadow */ - KGSL_MEM_ERR("shadow is disabled in bind_gmem\n"); - shadow->gmemshadow.size = 0; - } else { - /* Binding to a buffer */ - unsigned int width, height; - - BUG_ON(gmem_desc->x % 2); /* Needs to be a multiple of 2 */ - BUG_ON(gmem_desc->y % 2); /* Needs to be a multiple of 2 */ - BUG_ON(gmem_desc->width % 2); /* Needs to be a multiple of 2 */ - /* Needs to be a multiple of 2 */ - BUG_ON(gmem_desc->height % 2); - /* Needs to be a multiple of 32 */ - BUG_ON(gmem_desc->pitch % 32); - - BUG_ON(shadow_x % 2); /* Needs to be a multiple of 2 */ - BUG_ON(shadow_y % 2); /* Needs to be a multiple of 2 */ - - BUG_ON(shadow_buffer->format < COLORX_4_4_4_4); - BUG_ON(shadow_buffer->format > COLORX_32_32_32_32_FLOAT); - /* Needs to be a multiple of 32 */ - BUG_ON(shadow_buffer->pitch % 32); - - BUG_ON(buffer_id < 0); - BUG_ON(buffer_id > KGSL_MAX_GMEM_SHADOW_BUFFERS); - - width = gmem_desc->width; - height = gmem_desc->height; - - shadow->width = width; - shadow->format = shadow_buffer->format; - - shadow->height = height; - shadow->pitch = shadow_buffer->pitch; - - memset(&shadow->gmemshadow, 0, sizeof(struct kgsl_memdesc)); - shadow->gmemshadow.hostptr = shadow_buffer->hostptr; - shadow->gmemshadow.gpuaddr = shadow_buffer->gpuaddr; - shadow->gmemshadow.physaddr = shadow->gmemshadow.gpuaddr; - shadow->gmemshadow.size = shadow_buffer->size; - - /* Calculate offset */ - shadow->offset = - (int)(shadow_buffer->pitch) * ((int)shadow_y - - (int)gmem_desc->y) + - (int)shadow_x - (int)gmem_desc->x; - - shadow->offset_x = shadow_x; - shadow->offset_y = shadow_y; - shadow->gmem_offset_x = gmem_desc->x; - shadow->gmem_offset_y = gmem_desc->y; - - shadow->size = shadow->gmemshadow.size; - - shadow->gmem_pitch = gmem_desc->pitch; - - /* Modify quad vertices */ - set_gmem_copy_quad(shadow); - - /* Idle because we are reading PM override registers */ - kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT); - - /* Modify commands */ - build_gmem2sys_cmds(device, drawctxt, NULL, shadow); - build_sys2gmem_cmds(device, drawctxt, NULL, shadow); - - /* Release context GMEM shadow if found */ - if (drawctxt->context_gmem_shadow.gmemshadow.physaddr != 0) { - kgsl_sharedmem_free(&drawctxt->context_gmem_shadow. - gmemshadow); - drawctxt->context_gmem_shadow.gmemshadow.physaddr = 0; - } - } - - /* Enable GMEM shadowing if we have any of the user buffers enabled */ - drawctxt->flags &= ~CTXT_FLAGS_GMEM_SHADOW; - for (i = 0; i < KGSL_MAX_GMEM_SHADOW_BUFFERS; i++) { - if (drawctxt->user_gmem_shadow[i].gmemshadow.size > 0) - drawctxt->flags |= CTXT_FLAGS_GMEM_SHADOW; - } - - return 0; -} - -/* set bin base offset */ -int kgsl_drawctxt_set_bin_base_offset(struct kgsl_device *device, - unsigned int drawctxt_id, - unsigned int offset) -{ - struct kgsl_drawctxt *drawctxt; - - drawctxt = &device->drawctxt[drawctxt_id]; - - drawctxt->bin_base_offset = offset; - - return 0; -} - -/* switch drawing contexts */ -void -kgsl_drawctxt_switch(struct kgsl_device *device, struct kgsl_drawctxt *drawctxt, - unsigned int flags) -{ - struct kgsl_drawctxt *active_ctxt = device->drawctxt_active; - unsigned int cmds[2]; - - if (drawctxt) { - /* Set the flag in context so that the save is done - * when this context is switched out. */ - if (flags & KGSL_CONTEXT_SAVE_GMEM) - drawctxt->flags |= CTXT_FLAGS_GMEM_SAVE; - else - drawctxt->flags &= ~CTXT_FLAGS_GMEM_SAVE; - } - /* already current? */ - if (active_ctxt == drawctxt) - return; - - KGSL_CTXT_INFO("from %p to %p flags %d\n", - device->drawctxt_active, drawctxt, flags); - /* save old context*/ - if (active_ctxt != NULL) { - KGSL_CTXT_INFO("active_ctxt flags %08x\n", active_ctxt->flags); - /* save registers and constants. */ - KGSL_CTXT_DBG("save regs"); - kgsl_ringbuffer_issuecmds(device, 0, active_ctxt->reg_save, 3); - - if (active_ctxt->flags & CTXT_FLAGS_SHADER_SAVE) { - /* save shader partitioning and instructions. */ - KGSL_CTXT_DBG("save shader"); - kgsl_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, - active_ctxt->shader_save, 3); - - /* fixup shader partitioning parameter for - * SET_SHADER_BASES. - */ - KGSL_CTXT_DBG("save shader fixup"); - kgsl_ringbuffer_issuecmds(device, 0, - active_ctxt->shader_fixup, 3); - - active_ctxt->flags |= CTXT_FLAGS_SHADER_RESTORE; - } - - if (active_ctxt->flags & CTXT_FLAGS_GMEM_SAVE - && active_ctxt->flags & CTXT_FLAGS_GMEM_SHADOW) { - /* save gmem. - * (note: changes shader. shader must already be saved.) - */ - unsigned int i, numbuffers = 0; - KGSL_CTXT_DBG("save gmem"); - for (i = 0; i < KGSL_MAX_GMEM_SHADOW_BUFFERS; i++) { - if (active_ctxt->user_gmem_shadow[i].gmemshadow. - size > 0) { - kgsl_ringbuffer_issuecmds(device, - KGSL_CMD_FLAGS_PMODE, - active_ctxt->user_gmem_shadow[i]. - gmem_save, 3); - - /* Restore TP0_CHICKEN */ - kgsl_ringbuffer_issuecmds(device, 0, - active_ctxt->chicken_restore, 3); - - numbuffers++; - } - } - if (numbuffers == 0) { - kgsl_ringbuffer_issuecmds(device, - KGSL_CMD_FLAGS_PMODE, - active_ctxt->context_gmem_shadow.gmem_save, - 3); - - /* Restore TP0_CHICKEN */ - kgsl_ringbuffer_issuecmds(device, 0, - active_ctxt->chicken_restore, 3); - } - - active_ctxt->flags |= CTXT_FLAGS_GMEM_RESTORE; - } - } - - device->drawctxt_active = drawctxt; - - /* restore new context */ - if (drawctxt != NULL) { - - KGSL_CTXT_INFO("drawctxt flags %08x\n", drawctxt->flags); - KGSL_CTXT_DBG("restore pagetable"); - kgsl_mmu_setstate(device, drawctxt->pagetable); - - /* restore gmem. - * (note: changes shader. shader must not already be restored.) - */ - if (drawctxt->flags & CTXT_FLAGS_GMEM_RESTORE) { - unsigned int i, numbuffers = 0; - KGSL_CTXT_DBG("restore gmem"); - - for (i = 0; i < KGSL_MAX_GMEM_SHADOW_BUFFERS; i++) { - if (drawctxt->user_gmem_shadow[i].gmemshadow. - size > 0) { - kgsl_ringbuffer_issuecmds(device, - KGSL_CMD_FLAGS_PMODE, - drawctxt->user_gmem_shadow[i]. - gmem_restore, 3); - - /* Restore TP0_CHICKEN */ - kgsl_ringbuffer_issuecmds(device, 0, - drawctxt->chicken_restore, 3); - numbuffers++; - } - } - if (numbuffers == 0) { - kgsl_ringbuffer_issuecmds(device, - KGSL_CMD_FLAGS_PMODE, - drawctxt->context_gmem_shadow.gmem_restore, - 3); - - /* Restore TP0_CHICKEN */ - kgsl_ringbuffer_issuecmds(device, 0, - drawctxt->chicken_restore, 3); - } - drawctxt->flags &= ~CTXT_FLAGS_GMEM_RESTORE; - } - - /* restore registers and constants. */ - KGSL_CTXT_DBG("restore regs"); - kgsl_ringbuffer_issuecmds(device, 0, - drawctxt->reg_restore, 3); - - /* restore shader instructions & partitioning. */ - if (drawctxt->flags & CTXT_FLAGS_SHADER_RESTORE) - KGSL_CTXT_DBG("restore shader"); - kgsl_ringbuffer_issuecmds(device, 0, - drawctxt->shader_restore, 3); - - cmds[0] = pm4_type3_packet(PM4_SET_BIN_BASE_OFFSET, 1); - cmds[1] = drawctxt->bin_base_offset; - kgsl_ringbuffer_issuecmds(device, 0, cmds, 2); - - } else - kgsl_mmu_setstate(device, device->mmu.defaultpagetable); - KGSL_CTXT_INFO("return\n"); -} diff --git a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h b/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h deleted file mode 100644 index 3090373e30fe7..0000000000000 --- a/drivers/video/msm/gpu/kgsl/kgsl_drawctxt.h +++ /dev/null @@ -1,120 +0,0 @@ -/* -* (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 -* Copyright (c) 2008-2009 QUALCOMM USA, INC. -* -* All source code in this file is licensed under the following license -* -* 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, you can find it at http://www.fsf.org -*/ -#ifndef __GSL_DRAWCTXT_H -#define __GSL_DRAWCTXT_H - -/* Flags */ - -#define CTXT_FLAGS_NOT_IN_USE 0x00000000 -#define CTXT_FLAGS_IN_USE 0x00000001 - -/* state shadow memory allocated */ -#define CTXT_FLAGS_STATE_SHADOW 0x00000010 - -/* gmem shadow memory allocated */ -#define CTXT_FLAGS_GMEM_SHADOW 0x00000100 -/* gmem must be copied to shadow */ -#define CTXT_FLAGS_GMEM_SAVE 0x00000200 -/* gmem can be restored from shadow */ -#define CTXT_FLAGS_GMEM_RESTORE 0x00000400 -/* shader must be copied to shadow */ -#define CTXT_FLAGS_SHADER_SAVE 0x00002000 -/* shader can be restored from shadow */ -#define CTXT_FLAGS_SHADER_RESTORE 0x00004000 - -#include "kgsl_sharedmem.h" -#include "yamato_reg.h" - -#define KGSL_MAX_GMEM_SHADOW_BUFFERS 2 - -struct kgsl_device; - -/* types */ - -/* draw context */ -struct gmem_shadow_t { - struct kgsl_memdesc gmemshadow; /* Shadow buffer address */ - - /* 256 KB GMEM surface = 4 bytes-per-pixel x 256 pixels/row x - * 256 rows. */ - /* width & height must be a multiples of 32, in case tiled textures - * are used. */ - enum COLORFORMATX format; - unsigned int size; /* Size of surface used to store GMEM */ - unsigned int width; /* Width of surface used to store GMEM */ - unsigned int height; /* Height of surface used to store GMEM */ - unsigned int pitch; /* Pitch of surface used to store GMEM */ - int offset; - unsigned int offset_x; - unsigned int offset_y; - unsigned int gmem_offset_x; - unsigned int gmem_offset_y; - unsigned int gmem_pitch; /* Pitch value used for GMEM */ - unsigned int *gmem_save_commands; - unsigned int *gmem_restore_commands; - unsigned int gmem_save[3]; - unsigned int gmem_restore[3]; - struct kgsl_memdesc quad_vertices; - struct kgsl_memdesc quad_texcoords; -}; - -struct kgsl_drawctxt { - uint32_t flags; - struct kgsl_pagetable *pagetable; - struct kgsl_memdesc gpustate; - unsigned int reg_save[3]; - unsigned int reg_restore[3]; - unsigned int shader_save[3]; - unsigned int shader_fixup[3]; - unsigned int shader_restore[3]; - unsigned int chicken_restore[3]; - unsigned int bin_base_offset; - /* Information of the GMEM shadow that is created in context create */ - struct gmem_shadow_t context_gmem_shadow; - /* User defined GMEM shadow buffers */ - struct gmem_shadow_t user_gmem_shadow[KGSL_MAX_GMEM_SHADOW_BUFFERS]; -}; - - -int kgsl_drawctxt_create(struct kgsl_device *, struct kgsl_pagetable *, - unsigned int flags, - unsigned int *drawctxt_id); - -int kgsl_drawctxt_destroy(struct kgsl_device *device, unsigned int drawctxt_id); - -int kgsl_drawctxt_init(struct kgsl_device *device); - -int kgsl_drawctxt_close(struct kgsl_device *device); - -void kgsl_drawctxt_switch(struct kgsl_device *device, - struct kgsl_drawctxt *drawctxt, - unsigned int flags); -int kgsl_drawctxt_bind_gmem_shadow(struct kgsl_device *device, - unsigned int drawctxt_id, - const struct kgsl_gmem_desc *gmem_desc, - unsigned int shadow_x, - unsigned int shadow_y, - const struct kgsl_buffer_desc - *shadow_buffer, unsigned int buffer_id); - -int kgsl_drawctxt_set_bin_base_offset(struct kgsl_device *device, - unsigned int drawctxt_id, - unsigned int offset); - -#endif /* __GSL_DRAWCTXT_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_log.c b/drivers/video/msm/gpu/kgsl/kgsl_log.c deleted file mode 100644 index 6308087d87110..0000000000000 --- a/drivers/video/msm/gpu/kgsl/kgsl_log.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * (C) Copyright Advanced Micro Devices, Inc. 2002, 2008 - * Copyright (c) 2008-2009 QUALCOMM USA, INC. - * - * All source code in this file is licensed under the following license - * - * 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, you can find it at http://www.fsf.org - */ -#include -#include "kgsl_log.h" -#include "kgsl_ringbuffer.h" -#include "kgsl_device.h" -#include "kgsl.h" - -/*default log levels is error for everything*/ -#define KGSL_LOG_LEVEL_DEFAULT 3 -#define KGSL_LOG_LEVEL_MAX 7 -unsigned int kgsl_drv_log = KGSL_LOG_LEVEL_DEFAULT; -unsigned int kgsl_cmd_log = KGSL_LOG_LEVEL_DEFAULT; -unsigned int kgsl_ctxt_log = KGSL_LOG_LEVEL_DEFAULT; -unsigned int kgsl_mem_log = KGSL_LOG_LEVEL_DEFAULT; - -unsigned int kgsl_cache_enable; - -#ifdef CONFIG_DEBUG_FS -static int kgsl_log_set(unsigned int *log_val, void *data, u64 val) -{ - *log_val = min((unsigned int)val, (unsigned int)KGSL_LOG_LEVEL_MAX); - return 0; -} - -static int kgsl_drv_log_set(void *data, u64 val) -{ - return kgsl_log_set(&kgsl_drv_log, data, val); -} - -static int kgsl_drv_log_get(void *data, u64 *val) -{ - *val = kgsl_drv_log; - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(kgsl_drv_log_fops, kgsl_drv_log_get, - kgsl_drv_log_set, "%llu\n"); - -static int kgsl_cmd_log_set(void *data, u64 val) -{ - return kgsl_log_set(&kgsl_cmd_log, data, val); -} - -static int kgsl_cmd_log_get(void *data, u64 *val) -{ - *val = kgsl_cmd_log; - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(kgsl_cmd_log_fops, kgsl_cmd_log_get, - kgsl_cmd_log_set, "%llu\n"); - -static int kgsl_ctxt_log_set(void *data, u64 val) -{ - return kgsl_log_set(&kgsl_ctxt_log, data, val); -} - -static int kgsl_ctxt_log_get(void *data, u64 *val) -{ - *val = kgsl_ctxt_log; - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(kgsl_ctxt_log_fops, kgsl_ctxt_log_get, - kgsl_ctxt_log_set, "%llu\n"); - -static int kgsl_mem_log_set(void *data, u64 val) -{ - return kgsl_log_set(&kgsl_mem_log, data, val); -} - -static int kgsl_mem_log_get(void *data, u64 *val) -{ - *val = kgsl_mem_log; - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(kgsl_mem_log_fops, kgsl_mem_log_get, - kgsl_mem_log_set, "%llu\n"); - -#ifdef DEBUG -static ssize_t rb_regs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -static ssize_t rb_regs_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - const int debug_bufmax = 4096; - static char buffer[4096]; - int n = 0; - struct kgsl_device *device = NULL; - struct kgsl_ringbuffer *rb = NULL; - struct kgsl_rb_debug rb_debug; - - device = &kgsl_driver.yamato_device; - - rb = &device->ringbuffer; - - kgsl_ringbuffer_debug(rb, &rb_debug); - - n += scnprintf(buffer + n, debug_bufmax - n, - "rbbm_status %08x mem_rptr %08x mem_wptr_poll %08x\n", - rb_debug.rbbm_status, - rb_debug.mem_rptr, - rb_debug.mem_wptr_poll); - - n += scnprintf(buffer + n, debug_bufmax - n, - "rb_base %08x rb_cntl %08x rb_rptr_addr %08x" - " rb_rptr %08x rb_rptr_wr %08x\n", - rb_debug.cp_rb_base, - rb_debug.cp_rb_cntl, - rb_debug.cp_rb_rptr_addr, - rb_debug.cp_rb_rptr, - rb_debug.cp_rb_rptr_wr); - - n += scnprintf(buffer + n, debug_bufmax - n, - "rb_wptr %08x rb_wptr_delay %08x rb_wptr_base %08x" - " ib1_base %08x ib1_bufsz %08x\n", - rb_debug.cp_rb_wptr, - rb_debug.cp_rb_wptr_delay, - rb_debug.cp_rb_wptr_base, - rb_debug.cp_ib1_base, - rb_debug.cp_ib1_bufsz); - - n += scnprintf(buffer + n, debug_bufmax - n, - "ib2_base %08x ib2_bufsz %08x st_base %08x" - " st_bufsz %08x cp_me_cntl %08x cp_me_status %08x\n", - rb_debug.cp_ib2_base, - rb_debug.cp_ib2_bufsz, - rb_debug.cp_st_base, - rb_debug.cp_st_bufsz, - rb_debug.cp_me_cntl, - rb_debug.cp_me_status); - - n += scnprintf(buffer + n, debug_bufmax - n, - "csq_cp_rb %08x csq_cp_ib1 %08x csq_cp_ib2 %08x\n", - rb_debug.cp_csq_rb_stat, - rb_debug.cp_csq_ib1_stat, - rb_debug.cp_csq_ib2_stat); - - n += scnprintf(buffer + n, debug_bufmax - n, - "cp_debug %08x cp_stat %08x cp_int_status %08x" - " cp_int_cntl %08x\n", - rb_debug.cp_debug, - rb_debug.cp_stat, - rb_debug.cp_int_status, - rb_debug.cp_int_cntl); - - n += scnprintf(buffer + n, debug_bufmax - n, - "sop_timestamp: %0d eop_timestamp: %d\n", - rb_debug.sop_timestamp, - rb_debug.eop_timestamp); - n++; - buffer[n] = 0; - return simple_read_from_buffer(buf, count, ppos, buffer, n); -} - -static struct file_operations kgsl_rb_regs_fops = { - .read = rb_regs_read, - .open = rb_regs_open, -}; -#endif /*DEBUG*/ - -#ifdef DEBUG -static ssize_t mmu_regs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -static ssize_t mmu_regs_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - const int debug_bufmax = 4096; - static char buffer[4096]; - int n = 0; - struct kgsl_device *device = NULL; - struct kgsl_mmu *mmu = NULL; - struct kgsl_mmu_debug mmu_debug; - - device = &kgsl_driver.yamato_device; - - mmu = &device->mmu; - - kgsl_mmu_debug(mmu, &mmu_debug); - - n += scnprintf(buffer + n, debug_bufmax - n, - "config %08x mpu_base %08x mpu_end %08x\n", - mmu_debug.config, - mmu_debug.mpu_base, - mmu_debug.mpu_end); - - n += scnprintf(buffer + n, debug_bufmax - n, - "va_range %08x pt_base %08x\n", - mmu_debug.va_range, - mmu_debug.pt_base); - - n += scnprintf(buffer + n, debug_bufmax - n, - "page_fault %08x trans_error %08x axi_error %08x\n", - mmu_debug.page_fault, - mmu_debug.trans_error, - mmu_debug.axi_error); - - n += scnprintf(buffer + n, debug_bufmax - n, - "interrupt_mask %08x interrupt_status %08x\n", - mmu_debug.interrupt_mask, - mmu_debug.interrupt_status); - - n++; - buffer[n] = 0; - return simple_read_from_buffer(buf, count, ppos, buffer, n); -} - -static struct file_operations kgsl_mmu_regs_fops = { - .read = mmu_regs_read, - .open = mmu_regs_open, -}; -#endif /*DEBUG*/ - -#ifdef CONFIG_MSM_KGSL_MMU -static int kgsl_cache_enable_set(void *data, u64 val) -{ - kgsl_cache_enable = (val != 0); - return 0; -} - -static int kgsl_cache_enable_get(void *data, u64 *val) -{ - *val = kgsl_cache_enable; - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(kgsl_cache_enable_fops, kgsl_cache_enable_get, - kgsl_cache_enable_set, "%llu\n"); -#endif - -#endif /* CONFIG_DEBUG_FS */ - -int kgsl_debug_init(void) -{ -#ifdef CONFIG_DEBUG_FS - struct dentry *dent; - dent = debugfs_create_dir("kgsl", 0); - if (IS_ERR(dent)) - return 0; - - debugfs_create_file("log_level_cmd", 0644, dent, 0, - &kgsl_cmd_log_fops); - debugfs_create_file("log_level_ctxt", 0644, dent, 0, - &kgsl_ctxt_log_fops); - debugfs_create_file("log_level_drv", 0644, dent, 0, - &kgsl_drv_log_fops); - debugfs_create_file("log_level_mem", 0644, dent, 0, - &kgsl_mem_log_fops); -#ifdef DEBUG - debugfs_create_file("rb_regs", 0444, dent, 0, - &kgsl_rb_regs_fops); -#endif - -#ifdef DEBUG - debugfs_create_file("mmu_regs", 0444, dent, 0, - &kgsl_mmu_regs_fops); -#endif - -#ifdef CONFIG_MSM_KGSL_MMU - debugfs_create_file("cache_enable", 0644, dent, 0, - &kgsl_cache_enable_fops); -#endif - -#endif /* CONFIG_DEBUG_FS */ - return 0; -} diff --git a/drivers/video/msm/gpu/kgsl/kgsl_log.h b/drivers/video/msm/gpu/kgsl/kgsl_log.h deleted file mode 100644 index 3869ff74a732c..0000000000000 --- a/drivers/video/msm/gpu/kgsl/kgsl_log.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * (C) Copyright Advanced Micro Devices, Inc. 2002, 2008 - * Copyright (c) 2008-2009 QUALCOMM USA, INC. - * - * All source code in this file is licensed under the following license - * - * 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, you can find it at http://www.fsf.org - */ -#ifndef _GSL_LOG_H -#define _GSL_LOG_H - -#include -#include -#include -#include - -extern unsigned int kgsl_drv_log; -extern unsigned int kgsl_cmd_log; -extern unsigned int kgsl_ctxt_log; -extern unsigned int kgsl_mem_log; - -struct device *kgsl_driver_getdevnode(void); -int kgsl_debug_init(void); - -#define KGSL_LOG_VDBG(lvl, fmt, args...) \ - do { \ - if ((lvl) >= 7) \ - dev_vdbg(kgsl_driver_getdevnode(), "|%s| " fmt, \ - __func__, ##args);\ - } while (0) - -#define KGSL_LOG_DBG(lvl, fmt, args...) \ - do { \ - if ((lvl) >= 7) \ - dev_dbg(kgsl_driver_getdevnode(), "|%s| " fmt, \ - __func__, ##args);\ - } while (0) - -#define KGSL_LOG_INFO(lvl, fmt, args...) \ - do { \ - if ((lvl) >= 6) \ - dev_info(kgsl_driver_getdevnode(), "|%s| " fmt, \ - __func__, ##args);\ - } while (0) - -#define KGSL_LOG_WARN(lvl, fmt, args...) \ - do { \ - if ((lvl) >= 4) \ - dev_warn(kgsl_driver_getdevnode(), "|%s| " fmt, \ - __func__, ##args);\ - } while (0) - -#define KGSL_LOG_ERR(lvl, fmt, args...) \ - do { \ - if ((lvl) >= 3) \ - dev_err(kgsl_driver_getdevnode(), "|%s| " fmt, \ - __func__, ##args);\ - } while (0) - -#define KGSL_LOG_FATAL(lvl, fmt, args...) \ - do { \ - if ((lvl) >= 2) \ - dev_crit(kgsl_driver_getdevnode(), "|%s| " fmt, \ - __func__, ##args);\ - } while (0) - -#define KGSL_DRV_VDBG(fmt, args...) KGSL_LOG_VDBG(kgsl_drv_log, fmt, ##args) -#define KGSL_DRV_DBG(fmt, args...) KGSL_LOG_DBG(kgsl_drv_log, fmt, ##args) -#define KGSL_DRV_INFO(fmt, args...) KGSL_LOG_INFO(kgsl_drv_log, fmt, ##args) -#define KGSL_DRV_WARN(fmt, args...) KGSL_LOG_WARN(kgsl_drv_log, fmt, ##args) -#define KGSL_DRV_ERR(fmt, args...) KGSL_LOG_ERR(kgsl_drv_log, fmt, ##args) -#define KGSL_DRV_FATAL(fmt, args...) KGSL_LOG_FATAL(kgsl_drv_log, fmt, ##args) - -#define KGSL_CMD_VDBG(fmt, args...) KGSL_LOG_VDBG(kgsl_cmd_log, fmt, ##args) -#define KGSL_CMD_DBG(fmt, args...) KGSL_LOG_DBG(kgsl_cmd_log, fmt, ##args) -#define KGSL_CMD_INFO(fmt, args...) KGSL_LOG_INFO(kgsl_cmd_log, fmt, ##args) -#define KGSL_CMD_WARN(fmt, args...) KGSL_LOG_WARN(kgsl_cmd_log, fmt, ##args) -#define KGSL_CMD_ERR(fmt, args...) KGSL_LOG_ERR(kgsl_cmd_log, fmt, ##args) -#define KGSL_CMD_FATAL(fmt, args...) KGSL_LOG_FATAL(kgsl_cmd_log, fmt, ##args) - -#define KGSL_CTXT_VDBG(fmt, args...) KGSL_LOG_VDBG(kgsl_ctxt_log, fmt, ##args) -#define KGSL_CTXT_DBG(fmt, args...) KGSL_LOG_DBG(kgsl_ctxt_log, fmt, ##args) -#define KGSL_CTXT_INFO(fmt, args...) KGSL_LOG_INFO(kgsl_ctxt_log, fmt, ##args) -#define KGSL_CTXT_WARN(fmt, args...) KGSL_LOG_WARN(kgsl_ctxt_log, fmt, ##args) -#define KGSL_CTXT_ERR(fmt, args...) KGSL_LOG_ERR(kgsl_ctxt_log, fmt, ##args) -#define KGSL_CTXT_FATAL(fmt, args...) KGSL_LOG_FATAL(kgsl_ctxt_log, fmt, ##args) - -#define KGSL_MEM_VDBG(fmt, args...) KGSL_LOG_VDBG(kgsl_mem_log, fmt, ##args) -#define KGSL_MEM_DBG(fmt, args...) KGSL_LOG_DBG(kgsl_mem_log, fmt, ##args) -#define KGSL_MEM_INFO(fmt, args...) KGSL_LOG_INFO(kgsl_mem_log, fmt, ##args) -#define KGSL_MEM_WARN(fmt, args...) KGSL_LOG_WARN(kgsl_mem_log, fmt, ##args) -#define KGSL_MEM_ERR(fmt, args...) KGSL_LOG_ERR(kgsl_mem_log, fmt, ##args) -#define KGSL_MEM_FATAL(fmt, args...) KGSL_LOG_FATAL(kgsl_mem_log, fmt, ##args) - -#endif /* _GSL_LOG_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c b/drivers/video/msm/gpu/kgsl/kgsl_mmu.c deleted file mode 100644 index ebab5416d4e5c..0000000000000 --- a/drivers/video/msm/gpu/kgsl/kgsl_mmu.c +++ /dev/null @@ -1,672 +0,0 @@ -/* - * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 - * Copyright (c) 2008-2009 QUALCOMM USA, INC. - * - * All source code in this file is licensed under the following license - * - * 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, you can find it at http://www.fsf.org - */ -#include -#include -#include -#include - -#include -#include - -#include "kgsl_mmu.h" -#include "kgsl.h" -#include "kgsl_log.h" -#include "yamato_reg.h" - -struct kgsl_pte_debug { - unsigned int read:1; - unsigned int write:1; - unsigned int dirty:1; - unsigned int reserved:9; - unsigned int phyaddr:20; -}; - -#define GSL_PTE_SIZE 4 -#define GSL_PT_EXTRA_ENTRIES 16 - - -#define GSL_PT_PAGE_BITS_MASK 0x00000007 -#define GSL_PT_PAGE_ADDR_MASK (~(KGSL_PAGESIZE - 1)) - -#define GSL_MMU_INT_MASK \ - (MH_INTERRUPT_MASK__AXI_READ_ERROR | \ - MH_INTERRUPT_MASK__AXI_WRITE_ERROR) - -uint32_t kgsl_pt_entry_get(struct kgsl_pagetable *pt, uint32_t va) -{ - return (va - pt->va_base) >> KGSL_PAGESIZE_SHIFT; -} - -uint32_t kgsl_pt_map_get(struct kgsl_pagetable *pt, uint32_t pte) -{ - uint32_t *baseptr = (uint32_t *)pt->base.hostptr; - return baseptr[pte]; -} - -void kgsl_pt_map_set(struct kgsl_pagetable *pt, uint32_t pte, uint32_t val) -{ - uint32_t *baseptr = (uint32_t *)pt->base.hostptr; - baseptr[pte] = val; -} -#define GSL_PT_MAP_DEBUG(pte) ((struct kgsl_pte_debug *) \ - &gsl_pt_map_get(pagetable, pte)) - -void kgsl_pt_map_setbits(struct kgsl_pagetable *pt, uint32_t pte, uint32_t bits) -{ - uint32_t *baseptr = (uint32_t *)pt->base.hostptr; - baseptr[pte] |= bits; -} - -void kgsl_pt_map_setaddr(struct kgsl_pagetable *pt, uint32_t pte, - uint32_t pageaddr) -{ - uint32_t *baseptr = (uint32_t *)pt->base.hostptr; - uint32_t val = baseptr[pte]; - val &= ~GSL_PT_PAGE_ADDR_MASK; - val |= (pageaddr & GSL_PT_PAGE_ADDR_MASK); - baseptr[pte] = val; -} - -void kgsl_pt_map_resetall(struct kgsl_pagetable *pt, uint32_t pte) -{ - uint32_t *baseptr = (uint32_t *)pt->base.hostptr; - baseptr[pte] &= GSL_PT_PAGE_DIRTY; -} - -void kgsl_pt_map_resetbits(struct kgsl_pagetable *pt, uint32_t pte, - uint32_t bits) -{ - uint32_t *baseptr = (uint32_t *)pt->base.hostptr; - baseptr[pte] &= ~(bits & GSL_PT_PAGE_BITS_MASK); -} - -int kgsl_pt_map_isdirty(struct kgsl_pagetable *pt, uint32_t pte) -{ - uint32_t *baseptr = (uint32_t *)pt->base.hostptr; - return baseptr[pte] & GSL_PT_PAGE_DIRTY; -} - -uint32_t kgsl_pt_map_getaddr(struct kgsl_pagetable *pt, uint32_t pte) -{ - uint32_t *baseptr = (uint32_t *)pt->base.hostptr; - return baseptr[pte] & GSL_PT_PAGE_ADDR_MASK; -} - -void kgsl_mh_intrcallback(struct kgsl_device *device) -{ - unsigned int status = 0; - unsigned int reg; - unsigned int axi_error; - struct kgsl_mmu_debug dbg; - - KGSL_MEM_VDBG("enter (device=%p)\n", device); - - kgsl_yamato_regread(device, REG_MH_INTERRUPT_STATUS, &status); - - if (status & MH_INTERRUPT_MASK__AXI_READ_ERROR) { - kgsl_yamato_regread(device, REG_MH_AXI_ERROR, &axi_error); - KGSL_MEM_FATAL("axi read error interrupt (%08x)\n", axi_error); - kgsl_mmu_debug(&device->mmu, &dbg); - } else if (status & MH_INTERRUPT_MASK__AXI_WRITE_ERROR) { - kgsl_yamato_regread(device, REG_MH_AXI_ERROR, &axi_error); - KGSL_MEM_FATAL("axi write error interrupt (%08x)\n", axi_error); - kgsl_mmu_debug(&device->mmu, &dbg); - } else if (status & MH_INTERRUPT_MASK__MMU_PAGE_FAULT) { - kgsl_yamato_regread(device, REG_MH_MMU_PAGE_FAULT, ®); - KGSL_MEM_FATAL("mmu page fault interrupt: %08x\n", reg); - kgsl_mmu_debug(&device->mmu, &dbg); - } else { - KGSL_MEM_DBG("bad bits in REG_MH_INTERRUPT_STATUS %08x\n", - status); - } - - kgsl_yamato_regwrite(device, REG_MH_INTERRUPT_CLEAR, status); - - /*TODO: figure out how to handle errror interupts. - * specifically, page faults should probably nuke the client that - * caused them, but we don't have enough info to figure that out yet. - */ - - KGSL_MEM_VDBG("return\n"); -} - -#ifdef DEBUG -void kgsl_mmu_debug(struct kgsl_mmu *mmu, struct kgsl_mmu_debug *regs) -{ - memset(regs, 0, sizeof(struct kgsl_mmu_debug)); - - kgsl_yamato_regread(mmu->device, REG_MH_MMU_CONFIG, - ®s->config); - kgsl_yamato_regread(mmu->device, REG_MH_MMU_MPU_BASE, - ®s->mpu_base); - kgsl_yamato_regread(mmu->device, REG_MH_MMU_MPU_END, - ®s->mpu_end); - kgsl_yamato_regread(mmu->device, REG_MH_MMU_VA_RANGE, - ®s->va_range); - kgsl_yamato_regread(mmu->device, REG_MH_MMU_PT_BASE, - ®s->pt_base); - kgsl_yamato_regread(mmu->device, REG_MH_MMU_PAGE_FAULT, - ®s->page_fault); - kgsl_yamato_regread(mmu->device, REG_MH_MMU_TRAN_ERROR, - ®s->trans_error); - kgsl_yamato_regread(mmu->device, REG_MH_AXI_ERROR, - ®s->axi_error); - kgsl_yamato_regread(mmu->device, REG_MH_INTERRUPT_MASK, - ®s->interrupt_mask); - kgsl_yamato_regread(mmu->device, REG_MH_INTERRUPT_STATUS, - ®s->interrupt_status); - - KGSL_MEM_DBG("mmu config %08x mpu_base %08x mpu_end %08x\n", - regs->config, regs->mpu_base, regs->mpu_end); - KGSL_MEM_DBG("mmu va_range %08x pt_base %08x \n", - regs->va_range, regs->pt_base); - KGSL_MEM_DBG("mmu page_fault %08x tran_err %08x\n", - regs->page_fault, regs->trans_error); - KGSL_MEM_DBG("mmu int mask %08x int status %08x\n", - regs->interrupt_mask, regs->interrupt_status); -} -#endif - -struct kgsl_pagetable *kgsl_mmu_createpagetableobject(struct kgsl_mmu *mmu) -{ - int status = 0; - struct kgsl_pagetable *pagetable = NULL; - uint32_t flags; - - KGSL_MEM_VDBG("enter (mmu=%p)\n", mmu); - - pagetable = kzalloc(sizeof(struct kgsl_pagetable), GFP_KERNEL); - if (pagetable == NULL) { - KGSL_MEM_ERR("Unable to allocate pagetable object.\n"); - return NULL; - } - - pagetable->mmu = mmu; - pagetable->va_base = mmu->va_base; - pagetable->va_range = mmu->va_range; - pagetable->last_superpte = 0; - pagetable->max_entries = (mmu->va_range >> KGSL_PAGESIZE_SHIFT) - + GSL_PT_EXTRA_ENTRIES; - - pagetable->pool = gen_pool_create(KGSL_PAGESIZE_SHIFT, -1); - if (pagetable->pool == NULL) { - KGSL_MEM_ERR("Unable to allocate virtualaddr pool.\n"); - goto err_gen_pool_create; - } - - if (gen_pool_add(pagetable->pool, pagetable->va_base, - pagetable->va_range, -1)) { - KGSL_MEM_ERR("gen_pool_create failed for pagetable %p\n", - pagetable); - goto err_gen_pool_add; - } - - /* allocate page table memory */ - flags = (KGSL_MEMFLAGS_ALIGN4K | KGSL_MEMFLAGS_CONPHYS - | KGSL_MEMFLAGS_STRICTREQUEST); - status = kgsl_sharedmem_alloc(flags, - pagetable->max_entries * GSL_PTE_SIZE, - &pagetable->base); - - if (status) { - KGSL_MEM_ERR("cannot alloc page tables\n"); - goto err_kgsl_sharedmem_alloc; - } - - /* reset page table entries - * -- all pte's are marked as not dirty initially - */ - kgsl_sharedmem_set(&pagetable->base, 0, 0, pagetable->base.size); - pagetable->base.gpuaddr = pagetable->base.physaddr; - - KGSL_MEM_VDBG("return %p\n", pagetable); - - return pagetable; - -err_kgsl_sharedmem_alloc: -err_gen_pool_add: - gen_pool_destroy(pagetable->pool); -err_gen_pool_create: - kfree(pagetable); - return NULL; -} - -int kgsl_mmu_destroypagetableobject(struct kgsl_pagetable *pagetable) -{ - KGSL_MEM_VDBG("enter (pagetable=%p)\n", pagetable); - - if (pagetable) { - if (pagetable->base.gpuaddr) - kgsl_sharedmem_free(&pagetable->base); - - if (pagetable->pool) { - gen_pool_destroy(pagetable->pool); - pagetable->pool = NULL; - } - - kfree(pagetable); - - } - KGSL_MEM_VDBG("return 0x%08x\n", 0); - - return 0; -} - -int kgsl_mmu_setstate(struct kgsl_device *device, - struct kgsl_pagetable *pagetable) -{ - int status = 0; - struct kgsl_mmu *mmu = &device->mmu; - - KGSL_MEM_VDBG("enter (device=%p, pagetable=%p)\n", device, pagetable); - - if (mmu->flags & KGSL_FLAGS_STARTED) { - /* page table not current, then setup mmu to use new - * specified page table - */ - KGSL_MEM_INFO("from %p to %p\n", mmu->hwpagetable, pagetable); - if (mmu->hwpagetable != pagetable) { - mmu->hwpagetable = pagetable; - - /* call device specific set page table */ - status = kgsl_yamato_setstate(mmu->device, - KGSL_MMUFLAGS_TLBFLUSH | - KGSL_MMUFLAGS_PTUPDATE); - } - } - - KGSL_MEM_VDBG("return %d\n", status); - - return status; -} - -int kgsl_mmu_init(struct kgsl_device *device) -{ - /* - * intialize device mmu - * - * call this with the global lock held - */ - int status; - uint32_t flags; - struct kgsl_mmu *mmu = &device->mmu; -#ifdef _DEBUG - struct kgsl_mmu_debug regs; -#endif /* _DEBUG */ - - KGSL_MEM_VDBG("enter (device=%p)\n", device); - - if (mmu->flags & KGSL_FLAGS_INITIALIZED0) { - KGSL_MEM_INFO("MMU already initialized.\n"); - return 0; - } - - mmu->device = device; - -#ifndef CONFIG_MSM_KGSL_MMU - mmu->config = 0x00000000; -#endif - - /* setup MMU and sub-client behavior */ - kgsl_yamato_regwrite(device, REG_MH_MMU_CONFIG, mmu->config); - - /* enable axi interrupts */ - KGSL_MEM_DBG("enabling mmu interrupts mask=0x%08lx\n", - GSL_MMU_INT_MASK); - kgsl_yamato_regwrite(device, REG_MH_INTERRUPT_MASK, GSL_MMU_INT_MASK); - - mmu->flags |= KGSL_FLAGS_INITIALIZED0; - - /* MMU not enabled */ - if ((mmu->config & 0x1) == 0) { - KGSL_MEM_VDBG("return %d\n", 0); - return 0; - } - - /* idle device */ - kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT); - - /* make sure aligned to pagesize */ - BUG_ON(mmu->mpu_base & (KGSL_PAGESIZE - 1)); - BUG_ON((mmu->mpu_base + mmu->mpu_range) & (KGSL_PAGESIZE - 1)); - - /* define physical memory range accessible by the core */ - kgsl_yamato_regwrite(device, REG_MH_MMU_MPU_BASE, - mmu->mpu_base); - kgsl_yamato_regwrite(device, REG_MH_MMU_MPU_END, - mmu->mpu_base + mmu->mpu_range); - - /* enable axi interrupts */ - KGSL_MEM_DBG("enabling mmu interrupts mask=0x%08lx\n", - GSL_MMU_INT_MASK | MH_INTERRUPT_MASK__MMU_PAGE_FAULT); - kgsl_yamato_regwrite(device, REG_MH_INTERRUPT_MASK, - GSL_MMU_INT_MASK | MH_INTERRUPT_MASK__MMU_PAGE_FAULT); - - mmu->flags |= KGSL_FLAGS_INITIALIZED; - - /* sub-client MMU lookups require address translation */ - if ((mmu->config & ~0x1) > 0) { - /*make sure virtual address range is a multiple of 64Kb */ - BUG_ON(mmu->va_range & ((1 << 16) - 1)); - - /* allocate memory used for completing r/w operations that - * cannot be mapped by the MMU - */ - flags = (KGSL_MEMFLAGS_ALIGN4K | KGSL_MEMFLAGS_CONPHYS - | KGSL_MEMFLAGS_STRICTREQUEST); - status = kgsl_sharedmem_alloc(flags, 64, &mmu->dummyspace); - if (status != 0) { - KGSL_MEM_ERR - ("Unable to allocate dummy space memory.\n"); - kgsl_mmu_close(device); - return status; - } - - kgsl_sharedmem_set(&mmu->dummyspace, 0, 0, - mmu->dummyspace.size); - /* TRAN_ERROR needs a 32 byte (32 byte aligned) chunk of memory - * to complete transactions in case of an MMU fault. Note that - * we'll leave the bottom 32 bytes of the dummyspace for other - * purposes (e.g. use it when dummy read cycles are needed - * for other blocks */ - kgsl_yamato_regwrite(device, - REG_MH_MMU_TRAN_ERROR, - mmu->dummyspace.physaddr + 32); - - mmu->defaultpagetable = kgsl_mmu_createpagetableobject(mmu); - if (!mmu->defaultpagetable) { - KGSL_MEM_ERR("Failed to create global page table\n"); - kgsl_mmu_close(device); - return -ENOMEM; - } - mmu->hwpagetable = mmu->defaultpagetable; - mmu->tlbflushfilter.size = (mmu->va_range / - (PAGE_SIZE * GSL_PT_SUPER_PTE * 8)) + 1; - mmu->tlbflushfilter.base = (unsigned int *) - kzalloc(mmu->tlbflushfilter.size, GFP_KERNEL); - if (!mmu->tlbflushfilter.base) { - KGSL_MEM_ERR("Failed to create tlbflushfilter\n"); - kgsl_mmu_close(device); - return -ENOMEM; - } - GSL_TLBFLUSH_FILTER_RESET(); - kgsl_yamato_regwrite(device, REG_MH_MMU_PT_BASE, - mmu->hwpagetable->base.gpuaddr); - kgsl_yamato_regwrite(device, REG_MH_MMU_VA_RANGE, - (mmu->hwpagetable->va_base | - (mmu->hwpagetable->va_range >> 16))); - status = kgsl_yamato_setstate(device, KGSL_MMUFLAGS_TLBFLUSH); - if (status) { - kgsl_mmu_close(device); - return status; - } - - mmu->flags |= KGSL_FLAGS_STARTED; - } - - KGSL_MEM_VDBG("return %d\n", 0); - - return 0; -} - -#ifdef CONFIG_MSM_KGSL_MMU -pte_t *kgsl_get_pte_from_vaddr(unsigned int vaddr) -{ - pgd_t *pgd_ptr = NULL; - pmd_t *pmd_ptr = NULL; - pte_t *pte_ptr = NULL; - - pgd_ptr = pgd_offset(current->mm, vaddr); - if (pgd_none(*pgd) || pgd_bad(*pgd)) { - KGSL_MEM_ERR - ("Invalid pgd entry found while trying to convert virtual " - "address to physical\n"); - return 0; - } - - pmd_ptr = pmd_offset(pgd_ptr, vaddr); - if (pmd_none(*pmd_ptr) || pmd_bad(*pmd_ptr)) { - KGSL_MEM_ERR - ("Invalid pmd entry found while trying to convert virtual " - "address to physical\n"); - return 0; - } - - pte_ptr = pte_offset_map(pmd_ptr, vaddr); - if (!pte_ptr) { - KGSL_MEM_ERR - ("Unable to map pte entry while trying to convert virtual " - "address to physical\n"); - return 0; - } - return pte_ptr; -} - -int kgsl_mmu_map(struct kgsl_pagetable *pagetable, - unsigned int address, - int range, - unsigned int protflags, - unsigned int *gpuaddr, - unsigned int flags) -{ - int numpages; - unsigned int pte, ptefirst, ptelast, physaddr; - int flushtlb, alloc_size; - struct kgsl_mmu *mmu = NULL; - int phys_contiguous = flags & KGSL_MEMFLAGS_CONPHYS; - unsigned int align = flags & KGSL_MEMFLAGS_ALIGN_MASK; - - KGSL_MEM_VDBG("enter (pt=%p, physaddr=%08x, range=%08d, gpuaddr=%p)\n", - pagetable, address, range, gpuaddr); - - mmu = pagetable->mmu; - - BUG_ON(mmu == NULL); - BUG_ON(protflags & ~(GSL_PT_PAGE_RV | GSL_PT_PAGE_WV)); - BUG_ON(protflags == 0); - BUG_ON(range <= 0); - - /* Only support 4K and 8K alignment for now */ - if (align != KGSL_MEMFLAGS_ALIGN8K && align != KGSL_MEMFLAGS_ALIGN4K) { - KGSL_MEM_ERR("Cannot map memory according to " - "requested flags: %08x\n", flags); - return -EINVAL; - } - - /* Make sure address being mapped is at 4K boundary */ - if (!IS_ALIGNED(address, KGSL_PAGESIZE) || range & ~KGSL_PAGEMASK) { - KGSL_MEM_ERR("Cannot map address not aligned " - "at page boundary: address: %08x, range: %08x\n", - address, range); - return -EINVAL; - } - alloc_size = range; - if (align == KGSL_MEMFLAGS_ALIGN8K) - alloc_size += KGSL_PAGESIZE; - - *gpuaddr = gen_pool_alloc(pagetable->pool, alloc_size); - if (*gpuaddr == 0) { - KGSL_MEM_ERR("gen_pool_alloc failed: %d\n", alloc_size); - return -ENOMEM; - } - - if (align == KGSL_MEMFLAGS_ALIGN8K) { - if (*gpuaddr & ((1 << 13) - 1)) { - /* Not 8k aligned, align it */ - gen_pool_free(pagetable->pool, *gpuaddr, KGSL_PAGESIZE); - *gpuaddr = *gpuaddr + KGSL_PAGESIZE; - } else - gen_pool_free(pagetable->pool, *gpuaddr + range, - KGSL_PAGESIZE); - } - - numpages = (range >> KGSL_PAGESIZE_SHIFT); - - ptefirst = kgsl_pt_entry_get(pagetable, *gpuaddr); - ptelast = ptefirst + numpages; - - pte = ptefirst; - flushtlb = 0; - - /* tlb needs to be flushed when the first and last pte are not at - * superpte boundaries */ - if ((ptefirst & (GSL_PT_SUPER_PTE - 1)) != 0 || - ((ptelast + 1) & (GSL_PT_SUPER_PTE-1)) != 0) - flushtlb = 1; - - for (pte = ptefirst; pte < ptelast; pte++) { -#ifdef VERBOSE_DEBUG - /* check if PTE exists */ - uint32_t val = kgsl_pt_map_getaddr(pagetable, pte); - BUG_ON(val != 0 && val != GSL_PT_PAGE_DIRTY); -#endif - if ((pte & (GSL_PT_SUPER_PTE-1)) == 0) - if (GSL_TLBFLUSH_FILTER_ISDIRTY(pte / GSL_PT_SUPER_PTE)) - flushtlb = 1; - - /* mark pte as in use */ - if (phys_contiguous) - physaddr = address; - else { - physaddr = vmalloc_to_pfn((void *)address); - physaddr <<= PAGE_SHIFT; - } - - if (physaddr) - kgsl_pt_map_set(pagetable, pte, physaddr | protflags); - else { - KGSL_MEM_ERR - ("Unable to find physaddr for vmallloc address: %x\n", - address); - kgsl_mmu_unmap(pagetable, *gpuaddr, range); - return -EFAULT; - } - address += KGSL_PAGESIZE; - } - - KGSL_MEM_INFO("pt %p p %08x g %08x pte f %d l %d n %d f %d\n", - pagetable, address, *gpuaddr, ptefirst, ptelast, - numpages, flushtlb); - - dmb(); - - /* Invalidate tlb only if current page table used by GPU is the - * pagetable that we used to allocate */ - if (flushtlb && (pagetable == mmu->hwpagetable)) { - kgsl_yamato_setstate(mmu->device, KGSL_MMUFLAGS_TLBFLUSH); - GSL_TLBFLUSH_FILTER_RESET(); - } - - - KGSL_MEM_VDBG("return %d\n", 0); - - return 0; -} - -int -kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, unsigned int gpuaddr, - int range) -{ - unsigned int numpages; - unsigned int pte, ptefirst, ptelast, superpte; - struct kgsl_mmu *mmu = NULL; - - KGSL_MEM_VDBG("enter (pt=%p, gpuaddr=0x%08x, range=%d)\n", - pagetable, gpuaddr, range); - - BUG_ON(range <= 0); - - numpages = (range >> KGSL_PAGESIZE_SHIFT); - if (range & (KGSL_PAGESIZE - 1)) - numpages++; - - ptefirst = kgsl_pt_entry_get(pagetable, gpuaddr); - ptelast = ptefirst + numpages; - - KGSL_MEM_INFO("pt %p gpu %08x pte first %d last %d numpages %d\n", - pagetable, gpuaddr, ptefirst, ptelast, numpages); - - mmu = pagetable->mmu; - - superpte = ptefirst - (ptefirst & (GSL_PT_SUPER_PTE-1)); - GSL_TLBFLUSH_FILTER_SETDIRTY(superpte / GSL_PT_SUPER_PTE); - for (pte = ptefirst; pte < ptelast; pte++) { -#ifdef VERBOSE_DEBUG - /* check if PTE exists */ - BUG_ON(!kgsl_pt_map_getaddr(pagetable, pte)); -#endif - kgsl_pt_map_set(pagetable, pte, GSL_PT_PAGE_DIRTY); - superpte = pte - (pte & (GSL_PT_SUPER_PTE - 1)); - if (pte == superpte) - GSL_TLBFLUSH_FILTER_SETDIRTY(superpte / - GSL_PT_SUPER_PTE); - } - - dmb(); - - gen_pool_free(pagetable->pool, gpuaddr, range); - - KGSL_MEM_VDBG("return %d\n", 0); - - return 0; -} -#endif - -int kgsl_mmu_close(struct kgsl_device *device) -{ - /* - * close device mmu - * - * call this with the global lock held - */ - struct kgsl_mmu *mmu = &device->mmu; -#ifdef _DEBUG - int i; -#endif /* _DEBUG */ - - KGSL_MEM_VDBG("enter (device=%p)\n", device); - - if (mmu->flags & KGSL_FLAGS_INITIALIZED0) { - /* disable mh interrupts */ - KGSL_MEM_DBG("disabling mmu interrupts\n"); - kgsl_yamato_regwrite(device, REG_MH_INTERRUPT_MASK, 0); - - /* disable MMU */ - kgsl_yamato_regwrite(device, REG_MH_MMU_CONFIG, 0x00000000); - - if (mmu->dummyspace.gpuaddr) - kgsl_sharedmem_free(&mmu->dummyspace); - - if (mmu->tlbflushfilter.base) { - mmu->tlbflushfilter.size = 0; - kfree(mmu->tlbflushfilter.base); - mmu->tlbflushfilter.base = NULL; - } - - mmu->flags &= ~KGSL_FLAGS_STARTED; - mmu->flags &= ~KGSL_FLAGS_INITIALIZED; - mmu->flags &= ~KGSL_FLAGS_INITIALIZED0; - kgsl_mmu_destroypagetableobject(mmu->defaultpagetable); - mmu->defaultpagetable = NULL; - } - - KGSL_MEM_VDBG("return %d\n", 0); - - return 0; -} diff --git a/drivers/video/msm/gpu/kgsl/kgsl_mmu.h b/drivers/video/msm/gpu/kgsl/kgsl_mmu.h deleted file mode 100644 index 6074518756ffe..0000000000000 --- a/drivers/video/msm/gpu/kgsl/kgsl_mmu.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 - * Copyright (c) 2008-2009 QUALCOMM USA, INC. - * - * All source code in this file is licensed under the following license - * - * 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, you can find it at http://www.fsf.org - */ -#ifndef __GSL_MMU_H -#define __GSL_MMU_H -#include -#include -#include "kgsl_log.h" -#include "kgsl_sharedmem.h" - -#define GSL_PT_SUPER_PTE 8 -#define GSL_PT_PAGE_WV 0x00000001 -#define GSL_PT_PAGE_RV 0x00000002 -#define GSL_PT_PAGE_DIRTY 0x00000004 -/* MMU Flags */ -#define KGSL_MMUFLAGS_TLBFLUSH 0x10000000 -#define KGSL_MMUFLAGS_PTUPDATE 0x20000000 - -/* Macros to manage TLB flushing */ -#define GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS (sizeof(unsigned char) * 8) -#define GSL_TLBFLUSH_FILTER_GET(superpte) \ - (*((unsigned char *) \ - (((unsigned int)mmu->tlbflushfilter.base) \ - + (superpte / GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS)))) -#define GSL_TLBFLUSH_FILTER_SETDIRTY(superpte) \ - (GSL_TLBFLUSH_FILTER_GET((superpte)) |= 1 << \ - (superpte % GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS)) -#define GSL_TLBFLUSH_FILTER_ISDIRTY(superpte) \ - (GSL_TLBFLUSH_FILTER_GET((superpte)) & \ - (1 << (superpte % GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS))) -#define GSL_TLBFLUSH_FILTER_RESET() memset(mmu->tlbflushfilter.base,\ - 0, mmu->tlbflushfilter.size) - -extern unsigned int kgsl_cache_enable; - -struct kgsl_device; - -struct kgsl_mmu_debug { - unsigned int config; - unsigned int mpu_base; - unsigned int mpu_end; - unsigned int va_range; - unsigned int pt_base; - unsigned int page_fault; - unsigned int trans_error; - unsigned int axi_error; - unsigned int interrupt_mask; - unsigned int interrupt_status; -}; - -struct kgsl_ptstats { - int64_t maps; - int64_t unmaps; - int64_t superpteallocs; - int64_t superptefrees; - int64_t ptswitches; - int64_t tlbflushes[KGSL_DEVICE_MAX]; -}; - -struct kgsl_pagetable { - unsigned int refcnt; - struct kgsl_mmu *mmu; - struct kgsl_memdesc base; - uint32_t va_base; - unsigned int va_range; - unsigned int last_superpte; - unsigned int max_entries; - struct gen_pool *pool; -}; - -struct kgsl_tlbflushfilter { - unsigned int *base; - unsigned int size; -}; - -struct kgsl_mmu { - unsigned int refcnt; - uint32_t flags; - struct kgsl_device *device; - unsigned int config; - uint32_t mpu_base; - int mpu_range; - uint32_t va_base; - unsigned int va_range; - struct kgsl_memdesc dummyspace; - /* current page table object being used by device mmu */ - struct kgsl_pagetable *defaultpagetable; - struct kgsl_pagetable *hwpagetable; - /* Maintain filter to manage tlb flushing */ - struct kgsl_tlbflushfilter tlbflushfilter; -}; - - -static inline int -kgsl_mmu_isenabled(struct kgsl_mmu *mmu) -{ - return ((mmu)->flags & KGSL_FLAGS_STARTED) ? 1 : 0; -} - - -int kgsl_mmu_init(struct kgsl_device *device); - -int kgsl_mmu_close(struct kgsl_device *device); - -struct kgsl_pagetable *kgsl_mmu_createpagetableobject(struct kgsl_mmu *mmu); - -int kgsl_mmu_destroypagetableobject(struct kgsl_pagetable *pagetable); - -int kgsl_mmu_setstate(struct kgsl_device *device, - struct kgsl_pagetable *pagetable); - -#ifdef CONFIG_MSM_KGSL_MMU -int kgsl_mmu_map(struct kgsl_pagetable *pagetable, - unsigned int address, - int range, - unsigned int protflags, - unsigned int *gpuaddr, - unsigned int flags); - -int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, - unsigned int gpuaddr, int range); - -pte_t *kgsl_get_pte_from_vaddr(unsigned int vaddr); -#else -static inline int kgsl_mmu_map(struct kgsl_pagetable *pagetable, - unsigned int address, - int range, - unsigned int protflags, - unsigned int *gpuaddr, - unsigned int flags) -{ - *gpuaddr = address; - return 0; -} - -static inline int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, - unsigned int gpuaddr, int range) { return 0; } - -static inline pte_t *kgsl_get_pte_from_vaddr(unsigned int vaddr) {return NULL;} -#endif - -int kgsl_mmu_querystats(struct kgsl_pagetable *pagetable, - struct kgsl_ptstats *stats); - -void kgsl_mh_intrcallback(struct kgsl_device *device); - -#ifdef DEBUG -void kgsl_mmu_debug(struct kgsl_mmu *, struct kgsl_mmu_debug*); -#else -static inline void kgsl_mmu_debug(struct kgsl_mmu *mmu, - struct kgsl_mmu_debug *mmu_debug) { } -#endif /* DEBUG */ - -#endif /* __GSL_MMU_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_pm4types.h b/drivers/video/msm/gpu/kgsl/kgsl_pm4types.h deleted file mode 100644 index bc224b410ff57..0000000000000 --- a/drivers/video/msm/gpu/kgsl/kgsl_pm4types.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 - * Copyright (c) 2008-2009 QUALCOMM USA, INC. - * - * All source code in this file is licensed under the following license - * - * 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, you can find it at http://www.fsf.org - */ -#ifndef __GSL_PM4TYPES_H -#define __GSL_PM4TYPES_H - - -#define PM4_PKT_MASK 0xc0000000 - -#define PM4_TYPE0_PKT ((unsigned int)0 << 30) -#define PM4_TYPE1_PKT ((unsigned int)1 << 30) -#define PM4_TYPE2_PKT ((unsigned int)2 << 30) -#define PM4_TYPE3_PKT ((unsigned int)3 << 30) - - -/* type3 packets */ -/* initialize CP's micro-engine */ -#define PM4_ME_INIT 0x48 - -/* skip N 32-bit words to get to the next packet */ -#define PM4_NOP 0x10 - -/* indirect buffer dispatch. prefetch parser uses this packet type to determine -* whether to pre-fetch the IB -*/ -#define PM4_INDIRECT_BUFFER 0x3f - -/* indirect buffer dispatch. same as IB, but init is pipelined */ -#define PM4_INDIRECT_BUFFER_PFD 0x37 - -/* wait for the IDLE state of the engine */ -#define PM4_WAIT_FOR_IDLE 0x26 - -/* wait until a register or memory location is a specific value */ -#define PM4_WAIT_REG_MEM 0x3c - -/* wait until a register location is equal to a specific value */ -#define PM4_WAIT_REG_EQ 0x52 - -/* wait until a register location is >= a specific value */ -#define PM4_WAT_REG_GTE 0x53 - -/* wait until a read completes */ -#define PM4_WAIT_UNTIL_READ 0x5c - -/* wait until all base/size writes from an IB_PFD packet have completed */ -#define PM4_WAIT_IB_PFD_COMPLETE 0x5d - -/* register read/modify/write */ -#define PM4_REG_RMW 0x21 - -/* reads register in chip and writes to memory */ -#define PM4_REG_TO_MEM 0x3e - -/* write N 32-bit words to memory */ -#define PM4_MEM_WRITE 0x3d - -/* write CP_PROG_COUNTER value to memory */ -#define PM4_MEM_WRITE_CNTR 0x4f - -/* conditional execution of a sequence of packets */ -#define PM4_COND_EXEC 0x44 - -/* conditional write to memory or register */ -#define PM4_COND_WRITE 0x45 - -/* generate an event that creates a write to memory when completed */ -#define PM4_EVENT_WRITE 0x46 - -/* generate a VS|PS_done event */ -#define PM4_EVENT_WRITE_SHD 0x58 - -/* generate a cache flush done event */ -#define PM4_EVENT_WRITE_CFL 0x59 - -/* generate a z_pass done event */ -#define PM4_EVENT_WRITE_ZPD 0x5b - - -/* initiate fetch of index buffer and draw */ -#define PM4_DRAW_INDX 0x22 - -/* draw using supplied indices in packet */ -#define PM4_DRAW_INDX_2 0x36 - -/* initiate fetch of index buffer and binIDs and draw */ -#define PM4_DRAW_INDX_BIN 0x34 - -/* initiate fetch of bin IDs and draw using supplied indices */ -#define PM4_DRAW_INDX_2_BIN 0x35 - - -/* begin/end initiator for viz query extent processing */ -#define PM4_VIZ_QUERY 0x23 - -/* fetch state sub-blocks and initiate shader code DMAs */ -#define PM4_SET_STATE 0x25 - -/* load constant into chip and to memory */ -#define PM4_SET_CONSTANT 0x2d - -/* load sequencer instruction memory (pointer-based) */ -#define PM4_IM_LOAD 0x27 - -/* load sequencer instruction memory (code embedded in packet) */ -#define PM4_IM_LOAD_IMMEDIATE 0x2b - -/* load constants from a location in memory */ -#define PM4_LOAD_CONSTANT_CONTEXT 0x2e - -/* selective invalidation of state pointers */ -#define PM4_INVALIDATE_STATE 0x3b - - -/* dynamically changes shader instruction memory partition */ -#define PM4_SET_SHADER_BASES 0x4A - -/* sets the 64-bit BIN_MASK register in the PFP */ -#define PM4_SET_BIN_MASK 0x50 - -/* sets the 64-bit BIN_SELECT register in the PFP */ -#define PM4_SET_BIN_SELECT 0x51 - - -/* updates the current context, if needed */ -#define PM4_CONTEXT_UPDATE 0x5e - -/* generate interrupt from the command stream */ -#define PM4_INTERRUPT 0x40 - - -/* copy sequencer instruction memory to system memory */ -#define PM4_IM_STORE 0x2c - -/* program an offset that will added to the BIN_BASE value of - * the 3D_DRAW_INDX_BIN packet */ -#define PM4_SET_BIN_BASE_OFFSET 0x4B - -#define PM4_SET_PROTECTED_MODE 0x5f /* sets the register protection mode */ - -/* packet header building macros */ -#define pm4_type0_packet(regindx, cnt) \ - (PM4_TYPE0_PKT | (((cnt)-1) << 16) | ((regindx) & 0x7FFF)) - -#define pm4_type0_packet_for_sameregister(regindx, cnt) \ - ((PM4_TYPE0_PKT | (((cnt)-1) << 16) | ((1 << 15) | \ - ((regindx) & 0x7FFF))) - -#define pm4_type1_packet(reg0, reg1) \ - (PM4_TYPE1_PKT | ((reg1) << 12) | (reg0)) - -#define pm4_type3_packet(opcode, cnt) \ - (PM4_TYPE3_PKT | (((cnt)-1) << 16) | (((opcode) & 0xFF) << 8)) - -#define pm4_predicated_type3_packet(opcode, cnt) \ - (PM4_TYPE3_PKT | (((cnt)-1) << 16) | (((opcode) & 0xFF) << 8) | 0x1) - -#define pm4_nop_packet(cnt) \ - (PM4_TYPE3_PKT | (((cnt)-1) << 16) | (PM4_NOP << 8)) - - -/* packet headers */ -#define PM4_HDR_ME_INIT pm4_type3_packet(PM4_ME_INIT, 18) -#define PM4_HDR_INDIRECT_BUFFER_PFD pm4_type3_packet(PM4_INDIRECT_BUFFER_PFD, 2) -#define PM4_HDR_INDIRECT_BUFFER pm4_type3_packet(PM4_INDIRECT_BUFFER, 2) - -#endif /* __GSL_PM4TYPES_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c deleted file mode 100644 index 472d10c154f9f..0000000000000 --- a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.c +++ /dev/null @@ -1,837 +0,0 @@ -/* - * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 - * Copyright (c) 2008-2009 QUALCOMM USA, INC. - * - * All source code in this file is licensed under the following license - * - * 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, you can find it at http://www.fsf.org - */ -#include -#include -#include -#include - -#include "kgsl.h" -#include "kgsl_device.h" -#include "kgsl_log.h" -#include "kgsl_pm4types.h" -#include "kgsl_ringbuffer.h" -#include "kgsl_cmdstream.h" - -#include "yamato_reg.h" - -#define GSL_RB_NOP_SIZEDWORDS 2 -/* protected mode error checking below register address 0x800 -* note: if CP_INTERRUPT packet is used then checking needs -* to change to below register address 0x7C8 -*/ -#define GSL_RB_PROTECTED_MODE_CONTROL 0x200001F2 - -#define GSL_CP_INT_MASK \ - (CP_INT_CNTL__SW_INT_MASK | \ - CP_INT_CNTL__T0_PACKET_IN_IB_MASK | \ - CP_INT_CNTL__OPCODE_ERROR_MASK | \ - CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK | \ - CP_INT_CNTL__RESERVED_BIT_ERROR_MASK | \ - CP_INT_CNTL__IB_ERROR_MASK | \ - CP_INT_CNTL__IB2_INT_MASK | \ - CP_INT_CNTL__IB1_INT_MASK | \ - CP_INT_CNTL__RB_INT_MASK) - -#define YAMATO_PFP_FW "yamato_pfp.fw" -#define YAMATO_PM4_FW "yamato_pm4.fw" - -/* ringbuffer size log2 quadwords equivalent */ -inline unsigned int kgsl_ringbuffer_sizelog2quadwords(unsigned int sizedwords) -{ - unsigned int sizelog2quadwords = 0; - int i = sizedwords >> 1; - - while (i >>= 1) - sizelog2quadwords++; - - return sizelog2quadwords; -} - - -/* functions */ -void kgsl_cp_intrcallback(struct kgsl_device *device) -{ - unsigned int status = 0; - struct kgsl_ringbuffer *rb = &device->ringbuffer; - - KGSL_CMD_VDBG("enter (device=%p)\n", device); - - kgsl_yamato_regread(device, REG_CP_INT_STATUS, &status); - - if (status & CP_INT_CNTL__RB_INT_MASK) { - /* signal intr completion event */ - int init_reftimestamp = 0x7fffffff; - int enableflag = 0; - kgsl_sharedmem_write(&rb->device->memstore, - KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable), - &enableflag, 4); - kgsl_sharedmem_write(&rb->device->memstore, - KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), - &init_reftimestamp, 4); - KGSL_CMD_WARN("ringbuffer rb interrupt\n"); - } - - if (status & (CP_INT_CNTL__IB1_INT_MASK | CP_INT_CNTL__RB_INT_MASK)) { - KGSL_CMD_WARN("ringbuffer ib1/rb interrupt\n"); - wake_up_interruptible_all(&device->ib1_wq); - } - if (status & CP_INT_CNTL__T0_PACKET_IN_IB_MASK) { - KGSL_CMD_FATAL("ringbuffer TO packet in IB interrupt\n"); - kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0); - kgsl_ringbuffer_dump(rb); - } - if (status & CP_INT_CNTL__OPCODE_ERROR_MASK) { - KGSL_CMD_FATAL("ringbuffer opcode error interrupt\n"); - kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0); - kgsl_ringbuffer_dump(rb); - } - if (status & CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK) { - KGSL_CMD_FATAL("ringbuffer protected mode error interrupt\n"); - kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0); - kgsl_ringbuffer_dump(rb); - } - if (status & CP_INT_CNTL__RESERVED_BIT_ERROR_MASK) { - KGSL_CMD_FATAL("ringbuffer reserved bit error interrupt\n"); - kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0); - kgsl_ringbuffer_dump(rb); - } - if (status & CP_INT_CNTL__IB_ERROR_MASK) { - KGSL_CMD_FATAL("ringbuffer IB error interrupt\n"); - kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0); - kgsl_ringbuffer_dump(rb); - } - if (status & CP_INT_CNTL__SW_INT_MASK) - KGSL_CMD_DBG("ringbuffer software interrupt\n"); - - if (status & CP_INT_CNTL__IB2_INT_MASK) - KGSL_CMD_DBG("ringbuffer ib2 interrupt\n"); - - if (status & (~GSL_CP_INT_MASK)) - KGSL_CMD_DBG("bad bits in REG_CP_INT_STATUS %08x\n", status); - - /* only ack bits we understand */ - status &= GSL_CP_INT_MASK; - kgsl_yamato_regwrite(device, REG_CP_INT_ACK, status); - - KGSL_CMD_VDBG("return\n"); -} - - -void kgsl_ringbuffer_watchdog() -{ - struct kgsl_device *device = NULL; - struct kgsl_ringbuffer *rb = NULL; - - device = &kgsl_driver.yamato_device; - - BUG_ON(device == NULL); - - rb = &device->ringbuffer; - - KGSL_CMD_VDBG("enter\n"); - - if ((rb->flags & KGSL_FLAGS_STARTED) == 0) { - KGSL_CMD_VDBG("not started\n"); - return; - } - - GSL_RB_GET_READPTR(rb, &rb->rptr); - - if (rb->rptr == rb->wptr) { - /* clear rptr sample for interval n */ - rb->watchdog.flags &= ~KGSL_FLAGS_ACTIVE; - goto done; - } - /* ringbuffer is currently not empty */ - /* and a rptr sample was taken during interval n-1 */ - if (rb->watchdog.flags & KGSL_FLAGS_ACTIVE) { - /* and the rptr did not advance between - * interval n-1 and n */ - if (rb->rptr == rb->watchdog.rptr_sample) { - /* then the core has hung */ - KGSL_CMD_FATAL("Watchdog detected core hung.\n"); - goto done; - } - /* save rptr sample for interval n */ - rb->watchdog.flags |= KGSL_FLAGS_ACTIVE; - rb->watchdog.rptr_sample = rb->rptr; - } -done: - KGSL_CMD_VDBG("return\n"); -} - -static void kgsl_ringbuffer_submit(struct kgsl_ringbuffer *rb) -{ - BUG_ON(rb->wptr == 0); - - GSL_RB_UPDATE_WPTR_POLLING(rb); - /* Drain write buffer and data memory barrier */ - dsb(); - dmb(); - - /* Memory fence to ensure all data has posted. On some systems, - * like 7x27, the register block is not allocated as strongly ordered - * memory. Adding a memory fence ensures ordering during ringbuffer - * submits.*/ - mb(); - - kgsl_yamato_regwrite(rb->device, REG_CP_RB_WPTR, rb->wptr); - - rb->flags |= KGSL_FLAGS_ACTIVE; -} - -static int -kgsl_ringbuffer_waitspace(struct kgsl_ringbuffer *rb, unsigned int numcmds, - int wptr_ahead) -{ - int nopcount; - unsigned int freecmds; - unsigned int *cmds; - - KGSL_CMD_VDBG("enter (rb=%p, numcmds=%d, wptr_ahead=%d)\n", - rb, numcmds, wptr_ahead); - - /* if wptr ahead, fill the remaining with NOPs */ - if (wptr_ahead) { - /* -1 for header */ - nopcount = rb->sizedwords - rb->wptr - 1; - - cmds = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr; - GSL_RB_WRITE(cmds, pm4_nop_packet(nopcount)); - rb->wptr++; - - kgsl_ringbuffer_submit(rb); - - rb->wptr = 0; - } - - /* wait for space in ringbuffer */ - do { - GSL_RB_GET_READPTR(rb, &rb->rptr); - - freecmds = rb->rptr - rb->wptr; - - } while ((freecmds != 0) && (freecmds < numcmds)); - - KGSL_CMD_VDBG("return %d\n", 0); - - return 0; -} - - -static unsigned int *kgsl_ringbuffer_allocspace(struct kgsl_ringbuffer *rb, - unsigned int numcmds) -{ - unsigned int *ptr = NULL; - int status = 0; - - BUG_ON(numcmds >= rb->sizedwords); - - /* check for available space */ - if (rb->wptr >= rb->rptr) { - /* wptr ahead or equal to rptr */ - /* reserve dwords for nop packet */ - if ((rb->wptr + numcmds) > (rb->sizedwords - - GSL_RB_NOP_SIZEDWORDS)) - status = kgsl_ringbuffer_waitspace(rb, numcmds, 1); - } else { - /* wptr behind rptr */ - if ((rb->wptr + numcmds) >= rb->rptr) - status = kgsl_ringbuffer_waitspace(rb, numcmds, 0); - /* check for remaining space */ - /* reserve dwords for nop packet */ - if ((rb->wptr + numcmds) > (rb->sizedwords - - GSL_RB_NOP_SIZEDWORDS)) - status = kgsl_ringbuffer_waitspace(rb, numcmds, 1); - } - - if (status == 0) { - ptr = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr; - rb->wptr += numcmds; - } - - return ptr; -} - -static int kgsl_ringbuffer_load_pm4_ucode(struct kgsl_device *device) -{ - int status = 0; - int i; - const struct firmware *fw = NULL; - unsigned int *fw_ptr = NULL; - size_t fw_word_size = 0; - - status = request_firmware(&fw, YAMATO_PM4_FW, - kgsl_driver.misc.this_device); - if (status != 0) { - KGSL_DRV_ERR("request_firmware failed for %s with error %d\n", - YAMATO_PM4_FW, status); - goto done; - } - /*this firmware must come in 3 word chunks. plus 1 word of version*/ - if ((fw->size % (sizeof(uint32_t)*3)) != 4) { - KGSL_DRV_ERR("bad firmware size %d.\n", fw->size); - status = -EINVAL; - goto done; - } - fw_ptr = (unsigned int *)fw->data; - fw_word_size = fw->size/sizeof(uint32_t); - KGSL_DRV_INFO("loading pm4 ucode version: %d\n", fw_ptr[0]); - - kgsl_yamato_regwrite(device, REG_CP_DEBUG, 0x02000000); - kgsl_yamato_regwrite(device, REG_CP_ME_RAM_WADDR, 0); - for (i = 1; i < fw_word_size; i++) - kgsl_yamato_regwrite(device, REG_CP_ME_RAM_DATA, fw_ptr[i]); - -done: - release_firmware(fw); - return status; -} - -static int kgsl_ringbuffer_load_pfp_ucode(struct kgsl_device *device) -{ - int status = 0; - int i; - const struct firmware *fw = NULL; - unsigned int *fw_ptr = NULL; - size_t fw_word_size = 0; - - status = request_firmware(&fw, YAMATO_PFP_FW, - kgsl_driver.misc.this_device); - if (status != 0) { - KGSL_DRV_ERR("request_firmware for %s failed with error %d\n", - YAMATO_PFP_FW, status); - return status; - } - /*this firmware must come in 1 word chunks. */ - if ((fw->size % sizeof(uint32_t)) != 0) { - KGSL_DRV_ERR("bad firmware size %d.\n", fw->size); - release_firmware(fw); - return -EINVAL; - } - fw_ptr = (unsigned int *)fw->data; - fw_word_size = fw->size/sizeof(uint32_t); - - KGSL_DRV_INFO("loading pfp ucode version: %d\n", fw_ptr[0]); - - kgsl_yamato_regwrite(device, REG_CP_PFP_UCODE_ADDR, 0); - for (i = 1; i < fw_word_size; i++) - kgsl_yamato_regwrite(device, REG_CP_PFP_UCODE_DATA, fw_ptr[i]); - - release_firmware(fw); - return status; -} - -static int kgsl_ringbuffer_start(struct kgsl_ringbuffer *rb) -{ - int status; - /*cp_rb_cntl_u cp_rb_cntl; */ - union reg_cp_rb_cntl cp_rb_cntl; - unsigned int *cmds, rb_cntl; - struct kgsl_device *device = rb->device; - - KGSL_CMD_VDBG("enter (rb=%p)\n", rb); - - if (rb->flags & KGSL_FLAGS_STARTED) { - KGSL_CMD_VDBG("return %d\n", 0); - return 0; - } - kgsl_sharedmem_set(&rb->memptrs_desc, 0, 0, - sizeof(struct kgsl_rbmemptrs)); - - kgsl_sharedmem_set(&rb->buffer_desc, 0, 0xAA, - (rb->sizedwords << 2)); - - kgsl_yamato_regwrite(device, REG_CP_RB_WPTR_BASE, - (rb->memptrs_desc.gpuaddr - + GSL_RB_MEMPTRS_WPTRPOLL_OFFSET)); - - /* setup WPTR delay */ - kgsl_yamato_regwrite(device, REG_CP_RB_WPTR_DELAY, 0 /*0x70000010 */); - - /*setup REG_CP_RB_CNTL */ - kgsl_yamato_regread(device, REG_CP_RB_CNTL, &rb_cntl); - cp_rb_cntl.val = rb_cntl; - /* size of ringbuffer */ - cp_rb_cntl.f.rb_bufsz = - kgsl_ringbuffer_sizelog2quadwords(rb->sizedwords); - /* quadwords to read before updating mem RPTR */ - cp_rb_cntl.f.rb_blksz = rb->blksizequadwords; - cp_rb_cntl.f.rb_poll_en = GSL_RB_CNTL_POLL_EN; /* WPTR polling */ - /* mem RPTR writebacks */ - cp_rb_cntl.f.rb_no_update = GSL_RB_CNTL_NO_UPDATE; - - kgsl_yamato_regwrite(device, REG_CP_RB_CNTL, cp_rb_cntl.val); - - kgsl_yamato_regwrite(device, REG_CP_RB_BASE, rb->buffer_desc.gpuaddr); - - kgsl_yamato_regwrite(device, REG_CP_RB_RPTR_ADDR, - rb->memptrs_desc.gpuaddr + - GSL_RB_MEMPTRS_RPTR_OFFSET); - - /* explicitly clear all cp interrupts */ - kgsl_yamato_regwrite(device, REG_CP_INT_ACK, 0xFFFFFFFF); - - /* setup scratch/timestamp */ - kgsl_yamato_regwrite(device, REG_SCRATCH_ADDR, - device->memstore.gpuaddr + - KGSL_DEVICE_MEMSTORE_OFFSET(soptimestamp)); - - kgsl_yamato_regwrite(device, REG_SCRATCH_UMSK, - GSL_RB_MEMPTRS_SCRATCH_MASK); - - /* load the CP ucode */ - - status = kgsl_ringbuffer_load_pm4_ucode(device); - if (status != 0) { - KGSL_DRV_ERR("kgsl_ringbuffer_load_pm4_ucode failed %d\n", - status); - return status; - } - - - /* load the prefetch parser ucode */ - status = kgsl_ringbuffer_load_pfp_ucode(device); - if (status != 0) { - KGSL_DRV_ERR("kgsl_ringbuffer_load_pm4_ucode failed %d\n", - status); - return status; - } - - kgsl_yamato_regwrite(device, REG_CP_QUEUE_THRESHOLDS, 0x000C0804); - - rb->rptr = 0; - rb->wptr = 0; - - rb->timestamp = 0; - GSL_RB_INIT_TIMESTAMP(rb); - - INIT_LIST_HEAD(&rb->memqueue); - - /* clear ME_HALT to start micro engine */ - kgsl_yamato_regwrite(device, REG_CP_ME_CNTL, 0); - - /* ME_INIT */ - cmds = kgsl_ringbuffer_allocspace(rb, 19); - - GSL_RB_WRITE(cmds, PM4_HDR_ME_INIT); - /* All fields present (bits 9:0) */ - GSL_RB_WRITE(cmds, 0x000003ff); - /* Disable/Enable Real-Time Stream processing (present but ignored) */ - GSL_RB_WRITE(cmds, 0x00000000); - /* Enable (2D <-> 3D) implicit synchronization (present but ignored) */ - GSL_RB_WRITE(cmds, 0x00000000); - - GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_RB_SURFACE_INFO)); - GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SC_WINDOW_OFFSET)); - GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_VGT_MAX_VTX_INDX)); - GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_SQ_PROGRAM_CNTL)); - GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_RB_DEPTHCONTROL)); - GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SU_POINT_SIZE)); - GSL_RB_WRITE(cmds, GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SC_LINE_CNTL)); - GSL_RB_WRITE(cmds, - GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SU_POLY_OFFSET_FRONT_SCALE)); - - /* Vertex and Pixel Shader Start Addresses in instructions - * (3 DWORDS per instruction) */ - GSL_RB_WRITE(cmds, 0x80000180); - /* Maximum Contexts */ - GSL_RB_WRITE(cmds, 0x00000001); - /* Write Confirm Interval and The CP will wait the - * wait_interval * 16 clocks between polling */ - GSL_RB_WRITE(cmds, 0x00000000); - - /* NQ and External Memory Swap */ - GSL_RB_WRITE(cmds, 0x00000000); - /* Protected mode error checking */ - GSL_RB_WRITE(cmds, GSL_RB_PROTECTED_MODE_CONTROL); - /* Disable header dumping and Header dump address */ - GSL_RB_WRITE(cmds, 0x00000000); - /* Header dump size */ - GSL_RB_WRITE(cmds, 0x00000000); - - kgsl_ringbuffer_submit(rb); - - /* idle device to validate ME INIT */ - status = kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT); - - KGSL_CMD_DBG("enabling CP interrupts: mask %08lx\n", GSL_CP_INT_MASK); - kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, GSL_CP_INT_MASK); - if (status == 0) - rb->flags |= KGSL_FLAGS_STARTED; - - KGSL_CMD_VDBG("return %d\n", status); - - return status; -} - -static int kgsl_ringbuffer_stop(struct kgsl_ringbuffer *rb) -{ - KGSL_CMD_VDBG("enter (rb=%p)\n", rb); - - if (rb->flags & KGSL_FLAGS_STARTED) { - KGSL_CMD_DBG("disabling CP interrupts: mask %08x\n", 0); - kgsl_yamato_regwrite(rb->device, REG_CP_INT_CNTL, 0); - - /* ME_HALT */ - kgsl_yamato_regwrite(rb->device, REG_CP_ME_CNTL, 0x10000000); - - rb->flags &= ~KGSL_FLAGS_STARTED; - kgsl_ringbuffer_dump(rb); - } - - KGSL_CMD_VDBG("return %d\n", 0); - - return 0; -} - -int kgsl_ringbuffer_init(struct kgsl_device *device) -{ - int status; - uint32_t flags; - struct kgsl_ringbuffer *rb = &device->ringbuffer; - - KGSL_CMD_VDBG("enter (device=%p)\n", device); - - rb->device = device; - rb->sizedwords = (2 << kgsl_cfg_rb_sizelog2quadwords); - rb->blksizequadwords = kgsl_cfg_rb_blksizequadwords; - - /* allocate memory for ringbuffer, needs to be double octword aligned - * align on page from contiguous physical memory - */ - flags = - (KGSL_MEMFLAGS_ALIGNPAGE | KGSL_MEMFLAGS_CONPHYS | - KGSL_MEMFLAGS_STRICTREQUEST); - - status = kgsl_sharedmem_alloc(flags, (rb->sizedwords << 2), - &rb->buffer_desc); - - if (status != 0) { - kgsl_ringbuffer_close(rb); - KGSL_CMD_VDBG("return %d\n", status); - return status; - } - - /* allocate memory for polling and timestamps */ - /* This really can be at 4 byte alignment boundry but for using MMU - * we need to make it at page boundary */ - flags = (KGSL_MEMFLAGS_ALIGNPAGE | KGSL_MEMFLAGS_CONPHYS); - - status = kgsl_sharedmem_alloc(flags, sizeof(struct kgsl_rbmemptrs), - &rb->memptrs_desc); - - if (status != 0) { - kgsl_ringbuffer_close(rb); - KGSL_CMD_VDBG("return %d\n", status); - return status; - } - - /* last allocation of init process is made here so map all - * allocations to MMU */ - status = kgsl_yamato_setup_pt(device, device->mmu.defaultpagetable); - if (status != 0) { - kgsl_ringbuffer_close(rb); - KGSL_CMD_VDBG("return %d\n", status); - return status; - } - - /* overlay structure on memptrs memory */ - rb->memptrs = (struct kgsl_rbmemptrs *) rb->memptrs_desc.hostptr; - - rb->flags |= KGSL_FLAGS_INITIALIZED; - - status = kgsl_ringbuffer_start(rb); - if (status != 0) { - kgsl_ringbuffer_close(rb); - KGSL_CMD_VDBG("return %d\n", status); - return status; - } - - KGSL_CMD_VDBG("return %d\n", 0); - return 0; -} - -int kgsl_ringbuffer_close(struct kgsl_ringbuffer *rb) -{ - KGSL_CMD_VDBG("enter (rb=%p)\n", rb); - - kgsl_cmdstream_memqueue_drain(rb->device); - - kgsl_ringbuffer_stop(rb); - - /* this must happen before first sharedmem_free */ - kgsl_yamato_cleanup_pt(rb->device, rb->device->mmu.defaultpagetable); - - if (rb->buffer_desc.hostptr) - kgsl_sharedmem_free(&rb->buffer_desc); - - if (rb->memptrs_desc.hostptr) - kgsl_sharedmem_free(&rb->memptrs_desc); - - rb->flags &= ~KGSL_FLAGS_INITIALIZED; - - memset(rb, 0, sizeof(struct kgsl_ringbuffer)); - - KGSL_CMD_VDBG("return %d\n", 0); - return 0; -} - -static uint32_t -kgsl_ringbuffer_addcmds(struct kgsl_ringbuffer *rb, - int flags, unsigned int *cmds, - int sizedwords) -{ - unsigned int *ringcmds; - unsigned int timestamp; - unsigned int total_sizedwords = sizedwords + 6; - - /* reserve space to temporarily turn off protected mode - * error checking if needed - */ - total_sizedwords += flags & KGSL_CMD_FLAGS_PMODE ? 4 : 0; - total_sizedwords += !(flags & KGSL_CMD_FLAGS_NO_TS_CMP) ? 9 : 0; - - ringcmds = kgsl_ringbuffer_allocspace(rb, total_sizedwords); - - if (flags & KGSL_CMD_FLAGS_PMODE) { - /* disable protected mode error checking */ - *ringcmds++ = pm4_type3_packet(PM4_SET_PROTECTED_MODE, 1); - *ringcmds++ = 0; - } - - memcpy(ringcmds, cmds, (sizedwords << 2)); - - ringcmds += sizedwords; - - if (flags & KGSL_CMD_FLAGS_PMODE) { - /* re-enable protected mode error checking */ - *ringcmds++ = pm4_type3_packet(PM4_SET_PROTECTED_MODE, 1); - *ringcmds++ = 1; - } - - rb->timestamp++; - timestamp = rb->timestamp; - - /* start-of-pipeline and end-of-pipeline timestamps */ - *ringcmds++ = pm4_type0_packet(REG_CP_TIMESTAMP, 1); - *ringcmds++ = rb->timestamp; - *ringcmds++ = pm4_type3_packet(PM4_EVENT_WRITE, 3); - *ringcmds++ = CACHE_FLUSH_TS; - *ringcmds++ = - (rb->device->memstore.gpuaddr + - KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp)); - *ringcmds++ = rb->timestamp; - - if (!(flags & KGSL_CMD_FLAGS_NO_TS_CMP)) { - /* Add idle packet so avoid RBBM errors */ - *ringcmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *ringcmds++ = 0x00000000; - /* Conditional execution based on memory values */ - *ringcmds++ = pm4_type3_packet(PM4_COND_EXEC, 4); - *ringcmds++ = (rb->device->memstore.gpuaddr + - KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable)) >> 2; - *ringcmds++ = (rb->device->memstore.gpuaddr + - KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts)) >> 2; - *ringcmds++ = rb->timestamp; - /* # of conditional command DWORDs */ - *ringcmds++ = 2; - *ringcmds++ = pm4_type3_packet(PM4_INTERRUPT, 1); - *ringcmds++ = CP_INT_CNTL__RB_INT_MASK; - } - - kgsl_ringbuffer_submit(rb); - - GSL_RB_STATS(rb->stats.words_total += sizedwords); - GSL_RB_STATS(rb->stats.issues++); - - KGSL_CMD_VDBG("return %d\n", timestamp); - - /* return timestamp of issued coREG_ands */ - return timestamp; -} - -uint32_t -kgsl_ringbuffer_issuecmds(struct kgsl_device *device, - int flags, - unsigned int *cmds, - int sizedwords) -{ - unsigned int timestamp; - struct kgsl_ringbuffer *rb = &device->ringbuffer; - - KGSL_CMD_VDBG("enter (device->id=%d, flags=%d, cmds=%p, " - "sizedwords=%d)\n", device->id, flags, cmds, sizedwords); - - timestamp = kgsl_ringbuffer_addcmds(rb, flags, cmds, sizedwords); - - KGSL_CMD_VDBG("return %d\n)", timestamp); - return timestamp; -} - -int -kgsl_ringbuffer_issueibcmds(struct kgsl_device *device, - int drawctxt_index, - uint32_t ibaddr, - int sizedwords, - uint32_t *timestamp, - unsigned int flags) -{ - unsigned int link[3]; - - KGSL_CMD_VDBG("enter (device_id=%d, drawctxt_index=%d, ibaddr=0x%08x," - " sizedwords=%d, timestamp=%p)\n", - device->id, drawctxt_index, ibaddr, - sizedwords, timestamp); - - if (!(device->ringbuffer.flags & KGSL_FLAGS_STARTED)) { - KGSL_CMD_VDBG("return %d\n", -EINVAL); - return -EINVAL; - } - - BUG_ON(ibaddr == 0); - BUG_ON(sizedwords == 0); - - link[0] = PM4_HDR_INDIRECT_BUFFER_PFD; - link[1] = ibaddr; - link[2] = sizedwords; - - kgsl_drawctxt_switch(device, &device->drawctxt[drawctxt_index], flags); - - *timestamp = kgsl_ringbuffer_addcmds(&device->ringbuffer, - 0, &link[0], 3); - - - KGSL_CMD_INFO("ctxt %d g %08x sd %d ts %d\n", - drawctxt_index, ibaddr, sizedwords, *timestamp); - - KGSL_CMD_VDBG("return %d\n", 0); - - return 0; -} - - -#ifdef DEBUG -void kgsl_ringbuffer_debug(struct kgsl_ringbuffer *rb, - struct kgsl_rb_debug *rb_debug) -{ - memset(rb_debug, 0, sizeof(struct kgsl_rb_debug)); - - rb_debug->mem_rptr = rb->memptrs->rptr; - rb_debug->mem_wptr_poll = rb->memptrs->wptr_poll; - kgsl_yamato_regread(rb->device, REG_CP_RB_BASE, - (unsigned int *)&rb_debug->cp_rb_base); - kgsl_yamato_regread(rb->device, REG_CP_RB_CNTL, - (unsigned int *)&rb_debug->cp_rb_cntl); - kgsl_yamato_regread(rb->device, REG_CP_RB_RPTR_ADDR, - (unsigned int *)&rb_debug->cp_rb_rptr_addr); - kgsl_yamato_regread(rb->device, REG_CP_RB_RPTR, - (unsigned int *)&rb_debug->cp_rb_rptr); - kgsl_yamato_regread(rb->device, REG_CP_RB_RPTR_WR, - (unsigned int *)&rb_debug->cp_rb_rptr_wr); - kgsl_yamato_regread(rb->device, REG_CP_RB_WPTR, - (unsigned int *)&rb_debug->cp_rb_wptr); - kgsl_yamato_regread(rb->device, REG_CP_RB_WPTR_DELAY, - (unsigned int *)&rb_debug->cp_rb_wptr_delay); - kgsl_yamato_regread(rb->device, REG_CP_RB_WPTR_BASE, - (unsigned int *)&rb_debug->cp_rb_wptr_base); - kgsl_yamato_regread(rb->device, REG_CP_IB1_BASE, - (unsigned int *)&rb_debug->cp_ib1_base); - kgsl_yamato_regread(rb->device, REG_CP_IB1_BUFSZ, - (unsigned int *)&rb_debug->cp_ib1_bufsz); - kgsl_yamato_regread(rb->device, REG_CP_IB2_BASE, - (unsigned int *)&rb_debug->cp_ib2_base); - kgsl_yamato_regread(rb->device, REG_CP_IB2_BUFSZ, - (unsigned int *)&rb_debug->cp_ib2_bufsz); - kgsl_yamato_regread(rb->device, REG_CP_ST_BASE, - (unsigned int *)&rb_debug->cp_st_base); - kgsl_yamato_regread(rb->device, REG_CP_ST_BUFSZ, - (unsigned int *)&rb_debug->cp_st_bufsz); - kgsl_yamato_regread(rb->device, REG_CP_CSQ_RB_STAT, - (unsigned int *)&rb_debug->cp_csq_rb_stat); - kgsl_yamato_regread(rb->device, REG_CP_CSQ_IB1_STAT, - (unsigned int *)&rb_debug->cp_csq_ib1_stat); - kgsl_yamato_regread(rb->device, REG_CP_CSQ_IB2_STAT, - (unsigned int *)&rb_debug->cp_csq_ib2_stat); - kgsl_yamato_regread(rb->device, REG_SCRATCH_UMSK, - (unsigned int *)&rb_debug->scratch_umsk); - kgsl_yamato_regread(rb->device, REG_SCRATCH_ADDR, - (unsigned int *)&rb_debug->scratch_addr); - kgsl_yamato_regread(rb->device, REG_CP_ME_CNTL, - (unsigned int *)&rb_debug->cp_me_cntl); - kgsl_yamato_regread(rb->device, REG_CP_ME_STATUS, - (unsigned int *)&rb_debug->cp_me_status); - kgsl_yamato_regread(rb->device, REG_CP_DEBUG, - (unsigned int *)&rb_debug->cp_debug); - kgsl_yamato_regread(rb->device, REG_CP_STAT, - (unsigned int *)&rb_debug->cp_stat); - kgsl_yamato_regread(rb->device, REG_CP_INT_STATUS, - (unsigned int *)&rb_debug->cp_int_status); - kgsl_yamato_regread(rb->device, REG_CP_INT_CNTL, - (unsigned int *)&rb_debug->cp_int_cntl); - kgsl_yamato_regread(rb->device, REG_RBBM_STATUS, - (unsigned int *)&rb_debug->rbbm_status); - kgsl_yamato_regread(rb->device, REG_RBBM_INT_STATUS, - (unsigned int *)&rb_debug->rbbm_int_status); - GSL_RB_GET_SOP_TIMESTAMP(rb, (unsigned int *)&rb_debug->sop_timestamp); - GSL_RB_GET_EOP_TIMESTAMP(rb, (unsigned int *)&rb_debug->eop_timestamp); - -} -#endif /*DEBUG*/ - -#ifdef DEBUG -void kgsl_ringbuffer_dump(struct kgsl_ringbuffer *rb) -{ - struct kgsl_rb_debug rb_debug; - kgsl_ringbuffer_debug(rb, &rb_debug); - - KGSL_CMD_DBG("rbbm_status %08x rbbm_int_status %08x" - " mem_rptr %08x mem_wptr_poll %08x\n", - rb_debug.rbbm_status, - rb_debug.rbbm_int_status, - rb_debug.mem_rptr, rb_debug.mem_wptr_poll); - - KGSL_CMD_DBG("rb_base %08x rb_cntl %08x rb_rptr_addr %08x rb_rptr %08x" - " rb_rptr_wr %08x\n", - rb_debug.cp_rb_base, rb_debug.cp_rb_cntl, - rb_debug.cp_rb_rptr_addr, rb_debug.cp_rb_rptr, - rb_debug.cp_rb_rptr_wr); - - KGSL_CMD_DBG("rb_wptr %08x rb_wptr_delay %08x rb_wptr_base %08x" - " ib1_base %08x ib1_bufsz %08x\n", - rb_debug.cp_rb_wptr, rb_debug.cp_rb_wptr_delay, - rb_debug.cp_rb_wptr_base, rb_debug.cp_ib1_base, - rb_debug.cp_ib1_bufsz); - - KGSL_CMD_DBG("ib2_base %08x ib2_bufsz %08x st_base %08x st_bufsz %08x" - " cp_me_cntl %08x cp_me_status %08x\n", - rb_debug.cp_ib2_base, rb_debug.cp_ib2_bufsz, - rb_debug.cp_st_base, rb_debug.cp_st_bufsz, - rb_debug.cp_me_cntl, rb_debug.cp_me_status); - - KGSL_CMD_DBG("cp_debug %08x cp_stat %08x cp_int_status %08x" - " cp_int_cntl %08x\n", - rb_debug.cp_debug, rb_debug.cp_stat, - rb_debug.cp_int_status, rb_debug.cp_int_cntl); - - KGSL_CMD_DBG("sop_timestamp: %d eop_timestamp: %d\n", - rb_debug.sop_timestamp, rb_debug.eop_timestamp); - -} -#endif /* DEBUG */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.h b/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.h deleted file mode 100644 index 0d060209de4d8..0000000000000 --- a/drivers/video/msm/gpu/kgsl/kgsl_ringbuffer.h +++ /dev/null @@ -1,254 +0,0 @@ -/* - * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 - * Copyright (c) 2008-2009 QUALCOMM USA, INC. - * - * All source code in this file is licensed under the following license - * - * 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, you can find it at http://www.fsf.org - */ -#ifndef __GSL_RINGBUFFER_H -#define __GSL_RINGBUFFER_H - -#include -#include -#include -#include "kgsl_log.h" -#include "kgsl_sharedmem.h" -#include "yamato_reg.h" - -#define GSL_STATS_RINGBUFFER - -#define GSL_RB_USE_MEM_RPTR -#define GSL_RB_USE_MEM_TIMESTAMP -#define GSL_DEVICE_SHADOW_MEMSTORE_TO_USER - -/* ringbuffer sizes log2quadword */ -#define GSL_RB_SIZE_8 0 -#define GSL_RB_SIZE_16 1 -#define GSL_RB_SIZE_32 2 -#define GSL_RB_SIZE_64 3 -#define GSL_RB_SIZE_128 4 -#define GSL_RB_SIZE_256 5 -#define GSL_RB_SIZE_512 6 -#define GSL_RB_SIZE_1K 7 -#define GSL_RB_SIZE_2K 8 -#define GSL_RB_SIZE_4K 9 -#define GSL_RB_SIZE_8K 10 -#define GSL_RB_SIZE_16K 11 -#define GSL_RB_SIZE_32K 12 -#define GSL_RB_SIZE_64K 13 -#define GSL_RB_SIZE_128K 14 -#define GSL_RB_SIZE_256K 15 -#define GSL_RB_SIZE_512K 16 -#define GSL_RB_SIZE_1M 17 -#define GSL_RB_SIZE_2M 18 -#define GSL_RB_SIZE_4M 19 - -/* Yamato ringbuffer config*/ -static const unsigned int kgsl_cfg_rb_sizelog2quadwords = GSL_RB_SIZE_32K; -static const unsigned int kgsl_cfg_rb_blksizequadwords = GSL_RB_SIZE_16; - -/* CP timestamp register */ -#define REG_CP_TIMESTAMP REG_SCRATCH_REG0 - - -struct kgsl_device; -struct kgsl_drawctxt; -struct kgsl_ringbuffer; - -struct kgsl_rb_debug { - unsigned int pm4_ucode_rel; - unsigned int pfp_ucode_rel; - unsigned int mem_wptr_poll; - unsigned int mem_rptr; - unsigned int cp_rb_base; - unsigned int cp_rb_cntl; - unsigned int cp_rb_rptr_addr; - unsigned int cp_rb_rptr; - unsigned int cp_rb_rptr_wr; - unsigned int cp_rb_wptr; - unsigned int cp_rb_wptr_delay; - unsigned int cp_rb_wptr_base; - unsigned int cp_ib1_base; - unsigned int cp_ib1_bufsz; - unsigned int cp_ib2_base; - unsigned int cp_ib2_bufsz; - unsigned int cp_st_base; - unsigned int cp_st_bufsz; - unsigned int cp_csq_rb_stat; - unsigned int cp_csq_ib1_stat; - unsigned int cp_csq_ib2_stat; - unsigned int scratch_umsk; - unsigned int scratch_addr; - unsigned int cp_me_cntl; - unsigned int cp_me_status; - unsigned int cp_debug; - unsigned int cp_stat; - unsigned int cp_int_status; - unsigned int cp_int_cntl; - unsigned int rbbm_status; - unsigned int rbbm_int_status; - unsigned int sop_timestamp; - unsigned int eop_timestamp; -}; -#ifdef DEBUG -void kgsl_ringbuffer_debug(struct kgsl_ringbuffer *rb, - struct kgsl_rb_debug *rb_debug); - -void kgsl_ringbuffer_dump(struct kgsl_ringbuffer *rb); -#else -static inline void kgsl_ringbuffer_debug(struct kgsl_ringbuffer *rb, - struct kgsl_rb_debug *rb_debug) -{ -} - -static inline void kgsl_ringbuffer_dump(struct kgsl_ringbuffer *rb) -{ -} -#endif - -struct kgsl_rbwatchdog { - uint32_t flags; - unsigned int rptr_sample; -}; - -#define GSL_RB_MEMPTRS_SCRATCH_COUNT 8 -struct kgsl_rbmemptrs { - volatile int rptr; - volatile int wptr_poll; -} __attribute__ ((packed)); - -#define GSL_RB_MEMPTRS_RPTR_OFFSET \ - (offsetof(struct kgsl_rbmemptrs, rptr)) - -#define GSL_RB_MEMPTRS_WPTRPOLL_OFFSET \ - (offsetof(struct kgsl_rbmemptrs, wptr_poll)) - -struct kgsl_rbstats { - int64_t issues; - int64_t words_total; -}; - - -struct kgsl_ringbuffer { - struct kgsl_device *device; - uint32_t flags; - - struct kgsl_memdesc buffer_desc; - - struct kgsl_memdesc memptrs_desc; - struct kgsl_rbmemptrs *memptrs; - - /*ringbuffer size */ - unsigned int sizedwords; - unsigned int blksizequadwords; - - unsigned int wptr; /* write pointer offset in dwords from baseaddr */ - unsigned int rptr; /* read pointer offset in dwords from baseaddr */ - uint32_t timestamp; - - /* queue of memfrees pending timestamp elapse */ - struct list_head memqueue; - - struct kgsl_rbwatchdog watchdog; - -#ifdef GSL_STATS_RINGBUFFER - struct kgsl_rbstats stats; -#endif /* GSL_STATS_RINGBUFFER */ - -}; - -/* dword base address of the GFX decode space */ -#define GSL_HAL_SUBBLOCK_OFFSET(reg) ((unsigned int)((reg) - (0x2000))) - -#define GSL_RB_WRITE(ring, data) \ - do { \ - mb(); \ - writel(data, ring); \ - ring++; \ - } while (0) - -/* timestamp */ -#ifdef GSL_DEVICE_SHADOW_MEMSTORE_TO_USER -#define GSL_RB_USE_MEM_TIMESTAMP -#endif /* GSL_DEVICE_SHADOW_MEMSTORE_TO_USER */ - -#ifdef GSL_RB_USE_MEM_TIMESTAMP -/* enable timestamp (...scratch0) memory shadowing */ -#define GSL_RB_MEMPTRS_SCRATCH_MASK 0x1 -#define GSL_RB_INIT_TIMESTAMP(rb) - -#else -#define GSL_RB_MEMPTRS_SCRATCH_MASK 0x0 -#define GSL_RB_INIT_TIMESTAMP(rb) \ - kgsl_yamato_regwrite((rb)->device->id, REG_CP_TIMESTAMP, 0) - -#endif /* GSL_RB_USE_MEMTIMESTAMP */ - -/* mem rptr */ -#ifdef GSL_RB_USE_MEM_RPTR -#define GSL_RB_CNTL_NO_UPDATE 0x0 /* enable */ -#define GSL_RB_GET_READPTR(rb, data) \ - do { \ - *(data) = (rb)->memptrs->rptr; \ - } while (0) -#else -#define GSL_RB_CNTL_NO_UPDATE 0x1 /* disable */ -#define GSL_RB_GET_READPTR(rb, data) \ - do { \ - kgsl_yamato_regread((rb)->device->id, REG_CP_RB_RPTR, (data)); \ - } while (0) -#endif /* GSL_RB_USE_MEMRPTR */ - -/* wptr polling */ -#ifdef GSL_RB_USE_WPTR_POLLING -#define GSL_RB_CNTL_POLL_EN 0x1 /* enable */ -#define GSL_RB_UPDATE_WPTR_POLLING(rb) \ - do { (rb)->memptrs->wptr_poll = (rb)->wptr; } while (0) -#else -#define GSL_RB_CNTL_POLL_EN 0x0 /* disable */ -#define GSL_RB_UPDATE_WPTR_POLLING(rb) -#endif /* GSL_RB_USE_WPTR_POLLING */ - -/* stats */ -#ifdef GSL_STATS_RINGBUFFER -#define GSL_RB_STATS(x) x -#else -#define GSL_RB_STATS(x) -#endif /* GSL_STATS_RINGBUFFER */ - -struct kgsl_pmem_entry; - -int kgsl_ringbuffer_issueibcmds(struct kgsl_device *, int drawctxt_index, - uint32_t ibaddr, int sizedwords, - uint32_t *timestamp, - unsigned int flags); - -int kgsl_ringbuffer_init(struct kgsl_device *device); - -int kgsl_ringbuffer_close(struct kgsl_ringbuffer *rb); - -uint32_t kgsl_ringbuffer_issuecmds(struct kgsl_device *device, - int pmodeoff, - unsigned int *cmdaddr, - int sizedwords); - -int kgsl_ringbuffer_gettimestampshadow(struct kgsl_device *device, - unsigned int *sopaddr, - unsigned int *eopaddr); - -void kgsl_ringbuffer_watchdog(void); - -void kgsl_cp_intrcallback(struct kgsl_device *device); - -#endif /* __GSL_RINGBUFFER_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c deleted file mode 100644 index 4a1b42125e6d8..0000000000000 --- a/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.c +++ /dev/null @@ -1,300 +0,0 @@ -/* - * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 - * Copyright (c) 2008-2009 QUALCOMM USA, INC. - * - * All source code in this file is licensed under the following license - * - * 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, you can find it at http://www.fsf.org - */ -#include -#include -#include - -#include "kgsl_sharedmem.h" -#include "kgsl_device.h" -#include "kgsl.h" -#include "kgsl_log.h" - -/* block alignment shift count */ -static inline unsigned int -kgsl_memarena_get_order(uint32_t flags) -{ - unsigned int alignshift; - alignshift = ((flags & KGSL_MEMFLAGS_ALIGN_MASK) - >> KGSL_MEMFLAGS_ALIGN_SHIFT); - return alignshift; -} - -/* block alignment shift count */ -static inline unsigned int -kgsl_memarena_align(unsigned int address, unsigned int shift) -{ - unsigned int alignedbaseaddr = ((address) >> shift) << shift; - if (alignedbaseaddr < address) - alignedbaseaddr += (1 << shift); - - return alignedbaseaddr; -} - -int -kgsl_sharedmem_init(struct kgsl_sharedmem *shmem) -{ - int result = -EINVAL; - - if (!request_mem_region(shmem->physbase, shmem->size, DRIVER_NAME)) { - KGSL_MEM_ERR("request_mem_region failed\n"); - goto error; - } - - shmem->baseptr = ioremap(shmem->physbase, shmem->size); - KGSL_MEM_INFO("ioremap(shm) = %p\n", shmem->baseptr); - - if (shmem->baseptr == NULL) { - KGSL_MEM_ERR("ioremap failed for address %08x size %d\n", - shmem->physbase, shmem->size); - result = -ENODEV; - goto error_release_mem; - } - - shmem->pool = gen_pool_create(KGSL_PAGESIZE_SHIFT, -1); - if (shmem->pool == NULL) { - KGSL_MEM_ERR("gen_pool_create failed\n"); - result = -ENOMEM; - goto error_iounmap; - } - - if (gen_pool_add(shmem->pool, shmem->physbase, shmem->size, -1)) { - KGSL_MEM_ERR("gen_pool_create failed\n"); - result = -ENOMEM; - goto error_pool_destroy; - } - result = 0; - KGSL_MEM_INFO("physbase 0x%08x size 0x%08x baseptr 0x%p\n", - shmem->physbase, shmem->size, shmem->baseptr); - return 0; - -error_pool_destroy: - gen_pool_destroy(shmem->pool); -error_iounmap: - iounmap(shmem->baseptr); - shmem->baseptr = NULL; -error_release_mem: - release_mem_region(shmem->physbase, shmem->size); -error: - return result; -} - -int -kgsl_sharedmem_close(struct kgsl_sharedmem *shmem) -{ - if (shmem->pool) { - gen_pool_destroy(shmem->pool); - shmem->pool = NULL; - } - - if (shmem->baseptr != NULL) { - KGSL_MEM_INFO("iounmap(shm) = %p\n", shmem->baseptr); - iounmap(shmem->baseptr); - shmem->baseptr = NULL; - release_mem_region(shmem->physbase, shmem->size); - } - - return 0; -} -/* -* get the host mapped address for a hardware device address -*/ -static void *kgsl_memarena_gethostptr(struct kgsl_sharedmem *shmem, - uint32_t physaddr) -{ - void *result; - - KGSL_MEM_VDBG("enter (memarena=%p, physaddr=0x%08x)\n", - shmem, physaddr); - - BUG_ON(shmem == NULL); - - /* check address range */ - if (physaddr < shmem->physbase) - return NULL; - - if (physaddr >= shmem->physbase + shmem->size) - return NULL; - - if (shmem->baseptr == NULL) { - KGSL_MEM_VDBG("return: %p\n", NULL); - return NULL; - } - - result = ((physaddr - shmem->physbase) + shmem->baseptr); - - KGSL_MEM_VDBG("return: %p\n", result); - - return result; -} - - -int -kgsl_sharedmem_alloc(uint32_t flags, int size, - struct kgsl_memdesc *memdesc) -{ - struct kgsl_sharedmem *shmem; - int result = -ENOMEM; - unsigned int blksize; - unsigned int baseaddr; - unsigned int alignshift; - unsigned int alignedbaseaddr; - - KGSL_MEM_VDBG("enter (flags=0x%08x, size=%d, memdesc=%p)\n", - flags, size, memdesc); - - shmem = &kgsl_driver.shmem; - BUG_ON(memdesc == NULL); - BUG_ON(size <= 0); - - alignshift = kgsl_memarena_get_order(flags); - - size = ALIGN(size, KGSL_PAGESIZE); - blksize = size; - if (alignshift > KGSL_PAGESIZE_SHIFT) - blksize += (1 << alignshift) - KGSL_PAGESIZE; - - baseaddr = gen_pool_alloc(shmem->pool, blksize); - if (baseaddr == 0) { - KGSL_MEM_ERR("gen_pool_alloc failed\n"); - result = -ENOMEM; - goto done; - } - result = 0; - - if (alignshift > KGSL_PAGESIZE_SHIFT) { - alignedbaseaddr = ALIGN(baseaddr, (1 << alignshift)); - - KGSL_MEM_VDBG("ba %x al %x as %d m->as %d bs %x s %x\n", - baseaddr, alignedbaseaddr, alignshift, - KGSL_PAGESIZE_SHIFT, blksize, size); - if (alignedbaseaddr > baseaddr) { - KGSL_MEM_VDBG("physaddr %x free before %x size %x\n", - alignedbaseaddr, - baseaddr, alignedbaseaddr - baseaddr); - gen_pool_free(shmem->pool, baseaddr, - alignedbaseaddr - baseaddr); - blksize -= alignedbaseaddr - baseaddr; - } - if (blksize > size) { - KGSL_MEM_VDBG("physaddr %x free after %x size %x\n", - alignedbaseaddr, - alignedbaseaddr + size, - blksize - size); - gen_pool_free(shmem->pool, - alignedbaseaddr + size, - blksize - size); - } - } else { - alignedbaseaddr = baseaddr; - } - - memdesc->physaddr = alignedbaseaddr; - memdesc->hostptr = kgsl_memarena_gethostptr(shmem, memdesc->physaddr); - memdesc->size = size; - - KGSL_MEM_VDBG("ashift %d m->ashift %d blksize %d base %x abase %x\n", - alignshift, KGSL_PAGESIZE_SHIFT, blksize, baseaddr, - alignedbaseaddr); - -done: - if (result) - memset(memdesc, 0, sizeof(*memdesc)); - - - KGSL_MEM_VDBG("return: %d\n", result); - return result; -} - -void -kgsl_sharedmem_free(struct kgsl_memdesc *memdesc) -{ - struct kgsl_sharedmem *shmem = &kgsl_driver.shmem; - - KGSL_MEM_VDBG("enter (shmem=%p, memdesc=%p, physaddr=%08x, size=%d)\n", - shmem, memdesc, memdesc->physaddr, memdesc->size); - - BUG_ON(memdesc == NULL); - BUG_ON(memdesc->size <= 0); - BUG_ON(shmem->physbase > memdesc->physaddr); - BUG_ON((shmem->physbase + shmem->size) - < (memdesc->physaddr + memdesc->size)); - - gen_pool_free(shmem->pool, memdesc->physaddr, memdesc->size); - - memset(memdesc, 0, sizeof(struct kgsl_memdesc)); - KGSL_MEM_VDBG("return\n"); -} - -int -kgsl_sharedmem_read(const struct kgsl_memdesc *memdesc, void *dst, - unsigned int offsetbytes, unsigned int sizebytes) -{ - if (memdesc == NULL || memdesc->hostptr == NULL || dst == NULL) { - KGSL_MEM_ERR("bad ptr memdesc %p hostptr %p dst %p\n", - memdesc, - (memdesc ? memdesc->hostptr : NULL), - dst); - return -EINVAL; - } - if (offsetbytes + sizebytes > memdesc->size) { - KGSL_MEM_ERR("bad range: offset %d size %d memdesc %d\n", - offsetbytes, sizebytes, memdesc->size); - return -ERANGE; - } - memcpy(dst, memdesc->hostptr + offsetbytes, sizebytes); - return 0; -} - -int -kgsl_sharedmem_write(const struct kgsl_memdesc *memdesc, - unsigned int offsetbytes, - void *value, unsigned int sizebytes) -{ - if (memdesc == NULL || memdesc->hostptr == NULL) { - KGSL_MEM_ERR("bad ptr memdesc %p hostptr %p\n", memdesc, - (memdesc ? memdesc->hostptr : NULL)); - return -EINVAL; - } - if (offsetbytes + sizebytes > memdesc->size) { - KGSL_MEM_ERR("bad range: offset %d size %d memdesc %d\n", - offsetbytes, sizebytes, memdesc->size); - return -ERANGE; - } - memcpy(memdesc->hostptr + offsetbytes, value, sizebytes); - return 0; -} - -int -kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc, unsigned int offsetbytes, - unsigned int value, unsigned int sizebytes) -{ - if (memdesc == NULL || memdesc->hostptr == NULL) { - KGSL_MEM_ERR("bad ptr memdesc %p hostptr %p\n", memdesc, - (memdesc ? memdesc->hostptr : NULL)); - return -EINVAL; - } - if (offsetbytes + sizebytes > memdesc->size) { - KGSL_MEM_ERR("bad range: offset %d size %d memdesc %d\n", - offsetbytes, sizebytes, memdesc->size); - return -ERANGE; - } - memset(memdesc->hostptr + offsetbytes, value, sizebytes); - return 0; -} - diff --git a/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h b/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h deleted file mode 100644 index eaf840638bade..0000000000000 --- a/drivers/video/msm/gpu/kgsl/kgsl_sharedmem.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 - * Copyright (c) 2008-2009 QUALCOMM USA, INC. - * - * All source code in this file is licensed under the following license - * - * 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, you can find it at http://www.fsf.org - */ -#ifndef __GSL_SHAREDMEM_H -#define __GSL_SHAREDMEM_H - -#include -#include - -#define KGSL_PAGESIZE 0x1000 -#define KGSL_PAGESIZE_SHIFT 12 -#define KGSL_PAGEMASK (~(KGSL_PAGESIZE - 1)) - -struct kgsl_pagetable; - -struct platform_device; -struct gen_pool; - -/* memory allocation flags */ -#define KGSL_MEMFLAGS_ANY 0x00000000 /*dont care*/ - -#define KGSL_MEMFLAGS_APERTUREANY 0x00000000 -#define KGSL_MEMFLAGS_EMEM 0x00000000 -#define KGSL_MEMFLAGS_CONPHYS 0x00001000 - -#define KGSL_MEMFLAGS_ALIGNANY 0x00000000 -#define KGSL_MEMFLAGS_ALIGN32 0x00000000 -#define KGSL_MEMFLAGS_ALIGN64 0x00060000 -#define KGSL_MEMFLAGS_ALIGN128 0x00070000 -#define KGSL_MEMFLAGS_ALIGN256 0x00080000 -#define KGSL_MEMFLAGS_ALIGN512 0x00090000 -#define KGSL_MEMFLAGS_ALIGN1K 0x000A0000 -#define KGSL_MEMFLAGS_ALIGN2K 0x000B0000 -#define KGSL_MEMFLAGS_ALIGN4K 0x000C0000 -#define KGSL_MEMFLAGS_ALIGN8K 0x000D0000 -#define KGSL_MEMFLAGS_ALIGN16K 0x000E0000 -#define KGSL_MEMFLAGS_ALIGN32K 0x000F0000 -#define KGSL_MEMFLAGS_ALIGN64K 0x00100000 -#define KGSL_MEMFLAGS_ALIGNPAGE KGSL_MEMFLAGS_ALIGN4K - -/* fail the alloc if the flags cannot be honored */ -#define KGSL_MEMFLAGS_STRICTREQUEST 0x80000000 - -#define KGSL_MEMFLAGS_APERTURE_MASK 0x0000F000 -#define KGSL_MEMFLAGS_ALIGN_MASK 0x00FF0000 - -#define KGSL_MEMFLAGS_APERTURE_SHIFT 12 -#define KGSL_MEMFLAGS_ALIGN_SHIFT 16 - - -/* shared memory allocation */ -struct kgsl_memdesc { - struct kgsl_pagetable *pagetable; - void *hostptr; - unsigned int gpuaddr; - unsigned int physaddr; - unsigned int size; - unsigned int priv; -}; - -struct kgsl_sharedmem { - void *baseptr; - unsigned int physbase; - unsigned int size; - struct gen_pool *pool; -}; - -int kgsl_sharedmem_alloc(uint32_t flags, int size, - struct kgsl_memdesc *memdesc); - -/*TODO: add protection flags */ -int kgsl_sharedmem_import(struct kgsl_pagetable *, - uint32_t phys_addr, - uint32_t size, - struct kgsl_memdesc *memdesc); - - -void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc); - - -int kgsl_sharedmem_read(const struct kgsl_memdesc *memdesc, void *dst, - unsigned int offsetbytes, unsigned int sizebytes); - -int kgsl_sharedmem_write(const struct kgsl_memdesc *memdesc, - unsigned int offsetbytes, void *value, - unsigned int sizebytes); - -int kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc, - unsigned int offsetbytes, unsigned int value, - unsigned int sizebytes); - -int kgsl_sharedmem_init(struct kgsl_sharedmem *shmem); - -int kgsl_sharedmem_close(struct kgsl_sharedmem *shmem); - -#endif /* __GSL_SHAREDMEM_H */ diff --git a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c b/drivers/video/msm/gpu/kgsl/kgsl_yamato.c deleted file mode 100644 index af5a6ff223cba..0000000000000 --- a/drivers/video/msm/gpu/kgsl/kgsl_yamato.c +++ /dev/null @@ -1,1004 +0,0 @@ -/* - * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 - * Copyright (c) 2008-2009 QUALCOMM USA, INC. - * - * All source code in this file is licensed under the following license - * - * 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, you can find it at http://www.fsf.org - */ -#include -#include -#include -#include -#include -#include - -#include "kgsl.h" -#include "kgsl_log.h" -#include "kgsl_pm4types.h" -#include "kgsl_cmdstream.h" - -#include "yamato_reg.h" - -#define GSL_RBBM_INT_MASK \ - (RBBM_INT_CNTL__RDERR_INT_MASK | \ - RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK) - -#define GSL_SQ_INT_MASK \ - (SQ_INT_CNTL__PS_WATCHDOG_MASK | \ - SQ_INT_CNTL__VS_WATCHDOG_MASK) - -/* Yamato MH arbiter config*/ -#define KGSL_CFG_YAMATO_MHARB \ - (0x10 \ - | (0 << MH_ARBITER_CONFIG__SAME_PAGE_GRANULARITY__SHIFT) \ - | (1 << MH_ARBITER_CONFIG__L1_ARB_ENABLE__SHIFT) \ - | (1 << MH_ARBITER_CONFIG__L1_ARB_HOLD_ENABLE__SHIFT) \ - | (0 << MH_ARBITER_CONFIG__L2_ARB_CONTROL__SHIFT) \ - | (1 << MH_ARBITER_CONFIG__PAGE_SIZE__SHIFT) \ - | (1 << MH_ARBITER_CONFIG__TC_REORDER_ENABLE__SHIFT) \ - | (1 << MH_ARBITER_CONFIG__TC_ARB_HOLD_ENABLE__SHIFT) \ - | (0 << MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT_ENABLE__SHIFT) \ - | (0x8 << MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT__SHIFT) \ - | (1 << MH_ARBITER_CONFIG__CP_CLNT_ENABLE__SHIFT) \ - | (1 << MH_ARBITER_CONFIG__VGT_CLNT_ENABLE__SHIFT) \ - | (1 << MH_ARBITER_CONFIG__TC_CLNT_ENABLE__SHIFT) \ - | (1 << MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT) \ - | (1 << MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT)) - -void kgsl_register_dump(struct kgsl_device *device) -{ - unsigned int regValue; - - kgsl_yamato_regread(device, REG_RBBM_STATUS, ®Value); - KGSL_CMD_ERR("RBBM_STATUS = %8.8X\n", regValue); - kgsl_yamato_regread(device, REG_CP_RB_BASE, ®Value); - KGSL_CMD_ERR("CP_RB_BASE = %08x\n", regValue); - kgsl_yamato_regread(device, REG_CP_RB_CNTL, ®Value); - KGSL_CMD_ERR("CP_RB_CNTL = %08x\n", regValue); - kgsl_yamato_regread(device, REG_CP_RB_RPTR_ADDR, ®Value); - KGSL_CMD_ERR("CP_RB_RPTR_ADDR = %08x\n", regValue); - kgsl_yamato_regread(device, REG_CP_RB_RPTR, ®Value); - KGSL_CMD_ERR("CP_RB_RPTR = %08x\n", regValue); - kgsl_yamato_regread(device, REG_CP_RB_WPTR, ®Value); - KGSL_CMD_ERR("CP_RB_WPTR = %08x\n", regValue); - kgsl_yamato_regread(device, REG_CP_RB_RPTR_WR, ®Value); - KGSL_CMD_ERR("CP_RB_RPTR_WR = %08x\n", regValue); - kgsl_yamato_regread(device, REG_CP_INT_CNTL, ®Value); - KGSL_CMD_ERR("CP_INT_CNTL = %08x\n", regValue); - kgsl_yamato_regread(device, REG_CP_INT_STATUS, ®Value); - KGSL_CMD_ERR("CP_INT_STATUS = %08x\n", regValue); - kgsl_yamato_regread(device, REG_CP_ME_CNTL, ®Value); - KGSL_CMD_ERR("CP_ME_CNTL = %08x\n", regValue); - kgsl_yamato_regread(device, REG_CP_ME_STATUS, ®Value); - KGSL_CMD_ERR("CP_ME_STATUS = %08x\n", regValue); - kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE1, ®Value); - KGSL_CMD_ERR("RBBM_PM_OVERRIDE1 = %08x\n", regValue); - kgsl_yamato_regread(device, REG_RBBM_PM_OVERRIDE2, ®Value); - KGSL_CMD_ERR("RBBM_PM_OVERRIDE2 = %08x\n", regValue); - kgsl_yamato_regread(device, REG_RBBM_INT_CNTL, ®Value); - KGSL_CMD_ERR("RBBM_INT_CNTL = %08x\n", regValue); - kgsl_yamato_regread(device, REG_RBBM_INT_STATUS, ®Value); - KGSL_CMD_ERR("RBBM_INT_STATUS = %08x\n", regValue); - kgsl_yamato_regread(device, REG_MASTER_INT_SIGNAL, ®Value); - KGSL_CMD_ERR("MASTER_INT_SIGNAL = %08x\n", regValue); - kgsl_yamato_regread(device, REG_CP_IB1_BASE, ®Value); - KGSL_CMD_ERR("CP_IB1_BASE = %08x\n", regValue); - kgsl_yamato_regread(device, REG_CP_IB1_BUFSZ, ®Value); - KGSL_CMD_ERR("CP_IB1_BUFSZ = %08x\n", regValue); - kgsl_yamato_regread(device, REG_CP_IB2_BASE, ®Value); - KGSL_CMD_ERR("CP_IB2_BASE = %08x\n", regValue); - kgsl_yamato_regread(device, REG_CP_IB2_BUFSZ, ®Value); - KGSL_CMD_ERR("CP_IB2_BUFSZ = %08x\n", regValue); - kgsl_yamato_regread(device, REG_CP_STAT, ®Value); - KGSL_CMD_ERR("CP_STAT = %08x\n", regValue); - kgsl_yamato_regread(device, REG_SCRATCH_REG0, ®Value); - KGSL_CMD_ERR("SCRATCH_REG0 = %08x\n", regValue); - kgsl_yamato_regread(device, REG_COHER_SIZE_PM4, ®Value); - KGSL_CMD_ERR("COHER_SIZE_PM4 = %08x\n", regValue); - kgsl_yamato_regread(device, REG_COHER_BASE_PM4, ®Value); - KGSL_CMD_ERR("COHER_BASE_PM4 = %08x\n", regValue); - kgsl_yamato_regread(device, REG_COHER_STATUS_PM4, ®Value); - KGSL_CMD_ERR("COHER_STATUS_PM4 = %08x\n", regValue); - kgsl_yamato_regread(device, REG_RBBM_READ_ERROR, ®Value); - KGSL_CMD_ERR("RBBM_READ_ERROR = %08x\n", regValue); - kgsl_yamato_regread(device, REG_MH_AXI_ERROR, ®Value); - KGSL_CMD_ERR("MH_AXI_ERROR = %08x\n", regValue); -} - -static int kgsl_yamato_gmeminit(struct kgsl_device *device) -{ - union reg_rb_edram_info rb_edram_info; - unsigned int gmem_size; - unsigned int edram_value = 0; - - /* make sure edram range is aligned to size */ - BUG_ON(device->gmemspace.gpu_base & (device->gmemspace.sizebytes - 1)); - - /* get edram_size value equivalent */ - gmem_size = (device->gmemspace.sizebytes >> 14); - while (gmem_size >>= 1) - edram_value++; - - rb_edram_info.val = 0; - - rb_edram_info.f.edram_size = edram_value; - rb_edram_info.f.edram_mapping_mode = 0; /* EDRAM_MAP_UPPER */ - /* must be aligned to size */ - rb_edram_info.f.edram_range = (device->gmemspace.gpu_base >> 14); - - kgsl_yamato_regwrite(device, REG_RB_EDRAM_INFO, rb_edram_info.val); - - return 0; -} - -static int kgsl_yamato_gmemclose(struct kgsl_device *device) -{ - kgsl_yamato_regwrite(device, REG_RB_EDRAM_INFO, 0x00000000); - - return 0; -} - -void kgsl_yamato_rbbm_intrcallback(struct kgsl_device *device) -{ - unsigned int status = 0; - unsigned int rderr = 0; - - KGSL_DRV_VDBG("enter (device=%p)\n", device); - - kgsl_yamato_regread(device, REG_RBBM_INT_STATUS, &status); - - if (status & RBBM_INT_CNTL__RDERR_INT_MASK) { - kgsl_yamato_regread(device, REG_RBBM_READ_ERROR, &rderr); - KGSL_DRV_FATAL("rbbm read error interrupt: %08x\n", rderr); - } else if (status & RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK) { - KGSL_DRV_DBG("rbbm display update interrupt\n"); - } else if (status & RBBM_INT_CNTL__GUI_IDLE_INT_MASK) { - KGSL_DRV_DBG("rbbm gui idle interrupt\n"); - } else { - KGSL_CMD_DBG("bad bits in REG_CP_INT_STATUS %08x\n", status); - } - - status &= GSL_RBBM_INT_MASK; - kgsl_yamato_regwrite(device, REG_RBBM_INT_ACK, status); - - KGSL_DRV_VDBG("return\n"); -} - -void kgsl_yamato_sq_intrcallback(struct kgsl_device *device) -{ - unsigned int status = 0; - - KGSL_DRV_VDBG("enter (device=%p)\n", device); - - kgsl_yamato_regread(device, REG_SQ_INT_STATUS, &status); - - if (status & SQ_INT_CNTL__PS_WATCHDOG_MASK) - KGSL_DRV_DBG("sq ps watchdog interrupt\n"); - else if (status & SQ_INT_CNTL__VS_WATCHDOG_MASK) - KGSL_DRV_DBG("sq vs watchdog interrupt\n"); - else - KGSL_DRV_DBG("bad bits in REG_SQ_INT_STATUS %08x\n", status); - - - status &= GSL_SQ_INT_MASK; - kgsl_yamato_regwrite(device, REG_SQ_INT_ACK, status); - - KGSL_DRV_VDBG("return\n"); -} - -irqreturn_t kgsl_yamato_isr(int irq, void *data) -{ - irqreturn_t result = IRQ_NONE; - - struct kgsl_device *device = &kgsl_driver.yamato_device; - unsigned int status; - - kgsl_yamato_regread(device, REG_MASTER_INT_SIGNAL, &status); - - if (status & MASTER_INT_SIGNAL__MH_INT_STAT) { - kgsl_mh_intrcallback(device); - result = IRQ_HANDLED; - } - - if (status & MASTER_INT_SIGNAL__CP_INT_STAT) { - kgsl_cp_intrcallback(device); - result = IRQ_HANDLED; - } - - if (status & MASTER_INT_SIGNAL__RBBM_INT_STAT) { - kgsl_yamato_rbbm_intrcallback(device); - result = IRQ_HANDLED; - } - - if (status & MASTER_INT_SIGNAL__SQ_INT_STAT) { - kgsl_yamato_sq_intrcallback(device); - result = IRQ_HANDLED; - } - - - return result; -} - -int kgsl_yamato_cleanup_pt(struct kgsl_device *device, - struct kgsl_pagetable *pagetable) -{ - kgsl_mmu_unmap(pagetable, device->ringbuffer.buffer_desc.gpuaddr, - device->ringbuffer.buffer_desc.size); - - kgsl_mmu_unmap(pagetable, device->ringbuffer.memptrs_desc.gpuaddr, - device->ringbuffer.memptrs_desc.size); - - kgsl_mmu_unmap(pagetable, device->memstore.gpuaddr, - device->memstore.size); - - kgsl_mmu_unmap(pagetable, device->mmu.dummyspace.gpuaddr, - device->mmu.dummyspace.size); - - return 0; -} - -int kgsl_yamato_setup_pt(struct kgsl_device *device, - struct kgsl_pagetable *pagetable) -{ - int result = 0; - unsigned int gpuaddr; - - BUG_ON(device->ringbuffer.buffer_desc.physaddr == 0); - BUG_ON(device->ringbuffer.memptrs_desc.physaddr == 0); - BUG_ON(device->memstore.physaddr == 0); - BUG_ON(device->mmu.dummyspace.physaddr == 0); - - result = kgsl_mmu_map(pagetable, - device->ringbuffer.buffer_desc.physaddr, - device->ringbuffer.buffer_desc.size, - GSL_PT_PAGE_RV, &gpuaddr, - KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN4K); - - if (result) - goto error; - - if (device->ringbuffer.buffer_desc.gpuaddr == 0) - device->ringbuffer.buffer_desc.gpuaddr = gpuaddr; - BUG_ON(device->ringbuffer.buffer_desc.gpuaddr != gpuaddr); - - result = kgsl_mmu_map(pagetable, - device->ringbuffer.memptrs_desc.physaddr, - device->ringbuffer.memptrs_desc.size, - GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, &gpuaddr, - KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN4K); - if (result) - goto unmap_buffer_desc; - - if (device->ringbuffer.memptrs_desc.gpuaddr == 0) - device->ringbuffer.memptrs_desc.gpuaddr = gpuaddr; - BUG_ON(device->ringbuffer.memptrs_desc.gpuaddr != gpuaddr); - - result = kgsl_mmu_map(pagetable, device->memstore.physaddr, - device->memstore.size, - GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, &gpuaddr, - KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN4K); - if (result) - goto unmap_memptrs_desc; - - if (device->memstore.gpuaddr == 0) - device->memstore.gpuaddr = gpuaddr; - BUG_ON(device->memstore.gpuaddr != gpuaddr); - - result = kgsl_mmu_map(pagetable, - device->mmu.dummyspace.physaddr, - device->mmu.dummyspace.size, - GSL_PT_PAGE_RV | GSL_PT_PAGE_WV, &gpuaddr, - KGSL_MEMFLAGS_CONPHYS | KGSL_MEMFLAGS_ALIGN4K); - - if (result) - goto unmap_memstore_desc; - - if (device->mmu.dummyspace.gpuaddr == 0) - device->mmu.dummyspace.gpuaddr = gpuaddr; - BUG_ON(device->mmu.dummyspace.gpuaddr != gpuaddr); - - return result; - -unmap_memstore_desc: - kgsl_mmu_unmap(pagetable, device->memstore.gpuaddr, - device->memstore.size); - -unmap_memptrs_desc: - kgsl_mmu_unmap(pagetable, device->ringbuffer.memptrs_desc.gpuaddr, - device->ringbuffer.memptrs_desc.size); -unmap_buffer_desc: - kgsl_mmu_unmap(pagetable, device->ringbuffer.buffer_desc.gpuaddr, - device->ringbuffer.buffer_desc.size); -error: - return result; - -} - -#ifdef CONFIG_MSM_KGSL_MMU -int kgsl_yamato_setstate(struct kgsl_device *device, uint32_t flags) -{ - unsigned int link[32]; - unsigned int *cmds = &link[0]; - int sizedwords = 0; - unsigned int mh_mmu_invalidate = 0x00000003; /*invalidate all and tc */ - - KGSL_MEM_DBG("device %p ctxt %p pt %p\n", - device, - device->drawctxt_active, - device->mmu.hwpagetable); - /* if possible, set via command stream, - * otherwise set via direct register writes - */ - if (device->drawctxt_active) { - KGSL_MEM_DBG("cmds\n"); - if (flags & KGSL_MMUFLAGS_PTUPDATE) { - /* wait for graphics pipe to be idle */ - *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmds++ = 0x00000000; - - /* set page table base */ - *cmds++ = pm4_type0_packet(REG_MH_MMU_PT_BASE, 1); - *cmds++ = device->mmu.hwpagetable->base.gpuaddr; - sizedwords += 4; - } - - if (flags & KGSL_MMUFLAGS_TLBFLUSH) { - *cmds++ = pm4_type0_packet(REG_MH_MMU_INVALIDATE, 1); - *cmds++ = mh_mmu_invalidate; - sizedwords += 2; - } - - if (flags & KGSL_MMUFLAGS_PTUPDATE) { - /* HW workaround: to resolve MMU page fault interrupts - * caused by the VGT.It prevents the CP PFP from filling - * the VGT DMA request fifo too early,thereby ensuring - * that the VGT will not fetch vertex/bin data until - * after the page table base register has been updated. - * - * Two null DRAW_INDX_BIN packets are inserted right - * after the page table base update, followed by a - * wait for idle. The null packets will fill up the - * VGT DMA request fifo and prevent any further - * vertex/bin updates from occurring until the wait - * has finished. */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = (0x4 << 16) | - (REG_PA_SU_SC_MODE_CNTL - 0x2000); - *cmds++ = 0; /* disable faceness generation */ - *cmds++ = pm4_type3_packet(PM4_SET_BIN_BASE_OFFSET, 1); - *cmds++ = device->mmu.dummyspace.gpuaddr; - *cmds++ = pm4_type3_packet(PM4_DRAW_INDX_BIN, 6); - *cmds++ = 0; /* viz query info */ - *cmds++ = 0x0003C004; /* draw indicator */ - *cmds++ = 0; /* bin base */ - *cmds++ = 3; /* bin size */ - *cmds++ = device->mmu.dummyspace.gpuaddr; /* dma base */ - *cmds++ = 6; /* dma size */ - *cmds++ = pm4_type3_packet(PM4_DRAW_INDX_BIN, 6); - *cmds++ = 0; /* viz query info */ - *cmds++ = 0x0003C004; /* draw indicator */ - *cmds++ = 0; /* bin base */ - *cmds++ = 3; /* bin size */ - /* dma base */ - *cmds++ = device->mmu.dummyspace.gpuaddr; - *cmds++ = 6; /* dma size */ - *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmds++ = 0x00000000; - sizedwords += 21; - } - - if (flags & (KGSL_MMUFLAGS_PTUPDATE | KGSL_MMUFLAGS_TLBFLUSH)) { - *cmds++ = pm4_type3_packet(PM4_INVALIDATE_STATE, 1); - *cmds++ = 0x7fff; /* invalidate all base pointers */ - sizedwords += 2; - } - - kgsl_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, - &link[0], sizedwords); - } else { - KGSL_MEM_DBG("regs\n"); - - if (flags & KGSL_MMUFLAGS_PTUPDATE) { - kgsl_yamato_idle(device, KGSL_TIMEOUT_DEFAULT); - kgsl_yamato_regwrite(device, REG_MH_MMU_PT_BASE, - device->mmu.hwpagetable->base.gpuaddr); - } - - if (flags & KGSL_MMUFLAGS_TLBFLUSH) { - kgsl_yamato_regwrite(device, REG_MH_MMU_INVALIDATE, - mh_mmu_invalidate); - } - } - - return 0; -} -#endif - -static unsigned int -kgsl_yamato_getchipid(struct kgsl_device *device) -{ - unsigned int chipid; - unsigned int coreid, majorid, minorid, patchid, revid; - - /* YDX */ - kgsl_yamato_regread(device, REG_RBBM_PERIPHID1, &coreid); - coreid &= 0xF; - - kgsl_yamato_regread(device, REG_RBBM_PERIPHID2, &majorid); - majorid = (majorid >> 4) & 0xF; - - kgsl_yamato_regread(device, REG_RBBM_PATCH_RELEASE, &revid); - /* this is a 16bit field, but extremely unlikely it would ever get - * this high - */ - minorid = ((revid >> 0) & 0xFF); - - - patchid = ((revid >> 16) & 0xFF); - - chipid = ((coreid << 24) | (majorid << 16) | - (minorid << 8) | (patchid << 0)); - - /* Hardware revision 211 (8650) returns the wrong chip ID */ - if (chipid == KGSL_CHIPID_YAMATODX_REV21) - chipid = KGSL_CHIPID_YAMATODX_REV211; - - return chipid; -} - -int kgsl_yamato_init(struct kgsl_device *device, struct kgsl_devconfig *config) -{ - int status = -EINVAL; - int init_reftimestamp = 0x7fffffff; - struct kgsl_memregion *regspace = &device->regspace; - unsigned int memflags = KGSL_MEMFLAGS_ALIGNPAGE | KGSL_MEMFLAGS_CONPHYS; - - KGSL_DRV_VDBG("enter (device=%p, config=%p)\n", device, config); - - if (device->flags & KGSL_FLAGS_INITIALIZED) { - KGSL_DRV_VDBG("return %d\n", 0); - return 0; - } - memset(device, 0, sizeof(*device)); - - init_waitqueue_head(&device->ib1_wq); - - memcpy(regspace, &config->regspace, sizeof(device->regspace)); - if (regspace->mmio_phys_base == 0 || regspace->sizebytes == 0) { - KGSL_DRV_ERR("dev %d invalid regspace\n", device->id); - goto error; - } - if (!request_mem_region(regspace->mmio_phys_base, - regspace->sizebytes, DRIVER_NAME)) { - KGSL_DRV_ERR("request_mem_region failed for register memory\n"); - status = -ENODEV; - goto error; - } - - regspace->mmio_virt_base = ioremap(regspace->mmio_phys_base, - regspace->sizebytes); - KGSL_MEM_INFO("ioremap(regs) = %p\n", regspace->mmio_virt_base); - if (regspace->mmio_virt_base == NULL) { - KGSL_DRV_ERR("ioremap failed for register memory\n"); - status = -ENODEV; - goto error_release_mem; - } - - KGSL_DRV_INFO("dev %d regs phys 0x%08x size 0x%08x virt %p\n", - device->id, regspace->mmio_phys_base, - regspace->sizebytes, regspace->mmio_virt_base); - - memcpy(&device->gmemspace, &config->gmemspace, - sizeof(device->gmemspace)); - - device->id = KGSL_DEVICE_YAMATO; - - if (config->mmu_config) { - device->mmu.config = config->mmu_config; - device->mmu.mpu_base = config->mpu_base; - device->mmu.mpu_range = config->mpu_range; - device->mmu.va_base = config->va_base; - device->mmu.va_range = config->va_range; - } - - device->chip_id = kgsl_yamato_getchipid(device); - - /*We need to make sure all blocks are powered up and clocked before - *issuing a soft reset. The overrides will be turned off (set to 0) - *later in kgsl_yamato_start. - */ - kgsl_yamato_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0xfffffffe); - kgsl_yamato_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xffffffff); - - kgsl_yamato_regwrite(device, REG_RBBM_SOFT_RESET, 0xFFFFFFFF); - msleep(50); - kgsl_yamato_regwrite(device, REG_RBBM_SOFT_RESET, 0x00000000); - - kgsl_yamato_regwrite(device, REG_RBBM_CNTL, 0x00004442); - - kgsl_yamato_regwrite(device, REG_MH_ARBITER_CONFIG, - KGSL_CFG_YAMATO_MHARB); - - kgsl_yamato_regwrite(device, REG_SQ_VS_PROGRAM, 0x00000000); - kgsl_yamato_regwrite(device, REG_SQ_PS_PROGRAM, 0x00000000); - - - status = kgsl_mmu_init(device); - if (status != 0) { - status = -ENODEV; - goto error_iounmap; - } - - status = kgsl_cmdstream_init(device); - if (status != 0) { - status = -ENODEV; - goto error_close_mmu; - } - - status = kgsl_sharedmem_alloc(memflags, sizeof(device->memstore), - &device->memstore); - if (status != 0) { - status = -ENODEV; - goto error_close_cmdstream; - } - kgsl_sharedmem_set(&device->memstore, 0, 0, device->memstore.size); - - kgsl_sharedmem_write(&device->memstore, - KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), - &init_reftimestamp, 4); - - kgsl_yamato_regwrite(device, REG_RBBM_DEBUG, 0x00080000); - pr_info("msm_kgsl: initilized dev=%d mmu=%s\n", device->id, - kgsl_mmu_isenabled(&device->mmu) ? "on" : "off"); - - device->flags |= KGSL_FLAGS_INITIALIZED; - return 0; - -error_close_cmdstream: - kgsl_cmdstream_close(device); -error_close_mmu: - kgsl_mmu_close(device); -error_iounmap: - iounmap(regspace->mmio_virt_base); - regspace->mmio_virt_base = NULL; -error_release_mem: - release_mem_region(regspace->mmio_phys_base, regspace->sizebytes); -error: - return status; -} - -int kgsl_yamato_close(struct kgsl_device *device) -{ - struct kgsl_memregion *regspace = &device->regspace; - - if (device->memstore.hostptr) - kgsl_sharedmem_free(&device->memstore); - - kgsl_mmu_close(device); - - kgsl_cmdstream_close(device); - - if (regspace->mmio_virt_base != NULL) { - KGSL_MEM_INFO("iounmap(regs) = %p\n", regspace->mmio_virt_base); - iounmap(regspace->mmio_virt_base); - regspace->mmio_virt_base = NULL; - release_mem_region(regspace->mmio_phys_base, - regspace->sizebytes); - } - - KGSL_DRV_VDBG("return %d\n", 0); - device->flags &= ~KGSL_FLAGS_INITIALIZED; - return 0; -} - -int kgsl_yamato_start(struct kgsl_device *device, uint32_t flags) -{ - int status = -EINVAL; - - KGSL_DRV_VDBG("enter (device=%p)\n", device); - - if (!(device->flags & KGSL_FLAGS_INITIALIZED)) { - KGSL_DRV_ERR("Trying to start uninitialized device.\n"); - return -EINVAL; - } - - device->refcnt++; - - if (device->flags & KGSL_FLAGS_STARTED) { - KGSL_DRV_VDBG("already started"); - return 0; - } - - kgsl_yamato_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0); - kgsl_yamato_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0); - - KGSL_DRV_DBG("enabling RBBM interrupts mask 0x%08lx\n", - GSL_RBBM_INT_MASK); - kgsl_yamato_regwrite(device, REG_RBBM_INT_CNTL, GSL_RBBM_INT_MASK); - - /* make sure SQ interrupts are disabled */ - kgsl_yamato_regwrite(device, REG_SQ_INT_CNTL, 0); - - kgsl_yamato_gmeminit(device); - - status = kgsl_ringbuffer_init(device); - if (status != 0) { - kgsl_yamato_stop(device); - return status; - } - - status = kgsl_drawctxt_init(device); - if (status != 0) { - kgsl_yamato_stop(device); - return status; - } - - device->flags |= KGSL_FLAGS_STARTED; - - KGSL_DRV_VDBG("return %d\n", status); - return status; -} - -int kgsl_yamato_stop(struct kgsl_device *device) -{ - if (device->flags & KGSL_FLAGS_STARTED) { - - kgsl_yamato_regwrite(device, REG_RBBM_INT_CNTL, 0); - - kgsl_yamato_regwrite(device, REG_SQ_INT_CNTL, 0); - - kgsl_drawctxt_close(device); - - kgsl_ringbuffer_close(&device->ringbuffer); - - kgsl_yamato_gmemclose(device); - - device->flags &= ~KGSL_FLAGS_STARTED; - } - - return 0; -} - -int kgsl_yamato_getproperty(struct kgsl_device *device, - enum kgsl_property_type type, - void *value, - unsigned int sizebytes) -{ - int status = -EINVAL; - - switch (type) { - case KGSL_PROP_DEVICE_INFO: - { - struct kgsl_devinfo devinfo; - - if (sizebytes != sizeof(devinfo)) { - status = -EINVAL; - break; - } - - memset(&devinfo, 0, sizeof(devinfo)); - devinfo.device_id = device->id; - devinfo.chip_id = device->chip_id; - devinfo.mmu_enabled = kgsl_mmu_isenabled(&device->mmu); - devinfo.gmem_hostbaseaddr = - (unsigned int)device->gmemspace.mmio_virt_base; - devinfo.gmem_gpubaseaddr = device->gmemspace.gpu_base; - devinfo.gmem_sizebytes = device->gmemspace.sizebytes; - - if (copy_to_user(value, &devinfo, sizeof(devinfo)) != - 0) { - status = -EFAULT; - break; - } - status = 0; - } - break; - case KGSL_PROP_DEVICE_SHADOW: - { - struct kgsl_shadowprop shadowprop; - - if (sizebytes != sizeof(shadowprop)) { - status = -EINVAL; - break; - } - memset(&shadowprop, 0, sizeof(shadowprop)); - if (device->memstore.hostptr) { - /*NOTE: with mmu enabled, gpuaddr doesn't mean - * anything to mmap(). - */ - shadowprop.gpuaddr = device->memstore.physaddr; - shadowprop.size = device->memstore.size; - shadowprop.flags = KGSL_FLAGS_INITIALIZED; - } - if (copy_to_user(value, &shadowprop, - sizeof(shadowprop))) { - status = -EFAULT; - break; - } - status = 0; - } - break; - case KGSL_PROP_MMU_ENABLE: - { -#ifdef CONFIG_MSM_KGSL_MMU - int mmuProp = 1; -#else - int mmuProp = 0; -#endif - if (sizebytes != sizeof(int)) { - status = -EINVAL; - break; - } - if (copy_to_user(value, &mmuProp, sizeof(mmuProp))) { - status = -EFAULT; - break; - } - status = 0; - } - break; - case KGSL_PROP_INTERRUPT_WAITS: - { - int int_waits = 1; - if (sizebytes != sizeof(int)) { - status = -EINVAL; - break; - } - if (copy_to_user(value, &int_waits, sizeof(int))) { - status = -EFAULT; - break; - } - status = 0; - } - break; - default: - status = -EINVAL; - } - - return status; -} - -/* Note: This is either called from the standby timer, or while holding the - * driver mutex. - * - * The reader may obseve that this function may be called without holding the - * driver mutex (in the timer), which can cause the ringbuffer write pointer - * to change, when a user submits a command. However, the user must be holding - * the driver mutex when doing so, and then must - * have canceled the timer. If the timer was executing at the time of - * cancellation, the active flag would have been cleared, which the user - * ioctl checks for after cancelling the timer. - */ -bool kgsl_yamato_is_idle(struct kgsl_device *device) -{ - struct kgsl_ringbuffer *rb = &device->ringbuffer; - unsigned int rbbm_status; - - BUG_ON(!(rb->flags & KGSL_FLAGS_STARTED)); - - GSL_RB_GET_READPTR(rb, &rb->rptr); - if (rb->rptr == rb->wptr) { - kgsl_yamato_regread(device, REG_RBBM_STATUS, &rbbm_status); - if (rbbm_status == 0x110) - return true; - } - return false; -} - -int kgsl_yamato_idle(struct kgsl_device *device, unsigned int timeout) -{ - int status = -EINVAL; - struct kgsl_ringbuffer *rb = &device->ringbuffer; - struct kgsl_mmu_debug mmu_dbg; - unsigned int rbbm_status; - int idle_count = 0; -#define IDLE_COUNT_MAX 1000000 - - KGSL_DRV_VDBG("enter (device=%p, timeout=%d)\n", device, timeout); - - (void)timeout; - - /* first, wait until the CP has consumed all the commands in - * the ring buffer - */ - if (rb->flags & KGSL_FLAGS_STARTED) { - do { - idle_count++; - GSL_RB_GET_READPTR(rb, &rb->rptr); - - } while (rb->rptr != rb->wptr && idle_count < IDLE_COUNT_MAX); - if (idle_count == IDLE_COUNT_MAX) - goto err; - } - /* now, wait for the GPU to finish its operations */ - for (idle_count = 0; idle_count < IDLE_COUNT_MAX; idle_count++) { - kgsl_yamato_regread(device, REG_RBBM_STATUS, &rbbm_status); - - if (rbbm_status == 0x110) { - status = 0; - goto done; - } - } - -err: - KGSL_DRV_ERR("spun too long waiting for RB to idle\n"); - kgsl_register_dump(device); - kgsl_ringbuffer_dump(rb); - kgsl_mmu_debug(&device->mmu, &mmu_dbg); - BUG(); - -done: - KGSL_DRV_VDBG("return %d\n", status); - - return status; -} - -int kgsl_yamato_regread(struct kgsl_device *device, unsigned int offsetwords, - unsigned int *value) -{ - unsigned int *reg; - - if (offsetwords*sizeof(uint32_t) >= device->regspace.sizebytes) { - KGSL_DRV_ERR("invalid offset %d\n", offsetwords); - return -ERANGE; - } - - reg = (unsigned int *)(device->regspace.mmio_virt_base - + (offsetwords << 2)); - *value = readl(reg); - - return 0; -} - -int kgsl_yamato_regwrite(struct kgsl_device *device, unsigned int offsetwords, - unsigned int value) -{ - unsigned int *reg; - - if (offsetwords*sizeof(uint32_t) >= device->regspace.sizebytes) { - KGSL_DRV_ERR("invalid offset %d\n", offsetwords); - return -ERANGE; - } - - reg = (unsigned int *)(device->regspace.mmio_virt_base - + (offsetwords << 2)); - writel(value, reg); - - return 0; -} - -static inline int _wait_timestamp(struct kgsl_device *device, - unsigned int timestamp, - unsigned int msecs) -{ - long status; - - status = wait_event_interruptible_timeout(device->ib1_wq, - kgsl_cmdstream_check_timestamp(device, timestamp), - msecs_to_jiffies(msecs)); - - if (status > 0) - status = 0; - else if (status == 0) { - if (!kgsl_cmdstream_check_timestamp(device, timestamp)) { - status = -ETIMEDOUT; - kgsl_register_dump(device); - } - } - - return (int)status; -} - -/* MUST be called with the kgsl_driver.mutex held */ -int kgsl_yamato_waittimestamp(struct kgsl_device *device, - unsigned int timestamp, - unsigned int msecs) -{ - long status = 0; - uint32_t ref_ts; - int enableflag = 1; - unsigned int cmd[2]; - - KGSL_DRV_INFO("enter (device=%p,timestamp=%d,timeout=0x%08x)\n", - device, timestamp, msecs); - - if (!kgsl_cmdstream_check_timestamp(device, timestamp)) { - kgsl_sharedmem_read(&device->memstore, &ref_ts, - KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), 4); - if (timestamp_cmp(ref_ts, timestamp)) { - kgsl_sharedmem_write(&device->memstore, - KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), - ×tamp, 4); - } - - cmd[0] = pm4_type3_packet(PM4_INTERRUPT, 1); - cmd[1] = CP_INT_CNTL__IB1_INT_MASK; - kgsl_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NO_TS_CMP, - cmd, 2); - kgsl_sharedmem_write(&device->memstore, - KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable), - &enableflag, 4); - - mutex_unlock(&kgsl_driver.mutex); - status = _wait_timestamp(device, timestamp, msecs); - mutex_lock(&kgsl_driver.mutex); - } - - KGSL_DRV_INFO("return %ld\n", status); - return (int)status; -} - -int kgsl_yamato_runpending(struct kgsl_device *device) -{ - if (device->flags & KGSL_FLAGS_INITIALIZED) - kgsl_cmdstream_memqueue_drain(device); - return 0; -} - -int __init kgsl_yamato_config(struct kgsl_devconfig *devconfig, - struct platform_device *pdev) -{ - int result = 0; - struct resource *res = NULL; - - memset(devconfig, 0, sizeof(*devconfig)); - - /*find memory regions */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "kgsl_reg_memory"); - if (res == NULL) { - KGSL_DRV_ERR("platform_get_resource_byname failed\n"); - result = -EINVAL; - goto done; - } - KGSL_DRV_DBG("registers at %08x to %08x\n", res->start, res->end); - devconfig->regspace.mmio_phys_base = res->start; - devconfig->regspace.sizebytes = resource_size(res); - - devconfig->gmemspace.gpu_base = 0; - devconfig->gmemspace.sizebytes = SZ_256K; - - /*note: for all of these behavior masks: - * 0 = do not translate - * 1 = translate within va_range, otherwise use physical - * 2 = translate within va_range, otherwise fault - */ - devconfig->mmu_config = 1 /* mmu enable */ - | (2 << MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT) - | (2 << MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT) - | (2 << MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT) - | (2 << MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT) - | (2 << MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT) - | (2 << MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT) - | (2 << MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT) - | (2 << MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT) - | (2 << MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT) - | (2 << MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT) - | (2 << MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT); - - /*TODO: these should probably be configurable from platform device - * stuff */ - devconfig->va_base = 0x66000000; - devconfig->va_range = SZ_128M; - - /* turn off memory protection unit by setting acceptable physical - * address range to include all pages. Apparrently MPU causing - * problems. - */ - devconfig->mpu_base = 0x00000000; - devconfig->mpu_range = 0xFFFFF000; - - result = 0; -done: - return result; -} diff --git a/drivers/video/msm/gpu/kgsl/yamato_reg.h b/drivers/video/msm/gpu/kgsl/yamato_reg.h deleted file mode 100644 index 828b95aa61d6f..0000000000000 --- a/drivers/video/msm/gpu/kgsl/yamato_reg.h +++ /dev/null @@ -1,400 +0,0 @@ -/* - * (C) Copyright Advanced Micro Devices, Inc. 2002, 2007 - * Copyright (c) 2008-2009 QUALCOMM USA, INC. - * - * All source code in this file is licensed under the following license - * - * 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, you can find it at http://www.fsf.org - */ -#ifndef _YAMATO_REG_H -#define _YAMATO_REG_H - -enum VGT_EVENT_TYPE { - VS_DEALLOC = 0, - PS_DEALLOC = 1, - VS_DONE_TS = 2, - PS_DONE_TS = 3, - CACHE_FLUSH_TS = 4, - CONTEXT_DONE = 5, - CACHE_FLUSH = 6, - VIZQUERY_START = 7, - VIZQUERY_END = 8, - SC_WAIT_WC = 9, - RST_PIX_CNT = 13, - RST_VTX_CNT = 14, - TILE_FLUSH = 15, - CACHE_FLUSH_AND_INV_TS_EVENT = 20, - ZPASS_DONE = 21, - CACHE_FLUSH_AND_INV_EVENT = 22, - PERFCOUNTER_START = 23, - PERFCOUNTER_STOP = 24, - VS_FETCH_DONE = 27, - FACENESS_FLUSH = 28, -}; - -enum COLORFORMATX { - COLORX_4_4_4_4 = 0, - COLORX_1_5_5_5 = 1, - COLORX_5_6_5 = 2, - COLORX_8 = 3, - COLORX_8_8 = 4, - COLORX_8_8_8_8 = 5, - COLORX_S8_8_8_8 = 6, - COLORX_16_FLOAT = 7, - COLORX_16_16_FLOAT = 8, - COLORX_16_16_16_16_FLOAT = 9, - COLORX_32_FLOAT = 10, - COLORX_32_32_FLOAT = 11, - COLORX_32_32_32_32_FLOAT = 12, - COLORX_2_3_3 = 13, - COLORX_8_8_8 = 14, -}; - -enum SURFACEFORMAT { - FMT_1_REVERSE = 0, - FMT_1 = 1, - FMT_8 = 2, - FMT_1_5_5_5 = 3, - FMT_5_6_5 = 4, - FMT_6_5_5 = 5, - FMT_8_8_8_8 = 6, - FMT_2_10_10_10 = 7, - FMT_8_A = 8, - FMT_8_B = 9, - FMT_8_8 = 10, - FMT_Cr_Y1_Cb_Y0 = 11, - FMT_Y1_Cr_Y0_Cb = 12, - FMT_5_5_5_1 = 13, - FMT_8_8_8_8_A = 14, - FMT_4_4_4_4 = 15, - FMT_10_11_11 = 16, - FMT_11_11_10 = 17, - FMT_DXT1 = 18, - FMT_DXT2_3 = 19, - FMT_DXT4_5 = 20, - FMT_24_8 = 22, - FMT_24_8_FLOAT = 23, - FMT_16 = 24, - FMT_16_16 = 25, - FMT_16_16_16_16 = 26, - FMT_16_EXPAND = 27, - FMT_16_16_EXPAND = 28, - FMT_16_16_16_16_EXPAND = 29, - FMT_16_FLOAT = 30, - FMT_16_16_FLOAT = 31, - FMT_16_16_16_16_FLOAT = 32, - FMT_32 = 33, - FMT_32_32 = 34, - FMT_32_32_32_32 = 35, - FMT_32_FLOAT = 36, - FMT_32_32_FLOAT = 37, - FMT_32_32_32_32_FLOAT = 38, - FMT_32_AS_8 = 39, - FMT_32_AS_8_8 = 40, - FMT_16_MPEG = 41, - FMT_16_16_MPEG = 42, - FMT_8_INTERLACED = 43, - FMT_32_AS_8_INTERLACED = 44, - FMT_32_AS_8_8_INTERLACED = 45, - FMT_16_INTERLACED = 46, - FMT_16_MPEG_INTERLACED = 47, - FMT_16_16_MPEG_INTERLACED = 48, - FMT_DXN = 49, - FMT_8_8_8_8_AS_16_16_16_16 = 50, - FMT_DXT1_AS_16_16_16_16 = 51, - FMT_DXT2_3_AS_16_16_16_16 = 52, - FMT_DXT4_5_AS_16_16_16_16 = 53, - FMT_2_10_10_10_AS_16_16_16_16 = 54, - FMT_10_11_11_AS_16_16_16_16 = 55, - FMT_11_11_10_AS_16_16_16_16 = 56, - FMT_32_32_32_FLOAT = 57, - FMT_DXT3A = 58, - FMT_DXT5A = 59, - FMT_CTX1 = 60, - FMT_DXT3A_AS_1_1_1_1 = 61 -}; - -#define RB_EDRAM_INFO_EDRAM_SIZE_SIZE 4 -#define RB_EDRAM_INFO_EDRAM_MAPPING_MODE_SIZE 2 -#define RB_EDRAM_INFO_UNUSED0_SIZE 8 -#define RB_EDRAM_INFO_EDRAM_RANGE_SIZE 18 - -struct rb_edram_info_t { - unsigned int edram_size:RB_EDRAM_INFO_EDRAM_SIZE_SIZE; - unsigned int edram_mapping_mode:RB_EDRAM_INFO_EDRAM_MAPPING_MODE_SIZE; - unsigned int unused0:RB_EDRAM_INFO_UNUSED0_SIZE; - unsigned int edram_range:RB_EDRAM_INFO_EDRAM_RANGE_SIZE; -}; - -union reg_rb_edram_info { - unsigned int val:32; - struct rb_edram_info_t f; -}; - -#define CP_RB_CNTL_RB_BUFSZ_SIZE 6 -#define CP_RB_CNTL_UNUSED0_SIZE 2 -#define CP_RB_CNTL_RB_BLKSZ_SIZE 6 -#define CP_RB_CNTL_UNUSED1_SIZE 2 -#define CP_RB_CNTL_BUF_SWAP_SIZE 2 -#define CP_RB_CNTL_UNUSED2_SIZE 2 -#define CP_RB_CNTL_RB_POLL_EN_SIZE 1 -#define CP_RB_CNTL_UNUSED3_SIZE 6 -#define CP_RB_CNTL_RB_NO_UPDATE_SIZE 1 -#define CP_RB_CNTL_UNUSED4_SIZE 3 -#define CP_RB_CNTL_RB_RPTR_WR_ENA_SIZE 1 - -struct cp_rb_cntl_t { - unsigned int rb_bufsz:CP_RB_CNTL_RB_BUFSZ_SIZE; - unsigned int unused0:CP_RB_CNTL_UNUSED0_SIZE; - unsigned int rb_blksz:CP_RB_CNTL_RB_BLKSZ_SIZE; - unsigned int unused1:CP_RB_CNTL_UNUSED1_SIZE; - unsigned int buf_swap:CP_RB_CNTL_BUF_SWAP_SIZE; - unsigned int unused2:CP_RB_CNTL_UNUSED2_SIZE; - unsigned int rb_poll_en:CP_RB_CNTL_RB_POLL_EN_SIZE; - unsigned int unused3:CP_RB_CNTL_UNUSED3_SIZE; - unsigned int rb_no_update:CP_RB_CNTL_RB_NO_UPDATE_SIZE; - unsigned int unused4:CP_RB_CNTL_UNUSED4_SIZE; - unsigned int rb_rptr_wr_ena:CP_RB_CNTL_RB_RPTR_WR_ENA_SIZE; -}; - -union reg_cp_rb_cntl { - unsigned int val:32; - struct cp_rb_cntl_t f; -}; - -#define RB_COLOR_INFO__COLOR_FORMAT_MASK 0x0000000fL -#define RB_COPY_DEST_INFO__COPY_DEST_FORMAT__SHIFT 0x00000004 - - -#define SQ_INT_CNTL__PS_WATCHDOG_MASK 0x00000001L -#define SQ_INT_CNTL__VS_WATCHDOG_MASK 0x00000002L - -#define MH_INTERRUPT_MASK__AXI_READ_ERROR 0x00000001L -#define MH_INTERRUPT_MASK__AXI_WRITE_ERROR 0x00000002L -#define MH_INTERRUPT_MASK__MMU_PAGE_FAULT 0x00000004L - -#define RBBM_INT_CNTL__RDERR_INT_MASK 0x00000001L -#define RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK 0x00000002L -#define RBBM_INT_CNTL__GUI_IDLE_INT_MASK 0x00080000L - -#define RBBM_STATUS__CMDFIFO_AVAIL_MASK 0x0000001fL -#define RBBM_STATUS__TC_BUSY_MASK 0x00000020L -#define RBBM_STATUS__HIRQ_PENDING_MASK 0x00000100L -#define RBBM_STATUS__CPRQ_PENDING_MASK 0x00000200L -#define RBBM_STATUS__CFRQ_PENDING_MASK 0x00000400L -#define RBBM_STATUS__PFRQ_PENDING_MASK 0x00000800L -#define RBBM_STATUS__VGT_BUSY_NO_DMA_MASK 0x00001000L -#define RBBM_STATUS__RBBM_WU_BUSY_MASK 0x00004000L -#define RBBM_STATUS__CP_NRT_BUSY_MASK 0x00010000L -#define RBBM_STATUS__MH_BUSY_MASK 0x00040000L -#define RBBM_STATUS__MH_COHERENCY_BUSY_MASK 0x00080000L -#define RBBM_STATUS__SX_BUSY_MASK 0x00200000L -#define RBBM_STATUS__TPC_BUSY_MASK 0x00400000L -#define RBBM_STATUS__SC_CNTX_BUSY_MASK 0x01000000L -#define RBBM_STATUS__PA_BUSY_MASK 0x02000000L -#define RBBM_STATUS__VGT_BUSY_MASK 0x04000000L -#define RBBM_STATUS__SQ_CNTX17_BUSY_MASK 0x08000000L -#define RBBM_STATUS__SQ_CNTX0_BUSY_MASK 0x10000000L -#define RBBM_STATUS__RB_CNTX_BUSY_MASK 0x40000000L -#define RBBM_STATUS__GUI_ACTIVE_MASK 0x80000000L - -#define CP_INT_CNTL__SW_INT_MASK 0x00080000L -#define CP_INT_CNTL__T0_PACKET_IN_IB_MASK 0x00800000L -#define CP_INT_CNTL__OPCODE_ERROR_MASK 0x01000000L -#define CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK 0x02000000L -#define CP_INT_CNTL__RESERVED_BIT_ERROR_MASK 0x04000000L -#define CP_INT_CNTL__IB_ERROR_MASK 0x08000000L -#define CP_INT_CNTL__IB2_INT_MASK 0x20000000L -#define CP_INT_CNTL__IB1_INT_MASK 0x40000000L -#define CP_INT_CNTL__RB_INT_MASK 0x80000000L - -#define MASTER_INT_SIGNAL__MH_INT_STAT 0x00000020L -#define MASTER_INT_SIGNAL__SQ_INT_STAT 0x04000000L -#define MASTER_INT_SIGNAL__CP_INT_STAT 0x40000000L -#define MASTER_INT_SIGNAL__RBBM_INT_STAT 0x80000000L - -#define RB_EDRAM_INFO__EDRAM_SIZE_MASK 0x0000000fL -#define RB_EDRAM_INFO__EDRAM_RANGE_MASK 0xffffc000L - -#define MH_ARBITER_CONFIG__SAME_PAGE_GRANULARITY__SHIFT 0x00000006 -#define MH_ARBITER_CONFIG__L1_ARB_ENABLE__SHIFT 0x00000007 -#define MH_ARBITER_CONFIG__L1_ARB_HOLD_ENABLE__SHIFT 0x00000008 -#define MH_ARBITER_CONFIG__L2_ARB_CONTROL__SHIFT 0x00000009 -#define MH_ARBITER_CONFIG__PAGE_SIZE__SHIFT 0x0000000a -#define MH_ARBITER_CONFIG__TC_REORDER_ENABLE__SHIFT 0x0000000d -#define MH_ARBITER_CONFIG__TC_ARB_HOLD_ENABLE__SHIFT 0x0000000e -#define MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT_ENABLE__SHIFT 0x0000000f -#define MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT__SHIFT 0x00000010 -#define MH_ARBITER_CONFIG__CP_CLNT_ENABLE__SHIFT 0x00000016 -#define MH_ARBITER_CONFIG__VGT_CLNT_ENABLE__SHIFT 0x00000017 -#define MH_ARBITER_CONFIG__TC_CLNT_ENABLE__SHIFT 0x00000018 -#define MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT 0x00000019 -#define MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT 0x0000001a - -#define MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT 0x00000004 -#define MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT 0x00000006 -#define MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT 0x00000008 -#define MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT 0x0000000a -#define MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT 0x0000000c -#define MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT 0x0000000e -#define MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT 0x00000010 -#define MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT 0x00000012 -#define MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT 0x00000014 -#define MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT 0x00000016 -#define MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT 0x00000018 - -#define CP_RB_CNTL__RB_BUFSZ__SHIFT 0x00000000 -#define CP_RB_CNTL__RB_BLKSZ__SHIFT 0x00000008 -#define CP_RB_CNTL__RB_POLL_EN__SHIFT 0x00000014 -#define CP_RB_CNTL__RB_NO_UPDATE__SHIFT 0x0000001b - -#define RB_COLOR_INFO__COLOR_FORMAT__SHIFT 0x00000000 -#define RB_EDRAM_INFO__EDRAM_MAPPING_MODE__SHIFT 0x00000004 -#define RB_EDRAM_INFO__EDRAM_RANGE__SHIFT 0x0000000e - -#define REG_CP_CSQ_IB1_STAT 0x01FE -#define REG_CP_CSQ_IB2_STAT 0x01FF -#define REG_CP_CSQ_RB_STAT 0x01FD -#define REG_CP_DEBUG 0x01FC -#define REG_CP_IB1_BASE 0x0458 -#define REG_CP_IB1_BUFSZ 0x0459 -#define REG_CP_IB2_BASE 0x045A -#define REG_CP_IB2_BUFSZ 0x045B -#define REG_CP_INT_ACK 0x01F4 -#define REG_CP_INT_CNTL 0x01F2 -#define REG_CP_INT_STATUS 0x01F3 -#define REG_CP_ME_CNTL 0x01F6 -#define REG_CP_ME_RAM_DATA 0x01FA -#define REG_CP_ME_RAM_WADDR 0x01F8 -#define REG_CP_ME_STATUS 0x01F7 -#define REG_CP_PFP_UCODE_ADDR 0x00C0 -#define REG_CP_PFP_UCODE_DATA 0x00C1 -#define REG_CP_QUEUE_THRESHOLDS 0x01D5 -#define REG_CP_RB_BASE 0x01C0 -#define REG_CP_RB_CNTL 0x01C1 -#define REG_CP_RB_RPTR 0x01C4 -#define REG_CP_RB_RPTR_ADDR 0x01C3 -#define REG_CP_RB_RPTR_WR 0x01C7 -#define REG_CP_RB_WPTR 0x01C5 -#define REG_CP_RB_WPTR_BASE 0x01C8 -#define REG_CP_RB_WPTR_DELAY 0x01C6 -#define REG_CP_STAT 0x047F -#define REG_CP_STATE_DEBUG_DATA 0x01ED -#define REG_CP_STATE_DEBUG_INDEX 0x01EC -#define REG_CP_ST_BASE 0x044D -#define REG_CP_ST_BUFSZ 0x044E - -#define REG_MASTER_INT_SIGNAL 0x03B7 - -#define REG_MH_ARBITER_CONFIG 0x0A40 -#define REG_MH_INTERRUPT_CLEAR 0x0A44 -#define REG_MH_INTERRUPT_MASK 0x0A42 -#define REG_MH_INTERRUPT_STATUS 0x0A43 -#define REG_MH_MMU_CONFIG 0x0040 -#define REG_MH_MMU_INVALIDATE 0x0045 -#define REG_MH_MMU_MPU_BASE 0x0046 -#define REG_MH_MMU_MPU_END 0x0047 -#define REG_MH_MMU_PAGE_FAULT 0x0043 -#define REG_MH_MMU_PT_BASE 0x0042 -#define REG_MH_MMU_TRAN_ERROR 0x0044 -#define REG_MH_MMU_VA_RANGE 0x0041 - -#define REG_PA_CL_VPORT_XSCALE 0x210F -#define REG_PA_CL_VPORT_ZOFFSET 0x2114 -#define REG_PA_CL_VPORT_ZSCALE 0x2113 -#define REG_PA_CL_VTE_CNTL 0x2206 -#define REG_PA_SC_AA_MASK 0x2312 -#define REG_PA_SC_LINE_CNTL 0x2300 -#define REG_PA_SC_SCREEN_SCISSOR_BR 0x200F -#define REG_PA_SC_SCREEN_SCISSOR_TL 0x200E -#define REG_PA_SC_VIZ_QUERY 0x2293 -#define REG_PA_SC_VIZ_QUERY_STATUS 0x0C44 -#define REG_PA_SC_WINDOW_OFFSET 0x2080 -#define REG_PA_SC_WINDOW_SCISSOR_BR 0x2082 -#define REG_PA_SC_WINDOW_SCISSOR_TL 0x2081 -#define REG_PA_SU_FACE_DATA 0x0C86 -#define REG_PA_SU_POINT_SIZE 0x2280 -#define REG_PA_SU_POLY_OFFSET_BACK_OFFSET 0x2383 -#define REG_PA_SU_POLY_OFFSET_FRONT_SCALE 0x2380 -#define REG_PA_SU_SC_MODE_CNTL 0x2205 - -#define REG_RBBM_CNTL 0x003B -#define REG_RBBM_INT_ACK 0x03B6 -#define REG_RBBM_INT_CNTL 0x03B4 -#define REG_RBBM_INT_STATUS 0x03B5 -#define REG_RBBM_PATCH_RELEASE 0x0001 -#define REG_RBBM_PERIPHID1 0x03F9 -#define REG_RBBM_PERIPHID2 0x03FA -#define REG_RBBM_DEBUG 0x039B -#define REG_RBBM_PM_OVERRIDE1 0x039C -#define REG_RBBM_PM_OVERRIDE2 0x039D -#define REG_RBBM_READ_ERROR 0x03B3 -#define REG_RBBM_SOFT_RESET 0x003C -#define REG_RBBM_STATUS 0x05D0 - -#define REG_RB_COLORCONTROL 0x2202 -#define REG_RB_COLOR_DEST_MASK 0x2326 -#define REG_RB_COLOR_MASK 0x2104 -#define REG_RB_COPY_CONTROL 0x2318 -#define REG_RB_DEPTHCONTROL 0x2200 -#define REG_RB_EDRAM_INFO 0x0F02 -#define REG_RB_MODECONTROL 0x2208 -#define REG_RB_SURFACE_INFO 0x2000 - -#define REG_SCRATCH_ADDR 0x01DD -#define REG_SCRATCH_REG0 0x0578 -#define REG_SCRATCH_REG2 0x057A -#define REG_SCRATCH_UMSK 0x01DC - -#define REG_SQ_CF_BOOLEANS 0x4900 -#define REG_SQ_CF_LOOP 0x4908 -#define REG_SQ_GPR_MANAGEMENT 0x0D00 -#define REG_SQ_INST_STORE_MANAGMENT 0x0D02 -#define REG_SQ_INT_ACK 0x0D36 -#define REG_SQ_INT_CNTL 0x0D34 -#define REG_SQ_INT_STATUS 0x0D35 -#define REG_SQ_PROGRAM_CNTL 0x2180 -#define REG_SQ_PS_PROGRAM 0x21F6 -#define REG_SQ_VS_PROGRAM 0x21F7 -#define REG_SQ_WRAPPING_0 0x2183 -#define REG_SQ_WRAPPING_1 0x2184 - -#define REG_VGT_ENHANCE 0x2294 -#define REG_VGT_INDX_OFFSET 0x2102 -#define REG_VGT_MAX_VTX_INDX 0x2100 -#define REG_VGT_MIN_VTX_INDX 0x2101 - -#define REG_TP0_CHICKEN 0x0E1E -#define REG_TC_CNTL_STATUS 0x0E00 -#define REG_PA_SC_AA_CONFIG 0x2301 -#define REG_VGT_VERTEX_REUSE_BLOCK_CNTL 0x2316 -#define REG_SQ_INTERPOLATOR_CNTL 0x2182 -#define REG_RB_DEPTH_INFO 0x2002 -#define REG_COHER_DEST_BASE_0 0x2006 -#define REG_PA_SC_SCREEN_SCISSOR_BR 0x200F -#define REG_RB_FOG_COLOR 0x2109 -#define REG_RB_STENCILREFMASK_BF 0x210C -#define REG_PA_SC_LINE_STIPPLE 0x2283 -#define REG_SQ_PS_CONST 0x2308 -#define REG_VGT_VERTEX_REUSE_BLOCK_CNTL 0x2316 -#define REG_RB_DEPTH_CLEAR 0x231D -#define REG_RB_SAMPLE_COUNT_CTL 0x2324 -#define REG_SQ_CONSTANT_0 0x4000 -#define REG_SQ_FETCH_0 0x4800 - -#define REG_MH_AXI_ERROR 0xA45 -#define REG_COHER_BASE_PM4 0xA2A -#define REG_COHER_STATUS_PM4 0xA2B -#define REG_COHER_SIZE_PM4 0xA29 - -#endif /* _YAMATO_REG_H */ From 731c961f8410b8848a527dc76f0c56bc3e1c632b Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 9 Feb 2012 01:34:15 -0600 Subject: [PATCH 2081/2556] include: reset mdp header to original version w/ required additions to build ics TODO: most of this stuff relates to the rotator driver which qsd8k doesnt have, try and implement it in the current mdp driver --- drivers/video/msm/msm_fb.c | 2 +- include/linux/msm_mdp.h | 226 +++++++------------------------------ 2 files changed, 42 insertions(+), 186 deletions(-) diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index 513913526c1d8..45eb858382d54 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -655,7 +655,7 @@ static void msmfb_resume(struct work_struct *work) static int msmfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { - u32 size; + uint32_t size; if ((var->xres != info->var.xres) || (var->yres != info->var.yres) || diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h index 5c674712b1a08..53dad1f27cfba 100644 --- a/include/linux/msm_mdp.h +++ b/include/linux/msm_mdp.h @@ -15,66 +15,44 @@ #define _MSM_MDP_H_ #include -#include #define MSMFB_IOCTL_MAGIC 'm' #define MSMFB_GRP_DISP _IOW(MSMFB_IOCTL_MAGIC, 1, unsigned int) #define MSMFB_BLIT _IOW(MSMFB_IOCTL_MAGIC, 2, unsigned int) -#define MSMFB_SUSPEND_SW_REFRESHER _IOW(MSMFB_IOCTL_MAGIC, 128, unsigned int) -#define MSMFB_RESUME_SW_REFRESHER _IOW(MSMFB_IOCTL_MAGIC, 129, unsigned int) -#define MSMFB_CURSOR _IOW(MSMFB_IOCTL_MAGIC, 130, struct fb_cursor) -#define MSMFB_SET_LUT _IOW(MSMFB_IOCTL_MAGIC, 131, struct fb_cmap) -#define MSMFB_HISTOGRAM _IOWR(MSMFB_IOCTL_MAGIC, 132, struct mdp_histogram) -/* new ioctls's for set/get ccs matrix */ -#define MSMFB_GET_CCS_MATRIX _IOWR(MSMFB_IOCTL_MAGIC, 133, struct mdp_ccs) -#define MSMFB_SET_CCS_MATRIX _IOW(MSMFB_IOCTL_MAGIC, 134, struct mdp_ccs) + +/* drewis: adding for android 4.0 */ #define MSMFB_OVERLAY_SET _IOWR(MSMFB_IOCTL_MAGIC, 135, \ struct mdp_overlay) #define MSMFB_OVERLAY_UNSET _IOW(MSMFB_IOCTL_MAGIC, 136, unsigned int) #define MSMFB_OVERLAY_PLAY _IOW(MSMFB_IOCTL_MAGIC, 137, \ struct msmfb_overlay_data) -#define MSMFB_GET_PAGE_PROTECTION _IOR(MSMFB_IOCTL_MAGIC, 138, \ - struct mdp_page_protection) -#define MSMFB_SET_PAGE_PROTECTION _IOW(MSMFB_IOCTL_MAGIC, 139, \ - struct mdp_page_protection) #define MSMFB_OVERLAY_GET _IOR(MSMFB_IOCTL_MAGIC, 140, \ struct mdp_overlay) -#define MSMFB_OVERLAY_PLAY_ENABLE _IOW(MSMFB_IOCTL_MAGIC, 141, unsigned int) -#define MSMFB_OVERLAY_BLT _IOWR(MSMFB_IOCTL_MAGIC, 142, \ - struct msmfb_overlay_blt) -#define MSMFB_OVERLAY_BLT_OFFSET _IOW(MSMFB_IOCTL_MAGIC, 143, unsigned int) -#define MSMFB_HISTOGRAM_START _IO(MSMFB_IOCTL_MAGIC, 144) -#define MSMFB_HISTOGRAM_STOP _IO(MSMFB_IOCTL_MAGIC, 145) -#define MSMFB_NOTIFY_UPDATE _IOW(MSMFB_IOCTL_MAGIC, 146, unsigned int) - #define MSMFB_OVERLAY_3D _IOWR(MSMFB_IOCTL_MAGIC, 147, \ struct msmfb_overlay_3d) +/* drewis: end */ -#define MSMFB_MIXER_INFO _IOWR(MSMFB_IOCTL_MAGIC, 148, \ - struct msmfb_mixer_info_req) -#define MSMFB_OVERLAY_PLAY_WAIT _IOWR(MSMFB_IOCTL_MAGIC, 149, \ - struct msmfb_overlay_data) - -#define MSMFB_WRITEBACK_INIT _IO(MSMFB_IOCTL_MAGIC, 150) -#define MSMFB_WRITEBACK_REGISTER_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 151, \ - struct msmfb_writeback_data) -#define MSMFB_WRITEBACK_UNREGISTER_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 152, \ - struct msmfb_writeback_data) -#define MSMFB_WRITEBACK_QUEUE_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 153, \ - struct msmfb_data) -#define MSMFB_WRITEBACK_DEQUEUE_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 154, \ - struct msmfb_data) -#define MSMFB_WRITEBACK_TERMINATE _IO(MSMFB_IOCTL_MAGIC, 155) - -#define FB_TYPE_3D_PANEL 0x10101010 -#define MDP_IMGTYPE2_START 0x10000 -#define MSMFB_DRIVER_VERSION 0xF9E8D701 - +#if 0 /* drewis: superseded below */ enum { - NOTIFY_UPDATE_START, - NOTIFY_UPDATE_STOP, + MDP_RGB_565, /* RGB 565 planar */ + MDP_XRGB_8888, /* RGB 888 padded */ + MDP_Y_CBCR_H2V2, /* Y and CbCr, pseudo planar w/ Cb is in MSB */ + MDP_ARGB_8888, /* ARGB 888 */ + MDP_RGB_888, /* RGB 888 planar */ + MDP_Y_CRCB_H2V2, /* Y and CrCb, pseudo planar w/ Cr is in MSB */ + MDP_YCRYCB_H2V1, /* YCrYCb interleave */ + MDP_Y_CRCB_H2V1, /* Y and CrCb, pseduo planar w/ Cr is in MSB */ + MDP_Y_CBCR_H2V1, /* Y and CrCb, pseduo planar w/ Cr is in MSB */ + MDP_RGBA_8888, /* ARGB 888 */ + MDP_BGRA_8888, /* ABGR 888 */ + MDP_RGBX_8888, /* RGBX 888 */ + MDP_IMGTYPE_LIMIT /* Non valid image type after this enum */ }; +#endif +/* drewis: added for android 4.0 */ +#define FB_TYPE_3D_PANEL 0x10101010 +#define MDP_IMGTYPE2_START 0x10000 enum { MDP_RGB_565, /* RGB 565 planer */ MDP_XRGB_8888, /* RGB 888 padded */ @@ -100,60 +78,35 @@ enum { MDP_FB_FORMAT, /* framebuffer format */ MDP_IMGTYPE_LIMIT2 /* Non valid image type after this enum */ }; +/* drewis: end */ enum { PMEM_IMG, FB_IMG, }; -enum { - HSIC_HUE = 0, - HSIC_SAT, - HSIC_INT, - HSIC_CON, - NUM_HSIC_PARAM, -}; - -/* mdp_blit_req flag values */ -#define MDP_ROT_NOP 0 -#define MDP_FLIP_LR 0x1 -#define MDP_FLIP_UD 0x2 -#define MDP_ROT_90 0x4 -#define MDP_ROT_180 (MDP_FLIP_UD|MDP_FLIP_LR) -#define MDP_ROT_270 (MDP_ROT_90|MDP_FLIP_UD|MDP_FLIP_LR) -#define MDP_DITHER 0x8 -#define MDP_BLUR 0x10 +/* flag values */ +#define MDP_ROT_NOP 0 +#define MDP_FLIP_LR 0x1 +#define MDP_FLIP_UD 0x2 +#define MDP_ROT_90 0x4 +#define MDP_ROT_180 (MDP_FLIP_UD|MDP_FLIP_LR) +#define MDP_ROT_270 (MDP_ROT_90|MDP_FLIP_UD|MDP_FLIP_LR) +#define MDP_ROT_MASK 0x7 +#define MDP_DITHER 0x8 +#define MDP_BLUR 0x10 #define MDP_BLEND_FG_PREMULT 0x20000 + +#define MDP_TRANSP_NOP 0xffffffff +#define MDP_ALPHA_NOP 0xff + +/* drewis: added for android 4.0 */ #define MDP_DEINTERLACE 0x80000000 -#define MDP_SHARPENING 0x40000000 -#define MDP_NO_DMA_BARRIER_START 0x20000000 -#define MDP_NO_DMA_BARRIER_END 0x10000000 -#define MDP_NO_BLIT 0x08000000 -#define MDP_BLIT_WITH_DMA_BARRIERS 0x000 -#define MDP_BLIT_WITH_NO_DMA_BARRIERS \ - (MDP_NO_DMA_BARRIER_START | MDP_NO_DMA_BARRIER_END) -#define MDP_BLIT_SRC_GEM 0x04000000 -#define MDP_BLIT_DST_GEM 0x02000000 #define MDP_BLIT_NON_CACHED 0x01000000 #define MDP_OV_PIPE_SHARE 0x00800000 -#define MDP_DEINTERLACE_ODD 0x00400000 #define MDP_OV_PLAY_NOWAIT 0x00200000 #define MDP_SOURCE_ROTATED_90 0x00100000 -#define MDP_MEMORY_ID_TYPE_FB 0x00001000 -#define MDP_DPP_HSIC 0x00080000 - -#define MDP_TRANSP_NOP 0xffffffff -#define MDP_ALPHA_NOP 0xff - -#define MDP_FB_PAGE_PROTECTION_NONCACHED (0) -#define MDP_FB_PAGE_PROTECTION_WRITECOMBINE (1) -#define MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE (2) -#define MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE (3) -#define MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE (4) -/* Sentinel: Don't use! */ -#define MDP_FB_PAGE_PROTECTION_INVALID (5) -/* Count of the number of MDP_FB_PAGE_PROTECTION_... values. */ -#define MDP_NUM_FB_PAGE_PROTECTION_VALUES (5) +/* drewis: end */ struct mdp_rect { uint32_t x; @@ -168,41 +121,8 @@ struct mdp_img { uint32_t format; uint32_t offset; int memory_id; /* the file descriptor */ - uint32_t priv; -}; - -/* - * {3x3} + {3} ccs matrix - */ - -#define MDP_CCS_RGB2YUV 0 -#define MDP_CCS_YUV2RGB 1 - -#define MDP_CCS_SIZE 9 -#define MDP_BV_SIZE 3 - -struct mdp_ccs { - int direction; /* MDP_CCS_RGB2YUV or YUV2RGB */ - uint16_t ccs[MDP_CCS_SIZE]; /* 3x3 color coefficients */ - uint16_t bv[MDP_BV_SIZE]; /* 1x3 bias vector */ -}; - -struct mdp_csc { - int id; - uint32_t csc_mv[9]; - uint32_t csc_pre_bv[3]; - uint32_t csc_post_bv[3]; - uint32_t csc_pre_lv[6]; - uint32_t csc_post_lv[6]; }; -/* The version of the mdp_blit_req structure so that - * user applications can selectively decide which functionality - * to include - */ - -#define MDP_BLIT_REQ_VERSION 2 - struct mdp_blit_req { struct mdp_img src; struct mdp_img dst; @@ -211,7 +131,6 @@ struct mdp_blit_req { uint32_t alpha; uint32_t transp_mask; uint32_t flags; - int sharpening_strength; /* -127 <--> 127, default 64 */ }; struct mdp_blit_req_list { @@ -219,8 +138,7 @@ struct mdp_blit_req_list { struct mdp_blit_req req[]; }; -#define MSMFB_DATA_VERSION 2 - +/* drewis: added for android 4.0 */ struct msmfb_data { uint32_t offset; int memory_id; @@ -245,12 +163,6 @@ struct msmfb_img { uint32_t format; }; -#define MSMFB_WRITEBACK_DEQUEUE_BLOCKING 0x1 -struct msmfb_writeback_data { - struct msmfb_data buf_info; - struct msmfb_img img; -}; - struct dpp_ctrl { /* *'sharp_strength' has inputs = -128 <-> 127 @@ -259,7 +171,6 @@ struct dpp_ctrl { * smoothed picture. */ int8_t sharp_strength; - int8_t hsic_params[NUM_HSIC_PARAM]; }; struct mdp_overlay { @@ -281,61 +192,6 @@ struct msmfb_overlay_3d { uint32_t width; uint32_t height; }; +/* drewis: end */ - -struct msmfb_overlay_blt { - uint32_t enable; - uint32_t offset; - uint32_t width; - uint32_t height; - uint32_t bpp; -}; - -struct mdp_histogram { - uint32_t frame_cnt; - uint32_t bin_cnt; - uint32_t *r; - uint32_t *g; - uint32_t *b; -}; - -struct mdp_page_protection { - uint32_t page_protection; -}; - - -struct mdp_mixer_info { - int pndx; - int pnum; - int ptype; - int mixer_num; - int z_order; -}; - -#define MAX_PIPE_PER_MIXER 4 - -struct msmfb_mixer_info_req { - int mixer_num; - int cnt; - struct mdp_mixer_info info[MAX_PIPE_PER_MIXER]; -}; - - -#ifdef __KERNEL__ - -/* get the framebuffer physical address information */ -int get_fb_phys_info(unsigned long *start, unsigned long *len, int fb_num); -struct fb_info *msm_fb_get_writeback_fb(void); -int msm_fb_writeback_init(struct fb_info *info); -int msm_fb_writeback_register_buffer(struct fb_info *info, - struct msmfb_writeback_data *data); -int msm_fb_writeback_queue_buffer(struct fb_info *info, - struct msmfb_data *data); -int msm_fb_writeback_dequeue_buffer(struct fb_info *info, - struct msmfb_data *data); -int msm_fb_writeback_unregister_buffer(struct fb_info *info, - struct msmfb_writeback_data *data); -int msm_fb_writeback_terminate(struct fb_info *info); -#endif - -#endif /*_MSM_MDP_H_*/ +#endif /* _MSM_MDP_H_ */ From 02c1cd8301e809357e2fad273eac00acf42145df Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Fri, 10 Feb 2012 15:26:25 -0600 Subject: [PATCH 2082/2556] pmem: explicitly include sched.h fixes build on bravo --- drivers/misc/pmem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/pmem.c b/drivers/misc/pmem.c index 813fe6726f17e..d65d507cb1949 100644 --- a/drivers/misc/pmem.c +++ b/drivers/misc/pmem.c @@ -33,6 +33,7 @@ #include #include #include +#include #define PMEM_MAX_USER_SPACE_DEVICES (10) #define PMEM_MAX_KERNEL_SPACE_DEVICES (2) From 5f52c49bc53bc1b891db3ee6016d08b7a7e5c239 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 15 Feb 2012 13:41:52 -0600 Subject: [PATCH 2083/2556] drivers: video: msm: 32bit color hack manually applied a patch from LorDClockaN --- drivers/video/msm/msm_fb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index 45eb858382d54..d91dbd998b0fe 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -977,7 +977,7 @@ static struct file_operations debug_fops = { }; #endif -#define BITS_PER_PIXEL_DEF 16 +#define BITS_PER_PIXEL_DEF 32 static void setup_fb_info(struct msmfb_info *msmfb) { @@ -1003,6 +1003,7 @@ static void setup_fb_info(struct msmfb_info *msmfb) fb_info->var.yres_virtual = msmfb->yres * 2; fb_info->var.bits_per_pixel = BITS_PER_PIXEL_DEF; fb_info->var.accel_flags = 0; + fb_info->var.reserved[5] = 60; fb_info->var.yoffset = 0; From 8f8e3780e2fd1c193e8984018cb37498e3556c59 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 15 Feb 2012 19:53:45 -0600 Subject: [PATCH 2084/2556] drivers: video: msm: fix fps info for gralloc --- drivers/video/msm/msm_fb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index d91dbd998b0fe..25e14d4d340f5 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -1003,7 +1003,7 @@ static void setup_fb_info(struct msmfb_info *msmfb) fb_info->var.yres_virtual = msmfb->yres * 2; fb_info->var.bits_per_pixel = BITS_PER_PIXEL_DEF; fb_info->var.accel_flags = 0; - fb_info->var.reserved[5] = 60; + fb_info->var.reserved[4] = 60; fb_info->var.yoffset = 0; From a23eb557a0c6a57f34b4fb63cd69eb8413278219 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 15 Feb 2012 21:36:24 -0600 Subject: [PATCH 2085/2556] include: reset mdp header to original with two exceptions: u32 types changed to uint32_t added MDP_BLIT_NON_CACHED which still needs to me implemented in mdp_ppp.c all the rotator related stuff is no longer needed to build ics now that i have a custom qsd8k hwcomposer --- include/linux/msm_mdp.h | 104 ---------------------------------------- 1 file changed, 104 deletions(-) diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h index 53dad1f27cfba..a933facc5c5de 100644 --- a/include/linux/msm_mdp.h +++ b/include/linux/msm_mdp.h @@ -20,19 +20,6 @@ #define MSMFB_GRP_DISP _IOW(MSMFB_IOCTL_MAGIC, 1, unsigned int) #define MSMFB_BLIT _IOW(MSMFB_IOCTL_MAGIC, 2, unsigned int) -/* drewis: adding for android 4.0 */ -#define MSMFB_OVERLAY_SET _IOWR(MSMFB_IOCTL_MAGIC, 135, \ - struct mdp_overlay) -#define MSMFB_OVERLAY_UNSET _IOW(MSMFB_IOCTL_MAGIC, 136, unsigned int) -#define MSMFB_OVERLAY_PLAY _IOW(MSMFB_IOCTL_MAGIC, 137, \ - struct msmfb_overlay_data) -#define MSMFB_OVERLAY_GET _IOR(MSMFB_IOCTL_MAGIC, 140, \ - struct mdp_overlay) -#define MSMFB_OVERLAY_3D _IOWR(MSMFB_IOCTL_MAGIC, 147, \ - struct msmfb_overlay_3d) -/* drewis: end */ - -#if 0 /* drewis: superseded below */ enum { MDP_RGB_565, /* RGB 565 planar */ MDP_XRGB_8888, /* RGB 888 padded */ @@ -48,37 +35,6 @@ enum { MDP_RGBX_8888, /* RGBX 888 */ MDP_IMGTYPE_LIMIT /* Non valid image type after this enum */ }; -#endif - -/* drewis: added for android 4.0 */ -#define FB_TYPE_3D_PANEL 0x10101010 -#define MDP_IMGTYPE2_START 0x10000 -enum { - MDP_RGB_565, /* RGB 565 planer */ - MDP_XRGB_8888, /* RGB 888 padded */ - MDP_Y_CBCR_H2V2, /* Y and CbCr, pseudo planer w/ Cb is in MSB */ - MDP_ARGB_8888, /* ARGB 888 */ - MDP_RGB_888, /* RGB 888 planer */ - MDP_Y_CRCB_H2V2, /* Y and CrCb, pseudo planer w/ Cr is in MSB */ - MDP_YCRYCB_H2V1, /* YCrYCb interleave */ - MDP_Y_CRCB_H2V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */ - MDP_Y_CBCR_H2V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */ - MDP_RGBA_8888, /* ARGB 888 */ - MDP_BGRA_8888, /* ABGR 888 */ - MDP_RGBX_8888, /* RGBX 888 */ - MDP_Y_CRCB_H2V2_TILE, /* Y and CrCb, pseudo planer tile */ - MDP_Y_CBCR_H2V2_TILE, /* Y and CbCr, pseudo planer tile */ - MDP_Y_CR_CB_H2V2, /* Y, Cr and Cb, planar */ - MDP_Y_CR_CB_GH2V2, /* Y, Cr and Cb, planar aligned to Android YV12 */ - MDP_Y_CB_CR_H2V2, /* Y, Cb and Cr, planar */ - MDP_Y_CRCB_H1V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */ - MDP_Y_CBCR_H1V1, /* Y and CbCr, pseduo planer w/ Cb is in MSB */ - MDP_IMGTYPE_LIMIT, - MDP_BGR_565 = MDP_IMGTYPE2_START, /* BGR 565 planer */ - MDP_FB_FORMAT, /* framebuffer format */ - MDP_IMGTYPE_LIMIT2 /* Non valid image type after this enum */ -}; -/* drewis: end */ enum { PMEM_IMG, @@ -101,11 +57,7 @@ enum { #define MDP_ALPHA_NOP 0xff /* drewis: added for android 4.0 */ -#define MDP_DEINTERLACE 0x80000000 #define MDP_BLIT_NON_CACHED 0x01000000 -#define MDP_OV_PIPE_SHARE 0x00800000 -#define MDP_OV_PLAY_NOWAIT 0x00200000 -#define MDP_SOURCE_ROTATED_90 0x00100000 /* drewis: end */ struct mdp_rect { @@ -138,60 +90,4 @@ struct mdp_blit_req_list { struct mdp_blit_req req[]; }; -/* drewis: added for android 4.0 */ -struct msmfb_data { - uint32_t offset; - int memory_id; - int id; - uint32_t flags; - uint32_t priv; -}; - -#define MSMFB_NEW_REQUEST -1 - -struct msmfb_overlay_data { - uint32_t id; - struct msmfb_data data; - uint32_t version_key; - struct msmfb_data plane1_data; - struct msmfb_data plane2_data; -}; - -struct msmfb_img { - uint32_t width; - uint32_t height; - uint32_t format; -}; - -struct dpp_ctrl { - /* - *'sharp_strength' has inputs = -128 <-> 127 - * Increasingly positive values correlate with increasingly sharper - * picture. Increasingly negative values correlate with increasingly - * smoothed picture. - */ - int8_t sharp_strength; -}; - -struct mdp_overlay { - struct msmfb_img src; - struct mdp_rect src_rect; - struct mdp_rect dst_rect; - uint32_t z_order; /* stage number */ - uint32_t is_fg; /* control alpha & transp */ - uint32_t alpha; - uint32_t transp_mask; - uint32_t flags; - uint32_t id; - uint32_t user_data[8]; - struct dpp_ctrl dpp; -}; - -struct msmfb_overlay_3d { - uint32_t is_3d; - uint32_t width; - uint32_t height; -}; -/* drewis: end */ - #endif /* _MSM_MDP_H_ */ From 9bc3d0c96555fc5efb60f91c833122aa8ded4d03 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 15 Feb 2012 21:59:05 -0600 Subject: [PATCH 2086/2556] drivers: video: msm: implement MDP_BLIT_NON_CACHED --- drivers/video/msm/mdp_ppp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c index f80ba6a68a7a3..ea1581a199d25 100644 --- a/drivers/video/msm/mdp_ppp.c +++ b/drivers/video/msm/mdp_ppp.c @@ -413,6 +413,7 @@ static void flush_imgs(struct mdp_blit_req *req, struct ppp_regs *regs, #ifdef CONFIG_ANDROID_PMEM uint32_t src0_len, src1_len, dst0_len, dst1_len; + if (!(req->flags & MDP_BLIT_NON_CACHED)) { /* flush src images to memory before dma to mdp */ get_len(&req->src, &req->src_rect, regs->src_bpp, &src0_len, &src1_len); @@ -428,6 +429,7 @@ static void flush_imgs(struct mdp_blit_req *req, struct ppp_regs *regs, if (IS_PSEUDOPLNR(req->dst.format)) flush_pmem_file(dst_file, req->dst.offset + dst0_len, dst1_len); + } #endif } From e0218da8a01749d81e0d9e79a04042dc39ba47b7 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sun, 19 Feb 2012 17:43:03 -0500 Subject: [PATCH 2087/2556] include: wait: add wait_io_event_interruptible support --- include/linux/wait.h | 69 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/include/linux/wait.h b/include/linux/wait.h index 3efc9f3f43a08..945ca7d83650f 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -346,6 +346,75 @@ do { \ __ret; \ }) +/** + * wait_io_event_interruptible - sleep until an io condition gets true + * @wq: the waitqueue to wait on + * @condition: a C expression for the event to wait for + * + * The process is put to sleep (TASK_INTERRUPTIBLE) until the + * @condition evaluates to true or a signal is received. + * The @condition is checked each time the waitqueue @wq is woken up. + * + * wake_up() has to be called after changing any variable that could + * change the result of the wait condition. + * + * The function will return -ERESTARTSYS if it was interrupted by a + * signal and 0 if @condition evaluated to true. + */ +#define wait_io_event_interruptible(wq, condition) \ +({ \ + int __ret = 0; \ + if (!(condition)) \ + __wait_io_event_interruptible(wq, condition, __ret); \ + __ret; \ +}) + +#define __wait_io_event_interruptible_timeout(wq, condition, ret) \ +do { \ + DEFINE_WAIT(__wait); \ + \ + for (;;) { \ + prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \ + if (condition) \ + break; \ + if (!signal_pending(current)) { \ + ret = io_schedule_timeout(ret); \ + if (!ret) \ + break; \ + continue; \ + } \ + ret = -ERESTARTSYS; \ + break; \ + } \ + finish_wait(&wq, &__wait); \ +} while (0) + +/** + * wait_io_event_interruptible_timeout - sleep until an io condition gets true or a timeout elapses + * @wq: the waitqueue to wait on + * @condition: a C expression for the event to wait for + * @timeout: timeout, in jiffies + * + * The process is put to sleep (TASK_INTERRUPTIBLE) until the + * @condition evaluates to true or a signal is received. + * The @condition is checked each time the waitqueue @wq is woken up. + * + * wake_up() has to be called after changing any variable that could + * change the result of the wait condition. + * + * The function returns 0 if the @timeout elapsed, -ERESTARTSYS if it + * was interrupted by a signal, and the remaining jiffies otherwise + * if the condition evaluated to true before the timeout elapsed. + */ + +#define wait_io_event_interruptible_timeout(wq, condition, timeout) \ +({ \ + long __ret = timeout; \ + if (!(condition)) \ + __wait_io_event_interruptible_timeout(wq, condition, __ret); \ + __ret; \ +}) + #define __wait_event_interruptible_exclusive(wq, condition, ret) \ do { \ DEFINE_WAIT(__wait); \ From 1b6e5e0cf2ae8aeeeb1d5679c82f84b2a49b8251 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sun, 19 Feb 2012 16:11:29 -0500 Subject: [PATCH 2088/2556] msm: board: qsd8k: update for kgsl-3d0 bravo(c) incrediblec supersonic --- arch/arm/mach-msm/board-bravo.c | 81 ++++++++++++++++-------- arch/arm/mach-msm/board-incrediblec.c | 91 +++++++++++++++++---------- arch/arm/mach-msm/board-supersonic.c | 80 +++++++++++++++-------- 3 files changed, 168 insertions(+), 84 deletions(-) diff --git a/arch/arm/mach-msm/board-bravo.c b/arch/arm/mach-msm/board-bravo.c index 7de13afa7d00b..90026ca7a5e84 100644 --- a/arch/arm/mach-msm/board-bravo.c +++ b/arch/arm/mach-msm/board-bravo.c @@ -62,6 +62,8 @@ #include "board-bravo-tpa2018d1.h" #include "board-bravo-smb329.h" +#include + #ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL #include #endif @@ -280,26 +282,7 @@ static struct platform_device bravo_rfkill = { .id = -1, }; -static struct resource msm_kgsl_resources[] = { - { - .name = "kgsl_reg_memory", - .start = MSM_GPU_REG_PHYS, - .end = MSM_GPU_REG_PHYS + MSM_GPU_REG_SIZE - 1, - .flags = IORESOURCE_MEM, - }, - { - .name = "kgsl_phys_memory", - .start = MSM_GPU_MEM_BASE, - .end = MSM_GPU_MEM_BASE + MSM_GPU_MEM_SIZE - 1, - .flags = IORESOURCE_MEM, - }, - { - .start = INT_GRAPHICS, - .end = INT_GRAPHICS, - .flags = IORESOURCE_IRQ, - }, -}; - +#if 0 #define PWR_RAIL_GRP_CLK 8 static int bravo_kgsl_power_rail_mode(int follow_clk) { @@ -317,14 +300,58 @@ static int bravo_kgsl_power(bool on) cmd = on ? PCOM_CLKCTL_RPC_RAIL_ENABLE : PCOM_CLKCTL_RPC_RAIL_DISABLE; return msm_proc_comm(cmd, &rail_id, NULL); } +#endif -static struct platform_device msm_kgsl_device = { - .name = "kgsl", - .id = -1, - .resource = msm_kgsl_resources, - .num_resources = ARRAY_SIZE(msm_kgsl_resources), +/* start kgsl */ +static struct resource kgsl_3d0_resources[] = { + { + .name = KGSL_3D0_REG_MEMORY, + .start = 0xA0000000, + .end = 0xA001ffff, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_3D0_IRQ, + .start = INT_GRAPHICS, + .end = INT_GRAPHICS, + .flags = IORESOURCE_IRQ, + }, }; +static struct kgsl_device_platform_data kgsl_3d0_pdata = { + .pwr_data = { + .pwrlevel = { + { + .gpu_freq = 0, + .bus_freq = 128000000, + }, + }, + .init_level = 0, + .num_levels = 1, + .set_grp_async = NULL, + .idle_timeout = HZ/5, + }, + .clk = { + .name = { + .clk = "grp_clk", + }, + }, + .imem_clk_name = { + .clk = "imem_clk", + }, +}; + +struct platform_device msm_kgsl_3d0 = { + .name = "kgsl-3d0", + .id = 0, + .num_resources = ARRAY_SIZE(kgsl_3d0_resources), + .resource = kgsl_3d0_resources, + .dev = { + .platform_data = &kgsl_3d0_pdata, + }, +}; +/* end kgsl */ + static struct android_pmem_platform_data mdp_pmem_pdata = { .name = "pmem", .start = MSM_PMEM_MDP_BASE, @@ -1014,7 +1041,7 @@ static struct platform_device *devices[] __initdata = { #ifdef CONFIG_720P_CAMERA &android_pmem_venc_device, #endif - &msm_kgsl_device, + &msm_kgsl_3d0, &msm_device_i2c, &msm_camera_sensor_s5k3e2fx, &bravo_flashlight_device, @@ -1225,10 +1252,12 @@ static void __init bravo_init(void) gpio_request(BRAVO_GPIO_DS2482_SLP_N, "ds2482_slp_n"); +#if 0 /* set the gpu power rail to manual mode so clk en/dis will not * turn off gpu power, and hang it on resume */ bravo_kgsl_power_rail_mode(0); bravo_kgsl_power(true); +#endif msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index 0e31cd5f54429..a8988bb440c85 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -63,6 +63,8 @@ #include #include +#include + #define SMEM_SPINLOCK_I2C 6 #define INCREDIBLEC_MICROP_VER 0x04 @@ -485,26 +487,7 @@ static struct spi_platform_data incrediblec_spi_pdata = { }; -static struct resource msm_kgsl_resources[] = { - { - .name = "kgsl_reg_memory", - .start = MSM_GPU_REG_PHYS, - .end = MSM_GPU_REG_PHYS + MSM_GPU_REG_SIZE - 1, - .flags = IORESOURCE_MEM, - }, - { - .name = "kgsl_phys_memory", - .start = MSM_GPU_MEM_BASE, - .end = MSM_GPU_MEM_BASE + MSM_GPU_MEM_SIZE - 1, - .flags = IORESOURCE_MEM, - }, - { - .start = INT_GRAPHICS, - .end = INT_GRAPHICS, - .flags = IORESOURCE_IRQ, - }, -}; - +#if 0 #define PWR_RAIL_GRP_CLK 8 static int incrediblec_kgsl_power_rail_mode(int follow_clk) { @@ -522,13 +505,57 @@ static int incrediblec_kgsl_power(bool on) cmd = on ? PCOM_CLKCTL_RPC_RAIL_ENABLE : PCOM_CLKCTL_RPC_RAIL_DISABLE; return msm_proc_comm(cmd, &rail_id, NULL); } +#endif -static struct platform_device msm_kgsl_device = { - .name = "kgsl", - .id = -1, - .resource = msm_kgsl_resources, - .num_resources = ARRAY_SIZE(msm_kgsl_resources), +/* start kgsl */ +static struct resource kgsl_3d0_resources[] = { + { + .name = KGSL_3D0_REG_MEMORY, + .start = 0xA0000000, + .end = 0xA001ffff, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_3D0_IRQ, + .start = INT_GRAPHICS, + .end = INT_GRAPHICS, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct kgsl_device_platform_data kgsl_3d0_pdata = { + .pwr_data = { + .pwrlevel = { + { + .gpu_freq = 0, + .bus_freq = 128000000, + }, + }, + .init_level = 0, + .num_levels = 1, + .set_grp_async = NULL, + .idle_timeout = HZ/5, + }, + .clk = { + .name = { + .clk = "grp_clk", + }, + }, + .imem_clk_name = { + .clk = "imem_clk", + }, +}; + +struct platform_device msm_kgsl_3d0 = { + .name = "kgsl-3d0", + .id = 0, + .num_resources = ARRAY_SIZE(kgsl_3d0_resources), + .resource = kgsl_3d0_resources, + .dev = { + .platform_data = &kgsl_3d0_pdata, + }, }; +/* end kgsl */ static struct android_pmem_platform_data mdp_pmem_pdata = { .name = "pmem", @@ -1134,7 +1161,7 @@ static struct platform_device *devices[] __initdata = { &android_pmem_camera_device, #endif &msm_camera_sensor_ov8810, - &msm_kgsl_device, + &msm_kgsl_3d0, &msm_device_i2c, &incrediblec_flashlight_device, &incrediblec_leds, @@ -1331,13 +1358,13 @@ static void __init incrediblec_init(void) if (0 == engineerid || 0xF == engineerid) { mdp_pmem_pdata.start = MSM_PMEM_MDP_XA_BASE; android_pmem_adsp_pdata.start = MSM_PMEM_ADSP_XA_BASE; - msm_kgsl_resources[1].start = MSM_GPU_MEM_XA_BASE; - msm_kgsl_resources[1].end = MSM_GPU_MEM_XA_BASE + MSM_GPU_MEM_SIZE - 1; + kgsl_3d0_resources[1].start = MSM_GPU_MEM_XA_BASE; + kgsl_3d0_resources[1].end = MSM_GPU_MEM_XA_BASE + MSM_GPU_MEM_SIZE - 1; } else if (engineerid >= 3) { mdp_pmem_pdata.start = MSM_PMEM_MDP_BASE + MSM_MEM_128MB_OFFSET; android_pmem_adsp_pdata.start = MSM_PMEM_ADSP_BASE + MSM_MEM_128MB_OFFSET; - msm_kgsl_resources[1].start = MSM_GPU_MEM_BASE; - msm_kgsl_resources[1].end = msm_kgsl_resources[1].start + MSM_GPU_MEM_SIZE - 1; + kgsl_3d0_resources[1].start = MSM_GPU_MEM_BASE; + kgsl_3d0_resources[1].end = kgsl_3d0_resources[1].start + MSM_GPU_MEM_SIZE - 1; } incrediblec_board_serialno_setup(board_serialno()); @@ -1356,12 +1383,12 @@ static void __init incrediblec_init(void) bcm_bt_lpm_pdata.gpio_wake = INCREDIBLEC_GPIO_BT_CHIP_WAKE; config_gpio_table(bt_gpio_table_rev_CX, ARRAY_SIZE(bt_gpio_table_rev_CX)); - +#if 0 /* set the gpu power rail to manual mode so clk en/dis will not * turn off gpu power, and hang it on resume */ incrediblec_kgsl_power_rail_mode(0); incrediblec_kgsl_power(true); - +#endif #ifdef CONFIG_SPI_QSD msm_device_spi.dev.platform_data = &incrediblec_spi_pdata; diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index d35071751d615..a08dd52988f5e 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -72,6 +72,8 @@ #include "board-supersonic-tpa2018d1.h" +#include + #define SMEM_SPINLOCK_I2C 6 #ifdef CONFIG_ARCH_QSD8X50 @@ -517,26 +519,7 @@ static struct spi_platform_data supersonic_spi_pdata = { .clk_rate = 1200000, }; -static struct resource msm_kgsl_resources[] = { - { - .name = "kgsl_reg_memory", - .start = MSM_GPU_REG_PHYS, - .end = MSM_GPU_REG_PHYS + MSM_GPU_REG_SIZE - 1, - .flags = IORESOURCE_MEM, - }, - { - .name = "kgsl_phys_memory", - .start = MSM_GPU_MEM_BASE, - .end = MSM_GPU_MEM_BASE + MSM_GPU_MEM_SIZE - 1, - .flags = IORESOURCE_MEM, - }, - { - .start = INT_GRAPHICS, - .end = INT_GRAPHICS, - .flags = IORESOURCE_IRQ, - }, -}; - +#if 0 #define PWR_RAIL_GRP_CLK 8 static int supersonic_kgsl_power_rail_mode(int follow_clk) { @@ -554,14 +537,57 @@ static int supersonic_kgsl_power(bool on) cmd = on ? PCOM_CLKCTL_RPC_RAIL_ENABLE : PCOM_CLKCTL_RPC_RAIL_DISABLE; return msm_proc_comm(cmd, &rail_id, NULL); } +#endif -static struct platform_device msm_kgsl_device = { - .name = "kgsl", - .id = -1, - .resource = msm_kgsl_resources, - .num_resources = ARRAY_SIZE(msm_kgsl_resources), +/* start kgsl */ +static struct resource kgsl_3d0_resources[] = { + { + .name = KGSL_3D0_REG_MEMORY, + .start = 0xA0000000, + .end = 0xA001ffff, + .flags = IORESOURCE_MEM, + }, + { + .name = KGSL_3D0_IRQ, + .start = INT_GRAPHICS, + .end = INT_GRAPHICS, + .flags = IORESOURCE_IRQ, + }, }; +static struct kgsl_device_platform_data kgsl_3d0_pdata = { + .pwr_data = { + .pwrlevel = { + { + .gpu_freq = 0, + .bus_freq = 128000000, + }, + }, + .init_level = 0, + .num_levels = 1, + .set_grp_async = NULL, + .idle_timeout = HZ/5, + }, + .clk = { + .name = { + .clk = "grp_clk", + }, + }, + .imem_clk_name = { + .clk = "imem_clk", + }, +}; + +struct platform_device msm_kgsl_3d0 = { + .name = "kgsl-3d0", + .id = 0, + .num_resources = ARRAY_SIZE(kgsl_3d0_resources), + .resource = kgsl_3d0_resources, + .dev = { + .platform_data = &kgsl_3d0_pdata, + }, +}; +/* end kgsl */ static struct android_pmem_platform_data mdp_pmem_pdata = { .name = "pmem", .start = MSM_PMEM_MDP_BASE, @@ -1454,7 +1480,7 @@ static struct platform_device *devices[] __initdata = { &msm_camera_sensor_s5k3h1, &msm_camera_sensor_ov8810, &msm_camera_sensor_s5k6aafx, - &msm_kgsl_device, + &msm_kgsl_3d0, &msm_device_i2c, &msm_camera_sensor_ov9665, &supersonic_flashlight_device, @@ -1579,10 +1605,12 @@ static void __init supersonic_init(void) &msm_device_uart1.dev, 1, MSM_GPIO_TO_INT(SUPERSONIC_GPIO_UART1_RX)); #endif +#if 0 /* set the gpu power rail to manual mode so clk en/dis will not * turn off gpu power, and hang it on resume */ supersonic_kgsl_power_rail_mode(0); supersonic_kgsl_power(true); +#endif #ifdef CONFIG_SPI_QSD msm_device_spi.dev.platform_data = &supersonic_spi_pdata; From 7ae1d5ad5e38276b48a936c41d005ef8f5b06816 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sun, 19 Feb 2012 16:17:33 -0500 Subject: [PATCH 2089/2556] msm: board: qsd8k: update for new pmem braco(c) incrediblec supersonic --- arch/arm/mach-msm/board-bravo.c | 9 ++++++--- arch/arm/mach-msm/board-incrediblec.c | 12 ++++++++---- arch/arm/mach-msm/board-supersonic.c | 27 +++++++++++++++++++-------- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-msm/board-bravo.c b/arch/arm/mach-msm/board-bravo.c index 90026ca7a5e84..e4b9c17a07ce6 100644 --- a/arch/arm/mach-msm/board-bravo.c +++ b/arch/arm/mach-msm/board-bravo.c @@ -356,7 +356,8 @@ static struct android_pmem_platform_data mdp_pmem_pdata = { .name = "pmem", .start = MSM_PMEM_MDP_BASE, .size = MSM_PMEM_MDP_SIZE, - .no_allocator = 0, +/* .no_allocator = 0,*/ + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 1, }; @@ -364,7 +365,8 @@ static struct android_pmem_platform_data android_pmem_adsp_pdata = { .name = "pmem_adsp", .start = MSM_PMEM_ADSP_BASE, .size = MSM_PMEM_ADSP_SIZE, - .no_allocator = 0, +/* .no_allocator = 0,*/ + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 1, }; @@ -372,7 +374,8 @@ static struct android_pmem_platform_data android_pmem_venc_pdata = { .name = "pmem_venc", .start = MSM_PMEM_VENC_BASE, .size = MSM_PMEM_VENC_SIZE, - .no_allocator = 0, +/* .no_allocator = 0,*/ + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 1, }; diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index a8988bb440c85..ccd0f27a2d398 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -561,7 +561,8 @@ static struct android_pmem_platform_data mdp_pmem_pdata = { .name = "pmem", .start = MSM_PMEM_MDP_BASE, .size = MSM_PMEM_MDP_SIZE, - .no_allocator = 0, +/* .no_allocator = 0,*/ + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 1, }; @@ -569,7 +570,8 @@ static struct android_pmem_platform_data android_pmem_adsp_pdata = { .name = "pmem_adsp", .start = MSM_PMEM_ADSP_BASE, .size = MSM_PMEM_ADSP_SIZE, - .no_allocator = 0, +/* .no_allocator = 0,*/ + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 1, }; @@ -578,7 +580,8 @@ static struct android_pmem_platform_data android_pmem_venc_pdata = { .name = "pmem_venc", .start = MSM_PMEM_VENC_BASE, .size = MSM_PMEM_VENC_SIZE, - .no_allocator = 0, +/* .no_allocator = 0,*/ + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 1, }; #else @@ -586,7 +589,8 @@ static struct android_pmem_platform_data android_pmem_camera_pdata = { .name = "pmem_camera", .start = MSM_PMEM_CAMERA_BASE, .size = MSM_PMEM_CAMERA_SIZE, - .no_allocator = 1, +/* .no_allocator = 0,*/ + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 1, }; #endif diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index a08dd52988f5e..a1a947833e23d 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -588,11 +588,13 @@ struct platform_device msm_kgsl_3d0 = { }, }; /* end kgsl */ + static struct android_pmem_platform_data mdp_pmem_pdata = { .name = "pmem", .start = MSM_PMEM_MDP_BASE, .size = MSM_PMEM_MDP_SIZE, - .no_allocator = 0, +/* .no_allocator = 0,*/ + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 1, }; @@ -600,7 +602,8 @@ static struct android_pmem_platform_data android_pmem_adsp_pdata = { .name = "pmem_adsp", .start = MSM_PMEM_ADSP_BASE, .size = MSM_PMEM_ADSP_SIZE, - .no_allocator = 0, +/* .no_allocator = 0,*/ + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 1, }; @@ -608,17 +611,18 @@ static struct android_pmem_platform_data android_pmem_venc_pdata = { .name = "pmem_venc", .start = MSM_PMEM_VENC_BASE, .size = MSM_PMEM_VENC_SIZE, - .no_allocator = 0, +/* .no_allocator = 0,*/ + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 1, }; - #ifdef CONFIG_BUILD_CIQ static struct android_pmem_platform_data android_pmem_ciq_pdata = { .name = "pmem_ciq", .start = MSM_PMEM_CIQ_BASE, .size = MSM_PMEM_CIQ_SIZE, - .no_allocator = 0, +/* .no_allocator = 0,*/ + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 0, }; @@ -626,7 +630,8 @@ static struct android_pmem_platform_data android_pmem_ciq1_pdata = { .name = "pmem_ciq1", .start = MSM_PMEM_CIQ1_BASE, .size = MSM_PMEM_CIQ1_SIZE, - .no_allocator = 0, +/* .no_allocator = 0,*/ + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 0, }; @@ -634,7 +639,8 @@ static struct android_pmem_platform_data android_pmem_ciq2_pdata = { .name = "pmem_ciq2", .start = MSM_PMEM_CIQ2_BASE, .size = MSM_PMEM_CIQ2_SIZE, - .no_allocator = 0, +/* .no_allocator = 0,*/ + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 0, }; @@ -642,7 +648,8 @@ static struct android_pmem_platform_data android_pmem_ciq3_pdata = { .name = "pmem_ciq3", .start = MSM_PMEM_CIQ3_BASE, .size = MSM_PMEM_CIQ3_SIZE, - .no_allocator = 0, +/* .no_allocator = 0,*/ + .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 0, }; #endif @@ -1631,6 +1638,10 @@ static void __init supersonic_init(void) #endif platform_add_devices(devices, ARRAY_SIZE(devices)); + + platform_add_devices(msm_footswitch_devices, + msm_num_footswitch_devices); + if (!opt_usb_h2w_sw) { msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; config_supersonic_usb_id_gpios(0); From 09b5755e1b410b57a898cd9915cb47e1a914a00c Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sun, 19 Feb 2012 16:23:44 -0500 Subject: [PATCH 2090/2556] msm: board: qsd8k: add platform support for footswitch-pcom bravo(c) incrediblec supersonic --- arch/arm/mach-msm/board-bravo.c | 13 +++++++++++++ arch/arm/mach-msm/board-incrediblec.c | 14 ++++++++++++++ arch/arm/mach-msm/board-mahimahi.c | 1 + arch/arm/mach-msm/board-supersonic.c | 13 +++++++++++++ 4 files changed, 41 insertions(+) diff --git a/arch/arm/mach-msm/board-bravo.c b/arch/arm/mach-msm/board-bravo.c index e4b9c17a07ce6..f00366e7bb96a 100644 --- a/arch/arm/mach-msm/board-bravo.c +++ b/arch/arm/mach-msm/board-bravo.c @@ -63,6 +63,8 @@ #include "board-bravo-smb329.h" #include +#include +#include "footswitch.h" #ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL #include @@ -352,6 +354,14 @@ struct platform_device msm_kgsl_3d0 = { }; /* end kgsl */ +/* start footswitch regulator */ +struct platform_device *msm_footswitch_devices[] = { + FS_PCOM(FS_GFX3D, "fs_gfx3d"), +}; + +unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices); +/* end footswitch regulator */ + static struct android_pmem_platform_data mdp_pmem_pdata = { .name = "pmem", .start = MSM_PMEM_MDP_BASE, @@ -1267,6 +1277,9 @@ static void __init bravo_init(void) platform_add_devices(devices, ARRAY_SIZE(devices)); + platform_add_devices(msm_footswitch_devices, + msm_num_footswitch_devices); + i2c_register_board_info(0, base_i2c_devices, ARRAY_SIZE(base_i2c_devices)); diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index ccd0f27a2d398..609ef234f77bf 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -64,6 +64,8 @@ #include #include +#include +#include "footswitch.h" #define SMEM_SPINLOCK_I2C 6 #define INCREDIBLEC_MICROP_VER 0x04 @@ -557,6 +559,14 @@ struct platform_device msm_kgsl_3d0 = { }; /* end kgsl */ +/* start footswitch regulator */ +struct platform_device *msm_footswitch_devices[] = { + FS_PCOM(FS_GFX3D, "fs_gfx3d"), +}; + +unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices); +/* end footswitch regulator */ + static struct android_pmem_platform_data mdp_pmem_pdata = { .name = "pmem", .start = MSM_PMEM_MDP_BASE, @@ -1421,6 +1431,10 @@ static void __init incrediblec_init(void) } platform_add_devices(devices, ARRAY_SIZE(devices)); + + platform_add_devices(msm_footswitch_devices, + msm_num_footswitch_devices); + if (!opt_usb_h2w_sw) { msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; } diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 47d4cf925cf9b..809c3c1e2d61b 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -330,6 +330,7 @@ struct platform_device msm_kgsl_3d0 = { struct platform_device *msm_footswitch_devices[] = { FS_PCOM(FS_GFX3D, "fs_gfx3d"), }; + unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices); /* end footswitch regulator */ diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index a1a947833e23d..115b851caddc6 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -73,6 +73,8 @@ #include "board-supersonic-tpa2018d1.h" #include +#include +#include "footswitch.h" #define SMEM_SPINLOCK_I2C 6 @@ -589,6 +591,14 @@ struct platform_device msm_kgsl_3d0 = { }; /* end kgsl */ +/* start footswitch regulator */ +struct platform_device *msm_footswitch_devices[] = { + FS_PCOM(FS_GFX3D, "fs_gfx3d"), +}; + +unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices); +/* end footswitch regulator */ + static struct android_pmem_platform_data mdp_pmem_pdata = { .name = "pmem", .start = MSM_PMEM_MDP_BASE, @@ -1648,6 +1658,9 @@ static void __init supersonic_init(void) } i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); + platform_add_devices(msm_footswitch_devices, + msm_num_footswitch_devices); + ret = supersonic_init_mmc(system_rev); if (ret != 0) pr_crit("%s: Unable to initialize MMC\n", __func__); From 540f162124510be936e829901696606bb9dd2022 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sun, 19 Feb 2012 16:26:37 -0500 Subject: [PATCH 2091/2556] msm: board: qsd8k: remove unused pwr_rail code bravo(c) incrediblec supersonic --- arch/arm/mach-msm/board-bravo.c | 27 -------------------------- arch/arm/mach-msm/board-incrediblec.c | 28 --------------------------- arch/arm/mach-msm/board-supersonic.c | 27 -------------------------- 3 files changed, 82 deletions(-) diff --git a/arch/arm/mach-msm/board-bravo.c b/arch/arm/mach-msm/board-bravo.c index f00366e7bb96a..f7891df8ca443 100644 --- a/arch/arm/mach-msm/board-bravo.c +++ b/arch/arm/mach-msm/board-bravo.c @@ -284,26 +284,6 @@ static struct platform_device bravo_rfkill = { .id = -1, }; -#if 0 -#define PWR_RAIL_GRP_CLK 8 -static int bravo_kgsl_power_rail_mode(int follow_clk) -{ - int mode = follow_clk ? 0 : 1; - int rail_id = PWR_RAIL_GRP_CLK; - - return msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &rail_id, &mode); -} - -static int bravo_kgsl_power(bool on) -{ - int cmd; - int rail_id = PWR_RAIL_GRP_CLK; - - cmd = on ? PCOM_CLKCTL_RPC_RAIL_ENABLE : PCOM_CLKCTL_RPC_RAIL_DISABLE; - return msm_proc_comm(cmd, &rail_id, NULL); -} -#endif - /* start kgsl */ static struct resource kgsl_3d0_resources[] = { { @@ -1265,13 +1245,6 @@ static void __init bravo_init(void) gpio_request(BRAVO_GPIO_DS2482_SLP_N, "ds2482_slp_n"); -#if 0 - /* set the gpu power rail to manual mode so clk en/dis will not - * turn off gpu power, and hang it on resume */ - bravo_kgsl_power_rail_mode(0); - bravo_kgsl_power(true); -#endif - msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index 609ef234f77bf..0bca16ab04702 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -488,27 +488,6 @@ static struct spi_platform_data incrediblec_spi_pdata = { .clk_rate = 1200000, }; - -#if 0 -#define PWR_RAIL_GRP_CLK 8 -static int incrediblec_kgsl_power_rail_mode(int follow_clk) -{ - int mode = follow_clk ? 0 : 1; - int rail_id = PWR_RAIL_GRP_CLK; - - return msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &rail_id, &mode); -} - -static int incrediblec_kgsl_power(bool on) -{ - int cmd; - int rail_id = PWR_RAIL_GRP_CLK; - - cmd = on ? PCOM_CLKCTL_RPC_RAIL_ENABLE : PCOM_CLKCTL_RPC_RAIL_DISABLE; - return msm_proc_comm(cmd, &rail_id, NULL); -} -#endif - /* start kgsl */ static struct resource kgsl_3d0_resources[] = { { @@ -1397,13 +1376,6 @@ static void __init incrediblec_init(void) bcm_bt_lpm_pdata.gpio_wake = INCREDIBLEC_GPIO_BT_CHIP_WAKE; config_gpio_table(bt_gpio_table_rev_CX, ARRAY_SIZE(bt_gpio_table_rev_CX)); -#if 0 - /* set the gpu power rail to manual mode so clk en/dis will not - * turn off gpu power, and hang it on resume */ - incrediblec_kgsl_power_rail_mode(0); - incrediblec_kgsl_power(true); -#endif - #ifdef CONFIG_SPI_QSD msm_device_spi.dev.platform_data = &incrediblec_spi_pdata; #endif diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index 115b851caddc6..f6ffde031c7ed 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -521,26 +521,6 @@ static struct spi_platform_data supersonic_spi_pdata = { .clk_rate = 1200000, }; -#if 0 -#define PWR_RAIL_GRP_CLK 8 -static int supersonic_kgsl_power_rail_mode(int follow_clk) -{ - int mode = follow_clk ? 0 : 1; - int rail_id = PWR_RAIL_GRP_CLK; - - return msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &rail_id, &mode); -} - -static int supersonic_kgsl_power(bool on) -{ - int cmd; - int rail_id = PWR_RAIL_GRP_CLK; - - cmd = on ? PCOM_CLKCTL_RPC_RAIL_ENABLE : PCOM_CLKCTL_RPC_RAIL_DISABLE; - return msm_proc_comm(cmd, &rail_id, NULL); -} -#endif - /* start kgsl */ static struct resource kgsl_3d0_resources[] = { { @@ -1622,13 +1602,6 @@ static void __init supersonic_init(void) &msm_device_uart1.dev, 1, MSM_GPIO_TO_INT(SUPERSONIC_GPIO_UART1_RX)); #endif -#if 0 - /* set the gpu power rail to manual mode so clk en/dis will not - * turn off gpu power, and hang it on resume */ - supersonic_kgsl_power_rail_mode(0); - supersonic_kgsl_power(true); -#endif - #ifdef CONFIG_SPI_QSD msm_device_spi.dev.platform_data = &supersonic_spi_pdata; #endif From edb400a565c8b9cf8e440241b21ac0ea8f46099f Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sun, 19 Feb 2012 16:45:31 -0500 Subject: [PATCH 2092/2556] configs: cayniarb: rename and update defconfigs for new features --- ...avo_defconfig => cayniarb_bravo_defconfig} | 62 ++++++++++----- ...oc_defconfig => cayniarb_bravoc_defconfig} | 62 ++++++++++----- ...fconfig => cayniarb_incrediblec_defconfig} | 57 +++++++------- ..._defconfig => cayniarb_mahimahi_defconfig} | 60 +++++++++++---- ...efconfig => cayniarb_supersonic_defconfig} | 75 ++++++++++++------- 5 files changed, 216 insertions(+), 100 deletions(-) rename arch/arm/configs/{cyanogen_bravo_defconfig => cayniarb_bravo_defconfig} (97%) rename arch/arm/configs/{cyanogen_bravoc_defconfig => cayniarb_bravoc_defconfig} (97%) rename arch/arm/configs/{cyanogen_incrediblec_defconfig => cayniarb_incrediblec_defconfig} (98%) rename arch/arm/configs/{cyanogen_mahimahi_defconfig => cayniarb_mahimahi_defconfig} (97%) rename arch/arm/configs/{cyanogen_supersonic_defconfig => cayniarb_supersonic_defconfig} (96%) diff --git a/arch/arm/configs/cyanogen_bravo_defconfig b/arch/arm/configs/cayniarb_bravo_defconfig similarity index 97% rename from arch/arm/configs/cyanogen_bravo_defconfig rename to arch/arm/configs/cayniarb_bravo_defconfig index 1df9b7fb3ad8d..2973140f94f64 100644 --- a/arch/arm/configs/cyanogen_bravo_defconfig +++ b/arch/arm/configs/cayniarb_bravo_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux/arm 2.6.38.6 Kernel Configuration -# Sun May 15 15:47:56 2011 +# Linux/arm 2.6.38.8 Kernel Configuration +# Sun Feb 19 17:03:08 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -33,7 +33,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" -CONFIG_LOCALVERSION="-cyanogenmod" +CONFIG_LOCALVERSION="-cayniarb-ics-stock" CONFIG_LOCALVERSION_AUTO=y CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y @@ -41,7 +41,7 @@ CONFIG_HAVE_KERNEL_LZO=y # CONFIG_KERNEL_GZIP is not set CONFIG_KERNEL_LZMA=y # CONFIG_KERNEL_LZO is not set -# CONFIG_SWAP is not set +CONFIG_SWAP=y # CONFIG_SYSVIPC is not set # CONFIG_POSIX_MQUEUE is not set # CONFIG_BSD_PROCESS_ACCT is not set @@ -91,7 +91,7 @@ CONFIG_RT_GROUP_SCHED=y # CONFIG_NAMESPACES is not set # CONFIG_SCHED_AUTOGROUP is not set # CONFIG_SYSFS_DEPRECATED is not set -# CONFIG_RELAY is not set +CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" CONFIG_RD_GZIP=y @@ -442,7 +442,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 -CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" +CONFIG_CMDLINE="mem=64M console=ttyMSM0" # CONFIG_CMDLINE_FORCE is not set # CONFIG_XIP_KERNEL is not set # CONFIG_KEXEC is not set @@ -510,6 +510,7 @@ CONFIG_FB_EARLYSUSPEND=y # CONFIG_APM_EMULATION is not set # CONFIG_PM_RUNTIME is not set CONFIG_PM_OPS=y +# CONFIG_SUSPEND_TIME is not set CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_NET=y @@ -530,7 +531,10 @@ CONFIG_INET=y # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set CONFIG_IP_FIB_HASH=y -# CONFIG_IP_PNP is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE_DEMUX is not set # CONFIG_ARPD is not set @@ -627,11 +631,11 @@ CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y -# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y # CONFIG_NETFILTER_XT_TARGET_RATEEST is not set # CONFIG_NETFILTER_XT_TARGET_TEE is not set # CONFIG_NETFILTER_XT_TARGET_TPROXY is not set -# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +CONFIG_NETFILTER_XT_TARGET_TRACE=y # CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set # CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set @@ -656,7 +660,7 @@ CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y -CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set # CONFIG_NETFILTER_XT_MATCH_OSF is not set # CONFIG_NETFILTER_XT_MATCH_OWNER is not set CONFIG_NETFILTER_XT_MATCH_POLICY=y @@ -668,7 +672,7 @@ CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y # CONFIG_NETFILTER_XT_MATCH_RATEEST is not set # CONFIG_NETFILTER_XT_MATCH_REALM is not set -CONFIG_NETFILTER_XT_MATCH_RECENT=y +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set # CONFIG_NETFILTER_XT_MATCH_SCTP is not set CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y @@ -972,7 +976,7 @@ CONFIG_MTD_NAND_IDS=y CONFIG_BLK_DEV=y # CONFIG_BLK_DEV_COW_COMMON is not set CONFIG_BLK_DEV_LOOP=y -# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_CRYPTOLOOP=y # # DRBD disabled because PROC_FS, INET or CONNECTOR not selected @@ -1053,9 +1057,25 @@ CONFIG_DUMMY=y # CONFIG_EQUALIZER is not set CONFIG_TUN=y # CONFIG_VETH is not set -# CONFIG_MII is not set +CONFIG_MII=y # CONFIG_PHYLIB is not set -# CONFIG_NET_ETHERNET is not set +CONFIG_NET_ETHERNET=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set +# CONFIG_ETHOC is not set +CONFIG_SMC911X=y +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8851_MLL is not set CONFIG_NETDEV_1000=y # CONFIG_STMMAC_ETH is not set CONFIG_NETDEV_10000=y @@ -1091,7 +1111,7 @@ CONFIG_MSM_RMNET=y # CONFIG_MSM_RMNET_DEBUG is not set # CONFIG_NETPOLL is not set # CONFIG_NET_POLL_CONTROLLER is not set -# CONFIG_GAN_ETH is not set +CONFIG_GAN_ETH=y # CONFIG_ISDN is not set # CONFIG_PHONE is not set @@ -1435,6 +1455,16 @@ CONFIG_S5K3E2FX=y # Graphics support # # CONFIG_DRM is not set +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +CONFIG_MSM_KGSL_MMU=y +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +# CONFIG_MSM_KGSL_MMU_PAGE_FAULT is not set +# CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set # CONFIG_VGASTATE is not set CONFIG_VIDEO_OUTPUT_CONTROL=y CONFIG_FB=y @@ -1470,8 +1500,6 @@ CONFIG_FB_MSM_LEGACY_MDP=y CONFIG_FB_MSM_MDP_PPP=y CONFIG_FB_MSM_LCDC=y # CONFIG_FB_MSM_MDDI is not set -CONFIG_GPU_MSM_KGSL=y -CONFIG_MSM_KGSL_MMU=y # CONFIG_MSM_HDMI is not set # CONFIG_BACKLIGHT_LCD_SUPPORT is not set diff --git a/arch/arm/configs/cyanogen_bravoc_defconfig b/arch/arm/configs/cayniarb_bravoc_defconfig similarity index 97% rename from arch/arm/configs/cyanogen_bravoc_defconfig rename to arch/arm/configs/cayniarb_bravoc_defconfig index c688a13ec9b2e..c1b66de79daee 100644 --- a/arch/arm/configs/cyanogen_bravoc_defconfig +++ b/arch/arm/configs/cayniarb_bravoc_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux/arm 2.6.38.6 Kernel Configuration -# Sun May 15 15:48:38 2011 +# Linux/arm 2.6.38.8 Kernel Configuration +# Sun Feb 19 17:03:40 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -33,7 +33,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" -CONFIG_LOCALVERSION="-cyanogenmod" +CONFIG_LOCALVERSION="-cayniarb-ics-stock" CONFIG_LOCALVERSION_AUTO=y CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y @@ -41,7 +41,7 @@ CONFIG_HAVE_KERNEL_LZO=y # CONFIG_KERNEL_GZIP is not set CONFIG_KERNEL_LZMA=y # CONFIG_KERNEL_LZO is not set -# CONFIG_SWAP is not set +CONFIG_SWAP=y # CONFIG_SYSVIPC is not set # CONFIG_POSIX_MQUEUE is not set # CONFIG_BSD_PROCESS_ACCT is not set @@ -91,7 +91,7 @@ CONFIG_RT_GROUP_SCHED=y # CONFIG_NAMESPACES is not set # CONFIG_SCHED_AUTOGROUP is not set # CONFIG_SYSFS_DEPRECATED is not set -# CONFIG_RELAY is not set +CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" CONFIG_RD_GZIP=y @@ -442,7 +442,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 -CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" +CONFIG_CMDLINE="mem=64M console=ttyMSM0" # CONFIG_CMDLINE_FORCE is not set # CONFIG_XIP_KERNEL is not set # CONFIG_KEXEC is not set @@ -510,6 +510,7 @@ CONFIG_FB_EARLYSUSPEND=y # CONFIG_APM_EMULATION is not set # CONFIG_PM_RUNTIME is not set CONFIG_PM_OPS=y +# CONFIG_SUSPEND_TIME is not set CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_NET=y @@ -530,7 +531,10 @@ CONFIG_INET=y # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set CONFIG_IP_FIB_HASH=y -# CONFIG_IP_PNP is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE_DEMUX is not set # CONFIG_ARPD is not set @@ -627,11 +631,11 @@ CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y -# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y # CONFIG_NETFILTER_XT_TARGET_RATEEST is not set # CONFIG_NETFILTER_XT_TARGET_TEE is not set # CONFIG_NETFILTER_XT_TARGET_TPROXY is not set -# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +CONFIG_NETFILTER_XT_TARGET_TRACE=y # CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set # CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set @@ -656,7 +660,7 @@ CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y -CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set # CONFIG_NETFILTER_XT_MATCH_OSF is not set # CONFIG_NETFILTER_XT_MATCH_OWNER is not set CONFIG_NETFILTER_XT_MATCH_POLICY=y @@ -668,7 +672,7 @@ CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y # CONFIG_NETFILTER_XT_MATCH_RATEEST is not set # CONFIG_NETFILTER_XT_MATCH_REALM is not set -CONFIG_NETFILTER_XT_MATCH_RECENT=y +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set # CONFIG_NETFILTER_XT_MATCH_SCTP is not set CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y @@ -972,7 +976,7 @@ CONFIG_MTD_NAND_IDS=y CONFIG_BLK_DEV=y # CONFIG_BLK_DEV_COW_COMMON is not set CONFIG_BLK_DEV_LOOP=y -# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_CRYPTOLOOP=y # # DRBD disabled because PROC_FS, INET or CONNECTOR not selected @@ -1053,9 +1057,25 @@ CONFIG_DUMMY=y # CONFIG_EQUALIZER is not set CONFIG_TUN=y # CONFIG_VETH is not set -# CONFIG_MII is not set +CONFIG_MII=y # CONFIG_PHYLIB is not set -# CONFIG_NET_ETHERNET is not set +CONFIG_NET_ETHERNET=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set +# CONFIG_ETHOC is not set +CONFIG_SMC911X=y +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8851_MLL is not set CONFIG_NETDEV_1000=y # CONFIG_STMMAC_ETH is not set CONFIG_NETDEV_10000=y @@ -1091,7 +1111,7 @@ CONFIG_MSM_RMNET=y # CONFIG_MSM_RMNET_DEBUG is not set # CONFIG_NETPOLL is not set # CONFIG_NET_POLL_CONTROLLER is not set -# CONFIG_GAN_ETH is not set +CONFIG_GAN_ETH=y # CONFIG_ISDN is not set # CONFIG_PHONE is not set @@ -1435,6 +1455,16 @@ CONFIG_S5K3E2FX=y # Graphics support # # CONFIG_DRM is not set +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +CONFIG_MSM_KGSL_MMU=y +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +# CONFIG_MSM_KGSL_MMU_PAGE_FAULT is not set +# CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set # CONFIG_VGASTATE is not set CONFIG_VIDEO_OUTPUT_CONTROL=y CONFIG_FB=y @@ -1470,8 +1500,6 @@ CONFIG_FB_MSM_LEGACY_MDP=y CONFIG_FB_MSM_MDP_PPP=y CONFIG_FB_MSM_LCDC=y # CONFIG_FB_MSM_MDDI is not set -CONFIG_GPU_MSM_KGSL=y -CONFIG_MSM_KGSL_MMU=y # CONFIG_MSM_HDMI is not set # CONFIG_BACKLIGHT_LCD_SUPPORT is not set diff --git a/arch/arm/configs/cyanogen_incrediblec_defconfig b/arch/arm/configs/cayniarb_incrediblec_defconfig similarity index 98% rename from arch/arm/configs/cyanogen_incrediblec_defconfig rename to arch/arm/configs/cayniarb_incrediblec_defconfig index 9b8cf2e1a1c67..8b334d6b334be 100644 --- a/arch/arm/configs/cyanogen_incrediblec_defconfig +++ b/arch/arm/configs/cayniarb_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sat Jan 7 22:31:58 2012 +# Sun Feb 19 17:05:09 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -33,7 +33,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="arm-eabi-" -CONFIG_LOCALVERSION="-cyanogenmod" +CONFIG_LOCALVERSION="-cayniarb-ics-stock" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y @@ -41,7 +41,7 @@ CONFIG_HAVE_KERNEL_LZO=y # CONFIG_KERNEL_GZIP is not set CONFIG_KERNEL_LZMA=y # CONFIG_KERNEL_LZO is not set -# CONFIG_SWAP is not set +CONFIG_SWAP=y # CONFIG_SYSVIPC is not set # CONFIG_POSIX_MQUEUE is not set # CONFIG_BSD_PROCESS_ACCT is not set @@ -90,7 +90,7 @@ CONFIG_RT_GROUP_SCHED=y # CONFIG_NAMESPACES is not set # CONFIG_SCHED_AUTOGROUP is not set # CONFIG_SYSFS_DEPRECATED is not set -# CONFIG_RELAY is not set +CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" CONFIG_RD_GZIP=y @@ -445,7 +445,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 -CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" +CONFIG_CMDLINE="mem=64M console=ttyMSM0" # CONFIG_CMDLINE_FORCE is not set # CONFIG_XIP_KERNEL is not set # CONFIG_KEXEC is not set @@ -460,10 +460,10 @@ CONFIG_CPU_FREQ_TABLE=y # CONFIG_CPU_FREQ_DEBUG is not set CONFIG_CPU_FREQ_STAT=y CONFIG_CPU_FREQ_STAT_DETAILS=y -CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set -# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set CONFIG_CPU_FREQ_GOV_PERFORMANCE=y @@ -513,6 +513,7 @@ CONFIG_FB_EARLYSUSPEND=y # CONFIG_APM_EMULATION is not set # CONFIG_PM_RUNTIME is not set CONFIG_PM_OPS=y +# CONFIG_SUSPEND_TIME is not set CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_NET=y @@ -531,22 +532,20 @@ CONFIG_NET_KEY=y # CONFIG_NET_KEY_MIGRATE is not set CONFIG_INET=y # CONFIG_IP_MULTICAST is not set -CONFIG_IP_ADVANCED_ROUTER=y -CONFIG_ASK_IP_FIB_HASH=y -# CONFIG_IP_FIB_TRIE is not set +# CONFIG_IP_ADVANCED_ROUTER is not set CONFIG_IP_FIB_HASH=y -CONFIG_IP_MULTIPLE_TABLES=y -# CONFIG_IP_ROUTE_MULTIPATH is not set -# CONFIG_IP_ROUTE_VERBOSE is not set -# CONFIG_IP_PNP is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE_DEMUX is not set # CONFIG_ARPD is not set # CONFIG_SYN_COOKIES is not set # CONFIG_INET_AH is not set CONFIG_INET_ESP=y -CONFIG_INET_IPCOMP=y -CONFIG_INET_XFRM_TUNNEL=y +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set CONFIG_INET_TUNNEL=y CONFIG_INET_XFRM_MODE_TRANSPORT=y # CONFIG_INET_XFRM_MODE_TUNNEL is not set @@ -604,7 +603,7 @@ CONFIG_NF_CT_PROTO_SCTP=y CONFIG_NF_CT_PROTO_UDPLITE=y CONFIG_NF_CONNTRACK_AMANDA=y CONFIG_NF_CONNTRACK_FTP=y -# CONFIG_NF_CONNTRACK_H323 is not set +CONFIG_NF_CONNTRACK_H323=y CONFIG_NF_CONNTRACK_IRC=y CONFIG_NF_CONNTRACK_NETBIOS_NS=y CONFIG_NF_CONNTRACK_PPTP=y @@ -635,11 +634,11 @@ CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y -# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y # CONFIG_NETFILTER_XT_TARGET_RATEEST is not set # CONFIG_NETFILTER_XT_TARGET_TEE is not set # CONFIG_NETFILTER_XT_TARGET_TPROXY is not set -# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +CONFIG_NETFILTER_XT_TARGET_TRACE=y # CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set # CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set @@ -664,7 +663,7 @@ CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y -CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set # CONFIG_NETFILTER_XT_MATCH_OSF is not set # CONFIG_NETFILTER_XT_MATCH_OWNER is not set CONFIG_NETFILTER_XT_MATCH_POLICY=y @@ -676,7 +675,7 @@ CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y # CONFIG_NETFILTER_XT_MATCH_RATEEST is not set # CONFIG_NETFILTER_XT_MATCH_REALM is not set -CONFIG_NETFILTER_XT_MATCH_RECENT=y +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set # CONFIG_NETFILTER_XT_MATCH_SCTP is not set CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y @@ -718,7 +717,7 @@ CONFIG_NF_NAT_IRC=y CONFIG_NF_NAT_TFTP=y CONFIG_NF_NAT_AMANDA=y CONFIG_NF_NAT_PPTP=y -# CONFIG_NF_NAT_H323 is not set +CONFIG_NF_NAT_H323=y CONFIG_NF_NAT_SIP=y CONFIG_IP_NF_MANGLE=y # CONFIG_IP_NF_TARGET_CLUSTERIP is not set @@ -1123,7 +1122,7 @@ CONFIG_MSM_RMNET=y # CONFIG_MSM_RMNET_DEBUG is not set # CONFIG_NETPOLL is not set # CONFIG_NET_POLL_CONTROLLER is not set -# CONFIG_GAN_ETH is not set +CONFIG_GAN_ETH=y # CONFIG_ISDN is not set # CONFIG_PHONE is not set @@ -1478,6 +1477,16 @@ CONFIG_OV8810=y # Graphics support # # CONFIG_DRM is not set +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +CONFIG_MSM_KGSL_MMU=y +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +# CONFIG_MSM_KGSL_MMU_PAGE_FAULT is not set +# CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set # CONFIG_VGASTATE is not set CONFIG_VIDEO_OUTPUT_CONTROL=y CONFIG_FB=y @@ -1515,8 +1524,6 @@ CONFIG_FB_MSM_LCDC=y CONFIG_FB_MSM_MDDI=y # CONFIG_FB_MSM_MDDI_EPSON is not set # CONFIG_FB_MSM_MDDI_NOVTEC is not set -CONFIG_GPU_MSM_KGSL=y -CONFIG_MSM_KGSL_MMU=y # CONFIG_MSM_HDMI is not set # CONFIG_BACKLIGHT_LCD_SUPPORT is not set diff --git a/arch/arm/configs/cyanogen_mahimahi_defconfig b/arch/arm/configs/cayniarb_mahimahi_defconfig similarity index 97% rename from arch/arm/configs/cyanogen_mahimahi_defconfig rename to arch/arm/configs/cayniarb_mahimahi_defconfig index 3d72f52e620b9..a627b6b67de73 100644 --- a/arch/arm/configs/cyanogen_mahimahi_defconfig +++ b/arch/arm/configs/cayniarb_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sat Jan 7 22:30:19 2012 +# Sun Feb 19 17:02:24 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -33,7 +33,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" -CONFIG_LOCALVERSION="-cyanogenmod" +CONFIG_LOCALVERSION="-cayniarb-ics-stock" CONFIG_LOCALVERSION_AUTO=y CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y @@ -41,7 +41,7 @@ CONFIG_HAVE_KERNEL_LZO=y # CONFIG_KERNEL_GZIP is not set CONFIG_KERNEL_LZMA=y # CONFIG_KERNEL_LZO is not set -# CONFIG_SWAP is not set +CONFIG_SWAP=y # CONFIG_SYSVIPC is not set # CONFIG_POSIX_MQUEUE is not set # CONFIG_BSD_PROCESS_ACCT is not set @@ -91,7 +91,7 @@ CONFIG_RT_GROUP_SCHED=y # CONFIG_NAMESPACES is not set # CONFIG_SCHED_AUTOGROUP is not set # CONFIG_SYSFS_DEPRECATED is not set -# CONFIG_RELAY is not set +CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" CONFIG_RD_GZIP=y @@ -442,7 +442,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 -CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" +CONFIG_CMDLINE="mem=64M console=ttyMSM0" # CONFIG_CMDLINE_FORCE is not set # CONFIG_XIP_KERNEL is not set # CONFIG_KEXEC is not set @@ -510,6 +510,7 @@ CONFIG_FB_EARLYSUSPEND=y # CONFIG_APM_EMULATION is not set # CONFIG_PM_RUNTIME is not set CONFIG_PM_OPS=y +# CONFIG_SUSPEND_TIME is not set CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_NET=y @@ -530,7 +531,10 @@ CONFIG_INET=y # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set CONFIG_IP_FIB_HASH=y -# CONFIG_IP_PNP is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE_DEMUX is not set # CONFIG_ARPD is not set @@ -627,11 +631,11 @@ CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y -# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y # CONFIG_NETFILTER_XT_TARGET_RATEEST is not set # CONFIG_NETFILTER_XT_TARGET_TEE is not set # CONFIG_NETFILTER_XT_TARGET_TPROXY is not set -# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +CONFIG_NETFILTER_XT_TARGET_TRACE=y # CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set # CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set @@ -656,7 +660,7 @@ CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y -CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set # CONFIG_NETFILTER_XT_MATCH_OSF is not set # CONFIG_NETFILTER_XT_MATCH_OWNER is not set CONFIG_NETFILTER_XT_MATCH_POLICY=y @@ -668,7 +672,7 @@ CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y # CONFIG_NETFILTER_XT_MATCH_RATEEST is not set # CONFIG_NETFILTER_XT_MATCH_REALM is not set -CONFIG_NETFILTER_XT_MATCH_RECENT=y +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set # CONFIG_NETFILTER_XT_MATCH_SCTP is not set CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y @@ -972,7 +976,7 @@ CONFIG_MTD_NAND_IDS=y CONFIG_BLK_DEV=y # CONFIG_BLK_DEV_COW_COMMON is not set CONFIG_BLK_DEV_LOOP=y -# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_CRYPTOLOOP=y # # DRBD disabled because PROC_FS, INET or CONNECTOR not selected @@ -1053,15 +1057,31 @@ CONFIG_DUMMY=y # CONFIG_EQUALIZER is not set CONFIG_TUN=y # CONFIG_VETH is not set -# CONFIG_MII is not set +CONFIG_MII=y # CONFIG_PHYLIB is not set -# CONFIG_NET_ETHERNET is not set +CONFIG_NET_ETHERNET=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set +# CONFIG_ETHOC is not set +CONFIG_SMC911X=y +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8851_MLL is not set CONFIG_NETDEV_1000=y # CONFIG_STMMAC_ETH is not set CONFIG_NETDEV_10000=y CONFIG_WLAN=y CONFIG_BCM4329=m -CONFIG_BCM4329_FW_PATH="/system/etc/firmware/fw_bcm4329.bin" +CONFIG_BCM4329_FW_PATH="/vendor/firmware/fw_bcm4329.bin" CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" # CONFIG_HOSTAP is not set @@ -1432,6 +1452,16 @@ CONFIG_S5K3E2FX=y # Graphics support # # CONFIG_DRM is not set +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +CONFIG_MSM_KGSL_MMU=y +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +# CONFIG_MSM_KGSL_MMU_PAGE_FAULT is not set +# CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set # CONFIG_VGASTATE is not set CONFIG_VIDEO_OUTPUT_CONTROL=y CONFIG_FB=y @@ -1467,8 +1497,6 @@ CONFIG_FB_MSM_LEGACY_MDP=y CONFIG_FB_MSM_MDP_PPP=y CONFIG_FB_MSM_LCDC=y # CONFIG_FB_MSM_MDDI is not set -CONFIG_GPU_MSM_KGSL=y -CONFIG_MSM_KGSL_MMU=y # CONFIG_MSM_HDMI is not set # CONFIG_BACKLIGHT_LCD_SUPPORT is not set diff --git a/arch/arm/configs/cyanogen_supersonic_defconfig b/arch/arm/configs/cayniarb_supersonic_defconfig similarity index 96% rename from arch/arm/configs/cyanogen_supersonic_defconfig rename to arch/arm/configs/cayniarb_supersonic_defconfig index b159d9c84ff4d..fabfbef75fdf4 100644 --- a/arch/arm/configs/cyanogen_supersonic_defconfig +++ b/arch/arm/configs/cayniarb_supersonic_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sat Jan 7 22:25:33 2012 +# Sun Feb 19 17:05:33 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -34,7 +34,7 @@ CONFIG_BROKEN_ON_SMP=y CONFIG_LOCK_KERNEL=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" -CONFIG_LOCALVERSION="-GoDm0dE" +CONFIG_LOCALVERSION="-cayniarb-ics-stock" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y @@ -42,7 +42,7 @@ CONFIG_HAVE_KERNEL_LZO=y # CONFIG_KERNEL_GZIP is not set CONFIG_KERNEL_LZMA=y # CONFIG_KERNEL_LZO is not set -# CONFIG_SWAP is not set +CONFIG_SWAP=y # CONFIG_SYSVIPC is not set # CONFIG_POSIX_MQUEUE is not set # CONFIG_BSD_PROCESS_ACCT is not set @@ -97,7 +97,7 @@ CONFIG_PID_NS=y # CONFIG_NET_NS is not set CONFIG_SCHED_AUTOGROUP=y # CONFIG_SYSFS_DEPRECATED is not set -# CONFIG_RELAY is not set +CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" CONFIG_RD_GZIP=y @@ -449,7 +449,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 -CONFIG_CMDLINE="mem=64M console=ttyMSM,115200n8" +CONFIG_CMDLINE="mem=64M console=ttyMSM0" # CONFIG_CMDLINE_FORCE is not set # CONFIG_XIP_KERNEL is not set # CONFIG_KEXEC is not set @@ -517,6 +517,7 @@ CONFIG_FB_EARLYSUSPEND=y # CONFIG_APM_EMULATION is not set # CONFIG_PM_RUNTIME is not set CONFIG_PM_OPS=y +# CONFIG_SUSPEND_TIME is not set CONFIG_ARCH_SUSPEND_POSSIBLE=y CONFIG_NET=y @@ -535,22 +536,20 @@ CONFIG_NET_KEY=y # CONFIG_NET_KEY_MIGRATE is not set CONFIG_INET=y # CONFIG_IP_MULTICAST is not set -CONFIG_IP_ADVANCED_ROUTER=y -CONFIG_ASK_IP_FIB_HASH=y -# CONFIG_IP_FIB_TRIE is not set +# CONFIG_IP_ADVANCED_ROUTER is not set CONFIG_IP_FIB_HASH=y -CONFIG_IP_MULTIPLE_TABLES=y -# CONFIG_IP_ROUTE_MULTIPATH is not set -# CONFIG_IP_ROUTE_VERBOSE is not set -# CONFIG_IP_PNP is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE_DEMUX is not set # CONFIG_ARPD is not set # CONFIG_SYN_COOKIES is not set # CONFIG_INET_AH is not set CONFIG_INET_ESP=y -CONFIG_INET_IPCOMP=y -CONFIG_INET_XFRM_TUNNEL=y +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set CONFIG_INET_TUNNEL=y CONFIG_INET_XFRM_MODE_TRANSPORT=y # CONFIG_INET_XFRM_MODE_TUNNEL is not set @@ -608,7 +607,7 @@ CONFIG_NF_CT_PROTO_SCTP=y CONFIG_NF_CT_PROTO_UDPLITE=y CONFIG_NF_CONNTRACK_AMANDA=y CONFIG_NF_CONNTRACK_FTP=y -# CONFIG_NF_CONNTRACK_H323 is not set +CONFIG_NF_CONNTRACK_H323=y CONFIG_NF_CONNTRACK_IRC=y CONFIG_NF_CONNTRACK_NETBIOS_NS=y CONFIG_NF_CONNTRACK_PPTP=y @@ -639,11 +638,11 @@ CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y -# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y # CONFIG_NETFILTER_XT_TARGET_RATEEST is not set # CONFIG_NETFILTER_XT_TARGET_TEE is not set # CONFIG_NETFILTER_XT_TARGET_TPROXY is not set -# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +CONFIG_NETFILTER_XT_TARGET_TRACE=y # CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set # CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set @@ -668,7 +667,7 @@ CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y -CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set # CONFIG_NETFILTER_XT_MATCH_OSF is not set # CONFIG_NETFILTER_XT_MATCH_OWNER is not set CONFIG_NETFILTER_XT_MATCH_POLICY=y @@ -680,7 +679,7 @@ CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y # CONFIG_NETFILTER_XT_MATCH_RATEEST is not set # CONFIG_NETFILTER_XT_MATCH_REALM is not set -CONFIG_NETFILTER_XT_MATCH_RECENT=y +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set # CONFIG_NETFILTER_XT_MATCH_SCTP is not set CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y @@ -722,7 +721,7 @@ CONFIG_NF_NAT_IRC=y CONFIG_NF_NAT_TFTP=y CONFIG_NF_NAT_AMANDA=y CONFIG_NF_NAT_PPTP=y -# CONFIG_NF_NAT_H323 is not set +CONFIG_NF_NAT_H323=y CONFIG_NF_NAT_SIP=y CONFIG_IP_NF_MANGLE=y # CONFIG_IP_NF_TARGET_CLUSTERIP is not set @@ -1077,12 +1076,30 @@ CONFIG_TUN=y # CONFIG_VETH is not set CONFIG_MII=y # CONFIG_PHYLIB is not set -# CONFIG_NET_ETHERNET is not set +CONFIG_NET_ETHERNET=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set +# CONFIG_ENC28J60 is not set +# CONFIG_ETHOC is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set # CONFIG_NETDEV_1000 is not set # CONFIG_NETDEV_10000 is not set CONFIG_WLAN=y CONFIG_BCM4329=m -CONFIG_BCM4329_FW_PATH="/system/etc/firmware/fw_bcm4329.bin" +CONFIG_BCM4329_FW_PATH="/vendor/firmware/fw_bcm4329.bin" CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" # CONFIG_HOSTAP is not set @@ -1118,7 +1135,7 @@ CONFIG_MSM_RMNET=y # CONFIG_MSM_RMNET_DEBUG is not set # CONFIG_NETPOLL is not set # CONFIG_NET_POLL_CONTROLLER is not set -# CONFIG_GAN_ETH is not set +CONFIG_GAN_ETH=y # CONFIG_ISDN is not set # CONFIG_PHONE is not set @@ -1458,6 +1475,16 @@ CONFIG_S5K3H1GX=y # Graphics support # # CONFIG_DRM is not set +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +CONFIG_MSM_KGSL_MMU=y +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +# CONFIG_MSM_KGSL_MMU_PAGE_FAULT is not set +# CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set # CONFIG_VGASTATE is not set CONFIG_VIDEO_OUTPUT_CONTROL=y CONFIG_FB=y @@ -1495,8 +1522,6 @@ CONFIG_FB_MSM_LCDC=y CONFIG_FB_MSM_MDDI=y CONFIG_FB_MSM_MDDI_EPSON=y CONFIG_FB_MSM_MDDI_NOVTEC=y -CONFIG_GPU_MSM_KGSL=y -CONFIG_MSM_KGSL_MMU=y CONFIG_MSM_HDMI=y # CONFIG_BACKLIGHT_LCD_SUPPORT is not set From 0e9329271ab294fc23b7971444ca5a42e1821895 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sun, 19 Feb 2012 19:11:27 -0500 Subject: [PATCH 2093/2556] msm: board: incrediblec: remove unneeded engineerid kgsl code --- arch/arm/mach-msm/board-incrediblec.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index 0bca16ab04702..4ca89e3270411 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -1351,13 +1351,9 @@ static void __init incrediblec_init(void) if (0 == engineerid || 0xF == engineerid) { mdp_pmem_pdata.start = MSM_PMEM_MDP_XA_BASE; android_pmem_adsp_pdata.start = MSM_PMEM_ADSP_XA_BASE; - kgsl_3d0_resources[1].start = MSM_GPU_MEM_XA_BASE; - kgsl_3d0_resources[1].end = MSM_GPU_MEM_XA_BASE + MSM_GPU_MEM_SIZE - 1; } else if (engineerid >= 3) { mdp_pmem_pdata.start = MSM_PMEM_MDP_BASE + MSM_MEM_128MB_OFFSET; android_pmem_adsp_pdata.start = MSM_PMEM_ADSP_BASE + MSM_MEM_128MB_OFFSET; - kgsl_3d0_resources[1].start = MSM_GPU_MEM_BASE; - kgsl_3d0_resources[1].end = kgsl_3d0_resources[1].start + MSM_GPU_MEM_SIZE - 1; } incrediblec_board_serialno_setup(board_serialno()); From ec3782aaea09b00dc30a9805d9cbcc36c539db85 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Tue, 21 Feb 2012 16:54:14 -0500 Subject: [PATCH 2094/2556] configs: cayniarb: supersonic: disable MSM_HW3D --- arch/arm/configs/cayniarb_supersonic_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/cayniarb_supersonic_defconfig b/arch/arm/configs/cayniarb_supersonic_defconfig index fabfbef75fdf4..fb3998bdd4b8a 100644 --- a/arch/arm/configs/cayniarb_supersonic_defconfig +++ b/arch/arm/configs/cayniarb_supersonic_defconfig @@ -343,7 +343,7 @@ CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y CONFIG_MSM_CPU_FREQ_MAX=998400 CONFIG_MSM_CPU_FREQ_MIN=245000 # CONFIG_AXI_SCREEN_POLICY is not set -CONFIG_MSM_HW3D=y +# CONFIG_MSM_HW3D is not set CONFIG_HTC_ACOUSTIC_QSD=y CONFIG_MSM_QDSP6=y CONFIG_WIFI_CONTROL_FUNC=y From f1382f879e74cc6f0a15efff40279bdde265e81d Mon Sep 17 00:00:00 2001 From: Steve Muckle Date: Wed, 26 Jan 2011 22:55:22 +0800 Subject: [PATCH 2095/2556] ARM: vfp: fix fpsid register subarchitecture field mask width The subarchitecture field in the fpsid register is 7 bits wide. The topmost bit is used to designate that the subarchitecture designer is not ARM. This change has the side effect of causing VFP implementations other than ARM to be reported as VFPv3 with common VFP subarch v3. Before this change it would be reported as VFPv1 with an implementation defined subarchitecture. --- arch/arm/include/asm/vfp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/include/asm/vfp.h b/arch/arm/include/asm/vfp.h index ea2e3ac0cac01..a4459dcfaea48 100644 --- a/arch/arm/include/asm/vfp.h +++ b/arch/arm/include/asm/vfp.h @@ -21,7 +21,7 @@ #define FPSID_FORMAT_MASK (0x3 << FPSID_FORMAT_BIT) #define FPSID_NODOUBLE (1<<20) #define FPSID_ARCH_BIT (16) -#define FPSID_ARCH_MASK (0xF << FPSID_ARCH_BIT) +#define FPSID_ARCH_MASK (0x7F << FPSID_ARCH_BIT) #define FPSID_PART_BIT (8) #define FPSID_PART_MASK (0xFF << FPSID_PART_BIT) #define FPSID_VARIANT_BIT (4) From 1ffbbe1219ff88e2d66b5459d44fcba1d9035ee7 Mon Sep 17 00:00:00 2001 From: Jeff Ohlstein Date: Wed, 23 Jun 2010 12:59:04 -0700 Subject: [PATCH 2096/2556] sched: Extend completion api to allow io_wait time tracking Adds a function wait_for_completion_io which behaves like wait_for_completion, except it calls io_schedule instead of schedule. This indicates that the process waiting on the completion is waiting on an io event, and keeps statistics accordingly. Change-Id: I2514d62ff7f26441782a4cbebc4a18c07bb5ad74 Signed-off-by: Jeff Ohlstein --- include/linux/completion.h | 1 + kernel/sched.c | 35 ++++++++++++++++++++++++++--------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/include/linux/completion.h b/include/linux/completion.h index 51494e6b55487..a5b2e1ca5ceb1 100644 --- a/include/linux/completion.h +++ b/include/linux/completion.h @@ -77,6 +77,7 @@ static inline void init_completion(struct completion *x) } extern void wait_for_completion(struct completion *); +extern void wait_for_completion_io(struct completion *); extern int wait_for_completion_interruptible(struct completion *x); extern int wait_for_completion_killable(struct completion *x); extern unsigned long wait_for_completion_timeout(struct completion *x, diff --git a/kernel/sched.c b/kernel/sched.c index 488bd59505be8..0c7f4eceed1eb 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -4304,7 +4304,7 @@ void complete_all(struct completion *x) EXPORT_SYMBOL(complete_all); static inline long __sched -do_wait_for_common(struct completion *x, long timeout, int state) +do_wait_for_common(struct completion *x, long timeout, int state, int iowait) { if (!x->done) { DECLARE_WAITQUEUE(wait, current); @@ -4317,7 +4317,10 @@ do_wait_for_common(struct completion *x, long timeout, int state) } __set_current_state(state); spin_unlock_irq(&x->wait.lock); - timeout = schedule_timeout(timeout); + if (iowait) + timeout = io_schedule_timeout(timeout); + else + timeout = schedule_timeout(timeout); spin_lock_irq(&x->wait.lock); } while (!x->done && timeout); __remove_wait_queue(&x->wait, &wait); @@ -4329,12 +4332,12 @@ do_wait_for_common(struct completion *x, long timeout, int state) } static long __sched -wait_for_common(struct completion *x, long timeout, int state) +wait_for_common(struct completion *x, long timeout, int state, int iowait) { might_sleep(); spin_lock_irq(&x->wait.lock); - timeout = do_wait_for_common(x, timeout, state); + timeout = do_wait_for_common(x, timeout, state, iowait); spin_unlock_irq(&x->wait.lock); return timeout; } @@ -4351,10 +4354,23 @@ wait_for_common(struct completion *x, long timeout, int state) */ void __sched wait_for_completion(struct completion *x) { - wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE); + wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE, 0); } EXPORT_SYMBOL(wait_for_completion); +/** + * wait_for_completion_io: - waits for completion of a task + * @x: holds the state of this particular completion + * + * This waits for completion of a specific task to be signaled. Treats any + * sleeping as waiting for IO for the purposes of process accounting. + */ +void __sched wait_for_completion_io(struct completion *x) +{ + wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE, 1); +} +EXPORT_SYMBOL(wait_for_completion_io); + /** * wait_for_completion_timeout: - waits for completion of a task (w/timeout) * @x: holds the state of this particular completion @@ -4367,7 +4383,7 @@ EXPORT_SYMBOL(wait_for_completion); unsigned long __sched wait_for_completion_timeout(struct completion *x, unsigned long timeout) { - return wait_for_common(x, timeout, TASK_UNINTERRUPTIBLE); + return wait_for_common(x, timeout, TASK_UNINTERRUPTIBLE, 0); } EXPORT_SYMBOL(wait_for_completion_timeout); @@ -4380,7 +4396,8 @@ EXPORT_SYMBOL(wait_for_completion_timeout); */ int __sched wait_for_completion_interruptible(struct completion *x) { - long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_INTERRUPTIBLE); + long t = + wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_INTERRUPTIBLE, 0); if (t == -ERESTARTSYS) return t; return 0; @@ -4399,7 +4416,7 @@ long __sched wait_for_completion_interruptible_timeout(struct completion *x, unsigned long timeout) { - return wait_for_common(x, timeout, TASK_INTERRUPTIBLE); + return wait_for_common(x, timeout, TASK_INTERRUPTIBLE, 0); } EXPORT_SYMBOL(wait_for_completion_interruptible_timeout); @@ -4412,7 +4429,7 @@ EXPORT_SYMBOL(wait_for_completion_interruptible_timeout); */ int __sched wait_for_completion_killable(struct completion *x) { - long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_KILLABLE); + long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_KILLABLE, 0); if (t == -ERESTARTSYS) return t; return 0; From 9cca674750c71f0c4e653f6950a0c5fbd448f847 Mon Sep 17 00:00:00 2001 From: Murali Palnati Date: Mon, 18 Apr 2011 18:58:00 +0530 Subject: [PATCH 2097/2556] mmc: Attribute the IO wait time properly in mmc_wait_for_req(). In mmc_wait_for_req() function, change the call wait_for_completion() to wait_for_completion_io(). This change makes the kernel account for wait time as I/O wait and through another configuration this I/O wait is treated as busy which makes the acpu clock to scale up. CRs-Fixed: 283420 Change-Id: If3de6325b7e4cf8141c808e5853f4fc2dbb4e3ec Signed-off-by: Murali Palnati --- drivers/mmc/core/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 25d0c63324e2e..5094008e82e1c 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -243,8 +243,9 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) msmsdcc_request_end(msm_host, mrq); } #else - wait_for_completion(&complete); + wait_for_completion_io(&complete); #endif + } EXPORT_SYMBOL(mmc_wait_for_req); From 4c1ad4a08d1c2167363f4dd6d1617c63ada7fb72 Mon Sep 17 00:00:00 2001 From: Jin Hong Date: Wed, 20 Apr 2011 13:20:44 -0700 Subject: [PATCH 2098/2556] ARM: mm: add back config CPU_CACHE_ERR_REPORT CPU_CACHE_ERR_REPORT was lost from arch/arm/mm/Kconfig. Adding it back to support Scorpion processor cache error report. CRs-fixed: 284496 Change-Id: I0154f056564f0081bc7404c0e4abacbb26931f04 Signed-off-by: Jin Hong --- arch/arm/mm/Kconfig | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index e4509bae8fc48..af8aac5320d6b 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -730,6 +730,19 @@ config CPU_DCACHE_SIZE If your SoC is configured to have a different size, define the value here with proper conditions. +config CPU_CACHE_ERR_REPORT + bool "Report errors in the L1 and L2 caches" + depends on ARCH_MSM_SCORPION || ARCH_MSM_SCORPIONMP + default n + help + The Scorpion processor supports reporting L2 errors, L1 icache parity + errors, and L1 dcache parity errors as imprecise external aborts. If + this option is not enabled these errors will go unreported and data + corruption will occur. + + Say Y here to have errors in the L1 and L2 caches reported as + imprecise data aborts. + config CPU_DCACHE_WRITETHROUGH bool "Force write through D-cache" depends on (CPU_ARM740T || CPU_ARM920T || CPU_ARM922T || CPU_ARM925T || CPU_ARM926T || CPU_ARM940T || CPU_ARM946E || CPU_ARM1020 || CPU_FA526) && !CPU_DCACHE_DISABLE From 37cec2626f6f7c998bc04e24055e6e13429b5791 Mon Sep 17 00:00:00 2001 From: Jeffrey Ohlstein Date: Thu, 1 Oct 2009 16:56:30 -0700 Subject: [PATCH 2099/2556] msm: dma: Track waiting for requests as io_wait time Using wait_for_completion_io instead of wait_for_completion improves the kernel's statistics about time spent waiting for io. Signed-off-by: Jeffrey Ohlstein --- arch/arm/mach-msm/dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/dma.c b/arch/arm/mach-msm/dma.c index d6650f66b086a..8ad7f04494f30 100644 --- a/arch/arm/mach-msm/dma.c +++ b/arch/arm/mach-msm/dma.c @@ -151,7 +151,7 @@ int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr) init_completion(&cmd.complete); msm_dmov_enqueue_cmd(id, &cmd.dmov_cmd); - wait_for_completion(&cmd.complete); + wait_for_completion_io(&cmd.complete); if (cmd.result != 0x80000002) { PRINT_ERROR("dmov_exec_cmdptr(%d): ERROR, result: %x\n", id, cmd.result); From 7babd96deb03dc83d72e4488d7e5d83d0f60be6c Mon Sep 17 00:00:00 2001 From: "Raymond Golo (intersectRaven)" Date: Fri, 6 May 2011 15:08:34 +0800 Subject: [PATCH 2100/2556] Fix compile error due to wait_for_common. An earlier commit changed the number of arguments for wait_for_common to also include a flag for io-wait. We add 0 to the arguments passed since the only function to use io-wait is wait_for_completion_io. --- kernel/sched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched.c b/kernel/sched.c index 0c7f4eceed1eb..3a2d949118185 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -4449,7 +4449,7 @@ long __sched wait_for_completion_killable_timeout(struct completion *x, unsigned long timeout) { - return wait_for_common(x, timeout, TASK_KILLABLE); + return wait_for_common(x, timeout, TASK_KILLABLE, 0); } EXPORT_SYMBOL(wait_for_completion_killable_timeout); From 89795cd9b1f6c5871bf3d1bac2942745ec7f691e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Tue, 9 Jun 2009 20:21:44 -0700 Subject: [PATCH 2101/2556] futex: Restore one of the fast paths eliminated by 38d47c1b7075bd7ec3881141bb3629da58f88dab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This improves futex performance until our user-space code is fixed to use FUTEX_PRIVATE_FLAG for non-shared futexes. Signed-off-by: Arve Hjønnevåg --- kernel/futex.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/kernel/futex.c b/kernel/futex.c index d5065e8283dac..3464fa3b0968b 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -235,6 +235,7 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key) struct mm_struct *mm = current->mm; struct page *page, *page_head; int err; + struct vm_area_struct *vma; /* * The futex address must be "naturally" aligned. @@ -260,6 +261,37 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key) return 0; } + /* + * The futex is hashed differently depending on whether + * it's in a shared or private mapping. So check vma first. + */ + vma = find_extend_vma(mm, address); + if (unlikely(!vma)) + return -EFAULT; + + /* + * Permissions. + */ + if (unlikely((vma->vm_flags & (VM_IO|VM_READ)) != VM_READ)) + return (vma->vm_flags & VM_IO) ? -EPERM : -EACCES; + + /* + * Private mappings are handled in a simple way. + * + * NOTE: When userspace waits on a MAP_SHARED mapping, even if + * it's a read-only handle, it's expected that futexes attach to + * the object not the particular process. Therefore we use + * VM_MAYSHARE here, not VM_SHARED which is restricted to shared + * mappings of _writable_ handles. + */ + if (likely(!(vma->vm_flags & VM_MAYSHARE))) { + key->both.offset |= FUT_OFF_MMSHARED; /* reference taken on mm */ + key->private.mm = mm; + key->private.address = address; + get_futex_key_refs(key); + return 0; + } + again: err = get_user_pages_fast(address, 1, 1, &page); if (err < 0) From 7e00f83a4f97112b7197459be1edb5369237e8e4 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Mon, 3 Jan 2011 08:58:26 +0530 Subject: [PATCH 2102/2556] cgroup: fix cpuset permission check The permission check code added by "cgroup: Add generic cgroup subsystem permission checks" does not build and also leaves a variable uninitialized. Fix it. Change-Id: I8009d6315527725d45b20792b7cd74aedc9bff44 Signed-off-by: Rabin Vincent Signed-off-by: Christian Bejram --- kernel/cpuset.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/cpuset.c b/kernel/cpuset.c index e210b3d30085f..6c41a147c473c 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -1382,9 +1382,10 @@ static int cpuset_can_attach(struct cgroup_subsys *ss, struct cgroup *cont, int ret; struct cpuset *cs = cgroup_cs(cont); - if ((current != task) && (!capable(CAP_SYS_ADMIN))) { + if ((current != tsk) && (!capable(CAP_SYS_ADMIN))) { const struct cred *cred = current_cred(), *tcred; + tcred = __task_cred(tsk); if (cred->euid != tcred->uid && cred->euid != tcred->suid) return -EPERM; } From 35bb477df474aa052aa51dd10a962aa624caf068 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 23 Mar 2011 21:04:56 -0700 Subject: [PATCH 2103/2556] Treat all wake locks grabbed during driver resume as "wakeup wake locks" The first wakelock grabbed at resume time may not identify the waking activity. Treat all wake locks grabbed during driver resume as "wakeup wake locks". Change-Id: Ife6f47278ed76e3e13b1414695c5436d8030cef7 Signed-off-by: Todd Poynor --- include/linux/wakelock.h | 1 + kernel/power/wakelock.c | 57 +++++++++++++++++++++++++++++++++------- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/include/linux/wakelock.h b/include/linux/wakelock.h index a096d24ada1db..c5fcd1e85618e 100755 --- a/include/linux/wakelock.h +++ b/include/linux/wakelock.h @@ -43,6 +43,7 @@ struct wake_lock { int count; int expire_count; int wakeup_count; + int wakeup_last_generation; ktime_t total_time; ktime_t prevent_suspend_time; ktime_t max_time; diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c index c10d0ee7907ed..01bfbf395f8b3 100644 --- a/kernel/power/wakelock.c +++ b/kernel/power/wakelock.c @@ -50,9 +50,11 @@ suspend_state_t requested_suspend_state = PM_SUSPEND_MEM; static struct wake_lock unknown_wakeup; #ifdef CONFIG_WAKELOCK_STAT +static bool resuming_devices; +static int suspend_generation; static struct wake_lock deleted_wake_locks; static ktime_t last_sleep_time_update; -static int wait_for_wakeup; + int get_expired_time(struct wake_lock *lock, ktime_t *expire_time) { @@ -267,10 +269,37 @@ static void suspend(struct work_struct *work) } entry_event_num = current_event_num; + +#ifdef CONFIG_WAKELOCK_STAT + suspend_generation++; +#endif + sys_sync(); if (debug_mask & DEBUG_SUSPEND) pr_info("suspend: enter suspend\n"); ret = pm_suspend(requested_suspend_state); + +#ifdef CONFIG_WAKELOCK_STAT + if (debug_mask & DEBUG_WAKEUP) { + unsigned long irqflags; + struct wake_lock *lock; + int type; + + spin_lock_irqsave(&list_lock, irqflags); + pr_info("wakeup wake lock:"); + + for (type = 0; type < WAKE_LOCK_TYPE_COUNT; type++) { + list_for_each_entry(lock, &active_wake_locks[type], + link) + if (lock->stat.wakeup_last_generation == + suspend_generation) + pr_cont(" %s", lock->name); + } + pr_cont("\n"); + spin_unlock_irqrestore(&list_lock, irqflags); + } +#endif + if (debug_mask & DEBUG_EXIT_SUSPEND) { struct timespec ts; struct rtc_time tm; @@ -282,8 +311,8 @@ static void suspend(struct work_struct *work) tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); } if (current_event_num == entry_event_num) { - if (debug_mask & DEBUG_SUSPEND) - pr_info("suspend: pm_suspend returned with no event\n"); + if (debug_mask & DEBUG_WAKEUP) + pr_info("suspend: no wakelock for wakeup source\n"); wake_lock_timeout(&unknown_wakeup, HZ / 2); } } @@ -310,16 +339,24 @@ static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0); static int power_suspend_late(struct device *dev) { int ret = has_wake_lock(WAKE_LOCK_SUSPEND) ? -EAGAIN : 0; -#ifdef CONFIG_WAKELOCK_STAT - wait_for_wakeup = 1; -#endif + if (debug_mask & DEBUG_SUSPEND) pr_info("power_suspend_late return %d\n", ret); + + if (!ret) + resuming_devices = true; + return ret; } +static void wakelocks_resume_complete(struct device *dev) +{ + resuming_devices = false; +} + static struct dev_pm_ops power_driver_pm_ops = { .suspend_noirq = power_suspend_late, + .complete = wakelocks_resume_complete, }; static struct platform_driver power_driver = { @@ -344,6 +381,7 @@ void wake_lock_init(struct wake_lock *lock, int type, const char *name) lock->stat.count = 0; lock->stat.expire_count = 0; lock->stat.wakeup_count = 0; + lock->stat.wakeup_last_generation = 0; lock->stat.total_time = ktime_set(0, 0); lock->stat.prevent_suspend_time = ktime_set(0, 0); lock->stat.max_time = ktime_set(0, 0); @@ -397,11 +435,10 @@ static void wake_lock_internal( BUG_ON(type >= WAKE_LOCK_TYPE_COUNT); BUG_ON(!(lock->flags & WAKE_LOCK_INITIALIZED)); #ifdef CONFIG_WAKELOCK_STAT - if (type == WAKE_LOCK_SUSPEND && wait_for_wakeup) { - if (debug_mask & DEBUG_WAKEUP) - pr_info("wakeup wake lock: %s\n", lock->name); - wait_for_wakeup = 0; + if (type == WAKE_LOCK_SUSPEND && resuming_devices && + lock->stat.wakeup_last_generation != suspend_generation) { lock->stat.wakeup_count++; + lock->stat.wakeup_last_generation = suspend_generation; } if ((lock->flags & WAKE_LOCK_AUTO_EXPIRE) && (long)(lock->expires - jiffies) <= 0) { From 4223a544e55b1f6dcd2256a4ed2a6810be8193fd Mon Sep 17 00:00:00 2001 From: Yuanyuan Zhong Date: Fri, 14 Jan 2011 11:21:26 -0600 Subject: [PATCH 2104/2556] ashmem: fix deadlock when system is low on memory When system is low on memory, allocating memory while holding the ashmem_mutex may try to directly reclaim memory. Then ashmem_shrink() is called in same thread. It will deadlock at acquiring ashmem_mutex. This change lets ashmem_shrink() return failure if ashmem_mutex is not available. Memory will be reclaimed from others. Change-Id: I3b6024d5477d37245f4ebde6d7069fb77af789ee Signed-off-by: Yuanyuan Zhong --- mm/ashmem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mm/ashmem.c b/mm/ashmem.c index a16f3f7cfa2d2..803b90a16fb82 100644 --- a/mm/ashmem.c +++ b/mm/ashmem.c @@ -361,7 +361,8 @@ static int ashmem_shrink(struct shrinker *s, int nr_to_scan, gfp_t gfp_mask) if (!nr_to_scan) return lru_count; - mutex_lock(&ashmem_mutex); + if (!mutex_trylock(&ashmem_mutex)) + return -1; list_for_each_entry_safe(range, next, &ashmem_lru_list, lru) { struct inode *inode = range->asma->file->f_dentry->d_inode; loff_t start = range->pgstart * PAGE_SIZE; From aed9fb6a105afa378549657b553724eb9ff8dd3c Mon Sep 17 00:00:00 2001 From: Niranjana Vishwanathapura Date: Mon, 29 Nov 2010 14:58:14 -0700 Subject: [PATCH 2105/2556] msm: rpc: free the buffer if read fails. In do_read_data(), while reading a data fragment, if read() fails to read all the required bytes, free the fragment buffer. Also correct the allocation size for fragment buffer. Signed-off-by: Niranjana Vishwanathapura --- arch/arm/mach-msm/smd_rpcrouter.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c index ed9745a9a93fb..73b8b38502217 100644 --- a/arch/arm/mach-msm/smd_rpcrouter.c +++ b/arch/arm/mach-msm/smd_rpcrouter.c @@ -641,11 +641,13 @@ static void do_read_data(struct work_struct *work) hdr.size -= sizeof(pm); - frag = rr_malloc(hdr.size + sizeof(*frag)); + frag = rr_malloc(sizeof(*frag)); frag->next = NULL; frag->length = hdr.size; - if (rr_read(frag->data, hdr.size)) + if (rr_read(frag->data, hdr.size)) { + kfree(frag); goto fail_io; + } ept = rpcrouter_lookup_local_endpoint(hdr.dst_cid); if (!ept) { From d5805ec53e2da7938e9748ea983d99eb9b206b9a Mon Sep 17 00:00:00 2001 From: Mandeep Singh Baines Date: Fri, 22 Oct 2010 15:51:51 -0700 Subject: [PATCH 2106/2556] vmscan: move referenced VM_EXEC pages to active list In commit 64574746, "vmscan: detect mapped file pages used only once", Johannes Weiner, added logic to page_check_reference to cycle again used once pages. In commit 8cab4754, "vmscan: make mapped executable pages the first class citizen", Wu Fengguang, added logic to shrink_active_list which protects file-backed VM_EXEC pages by keeping them in the active_list if they are referenced. This patch adds logic to move such pages from the inactive list to the active list immediately if they have been referenced. If a VM_EXEC page is seen as referenced during an inactive list scan, that reference must have occurred after the page was put on the inactive list. There is no need to wait for the page to be referenced again. Change-Id: I17c312e916377e93e5a92c52518b6c829f9ab30b Signed-off-by: Mandeep Singh Baines --- mm/vmscan.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mm/vmscan.c b/mm/vmscan.c index a74bf72e5a679..ddd437b35037d 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -657,6 +657,17 @@ static enum page_references page_check_references(struct page *page, if (referenced_ptes) { if (PageAnon(page)) return PAGEREF_ACTIVATE; + + /* + * Identify referenced, file-backed active pages and move them + * to the active list. We know that this page has been + * referenced since being put on the inactive list. VM_EXEC + * pages are only moved to the inactive list when they have not + * been referenced between scans (see shrink_active_list). + */ + if ((vm_flags & VM_EXEC) && page_is_file_cache(page)) + return PAGEREF_ACTIVATE; + /* * All mapped pages start out with page table * references from the instantiating fault, so we need From d470147c4d1c67067c56567162c9636d0145264d Mon Sep 17 00:00:00 2001 From: Steven Barrett Date: Sun, 16 Jan 2011 18:57:32 -0600 Subject: [PATCH 2107/2556] net: allow TCP YeAH configuration as default congestion control --- net/ipv4/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index a5a1050595d18..8a79fbb4641eb 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -616,6 +616,9 @@ choice config DEFAULT_VEGAS bool "Vegas" if TCP_CONG_VEGAS=y + config DEFAULT_YEAH + bool "YeAH" if TCP_CONG_YEAH=y + config DEFAULT_VENO bool "Veno" if TCP_CONG_VENO=y @@ -641,6 +644,7 @@ config DEFAULT_TCP_CONG default "htcp" if DEFAULT_HTCP default "hybla" if DEFAULT_HYBLA default "vegas" if DEFAULT_VEGAS + default "yeah" if DEFAULT_YEAH default "westwood" if DEFAULT_WESTWOOD default "veno" if DEFAULT_VENO default "reno" if DEFAULT_RENO From 2e5ce40906acab7449c404bd0af0456aab21cf62 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Sat, 18 Dec 2010 12:46:35 +1100 Subject: [PATCH 2108/2556] fs: mark_inode_dirty barrier fix Filesystems appear to be using ->dirty_inode, expecting that the dirtying operating is done and visible to all CPUs (eg. setting private inode dirty bits, without any barriers themselves). So release the dirty "critical section" with a barrier before calling ->dirty_inode. Cost is not significantly changed, because we're just moving the barrier. Those filesystems that do use ->dirty_inode should have to care slightly less about barriers, which is a good thing. Signed-off-by: Nick Piggin --- fs/fs-writeback.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 0fd1195867164..6a2e154a6d3a3 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -994,6 +994,15 @@ void __mark_inode_dirty(struct inode *inode, int flags) struct backing_dev_info *bdi = NULL; bool wakeup_bdi = false; + /* + * Make sure that changes are seen by all cpus before we test i_state + * or mark anything as being dirty. Ie. all dirty state should be + * written to the inode and visible. Like an "unlock" operation, the + * mark_inode_dirty call must "release" our ordering window that is + * opened when we started modifying the inode. + */ + smp_mb(); + /* * Don't do this for I_DIRTY_PAGES - that doesn't actually * dirty the inode itself @@ -1003,12 +1012,6 @@ void __mark_inode_dirty(struct inode *inode, int flags) sb->s_op->dirty_inode(inode); } - /* - * make sure that changes are seen by all cpus before we test i_state - * -- mikulas - */ - smp_mb(); - /* avoid the locking if we can */ if ((inode->i_state & flags) == flags) return; From 740cd5cd68e136f03b305adf7a65c6e1ac156147 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Sat, 18 Dec 2010 12:46:36 +1100 Subject: [PATCH 2109/2556] fs: simple fsync race fix It is incorrect to test inode dirty bits without participating in the inode writeback protocol. Inode writeback sets I_SYNC and clears I_DIRTY_?, then writes out the particular bits, then clears I_SYNC when it is done. BTW. it may not completely write all pages out, so I_DIRTY_PAGES would get set again. This is a standard pattern used throughout the kernel's writeback caches (I_SYNC ~= I_WRITEBACK, if that makes it clearer). And so it is not possible to determine an inode's dirty status just by checking I_DIRTY bits. Especially not for the purpose of data integrity syncs. Missing the check for these bits means that fsync can complete while writeback to the inode is underway. Inode writeback functions get this right, so call into them rather than try to shortcut things by testing dirty state improperly. Signed-off-by: Nick Piggin --- fs/exofs/file.c | 5 ----- fs/libfs.c | 5 ----- 2 files changed, 10 deletions(-) diff --git a/fs/exofs/file.c b/fs/exofs/file.c index b905c79b4f0af..4c0d6bac91433 100644 --- a/fs/exofs/file.c +++ b/fs/exofs/file.c @@ -48,11 +48,6 @@ static int exofs_file_fsync(struct file *filp, int datasync) struct inode *inode = filp->f_mapping->host; struct super_block *sb; - if (!(inode->i_state & I_DIRTY)) - return 0; - if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) - return 0; - ret = sync_inode_metadata(inode, 1); /* This is a good place to write the sb */ diff --git a/fs/libfs.c b/fs/libfs.c index c88eab55aec95..2645d06e7e92c 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -912,11 +912,6 @@ int generic_file_fsync(struct file *file, int datasync) int ret; ret = sync_mapping_buffers(inode->i_mapping); - if (!(inode->i_state & I_DIRTY)) - return ret; - if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) - return ret; - err = sync_inode_metadata(inode, 1); if (ret == 0) ret = err; From 2683189064bf4569e10ace4a7a7774bfa956dbcf Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Sat, 18 Dec 2010 12:46:37 +1100 Subject: [PATCH 2110/2556] fs: introduce inode writeback helpers Inode dirty state cannot be securely tested without participating properly in the inode writeback protocol. Some filesystems need to check this state, so break out the code into helpers and make them available. This could also be used to reduce strange interactions between background writeback and fsync. Currently if we fsync a single page in a file, the entire file gets requeued to the back of the background IO list, even if it is due for writeout and has a large number of pages. That's left for a later time. Signed-off-by: Nick Piggin --- fs/fs-writeback.c | 168 +++++++++++++++++++++++++++++---------------- include/linux/fs.h | 2 + 2 files changed, 112 insertions(+), 58 deletions(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 6a2e154a6d3a3..4e2cc7f67d40d 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -313,26 +313,25 @@ static void inode_wait_for_writeback(struct inode *inode) } } -/* - * Write out an inode's dirty pages. Called under inode_lock. Either the - * caller has ref on the inode (either via __iget or via syscall against an fd) - * or the inode has I_WILL_FREE set (via generic_forget_inode) +/** + * inode_writeback_begin -- prepare to writeback an inode + * @indoe: inode to write back + * @wait: synch writeout or not + * @Returns: 0 if wait == 0 and this call would block (due to other writeback). + * otherwise returns 1. * - * If `wait' is set, wait on the writeout. + * Context: inode_lock must be held, may be dropped. Returns with it held. * - * The whole writeout design is quite complex and fragile. We want to avoid - * starvation of particular inodes when others are being redirtied, prevent - * livelocks, etc. + * inode_writeback_begin sets up an inode to be written back (data and/or + * metadata). This must be called before examining I_DIRTY state of the + * inode, and should be called at least before any data integrity writeout. * - * Called under inode_lock. + * If inode_writeback_begin returns 1, it must be followed by a call to + * inode_writeback_end. */ -static int -writeback_single_inode(struct inode *inode, struct writeback_control *wbc) +int inode_writeback_begin(struct inode *inode, int wait) { - struct address_space *mapping = inode->i_mapping; - unsigned dirty; - int ret; - + assert_spin_locked(&inode_lock); if (!atomic_read(&inode->i_count)) WARN_ON(!(inode->i_state & (I_WILL_FREE|I_FREEING))); else @@ -341,16 +340,10 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc) if (inode->i_state & I_SYNC) { /* * If this inode is locked for writeback and we are not doing - * writeback-for-data-integrity, move it to b_more_io so that - * writeback can proceed with the other inodes on s_io. - * - * We'll have another go at writing back this inode when we - * completed a full scan of b_io. + * writeback-for-data-integrity, skip it. */ - if (wbc->sync_mode != WB_SYNC_ALL) { - requeue_io(inode); + if (!wait) return 0; - } /* * It's a data-integrity sync. We must wait. @@ -360,9 +353,91 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc) BUG_ON(inode->i_state & I_SYNC); - /* Set I_SYNC, reset I_DIRTY_PAGES */ inode->i_state |= I_SYNC; inode->i_state &= ~I_DIRTY_PAGES; + + return 1; +} +EXPORT_SYMBOL(inode_writeback_begin); + +/** + * inode_writeback_end - end a writeback section opened by inode_writeback_begin + * @inode: inode in question + * @Returns: 0 if the inode still has dirty pagecache, otherwise 1. + * + * Context: inode_lock must be held, not dropped. + * + * inode_writeback_end must follow a successful call to inode_writeback_begin + * after we have finished submitting writeback to the inode. + */ +int inode_writeback_end(struct inode *inode) +{ + int ret = 1; + + assert_spin_locked(&inode_lock); + BUG_ON(!(inode->i_state & I_SYNC)); + + if (!(inode->i_state & I_FREEING)) { + if (mapping_tagged(inode->i_mapping, PAGECACHE_TAG_DIRTY)) { + /* + * We didn't write back all the pages. nfs_writepages() + * sometimes bales out without doing anything. + */ + inode->i_state |= I_DIRTY_PAGES; + ret = 0; + } else if (inode->i_state & I_DIRTY) { + /* + * Filesystems can dirty the inode during writeback + * operations, such as delayed allocation during + * submission or metadata updates after data IO + * completion. + */ + redirty_tail(inode); + } else { + /* + * The inode is clean. At this point we either have + * a reference to the inode or it's on it's way out. + * No need to add it back to the LRU. + */ + list_del_init(&inode->i_wb_list); + } + } + inode->i_state &= ~I_SYNC; + inode_sync_complete(inode); + + return ret; +} +EXPORT_SYMBOL(inode_writeback_end); + +/* + * Write out an inode's dirty pages. Called under inode_lock. Either the + * caller has ref on the inode (either via __iget or via syscall against an fd) + * or the inode has I_WILL_FREE set (via generic_forget_inode) + * + * If `wait' is set, wait on the writeout. + * + * The whole writeout design is quite complex and fragile. We want to avoid + * starvation of particular inodes when others are being redirtied, prevent + * livelocks, etc. + * + * Called under inode_lock. + */ +static int +writeback_single_inode(struct inode *inode, struct writeback_control *wbc) +{ + struct address_space *mapping = inode->i_mapping; + unsigned dirty; + int ret; + + if (!inode_writeback_begin(inode, wbc->sync_mode == WB_SYNC_ALL)) { + /* + * We'll have another go at writing back this inode when we + * completed a full scan of b_io. + */ + requeue_io(inode); + return 0; + } + spin_unlock(&inode_lock); ret = do_writepages(mapping, wbc); @@ -395,47 +470,24 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc) } spin_lock(&inode_lock); - inode->i_state &= ~I_SYNC; - if (!(inode->i_state & I_FREEING)) { - if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) { - /* - * We didn't write back all the pages. nfs_writepages() - * sometimes bales out without doing anything. - */ - inode->i_state |= I_DIRTY_PAGES; - if (wbc->nr_to_write <= 0) { - /* - * slice used up: queue for next turn - */ - requeue_io(inode); - } else { - /* - * Writeback blocked by something other than - * congestion. Delay the inode for some time to - * avoid spinning on the CPU (100% iowait) - * retrying writeback of the dirty page/inode - * that cannot be performed immediately. - */ - redirty_tail(inode); - } - } else if (inode->i_state & I_DIRTY) { + if (!inode_writeback_end(inode)) { + if (wbc->nr_to_write <= 0) { /* - * Filesystems can dirty the inode during writeback - * operations, such as delayed allocation during - * submission or metadata updates after data IO - * completion. + * slice used up: queue for next turn */ - redirty_tail(inode); + requeue_io(inode); } else { /* - * The inode is clean. At this point we either have - * a reference to the inode or it's on it's way out. - * No need to add it back to the LRU. + * Writeback blocked by something other than + * congestion. Delay the inode for some time to + * avoid spinning on the CPU (100% iowait) + * retrying writeback of the dirty page/inode + * that cannot be performed immediately. */ - list_del_init(&inode->i_wb_list); + redirty_tail(inode); } } - inode_sync_complete(inode); + return ret; } diff --git a/include/linux/fs.h b/include/linux/fs.h index e38b50a4b9d25..52079af48ecc8 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1780,6 +1780,8 @@ static inline void file_accessed(struct file *file) int sync_inode(struct inode *inode, struct writeback_control *wbc); int sync_inode_metadata(struct inode *inode, int wait); +int inode_writeback_begin(struct inode *inode, int wait); +int inode_writeback_end(struct inode *inode); struct file_system_type { const char *name; From ebe5e8d8fc682e77b48815353e82b23fea1059f4 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Sat, 18 Dec 2010 12:46:38 +1100 Subject: [PATCH 2111/2556] fs: preserve inode dirty bits on failed metadata writeback Otherwise we think the inode is clean even if syncing failed. Signed-off-by: Nick Piggin --- fs/fs-writeback.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 4e2cc7f67d40d..62bdd807ca6d2 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -461,15 +461,25 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc) spin_lock(&inode_lock); dirty = inode->i_state & I_DIRTY; inode->i_state &= ~(I_DIRTY_SYNC | I_DIRTY_DATASYNC); - spin_unlock(&inode_lock); /* Don't write the inode if only I_DIRTY_PAGES was set */ if (dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) { - int err = write_inode(inode, wbc); + int err; + + spin_unlock(&inode_lock); + err = write_inode(inode, wbc); if (ret == 0) ret = err; + spin_lock(&inode_lock); + if (err) { + /* + * Inode writeout failed, restore inode metadata + * dirty bits. + */ + inode->i_state |= dirty & + (I_DIRTY_SYNC | I_DIRTY_DATASYNC); + } } - spin_lock(&inode_lock); if (!inode_writeback_end(inode)) { if (wbc->nr_to_write <= 0) { /* From 9f4ef2a6ba3dd7c2b0ca2da7016d85b4366af18c Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Sat, 18 Dec 2010 12:46:39 +1100 Subject: [PATCH 2112/2556] fs: ext2 inode sync fix There is a big fuckup with inode metadata writeback (I suspect in a lot of filesystems, but I've only dared to look at a couple as yet). ext2 relies on ->write_inode being called from sync_inode_metadata in fsync in order to sync the inode. However I_DIRTY_SYNC gets cleared after a call to this guy, and it doesn't actually write back and wait on the inode block unless it is called for sync. This means that write_inode from background writeback can kill the inode dirty bits without the data getting to disk. Fsync will subsequently miss it. The fix is for ->write_inode to dirty the buffers/cache, and then ->fsync to write back the dirty data. In the full filesystem sync case, buffercache writeback in the generic code will write back the dirty data. Other filesystems could use ->sync_fs for this. Signed-off-by: Nick Piggin --- fs/adfs/inode.c | 2 +- fs/affs/file.c | 1 + fs/bfs/inode.c | 2 +- fs/exofs/inode.c | 2 +- fs/ext2/ext2.h | 2 ++ fs/ext2/file.c | 24 +++++++++++++++++++++--- fs/ext2/inode.c | 12 ++---------- fs/ext4/inode.c | 2 +- fs/fat/inode.c | 2 +- fs/jfs/inode.c | 2 +- fs/minix/inode.c | 2 +- fs/omfs/inode.c | 2 +- fs/reiserfs/inode.c | 2 ++ fs/sysv/inode.c | 2 +- fs/udf/inode.c | 2 +- fs/ufs/inode.c | 2 +- 16 files changed, 39 insertions(+), 24 deletions(-) diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c index 65794b8fe79eb..615c8d33bea81 100644 --- a/fs/adfs/inode.c +++ b/fs/adfs/inode.c @@ -383,7 +383,7 @@ int adfs_write_inode(struct inode *inode, struct writeback_control *wbc) obj.attr = ADFS_I(inode)->attr; obj.size = inode->i_size; - ret = adfs_dir_update(sb, &obj, wbc->sync_mode == WB_SYNC_ALL); + ret = adfs_dir_update(sb, &obj, 1 /* XXX: fix fsync and use 'wbc->sync_mode == WB_SYNC_ALL' */); unlock_kernel(); return ret; } diff --git a/fs/affs/file.c b/fs/affs/file.c index 0a90dcd46de28..465d7ae54c599 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -931,6 +931,7 @@ int affs_file_fsync(struct file *filp, int datasync) int ret, err; ret = write_inode_now(inode, 0); + /* XXX: could just sync the buffer been dirtied by write_inode */ err = sync_blockdev(inode->i_sb->s_bdev); if (!ret) ret = err; diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index a8e37f81d097b..692b2700b8b64 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -151,7 +151,7 @@ static int bfs_write_inode(struct inode *inode, struct writeback_control *wbc) di->i_eoffset = cpu_to_le32(i_sblock * BFS_BSIZE + inode->i_size - 1); mark_buffer_dirty(bh); - if (wbc->sync_mode == WB_SYNC_ALL) { + if (1 /* XXX: fix fsync and use wbc->sync_mode == WB_SYNC_ALL */ ) { sync_dirty_buffer(bh); if (buffer_req(bh) && !buffer_uptodate(bh)) err = -EIO; diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c index a7555238c41aa..007364605488c 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -1271,7 +1271,7 @@ static int exofs_update_inode(struct inode *inode, int do_sync) int exofs_write_inode(struct inode *inode, struct writeback_control *wbc) { - return exofs_update_inode(inode, wbc->sync_mode == WB_SYNC_ALL); + return exofs_update_inode(inode, 1 /* XXX: fix fsync and use wbc->sync_mode == WB_SYNC_ALL */ ); } /* diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 6346a2acf3260..8fdbe05061ae9 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -124,6 +124,8 @@ extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int); extern int ext2_setattr (struct dentry *, struct iattr *); extern void ext2_set_inode_flags(struct inode *inode); extern void ext2_get_inode_flags(struct ext2_inode_info *); +extern struct ext2_inode *ext2_get_inode(struct super_block *sb, ino_t ino, + struct buffer_head **p); extern int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len); diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 49eec9456c5b3..4ccb040f28f58 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "ext2.h" #include "xattr.h" #include "acl.h" @@ -43,16 +44,33 @@ static int ext2_release_file (struct inode * inode, struct file * filp) int ext2_fsync(struct file *file, int datasync) { int ret; - struct super_block *sb = file->f_mapping->host->i_sb; - struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping; + struct inode *inode = file->f_mapping->host; + ino_t ino = inode->i_ino; + struct super_block *sb = inode->i_sb; + struct address_space *sb_mapping = sb->s_bdev->bd_inode->i_mapping; + struct buffer_head *bh; + struct ext2_inode *raw_inode; ret = generic_file_fsync(file, datasync); - if (ret == -EIO || test_and_clear_bit(AS_EIO, &mapping->flags)) { + if (ret == -EIO || test_and_clear_bit(AS_EIO, &sb_mapping->flags)) { /* We don't really know where the IO error happened... */ ext2_error(sb, __func__, "detected IO error when writing metadata buffers"); + return -EIO; + } + + raw_inode = ext2_get_inode(sb, ino, &bh); + if (IS_ERR(raw_inode)) + return -EIO; + + sync_dirty_buffer(bh); + if (buffer_req(bh) && !buffer_uptodate(bh)) { + printk ("IO error syncing ext2 inode [%s:%08lx]\n", + sb->s_id, (unsigned long) ino); ret = -EIO; } + brelse (bh); + return ret; } diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 40ad210a5049a..ad0b45b683b37 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1211,7 +1211,7 @@ static int ext2_setsize(struct inode *inode, loff_t newsize) return 0; } -static struct ext2_inode *ext2_get_inode(struct super_block *sb, ino_t ino, +struct ext2_inode *ext2_get_inode(struct super_block *sb, ino_t ino, struct buffer_head **p) { struct buffer_head * bh; @@ -1505,16 +1505,8 @@ static int __ext2_write_inode(struct inode *inode, int do_sync) } else for (n = 0; n < EXT2_N_BLOCKS; n++) raw_inode->i_block[n] = ei->i_data[n]; mark_buffer_dirty(bh); - if (do_sync) { - sync_dirty_buffer(bh); - if (buffer_req(bh) && !buffer_uptodate(bh)) { - printk ("IO error syncing ext2 inode [%s:%08lx]\n", - sb->s_id, (unsigned long) ino); - err = -EIO; - } - } - ei->i_state &= ~EXT2_STATE_NEW; brelse (bh); + ei->i_state &= ~EXT2_STATE_NEW; return err; } diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index fee51dbf74dcd..5034aa1184f9b 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5274,7 +5274,7 @@ int ext4_write_inode(struct inode *inode, struct writeback_control *wbc) err = __ext4_get_inode_loc(inode, &iloc, 0); if (err) return err; - if (wbc->sync_mode == WB_SYNC_ALL) + if (1 /* XXX: fix fxync and use wbc->sync_mode == WB_SYNC_ALL */) sync_dirty_buffer(iloc.bh); if (buffer_req(iloc.bh) && !buffer_uptodate(iloc.bh)) { EXT4_ERROR_INODE_BLOCK(inode, iloc.bh->b_blocknr, diff --git a/fs/fat/inode.c b/fs/fat/inode.c index b147d434b3f2c..3a74cefca2732 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -652,7 +652,7 @@ static int __fat_write_inode(struct inode *inode, int wait) spin_unlock(&sbi->inode_hash_lock); mark_buffer_dirty(bh); err = 0; - if (wait) + if (1 /* XXX: fix fsync and use wait */) err = sync_dirty_buffer(bh); brelse(bh); return err; diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index 9978803ceedc5..a99e77d270783 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -123,7 +123,7 @@ int jfs_commit_inode(struct inode *inode, int wait) int jfs_write_inode(struct inode *inode, struct writeback_control *wbc) { - int wait = wbc->sync_mode == WB_SYNC_ALL; + int wait = 1; /* XXX fix fsync and use wbc->sync_mode == WB_SYNC_ALL; */ if (test_cflag(COMMIT_Nolink, inode)) return 0; diff --git a/fs/minix/inode.c b/fs/minix/inode.c index ae0b83f476a63..c47e397d0da59 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -583,7 +583,7 @@ static int minix_write_inode(struct inode *inode, struct writeback_control *wbc) bh = V2_minix_update_inode(inode); if (!bh) return -EIO; - if (wbc->sync_mode == WB_SYNC_ALL && buffer_dirty(bh)) { + if (1 /* XXX: fix fsync and use wbc->sync_mode == WB_SYNC_ALL */ && buffer_dirty(bh)) { sync_dirty_buffer(bh); if (buffer_req(bh) && !buffer_uptodate(bh)) { printk("IO error syncing minix inode [%s:%08lx]\n", diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c index e043c4cb9a972..2606b49e592a6 100644 --- a/fs/omfs/inode.c +++ b/fs/omfs/inode.c @@ -169,7 +169,7 @@ static int __omfs_write_inode(struct inode *inode, int wait) static int omfs_write_inode(struct inode *inode, struct writeback_control *wbc) { - return __omfs_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL); + return __omfs_write_inode(inode, 1 /* XXX: fix fsync and use wbc->sync_mode == WB_SYNC_ALL */); } int omfs_sync_inode(struct inode *inode) diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 0bae036831e2c..615a996ef77c9 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -1635,6 +1635,8 @@ int reiserfs_write_inode(struct inode *inode, struct writeback_control *wbc) ** these cases are just when the system needs ram, not when the ** inode needs to reach disk for safety, and they can safely be ** ignored because the altered inode has already been logged. + ** XXX: is this really OK? The caller clears the inode dirty bit, so + ** a subsequent sync for integrity might never reach here. */ if (wbc->sync_mode == WB_SYNC_ALL && !(current->flags & PF_MEMALLOC)) { reiserfs_write_lock(inode->i_sb); diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 0630eb969a280..9f7e214fff135 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -286,7 +286,7 @@ static int __sysv_write_inode(struct inode *inode, int wait) write3byte(sbi, (u8 *)&si->i_data[block], &raw_inode->i_data[3*block]); mark_buffer_dirty(bh); - if (wait) { + if (1 /* XXX: fix fsync and use wait */) { sync_dirty_buffer(bh); if (buffer_req(bh) && !buffer_uptodate(bh)) { printk ("IO error syncing sysv inode [%s:%08x]\n", diff --git a/fs/udf/inode.c b/fs/udf/inode.c index c6a2e782b97b1..ed51a4c347432 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1591,7 +1591,7 @@ static int udf_update_inode(struct inode *inode, int do_sync) /* write the data blocks */ mark_buffer_dirty(bh); - if (do_sync) { + if (1 /* XXX fix fsync and use do_sync */) { sync_dirty_buffer(bh); if (buffer_write_io_error(bh)) { printk(KERN_WARNING "IO error syncing udf inode " diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 2b251f2093afc..b2119db47cb20 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -889,7 +889,7 @@ static int ufs_update_inode(struct inode * inode, int do_sync) } mark_buffer_dirty(bh); - if (do_sync) + if (1 /* XXX: fix fsync and use do_sync */) sync_dirty_buffer(bh); brelse (bh); From e422ffb446f5308b187191dd3fe1dfdacc738536 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Sat, 18 Dec 2010 12:46:40 +1100 Subject: [PATCH 2113/2556] fs: fsync optimisations Optimise fsync by adding a datasync parameter to sync_inode_metadata to DTRT with writing back the inode (->write_inode in theory should have a datasync parameter too perhaps, but that's for another time). Also, implement the metadata sync optimally rather than reusing the normal data writeback path. This means less useless moving the inode around the writeback lists, and less dropping and retaking of inode_lock, and avoiding the data writeback call with nr_pages == 0. Signed-off-by: Nick Piggin --- drivers/staging/pohmelfs/inode.c | 2 +- fs/exofs/file.c | 2 +- fs/ext2/dir.c | 2 +- fs/ext2/inode.c | 2 +- fs/ext2/xattr.c | 2 +- fs/fs-writeback.c | 41 ++++++++++++++++++++++++++++++-- fs/libfs.c | 2 +- fs/nfsd/vfs.c | 2 +- include/linux/fs.h | 2 +- 9 files changed, 47 insertions(+), 10 deletions(-) diff --git a/drivers/staging/pohmelfs/inode.c b/drivers/staging/pohmelfs/inode.c index 56d3a4e5622f2..1edacd0cf4ebd 100644 --- a/drivers/staging/pohmelfs/inode.c +++ b/drivers/staging/pohmelfs/inode.c @@ -890,7 +890,7 @@ static int pohmelfs_fsync(struct file *file, int datasync) { struct inode *inode = file->f_mapping->host; - return sync_inode_metadata(inode, 1); + return sync_inode_metadata(inode, datasync, 1); } ssize_t pohmelfs_write(struct file *file, const char __user *buf, diff --git a/fs/exofs/file.c b/fs/exofs/file.c index 4c0d6bac91433..1f089e0411bf7 100644 --- a/fs/exofs/file.c +++ b/fs/exofs/file.c @@ -48,7 +48,7 @@ static int exofs_file_fsync(struct file *filp, int datasync) struct inode *inode = filp->f_mapping->host; struct super_block *sb; - ret = sync_inode_metadata(inode, 1); + ret = sync_inode_metadata(inode, datasync, 1); /* This is a good place to write the sb */ /* TODO: Sechedule an sb-sync on create */ diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index 47cda410b548a..60c67072c7651 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -107,7 +107,7 @@ static int ext2_commit_chunk(struct page *page, loff_t pos, unsigned len) if (IS_DIRSYNC(dir)) { err = write_one_page(page, 1); if (!err) - err = sync_inode_metadata(dir, 1); + err = sync_inode_metadata(dir, 0, 1); } else { unlock_page(page); } diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index ad0b45b683b37..be07143cd045f 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1203,7 +1203,7 @@ static int ext2_setsize(struct inode *inode, loff_t newsize) inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; if (inode_needs_sync(inode)) { sync_mapping_buffers(inode->i_mapping); - sync_inode_metadata(inode, 1); + sync_inode_metadata(inode, 0, 1); } else { mark_inode_dirty(inode); } diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index c2e4dce984d2a..02ef139bc85fc 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -691,7 +691,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, EXT2_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0; inode->i_ctime = CURRENT_TIME_SEC; if (IS_SYNC(inode)) { - error = sync_inode_metadata(inode, 1); + error = sync_inode_metadata(inode, 0, 1); /* In case sync failed due to ENOSPC the inode was actually * written (only some dirty data were not) so we just proceed * as if nothing happened and cleanup the unused block */ diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 62bdd807ca6d2..96fde5ef1b971 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -1376,13 +1376,50 @@ EXPORT_SYMBOL(sync_inode); * * Note: only writes the actual inode, no associated data or other metadata. */ -int sync_inode_metadata(struct inode *inode, int wait) +int sync_inode_metadata(struct inode *inode, int datasync, int wait) { + struct address_space *mapping = inode->i_mapping; struct writeback_control wbc = { .sync_mode = wait ? WB_SYNC_ALL : WB_SYNC_NONE, .nr_to_write = 0, /* metadata-only */ }; + unsigned dirty, mask; + int ret = 0; + + /* + * This is a similar implementation to writeback_single_inode. + * Keep them in sync. + */ + spin_lock(&inode_lock); + if (!inode_writeback_begin(inode, wait)) + goto out; + + if (datasync) + mask = I_DIRTY_DATASYNC; + else + mask = I_DIRTY_SYNC | I_DIRTY_DATASYNC; + dirty = inode->i_state & mask; + if (!dirty) + goto out_wb_end; + /* + * Generic write_inode doesn't distinguish between sync and datasync, + * so even a datasync can clear the sync state. Filesystems which + * distiguish these cases must only clear 'mask' in their metadata + * sync code. + */ + inode->i_state &= ~(I_DIRTY_SYNC | I_DIRTY_DATASYNC); + + spin_unlock(&inode_lock); + ret = write_inode(inode, &wbc); + spin_lock(&inode_lock); + if (ret) + inode->i_state |= dirty; /* couldn't write out inode */ - return sync_inode(inode, &wbc); +out_wb_end: + inode_writeback_end(inode); + +out: + spin_unlock(&inode_lock); + return ret; } EXPORT_SYMBOL(sync_inode_metadata); diff --git a/fs/libfs.c b/fs/libfs.c index 2645d06e7e92c..dfea85e9139b5 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -912,7 +912,7 @@ int generic_file_fsync(struct file *file, int datasync) int ret; ret = sync_mapping_buffers(inode->i_mapping); - err = sync_inode_metadata(inode, 1); + err = sync_inode_metadata(inode, datasync, 1); if (ret == 0) ret = err; return ret; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 435f407377bbe..afd5895ccf05d 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -294,7 +294,7 @@ commit_metadata(struct svc_fh *fhp) if (export_ops->commit_metadata) return export_ops->commit_metadata(inode); - return sync_inode_metadata(inode, 1); + return sync_inode_metadata(inode, 0, 1); } /* diff --git a/include/linux/fs.h b/include/linux/fs.h index 52079af48ecc8..a42fb95e04c99 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1779,7 +1779,7 @@ static inline void file_accessed(struct file *file) } int sync_inode(struct inode *inode, struct writeback_control *wbc); -int sync_inode_metadata(struct inode *inode, int wait); +int sync_inode_metadata(struct inode *inode, int datasync, int wait); int inode_writeback_begin(struct inode *inode, int wait); int inode_writeback_end(struct inode *inode); From 342f98317731368fae5db49edc2552e648f0df6b Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 25 Aug 2011 19:29:45 -0700 Subject: [PATCH 2114/2556] PM: Back off suspend if repeated attempts fail If 10 attempts to suspend either fail or immediately resume within 1 second, back off suspending for 5 seconds multiplied by the number of consecutive backoffs. That is, pause suspends for 5 seconds after the first 10 failures, 10 seconds after the next 10 failures, and so on. Avoid continually trying to suspend in situations in which a driver is repeatedly rejecting suspend or a pending wakeup interrupt is not handled, burning CPU in the continuous suspend attempts. Change-Id: I32289676d95a307ea3aa5e78f6c126ca979c0fec Signed-off-by: Todd Poynor Conflicts: kernel/power/wakelock.c --- kernel/power/wakelock.c | 42 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c index 01bfbf395f8b3..b054e9cee3463 100644 --- a/kernel/power/wakelock.c +++ b/kernel/power/wakelock.c @@ -48,6 +48,13 @@ struct workqueue_struct *suspend_work_queue; struct wake_lock main_wake_lock; suspend_state_t requested_suspend_state = PM_SUSPEND_MEM; static struct wake_lock unknown_wakeup; +static struct wake_lock suspend_backoff_lock; + +#define SUSPEND_BACKOFF_FAILURES 10 +#define SUSPEND_BACKOFF_INTERVAL 5000 + +static unsigned suspend_backoff_count; +static unsigned suspend_fail_count; #ifdef CONFIG_WAKELOCK_STAT static bool resuming_devices; @@ -257,10 +264,20 @@ long has_wake_lock(int type) return ret; } +static void suspend_backoff(void) +{ + pr_info("suspend: too many immediate wakeups, back off\n"); + ++suspend_backoff_count; + wake_lock_timeout(&suspend_backoff_lock, + msecs_to_jiffies(suspend_backoff_count * + SUSPEND_BACKOFF_INTERVAL)); +} + static void suspend(struct work_struct *work) { int ret; int entry_event_num; + struct timespec ts_entry, ts_exit; if (has_wake_lock(WAKE_LOCK_SUSPEND)) { if (debug_mask & DEBUG_SUSPEND) @@ -277,7 +294,9 @@ static void suspend(struct work_struct *work) sys_sync(); if (debug_mask & DEBUG_SUSPEND) pr_info("suspend: enter suspend\n"); + getnstimeofday(&ts_entry); ret = pm_suspend(requested_suspend_state); + getnstimeofday(&ts_exit); #ifdef CONFIG_WAKELOCK_STAT if (debug_mask & DEBUG_WAKEUP) { @@ -301,15 +320,26 @@ static void suspend(struct work_struct *work) #endif if (debug_mask & DEBUG_EXIT_SUSPEND) { - struct timespec ts; struct rtc_time tm; - getnstimeofday(&ts); - rtc_time_to_tm(ts.tv_sec, &tm); + rtc_time_to_tm(ts_exit.tv_sec, &tm); pr_info("suspend: exit suspend, ret = %d " "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec); + tm.tm_hour, tm.tm_min, tm.tm_sec, ts_exit.tv_nsec); } + + if (ts_exit.tv_sec - ts_entry.tv_sec <= 1) { + ++suspend_fail_count; + + if (suspend_fail_count == SUSPEND_BACKOFF_FAILURES) { + suspend_backoff(); + suspend_fail_count = 0; + } + } else { + suspend_fail_count = 0; + suspend_backoff_count = 0; + } + if (current_event_num == entry_event_num) { if (debug_mask & DEBUG_WAKEUP) pr_info("suspend: no wakelock for wakeup source\n"); @@ -584,6 +614,8 @@ static int __init wakelocks_init(void) wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main"); wake_lock(&main_wake_lock); wake_lock_init(&unknown_wakeup, WAKE_LOCK_SUSPEND, "unknown_wakeups"); + wake_lock_init(&suspend_backoff_lock, WAKE_LOCK_SUSPEND, + "suspend_backoff"); ret = platform_device_register(&power_device); if (ret) { @@ -613,6 +645,7 @@ static int __init wakelocks_init(void) err_platform_driver_register: platform_device_unregister(&power_device); err_platform_device_register: + wake_lock_destroy(&suspend_backoff_lock); wake_lock_destroy(&unknown_wakeup); wake_lock_destroy(&main_wake_lock); #ifdef CONFIG_WAKELOCK_STAT @@ -629,6 +662,7 @@ static void __exit wakelocks_exit(void) destroy_workqueue(suspend_work_queue); platform_driver_unregister(&power_driver); platform_device_unregister(&power_device); + wake_lock_destroy(&suspend_backoff_lock); wake_lock_destroy(&unknown_wakeup); wake_lock_destroy(&main_wake_lock); #ifdef CONFIG_WAKELOCK_STAT From d33476dfe1dee3bae49c127f219759d9f2bcbee0 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Wed, 22 Feb 2012 00:51:07 -0500 Subject: [PATCH 2115/2556] configs: cayniarb: update configs --- arch/arm/configs/cayniarb_bravo_defconfig | 1 + arch/arm/configs/cayniarb_bravoc_defconfig | 1 + .../configs/cayniarb_incrediblec_defconfig | 1 + arch/arm/configs/cayniarb_mahimahi_defconfig | 1 + .../arm/configs/cayniarb_supersonic_defconfig | 61 ++++++++----------- 5 files changed, 28 insertions(+), 37 deletions(-) diff --git a/arch/arm/configs/cayniarb_bravo_defconfig b/arch/arm/configs/cayniarb_bravo_defconfig index 2973140f94f64..00c313bf987b3 100644 --- a/arch/arm/configs/cayniarb_bravo_defconfig +++ b/arch/arm/configs/cayniarb_bravo_defconfig @@ -377,6 +377,7 @@ CONFIG_ARM_THUMBEE=y # CONFIG_SWP_EMULATE is not set # CONFIG_CPU_ICACHE_DISABLE is not set # CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_CACHE_ERR_REPORT is not set # CONFIG_CPU_BPREDICT_DISABLE is not set CONFIG_ARM_L1_CACHE_SHIFT=5 CONFIG_ARM_DMA_MEM_BUFFERABLE=y diff --git a/arch/arm/configs/cayniarb_bravoc_defconfig b/arch/arm/configs/cayniarb_bravoc_defconfig index c1b66de79daee..6691a42587af7 100644 --- a/arch/arm/configs/cayniarb_bravoc_defconfig +++ b/arch/arm/configs/cayniarb_bravoc_defconfig @@ -377,6 +377,7 @@ CONFIG_ARM_THUMBEE=y # CONFIG_SWP_EMULATE is not set # CONFIG_CPU_ICACHE_DISABLE is not set # CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_CACHE_ERR_REPORT is not set # CONFIG_CPU_BPREDICT_DISABLE is not set CONFIG_ARM_L1_CACHE_SHIFT=5 CONFIG_ARM_DMA_MEM_BUFFERABLE=y diff --git a/arch/arm/configs/cayniarb_incrediblec_defconfig b/arch/arm/configs/cayniarb_incrediblec_defconfig index 8b334d6b334be..2e66c5ba3eabb 100644 --- a/arch/arm/configs/cayniarb_incrediblec_defconfig +++ b/arch/arm/configs/cayniarb_incrediblec_defconfig @@ -380,6 +380,7 @@ CONFIG_ARM_THUMBEE=y # CONFIG_SWP_EMULATE is not set # CONFIG_CPU_ICACHE_DISABLE is not set # CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_CACHE_ERR_REPORT is not set # CONFIG_CPU_BPREDICT_DISABLE is not set CONFIG_ARM_L1_CACHE_SHIFT=5 CONFIG_ARM_DMA_MEM_BUFFERABLE=y diff --git a/arch/arm/configs/cayniarb_mahimahi_defconfig b/arch/arm/configs/cayniarb_mahimahi_defconfig index a627b6b67de73..9ba6a30876163 100644 --- a/arch/arm/configs/cayniarb_mahimahi_defconfig +++ b/arch/arm/configs/cayniarb_mahimahi_defconfig @@ -377,6 +377,7 @@ CONFIG_ARM_THUMBEE=y # CONFIG_SWP_EMULATE is not set # CONFIG_CPU_ICACHE_DISABLE is not set # CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_CACHE_ERR_REPORT is not set # CONFIG_CPU_BPREDICT_DISABLE is not set CONFIG_ARM_L1_CACHE_SHIFT=5 CONFIG_ARM_DMA_MEM_BUFFERABLE=y diff --git a/arch/arm/configs/cayniarb_supersonic_defconfig b/arch/arm/configs/cayniarb_supersonic_defconfig index fb3998bdd4b8a..93c9d59c4bb5d 100644 --- a/arch/arm/configs/cayniarb_supersonic_defconfig +++ b/arch/arm/configs/cayniarb_supersonic_defconfig @@ -77,10 +77,10 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=17 CONFIG_CGROUPS=y -CONFIG_CGROUP_DEBUG=y -CONFIG_CGROUP_NS=y -CONFIG_CGROUP_FREEZER=y -CONFIG_CGROUP_DEVICE=y +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CGROUP_NS is not set +# CONFIG_CGROUP_FREEZER is not set +# CONFIG_CGROUP_DEVICE is not set # CONFIG_CPUSETS is not set CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y @@ -384,6 +384,7 @@ CONFIG_ARM_THUMBEE=y # CONFIG_SWP_EMULATE is not set # CONFIG_CPU_ICACHE_DISABLE is not set # CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_CACHE_ERR_REPORT is not set # CONFIG_CPU_BPREDICT_DISABLE is not set CONFIG_ARM_L1_CACHE_SHIFT=5 CONFIG_ARM_DMA_MEM_BUFFERABLE=y @@ -536,20 +537,22 @@ CONFIG_NET_KEY=y # CONFIG_NET_KEY_MIGRATE is not set CONFIG_INET=y # CONFIG_IP_MULTICAST is not set -# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_ASK_IP_FIB_HASH=y +# CONFIG_IP_FIB_TRIE is not set CONFIG_IP_FIB_HASH=y -CONFIG_IP_PNP=y -CONFIG_IP_PNP_DHCP=y -# CONFIG_IP_PNP_BOOTP is not set -# CONFIG_IP_PNP_RARP is not set +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_IP_ROUTE_MULTIPATH is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +# CONFIG_IP_PNP is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE_DEMUX is not set # CONFIG_ARPD is not set # CONFIG_SYN_COOKIES is not set # CONFIG_INET_AH is not set CONFIG_INET_ESP=y -# CONFIG_INET_IPCOMP is not set -# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_IPCOMP=y +CONFIG_INET_XFRM_TUNNEL=y CONFIG_INET_TUNNEL=y CONFIG_INET_XFRM_MODE_TRANSPORT=y # CONFIG_INET_XFRM_MODE_TUNNEL is not set @@ -607,7 +610,7 @@ CONFIG_NF_CT_PROTO_SCTP=y CONFIG_NF_CT_PROTO_UDPLITE=y CONFIG_NF_CONNTRACK_AMANDA=y CONFIG_NF_CONNTRACK_FTP=y -CONFIG_NF_CONNTRACK_H323=y +# CONFIG_NF_CONNTRACK_H323 is not set CONFIG_NF_CONNTRACK_IRC=y CONFIG_NF_CONNTRACK_NETBIOS_NS=y CONFIG_NF_CONNTRACK_PPTP=y @@ -638,11 +641,11 @@ CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y -CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set # CONFIG_NETFILTER_XT_TARGET_RATEEST is not set # CONFIG_NETFILTER_XT_TARGET_TEE is not set # CONFIG_NETFILTER_XT_TARGET_TPROXY is not set -CONFIG_NETFILTER_XT_TARGET_TRACE=y +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set # CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set # CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set @@ -667,7 +670,7 @@ CONFIG_NETFILTER_XT_MATCH_LENGTH=y CONFIG_NETFILTER_XT_MATCH_LIMIT=y CONFIG_NETFILTER_XT_MATCH_MAC=y CONFIG_NETFILTER_XT_MATCH_MARK=y -# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y # CONFIG_NETFILTER_XT_MATCH_OSF is not set # CONFIG_NETFILTER_XT_MATCH_OWNER is not set CONFIG_NETFILTER_XT_MATCH_POLICY=y @@ -679,7 +682,7 @@ CONFIG_NETFILTER_XT_MATCH_QUOTA2=y CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y # CONFIG_NETFILTER_XT_MATCH_RATEEST is not set # CONFIG_NETFILTER_XT_MATCH_REALM is not set -# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +CONFIG_NETFILTER_XT_MATCH_RECENT=y # CONFIG_NETFILTER_XT_MATCH_SCTP is not set CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y @@ -721,7 +724,7 @@ CONFIG_NF_NAT_IRC=y CONFIG_NF_NAT_TFTP=y CONFIG_NF_NAT_AMANDA=y CONFIG_NF_NAT_PPTP=y -CONFIG_NF_NAT_H323=y +# CONFIG_NF_NAT_H323 is not set CONFIG_NF_NAT_SIP=y CONFIG_IP_NF_MANGLE=y # CONFIG_IP_NF_TARGET_CLUSTERIP is not set @@ -832,6 +835,7 @@ CONFIG_NET_ACT_MIRRED=y # CONFIG_NET_CLS_IND is not set CONFIG_NET_SCH_FIFO=y # CONFIG_DCB is not set +CONFIG_DNS_RESOLVER=y # CONFIG_BATMAN_ADV is not set # @@ -1076,30 +1080,12 @@ CONFIG_TUN=y # CONFIG_VETH is not set CONFIG_MII=y # CONFIG_PHYLIB is not set -CONFIG_NET_ETHERNET=y -# CONFIG_AX88796 is not set -CONFIG_SMC91X=y -# CONFIG_DM9000 is not set -# CONFIG_ENC28J60 is not set -# CONFIG_ETHOC is not set -# CONFIG_SMC911X is not set -# CONFIG_SMSC911X is not set -# CONFIG_DNET is not set -# CONFIG_IBM_NEW_EMAC_ZMII is not set -# CONFIG_IBM_NEW_EMAC_RGMII is not set -# CONFIG_IBM_NEW_EMAC_TAH is not set -# CONFIG_IBM_NEW_EMAC_EMAC4 is not set -# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set -# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set -# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set -# CONFIG_B44 is not set -# CONFIG_KS8851 is not set -# CONFIG_KS8851_MLL is not set +# CONFIG_NET_ETHERNET is not set # CONFIG_NETDEV_1000 is not set # CONFIG_NETDEV_10000 is not set CONFIG_WLAN=y CONFIG_BCM4329=m -CONFIG_BCM4329_FW_PATH="/vendor/firmware/fw_bcm4329.bin" +CONFIG_BCM4329_FW_PATH="/system/etc/firmware/fw_bcm4329.bin" CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" # CONFIG_HOSTAP is not set @@ -1850,6 +1836,7 @@ CONFIG_TMPFS=y CONFIG_MISC_FILESYSTEMS=y # CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set +# CONFIG_ECRYPT_FS is not set # CONFIG_HFS_FS is not set # CONFIG_HFSPLUS_FS is not set # CONFIG_BEFS_FS is not set From 581d46830bafb0297c1b0c12e215723f7674833e Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sat, 25 Feb 2012 01:11:17 -0500 Subject: [PATCH 2116/2556] configs: cayniarb: enable zram and ksm --- arch/arm/configs/cayniarb_bravo_defconfig | 4 ++-- arch/arm/configs/cayniarb_bravoc_defconfig | 4 ++-- arch/arm/configs/cayniarb_incrediblec_defconfig | 4 ++-- arch/arm/configs/cayniarb_mahimahi_defconfig | 4 ++-- arch/arm/configs/cayniarb_supersonic_defconfig | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/arm/configs/cayniarb_bravo_defconfig b/arch/arm/configs/cayniarb_bravo_defconfig index 00c313bf987b3..7eb625a7f0394 100644 --- a/arch/arm/configs/cayniarb_bravo_defconfig +++ b/arch/arm/configs/cayniarb_bravo_defconfig @@ -428,7 +428,7 @@ CONFIG_SPLIT_PTLOCK_CPUS=4 # CONFIG_PHYS_ADDR_T_64BIT is not set CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y -# CONFIG_KSM is not set +CONFIG_KSM=y CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 CONFIG_NEED_PER_CPU_KM=y CONFIG_FORCE_MAX_ZONEORDER=11 @@ -1729,7 +1729,7 @@ CONFIG_ANDROID_TIMED_GPIO=y CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_POHMELFS is not set # CONFIG_IIO is not set -# CONFIG_ZRAM is not set +CONFIG_ZRAM=y # CONFIG_FB_SM7XX is not set # diff --git a/arch/arm/configs/cayniarb_bravoc_defconfig b/arch/arm/configs/cayniarb_bravoc_defconfig index 6691a42587af7..1aeb85d443c67 100644 --- a/arch/arm/configs/cayniarb_bravoc_defconfig +++ b/arch/arm/configs/cayniarb_bravoc_defconfig @@ -428,7 +428,7 @@ CONFIG_SPLIT_PTLOCK_CPUS=4 # CONFIG_PHYS_ADDR_T_64BIT is not set CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y -# CONFIG_KSM is not set +CONFIG_KSM=y CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 CONFIG_NEED_PER_CPU_KM=y CONFIG_FORCE_MAX_ZONEORDER=11 @@ -1729,7 +1729,7 @@ CONFIG_ANDROID_TIMED_GPIO=y CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_POHMELFS is not set # CONFIG_IIO is not set -# CONFIG_ZRAM is not set +CONFIG_ZRAM=y # CONFIG_FB_SM7XX is not set # diff --git a/arch/arm/configs/cayniarb_incrediblec_defconfig b/arch/arm/configs/cayniarb_incrediblec_defconfig index 2e66c5ba3eabb..c0b5f6e921827 100644 --- a/arch/arm/configs/cayniarb_incrediblec_defconfig +++ b/arch/arm/configs/cayniarb_incrediblec_defconfig @@ -431,7 +431,7 @@ CONFIG_SPLIT_PTLOCK_CPUS=4 # CONFIG_PHYS_ADDR_T_64BIT is not set CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y -# CONFIG_KSM is not set +CONFIG_KSM=y CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 CONFIG_NEED_PER_CPU_KM=y CONFIG_FORCE_MAX_ZONEORDER=11 @@ -1765,7 +1765,7 @@ CONFIG_ANDROID_TIMED_GPIO=y CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_POHMELFS is not set # CONFIG_IIO is not set -# CONFIG_ZRAM is not set +CONFIG_ZRAM=y # CONFIG_FB_SM7XX is not set # diff --git a/arch/arm/configs/cayniarb_mahimahi_defconfig b/arch/arm/configs/cayniarb_mahimahi_defconfig index 9ba6a30876163..095008d0cf84b 100644 --- a/arch/arm/configs/cayniarb_mahimahi_defconfig +++ b/arch/arm/configs/cayniarb_mahimahi_defconfig @@ -428,7 +428,7 @@ CONFIG_SPLIT_PTLOCK_CPUS=4 # CONFIG_PHYS_ADDR_T_64BIT is not set CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y -# CONFIG_KSM is not set +CONFIG_KSM=y CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 CONFIG_NEED_PER_CPU_KM=y CONFIG_FORCE_MAX_ZONEORDER=11 @@ -1726,7 +1726,7 @@ CONFIG_ANDROID_TIMED_GPIO=y CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_POHMELFS is not set # CONFIG_IIO is not set -# CONFIG_ZRAM is not set +CONFIG_ZRAM=y # CONFIG_FB_SM7XX is not set # diff --git a/arch/arm/configs/cayniarb_supersonic_defconfig b/arch/arm/configs/cayniarb_supersonic_defconfig index 93c9d59c4bb5d..e9ebb0616de72 100644 --- a/arch/arm/configs/cayniarb_supersonic_defconfig +++ b/arch/arm/configs/cayniarb_supersonic_defconfig @@ -435,7 +435,7 @@ CONFIG_SPLIT_PTLOCK_CPUS=4 # CONFIG_PHYS_ADDR_T_64BIT is not set CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y -# CONFIG_KSM is not set +CONFIG_KSM=y CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 CONFIG_NEED_PER_CPU_KM=y CONFIG_FORCE_MAX_ZONEORDER=11 @@ -1751,7 +1751,7 @@ CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_POHMELFS is not set # CONFIG_AUTOFS_FS is not set # CONFIG_IIO is not set -# CONFIG_ZRAM is not set +CONFIG_ZRAM=y # CONFIG_FB_SM7XX is not set # From 30c9752fd11b9aa6d9bb30da0547cfcee1b7e4c6 Mon Sep 17 00:00:00 2001 From: cayniarb Date: Sat, 25 Feb 2012 18:01:42 -0500 Subject: [PATCH 2117/2556] msm: board: supersonic: fix typo this should not be here twice --- arch/arm/mach-msm/board-supersonic.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index f6ffde031c7ed..25b3e87a4d3af 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -1631,9 +1631,6 @@ static void __init supersonic_init(void) } i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); - platform_add_devices(msm_footswitch_devices, - msm_num_footswitch_devices); - ret = supersonic_init_mmc(system_rev); if (ret != 0) pr_crit("%s: Unable to initialize MMC\n", __func__); From de0e8ca8a3b9f2f6a1305e1ead8d0e4ae00f7fb3 Mon Sep 17 00:00:00 2001 From: Jerome Marchand Date: Fri, 17 Dec 2010 16:59:33 +0100 Subject: [PATCH 2118/2556] Staging: zram: make ZRAM depends on SYSFS We can not configure zram device without sysfs anyway, so make zram depends on it. Signed-off-by: Jerome Marchand Acked-by: Jeff Moyer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/Kconfig | 2 +- drivers/staging/zram/zram_drv.c | 4 ---- drivers/staging/zram/zram_sysfs.c | 4 ---- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/staging/zram/Kconfig b/drivers/staging/zram/Kconfig index da079f8d6e3dd..d3982e6fdb40f 100644 --- a/drivers/staging/zram/Kconfig +++ b/drivers/staging/zram/Kconfig @@ -1,6 +1,6 @@ config ZRAM tristate "Compressed RAM block device support" - depends on BLOCK + depends on BLOCK && SYSFS select LZO_COMPRESS select LZO_DECOMPRESS default n diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 4bd8cbdaee76f..0d605be501780 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -630,14 +630,12 @@ static int create_device(struct zram *zram, int device_id) add_disk(zram->disk); -#ifdef CONFIG_SYSFS ret = sysfs_create_group(&disk_to_dev(zram->disk)->kobj, &zram_disk_attr_group); if (ret < 0) { pr_warning("Error creating sysfs group"); goto out; } -#endif zram->init_done = 0; @@ -647,10 +645,8 @@ static int create_device(struct zram *zram, int device_id) static void destroy_device(struct zram *zram) { -#ifdef CONFIG_SYSFS sysfs_remove_group(&disk_to_dev(zram->disk)->kobj, &zram_disk_attr_group); -#endif if (zram->disk) { del_gendisk(zram->disk); diff --git a/drivers/staging/zram/zram_sysfs.c b/drivers/staging/zram/zram_sysfs.c index 6b3cf00b0ff42..ad62db221b6fd 100644 --- a/drivers/staging/zram/zram_sysfs.c +++ b/drivers/staging/zram/zram_sysfs.c @@ -17,8 +17,6 @@ #include "zram_drv.h" -#ifdef CONFIG_SYSFS - static u64 zram_stat64_read(struct zram *zram, u64 *v) { u64 val; @@ -220,5 +218,3 @@ static struct attribute *zram_disk_attrs[] = { struct attribute_group zram_disk_attr_group = { .attrs = zram_disk_attrs, }; - -#endif /* CONFIG_SYSFS */ From cef60c2dec7d7271c7a23d670f108337eacb5726 Mon Sep 17 00:00:00 2001 From: Jerome Marchand Date: Fri, 17 Dec 2010 17:02:28 +0100 Subject: [PATCH 2119/2556] Staging: zram: round up the disk size provided by user Currently disksize_store() round down the disk size provided by user. This is probably not what one would expect, so round up instead. Signed-off-by: Jerome Marchand Acked-by: Jeff Moyer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/zram_sysfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/zram/zram_sysfs.c b/drivers/staging/zram/zram_sysfs.c index ad62db221b6fd..a70cc010d18da 100644 --- a/drivers/staging/zram/zram_sysfs.c +++ b/drivers/staging/zram/zram_sysfs.c @@ -14,6 +14,7 @@ #include #include +#include #include "zram_drv.h" @@ -65,7 +66,7 @@ static ssize_t disksize_store(struct device *dev, if (ret) return ret; - zram->disksize &= PAGE_MASK; + zram->disksize = PAGE_ALIGN(zram->disksize); set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); return len; From 6c86250d49b99df14a911feef788dd7187ace2e3 Mon Sep 17 00:00:00 2001 From: Jerome Marchand Date: Fri, 17 Dec 2010 17:03:15 +0100 Subject: [PATCH 2120/2556] Staging: zram: make zram_read return a bio error if the device is not initialized Make zram_read() return a bio error if the device is not initialized instead of pretending nothing happened. Signed-off-by: Jerome Marchand Acked-by: Jeff Moyer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/zram_drv.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 0d605be501780..6c92cd576426f 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -208,8 +208,7 @@ static int zram_read(struct zram *zram, struct bio *bio) struct bio_vec *bvec; if (unlikely(!zram->init_done)) { - set_bit(BIO_UPTODATE, &bio->bi_flags); - bio_endio(bio, 0); + bio_endio(bio, -ENXIO); return 0; } From b3b6e1f58f868b54430028139200cb65114bb7a8 Mon Sep 17 00:00:00 2001 From: "Justin P. Mattock" Date: Mon, 27 Dec 2010 22:22:31 -0800 Subject: [PATCH 2121/2556] staging: zram: xvmalloc.c: Fix a typo. Not exactly sure if this is a typo or not, due to my search results comming up with not that many hits. Either its dereferenceable or dereferencable from the two I choose the later. if it's wrong let me know and I'll resend. Signed-off-by: Justin P. Mattock Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/xvmalloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/zram/xvmalloc.c b/drivers/staging/zram/xvmalloc.c index b64406739d05e..3ed744ba7ba01 100644 --- a/drivers/staging/zram/xvmalloc.c +++ b/drivers/staging/zram/xvmalloc.c @@ -46,7 +46,7 @@ static void clear_flag(struct block_header *block, enum blockflags flag) } /* - * Given pair, provide a derefrencable pointer. + * Given pair, provide a dereferencable pointer. * This is called from xv_malloc/xv_free path, so it * needs to be fast. */ From 625596b0ef6c254e943879545e43ddac37baf4f9 Mon Sep 17 00:00:00 2001 From: Nitin Gupta Date: Sat, 22 Jan 2011 07:36:15 -0500 Subject: [PATCH 2122/2556] Staging: zram: simplify zram_make_request zram_read() and zram_write() always return zero, so make them return void to simplify the code. Signed-off-by: Nitin Gupta Signed-off-by: Jerome Marchand Acked-by: Jeff Moyer Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/zram_drv.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 6c92cd576426f..f1f3307c9db5e 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -200,7 +200,7 @@ static void handle_uncompressed_page(struct zram *zram, flush_dcache_page(page); } -static int zram_read(struct zram *zram, struct bio *bio) +static void zram_read(struct zram *zram, struct bio *bio) { int i; @@ -209,7 +209,7 @@ static int zram_read(struct zram *zram, struct bio *bio) if (unlikely(!zram->init_done)) { bio_endio(bio, -ENXIO); - return 0; + return; } zram_stat64_inc(zram, &zram->stats.num_reads); @@ -274,14 +274,13 @@ static int zram_read(struct zram *zram, struct bio *bio) set_bit(BIO_UPTODATE, &bio->bi_flags); bio_endio(bio, 0); - return 0; + return; out: bio_io_error(bio); - return 0; } -static int zram_write(struct zram *zram, struct bio *bio) +static void zram_write(struct zram *zram, struct bio *bio) { int i, ret; u32 index; @@ -406,11 +405,10 @@ static int zram_write(struct zram *zram, struct bio *bio) set_bit(BIO_UPTODATE, &bio->bi_flags); bio_endio(bio, 0); - return 0; + return; out: bio_io_error(bio); - return 0; } /* @@ -435,7 +433,6 @@ static inline int valid_io_request(struct zram *zram, struct bio *bio) */ static int zram_make_request(struct request_queue *queue, struct bio *bio) { - int ret = 0; struct zram *zram = queue->queuedata; if (!valid_io_request(zram, bio)) { @@ -446,15 +443,15 @@ static int zram_make_request(struct request_queue *queue, struct bio *bio) switch (bio_data_dir(bio)) { case READ: - ret = zram_read(zram, bio); + zram_read(zram, bio); break; case WRITE: - ret = zram_write(zram, bio); + zram_write(zram, bio); break; } - return ret; + return 0; } void zram_reset_device(struct zram *zram) From 919b1c60c3a470b40ce4b446581d04e0360746c5 Mon Sep 17 00:00:00 2001 From: Robert Jennings Date: Fri, 28 Jan 2011 08:57:27 -0600 Subject: [PATCH 2123/2556] zram/vmalloc: Correct tunings to enable use with 64K pages xvmalloc will not currently function with 64K pages. Newly allocated pages will be inserted at an offset beyond the end of the first-level index. This tuning is needed to properly size the allocator for 64K pages. The default 3 byte shift results in a second level list size which can not be indexed using the 64 bits of the flbitmap in the xv_pool structure. The shift must increase to 4 bytes between second level list entries to fit the size of the first level bitmap. Here are a few statistics for structure sizes on 32- and 64-bit CPUs with 4KB and 64KB page sizes. bits_per_long 32 64 64 page_size 4,096 4,096 65,535 xv_align 4 8 8 fl_delta 3 3 4 num_free_lists 508 508 4,094 xv_pool size 4,144b 8,216b 66,040b per object overhead 32 64 64 zram struct 0.5GB disk 512KB 1024KB 64KB This patch maintains the current tunings for 4K pages, adds an optimal sizing for 64K pages and adds a safe tuning for any other page sizes. Signed-off-by: Robert Jennings Reviewed-by: Pekka Enberg Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/xvmalloc_int.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/staging/zram/xvmalloc_int.h b/drivers/staging/zram/xvmalloc_int.h index e23ed5c8b8e4e..82a31fb99574b 100644 --- a/drivers/staging/zram/xvmalloc_int.h +++ b/drivers/staging/zram/xvmalloc_int.h @@ -19,7 +19,11 @@ /* User configurable params */ /* Must be power of two */ +#ifdef CONFIG_64BIT +#define XV_ALIGN_SHIFT 3 +#else #define XV_ALIGN_SHIFT 2 +#endif #define XV_ALIGN (1 << XV_ALIGN_SHIFT) #define XV_ALIGN_MASK (XV_ALIGN - 1) @@ -27,8 +31,16 @@ #define XV_MIN_ALLOC_SIZE 32 #define XV_MAX_ALLOC_SIZE (PAGE_SIZE - XV_ALIGN) -/* Free lists are separated by FL_DELTA bytes */ -#define FL_DELTA_SHIFT 3 +/* + * Free lists are separated by FL_DELTA bytes + * This value is 3 for 4k pages and 4 for 64k pages, for any + * other page size, a conservative (PAGE_SHIFT - 9) is used. + */ +#if PAGE_SHIFT == 16 +#define FL_DELTA_SHIFT 4 +#else +#define FL_DELTA_SHIFT (PAGE_SHIFT - 9) +#endif #define FL_DELTA (1 << FL_DELTA_SHIFT) #define FL_DELTA_MASK (FL_DELTA - 1) #define NUM_FREE_LISTS ((XV_MAX_ALLOC_SIZE - XV_MIN_ALLOC_SIZE) \ From 3c8c8399c8c8874fe2f86dc4514a71ade02a9575 Mon Sep 17 00:00:00 2001 From: Robert Jennings Date: Fri, 28 Jan 2011 08:58:17 -0600 Subject: [PATCH 2124/2556] zram: Prevent overflow in logical block size On a 64K page kernel, the value PAGE_SIZE passed to blk_queue_logical_block_size would overflow the logical block size argument (resulting in setting it to 0). This patch sets the logical block size to 4096, using a new ZRAM_LOGICAL_BLOCK_SIZE constant. Signed-off-by: Robert Jennings Reviewed-by: Pekka Enberg Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/zram_drv.c | 3 ++- drivers/staging/zram/zram_drv.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index f1f3307c9db5e..1ead5849a81c5 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -620,7 +620,8 @@ static int create_device(struct zram *zram, int device_id) * and n*PAGE_SIZED sized I/O requests. */ blk_queue_physical_block_size(zram->disk->queue, PAGE_SIZE); - blk_queue_logical_block_size(zram->disk->queue, PAGE_SIZE); + blk_queue_logical_block_size(zram->disk->queue, + ZRAM_LOGICAL_BLOCK_SIZE); blk_queue_io_min(zram->disk->queue, PAGE_SIZE); blk_queue_io_opt(zram->disk->queue, PAGE_SIZE); diff --git a/drivers/staging/zram/zram_drv.h b/drivers/staging/zram/zram_drv.h index a48155112b1e6..408b2c067fc9c 100644 --- a/drivers/staging/zram/zram_drv.h +++ b/drivers/staging/zram/zram_drv.h @@ -61,6 +61,7 @@ static const unsigned max_zpage_size = PAGE_SIZE / 4 * 3; #define SECTOR_SIZE (1 << SECTOR_SHIFT) #define SECTORS_PER_PAGE_SHIFT (PAGE_SHIFT - SECTOR_SHIFT) #define SECTORS_PER_PAGE (1 << SECTORS_PER_PAGE_SHIFT) +#define ZRAM_LOGICAL_BLOCK_SIZE 4096 /* Flags for zram pages (table[page_no].flags) */ enum zram_pageflags { From c1741a85a0bd7cf95fa3118798677b2990fe106b Mon Sep 17 00:00:00 2001 From: Robert Jennings Date: Fri, 28 Jan 2011 08:58:54 -0600 Subject: [PATCH 2125/2556] zram/xvmalloc: free bit block insertion optimization This change is in a conditional block which is entered only when there is an existing data block on the freelist where the insert has taken place. The new block is pushed onto the freelist stack and this conditional block is updating links in the prior stack head to point to the new stack head. After this conditional block the first-/second-level indices are updated to indicate that there is a free block at this location. This patch adds an immediate return from the conditional block to avoid setting bits again to indicate a free block on this freelist. The bits would already be set because there was an existing free block on this freelist. Signed-off-by: Robert Jennings Reviewed-by: Pekka Enberg Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/xvmalloc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/zram/xvmalloc.c b/drivers/staging/zram/xvmalloc.c index 3ed744ba7ba01..9cbe04a496e1b 100644 --- a/drivers/staging/zram/xvmalloc.c +++ b/drivers/staging/zram/xvmalloc.c @@ -200,6 +200,8 @@ static void insert_block(struct xv_pool *pool, struct page *page, u32 offset, nextblock->link.prev_page = page; nextblock->link.prev_offset = offset; put_ptr_atomic(nextblock, KM_USER1); + /* If there was a next page then the free bits are set. */ + return; } __set_bit(slindex % BITS_PER_LONG, &pool->slbitmap[flindex]); From 1125bd094bb0948f52b9e90123198ce4189a1f1c Mon Sep 17 00:00:00 2001 From: Robert Jennings Date: Fri, 28 Jan 2011 08:59:26 -0600 Subject: [PATCH 2126/2556] zram/xvmalloc: create CONFIG_ZRAM_DEBUG for debug code Add a debug config flag to enable debug printk output and future debug code. Signed-off-by: Robert Jennings Reviewed-by: Pekka Enberg Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/Kconfig | 8 ++++++++ drivers/staging/zram/xvmalloc.c | 4 ++++ drivers/staging/zram/zram_drv.c | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/drivers/staging/zram/Kconfig b/drivers/staging/zram/Kconfig index d3982e6fdb40f..2f3b484ce5a45 100644 --- a/drivers/staging/zram/Kconfig +++ b/drivers/staging/zram/Kconfig @@ -15,3 +15,11 @@ config ZRAM See zram.txt for more information. Project home: http://compcache.googlecode.com/ + +config ZRAM_DEBUG + bool "Compressed RAM block device debug support" + depends on ZRAM + default n + help + This option adds additional debugging code to the compressed + RAM block device driver. diff --git a/drivers/staging/zram/xvmalloc.c b/drivers/staging/zram/xvmalloc.c index 9cbe04a496e1b..4f6cb8de6865b 100644 --- a/drivers/staging/zram/xvmalloc.c +++ b/drivers/staging/zram/xvmalloc.c @@ -10,6 +10,10 @@ * Released under the terms of GNU General Public License Version 2.0 */ +#ifdef CONFIG_ZRAM_DEBUG +#define DEBUG +#endif + #include #include #include diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 1ead5849a81c5..f9581559d15d4 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -15,6 +15,10 @@ #define KMSG_COMPONENT "zram" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#ifdef CONFIG_ZRAM_DEBUG +#define DEBUG +#endif + #include #include #include From 5dd0beb8ef48c801f8feb34e0014e367a0378157 Mon Sep 17 00:00:00 2001 From: Robert Jennings Date: Fri, 28 Jan 2011 09:00:03 -0600 Subject: [PATCH 2127/2556] zram/xvmalloc: Close 32byte hole on 64bit CPUs By swapping the total_pages statistic with the lock we close a hole in the structure for 64-bit CPUs. Signed-off-by: Robert Jennings Reviewed-by: Pekka Enberg Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/xvmalloc_int.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/staging/zram/xvmalloc_int.h b/drivers/staging/zram/xvmalloc_int.h index 82a31fb99574b..b5f1f7febcf63 100644 --- a/drivers/staging/zram/xvmalloc_int.h +++ b/drivers/staging/zram/xvmalloc_int.h @@ -87,12 +87,9 @@ struct block_header { struct xv_pool { ulong flbitmap; ulong slbitmap[MAX_FLI]; - spinlock_t lock; - + u64 total_pages; /* stats */ struct freelist_entry freelist[NUM_FREE_LISTS]; - - /* stats */ - u64 total_pages; + spinlock_t lock; }; #endif From 60517a6630f56111d85046cc9c66589af2a5266e Mon Sep 17 00:00:00 2001 From: Robert Jennings Date: Fri, 28 Jan 2011 09:00:42 -0600 Subject: [PATCH 2128/2556] zram: Return zero'd pages on new reads Currently zram will do nothing to the page in the bvec when that page has not been previously written. This allows random data to leak to user space. That can be seen by doing the following: ## Load the module and create a 256Mb zram device called /dev/zram0 # modprobe zram # echo $((256*1024*1024)) > /sys/class/block/zram0/disksize ## Initialize the device by writing zero to the first block # dd if=/dev/zero of=/dev/zram0 bs=512 count=1 ## Read ~256Mb of memory into a file and hope for something interesting # dd if=/dev/zram0 of=file This patch will treat an unwritten page as a zero-filled page. If a page is read before a write has occurred the data returned is all 0's. Signed-off-by: Robert Jennings Reviewed-by: Pekka Enberg Signed-off-by: Greg Kroah-Hartman Conflicts: drivers/staging/zram/zram_drv.c Fixed to match merge cleanup by Greg Kroah-Hartman in: https://www.codeaurora.org/gitweb/quic/la/?p=kernel/msm.git;a=commitdiff;h=80ae3fa5d6712ef3625eff617f72e190645d6361 --- drivers/staging/zram/zram_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index f9581559d15d4..5fdf9d2fc950f 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -238,7 +238,7 @@ static void zram_read(struct zram *zram, struct bio *bio) if (unlikely(!zram->table[index].page)) { pr_debug("Read before write: sector=%lu, size=%u", (ulong)(bio->bi_sector), bio->bi_size); - /* Do nothing */ + handle_zero_page(page); index++; continue; } From 2cea842e7e7fa2ec7273dcb634bb9d4a9e2fcff5 Mon Sep 17 00:00:00 2001 From: Robert Jennings Date: Fri, 28 Jan 2011 09:01:55 -0600 Subject: [PATCH 2129/2556] zram/xvmalloc: combine duplicate block delete code This patch eliminates duplicate code. The remove_block_head function is a special case of remove_block which can be contained in remove_block without confusion. The portion of code in remove_block_head which was noted as "DEBUG ONLY" is now mandatory. Doing this provides consistent management of the double linked list of blocks under a freelist and makes this consolidation of delete block code safe. The first and last blocks will have NULL pointers in their previous and next page pointers respectively. Additionally, any time a block is removed from a free list the next and previous pointers will be set to NULL to avoid misuse outside xvmalloc. Signed-off-by: Robert Jennings Reviewed-by: Pekka Enberg Acked-by: Nitin Gupta Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/xvmalloc.c | 73 ++++++++++++++------------------- 1 file changed, 31 insertions(+), 42 deletions(-) diff --git a/drivers/staging/zram/xvmalloc.c b/drivers/staging/zram/xvmalloc.c index 4f6cb8de6865b..ae0623a65ab9c 100644 --- a/drivers/staging/zram/xvmalloc.c +++ b/drivers/staging/zram/xvmalloc.c @@ -212,55 +212,15 @@ static void insert_block(struct xv_pool *pool, struct page *page, u32 offset, __set_bit(flindex, &pool->flbitmap); } -/* - * Remove block from head of freelist. Index 'slindex' identifies the freelist. - */ -static void remove_block_head(struct xv_pool *pool, - struct block_header *block, u32 slindex) -{ - struct block_header *tmpblock; - u32 flindex = slindex / BITS_PER_LONG; - - pool->freelist[slindex].page = block->link.next_page; - pool->freelist[slindex].offset = block->link.next_offset; - block->link.prev_page = NULL; - block->link.prev_offset = 0; - - if (!pool->freelist[slindex].page) { - __clear_bit(slindex % BITS_PER_LONG, &pool->slbitmap[flindex]); - if (!pool->slbitmap[flindex]) - __clear_bit(flindex, &pool->flbitmap); - } else { - /* - * DEBUG ONLY: We need not reinitialize freelist head previous - * pointer to 0 - we never depend on its value. But just for - * sanity, lets do it. - */ - tmpblock = get_ptr_atomic(pool->freelist[slindex].page, - pool->freelist[slindex].offset, KM_USER1); - tmpblock->link.prev_page = NULL; - tmpblock->link.prev_offset = 0; - put_ptr_atomic(tmpblock, KM_USER1); - } -} - /* * Remove block from freelist. Index 'slindex' identifies the freelist. */ static void remove_block(struct xv_pool *pool, struct page *page, u32 offset, struct block_header *block, u32 slindex) { - u32 flindex; + u32 flindex = slindex / BITS_PER_LONG; struct block_header *tmpblock; - if (pool->freelist[slindex].page == page - && pool->freelist[slindex].offset == offset) { - remove_block_head(pool, block, slindex); - return; - } - - flindex = slindex / BITS_PER_LONG; - if (block->link.prev_page) { tmpblock = get_ptr_atomic(block->link.prev_page, block->link.prev_offset, KM_USER1); @@ -276,6 +236,35 @@ static void remove_block(struct xv_pool *pool, struct page *page, u32 offset, tmpblock->link.prev_offset = block->link.prev_offset; put_ptr_atomic(tmpblock, KM_USER1); } + + /* Is this block is at the head of the freelist? */ + if (pool->freelist[slindex].page == page + && pool->freelist[slindex].offset == offset) { + + pool->freelist[slindex].page = block->link.next_page; + pool->freelist[slindex].offset = block->link.next_offset; + + if (pool->freelist[slindex].page) { + struct block_header *tmpblock; + tmpblock = get_ptr_atomic(pool->freelist[slindex].page, + pool->freelist[slindex].offset, + KM_USER1); + tmpblock->link.prev_page = NULL; + tmpblock->link.prev_offset = 0; + put_ptr_atomic(tmpblock, KM_USER1); + } else { + /* This freelist bucket is empty */ + __clear_bit(slindex % BITS_PER_LONG, + &pool->slbitmap[flindex]); + if (!pool->slbitmap[flindex]) + __clear_bit(flindex, &pool->flbitmap); + } + } + + block->link.prev_page = NULL; + block->link.prev_offset = 0; + block->link.next_page = NULL; + block->link.next_offset = 0; } /* @@ -384,7 +373,7 @@ int xv_malloc(struct xv_pool *pool, u32 size, struct page **page, block = get_ptr_atomic(*page, *offset, KM_USER0); - remove_block_head(pool, block, index); + remove_block(pool, *page, *offset, block, index); /* Split the block if required */ tmpoffset = *offset + size + XV_ALIGN; From 644a297df446ec41a40ed8dd3699de49b18de566 Mon Sep 17 00:00:00 2001 From: Jerome Marchand Date: Thu, 17 Feb 2011 17:11:49 +0100 Subject: [PATCH 2130/2556] Staging: zram: initialize device on first read Currently the device is initialized when first write is done to the device. Any read attempt before the first write would fail, including "hidden" read the user may not know about (as for example if he tries to write a partial block). This patch initializes the device on first request, whether read or write. Signed-off-by: Jerome Marchand Cc: Nitin Gupta Signed-off-by: Greg Kroah-Hartman --- drivers/staging/zram/zram_drv.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index 5fdf9d2fc950f..aab4ec482124e 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -211,11 +211,6 @@ static void zram_read(struct zram *zram, struct bio *bio) u32 index; struct bio_vec *bvec; - if (unlikely(!zram->init_done)) { - bio_endio(bio, -ENXIO); - return; - } - zram_stat64_inc(zram, &zram->stats.num_reads); index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT; @@ -286,20 +281,15 @@ static void zram_read(struct zram *zram, struct bio *bio) static void zram_write(struct zram *zram, struct bio *bio) { - int i, ret; + int i; u32 index; struct bio_vec *bvec; - if (unlikely(!zram->init_done)) { - ret = zram_init_device(zram); - if (ret) - goto out; - } - zram_stat64_inc(zram, &zram->stats.num_writes); index = bio->bi_sector >> SECTORS_PER_PAGE_SHIFT; bio_for_each_segment(bvec, bio, i) { + int ret; u32 offset; size_t clen; struct zobj_header *zheader; @@ -445,6 +435,11 @@ static int zram_make_request(struct request_queue *queue, struct bio *bio) return 0; } + if (unlikely(!zram->init_done) && zram_init_device(zram)) { + bio_io_error(bio); + return 0; + } + switch (bio_data_dir(bio)) { case READ: zram_read(zram, bio); From e5dd8240a2b20f1b9a21686f447bcd75ace03904 Mon Sep 17 00:00:00 2001 From: Nitin Gupta Date: Fri, 18 Feb 2011 17:33:18 -0500 Subject: [PATCH 2131/2556] staging: Allow sharing xvmalloc for zram and zcache Both zram and zcache use xvmalloc allocator. If xvmalloc is compiled separately for both of them, we will get linker error if they are both selected as "built-in". We can also get linker error regarding missing xvmalloc symbols if zram is not built. So, we now compile xvmalloc separately and export its symbols which are then used by both of zram and zcache. Signed-off-by: Nitin Gupta Acked-by: Randy Dunlap Signed-off-by: Greg Kroah-Hartman Conflicts: drivers/staging/Makefile drivers/staging/zcache/Makefile Kernel does not presently support zcache. Those elements removed. --- drivers/staging/Makefile | 1 + drivers/staging/zram/Kconfig | 5 +++++ drivers/staging/zram/Makefile | 3 ++- drivers/staging/zram/xvmalloc.c | 8 ++++++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 0208ba2fc650c..e98af574e8e11 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_DX_SEP) += sep/ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio/ obj-$(CONFIG_ZRAM) += zram/ +obj-$(CONFIG_XVMALLOC) += zram/ obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/ obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/ obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop/ diff --git a/drivers/staging/zram/Kconfig b/drivers/staging/zram/Kconfig index 2f3b484ce5a45..3bec4dba3fe5d 100644 --- a/drivers/staging/zram/Kconfig +++ b/drivers/staging/zram/Kconfig @@ -1,6 +1,11 @@ +config XVMALLOC + bool + default n + config ZRAM tristate "Compressed RAM block device support" depends on BLOCK && SYSFS + select XVMALLOC select LZO_COMPRESS select LZO_DECOMPRESS default n diff --git a/drivers/staging/zram/Makefile b/drivers/staging/zram/Makefile index b1709c57f636a..2a6d3213a7564 100644 --- a/drivers/staging/zram/Makefile +++ b/drivers/staging/zram/Makefile @@ -1,3 +1,4 @@ -zram-y := zram_drv.o zram_sysfs.o xvmalloc.o +zram-y := zram_drv.o zram_sysfs.o obj-$(CONFIG_ZRAM) += zram.o +obj-$(CONFIG_XVMALLOC) += xvmalloc.o \ No newline at end of file diff --git a/drivers/staging/zram/xvmalloc.c b/drivers/staging/zram/xvmalloc.c index ae0623a65ab9c..1f9c5082b6d5d 100644 --- a/drivers/staging/zram/xvmalloc.c +++ b/drivers/staging/zram/xvmalloc.c @@ -14,6 +14,8 @@ #define DEBUG #endif +#include +#include #include #include #include @@ -315,11 +317,13 @@ struct xv_pool *xv_create_pool(void) return pool; } +EXPORT_SYMBOL_GPL(xv_create_pool); void xv_destroy_pool(struct xv_pool *pool) { kfree(pool); } +EXPORT_SYMBOL_GPL(xv_destroy_pool); /** * xv_malloc - Allocate block of given size from pool. @@ -408,6 +412,7 @@ int xv_malloc(struct xv_pool *pool, u32 size, struct page **page, return 0; } +EXPORT_SYMBOL_GPL(xv_malloc); /* * Free block identified with @@ -484,6 +489,7 @@ void xv_free(struct xv_pool *pool, struct page *page, u32 offset) put_ptr_atomic(page_start, KM_USER0); spin_unlock(&pool->lock); } +EXPORT_SYMBOL_GPL(xv_free); u32 xv_get_object_size(void *obj) { @@ -492,6 +498,7 @@ u32 xv_get_object_size(void *obj) blk = (struct block_header *)((char *)(obj) - XV_ALIGN); return blk->size; } +EXPORT_SYMBOL_GPL(xv_get_object_size); /* * Returns total memory used by allocator (userdata + metadata) @@ -500,3 +507,4 @@ u64 xv_get_total_size_bytes(struct xv_pool *pool) { return pool->total_pages << PAGE_SHIFT; } +EXPORT_SYMBOL_GPL(xv_get_total_size_bytes); From 72cb1def9a975e57fc114cb72bf7ce9eef624f2f Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 22 Mar 2011 16:32:46 -0700 Subject: [PATCH 2132/2556] mm: rename drop_anon_vma() to put_anon_vma() The normal code pattern used in the kernel is: get/put. Signed-off-by: Peter Zijlstra Reviewed-by: KAMEZAWA Hiroyuki Acked-by: Hugh Dickins Reviewed-by: Rik van Riel Acked-by: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/rmap.h | 4 ++-- mm/ksm.c | 23 +++++------------------ mm/migrate.c | 4 ++-- mm/rmap.c | 4 ++-- 4 files changed, 11 insertions(+), 24 deletions(-) diff --git a/include/linux/rmap.h b/include/linux/rmap.h index e9fd04ca1e518..b9b23ddca63af 100644 --- a/include/linux/rmap.h +++ b/include/linux/rmap.h @@ -87,7 +87,7 @@ static inline void get_anon_vma(struct anon_vma *anon_vma) atomic_inc(&anon_vma->external_refcount); } -void drop_anon_vma(struct anon_vma *); +void put_anon_vma(struct anon_vma *); #else static inline void anonvma_external_refcount_init(struct anon_vma *anon_vma) { @@ -102,7 +102,7 @@ static inline void get_anon_vma(struct anon_vma *anon_vma) { } -static inline void drop_anon_vma(struct anon_vma *anon_vma) +static inline void put_anon_vma(struct anon_vma *anon_vma) { } #endif /* CONFIG_KSM */ diff --git a/mm/ksm.c b/mm/ksm.c index c2b2a94f9d677..1bbe785aa559c 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -301,20 +301,6 @@ static inline int in_stable_tree(struct rmap_item *rmap_item) return rmap_item->address & STABLE_FLAG; } -static void hold_anon_vma(struct rmap_item *rmap_item, - struct anon_vma *anon_vma) -{ - rmap_item->anon_vma = anon_vma; - get_anon_vma(anon_vma); -} - -static void ksm_drop_anon_vma(struct rmap_item *rmap_item) -{ - struct anon_vma *anon_vma = rmap_item->anon_vma; - - drop_anon_vma(anon_vma); -} - /* * ksmd, and unmerge_and_remove_all_rmap_items(), must not touch an mm's * page tables after it has passed through ksm_exit() - which, if necessary, @@ -397,7 +383,7 @@ static void break_cow(struct rmap_item *rmap_item) * It is not an accident that whenever we want to break COW * to undo, we also need to drop a reference to the anon_vma. */ - ksm_drop_anon_vma(rmap_item); + put_anon_vma(rmap_item->anon_vma); down_read(&mm->mmap_sem); if (ksm_test_exit(mm)) @@ -466,7 +452,7 @@ static void remove_node_from_stable_tree(struct stable_node *stable_node) ksm_pages_sharing--; else ksm_pages_shared--; - ksm_drop_anon_vma(rmap_item); + put_anon_vma(rmap_item->anon_vma); rmap_item->address &= PAGE_MASK; cond_resched(); } @@ -554,7 +540,7 @@ static void remove_rmap_item_from_tree(struct rmap_item *rmap_item) else ksm_pages_shared--; - ksm_drop_anon_vma(rmap_item); + put_anon_vma(rmap_item->anon_vma); rmap_item->address &= PAGE_MASK; } else if (rmap_item->address & UNSTABLE_FLAG) { @@ -949,7 +935,8 @@ static int try_to_merge_with_ksm_page(struct rmap_item *rmap_item, goto out; /* Must get reference to anon_vma while still holding mmap_sem */ - hold_anon_vma(rmap_item, vma->anon_vma); + rmap_item->anon_vma = vma->anon_vma; + get_anon_vma(vma->anon_vma); out: up_read(&mm->mmap_sem); return err; diff --git a/mm/migrate.c b/mm/migrate.c index 352de555626c4..00bb07924da21 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -764,7 +764,7 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, /* Drop an anon_vma reference if we took one */ if (anon_vma) - drop_anon_vma(anon_vma); + put_anon_vma(anon_vma); uncharge: if (!charge) @@ -856,7 +856,7 @@ static int unmap_and_move_huge_page(new_page_t get_new_page, remove_migration_ptes(hpage, hpage); if (anon_vma) - drop_anon_vma(anon_vma); + put_anon_vma(anon_vma); out: unlock_page(hpage); diff --git a/mm/rmap.c b/mm/rmap.c index 941bf82e89612..ad416afb2061f 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -278,7 +278,7 @@ static void anon_vma_unlink(struct anon_vma_chain *anon_vma_chain) if (empty) { /* We no longer need the root anon_vma */ if (anon_vma->root != anon_vma) - drop_anon_vma(anon_vma->root); + put_anon_vma(anon_vma->root); anon_vma_free(anon_vma); } } @@ -1493,7 +1493,7 @@ int try_to_munlock(struct page *page) * we know we are the last user, nobody else can get a reference and we * can do the freeing without the lock. */ -void drop_anon_vma(struct anon_vma *anon_vma) +void put_anon_vma(struct anon_vma *anon_vma) { BUG_ON(atomic_read(&anon_vma->external_refcount) <= 0); if (atomic_dec_and_lock(&anon_vma->external_refcount, &anon_vma->root->lock)) { From 830000e437990defb8d6ffcd3ce4333759dc1504 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 24 May 2011 17:11:40 -0700 Subject: [PATCH 2133/2556] oom: replace PF_OOM_ORIGIN with toggling oom_score_adj There's a kernel-wide shortage of per-process flags, so it's always helpful to trim one when possible without incurring a significant penalty. It's even more important when you're planning on adding a per- process flag yourself, which I plan to do shortly for transparent hugepages. PF_OOM_ORIGIN is used by ksm and swapoff to prefer current since it has a tendency to allocate large amounts of memory and should be preferred for killing over other tasks. We'd rather immediately kill the task making the errant syscall rather than penalizing an innocent task. This patch removes PF_OOM_ORIGIN since its behavior is equivalent to setting the process's oom_score_adj to OOM_SCORE_ADJ_MAX. The process's old oom_score_adj is stored and then set to OOM_SCORE_ADJ_MAX during the time it used to have PF_OOM_ORIGIN. The old value is then reinstated when the process should no longer be considered a high priority for oom killing. Signed-off-by: David Rientjes Reviewed-by: KOSAKI Motohiro Reviewed-by: Minchan Kim Cc: Hugh Dickins Cc: Izik Eidus Cc: KAMEZAWA Hiroyuki Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/oom.h | 2 ++ include/linux/sched.h | 1 - mm/ksm.c | 7 +++++-- mm/oom_kill.c | 36 +++++++++++++++++++++++++++--------- mm/swapfile.c | 6 ++++-- 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/include/linux/oom.h b/include/linux/oom.h index 5e3aa8311c5ed..4952fb874ad3d 100644 --- a/include/linux/oom.h +++ b/include/linux/oom.h @@ -40,6 +40,8 @@ enum oom_constraint { CONSTRAINT_MEMCG, }; +extern int test_set_oom_score_adj(int new_val); + extern unsigned int oom_badness(struct task_struct *p, struct mem_cgroup *mem, const nodemask_t *nodemask, unsigned long totalpages); extern int try_set_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); diff --git a/include/linux/sched.h b/include/linux/sched.h index 815480e21121d..cc42d1ce6a505 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1742,7 +1742,6 @@ extern int task_free_unregister(struct notifier_block *n); #define PF_FROZEN 0x00010000 /* frozen for system suspend */ #define PF_FSTRANS 0x00020000 /* inside a filesystem transaction */ #define PF_KSWAPD 0x00040000 /* I am kswapd */ -#define PF_OOM_ORIGIN 0x00080000 /* Allocating much memory to others */ #define PF_LESS_THROTTLE 0x00100000 /* Throttle me less: I clean memory */ #define PF_KTHREAD 0x00200000 /* I am a kernel thread */ #define PF_RANDOMIZE 0x00400000 /* randomize virtual address space */ diff --git a/mm/ksm.c b/mm/ksm.c index 1bbe785aa559c..eed97e1e7dd88 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "internal.h" @@ -1894,9 +1895,11 @@ static ssize_t run_store(struct kobject *kobj, struct kobj_attribute *attr, if (ksm_run != flags) { ksm_run = flags; if (flags & KSM_RUN_UNMERGE) { - current->flags |= PF_OOM_ORIGIN; + int oom_score_adj; + + oom_score_adj = test_set_oom_score_adj(OOM_SCORE_ADJ_MAX); err = unmerge_and_remove_all_rmap_items(); - current->flags &= ~PF_OOM_ORIGIN; + test_set_oom_score_adj(oom_score_adj); if (err) { ksm_run = KSM_RUN_STOP; count = err; diff --git a/mm/oom_kill.c b/mm/oom_kill.c index 49ea0cc9ae129..4b34bb0ba75ae 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -38,6 +38,33 @@ int sysctl_oom_kill_allocating_task; int sysctl_oom_dump_tasks = 1; static DEFINE_SPINLOCK(zone_scan_lock); +/** + * test_set_oom_score_adj() - set current's oom_score_adj and return old value + * @new_val: new oom_score_adj value + * + * Sets the oom_score_adj value for current to @new_val with proper + * synchronization and returns the old value. Usually used to temporarily + * set a value, save the old value in the caller, and then reinstate it later. + */ +int test_set_oom_score_adj(int new_val) +{ + struct sighand_struct *sighand = current->sighand; + int old_val; + + spin_lock_irq(&sighand->siglock); + old_val = current->signal->oom_score_adj; + if (new_val != old_val) { + if (new_val == OOM_SCORE_ADJ_MIN) + atomic_inc(¤t->mm->oom_disable_count); + else if (old_val == OOM_SCORE_ADJ_MIN) + atomic_dec(¤t->mm->oom_disable_count); + current->signal->oom_score_adj = new_val; + } + spin_unlock_irq(&sighand->siglock); + + return old_val; +} + #ifdef CONFIG_NUMA /** * has_intersects_mems_allowed() - check task eligiblity for kill @@ -154,15 +181,6 @@ unsigned int oom_badness(struct task_struct *p, struct mem_cgroup *mem, return 0; } - /* - * When the PF_OOM_ORIGIN bit is set, it indicates the task should have - * priority for oom killing. - */ - if (p->flags & PF_OOM_ORIGIN) { - task_unlock(p); - return 1000; - } - /* * The memory controller may have a limit of 0 bytes, so avoid a divide * by zero, if necessary. diff --git a/mm/swapfile.c b/mm/swapfile.c index 6d6d28c0a72f6..7fd98e62cda34 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -1558,6 +1559,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) struct address_space *mapping; struct inode *inode; char *pathname; + int oom_score_adj; int i, type, prev; int err; @@ -1616,9 +1618,9 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile) p->flags &= ~SWP_WRITEOK; spin_unlock(&swap_lock); - current->flags |= PF_OOM_ORIGIN; + oom_score_adj = test_set_oom_score_adj(OOM_SCORE_ADJ_MAX); err = try_to_unuse(type); - current->flags &= ~PF_OOM_ORIGIN; + test_set_oom_score_adj(oom_score_adj); if (err) { /* re-insert swap space back into swap_list */ From 0d23d1c90bcc64af1a8cfae6e4f6b0fe9eb400dd Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Wed, 15 Jun 2011 15:08:58 -0700 Subject: [PATCH 2134/2556] ksm: fix NULL pointer dereference in scan_get_next_rmap_item() Andrea Righi reported a case where an exiting task can race against ksmd::scan_get_next_rmap_item (http://lkml.org/lkml/2011/6/1/742) easily triggering a NULL pointer dereference in ksmd. ksm_scan.mm_slot == &ksm_mm_head with only one registered mm CPU 1 (__ksm_exit) CPU 2 (scan_get_next_rmap_item) list_empty() is false lock slot == &ksm_mm_head list_del(slot->mm_list) (list now empty) unlock lock slot = list_entry(slot->mm_list.next) (list is empty, so slot is still ksm_mm_head) unlock slot->mm == NULL ... Oops Close this race by revalidating that the new slot is not simply the list head again. Andrea's test case: #include #include #include #include #define BUFSIZE getpagesize() int main(int argc, char **argv) { void *ptr; if (posix_memalign(&ptr, getpagesize(), BUFSIZE) < 0) { perror("posix_memalign"); exit(1); } if (madvise(ptr, BUFSIZE, MADV_MERGEABLE) < 0) { perror("madvise"); exit(1); } *(char *)NULL = 0; return 0; } Reported-by: Andrea Righi Tested-by: Andrea Righi Cc: Andrea Arcangeli Signed-off-by: Hugh Dickins Signed-off-by: Chris Wright Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/ksm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mm/ksm.c b/mm/ksm.c index eed97e1e7dd88..78df7f472a425 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -1302,6 +1302,12 @@ static struct rmap_item *scan_get_next_rmap_item(struct page **page) slot = list_entry(slot->mm_list.next, struct mm_slot, mm_list); ksm_scan.mm_slot = slot; spin_unlock(&ksm_mmlist_lock); + /* + * Although we tested list_empty() above, a racing __ksm_exit + * of the last mm on the list may have removed it since then. + */ + if (slot == &ksm_mm_head) + return NULL; next_mm: ksm_scan.address = 0; ksm_scan.rmap_list = &slot->rmap_list; From 52804ab9df63c3044bf3c41446664ab983252f21 Mon Sep 17 00:00:00 2001 From: Jeremy Gebben Date: Fri, 13 May 2011 14:55:52 -0600 Subject: [PATCH 2135/2556] msm: kgsl: clean up adreno_chipid() and add gpu revision tracking The chipid isn't calculated correctly on some msms, 8x50, 8x60v2 and 8960v1 at least. Change the calculation to detect these from the cpuinfo instead of the broken chipid values. However, even when it is correct chipid isn't really useful for distingushing between major gpu revisions. Our previous system of handling adreno220 vs older gpus is broken now that we're supporting 8960v1 which has a different patch release versus 8660v2. This change adds an enum for adreno major versions and functions to check them easily. It also switches existing checks to use the new functions instead of relying on the 8660v2 chip id. Change-Id: Ic40c95ad6d3e97bd1b2aadab507d1b71dbe68eac Signed-off-by: Jordan Crouse Signed-off-by: Jeremy Gebben Conflicts: drivers/gpu/msm/adreno.c --- drivers/gpu/msm/adreno.c | 100 ++++++++++++++++++++-------- drivers/gpu/msm/adreno.h | 43 ++++++++++++ drivers/gpu/msm/adreno_drawctxt.c | 49 +++++++------- drivers/gpu/msm/adreno_postmortem.c | 29 ++------ drivers/gpu/msm/adreno_ringbuffer.c | 4 +- drivers/gpu/msm/kgsl_device.h | 5 -- drivers/gpu/msm/z180.c | 2 +- 7 files changed, 148 insertions(+), 84 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index d130607ff151f..25e239e20ee73 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -16,6 +16,8 @@ #include #include +#include + #include "kgsl.h" #include "kgsl_pwrscale.h" #include "kgsl_cffdump.h" @@ -137,7 +139,7 @@ static int adreno_gmeminit(struct adreno_device *adreno_dev) rb_edram_info.val = 0; rb_edram_info.f.edram_size = edram_value; - if (device->chip_id != KGSL_CHIPID_LEIA_REV470) + if (!adreno_is_a220(adreno_dev)) rb_edram_info.f.edram_mapping_mode = 0; /* EDRAM_MAP_UPPER */ /* must be aligned to size */ @@ -335,7 +337,7 @@ static int adreno_setstate(struct kgsl_device *device, uint32_t flags) } if (flags & KGSL_MMUFLAGS_PTUPDATE && - device->chip_id != KGSL_CHIPID_LEIA_REV470) { + !adreno_is_a220(adreno_dev)) { /* HW workaround: to resolve MMU page fault interrupts * caused by the VGT.It prevents the CP PFP from filling * the VGT DMA request fifo too early,thereby ensuring @@ -401,38 +403,78 @@ static int adreno_setstate(struct kgsl_device *device, uint32_t flags) static unsigned int adreno_getchipid(struct kgsl_device *device) { - unsigned int chipid; + unsigned int chipid = 0; unsigned int coreid, majorid, minorid, patchid, revid; - /* YDX */ adreno_regread(device, REG_RBBM_PERIPHID1, &coreid); - coreid &= 0xF; - adreno_regread(device, REG_RBBM_PERIPHID2, &majorid); - majorid = (majorid >> 4) & 0xF; - adreno_regread(device, REG_RBBM_PATCH_RELEASE, &revid); - /* this is a 16bit field, but extremely unlikely it would ever get - * this high + + /* + * adreno 22x gpus are indicated by coreid 2, + * but REG_RBBM_PERIPHID1 always contains 0 for this field */ - minorid = ((revid >> 0) & 0xFF); + if (cpu_is_msm8960() || cpu_is_msm8x60()) + chipid = 2 << 24; + else + chipid = (coreid & 0xF) << 24; + chipid |= ((majorid >> 4) & 0xF) << 16; + + minorid = ((revid >> 0) & 0xFF); patchid = ((revid >> 16) & 0xFF); - chipid = ((coreid << 24) | (majorid << 16) | - (minorid << 8) | (patchid << 0)); + /* 8x50 returns 0 for patch release, but it should be 1 */ + if (cpu_is_qsd8x50()) + patchid = 1; + /* userspace isn't prepared to deal with patch id for these chips yet */ + else if (cpu_is_msm8960() || cpu_is_msm8x60()) + patchid = 0; - /* Hardware revision 211 (8650) returns the wrong chip ID */ - if (chipid == KGSL_CHIPID_YAMATODX_REV21) - chipid = KGSL_CHIPID_YAMATODX_REV211; + chipid |= (minorid << 8) | patchid; - /* Workaround Hardware revision issue of Z470 */ - if (chipid == KGSL_CHIPID_LEIA_REV470_TEMP) - chipid = KGSL_CHIPID_LEIA_REV470; + return chipid; +} +/* all chipid fields are 8 bits wide so 256 won't occur in a real chipid */ +#define DONT_CARE 256 +static const struct { + unsigned int core; + unsigned int major; + unsigned int minor; + enum adreno_gpurev gpurev; +} gpurev_table[] = { + /* major and minor may be DONT_CARE, but core must not be */ + {0, 2, DONT_CARE, ADRENO_REV_A200}, + {0, 1, 0, ADRENO_REV_A205}, + {2, 1, DONT_CARE, ADRENO_REV_A220}, + {2, 2, DONT_CARE, ADRENO_REV_A225}, +}; - return chipid; +static inline bool _rev_match(unsigned int id, unsigned int entry) +{ + return (entry == DONT_CARE || entry == id); +} +#undef DONT_CARE + +enum adreno_gpurev adreno_get_rev(struct adreno_device *adreno_dev) +{ + enum adreno_gpurev gpurev = ADRENO_REV_UNKNOWN; + unsigned int i, core, major, minor; + core = (adreno_dev->chip_id >> 24) & 0xff; + major = (adreno_dev->chip_id >> 16) & 0xff; + minor = (adreno_dev->chip_id >> 8) & 0xff; + + for (i = 0; i < ARRAY_SIZE(gpurev_table); i++) { + if (core == gpurev_table[i].core && + _rev_match(major, gpurev_table[i].major) && + _rev_match(minor, gpurev_table[i].minor)) { + gpurev = gpurev_table[i].gpurev; + break; + } + } + return gpurev; } static int __devinit @@ -499,20 +541,20 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) if (kgsl_mmu_start(device)) goto error_clk_off; + adreno_dev->chip_id = adreno_getchipid(device); + /*We need to make sure all blocks are powered up and clocked before *issuing a soft reset. The overrides will then be turned off (set to 0) */ adreno_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0xfffffffe); - device->chip_id = adreno_getchipid(device); - - if (device->chip_id == CHIP_REV_251) + if (adreno_dev->chip_id == CHIP_REV_251) adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0x000000ff); else adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xffffffff); /* Only reset CP block if all blocks have previously been reset */ if (!(device->flags & KGSL_FLAGS_SOFT_RESET) || - (device->chip_id != KGSL_CHIPID_LEIA_REV470)) { + !adreno_is_a220(adreno_dev)) { adreno_regwrite(device, REG_RBBM_SOFT_RESET, 0xFFFFFFFF); device->flags |= KGSL_FLAGS_SOFT_RESET; } else @@ -530,7 +572,7 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) adreno_regwrite(device, REG_MH_ARBITER_CONFIG, adreno_dev->mharb); - if (device->chip_id != KGSL_CHIPID_LEIA_REV470) { + if (!adreno_is_a220(adreno_dev)) { adreno_regwrite(device, REG_MH_CLNT_INTF_CTRL_CONFIG1, 0x00030f27); adreno_regwrite(device, @@ -539,7 +581,7 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) /* Remove 1k boundary check in z470 to avoid GPU hang. Notice that, this solution won't work if both EBI and SMI are used */ - if (device->chip_id == KGSL_CHIPID_LEIA_REV470) { + if (adreno_is_a220(adreno_dev)) { adreno_regwrite(device, REG_MH_CLNT_INTF_CTRL_CONFIG1, 0x00032f07); } @@ -548,7 +590,7 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) adreno_regwrite(device, REG_SQ_PS_PROGRAM, 0x00000000); adreno_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0); - if (device->chip_id != KGSL_CHIPID_LEIA_REV470) + if (!adreno_is_a220(adreno_dev)) adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0); else adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0x80); @@ -564,7 +606,7 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) /* make sure SQ interrupts are disabled */ adreno_regwrite(device, REG_SQ_INT_CNTL, 0); - if (device->chip_id == KGSL_CHIPID_LEIA_REV470) + if (adreno_is_a220(adreno_dev)) adreno_dev->gmemspace.sizebytes = SZ_512K; else adreno_dev->gmemspace.sizebytes = SZ_256K; @@ -757,7 +799,7 @@ static int adreno_getproperty(struct kgsl_device *device, memset(&devinfo, 0, sizeof(devinfo)); devinfo.device_id = device->id+1; - devinfo.chip_id = device->chip_id; + devinfo.chip_id = adreno_dev->chip_id; devinfo.mmu_enabled = kgsl_mmu_enabled(); devinfo.gmem_hostbaseaddr = (unsigned int) adreno_dev->gmemspace.mmio_virt_base; diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index 5cbff5d81d16c..74b3a845723ee 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -49,6 +49,7 @@ struct adreno_device { struct kgsl_device dev; /* Must be first field in this struct */ + unsigned int chip_id; struct kgsl_memregion gmemspace; struct adreno_context *drawctxt_active; wait_queue_head_t ib1_wq; @@ -75,4 +76,46 @@ void adreno_regwrite_isr(struct kgsl_device *device, uint8_t *kgsl_sharedmem_convertaddr(struct kgsl_device *device, unsigned int pt_base, unsigned int gpuaddr, unsigned int *size); +enum adreno_gpurev { + ADRENO_REV_UNKNOWN = 0, + ADRENO_REV_A200 = 200, + ADRENO_REV_A205 = 205, + ADRENO_REV_A220 = 220, + ADRENO_REV_A225 = 225, +}; + +enum adreno_gpurev adreno_get_rev(struct adreno_device *adreno_dev); + +static inline int adreno_is_a200(struct adreno_device *adreno_dev) +{ + return (adreno_get_rev(adreno_dev) == ADRENO_REV_A200); +} + +static inline int adreno_is_a205(struct adreno_device *adreno_dev) +{ + return (adreno_get_rev(adreno_dev) == ADRENO_REV_A200); +} + +static inline int adreno_is_a20x(struct adreno_device *adreno_dev) +{ + enum adreno_gpurev rev = adreno_get_rev(adreno_dev); + return (rev == ADRENO_REV_A200 || rev == ADRENO_REV_A205); +} + +static inline int adreno_is_a220(struct adreno_device *adreno_dev) +{ + return (adreno_get_rev(adreno_dev) == ADRENO_REV_A220); +} + +static inline int adreno_is_a225(struct adreno_device *adreno_dev) +{ + return (adreno_get_rev(adreno_dev) == ADRENO_REV_A225); +} + +static inline int adreno_is_a22x(struct adreno_device *adreno_dev) +{ + enum adreno_gpurev rev = adreno_get_rev(adreno_dev); + return (rev == ADRENO_REV_A220 || rev == ADRENO_REV_A225); +} + #endif /*__ADRENO_H */ diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c index 85b7611e97d4c..ab28b0d2362e1 100644 --- a/drivers/gpu/msm/adreno_drawctxt.c +++ b/drivers/gpu/msm/adreno_drawctxt.c @@ -458,7 +458,7 @@ static unsigned int *build_chicken_restore_cmds( /* save h/w regs, alu constants, texture contants, etc. ... * requires: bool_shadow_gpuaddr, loop_shadow_gpuaddr */ -static void build_regsave_cmds(struct kgsl_device *device, +static void build_regsave_cmds(struct adreno_device *adreno_dev, struct adreno_context *drawctxt, struct tmp_ctx *ctx) { @@ -485,7 +485,7 @@ static void build_regsave_cmds(struct kgsl_device *device, build_reg_to_mem_range(REG_PA_SC_WINDOW_OFFSET, REG_PA_SC_WINDOW_SCISSOR_BR, &cmd, drawctxt); - if (device->chip_id != KGSL_CHIPID_LEIA_REV470) { + if (!adreno_is_a220(adreno_dev)) { build_reg_to_mem_range(REG_VGT_MAX_VTX_INDX, REG_RB_FOG_COLOR, &cmd, drawctxt); } else { @@ -501,7 +501,7 @@ static void build_regsave_cmds(struct kgsl_device *device, &cmd, drawctxt); build_reg_to_mem_range(REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1, &cmd, drawctxt); - if (device->chip_id != KGSL_CHIPID_LEIA_REV470) { + if (!adreno_is_a220(adreno_dev)) { build_reg_to_mem_range(REG_RB_DEPTHCONTROL, REG_RB_MODECONTROL, &cmd, drawctxt); build_reg_to_mem_range(REG_PA_SU_POINT_SIZE, @@ -527,7 +527,7 @@ static void build_regsave_cmds(struct kgsl_device *device, &cmd, drawctxt); build_reg_to_mem_range(REG_PA_SC_AA_MASK, REG_PA_SC_AA_MASK, &cmd, drawctxt); - if (device->chip_id != KGSL_CHIPID_LEIA_REV470) { + if (!adreno_is_a220(adreno_dev)) { build_reg_to_mem_range(REG_VGT_VERTEX_REUSE_BLOCK_CNTL, REG_RB_DEPTH_CLEAR, &cmd, drawctxt); @@ -605,7 +605,7 @@ static void build_regsave_cmds(struct kgsl_device *device, *cmd++ = REG_RBBM_PM_OVERRIDE2; *cmd++ = ctx->reg_values[2]; - if (device->chip_id == KGSL_CHIPID_LEIA_REV470) { + if (adreno_is_a220(adreno_dev)) { unsigned int i; unsigned int j = 3; for (i = REG_LEIA_VSC_BIN_SIZE; i <= @@ -631,7 +631,7 @@ static void build_regsave_cmds(struct kgsl_device *device, } /*copy colour, depth, & stencil buffers from graphics memory to system memory*/ -static unsigned int *build_gmem2sys_cmds(struct kgsl_device *device, +static unsigned int *build_gmem2sys_cmds(struct adreno_device *adreno_dev, struct adreno_context *drawctxt, struct tmp_ctx *ctx, struct gmem_shadow_t *shadow) @@ -697,7 +697,7 @@ static unsigned int *build_gmem2sys_cmds(struct kgsl_device *device, /* SQ_PROGRAM_CNTL / SQ_CONTEXT_MISC */ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); *cmds++ = PM4_REG(REG_SQ_PROGRAM_CNTL); - if (device->chip_id == KGSL_CHIPID_LEIA_REV470) + if (adreno_is_a220(adreno_dev)) *cmds++ = 0x10018001; else *cmds++ = 0x10010001; @@ -735,7 +735,7 @@ static unsigned int *build_gmem2sys_cmds(struct kgsl_device *device, /* disable Z */ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); *cmds++ = PM4_REG(REG_RB_DEPTHCONTROL); - if (device->chip_id == KGSL_CHIPID_LEIA_REV470) + if (adreno_is_a220(adreno_dev)) *cmds++ = 0x08; else *cmds++ = 0; @@ -792,7 +792,7 @@ static unsigned int *build_gmem2sys_cmds(struct kgsl_device *device, *cmds++ = PM4_REG(REG_RB_MODECONTROL); *cmds++ = 0x6; /* EDRAM copy */ - if (device->chip_id == KGSL_CHIPID_LEIA_REV470) { + if (adreno_is_a220(adreno_dev)) { *cmds++ = 0xc0043600; /* packet 3 3D_DRAW_INDX_2 */ *cmds++ = 0x0; *cmds++ = 0x00004046; /* tristrip */ @@ -816,7 +816,7 @@ static unsigned int *build_gmem2sys_cmds(struct kgsl_device *device, /* context restore */ /*copy colour, depth, & stencil buffers from system memory to graphics memory*/ -static unsigned int *build_sys2gmem_cmds(struct kgsl_device *device, +static unsigned int *build_sys2gmem_cmds(struct adreno_device *adreno_dev, struct adreno_context *drawctxt, struct tmp_ctx *ctx, struct gmem_shadow_t *shadow) @@ -879,7 +879,7 @@ static unsigned int *build_sys2gmem_cmds(struct kgsl_device *device, *cmds++ = PM4_REG(REG_PA_SC_AA_MASK); *cmds++ = 0x0000ffff; /* REG_PA_SC_AA_MASK */ - if (device->chip_id != KGSL_CHIPID_LEIA_REV470) { + if (!adreno_is_a220(adreno_dev)) { /* PA_SC_VIZ_QUERY */ *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); *cmds++ = PM4_REG(REG_PA_SC_VIZ_QUERY); @@ -953,7 +953,7 @@ static unsigned int *build_sys2gmem_cmds(struct kgsl_device *device, *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); *cmds++ = PM4_REG(REG_RB_DEPTHCONTROL); - if (device->chip_id == KGSL_CHIPID_LEIA_REV470) + if (adreno_is_a220(adreno_dev)) *cmds++ = 8; /* disable Z */ else *cmds++ = 0; /* disable Z */ @@ -1001,7 +1001,7 @@ static unsigned int *build_sys2gmem_cmds(struct kgsl_device *device, /* draw pixels with color and depth/stencil component */ *cmds++ = 0x4; - if (device->chip_id == KGSL_CHIPID_LEIA_REV470) { + if (adreno_is_a220(adreno_dev)) { *cmds++ = 0xc0043600; /* packet 3 3D_DRAW_INDX_2 */ *cmds++ = 0x0; *cmds++ = 0x00004046; /* tristrip */ @@ -1031,7 +1031,7 @@ static unsigned *reg_range(unsigned int *cmd, unsigned int start, return cmd; } -static void build_regrestore_cmds(struct kgsl_device *device, +static void build_regrestore_cmds(struct adreno_device *adreno_dev, struct adreno_context *drawctxt, struct tmp_ctx *ctx) { @@ -1051,7 +1051,7 @@ static void build_regrestore_cmds(struct kgsl_device *device, *cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000; #endif - if (device->chip_id != KGSL_CHIPID_LEIA_REV470) { + if (!adreno_is_a220(adreno_dev)) { cmd = reg_range(cmd, REG_RB_SURFACE_INFO, REG_PA_SC_SCREEN_SCISSOR_BR); } else { @@ -1061,7 +1061,7 @@ static void build_regrestore_cmds(struct kgsl_device *device, } cmd = reg_range(cmd, REG_PA_SC_WINDOW_OFFSET, REG_PA_SC_WINDOW_SCISSOR_BR); - if (device->chip_id != KGSL_CHIPID_LEIA_REV470) { + if (!adreno_is_a220(adreno_dev)) { cmd = reg_range(cmd, REG_VGT_MAX_VTX_INDX, REG_PA_CL_VPORT_ZOFFSET); } else { @@ -1072,7 +1072,7 @@ static void build_regrestore_cmds(struct kgsl_device *device, REG_PA_CL_VPORT_ZOFFSET); } cmd = reg_range(cmd, REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1); - if (device->chip_id != KGSL_CHIPID_LEIA_REV470) { + if (!adreno_is_a220(adreno_dev)) { cmd = reg_range(cmd, REG_RB_DEPTHCONTROL, REG_RB_MODECONTROL); cmd = reg_range(cmd, REG_PA_SU_POINT_SIZE, REG_PA_SC_VIZ_QUERY); /*REG_VGT_ENHANCE */ @@ -1119,12 +1119,12 @@ static void build_regrestore_cmds(struct kgsl_device *device, *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE2, 1); ctx->reg_values[2] = gpuaddr(cmd, &drawctxt->gpustate); - if (device->chip_id != KGSL_CHIPID_LEIA_REV470) + if (!adreno_is_a220(adreno_dev)) *cmd++ = 0x00000000; else *cmd++ = 0x80; - if (device->chip_id == KGSL_CHIPID_LEIA_REV470) { + if (adreno_is_a220(adreno_dev)) { unsigned int i; unsigned int j = 3; for (i = REG_LEIA_VSC_BIN_SIZE; i <= @@ -1371,6 +1371,7 @@ create_gpustate_shadow(struct kgsl_device *device, struct adreno_context *drawctxt, struct tmp_ctx *ctx) { + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); int result; /* Allocate vmalloc memory to store the gpustate */ @@ -1391,8 +1392,8 @@ create_gpustate_shadow(struct kgsl_device *device, /* build indirect command buffers to save & restore regs/constants */ adreno_idle(device, KGSL_TIMEOUT_DEFAULT); - build_regrestore_cmds(device, drawctxt, ctx); - build_regsave_cmds(device, drawctxt, ctx); + build_regrestore_cmds(adreno_dev, drawctxt, ctx); + build_regsave_cmds(adreno_dev, drawctxt, ctx); build_shader_save_restore_cmds(drawctxt, ctx); @@ -1441,11 +1442,11 @@ create_gmem_shadow(struct adreno_device *adreno_dev, adreno_idle(device, KGSL_TIMEOUT_DEFAULT); drawctxt->context_gmem_shadow.gmem_save_commands = ctx->cmd; ctx->cmd = - build_gmem2sys_cmds(device, drawctxt, ctx, + build_gmem2sys_cmds(adreno_dev, drawctxt, ctx, &drawctxt->context_gmem_shadow); drawctxt->context_gmem_shadow.gmem_restore_commands = ctx->cmd; ctx->cmd = - build_sys2gmem_cmds(device, drawctxt, ctx, + build_sys2gmem_cmds(adreno_dev, drawctxt, ctx, &drawctxt->context_gmem_shadow); kgsl_cache_range_op(&drawctxt->context_gmem_shadow.gmemshadow, @@ -1672,7 +1673,7 @@ adreno_drawctxt_switch(struct adreno_device *adreno_dev, cmds[0] = pm4_type3_packet(PM4_SET_BIN_BASE_OFFSET, 1); cmds[1] = drawctxt->bin_base_offset; - if (device->chip_id != KGSL_CHIPID_LEIA_REV470) + if (!adreno_is_a220(adreno_dev)) adreno_ringbuffer_issuecmds(device, 0, cmds, 2); } else diff --git a/drivers/gpu/msm/adreno_postmortem.c b/drivers/gpu/msm/adreno_postmortem.c index de386f957177c..4911e93775901 100644 --- a/drivers/gpu/msm/adreno_postmortem.c +++ b/drivers/gpu/msm/adreno_postmortem.c @@ -156,22 +156,6 @@ static const int a220_registers[] = { 0x12400, 0x12400, 0x12420, 0x12420 }; -static struct { - int id; - const int *registers; - int len; -} kgsl_registers[] = { - { KGSL_CHIPID_LEIA_REV470, a220_registers, - ARRAY_SIZE(a220_registers) / 2 }, - { KGSL_CHIPID_LEIA_REV470_TEMP, a220_registers, - ARRAY_SIZE(a220_registers) / 2 }, - { KGSL_CHIPID_YAMATODX_REV21, a200_registers, - ARRAY_SIZE(a200_registers) / 2 }, - { KGSL_CHIPID_YAMATODX_REV211, a200_registers, - ARRAY_SIZE(a200_registers) / 2 }, - { 0x0, NULL, 0}, -}; - static uint32_t adreno_is_pm4_len(uint32_t word) { if (word == INVALID_RB_CMD) @@ -775,13 +759,12 @@ static int adreno_dump(struct kgsl_device *device) /* Dump the registers if the user asked for it */ - for (i = 0; kgsl_pmregs_enabled() && kgsl_registers[i].id; i++) { - if (kgsl_registers[i].id == device->chip_id) { - adreno_dump_regs(device, kgsl_registers[i].registers, - kgsl_registers[i].len); - break; - } - } + if (adreno_is_a20x(adreno_dev)) + adreno_dump_regs(device, a200_registers, + ARRAY_SIZE(a200_registers) / 2); + else if (adreno_is_a22x(adreno_dev)) + adreno_dump_regs(device, a220_registers, + ARRAY_SIZE(a220_registers) / 2); error_vfree: vfree(rb_copy); diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index 45dc3f5b3b5b1..4aaa2c6c33570 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -269,7 +269,7 @@ static int adreno_ringbuffer_load_pm4_ucode(struct kgsl_device *device) const char *fwfile; int i, ret = 0; - if (device->chip_id == KGSL_CHIPID_LEIA_REV470) + if (adreno_is_a220(adreno_dev)) fwfile = A220_PM4_470_FW; else fwfile = A200_PM4_FW; @@ -312,7 +312,7 @@ static int adreno_ringbuffer_load_pfp_ucode(struct kgsl_device *device) const char *fwfile; int i, ret = 0; - if (device->chip_id == KGSL_CHIPID_LEIA_REV470) + if (adreno_is_a220(adreno_dev)) fwfile = A220_PFP_470_FW; else fwfile = A200_PFP_FW; diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index df986009b1c3b..bb0f2b35ab3d6 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -42,10 +42,6 @@ #define FIRST_TIMEOUT (HZ / 2) -#define KGSL_CHIPID_YAMATODX_REV21 0x20100 -#define KGSL_CHIPID_YAMATODX_REV211 0x20101 -#define KGSL_CHIPID_LEIA_REV470_TEMP 0x10001 -#define KGSL_CHIPID_LEIA_REV470 0x2010000 /* KGSL device state is initialized to INIT when platform_probe * * sucessfully initialized the device. Once a device has been opened * @@ -140,7 +136,6 @@ struct kgsl_device { unsigned int ver_minor; uint32_t flags; enum kgsl_deviceid id; - unsigned int chip_id; struct kgsl_memregion regspace; struct kgsl_memdesc memstore; const char *iomemname; diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c index f1bc2caad4794..27da432ed8336 100644 --- a/drivers/gpu/msm/z180.c +++ b/drivers/gpu/msm/z180.c @@ -667,7 +667,7 @@ static int z180_getproperty(struct kgsl_device *device, memset(&devinfo, 0, sizeof(devinfo)); devinfo.device_id = device->id+1; - devinfo.chip_id = device->chip_id; + devinfo.chip_id = 0; devinfo.mmu_enabled = kgsl_mmu_enabled(); if (copy_to_user(value, &devinfo, sizeof(devinfo)) != From 72c787ac111d43c584b314766bba12a079d49e21 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 26 Feb 2012 19:03:33 -0600 Subject: [PATCH 2136/2556] Revert "Revert "msm: kgsl: put gpu_id into struct kgsl_devinfo"" This reverts commit 937c732a2063700b625c645ce1d262d1745d6fb5. Conflicts: include/linux/msm_kgsl.h --- drivers/gpu/msm/adreno.c | 3 +-- include/linux/msm_kgsl.h | 8 +++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 25e239e20ee73..bd19aa4ce3341 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -801,8 +801,7 @@ static int adreno_getproperty(struct kgsl_device *device, devinfo.device_id = device->id+1; devinfo.chip_id = adreno_dev->chip_id; devinfo.mmu_enabled = kgsl_mmu_enabled(); - devinfo.gmem_hostbaseaddr = (unsigned int) - adreno_dev->gmemspace.mmio_virt_base; + devinfo.gpu_id = adreno_get_rev(adreno_dev); devinfo.gmem_gpubaseaddr = adreno_dev->gmemspace. gpu_base; devinfo.gmem_sizebytes = adreno_dev->gmemspace. diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index 9af0605e7ead9..ceada7850c1ff 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -83,9 +83,11 @@ struct kgsl_devinfo { unsigned int chip_id; unsigned int mmu_enabled; unsigned int gmem_gpubaseaddr; - /* if gmem_hostbaseaddr is NULL, we would know its not mapped into - * mmio space */ - unsigned int gmem_hostbaseaddr; + /* + * This field contains the adreno revision + * number 200, 205, 220, etc... + */ + unsigned int gpu_id; unsigned int gmem_sizebytes; }; From 1a61515edfe523671b6478cf29d2d80b49b61082 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sat, 25 Feb 2012 22:25:01 -0600 Subject: [PATCH 2137/2556] kgsl: getchipid: remove soc checks we know we are building 8x50 and the cpu_is** functions dont work on all our devices anyway. this is easier that adding our boards to socinfo. --- drivers/gpu/msm/adreno.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index bd19aa4ce3341..db3d9782d34e6 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -16,8 +16,6 @@ #include #include -#include - #include "kgsl.h" #include "kgsl_pwrscale.h" #include "kgsl_cffdump.h" @@ -403,6 +401,7 @@ static int adreno_setstate(struct kgsl_device *device, uint32_t flags) static unsigned int adreno_getchipid(struct kgsl_device *device) { + /* XXX: drewis edit: only for 8x50 */ unsigned int chipid = 0; unsigned int coreid, majorid, minorid, patchid, revid; @@ -410,27 +409,13 @@ adreno_getchipid(struct kgsl_device *device) adreno_regread(device, REG_RBBM_PERIPHID2, &majorid); adreno_regread(device, REG_RBBM_PATCH_RELEASE, &revid); - /* - * adreno 22x gpus are indicated by coreid 2, - * but REG_RBBM_PERIPHID1 always contains 0 for this field - */ - if (cpu_is_msm8960() || cpu_is_msm8x60()) - chipid = 2 << 24; - else - chipid = (coreid & 0xF) << 24; + chipid = (coreid & 0xF) << 24; chipid |= ((majorid >> 4) & 0xF) << 16; minorid = ((revid >> 0) & 0xFF); - patchid = ((revid >> 16) & 0xFF); - - /* 8x50 returns 0 for patch release, but it should be 1 */ - if (cpu_is_qsd8x50()) - patchid = 1; - /* userspace isn't prepared to deal with patch id for these chips yet */ - else if (cpu_is_msm8960() || cpu_is_msm8x60()) - patchid = 0; + patchid = 1; chipid |= (minorid << 8) | patchid; From c5a8f2522a71a635158a081d229f96a4713d9481 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sat, 25 Feb 2012 22:43:25 -0600 Subject: [PATCH 2138/2556] kgsl: cffdump: remove soc check --- drivers/gpu/msm/kgsl_cffdump.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c index 74075bf40eddb..702e1ac7428ad 100644 --- a/drivers/gpu/msm/kgsl_cffdump.c +++ b/drivers/gpu/msm/kgsl_cffdump.c @@ -20,7 +20,6 @@ #include #include #include -#include #include "kgsl.h" #include "kgsl_cffdump.h" @@ -363,10 +362,8 @@ void kgsl_cffdump_open(enum kgsl_deviceid device_id) /*TODO: move this to where we can report correct gmemsize*/ unsigned int va_base; - if (cpu_is_msm8x60() || cpu_is_msm8960()) - va_base = 0x40000000; - else - va_base = 0x20000000; + /* XXX: drewis edit: only for 8x50 */ + va_base = 0x20000000; kgsl_cffdump_memory_base(device_id, va_base, CONFIG_MSM_KGSL_PAGE_TABLE_SIZE, SZ_256K); From 2f20957cbb2e41e1069a86b225160f4b262ce484 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sat, 25 Feb 2012 22:46:14 -0600 Subject: [PATCH 2139/2556] msm: remove socinfo it was broke and only needed by kgsl --- arch/arm/mach-msm/Makefile | 1 - arch/arm/mach-msm/include/mach/socinfo.h | 138 ----- arch/arm/mach-msm/socinfo.c | 655 ----------------------- 3 files changed, 794 deletions(-) delete mode 100644 arch/arm/mach-msm/include/mach/socinfo.h delete mode 100644 arch/arm/mach-msm/socinfo.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 606149d557adf..ed9a59bd2c114 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -8,7 +8,6 @@ obj-y += nand_partitions.o obj-y += pmic.o obj-y += drv_callback.o obj-y += htc_board_tags.o -obj-y += socinfo.o obj-$(CONFIG_ARCH_MSM_ARM11) += acpuclock-arm11.o idle.o obj-$(CONFIG_ARCH_MSM_SCORPION) += idle-v7.o diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h deleted file mode 100644 index 5040212a0e93d..0000000000000 --- a/arch/arm/mach-msm/include/mach/socinfo.h +++ /dev/null @@ -1,138 +0,0 @@ -/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef _ARCH_ARM_MACH_MSM_SOCINFO_H_ -#define _ARCH_ARM_MACH_MSM_SOCINFO_H_ - -#include - -/* - * SOC version type with major number in the upper 16 bits and minor - * number in the lower 16 bits. For example: - * 1.0 -> 0x00010000 - * 2.3 -> 0x00020003 - */ -#define SOCINFO_VERSION_MAJOR(ver) ((ver & 0xffff0000) >> 16) -#define SOCINFO_VERSION_MINOR(ver) (ver & 0x0000ffff) - -enum msm_cpu { - MSM_CPU_UNKNOWN = 0, - MSM_CPU_7X01, - MSM_CPU_7X25, - MSM_CPU_7X27, - MSM_CPU_8X50, - MSM_CPU_8X50A, - MSM_CPU_7X30, - MSM_CPU_8X55, - MSM_CPU_8X60, - MSM_CPU_8960, - MSM_CPU_7X27A, -}; - -enum msm_cpu socinfo_get_msm_cpu(void); -uint32_t socinfo_get_id(void); -uint32_t socinfo_get_version(void); -char *socinfo_get_build_id(void); -uint32_t socinfo_get_platform_type(void); -uint32_t socinfo_get_platform_subtype(void); -uint32_t socinfo_get_platform_version(void); -int __init socinfo_init(void) __must_check; - -static inline int cpu_is_msm7x01(void) -{ - enum msm_cpu cpu = socinfo_get_msm_cpu(); - - BUG_ON(cpu == MSM_CPU_UNKNOWN); - return cpu == MSM_CPU_7X01; -} - -static inline int cpu_is_msm7x25(void) -{ - enum msm_cpu cpu = socinfo_get_msm_cpu(); - - BUG_ON(cpu == MSM_CPU_UNKNOWN); - return cpu == MSM_CPU_7X25; -} - -static inline int cpu_is_msm7x27(void) -{ - enum msm_cpu cpu = socinfo_get_msm_cpu(); - - BUG_ON(cpu == MSM_CPU_UNKNOWN); - return cpu == MSM_CPU_7X27; -} - -static inline int cpu_is_msm7x27a(void) -{ - enum msm_cpu cpu = socinfo_get_msm_cpu(); - - BUG_ON(cpu == MSM_CPU_UNKNOWN); - return cpu == MSM_CPU_7X27A; -} - -static inline int cpu_is_msm7x30(void) -{ - enum msm_cpu cpu = socinfo_get_msm_cpu(); - - BUG_ON(cpu == MSM_CPU_UNKNOWN); - return cpu == MSM_CPU_7X30; -} - -static inline int cpu_is_qsd8x50(void) -{ - enum msm_cpu cpu = socinfo_get_msm_cpu(); - - BUG_ON(cpu == MSM_CPU_UNKNOWN); - return cpu == MSM_CPU_8X50; -} - -static inline int cpu_is_msm8x55(void) -{ - enum msm_cpu cpu = socinfo_get_msm_cpu(); - - BUG_ON(cpu == MSM_CPU_UNKNOWN); - return cpu == MSM_CPU_8X55; -} - -static inline int cpu_is_msm8x60(void) -{ - enum msm_cpu cpu = socinfo_get_msm_cpu(); - - BUG_ON(cpu == MSM_CPU_UNKNOWN); - return cpu == MSM_CPU_8X60; -} - -static inline int cpu_is_msm8960(void) -{ - enum msm_cpu cpu = socinfo_get_msm_cpu(); - - BUG_ON(cpu == MSM_CPU_UNKNOWN); - return cpu == MSM_CPU_8960; -} -#endif diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c deleted file mode 100644 index 313ef99bde9ba..0000000000000 --- a/arch/arm/mach-msm/socinfo.c +++ /dev/null @@ -1,655 +0,0 @@ -/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ -/* - * SOC Info Routines - * - */ - -#include -#include -#include -#include - -#include "smd_private.h" - -#define BUILD_ID_LENGTH 32 - -enum { - HW_PLATFORM_UNKNOWN = 0, - HW_PLATFORM_SURF = 1, - HW_PLATFORM_FFA = 2, - HW_PLATFORM_FLUID = 3, - HW_PLATFORM_SVLTE_FFA = 4, - HW_PLATFORM_SVLTE_SURF = 5, - HW_PLATFORM_INVALID -}; - -const char *hw_platform[] = { - [HW_PLATFORM_UNKNOWN] = "Unknown", - [HW_PLATFORM_SURF] = "Surf", - [HW_PLATFORM_FFA] = "FFA", - [HW_PLATFORM_FLUID] = "Fluid", - [HW_PLATFORM_SVLTE_FFA] = "SVLTE_FFA", - [HW_PLATFORM_SVLTE_SURF] = "SLVTE_SURF" -}; - -enum { - ACCESSORY_CHIP_UNKNOWN = 0, - ACCESSORY_CHIP_CHARM = 58, -}; - -enum { - PLATFORM_SUBTYPE_UNKNOWN = 0x0, - PLATFORM_SUBTYPE_CHARM = 0x1, - PLATFORM_SUBTYPE_STRANGE = 0x2, - PLATFORM_SUBTYPE_STRANGE_2A = 0x3, - PLATFORM_SUBTYPE_INVALID, -}; - -const char *hw_platform_subtype[] = { - [PLATFORM_SUBTYPE_UNKNOWN] = "Unknown", - [PLATFORM_SUBTYPE_CHARM] = "charm", - [PLATFORM_SUBTYPE_STRANGE] = "strange", - [PLATFORM_SUBTYPE_STRANGE_2A] = "strange_2a," -}; - -/* Used to parse shared memory. Must match the modem. */ -struct socinfo_v1 { - uint32_t format; - uint32_t id; - uint32_t version; - char build_id[BUILD_ID_LENGTH]; -}; - -struct socinfo_v2 { - struct socinfo_v1 v1; - - /* only valid when format==2 */ - uint32_t raw_id; - uint32_t raw_version; -}; - -struct socinfo_v3 { - struct socinfo_v2 v2; - - /* only valid when format==3 */ - uint32_t hw_platform; -}; - -struct socinfo_v4 { - struct socinfo_v3 v3; - - /* only valid when format==4 */ - uint32_t platform_version; -}; - -struct socinfo_v5 { - struct socinfo_v4 v4; - - /* only valid when format==5 */ - uint32_t accessory_chip; -}; - -struct socinfo_v6 { - struct socinfo_v5 v5; - - /* only valid when format==6 */ - uint32_t hw_platform_subtype; -}; - -static union { - struct socinfo_v1 v1; - struct socinfo_v2 v2; - struct socinfo_v3 v3; - struct socinfo_v4 v4; - struct socinfo_v5 v5; - struct socinfo_v6 v6; -} *socinfo; - -static enum msm_cpu cpu_of_id[] = { - - /* 7x01 IDs */ - [1] = MSM_CPU_7X01, - [16] = MSM_CPU_7X01, - [17] = MSM_CPU_7X01, - [18] = MSM_CPU_7X01, - [19] = MSM_CPU_7X01, - [23] = MSM_CPU_7X01, - [25] = MSM_CPU_7X01, - [26] = MSM_CPU_7X01, - [32] = MSM_CPU_7X01, - [33] = MSM_CPU_7X01, - [34] = MSM_CPU_7X01, - [35] = MSM_CPU_7X01, - - /* 7x25 IDs */ - [20] = MSM_CPU_7X25, - [21] = MSM_CPU_7X25, /* 7225 */ - [24] = MSM_CPU_7X25, /* 7525 */ - [27] = MSM_CPU_7X25, /* 7625 */ - [39] = MSM_CPU_7X25, - [40] = MSM_CPU_7X25, - [41] = MSM_CPU_7X25, - [42] = MSM_CPU_7X25, - [62] = MSM_CPU_7X25, /* 7625-1 */ - [63] = MSM_CPU_7X25, /* 7225-1 */ - [66] = MSM_CPU_7X25, /* 7225-2 */ - - - /* 7x27 IDs */ - [43] = MSM_CPU_7X27, - [44] = MSM_CPU_7X27, - [61] = MSM_CPU_7X27, - [67] = MSM_CPU_7X27, /* 7227-1 */ - [68] = MSM_CPU_7X27, /* 7627-1 */ - [69] = MSM_CPU_7X27, /* 7627-2 */ - - - /* 8x50 IDs */ - [30] = MSM_CPU_8X50, - [36] = MSM_CPU_8X50, - [37] = MSM_CPU_8X50, - [38] = MSM_CPU_8X50, - - /* 8x50A IDs */ - [64] = MSM_CPU_8X50A, - [65] = MSM_CPU_8X50A, - - /* 7x30 IDs */ - [59] = MSM_CPU_7X30, - [60] = MSM_CPU_7X30, - - /* 8x55 IDs */ - [74] = MSM_CPU_8X55, - [75] = MSM_CPU_8X55, - [85] = MSM_CPU_8X55, - - /* 8x60 IDs */ - [70] = MSM_CPU_8X60, - [71] = MSM_CPU_8X60, - [86] = MSM_CPU_8X60, - - /* 8960 IDs */ - [87] = MSM_CPU_8960, - - /* 7x27A IDs */ - [90] = MSM_CPU_7X27A, - [91] = MSM_CPU_7X27A, - [92] = MSM_CPU_7X27A, - - /* Uninitialized IDs are not known to run Linux. - MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are - considered as unknown CPU. */ -}; - -static enum msm_cpu cur_cpu; - -static struct socinfo_v1 dummy_socinfo = { - .format = 1, - .version = 1, - .build_id = "Dummy socinfo placeholder" -}; - -uint32_t socinfo_get_id(void) -{ - return (socinfo) ? socinfo->v1.id : 0; -} -EXPORT_SYMBOL_GPL(socinfo_get_id); - -uint32_t socinfo_get_version(void) -{ - return (socinfo) ? socinfo->v1.version : 0; -} - -char *socinfo_get_build_id(void) -{ - return (socinfo) ? socinfo->v1.build_id : NULL; -} - -uint32_t socinfo_get_raw_id(void) -{ - return socinfo ? - (socinfo->v1.format >= 2 ? socinfo->v2.raw_id : 0) - : 0; -} - -uint32_t socinfo_get_raw_version(void) -{ - return socinfo ? - (socinfo->v1.format >= 2 ? socinfo->v2.raw_version : 0) - : 0; -} - -uint32_t socinfo_get_platform_type(void) -{ - return socinfo ? - (socinfo->v1.format >= 3 ? socinfo->v3.hw_platform : 0) - : 0; -} - - -uint32_t socinfo_get_platform_version(void) -{ - return socinfo ? - (socinfo->v1.format >= 4 ? socinfo->v4.platform_version : 0) - : 0; -} - -/* This information is directly encoded by the machine id */ -/* Thus no external callers rely on this information at the moment */ -static uint32_t socinfo_get_accessory_chip(void) -{ - return socinfo ? - (socinfo->v1.format >= 5 ? socinfo->v5.accessory_chip : 0) - : 0; -} - -uint32_t socinfo_get_platform_subtype(void) -{ - return socinfo ? - (socinfo->v1.format >= 6 ? socinfo->v6.hw_platform_subtype : 0) - : 0; -} - -enum msm_cpu socinfo_get_msm_cpu(void) -{ - return cur_cpu; -} -EXPORT_SYMBOL_GPL(socinfo_get_msm_cpu); - -static ssize_t -socinfo_show_id(struct sys_device *dev, - struct sysdev_attribute *attr, - char *buf) -{ - if (!socinfo) { - pr_err("%s: No socinfo found!\n", __func__); - return 0; - } - - return snprintf(buf, PAGE_SIZE, "%u\n", socinfo_get_id()); -} - -static ssize_t -socinfo_show_version(struct sys_device *dev, - struct sysdev_attribute *attr, - char *buf) -{ - uint32_t version; - - if (!socinfo) { - pr_err("%s: No socinfo found!\n", __func__); - return 0; - } - - version = socinfo_get_version(); - return snprintf(buf, PAGE_SIZE, "%u.%u\n", - SOCINFO_VERSION_MAJOR(version), - SOCINFO_VERSION_MINOR(version)); -} - -static ssize_t -socinfo_show_build_id(struct sys_device *dev, - struct sysdev_attribute *attr, - char *buf) -{ - if (!socinfo) { - pr_err("%s: No socinfo found!\n", __func__); - return 0; - } - - return snprintf(buf, PAGE_SIZE, "%-.32s\n", socinfo_get_build_id()); -} - -static ssize_t -socinfo_show_raw_id(struct sys_device *dev, - struct sysdev_attribute *attr, - char *buf) -{ - if (!socinfo) { - pr_err("%s: No socinfo found!\n", __func__); - return 0; - } - if (socinfo->v1.format < 2) { - pr_err("%s: Raw ID not available!\n", __func__); - return 0; - } - - return snprintf(buf, PAGE_SIZE, "%u\n", socinfo_get_raw_id()); -} - -static ssize_t -socinfo_show_raw_version(struct sys_device *dev, - struct sysdev_attribute *attr, - char *buf) -{ - if (!socinfo) { - pr_err("%s: No socinfo found!\n", __func__); - return 0; - } - if (socinfo->v1.format < 2) { - pr_err("%s: Raw version not available!\n", __func__); - return 0; - } - - return snprintf(buf, PAGE_SIZE, "%u\n", socinfo_get_raw_version()); -} - -static ssize_t -socinfo_show_platform_type(struct sys_device *dev, - struct sysdev_attribute *attr, - char *buf) -{ - uint32_t hw_type; - - if (!socinfo) { - pr_err("%s: No socinfo found!\n", __func__); - return 0; - } - if (socinfo->v1.format < 3) { - pr_err("%s: platform type not available!\n", __func__); - return 0; - } - - hw_type = socinfo_get_platform_type(); - if (hw_type >= HW_PLATFORM_INVALID) { - pr_err("%s: Invalid hardware platform type found\n", - __func__); - hw_type = HW_PLATFORM_UNKNOWN; - } - - return snprintf(buf, PAGE_SIZE, "%-.32s\n", hw_platform[hw_type]); -} - -static ssize_t -socinfo_show_platform_version(struct sys_device *dev, - struct sysdev_attribute *attr, - char *buf) -{ - - if (!socinfo) { - pr_err("%s: No socinfo found!\n", __func__); - return 0; - } - if (socinfo->v1.format < 4) { - pr_err("%s: platform version not available!\n", __func__); - return 0; - } - - return snprintf(buf, PAGE_SIZE, "%u\n", - socinfo_get_platform_version()); -} - -static ssize_t -socinfo_show_accessory_chip(struct sys_device *dev, - struct sysdev_attribute *attr, - char *buf) -{ - if (!socinfo) { - pr_err("%s: No socinfo found!\n", __func__); - return 0; - } - if (socinfo->v1.format < 5) { - pr_err("%s: accessory chip not available!\n", __func__); - return 0; - } - - return snprintf(buf, PAGE_SIZE, "%u\n", - socinfo_get_accessory_chip()); -} - -static ssize_t -socinfo_show_platform_subtype(struct sys_device *dev, - struct sysdev_attribute *attr, - char *buf) -{ - uint32_t hw_subtype; - if (!socinfo) { - pr_err("%s: No socinfo found!\n", __func__); - return 0; - } - if (socinfo->v1.format < 6) { - pr_err("%s: platform subtype not available!\n", __func__); - return 0; - } - - hw_subtype = socinfo_get_platform_subtype(); - if (hw_subtype >= PLATFORM_SUBTYPE_INVALID) { - pr_err("%s: Invalid hardware platform sub type found\n", - __func__); - hw_subtype = PLATFORM_SUBTYPE_UNKNOWN; - } - return snprintf(buf, PAGE_SIZE, "%-.32s\n", - hw_platform_subtype[hw_subtype]); -} - -static struct sysdev_attribute socinfo_v1_files[] = { - _SYSDEV_ATTR(id, 0444, socinfo_show_id, NULL), - _SYSDEV_ATTR(version, 0444, socinfo_show_version, NULL), - _SYSDEV_ATTR(build_id, 0444, socinfo_show_build_id, NULL), -}; - -static struct sysdev_attribute socinfo_v2_files[] = { - _SYSDEV_ATTR(raw_id, 0444, socinfo_show_raw_id, NULL), - _SYSDEV_ATTR(raw_version, 0444, socinfo_show_raw_version, NULL), -}; - -static struct sysdev_attribute socinfo_v3_files[] = { - _SYSDEV_ATTR(hw_platform, 0444, socinfo_show_platform_type, NULL), -}; - -static struct sysdev_attribute socinfo_v4_files[] = { - _SYSDEV_ATTR(platform_version, 0444, - socinfo_show_platform_version, NULL), -}; - -static struct sysdev_attribute socinfo_v5_files[] = { - _SYSDEV_ATTR(accessory_chip, 0444, - socinfo_show_accessory_chip, NULL), -}; - -static struct sysdev_attribute socinfo_v6_files[] = { - _SYSDEV_ATTR(platform_subtype, 0444, - socinfo_show_platform_subtype, NULL), -}; - -static struct sysdev_class soc_sysdev_class = { - .name = "soc", -}; - -static struct sys_device soc_sys_device = { - .id = 0, - .cls = &soc_sysdev_class, -}; - -static int __init socinfo_create_files(struct sys_device *dev, - struct sysdev_attribute files[], - int size) -{ - int i; - for (i = 0; i < size; i++) { - int err = sysdev_create_file(dev, &files[i]); - if (err) { - pr_err("%s: sysdev_create_file(%s)=%d\n", - __func__, files[i].attr.name, err); - return err; - } - } - return 0; -} - -static int __init socinfo_init_sysdev(void) -{ - int err; - - if (!socinfo) { - pr_err("%s: No socinfo found!\n", __func__); - return -ENODEV; - } - - err = sysdev_class_register(&soc_sysdev_class); - if (err) { - pr_err("%s: sysdev_class_register fail (%d)\n", - __func__, err); - return err; - } - err = sysdev_register(&soc_sys_device); - if (err) { - pr_err("%s: sysdev_register fail (%d)\n", - __func__, err); - return err; - } - socinfo_create_files(&soc_sys_device, socinfo_v1_files, - ARRAY_SIZE(socinfo_v1_files)); - if (socinfo->v1.format < 2) - return err; - socinfo_create_files(&soc_sys_device, socinfo_v2_files, - ARRAY_SIZE(socinfo_v2_files)); - - if (socinfo->v1.format < 3) - return err; - - socinfo_create_files(&soc_sys_device, socinfo_v3_files, - ARRAY_SIZE(socinfo_v3_files)); - - if (socinfo->v1.format < 4) - return err; - - socinfo_create_files(&soc_sys_device, socinfo_v4_files, - ARRAY_SIZE(socinfo_v4_files)); - - if (socinfo->v1.format < 5) - return err; - - socinfo_create_files(&soc_sys_device, socinfo_v5_files, - ARRAY_SIZE(socinfo_v5_files)); - - if (socinfo->v1.format < 6) - return err; - - return socinfo_create_files(&soc_sys_device, socinfo_v6_files, - ARRAY_SIZE(socinfo_v6_files)); - -} - -arch_initcall(socinfo_init_sysdev); - -void *setup_dummy_socinfo(void) -{ - if (machine_is_msm8960_rumi3() || machine_is_msm8960_sim())/* || - machine_is_msm8960_cdp())*/ - dummy_socinfo.id = 87; - return (void *) &dummy_socinfo; -} - -int __init socinfo_init(void) -{ - socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, sizeof(struct socinfo_v6)); - - if (!socinfo) - socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, - sizeof(struct socinfo_v5)); - - if (!socinfo) - socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, - sizeof(struct socinfo_v4)); - - if (!socinfo) - socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, - sizeof(struct socinfo_v3)); - - if (!socinfo) - socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, - sizeof(struct socinfo_v2)); - - if (!socinfo) - socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, - sizeof(struct socinfo_v1)); - - if (!socinfo) { - pr_warn("%s: Can't find SMEM_HW_SW_BUILD_ID; falling back on " - "dummy values.\n", __func__); - socinfo = setup_dummy_socinfo(); - } - - WARN(!socinfo_get_id(), "Unknown SOC ID!\n"); - WARN(socinfo_get_id() >= ARRAY_SIZE(cpu_of_id), - "New IDs added! ID => CPU mapping might need an update.\n"); - - if (socinfo->v1.id < ARRAY_SIZE(cpu_of_id)) - cur_cpu = cpu_of_id[socinfo->v1.id]; - - switch (socinfo->v1.format) { - case 1: - pr_info("%s: v%u, id=%u, ver=%u.%u\n", - __func__, socinfo->v1.format, socinfo->v1.id, - SOCINFO_VERSION_MAJOR(socinfo->v1.version), - SOCINFO_VERSION_MINOR(socinfo->v1.version)); - break; - case 2: - pr_info("%s: v%u, id=%u, ver=%u.%u, " - "raw_id=%u, raw_ver=%u\n", - __func__, socinfo->v1.format, socinfo->v1.id, - SOCINFO_VERSION_MAJOR(socinfo->v1.version), - SOCINFO_VERSION_MINOR(socinfo->v1.version), - socinfo->v2.raw_id, socinfo->v2.raw_version); - break; - case 3: - pr_info("%s: v%u, id=%u, ver=%u.%u, " - "raw_id=%u, raw_ver=%u, hw_plat=%u\n", - __func__, socinfo->v1.format, socinfo->v1.id, - SOCINFO_VERSION_MAJOR(socinfo->v1.version), - SOCINFO_VERSION_MINOR(socinfo->v1.version), - socinfo->v2.raw_id, socinfo->v2.raw_version, - socinfo->v3.hw_platform); - break; - case 4: - pr_info("%s: v%u, id=%u, ver=%u.%u, " - "raw_id=%u, raw_ver=%u, hw_plat=%u, hw_plat_ver=%u\n", - __func__, socinfo->v1.format, socinfo->v1.id, - SOCINFO_VERSION_MAJOR(socinfo->v1.version), - SOCINFO_VERSION_MINOR(socinfo->v1.version), - socinfo->v2.raw_id, socinfo->v2.raw_version, - socinfo->v3.hw_platform, socinfo->v4.platform_version); - break; - case 5: - pr_info("%s: v%u, id=%u, ver=%u.%u, " - "raw_id=%u, raw_ver=%u, hw_plat=%u, hw_plat_ver=%u\n" - " accessory_chip=%u\n", __func__, socinfo->v1.format, - socinfo->v1.id, - SOCINFO_VERSION_MAJOR(socinfo->v1.version), - SOCINFO_VERSION_MINOR(socinfo->v1.version), - socinfo->v2.raw_id, socinfo->v2.raw_version, - socinfo->v3.hw_platform, socinfo->v4.platform_version, - socinfo->v5.accessory_chip); - break; - case 6: - pr_info("%s: v%u, id=%u, ver=%u.%u, " - "raw_id=%u, raw_ver=%u, hw_plat=%u, hw_plat_ver=%u\n" - " accessory_chip=%u hw_plat_subtype=%u\n", __func__, - socinfo->v1.format, - socinfo->v1.id, - SOCINFO_VERSION_MAJOR(socinfo->v1.version), - SOCINFO_VERSION_MINOR(socinfo->v1.version), - socinfo->v2.raw_id, socinfo->v2.raw_version, - socinfo->v3.hw_platform, socinfo->v4.platform_version, - socinfo->v5.accessory_chip, - socinfo->v6.hw_platform_subtype); - break; - default: - pr_err("%s: Unknown format found\n", __func__); - break; - } - - return 0; -} From e004de89e6fc4eb9f2bee147b0eff481a7233a24 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 26 Feb 2012 20:16:06 -0500 Subject: [PATCH 2140/2556] configs: cayniarb: enable CONFIG_MSM_KGSL_MMU_PAGE_FAULT replaces error when rotating the device: kgsl kgsl-3d0: |kgsl_mh_intrcallback| axi write error interrupt with: kgsl kgsl-3d0: |kgsl_mh_intrcallback| mmu page fault interrupt with this the phone doesnt lockup when rotating the display. --- arch/arm/configs/cayniarb_bravo_defconfig | 2 +- arch/arm/configs/cayniarb_bravoc_defconfig | 2 +- arch/arm/configs/cayniarb_incrediblec_defconfig | 2 +- arch/arm/configs/cayniarb_mahimahi_defconfig | 2 +- arch/arm/configs/cayniarb_supersonic_defconfig | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/configs/cayniarb_bravo_defconfig b/arch/arm/configs/cayniarb_bravo_defconfig index 7eb625a7f0394..d0966cf6e0ae9 100644 --- a/arch/arm/configs/cayniarb_bravo_defconfig +++ b/arch/arm/configs/cayniarb_bravo_defconfig @@ -1464,7 +1464,7 @@ CONFIG_MSM_KGSL=y CONFIG_MSM_KGSL_MMU=y # CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 -# CONFIG_MSM_KGSL_MMU_PAGE_FAULT is not set +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y # CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set # CONFIG_VGASTATE is not set CONFIG_VIDEO_OUTPUT_CONTROL=y diff --git a/arch/arm/configs/cayniarb_bravoc_defconfig b/arch/arm/configs/cayniarb_bravoc_defconfig index 1aeb85d443c67..5323a4da24e13 100644 --- a/arch/arm/configs/cayniarb_bravoc_defconfig +++ b/arch/arm/configs/cayniarb_bravoc_defconfig @@ -1464,7 +1464,7 @@ CONFIG_MSM_KGSL=y CONFIG_MSM_KGSL_MMU=y # CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 -# CONFIG_MSM_KGSL_MMU_PAGE_FAULT is not set +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y # CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set # CONFIG_VGASTATE is not set CONFIG_VIDEO_OUTPUT_CONTROL=y diff --git a/arch/arm/configs/cayniarb_incrediblec_defconfig b/arch/arm/configs/cayniarb_incrediblec_defconfig index c0b5f6e921827..9a5f520d5f3bf 100644 --- a/arch/arm/configs/cayniarb_incrediblec_defconfig +++ b/arch/arm/configs/cayniarb_incrediblec_defconfig @@ -1486,7 +1486,7 @@ CONFIG_MSM_KGSL=y CONFIG_MSM_KGSL_MMU=y # CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 -# CONFIG_MSM_KGSL_MMU_PAGE_FAULT is not set +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y # CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set # CONFIG_VGASTATE is not set CONFIG_VIDEO_OUTPUT_CONTROL=y diff --git a/arch/arm/configs/cayniarb_mahimahi_defconfig b/arch/arm/configs/cayniarb_mahimahi_defconfig index 095008d0cf84b..eceab5cb55ff4 100644 --- a/arch/arm/configs/cayniarb_mahimahi_defconfig +++ b/arch/arm/configs/cayniarb_mahimahi_defconfig @@ -1461,7 +1461,7 @@ CONFIG_MSM_KGSL=y CONFIG_MSM_KGSL_MMU=y # CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 -# CONFIG_MSM_KGSL_MMU_PAGE_FAULT is not set +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y # CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set # CONFIG_VGASTATE is not set CONFIG_VIDEO_OUTPUT_CONTROL=y diff --git a/arch/arm/configs/cayniarb_supersonic_defconfig b/arch/arm/configs/cayniarb_supersonic_defconfig index e9ebb0616de72..9599e992308e9 100644 --- a/arch/arm/configs/cayniarb_supersonic_defconfig +++ b/arch/arm/configs/cayniarb_supersonic_defconfig @@ -1469,7 +1469,7 @@ CONFIG_MSM_KGSL=y CONFIG_MSM_KGSL_MMU=y # CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 -# CONFIG_MSM_KGSL_MMU_PAGE_FAULT is not set +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y # CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set # CONFIG_VGASTATE is not set CONFIG_VIDEO_OUTPUT_CONTROL=y From 234772a95a1c3a237f4ac6e84924c24d7278ac3b Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Wed, 25 Jan 2012 14:40:51 -0700 Subject: [PATCH 2141/2556] base: genlock: Remove genlock_release_lock and associated ioctl Allowing a lock to be asynchronously released while a handle was still active turned out to be too dangerous to use in a multi-threaded environment and it served no pratical purpose anyway. Handles now hold an attached lock until they are destroyed. (cherry picked from commit 4df70a254d9ddea0ee4bede39d4d12e6360e5b99) Change-Id: I969ac3cf84c5982cc7c03c5651c7fe328567c02b CRs-fixed: 333141 Signed-off-by: Jordan Crouse --- Documentation/genlock.txt | 11 +++-------- drivers/base/genlock.c | 17 ++++++++--------- include/linux/genlock.h | 4 +++- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/Documentation/genlock.txt b/Documentation/genlock.txt index d3a44e2ffa725..6f24a7695615d 100644 --- a/Documentation/genlock.txt +++ b/Documentation/genlock.txt @@ -82,15 +82,13 @@ descriptor. Release a handle. * struct genlock * genlock_create_lock(struct genlock_handle *) -Create a new lock and attach it to the handle. +Create a new lock and attach it to the handle. Once a lock is attached to a +handle it stays attached until the handle is destroyed. * struct genlock * genlock_attach_lock(struct genlock_handle *handle, int fd) Given a valid file descriptor, get the lock associated with it and attach it to the handle. -* void genlock_release_lock(struct genlock_handle *) -Release a lock attached to a handle. - * int genlock_lock(struct genlock_handle *, int op, int flags, u32 timeout) Lock or unlock the lock attached to the handle. A zero timeout value will be treated just like if the GENOCK_NOBLOCK flag is passed; if the lock @@ -155,7 +153,4 @@ passed in genlock_lock.timeout. Returns 0 when the lock has been released, -EINVAL if a zero timeout is passed, or -ETIMEDOUT if the timeout expires. * GENLOCK_IOC_RELEASE -Use this to release an existing lock. This is useful if you wish to attach a -different lock to the same handle. You do not need to call this under normal -circumstances; when the handle is closed the reference to the lock is released. -No data is passed from the user for this ioctl. +This ioctl has been deprecated. Do not use. diff --git a/drivers/base/genlock.c b/drivers/base/genlock.c index 27717e0fbdbf4..150791550c4dd 100644 --- a/drivers/base/genlock.c +++ b/drivers/base/genlock.c @@ -496,12 +496,7 @@ int genlock_wait(struct genlock_handle *handle, uint32_t timeout) return ret; } -/** - * genlock_release_lock - Release a lock attached to a handle - * @handle - Pointer to the handle holding the lock - */ - -void genlock_release_lock(struct genlock_handle *handle) +static void genlock_release_lock(struct genlock_handle *handle) { unsigned long flags; @@ -522,7 +517,6 @@ void genlock_release_lock(struct genlock_handle *handle) handle->lock = NULL; handle->active = 0; } -EXPORT_SYMBOL(genlock_release_lock); /* * Release function called when all references to a handle are released @@ -671,8 +665,13 @@ static long genlock_dev_ioctl(struct file *filep, unsigned int cmd, return genlock_wait(handle, param.timeout); } case GENLOCK_IOC_RELEASE: { - genlock_release_lock(handle); - return 0; + /* + * Return error - this ioctl has been deprecated. + * Locks should only be released when the handle is + * destroyed + */ + GENLOCK_LOG_ERR("Deprecated RELEASE ioctl called\n"); + return -EINVAL; } default: GENLOCK_LOG_ERR("Invalid ioctl\n"); diff --git a/include/linux/genlock.h b/include/linux/genlock.h index 2e9f9d682a357..9351a15626b60 100644 --- a/include/linux/genlock.h +++ b/include/linux/genlock.h @@ -12,7 +12,7 @@ void genlock_put_handle(struct genlock_handle *handle); struct genlock *genlock_create_lock(struct genlock_handle *); struct genlock *genlock_attach_lock(struct genlock_handle *, int fd); int genlock_wait(struct genlock_handle *handle, u32 timeout); -void genlock_release_lock(struct genlock_handle *); +/* genlock_release_lock was deprecated */ int genlock_lock(struct genlock_handle *handle, int op, int flags, u32 timeout); #endif @@ -39,6 +39,8 @@ struct genlock_lock { struct genlock_lock) #define GENLOCK_IOC_LOCK _IOW(GENLOCK_IOC_MAGIC, 3, \ struct genlock_lock) + +/* Deprecated */ #define GENLOCK_IOC_RELEASE _IO(GENLOCK_IOC_MAGIC, 4) #define GENLOCK_IOC_WAIT _IOW(GENLOCK_IOC_MAGIC, 5, \ struct genlock_lock) From 56762a11252fe4cc2c89c86010016a5d788ea8b6 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 25 Mar 2012 19:10:05 -0500 Subject: [PATCH 2142/2556] arm: allow XZ zImage compression --- .gitignore | 1 + arch/arm/Kconfig | 1 + arch/arm/boot/compressed/Makefile | 11 ++++- arch/arm/boot/compressed/ashldi3.S | 53 +++++++++++++++++++++++++ arch/arm/boot/compressed/decompress.c | 4 ++ arch/arm/boot/compressed/piggy.xzkern.S | 6 +++ lib/xz/xz_dec_stream.c | 1 + 7 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 arch/arm/boot/compressed/ashldi3.S create mode 100644 arch/arm/boot/compressed/piggy.xzkern.S diff --git a/.gitignore b/.gitignore index 5d56a3fd0de6b..569cf7f154fbb 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ modules.builtin *.bz2 *.lzma *.xz +*.xzkern *.lzo *.patch *.gcno diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 1d26df86a6059..8400724f4377c 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -20,6 +20,7 @@ config ARM select HAVE_KERNEL_GZIP select HAVE_KERNEL_LZO select HAVE_KERNEL_LZMA + select HAVE_KERNEL_XZ select HAVE_IRQ_WORK select HAVE_PERF_EVENTS select PERF_USE_VMALLOC diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index e1bea82284ae5..2bf7a65518b6a 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -67,13 +67,14 @@ SEDFLAGS = s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/ suffix_$(CONFIG_KERNEL_GZIP) = gzip suffix_$(CONFIG_KERNEL_LZO) = lzo suffix_$(CONFIG_KERNEL_LZMA) = lzma +suffix_$(CONFIG_KERNEL_XZ) = xzkern targets := vmlinux vmlinux.lds \ piggy.$(suffix_y) piggy.$(suffix_y).o \ font.o font.c head.o misc.o $(OBJS) # Make sure files are removed during clean -extra-y += piggy.gzip piggy.lzo piggy.lzma lib1funcs.S +extra-y += piggy.gzip piggy.lzo piggy.lzma piggy.xzkern lib1funcs.S ashldi3.S ifeq ($(CONFIG_FUNCTION_TRACER),y) ORIG_CFLAGS := $(KBUILD_CFLAGS) @@ -105,8 +106,14 @@ lib1funcs = $(obj)/lib1funcs.o $(obj)/lib1funcs.S: $(srctree)/arch/$(SRCARCH)/lib/lib1funcs.S FORCE $(call cmd,shipped) +# For __aeabi_llsl +ashldi3 = $(obj)/ashldi3.o + +$(obj)/ashldi3.S: $(srctree)/arch/$(SRCARCH)/lib/ashldi3.S FORCE + $(call cmd,shipped) + $(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.$(suffix_y).o \ - $(addprefix $(obj)/, $(OBJS)) $(lib1funcs) FORCE + $(addprefix $(obj)/, $(OBJS)) $(lib1funcs) $(ashldi3) FORCE $(call if_changed,ld) @: diff --git a/arch/arm/boot/compressed/ashldi3.S b/arch/arm/boot/compressed/ashldi3.S new file mode 100644 index 0000000000000..638deb13da1c3 --- /dev/null +++ b/arch/arm/boot/compressed/ashldi3.S @@ -0,0 +1,53 @@ +/* Copyright 1995, 1996, 1998, 1999, 2000, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file 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, or (at your option) any +later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +This file 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; see the file COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + + +#include + +#ifdef __ARMEB__ +#define al r1 +#define ah r0 +#else +#define al r0 +#define ah r1 +#endif + +ENTRY(__ashldi3) +ENTRY(__aeabi_llsl) + + subs r3, r2, #32 + rsb ip, r2, #32 + movmi ah, ah, lsl r2 + movpl ah, al, lsl r3 + ARM( orrmi ah, ah, al, lsr ip ) + THUMB( lsrmi r3, al, ip ) + THUMB( orrmi ah, ah, r3 ) + mov al, al, lsl r2 + mov pc, lr + +ENDPROC(__ashldi3) +ENDPROC(__aeabi_llsl) diff --git a/arch/arm/boot/compressed/decompress.c b/arch/arm/boot/compressed/decompress.c index 4c72a97bc3e14..5210840076169 100644 --- a/arch/arm/boot/compressed/decompress.c +++ b/arch/arm/boot/compressed/decompress.c @@ -44,6 +44,10 @@ extern void error(char *); #include "../../../../lib/decompress_unlzma.c" #endif +#ifdef CONFIG_KERNEL_XZ +#include "../../../../lib/decompress_unxz.c" +#endif + void do_decompress(u8 *input, int len, u8 *output, void (*error)(char *x)) { decompress(input, len, NULL, NULL, output, NULL, error); diff --git a/arch/arm/boot/compressed/piggy.xzkern.S b/arch/arm/boot/compressed/piggy.xzkern.S new file mode 100644 index 0000000000000..6c83f7f598b1d --- /dev/null +++ b/arch/arm/boot/compressed/piggy.xzkern.S @@ -0,0 +1,6 @@ + .section .piggydata,#alloc + .globl input_data +input_data: + .incbin "arch/arm/boot/compressed/piggy.xzkern" + .globl input_data_end +input_data_end: diff --git a/lib/xz/xz_dec_stream.c b/lib/xz/xz_dec_stream.c index ac809b1e64f78..9a60cc21964ff 100644 --- a/lib/xz/xz_dec_stream.c +++ b/lib/xz/xz_dec_stream.c @@ -9,6 +9,7 @@ #include "xz_private.h" #include "xz_stream.h" +#include /* Hash used to validate the Index field */ struct xz_dec_hash { From f58b2be4429c987fcfd785ec227f70a9fb487ae0 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 25 Mar 2012 21:21:12 -0500 Subject: [PATCH 2143/2556] configs: cayniarb: enable KERNEL_XZ also remove LOCAL_VERSION_AUTO (its too long) --- arch/arm/configs/cayniarb_bravo_defconfig | 12 +++++++++--- arch/arm/configs/cayniarb_mahimahi_defconfig | 12 +++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/arch/arm/configs/cayniarb_bravo_defconfig b/arch/arm/configs/cayniarb_bravo_defconfig index d0966cf6e0ae9..dbe07e0d17edd 100644 --- a/arch/arm/configs/cayniarb_bravo_defconfig +++ b/arch/arm/configs/cayniarb_bravo_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sun Feb 19 17:03:08 2012 +# Sun Mar 25 18:22:22 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -34,12 +34,14 @@ CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" CONFIG_LOCALVERSION="-cayniarb-ics-stock" -CONFIG_LOCALVERSION_AUTO=y +# CONFIG_LOCALVERSION_AUTO is not set CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y CONFIG_HAVE_KERNEL_LZO=y # CONFIG_KERNEL_GZIP is not set -CONFIG_KERNEL_LZMA=y +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y # CONFIG_KERNEL_LZO is not set CONFIG_SWAP=y # CONFIG_SYSVIPC is not set @@ -1729,7 +1731,9 @@ CONFIG_ANDROID_TIMED_GPIO=y CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_POHMELFS is not set # CONFIG_IIO is not set +CONFIG_XVMALLOC=y CONFIG_ZRAM=y +# CONFIG_ZRAM_DEBUG is not set # CONFIG_FB_SM7XX is not set # @@ -2110,6 +2114,8 @@ CONFIG_CRC32=y CONFIG_LIBCRC32C=y CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y # CONFIG_XZ_DEC is not set # CONFIG_XZ_DEC_BCJ is not set CONFIG_DECOMPRESS_GZIP=y diff --git a/arch/arm/configs/cayniarb_mahimahi_defconfig b/arch/arm/configs/cayniarb_mahimahi_defconfig index eceab5cb55ff4..17a9ed96ad928 100644 --- a/arch/arm/configs/cayniarb_mahimahi_defconfig +++ b/arch/arm/configs/cayniarb_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sun Feb 19 17:02:24 2012 +# Sun Mar 25 18:09:45 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -34,12 +34,14 @@ CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" CONFIG_LOCALVERSION="-cayniarb-ics-stock" -CONFIG_LOCALVERSION_AUTO=y +# CONFIG_LOCALVERSION_AUTO is not set CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y CONFIG_HAVE_KERNEL_LZO=y # CONFIG_KERNEL_GZIP is not set -CONFIG_KERNEL_LZMA=y +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y # CONFIG_KERNEL_LZO is not set CONFIG_SWAP=y # CONFIG_SYSVIPC is not set @@ -1726,7 +1728,9 @@ CONFIG_ANDROID_TIMED_GPIO=y CONFIG_ANDROID_LOW_MEMORY_KILLER=y # CONFIG_POHMELFS is not set # CONFIG_IIO is not set +CONFIG_XVMALLOC=y CONFIG_ZRAM=y +# CONFIG_ZRAM_DEBUG is not set # CONFIG_FB_SM7XX is not set # @@ -2109,6 +2113,8 @@ CONFIG_CRC32=y CONFIG_LIBCRC32C=y CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y # CONFIG_XZ_DEC is not set # CONFIG_XZ_DEC_BCJ is not set CONFIG_DECOMPRESS_GZIP=y From 061f522ec41519edc414cb12d733e68ec1f4c907 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Mon, 16 Apr 2012 02:36:30 -0500 Subject: [PATCH 2144/2556] Remove 'SHIPPED' library ashlidi3.S added with the xz commit --- arch/arm/boot/compressed/ashldi3.S | 53 ------------------------------ 1 file changed, 53 deletions(-) delete mode 100644 arch/arm/boot/compressed/ashldi3.S diff --git a/arch/arm/boot/compressed/ashldi3.S b/arch/arm/boot/compressed/ashldi3.S deleted file mode 100644 index 638deb13da1c3..0000000000000 --- a/arch/arm/boot/compressed/ashldi3.S +++ /dev/null @@ -1,53 +0,0 @@ -/* Copyright 1995, 1996, 1998, 1999, 2000, 2003, 2004, 2005 - Free Software Foundation, Inc. - -This file 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, or (at your option) any -later version. - -In addition to the permissions in the GNU General Public License, the -Free Software Foundation gives you unlimited permission to link the -compiled version of this file into combinations with other programs, -and to distribute those combinations without any restriction coming -from the use of this file. (The General Public License restrictions -do apply in other respects; for example, they cover modification of -the file, and distribution when not linked into a combine -executable.) - -This file 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; see the file COPYING. If not, write to -the Free Software Foundation, 51 Franklin Street, Fifth Floor, -Boston, MA 02110-1301, USA. */ - - -#include - -#ifdef __ARMEB__ -#define al r1 -#define ah r0 -#else -#define al r0 -#define ah r1 -#endif - -ENTRY(__ashldi3) -ENTRY(__aeabi_llsl) - - subs r3, r2, #32 - rsb ip, r2, #32 - movmi ah, ah, lsl r2 - movpl ah, al, lsl r3 - ARM( orrmi ah, ah, al, lsr ip ) - THUMB( lsrmi r3, al, ip ) - THUMB( orrmi ah, ah, r3 ) - mov al, al, lsl r2 - mov pc, lr - -ENDPROC(__ashldi3) -ENDPROC(__aeabi_llsl) From 8776119635f2e537cbf9dc3bde6177f4808d9cd4 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Mon, 16 Apr 2012 02:41:17 -0500 Subject: [PATCH 2145/2556] ignore ashldi3.S --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 569cf7f154fbb..3244d10e5a118 100644 --- a/.gitignore +++ b/.gitignore @@ -79,3 +79,7 @@ GTAGS *.orig *~ \#*# + +# Special rule +# XXX: there's got to be a more generic way to do this +arch/arm/boot/compressed/ashldi3.S From 0e7b176d663a58196465c2446f165e973f479b2d Mon Sep 17 00:00:00 2001 From: Arne Coucheron Date: Mon, 6 Jun 2011 00:07:07 +0200 Subject: [PATCH 2146/2556] lib: Introduce some memory copy macros and functions The kernel's memcpy and memmove is very inefficient. But the glibc version is quite fast, in some cases it is 10 times faster than the kernel version. So I introduce some memory copy macros and functions of the glibc to improve the kernel version's performance. The strategy of the memory functions is: 1. Copy bytes until the destination pointer is aligned. 2. Copy words in unrolled loops. If the source and destination are not aligned in the same way, use word memory operations, but shift and merge two read words before writing. 3. Copy the few remaining bytes. Signed-off-by: Miao Xie Conflicts: lib/Makefile --- include/linux/memcopy.h | 226 ++++++++++++++++++++++ lib/Makefile | 2 +- lib/memcopy.c | 403 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 630 insertions(+), 1 deletion(-) create mode 100644 include/linux/memcopy.h create mode 100644 lib/memcopy.c diff --git a/include/linux/memcopy.h b/include/linux/memcopy.h new file mode 100644 index 0000000000000..18bc86091dd3d --- /dev/null +++ b/include/linux/memcopy.h @@ -0,0 +1,226 @@ +/* + * memcopy.h -- definitions for memory copy functions. Generic C version. + * + * 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * The code is derived from the GNU C Library. + * Copyright (C) 1991, 1992, 1993, 1997, 2004 Free Software Foundation, Inc. + */ +#ifndef _LINUX_MEMCOPY_H_ +#define _LINUX_MEMCOPY_H_ + +/* + * The strategy of the memory functions is: + * + * 1. Copy bytes until the destination pointer is aligned. + * + * 2. Copy words in unrolled loops. If the source and destination + * are not aligned in the same way, use word memory operations, + * but shift and merge two read words before writing. + * + * 3. Copy the few remaining bytes. + * + * This is fast on processors that have at least 10 registers for + * allocation by GCC, and that can access memory at reg+const in one + * instruction. + */ + +#include +#include +#include + +/* + * The macros defined in this file are: + * + * BYTE_COPY_FWD(dst_beg_ptr, src_beg_ptr, nbytes_to_copy) + * + * BYTE_COPY_BWD(dst_end_ptr, src_end_ptr, nbytes_to_copy) + * + * WORD_COPY_FWD(dst_beg_ptr, src_beg_ptr, nbytes_remaining, nbytes_to_copy) + * + * WORD_COPY_BWD(dst_end_ptr, src_end_ptr, nbytes_remaining, nbytes_to_copy) + * + * MERGE(old_word, sh_1, new_word, sh_2) + * + * MEM_COPY_FWD(dst_beg_ptr, src_beg_ptr, nbytes_to_copy) + * + * MEM_COPY_BWD(dst_end_ptr, src_end_ptr, nbytes_to_copy) + */ + +#define OP_T_THRESHOLD 16 + +/* + * Type to use for aligned memory operations. + * This should normally be the biggest type supported by a single load + * and store. + */ +#define op_t unsigned long int +#define OPSIZ (sizeof(op_t)) + +/* Type to use for unaligned operations. */ +typedef unsigned char byte; + +#ifndef MERGE +# ifdef __LITTLE_ENDIAN +# define MERGE(w0, sh_1, w1, sh_2) (((w0) >> (sh_1)) | ((w1) << (sh_2))) +# elif defined(__BIG_ENDIAN) +# define MERGE(w0, sh_1, w1, sh_2) (((w0) << (sh_1)) | ((w1) >> (sh_2))) +# else +# error "Macro MERGE() hasn't defined!" +# endif +#endif + +/* + * Copy exactly NBYTES bytes from SRC_BP to DST_BP, + * without any assumptions about alignment of the pointers. + */ +#ifndef BYTE_COPY_FWD +#define BYTE_COPY_FWD(dst_bp, src_bp, nbytes) \ +do { \ + size_t __nbytes = (nbytes); \ + while (__nbytes > 0) { \ + byte __x = ((byte *) src_bp)[0]; \ + src_bp += 1; \ + __nbytes -= 1; \ + ((byte *) dst_bp)[0] = __x; \ + dst_bp += 1; \ + } \ +} while (0) +#endif + +/* + * Copy exactly NBYTES_TO_COPY bytes from SRC_END_PTR to DST_END_PTR, + * beginning at the bytes right before the pointers and continuing towards + * smaller addresses. Don't assume anything about alignment of the + * pointers. + */ +#ifndef BYTE_COPY_BWD +#define BYTE_COPY_BWD(dst_ep, src_ep, nbytes) \ +do { \ + size_t __nbytes = (nbytes); \ + while (__nbytes > 0) { \ + byte __x; \ + src_ep -= 1; \ + __x = ((byte *) src_ep)[0]; \ + dst_ep -= 1; \ + __nbytes -= 1; \ + ((byte *) dst_ep)[0] = __x; \ + } \ +} while (0) +#endif +/* + * Copy *up to* NBYTES bytes from SRC_BP to DST_BP, with + * the assumption that DST_BP is aligned on an OPSIZ multiple. If + * not all bytes could be easily copied, store remaining number of bytes + * in NBYTES_LEFT, otherwise store 0. + */ +extern void _wordcopy_fwd_aligned(long int, long int, size_t); +extern void _wordcopy_fwd_dest_aligned(long int, long int, size_t); +#ifndef WORD_COPY_FWD +#define WORD_COPY_FWD(dst_bp, src_bp, nbytes_left, nbytes) \ +do { \ + if (src_bp % OPSIZ == 0) \ + _wordcopy_fwd_aligned (dst_bp, src_bp, (nbytes) / OPSIZ); \ + else \ + _wordcopy_fwd_dest_aligned (dst_bp, src_bp, (nbytes) / OPSIZ);\ + \ + src_bp += (nbytes) & -OPSIZ; \ + dst_bp += (nbytes) & -OPSIZ; \ + (nbytes_left) = (nbytes) % OPSIZ; \ +} while (0) +#endif + +/* + * Copy *up to* NBYTES_TO_COPY bytes from SRC_END_PTR to DST_END_PTR, + * beginning at the words (of type op_t) right before the pointers and + * continuing towards smaller addresses. May take advantage of that + * DST_END_PTR is aligned on an OPSIZ multiple. If not all bytes could be + * easily copied, store remaining number of bytes in NBYTES_REMAINING, + * otherwise store 0. + */ +extern void _wordcopy_bwd_aligned(long int, long int, size_t); +extern void _wordcopy_bwd_dest_aligned(long int, long int, size_t); +#ifndef WORD_COPY_BWD +#define WORD_COPY_BWD(dst_ep, src_ep, nbytes_left, nbytes) \ +do { \ + if (src_ep % OPSIZ == 0) \ + _wordcopy_bwd_aligned (dst_ep, src_ep, (nbytes) / OPSIZ); \ + else \ + _wordcopy_bwd_dest_aligned (dst_ep, src_ep, (nbytes) / OPSIZ);\ + \ + src_ep -= (nbytes) & -OPSIZ; \ + dst_ep -= (nbytes) & -OPSIZ; \ + (nbytes_left) = (nbytes) % OPSIZ; \ +} while (0) +#endif + +/* Copy memory from the beginning to the end */ +#ifndef MEM_COPY_FWD +static __always_inline void mem_copy_fwd(unsigned long dstp, + unsigned long srcp, + size_t count) +{ + /* If there not too few bytes to copy, use word copy. */ + if (count >= OP_T_THRESHOLD) { + /* Copy just a few bytes to make dstp aligned. */ + count -= (-dstp) % OPSIZ; + BYTE_COPY_FWD(dstp, srcp, (-dstp) % OPSIZ); + + /* + * Copy from srcp to dstp taking advantage of the known + * alignment of dstp. Number if bytes remaining is put in + * the third argument. + */ + WORD_COPY_FWD(dstp, srcp, count, count); + + /* Fall out and copy the tail. */ + } + + /* There are just a few bytes to copy. Use byte memory operations. */ + BYTE_COPY_FWD(dstp, srcp, count); +} +#endif + +/* Copy memory from the end to the beginning. */ +#ifndef MEM_COPY_BWD +static __always_inline void mem_copy_bwd(unsigned long dstp, + unsigned long srcp, + size_t count) +{ + srcp += count; + dstp += count; + + /* If there not too few bytes to copy, use word copy. */ + if (count >= OP_T_THRESHOLD) { + /* Copy just a few bytes to make dstp aligned. */ + count -= dstp % OPSIZ; + BYTE_COPY_BWD(dstp, srcp, dstp % OPSIZ); + + /* + * Copy from srcp to dstp taking advantage of the known + * alignment of dstp. Number if bytes remaining is put in + * the third argument. + */ + WORD_COPY_BWD(dstp, srcp, count, count); + + /* Fall out and copy the tail. */ + } + + /* There are just a few bytes to copy. Use byte memory operations. */ + BYTE_COPY_BWD (dstp, srcp, count); +} +#endif + +#endif diff --git a/lib/Makefile b/lib/Makefile index 2990d70ebe872..f2bf6e6f7a049 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -13,7 +13,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \ sha1.o irq_regs.o reciprocal_div.o argv_split.o \ proportions.o prio_heap.o ratelimit.o show_mem.o \ is_single_threaded.o plist.o decompress.o \ - memory_alloc.o + memory_alloc.o memcopy.o lib-$(CONFIG_MMU) += ioremap.o lib-$(CONFIG_SMP) += cpumask.o diff --git a/lib/memcopy.c b/lib/memcopy.c new file mode 100644 index 0000000000000..92c300faaf713 --- /dev/null +++ b/lib/memcopy.c @@ -0,0 +1,403 @@ +/* + * memcopy.c -- subroutines for memory copy functions. + * + * 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * The code is derived from the GNU C Library. + * Copyright (C) 1991, 1992, 1993, 1997, 2004 Free Software Foundation, Inc. + */ + +/* BE VERY CAREFUL IF YOU CHANGE THIS CODE...! */ + +#include + +/* + * _wordcopy_fwd_aligned -- Copy block beginning at SRCP to block beginning + * at DSTP with LEN `op_t' words (not LEN bytes!). + * Both SRCP and DSTP should be aligned for memory operations on `op_t's. + */ +void _wordcopy_fwd_aligned (long int dstp, long int srcp, size_t len) +{ + op_t a0, a1; + + switch (len % 8) { + case 2: + a0 = ((op_t *) srcp)[0]; + srcp -= 6 * OPSIZ; + dstp -= 7 * OPSIZ; + len += 6; + goto do1; + case 3: + a1 = ((op_t *) srcp)[0]; + srcp -= 5 * OPSIZ; + dstp -= 6 * OPSIZ; + len += 5; + goto do2; + case 4: + a0 = ((op_t *) srcp)[0]; + srcp -= 4 * OPSIZ; + dstp -= 5 * OPSIZ; + len += 4; + goto do3; + case 5: + a1 = ((op_t *) srcp)[0]; + srcp -= 3 * OPSIZ; + dstp -= 4 * OPSIZ; + len += 3; + goto do4; + case 6: + a0 = ((op_t *) srcp)[0]; + srcp -= 2 * OPSIZ; + dstp -= 3 * OPSIZ; + len += 2; + goto do5; + case 7: + a1 = ((op_t *) srcp)[0]; + srcp -= 1 * OPSIZ; + dstp -= 2 * OPSIZ; + len += 1; + goto do6; + case 0: + if (OP_T_THRESHOLD <= 3 * OPSIZ && len == 0) + return; + a0 = ((op_t *) srcp)[0]; + srcp -= 0 * OPSIZ; + dstp -= 1 * OPSIZ; + goto do7; + case 1: + a1 = ((op_t *) srcp)[0]; + srcp -=-1 * OPSIZ; + dstp -= 0 * OPSIZ; + len -= 1; + if (OP_T_THRESHOLD <= 3 * OPSIZ && len == 0) + goto do0; + goto do8; /* No-op. */ + } + + do { +do8: + a0 = ((op_t *) srcp)[0]; + ((op_t *) dstp)[0] = a1; +do7: + a1 = ((op_t *) srcp)[1]; + ((op_t *) dstp)[1] = a0; +do6: + a0 = ((op_t *) srcp)[2]; + ((op_t *) dstp)[2] = a1; +do5: + a1 = ((op_t *) srcp)[3]; + ((op_t *) dstp)[3] = a0; +do4: + a0 = ((op_t *) srcp)[4]; + ((op_t *) dstp)[4] = a1; +do3: + a1 = ((op_t *) srcp)[5]; + ((op_t *) dstp)[5] = a0; +do2: + a0 = ((op_t *) srcp)[6]; + ((op_t *) dstp)[6] = a1; +do1: + a1 = ((op_t *) srcp)[7]; + ((op_t *) dstp)[7] = a0; + + srcp += 8 * OPSIZ; + dstp += 8 * OPSIZ; + len -= 8; + } while (len != 0); + + /* + * This is the right position for do0. Please don't move it into + * the loop. + */ +do0: + ((op_t *) dstp)[0] = a1; +} + +/* + * _wordcopy_fwd_dest_aligned -- Copy block beginning at SRCP to block + * beginning at DSTP with LEN `op_t' words (not LEN bytes!). DSTP should + * be aligned for memory operations on `op_t's, but SRCP must *not* be aligned. + */ + +void _wordcopy_fwd_dest_aligned (long int dstp, long int srcp, size_t len) +{ + op_t a0, a1, a2, a3; + int sh_1, sh_2; + + /* + * Calculate how to shift a word read at the memory operation aligned + * srcp to make it aligned for copy. + */ + sh_1 = 8 * (srcp % OPSIZ); + sh_2 = 8 * OPSIZ - sh_1; + + /* + * Make SRCP aligned by rounding it down to the beginning of the `op_t' + * it points in the middle of. + */ + srcp &= -OPSIZ; + + switch (len % 4) { + case 2: + a1 = ((op_t *) srcp)[0]; + a2 = ((op_t *) srcp)[1]; + srcp -= 1 * OPSIZ; + dstp -= 3 * OPSIZ; + len += 2; + goto do1; + case 3: + a0 = ((op_t *) srcp)[0]; + a1 = ((op_t *) srcp)[1]; + srcp -= 0 * OPSIZ; + dstp -= 2 * OPSIZ; + len += 1; + goto do2; + case 0: + if (OP_T_THRESHOLD <= 3 * OPSIZ && len == 0) + return; + a3 = ((op_t *) srcp)[0]; + a0 = ((op_t *) srcp)[1]; + srcp -=-1 * OPSIZ; + dstp -= 1 * OPSIZ; + len += 0; + goto do3; + case 1: + a2 = ((op_t *) srcp)[0]; + a3 = ((op_t *) srcp)[1]; + srcp -=-2 * OPSIZ; + dstp -= 0 * OPSIZ; + len -= 1; + if (OP_T_THRESHOLD <= 3 * OPSIZ && len == 0) + goto do0; + goto do4; /* No-op. */ + } + + do { +do4: + a0 = ((op_t *) srcp)[0]; + ((op_t *) dstp)[0] = MERGE (a2, sh_1, a3, sh_2); +do3: + a1 = ((op_t *) srcp)[1]; + ((op_t *) dstp)[1] = MERGE (a3, sh_1, a0, sh_2); +do2: + a2 = ((op_t *) srcp)[2]; + ((op_t *) dstp)[2] = MERGE (a0, sh_1, a1, sh_2); +do1: + a3 = ((op_t *) srcp)[3]; + ((op_t *) dstp)[3] = MERGE (a1, sh_1, a2, sh_2); + + srcp += 4 * OPSIZ; + dstp += 4 * OPSIZ; + len -= 4; + } while (len != 0); + + /* + * This is the right position for do0. Please don't move it into + * the loop. + */ +do0: + ((op_t *) dstp)[0] = MERGE (a2, sh_1, a3, sh_2); +} + +/* + * _wordcopy_bwd_aligned -- Copy block finishing right before + * SRCP to block finishing right before DSTP with LEN `op_t' words (not LEN + * bytes!). Both SRCP and DSTP should be aligned for memory operations + * on `op_t's. + */ +void _wordcopy_bwd_aligned (long int dstp, long int srcp, size_t len) +{ + op_t a0, a1; + + switch (len % 8) { + case 2: + srcp -= 2 * OPSIZ; + dstp -= 1 * OPSIZ; + a0 = ((op_t *) srcp)[1]; + len += 6; + goto do1; + case 3: + srcp -= 3 * OPSIZ; + dstp -= 2 * OPSIZ; + a1 = ((op_t *) srcp)[2]; + len += 5; + goto do2; + case 4: + srcp -= 4 * OPSIZ; + dstp -= 3 * OPSIZ; + a0 = ((op_t *) srcp)[3]; + len += 4; + goto do3; + case 5: + srcp -= 5 * OPSIZ; + dstp -= 4 * OPSIZ; + a1 = ((op_t *) srcp)[4]; + len += 3; + goto do4; + case 6: + srcp -= 6 * OPSIZ; + dstp -= 5 * OPSIZ; + a0 = ((op_t *) srcp)[5]; + len += 2; + goto do5; + case 7: + srcp -= 7 * OPSIZ; + dstp -= 6 * OPSIZ; + a1 = ((op_t *) srcp)[6]; + len += 1; + goto do6; + case 0: + if (OP_T_THRESHOLD <= 3 * OPSIZ && len == 0) + return; + srcp -= 8 * OPSIZ; + dstp -= 7 * OPSIZ; + a0 = ((op_t *) srcp)[7]; + goto do7; + case 1: + srcp -= 9 * OPSIZ; + dstp -= 8 * OPSIZ; + a1 = ((op_t *) srcp)[8]; + len -= 1; + if (OP_T_THRESHOLD <= 3 * OPSIZ && len == 0) + goto do0; + goto do8; /* No-op. */ + } + + do { +do8: + a0 = ((op_t *) srcp)[7]; + ((op_t *) dstp)[7] = a1; +do7: + a1 = ((op_t *) srcp)[6]; + ((op_t *) dstp)[6] = a0; +do6: + a0 = ((op_t *) srcp)[5]; + ((op_t *) dstp)[5] = a1; +do5: + a1 = ((op_t *) srcp)[4]; + ((op_t *) dstp)[4] = a0; +do4: + a0 = ((op_t *) srcp)[3]; + ((op_t *) dstp)[3] = a1; +do3: + a1 = ((op_t *) srcp)[2]; + ((op_t *) dstp)[2] = a0; +do2: + a0 = ((op_t *) srcp)[1]; + ((op_t *) dstp)[1] = a1; +do1: + a1 = ((op_t *) srcp)[0]; + ((op_t *) dstp)[0] = a0; + + srcp -= 8 * OPSIZ; + dstp -= 8 * OPSIZ; + len -= 8; + } while (len != 0); + + /* + * This is the right position for do0. Please don't move it into + * the loop. + */ +do0: + ((op_t *) dstp)[7] = a1; +} + +/* + * _wordcopy_bwd_dest_aligned -- Copy block finishing right before SRCP to + * block finishing right before DSTP with LEN `op_t' words (not LEN bytes!). + * DSTP should be aligned for memory operations on `op_t', but SRCP must *not* + * be aligned. + */ +void _wordcopy_bwd_dest_aligned (long int dstp, long int srcp, size_t len) +{ + op_t a0, a1, a2, a3; + int sh_1, sh_2; + + /* + * Calculate how to shift a word read at the memory operation aligned + * srcp to make it aligned for copy. + */ + + sh_1 = 8 * (srcp % OPSIZ); + sh_2 = 8 * OPSIZ - sh_1; + + /* + * Make srcp aligned by rounding it down to the beginning of the op_t + * it points in the middle of. + */ + srcp &= -OPSIZ; + srcp += OPSIZ; + + switch (len % 4) { + case 2: + srcp -= 3 * OPSIZ; + dstp -= 1 * OPSIZ; + a2 = ((op_t *) srcp)[2]; + a1 = ((op_t *) srcp)[1]; + len += 2; + goto do1; + case 3: + srcp -= 4 * OPSIZ; + dstp -= 2 * OPSIZ; + a3 = ((op_t *) srcp)[3]; + a2 = ((op_t *) srcp)[2]; + len += 1; + goto do2; + case 0: + if (OP_T_THRESHOLD <= 3 * OPSIZ && len == 0) + return; + srcp -= 5 * OPSIZ; + dstp -= 3 * OPSIZ; + a0 = ((op_t *) srcp)[4]; + a3 = ((op_t *) srcp)[3]; + goto do3; + case 1: + srcp -= 6 * OPSIZ; + dstp -= 4 * OPSIZ; + a1 = ((op_t *) srcp)[5]; + a0 = ((op_t *) srcp)[4]; + len -= 1; + if (OP_T_THRESHOLD <= 3 * OPSIZ && len == 0) + goto do0; + goto do4; /* No-op. */ + } + + do { +do4: + a3 = ((op_t *) srcp)[3]; + ((op_t *) dstp)[3] = MERGE (a0, sh_1, a1, sh_2); +do3: + a2 = ((op_t *) srcp)[2]; + ((op_t *) dstp)[2] = MERGE (a3, sh_1, a0, sh_2); +do2: + a1 = ((op_t *) srcp)[1]; + ((op_t *) dstp)[1] = MERGE (a2, sh_1, a3, sh_2); +do1: + a0 = ((op_t *) srcp)[0]; + ((op_t *) dstp)[0] = MERGE (a1, sh_1, a2, sh_2); + + srcp -= 4 * OPSIZ; + dstp -= 4 * OPSIZ; + len -= 4; + } while (len != 0); + + /* + * This is the right position for do0. Please don't move it into + * the loop. + */ +do0: + ((op_t *) dstp)[3] = MERGE (a0, sh_1, a1, sh_2); +} + From 4ce1bfbb2dac6de7f802b2ca7e9097c216d2118b Mon Sep 17 00:00:00 2001 From: Arne Coucheron Date: Mon, 6 Jun 2011 00:11:31 +0200 Subject: [PATCH 2147/2556] lib: Improve the performance of memcpy and memmove of the general version The performance of memcpy and memmove of the general version is very inefficient, this patch improves them. --- lib/string.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/lib/string.c b/lib/string.c index f71bead1be3ef..6cbf6d834be12 100644 --- a/lib/string.c +++ b/lib/string.c @@ -23,6 +23,7 @@ #include #include #include +#include #ifndef __HAVE_ARCH_STRNICMP /** @@ -567,11 +568,12 @@ EXPORT_SYMBOL(memset); */ void *memcpy(void *dest, const void *src, size_t count) { - char *tmp = dest; - const char *s = src; + unsigned long dstp = (unsigned long)dest; + unsigned long srcp = (unsigned long)src; + + /* Copy from the beginning to the end */ + mem_copy_fwd(dstp, srcp, count); - while (count--) - *tmp++ = *s++; return dest; } EXPORT_SYMBOL(memcpy); @@ -588,21 +590,15 @@ EXPORT_SYMBOL(memcpy); */ void *memmove(void *dest, const void *src, size_t count) { - char *tmp; - const char *s; - - if (dest <= src) { - tmp = dest; - s = src; - while (count--) - *tmp++ = *s++; + unsigned long dstp = (unsigned long)dest; + unsigned long srcp = (unsigned long)src; + + if (dest - src >= count) { + /* Copy from the beginning to the end */ + mem_copy_fwd(dstp, srcp, count); } else { - tmp = dest; - tmp += count; - s = src; - s += count; - while (count--) - *--tmp = *--s; + /* Copy from the end to the beginning */ + mem_copy_bwd(dstp, srcp, count); } return dest; } From 91840151fb288aee3f19c588d671d1e9c9b54a0a Mon Sep 17 00:00:00 2001 From: Mandeep Singh Baines Date: Fri, 19 Aug 2011 02:28:27 +0200 Subject: [PATCH 2148/2556] lib/sha1: use the git implementation of SHA-1 For ChromiumOS, we use SHA-1 to verify the integrity of the root filesystem. The speed of the kernel sha-1 implementation has a major impact on our boot performance. To improve boot performance, we investigated using the heavily optimized sha-1 implementation used in git. With the git sha-1 implementation, we see a 11.7% improvement in boot time. 10 reboots, remove slowest/fastest. Before: Mean: 6.58 seconds Stdev: 0.14 After (with git sha-1, this patch): Mean: 5.89 seconds Stdev: 0.07 The other cool thing about the git SHA-1 implementation is that it only needs 64 bytes of stack for the workspace while the original kernel implementation needed 320 bytes. Signed-off-by: Mandeep Singh Baines Cc: Ramsay Jones Cc: Nicolas Pitre Cc: Herbert Xu Cc: David S. Miller Cc: linux-crypto@vger.kernel.org Signed-off-by: Linus Torvalds --- include/linux/cryptohash.h | 2 +- lib/sha1.c | 212 +++++++++++++++++++++++++++---------- 2 files changed, 159 insertions(+), 55 deletions(-) diff --git a/include/linux/cryptohash.h b/include/linux/cryptohash.h index ec78a4bbe1d5b..f9452185b0190 100644 --- a/include/linux/cryptohash.h +++ b/include/linux/cryptohash.h @@ -3,7 +3,7 @@ #define SHA_DIGEST_WORDS 5 #define SHA_MESSAGE_BYTES (512 /*bits*/ / 8) -#define SHA_WORKSPACE_WORDS 80 +#define SHA_WORKSPACE_WORDS 16 void sha_init(__u32 *buf); void sha_transform(__u32 *digest, const char *data, __u32 *W); diff --git a/lib/sha1.c b/lib/sha1.c index 4c45fd50e9136..f33271dd00cbc 100644 --- a/lib/sha1.c +++ b/lib/sha1.c @@ -1,31 +1,72 @@ /* - * SHA transform algorithm, originally taken from code written by - * Peter Gutmann, and placed in the public domain. + * SHA1 routine optimized to do word accesses rather than byte accesses, + * and to avoid unnecessary copies into the context array. + * + * This was based on the git SHA1 implementation. */ #include #include -#include +#include +#include -/* The SHA f()-functions. */ +/* + * If you have 32 registers or more, the compiler can (and should) + * try to change the array[] accesses into registers. However, on + * machines with less than ~25 registers, that won't really work, + * and at least gcc will make an unholy mess of it. + * + * So to avoid that mess which just slows things down, we force + * the stores to memory to actually happen (we might be better off + * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as + * suggested by Artur Skawina - that will also make gcc unable to + * try to do the silly "optimize away loads" part because it won't + * see what the value will be). + * + * Ben Herrenschmidt reports that on PPC, the C version comes close + * to the optimized asm with this (ie on PPC you don't want that + * 'volatile', since there are lots of registers). + * + * On ARM we get the best code generation by forcing a full memory barrier + * between each SHA_ROUND, otherwise gcc happily get wild with spilling and + * the stack frame size simply explode and performance goes down the drain. + */ -#define f1(x,y,z) (z ^ (x & (y ^ z))) /* x ? y : z */ -#define f2(x,y,z) (x ^ y ^ z) /* XOR */ -#define f3(x,y,z) ((x & y) + (z & (x ^ y))) /* majority */ +#ifdef CONFIG_X86 + #define setW(x, val) (*(volatile __u32 *)&W(x) = (val)) +#elif defined(CONFIG_ARM) + #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0) +#else + #define setW(x, val) (W(x) = (val)) +#endif -/* The SHA Mysterious Constants */ +/* This "rolls" over the 512-bit array */ +#define W(x) (array[(x)&15]) -#define K1 0x5A827999L /* Rounds 0-19: sqrt(2) * 2^30 */ -#define K2 0x6ED9EBA1L /* Rounds 20-39: sqrt(3) * 2^30 */ -#define K3 0x8F1BBCDCL /* Rounds 40-59: sqrt(5) * 2^30 */ -#define K4 0xCA62C1D6L /* Rounds 60-79: sqrt(10) * 2^30 */ +/* + * Where do we get the source from? The first 16 iterations get it from + * the input data, the next mix it from the 512-bit array. + */ +#define SHA_SRC(t) get_unaligned_be32((__u32 *)data + t) +#define SHA_MIX(t) rol32(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1) + +#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \ + __u32 TEMP = input(t); setW(t, TEMP); \ + E += TEMP + rol32(A,5) + (fn) + (constant); \ + B = ror32(B, 2); } while (0) + +#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) +#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) +#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E ) +#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E ) +#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E ) /** * sha_transform - single block SHA1 transform * * @digest: 160 bit digest to update * @data: 512 bits of data to hash - * @W: 80 words of workspace (see note) + * @array: 16 words of workspace (see note) * * This function generates a SHA1 digest for a single 512-bit block. * Be warned, it does not handle padding and message digest, do not @@ -36,47 +77,111 @@ * to clear the workspace. This is left to the caller to avoid * unnecessary clears between chained hashing operations. */ -void sha_transform(__u32 *digest, const char *in, __u32 *W) +void sha_transform(__u32 *digest, const char *data, __u32 *array) { - __u32 a, b, c, d, e, t, i; - - for (i = 0; i < 16; i++) - W[i] = be32_to_cpu(((const __be32 *)in)[i]); - - for (i = 0; i < 64; i++) - W[i+16] = rol32(W[i+13] ^ W[i+8] ^ W[i+2] ^ W[i], 1); - - a = digest[0]; - b = digest[1]; - c = digest[2]; - d = digest[3]; - e = digest[4]; - - for (i = 0; i < 20; i++) { - t = f1(b, c, d) + K1 + rol32(a, 5) + e + W[i]; - e = d; d = c; c = rol32(b, 30); b = a; a = t; - } - - for (; i < 40; i ++) { - t = f2(b, c, d) + K2 + rol32(a, 5) + e + W[i]; - e = d; d = c; c = rol32(b, 30); b = a; a = t; - } - - for (; i < 60; i ++) { - t = f3(b, c, d) + K3 + rol32(a, 5) + e + W[i]; - e = d; d = c; c = rol32(b, 30); b = a; a = t; - } - - for (; i < 80; i ++) { - t = f2(b, c, d) + K4 + rol32(a, 5) + e + W[i]; - e = d; d = c; c = rol32(b, 30); b = a; a = t; - } - - digest[0] += a; - digest[1] += b; - digest[2] += c; - digest[3] += d; - digest[4] += e; + __u32 A, B, C, D, E; + + A = digest[0]; + B = digest[1]; + C = digest[2]; + D = digest[3]; + E = digest[4]; + + /* Round 1 - iterations 0-16 take their input from 'data' */ + T_0_15( 0, A, B, C, D, E); + T_0_15( 1, E, A, B, C, D); + T_0_15( 2, D, E, A, B, C); + T_0_15( 3, C, D, E, A, B); + T_0_15( 4, B, C, D, E, A); + T_0_15( 5, A, B, C, D, E); + T_0_15( 6, E, A, B, C, D); + T_0_15( 7, D, E, A, B, C); + T_0_15( 8, C, D, E, A, B); + T_0_15( 9, B, C, D, E, A); + T_0_15(10, A, B, C, D, E); + T_0_15(11, E, A, B, C, D); + T_0_15(12, D, E, A, B, C); + T_0_15(13, C, D, E, A, B); + T_0_15(14, B, C, D, E, A); + T_0_15(15, A, B, C, D, E); + + /* Round 1 - tail. Input from 512-bit mixing array */ + T_16_19(16, E, A, B, C, D); + T_16_19(17, D, E, A, B, C); + T_16_19(18, C, D, E, A, B); + T_16_19(19, B, C, D, E, A); + + /* Round 2 */ + T_20_39(20, A, B, C, D, E); + T_20_39(21, E, A, B, C, D); + T_20_39(22, D, E, A, B, C); + T_20_39(23, C, D, E, A, B); + T_20_39(24, B, C, D, E, A); + T_20_39(25, A, B, C, D, E); + T_20_39(26, E, A, B, C, D); + T_20_39(27, D, E, A, B, C); + T_20_39(28, C, D, E, A, B); + T_20_39(29, B, C, D, E, A); + T_20_39(30, A, B, C, D, E); + T_20_39(31, E, A, B, C, D); + T_20_39(32, D, E, A, B, C); + T_20_39(33, C, D, E, A, B); + T_20_39(34, B, C, D, E, A); + T_20_39(35, A, B, C, D, E); + T_20_39(36, E, A, B, C, D); + T_20_39(37, D, E, A, B, C); + T_20_39(38, C, D, E, A, B); + T_20_39(39, B, C, D, E, A); + + /* Round 3 */ + T_40_59(40, A, B, C, D, E); + T_40_59(41, E, A, B, C, D); + T_40_59(42, D, E, A, B, C); + T_40_59(43, C, D, E, A, B); + T_40_59(44, B, C, D, E, A); + T_40_59(45, A, B, C, D, E); + T_40_59(46, E, A, B, C, D); + T_40_59(47, D, E, A, B, C); + T_40_59(48, C, D, E, A, B); + T_40_59(49, B, C, D, E, A); + T_40_59(50, A, B, C, D, E); + T_40_59(51, E, A, B, C, D); + T_40_59(52, D, E, A, B, C); + T_40_59(53, C, D, E, A, B); + T_40_59(54, B, C, D, E, A); + T_40_59(55, A, B, C, D, E); + T_40_59(56, E, A, B, C, D); + T_40_59(57, D, E, A, B, C); + T_40_59(58, C, D, E, A, B); + T_40_59(59, B, C, D, E, A); + + /* Round 4 */ + T_60_79(60, A, B, C, D, E); + T_60_79(61, E, A, B, C, D); + T_60_79(62, D, E, A, B, C); + T_60_79(63, C, D, E, A, B); + T_60_79(64, B, C, D, E, A); + T_60_79(65, A, B, C, D, E); + T_60_79(66, E, A, B, C, D); + T_60_79(67, D, E, A, B, C); + T_60_79(68, C, D, E, A, B); + T_60_79(69, B, C, D, E, A); + T_60_79(70, A, B, C, D, E); + T_60_79(71, E, A, B, C, D); + T_60_79(72, D, E, A, B, C); + T_60_79(73, C, D, E, A, B); + T_60_79(74, B, C, D, E, A); + T_60_79(75, A, B, C, D, E); + T_60_79(76, E, A, B, C, D); + T_60_79(77, D, E, A, B, C); + T_60_79(78, C, D, E, A, B); + T_60_79(79, B, C, D, E, A); + + digest[0] += A; + digest[1] += B; + digest[2] += C; + digest[3] += D; + digest[4] += E; } EXPORT_SYMBOL(sha_transform); @@ -92,4 +197,3 @@ void sha_init(__u32 *buf) buf[3] = 0x10325476; buf[4] = 0xc3d2e1f0; } - From e40b5f232d0220e263e0b68d1fc8b32a34d8bd00 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 19 Aug 2011 02:31:00 +0200 Subject: [PATCH 2149/2556] arm: remove "optimized" SHA1 routines Since commit 1eb19a12bd22 ("lib/sha1: use the git implementation of SHA-1"), the ARM SHA1 routines no longer work. The reason? They depended on the larger 320-byte workspace, and now the sha1 workspace is just 16 words (64 bytes). So the assembly version would overwrite the stack randomly. The optimized asm version is also probably slower than the new improved C version, so there's no reason to keep it around. At least that was the case in git, where what appears to be the same assembly language version was removed two years ago because the optimized C BLK_SHA1 code was faster. Reported-and-tested-by: Joachim Eastwood Cc: Andreas Schwab Cc: Nicolas Pitre Signed-off-by: Linus Torvalds --- arch/arm/lib/Makefile | 2 +- arch/arm/lib/sha1.S | 211 ------------------------------------------ 2 files changed, 1 insertion(+), 212 deletions(-) delete mode 100644 arch/arm/lib/sha1.S diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index 59ff42ddf0aed..cf73a7f742ddf 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile @@ -12,7 +12,7 @@ lib-y := backtrace.o changebit.o csumipv6.o csumpartial.o \ strchr.o strrchr.o \ testchangebit.o testclearbit.o testsetbit.o \ ashldi3.o ashrdi3.o lshrdi3.o muldi3.o \ - ucmpdi2.o lib1funcs.o div64.o sha1.o \ + ucmpdi2.o lib1funcs.o div64.o \ io-readsb.o io-writesb.o io-readsl.o io-writesl.o mmu-y := clear_user.o copy_page.o getuser.o putuser.o diff --git a/arch/arm/lib/sha1.S b/arch/arm/lib/sha1.S deleted file mode 100644 index eb0edb80d7b84..0000000000000 --- a/arch/arm/lib/sha1.S +++ /dev/null @@ -1,211 +0,0 @@ -/* - * linux/arch/arm/lib/sha1.S - * - * SHA transform optimized for ARM - * - * Copyright: (C) 2005 by Nicolas Pitre - * Created: September 17, 2005 - * - * 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. - * - * The reference implementation for this code is linux/lib/sha1.c - */ - -#include - - .text - - -/* - * void sha_transform(__u32 *digest, const char *in, __u32 *W) - * - * Note: the "in" ptr may be unaligned. - */ - -ENTRY(sha_transform) - - stmfd sp!, {r4 - r8, lr} - - @ for (i = 0; i < 16; i++) - @ W[i] = be32_to_cpu(in[i]); - -#ifdef __ARMEB__ - mov r4, r0 - mov r0, r2 - mov r2, #64 - bl memcpy - mov r2, r0 - mov r0, r4 -#else - mov r3, r2 - mov lr, #16 -1: ldrb r4, [r1], #1 - ldrb r5, [r1], #1 - ldrb r6, [r1], #1 - ldrb r7, [r1], #1 - subs lr, lr, #1 - orr r5, r5, r4, lsl #8 - orr r6, r6, r5, lsl #8 - orr r7, r7, r6, lsl #8 - str r7, [r3], #4 - bne 1b -#endif - - @ for (i = 0; i < 64; i++) - @ W[i+16] = ror(W[i+13] ^ W[i+8] ^ W[i+2] ^ W[i], 31); - - sub r3, r2, #4 - mov lr, #64 -2: ldr r4, [r3, #4]! - subs lr, lr, #1 - ldr r5, [r3, #8] - ldr r6, [r3, #32] - ldr r7, [r3, #52] - eor r4, r4, r5 - eor r4, r4, r6 - eor r4, r4, r7 - mov r4, r4, ror #31 - str r4, [r3, #64] - bne 2b - - /* - * The SHA functions are: - * - * f1(B,C,D) = (D ^ (B & (C ^ D))) - * f2(B,C,D) = (B ^ C ^ D) - * f3(B,C,D) = ((B & C) | (D & (B | C))) - * - * Then the sub-blocks are processed as follows: - * - * A' = ror(A, 27) + f(B,C,D) + E + K + *W++ - * B' = A - * C' = ror(B, 2) - * D' = C - * E' = D - * - * We therefore unroll each loop 5 times to avoid register shuffling. - * Also the ror for C (and also D and E which are successivelyderived - * from it) is applied in place to cut on an additional mov insn for - * each round. - */ - - .macro sha_f1, A, B, C, D, E - ldr r3, [r2], #4 - eor ip, \C, \D - add \E, r1, \E, ror #2 - and ip, \B, ip, ror #2 - add \E, \E, \A, ror #27 - eor ip, ip, \D, ror #2 - add \E, \E, r3 - add \E, \E, ip - .endm - - .macro sha_f2, A, B, C, D, E - ldr r3, [r2], #4 - add \E, r1, \E, ror #2 - eor ip, \B, \C, ror #2 - add \E, \E, \A, ror #27 - eor ip, ip, \D, ror #2 - add \E, \E, r3 - add \E, \E, ip - .endm - - .macro sha_f3, A, B, C, D, E - ldr r3, [r2], #4 - add \E, r1, \E, ror #2 - orr ip, \B, \C, ror #2 - add \E, \E, \A, ror #27 - and ip, ip, \D, ror #2 - add \E, \E, r3 - and r3, \B, \C, ror #2 - orr ip, ip, r3 - add \E, \E, ip - .endm - - ldmia r0, {r4 - r8} - - mov lr, #4 - ldr r1, .L_sha_K + 0 - - /* adjust initial values */ - mov r6, r6, ror #30 - mov r7, r7, ror #30 - mov r8, r8, ror #30 - -3: subs lr, lr, #1 - sha_f1 r4, r5, r6, r7, r8 - sha_f1 r8, r4, r5, r6, r7 - sha_f1 r7, r8, r4, r5, r6 - sha_f1 r6, r7, r8, r4, r5 - sha_f1 r5, r6, r7, r8, r4 - bne 3b - - ldr r1, .L_sha_K + 4 - mov lr, #4 - -4: subs lr, lr, #1 - sha_f2 r4, r5, r6, r7, r8 - sha_f2 r8, r4, r5, r6, r7 - sha_f2 r7, r8, r4, r5, r6 - sha_f2 r6, r7, r8, r4, r5 - sha_f2 r5, r6, r7, r8, r4 - bne 4b - - ldr r1, .L_sha_K + 8 - mov lr, #4 - -5: subs lr, lr, #1 - sha_f3 r4, r5, r6, r7, r8 - sha_f3 r8, r4, r5, r6, r7 - sha_f3 r7, r8, r4, r5, r6 - sha_f3 r6, r7, r8, r4, r5 - sha_f3 r5, r6, r7, r8, r4 - bne 5b - - ldr r1, .L_sha_K + 12 - mov lr, #4 - -6: subs lr, lr, #1 - sha_f2 r4, r5, r6, r7, r8 - sha_f2 r8, r4, r5, r6, r7 - sha_f2 r7, r8, r4, r5, r6 - sha_f2 r6, r7, r8, r4, r5 - sha_f2 r5, r6, r7, r8, r4 - bne 6b - - ldmia r0, {r1, r2, r3, ip, lr} - add r4, r1, r4 - add r5, r2, r5 - add r6, r3, r6, ror #2 - add r7, ip, r7, ror #2 - add r8, lr, r8, ror #2 - stmia r0, {r4 - r8} - - ldmfd sp!, {r4 - r8, pc} - -ENDPROC(sha_transform) - - .align 2 -.L_sha_K: - .word 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 - - -/* - * void sha_init(__u32 *buf) - */ - - .align 2 -.L_sha_initial_digest: - .word 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 - -ENTRY(sha_init) - - str lr, [sp, #-4]! - adr r1, .L_sha_initial_digest - ldmia r1, {r1, r2, r3, ip, lr} - stmia r0, {r1, r2, r3, ip, lr} - ldr pc, [sp], #4 - -ENDPROC(sha_init) From 6942836ee16b74c4c1bc81a24921badd0d959714 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 7 Aug 2011 15:49:11 -0700 Subject: [PATCH 2150/2556] arm: remove stale export of 'sha_transform' The generic library code already exports the generic function, this was left-over from the ARM-specific version that just got removed. Signed-off-by: Linus Torvalds --- arch/arm/kernel/armksyms.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c index e5e1e5387678f..cadd32b0b5f8e 100644 --- a/arch/arm/kernel/armksyms.c +++ b/arch/arm/kernel/armksyms.c @@ -112,9 +112,6 @@ EXPORT_SYMBOL(__put_user_4); EXPORT_SYMBOL(__put_user_8); #endif - /* crypto hash */ -EXPORT_SYMBOL(sha_transform); - /* gcc lib functions */ EXPORT_SYMBOL(__ashldi3); EXPORT_SYMBOL(__ashrdi3); From 7a55c2105879394f2c64af31dfdf173d31a4a31e Mon Sep 17 00:00:00 2001 From: Axel Haslam Date: Sun, 13 Nov 2011 14:07:36 +0100 Subject: [PATCH 2151/2556] cpufreq: interactive: fix possible Division by zero. The following dump was seen sometimes while resuming, the only division by zero on this function can happen after delta_time is reassigned, since at the start of the function, there is jump that protects against values less than 1000. After that, If delta_time and delta_idle == 0, we will hit a div 0 Division by zero in kernel. Backtrace: [] (dump_backtrace+0x0/0x110) from [] (dump_stack+0x18/0x1c) r6:010f3000 r5:c113dfb0 r4:c004afb0 r3:c6ff0000 [] (dump_stack+0x0/0x1c) from [] (__div0+0x1c/0x20) [] (__div0+0x0/0x20) from [] (Ldiv0+0x8/0x10) [] (cpufreq_interactive_timer+0x0/0x2c0) from [] (run_timer_softirq+0x154/0x260) [] (run_timer_softirq+0x0/0x260) from [] (__do_softirq+0xc8/0x194) [] (__do_softirq+0x0/0x194) from [] (irq_exit+0xb4/0xb8) [] (irq_exit+0x0/0xb8) from [] (ipi_timer+0x44/0x48) r4:c004a040 r3:00000001 [] (ipi_timer+0x0/0x48) from [] (do_local_timer+0x68/0x84) r5:c004ae2c r4:c07991e8 [] (do_local_timer+0x0/0x84) from [] (__irq_svc+0x48/0xe0) --- drivers/cpufreq/cpufreq_interactive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 111d96041789d..96cb62dc1b2af 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -244,7 +244,7 @@ static void cpufreq_interactive_timer(unsigned long data) delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time, pcpu->freq_change_time); - if (delta_idle > delta_time) + if ((delta_time == 0) || (delta_idle > delta_time)) load_since_change = 0; else load_since_change = From 39bd9ca97652e81febb9eb6f7c13a6239f5b327a Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 6 Jun 2011 18:30:23 -0700 Subject: [PATCH 2152/2556] cpufreq interactive: support shared CPU scaling Change-Id: Id5267f04067bf023f6b140b4de2e88ef7287e941 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 68 ++++++++++++++++----------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 96cb62dc1b2af..1e7ff3248a288 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -576,26 +576,34 @@ static struct attribute_group interactive_attr_group = { .name = "interactive", }; -static int cpufreq_governor_interactive(struct cpufreq_policy *new_policy, +static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event) { int rc; - struct cpufreq_interactive_cpuinfo *pcpu = - &per_cpu(cpuinfo, new_policy->cpu); + unsigned int j; + struct cpufreq_interactive_cpuinfo *pcpu; + struct cpufreq_frequency_table *freq_table; switch (event) { case CPUFREQ_GOV_START: - if (!cpu_online(new_policy->cpu)) + if (!cpu_online(policy->cpu)) return -EINVAL; - pcpu->policy = new_policy; - pcpu->freq_table = cpufreq_frequency_get_table(new_policy->cpu); - pcpu->target_freq = new_policy->cur; - pcpu->freq_change_time_in_idle = - get_cpu_idle_time_us(new_policy->cpu, + freq_table = + cpufreq_frequency_get_table(policy->cpu); + + for_each_cpu(j, policy->cpus) { + pcpu = &per_cpu(cpuinfo, j); + pcpu->policy = policy; + pcpu->target_freq = policy->cur; + pcpu->freq_table = freq_table; + pcpu->freq_change_time_in_idle = + get_cpu_idle_time_us(j, &pcpu->freq_change_time); - pcpu->governor_enabled = 1; - smp_wmb(); + pcpu->governor_enabled = 1; + smp_wmb(); + } + /* * Do not register the idle hook and create sysfs * entries if we have already done so. @@ -613,18 +621,22 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *new_policy, break; case CPUFREQ_GOV_STOP: - pcpu->governor_enabled = 0; - smp_wmb(); - del_timer_sync(&pcpu->cpu_timer); - flush_work(&freq_scale_down_work); - /* - * Reset idle exit time since we may cancel the timer - * before it can run after the last idle exit time, - * to avoid tripping the check in idle exit for a timer - * that is trying to run. - */ - pcpu->idle_exit_time = 0; + for_each_cpu(j, policy->cpus) { + pcpu = &per_cpu(cpuinfo, j); + pcpu->governor_enabled = 0; + smp_wmb(); + del_timer_sync(&pcpu->cpu_timer); + /* + * Reset idle exit time since we may cancel the timer + * before it can run after the last idle exit time, + * to avoid tripping the check in idle exit for a timer + * that is trying to run. + */ + pcpu->idle_exit_time = 0; + } + + flush_work(&freq_scale_down_work); if (atomic_dec_return(&active_count) > 0) return 0; @@ -635,12 +647,12 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *new_policy, break; case CPUFREQ_GOV_LIMITS: - if (new_policy->max < new_policy->cur) - __cpufreq_driver_target(new_policy, - new_policy->max, CPUFREQ_RELATION_H); - else if (new_policy->min > new_policy->cur) - __cpufreq_driver_target(new_policy, - new_policy->min, CPUFREQ_RELATION_L); + if (policy->max < policy->cur) + __cpufreq_driver_target(policy, + policy->max, CPUFREQ_RELATION_H); + else if (policy->min > policy->cur) + __cpufreq_driver_target(policy, + policy->min, CPUFREQ_RELATION_L); break; } return 0; From b82181345a2d20a538aac3e6d738b5bc265bcf21 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Tue, 28 Jun 2011 10:40:30 -0700 Subject: [PATCH 2153/2556] cpufreq: interactive: remove debug trace code Remove debug trace code in preparation of upstreaming Change-Id: I0905885e75031f5e9d7cb06878fb68c1fd06d4fe Signed-off-by: Allen Martin --- drivers/cpufreq/cpufreq_interactive.c | 164 +------------------------- 1 file changed, 6 insertions(+), 158 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 1e7ff3248a288..1bf2c22f4b410 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -71,93 +71,6 @@ static unsigned long go_maxspeed_load; #endif static unsigned long min_sample_time; -#define DEBUG 0 -#define BUFSZ 128 - -#if DEBUG -#include - -struct dbgln { - int cpu; - unsigned long jiffy; - unsigned long run; - char buf[BUFSZ]; -}; - -#define NDBGLNS 256 - -static struct dbgln dbgbuf[NDBGLNS]; -static int dbgbufs; -static int dbgbufe; -static struct proc_dir_entry *dbg_proc; -static spinlock_t dbgpr_lock; - -static u64 up_request_time; -static unsigned int up_max_latency; - -static void dbgpr(char *fmt, ...) -{ - va_list args; - int n; - unsigned long flags; - - spin_lock_irqsave(&dbgpr_lock, flags); - n = dbgbufe; - va_start(args, fmt); - vsnprintf(dbgbuf[n].buf, BUFSZ, fmt, args); - va_end(args); - dbgbuf[n].cpu = smp_processor_id(); - dbgbuf[n].run = nr_running(); - dbgbuf[n].jiffy = jiffies; - - if (++dbgbufe >= NDBGLNS) - dbgbufe = 0; - - if (dbgbufe == dbgbufs) - if (++dbgbufs >= NDBGLNS) - dbgbufs = 0; - - spin_unlock_irqrestore(&dbgpr_lock, flags); -} - -static void dbgdump(void) -{ - int i, j; - unsigned long flags; - static struct dbgln prbuf[NDBGLNS]; - - spin_lock_irqsave(&dbgpr_lock, flags); - i = dbgbufs; - j = dbgbufe; - memcpy(prbuf, dbgbuf, sizeof(dbgbuf)); - dbgbufs = 0; - dbgbufe = 0; - spin_unlock_irqrestore(&dbgpr_lock, flags); - - while (i != j) - { - printk("%lu %d %lu %s", - prbuf[i].jiffy, prbuf[i].cpu, prbuf[i].run, - prbuf[i].buf); - if (++i == NDBGLNS) - i = 0; - } -} - -static int dbg_proc_read(char *buffer, char **start, off_t offset, - int count, int *peof, void *dat) -{ - printk("max up_task latency=%uus\n", up_max_latency); - dbgdump(); - *peof = 1; - return 0; -} - - -#else -#define dbgpr(...) do {} while (0) -#endif - static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event); @@ -210,16 +123,8 @@ static void cpufreq_interactive_timer(unsigned long data) smp_wmb(); /* If we raced with cancelling a timer, skip. */ - if (!idle_exit_time) { - dbgpr("timer %d: no valid idle exit sample\n", (int) data); + if (!idle_exit_time) goto exit; - } - -#if DEBUG - if ((int) jiffies - (int) pcpu->cpu_timer.expires >= 10) - dbgpr("timer %d: late by %d ticks\n", - (int) data, jiffies - pcpu->cpu_timer.expires); -#endif delta_idle = (unsigned int) cputime64_sub(now_idle, time_in_idle); delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time, @@ -228,11 +133,8 @@ static void cpufreq_interactive_timer(unsigned long data) /* * If timer ran less than 1ms after short-term sample started, retry. */ - if (delta_time < 1000) { - dbgpr("timer %d: time delta %u too short exit=%llu now=%llu\n", (int) data, - delta_time, idle_exit_time, pcpu->timer_run_time); + if (delta_time < 1000) goto rearm; - } if (delta_idle > delta_time) cpu_load = 0; @@ -266,17 +168,15 @@ static void cpufreq_interactive_timer(unsigned long data) if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, new_freq, CPUFREQ_RELATION_H, &index)) { - dbgpr("timer %d: cpufreq_frequency_table_target error\n", (int) data); + pr_warn_once("timer %d: cpufreq_frequency_table_target error\n", + (int) data); goto rearm; } new_freq = pcpu->freq_table[index].frequency; if (pcpu->target_freq == new_freq) - { - dbgpr("timer %d: load=%d, already at %d\n", (int) data, cpu_load, new_freq); goto rearm_if_notmax; - } /* * Do not scale down unless we have been at this frequency for the @@ -284,14 +184,10 @@ static void cpufreq_interactive_timer(unsigned long data) */ if (new_freq < pcpu->target_freq) { if (cputime64_sub(pcpu->timer_run_time, pcpu->freq_change_time) < - min_sample_time) { - dbgpr("timer %d: load=%d cur=%d tgt=%d not yet\n", (int) data, cpu_load, pcpu->target_freq, new_freq); + min_sample_time) goto rearm; - } } - dbgpr("timer %d: load=%d cur=%d tgt=%d queue\n", (int) data, cpu_load, pcpu->target_freq, new_freq); - if (new_freq < pcpu->target_freq) { pcpu->target_freq = new_freq; spin_lock_irqsave(&down_cpumask_lock, flags); @@ -300,9 +196,6 @@ static void cpufreq_interactive_timer(unsigned long data) queue_work(down_wq, &freq_scale_down_work); } else { pcpu->target_freq = new_freq; -#if DEBUG - up_request_time = ktime_to_us(ktime_get()); -#endif spin_lock_irqsave(&up_cpumask_lock, flags); cpumask_set_cpu(data, &up_cpumask); spin_unlock_irqrestore(&up_cpumask_lock, flags); @@ -327,10 +220,8 @@ static void cpufreq_interactive_timer(unsigned long data) if (pcpu->target_freq == pcpu->policy->min) { smp_rmb(); - if (pcpu->idling) { - dbgpr("timer %d: cpu idle, don't re-arm\n", (int) data); + if (pcpu->idling) goto exit; - } pcpu->timer_idlecancel = 1; } @@ -338,7 +229,6 @@ static void cpufreq_interactive_timer(unsigned long data) pcpu->time_in_idle = get_cpu_idle_time_us( data, &pcpu->idle_exit_time); mod_timer(&pcpu->cpu_timer, jiffies + 2); - dbgpr("timer %d: set timer for %lu exit=%llu\n", (int) data, pcpu->cpu_timer.expires, pcpu->idle_exit_time); } exit: @@ -375,9 +265,6 @@ static void cpufreq_interactive_idle(void) smp_processor_id(), &pcpu->idle_exit_time); pcpu->timer_idlecancel = 0; mod_timer(&pcpu->cpu_timer, jiffies + 2); - dbgpr("idle: enter at %d, set timer for %lu exit=%llu\n", - pcpu->target_freq, pcpu->cpu_timer.expires, - pcpu->idle_exit_time); } #endif } else { @@ -388,7 +275,6 @@ static void cpufreq_interactive_idle(void) * CPU didn't go busy; we'll recheck things upon idle exit. */ if (pending && pcpu->timer_idlecancel) { - dbgpr("idle: cancel timer for %lu\n", pcpu->cpu_timer.expires); del_timer(&pcpu->cpu_timer); /* * Ensure last timer run time is after current idle @@ -423,13 +309,6 @@ static void cpufreq_interactive_idle(void) &pcpu->idle_exit_time); pcpu->timer_idlecancel = 0; mod_timer(&pcpu->cpu_timer, jiffies + 2); - dbgpr("idle: exit, set timer for %lu exit=%llu\n", pcpu->cpu_timer.expires, pcpu->idle_exit_time); -#if DEBUG - } else if (timer_pending(&pcpu->cpu_timer) == 0 && - pcpu->timer_run_time < pcpu->idle_exit_time) { - dbgpr("idle: timer not run yet: exit=%llu tmrrun=%llu\n", - pcpu->idle_exit_time, pcpu->timer_run_time); -#endif } } @@ -441,12 +320,6 @@ static int cpufreq_interactive_up_task(void *data) unsigned long flags; struct cpufreq_interactive_cpuinfo *pcpu; -#if DEBUG - u64 now; - u64 then; - unsigned int lat; -#endif - while (1) { set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&up_cpumask_lock, flags); @@ -463,18 +336,6 @@ static int cpufreq_interactive_up_task(void *data) set_current_state(TASK_RUNNING); -#if DEBUG - then = up_request_time; - now = ktime_to_us(ktime_get()); - - if (now > then) { - lat = ktime_to_us(ktime_get()) - then; - - if (lat > up_max_latency) - up_max_latency = lat; - } -#endif - tmp_mask = up_cpumask; cpumask_clear(&up_cpumask); spin_unlock_irqrestore(&up_cpumask_lock, flags); @@ -482,11 +343,6 @@ static int cpufreq_interactive_up_task(void *data) for_each_cpu(cpu, &tmp_mask) { pcpu = &per_cpu(cpuinfo, cpu); - if (nr_running() == 1) { - dbgpr("up %d: tgt=%d nothing else running\n", cpu, - pcpu->target_freq); - } - smp_rmb(); if (!pcpu->governor_enabled) @@ -498,7 +354,6 @@ static int cpufreq_interactive_up_task(void *data) pcpu->freq_change_time_in_idle = get_cpu_idle_time_us(cpu, &pcpu->freq_change_time); - dbgpr("up %d: set tgt=%d (actual=%d)\n", cpu, pcpu->target_freq, pcpu->policy->cur); } } @@ -531,7 +386,6 @@ static void cpufreq_interactive_freq_down(struct work_struct *work) pcpu->freq_change_time_in_idle = get_cpu_idle_time_us(cpu, &pcpu->freq_change_time); - dbgpr("down %d: set tgt=%d (actual=%d)\n", cpu, pcpu->target_freq, pcpu->policy->cur); } } @@ -696,12 +550,6 @@ static int __init cpufreq_interactive_init(void) spin_lock_init(&up_cpumask_lock); spin_lock_init(&down_cpumask_lock); -#if DEBUG - spin_lock_init(&dbgpr_lock); - dbg_proc = create_proc_entry("igov", S_IWUSR | S_IRUGO, NULL); - dbg_proc->read_proc = dbg_proc_read; -#endif - return cpufreq_register_governor(&cpufreq_gov_interactive); err_freeuptask: From 3049b8e04b48148d28d586207d22ddbedbaa225c Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Thu, 30 Jun 2011 16:59:19 -0700 Subject: [PATCH 2154/2556] cpufreq: interactive: Add error checking on sysfs interfaces This adds better error checking on tunable parameters on sysfs interfaces. Also fixes return value from these functions, previously on success they would return 0 which would cause a infinite loop. Change-Id: Ic05038492166f8673d007202092471f98a2f0dfa Signed-off-by: Allen Martin --- drivers/cpufreq/cpufreq_interactive.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 1bf2c22f4b410..d24fbe48af22d 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -398,7 +398,14 @@ static ssize_t show_go_maxspeed_load(struct kobject *kobj, static ssize_t store_go_maxspeed_load(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { - return strict_strtoul(buf, 0, &go_maxspeed_load); + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + go_maxspeed_load = val; + return count; } static struct global_attr go_maxspeed_load_attr = __ATTR(go_maxspeed_load, 0644, @@ -413,7 +420,14 @@ static ssize_t show_min_sample_time(struct kobject *kobj, static ssize_t store_min_sample_time(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { - return strict_strtoul(buf, 0, &min_sample_time); + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + min_sample_time = val; + return count; } static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644, From c1cc1c65aa20dd6b88c133767a47368c27b095d0 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Thu, 30 Jun 2011 23:54:07 -0700 Subject: [PATCH 2155/2556] cpufreq: interactive: add sysfs control for timer rate Add a new sysfs control that tunes the rate of the timer used to increase cpu frequency Change-Id: I1aa13ae54bb43aff5b3688984d2955f56aae1658 Signed-off-by: Allen Martin --- drivers/cpufreq/cpufreq_interactive.c | 36 ++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index d24fbe48af22d..5f49a318078d4 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -71,6 +71,12 @@ static unsigned long go_maxspeed_load; #endif static unsigned long min_sample_time; +/* + * The sample rate of the timer used to increase frequency + */ +#define DEFAULT_TIMER_RATE 30000; +static unsigned long timer_rate; + static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event); @@ -228,7 +234,7 @@ static void cpufreq_interactive_timer(unsigned long data) pcpu->time_in_idle = get_cpu_idle_time_us( data, &pcpu->idle_exit_time); - mod_timer(&pcpu->cpu_timer, jiffies + 2); + mod_timer(&pcpu->cpu_timer, jiffies + usecs_to_jiffies(timer_rate)); } exit: @@ -264,7 +270,7 @@ static void cpufreq_interactive_idle(void) pcpu->time_in_idle = get_cpu_idle_time_us( smp_processor_id(), &pcpu->idle_exit_time); pcpu->timer_idlecancel = 0; - mod_timer(&pcpu->cpu_timer, jiffies + 2); + mod_timer(&pcpu->cpu_timer, jiffies + usecs_to_jiffies(timer_rate)); } #endif } else { @@ -308,7 +314,7 @@ static void cpufreq_interactive_idle(void) get_cpu_idle_time_us(smp_processor_id(), &pcpu->idle_exit_time); pcpu->timer_idlecancel = 0; - mod_timer(&pcpu->cpu_timer, jiffies + 2); + mod_timer(&pcpu->cpu_timer, jiffies + usecs_to_jiffies(timer_rate)); } } @@ -433,9 +439,32 @@ static ssize_t store_min_sample_time(struct kobject *kobj, static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644, show_min_sample_time, store_min_sample_time); +static ssize_t show_timer_rate(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", timer_rate); +} + +static ssize_t store_timer_rate(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + timer_rate = val; + return count; +} + +static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, + show_timer_rate, store_timer_rate); + static struct attribute *interactive_attributes[] = { &go_maxspeed_load_attr.attr, &min_sample_time_attr.attr, + &timer_rate_attr.attr, NULL, }; @@ -534,6 +563,7 @@ static int __init cpufreq_interactive_init(void) go_maxspeed_load = DEFAULT_GO_MAXSPEED_LOAD; min_sample_time = DEFAULT_MIN_SAMPLE_TIME; + timer_rate = DEFAULT_TIMER_RATE; /* Initalize per-cpu timers */ for_each_possible_cpu(i) { From 68f9a163be578c67f84a2e63943db9cdba01b280 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Fri, 1 Jul 2011 11:17:39 -0700 Subject: [PATCH 2156/2556] cpufreq: Add more verbose description of interactive governor Update the Kconfig help paragraph to give more detail about interactive governor. Change-Id: I607b817b370accac3a685001649a15e2f7894f59 Signed-off-by: Allen Martin --- drivers/cpufreq/Kconfig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index d384bcaabff9d..49ce53017aee0 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -183,6 +183,17 @@ config CPU_FREQ_GOV_INTERACTIVE 'interactive' - This driver adds a dynamic cpufreq policy governor designed for latency-sensitive workloads. + This governor attempts to reduce the latency of clock + increases so that the system is more responsive to + interactive workloads. + + To compile this driver as a module, choose M here: the + module will be called cpufreq_interactive. + + For details, take a look at linux/Documentation/cpu-freq. + + If in doubt, say N. + config CPU_FREQ_GOV_CONSERVATIVE tristate "'conservative' cpufreq governor" depends on CPU_FREQ From 1d58637dc042501956fa3f4994b48f1a75314e63 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Tue, 19 Jul 2011 14:56:21 -0700 Subject: [PATCH 2157/2556] cpufreq: interactive: fix checkpatch warnings on long lines Fix up checkpatch warning introduced by long lines in timer_rate patch. Change-Id: I22b105dafb1b49390799bb7577464da03f0f8afb Signed-off-by: Allen Martin --- drivers/cpufreq/cpufreq_interactive.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 5f49a318078d4..8dd80ef6e0a9f 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -234,7 +234,8 @@ static void cpufreq_interactive_timer(unsigned long data) pcpu->time_in_idle = get_cpu_idle_time_us( data, &pcpu->idle_exit_time); - mod_timer(&pcpu->cpu_timer, jiffies + usecs_to_jiffies(timer_rate)); + mod_timer(&pcpu->cpu_timer, + jiffies + usecs_to_jiffies(timer_rate)); } exit: @@ -270,7 +271,8 @@ static void cpufreq_interactive_idle(void) pcpu->time_in_idle = get_cpu_idle_time_us( smp_processor_id(), &pcpu->idle_exit_time); pcpu->timer_idlecancel = 0; - mod_timer(&pcpu->cpu_timer, jiffies + usecs_to_jiffies(timer_rate)); + mod_timer(&pcpu->cpu_timer, + jiffies + usecs_to_jiffies(timer_rate)); } #endif } else { @@ -314,7 +316,8 @@ static void cpufreq_interactive_idle(void) get_cpu_idle_time_us(smp_processor_id(), &pcpu->idle_exit_time); pcpu->timer_idlecancel = 0; - mod_timer(&pcpu->cpu_timer, jiffies + usecs_to_jiffies(timer_rate)); + mod_timer(&pcpu->cpu_timer, + jiffies + usecs_to_jiffies(timer_rate)); } } From 7942d288b6edbe05f37a9e3d8d1cc65daff7138a Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Sun, 16 Oct 2011 22:40:29 -0700 Subject: [PATCH 2158/2556] cpufreq: interactive governor: default timer 10ms, maxspeed load 95% Modify default timer from 30ms to 10ms, sampling 2 jiffies after idle exit on ARM as in Honeycomb. Modify default go_maxspeed_load from 85% loaded to 95% loaded, for use in phones where power savings is more important (tablets may be best served overriding this). Change-Id: I3361a6279979bfae1df5262666a2e30ea7a44328 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 8dd80ef6e0a9f..82728e02b3e93 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -58,7 +58,7 @@ static cpumask_t down_cpumask; static spinlock_t down_cpumask_lock; /* Go to max speed when CPU load at or above this value. */ -#define DEFAULT_GO_MAXSPEED_LOAD 85 +#define DEFAULT_GO_MAXSPEED_LOAD 95 static unsigned long go_maxspeed_load; /* @@ -74,7 +74,7 @@ static unsigned long min_sample_time; /* * The sample rate of the timer used to increase frequency */ -#define DEFAULT_TIMER_RATE 30000; +#define DEFAULT_TIMER_RATE 10000; static unsigned long timer_rate; static int cpufreq_governor_interactive(struct cpufreq_policy *policy, From dc3ffb935131310f13d98eea982f7f58bf032163 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 19 Oct 2011 13:12:10 -0700 Subject: [PATCH 2159/2556] power: cpufreq interactive governor: use default sample time 20ms Lower the default time at which a higher speed is allowed to run before lowering based on lower CPU load from 80ms to 20ms. Most Android devices should trade power for performance here, although tablets and non-battery-powered devices may want to override this default. Change-Id: I1a4f7faeca12793c51d5b92db30a63cca8d4f1be Signed-off-by: Todd Poynor Conflicts: drivers/cpufreq/cpufreq_interactive.c --- drivers/cpufreq/cpufreq_interactive.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 82728e02b3e93..b8d6ae1ded9d0 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -64,11 +64,8 @@ static unsigned long go_maxspeed_load; /* * The minimum amount of time to spend at a frequency before we can ramp down. */ -#if defined(CONFIG_ARCH_MSM_SCORPION) -#define DEFAULT_MIN_SAMPLE_TIME 75000; -#else -#define DEFAULT_MIN_SAMPLE_TIME 80000; -#endif + +#define DEFAULT_MIN_SAMPLE_TIME 20000; static unsigned long min_sample_time; /* From 54cc01049bcd992ac6e962a9a63fb0b3587e2850 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 27 Oct 2011 18:25:59 -0700 Subject: [PATCH 2160/2556] cpufreq: interactive governor: apply intermediate load on current speed Calculate intermediate speed by applyng CPU load to current speed, not max speed. Change-Id: Idecf598b9a203b07c989c5d9e9c6efc67a1afc2e Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index b8d6ae1ded9d0..d5a03c555a21e 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -166,7 +166,7 @@ static void cpufreq_interactive_timer(unsigned long data) if (cpu_load >= go_maxspeed_load) new_freq = pcpu->policy->max; else - new_freq = pcpu->policy->max * cpu_load / 100; + new_freq = pcpu->policy->cur * cpu_load / 100; if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, new_freq, CPUFREQ_RELATION_H, From 4ef95baf47f9cec478fcb334ff8d767d17be5aa9 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 3 Nov 2011 21:22:54 -0700 Subject: [PATCH 2161/2556] cpufreq: interactive governor: scale to max only if at min speed Change-Id: Ieffb2aa56b5290036285c948718be7be0d3af9e8 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index d5a03c555a21e..0bd54cc70793e 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -163,10 +163,14 @@ static void cpufreq_interactive_timer(unsigned long data) if (load_since_change > cpu_load) cpu_load = load_since_change; - if (cpu_load >= go_maxspeed_load) - new_freq = pcpu->policy->max; - else + if (cpu_load >= go_maxspeed_load) { + if (pcpu->policy->cur == pcpu->policy->min) + new_freq = pcpu->policy->max; + else + new_freq = pcpu->policy->max * cpu_load / 100; + } else { new_freq = pcpu->policy->cur * cpu_load / 100; + } if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, new_freq, CPUFREQ_RELATION_H, From 4ec221fd9fa7add337c98884bc5c60d4fd86f9c0 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 8 Nov 2011 19:54:07 -0800 Subject: [PATCH 2162/2556] cpufreq: interactive governor: go to intermediate hi speed before max * Add attribute hispeed_freq, which defaults to max. * Rename go_maxspeed_load to go_hispeed_load. * If hit go_hispeed_load and at min speed, go to hispeed_freq; if hit go_hispeed_load and already above min speed go to max speed. Change-Id: I1050dec5f013fc1177387352ba787a7e1c68703e Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 57 +++++++++++++++++++++------ 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 0bd54cc70793e..2b8a6ab9d0f20 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -57,9 +57,12 @@ static spinlock_t up_cpumask_lock; static cpumask_t down_cpumask; static spinlock_t down_cpumask_lock; -/* Go to max speed when CPU load at or above this value. */ -#define DEFAULT_GO_MAXSPEED_LOAD 95 -static unsigned long go_maxspeed_load; +/* Hi speed to bump to from lo speed when load burst (default max) */ +static u64 hispeed_freq; + +/* Go to hi speed when CPU load at or above this value. */ +#define DEFAULT_GO_HISPEED_LOAD 95 +static unsigned long go_hispeed_load; /* * The minimum amount of time to spend at a frequency before we can ramp down. @@ -163,9 +166,9 @@ static void cpufreq_interactive_timer(unsigned long data) if (load_since_change > cpu_load) cpu_load = load_since_change; - if (cpu_load >= go_maxspeed_load) { + if (cpu_load >= go_hispeed_load) { if (pcpu->policy->cur == pcpu->policy->min) - new_freq = pcpu->policy->max; + new_freq = hispeed_freq; else new_freq = pcpu->policy->max * cpu_load / 100; } else { @@ -399,13 +402,37 @@ static void cpufreq_interactive_freq_down(struct work_struct *work) } } -static ssize_t show_go_maxspeed_load(struct kobject *kobj, +static ssize_t show_hispeed_freq(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%llu\n", hispeed_freq); +} + +static ssize_t store_hispeed_freq(struct kobject *kobj, + struct attribute *attr, const char *buf, + size_t count) +{ + int ret; + u64 val; + + ret = strict_strtoull(buf, 0, &val); + if (ret < 0) + return ret; + hispeed_freq = val; + return count; +} + +static struct global_attr hispeed_freq_attr = __ATTR(hispeed_freq, 0644, + show_hispeed_freq, store_hispeed_freq); + + +static ssize_t show_go_hispeed_load(struct kobject *kobj, struct attribute *attr, char *buf) { - return sprintf(buf, "%lu\n", go_maxspeed_load); + return sprintf(buf, "%lu\n", go_hispeed_load); } -static ssize_t store_go_maxspeed_load(struct kobject *kobj, +static ssize_t store_go_hispeed_load(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { int ret; @@ -414,12 +441,12 @@ static ssize_t store_go_maxspeed_load(struct kobject *kobj, ret = strict_strtoul(buf, 0, &val); if (ret < 0) return ret; - go_maxspeed_load = val; + go_hispeed_load = val; return count; } -static struct global_attr go_maxspeed_load_attr = __ATTR(go_maxspeed_load, 0644, - show_go_maxspeed_load, store_go_maxspeed_load); +static struct global_attr go_hispeed_load_attr = __ATTR(go_hispeed_load, 0644, + show_go_hispeed_load, store_go_hispeed_load); static ssize_t show_min_sample_time(struct kobject *kobj, struct attribute *attr, char *buf) @@ -466,7 +493,8 @@ static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, show_timer_rate, store_timer_rate); static struct attribute *interactive_attributes[] = { - &go_maxspeed_load_attr.attr, + &hispeed_freq_attr.attr, + &go_hispeed_load_attr.attr, &min_sample_time_attr.attr, &timer_rate_attr.attr, NULL, @@ -505,6 +533,9 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, smp_wmb(); } + if (!hispeed_freq) + hispeed_freq = policy->max; + /* * Do not register the idle hook and create sysfs * entries if we have already done so. @@ -565,7 +596,7 @@ static int __init cpufreq_interactive_init(void) struct cpufreq_interactive_cpuinfo *pcpu; struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; - go_maxspeed_load = DEFAULT_GO_MAXSPEED_LOAD; + go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; min_sample_time = DEFAULT_MIN_SAMPLE_TIME; timer_rate = DEFAULT_TIMER_RATE; From d97cc2412cfaf578498bf28ef2784f22e2b4dafc Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 9 Nov 2011 16:56:18 -0800 Subject: [PATCH 2163/2556] cpufreq: interactive governor: default 20ms timer Change-Id: Ie9952f07b38667f2932474090044195c57976faa Signed-off-by: Todd Poynor Conflicts: drivers/cpufreq/cpufreq_interactive.c --- drivers/cpufreq/cpufreq_interactive.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 2b8a6ab9d0f20..e1f81e5124b70 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -67,14 +68,13 @@ static unsigned long go_hispeed_load; /* * The minimum amount of time to spend at a frequency before we can ramp down. */ - -#define DEFAULT_MIN_SAMPLE_TIME 20000; +#define DEFAULT_MIN_SAMPLE_TIME 20 * USEC_PER_MSEC static unsigned long min_sample_time; /* * The sample rate of the timer used to increase frequency */ -#define DEFAULT_TIMER_RATE 10000; +#define DEFAULT_TIMER_RATE 20 * USEC_PER_MSEC static unsigned long timer_rate; static int cpufreq_governor_interactive(struct cpufreq_policy *policy, From f1e42cc9d8dd75aa9eb7e8a583a69796ae274973 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 22 Jun 2011 14:34:23 -0700 Subject: [PATCH 2164/2556] cpufreq: interactive: Fix handling of SMP where CPUs must scale in tandem The interactive governor relies on quirks of the Tegra 2 cpufreq implementation for handling SMP systems where the CPUs do not have separate rate controls. It needs to determine the maximum rate for all CPUs covered by the policy and set that speed. Change-Id: I1ed9fa171e5a9c45a1fa5944e3fa823eb157e81f Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 47 +++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index e1f81e5124b70..9ba664ab11542 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -57,6 +58,7 @@ static cpumask_t up_cpumask; static spinlock_t up_cpumask_lock; static cpumask_t down_cpumask; static spinlock_t down_cpumask_lock; +static struct mutex set_speed_lock; /* Hi speed to bump to from lo speed when load burst (default max) */ static u64 hispeed_freq; @@ -354,6 +356,9 @@ static int cpufreq_interactive_up_task(void *data) spin_unlock_irqrestore(&up_cpumask_lock, flags); for_each_cpu(cpu, &tmp_mask) { + unsigned int j; + unsigned int max_freq = 0; + pcpu = &per_cpu(cpuinfo, cpu); smp_rmb(); @@ -361,9 +366,22 @@ static int cpufreq_interactive_up_task(void *data) if (!pcpu->governor_enabled) continue; - __cpufreq_driver_target(pcpu->policy, - pcpu->target_freq, - CPUFREQ_RELATION_H); + mutex_lock(&set_speed_lock); + + for_each_cpu(j, pcpu->policy->cpus) { + struct cpufreq_interactive_cpuinfo *pjcpu = + &per_cpu(cpuinfo, j); + + if (pjcpu->target_freq > max_freq) + max_freq = pjcpu->target_freq; + } + + if (max_freq != pcpu->policy->cur) + __cpufreq_driver_target(pcpu->policy, + max_freq, + CPUFREQ_RELATION_H); + mutex_unlock(&set_speed_lock); + pcpu->freq_change_time_in_idle = get_cpu_idle_time_us(cpu, &pcpu->freq_change_time); @@ -386,16 +404,30 @@ static void cpufreq_interactive_freq_down(struct work_struct *work) spin_unlock_irqrestore(&down_cpumask_lock, flags); for_each_cpu(cpu, &tmp_mask) { - pcpu = &per_cpu(cpuinfo, cpu); + unsigned int j; + unsigned int max_freq = 0; + pcpu = &per_cpu(cpuinfo, cpu); smp_rmb(); if (!pcpu->governor_enabled) continue; - __cpufreq_driver_target(pcpu->policy, - pcpu->target_freq, - CPUFREQ_RELATION_H); + mutex_lock(&set_speed_lock); + + for_each_cpu(j, pcpu->policy->cpus) { + struct cpufreq_interactive_cpuinfo *pjcpu = + &per_cpu(cpuinfo, j); + + if (pjcpu->target_freq > max_freq) + max_freq = pjcpu->target_freq; + } + + if (max_freq != pcpu->policy->cur) + __cpufreq_driver_target(pcpu->policy, max_freq, + CPUFREQ_RELATION_H); + + mutex_unlock(&set_speed_lock); pcpu->freq_change_time_in_idle = get_cpu_idle_time_us(cpu, &pcpu->freq_change_time); @@ -628,6 +660,7 @@ static int __init cpufreq_interactive_init(void) spin_lock_init(&up_cpumask_lock); spin_lock_init(&down_cpumask_lock); + mutex_init(&set_speed_lock); return cpufreq_register_governor(&cpufreq_gov_interactive); From 6ea2577dcbc90514f1e329f601093f7999270988 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Thu, 30 Jun 2011 23:54:07 -0700 Subject: [PATCH 2165/2556] Documentation: cpufreq: add description of timer_rate Add description of timer_rate tunable and clean up some typos. Change-Id: I4b96a36aad51eed3bef0ee5f571dc6e0a94c8dd9 Signed-off-by: Allen Martin --- Documentation/cpu-freq/governors.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index b37d805e19ead..45e61959a6014 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -204,10 +204,10 @@ to MAX speed. If the cpu was not sufficiently busy to immediately ramp to MAX speed, then governor evaluates the cpu load since the last speed adjustment, -choosing th highest value between that longer-term load or the +choosing the highest value between that longer-term load or the short-term load since idle exit to determine the cpu speed to ramp to. -The tuneable value for this governor are: +The tuneable values for this governor are: min_sample_time: The minimum amount of time to spend at the current frequency before ramping down. This is to ensure that the governor has @@ -217,6 +217,8 @@ workload. Default is 80000 uS. go_maxspeed_load: The CPU load at which to ramp to max speed. Default is 85. +timer_rate: Sample rate for reevaluating cpu load when the system is +not idle. Default is 30000 uS. 3. The Governor Interface in the CPUfreq Core ============================================= From 6dc448882a473887670e0afaef58d0b329a153df Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Fri, 1 Jul 2011 11:16:06 -0700 Subject: [PATCH 2166/2556] Documentation: remove trailing whitespace in governors.txt Fixes a checkpatch warning Change-Id: I2962b7c32f336188de0d2fe4f0f13f1199cb68e2 Signed-off-by: Allen Martin --- Documentation/cpu-freq/governors.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index 45e61959a6014..fdf1b2cca0e95 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -201,7 +201,7 @@ idle. When the cpu comes out of idle, a timer is configured to fire within 1-2 ticks. If the cpu is very busy between exiting idle and when the timer fires then we assume the cpu is underpowered and ramp to MAX speed. - + If the cpu was not sufficiently busy to immediately ramp to MAX speed, then governor evaluates the cpu load since the last speed adjustment, choosing the highest value between that longer-term load or the From a704f8a0e1297afb6653a435b70a163e5d1057f6 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Tue, 28 Jun 2011 09:58:39 -0700 Subject: [PATCH 2167/2556] cpufreq: interactive: use idle notifier Convert interactive governor to use idle notifier instead of hooking pm_idle directly. Change-Id: I47e007f330468ac559240a0ae8a3cb06a89ccb67 Signed-off-by: Allen Martin --- drivers/cpufreq/cpufreq_interactive.c | 37 ++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 9ba664ab11542..1fb581285a1e0 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -30,7 +30,6 @@ #include -static void (*pm_idle_old)(void); static atomic_t active_count = ATOMIC_INIT(0); struct cpufreq_interactive_cpuinfo { @@ -248,14 +247,13 @@ static void cpufreq_interactive_timer(unsigned long data) return; } -static void cpufreq_interactive_idle(void) +static void cpufreq_interactive_idle_start(void) { struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, smp_processor_id()); int pending; if (!pcpu->governor_enabled) { - pm_idle_old(); return; } @@ -300,7 +298,13 @@ static void cpufreq_interactive_idle(void) } } - pm_idle_old(); +} + +static void cpufreq_interactive_idle_end(void) +{ + struct cpufreq_interactive_cpuinfo *pcpu = + &per_cpu(cpuinfo, smp_processor_id()); + pcpu->idling = 0; smp_wmb(); @@ -580,8 +584,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, if (rc) return rc; - pm_idle_old = pm_idle; - pm_idle = cpufreq_interactive_idle; break; case CPUFREQ_GOV_STOP: @@ -607,7 +609,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, sysfs_remove_group(cpufreq_global_kobject, &interactive_attr_group); - pm_idle = pm_idle_old; break; case CPUFREQ_GOV_LIMITS: @@ -622,6 +623,26 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, return 0; } +static int cpufreq_interactive_idle_notifier(struct notifier_block *nb, + unsigned long val, + void *data) +{ + switch (val) { + case IDLE_START: + cpufreq_interactive_idle_start(); + break; + case IDLE_END: + cpufreq_interactive_idle_end(); + break; + } + + return 0; +} + +static struct notifier_block cpufreq_interactive_idle_nb = { + .notifier_call = cpufreq_interactive_idle_notifier, +}; + static int __init cpufreq_interactive_init(void) { unsigned int i; @@ -662,6 +683,8 @@ static int __init cpufreq_interactive_init(void) spin_lock_init(&down_cpumask_lock); mutex_init(&set_speed_lock); + idle_notifier_register(&cpufreq_interactive_idle_nb); + return cpufreq_register_governor(&cpufreq_gov_interactive); err_freeuptask: From 27f2d6afaf64398b1f2076df0edb7c63d11a642e Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 15 Jun 2011 17:21:57 -0700 Subject: [PATCH 2168/2556] Move x86_64 idle notifiers to generic Move the x86_64 idle notifiers originally by Andi Kleen and Venkatesh Pallipadi to generic. Change-Id: Idf29cda15be151f494ff245933c12462643388d5 Acked-by: Nicolas Pitre Signed-off-by: Todd Poynor --- arch/x86/include/asm/idle.h | 7 ------- arch/x86/kernel/process_64.c | 18 ++---------------- include/linux/cpu.h | 7 +++++++ kernel/cpu.c | 20 ++++++++++++++++++++ 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/arch/x86/include/asm/idle.h b/arch/x86/include/asm/idle.h index 38d87379e2705..192e5a02a8c98 100644 --- a/arch/x86/include/asm/idle.h +++ b/arch/x86/include/asm/idle.h @@ -1,13 +1,6 @@ #ifndef _ASM_X86_IDLE_H #define _ASM_X86_IDLE_H -#define IDLE_START 1 -#define IDLE_END 2 - -struct notifier_block; -void idle_notifier_register(struct notifier_block *n); -void idle_notifier_unregister(struct notifier_block *n); - #ifdef CONFIG_X86_64 void enter_idle(void); void exit_idle(void); diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index bd387e8f73b47..080ea55522140 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -56,31 +56,17 @@ asmlinkage extern void ret_from_fork(void); DEFINE_PER_CPU(unsigned long, old_rsp); static DEFINE_PER_CPU(unsigned char, is_idle); -static ATOMIC_NOTIFIER_HEAD(idle_notifier); - -void idle_notifier_register(struct notifier_block *n) -{ - atomic_notifier_chain_register(&idle_notifier, n); -} -EXPORT_SYMBOL_GPL(idle_notifier_register); - -void idle_notifier_unregister(struct notifier_block *n) -{ - atomic_notifier_chain_unregister(&idle_notifier, n); -} -EXPORT_SYMBOL_GPL(idle_notifier_unregister); - void enter_idle(void) { percpu_write(is_idle, 1); - atomic_notifier_call_chain(&idle_notifier, IDLE_START, NULL); + idle_notifier_call_chain(IDLE_START); } static void __exit_idle(void) { if (x86_test_and_clear_bit_percpu(0, is_idle) == 0) return; - atomic_notifier_call_chain(&idle_notifier, IDLE_END, NULL); + idle_notifier_call_chain(IDLE_END); } /* Called from interrupts to signify idle end */ diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 5f09323ee8808..97f1ca76b4aa4 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -174,4 +174,11 @@ static inline int disable_nonboot_cpus(void) { return 0; } static inline void enable_nonboot_cpus(void) {} #endif /* !CONFIG_PM_SLEEP_SMP */ +#define IDLE_START 1 +#define IDLE_END 2 + +void idle_notifier_register(struct notifier_block *n); +void idle_notifier_unregister(struct notifier_block *n); +void idle_notifier_call_chain(unsigned long val); + #endif /* _LINUX_CPU_H_ */ diff --git a/kernel/cpu.c b/kernel/cpu.c index 156cc55561408..559457492dc04 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -595,3 +595,23 @@ void init_cpu_online(const struct cpumask *src) { cpumask_copy(to_cpumask(cpu_online_bits), src); } + +static ATOMIC_NOTIFIER_HEAD(idle_notifier); + +void idle_notifier_register(struct notifier_block *n) +{ + atomic_notifier_chain_register(&idle_notifier, n); +} +EXPORT_SYMBOL_GPL(idle_notifier_register); + +void idle_notifier_unregister(struct notifier_block *n) +{ + atomic_notifier_chain_unregister(&idle_notifier, n); +} +EXPORT_SYMBOL_GPL(idle_notifier_unregister); + +void idle_notifier_call_chain(unsigned long val) +{ + atomic_notifier_call_chain(&idle_notifier, val, NULL); +} +EXPORT_SYMBOL_GPL(idle_notifier_call_chain); From d11ec0137d30a069c5d3a805df7aa5b71228dd7c Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Fri, 1 Jul 2011 11:19:14 -0700 Subject: [PATCH 2169/2556] cpufreq: interactive: cleanup checkpatch warnings Cleanup some style warnings reported by checkpatch Change-Id: Ie2e6903d52867fb3347e009d7efa3bc4ca755cea Signed-off-by: Allen Martin --- drivers/cpufreq/cpufreq_interactive.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 1fb581285a1e0..4d8bd52187cab 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -149,7 +149,7 @@ static void cpufreq_interactive_timer(unsigned long data) cpu_load = 100 * (delta_time - delta_idle) / delta_time; delta_idle = (unsigned int) cputime64_sub(now_idle, - pcpu->freq_change_time_in_idle); + pcpu->freq_change_time_in_idle); delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time, pcpu->freq_change_time); @@ -194,8 +194,8 @@ static void cpufreq_interactive_timer(unsigned long data) * minimum sample time. */ if (new_freq < pcpu->target_freq) { - if (cputime64_sub(pcpu->timer_run_time, pcpu->freq_change_time) < - min_sample_time) + if (cputime64_sub(pcpu->timer_run_time, pcpu->freq_change_time) + < min_sample_time) goto rearm; } @@ -253,9 +253,8 @@ static void cpufreq_interactive_idle_start(void) &per_cpu(cpuinfo, smp_processor_id()); int pending; - if (!pcpu->governor_enabled) { + if (!pcpu->governor_enabled) return; - } pcpu->idling = 1; smp_wmb(); @@ -673,7 +672,7 @@ static int __init cpufreq_interactive_init(void) warm cache (probably doesn't matter much). */ down_wq = alloc_workqueue("knteractive_down", 0, 1); - if (! down_wq) + if (!down_wq) goto err_freeuptask; INIT_WORK(&freq_scale_down_work, From 3647f40fad7ddc5189cef37bfe206c697d7c4de4 Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Thu, 24 Nov 2011 02:00:33 -0500 Subject: [PATCH 2170/2556] drivers: cpufreq: interactive: adjust thresholds --- drivers/cpufreq/cpufreq_interactive.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 4d8bd52187cab..6e7097ba5edcd 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -63,7 +63,7 @@ static struct mutex set_speed_lock; static u64 hispeed_freq; /* Go to hi speed when CPU load at or above this value. */ -#define DEFAULT_GO_HISPEED_LOAD 95 +#define DEFAULT_GO_HISPEED_LOAD 85 static unsigned long go_hispeed_load; /* @@ -88,7 +88,7 @@ struct cpufreq_governor cpufreq_gov_interactive = { .name = "interactive", .governor = cpufreq_governor_interactive, #if defined(CONFIG_ARCH_MSM_SCORPION) - .max_transition_latency = 8000000, + .max_transition_latency = 9500000, #else .max_transition_latency = 10000000, #endif From 5fc2de33202a220cc2a534ed1381183748ea8489 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 26 Jan 2011 12:12:50 +0100 Subject: [PATCH 2171/2556] cpufreq: use system_wq instead of dedicated workqueues With cmwq, there's no reason for cpufreq drivers to use separate workqueues. Remove the dedicated workqueues from cpufreq_conservative and cpufreq_ondemand and use system_wq instead. The work items are already sync canceled on stop, so it's already guaranteed that no work is running on module exit. Signed-off-by: Tejun Heo Acked-by: Dave Jones Cc: cpufreq@vger.kernel.org --- drivers/cpufreq/cpufreq_conservative.c | 22 +++------------------- drivers/cpufreq/cpufreq_ondemand.c | 20 +++----------------- 2 files changed, 6 insertions(+), 36 deletions(-) diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 526bfbf696112..94284c8473b1f 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -81,8 +81,6 @@ static unsigned int dbs_enable; /* number of CPUs using this policy */ */ static DEFINE_MUTEX(dbs_mutex); -static struct workqueue_struct *kconservative_wq; - static struct dbs_tuners { unsigned int sampling_rate; unsigned int sampling_down_factor; @@ -560,7 +558,7 @@ static void do_dbs_timer(struct work_struct *work) dbs_check_cpu(dbs_info); - queue_delayed_work_on(cpu, kconservative_wq, &dbs_info->work, delay); + schedule_delayed_work_on(cpu, &dbs_info->work, delay); mutex_unlock(&dbs_info->timer_mutex); } @@ -572,8 +570,7 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) dbs_info->enable = 1; INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer); - queue_delayed_work_on(dbs_info->cpu, kconservative_wq, &dbs_info->work, - delay); + schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work, delay); } static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info) @@ -716,25 +713,12 @@ struct cpufreq_governor cpufreq_gov_conservative = { static int __init cpufreq_gov_dbs_init(void) { - int err; - - kconservative_wq = create_workqueue("kconservative"); - if (!kconservative_wq) { - printk(KERN_ERR "Creation of kconservative failed\n"); - return -EFAULT; - } - - err = cpufreq_register_governor(&cpufreq_gov_conservative); - if (err) - destroy_workqueue(kconservative_wq); - - return err; + return cpufreq_register_governor(&cpufreq_gov_conservative); } static void __exit cpufreq_gov_dbs_exit(void) { cpufreq_unregister_governor(&cpufreq_gov_conservative); - destroy_workqueue(kconservative_wq); } diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 5c8d6d1501555..899a0e0c830ee 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -116,8 +116,6 @@ static unsigned int dbs_enable; /* number of CPUs using this policy */ */ static DEFINE_MUTEX(dbs_mutex); -static struct workqueue_struct *kondemand_wq; - static struct dbs_tuners { unsigned int sampling_rate; unsigned int up_threshold; @@ -679,7 +677,7 @@ static void do_dbs_timer(struct work_struct *work) __cpufreq_driver_target(dbs_info->cur_policy, dbs_info->freq_lo, CPUFREQ_RELATION_H); } - queue_delayed_work_on(cpu, kondemand_wq, &dbs_info->work, delay); + schedule_delayed_work_on(cpu, &dbs_info->work, delay); mutex_unlock(&dbs_info->timer_mutex); } @@ -693,8 +691,7 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) dbs_info->sample_type = DBS_NORMAL_SAMPLE; INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer); - queue_delayed_work_on(dbs_info->cpu, kondemand_wq, &dbs_info->work, - delay); + schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work, delay); } static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info) @@ -826,7 +823,6 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, static int __init cpufreq_gov_dbs_init(void) { - int err; cputime64_t wall; u64 idle_time; int cpu = get_cpu(); @@ -850,22 +846,12 @@ static int __init cpufreq_gov_dbs_init(void) MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10); } - kondemand_wq = create_workqueue("kondemand"); - if (!kondemand_wq) { - printk(KERN_ERR "Creation of kondemand failed\n"); - return -EFAULT; - } - err = cpufreq_register_governor(&cpufreq_gov_ondemand); - if (err) - destroy_workqueue(kondemand_wq); - - return err; + return cpufreq_register_governor(&cpufreq_gov_ondemand); } static void __exit cpufreq_gov_dbs_exit(void) { cpufreq_unregister_governor(&cpufreq_gov_ondemand); - destroy_workqueue(kondemand_wq); } From 96f12e4da437da92de9da906c7ae64573904a77a Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Mon, 7 Feb 2011 17:14:25 +0100 Subject: [PATCH 2172/2556] [CPUFREQ] calculate delay after dbs_check_cpu calculate ondemand delay after dbs_check_cpu call because it can modify rate_mult value use freq_lo_jiffies value for the sub sample period of powersave_bias mode Signed-off-by: Vincent Guittot Signed-off-by: Dave Jones --- drivers/cpufreq/cpufreq_ondemand.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 899a0e0c830ee..d850f3c29ac32 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -654,12 +654,7 @@ static void do_dbs_timer(struct work_struct *work) unsigned int cpu = dbs_info->cpu; int sample_type = dbs_info->sample_type; - /* We want all CPUs to do sampling nearly on same jiffy */ - int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate - * dbs_info->rate_mult); - - if (num_online_cpus() > 1) - delay -= jiffies % delay; + int delay; mutex_lock(&dbs_info->timer_mutex); @@ -672,10 +667,20 @@ static void do_dbs_timer(struct work_struct *work) /* Setup timer for SUB_SAMPLE */ dbs_info->sample_type = DBS_SUB_SAMPLE; delay = dbs_info->freq_hi_jiffies; + } else { + /* We want all CPUs to do sampling nearly on + * same jiffy + */ + delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate + * dbs_info->rate_mult); + + if (num_online_cpus() > 1) + delay -= jiffies % delay; } } else { __cpufreq_driver_target(dbs_info->cur_policy, dbs_info->freq_lo, CPUFREQ_RELATION_H); + delay = dbs_info->freq_lo_jiffies; } schedule_delayed_work_on(cpu, &dbs_info->work, delay); mutex_unlock(&dbs_info->timer_mutex); From 656afa3f317b17ee610f62d14bc0cab226efd1f7 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Thu, 3 Mar 2011 21:31:25 +0100 Subject: [PATCH 2173/2556] [CPUFREQ] Remove deprecated sysfs file sampling_rate_max Marked deprecated for quite a while now... Signed-off-by: Thomas Renninger Signed-off-by: Dave Jones CC: cpufreq@vger.kernel.org --- drivers/cpufreq/cpufreq_conservative.c | 13 ------------- drivers/cpufreq/cpufreq_ondemand.c | 13 ------------- 2 files changed, 26 deletions(-) diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 94284c8473b1f..52b67e4186b6a 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -162,21 +162,12 @@ static struct notifier_block dbs_cpufreq_notifier_block = { }; /************************** sysfs interface ************************/ -static ssize_t show_sampling_rate_max(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - printk_once(KERN_INFO "CPUFREQ: conservative sampling_rate_max " - "sysfs file is deprecated - used by: %s\n", current->comm); - return sprintf(buf, "%u\n", -1U); -} - static ssize_t show_sampling_rate_min(struct kobject *kobj, struct attribute *attr, char *buf) { return sprintf(buf, "%u\n", min_sampling_rate); } -define_one_global_ro(sampling_rate_max); define_one_global_ro(sampling_rate_min); /* cpufreq_conservative Governor Tunables */ @@ -213,10 +204,8 @@ show_one_old(down_threshold); show_one_old(ignore_nice_load); show_one_old(freq_step); show_one_old(sampling_rate_min); -show_one_old(sampling_rate_max); cpufreq_freq_attr_ro_old(sampling_rate_min); -cpufreq_freq_attr_ro_old(sampling_rate_max); /*** delete after deprecation time ***/ @@ -362,7 +351,6 @@ define_one_global_rw(ignore_nice_load); define_one_global_rw(freq_step); static struct attribute *dbs_attributes[] = { - &sampling_rate_max.attr, &sampling_rate_min.attr, &sampling_rate.attr, &sampling_down_factor.attr, @@ -403,7 +391,6 @@ cpufreq_freq_attr_rw_old(ignore_nice_load); cpufreq_freq_attr_rw_old(freq_step); static struct attribute *dbs_attributes_old[] = { - &sampling_rate_max_old.attr, &sampling_rate_min_old.attr, &sampling_rate_old.attr, &sampling_down_factor_old.attr, diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index d850f3c29ac32..16471e5c55e75 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -247,21 +247,12 @@ static void ondemand_powersave_bias_init(void) /************************** sysfs interface ************************/ -static ssize_t show_sampling_rate_max(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - printk_once(KERN_INFO "CPUFREQ: ondemand sampling_rate_max " - "sysfs file is deprecated - used by: %s\n", current->comm); - return sprintf(buf, "%u\n", -1U); -} - static ssize_t show_sampling_rate_min(struct kobject *kobj, struct attribute *attr, char *buf) { return sprintf(buf, "%u\n", min_sampling_rate); } -define_one_global_ro(sampling_rate_max); define_one_global_ro(sampling_rate_min); /* cpufreq_ondemand Governor Tunables */ @@ -297,10 +288,8 @@ show_one_old(up_threshold); show_one_old(ignore_nice_load); show_one_old(powersave_bias); show_one_old(sampling_rate_min); -show_one_old(sampling_rate_max); cpufreq_freq_attr_ro_old(sampling_rate_min); -cpufreq_freq_attr_ro_old(sampling_rate_max); /*** delete after deprecation time ***/ @@ -445,7 +434,6 @@ define_one_global_rw(ignore_nice_load); define_one_global_rw(powersave_bias); static struct attribute *dbs_attributes[] = { - &sampling_rate_max.attr, &sampling_rate_min.attr, &sampling_rate.attr, &up_threshold.attr, @@ -482,7 +470,6 @@ cpufreq_freq_attr_rw_old(ignore_nice_load); cpufreq_freq_attr_rw_old(powersave_bias); static struct attribute *dbs_attributes_old[] = { - &sampling_rate_max_old.attr, &sampling_rate_min_old.attr, &sampling_rate_old.attr, &up_threshold_old.attr, From be7367983d09efcc4f3f3aebaaff5f25737e03bf Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Thu, 3 Mar 2011 21:31:26 +0100 Subject: [PATCH 2174/2556] [CPUFREQ] Remove old, deprecated per cpu ondemand/conservative sysfs files Marked deprecated for quite a whilte now... Signed-off-by: Thomas Renninger Signed-off-by: Dave Jones CC: cpufreq@vger.kernel.org --- drivers/cpufreq/cpufreq_conservative.c | 74 -------------------------- drivers/cpufreq/cpufreq_ondemand.c | 67 ----------------------- include/linux/cpufreq.h | 9 ---- 3 files changed, 150 deletions(-) diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 52b67e4186b6a..139bab3d6e93d 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -184,31 +184,6 @@ show_one(down_threshold, down_threshold); show_one(ignore_nice_load, ignore_nice); show_one(freq_step, freq_step); -/*** delete after deprecation time ***/ -#define DEPRECATION_MSG(file_name) \ - printk_once(KERN_INFO "CPUFREQ: Per core conservative sysfs " \ - "interface is deprecated - " #file_name "\n"); - -#define show_one_old(file_name) \ -static ssize_t show_##file_name##_old \ -(struct cpufreq_policy *unused, char *buf) \ -{ \ - printk_once(KERN_INFO "CPUFREQ: Per core conservative sysfs " \ - "interface is deprecated - " #file_name "\n"); \ - return show_##file_name(NULL, NULL, buf); \ -} -show_one_old(sampling_rate); -show_one_old(sampling_down_factor); -show_one_old(up_threshold); -show_one_old(down_threshold); -show_one_old(ignore_nice_load); -show_one_old(freq_step); -show_one_old(sampling_rate_min); - -cpufreq_freq_attr_ro_old(sampling_rate_min); - -/*** delete after deprecation time ***/ - static ssize_t store_sampling_down_factor(struct kobject *a, struct attribute *b, const char *buf, size_t count) @@ -366,48 +341,6 @@ static struct attribute_group dbs_attr_group = { .name = "conservative", }; -/*** delete after deprecation time ***/ - -#define write_one_old(file_name) \ -static ssize_t store_##file_name##_old \ -(struct cpufreq_policy *unused, const char *buf, size_t count) \ -{ \ - printk_once(KERN_INFO "CPUFREQ: Per core conservative sysfs " \ - "interface is deprecated - " #file_name "\n"); \ - return store_##file_name(NULL, NULL, buf, count); \ -} -write_one_old(sampling_rate); -write_one_old(sampling_down_factor); -write_one_old(up_threshold); -write_one_old(down_threshold); -write_one_old(ignore_nice_load); -write_one_old(freq_step); - -cpufreq_freq_attr_rw_old(sampling_rate); -cpufreq_freq_attr_rw_old(sampling_down_factor); -cpufreq_freq_attr_rw_old(up_threshold); -cpufreq_freq_attr_rw_old(down_threshold); -cpufreq_freq_attr_rw_old(ignore_nice_load); -cpufreq_freq_attr_rw_old(freq_step); - -static struct attribute *dbs_attributes_old[] = { - &sampling_rate_min_old.attr, - &sampling_rate_old.attr, - &sampling_down_factor_old.attr, - &up_threshold_old.attr, - &down_threshold_old.attr, - &ignore_nice_load_old.attr, - &freq_step_old.attr, - NULL -}; - -static struct attribute_group dbs_attr_group_old = { - .attrs = dbs_attributes_old, - .name = "conservative", -}; - -/*** delete after deprecation time ***/ - /************************** sysfs end ************************/ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) @@ -583,12 +516,6 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, mutex_lock(&dbs_mutex); - rc = sysfs_create_group(&policy->kobj, &dbs_attr_group_old); - if (rc) { - mutex_unlock(&dbs_mutex); - return rc; - } - for_each_cpu(j, policy->cpus) { struct cpu_dbs_info_s *j_dbs_info; j_dbs_info = &per_cpu(cs_cpu_dbs_info, j); @@ -651,7 +578,6 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, dbs_timer_exit(this_dbs_info); mutex_lock(&dbs_mutex); - sysfs_remove_group(&policy->kobj, &dbs_attr_group_old); dbs_enable--; mutex_destroy(&this_dbs_info->timer_mutex); diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 16471e5c55e75..e33f0b8c6e3ba 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -269,30 +269,6 @@ show_one(sampling_down_factor, sampling_down_factor); show_one(ignore_nice_load, ignore_nice); show_one(powersave_bias, powersave_bias); -/*** delete after deprecation time ***/ - -#define DEPRECATION_MSG(file_name) \ - printk_once(KERN_INFO "CPUFREQ: Per core ondemand sysfs " \ - "interface is deprecated - " #file_name "\n"); - -#define show_one_old(file_name) \ -static ssize_t show_##file_name##_old \ -(struct cpufreq_policy *unused, char *buf) \ -{ \ - printk_once(KERN_INFO "CPUFREQ: Per core ondemand sysfs " \ - "interface is deprecated - " #file_name "\n"); \ - return show_##file_name(NULL, NULL, buf); \ -} -show_one_old(sampling_rate); -show_one_old(up_threshold); -show_one_old(ignore_nice_load); -show_one_old(powersave_bias); -show_one_old(sampling_rate_min); - -cpufreq_freq_attr_ro_old(sampling_rate_min); - -/*** delete after deprecation time ***/ - static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, const char *buf, size_t count) { @@ -449,42 +425,6 @@ static struct attribute_group dbs_attr_group = { .name = "ondemand", }; -/*** delete after deprecation time ***/ - -#define write_one_old(file_name) \ -static ssize_t store_##file_name##_old \ -(struct cpufreq_policy *unused, const char *buf, size_t count) \ -{ \ - printk_once(KERN_INFO "CPUFREQ: Per core ondemand sysfs " \ - "interface is deprecated - " #file_name "\n"); \ - return store_##file_name(NULL, NULL, buf, count); \ -} -write_one_old(sampling_rate); -write_one_old(up_threshold); -write_one_old(ignore_nice_load); -write_one_old(powersave_bias); - -cpufreq_freq_attr_rw_old(sampling_rate); -cpufreq_freq_attr_rw_old(up_threshold); -cpufreq_freq_attr_rw_old(ignore_nice_load); -cpufreq_freq_attr_rw_old(powersave_bias); - -static struct attribute *dbs_attributes_old[] = { - &sampling_rate_min_old.attr, - &sampling_rate_old.attr, - &up_threshold_old.attr, - &ignore_nice_load_old.attr, - &powersave_bias_old.attr, - NULL -}; - -static struct attribute_group dbs_attr_group_old = { - .attrs = dbs_attributes_old, - .name = "ondemand", -}; - -/*** delete after deprecation time ***/ - /************************** sysfs end ************************/ static void dbs_freq_increase(struct cpufreq_policy *p, unsigned int freq) @@ -731,12 +671,6 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, mutex_lock(&dbs_mutex); - rc = sysfs_create_group(&policy->kobj, &dbs_attr_group_old); - if (rc) { - mutex_unlock(&dbs_mutex); - return rc; - } - dbs_enable++; for_each_cpu(j, policy->cpus) { struct cpu_dbs_info_s *j_dbs_info; @@ -789,7 +723,6 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, dbs_timer_exit(this_dbs_info); mutex_lock(&dbs_mutex); - sysfs_remove_group(&policy->kobj, &dbs_attr_group_old); mutex_destroy(&this_dbs_info->timer_mutex); dbs_enable--; mutex_unlock(&dbs_mutex); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index e71e0f6ecd58e..7b3046ce40b43 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -281,19 +281,10 @@ __ATTR(_name, 0444, show_##_name, NULL) static struct freq_attr _name = \ __ATTR(_name, _perm, show_##_name, NULL) -#define cpufreq_freq_attr_ro_old(_name) \ -static struct freq_attr _name##_old = \ -__ATTR(_name, 0444, show_##_name##_old, NULL) - #define cpufreq_freq_attr_rw(_name) \ static struct freq_attr _name = \ __ATTR(_name, 0644, show_##_name, store_##_name) -#define cpufreq_freq_attr_rw_old(_name) \ -static struct freq_attr _name##_old = \ -__ATTR(_name, 0644, show_##_name##_old, store_##_name##_old) - - struct global_attr { struct attribute attr; ssize_t (*show)(struct kobject *kobj, From 66026452c53a48cea46f29a8e74ca3410e619099 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Thu, 3 Mar 2011 21:31:27 +0100 Subject: [PATCH 2175/2556] [CPUFREQ] Remove unneeded locks There cannot be any concurrent access to these through different cpu sysfs files anymore, because these tunables are now all global (not per cpu). I still have some doubts whether some of these locks were needed at all. Anyway, let's get rid of them. Signed-off-by: Thomas Renninger Signed-off-by: Dave Jones CC: cpufreq@vger.kernel.org --- drivers/cpufreq/cpufreq_conservative.c | 34 ++++---------------------- drivers/cpufreq/cpufreq_ondemand.c | 25 +------------------ 2 files changed, 6 insertions(+), 53 deletions(-) diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 139bab3d6e93d..7e7a3e20f2c7e 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -76,8 +76,7 @@ static DEFINE_PER_CPU(struct cpu_dbs_info_s, cs_cpu_dbs_info); static unsigned int dbs_enable; /* number of CPUs using this policy */ /* - * dbs_mutex protects data in dbs_tuners_ins from concurrent changes on - * different CPUs. It protects dbs_enable in governor start/stop. + * dbs_mutex protects dbs_enable in governor start/stop. */ static DEFINE_MUTEX(dbs_mutex); @@ -195,10 +194,7 @@ static ssize_t store_sampling_down_factor(struct kobject *a, if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1) return -EINVAL; - mutex_lock(&dbs_mutex); dbs_tuners_ins.sampling_down_factor = input; - mutex_unlock(&dbs_mutex); - return count; } @@ -212,10 +208,7 @@ static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, if (ret != 1) return -EINVAL; - mutex_lock(&dbs_mutex); dbs_tuners_ins.sampling_rate = max(input, min_sampling_rate); - mutex_unlock(&dbs_mutex); - return count; } @@ -226,16 +219,11 @@ static ssize_t store_up_threshold(struct kobject *a, struct attribute *b, int ret; ret = sscanf(buf, "%u", &input); - mutex_lock(&dbs_mutex); if (ret != 1 || input > 100 || - input <= dbs_tuners_ins.down_threshold) { - mutex_unlock(&dbs_mutex); + input <= dbs_tuners_ins.down_threshold) return -EINVAL; - } dbs_tuners_ins.up_threshold = input; - mutex_unlock(&dbs_mutex); - return count; } @@ -246,17 +234,12 @@ static ssize_t store_down_threshold(struct kobject *a, struct attribute *b, int ret; ret = sscanf(buf, "%u", &input); - mutex_lock(&dbs_mutex); /* cannot be lower than 11 otherwise freq will not fall */ if (ret != 1 || input < 11 || input > 100 || - input >= dbs_tuners_ins.up_threshold) { - mutex_unlock(&dbs_mutex); + input >= dbs_tuners_ins.up_threshold) return -EINVAL; - } dbs_tuners_ins.down_threshold = input; - mutex_unlock(&dbs_mutex); - return count; } @@ -275,11 +258,9 @@ static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b, if (input > 1) input = 1; - mutex_lock(&dbs_mutex); - if (input == dbs_tuners_ins.ignore_nice) { /* nothing to do */ - mutex_unlock(&dbs_mutex); + if (input == dbs_tuners_ins.ignore_nice) /* nothing to do */ return count; - } + dbs_tuners_ins.ignore_nice = input; /* we need to re-evaluate prev_cpu_idle */ @@ -291,8 +272,6 @@ static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b, if (dbs_tuners_ins.ignore_nice) dbs_info->prev_cpu_nice = kstat_cpu(j).cpustat.nice; } - mutex_unlock(&dbs_mutex); - return count; } @@ -311,10 +290,7 @@ static ssize_t store_freq_step(struct kobject *a, struct attribute *b, /* no need to test here if freq_step is zero as the user might actually * want this, they would be crazy though :) */ - mutex_lock(&dbs_mutex); dbs_tuners_ins.freq_step = input; - mutex_unlock(&dbs_mutex); - return count; } diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index e33f0b8c6e3ba..004efb02db1ad 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -111,8 +111,7 @@ static DEFINE_PER_CPU(struct cpu_dbs_info_s, od_cpu_dbs_info); static unsigned int dbs_enable; /* number of CPUs using this policy */ /* - * dbs_mutex protects data in dbs_tuners_ins from concurrent changes on - * different CPUs. It protects dbs_enable in governor start/stop. + * dbs_mutex protects dbs_enable in governor start/stop. */ static DEFINE_MUTEX(dbs_mutex); @@ -277,11 +276,7 @@ static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, ret = sscanf(buf, "%u", &input); if (ret != 1) return -EINVAL; - - mutex_lock(&dbs_mutex); dbs_tuners_ins.sampling_rate = max(input, min_sampling_rate); - mutex_unlock(&dbs_mutex); - return count; } @@ -294,11 +289,7 @@ static ssize_t store_io_is_busy(struct kobject *a, struct attribute *b, ret = sscanf(buf, "%u", &input); if (ret != 1) return -EINVAL; - - mutex_lock(&dbs_mutex); dbs_tuners_ins.io_is_busy = !!input; - mutex_unlock(&dbs_mutex); - return count; } @@ -313,11 +304,7 @@ static ssize_t store_up_threshold(struct kobject *a, struct attribute *b, input < MIN_FREQUENCY_UP_THRESHOLD) { return -EINVAL; } - - mutex_lock(&dbs_mutex); dbs_tuners_ins.up_threshold = input; - mutex_unlock(&dbs_mutex); - return count; } @@ -330,7 +317,6 @@ static ssize_t store_sampling_down_factor(struct kobject *a, if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1) return -EINVAL; - mutex_lock(&dbs_mutex); dbs_tuners_ins.sampling_down_factor = input; /* Reset down sampling multiplier in case it was active */ @@ -339,8 +325,6 @@ static ssize_t store_sampling_down_factor(struct kobject *a, dbs_info = &per_cpu(od_cpu_dbs_info, j); dbs_info->rate_mult = 1; } - mutex_unlock(&dbs_mutex); - return count; } @@ -359,9 +343,7 @@ static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b, if (input > 1) input = 1; - mutex_lock(&dbs_mutex); if (input == dbs_tuners_ins.ignore_nice) { /* nothing to do */ - mutex_unlock(&dbs_mutex); return count; } dbs_tuners_ins.ignore_nice = input; @@ -376,8 +358,6 @@ static ssize_t store_ignore_nice_load(struct kobject *a, struct attribute *b, dbs_info->prev_cpu_nice = kstat_cpu(j).cpustat.nice; } - mutex_unlock(&dbs_mutex); - return count; } @@ -394,11 +374,8 @@ static ssize_t store_powersave_bias(struct kobject *a, struct attribute *b, if (input > 1000) input = 1000; - mutex_lock(&dbs_mutex); dbs_tuners_ins.powersave_bias = input; ondemand_powersave_bias_init(); - mutex_unlock(&dbs_mutex); - return count; } From df0b79988af221a32e3dc28dcb4af83e1b656e08 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 14 Nov 2010 19:04:23 -0800 Subject: [PATCH 2176/2556] [CPUFREQ] drivers/cpufreq: Remove unnecessary semicolons Signed-off-by: Joe Perches Signed-off-by: Dave Jones --- drivers/cpufreq/cpufreq_conservative.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 7e7a3e20f2c7e..33b56e5c5c14a 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -115,7 +115,7 @@ static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu, if (wall) *wall = (cputime64_t)jiffies_to_usecs(cur_wall_time); - return (cputime64_t)jiffies_to_usecs(idle_time);; + return (cputime64_t)jiffies_to_usecs(idle_time); } static inline cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall) From d09bc03f2305546c47103597ad2864a768b51e81 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 10 Mar 2011 21:13:05 +0100 Subject: [PATCH 2177/2556] [CPUFREQ] Remove the pm_message_t argument from driver suspend None of the existing cpufreq drivers uses the second argument of its .suspend() callback (which isn't useful anyway), so remove it. Signed-off-by: Rafael J. Wysocki Signed-off-by: Dave Jones --- arch/arm/mach-s5pv210/cpufreq.c | 3 +-- arch/arm/mach-s5pv310/cpufreq.c | 3 +-- arch/arm/plat-s3c24xx/cpu-freq.c | 2 +- arch/powerpc/platforms/powermac/cpufreq_32.c | 2 +- drivers/cpufreq/cpufreq.c | 2 +- include/linux/cpufreq.h | 2 +- 6 files changed, 6 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-s5pv210/cpufreq.c b/arch/arm/mach-s5pv210/cpufreq.c index a6f22920a2c20..22046e2f53c2a 100644 --- a/arch/arm/mach-s5pv210/cpufreq.c +++ b/arch/arm/mach-s5pv210/cpufreq.c @@ -390,8 +390,7 @@ static int s5pv210_target(struct cpufreq_policy *policy, } #ifdef CONFIG_PM -static int s5pv210_cpufreq_suspend(struct cpufreq_policy *policy, - pm_message_t pmsg) +static int s5pv210_cpufreq_suspend(struct cpufreq_policy *policy) { return 0; } diff --git a/arch/arm/mach-s5pv310/cpufreq.c b/arch/arm/mach-s5pv310/cpufreq.c index b04cbc731128f..7c08ad7d88872 100644 --- a/arch/arm/mach-s5pv310/cpufreq.c +++ b/arch/arm/mach-s5pv310/cpufreq.c @@ -458,8 +458,7 @@ static int s5pv310_target(struct cpufreq_policy *policy, } #ifdef CONFIG_PM -static int s5pv310_cpufreq_suspend(struct cpufreq_policy *policy, - pm_message_t pmsg) +static int s5pv310_cpufreq_suspend(struct cpufreq_policy *policy) { return 0; } diff --git a/arch/arm/plat-s3c24xx/cpu-freq.c b/arch/arm/plat-s3c24xx/cpu-freq.c index 25a8fc7f512e6..eea75ff81d15a 100644 --- a/arch/arm/plat-s3c24xx/cpu-freq.c +++ b/arch/arm/plat-s3c24xx/cpu-freq.c @@ -433,7 +433,7 @@ static int s3c_cpufreq_verify(struct cpufreq_policy *policy) static struct cpufreq_frequency_table suspend_pll; static unsigned int suspend_freq; -static int s3c_cpufreq_suspend(struct cpufreq_policy *policy, pm_message_t pmsg) +static int s3c_cpufreq_suspend(struct cpufreq_policy *policy) { suspend_pll.frequency = clk_get_rate(_clk_mpll); suspend_pll.index = __raw_readl(S3C2410_MPLLCON); diff --git a/arch/powerpc/platforms/powermac/cpufreq_32.c b/arch/powerpc/platforms/powermac/cpufreq_32.c index 415ca6d6b2739..04af5f48b4eb1 100644 --- a/arch/powerpc/platforms/powermac/cpufreq_32.c +++ b/arch/powerpc/platforms/powermac/cpufreq_32.c @@ -429,7 +429,7 @@ static u32 read_gpio(struct device_node *np) return offset; } -static int pmac_cpufreq_suspend(struct cpufreq_policy *policy, pm_message_t pmsg) +static int pmac_cpufreq_suspend(struct cpufreq_policy *policy) { /* Ok, this could be made a bit smarter, but let's be robust for now. We * always force a speed change to high speed before sleep, to make sure diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index f06a3b9b5f167..232b65d0666ef 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1387,7 +1387,7 @@ static int cpufreq_suspend(struct sys_device *sysdev, pm_message_t pmsg) goto out; if (cpufreq_driver->suspend) { - ret = cpufreq_driver->suspend(cpu_policy, pmsg); + ret = cpufreq_driver->suspend(cpu_policy); if (ret) printk(KERN_ERR "cpufreq: suspend failed in ->suspend " "step on CPU %u\n", cpu_policy->cpu); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 7b3046ce40b43..32fb6cae42945 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -230,7 +230,7 @@ struct cpufreq_driver { int (*bios_limit) (int cpu, unsigned int *limit); int (*exit) (struct cpufreq_policy *policy); - int (*suspend) (struct cpufreq_policy *policy, pm_message_t pmsg); + int (*suspend) (struct cpufreq_policy *policy); int (*resume) (struct cpufreq_policy *policy); struct freq_attr **attr; }; From ea822e689602a70940962540411cf422711d9a3c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 15 Mar 2011 00:43:46 +0100 Subject: [PATCH 2178/2556] PM / Core: Introduce struct syscore_ops for core subsystems PM Some subsystems need to carry out suspend/resume and shutdown operations with one CPU on-line and interrupts disabled. The only way to register such operations is to define a sysdev class and a sysdev specifically for this purpose which is cumbersome and inefficient. Moreover, the arguments taken by sysdev suspend, resume and shutdown callbacks are practically never necessary. For this reason, introduce a simpler interface allowing subsystems to register operations to be executed very late during system suspend and shutdown and very early during resume in the form of strcut syscore_ops objects. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- drivers/base/Makefile | 2 +- drivers/base/syscore.c | 117 ++++++++++++++++++++++++++++++++++++ include/linux/syscore_ops.h | 29 +++++++++ kernel/power/hibernate.c | 9 +++ kernel/power/suspend.c | 4 ++ kernel/sys.c | 4 ++ 6 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 drivers/base/syscore.c create mode 100644 include/linux/syscore_ops.h diff --git a/drivers/base/Makefile b/drivers/base/Makefile index d1bb577ebebb8..d3e2ec1324052 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -1,6 +1,6 @@ # Makefile for the Linux device tree -obj-y := core.o sys.o bus.o dd.o \ +obj-y := core.o sys.o bus.o dd.o syscore.o \ driver.o class.o platform.o \ cpu.o firmware.o init.o map.o devres.o \ attribute_container.o transport_class.o diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c new file mode 100644 index 0000000000000..90af2943f9e4a --- /dev/null +++ b/drivers/base/syscore.c @@ -0,0 +1,117 @@ +/* + * syscore.c - Execution of system core operations. + * + * Copyright (C) 2011 Rafael J. Wysocki , Novell Inc. + * + * This file is released under the GPLv2. + */ + +#include +#include +#include + +static LIST_HEAD(syscore_ops_list); +static DEFINE_MUTEX(syscore_ops_lock); + +/** + * register_syscore_ops - Register a set of system core operations. + * @ops: System core operations to register. + */ +void register_syscore_ops(struct syscore_ops *ops) +{ + mutex_lock(&syscore_ops_lock); + list_add_tail(&ops->node, &syscore_ops_list); + mutex_unlock(&syscore_ops_lock); +} +EXPORT_SYMBOL_GPL(register_syscore_ops); + +/** + * unregister_syscore_ops - Unregister a set of system core operations. + * @ops: System core operations to unregister. + */ +void unregister_syscore_ops(struct syscore_ops *ops) +{ + mutex_lock(&syscore_ops_lock); + list_del(&ops->node); + mutex_unlock(&syscore_ops_lock); +} +EXPORT_SYMBOL_GPL(unregister_syscore_ops); + +#ifdef CONFIG_PM_SLEEP +/** + * syscore_suspend - Execute all the registered system core suspend callbacks. + * + * This function is executed with one CPU on-line and disabled interrupts. + */ +int syscore_suspend(void) +{ + struct syscore_ops *ops; + int ret = 0; + + WARN_ONCE(!irqs_disabled(), + "Interrupts enabled before system core suspend.\n"); + + list_for_each_entry_reverse(ops, &syscore_ops_list, node) + if (ops->suspend) { + if (initcall_debug) + pr_info("PM: Calling %pF\n", ops->suspend); + ret = ops->suspend(); + if (ret) + goto err_out; + WARN_ONCE(!irqs_disabled(), + "Interrupts enabled after %pF\n", ops->suspend); + } + + return 0; + + err_out: + pr_err("PM: System core suspend callback %pF failed.\n", ops->suspend); + + list_for_each_entry_continue(ops, &syscore_ops_list, node) + if (ops->resume) + ops->resume(); + + return ret; +} + +/** + * syscore_resume - Execute all the registered system core resume callbacks. + * + * This function is executed with one CPU on-line and disabled interrupts. + */ +void syscore_resume(void) +{ + struct syscore_ops *ops; + + WARN_ONCE(!irqs_disabled(), + "Interrupts enabled before system core resume.\n"); + + list_for_each_entry(ops, &syscore_ops_list, node) + if (ops->resume) { + if (initcall_debug) + pr_info("PM: Calling %pF\n", ops->resume); + ops->resume(); + WARN_ONCE(!irqs_disabled(), + "Interrupts enabled after %pF\n", ops->resume); + } +} +#endif /* CONFIG_PM_SLEEP */ + +/** + * syscore_shutdown - Execute all the registered system core shutdown callbacks. + */ +void syscore_shutdown(void) +{ + struct syscore_ops *ops; + + mutex_lock(&syscore_ops_lock); + + list_for_each_entry_reverse(ops, &syscore_ops_list, node) + if (ops->shutdown) { + if (initcall_debug) + pr_info("PM: Calling %pF\n", ops->shutdown); + ops->shutdown(); + } + + mutex_unlock(&syscore_ops_lock); +} diff --git a/include/linux/syscore_ops.h b/include/linux/syscore_ops.h new file mode 100644 index 0000000000000..27b3b0bc41a94 --- /dev/null +++ b/include/linux/syscore_ops.h @@ -0,0 +1,29 @@ +/* + * syscore_ops.h - System core operations. + * + * Copyright (C) 2011 Rafael J. Wysocki , Novell Inc. + * + * This file is released under the GPLv2. + */ + +#ifndef _LINUX_SYSCORE_OPS_H +#define _LINUX_SYSCORE_OPS_H + +#include + +struct syscore_ops { + struct list_head node; + int (*suspend)(void); + void (*resume)(void); + void (*shutdown)(void); +}; + +extern void register_syscore_ops(struct syscore_ops *ops); +extern void unregister_syscore_ops(struct syscore_ops *ops); +#ifdef CONFIG_PM_SLEEP +extern int syscore_suspend(void); +extern void syscore_resume(void); +#endif +extern void syscore_shutdown(void); + +#endif diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 1832bd2642192..aeabd26e33427 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -272,6 +273,8 @@ static int create_image(int platform_mode) local_irq_disable(); error = sysdev_suspend(PMSG_FREEZE); + if (!error) + error = syscore_suspend(); if (error) { printk(KERN_ERR "PM: Some system devices failed to power down, " "aborting hibernation\n"); @@ -295,6 +298,7 @@ static int create_image(int platform_mode) } Power_up: + syscore_resume(); sysdev_resume(); /* NOTE: dpm_resume_noirq() is just a resume() for devices * that suspended with irqs off ... no overall powerup. @@ -403,6 +407,8 @@ static int resume_target_kernel(bool platform_mode) local_irq_disable(); error = sysdev_suspend(PMSG_QUIESCE); + if (!error) + error = syscore_suspend(); if (error) goto Enable_irqs; @@ -429,6 +435,7 @@ static int resume_target_kernel(bool platform_mode) restore_processor_state(); touch_softlockup_watchdog(); + syscore_resume(); sysdev_resume(); Enable_irqs: @@ -516,6 +523,7 @@ int hibernation_platform_enter(void) local_irq_disable(); sysdev_suspend(PMSG_HIBERNATE); + syscore_suspend(); if (pm_wakeup_pending()) { error = -EAGAIN; goto Power_up; @@ -526,6 +534,7 @@ int hibernation_platform_enter(void) while (1); Power_up: + syscore_resume(); sysdev_resume(); local_irq_enable(); enable_nonboot_cpus(); diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 1bb0e01b403c8..ccf4e7c2d3f41 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "power.h" @@ -166,11 +167,14 @@ static int suspend_enter(suspend_state_t state) BUG_ON(!irqs_disabled()); error = sysdev_suspend(PMSG_SUSPEND); + if (!error) + error = syscore_suspend(); if (!error) { if (!(suspend_test(TEST_CORE) || pm_wakeup_pending())) { error = suspend_ops->enter(state); events_check_enabled = false; } + syscore_resume(); sysdev_resume(); } diff --git a/kernel/sys.c b/kernel/sys.c index 18da702ec813c..1ad48b3b9068f 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -298,6 +299,7 @@ void kernel_restart_prepare(char *cmd) system_state = SYSTEM_RESTART; device_shutdown(); sysdev_shutdown(); + syscore_shutdown(); } /** @@ -336,6 +338,7 @@ void kernel_halt(void) { kernel_shutdown_prepare(SYSTEM_HALT); sysdev_shutdown(); + syscore_shutdown(); printk(KERN_EMERG "System halted.\n"); kmsg_dump(KMSG_DUMP_HALT); machine_halt(); @@ -355,6 +358,7 @@ void kernel_power_off(void) pm_power_off_prepare(); disable_nonboot_cpus(); sysdev_shutdown(); + syscore_shutdown(); printk(KERN_EMERG "Power down.\n"); kmsg_dump(KMSG_DUMP_POWEROFF); machine_power_off(); From 0f78feb08e38fa8e35619dc6acca6149e73b1fc0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 23 Mar 2011 22:16:32 +0100 Subject: [PATCH 2179/2556] cpufreq: Use syscore_ops for boot CPU suspend/resume (v2) The cpufreq subsystem uses sysdev suspend and resume for executing cpufreq_suspend() and cpufreq_resume(), respectively, during system suspend, after interrupts have been switched off on the boot CPU, and during system resume, while interrupts are still off on the boot CPU. In both cases the other CPUs are off-line at the relevant point (either they have been switched off via CPU hotplug during suspend, or they haven't been switched on yet during resume). For this reason, although it may seem that cpufreq_suspend() and cpufreq_resume() are executed for all CPUs in the system, they are only called for the boot CPU in fact, which is quite confusing. To remove the confusion and to prepare for elimiating sysdev suspend and resume operations from the kernel enirely, convernt cpufreq to using a struct syscore_ops object for the boot CPU suspend and resume and rename the callbacks so that their names reflect their purpose. In addition, put some explanatory remarks into their kerneldoc comments. Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 66 +++++++++++++++------------------------ 1 file changed, 26 insertions(+), 40 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 232b65d0666ef..d248ceb310dd2 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -1356,35 +1357,31 @@ unsigned int cpufreq_get(unsigned int cpu) } EXPORT_SYMBOL(cpufreq_get); +static struct sysdev_driver cpufreq_sysdev_driver = { + .add = cpufreq_add_dev, + .remove = cpufreq_remove_dev, +}; + /** - * cpufreq_suspend - let the low level driver prepare for suspend + * cpufreq_bp_suspend - Prepare the boot CPU for system suspend. + * + * This function is only executed for the boot processor. The other CPUs + * have been put offline by means of CPU hotplug. */ - -static int cpufreq_suspend(struct sys_device *sysdev, pm_message_t pmsg) +static int cpufreq_bp_suspend(void) { int ret = 0; - int cpu = sysdev->id; + int cpu = smp_processor_id(); struct cpufreq_policy *cpu_policy; dprintk("suspending cpu %u\n", cpu); - if (!cpu_online(cpu)) - return 0; - - /* we may be lax here as interrupts are off. Nonetheless - * we need to grab the correct cpu policy, as to check - * whether we really run on this CPU. - */ - + /* If there's no policy for the boot CPU, we have nothing to do. */ cpu_policy = cpufreq_cpu_get(cpu); if (!cpu_policy) - return -EINVAL; - - /* only handle each CPU group once */ - if (unlikely(cpu_policy->cpu != cpu)) - goto out; + return 0; if (cpufreq_driver->suspend) { ret = cpufreq_driver->suspend(cpu_policy); @@ -1393,13 +1390,12 @@ static int cpufreq_suspend(struct sys_device *sysdev, pm_message_t pmsg) "step on CPU %u\n", cpu_policy->cpu); } -out: cpufreq_cpu_put(cpu_policy); return ret; } /** - * cpufreq_resume - restore proper CPU frequency handling after resume + * cpufreq_bp_resume - Restore proper frequency handling of the boot CPU. * * 1.) resume CPUfreq hardware support (cpufreq_driver->resume()) * 2.) schedule call cpufreq_update_policy() ASAP as interrupts are @@ -1407,31 +1403,23 @@ static int cpufreq_suspend(struct sys_device *sysdev, pm_message_t pmsg) * what we believe it to be. This is a bit later than when it * should be, but nonethteless it's better than calling * cpufreq_driver->get() here which might re-enable interrupts... + * + * This function is only executed for the boot CPU. The other CPUs have not + * been turned on yet. */ -static int cpufreq_resume(struct sys_device *sysdev) +static void cpufreq_bp_resume(void) { int ret = 0; - int cpu = sysdev->id; + int cpu = smp_processor_id(); struct cpufreq_policy *cpu_policy; dprintk("resuming cpu %u\n", cpu); - if (!cpu_online(cpu)) - return 0; - - /* we may be lax here as interrupts are off. Nonetheless - * we need to grab the correct cpu policy, as to check - * whether we really run on this CPU. - */ - + /* If there's no policy for the boot CPU, we have nothing to do. */ cpu_policy = cpufreq_cpu_get(cpu); if (!cpu_policy) - return -EINVAL; - - /* only handle each CPU group once */ - if (unlikely(cpu_policy->cpu != cpu)) - goto fail; + return; if (cpufreq_driver->resume) { ret = cpufreq_driver->resume(cpu_policy); @@ -1446,14 +1434,11 @@ static int cpufreq_resume(struct sys_device *sysdev) fail: cpufreq_cpu_put(cpu_policy); - return ret; } -static struct sysdev_driver cpufreq_sysdev_driver = { - .add = cpufreq_add_dev, - .remove = cpufreq_remove_dev, - .suspend = cpufreq_suspend, - .resume = cpufreq_resume, +static struct syscore_ops cpufreq_syscore_ops = { + .suspend = cpufreq_bp_suspend, + .resume = cpufreq_bp_resume, }; @@ -2018,6 +2003,7 @@ static int __init cpufreq_core_init(void) cpufreq_global_kobject = kobject_create_and_add("cpufreq", &cpu_sysdev_class.kset.kobj); BUG_ON(!cpufreq_global_kobject); + register_syscore_ops(&cpufreq_syscore_ops); return 0; } From 2811cdb6fa421a25ba0247667e409766442190c2 Mon Sep 17 00:00:00 2001 From: Karthigan Srinivasan Date: Fri, 1 Apr 2011 17:34:47 -0500 Subject: [PATCH 2180/2556] [CPUFREQ] cpufreq_stats.c: Fixed brace coding style issue Fixed brace coding style issue. Signed-off-by: Karthigan Srinivasan Signed-off-by: Dave Jones --- drivers/cpufreq/cpufreq_stats.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index ebb536ce7acc7..46e60d798d57d 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -360,8 +360,7 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, } /* priority=1 so this will get called before cpufreq_remove_dev */ -static struct notifier_block cpufreq_stat_cpu_notifier __refdata = -{ +static struct notifier_block cpufreq_stat_cpu_notifier __refdata = { .notifier_call = cpufreq_stat_cpu_callback, .priority = 1, }; From d6df4166ae21b395520ea77797b8fce23a243866 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 4 May 2011 08:38:56 -0700 Subject: [PATCH 2181/2556] [CPUFREQ] remove redundant sprintf from request_module call. Since format string handling is part of request_module, there is no need to construct the module name. As such, drop the redundant sprintf and heap usage. Signed-off-by: Kees Cook Signed-off-by: Dave Jones --- drivers/cpufreq/cpufreq.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index d248ceb310dd2..d1ae4e54574ec 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -411,21 +411,14 @@ static int cpufreq_parse_governor(char *str_governor, unsigned int *policy, t = __find_governor(str_governor); if (t == NULL) { - char *name = kasprintf(GFP_KERNEL, "cpufreq_%s", - str_governor); + int ret; - if (name) { - int ret; + mutex_unlock(&cpufreq_governor_mutex); + ret = request_module("cpufreq_%s", str_governor); + mutex_lock(&cpufreq_governor_mutex); - mutex_unlock(&cpufreq_governor_mutex); - ret = request_module("%s", name); - mutex_lock(&cpufreq_governor_mutex); - - if (ret == 0) - t = __find_governor(str_governor); - } - - kfree(name); + if (ret == 0) + t = __find_governor(str_governor); } if (t != NULL) { From a040e4140d586144cb78a652be49cb6b782fbf23 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Sun, 12 Jun 2011 16:35:28 -0400 Subject: [PATCH 2182/2556] [CPUFREQ] Remove cpufreq_stats sysfs entries on module unload. cpufreq_stats leaves behind its sysfs entries, which causes a panic when something stumbled across them. (Discovered by unloading cpufreq_stats while powertop was loaded). Signed-off-by: Dave Jones Cc: stable@kernel.org --- drivers/cpufreq/cpufreq_stats.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 46e60d798d57d..39b00d362597b 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -409,6 +409,7 @@ static void __exit cpufreq_stats_exit(void) unregister_hotcpu_notifier(&cpufreq_stat_cpu_notifier); for_each_online_cpu(cpu) { cpufreq_stats_free_table(cpu); + cpufreq_stats_free_sysfs(cpu); } } From 24b1852825c239d23e7daaefdef637fbfb83c9f1 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Sun, 27 Mar 2011 15:04:46 +0200 Subject: [PATCH 2183/2556] [CPUFREQ] use dynamic debug instead of custom infrastructure With dynamic debug having gained the capability to report debug messages also during the boot process, it offers a far superior interface for debug messages than the custom cpufreq infrastructure. As a first step, remove the old cpufreq_debug_printk() function and replace it with a call to the generic pr_debug() function. How can dynamic debug be used on cpufreq? You need a kernel which has CONFIG_DYNAMIC_DEBUG enabled. To enabled debugging during runtime, mount debugfs and $ echo -n 'module cpufreq +p' > /sys/kernel/debug/dynamic_debug/control for debugging the complete "cpufreq" module. To achieve the same goal during boot, append ddebug_query="module cpufreq +p" as a boot parameter to the kernel of your choice. For more detailled instructions, please see Documentation/dynamic-debug-howto.txt Signed-off-by: Dominik Brodowski Signed-off-by: Dave Jones Conflicts: arch/x86/kernel/cpu/cpufreq/speedstep-smi.c Signed-off-by: Andrew Sutherland --- arch/arm/mach-davinci/cpufreq.c | 4 +- arch/blackfin/mach-common/dpmc.c | 3 - arch/ia64/kernel/cpufreq/acpi-cpufreq.c | 44 ++--- arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c | 45 ++--- arch/x86/kernel/cpu/cpufreq/cpufreq-nforce2.c | 6 +- arch/x86/kernel/cpu/cpufreq/gx-suspmod.c | 21 +- arch/x86/kernel/cpu/cpufreq/longhaul.c | 11 +- arch/x86/kernel/cpu/cpufreq/longrun.c | 17 +- arch/x86/kernel/cpu/cpufreq/p4-clockmod.c | 10 +- arch/x86/kernel/cpu/cpufreq/pcc-cpufreq.c | 47 +++-- arch/x86/kernel/cpu/cpufreq/powernow-k7.c | 33 ++-- arch/x86/kernel/cpu/cpufreq/powernow-k8.c | 100 +++++----- arch/x86/kernel/cpu/cpufreq/powernow-k8.h | 2 - arch/x86/kernel/cpu/cpufreq/sc520_freq.c | 6 +- .../kernel/cpu/cpufreq/speedstep-centrino.c | 23 +-- arch/x86/kernel/cpu/cpufreq/speedstep-ich.c | 28 ++- arch/x86/kernel/cpu/cpufreq/speedstep-lib.c | 43 ++--- arch/x86/kernel/cpu/cpufreq/speedstep-smi.c | 41 ++-- drivers/acpi/processor_perflib.c | 6 +- drivers/cpufreq/Kconfig | 13 -- drivers/cpufreq/cpufreq.c | 180 ++++-------------- drivers/cpufreq/cpufreq_performance.c | 5 +- drivers/cpufreq/cpufreq_powersave.c | 5 +- drivers/cpufreq/cpufreq_userspace.c | 13 +- drivers/cpufreq/freq_table.c | 19 +- include/linux/cpufreq.h | 19 -- 26 files changed, 270 insertions(+), 474 deletions(-) diff --git a/arch/arm/mach-davinci/cpufreq.c b/arch/arm/mach-davinci/cpufreq.c index 4a68c2b1ec11f..5f05e746fef35 100644 --- a/arch/arm/mach-davinci/cpufreq.c +++ b/arch/arm/mach-davinci/cpufreq.c @@ -94,9 +94,7 @@ static int davinci_target(struct cpufreq_policy *policy, if (freqs.old == freqs.new) return ret; - cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, - dev_driver_string(cpufreq.dev), - "transition: %u --> %u\n", freqs.old, freqs.new); + dev_dbg(&cpufreq.dev, "transition: %u --> %u\n", freqs.old, freqs.new); ret = cpufreq_frequency_table_target(policy, pdata->freq_table, freqs.new, relation, &idx); diff --git a/arch/blackfin/mach-common/dpmc.c b/arch/blackfin/mach-common/dpmc.c index 02c7efd1bcf48..a76071a4b2cf1 100644 --- a/arch/blackfin/mach-common/dpmc.c +++ b/arch/blackfin/mach-common/dpmc.c @@ -19,9 +19,6 @@ #define DRIVER_NAME "bfin dpmc" -#define dprintk(msg...) \ - cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, DRIVER_NAME, msg) - struct bfin_dpmc_platform_data *pdata; /** diff --git a/arch/ia64/kernel/cpufreq/acpi-cpufreq.c b/arch/ia64/kernel/cpufreq/acpi-cpufreq.c index 22f61526a8e1d..f09b174244d5b 100644 --- a/arch/ia64/kernel/cpufreq/acpi-cpufreq.c +++ b/arch/ia64/kernel/cpufreq/acpi-cpufreq.c @@ -23,8 +23,6 @@ #include #include -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "acpi-cpufreq", msg) - MODULE_AUTHOR("Venkatesh Pallipadi"); MODULE_DESCRIPTION("ACPI Processor P-States Driver"); MODULE_LICENSE("GPL"); @@ -47,12 +45,12 @@ processor_set_pstate ( { s64 retval; - dprintk("processor_set_pstate\n"); + pr_debug("processor_set_pstate\n"); retval = ia64_pal_set_pstate((u64)value); if (retval) { - dprintk("Failed to set freq to 0x%x, with error 0x%lx\n", + pr_debug("Failed to set freq to 0x%x, with error 0x%lx\n", value, retval); return -ENODEV; } @@ -67,14 +65,14 @@ processor_get_pstate ( u64 pstate_index = 0; s64 retval; - dprintk("processor_get_pstate\n"); + pr_debug("processor_get_pstate\n"); retval = ia64_pal_get_pstate(&pstate_index, PAL_GET_PSTATE_TYPE_INSTANT); *value = (u32) pstate_index; if (retval) - dprintk("Failed to get current freq with " + pr_debug("Failed to get current freq with " "error 0x%lx, idx 0x%x\n", retval, *value); return (int)retval; @@ -90,7 +88,7 @@ extract_clock ( { unsigned long i; - dprintk("extract_clock\n"); + pr_debug("extract_clock\n"); for (i = 0; i < data->acpi_data.state_count; i++) { if (value == data->acpi_data.states[i].status) @@ -110,7 +108,7 @@ processor_get_freq ( cpumask_t saved_mask; unsigned long clock_freq; - dprintk("processor_get_freq\n"); + pr_debug("processor_get_freq\n"); saved_mask = current->cpus_allowed; set_cpus_allowed_ptr(current, cpumask_of(cpu)); @@ -148,7 +146,7 @@ processor_set_freq ( cpumask_t saved_mask; int retval; - dprintk("processor_set_freq\n"); + pr_debug("processor_set_freq\n"); saved_mask = current->cpus_allowed; set_cpus_allowed_ptr(current, cpumask_of(cpu)); @@ -159,16 +157,16 @@ processor_set_freq ( if (state == data->acpi_data.state) { if (unlikely(data->resume)) { - dprintk("Called after resume, resetting to P%d\n", state); + pr_debug("Called after resume, resetting to P%d\n", state); data->resume = 0; } else { - dprintk("Already at target state (P%d)\n", state); + pr_debug("Already at target state (P%d)\n", state); retval = 0; goto migrate_end; } } - dprintk("Transitioning from P%d to P%d\n", + pr_debug("Transitioning from P%d to P%d\n", data->acpi_data.state, state); /* cpufreq frequency struct */ @@ -186,7 +184,7 @@ processor_set_freq ( value = (u32) data->acpi_data.states[state].control; - dprintk("Transitioning to state: 0x%08x\n", value); + pr_debug("Transitioning to state: 0x%08x\n", value); ret = processor_set_pstate(value); if (ret) { @@ -219,7 +217,7 @@ acpi_cpufreq_get ( { struct cpufreq_acpi_io *data = acpi_io_data[cpu]; - dprintk("acpi_cpufreq_get\n"); + pr_debug("acpi_cpufreq_get\n"); return processor_get_freq(data, cpu); } @@ -235,7 +233,7 @@ acpi_cpufreq_target ( unsigned int next_state = 0; unsigned int result = 0; - dprintk("acpi_cpufreq_setpolicy\n"); + pr_debug("acpi_cpufreq_setpolicy\n"); result = cpufreq_frequency_table_target(policy, data->freq_table, target_freq, relation, &next_state); @@ -255,7 +253,7 @@ acpi_cpufreq_verify ( unsigned int result = 0; struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu]; - dprintk("acpi_cpufreq_verify\n"); + pr_debug("acpi_cpufreq_verify\n"); result = cpufreq_frequency_table_verify(policy, data->freq_table); @@ -273,7 +271,7 @@ acpi_cpufreq_cpu_init ( struct cpufreq_acpi_io *data; unsigned int result = 0; - dprintk("acpi_cpufreq_cpu_init\n"); + pr_debug("acpi_cpufreq_cpu_init\n"); data = kzalloc(sizeof(struct cpufreq_acpi_io), GFP_KERNEL); if (!data) @@ -288,7 +286,7 @@ acpi_cpufreq_cpu_init ( /* capability check */ if (data->acpi_data.state_count <= 1) { - dprintk("No P-States\n"); + pr_debug("No P-States\n"); result = -ENODEV; goto err_unreg; } @@ -297,7 +295,7 @@ acpi_cpufreq_cpu_init ( ACPI_ADR_SPACE_FIXED_HARDWARE) || (data->acpi_data.status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) { - dprintk("Unsupported address space [%d, %d]\n", + pr_debug("Unsupported address space [%d, %d]\n", (u32) (data->acpi_data.control_register.space_id), (u32) (data->acpi_data.status_register.space_id)); result = -ENODEV; @@ -348,7 +346,7 @@ acpi_cpufreq_cpu_init ( "activated.\n", cpu); for (i = 0; i < data->acpi_data.state_count; i++) - dprintk(" %cP%d: %d MHz, %d mW, %d uS, %d uS, 0x%x 0x%x\n", + pr_debug(" %cP%d: %d MHz, %d mW, %d uS, %d uS, 0x%x 0x%x\n", (i == data->acpi_data.state?'*':' '), i, (u32) data->acpi_data.states[i].core_frequency, (u32) data->acpi_data.states[i].power, @@ -383,7 +381,7 @@ acpi_cpufreq_cpu_exit ( { struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu]; - dprintk("acpi_cpufreq_cpu_exit\n"); + pr_debug("acpi_cpufreq_cpu_exit\n"); if (data) { cpufreq_frequency_table_put_attr(policy->cpu); @@ -418,7 +416,7 @@ static struct cpufreq_driver acpi_cpufreq_driver = { static int __init acpi_cpufreq_init (void) { - dprintk("acpi_cpufreq_init\n"); + pr_debug("acpi_cpufreq_init\n"); return cpufreq_register_driver(&acpi_cpufreq_driver); } @@ -427,7 +425,7 @@ acpi_cpufreq_init (void) static void __exit acpi_cpufreq_exit (void) { - dprintk("acpi_cpufreq_exit\n"); + pr_debug("acpi_cpufreq_exit\n"); cpufreq_unregister_driver(&acpi_cpufreq_driver); return; diff --git a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c index a2baafb2fe6d3..4e04e12743887 100644 --- a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -47,9 +47,6 @@ #include #include "mperf.h" -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ - "acpi-cpufreq", msg) - MODULE_AUTHOR("Paul Diefenbaugh, Dominik Brodowski"); MODULE_DESCRIPTION("ACPI Processor P-States Driver"); MODULE_LICENSE("GPL"); @@ -233,7 +230,7 @@ static u32 get_cur_val(const struct cpumask *mask) cmd.mask = mask; drv_read(&cmd); - dprintk("get_cur_val = %u\n", cmd.val); + pr_debug("get_cur_val = %u\n", cmd.val); return cmd.val; } @@ -244,7 +241,7 @@ static unsigned int get_cur_freq_on_cpu(unsigned int cpu) unsigned int freq; unsigned int cached_freq; - dprintk("get_cur_freq_on_cpu (%d)\n", cpu); + pr_debug("get_cur_freq_on_cpu (%d)\n", cpu); if (unlikely(data == NULL || data->acpi_data == NULL || data->freq_table == NULL)) { @@ -261,7 +258,7 @@ static unsigned int get_cur_freq_on_cpu(unsigned int cpu) data->resume = 1; } - dprintk("cur freq = %u\n", freq); + pr_debug("cur freq = %u\n", freq); return freq; } @@ -293,7 +290,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, unsigned int i; int result = 0; - dprintk("acpi_cpufreq_target %d (%d)\n", target_freq, policy->cpu); + pr_debug("acpi_cpufreq_target %d (%d)\n", target_freq, policy->cpu); if (unlikely(data == NULL || data->acpi_data == NULL || data->freq_table == NULL)) { @@ -313,11 +310,11 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, next_perf_state = data->freq_table[next_state].index; if (perf->state == next_perf_state) { if (unlikely(data->resume)) { - dprintk("Called after resume, resetting to P%d\n", + pr_debug("Called after resume, resetting to P%d\n", next_perf_state); data->resume = 0; } else { - dprintk("Already at target state (P%d)\n", + pr_debug("Already at target state (P%d)\n", next_perf_state); goto out; } @@ -357,7 +354,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, if (acpi_pstate_strict) { if (!check_freqs(cmd.mask, freqs.new, data)) { - dprintk("acpi_cpufreq_target failed (%d)\n", + pr_debug("acpi_cpufreq_target failed (%d)\n", policy->cpu); result = -EAGAIN; goto out; @@ -378,7 +375,7 @@ static int acpi_cpufreq_verify(struct cpufreq_policy *policy) { struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu); - dprintk("acpi_cpufreq_verify\n"); + pr_debug("acpi_cpufreq_verify\n"); return cpufreq_frequency_table_verify(policy, data->freq_table); } @@ -433,11 +430,11 @@ static void free_acpi_perf_data(void) static int __init acpi_cpufreq_early_init(void) { unsigned int i; - dprintk("acpi_cpufreq_early_init\n"); + pr_debug("acpi_cpufreq_early_init\n"); acpi_perf_data = alloc_percpu(struct acpi_processor_performance); if (!acpi_perf_data) { - dprintk("Memory allocation error for acpi_perf_data.\n"); + pr_debug("Memory allocation error for acpi_perf_data.\n"); return -ENOMEM; } for_each_possible_cpu(i) { @@ -519,7 +516,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) static int blacklisted; #endif - dprintk("acpi_cpufreq_cpu_init\n"); + pr_debug("acpi_cpufreq_cpu_init\n"); #ifdef CONFIG_SMP if (blacklisted) @@ -566,7 +563,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) /* capability check */ if (perf->state_count <= 1) { - dprintk("No P-States\n"); + pr_debug("No P-States\n"); result = -ENODEV; goto err_unreg; } @@ -578,11 +575,11 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) switch (perf->control_register.space_id) { case ACPI_ADR_SPACE_SYSTEM_IO: - dprintk("SYSTEM IO addr space\n"); + pr_debug("SYSTEM IO addr space\n"); data->cpu_feature = SYSTEM_IO_CAPABLE; break; case ACPI_ADR_SPACE_FIXED_HARDWARE: - dprintk("HARDWARE addr space\n"); + pr_debug("HARDWARE addr space\n"); if (!check_est_cpu(cpu)) { result = -ENODEV; goto err_unreg; @@ -590,7 +587,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) data->cpu_feature = SYSTEM_INTEL_MSR_CAPABLE; break; default: - dprintk("Unknown addr space %d\n", + pr_debug("Unknown addr space %d\n", (u32) (perf->control_register.space_id)); result = -ENODEV; goto err_unreg; @@ -661,9 +658,9 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) if (cpu_has(c, X86_FEATURE_APERFMPERF)) acpi_cpufreq_driver.getavg = cpufreq_get_measured_perf; - dprintk("CPU%u - ACPI performance management activated.\n", cpu); + pr_debug("CPU%u - ACPI performance management activated.\n", cpu); for (i = 0; i < perf->state_count; i++) - dprintk(" %cP%d: %d MHz, %d mW, %d uS\n", + pr_debug(" %cP%d: %d MHz, %d mW, %d uS\n", (i == perf->state ? '*' : ' '), i, (u32) perf->states[i].core_frequency, (u32) perf->states[i].power, @@ -694,7 +691,7 @@ static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy) { struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu); - dprintk("acpi_cpufreq_cpu_exit\n"); + pr_debug("acpi_cpufreq_cpu_exit\n"); if (data) { cpufreq_frequency_table_put_attr(policy->cpu); @@ -712,7 +709,7 @@ static int acpi_cpufreq_resume(struct cpufreq_policy *policy) { struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu); - dprintk("acpi_cpufreq_resume\n"); + pr_debug("acpi_cpufreq_resume\n"); data->resume = 1; @@ -743,7 +740,7 @@ static int __init acpi_cpufreq_init(void) if (acpi_disabled) return 0; - dprintk("acpi_cpufreq_init\n"); + pr_debug("acpi_cpufreq_init\n"); ret = acpi_cpufreq_early_init(); if (ret) @@ -758,7 +755,7 @@ static int __init acpi_cpufreq_init(void) static void __exit acpi_cpufreq_exit(void) { - dprintk("acpi_cpufreq_exit\n"); + pr_debug("acpi_cpufreq_exit\n"); cpufreq_unregister_driver(&acpi_cpufreq_driver); diff --git a/arch/x86/kernel/cpu/cpufreq/cpufreq-nforce2.c b/arch/x86/kernel/cpu/cpufreq/cpufreq-nforce2.c index 141abebc4516a..7bac808804f3a 100644 --- a/arch/x86/kernel/cpu/cpufreq/cpufreq-nforce2.c +++ b/arch/x86/kernel/cpu/cpufreq/cpufreq-nforce2.c @@ -57,8 +57,6 @@ MODULE_PARM_DESC(min_fsb, "Minimum FSB to use, if not defined: current FSB - 50"); #define PFX "cpufreq-nforce2: " -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ - "cpufreq-nforce2", msg) /** * nforce2_calc_fsb - calculate FSB @@ -270,7 +268,7 @@ static int nforce2_target(struct cpufreq_policy *policy, if (freqs.old == freqs.new) return 0; - dprintk("Old CPU frequency %d kHz, new %d kHz\n", + pr_debug("Old CPU frequency %d kHz, new %d kHz\n", freqs.old, freqs.new); cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); @@ -282,7 +280,7 @@ static int nforce2_target(struct cpufreq_policy *policy, printk(KERN_ERR PFX "Changing FSB to %d failed\n", target_fsb); else - dprintk("Changed FSB successfully to %d\n", + pr_debug("Changed FSB successfully to %d\n", target_fsb); /* Enable IRQs */ diff --git a/arch/x86/kernel/cpu/cpufreq/gx-suspmod.c b/arch/x86/kernel/cpu/cpufreq/gx-suspmod.c index 32974cf84232f..ffe1f2c92ed3f 100644 --- a/arch/x86/kernel/cpu/cpufreq/gx-suspmod.c +++ b/arch/x86/kernel/cpu/cpufreq/gx-suspmod.c @@ -142,9 +142,6 @@ module_param(max_duration, int, 0444); #define POLICY_MIN_DIV 20 -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ - "gx-suspmod", msg) - /** * we can detect a core multipiler from dir0_lsb * from GX1 datasheet p.56, @@ -191,7 +188,7 @@ static __init struct pci_dev *gx_detect_chipset(void) /* check if CPU is a MediaGX or a Geode. */ if ((boot_cpu_data.x86_vendor != X86_VENDOR_NSC) && (boot_cpu_data.x86_vendor != X86_VENDOR_CYRIX)) { - dprintk("error: no MediaGX/Geode processor found!\n"); + pr_debug("error: no MediaGX/Geode processor found!\n"); return NULL; } @@ -201,7 +198,7 @@ static __init struct pci_dev *gx_detect_chipset(void) return gx_pci; } - dprintk("error: no supported chipset found!\n"); + pr_debug("error: no supported chipset found!\n"); return NULL; } @@ -305,14 +302,14 @@ static void gx_set_cpuspeed(unsigned int khz) break; default: local_irq_restore(flags); - dprintk("fatal: try to set unknown chipset.\n"); + pr_debug("fatal: try to set unknown chipset.\n"); return; } } else { suscfg = gx_params->pci_suscfg & ~(SUSMOD); gx_params->off_duration = 0; gx_params->on_duration = 0; - dprintk("suspend modulation disabled: cpu runs 100%% speed.\n"); + pr_debug("suspend modulation disabled: cpu runs 100%% speed.\n"); } gx_write_byte(PCI_MODOFF, gx_params->off_duration); @@ -327,9 +324,9 @@ static void gx_set_cpuspeed(unsigned int khz) cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); - dprintk("suspend modulation w/ duration of ON:%d us, OFF:%d us\n", + pr_debug("suspend modulation w/ duration of ON:%d us, OFF:%d us\n", gx_params->on_duration * 32, gx_params->off_duration * 32); - dprintk("suspend modulation w/ clock speed: %d kHz.\n", freqs.new); + pr_debug("suspend modulation w/ clock speed: %d kHz.\n", freqs.new); } /**************************************************************** @@ -428,8 +425,8 @@ static int cpufreq_gx_cpu_init(struct cpufreq_policy *policy) stock_freq = maxfreq; curfreq = gx_get_cpuspeed(0); - dprintk("cpu max frequency is %d.\n", maxfreq); - dprintk("cpu current frequency is %dkHz.\n", curfreq); + pr_debug("cpu max frequency is %d.\n", maxfreq); + pr_debug("cpu current frequency is %dkHz.\n", curfreq); /* setup basic struct for cpufreq API */ policy->cpu = 0; @@ -475,7 +472,7 @@ static int __init cpufreq_gx_init(void) if (max_duration > 0xff) max_duration = 0xff; - dprintk("geode suspend modulation available.\n"); + pr_debug("geode suspend modulation available.\n"); params = kzalloc(sizeof(struct gxfreq_params), GFP_KERNEL); if (params == NULL) diff --git a/arch/x86/kernel/cpu/cpufreq/longhaul.c b/arch/x86/kernel/cpu/cpufreq/longhaul.c index 03162dac6271d..7ef3b1b439ba9 100644 --- a/arch/x86/kernel/cpu/cpufreq/longhaul.c +++ b/arch/x86/kernel/cpu/cpufreq/longhaul.c @@ -77,9 +77,6 @@ static int scale_voltage; static int disable_acpi_c3; static int revid_errata; -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ - "longhaul", msg) - /* Clock ratios multiplied by 10 */ static int mults[32]; @@ -87,7 +84,6 @@ static int eblcr[32]; static int longhaul_version; static struct cpufreq_frequency_table *longhaul_table; -#ifdef CONFIG_CPU_FREQ_DEBUG static char speedbuffer[8]; static char *print_speed(int speed) @@ -106,7 +102,6 @@ static char *print_speed(int speed) return speedbuffer; } -#endif static unsigned int calc_speed(int mult) @@ -275,7 +270,7 @@ static void longhaul_setstate(unsigned int table_index) cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - dprintk("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n", + pr_debug("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n", fsb, mult/10, mult%10, print_speed(speed/1000)); retry_loop: preempt_disable(); @@ -460,12 +455,12 @@ static int __cpuinit longhaul_get_ranges(void) break; } - dprintk("MinMult:%d.%dx MaxMult:%d.%dx\n", + pr_debug("MinMult:%d.%dx MaxMult:%d.%dx\n", minmult/10, minmult%10, maxmult/10, maxmult%10); highest_speed = calc_speed(maxmult); lowest_speed = calc_speed(minmult); - dprintk("FSB:%dMHz Lowest speed: %s Highest speed:%s\n", fsb, + pr_debug("FSB:%dMHz Lowest speed: %s Highest speed:%s\n", fsb, print_speed(lowest_speed/1000), print_speed(highest_speed/1000)); diff --git a/arch/x86/kernel/cpu/cpufreq/longrun.c b/arch/x86/kernel/cpu/cpufreq/longrun.c index d9f51367666b0..34ea359b370eb 100644 --- a/arch/x86/kernel/cpu/cpufreq/longrun.c +++ b/arch/x86/kernel/cpu/cpufreq/longrun.c @@ -15,9 +15,6 @@ #include #include -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ - "longrun", msg) - static struct cpufreq_driver longrun_driver; /** @@ -40,14 +37,14 @@ static void __cpuinit longrun_get_policy(struct cpufreq_policy *policy) u32 msr_lo, msr_hi; rdmsr(MSR_TMTA_LONGRUN_FLAGS, msr_lo, msr_hi); - dprintk("longrun flags are %x - %x\n", msr_lo, msr_hi); + pr_debug("longrun flags are %x - %x\n", msr_lo, msr_hi); if (msr_lo & 0x01) policy->policy = CPUFREQ_POLICY_PERFORMANCE; else policy->policy = CPUFREQ_POLICY_POWERSAVE; rdmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi); - dprintk("longrun ctrl is %x - %x\n", msr_lo, msr_hi); + pr_debug("longrun ctrl is %x - %x\n", msr_lo, msr_hi); msr_lo &= 0x0000007F; msr_hi &= 0x0000007F; @@ -150,7 +147,7 @@ static unsigned int longrun_get(unsigned int cpu) return 0; cpuid(0x80860007, &eax, &ebx, &ecx, &edx); - dprintk("cpuid eax is %u\n", eax); + pr_debug("cpuid eax is %u\n", eax); return eax * 1000; } @@ -196,7 +193,7 @@ static int __cpuinit longrun_determine_freqs(unsigned int *low_freq, rdmsr(MSR_TMTA_LRTI_VOLT_MHZ, msr_lo, msr_hi); *high_freq = msr_lo * 1000; /* to kHz */ - dprintk("longrun table interface told %u - %u kHz\n", + pr_debug("longrun table interface told %u - %u kHz\n", *low_freq, *high_freq); if (*low_freq > *high_freq) @@ -207,7 +204,7 @@ static int __cpuinit longrun_determine_freqs(unsigned int *low_freq, /* set the upper border to the value determined during TSC init */ *high_freq = (cpu_khz / 1000); *high_freq = *high_freq * 1000; - dprintk("high frequency is %u kHz\n", *high_freq); + pr_debug("high frequency is %u kHz\n", *high_freq); /* get current borders */ rdmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi); @@ -233,7 +230,7 @@ static int __cpuinit longrun_determine_freqs(unsigned int *low_freq, /* restore values */ wrmsr(MSR_TMTA_LONGRUN_CTRL, save_lo, save_hi); } - dprintk("percentage is %u %%, freq is %u MHz\n", ecx, eax); + pr_debug("percentage is %u %%, freq is %u MHz\n", ecx, eax); /* performance_pctg = (current_freq - low_freq)/(high_freq - low_freq) * eqals @@ -249,7 +246,7 @@ static int __cpuinit longrun_determine_freqs(unsigned int *low_freq, edx = ((eax - ebx) * 100) / (100 - ecx); *low_freq = edx * 1000; /* back to kHz */ - dprintk("low frequency is %u kHz\n", *low_freq); + pr_debug("low frequency is %u kHz\n", *low_freq); if (*low_freq > *high_freq) *low_freq = *high_freq; diff --git a/arch/x86/kernel/cpu/cpufreq/p4-clockmod.c b/arch/x86/kernel/cpu/cpufreq/p4-clockmod.c index 52c93648e492f..6be3e0760c26f 100644 --- a/arch/x86/kernel/cpu/cpufreq/p4-clockmod.c +++ b/arch/x86/kernel/cpu/cpufreq/p4-clockmod.c @@ -35,8 +35,6 @@ #include "speedstep-lib.h" #define PFX "p4-clockmod: " -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ - "p4-clockmod", msg) /* * Duty Cycle (3bits), note DC_DISABLE is not specified in @@ -66,7 +64,7 @@ static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate) rdmsr_on_cpu(cpu, MSR_IA32_THERM_STATUS, &l, &h); if (l & 0x01) - dprintk("CPU#%d currently thermal throttled\n", cpu); + pr_debug("CPU#%d currently thermal throttled\n", cpu); if (has_N44_O17_errata[cpu] && (newstate == DC_25PT || newstate == DC_DFLT)) @@ -74,10 +72,10 @@ static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate) rdmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, &l, &h); if (newstate == DC_DISABLE) { - dprintk("CPU#%d disabling modulation\n", cpu); + pr_debug("CPU#%d disabling modulation\n", cpu); wrmsr_on_cpu(cpu, MSR_IA32_THERM_CONTROL, l & ~(1<<4), h); } else { - dprintk("CPU#%d setting duty cycle to %d%%\n", + pr_debug("CPU#%d setting duty cycle to %d%%\n", cpu, ((125 * newstate) / 10)); /* bits 63 - 5 : reserved * bit 4 : enable/disable @@ -217,7 +215,7 @@ static int cpufreq_p4_cpu_init(struct cpufreq_policy *policy) case 0x0f11: case 0x0f12: has_N44_O17_errata[policy->cpu] = 1; - dprintk("has errata -- disabling low frequencies\n"); + pr_debug("has errata -- disabling low frequencies\n"); } if (speedstep_detect_processor() == SPEEDSTEP_CPU_P4D && diff --git a/arch/x86/kernel/cpu/cpufreq/pcc-cpufreq.c b/arch/x86/kernel/cpu/cpufreq/pcc-cpufreq.c index 500a242853e7b..f34ac52b9b8d7 100644 --- a/arch/x86/kernel/cpu/cpufreq/pcc-cpufreq.c +++ b/arch/x86/kernel/cpu/cpufreq/pcc-cpufreq.c @@ -48,9 +48,6 @@ #define BUF_SZ 4 -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ - "pcc-cpufreq", msg) - struct pcc_register_resource { u8 descriptor; u16 length; @@ -152,7 +149,7 @@ static unsigned int pcc_get_freq(unsigned int cpu) spin_lock(&pcc_lock); - dprintk("get: get_freq for CPU %d\n", cpu); + pr_debug("get: get_freq for CPU %d\n", cpu); pcc_cpu_data = per_cpu_ptr(pcc_cpu_info, cpu); input_buffer = 0x1; @@ -170,7 +167,7 @@ static unsigned int pcc_get_freq(unsigned int cpu) status = ioread16(&pcch_hdr->status); if (status != CMD_COMPLETE) { - dprintk("get: FAILED: for CPU %d, status is %d\n", + pr_debug("get: FAILED: for CPU %d, status is %d\n", cpu, status); goto cmd_incomplete; } @@ -178,14 +175,14 @@ static unsigned int pcc_get_freq(unsigned int cpu) curr_freq = (((ioread32(&pcch_hdr->nominal) * (output_buffer & 0xff)) / 100) * 1000); - dprintk("get: SUCCESS: (virtual) output_offset for cpu %d is " - "0x%x, contains a value of: 0x%x. Speed is: %d MHz\n", + pr_debug("get: SUCCESS: (virtual) output_offset for cpu %d is " + "0x%p, contains a value of: 0x%x. Speed is: %d MHz\n", cpu, (pcch_virt_addr + pcc_cpu_data->output_offset), output_buffer, curr_freq); freq_limit = (output_buffer >> 8) & 0xff; if (freq_limit != 0xff) { - dprintk("get: frequency for cpu %d is being temporarily" + pr_debug("get: frequency for cpu %d is being temporarily" " capped at %d\n", cpu, curr_freq); } @@ -212,8 +209,8 @@ static int pcc_cpufreq_target(struct cpufreq_policy *policy, cpu = policy->cpu; pcc_cpu_data = per_cpu_ptr(pcc_cpu_info, cpu); - dprintk("target: CPU %d should go to target freq: %d " - "(virtual) input_offset is 0x%x\n", + pr_debug("target: CPU %d should go to target freq: %d " + "(virtual) input_offset is 0x%p\n", cpu, target_freq, (pcch_virt_addr + pcc_cpu_data->input_offset)); @@ -234,14 +231,14 @@ static int pcc_cpufreq_target(struct cpufreq_policy *policy, status = ioread16(&pcch_hdr->status); if (status != CMD_COMPLETE) { - dprintk("target: FAILED for cpu %d, with status: 0x%x\n", + pr_debug("target: FAILED for cpu %d, with status: 0x%x\n", cpu, status); goto cmd_incomplete; } iowrite16(0, &pcch_hdr->status); cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); - dprintk("target: was SUCCESSFUL for cpu %d\n", cpu); + pr_debug("target: was SUCCESSFUL for cpu %d\n", cpu); spin_unlock(&pcc_lock); return 0; @@ -293,7 +290,7 @@ static int pcc_get_offset(int cpu) memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ); memset_io((pcch_virt_addr + pcc_cpu_data->output_offset), 0, BUF_SZ); - dprintk("pcc_get_offset: for CPU %d: pcc_cpu_data " + pr_debug("pcc_get_offset: for CPU %d: pcc_cpu_data " "input_offset: 0x%x, pcc_cpu_data output_offset: 0x%x\n", cpu, pcc_cpu_data->input_offset, pcc_cpu_data->output_offset); out_free: @@ -412,7 +409,7 @@ static int __init pcc_cpufreq_probe(void) if (ACPI_SUCCESS(status)) { ret = pcc_cpufreq_do_osc(&osc_handle); if (ret) - dprintk("probe: _OSC evaluation did not succeed\n"); + pr_debug("probe: _OSC evaluation did not succeed\n"); /* Firmware's use of _OSC is optional */ ret = 0; } @@ -435,7 +432,7 @@ static int __init pcc_cpufreq_probe(void) mem_resource = (struct pcc_memory_resource *)member->buffer.pointer; - dprintk("probe: mem_resource descriptor: 0x%x," + pr_debug("probe: mem_resource descriptor: 0x%x," " length: %d, space_id: %d, resource_usage: %d," " type_specific: %d, granularity: 0x%llx," " minimum: 0x%llx, maximum: 0x%llx," @@ -455,13 +452,13 @@ static int __init pcc_cpufreq_probe(void) pcch_virt_addr = ioremap_nocache(mem_resource->minimum, mem_resource->address_length); if (pcch_virt_addr == NULL) { - dprintk("probe: could not map shared mem region\n"); + pr_debug("probe: could not map shared mem region\n"); goto out_free; } pcch_hdr = pcch_virt_addr; - dprintk("probe: PCCH header (virtual) addr: 0x%p\n", pcch_hdr); - dprintk("probe: PCCH header is at physical address: 0x%llx," + pr_debug("probe: PCCH header (virtual) addr: 0x%p\n", pcch_hdr); + pr_debug("probe: PCCH header is at physical address: 0x%llx," " signature: 0x%x, length: %d bytes, major: %d, minor: %d," " supported features: 0x%x, command field: 0x%x," " status field: 0x%x, nominal latency: %d us\n", @@ -471,7 +468,7 @@ static int __init pcc_cpufreq_probe(void) ioread16(&pcch_hdr->command), ioread16(&pcch_hdr->status), ioread32(&pcch_hdr->latency)); - dprintk("probe: min time between commands: %d us," + pr_debug("probe: min time between commands: %d us," " max time between commands: %d us," " nominal CPU frequency: %d MHz," " minimum CPU frequency: %d MHz," @@ -496,7 +493,7 @@ static int __init pcc_cpufreq_probe(void) doorbell.access_width = 64; doorbell.address = reg_resource->address; - dprintk("probe: doorbell: space_id is %d, bit_width is %d, " + pr_debug("probe: doorbell: space_id is %d, bit_width is %d, " "bit_offset is %d, access_width is %d, address is 0x%llx\n", doorbell.space_id, doorbell.bit_width, doorbell.bit_offset, doorbell.access_width, reg_resource->address); @@ -517,7 +514,7 @@ static int __init pcc_cpufreq_probe(void) doorbell_write = member->integer.value; - dprintk("probe: doorbell_preserve: 0x%llx," + pr_debug("probe: doorbell_preserve: 0x%llx," " doorbell_write: 0x%llx\n", doorbell_preserve, doorbell_write); @@ -552,7 +549,7 @@ static int pcc_cpufreq_cpu_init(struct cpufreq_policy *policy) result = pcc_get_offset(cpu); if (result) { - dprintk("init: PCCP evaluation failed\n"); + pr_debug("init: PCCP evaluation failed\n"); goto out; } @@ -563,12 +560,12 @@ static int pcc_cpufreq_cpu_init(struct cpufreq_policy *policy) policy->cur = pcc_get_freq(cpu); if (!policy->cur) { - dprintk("init: Unable to get current CPU frequency\n"); + pr_debug("init: Unable to get current CPU frequency\n"); result = -EINVAL; goto out; } - dprintk("init: policy->max is %d, policy->min is %d\n", + pr_debug("init: policy->max is %d, policy->min is %d\n", policy->max, policy->min); out: return result; @@ -599,7 +596,7 @@ static int __init pcc_cpufreq_init(void) ret = pcc_cpufreq_probe(); if (ret) { - dprintk("pcc_cpufreq_init: PCCH evaluation failed\n"); + pr_debug("pcc_cpufreq_init: PCCH evaluation failed\n"); return ret; } diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k7.c b/arch/x86/kernel/cpu/cpufreq/powernow-k7.c index 4a45fd6e41ba9..d71d9f3723590 100644 --- a/arch/x86/kernel/cpu/cpufreq/powernow-k7.c +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k7.c @@ -68,7 +68,6 @@ union powernow_acpi_control_t { }; #endif -#ifdef CONFIG_CPU_FREQ_DEBUG /* divide by 1000 to get VCore voltage in V. */ static const int mobile_vid_table[32] = { 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650, @@ -76,7 +75,6 @@ static const int mobile_vid_table[32] = { 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100, 1075, 1050, 1025, 1000, 975, 950, 925, 0, }; -#endif /* divide by 10 to get FID. */ static const int fid_codes[32] = { @@ -103,9 +101,6 @@ static unsigned int fsb; static unsigned int latency; static char have_a0; -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ - "powernow-k7", msg) - static int check_fsb(unsigned int fsbspeed) { int delta; @@ -209,7 +204,7 @@ static int get_ranges(unsigned char *pst) vid = *pst++; powernow_table[j].index |= (vid << 8); /* upper 8 bits */ - dprintk(" FID: 0x%x (%d.%dx [%dMHz]) " + pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) " "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10, fid_codes[fid] % 10, speed/1000, vid, mobile_vid_table[vid]/1000, @@ -367,7 +362,7 @@ static int powernow_acpi_init(void) unsigned int speed, speed_mhz; pc.val = (unsigned long) state->control; - dprintk("acpi: P%d: %d MHz %d mW %d uS control %08x SGTC %d\n", + pr_debug("acpi: P%d: %d MHz %d mW %d uS control %08x SGTC %d\n", i, (u32) state->core_frequency, (u32) state->power, @@ -401,7 +396,7 @@ static int powernow_acpi_init(void) invalidate_entry(i); } - dprintk(" FID: 0x%x (%d.%dx [%dMHz]) " + pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) " "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10, fid_codes[fid] % 10, speed_mhz, vid, mobile_vid_table[vid]/1000, @@ -409,7 +404,7 @@ static int powernow_acpi_init(void) if (state->core_frequency != speed_mhz) { state->core_frequency = speed_mhz; - dprintk(" Corrected ACPI frequency to %d\n", + pr_debug(" Corrected ACPI frequency to %d\n", speed_mhz); } @@ -453,8 +448,8 @@ static int powernow_acpi_init(void) static void print_pst_entry(struct pst_s *pst, unsigned int j) { - dprintk("PST:%d (@%p)\n", j, pst); - dprintk(" cpuid: 0x%x fsb: %d maxFID: 0x%x startvid: 0x%x\n", + pr_debug("PST:%d (@%p)\n", j, pst); + pr_debug(" cpuid: 0x%x fsb: %d maxFID: 0x%x startvid: 0x%x\n", pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid); } @@ -474,20 +469,20 @@ static int powernow_decode_bios(int maxfid, int startvid) p = phys_to_virt(i); if (memcmp(p, "AMDK7PNOW!", 10) == 0) { - dprintk("Found PSB header at %p\n", p); + pr_debug("Found PSB header at %p\n", p); psb = (struct psb_s *) p; - dprintk("Table version: 0x%x\n", psb->tableversion); + pr_debug("Table version: 0x%x\n", psb->tableversion); if (psb->tableversion != 0x12) { printk(KERN_INFO PFX "Sorry, only v1.2 tables" " supported right now\n"); return -ENODEV; } - dprintk("Flags: 0x%x\n", psb->flags); + pr_debug("Flags: 0x%x\n", psb->flags); if ((psb->flags & 1) == 0) - dprintk("Mobile voltage regulator\n"); + pr_debug("Mobile voltage regulator\n"); else - dprintk("Desktop voltage regulator\n"); + pr_debug("Desktop voltage regulator\n"); latency = psb->settlingtime; if (latency < 100) { @@ -497,9 +492,9 @@ static int powernow_decode_bios(int maxfid, int startvid) "Correcting.\n", latency); latency = 100; } - dprintk("Settling Time: %d microseconds.\n", + pr_debug("Settling Time: %d microseconds.\n", psb->settlingtime); - dprintk("Has %d PST tables. (Only dumping ones " + pr_debug("Has %d PST tables. (Only dumping ones " "relevant to this CPU).\n", psb->numpst); @@ -650,7 +645,7 @@ static int __cpuinit powernow_cpu_init(struct cpufreq_policy *policy) printk(KERN_WARNING PFX "can not determine bus frequency\n"); return -EINVAL; } - dprintk("FSB: %3dMHz\n", fsb/1000); + pr_debug("FSB: %3dMHz\n", fsb/1000); if (dmi_check_system(powernow_dmi_table) || acpi_force) { printk(KERN_INFO PFX "PSB/PST known to be broken. " diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c index c567dec854f69..19db92af94986 100644 --- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c @@ -139,7 +139,7 @@ static int query_current_values_with_pending_wait(struct powernow_k8_data *data) } do { if (i++ > 10000) { - dprintk("detected change pending stuck\n"); + pr_debug("detected change pending stuck\n"); return 1; } rdmsr(MSR_FIDVID_STATUS, lo, hi); @@ -176,7 +176,7 @@ static void fidvid_msr_init(void) fid = lo & MSR_S_LO_CURRENT_FID; lo = fid | (vid << MSR_C_LO_VID_SHIFT); hi = MSR_C_HI_STP_GNT_BENIGN; - dprintk("cpu%d, init lo 0x%x, hi 0x%x\n", smp_processor_id(), lo, hi); + pr_debug("cpu%d, init lo 0x%x, hi 0x%x\n", smp_processor_id(), lo, hi); wrmsr(MSR_FIDVID_CTL, lo, hi); } @@ -196,7 +196,7 @@ static int write_new_fid(struct powernow_k8_data *data, u32 fid) lo |= (data->currvid << MSR_C_LO_VID_SHIFT); lo |= MSR_C_LO_INIT_FID_VID; - dprintk("writing fid 0x%x, lo 0x%x, hi 0x%x\n", + pr_debug("writing fid 0x%x, lo 0x%x, hi 0x%x\n", fid, lo, data->plllock * PLL_LOCK_CONVERSION); do { @@ -244,7 +244,7 @@ static int write_new_vid(struct powernow_k8_data *data, u32 vid) lo |= (vid << MSR_C_LO_VID_SHIFT); lo |= MSR_C_LO_INIT_FID_VID; - dprintk("writing vid 0x%x, lo 0x%x, hi 0x%x\n", + pr_debug("writing vid 0x%x, lo 0x%x, hi 0x%x\n", vid, lo, STOP_GRANT_5NS); do { @@ -325,7 +325,7 @@ static int transition_fid_vid(struct powernow_k8_data *data, return 1; } - dprintk("transitioned (cpu%d): new fid 0x%x, vid 0x%x\n", + pr_debug("transitioned (cpu%d): new fid 0x%x, vid 0x%x\n", smp_processor_id(), data->currfid, data->currvid); return 0; @@ -339,7 +339,7 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 savefid = data->currfid; u32 maxvid, lo, rvomult = 1; - dprintk("ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, " + pr_debug("ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, " "reqvid 0x%x, rvo 0x%x\n", smp_processor_id(), data->currfid, data->currvid, reqvid, data->rvo); @@ -349,12 +349,12 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, rvosteps *= rvomult; rdmsr(MSR_FIDVID_STATUS, lo, maxvid); maxvid = 0x1f & (maxvid >> 16); - dprintk("ph1 maxvid=0x%x\n", maxvid); + pr_debug("ph1 maxvid=0x%x\n", maxvid); if (reqvid < maxvid) /* lower numbers are higher voltages */ reqvid = maxvid; while (data->currvid > reqvid) { - dprintk("ph1: curr 0x%x, req vid 0x%x\n", + pr_debug("ph1: curr 0x%x, req vid 0x%x\n", data->currvid, reqvid); if (decrease_vid_code_by_step(data, reqvid, data->vidmvs)) return 1; @@ -365,7 +365,7 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, if (data->currvid == maxvid) { rvosteps = 0; } else { - dprintk("ph1: changing vid for rvo, req 0x%x\n", + pr_debug("ph1: changing vid for rvo, req 0x%x\n", data->currvid - 1); if (decrease_vid_code_by_step(data, data->currvid-1, 1)) return 1; @@ -382,7 +382,7 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, return 1; } - dprintk("ph1 complete, currfid 0x%x, currvid 0x%x\n", + pr_debug("ph1 complete, currfid 0x%x, currvid 0x%x\n", data->currfid, data->currvid); return 0; @@ -400,7 +400,7 @@ static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid) return 0; } - dprintk("ph2 (cpu%d): starting, currfid 0x%x, currvid 0x%x, " + pr_debug("ph2 (cpu%d): starting, currfid 0x%x, currvid 0x%x, " "reqfid 0x%x\n", smp_processor_id(), data->currfid, data->currvid, reqfid); @@ -457,7 +457,7 @@ static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid) return 1; } - dprintk("ph2 complete, currfid 0x%x, currvid 0x%x\n", + pr_debug("ph2 complete, currfid 0x%x, currvid 0x%x\n", data->currfid, data->currvid); return 0; @@ -470,7 +470,7 @@ static int core_voltage_post_transition(struct powernow_k8_data *data, u32 savefid = data->currfid; u32 savereqvid = reqvid; - dprintk("ph3 (cpu%d): starting, currfid 0x%x, currvid 0x%x\n", + pr_debug("ph3 (cpu%d): starting, currfid 0x%x, currvid 0x%x\n", smp_processor_id(), data->currfid, data->currvid); @@ -498,17 +498,17 @@ static int core_voltage_post_transition(struct powernow_k8_data *data, return 1; if (savereqvid != data->currvid) { - dprintk("ph3 failed, currvid 0x%x\n", data->currvid); + pr_debug("ph3 failed, currvid 0x%x\n", data->currvid); return 1; } if (savefid != data->currfid) { - dprintk("ph3 failed, currfid changed 0x%x\n", + pr_debug("ph3 failed, currfid changed 0x%x\n", data->currfid); return 1; } - dprintk("ph3 complete, currfid 0x%x, currvid 0x%x\n", + pr_debug("ph3 complete, currfid 0x%x, currvid 0x%x\n", data->currfid, data->currvid); return 0; @@ -708,7 +708,7 @@ static int fill_powernow_table(struct powernow_k8_data *data, return -EIO; } - dprintk("cfid 0x%x, cvid 0x%x\n", data->currfid, data->currvid); + pr_debug("cfid 0x%x, cvid 0x%x\n", data->currfid, data->currvid); data->powernow_table = powernow_table; if (cpumask_first(cpu_core_mask(data->cpu)) == data->cpu) print_basics(data); @@ -718,7 +718,7 @@ static int fill_powernow_table(struct powernow_k8_data *data, (pst[j].vid == data->currvid)) return 0; - dprintk("currfid/vid do not match PST, ignoring\n"); + pr_debug("currfid/vid do not match PST, ignoring\n"); return 0; } @@ -740,36 +740,36 @@ static int find_psb_table(struct powernow_k8_data *data) if (memcmp(psb, PSB_ID_STRING, PSB_ID_STRING_LEN) != 0) continue; - dprintk("found PSB header at 0x%p\n", psb); + pr_debug("found PSB header at 0x%p\n", psb); - dprintk("table vers: 0x%x\n", psb->tableversion); + pr_debug("table vers: 0x%x\n", psb->tableversion); if (psb->tableversion != PSB_VERSION_1_4) { printk(KERN_ERR FW_BUG PFX "PSB table is not v1.4\n"); return -ENODEV; } - dprintk("flags: 0x%x\n", psb->flags1); + pr_debug("flags: 0x%x\n", psb->flags1); if (psb->flags1) { printk(KERN_ERR FW_BUG PFX "unknown flags\n"); return -ENODEV; } data->vstable = psb->vstable; - dprintk("voltage stabilization time: %d(*20us)\n", + pr_debug("voltage stabilization time: %d(*20us)\n", data->vstable); - dprintk("flags2: 0x%x\n", psb->flags2); + pr_debug("flags2: 0x%x\n", psb->flags2); data->rvo = psb->flags2 & 3; data->irt = ((psb->flags2) >> 2) & 3; mvs = ((psb->flags2) >> 4) & 3; data->vidmvs = 1 << mvs; data->batps = ((psb->flags2) >> 6) & 3; - dprintk("ramp voltage offset: %d\n", data->rvo); - dprintk("isochronous relief time: %d\n", data->irt); - dprintk("maximum voltage step: %d - 0x%x\n", mvs, data->vidmvs); + pr_debug("ramp voltage offset: %d\n", data->rvo); + pr_debug("isochronous relief time: %d\n", data->irt); + pr_debug("maximum voltage step: %d - 0x%x\n", mvs, data->vidmvs); - dprintk("numpst: 0x%x\n", psb->num_tables); + pr_debug("numpst: 0x%x\n", psb->num_tables); cpst = psb->num_tables; if ((psb->cpuid == 0x00000fc0) || (psb->cpuid == 0x00000fe0)) { @@ -784,13 +784,13 @@ static int find_psb_table(struct powernow_k8_data *data) } data->plllock = psb->plllocktime; - dprintk("plllocktime: 0x%x (units 1us)\n", psb->plllocktime); - dprintk("maxfid: 0x%x\n", psb->maxfid); - dprintk("maxvid: 0x%x\n", psb->maxvid); + pr_debug("plllocktime: 0x%x (units 1us)\n", psb->plllocktime); + pr_debug("maxfid: 0x%x\n", psb->maxfid); + pr_debug("maxvid: 0x%x\n", psb->maxvid); maxvid = psb->maxvid; data->numps = psb->numps; - dprintk("numpstates: 0x%x\n", data->numps); + pr_debug("numpstates: 0x%x\n", data->numps); return fill_powernow_table(data, (struct pst_s *)(psb+1), maxvid); } @@ -835,13 +835,13 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) u64 control, status; if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) { - dprintk("register performance failed: bad ACPI data\n"); + pr_debug("register performance failed: bad ACPI data\n"); return -EIO; } /* verify the data contained in the ACPI structures */ if (data->acpi_data.state_count <= 1) { - dprintk("No ACPI P-States\n"); + pr_debug("No ACPI P-States\n"); goto err_out; } @@ -850,7 +850,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) if ((control != ACPI_ADR_SPACE_FIXED_HARDWARE) || (status != ACPI_ADR_SPACE_FIXED_HARDWARE)) { - dprintk("Invalid control/status registers (%x - %x)\n", + pr_debug("Invalid control/status registers (%llx - %llx)\n", control, status); goto err_out; } @@ -859,7 +859,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) * (data->acpi_data.state_count + 1)), GFP_KERNEL); if (!powernow_table) { - dprintk("powernow_table memory alloc failure\n"); + pr_debug("powernow_table memory alloc failure\n"); goto err_out; } @@ -929,7 +929,7 @@ static int fill_powernow_table_pstate(struct powernow_k8_data *data, } rdmsr(MSR_PSTATE_DEF_BASE + index, lo, hi); if (!(hi & HW_PSTATE_VALID_MASK)) { - dprintk("invalid pstate %d, ignoring\n", index); + pr_debug("invalid pstate %d, ignoring\n", index); invalidate_entry(powernow_table, i); continue; } @@ -969,7 +969,7 @@ static int fill_powernow_table_fidvid(struct powernow_k8_data *data, vid = (control >> VID_SHIFT) & VID_MASK; } - dprintk(" %d : fid 0x%x, vid 0x%x\n", i, fid, vid); + pr_debug(" %d : fid 0x%x, vid 0x%x\n", i, fid, vid); index = fid | (vid<<8); powernow_table[i].index = index; @@ -979,7 +979,7 @@ static int fill_powernow_table_fidvid(struct powernow_k8_data *data, /* verify frequency is OK */ if ((freq > (MAX_FREQ * 1000)) || (freq < (MIN_FREQ * 1000))) { - dprintk("invalid freq %u kHz, ignoring\n", freq); + pr_debug("invalid freq %u kHz, ignoring\n", freq); invalidate_entry(powernow_table, i); continue; } @@ -987,7 +987,7 @@ static int fill_powernow_table_fidvid(struct powernow_k8_data *data, /* verify voltage is OK - * BIOSs are using "off" to indicate invalid */ if (vid == VID_OFF) { - dprintk("invalid vid %u, ignoring\n", vid); + pr_debug("invalid vid %u, ignoring\n", vid); invalidate_entry(powernow_table, i); continue; } @@ -1048,7 +1048,7 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data, int res, i; struct cpufreq_freqs freqs; - dprintk("cpu %d transition to index %u\n", smp_processor_id(), index); + pr_debug("cpu %d transition to index %u\n", smp_processor_id(), index); /* fid/vid correctness check for k8 */ /* fid are the lower 8 bits of the index we stored into @@ -1058,18 +1058,18 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data, fid = data->powernow_table[index].index & 0xFF; vid = (data->powernow_table[index].index & 0xFF00) >> 8; - dprintk("table matched fid 0x%x, giving vid 0x%x\n", fid, vid); + pr_debug("table matched fid 0x%x, giving vid 0x%x\n", fid, vid); if (query_current_values_with_pending_wait(data)) return 1; if ((data->currvid == vid) && (data->currfid == fid)) { - dprintk("target matches current values (fid 0x%x, vid 0x%x)\n", + pr_debug("target matches current values (fid 0x%x, vid 0x%x)\n", fid, vid); return 0; } - dprintk("cpu %d, changing to fid 0x%x, vid 0x%x\n", + pr_debug("cpu %d, changing to fid 0x%x, vid 0x%x\n", smp_processor_id(), fid, vid); freqs.old = find_khz_freq_from_fid(data->currfid); freqs.new = find_khz_freq_from_fid(fid); @@ -1097,7 +1097,7 @@ static int transition_frequency_pstate(struct powernow_k8_data *data, int res, i; struct cpufreq_freqs freqs; - dprintk("cpu %d transition to index %u\n", smp_processor_id(), index); + pr_debug("cpu %d transition to index %u\n", smp_processor_id(), index); /* get MSR index for hardware pstate transition */ pstate = index & HW_PSTATE_MASK; @@ -1157,14 +1157,14 @@ static int powernowk8_target(struct cpufreq_policy *pol, goto err_out; } - dprintk("targ: cpu %d, %d kHz, min %d, max %d, relation %d\n", + pr_debug("targ: cpu %d, %d kHz, min %d, max %d, relation %d\n", pol->cpu, targfreq, pol->min, pol->max, relation); if (query_current_values_with_pending_wait(data)) goto err_out; if (cpu_family != CPU_HW_PSTATE) { - dprintk("targ: curr fid 0x%x, vid 0x%x\n", + pr_debug("targ: curr fid 0x%x, vid 0x%x\n", data->currfid, data->currvid); if ((checkvid != data->currvid) || @@ -1320,7 +1320,7 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol) data->currpstate); else pol->cur = find_khz_freq_from_fid(data->currfid); - dprintk("policy current frequency %d kHz\n", pol->cur); + pr_debug("policy current frequency %d kHz\n", pol->cur); /* min/max the cpu is capable of */ if (cpufreq_frequency_table_cpuinfo(pol, data->powernow_table)) { @@ -1338,10 +1338,10 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol) cpufreq_frequency_table_get_attr(data->powernow_table, pol->cpu); if (cpu_family == CPU_HW_PSTATE) - dprintk("cpu_init done, current pstate 0x%x\n", + pr_debug("cpu_init done, current pstate 0x%x\n", data->currpstate); else - dprintk("cpu_init done, current fid 0x%x, vid 0x%x\n", + pr_debug("cpu_init done, current fid 0x%x, vid 0x%x\n", data->currfid, data->currvid); per_cpu(powernow_data, pol->cpu) = data; @@ -1587,7 +1587,7 @@ static int __cpuinit powernowk8_init(void) /* driver entry point for term */ static void __exit powernowk8_exit(void) { - dprintk("exit\n"); + pr_debug("exit\n"); if (boot_cpu_has(X86_FEATURE_CPB)) { msrs_free(msrs); diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k8.h b/arch/x86/kernel/cpu/cpufreq/powernow-k8.h index df3529b1c02d7..3744d26cdc2b3 100644 --- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.h +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.h @@ -211,8 +211,6 @@ struct pst_s { u8 vid; }; -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "powernow-k8", msg) - static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid, u32 regfid); static int core_voltage_post_transition(struct powernow_k8_data *data, u32 reqvid); diff --git a/arch/x86/kernel/cpu/cpufreq/sc520_freq.c b/arch/x86/kernel/cpu/cpufreq/sc520_freq.c index 435a996a613a6..1e205e6b1727e 100644 --- a/arch/x86/kernel/cpu/cpufreq/sc520_freq.c +++ b/arch/x86/kernel/cpu/cpufreq/sc520_freq.c @@ -29,8 +29,6 @@ static __u8 __iomem *cpuctl; -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ - "sc520_freq", msg) #define PFX "sc520_freq: " static struct cpufreq_frequency_table sc520_freq_table[] = { @@ -66,7 +64,7 @@ static void sc520_freq_set_cpu_state(unsigned int state) cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - dprintk("attempting to set frequency to %i kHz\n", + pr_debug("attempting to set frequency to %i kHz\n", sc520_freq_table[state].frequency); local_irq_disable(); @@ -161,7 +159,7 @@ static int __init sc520_freq_init(void) /* Test if we have the right hardware */ if (c->x86_vendor != X86_VENDOR_AMD || c->x86 != 4 || c->x86_model != 9) { - dprintk("no Elan SC520 processor found!\n"); + pr_debug("no Elan SC520 processor found!\n"); return -ENODEV; } cpuctl = ioremap((unsigned long)(MMCR_BASE + OFFS_CPUCTL), 1); diff --git a/arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c b/arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c index 9b1ff37de46ae..6ea3455def216 100644 --- a/arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c +++ b/arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c @@ -29,9 +29,6 @@ #define PFX "speedstep-centrino: " #define MAINTAINER "cpufreq@vger.kernel.org" -#define dprintk(msg...) \ - cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "speedstep-centrino", msg) - #define INTEL_MSR_RANGE (0xffff) struct cpu_id @@ -244,7 +241,7 @@ static int centrino_cpu_init_table(struct cpufreq_policy *policy) if (model->cpu_id == NULL) { /* No match at all */ - dprintk("no support for CPU model \"%s\": " + pr_debug("no support for CPU model \"%s\": " "send /proc/cpuinfo to " MAINTAINER "\n", cpu->x86_model_id); return -ENOENT; @@ -252,15 +249,15 @@ static int centrino_cpu_init_table(struct cpufreq_policy *policy) if (model->op_points == NULL) { /* Matched a non-match */ - dprintk("no table support for CPU model \"%s\"\n", + pr_debug("no table support for CPU model \"%s\"\n", cpu->x86_model_id); - dprintk("try using the acpi-cpufreq driver\n"); + pr_debug("try using the acpi-cpufreq driver\n"); return -ENOENT; } per_cpu(centrino_model, policy->cpu) = model; - dprintk("found \"%s\": max frequency: %dkHz\n", + pr_debug("found \"%s\": max frequency: %dkHz\n", model->model_name, model->max_freq); return 0; @@ -369,7 +366,7 @@ static int centrino_cpu_init(struct cpufreq_policy *policy) per_cpu(centrino_cpu, policy->cpu) = &cpu_ids[i]; if (!per_cpu(centrino_cpu, policy->cpu)) { - dprintk("found unsupported CPU with " + pr_debug("found unsupported CPU with " "Enhanced SpeedStep: send /proc/cpuinfo to " MAINTAINER "\n"); return -ENODEV; @@ -385,7 +382,7 @@ static int centrino_cpu_init(struct cpufreq_policy *policy) if (!(l & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) { l |= MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP; - dprintk("trying to enable Enhanced SpeedStep (%x)\n", l); + pr_debug("trying to enable Enhanced SpeedStep (%x)\n", l); wrmsr(MSR_IA32_MISC_ENABLE, l, h); /* check to see if it stuck */ @@ -402,7 +399,7 @@ static int centrino_cpu_init(struct cpufreq_policy *policy) /* 10uS transition latency */ policy->cur = freq; - dprintk("centrino_cpu_init: cur=%dkHz\n", policy->cur); + pr_debug("centrino_cpu_init: cur=%dkHz\n", policy->cur); ret = cpufreq_frequency_table_cpuinfo(policy, per_cpu(centrino_model, policy->cpu)->op_points); @@ -498,7 +495,7 @@ static int centrino_target (struct cpufreq_policy *policy, good_cpu = j; if (good_cpu >= nr_cpu_ids) { - dprintk("couldn't limit to CPUs in this domain\n"); + pr_debug("couldn't limit to CPUs in this domain\n"); retval = -EAGAIN; if (first_cpu) { /* We haven't started the transition yet. */ @@ -512,7 +509,7 @@ static int centrino_target (struct cpufreq_policy *policy, if (first_cpu) { rdmsr_on_cpu(good_cpu, MSR_IA32_PERF_CTL, &oldmsr, &h); if (msr == (oldmsr & 0xffff)) { - dprintk("no change needed - msr was and needs " + pr_debug("no change needed - msr was and needs " "to be %x\n", oldmsr); retval = 0; goto out; @@ -521,7 +518,7 @@ static int centrino_target (struct cpufreq_policy *policy, freqs.old = extract_clock(oldmsr, cpu, 0); freqs.new = extract_clock(msr, cpu, 0); - dprintk("target=%dkHz old=%d new=%d msr=%04x\n", + pr_debug("target=%dkHz old=%d new=%d msr=%04x\n", target_freq, freqs.old, freqs.new, msr); for_each_cpu(k, policy->cpus) { diff --git a/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c b/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c index 561758e951802..a748ce782fee7 100644 --- a/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c +++ b/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c @@ -53,10 +53,6 @@ static struct cpufreq_frequency_table speedstep_freqs[] = { }; -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ - "speedstep-ich", msg) - - /** * speedstep_find_register - read the PMBASE address * @@ -80,7 +76,7 @@ static int speedstep_find_register(void) return -ENODEV; } - dprintk("pmbase is 0x%x\n", pmbase); + pr_debug("pmbase is 0x%x\n", pmbase); return 0; } @@ -106,13 +102,13 @@ static void speedstep_set_state(unsigned int state) /* read state */ value = inb(pmbase + 0x50); - dprintk("read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value); + pr_debug("read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value); /* write new state */ value &= 0xFE; value |= state; - dprintk("writing 0x%x to pmbase 0x%x + 0x50\n", value, pmbase); + pr_debug("writing 0x%x to pmbase 0x%x + 0x50\n", value, pmbase); /* Disable bus master arbitration */ pm2_blk = inb(pmbase + 0x20); @@ -132,10 +128,10 @@ static void speedstep_set_state(unsigned int state) /* Enable IRQs */ local_irq_restore(flags); - dprintk("read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value); + pr_debug("read at pmbase 0x%x + 0x50 returned 0x%x\n", pmbase, value); if (state == (value & 0x1)) - dprintk("change to %u MHz succeeded\n", + pr_debug("change to %u MHz succeeded\n", speedstep_get_frequency(speedstep_processor) / 1000); else printk(KERN_ERR "cpufreq: change failed - I/O error\n"); @@ -165,7 +161,7 @@ static int speedstep_activate(void) pci_read_config_word(speedstep_chipset_dev, 0x00A0, &value); if (!(value & 0x08)) { value |= 0x08; - dprintk("activating SpeedStep (TM) registers\n"); + pr_debug("activating SpeedStep (TM) registers\n"); pci_write_config_word(speedstep_chipset_dev, 0x00A0, value); } @@ -218,7 +214,7 @@ static unsigned int speedstep_detect_chipset(void) return 2; /* 2-M */ if (hostbridge->revision < 5) { - dprintk("hostbridge does not support speedstep\n"); + pr_debug("hostbridge does not support speedstep\n"); speedstep_chipset_dev = NULL; pci_dev_put(hostbridge); return 0; @@ -246,7 +242,7 @@ static unsigned int speedstep_get(unsigned int cpu) if (smp_call_function_single(cpu, get_freq_data, &speed, 1) != 0) BUG(); - dprintk("detected %u kHz as current frequency\n", speed); + pr_debug("detected %u kHz as current frequency\n", speed); return speed; } @@ -276,7 +272,7 @@ static int speedstep_target(struct cpufreq_policy *policy, freqs.new = speedstep_freqs[newstate].frequency; freqs.cpu = policy->cpu; - dprintk("transiting from %u to %u kHz\n", freqs.old, freqs.new); + pr_debug("transiting from %u to %u kHz\n", freqs.old, freqs.new); /* no transition necessary */ if (freqs.old == freqs.new) @@ -351,7 +347,7 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy) if (!speed) return -EIO; - dprintk("currently at %s speed setting - %i MHz\n", + pr_debug("currently at %s speed setting - %i MHz\n", (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low" : "high", (speed / 1000)); @@ -405,14 +401,14 @@ static int __init speedstep_init(void) /* detect processor */ speedstep_processor = speedstep_detect_processor(); if (!speedstep_processor) { - dprintk("Intel(R) SpeedStep(TM) capable processor " + pr_debug("Intel(R) SpeedStep(TM) capable processor " "not found\n"); return -ENODEV; } /* detect chipset */ if (!speedstep_detect_chipset()) { - dprintk("Intel(R) SpeedStep(TM) for this chipset not " + pr_debug("Intel(R) SpeedStep(TM) for this chipset not " "(yet) available.\n"); return -ENODEV; } diff --git a/arch/x86/kernel/cpu/cpufreq/speedstep-lib.c b/arch/x86/kernel/cpu/cpufreq/speedstep-lib.c index a94ec6be69fa5..8af2d2fd9d511 100644 --- a/arch/x86/kernel/cpu/cpufreq/speedstep-lib.c +++ b/arch/x86/kernel/cpu/cpufreq/speedstep-lib.c @@ -18,9 +18,6 @@ #include #include "speedstep-lib.h" -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ - "speedstep-lib", msg) - #define PFX "speedstep-lib: " #ifdef CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK @@ -75,7 +72,7 @@ static unsigned int pentium3_get_frequency(enum speedstep_processor processor) /* read MSR 0x2a - we only need the low 32 bits */ rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp); - dprintk("P3 - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n", msr_lo, msr_tmp); + pr_debug("P3 - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n", msr_lo, msr_tmp); msr_tmp = msr_lo; /* decode the FSB */ @@ -89,7 +86,7 @@ static unsigned int pentium3_get_frequency(enum speedstep_processor processor) /* decode the multiplier */ if (processor == SPEEDSTEP_CPU_PIII_C_EARLY) { - dprintk("workaround for early PIIIs\n"); + pr_debug("workaround for early PIIIs\n"); msr_lo &= 0x03c00000; } else msr_lo &= 0x0bc00000; @@ -100,7 +97,7 @@ static unsigned int pentium3_get_frequency(enum speedstep_processor processor) j++; } - dprintk("speed is %u\n", + pr_debug("speed is %u\n", (msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100)); return msr_decode_mult[j].ratio * msr_decode_fsb[i].value * 100; @@ -112,7 +109,7 @@ static unsigned int pentiumM_get_frequency(void) u32 msr_lo, msr_tmp; rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp); - dprintk("PM - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n", msr_lo, msr_tmp); + pr_debug("PM - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n", msr_lo, msr_tmp); /* see table B-2 of 24547212.pdf */ if (msr_lo & 0x00040000) { @@ -122,7 +119,7 @@ static unsigned int pentiumM_get_frequency(void) } msr_tmp = (msr_lo >> 22) & 0x1f; - dprintk("bits 22-26 are 0x%x, speed is %u\n", + pr_debug("bits 22-26 are 0x%x, speed is %u\n", msr_tmp, (msr_tmp * 100 * 1000)); return msr_tmp * 100 * 1000; @@ -160,11 +157,11 @@ static unsigned int pentium_core_get_frequency(void) } rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_tmp); - dprintk("PCORE - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n", + pr_debug("PCORE - MSR_IA32_EBL_CR_POWERON: 0x%x 0x%x\n", msr_lo, msr_tmp); msr_tmp = (msr_lo >> 22) & 0x1f; - dprintk("bits 22-26 are 0x%x, speed is %u\n", + pr_debug("bits 22-26 are 0x%x, speed is %u\n", msr_tmp, (msr_tmp * fsb)); ret = (msr_tmp * fsb); @@ -190,7 +187,7 @@ static unsigned int pentium4_get_frequency(void) rdmsr(0x2c, msr_lo, msr_hi); - dprintk("P4 - MSR_EBC_FREQUENCY_ID: 0x%x 0x%x\n", msr_lo, msr_hi); + pr_debug("P4 - MSR_EBC_FREQUENCY_ID: 0x%x 0x%x\n", msr_lo, msr_hi); /* decode the FSB: see IA-32 Intel (C) Architecture Software * Developer's Manual, Volume 3: System Prgramming Guide, @@ -217,7 +214,7 @@ static unsigned int pentium4_get_frequency(void) /* Multiplier. */ mult = msr_lo >> 24; - dprintk("P4 - FSB %u kHz; Multiplier %u; Speed %u kHz\n", + pr_debug("P4 - FSB %u kHz; Multiplier %u; Speed %u kHz\n", fsb, mult, (fsb * mult)); ret = (fsb * mult); @@ -257,7 +254,7 @@ unsigned int speedstep_detect_processor(void) struct cpuinfo_x86 *c = &cpu_data(0); u32 ebx, msr_lo, msr_hi; - dprintk("x86: %x, model: %x\n", c->x86, c->x86_model); + pr_debug("x86: %x, model: %x\n", c->x86, c->x86_model); if ((c->x86_vendor != X86_VENDOR_INTEL) || ((c->x86 != 6) && (c->x86 != 0xF))) @@ -272,7 +269,7 @@ unsigned int speedstep_detect_processor(void) ebx = cpuid_ebx(0x00000001); ebx &= 0x000000FF; - dprintk("ebx value is %x, x86_mask is %x\n", ebx, c->x86_mask); + pr_debug("ebx value is %x, x86_mask is %x\n", ebx, c->x86_mask); switch (c->x86_mask) { case 4: @@ -327,7 +324,7 @@ unsigned int speedstep_detect_processor(void) /* cpuid_ebx(1) is 0x04 for desktop PIII, * 0x06 for mobile PIII-M */ ebx = cpuid_ebx(0x00000001); - dprintk("ebx is %x\n", ebx); + pr_debug("ebx is %x\n", ebx); ebx &= 0x000000FF; @@ -344,7 +341,7 @@ unsigned int speedstep_detect_processor(void) /* all mobile PIII Coppermines have FSB 100 MHz * ==> sort out a few desktop PIIIs. */ rdmsr(MSR_IA32_EBL_CR_POWERON, msr_lo, msr_hi); - dprintk("Coppermine: MSR_IA32_EBL_CR_POWERON is 0x%x, 0x%x\n", + pr_debug("Coppermine: MSR_IA32_EBL_CR_POWERON is 0x%x, 0x%x\n", msr_lo, msr_hi); msr_lo &= 0x00c0000; if (msr_lo != 0x0080000) @@ -357,12 +354,12 @@ unsigned int speedstep_detect_processor(void) * bit 56 or 57 is set */ rdmsr(MSR_IA32_PLATFORM_ID, msr_lo, msr_hi); - dprintk("Coppermine: MSR_IA32_PLATFORM ID is 0x%x, 0x%x\n", + pr_debug("Coppermine: MSR_IA32_PLATFORM ID is 0x%x, 0x%x\n", msr_lo, msr_hi); if ((msr_hi & (1<<18)) && (relaxed_check ? 1 : (msr_hi & (3<<24)))) { if (c->x86_mask == 0x01) { - dprintk("early PIII version\n"); + pr_debug("early PIII version\n"); return SPEEDSTEP_CPU_PIII_C_EARLY; } else return SPEEDSTEP_CPU_PIII_C; @@ -393,14 +390,14 @@ unsigned int speedstep_get_freqs(enum speedstep_processor processor, if ((!processor) || (!low_speed) || (!high_speed) || (!set_state)) return -EINVAL; - dprintk("trying to determine both speeds\n"); + pr_debug("trying to determine both speeds\n"); /* get current speed */ prev_speed = speedstep_get_frequency(processor); if (!prev_speed) return -EIO; - dprintk("previous speed is %u\n", prev_speed); + pr_debug("previous speed is %u\n", prev_speed); local_irq_save(flags); @@ -412,7 +409,7 @@ unsigned int speedstep_get_freqs(enum speedstep_processor processor, goto out; } - dprintk("low speed is %u\n", *low_speed); + pr_debug("low speed is %u\n", *low_speed); /* start latency measurement */ if (transition_latency) @@ -431,7 +428,7 @@ unsigned int speedstep_get_freqs(enum speedstep_processor processor, goto out; } - dprintk("high speed is %u\n", *high_speed); + pr_debug("high speed is %u\n", *high_speed); if (*low_speed == *high_speed) { ret = -ENODEV; @@ -445,7 +442,7 @@ unsigned int speedstep_get_freqs(enum speedstep_processor processor, if (transition_latency) { *transition_latency = (tv2.tv_sec - tv1.tv_sec) * USEC_PER_SEC + tv2.tv_usec - tv1.tv_usec; - dprintk("transition latency is %u uSec\n", *transition_latency); + pr_debug("transition latency is %u uSec\n", *transition_latency); /* convert uSec to nSec and add 20% for safety reasons */ *transition_latency *= 1200; diff --git a/arch/x86/kernel/cpu/cpufreq/speedstep-smi.c b/arch/x86/kernel/cpu/cpufreq/speedstep-smi.c index 8abd869baabfb..c76ead3490bf7 100644 --- a/arch/x86/kernel/cpu/cpufreq/speedstep-smi.c +++ b/arch/x86/kernel/cpu/cpufreq/speedstep-smi.c @@ -55,9 +55,6 @@ static struct cpufreq_frequency_table speedstep_freqs[] = { * of DMA activity going on? */ #define SMI_TRIES 5 -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \ - "speedstep-smi", msg) - /** * speedstep_smi_ownership */ @@ -70,7 +67,7 @@ static int speedstep_smi_ownership(void) command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff); magic = virt_to_phys(magic_data); - dprintk("trying to obtain ownership with command %x at port %x\n", + pr_debug("trying to obtain ownership with command %x at port %x\n", command, smi_port); __asm__ __volatile__( @@ -85,7 +82,7 @@ static int speedstep_smi_ownership(void) : "memory" ); - dprintk("result is %x\n", result); + pr_debug("result is %x\n", result); return result; } @@ -106,13 +103,13 @@ static int speedstep_smi_get_freqs(unsigned int *low, unsigned int *high) u32 function = GET_SPEEDSTEP_FREQS; if (!(ist_info.event & 0xFFFF)) { - dprintk("bug #1422 -- can't read freqs from BIOS\n"); + pr_debug("bug #1422 -- can't read freqs from BIOS\n"); return -ENODEV; } command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff); - dprintk("trying to determine frequencies with command %x at port %x\n", + pr_debug("trying to determine frequencies with command %x at port %x\n", command, smi_port); __asm__ __volatile__( @@ -129,7 +126,7 @@ static int speedstep_smi_get_freqs(unsigned int *low, unsigned int *high) "d" (smi_port), "S" (0), "D" (0) ); - dprintk("result %x, low_freq %u, high_freq %u\n", + pr_debug("result %x, low_freq %u, high_freq %u\n", result, low_mhz, high_mhz); /* abort if results are obviously incorrect... */ @@ -154,7 +151,7 @@ static int speedstep_get_state(void) command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff); - dprintk("trying to determine current setting with command %x " + pr_debug("trying to determine current setting with command %x " "at port %x\n", command, smi_port); __asm__ __volatile__( @@ -168,7 +165,7 @@ static int speedstep_get_state(void) "d" (smi_port), "S" (0), "D" (0) ); - dprintk("state is %x, result is %x\n", state, result); + pr_debug("state is %x, result is %x\n", state, result); return state & 1; } @@ -194,13 +191,13 @@ static void speedstep_set_state(unsigned int state) command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff); - dprintk("trying to set frequency to state %u " + pr_debug("trying to set frequency to state %u " "with command %x at port %x\n", state, command, smi_port); do { if (retry) { - dprintk("retry %u, previous result %u, waiting...\n", + pr_debug("retry %u, previous result %u, waiting...\n", retry, result); mdelay(retry * 50); } @@ -221,7 +218,7 @@ static void speedstep_set_state(unsigned int state) local_irq_restore(flags); if (new_state == state) - dprintk("change to %u MHz succeeded after %u tries " + pr_debug("change to %u MHz succeeded after %u tries " "with result %u\n", (speedstep_freqs[new_state].frequency / 1000), retry, result); @@ -292,7 +289,7 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy) result = speedstep_smi_ownership(); if (result) { - dprintk("fails in aquiring ownership of a SMI interface.\n"); + pr_debug("fails in acquiring ownership of a SMI interface.\n"); return -EINVAL; } @@ -304,7 +301,7 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy) if (result) { /* fall back to speedstep_lib.c dection mechanism: * try both states out */ - dprintk("could not detect low and high frequencies " + pr_debug("could not detect low and high frequencies " "by SMI call.\n"); result = speedstep_get_freqs(speedstep_processor, low, high, @@ -312,18 +309,18 @@ static int speedstep_cpu_init(struct cpufreq_policy *policy) &speedstep_set_state); if (result) { - dprintk("could not detect two different speeds" + pr_debug("could not detect two different speeds" " -- aborting.\n"); return result; } else - dprintk("workaround worked.\n"); + pr_debug("workaround worked.\n"); } /* get current speed setting */ state = speedstep_get_state(); speed = speedstep_freqs[state].frequency; - dprintk("currently at %s speed setting - %i MHz\n", + pr_debug("currently at %s speed setting - %i MHz\n", (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low" : "high", (speed / 1000)); @@ -360,7 +357,7 @@ static int speedstep_resume(struct cpufreq_policy *policy) int result = speedstep_smi_ownership(); if (result) - dprintk("fails in re-aquiring ownership of a SMI interface.\n"); + pr_debug("fails in re-acquiring ownership of a SMI interface.\n"); return result; } @@ -403,12 +400,12 @@ static int __init speedstep_init(void) } if (!speedstep_processor) { - dprintk("No supported Intel CPU detected.\n"); + pr_debug("No supported Intel CPU detected.\n"); return -ENODEV; } - dprintk("signature:0x%.8lx, command:0x%.8lx, " - "event:0x%.8lx, perf_level:0x%.8lx.\n", + pr_debug("signature:0x%.8ulx, command:0x%.8ulx, " + "event:0x%.8ulx, perf_level:0x%.8ulx.\n", ist_info.signature, ist_info.command, ist_info.event, ist_info.perf_level); diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index 3a73a93596e88..85b32376dad7e 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -49,10 +49,6 @@ ACPI_MODULE_NAME("processor_perflib"); static DEFINE_MUTEX(performance_mutex); -/* Use cpufreq debug layer for _PPC changes. */ -#define cpufreq_printk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, \ - "cpufreq-core", msg) - /* * _PPC support is implemented as a CPUfreq policy notifier: * This means each time a CPUfreq driver registered also with @@ -145,7 +141,7 @@ static int acpi_processor_get_platform_limit(struct acpi_processor *pr) return -ENODEV; } - cpufreq_printk("CPU %d: _PPC is %d - frequency %s limited\n", pr->id, + pr_debug("CPU %d: _PPC is %d - frequency %s limited\n", pr->id, (int)ppc, ppc ? "" : "not"); pr->performance_platform_limit = (int)ppc; diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 49ce53017aee0..8dfd9ff915f9a 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -18,19 +18,6 @@ if CPU_FREQ config CPU_FREQ_TABLE tristate -config CPU_FREQ_DEBUG - bool "Enable CPUfreq debugging" - help - Say Y here to enable CPUfreq subsystem (including drivers) - debugging. You will need to activate it via the kernel - command line by passing - cpufreq.debug= - - To get , add - 1 to activate CPUfreq core debugging, - 2 to activate CPUfreq drivers debugging, and - 4 to activate CPUfreq governor debugging - config CPU_FREQ_STAT tristate "CPU frequency translation statistics" select CPU_FREQ_TABLE diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index d1ae4e54574ec..df27d08200f20 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -32,9 +32,6 @@ #include -#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, \ - "cpufreq-core", msg) - /** * The "cpufreq driver" - the arch- or hardware-dependent low * level driver of CPUFreq support, and its spinlock. This lock @@ -180,93 +177,6 @@ void cpufreq_cpu_put(struct cpufreq_policy *data) EXPORT_SYMBOL_GPL(cpufreq_cpu_put); -/********************************************************************* - * UNIFIED DEBUG HELPERS * - *********************************************************************/ -#ifdef CONFIG_CPU_FREQ_DEBUG - -/* what part(s) of the CPUfreq subsystem are debugged? */ -static unsigned int debug; - -/* is the debug output ratelimit'ed using printk_ratelimit? User can - * set or modify this value. - */ -static unsigned int debug_ratelimit = 1; - -/* is the printk_ratelimit'ing enabled? It's enabled after a successful - * loading of a cpufreq driver, temporarily disabled when a new policy - * is set, and disabled upon cpufreq driver removal - */ -static unsigned int disable_ratelimit = 1; -static DEFINE_SPINLOCK(disable_ratelimit_lock); - -static void cpufreq_debug_enable_ratelimit(void) -{ - unsigned long flags; - - spin_lock_irqsave(&disable_ratelimit_lock, flags); - if (disable_ratelimit) - disable_ratelimit--; - spin_unlock_irqrestore(&disable_ratelimit_lock, flags); -} - -static void cpufreq_debug_disable_ratelimit(void) -{ - unsigned long flags; - - spin_lock_irqsave(&disable_ratelimit_lock, flags); - disable_ratelimit++; - spin_unlock_irqrestore(&disable_ratelimit_lock, flags); -} - -void cpufreq_debug_printk(unsigned int type, const char *prefix, - const char *fmt, ...) -{ - char s[256]; - va_list args; - unsigned int len; - unsigned long flags; - - WARN_ON(!prefix); - if (type & debug) { - spin_lock_irqsave(&disable_ratelimit_lock, flags); - if (!disable_ratelimit && debug_ratelimit - && !printk_ratelimit()) { - spin_unlock_irqrestore(&disable_ratelimit_lock, flags); - return; - } - spin_unlock_irqrestore(&disable_ratelimit_lock, flags); - - len = snprintf(s, 256, KERN_DEBUG "%s: ", prefix); - - va_start(args, fmt); - len += vsnprintf(&s[len], (256 - len), fmt, args); - va_end(args); - - printk(s); - - WARN_ON(len < 5); - } -} -EXPORT_SYMBOL(cpufreq_debug_printk); - - -module_param(debug, uint, 0644); -MODULE_PARM_DESC(debug, "CPUfreq debugging: add 1 to debug core," - " 2 to debug drivers, and 4 to debug governors."); - -module_param(debug_ratelimit, uint, 0644); -MODULE_PARM_DESC(debug_ratelimit, "CPUfreq debugging:" - " set to 0 to disable ratelimiting."); - -#else /* !CONFIG_CPU_FREQ_DEBUG */ - -static inline void cpufreq_debug_enable_ratelimit(void) { return; } -static inline void cpufreq_debug_disable_ratelimit(void) { return; } - -#endif /* CONFIG_CPU_FREQ_DEBUG */ - - /********************************************************************* * EXTERNALLY AFFECTING FREQUENCY CHANGES * *********************************************************************/ @@ -291,7 +201,7 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) if (!l_p_j_ref_freq) { l_p_j_ref = loops_per_jiffy; l_p_j_ref_freq = ci->old; - dprintk("saving %lu as reference value for loops_per_jiffy; " + pr_debug("saving %lu as reference value for loops_per_jiffy; " "freq is %u kHz\n", l_p_j_ref, l_p_j_ref_freq); } if ((val == CPUFREQ_PRECHANGE && ci->old < ci->new) || @@ -299,7 +209,7 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) (val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) { loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new); - dprintk("scaling loops_per_jiffy to %lu " + pr_debug("scaling loops_per_jiffy to %lu " "for frequency %u kHz\n", loops_per_jiffy, ci->new); } } @@ -326,7 +236,7 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) BUG_ON(irqs_disabled()); freqs->flags = cpufreq_driver->flags; - dprintk("notification %u of frequency transition to %u kHz\n", + pr_debug("notification %u of frequency transition to %u kHz\n", state, freqs->new); policy = per_cpu(cpufreq_cpu_data, freqs->cpu); @@ -340,7 +250,7 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) if (!(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) { if ((policy) && (policy->cpu == freqs->cpu) && (policy->cur) && (policy->cur != freqs->old)) { - dprintk("Warning: CPU frequency is" + pr_debug("Warning: CPU frequency is" " %u, cpufreq assumed %u kHz.\n", freqs->old, policy->cur); freqs->old = policy->cur; @@ -353,7 +263,7 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) case CPUFREQ_POSTCHANGE: adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); - dprintk("FREQ: %lu - CPU: %lu", (unsigned long)freqs->new, + pr_debug("FREQ: %lu - CPU: %lu", (unsigned long)freqs->new, (unsigned long)freqs->cpu); trace_power_frequency(POWER_PSTATE, freqs->new, freqs->cpu); trace_cpu_frequency(freqs->new, freqs->cpu); @@ -746,7 +656,7 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr, static void cpufreq_sysfs_release(struct kobject *kobj) { struct cpufreq_policy *policy = to_policy(kobj); - dprintk("last reference is dropped\n"); + pr_debug("last reference is dropped\n"); complete(&policy->kobj_unregister); } @@ -781,7 +691,7 @@ static int cpufreq_add_dev_policy(unsigned int cpu, gov = __find_governor(per_cpu(cpufreq_cpu_governor, cpu)); if (gov) { policy->governor = gov; - dprintk("Restoring governor %s for cpu %d\n", + pr_debug("Restoring governor %s for cpu %d\n", policy->governor->name, cpu); } #endif @@ -817,7 +727,7 @@ static int cpufreq_add_dev_policy(unsigned int cpu, per_cpu(cpufreq_cpu_data, cpu) = managed_policy; spin_unlock_irqrestore(&cpufreq_driver_lock, flags); - dprintk("CPU already managed, adding link\n"); + pr_debug("CPU already managed, adding link\n"); ret = sysfs_create_link(&sys_dev->kobj, &managed_policy->kobj, "cpufreq"); @@ -858,7 +768,7 @@ static int cpufreq_add_dev_symlink(unsigned int cpu, if (!cpu_online(j)) continue; - dprintk("CPU %u already managed, adding link\n", j); + pr_debug("CPU %u already managed, adding link\n", j); managed_policy = cpufreq_cpu_get(cpu); cpu_sys_dev = get_cpu_sysdev(j); ret = sysfs_create_link(&cpu_sys_dev->kobj, &policy->kobj, @@ -934,7 +844,7 @@ static int cpufreq_add_dev_interface(unsigned int cpu, policy->user_policy.governor = policy->governor; if (ret) { - dprintk("setting policy failed\n"); + pr_debug("setting policy failed\n"); if (cpufreq_driver->exit) cpufreq_driver->exit(policy); } @@ -970,8 +880,7 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) if (cpu_is_offline(cpu)) return 0; - cpufreq_debug_disable_ratelimit(); - dprintk("adding CPU %u\n", cpu); + pr_debug("adding CPU %u\n", cpu); #ifdef CONFIG_SMP /* check whether a different CPU already registered this @@ -979,7 +888,6 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) policy = cpufreq_cpu_get(cpu); if (unlikely(policy)) { cpufreq_cpu_put(policy); - cpufreq_debug_enable_ratelimit(); return 0; } #endif @@ -1030,7 +938,7 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) */ ret = cpufreq_driver->init(policy); if (ret) { - dprintk("initialization failed\n"); + pr_debug("initialization failed\n"); goto err_unlock_policy; } policy->user_policy.min = policy->min; @@ -1056,8 +964,7 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) kobject_uevent(&policy->kobj, KOBJ_ADD); module_put(cpufreq_driver->owner); - dprintk("initialization complete\n"); - cpufreq_debug_enable_ratelimit(); + pr_debug("initialization complete\n"); return 0; @@ -1081,7 +988,6 @@ static int cpufreq_add_dev(struct sys_device *sys_dev) nomem_out: module_put(cpufreq_driver->owner); module_out: - cpufreq_debug_enable_ratelimit(); return ret; } @@ -1105,15 +1011,13 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) unsigned int j; #endif - cpufreq_debug_disable_ratelimit(); - dprintk("unregistering CPU %u\n", cpu); + pr_debug("unregistering CPU %u\n", cpu); spin_lock_irqsave(&cpufreq_driver_lock, flags); data = per_cpu(cpufreq_cpu_data, cpu); if (!data) { spin_unlock_irqrestore(&cpufreq_driver_lock, flags); - cpufreq_debug_enable_ratelimit(); unlock_policy_rwsem_write(cpu); return -EINVAL; } @@ -1125,12 +1029,11 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) * only need to unlink, put and exit */ if (unlikely(cpu != data->cpu)) { - dprintk("removing link\n"); + pr_debug("removing link\n"); cpumask_clear_cpu(cpu, data->cpus); spin_unlock_irqrestore(&cpufreq_driver_lock, flags); kobj = &sys_dev->kobj; cpufreq_cpu_put(data); - cpufreq_debug_enable_ratelimit(); unlock_policy_rwsem_write(cpu); sysfs_remove_link(kobj, "cpufreq"); return 0; @@ -1163,7 +1066,7 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) for_each_cpu(j, data->cpus) { if (j == cpu) continue; - dprintk("removing link for cpu %u\n", j); + pr_debug("removing link for cpu %u\n", j); #ifdef CONFIG_HOTPLUG_CPU strncpy(per_cpu(cpufreq_cpu_governor, j), data->governor->name, CPUFREQ_NAME_LEN); @@ -1192,17 +1095,15 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) * not referenced anymore by anybody before we proceed with * unloading. */ - dprintk("waiting for dropping of refcount\n"); + pr_debug("waiting for dropping of refcount\n"); wait_for_completion(cmp); - dprintk("wait complete\n"); + pr_debug("wait complete\n"); lock_policy_rwsem_write(cpu); if (cpufreq_driver->exit) cpufreq_driver->exit(data); unlock_policy_rwsem_write(cpu); - cpufreq_debug_enable_ratelimit(); - #ifdef CONFIG_HOTPLUG_CPU /* when the CPU which is the parent of the kobj is hotplugged * offline, check for siblings, and create cpufreq sysfs interface @@ -1248,7 +1149,7 @@ static void handle_update(struct work_struct *work) struct cpufreq_policy *policy = container_of(work, struct cpufreq_policy, update); unsigned int cpu = policy->cpu; - dprintk("handle_update for cpu %u called\n", cpu); + pr_debug("handle_update for cpu %u called\n", cpu); cpufreq_update_policy(cpu); } @@ -1266,7 +1167,7 @@ static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq, { struct cpufreq_freqs freqs; - dprintk("Warning: CPU frequency out of sync: cpufreq and timing " + pr_debug("Warning: CPU frequency out of sync: cpufreq and timing " "core thinks of %u, is %u kHz.\n", old_freq, new_freq); freqs.cpu = cpu; @@ -1369,7 +1270,7 @@ static int cpufreq_bp_suspend(void) int cpu = smp_processor_id(); struct cpufreq_policy *cpu_policy; - dprintk("suspending cpu %u\n", cpu); + pr_debug("suspending cpu %u\n", cpu); /* If there's no policy for the boot CPU, we have nothing to do. */ cpu_policy = cpufreq_cpu_get(cpu); @@ -1407,7 +1308,7 @@ static void cpufreq_bp_resume(void) int cpu = smp_processor_id(); struct cpufreq_policy *cpu_policy; - dprintk("resuming cpu %u\n", cpu); + pr_debug("resuming cpu %u\n", cpu); /* If there's no policy for the boot CPU, we have nothing to do. */ cpu_policy = cpufreq_cpu_get(cpu); @@ -1519,7 +1420,7 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, { int retval = -EINVAL; - dprintk("target for CPU %u: %u kHz, relation %u\n", policy->cpu, + pr_debug("target for CPU %u: %u kHz, relation %u\n", policy->cpu, target_freq, relation); if (cpu_online(policy->cpu) && cpufreq_driver->target) retval = cpufreq_driver->target(policy, target_freq, relation); @@ -1605,7 +1506,7 @@ static int __cpufreq_governor(struct cpufreq_policy *policy, if (!try_module_get(policy->governor->owner)) return -EINVAL; - dprintk("__cpufreq_governor for CPU %u, event %u\n", + pr_debug("__cpufreq_governor for CPU %u, event %u\n", policy->cpu, event); ret = policy->governor->governor(policy, event); @@ -1706,8 +1607,7 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, { int ret = 0; - cpufreq_debug_disable_ratelimit(); - dprintk("setting new policy for CPU %u: %u - %u kHz\n", policy->cpu, + pr_debug("setting new policy for CPU %u: %u - %u kHz\n", policy->cpu, policy->min, policy->max); memcpy(&policy->cpuinfo, &data->cpuinfo, @@ -1744,19 +1644,19 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, data->min = policy->min; data->max = policy->max; - dprintk("new min and max freqs are %u - %u kHz\n", + pr_debug("new min and max freqs are %u - %u kHz\n", data->min, data->max); if (cpufreq_driver->setpolicy) { data->policy = policy->policy; - dprintk("setting range\n"); + pr_debug("setting range\n"); ret = cpufreq_driver->setpolicy(policy); } else { if (policy->governor != data->governor) { /* save old, working values */ struct cpufreq_governor *old_gov = data->governor; - dprintk("governor switch\n"); + pr_debug("governor switch\n"); /* end old governor */ if (data->governor) @@ -1766,7 +1666,7 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, data->governor = policy->governor; if (__cpufreq_governor(data, CPUFREQ_GOV_START)) { /* new governor failed, so re-start old one */ - dprintk("starting governor %s failed\n", + pr_debug("starting governor %s failed\n", data->governor->name); if (old_gov) { data->governor = old_gov; @@ -1778,12 +1678,11 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, } /* might be a policy change, too, so fall through */ } - dprintk("governor: change or update limits\n"); + pr_debug("governor: change or update limits\n"); __cpufreq_governor(data, CPUFREQ_GOV_LIMITS); } error_out: - cpufreq_debug_enable_ratelimit(); return ret; } @@ -1810,7 +1709,7 @@ int cpufreq_update_policy(unsigned int cpu) goto fail; } - dprintk("updating policy for CPU %u\n", cpu); + pr_debug("updating policy for CPU %u\n", cpu); memcpy(&policy, data, sizeof(struct cpufreq_policy)); policy.min = data->user_policy.min; policy.max = data->user_policy.max; @@ -1822,7 +1721,7 @@ int cpufreq_update_policy(unsigned int cpu) if (cpufreq_driver->get) { policy.cur = cpufreq_driver->get(cpu); if (!data->cur) { - dprintk("Driver did not initialize current freq"); + pr_debug("Driver did not initialize current freq"); data->cur = policy.cur; } else { if (data->cur != policy.cur) @@ -1898,7 +1797,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) ((!driver_data->setpolicy) && (!driver_data->target))) return -EINVAL; - dprintk("trying to register driver %s\n", driver_data->name); + pr_debug("trying to register driver %s\n", driver_data->name); if (driver_data->setpolicy) driver_data->flags |= CPUFREQ_CONST_LOOPS; @@ -1929,15 +1828,14 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) /* if all ->init() calls failed, unregister */ if (ret) { - dprintk("no CPU initialized for driver %s\n", + pr_debug("no CPU initialized for driver %s\n", driver_data->name); goto err_sysdev_unreg; } } register_hotcpu_notifier(&cpufreq_cpu_notifier); - dprintk("driver %s up and running\n", driver_data->name); - cpufreq_debug_enable_ratelimit(); + pr_debug("driver %s up and running\n", driver_data->name); return 0; err_sysdev_unreg: @@ -1964,14 +1862,10 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver) { unsigned long flags; - cpufreq_debug_disable_ratelimit(); - - if (!cpufreq_driver || (driver != cpufreq_driver)) { - cpufreq_debug_enable_ratelimit(); + if (!cpufreq_driver || (driver != cpufreq_driver)) return -EINVAL; - } - dprintk("unregistering driver %s\n", driver->name); + pr_debug("unregistering driver %s\n", driver->name); sysdev_driver_unregister(&cpu_sysdev_class, &cpufreq_sysdev_driver); unregister_hotcpu_notifier(&cpufreq_cpu_notifier); diff --git a/drivers/cpufreq/cpufreq_performance.c b/drivers/cpufreq/cpufreq_performance.c index 7e2e515087f89..f13a8a9af6a13 100644 --- a/drivers/cpufreq/cpufreq_performance.c +++ b/drivers/cpufreq/cpufreq_performance.c @@ -15,9 +15,6 @@ #include #include -#define dprintk(msg...) \ - cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "performance", msg) - static int cpufreq_governor_performance(struct cpufreq_policy *policy, unsigned int event) @@ -25,7 +22,7 @@ static int cpufreq_governor_performance(struct cpufreq_policy *policy, switch (event) { case CPUFREQ_GOV_START: case CPUFREQ_GOV_LIMITS: - dprintk("setting to %u kHz because of event %u\n", + pr_debug("setting to %u kHz because of event %u\n", policy->max, event); __cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H); diff --git a/drivers/cpufreq/cpufreq_powersave.c b/drivers/cpufreq/cpufreq_powersave.c index e6db5faf3eb11..4c2eb512f2bc3 100644 --- a/drivers/cpufreq/cpufreq_powersave.c +++ b/drivers/cpufreq/cpufreq_powersave.c @@ -15,16 +15,13 @@ #include #include -#define dprintk(msg...) \ - cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "powersave", msg) - static int cpufreq_governor_powersave(struct cpufreq_policy *policy, unsigned int event) { switch (event) { case CPUFREQ_GOV_START: case CPUFREQ_GOV_LIMITS: - dprintk("setting to %u kHz because of event %u\n", + pr_debug("setting to %u kHz because of event %u\n", policy->min, event); __cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L); diff --git a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c index 66d2d1d6c80f1..f231015904c0a 100644 --- a/drivers/cpufreq/cpufreq_userspace.c +++ b/drivers/cpufreq/cpufreq_userspace.c @@ -37,9 +37,6 @@ static DEFINE_PER_CPU(unsigned int, cpu_is_managed); static DEFINE_MUTEX(userspace_mutex); static int cpus_using_userspace_governor; -#define dprintk(msg...) \ - cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "userspace", msg) - /* keep track of frequency transitions */ static int userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val, @@ -50,7 +47,7 @@ userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val, if (!per_cpu(cpu_is_managed, freq->cpu)) return 0; - dprintk("saving cpu_cur_freq of cpu %u to be %u kHz\n", + pr_debug("saving cpu_cur_freq of cpu %u to be %u kHz\n", freq->cpu, freq->new); per_cpu(cpu_cur_freq, freq->cpu) = freq->new; @@ -73,7 +70,7 @@ static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq) { int ret = -EINVAL; - dprintk("cpufreq_set for cpu %u, freq %u kHz\n", policy->cpu, freq); + pr_debug("cpufreq_set for cpu %u, freq %u kHz\n", policy->cpu, freq); mutex_lock(&userspace_mutex); if (!per_cpu(cpu_is_managed, policy->cpu)) @@ -134,7 +131,7 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy, per_cpu(cpu_max_freq, cpu) = policy->max; per_cpu(cpu_cur_freq, cpu) = policy->cur; per_cpu(cpu_set_freq, cpu) = policy->cur; - dprintk("managing cpu %u started " + pr_debug("managing cpu %u started " "(%u - %u kHz, currently %u kHz)\n", cpu, per_cpu(cpu_min_freq, cpu), @@ -156,12 +153,12 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy, per_cpu(cpu_min_freq, cpu) = 0; per_cpu(cpu_max_freq, cpu) = 0; per_cpu(cpu_set_freq, cpu) = 0; - dprintk("managing cpu %u stopped\n", cpu); + pr_debug("managing cpu %u stopped\n", cpu); mutex_unlock(&userspace_mutex); break; case CPUFREQ_GOV_LIMITS: mutex_lock(&userspace_mutex); - dprintk("limit event for cpu %u: %u - %u kHz, " + pr_debug("limit event for cpu %u: %u - %u kHz, " "currently %u kHz, last set to %u kHz\n", cpu, policy->min, policy->max, per_cpu(cpu_cur_freq, cpu), diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index 05432216e2246..90431cb92804b 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -14,9 +14,6 @@ #include #include -#define dprintk(msg...) \ - cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, "freq-table", msg) - /********************************************************************* * FREQUENCY TABLE HELPERS * *********************************************************************/ @@ -31,11 +28,11 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy, for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) { unsigned int freq = table[i].frequency; if (freq == CPUFREQ_ENTRY_INVALID) { - dprintk("table entry %u is invalid, skipping\n", i); + pr_debug("table entry %u is invalid, skipping\n", i); continue; } - dprintk("table entry %u: %u kHz, %u index\n", + pr_debug("table entry %u: %u kHz, %u index\n", i, freq, table[i].index); if (freq < min_freq) min_freq = freq; @@ -61,7 +58,7 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy, unsigned int i; unsigned int count = 0; - dprintk("request for verification of policy (%u - %u kHz) for cpu %u\n", + pr_debug("request for verification of policy (%u - %u kHz) for cpu %u\n", policy->min, policy->max, policy->cpu); if (!cpu_online(policy->cpu)) @@ -86,7 +83,7 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy, cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); - dprintk("verification lead to (%u - %u kHz) for cpu %u\n", + pr_debug("verification lead to (%u - %u kHz) for cpu %u\n", policy->min, policy->max, policy->cpu); return 0; @@ -110,7 +107,7 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, }; unsigned int i; - dprintk("request for target %u kHz (relation: %u) for cpu %u\n", + pr_debug("request for target %u kHz (relation: %u) for cpu %u\n", target_freq, relation, policy->cpu); switch (relation) { @@ -167,7 +164,7 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, } else *index = optimal.index; - dprintk("target is %u (%u kHz, %u)\n", *index, table[*index].frequency, + pr_debug("target is %u (%u kHz, %u)\n", *index, table[*index].frequency, table[*index].index); return 0; @@ -216,14 +213,14 @@ EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs); void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table, unsigned int cpu) { - dprintk("setting show_table for cpu %u to %p\n", cpu, table); + pr_debug("setting show_table for cpu %u to %p\n", cpu, table); per_cpu(cpufreq_show_table, cpu) = table; } EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_attr); void cpufreq_frequency_table_put_attr(unsigned int cpu) { - dprintk("clearing show_table for cpu %u\n", cpu); + pr_debug("clearing show_table for cpu %u\n", cpu); per_cpu(cpufreq_show_table, cpu) = NULL; } EXPORT_SYMBOL_GPL(cpufreq_frequency_table_put_attr); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 32fb6cae42945..6fc1e824cdb83 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -400,23 +400,4 @@ void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table, void cpufreq_frequency_table_put_attr(unsigned int cpu); -/********************************************************************* - * UNIFIED DEBUG HELPERS * - *********************************************************************/ - -#define CPUFREQ_DEBUG_CORE 1 -#define CPUFREQ_DEBUG_DRIVER 2 -#define CPUFREQ_DEBUG_GOVERNOR 4 - -#ifdef CONFIG_CPU_FREQ_DEBUG - -extern void cpufreq_debug_printk(unsigned int type, const char *prefix, - const char *fmt, ...); - -#else - -#define cpufreq_debug_printk(msg...) do { } while(0) - -#endif /* CONFIG_CPU_FREQ_DEBUG */ - #endif /* _LINUX_CPUFREQ_H */ From 25fa8960b72804e7c549479caf4e23da41f3a663 Mon Sep 17 00:00:00 2001 From: Prashant Somashekar Date: Mon, 2 Jan 2012 10:56:25 -0500 Subject: [PATCH 2184/2556] drivers: cpufreq: adjust ondemand thresholds -reference DEF_SAMPLING_DOWN_FACTOR from cyanogenmod, htc-kernel-msm7x30 --- drivers/cpufreq/cpufreq_ondemand.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 004efb02db1ad..2511ebf4f10f0 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -29,25 +29,25 @@ */ #if defined(CONFIG_ARCH_MSM_SCORPION) -#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10) +#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (6) #define DEF_FREQUENCY_UP_THRESHOLD (80) -#define DEF_SAMPLING_DOWN_FACTOR (1) +#define DEF_SAMPLING_DOWN_FACTOR (10) #define MAX_SAMPLING_DOWN_FACTOR (95000) -#define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3) +#define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (2) #define MICRO_FREQUENCY_UP_THRESHOLD (85) #define MICRO_FREQUENCY_MIN_SAMPLE_RATE (9500) -#define MIN_FREQUENCY_UP_THRESHOLD (11) +#define MIN_FREQUENCY_UP_THRESHOLD (7) #define MAX_FREQUENCY_UP_THRESHOLD (100) #else -#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10) -#define DEF_FREQUENCY_UP_THRESHOLD (80) -#define DEF_SAMPLING_DOWN_FACTOR (1) -#define MAX_SAMPLING_DOWN_FACTOR (100000) -#define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3) -#define MICRO_FREQUENCY_UP_THRESHOLD (95) -#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000) -#define MIN_FREQUENCY_UP_THRESHOLD (11) -#define MAX_FREQUENCY_UP_THRESHOLD (100) +#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10) +#define DEF_FREQUENCY_UP_THRESHOLD (80) +#define DEF_SAMPLING_DOWN_FACTOR (1) +#define MAX_SAMPLING_DOWN_FACTOR (100000) +#define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3) +#define MICRO_FREQUENCY_UP_THRESHOLD (95) +#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000) +#define MIN_FREQUENCY_UP_THRESHOLD (11) +#define MAX_FREQUENCY_UP_THRESHOLD (100) #endif /* From 58e0c766a13812d683484984fb2a1371c530864a Mon Sep 17 00:00:00 2001 From: Will Tisdale Date: Tue, 4 Oct 2011 13:31:43 +0100 Subject: [PATCH 2185/2556] drivers: a1026: Fix warning: "ERROR" redefined In file included from include/linux/usb/android_composite.h:20, from arch/arm/mach-msm/board-mahimahi.c:27: include/linux/usb/composite.h:374:1: warning: this is the location of the previous definition --- include/linux/a1026.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/a1026.h b/include/linux/a1026.h index 474a971942c3f..5b02240df6209 100644 --- a/include/linux/a1026.h +++ b/include/linux/a1026.h @@ -215,7 +215,6 @@ enum A1026_NS_states { #define A1026_SLEEP 0 #define A1026_ACTIVE 1 #define A1026_CMD_FIFO_DEPTH 64 -#define ERROR 0xffffffff enum A1026_config_mode { A1026_CONFIG_FULL, From 4febe5aff35c145270508fd330a21908c7bf3cf5 Mon Sep 17 00:00:00 2001 From: thalamus Date: Sun, 17 Apr 2011 12:39:03 +0100 Subject: [PATCH 2186/2556] iosched: tweak deadline for flash usage --- block/deadline-iosched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/deadline-iosched.c b/block/deadline-iosched.c index b547cbca7b23a..b2184bf749949 100644 --- a/block/deadline-iosched.c +++ b/block/deadline-iosched.c @@ -20,7 +20,7 @@ static const int read_expire = HZ / 2; /* max time before a read is submitted. */ static const int write_expire = 5 * HZ; /* ditto for writes, these limits are SOFT! */ static const int writes_starved = 2; /* max times reads can starve a write */ -static const int fifo_batch = 16; /* # of sequential requests treated as one +static const int fifo_batch = 1; /* # of sequential requests treated as one by the above parameters. For throughput. */ struct deadline_data { From 1a6eb58b70df450d77d3a5c2e09af4fa3390d321 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Mon, 16 Apr 2012 21:41:37 -0500 Subject: [PATCH 2187/2556] Revert "add Makefile FLAGS" This reverts commit a436b50a32801e6c3fa647e499fa6a0177e51d75. remove these optimizations for stock kernel i mainly dont like the cortex-a8 flag i dont think scorpions should have this flag --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 2fcd15e52a745..719bedc63e298 100644 --- a/Makefile +++ b/Makefile @@ -340,8 +340,8 @@ CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ CFLAGS_MODULE = AFLAGS_MODULE = LDFLAGS_MODULE = -CFLAGS_KERNEL = -O1 -mtune=cortex-a8 -ftree-vectorize -ffast-math -fsingle-precision-constant -AFLAGS_KERNEL = -O1 -mtune=cortex-a8 -ftree-vectorize -ffast-math -fsingle-precision-constant +CFLAGS_KERNEL = +AFLAGS_KERNEL = CFLAGS_GCOV = -fprofile-arcs -ftest-coverage From e67c29abf7a6e2fe9d81a0cd81fd308fa69d180e Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sat, 21 Apr 2012 22:19:11 -0500 Subject: [PATCH 2188/2556] Revert "arch: arm: msm: acpuclock-8x50: increase the low end by +.025" This reverts commit b8c9229fcd1acfefa0119ef5d3741e993647299e. return to more stock like values --- arch/arm/mach-msm/acpuclock-qsd8x50.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-msm/acpuclock-qsd8x50.c b/arch/arm/mach-msm/acpuclock-qsd8x50.c index 3978c60b25fb5..e7d5f2efd3ea9 100644 --- a/arch/arm/mach-msm/acpuclock-qsd8x50.c +++ b/arch/arm/mach-msm/acpuclock-qsd8x50.c @@ -74,11 +74,11 @@ static unsigned long max_axi_rate; #define SRC_PLL1 3 /* 768 MHz */ struct clkctl_acpu_speed acpu_freq_tbl[] = { - { 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 1000, 14000 }, - { 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 1000, 14000 }, - { 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 1025, 29000 }, + { 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 975, 14000 }, + { 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 975, 14000 }, + { 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 1000, 29000 }, /* Work around for acpu resume hung, GPLL is turn off by arm9 */ - /*{ 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 1025, 29000 },*/ + /*{ 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 1000, 29000 },*/ { 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 1025, 58000 }, { 422400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0B, 0, 1050, 117000 }, { 460800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0C, 0, 1050, 117000 }, From df3022a8a505d88fc797f0b40f60b92f4b0a50aa Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 24 Apr 2012 00:04:23 -0500 Subject: [PATCH 2189/2556] configs: add evervolv defconfigs --- arch/arm/configs/evervolv_bravo_defconfig | 2132 ++++++++++++++++ .../configs/evervolv_incrediblec_defconfig | 2167 +++++++++++++++++ arch/arm/configs/evervolv_mahimahi_defconfig | 2131 ++++++++++++++++ .../arm/configs/evervolv_supersonic_defconfig | 2157 ++++++++++++++++ 4 files changed, 8587 insertions(+) create mode 100644 arch/arm/configs/evervolv_bravo_defconfig create mode 100644 arch/arm/configs/evervolv_incrediblec_defconfig create mode 100644 arch/arm/configs/evervolv_mahimahi_defconfig create mode 100644 arch/arm/configs/evervolv_supersonic_defconfig diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig new file mode 100644 index 0000000000000..575176ec0b9a5 --- /dev/null +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -0,0 +1,2132 @@ +# +# Automatically generated make config: don't edit +# Linux/arm 2.6.38.8 Kernel Configuration +# Mon Apr 23 23:59:04 2012 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="-evervolv-turba-ics" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_SWAP=y +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED is not set +CONFIG_HAVE_SPARSE_IRQ=y +# CONFIG_GENERIC_PENDING_IRQ is not set +# CONFIG_AUTO_IRQ_AFFINITY is not set +# CONFIG_IRQ_PER_CPU is not set +# CONFIG_SPARSE_IRQ is not set + +# +# RCU Subsystem +# +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +CONFIG_TINY_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_TRACE is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_RCU_BOOST=y +CONFIG_RCU_BOOST_PRIO=1 +CONFIG_RCU_BOOST_DELAY=500 +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +# CONFIG_CGROUP_NS is not set +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +# CONFIG_CGROUP_MEM_RES_CTLR is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +# CONFIG_BLK_CGROUP is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_ASHMEM=y +CONFIG_AIO=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_IOSCHED_BFQ=y +# CONFIG_CGROUP_BFQIO is not set +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +CONFIG_DEFAULT_BFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="bfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +CONFIG_ARCH_MSM=y +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_S5PV310 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_TCC_926 is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_ARCH_MSM7X00A is not set +# CONFIG_ARCH_MSM7X30 is not set +CONFIG_ARCH_QSD8X50=y +# CONFIG_ARCH_MSM8X60 is not set +CONFIG_ARCH_MSM_SCORPION=y +CONFIG_HAS_MSM_DEBUG_UART_PHYS=y +CONFIG_MSM_MDP31=y +# CONFIG_PERFLOCK is not set + +# +# Qualcomm MSM Board Type +# +# CONFIG_MACH_SWORDFISH is not set +CONFIG_MACH_QSD8X50_SURF=y +# CONFIG_MACH_QSD8X50A_ST1_5 is not set +CONFIG_MSM_DEBUG_UART=1 +# CONFIG_MSM_DEBUG_UART_NONE is not set +CONFIG_MSM_DEBUG_UART1=y +# CONFIG_MSM_DEBUG_UART2 is not set +# CONFIG_MSM_DEBUG_UART3 is not set +CONFIG_MSM_PROC_COMM=y +# CONFIG_MACH_MAHIMAHI is not set +# CONFIG_MACH_BRAVO_NONE is not set +CONFIG_MACH_BRAVO=y +# CONFIG_MACH_BRAVOC is not set +# CONFIG_MACH_INCREDIBLE is not set +# CONFIG_MACH_INCREDIBLEC is not set +# CONFIG_MACH_SUPERSONIC is not set +# CONFIG_MACH_QSD8X50_FFA is not set +# CONFIG_HTC_HEADSET is not set +CONFIG_HTC_35MM_JACK=y +# CONFIG_HTC_BATTCHG is not set +CONFIG_HTC_PWRSPLY=y +# CONFIG_HTC_PWRSINK is not set +# CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set +# CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_MSM7X00A_USE_GP_TIMER=y +# CONFIG_MSM7X00A_USE_DG_TIMER is not set +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=1 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_FIQ_SUPPORT=y +# CONFIG_MSM_SERIAL_DEBUGGER is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_DAL=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y +CONFIG_MSM_CPU_FREQ_MAX=998400 +CONFIG_MSM_CPU_FREQ_MIN=245000 +# CONFIG_AXI_SCREEN_POLICY is not set +# CONFIG_MSM_HW3D is not set +CONFIG_HTC_ACOUSTIC_QSD=y +CONFIG_MSM_QDSP6=y +CONFIG_WIFI_CONTROL_FUNC=y +# CONFIG_WIFI_MEM_PREALLOC is not set +CONFIG_ARCH_MSM_FLASHLIGHT=y +# CONFIG_MICROP_COMMON is not set +# CONFIG_HTC_HEADSET_MGR is not set +# CONFIG_VIRTUAL_KPANIC_PARTITION is not set + +# +# System MMU +# + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_VERIFY_PERMISSION_FAULT=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_CACHE_ERR_REPORT is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_CPU_HAS_PMU=y +CONFIG_ARM_ERRATA_430973=y +CONFIG_ARM_ERRATA_458693=y +CONFIG_ARM_ERRATA_460075=y +CONFIG_ARM_ERRATA_743622=y +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_VMALLOC_RESERVE=0x08000000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_NEED_PER_CPU_KM=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM0" +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM_OPS=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +CONFIG_INET_ESP=y +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NETFILTER_XT_TARGET_HL=y +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_LED=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +CONFIG_NETFILTER_XT_TARGET_TRACE=y +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +CONFIG_NETFILTER_XT_MATCH_POLICY=y +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +# CONFIG_NF_NAT_SNMP_BASIC is not set +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +CONFIG_NF_NAT_H323=y +CONFIG_NF_NAT_SIP=y +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK_IPV6=y +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_TARGET_HL=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +# CONFIG_IP6_NF_RAW is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +CONFIG_NET_SCH_INGRESS=y + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +# CONFIG_GACT_PROB is not set +CONFIG_NET_ACT_MIRRED=y +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_PRIV=y +# CONFIG_CFG80211 is not set +CONFIG_WIRELESS_EXT_SYSFS=y +# CONFIG_LIB80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# + +# +# Some wireless drivers require a rate control algorithm +# +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_MSM_NAND=y +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_CRYPTOLOOP=y + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set +CONFIG_MISC_DEVICES=y +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_KERNEL_DEBUGGER_CORE=y +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +CONFIG_SENSORS_AKM8973=y +# CONFIG_SENSORS_AKM8976 is not set +# CONFIG_VP_A1026 is not set +# CONFIG_DS1682 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_WL127X_RFKILL is not set +# CONFIG_APANIC is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_IFB=y +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set +CONFIG_MII=y +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set +# CONFIG_ETHOC is not set +CONFIG_SMC911X=y +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8851_MLL is not set +CONFIG_NETDEV_1000=y +# CONFIG_STMMAC_ETH is not set +CONFIG_NETDEV_10000=y +CONFIG_WLAN=y +CONFIG_BCM4329=m +CONFIG_BCM4329_FW_PATH="/vendor/firmware/fw_bcm4329.bin" +CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" +# CONFIG_HOSTAP is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set + +# +# CAIF transport drivers +# +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +# CONFIG_PPPOE is not set +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_GAN_ETH=y +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_ATMEL is not set +# CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELAN_I2C_8232 is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_QT602240 is not set +# CONFIG_TOUCHSCREEN_MSM is not set +CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y +CONFIG_TOUCHSCREEN_DUPLICATED_FILTER=y +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set +# CONFIG_INPUT_CAPELLA_CM3602 is not set +CONFIG_INPUT_CAPELLA_CM3602_HTC=y +CONFIG_LIGHTSENSOR_MICROP=y +CONFIG_INPUT_OPTICALJOYSTICK=y +CONFIG_OPTICALJOYSTICK_CRUCIAL=y +CONFIG_OPTICALJOYSTICK_CRUCIAL_uP=y +# CONFIG_OPTICALJOYSTICK_CRUCIAL_SPI is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_BCM_BT_LPM=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_MSM=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_SPI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_BASIC_MMIO is not set +# CONFIG_GPIO_IT8761E is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +CONFIG_W1=y + +# +# 1-wire Bus Masters +# +CONFIG_W1_MASTER_DS2482=y +# CONFIG_W1_MASTER_DS1WM is not set +# CONFIG_W1_MASTER_GPIO is not set + +# +# 1-wire Slaves +# +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_DS2423 is not set +# CONFIG_W1_SLAVE_DS2431 is not set +# CONFIG_W1_SLAVE_DS2433 is not set +# CONFIG_W1_SLAVE_DS2760 is not set +# CONFIG_W1_SLAVE_BQ27000 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2782 is not set +CONFIG_BATTERY_DS2784=y +# CONFIG_BATTERY_BQ20Z75 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_MFD_SUPPORT=y +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_TPS65200 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_WL1273_CORE is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +CONFIG_REGULATOR_TPS65023=y +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_AD5398 is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +CONFIG_RC_CORE=y +CONFIG_LIRC=y +CONFIG_RC_MAP=y +CONFIG_IR_NEC_DECODER=y +CONFIG_IR_RC5_DECODER=y +CONFIG_IR_RC6_DECODER=y +CONFIG_IR_JVC_DECODER=y +CONFIG_IR_SONY_DECODER=y +CONFIG_IR_RC5_SZ_DECODER=y +CONFIG_IR_LIRC_CODEC=y +# CONFIG_IR_IMON is not set +# CONFIG_IR_MCEUSB is not set +# CONFIG_IR_STREAMZAP is not set +# CONFIG_RC_LOOPBACK is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +CONFIG_720P_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set + +# +# Camera Sensor Selection +# +# CONFIG_MT9T013 is not set +# CONFIG_MT9D112 is not set +# CONFIG_MT9P012 is not set +CONFIG_S5K3E2FX=y +# CONFIG_S5K6AAFX is not set +# CONFIG_OV8810 is not set +# CONFIG_OV9665 is not set +# CONFIG_S5K3H1GX is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +CONFIG_MSM_KGSL_MMU=y +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y +# CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_LEGACY_MDP=y +CONFIG_FB_MSM_MDP_PPP=y +CONFIG_FB_MSM_LCDC=y +# CONFIG_FB_MSM_MDDI is not set +# CONFIG_MSM_HDMI is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set +# CONFIG_HID_PID is not set + +# +# Special HID drivers +# +CONFIG_HID_APPLE=y +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_WACOM is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +# CONFIG_USB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA_U2O is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_CI13XXX_MSM is not set +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_MSM_72K=y +# CONFIG_USB_MSM_72K_HTC is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_ANDROID=y +# CONFIG_USB_ANDROID_ACM is not set +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_DIAG=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +# CONFIG_USB_ANDROID_MTP is not set +CONFIG_USB_ANDROID_RNDIS=y +CONFIG_USB_ANDROID_RNDIS_WCEIS=y +CONFIG_USB_ANDROID_ACCESSORY=y +CONFIG_USB_CSW_HACK=y +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG_72K is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_CLKGATE is not set +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_MSM=y +# CONFIG_MMC_DW is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +CONFIG_LEDS_CPLD=y +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +CONFIG_LEDS_TRIGGER_SLEEP=y + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_NFC_DEVICES is not set +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_MSM7X00A=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_ECHO is not set +# CONFIG_BRCM80211 is not set +# CONFIG_COMEDI is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE=128 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE=16 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE=8 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_POHMELFS is not set +# CONFIG_IIO is not set +CONFIG_XVMALLOC=y +CONFIG_ZRAM=y +# CONFIG_ZRAM_DEBUG is not set +# CONFIG_FB_SM7XX is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_ST_BT is not set +# CONFIG_LIRC_STAGING is not set +CONFIG_MACH_NO_WESTBRIDGE=y +# CONFIG_ATH6K_LEGACY is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +# CONFIG_DNOTIFY is not set +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=y +# CONFIG_CUSE is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=y +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_WEAK_PW_HASH=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_ACL is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_BKL is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_ARM_UNWIND is not set +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig new file mode 100644 index 0000000000000..84b8399fbc393 --- /dev/null +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -0,0 +1,2167 @@ +# +# Automatically generated make config: don't edit +# Linux/arm 2.6.38.8 Kernel Configuration +# Tue Apr 24 00:02:27 2012 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="arm-eabi-" +CONFIG_LOCALVERSION="-evervolv-dives-ics" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +CONFIG_KERNEL_LZMA=y +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_SWAP=y +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED is not set +CONFIG_HAVE_SPARSE_IRQ=y +# CONFIG_GENERIC_PENDING_IRQ is not set +# CONFIG_AUTO_IRQ_AFFINITY is not set +# CONFIG_IRQ_PER_CPU is not set +# CONFIG_SPARSE_IRQ is not set + +# +# RCU Subsystem +# +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +CONFIG_TINY_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_TRACE is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_RCU_BOOST=y +CONFIG_RCU_BOOST_PRIO=1 +CONFIG_RCU_BOOST_DELAY=500 +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_CGROUPS=y +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CGROUP_NS is not set +# CONFIG_CGROUP_FREEZER is not set +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +# CONFIG_RESOURCE_COUNTERS is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +# CONFIG_BLK_CGROUP is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_ASHMEM=y +CONFIG_AIO=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y + +# +# GCOV-based kernel profiling +# +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_IOSCHED_BFQ=y +# CONFIG_CGROUP_BFQIO is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_BFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +CONFIG_ARCH_MSM=y +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_S5PV310 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_TCC_926 is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_ARCH_MSM7X00A is not set +# CONFIG_ARCH_MSM7X30 is not set +CONFIG_ARCH_QSD8X50=y +# CONFIG_ARCH_MSM8X60 is not set +CONFIG_ARCH_MSM_SCORPION=y +CONFIG_HAS_MSM_DEBUG_UART_PHYS=y +CONFIG_MSM_MDP31=y +# CONFIG_PERFLOCK is not set + +# +# Qualcomm MSM Board Type +# +# CONFIG_MACH_SWORDFISH is not set +CONFIG_MACH_QSD8X50_SURF=y +# CONFIG_MACH_QSD8X50A_ST1_5 is not set +CONFIG_MSM_DEBUG_UART_NONE=y +# CONFIG_MSM_DEBUG_UART1 is not set +# CONFIG_MSM_DEBUG_UART2 is not set +# CONFIG_MSM_DEBUG_UART3 is not set +CONFIG_MSM_PROC_COMM=y +# CONFIG_MACH_MAHIMAHI is not set +CONFIG_MACH_BRAVO_NONE=y +# CONFIG_MACH_BRAVO is not set +# CONFIG_MACH_BRAVOC is not set +# CONFIG_MACH_INCREDIBLE is not set +CONFIG_MACH_INCREDIBLEC=y +# CONFIG_MACH_SUPERSONIC is not set +# CONFIG_MACH_QSD8X50_FFA is not set +# CONFIG_HTC_HEADSET is not set +# CONFIG_HTC_35MM_JACK is not set +CONFIG_HTC_BATTCHG=y +CONFIG_HTC_BATTCHG_SMEM=y +# CONFIG_HTC_PWRSPLY is not set +# CONFIG_HTC_PWRSINK is not set +# CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set +# CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_MSM7X00A_USE_GP_TIMER=y +# CONFIG_MSM7X00A_USE_DG_TIMER is not set +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=2 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=20000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_FIQ_SUPPORT=y +CONFIG_MSM_SERIAL_DEBUGGER=y +CONFIG_MSM_SERIAL_DEBUGGER_NO_SLEEP=y +# CONFIG_MSM_SERIAL_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON is not set +# CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_DAL=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y +CONFIG_MSM_CPU_FREQ_MAX=998000 +CONFIG_MSM_CPU_FREQ_MIN=245760 +# CONFIG_AXI_SCREEN_POLICY is not set +# CONFIG_MSM_HW3D is not set +CONFIG_HTC_ACOUSTIC_QSD=y +CONFIG_MSM_QDSP6=y +CONFIG_WIFI_CONTROL_FUNC=y +# CONFIG_WIFI_MEM_PREALLOC is not set +CONFIG_ARCH_MSM_FLASHLIGHT=y +CONFIG_MICROP_COMMON=y +CONFIG_HTC_HEADSET_MGR=y +CONFIG_HTC_HEADSET_GPIO=y +CONFIG_HTC_HEADSET_MICROP=y +# CONFIG_VIRTUAL_KPANIC_PARTITION is not set + +# +# System MMU +# + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_VERIFY_PERMISSION_FAULT=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_CACHE_ERR_REPORT is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_CPU_HAS_PMU=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_VMALLOC_RESERVE=0x08000000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_NEED_PER_CPU_KM=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM0" +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM_OPS=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +CONFIG_INET_ESP=y +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NETFILTER_XT_TARGET_HL=y +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_LED=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +CONFIG_NETFILTER_XT_TARGET_TRACE=y +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +CONFIG_NETFILTER_XT_MATCH_POLICY=y +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +# CONFIG_NF_NAT_SNMP_BASIC is not set +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +CONFIG_NF_NAT_H323=y +CONFIG_NF_NAT_SIP=y +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK_IPV6=y +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_TARGET_HL=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +# CONFIG_IP6_NF_RAW is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +CONFIG_NET_SCH_INGRESS=y + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +# CONFIG_GACT_PROB is not set +CONFIG_NET_ACT_MIRRED=y +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +CONFIG_DNS_RESOLVER=y +# CONFIG_BATMAN_ADV is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_PRIV=y +# CONFIG_CFG80211 is not set +CONFIG_WIRELESS_EXT_SYSFS=y +# CONFIG_LIB80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# + +# +# Some wireless drivers require a rate control algorithm +# +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_MSM_NAND=y +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_CRYPTOLOOP=y + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set +CONFIG_MISC_DEVICES=y +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_KERNEL_DEBUGGER_CORE=y +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +CONFIG_SENSORS_AKM8973=y +# CONFIG_SENSORS_AKM8976 is not set +# CONFIG_VP_A1026 is not set +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_SENSORS_BMA150_SPI=y +# CONFIG_APANIC is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=y +# CONFIG_DM_DEBUG is not set +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_IFB=m +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set +CONFIG_MII=y +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set +# CONFIG_ENC28J60 is not set +# CONFIG_ETHOC is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +CONFIG_WLAN=y +CONFIG_BCM4329=m +CONFIG_BCM4329_FW_PATH="/vendor/firmware/fw_bcm4329.bin" +CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" +# CONFIG_HOSTAP is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set + +# +# CAIF transport drivers +# +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +# CONFIG_PPPOE is not set +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_GAN_ETH=y +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL=y +CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT=y +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELAN_I2C_8232 is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_QT602240 is not set +# CONFIG_TOUCHSCREEN_MSM is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set +# CONFIG_INPUT_CAPELLA_CM3602 is not set +# CONFIG_INPUT_CAPELLA_CM3602_HTC is not set +CONFIG_LIGHTSENSOR_MICROP=y +CONFIG_INPUT_OPTICALJOYSTICK=y +CONFIG_OPTICALJOYSTICK_CRUCIAL=y +CONFIG_OPTICALJOYSTICK_CRUCIAL_uP=y +# CONFIG_OPTICALJOYSTICK_CRUCIAL_SPI is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_MSM=y +# CONFIG_SERIAL_MSM_CONSOLE is not set +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_BCM_BT_LPM=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_IFX6X60 is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_MSM=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_QSD=y +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_BASIC_MMIO is not set +# CONFIG_GPIO_IT8761E is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_DS2784 is not set +# CONFIG_BATTERY_BQ20Z75 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_MFD_SUPPORT=y +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_TPS65200 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_WL1273_CORE is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +CONFIG_REGULATOR_TPS65023=y +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_TPS6524X is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +CONFIG_RC_CORE=y +CONFIG_LIRC=y +CONFIG_RC_MAP=y +CONFIG_IR_NEC_DECODER=y +CONFIG_IR_RC5_DECODER=y +CONFIG_IR_RC6_DECODER=y +CONFIG_IR_JVC_DECODER=y +CONFIG_IR_SONY_DECODER=y +CONFIG_IR_RC5_SZ_DECODER=y +CONFIG_IR_LIRC_CODEC=y +# CONFIG_IR_IMON is not set +# CONFIG_IR_MCEUSB is not set +# CONFIG_IR_STREAMZAP is not set +# CONFIG_RC_LOOPBACK is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +CONFIG_720P_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set + +# +# Camera Sensor Selection +# +# CONFIG_MT9T013 is not set +# CONFIG_MT9D112 is not set +# CONFIG_MT9P012 is not set +# CONFIG_S5K3E2FX is not set +# CONFIG_S5K6AAFX is not set +CONFIG_OV8810=y +# CONFIG_OV9665 is not set +# CONFIG_S5K3H1GX is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +CONFIG_MSM_KGSL_MMU=y +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y +# CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_LEGACY_MDP=y +CONFIG_FB_MSM_MDP_PPP=y +CONFIG_FB_MSM_LCDC=y +CONFIG_FB_MSM_MDDI=y +# CONFIG_FB_MSM_MDDI_EPSON is not set +# CONFIG_FB_MSM_MDDI_NOVTEC is not set +# CONFIG_MSM_HDMI is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set +# CONFIG_HID_PID is not set + +# +# Special HID drivers +# +CONFIG_HID_APPLE=y +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_WACOM is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +# CONFIG_USB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA_U2O is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_CI13XXX_MSM is not set +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_MSM_72K=y +CONFIG_USB_MSM_72K_HTC=y +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_ANDROID=y +# CONFIG_USB_ANDROID_ACM is not set +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_DIAG=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +# CONFIG_USB_ANDROID_MTP is not set +CONFIG_USB_ANDROID_RNDIS=y +# CONFIG_USB_ANDROID_RNDIS_WCEIS is not set +# CONFIG_USB_ANDROID_ACCESSORY is not set +CONFIG_USB_CSW_HACK=y +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_ACCESSORY_DETECT is not set +# CONFIG_DOCK_ACCESSORY_DETECT is not set +# CONFIG_USB_BYPASS_VBUS_NOTIFY is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG_72K is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_CLKGATE is not set +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_MSM=y +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +CONFIG_LEDS_CPLD=y +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +CONFIG_LEDS_TRIGGER_SLEEP=y + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_NFC_DEVICES is not set +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_MSM7X00A=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_ECHO is not set +# CONFIG_BRCM80211 is not set +# CONFIG_COMEDI is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE=128 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE=16 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE=8 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_POHMELFS is not set +# CONFIG_IIO is not set +CONFIG_XVMALLOC=y +CONFIG_ZRAM=y +# CONFIG_ZRAM_DEBUG is not set +# CONFIG_FB_SM7XX is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_ST_BT is not set +# CONFIG_LIRC_STAGING is not set +CONFIG_MACH_NO_WESTBRIDGE=y +# CONFIG_ATH6K_LEGACY is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +# CONFIG_DNOTIFY is not set +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_AFFS_FS is not set +# CONFIG_ECRYPT_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=y +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_WEAK_PW_HASH=y +# CONFIG_CIFS_UPCALL is not set +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_DFS_UPCALL is not set +# CONFIG_CIFS_ACL is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +CONFIG_SCHED_DEBUG=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_BKL is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_ARM_UNWIND=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +CONFIG_KEYS=y +# CONFIG_KEYS_DEBUG_PROC_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig new file mode 100644 index 0000000000000..66c44a86f6d54 --- /dev/null +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -0,0 +1,2131 @@ +# +# Automatically generated make config: don't edit +# Linux/arm 2.6.38.8 Kernel Configuration +# Mon Apr 23 23:58:08 2012 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="-evervolv-perdo-ics" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_SWAP=y +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED is not set +CONFIG_HAVE_SPARSE_IRQ=y +# CONFIG_GENERIC_PENDING_IRQ is not set +# CONFIG_AUTO_IRQ_AFFINITY is not set +# CONFIG_IRQ_PER_CPU is not set +# CONFIG_SPARSE_IRQ is not set + +# +# RCU Subsystem +# +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +CONFIG_TINY_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_TRACE is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_RCU_BOOST=y +CONFIG_RCU_BOOST_PRIO=1 +CONFIG_RCU_BOOST_DELAY=500 +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +# CONFIG_CGROUP_NS is not set +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +# CONFIG_CGROUP_MEM_RES_CTLR is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +# CONFIG_BLK_CGROUP is not set +# CONFIG_NAMESPACES is not set +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_ASHMEM=y +CONFIG_AIO=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_IOSCHED_BFQ=y +# CONFIG_CGROUP_BFQIO is not set +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +CONFIG_DEFAULT_BFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="bfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +CONFIG_ARCH_MSM=y +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_S5PV310 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_TCC_926 is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_ARCH_MSM7X00A is not set +# CONFIG_ARCH_MSM7X30 is not set +CONFIG_ARCH_QSD8X50=y +# CONFIG_ARCH_MSM8X60 is not set +CONFIG_ARCH_MSM_SCORPION=y +CONFIG_HAS_MSM_DEBUG_UART_PHYS=y +CONFIG_MSM_MDP31=y +# CONFIG_PERFLOCK is not set + +# +# Qualcomm MSM Board Type +# +# CONFIG_MACH_SWORDFISH is not set +CONFIG_MACH_QSD8X50_SURF=y +# CONFIG_MACH_QSD8X50A_ST1_5 is not set +CONFIG_MSM_DEBUG_UART=1 +# CONFIG_MSM_DEBUG_UART_NONE is not set +CONFIG_MSM_DEBUG_UART1=y +# CONFIG_MSM_DEBUG_UART2 is not set +# CONFIG_MSM_DEBUG_UART3 is not set +CONFIG_MSM_PROC_COMM=y +CONFIG_MACH_MAHIMAHI=y +CONFIG_MACH_BRAVO_NONE=y +# CONFIG_MACH_BRAVO is not set +# CONFIG_MACH_BRAVOC is not set +# CONFIG_MACH_INCREDIBLE is not set +# CONFIG_MACH_INCREDIBLEC is not set +# CONFIG_MACH_SUPERSONIC is not set +# CONFIG_MACH_QSD8X50_FFA is not set +# CONFIG_HTC_HEADSET is not set +CONFIG_HTC_35MM_JACK=y +# CONFIG_HTC_BATTCHG is not set +CONFIG_HTC_PWRSPLY=y +# CONFIG_HTC_PWRSINK is not set +# CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set +# CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_MSM7X00A_USE_GP_TIMER=y +# CONFIG_MSM7X00A_USE_DG_TIMER is not set +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=1 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_FIQ_SUPPORT=y +# CONFIG_MSM_SERIAL_DEBUGGER is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_DAL=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y +CONFIG_MSM_CPU_FREQ_MAX=998400 +CONFIG_MSM_CPU_FREQ_MIN=245000 +# CONFIG_AXI_SCREEN_POLICY is not set +# CONFIG_MSM_HW3D is not set +CONFIG_HTC_ACOUSTIC_QSD=y +CONFIG_MSM_QDSP6=y +CONFIG_WIFI_CONTROL_FUNC=y +# CONFIG_WIFI_MEM_PREALLOC is not set +CONFIG_ARCH_MSM_FLASHLIGHT=y +# CONFIG_MICROP_COMMON is not set +# CONFIG_HTC_HEADSET_MGR is not set +# CONFIG_VIRTUAL_KPANIC_PARTITION is not set + +# +# System MMU +# + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_VERIFY_PERMISSION_FAULT=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_CACHE_ERR_REPORT is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_CPU_HAS_PMU=y +CONFIG_ARM_ERRATA_430973=y +CONFIG_ARM_ERRATA_458693=y +CONFIG_ARM_ERRATA_460075=y +CONFIG_ARM_ERRATA_743622=y +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_VMALLOC_RESERVE=0x08000000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_NEED_PER_CPU_KM=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM0" +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM_OPS=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +CONFIG_INET_ESP=y +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NETFILTER_XT_TARGET_HL=y +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_LED=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +CONFIG_NETFILTER_XT_TARGET_TRACE=y +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +CONFIG_NETFILTER_XT_MATCH_POLICY=y +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +# CONFIG_NF_NAT_SNMP_BASIC is not set +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +CONFIG_NF_NAT_H323=y +CONFIG_NF_NAT_SIP=y +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK_IPV6=y +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_TARGET_HL=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +# CONFIG_IP6_NF_RAW is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +CONFIG_NET_SCH_INGRESS=y + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +# CONFIG_GACT_PROB is not set +CONFIG_NET_ACT_MIRRED=y +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_PRIV=y +# CONFIG_CFG80211 is not set +CONFIG_WIRELESS_EXT_SYSFS=y +# CONFIG_LIB80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# + +# +# Some wireless drivers require a rate control algorithm +# +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_MSM_NAND=y +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_CRYPTOLOOP=y + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set +CONFIG_MISC_DEVICES=y +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_KERNEL_DEBUGGER_CORE=y +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +CONFIG_SENSORS_AKM8973=y +# CONFIG_SENSORS_AKM8976 is not set +CONFIG_VP_A1026=y +# CONFIG_DS1682 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_WL127X_RFKILL is not set +# CONFIG_APANIC is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_IFB=y +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set +CONFIG_MII=y +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set +# CONFIG_ETHOC is not set +CONFIG_SMC911X=y +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8851_MLL is not set +CONFIG_NETDEV_1000=y +# CONFIG_STMMAC_ETH is not set +CONFIG_NETDEV_10000=y +CONFIG_WLAN=y +CONFIG_BCM4329=m +CONFIG_BCM4329_FW_PATH="/vendor/firmware/fw_bcm4329.bin" +CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" +# CONFIG_HOSTAP is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set + +# +# CAIF transport drivers +# +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +# CONFIG_PPPOE is not set +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_GAN_ETH=y +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_ATMEL is not set +# CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELAN_I2C_8232 is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_QT602240 is not set +# CONFIG_TOUCHSCREEN_MSM is not set +CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y +CONFIG_TOUCHSCREEN_DUPLICATED_FILTER=y +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set +CONFIG_INPUT_CAPELLA_CM3602=y +# CONFIG_INPUT_CAPELLA_CM3602_HTC is not set +# CONFIG_LIGHTSENSOR_MICROP is not set +# CONFIG_INPUT_OPTICALJOYSTICK is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_BCM_BT_LPM=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_MSM=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_SPI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_BASIC_MMIO is not set +# CONFIG_GPIO_IT8761E is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +CONFIG_W1=y + +# +# 1-wire Bus Masters +# +CONFIG_W1_MASTER_DS2482=y +# CONFIG_W1_MASTER_DS1WM is not set +# CONFIG_W1_MASTER_GPIO is not set + +# +# 1-wire Slaves +# +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_DS2423 is not set +# CONFIG_W1_SLAVE_DS2431 is not set +# CONFIG_W1_SLAVE_DS2433 is not set +# CONFIG_W1_SLAVE_DS2760 is not set +# CONFIG_W1_SLAVE_BQ27000 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2782 is not set +CONFIG_BATTERY_DS2784=y +# CONFIG_BATTERY_BQ20Z75 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_MFD_SUPPORT=y +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_TPS65200 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_WL1273_CORE is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +CONFIG_REGULATOR_TPS65023=y +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_AD5398 is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +CONFIG_RC_CORE=y +CONFIG_LIRC=y +CONFIG_RC_MAP=y +CONFIG_IR_NEC_DECODER=y +CONFIG_IR_RC5_DECODER=y +CONFIG_IR_RC6_DECODER=y +CONFIG_IR_JVC_DECODER=y +CONFIG_IR_SONY_DECODER=y +CONFIG_IR_RC5_SZ_DECODER=y +CONFIG_IR_LIRC_CODEC=y +# CONFIG_IR_IMON is not set +# CONFIG_IR_MCEUSB is not set +# CONFIG_IR_STREAMZAP is not set +# CONFIG_RC_LOOPBACK is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +CONFIG_720P_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set + +# +# Camera Sensor Selection +# +# CONFIG_MT9T013 is not set +# CONFIG_MT9D112 is not set +# CONFIG_MT9P012 is not set +CONFIG_S5K3E2FX=y +# CONFIG_S5K6AAFX is not set +# CONFIG_OV8810 is not set +# CONFIG_OV9665 is not set +# CONFIG_S5K3H1GX is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +CONFIG_MSM_KGSL_MMU=y +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y +# CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_LEGACY_MDP=y +CONFIG_FB_MSM_MDP_PPP=y +CONFIG_FB_MSM_LCDC=y +# CONFIG_FB_MSM_MDDI is not set +# CONFIG_MSM_HDMI is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set +# CONFIG_HID_PID is not set + +# +# Special HID drivers +# +CONFIG_HID_APPLE=y +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_WACOM is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +# CONFIG_USB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA_U2O is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_CI13XXX_MSM is not set +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_MSM_72K=y +# CONFIG_USB_MSM_72K_HTC is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_ANDROID=y +# CONFIG_USB_ANDROID_ACM is not set +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_DIAG=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +# CONFIG_USB_ANDROID_MTP is not set +CONFIG_USB_ANDROID_RNDIS=y +CONFIG_USB_ANDROID_RNDIS_WCEIS=y +CONFIG_USB_ANDROID_ACCESSORY=y +CONFIG_USB_CSW_HACK=y +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG_72K is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_CLKGATE is not set +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_MSM=y +# CONFIG_MMC_DW is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +CONFIG_LEDS_CPLD=y +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +CONFIG_LEDS_TRIGGER_SLEEP=y + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_NFC_DEVICES is not set +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_MSM7X00A=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_ECHO is not set +# CONFIG_BRCM80211 is not set +# CONFIG_COMEDI is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE=128 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE=16 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE=8 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_POHMELFS is not set +# CONFIG_IIO is not set +CONFIG_XVMALLOC=y +CONFIG_ZRAM=y +# CONFIG_ZRAM_DEBUG is not set +# CONFIG_FB_SM7XX is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_ST_BT is not set +# CONFIG_LIRC_STAGING is not set +CONFIG_MACH_NO_WESTBRIDGE=y +# CONFIG_ATH6K_LEGACY is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +# CONFIG_DNOTIFY is not set +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=y +# CONFIG_CUSE is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=y +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_WEAK_PW_HASH=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_ACL is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +CONFIG_SCHED_DEBUG=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_BKL is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_ARM_UNWIND is not set +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_ERRORS=y +CONFIG_DEBUG_STACK_USAGE=y +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig new file mode 100644 index 0000000000000..ff486797d1a33 --- /dev/null +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -0,0 +1,2157 @@ +# +# Automatically generated make config: don't edit +# Linux/arm 2.6.38.8 Kernel Configuration +# Tue Apr 24 00:00:24 2012 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="-evervolv-acies-ics" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +CONFIG_KERNEL_LZMA=y +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_SWAP=y +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED is not set +CONFIG_HAVE_SPARSE_IRQ=y +# CONFIG_GENERIC_PENDING_IRQ is not set +# CONFIG_AUTO_IRQ_AFFINITY is not set +# CONFIG_IRQ_PER_CPU is not set +# CONFIG_SPARSE_IRQ is not set + +# +# RCU Subsystem +# +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +CONFIG_TINY_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_TRACE is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_RCU_BOOST=y +CONFIG_RCU_BOOST_PRIO=1 +CONFIG_RCU_BOOST_DELAY=500 +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_CGROUPS=y +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CGROUP_NS is not set +# CONFIG_CGROUP_FREEZER is not set +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +# CONFIG_CGROUP_MEM_RES_CTLR is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_CGROUP=y +# CONFIG_DEBUG_BLK_CGROUP is not set +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_USER_NS=y +CONFIG_PID_NS=y +# CONFIG_NET_NS is not set +CONFIG_SCHED_AUTOGROUP=y +# CONFIG_SYSFS_DEPRECATED is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_ASHMEM=y +CONFIG_AIO=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +CONFIG_BLK_DEV_BSG=y +CONFIG_BLK_DEV_INTEGRITY=y +CONFIG_BLK_DEV_THROTTLING=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_IOSCHED_BFQ=y +CONFIG_CGROUP_BFQIO=y +CONFIG_DEFAULT_BFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="bfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +CONFIG_ARCH_MSM=y +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_S5PV310 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_TCC_926 is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_ARCH_MSM7X00A is not set +# CONFIG_ARCH_MSM7X30 is not set +CONFIG_ARCH_QSD8X50=y +# CONFIG_ARCH_MSM8X60 is not set +CONFIG_ARCH_MSM_SCORPION=y +CONFIG_HAS_MSM_DEBUG_UART_PHYS=y +CONFIG_MSM_MDP31=y +# CONFIG_PERFLOCK is not set + +# +# Qualcomm MSM Board Type +# +# CONFIG_MACH_SWORDFISH is not set +CONFIG_MACH_QSD8X50_SURF=y +# CONFIG_MACH_QSD8X50A_ST1_5 is not set +CONFIG_MSM_DEBUG_UART_NONE=y +# CONFIG_MSM_DEBUG_UART1 is not set +# CONFIG_MSM_DEBUG_UART2 is not set +# CONFIG_MSM_DEBUG_UART3 is not set +CONFIG_MSM_PROC_COMM=y +# CONFIG_MACH_MAHIMAHI is not set +CONFIG_MACH_BRAVO_NONE=y +# CONFIG_MACH_BRAVO is not set +# CONFIG_MACH_BRAVOC is not set +# CONFIG_MACH_INCREDIBLE is not set +# CONFIG_MACH_INCREDIBLEC is not set +CONFIG_MACH_SUPERSONIC=y +# CONFIG_MACH_QSD8X50_FFA is not set +# CONFIG_HTC_HEADSET is not set +# CONFIG_HTC_35MM_JACK is not set +CONFIG_HTC_BATTCHG=y +CONFIG_HTC_BATTCHG_SMEM=y +# CONFIG_HTC_PWRSPLY is not set +# CONFIG_HTC_PWRSINK is not set +# CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set +# CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_MSM7X00A_USE_GP_TIMER=y +# CONFIG_MSM7X00A_USE_DG_TIMER is not set +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=1 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_FIQ_SUPPORT=y +# CONFIG_MSM_SERIAL_DEBUGGER is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_DAL=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y +CONFIG_MSM_CPU_FREQ_MAX=998400 +CONFIG_MSM_CPU_FREQ_MIN=245000 +# CONFIG_AXI_SCREEN_POLICY is not set +# CONFIG_MSM_HW3D is not set +CONFIG_HTC_ACOUSTIC_QSD=y +CONFIG_MSM_QDSP6=y +CONFIG_WIFI_CONTROL_FUNC=y +CONFIG_WIFI_MEM_PREALLOC=y +# CONFIG_ARCH_MSM_FLASHLIGHT is not set +CONFIG_MICROP_COMMON=y +CONFIG_HTC_HEADSET_MGR=y +CONFIG_HTC_HEADSET_GPIO=y +CONFIG_HTC_HEADSET_MICROP=y +# CONFIG_VIRTUAL_KPANIC_PARTITION is not set + +# +# System MMU +# + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_VERIFY_PERMISSION_FAULT=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_CACHE_ERR_REPORT is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_CPU_HAS_PMU=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_VMALLOC_RESERVE=0x08000000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_NEED_PER_CPU_KM=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM0" +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM_OPS=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_ASK_IP_FIB_HASH=y +# CONFIG_IP_FIB_TRIE is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_IP_ROUTE_MULTIPATH is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_XFRM_TUNNEL=y +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +# CONFIG_NF_CONNTRACK_H323 is not set +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NETFILTER_XT_TARGET_HL=y +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_LED=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +CONFIG_NETFILTER_XT_MATCH_POLICY=y +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +CONFIG_NETFILTER_XT_MATCH_RECENT=y +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +# CONFIG_NF_NAT_SNMP_BASIC is not set +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +# CONFIG_NF_NAT_H323 is not set +CONFIG_NF_NAT_SIP=y +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK_IPV6=y +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_TARGET_HL=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +# CONFIG_IP6_NF_RAW is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +CONFIG_NET_SCH_INGRESS=y + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +# CONFIG_GACT_PROB is not set +CONFIG_NET_ACT_MIRRED=y +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_PRIV=y +# CONFIG_CFG80211 is not set +CONFIG_WIRELESS_EXT_SYSFS=y +# CONFIG_LIB80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# + +# +# Some wireless drivers require a rate control algorithm +# +CONFIG_WIMAX=y +CONFIG_WIMAX_DEBUG_LEVEL=8 +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_MSM_NAND=y +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_CRYPTOLOOP=y + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set +CONFIG_MISC_DEVICES=y +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_KERNEL_DEBUGGER_CORE=y +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +CONFIG_SENSORS_AKM8973=y +# CONFIG_SENSORS_AKM8976 is not set +CONFIG_VP_A1026=y +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_SENSORS_BMA150_SPI=y +# CONFIG_APANIC is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +# CONFIG_IFB is not set +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set +CONFIG_MII=y +# CONFIG_PHYLIB is not set +# CONFIG_NET_ETHERNET is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +CONFIG_WLAN=y +CONFIG_BCM4329=m +CONFIG_BCM4329_FW_PATH="/system/etc/firmware/fw_bcm4329.bin" +CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" +# CONFIG_HOSTAP is not set + +# +# WiMAX Wireless Broadband devices +# + +# +# Enable USB support to see WiMAX USB drivers +# +# CONFIG_WIMAX_I2400M_SDIO is not set +CONFIG_WIMAX_SQN=m +# CONFIG_WAN is not set + +# +# CAIF transport drivers +# +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +# CONFIG_PPPOE is not set +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_GAN_ETH=y +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL=y +CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT=y +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELAN_I2C_8232 is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_QT602240 is not set +# CONFIG_TOUCHSCREEN_MSM is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set +# CONFIG_INPUT_CAPELLA_CM3602 is not set +# CONFIG_INPUT_CAPELLA_CM3602_HTC is not set +CONFIG_LIGHTSENSOR_MICROP=y +# CONFIG_INPUT_OPTICALJOYSTICK is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_BCM_BT_LPM=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_IFX6X60 is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_MSM=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +CONFIG_SPI_DEBUG=y +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_QSD=y +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_BASIC_MMIO is not set +# CONFIG_GPIO_IT8761E is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_DS2784 is not set +# CONFIG_BATTERY_BQ20Z75 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_MFD_SUPPORT=y +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +CONFIG_TPS65200=y +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_WL1273_CORE is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +CONFIG_REGULATOR_TPS65023=y +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_TPS6524X is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +CONFIG_720P_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set + +# +# Camera Sensor Selection +# +# CONFIG_MT9T013 is not set +# CONFIG_MT9D112 is not set +# CONFIG_MT9P012 is not set +# CONFIG_S5K3E2FX is not set +CONFIG_S5K6AAFX=y +CONFIG_OV8810=y +CONFIG_OV9665=y +CONFIG_S5K3H1GX=y + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +CONFIG_MSM_KGSL_MMU=y +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y +# CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_LEGACY_MDP=y +CONFIG_FB_MSM_MDP_PPP=y +CONFIG_FB_MSM_LCDC=y +CONFIG_FB_MSM_MDDI=y +CONFIG_FB_MSM_MDDI_EPSON=y +CONFIG_FB_MSM_MDDI_NOVTEC=y +CONFIG_MSM_HDMI=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set +# CONFIG_HID_PID is not set + +# +# Special HID drivers +# +CONFIG_HID_APPLE=y +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_WACOM is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +# CONFIG_USB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA_U2O is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_CI13XXX_MSM is not set +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_MSM_72K=y +CONFIG_USB_MSM_72K_HTC=y +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_ANDROID=y +# CONFIG_USB_ANDROID_ACM is not set +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_DIAG=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +# CONFIG_USB_ANDROID_MTP is not set +CONFIG_USB_ANDROID_RNDIS=y +CONFIG_USB_ANDROID_RNDIS_WCEIS=y +CONFIG_USB_ANDROID_ACCESSORY=y +CONFIG_USB_CSW_HACK=y +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +CONFIG_USB_ACCESSORY_DETECT=y +# CONFIG_USB_ACCESSORY_DETECT_BY_ADC is not set +CONFIG_DOCK_ACCESSORY_DETECT=y +# CONFIG_USB_BYPASS_VBUS_NOTIFY is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG_72K is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_CLKGATE is not set +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_MSM=y +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +CONFIG_LEDS_CPLD=y +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +CONFIG_LEDS_TRIGGER_SLEEP=y + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_NFC_DEVICES is not set +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_MSM7X00A=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_ECHO is not set +# CONFIG_BRCM80211 is not set +# CONFIG_COMEDI is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE=128 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE=16 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE=8 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_POHMELFS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_IIO is not set +CONFIG_XVMALLOC=y +CONFIG_ZRAM=y +# CONFIG_ZRAM_DEBUG is not set +# CONFIG_FB_SM7XX is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_ST_BT is not set +# CONFIG_SMB_FS is not set +CONFIG_MACH_NO_WESTBRIDGE=y +# CONFIG_ATH6K_LEGACY is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +# CONFIG_DNOTIFY is not set +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=y +# CONFIG_CUSE is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=y +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_WEAK_PW_HASH=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_ACL is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +CONFIG_SCHED_DEBUG=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +CONFIG_DEBUG_MUTEXES=y +CONFIG_BKL=y +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_ARM_UNWIND is not set +# CONFIG_DEBUG_USER is not set +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y From ce035e599218ab32bcf4770dd1a83af98176a495 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 18 Apr 2011 23:58:59 +0200 Subject: [PATCH 2190/2556] PM: Fix error code paths executed after failing syscore_suspend() If syscore_suspend() fails in suspend_enter(), create_image() or resume_target_kernel(), it is necessary to call sysdev_resume(), because sysdev_suspend() has been called already and succeeded and we are going to abort the transition. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- kernel/power/hibernate.c | 10 ++++++++-- kernel/power/suspend.c | 5 ++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index aeabd26e33427..50aae660174d6 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -273,8 +273,11 @@ static int create_image(int platform_mode) local_irq_disable(); error = sysdev_suspend(PMSG_FREEZE); - if (!error) + if (!error) { error = syscore_suspend(); + if (error) + sysdev_resume(); + } if (error) { printk(KERN_ERR "PM: Some system devices failed to power down, " "aborting hibernation\n"); @@ -407,8 +410,11 @@ static int resume_target_kernel(bool platform_mode) local_irq_disable(); error = sysdev_suspend(PMSG_QUIESCE); - if (!error) + if (!error) { error = syscore_suspend(); + if (error) + sysdev_resume(); + } if (error) goto Enable_irqs; diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index ccf4e7c2d3f41..98df7719127b9 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -167,8 +167,11 @@ static int suspend_enter(suspend_state_t state) BUG_ON(!irqs_disabled()); error = sysdev_suspend(PMSG_SUSPEND); - if (!error) + if (!error) { error = syscore_suspend(); + if (error) + sysdev_resume(); + } if (!error) { if (!(suspend_test(TEST_CORE) || pm_wakeup_pending())) { error = suspend_ops->enter(state); From 5d78da21a77814e026679da17b077bdf779ad028 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 20 Apr 2011 00:36:11 +0200 Subject: [PATCH 2191/2556] PM: Add missing syscore_suspend() and syscore_resume() calls Device suspend/resume infrastructure is used not only by the suspend and hibernate code in kernel/power, but also by APM, Xen and the kexec jump feature. However, commit 40dc166cb5dddbd36aa4ad11c03915ea (PM / Core: Introduce struct syscore_ops for core subsystems PM) failed to add syscore_suspend() and syscore_resume() calls to that code, which generally leads to breakage when the features in question are used. To fix this problem, add the missing syscore_suspend() and syscore_resume() calls to arch/x86/kernel/apm_32.c, kernel/kexec.c and drivers/xen/manage.c. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman Acked-by: Ian Campbell Conflicts: arch/x86/kernel/apm_32.c drivers/xen/manage.c Signed-off-by: Andrew Sutherland --- arch/x86/kernel/apm_32.c | 24 ++---- drivers/base/syscore.c | 2 + drivers/xen/manage.c | 174 ++++++++++++++++++++++----------------- kernel/kexec.c | 7 ++ 4 files changed, 118 insertions(+), 89 deletions(-) diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c index 0e4f24c2a746f..adee12e0da1fc 100644 --- a/arch/x86/kernel/apm_32.c +++ b/arch/x86/kernel/apm_32.c @@ -66,7 +66,7 @@ * 1.5: Fix segment register reloading (in case of bad segments saved * across BIOS call). * Stephen Rothwell - * 1.6: Cope with complier/assembler differences. + * 1.6: Cope with compiler/assembler differences. * Only try to turn off the first display device. * Fix OOPS at power off with no APM BIOS by Jan Echternach * @@ -227,6 +227,8 @@ #include #include #include +#include +#include #include #include @@ -975,20 +977,10 @@ static void apm_cpu_idle(void) static void apm_power_off(void) { - unsigned char po_bios_call[] = { - 0xb8, 0x00, 0x10, /* movw $0x1000,ax */ - 0x8e, 0xd0, /* movw ax,ss */ - 0xbc, 0x00, 0xf0, /* movw $0xf000,sp */ - 0xb8, 0x07, 0x53, /* movw $0x5307,ax */ - 0xbb, 0x01, 0x00, /* movw $0x0001,bx */ - 0xb9, 0x03, 0x00, /* movw $0x0003,cx */ - 0xcd, 0x15 /* int $0x15 */ - }; - /* Some bioses don't like being called from CPU != 0 */ if (apm_info.realmode_power_off) { set_cpus_allowed_ptr(current, cpumask_of(0)); - machine_real_restart(po_bios_call, sizeof(po_bios_call)); + machine_real_restart(MRR_APM); } else { (void)set_system_power_state(APM_STATE_OFF); } @@ -1247,6 +1239,7 @@ static int suspend(int vetoable) local_irq_disable(); sysdev_suspend(PMSG_SUSPEND); + syscore_suspend(); local_irq_enable(); @@ -1264,6 +1257,7 @@ static int suspend(int vetoable) apm_error("suspend", err); err = (err == APM_SUCCESS) ? 0 : -EIO; + syscore_resume(); sysdev_resume(); local_irq_enable(); @@ -1289,6 +1283,7 @@ static void standby(void) local_irq_disable(); sysdev_suspend(PMSG_SUSPEND); + syscore_suspend(); local_irq_enable(); err = set_system_power_state(APM_STATE_STANDBY); @@ -1296,6 +1291,7 @@ static void standby(void) apm_error("standby", err); local_irq_disable(); + syscore_resume(); sysdev_resume(); local_irq_enable(); @@ -2331,12 +2327,11 @@ static int __init apm_init(void) apm_info.disabled = 1; return -ENODEV; } - if (pm_flags & PM_ACPI) { + if (!acpi_disabled) { printk(KERN_NOTICE "apm: overridden by ACPI.\n"); apm_info.disabled = 1; return -ENODEV; } - pm_flags |= PM_APM; /* * Set up the long jump entry point to the APM BIOS, which is called @@ -2428,7 +2423,6 @@ static void __exit apm_exit(void) kthread_stop(kapmd_task); kapmd_task = NULL; } - pm_flags &= ~PM_APM; } module_init(apm_init); diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c index 90af2943f9e4a..c126db3cb7d12 100644 --- a/drivers/base/syscore.c +++ b/drivers/base/syscore.c @@ -73,6 +73,7 @@ int syscore_suspend(void) return ret; } +EXPORT_SYMBOL_GPL(syscore_suspend); /** * syscore_resume - Execute all the registered system core resume callbacks. @@ -95,6 +96,7 @@ void syscore_resume(void) "Interrupts enabled after %pF\n", ops->resume); } } +EXPORT_SYMBOL_GPL(syscore_resume); #endif /* CONFIG_PM_SLEEP */ /** diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index 24177272bcb84..a2eee574784e8 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -34,73 +35,73 @@ enum shutdown_state { /* Ignore multiple shutdown requests. */ static enum shutdown_state shutting_down = SHUTDOWN_INVALID; -#ifdef CONFIG_PM_SLEEP -static int xen_hvm_suspend(void *data) -{ - int err; - struct sched_shutdown r = { .reason = SHUTDOWN_suspend }; - int *cancelled = data; - - BUG_ON(!irqs_disabled()); - - err = sysdev_suspend(PMSG_SUSPEND); - if (err) { - printk(KERN_ERR "xen_hvm_suspend: sysdev_suspend failed: %d\n", - err); - return err; - } - - *cancelled = HYPERVISOR_sched_op(SCHEDOP_shutdown, &r); +struct suspend_info { + int cancelled; + unsigned long arg; /* extra hypercall argument */ + void (*pre)(void); + void (*post)(int cancelled); +}; - xen_hvm_post_suspend(*cancelled); +static void xen_hvm_post_suspend(int cancelled) +{ + xen_arch_hvm_post_suspend(cancelled); gnttab_resume(); +} - if (!*cancelled) { - xen_irq_resume(); - xen_console_resume(); - xen_timer_resume(); - } - - sysdev_resume(); +static void xen_pre_suspend(void) +{ + xen_mm_pin_all(); + gnttab_suspend(); + xen_arch_pre_suspend(); +} - return 0; +static void xen_post_suspend(int cancelled) +{ + xen_arch_post_suspend(cancelled); + gnttab_resume(); + xen_mm_unpin_all(); } +#ifdef CONFIG_HIBERNATE_CALLBACKS static int xen_suspend(void *data) { + struct suspend_info *si = data; int err; - int *cancelled = data; BUG_ON(!irqs_disabled()); - err = sysdev_suspend(PMSG_SUSPEND); + err = sysdev_suspend(PMSG_FREEZE); + if (!err) { + err = syscore_suspend(); + if (err) + sysdev_resume(); + } if (err) { - printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n", + printk(KERN_ERR "xen_suspend: system core suspend failed: %d\n", err); return err; } - xen_mm_pin_all(); - gnttab_suspend(); - xen_pre_suspend(); + if (si->pre) + si->pre(); /* * This hypercall returns 1 if suspend was cancelled * or the domain was merely checkpointed, and 0 if it * is resuming in a new domain. */ - *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info)); + si->cancelled = HYPERVISOR_suspend(si->arg); - xen_post_suspend(*cancelled); - gnttab_resume(); - xen_mm_unpin_all(); + if (si->post) + si->post(si->cancelled); - if (!*cancelled) { + if (!si->cancelled) { xen_irq_resume(); xen_console_resume(); xen_timer_resume(); } + syscore_resume(); sysdev_resume(); return 0; @@ -109,7 +110,7 @@ static int xen_suspend(void *data) static void do_suspend(void) { int err; - int cancelled = 1; + struct suspend_info si; shutting_down = SHUTDOWN_SUSPEND; @@ -124,7 +125,7 @@ static void do_suspend(void) } #endif - err = dpm_suspend_start(PMSG_SUSPEND); + err = dpm_suspend_start(PMSG_FREEZE); if (err) { printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err); goto out_thaw; @@ -133,32 +134,41 @@ static void do_suspend(void) printk(KERN_DEBUG "suspending xenstore...\n"); xs_suspend(); - err = dpm_suspend_noirq(PMSG_SUSPEND); + err = dpm_suspend_noirq(PMSG_FREEZE); if (err) { printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err); goto out_resume; } - if (xen_hvm_domain()) - err = stop_machine(xen_hvm_suspend, &cancelled, cpumask_of(0)); - else - err = stop_machine(xen_suspend, &cancelled, cpumask_of(0)); + si.cancelled = 1; + + if (xen_hvm_domain()) { + si.arg = 0UL; + si.pre = NULL; + si.post = &xen_hvm_post_suspend; + } else { + si.arg = virt_to_mfn(xen_start_info); + si.pre = &xen_pre_suspend; + si.post = &xen_post_suspend; + } + + err = stop_machine(xen_suspend, &si, cpumask_of(0)); - dpm_resume_noirq(PMSG_RESUME); + dpm_resume_noirq(si.cancelled ? PMSG_THAW : PMSG_RESTORE); if (err) { printk(KERN_ERR "failed to start xen_suspend: %d\n", err); - cancelled = 1; + si.cancelled = 1; } out_resume: - if (!cancelled) { + if (!si.cancelled) { xen_arch_resume(); xs_resume(); } else xs_suspend_cancel(); - dpm_resume_end(PMSG_RESUME); + dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE); /* Make sure timer events get retriggered on all CPUs */ clock_was_set(); @@ -170,7 +180,24 @@ static void do_suspend(void) #endif shutting_down = SHUTDOWN_INVALID; } -#endif /* CONFIG_PM_SLEEP */ +#endif /* CONFIG_HIBERNATE_CALLBACKS */ + +struct shutdown_handler { + const char *command; + void (*cb)(void); +}; + +static void do_poweroff(void) +{ + shutting_down = SHUTDOWN_POWEROFF; + orderly_poweroff(false); +} + +static void do_reboot(void) +{ + shutting_down = SHUTDOWN_POWEROFF; /* ? */ + ctrl_alt_del(); +} static void shutdown_handler(struct xenbus_watch *watch, const char **vec, unsigned int len) @@ -178,6 +205,16 @@ static void shutdown_handler(struct xenbus_watch *watch, char *str; struct xenbus_transaction xbt; int err; + static struct shutdown_handler handlers[] = { + { "poweroff", do_poweroff }, + { "halt", do_poweroff }, + { "reboot", do_reboot }, +#ifdef CONFIG_HIBERNATE_CALLBACKS + { "suspend", do_suspend }, +#endif + {NULL, NULL}, + }; + static struct shutdown_handler *handler; if (shutting_down != SHUTDOWN_INVALID) return; @@ -194,7 +231,14 @@ static void shutdown_handler(struct xenbus_watch *watch, return; } - xenbus_write(xbt, "control", "shutdown", ""); + for (handler = &handlers[0]; handler->command; handler++) { + if (strcmp(str, handler->command) == 0) + break; + } + + /* Only acknowledge commands which we are prepared to handle. */ + if (handler->cb) + xenbus_write(xbt, "control", "shutdown", ""); err = xenbus_transaction_end(xbt, 0); if (err == -EAGAIN) { @@ -202,17 +246,8 @@ static void shutdown_handler(struct xenbus_watch *watch, goto again; } - if (strcmp(str, "poweroff") == 0 || - strcmp(str, "halt") == 0) { - shutting_down = SHUTDOWN_POWEROFF; - orderly_poweroff(false); - } else if (strcmp(str, "reboot") == 0) { - shutting_down = SHUTDOWN_POWEROFF; /* ? */ - ctrl_alt_del(); -#ifdef CONFIG_PM_SLEEP - } else if (strcmp(str, "suspend") == 0) { - do_suspend(); -#endif + if (handler->cb) { + handler->cb(); } else { printk(KERN_INFO "Ignoring shutdown request: %s\n", str); shutting_down = SHUTDOWN_INVALID; @@ -291,27 +326,18 @@ static int shutdown_event(struct notifier_block *notifier, return NOTIFY_DONE; } -static int __init __setup_shutdown_event(void) -{ - /* Delay initialization in the PV on HVM case */ - if (xen_hvm_domain()) - return 0; - - if (!xen_pv_domain()) - return -ENODEV; - - return xen_setup_shutdown_event(); -} - int xen_setup_shutdown_event(void) { static struct notifier_block xenstore_notifier = { .notifier_call = shutdown_event }; + + if (!xen_domain()) + return -ENODEV; register_xenstore_notifier(&xenstore_notifier); return 0; } EXPORT_SYMBOL_GPL(xen_setup_shutdown_event); -subsys_initcall(__setup_shutdown_event); +subsys_initcall(xen_setup_shutdown_event); diff --git a/kernel/kexec.c b/kernel/kexec.c index ec19b92c7ebda..9669d06bb4023 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -1531,6 +1532,11 @@ int kernel_kexec(void) local_irq_disable(); /* Suspend system devices */ error = sysdev_suspend(PMSG_FREEZE); + if (!error) { + error = syscore_suspend(); + if (error) + sysdev_resume(); + } if (error) goto Enable_irqs; } else @@ -1545,6 +1551,7 @@ int kernel_kexec(void) #ifdef CONFIG_KEXEC_JUMP if (kexec_image->preserve_context) { + syscore_resume(); sysdev_resume(); Enable_irqs: local_irq_enable(); From 9066e6569c60888d8a03b3b92169df198cb433ae Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 1 Feb 2011 17:19:57 +0100 Subject: [PATCH 2192/2556] sysdev: Fixup warning message Use gcc's __func__ instead of the function name. Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/base/sys.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 1667aaf4fde68..b094e0d6139fa 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -181,8 +181,8 @@ int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv) int err = 0; if (!cls) { - WARN(1, KERN_WARNING "sysdev: invalid class passed to " - "sysdev_driver_register!\n"); + WARN(1, KERN_WARNING "sysdev: invalid class passed to %s!\n", + __func__); return -EINVAL; } From 61d51f11379a5890856dcf096706c5d204403e33 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 1 Feb 2011 17:19:56 +0100 Subject: [PATCH 2193/2556] sysdev: Do not register with sysdev when erroring on add When encountering an error while executing the driver's ->add method, we should cancel registration and unwind what we've regged so far. The low level ->add methods do return proper error codes but those aren't looked at in sysdev_driver_register(). Fix that by sharing the unregistering code. Signed-off-by: Borislav Petkov Signed-off-by: Greg Kroah-Hartman --- drivers/base/sys.c | 61 +++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/drivers/base/sys.c b/drivers/base/sys.c index b094e0d6139fa..f6fb54741602b 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -166,6 +166,36 @@ EXPORT_SYMBOL_GPL(sysdev_class_unregister); static DEFINE_MUTEX(sysdev_drivers_lock); +/* + * @dev != NULL means that we're unwinding because some drv->add() + * failed for some reason. You need to grab sysdev_drivers_lock before + * calling this. + */ +static void __sysdev_driver_remove(struct sysdev_class *cls, + struct sysdev_driver *drv, + struct sys_device *from_dev) +{ + struct sys_device *dev = from_dev; + + list_del_init(&drv->entry); + if (!cls) + return; + + if (!drv->remove) + goto kset_put; + + if (dev) + list_for_each_entry_continue_reverse(dev, &cls->kset.list, + kobj.entry) + drv->remove(dev); + else + list_for_each_entry(dev, &cls->kset.list, kobj.entry) + drv->remove(dev); + +kset_put: + kset_put(&cls->kset); +} + /** * sysdev_driver_register - Register auxillary driver * @cls: Device class driver belongs to. @@ -175,9 +205,9 @@ static DEFINE_MUTEX(sysdev_drivers_lock); * called on each operation on devices of that class. The refcount * of @cls is incremented. */ - int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv) { + struct sys_device *dev = NULL; int err = 0; if (!cls) { @@ -198,19 +228,27 @@ int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv) /* If devices of this class already exist, tell the driver */ if (drv->add) { - struct sys_device *dev; - list_for_each_entry(dev, &cls->kset.list, kobj.entry) - drv->add(dev); + list_for_each_entry(dev, &cls->kset.list, kobj.entry) { + err = drv->add(dev); + if (err) + goto unwind; + } } } else { err = -EINVAL; WARN(1, KERN_ERR "%s: invalid device class\n", __func__); } + + goto unlock; + +unwind: + __sysdev_driver_remove(cls, drv, dev); + +unlock: mutex_unlock(&sysdev_drivers_lock); return err; } - /** * sysdev_driver_unregister - Remove an auxillary driver. * @cls: Class driver belongs to. @@ -220,23 +258,12 @@ void sysdev_driver_unregister(struct sysdev_class *cls, struct sysdev_driver *drv) { mutex_lock(&sysdev_drivers_lock); - list_del_init(&drv->entry); - if (cls) { - if (drv->remove) { - struct sys_device *dev; - list_for_each_entry(dev, &cls->kset.list, kobj.entry) - drv->remove(dev); - } - kset_put(&cls->kset); - } + __sysdev_driver_remove(cls, drv, NULL); mutex_unlock(&sysdev_drivers_lock); } - EXPORT_SYMBOL_GPL(sysdev_driver_register); EXPORT_SYMBOL_GPL(sysdev_driver_unregister); - - /** * sysdev_register - add a system device to the tree * @sysdev: device in question From cf52d3cbd96deff9160d7030c1e3f6626124d1e3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 23 Mar 2011 22:16:41 +0100 Subject: [PATCH 2194/2556] Introduce ARCH_NO_SYSDEV_OPS config option (v2) Introduce Kconfig option allowing architectures where sysdev operations used during system suspend, resume and shutdown have been completely replaced with struct sycore_ops operations to avoid building sysdev code that will never be used. Make callbacks in struct sys_device and struct sysdev_driver depend on ARCH_NO_SYSDEV_OPS to allows us to verify if all of the references have been actually removed from the code the given architecture depends on. Make x86 select ARCH_NO_SYSDEV_OPS. Signed-off-by: Rafael J. Wysocki Conflicts: drivers/base/Kconfig drewis: patch "Fix common misspellings" for sys.c as well Signed-off-by: Andrew Sutherland --- arch/x86/Kconfig | 1 + drivers/base/Kconfig | 6 ++++++ drivers/base/sys.c | 17 +++++++++-------- include/linux/device.h | 4 ++++ include/linux/pm.h | 10 ++++++++-- include/linux/sysdev.h | 7 +++++-- 6 files changed, 33 insertions(+), 12 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index d5ed94d30aadf..6d506dc2dbe02 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -67,6 +67,7 @@ config X86 select GENERIC_IRQ_PROBE select GENERIC_PENDING_IRQ if SMP select USE_GENERIC_SMP_HELPERS if SMP + select ARCH_NO_SYSDEV_OPS config INSTRUCTION_DECODER def_bool (KPROBES || PERF_EVENTS) diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index ad6fb3a3d98a9..2e743879779a0 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -182,5 +182,11 @@ config GENLOCK_MISCDEVICE Create a miscdevice for the purposes of allowing userspace to create and interact with locks created using genlock. +config ARCH_NO_SYSDEV_OPS + bool + ---help--- + To be selected by architectures that don't use sysdev class or + sysdev driver power management (suspend/resume) and shutdown + operations. endmenu diff --git a/drivers/base/sys.c b/drivers/base/sys.c index f6fb54741602b..acde9b5ee1314 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -197,7 +197,7 @@ static void __sysdev_driver_remove(struct sysdev_class *cls, } /** - * sysdev_driver_register - Register auxillary driver + * sysdev_driver_register - Register auxiliary driver * @cls: Device class driver belongs to. * @drv: Driver. * @@ -250,7 +250,7 @@ int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv) } /** - * sysdev_driver_unregister - Remove an auxillary driver. + * sysdev_driver_unregister - Remove an auxiliary driver. * @cls: Class driver belongs to. * @drv: Driver. */ @@ -302,7 +302,7 @@ int sysdev_register(struct sys_device *sysdev) * code that should have called us. */ - /* Notify class auxillary drivers */ + /* Notify class auxiliary drivers */ list_for_each_entry(drv, &cls->drivers, entry) { if (drv->add) drv->add(sysdev); @@ -329,13 +329,13 @@ void sysdev_unregister(struct sys_device *sysdev) } - +#ifndef CONFIG_ARCH_NO_SYSDEV_OPS /** * sysdev_shutdown - Shut down all system devices. * * Loop over each class of system devices, and the devices in each * of those classes. For each device, we call the shutdown method for - * each driver registered for the device - the auxillaries, + * each driver registered for the device - the auxiliaries, * and the class driver. * * Note: The list is iterated in reverse order, so that we shut down @@ -360,7 +360,7 @@ void sysdev_shutdown(void) struct sysdev_driver *drv; pr_debug(" %s\n", kobject_name(&sysdev->kobj)); - /* Call auxillary drivers first */ + /* Call auxiliary drivers first */ list_for_each_entry(drv, &cls->drivers, entry) { if (drv->shutdown) drv->shutdown(sysdev); @@ -385,7 +385,7 @@ static void __sysdev_resume(struct sys_device *dev) WARN_ONCE(!irqs_disabled(), "Interrupts enabled after %pF\n", cls->resume); - /* Call auxillary drivers next. */ + /* Call auxiliary drivers next. */ list_for_each_entry(drv, &cls->drivers, entry) { if (drv->resume) drv->resume(dev); @@ -432,7 +432,7 @@ int sysdev_suspend(pm_message_t state) list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { pr_debug(" %s\n", kobject_name(&sysdev->kobj)); - /* Call auxillary drivers first */ + /* Call auxiliary drivers first */ list_for_each_entry(drv, &cls->drivers, entry) { if (drv->suspend) { ret = drv->suspend(sysdev, state); @@ -524,6 +524,7 @@ int sysdev_resume(void) return 0; } EXPORT_SYMBOL_GPL(sysdev_resume); +#endif /* CONFIG_ARCH_NO_SYSDEV_OPS */ int __init system_bus_init(void) { diff --git a/include/linux/device.h b/include/linux/device.h index 1bf5cf0b45131..ceb5209f7f360 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -634,8 +634,12 @@ static inline int devtmpfs_mount(const char *mountpoint) { return 0; } /* drivers/base/power/shutdown.c */ extern void device_shutdown(void); +#ifndef CONFIG_ARCH_NO_SYSDEV_OPS /* drivers/base/sys.c */ extern void sysdev_shutdown(void); +#else +static inline void sysdev_shutdown(void) { } +#endif /* debugging and troubleshooting/diagnostic helpers. */ extern const char *dev_driver_string(const struct device *dev); diff --git a/include/linux/pm.h b/include/linux/pm.h index 21415cc91cbb4..136523856f201 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -521,13 +521,19 @@ extern void update_pm_runtime_accounting(struct device *dev); */ #ifdef CONFIG_PM_SLEEP -extern void device_pm_lock(void); +#ifndef CONFIG_ARCH_NO_SYSDEV_OPS +extern int sysdev_suspend(pm_message_t state); extern int sysdev_resume(void); +#else +static inline int sysdev_suspend(pm_message_t state) { return 0; } +static inline int sysdev_resume(void) { return 0; } +#endif + +extern void device_pm_lock(void); extern void dpm_resume_noirq(pm_message_t state); extern void dpm_resume_end(pm_message_t state); extern void device_pm_unlock(void); -extern int sysdev_suspend(pm_message_t state); extern int dpm_suspend_noirq(pm_message_t state); extern int dpm_suspend_start(pm_message_t state); diff --git a/include/linux/sysdev.h b/include/linux/sysdev.h index 1154c29f41018..8a75da551e4e9 100644 --- a/include/linux/sysdev.h +++ b/include/linux/sysdev.h @@ -33,12 +33,13 @@ struct sysdev_class { const char *name; struct list_head drivers; struct sysdev_class_attribute **attrs; - + struct kset kset; +#ifndef CONFIG_ARCH_NO_SYSDEV_OPS /* Default operations for these types of devices */ int (*shutdown)(struct sys_device *); int (*suspend)(struct sys_device *, pm_message_t state); int (*resume)(struct sys_device *); - struct kset kset; +#endif }; struct sysdev_class_attribute { @@ -76,9 +77,11 @@ struct sysdev_driver { struct list_head entry; int (*add)(struct sys_device *); int (*remove)(struct sys_device *); +#ifndef CONFIG_ARCH_NO_SYSDEV_OPS int (*shutdown)(struct sys_device *); int (*suspend)(struct sys_device *, pm_message_t state); int (*resume)(struct sys_device *); +#endif }; From d6269336548154a8a095dd76554b8a670f6c5e34 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 26 Apr 2011 19:15:07 +0200 Subject: [PATCH 2195/2556] PM: Remove sysdev suspend, resume and shutdown operations Since suspend, resume and shutdown operations in struct sysdev_class and struct sysdev_driver are not used any more, remove them. Also drop sysdev_suspend(), sysdev_resume() and sysdev_shutdown() used for executing those operations and modify all of their users accordingly. This reduces kernel code size quite a bit and reduces its complexity. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman Conflicts: arch/sh/Kconfig drivers/base/Kconfig --- arch/x86/Kconfig | 1 - arch/x86/kernel/apm_32.c | 4 - drivers/base/Kconfig | 7 -- drivers/base/base.h | 2 - drivers/base/sys.c | 202 +-------------------------------------- drivers/xen/manage.c | 8 +- include/linux/device.h | 7 -- include/linux/pm.h | 8 -- include/linux/sysdev.h | 11 --- kernel/kexec.c | 9 +- kernel/power/hibernate.c | 18 +--- kernel/power/suspend.c | 8 +- kernel/sys.c | 3 - 13 files changed, 7 insertions(+), 281 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 6d506dc2dbe02..d5ed94d30aadf 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -67,7 +67,6 @@ config X86 select GENERIC_IRQ_PROBE select GENERIC_PENDING_IRQ if SMP select USE_GENERIC_SMP_HELPERS if SMP - select ARCH_NO_SYSDEV_OPS config INSTRUCTION_DECODER def_bool (KPROBES || PERF_EVENTS) diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c index adee12e0da1fc..3bfa022359659 100644 --- a/arch/x86/kernel/apm_32.c +++ b/arch/x86/kernel/apm_32.c @@ -1238,7 +1238,6 @@ static int suspend(int vetoable) dpm_suspend_noirq(PMSG_SUSPEND); local_irq_disable(); - sysdev_suspend(PMSG_SUSPEND); syscore_suspend(); local_irq_enable(); @@ -1258,7 +1257,6 @@ static int suspend(int vetoable) err = (err == APM_SUCCESS) ? 0 : -EIO; syscore_resume(); - sysdev_resume(); local_irq_enable(); dpm_resume_noirq(PMSG_RESUME); @@ -1282,7 +1280,6 @@ static void standby(void) dpm_suspend_noirq(PMSG_SUSPEND); local_irq_disable(); - sysdev_suspend(PMSG_SUSPEND); syscore_suspend(); local_irq_enable(); @@ -1292,7 +1289,6 @@ static void standby(void) local_irq_disable(); syscore_resume(); - sysdev_resume(); local_irq_enable(); dpm_resume_noirq(PMSG_RESUME); diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 2e743879779a0..e59425baf8498 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -182,11 +182,4 @@ config GENLOCK_MISCDEVICE Create a miscdevice for the purposes of allowing userspace to create and interact with locks created using genlock. -config ARCH_NO_SYSDEV_OPS - bool - ---help--- - To be selected by architectures that don't use sysdev class or - sysdev driver power management (suspend/resume) and shutdown - operations. - endmenu diff --git a/drivers/base/base.h b/drivers/base/base.h index 19f49e41ce5de..a34dca0ad0417 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -111,8 +111,6 @@ static inline int driver_match_device(struct device_driver *drv, return drv->bus->match ? drv->bus->match(dev, drv) : 1; } -extern void sysdev_shutdown(void); - extern char *make_class_name(const char *name, struct kobject *kobj); extern int devres_release_all(struct device *dev); diff --git a/drivers/base/sys.c b/drivers/base/sys.c index acde9b5ee1314..9dff77bfe1e34 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -328,203 +328,8 @@ void sysdev_unregister(struct sys_device *sysdev) kobject_put(&sysdev->kobj); } - -#ifndef CONFIG_ARCH_NO_SYSDEV_OPS -/** - * sysdev_shutdown - Shut down all system devices. - * - * Loop over each class of system devices, and the devices in each - * of those classes. For each device, we call the shutdown method for - * each driver registered for the device - the auxiliaries, - * and the class driver. - * - * Note: The list is iterated in reverse order, so that we shut down - * child devices before we shut down their parents. The list ordering - * is guaranteed by virtue of the fact that child devices are registered - * after their parents. - */ -void sysdev_shutdown(void) -{ - struct sysdev_class *cls; - - pr_debug("Shutting Down System Devices\n"); - - mutex_lock(&sysdev_drivers_lock); - list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) { - struct sys_device *sysdev; - - pr_debug("Shutting down type '%s':\n", - kobject_name(&cls->kset.kobj)); - - list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { - struct sysdev_driver *drv; - pr_debug(" %s\n", kobject_name(&sysdev->kobj)); - - /* Call auxiliary drivers first */ - list_for_each_entry(drv, &cls->drivers, entry) { - if (drv->shutdown) - drv->shutdown(sysdev); - } - - /* Now call the generic one */ - if (cls->shutdown) - cls->shutdown(sysdev); - } - } - mutex_unlock(&sysdev_drivers_lock); -} - -static void __sysdev_resume(struct sys_device *dev) -{ - struct sysdev_class *cls = dev->cls; - struct sysdev_driver *drv; - - /* First, call the class-specific one */ - if (cls->resume) - cls->resume(dev); - WARN_ONCE(!irqs_disabled(), - "Interrupts enabled after %pF\n", cls->resume); - - /* Call auxiliary drivers next. */ - list_for_each_entry(drv, &cls->drivers, entry) { - if (drv->resume) - drv->resume(dev); - WARN_ONCE(!irqs_disabled(), - "Interrupts enabled after %pF\n", drv->resume); - } -} - -/** - * sysdev_suspend - Suspend all system devices. - * @state: Power state to enter. - * - * We perform an almost identical operation as sysdev_shutdown() - * above, though calling ->suspend() instead. Interrupts are disabled - * when this called. Devices are responsible for both saving state and - * quiescing or powering down the device. - * - * This is only called by the device PM core, so we let them handle - * all synchronization. - */ -int sysdev_suspend(pm_message_t state) -{ - struct sysdev_class *cls; - struct sys_device *sysdev, *err_dev; - struct sysdev_driver *drv, *err_drv; - int ret; - - pr_debug("Checking wake-up interrupts\n"); - - /* Return error code if there are any wake-up interrupts pending */ - ret = check_wakeup_irqs(); - if (ret) - return ret; - - WARN_ONCE(!irqs_disabled(), - "Interrupts enabled while suspending system devices\n"); - - pr_debug("Suspending System Devices\n"); - - list_for_each_entry_reverse(cls, &system_kset->list, kset.kobj.entry) { - pr_debug("Suspending type '%s':\n", - kobject_name(&cls->kset.kobj)); - - list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { - pr_debug(" %s\n", kobject_name(&sysdev->kobj)); - - /* Call auxiliary drivers first */ - list_for_each_entry(drv, &cls->drivers, entry) { - if (drv->suspend) { - ret = drv->suspend(sysdev, state); - if (ret) - goto aux_driver; - } - WARN_ONCE(!irqs_disabled(), - "Interrupts enabled after %pF\n", - drv->suspend); - } - - /* Now call the generic one */ - if (cls->suspend) { - ret = cls->suspend(sysdev, state); - if (ret) - goto cls_driver; - WARN_ONCE(!irqs_disabled(), - "Interrupts enabled after %pF\n", - cls->suspend); - } - } - } - return 0; - /* resume current sysdev */ -cls_driver: - drv = NULL; - printk(KERN_ERR "Class suspend failed for %s: %d\n", - kobject_name(&sysdev->kobj), ret); - -aux_driver: - if (drv) - printk(KERN_ERR "Class driver suspend failed for %s: %d\n", - kobject_name(&sysdev->kobj), ret); - list_for_each_entry(err_drv, &cls->drivers, entry) { - if (err_drv == drv) - break; - if (err_drv->resume) - err_drv->resume(sysdev); - } - - /* resume other sysdevs in current class */ - list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) { - if (err_dev == sysdev) - break; - pr_debug(" %s\n", kobject_name(&err_dev->kobj)); - __sysdev_resume(err_dev); - } - - /* resume other classes */ - list_for_each_entry_continue(cls, &system_kset->list, kset.kobj.entry) { - list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) { - pr_debug(" %s\n", kobject_name(&err_dev->kobj)); - __sysdev_resume(err_dev); - } - } - return ret; -} -EXPORT_SYMBOL_GPL(sysdev_suspend); - -/** - * sysdev_resume - Bring system devices back to life. - * - * Similar to sysdev_suspend(), but we iterate the list forwards - * to guarantee that parent devices are resumed before their children. - * - * Note: Interrupts are disabled when called. - */ -int sysdev_resume(void) -{ - struct sysdev_class *cls; - - WARN_ONCE(!irqs_disabled(), - "Interrupts enabled while resuming system devices\n"); - - pr_debug("Resuming System Devices\n"); - - list_for_each_entry(cls, &system_kset->list, kset.kobj.entry) { - struct sys_device *sysdev; - - pr_debug("Resuming type '%s':\n", - kobject_name(&cls->kset.kobj)); - - list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { - pr_debug(" %s\n", kobject_name(&sysdev->kobj)); - - __sysdev_resume(sysdev); - } - } - return 0; -} -EXPORT_SYMBOL_GPL(sysdev_resume); -#endif /* CONFIG_ARCH_NO_SYSDEV_OPS */ +EXPORT_SYMBOL_GPL(sysdev_register); +EXPORT_SYMBOL_GPL(sysdev_unregister); int __init system_bus_init(void) { @@ -534,9 +339,6 @@ int __init system_bus_init(void) return 0; } -EXPORT_SYMBOL_GPL(sysdev_register); -EXPORT_SYMBOL_GPL(sysdev_unregister); - #define to_ext_attr(x) container_of(x, struct sysdev_ext_attribute, attr) ssize_t sysdev_store_ulong(struct sys_device *sysdev, diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index a2eee574784e8..0b5366b5be201 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -70,12 +70,7 @@ static int xen_suspend(void *data) BUG_ON(!irqs_disabled()); - err = sysdev_suspend(PMSG_FREEZE); - if (!err) { - err = syscore_suspend(); - if (err) - sysdev_resume(); - } + err = syscore_suspend(); if (err) { printk(KERN_ERR "xen_suspend: system core suspend failed: %d\n", err); @@ -102,7 +97,6 @@ static int xen_suspend(void *data) } syscore_resume(); - sysdev_resume(); return 0; } diff --git a/include/linux/device.h b/include/linux/device.h index ceb5209f7f360..c69f45d1e50f7 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -634,13 +634,6 @@ static inline int devtmpfs_mount(const char *mountpoint) { return 0; } /* drivers/base/power/shutdown.c */ extern void device_shutdown(void); -#ifndef CONFIG_ARCH_NO_SYSDEV_OPS -/* drivers/base/sys.c */ -extern void sysdev_shutdown(void); -#else -static inline void sysdev_shutdown(void) { } -#endif - /* debugging and troubleshooting/diagnostic helpers. */ extern const char *dev_driver_string(const struct device *dev); diff --git a/include/linux/pm.h b/include/linux/pm.h index 136523856f201..6fedd1c2a5a2a 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -521,14 +521,6 @@ extern void update_pm_runtime_accounting(struct device *dev); */ #ifdef CONFIG_PM_SLEEP -#ifndef CONFIG_ARCH_NO_SYSDEV_OPS -extern int sysdev_suspend(pm_message_t state); -extern int sysdev_resume(void); -#else -static inline int sysdev_suspend(pm_message_t state) { return 0; } -static inline int sysdev_resume(void) { return 0; } -#endif - extern void device_pm_lock(void); extern void dpm_resume_noirq(pm_message_t state); extern void dpm_resume_end(pm_message_t state); diff --git a/include/linux/sysdev.h b/include/linux/sysdev.h index 8a75da551e4e9..6555679b415bc 100644 --- a/include/linux/sysdev.h +++ b/include/linux/sysdev.h @@ -34,12 +34,6 @@ struct sysdev_class { struct list_head drivers; struct sysdev_class_attribute **attrs; struct kset kset; -#ifndef CONFIG_ARCH_NO_SYSDEV_OPS - /* Default operations for these types of devices */ - int (*shutdown)(struct sys_device *); - int (*suspend)(struct sys_device *, pm_message_t state); - int (*resume)(struct sys_device *); -#endif }; struct sysdev_class_attribute { @@ -77,11 +71,6 @@ struct sysdev_driver { struct list_head entry; int (*add)(struct sys_device *); int (*remove)(struct sys_device *); -#ifndef CONFIG_ARCH_NO_SYSDEV_OPS - int (*shutdown)(struct sys_device *); - int (*suspend)(struct sys_device *, pm_message_t state); - int (*resume)(struct sys_device *); -#endif }; diff --git a/kernel/kexec.c b/kernel/kexec.c index 9669d06bb4023..1914fd5daf236 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -1530,13 +1530,7 @@ int kernel_kexec(void) if (error) goto Enable_cpus; local_irq_disable(); - /* Suspend system devices */ - error = sysdev_suspend(PMSG_FREEZE); - if (!error) { - error = syscore_suspend(); - if (error) - sysdev_resume(); - } + error = syscore_suspend(); if (error) goto Enable_irqs; } else @@ -1552,7 +1546,6 @@ int kernel_kexec(void) #ifdef CONFIG_KEXEC_JUMP if (kexec_image->preserve_context) { syscore_resume(); - sysdev_resume(); Enable_irqs: local_irq_enable(); Enable_cpus: diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 50aae660174d6..554d3b049f353 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -272,12 +272,7 @@ static int create_image(int platform_mode) local_irq_disable(); - error = sysdev_suspend(PMSG_FREEZE); - if (!error) { - error = syscore_suspend(); - if (error) - sysdev_resume(); - } + error = syscore_suspend(); if (error) { printk(KERN_ERR "PM: Some system devices failed to power down, " "aborting hibernation\n"); @@ -302,7 +297,6 @@ static int create_image(int platform_mode) Power_up: syscore_resume(); - sysdev_resume(); /* NOTE: dpm_resume_noirq() is just a resume() for devices * that suspended with irqs off ... no overall powerup. */ @@ -409,12 +403,7 @@ static int resume_target_kernel(bool platform_mode) local_irq_disable(); - error = sysdev_suspend(PMSG_QUIESCE); - if (!error) { - error = syscore_suspend(); - if (error) - sysdev_resume(); - } + error = syscore_suspend(); if (error) goto Enable_irqs; @@ -442,7 +431,6 @@ static int resume_target_kernel(bool platform_mode) touch_softlockup_watchdog(); syscore_resume(); - sysdev_resume(); Enable_irqs: local_irq_enable(); @@ -528,7 +516,6 @@ int hibernation_platform_enter(void) goto Platform_finish; local_irq_disable(); - sysdev_suspend(PMSG_HIBERNATE); syscore_suspend(); if (pm_wakeup_pending()) { error = -EAGAIN; @@ -541,7 +528,6 @@ int hibernation_platform_enter(void) Power_up: syscore_resume(); - sysdev_resume(); local_irq_enable(); enable_nonboot_cpus(); diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 98df7719127b9..067fae0f9b997 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -166,19 +166,13 @@ static int suspend_enter(suspend_state_t state) arch_suspend_disable_irqs(); BUG_ON(!irqs_disabled()); - error = sysdev_suspend(PMSG_SUSPEND); - if (!error) { - error = syscore_suspend(); - if (error) - sysdev_resume(); - } + error = syscore_suspend(); if (!error) { if (!(suspend_test(TEST_CORE) || pm_wakeup_pending())) { error = suspend_ops->enter(state); events_check_enabled = false; } syscore_resume(); - sysdev_resume(); } arch_suspend_enable_irqs(); diff --git a/kernel/sys.c b/kernel/sys.c index 1ad48b3b9068f..b837d4a35fdc0 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -298,7 +298,6 @@ void kernel_restart_prepare(char *cmd) blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); system_state = SYSTEM_RESTART; device_shutdown(); - sysdev_shutdown(); syscore_shutdown(); } @@ -337,7 +336,6 @@ static void kernel_shutdown_prepare(enum system_states state) void kernel_halt(void) { kernel_shutdown_prepare(SYSTEM_HALT); - sysdev_shutdown(); syscore_shutdown(); printk(KERN_EMERG "System halted.\n"); kmsg_dump(KMSG_DUMP_HALT); @@ -357,7 +355,6 @@ void kernel_power_off(void) if (pm_power_off_prepare) pm_power_off_prepare(); disable_nonboot_cpus(); - sysdev_shutdown(); syscore_shutdown(); printk(KERN_EMERG "Power down.\n"); kmsg_dump(KMSG_DUMP_POWEROFF); From 99db62610f1ab02f14475f725d49ace62cc34643 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 11 Jul 2011 10:51:49 +0200 Subject: [PATCH 2196/2556] PM: Reintroduce dropped call to check_wakeup_irqs Patch 2e711c04dbbf7a7732a3f7073b1fc285d12b369d (PM: Remove sysdev suspend, resume and shutdown operations) deleted sysdev_suspend(), which was being relied on to call check_wakeup_irqs() in suspend. If check_wakeup_irqs() is not called, wake interrupts that are pending when suspend is entered may be lost. It also breaks IRQCHIP_MASK_ON_SUSPEND, which is handled in check_wakeup_irqs(). This patch adds a call to check_wakeup_irqs() in syscore_suspend(), similar to what was deleted in sysdev_suspend(). Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- drivers/base/syscore.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c index c126db3cb7d12..e8d11b6630eeb 100644 --- a/drivers/base/syscore.c +++ b/drivers/base/syscore.c @@ -9,6 +9,7 @@ #include #include #include +#include static LIST_HEAD(syscore_ops_list); static DEFINE_MUTEX(syscore_ops_lock); @@ -48,6 +49,13 @@ int syscore_suspend(void) struct syscore_ops *ops; int ret = 0; + pr_debug("Checking wakeup interrupts\n"); + + /* Return error code if there are any wakeup interrupts pending. */ + ret = check_wakeup_irqs(); + if (ret) + return ret; + WARN_ONCE(!irqs_disabled(), "Interrupts enabled before system core suspend.\n"); From 74f4668aa0677e3f67d1fdeb24fa83c1182e4469 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Mon, 23 Apr 2012 03:16:39 -0500 Subject: [PATCH 2197/2556] Revert "timer: import get_xtime_and_monotonic_and_sleep_offset from AOSP's, android-2.6.39" This reverts commit f17030972ff248fcd9757de7298a178e54d7d5f2. --- include/linux/time.h | 2 -- kernel/hrtimer.c | 8 ++------ kernel/time/timekeeping.c | 21 --------------------- 3 files changed, 2 insertions(+), 29 deletions(-) diff --git a/include/linux/time.h b/include/linux/time.h index 90e3f0a6b36e8..1e6d3b59238d3 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -127,8 +127,6 @@ struct timespec current_kernel_time(void); struct timespec __current_kernel_time(void); /* does not take xtime_lock */ struct timespec __get_wall_to_monotonic(void); /* does not take xtime_lock */ struct timespec get_monotonic_coarse(void); -void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, - struct timespec *wtom, struct timespec *sleep); #define CURRENT_TIME (current_kernel_time()) #define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 }) diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index c2236623b3ccf..0c8d7c0486154 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -84,7 +84,7 @@ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) = static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base) { ktime_t xtim, tomono; - struct timespec xts, tom, slp; + struct timespec xts, tom; unsigned long seq; do { @@ -93,8 +93,6 @@ static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base) tom = __get_wall_to_monotonic(); } while (read_seqretry(&xtime_lock, seq)); - get_xtime_and_monotonic_and_sleep_offset(&xts, &tom, &slp); - xtim = timespec_to_ktime(xts); tomono = timespec_to_ktime(tom); base->clock_base[CLOCK_REALTIME].softirq_time = xtim; @@ -613,7 +611,7 @@ static int hrtimer_reprogram(struct hrtimer *timer, static void retrigger_next_event(void *arg) { struct hrtimer_cpu_base *base; - struct timespec realtime_offset, wtm, sleep; + struct timespec realtime_offset, wtm; unsigned long seq; if (!hrtimer_hres_active()) @@ -623,8 +621,6 @@ static void retrigger_next_event(void *arg) seq = read_seqbegin(&xtime_lock); wtm = __get_wall_to_monotonic(); } while (read_seqretry(&xtime_lock, seq)); - get_xtime_and_monotonic_and_sleep_offset(&realtime_offset, &wtm, - &sleep); set_normalized_timespec(&realtime_offset, -wtm.tv_sec, -wtm.tv_nsec); base = &__get_cpu_var(hrtimer_bases); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 3991ffa03dc8d..d27c7562902cb 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -946,24 +946,3 @@ struct timespec get_monotonic_coarse(void) now.tv_nsec + mono.tv_nsec); return now; } - -/** - * get_xtime_and_monotonic_and_sleep_offset() - get xtime, wall_to_monotonic, - and sleep offsets. - * @xtim: pointer to timespec to be set with xtime - * @wtom: pointer to timespec to be set with wall_to_monotonic - * @sleep: pointer to timespec to be set with time in suspend - */ - -void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, - struct timespec *wtom, struct timespec *sleep) -{ - unsigned long seq; - - do { - seq = read_seqbegin(&xtime_lock); - *xtim = xtime; - *wtom = wall_to_monotonic; - *sleep = total_sleep_time; - } while (read_seqretry(&xtime_lock, seq)); -} From f52d673288d89aa8e96b0b009b5708d5b9434d7e Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Thu, 27 Jan 2011 15:58:55 +0100 Subject: [PATCH 2198/2556] time: Move do_timer() to kernel/time/timekeeping.c do_timer() is primary timekeeping related. calc_global_load() is called from do_timer() as well, but that's more for historical reasons. [ tglx: Fixed up the calc_global_load() reject andmassaged changelog ] Signed-off-by: Torben Hohn Cc: Peter Zijlstra Cc: johnstul@us.ibm.com Cc: yong.zhang0@gmail.com Cc: hch@infradead.org LKML-Reference: <20110127145855.23248.56933.stgit@localhost> Signed-off-by: Thomas Gleixner --- include/linux/time.h | 1 - kernel/time/timekeeping.c | 14 +++++++++++++- kernel/timer.c | 13 ------------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/include/linux/time.h b/include/linux/time.h index 1e6d3b59238d3..86a9c487fdd8b 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -166,7 +166,6 @@ extern void monotonic_to_bootbased(struct timespec *ts); extern struct timespec timespec_trunc(struct timespec t, unsigned gran); extern int timekeeping_valid_for_hres(void); extern u64 timekeeping_max_deferment(void); -extern void update_wall_time(void); extern void timekeeping_leap_insert(int leapsecond); struct tms; diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index d27c7562902cb..c1a178ca0f50d 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -779,7 +779,7 @@ static cycle_t logarithmic_accumulation(cycle_t offset, int shift) * * Called from the timer interrupt, must hold a write on xtime_lock. */ -void update_wall_time(void) +static void update_wall_time(void) { struct clocksource *clock; cycle_t offset; @@ -946,3 +946,15 @@ struct timespec get_monotonic_coarse(void) now.tv_nsec + mono.tv_nsec); return now; } + +/* + * The 64-bit jiffies value is not atomic - you MUST NOT read it + * without sampling the sequence number in xtime_lock. + * jiffies is defined in the linker script... + */ +void do_timer(unsigned long ticks) +{ + jiffies_64 += ticks; + update_wall_time(); + calc_global_load(ticks); +} diff --git a/kernel/timer.c b/kernel/timer.c index d6459923d2452..c848cd8abe2ae 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -1295,19 +1295,6 @@ void run_local_timers(void) raise_softirq(TIMER_SOFTIRQ); } -/* - * The 64-bit jiffies value is not atomic - you MUST NOT read it - * without sampling the sequence number in xtime_lock. - * jiffies is defined in the linker script... - */ - -void do_timer(unsigned long ticks) -{ - jiffies_64 += ticks; - update_wall_time(); - calc_global_load(ticks); -} - #ifdef __ARCH_WANT_SYS_ALARM /* From 689c4c074eb49e67034897b5fc9d33ecbf31c60f Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Thu, 27 Jan 2011 15:59:05 +0100 Subject: [PATCH 2199/2556] time: Provide get_xtime_and_monotonic_offset() The hrtimer code accesses timekeeping variables under xtime_lock. Provide a sensible accessor function and use it. [ tglx: Removed the conditionals, unused variable, fixed codingstyle and massaged changelog ] Signed-off-by: Torben Hohn Cc: Peter Zijlstra Cc: johnstul@us.ibm.com Cc: yong.zhang0@gmail.com Cc: hch@infradead.org LKML-Reference: <20110127145905.23248.30458.stgit@localhost> Signed-off-by: Thomas Gleixner --- include/linux/time.h | 1 + kernel/hrtimer.c | 13 ++----------- kernel/time/timekeeping.c | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/include/linux/time.h b/include/linux/time.h index 86a9c487fdd8b..4007a12a1b505 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -127,6 +127,7 @@ struct timespec current_kernel_time(void); struct timespec __current_kernel_time(void); /* does not take xtime_lock */ struct timespec __get_wall_to_monotonic(void); /* does not take xtime_lock */ struct timespec get_monotonic_coarse(void); +void get_xtime_and_monotonic_offset(struct timespec *xtim, struct timespec *wtom); #define CURRENT_TIME (current_kernel_time()) #define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 }) diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 0c8d7c0486154..57c4d33c9a9d6 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -85,13 +85,8 @@ static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base) { ktime_t xtim, tomono; struct timespec xts, tom; - unsigned long seq; - do { - seq = read_seqbegin(&xtime_lock); - xts = __current_kernel_time(); - tom = __get_wall_to_monotonic(); - } while (read_seqretry(&xtime_lock, seq)); + get_xtime_and_monotonic_offset(&xts, &tom); xtim = timespec_to_ktime(xts); tomono = timespec_to_ktime(tom); @@ -612,15 +607,11 @@ static void retrigger_next_event(void *arg) { struct hrtimer_cpu_base *base; struct timespec realtime_offset, wtm; - unsigned long seq; if (!hrtimer_hres_active()) return; - do { - seq = read_seqbegin(&xtime_lock); - wtm = __get_wall_to_monotonic(); - } while (read_seqretry(&xtime_lock, seq)); + get_xtime_and_monotonic_offset(&realtime_offset, &wtm); set_normalized_timespec(&realtime_offset, -wtm.tv_sec, -wtm.tv_nsec); base = &__get_cpu_var(hrtimer_bases); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index c1a178ca0f50d..c50aaf6cd01d7 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -958,3 +958,19 @@ void do_timer(unsigned long ticks) update_wall_time(); calc_global_load(ticks); } + +/** + * get_xtime_and_monotonic_offset() - get xtime and wall_to_monotonic + * @xtim: pointer to timespec to be set with xtime + * @wtom: pointer to timespec to be set with wall_to_monotonic + */ +void get_xtime_and_monotonic_offset(struct timespec *xtim, struct timespec *wtom) +{ + unsigned long seq; + + do { + seq = read_seqbegin(&xtime_lock); + *xtim = xtime; + *wtom = wall_to_monotonic; + } while (read_seqretry(&xtime_lock, seq)); +} From 91530ee6c0d53e8aa34f67cf20f19ec6057838d1 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 31 Jan 2011 11:07:54 +0100 Subject: [PATCH 2200/2556] time: Remove unused __get_wall_to_monotonic() No users left. Remove it. Signed-off-by: Thomas Gleixner --- include/linux/time.h | 1 - kernel/time/timekeeping.c | 5 ----- 2 files changed, 6 deletions(-) diff --git a/include/linux/time.h b/include/linux/time.h index 4007a12a1b505..ce29c86882b13 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -125,7 +125,6 @@ extern int timekeeping_suspended; unsigned long get_seconds(void); struct timespec current_kernel_time(void); struct timespec __current_kernel_time(void); /* does not take xtime_lock */ -struct timespec __get_wall_to_monotonic(void); /* does not take xtime_lock */ struct timespec get_monotonic_coarse(void); void get_xtime_and_monotonic_offset(struct timespec *xtim, struct timespec *wtom); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index c50aaf6cd01d7..8da35d1b9e169 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -910,11 +910,6 @@ struct timespec __current_kernel_time(void) return xtime; } -struct timespec __get_wall_to_monotonic(void) -{ - return wall_to_monotonic; -} - struct timespec current_kernel_time(void) { struct timespec now; From c32a323b32f133e8f4e3db92b60d2cab64f9fd0f Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Thu, 27 Jan 2011 15:59:10 +0100 Subject: [PATCH 2201/2556] time: Provide xtime_update() xtime_update() takes xtime_lock write locked and calls do_timer(). Provided to replace the do_timer() calls in the architecture code. Signed-off-by: Torben Hohn Cc: Peter Zijlstra Cc: johnstul@us.ibm.com Cc: yong.zhang0@gmail.com Cc: hch@infradead.org LKML-Reference: <20110127145910.23248.21379.stgit@localhost> Signed-off-by: Thomas Gleixner --- include/linux/sched.h | 1 + kernel/time/timekeeping.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/include/linux/sched.h b/include/linux/sched.h index cc42d1ce6a505..d9b55bb28d8ef 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2058,6 +2058,7 @@ extern void release_uids(struct user_namespace *ns); #include extern void do_timer(unsigned long ticks); +extern void xtime_update(unsigned long ticks); extern int wake_up_state(struct task_struct *tsk, unsigned int state); extern int wake_up_process(struct task_struct *tsk); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 8da35d1b9e169..02c13a313d15c 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -969,3 +969,16 @@ void get_xtime_and_monotonic_offset(struct timespec *xtim, struct timespec *wtom *wtom = wall_to_monotonic; } while (read_seqretry(&xtime_lock, seq)); } + +/** + * xtime_update() - advances the timekeeping infrastructure + * @ticks: number of ticks, that have elapsed since the last call. + * + * Must be called with interrupts disabled. + */ +void xtime_update(unsigned long ticks) +{ + write_seqlock(&xtime_lock); + do_timer(ticks); + write_sequnlock(&xtime_lock); +} From 16acae6811f5e26a6d18ec32f8dffa56f97f2ab2 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Tue, 1 Feb 2011 13:50:58 +0000 Subject: [PATCH 2202/2556] time: Correct the *settime* parameters Both settimeofday() and clock_settime() promise with a 'const' attribute not to alter the arguments passed in. This patch adds the missing 'const' attribute into the various kernel functions implementing these calls. Signed-off-by: Richard Cochran Acked-by: John Stultz LKML-Reference: <20110201134417.545698637@linutronix.de> Signed-off-by: Thomas Gleixner --- drivers/char/mmtimer.c | 2 +- include/linux/posix-timers.h | 5 +++-- include/linux/security.h | 9 +++++---- include/linux/time.h | 5 +++-- kernel/posix-timers.c | 4 ++-- kernel/time.c | 2 +- kernel/time/timekeeping.c | 2 +- security/commoncap.c | 2 +- security/security.c | 2 +- 9 files changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c index e6d75627c6c81..ecd0082502efe 100644 --- a/drivers/char/mmtimer.c +++ b/drivers/char/mmtimer.c @@ -487,7 +487,7 @@ static int sgi_clock_get(clockid_t clockid, struct timespec *tp) return 0; }; -static int sgi_clock_set(clockid_t clockid, struct timespec *tp) +static int sgi_clock_set(const clockid_t clockid, const struct timespec *tp) { u64 nsec; diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index 3e23844a6990c..b2c14cbd47a6f 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -69,7 +69,8 @@ struct k_itimer { struct k_clock { int res; /* in nanoseconds */ int (*clock_getres) (const clockid_t which_clock, struct timespec *tp); - int (*clock_set) (const clockid_t which_clock, struct timespec * tp); + int (*clock_set) (const clockid_t which_clock, + const struct timespec *tp); int (*clock_get) (const clockid_t which_clock, struct timespec * tp); int (*timer_create) (struct k_itimer *timer); int (*nsleep) (const clockid_t which_clock, int flags, @@ -89,7 +90,7 @@ void register_posix_clock(const clockid_t clock_id, struct k_clock *new_clock); /* error handlers for timer_create, nanosleep and settime */ int do_posix_clock_nonanosleep(const clockid_t, int flags, struct timespec *, struct timespec __user *); -int do_posix_clock_nosettime(const clockid_t, struct timespec *tp); +int do_posix_clock_nosettime(const clockid_t, const struct timespec *tp); /* function to call to trigger timer event */ int posix_timer_event(struct k_itimer *timr, int si_private); diff --git a/include/linux/security.h b/include/linux/security.h index b2b7f9749f5eb..debbd97db7abc 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -53,7 +53,7 @@ struct audit_krule; */ extern int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap, int audit); -extern int cap_settime(struct timespec *ts, struct timezone *tz); +extern int cap_settime(const struct timespec *ts, const struct timezone *tz); extern int cap_ptrace_access_check(struct task_struct *child, unsigned int mode); extern int cap_ptrace_traceme(struct task_struct *parent); extern int cap_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); @@ -1387,7 +1387,7 @@ struct security_operations { int (*quotactl) (int cmds, int type, int id, struct super_block *sb); int (*quota_on) (struct dentry *dentry); int (*syslog) (int type); - int (*settime) (struct timespec *ts, struct timezone *tz); + int (*settime) (const struct timespec *ts, const struct timezone *tz); int (*vm_enough_memory) (struct mm_struct *mm, long pages); int (*bprm_set_creds) (struct linux_binprm *bprm); @@ -1669,7 +1669,7 @@ int security_sysctl(struct ctl_table *table, int op); int security_quotactl(int cmds, int type, int id, struct super_block *sb); int security_quota_on(struct dentry *dentry); int security_syslog(int type); -int security_settime(struct timespec *ts, struct timezone *tz); +int security_settime(const struct timespec *ts, const struct timezone *tz); int security_vm_enough_memory(long pages); int security_vm_enough_memory_mm(struct mm_struct *mm, long pages); int security_vm_enough_memory_kern(long pages); @@ -1904,7 +1904,8 @@ static inline int security_syslog(int type) return 0; } -static inline int security_settime(struct timespec *ts, struct timezone *tz) +static inline int security_settime(const struct timespec *ts, + const struct timezone *tz) { return cap_settime(ts, tz); } diff --git a/include/linux/time.h b/include/linux/time.h index ce29c86882b13..a3333872f5dc9 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -147,8 +147,9 @@ static inline u32 arch_gettimeoffset(void) { return 0; } #endif extern void do_gettimeofday(struct timeval *tv); -extern int do_settimeofday(struct timespec *tv); -extern int do_sys_settimeofday(struct timespec *tv, struct timezone *tz); +extern int do_settimeofday(const struct timespec *tv); +extern int do_sys_settimeofday(const struct timespec *tv, + const struct timezone *tz); #define do_posix_clock_monotonic_gettime(ts) ktime_get_ts(ts) extern long do_utimes(int dfd, const char __user *filename, struct timespec *times, int flags); struct itimerval; diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c index 93bd2eb2bc53e..21b7ca205f382 100644 --- a/kernel/posix-timers.c +++ b/kernel/posix-timers.c @@ -192,7 +192,7 @@ static int common_clock_get(clockid_t which_clock, struct timespec *tp) } static inline int common_clock_set(const clockid_t which_clock, - struct timespec *tp) + const struct timespec *tp) { return do_sys_settimeofday(tp, NULL); } @@ -928,7 +928,7 @@ void exit_itimers(struct signal_struct *sig) } /* Not available / possible... functions */ -int do_posix_clock_nosettime(const clockid_t clockid, struct timespec *tp) +int do_posix_clock_nosettime(const clockid_t clockid, const struct timespec *tp) { return -EINVAL; } diff --git a/kernel/time.c b/kernel/time.c index 32174359576fa..70ca1ee1767ce 100644 --- a/kernel/time.c +++ b/kernel/time.c @@ -150,7 +150,7 @@ static inline void warp_clock(void) * various programs will get confused when the clock gets warped. */ -int do_sys_settimeofday(struct timespec *tv, struct timezone *tz) +int do_sys_settimeofday(const struct timespec *tv, const struct timezone *tz) { static int firsttime = 1; int error = 0; diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 02c13a313d15c..4f9f65b913238 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -353,7 +353,7 @@ EXPORT_SYMBOL(do_gettimeofday); * * Sets the time of day to the new time and update NTP and notify hrtimers */ -int do_settimeofday(struct timespec *tv) +int do_settimeofday(const struct timespec *tv) { struct timespec ts_delta; unsigned long flags; diff --git a/security/commoncap.c b/security/commoncap.c index d30c4684654ee..356a415dd2b8d 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -103,7 +103,7 @@ int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap, * Determine whether the current process may set the system clock and timezone * information, returning 0 if permission granted, -ve if denied. */ -int cap_settime(struct timespec *ts, struct timezone *tz) +int cap_settime(const struct timespec *ts, const struct timezone *tz) { if (!capable(CAP_SYS_TIME)) return -EPERM; diff --git a/security/security.c b/security/security.c index 7b7308ace8c5b..bb33ecadcf958 100644 --- a/security/security.c +++ b/security/security.c @@ -201,7 +201,7 @@ int security_syslog(int type) return security_ops->syslog(type); } -int security_settime(struct timespec *ts, struct timezone *tz) +int security_settime(const struct timespec *ts, const struct timezone *tz) { return security_ops->settime(ts, tz); } From d83cdafa89cf10dace120a5da67c22c2a2322678 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Tue, 1 Feb 2011 13:52:17 +0000 Subject: [PATCH 2203/2556] time: Introduce timekeeping_inject_offset This adds a kernel-internal timekeeping interface to add or subtract a fixed amount from CLOCK_REALTIME. This makes it so kernel users or interfaces trying to do so do not have to read the time, then add an offset and then call settimeofday(), which adds some extra error in comparision to just simply adding the offset in the kernel timekeeping core. Signed-off-by: John Stultz Signed-off-by: Richard Cochran LKML-Reference: <20110201134419.584311693@linutronix.de> Signed-off-by: Thomas Gleixner --- include/linux/time.h | 1 + kernel/time/timekeeping.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/include/linux/time.h b/include/linux/time.h index a3333872f5dc9..55d73a9bc3e50 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -168,6 +168,7 @@ extern struct timespec timespec_trunc(struct timespec t, unsigned gran); extern int timekeeping_valid_for_hres(void); extern u64 timekeeping_max_deferment(void); extern void timekeeping_leap_insert(int leapsecond); +extern int timekeeping_inject_offset(struct timespec *ts); struct tms; extern void do_sys_times(struct tms *); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 4f9f65b913238..6262c1d183972 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -387,6 +387,42 @@ int do_settimeofday(const struct timespec *tv) EXPORT_SYMBOL(do_settimeofday); + +/** + * timekeeping_inject_offset - Adds or subtracts from the current time. + * @tv: pointer to the timespec variable containing the offset + * + * Adds or subtracts an offset value from the current time. + */ +int timekeeping_inject_offset(struct timespec *ts) +{ + unsigned long flags; + + if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC) + return -EINVAL; + + write_seqlock_irqsave(&xtime_lock, flags); + + timekeeping_forward_now(); + + xtime = timespec_add(xtime, *ts); + wall_to_monotonic = timespec_sub(wall_to_monotonic, *ts); + + timekeeper.ntp_error = 0; + ntp_clear(); + + update_vsyscall(&xtime, &wall_to_monotonic, timekeeper.clock, + timekeeper.mult); + + write_sequnlock_irqrestore(&xtime_lock, flags); + + /* signal hrtimers about time change */ + clock_was_set(); + + return 0; +} +EXPORT_SYMBOL(timekeeping_inject_offset); + /** * change_clocksource - Swaps clocksources if a new one is available * From 234292e5b94d58d33d9f46dbfb88cbdc7ace47d0 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 14 Feb 2011 17:52:09 -0800 Subject: [PATCH 2204/2556] time: Introduce get_monotonic_boottime and ktime_get_boottime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds new functions that return the monotonic time since boot (in other words, CLOCK_MONOTONIC + suspend time). CC: Jamie Lokier CC: Thomas Gleixner CC: Alexander Shishkin CC: Arve Hjønnevåg Signed-off-by: John Stultz --- include/linux/hrtimer.h | 1 + include/linux/time.h | 1 + kernel/time/timekeeping.c | 51 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index f376ddc64c4dd..b3785ab060e73 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -308,6 +308,7 @@ static inline int hrtimer_is_hres_active(struct hrtimer *timer) extern ktime_t ktime_get(void); extern ktime_t ktime_get_real(void); +extern ktime_t ktime_get_boottime(void); DECLARE_PER_CPU(struct tick_device, tick_cpu_device); diff --git a/include/linux/time.h b/include/linux/time.h index 55d73a9bc3e50..631723de12e70 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -163,6 +163,7 @@ extern void getnstime_raw_and_real(struct timespec *ts_raw, struct timespec *ts_real); extern void getboottime(struct timespec *ts); extern void monotonic_to_bootbased(struct timespec *ts); +extern void get_monotonic_boottime(struct timespec *ts); extern struct timespec timespec_trunc(struct timespec t, unsigned gran); extern int timekeeping_valid_for_hres(void); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 6262c1d183972..5fbd9aa7df95b 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -907,7 +907,7 @@ static void update_wall_time(void) * getboottime - Return the real time of system boot. * @ts: pointer to the timespec to be set * - * Returns the time of day in a timespec. + * Returns the wall-time of boot in a timespec. * * This is based on the wall_to_monotonic offset and the total suspend * time. Calls to settimeofday will affect the value returned (which @@ -925,6 +925,55 @@ void getboottime(struct timespec *ts) } EXPORT_SYMBOL_GPL(getboottime); + +/** + * get_monotonic_boottime - Returns monotonic time since boot + * @ts: pointer to the timespec to be set + * + * Returns the monotonic time since boot in a timespec. + * + * This is similar to CLOCK_MONTONIC/ktime_get_ts, but also + * includes the time spent in suspend. + */ +void get_monotonic_boottime(struct timespec *ts) +{ + struct timespec tomono, sleep; + unsigned int seq; + s64 nsecs; + + WARN_ON(timekeeping_suspended); + + do { + seq = read_seqbegin(&xtime_lock); + *ts = xtime; + tomono = wall_to_monotonic; + sleep = total_sleep_time; + nsecs = timekeeping_get_ns(); + + } while (read_seqretry(&xtime_lock, seq)); + + set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec + sleep.tv_sec, + ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec + nsecs); +} +EXPORT_SYMBOL_GPL(get_monotonic_boottime); + +/** + * ktime_get_boottime - Returns monotonic time since boot in a ktime + * + * Returns the monotonic time since boot in a ktime + * + * This is similar to CLOCK_MONTONIC/ktime_get, but also + * includes the time spent in suspend. + */ +ktime_t ktime_get_boottime(void) +{ + struct timespec ts; + + get_monotonic_boottime(&ts); + return timespec_to_ktime(ts); +} +EXPORT_SYMBOL_GPL(ktime_get_boottime); + /** * monotonic_to_bootbased - Convert the monotonic time to boot based. * @ts: pointer to the timespec to be converted From 49d2c9e6620eed868bf59a7644578208cb8f8298 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 14 Feb 2011 18:43:08 -0800 Subject: [PATCH 2205/2556] time: Extend get_xtime_and_monotonic_offset() to also return sleep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend get_xtime_and_monotonic_offset to get_xtime_and_monotonic_and_sleep_offset(). CC: Jamie Lokier CC: Thomas Gleixner CC: Alexander Shishkin CC: Arve Hjønnevåg Signed-off-by: John Stultz --- include/linux/time.h | 3 ++- kernel/hrtimer.c | 9 +++++---- kernel/time/timekeeping.c | 8 ++++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/include/linux/time.h b/include/linux/time.h index 631723de12e70..1d2b1d4590bd4 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -126,7 +126,8 @@ unsigned long get_seconds(void); struct timespec current_kernel_time(void); struct timespec __current_kernel_time(void); /* does not take xtime_lock */ struct timespec get_monotonic_coarse(void); -void get_xtime_and_monotonic_offset(struct timespec *xtim, struct timespec *wtom); +void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, + struct timespec *wtom, struct timespec *sleep); #define CURRENT_TIME (current_kernel_time()) #define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 }) diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 57c4d33c9a9d6..5afa04ce856b9 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -84,9 +84,9 @@ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) = static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base) { ktime_t xtim, tomono; - struct timespec xts, tom; + struct timespec xts, tom, slp; - get_xtime_and_monotonic_offset(&xts, &tom); + get_xtime_and_monotonic_and_sleep_offset(&xts, &tom, &slp); xtim = timespec_to_ktime(xts); tomono = timespec_to_ktime(tom); @@ -606,12 +606,13 @@ static int hrtimer_reprogram(struct hrtimer *timer, static void retrigger_next_event(void *arg) { struct hrtimer_cpu_base *base; - struct timespec realtime_offset, wtm; + struct timespec realtime_offset, wtm, sleep; if (!hrtimer_hres_active()) return; - get_xtime_and_monotonic_offset(&realtime_offset, &wtm); + get_xtime_and_monotonic_and_sleep_offset(&realtime_offset, &wtm, + &sleep); set_normalized_timespec(&realtime_offset, -wtm.tv_sec, -wtm.tv_nsec); base = &__get_cpu_var(hrtimer_bases); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 5fbd9aa7df95b..3bd7e3d5c6325 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1040,11 +1040,14 @@ void do_timer(unsigned long ticks) } /** - * get_xtime_and_monotonic_offset() - get xtime and wall_to_monotonic + * get_xtime_and_monotonic_and_sleep_offset() - get xtime, wall_to_monotonic, + * and sleep offsets. * @xtim: pointer to timespec to be set with xtime * @wtom: pointer to timespec to be set with wall_to_monotonic + * @sleep: pointer to timespec to be set with time in suspend */ -void get_xtime_and_monotonic_offset(struct timespec *xtim, struct timespec *wtom) +void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, + struct timespec *wtom, struct timespec *sleep) { unsigned long seq; @@ -1052,6 +1055,7 @@ void get_xtime_and_monotonic_offset(struct timespec *xtim, struct timespec *wtom seq = read_seqbegin(&xtime_lock); *xtim = xtime; *wtom = wall_to_monotonic; + *sleep = total_sleep_time; } while (read_seqretry(&xtime_lock, seq)); } From bc5bf8512c63ac3a5b9cb1a6d90628adfc944ae6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 23 Mar 2011 22:16:04 +0100 Subject: [PATCH 2206/2556] timekeeping: Use syscore_ops instead of sysdev class and sysdev The timekeeping subsystem uses a sysdev class and a sysdev for executing timekeeping_suspend() after interrupts have been turned off on the boot CPU (during system suspend) and for executing timekeeping_resume() before turning on interrupts on the boot CPU (during system resume). However, since both of these functions ignore their arguments, the entire mechanism may be replaced with a struct syscore_ops object which is simpler. Signed-off-by: Rafael J. Wysocki Reviewed-by: Thomas Gleixner --- kernel/time/timekeeping.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 3bd7e3d5c6325..8ad5d576755ea 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -597,13 +597,12 @@ static struct timespec timekeeping_suspend_time; /** * timekeeping_resume - Resumes the generic timekeeping subsystem. - * @dev: unused * * This is for the generic clocksource timekeeping. * xtime/wall_to_monotonic/jiffies/etc are * still managed by arch specific suspend/resume code. */ -static int timekeeping_resume(struct sys_device *dev) +static void timekeeping_resume(void) { unsigned long flags; struct timespec ts; @@ -632,11 +631,9 @@ static int timekeeping_resume(struct sys_device *dev) /* Resume hrtimers */ hres_timers_resume(); - - return 0; } -static int timekeeping_suspend(struct sys_device *dev, pm_message_t state) +static int timekeeping_suspend(void) { unsigned long flags; @@ -654,26 +651,18 @@ static int timekeeping_suspend(struct sys_device *dev, pm_message_t state) } /* sysfs resume/suspend bits for timekeeping */ -static struct sysdev_class timekeeping_sysclass = { - .name = "timekeeping", +static struct syscore_ops timekeeping_syscore_ops = { .resume = timekeeping_resume, .suspend = timekeeping_suspend, }; -static struct sys_device device_timer = { - .id = 0, - .cls = &timekeeping_sysclass, -}; - -static int __init timekeeping_init_device(void) +static int __init timekeeping_init_ops(void) { - int error = sysdev_class_register(&timekeeping_sysclass); - if (!error) - error = sysdev_register(&device_timer); - return error; + register_syscore_ops(&timekeeping_syscore_ops); + return 0; } -device_initcall(timekeeping_init_device); +device_initcall(timekeeping_init_ops); /* * If the error is already larger, we look ahead even further From a32bc05818ae155d61948220c9249caa97366291 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Mon, 23 Apr 2012 03:22:20 -0500 Subject: [PATCH 2207/2556] Revert "rtc: Try to prevent RTC errors from accumulating." This reverts commit 6944d705a04be6febc49cf22e2b5cb5ef2246434. --- drivers/rtc/class.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index ec7d9de4d12ee..c404b61386bf0 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -42,32 +42,25 @@ static void rtc_device_release(struct device *dev) */ static struct timespec delta; -static struct timespec delta_delta; static time_t oldtime; static int rtc_suspend(struct device *dev, pm_message_t mesg) { struct rtc_device *rtc = to_rtc_device(dev); struct rtc_time tm; - struct timespec ts; - struct timespec new_delta; + struct timespec ts = current_kernel_time(); if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0) return 0; - getnstimeofday(&ts); rtc_read_time(rtc, &tm); rtc_tm_to_time(&tm, &oldtime); /* RTC precision is 1 second; adjust delta for avg 1/2 sec err */ - set_normalized_timespec(&new_delta, + set_normalized_timespec(&delta, ts.tv_sec - oldtime, ts.tv_nsec - (NSEC_PER_SEC >> 1)); - /* prevent 1/2 sec errors from accumulating */ - delta_delta = timespec_sub(new_delta, delta); - if (delta_delta.tv_sec < -2 || delta_delta.tv_sec >= 2) - delta = new_delta; return 0; } @@ -87,8 +80,6 @@ static int rtc_resume(struct device *dev) return 0; } rtc_tm_to_time(&tm, &newtime); - if (delta_delta.tv_sec < -1) - newtime++; if (newtime <= oldtime) { if (newtime < oldtime) pr_debug("%s: time travel!\n", dev_name(&rtc->dev)); From a6ec0af432b05a711102de18f8182bae9d280ec6 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 21 Feb 2011 22:58:51 -0800 Subject: [PATCH 2208/2556] RTC: Initialize kernel state from RTC Mark Brown pointed out a corner case: that RTC alarms should be allowed to be persistent across reboots if the hardware supported it. The rework of the generic layer to virtualize the RTC alarm virtualized much of the alarm handling, and removed the code used to read the alarm time from the hardware. Mark noted if we want the alarm to be persistent across reboots, we need to re-read the alarm value into the virtualized generic layer at boot up, so that the generic layer properly exposes that value. This patch restores much of the earlier removed rtc_read_alarm code and wires it in so that we set the kernel's alarm value to what we find in the hardware at boot time. NOTE: Not all hardware supports persistent RTC alarm state across system reset. rtc-cmos for example will keep the alarm time, but disables the AIE mode irq. Applications should not expect the RTC alarm to be valid after a system reset. We will preserve what we can, to represent the hardware state at boot, but its not guarenteed. Further, in the future, with multiplexed RTC alarms, the soonest alarm to fire may not be the one set via the /dev/rt ioctls. So an application may set the alarm with RTC_ALM_SET, but after a reset find that RTC_ALM_READ returns an earlier time. Again, we preserve what we can, but applications should not expect the RTC alarm state to persist across a system reset. Big thanks to Mark for pointing out the issue! Thanks also to Marcelo for helping think through the solution. CC: Mark Brown CC: Marcelo Roberto Jimenez CC: Thomas Gleixner CC: Alessandro Zummo CC: rtc-linux@googlegroups.com Reported-by: Mark Brown Signed-off-by: John Stultz --- drivers/rtc/class.c | 7 ++ drivers/rtc/interface.c | 180 ++++++++++++++++++++++++++++++++++++++++ include/linux/rtc.h | 1 + 3 files changed, 188 insertions(+) diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index c404b61386bf0..09b4437b3e616 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -117,6 +117,7 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev, struct module *owner) { struct rtc_device *rtc; + struct rtc_wkalrm alrm; int id, err; if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) { @@ -166,6 +167,12 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev, rtc->pie_timer.function = rtc_pie_update_irq; rtc->pie_enabled = 0; + /* Check to see if there is an ALARM already set in hw */ + err = __rtc_read_alarm(rtc, &alrm); + + if (!err && !rtc_valid_tm(&alrm.time)) + rtc_set_alarm(rtc, &alrm); + strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE); dev_set_name(&rtc->dev, "rtc%d", id); diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index cb2f0728fd70d..8ec6b069a7f58 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -116,6 +116,186 @@ int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs) } EXPORT_SYMBOL_GPL(rtc_set_mmss); +static int rtc_read_alarm_internal(struct rtc_device *rtc, struct rtc_wkalrm *alarm) +{ + int err; + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + + if (rtc->ops == NULL) + err = -ENODEV; + else if (!rtc->ops->read_alarm) + err = -EINVAL; + else { + memset(alarm, 0, sizeof(struct rtc_wkalrm)); + err = rtc->ops->read_alarm(rtc->dev.parent, alarm); + } + + mutex_unlock(&rtc->ops_lock); + return err; +} + +int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) +{ + int err; + struct rtc_time before, now; + int first_time = 1; + unsigned long t_now, t_alm; + enum { none, day, month, year } missing = none; + unsigned days; + + /* The lower level RTC driver may return -1 in some fields, + * creating invalid alarm->time values, for reasons like: + * + * - The hardware may not be capable of filling them in; + * many alarms match only on time-of-day fields, not + * day/month/year calendar data. + * + * - Some hardware uses illegal values as "wildcard" match + * values, which non-Linux firmware (like a BIOS) may try + * to set up as e.g. "alarm 15 minutes after each hour". + * Linux uses only oneshot alarms. + * + * When we see that here, we deal with it by using values from + * a current RTC timestamp for any missing (-1) values. The + * RTC driver prevents "periodic alarm" modes. + * + * But this can be racey, because some fields of the RTC timestamp + * may have wrapped in the interval since we read the RTC alarm, + * which would lead to us inserting inconsistent values in place + * of the -1 fields. + * + * Reading the alarm and timestamp in the reverse sequence + * would have the same race condition, and not solve the issue. + * + * So, we must first read the RTC timestamp, + * then read the RTC alarm value, + * and then read a second RTC timestamp. + * + * If any fields of the second timestamp have changed + * when compared with the first timestamp, then we know + * our timestamp may be inconsistent with that used by + * the low-level rtc_read_alarm_internal() function. + * + * So, when the two timestamps disagree, we just loop and do + * the process again to get a fully consistent set of values. + * + * This could all instead be done in the lower level driver, + * but since more than one lower level RTC implementation needs it, + * then it's probably best best to do it here instead of there.. + */ + + /* Get the "before" timestamp */ + err = rtc_read_time(rtc, &before); + if (err < 0) + return err; + do { + if (!first_time) + memcpy(&before, &now, sizeof(struct rtc_time)); + first_time = 0; + + /* get the RTC alarm values, which may be incomplete */ + err = rtc_read_alarm_internal(rtc, alarm); + if (err) + return err; + + /* full-function RTCs won't have such missing fields */ + if (rtc_valid_tm(&alarm->time) == 0) + return 0; + + /* get the "after" timestamp, to detect wrapped fields */ + err = rtc_read_time(rtc, &now); + if (err < 0) + return err; + + /* note that tm_sec is a "don't care" value here: */ + } while ( before.tm_min != now.tm_min + || before.tm_hour != now.tm_hour + || before.tm_mon != now.tm_mon + || before.tm_year != now.tm_year); + + /* Fill in the missing alarm fields using the timestamp; we + * know there's at least one since alarm->time is invalid. + */ + if (alarm->time.tm_sec == -1) + alarm->time.tm_sec = now.tm_sec; + if (alarm->time.tm_min == -1) + alarm->time.tm_min = now.tm_min; + if (alarm->time.tm_hour == -1) + alarm->time.tm_hour = now.tm_hour; + + /* For simplicity, only support date rollover for now */ + if (alarm->time.tm_mday == -1) { + alarm->time.tm_mday = now.tm_mday; + missing = day; + } + if (alarm->time.tm_mon == -1) { + alarm->time.tm_mon = now.tm_mon; + if (missing == none) + missing = month; + } + if (alarm->time.tm_year == -1) { + alarm->time.tm_year = now.tm_year; + if (missing == none) + missing = year; + } + + /* with luck, no rollover is needed */ + rtc_tm_to_time(&now, &t_now); + rtc_tm_to_time(&alarm->time, &t_alm); + if (t_now < t_alm) + goto done; + + switch (missing) { + + /* 24 hour rollover ... if it's now 10am Monday, an alarm that + * that will trigger at 5am will do so at 5am Tuesday, which + * could also be in the next month or year. This is a common + * case, especially for PCs. + */ + case day: + dev_dbg(&rtc->dev, "alarm rollover: %s\n", "day"); + t_alm += 24 * 60 * 60; + rtc_time_to_tm(t_alm, &alarm->time); + break; + + /* Month rollover ... if it's the 31th, an alarm on the 3rd will + * be next month. An alarm matching on the 30th, 29th, or 28th + * may end up in the month after that! Many newer PCs support + * this type of alarm. + */ + case month: + dev_dbg(&rtc->dev, "alarm rollover: %s\n", "month"); + do { + if (alarm->time.tm_mon < 11) + alarm->time.tm_mon++; + else { + alarm->time.tm_mon = 0; + alarm->time.tm_year++; + } + days = rtc_month_days(alarm->time.tm_mon, + alarm->time.tm_year); + } while (days < alarm->time.tm_mday); + break; + + /* Year rollover ... easy except for leap years! */ + case year: + dev_dbg(&rtc->dev, "alarm rollover: %s\n", "year"); + do { + alarm->time.tm_year++; + } while (rtc_valid_tm(&alarm->time) != 0); + break; + + default: + dev_warn(&rtc->dev, "alarm rollover not handled\n"); + } + +done: + return 0; +} + int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) { int err; diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 89c3e51829911..db3832d5f280b 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -227,6 +227,7 @@ extern void rtc_device_unregister(struct rtc_device *rtc); extern int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm); extern int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm); extern int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs); +int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm); extern int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alrm); extern int rtc_set_alarm(struct rtc_device *rtc, From 949fbf082d26f472ce2791b492ec954f62d80c31 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Tue, 29 Mar 2011 18:00:27 -0700 Subject: [PATCH 2209/2556] RTC: Fix early irqs caused by calling rtc_set_alarm too early When we register an rtc device at boot, we read the alarm value in hardware and set the rtc device's aie_timer to that value. The initial method to do this was to simply call rtc_set_alarm() with the value read from hardware. However, this may cause problems as rtc_set_alarm may enable interupts, and the RTC alarm might fire, which can cause invalid pointer dereferencing since the RTC registration is not complete. This patch solves the issue by initializing the rtc_device.aie_timer y hand via rtc_initialize_alarm(). This avoids any calls to the RTC hardware which might enable interrupts too early. CC: Thomas Gleixner CC: Alessandro Zummo Reported-by: Konrad Rzeszutek Wilk Tested-by: Konrad Rzeszutek Wilk Signed-off-by: John Stultz --- drivers/rtc/class.c | 2 +- drivers/rtc/interface.c | 26 ++++++++++++++++++++++++++ include/linux/rtc.h | 2 ++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 09b4437b3e616..39013867cbd6c 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -171,7 +171,7 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev, err = __rtc_read_alarm(rtc, &alrm); if (!err && !rtc_valid_tm(&alrm.time)) - rtc_set_alarm(rtc, &alrm); + rtc_initialize_alarm(rtc, &alrm); strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE); dev_set_name(&rtc->dev, "rtc%d", id); diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 8ec6b069a7f58..b2fea80dfb65e 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -375,6 +375,32 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) } EXPORT_SYMBOL_GPL(rtc_set_alarm); +/* Called once per device from rtc_device_register */ +int rtc_initialize_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) +{ + int err; + + err = rtc_valid_tm(&alarm->time); + if (err != 0) + return err; + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + + rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time); + rtc->aie_timer.period = ktime_set(0, 0); + if (alarm->enabled) { + rtc->aie_timer.enabled = 1; + timerqueue_add(&rtc->timerqueue, &rtc->aie_timer.node); + } + mutex_unlock(&rtc->ops_lock); + return err; +} +EXPORT_SYMBOL_GPL(rtc_initialize_alarm); + + + int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled) { int err = mutex_lock_interruptible(&rtc->ops_lock); diff --git a/include/linux/rtc.h b/include/linux/rtc.h index db3832d5f280b..020847fbe22b3 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -232,6 +232,8 @@ extern int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alrm); extern int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alrm); +extern int rtc_initialize_alarm(struct rtc_device *rtc, + struct rtc_wkalrm *alrm); extern void rtc_update_irq(struct rtc_device *rtc, unsigned long num, unsigned long events); From b0b15a24ccbf88f5928e7310df213fabb0a96017 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 1 Apr 2011 14:32:09 -0700 Subject: [PATCH 2210/2556] time: Add timekeeping_inject_sleeptime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some platforms cannot implement read_persistent_clock, as their RTC devices are only accessible when interrupts are enabled. This keeps them from being used by the timekeeping code on resume to measure the time in suspend. The RTC layer tries to work around this, by calling do_settimeofday on resume after irqs are reenabled to set the time properly. However, this only corrects CLOCK_REALTIME, and does not properly adjust the sleep time value. This causes btime in /proc/stat to be incorrect as well as making the new CLOCK_BOTTTIME inaccurate. This patch resolves the issue by introducing a new timekeeping hook to allow the RTC layer to inject the sleep time on resume. The code also checks to make sure that read_persistent_clock is nonfunctional before setting the sleep time, so that should the RTC's HCTOSYS option be configured in on a system that does support read_persistent_clock we will not increase the total_sleep_time twice. CC: Arve Hjønnevåg CC: Thomas Gleixner Acked-by: Arnd Bergmann Signed-off-by: John Stultz --- drivers/rtc/class.c | 23 +++++++--------- include/linux/time.h | 1 + kernel/time/timekeeping.c | 56 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 63 insertions(+), 17 deletions(-) diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 39013867cbd6c..4194e59e14cd7 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -41,26 +41,21 @@ static void rtc_device_release(struct device *dev) * system's wall clock; restore it on resume(). */ -static struct timespec delta; static time_t oldtime; +static struct timespec oldts; static int rtc_suspend(struct device *dev, pm_message_t mesg) { struct rtc_device *rtc = to_rtc_device(dev); struct rtc_time tm; - struct timespec ts = current_kernel_time(); if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0) return 0; rtc_read_time(rtc, &tm); + ktime_get_ts(&oldts); rtc_tm_to_time(&tm, &oldtime); - /* RTC precision is 1 second; adjust delta for avg 1/2 sec err */ - set_normalized_timespec(&delta, - ts.tv_sec - oldtime, - ts.tv_nsec - (NSEC_PER_SEC >> 1)); - return 0; } @@ -70,10 +65,12 @@ static int rtc_resume(struct device *dev) struct rtc_time tm; time_t newtime; struct timespec time; + struct timespec newts; if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0) return 0; + ktime_get_ts(&newts); rtc_read_time(rtc, &tm); if (rtc_valid_tm(&tm) != 0) { pr_debug("%s: bogus resume time\n", dev_name(&rtc->dev)); @@ -85,15 +82,13 @@ static int rtc_resume(struct device *dev) pr_debug("%s: time travel!\n", dev_name(&rtc->dev)); return 0; } + /* calculate the RTC time delta */ + set_normalized_timespec(&time, newtime - oldtime, 0); - /* restore wall clock using delta against this RTC; - * adjust again for avg 1/2 second RTC sampling error - */ - set_normalized_timespec(&time, - newtime + delta.tv_sec, - (NSEC_PER_SEC >> 1) + delta.tv_nsec); - do_settimeofday(&time); + /* subtract kernel time between rtc_suspend to rtc_resume */ + time = timespec_sub(time, timespec_sub(newts, oldts)); + timekeeping_inject_sleeptime(&time); return 0; } diff --git a/include/linux/time.h b/include/linux/time.h index 1d2b1d4590bd4..a9c186e6f95cf 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -128,6 +128,7 @@ struct timespec __current_kernel_time(void); /* does not take xtime_lock */ struct timespec get_monotonic_coarse(void); void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, struct timespec *wtom, struct timespec *sleep); +void timekeeping_inject_sleeptime(struct timespec *delta); #define CURRENT_TIME (current_kernel_time()) #define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 }) diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 8ad5d576755ea..8e6a05a5915a2 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -595,6 +595,58 @@ void __init timekeeping_init(void) /* time in seconds when suspend began */ static struct timespec timekeeping_suspend_time; +/** + * __timekeeping_inject_sleeptime - Internal function to add sleep interval + * @delta: pointer to a timespec delta value + * + * Takes a timespec offset measuring a suspend interval and properly + * adds the sleep offset to the timekeeping variables. + */ +static void __timekeeping_inject_sleeptime(struct timespec *delta) +{ + xtime = timespec_add(xtime, *delta); + wall_to_monotonic = timespec_sub(wall_to_monotonic, *delta); + total_sleep_time = timespec_add(total_sleep_time, *delta); +} + + +/** + * timekeeping_inject_sleeptime - Adds suspend interval to timeekeeping values + * @delta: pointer to a timespec delta value + * + * This hook is for architectures that cannot support read_persistent_clock + * because their RTC/persistent clock is only accessible when irqs are enabled. + * + * This function should only be called by rtc_resume(), and allows + * a suspend offset to be injected into the timekeeping values. + */ +void timekeeping_inject_sleeptime(struct timespec *delta) +{ + unsigned long flags; + struct timespec ts; + + /* Make sure we don't set the clock twice */ + read_persistent_clock(&ts); + if (!(ts.tv_sec == 0 && ts.tv_nsec == 0)) + return; + + write_seqlock_irqsave(&xtime_lock, flags); + timekeeping_forward_now(); + + __timekeeping_inject_sleeptime(delta); + + timekeeper.ntp_error = 0; + ntp_clear(); + update_vsyscall(&xtime, &wall_to_monotonic, timekeeper.clock, + timekeeper.mult); + + write_sequnlock_irqrestore(&xtime_lock, flags); + + /* signal hrtimers about time change */ + clock_was_set(); +} + + /** * timekeeping_resume - Resumes the generic timekeeping subsystem. * @@ -615,9 +667,7 @@ static void timekeeping_resume(void) if (timespec_compare(&ts, &timekeeping_suspend_time) > 0) { ts = timespec_sub(ts, timekeeping_suspend_time); - xtime = timespec_add(xtime, ts); - wall_to_monotonic = timespec_sub(wall_to_monotonic, ts); - total_sleep_time = timespec_add(total_sleep_time, ts); + __timekeeping_inject_sleeptime(&ts); } /* re-base the last cycle value */ timekeeper.clock->cycle_last = timekeeper.clock->read(timekeeper.clock); From 1c6a864319eff534d143efe4198e853304a83293 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 27 May 2011 11:33:18 -0700 Subject: [PATCH 2211/2556] rtc: Avoid accumulating time drift in suspend/resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because the RTC interface is only a second granular interface, each time we read from the RTC for suspend/resume, we introduce a half second (on average) of error. In order to avoid this error accumulating as the system is suspended over and over, this patch measures the time delta between the RTC and the system CLOCK_REALTIME. If the delta is less then 2 seconds from the last suspend, we compensate by using the previous time delta (keeping it close). If it is larger then 2 seconds, we assume the clock was set or has been changed, so we do no correction and update the delta. Note: If NTP is running, ths could seem to "fight" with the NTP corrected time, where as if the system time was off by 1 second, and NTP slewed the value in, a suspend/resume cycle could undo this correction, by trying to restore the previous offset from the RTC. However, without this patch, since each read could cause almost a full second worth of error, its possible to get almost 2 seconds of error just from the suspend/resume cycle alone, so this about equal to any offset added by the compensation. Further on systems that suspend/resume frequently, this should keep time closer then NTP could compensate for if the errors were allowed to accumulate. Credits to Arve Hjønnevåg for suggesting this solution. This patch also improves some of the variable names and adds more clear comments. CRs-Fixed: 319459 Change-Id: I245597716b0379fbcd1e6675ab707fdaabab1225 CC: Arve Hjønnevåg CC: Thomas Gleixner Signed-off-by: John Stultz Signed-off-by: Ashay Jaiswal --- drivers/rtc/class.c | 65 +++++++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 17 deletions(-) diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 4194e59e14cd7..01a7df5317c1b 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -41,20 +41,41 @@ static void rtc_device_release(struct device *dev) * system's wall clock; restore it on resume(). */ -static time_t oldtime; -static struct timespec oldts; +static struct timespec old_rtc, old_system, old_delta; + static int rtc_suspend(struct device *dev, pm_message_t mesg) { struct rtc_device *rtc = to_rtc_device(dev); struct rtc_time tm; - + struct timespec delta, delta_delta; if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0) return 0; + /* snapshot the current RTC and system time at suspend*/ rtc_read_time(rtc, &tm); - ktime_get_ts(&oldts); - rtc_tm_to_time(&tm, &oldtime); + getnstimeofday(&old_system); + rtc_tm_to_time(&tm, &old_rtc.tv_sec); + + + /* + * To avoid drift caused by repeated suspend/resumes, + * which each can add ~1 second drift error, + * try to compensate so the difference in system time + * and rtc time stays close to constant. + */ + delta = timespec_sub(old_system, old_rtc); + delta_delta = timespec_sub(delta, old_delta); + if (abs(delta_delta.tv_sec) >= 2) { + /* + * if delta_delta is too large, assume time correction + * has occured and set old_delta to the current delta. + */ + old_delta = delta; + } else { + /* Otherwise try to adjust old_system to compensate */ + old_system = timespec_sub(old_system, delta_delta); + } return 0; } @@ -63,32 +84,42 @@ static int rtc_resume(struct device *dev) { struct rtc_device *rtc = to_rtc_device(dev); struct rtc_time tm; - time_t newtime; - struct timespec time; - struct timespec newts; + struct timespec new_system, new_rtc; + struct timespec sleep_time; if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0) return 0; - ktime_get_ts(&newts); + /* snapshot the current rtc and system time at resume */ + getnstimeofday(&new_system); rtc_read_time(rtc, &tm); if (rtc_valid_tm(&tm) != 0) { pr_debug("%s: bogus resume time\n", dev_name(&rtc->dev)); return 0; } - rtc_tm_to_time(&tm, &newtime); - if (newtime <= oldtime) { - if (newtime < oldtime) + rtc_tm_to_time(&tm, &new_rtc.tv_sec); + new_rtc.tv_nsec = 0; + + if (new_rtc.tv_sec <= old_rtc.tv_sec) { + if (new_rtc.tv_sec < old_rtc.tv_sec) pr_debug("%s: time travel!\n", dev_name(&rtc->dev)); return 0; } - /* calculate the RTC time delta */ - set_normalized_timespec(&time, newtime - oldtime, 0); - /* subtract kernel time between rtc_suspend to rtc_resume */ - time = timespec_sub(time, timespec_sub(newts, oldts)); + /* calculate the RTC time delta (sleep time)*/ + sleep_time = timespec_sub(new_rtc, old_rtc); + + /* + * Since these RTC suspend/resume handlers are not called + * at the very end of suspend or the start of resume, + * some run-time may pass on either sides of the sleep time + * so subtract kernel run-time between rtc_suspend to rtc_resume + * to keep things accurate. + */ + sleep_time = timespec_sub(sleep_time, + timespec_sub(new_system, old_system)); - timekeeping_inject_sleeptime(&time); + timekeeping_inject_sleeptime(&sleep_time); return 0; } From 57fdca19e90974d00312e8d28c4143f905ea2d71 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 22 Apr 2011 22:02:33 +0200 Subject: [PATCH 2212/2556] ARM: Use struct syscore_ops instead of sysdevs for PM in common code Convert some ARM architecture's common code to using struct syscore_ops objects for power management instead of sysdev classes and sysdevs. This simplifies the code and reduces the kernel's memory footprint. It also is necessary for removing sysdevs from the kernel entirely in the future. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- arch/arm/common/vic.c | 69 ++++++++++++-------------------- arch/arm/include/asm/mach/time.h | 1 - arch/arm/kernel/leds.c | 28 +++++++------ arch/arm/kernel/time.c | 35 ++++++---------- arch/arm/vfp/vfpmodule.c | 19 +++------ 5 files changed, 58 insertions(+), 94 deletions(-) diff --git a/arch/arm/common/vic.c b/arch/arm/common/vic.c index ae5fe7292e0d4..1176613650360 100644 --- a/arch/arm/common/vic.c +++ b/arch/arm/common/vic.c @@ -22,17 +22,16 @@ #include #include #include -#include +#include #include #include #include #include -#if defined(CONFIG_PM) +#ifdef CONFIG_PM /** * struct vic_device - VIC PM device - * @sysdev: The system device which is registered. * @irq: The IRQ number for the base of the VIC. * @base: The register base for the VIC. * @resume_sources: A bitmask of interrupts for resume. @@ -43,8 +42,6 @@ * @protect: Save for VIC_PROTECT. */ struct vic_device { - struct sys_device sysdev; - void __iomem *base; int irq; u32 resume_sources; @@ -59,11 +56,6 @@ struct vic_device { static struct vic_device vic_devices[CONFIG_ARM_VIC_NR]; static int vic_id; - -static inline struct vic_device *to_vic(struct sys_device *sys) -{ - return container_of(sys, struct vic_device, sysdev); -} #endif /* CONFIG_PM */ /** @@ -85,10 +77,9 @@ static void vic_init2(void __iomem *base) writel(32, base + VIC_PL190_DEF_VECT_ADDR); } -#if defined(CONFIG_PM) -static int vic_class_resume(struct sys_device *dev) +#ifdef CONFIG_PM +static void resume_one_vic(struct vic_device *vic) { - struct vic_device *vic = to_vic(dev); void __iomem *base = vic->base; printk(KERN_DEBUG "%s: resuming vic at %p\n", __func__, base); @@ -107,13 +98,18 @@ static int vic_class_resume(struct sys_device *dev) writel(vic->soft_int, base + VIC_INT_SOFT); writel(~vic->soft_int, base + VIC_INT_SOFT_CLEAR); +} - return 0; +static void vic_resume(void) +{ + int id; + + for (id = vic_id - 1; id >= 0; id--) + resume_one_vic(vic_devices + id); } -static int vic_class_suspend(struct sys_device *dev, pm_message_t state) +static void suspend_one_vic(struct vic_device *vic) { - struct vic_device *vic = to_vic(dev); void __iomem *base = vic->base; printk(KERN_DEBUG "%s: suspending vic at %p\n", __func__, base); @@ -128,14 +124,21 @@ static int vic_class_suspend(struct sys_device *dev, pm_message_t state) writel(vic->resume_irqs, base + VIC_INT_ENABLE); writel(~vic->resume_irqs, base + VIC_INT_ENABLE_CLEAR); +} + +static int vic_suspend(void) +{ + int id; + + for (id = 0; id < vic_id; id++) + suspend_one_vic(vic_devices + id); return 0; } -struct sysdev_class vic_class = { - .name = "vic", - .suspend = vic_class_suspend, - .resume = vic_class_resume, +struct syscore_ops vic_syscore_ops = { + .suspend = vic_suspend, + .resume = vic_resume, }; /** @@ -147,30 +150,8 @@ struct sysdev_class vic_class = { */ static int __init vic_pm_init(void) { - struct vic_device *dev = vic_devices; - int err; - int id; - - if (vic_id == 0) - return 0; - - err = sysdev_class_register(&vic_class); - if (err) { - printk(KERN_ERR "%s: cannot register class\n", __func__); - return err; - } - - for (id = 0; id < vic_id; id++, dev++) { - dev->sysdev.id = id; - dev->sysdev.cls = &vic_class; - - err = sysdev_register(&dev->sysdev); - if (err) { - printk(KERN_ERR "%s: failed to register device\n", - __func__); - return err; - } - } + if (vic_id > 0) + register_syscore_ops(&vic_syscore_ops); return 0; } diff --git a/arch/arm/include/asm/mach/time.h b/arch/arm/include/asm/mach/time.h index 883f6be5117a7..d5adaae5ee2c0 100644 --- a/arch/arm/include/asm/mach/time.h +++ b/arch/arm/include/asm/mach/time.h @@ -34,7 +34,6 @@ * timer interrupt which may be pending. */ struct sys_timer { - struct sys_device dev; void (*init)(void); void (*suspend)(void); void (*resume)(void); diff --git a/arch/arm/kernel/leds.c b/arch/arm/kernel/leds.c index 31a316c1777b8..0f107dcb03479 100644 --- a/arch/arm/kernel/leds.c +++ b/arch/arm/kernel/leds.c @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -69,36 +70,37 @@ static ssize_t leds_store(struct sys_device *dev, static SYSDEV_ATTR(event, 0200, NULL, leds_store); -static int leds_suspend(struct sys_device *dev, pm_message_t state) +static struct sysdev_class leds_sysclass = { + .name = "leds", +}; + +static struct sys_device leds_device = { + .id = 0, + .cls = &leds_sysclass, +}; + +static int leds_suspend(void) { leds_event(led_stop); return 0; } -static int leds_resume(struct sys_device *dev) +static void leds_resume(void) { leds_event(led_start); - return 0; } -static int leds_shutdown(struct sys_device *dev) +static void leds_shutdown(void) { leds_event(led_halted); - return 0; } -static struct sysdev_class leds_sysclass = { - .name = "leds", +static struct syscore_ops leds_syscore_ops = { .shutdown = leds_shutdown, .suspend = leds_suspend, .resume = leds_resume, }; -static struct sys_device leds_device = { - .id = 0, - .cls = &leds_sysclass, -}; - static int __init leds_init(void) { int ret; @@ -107,6 +109,8 @@ static int __init leds_init(void) ret = sysdev_register(&leds_device); if (ret == 0) ret = sysdev_create_file(&leds_device, &attr_event); + if (ret == 0) + register_syscore_ops(&leds_syscore_ops); return ret; } diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index 3d76bf2337347..768861a488978 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include @@ -117,48 +117,37 @@ void timer_tick(void) #endif #if defined(CONFIG_PM) && !defined(CONFIG_GENERIC_CLOCKEVENTS) -static int timer_suspend(struct sys_device *dev, pm_message_t state) +static int timer_suspend(void) { - struct sys_timer *timer = container_of(dev, struct sys_timer, dev); - - if (timer->suspend != NULL) - timer->suspend(); + if (system_timer->suspend) + system_timer->suspend(); return 0; } -static int timer_resume(struct sys_device *dev) +static void timer_resume(void) { - struct sys_timer *timer = container_of(dev, struct sys_timer, dev); - - if (timer->resume != NULL) - timer->resume(); - - return 0; + if (system_timer->resume) + system_timer->resume(); } #else #define timer_suspend NULL #define timer_resume NULL #endif -static struct sysdev_class timer_sysclass = { - .name = "timer", +static struct syscore_ops timer_syscore_ops = { .suspend = timer_suspend, .resume = timer_resume, }; -static int __init timer_init_sysfs(void) +static int __init timer_init_syscore_ops(void) { - int ret = sysdev_class_register(&timer_sysclass); - if (ret == 0) { - system_timer->dev.cls = &timer_sysclass; - ret = sysdev_register(&system_timer->dev); - } + register_syscore_ops(&timer_syscore_ops); - return ret; + return 0; } -device_initcall(timer_init_sysfs); +device_initcall(timer_init_syscore_ops); void __init time_init(void) { diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index 7ed63ac12a96e..e23016fa41e3c 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c @@ -426,9 +426,9 @@ void vfp_reinit(void) #ifdef CONFIG_PM -#include +#include -static int vfp_pm_suspend(struct sys_device *dev, pm_message_t state) +static int vfp_pm_suspend(void) { struct thread_info *ti = current_thread_info(); u32 fpexc = fmrx(FPEXC); @@ -448,34 +448,25 @@ static int vfp_pm_suspend(struct sys_device *dev, pm_message_t state) return 0; } -static int vfp_pm_resume(struct sys_device *dev) +static void vfp_pm_resume(void) { /* ensure we have access to the vfp */ vfp_enable(NULL); /* and disable it to ensure the next usage restores the state */ fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); - - return 0; } -static struct sysdev_class vfp_pm_sysclass = { - .name = "vfp", +static struct syscore_ops vfp_pm_syscore_ops = { .suspend = vfp_pm_suspend, .resume = vfp_pm_resume, }; -static struct sys_device vfp_pm_sysdev = { - .cls = &vfp_pm_sysclass, -}; - static void vfp_pm_init(void) { - sysdev_class_register(&vfp_pm_sysclass); - sysdev_register(&vfp_pm_sysdev); + register_syscore_ops(&vfp_pm_syscore_ops); } - #else static inline void vfp_pm_init(void) { } #endif /* CONFIG_PM */ From 6d239ab4b76d3983f4f0da418018d49bbdefb65f Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 13 Jul 2011 10:43:20 -0700 Subject: [PATCH 2213/2556] net: wireless: bcm4329: Fix roaming message processing Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/wl_iw.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/bcm4329/wl_iw.c b/drivers/net/wireless/bcm4329/wl_iw.c index b98392665d831..974c80c1e958b 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.c +++ b/drivers/net/wireless/bcm4329/wl_iw.c @@ -7893,9 +7893,12 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) cmd = SIOCGIWAP; if (status == WLC_E_STATUS_SUCCESS) { - memcpy(wrqu.addr.sa_data, &e->addr.octet, ETHER_ADDR_LEN); - wrqu.addr.sa_family = ARPHRD_ETHER; - cmd = SIOCGIWAP; + WL_ASSOC(("%s: WLC_E_ROAM: success\n", __FUNCTION__)); +#if defined(ROAM_NOT_USED) + roam_no_success_send = FALSE; + roam_no_success = 0; +#endif + goto wl_iw_event_end; } #if defined(ROAM_NOT_USED) else if (status == WLC_E_STATUS_NO_NETWORKS) { @@ -8099,7 +8102,6 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) #endif #if WIRELESS_EXT > 14 - memset(extra, 0, sizeof(extra)); if (wl_iw_check_conn_fail(e, extra, sizeof(extra))) { cmd = IWEVCUSTOM; From 1fc7216b64184dc5c314648a6e6bbfc100730bad Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Fri, 17 Jun 2011 16:37:22 -0700 Subject: [PATCH 2214/2556] binder: Quiet binder The majority of the binder messages are not very informative and useful to the reader. Make them available via debug mechanisms. (cherry picked from commit 6764fd9f8a0d1cb1f9bbff4100fda0df6aeafafc) Change-Id: Icc3f3619105b4d5477954e0eb8806d4f28e543ce Signed-off-by: Laura Abbott --- drivers/staging/android/binder.c | 209 ++++++++++++++++++++----------- 1 file changed, 138 insertions(+), 71 deletions(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index e13b4c4834076..a0763da99a84c 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -3,6 +3,7 @@ * Android IPC Subsystem * * Copyright (C) 2007-2008 Google, Inc. + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -97,9 +98,9 @@ enum { BINDER_DEBUG_BUFFER_ALLOC = 1U << 13, BINDER_DEBUG_PRIORITY_CAP = 1U << 14, BINDER_DEBUG_BUFFER_ALLOC_ASYNC = 1U << 15, + BINDER_DEBUG_TOP_ERRORS = 1U << 16, }; -static uint32_t binder_debug_mask = BINDER_DEBUG_USER_ERROR | - BINDER_DEBUG_FAILED_TRANSACTION | BINDER_DEBUG_DEAD_TRANSACTION; +static uint32_t binder_debug_mask; module_param_named(debug_mask, binder_debug_mask, uint, S_IWUSR | S_IRUGO); static int binder_debug_no_lock; @@ -638,7 +639,8 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate, goto free_range; if (vma == NULL) { - printk(KERN_ERR "binder: %d: binder_alloc_buf failed to " + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: %d: binder_alloc_buf failed to " "map pages in userspace, no vma\n", proc->pid); goto err_no_vma; } @@ -651,7 +653,8 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate, BUG_ON(*page); *page = alloc_page(GFP_KERNEL | __GFP_ZERO); if (*page == NULL) { - printk(KERN_ERR "binder: %d: binder_alloc_buf failed " + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: %d: binder_alloc_buf failed " "for page at %p\n", proc->pid, page_addr); goto err_alloc_page_failed; } @@ -660,7 +663,8 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate, page_array_ptr = page; ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr); if (ret) { - printk(KERN_ERR "binder: %d: binder_alloc_buf failed " + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: %d: binder_alloc_buf failed " "to map page at %p in kernel\n", proc->pid, page_addr); goto err_map_kernel_failed; @@ -669,7 +673,8 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate, (uintptr_t)page_addr + proc->user_buffer_offset; ret = vm_insert_page(vma, user_page_addr, page[0]); if (ret) { - printk(KERN_ERR "binder: %d: binder_alloc_buf failed " + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: %d: binder_alloc_buf failed " "to map page at %lx in userspace\n", proc->pid, user_page_addr); goto err_vm_insert_page_failed; @@ -718,7 +723,8 @@ static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc, size_t size; if (proc->vma == NULL) { - printk(KERN_ERR "binder: %d: binder_alloc_buf, no vma\n", + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: %d: binder_alloc_buf, no vma\n", proc->pid); return NULL; } @@ -756,7 +762,8 @@ static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc, } } if (best_fit == NULL) { - printk(KERN_ERR "binder: %d: binder_alloc_buf size %zd failed, " + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: %d: binder_alloc_buf size %zd failed, " "no address space\n", proc->pid, size); return NULL; } @@ -991,7 +998,8 @@ static int binder_inc_node(struct binder_node *node, int strong, int internal, node->internal_strong_refs == 0 && !(node == binder_context_mgr_node && node->has_strong_ref)) { - printk(KERN_ERR "binder: invalid inc strong " + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: invalid inc strong " "node for %d\n", node->debug_id); return -EINVAL; } @@ -1007,7 +1015,8 @@ static int binder_inc_node(struct binder_node *node, int strong, int internal, node->local_weak_refs++; if (!node->has_weak_ref && list_empty(&node->work.entry)) { if (target_list == NULL) { - printk(KERN_ERR "binder: invalid inc weak node " + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: invalid inc weak node " "for %d\n", node->debug_id); return -EINVAL; } @@ -1044,7 +1053,7 @@ static int binder_dec_node(struct binder_node *node, int strong, int internal) if (node->proc) { rb_erase(&node->rb_node, &node->proc->nodes); binder_debug(BINDER_DEBUG_INTERNAL_REFS, - "binder: refless node %d deleted\n", + "binder: refless node %d deleted\n", node->debug_id); } else { hlist_del(&node->dead_node); @@ -1263,14 +1272,16 @@ static void binder_send_failed_reply(struct binder_transaction *t, binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, "binder: send failed reply for " "transaction %d to %d:%d\n", - t->debug_id, target_thread->proc->pid, + t->debug_id, + target_thread->proc->pid, target_thread->pid); binder_pop_transaction(target_thread, t); target_thread->return_error = error_code; wake_up_interruptible(&target_thread->wait); } else { - printk(KERN_ERR "binder: reply failed, target " + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: reply failed, target " "thread, %d:%d, has error code %d " "already\n", target_thread->proc->pid, target_thread->pid, @@ -1308,14 +1319,15 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, int debug_id = buffer->debug_id; binder_debug(BINDER_DEBUG_TRANSACTION, - "binder: %d buffer release %d, size %zd-%zd, failed at %p\n", - proc->pid, buffer->debug_id, + "binder: %d buffer release %d, size %zd-%zd, failed at" + " %p\n", proc->pid, buffer->debug_id, buffer->data_size, buffer->offsets_size, failed_at); if (buffer->target_node) binder_dec_node(buffer->target_node, 1, 0); - offp = (size_t *)(buffer->data + ALIGN(buffer->data_size, sizeof(void *))); + offp = (size_t *)(buffer->data + ALIGN(buffer->data_size, + sizeof(void *))); if (failed_at) off_end = failed_at; else @@ -1325,7 +1337,8 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, if (*offp > buffer->data_size - sizeof(*fp) || buffer->data_size < sizeof(*fp) || !IS_ALIGNED(*offp, sizeof(void *))) { - printk(KERN_ERR "binder: transaction release %d bad" + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: transaction release %d bad" "offset %zd, size %zd\n", debug_id, *offp, buffer->data_size); continue; @@ -1334,29 +1347,35 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, switch (fp->type) { case BINDER_TYPE_BINDER: case BINDER_TYPE_WEAK_BINDER: { - struct binder_node *node = binder_get_node(proc, fp->binder); + struct binder_node *node = binder_get_node(proc, + fp->binder); if (node == NULL) { - printk(KERN_ERR "binder: transaction release %d" + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: transaction release %d" " bad node %p\n", debug_id, fp->binder); break; } binder_debug(BINDER_DEBUG_TRANSACTION, " node %d u%p\n", node->debug_id, node->ptr); - binder_dec_node(node, fp->type == BINDER_TYPE_BINDER, 0); + binder_dec_node(node, fp->type == BINDER_TYPE_BINDER, + 0); } break; case BINDER_TYPE_HANDLE: case BINDER_TYPE_WEAK_HANDLE: { - struct binder_ref *ref = binder_get_ref(proc, fp->handle); + struct binder_ref *ref = binder_get_ref(proc, + fp->handle); if (ref == NULL) { - printk(KERN_ERR "binder: transaction release %d" + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: transaction release %d" " bad handle %ld\n", debug_id, fp->handle); break; } binder_debug(BINDER_DEBUG_TRANSACTION, " ref %d desc %d (node %d)\n", - ref->debug_id, ref->desc, ref->node->debug_id); + ref->debug_id, ref->desc, + ref->node->debug_id); binder_dec_ref(ref, fp->type == BINDER_TYPE_HANDLE); } break; @@ -1368,7 +1387,8 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, break; default: - printk(KERN_ERR "binder: transaction release %d bad " + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: transaction release %d bad " "object type %lx\n", debug_id, fp->type); break; } @@ -1594,15 +1614,19 @@ static void binder_transaction(struct binder_proc *proc, case BINDER_TYPE_BINDER: case BINDER_TYPE_WEAK_BINDER: { struct binder_ref *ref; - struct binder_node *node = binder_get_node(proc, fp->binder); + struct binder_node *node = binder_get_node(proc, + fp->binder); if (node == NULL) { - node = binder_new_node(proc, fp->binder, fp->cookie); + node = binder_new_node(proc, fp->binder, + fp->cookie); if (node == NULL) { return_error = BR_FAILED_REPLY; goto err_binder_new_node_failed; } - node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK; - node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); + node->min_priority = fp->flags & + FLAT_BINDER_FLAG_PRIORITY_MASK; + node->accept_fds = !!(fp->flags & + FLAT_BINDER_FLAG_ACCEPTS_FDS); } if (fp->cookie != node->cookie) { binder_user_error("binder: %d:%d sending u%p " @@ -1632,7 +1656,8 @@ static void binder_transaction(struct binder_proc *proc, } break; case BINDER_TYPE_HANDLE: case BINDER_TYPE_WEAK_HANDLE: { - struct binder_ref *ref = binder_get_ref(proc, fp->handle); + struct binder_ref *ref = binder_get_ref(proc, + fp->handle); if (ref == NULL) { binder_user_error("binder: %d:%d got " "transaction with invalid " @@ -1648,24 +1673,31 @@ static void binder_transaction(struct binder_proc *proc, fp->type = BINDER_TYPE_WEAK_BINDER; fp->binder = ref->node->ptr; fp->cookie = ref->node->cookie; - binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL); + binder_inc_node(ref->node, fp->type == + BINDER_TYPE_BINDER, 0, NULL); binder_debug(BINDER_DEBUG_TRANSACTION, - " ref %d desc %d -> node %d u%p\n", - ref->debug_id, ref->desc, ref->node->debug_id, - ref->node->ptr); + " ref %d desc %d -> node %d u%p\n", + ref->debug_id, ref->desc, + ref->node->debug_id, + ref->node->ptr); } else { struct binder_ref *new_ref; - new_ref = binder_get_ref_for_node(target_proc, ref->node); + new_ref = binder_get_ref_for_node(target_proc, + ref->node); if (new_ref == NULL) { return_error = BR_FAILED_REPLY; goto err_binder_get_ref_for_node_failed; } fp->handle = new_ref->desc; - binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL); + binder_inc_ref(new_ref, fp->type == + BINDER_TYPE_HANDLE, NULL); binder_debug(BINDER_DEBUG_TRANSACTION, - " ref %d desc %d -> ref %d desc %d (node %d)\n", - ref->debug_id, ref->desc, new_ref->debug_id, - new_ref->desc, ref->node->debug_id); + " ref %d desc %d -> ref %d" + " desc %d (node %d)\n", + ref->debug_id, ref->desc, + new_ref->debug_id, + new_ref->desc, + ref->node->debug_id); } } break; @@ -1675,13 +1707,19 @@ static void binder_transaction(struct binder_proc *proc, if (reply) { if (!(in_reply_to->flags & TF_ACCEPT_FDS)) { - binder_user_error("binder: %d:%d got reply with fd, %ld, but target does not allow fds\n", - proc->pid, thread->pid, fp->handle); + binder_user_error("binder: %d:%d got" + " reply with fd, %ld, but" + " target does not allow fds\n", + proc->pid, thread->pid, + fp->handle); return_error = BR_FAILED_REPLY; goto err_fd_not_allowed; } } else if (!target_node->accept_fds) { - binder_user_error("binder: %d:%d got transaction with fd, %ld, but target does not allow fds\n", + binder_user_error( + "binder: %d:%d got transaction" + " with fd, %ld, but target does" + " not allow fds\n", proc->pid, thread->pid, fp->handle); return_error = BR_FAILED_REPLY; goto err_fd_not_allowed; @@ -1689,12 +1727,15 @@ static void binder_transaction(struct binder_proc *proc, file = fget(fp->handle); if (file == NULL) { - binder_user_error("binder: %d:%d got transaction with invalid fd, %ld\n", + binder_user_error( + "binder: %d:%d got transaction" + " with invalid fd, %ld\n", proc->pid, thread->pid, fp->handle); return_error = BR_FAILED_REPLY; goto err_fget_failed; } - target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC); + target_fd = task_get_unused_fd_flags(target_proc, + O_CLOEXEC); if (target_fd < 0) { fput(file); return_error = BR_FAILED_REPLY; @@ -1702,7 +1743,8 @@ static void binder_transaction(struct binder_proc *proc, } task_fd_install(target_proc, target_fd, file); binder_debug(BINDER_DEBUG_TRANSACTION, - " fd %ld -> %d\n", fp->handle, target_fd); + " fd %ld -> %d\n", fp->handle, + target_fd); /* TODO: fput? */ fp->handle = target_fd; } break; @@ -1851,9 +1893,11 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, break; } binder_debug(BINDER_DEBUG_USER_REFS, - "binder: %d:%d %s ref %d desc %d s %d w %d for node %d\n", - proc->pid, thread->pid, debug_string, ref->debug_id, - ref->desc, ref->strong, ref->weak, ref->node->debug_id); + "binder: %d:%d %s ref %d desc %d s %d w %d" + " for node %d\n", proc->pid, thread->pid, + debug_string, ref->debug_id, ref->desc, + ref->strong, ref->weak, + ref->node->debug_id); break; } case BC_INCREFS_DONE: @@ -1914,15 +1958,19 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, binder_debug(BINDER_DEBUG_USER_REFS, "binder: %d:%d %s node %d ls %d lw %d\n", proc->pid, thread->pid, - cmd == BC_INCREFS_DONE ? "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE", - node->debug_id, node->local_strong_refs, node->local_weak_refs); + cmd == BC_INCREFS_DONE ? "BC_INCREFS_DONE" + : "BC_ACQUIRE_DONE", + node->debug_id, node->local_strong_refs, + node->local_weak_refs); break; } case BC_ATTEMPT_ACQUIRE: - printk(KERN_ERR "binder: BC_ATTEMPT_ACQUIRE not supported\n"); + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: BC_ATTEMPT_ACQUIRE not supported\n"); return -EINVAL; case BC_ACQUIRE_RESULT: - printk(KERN_ERR "binder: BC_ACQUIRE_RESULT not supported\n"); + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: BC_ACQUIRE_RESULT not supported\n"); return -EINVAL; case BC_FREE_BUFFER: { @@ -1948,9 +1996,11 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, break; } binder_debug(BINDER_DEBUG_FREE_BUFFER, - "binder: %d:%d BC_FREE_BUFFER u%p found buffer %d for %s transaction\n", - proc->pid, thread->pid, data_ptr, buffer->debug_id, - buffer->transaction ? "active" : "finished"); + "binder: %d:%d BC_FREE_BUFFER u%p found" + " buffer %d for %s transaction\n", + proc->pid, thread->pid, data_ptr, + buffer->debug_id, buffer->transaction ? + "active" : "finished"); if (buffer->transaction) { buffer->transaction->buffer = NULL; @@ -2047,13 +2097,15 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, } binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION, - "binder: %d:%d %s %p ref %d desc %d s %d w %d for node %d\n", + "binder: %d:%d %s %p ref %d desc %d s %d" + " w %d for node %d\n", proc->pid, thread->pid, cmd == BC_REQUEST_DEATH_NOTIFICATION ? "BC_REQUEST_DEATH_NOTIFICATION" : "BC_CLEAR_DEATH_NOTIFICATION", cookie, ref->debug_id, ref->desc, - ref->strong, ref->weak, ref->node->debug_id); + ref->strong, ref->weak, + ref->node->debug_id); if (cmd == BC_REQUEST_DEATH_NOTIFICATION) { if (ref->death) { @@ -2067,10 +2119,12 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, death = kzalloc(sizeof(*death), GFP_KERNEL); if (death == NULL) { thread->return_error = BR_ERROR; - binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, - "binder: %d:%d " - "BC_REQUEST_DEATH_NOTIFICATION failed\n", - proc->pid, thread->pid); + binder_debug( + BINDER_DEBUG_FAILED_TRANSACTION, + "binder: %d:%d " + "BC_REQUEST_DEATH_NOTIFICATION" + " failed\n", + proc->pid, thread->pid); break; } binder_stats_created(BINDER_STAT_DEATH); @@ -2159,7 +2213,8 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, } break; default: - printk(KERN_ERR "binder: %d:%d unknown command %d\n", + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: %d:%d unknown command %d\n", proc->pid, thread->pid, cmd); return -EINVAL; } @@ -2629,9 +2684,11 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) unsigned int size = _IOC_SIZE(cmd); void __user *ubuf = (void __user *)arg; - /*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/ + /*binder_debug(BINDER_DEBUG_TOP_ERRORS, "binder_ioctl: %d:%d %x %lx\n", + proc->pid, current->pid, cmd, arg);*/ - ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); + ret = wait_event_interruptible(binder_user_error_wait, + binder_stop_on_user_error < 2); if (ret) return ret; @@ -2688,20 +2745,23 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) break; } case BINDER_SET_MAX_THREADS: - if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) { + if (copy_from_user(&proc->max_threads, ubuf, + sizeof(proc->max_threads))) { ret = -EINVAL; goto err; } break; case BINDER_SET_CONTEXT_MGR: if (binder_context_mgr_node != NULL) { - printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n"); + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: BINDER_SET_CONTEXT_MGR already set\n"); ret = -EBUSY; goto err; } if (binder_context_mgr_uid != -1) { if (binder_context_mgr_uid != current->cred->euid) { - printk(KERN_ERR "binder: BINDER_SET_" + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: BINDER_SET_" "CONTEXT_MGR bad uid %d != %d\n", current->cred->euid, binder_context_mgr_uid); @@ -2747,7 +2807,9 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) mutex_unlock(&binder_lock); wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); if (ret && ret != -ERESTARTSYS) - printk(KERN_INFO "binder: %d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret); + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: %d:%d ioctl %x %lx returned %d\n", + proc->pid, current->pid, cmd, arg, ret); return ret; } @@ -2821,7 +2883,9 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) #ifdef CONFIG_CPU_CACHE_VIPT if (cache_is_vipt_aliasing()) { while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) { - printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer); + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder_mmap: %d %lx-%lx maps %p bad alignment\n", + proc->pid, vma->vm_start, vma->vm_end, proc->buffer); vma->vm_start += PAGE_SIZE; } } @@ -2852,7 +2916,8 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) proc->files = get_files_struct(current); proc->vma = vma; - /*printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p\n", + /*binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder_mmap: %d %lx-%lx maps %p\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/ return 0; @@ -2865,7 +2930,8 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) err_get_vm_area_failed: err_already_mapped: err_bad_arg: - printk(KERN_ERR "binder_mmap: %d %lx-%lx %s failed %d\n", + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder_mmap: %d %lx-%lx %s failed %d\n", proc->pid, vma->vm_start, vma->vm_end, failure_string, ret); return ret; } @@ -3020,7 +3086,8 @@ static void binder_deferred_release(struct binder_proc *proc) if (t) { t->buffer = NULL; buffer->transaction = NULL; - printk(KERN_ERR "binder: release proc %d, " + binder_debug(BINDER_DEBUG_TOP_ERRORS, + "binder: release proc %d, " "transaction %d, not freed\n", proc->pid, t->debug_id); /*BUG();*/ From df04067ea413337ad3f445718d3fc2b9f04fb47e Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Thu, 11 Aug 2011 21:33:35 -0700 Subject: [PATCH 2215/2556] android: logger: bump up the logger buffer sizes (port from common android-2.6.39 commit: 11430f16545205c614dd5bd58e4a7ee630fc0f9f) events: (no change, 256) main: 64 -> 256 radio: 64 -> 256 system: 64 -> 256 Change-Id: I42c0d4cc9fa89735d2f05010236070e1e4737ffc Signed-off-by: JP Abgrall --- drivers/staging/android/logger.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/android/logger.c b/drivers/staging/android/logger.c index 531bdbeede89f..fa76ce7678a6e 100644 --- a/drivers/staging/android/logger.c +++ b/drivers/staging/android/logger.c @@ -555,10 +555,10 @@ static struct logger_log VAR = { \ .size = SIZE, \ }; -DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 64*1024) +DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 256*1024) DEFINE_LOGGER_DEVICE(log_events, LOGGER_LOG_EVENTS, 256*1024) -DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO, 64*1024) -DEFINE_LOGGER_DEVICE(log_system, LOGGER_LOG_SYSTEM, 64*1024) +DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO, 256*1024) +DEFINE_LOGGER_DEVICE(log_system, LOGGER_LOG_SYSTEM, 256*1024) static struct logger_log *get_log_from_minor(int minor) { From 5a9905ef6165547e982e3c830dc469f9851d425b Mon Sep 17 00:00:00 2001 From: Ying Han Date: Tue, 24 May 2011 17:12:26 -0700 Subject: [PATCH 2216/2556] vmscan: change shrink_slab() interfaces by passing shrink_control Consolidate the existing parameters to shrink_slab() into a new shrink_control struct. This is needed later to pass the same struct to shrinkers. Signed-off-by: Ying Han Cc: KOSAKI Motohiro Cc: Minchan Kim Acked-by: Pavel Emelyanov Cc: KAMEZAWA Hiroyuki Cc: Mel Gorman Acked-by: Rik van Riel Cc: Johannes Weiner Cc: Hugh Dickins Cc: Dave Hansen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/drop_caches.c | 6 +++++- include/linux/mm.h | 13 +++++++++++-- mm/memory-failure.c | 7 ++++++- mm/vmscan.c | 46 ++++++++++++++++++++++++++++++++------------- 4 files changed, 55 insertions(+), 17 deletions(-) diff --git a/fs/drop_caches.c b/fs/drop_caches.c index 2195c213ab2f5..393f6353649a0 100644 --- a/fs/drop_caches.c +++ b/fs/drop_caches.c @@ -36,9 +36,13 @@ static void drop_pagecache_sb(struct super_block *sb, void *unused) static void drop_slab(void) { int nr_objects; + struct shrink_control shrink = { + .gfp_mask = GFP_KERNEL, + .nr_scanned = 1000, + }; do { - nr_objects = shrink_slab(1000, GFP_KERNEL, 1000); + nr_objects = shrink_slab(&shrink, 1000); } while (nr_objects > 10); } diff --git a/include/linux/mm.h b/include/linux/mm.h index 9b67ae75253fd..e3c59f8ee1677 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1146,6 +1146,15 @@ static inline void sync_mm_rss(struct task_struct *task, struct mm_struct *mm) } #endif +/* + * This struct is used to pass information from page reclaim to the shrinkers. + * We consolidate the values for easier extention later. + */ +struct shrink_control { + unsigned long nr_scanned; + gfp_t gfp_mask; +}; + /* * A callback you can register to apply pressure to ageable caches. * @@ -1610,8 +1619,8 @@ int in_gate_area_no_task(unsigned long addr); int drop_caches_sysctl_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *); -unsigned long shrink_slab(unsigned long scanned, gfp_t gfp_mask, - unsigned long lru_pages); +unsigned long shrink_slab(struct shrink_control *shrink, + unsigned long lru_pages); #ifndef CONFIG_MMU #define randomize_va_space 0 diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 0207c2f6f8bd7..fc75973498db8 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -239,7 +239,12 @@ void shake_page(struct page *p, int access) if (access) { int nr; do { - nr = shrink_slab(1000, GFP_KERNEL, 1000); + struct shrink_control shrink = { + .gfp_mask = GFP_KERNEL, + .nr_scanned = 1000, + }; + + nr = shrink_slab(&shrink, 1000); if (page_count(p) == 1) break; } while (nr > 10); diff --git a/mm/vmscan.c b/mm/vmscan.c index ddd437b35037d..bf00f825d68e6 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -221,11 +221,13 @@ EXPORT_SYMBOL(unregister_shrinker); * * Returns the number of slab objects which we shrunk. */ -unsigned long shrink_slab(unsigned long scanned, gfp_t gfp_mask, - unsigned long lru_pages) +unsigned long shrink_slab(struct shrink_control *shrink, + unsigned long lru_pages) { struct shrinker *shrinker; unsigned long ret = 0; + unsigned long scanned = shrink->nr_scanned; + gfp_t gfp_mask = shrink->gfp_mask; if (scanned == 0) scanned = SWAP_CLUSTER_MAX; @@ -2042,7 +2044,8 @@ static bool all_unreclaimable(struct zonelist *zonelist, * else, the number of pages reclaimed */ static unsigned long do_try_to_free_pages(struct zonelist *zonelist, - struct scan_control *sc) + struct scan_control *sc, + struct shrink_control *shrink) { int priority; unsigned long total_scanned = 0; @@ -2076,7 +2079,8 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist, lru_pages += zone_reclaimable_pages(zone); } - shrink_slab(sc->nr_scanned, sc->gfp_mask, lru_pages); + shrink->nr_scanned = sc->nr_scanned; + shrink_slab(shrink, lru_pages); if (reclaim_state) { sc->nr_reclaimed += reclaim_state->reclaimed_slab; reclaim_state->reclaimed_slab = 0; @@ -2148,12 +2152,15 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order, .mem_cgroup = NULL, .nodemask = nodemask, }; + struct shrink_control shrink = { + .gfp_mask = sc.gfp_mask, + }; trace_mm_vmscan_direct_reclaim_begin(order, sc.may_writepage, gfp_mask); - nr_reclaimed = do_try_to_free_pages(zonelist, &sc); + nr_reclaimed = do_try_to_free_pages(zonelist, &sc, &shrink); trace_mm_vmscan_direct_reclaim_end(nr_reclaimed); @@ -2213,17 +2220,20 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *mem_cont, .order = 0, .mem_cgroup = mem_cont, .nodemask = NULL, /* we don't care the placement */ + .gfp_mask = (gfp_mask & GFP_RECLAIM_MASK) | + (GFP_HIGHUSER_MOVABLE & ~GFP_RECLAIM_MASK), + }; + struct shrink_control shrink = { + .gfp_mask = sc.gfp_mask, }; - sc.gfp_mask = (gfp_mask & GFP_RECLAIM_MASK) | - (GFP_HIGHUSER_MOVABLE & ~GFP_RECLAIM_MASK); zonelist = NODE_DATA(numa_node_id())->node_zonelists; trace_mm_vmscan_memcg_reclaim_begin(0, sc.may_writepage, sc.gfp_mask); - nr_reclaimed = do_try_to_free_pages(zonelist, &sc); + nr_reclaimed = do_try_to_free_pages(zonelist, &sc, &shrink); trace_mm_vmscan_memcg_reclaim_end(nr_reclaimed); @@ -2351,6 +2361,9 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, .order = order, .mem_cgroup = NULL, }; + struct shrink_control shrink = { + .gfp_mask = sc.gfp_mask, + }; loop_again: total_scanned = 0; sc.nr_reclaimed = 0; @@ -2440,8 +2453,8 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, 8*high_wmark_pages(zone), end_zone, 0)) shrink_zone(priority, zone, &sc); reclaim_state->reclaimed_slab = 0; - nr_slab = shrink_slab(sc.nr_scanned, GFP_KERNEL, - lru_pages); + shrink.nr_scanned = sc.nr_scanned; + nr_slab = shrink_slab(&shrink, lru_pages); sc.nr_reclaimed += reclaim_state->reclaimed_slab; total_scanned += sc.nr_scanned; @@ -2793,7 +2806,10 @@ unsigned long shrink_all_memory(unsigned long nr_to_reclaim) .swappiness = vm_swappiness, .order = 0, }; - struct zonelist * zonelist = node_zonelist(numa_node_id(), sc.gfp_mask); + struct shrink_control shrink = { + .gfp_mask = sc.gfp_mask, + }; + struct zonelist *zonelist = node_zonelist(numa_node_id(), sc.gfp_mask); struct task_struct *p = current; unsigned long nr_reclaimed; @@ -2802,7 +2818,7 @@ unsigned long shrink_all_memory(unsigned long nr_to_reclaim) reclaim_state.reclaimed_slab = 0; p->reclaim_state = &reclaim_state; - nr_reclaimed = do_try_to_free_pages(zonelist, &sc); + nr_reclaimed = do_try_to_free_pages(zonelist, &sc, &shrink); p->reclaim_state = NULL; lockdep_clear_current_reclaim_state(); @@ -2977,6 +2993,9 @@ static int __zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order) .swappiness = vm_swappiness, .order = order, }; + struct shrink_control shrink = { + .gfp_mask = sc.gfp_mask, + }; unsigned long nr_slab_pages0, nr_slab_pages1; cond_resched(); @@ -3003,6 +3022,7 @@ static int __zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order) } nr_slab_pages0 = zone_page_state(zone, NR_SLAB_RECLAIMABLE); + shrink.nr_scanned = sc.nr_scanned; if (nr_slab_pages0 > zone->min_slab_pages) { /* * shrink_slab() does not currently allow us to determine how @@ -3018,7 +3038,7 @@ static int __zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order) unsigned long lru_pages = zone_reclaimable_pages(zone); /* No reclaimable slab or very low memory pressure */ - if (!shrink_slab(sc.nr_scanned, gfp_mask, lru_pages)) + if (!shrink_slab(&shrink, lru_pages)) break; /* Freed enough memory */ From 73d02e1bb50bf3e59d7be12920de848631a8dbab Mon Sep 17 00:00:00 2001 From: Ying Han Date: Tue, 24 May 2011 17:12:27 -0700 Subject: [PATCH 2217/2556] vmscan: change shrinker API by passing shrink_control struct Change each shrinker's API by consolidating the existing parameters into shrink_control struct. This will simplify any further features added w/o touching each file of shrinker. [akpm@linux-foundation.org: fix build] [akpm@linux-foundation.org: fix warning] [kosaki.motohiro@jp.fujitsu.com: fix up new shrinker API] [akpm@linux-foundation.org: fix xfs warning] [akpm@linux-foundation.org: update gfs2] Signed-off-by: Ying Han Cc: KOSAKI Motohiro Cc: Minchan Kim Acked-by: Pavel Emelyanov Cc: KAMEZAWA Hiroyuki Cc: Mel Gorman Acked-by: Rik van Riel Cc: Johannes Weiner Cc: Hugh Dickins Cc: Dave Hansen Cc: Steven Whitehouse Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Conflicts: drivers/staging/zcache/zcache.c --- arch/x86/kvm/mmu.c | 3 ++- drivers/gpu/drm/i915/i915_gem.c | 9 +++----- drivers/gpu/drm/ttm/ttm_page_alloc.c | 4 +++- fs/dcache.c | 8 +++++-- fs/drop_caches.c | 3 +-- fs/gfs2/glock.c | 5 +++- fs/gfs2/quota.c | 12 ++++++---- fs/gfs2/quota.h | 4 +++- fs/inode.c | 6 ++++- fs/mbcache.c | 10 ++++---- fs/nfs/dir.c | 5 +++- fs/nfs/internal.h | 2 +- fs/quota/dquot.c | 5 +++- fs/xfs/linux-2.6/xfs_buf.c | 4 ++-- fs/xfs/linux-2.6/xfs_sync.c | 5 ++-- fs/xfs/quota/xfs_qm.c | 6 ++--- include/linux/mm.h | 19 +++++++++------- mm/memory-failure.c | 3 +-- mm/vmscan.c | 34 ++++++++++++++++------------ net/sunrpc/auth.c | 4 +++- 20 files changed, 91 insertions(+), 60 deletions(-) diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index f02b8edc3d449..1bf7b28bed230 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -3575,10 +3575,11 @@ static int kvm_mmu_remove_some_alloc_mmu_pages(struct kvm *kvm, return kvm_mmu_prepare_zap_page(kvm, page, invalid_list); } -static int mmu_shrink(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) +static int mmu_shrink(struct shrinker *shrink, struct shrink_control *sc) { struct kvm *kvm; struct kvm *kvm_freed = NULL; + int nr_to_scan = sc->nr_to_scan; if (nr_to_scan == 0) goto out; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 729c95a8fe6ea..382ae273519b0 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -56,9 +56,7 @@ static int i915_gem_phys_pwrite(struct drm_device *dev, static void i915_gem_free_object_tail(struct drm_i915_gem_object *obj); static int i915_gem_inactive_shrink(struct shrinker *shrinker, - int nr_to_scan, - gfp_t gfp_mask); - + struct shrink_control *sc); /* some bookkeeping */ static void i915_gem_info_add_obj(struct drm_i915_private *dev_priv, @@ -4064,9 +4062,7 @@ i915_gpu_is_active(struct drm_device *dev) } static int -i915_gem_inactive_shrink(struct shrinker *shrinker, - int nr_to_scan, - gfp_t gfp_mask) +i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc) { struct drm_i915_private *dev_priv = container_of(shrinker, @@ -4074,6 +4070,7 @@ i915_gem_inactive_shrink(struct shrinker *shrinker, mm.inactive_shrinker); struct drm_device *dev = dev_priv->dev; struct drm_i915_gem_object *obj, *next; + int nr_to_scan = sc->nr_to_scan; int cnt; if (!mutex_trylock(&dev->struct_mutex)) diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c index b1e02fffd3ccd..7f730f8262034 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c @@ -394,12 +394,14 @@ static int ttm_pool_get_num_unused_pages(void) /** * Callback for mm to request pool to reduce number of page held. */ -static int ttm_pool_mm_shrink(struct shrinker *shrink, int shrink_pages, gfp_t gfp_mask) +static int ttm_pool_mm_shrink(struct shrinker *shrink, + struct shrink_control *sc) { static atomic_t start_pool = ATOMIC_INIT(0); unsigned i; unsigned pool_offset = atomic_add_return(1, &start_pool); struct ttm_page_pool *pool; + int shrink_pages = sc->nr_to_scan; pool_offset = pool_offset % NUM_POOLS; /* select start pool in round robin fashion */ diff --git a/fs/dcache.c b/fs/dcache.c index 1baddc1cec48f..9bb566d0b6db2 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1241,7 +1241,7 @@ void shrink_dcache_parent(struct dentry * parent) EXPORT_SYMBOL(shrink_dcache_parent); /* - * Scan `nr' dentries and return the number which remain. + * Scan `sc->nr_slab_to_reclaim' dentries and return the number which remain. * * We need to avoid reentering the filesystem if the caller is performing a * GFP_NOFS allocation attempt. One example deadlock is: @@ -1252,8 +1252,12 @@ EXPORT_SYMBOL(shrink_dcache_parent); * * In this case we return -1 to tell the caller that we baled. */ -static int shrink_dcache_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask) +static int shrink_dcache_memory(struct shrinker *shrink, + struct shrink_control *sc) { + int nr = sc->nr_to_scan; + gfp_t gfp_mask = sc->gfp_mask; + if (nr) { if (!(gfp_mask & __GFP_FS)) return -1; diff --git a/fs/drop_caches.c b/fs/drop_caches.c index 393f6353649a0..0891072d600c2 100644 --- a/fs/drop_caches.c +++ b/fs/drop_caches.c @@ -38,11 +38,10 @@ static void drop_slab(void) int nr_objects; struct shrink_control shrink = { .gfp_mask = GFP_KERNEL, - .nr_scanned = 1000, }; do { - nr_objects = shrink_slab(&shrink, 1000); + nr_objects = shrink_slab(&shrink, 1000, 1000); } while (nr_objects > 10); } diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 7cd9a5a68d59d..793239839e3a8 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -1382,11 +1382,14 @@ void gfs2_glock_complete(struct gfs2_glock *gl, int ret) } -static int gfs2_shrink_glock_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask) +static int gfs2_shrink_glock_memory(struct shrinker *shrink, + struct shrink_control *sc) { struct gfs2_glock *gl; int may_demote; int nr_skipped = 0; + int nr = sc->nr_to_scan; + gfp_t gfp_mask = sc->gfp_mask; LIST_HEAD(skipped); if (nr == 0) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index a689901963dea..9bc65a26535a4 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -77,19 +78,20 @@ static LIST_HEAD(qd_lru_list); static atomic_t qd_lru_count = ATOMIC_INIT(0); static DEFINE_SPINLOCK(qd_lru_lock); -int gfs2_shrink_qd_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask) +int gfs2_shrink_qd_memory(struct shrinker *shrink, struct shrink_control *sc) { struct gfs2_quota_data *qd; struct gfs2_sbd *sdp; + int nr_to_scan = sc->nr_to_scan; - if (nr == 0) + if (nr_to_scan == 0) goto out; - if (!(gfp_mask & __GFP_FS)) + if (!(sc->gfp_mask & __GFP_FS)) return -1; spin_lock(&qd_lru_lock); - while (nr && !list_empty(&qd_lru_list)) { + while (nr_to_scan && !list_empty(&qd_lru_list)) { qd = list_entry(qd_lru_list.next, struct gfs2_quota_data, qd_reclaim); sdp = qd->qd_gl->gl_sbd; @@ -110,7 +112,7 @@ int gfs2_shrink_qd_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask) spin_unlock(&qd_lru_lock); kmem_cache_free(gfs2_quotad_cachep, qd); spin_lock(&qd_lru_lock); - nr--; + nr_to_scan--; } spin_unlock(&qd_lru_lock); diff --git a/fs/gfs2/quota.h b/fs/gfs2/quota.h index e7d236ca48bd2..90bf1c302a983 100644 --- a/fs/gfs2/quota.h +++ b/fs/gfs2/quota.h @@ -12,6 +12,7 @@ struct gfs2_inode; struct gfs2_sbd; +struct shrink_control; #define NO_QUOTA_CHANGE ((u32)-1) @@ -51,7 +52,8 @@ static inline int gfs2_quota_lock_check(struct gfs2_inode *ip) return ret; } -extern int gfs2_shrink_qd_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask); +extern int gfs2_shrink_qd_memory(struct shrinker *shrink, + struct shrink_control *sc); extern const struct quotactl_ops gfs2_quotactl_ops; #endif /* __QUOTA_DOT_H__ */ diff --git a/fs/inode.c b/fs/inode.c index 0647d80accf6f..e0520542ced8b 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -702,8 +702,12 @@ static void prune_icache(int nr_to_scan) * This function is passed the number of inodes to scan, and it returns the * total number of remaining possibly-reclaimable inodes. */ -static int shrink_icache_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask) +static int shrink_icache_memory(struct shrinker *shrink, + struct shrink_control *sc) { + int nr = sc->nr_to_scan; + gfp_t gfp_mask = sc->gfp_mask; + if (nr) { /* * Nasty deadlock avoidance. We may hold various FS locks, diff --git a/fs/mbcache.c b/fs/mbcache.c index a25444ab2baf8..e6196feccc1e4 100644 --- a/fs/mbcache.c +++ b/fs/mbcache.c @@ -90,7 +90,8 @@ static DEFINE_SPINLOCK(mb_cache_spinlock); * What the mbcache registers as to get shrunk dynamically. */ -static int mb_cache_shrink_fn(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask); +static int mb_cache_shrink_fn(struct shrinker *shrink, + struct shrink_control *sc); static struct shrinker mb_cache_shrinker = { .shrink = mb_cache_shrink_fn, @@ -156,18 +157,19 @@ __mb_cache_entry_release_unlock(struct mb_cache_entry *ce) * gets low. * * @shrink: (ignored) - * @nr_to_scan: Number of objects to scan - * @gfp_mask: (ignored) + * @sc: shrink_control passed from reclaim * * Returns the number of objects which are present in the cache. */ static int -mb_cache_shrink_fn(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) +mb_cache_shrink_fn(struct shrinker *shrink, struct shrink_control *sc) { LIST_HEAD(free_list); struct mb_cache *cache; struct mb_cache_entry *entry, *tmp; int count = 0; + int nr_to_scan = sc->nr_to_scan; + gfp_t gfp_mask = sc->gfp_mask; mb_debug("trying to free %d entries", nr_to_scan); spin_lock(&mb_cache_spinlock); diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 2c3eb33b904dc..242d7012c5b1f 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1962,11 +1962,14 @@ static void nfs_access_free_list(struct list_head *head) } } -int nfs_access_cache_shrinker(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) +int nfs_access_cache_shrinker(struct shrinker *shrink, + struct shrink_control *sc) { LIST_HEAD(head); struct nfs_inode *nfsi, *next; struct nfs_access_entry *cache; + int nr_to_scan = sc->nr_to_scan; + gfp_t gfp_mask = sc->gfp_mask; if ((gfp_mask & GFP_KERNEL) != GFP_KERNEL) return (nr_to_scan == 0) ? 0 : -1; diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index cf9fdbdabc675..fabb489c2b85e 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -218,7 +218,7 @@ void nfs_close_context(struct nfs_open_context *ctx, int is_sync); /* dir.c */ extern int nfs_access_cache_shrinker(struct shrinker *shrink, - int nr_to_scan, gfp_t gfp_mask); + struct shrink_control *sc); /* inode.c */ extern struct workqueue_struct *nfsiod_workqueue; diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index b59ee61f4b9a0..afac03212ad47 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -691,8 +691,11 @@ static void prune_dqcache(int count) * This is called from kswapd when we think we need some * more memory */ -static int shrink_dqcache_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask) +static int shrink_dqcache_memory(struct shrinker *shrink, + struct shrink_control *sc) { + int nr = sc->nr_to_scan; + if (nr) { spin_lock(&dq_list_lock); prune_dqcache(nr); diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index ac1c7e8378ddd..8321493bf8f44 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -1543,12 +1543,12 @@ xfs_wait_buftarg( int xfs_buftarg_shrink( struct shrinker *shrink, - int nr_to_scan, - gfp_t mask) + struct shrink_control *sc) { struct xfs_buftarg *btp = container_of(shrink, struct xfs_buftarg, bt_shrinker); struct xfs_buf *bp; + int nr_to_scan = sc->nr_to_scan; LIST_HEAD(dispose); if (!nr_to_scan) diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index e22f0057d21fa..aaa4cf738a5df 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -999,13 +999,14 @@ xfs_reclaim_inodes( static int xfs_reclaim_inode_shrink( struct shrinker *shrink, - int nr_to_scan, - gfp_t gfp_mask) + struct shrink_control *sc) { struct xfs_mount *mp; struct xfs_perag *pag; xfs_agnumber_t ag; int reclaimable; + int nr_to_scan = sc->nr_to_scan; + gfp_t gfp_mask = sc->gfp_mask; mp = container_of(shrink, struct xfs_mount, m_inode_shrink); if (nr_to_scan) { diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c index 206a2815ced67..471f911c7d9e6 100644 --- a/fs/xfs/quota/xfs_qm.c +++ b/fs/xfs/quota/xfs_qm.c @@ -60,7 +60,7 @@ STATIC void xfs_qm_list_destroy(xfs_dqlist_t *); STATIC int xfs_qm_init_quotainos(xfs_mount_t *); STATIC int xfs_qm_init_quotainfo(xfs_mount_t *); -STATIC int xfs_qm_shake(struct shrinker *, int, gfp_t); +STATIC int xfs_qm_shake(struct shrinker *, struct shrink_control *); static struct shrinker xfs_qm_shaker = { .shrink = xfs_qm_shake, @@ -2027,10 +2027,10 @@ xfs_qm_shake_freelist( STATIC int xfs_qm_shake( struct shrinker *shrink, - int nr_to_scan, - gfp_t gfp_mask) + struct shrink_control *sc) { int ndqused, nfree, n; + gfp_t gfp_mask = sc->gfp_mask; if (!kmem_shake_allow(gfp_mask)) return 0; diff --git a/include/linux/mm.h b/include/linux/mm.h index e3c59f8ee1677..a021c046e7743 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1151,18 +1151,20 @@ static inline void sync_mm_rss(struct task_struct *task, struct mm_struct *mm) * We consolidate the values for easier extention later. */ struct shrink_control { - unsigned long nr_scanned; gfp_t gfp_mask; + + /* How many slab objects shrinker() should scan and try to reclaim */ + unsigned long nr_to_scan; }; /* * A callback you can register to apply pressure to ageable caches. * - * 'shrink' is passed a count 'nr_to_scan' and a 'gfpmask'. It should - * look through the least-recently-used 'nr_to_scan' entries and - * attempt to free them up. It should return the number of objects - * which remain in the cache. If it returns -1, it means it cannot do - * any scanning at this time (eg. there is a risk of deadlock). + * 'sc' is passed shrink_control which includes a count 'nr_to_scan' + * and a 'gfpmask'. It should look through the least-recently-used + * 'nr_to_scan' entries and attempt to free them up. It should return + * the number of objects which remain in the cache. If it returns -1, it means + * it cannot do any scanning at this time (eg. there is a risk of deadlock). * * The 'gfpmask' refers to the allocation we are currently trying to * fulfil. @@ -1171,7 +1173,7 @@ struct shrink_control { * querying the cache size, so a fastpath for that case is appropriate. */ struct shrinker { - int (*shrink)(struct shrinker *, int nr_to_scan, gfp_t gfp_mask); + int (*shrink)(struct shrinker *, struct shrink_control *sc); int seeks; /* seeks to recreate an obj */ /* These are for internal use */ @@ -1620,7 +1622,8 @@ int in_gate_area_no_task(unsigned long addr); int drop_caches_sysctl_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *); unsigned long shrink_slab(struct shrink_control *shrink, - unsigned long lru_pages); + unsigned long nr_pages_scanned, + unsigned long lru_pages); #ifndef CONFIG_MMU #define randomize_va_space 0 diff --git a/mm/memory-failure.c b/mm/memory-failure.c index fc75973498db8..a0166ab250516 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -241,10 +241,9 @@ void shake_page(struct page *p, int access) do { struct shrink_control shrink = { .gfp_mask = GFP_KERNEL, - .nr_scanned = 1000, }; - nr = shrink_slab(&shrink, 1000); + nr = shrink_slab(&shrink, 1000, 1000); if (page_count(p) == 1) break; } while (nr > 10); diff --git a/mm/vmscan.c b/mm/vmscan.c index bf00f825d68e6..3c94e79c6cf52 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -201,6 +201,14 @@ void unregister_shrinker(struct shrinker *shrinker) } EXPORT_SYMBOL(unregister_shrinker); +static inline int do_shrinker_shrink(struct shrinker *shrinker, + struct shrink_control *sc, + unsigned long nr_to_scan) +{ + sc->nr_to_scan = nr_to_scan; + return (*shrinker->shrink)(shrinker, sc); +} + #define SHRINK_BATCH 128 /* * Call the shrink functions to age shrinkable caches @@ -222,15 +230,14 @@ EXPORT_SYMBOL(unregister_shrinker); * Returns the number of slab objects which we shrunk. */ unsigned long shrink_slab(struct shrink_control *shrink, + unsigned long nr_pages_scanned, unsigned long lru_pages) { struct shrinker *shrinker; unsigned long ret = 0; - unsigned long scanned = shrink->nr_scanned; - gfp_t gfp_mask = shrink->gfp_mask; - if (scanned == 0) - scanned = SWAP_CLUSTER_MAX; + if (nr_pages_scanned == 0) + nr_pages_scanned = SWAP_CLUSTER_MAX; if (!down_read_trylock(&shrinker_rwsem)) { /* Assume we'll be able to shrink next time */ @@ -243,8 +250,8 @@ unsigned long shrink_slab(struct shrink_control *shrink, unsigned long total_scan; unsigned long max_pass; - max_pass = (*shrinker->shrink)(shrinker, 0, gfp_mask); - delta = (4 * scanned) / shrinker->seeks; + max_pass = do_shrinker_shrink(shrinker, shrink, 0); + delta = (4 * nr_pages_scanned) / shrinker->seeks; delta *= max_pass; do_div(delta, lru_pages + 1); shrinker->nr += delta; @@ -271,9 +278,9 @@ unsigned long shrink_slab(struct shrink_control *shrink, int shrink_ret; int nr_before; - nr_before = (*shrinker->shrink)(shrinker, 0, gfp_mask); - shrink_ret = (*shrinker->shrink)(shrinker, this_scan, - gfp_mask); + nr_before = do_shrinker_shrink(shrinker, shrink, 0); + shrink_ret = do_shrinker_shrink(shrinker, shrink, + this_scan); if (shrink_ret == -1) break; if (shrink_ret < nr_before) @@ -2079,8 +2086,7 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist, lru_pages += zone_reclaimable_pages(zone); } - shrink->nr_scanned = sc->nr_scanned; - shrink_slab(shrink, lru_pages); + shrink_slab(shrink, sc->nr_scanned, lru_pages); if (reclaim_state) { sc->nr_reclaimed += reclaim_state->reclaimed_slab; reclaim_state->reclaimed_slab = 0; @@ -2453,8 +2459,7 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, 8*high_wmark_pages(zone), end_zone, 0)) shrink_zone(priority, zone, &sc); reclaim_state->reclaimed_slab = 0; - shrink.nr_scanned = sc.nr_scanned; - nr_slab = shrink_slab(&shrink, lru_pages); + nr_slab = shrink_slab(&shrink, sc.nr_scanned, lru_pages); sc.nr_reclaimed += reclaim_state->reclaimed_slab; total_scanned += sc.nr_scanned; @@ -3022,7 +3027,6 @@ static int __zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order) } nr_slab_pages0 = zone_page_state(zone, NR_SLAB_RECLAIMABLE); - shrink.nr_scanned = sc.nr_scanned; if (nr_slab_pages0 > zone->min_slab_pages) { /* * shrink_slab() does not currently allow us to determine how @@ -3038,7 +3042,7 @@ static int __zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order) unsigned long lru_pages = zone_reclaimable_pages(zone); /* No reclaimable slab or very low memory pressure */ - if (!shrink_slab(&shrink, lru_pages)) + if (!shrink_slab(&shrink, sc.nr_scanned, lru_pages)) break; /* Freed enough memory */ diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 67e31276682ab..cd6e4aa19dbfc 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -326,10 +326,12 @@ rpcauth_prune_expired(struct list_head *free, int nr_to_scan) * Run memory cache shrinker. */ static int -rpcauth_cache_shrinker(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) +rpcauth_cache_shrinker(struct shrinker *shrink, struct shrink_control *sc) { LIST_HEAD(free); int res; + int nr_to_scan = sc->nr_to_scan; + gfp_t gfp_mask = sc->gfp_mask; if ((gfp_mask & GFP_KERNEL) != GFP_KERNEL) return (nr_to_scan == 0) ? 0 : -1; From cd85296a84785342c9bd6a7493f9db2746a47548 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 29 May 2011 13:46:08 +0100 Subject: [PATCH 2218/2556] cifs/ubifs: Fix shrinker API change fallout Commit 1495f230fa77 ("vmscan: change shrinker API by passing shrink_control struct") changed the API of ->shrink(), but missed ubifs and cifs instances. Signed-off-by: Al Viro Signed-off-by: Linus Torvalds Conflicts: fs/cifs/cifsacl.c drewis: checkout --ours (referenced fuction does not exist in current cifs) --- fs/ubifs/shrinker.c | 3 ++- fs/ubifs/ubifs.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/ubifs/shrinker.c b/fs/ubifs/shrinker.c index 46961c0032362..ca953a945029a 100644 --- a/fs/ubifs/shrinker.c +++ b/fs/ubifs/shrinker.c @@ -277,8 +277,9 @@ static int kick_a_thread(void) return 0; } -int ubifs_shrinker(struct shrinker *shrink, int nr, gfp_t gfp_mask) +int ubifs_shrinker(struct shrinker *shrink, struct shrink_control *sc) { + int nr = sc->nr_to_scan; int freed, contention = 0; long clean_zn_cnt = atomic_long_read(&ubifs_clean_zn_cnt); diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index 381d6b207a525..e9e694e2a18f5 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -1586,7 +1586,7 @@ int ubifs_tnc_start_commit(struct ubifs_info *c, struct ubifs_zbranch *zroot); int ubifs_tnc_end_commit(struct ubifs_info *c); /* shrinker.c */ -int ubifs_shrinker(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask); +int ubifs_shrinker(struct shrinker *shrink, struct shrink_control *sc); /* commit.c */ int ubifs_bg_thread(void *info); From fef2e433127ea9e078793453e99698302e740148 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 22 Jun 2011 16:05:47 -0700 Subject: [PATCH 2219/2556] android: lowmemorykiller: Fix arguments to lowmem_shrink The arguments to shrink functions have changed, update lowmem_shrink to match. Change-Id: I48f436e0509c416dc0b18db500234f7b1d6d42e1 Signed-off-by: Colin Cross --- drivers/staging/android/lowmemorykiller.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 897bc3ab96672..86d51959b29f8 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -79,7 +79,7 @@ task_notify_func(struct notifier_block *self, unsigned long val, void *data) return NOTIFY_OK; } -static int lowmem_shrink(struct shrinker *s, int nr_to_scan, gfp_t gfp_mask) +static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) { struct task_struct *p; struct task_struct *selected = NULL; @@ -116,17 +116,17 @@ static int lowmem_shrink(struct shrinker *s, int nr_to_scan, gfp_t gfp_mask) break; } } - if (nr_to_scan > 0) - lowmem_print(3, "lowmem_shrink %d, %x, ofree %d %d, ma %d\n", - nr_to_scan, gfp_mask, other_free, other_file, + if (sc->nr_to_scan > 0) + lowmem_print(3, "lowmem_shrink %lu, %x, ofree %d %d, ma %d\n", + sc->nr_to_scan, sc->gfp_mask, other_free, other_file, min_adj); rem = global_page_state(NR_ACTIVE_ANON) + global_page_state(NR_ACTIVE_FILE) + global_page_state(NR_INACTIVE_ANON) + global_page_state(NR_INACTIVE_FILE); - if (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) { - lowmem_print(5, "lowmem_shrink %d, %x, return %d\n", - nr_to_scan, gfp_mask, rem); + if (sc->nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) { + lowmem_print(5, "lowmem_shrink %lu, %x, return %d\n", + sc->nr_to_scan, sc->gfp_mask, rem); return rem; } selected_oom_adj = min_adj; @@ -175,8 +175,8 @@ static int lowmem_shrink(struct shrinker *s, int nr_to_scan, gfp_t gfp_mask) force_sig(SIGKILL, selected); rem -= selected_tasksize; } - lowmem_print(4, "lowmem_shrink %d, %x, return %d\n", - nr_to_scan, gfp_mask, rem); + lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n", + sc->nr_to_scan, sc->gfp_mask, rem); read_unlock(&tasklist_lock); return rem; } From 68521ec547ed1cf39735b862d7766ef5bf970cd2 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 22 Jun 2011 16:05:47 -0700 Subject: [PATCH 2220/2556] mm: ashmem: Fix arguments to ashmem_shrink The arguments to shrink functions have changed, update ashmem_shrink to match. Change-Id: Id279d22d761a2a7c4965c957960eef804d06cc07 Signed-off-by: Colin Cross --- mm/ashmem.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/mm/ashmem.c b/mm/ashmem.c index 803b90a16fb82..d24f0094ca99b 100644 --- a/mm/ashmem.c +++ b/mm/ashmem.c @@ -351,14 +351,14 @@ static int ashmem_mmap(struct file *file, struct vm_area_struct *vma) * chunks of ashmem regions LRU-wise one-at-a-time until we hit 'nr_to_scan' * pages freed. */ -static int ashmem_shrink(struct shrinker *s, int nr_to_scan, gfp_t gfp_mask) +static int ashmem_shrink(struct shrinker *s, struct shrink_control *sc) { struct ashmem_range *range, *next; /* We might recurse into filesystem code, so bail out if necessary */ - if (nr_to_scan && !(gfp_mask & __GFP_FS)) + if (sc->nr_to_scan && !(sc->gfp_mask & __GFP_FS)) return -1; - if (!nr_to_scan) + if (!sc->nr_to_scan) return lru_count; if (!mutex_trylock(&ashmem_mutex)) @@ -372,8 +372,8 @@ static int ashmem_shrink(struct shrinker *s, int nr_to_scan, gfp_t gfp_mask) range->purged = ASHMEM_WAS_PURGED; lru_del(range); - nr_to_scan -= range_size(range); - if (nr_to_scan <= 0) + sc->nr_to_scan -= range_size(range); + if (sc->nr_to_scan <= 0) break; } mutex_unlock(&ashmem_mutex); @@ -730,8 +730,13 @@ static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case ASHMEM_PURGE_ALL_CACHES: ret = -EPERM; if (capable(CAP_SYS_ADMIN)) { - ret = ashmem_shrink(&ashmem_shrinker, 0, GFP_KERNEL); - ashmem_shrink(&ashmem_shrinker, ret, GFP_KERNEL); + struct shrink_control sc = { + .gfp_mask = GFP_KERNEL, + .nr_to_scan = 0, + }; + ret = ashmem_shrink(&ashmem_shrinker, &sc); + sc.nr_to_scan = ret; + ashmem_shrink(&ashmem_shrinker, &sc); } break; case ASHMEM_CACHE_FLUSH_RANGE: From 154d2d13a6e043c1cb9e3e3eae9f901710e81432 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 26 Jun 2011 10:34:02 +0100 Subject: [PATCH 2221/2556] ARM: entry: re-allocate registers in irq entry assembly macros This avoids the irq entry assembly corrupting r5, thereby allowing it to be preserved through to the svc exit code. Change-Id: I2ddf6dd73fe4f92a4e1bf526966a65872bc9ac81 Signed-off-by: Russell King Signed-off-by: Taniya Das --- arch/arm/kernel/entry-armv.S | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 94fbf2c72ab9a..79fc0eacd956c 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -28,16 +28,16 @@ #include /* - * Interrupt handling. Preserves r7, r8, r9 + * Interrupt handling. */ .macro irq_handler #ifdef CONFIG_MULTI_IRQ_HANDLER - ldr r5, =handle_arch_irq + ldr r1, =handle_arch_irq mov r0, sp - ldr r5, [r5] + ldr r1, [r1] adr lr, BSYM(9997f) - teq r5, #0 - movne pc, r5 + teq r1, #0 + movne pc, r1 #endif arch_irq_handler_default 9997: From e5ecae6ef68e9772d4382bdbf139408b39c45a98 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 27 Sep 2011 18:56:26 +0530 Subject: [PATCH 2222/2556] ARM: Make global handler and CONFIG_MULTI_IRQ_HANDLER mutually exclusive Even when CONFIG_MULTI_IRQ_HANDLER is selected, the core code requires the arch_irq_handler_default macro to be defined as a fallback. It turns out nobody is using that particular feature as both PXA and shmobile have all their machine descriptors populated with the interrupt handler, leaving unused code (or empty macros) in their entry-macro.S file just to be able to compile entry-armv.S. Make CONFIG_MULTI_IRQ_HANDLER exclusive wrt arch_irq_handler_default, which allows to remove one test from the hot path. Also cleanup both PXA and shmobile entry-macro.S. Cc: Eric Miao Cc: Paul Mundt Change-Id: I73216afceaecd765f50913707b5252802863d702 Signed-off-by: Marc Zyngier Signed-off-by: Taniya Das --- arch/arm/kernel/entry-armv.S | 7 ++-- arch/arm/mach-pxa/include/mach/entry-macro.S | 36 ------------------- .../mach-shmobile/include/mach/entry-macro.S | 12 ------- 3 files changed, 3 insertions(+), 52 deletions(-) diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 79fc0eacd956c..095fb91f611fa 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -34,12 +34,11 @@ #ifdef CONFIG_MULTI_IRQ_HANDLER ldr r1, =handle_arch_irq mov r0, sp - ldr r1, [r1] adr lr, BSYM(9997f) - teq r1, #0 - movne pc, r1 -#endif + ldr pc, [r1] +#else arch_irq_handler_default +#endif 9997: .endm diff --git a/arch/arm/mach-pxa/include/mach/entry-macro.S b/arch/arm/mach-pxa/include/mach/entry-macro.S index a73bc86a3c263..260c0c17692a0 100644 --- a/arch/arm/mach-pxa/include/mach/entry-macro.S +++ b/arch/arm/mach-pxa/include/mach/entry-macro.S @@ -7,45 +7,9 @@ * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ -#include -#include .macro disable_fiq .endm - .macro get_irqnr_preamble, base, tmp - .endm - .macro arch_ret_to_user, tmp1, tmp2 .endm - - .macro get_irqnr_and_base, irqnr, irqstat, base, tmp - mrc p15, 0, \tmp, c0, c0, 0 @ CPUID - mov \tmp, \tmp, lsr #13 - and \tmp, \tmp, #0x7 @ Core G - cmp \tmp, #1 - bhi 1002f - - @ Core Generation 1 (PXA25x) - mov \base, #io_p2v(0x40000000) @ IIR Ctl = 0x40d00000 - add \base, \base, #0x00d00000 - ldr \irqstat, [\base, #0] @ ICIP - ldr \irqnr, [\base, #4] @ ICMR - - ands \irqnr, \irqstat, \irqnr - beq 1001f - rsb \irqstat, \irqnr, #0 - and \irqstat, \irqstat, \irqnr - clz \irqnr, \irqstat - rsb \irqnr, \irqnr, #(31 + PXA_IRQ(0)) - b 1001f -1002: - @ Core Generation 2 (PXA27x) or Core Generation 3 (PXA3xx) - mrc p6, 0, \irqstat, c5, c0, 0 @ ICHP - tst \irqstat, #0x80000000 - beq 1001f - bic \irqstat, \irqstat, #0x80000000 - mov \irqnr, \irqstat, lsr #16 - add \irqnr, \irqnr, #(PXA_IRQ(0)) -1001: - .endm diff --git a/arch/arm/mach-shmobile/include/mach/entry-macro.S b/arch/arm/mach-shmobile/include/mach/entry-macro.S index d791f10eeac7e..2a57b2964ee91 100644 --- a/arch/arm/mach-shmobile/include/mach/entry-macro.S +++ b/arch/arm/mach-shmobile/include/mach/entry-macro.S @@ -18,17 +18,5 @@ .macro disable_fiq .endm - .macro get_irqnr_preamble, base, tmp - .endm - - .macro get_irqnr_and_base, irqnr, irqstat, base, tmp - .endm - - .macro test_for_ipi, irqnr, irqstat, base, tmp - .endm - - .macro test_for_ltirq, irqnr, irqstat, base, tmp - .endm - .macro arch_ret_to_user, tmp1, tmp2 .endm From 4e35290cbd61248cbda23b77be90a0a5604c73c7 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Wed, 4 May 2011 15:56:49 -0700 Subject: [PATCH 2223/2556] arm: Add unwinding support for division functions The software division functions never had unwinding annotations added. Currently, when a division by zero occurs the backtrace shown will stop at Ldiv0 or some completely unrelated function. Add unwinding annotations in hopes of getting a more useful backtrace when a division by zero occurs. Change-Id: I747732bc883867a5c89332596dd0cc598c428f8e Signed-off-by: Laura Abbott --- arch/arm/lib/lib1funcs.S | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/arch/arm/lib/lib1funcs.S b/arch/arm/lib/lib1funcs.S index 6dc06487f3c3e..63b75df56d2fb 100644 --- a/arch/arm/lib/lib1funcs.S +++ b/arch/arm/lib/lib1funcs.S @@ -35,7 +35,7 @@ Boston, MA 02111-1307, USA. */ #include #include - +#include .macro ARM_DIV_BODY dividend, divisor, result, curbit @@ -207,6 +207,7 @@ Boston, MA 02111-1307, USA. */ ENTRY(__udivsi3) ENTRY(__aeabi_uidiv) +UNWIND(.fnstart) subs r2, r1, #1 moveq pc, lr @@ -230,10 +231,12 @@ ENTRY(__aeabi_uidiv) mov r0, r0, lsr r2 mov pc, lr +UNWIND(.fnend) ENDPROC(__udivsi3) ENDPROC(__aeabi_uidiv) ENTRY(__umodsi3) +UNWIND(.fnstart) subs r2, r1, #1 @ compare divisor with 1 bcc Ldiv0 @@ -247,10 +250,12 @@ ENTRY(__umodsi3) mov pc, lr +UNWIND(.fnend) ENDPROC(__umodsi3) ENTRY(__divsi3) ENTRY(__aeabi_idiv) +UNWIND(.fnstart) cmp r1, #0 eor ip, r0, r1 @ save the sign of the result. @@ -287,10 +292,12 @@ ENTRY(__aeabi_idiv) rsbmi r0, r0, #0 mov pc, lr +UNWIND(.fnend) ENDPROC(__divsi3) ENDPROC(__aeabi_idiv) ENTRY(__modsi3) +UNWIND(.fnstart) cmp r1, #0 beq Ldiv0 @@ -310,11 +317,14 @@ ENTRY(__modsi3) rsbmi r0, r0, #0 mov pc, lr +UNWIND(.fnend) ENDPROC(__modsi3) #ifdef CONFIG_AEABI ENTRY(__aeabi_uidivmod) +UNWIND(.fnstart) +UNWIND(.save {r0, r1, ip, lr} ) stmfd sp!, {r0, r1, ip, lr} bl __aeabi_uidiv @@ -323,10 +333,12 @@ ENTRY(__aeabi_uidivmod) sub r1, r1, r3 mov pc, lr +UNWIND(.fnend) ENDPROC(__aeabi_uidivmod) ENTRY(__aeabi_idivmod) - +UNWIND(.fnstart) +UNWIND(.save {r0, r1, ip, lr} ) stmfd sp!, {r0, r1, ip, lr} bl __aeabi_idiv ldmfd sp!, {r1, r2, ip, lr} @@ -334,15 +346,18 @@ ENTRY(__aeabi_idivmod) sub r1, r1, r3 mov pc, lr +UNWIND(.fnend) ENDPROC(__aeabi_idivmod) #endif -Ldiv0: - +ENTRY(Ldiv0) +UNWIND(.fnstart) +UNWIND(.pad #4) +UNWIND(.save {lr}) str lr, [sp, #-8]! bl __div0 mov r0, #0 @ About as wrong as it could be. ldr pc, [sp], #8 - - +UNWIND(.fnend) +ENDPROC(Ldiv0) From cfe1d2404b7f40fe9ed9714e7493e38eb30b7f30 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Mon, 14 May 2012 22:58:26 -0500 Subject: [PATCH 2224/2556] arm: selective checkout cafs/msm-2.6.38 generic caf stuff, memory management, smp, wont use alot of it but makes us basically the same excluding the mach-msm dir --- Documentation/arm/msm/boot.txt | 23 + .../arm/msm/emulate_domain_manager.txt | 282 +++ Documentation/arm/msm/kgsl-sysfs.txt | 98 + Documentation/kernel-parameters.txt | 4 + Documentation/sysctl/kernel.txt | 14 + arch/Kconfig | 7 + arch/arm/Kconfig | 118 +- arch/arm/Makefile | 1 + arch/arm/boot/compressed/head.S | 2 +- arch/arm/boot/compressed/misc.c | 2 +- arch/arm/common/Makefile | 1 + arch/arm/common/cpaccess.c | 253 +++ arch/arm/include/asm/cacheflush.h | 30 +- arch/arm/include/asm/cputype.h | 6 + arch/arm/include/asm/delay.h | 5 +- arch/arm/include/asm/dma-mapping.h | 92 + arch/arm/include/asm/domain.h | 13 + arch/arm/include/asm/entry-macro-multi.S | 14 +- arch/arm/include/asm/hardware/cache-l2x0.h | 5 + arch/arm/include/asm/hardware/gic.h | 3 + arch/arm/include/asm/io.h | 2 + arch/arm/include/asm/irq.h | 1 + arch/arm/include/asm/mach/flash.h | 2 + arch/arm/include/asm/mach/map.h | 12 +- arch/arm/include/asm/mach/mmc.h | 126 +- arch/arm/include/asm/memory.h | 7 + arch/arm/include/asm/mutex.h | 8 + arch/arm/include/asm/outercache.h | 1 + arch/arm/include/asm/page.h | 5 + arch/arm/include/asm/perf_event.h | 3 + arch/arm/include/asm/perftypes.h | 48 + arch/arm/include/asm/pgtable.h | 14 +- arch/arm/include/asm/pmu.h | 1 + arch/arm/include/asm/processor.h | 2 + arch/arm/include/asm/remote_spinlock.h | 34 + arch/arm/include/asm/rwsem.h | 5 - arch/arm/include/asm/setup.h | 13 + arch/arm/include/asm/spinlock.h | 137 ++ arch/arm/include/asm/system.h | 2 + arch/arm/include/asm/tlbflush.h | 6 +- arch/arm/kernel/armksyms.c | 4 - arch/arm/kernel/debug.S | 2 +- arch/arm/kernel/entry-armv.S | 8 + arch/arm/kernel/head.S | 8 + arch/arm/kernel/hw_breakpoint.c | 22 +- arch/arm/kernel/irq.c | 23 +- arch/arm/kernel/machine_kexec.c | 3 +- arch/arm/kernel/perf_event.c | 57 +- arch/arm/kernel/perf_event_msm.c | 710 +++++++ arch/arm/kernel/perf_event_msm_krait.c | 399 ++++ arch/arm/kernel/perf_event_msm_krait_l2.c | 649 ++++++ arch/arm/kernel/perf_event_msm_l2.c | 974 +++++++++ arch/arm/kernel/perf_event_v6.c | 2 +- arch/arm/kernel/perf_event_v7.c | 4 +- arch/arm/kernel/perf_event_xscale.c | 4 +- arch/arm/kernel/pmu.c | 32 +- arch/arm/kernel/setup.c | 6 + arch/arm/kernel/smp.c | 15 +- arch/arm/kernel/smp_twd.c | 3 +- arch/arm/kernel/traps.c | 4 + arch/arm/kernel/vmlinux.lds.S | 13 + arch/arm/lib/delay.c | 90 + arch/arm/mm/Kconfig | 35 +- arch/arm/mm/Makefile | 3 + arch/arm/mm/cache-fa.S | 6 +- arch/arm/mm/cache-l2x0.c | 172 +- arch/arm/mm/cache-v3.S | 29 +- arch/arm/mm/cache-v4.S | 29 +- arch/arm/mm/cache-v4wb.S | 6 +- arch/arm/mm/cache-v4wt.S | 15 +- arch/arm/mm/cache-v6.S | 7 +- arch/arm/mm/cache-v7.S | 6 +- arch/arm/mm/dma-mapping.c | 7 +- arch/arm/mm/emulate_domain_manager-v7.c | 345 ++++ arch/arm/mm/fault.c | 97 +- arch/arm/mm/init.c | 128 +- arch/arm/mm/ioremap.c | 8 +- arch/arm/mm/mm.h | 10 +- arch/arm/mm/mmu.c | 81 +- arch/arm/mm/proc-arm1020.S | 6 +- arch/arm/mm/proc-arm1020e.S | 6 +- arch/arm/mm/proc-arm1022.S | 6 +- arch/arm/mm/proc-arm1026.S | 6 +- arch/arm/mm/proc-arm920.S | 6 +- arch/arm/mm/proc-arm922.S | 6 +- arch/arm/mm/proc-arm925.S | 6 +- arch/arm/mm/proc-arm926.S | 6 +- arch/arm/mm/proc-arm940.S | 6 +- arch/arm/mm/proc-arm946.S | 6 +- arch/arm/mm/proc-feroceon.S | 12 +- arch/arm/mm/proc-mohawk.S | 6 +- arch/arm/mm/proc-v7.S | 48 +- arch/arm/mm/proc-xsc3.S | 6 +- arch/arm/mm/proc-xscale.S | 6 +- arch/arm/mm/tlb-v7.S | 12 + arch/arm/mm/vcm.c | 1830 +++++++++++++++++ arch/arm/mm/vcm_alloc.c | 557 +++++ arch/arm/mm/vcm_mm.c | 253 +++ arch/arm/oprofile/common.c | 6 + arch/arm/perfmon/Makefile | 6 + arch/arm/perfmon/cp15_registers.h | 109 + arch/arm/perfmon/l2_cp15_registers.h | 103 + arch/arm/perfmon/mcrmrc.h | 101 + arch/arm/perfmon/per-axi.c | 759 +++++++ arch/arm/perfmon/per-axi.h | 91 + arch/arm/perfmon/per-process-perf.c | 1251 +++++++++++ arch/arm/perfmon/per.c | 59 + arch/arm/perfmon/perf-function-hooks.c | 81 + arch/arm/perfmon/perf-smp.c | 751 +++++++ arch/arm/perfmon/perf-v7.c | 1009 +++++++++ arch/arm/perfmon/perf.h | 101 + arch/arm/tools/mach-types | 19 + 112 files changed, 12440 insertions(+), 214 deletions(-) create mode 100644 Documentation/arm/msm/boot.txt create mode 100644 Documentation/arm/msm/emulate_domain_manager.txt create mode 100644 Documentation/arm/msm/kgsl-sysfs.txt create mode 100644 arch/arm/common/cpaccess.c create mode 100644 arch/arm/include/asm/perftypes.h create mode 100644 arch/arm/include/asm/remote_spinlock.h create mode 100644 arch/arm/kernel/perf_event_msm.c create mode 100644 arch/arm/kernel/perf_event_msm_krait.c create mode 100644 arch/arm/kernel/perf_event_msm_krait_l2.c create mode 100644 arch/arm/kernel/perf_event_msm_l2.c create mode 100644 arch/arm/lib/delay.c create mode 100644 arch/arm/mm/emulate_domain_manager-v7.c create mode 100644 arch/arm/mm/vcm.c create mode 100644 arch/arm/mm/vcm_alloc.c create mode 100644 arch/arm/mm/vcm_mm.c create mode 100644 arch/arm/perfmon/Makefile create mode 100644 arch/arm/perfmon/cp15_registers.h create mode 100644 arch/arm/perfmon/l2_cp15_registers.h create mode 100644 arch/arm/perfmon/mcrmrc.h create mode 100644 arch/arm/perfmon/per-axi.c create mode 100644 arch/arm/perfmon/per-axi.h create mode 100644 arch/arm/perfmon/per-process-perf.c create mode 100644 arch/arm/perfmon/per.c create mode 100644 arch/arm/perfmon/perf-function-hooks.c create mode 100644 arch/arm/perfmon/perf-smp.c create mode 100644 arch/arm/perfmon/perf-v7.c create mode 100644 arch/arm/perfmon/perf.h diff --git a/Documentation/arm/msm/boot.txt b/Documentation/arm/msm/boot.txt new file mode 100644 index 0000000000000..1a41cd5320207 --- /dev/null +++ b/Documentation/arm/msm/boot.txt @@ -0,0 +1,23 @@ +Introduction +============= +The power management integrated circuit (PMIC) records the reason the +Application processor was powered on in Shared Memory. +The hardware and software used is the shared memory interface. This document +is not for the purpose of describing this interface, but to identify the +possible values for this data item. + +Description +=========== +Shared memory item (SMEM_POWER_ON_STATUS_INFO) is read to get access to +this data. The table below identifies the possible values stored. + +power_on_status values set by the PMIC for power on event: +---------------------------------------------------------- +0x01 -- keyboard power on +0x02 -- RTC alarm +0x04 -- cable power on +0x08 -- SMPL +0x10 -- Watch Dog timeout +0x20 -- USB charger +0x40 -- Wall charger +0xFF -- error reading power_on_status value diff --git a/Documentation/arm/msm/emulate_domain_manager.txt b/Documentation/arm/msm/emulate_domain_manager.txt new file mode 100644 index 0000000000000..97a25663df64a --- /dev/null +++ b/Documentation/arm/msm/emulate_domain_manager.txt @@ -0,0 +1,282 @@ +Copyright (c) 2009, Code Aurora Forum. All rights reserved. + +Redistribution and use in source form and compiled forms (SGML, HTML, PDF, +PostScript, RTF and so forth) with or without modification, are permitted +provided that the following conditions are met: + +Redistributions in source form must retain the above copyright notice, this +list of conditions and the following disclaimer as the first lines of this +file unmodified. + +Redistributions in compiled form (transformed to other DTDs, converted to +PDF, PostScript, RTF and other formats) must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS DOCUMENTATION IS PROVIDED BY THE CODE AURORA FORUM "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +AND NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD +DOCUMENTATION PROJECT BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS DOCUMENTATION, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Introduction +============ + +8x50 chipset requires the ability to disable HW domain manager function. + +The ARM MMU architecture has a feature known as domain manager mode. +Briefly each page table, section, or supersection is assigned a domain. +Each domain can be globally configured to NoAccess, Client, or Manager +mode. These global configurations allow the access permissions of the +entire domain to be changed simultaneously. + +The domain manger emulation is required to fix a HW problem on the 8x50 +chipset. The problem is simple to repair except when domain manager mode +is enabled. The emulation allows the problem to be completely resolved. + + +Hardware description +==================== + +When domain manager mode is enabled on a specific domain, the MMU +hardware ignores the access permission bits and the execute never bit. All +accesses, to memory in the domain, are granted full read, write, +execute permissions. + +The mode of each domain is controlled by a field in the cp15 dacr register. +Each domain can be globally configured to NoAccess, Client, or Manager mode. + +See: ARMv7 Architecture Reference Manual + + +Software description +==================== + +In order to disable domain manager mode the equivalent HW functionality must +be emulated in SW. Any attempts to enable domain manager mode, must be +intercepted. + +Because domain manager mode is not enabled, permissions for the +associated domain will remain restricted. Permission faults will be generated. +The permission faults will be intercepted. The faulted pages/sections will +be modified to grant full access and execute permissions. + +The modified page tables must be restored when exiting domain manager mode. + + +Design +====== + +Design Goals: + +Disable Domain Manager Mode +Exact SW emulation of Domain Manager Mode +Minimal Kernel changes +Minimal Security Risk + +Design Decisions: + +Detect kernel page table modifications on restore +Direct ARMv7 HW MMU table manipulation +Restore emulation modified MMU entries on context switch +No need to restore MMU entries for MMU entry copy operations +Invalidate TLB entries on modification +Store Domain Manager bits in memory +8 entry MMU entry cache +Use spin_lock_irqsave to protect domain manipulation +Assume no split MMU table + +Design Discussion: + +Detect kernel page table modifications on restore - +When restoring original page/section permission faults, the submitted design +verifies the MMU entry has not been modified. The kernel modifies MMU +entries for the following purposes : create a memory mapping, release a +memory mapping, add permissions during a permission fault, and map a page +during a translation fault. The submitted design works with the listed +scenarios. The translation fault and permission faults simply do not happen on +relevant entries (valid entries with full access permissions). The alternative +would be to hook every MMU table modification. The alternative greatly +increases complexity and code maintenance issues. + +Direct ARMv7 HW MMU table manipulation - +The natural choice would be to use the kernel provided mechanism to manipulate +MMU page table entries. The ARM MMU interface is described in pgtable.h. +This interface is complicated by the Linux implementation. The level 1 pgd +entries are treated and manipulated as entry pairs. The level 2 entries are +shadowed and cloned. The compromise was chosen to actually use the ARMv7 HW +registers to walk and modify the MMU table entries. The choice limits the +usage of this implementation to ARMv7 and similar ARM MMU architectures. Since +this implementation is targeted at fixing an issue in 8x50 ARMv7, the choice is +logical. The HW manipulation is in distinct low level functions. These could +easily be replaced or generalized to support other architectures as necessary. + +Restore emulation modified MMU entries on context switch - +This additional hook was added to minimize performance impact. By guaranteeing +the ASID will not change during the emulation, the emulation may invalidate each +entry by MVA & ASID. Only the affected page table entries will be removed from +the TLB cache. The performance cost of the invalidate on context switch is near +zero. Typically on context switch the domain mode would also change, forcing a +complete restore of all modified MMU entries. The alternative would be to +invalidate the entire TLB every time a table entry is restored. + +No need to restore MMU entries for copy operations - +Operations which copy MMU entries are relatively rare in the kernel. Because +we modify the level 2 pte entries directly in hardware, the Linux shadow copies +are left untouched. The kernel treats the shadow copies as the primary pte +entry. Any pte copy operations would be unaffected by the HW modification. +On translation section fault, pgd entries are copied from the kernel master +page table to the current thread page table. Since we restore MMU entries on +context switch, we guarantee the master table will not contain modifications, +while faulting on a process local entry. Other read, modify write operations +occur during permission fault handling. Since we open permission on modified +entries, these do not need to be restored, because we guarantee these +permission fault operations will not happen. + +Invalidate TLB entries on modification - +No real choice here. This is more of a design requirement. On permission +fault, the MMU entry with restricted permissions will be in the TLB. To open +access permissions, the TLB entry must be invalidated. Otherwise the access +will permission fault again. Upon restoring original MMU entries, the TLB +must be invalidated to restrict memory access. + +Store Domain Manager bits in memory - +There was only one alternative here. 2.6.29 kernel only uses 3 of 16 +possible domains. Additional bits in dacr could be used to store the +manager bits. This would allow faster access to the manager bits. +Overall this would reduce any performance impact. The performance +needs did not seem to justify the added weirdness. + +8 entry MMU entry cache- +The size of the modified MMU entry cache is somewhat arbitrary. The thought +process is that typically, a thread is using two pointers to perform a copy +operation. In this case only 2 entries would be required. One could imagine +a more complicated operation, a masked copy for instance, which would require +more pointers. 8 pointer seemed to be large enough to minimize risk of +permission fault thrashing. The disadvantage of a larger cache would simply +be a longer list of entries to restore. + +Use spin_lock_irqsave to protect domain manipulation - +The obvious choice. + +Assume no split MMU table - +This same assumption is documented in cpu_v7_switch_mm. + + +Power Management +================ + +Not affected. + + +SMP/multi-core +============== + +SMP/multicore not supported. This is intended as a 8x50 workaround. + + +Security +======== + +MMU page/section permissions must be manipulated correctly to emulate domain +manager mode. If page permission are left in full access mode, any process +can read associated memory. + + +Performance +=========== + +Performance should be impacted only minimally. When emulating domain manager +mode, there is overhead added to MMU table/context switches, set_domain() +calls, data aborts, and prefetch aborts. + +Normally the kernel operates with domain != DOMAIN_MANAGER. In this case the +overhead is minimal. An additional check is required to see if domain manager +mode is on. This minimal code is added to each of emulation entry points : +set, data abort, prefetch abort, and MMU table/context switch. + +Initial accesses to a MMU protected page/section will generate a permission +fault. The page will be manipulated to grant full access permissions and +the access will be retried. This will typically require 2-3 page table +walks. + +On a context switch, all modified MMU entries will be restored. On thread +resume, additional accesses will be treated as initial accesses. + + +Interface +========= + +The emulation does not have clients. It is hooked to the kernel through a +small list of functions. + +void emulate_domain_manager_set(u32 domain); +int emulate_domain_manager_data_abort(u32 dfsr, u32 dfar); +int emulate_domain_manager_prefetch_abort(u32 ifsr, u32 ifar); +void emulate_domain_manager_switch_mm( + unsigned long pgd_phys, + struct mm_struct *mm, + void (*switch_mm)(unsigned long pgd_phys, struct mm_struct *)); + +emulate_domain_manager_set() is the set_domain handler. This replaces the +direct manipulation of CP15 dacr with a function call. This allows emulation +to prevent setting dacr manager bits. It also allows emulation to restore +page/section permissions when domain manger is disabled. + +emulate_domain_manager_data_abort() handles data aborts caused by domain +not being set in HW, and handles section/page manipulation. + +emulate_domain_manager_prefetch_abort() is the similar prefetch abort handler. + +emulate_domain_manager_switch_mm() handles MMU table and context switches. +This notifies the emulation that the MMU context is changing. Allowing the +emulation to restore page table entry permission before switching contexts. + + +Config options +============== + +This option is enable/disable by the EMULATE_DOMAIN_MANAGER_V7 option. + + +Dependencies +============ + +Implementation is for ARMv7, MMU, and !SMP. Targets solving issue for 8x50 +chipset. + + +User space utilities +==================== + +None + + +Other +===== + +Code is implemented in kernel/arch/arm/mm. + + +arch/arm/mm/emulate_domain_manager.c contains comments. No additional public +documentation available or planned. + + +Known issues +============ + +No intent to support SMP or non ARMv7 architectures + + +To do +===== + +None + diff --git a/Documentation/arm/msm/kgsl-sysfs.txt b/Documentation/arm/msm/kgsl-sysfs.txt new file mode 100644 index 0000000000000..c572312c43732 --- /dev/null +++ b/Documentation/arm/msm/kgsl-sysfs.txt @@ -0,0 +1,98 @@ +This document lists details for the device specific sysfs attributes +created by the KGSL GPU driver. + +- /sys/devices/platform/kgsl/vmalloc + The total amount of vmalloc()ed memory currently allocated by the driver + (in bytes) + +- /sys/devices/platform/kgsl/vmalloc_max + The maximum amount of vmalloc()ed memory allocated at any one + time by the driver since the system was booted (in bytes) + +- /sys/devices/platform/kgsl/coherent + The total amount of coherent DMA memory currently allocated by the driver + (in bytes) + +- /sys/devices/platform/kgsl/coherent_max + The maximum amount of coherent DMA memory allocated at any one + time by the driver since the system was booted (in bytes) + + +- /sys/devices/platform/kgsl/histogram + A histogram of the sizes of vmalloc allocations by the driver + since the system was booted. The allocations are grouped by the order + of the allocation size in pages. For example, order 0 are 1 page + allocations, order 1 are 2 page allocations, order 2 are 4 page allocations, + and so forth, up to order 16 (32768) pages. + +- /sys/devices/platform/kgsl/proc + This directory contains individual entries for each active rendering + process. Rendering instances are created for each unique process that + opens the GPU devices, and are named for the id of the creating process. + In the driver, memory allocations are owned by the process that allocates + them, and outstanding memory is garbage collected when the process closes + the device. + + - /sys/devices/platform/kgsl/proc/NN/vmalloc + The total amount of vmalloc memory currently allocated by the process + (in bytes) + + - /sys/devices/platform/kgsl/proc/NN/vmalloc_max + The maximum amount of vmalloc memory allocated at any one + time by the process since it was created (in bytes) + + - /sys/devices/platform/kgsl/proc/NN/exmem + The total amount of external memory devices currently mapped by the process + (in bytes). This includes PMEM, ASHMEM and external memory pointers from + userspace. + + - /sys/devices/platform/kgsl/proc/NN/exmem_max + The maximum amount of external memory devices allocated at any one + time by the process since it was created (in bytes). This includes PMEM, + ASHMEM and external memory pointers from userspace. + + - /sys/devices/platform/kgsl/proc/NN/flushes + The total number of cache flushes performed by this process since it + was created. + +- /sys/devices/platform/kgsl/pagetables + This directory contains individual entries for each active pagetable. + There will always be a global pagetable with ID 0. If per-process + pagetables are not enabled, pagetable ID 0 will also be the default + pagetable for all processes. If per-process pagetables are enabled, + there will be an entry for each pagetable, named after the ID of the + process that created it. + + - /sys/devices/platform/kgsl/pagetables/NN/entries + The number of concurrent entries mapped in the GPU MMU. + + - /sys/devices/platform/kgsl/pagetables/NN/mapped + The number of bytes currently mapped in the GPU MMU. + + - /sys/devices/platform/kgsl/pagetables/NN/va_range + The virtual address size of the MMU pagetable (in bytes). + + - /sys/devices/platform/kgsl/pagetables/NN/max_mapped + The maximum number of bytes concurrently mapped in the GPU MMU since + the pagetable was created. + + - /sys/devices/platform/kgsl/pagetables/NN/max_entries + The maximum number of entries concurrently mapped in the GPU MMU since + the pagetable was created. + +- /sys/devices/platform/kgsl/msm_kgsl/ + Each individual GPU device (2D or 3D) will have its own device node in + this directory. All platforms will have kgsl-3d0 (3D device), some + devices may have 1 2D device (kgsl-2d0) and others might add a second 2D + device (kgsl-2d1). + + - /sys/devices/platform/kgsl/msm_kgsl/kgsl-XXX/pwrnap + Controls the system ability to nap (lightly sleep between frames). 1 + indicates napping is enabled, 0 indicates it is disabled. Write a 1 or + a 0 to the file to control napping. + + - /sys/devices/platform/kgsl/msm_kgsl/kgsl-XXX/gpuclk + Shows the last active requested speed of the GPU clock in HZ, does not + actually measure the current clock rate. Write a clock speed to the file + corresponding to a supported platform power level to change to that power + level. The bandwidth vote will also be adjusted. diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index f4a04c0c7edca..89099cab0a82c 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2317,6 +2317,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. 1: Fast pin select (default) 2: ATC IRMode + snddev_icodec.msm_codec_i2s_slave_mode= [ARM-MSM] + 1, codec is I2S master + 0, MSM is I2S master (default) + softlockup_panic= [KNL] Should the soft-lockup detector generate panics. diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index 11d5ceda5bb06..c0561067b4bbf 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -21,6 +21,7 @@ show up in /proc/sys/kernel: - acct - bootloader_type [ X86 only ] - bootloader_version [ X86 only ] +- boot_reason [ ARM only ] - callhome [ S390 only ] - auto_msgmni - core_pattern @@ -126,6 +127,19 @@ Documentation/x86/boot.txt for additional information. ============================================================== +boot_reason: + +ARM -- reason for device boot + +A single bit will be set in the unsigned integer value to identify the +reason the device was booted / powered on. The value will be zero if this +feature is not supported on the ARM device being booted. + +See the power-on-status field definitions in +Documentation/arm/msm/boot.txt for Qualcomm's family of devices. + +============================================================== + callhome: Controls the kernel's callhome behavior in case of a kernel panic. diff --git a/arch/Kconfig b/arch/Kconfig index f78c2be4242b4..923a8cf3911b1 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -151,6 +151,13 @@ config HAVE_HW_BREAKPOINT bool depends on PERF_EVENTS +config HAVE_HW_BRKPT_RESERVED_RW_ACCESS + bool + depends on HAVE_HW_BREAKPOINT + help + Some of the hardware might not have r/w access beyond a certain number + of breakpoint register access. + config HAVE_MIXED_BREAKPOINTS_REGS bool depends on HAVE_HW_BREAKPOINT diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 8400724f4377c..10233cb550d67 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -62,7 +62,7 @@ config GENERIC_CLOCKEVENTS config GENERIC_CLOCKEVENTS_BROADCAST bool depends on GENERIC_CLOCKEVENTS - default y if SMP + default y if SMP && !LOCAL_TIMERS config HAVE_TCM bool @@ -127,9 +127,22 @@ config GENERIC_IRQ_PROBE config GENERIC_LOCKBREAK bool - default y + help + Enable generic lock breaking on preemptible SMP platforms. + Some platforms may be susceptible to livelock with this + item enabled. + default n depends on SMP && PREEMPT +config ARM_TICKET_LOCKS + bool + help + Enable ticket locks, which help preserve fairness among + contended locks and prevent livelock in multicore systems. + Say 'y' if system stability is important. + default y if ARCH_MSM_SCORPIONMP + depends on SMP + config RWSEM_GENERIC_SPINLOCK bool @@ -615,18 +628,23 @@ config ARCH_PXA Support for Intel/Marvell's PXA2xx/PXA3xx processor line. config ARCH_MSM - bool "Qualcomm MSM" + bool "Qualcomm MSM/QSD" select ARCH_HAS_CPUFREQ select ARCH_REQUIRE_GPIOLIB select HAVE_CLK select GENERIC_GPIO +# select GENERIC_TIME select GENERIC_CLOCKEVENTS select ARCH_REQUIRE_GPIOLIB +# select GENERIC_ALLOCATOR +# select ARCH_WANT_OPTIONAL_GPIOLIB +# select CLKDEV_LOOKUP +# select HAVE_SCHED_CLOCK help Support for Qualcomm MSM/QSD based systems. This runs on the - apps processor of the MSM/QSD and depends on a shared memory - interface to the modem processor which runs the baseband - stack and controls some vital subsystems + ARM11/Scorpion apps processor of the MSM/QSD and depends on a + shared memory interface to the ARM9 modem processor which + runs the baseband stack and controls some vital subsystems (clock and power control, etc). config ARCH_SHMOBILE @@ -1036,6 +1054,15 @@ config ARM_TIMER_SP804 source arch/arm/mm/Kconfig +config RESERVE_FIRST_PAGE + bool + default n + help + Reserve the first page at PHYS_OFFSET. The first + physical page is used by many platforms for warm + boot operations. Reserve this page so that it is + not allocated by the kernel. + config IWMMXT bool "Enable iWMMXt support" depends on CPU_XSCALE || CPU_XSC3 || CPU_MOHAWK || CPU_PJ4 @@ -1143,7 +1170,7 @@ config ARM_ERRATA_742231 config PL310_ERRATA_588369 bool "Clean & Invalidate maintenance operations do not invalidate clean lines" - depends on CACHE_L2X0 && ARCH_OMAP4 + depends on CACHE_L2X0 help The PL310 L2 cache controller implements three types of Clean & Invalidate maintenance operations: by Physical Address @@ -1152,8 +1179,7 @@ config PL310_ERRATA_588369 clean operation followed immediately by an invalidate operation, both performing to the same memory location. This functionality is not correctly implemented in PL310 as clean lines are not - invalidated as a result of these operations. Note that this errata - uses Texas Instrument's secure monitor api. + invalidated as a result of these operations. config ARM_ERRATA_720789 bool "ARM errata: TLBIASIDIS and TLBIMVAIS operations can broadcast a faulty ASID" @@ -1205,6 +1231,30 @@ config ARM_ERRATA_753970 This has the same effect as the cache sync operation: store buffer drain and waiting for all buffers empty. +config PL310_ERRATA_727915 + bool "Background Clean & Invalidate by Way operation can cause data corruption" + depends on CACHE_L2X0 + help + PL310 implements the Clean & Invalidate by Way L2 cache maintenance + operation (offset 0x7FC). This operation runs in background so that + PL310 can handle normal accesses while it is in progress. Under very + rare circumstances, due to this erratum, write data can be lost when + PL310 treats a cacheable write transaction during a Clean & + Invalidate by Way operation. + +config KSAPI + tristate "KSAPI support (EXPERIMENTAL)" + depends on ARCH_MSM_SCORPION || ARCH_MSM_KRAIT + default n + help + KSAPI: Performance monitoring tool for linux. + KSAPI records performance statistics for Snapdragon linux platform. + It uses the /proc FS as a means to exchange configuration data and + counter statistics. It can monitor the counter statistics for + Scorpion processor supported hardware performance counters on a per + thread basis or AXI counters on an overall system basis. + + endmenu source "arch/arm/common/Kconfig" @@ -1282,9 +1332,9 @@ config SMP depends on REALVIEW_EB_ARM11MP || REALVIEW_EB_A9MP || \ MACH_REALVIEW_PB11MP || MACH_REALVIEW_PBX || ARCH_OMAP4 || \ ARCH_S5PV310 || ARCH_TEGRA || ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || \ - ARCH_MSM_SCORPIONMP || ARCH_SHMOBILE + MSM_SMP || ARCH_SHMOBILE select USE_GENERIC_SMP_HELPERS - select HAVE_ARM_SCU if !ARCH_MSM_SCORPIONMP + select HAVE_ARM_SCU if !MSM_SMP help This enables support for systems with more than one CPU. If you have a system with only one CPU, like most personal computers, say N. If @@ -1360,16 +1410,17 @@ config NR_CPUS config HOTPLUG_CPU bool "Support for hot-pluggable CPUs (EXPERIMENTAL)" depends on SMP && HOTPLUG && EXPERIMENTAL - depends on !ARCH_MSM help Say Y here to experiment with turning CPUs off and on. CPUs can be controlled through /sys/devices/system/cpu. config LOCAL_TIMERS bool "Use local timer interrupts" - depends on SMP + depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || \ + REALVIEW_EB_A9MP || MACH_REALVIEW_PBX || ARCH_OMAP4 || ARCH_U8500 || \ + MSM_SMP) default y - select HAVE_ARM_TWD if !ARCH_MSM_SCORPIONMP + select HAVE_ARM_TWD if (ARCH_REALVIEW || ARCH_OMAP4 || ARCH_U8500) help Enable support for local timers on SMP platforms, rather then the legacy IPI broadcast method. Local timers allows the system @@ -1485,6 +1536,30 @@ config VMALLOC_RESERVE source "mm/Kconfig" +config ARCH_MEMORY_PROBE + def_bool n + depends on MEMORY_HOTPLUG + +config ARCH_MEMORY_REMOVE + def_bool n + depends on MEMORY_HOTPLUG + +config ARCH_POPULATES_NODE_MAP + def_bool n + depends on MEMORY_HOTPLUG + +config DONT_RESERVE_FROM_MOVABLE_ZONE + def_bool y + depends on MEMORY_HOTPLUG + +config DONT_MAP_HOLE_AFTER_MEMBANK0 + def_bool n + depends on SPARSEMEM + +config FIX_MOVABLE_ZONE + def_bool y + depends on MEMORY_HOTPLUG + config FORCE_MAX_ZONEORDER int "Maximum zone order" if ARCH_SHMOBILE range 11 64 if ARCH_SHMOBILE @@ -1617,6 +1692,17 @@ config DEPRECATED_PARAM_STRUCT This was deprecated in 2001 and announced to live on for 5 years. Some old boot loaders still use this way. +config CP_ACCESS + tristate "CP register access tool" + default n + help + Provide support for Coprocessor register access using /sys + interface. Read and write to CP registers from userspace + through sysfs interface. A sys file (cp_rw) will be created under + /sys/devices/system/cpaccess/cpaccess0. + + If unsure, say N. + endmenu menu "Boot options" @@ -1841,6 +1927,8 @@ endif source "drivers/cpuidle/Kconfig" +endmenu + config CPU_FREQ_MSM bool depends on CPU_FREQ && ARCH_MSM @@ -1849,8 +1937,6 @@ config CPU_FREQ_MSM This enables the CPUFreq driver for Qualcomm CPUs. If in doubt, say Y. -endmenu - menu "Floating point emulation" comment "At least one emulation must be selected" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 6f7b29294c80f..5c969699e7dad 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -257,6 +257,7 @@ core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/ core-y += $(machdirs) $(platdirs) drivers-$(CONFIG_OPROFILE) += arch/arm/oprofile/ +core-y += arch/arm/perfmon/ libs-y := arch/arm/lib/ $(libs-y) diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index 48cbc0c50116a..eb7ba6a20acf5 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -21,7 +21,7 @@ #if defined(CONFIG_DEBUG_ICEDCC) -#ifdef CONFIG_CPU_V6 +#ifdef defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_V7) .macro loadsp, rb, tmp .endm .macro writeb, ch, rb diff --git a/arch/arm/boot/compressed/misc.c b/arch/arm/boot/compressed/misc.c index e653a6d3c8d90..253ecc88a7bb9 100644 --- a/arch/arm/boot/compressed/misc.c +++ b/arch/arm/boot/compressed/misc.c @@ -36,7 +36,7 @@ extern void error(char *x); #ifdef CONFIG_DEBUG_ICEDCC -#ifdef CONFIG_CPU_V6 +#if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_V7) static void icedcc_putc(int ch) { diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index ad8d13ddeb947..ce8a4f2315922 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_ARCH_IXP2000) += uengine.o obj-$(CONFIG_ARCH_IXP23XX) += uengine.o obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_COMMON_CLKDEV) += clkdev.o +obj-$(CONFIG_CP_ACCESS) += cpaccess.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o diff --git a/arch/arm/common/cpaccess.c b/arch/arm/common/cpaccess.c new file mode 100644 index 0000000000000..241e33953927d --- /dev/null +++ b/arch/arm/common/cpaccess.c @@ -0,0 +1,253 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * CP parameters + */ +struct cp_params { + unsigned long cp; + unsigned long op1; + unsigned long op2; + unsigned long crn; + unsigned long crm; + unsigned long write_value; + char rw; +}; + +static struct semaphore cp_sem; +static int cpu; + +static DEFINE_PER_CPU(struct cp_params, cp_param) + = { 15, 0, 0, 0, 0, 0, 'r' }; + +static struct sysdev_class cpaccess_sysclass = { + .name = "cpaccess", +}; + +/* + * get_asm_value - Dummy fuction + * @write_val: Write value incase of a CP register write operation. + * + * This function is just a placeholder. The first 2 instructions + * will be inserted to perform MRC/MCR instruction and a return. + * See do_cpregister_rw function. Value passed to function is + * accessed from r0 register. + */ +static noinline unsigned long cpaccess_dummy(unsigned long write_val) +{ + asm("mrc p15, 0, r0, c0, c0, 0\n\t"); + asm("bx lr\n\t"); + return 0xBEEF; +} __attribute__((aligned(32))) + +/* + * get_asm_value - Read/Write CP registers + * @ret: Pointer to return value in case of CP register + * read op. + * + */ +static void get_asm_value(void *ret) +{ + *(unsigned long *)ret = + cpaccess_dummy(per_cpu(cp_param.write_value, cpu)); +} + +/* + * dp_cpregister_rw - Read/Write CP registers + * @write: 1 for Write and 0 for Read operation + * + * Returns value read from CP register + */ +static unsigned long do_cpregister_rw(int write) +{ + unsigned long opcode, ret, *p_opcode; + + /* + * Mask the crn, crm, op1, op2 and cp values so they do not + * interfer with other fields of the op code. + */ + per_cpu(cp_param.cp, cpu) &= 0xF; + per_cpu(cp_param.crn, cpu) &= 0xF; + per_cpu(cp_param.crm, cpu) &= 0xF; + per_cpu(cp_param.op1, cpu) &= 0x7; + per_cpu(cp_param.op2, cpu) &= 0x7; + + /* + * Base MRC opcode for MIDR is EE100010, + * MCR is 0xEE000010 + */ + opcode = (write == 1 ? 0xEE000010 : 0xEE100010); + opcode |= (per_cpu(cp_param.crn, cpu)<<16) | + (per_cpu(cp_param.crm, cpu)<<0) | + (per_cpu(cp_param.op1, cpu)<<21) | + (per_cpu(cp_param.op2, cpu)<<5) | + (per_cpu(cp_param.cp, cpu) << 8); + + /* + * Grab address of the Dummy function, insert MRC/MCR + * instruction and a return instruction ("bx lr"). Do + * a D cache clean and I cache invalidate after inserting + * new code. + */ + p_opcode = (unsigned long *)&cpaccess_dummy; + *p_opcode++ = opcode; + *p_opcode-- = 0xE12FFF1E; + __cpuc_coherent_kern_range((unsigned long)p_opcode, + ((unsigned long)p_opcode + (sizeof(long) * 2))); + +#ifdef CONFIG_SMP + /* + * Use smp_call_function_single to do CPU core specific + * get_asm_value function call. + */ + if (smp_call_function_single(cpu, get_asm_value, &ret, 1)) + printk(KERN_ERR "Error cpaccess smp call single\n"); +#else + get_asm_value(&ret); +#endif + + return ret; +} + +/* + * cp_register_write_sysfs - sysfs interface for writing to + * CP register + * @dev: sys device + * @attr: device attribute + * @buf: write value + * @cnt: not used + * + */ +static ssize_t cp_register_write_sysfs(struct sys_device *dev, + struct sysdev_attribute *attr, const char *buf, size_t cnt) +{ + unsigned long op1, op2, crn, crm, cp = 15, write_value, ret; + char rw; + if (down_timeout(&cp_sem, 6000)) + return -ERESTARTSYS; + + sscanf(buf, "%lu:%lu:%lu:%lu:%lu:%c:%lx:%d", &cp, &op1, &crn, + &crm, &op2, &rw, &write_value, &cpu); + per_cpu(cp_param.cp, cpu) = cp; + per_cpu(cp_param.op1, cpu) = op1; + per_cpu(cp_param.crn, cpu) = crn; + per_cpu(cp_param.crm, cpu) = crm; + per_cpu(cp_param.op2, cpu) = op2; + per_cpu(cp_param.rw, cpu) = rw; + per_cpu(cp_param.write_value, cpu) = write_value; + + if (per_cpu(cp_param.rw, cpu) == 'w') { + do_cpregister_rw(1); + ret = cnt; + } + + if ((per_cpu(cp_param.rw, cpu) != 'w') && + (per_cpu(cp_param.rw, cpu) != 'r')) { + ret = -1; + printk(KERN_INFO "Wrong Entry for 'r' or 'w'. \ + Use cp:op1:crn:crm:op2:r/w:write_value.\n"); + } + + return cnt; +} + +/* + * cp_register_read_sysfs - sysfs interface for reading CP registers + * @dev: sys device + * @attr: device attribute + * @buf: write value + * + * Code to read in the CPxx crn, crm, op1, op2 variables, or into + * the base MRC opcode, store to executable memory, clean/invalidate + * caches and then execute the new instruction and provide the + * result to the caller. + */ +static ssize_t cp_register_read_sysfs(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) +{ + int ret; + ret = sprintf(buf, "%lx\n", do_cpregister_rw(0)); + + if (cp_sem.count <= 0) + up(&cp_sem); + + return ret; +} + +/* + * Setup sysfs files + */ +SYSDEV_ATTR(cp_rw, 0644, cp_register_read_sysfs, + cp_register_write_sysfs); + +static struct sys_device device_cpaccess = { + .id = 0, + .cls = &cpaccess_sysclass, +}; + +/* + * init_cpaccess_sysfs - initialize sys devices + */ +static int __init init_cpaccess_sysfs(void) +{ + int error = sysdev_class_register(&cpaccess_sysclass); + + if (!error) + error = sysdev_register(&device_cpaccess); + else + printk(KERN_ERR "Error initializing cpaccess \ + interface\n"); + + if (!error) + error = sysdev_create_file(&device_cpaccess, + &attr_cp_rw); + else { + printk(KERN_ERR "Error initializing cpaccess \ + interface\n"); + sysdev_unregister(&device_cpaccess); + sysdev_class_unregister(&cpaccess_sysclass); + } + + sema_init(&cp_sem, 1); + + return error; +} + +static void __exit exit_cpaccess_sysfs(void) +{ + sysdev_remove_file(&device_cpaccess, &attr_cp_rw); + sysdev_unregister(&device_cpaccess); + sysdev_class_unregister(&cpaccess_sysclass); +} + +module_init(init_cpaccess_sysfs); +module_exit(exit_cpaccess_sysfs); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h index f572b36cf7beb..994e59b0c8f0f 100644 --- a/arch/arm/include/asm/cacheflush.h +++ b/arch/arm/include/asm/cacheflush.h @@ -204,20 +204,20 @@ * DMA Cache Coherency * =================== * - * dma_inv_range(start, end) + * dma_inv_range(start, end) * - * Invalidate (discard) the specified virtual address range. - * May not write back any entries. If 'start' or 'end' - * are not cache line aligned, those lines must be written - * back. - * - start - virtual start address - * - end - virtual end address + * Invalidate (discard) the specified virtual address range. + * May not write back any entries. If 'start' or 'end' + * are not cache line aligned, those lines must be written + * back. + * - start - virtual start address + * - end - virtual end address * - * dma_clean_range(start, end) + * dma_clean_range(start, end) * - * Clean (write back) the specified virtual address range. - * - start - virtual start address - * - end - virtual end address + * Clean (write back) the specified virtual address range. + * - start - virtual start address + * - end - virtual end address * * dma_flush_range(start, end) * @@ -267,8 +267,8 @@ extern struct cpu_cache_fns cpu_cache; */ #define dmac_map_area cpu_cache.dma_map_area #define dmac_unmap_area cpu_cache.dma_unmap_area -#define dmac_inv_range cpu_cache.dma_inv_range -#define dmac_clean_range cpu_cache.dma_clean_range +#define dmac_inv_range cpu_cache.dma_inv_range +#define dmac_clean_range cpu_cache.dma_clean_range #define dmac_flush_range cpu_cache.dma_flush_range #else @@ -297,8 +297,8 @@ extern void __cpuc_flush_dcache_area(void *, size_t); */ #define dmac_map_area __glue(_CACHE,_dma_map_area) #define dmac_unmap_area __glue(_CACHE,_dma_unmap_area) -#define dmac_inv_range __glue(_CACHE,_dma_inv_range) -#define dmac_clean_range __glue(_CACHE,_dma_clean_range) +#define dmac_inv_range __glue(_CACHE,_dma_inv_range) +#define dmac_clean_range __glue(_CACHE,_dma_clean_range) #define dmac_flush_range __glue(_CACHE,_dma_flush_range) extern void dmac_map_area(const void *, size_t, int); diff --git a/arch/arm/include/asm/cputype.h b/arch/arm/include/asm/cputype.h index 20ae96cc0020e..8d056b865962c 100644 --- a/arch/arm/include/asm/cputype.h +++ b/arch/arm/include/asm/cputype.h @@ -7,6 +7,7 @@ #define CPUID_CACHETYPE 1 #define CPUID_TCM 2 #define CPUID_TLBTYPE 3 +#define CPUID_MPIDR 5 #define CPUID_EXT_PFR0 "c1, 0" #define CPUID_EXT_PFR1 "c1, 1" @@ -68,6 +69,11 @@ static inline unsigned int __attribute_const__ read_cpuid_tcmstatus(void) return read_cpuid(CPUID_TCM); } +static inline unsigned int __attribute_const__ read_cpuid_mpidr(void) +{ + return read_cpuid(CPUID_MPIDR); +} + /* * Intel's XScale3 core supports some v6 features (supersections, L2) * but advertises itself as v5 as it does not support the v6 ISA. For diff --git a/arch/arm/include/asm/delay.h b/arch/arm/include/asm/delay.h index b2deda1815496..5c6b9a3c5df52 100644 --- a/arch/arm/include/asm/delay.h +++ b/arch/arm/include/asm/delay.h @@ -8,7 +8,7 @@ #include /* HZ */ -extern void __delay(int loops); +extern void __delay(unsigned long loops); /* * This function intentionally does not exist; if you see references to @@ -40,5 +40,8 @@ extern void __const_udelay(unsigned long); __const_udelay((n) * ((2199023U*HZ)>>11))) : \ __udelay(n)) +extern void set_delay_fn(void (*fn)(unsigned long)); +extern void read_current_timer_delay_loop(unsigned long loops); + #endif /* defined(_ARM_DELAY_H) */ diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index 4fff837363edd..6f48921b08d61 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -172,6 +172,46 @@ static inline void dma_free_noncoherent(struct device *dev, size_t size, { } + +/* + * dma_coherent_pre_ops - barrier functions for coherent memory before DMA. + * A barrier is required to ensure memory operations are complete before the + * initiation of a DMA xfer. + * If the coherent memory is Strongly Ordered + * - pre ARMv7 and 8x50 guarantees ordering wrt other mem accesses + * - ARMv7 guarantees ordering only within a 1KB block, so we need a barrier + * If coherent memory is normal then we need a barrier to prevent + * reordering + */ +static inline void dma_coherent_pre_ops(void) +{ +#if COHERENT_IS_NORMAL == 1 + dmb(); +#else + if (arch_is_coherent()) + dmb(); + else + barrier(); +#endif +} +/* + * dma_post_coherent_ops - barrier functions for coherent memory after DMA. + * If the coherent memory is Strongly Ordered we dont need a barrier since + * there are no speculative fetches to Strongly Ordered memory. + * If coherent memory is normal then we need a barrier to prevent reordering + */ +static inline void dma_coherent_post_ops(void) +{ +#if COHERENT_IS_NORMAL == 1 + dmb(); +#else + if (arch_is_coherent()) + dmb(); + else + barrier(); +#endif +} + /** * dma_alloc_coherent - allocate consistent memory for DMA * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices @@ -385,6 +425,58 @@ static inline dma_addr_t dma_map_single(struct device *dev, void *cpu_addr, return addr; } +/** + * dma_cache_pre_ops - clean or invalidate cache before dma transfer is + * initiated and perform a barrier operation. + * @virtual_addr: A kernel logical or kernel virtual address + * @size: size of buffer to map + * @dir: DMA transfer direction + * + * Ensure that any data held in the cache is appropriately discarded + * or written back. + * + */ +static inline void dma_cache_pre_ops(void *virtual_addr, + size_t size, enum dma_data_direction dir) +{ + extern void ___dma_single_cpu_to_dev(const void *, size_t, + enum dma_data_direction); + + BUG_ON(!valid_dma_direction(dir)); + + if (!arch_is_coherent()) + ___dma_single_cpu_to_dev(virtual_addr, size, dir); +} + +/** + * dma_cache_post_ops - clean or invalidate cache after dma transfer is + * initiated and perform a barrier operation. + * @virtual_addr: A kernel logical or kernel virtual address + * @size: size of buffer to map + * @dir: DMA transfer direction + * + * Ensure that any data held in the cache is appropriately discarded + * or written back. + * + */ +static inline void dma_cache_post_ops(void *virtual_addr, + size_t size, enum dma_data_direction dir) +{ + extern void ___dma_single_cpu_to_dev(const void *, size_t, + enum dma_data_direction); + + BUG_ON(!valid_dma_direction(dir)); + + if (arch_has_speculative_dfetch() && !arch_is_coherent() + && dir != DMA_TO_DEVICE) + /* + * Treat DMA_BIDIRECTIONAL and DMA_FROM_DEVICE + * identically: invalidate + */ + ___dma_single_cpu_to_dev(virtual_addr, + size, DMA_FROM_DEVICE); +} + /** * dma_map_page - map a portion of a page for streaming DMA * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices diff --git a/arch/arm/include/asm/domain.h b/arch/arm/include/asm/domain.h index 5c35625ff1b0a..0854849e4d969 100644 --- a/arch/arm/include/asm/domain.h +++ b/arch/arm/include/asm/domain.h @@ -2,6 +2,7 @@ * arch/arm/include/asm/domain.h * * Copyright (C) 1999 Russell King. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -61,6 +62,17 @@ #ifndef __ASSEMBLY__ #ifdef CONFIG_CPU_USE_DOMAINS +#ifdef CONFIG_EMULATE_DOMAIN_MANAGER_V7 +void emulate_domain_manager_set(u32 domain); +int emulate_domain_manager_data_abort(u32 dfsr, u32 dfar); +int emulate_domain_manager_prefetch_abort(u32 ifsr, u32 ifar); +void emulate_domain_manager_switch_mm( + unsigned long pgd_phys, + struct mm_struct *mm, + void (*switch_mm)(unsigned long pgd_phys, struct mm_struct *)); + +#define set_domain(x) emulate_domain_manager_set(x) +#else #define set_domain(x) \ do { \ __asm__ __volatile__( \ @@ -68,6 +80,7 @@ : : "r" (x)); \ isb(); \ } while (0) +#endif #define modify_domain(dom,type) \ do { \ diff --git a/arch/arm/include/asm/entry-macro-multi.S b/arch/arm/include/asm/entry-macro-multi.S index ec0bbf79c71fd..febdb7d4c5922 100644 --- a/arch/arm/include/asm/entry-macro-multi.S +++ b/arch/arm/include/asm/entry-macro-multi.S @@ -2,8 +2,8 @@ * Interrupt handling. Preserves r7, r8, r9 */ .macro arch_irq_handler_default - get_irqnr_preamble r5, lr -1: get_irqnr_and_base r0, r6, r5, lr + get_irqnr_preamble r6, lr +1: get_irqnr_and_base r0, r2, r6, lr movne r1, sp @ @ routine called with r0 = irq number, r1 = struct pt_regs * @@ -15,17 +15,17 @@ /* * XXX * - * this macro assumes that irqstat (r6) and base (r5) are + * this macro assumes that irqstat (r2) and base (r6) are * preserved from get_irqnr_and_base above */ - ALT_SMP(test_for_ipi r0, r6, r5, lr) + ALT_SMP(test_for_ipi r0, r2, r6, lr) ALT_UP_B(9997f) movne r1, sp adrne lr, BSYM(1b) bne do_IPI #ifdef CONFIG_LOCAL_TIMERS - test_for_ltirq r0, r6, r5, lr + test_for_ltirq r0, r2, r6, lr movne r0, sp adrne lr, BSYM(1b) bne do_local_timer @@ -38,7 +38,7 @@ .align 5 .global \symbol_name \symbol_name: - mov r4, lr + mov r8, lr arch_irq_handler_default - mov pc, r4 + mov pc, r8 .endm diff --git a/arch/arm/include/asm/hardware/cache-l2x0.h b/arch/arm/include/asm/hardware/cache-l2x0.h index 16bd48031583d..1fc2f49a9546d 100644 --- a/arch/arm/include/asm/hardware/cache-l2x0.h +++ b/arch/arm/include/asm/hardware/cache-l2x0.h @@ -59,6 +59,7 @@ /* Registers shifts and masks */ #define L2X0_CACHE_ID_PART_MASK (0xf << 6) #define L2X0_CACHE_ID_PART_L210 (1 << 6) +#define L2X0_CACHE_ID_PART_L220 (2 << 6) #define L2X0_CACHE_ID_PART_L310 (3 << 6) #define L2X0_AUX_CTRL_MASK 0xc0000fff @@ -71,9 +72,13 @@ #define L2X0_AUX_CTRL_DATA_PREFETCH_SHIFT 28 #define L2X0_AUX_CTRL_INSTR_PREFETCH_SHIFT 29 #define L2X0_AUX_CTRL_EARLY_BRESP_SHIFT 30 +#define L2X0_AUX_CTRL_EVNT_MON_BUS_EN_SHIFT 20 #ifndef __ASSEMBLY__ extern void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask); +extern void l2x0_suspend(void); +extern void l2x0_resume(int collapsed); +extern void l2x0_cache_sync(void); #endif #endif diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h index 84557d3210013..dd20b946d06e8 100644 --- a/arch/arm/include/asm/hardware/gic.h +++ b/arch/arm/include/asm/hardware/gic.h @@ -40,6 +40,9 @@ void gic_secondary_init(unsigned int); void gic_cascade_irq(unsigned int gic_nr, unsigned int irq); void gic_raise_softirq(const struct cpumask *mask, unsigned int irq); void gic_enable_ppi(unsigned int); +void gic_show_resume_irq(unsigned int gic_nr); +bool gic_is_spi_pending(unsigned int irq); +void gic_clear_spi_pending(unsigned int irq); #endif #endif diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h index d66605dea55a2..761c29e01eee9 100644 --- a/arch/arm/include/asm/io.h +++ b/arch/arm/include/asm/io.h @@ -248,6 +248,8 @@ extern void _memset_io(volatile void __iomem *, int, size_t); #define ioremap(cookie,size) __arch_ioremap((cookie), (size), MT_DEVICE) #define ioremap_nocache(cookie,size) __arch_ioremap((cookie), (size), MT_DEVICE) +#define ioremap_strongly_ordered(cookie, size) __arch_ioremap(cookie, size, \ + MT_DEVICE_STRONGLY_ORDERED) #define ioremap_cached(cookie,size) __arch_ioremap((cookie), (size), MT_DEVICE_CACHED) #define ioremap_wc(cookie,size) __arch_ioremap((cookie), (size), MT_DEVICE_WC) #define iounmap __arch_iounmap diff --git a/arch/arm/include/asm/irq.h b/arch/arm/include/asm/irq.h index 2721a5814cb93..5a526afb5f185 100644 --- a/arch/arm/include/asm/irq.h +++ b/arch/arm/include/asm/irq.h @@ -23,6 +23,7 @@ struct pt_regs; extern void migrate_irqs(void); extern void asm_do_IRQ(unsigned int, struct pt_regs *); +void handle_IRQ(unsigned int, struct pt_regs *); void init_IRQ(void); #endif diff --git a/arch/arm/include/asm/mach/flash.h b/arch/arm/include/asm/mach/flash.h index 4ca69fe2c850c..36938ea24a3dd 100644 --- a/arch/arm/include/asm/mach/flash.h +++ b/arch/arm/include/asm/mach/flash.h @@ -17,6 +17,7 @@ struct mtd_info; * map_name: the map probe function name * name: flash device name (eg, as used with mtdparts=) * width: width of mapped device + * interleave: interleave mode feature support * init: method called at driver/device initialisation * exit: method called at driver/device removal * set_vpp: method called to enable or disable VPP @@ -28,6 +29,7 @@ struct flash_platform_data { const char *map_name; const char *name; unsigned int width; + unsigned int interleave; int (*init)(void); void (*exit)(void); void (*set_vpp)(int on); diff --git a/arch/arm/include/asm/mach/map.h b/arch/arm/include/asm/mach/map.h index d2fedb5aeb1f3..a124dde6f4b9a 100644 --- a/arch/arm/include/asm/mach/map.h +++ b/arch/arm/include/asm/mach/map.h @@ -29,11 +29,21 @@ struct map_desc { #define MT_MEMORY_NONCACHED 11 #define MT_MEMORY_DTCM 12 #define MT_MEMORY_ITCM 13 +#define MT_DEVICE_STRONGLY_ORDERED 14 +#define MT_MEMORY_R 15 +#define MT_MEMORY_RW 16 +#define MT_MEMORY_RX 17 #ifdef CONFIG_MMU extern void iotable_init(struct map_desc *, int); -struct mem_type; +struct mem_type { + pteval_t prot_pte; + unsigned int prot_l1; + unsigned int prot_sect; + unsigned int domain; +}; + extern const struct mem_type *get_mem_type(unsigned int type); /* * external interface to remap single page with appropriate type diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h index f8d391ad92037..57216658dd9a9 100644 --- a/arch/arm/include/asm/mach/mmc.h +++ b/arch/arm/include/asm/mach/mmc.h @@ -7,6 +7,12 @@ #include #include #include +#include + +#define SDC_DAT1_DISABLE 0 +#define SDC_DAT1_ENABLE 1 +#define SDC_DAT1_ENWAKE 2 +#define SDC_DAT1_DISWAKE 3 struct embedded_sdio_data { struct sdio_cis cis; @@ -15,13 +21,129 @@ struct embedded_sdio_data { int num_funcs; }; +/* This structure keeps information per regulator */ +struct msm_mmc_reg_data { + /* voltage regulator handle */ + struct regulator *reg; + /* regulator name */ + const char *name; + /* voltage level to be set */ + unsigned int low_vol_level; + unsigned int high_vol_level; + /* Load values for low power and high power mode */ + unsigned int lpm_uA; + unsigned int hpm_uA; + /* + * is set voltage supported for this regulator? + * false => set voltage is not supported + * true => set voltage is supported + */ + bool set_voltage_sup; + /* is this regulator enabled? */ + bool is_enabled; + /* is this regulator needs to be always on? */ + bool always_on; + /* is low power mode setting required for this regulator? */ + bool lpm_sup; +}; + +/* + * This structure keeps information for all the + * regulators required for a SDCC slot. + */ +struct msm_mmc_slot_reg_data { + struct msm_mmc_reg_data *vdd_data; /* keeps VDD/VCC regulator info */ + struct msm_mmc_reg_data *vccq_data; /* keeps VCCQ regulator info */ + struct msm_mmc_reg_data *vddp_data; /* keeps VDD Pad regulator info */ +}; + +struct msm_mmc_gpio { + u32 no; + const char *name; + bool is_always_on; + bool is_enabled; +}; + +struct msm_mmc_gpio_data { + struct msm_mmc_gpio *gpio; + u8 size; +}; + +struct msm_mmc_pad_pull { + enum msm_tlmm_pull_tgt no; + u32 val; +}; + +struct msm_mmc_pad_pull_data { + struct msm_mmc_pad_pull *on; + struct msm_mmc_pad_pull *off; + u8 size; +}; + +struct msm_mmc_pad_drv { + enum msm_tlmm_hdrive_tgt no; + u32 val; +}; + +struct msm_mmc_pad_drv_data { + struct msm_mmc_pad_drv *on; + struct msm_mmc_pad_drv *off; + u8 size; +}; + +struct msm_mmc_pad_data { + struct msm_mmc_pad_pull_data *pull; + struct msm_mmc_pad_drv_data *drv; +}; + +struct msm_mmc_pin_data { + /* + * = 1 if controller pins are using gpios + * = 0 if controller has dedicated MSM pads + */ + u8 is_gpio; + u8 cfg_sts; + struct msm_mmc_gpio_data *gpio_data; + struct msm_mmc_pad_data *pad_data; +}; + struct mmc_platform_data { unsigned int ocr_mask; /* available voltages */ - int built_in; /* built-in device flag */ + /* + * XPC controls the maximum current in the + * default speed mode of SDXC card. + */ + unsigned int xpc_cap; + /* Supported UHS-I Modes */ + unsigned int uhs_caps; u32 (*translate_vdd)(struct device *, unsigned int); + void (*sdio_lpm_gpio_setup)(struct device *, unsigned int); unsigned int (*status)(struct device *); - struct embedded_sdio_data *embedded_sdio; + unsigned int status_irq; + unsigned int status_gpio; + struct embedded_sdio_data *embedded_sdio; + unsigned int sdiowakeup_irq; int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); + unsigned long irq_flags; + unsigned long mmc_bus_width; + int (*wpswitch) (struct device *); + unsigned int msmsdcc_fmin; + unsigned int msmsdcc_fmid; + unsigned int msmsdcc_fmax; + bool nonremovable; + bool pclk_src_dfab; + int (*cfg_mpm_sdiowakeup)(struct device *, unsigned); + bool sdcc_v4_sup; + unsigned int wpswitch_gpio; + unsigned char wpswitch_polarity; + struct msm_mmc_slot_reg_data *vreg_data; + int is_sdio_al_client; + unsigned int *sup_clk_table; + unsigned char sup_clk_cnt; + struct msm_mmc_pin_data *pin_data; + bool disable_bam; + bool disable_runtime_pm; + bool disable_cmd23; }; #endif diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h index d0ee74b7cf86b..308b0adf9d9a3 100644 --- a/arch/arm/include/asm/memory.h +++ b/arch/arm/include/asm/memory.h @@ -256,6 +256,13 @@ static inline __deprecated void *bus_to_virt(unsigned long x) #define arch_is_coherent() 0 #endif +/* + * Set if the architecture speculatively fetches data into cache. + */ +#ifndef arch_has_speculative_dfetch +#define arch_has_speculative_dfetch() 0 +#endif + #endif #include diff --git a/arch/arm/include/asm/mutex.h b/arch/arm/include/asm/mutex.h index 93226cf23ae0a..fd3f17ef94a04 100644 --- a/arch/arm/include/asm/mutex.h +++ b/arch/arm/include/asm/mutex.h @@ -41,6 +41,8 @@ __mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *)) __res |= __ex_flag; if (unlikely(__res != 0)) fail_fn(count); + else + smp_rmb(); } static inline int @@ -61,6 +63,9 @@ __mutex_fastpath_lock_retval(atomic_t *count, int (*fail_fn)(atomic_t *)) __res |= __ex_flag; if (unlikely(__res != 0)) __res = fail_fn(count); + else + smp_rmb(); + return __res; } @@ -74,6 +79,7 @@ __mutex_fastpath_unlock(atomic_t *count, void (*fail_fn)(atomic_t *)) { int __ex_flag, __res, __orig; + smp_wmb(); __asm__ ( "ldrex %0, [%3] \n\t" @@ -119,6 +125,8 @@ __mutex_fastpath_trylock(atomic_t *count, int (*fail_fn)(atomic_t *)) : "=&r" (__orig), "=&r" (__res), "=&r" (__ex_flag) : "r" (&count->counter) : "cc", "memory" ); + if (__orig) + smp_rmb(); return __orig; } diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h index fc19009252753..348d513afa923 100644 --- a/arch/arm/include/asm/outercache.h +++ b/arch/arm/include/asm/outercache.h @@ -31,6 +31,7 @@ struct outer_cache_fns { #ifdef CONFIG_OUTER_CACHE_SYNC void (*sync)(void); #endif + void (*set_debug)(unsigned long); }; #ifdef CONFIG_OUTER_CACHE diff --git a/arch/arm/include/asm/page.h b/arch/arm/include/asm/page.h index f51a69595f6ed..68a71effe1b90 100644 --- a/arch/arm/include/asm/page.h +++ b/arch/arm/include/asm/page.h @@ -201,6 +201,11 @@ typedef struct page *pgtable_t; extern int pfn_valid(unsigned long); #endif +#ifdef CONFIG_MEMORY_HOTPLUG_SPARSE +extern int _early_pfn_valid(unsigned long); +#define early_pfn_valid(pfn) (_early_pfn_valid(pfn)) +#endif + #include #endif /* !__ASSEMBLY__ */ diff --git a/arch/arm/include/asm/perf_event.h b/arch/arm/include/asm/perf_event.h index c4aa4e8c6af9c..0e1fd19010525 100644 --- a/arch/arm/include/asm/perf_event.h +++ b/arch/arm/include/asm/perf_event.h @@ -24,6 +24,9 @@ enum arm_perf_pmu_ids { ARM_PERF_PMU_ID_V6MP, ARM_PERF_PMU_ID_CA8, ARM_PERF_PMU_ID_CA9, + ARM_PERF_PMU_ID_SCORPION, + ARM_PERF_PMU_ID_SCORPIONMP, + ARM_PERF_PMU_ID_KRAIT, ARM_NUM_PMU_IDS, }; diff --git a/arch/arm/include/asm/perftypes.h b/arch/arm/include/asm/perftypes.h new file mode 100644 index 0000000000000..d659a9cdad2c4 --- /dev/null +++ b/arch/arm/include/asm/perftypes.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* +** perftypes.h +** DESCRIPTION +** ksapi.ko function hooks header file +*/ + +#ifndef __PERFTYPES_H__ +#define __PERFTYPES_H__ + +typedef void (*VPVF)(void); +typedef void (*VPULF)(unsigned long); +typedef void (*VPULULF)(unsigned long, unsigned long); + +extern VPVF pp_interrupt_out_ptr; +extern VPVF pp_interrupt_in_ptr; +extern VPULF pp_process_remove_ptr; +extern void perf_mon_interrupt_in(void); +extern void perf_mon_interrupt_out(void); + +#endif diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h index 1c898fcf80105..47192284ae608 100644 --- a/arch/arm/include/asm/pgtable.h +++ b/arch/arm/include/asm/pgtable.h @@ -24,6 +24,7 @@ #include #include #include +#include /* * Just any arbitrary offset to the start of the vmalloc VM area: the @@ -236,16 +237,27 @@ extern pgprot_t pgprot_kernel; #define pgprot_device(prot) \ __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_DEV_NONSHARED) +#define pgprot_writethroughcache(prot) \ + __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_WRITETHROUGH) + +#define pgprot_writebackcache(prot) \ + __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_WRITEBACK) + +#define pgprot_writebackwacache(prot) \ + __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_WRITEALLOC) + #ifdef CONFIG_ARM_DMA_MEM_BUFFERABLE #define pgprot_dmacoherent(prot) \ __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE | L_PTE_XN) #define __HAVE_PHYS_MEM_ACCESS_PROT +#define COHERENT_IS_NORMAL 1 struct file; extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, unsigned long size, pgprot_t vma_prot); #else #define pgprot_dmacoherent(prot) \ __pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_UNCACHED | L_PTE_XN) +#define COHERENT_IS_NORMAL 0 #endif #endif /* __ASSEMBLY__ */ @@ -472,8 +484,6 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) * remap a physical page `pfn' of size `size' with page protection `prot' * into virtual address `from' */ - - #ifndef HAS_ARCH_IO_REMAP_PFN_RANGE #define io_remap_pfn_range(vma,from,pfn,size,prot) \ remap_pfn_range(vma,from,pfn,size,prot) diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h index 8ccea012722cb..5422501275171 100644 --- a/arch/arm/include/asm/pmu.h +++ b/arch/arm/include/asm/pmu.h @@ -14,6 +14,7 @@ enum arm_pmu_type { ARM_PMU_DEVICE_CPU = 0, + ARM_PMU_DEVICE_L2 = 1, ARM_NUM_PMU_DEVICES, }; diff --git a/arch/arm/include/asm/processor.h b/arch/arm/include/asm/processor.h index 67357baaeeebd..9d223c02911a4 100644 --- a/arch/arm/include/asm/processor.h +++ b/arch/arm/include/asm/processor.h @@ -29,6 +29,8 @@ #define STACK_TOP_MAX TASK_SIZE #endif +extern unsigned int boot_reason; + union debug_insn { u32 arm; u16 thumb; diff --git a/arch/arm/include/asm/remote_spinlock.h b/arch/arm/include/asm/remote_spinlock.h new file mode 100644 index 0000000000000..de39fdd8a86f5 --- /dev/null +++ b/arch/arm/include/asm/remote_spinlock.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __ASM_REMOTE_SPINLOCK_H +#define __ASM_REMOTE_SPINLOCK_H + +#include + +#endif /* __ASM_REMOTE_SPINLOCK_H */ diff --git a/arch/arm/include/asm/rwsem.h b/arch/arm/include/asm/rwsem.h index 694cf4005afc5..5532e242a8648 100644 --- a/arch/arm/include/asm/rwsem.h +++ b/arch/arm/include/asm/rwsem.h @@ -10,11 +10,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ #ifndef _ASM_ARM_RWSEM_H diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h index db64de809d29b..25a0f16845b03 100644 --- a/arch/arm/include/asm/setup.h +++ b/arch/arm/include/asm/setup.h @@ -250,6 +250,19 @@ extern struct meminfo meminfo; #define bank_phys_end(bank) ((bank)->start + (bank)->size) #define bank_phys_size(bank) (bank)->size + +/* + * Early command line parameters. + */ +struct early_params { + const char *arg; + void (*fn)(char **p); +}; + +#define __early_param(name,fn) \ +static struct early_params __early_##fn __used \ +__attribute__((__section__(".early_param.init"))) = { name, fn } + #endif /* __KERNEL__ */ #endif diff --git a/arch/arm/include/asm/spinlock.h b/arch/arm/include/asm/spinlock.h index 17eb355707dd3..db41b137c760a 100644 --- a/arch/arm/include/asm/spinlock.h +++ b/arch/arm/include/asm/spinlock.h @@ -5,6 +5,17 @@ #error SMP not supported on pre-ARMv6 CPUs #endif +/* + * Portions based on arch/ia64/include/asm/spinlock.h + * + * Copyright (C) 1998-2003 Hewlett-Packard Co + * David Mosberger-Tang + * Copyright (C) 1999 Walt Drummond + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This file is used for SMP configurations only. + */ + static inline void dsb_sev(void) { #if __LINUX_ARM_ARCH__ >= 7 @@ -21,6 +32,7 @@ static inline void dsb_sev(void) #endif } +#ifndef CONFIG_ARM_TICKET_LOCKS /* * ARMv6 Spin-locking. * @@ -91,6 +103,131 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock) dsb_sev(); } +#else +/* + * ARM Ticket spin-locking + * + * Ticket locks are conceptually two parts, one indicating the current head of + * the queue, and the other indicating the current tail. The lock is acquired + * by atomically noting the tail and incrementing it by one (thus adding + * ourself to the queue and noting our position), then waiting until the head + * becomes equal to the the initial value of the tail. + * + * Unlocked value: 0 + * Locked value: now_serving != next_ticket + * + * 31 17 16 15 14 0 + * +----------------------------------------------------+ + * | now_serving | next_ticket | + * +----------------------------------------------------+ + */ + +#define TICKET_SHIFT 16 +#define TICKET_BITS 16 +#define TICKET_MASK 0xFFFF + +#define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock) + +static inline void arch_spin_lock(arch_spinlock_t *lock) +{ + unsigned long tmp, ticket, next_ticket; + + /* Grab the next ticket and wait for it to be "served" */ + __asm__ __volatile__( +"1: ldrex %[ticket], [%[lockaddr]]\n" +" uadd16 %[next_ticket], %[ticket], %[val1]\n" +" strex %[tmp], %[next_ticket], [%[lockaddr]]\n" +" teq %[tmp], #0\n" +" bne 1b\n" +" uxth %[ticket], %[ticket]\n" +"2:\n" +#ifdef CONFIG_CPU_32v6K +" wfene\n" +#endif +" ldr %[tmp], [%[lockaddr]]\n" +" cmp %[ticket], %[tmp], lsr #16\n" +" bne 2b" + : [ticket]"=&r" (ticket), [tmp]"=&r" (tmp), [next_ticket]"=&r" (next_ticket) + : [lockaddr]"r" (&lock->lock), [val1]"r" (1) + : "cc"); + smp_mb(); +} + +static inline int arch_spin_trylock(arch_spinlock_t *lock) +{ + unsigned long tmp, ticket, next_ticket; + + /* Grab lock if now_serving == next_ticket and access is exclusive */ + __asm__ __volatile__( +" ldrex %[ticket], [%[lockaddr]]\n" +" ror %[tmp], %[ticket], #16\n" +" eors %[tmp], %[tmp], %[ticket]\n" +" bne 1f\n" +" uadd16 %[next_ticket], %[ticket], %[val1]\n" +" strex %[tmp], %[next_ticket], [%[lockaddr]]\n" +"1:" + : [ticket]"=&r" (ticket), [tmp]"=&r" (tmp), + [next_ticket]"=&r" (next_ticket) + : [lockaddr]"r" (&lock->lock), [val1]"r" (1) + : "cc"); + if (!tmp) + smp_mb(); + return !tmp; +} + +static inline void arch_spin_unlock(arch_spinlock_t *lock) +{ + unsigned long ticket, tmp; + + smp_mb(); + + /* Bump now_serving by 1 */ + __asm__ __volatile__( +"1: ldrex %[ticket], [%[lockaddr]]\n" +" uadd16 %[ticket], %[ticket], %[serving1]\n" +" strex %[tmp], %[ticket], [%[lockaddr]]\n" +" teq %[tmp], #0\n" +" bne 1b" + : [ticket]"=&r" (ticket), [tmp]"=&r" (tmp) + : [lockaddr]"r" (&lock->lock), [serving1]"r" (0x00010000) + : "cc"); + dsb_sev(); +} + +static inline void arch_spin_unlock_wait(arch_spinlock_t *lock) +{ + unsigned long ticket; + + /* Wait for now_serving == next_ticket */ + __asm__ __volatile__( +#ifdef CONFIG_CPU_32v6K +" cmpne %[lockaddr], %[lockaddr]\n" +"1: wfene\n" +#else +"1:\n" +#endif +" ldr %[ticket], [%[lockaddr]]\n" +" eor %[ticket], %[ticket], %[ticket], lsr #16\n" +" uxth %[ticket], %[ticket]\n" +" cmp %[ticket], #0\n" +" bne 1b" + : [ticket]"=&r" (ticket) + : [lockaddr]"r" (&lock->lock) + : "cc"); +} + +static inline int arch_spin_is_locked(arch_spinlock_t *lock) +{ + unsigned long tmp = ACCESS_ONCE(lock->lock); + return (((tmp >> TICKET_SHIFT) ^ tmp) & TICKET_MASK) != 0; +} + +static inline int arch_spin_is_contended(arch_spinlock_t *lock) +{ + unsigned long tmp = ACCESS_ONCE(lock->lock); + return ((tmp - (tmp >> TICKET_SHIFT)) & TICKET_MASK) > 1; +} +#endif /* * RWLOCKS diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 913c582e32dd8..9e51aef4637a6 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -69,6 +69,8 @@ #define __exception_irq_entry __exception #endif +void cpu_idle_wait(void); + struct thread_info; struct task_struct; diff --git a/arch/arm/include/asm/tlbflush.h b/arch/arm/include/asm/tlbflush.h index d2005de383b8c..ab2a488ae77e8 100644 --- a/arch/arm/include/asm/tlbflush.h +++ b/arch/arm/include/asm/tlbflush.h @@ -433,7 +433,7 @@ local_flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) if (tlb_flag(TLB_V6_I_PAGE)) asm("mcr p15, 0, %0, c8, c5, 1" : : "r" (uaddr) : "cc"); if (tlb_flag(TLB_V7_UIS_PAGE)) -#ifdef CONFIG_ARM_ERRATA_720789 +#if defined(CONFIG_ARM_ERRATA_720789) || defined(CONFIG_ARCH_MSM8X60) asm("mcr p15, 0, %0, c8, c3, 3" : : "r" (uaddr & PAGE_MASK) : "cc"); #else asm("mcr p15, 0, %0, c8, c3, 1" : : "r" (uaddr) : "cc"); @@ -480,7 +480,11 @@ static inline void local_flush_tlb_kernel_page(unsigned long kaddr) if (tlb_flag(TLB_V6_I_PAGE)) asm("mcr p15, 0, %0, c8, c5, 1" : : "r" (kaddr) : "cc"); if (tlb_flag(TLB_V7_UIS_PAGE)) +#ifdef CONFIG_ARCH_MSM8X60 + asm("mcr p15, 0, %0, c8, c3, 3" : : "r" (kaddr) : "cc"); +#else asm("mcr p15, 0, %0, c8, c3, 1" : : "r" (kaddr) : "cc"); +#endif if (tlb_flag(TLB_BTB)) { /* flush the branch target cache */ diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c index cadd32b0b5f8e..687a26d2961ca 100644 --- a/arch/arm/kernel/armksyms.c +++ b/arch/arm/kernel/armksyms.c @@ -52,10 +52,6 @@ extern void fpundefinstr(void); EXPORT_SYMBOL(__backtrace); - /* platform dependent support */ -EXPORT_SYMBOL(__udelay); -EXPORT_SYMBOL(__const_udelay); - /* networking */ EXPORT_SYMBOL(csum_partial); EXPORT_SYMBOL(csum_partial_copy_from_user); diff --git a/arch/arm/kernel/debug.S b/arch/arm/kernel/debug.S index a0f07521ca8a2..c1bf7c5c5803a 100644 --- a/arch/arm/kernel/debug.S +++ b/arch/arm/kernel/debug.S @@ -25,7 +25,7 @@ .macro addruart, rp, rv .endm -#if defined(CONFIG_CPU_V6) +#if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_V7) .macro senduart, rd, rx mcr p14, 0, \rd, c0, c5, 0 diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 095fb91f611fa..7d674b29923b4 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -4,6 +4,7 @@ * Copyright (C) 1996,1997,1998 Russell King. * ARM700 fix by Matthew Godbolt (linux-user@willothewisp.demon.co.uk) * nommu support by Hyok S. Choi (hyok.choi@samsung.com) + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -728,7 +729,14 @@ ENTRY(__switch_to) ldr r7, [r7, #TSK_STACK_CANARY] #endif #ifdef CONFIG_CPU_USE_DOMAINS +#ifdef CONFIG_EMULATE_DOMAIN_MANAGER_V7 + stmdb r13!, {r0-r3, lr} + mov r0, r6 + bl emulate_domain_manager_set + ldmia r13!, {r0-r3, lr} +#else mcr p15, 0, r6, c3, c0, 0 @ Set domain register +#endif #endif mov r5, r0 add r4, r2, #TI_CPU_SAVE diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index 610ba6cca8a0d..b9f689a340708 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S @@ -4,6 +4,7 @@ * Copyright (C) 1994-2002 Russell King * Copyright (c) 2003 ARM Limited * All Rights Reserved + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -355,10 +356,17 @@ __enable_mmu: #ifdef CONFIG_CPU_ICACHE_DISABLE bic r0, r0, #CR_I #endif +#ifdef CONFIG_EMULATE_DOMAIN_MANAGER_V7 + mov r5, #(domain_val(DOMAIN_USER, DOMAIN_CLIENT) | \ + domain_val(DOMAIN_KERNEL, DOMAIN_CLIENT) | \ + domain_val(DOMAIN_TABLE, DOMAIN_CLIENT) | \ + domain_val(DOMAIN_IO, DOMAIN_CLIENT)) +#else mov r5, #(domain_val(DOMAIN_USER, DOMAIN_CLIENT) | \ domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \ domain_val(DOMAIN_TABLE, DOMAIN_CLIENT) | \ domain_val(DOMAIN_IO, DOMAIN_CLIENT)) +#endif mcr p15, 0, r5, c3, c0, 0 @ load domain access register mcr p15, 0, r4, c2, c0, 0 @ load page table pointer b __turn_mmu_on diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c index 7e9a0c7f19889..b362f437c30b4 100644 --- a/arch/arm/kernel/hw_breakpoint.c +++ b/arch/arm/kernel/hw_breakpoint.c @@ -833,6 +833,18 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, return ret; } +static void reset_brps_reserved_reg(int n) +{ + int i; + + /* we must also reset any reserved registers. */ + for (i = 0; i < n; ++i) { + write_wb_reg(ARM_BASE_BCR + i, 0UL); + write_wb_reg(ARM_BASE_BVR + i, 0UL); + } + +} + /* * One-time initialisation. */ @@ -880,11 +892,11 @@ static void reset_ctrl_regs(void *info) if (enable_monitor_mode()) return; - /* We must also reset any reserved registers. */ - for (i = 0; i < core_num_brps + core_num_reserved_brps; ++i) { - write_wb_reg(ARM_BASE_BCR + i, 0UL); - write_wb_reg(ARM_BASE_BVR + i, 0UL); - } +#ifdef CONFIG_HAVE_HW_BRKPT_RESERVED_RW_ACCESS + reset_brps_reserved_reg(core_num_brps); +#else + reset_brps_reserved_reg(core_num_brps + core_num_reserved_brps); +#endif for (i = 0; i < core_num_wrps; ++i) { write_wb_reg(ARM_BASE_WCR + i, 0UL); diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 28536e352deb5..d18a37e6218fc 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -42,6 +42,8 @@ #include #include +#include + /* * No architecture-specific irq_finish function defined in arm/arch/irqs.h. */ @@ -112,15 +114,16 @@ int show_interrupts(struct seq_file *p, void *v) } /* - * do_IRQ handles all hardware IRQ's. Decoded IRQs should not - * come via this function. Instead, they should provide their - * own 'handler' + * handle_IRQ handles all hardware IRQ's. Decoded IRQs should + * not come via this function. Instead, they should provide their + * own 'handler'. Used by platform code implementing C-based 1st + * level decoding. */ -asmlinkage void __exception_irq_entry -asm_do_IRQ(unsigned int irq, struct pt_regs *regs) +void handle_IRQ(unsigned int irq, struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); + perf_mon_interrupt_in(); irq_enter(); /* @@ -140,6 +143,16 @@ asm_do_IRQ(unsigned int irq, struct pt_regs *regs) irq_exit(); set_irq_regs(old_regs); + perf_mon_interrupt_out(); +} + +/* + * asm_do_IRQ is the interface to be used from assembly code. + */ +asmlinkage void __exception_irq_entry +asm_do_IRQ(unsigned int irq, struct pt_regs *regs) +{ + handle_IRQ(irq, regs); } void set_irq_flags(unsigned int irq, unsigned int iflags) diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c index 30ead135ff5f4..0a4256224b71c 100644 --- a/arch/arm/kernel/machine_kexec.c +++ b/arch/arm/kernel/machine_kexec.c @@ -81,6 +81,7 @@ void machine_kexec(struct kimage *image) unsigned long reboot_code_buffer_phys; void *reboot_code_buffer; + arch_kexec(); page_list = image->head & PAGE_MASK; @@ -113,5 +114,5 @@ void machine_kexec(struct kimage *image) cpu_proc_fin(); outer_inv_all(); flush_cache_all(); - cpu_reset(reboot_code_buffer_phys); + __virt_to_phys(cpu_reset)(reboot_code_buffer_phys); } diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c index d150ad1ccb5d8..bc2a90da25048 100644 --- a/arch/arm/kernel/perf_event.c +++ b/arch/arm/kernel/perf_event.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -71,6 +72,10 @@ struct arm_pmu { enum arm_perf_pmu_ids id; const char *name; irqreturn_t (*handle_irq)(int irq_num, void *dev); +#ifdef CONFIG_SMP + void (*secondary_enable)(unsigned int irq); + void (*secondary_disable)(unsigned int irq); +#endif void (*enable)(struct hw_perf_event *evt, int idx); void (*disable)(struct hw_perf_event *evt, int idx); int (*get_event_idx)(struct cpu_hw_events *cpuc, @@ -204,10 +209,9 @@ armpmu_event_set_period(struct perf_event *event, static u64 armpmu_event_update(struct perf_event *event, struct hw_perf_event *hwc, - int idx) + int idx, int overflow) { - int shift = 64 - 32; - s64 prev_raw_count, new_raw_count; + u64 prev_raw_count, new_raw_count; u64 delta; again: @@ -218,8 +222,13 @@ armpmu_event_update(struct perf_event *event, new_raw_count) != prev_raw_count) goto again; - delta = (new_raw_count << shift) - (prev_raw_count << shift); - delta >>= shift; + new_raw_count &= armpmu->max_period; + prev_raw_count &= armpmu->max_period; + + if (overflow) + delta = armpmu->max_period - prev_raw_count + new_raw_count; + else + delta = new_raw_count - prev_raw_count; local64_add(delta, &event->count); local64_sub(delta, &hwc->period_left); @@ -236,7 +245,7 @@ armpmu_read(struct perf_event *event) if (hwc->idx < 0) return; - armpmu_event_update(event, hwc, hwc->idx); + armpmu_event_update(event, hwc, hwc->idx, 0); } static void @@ -254,7 +263,7 @@ armpmu_stop(struct perf_event *event, int flags) if (!(hwc->state & PERF_HES_STOPPED)) { armpmu->disable(hwc, hwc->idx); barrier(); /* why? */ - armpmu_event_update(event, hwc, hwc->idx); + armpmu_event_update(event, hwc, hwc->idx, 0); hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; } } @@ -407,6 +416,10 @@ armpmu_reserve_hardware(void) pr_warning("unable to request IRQ%d for ARM perf " "counters\n", irq); break; +#ifdef CONFIG_SMP + } else if (armpmu->secondary_enable) { + armpmu->secondary_enable(irq); +#endif } } @@ -430,8 +443,13 @@ armpmu_release_hardware(void) for (i = pmu_device->num_resources - 1; i >= 0; --i) { irq = platform_get_irq(pmu_device, i); - if (irq >= 0) + if (irq >= 0) { free_irq(irq, NULL); +#ifdef CONFIG_SMP + if (armpmu->secondary_disable) + armpmu->secondary_disable(irq); +#endif + } } armpmu->stop(); @@ -608,6 +626,10 @@ static struct pmu pmu = { #include "perf_event_xscale.c" #include "perf_event_v6.c" #include "perf_event_v7.c" +#include "perf_event_msm.c" +#include "perf_event_msm_l2.c" +#include "perf_event_msm_krait.c" +#include "perf_event_msm_krait_l2.c" static int __init init_hw_perf_events(void) @@ -645,6 +667,22 @@ init_hw_perf_events(void) armpmu = xscale2pmu_init(); break; } + /* Qualcomm CPUs */ + } else if (0x51 == implementor) { + switch (part_number) { + case 0x00F0: /* 8x50 & 7x30*/ + armpmu = armv7_scorpion_pmu_init(); + break; + case 0x02D0: /* 8x60 */ + armpmu = armv7_scorpionmp_pmu_init(); + scorpionmp_l2_pmu_init(); + break; + case 0x0490: /* 8960 sim */ + case 0x04D0: /* 8960 */ + armpmu = armv7_krait_pmu_init(); + krait_l2_pmu_init(); + break; + } } if (armpmu) { @@ -714,7 +752,8 @@ perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs) tail = (struct frame_tail __user *)regs->ARM_fp - 1; - while (tail && !((unsigned long)tail & 0x3)) + while ((entry->nr < PERF_MAX_STACK_DEPTH) && + tail && !((unsigned long)tail & 0x3)) tail = user_backtrace(tail, entry); } diff --git a/arch/arm/kernel/perf_event_msm.c b/arch/arm/kernel/perf_event_msm.c new file mode 100644 index 0000000000000..be088d132f384 --- /dev/null +++ b/arch/arm/kernel/perf_event_msm.c @@ -0,0 +1,710 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include "../vfp/vfpinstr.h" + +#ifdef CONFIG_CPU_V7 +enum scorpion_perf_common { + SCORPION_EVT_START_IDX = 0x4c, + SCORPION_ICACHE_EXPL_INV = 0x4c, + SCORPION_ICACHE_MISS = 0x4d, + SCORPION_ICACHE_ACCESS = 0x4e, + SCORPION_ICACHE_CACHEREQ_L2 = 0x4f, + SCORPION_ICACHE_NOCACHE_L2 = 0x50, + SCORPION_HIQUP_NOPED = 0x51, + SCORPION_DATA_ABORT = 0x52, + SCORPION_IRQ = 0x53, + SCORPION_FIQ = 0x54, + SCORPION_ALL_EXCPT = 0x55, + SCORPION_UNDEF = 0x56, + SCORPION_SVC = 0x57, + SCORPION_SMC = 0x58, + SCORPION_PREFETCH_ABORT = 0x59, + SCORPION_INDEX_CHECK = 0x5a, + SCORPION_NULL_CHECK = 0x5b, + SCORPION_ICIMVAU_IMPL_ICIALLU = 0x5c, + SCORPION_NONICIALLU_BTAC_INV = 0x5d, + SCORPION_IMPL_ICIALLU = 0x5e, + SCORPION_EXPL_ICIALLU = 0x5f, + SCORPION_SPIPE_ONLY_CYCLES = 0x60, + SCORPION_XPIPE_ONLY_CYCLES = 0x61, + SCORPION_DUAL_CYCLES = 0x62, + SCORPION_DISPATCH_ANY_CYCLES = 0x63, + SCORPION_FIFO_FULLBLK_CMT = 0x64, + SCORPION_FAIL_COND_INST = 0x65, + SCORPION_PASS_COND_INST = 0x66, + SCORPION_ALLOW_VU_CLK = 0x67, + SCORPION_VU_IDLE = 0x68, + SCORPION_ALLOW_L2_CLK = 0x69, + SCORPION_L2_IDLE = 0x6a, + SCORPION_DTLB_IMPL_INV_SCTLR_DACR = 0x6b, + SCORPION_DTLB_EXPL_INV = 0x6c, + SCORPION_DTLB_MISS = 0x6d, + SCORPION_DTLB_ACCESS = 0x6e, + SCORPION_ITLB_MISS = 0x6f, + SCORPION_ITLB_IMPL_INV = 0x70, + SCORPION_ITLB_EXPL_INV = 0x71, + SCORPION_UTLB_D_MISS = 0x72, + SCORPION_UTLB_D_ACCESS = 0x73, + SCORPION_UTLB_I_MISS = 0x74, + SCORPION_UTLB_I_ACCESS = 0x75, + SCORPION_UTLB_INV_ASID = 0x76, + SCORPION_UTLB_INV_MVA = 0x77, + SCORPION_UTLB_INV_ALL = 0x78, + SCORPION_S2_HOLD_RDQ_UNAVAIL = 0x79, + SCORPION_S2_HOLD = 0x7a, + SCORPION_S2_HOLD_DEV_OP = 0x7b, + SCORPION_S2_HOLD_ORDER = 0x7c, + SCORPION_S2_HOLD_BARRIER = 0x7d, + SCORPION_VIU_DUAL_CYCLE = 0x7e, + SCORPION_VIU_SINGLE_CYCLE = 0x7f, + SCORPION_VX_PIPE_WAR_STALL_CYCLES = 0x80, + SCORPION_VX_PIPE_WAW_STALL_CYCLES = 0x81, + SCORPION_VX_PIPE_RAW_STALL_CYCLES = 0x82, + SCORPION_VX_PIPE_LOAD_USE_STALL = 0x83, + SCORPION_VS_PIPE_WAR_STALL_CYCLES = 0x84, + SCORPION_VS_PIPE_WAW_STALL_CYCLES = 0x85, + SCORPION_VS_PIPE_RAW_STALL_CYCLES = 0x86, + SCORPION_EXCEPTIONS_INV_OPERATION = 0x87, + SCORPION_EXCEPTIONS_DIV_BY_ZERO = 0x88, + SCORPION_COND_INST_FAIL_VX_PIPE = 0x89, + SCORPION_COND_INST_FAIL_VS_PIPE = 0x8a, + SCORPION_EXCEPTIONS_OVERFLOW = 0x8b, + SCORPION_EXCEPTIONS_UNDERFLOW = 0x8c, + SCORPION_EXCEPTIONS_DENORM = 0x8d, +}; + +enum scorpion_perf_smp { + SCORPIONMP_NUM_BARRIERS = 0x8e, + SCORPIONMP_BARRIER_CYCLES = 0x8f, +}; + +enum scorpion_perf_up { + SCORPION_BANK_AB_HIT = 0x8e, + SCORPION_BANK_AB_ACCESS = 0x8f, + SCORPION_BANK_CD_HIT = 0x90, + SCORPION_BANK_CD_ACCESS = 0x91, + SCORPION_BANK_AB_DSIDE_HIT = 0x92, + SCORPION_BANK_AB_DSIDE_ACCESS = 0x93, + SCORPION_BANK_CD_DSIDE_HIT = 0x94, + SCORPION_BANK_CD_DSIDE_ACCESS = 0x95, + SCORPION_BANK_AB_ISIDE_HIT = 0x96, + SCORPION_BANK_AB_ISIDE_ACCESS = 0x97, + SCORPION_BANK_CD_ISIDE_HIT = 0x98, + SCORPION_BANK_CD_ISIDE_ACCESS = 0x99, + SCORPION_ISIDE_RD_WAIT = 0x9a, + SCORPION_DSIDE_RD_WAIT = 0x9b, + SCORPION_BANK_BYPASS_WRITE = 0x9c, + SCORPION_BANK_AB_NON_CASTOUT = 0x9d, + SCORPION_BANK_AB_L2_CASTOUT = 0x9e, + SCORPION_BANK_CD_NON_CASTOUT = 0x9f, + SCORPION_BANK_CD_L2_CASTOUT = 0xa0, +}; + +static const unsigned armv7_scorpion_perf_map[PERF_COUNT_HW_MAX] = { + [PERF_COUNT_HW_CPU_CYCLES] = ARMV7_PERFCTR_CPU_CYCLES, + [PERF_COUNT_HW_INSTRUCTIONS] = ARMV7_PERFCTR_INSTR_EXECUTED, + [PERF_COUNT_HW_CACHE_REFERENCES] = HW_OP_UNSUPPORTED, + [PERF_COUNT_HW_CACHE_MISSES] = HW_OP_UNSUPPORTED, + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV7_PERFCTR_PC_WRITE, + [PERF_COUNT_HW_BRANCH_MISSES] = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED, + [PERF_COUNT_HW_BUS_CYCLES] = ARMV7_PERFCTR_CLOCK_CYCLES, +}; + +static const unsigned armv7_scorpion_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX] = { + [C(L1D)] = { + /* + * The performance counters don't differentiate between read + * and write accesses/misses so this isn't strictly correct, + * but it's the best we can do. Writes and reads get + * combined. + */ + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = ARMV7_PERFCTR_DCACHE_ACCESS, + [C(RESULT_MISS)] = ARMV7_PERFCTR_DCACHE_REFILL, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = ARMV7_PERFCTR_DCACHE_ACCESS, + [C(RESULT_MISS)] = ARMV7_PERFCTR_DCACHE_REFILL, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + }, + [C(L1I)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = SCORPION_ICACHE_ACCESS, + [C(RESULT_MISS)] = SCORPION_ICACHE_MISS, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = SCORPION_ICACHE_ACCESS, + [C(RESULT_MISS)] = SCORPION_ICACHE_MISS, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + }, + [C(LL)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + }, + [C(DTLB)] = { + /* + * Only ITLB misses and DTLB refills are supported. + * If users want the DTLB refills misses a raw counter + * must be used. + */ + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = SCORPION_DTLB_ACCESS, + [C(RESULT_MISS)] = SCORPION_DTLB_MISS, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = SCORPION_DTLB_ACCESS, + [C(RESULT_MISS)] = SCORPION_DTLB_MISS, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + }, + [C(ITLB)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = SCORPION_ITLB_MISS, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = SCORPION_ITLB_MISS, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + }, + [C(BPU)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] + = ARMV7_PERFCTR_PC_BRANCH_MIS_USED, + [C(RESULT_MISS)] + = ARMV7_PERFCTR_PC_BRANCH_MIS_USED, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] + = ARMV7_PERFCTR_PC_BRANCH_MIS_USED, + [C(RESULT_MISS)] + = ARMV7_PERFCTR_PC_BRANCH_MIS_USED, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + }, +}; + +struct scorpion_evt { + /* + * The scorpion_evt_type field corresponds to the actual Scorpion + * event codes. These map many-to-one to the armv7 defined codes + */ + u32 scorpion_evt_type; + + /* + * The group_setval field corresponds to the value that the group + * register needs to be set to. This value is deduced from the row + * and column that the event belongs to in the event table + */ + u32 group_setval; + + /* + * The groupcode corresponds to the group that the event belongs to. + * Scorpion has 5 groups of events LPM0, LPM1, LPM2, L2LPM and VLPM + * going from 0 to 4 in terms of the codes used + */ + u8 groupcode; + + /* + * The armv7_evt_type field corresponds to the armv7 defined event + * code that the Scorpion events map to + */ + u32 armv7_evt_type; +}; + +static const struct scorpion_evt scorpion_event[] = { + {SCORPION_ICACHE_EXPL_INV, 0x80000500, 0, 0x4d}, + {SCORPION_ICACHE_MISS, 0x80050000, 0, 0x4e}, + {SCORPION_ICACHE_ACCESS, 0x85000000, 0, 0x4f}, + {SCORPION_ICACHE_CACHEREQ_L2, 0x86000000, 0, 0x4f}, + {SCORPION_ICACHE_NOCACHE_L2, 0x87000000, 0, 0x4f}, + {SCORPION_HIQUP_NOPED, 0x80080000, 0, 0x4e}, + {SCORPION_DATA_ABORT, 0x8000000a, 0, 0x4c}, + {SCORPION_IRQ, 0x80000a00, 0, 0x4d}, + {SCORPION_FIQ, 0x800a0000, 0, 0x4e}, + {SCORPION_ALL_EXCPT, 0x8a000000, 0, 0x4f}, + {SCORPION_UNDEF, 0x8000000b, 0, 0x4c}, + {SCORPION_SVC, 0x80000b00, 0, 0x4d}, + {SCORPION_SMC, 0x800b0000, 0, 0x4e}, + {SCORPION_PREFETCH_ABORT, 0x8b000000, 0, 0x4f}, + {SCORPION_INDEX_CHECK, 0x8000000c, 0, 0x4c}, + {SCORPION_NULL_CHECK, 0x80000c00, 0, 0x4d}, + {SCORPION_ICIMVAU_IMPL_ICIALLU, 0x8000000d, 0, 0x4c}, + {SCORPION_NONICIALLU_BTAC_INV, 0x80000d00, 0, 0x4d}, + {SCORPION_IMPL_ICIALLU, 0x800d0000, 0, 0x4e}, + {SCORPION_EXPL_ICIALLU, 0x8d000000, 0, 0x4f}, + + {SCORPION_SPIPE_ONLY_CYCLES, 0x80000600, 1, 0x51}, + {SCORPION_XPIPE_ONLY_CYCLES, 0x80060000, 1, 0x52}, + {SCORPION_DUAL_CYCLES, 0x86000000, 1, 0x53}, + {SCORPION_DISPATCH_ANY_CYCLES, 0x89000000, 1, 0x53}, + {SCORPION_FIFO_FULLBLK_CMT, 0x8000000d, 1, 0x50}, + {SCORPION_FAIL_COND_INST, 0x800d0000, 1, 0x52}, + {SCORPION_PASS_COND_INST, 0x8d000000, 1, 0x53}, + {SCORPION_ALLOW_VU_CLK, 0x8000000e, 1, 0x50}, + {SCORPION_VU_IDLE, 0x80000e00, 1, 0x51}, + {SCORPION_ALLOW_L2_CLK, 0x800e0000, 1, 0x52}, + {SCORPION_L2_IDLE, 0x8e000000, 1, 0x53}, + + {SCORPION_DTLB_IMPL_INV_SCTLR_DACR, 0x80000001, 2, 0x54}, + {SCORPION_DTLB_EXPL_INV, 0x80000100, 2, 0x55}, + {SCORPION_DTLB_MISS, 0x80010000, 2, 0x56}, + {SCORPION_DTLB_ACCESS, 0x81000000, 2, 0x57}, + {SCORPION_ITLB_MISS, 0x80000200, 2, 0x55}, + {SCORPION_ITLB_IMPL_INV, 0x80020000, 2, 0x56}, + {SCORPION_ITLB_EXPL_INV, 0x82000000, 2, 0x57}, + {SCORPION_UTLB_D_MISS, 0x80000003, 2, 0x54}, + {SCORPION_UTLB_D_ACCESS, 0x80000300, 2, 0x55}, + {SCORPION_UTLB_I_MISS, 0x80030000, 2, 0x56}, + {SCORPION_UTLB_I_ACCESS, 0x83000000, 2, 0x57}, + {SCORPION_UTLB_INV_ASID, 0x80000400, 2, 0x55}, + {SCORPION_UTLB_INV_MVA, 0x80040000, 2, 0x56}, + {SCORPION_UTLB_INV_ALL, 0x84000000, 2, 0x57}, + {SCORPION_S2_HOLD_RDQ_UNAVAIL, 0x80000800, 2, 0x55}, + {SCORPION_S2_HOLD, 0x88000000, 2, 0x57}, + {SCORPION_S2_HOLD_DEV_OP, 0x80000900, 2, 0x55}, + {SCORPION_S2_HOLD_ORDER, 0x80090000, 2, 0x56}, + {SCORPION_S2_HOLD_BARRIER, 0x89000000, 2, 0x57}, + + {SCORPION_VIU_DUAL_CYCLE, 0x80000001, 4, 0x5c}, + {SCORPION_VIU_SINGLE_CYCLE, 0x80000100, 4, 0x5d}, + {SCORPION_VX_PIPE_WAR_STALL_CYCLES, 0x80000005, 4, 0x5c}, + {SCORPION_VX_PIPE_WAW_STALL_CYCLES, 0x80000500, 4, 0x5d}, + {SCORPION_VX_PIPE_RAW_STALL_CYCLES, 0x80050000, 4, 0x5e}, + {SCORPION_VX_PIPE_LOAD_USE_STALL, 0x80000007, 4, 0x5c}, + {SCORPION_VS_PIPE_WAR_STALL_CYCLES, 0x80000008, 4, 0x5c}, + {SCORPION_VS_PIPE_WAW_STALL_CYCLES, 0x80000800, 4, 0x5d}, + {SCORPION_VS_PIPE_RAW_STALL_CYCLES, 0x80080000, 4, 0x5e}, + {SCORPION_EXCEPTIONS_INV_OPERATION, 0x8000000b, 4, 0x5c}, + {SCORPION_EXCEPTIONS_DIV_BY_ZERO, 0x80000b00, 4, 0x5d}, + {SCORPION_COND_INST_FAIL_VX_PIPE, 0x800b0000, 4, 0x5e}, + {SCORPION_COND_INST_FAIL_VS_PIPE, 0x8b000000, 4, 0x5f}, + {SCORPION_EXCEPTIONS_OVERFLOW, 0x8000000c, 4, 0x5c}, + {SCORPION_EXCEPTIONS_UNDERFLOW, 0x80000c00, 4, 0x5d}, + {SCORPION_EXCEPTIONS_DENORM, 0x8c000000, 4, 0x5f}, + +#ifdef CONFIG_MSM_SMP + {SCORPIONMP_NUM_BARRIERS, 0x80000e00, 3, 0x59}, + {SCORPIONMP_BARRIER_CYCLES, 0x800e0000, 3, 0x5a}, +#else + {SCORPION_BANK_AB_HIT, 0x80000001, 3, 0x58}, + {SCORPION_BANK_AB_ACCESS, 0x80000100, 3, 0x59}, + {SCORPION_BANK_CD_HIT, 0x80010000, 3, 0x5a}, + {SCORPION_BANK_CD_ACCESS, 0x81000000, 3, 0x5b}, + {SCORPION_BANK_AB_DSIDE_HIT, 0x80000002, 3, 0x58}, + {SCORPION_BANK_AB_DSIDE_ACCESS, 0x80000200, 3, 0x59}, + {SCORPION_BANK_CD_DSIDE_HIT, 0x80020000, 3, 0x5a}, + {SCORPION_BANK_CD_DSIDE_ACCESS, 0x82000000, 3, 0x5b}, + {SCORPION_BANK_AB_ISIDE_HIT, 0x80000003, 3, 0x58}, + {SCORPION_BANK_AB_ISIDE_ACCESS, 0x80000300, 3, 0x59}, + {SCORPION_BANK_CD_ISIDE_HIT, 0x80030000, 3, 0x5a}, + {SCORPION_BANK_CD_ISIDE_ACCESS, 0x83000000, 3, 0x5b}, + {SCORPION_ISIDE_RD_WAIT, 0x80000009, 3, 0x58}, + {SCORPION_DSIDE_RD_WAIT, 0x80090000, 3, 0x5a}, + {SCORPION_BANK_BYPASS_WRITE, 0x8000000a, 3, 0x58}, + {SCORPION_BANK_AB_NON_CASTOUT, 0x8000000c, 3, 0x58}, + {SCORPION_BANK_AB_L2_CASTOUT, 0x80000c00, 3, 0x59}, + {SCORPION_BANK_CD_NON_CASTOUT, 0x800c0000, 3, 0x5a}, + {SCORPION_BANK_CD_L2_CASTOUT, 0x8c000000, 3, 0x5b}, +#endif +}; + +static unsigned int get_scorpion_evtinfo(unsigned int scorpion_evt_type, + struct scorpion_evt *evtinfo) +{ + u32 idx; + + if (scorpion_evt_type < SCORPION_EVT_START_IDX || scorpion_evt_type >= + (ARRAY_SIZE(scorpion_event) + SCORPION_EVT_START_IDX)) + return -EINVAL; + idx = scorpion_evt_type - SCORPION_EVT_START_IDX; + if (scorpion_event[idx].scorpion_evt_type == scorpion_evt_type) { + evtinfo->group_setval = scorpion_event[idx].group_setval; + evtinfo->groupcode = scorpion_event[idx].groupcode; + evtinfo->armv7_evt_type = scorpion_event[idx].armv7_evt_type; + return scorpion_event[idx].armv7_evt_type; + } + return -EINVAL; +} + +static u32 scorpion_read_lpm0(void) +{ + u32 val; + + asm volatile("mrc p15, 0, %0, c15, c0, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_lpm0(u32 val) +{ + asm volatile("mcr p15, 0, %0, c15, c0, 0" : : "r" (val)); +} + +static u32 scorpion_read_lpm1(void) +{ + u32 val; + + asm volatile("mrc p15, 1, %0, c15, c0, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_lpm1(u32 val) +{ + asm volatile("mcr p15, 1, %0, c15, c0, 0" : : "r" (val)); +} + +static u32 scorpion_read_lpm2(void) +{ + u32 val; + + asm volatile("mrc p15, 2, %0, c15, c0, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_lpm2(u32 val) +{ + asm volatile("mcr p15, 2, %0, c15, c0, 0" : : "r" (val)); +} + +static u32 scorpion_read_l2lpm(void) +{ + u32 val; + + asm volatile("mrc p15, 3, %0, c15, c2, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_l2lpm(u32 val) +{ + asm volatile("mcr p15, 3, %0, c15, c2, 0" : : "r" (val)); +} + +static u32 scorpion_read_vlpm(void) +{ + u32 val; + + asm volatile("mrc p10, 7, %0, c11, c0, 0" : "=r" (val)); + return val; +} + +static void scorpion_write_vlpm(u32 val) +{ + asm volatile("mcr p10, 7, %0, c11, c0, 0" : : "r" (val)); +} + +/* + * The Scorpion processor supports performance monitoring for Venum unit. + * In order to access the performance monitor registers corresponding to + * VFP, CPACR and FPEXC registers need to be set up beforehand. + * Also, they need to be recovered once the access is done. + * This is the reason for having pre and post functions + */ + +static DEFINE_PER_CPU(u32, venum_orig_val); +static DEFINE_PER_CPU(u32, fp_orig_val); + +static void scorpion_pre_vlpm(void) +{ + u32 venum_new_val; + u32 fp_new_val; + + /* CPACR Enable CP10 access*/ + venum_orig_val = get_copro_access(); + venum_new_val = venum_orig_val | CPACC_SVC(10); + set_copro_access(venum_new_val); + /* Enable FPEXC */ + fp_orig_val = fmrx(FPEXC); + fp_new_val = fp_orig_val | FPEXC_EN; + fmxr(FPEXC, fp_new_val); +} + +static void scorpion_post_vlpm(void) +{ + /* Restore FPEXC*/ + fmxr(FPEXC, fp_orig_val); + isb(); + /* Restore CPACR*/ + set_copro_access(venum_orig_val); +} + +struct scorpion_access_funcs { + u32 (*read) (void); + void (*write) (u32); + void (*pre) (void); + void (*post) (void); +}; + +/* + * The scorpion_functions array is used to set up the event register codes + * based on the group to which an event belongs to. + * Having the following array modularizes the code for doing that. + */ +struct scorpion_access_funcs scorpion_functions[] = { + {scorpion_read_lpm0, scorpion_write_lpm0, NULL, NULL}, + {scorpion_read_lpm1, scorpion_write_lpm1, NULL, NULL}, + {scorpion_read_lpm2, scorpion_write_lpm2, NULL, NULL}, + {scorpion_read_l2lpm, scorpion_write_l2lpm, NULL, NULL}, + {scorpion_read_vlpm, scorpion_write_vlpm, scorpion_pre_vlpm, + scorpion_post_vlpm}, +}; + +static inline u32 scorpion_get_columnmask(u32 evt_code) +{ + const u32 columnmasks[] = {0xffffff00, 0xffff00ff, 0xff00ffff, + 0x80ffffff}; + + return columnmasks[evt_code & 0x3]; +} + +static void scorpion_evt_setup(u32 gr, u32 setval, u32 evt_code) +{ + u32 val; + + if (scorpion_functions[gr].pre) + scorpion_functions[gr].pre(); + val = scorpion_get_columnmask(evt_code) & scorpion_functions[gr].read(); + val = val | setval; + scorpion_functions[gr].write(val); + if (scorpion_functions[gr].post) + scorpion_functions[gr].post(); +} + +static void scorpion_clear_pmuregs(void) +{ + unsigned long flags; + + scorpion_write_lpm0(0); + scorpion_write_lpm1(0); + scorpion_write_lpm2(0); + scorpion_write_l2lpm(0); + raw_spin_lock_irqsave(&pmu_lock, flags); + scorpion_pre_vlpm(); + scorpion_write_vlpm(0); + scorpion_post_vlpm(); + raw_spin_unlock_irqrestore(&pmu_lock, flags); +} + +static void scorpion_clearpmu(u32 grp, u32 val, u32 evt_code) +{ + u32 orig_pmuval, new_pmuval; + + if (scorpion_functions[grp].pre) + scorpion_functions[grp].pre(); + orig_pmuval = scorpion_functions[grp].read(); + val = val & ~scorpion_get_columnmask(evt_code); + new_pmuval = orig_pmuval & ~val; + scorpion_functions[grp].write(new_pmuval); + if (scorpion_functions[grp].post) + scorpion_functions[grp].post(); +} + +static void scorpion_pmu_disable_event(struct hw_perf_event *hwc, int idx) +{ + unsigned long flags; + u32 val = 0; + u32 gr; + unsigned long event; + struct scorpion_evt evtinfo; + + /* Disable counter and interrupt */ + raw_spin_lock_irqsave(&pmu_lock, flags); + + /* Disable counter */ + armv7_pmnc_disable_counter(idx); + + /* + * Clear lpm code (if destined for PMNx counters) + * We don't need to set the event if it's a cycle count + */ + if (idx != ARMV7_CYCLE_COUNTER) { + val = hwc->config_base; + val &= ARMV7_EVTSEL_MASK; + if (val > 0x40) { + event = get_scorpion_evtinfo(val, &evtinfo); + if (event == -EINVAL) + goto scorpion_dis_out; + val = evtinfo.group_setval; + gr = evtinfo.groupcode; + scorpion_clearpmu(gr, val, evtinfo.armv7_evt_type); + } + } + /* Disable interrupt for this counter */ + armv7_pmnc_disable_intens(idx); + +scorpion_dis_out: + raw_spin_unlock_irqrestore(&pmu_lock, flags); +} + +static void scorpion_pmu_enable_event(struct hw_perf_event *hwc, int idx) +{ + unsigned long flags; + u32 val = 0; + u32 gr; + unsigned long event; + struct scorpion_evt evtinfo; + + /* + * Enable counter and interrupt, and set the counter to count + * the event that we're interested in. + */ + raw_spin_lock_irqsave(&pmu_lock, flags); + + /* Disable counter */ + armv7_pmnc_disable_counter(idx); + + /* + * Set event (if destined for PMNx counters) + * We don't need to set the event if it's a cycle count + */ + if (idx != ARMV7_CYCLE_COUNTER) { + val = hwc->config_base; + val &= ARMV7_EVTSEL_MASK; + if (val < 0x40) { + armv7_pmnc_write_evtsel(idx, hwc->config_base); + } else { + event = get_scorpion_evtinfo(val, &evtinfo); + + if (event == -EINVAL) + goto scorpion_out; + /* + * Set event (if destined for PMNx counters) + * We don't need to set the event if it's a cycle count + */ + armv7_pmnc_write_evtsel(idx, event); + val = 0x0; + asm volatile("mcr p15, 0, %0, c9, c15, 0" : : + "r" (val)); + val = evtinfo.group_setval; + gr = evtinfo.groupcode; + scorpion_evt_setup(gr, val, evtinfo.armv7_evt_type); + } + } + + /* Enable interrupt for this counter */ + armv7_pmnc_enable_intens(idx); + + /* Enable counter */ + armv7_pmnc_enable_counter(idx); + +scorpion_out: + raw_spin_unlock_irqrestore(&pmu_lock, flags); +} + +#ifdef CONFIG_SMP +static void scorpion_secondary_enable_callback(void *info) +{ + int irq = *(unsigned int *)info; + + if (get_irq_chip(irq)->irq_unmask) + get_irq_chip(irq)->irq_unmask(irq_get_irq_data(irq)); +} +static void scorpion_secondary_disable_callback(void *info) +{ + int irq = *(unsigned int *)info; + + if (get_irq_chip(irq)->irq_mask) + get_irq_chip(irq)->irq_mask(irq_get_irq_data(irq)); +} + +static void scorpion_secondary_enable(unsigned int irq) +{ + smp_call_function(scorpion_secondary_enable_callback, &irq, 1); +} + +static void scorpion_secondary_disable(unsigned int irq) +{ + smp_call_function(scorpion_secondary_disable_callback, &irq, 1); +} +#endif + +static struct arm_pmu scorpion_pmu = { + .handle_irq = armv7pmu_handle_irq, +#ifdef CONFIG_SMP + .secondary_enable = scorpion_secondary_enable, + .secondary_disable = scorpion_secondary_disable, +#endif + .enable = scorpion_pmu_enable_event, + .disable = scorpion_pmu_disable_event, + .read_counter = armv7pmu_read_counter, + .write_counter = armv7pmu_write_counter, + .raw_event_mask = 0xFF, + .get_event_idx = armv7pmu_get_event_idx, + .start = armv7pmu_start, + .stop = armv7pmu_stop, + .max_period = (1LLU << 32) - 1, +}; + +static const struct arm_pmu *__init armv7_scorpion_pmu_init(void) +{ + scorpion_pmu.id = ARM_PERF_PMU_ID_SCORPION; + scorpion_pmu.name = "ARMv7 Scorpion"; + scorpion_pmu.cache_map = &armv7_scorpion_perf_cache_map; + scorpion_pmu.event_map = &armv7_scorpion_perf_map; + scorpion_pmu.num_events = armv7_reset_read_pmnc(); + scorpion_clear_pmuregs(); + return &scorpion_pmu; +} + +static const struct arm_pmu *__init armv7_scorpionmp_pmu_init(void) +{ + scorpion_pmu.id = ARM_PERF_PMU_ID_SCORPIONMP; + scorpion_pmu.name = "ARMv7 Scorpion-MP"; + scorpion_pmu.cache_map = &armv7_scorpion_perf_cache_map; + scorpion_pmu.event_map = &armv7_scorpion_perf_map; + scorpion_pmu.num_events = armv7_reset_read_pmnc(); + scorpion_clear_pmuregs(); + return &scorpion_pmu; +} +#else +static const struct arm_pmu *__init armv7_scorpion_pmu_init(void) +{ + return NULL; +} +static const struct arm_pmu *__init armv7_scorpionmp_pmu_init(void) +{ + return NULL; +} +#endif /* CONFIG_CPU_V7 */ diff --git a/arch/arm/kernel/perf_event_msm_krait.c b/arch/arm/kernel/perf_event_msm_krait.c new file mode 100644 index 0000000000000..8c0f09c1ab241 --- /dev/null +++ b/arch/arm/kernel/perf_event_msm_krait.c @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +#ifdef CONFIG_CPU_V7 +#define KRAIT_EVT_PREFIX 1 +#define KRAIT_MAX_L1_REG 2 +/* + event encoding: prccg + p = prefix (1 for Krait L1) + r = register + cc = code + g = group +*/ +#define KRAIT_L1_ICACHE_MISS 0x10010 +#define KRAIT_L1_ICACHE_ACCESS 0x10011 +#define KRAIT_DTLB_ACCESS 0x121B2 +#define KRAIT_ITLB_ACCESS 0x121C0 + +u32 evt_type_base[] = {0x4c, 0x50, 0x54}; + +static const unsigned armv7_krait_perf_map[PERF_COUNT_HW_MAX] = { + [PERF_COUNT_HW_CPU_CYCLES] = ARMV7_PERFCTR_CPU_CYCLES, + [PERF_COUNT_HW_INSTRUCTIONS] = ARMV7_PERFCTR_INSTR_EXECUTED, + [PERF_COUNT_HW_CACHE_REFERENCES] = HW_OP_UNSUPPORTED, + [PERF_COUNT_HW_CACHE_MISSES] = HW_OP_UNSUPPORTED, + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV7_PERFCTR_PC_WRITE, + [PERF_COUNT_HW_BRANCH_MISSES] = ARMV7_PERFCTR_PC_BRANCH_MIS_PRED, + [PERF_COUNT_HW_BUS_CYCLES] = ARMV7_PERFCTR_CLOCK_CYCLES, +}; + +static const unsigned armv7_krait_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX] = { + [C(L1D)] = { + /* + * The performance counters don't differentiate between read + * and write accesses/misses so this isn't strictly correct, + * but it's the best we can do. Writes and reads get + * combined. + */ + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = ARMV7_PERFCTR_DCACHE_ACCESS, + [C(RESULT_MISS)] = ARMV7_PERFCTR_DCACHE_REFILL, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = ARMV7_PERFCTR_DCACHE_ACCESS, + [C(RESULT_MISS)] = ARMV7_PERFCTR_DCACHE_REFILL, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + }, + [C(L1I)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = KRAIT_L1_ICACHE_ACCESS, + [C(RESULT_MISS)] = KRAIT_L1_ICACHE_MISS, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = KRAIT_L1_ICACHE_ACCESS, + [C(RESULT_MISS)] = KRAIT_L1_ICACHE_MISS, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + }, + [C(LL)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + }, + [C(DTLB)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = KRAIT_DTLB_ACCESS, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = KRAIT_DTLB_ACCESS, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + }, + [C(ITLB)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = KRAIT_ITLB_ACCESS, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = KRAIT_ITLB_ACCESS, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + }, + [C(BPU)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] + = ARMV7_PERFCTR_PC_BRANCH_MIS_USED, + [C(RESULT_MISS)] + = ARMV7_PERFCTR_PC_BRANCH_MIS_USED, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] + = ARMV7_PERFCTR_PC_BRANCH_MIS_USED, + [C(RESULT_MISS)] + = ARMV7_PERFCTR_PC_BRANCH_MIS_USED, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED, + [C(RESULT_MISS)] = CACHE_OP_UNSUPPORTED, + }, + }, +}; + +struct krait_evt { + /* + * The group_setval field corresponds to the value that the group + * register needs to be set to. This value is calculated from the row + * and column that the event belongs to in the event table + */ + u32 group_setval; + + /* + * The groupcode corresponds to the group that the event belongs to. + * Krait has 3 groups of events PMRESR0, 1, 2 + * going from 0 to 2 in terms of the codes used + */ + u8 groupcode; + + /* + * The armv7_evt_type field corresponds to the armv7 defined event + * code that the Krait events map to + */ + u32 armv7_evt_type; +}; + +static unsigned int get_krait_evtinfo(unsigned int krait_evt_type, + struct krait_evt *evtinfo) +{ + u8 prefix; + u8 reg; + u8 code; + u8 group; + + prefix = (krait_evt_type & 0xF0000) >> 16; + reg = (krait_evt_type & 0x0F000) >> 12; + code = (krait_evt_type & 0x00FF0) >> 4; + group = krait_evt_type & 0x0000F; + + if ((prefix != KRAIT_EVT_PREFIX) || (group > 3) || + (reg > KRAIT_MAX_L1_REG)) + return -EINVAL; + + evtinfo->group_setval = 0x80000000 | (code << (group * 8)); + evtinfo->groupcode = reg; + evtinfo->armv7_evt_type = evt_type_base[reg] | group; + + return evtinfo->armv7_evt_type; +} + +static u32 krait_read_pmresr0(void) +{ + u32 val; + + asm volatile("mrc p15, 1, %0, c9, c15, 0" : "=r" (val)); + return val; +} + +static void krait_write_pmresr0(u32 val) +{ + asm volatile("mcr p15, 1, %0, c9, c15, 0" : : "r" (val)); +} + +static u32 krait_read_pmresr1(void) +{ + u32 val; + + asm volatile("mrc p15, 1, %0, c9, c15, 1" : "=r" (val)); + return val; +} + +static void krait_write_pmresr1(u32 val) +{ + asm volatile("mcr p15, 1, %0, c9, c15, 1" : : "r" (val)); +} + +static u32 krait_read_pmresr2(void) +{ + u32 val; + + asm volatile("mrc p15, 1, %0, c9, c15, 2" : "=r" (val)); + return val; +} + +static void krait_write_pmresr2(u32 val) +{ + asm volatile("mcr p15, 1, %0, c9, c15, 2" : : "r" (val)); +} + +struct krait_access_funcs { + u32 (*read) (void); + void (*write) (u32); +}; + +/* + * The krait_functions array is used to set up the event register codes + * based on the group to which an event belongs. + * Having the following array modularizes the code for doing that. + */ +struct krait_access_funcs krait_functions[] = { + {krait_read_pmresr0, krait_write_pmresr0}, + {krait_read_pmresr1, krait_write_pmresr1}, + {krait_read_pmresr2, krait_write_pmresr2}, +}; + +static inline u32 krait_get_columnmask(u32 evt_code) +{ + const u32 columnmasks[] = {0xffffff00, 0xffff00ff, 0xff00ffff, + 0x80ffffff}; + + return columnmasks[evt_code & 0x3]; +} + +static void krait_evt_setup(u32 gr, u32 setval, u32 evt_code) +{ + u32 val; + + val = krait_get_columnmask(evt_code) & krait_functions[gr].read(); + val = val | setval; + krait_functions[gr].write(val); +} + +static void krait_clear_pmuregs(void) +{ + krait_write_pmresr0(0); + krait_write_pmresr1(0); + krait_write_pmresr2(0); +} + +static void krait_clearpmu(u32 grp, u32 val, u32 evt_code) +{ + u32 new_pmuval; + + new_pmuval = krait_functions[grp].read() & + krait_get_columnmask(evt_code); + krait_functions[grp].write(new_pmuval); +} + +static void krait_pmu_disable_event(struct hw_perf_event *hwc, int idx) +{ + unsigned long flags; + u32 val = 0; + u32 gr; + unsigned long event; + struct krait_evt evtinfo; + + /* Disable counter and interrupt */ + raw_spin_lock_irqsave(&pmu_lock, flags); + + /* Disable counter */ + armv7_pmnc_disable_counter(idx); + + /* + * Clear pmresr code (if destined for PMNx counters) + * We don't need to set the event if it's a cycle count + */ + if (idx != ARMV7_CYCLE_COUNTER) { + val = hwc->config_base; + if (val > 0x40) { + event = get_krait_evtinfo(val, &evtinfo); + if (event == -EINVAL) + goto krait_dis_out; + val = evtinfo.group_setval; + gr = evtinfo.groupcode; + krait_clearpmu(gr, val, evtinfo.armv7_evt_type); + } + } + /* Disable interrupt for this counter */ + armv7_pmnc_disable_intens(idx); + +krait_dis_out: + raw_spin_unlock_irqrestore(&pmu_lock, flags); +} + +static void krait_pmu_enable_event(struct hw_perf_event *hwc, int idx) +{ + unsigned long flags; + u32 val = 0; + u32 gr; + unsigned long event; + struct krait_evt evtinfo; + + /* + * Enable counter and interrupt, and set the counter to count + * the event that we're interested in. + */ + raw_spin_lock_irqsave(&pmu_lock, flags); + + /* Disable counter */ + armv7_pmnc_disable_counter(idx); + + /* + * Set event (if destined for PMNx counters) + * We don't need to set the event if it's a cycle count + */ + if (idx != ARMV7_CYCLE_COUNTER) { + val = hwc->config_base; + if (val < 0x40) { + armv7_pmnc_write_evtsel(idx, hwc->config_base); + } else { + event = get_krait_evtinfo(val, &evtinfo); + + if (event == -EINVAL) + goto krait_out; + /* + * Set event (if destined for PMNx counters) + * We don't need to set the event if it's a cycle count + */ + armv7_pmnc_write_evtsel(idx, event); + val = 0x0; + asm volatile("mcr p15, 0, %0, c9, c15, 0" : : + "r" (val)); + val = evtinfo.group_setval; + gr = evtinfo.groupcode; + krait_evt_setup(gr, val, evtinfo.armv7_evt_type); + } + } + + /* Enable interrupt for this counter */ + armv7_pmnc_enable_intens(idx); + + /* Enable counter */ + armv7_pmnc_enable_counter(idx); + +krait_out: + raw_spin_unlock_irqrestore(&pmu_lock, flags); +} + +static struct arm_pmu krait_pmu = { + .handle_irq = armv7pmu_handle_irq, +#ifdef CONFIG_ARCH_MSM_SMP + .secondary_enable = scorpion_secondary_enable, + .secondary_disable = scorpion_secondary_disable, +#endif + .enable = krait_pmu_enable_event, + .disable = krait_pmu_disable_event, + .read_counter = armv7pmu_read_counter, + .write_counter = armv7pmu_write_counter, + .raw_event_mask = 0xFFFFF, + .get_event_idx = armv7pmu_get_event_idx, + .start = armv7pmu_start, + .stop = armv7pmu_stop, + .max_period = (1LLU << 32) - 1, +}; + +static const struct arm_pmu *__init armv7_krait_pmu_init(void) +{ + krait_pmu.id = ARM_PERF_PMU_ID_KRAIT; + krait_pmu.name = "ARMv7 Krait"; + krait_pmu.cache_map = &armv7_krait_perf_cache_map; + krait_pmu.event_map = &armv7_krait_perf_map; + krait_pmu.num_events = armv7_reset_read_pmnc(); + krait_clear_pmuregs(); + return &krait_pmu; +} + +#else +static const struct arm_pmu *__init armv7_krait_pmu_init(void) +{ + return NULL; +} +#endif /* CONFIG_CPU_V7 */ diff --git a/arch/arm/kernel/perf_event_msm_krait_l2.c b/arch/arm/kernel/perf_event_msm_krait_l2.c new file mode 100644 index 0000000000000..97b81b6d14f48 --- /dev/null +++ b/arch/arm/kernel/perf_event_msm_krait_l2.c @@ -0,0 +1,649 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifdef CONFIG_CPU_HAS_L2_PMU + +#include + +#include + +#define MAX_L2_PERIOD ((1ULL << 32) - 1) +#define MAX_KRAIT_L2_CTRS 5 + +#define L2PMCCNTR 0x409 +#define L2PMCCNTCR 0x408 +#define L2PMCCNTSR 0x40A +#define L2CYCLE_CTR_BIT 31 +#define L2CYCLE_CTR_EVENT_IDX 4 +#define L2CYCLE_CTR_RAW_CODE 0xfe + +#define L2PMOVSR 0x406 + +#define L2PMCR 0x400 +#define L2PMCR_RESET_ALL 0x6 +#define L2PMCR_GLOBAL_ENABLE 0x1 +#define L2PMCR_GLOBAL_DISABLE 0x0 + +#define L2PMCNTENSET 0x403 +#define L2PMCNTENCLR 0x402 + +#define L2PMINTENSET 0x405 +#define L2PMINTENCLR 0x404 + +#define IA_L2PMXEVCNTCR_BASE 0x420 +#define IA_L2PMXEVTYPER_BASE 0x424 +#define IA_L2PMRESX_BASE 0x410 +#define IA_L2PMXEVFILTER_BASE 0x423 +#define IA_L2PMXEVCNTR_BASE 0x421 + +/* event format is -e rsRCCG See get_event_desc() */ + +#define EVENT_REG_MASK 0xf000 +#define EVENT_GROUPSEL_MASK 0x000f +#define EVENT_GROUPCODE_MASK 0x0ff0 +#define EVENT_REG_SHIFT 12 +#define EVENT_GROUPCODE_SHIFT 4 + +#define RESRX_VALUE_EN 0x80000000 + +static struct platform_device *l2_pmu_device; + +struct hw_krait_l2_pmu { + struct perf_event *events[MAX_KRAIT_L2_CTRS]; + unsigned long active_mask[BITS_TO_LONGS(MAX_KRAIT_L2_CTRS)]; + raw_spinlock_t lock; +}; + +struct hw_krait_l2_pmu hw_krait_l2_pmu; + +struct event_desc { + int event_groupsel; + int event_reg; + int event_group_code; +}; + +void get_event_desc(u64 config, struct event_desc *evdesc) +{ + /* L2PMEVCNTRX */ + evdesc->event_reg = (config & EVENT_REG_MASK) >> EVENT_REG_SHIFT; + /* Group code (row ) */ + evdesc->event_group_code = + (config & EVENT_GROUPCODE_MASK) >> EVENT_GROUPCODE_SHIFT; + /* Group sel (col) */ + evdesc->event_groupsel = (config & EVENT_GROUPSEL_MASK); + + pr_debug("%s: reg: %x, group_code: %x, groupsel: %x\n", __func__, + evdesc->event_reg, evdesc->event_group_code, + evdesc->event_groupsel); +} + +static void set_evcntcr(int ctr) +{ + u32 evtcr_reg = (ctr * 16) + IA_L2PMXEVCNTCR_BASE; + + set_l2_indirect_reg(evtcr_reg, 0x0); +} + +static void set_evtyper(int event_groupsel, int event_reg, int ctr) +{ + u32 evtype_reg = (ctr * 16) + IA_L2PMXEVTYPER_BASE; + u32 evtype_val = event_groupsel + (4 * event_reg); + + set_l2_indirect_reg(evtype_reg, evtype_val); +} + +static void set_evres(int event_groupsel, int event_reg, int event_group_code) +{ + u32 group_reg = event_reg + IA_L2PMRESX_BASE; + u32 group_val = + RESRX_VALUE_EN | (event_group_code << (8 * event_groupsel)); + u32 resr_val; + u32 group_byte = 0xff; + u32 group_mask = ~(group_byte << (8 * event_groupsel)); + + resr_val = get_l2_indirect_reg(group_reg); + resr_val &= group_mask; + resr_val |= group_val; + + set_l2_indirect_reg(group_reg, resr_val); +} + +static void set_evfilter(int ctr) +{ + u32 filter_reg = (ctr * 16) + IA_L2PMXEVFILTER_BASE; + u32 filter_val = 0x000f0030 | 1 << smp_processor_id(); + + set_l2_indirect_reg(filter_reg, filter_val); +} + +static void enable_intenset(u32 idx) +{ + if (idx == L2CYCLE_CTR_EVENT_IDX) + set_l2_indirect_reg(L2PMINTENSET, 1 << L2CYCLE_CTR_BIT); + else + set_l2_indirect_reg(L2PMINTENSET, 1 << idx); +} + +static void disable_intenclr(u32 idx) +{ + if (idx == L2CYCLE_CTR_EVENT_IDX) + set_l2_indirect_reg(L2PMINTENCLR, 1 << L2CYCLE_CTR_BIT); + else + set_l2_indirect_reg(L2PMINTENCLR, 1 << idx); +} + +static void enable_counter(u32 idx) +{ + if (idx == L2CYCLE_CTR_EVENT_IDX) + set_l2_indirect_reg(L2PMCNTENSET, 1 << L2CYCLE_CTR_BIT); + else + set_l2_indirect_reg(L2PMCNTENSET, 1 << idx); +} + +static void disable_counter(u32 idx) +{ + if (idx == L2CYCLE_CTR_EVENT_IDX) + set_l2_indirect_reg(L2PMCNTENCLR, 1 << L2CYCLE_CTR_BIT); + else + set_l2_indirect_reg(L2PMCNTENCLR, 1 << idx); +} + +static u64 read_counter(u32 idx) +{ + u32 val; + u32 counter_reg = (idx * 16) + IA_L2PMXEVCNTR_BASE; + + if (idx == L2CYCLE_CTR_EVENT_IDX) + val = get_l2_indirect_reg(L2PMCCNTR); + else + val = get_l2_indirect_reg(counter_reg); + + return val; +} + +static void write_counter(u32 idx, u32 val) +{ + u32 counter_reg = (idx * 16) + IA_L2PMXEVCNTR_BASE; + + if (idx == L2CYCLE_CTR_EVENT_IDX) + set_l2_indirect_reg(L2PMCCNTR, val); + else + set_l2_indirect_reg(counter_reg, val); +} + +static int +pmu_event_set_period(struct perf_event *event, + struct hw_perf_event *hwc, int idx) +{ + s64 left = local64_read(&hwc->period_left); + s64 period = hwc->sample_period; + int ret = 0; + + if (unlikely(left <= -period)) { + left = period; + local64_set(&hwc->period_left, left); + hwc->last_period = period; + ret = 1; + } + + if (unlikely(left <= 0)) { + left += period; + local64_set(&hwc->period_left, left); + hwc->last_period = period; + ret = 1; + } + + if (left > (s64) MAX_L2_PERIOD) + left = MAX_L2_PERIOD; + + local64_set(&hwc->prev_count, (u64)-left); + + write_counter(idx, (u64) (-left) & 0xffffffff); + + perf_event_update_userpage(event); + + return ret; +} + +static u64 +pmu_event_update(struct perf_event *event, struct hw_perf_event *hwc, int idx, + int overflow) +{ + u64 prev_raw_count, new_raw_count; + u64 delta; + +again: + prev_raw_count = local64_read(&hwc->prev_count); + new_raw_count = read_counter(idx); + + if (local64_cmpxchg(&hwc->prev_count, prev_raw_count, + new_raw_count) != prev_raw_count) + goto again; + + new_raw_count &= MAX_L2_PERIOD; + prev_raw_count &= MAX_L2_PERIOD; + + if (overflow) + delta = MAX_L2_PERIOD - prev_raw_count + new_raw_count; + else + delta = new_raw_count - prev_raw_count; + + local64_add(delta, &event->count); + local64_sub(delta, &hwc->period_left); + + pr_debug("%s: new: %lld, prev: %lld, event: %ld count: %lld\n", + __func__, new_raw_count, prev_raw_count, + hwc->config_base, local64_read(&event->count)); + + return new_raw_count; +} + +static void krait_l2_read(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + + pmu_event_update(event, hwc, hwc->idx, 0); +} + +static void krait_l2_stop_counter(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + int idx = hwc->idx; + + if (!(hwc->state & PERF_HES_STOPPED)) { + disable_intenclr(idx); + disable_counter(idx); + + pmu_event_update(event, hwc, idx, 0); + hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; + } + + pr_debug("%s: event: %ld ctr: %d stopped\n", __func__, hwc->config_base, + idx); +} + +static void krait_l2_start_counter(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + int idx = hwc->idx; + struct event_desc evdesc; + + if (flags & PERF_EF_RELOAD) + WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE)); + + hwc->state = 0; + + pmu_event_set_period(event, hwc, idx); + + if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) + goto out; + + set_evcntcr(idx); + + memset(&evdesc, 0, sizeof(evdesc)); + + get_event_desc(hwc->config_base, &evdesc); + + set_evtyper(evdesc.event_groupsel, evdesc.event_reg, idx); + + set_evres(evdesc.event_groupsel, evdesc.event_reg, + evdesc.event_group_code); + + set_evfilter(idx); + +out: + enable_intenset(idx); + enable_counter(idx); + + pr_debug + ("%s: ctr: %d group: %ld group_code: %lld started from cpu:%d\n", + __func__, idx, hwc->config_base, hwc->config, smp_processor_id()); +} + +static void krait_l2_del_event(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + int idx = hwc->idx; + unsigned long iflags; + + raw_spin_lock_irqsave(&hw_krait_l2_pmu.lock, iflags); + + clear_bit(idx, (long unsigned int *)(&hw_krait_l2_pmu.active_mask)); + + krait_l2_stop_counter(event, PERF_EF_UPDATE); + hw_krait_l2_pmu.events[idx] = NULL; + hwc->idx = -1; + + raw_spin_unlock_irqrestore(&hw_krait_l2_pmu.lock, iflags); + + pr_debug("%s: event: %ld deleted\n", __func__, hwc->config_base); + + perf_event_update_userpage(event); +} + +static int krait_l2_add_event(struct perf_event *event, int flags) +{ + int ctr = 0; + struct hw_perf_event *hwc = &event->hw; + unsigned long iflags; + int err = 0; + + perf_pmu_disable(event->pmu); + + raw_spin_lock_irqsave(&hw_krait_l2_pmu.lock, iflags); + + /* Cycle counter has a resrvd index */ + if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) { + if (hw_krait_l2_pmu.events[L2CYCLE_CTR_EVENT_IDX]) { + pr_err("%s: Stale cycle ctr event ptr !\n", __func__); + err = -EINVAL; + goto out; + } + hwc->idx = L2CYCLE_CTR_EVENT_IDX; + hw_krait_l2_pmu.events[L2CYCLE_CTR_EVENT_IDX] = event; + set_bit(L2CYCLE_CTR_EVENT_IDX, + (long unsigned int *)&hw_krait_l2_pmu.active_mask); + goto skip_ctr_loop; + } + + for (ctr = 0; ctr < MAX_KRAIT_L2_CTRS - 1; ctr++) { + if (!hw_krait_l2_pmu.events[ctr]) { + hwc->idx = ctr; + hw_krait_l2_pmu.events[ctr] = event; + set_bit(ctr, + (long unsigned int *) + &hw_krait_l2_pmu.active_mask); + break; + } + } + + if (hwc->idx < 0) { + err = -ENOSPC; + pr_err("%s: No space for event: %llx!!\n", __func__, + event->attr.config); + goto out; + } + +skip_ctr_loop: + + disable_counter(hwc->idx); + + hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; + + if (flags & PERF_EF_START) + krait_l2_start_counter(event, PERF_EF_RELOAD); + + perf_event_update_userpage(event); + + pr_debug("%s: event: %ld, ctr: %d added from cpu:%d\n", + __func__, hwc->config_base, hwc->idx, smp_processor_id()); +out: + raw_spin_unlock_irqrestore(&hw_krait_l2_pmu.lock, iflags); + + /* Resume the PMU even if this event could not be added */ + perf_pmu_enable(event->pmu); + + return err; +} + +static void krait_l2_pmu_enable(struct pmu *pmu) +{ + isb(); + set_l2_indirect_reg(L2PMCR, L2PMCR_GLOBAL_ENABLE); +} + +static void krait_l2_pmu_disable(struct pmu *pmu) +{ + set_l2_indirect_reg(L2PMCR, L2PMCR_GLOBAL_DISABLE); + isb(); +} + +u32 get_reset_pmovsr(void) +{ + int val; + + val = get_l2_indirect_reg(L2PMOVSR); + /* reset it */ + val &= 0xffffffff; + set_l2_indirect_reg(L2PMOVSR, val); + + return val; +} + +static irqreturn_t krait_l2_handle_irq(int irq_num, void *dev) +{ + unsigned long pmovsr; + struct perf_sample_data data; + struct pt_regs *regs; + struct perf_event *event; + struct hw_perf_event *hwc; + int bitp; + int idx = 0; + + pmovsr = get_reset_pmovsr(); + + if (!(pmovsr & 0xffffffff)) + return IRQ_NONE; + + regs = get_irq_regs(); + + perf_sample_data_init(&data, 0); + + raw_spin_lock(&hw_krait_l2_pmu.lock); + + while (pmovsr) { + bitp = __ffs(pmovsr); + + if (bitp == L2CYCLE_CTR_BIT) + idx = L2CYCLE_CTR_EVENT_IDX; + else + idx = bitp; + + event = hw_krait_l2_pmu.events[idx]; + + if (!event) + goto next; + + if (!test_bit(idx, hw_krait_l2_pmu.active_mask)) + goto next; + + hwc = &event->hw; + pmu_event_update(event, hwc, idx, 1); + data.period = event->hw.last_period; + + if (!pmu_event_set_period(event, hwc, idx)) + goto next; + + if (perf_event_overflow(event, 0, &data, regs)) + disable_counter(hwc->idx); +next: + pmovsr &= (pmovsr - 1); + } + + raw_spin_unlock(&hw_krait_l2_pmu.lock); + + irq_work_run(); + + return IRQ_HANDLED; +} + +static atomic_t active_l2_events = ATOMIC_INIT(0); +static DEFINE_MUTEX(krait_pmu_reserve_mutex); + +static int pmu_reserve_hardware(void) +{ + int i, err = -ENODEV, irq; + + l2_pmu_device = reserve_pmu(ARM_PMU_DEVICE_L2); + + if (IS_ERR(l2_pmu_device)) { + pr_warning("unable to reserve pmu\n"); + return PTR_ERR(l2_pmu_device); + } + + if (l2_pmu_device->num_resources < 1) { + pr_err("no irqs for PMUs defined\n"); + return -ENODEV; + } + + if (strncmp(l2_pmu_device->name, "l2-arm-pmu", 6)) { + pr_err("Incorrect pdev reserved !\n"); + return -EINVAL; + } + + for (i = 0; i < l2_pmu_device->num_resources; ++i) { + irq = platform_get_irq(l2_pmu_device, i); + if (irq < 0) + continue; + + err = request_irq(irq, krait_l2_handle_irq, + IRQF_DISABLED | IRQF_NOBALANCING, + "krait-l2-pmu", NULL); + if (err) { + pr_warning("unable to request IRQ%d for Krait L2 perf " + "counters\n", irq); + break; + } + + get_irq_chip(irq)->irq_unmask(irq_get_irq_data(irq)); + } + + if (err) { + for (i = i - 1; i >= 0; --i) { + irq = platform_get_irq(l2_pmu_device, i); + if (irq >= 0) + free_irq(irq, NULL); + } + release_pmu(l2_pmu_device); + l2_pmu_device = NULL; + } + + return err; +} + +static void pmu_release_hardware(void) +{ + int i, irq; + + for (i = l2_pmu_device->num_resources - 1; i >= 0; --i) { + irq = platform_get_irq(l2_pmu_device, i); + if (irq >= 0) + free_irq(irq, NULL); + } + + krait_l2_pmu_disable(NULL); + + release_pmu(l2_pmu_device); + l2_pmu_device = NULL; +} + +static void pmu_perf_event_destroy(struct perf_event *event) +{ + if (atomic_dec_and_mutex_lock + (&active_l2_events, &krait_pmu_reserve_mutex)) { + pmu_release_hardware(); + mutex_unlock(&krait_pmu_reserve_mutex); + } +} + +static int krait_l2_event_init(struct perf_event *event) +{ + int err = 0; + struct hw_perf_event *hwc = &event->hw; + int status = 0; + + switch (event->attr.type) { + case PERF_TYPE_SHARED: + break; + + default: + return -ENOENT; + } + + hwc->idx = -1; + + event->destroy = pmu_perf_event_destroy; + + if (!atomic_inc_not_zero(&active_l2_events)) { + /* 0 active events */ + mutex_lock(&krait_pmu_reserve_mutex); + err = pmu_reserve_hardware(); + mutex_unlock(&krait_pmu_reserve_mutex); + if (!err) + atomic_inc(&active_l2_events); + else + return err; + } + + hwc->config_base = event->attr.config; + hwc->config = 0; + hwc->event_base = 0; + + /* Only one CPU can control the cycle counter */ + if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) { + /* Check if its already running */ + status = get_l2_indirect_reg(L2PMCCNTSR); + if (status == 0x2) { + err = -ENOSPC; + goto out; + } + } + + if (!hwc->sample_period) { + hwc->sample_period = MAX_L2_PERIOD; + hwc->last_period = hwc->sample_period; + local64_set(&hwc->period_left, hwc->sample_period); + } + + pr_debug("%s: event: %lld init'd\n", __func__, event->attr.config); + +out: + if (err < 0) + pmu_perf_event_destroy(event); + + return err; +} + +static struct pmu krait_l2_pmu = { + .pmu_enable = krait_l2_pmu_enable, + .pmu_disable = krait_l2_pmu_disable, + .event_init = krait_l2_event_init, + .add = krait_l2_add_event, + .del = krait_l2_del_event, + .start = krait_l2_start_counter, + .stop = krait_l2_stop_counter, + .read = krait_l2_read, +}; + +static const struct arm_pmu *__init krait_l2_pmu_init(void) +{ + /* Register our own PMU here */ + perf_pmu_register(&krait_l2_pmu, "Krait L2", PERF_TYPE_SHARED); + + memset(&hw_krait_l2_pmu, 0, sizeof(hw_krait_l2_pmu)); + + /* Reset all ctrs */ + set_l2_indirect_reg(L2PMCR, L2PMCR_RESET_ALL); + + /* Avoid spurious interrupt if any */ + get_reset_pmovsr(); + + /* Don't return an arm_pmu here */ + return NULL; +} +#else + +static const struct arm_pmu *__init krait_l2_pmu_init(void) +{ + return NULL; +} +#endif diff --git a/arch/arm/kernel/perf_event_msm_l2.c b/arch/arm/kernel/perf_event_msm_l2.c new file mode 100644 index 0000000000000..46733ab1d73b4 --- /dev/null +++ b/arch/arm/kernel/perf_event_msm_l2.c @@ -0,0 +1,974 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifdef CONFIG_CPU_HAS_L2_PMU + +#include + +#define MAX_BB_L2_PERIOD ((1ULL << 32) - 1) +#define MAX_BB_L2_CTRS 5 +#define BB_L2CYCLE_CTR_BIT 31 +#define BB_L2CYCLE_CTR_EVENT_IDX 4 +#define BB_L2CYCLE_CTR_RAW_CODE 0xfe +#define SCORPIONL2_PMNC_E (1 << 0) /* Enable all counters */ + +/* + * Lock to protect r/m/w sequences to the L2 PMU. + */ +DEFINE_RAW_SPINLOCK(bb_l2_pmu_lock); + +static struct platform_device *bb_l2_pmu_device; + +struct hw_bb_l2_pmu { + struct perf_event *events[MAX_BB_L2_CTRS]; + unsigned long active_mask[BITS_TO_LONGS(MAX_BB_L2_CTRS)]; + raw_spinlock_t lock; +}; + +struct hw_bb_l2_pmu hw_bb_l2_pmu; + +struct bb_l2_scorp_evt { + u32 evt_type; + u32 val; + u8 grp; + u32 evt_type_act; +}; + +enum scorpion_perf_types { + SCORPIONL2_TOTAL_BANK_REQ = 0x90, + SCORPIONL2_DSIDE_READ = 0x91, + SCORPIONL2_DSIDE_WRITE = 0x92, + SCORPIONL2_ISIDE_READ = 0x93, + SCORPIONL2_L2CACHE_ISIDE_READ = 0x94, + SCORPIONL2_L2CACHE_BANK_REQ = 0x95, + SCORPIONL2_L2CACHE_DSIDE_READ = 0x96, + SCORPIONL2_L2CACHE_DSIDE_WRITE = 0x97, + SCORPIONL2_L2NOCACHE_DSIDE_WRITE = 0x98, + SCORPIONL2_L2NOCACHE_ISIDE_READ = 0x99, + SCORPIONL2_L2NOCACHE_TOTAL_REQ = 0x9a, + SCORPIONL2_L2NOCACHE_DSIDE_READ = 0x9b, + SCORPIONL2_DSIDE_READ_NOL1 = 0x9c, + SCORPIONL2_L2CACHE_WRITETHROUGH = 0x9d, + SCORPIONL2_BARRIERS = 0x9e, + SCORPIONL2_HARDWARE_TABLE_WALKS = 0x9f, + SCORPIONL2_MVA_POC = 0xa0, + SCORPIONL2_L2CACHE_HW_TABLE_WALKS = 0xa1, + SCORPIONL2_SETWAY_CACHE_OPS = 0xa2, + SCORPIONL2_DSIDE_WRITE_HITS = 0xa3, + SCORPIONL2_ISIDE_READ_HITS = 0xa4, + SCORPIONL2_CACHE_DSIDE_READ_NOL1 = 0xa5, + SCORPIONL2_TOTAL_CACHE_HITS = 0xa6, + SCORPIONL2_CACHE_MATCH_MISS = 0xa7, + SCORPIONL2_DREAD_HIT_L1_DATA = 0xa8, + SCORPIONL2_L2LINE_LOCKED = 0xa9, + SCORPIONL2_HW_TABLE_WALK_HIT = 0xaa, + SCORPIONL2_CACHE_MVA_POC = 0xab, + SCORPIONL2_L2ALLOC_DWRITE_MISS = 0xac, + SCORPIONL2_CORRECTED_TAG_ARRAY = 0xad, + SCORPIONL2_CORRECTED_DATA_ARRAY = 0xae, + SCORPIONL2_CORRECTED_REPLACEMENT_ARRAY = 0xaf, + SCORPIONL2_PMBUS_MPAAF = 0xb0, + SCORPIONL2_PMBUS_MPWDAF = 0xb1, + SCORPIONL2_PMBUS_MPBRT = 0xb2, + SCORPIONL2_CPU0_GRANT = 0xb3, + SCORPIONL2_CPU1_GRANT = 0xb4, + SCORPIONL2_CPU0_NOGRANT = 0xb5, + SCORPIONL2_CPU1_NOGRANT = 0xb6, + SCORPIONL2_CPU0_LOSING_ARB = 0xb7, + SCORPIONL2_CPU1_LOSING_ARB = 0xb8, + SCORPIONL2_SLAVEPORT_NOGRANT = 0xb9, + SCORPIONL2_SLAVEPORT_BPQ_FULL = 0xba, + SCORPIONL2_SLAVEPORT_LOSING_ARB = 0xbb, + SCORPIONL2_SLAVEPORT_GRANT = 0xbc, + SCORPIONL2_SLAVEPORT_GRANTLOCK = 0xbd, + SCORPIONL2_L2EM_STREX_PASS = 0xbe, + SCORPIONL2_L2EM_STREX_FAIL = 0xbf, + SCORPIONL2_LDREX_RESERVE_L2EM = 0xc0, + SCORPIONL2_SLAVEPORT_LDREX = 0xc1, + SCORPIONL2_CPU0_L2EM_CLEARED = 0xc2, + SCORPIONL2_CPU1_L2EM_CLEARED = 0xc3, + SCORPIONL2_SLAVEPORT_L2EM_CLEARED = 0xc4, + SCORPIONL2_CPU0_CLAMPED = 0xc5, + SCORPIONL2_CPU1_CLAMPED = 0xc6, + SCORPIONL2_CPU0_WAIT = 0xc7, + SCORPIONL2_CPU1_WAIT = 0xc8, + SCORPIONL2_CPU0_NONAMBAS_WAIT = 0xc9, + SCORPIONL2_CPU1_NONAMBAS_WAIT = 0xca, + SCORPIONL2_CPU0_DSB_WAIT = 0xcb, + SCORPIONL2_CPU1_DSB_WAIT = 0xcc, + SCORPIONL2_AXI_READ = 0xcd, + SCORPIONL2_AXI_WRITE = 0xce, + + SCORPIONL2_1BEAT_WRITE = 0xcf, + SCORPIONL2_2BEAT_WRITE = 0xd0, + SCORPIONL2_4BEAT_WRITE = 0xd1, + SCORPIONL2_8BEAT_WRITE = 0xd2, + SCORPIONL2_12BEAT_WRITE = 0xd3, + SCORPIONL2_16BEAT_WRITE = 0xd4, + SCORPIONL2_1BEAT_DSIDE_READ = 0xd5, + SCORPIONL2_2BEAT_DSIDE_READ = 0xd6, + SCORPIONL2_4BEAT_DSIDE_READ = 0xd7, + SCORPIONL2_8BEAT_DSIDE_READ = 0xd8, + SCORPIONL2_CSYS_READ_1BEAT = 0xd9, + SCORPIONL2_CSYS_READ_2BEAT = 0xda, + SCORPIONL2_CSYS_READ_4BEAT = 0xdb, + SCORPIONL2_CSYS_READ_8BEAT = 0xdc, + SCORPIONL2_4BEAT_IFETCH_READ = 0xdd, + SCORPIONL2_8BEAT_IFETCH_READ = 0xde, + SCORPIONL2_CSYS_WRITE_1BEAT = 0xdf, + SCORPIONL2_CSYS_WRITE_2BEAT = 0xe0, + SCORPIONL2_AXI_READ_DATA_BEAT = 0xe1, + SCORPIONL2_AXI_WRITE_EVT1 = 0xe2, + SCORPIONL2_AXI_WRITE_EVT2 = 0xe3, + SCORPIONL2_LDREX_REQ = 0xe4, + SCORPIONL2_STREX_PASS = 0xe5, + SCORPIONL2_STREX_FAIL = 0xe6, + SCORPIONL2_CPREAD = 0xe7, + SCORPIONL2_CPWRITE = 0xe8, + SCORPIONL2_BARRIER_REQ = 0xe9, + SCORPIONL2_AXI_READ_SLVPORT = 0xea, + SCORPIONL2_AXI_WRITE_SLVPORT = 0xeb, + SCORPIONL2_AXI_READ_SLVPORT_DATABEAT = 0xec, + SCORPIONL2_AXI_WRITE_SLVPORT_DATABEAT = 0xed, + SCORPIONL2_SNOOPKILL_PREFILTER = 0xee, + SCORPIONL2_SNOOPKILL_FILTEROUT = 0xef, + SCORPIONL2_SNOOPED_IC = 0xf0, + SCORPIONL2_SNOOPED_BP = 0xf1, + SCORPIONL2_SNOOPED_BARRIERS = 0xf2, + SCORPIONL2_SNOOPED_TLB = 0xf3, + BB_L2_MAX_EVT, +}; + +static const struct bb_l2_scorp_evt sc_evt[] = { + {SCORPIONL2_TOTAL_BANK_REQ, 0x80000001, 0, 0x00}, + {SCORPIONL2_DSIDE_READ, 0x80000100, 0, 0x01}, + {SCORPIONL2_DSIDE_WRITE, 0x80010000, 0, 0x02}, + {SCORPIONL2_ISIDE_READ, 0x81000000, 0, 0x03}, + {SCORPIONL2_L2CACHE_ISIDE_READ, 0x80000002, 0, 0x00}, + {SCORPIONL2_L2CACHE_BANK_REQ, 0x80000200, 0, 0x01}, + {SCORPIONL2_L2CACHE_DSIDE_READ, 0x80020000, 0, 0x02}, + {SCORPIONL2_L2CACHE_DSIDE_WRITE, 0x82000000, 0, 0x03}, + {SCORPIONL2_L2NOCACHE_DSIDE_WRITE, 0x80000003, 0, 0x00}, + {SCORPIONL2_L2NOCACHE_ISIDE_READ, 0x80000300, 0, 0x01}, + {SCORPIONL2_L2NOCACHE_TOTAL_REQ, 0x80030000, 0, 0x02}, + {SCORPIONL2_L2NOCACHE_DSIDE_READ, 0x83000000, 0, 0x03}, + {SCORPIONL2_DSIDE_READ_NOL1, 0x80000004, 0, 0x00}, + {SCORPIONL2_L2CACHE_WRITETHROUGH, 0x80000400, 0, 0x01}, + {SCORPIONL2_BARRIERS, 0x84000000, 0, 0x03}, + {SCORPIONL2_HARDWARE_TABLE_WALKS, 0x80000005, 0, 0x00}, + {SCORPIONL2_MVA_POC, 0x80000500, 0, 0x01}, + {SCORPIONL2_L2CACHE_HW_TABLE_WALKS, 0x80050000, 0, 0x02}, + {SCORPIONL2_SETWAY_CACHE_OPS, 0x85000000, 0, 0x03}, + {SCORPIONL2_DSIDE_WRITE_HITS, 0x80000006, 0, 0x00}, + {SCORPIONL2_ISIDE_READ_HITS, 0x80000600, 0, 0x01}, + {SCORPIONL2_CACHE_DSIDE_READ_NOL1, 0x80060000, 0, 0x02}, + {SCORPIONL2_TOTAL_CACHE_HITS, 0x86000000, 0, 0x03}, + {SCORPIONL2_CACHE_MATCH_MISS, 0x80000007, 0, 0x00}, + {SCORPIONL2_DREAD_HIT_L1_DATA, 0x87000000, 0, 0x03}, + {SCORPIONL2_L2LINE_LOCKED, 0x80000008, 0, 0x00}, + {SCORPIONL2_HW_TABLE_WALK_HIT, 0x80000800, 0, 0x01}, + {SCORPIONL2_CACHE_MVA_POC, 0x80080000, 0, 0x02}, + {SCORPIONL2_L2ALLOC_DWRITE_MISS, 0x88000000, 0, 0x03}, + {SCORPIONL2_CORRECTED_TAG_ARRAY, 0x80001A00, 0, 0x01}, + {SCORPIONL2_CORRECTED_DATA_ARRAY, 0x801A0000, 0, 0x02}, + {SCORPIONL2_CORRECTED_REPLACEMENT_ARRAY, 0x9A000000, 0, 0x03}, + {SCORPIONL2_PMBUS_MPAAF, 0x80001C00, 0, 0x01}, + {SCORPIONL2_PMBUS_MPWDAF, 0x801C0000, 0, 0x02}, + {SCORPIONL2_PMBUS_MPBRT, 0x9C000000, 0, 0x03}, + + {SCORPIONL2_CPU0_GRANT, 0x80000001, 1, 0x04}, + {SCORPIONL2_CPU1_GRANT, 0x80000100, 1, 0x05}, + {SCORPIONL2_CPU0_NOGRANT, 0x80020000, 1, 0x06}, + {SCORPIONL2_CPU1_NOGRANT, 0x82000000, 1, 0x07}, + {SCORPIONL2_CPU0_LOSING_ARB, 0x80040000, 1, 0x06}, + {SCORPIONL2_CPU1_LOSING_ARB, 0x84000000, 1, 0x07}, + {SCORPIONL2_SLAVEPORT_NOGRANT, 0x80000007, 1, 0x04}, + {SCORPIONL2_SLAVEPORT_BPQ_FULL, 0x80000700, 1, 0x05}, + {SCORPIONL2_SLAVEPORT_LOSING_ARB, 0x80070000, 1, 0x06}, + {SCORPIONL2_SLAVEPORT_GRANT, 0x87000000, 1, 0x07}, + {SCORPIONL2_SLAVEPORT_GRANTLOCK, 0x80000008, 1, 0x04}, + {SCORPIONL2_L2EM_STREX_PASS, 0x80000009, 1, 0x04}, + {SCORPIONL2_L2EM_STREX_FAIL, 0x80000900, 1, 0x05}, + {SCORPIONL2_LDREX_RESERVE_L2EM, 0x80090000, 1, 0x06}, + {SCORPIONL2_SLAVEPORT_LDREX, 0x89000000, 1, 0x07}, + {SCORPIONL2_CPU0_L2EM_CLEARED, 0x800A0000, 1, 0x06}, + {SCORPIONL2_CPU1_L2EM_CLEARED, 0x8A000000, 1, 0x07}, + {SCORPIONL2_SLAVEPORT_L2EM_CLEARED, 0x80000B00, 1, 0x05}, + {SCORPIONL2_CPU0_CLAMPED, 0x8000000E, 1, 0x04}, + {SCORPIONL2_CPU1_CLAMPED, 0x80000E00, 1, 0x05}, + {SCORPIONL2_CPU0_WAIT, 0x800F0000, 1, 0x06}, + {SCORPIONL2_CPU1_WAIT, 0x8F000000, 1, 0x07}, + {SCORPIONL2_CPU0_NONAMBAS_WAIT, 0x80000010, 1, 0x04}, + {SCORPIONL2_CPU1_NONAMBAS_WAIT, 0x80001000, 1, 0x05}, + {SCORPIONL2_CPU0_DSB_WAIT, 0x80000014, 1, 0x04}, + {SCORPIONL2_CPU1_DSB_WAIT, 0x80001400, 1, 0x05}, + + {SCORPIONL2_AXI_READ, 0x80000001, 2, 0x08}, + {SCORPIONL2_AXI_WRITE, 0x80000100, 2, 0x09}, + {SCORPIONL2_1BEAT_WRITE, 0x80010000, 2, 0x0a}, + {SCORPIONL2_2BEAT_WRITE, 0x80010000, 2, 0x0b}, + {SCORPIONL2_4BEAT_WRITE, 0x80000002, 2, 0x08}, + {SCORPIONL2_8BEAT_WRITE, 0x80000200, 2, 0x09}, + {SCORPIONL2_12BEAT_WRITE, 0x80020000, 2, 0x0a}, + {SCORPIONL2_16BEAT_WRITE, 0x82000000, 2, 0x0b}, + {SCORPIONL2_1BEAT_DSIDE_READ, 0x80000003, 2, 0x08}, + {SCORPIONL2_2BEAT_DSIDE_READ, 0x80000300, 2, 0x09}, + {SCORPIONL2_4BEAT_DSIDE_READ, 0x80030000, 2, 0x0a}, + {SCORPIONL2_8BEAT_DSIDE_READ, 0x83000000, 2, 0x0b}, + {SCORPIONL2_CSYS_READ_1BEAT, 0x80000004, 2, 0x08}, + {SCORPIONL2_CSYS_READ_2BEAT, 0x80000400, 2, 0x09}, + {SCORPIONL2_CSYS_READ_4BEAT, 0x80040000, 2, 0x0a}, + {SCORPIONL2_CSYS_READ_8BEAT, 0x84000000, 2, 0x0b}, + {SCORPIONL2_4BEAT_IFETCH_READ, 0x80000005, 2, 0x08}, + {SCORPIONL2_8BEAT_IFETCH_READ, 0x80000500, 2, 0x09}, + {SCORPIONL2_CSYS_WRITE_1BEAT, 0x80050000, 2, 0x0a}, + {SCORPIONL2_CSYS_WRITE_2BEAT, 0x85000000, 2, 0x0b}, + {SCORPIONL2_AXI_READ_DATA_BEAT, 0x80000600, 2, 0x09}, + {SCORPIONL2_AXI_WRITE_EVT1, 0x80060000, 2, 0x0a}, + {SCORPIONL2_AXI_WRITE_EVT2, 0x86000000, 2, 0x0b}, + {SCORPIONL2_LDREX_REQ, 0x80000007, 2, 0x08}, + {SCORPIONL2_STREX_PASS, 0x80000700, 2, 0x09}, + {SCORPIONL2_STREX_FAIL, 0x80070000, 2, 0x0a}, + {SCORPIONL2_CPREAD, 0x80000008, 2, 0x08}, + {SCORPIONL2_CPWRITE, 0x80000800, 2, 0x09}, + {SCORPIONL2_BARRIER_REQ, 0x88000000, 2, 0x0b}, + + {SCORPIONL2_AXI_READ_SLVPORT, 0x80000001, 3, 0x0c}, + {SCORPIONL2_AXI_WRITE_SLVPORT, 0x80000100, 3, 0x0d}, + {SCORPIONL2_AXI_READ_SLVPORT_DATABEAT, 0x80010000, 3, 0x0e}, + {SCORPIONL2_AXI_WRITE_SLVPORT_DATABEAT, 0x81000000, 3, 0x0f}, + + {SCORPIONL2_SNOOPKILL_PREFILTER, 0x80000001, 4, 0x10}, + {SCORPIONL2_SNOOPKILL_FILTEROUT, 0x80000100, 4, 0x11}, + {SCORPIONL2_SNOOPED_IC, 0x80000002, 4, 0x10}, + {SCORPIONL2_SNOOPED_BP, 0x80000200, 4, 0x11}, + {SCORPIONL2_SNOOPED_BARRIERS, 0x80020000, 4, 0x12}, + {SCORPIONL2_SNOOPED_TLB, 0x82000000, 4, 0x13}, +}; + +static u32 bb_l2_read_l2pm0(void) +{ + u32 val; + asm volatile ("mrc p15, 3, %0, c15, c7, 0" : "=r" (val)); + return val; +} + +static void bb_l2_write_l2pm0(u32 val) +{ + asm volatile ("mcr p15, 3, %0, c15, c7, 0" : : "r" (val)); +} + +static u32 bb_l2_read_l2pm1(void) +{ + u32 val; + asm volatile ("mrc p15, 3, %0, c15, c7, 1" : "=r" (val)); + return val; +} + +static void bb_l2_write_l2pm1(u32 val) +{ + asm volatile ("mcr p15, 3, %0, c15, c7, 1" : : "r" (val)); +} + +static u32 bb_l2_read_l2pm2(void) +{ + u32 val; + asm volatile ("mrc p15, 3, %0, c15, c7, 2" : "=r" (val)); + return val; +} + +static void bb_l2_write_l2pm2(u32 val) +{ + asm volatile ("mcr p15, 3, %0, c15, c7, 2" : : "r" (val)); +} + +static u32 bb_l2_read_l2pm3(void) +{ + u32 val; + asm volatile ("mrc p15, 3, %0, c15, c7, 3" : "=r" (val)); + return val; +} + +static void bb_l2_write_l2pm3(u32 val) +{ + asm volatile ("mcr p15, 3, %0, c15, c7, 3" : : "r" (val)); +} + +static u32 bb_l2_read_l2pm4(void) +{ + u32 val; + asm volatile ("mrc p15, 3, %0, c15, c7, 4" : "=r" (val)); + return val; +} + +static void bb_l2_write_l2pm4(u32 val) +{ + asm volatile ("mcr p15, 3, %0, c15, c7, 4" : : "r" (val)); +} + +struct bb_scorpion_access_funcs { + u32(*read) (void); + void (*write) (u32); + void (*pre) (void); + void (*post) (void); +}; + +struct bb_scorpion_access_funcs bb_l2_func[] = { + {bb_l2_read_l2pm0, bb_l2_write_l2pm0, NULL, NULL}, + {bb_l2_read_l2pm1, bb_l2_write_l2pm1, NULL, NULL}, + {bb_l2_read_l2pm2, bb_l2_write_l2pm2, NULL, NULL}, + {bb_l2_read_l2pm3, bb_l2_write_l2pm3, NULL, NULL}, + {bb_l2_read_l2pm4, bb_l2_write_l2pm4, NULL, NULL}, +}; + +#define COLMN0MASK 0x000000ff +#define COLMN1MASK 0x0000ff00 +#define COLMN2MASK 0x00ff0000 + +static u32 bb_l2_get_columnmask(u32 setval) +{ + if (setval & COLMN0MASK) + return 0xffffff00; + else if (setval & COLMN1MASK) + return 0xffff00ff; + else if (setval & COLMN2MASK) + return 0xff00ffff; + else + return 0x80ffffff; +} + +static void bb_l2_evt_setup(u32 gr, u32 setval) +{ + u32 val; + if (bb_l2_func[gr].pre) + bb_l2_func[gr].pre(); + val = bb_l2_get_columnmask(setval) & bb_l2_func[gr].read(); + val = val | setval; + bb_l2_func[gr].write(val); + if (bb_l2_func[gr].post) + bb_l2_func[gr].post(); +} + +#define BB_L2_EVT_START_IDX 0x90 +#define BB_L2_INV_EVTYPE 0 + +static unsigned int get_bb_l2_evtinfo(unsigned int evt_type, + struct bb_l2_scorp_evt *evtinfo) +{ + u32 idx; + if (evt_type < BB_L2_EVT_START_IDX || evt_type >= BB_L2_MAX_EVT) + return BB_L2_INV_EVTYPE; + idx = evt_type - BB_L2_EVT_START_IDX; + if (sc_evt[idx].evt_type == evt_type) { + evtinfo->val = sc_evt[idx].val; + evtinfo->grp = sc_evt[idx].grp; + evtinfo->evt_type_act = sc_evt[idx].evt_type_act; + return sc_evt[idx].evt_type_act; + } + return BB_L2_INV_EVTYPE; +} + +static inline void bb_l2_pmnc_write(unsigned long val) +{ + val &= 0xff; + asm volatile ("mcr p15, 3, %0, c15, c4, 0" : : "r" (val)); +} + +static inline unsigned long bb_l2_pmnc_read(void) +{ + u32 val; + asm volatile ("mrc p15, 3, %0, c15, c4, 0" : "=r" (val)); + return val; +} + +static void bb_l2_set_evcntcr(void) +{ + u32 val = 0x0; + asm volatile ("mcr p15, 3, %0, c15, c6, 4" : : "r" (val)); +} + +static inline void bb_l2_set_evtyper(int ctr, int val) +{ + /* select ctr */ + asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (ctr)); + + /* write into EVTYPER */ + asm volatile ("mcr p15, 3, %0, c15, c6, 7" : : "r" (val)); +} + +static void bb_l2_set_evfilter(void) +{ + u32 filter_val = 0x000f0030 | 1 << smp_processor_id(); + + asm volatile ("mcr p15, 3, %0, c15, c6, 3" : : "r" (filter_val)); +} + +static void bb_l2_enable_intenset(u32 idx) +{ + if (idx == BB_L2CYCLE_CTR_EVENT_IDX) { + asm volatile ("mcr p15, 3, %0, c15, c5, 1" : : "r" + (1 << BB_L2CYCLE_CTR_BIT)); + } else { + asm volatile ("mcr p15, 3, %0, c15, c5, 1" : : "r" (1 << idx)); + } +} + +static void bb_l2_disable_intenclr(u32 idx) +{ + if (idx == BB_L2CYCLE_CTR_EVENT_IDX) { + asm volatile ("mcr p15, 3, %0, c15, c5, 0" : : "r" + (1 << BB_L2CYCLE_CTR_BIT)); + } else { + asm volatile ("mcr p15, 3, %0, c15, c5, 0" : : "r" (1 << idx)); + } +} + +static void bb_l2_enable_counter(u32 idx) +{ + if (idx == BB_L2CYCLE_CTR_EVENT_IDX) { + asm volatile ("mcr p15, 3, %0, c15, c4, 3" : : "r" + (1 << BB_L2CYCLE_CTR_BIT)); + } else { + asm volatile ("mcr p15, 3, %0, c15, c4, 3" : : "r" (1 << idx)); + } +} + +static void bb_l2_disable_counter(u32 idx) +{ + if (idx == BB_L2CYCLE_CTR_EVENT_IDX) { + asm volatile ("mcr p15, 3, %0, c15, c4, 2" : : "r" + (1 << BB_L2CYCLE_CTR_BIT)); + + } else { + asm volatile ("mcr p15, 3, %0, c15, c4, 2" : : "r" (1 << idx)); + } +} + +static u64 bb_l2_read_counter(u32 idx) +{ + u32 val; + unsigned long flags; + + if (idx == BB_L2CYCLE_CTR_EVENT_IDX) { + asm volatile ("mrc p15, 3, %0, c15, c4, 5" : "=r" (val)); + } else { + raw_spin_lock_irqsave(&bb_l2_pmu_lock, flags); + asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (idx)); + + /* read val from counter */ + asm volatile ("mrc p15, 3, %0, c15, c6, 5" : "=r" (val)); + raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, flags); + } + + return val; +} + +static void bb_l2_write_counter(u32 idx, u32 val) +{ + unsigned long flags; + + if (idx == BB_L2CYCLE_CTR_EVENT_IDX) { + asm volatile ("mcr p15, 3, %0, c15, c4, 5" : : "r" (val)); + } else { + raw_spin_lock_irqsave(&bb_l2_pmu_lock, flags); + /* select counter */ + asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (idx)); + + /* write val into counter */ + asm volatile ("mcr p15, 3, %0, c15, c6, 5" : : "r" (val)); + raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, flags); + } +} + +static int +bb_pmu_event_set_period(struct perf_event *event, + struct hw_perf_event *hwc, int idx) +{ + s64 left = local64_read(&hwc->period_left); + s64 period = hwc->sample_period; + int ret = 0; + + if (unlikely(left <= -period)) { + left = period; + local64_set(&hwc->period_left, left); + hwc->last_period = period; + ret = 1; + } + + if (unlikely(left <= 0)) { + left += period; + local64_set(&hwc->period_left, left); + hwc->last_period = period; + ret = 1; + } + + if (left > (s64) MAX_BB_L2_PERIOD) + left = MAX_BB_L2_PERIOD; + + local64_set(&hwc->prev_count, (u64)-left); + + bb_l2_write_counter(idx, (u64) (-left) & 0xffffffff); + + perf_event_update_userpage(event); + + return ret; +} + +static u64 +bb_pmu_event_update(struct perf_event *event, struct hw_perf_event *hwc, + int idx, int overflow) +{ + u64 prev_raw_count, new_raw_count; + u64 delta; + +again: + prev_raw_count = local64_read(&hwc->prev_count); + new_raw_count = bb_l2_read_counter(idx); + + if (local64_cmpxchg(&hwc->prev_count, prev_raw_count, + new_raw_count) != prev_raw_count) + goto again; + + new_raw_count &= MAX_BB_L2_PERIOD; + prev_raw_count &= MAX_BB_L2_PERIOD; + + if (overflow) { + delta = MAX_BB_L2_PERIOD - prev_raw_count + new_raw_count; + pr_err("%s: delta: %lld\n", __func__, delta); + } else + delta = new_raw_count - prev_raw_count; + + local64_add(delta, &event->count); + local64_sub(delta, &hwc->period_left); + + pr_debug("%s: new: %lld, prev: %lld, event: %ld count: %lld\n", + __func__, new_raw_count, prev_raw_count, + hwc->config_base, local64_read(&event->count)); + + return new_raw_count; +} + +static void bb_l2_read(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + + bb_pmu_event_update(event, hwc, hwc->idx, 0); +} + +static void bb_l2_stop_counter(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + int idx = hwc->idx; + + if (!(hwc->state & PERF_HES_STOPPED)) { + bb_l2_disable_intenclr(idx); + bb_l2_disable_counter(idx); + + bb_pmu_event_update(event, hwc, idx, 0); + hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; + } + + pr_debug("%s: event: %ld ctr: %d stopped\n", __func__, hwc->config_base, + idx); +} + +static void bb_l2_start_counter(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + int idx = hwc->idx; + struct bb_l2_scorp_evt evtinfo; + int evtype = hwc->config_base; + int ev_typer; + unsigned long iflags; + int cpu_id = smp_processor_id(); + + if (flags & PERF_EF_RELOAD) + WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE)); + + hwc->state = 0; + + bb_pmu_event_set_period(event, hwc, idx); + + if (hwc->config_base == BB_L2CYCLE_CTR_RAW_CODE) + goto out; + + memset(&evtinfo, 0, sizeof(evtinfo)); + + ev_typer = get_bb_l2_evtinfo(evtype, &evtinfo); + + raw_spin_lock_irqsave(&bb_l2_pmu_lock, iflags); + + bb_l2_set_evtyper(idx, ev_typer); + + bb_l2_set_evcntcr(); + + bb_l2_set_evfilter(); + + bb_l2_evt_setup(evtinfo.grp, evtinfo.val); + + raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, iflags); + +out: + + bb_l2_enable_intenset(idx); + + bb_l2_enable_counter(idx); + + pr_debug("%s: idx: %d, event: %d, val: %x, cpu: %d\n", + __func__, idx, evtype, evtinfo.val, cpu_id); +} + +static void bb_l2_del_event(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + int idx = hwc->idx; + unsigned long iflags; + + raw_spin_lock_irqsave(&hw_bb_l2_pmu.lock, iflags); + + clear_bit(idx, (long unsigned int *)(&hw_bb_l2_pmu.active_mask)); + + bb_l2_stop_counter(event, PERF_EF_UPDATE); + hw_bb_l2_pmu.events[idx] = NULL; + hwc->idx = -1; + + raw_spin_unlock_irqrestore(&hw_bb_l2_pmu.lock, iflags); + + pr_debug("%s: event: %ld deleted\n", __func__, hwc->config_base); + + perf_event_update_userpage(event); +} + +static int bb_l2_add_event(struct perf_event *event, int flags) +{ + int ctr = 0; + struct hw_perf_event *hwc = &event->hw; + unsigned long iflags; + int err = 0; + + perf_pmu_disable(event->pmu); + + raw_spin_lock_irqsave(&hw_bb_l2_pmu.lock, iflags); + + /* Cycle counter has a resrvd index */ + if (hwc->config_base == BB_L2CYCLE_CTR_RAW_CODE) { + if (hw_bb_l2_pmu.events[BB_L2CYCLE_CTR_EVENT_IDX]) { + pr_err("%s: Stale cycle ctr event ptr !\n", __func__); + err = -EINVAL; + goto out; + } + hwc->idx = BB_L2CYCLE_CTR_EVENT_IDX; + hw_bb_l2_pmu.events[BB_L2CYCLE_CTR_EVENT_IDX] = event; + set_bit(BB_L2CYCLE_CTR_EVENT_IDX, + (long unsigned int *)&hw_bb_l2_pmu.active_mask); + goto skip_ctr_loop; + } + + for (ctr = 0; ctr < MAX_BB_L2_CTRS - 1; ctr++) { + if (!hw_bb_l2_pmu.events[ctr]) { + hwc->idx = ctr; + hw_bb_l2_pmu.events[ctr] = event; + set_bit(ctr, (long unsigned int *) + &hw_bb_l2_pmu.active_mask); + break; + } + } + + if (hwc->idx < 0) { + err = -ENOSPC; + pr_err("%s: No space for event: %llx!!\n", __func__, + event->attr.config); + goto out; + } + +skip_ctr_loop: + + bb_l2_disable_counter(hwc->idx); + + hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; + + if (flags & PERF_EF_START) + bb_l2_start_counter(event, PERF_EF_RELOAD); + + perf_event_update_userpage(event); + + pr_debug("%s: event: %ld, ctr: %d added from cpu:%d\n", + __func__, hwc->config_base, hwc->idx, smp_processor_id()); +out: + raw_spin_unlock_irqrestore(&hw_bb_l2_pmu.lock, iflags); + + /* Resume the PMU even if this event could not be added */ + perf_pmu_enable(event->pmu); + + return err; +} + +static void bb_l2_pmu_enable(struct pmu *pmu) +{ + unsigned long flags; + isb(); + raw_spin_lock_irqsave(&bb_l2_pmu_lock, flags); + /* Enable all counters */ + bb_l2_pmnc_write(bb_l2_pmnc_read() | SCORPIONL2_PMNC_E); + raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, flags); +} + +static void bb_l2_pmu_disable(struct pmu *pmu) +{ + unsigned long flags; + raw_spin_lock_irqsave(&bb_l2_pmu_lock, flags); + /* Disable all counters */ + bb_l2_pmnc_write(bb_l2_pmnc_read() & ~SCORPIONL2_PMNC_E); + raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, flags); + isb(); +} + +static inline u32 bb_l2_get_reset_pmovsr(void) +{ + u32 val; + + /* Read */ + asm volatile ("mrc p15, 3, %0, c15, c4, 1" : "=r" (val)); + + /* Write to clear flags */ + val &= 0xffffffff; + asm volatile ("mcr p15, 3, %0, c15, c4, 1" : : "r" (val)); + + return val; +} + +static irqreturn_t bb_l2_handle_irq(int irq_num, void *dev) +{ + unsigned long pmovsr; + struct perf_sample_data data; + struct pt_regs *regs; + struct perf_event *event; + struct hw_perf_event *hwc; + int bitp; + int idx = 0; + + pmovsr = bb_l2_get_reset_pmovsr(); + + if (!(pmovsr & 0xffffffff)) + return IRQ_NONE; + + regs = get_irq_regs(); + + perf_sample_data_init(&data, 0); + + raw_spin_lock(&hw_bb_l2_pmu.lock); + + while (pmovsr) { + bitp = __ffs(pmovsr); + + if (bitp == BB_L2CYCLE_CTR_BIT) + idx = BB_L2CYCLE_CTR_EVENT_IDX; + else + idx = bitp; + + event = hw_bb_l2_pmu.events[idx]; + + if (!event) + goto next; + + if (!test_bit(idx, hw_bb_l2_pmu.active_mask)) + goto next; + + hwc = &event->hw; + bb_pmu_event_update(event, hwc, idx, 1); + data.period = event->hw.last_period; + + if (!bb_pmu_event_set_period(event, hwc, idx)) + goto next; + + if (perf_event_overflow(event, 0, &data, regs)) + bb_l2_disable_counter(hwc->idx); +next: + pmovsr &= (pmovsr - 1); + } + + raw_spin_unlock(&hw_bb_l2_pmu.lock); + + irq_work_run(); + + return IRQ_HANDLED; +} + +static atomic_t active_bb_l2_events = ATOMIC_INIT(0); +static DEFINE_MUTEX(bb_pmu_reserve_mutex); + +static int bb_pmu_reserve_hardware(void) +{ + int i, err = -ENODEV, irq; + + bb_l2_pmu_device = reserve_pmu(ARM_PMU_DEVICE_L2); + + if (IS_ERR(bb_l2_pmu_device)) { + pr_warning("unable to reserve pmu\n"); + return PTR_ERR(bb_l2_pmu_device); + } + + if (bb_l2_pmu_device->num_resources < 1) { + pr_err("no irqs for PMUs defined\n"); + return -ENODEV; + } + + if (strncmp(bb_l2_pmu_device->name, "l2-arm-pmu", 6)) { + pr_err("Incorrect pdev reserved !\n"); + return -EINVAL; + } + + for (i = 0; i < bb_l2_pmu_device->num_resources; ++i) { + irq = platform_get_irq(bb_l2_pmu_device, i); + if (irq < 0) + continue; + + err = request_irq(irq, bb_l2_handle_irq, + IRQF_DISABLED | IRQF_NOBALANCING, + "bb-l2-pmu", NULL); + if (err) { + pr_warning("unable to request IRQ%d for Krait L2 perf " + "counters\n", irq); + break; + } + + get_irq_chip(irq)->irq_unmask(irq_get_irq_data(irq)); + } + + if (err) { + for (i = i - 1; i >= 0; --i) { + irq = platform_get_irq(bb_l2_pmu_device, i); + if (irq >= 0) + free_irq(irq, NULL); + } + release_pmu(bb_l2_pmu_device); + bb_l2_pmu_device = NULL; + } + + return err; +} + +static void bb_pmu_release_hardware(void) +{ + int i, irq; + + for (i = bb_l2_pmu_device->num_resources - 1; i >= 0; --i) { + irq = platform_get_irq(bb_l2_pmu_device, i); + if (irq >= 0) + free_irq(irq, NULL); + } + + bb_l2_pmu_disable(NULL); + + release_pmu(bb_l2_pmu_device); + bb_l2_pmu_device = NULL; +} + +static void bb_pmu_perf_event_destroy(struct perf_event *event) +{ + if (atomic_dec_and_mutex_lock + (&active_bb_l2_events, &bb_pmu_reserve_mutex)) { + bb_pmu_release_hardware(); + mutex_unlock(&bb_pmu_reserve_mutex); + } +} + +static int bb_l2_event_init(struct perf_event *event) +{ + int err = 0; + struct hw_perf_event *hwc = &event->hw; + int status = 0; + + switch (event->attr.type) { + case PERF_TYPE_SHARED: + break; + + default: + return -ENOENT; + } + + hwc->idx = -1; + + event->destroy = bb_pmu_perf_event_destroy; + + if (!atomic_inc_not_zero(&active_bb_l2_events)) { + /* 0 active events */ + mutex_lock(&bb_pmu_reserve_mutex); + err = bb_pmu_reserve_hardware(); + mutex_unlock(&bb_pmu_reserve_mutex); + if (!err) + atomic_inc(&active_bb_l2_events); + else + return err; + } + + hwc->config_base = event->attr.config & 0xff; + hwc->config = 0; + hwc->event_base = 0; + + /* Only one CPU can control the cycle counter */ + if (hwc->config_base == BB_L2CYCLE_CTR_RAW_CODE) { + /* Check if its already running */ + asm volatile ("mrc p15, 3, %0, c15, c4, 6" : "=r" (status)); + if (status == 0x2) { + err = -ENOSPC; + goto out; + } + } + + if (!hwc->sample_period) { + hwc->sample_period = MAX_BB_L2_PERIOD; + hwc->last_period = hwc->sample_period; + local64_set(&hwc->period_left, hwc->sample_period); + } + + pr_debug("%s: event: %lld init'd\n", __func__, event->attr.config); + +out: + if (err < 0) + bb_pmu_perf_event_destroy(event); + + return err; +} + +static struct pmu bb_l2_pmu = { + .pmu_enable = bb_l2_pmu_enable, + .pmu_disable = bb_l2_pmu_disable, + .event_init = bb_l2_event_init, + .add = bb_l2_add_event, + .del = bb_l2_del_event, + .start = bb_l2_start_counter, + .stop = bb_l2_stop_counter, + .read = bb_l2_read, +}; + +static const struct arm_pmu *__init scorpionmp_l2_pmu_init(void) +{ + /* Register our own PMU here */ + perf_pmu_register(&bb_l2_pmu, "BB L2", PERF_TYPE_SHARED); + + memset(&hw_bb_l2_pmu, 0, sizeof(hw_bb_l2_pmu)); + + /* Avoid spurious interrupts at startup */ + bb_l2_get_reset_pmovsr(); + + /* Don't return an arm_pmu here */ + return NULL; +} +#else + +static const struct arm_pmu *__init scorpionmp_l2_pmu_init(void) +{ + return NULL; +} + +#endif diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c index c058bfc8532be..66ce900b1c5aa 100644 --- a/arch/arm/kernel/perf_event_v6.c +++ b/arch/arm/kernel/perf_event_v6.c @@ -474,7 +474,7 @@ armv6pmu_handle_irq(int irq_num, continue; hwc = &event->hw; - armpmu_event_update(event, hwc, idx); + armpmu_event_update(event, hwc, idx, 1); data.period = event->hw.last_period; if (!armpmu_event_set_period(event, hwc, idx)) continue; diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index 2e1402556fa0c..2aa83c2f82d21 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -466,6 +466,7 @@ static inline unsigned long armv7_pmnc_read(void) static inline void armv7_pmnc_write(unsigned long val) { val &= ARMV7_PMNC_MASK; + isb(); asm volatile("mcr p15, 0, %0, c9, c12, 0" : : "r"(val)); } @@ -502,6 +503,7 @@ static inline int armv7_pmnc_select_counter(unsigned int idx) val = (idx - ARMV7_EVENT_CNT_TO_CNTx) & ARMV7_SELECT_MASK; asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (val)); + isb(); return idx; } @@ -780,7 +782,7 @@ static irqreturn_t armv7pmu_handle_irq(int irq_num, void *dev) continue; hwc = &event->hw; - armpmu_event_update(event, hwc, idx); + armpmu_event_update(event, hwc, idx, 1); data.period = event->hw.last_period; if (!armpmu_event_set_period(event, hwc, idx)) continue; diff --git a/arch/arm/kernel/perf_event_xscale.c b/arch/arm/kernel/perf_event_xscale.c index 28cd3b025bc36..39affbe4fdb24 100644 --- a/arch/arm/kernel/perf_event_xscale.c +++ b/arch/arm/kernel/perf_event_xscale.c @@ -246,7 +246,7 @@ xscale1pmu_handle_irq(int irq_num, void *dev) continue; hwc = &event->hw; - armpmu_event_update(event, hwc, idx); + armpmu_event_update(event, hwc, idx, 1); data.period = event->hw.last_period; if (!armpmu_event_set_period(event, hwc, idx)) continue; @@ -578,7 +578,7 @@ xscale2pmu_handle_irq(int irq_num, void *dev) continue; hwc = &event->hw; - armpmu_event_update(event, hwc, idx); + armpmu_event_update(event, hwc, idx, 1); data.period = event->hw.last_period; if (!armpmu_event_set_period(event, hwc, idx)) continue; diff --git a/arch/arm/kernel/pmu.c b/arch/arm/kernel/pmu.c index 2c79eec192629..1df39b8321fd6 100644 --- a/arch/arm/kernel/pmu.c +++ b/arch/arm/kernel/pmu.c @@ -3,6 +3,7 @@ * * Copyright (C) 2009 picoChip Designs Ltd, Jamie Iles * Copyright (C) 2010 ARM Ltd, Will Deacon + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -45,16 +46,41 @@ static int __devinit pmu_device_probe(struct platform_device *pdev) return 0; } -static struct platform_driver pmu_driver = { +static struct platform_driver cpu_pmu_driver = { .driver = { - .name = "arm-pmu", + .name = "cpu-arm-pmu", }, .probe = pmu_device_probe, }; +static struct platform_driver l2_pmu_driver = { + .driver = { + .name = "l2-arm-pmu", + }, + .probe = pmu_device_probe, +}; + +static struct platform_driver *pmu_drivers[] __initdata = { + &cpu_pmu_driver, + &l2_pmu_driver, +}; + static int __init register_pmu_driver(void) { - return platform_driver_register(&pmu_driver); + int i; + int err; + + for (i = 0; i < ARM_NUM_PMU_DEVICES; i++) { + err = platform_driver_register(pmu_drivers[i]); + if (err) { + pr_err("%s: failed to register id:%d\n", __func__, i); + while (--i >= 0) + platform_driver_unregister(pmu_drivers[i]); + break; + } + } + + return err; } device_initcall(register_pmu_driver); diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index a4f4872296636..9f39fcc1a8208 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -108,6 +108,8 @@ EXPORT_SYMBOL(system_serial_high); unsigned int elf_hwcap __read_mostly; EXPORT_SYMBOL(elf_hwcap); +unsigned int boot_reason; +EXPORT_SYMBOL(boot_reason); #ifdef MULTI_CPU struct processor processor __read_mostly; @@ -695,8 +697,12 @@ __tagtable(ATAG_PS, parse_tag_ps_calibration); static int __init parse_tag_cmdline(const struct tag *tag) { +#ifndef CONFIG_CMDLINE_FORCE strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE); +#else + pr_warning("Ignoring tag cmdline (using the default kernel command line)\n"); +#endif /* CONFIG_CMDLINE_FORCE */ return 0; } diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 4539ebcb089fa..0f31dbc836d27 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -4,7 +4,7 @@ * Copyright (C) 2002 ARM Limited, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as + * it under the termsCON of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include @@ -278,8 +278,6 @@ asmlinkage void __cpuinit secondary_start_kernel(void) struct mm_struct *mm = &init_mm; unsigned int cpu = smp_processor_id(); - printk("CPU%u: Booted secondary processor\n", cpu); - /* * All kernel threads share the same mm context; grab a * reference and switch to it. @@ -470,11 +468,7 @@ static void smp_timer_broadcast(const struct cpumask *mask) { smp_cross_call(mask, IPI_TIMER); } -#else -#define smp_timer_broadcast NULL -#endif -#ifndef CONFIG_LOCAL_TIMERS static void broadcast_timer_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) { @@ -489,6 +483,7 @@ static void local_timer_setup(struct clock_event_device *evt) evt->rating = 400; evt->mult = 1; evt->set_mode = broadcast_timer_set_mode; + evt->broadcast = smp_timer_broadcast; clockevents_register_device(evt); } @@ -500,7 +495,6 @@ void __cpuinit percpu_timer_setup(void) struct clock_event_device *evt = &per_cpu(percpu_clockevent, cpu); evt->cpumask = cpumask_of(cpu); - evt->broadcast = smp_timer_broadcast; local_timer_setup(evt); } @@ -589,6 +583,11 @@ asmlinkage void __exception_irq_entry do_IPI(int ipinr, struct pt_regs *regs) void smp_send_reschedule(int cpu) { + + if (unlikely(cpu_is_offline(cpu))) { + WARN_ON(1); + return; + } smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE); } diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index 60636f499cb3e..377dbe972b30b 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -127,8 +127,7 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk) twd_calibrate_rate(); clk->name = "local_timer"; - clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | - CLOCK_EVT_FEAT_C3STOP; + clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; clk->rating = 350; clk->set_mode = twd_set_mode; clk->set_next_event = twd_set_next_event; diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index d71941d661cb9..2e76788f88a72 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -455,6 +455,10 @@ do_cache_op(unsigned long start, unsigned long end, int flags) up_read(&mm->mmap_sem); flush_cache_user_range(start, end); + +#ifdef CONFIG_ARCH_MSM7X27 + mb(); +#endif return; } up_read(&mm->mmap_sem); diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 61462790757f5..479ddae2b2be0 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -7,6 +7,9 @@ #include #include #include +#ifdef CONFIG_STRICT_MEMORY_RWX +#include +#endif #define PROC_INFO \ VMLINUX_SYMBOL(__proc_info_begin) = .; \ @@ -104,6 +107,9 @@ SECTIONS #endif } +#ifdef CONFIG_STRICT_MEMORY_RWX + . = ALIGN(1< + * Copyright (C) 2005-2006 Atmel Corporation + * + * 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. + */ +#include +#include +#include + +/* + * Oh, if only we had a cycle counter... + */ +void delay_loop(unsigned long loops) +{ + asm volatile( + "1: subs %0, %0, #1 \n" + " bhi 1b \n" + : /* No output */ + : "r" (loops) + ); +} + +#ifdef ARCH_HAS_READ_CURRENT_TIMER +/* + * Assuming read_current_timer() is monotonically increasing + * across calls. + */ +void read_current_timer_delay_loop(unsigned long loops) +{ + unsigned long bclock, now; + + read_current_timer(&bclock); + do { + read_current_timer(&now); + } while ((now - bclock) < loops); +} +#endif + +static void (*delay_fn)(unsigned long) = delay_loop; + +void set_delay_fn(void (*fn)(unsigned long)) +{ + delay_fn = fn; +} + +/* + * loops = usecs * HZ * loops_per_jiffy / 1000000 + */ +void __delay(unsigned long loops) +{ + delay_fn(loops); +} +EXPORT_SYMBOL(__delay); + +/* + * 0 <= xloops <= 0x7fffff06 + * loops_per_jiffy <= 0x01ffffff (max. 3355 bogomips) + */ +void __const_udelay(unsigned long xloops) +{ + unsigned long lpj; + unsigned long loops; + + xloops >>= 14; /* max = 0x01ffffff */ + lpj = loops_per_jiffy >> 10; /* max = 0x0001ffff */ + loops = lpj * xloops; /* max = 0x00007fff */ + loops >>= 6; /* max = 2^32-1 */ + + if (loops) + __delay(loops); +} +EXPORT_SYMBOL(__const_udelay); + +/* + * usecs <= 2000 + * HZ <= 1000 + */ +void __udelay(unsigned long usecs) +{ + __const_udelay(usecs * ((2199023UL*HZ)>>11)); +} +EXPORT_SYMBOL(__udelay); diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index af8aac5320d6b..74d1895a7452b 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -578,6 +578,9 @@ config CPU_TLB_V6 config CPU_TLB_V7 bool +config EMULATE_DOMAIN_MANAGER_V7 + bool + config VERIFY_PERMISSION_FAULT bool endif @@ -732,7 +735,7 @@ config CPU_DCACHE_SIZE config CPU_CACHE_ERR_REPORT bool "Report errors in the L1 and L2 caches" - depends on ARCH_MSM_SCORPION || ARCH_MSM_SCORPIONMP + depends on ARCH_MSM_SCORPION default n help The Scorpion processor supports reporting L2 errors, L1 icache parity @@ -826,7 +829,8 @@ config CACHE_L2X0 depends on REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176 || \ REALVIEW_EB_A9MP || ARCH_MX35 || ARCH_MX31 || MACH_REALVIEW_PBX || \ ARCH_NOMADIK || ARCH_OMAP4 || ARCH_S5PV310 || ARCH_TEGRA || \ - ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || ARCH_SHMOBILE + ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || ARCH_SHMOBILE || \ + ARCH_MSM7X27 || ARCH_MSM9615 default y select OUTER_CACHE select OUTER_CACHE_SYNC @@ -889,3 +893,30 @@ config ARCH_HAS_BARRIERS help This option allows the use of custom mandatory barriers included via the mach/barriers.h file. + +config VCM_MM + bool + +config VCM + bool "Virtual Contiguous Memory (VCM) Layer" + depends on MMU + select GENERIC_ALLOCATOR + select VCM_MM + default n + help + Virtual Contiguous Memory layer. This is the layer that is intended to + replace PMEM. + + If you don't know what this is, say N here. + +config STRICT_MEMORY_RWX + bool "restrict kernel memory permissions as much as possible" + default n + help + If this is set, kernel text will be made RX, kernel data and stack + RW, rodata R (otherwise all of the kernel 1-to-1 mapping is + made RWX). + The tradeoff is that several sections are padded to + 1M boundaries (because their permissions are different and + splitting the 1M pages into 4K ones causes TLB performance + problems), wasting memory. diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile index 00d74a04af3af..47b8c289104a8 100644 --- a/arch/arm/mm/Makefile +++ b/arch/arm/mm/Makefile @@ -91,6 +91,7 @@ obj-$(CONFIG_CPU_MOHAWK) += proc-mohawk.o obj-$(CONFIG_CPU_FEROCEON) += proc-feroceon.o obj-$(CONFIG_CPU_V6) += proc-v6.o obj-$(CONFIG_CPU_V7) += proc-v7.o +obj-$(CONFIG_EMULATE_DOMAIN_MANAGER_V7) += emulate_domain_manager-v7.o AFLAGS_proc-v6.o :=-Wa,-march=armv6 AFLAGS_proc-v7.o :=-Wa,-march=armv7-a @@ -99,3 +100,5 @@ obj-$(CONFIG_CACHE_FEROCEON_L2) += cache-feroceon-l2.o obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o obj-$(CONFIG_CACHE_XSC3L2) += cache-xsc3l2.o obj-$(CONFIG_CACHE_TAUROS2) += cache-tauros2.o +obj-$(CONFIG_VCM) += vcm.o vcm_alloc.o +obj-$(CONFIG_VCM_MM) += vcm_mm.o diff --git a/arch/arm/mm/cache-fa.S b/arch/arm/mm/cache-fa.S index 1fa6f71470de5..ad953fe4ef50d 100644 --- a/arch/arm/mm/cache-fa.S +++ b/arch/arm/mm/cache-fa.S @@ -168,7 +168,7 @@ ENTRY(fa_flush_kern_dcache_area) * - start - virtual start address * - end - virtual end address */ -fa_dma_inv_range: +ENTRY(fa_dma_inv_range) tst r0, #CACHE_DLINESIZE - 1 bic r0, r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c14, 1 @ clean & invalidate D entry @@ -191,7 +191,7 @@ fa_dma_inv_range: * - start - virtual start address * - end - virtual end address */ -fa_dma_clean_range: +ENTRY(fa_dma_clean_range) bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #CACHE_DLINESIZE @@ -253,5 +253,7 @@ ENTRY(fa_cache_fns) .long fa_flush_kern_dcache_area .long fa_dma_map_area .long fa_dma_unmap_area + .long fa_dma_inv_range + .long fa_dma_clean_range .long fa_dma_flush_range .size fa_cache_fns, . - fa_cache_fns diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 770f612e3afbc..becaf13db37f6 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -2,6 +2,7 @@ * arch/arm/mm/cache-l2x0.c - L210/L220 cache controller support * * Copyright (C) 2007 ARM Limited + * Copyright (c) 2009, 2011, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -26,6 +27,8 @@ #define CACHE_LINE_SIZE 32 static void __iomem *l2x0_base; +static uint32_t aux_ctrl_save; +static uint32_t data_latency_ctrl; static DEFINE_SPINLOCK(l2x0_lock); static uint32_t l2x0_way_mask; /* Bitmask of active ways */ static uint32_t l2x0_size; @@ -73,18 +76,24 @@ static inline void l2x0_inv_line(unsigned long addr) writel_relaxed(addr, base + L2X0_INV_LINE_PA); } -#ifdef CONFIG_PL310_ERRATA_588369 -static void debug_writel(unsigned long val) -{ - extern void omap_smc1(u32 fn, u32 arg); +#if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915) - /* - * Texas Instrument secure monitor api to modify the - * PL310 Debug Control Register. - */ - omap_smc1(0x100, val); +#define debug_writel(val) outer_cache.set_debug(val) + +static void l2x0_set_debug(unsigned long val) +{ + writel_relaxed(val, l2x0_base + L2X0_DEBUG_CTRL); +} +#else +/* Optimised out for non-errata case */ +static inline void debug_writel(unsigned long val) +{ } +#define l2x0_set_debug NULL +#endif + +#ifdef CONFIG_PL310_ERRATA_588369 static inline void l2x0_flush_line(unsigned long addr) { void __iomem *base = l2x0_base; @@ -97,11 +106,6 @@ static inline void l2x0_flush_line(unsigned long addr) } #else -/* Optimised out for non-errata case */ -static inline void debug_writel(unsigned long val) -{ -} - static inline void l2x0_flush_line(unsigned long addr) { void __iomem *base = l2x0_base; @@ -110,13 +114,9 @@ static inline void l2x0_flush_line(unsigned long addr) } #endif -static void l2x0_cache_sync(void) +void l2x0_cache_sync(void) { - unsigned long flags; - - spin_lock_irqsave(&l2x0_lock, flags); cache_sync(); - spin_unlock_irqrestore(&l2x0_lock, flags); } static void l2x0_flush_all(void) @@ -125,9 +125,11 @@ static void l2x0_flush_all(void) /* clean all ways */ spin_lock_irqsave(&l2x0_lock, flags); + debug_writel(0x03); writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_INV_WAY); cache_wait_way(l2x0_base + L2X0_CLEAN_INV_WAY, l2x0_way_mask); cache_sync(); + debug_writel(0x00); spin_unlock_irqrestore(&l2x0_lock, flags); } @@ -196,6 +198,27 @@ static void l2x0_inv_range(unsigned long start, unsigned long end) spin_unlock_irqrestore(&l2x0_lock, flags); } +static void l2x0_inv_range_atomic(unsigned long start, unsigned long end) +{ + unsigned long addr; + + if (start & (CACHE_LINE_SIZE - 1)) { + start &= ~(CACHE_LINE_SIZE - 1); + writel_relaxed(start, l2x0_base + L2X0_CLEAN_INV_LINE_PA); + start += CACHE_LINE_SIZE; + } + + if (end & (CACHE_LINE_SIZE - 1)) { + end &= ~(CACHE_LINE_SIZE - 1); + writel_relaxed(end, l2x0_base + L2X0_CLEAN_INV_LINE_PA); + } + + for (addr = start; addr < end; addr += CACHE_LINE_SIZE) + writel_relaxed(addr, l2x0_base + L2X0_INV_LINE_PA); + + mb(); +} + static void l2x0_clean_range(unsigned long start, unsigned long end) { void __iomem *base = l2x0_base; @@ -226,6 +249,17 @@ static void l2x0_clean_range(unsigned long start, unsigned long end) spin_unlock_irqrestore(&l2x0_lock, flags); } +static void l2x0_clean_range_atomic(unsigned long start, unsigned long end) +{ + unsigned long addr; + + start &= ~(CACHE_LINE_SIZE - 1); + for (addr = start; addr < end; addr += CACHE_LINE_SIZE) + writel_relaxed(addr, l2x0_base + L2X0_CLEAN_LINE_PA); + + mb(); +} + static void l2x0_flush_range(unsigned long start, unsigned long end) { void __iomem *base = l2x0_base; @@ -258,6 +292,17 @@ static void l2x0_flush_range(unsigned long start, unsigned long end) spin_unlock_irqrestore(&l2x0_lock, flags); } +void l2x0_flush_range_atomic(unsigned long start, unsigned long end) +{ + unsigned long addr; + + start &= ~(CACHE_LINE_SIZE - 1); + for (addr = start; addr < end; addr += CACHE_LINE_SIZE) + writel_relaxed(addr, l2x0_base + L2X0_CLEAN_INV_LINE_PA); + + mb(); +} + static void l2x0_disable(void) { unsigned long flags; @@ -269,15 +314,19 @@ static void l2x0_disable(void) void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask) { - __u32 aux; + __u32 aux, bits; __u32 cache_id; __u32 way_size = 0; int ways; const char *type; l2x0_base = base; - cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID); + + bits = readl_relaxed(l2x0_base + L2X0_CTRL); + bits &= ~0x01; /* clear bit 0 */ + writel_relaxed(bits, l2x0_base + L2X0_CTRL); + aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); aux &= aux_mask; @@ -302,9 +351,8 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask) type = "L2x0 series"; break; } - writel(aux, l2x0_base + L2X0_AUX_CTRL); + writel_relaxed(aux, l2x0_base + L2X0_AUX_CTRL); l2x0_way_mask = (1 << ways) - 1; - l2x0_inv_all(); /* * L2 cache Size = Way size * Number of ways @@ -313,31 +361,79 @@ void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask) way_size = 1 << (way_size + 3); l2x0_size = ways * way_size * SZ_1K; - /* - * Check if l2x0 controller is already enabled. - * If you are booting from non-secure mode - * accessing the below registers will fault. - */ - if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & 1)) { - - /* l2x0 controller is disabled */ - writel_relaxed(aux, l2x0_base + L2X0_AUX_CTRL); + l2x0_inv_all(); - l2x0_inv_all(); + /* enable L2X0 */ + bits = readl_relaxed(l2x0_base + L2X0_CTRL); + bits |= 0x01; /* set bit 0 */ + writel_relaxed(bits, l2x0_base + L2X0_CTRL); - /* enable L2X0 */ - writel_relaxed(1, l2x0_base + L2X0_CTRL); + switch (cache_id & L2X0_CACHE_ID_PART_MASK) { + case L2X0_CACHE_ID_PART_L220: + outer_cache.inv_range = l2x0_inv_range; + outer_cache.clean_range = l2x0_clean_range; + outer_cache.flush_range = l2x0_flush_range; + printk(KERN_INFO "L220 cache controller enabled\n"); + break; + case L2X0_CACHE_ID_PART_L310: + outer_cache.inv_range = l2x0_inv_range; + outer_cache.clean_range = l2x0_clean_range; + outer_cache.flush_range = l2x0_flush_range; + printk(KERN_INFO "L310 cache controller enabled\n"); + break; + case L2X0_CACHE_ID_PART_L210: + default: + outer_cache.inv_range = l2x0_inv_range_atomic; + outer_cache.clean_range = l2x0_clean_range_atomic; + outer_cache.flush_range = l2x0_flush_range_atomic; + printk(KERN_INFO "L210 cache controller enabled\n"); + break; } - outer_cache.inv_range = l2x0_inv_range; - outer_cache.clean_range = l2x0_clean_range; - outer_cache.flush_range = l2x0_flush_range; outer_cache.sync = l2x0_cache_sync; + outer_cache.flush_all = l2x0_flush_all; outer_cache.inv_all = l2x0_inv_all; outer_cache.disable = l2x0_disable; + outer_cache.set_debug = l2x0_set_debug; + mb(); printk(KERN_INFO "%s cache controller enabled\n", type); printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x, Cache size: %d B\n", ways, cache_id, aux, l2x0_size); } + +void l2x0_suspend(void) +{ + /* Save aux control register value */ + aux_ctrl_save = readl_relaxed(l2x0_base + L2X0_AUX_CTRL); + data_latency_ctrl = readl_relaxed(l2x0_base + L2X0_DATA_LATENCY_CTRL); + /* Flush all cache */ + l2x0_flush_all(); + /* Disable the cache */ + writel_relaxed(0, l2x0_base + L2X0_CTRL); + + /* Memory barrier */ + dmb(); +} + +void l2x0_resume(int collapsed) +{ + if (collapsed) { + /* Disable the cache */ + writel_relaxed(0, l2x0_base + L2X0_CTRL); + + /* Restore aux control register value */ + writel_relaxed(aux_ctrl_save, l2x0_base + L2X0_AUX_CTRL); + writel_relaxed(data_latency_ctrl, l2x0_base + + L2X0_DATA_LATENCY_CTRL); + + /* Invalidate the cache */ + l2x0_inv_all(); + } + + /* Enable the cache */ + writel_relaxed(1, l2x0_base + L2X0_CTRL); + + mb(); +} diff --git a/arch/arm/mm/cache-v3.S b/arch/arm/mm/cache-v3.S index 2e2bc406a18d6..64f739eaa4c67 100644 --- a/arch/arm/mm/cache-v3.S +++ b/arch/arm/mm/cache-v3.S @@ -92,6 +92,20 @@ ENTRY(v3_coherent_user_range) ENTRY(v3_flush_kern_dcache_area) /* FALLTHROUGH */ +/* + * dma_inv_range(start, end) + * + * Invalidate (discard) the specified virtual address range. + * May not write back any entries. If 'start' or 'end' + * are not cache line aligned, those lines must be written + * back. + * + * - start - virtual start address + * - end - virtual end address + */ +ENTRY(v3_dma_inv_range) + /* FALLTHROUGH */ + /* * dma_flush_range(start, end) * @@ -103,6 +117,17 @@ ENTRY(v3_flush_kern_dcache_area) ENTRY(v3_dma_flush_range) mov r0, #0 mcr p15, 0, r0, c7, c0, 0 @ flush ID cache + /* FALLTHROUGH */ + +/* + * dma_clean_range(start, end) + * + * Clean (write back) the specified virtual address range. + * + * - start - virtual start address + * - end - virtual end address + */ +ENTRY(v3_dma_clean_range) mov pc, lr /* @@ -113,7 +138,7 @@ ENTRY(v3_dma_flush_range) */ ENTRY(v3_dma_unmap_area) teq r2, #DMA_TO_DEVICE - bne v3_dma_flush_range + bne v3_dma_inv_range /* FALLTHROUGH */ /* @@ -140,5 +165,7 @@ ENTRY(v3_cache_fns) .long v3_flush_kern_dcache_area .long v3_dma_map_area .long v3_dma_unmap_area + .long v3_dma_inv_range + .long v3_dma_clean_range .long v3_dma_flush_range .size v3_cache_fns, . - v3_cache_fns diff --git a/arch/arm/mm/cache-v4.S b/arch/arm/mm/cache-v4.S index a8fefb523f194..7824cf6e14a38 100644 --- a/arch/arm/mm/cache-v4.S +++ b/arch/arm/mm/cache-v4.S @@ -102,6 +102,20 @@ ENTRY(v4_coherent_user_range) ENTRY(v4_flush_kern_dcache_area) /* FALLTHROUGH */ +/* + * dma_inv_range(start, end) + * + * Invalidate (discard) the specified virtual address range. + * May not write back any entries. If 'start' or 'end' + * are not cache line aligned, those lines must be written + * back. + * + * - start - virtual start address + * - end - virtual end address + */ +ENTRY(v4_dma_inv_range) + /* FALLTHROUGH */ + /* * dma_flush_range(start, end) * @@ -115,6 +129,17 @@ ENTRY(v4_dma_flush_range) mov r0, #0 mcr p15, 0, r0, c7, c7, 0 @ flush ID cache #endif + /* FALLTHROUGH */ + +/* + * dma_clean_range(start, end) + * + * Clean (write back) the specified virtual address range. + * + * - start - virtual start address + * - end - virtual end address + */ +ENTRY(v4_dma_clean_range) mov pc, lr /* @@ -125,7 +150,7 @@ ENTRY(v4_dma_flush_range) */ ENTRY(v4_dma_unmap_area) teq r2, #DMA_TO_DEVICE - bne v4_dma_flush_range + bne v4_dma_inv_range /* FALLTHROUGH */ /* @@ -152,5 +177,7 @@ ENTRY(v4_cache_fns) .long v4_flush_kern_dcache_area .long v4_dma_map_area .long v4_dma_unmap_area + .long v4_dma_inv_range + .long v4_dma_clean_range .long v4_dma_flush_range .size v4_cache_fns, . - v4_cache_fns diff --git a/arch/arm/mm/cache-v4wb.S b/arch/arm/mm/cache-v4wb.S index d3644db467b74..57b8a95b8a323 100644 --- a/arch/arm/mm/cache-v4wb.S +++ b/arch/arm/mm/cache-v4wb.S @@ -184,7 +184,7 @@ ENTRY(v4wb_coherent_user_range) * - start - virtual start address * - end - virtual end address */ -v4wb_dma_inv_range: +ENTRY(v4wb_dma_inv_range) tst r0, #CACHE_DLINESIZE - 1 bic r0, r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -205,7 +205,7 @@ v4wb_dma_inv_range: * - start - virtual start address * - end - virtual end address */ -v4wb_dma_clean_range: +ENTRY(v4wb_dma_clean_range) bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #CACHE_DLINESIZE @@ -264,5 +264,7 @@ ENTRY(v4wb_cache_fns) .long v4wb_flush_kern_dcache_area .long v4wb_dma_map_area .long v4wb_dma_unmap_area + .long v4wb_dma_inv_range + .long v4wb_dma_clean_range .long v4wb_dma_flush_range .size v4wb_cache_fns, . - v4wb_cache_fns diff --git a/arch/arm/mm/cache-v4wt.S b/arch/arm/mm/cache-v4wt.S index 49c2b66cf3dd6..fe9038dc004a8 100644 --- a/arch/arm/mm/cache-v4wt.S +++ b/arch/arm/mm/cache-v4wt.S @@ -153,12 +153,23 @@ ENTRY(v4wt_flush_kern_dcache_area) * - start - virtual start address * - end - virtual end address */ -v4wt_dma_inv_range: +ENTRY(v4wt_dma_inv_range) bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry add r0, r0, #CACHE_DLINESIZE cmp r0, r1 blo 1b + /* FALLTHROUGH */ + +/* + * dma_clean_range(start, end) + * + * Clean the specified virtual address range. + * + * - start - virtual start address + * - end - virtual end address + */ +ENTRY(v4wt_dma_clean_range) mov pc, lr /* @@ -208,5 +219,7 @@ ENTRY(v4wt_cache_fns) .long v4wt_flush_kern_dcache_area .long v4wt_dma_map_area .long v4wt_dma_unmap_area + .long v4wt_dma_inv_range + .long v4wt_dma_clean_range .long v4wt_dma_flush_range .size v4wt_cache_fns, . - v4wt_cache_fns diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S index 6b5441d737be6..f6851f13b4d40 100644 --- a/arch/arm/mm/cache-v6.S +++ b/arch/arm/mm/cache-v6.S @@ -176,7 +176,6 @@ ENDPROC(v6_coherent_kern_range) */ ENTRY(v6_flush_kern_dcache_area) add r1, r0, r1 - bic r0, r0, #D_CACHE_LINE_SIZE - 1 1: #ifdef HARVARD_CACHE mcr p15, 0, r0, c7, c14, 1 @ clean & invalidate D line @@ -203,7 +202,7 @@ ENTRY(v6_flush_kern_dcache_area) * - start - virtual start address of region * - end - virtual end address of region */ -v6_dma_inv_range: +ENTRY(v6_dma_inv_range) #ifdef CONFIG_DMA_CACHE_RWFO ldrb r2, [r0] @ read for ownership strb r2, [r0] @ write for ownership @@ -248,7 +247,7 @@ v6_dma_inv_range: * - start - virtual start address of region * - end - virtual end address of region */ -v6_dma_clean_range: +ENTRY(v6_dma_clean_range) bic r0, r0, #D_CACHE_LINE_SIZE - 1 1: #ifdef CONFIG_DMA_CACHE_RWFO @@ -358,5 +357,7 @@ ENTRY(v6_cache_fns) .long v6_flush_kern_dcache_area .long v6_dma_map_area .long v6_dma_unmap_area + .long v6_dma_inv_range + .long v6_dma_clean_range .long v6_dma_flush_range .size v6_cache_fns, . - v6_cache_fns diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S index 3b97ac4fbe045..4eba077dc4dd1 100644 --- a/arch/arm/mm/cache-v7.S +++ b/arch/arm/mm/cache-v7.S @@ -221,8 +221,6 @@ ENDPROC(v7_coherent_user_range) ENTRY(v7_flush_kern_dcache_area) dcache_line_size r2, r3 add r1, r0, r1 - sub r3, r2, #1 - bic r0, r0, r3 1: mcr p15, 0, r0, c7, c14, 1 @ clean & invalidate D line / unified line add r0, r0, r2 @@ -336,7 +334,7 @@ ENTRY(v7_cache_fns) .long v7_flush_kern_dcache_area .long v7_dma_map_area .long v7_dma_unmap_area - .long v7_dma_inv_range - .long v7_dma_clean_range + .long v7_dma_inv_range + .long v7_dma_clean_range .long v7_dma_flush_range .size v7_cache_fns, . - v7_cache_fns diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 4771dba614481..75fd74e8f88df 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -420,18 +420,22 @@ EXPORT_SYMBOL(dma_free_coherent); void ___dma_single_cpu_to_dev(const void *kaddr, size_t size, enum dma_data_direction dir) { +#ifdef CONFIG_OUTER_CACHE unsigned long paddr; BUG_ON(!virt_addr_valid(kaddr) || !virt_addr_valid(kaddr + size - 1)); +#endif dmac_map_area(kaddr, size, dir); +#ifdef CONFIG_OUTER_CACHE paddr = __pa(kaddr); if (dir == DMA_FROM_DEVICE) { outer_inv_range(paddr, paddr + size); } else { outer_clean_range(paddr, paddr + size); } +#endif /* FIXME: non-speculating: flush on bidirectional mappings? */ } EXPORT_SYMBOL(___dma_single_cpu_to_dev); @@ -439,6 +443,7 @@ EXPORT_SYMBOL(___dma_single_cpu_to_dev); void ___dma_single_dev_to_cpu(const void *kaddr, size_t size, enum dma_data_direction dir) { +#ifdef CONFIG_OUTER_CACHE BUG_ON(!virt_addr_valid(kaddr) || !virt_addr_valid(kaddr + size - 1)); /* FIXME: non-speculating: not required */ @@ -447,7 +452,7 @@ void ___dma_single_dev_to_cpu(const void *kaddr, size_t size, unsigned long paddr = __pa(kaddr); outer_inv_range(paddr, paddr + size); } - +#endif dmac_unmap_area(kaddr, size, dir); } EXPORT_SYMBOL(___dma_single_dev_to_cpu); diff --git a/arch/arm/mm/emulate_domain_manager-v7.c b/arch/arm/mm/emulate_domain_manager-v7.c new file mode 100644 index 0000000000000..3797e211a4d8a --- /dev/null +++ b/arch/arm/mm/emulate_domain_manager-v7.c @@ -0,0 +1,345 @@ +/* + * Basic implementation of a SW emulation of the domain manager feature in + * ARM architecture. Assumes single processor ARMv7 chipset. + * + * Requires hooks to be alerted to any runtime changes of dacr or MMU context. + * + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#define DOMAIN_MANAGER_BITS (0xAAAAAAAA) + +#define DFSR_DOMAIN(dfsr) ((dfsr >> 4) & (16-1)) + +#define FSR_PERMISSION_FAULT(fsr) ((fsr & 0x40D) == 0x00D) +#define FSR_PERMISSION_SECT(fsr) ((fsr & 0x40F) == 0x00D) + +/* ARMv7 MMU HW Macros. Not conveniently defined elsewhere */ +#define MMU_TTB_ADDRESS(x) ((u32 *)(((u32)(x)) & ~((1 << 14) - 1))) +#define MMU_PMD_INDEX(addr) (((u32)addr) >> SECTION_SHIFT) +#define MMU_TABLE_ADDRESS(x) ((u32 *)((x) & ~((1 << 10) - 1))) +#define MMU_TABLE_INDEX(x) ((((u32)x) >> 12) & (256 - 1)) + +/* Convenience Macros */ +#define PMD_IS_VALID(x) (PMD_IS_TABLE(x) || PMD_IS_SECTION(x)) +#define PMD_IS_TABLE(x) ((x & PMD_TYPE_MASK) == PMD_TYPE_TABLE) +#define PMD_IS_SECTION(x) ((x & PMD_TYPE_MASK) == PMD_TYPE_SECT) +#define PMD_IS_SUPERSECTION(x) \ + (PMD_IS_SECTION(x) && ((x & PMD_SECT_SUPER) == PMD_SECT_SUPER)) + +#define PMD_GET_DOMAIN(x) \ + (PMD_IS_TABLE(x) || \ + (PMD_IS_SECTION(x) && !PMD_IS_SUPERSECTION(x)) ? \ + 0 : (x >> 5) & (16-1)) + +#define PTE_IS_LARGE(x) ((x & PTE_TYPE_MASK) == PTE_TYPE_LARGE) + + +/* Only DOMAIN_MMU_ENTRIES will be granted access simultaneously */ +#define DOMAIN_MMU_ENTRIES (8) + +#define LRU_INC(lru) ((lru + 1) >= DOMAIN_MMU_ENTRIES ? 0 : lru + 1) + + +static DEFINE_SPINLOCK(edm_lock); + +static u32 edm_manager_bits; + +struct domain_entry_save { + u32 *mmu_entry; + u32 *addr; + u32 value; + u16 sect; + u16 size; +}; + +static struct domain_entry_save edm_save[DOMAIN_MMU_ENTRIES]; + +static u32 edm_lru; + + +/* + * Return virtual address of pmd (level 1) entry for addr + * + * This routine walks the ARMv7 page tables in HW. + */ +static inline u32 *__get_pmd_v7(u32 *addr) +{ + u32 *ttb; + + __asm__ __volatile__( + "mrc p15, 0, %0, c2, c0, 0 @ ttbr0\n\t" + : "=r" (ttb) + : + ); + + return __va(MMU_TTB_ADDRESS(ttb) + MMU_PMD_INDEX(addr)); +} + +/* + * Return virtual address of pte (level 2) entry for addr + * + * This routine walks the ARMv7 page tables in HW. + */ +static inline u32 *__get_pte_v7(u32 *addr) +{ + u32 *pmd = __get_pmd_v7(addr); + u32 *table_pa = pmd && PMD_IS_TABLE(*pmd) ? + MMU_TABLE_ADDRESS(*pmd) : 0; + u32 *entry = table_pa ? __va(table_pa[MMU_TABLE_INDEX(addr)]) : 0; + + return entry; +} + +/* + * Invalidate the TLB for a given address for the current context + * + * After manipulating access permissions, TLB invalidation changes are + * observed + */ +static inline void __tlb_invalidate(u32 *addr) +{ + __asm__ __volatile__( + "mrc p15, 0, %%r2, c13, c0, 1 @ contextidr\n\t" + "and %%r2, %%r2, #0xff @ asid\n\t" + "mov %%r3, %0, lsr #12 @ mva[31:12]\n\t" + "orr %%r2, %%r2, %%r3, lsl #12 @ tlb mva and asid\n\t" + "mcr p15, 0, %%r2, c8, c7, 1 @ utlbimva\n\t" + "isb" + : + : "r" (addr) + : "r2", "r3" + ); +} + +/* + * Set HW MMU entry and do required synchronization operations. + */ +static inline void __set_entry(u32 *entry, u32 *addr, u32 value, int size) +{ + int i; + + if (!entry) + return; + + entry = (u32 *)((u32) entry & ~(size * sizeof(u32) - 1)); + + for (i = 0; i < size; i++) + entry[i] = value; + + __asm__ __volatile__( + "mcr p15, 0, %0, c7, c10, 1 @ flush entry\n\t" + "dsb\n\t" + "isb\n\t" + : + : "r" (entry) + ); + __tlb_invalidate(addr); +} + +/* + * Return the number of duplicate entries associated with entry value. + * Supersections and Large page table entries are replicated 16x. + */ +static inline int __entry_size(int sect, int value) +{ + u32 size; + + if (sect) + size = PMD_IS_SUPERSECTION(value) ? 16 : 1; + else + size = PTE_IS_LARGE(value) ? 16 : 1; + + return size; +} + +/* + * Change entry permissions to emulate domain manager access + */ +static inline int __manager_perm(int sect, int value) +{ + u32 edm_value; + + if (sect) { + edm_value = (value & ~(PMD_SECT_APX | PMD_SECT_XN)) | + (PMD_SECT_AP_READ | PMD_SECT_AP_WRITE); + } else { + edm_value = (value & ~(PTE_EXT_APX | PTE_EXT_XN)) | + (PTE_EXT_AP1 | PTE_EXT_AP0); + } + return edm_value; +} + +/* + * Restore original HW MMU entry. Cancels domain manager access + */ +static inline void __restore_entry(int index) +{ + struct domain_entry_save *entry = &edm_save[index]; + u32 edm_value; + + if (!entry->mmu_entry) + return; + + edm_value = __manager_perm(entry->sect, entry->value); + + if (*entry->mmu_entry == edm_value) + __set_entry(entry->mmu_entry, entry->addr, + entry->value, entry->size); + + entry->mmu_entry = 0; +} + +/* + * Modify HW MMU entry to grant domain manager access for a given MMU entry. + * This adds full read, write, and exec access permissions. + */ +static inline void __set_manager(int sect, u32 *addr) +{ + u32 *entry = sect ? __get_pmd_v7(addr) : __get_pte_v7(addr); + u32 value; + u32 edm_value; + u16 size; + + if (!entry) + return; + + value = *entry; + + size = __entry_size(sect, value); + edm_value = __manager_perm(sect, value); + + __set_entry(entry, addr, edm_value, size); + + __restore_entry(edm_lru); + + edm_save[edm_lru].mmu_entry = entry; + edm_save[edm_lru].addr = addr; + edm_save[edm_lru].value = value; + edm_save[edm_lru].sect = sect; + edm_save[edm_lru].size = size; + + edm_lru = LRU_INC(edm_lru); +} + +/* + * Restore original HW MMU entries. + * + * entry - MVA for HW MMU entry + */ +static inline void __restore(void) +{ + if (unlikely(edm_manager_bits)) { + u32 i; + + for (i = 0; i < DOMAIN_MMU_ENTRIES; i++) + __restore_entry(i); + } +} + +/* + * Common abort handler code + * + * If domain manager was actually set, permission fault would not happen. + * Open access permissions to emulate. Save original settings to restore + * later. Return 1 to pretend fault did not happen. + */ +static int __emulate_domain_manager_abort(u32 fsr, u32 far, int dabort) +{ + if (unlikely(FSR_PERMISSION_FAULT(fsr) && edm_manager_bits)) { + int domain = dabort ? DFSR_DOMAIN(fsr) : PMD_GET_DOMAIN(far); + if (edm_manager_bits & domain_val(domain, DOMAIN_MANAGER)) { + unsigned long flags; + + spin_lock_irqsave(&edm_lock, flags); + + __set_manager(FSR_PERMISSION_SECT(fsr), (u32 *) far); + + spin_unlock_irqrestore(&edm_lock, flags); + return 1; + } + } + return 0; +} + +/* + * Change domain setting. + * + * Lock and restore original contents. Extract and save manager bits. Set + * DACR, excluding manager bits. + */ +void emulate_domain_manager_set(u32 domain) +{ + unsigned long flags; + + spin_lock_irqsave(&edm_lock, flags); + + if (edm_manager_bits != (domain & DOMAIN_MANAGER_BITS)) { + __restore(); + edm_manager_bits = domain & DOMAIN_MANAGER_BITS; + } + + __asm__ __volatile__( + "mcr p15, 0, %0, c3, c0, 0 @ set domain\n\t" + "isb" + : + : "r" (domain & ~DOMAIN_MANAGER_BITS) + ); + + spin_unlock_irqrestore(&edm_lock, flags); +} +EXPORT_SYMBOL_GPL(emulate_domain_manager_set); + +/* + * Switch thread context. Restore original contents. + */ +void emulate_domain_manager_switch_mm(unsigned long pgd_phys, + struct mm_struct *mm, + void (*switch_mm)(unsigned long pgd_phys, struct mm_struct *)) +{ + unsigned long flags; + + spin_lock_irqsave(&edm_lock, flags); + + __restore(); + + /* Call underlying kernel handler */ + switch_mm(pgd_phys, mm); + + spin_unlock_irqrestore(&edm_lock, flags); +} +EXPORT_SYMBOL_GPL(emulate_domain_manager_switch_mm); + +/* + * Kernel data_abort hook + */ +int emulate_domain_manager_data_abort(u32 dfsr, u32 dfar) +{ + return __emulate_domain_manager_abort(dfsr, dfar, 1); +} +EXPORT_SYMBOL_GPL(emulate_domain_manager_data_abort); + +/* + * Kernel prefetch_abort hook + */ +int emulate_domain_manager_prefetch_abort(u32 ifsr, u32 ifar) +{ + return __emulate_domain_manager_abort(ifsr, ifar, 0); +} +EXPORT_SYMBOL_GPL(emulate_domain_manager_prefetch_abort); + diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index 92cce206779e0..c86c6a59f67ae 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -23,11 +23,16 @@ #include #include #include -#ifdef CONFIG_ARCH_MSM_SCORPION +#include +#if defined(CONFIG_ARCH_MSM_SCORPION) && !defined(CONFIG_MSM_SMP) #include #include #endif +#ifdef CONFIG_EMULATE_DOMAIN_MANAGER_V7 +#include +#endif /* CONFIG_EMULATE_DOMAIN_MANAGER_V7 */ + #include "fault.h" /* @@ -465,7 +470,7 @@ do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs) return 1; } -#ifdef CONFIG_ARCH_MSM_SCORPION +#if defined(CONFIG_ARCH_MSM_SCORPION) && !defined(CONFIG_MSM_SMP) #define __str(x) #x #define MRC(x, v1, v2, v4, v5, v6) do { \ unsigned int __##x; \ @@ -482,7 +487,7 @@ do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs) static int do_imprecise_ext(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { -#ifdef CONFIG_ARCH_MSM_SCORPION +#if defined(CONFIG_ARCH_MSM_SCORPION) && !defined(CONFIG_MSM_SMP) MRC(ADFSR, p15, 0, c5, c1, 0); MRC(DFSR, p15, 0, c5, c0, 0); MRC(ACTLR, p15, 0, c1, c0, 1); @@ -502,7 +507,7 @@ do_imprecise_ext(unsigned long addr, unsigned int fsr, struct pt_regs *regs) "mcr p15, 0, %0, c5, c1, 0" : : "r" (0)); #endif -#ifdef CONFIG_ARCH_MSM_SCORPION +#if defined(CONFIG_ARCH_MSM_SCORPION) && !defined(CONFIG_MSM_SMP) pr_info("%s: TCSR_SPARE2 = 0x%.8x\n", __func__, readl(MSM_TCSR_SPARE2)); #endif return 1; @@ -570,6 +575,75 @@ hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *) fsr_info[nr].name = name; } +#ifdef CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER +static int krait_tbb_fixup(unsigned int fsr, struct pt_regs *regs) +{ + int base_cond, cond = 0; + unsigned int p1, cpsr_z, cpsr_c, cpsr_n, cpsr_v; + + if ((read_cpuid_id() & 0xFFFFFFFC) != 0x510F04D0) + return 0; + + if (!thumb_mode(regs)) + return 0; + + /* If ITSTATE is 0, return quickly */ + if ((regs->ARM_cpsr & PSR_IT_MASK) == 0) + return 0; + + cpsr_n = (regs->ARM_cpsr & PSR_N_BIT) ? 1 : 0; + cpsr_z = (regs->ARM_cpsr & PSR_Z_BIT) ? 1 : 0; + cpsr_c = (regs->ARM_cpsr & PSR_C_BIT) ? 1 : 0; + cpsr_v = (regs->ARM_cpsr & PSR_V_BIT) ? 1 : 0; + + p1 = (regs->ARM_cpsr & BIT(12)) ? 1 : 0; + + base_cond = (regs->ARM_cpsr >> 13) & 0x07; + + switch (base_cond) { + case 0x0: /* equal */ + cond = cpsr_z; + break; + + case 0x1: /* carry set */ + cond = cpsr_c; + break; + + case 0x2: /* minus / negative */ + cond = cpsr_n; + break; + + case 0x3: /* overflow */ + cond = cpsr_v; + break; + + case 0x4: /* unsigned higher */ + cond = (cpsr_c == 1) && (cpsr_z == 0); + break; + + case 0x5: /* signed greater / equal */ + cond = (cpsr_n == cpsr_v); + break; + + case 0x6: /* signed greater */ + cond = (cpsr_z == 0) && (cpsr_n == cpsr_v); + break; + + case 0x7: /* always */ + cond = 1; + break; + }; + + if (cond == p1) { + pr_debug("Conditional abort fixup, PC=%08x, base=%d, cond=%d\n", + (unsigned int) regs->ARM_pc, base_cond, cond); + regs->ARM_pc += 2; + return 1; + } + return 0; +} +#endif + /* * Dispatch a data abort to the relevant handler. */ @@ -579,6 +653,16 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) const struct fsr_info *inf = fsr_info + fsr_fs(fsr); struct siginfo info; +#ifdef CONFIG_EMULATE_DOMAIN_MANAGER_V7 + if (emulate_domain_manager_data_abort(fsr, addr)) + return; +#endif + +#ifdef CONFIG_MSM_KRAIT_TBB_ABORT_HANDLER + if (krait_tbb_fixup(fsr, regs)) + return; +#endif + if (!inf->fn(addr, fsr & ~FSR_LNX_PF, regs)) return; @@ -647,6 +731,11 @@ do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs) const struct fsr_info *inf = ifsr_info + fsr_fs(ifsr); struct siginfo info; +#ifdef CONFIG_EMULATE_DOMAIN_MANAGER_V7 + if (emulate_domain_manager_prefetch_abort(ifsr, addr)) + return; +#endif + if (!inf->fn(addr, ifsr | FSR_LNX_PF, regs)) return; diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index f34700d54e66e..2951de78baef1 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -201,6 +201,29 @@ static void __init arm_bootmem_init(unsigned long start_pfn, } } +#ifdef CONFIG_ARCH_POPULATES_NODE_MAP +static void __init arm_bootmem_free_apnm(unsigned long max_low, + unsigned long max_high) +{ + unsigned long max_zone_pfns[MAX_NR_ZONES]; + struct memblock_region *reg; + + memset(max_zone_pfns, 0, sizeof(max_zone_pfns)); + + max_zone_pfns[0] = max_low; +#ifdef CONFIG_HIGHMEM + max_zone_pfns[ZONE_HIGHMEM] = max_high; +#endif + for_each_memblock(memory, reg) { + unsigned long start = memblock_region_memory_base_pfn(reg); + unsigned long end = memblock_region_memory_end_pfn(reg); + + add_active_range(0, start, end); + } + free_area_init_nodes(max_zone_pfns); +} + +#else static void __init arm_bootmem_free(unsigned long min, unsigned long max_low, unsigned long max_high) { @@ -251,6 +274,7 @@ static void __init arm_bootmem_free(unsigned long min, unsigned long max_low, free_area_init_node(0, zone_size, min, zhole_size); } +#endif #ifndef CONFIG_SPARSEMEM int pfn_valid(unsigned long pfn) @@ -280,6 +304,13 @@ static int __init meminfo_cmp(const void *_a, const void *_b) return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; } +#ifdef CONFIG_DONT_MAP_HOLE_AFTER_MEMBANK0 +unsigned long membank0_size; +EXPORT_SYMBOL(membank0_size); +unsigned long membank1_start; +EXPORT_SYMBOL(membank1_start); +#endif + void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc) { int i; @@ -290,6 +321,11 @@ void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc) for (i = 0; i < mi->nr_banks; i++) memblock_add(mi->bank[i].start, mi->bank[i].size); +#ifdef CONFIG_DONT_MAP_HOLE_AFTER_MEMBANK0 + membank0_size = meminfo.bank[0].size; + membank1_start = meminfo.bank[1].start; +#endif + /* Register the kernel text, kernel data and initrd with memblock. */ #ifdef CONFIG_XIP_KERNEL memblock_reserve(__pa(_sdata), _end - _sdata); @@ -322,6 +358,28 @@ void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc) memblock_dump_all(); } +#ifdef CONFIG_MEMORY_HOTPLUG_SPARSE +int _early_pfn_valid(unsigned long pfn) +{ + struct meminfo *mi = &meminfo; + unsigned int left = 0, right = mi->nr_banks; + + do { + unsigned int mid = (right + left) / 2; + struct membank *bank = &mi->bank[mid]; + + if (pfn < bank_pfn_start(bank)) + right = mid; + else if (pfn >= bank_pfn_end(bank)) + left = mid + 1; + else + return 1; + } while (left < right); + return 0; +} +EXPORT_SYMBOL(_early_pfn_valid); +#endif + void __init bootmem_init(void) { unsigned long min, max_low, max_high; @@ -343,12 +401,16 @@ void __init bootmem_init(void) */ sparse_init(); +#ifdef CONFIG_ARCH_POPULATES_NODE_MAP + arm_bootmem_free_apnm(max_low, max_high); +#else /* * Now free the memory - free_area_init_node needs * the sparse mem_map arrays initialized by sparse_init() * for memmap_init_zone(), otherwise all PFNs are invalid. */ arm_bootmem_free(min, max_low, max_high); +#endif high_memory = __va((max_low << PAGE_SHIFT) - 1) + 1; @@ -392,7 +454,7 @@ free_memmap(unsigned long start_pfn, unsigned long end_pfn) * Convert start_pfn/end_pfn to a struct page pointer. */ start_pg = pfn_to_page(start_pfn - 1) + 1; - end_pg = pfn_to_page(end_pfn); + end_pg = pfn_to_page(end_pfn - 1) + 1; /* * Convert to physical addresses, and @@ -410,7 +472,10 @@ free_memmap(unsigned long start_pfn, unsigned long end_pfn) } /* - * The mem_map array can get very big. Free the unused area of the memory map. + * The mem_map array can get very big. Free as much of the unused portion of + * the mem_map that we are allowed to. The page migration code moves pages + * in blocks that are rounded per the MAX_ORDER_NR_PAGES definition, so we + * can't free mem_map entries that may be dereferenced in this manner. */ static void __init free_unused_memmap(struct meminfo *mi) { @@ -424,12 +489,17 @@ static void __init free_unused_memmap(struct meminfo *mi) for_each_bank(i, mi) { struct membank *bank = &mi->bank[i]; - /* Round bank_start down to the start of a pageblock so that - * all pages in a pageblock always have a mapping. - */ bank_start = round_down(bank_pfn_start(bank), MAX_ORDER_NR_PAGES); +#ifdef CONFIG_SPARSEMEM + /* + * Take care not to free memmap entries that don't exist + * due to SPARSEMEM sections which aren't present. + */ + bank_start = min(bank_start, + ALIGN(prev_bank_end, PAGES_PER_SECTION)); +#endif /* * If we had a previous bank, and there is a space * between the current bank and the previous, free it. @@ -437,13 +507,15 @@ static void __init free_unused_memmap(struct meminfo *mi) if (prev_bank_end && prev_bank_end < bank_start) free_memmap(prev_bank_end, bank_start); - /* - * Align up here since the VM subsystem insists that the - * memmap entries are valid from the bank end aligned to - * MAX_ORDER_NR_PAGES. - */ - prev_bank_end = ALIGN(bank_pfn_end(bank), MAX_ORDER_NR_PAGES); + prev_bank_end = round_up(bank_pfn_end(bank), + MAX_ORDER_NR_PAGES); } + +#ifdef CONFIG_SPARSEMEM + if (!IS_ALIGNED(prev_bank_end, PAGES_PER_SECTION)) + free_memmap(prev_bank_end, + ALIGN(prev_bank_end, PAGES_PER_SECTION)); +#endif } static void __init free_highpages(void) @@ -546,7 +618,14 @@ void __init mem_init(void) else if (!page_count(page)) free_pages++; page++; +#ifdef CONFIG_SPARSEMEM + pfn1++; + if (!(pfn1 % PAGES_PER_SECTION)) + page = pfn_to_page(pfn1); + } while (pfn1 < pfn2); +#else } while (page < end); +#endif } /* @@ -663,6 +742,33 @@ void free_initmem(void) "init"); } +#ifdef CONFIG_MEMORY_HOTPLUG +int arch_add_memory(int nid, u64 start, u64 size) +{ + struct pglist_data *pgdata = NODE_DATA(nid); + struct zone *zone = pgdata->node_zones + ZONE_MOVABLE; + unsigned long start_pfn = start >> PAGE_SHIFT; + unsigned long nr_pages = size >> PAGE_SHIFT; + + return __add_pages(nid, zone, start_pfn, nr_pages); +} + +int arch_physical_active_memory(u64 start, u64 size) +{ + return platform_physical_active_pages(start, size); +} + +int arch_physical_remove_memory(u64 start, u64 size) +{ + return platform_physical_remove_pages(start, size); +} + +int arch_physical_low_power_memory(u64 start, u64 size) +{ + return platform_physical_low_power_pages(start, size); +} +#endif + #ifdef CONFIG_BLK_DEV_INITRD static int keep_initrd; diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index ab506272b2d3e..17e7b0b57e49f 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c @@ -204,8 +204,12 @@ void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn, /* * Don't allow RAM to be mapped - this causes problems with ARMv6+ */ - if (WARN_ON(pfn_valid(pfn))) - return NULL; + if (pfn_valid(pfn)) { + printk(KERN_WARNING "BUG: Your driver calls ioremap() on system memory. This leads\n" + KERN_WARNING "to architecturally unpredictable behaviour on ARMv6+, and ioremap()\n" + KERN_WARNING "will fail in the next kernel release. Please fix your driver.\n"); + WARN_ON(1); + } type = get_mem_type(mtype); if (!type) diff --git a/arch/arm/mm/mm.h b/arch/arm/mm/mm.h index 36960df5fb762..229200dbd7ebd 100644 --- a/arch/arm/mm/mm.h +++ b/arch/arm/mm/mm.h @@ -15,12 +15,7 @@ static inline pmd_t *pmd_off_k(unsigned long virt) return pmd_off(pgd_offset_k(virt), virt); } -struct mem_type { - pteval_t prot_pte; - unsigned int prot_l1; - unsigned int prot_sect; - unsigned int domain; -}; +struct mem_type; const struct mem_type *get_mem_type(unsigned int type); @@ -28,5 +23,8 @@ extern void __flush_dcache_page(struct address_space *mapping, struct page *page #endif +struct map_desc; + void __init bootmem_init(void); void arm_mm_memblock_reserve(void); +void __init create_mapping(struct map_desc *md); diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index 27c20ce568285..55def0a790d5e 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -214,6 +214,12 @@ static struct mem_type mem_types[] = { .prot_sect = PROT_SECT_DEVICE | PMD_SECT_WB, .domain = DOMAIN_IO, }, + [MT_DEVICE_STRONGLY_ORDERED] = { /* Guaranteed strongly ordered */ + .prot_pte = PROT_PTE_DEVICE, + .prot_l1 = PMD_TYPE_TABLE, + .prot_sect = PROT_SECT_DEVICE | PMD_SECT_UNCACHED, + .domain = DOMAIN_IO, + }, [MT_DEVICE_WC] = { /* ioremap_wc */ .prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_WC, .prot_l1 = PMD_TYPE_TABLE, @@ -252,6 +258,18 @@ static struct mem_type mem_types[] = { .prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE, .domain = DOMAIN_KERNEL, }, + [MT_MEMORY_R] = { + .prot_sect = PMD_TYPE_SECT | PMD_SECT_XN, + .domain = DOMAIN_KERNEL, + }, + [MT_MEMORY_RW] = { + .prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_XN, + .domain = DOMAIN_KERNEL, + }, + [MT_MEMORY_RX] = { + .prot_sect = PMD_TYPE_SECT, + .domain = DOMAIN_KERNEL, + }, [MT_ROM] = { .prot_sect = PMD_TYPE_SECT, .domain = DOMAIN_KERNEL, @@ -355,6 +373,8 @@ static void __init build_mem_type_table(void) mem_types[MT_DEVICE_NONSHARED].prot_sect |= PMD_SECT_XN; mem_types[MT_DEVICE_CACHED].prot_sect |= PMD_SECT_XN; mem_types[MT_DEVICE_WC].prot_sect |= PMD_SECT_XN; + mem_types[MT_DEVICE_STRONGLY_ORDERED].prot_sect |= + PMD_SECT_XN; } if (cpu_arch >= CPU_ARCH_ARMv7 && (cr & CR_TRE)) { /* @@ -428,6 +448,8 @@ static void __init build_mem_type_table(void) * from SVC mode and no access from userspace. */ mem_types[MT_ROM].prot_sect |= PMD_SECT_APX|PMD_SECT_AP_WRITE; + mem_types[MT_MEMORY_RX].prot_sect |= PMD_SECT_APX|PMD_SECT_AP_WRITE; + mem_types[MT_MEMORY_R].prot_sect |= PMD_SECT_APX|PMD_SECT_AP_WRITE; mem_types[MT_MINICLEAN].prot_sect |= PMD_SECT_APX|PMD_SECT_AP_WRITE; mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_APX|PMD_SECT_AP_WRITE; @@ -446,6 +468,9 @@ static void __init build_mem_type_table(void) mem_types[MT_MEMORY].prot_sect |= PMD_SECT_S; mem_types[MT_MEMORY].prot_pte |= L_PTE_SHARED; mem_types[MT_MEMORY_NONCACHED].prot_sect |= PMD_SECT_S; + mem_types[MT_MEMORY_R].prot_sect |= PMD_SECT_S; + mem_types[MT_MEMORY_RW].prot_sect |= PMD_SECT_S; + mem_types[MT_MEMORY_RX].prot_sect |= PMD_SECT_S; mem_types[MT_MEMORY_NONCACHED].prot_pte |= L_PTE_SHARED; } } @@ -485,6 +510,9 @@ static void __init build_mem_type_table(void) mem_types[MT_MEMORY].prot_sect |= ecc_mask | cp->pmd; mem_types[MT_MEMORY].prot_pte |= kern_pgprot; mem_types[MT_MEMORY_NONCACHED].prot_sect |= ecc_mask; + mem_types[MT_MEMORY_R].prot_sect |= ecc_mask | cp->pmd; + mem_types[MT_MEMORY_RW].prot_sect |= ecc_mask | cp->pmd; + mem_types[MT_MEMORY_RX].prot_sect |= ecc_mask | cp->pmd; mem_types[MT_ROM].prot_sect |= cp->pmd; switch (cp->pmd) { @@ -650,7 +678,7 @@ static void __init create_36bit_mapping(struct map_desc *md, * offsets, and we take full advantage of sections and * supersections. */ -static void __init create_mapping(struct map_desc *md) +void __init create_mapping(struct map_desc *md) { unsigned long phys, addr, length, end; const struct mem_type *type; @@ -714,8 +742,7 @@ void __init iotable_init(struct map_desc *io_desc, int nr) create_mapping(io_desc + i); } -static void * __initdata vmalloc_min = (void *)(VMALLOC_END - - CONFIG_VMALLOC_RESERVE); +static void * __initdata vmalloc_min = (void *)(VMALLOC_END - CONFIG_VMALLOC_RESERVE); /* * vmalloc=size forces the vmalloc area to be exactly 'size' @@ -750,7 +777,12 @@ static phys_addr_t lowmem_limit __initdata = 0; static void __init sanity_check_meminfo(void) { int i, j, highmem = 0; +#if (defined CONFIG_HIGHMEM) && (defined CONFIG_FIX_MOVABLE_ZONE) + void *v_movable_start = __va(movable_reserved_start); + if (vmalloc_min > v_movable_start) + vmalloc_min = v_movable_start - SECTION_SIZE; +#endif lowmem_limit = __pa(vmalloc_min - 1) + 1; memblock_set_current_limit(lowmem_limit); @@ -1012,8 +1044,39 @@ static void __init map_lowmem(void) map.pfn = __phys_to_pfn(start); map.virtual = __phys_to_virt(start); +#ifdef CONFIG_STRICT_MEMORY_RWX + if (start <= __pa(_text) && __pa(_text) < end) { + map.length = (unsigned long)_text - map.virtual; + map.type = MT_MEMORY; + + create_mapping(&map); + + map.pfn = __phys_to_pfn(__pa(_text)); + map.virtual = (unsigned long)_text; + map.length = __start_rodata - _text; + map.type = MT_MEMORY_RX; + + create_mapping(&map); + + map.pfn = __phys_to_pfn(__pa(__start_rodata)); + map.virtual = (unsigned long)__start_rodata; + map.length = _sdata - __start_rodata; + map.type = MT_MEMORY_R; + + create_mapping(&map); + + map.pfn = __phys_to_pfn(__pa(_sdata)); + map.virtual = (unsigned long)_sdata; + map.length = __phys_to_virt(end) - (unsigned int)_sdata; + map.type = MT_MEMORY_RW; + } else { + map.length = end - start; + map.type = MT_MEMORY_RW; + } +#else map.length = end - start; map.type = MT_MEMORY; +#endif create_mapping(&map); } @@ -1043,4 +1106,16 @@ void __init paging_init(struct machine_desc *mdesc) empty_zero_page = virt_to_page(zero_page); __flush_dcache_page(NULL, empty_zero_page); + +#if defined(CONFIG_ARCH_MSM7X27) + /* + * ensure that the strongly ordered page is mapped before the + * first call to write_to_strongly_ordered_memory. This page + * is necessary for the msm 7x27 due to hardware quirks. The + * map call is made here to ensure the bootmem call is made + * in the right window (after initialization, before full + * allocators are initialized) + */ + map_page_strongly_ordered(); +#endif } diff --git a/arch/arm/mm/proc-arm1020.S b/arch/arm/mm/proc-arm1020.S index bcf748d9f4e25..41ab64752ff3c 100644 --- a/arch/arm/mm/proc-arm1020.S +++ b/arch/arm/mm/proc-arm1020.S @@ -275,7 +275,7 @@ ENTRY(arm1020_flush_kern_dcache_area) * * (same as v4wb) */ -arm1020_dma_inv_range: +ENTRY(arm1020_dma_inv_range) mov ip, #0 #ifndef CONFIG_CPU_DCACHE_DISABLE tst r0, #CACHE_DLINESIZE - 1 @@ -305,7 +305,7 @@ arm1020_dma_inv_range: * * (same as v4wb) */ -arm1020_dma_clean_range: +ENTRY(arm1020_dma_clean_range) mov ip, #0 #ifndef CONFIG_CPU_DCACHE_DISABLE bic r0, r0, #CACHE_DLINESIZE - 1 @@ -374,6 +374,8 @@ ENTRY(arm1020_cache_fns) .long arm1020_flush_kern_dcache_area .long arm1020_dma_map_area .long arm1020_dma_unmap_area + .long arm1020_dma_inv_range + .long arm1020_dma_clean_range .long arm1020_dma_flush_range .align 5 diff --git a/arch/arm/mm/proc-arm1020e.S b/arch/arm/mm/proc-arm1020e.S index ab7ec26657eaf..d22f3260c8944 100644 --- a/arch/arm/mm/proc-arm1020e.S +++ b/arch/arm/mm/proc-arm1020e.S @@ -268,7 +268,7 @@ ENTRY(arm1020e_flush_kern_dcache_area) * * (same as v4wb) */ -arm1020e_dma_inv_range: +ENTRY(arm1020e_dma_inv_range) mov ip, #0 #ifndef CONFIG_CPU_DCACHE_DISABLE tst r0, #CACHE_DLINESIZE - 1 @@ -294,7 +294,7 @@ arm1020e_dma_inv_range: * * (same as v4wb) */ -arm1020e_dma_clean_range: +ENTRY(arm1020e_dma_clean_range) mov ip, #0 #ifndef CONFIG_CPU_DCACHE_DISABLE bic r0, r0, #CACHE_DLINESIZE - 1 @@ -360,6 +360,8 @@ ENTRY(arm1020e_cache_fns) .long arm1020e_flush_kern_dcache_area .long arm1020e_dma_map_area .long arm1020e_dma_unmap_area + .long arm1020e_dma_inv_range + .long arm1020e_dma_clean_range .long arm1020e_dma_flush_range .align 5 diff --git a/arch/arm/mm/proc-arm1022.S b/arch/arm/mm/proc-arm1022.S index 831c5e54e22f0..d31ce0256f746 100644 --- a/arch/arm/mm/proc-arm1022.S +++ b/arch/arm/mm/proc-arm1022.S @@ -257,7 +257,7 @@ ENTRY(arm1022_flush_kern_dcache_area) * * (same as v4wb) */ -arm1022_dma_inv_range: +ENTRY(arm1022_dma_inv_range) mov ip, #0 #ifndef CONFIG_CPU_DCACHE_DISABLE tst r0, #CACHE_DLINESIZE - 1 @@ -283,7 +283,7 @@ arm1022_dma_inv_range: * * (same as v4wb) */ -arm1022_dma_clean_range: +ENTRY(arm1022_dma_clean_range) mov ip, #0 #ifndef CONFIG_CPU_DCACHE_DISABLE bic r0, r0, #CACHE_DLINESIZE - 1 @@ -349,6 +349,8 @@ ENTRY(arm1022_cache_fns) .long arm1022_flush_kern_dcache_area .long arm1022_dma_map_area .long arm1022_dma_unmap_area + .long arm1022_dma_inv_range + .long arm1022_dma_clean_range .long arm1022_dma_flush_range .align 5 diff --git a/arch/arm/mm/proc-arm1026.S b/arch/arm/mm/proc-arm1026.S index e3f7e9a166bfc..14c25f58e6c30 100644 --- a/arch/arm/mm/proc-arm1026.S +++ b/arch/arm/mm/proc-arm1026.S @@ -251,7 +251,7 @@ ENTRY(arm1026_flush_kern_dcache_area) * * (same as v4wb) */ -arm1026_dma_inv_range: +ENTRY(arm1026_dma_inv_range) mov ip, #0 #ifndef CONFIG_CPU_DCACHE_DISABLE tst r0, #CACHE_DLINESIZE - 1 @@ -277,7 +277,7 @@ arm1026_dma_inv_range: * * (same as v4wb) */ -arm1026_dma_clean_range: +ENTRY(arm1026_dma_clean_range) mov ip, #0 #ifndef CONFIG_CPU_DCACHE_DISABLE bic r0, r0, #CACHE_DLINESIZE - 1 @@ -343,6 +343,8 @@ ENTRY(arm1026_cache_fns) .long arm1026_flush_kern_dcache_area .long arm1026_dma_map_area .long arm1026_dma_unmap_area + .long arm1026_dma_inv_range + .long arm1026_dma_clean_range .long arm1026_dma_flush_range .align 5 diff --git a/arch/arm/mm/proc-arm920.S b/arch/arm/mm/proc-arm920.S index 6109f278a9045..35bd31ddd6285 100644 --- a/arch/arm/mm/proc-arm920.S +++ b/arch/arm/mm/proc-arm920.S @@ -242,7 +242,7 @@ ENTRY(arm920_flush_kern_dcache_area) * * (same as v4wb) */ -arm920_dma_inv_range: +ENTRY(arm920_dma_inv_range) tst r0, #CACHE_DLINESIZE - 1 bic r0, r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -265,7 +265,7 @@ arm920_dma_inv_range: * * (same as v4wb) */ -arm920_dma_clean_range: +ENTRY(arm920_dma_clean_range) bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #CACHE_DLINESIZE @@ -325,6 +325,8 @@ ENTRY(arm920_cache_fns) .long arm920_flush_kern_dcache_area .long arm920_dma_map_area .long arm920_dma_unmap_area + .long arm920_dma_inv_range + .long arm920_dma_clean_range .long arm920_dma_flush_range #endif diff --git a/arch/arm/mm/proc-arm922.S b/arch/arm/mm/proc-arm922.S index bb2f0f46a5e6c..ee6c4daac23b2 100644 --- a/arch/arm/mm/proc-arm922.S +++ b/arch/arm/mm/proc-arm922.S @@ -244,7 +244,7 @@ ENTRY(arm922_flush_kern_dcache_area) * * (same as v4wb) */ -arm922_dma_inv_range: +ENTRY(arm922_dma_inv_range) tst r0, #CACHE_DLINESIZE - 1 bic r0, r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -267,7 +267,7 @@ arm922_dma_inv_range: * * (same as v4wb) */ -arm922_dma_clean_range: +ENTRY(arm922_dma_clean_range) bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #CACHE_DLINESIZE @@ -327,6 +327,8 @@ ENTRY(arm922_cache_fns) .long arm922_flush_kern_dcache_area .long arm922_dma_map_area .long arm922_dma_unmap_area + .long arm922_dma_inv_range + .long arm922_dma_clean_range .long arm922_dma_flush_range #endif diff --git a/arch/arm/mm/proc-arm925.S b/arch/arm/mm/proc-arm925.S index c13e01accfe2e..ba147cff4685e 100644 --- a/arch/arm/mm/proc-arm925.S +++ b/arch/arm/mm/proc-arm925.S @@ -290,7 +290,7 @@ ENTRY(arm925_flush_kern_dcache_area) * * (same as v4wb) */ -arm925_dma_inv_range: +ENTRY(arm925_dma_inv_range) #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH tst r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -315,7 +315,7 @@ arm925_dma_inv_range: * * (same as v4wb) */ -arm925_dma_clean_range: +ENTRY(arm925_dma_clean_range) #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry @@ -382,6 +382,8 @@ ENTRY(arm925_cache_fns) .long arm925_flush_kern_dcache_area .long arm925_dma_map_area .long arm925_dma_unmap_area + .long arm925_dma_inv_range + .long arm925_dma_clean_range .long arm925_dma_flush_range ENTRY(cpu_arm925_dcache_clean_area) diff --git a/arch/arm/mm/proc-arm926.S b/arch/arm/mm/proc-arm926.S index 42eb4315740b1..41c9b5dd573d3 100644 --- a/arch/arm/mm/proc-arm926.S +++ b/arch/arm/mm/proc-arm926.S @@ -253,7 +253,7 @@ ENTRY(arm926_flush_kern_dcache_area) * * (same as v4wb) */ -arm926_dma_inv_range: +ENTRY(arm926_dma_inv_range) #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH tst r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -278,7 +278,7 @@ arm926_dma_inv_range: * * (same as v4wb) */ -arm926_dma_clean_range: +ENTRY(arm926_dma_clean_range) #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry @@ -345,6 +345,8 @@ ENTRY(arm926_cache_fns) .long arm926_flush_kern_dcache_area .long arm926_dma_map_area .long arm926_dma_unmap_area + .long arm926_dma_inv_range + .long arm926_dma_clean_range .long arm926_dma_flush_range ENTRY(cpu_arm926_dcache_clean_area) diff --git a/arch/arm/mm/proc-arm940.S b/arch/arm/mm/proc-arm940.S index 7b11cdb9935ff..937f978ac85e8 100644 --- a/arch/arm/mm/proc-arm940.S +++ b/arch/arm/mm/proc-arm940.S @@ -178,7 +178,7 @@ ENTRY(arm940_flush_kern_dcache_area) * - start - virtual start address * - end - virtual end address */ -arm940_dma_inv_range: +ENTRY(arm940_dma_inv_range) mov ip, #0 mov r1, #(CACHE_DSEGMENTS - 1) << 4 @ 4 segments 1: orr r3, r1, #(CACHE_DENTRIES - 1) << 26 @ 64 entries @@ -199,7 +199,7 @@ arm940_dma_inv_range: * - start - virtual start address * - end - virtual end address */ -arm940_dma_clean_range: +ENTRY(arm940_dma_clean_range) ENTRY(cpu_arm940_dcache_clean_area) mov ip, #0 #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH @@ -274,6 +274,8 @@ ENTRY(arm940_cache_fns) .long arm940_flush_kern_dcache_area .long arm940_dma_map_area .long arm940_dma_unmap_area + .long arm940_dma_inv_range + .long arm940_dma_clean_range .long arm940_dma_flush_range __CPUINIT diff --git a/arch/arm/mm/proc-arm946.S b/arch/arm/mm/proc-arm946.S index 1a5bbf0803427..76be43b359e1d 100644 --- a/arch/arm/mm/proc-arm946.S +++ b/arch/arm/mm/proc-arm946.S @@ -222,7 +222,7 @@ ENTRY(arm946_flush_kern_dcache_area) * - end - virtual end address * (same as arm926) */ -arm946_dma_inv_range: +ENTRY(arm946_dma_inv_range) #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH tst r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -247,7 +247,7 @@ arm946_dma_inv_range: * * (same as arm926) */ -arm946_dma_clean_range: +ENTRY(arm946_dma_clean_range) #ifndef CONFIG_CPU_DCACHE_WRITETHROUGH bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry @@ -316,6 +316,8 @@ ENTRY(arm946_cache_fns) .long arm946_flush_kern_dcache_area .long arm946_dma_map_area .long arm946_dma_unmap_area + .long arm946_dma_inv_range + .long arm946_dma_clean_range .long arm946_dma_flush_range diff --git a/arch/arm/mm/proc-feroceon.S b/arch/arm/mm/proc-feroceon.S index b4597edbff97f..0fde230c457f4 100644 --- a/arch/arm/mm/proc-feroceon.S +++ b/arch/arm/mm/proc-feroceon.S @@ -280,7 +280,7 @@ ENTRY(feroceon_range_flush_kern_dcache_area) * (same as v4wb) */ .align 5 -feroceon_dma_inv_range: +ENTRY(feroceon_dma_inv_range) tst r0, #CACHE_DLINESIZE - 1 bic r0, r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -294,7 +294,7 @@ feroceon_dma_inv_range: mov pc, lr .align 5 -feroceon_range_dma_inv_range: +ENTRY(feroceon_range_dma_inv_range) mrs r2, cpsr tst r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -320,7 +320,7 @@ feroceon_range_dma_inv_range: * (same as v4wb) */ .align 5 -feroceon_dma_clean_range: +ENTRY(feroceon_dma_clean_range) bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #CACHE_DLINESIZE @@ -330,7 +330,7 @@ feroceon_dma_clean_range: mov pc, lr .align 5 -feroceon_range_dma_clean_range: +ENTRY(feroceon_range_dma_clean_range) mrs r2, cpsr cmp r1, r0 subne r1, r1, #1 @ top address is inclusive @@ -421,6 +421,8 @@ ENTRY(feroceon_cache_fns) .long feroceon_flush_kern_dcache_area .long feroceon_dma_map_area .long feroceon_dma_unmap_area + .long feroceon_dma_inv_range + .long feroceon_dma_clean_range .long feroceon_dma_flush_range ENTRY(feroceon_range_cache_fns) @@ -433,6 +435,8 @@ ENTRY(feroceon_range_cache_fns) .long feroceon_range_flush_kern_dcache_area .long feroceon_range_dma_map_area .long feroceon_dma_unmap_area + .long feroceon_range_dma_inv_range + .long feroceon_range_dma_clean_range .long feroceon_range_dma_flush_range .align 5 diff --git a/arch/arm/mm/proc-mohawk.S b/arch/arm/mm/proc-mohawk.S index 4458ee6aa7133..0b94767e64bc3 100644 --- a/arch/arm/mm/proc-mohawk.S +++ b/arch/arm/mm/proc-mohawk.S @@ -214,7 +214,7 @@ ENTRY(mohawk_flush_kern_dcache_area) * * (same as v4wb) */ -mohawk_dma_inv_range: +ENTRY(mohawk_dma_inv_range) tst r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry tst r1, #CACHE_DLINESIZE - 1 @@ -237,7 +237,7 @@ mohawk_dma_inv_range: * * (same as v4wb) */ -mohawk_dma_clean_range: +ENTRY(mohawk_dma_clean_range) bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #CACHE_DLINESIZE @@ -297,6 +297,8 @@ ENTRY(mohawk_cache_fns) .long mohawk_flush_kern_dcache_area .long mohawk_dma_map_area .long mohawk_dma_unmap_area + .long mohawk_dma_inv_range + .long mohawk_dma_clean_range .long mohawk_dma_flush_range ENTRY(cpu_mohawk_dcache_clean_area) diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index 9f863e3085a05..cb25277f1f810 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -61,7 +62,14 @@ ENDPROC(cpu_v7_proc_fin) */ .align 5 ENTRY(cpu_v7_reset) - mov pc, r0 + mrc p15, 0, r1, c1, c0, 0 @ ctrl register + bic r1, r1, #0x0001 @ ...............m + mcr p15, 0, r1, c1, c0, 0 @ Turn off MMU + mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D,flush TLB + mcr p15, 0, ip, c7, c5, 6 @ flush BTC + dsb + isb + mov pc,r0 ENDPROC(cpu_v7_reset) /* @@ -101,6 +109,11 @@ ENDPROC(cpu_v7_dcache_clean_area) */ ENTRY(cpu_v7_switch_mm) #ifdef CONFIG_MMU +#ifdef CONFIG_EMULATE_DOMAIN_MANAGER_V7 + ldr r2, =cpu_v7_switch_mm_private + b emulate_domain_manager_switch_mm +cpu_v7_switch_mm_private: +#endif mov r2, #0 ldr r1, [r1, #MM_CONTEXT_ID] @ get mm->context.id ALT_SMP(orr r0, r0, #TTB_FLAGS_SMP) @@ -189,9 +202,8 @@ cpu_v7_name: * - cache type register is implemented */ __v7_ca9mp_setup: -#ifdef CONFIG_SMP - ALT_SMP(mrc p15, 0, r0, c1, c0, 1) - ALT_UP(mov r0, #(1 << 6)) @ fake it for UP +#if defined(CONFIG_SMP) + mrc p15, 0, r0, c1, c0, 1 tst r0, #(1 << 6) @ SMP/nAMP mode enabled? orreq r0, r0, #(1 << 6) | (1 << 0) @ Enable SMP/nAMP mode and mcreq p15, 0, r0, c1, c0, 1 @ TLB ops broadcasting @@ -282,21 +294,35 @@ __v7_setup: ALT_SMP(orr r4, r4, #TTB_FLAGS_SMP) ALT_UP(orr r4, r4, #TTB_FLAGS_UP) mcr p15, 0, r4, c2, c0, 1 @ load TTB1 -#ifdef CONFIG_ARCH_MSM_SCORPION -#ifdef CONFIG_ARCH_QSD8X50 - mov r0, #0x77 -#else - mov r0, #0x33 +#ifndef CONFIG_EMULATE_DOMAIN_MANAGER_V7 + mov r10, #0x1f @ domains 0, 1 = manager + mcr p15, 0, r10, c3, c0, 0 @ load domain access register #endif +#if defined(CONFIG_ARCH_MSM_SCORPION) && !defined(CONFIG_MSM_SMP) + mov r0, #0x33 mcr p15, 3, r0, c15, c0, 3 @ set L2CR1 +#endif +#if defined (CONFIG_ARCH_MSM_SCORPION) mrc p15, 0, r0, c1, c0, 1 @ read ACTLR #ifdef CONFIG_CPU_CACHE_ERR_REPORT orr r0, r0, #0x37 @ turn on L1/L2 error reporting #else bic r0, r0, #0x37 #endif +#if defined (CONFIG_ARCH_MSM_SCORPIONMP) + orr r0, r0, #0x1 << 24 @ optimal setting for Scorpion MP +#endif +#ifndef CONFIG_ARCH_MSM_KRAIT mcr p15, 0, r0, c1, c0, 1 @ write ACTLR #endif +#endif + +#if defined (CONFIG_ARCH_MSM_SCORPIONMP) + mrc p15, 3, r0, c15, c0, 2 @ optimal setting for Scorpion MP + orr r0, r0, #0x1 << 21 + mcr p15, 3, r0, c15, c0, 2 +#endif + /* * Memory region attributes with SCTLR.TRE=1 * @@ -326,7 +352,11 @@ __v7_setup: * NOS = PRRR[24+n] = 1 - not outer shareable */ ldr r5, =0xff0a81a8 @ PRRR +#ifdef CONFIG_MSM_SMP + ldr r6, =0x40e080e0 @ NMRR +#else ldr r6, =0x40e040e0 @ NMRR +#endif mcr p15, 0, r5, c10, c2, 0 @ write PRRR mcr p15, 0, r6, c10, c2, 1 @ write NMRR #endif diff --git a/arch/arm/mm/proc-xsc3.S b/arch/arm/mm/proc-xsc3.S index ec26355cb7c25..f1bd758a63d07 100644 --- a/arch/arm/mm/proc-xsc3.S +++ b/arch/arm/mm/proc-xsc3.S @@ -264,7 +264,7 @@ ENTRY(xsc3_flush_kern_dcache_area) * - start - virtual start address * - end - virtual end address */ -xsc3_dma_inv_range: +ENTRY(xsc3_dma_inv_range) tst r0, #CACHELINESIZE - 1 bic r0, r0, #CACHELINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean L1 D line @@ -285,7 +285,7 @@ xsc3_dma_inv_range: * - start - virtual start address * - end - virtual end address */ -xsc3_dma_clean_range: +ENTRY(xsc3_dma_clean_range) bic r0, r0, #CACHELINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean L1 D line add r0, r0, #CACHELINESIZE @@ -345,6 +345,8 @@ ENTRY(xsc3_cache_fns) .long xsc3_flush_kern_dcache_area .long xsc3_dma_map_area .long xsc3_dma_unmap_area + .long xsc3_dma_inv_range + .long xsc3_dma_clean_range .long xsc3_dma_flush_range ENTRY(cpu_xsc3_dcache_clean_area) diff --git a/arch/arm/mm/proc-xscale.S b/arch/arm/mm/proc-xscale.S index 5a37c5e45c411..456b9f6e80601 100644 --- a/arch/arm/mm/proc-xscale.S +++ b/arch/arm/mm/proc-xscale.S @@ -322,7 +322,7 @@ ENTRY(xscale_flush_kern_dcache_area) * - start - virtual start address * - end - virtual end address */ -xscale_dma_inv_range: +ENTRY(xscale_dma_inv_range) tst r0, #CACHELINESIZE - 1 bic r0, r0, #CACHELINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry @@ -343,7 +343,7 @@ xscale_dma_inv_range: * - start - virtual start address * - end - virtual end address */ -xscale_dma_clean_range: +ENTRY(xscale_dma_clean_range) bic r0, r0, #CACHELINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #CACHELINESIZE @@ -417,6 +417,8 @@ ENTRY(xscale_cache_fns) .long xscale_flush_kern_dcache_area .long xscale_dma_map_area .long xscale_dma_unmap_area + .long xscale_dma_inv_range + .long xscale_dma_clean_range .long xscale_dma_flush_range /* diff --git a/arch/arm/mm/tlb-v7.S b/arch/arm/mm/tlb-v7.S index 53cd5b4546731..8338c6e37d612 100644 --- a/arch/arm/mm/tlb-v7.S +++ b/arch/arm/mm/tlb-v7.S @@ -38,11 +38,19 @@ ENTRY(v7wbi_flush_user_tlb_range) dsb mov r0, r0, lsr #PAGE_SHIFT @ align address mov r1, r1, lsr #PAGE_SHIFT +#ifdef CONFIG_ARCH_MSM8X60 + mov r0, r0, lsl #PAGE_SHIFT +#else asid r3, r3 @ mask ASID orr r0, r3, r0, lsl #PAGE_SHIFT @ Create initial MVA +#endif mov r1, r1, lsl #PAGE_SHIFT 1: +#ifdef CONFIG_ARCH_MSM8X60 + ALT_SMP(mcr p15, 0, r0, c8, c3, 3) @ TLB invalidate U MVA (shareable) +#else ALT_SMP(mcr p15, 0, r0, c8, c3, 1) @ TLB invalidate U MVA (shareable) +#endif ALT_UP(mcr p15, 0, r0, c8, c7, 1) @ TLB invalidate U MVA add r0, r0, #PAGE_SZ @@ -70,7 +78,11 @@ ENTRY(v7wbi_flush_kern_tlb_range) mov r0, r0, lsl #PAGE_SHIFT mov r1, r1, lsl #PAGE_SHIFT 1: +#ifdef CONFIG_ARCH_MSM8X60 + ALT_SMP(mcr p15, 0, r0, c8, c3, 3) @ TLB invalidate U MVA (shareable) +#else ALT_SMP(mcr p15, 0, r0, c8, c3, 1) @ TLB invalidate U MVA (shareable) +#endif ALT_UP(mcr p15, 0, r0, c8, c7, 1) @ TLB invalidate U MVA add r0, r0, #PAGE_SZ cmp r0, r1 diff --git a/arch/arm/mm/vcm.c b/arch/arm/mm/vcm.c new file mode 100644 index 0000000000000..5c52a9cd00e35 --- /dev/null +++ b/arch/arm/mm/vcm.c @@ -0,0 +1,1830 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* alloc_vm_area */ +#include +#include +#include + +#include +#include + +#define ONE_TO_ONE_CHK 1 + +#define vcm_err(a, ...) \ + pr_err("ERROR %s %i " a, __func__, __LINE__, ##__VA_ARGS__) + +static unsigned int smmu_map_sizes[4] = {SZ_16M, SZ_1M, SZ_64K, SZ_4K}; + +static phys_addr_t *bootmem_cont; +static int cont_sz; +static struct vcm *cont_vcm_id; +static struct phys_chunk *cont_phys_chunk; + +DEFINE_SPINLOCK(vcmlock); + +/* Leaving this in for now to keep compatibility of the API. */ +/* This will disappear. */ +phys_addr_t vcm_get_dev_addr(struct res *res) +{ + if (!res) { + vcm_err("NULL RES"); + return -EINVAL; + } + return res->dev_addr; +} + +static int vcm_no_res(struct vcm *vcm) +{ + if (!vcm) { + vcm_err("NULL vcm\n"); + goto fail; + } + + return list_empty(&vcm->res_head); +fail: + return -EINVAL; +} + +static int vcm_no_assoc(struct vcm *vcm) +{ + if (!vcm) { + vcm_err("NULL vcm\n"); + goto fail; + } + + return list_empty(&vcm->assoc_head); +fail: + return -EINVAL; +} + +static int vcm_all_activated(struct vcm *vcm) +{ + struct avcm *avcm; + + if (!vcm) { + vcm_err("NULL vcm\n"); + goto fail; + } + + list_for_each_entry(avcm, &vcm->assoc_head, assoc_elm) + if (!avcm->is_active) + return 0; + + return 1; +fail: + return -EINVAL; +} + +static void vcm_destroy_common(struct vcm *vcm) +{ + if (!vcm) { + vcm_err("NULL vcm\n"); + return; + } + + memset(vcm, 0, sizeof(*vcm)); + kfree(vcm); +} + +static struct vcm *vcm_create_common(void) +{ + struct vcm *vcm = 0; + + vcm = kzalloc(sizeof(*vcm), GFP_KERNEL); + if (!vcm) { + vcm_err("kzalloc(%i, GFP_KERNEL) ret 0\n", + sizeof(*vcm)); + goto fail; + } + + INIT_LIST_HEAD(&vcm->res_head); + INIT_LIST_HEAD(&vcm->assoc_head); + + return vcm; + +fail: + return NULL; +} + + +static int vcm_create_pool(struct vcm *vcm, unsigned long start_addr, + size_t len) +{ + int ret = 0; + + if (!vcm) { + vcm_err("NULL vcm\n"); + goto fail; + } + + vcm->start_addr = start_addr; + vcm->len = len; + + vcm->pool = gen_pool_create(PAGE_SHIFT, -1); + if (!vcm->pool) { + vcm_err("gen_pool_create(%x, -1) ret 0\n", PAGE_SHIFT); + ret = -EINVAL; + goto fail; + } + + ret = gen_pool_add(vcm->pool, start_addr, len, -1); + if (ret) { + vcm_err("gen_pool_add(%p, %p, %i, -1) ret %i\n", vcm->pool, + (void *) start_addr, len, ret); + goto fail; + } + + vcm->domain = iommu_domain_alloc(); + if (!vcm->domain) { + vcm_err("Could not allocate domain\n"); + ret = -ENOMEM; + goto fail; + } + +fail: + if (ret && vcm->pool) + gen_pool_destroy(vcm->pool); + + return ret; +} + + +static struct vcm *vcm_create_flagged(int flag, unsigned long start_addr, + size_t len) +{ + int ret = 0; + struct vcm *vcm = 0; + + vcm = vcm_create_common(); + if (!vcm) { + vcm_err("NULL vcm\n"); + goto fail; + } + + /* special one-to-one mapping case */ + if ((flag & ONE_TO_ONE_CHK) && + bootmem_cont && + start_addr == (size_t) bootmem_cont && + len == cont_sz) { + vcm->type = VCM_ONE_TO_ONE; + } else { + ret = vcm_create_pool(vcm, start_addr, len); + vcm->type = VCM_DEVICE; + } + + if (ret) { + vcm_err("vcm_create_pool(%p, %p, %i) ret %i\n", vcm, + (void *) start_addr, len, ret); + goto fail2; + } + + return vcm; + +fail2: + vcm_destroy_common(vcm); +fail: + return NULL; +} + +struct vcm *vcm_create(unsigned long start_addr, size_t len) +{ + unsigned long flags; + struct vcm *vcm; + + spin_lock_irqsave(&vcmlock, flags); + vcm = vcm_create_flagged(ONE_TO_ONE_CHK, start_addr, len); + spin_unlock_irqrestore(&vcmlock, flags); + return vcm; +} + + +static int ext_vcm_id_valid(size_t ext_vcm_id) +{ + return ((ext_vcm_id == VCM_PREBUILT_KERNEL) || + (ext_vcm_id == VCM_PREBUILT_USER)); +} + + +struct vcm *vcm_create_from_prebuilt(size_t ext_vcm_id) +{ + unsigned long flags; + struct vcm *vcm = 0; + + spin_lock_irqsave(&vcmlock, flags); + + if (!ext_vcm_id_valid(ext_vcm_id)) { + vcm_err("ext_vcm_id_valid(%i) ret 0\n", ext_vcm_id); + goto fail; + } + + vcm = vcm_create_common(); + if (!vcm) { + vcm_err("NULL vcm\n"); + goto fail; + } + + if (ext_vcm_id == VCM_PREBUILT_KERNEL) + vcm->type = VCM_EXT_KERNEL; + else if (ext_vcm_id == VCM_PREBUILT_USER) + vcm->type = VCM_EXT_USER; + else { + vcm_err("UNREACHABLE ext_vcm_id is illegal\n"); + goto fail_free; + } + + /* TODO: set kernel and userspace start_addr and len, if this + * makes sense */ + + spin_unlock_irqrestore(&vcmlock, flags); + return vcm; + +fail_free: + vcm_destroy_common(vcm); +fail: + spin_unlock_irqrestore(&vcmlock, flags); + return NULL; +} + + +struct vcm *vcm_clone(struct vcm *vcm) +{ + return 0; +} + + +/* No lock needed, vcm->start_addr is never updated after creation */ +size_t vcm_get_start_addr(struct vcm *vcm) +{ + if (!vcm) { + vcm_err("NULL vcm\n"); + return 1; + } + + return vcm->start_addr; +} + + +/* No lock needed, vcm->len is never updated after creation */ +size_t vcm_get_len(struct vcm *vcm) +{ + if (!vcm) { + vcm_err("NULL vcm\n"); + return 0; + } + + return vcm->len; +} + + +static int vcm_free_common_rule(struct vcm *vcm) +{ + int ret; + + if (!vcm) { + vcm_err("NULL vcm\n"); + goto fail; + } + + ret = vcm_no_res(vcm); + if (!ret) { + vcm_err("vcm_no_res(%p) ret 0\n", vcm); + goto fail_busy; + } + + if (ret == -EINVAL) { + vcm_err("vcm_no_res(%p) ret -EINVAL\n", vcm); + goto fail; + } + + ret = vcm_no_assoc(vcm); + if (!ret) { + vcm_err("vcm_no_assoc(%p) ret 0\n", vcm); + goto fail_busy; + } + + if (ret == -EINVAL) { + vcm_err("vcm_no_assoc(%p) ret -EINVAL\n", vcm); + goto fail; + } + + return 0; + +fail_busy: + return -EBUSY; +fail: + return -EINVAL; +} + + +static int vcm_free_pool_rule(struct vcm *vcm) +{ + if (!vcm) { + vcm_err("NULL vcm\n"); + goto fail; + } + + /* A vcm always has a valid pool, don't free the vcm because + what we got is probably invalid. + */ + if (!vcm->pool) { + vcm_err("NULL vcm->pool\n"); + goto fail; + } + + return 0; + +fail: + return -EINVAL; +} + + +static void vcm_free_common(struct vcm *vcm) +{ + memset(vcm, 0, sizeof(*vcm)); + + kfree(vcm); +} + + +static int vcm_free_pool(struct vcm *vcm) +{ + if (!vcm) { + vcm_err("NULL vcm\n"); + goto fail; + } + + gen_pool_destroy(vcm->pool); + + return 0; + +fail: + return -EINVAL; +} + + +static int __vcm_free(struct vcm *vcm) +{ + int ret; + + if (!vcm) { + vcm_err("NULL vcm\n"); + goto fail; + } + + ret = vcm_free_common_rule(vcm); + if (ret != 0) { + vcm_err("vcm_free_common_rule(%p) ret %i\n", vcm, ret); + goto fail; + } + + if (vcm->type == VCM_DEVICE) { + ret = vcm_free_pool_rule(vcm); + if (ret != 0) { + vcm_err("vcm_free_pool_rule(%p) ret %i\n", + (void *) vcm, ret); + goto fail; + } + if (vcm->domain) + iommu_domain_free(vcm->domain); + + vcm->domain = NULL; + ret = vcm_free_pool(vcm); + if (ret != 0) { + vcm_err("vcm_free_pool(%p) ret %i", (void *) vcm, ret); + goto fail; + } + } + + vcm_free_common(vcm); + + return 0; + +fail: + return -EINVAL; +} + +int vcm_free(struct vcm *vcm) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&vcmlock, flags); + ret = __vcm_free(vcm); + spin_unlock_irqrestore(&vcmlock, flags); + + return ret; +} + + +static struct res *__vcm_reserve(struct vcm *vcm, size_t len, u32 attr) +{ + struct res *res = NULL; + int align_attr = 0, i = 0; + + if (!vcm) { + vcm_err("NULL vcm\n"); + goto fail; + } + + if (len == 0) { + vcm_err("len is 0\n"); + goto fail; + } + + res = kzalloc(sizeof(*res), GFP_KERNEL); + if (!res) { + vcm_err("kzalloc(%i, GFP_KERNEL) ret 0", sizeof(*res)); + goto fail; + } + + align_attr = (attr >> VCM_ALIGN_SHIFT) & VCM_ALIGN_MASK; + + if (align_attr >= 32) { + vcm_err("Invalid alignment attribute: %d\n", align_attr); + goto fail2; + } + + INIT_LIST_HEAD(&res->res_elm); + res->vcm = vcm; + res->len = len; + res->attr = attr; + res->alignment_req = smmu_map_sizes[ARRAY_SIZE(smmu_map_sizes) - 1]; + + if (align_attr == 0) { + for (i = 0; i < ARRAY_SIZE(smmu_map_sizes); i++) + if (len / smmu_map_sizes[i]) { + res->alignment_req = smmu_map_sizes[i]; + break; + } + } else + res->alignment_req = 1 << align_attr; + + res->aligned_len = res->alignment_req + len; + + switch (vcm->type) { + case VCM_DEVICE: + /* should always be not zero */ + if (!vcm->pool) { + vcm_err("NULL vcm->pool\n"); + goto fail2; + } + + res->ptr = gen_pool_alloc(vcm->pool, res->aligned_len); + if (!res->ptr) { + vcm_err("gen_pool_alloc(%p, %i) ret 0\n", + vcm->pool, res->aligned_len); + goto fail2; + } + + /* Calculate alignment... this will all change anyway */ + res->dev_addr = res->ptr + + (res->alignment_req - + (res->ptr & (res->alignment_req - 1))); + + break; + case VCM_EXT_KERNEL: + res->vm_area = alloc_vm_area(res->aligned_len); + res->mapped = 0; /* be explicit */ + if (!res->vm_area) { + vcm_err("NULL res->vm_area\n"); + goto fail2; + } + + res->dev_addr = (size_t) res->vm_area->addr + + (res->alignment_req - + ((size_t) res->vm_area->addr & + (res->alignment_req - 1))); + + break; + case VCM_ONE_TO_ONE: + break; + default: + vcm_err("%i is an invalid vcm->type\n", vcm->type); + goto fail2; + } + + list_add_tail(&res->res_elm, &vcm->res_head); + + return res; + +fail2: + kfree(res); +fail: + return 0; +} + + +struct res *vcm_reserve(struct vcm *vcm, size_t len, u32 attr) +{ + unsigned long flags; + struct res *res; + + spin_lock_irqsave(&vcmlock, flags); + res = __vcm_reserve(vcm, len, attr); + spin_unlock_irqrestore(&vcmlock, flags); + + return res; +} + + +struct res *vcm_reserve_at(enum memtarget_t memtarget, struct vcm *vcm, + size_t len, u32 attr) +{ + return 0; +} + + +static int __vcm_unreserve(struct res *res) +{ + struct vcm *vcm; + + if (!res) { + vcm_err("NULL res\n"); + goto fail; + } + + if (!res->vcm) { + vcm_err("NULL res->vcm\n"); + goto fail; + } + + vcm = res->vcm; + if (!vcm) { + vcm_err("NULL vcm\n"); + goto fail; + } + + switch (vcm->type) { + case VCM_DEVICE: + if (!res->vcm->pool) { + vcm_err("NULL (res->vcm))->pool\n"); + goto fail; + } + + /* res->ptr could be zero, this isn't an error */ + gen_pool_free(res->vcm->pool, res->ptr, + res->aligned_len); + break; + case VCM_EXT_KERNEL: + if (res->mapped) { + vcm_err("res->mapped is true\n"); + goto fail; + } + + /* This may take a little explaining. + * In the kernel vunmap will free res->vm_area + * so if we've called it then we shouldn't call + * free_vm_area(). If we've called it we set + * res->vm_area to 0. + */ + if (res->vm_area) { + free_vm_area(res->vm_area); + res->vm_area = 0; + } + + break; + case VCM_ONE_TO_ONE: + break; + default: + vcm_err("%i is an invalid vcm->type\n", vcm->type); + goto fail; + } + + list_del(&res->res_elm); + + /* be extra careful by clearing the memory before freeing it */ + memset(res, 0, sizeof(*res)); + + kfree(res); + + return 0; + +fail: + return -EINVAL; +} + + +int vcm_unreserve(struct res *res) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&vcmlock, flags); + ret = __vcm_unreserve(res); + spin_unlock_irqrestore(&vcmlock, flags); + + return ret; +} + + +/* No lock needed, res->len is never updated after creation */ +size_t vcm_get_res_len(struct res *res) +{ + if (!res) { + vcm_err("res is 0\n"); + return 0; + } + + return res->len; +} + + +int vcm_set_res_attr(struct res *res, u32 attr) +{ + return 0; +} + + +u32 vcm_get_res_attr(struct res *res) +{ + return 0; +} + + +size_t vcm_get_num_res(struct vcm *vcm) +{ + return 0; +} + + +struct res *vcm_get_next_res(struct vcm *vcm, struct res *res) +{ + return 0; +} + + +size_t vcm_res_copy(struct res *to, size_t to_off, struct res *from, size_t + from_off, size_t len) +{ + return 0; +} + + +size_t vcm_get_min_page_size(void) +{ + return PAGE_SIZE; +} + + +static int vcm_to_smmu_attr(u32 attr) +{ + int smmu_attr = 0; + + switch (attr & VCM_CACHE_POLICY) { + case VCM_NOTCACHED: + smmu_attr = VCM_DEV_ATTR_NONCACHED; + break; + case VCM_WB_WA: + smmu_attr = VCM_DEV_ATTR_CACHED_WB_WA; + smmu_attr |= VCM_DEV_ATTR_SH; + break; + case VCM_WB_NWA: + smmu_attr = VCM_DEV_ATTR_CACHED_WB_NWA; + smmu_attr |= VCM_DEV_ATTR_SH; + break; + case VCM_WT: + smmu_attr = VCM_DEV_ATTR_CACHED_WT; + smmu_attr |= VCM_DEV_ATTR_SH; + break; + default: + return -EINVAL; + } + + return smmu_attr; +} + + +static int vcm_process_chunk(struct iommu_domain *domain, phys_addr_t pa, + unsigned long va, size_t len, u32 attr, int map) +{ + int ret, i, map_order; + unsigned long map_len = smmu_map_sizes[ARRAY_SIZE(smmu_map_sizes) - 1]; + + for (i = 0; i < ARRAY_SIZE(smmu_map_sizes); i++) { + if (IS_ALIGNED(va, smmu_map_sizes[i]) && len >= + smmu_map_sizes[i]) { + map_len = smmu_map_sizes[i]; + break; + } + } + +#ifdef VCM_PERF_DEBUG + if (va & (len - 1)) + pr_warning("Warning! Suboptimal VCM mapping alignment " + "va = %p, len = %p. Expect TLB performance " + "degradation.\n", (void *) va, (void *) len); +#endif + + map_order = get_order(map_len); + + while (len) { + if (va & (SZ_4K - 1)) { + vcm_err("Tried to map w/ align < 4k! va = %08lx\n", va); + goto fail; + } + + if (map_len > len) { + vcm_err("map_len = %lu, len = %d, trying to overmap\n", + map_len, len); + goto fail; + } + + if (map) + ret = iommu_map(domain, va, pa, map_order, attr); + else + ret = iommu_unmap(domain, va, map_order); + + if (ret) { + vcm_err("iommu_map/unmap(%p, %p, %p, 0x%x, 0x%x) ret %i" + "map = %d", (void *) domain, (void *) pa, + (void *) va, (int) map_len, attr, ret, map); + goto fail; + } + + va += map_len; + pa += map_len; + len -= map_len; + } + + return 0; +fail: + return -EINVAL; +} + +/* TBD if you vcm_back again what happens? */ +int vcm_back(struct res *res, struct physmem *physmem) +{ + unsigned long flags; + struct vcm *vcm; + struct phys_chunk *chunk; + size_t va = 0; + int ret; + int attr; + + spin_lock_irqsave(&vcmlock, flags); + + if (!res) { + vcm_err("NULL res\n"); + goto fail; + } + + vcm = res->vcm; + if (!vcm) { + vcm_err("NULL vcm\n"); + goto fail; + } + + switch (vcm->type) { + case VCM_DEVICE: + case VCM_EXT_KERNEL: /* hack part 1 */ + attr = vcm_to_smmu_attr(res->attr); + if (attr == -1) { + vcm_err("Bad SMMU attr\n"); + goto fail; + } + break; + default: + attr = 0; + break; + } + + if (!physmem) { + vcm_err("NULL physmem\n"); + goto fail; + } + + if (res->len == 0) { + vcm_err("res->len is 0\n"); + goto fail; + } + + if (physmem->len == 0) { + vcm_err("physmem->len is 0\n"); + goto fail; + } + + if (res->len != physmem->len) { + vcm_err("res->len (%i) != physmem->len (%i)\n", + res->len, physmem->len); + goto fail; + } + + if (physmem->is_cont) { + if (physmem->res == 0) { + vcm_err("cont physmem->res is 0"); + goto fail; + } + } else { + /* fail if no physmem */ + if (list_empty(&physmem->alloc_head.allocated)) { + vcm_err("no allocated phys memory"); + goto fail; + } + } + + ret = vcm_no_assoc(res->vcm); + if (ret == 1) { + vcm_err("can't back un associated VCM\n"); + goto fail; + } + + if (ret == -1) { + vcm_err("vcm_no_assoc() ret -1\n"); + goto fail; + } + + ret = vcm_all_activated(res->vcm); + if (ret == 0) { + vcm_err("can't back, not all associations are activated\n"); + goto fail_eagain; + } + + if (ret == -1) { + vcm_err("vcm_all_activated() ret -1\n"); + goto fail; + } + + va = res->dev_addr; + + list_for_each_entry(chunk, &physmem->alloc_head.allocated, + allocated) { + struct vcm *vcm = res->vcm; + size_t chunk_size = chunk->size; + + if (chunk_size <= 0) { + vcm_err("Bad chunk size: %d\n", chunk_size); + goto fail; + } + + switch (vcm->type) { + case VCM_DEVICE: + { + /* map all */ + ret = vcm_process_chunk(vcm->domain, chunk->pa, + va, chunk_size, attr, 1); + if (ret != 0) { + vcm_err("vcm_process_chunk(%p, %p, %p," + " 0x%x, 0x%x)" + " ret %i", + vcm->domain, + (void *) chunk->pa, + (void *) va, + (int) chunk_size, attr, ret); + goto fail; + } + break; + } + + case VCM_EXT_KERNEL: + { + unsigned int pages_in_chunk = chunk_size / PAGE_SIZE; + unsigned long loc_va = va; + unsigned long loc_pa = chunk->pa; + + const struct mem_type *mtype; + + /* TODO: get this based on MEMTYPE */ + mtype = get_mem_type(MT_DEVICE); + if (!mtype) { + vcm_err("mtype is 0\n"); + goto fail; + } + + /* TODO: Map with the same chunk size */ + while (pages_in_chunk--) { + ret = ioremap_page(loc_va, + loc_pa, + mtype); + if (ret != 0) { + vcm_err("ioremap_page(%p, %p, %p) ret" + " %i", (void *) loc_va, + (void *) loc_pa, + (void *) mtype, ret); + goto fail; + /* TODO handle weird + inter-map case */ + } + + /* hack part 2 */ + /* we're changing the PT entry behind + * linux's back + */ + ret = cpu_set_attr(loc_va, PAGE_SIZE, attr); + if (ret != 0) { + vcm_err("cpu_set_attr(%p, %lu, %x)" + "ret %i\n", + (void *) loc_va, PAGE_SIZE, + attr, ret); + goto fail; + /* TODO handle weird + inter-map case */ + } + + res->mapped = 1; + + loc_va += PAGE_SIZE; + loc_pa += PAGE_SIZE; + } + + flush_cache_vmap(va, loc_va); + break; + } + case VCM_ONE_TO_ONE: + va = chunk->pa; + break; + default: + /* this should never happen */ + goto fail; + } + + va += chunk_size; + /* also add res to the allocated chunk list of refs */ + } + + /* note the reservation */ + res->physmem = physmem; + + spin_unlock_irqrestore(&vcmlock, flags); + return 0; +fail_eagain: + spin_unlock_irqrestore(&vcmlock, flags); + return -EAGAIN; +fail: + spin_unlock_irqrestore(&vcmlock, flags); + return -EINVAL; +} + + +int vcm_unback(struct res *res) +{ + unsigned long flags; + struct vcm *vcm; + struct physmem *physmem; + int ret; + + spin_lock_irqsave(&vcmlock, flags); + + if (!res) + goto fail; + + vcm = res->vcm; + if (!vcm) { + vcm_err("NULL vcm\n"); + goto fail; + } + + if (!res->physmem) { + vcm_err("can't unback a non-backed reservation\n"); + goto fail; + } + + physmem = res->physmem; + if (!physmem) { + vcm_err("physmem is NULL\n"); + goto fail; + } + + if (list_empty(&physmem->alloc_head.allocated)) { + vcm_err("physmem allocation is empty\n"); + goto fail; + } + + ret = vcm_no_assoc(res->vcm); + if (ret == 1) { + vcm_err("can't unback a unassociated reservation\n"); + goto fail; + } + + if (ret == -1) { + vcm_err("vcm_no_assoc(%p) ret -1\n", (void *) res->vcm); + goto fail; + } + + ret = vcm_all_activated(res->vcm); + if (ret == 0) { + vcm_err("can't unback, not all associations are active\n"); + goto fail_eagain; + } + + if (ret == -1) { + vcm_err("vcm_all_activated(%p) ret -1\n", (void *) res->vcm); + goto fail; + } + + + switch (vcm->type) { + case VCM_EXT_KERNEL: + if (!res->mapped) { + vcm_err("can't unback an unmapped VCM_EXT_KERNEL" + " VCM\n"); + goto fail; + } + + /* vunmap free's vm_area */ + vunmap(res->vm_area->addr); + res->vm_area = 0; + + res->mapped = 0; + break; + + case VCM_DEVICE: + { + struct phys_chunk *chunk; + size_t va = res->dev_addr; + + list_for_each_entry(chunk, &physmem->alloc_head.allocated, + allocated) { + struct vcm *vcm = res->vcm; + size_t chunk_size = chunk->size; + + ret = vcm_process_chunk(vcm->domain, 0, va, + chunk_size, 0, 0); + if (ret != 0) { + vcm_err("vcm_unback_chunk(%p, %p, 0x%x)" + " ret %i", + (void *) vcm->domain, + (void *) va, + (int) chunk_size, ret); + goto fail; + /* TODO handle weird inter-unmap state*/ + } + + va += chunk_size; + /* may to a light unback, depending on the requested + * functionality + */ + } + break; + } + + case VCM_ONE_TO_ONE: + break; + default: + /* this should never happen */ + goto fail; + } + + /* clear the reservation */ + res->physmem = 0; + + spin_unlock_irqrestore(&vcmlock, flags); + return 0; +fail_eagain: + spin_unlock_irqrestore(&vcmlock, flags); + return -EAGAIN; +fail: + spin_unlock_irqrestore(&vcmlock, flags); + return -EINVAL; +} + + +enum memtarget_t vcm_get_memtype_of_res(struct res *res) +{ + return VCM_INVALID; +} + +static int vcm_free_max_munch_cont(struct phys_chunk *head) +{ + struct phys_chunk *chunk, *tmp; + + if (!head) + return -EINVAL; + + list_for_each_entry_safe(chunk, tmp, &head->allocated, + allocated) { + list_del_init(&chunk->allocated); + } + + return 0; +} + +static int vcm_alloc_max_munch_cont(size_t start_addr, size_t len, + struct phys_chunk *head) +{ + /* this function should always succeed, since it + parallels a VCM */ + + int i, j; + + if (!head) { + vcm_err("head is NULL in continuous map.\n"); + goto fail; + } + + if (start_addr < (int) bootmem_cont) { + vcm_err("phys start addr (%p) < base (%p)\n", + (void *) start_addr, (void *) bootmem_cont); + goto fail; + } + + if ((start_addr + len) >= ((size_t) bootmem_cont + cont_sz)) { + vcm_err("requested region (%p + %i) > " + " available region (%p + %i)", + (void *) start_addr, (int) len, + (void *) bootmem_cont, cont_sz); + goto fail; + } + + i = (start_addr - (size_t) bootmem_cont)/SZ_4K; + + for (j = 0; j < ARRAY_SIZE(smmu_map_sizes); ++j) { + while (len/smmu_map_sizes[j]) { + if (!list_empty(&cont_phys_chunk[i].allocated)) { + vcm_err("chunk %i ( addr %p) already mapped\n", + i, (void *) (start_addr + + (i*smmu_map_sizes[j]))); + goto fail_free; + } + list_add_tail(&cont_phys_chunk[i].allocated, + &head->allocated); + cont_phys_chunk[i].size = smmu_map_sizes[j]; + + len -= smmu_map_sizes[j]; + i += smmu_map_sizes[j]/SZ_4K; + } + } + + if (len % SZ_4K) { + if (!list_empty(&cont_phys_chunk[i].allocated)) { + vcm_err("chunk %i (addr %p) already mapped\n", + i, (void *) (start_addr + (i*SZ_4K))); + goto fail_free; + } + len -= SZ_4K; + list_add_tail(&cont_phys_chunk[i].allocated, + &head->allocated); + + i++; + } + + return i; + +fail_free: + { + struct phys_chunk *chunk, *tmp; + /* just remove from list, if we're double alloc'ing + we don't want to stamp on the other guy */ + list_for_each_entry_safe(chunk, tmp, &head->allocated, + allocated) { + list_del(&chunk->allocated); + } + } +fail: + return 0; +} + +struct physmem *vcm_phys_alloc(enum memtype_t memtype, size_t len, u32 attr) +{ + unsigned long flags; + int ret; + struct physmem *physmem = NULL; + int blocks_allocated; + + spin_lock_irqsave(&vcmlock, flags); + + physmem = kzalloc(sizeof(*physmem), GFP_KERNEL); + if (!physmem) { + vcm_err("physmem is NULL\n"); + goto fail; + } + + physmem->memtype = memtype; + physmem->len = len; + physmem->attr = attr; + + INIT_LIST_HEAD(&physmem->alloc_head.allocated); + + if (attr & VCM_PHYS_CONT) { + if (!cont_vcm_id) { + vcm_err("cont_vcm_id is NULL\n"); + goto fail2; + } + + physmem->is_cont = 1; + + /* TODO: get attributes */ + physmem->res = __vcm_reserve(cont_vcm_id, len, 0); + if (physmem->res == 0) { + vcm_err("contiguous space allocation failed\n"); + goto fail2; + } + + /* if we're here we know we have memory, create + the shadow physmem links*/ + blocks_allocated = + vcm_alloc_max_munch_cont( + physmem->res->dev_addr, + len, + &physmem->alloc_head); + + if (blocks_allocated == 0) { + vcm_err("shadow physmem allocation failed\n"); + goto fail3; + } + } else { + blocks_allocated = vcm_alloc_max_munch(len, memtype, + &physmem->alloc_head); + if (blocks_allocated == 0) { + vcm_err("physical allocation failed:" + " vcm_alloc_max_munch(%i, %p) ret 0\n", + len, &physmem->alloc_head); + goto fail2; + } + } + + spin_unlock_irqrestore(&vcmlock, flags); + return physmem; + +fail3: + ret = __vcm_unreserve(physmem->res); + if (ret != 0) { + vcm_err("vcm_unreserve(%p) ret %i during cleanup", + (void *) physmem->res, ret); + spin_unlock_irqrestore(&vcmlock, flags); + return 0; + } +fail2: + kfree(physmem); +fail: + spin_unlock_irqrestore(&vcmlock, flags); + return 0; +} + + +int vcm_phys_free(struct physmem *physmem) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&vcmlock, flags); + + if (!physmem) { + vcm_err("physmem is NULL\n"); + goto fail; + } + + if (physmem->is_cont) { + if (physmem->res == 0) { + vcm_err("contiguous reservation is NULL\n"); + goto fail; + } + + ret = vcm_free_max_munch_cont(&physmem->alloc_head); + if (ret != 0) { + vcm_err("failed to free physical blocks:" + " vcm_free_max_munch_cont(%p) ret %i\n", + (void *) &physmem->alloc_head, ret); + goto fail; + } + + ret = __vcm_unreserve(physmem->res); + if (ret != 0) { + vcm_err("failed to free virtual blocks:" + " vcm_unreserve(%p) ret %i\n", + (void *) physmem->res, ret); + goto fail; + } + + } else { + + ret = vcm_alloc_free_blocks(physmem->memtype, + &physmem->alloc_head); + if (ret != 0) { + vcm_err("failed to free physical blocks:" + " vcm_alloc_free_blocks(%p) ret %i\n", + (void *) &physmem->alloc_head, ret); + goto fail; + } + } + + memset(physmem, 0, sizeof(*physmem)); + + kfree(physmem); + + spin_unlock_irqrestore(&vcmlock, flags); + return 0; + +fail: + spin_unlock_irqrestore(&vcmlock, flags); + return -EINVAL; +} + + +struct avcm *vcm_assoc(struct vcm *vcm, struct device *dev, u32 attr) +{ + unsigned long flags; + struct avcm *avcm = NULL; + + spin_lock_irqsave(&vcmlock, flags); + + if (!vcm) { + vcm_err("vcm is NULL\n"); + goto fail; + } + + if (!dev) { + vcm_err("dev_id is NULL\n"); + goto fail; + } + + if (vcm->type == VCM_EXT_KERNEL && !list_empty(&vcm->assoc_head)) { + vcm_err("only one device may be assocoated with a" + " VCM_EXT_KERNEL\n"); + goto fail; + } + + avcm = kzalloc(sizeof(*avcm), GFP_KERNEL); + if (!avcm) { + vcm_err("kzalloc(%i, GFP_KERNEL) ret NULL\n", sizeof(*avcm)); + goto fail; + } + + avcm->dev = dev; + + avcm->vcm = vcm; + avcm->attr = attr; + avcm->is_active = 0; + + INIT_LIST_HEAD(&avcm->assoc_elm); + list_add(&avcm->assoc_elm, &vcm->assoc_head); + + spin_unlock_irqrestore(&vcmlock, flags); + return avcm; + +fail: + spin_unlock_irqrestore(&vcmlock, flags); + return 0; +} + + +int vcm_deassoc(struct avcm *avcm) +{ + unsigned long flags; + + spin_lock_irqsave(&vcmlock, flags); + + if (!avcm) { + vcm_err("avcm is NULL\n"); + goto fail; + } + + if (list_empty(&avcm->assoc_elm)) { + vcm_err("nothing to deassociate\n"); + goto fail; + } + + if (avcm->is_active) { + vcm_err("association still activated\n"); + goto fail_busy; + } + + list_del(&avcm->assoc_elm); + + memset(avcm, 0, sizeof(*avcm)); + + kfree(avcm); + spin_unlock_irqrestore(&vcmlock, flags); + return 0; +fail_busy: + spin_unlock_irqrestore(&vcmlock, flags); + return -EBUSY; +fail: + spin_unlock_irqrestore(&vcmlock, flags); + return -EINVAL; +} + + +int vcm_set_assoc_attr(struct avcm *avcm, u32 attr) +{ + return 0; +} + + +u32 vcm_get_assoc_attr(struct avcm *avcm) +{ + return 0; +} + + +int vcm_activate(struct avcm *avcm) +{ + unsigned long flags; + struct vcm *vcm; + + spin_lock_irqsave(&vcmlock, flags); + + if (!avcm) { + vcm_err("avcm is NULL\n"); + goto fail; + } + + vcm = avcm->vcm; + if (!vcm) { + vcm_err("NULL vcm\n"); + goto fail; + } + + if (!avcm->dev) { + vcm_err("cannot activate without a device\n"); + goto fail_nodev; + } + + if (avcm->is_active) { + vcm_err("double activate\n"); + goto fail_busy; + } + + if (vcm->type == VCM_DEVICE) { +#ifdef CONFIG_SMMU + int ret; + ret = iommu_attach_device(vcm->domain, avcm->dev); + if (ret != 0) { + dev_err(avcm->dev, "failed to attach to domain\n"); + goto fail_dev; + } +#else + vcm_err("No SMMU support - cannot activate/deactivate\n"); + goto fail_nodev; +#endif + } + + avcm->is_active = 1; + spin_unlock_irqrestore(&vcmlock, flags); + return 0; + +#ifdef CONFIG_SMMU +fail_dev: + spin_unlock_irqrestore(&vcmlock, flags); + return -ENODEV; +#endif +fail_busy: + spin_unlock_irqrestore(&vcmlock, flags); + return -EBUSY; +fail_nodev: + spin_unlock_irqrestore(&vcmlock, flags); + return -ENODEV; +fail: + spin_unlock_irqrestore(&vcmlock, flags); + return -EINVAL; +} + + +int vcm_deactivate(struct avcm *avcm) +{ + unsigned long flags; + struct vcm *vcm; + + spin_lock_irqsave(&vcmlock, flags); + + if (!avcm) + goto fail; + + vcm = avcm->vcm; + if (!vcm) { + vcm_err("NULL vcm\n"); + goto fail; + } + + if (!avcm->dev) { + vcm_err("cannot deactivate without a device\n"); + goto fail; + } + + if (!avcm->is_active) { + vcm_err("double deactivate\n"); + goto fail_nobusy; + } + + if (vcm->type == VCM_DEVICE) { +#ifdef CONFIG_SMMU + /* TODO, pmem check */ + iommu_detach_device(vcm->domain, avcm->dev); +#else + vcm_err("No SMMU support - cannot activate/deactivate\n"); + goto fail; +#endif + } + + avcm->is_active = 0; + spin_unlock_irqrestore(&vcmlock, flags); + return 0; +fail_nobusy: + spin_unlock_irqrestore(&vcmlock, flags); + return -ENOENT; +fail: + spin_unlock_irqrestore(&vcmlock, flags); + return -EINVAL; +} + +struct bound *vcm_create_bound(struct vcm *vcm, size_t len) +{ + return 0; +} + + +int vcm_free_bound(struct bound *bound) +{ + return -EINVAL; +} + + +struct res *vcm_reserve_from_bound(struct bound *bound, size_t len, + u32 attr) +{ + return 0; +} + + +size_t vcm_get_bound_start_addr(struct bound *bound) +{ + return 0; +} + + +size_t vcm_get_bound_len(struct bound *bound) +{ + return 0; +} + + +struct physmem *vcm_map_phys_addr(phys_addr_t phys, size_t len) +{ + return 0; +} + + +size_t vcm_get_next_phys_addr(struct physmem *physmem, phys_addr_t phys, + size_t *len) +{ + return 0; +} + + +struct res *vcm_get_res(unsigned long dev_addr, struct vcm *vcm) +{ + return 0; +} + + +size_t vcm_translate(struct device *src_dev, struct vcm *src_vcm, + struct vcm *dst_vcm) +{ + return 0; +} + + +size_t vcm_get_phys_num_res(phys_addr_t phys) +{ + return 0; +} + + +struct res *vcm_get_next_phys_res(phys_addr_t phys, struct res *res, + size_t *len) +{ + return 0; +} + + +phys_addr_t vcm_get_pgtbl_pa(struct vcm *vcm) +{ + return 0; +} + + +/* No lock needed, smmu_translate has its own lock */ +phys_addr_t vcm_dev_addr_to_phys_addr(struct vcm *vcm, unsigned long dev_addr) +{ + if (!vcm) + return -EINVAL; +#ifdef CONFIG_SMMU + return iommu_iova_to_phys(vcm->domain, dev_addr); +#else + vcm_err("No support for SMMU - manual translation not supported\n"); + return -ENODEV; +#endif +} + + +/* No lock needed, bootmem_cont never changes after */ +phys_addr_t vcm_get_cont_memtype_pa(enum memtype_t memtype) +{ + if (memtype != VCM_MEMTYPE_0) { + vcm_err("memtype != VCM_MEMTYPE_0\n"); + goto fail; + } + + if (!bootmem_cont) { + vcm_err("bootmem_cont 0\n"); + goto fail; + } + + return (size_t) bootmem_cont; +fail: + return 0; +} + + +/* No lock needed, constant */ +size_t vcm_get_cont_memtype_len(enum memtype_t memtype) +{ + if (memtype != VCM_MEMTYPE_0) { + vcm_err("memtype != VCM_MEMTYPE_0\n"); + return 0; + } + + return cont_sz; +} + +int vcm_hook(struct device *dev, vcm_handler handler, void *data) +{ +#ifdef CONFIG_SMMU + vcm_err("No interrupts in IOMMU API\n"); + return -ENODEV; +#else + vcm_err("No support for SMMU - interrupts not supported\n"); + return -ENODEV; +#endif +} + + +size_t vcm_hw_ver(size_t dev) +{ + return 0; +} + + +static int vcm_cont_phys_chunk_init(void) +{ + int i; + int cont_pa; + + if (!cont_phys_chunk) { + vcm_err("cont_phys_chunk 0\n"); + goto fail; + } + + if (!bootmem_cont) { + vcm_err("bootmem_cont 0\n"); + goto fail; + } + + cont_pa = (size_t) bootmem_cont; + + for (i = 0; i < cont_sz/PAGE_SIZE; ++i) { + cont_phys_chunk[i].pa = cont_pa; cont_pa += PAGE_SIZE; + cont_phys_chunk[i].size = SZ_4K; + /* Not part of an allocator-managed pool */ + cont_phys_chunk[i].pool_idx = -1; + INIT_LIST_HEAD(&cont_phys_chunk[i].allocated); + } + + return 0; + +fail: + return -EINVAL; +} + +int vcm_sys_init(struct physmem_region *mem, int n_regions, + struct vcm_memtype_map *mt_map, int n_mt, + void *cont_pa, unsigned int cont_len) +{ + int ret; + printk(KERN_INFO "VCM Initialization\n"); + bootmem_cont = cont_pa; + cont_sz = cont_len; + + if (!bootmem_cont) { + vcm_err("bootmem_cont is 0\n"); + ret = -1; + goto fail; + } + + ret = vcm_setup_tex_classes(); + if (ret != 0) { + printk(KERN_INFO "Could not determine TEX attribute mapping\n"); + ret = -1; + goto fail; + } + + + ret = vcm_alloc_init(mem, n_regions, mt_map, n_mt); + + if (ret != 0) { + vcm_err("vcm_alloc_init() ret %i\n", ret); + ret = -1; + goto fail; + } + + cont_phys_chunk = kzalloc(sizeof(*cont_phys_chunk)*(cont_sz/PAGE_SIZE), + GFP_KERNEL); + if (!cont_phys_chunk) { + vcm_err("kzalloc(%lu, GFP_KERNEL) ret 0", + sizeof(*cont_phys_chunk)*(cont_sz/PAGE_SIZE)); + goto fail_free; + } + + /* the address and size will hit our special case unless we + pass an override */ + cont_vcm_id = vcm_create_flagged(0, (size_t)bootmem_cont, cont_sz); + if (cont_vcm_id == 0) { + vcm_err("vcm_create_flagged(0, %p, %i) ret 0\n", + bootmem_cont, cont_sz); + ret = -1; + goto fail_free2; + } + + ret = vcm_cont_phys_chunk_init(); + if (ret != 0) { + vcm_err("vcm_cont_phys_chunk_init() ret %i\n", ret); + goto fail_free3; + } + + printk(KERN_INFO "VCM Initialization OK\n"); + return 0; + +fail_free3: + ret = __vcm_free(cont_vcm_id); + if (ret != 0) { + vcm_err("vcm_free(%p) ret %i during failure path\n", + (void *) cont_vcm_id, ret); + return ret; + } + +fail_free2: + kfree(cont_phys_chunk); + cont_phys_chunk = 0; + +fail_free: + ret = vcm_alloc_destroy(); + if (ret != 0) + vcm_err("vcm_alloc_destroy() ret %i during failure path\n", + ret); + + ret = -EINVAL; +fail: + return ret; +} + + +int vcm_sys_destroy(void) +{ + int ret = 0; + + if (!cont_phys_chunk) { + vcm_err("cont_phys_chunk is 0\n"); + return -ENODEV; + } + + if (!cont_vcm_id) { + vcm_err("cont_vcm_id is 0\n"); + return -ENODEV; + } + + ret = __vcm_free(cont_vcm_id); + if (ret != 0) { + vcm_err("vcm_free(%p) ret %i\n", (void *) cont_vcm_id, ret); + return -ENODEV; + } + + cont_vcm_id = 0; + + kfree(cont_phys_chunk); + cont_phys_chunk = 0; + + ret = vcm_alloc_destroy(); + if (ret != 0) { + vcm_err("vcm_alloc_destroy() ret %i\n", ret); + return ret; + } + + return ret; +} + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Zach Pfeffer "); diff --git a/arch/arm/mm/vcm_alloc.c b/arch/arm/mm/vcm_alloc.c new file mode 100644 index 0000000000000..5f3c024757d1a --- /dev/null +++ b/arch/arm/mm/vcm_alloc.c @@ -0,0 +1,557 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +int basicalloc_init; + +#define vcm_alloc_err(a, ...) \ + pr_err("ERROR %s %i " a, __func__, __LINE__, ##__VA_ARGS__) + +struct phys_chunk_head { + struct list_head head; + int num; +}; + +struct phys_pool { + int size; + int chunk_size; + struct phys_chunk_head head; +}; + +static int vcm_num_phys_pools; +static int vcm_num_memtypes; +static struct phys_pool *vcm_phys_pool; +static struct vcm_memtype_map *memtype_map; + +static int num_pools(enum memtype_t memtype) +{ + if (memtype >= vcm_num_memtypes) { + vcm_alloc_err("Bad memtype: %d\n", memtype); + return -EINVAL; + } + return memtype_map[memtype].num_pools; +} + +static int pool_chunk_size(enum memtype_t memtype, int prio_idx) +{ + int pool_idx; + if (memtype >= vcm_num_memtypes) { + vcm_alloc_err("Bad memtype: %d\n", memtype); + return -EINVAL; + } + + if (prio_idx >= num_pools(memtype)) { + vcm_alloc_err("Bad prio index: %d, max=%d, mt=%d\n", prio_idx, + num_pools(memtype), memtype); + return -EINVAL; + } + + pool_idx = memtype_map[memtype].pool_id[prio_idx]; + return vcm_phys_pool[pool_idx].chunk_size; +} + +int vcm_alloc_pool_idx_to_size(int pool_idx) +{ + if (pool_idx >= vcm_num_phys_pools) { + vcm_alloc_err("Bad pool index: %d\n, max=%d\n", pool_idx, + vcm_num_phys_pools); + return -EINVAL; + } + return vcm_phys_pool[pool_idx].chunk_size; +} + +static struct phys_chunk_head *get_chunk_list(enum memtype_t memtype, + int prio_idx) +{ + unsigned int pool_idx; + + if (memtype >= vcm_num_memtypes) { + vcm_alloc_err("Bad memtype: %d\n", memtype); + return NULL; + } + + if (prio_idx >= num_pools(memtype)) { + vcm_alloc_err("bad chunk size: mt=%d, prioidx=%d, np=%d\n", + memtype, prio_idx, num_pools(memtype)); + BUG(); + return NULL; + } + + if (!vcm_phys_pool) { + vcm_alloc_err("phys_pool is null\n"); + return NULL; + } + + /* We don't have a "pool count" anywhere but this is coming + * strictly from data in a board file + */ + pool_idx = memtype_map[memtype].pool_id[prio_idx]; + + return &vcm_phys_pool[pool_idx].head; +} + +static int is_allocated(struct list_head *allocated) +{ + /* This should not happen under normal conditions */ + if (!allocated) { + vcm_alloc_err("no allocated\n"); + return 0; + } + + if (!basicalloc_init) { + vcm_alloc_err("no basicalloc_init\n"); + return 0; + } + return !list_empty(allocated); +} + +static int count_allocated_size(enum memtype_t memtype, int idx) +{ + int cnt = 0; + struct phys_chunk *chunk, *tmp; + struct phys_chunk_head *pch; + + if (!basicalloc_init) { + vcm_alloc_err("no basicalloc_init\n"); + return 0; + } + + pch = get_chunk_list(memtype, idx); + if (!pch) { + vcm_alloc_err("null pch\n"); + return -EINVAL; + } + + list_for_each_entry_safe(chunk, tmp, &pch->head, list) { + if (is_allocated(&chunk->allocated)) + cnt++; + } + + return cnt; +} + + +int vcm_alloc_get_mem_size(void) +{ + if (!vcm_phys_pool) { + vcm_alloc_err("No physical pool set up!\n"); + return -ENODEV; + } + return vcm_phys_pool[0].size; +} +EXPORT_SYMBOL(vcm_alloc_get_mem_size); + +void vcm_alloc_print_list(enum memtype_t memtype, int just_allocated) +{ + int i; + struct phys_chunk *chunk, *tmp; + struct phys_chunk_head *pch; + + if (!basicalloc_init) { + vcm_alloc_err("no basicalloc_init\n"); + return; + } + + for (i = 0; i < num_pools(memtype); ++i) { + pch = get_chunk_list(memtype, i); + + if (!pch) { + vcm_alloc_err("pch is null\n"); + return; + } + + if (list_empty(&pch->head)) + continue; + + list_for_each_entry_safe(chunk, tmp, &pch->head, list) { + if (just_allocated && !is_allocated(&chunk->allocated)) + continue; + + printk(KERN_INFO "pa = %#x, size = %#x\n", + chunk->pa, vcm_phys_pool[chunk->pool_idx].chunk_size); + } + } +} +EXPORT_SYMBOL(vcm_alloc_print_list); + +int vcm_alloc_blocks_avail(enum memtype_t memtype, int idx) +{ + struct phys_chunk_head *pch; + if (!basicalloc_init) { + vcm_alloc_err("no basicalloc_init\n"); + return 0; + } + pch = get_chunk_list(memtype, idx); + + if (!pch) { + vcm_alloc_err("pch is null\n"); + return 0; + } + return pch->num; +} +EXPORT_SYMBOL(vcm_alloc_blocks_avail); + + +int vcm_alloc_get_num_chunks(enum memtype_t memtype) +{ + return num_pools(memtype); +} +EXPORT_SYMBOL(vcm_alloc_get_num_chunks); + + +int vcm_alloc_all_blocks_avail(enum memtarget_t memtype) +{ + int i; + int cnt = 0; + + if (!basicalloc_init) { + vcm_alloc_err("no basicalloc_init\n"); + return 0; + } + + for (i = 0; i < num_pools(memtype); ++i) + cnt += vcm_alloc_blocks_avail(memtype, i); + return cnt; +} +EXPORT_SYMBOL(vcm_alloc_all_blocks_avail); + + +int vcm_alloc_count_allocated(enum memtype_t memtype) +{ + int i; + int cnt = 0; + + if (!basicalloc_init) { + vcm_alloc_err("no basicalloc_init\n"); + return 0; + } + + for (i = 0; i < num_pools(memtype); ++i) + cnt += count_allocated_size(memtype, i); + return cnt; +} +EXPORT_SYMBOL(vcm_alloc_count_allocated); + +int vcm_alloc_destroy(void) +{ + int i, mt; + struct phys_chunk *chunk, *tmp; + + if (!basicalloc_init) { + vcm_alloc_err("no basicalloc_init\n"); + return -ENODEV; + } + + /* can't destroy a space that has allocations */ + for (mt = 0; mt < vcm_num_memtypes; mt++) + if (vcm_alloc_count_allocated(mt)) { + vcm_alloc_err("allocations still present\n"); + return -EBUSY; + } + + for (i = 0; i < vcm_num_phys_pools; i++) { + struct phys_chunk_head *pch = &vcm_phys_pool[i].head; + + if (list_empty(&pch->head)) + continue; + list_for_each_entry_safe(chunk, tmp, &pch->head, list) { + list_del(&chunk->list); + memset(chunk, 0, sizeof(*chunk)); + kfree(chunk); + } + vcm_phys_pool[i].head.num = 0; + } + + kfree(vcm_phys_pool); + kfree(memtype_map); + + vcm_phys_pool = NULL; + memtype_map = NULL; + basicalloc_init = 0; + vcm_num_phys_pools = 0; + return 0; +} +EXPORT_SYMBOL(vcm_alloc_destroy); + + +int vcm_alloc_init(struct physmem_region *mem, int n_regions, + struct vcm_memtype_map *mt_map, int n_mt) +{ + int i = 0, j = 0, r = 0, num_chunks; + struct phys_chunk *chunk; + struct phys_chunk_head *pch = NULL; + unsigned long pa; + + /* no double inits */ + if (basicalloc_init) { + vcm_alloc_err("double basicalloc_init\n"); + BUG(); + goto fail; + } + memtype_map = kzalloc(sizeof(*mt_map) * n_mt, GFP_KERNEL); + if (!memtype_map) { + vcm_alloc_err("Could not copy memtype map\n"); + goto fail; + } + memcpy(memtype_map, mt_map, sizeof(*mt_map) * n_mt); + + vcm_phys_pool = kzalloc(sizeof(*vcm_phys_pool) * n_regions, GFP_KERNEL); + vcm_num_phys_pools = n_regions; + vcm_num_memtypes = n_mt; + + if (!vcm_phys_pool) { + vcm_alloc_err("Could not allocate physical pool structure\n"); + goto fail; + } + + /* separate out to ensure good cleanup */ + for (i = 0; i < n_regions; i++) { + pch = &vcm_phys_pool[i].head; + INIT_LIST_HEAD(&pch->head); + pch->num = 0; + } + + for (r = 0; r < n_regions; r++) { + pa = mem[r].addr; + vcm_phys_pool[r].size = mem[r].size; + vcm_phys_pool[r].chunk_size = mem[r].chunk_size; + pch = &vcm_phys_pool[r].head; + + num_chunks = mem[r].size / mem[r].chunk_size; + + printk(KERN_INFO "VCM Init: region %d, chunk size=%d, " + "num=%d, pa=%p\n", r, mem[r].chunk_size, num_chunks, + (void *)pa); + + for (j = 0; j < num_chunks; ++j) { + chunk = kzalloc(sizeof(*chunk), GFP_KERNEL); + if (!chunk) { + vcm_alloc_err("null chunk\n"); + goto fail; + } + chunk->pa = pa; + chunk->size = mem[r].chunk_size; + pa += mem[r].chunk_size; + chunk->pool_idx = r; + INIT_LIST_HEAD(&chunk->allocated); + list_add_tail(&chunk->list, &pch->head); + pch->num++; + } + } + + basicalloc_init = 1; + return 0; +fail: + vcm_alloc_destroy(); + return -EINVAL; +} +EXPORT_SYMBOL(vcm_alloc_init); + + +int vcm_alloc_free_blocks(enum memtype_t memtype, struct phys_chunk *alloc_head) +{ + struct phys_chunk *chunk, *tmp; + struct phys_chunk_head *pch = NULL; + + if (!basicalloc_init) { + vcm_alloc_err("no basicalloc_init\n"); + goto fail; + } + + if (!alloc_head) { + vcm_alloc_err("no alloc_head\n"); + goto fail; + } + + list_for_each_entry_safe(chunk, tmp, &alloc_head->allocated, + allocated) { + list_del_init(&chunk->allocated); + pch = &vcm_phys_pool[chunk->pool_idx].head; + + if (!pch) { + vcm_alloc_err("null pch\n"); + goto fail; + } + pch->num++; + } + + return 0; +fail: + return -ENODEV; +} +EXPORT_SYMBOL(vcm_alloc_free_blocks); + + +int vcm_alloc_num_blocks(int num, enum memtype_t memtype, int idx, + struct phys_chunk *alloc_head) +{ + struct phys_chunk *chunk; + struct phys_chunk_head *pch = NULL; + int num_allocated = 0; + + if (!basicalloc_init) { + vcm_alloc_err("no basicalloc_init\n"); + goto fail; + } + + if (!alloc_head) { + vcm_alloc_err("no alloc_head\n"); + goto fail; + } + + pch = get_chunk_list(memtype, idx); + + if (!pch) { + vcm_alloc_err("null pch\n"); + goto fail; + } + if (list_empty(&pch->head)) { + vcm_alloc_err("list is empty\n"); + goto fail; + } + + if (vcm_alloc_blocks_avail(memtype, idx) < num) { + vcm_alloc_err("not enough blocks? num=%d\n", num); + goto fail; + } + + list_for_each_entry(chunk, &pch->head, list) { + if (num_allocated == num) + break; + if (is_allocated(&chunk->allocated)) + continue; + + list_add_tail(&chunk->allocated, &alloc_head->allocated); + pch->num--; + num_allocated++; + } + return num_allocated; +fail: + return 0; +} +EXPORT_SYMBOL(vcm_alloc_num_blocks); + + +int vcm_alloc_max_munch(int len, enum memtype_t memtype, + struct phys_chunk *alloc_head) +{ + int i; + + int blocks_req = 0; + int block_residual = 0; + int blocks_allocated = 0; + int cur_chunk_size = 0; + int ba = 0; + + if (!basicalloc_init) { + vcm_alloc_err("basicalloc_init is 0\n"); + goto fail; + } + + if (!alloc_head) { + vcm_alloc_err("alloc_head is NULL\n"); + goto fail; + } + + if (num_pools(memtype) <= 0) { + vcm_alloc_err("Memtype %d has improper mempool configuration\n", + memtype); + goto fail; + } + + for (i = 0; i < num_pools(memtype); ++i) { + cur_chunk_size = pool_chunk_size(memtype, i); + if (cur_chunk_size <= 0) { + vcm_alloc_err("Bad chunk size: %d\n", cur_chunk_size); + goto fail; + } + + blocks_req = len / cur_chunk_size; + block_residual = len % cur_chunk_size; + + len = block_residual; /* len left */ + if (blocks_req) { + int blocks_available = 0; + int blocks_diff = 0; + int bytes_diff = 0; + + blocks_available = vcm_alloc_blocks_avail(memtype, i); + if (blocks_available < blocks_req) { + blocks_diff = + (blocks_req - blocks_available); + bytes_diff = + blocks_diff * cur_chunk_size; + + /* add back in the rest */ + len += bytes_diff; + } else { + /* got all the blocks I need */ + blocks_available = + (blocks_available > blocks_req) + ? blocks_req : blocks_available; + } + + ba = vcm_alloc_num_blocks(blocks_available, memtype, i, + alloc_head); + + if (ba != blocks_available) { + vcm_alloc_err("blocks allocated (%i) !=" + " blocks_available (%i):" + " chunk size = %#x," + " alloc_head = %p\n", + ba, blocks_available, + i, (void *) alloc_head); + goto fail; + } + blocks_allocated += blocks_available; + } + } + + if (len) { + int blocks_available = 0; + int last_sz = num_pools(memtype) - 1; + blocks_available = vcm_alloc_blocks_avail(memtype, last_sz); + + if (blocks_available > 0) { + ba = vcm_alloc_num_blocks(1, memtype, last_sz, + alloc_head); + if (ba != 1) { + vcm_alloc_err("blocks allocated (%i) !=" + " blocks_available (%i):" + " chunk size = %#x," + " alloc_head = %p\n", + ba, 1, + last_sz, + (void *) alloc_head); + goto fail; + } + blocks_allocated += 1; + } else { + vcm_alloc_err("blocks_available (%#x) <= 1\n", + blocks_available); + goto fail; + } + } + + return blocks_allocated; +fail: + vcm_alloc_free_blocks(memtype, alloc_head); + return 0; +} +EXPORT_SYMBOL(vcm_alloc_max_munch); diff --git a/arch/arm/mm/vcm_mm.c b/arch/arm/mm/vcm_mm.c new file mode 100644 index 0000000000000..dee51fab8a49e --- /dev/null +++ b/arch/arm/mm/vcm_mm.c @@ -0,0 +1,253 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* Architecture-specific VCM functions */ + +#include +#include + +#include +#include + +#define MRC(reg, processor, op1, crn, crm, op2) \ +__asm__ __volatile__ ( \ +" mrc " #processor "," #op1 ", %0," #crn "," #crm "," #op2 " \n" \ +: "=r" (reg)) + +#define RCP15_PRRR(reg) MRC(reg, p15, 0, c10, c2, 0) +#define RCP15_NMRR(reg) MRC(reg, p15, 0, c10, c2, 1) + + +/* Local type attributes (not the same as VCM) */ +#define ARM_MT_NORMAL 2 +#define ARM_MT_STRONGLYORDERED 0 +#define ARM_MT_DEVICE 1 + +#define ARM_CP_NONCACHED 0 +#define ARM_CP_WB_WA 1 +#define ARM_CP_WB_NWA 3 +#define ARM_CP_WT_NWA 2 + +#define smmu_err(a, ...) \ + pr_err("ERROR %s %i " a, __func__, __LINE__, ##__VA_ARGS__) + +#define FL_OFFSET(va) (((va) & 0xFFF00000) >> 20) +#define SL_OFFSET(va) (((va) & 0xFF000) >> 12) + +int vcm_driver_tex_class[4]; + +static int find_tex_class(int icp, int ocp, int mt, int nos) +{ + int i = 0; + unsigned int prrr = 0; + unsigned int nmrr = 0; + int c_icp, c_ocp, c_mt, c_nos; + + RCP15_PRRR(prrr); + RCP15_NMRR(nmrr); + + /* There are only 8 classes on this architecture */ + /* If they add more classes, registers will VASTLY change */ + for (i = 0; i < 8; i++) { + c_nos = prrr & (1 << (i + 24)) ? 1 : 0; + c_mt = (prrr & (3 << (i * 2))) >> (i * 2); + c_icp = (nmrr & (3 << (i * 2))) >> (i * 2); + c_ocp = (nmrr & (3 << (i * 2 + 16))) >> (i * 2 + 16); + + if (icp == c_icp && ocp == c_ocp && c_mt == mt && c_nos == nos) + return i; + } + smmu_err("Could not find TEX class for ICP=%d, OCP=%d, MT=%d, NOS=%d\n", + icp, ocp, mt, nos); + + /* In reality, we may want to remove this panic. Some classes just */ + /* will not be available, and will fail in smmu_set_attr */ + panic("SMMU: Could not determine TEX attribute mapping.\n"); + return -1; +} + + +int vcm_setup_tex_classes(void) +{ + unsigned int cpu_prrr; + unsigned int cpu_nmrr; + + if (!(get_cr() & CR_TRE)) /* No TRE? */ + panic("TEX remap not enabled, but the SMMU driver needs it!\n"); + + RCP15_PRRR(cpu_prrr); + RCP15_NMRR(cpu_nmrr); + + vcm_driver_tex_class[VCM_DEV_ATTR_NONCACHED] = + find_tex_class(ARM_CP_NONCACHED, ARM_CP_NONCACHED, + ARM_MT_NORMAL, 1); + + vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WB_WA] = + find_tex_class(ARM_CP_WB_WA, ARM_CP_WB_WA, + ARM_MT_NORMAL, 1); + + vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WB_NWA] = + find_tex_class(ARM_CP_WB_NWA, ARM_CP_WB_NWA, + ARM_MT_NORMAL, 1); + + vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WT] = + find_tex_class(ARM_CP_WT_NWA, ARM_CP_WT_NWA, + ARM_MT_NORMAL, 1); +#ifdef DEBUG_TEX + printk(KERN_INFO "VCM driver debug: Using TEX classes: %d %d %d %d\n", + vcm_driver_tex_class[VCM_DEV_ATTR_NONCACHED], + vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WB_WA], + vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WB_NWA], + vcm_driver_tex_class[VCM_DEV_ATTR_CACHED_WT]); +#endif + return 0; +} + + +int set_arm7_pte_attr(unsigned long pt_base, unsigned long va, + unsigned long len, unsigned int attr) +{ + unsigned long *fl_table = NULL; + unsigned long *fl_pte = NULL; + unsigned long fl_offset = 0; + unsigned long *sl_table = NULL; + unsigned long *sl_pte = NULL; + unsigned long sl_offset = 0; + int i; + int sh = 0; + int class = 0; + + /* Alignment */ + if (va & (len-1)) { + smmu_err("misaligned va: %p\n", (void *) va); + goto fail; + } + if (attr > 7) { + smmu_err("bad attribute: %d\n", attr); + goto fail; + } + + sh = (attr & VCM_DEV_ATTR_SH) ? 1 : 0; + class = vcm_driver_tex_class[attr & 0x03]; + + if (class > 7 || class < 0) { /* Bad class */ + smmu_err("bad tex class: %d\n", class); + goto fail; + } + + if (len != SZ_16M && len != SZ_1M && + len != SZ_64K && len != SZ_4K) { + smmu_err("bad size: %lu\n", len); + goto fail; + } + + fl_table = (unsigned long *) pt_base; + + if (!fl_table) { + smmu_err("null page table\n"); + goto fail; + } + + fl_offset = FL_OFFSET(va); /* Upper 12 bits */ + fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */ + + if (*fl_pte == 0) { /* Nothing there! */ + smmu_err("first level pte is 0\n"); + goto fail; + } + + /* Supersection attributes */ + if (len == SZ_16M) { + for (i = 0; i < 16; i++) { + /* Clear the old bits */ + *(fl_pte+i) &= ~(PMD_SECT_S | PMD_SECT_CACHEABLE | + PMD_SECT_BUFFERABLE | PMD_SECT_TEX(1)); + + /* Assign new class and S bit */ + *(fl_pte+i) |= sh ? PMD_SECT_S : 0; + *(fl_pte+i) |= class & 0x01 ? PMD_SECT_BUFFERABLE : 0; + *(fl_pte+i) |= class & 0x02 ? PMD_SECT_CACHEABLE : 0; + *(fl_pte+i) |= class & 0x04 ? PMD_SECT_TEX(1) : 0; + } + } else if (len == SZ_1M) { + + /* Clear the old bits */ + *(fl_pte) &= ~(PMD_SECT_S | PMD_SECT_CACHEABLE | + PMD_SECT_BUFFERABLE | PMD_SECT_TEX(1)); + + /* Assign new class and S bit */ + *(fl_pte) |= sh ? PMD_SECT_S : 0; + *(fl_pte) |= class & 0x01 ? PMD_SECT_BUFFERABLE : 0; + *(fl_pte) |= class & 0x02 ? PMD_SECT_CACHEABLE : 0; + *(fl_pte) |= class & 0x04 ? PMD_SECT_TEX(1) : 0; + } + + sl_table = (unsigned long *) __va(((*fl_pte) & 0xFFFFFC00)); + sl_offset = SL_OFFSET(va); + sl_pte = sl_table + sl_offset; + + if (len == SZ_64K) { + for (i = 0; i < 16; i++) { + /* Clear the old bits */ + *(sl_pte+i) &= ~(PTE_EXT_SHARED | PTE_CACHEABLE | + PTE_BUFFERABLE | PTE_EXT_TEX(1)); + + /* Assign new class and S bit */ + *(sl_pte+i) |= sh ? PTE_EXT_SHARED : 0; + *(sl_pte+i) |= class & 0x01 ? PTE_BUFFERABLE : 0; + *(sl_pte+i) |= class & 0x02 ? PTE_CACHEABLE : 0; + *(sl_pte+i) |= class & 0x04 ? PTE_EXT_TEX(1) : 0; + } + } else if (len == SZ_4K) { + /* Clear the old bits */ + *(sl_pte) &= ~(PTE_EXT_SHARED | PTE_CACHEABLE | + PTE_BUFFERABLE | PTE_EXT_TEX(1)); + + /* Assign new class and S bit */ + *(sl_pte) |= sh ? PTE_EXT_SHARED : 0; + *(sl_pte) |= class & 0x01 ? PTE_BUFFERABLE : 0; + *(sl_pte) |= class & 0x02 ? PTE_CACHEABLE : 0; + *(sl_pte) |= class & 0x04 ? PTE_EXT_TEX(1) : 0; + } + + + mb(); + return 0; +fail: + return 1; +} + + +int cpu_set_attr(unsigned long va, unsigned long len, unsigned int attr) +{ + int ret; + pgd_t *pgd = init_mm.pgd; + + if (!pgd) { + smmu_err("null pgd\n"); + goto fail; + } + + ret = set_arm7_pte_attr((unsigned long)pgd, va, len, attr); + + if (ret != 0) { + smmu_err("could not set attribute: \ + pgd=%p, va=%p, len=%lu, attr=%d\n", + (void *) pgd, (void *) va, len, attr); + goto fail; + } + dmb(); + flush_tlb_all(); + return 0; +fail: + return -1; +} diff --git a/arch/arm/oprofile/common.c b/arch/arm/oprofile/common.c index c074e66ad224e..afd7afba96ec5 100644 --- a/arch/arm/oprofile/common.c +++ b/arch/arm/oprofile/common.c @@ -40,6 +40,12 @@ char *op_name_from_perf_id(void) return "arm/armv7"; case ARM_PERF_PMU_ID_CA9: return "arm/armv7-ca9"; + case ARM_PERF_PMU_ID_SCORPION: + return "arm/armv7-scorpion"; + case ARM_PERF_PMU_ID_SCORPIONMP: + return "arm/armv7-scorpionmp"; + case ARM_PERF_PMU_ID_KRAIT: + return "arm/armv7-krait"; default: return NULL; } diff --git a/arch/arm/perfmon/Makefile b/arch/arm/perfmon/Makefile new file mode 100644 index 0000000000000..716e0873d3903 --- /dev/null +++ b/arch/arm/perfmon/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_KSAPI) += ksapi.o + +# Object file lists. +obj-y += perf-function-hooks.o +ksapi-y += perf-v7.o per.o per-process-perf.o per-axi.o +ksapi-$(CONFIG_ARCH_MSM8X60) += perf-smp.o diff --git a/arch/arm/perfmon/cp15_registers.h b/arch/arm/perfmon/cp15_registers.h new file mode 100644 index 0000000000000..1adfceedea20d --- /dev/null +++ b/arch/arm/perfmon/cp15_registers.h @@ -0,0 +1,109 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* +cp15_registers.h + +DESCRIPTION: define macros for reading and writing to the cp registers +for the ARMv7 + +REV/DATE: Fri Mar 18 15:54:32 EST 2005 +*/ + +#ifndef __cp15_registers__ +#define __cp15_registers__ + +#include "mcrmrc.h" + +#define WCP15_SDER(reg) MCR15(reg, 0, c1, c1, 1) +/* +* Performance Monitor Registers +*/ +#define WCP15_PMACTLR(reg) MCR15(reg, 0, c9, c15, 5) +#define WCP15_PMCCNTCR(reg) MCR15(reg, 0, c9, c15, 2) +#define WCP15_PMCCNTR(reg) MCR15(reg, 0, c9, c13, 0) +#define WCP15_PMCCNTSR(reg) MCR15(reg, 0, c9, c13, 3) +#define WCP15_PMCNTENCLR(reg) MCR15(reg, 0, c9, c12, 2) +#define WCP15_PMCNTENSET(reg) MCR15(reg, 0, c9, c12, 1) +#define WCP15_PMCR(reg) MCR15(reg, 0, c9, c12, 0) +#define WCP15_PMINTENCLR(reg) MCR15(reg, 0, c9, c14, 2) +#define WCP15_PMINTENSET(reg) MCR15(reg, 0, c9, c14, 1) +#define WCP15_PMOVSR(reg) MCR15(reg, 0, c9, c12, 3) +#define WCP15_PMRLDR(reg) MCR15(reg, 0, c9, c15, 4) +#define WCP15_PMSELR(reg) MCR15(reg, 0, c9, c12, 5) +#define WCP15_PMSWINC(reg) MCR15(reg, 0, c9, c12, 4) +#define WCP15_PMUSERENR(reg) MCR15(reg, 0, c9, c14, 0) +#define WCP15_PMXEVCNTCR(reg) MCR15(reg, 0, c9, c15, 0) +#define WCP15_PMXEVCNTR(reg) MCR15(reg, 0, c9, c13, 2) +#define WCP15_PMXEVCNTSR(reg) MCR15(reg, 0, c9, c15, 1) +#define WCP15_PMXEVTYPER(reg) MCR15(reg, 0, c9, c13, 1) +#define WCP15_LPM0EVTYPER(reg) MCR15(reg, 0, c15, c0, 0) +#define WCP15_LPM1EVTYPER(reg) MCR15(reg, 1, c15, c0, 0) +#define WCP15_LPM2EVTYPER(reg) MCR15(reg, 2, c15, c0, 0) +#define WCP15_LPM3EVTYPER(reg) MCR15(reg, 3, c15, c0, 0) +#define WCP15_L2LPMEVTYPER(reg) MCR15(reg, 3, c15, c2, 0) +#define WCP15_VLPMEVTYPER(reg) MCR15(reg, 7, c11, c0, 0) +#define WCP15_L2VR3F1(reg) MCR15(reg, 3, c15, c15, 1) + +/* +* READ the registers +*/ +#define RCP15_SDER(reg) MRC15(reg, 0, c1, c1, 1) +/* +* Performance Monitor Registers +*/ +#define RCP15_PMACTLR(reg) MRC15(reg, 0, c9, c15, 5) +#define RCP15_PMCCNTCR(reg) MRC15(reg, 0, c9, c15, 2) +#define RCP15_PMCCNTR(reg) MRC15(reg, 0, c9, c13, 0) +#define RCP15_PMCCNTSR(reg) MRC15(reg, 0, c9, c13, 3) +#define RCP15_PMCNTENCLR(reg) MRC15(reg, 0, c9, c12, 2) +#define RCP15_PMCNTENSET(reg) MRC15(reg, 0, c9, c12, 1) +#define RCP15_PMCR(reg) MRC15(reg, 0, c9, c12, 0) +#define RCP15_PMINTENCLR(reg) MRC15(reg, 0, c9, c14, 2) +#define RCP15_PMINTENSET(reg) MRC15(reg, 0, c9, c14, 1) +#define RCP15_PMOVSR(reg) MRC15(reg, 0, c9, c12, 3) +#define RCP15_PMRLDR(reg) MRC15(reg, 0, c9, c15, 4) +#define RCP15_PMSELR(reg) MRC15(reg, 0, c9, c12, 5) +#define RCP15_PMSWINC(reg) MRC15(reg, 0, c9, c12, 4) +#define RCP15_PMUSERENR(reg) MRC15(reg, 0, c9, c14, 0) +#define RCP15_PMXEVCNTCR(reg) MRC15(reg, 0, c9, c15, 0) +#define RCP15_PMXEVCNTR(reg) MRC15(reg, 0, c9, c13, 2) +#define RCP15_PMXEVCNTSR(reg) MRC15(reg, 0, c9, c15, 1) +#define RCP15_PMXEVTYPER(reg) MRC15(reg, 0, c9, c13, 1) +#define RCP15_LPM0EVTYPER(reg) MRC15(reg, 0, c15, c0, 0) +#define RCP15_LPM1EVTYPER(reg) MRC15(reg, 1, c15, c0, 0) +#define RCP15_LPM2EVTYPER(reg) MRC15(reg, 2, c15, c0, 0) +#define RCP15_LPM3EVTYPER(reg) MRC15(reg, 3, c15, c0, 0) +#define RCP15_L2LPMEVTYPER(reg) MRC15(reg, 3, c15, c2, 0) +#define RCP15_VLPMEVTYPER(reg) MRC15(reg, 7, c11, c0, 0) +#define RCP15_CONTEXTIDR(reg) MRC15(reg, 0, c13, c0, 1) +#define RCP15_L2CR0(reg) MRC15(reg, 3, c15, c0, 1) +#define RCP15_L2VR3F1(reg) MRC15(reg, 3, c15, c15, 1) + +#endif + diff --git a/arch/arm/perfmon/l2_cp15_registers.h b/arch/arm/perfmon/l2_cp15_registers.h new file mode 100644 index 0000000000000..e53aeaff36bc7 --- /dev/null +++ b/arch/arm/perfmon/l2_cp15_registers.h @@ -0,0 +1,103 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* +cp15_registers.h + +DESCRIPTION: define macros for reading and writing to the cp registers +for the ARMv7 + +REV/DATE: Fri Mar 18 15:54:32 EST 2005 +*/ + +#ifndef __l2_cp15_registers__ +#define __l2_cp15_registers__ + +#include "mcrmrc.h" + +#define WCP15_SDER(reg) MCR15(reg, 0, c1, c1, 1) +/* +* Performance Monitor Registers +*/ +#define WCP15_L2MPCR(reg) MCR15(reg, 3, c15, c0, 4) +#define WCP15_L2PMCCNTCR(reg) MCR15(reg, 3, c15, c4, 4) +#define WCP15_L2PMCCNTR(reg) MCR15(reg, 3, c15, c4, 5) +#define WCP15_L2PMCCNTSR(reg) MCR15(reg, 3, c15, c4, 6) +#define WCP15_L2PMCNTENCLR(reg) MCR15(reg, 3, c15, c4, 2) +#define WCP15_L2PMCNTENSET(reg) MCR15(reg, 3, c15, c4, 3) +#define WCP15_L2PMCR(reg) MCR15(reg, 3, c15, c4, 0) +#define WCP15_L2PMINTENCLR(reg) MCR15(reg, 3, c15, c5, 0) +#define WCP15_L2PMINTENSET(reg) MCR15(reg, 3, c15, c5, 1) +#define WCP15_L2PMOVSR(reg) MCR15(reg, 3, c15, c4, 1) +#define WCP15_L2PMRLDR(reg) MCR15(reg, 3, c15, c4, 7) +#define WCP15_L2PMSELR(reg) MCR15(reg, 3, c15, c6, 0) +#define WCP15_L2PMXEVCNTCR(reg) MCR15(reg, 3, c15, c6, 4) +#define WCP15_L2PMXEVCNTR(reg) MCR15(reg, 3, c15, c6, 5) +#define WCP15_L2PMXEVCNTSR(reg) MCR15(reg, 3, c15, c6, 6) +#define WCP15_L2PMXEVTYPER(reg) MCR15(reg, 3, c15, c6, 7) +#define WCP15_L2PMXEVFILTER(reg) MCR15(reg, 3, c15, c6, 3) +#define WCP15_L2PMEVTYPER0(reg) MCR15(reg, 3, c15, c7, 0) +#define WCP15_L2PMEVTYPER1(reg) MCR15(reg, 3, c15, c7, 1) +#define WCP15_L2PMEVTYPER2(reg) MCR15(reg, 3, c15, c7, 2) +#define WCP15_L2PMEVTYPER3(reg) MCR15(reg, 3, c15, c7, 3) +#define WCP15_L2PMEVTYPER4(reg) MCR15(reg, 3, c15, c7, 4) +#define WCP15_L2VR3F1(reg) MCR15(reg, 3, c15, c15, 1) + +/* +* READ the registers +*/ +#define RCP15_SDER(reg) MRC15(reg, 0, c1, c1, 1) +/* +* Performance Monitor Registers +*/ +#define RCP15_L2MPCR(reg) MRC15(reg, 3, c15, c0, 4) +#define RCP15_L2PMCCNTCR(reg) MRC15(reg, 3, c15, c4, 4) +#define RCP15_L2PMCCNTR(reg) MRC15(reg, 3, c15, c4, 5) +#define RCP15_L2PMCCNTSR(reg) MRC15(reg, 3, c15, c4, 6) +#define RCP15_L2PMCNTENCLR(reg) MRC15(reg, 3, c15, c4, 2) +#define RCP15_L2PMCNTENSET(reg) MRC15(reg, 3, c15, c4, 3) +#define RCP15_L2PMCR(reg) MRC15(reg, 3, c15, c4, 0) +#define RCP15_L2PMINTENCLR(reg) MRC15(reg, 3, c15, c5, 0) +#define RCP15_L2PMINTENSET(reg) MRC15(reg, 3, c15, c5, 1) +#define RCP15_L2PMOVSR(reg) MRC15(reg, 3, c15, c4, 1) +#define RCP15_L2PMRLDR(reg) MRC15(reg, 3, c15, c4, 7) +#define RCP15_L2PMSELR(reg) MRC15(reg, 3, c15, c6, 0) +#define RCP15_L2PMXEVCNTCR(reg) MRC15(reg, 3, c15, c6, 4) +#define RCP15_L2PMXEVCNTR(reg) MRC15(reg, 3, c15, c6, 5) +#define RCP15_L2PMXEVCNTSR(reg) MRC15(reg, 3, c15, c6, 6) +#define RCP15_L2PMXEVTYPER(reg) MRC15(reg, 3, c15, c6, 7) +#define RCP15_L2PMXEVFILTER(reg) MRC15(reg, 3, c15, c6, 3) +#define RCP15_L2PMEVTYPER0(reg) MRC15(reg, 3, c15, c7, 0) +#define RCP15_L2PMEVTYPER1(reg) MRC15(reg, 3, c15, c7, 1) +#define RCP15_L2PMEVTYPER2(reg) MRC15(reg, 3, c15, c7, 2) +#define RCP15_L2PMEVTYPER3(reg) MRC15(reg, 3, c15, c7, 3) +#define RCP15_L2PMEVTYPER4(reg) MRC15(reg, 3, c15, c7, 4) +#define RCP15_L2VR3F1(reg) MRC15(reg, 3, c15, c15, 1) + +#endif + diff --git a/arch/arm/perfmon/mcrmrc.h b/arch/arm/perfmon/mcrmrc.h new file mode 100644 index 0000000000000..c161af06e41df --- /dev/null +++ b/arch/arm/perfmon/mcrmrc.h @@ -0,0 +1,101 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* +mrcmcr.h + +DESCRIPTION: Convenience macros for access the cp registers in the arm. + +REV/DATE: Fri Mar 18 16:34:44 EST 2005 +*/ + +#ifndef __mrcmcr__h_ +#define __mrcmcr__h_ + +/* +* Define some convenience macros to acccess the cp registers from c code +* Lots of macro trickery here. +* +* Takes the same format as the asm instructions and unfortunatly you cannot +* use variables to select the crn, crn or op fields... +* +* For those unfamiliar with the # and string stuff. +* # creates a string from the value and any two strings that are beside +* are concatenated...thus these create one big asm string for the +* inline asm code. +* +* When compiled these compile to single asm instructions (fast) but +* without all the hassel of __asm__ __volatile__ (...) =r +* +* Format is: +* +* unsigned long reg; // destination variable +* MRC(reg, p15, 0, c1, c0, 0 ); +* +* MRC read control register +* MCR control register write +*/ + +/* +* Some assembly macros so we can use the same macros as in the C version. +* Turns the ASM code a little C-ish but keeps the code consistent and in +* one location... +*/ +#ifdef __ASSEMBLY__ + + +#define MRC(reg, processor, op1, crn, crm, op2) \ +(mrc processor , op1 , reg, crn , crm , op2) + +#define MCR(reg, processor, op1, crn, crm, op2) \ +(mcr processor , op1 , reg, crn , crm , op2) + +/* +* C version of the macros. +*/ +#else + +#define MRC(reg, processor, op1, crn, crm, op2) \ +__asm__ __volatile__ ( \ +" mrc " #processor "," #op1 ", %0," #crn "," #crm "," #op2 "\n" \ +: "=r" (reg)) + +#define MCR(reg, processor, op1, crn, crm, op2) \ +__asm__ __volatile__ ( \ +" mcr " #processor "," #op1 ", %0," #crn "," #crm "," #op2 "\n" \ +: : "r" (reg)) +#endif + + +/* +* Easy access convenience function to read CP15 registers from c code +*/ +#define MRC15(reg, op1, crn, crm, op2) MRC(reg, p15, op1, crn, crm, op2) +#define MCR15(reg, op1, crn, crm, op2) MCR(reg, p15, op1, crn, crm, op2) + +#endif diff --git a/arch/arm/perfmon/per-axi.c b/arch/arm/perfmon/per-axi.c new file mode 100644 index 0000000000000..48309bed82e5b --- /dev/null +++ b/arch/arm/perfmon/per-axi.c @@ -0,0 +1,759 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* +per-axi +DESCRIPTION +Functions related to AXI bus performance counter manipulations. +*/ + +#include +#include +#include +#include +#include +#include "asm/uaccess.h" +#include "per-axi.h" +#include "perf.h" + +/* +Definitions for AXI register addresses, macros to set and get register values +*/ +#define AXI_BASE_SIZE 0x00004000 +#define AXI_REG_BASE (AXI_BASE + 0x00000000) +#define AXI_REG_BASE_PHYS 0xa8200000 + +#define __inpdw(port) ioread32(port) +#define in_dword_masked(addr, mask) (__inpdw(addr) & (mask)) +#define __outpdw(port, val) (iowrite32((uint32_t) (val), port)) +#define out_dword(addr, val) __outpdw(addr, val) + +#define HWIO_AXI_MONITOR_CYCLE_COUNT_UPPER_REG_ADDR \ + (AXI_REG_BASE + 0x00003434) +#define HWIO_AXI_MONITOR_CYCLE_COUNT_UPPER_REG_RMSK 0xffff +#define HWIO_AXI_MONITOR_CYCLE_COUNT_UPPER_REG_IN \ + in_dword_masked(HWIO_AXI_MONITOR_CYCLE_COUNT_UPPER_REG_ADDR, \ + HWIO_AXI_MONITOR_CYCLE_COUNT_UPPER_REG_RMSK) + +#define HWIO_AXI_MONITOR_CYCLE_COUNT_LOWER_REG_ADDR (AXI_REG_BASE + 0x00003438) +#define HWIO_AXI_MONITOR_CYCLE_COUNT_LOWER_REG_RMSK 0xffff +#define HWIO_AXI_MONITOR_CYCLE_COUNT_LOWER_REG_IN \ + in_dword_masked(HWIO_AXI_MONITOR_CYCLE_COUNT_LOWER_REG_ADDR, \ + HWIO_AXI_MONITOR_CYCLE_COUNT_LOWER_REG_RMSK) + +#define HWIO_AXI_MONITOR_SELECTION_REG0_ADDR (AXI_REG_BASE + 0x00003428) +#define HWIO_AXI_MONITOR_SELECTION_REG1_ADDR (AXI_REG_BASE + 0x0000342c) +#define HWIO_AXI_MONITOR_TENURE_SELECTION_REG_ADDR (AXI_REG_BASE + 0x00003430) +#define HWIO_AXI_MONITOR_SELECTION_REG0_ETC_BMSK 0x4000 +#define HWIO_AXI_MONITOR_SELECTION_REG0_ECC_BMSK 0x2000 +#define HWIO_AXI_MONITOR_SELECTION_REG0_EEC1_BMSK 0x800 +#define HWIO_AXI_MONITOR_SELECTION_REG0_EEC0_BMSK 0x200 +#define HWIO_AXI_MONITOR_CYCLE_COUNT_UPPER_REG_OUT(v) \ + out_dword(HWIO_AXI_MONITOR_CYCLE_COUNT_UPPER_REG_ADDR, v) +#define HWIO_AXI_MONITOR_CYCLE_COUNT_LOWER_REG_OUT(v) \ + out_dword(HWIO_AXI_MONITOR_CYCLE_COUNT_LOWER_REG_ADDR, v) +#define HWIO_AXI_MONITOR_SELECTION_REG0_OUT(v) \ + out_dword(HWIO_AXI_MONITOR_SELECTION_REG0_ADDR, v) +#define HWIO_AXI_MONITOR_SELECTION_REG1_OUT(v) \ + out_dword(HWIO_AXI_MONITOR_SELECTION_REG1_ADDR, v) +#define HWIO_AXI_MONITOR_TENURE_SELECTION_REG_OUT(v) \ + out_dword(HWIO_AXI_MONITOR_TENURE_SELECTION_REG_ADDR, v) +#define HWIO_AXI_MONITOR_SELECTION_REG0_RMSK 0xffff +#define HWIO_AXI_MONITOR_SELECTION_REG0_IN \ + in_dword_masked(HWIO_AXI_MONITOR_SELECTION_REG0_ADDR, \ + HWIO_AXI_MONITOR_SELECTION_REG0_RMSK) + +#define HWIO_AXI_CONFIGURATION_REG_ADDR (AXI_REG_BASE + 0x00000008) +#define HWIO_AXI_CONFIGURATION_REG_OUT(v) \ + out_dword(HWIO_AXI_CONFIGURATION_REG_ADDR, v) +#define HWIO_AXI_CONFIGURATION_REG_PPDM_BMSK 0x0 +#define HWIO_AXI_CONFIGURATION_REG_DISABLE 0x2 +#define AXI_EVTSEL_ENABLE_MASK 0x6a00 +#define AXI_EVTSEL_DISABLE_MASK 0x95ff +#define AXI_EVTSEL_RESET_MASK 0xfe40 + +#define HWIO_AXI_MONITOR_EVENT_LOWER_REG0_ADDR (AXI_REG_BASE + 0x00003450) +#define HWIO_AXI_MONITOR_EVENT_LOWER_REG0_RMSK 0xffff +#define HWIO_AXI_MONITOR_EVENT_LOWER_REG0_SHFT 0 +#define HWIO_AXI_MONITOR_EVENT_LOWER_REG0_IN \ + in_dword_masked(HWIO_AXI_MONITOR_EVENT_LOWER_REG0_ADDR, \ + HWIO_AXI_MONITOR_EVENT_LOWER_REG0_RMSK) +#define HWIO_AXI_MONITOR_EVENT_UPPER_REG0_ADDR (AXI_REG_BASE + 0x00003454) +#define HWIO_AXI_MONITOR_EVENT_UPPER_REG0_RMSK 0xffff +#define HWIO_AXI_MONITOR_EVENT_UPPER_REG0_SHFT 0 +#define HWIO_AXI_MONITOR_EVENT_UPPER_REG0_IN \ + in_dword_masked(HWIO_AXI_MONITOR_EVENT_UPPER_REG0_ADDR, \ + HWIO_AXI_MONITOR_EVENT_UPPER_REG0_RMSK) + +#define HWIO_AXI_MONITOR_EVENT_LOWER_REG1_ADDR (AXI_REG_BASE + 0x00003458) +#define HWIO_AXI_MONITOR_EVENT_LOWER_REG1_RMSK 0xffff +#define HWIO_AXI_MONITOR_EVENT_LOWER_REG1_SHFT 0 +#define HWIO_AXI_MONITOR_EVENT_LOWER_REG1_IN \ + in_dword_masked(HWIO_AXI_MONITOR_EVENT_LOWER_REG1_ADDR, \ + HWIO_AXI_MONITOR_EVENT_LOWER_REG1_RMSK) +#define HWIO_AXI_MONITOR_EVENT_UPPER_REG1_ADDR (AXI_REG_BASE + 0x0000345c) +#define HWIO_AXI_MONITOR_EVENT_UPPER_REG1_RMSK 0xffff +#define HWIO_AXI_MONITOR_EVENT_UPPER_REG1_SHFT 0 +#define HWIO_AXI_MONITOR_EVENT_UPPER_REG1_IN \ + in_dword_masked(HWIO_AXI_MONITOR_EVENT_UPPER_REG1_ADDR, \ + HWIO_AXI_MONITOR_EVENT_UPPER_REG1_RMSK) + +#define HWIO_AXI_MONITOR_TENURE_LOWER_REG_ADDR (AXI_REG_BASE + 0x00003448) +#define HWIO_AXI_MONITOR_TENURE_LOWER_REG_RMSK 0xffff +#define HWIO_AXI_MONITOR_TENURE_LOWER_REG_SHFT 0 +#define HWIO_AXI_MONITOR_TENURE_LOWER_REG_IN \ + in_dword_masked(HWIO_AXI_MONITOR_TENURE_LOWER_REG_ADDR, \ + HWIO_AXI_MONITOR_TENURE_LOWER_REG_RMSK) +#define HWIO_AXI_MONITOR_TENURE_UPPER_REG_ADDR (AXI_REG_BASE + 0x00003444) +#define HWIO_AXI_MONITOR_TENURE_UPPER_REG_RMSK 0xffff +#define HWIO_AXI_MONITOR_TENURE_UPPER_REG_SHFT 0 +#define HWIO_AXI_MONITOR_TENURE_UPPER_REG_IN \ + in_dword_masked(HWIO_AXI_MONITOR_TENURE_UPPER_REG_ADDR, \ + HWIO_AXI_MONITOR_TENURE_UPPER_REG_RMSK) + +#define HWIO_AXI_MONITOR_MIN_REG_ADDR (AXI_REG_BASE + 0x0000343c) +#define HWIO_AXI_MONITOR_MIN_REG_RMSK 0xffff +#define HWIO_AXI_MONITOR_MIN_REG_SHFT 0 +#define HWIO_AXI_MONITOR_MIN_REG_IN \ + in_dword_masked(HWIO_AXI_MONITOR_MIN_REG_ADDR, \ + HWIO_AXI_MONITOR_MIN_REG_RMSK) +#define HWIO_AXI_MONITOR_MAX_REG_ADDR (AXI_REG_BASE + 0x00003440) +#define HWIO_AXI_MONITOR_MAX_REG_RMSK 0xffff +#define HWIO_AXI_MONITOR_MAX_REG_SHFT 0 +#define HWIO_AXI_MONITOR_MAX_REG_IN \ + in_dword_masked(HWIO_AXI_MONITOR_MAX_REG_ADDR, \ + HWIO_AXI_MONITOR_MAX_REG_RMSK) +#define HWIO_AXI_MONITOR_LAST_TENURE_REG_ADDR (AXI_REG_BASE + 0x0000344c) +#define HWIO_AXI_MONITOR_LAST_TENURE_REG_RMSK 0xffff +#define HWIO_AXI_MONITOR_LAST_TENURE_REG_SHFT 0 +#define HWIO_AXI_MONITOR_LAST_TENURE_REG_IN \ + in_dword_masked(HWIO_AXI_MONITOR_LAST_TENURE_REG_ADDR, \ + HWIO_AXI_MONITOR_LAST_TENURE_REG_RMSK) +#define HWIO_AXI_MONITOR_TENURE_UPPER_REG_OUT(v) \ + out_dword(HWIO_AXI_MONITOR_TENURE_UPPER_REG_ADDR, v) +#define HWIO_AXI_MONITOR_TENURE_LOWER_REG_OUT(v) \ + out_dword(HWIO_AXI_MONITOR_TENURE_LOWER_REG_ADDR, v) + +#define HWIO_AXI_RESET_ALL 0x9400 +#define HWIO_AXI_ENABLE_ALL_NOCYCLES 0x4a00 +#define HWIO_AXI_DISABLE_ALL 0xb500 +uint32_t AXI_BASE; + +unsigned int is_first = 1; +struct perf_mon_axi_data pm_axi_info; +struct perf_mon_axi_cnts axi_cnts; + +/* +FUNCTION get_axi_sel_reg0 + +DESCRIPTION + Retrieve the value of AXI_SEL_REG0 + +DEPENDENCIES + +RETURN VALUE + AXI_SEL_REG0 +SIDE EFFECTS +*/ +unsigned long get_axi_sel_reg0(void) +{ + return pm_axi_info.sel_reg0; +} + +/* +FUNCTION get_axi_sel_reg1 + +DESCRIPTION + Retrieve the value of AXI_SEL_REG1 + +DEPENDENCIES + +RETURN VALUE + AXI_SEL_REG1 +SIDE EFFECTS +*/ +unsigned long get_axi_sel_reg1(void) +{ + return pm_axi_info.sel_reg1; +} + +/* +FUNCTION get_axi_ten_sel_reg + +DESCRIPTION + Retrieve the value of AXI_TEN_REG + +DEPENDENCIES + +RETURN VALUE + AXI_TEN_REG +SIDE EFFECTS +*/ +unsigned long get_axi_ten_sel_reg(void) +{ + return pm_axi_info.ten_sel_reg; +} + +/* +FUNCTION get_axi_valid + +DESCRIPTION + Retrieve the value of AXI valid bit + +DEPENDENCIES + +RETURN VALUE + AXI Valid bit +SIDE EFFECTS +*/ +unsigned long get_axi_valid(void) +{ + return pm_axi_info.valid; +} + +/* +FUNCTION get_axi_enable + +DESCRIPTION + Retrieve the value of AXI enable bit + +DEPENDENCIES + +RETURN VALUE + AXI enable bit +SIDE EFFECTS +*/ +unsigned long get_axi_enable(void) +{ + return pm_axi_info.enable; +} + +/* +FUNCTION get_axi_clear + +DESCRIPTION + Retrieve the value of AXI clear bit + +DEPENDENCIES + +RETURN VALUE + AXI clear bit +SIDE EFFECTS +*/ +unsigned long get_axi_clear(void) +{ + return pm_axi_info.clear; +} + +/* +FUNCTION pm_axi_cnts_write + +DESCRIPTION + Write handler for the /proc axi results directory. + +DEPENDENCIES + +RETURN VALUE + Number of characters to output. + +SIDE EFFECTS +*/ +int pm_axi_cnts_write(struct file *file, const char *buff, + unsigned long cnt, void *data) +{ + char *newbuf; + struct PerfMonAxiCnts *p = + (struct PerfMonAxiCnts *)data; + + if (p == 0) + return cnt; + /* + * Alloc the user data in kernel space. and then copy user to kernel + */ + newbuf = kmalloc(cnt + 1, GFP_KERNEL); + if (0 == newbuf) + return cnt; + if (copy_from_user(newbuf, buff, cnt) != 0) { + printk(KERN_INFO "%s copy_from_user failed\n", __func__); + return cnt; + } + return cnt; +} + +/* +FUNCTION pm_axi_update_cnts + +DESCRIPTION + Read the current AXI counter values. Check for overflows and + adjust the values stored accordingly. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +void pm_axi_update_cnts(void) +{ + if (is_first) { + pm_axi_start(); + } else { + if (pm_axi_info.valid == 1) { + pm_axi_info.valid = 0; + pm_axi_update(); + } else { + pm_axi_enable(); + } + } + is_first = 0; + axi_cnts.cycles += pm_get_axi_cycle_count(); + axi_cnts.cnt0 += pm_get_axi_evt0_count(); + axi_cnts.cnt1 += pm_get_axi_evt1_count(); + axi_cnts.tenure_total += pm_get_axi_ten_total_count(); + + axi_cnts.tenure_min = pm_get_axi_ten_min_count(); + axi_cnts.tenure_max = pm_get_axi_ten_max_count(); + axi_cnts.tenure_last = pm_get_axi_ten_last_count(); + + pm_axi_start(); +} + +/* +FUNCTION pm_axi_clear_cnts + +DESCRIPTION + Clear the locally stored AXI counter values. + Also clear the AXI counter registers. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +void pm_axi_clear_cnts(void) +{ + axi_cnts.cycles = 0; + axi_cnts.cnt0 = 0; + axi_cnts.cnt1 = 0; + axi_cnts.tenure_total = 0; + axi_cnts.tenure_min = 0; + axi_cnts.tenure_max = 0; + axi_cnts.tenure_last = 0; + pm_axi_start(); +} + +/* +FUNCTION pm_axi_read_decimal + +DESCRIPTION + Read handler for the /proc axi results directory in decimal format. + +DEPENDENCIES + +RETURN VALUE + Number of characters to output. + +SIDE EFFECTS +*/ +int pm_axi_read_decimal(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct perf_mon_axi_cnts *p = (struct perf_mon_axi_cnts *)data; + + return sprintf(page, "cnt0:%llu cnt1:%llu tenure:%llu ten_max:%llu \ + ten_min:%llu ten_last:%llu cycles:%llu\n", + p->cnt0, + p->cnt1, + p->tenure_total, + p->tenure_max, + p->tenure_min, + p->tenure_last, + p->cycles); +} + +/* +FUNCTION pm_axi_read_hex + +DESCRIPTION + Read handler for the /proc axi results directory in hex format. + +DEPENDENCIES + +RETURN VALUE + Number of characters to output. + +SIDE EFFECTS +*/ +int pm_axi_read_hex(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct perf_mon_axi_cnts *p = (struct perf_mon_axi_cnts *)data; + + return sprintf(page, "cnt0:%llx cnt1:%llx tenure:%llx ten_max:%llx \ + ten_min:%llx ten_last:%llx cycles:%llx\n", + p->cnt0, + p->cnt1, + p->tenure_total, + p->tenure_max, + p->tenure_min, + p->tenure_last, + p->cycles); + +} + +/* +FUNCTION pm_axi_set_proc_entry + +DESCRIPTION + Create a generic entry for the /proc axi settings directory. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +void pm_axi_set_proc_entry(char *name, unsigned long *var, + struct proc_dir_entry *d, int hex) +{ + struct proc_dir_entry *pe; + pe = create_proc_entry(name, 0777, d); + if (0 == pe) + return; + if (hex) { + pe->read_proc = per_process_read; + pe->write_proc = per_process_write_hex; + } else { + pe->read_proc = per_process_read_decimal; + pe->write_proc = per_process_write_dec; + } + pe->data = (void *)var; +} + +/* +FUNCTION pm_axi_get_cnt_proc_entry + +DESCRIPTION + Create a generic entry for the /proc axi results directory. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +void pm_axi_get_cnt_proc_entry(char *name, struct perf_mon_axi_cnts *var, + struct proc_dir_entry *d, int hex) +{ + struct proc_dir_entry *pe; + pe = create_proc_entry(name, 0777, d); + if (0 == pe) + return; + if (hex) { + pe->read_proc = pm_axi_read_hex; + pe->write_proc = pm_axi_cnts_write; + } else { + pe->read_proc = pm_axi_read_decimal; + pe->write_proc = pm_axi_cnts_write; + } + pe->data = (void *)var; +} + +/* +FUNCTION pm_axi_clear_tenure + +DESCRIPTION + Clear AXI tenure cntr manually. Temporary solution till hardware bug + is fixed + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +void pm_axi_clear_tenure(void) +{ + HWIO_AXI_MONITOR_TENURE_UPPER_REG_OUT(0x0); + HWIO_AXI_MONITOR_TENURE_LOWER_REG_OUT(0x0); +} + +/* +FUNCTION pm_axi_init + +DESCRIPTION + Map AXI region to virtual memory. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +void pm_axi_init() +{ + /*Map the AXI regs*/ + #ifdef CONFIG_ARCH_QSD8X50 + { + /*Map the AXI regs*/ + AXI_BASE = (uint32_t)ioremap(AXI_REG_BASE_PHYS, AXI_BASE_SIZE); + if (!AXI_BASE) + printk(KERN_ERR "Mem map failed\n"); + } + #else + { + AXI_BASE = (uint32_t)kmalloc(AXI_BASE_SIZE, GFP_KERNEL); + } + #endif + +} + +/* +FUNCTION pm_axi_start + +DESCRIPTION + Set event0, event1 and tenure registers based on the /proc entries. + Set cycle cntr to fffffffe to start counters. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +void +pm_axi_start() +{ + unsigned long sel_reg0, sel_reg1, ten_sel_reg; + sel_reg0 = get_axi_sel_reg0(); + sel_reg1 = get_axi_sel_reg1(); + ten_sel_reg = get_axi_ten_sel_reg(); + HWIO_AXI_CONFIGURATION_REG_OUT(HWIO_AXI_CONFIGURATION_REG_PPDM_BMSK); + /*Set AXI Cycle Counter to enable AXI Monitors*/ + HWIO_AXI_MONITOR_CYCLE_COUNT_UPPER_REG_OUT(0xffff); + HWIO_AXI_MONITOR_CYCLE_COUNT_LOWER_REG_OUT(0xfffe); + /*Set master/slave*/ + HWIO_AXI_MONITOR_SELECTION_REG1_OUT(sel_reg1); + HWIO_AXI_MONITOR_SELECTION_REG0_OUT(HWIO_AXI_RESET_ALL); + HWIO_AXI_MONITOR_SELECTION_REG0_OUT(HWIO_AXI_ENABLE_ALL_NOCYCLES); + HWIO_AXI_MONITOR_SELECTION_REG0_OUT(HWIO_AXI_MONITOR_SELECTION_REG0_IN + | sel_reg0); + HWIO_AXI_MONITOR_SELECTION_REG0_OUT(HWIO_AXI_MONITOR_SELECTION_REG0_IN + | HWIO_AXI_MONITOR_SELECTION_REG0_ECC_BMSK); + HWIO_AXI_CONFIGURATION_REG_OUT(HWIO_AXI_CONFIGURATION_REG_PPDM_BMSK); +} + +/* +FUNCTION pm_axi_update + +DESCRIPTION + Set event0, event1 and tenure registers based on the /proc entries. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +void +pm_axi_update() +{ + HWIO_AXI_CONFIGURATION_REG_OUT(HWIO_AXI_CONFIGURATION_REG_PPDM_BMSK); + HWIO_AXI_MONITOR_SELECTION_REG0_OUT(HWIO_AXI_MONITOR_SELECTION_REG0_IN + | HWIO_AXI_RESET_ALL); + HWIO_AXI_MONITOR_SELECTION_REG0_OUT(HWIO_AXI_MONITOR_SELECTION_REG0_IN + & HWIO_AXI_DISABLE_ALL); + pm_axi_start(); +} + +/* +FUNCTION pm_axi_disable + +DESCRIPTION + Disable all cntrs. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +void +pm_axi_disable(void) +{ + unsigned long sel_reg0; + /*Disable cntrs*/ + sel_reg0 = get_axi_sel_reg0(); + HWIO_AXI_MONITOR_SELECTION_REG0_OUT(sel_reg0 & AXI_EVTSEL_DISABLE_MASK); + /*Disable clk*/ + HWIO_AXI_CONFIGURATION_REG_OUT(HWIO_AXI_CONFIGURATION_REG_DISABLE); +} + +/* +FUNCTION pm_axi_enable + +DESCRIPTION + Enable all cntrs. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +void +pm_axi_enable(void) +{ + unsigned long sel_reg0; + /*Enable cntrs*/ + sel_reg0 = get_axi_sel_reg0(); + HWIO_AXI_MONITOR_SELECTION_REG0_OUT(sel_reg0 | 0x6a00); + /*Enable clk*/ + HWIO_AXI_CONFIGURATION_REG_OUT(HWIO_AXI_CONFIGURATION_REG_PPDM_BMSK); +} + +/* +FUNCTION pm_axi_disable_cnts + +DESCRIPTION + Read cycle cntr value + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +unsigned long +pm_get_axi_cycle_count(void) +{ + if (HWIO_AXI_MONITOR_CYCLE_COUNT_UPPER_REG_IN == 0x0 && + HWIO_AXI_MONITOR_CYCLE_COUNT_LOWER_REG_IN == 0x0) { + /*Set AXI Cycle Counter to enable AXI Monitors*/ + HWIO_AXI_MONITOR_CYCLE_COUNT_UPPER_REG_OUT(0xffff); + HWIO_AXI_MONITOR_CYCLE_COUNT_LOWER_REG_OUT(0xfffe); + } + return 0xfffffffe - ((HWIO_AXI_MONITOR_CYCLE_COUNT_UPPER_REG_IN << 16) + + HWIO_AXI_MONITOR_CYCLE_COUNT_LOWER_REG_IN); +} + +/* +FUNCTION pm_get_axi_evt0_count + +DESCRIPTION + Read Event0 cntr value + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +unsigned long +pm_get_axi_evt0_count(void) +{ + return (HWIO_AXI_MONITOR_EVENT_UPPER_REG0_IN << 16) + + HWIO_AXI_MONITOR_EVENT_LOWER_REG0_IN; +} + +/* +FUNCTION pm_get_axi_evt1_count + +DESCRIPTION + Read Event1 cntr value + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +unsigned long +pm_get_axi_evt1_count(void) +{ + return (HWIO_AXI_MONITOR_EVENT_UPPER_REG1_IN << 16) + + HWIO_AXI_MONITOR_EVENT_LOWER_REG1_IN; +} + +/* +FUNCTION pm_get_axi_ten_min_count + +DESCRIPTION + Read min tenure cntr value + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +unsigned long +pm_get_axi_ten_min_count(void) +{ + return HWIO_AXI_MONITOR_MIN_REG_IN; +} + +/* +FUNCTION pm_get_axi_ten_max_count + +DESCRIPTION + Read max tenure cntr value + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +unsigned long +pm_get_axi_ten_max_count(void) +{ + return HWIO_AXI_MONITOR_MAX_REG_IN; +} + +/* +FUNCTION pm_get_axi_ten_total_count + +DESCRIPTION + Read total tenure cntr value + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +unsigned long +pm_get_axi_ten_total_count(void) +{ + return (HWIO_AXI_MONITOR_TENURE_UPPER_REG_IN << 16) + + HWIO_AXI_MONITOR_TENURE_LOWER_REG_IN; +} + +/* +FUNCTION pm_get_axi_ten_last_count + +DESCRIPTION + Read last tenure cntr value + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +unsigned long +pm_get_axi_ten_last_count(void) +{ + return HWIO_AXI_MONITOR_LAST_TENURE_REG_IN; +} diff --git a/arch/arm/perfmon/per-axi.h b/arch/arm/perfmon/per-axi.h new file mode 100644 index 0000000000000..185bb154a44c7 --- /dev/null +++ b/arch/arm/perfmon/per-axi.h @@ -0,0 +1,91 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* +*per-axi +*DESCRIPTION +*Header File for Functions related to AXI bus performance counter manipulations. +*/ + +#ifndef __PER_AXI_H__ +#define __PER_AXI_H__ +unsigned long pm_get_axi_cycle_count(void); +unsigned long pm_get_axi_evt0_count(void); +unsigned long pm_get_axi_evt1_count(void); +unsigned long pm_get_axi_evt2_count(void); +unsigned long pm_get_axi_ten_min_count(void); +unsigned long pm_get_axi_ten_max_count(void); +unsigned long pm_get_axi_ten_total_count(void); +unsigned long pm_get_axi_ten_last_count(void); + +unsigned long get_axi_sel_reg0(void); +unsigned long get_axi_sel_seg1(void); +unsigned long get_axi_ten_sel_reg(void); +unsigned long get_axi_valid(void); +unsigned long get_axi_enable(void); +unsigned long get_axi_clear(void); + +void pm_axi_clear_cnts(void); +void pm_axi_update_cnts(void); + +void pm_axi_init(void); +void pm_axi_start(void); +void pm_axi_update(void); +void pm_axi_disable(void); +void pm_axi_enable(void); + +struct perf_mon_axi_cnts{ + unsigned long long cycles; + unsigned long long cnt0; + unsigned long long cnt1; + unsigned long long tenure_total; + unsigned long long tenure_min; + unsigned long long tenure_max; + unsigned long long tenure_last; +}; + +struct perf_mon_axi_data{ + struct proc_dir_entry *proc; + unsigned long enable; + unsigned long clear; + unsigned long valid; + unsigned long sel_reg0; + unsigned long sel_reg1; + unsigned long ten_sel_reg; + unsigned long refresh; +}; + +extern struct perf_mon_axi_data pm_axi_info; +extern struct perf_mon_axi_cnts axi_cnts; + +void pm_axi_set_proc_entry(char *name, unsigned long *var, + struct proc_dir_entry *d, int hex); +void pm_axi_get_cnt_proc_entry(char *name, struct perf_mon_axi_cnts *var, + struct proc_dir_entry *d, int hex); + +#endif diff --git a/arch/arm/perfmon/per-process-perf.c b/arch/arm/perfmon/per-process-perf.c new file mode 100644 index 0000000000000..c8bebd842d192 --- /dev/null +++ b/arch/arm/perfmon/per-process-perf.c @@ -0,0 +1,1251 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. +* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +/* +per-process_perf +DESCRIPTION +Capture the processor performances registers when the process context +switches. The /proc file system is used to control and access the results +of the performance counters. + +Each time a process is context switched, the performance counters for +the Snoop Control Unit and the standard ARM counters are set according +to the values stored for that process. + +The events to capture per process are set in the /proc/ppPerf/settings +directory. + +EXTERNALIZED FUNCTIONS + +INITIALIZATION AND SEQUENCING REQUIREMENTS +Detail how to initialize and use this service. The sequencing aspect +is only needed if the order of operations is important. +*/ + +/* +INCLUDE FILES FOR MODULE +*/ +#include +#include +#include +#include +#include +#include "linux/proc_fs.h" +#include "linux/kernel_stat.h" +#include +#include "asm/uaccess.h" +#include "cp15_registers.h" +#include "l2_cp15_registers.h" +#include +#include "per-axi.h" +#include "perf.h" + +#define DEBUG_SWAPIO +#ifdef DEBUG_SWAPIO +#define MR_SIZE 1024 +#define PM_PP_ERR -1 +struct mark_data_s { + long c; + long cpu; + unsigned long pid_old; + unsigned long pid_new; +}; + +struct mark_data_s markRay[MR_SIZE] __attribute__((aligned(16))); +int mrcnt; + +DEFINE_SPINLOCK(_mark_lock); + +static inline void MARKPIDS(char a, int opid, int npid) +{ + int cpu = smp_processor_id(); + + if (opid == 0) + return; + spin_lock(&_mark_lock); + if (++mrcnt >= MR_SIZE) + mrcnt = 0; + spin_unlock(&_mark_lock); + + markRay[mrcnt].pid_old = opid; + markRay[mrcnt].pid_new = npid; + markRay[mrcnt].cpu = cpu; + markRay[mrcnt].c = a; +} +static inline void MARK(char a) { MARKPIDS(a, 0xFFFF, 0xFFFF); } +static inline void MARKPID(char a, int pid) { MARKPIDS(a, pid, 0xFFFF); } + +#else +#define MARK(a) +#define MARKPID(a, b) +#define MARKPIDS(a, b, c) + +#endif /* DEBUG_SWAPIO */ + +/* +DEFINITIONS AND DECLARATIONS FOR MODULE + +This section contains definitions for constants, macros, types, variables +and other items needed by this module. +*/ + +/* +Constant / Define Declarations +*/ + +#define PERF_MON_PROCESS_NUM 0x400 +#define PERF_MON_PROCESS_MASK (PERF_MON_PROCESS_NUM-1) +#define PP_MAX_PROC_ENTRIES 32 + +/* + * The entry is locked and is not to be replaced. + */ +#define PERF_ENTRY_LOCKED (1<<0) +#define PERF_NOT_FIRST_TIME (1<<1) +#define PERF_EXITED (1<<2) +#define PERF_AUTOLOCK (1<<3) + +#define IS_LOCKED(p) (p->flags & PERF_ENTRY_LOCKED) + +#define PERF_NUM_MONITORS 4 + +#define L1_EVENTS_0 0 +#define L1_EVENTS_1 1 +#define L2_EVENTS_0 2 +#define L2_EVENTS_1 3 + +#define PM_CYCLE_OVERFLOW_MASK 0x80000000 +#define L2_PM_CYCLE_OVERFLOW_MASK 0x80000000 + +#define PM_START_ALL() do {\ + if (pm_global) \ + pmStartAll();\ + } while (0); +#define PM_STOP_ALL() do {\ + if (pm_global)\ + pmStopAll();\ + } while (0); +#define PM_RESET_ALL() do {\ + if (pm_global)\ + pmResetAll();\ + } while (0); + +/* + * Accessors for SMP based variables. + */ +#define _SWAPS(p) ((p)->cnts[smp_processor_id()].swaps) +#define _CYCLES(p) ((p)->cnts[smp_processor_id()].cycles) +#define _COUNTS(p, i) ((p)->cnts[smp_processor_id()].counts[i]) +#define _L2COUNTS(p, i) ((p)->cnts[smp_processor_id()].l2_counts[i]) +#define _L2CYCLES(p) ((p)->cnts[smp_processor_id()].l2_cycles) + +/* + Type Declarations +*/ + +/* + * Counts are on a per core basis. + */ +struct pm_counters_s { + unsigned long long cycles; + unsigned long long l2_cycles; + unsigned long long counts[PERF_NUM_MONITORS]; + unsigned long long l2_counts[PERF_NUM_MONITORS]; + unsigned long swaps; +}; + +struct per_process_perf_mon_type{ + struct pm_counters_s cnts[NR_CPUS]; + unsigned long control; + unsigned long index[PERF_NUM_MONITORS]; + unsigned long l2_index[PERF_NUM_MONITORS]; + unsigned long pid; + struct proc_dir_entry *proc; + struct proc_dir_entry *l2_proc; + unsigned short flags; + unsigned short running_cpu; + char *pidName; + unsigned long lpm0evtyper; + unsigned long lpm1evtyper; + unsigned long lpm2evtyper; + unsigned long l2lpmevtyper; + unsigned long vlpmevtyper; + unsigned long l2pmevtyper0; + unsigned long l2pmevtyper1; + unsigned long l2pmevtyper2; + unsigned long l2pmevtyper3; + unsigned long l2pmevtyper4; +}; + +unsigned long last_in_pid[NR_CPUS]; +unsigned long fake_swap_out[NR_CPUS] = {0}; + +/* + Local Object Definitions +*/ +struct per_process_perf_mon_type perf_mons[PERF_MON_PROCESS_NUM]; +struct proc_dir_entry *proc_dir; +struct proc_dir_entry *settings_dir; +struct proc_dir_entry *values_dir; +struct proc_dir_entry *axi_dir; +struct proc_dir_entry *l2_dir; +struct proc_dir_entry *axi_settings_dir; +struct proc_dir_entry *axi_results_dir; +struct proc_dir_entry *l2_results_dir; + +unsigned long pp_enabled; +unsigned long pp_settings_valid = -1; +unsigned long pp_auto_lock; +unsigned long pp_set_pid; +signed long pp_clear_pid = -1; +unsigned long per_proc_event[PERF_NUM_MONITORS]; +unsigned long l2_per_proc_event[PERF_NUM_MONITORS]; +unsigned long dbg_flags; +unsigned long pp_lpm0evtyper; +unsigned long pp_lpm1evtyper; +unsigned long pp_lpm2evtyper; +unsigned long pp_l2lpmevtyper; +unsigned long pp_vlpmevtyper; +unsigned long pm_stop_for_interrupts; +unsigned long pm_global; /* track all, not process based */ +unsigned long pm_global_enable; +unsigned long pm_remove_pid; + +unsigned long pp_l2pmevtyper0; +unsigned long pp_l2pmevtyper1; +unsigned long pp_l2pmevtyper2; +unsigned long pp_l2pmevtyper3; +unsigned long pp_l2pmevtyper4; + +unsigned long pp_proc_entry_index; +char *per_process_proc_names[PP_MAX_PROC_ENTRIES]; + +unsigned int axi_swaps; +#define MAX_AXI_SWAPS 10 +int first_switch = 1; +/* + Forward Declarations +*/ + +/* +Function Definitions +*/ + +/* +FUNCTION per_process_find + +DESCRIPTION + Find the per process information based on the process id (pid) passed. + This is a simple mask based on the number of entries stored in the + static array + +DEPENDENCIES + +RETURN VALUE + Pointer to the per process data +SIDE EFFECTS + +*/ +struct per_process_perf_mon_type *per_process_find(unsigned long pid) +{ + return &perf_mons[pid & PERF_MON_PROCESS_MASK]; +} + +/* +FUNCTION per_process_get_name + +DESCRIPTION + Retreive the name of the performance counter based on the table and + index passed. We have two different sets of performance counters so + different table need to be used. + +DEPENDENCIES + +RETURN VALUE + Pointer to char string with the name of the event or "BAD" + Never returns NULL or a bad pointer. + +SIDE EFFECTS +*/ +char *per_process_get_name(unsigned long index) +{ + return pm_find_event_name(index); +} + +/* +FUNCTION per_process_results_read + +DESCRIPTION + Print out the formatted results from the process id read. Event names + and counts are printed. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +int per_process_results_read(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct per_process_perf_mon_type *p = + (struct per_process_perf_mon_type *)data; + struct pm_counters_s cnts; + int i, j; + + /* + * Total across all CPUS + */ + memset(&cnts, 0, sizeof(cnts)); + for (i = 0; i < num_possible_cpus(); i++) { + cnts.swaps += p->cnts[i].swaps; + cnts.cycles += p->cnts[i].cycles; + for (j = 0; j < PERF_NUM_MONITORS; j++) + cnts.counts[j] += p->cnts[i].counts[j]; + } + + /* + * Display as single results of the totals calculated above. + * Do we want to display or have option to display individula cores? + */ + return sprintf(page, "pid:%lu one:%s:%llu two:%s:%llu three:%s:%llu \ + four:%s:%llu cycles:%llu swaps:%lu\n", + p->pid, + per_process_get_name(p->index[0]), cnts.counts[0], + per_process_get_name(p->index[1]), cnts.counts[1], + per_process_get_name(p->index[2]), cnts.counts[2], + per_process_get_name(p->index[3]), cnts.counts[3], + cnts.cycles, cnts.swaps); +} + +int per_process_l2_results_read(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct per_process_perf_mon_type *p = + (struct per_process_perf_mon_type *)data; + struct pm_counters_s cnts; + int i, j; + + /* + * Total across all CPUS + */ + memset(&cnts, 0, sizeof(cnts)); + for (i = 0; i < num_possible_cpus(); i++) { + cnts.l2_cycles += p->cnts[i].l2_cycles; + for (j = 0; j < PERF_NUM_MONITORS; j++) + cnts.l2_counts[j] += p->cnts[i].l2_counts[j]; + } + + /* + * Display as single results of the totals calculated above. + * Do we want to display or have option to display individula cores? + */ + return sprintf(page, "pid:%lu l2_one:%s:%llu l2_two:%s:%llu \ + l2_three:%s:%llu \ + l2_four:%s:%llu l2_cycles:%llu\n", + p->pid, + per_process_get_name(p->l2_index[0]), cnts.l2_counts[0], + per_process_get_name(p->l2_index[1]), cnts.l2_counts[1], + per_process_get_name(p->l2_index[2]), cnts.l2_counts[2], + per_process_get_name(p->l2_index[3]), cnts.l2_counts[3], + cnts.l2_cycles); +} + +/* +FUNCTION per_process_results_write + +DESCRIPTION + Allow some control over the results. If the user forgets to autolock or + wants to unlock the results so they will be deleted, then this is + where it is processed. + + For example, to unlock process 23 + echo "unlock" > 23 + +DEPENDENCIES + +RETURN VALUE + Number of characters used (all of them!) + +SIDE EFFECTS +*/ +int per_process_results_write(struct file *file, const char *buff, + unsigned long cnt, void *data) +{ + char *newbuf; + struct per_process_perf_mon_type *p = + (struct per_process_perf_mon_type *)data; + + if (p == 0) + return cnt; + /* + * Alloc the user data in kernel space. and then copy user to kernel + */ + newbuf = kmalloc(cnt + 1, GFP_KERNEL); + if (0 == newbuf) + return cnt; + if (copy_from_user(newbuf, buff, cnt) != 0) { + printk(KERN_INFO "%s copy_from_user failed\n", __func__); + return cnt; + } + + if (0 == strcmp("lock", newbuf)) + p->flags |= PERF_ENTRY_LOCKED; + else if (0 == strcmp("unlock", newbuf)) + p->flags &= ~PERF_ENTRY_LOCKED; + else if (0 == strcmp("auto", newbuf)) + p->flags |= PERF_AUTOLOCK; + else if (0 == strcmp("autoun", newbuf)) + p->flags &= ~PERF_AUTOLOCK; + + return cnt; +} + +/* +FUNCTION perProcessCreateResults + +DESCRIPTION + Create the results /proc file if the system parameters allow it... +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +void per_process_create_results_proc(struct per_process_perf_mon_type *p) +{ + + if (0 == p->pidName) + p->pidName = kmalloc(12, GFP_KERNEL); + if (0 == p->pidName) + return; + sprintf(p->pidName, "%ld", p->pid); + + if (0 == p->proc) { + p->proc = create_proc_entry(p->pidName, 0777, values_dir); + if (0 == p->proc) + return; + } else { + p->proc->name = p->pidName; + } + + p->proc->read_proc = per_process_results_read; + p->proc->write_proc = per_process_results_write; + p->proc->data = (void *)p; +} + +void per_process_create_l2_results_proc(struct per_process_perf_mon_type *p) +{ + + if (0 == p->pidName) + p->pidName = kmalloc(12, GFP_KERNEL); + if (0 == p->pidName) + return; + sprintf(p->pidName, "%ld", p->pid); + + if (0 == p->l2_proc) { + p->l2_proc = create_proc_entry(p->pidName, 0777, + l2_results_dir); + if (0 == p->l2_proc) + return; + } else { + p->l2_proc->name = p->pidName; + } + + p->l2_proc->read_proc = per_process_l2_results_read; + p->l2_proc->write_proc = per_process_results_write; + p->l2_proc->data = (void *)p; +} +/* +FUNCTION per_process_swap_out + +DESCRIPTION + Store the counters from the process that is about to swap out. We take + the old counts and add them to the current counts in the perf registers. + Before the new process is swapped in, the counters are reset. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +typedef void (*vfun)(void *); +void per_process_swap_out(struct per_process_perf_mon_type *data) +{ + int i; + unsigned long overflow; +#ifdef CONFIG_ARCH_MSM8X60 + unsigned long l2_overflow; +#endif + struct per_process_perf_mon_type *p = data; + + MARKPIDS('O', p->pid, 0); + RCP15_PMOVSR(overflow); +#ifdef CONFIG_ARCH_MSM8X60 + RCP15_L2PMOVSR(l2_overflow); +#endif + + if (!pp_enabled) + return; + + /* + * The kernel for some reason (2.6.32.9) starts a process context on + * one core and ends on another. So the swap in and swap out can be + * on different cores. If this happens, we need to stop the + * counters and collect the data on the core that started the counters + * ....otherwise we receive invalid data. So we mark the the core with + * the process as deferred. The next time a process is swapped on + * the core that the process was running on, the counters will be + * updated. + */ + if ((smp_processor_id() != p->running_cpu) && (p->pid != 0)) { + fake_swap_out[p->running_cpu] = 1; + return; + } + + _SWAPS(p)++; + _CYCLES(p) += pm_get_cycle_count(); + + if (overflow & PM_CYCLE_OVERFLOW_MASK) + _CYCLES(p) += 0xFFFFFFFF; + + for (i = 0; i < PERF_NUM_MONITORS; i++) { + _COUNTS(p, i) += pm_get_count(i); + if (overflow & (1 << i)) + _COUNTS(p, i) += 0xFFFFFFFF; + } + +#ifdef CONFIG_ARCH_MSM8X60 + _L2CYCLES(p) += l2_pm_get_cycle_count(); + if (l2_overflow & L2_PM_CYCLE_OVERFLOW_MASK) + _L2CYCLES(p) += 0xFFFFFFFF; + for (i = 0; i < PERF_NUM_MONITORS; i++) { + _L2COUNTS(p, i) += l2_pm_get_count(i); + if (l2_overflow & (1 << i)) + _L2COUNTS(p, i) += 0xFFFFFFFF; + } +#endif +} + +/* +FUNCTION per_process_remove_manual + +DESCRIPTION + Remove an entry from the results directory if the flags allow this. + When not enbled or the entry is locked, the values/results will + not be removed. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +void per_process_remove_manual(unsigned long pid) +{ + struct per_process_perf_mon_type *p = per_process_find(pid); + + /* + * Check all of the flags to see if we can remove this one + * Then mark as not used + */ + if (0 == p) + return; + p->pid = (0xFFFFFFFF); + + /* + * Remove the proc entry. + */ + if (p->proc) + remove_proc_entry(p->pidName, values_dir); + if (p->l2_proc) + remove_proc_entry(p->pidName, l2_results_dir); + kfree(p->pidName); + + /* + * Clear them out...and ensure the pid is invalid + */ + memset(p, 0, sizeof *p); + p->pid = 0xFFFFFFFF; + pm_remove_pid = -1; +} + +/* +* Remove called when a process exits... +*/ +void _per_process_remove(unsigned long pid) {} + +/* +FUNCTION per_process_initialize + +DESCRIPTION +Initialize performance collection information for a new process. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +May create a new proc entry +*/ +void per_process_initialize(struct per_process_perf_mon_type *p, + unsigned long pid) +{ + int i; + + /* + * See if this is the pid we are interested in... + */ + if (pp_settings_valid == -1) + return; + if ((pp_set_pid != pid) && (pp_set_pid != 0)) + return; + + /* + * Clear out the statistics table then insert this pid + * We want to keep the proc entry and the name + */ + p->pid = pid; + + /* + * Create a proc entry for this pid, then get the current event types and + * store in data struct so when the process is switched in we can track + * it. + */ + if (p->proc == 0) { + per_process_create_results_proc(p); +#ifdef CONFIG_ARCH_MSM8X60 + per_process_create_l2_results_proc(p); +#endif + } + _CYCLES(p) = 0; + _L2CYCLES(p) = 0; + _SWAPS(p) = 0; + /* + * Set the per process data struct, but not the monitors until later... + * Init only happens with the user sets the SetPID variable to this pid + * so we can load new values. + */ + for (i = 0; i < PERF_NUM_MONITORS; i++) { + p->index[i] = per_proc_event[i]; +#ifdef CONFIG_ARCH_MSM8X60 + p->l2_index[i] = l2_per_proc_event[i]; +#endif + _COUNTS(p, i) = 0; + _L2COUNTS(p, i) = 0; + } + p->lpm0evtyper = pp_lpm0evtyper; + p->lpm1evtyper = pp_lpm1evtyper; + p->lpm2evtyper = pp_lpm2evtyper; + p->l2lpmevtyper = pp_l2lpmevtyper; + p->vlpmevtyper = pp_vlpmevtyper; + +#ifdef CONFIG_ARCH_MSM8X60 + p->l2pmevtyper0 = pp_l2pmevtyper0; + p->l2pmevtyper1 = pp_l2pmevtyper1; + p->l2pmevtyper2 = pp_l2pmevtyper2; + p->l2pmevtyper3 = pp_l2pmevtyper3; + p->l2pmevtyper4 = pp_l2pmevtyper4; +#endif + + /* + * Reset pid and settings value + */ + pp_set_pid = -1; + pp_settings_valid = -1; +} + +/* +FUNCTION per_process_swap_in + +DESCRIPTION + Called when a context switch is about to start this PID. + We check to see if this process has an entry or not and create one + if not locked... + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +void per_process_swap_in(struct per_process_perf_mon_type *p_new, + unsigned long pid) +{ + int i; + + MARKPIDS('I', p_new->pid, 0); + /* + * If the set proc variable == the current pid then init a new + * entry... + */ + if (pp_set_pid == pid) + per_process_initialize(p_new, pid); + + p_new->running_cpu = smp_processor_id(); + last_in_pid[smp_processor_id()] = pid; + + /* + * setup the monitors for this process. + */ + for (i = 0; i < PERF_NUM_MONITORS; i++) { + pm_set_event(i, p_new->index[i]); +#ifdef CONFIG_ARCH_MSM8X60 + l2_pm_set_event(i, p_new->l2_index[i]); +#endif + } + pm_set_local_iu(p_new->lpm0evtyper); + pm_set_local_xu(p_new->lpm1evtyper); + pm_set_local_su(p_new->lpm2evtyper); + pm_set_local_l2(p_new->l2lpmevtyper); + +#ifdef CONFIG_ARCH_MSM8X60 + pm_set_local_bu(p_new->l2pmevtyper0); + pm_set_local_cb(p_new->l2pmevtyper1); + pm_set_local_mp(p_new->l2pmevtyper2); + pm_set_local_sp(p_new->l2pmevtyper3); + pm_set_local_scu(p_new->l2pmevtyper4); +#endif +} + +/* +FUNCTION perProcessSwitch + +DESCRIPTION + Called during context switch. Updates the counts on the process about to + be swapped out and brings in the counters for the process about to be + swapped in. + + All is dependant on the enabled and lock flags. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ + +DEFINE_SPINLOCK(pm_lock); +void _per_process_switch(unsigned long old_pid, unsigned long new_pid) +{ + struct per_process_perf_mon_type *p_old, *p_new; + + if (pm_global_enable == 0) + return; + + spin_lock(&pm_lock); + + pm_stop_all(); +#ifdef CONFIG_ARCH_MSM8X60 + l2_pm_stop_all(); +#endif + + /* + * We detected that the process was swapped in on one core and out on + * a different core. This does not allow us to stop and stop counters + * properly so we need to defer processing. This checks to see if there + * is any defered processing necessary. And does it... */ + if (fake_swap_out[smp_processor_id()] != 0) { + fake_swap_out[smp_processor_id()] = 0; + p_old = per_process_find(last_in_pid[smp_processor_id()]); + last_in_pid[smp_processor_id()] = 0; + if (p_old != 0) + per_process_swap_out(p_old); + } + + /* + * Clear the data collected so far for this process? + */ + if (pp_clear_pid != -1) { + struct per_process_perf_mon_type *p_clear = + per_process_find(pp_clear_pid); + if (p_clear) { + memset(p_clear->cnts, 0, + sizeof(struct pm_counters_s)*num_possible_cpus()); + printk(KERN_INFO "Clear Per Processor Stats for \ + PID:%ld\n", pp_clear_pid); + pp_clear_pid = -1; + } + } + /* + * Always collect for 0, it collects for all. + */ + if (pp_enabled) { + if (first_switch == 1) { + per_process_initialize(&perf_mons[0], 0); + first_switch = 0; + } + if (pm_global) { + per_process_swap_out(&perf_mons[0]); + per_process_swap_in(&perf_mons[0], 0); + } else { + p_old = per_process_find(old_pid); + p_new = per_process_find(new_pid); + + + /* + * save the old counts to the old data struct, if the + * returned ptr is NULL or the process id passed is not + * the same as the process id in the data struct then + * don't update the data. + */ + if ((p_old) && (p_old->pid == old_pid) && + (p_old->pid != 0)) { + per_process_swap_out(p_old); + } + + /* + * Setup the counters for the new process + */ + if (pp_set_pid == new_pid) + per_process_initialize(p_new, new_pid); + if ((p_new->pid == new_pid) && (new_pid != 0)) + per_process_swap_in(p_new, new_pid); + } + pm_reset_all(); +#ifdef CONFIG_ARCH_MSM8X60 + l2_pm_reset_all(); +#endif +#ifdef CONFIG_ARCH_QSD8X50 + axi_swaps++; + if (axi_swaps%pm_axi_info.refresh == 0) { + if (pm_axi_info.clear == 1) { + pm_axi_clear_cnts(); + pm_axi_info.clear = 0; + } + if (pm_axi_info.enable == 0) + pm_axi_disable(); + else + pm_axi_update_cnts(); + axi_swaps = 0; + } +#endif + } + pm_start_all(); +#ifdef CONFIG_ARCH_MSM8X60 + l2_pm_start_all(); +#endif + + spin_unlock(&pm_lock); +} + +/* +FUNCTION pmInterruptIn + +DESCRIPTION + Called when an interrupt is being processed. If the pmStopForInterrutps + flag is non zero then we disable the counting of performance monitors. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +static int pm_interrupt_nesting_count; +static unsigned long pm_cycle_in, pm_cycle_out; +void _perf_mon_interrupt_in(void) +{ + if (pm_global_enable == 0) + return; + if (pm_stop_for_interrupts == 0) + return; + pm_interrupt_nesting_count++; /* Atomic */ + pm_stop_all(); + pm_cycle_in = pm_get_cycle_count(); +} + +/* +FUNCTION perfMonInterruptOut + +DESCRIPTION + Reenable performance monitor counting whn the nest count goes to zero + provided the counting has been stoped + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +void _perf_mon_interrupt_out(void) +{ + if (pm_global_enable == 0) + return; + if (pm_stop_for_interrupts == 0) + return; + --pm_interrupt_nesting_count; /* Atomic?? */ + + if (pm_interrupt_nesting_count <= 0) { + pm_cycle_out = pm_get_cycle_count(); + if (pm_cycle_in != pm_cycle_out) + printk(KERN_INFO "pmIn!=pmOut in:%lx out:%lx\n", + pm_cycle_in, pm_cycle_out); + if (pp_enabled) { + pm_start_all(); +#ifdef CONFIG_ARCH_MSM8X60 + l2_pm_start_all(); +#endif + } + pm_interrupt_nesting_count = 0; + } +} + +void per_process_do_global(unsigned long g) +{ + pm_global = g; + + if (pm_global == 1) { + pm_stop_all(); +#ifdef CONFIG_ARCH_MSM8X60 + l2_pm_stop_all(); +#endif + pm_reset_all(); +#ifdef CONFIG_ARCH_MSM8X60 + l2_pm_reset_all(); +#endif + pp_set_pid = 0; + per_process_swap_in(&perf_mons[0], 0); + pm_start_all(); +#ifdef CONFIG_ARCH_MSM8X60 + l2_pm_start_all(); +#endif + } else { + pm_stop_all(); +#ifdef CONFIG_ARCH_MSM8X60 + l2_pm_stop_all(); +#endif + } +} + + +/* +FUNCTION per_process_write + +DESCRIPTION + Generic routine to handle any of the settings /proc directory writes. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +int per_process_write(struct file *file, const char *buff, + unsigned long cnt, void *data, const char *fmt) +{ + char *newbuf; + unsigned long *d = (unsigned long *)data; + + /* + * Alloc the user data in kernel space. and then copy user to kernel + */ + newbuf = kmalloc(cnt + 1, GFP_KERNEL); + if (0 == newbuf) + return PM_PP_ERR; + if (copy_from_user(newbuf, buff, cnt) != 0) { + printk(KERN_INFO "%s copy_from_user failed\n", __func__); + return cnt; + } + sscanf(newbuf, fmt, d); + kfree(newbuf); + + /* + * If this is a remove command then do it now... + */ + if (d == &pm_remove_pid) + per_process_remove_manual(*d); + if (d == &pm_global) + per_process_do_global(*d); + return cnt; +} + +int per_process_write_dec(struct file *file, const char *buff, + unsigned long cnt, void *data) +{ + return per_process_write(file, buff, cnt, data, "%ld"); +} + +int per_process_write_hex(struct file *file, const char *buff, + unsigned long cnt, void *data) +{ + return per_process_write(file, buff, cnt, data, "%lx"); +} + +/* +FUNCTION per_process_read + +DESCRIPTION + Generic read handler for the /proc settings directory. + +DEPENDENCIES + +RETURN VALUE + Number of characters to output. + +SIDE EFFECTS +*/ +int per_process_read(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + unsigned long *d = (unsigned long *)data; + return sprintf(page, "%lx", *d); +} + +int per_process_read_decimal(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + unsigned long *d = (unsigned long *)data; + return sprintf(page, "%ld", *d); +} + +/* +FUNCTION per_process_proc_entry + +DESCRIPTION + Create a generic entry for the /proc settings directory. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +void per_process_proc_entry(char *name, unsigned long *var, + struct proc_dir_entry *d, int hex) +{ + struct proc_dir_entry *pe; + + pe = create_proc_entry(name, 0777, d); + if (0 == pe) + return; + if (hex) { + pe->read_proc = per_process_read; + pe->write_proc = per_process_write_hex; + } else { + pe->read_proc = per_process_read_decimal; + pe->write_proc = per_process_write_dec; + } + pe->data = (void *)var; + + if (pp_proc_entry_index >= PP_MAX_PROC_ENTRIES) { + printk(KERN_INFO "PERF: proc entry overflow,\ + memleak on module unload occured"); + return; + } + per_process_proc_names[pp_proc_entry_index++] = name; +} + +static int perfmon_notifier(struct notifier_block *self, unsigned long cmd, + void *v) +{ + static int old_pid = -1; + struct thread_info *thread = v; + int current_pid; + + if (cmd != THREAD_NOTIFY_SWITCH) + return old_pid; + + current_pid = thread->task->pid; + if (old_pid != -1) + _per_process_switch(old_pid, current_pid); + old_pid = current_pid; + return old_pid; +} + +static struct notifier_block perfmon_notifier_block = { + .notifier_call = perfmon_notifier, +}; + +/* +FUNCTION per_process_perf_init + +DESCRIPTION + Initialze the per process performance monitor variables and /proc space. + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +int per_process_perf_init(void) +{ +#ifdef CONFIG_ARCH_MSM8X60 + smp_call_function_single(0, (void *)pm_initialize, (void *)NULL, 1); + smp_call_function_single(1, (void *)pm_initialize, (void *)NULL, 1); + l2_pm_initialize(); +#else + pm_initialize(); +#endif + pm_axi_init(); + pm_axi_clear_cnts(); + proc_dir = proc_mkdir("ppPerf", NULL); + values_dir = proc_mkdir("results", proc_dir); + settings_dir = proc_mkdir("settings", proc_dir); + per_process_proc_entry("enable", &pp_enabled, settings_dir, 1); + per_process_proc_entry("valid", &pp_settings_valid, settings_dir, 1); + per_process_proc_entry("setPID", &pp_set_pid, settings_dir, 0); + per_process_proc_entry("clearPID", &pp_clear_pid, settings_dir, 0); + per_process_proc_entry("event0", &per_proc_event[0], settings_dir, 1); + per_process_proc_entry("event1", &per_proc_event[1], settings_dir, 1); + per_process_proc_entry("event2", &per_proc_event[2], settings_dir, 1); + per_process_proc_entry("event3", &per_proc_event[3], settings_dir, 1); + per_process_proc_entry("l2_event0", &l2_per_proc_event[0], settings_dir, + 1); + per_process_proc_entry("l2_event1", &l2_per_proc_event[1], settings_dir, + 1); + per_process_proc_entry("l2_event2", &l2_per_proc_event[2], settings_dir, + 1); + per_process_proc_entry("l2_event3", &l2_per_proc_event[3], settings_dir, + 1); + per_process_proc_entry("debug", &dbg_flags, settings_dir, 1); + per_process_proc_entry("autolock", &pp_auto_lock, settings_dir, 1); + per_process_proc_entry("lpm0evtyper", &pp_lpm0evtyper, settings_dir, 1); + per_process_proc_entry("lpm1evtyper", &pp_lpm1evtyper, settings_dir, 1); + per_process_proc_entry("lpm2evtyper", &pp_lpm2evtyper, settings_dir, 1); + per_process_proc_entry("l2lpmevtyper", &pp_l2lpmevtyper, settings_dir, + 1); + per_process_proc_entry("vlpmevtyper", &pp_vlpmevtyper, settings_dir, 1); + per_process_proc_entry("l2pmevtyper0", &pp_l2pmevtyper0, settings_dir, + 1); + per_process_proc_entry("l2pmevtyper1", &pp_l2pmevtyper1, settings_dir, + 1); + per_process_proc_entry("l2pmevtyper2", &pp_l2pmevtyper2, settings_dir, + 1); + per_process_proc_entry("l2pmevtyper3", &pp_l2pmevtyper3, settings_dir, + 1); + per_process_proc_entry("l2pmevtyper4", &pp_l2pmevtyper4, settings_dir, + 1); + per_process_proc_entry("stopForInterrupts", &pm_stop_for_interrupts, + settings_dir, 1); + per_process_proc_entry("global", &pm_global, settings_dir, 1); + per_process_proc_entry("globalEnable", &pm_global_enable, settings_dir, + 1); + per_process_proc_entry("removePID", &pm_remove_pid, settings_dir, 0); + + axi_dir = proc_mkdir("axi", proc_dir); + axi_settings_dir = proc_mkdir("settings", axi_dir); + axi_results_dir = proc_mkdir("results", axi_dir); + pm_axi_set_proc_entry("axi_enable", &pm_axi_info.enable, + axi_settings_dir, 1); + pm_axi_set_proc_entry("axi_clear", &pm_axi_info.clear, axi_settings_dir, + 0); + pm_axi_set_proc_entry("axi_valid", &pm_axi_info.valid, axi_settings_dir, + 1); + pm_axi_set_proc_entry("axi_sel_reg0", &pm_axi_info.sel_reg0, + axi_settings_dir, 1); + pm_axi_set_proc_entry("axi_sel_reg1", &pm_axi_info.sel_reg1, + axi_settings_dir, 1); + pm_axi_set_proc_entry("axi_ten_sel", &pm_axi_info.ten_sel_reg, + axi_settings_dir, 1); + pm_axi_set_proc_entry("axi_refresh", &pm_axi_info.refresh, + axi_settings_dir, 1); + pm_axi_get_cnt_proc_entry("axi_cnts", &axi_cnts, axi_results_dir, 0); + l2_dir = proc_mkdir("l2", proc_dir); + l2_results_dir = proc_mkdir("results", l2_dir); + + memset(perf_mons, 0, sizeof(perf_mons)); + per_process_create_results_proc(&perf_mons[0]); + per_process_create_l2_results_proc(&perf_mons[0]); + thread_register_notifier(&perfmon_notifier_block); + /* + * Set the function pointers so the module can be activated. + */ + pp_interrupt_out_ptr = _perf_mon_interrupt_out; + pp_interrupt_in_ptr = _perf_mon_interrupt_in; + pp_process_remove_ptr = _per_process_remove; + pp_loaded = 1; + pm_axi_info.refresh = 1; + +#ifdef CONFIG_ARCH_MSM8X60 + smp_call_function_single(0, (void *)pm_reset_all, (void *)NULL, 1); + smp_call_function_single(1, (void *)pm_reset_all, (void *)NULL, 1); + smp_call_function_single(0, (void *)l2_pm_reset_all, (void *)NULL, 1); + smp_call_function_single(1, (void *)l2_pm_reset_all, (void *)NULL, 1); +#else + pm_reset_all(); +#endif + + return 0; +} + +/* +FUNCTION per_process_perf_exit + +DESCRIPTION + Module exit functionm, clean up, renmove proc entries + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS + No more per process +*/ +void per_process_perf_exit(void) +{ + unsigned long i; + /* + * Sert the function pointers to 0 so the functions will no longer + * be invoked + */ + pp_loaded = 0; + pp_interrupt_out_ptr = 0; + pp_interrupt_in_ptr = 0; + pp_process_remove_ptr = 0; + /* + * Remove the results + */ + for (i = 0; i < PERF_MON_PROCESS_NUM; i++) + per_process_remove_manual(perf_mons[i].pid); + /* + * Remove the proc entries in the settings dir + */ + i = 0; + for (i = 0; i < pp_proc_entry_index; i++) + remove_proc_entry(per_process_proc_names[i], settings_dir); + + /*remove proc axi files*/ + remove_proc_entry("axi_enable", axi_settings_dir); + remove_proc_entry("axi_valid", axi_settings_dir); + remove_proc_entry("axi_refresh", axi_settings_dir); + remove_proc_entry("axi_clear", axi_settings_dir); + remove_proc_entry("axi_sel_reg0", axi_settings_dir); + remove_proc_entry("axi_sel_reg1", axi_settings_dir); + remove_proc_entry("axi_ten_sel", axi_settings_dir); + remove_proc_entry("axi_cnts", axi_results_dir); + /* + * Remove the directories + */ + remove_proc_entry("results", l2_dir); + remove_proc_entry("l2", proc_dir); + remove_proc_entry("results", proc_dir); + remove_proc_entry("settings", proc_dir); + remove_proc_entry("results", axi_dir); + remove_proc_entry("settings", axi_dir); + remove_proc_entry("axi", proc_dir); + remove_proc_entry("ppPerf", NULL); + pm_free_irq(); +#ifdef CONFIG_ARCH_MSM8X60 + l2_pm_free_irq(); +#endif + thread_unregister_notifier(&perfmon_notifier_block); +#ifdef CONFIG_ARCH_MSM8X60 + smp_call_function_single(0, (void *)pm_deinitialize, (void *)NULL, 1); + smp_call_function_single(1, (void *)pm_deinitialize, (void *)NULL, 1); + l2_pm_deinitialize(); +#else + pm_deinitialize(); +#endif +} diff --git a/arch/arm/perfmon/per.c b/arch/arm/perfmon/per.c new file mode 100644 index 0000000000000..4222844f12147 --- /dev/null +++ b/arch/arm/perfmon/per.c @@ -0,0 +1,59 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* +per.c + +DESCRIPTION: Performance count interface for linux via proc in the T32 +command file style +*/ + +#include +#include +#include +#include +#include +#include "linux/proc_fs.h" +#include "linux/kernel_stat.h" +#include "asm/uaccess.h" +#include "cp15_registers.h" +#include "perf.h" + +#define PM_PER_ERR -1 +/* +FUNCTION perf_if_proc_init + +DESCRIPTION Initialize the proc interface for thje performance data. +*/ +static __init int per_init(void) +{ + + if (atomic_read(&pm_op_lock) == 1) { + printk(KERN_INFO "Can not load KSAPI, monitors are in use\n"); + return PM_PER_ERR; + } + atomic_set(&pm_op_lock, 1); + per_process_perf_init(); + printk(KERN_INFO "ksapi init\n"); + return 0; +} + +static void __exit per_exit(void) +{ + per_process_perf_exit(); + printk(KERN_INFO "ksapi exit\n"); + atomic_set(&pm_op_lock, 0); +} + +MODULE_LICENSE("GPL v2"); +module_init(per_init); +module_exit(per_exit); diff --git a/arch/arm/perfmon/perf-function-hooks.c b/arch/arm/perfmon/perf-function-hooks.c new file mode 100644 index 0000000000000..aacc353741c96 --- /dev/null +++ b/arch/arm/perfmon/perf-function-hooks.c @@ -0,0 +1,81 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* +* perf-function-hooks.c +* DESCRIPTION +* Hooks for ksapi.ko +*/ + +#include +#include +#include +#include +#include +#include +#include "linux/proc_fs.h" +#include "linux/kernel_stat.h" +#include "asm/uaccess.h" +#include +#include "cp15_registers.h" +#include +#include "perf.h" + +/* +* Function Pointers for when the module is installed... +* Should we use a single "ready" variable for the testing +* in the functions below, will be safer when module is removed +* testing for a locked variable... +*/ +VPVF pp_interrupt_out_ptr; +VPVF pp_interrupt_in_ptr; +VPULF pp_process_remove_ptr; +unsigned int pp_loaded; +EXPORT_SYMBOL(pp_loaded); +atomic_t pm_op_lock; +EXPORT_SYMBOL(pm_op_lock); + +/* +FUNCTION VARIOUS + +DESCRIPTION +Hooks to callinto the module functions after they are loaded. The +above pointers will be set and then these functions are ready to be +called. + +DEPENDENCIES +THe per preocess performance monitor needs to be loaded ... + +RETURN VALUE + +SIDE EFFECTS +*/ +void perf_mon_interrupt_out(void) +{ + if (pp_loaded) + (*pp_interrupt_out_ptr)(); +} +EXPORT_SYMBOL(pp_interrupt_out_ptr); + +void perf_mon_interrupt_in(void) +{ + if (pp_loaded) + (*pp_interrupt_in_ptr)(); +} +EXPORT_SYMBOL(pp_interrupt_in_ptr); + +void per_process_remove(unsigned long pid) +{ + if (pp_loaded) + (*pp_process_remove_ptr)(pid); +} +EXPORT_SYMBOL(pp_process_remove_ptr); diff --git a/arch/arm/perfmon/perf-smp.c b/arch/arm/perfmon/perf-smp.c new file mode 100644 index 0000000000000..5417fc7f6a5d3 --- /dev/null +++ b/arch/arm/perfmon/perf-smp.c @@ -0,0 +1,751 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* +perf-smp.c +DESCRIPTION +Manipulation, initialization of the ARMV7 Performance counter register. + + +EXTERNALIZED FUNCTIONS + +INITIALIZATION AND SEQUENCING REQUIREMENTS +*/ + +/* +INCLUDE FILES FOR MODULE +*/ +#include +#include +#include +#include +#include + +#include +#include +#include "l2_cp15_registers.h" + +/* +DEFINITIONS AND DECLARATIONS FOR MODULE + +This section contains definitions for constants, macros, types, variables +and other items needed by this module. +*/ + +/* + Constant / Define Declarations +*/ + +#define PM_NUM_COUNTERS 4 +#define L2_PM_ERR -1 + +/*------------------------------------------------------------------------ + * Global control bits +------------------------------------------------------------------------*/ +#define PM_L2_GLOBAL_ENABLE (1<<0) +#define PM_L2_EVENT_RESET (1<<1) +#define PM_L2_CYCLE_RESET (1<<2) +#define PM_L2_CLKDIV (1<<3) +#define PM_L2_GLOBAL_TRACE (1<<4) +#define PM_L2_DISABLE_PROHIBIT (1<<5) + +/*--------------------------------------------------------------------------- + * Enable and clear bits for each event/trigger +----------------------------------------------------------------------------*/ +#define PM_L2EV0_ENABLE (1<<0) +#define PM_L2EV1_ENABLE (1<<1) +#define PM_L2EV2_ENABLE (1<<2) +#define PM_L2EV3_ENABLE (1<<3) +#define PM_L2_COUNT_ENABLE (1<<31) +#define PM_L2_ALL_ENABLE (0x8000000F) + + +/*----------------------------------------------------------------------------- + * Overflow actions +------------------------------------------------------------------------------*/ +#define PM_L2_OVERFLOW_NOACTION (0) +#define PM_L2_OVERFLOW_HALT (1) +#define PM_L2_OVERFLOW_STOP (2) +#define PM_L2_OVERFLOW_SKIP (3) + +/* + * Shifts for each trigger type + */ +#define PM_STOP_SHIFT 24 +#define PM_RELOAD_SHIFT 22 +#define PM_RESUME_SHIFT 20 +#define PM_SUSPEND_SHIFT 18 +#define PM_START_SHIFT 16 +#define PM_STOPALL_SHIFT 15 +#define PM_STOPCOND_SHIFT 12 +#define PM_RELOADCOND_SHIFT 9 +#define PM_RESUMECOND_SHIFT 6 +#define PM_SUSPENDCOND_SHIFT 3 +#define PM_STARTCOND_SHIFT 0 + + +/*--------------------------------------------------------------------------- +External control register. What todo when various events happen. +Triggering events, etc. +----------------------------------------------------------------------------*/ +#define PM_EXTTR0 0 +#define PM_EXTTR1 1 +#define PM_EXTTR2 2 +#define PM_EXTTR3 3 + +#define PM_COND_NO_STOP 0 +#define PM_COND_STOP_CNTOVRFLW 1 +#define PM_COND_STOP_EXTERNAL 4 +#define PM_COND_STOP_TRACE 5 +#define PM_COND_STOP_EVOVRFLW 6 +#define PM_COND_STOP_EVTYPER 7 + +/*-------------------------------------------------------------------------- +Protect against concurrent access. There is an index register that is +used to select the appropriate bank of registers. If multiple processes +are writting this at different times we could have a mess... +---------------------------------------------------------------------------*/ +#define PM_LOCK() +#define PM_UNLOCK() +#define PRINT printk + +/*-------------------------------------------------------------------------- +The Event definitions +--------------------------------------------------------------------------*/ +#define L2PM_EVT_PM0_EVT0 0x00 +#define L2PM_EVT_PM0_EVT1 0x01 +#define L2PM_EVT_PM0_EVT2 0x02 +#define L2PM_EVT_PM0_EVT3 0x03 +#define L2PM_EVT_PM1_EVT0 0x04 +#define L2PM_EVT_PM1_EVT1 0x05 +#define L2PM_EVT_PM1_EVT2 0x06 +#define L2PM_EVT_PM1_EVT3 0x07 +#define L2PM_EVT_PM2_EVT0 0x08 +#define L2PM_EVT_PM2_EVT1 0x09 +#define L2PM_EVT_PM2_EVT2 0x0a +#define L2PM_EVT_PM2_EVT3 0x0b +#define L2PM_EVT_PM3_EVT0 0x0c +#define L2PM_EVT_PM3_EVT1 0x0d +#define L2PM_EVT_PM3_EVT2 0x0e +#define L2PM_EVT_PM3_EVT3 0x0f +#define L2PM_EVT_PM4_EVT0 0x10 +#define L2PM_EVT_PM4_EVT1 0x11 +#define L2PM_EVT_PM4_EVT2 0x12 +#define L2PM_EVT_PM4_EVT3 0x13 + +/* +Type Declarations +*/ + +/* +Local Object Definitions +*/ + +unsigned long l2_pm_cycle_overflow_count; +unsigned long l2_pm_overflow_count[PM_NUM_COUNTERS]; + +/*--------------------------------------------------------------------------- +Max number of events read from the config registers +---------------------------------------------------------------------------*/ +static int pm_l2_max_events; + +static int irqid; + +/* +Function Definitions +*/ + +/* +FUNCTION l2_pm_group_stop + +DESCRIPTION Stop a group of the performance monitors. Event monitor 0 is bit +0, event monitor 1 bit 1, etc. The cycle count can also be disable with +bit 31. Macros are provided for all of the indexes including an ALL. + +DEPENDENCIES + +RETURN VALUE +None + +SIDE EFFECTS +Stops the performance monitoring for the index passed. +*/ +void pm_l2_group_stop(unsigned long mask) +{ + WCP15_L2PMCNTENCLR(mask); +} + +/* +FUNCTION l2_pm_group_start + +DESCRIPTION Start a group of the performance monitors. Event monitor 0 is bit +0, event monitor 1 bit 1, etc. The cycle count can also be enabled with +bit 31. Macros are provided for all of the indexes including an ALL. + +DEPENDENCIES + +RETURN VALUE +None + +SIDE EFFECTS +Starts the performance monitoring for the index passed. +*/ +void pm_l2_group_start(unsigned long mask) +{ + WCP15_L2PMCNTENSET(mask); +} + +/* +FUNCTION l2_pm_get_overflow + +DESCRIPTION Return the overflow condition for the index passed. + +DEPENDENCIES + +RETURN VALUE +0 no overflow +!0 (anything else) overflow; + +SIDE EFFECTS +*/ +unsigned long l2_pm_get_overflow(int index) +{ + unsigned long overflow = 0; + +/* +* Range check +*/ + if (index > pm_l2_max_events) + return L2_PM_ERR; + RCP15_L2PMOVSR(overflow); + + return overflow & (1< pm_l2_max_events) + return; + WCP15_L2PMCNTENCLR(1< pm_l2_max_events) + return; + WCP15_L2PMCNTENSET(1< pm_l2_max_events) + return L2_PM_ERR; + +/* +* Lock, select the index and read the count...unlock +*/ + PM_LOCK(); + WCP15_L2PMSELR(index); + WCP15_L2PMXEVCNTR(new_value); + PM_UNLOCK(); + return reg; +} + +int l2_pm_reset_count(int index) +{ + return l2_pm_set_count(index, 0); +} + +/* +FUNCTION l2_pm_get_count + +DESCRIPTION Return the number of events that have happened for the index +passed. + +DEPENDENCIES + +RETURN VALUE +-1 if the index is out of range +The number of events if inrange + +SIDE EFFECTS +*/ +unsigned long l2_pm_get_count(int index) +{ + unsigned long reg = 0; + +/* +* Range check +*/ + if (index > pm_l2_max_events) + return L2_PM_ERR; + +/* +* Lock, select the index and read the count...unlock +*/ + PM_LOCK(); + WCP15_L2PMSELR(index); + RCP15_L2PMXEVCNTR(reg); + PM_UNLOCK(); + return reg; +} + +unsigned long get_filter_code(unsigned long event) +{ + if (event == 0x0 || event == 0x4 || event == 0x08 + || event == 0x0c || event == 0x10) + return 0x0001003f; + else if (event == 0x1 || event == 0x5 || event == 0x09 + || event == 0x0d || event == 0x11) + return 0x0002003f; + else if (event == 0x2 || event == 0x6 || event == 0x0a + || event == 0x0e || event == 0x12) + return 0x0004003f; + else if (event == 0x3 || event == 0x7 || event == 0x0b + || event == 0x0f || event == 0x13) + return 0x0008003f; + else + return 0; +} + +int l2_pm_set_event(int index, unsigned long event) +{ + unsigned long reg = 0; + + /* + * Range check + */ + if (index > pm_l2_max_events) + return L2_PM_ERR; + + /* + * Lock, select the index and read the count...unlock + */ + PM_LOCK(); + WCP15_L2PMSELR(index); + WCP15_L2PMXEVTYPER(event); + /* WCP15_L2PMXEVFILTER(get_filter_code(event)); */ + WCP15_L2PMXEVFILTER(0x000f003f); + PM_UNLOCK(); + return reg; +} + +/* +FUNCTION pm_set_local_bu + +DESCRIPTION Set the local BU triggers. Note that the MSB determines if + these are enabled or not. + +DEPENDENCIES + +RETURN VALUE + NONE + +SIDE EFFECTS +*/ +void pm_set_local_bu(unsigned long value) +{ + WCP15_L2PMEVTYPER0(value); +} + +/* +FUNCTION pm_set_local_cb + +DESCRIPTION Set the local CB triggers. Note that the MSB determines if + these are enabled or not. + +DEPENDENCIES + +RETURN VALUE + NONE + +SIDE EFFECTS +*/ +void pm_set_local_cb(unsigned long value) +{ + WCP15_L2PMEVTYPER1(value); +} + +/* +FUNCTION pm_set_local_mp + +DESCRIPTION Set the local MP triggers. Note that the MSB determines if + these are enabled or not. + +DEPENDENCIES + +RETURN VALUE + NONE + +SIDE EFFECTS +*/ +void pm_set_local_mp(unsigned long value) +{ + WCP15_L2PMEVTYPER2(value); +} + +/* +FUNCTION pm_set_local_sp + +DESCRIPTION Set the local SP triggers. Note that the MSB determines if + these are enabled or not. + +DEPENDENCIES + +RETURN VALUE + NONE + +SIDE EFFECTS +*/ +void pm_set_local_sp(unsigned long value) +{ + WCP15_L2PMEVTYPER3(value); +} + +/* +FUNCTION pm_set_local_scu + +DESCRIPTION Set the local SCU triggers. Note that the MSB determines if + these are enabled or not. + +DEPENDENCIES + +RETURN VALUE + NONE + +SIDE EFFECTS +*/ +void pm_set_local_scu(unsigned long value) +{ + WCP15_L2PMEVTYPER4(value); +} + +/* +FUNCTION l2_pm_isr + +DESCRIPTION: + Performance Monitor interrupt service routine to capture overflows + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +static irqreturn_t l2_pm_isr(int irq, void *d) +{ + int i; + + for (i = 0; i < PM_NUM_COUNTERS; i++) { + if (l2_pm_get_overflow(i)) { + l2_pm_overflow_count[i]++; + l2_pm_reset_overflow(i); + } + } + + if (l2_pm_get_cycle_overflow()) { + l2_pm_cycle_overflow_count++; + l2_pm_reset_cycle_overflow(); + } + + return IRQ_HANDLED; +} + + +void l2_pm_stop_all(void) +{ + WCP15_L2PMCNTENCLR(0xFFFFFFFF); +} + +void l2_pm_reset_all(void) +{ + WCP15_L2PMCR(0xF); + WCP15_L2PMOVSR(PM_L2_ALL_ENABLE); /* overflow clear */ +} + +void l2_pm_start_all(void) +{ + WCP15_L2PMCNTENSET(PM_L2_ALL_ENABLE); +} + +/* +FUNCTION l2_pm_initialize + +DESCRIPTION Initialize the performanca monitoring for the v7 processor. + Ensures the cycle count is running and the event counters are enabled. + +DEPENDENCIES + +RETURN VALUE + NONE + +SIDE EFFECTS +*/ +void l2_pm_initialize(void) +{ + unsigned long reg = 0; + unsigned char imp; + unsigned char id; + unsigned char num; + unsigned long enables = 0; + static int initialized; + + if (initialized) + return; + initialized = 1; + + irqid = SC_SICL2PERFMONIRPTREQ; + RCP15_L2PMCR(reg); + imp = (reg>>24) & 0xFF; + id = (reg>>16) & 0xFF; + pm_l2_max_events = num = (reg>>11) & 0xFF; + PRINT("V7 MP L2SCU Performance Monitor Capabilities\n"); + PRINT(" Implementor %c(%d)\n", imp, imp); + PRINT(" Id %d %x\n", id, id); + PRINT(" Num Events %d %x\n", num, num); + PRINT("\nCycle counter enabled by default...\n"); + + /* + * Global enable, ensure the global enable is set so all + * subsequent actions take effect. Also resets the counts + */ + RCP15_L2PMCR(enables); + WCP15_L2PMCR(enables | PM_L2_GLOBAL_ENABLE | PM_L2_EVENT_RESET | + PM_L2_CYCLE_RESET | PM_L2_CLKDIV); + + /* + * Enable access from user space + */ + + /* + * Install interrupt handler and the enable the interrupts + */ + l2_pm_reset_cycle_overflow(); + l2_pm_reset_overflow(0); + l2_pm_reset_overflow(1); + l2_pm_reset_overflow(2); + l2_pm_reset_overflow(3); + l2_pm_reset_overflow(4); + + if (0 != request_irq(irqid, l2_pm_isr, 0, "l2perfmon", 0)) + printk(KERN_ERR "%s:%d request_irq returned error\n", + __FILE__, __LINE__); + WCP15_L2PMINTENSET(PM_L2_ALL_ENABLE); + /* + * Enable the cycle counter. Default, count 1:1 no divisor. + */ + l2_pm_enable_cycle_counter(); + +} + +void l2_pm_free_irq(void) +{ + free_irq(irqid, 0); +} + +void l2_pm_deinitialize(void) +{ + unsigned long enables = 0; + RCP15_L2PMCR(enables); + WCP15_L2PMCR(enables & ~PM_L2_GLOBAL_ENABLE); +} + diff --git a/arch/arm/perfmon/perf-v7.c b/arch/arm/perfmon/perf-v7.c new file mode 100644 index 0000000000000..614eedc92181a --- /dev/null +++ b/arch/arm/perfmon/perf-v7.c @@ -0,0 +1,1009 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* +perf-v7.c +DESCRIPTION +Manipulation, initialization of the ARMV7 Performance counter register. + + +EXTERNALIZED FUNCTIONS + +INITIALIZATION AND SEQUENCING REQUIREMENTS +*/ + +/* +INCLUDE FILES FOR MODULE +*/ +#include +#include +#include +#include +#include + +#include +#include +#include "cp15_registers.h" + +/* +DEFINITIONS AND DECLARATIONS FOR MODULE + +This section contains definitions for constants, macros, types, variables +and other items needed by this module. +*/ + +/* + Constant / Define Declarations +*/ + +#define PM_NUM_COUNTERS 4 +#define PM_V7_ERR -1 + +/*------------------------------------------------------------------------ + * Global control bits +------------------------------------------------------------------------*/ +#define PM_GLOBAL_ENABLE (1<<0) +#define PM_EVENT_RESET (1<<1) +#define PM_CYCLE_RESET (1<<2) +#define PM_CLKDIV (1<<3) +#define PM_GLOBAL_TRACE (1<<4) +#define PM_DISABLE_PROHIBIT (1<<5) + +/*--------------------------------------------------------------------------- + * Enable and clear bits for each event/trigger +----------------------------------------------------------------------------*/ +#define PM_EV0_ENABLE (1<<0) +#define PM_EV1_ENABLE (1<<1) +#define PM_EV2_ENABLE (1<<2) +#define PM_EV3_ENABLE (1<<3) +#define PM_COUNT_ENABLE (1<<31) +#define PM_ALL_ENABLE (0x8000000F) + + +/*----------------------------------------------------------------------------- + * Overflow actions +------------------------------------------------------------------------------*/ +#define PM_OVERFLOW_NOACTION (0) +#define PM_OVERFLOW_HALT (1) +#define PM_OVERFLOW_STOP (2) +#define PM_OVERFLOW_SKIP (3) + +/* + * Shifts for each trigger type + */ +#define PM_STOP_SHIFT 24 +#define PM_RELOAD_SHIFT 22 +#define PM_RESUME_SHIFT 20 +#define PM_SUSPEND_SHIFT 18 +#define PM_START_SHIFT 16 +#define PM_STOPALL_SHIFT 15 +#define PM_STOPCOND_SHIFT 12 +#define PM_RELOADCOND_SHIFT 9 +#define PM_RESUMECOND_SHIFT 6 +#define PM_SUSPENDCOND_SHIFT 3 +#define PM_STARTCOND_SHIFT 0 + + +/*--------------------------------------------------------------------------- +External control register. What todo when various events happen. +Triggering events, etc. +----------------------------------------------------------------------------*/ +#define PM_EXTTR0 0 +#define PM_EXTTR1 1 +#define PM_EXTTR2 2 +#define PM_EXTTR3 3 + +#define PM_COND_NO_STOP 0 +#define PM_COND_STOP_CNTOVRFLW 1 +#define PM_COND_STOP_EXTERNAL 4 +#define PM_COND_STOP_TRACE 5 +#define PM_COND_STOP_EVOVRFLW 6 +#define PM_COND_STOP_EVTYPER 7 + +/*-------------------------------------------------------------------------- +Protect against concurrent access. There is an index register that is +used to select the appropriate bank of registers. If multiple processes +are writting this at different times we could have a mess... +---------------------------------------------------------------------------*/ +#define PM_LOCK() +#define PM_UNLOCK() +#define PRINT printk + +/*-------------------------------------------------------------------------- +The Event definitions +--------------------------------------------------------------------------*/ +#define PM_EVT_SW_INCREMENT 0 +#define PM_EVT_L1_I_MISS 1 +#define PM_EVT_ITLB_MISS 2 +#define PM_EVT_L1_D_MISS 3 +#define PM_EVT_L1_D_ACCESS 4 +#define PM_EVT_DTLB_MISS 5 +#define PM_EVT_DATA_READ 6 +#define PM_EVT_DATA_WRITE 7 +#define PM_EVT_INSTRUCTION 8 +#define PM_EVT_EXCEPTIONS 9 +#define PM_EVT_EXCEPTION_RET 10 +#define PM_EVT_CTX_CHANGE 11 +#define PM_EVT_PC_CHANGE 12 +#define PM_EVT_BRANCH 13 +#define PM_EVT_RETURN 14 +#define PM_EVT_UNALIGNED 15 +#define PM_EVT_BRANCH_MISS 16 +#define PM_EVT_EXTERNAL0 0x40 +#define PM_EVT_EXTERNAL1 0x41 +#define PM_EVT_EXTERNAL2 0x42 +#define PM_EVT_EXTERNAL3 0x43 +#define PM_EVT_TRACE0 0x44 +#define PM_EVT_TRACE1 0x45 +#define PM_EVT_TRACE2 0x46 +#define PM_EVT_TRACE3 0x47 +#define PM_EVT_PM0 0x48 +#define PM_EVT_PM1 0x49 +#define PM_EVT_PM2 0x4a +#define PM_EVT_PM3 0x4b +#define PM_EVT_LPM0_EVT0 0x4c +#define PM_EVT_LPM0_EVT1 0x4d +#define PM_EVT_LPM0_EVT2 0x4e +#define PM_EVT_LPM0_EVT3 0x4f +#define PM_EVT_LPM1_EVT0 0x50 +#define PM_EVT_LPM1_EVT1 0x51 +#define PM_EVT_LPM1_EVT2 0x52 +#define PM_EVT_LPM1_EVT3 0x53 +#define PM_EVT_LPM2_EVT0 0x54 +#define PM_EVT_LPM2_EVT1 0x55 +#define PM_EVT_LPM2_EVT2 0x56 +#define PM_EVT_LPM2_EVT3 0x57 +#define PM_EVT_L2_EVT0 0x58 +#define PM_EVT_L2_EVT1 0x59 +#define PM_EVT_L2_EVT2 0x5a +#define PM_EVT_L2_EVT3 0x5b +#define PM_EVT_VLP_EVT0 0x5c +#define PM_EVT_VLP_EVT1 0x5d +#define PM_EVT_VLP_EVT2 0x5e +#define PM_EVT_VLP_EVT3 0x5f + +/* +Type Declarations +*/ + +/*-------------------------------------------------------------------------- +A performance monitor trigger setup/initialization structure. Contains +all of the fields necessary to setup a complex trigger with the internal +performance monitor. +---------------------------------------------------------------------------*/ +struct pm_trigger_s { + int index; + int event_type; + bool interrupt; + bool overflow_enable; + bool event_export; + unsigned char overflow_action; + unsigned char stop_index; + unsigned char reload_index; + unsigned char resume_index; + unsigned char suspend_index; + unsigned char start_index; + bool overflow_stop; + unsigned char stop_condition; + unsigned char reload_condition; + unsigned char resume_condition; + unsigned char suspend_condition; + unsigned char start_condition; +}; + +/* +* Name and index place holder so we can display the event +*/ +struct pm_name_s { + unsigned long index; + char *name; +}; + +/* +Local Object Definitions +*/ + +unsigned long pm_cycle_overflow_count; +unsigned long pm_overflow_count[PM_NUM_COUNTERS]; + +/*--------------------------------------------------------------------------- +Max number of events read from the config registers +---------------------------------------------------------------------------*/ +static int pm_max_events; + +/*-------------------------------------------------------------------------- +Storage area for each of the triggers +*---------------------------------------------------------------------------*/ +static struct pm_trigger_s pm_triggers[4]; + +/*-------------------------------------------------------------------------- +Names and indexes of the events +--------------------------------------------------------------------------*/ +static struct pm_name_s pm_names[] = { + { PM_EVT_SW_INCREMENT, "SW Increment"}, + { PM_EVT_L1_I_MISS, "L1 I MISS"}, + { PM_EVT_ITLB_MISS, "L1 ITLB MISS"}, + { PM_EVT_L1_D_MISS, "L1 D MISS"}, + { PM_EVT_L1_D_ACCESS, "L1 D ACCESS"}, + { PM_EVT_DTLB_MISS, "DTLB MISS"}, + { PM_EVT_DATA_READ, "DATA READ"}, + { PM_EVT_DATA_WRITE, "DATA WRITE"}, + { PM_EVT_INSTRUCTION, "INSTRUCTIONS"}, + { PM_EVT_EXCEPTIONS, "EXCEPTIONS"}, + { PM_EVT_EXCEPTION_RET, "EXCEPTION RETURN"}, + { PM_EVT_CTX_CHANGE, "CTX CHANGE"}, + { PM_EVT_PC_CHANGE, "PC CHANGE"}, + { PM_EVT_BRANCH, "BRANCH"}, + { PM_EVT_RETURN, "RETURN"}, + { PM_EVT_UNALIGNED, "UNALIGNED"}, + { PM_EVT_BRANCH_MISS, "BRANCH MISS"}, + { PM_EVT_EXTERNAL0, "EXTERNAL 0"}, + { PM_EVT_EXTERNAL1, "EXTERNAL 1"}, + { PM_EVT_EXTERNAL2, "EXTERNAL 2"}, + { PM_EVT_EXTERNAL3, "EXTERNAL 3"}, + { PM_EVT_TRACE0, "TRACE 0"}, + { PM_EVT_TRACE1, "TRACE 1"}, + { PM_EVT_TRACE2, "TRACE 2"}, + { PM_EVT_TRACE3, "TRACE 3"}, + { PM_EVT_PM0, "PM0"}, + { PM_EVT_PM1, "PM1"}, + { PM_EVT_PM2, "PM2"}, + { PM_EVT_PM3, "PM3"}, + { PM_EVT_LPM0_EVT0, "LPM0 E0"}, + { PM_EVT_LPM0_EVT1, "LPM0 E1"}, + { PM_EVT_LPM0_EVT2 , "LPM0 E2"}, + { PM_EVT_LPM0_EVT3, "LPM0 E3"}, + { PM_EVT_LPM1_EVT0, "LPM1 E0"}, + { PM_EVT_LPM1_EVT1, "LPM1 E1"}, + { PM_EVT_LPM1_EVT2, "LPM1 E2"}, + { PM_EVT_LPM1_EVT3, "LPM1 E3"}, + { PM_EVT_LPM2_EVT0, "LPM2 E0"}, + { PM_EVT_LPM2_EVT1 , "LPM2 E1"}, + { PM_EVT_LPM2_EVT2, "LPM2 E2"}, + { PM_EVT_LPM2_EVT3, "LPM2 E3"}, + { PM_EVT_L2_EVT0 , "L2 E0"}, + { PM_EVT_L2_EVT1, "L2 E1"}, + { PM_EVT_L2_EVT2, "L2 E2"}, + { PM_EVT_L2_EVT3 , "L2 E3"}, + { PM_EVT_VLP_EVT0 , "VLP E0"}, + { PM_EVT_VLP_EVT1, "VLP E1"}, + { PM_EVT_VLP_EVT2, "VLP E2"}, + { PM_EVT_VLP_EVT3, "VLP E3"}, +}; + +static int irqid; + +/* +Function Definitions +*/ + +/* +FUNCTION pm_find_event_name + +DESCRIPTION Find the name associated with the event index passed and return +the pointer. + +DEPENDENCIES + +RETURN VALUE +Pointer to text string containing the name of the event or pointer to +an error string. Either way access to the returned string will not +cause an access error. + +SIDE EFFECTS +*/ +char *pm_find_event_name(unsigned long index) +{ + unsigned long i = 0; + + while (pm_names[i].index != -1) { + if (pm_names[i].index == index) + return pm_names[i].name; + i++; + } + return "BAD INDEX"; +} + +/* +FUNCTION pm_group_stop + +DESCRIPTION Stop a group of the performance monitors. Event monitor 0 is bit +0, event monitor 1 bit 1, etc. The cycle count can also be disabled with +bit 31. Macros are provided for all of the indexes including an ALL. + +DEPENDENCIES + +RETURN VALUE +None + +SIDE EFFECTS +Stops the performance monitoring for the index passed. +*/ +void pm_group_stop(unsigned long mask) +{ + WCP15_PMCNTENCLR(mask); +} + +/* +FUNCTION pm_group_start + +DESCRIPTION Start a group of the performance monitors. Event monitor 0 is bit +0, event monitor 1 bit 1, etc. The cycle count can also be enabled with +bit 31. Macros are provided for all of the indexes including an ALL. + +DEPENDENCIES + +RETURN VALUE +None + +SIDE EFFECTS +Starts the performance monitoring for the index passed. +*/ +void pm_group_start(unsigned long mask) +{ + WCP15_PMCNTENSET(mask); +} + +/* +FUNCTION pm_cycle_overflow_action + +DESCRIPTION Action to take for an overflow of the cycle counter. + +DEPENDENCIES + +RETURN VALUE +None + +SIDE EFFECTS +Modify the state actions for overflow +*/ +void pm_cycle_overflow_action(int action) +{ + unsigned long reg = 0; + + if ((action > PM_OVERFLOW_SKIP) || (action < 0)) + return; + + RCP15_PMACTLR(reg); + reg &= ~(1<<30); /*clear it*/ + WCP15_PMACTLR(reg | (action<<30)); +} + +/* +FUNCTION pm_get_overflow + +DESCRIPTION Return the overflow condition for the index passed. + +DEPENDENCIES + +RETURN VALUE +0 no overflow +!0 (anything else) overflow; + +SIDE EFFECTS +*/ +unsigned long pm_get_overflow(int index) +{ + unsigned long overflow = 0; + +/* +* Range check +*/ + if (index > pm_max_events) + return PM_V7_ERR; + RCP15_PMOVSR(overflow); + + return overflow & (1< pm_max_events) + return; + WCP15_PMCNTENCLR(1< pm_max_events) + return; + WCP15_PMCNTENSET(1< pm_max_events) + return PM_V7_ERR; + +/* +* Lock, select the index and read the count...unlock +*/ + PM_LOCK(); + WCP15_PMSELR(index); + WCP15_PMXEVCNTR(new_value); + PM_UNLOCK(); + return reg; +} + +int pm_reset_count(int index) +{ + return pm_set_count(index, 0); +} + +/* +FUNCTION pm_get_count + +DESCRIPTION Return the number of events that have happened for the index +passed. + +DEPENDENCIES + +RETURN VALUE +-1 if the index is out of range +The number of events if inrange + +SIDE EFFECTS +*/ +unsigned long pm_get_count(int index) +{ + unsigned long reg = 0; + +/* +* Range check +*/ + if (index > pm_max_events) + return PM_V7_ERR; + +/* +* Lock, select the index and read the count...unlock +*/ + PM_LOCK(); + WCP15_PMSELR(index); + RCP15_PMXEVCNTR(reg); + PM_UNLOCK(); + return reg; +} + +/* +FUNCTION pm_show_event_info + +DESCRIPTION Display (print) the information about the event at the index +passed. Shows the index, name and count if a valid index is passed. If +the index is not valid, then nothing is displayed. + +DEPENDENCIES + +RETURN VALUE +None + +SIDE EFFECTS +*/ +void pm_show_event_info(unsigned long index) +{ + unsigned long count; + unsigned long event_type; + + if (index > pm_max_events) + return; + if (pm_triggers[index].index > pm_max_events) + return; + + count = pm_get_count(index); + event_type = pm_triggers[index].event_type; + + PRINT("Event %ld Trigger %s(%ld) count:%ld\n", index, + pm_find_event_name(event_type), event_type, count); +} + +/* +FUNCTION pm_event_init + +DESCRIPTION Given the struct pm_trigger_s info passed, configure the event. +This can be a complex trigger or a simple trigger. Any old values in the +event are lost. + +DEPENDENCIES + +RETURN VALUE +status + +SIDE EFFECTS +stops and clears the event at the index passed. +*/ +int pm_event_init(struct pm_trigger_s *data) +{ + unsigned long trigger; + unsigned long actlr = 0; + + if (0 == data) + return PM_V7_ERR; + if (data->index > pm_max_events) + return PM_V7_ERR; + + /* + * Setup the trigger based ont he passed values + */ + trigger = ((data->overflow_enable&1)<<31) | + ((data->event_export&1)<<30) | + ((data->stop_index&3)<reload_index&3)<resume_index&3)<suspend_index&3)<start_index&3)<overflow_stop&1)<stop_condition&7)<reload_condition&7)<resume_condition&7)<suspend_condition&7)<start_condition&7)<index); + + /* + * Lock, select the bank, set the trigger event and the event type + * then unlock. + */ + PM_LOCK(); + RCP15_PMACTLR(actlr); + actlr &= ~(3<<(data->index<<1)); + WCP15_PMACTLR(actlr | ((data->overflow_action&3) << (data->index<<1))); + WCP15_PMSELR(data->index); + WCP15_PMXEVTYPER(data->event_type); + WCP15_PMXEVCNTCR(trigger); + PM_UNLOCK(); + + /* + * Make a copy of the trigger so we know what it is when/if it triggers. + */ + memcpy(&pm_triggers[data->index], data, sizeof(*data)); + + /* + * We do not re-enable this here so events can be started together with + * pm_group_start() that way an accurate measure can be taken... + */ + + return 0; +} + +int pm_set_event(int index, unsigned long event) +{ + unsigned long reg = 0; + + /* + * Range check + */ + if (index > pm_max_events) + return PM_V7_ERR; + + /* + * Lock, select the index and read the count...unlock + */ + PM_LOCK(); + WCP15_PMSELR(index); + WCP15_PMXEVTYPER(event); + PM_UNLOCK(); + return reg; +} + +/* +FUNCTION pm_set_local_iu + +DESCRIPTION Set the local IU triggers. Note that the MSB determines if + these are enabled or not. + +DEPENDENCIES + +RETURN VALUE + NONE + +SIDE EFFECTS +*/ +void pm_set_local_iu(unsigned long value) +{ + WCP15_LPM0EVTYPER(value); +} + +/* +FUNCTION pm_set_local_iu + +DESCRIPTION Set the local IU triggers. Note that the MSB determines if + these are enabled or not. + +DEPENDENCIES + +RETURN VALUE + NONE + +SIDE EFFECTS +*/ +void pm_set_local_xu(unsigned long value) +{ + WCP15_LPM1EVTYPER(value); +} + +/* +FUNCTION pm_set_local_su + +DESCRIPTION Set the local SU triggers. Note that the MSB determines if + these are enabled or not. + +DEPENDENCIES + +RETURN VALUE + NONE + +SIDE EFFECTS +*/ +void pm_set_local_su(unsigned long value) +{ + WCP15_LPM2EVTYPER(value); +} + +/* +FUNCTION pm_set_local_l2 + +DESCRIPTION Set the local L2 triggers. Note that the MSB determines if + these are enabled or not. + +DEPENDENCIES + +RETURN VALUE + NONE + +SIDE EFFECTS +*/ +void pm_set_local_l2(unsigned long value) +{ + WCP15_L2LPMEVTYPER(value); +} + +/* +FUNCTION pm_set_local_vu + +DESCRIPTION Set the local VU triggers. Note that the MSB determines if + these are enabled or not. + +DEPENDENCIES + +RETURN VALUE + NONE + +SIDE EFFECTS +*/ +void pm_set_local_vu(unsigned long value) +{ + WCP15_VLPMEVTYPER(value); +} + +/* +FUNCTION pm_isr + +DESCRIPTION: + Performance Monitor interrupt service routine to capture overflows + +DEPENDENCIES + +RETURN VALUE + +SIDE EFFECTS +*/ +static irqreturn_t pm_isr(int irq, void *d) +{ + int i; + + for (i = 0; i < PM_NUM_COUNTERS; i++) { + if (pm_get_overflow(i)) { + pm_overflow_count[i]++; + pm_reset_overflow(i); + } + } + + if (pm_get_cycle_overflow()) { + pm_cycle_overflow_count++; + pm_reset_cycle_overflow(); + } + + return IRQ_HANDLED; +} + + +void pm_stop_all(void) +{ + WCP15_PMCNTENCLR(0xFFFFFFFF); +} + +void pm_reset_all(void) +{ + WCP15_PMCR(0xF); + WCP15_PMOVSR(PM_ALL_ENABLE); /* overflow clear */ +} + +void pm_start_all(void) +{ + WCP15_PMCNTENSET(PM_ALL_ENABLE); +} + +/* +FUNCTION pm_initialize + +DESCRIPTION Initialize the performanca monitoring for the v7 processor. + Ensures the cycle count is running and the event counters are enabled. + +DEPENDENCIES + +RETURN VALUE + NONE + +SIDE EFFECTS +*/ +void pm_initialize(void) +{ + unsigned long reg = 0; + unsigned char imp; + unsigned char id; + unsigned char num; + unsigned long enables = 0; + static int initialized; + + if (initialized) + return; + initialized = 1; + + irqid = INT_ARMQC_PERFMON; + RCP15_PMCR(reg); + imp = (reg>>24) & 0xFF; + id = (reg>>16) & 0xFF; + pm_max_events = num = (reg>>11) & 0xFF; + PRINT("V7Performance Monitor Capabilities\n"); + PRINT(" Implementor %c(%d)\n", imp, imp); + PRINT(" Id %d %x\n", id, id); + PRINT(" Num Events %d %x\n", num, num); + PRINT("\nCycle counter enabled by default...\n"); + + /* + * Global enable, ensure the global enable is set so all + * subsequent actions take effect. Also resets the counts + */ + RCP15_PMCR(enables); + WCP15_PMCR(enables | PM_GLOBAL_ENABLE | PM_EVENT_RESET | + PM_CYCLE_RESET | PM_CLKDIV); + + /* + * Enable access from user space + */ + WCP15_PMUSERENR(1); + WCP15_PMACTLR(1); + + /* + * Install interrupt handler and the enable the interrupts + */ + pm_reset_cycle_overflow(); + pm_reset_overflow(0); + pm_reset_overflow(1); + pm_reset_overflow(2); + pm_reset_overflow(3); + + if (0 != request_irq(irqid, pm_isr, 0, "perfmon", 0)) + printk(KERN_ERR "%s:%d request_irq returned error\n", + __FILE__, __LINE__); + WCP15_PMINTENSET(PM_ALL_ENABLE); + /* + * Enable the cycle counter. Default, count 1:1 no divisor. + */ + pm_enable_cycle_counter(); + +} + +void pm_free_irq(void) +{ + free_irq(irqid, 0); +} + +void pm_deinitialize(void) +{ + unsigned long enables = 0; + RCP15_PMCR(enables); + WCP15_PMCR(enables & ~PM_GLOBAL_ENABLE); +} diff --git a/arch/arm/perfmon/perf.h b/arch/arm/perfmon/perf.h new file mode 100644 index 0000000000000..21ee719b2164a --- /dev/null +++ b/arch/arm/perfmon/perf.h @@ -0,0 +1,101 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* +perf.h + +DESCRIPTION: Reads and writes the performance monitoring registers in the ARM +by using the MRC and MCR instructions. +*/ +#ifndef PERF_H +#define PERF_H +extern unsigned long perf_get_cycles(void); +extern void perf_set_count1(unsigned long val); +extern void perf_set_count0(unsigned long val); +extern unsigned long perf_get_count1(void); +extern unsigned long perf_get_count0(void); +extern unsigned long perf_get_ctrl(void); +extern void perf_set_ctrl(void); +extern void perf_set_ctrl_with(unsigned long v); +extern void perf_enable_counting(void); +extern void perf_disable_counting(void); +extern void perf_set_divider(int d); +extern unsigned long perf_get_overflow(void); +extern void perf_clear_overflow(unsigned long bit); +extern void perf_export_event(unsigned long bit); +extern void perf_reset_counts(void); +extern int perf_set_event(unsigned long index, unsigned long val); +extern unsigned long perf_get_count(unsigned long index); +extern void perf_set_cycles(unsigned long c); + +extern void pm_stop_all(void); +extern void l2_pm_stop_all(void); +extern void pm_start_all(void); +extern void l2_pm_start_all(void); +extern void pm_reset_all(void); +extern void l2_pm_reset_all(void); +extern void pm_set_event(unsigned long monitorIndex, unsigned long eventIndex); +extern void l2_pm_set_event(unsigned long monitorIndex, + unsigned long eventIndex); +extern unsigned long pm_get_count(unsigned long monitorIndex); +extern unsigned long l2_pm_get_count(unsigned long monitorIndex); +extern unsigned long pm_get_cycle_count(void); +extern unsigned long l2_pm_get_cycle_count(void); +extern char *pm_find_event_name(unsigned long index); +extern void pm_set_local_iu(unsigned long events); +extern void pm_set_local_xu(unsigned long events); +extern void pm_set_local_su(unsigned long events); +extern void pm_set_local_l2(unsigned long events); +extern void pm_set_local_vu(unsigned long events); +extern void pm_set_local_bu(unsigned long events); +extern void pm_set_local_cb(unsigned long events); +extern void pm_set_local_mp(unsigned long events); +extern void pm_set_local_sp(unsigned long events); +extern void pm_set_local_scu(unsigned long events); +extern void pm_initialize(void); +extern void pm_deinitialize(void); +extern void l2_pm_initialize(void); +extern void l2_pm_deinitialize(void); +extern void pm_free_irq(void); +extern void l2_pm_free_irq(void); + +extern int per_process_perf_init(void); +extern void per_process_perf_exit(void); +int per_process_read(char *page, char **start, off_t off, int count, + int *eof, void *data); +int per_process_write_hex(struct file *file, const char *buff, + unsigned long cnt, void *data); +int per_process_read_decimal(char *page, char **start, off_t off, int count, + int *eof, void *data); +int per_process_write_dec(struct file *file, const char *buff, + unsigned long cnt, void *data); +void perfmon_register_callback(void); +void _per_process_switch(unsigned long oldPid, unsigned long newPid); +extern unsigned int pp_loaded; +extern atomic_t pm_op_lock; +#endif /*PERF_H*/ diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types index 9d6feaabbe7d1..b1fbb4ff02364 100644 --- a/arch/arm/tools/mach-types +++ b/arch/arm/tools/mach-types @@ -3316,3 +3316,22 @@ rover_g8 MACH_ROVER_G8 ROVER_G8 3335 t5388p MACH_T5388P T5388P 3336 dingo MACH_DINGO DINGO 3337 goflexhome MACH_GOFLEXHOME GOFLEXHOME 3338 +msm7x27a_ffa MACH_MSM7X27A_FFA MSM7X27A_FFA 3351 +msm7x27a_surf MACH_MSM7X27A_SURF MSM7X27A_SURF 3352 +msm7x27a_rumi3 MACH_MSM7X27A_RUMI3 MSM7X27A_RUMI3 3353 +msm8960_cdp MACH_MSM8960_CDP MSM8960_CDP 3396 +msm8960_mtp MACH_MSM8960_MTP MSM8960_MTP 3397 +msm8960_fluid MACH_MSM8960_FLUID MSM8960_FLUID 3398 +msm8960_apq MACH_MSM8960_APQ MSM8960_APQ 3399 +msm8960_liquid MACH_MSM8960_LIQUID MSM8960_LIQUID 3535 +msm8x60_dragon MACH_MSM8X60_DRAGON MSM8X60_DRAGON 3586 +apq8064_sim MACH_APQ8064_SIM APQ8064_SIM 3572 +msm9615_cdp MACH_MSM9615_CDP MSM9615_CDP 3675 +apq8064_rumi3 MACH_APQ8064_RUMI3 APQ8064_RUMI3 3679 +msm9615_mtp MACH_MSM9615_MTP MSM9615_MTP 3681 +msm8930_cdp MACH_MSM8930_CDP MSM8930_CDP 3727 +msm8930_mtp MACH_MSM8930_MTP MSM8930_MTP 3728 +msm8930_fluid MACH_MSM8930_FLUID MSM8930_FLUID 3729 +msm7627a_qrd1 MACH_MSM7627A_QRD1 MSM7627A_QRD1 3756 +msm7625a_ffa MACH_MSM7625A_FFA MSM7625A_FFA 3771 +msm7625a_surf MACH_MSM7625A_SURF MSM7625A_SURF 3772 From 079ab046a8bdec02f9d83f61d14d1b25e4ae62fc Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 15 May 2012 03:54:01 -0500 Subject: [PATCH 2225/2556] arm: common: checkout gic.c from caf commit 3f2bc4d6eb5a4fada842462ba22bb6bbb41d00c7 Initial Contribution msm-3.0 do this to utilize syscore_ops over sysdev (present in 38 branch) --- arch/arm/common/gic.c | 323 +++++++++++++++++++++++++++++++++++------- 1 file changed, 271 insertions(+), 52 deletions(-) diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index 2243772111518..8996f06a76a67 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -28,10 +28,12 @@ #include #include #include +#include #include #include #include +#include static DEFINE_SPINLOCK(irq_controller_lock); @@ -42,6 +44,25 @@ struct gic_chip_data { unsigned int irq_offset; void __iomem *dist_base; void __iomem *cpu_base; + unsigned int max_irq; +#ifdef CONFIG_PM + unsigned int wakeup_irqs[32]; + unsigned int enabled_irqs[32]; +#endif +}; + +/* + * Supported arch specific GIC irq extension. + * Default make them NULL. + */ +struct irq_chip gic_arch_extn = { + .irq_eoi = NULL, + .irq_mask = NULL, + .irq_unmask = NULL, + .irq_retrigger = NULL, + .irq_set_type = NULL, + .irq_set_wake = NULL, + .irq_disable = NULL, }; #ifndef MAX_GIC_NR @@ -71,29 +92,136 @@ static inline unsigned int gic_irq(struct irq_data *d) /* * Routines to acknowledge, disable and enable interrupts */ -static void gic_ack_irq(struct irq_data *d) +static void gic_mask_irq(struct irq_data *d) { + u32 mask = 1 << (d->irq % 32); + spin_lock(&irq_controller_lock); - writel(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI); + writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4); + if (gic_arch_extn.irq_mask) + gic_arch_extn.irq_mask(d); spin_unlock(&irq_controller_lock); + } -static void gic_mask_irq(struct irq_data *d) +static void gic_unmask_irq(struct irq_data *d) { u32 mask = 1 << (d->irq % 32); spin_lock(&irq_controller_lock); - writel(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4); + if (gic_arch_extn.irq_unmask) + gic_arch_extn.irq_unmask(d); + writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4); spin_unlock(&irq_controller_lock); } -static void gic_unmask_irq(struct irq_data *d) +static void gic_disable_irq(struct irq_data *d) { - u32 mask = 1 << (d->irq % 32); + if (gic_arch_extn.irq_disable) + gic_arch_extn.irq_disable(d); +} + +#ifdef CONFIG_PM +static int gic_suspend_one(struct gic_chip_data *gic) +{ + unsigned int i; + void __iomem *base = gic->dist_base; + + for (i = 0; i * 32 < gic->max_irq; i++) { + gic->enabled_irqs[i] + = readl_relaxed(base + GIC_DIST_ENABLE_SET + i * 4); + /* disable all of them */ + writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4); + /* enable the wakeup set */ + writel_relaxed(gic->wakeup_irqs[i], + base + GIC_DIST_ENABLE_SET + i * 4); + } + mb(); + return 0; +} + +static int gic_suspend(void) +{ + int i; + for (i = 0; i < MAX_GIC_NR; i++) + gic_suspend_one(&gic_data[i]); + return 0; +} + +extern int msm_show_resume_irq_mask; + +static void gic_show_resume_irq(struct gic_chip_data *gic) +{ + unsigned int i; + u32 enabled; + unsigned long pending[32]; + void __iomem *base = gic->dist_base; + + if (!msm_show_resume_irq_mask) + return; spin_lock(&irq_controller_lock); - writel(mask, gic_dist_base(d) + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4); + for (i = 0; i * 32 < gic->max_irq; i++) { + enabled = readl_relaxed(base + GIC_DIST_ENABLE_CLEAR + i * 4); + pending[i] = readl_relaxed(base + GIC_DIST_PENDING_SET + i * 4); + pending[i] &= enabled; + } spin_unlock(&irq_controller_lock); + + for (i = find_first_bit(pending, gic->max_irq); + i < gic->max_irq; + i = find_next_bit(pending, gic->max_irq, i+1)) { + pr_warning("%s: %d triggered", __func__, + i + gic->irq_offset); + } +} + +static void gic_resume_one(struct gic_chip_data *gic) +{ + unsigned int i; + void __iomem *base = gic->dist_base; + + gic_show_resume_irq(gic); + for (i = 0; i * 32 < gic->max_irq; i++) { + /* disable all of them */ + writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4); + /* enable the enabled set */ + writel_relaxed(gic->enabled_irqs[i], + base + GIC_DIST_ENABLE_SET + i * 4); + } + mb(); +} + +static void gic_resume(void) +{ + int i; + for (i = 0; i < MAX_GIC_NR; i++) + gic_resume_one(&gic_data[i]); +} + +static struct syscore_ops gic_syscore_ops = { + .suspend = gic_suspend, + .resume = gic_resume, +}; + +static int __init gic_init_sys(void) +{ + register_syscore_ops(&gic_syscore_ops); + return 0; +} +arch_initcall(gic_init_sys); + +#endif + +static void gic_eoi_irq(struct irq_data *d) +{ + if (gic_arch_extn.irq_eoi) { + spin_lock(&irq_controller_lock); + gic_arch_extn.irq_eoi(d); + spin_unlock(&irq_controller_lock); + } + + writel_relaxed(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI); } static int gic_set_type(struct irq_data *d, unsigned int type) @@ -116,7 +244,10 @@ static int gic_set_type(struct irq_data *d, unsigned int type) spin_lock(&irq_controller_lock); - val = readl(base + GIC_DIST_CONFIG + confoff); + if (gic_arch_extn.irq_set_type) + gic_arch_extn.irq_set_type(d, type); + + val = readl_relaxed(base + GIC_DIST_CONFIG + confoff); if (type == IRQ_TYPE_LEVEL_HIGH) val &= ~confmask; else if (type == IRQ_TYPE_EDGE_RISING) @@ -126,59 +257,94 @@ static int gic_set_type(struct irq_data *d, unsigned int type) * As recommended by the spec, disable the interrupt before changing * the configuration */ - if (readl(base + GIC_DIST_ENABLE_SET + enableoff) & enablemask) { - writel(enablemask, base + GIC_DIST_ENABLE_CLEAR + enableoff); + if (readl_relaxed(base + GIC_DIST_ENABLE_SET + enableoff) & enablemask) { + writel_relaxed(enablemask, base + GIC_DIST_ENABLE_CLEAR + enableoff); enabled = true; } - writel(val, base + GIC_DIST_CONFIG + confoff); + writel_relaxed(val, base + GIC_DIST_CONFIG + confoff); if (enabled) - writel(enablemask, base + GIC_DIST_ENABLE_SET + enableoff); + writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff); spin_unlock(&irq_controller_lock); return 0; } +static int gic_retrigger(struct irq_data *d) +{ + if (gic_arch_extn.irq_retrigger) + return gic_arch_extn.irq_retrigger(d); + + return -ENXIO; +} + #ifdef CONFIG_SMP -static int -gic_set_cpu(struct irq_data *d, const struct cpumask *mask_val, bool force) +static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, + bool force) { void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3); unsigned int shift = (d->irq % 4) * 8; unsigned int cpu = cpumask_first(mask_val); - u32 val; - struct irq_desc *desc; + u32 val, mask, bit; - spin_lock(&irq_controller_lock); - desc = irq_to_desc(d->irq); - if (desc == NULL) { - spin_unlock(&irq_controller_lock); + if (cpu >= 8) return -EINVAL; - } + + mask = 0xff << shift; + bit = 1 << (cpu + shift); + + spin_lock(&irq_controller_lock); d->node = cpu; - val = readl(reg) & ~(0xff << shift); - val |= 1 << (cpu + shift); - writel(val, reg); + val = readl_relaxed(reg) & ~mask; + writel_relaxed(val | bit, reg); spin_unlock(&irq_controller_lock); return 0; } #endif +#ifdef CONFIG_PM +static int gic_set_wake(struct irq_data *d, unsigned int on) +{ + int ret = -ENXIO; + unsigned int reg_offset, bit_offset; + unsigned int gicirq = gic_irq(d); + struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d); + + /* per-cpu interrupts cannot be wakeup interrupts */ + WARN_ON(gicirq < 32); + + reg_offset = gicirq / 32; + bit_offset = gicirq % 32; + + if (on) + gic_data->wakeup_irqs[reg_offset] |= 1 << bit_offset; + else + gic_data->wakeup_irqs[reg_offset] &= ~(1 << bit_offset); + + if (gic_arch_extn.irq_set_wake) + ret = gic_arch_extn.irq_set_wake(d, on); + + return ret; +} + +#else +#define gic_set_wake NULL +#endif + static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) { - struct gic_chip_data *chip_data = get_irq_data(irq); - struct irq_chip *chip = get_irq_chip(irq); + struct gic_chip_data *chip_data = irq_get_handler_data(irq); + struct irq_chip *chip = irq_get_chip(irq); unsigned int cascade_irq, gic_irq; unsigned long status; - /* primary controller ack'ing */ - chip->irq_ack(&desc->irq_data); + chained_irq_enter(chip, desc); spin_lock(&irq_controller_lock); - status = readl(chip_data->cpu_base + GIC_CPU_INTACK); + status = readl_relaxed(chip_data->cpu_base + GIC_CPU_INTACK); spin_unlock(&irq_controller_lock); gic_irq = (status & 0x3ff); @@ -192,28 +358,30 @@ static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) generic_handle_irq(cascade_irq); out: - /* primary controller unmasking */ - chip->irq_unmask(&desc->irq_data); + chained_irq_exit(chip, desc); } static struct irq_chip gic_chip = { .name = "GIC", - .irq_ack = gic_ack_irq, .irq_mask = gic_mask_irq, .irq_unmask = gic_unmask_irq, + .irq_eoi = gic_eoi_irq, .irq_set_type = gic_set_type, + .irq_retrigger = gic_retrigger, #ifdef CONFIG_SMP - .irq_set_affinity = gic_set_cpu, + .irq_set_affinity = gic_set_affinity, #endif + .irq_disable = gic_disable_irq, + .irq_set_wake = gic_set_wake, }; void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) { if (gic_nr >= MAX_GIC_NR) BUG(); - if (set_irq_data(irq, &gic_data[gic_nr]) != 0) + if (irq_set_handler_data(irq, &gic_data[gic_nr]) != 0) BUG(); - set_irq_chained_handler(irq, gic_handle_cascade_irq); + irq_set_chained_handler(irq, gic_handle_cascade_irq); } static void __init gic_dist_init(struct gic_chip_data *gic, @@ -226,13 +394,13 @@ static void __init gic_dist_init(struct gic_chip_data *gic, cpumask |= cpumask << 8; cpumask |= cpumask << 16; - writel(0, base + GIC_DIST_CTRL); + writel_relaxed(0, base + GIC_DIST_CTRL); /* * Find out how many interrupts are supported. * The GIC only supports up to 1020 interrupt sources. */ - gic_irqs = readl(base + GIC_DIST_CTR) & 0x1f; + gic_irqs = readl_relaxed(base + GIC_DIST_CTR) & 0x1f; gic_irqs = (gic_irqs + 1) * 32; if (gic_irqs > 1020) gic_irqs = 1020; @@ -241,26 +409,26 @@ static void __init gic_dist_init(struct gic_chip_data *gic, * Set all global interrupts to be level triggered, active low. */ for (i = 32; i < gic_irqs; i += 16) - writel(0, base + GIC_DIST_CONFIG + i * 4 / 16); + writel_relaxed(0, base + GIC_DIST_CONFIG + i * 4 / 16); /* * Set all global interrupts to this CPU only. */ for (i = 32; i < gic_irqs; i += 4) - writel(cpumask, base + GIC_DIST_TARGET + i * 4 / 4); + writel_relaxed(cpumask, base + GIC_DIST_TARGET + i * 4 / 4); /* * Set priority on all global interrupts. */ for (i = 32; i < gic_irqs; i += 4) - writel(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4); + writel_relaxed(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4); /* * Disable all interrupts. Leave the PPI and SGIs alone * as these enables are banked registers. */ for (i = 32; i < gic_irqs; i += 32) - writel(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32); + writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32); /* * Limit number of interrupts registered to the platform maximum @@ -273,13 +441,15 @@ static void __init gic_dist_init(struct gic_chip_data *gic, * Setup the Linux IRQ subsystem. */ for (i = irq_start; i < irq_limit; i++) { - set_irq_chip(i, &gic_chip); - set_irq_chip_data(i, gic); - set_irq_handler(i, handle_level_irq); + irq_set_chip_and_handler(i, &gic_chip, handle_fasteoi_irq); + irq_set_chip_data(i, gic); set_irq_flags(i, IRQF_VALID | IRQF_PROBE); } - writel(1, base + GIC_DIST_CTRL); + gic->max_irq = gic_irqs; + + writel_relaxed(1, base + GIC_DIST_CTRL); + mb(); } static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) @@ -292,17 +462,18 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) * Deal with the banked PPI and SGI interrupts - disable all * PPI interrupts, ensure all SGI interrupts are enabled. */ - writel(0xffff0000, dist_base + GIC_DIST_ENABLE_CLEAR); - writel(0x0000ffff, dist_base + GIC_DIST_ENABLE_SET); + writel_relaxed(0xffff0000, dist_base + GIC_DIST_ENABLE_CLEAR); + writel_relaxed(0x0000ffff, dist_base + GIC_DIST_ENABLE_SET); /* * Set priority on PPI and SGI interrupts */ for (i = 0; i < 32; i += 4) - writel(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4 / 4); + writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4 / 4); - writel(0xf0, base + GIC_CPU_PRIMASK); - writel(1, base + GIC_CPU_CTRL); + writel_relaxed(0xf0, base + GIC_CPU_PRIMASK); + writel_relaxed(1, base + GIC_CPU_CTRL); + mb(); } void __init gic_init(unsigned int gic_nr, unsigned int irq_start, @@ -336,7 +507,7 @@ void __cpuinit gic_enable_ppi(unsigned int irq) unsigned long flags; local_irq_save(flags); - irq_to_desc(irq)->status |= IRQ_NOPROBE; + irq_set_status_flags(irq, IRQ_NOPROBE); gic_unmask_irq(irq_get_irq_data(irq)); local_irq_restore(flags); } @@ -346,7 +517,55 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) { unsigned long map = *cpus_addr(*mask); + /* + * Ensure that stores to Normal memory are visible to the + * other CPUs before issuing the IPI. + */ + dsb(); + /* this always happens on GIC0 */ - writel(map << 16 | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT); + writel_relaxed(map << 16 | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT); + mb(); } #endif + +/* before calling this function the interrupts should be disabled + * and the irq must be disabled at gic to avoid spurious interrupts */ +bool gic_is_spi_pending(unsigned int irq) +{ + struct irq_data *d = irq_get_irq_data(irq); + struct gic_chip_data *gic_data = &gic_data[0]; + u32 mask, val; + + WARN_ON(!irqs_disabled()); + spin_lock(&irq_controller_lock); + mask = 1 << (gic_irq(d) % 32); + val = readl(gic_dist_base(d) + + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4); + /* warn if the interrupt is enabled */ + WARN_ON(val & mask); + val = readl(gic_dist_base(d) + + GIC_DIST_PENDING_SET + (gic_irq(d) / 32) * 4); + spin_unlock(&irq_controller_lock); + return (bool) (val & mask); +} + +/* before calling this function the interrupts should be disabled + * and the irq must be disabled at gic to avoid spurious interrupts */ +void gic_clear_spi_pending(unsigned int irq) +{ + struct gic_chip_data *gic_data = &gic_data[0]; + struct irq_data *d = irq_get_irq_data(irq); + + u32 mask, val; + WARN_ON(!irqs_disabled()); + spin_lock(&irq_controller_lock); + mask = 1 << (gic_irq(d) % 32); + val = readl(gic_dist_base(d) + + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4); + /* warn if the interrupt is enabled */ + WARN_ON(val & mask); + writel(mask, gic_dist_base(d) + + GIC_DIST_PENDING_CLEAR + (gic_irq(d) / 32) * 4); + spin_unlock(&irq_controller_lock); +} From 1eab316617feb60ded589559f92904c875c21fd9 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 15 May 2012 14:06:34 -0500 Subject: [PATCH 2226/2556] move xz gitignore rules to proper directory --- .gitignore | 5 ----- arch/arm/boot/compressed/.gitignore | 2 ++ 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 3244d10e5a118..5d56a3fd0de6b 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,6 @@ modules.builtin *.bz2 *.lzma *.xz -*.xzkern *.lzo *.patch *.gcno @@ -79,7 +78,3 @@ GTAGS *.orig *~ \#*# - -# Special rule -# XXX: there's got to be a more generic way to do this -arch/arm/boot/compressed/ashldi3.S diff --git a/arch/arm/boot/compressed/.gitignore b/arch/arm/boot/compressed/.gitignore index c6028967d3367..f504fc155e683 100644 --- a/arch/arm/boot/compressed/.gitignore +++ b/arch/arm/boot/compressed/.gitignore @@ -1,7 +1,9 @@ font.c +ashldi3.S lib1funcs.S piggy.gzip piggy.lzo piggy.lzma +piggy.xzkern vmlinux vmlinux.lds From ea7b1a6fe10bacfece8ee805eb52ea7e25e58aab Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 15 May 2012 14:36:50 -0500 Subject: [PATCH 2227/2556] msm: update Makefile.boot per cafs/msm-2.6.38 --- arch/arm/mach-msm/Makefile.boot | 71 ++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot index a423a2ee969c2..b9c45d980051b 100644 --- a/arch/arm/mach-msm/Makefile.boot +++ b/arch/arm/mach-msm/Makefile.boot @@ -1,17 +1,58 @@ - zreladdr-y := 0x10008000 -params_phys-y := 0x10000100 -initrd_phys-y := 0x10800000 +# MSM7x01A + zreladdr-$(CONFIG_ARCH_MSM7X01A) := 0x10008000 +params_phys-$(CONFIG_ARCH_MSM7X01A) := 0x10000100 +initrd_phys-$(CONFIG_ARCH_MSM7X01A) := 0x10800000 + +# MSM7x25 + zreladdr-$(CONFIG_ARCH_MSM7X25) := 0x00208000 +params_phys-$(CONFIG_ARCH_MSM7X25) := 0x00200100 +initrd_phys-$(CONFIG_ARCH_MSM7X25) := 0x0A000000 + +# MSM7x27 + zreladdr-$(CONFIG_ARCH_MSM7X27) := 0x00208000 +params_phys-$(CONFIG_ARCH_MSM7X27) := 0x00200100 +initrd_phys-$(CONFIG_ARCH_MSM7X27) := 0x0A000000 + +# MSM7x27A + zreladdr-$(CONFIG_ARCH_MSM7X27A) := 0x00208000 +params_phys-$(CONFIG_ARCH_MSM7X27A) := 0x00200100 + +# MSM7x30 + zreladdr-$(CONFIG_ARCH_MSM7X30) := 0x00208000 +params_phys-$(CONFIG_ARCH_MSM7X30) := 0x00200100 +initrd_phys-$(CONFIG_ARCH_MSM7X30) := 0x01200000 + +ifeq ($(CONFIG_MSM_SOC_REV_A),y) +# QSD8x50A + zreladdr-$(CONFIG_ARCH_QSD8X50) := 0x00008000 +params_phys-$(CONFIG_ARCH_QSD8X50) := 0x00000100 +initrd_phys-$(CONFIG_ARCH_QSD8X50) := 0x04000000 +else +# QSD8x50 + zreladdr-$(CONFIG_ARCH_QSD8X50) := 0x20008000 +params_phys-$(CONFIG_ARCH_QSD8X50) := 0x20000100 +initrd_phys-$(CONFIG_ARCH_QSD8X50) := 0x24000000 +#initrd_phys-$(CONFIG_ARCH_QSD8X50) := 0x21000000 +endif + +# MSM8x60 + zreladdr-$(CONFIG_ARCH_MSM8X60) := 0x40208000 + +# MSM8960 + zreladdr-$(CONFIG_ARCH_MSM8960) := 0x80208000 + +# APQ8064 + zreladdr-$(CONFIG_ARCH_APQ8064) := 0x80208000 + +# MSM9615 + zreladdr-$(CONFIG_ARCH_MSM9615) := 0x40808000 + +# FSM9XXX + zreladdr-$(CONFIG_ARCH_FSM9XXX) := 0x10008000 +params_phys-$(CONFIG_ARCH_FSM9XXX) := 0x10000100 +initrd_phys-$(CONFIG_ARCH_FSM9XXX) := 0x12000000 # override for Sapphire - zreladdr-$(CONFIG_MACH_SAPPHIRE) := 0x02008000 -params_phys-$(CONFIG_MACH_SAPPHIRE) := 0x02000100 -initrd_phys-$(CONFIG_MACH_SAPPHIRE) := 0x02800000 - -# for now, override for QSD8x50 - zreladdr-$(CONFIG_ARCH_QSD8X50) := 0x20008000 -params_phys-$(CONFIG_ARCH_QSD8X50) := 0x20000100 -initrd_phys-$(CONFIG_ARCH_QSD8X50) := 0x21000000 - - zreladdr-$(CONFIG_ARCH_MSM7X30) := 0x00208000 -params_phys-$(CONFIG_ARCH_MSM7X30) := 0x00200100 -initrd_phys-$(CONFIG_ARCH_MSM7X30) := 0x01200000 + zreladdr-$(CONFIG_MACH_SAPPHIRE) := 0x02008000 +params_phys-$(CONFIG_MACH_SAPPHIRE) := 0x02000100 +initrd_phys-$(CONFIG_MACH_SAPPHIRE) := 0x02800000 From bdf41e016b057099e7196b03af5d4f623075070a Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 15 May 2012 17:16:04 -0500 Subject: [PATCH 2228/2556] msm: cleanup Makefile a bit --- arch/arm/mach-msm/Makefile | 70 ++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index ed9a59bd2c114..8e193c2a55614 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -1,16 +1,22 @@ -obj-y += proc_comm.o -obj-y += io.o timer.o -obj-y += memory.o +obj-y += io.o dma.o memory.o timer.o +obj-y += clock.o +obj-$(CONFIG_CPU_FREQ_MSM) += cpufreq.o + +obj-$(CONFIG_MSM_PROC_COMM) += proc_comm.o ifndef CONFIG_ARCH_MSM8X60 -obj-y += dma.o + obj-$(CONFIG_MSM_PROC_COMM) += clock-pcom.o + obj-$(CONFIG_MSM_PROC_COMM) += vreg.o + ifdef CONFIG_MSM_PROC_COMM +ifndef CONFIG_ARCH_FSM9XXX + obj-$(CONFIG_REGULATOR) += footswitch-pcom.o +endif + endif endif -obj-y += nand_partitions.o -obj-y += pmic.o + obj-y += drv_callback.o obj-y += htc_board_tags.o obj-$(CONFIG_ARCH_MSM_ARM11) += acpuclock-arm11.o idle.o -obj-$(CONFIG_ARCH_MSM_SCORPION) += idle-v7.o obj-$(CONFIG_ARCH_MSM_SCORPION) += arch-init-scorpion.o obj-$(CONFIG_ARCH_QSD8X50) += acpuclock-qsd8x50.o @@ -21,18 +27,31 @@ obj-y += irq.o endif obj-$(CONFIG_ARCH_MSM8X60) += clock-dummy.o iommu.o iommu_dev.o devices-msm8x60-iommu.o -obj-$(CONFIG_MSM_PROC_COMM) += proc_comm.o clock-pcom.o vreg.o -obj-$(CONFIG_MSM_PROC_COMM) += clock.o -obj-$(CONFIG_MSM_PROC_COMM) += footswitch-pcom.o +obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o +obj-$(CONFIG_SMP) += headsmp.o platsmp.o +obj-$(CONFIG_CPU_V7) += idle-v7.o +obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o obj-$(CONFIG_ARCH_QSD8X50) += sirc.o obj-$(CONFIG_MSM_FIQ_SUPPORT) += fiq_glue.o -obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o smem_log.o +obj-$(CONFIG_MSM_SMD_LOGGING) += smem_log.o +obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o obj-$(CONFIG_MSM_SMD) += smd_tty.o smd_qmi.o obj-$(CONFIG_MSM_SMD) += last_radio_log.o -obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o -obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o -obj-$(CONFIG_SMP) += headsmp.o platsmp.o +ifndef CONFIG_ARCH_MSM9615 +ifndef CONFIG_ARCH_MSM8960 +ifndef CONFIG_ARCH_MSM8X60 + obj-$(CONFIG_MSM_SMD) += pmic.o +endif +endif +endif +ifndef CONFIG_ARCH_MSM8960 +ifndef CONFIG_ARCH_MSM8X60 +ifndef CONFIG_ARCH_APQ8064 + obj-y += nand_partitions.o +endif +endif +endif obj-$(CONFIG_MSM_DAL) += dal.o @@ -45,12 +64,13 @@ obj-$(CONFIG_MSM_ADSP) += qdsp5/ obj-$(CONFIG_MSM_QDSP5V2) += qdsp5v2/ obj-$(CONFIG_MSM_QDSP6) += qdsp6/ obj-$(CONFIG_MSM_HW3D) += hw3d.o -obj-$(CONFIG_PM) += pm.o -obj-$(CONFIG_CPU_FREQ) += cpufreq.o obj-$(CONFIG_MSM_REMOTE_SPINLOCK) += remote_spinlock.o obj-$(CONFIG_MSM_SSBI) += ssbi.o -obj-$(CONFIG_HTC_ACOUSTIC) += htc_acoustic.o -obj-$(CONFIG_HTC_ACOUSTIC_QSD) += htc_acoustic_qsd.o +ifdef CONFIG_PM + obj-y += pm.o +else + obj-y += no-pm.o +endif obj-$(CONFIG_MACH_TROUT) += board-trout-rfkill.o obj-$(CONFIG_MACH_TROUT) += board-trout-wifi.o @@ -71,6 +91,9 @@ obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-wifi.o board-mahimahi-audio.o obj-$(CONFIG_MACH_MAHIMAHI) += msm_vibrator.o obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-microp.o obj-$(CONFIG_MACH_MAHIMAHI) += htc_acoustic_qsd.o +obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-tpa2018d1.o +obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-smb329.o + obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-keypad.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut-panel.o fish_battery.o @@ -121,7 +144,6 @@ obj-$(CONFIG_MACH_INCREDIBLEC) += msm_vibrator.o obj-$(CONFIG_MACH_INCREDIBLEC) += proc_engineerid.o board-incrediblec-tpa6130.o obj-$(CONFIG_MACH_INCREDIBLEC) += htc_acoustic_qsd.o - obj-$(CONFIG_MACH_BRAVO) += board-bravo.o board-bravo-panel.o obj-$(CONFIG_MACH_BRAVO) += board-bravo-keypad.o board-bravo-mmc.o obj-$(CONFIG_MACH_BRAVO) += board-bravo-rfkill.o htc_wifi_nvs.o htc_awb_cal.o @@ -140,21 +162,17 @@ obj-$(CONFIG_MACH_BRAVOC) += board-bravo-microp.o obj-$(CONFIG_MACH_BRAVOC) += board-bravo-tpa2018d1.o obj-$(CONFIG_MACH_BRAVOC) += board-bravo-smb329.o +obj-$(CONFIG_HTC_ACOUSTIC) += htc_acoustic.o +obj-$(CONFIG_HTC_ACOUSTIC_QSD) += htc_acoustic_qsd.o obj-$(CONFIG_HTC_BATTCHG) += htc_battery.o obj-$(CONFIG_HTC_PWRSINK) += htc_pwrsink.o obj-$(CONFIG_HTC_PWRSPLY) += htc_power_supply.o obj-$(CONFIG_HTC_HEADSET) += htc_headset.o obj-$(CONFIG_HTC_35MM_JACK) += htc_35mm_jack.o - -obj-$(CONFIG_MICROP_COMMON) += atmega_microp_common.o obj-$(CONFIG_HTC_HEADSET_MGR) += htc_headset_mgr.o obj-$(CONFIG_HTC_HEADSET_GPIO) += htc_headset_gpio.o obj-$(CONFIG_HTC_HEADSET_MICROP) += htc_headset_microp.o obj-$(CONFIG_PERFLOCK) += perflock.o obj-$(CONFIG_PERFLOCK) += htc_set_perflock.o - -obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-tpa2018d1.o -obj-$(CONFIG_MACH_MAHIMAHI) += board-mahimahi-smb329.o - +obj-$(CONFIG_MICROP_COMMON) += atmega_microp_common.o obj-$(CONFIG_ARCH_MSM_FLASHLIGHT) += msm_flashlight.o - From 36758e0a0213606e8ce03f8fdc8292122371dfde Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 15 May 2012 16:25:54 -0500 Subject: [PATCH 2229/2556] msm: htc: misc cleanup --- arch/arm/mach-msm/htc_power_supply.c | 6 ++---- arch/arm/mach-msm/htc_wifi_nvs.c | 9 ++++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-msm/htc_power_supply.c b/arch/arm/mach-msm/htc_power_supply.c index 9cfe2ccad7f6a..bd286c9f3a8b1 100644 --- a/arch/arm/mach-msm/htc_power_supply.c +++ b/arch/arm/mach-msm/htc_power_supply.c @@ -28,7 +28,6 @@ #include #include #include -#include #include "board-mahimahi.h" @@ -413,11 +412,10 @@ static int power_get_property(struct power_supply *psy, if (psp != POWER_SUPPLY_PROP_ONLINE) return -EINVAL; - if (psy->type == POWER_SUPPLY_TYPE_MAINS) { + if (psy->type == POWER_SUPPLY_TYPE_MAINS) val->intval = (vbus_present && (usb_status == 2 || dock_mains)); - } else { + else val->intval = vbus_present; - } return 0; } diff --git a/arch/arm/mach-msm/htc_wifi_nvs.c b/arch/arm/mach-msm/htc_wifi_nvs.c index 17535959de672..db6f678958a2d 100644 --- a/arch/arm/mach-msm/htc_wifi_nvs.c +++ b/arch/arm/mach-msm/htc_wifi_nvs.c @@ -36,7 +36,7 @@ static struct proc_dir_entry *wifi_calibration; unsigned char *get_wifi_nvs_ram( void ) { - return wifi_nvs_ram; + return( wifi_nvs_ram ); } EXPORT_SYMBOL(get_wifi_nvs_ram); @@ -51,11 +51,10 @@ static int __init parse_tag_msm_wifi(const struct tag *tag) size = min((tag->hdr.size - 2) * sizeof(__u32), NVS_MAX_SIZE); #ifdef ATAG_MSM_WIFI_DEBUG printk("WiFi Data size = %d , 0x%x\n", tag->hdr.size, tag->hdr.tag); - for(i=0;( i < size );i++) { + for (i = 0; i < size; i++) printk("%02x ", *dptr++); - } -#endif - memcpy(wifi_nvs_ram, dptr, size); +#endif + memcpy( (void *)wifi_nvs_ram, (void *)dptr, size ); return 0; } From dc2265d9a330ab3cb400aad365b842390a7899de Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 15 May 2012 19:36:15 -0500 Subject: [PATCH 2230/2556] msm: socinfo that now works / add init to boards --- arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/board-bravo.c | 3 + arch/arm/mach-msm/board-incrediblec.c | 3 + arch/arm/mach-msm/board-mahimahi.c | 3 + arch/arm/mach-msm/board-supersonic.c | 3 + arch/arm/mach-msm/include/mach/socinfo.h | 255 ++++++++ arch/arm/mach-msm/socinfo.c | 746 +++++++++++++++++++++++ 7 files changed, 1014 insertions(+) create mode 100644 arch/arm/mach-msm/include/mach/socinfo.h create mode 100644 arch/arm/mach-msm/socinfo.c diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 8e193c2a55614..f381929de1c10 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -1,6 +1,7 @@ obj-y += io.o dma.o memory.o timer.o obj-y += clock.o obj-$(CONFIG_CPU_FREQ_MSM) += cpufreq.o +obj-y += socinfo.o obj-$(CONFIG_MSM_PROC_COMM) += proc_comm.o ifndef CONFIG_ARCH_MSM8X60 diff --git a/arch/arm/mach-msm/board-bravo.c b/arch/arm/mach-msm/board-bravo.c index f7891df8ca443..0ddfd5146780f 100644 --- a/arch/arm/mach-msm/board-bravo.c +++ b/arch/arm/mach-msm/board-bravo.c @@ -55,6 +55,7 @@ #endif #include #include +#include #include "board-bravo.h" #include "devices.h" @@ -1287,6 +1288,8 @@ static void __init bravo_map_io(void) { msm_map_qsd8x50_io(); msm_clock_init(msm_clocks_8x50, msm_num_clocks_8x50); + if (socinfo_init() < 0) + printk(KERN_ERR "%s: socinfo_init() failed!\n",__func__); } extern struct sys_timer msm_timer; diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index 4ca89e3270411..b30039d7d4610 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -1454,6 +1455,8 @@ static void __init incrediblec_map_io(void) { msm_map_qsd8x50_io(); msm_clock_init(msm_clocks_8x50, msm_num_clocks_8x50); + if (socinfo_init() < 0) + printk(KERN_ERR "%s: socinfo_init() failed!\n",__func__); } extern struct sys_timer msm_timer; diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 809c3c1e2d61b..c290faf9dc8fe 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -54,6 +54,7 @@ #ifdef CONFIG_PERFLOCK #include #endif +#include #include "board-mahimahi.h" #include "devices.h" #include "proc_comm.h" @@ -1190,6 +1191,8 @@ static void __init mahimahi_map_io(void) { msm_map_qsd8x50_io(); msm_clock_init(msm_clocks_8x50, msm_num_clocks_8x50); + if (socinfo_init() < 0) + printk(KERN_ERR "%s: socinfo_init() failed!\n",__func__); } extern struct sys_timer msm_timer; diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index 25b3e87a4d3af..8a02380955d58 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include "board-supersonic.h" @@ -1667,6 +1668,8 @@ static void __init supersonic_map_io(void) { msm_map_qsd8x50_io(); msm_clock_init(msm_clocks_8x50, msm_num_clocks_8x50); + if (socinfo_init() < 0) + printk(KERN_ERR "%s: socinfo_init() failed!\n",__func__); } extern struct sys_timer msm_timer; diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h new file mode 100644 index 0000000000000..4d68132964a80 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/socinfo.h @@ -0,0 +1,255 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_SOCINFO_H_ +#define _ARCH_ARM_MACH_MSM_SOCINFO_H_ + +#include +#include + +#include +#include +/* + * SOC version type with major number in the upper 16 bits and minor + * number in the lower 16 bits. For example: + * 1.0 -> 0x00010000 + * 2.3 -> 0x00020003 + */ +#define SOCINFO_VERSION_MAJOR(ver) ((ver & 0xffff0000) >> 16) +#define SOCINFO_VERSION_MINOR(ver) (ver & 0x0000ffff) + +enum msm_cpu { + MSM_CPU_UNKNOWN = 0, + MSM_CPU_7X01, + MSM_CPU_7X25, + MSM_CPU_7X27, + MSM_CPU_8X50, + MSM_CPU_8X50A, + MSM_CPU_7X30, + MSM_CPU_8X55, + MSM_CPU_8X60, + MSM_CPU_8960, + MSM_CPU_7X27A, + FSM_CPU_9XXX, + MSM_CPU_7X25A, + MSM_CPU_7X25AA, + MSM_CPU_8064, + MSM_CPU_8X30, + MSM_CPU_7X27AA, + MSM_CPU_9615, +}; + +enum msm_cpu socinfo_get_msm_cpu(void); +uint32_t socinfo_get_id(void); +uint32_t socinfo_get_version(void); +char *socinfo_get_build_id(void); +uint32_t socinfo_get_platform_type(void); +uint32_t socinfo_get_platform_subtype(void); +uint32_t socinfo_get_platform_version(void); +int __init socinfo_init(void) __must_check; +const int read_msm_cpu_type(void); +const int get_core_count(void); +const int cpu_is_krait_v1(void); + +static inline int cpu_is_msm7x01(void) +{ +#ifdef CONFIG_ARCH_MSM7X01A + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X01; +#else + return 0; +#endif +} + +static inline int cpu_is_msm7x25(void) +{ +#ifdef CONFIG_ARCH_MSM7X25 + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X25; +#else + return 0; +#endif +} + +static inline int cpu_is_msm7x27(void) +{ +#ifdef CONFIG_ARCH_MSM7X27 + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X27; +#else + return 0; +#endif +} + +static inline int cpu_is_msm7x27a(void) +{ +#ifdef CONFIG_ARCH_MSM7X27A + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X27A; +#else + return 0; +#endif +} + +static inline int cpu_is_msm7x27aa(void) +{ +#ifdef CONFIG_ARCH_MSM7X27A + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X27AA; +#else + return 0; +#endif +} + +static inline int cpu_is_msm7x25a(void) +{ +#ifdef CONFIG_ARCH_MSM7X27A + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X25A; +#else + return 0; +#endif +} + +static inline int cpu_is_msm7x25aa(void) +{ +#ifdef CONFIG_ARCH_MSM7X27A + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X25AA; +#else + return 0; +#endif +} + +static inline int cpu_is_msm7x30(void) +{ +#ifdef CONFIG_ARCH_MSM7X30 + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X30; +#else + return 0; +#endif +} + +static inline int cpu_is_qsd8x50(void) +{ +#ifdef CONFIG_ARCH_QSD8X50 + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_8X50; +#else + return 0; +#endif +} + +static inline int cpu_is_msm8x55(void) +{ +#ifdef CONFIG_ARCH_MSM7X30 + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_8X55; +#else + return 0; +#endif +} + +static inline int cpu_is_msm8x60(void) +{ +#ifdef CONFIG_ARCH_MSM8X60 + return read_msm_cpu_type() == MSM_CPU_8X60; +#else + return 0; +#endif +} + +static inline int cpu_is_msm8960(void) +{ +#ifdef CONFIG_ARCH_MSM8960 + return read_msm_cpu_type() == MSM_CPU_8960; +#else + return 0; +#endif +} + +static inline int cpu_is_apq8064(void) +{ +#ifdef CONFIG_ARCH_APQ8064 + return read_msm_cpu_type() == MSM_CPU_8064; +#else + return 0; +#endif +} + +static inline int cpu_is_msm8x30(void) +{ + return read_msm_cpu_type() == MSM_CPU_8X30; +} + +static inline int cpu_is_fsm9xxx(void) +{ +#ifdef CONFIG_ARCH_FSM9XXX + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == FSM_CPU_9XXX; +#else + return 0; +#endif +} + +static inline int cpu_is_msm9615(void) +{ +#ifdef CONFIG_ARCH_MSM9615 + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_9615; +#else + return 0; +#endif +} +#endif diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c new file mode 100644 index 0000000000000..1e65e92054fbe --- /dev/null +++ b/arch/arm/mach-msm/socinfo.c @@ -0,0 +1,746 @@ +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * SOC Info Routines + * + */ + +#include +#include +#include +#include + +#include "smd_private.h" + +#define BUILD_ID_LENGTH 32 + +enum { + HW_PLATFORM_UNKNOWN = 0, + HW_PLATFORM_SURF = 1, + HW_PLATFORM_FFA = 2, + HW_PLATFORM_FLUID = 3, + HW_PLATFORM_SVLTE_FFA = 4, + HW_PLATFORM_SVLTE_SURF = 5, + /* Dragonboard platform id is assigned as 10 in CDT */ + HW_PLATFORM_DRAGON = 10, + HW_PLATFORM_INVALID +}; + +const char *hw_platform[] = { + [HW_PLATFORM_UNKNOWN] = "Unknown", + [HW_PLATFORM_SURF] = "Surf", + [HW_PLATFORM_FFA] = "FFA", + [HW_PLATFORM_FLUID] = "Fluid", + [HW_PLATFORM_SVLTE_FFA] = "SVLTE_FFA", + [HW_PLATFORM_SVLTE_SURF] = "SLVTE_SURF", + [HW_PLATFORM_DRAGON] = "Dragon" +}; + +enum { + ACCESSORY_CHIP_UNKNOWN = 0, + ACCESSORY_CHIP_CHARM = 58, +}; + +enum { + PLATFORM_SUBTYPE_UNKNOWN = 0x0, + PLATFORM_SUBTYPE_CHARM = 0x1, + PLATFORM_SUBTYPE_STRANGE = 0x2, + PLATFORM_SUBTYPE_STRANGE_2A = 0x3, + PLATFORM_SUBTYPE_INVALID, +}; + +const char *hw_platform_subtype[] = { + [PLATFORM_SUBTYPE_UNKNOWN] = "Unknown", + [PLATFORM_SUBTYPE_CHARM] = "charm", + [PLATFORM_SUBTYPE_STRANGE] = "strange", + [PLATFORM_SUBTYPE_STRANGE_2A] = "strange_2a," +}; + +/* Used to parse shared memory. Must match the modem. */ +struct socinfo_v1 { + uint32_t format; + uint32_t id; + uint32_t version; + char build_id[BUILD_ID_LENGTH]; +}; + +struct socinfo_v2 { + struct socinfo_v1 v1; + + /* only valid when format==2 */ + uint32_t raw_id; + uint32_t raw_version; +}; + +struct socinfo_v3 { + struct socinfo_v2 v2; + + /* only valid when format==3 */ + uint32_t hw_platform; +}; + +struct socinfo_v4 { + struct socinfo_v3 v3; + + /* only valid when format==4 */ + uint32_t platform_version; +}; + +struct socinfo_v5 { + struct socinfo_v4 v4; + + /* only valid when format==5 */ + uint32_t accessory_chip; +}; + +struct socinfo_v6 { + struct socinfo_v5 v5; + + /* only valid when format==6 */ + uint32_t hw_platform_subtype; +}; + +static union { + struct socinfo_v1 v1; + struct socinfo_v2 v2; + struct socinfo_v3 v3; + struct socinfo_v4 v4; + struct socinfo_v5 v5; + struct socinfo_v6 v6; +} *socinfo; + +static enum msm_cpu cpu_of_id[] = { + + /* 7x01 IDs */ + [1] = MSM_CPU_7X01, + [16] = MSM_CPU_7X01, + [17] = MSM_CPU_7X01, + [18] = MSM_CPU_7X01, + [19] = MSM_CPU_7X01, + [23] = MSM_CPU_7X01, + [25] = MSM_CPU_7X01, + [26] = MSM_CPU_7X01, + [32] = MSM_CPU_7X01, + [33] = MSM_CPU_7X01, + [34] = MSM_CPU_7X01, + [35] = MSM_CPU_7X01, + + /* 7x25 IDs */ + [20] = MSM_CPU_7X25, + [21] = MSM_CPU_7X25, /* 7225 */ + [24] = MSM_CPU_7X25, /* 7525 */ + [27] = MSM_CPU_7X25, /* 7625 */ + [39] = MSM_CPU_7X25, + [40] = MSM_CPU_7X25, + [41] = MSM_CPU_7X25, + [42] = MSM_CPU_7X25, + [62] = MSM_CPU_7X25, /* 7625-1 */ + [63] = MSM_CPU_7X25, /* 7225-1 */ + [66] = MSM_CPU_7X25, /* 7225-2 */ + + + /* 7x27 IDs */ + [43] = MSM_CPU_7X27, + [44] = MSM_CPU_7X27, + [61] = MSM_CPU_7X27, + [67] = MSM_CPU_7X27, /* 7227-1 */ + [68] = MSM_CPU_7X27, /* 7627-1 */ + [69] = MSM_CPU_7X27, /* 7627-2 */ + + + /* 8x50 IDs */ + [30] = MSM_CPU_8X50, + [36] = MSM_CPU_8X50, + [37] = MSM_CPU_8X50, + [38] = MSM_CPU_8X50, + + /* 8x50A IDs */ + [64] = MSM_CPU_8X50A, + [65] = MSM_CPU_8X50A, + + /* 7x30 IDs */ + [59] = MSM_CPU_7X30, + [60] = MSM_CPU_7X30, + + /* 8x55 IDs */ + [74] = MSM_CPU_8X55, + [75] = MSM_CPU_8X55, + [85] = MSM_CPU_8X55, + + /* 8x60 IDs */ + [70] = MSM_CPU_8X60, + [71] = MSM_CPU_8X60, + [86] = MSM_CPU_8X60, + + /* 8960 IDs */ + [87] = MSM_CPU_8960, + + /* 7x25A IDs */ + [88] = MSM_CPU_7X25A, + [89] = MSM_CPU_7X25A, + [96] = MSM_CPU_7X25A, + + /* 7x27A IDs */ + [90] = MSM_CPU_7X27A, + [91] = MSM_CPU_7X27A, + [92] = MSM_CPU_7X27A, + [97] = MSM_CPU_7X27A, + + /* FSM9xxx ID */ + [94] = FSM_CPU_9XXX, + [95] = FSM_CPU_9XXX, + + /* 7x25AA ID */ + [98] = MSM_CPU_7X25AA, + [99] = MSM_CPU_7X25AA, + [100] = MSM_CPU_7X25AA, + + /* 7x27AA ID */ + [101] = MSM_CPU_7X27AA, + [102] = MSM_CPU_7X27AA, + [103] = MSM_CPU_7X27AA, + + /* 9x15 ID */ + [104] = MSM_CPU_9615, + [105] = MSM_CPU_9615, + + /* 8064 IDs*/ + [109] = MSM_CPU_8064, + + /* Uninitialized IDs are not known to run Linux. + MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are + considered as unknown CPU. */ +}; + +static enum msm_cpu cur_cpu; + +static struct socinfo_v1 dummy_socinfo = { + .format = 1, + .version = 1, + .build_id = "Dummy socinfo placeholder" +}; + +uint32_t socinfo_get_id(void) +{ + return (socinfo) ? socinfo->v1.id : 0; +} +EXPORT_SYMBOL_GPL(socinfo_get_id); + +uint32_t socinfo_get_version(void) +{ + return (socinfo) ? socinfo->v1.version : 0; +} + +char *socinfo_get_build_id(void) +{ + return (socinfo) ? socinfo->v1.build_id : NULL; +} + +uint32_t socinfo_get_raw_id(void) +{ + return socinfo ? + (socinfo->v1.format >= 2 ? socinfo->v2.raw_id : 0) + : 0; +} + +uint32_t socinfo_get_raw_version(void) +{ + return socinfo ? + (socinfo->v1.format >= 2 ? socinfo->v2.raw_version : 0) + : 0; +} + +uint32_t socinfo_get_platform_type(void) +{ + return socinfo ? + (socinfo->v1.format >= 3 ? socinfo->v3.hw_platform : 0) + : 0; +} + + +uint32_t socinfo_get_platform_version(void) +{ + return socinfo ? + (socinfo->v1.format >= 4 ? socinfo->v4.platform_version : 0) + : 0; +} + +/* This information is directly encoded by the machine id */ +/* Thus no external callers rely on this information at the moment */ +static uint32_t socinfo_get_accessory_chip(void) +{ + return socinfo ? + (socinfo->v1.format >= 5 ? socinfo->v5.accessory_chip : 0) + : 0; +} + +uint32_t socinfo_get_platform_subtype(void) +{ + return socinfo ? + (socinfo->v1.format >= 6 ? socinfo->v6.hw_platform_subtype : 0) + : 0; +} + +enum msm_cpu socinfo_get_msm_cpu(void) +{ + return cur_cpu; +} +EXPORT_SYMBOL_GPL(socinfo_get_msm_cpu); + +static ssize_t +socinfo_show_id(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", socinfo_get_id()); +} + +static ssize_t +socinfo_show_version(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + uint32_t version; + + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + + version = socinfo_get_version(); + return snprintf(buf, PAGE_SIZE, "%u.%u\n", + SOCINFO_VERSION_MAJOR(version), + SOCINFO_VERSION_MINOR(version)); +} + +static ssize_t +socinfo_show_build_id(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%-.32s\n", socinfo_get_build_id()); +} + +static ssize_t +socinfo_show_raw_id(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 2) { + pr_err("%s: Raw ID not available!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", socinfo_get_raw_id()); +} + +static ssize_t +socinfo_show_raw_version(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 2) { + pr_err("%s: Raw version not available!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", socinfo_get_raw_version()); +} + +static ssize_t +socinfo_show_platform_type(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + uint32_t hw_type; + + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 3) { + pr_err("%s: platform type not available!\n", __func__); + return 0; + } + + hw_type = socinfo_get_platform_type(); + if (hw_type >= HW_PLATFORM_INVALID) { + pr_err("%s: Invalid hardware platform type found\n", + __func__); + hw_type = HW_PLATFORM_UNKNOWN; + } + + return snprintf(buf, PAGE_SIZE, "%-.32s\n", hw_platform[hw_type]); +} + +static ssize_t +socinfo_show_platform_version(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 4) { + pr_err("%s: platform version not available!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", + socinfo_get_platform_version()); +} + +static ssize_t +socinfo_show_accessory_chip(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 5) { + pr_err("%s: accessory chip not available!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", + socinfo_get_accessory_chip()); +} + +static ssize_t +socinfo_show_platform_subtype(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + uint32_t hw_subtype; + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format < 6) { + pr_err("%s: platform subtype not available!\n", __func__); + return 0; + } + + hw_subtype = socinfo_get_platform_subtype(); + if (hw_subtype >= PLATFORM_SUBTYPE_INVALID) { + pr_err("%s: Invalid hardware platform sub type found\n", + __func__); + hw_subtype = PLATFORM_SUBTYPE_UNKNOWN; + } + return snprintf(buf, PAGE_SIZE, "%-.32s\n", + hw_platform_subtype[hw_subtype]); +} + +static struct sysdev_attribute socinfo_v1_files[] = { + _SYSDEV_ATTR(id, 0444, socinfo_show_id, NULL), + _SYSDEV_ATTR(version, 0444, socinfo_show_version, NULL), + _SYSDEV_ATTR(build_id, 0444, socinfo_show_build_id, NULL), +}; + +static struct sysdev_attribute socinfo_v2_files[] = { + _SYSDEV_ATTR(raw_id, 0444, socinfo_show_raw_id, NULL), + _SYSDEV_ATTR(raw_version, 0444, socinfo_show_raw_version, NULL), +}; + +static struct sysdev_attribute socinfo_v3_files[] = { + _SYSDEV_ATTR(hw_platform, 0444, socinfo_show_platform_type, NULL), +}; + +static struct sysdev_attribute socinfo_v4_files[] = { + _SYSDEV_ATTR(platform_version, 0444, + socinfo_show_platform_version, NULL), +}; + +static struct sysdev_attribute socinfo_v5_files[] = { + _SYSDEV_ATTR(accessory_chip, 0444, + socinfo_show_accessory_chip, NULL), +}; + +static struct sysdev_attribute socinfo_v6_files[] = { + _SYSDEV_ATTR(platform_subtype, 0444, + socinfo_show_platform_subtype, NULL), +}; + +static struct sysdev_class soc_sysdev_class = { + .name = "soc", +}; + +static struct sys_device soc_sys_device = { + .id = 0, + .cls = &soc_sysdev_class, +}; + +static int __init socinfo_create_files(struct sys_device *dev, + struct sysdev_attribute files[], + int size) +{ + int i; + for (i = 0; i < size; i++) { + int err = sysdev_create_file(dev, &files[i]); + if (err) { + pr_err("%s: sysdev_create_file(%s)=%d\n", + __func__, files[i].attr.name, err); + return err; + } + } + return 0; +} + +static int __init socinfo_init_sysdev(void) +{ + int err; + + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return -ENODEV; + } + + err = sysdev_class_register(&soc_sysdev_class); + if (err) { + pr_err("%s: sysdev_class_register fail (%d)\n", + __func__, err); + return err; + } + err = sysdev_register(&soc_sys_device); + if (err) { + pr_err("%s: sysdev_register fail (%d)\n", + __func__, err); + return err; + } + socinfo_create_files(&soc_sys_device, socinfo_v1_files, + ARRAY_SIZE(socinfo_v1_files)); + if (socinfo->v1.format < 2) + return err; + socinfo_create_files(&soc_sys_device, socinfo_v2_files, + ARRAY_SIZE(socinfo_v2_files)); + + if (socinfo->v1.format < 3) + return err; + + socinfo_create_files(&soc_sys_device, socinfo_v3_files, + ARRAY_SIZE(socinfo_v3_files)); + + if (socinfo->v1.format < 4) + return err; + + socinfo_create_files(&soc_sys_device, socinfo_v4_files, + ARRAY_SIZE(socinfo_v4_files)); + + if (socinfo->v1.format < 5) + return err; + + socinfo_create_files(&soc_sys_device, socinfo_v5_files, + ARRAY_SIZE(socinfo_v5_files)); + + if (socinfo->v1.format < 6) + return err; + + return socinfo_create_files(&soc_sys_device, socinfo_v6_files, + ARRAY_SIZE(socinfo_v6_files)); + +} + +arch_initcall(socinfo_init_sysdev); + +void *setup_dummy_socinfo(void) +{ + if (machine_is_msm8960_rumi3() || machine_is_msm8960_sim() || + machine_is_msm8960_cdp()) + dummy_socinfo.id = 87; + else if (machine_is_apq8064_rumi3() || machine_is_apq8064_sim()) + dummy_socinfo.id = 109; + else if (machine_is_msm9615_mtp() || machine_is_msm9615_cdp()) + dummy_socinfo.id = 104; + return (void *) &dummy_socinfo; +} + +int __init socinfo_init(void) +{ + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, sizeof(struct socinfo_v6)); + + if (!socinfo) + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, + sizeof(struct socinfo_v5)); + + if (!socinfo) + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, + sizeof(struct socinfo_v4)); + + if (!socinfo) + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, + sizeof(struct socinfo_v3)); + + if (!socinfo) + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, + sizeof(struct socinfo_v2)); + + if (!socinfo) + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, + sizeof(struct socinfo_v1)); + + if (!socinfo) { + pr_warn("%s: Can't find SMEM_HW_SW_BUILD_ID; falling back on " + "dummy values.\n", __func__); + socinfo = setup_dummy_socinfo(); + } + + WARN(!socinfo_get_id(), "Unknown SOC ID!\n"); + WARN(socinfo_get_id() >= ARRAY_SIZE(cpu_of_id), + "New IDs added! ID => CPU mapping might need an update.\n"); + + if (socinfo->v1.id < ARRAY_SIZE(cpu_of_id)) + cur_cpu = cpu_of_id[socinfo->v1.id]; + + switch (socinfo->v1.format) { + case 1: + pr_info("%s: v%u, id=%u, ver=%u.%u\n", + __func__, socinfo->v1.format, socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version)); + break; + case 2: + pr_info("%s: v%u, id=%u, ver=%u.%u, " + "raw_id=%u, raw_ver=%u\n", + __func__, socinfo->v1.format, socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version), + socinfo->v2.raw_id, socinfo->v2.raw_version); + break; + case 3: + pr_info("%s: v%u, id=%u, ver=%u.%u, " + "raw_id=%u, raw_ver=%u, hw_plat=%u\n", + __func__, socinfo->v1.format, socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version), + socinfo->v2.raw_id, socinfo->v2.raw_version, + socinfo->v3.hw_platform); + break; + case 4: + pr_info("%s: v%u, id=%u, ver=%u.%u, " + "raw_id=%u, raw_ver=%u, hw_plat=%u, hw_plat_ver=%u\n", + __func__, socinfo->v1.format, socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version), + socinfo->v2.raw_id, socinfo->v2.raw_version, + socinfo->v3.hw_platform, socinfo->v4.platform_version); + break; + case 5: + pr_info("%s: v%u, id=%u, ver=%u.%u, " + "raw_id=%u, raw_ver=%u, hw_plat=%u, hw_plat_ver=%u\n" + " accessory_chip=%u\n", __func__, socinfo->v1.format, + socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version), + socinfo->v2.raw_id, socinfo->v2.raw_version, + socinfo->v3.hw_platform, socinfo->v4.platform_version, + socinfo->v5.accessory_chip); + break; + case 6: + pr_info("%s: v%u, id=%u, ver=%u.%u, " + "raw_id=%u, raw_ver=%u, hw_plat=%u, hw_plat_ver=%u\n" + " accessory_chip=%u hw_plat_subtype=%u\n", __func__, + socinfo->v1.format, + socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version), + socinfo->v2.raw_id, socinfo->v2.raw_version, + socinfo->v3.hw_platform, socinfo->v4.platform_version, + socinfo->v5.accessory_chip, + socinfo->v6.hw_platform_subtype); + break; + default: + pr_err("%s: Unknown format found\n", __func__); + break; + } + + return 0; +} + +const int get_core_count(void) +{ + if (!(read_cpuid_mpidr() & BIT(31))) + return 1; + + if (read_cpuid_mpidr() & BIT(30) && + !machine_is_msm8960_sim() && + !machine_is_apq8064_sim()) + return 1; + + /* 1 + the PART[1:0] field of MIDR */ + return ((read_cpuid_id() >> 4) & 3) + 1; +} + +const int read_msm_cpu_type(void) +{ + if (machine_is_msm8960_sim()) + return MSM_CPU_8960; + + switch (read_cpuid_id()) { + case 0x510F02D0: + case 0x510F02D2: + case 0x510F02D4: + return MSM_CPU_8X60; + + case 0x510F04D0: + case 0x510F04D1: + case 0x510F04D2: + return MSM_CPU_8960; + + case 0x511F04D0: + if (get_core_count() == 2) + return MSM_CPU_8960; + else + return MSM_CPU_8X30; + + case 0x510F06F0: + return MSM_CPU_8064; + + default: + return MSM_CPU_UNKNOWN; + }; +} + +const int cpu_is_krait_v1(void) +{ + switch (read_cpuid_id()) { + case 0x510F04D0: + case 0x510F04D1: + case 0x510F04D2: + return 1; + + default: + return 0; + }; +} From 970f7b62ec48294e3d516e2c773bff6bf55b6a57 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 16 May 2012 23:49:06 -0500 Subject: [PATCH 2231/2556] msm: memory: checkout cafs/msm-2.6.38 removed pmem functions as we are not ready for them --- arch/arm/mach-msm/Kconfig | 12 + arch/arm/mach-msm/include/mach/memory.h | 117 +++++-- arch/arm/mach-msm/include/mach/msm_memtypes.h | 81 +++++ arch/arm/mach-msm/memory.c | 291 +++++++++++++++++- arch/arm/mach-msm/pm.c | 2 + 5 files changed, 480 insertions(+), 23 deletions(-) create mode 100644 arch/arm/mach-msm/include/mach/msm_memtypes.h diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 4bc6e2747f09d..f977a069fe906 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -407,6 +407,18 @@ config CACHE_FLUSH_RANGE_LIMIT entire cache instead. Flushing a large range can be slower than flushing, then refilling, the entire cache. +config PHYS_OFFSET + hex "Physical Offset" + default "0x40800000" if ARCH_MSM9615 + default "0x80200000" if ARCH_APQ8064 + default "0x80200000" if ARCH_MSM8960 + default "0x10000000" if ARCH_FSM9XXX + default "0x00200000" if !MSM_STACKED_MEMORY + default "0x00000000" if ARCH_QSD8X50 && MSM_SOC_REV_A + default "0x20000000" if ARCH_QSD8X50 + default "0x40200000" if ARCH_MSM8X60 + default "0x10000000" + choice prompt "Default Timer" default MSM7X00A_USE_GP_TIMER diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h index a9f235799fb8b..903b7b7b106d1 100644 --- a/arch/arm/mach-msm/include/mach/memory.h +++ b/arch/arm/mach-msm/include/mach/memory.h @@ -1,6 +1,7 @@ /* arch/arm/mach-msm/include/mach/memory.h * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -12,45 +13,117 @@ * GNU General Public License for more details. * */ - #ifndef __ASM_ARCH_MEMORY_H #define __ASM_ARCH_MEMORY_H +#include /* physical offset of RAM */ -#if defined(CONFIG_ARCH_QSD8X50) && defined(CONFIG_MSM_SOC_REV_A) -#define PHYS_OFFSET UL(0x00000000) -#elif defined(CONFIG_ARCH_QSD8X50) -#define PHYS_OFFSET UL(0x20000000) -#define RESET_VECTOR UL(0x00000000) -#elif defined(CONFIG_ARCH_MSM7X30) -#define PHYS_OFFSET UL(0x00200000) -#define RESET_VECTOR UL(0x00000000) -#elif defined(CONFIG_ARCH_MSM8X60) -#define PHYS_OFFSET UL(0x40200000) -#define RESET_VECTOR UL(0x00000000) -#else -#ifdef CONFIG_MACH_SAPPHIRE -#define PHYS_OFFSET UL(0x02000000) -#define RESET_VECTOR UL(0x00000000) -#else -#define PHYS_OFFSET UL(0x10000000) -#define RESET_VECTOR UL(0x00000000) +#define PHYS_OFFSET UL(CONFIG_PHYS_OFFSET) + +#define MAX_PHYSMEM_BITS 32 +#define SECTION_SIZE_BITS 29 + +/* Maximum number of Memory Regions */ +#define MAX_NR_REGIONS 4 + +/* Certain configurations of MSM7x30 have multiple memory banks. +* One or more of these banks can contain holes in the memory map as well. +* These macros define appropriate conversion routines between the physical +* and virtual address domains for supporting these configurations using +* SPARSEMEM and a 3G/1G VM split. +*/ + +#if defined(CONFIG_ARCH_MSM7X30) + +#define EBI0_PHYS_OFFSET PHYS_OFFSET +#define EBI0_PAGE_OFFSET PAGE_OFFSET +#define EBI0_SIZE 0x10000000 + +#define EBI1_PHYS_OFFSET 0x40000000 +#define EBI1_PAGE_OFFSET (EBI0_PAGE_OFFSET + EBI0_SIZE) + +#if (defined(CONFIG_SPARSEMEM) && defined(CONFIG_VMSPLIT_3G)) + +#define __phys_to_virt(phys) \ + ((phys) >= EBI1_PHYS_OFFSET ? \ + (phys) - EBI1_PHYS_OFFSET + EBI1_PAGE_OFFSET : \ + (phys) - EBI0_PHYS_OFFSET + EBI0_PAGE_OFFSET) + +#define __virt_to_phys(virt) \ + ((virt) >= EBI1_PAGE_OFFSET ? \ + (virt) - EBI1_PAGE_OFFSET + EBI1_PHYS_OFFSET : \ + (virt) - EBI0_PAGE_OFFSET + EBI0_PHYS_OFFSET) + #endif + #endif #define HAS_ARCH_IO_REMAP_PFN_RANGE #ifndef __ASSEMBLY__ +void *alloc_bootmem_aligned(unsigned long size, unsigned long alignment); +void *allocate_contiguous_ebi(unsigned long, unsigned long, int); +unsigned long allocate_contiguous_ebi_nomap(unsigned long, unsigned long); void clean_and_invalidate_caches(unsigned long, unsigned long, unsigned long); void clean_caches(unsigned long, unsigned long, unsigned long); void invalidate_caches(unsigned long, unsigned long, unsigned long); +int platform_physical_remove_pages(u64, u64); +int platform_physical_active_pages(u64, u64); +int platform_physical_low_power_pages(u64, u64); + +extern int (*change_memory_power)(u64, u64, int); + +#if defined(CONFIG_ARCH_MSM_ARM11) || defined(CONFIG_ARCH_MSM_CORTEX_A5) +void write_to_strongly_ordered_memory(void); +void map_page_strongly_ordered(void); #endif -#define CONSISTENT_DMA_SIZE (4*SZ_1M) +#ifdef CONFIG_CACHE_L2X0 +extern void l2x0_cache_sync(void); +#define finish_arch_switch(prev) do { l2x0_cache_sync(); } while (0) +#endif + +#if defined(CONFIG_ARCH_MSM8X60) || defined(CONFIG_ARCH_MSM8960) +extern void store_ttbr0(void); +#define finish_arch_switch(prev) do { store_ttbr0(); } while (0) +#endif + +#ifdef CONFIG_DONT_MAP_HOLE_AFTER_MEMBANK0 +extern unsigned long membank0_size; +extern unsigned long membank1_start; + +#define MEMBANK0_PHYS_OFFSET PHYS_OFFSET +#define MEMBANK0_PAGE_OFFSET PAGE_OFFSET + +#define MEMBANK1_PHYS_OFFSET (membank1_start) +#define MEMBANK1_PAGE_OFFSET (MEMBANK0_PAGE_OFFSET + (membank0_size)) + +#define __phys_to_virt(phys) \ + ((MEMBANK1_PHYS_OFFSET && ((phys) >= MEMBANK1_PHYS_OFFSET)) ? \ + (phys) - MEMBANK1_PHYS_OFFSET + MEMBANK1_PAGE_OFFSET : \ + (phys) - MEMBANK0_PHYS_OFFSET + MEMBANK0_PAGE_OFFSET) -#ifdef CONFIG_ARCH_MSM_SCORPION -#define arch_has_speculative_dfetch() 1 +#define __virt_to_phys(virt) \ + ((MEMBANK1_PHYS_OFFSET && ((virt) >= MEMBANK1_PAGE_OFFSET)) ? \ + (virt) - MEMBANK1_PAGE_OFFSET + MEMBANK1_PHYS_OFFSET : \ + (virt) - MEMBANK0_PAGE_OFFSET + MEMBANK0_PHYS_OFFSET) #endif #endif +#if defined CONFIG_ARCH_MSM_SCORPION || defined CONFIG_ARCH_MSM_KRAIT +#define arch_has_speculative_dfetch() 1 +#endif + +#endif + +/* these correspond to values known by the modem */ +#define MEMORY_DEEP_POWERDOWN 0 +#define MEMORY_SELF_REFRESH 1 +#define MEMORY_ACTIVE 2 + +#define NPA_MEMORY_NODE_NAME "/mem/apps/ddr_dpd" + +#ifndef CONFIG_ARCH_MSM7X27 +#define CONSISTENT_DMA_SIZE (SZ_1M * 14) +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_memtypes.h b/arch/arm/mach-msm/include/mach/msm_memtypes.h new file mode 100644 index 0000000000000..406260a92d227 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_memtypes.h @@ -0,0 +1,81 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* The MSM Hardware supports multiple flavors of physical memory. + * This file captures hardware specific information of these types. +*/ + +#ifndef __ASM_ARCH_MSM_MEMTYPES_H +#define __ASM_ARCH_MSM_MEMTYPES_H + +#include +#include + +int __init meminfo_init(unsigned int, unsigned int); +/* Redundant check to prevent this from being included outside of 7x30 */ +#if defined(CONFIG_ARCH_MSM7X30) +unsigned int get_num_populated_chipselects(void); +#endif + +unsigned int get_num_memory_banks(void); +unsigned int get_memory_bank_size(unsigned int); +unsigned int get_memory_bank_start(unsigned int); +int soc_change_memory_power(u64, u64, int); + +enum { + MEMTYPE_NONE = -1, + MEMTYPE_SMI_KERNEL = 0, + MEMTYPE_SMI, + MEMTYPE_EBI0, + MEMTYPE_EBI1, + MEMTYPE_MAX, +}; + +void msm_reserve(void); + +#define MEMTYPE_FLAGS_FIXED 0x1 +#define MEMTYPE_FLAGS_1M_ALIGN 0x2 + +struct memtype_reserve { + unsigned long start; + unsigned long size; + unsigned long limit; + int flags; +}; + +struct reserve_info { + struct memtype_reserve *memtype_reserve_table; + void (*calculate_reserve_sizes)(void); + int (*paddr_to_memtype)(unsigned int); + unsigned long low_unstable_address; + unsigned long max_unstable_size; + unsigned long bank_size; +}; + +extern struct reserve_info *reserve_info; +#endif diff --git a/arch/arm/mach-msm/memory.c b/arch/arm/mach-msm/memory.c index 4157211eb6119..21a3db208fb93 100644 --- a/arch/arm/mach-msm/memory.c +++ b/arch/arm/mach-msm/memory.c @@ -1,6 +1,7 @@ /* arch/arm/mach-msm/memory.c * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -15,7 +16,27 @@ #include #include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_MSM_NPA_REMOTE) +#include "npa_remote.h" +#include +#include +#endif +#include +#include +#include +#include <../../mm/mm.h> int arch_io_remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t prot) @@ -23,11 +44,64 @@ int arch_io_remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn_addr = pfn << PAGE_SHIFT; if ((pfn_addr >= 0x88000000) && (pfn_addr < 0xD0000000)) { prot = pgprot_device(prot); - printk("remapping device %lx\n", prot); + pr_debug("remapping device %lx\n", prot); } return remap_pfn_range(vma, addr, pfn, size, prot); } +void *strongly_ordered_page; +char strongly_ordered_mem[PAGE_SIZE*2-4]; + +void map_page_strongly_ordered(void) +{ +#if defined(CONFIG_ARCH_MSM7X27) && !defined(CONFIG_ARCH_MSM7X27A) + long unsigned int phys; + struct map_desc map; + + if (strongly_ordered_page) + return; + + strongly_ordered_page = (void*)PFN_ALIGN((int)&strongly_ordered_mem); + phys = __pa(strongly_ordered_page); + + map.pfn = __phys_to_pfn(phys); + map.virtual = MSM_STRONGLY_ORDERED_PAGE; + map.length = PAGE_SIZE; + map.type = MT_DEVICE_STRONGLY_ORDERED; + create_mapping(&map); + + printk(KERN_ALERT "Initialized strongly ordered page successfully\n"); +#endif +} +EXPORT_SYMBOL(map_page_strongly_ordered); + +void write_to_strongly_ordered_memory(void) +{ +#if defined(CONFIG_ARCH_MSM7X27) && !defined(CONFIG_ARCH_MSM7X27A) + if (!strongly_ordered_page) { + if (!in_interrupt()) + map_page_strongly_ordered(); + else { + printk(KERN_ALERT "Cannot map strongly ordered page in " + "Interrupt Context\n"); + /* capture it here before the allocation fails later */ + BUG(); + } + } + *(int *)MSM_STRONGLY_ORDERED_PAGE = 0; +#endif +} +EXPORT_SYMBOL(write_to_strongly_ordered_memory); + +void flush_axi_bus_buffer(void) +{ +#if defined(CONFIG_ARCH_MSM7X27) && !defined(CONFIG_ARCH_MSM7X27A) + __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" \ + : : "r" (0) : "memory"); + write_to_strongly_ordered_memory(); +#endif +} + #define CACHE_LINE_SIZE 32 /* These cache related routines make the assumption that the associated @@ -46,6 +120,8 @@ void clean_and_invalidate_caches(unsigned long vstart, #endif asm ("mcr p15, 0, %0, c7, c10, 4" : : "r" (0)); asm ("mcr p15, 0, %0, c7, c5, 0" : : "r" (0)); + + flush_axi_bus_buffer(); } void clean_caches(unsigned long vstart, @@ -60,6 +136,8 @@ void clean_caches(unsigned long vstart, #endif asm ("mcr p15, 0, %0, c7, c10, 4" : : "r" (0)); asm ("mcr p15, 0, %0, c7, c5, 0" : : "r" (0)); + + flush_axi_bus_buffer(); } void invalidate_caches(unsigned long vstart, @@ -74,4 +152,215 @@ void invalidate_caches(unsigned long vstart, #endif asm ("mcr p15, 0, %0, c7, c10, 4" : : "r" (0)); asm ("mcr p15, 0, %0, c7, c5, 0" : : "r" (0)); + + flush_axi_bus_buffer(); +} + +void *alloc_bootmem_aligned(unsigned long size, unsigned long alignment) +{ + void *unused_addr = NULL; + unsigned long addr, tmp_size, unused_size; + + /* Allocate maximum size needed, see where it ends up. + * Then free it -- in this path there are no other allocators + * so we can depend on getting the same address back + * when we allocate a smaller piece that is aligned + * at the end (if necessary) and the piece we really want, + * then free the unused first piece. + */ + + tmp_size = size + alignment - PAGE_SIZE; + addr = (unsigned long)alloc_bootmem(tmp_size); + free_bootmem(__pa(addr), tmp_size); + + unused_size = alignment - (addr % alignment); + if (unused_size) + unused_addr = alloc_bootmem(unused_size); + + addr = (unsigned long)alloc_bootmem(size); + if (unused_size) + free_bootmem(__pa(unused_addr), unused_size); + + return (void *)addr; +} + +int (*change_memory_power)(u64, u64, int); + +int platform_physical_remove_pages(u64 start, u64 size) +{ + if (!change_memory_power) + return 0; + return change_memory_power(start, size, MEMORY_DEEP_POWERDOWN); +} + +int platform_physical_active_pages(u64 start, u64 size) +{ + if (!change_memory_power) + return 0; + return change_memory_power(start, size, MEMORY_ACTIVE); +} + +int platform_physical_low_power_pages(u64 start, u64 size) +{ + if (!change_memory_power) + return 0; + return change_memory_power(start, size, MEMORY_SELF_REFRESH); +} + +char *memtype_name[] = { + "SMI_KERNEL", + "SMI", + "EBI0", + "EBI1" +}; + +struct reserve_info *reserve_info; + +static unsigned long stable_size(struct membank *mb, + unsigned long unstable_limit) +{ + if (!unstable_limit || mb->start + mb->size <= unstable_limit) + return mb->size; + if (mb->start >= unstable_limit) + return 0; + return unstable_limit - mb->start; +} + +static void __init calculate_reserve_limits(void) +{ + int i; + struct membank *mb; + int memtype; + struct memtype_reserve *mt; + unsigned long size; + + for (i = 0, mb = &meminfo.bank[0]; i < meminfo.nr_banks; i++, mb++) { + memtype = reserve_info->paddr_to_memtype(mb->start); + if (memtype == MEMTYPE_NONE) { + pr_warning("unknown memory type for bank at %lx\n", + mb->start); + continue; + } + mt = &reserve_info->memtype_reserve_table[memtype]; + size = stable_size(mb, reserve_info->low_unstable_address); + mt->limit = max(mt->limit, size); + } +} + +static void __init adjust_reserve_sizes(void) +{ + int i; + struct memtype_reserve *mt; + + mt = &reserve_info->memtype_reserve_table[0]; + for (i = 0; i < MEMTYPE_MAX; i++, mt++) { + if (mt->flags & MEMTYPE_FLAGS_1M_ALIGN) + mt->size = (mt->size + SECTION_SIZE - 1) & SECTION_MASK; + if (mt->size > mt->limit) { + pr_warning("%lx size for %s too large, setting to %lx\n", + mt->size, memtype_name[i], mt->limit); + mt->size = mt->limit; + } + } +} + +static void __init reserve_memory_for_mempools(void) +{ + int i, memtype, membank_type; + struct memtype_reserve *mt; + struct membank *mb; + int ret; + unsigned long size; + + mt = &reserve_info->memtype_reserve_table[0]; + for (memtype = 0; memtype < MEMTYPE_MAX; memtype++, mt++) { + if (mt->flags & MEMTYPE_FLAGS_FIXED || !mt->size) + continue; + + /* We know we will find a memory bank of the proper size + * as we have limited the size of the memory pool for + * each memory type to the size of the largest memory + * bank. Choose the memory bank with the highest physical + * address which is large enough, so that we will not + * take memory from the lowest memory bank which the kernel + * is in (and cause boot problems) and so that we might + * be able to steal memory that would otherwise become + * highmem. However, do not use unstable memory. + */ + for (i = meminfo.nr_banks - 1; i >= 0; i--) { + mb = &meminfo.bank[i]; + membank_type = + reserve_info->paddr_to_memtype(mb->start); + if (memtype != membank_type) + continue; + size = stable_size(mb, + reserve_info->low_unstable_address); + if (size >= mt->size) { + mt->start = mb->start + size - mt->size; + ret = memblock_remove(mt->start, mt->size); + BUG_ON(ret); + break; + } + } + } +} + +static void __init initialize_mempools(void) +{ + struct mem_pool *mpool; + int memtype; + struct memtype_reserve *mt; + + mt = &reserve_info->memtype_reserve_table[0]; + for (memtype = 0; memtype < MEMTYPE_MAX; memtype++, mt++) { + if (!mt->size) + continue; + mpool = initialize_memory_pool(mt->start, mt->size, memtype); + if (!mpool) + pr_warning("failed to create %s mempool\n", + memtype_name[memtype]); + } +} + +void __init msm_reserve(void) +{ + memory_pool_init(); + reserve_info->calculate_reserve_sizes(); + calculate_reserve_limits(); + adjust_reserve_sizes(); + reserve_memory_for_mempools(); + initialize_mempools(); +} + +static int get_ebi_memtype(void) +{ + /* on 7x30 and 8x55 "EBI1 kernel PMEM" is really on EBI0 */ + if (cpu_is_msm7x30() || cpu_is_msm8x55()) + return MEMTYPE_EBI0; + return MEMTYPE_EBI1; +} + +void *allocate_contiguous_ebi(unsigned long size, + unsigned long align, int cached) +{ + return allocate_contiguous_memory(size, get_ebi_memtype(), + align, cached); +} +EXPORT_SYMBOL(allocate_contiguous_ebi); + +unsigned long allocate_contiguous_ebi_nomap(unsigned long size, + unsigned long align) +{ + return _allocate_contiguous_memory_nomap(size, get_ebi_memtype(), + align, __builtin_return_address(0)); +} +EXPORT_SYMBOL(allocate_contiguous_ebi_nomap); + +unsigned int msm_ttbr0; + +void store_ttbr0(void) +{ + /* Store TTBR0 for post-mortem debugging purposes. */ + asm("mrc p15, 0, %0, c2, c0, 0\n" + : "=r" (msm_ttbr0)); } diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c index 801ab7643c172..f182233a09bda 100644 --- a/arch/arm/mach-msm/pm.c +++ b/arch/arm/mach-msm/pm.c @@ -41,6 +41,8 @@ #include #endif +#define RESET_VECTOR UL(0x00000000) + enum { MSM_PM_DEBUG_SUSPEND = 1U << 0, MSM_PM_DEBUG_POWER_COLLAPSE = 1U << 1, From 718263b16d9c90c6b77b56ea6ddd6a76075580b8 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 17 May 2012 00:04:50 -0500 Subject: [PATCH 2232/2556] configs: set PHYS_OFFSET --- arch/arm/configs/evervolv_bravo_defconfig | 1 + arch/arm/configs/evervolv_incrediblec_defconfig | 1 + arch/arm/configs/evervolv_mahimahi_defconfig | 8 +++++++- arch/arm/configs/evervolv_supersonic_defconfig | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index 575176ec0b9a5..d99e1635ace42 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -310,6 +310,7 @@ CONFIG_HTC_PWRSPLY=y # CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set # CONFIG_HTC_ONMODE_CHARGING is not set CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_PHYS_OFFSET=0x20000000 CONFIG_MSM7X00A_USE_GP_TIMER=y # CONFIG_MSM7X00A_USE_DG_TIMER is not set CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index 84b8399fbc393..0c2807df4b0d5 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -308,6 +308,7 @@ CONFIG_HTC_BATTCHG_SMEM=y # CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set # CONFIG_HTC_ONMODE_CHARGING is not set CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_PHYS_OFFSET=0x20000000 CONFIG_MSM7X00A_USE_GP_TIMER=y # CONFIG_MSM7X00A_USE_DG_TIMER is not set CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index 66c44a86f6d54..0811553308b72 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Mon Apr 23 23:58:08 2012 +# Wed May 16 23:42:48 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -310,6 +310,7 @@ CONFIG_HTC_PWRSPLY=y # CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set # CONFIG_HTC_ONMODE_CHARGING is not set CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_PHYS_OFFSET=0x20000000 CONFIG_MSM7X00A_USE_GP_TIMER=y # CONFIG_MSM7X00A_USE_DG_TIMER is not set CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y @@ -383,11 +384,15 @@ CONFIG_ARM_THUMBEE=y # CONFIG_CPU_BPREDICT_DISABLE is not set CONFIG_ARM_L1_CACHE_SHIFT=5 CONFIG_ARM_DMA_MEM_BUFFERABLE=y +# CONFIG_VCM is not set +# CONFIG_STRICT_MEMORY_RWX is not set +# CONFIG_RESERVE_FIRST_PAGE is not set CONFIG_CPU_HAS_PMU=y CONFIG_ARM_ERRATA_430973=y CONFIG_ARM_ERRATA_458693=y CONFIG_ARM_ERRATA_460075=y CONFIG_ARM_ERRATA_743622=y +# CONFIG_KSAPI is not set # CONFIG_FIQ_DEBUGGER is not set # @@ -439,6 +444,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_SECCOMP is not set # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_CP_ACCESS is not set # # Boot options diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index ff486797d1a33..f69d61a5d0220 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -315,6 +315,7 @@ CONFIG_HTC_BATTCHG_SMEM=y # CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set # CONFIG_HTC_ONMODE_CHARGING is not set CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_PHYS_OFFSET=0x20000000 CONFIG_MSM7X00A_USE_GP_TIMER=y # CONFIG_MSM7X00A_USE_DG_TIMER is not set CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y From bbeac04128e408774ce9d4aec7a05bcf2c788f8f Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 17 May 2012 00:09:01 -0500 Subject: [PATCH 2233/2556] kgsl: checkout per d8f1c6b7fab801f8ca This is the latest kgsl commit in the long list of cherry-picks a few months ago. this brings us up to date with that. adding all the memory changes i ommitted before. board is now booting with changes required to build those parts. commit d8f1c6b7fab801f8ca05eb06045dd82cf046525c Author: Jordan Crouse Date: Tue Oct 4 09:31:29 2011 -0600 msm: kgsl: Cleanup virtual files on module exit Make sure that all sysfs and debugfs entries are cleaned up when the driver exits. Signed-off-by: Jordan Crouse --- drivers/gpu/msm/Kconfig | 18 +- drivers/gpu/msm/Makefile | 9 +- drivers/gpu/msm/a2xx_reg.h | 418 ++++++ drivers/gpu/msm/adreno.c | 542 +++---- drivers/gpu/msm/adreno.h | 102 +- drivers/gpu/msm/adreno_a2xx.c | 1606 ++++++++++++++++++++ drivers/gpu/msm/adreno_debugfs.c | 9 +- drivers/gpu/msm/adreno_debugfs.h | 30 +- drivers/gpu/msm/adreno_drawctxt.c | 1615 ++------------------- drivers/gpu/msm/adreno_drawctxt.h | 100 +- drivers/gpu/msm/adreno_pm4types.h | 168 +-- drivers/gpu/msm/adreno_postmortem.c | 125 +- drivers/gpu/msm/adreno_postmortem.h | 30 +- drivers/gpu/msm/adreno_ringbuffer.c | 201 +-- drivers/gpu/msm/adreno_ringbuffer.h | 32 +- drivers/gpu/msm/kgsl.c | 189 +-- drivers/gpu/msm/kgsl.h | 151 +- drivers/gpu/msm/kgsl_cffdump.c | 12 +- drivers/gpu/msm/kgsl_cffdump.h | 30 +- drivers/gpu/msm/kgsl_debugfs.c | 1 + drivers/gpu/msm/kgsl_device.h | 196 ++- drivers/gpu/msm/kgsl_drm.c | 2 +- drivers/gpu/msm/kgsl_gpummu.c | 787 ++++++++++ drivers/gpu/msm/kgsl_gpummu.h | 85 ++ drivers/gpu/msm/kgsl_iommu.c | 353 +++++ drivers/gpu/msm/kgsl_log.h | 30 +- drivers/gpu/msm/kgsl_mmu.c | 960 ++++-------- drivers/gpu/msm/kgsl_mmu.h | 312 ++-- drivers/gpu/msm/kgsl_pwrctrl.c | 202 ++- drivers/gpu/msm/kgsl_pwrctrl.h | 49 +- drivers/gpu/msm/kgsl_pwrscale.c | 71 +- drivers/gpu/msm/kgsl_pwrscale.h | 36 +- drivers/gpu/msm/kgsl_pwrscale_idlestats.c | 134 ++ drivers/gpu/msm/kgsl_pwrscale_trustzone.c | 197 +++ drivers/gpu/msm/kgsl_sharedmem.c | 129 +- drivers/gpu/msm/kgsl_sharedmem.h | 78 +- drivers/gpu/msm/z180.c | 410 ++---- drivers/gpu/msm/z180.h | 32 +- drivers/gpu/msm/z180_reg.h | 58 +- include/linux/msm_kgsl.h | 36 +- 40 files changed, 5455 insertions(+), 4090 deletions(-) create mode 100644 drivers/gpu/msm/a2xx_reg.h create mode 100644 drivers/gpu/msm/adreno_a2xx.c create mode 100644 drivers/gpu/msm/kgsl_gpummu.c create mode 100644 drivers/gpu/msm/kgsl_gpummu.h create mode 100644 drivers/gpu/msm/kgsl_iommu.c create mode 100644 drivers/gpu/msm/kgsl_pwrscale_idlestats.c create mode 100644 drivers/gpu/msm/kgsl_pwrscale_trustzone.c diff --git a/drivers/gpu/msm/Kconfig b/drivers/gpu/msm/Kconfig index 64cbc304c0968..5852e2698b33e 100644 --- a/drivers/gpu/msm/Kconfig +++ b/drivers/gpu/msm/Kconfig @@ -64,22 +64,30 @@ config MSM_KGSL_DRM bool "Build a DRM interface for the MSM_KGSL driver" depends on MSM_KGSL && DRM -config MSM_KGSL_MMU +config MSM_KGSL_GPUMMU bool "Enable the GPU MMU in the MSM_KGSL driver" - depends on MSM_KGSL && MMU && !MSM_KGSL_CFF_DUMP + depends on MSM_KGSL && !MSM_KGSL_CFF_DUMP + default y + +config MSM_KGSL_IOMMU + bool "Enable the use of IOMMU in the MSM_KGSL driver" + depends on MSM_KGSL && MSM_IOMMU && !MSM_KGSL_GPUMMU && !MSM_KGSL_CFF_DUMP + +config MSM_KGSL_MMU + bool + depends on MSM_KGSL_GPUMMU || MSM_KGSL_IOMMU default y config KGSL_PER_PROCESS_PAGE_TABLE bool "Enable Per Process page tables for the KGSL driver" default n - depends on MSM_KGSL_MMU && !MSM_KGSL_DRM + depends on MSM_KGSL_GPUMMU && !MSM_KGSL_DRM ---help--- The MMU will use per process pagetables when enabled. config MSM_KGSL_PAGE_TABLE_SIZE hex "Size of pagetables" default 0xFFF0000 - depends on MSM_KGSL_MMU ---help--- Sets the pagetable size used by the MMU. The max value is 0xFFF0000 or (256M - 64K). @@ -97,7 +105,7 @@ config MSM_KGSL_PAGE_TABLE_COUNT config MSM_KGSL_MMU_PAGE_FAULT bool "Force the GPU MMU to page fault for unmapped regions" default y - depends on MSM_KGSL_MMU + depends on MSM_KGSL_GPUMMU config MSM_KGSL_DISABLE_SHADOW_WRITES bool "Disable register shadow writes for context switches" diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile index c905bfecfc600..b4cd286c86626 100644 --- a/drivers/gpu/msm/Makefile +++ b/drivers/gpu/msm/Makefile @@ -4,17 +4,22 @@ msm_kgsl_core-y = \ kgsl.o \ kgsl_sharedmem.o \ kgsl_pwrctrl.o \ - kgsl_pwrscale.o + kgsl_pwrscale.o \ + kgsl_mmu.o \ + kgsl_gpummu.o \ + kgsl_iommu.o msm_kgsl_core-$(CONFIG_DEBUG_FS) += kgsl_debugfs.o -msm_kgsl_core-$(CONFIG_MSM_KGSL_MMU) += kgsl_mmu.o msm_kgsl_core-$(CONFIG_MSM_KGSL_CFF_DUMP) += kgsl_cffdump.o msm_kgsl_core-$(CONFIG_MSM_KGSL_DRM) += kgsl_drm.o +msm_kgsl_core-$(CONFIG_MSM_SCM) += kgsl_pwrscale_trustzone.o +msm_kgsl_core-$(CONFIG_MSM_SLEEP_STATS) += kgsl_pwrscale_idlestats.o msm_adreno-y += \ adreno_ringbuffer.o \ adreno_drawctxt.o \ adreno_postmortem.o \ + adreno_a2xx.o \ adreno.o msm_adreno-$(CONFIG_DEBUG_FS) += adreno_debugfs.o diff --git a/drivers/gpu/msm/a2xx_reg.h b/drivers/gpu/msm/a2xx_reg.h new file mode 100644 index 0000000000000..d859d61c4742c --- /dev/null +++ b/drivers/gpu/msm/a2xx_reg.h @@ -0,0 +1,418 @@ +/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __A200_REG_H +#define __A200_REG_H + +enum VGT_EVENT_TYPE { + VS_DEALLOC = 0, + PS_DEALLOC = 1, + VS_DONE_TS = 2, + PS_DONE_TS = 3, + CACHE_FLUSH_TS = 4, + CONTEXT_DONE = 5, + CACHE_FLUSH = 6, + VIZQUERY_START = 7, + VIZQUERY_END = 8, + SC_WAIT_WC = 9, + RST_PIX_CNT = 13, + RST_VTX_CNT = 14, + TILE_FLUSH = 15, + CACHE_FLUSH_AND_INV_TS_EVENT = 20, + ZPASS_DONE = 21, + CACHE_FLUSH_AND_INV_EVENT = 22, + PERFCOUNTER_START = 23, + PERFCOUNTER_STOP = 24, + VS_FETCH_DONE = 27, + FACENESS_FLUSH = 28, +}; + +enum COLORFORMATX { + COLORX_4_4_4_4 = 0, + COLORX_1_5_5_5 = 1, + COLORX_5_6_5 = 2, + COLORX_8 = 3, + COLORX_8_8 = 4, + COLORX_8_8_8_8 = 5, + COLORX_S8_8_8_8 = 6, + COLORX_16_FLOAT = 7, + COLORX_16_16_FLOAT = 8, + COLORX_16_16_16_16_FLOAT = 9, + COLORX_32_FLOAT = 10, + COLORX_32_32_FLOAT = 11, + COLORX_32_32_32_32_FLOAT = 12, + COLORX_2_3_3 = 13, + COLORX_8_8_8 = 14, +}; + +enum SURFACEFORMAT { + FMT_1_REVERSE = 0, + FMT_1 = 1, + FMT_8 = 2, + FMT_1_5_5_5 = 3, + FMT_5_6_5 = 4, + FMT_6_5_5 = 5, + FMT_8_8_8_8 = 6, + FMT_2_10_10_10 = 7, + FMT_8_A = 8, + FMT_8_B = 9, + FMT_8_8 = 10, + FMT_Cr_Y1_Cb_Y0 = 11, + FMT_Y1_Cr_Y0_Cb = 12, + FMT_5_5_5_1 = 13, + FMT_8_8_8_8_A = 14, + FMT_4_4_4_4 = 15, + FMT_10_11_11 = 16, + FMT_11_11_10 = 17, + FMT_DXT1 = 18, + FMT_DXT2_3 = 19, + FMT_DXT4_5 = 20, + FMT_24_8 = 22, + FMT_24_8_FLOAT = 23, + FMT_16 = 24, + FMT_16_16 = 25, + FMT_16_16_16_16 = 26, + FMT_16_EXPAND = 27, + FMT_16_16_EXPAND = 28, + FMT_16_16_16_16_EXPAND = 29, + FMT_16_FLOAT = 30, + FMT_16_16_FLOAT = 31, + FMT_16_16_16_16_FLOAT = 32, + FMT_32 = 33, + FMT_32_32 = 34, + FMT_32_32_32_32 = 35, + FMT_32_FLOAT = 36, + FMT_32_32_FLOAT = 37, + FMT_32_32_32_32_FLOAT = 38, + FMT_32_AS_8 = 39, + FMT_32_AS_8_8 = 40, + FMT_16_MPEG = 41, + FMT_16_16_MPEG = 42, + FMT_8_INTERLACED = 43, + FMT_32_AS_8_INTERLACED = 44, + FMT_32_AS_8_8_INTERLACED = 45, + FMT_16_INTERLACED = 46, + FMT_16_MPEG_INTERLACED = 47, + FMT_16_16_MPEG_INTERLACED = 48, + FMT_DXN = 49, + FMT_8_8_8_8_AS_16_16_16_16 = 50, + FMT_DXT1_AS_16_16_16_16 = 51, + FMT_DXT2_3_AS_16_16_16_16 = 52, + FMT_DXT4_5_AS_16_16_16_16 = 53, + FMT_2_10_10_10_AS_16_16_16_16 = 54, + FMT_10_11_11_AS_16_16_16_16 = 55, + FMT_11_11_10_AS_16_16_16_16 = 56, + FMT_32_32_32_FLOAT = 57, + FMT_DXT3A = 58, + FMT_DXT5A = 59, + FMT_CTX1 = 60, + FMT_DXT3A_AS_1_1_1_1 = 61 +}; + +#define REG_PERF_MODE_CNT 0x0 +#define REG_PERF_STATE_RESET 0x0 +#define REG_PERF_STATE_ENABLE 0x1 +#define REG_PERF_STATE_FREEZE 0x2 + +#define RB_EDRAM_INFO_EDRAM_SIZE_SIZE 4 +#define RB_EDRAM_INFO_EDRAM_MAPPING_MODE_SIZE 2 +#define RB_EDRAM_INFO_UNUSED0_SIZE 8 +#define RB_EDRAM_INFO_EDRAM_RANGE_SIZE 18 + +struct rb_edram_info_t { + unsigned int edram_size:RB_EDRAM_INFO_EDRAM_SIZE_SIZE; + unsigned int edram_mapping_mode:RB_EDRAM_INFO_EDRAM_MAPPING_MODE_SIZE; + unsigned int unused0:RB_EDRAM_INFO_UNUSED0_SIZE; + unsigned int edram_range:RB_EDRAM_INFO_EDRAM_RANGE_SIZE; +}; + +union reg_rb_edram_info { + unsigned int val; + struct rb_edram_info_t f; +}; + +#define RBBM_READ_ERROR_UNUSED0_SIZE 2 +#define RBBM_READ_ERROR_READ_ADDRESS_SIZE 15 +#define RBBM_READ_ERROR_UNUSED1_SIZE 13 +#define RBBM_READ_ERROR_READ_REQUESTER_SIZE 1 +#define RBBM_READ_ERROR_READ_ERROR_SIZE 1 + +struct rbbm_read_error_t { + unsigned int unused0:RBBM_READ_ERROR_UNUSED0_SIZE; + unsigned int read_address:RBBM_READ_ERROR_READ_ADDRESS_SIZE; + unsigned int unused1:RBBM_READ_ERROR_UNUSED1_SIZE; + unsigned int read_requester:RBBM_READ_ERROR_READ_REQUESTER_SIZE; + unsigned int read_error:RBBM_READ_ERROR_READ_ERROR_SIZE; +}; + +union rbbm_read_error_u { + unsigned int val:32; + struct rbbm_read_error_t f; +}; + +#define CP_RB_CNTL_RB_BUFSZ_SIZE 6 +#define CP_RB_CNTL_UNUSED0_SIZE 2 +#define CP_RB_CNTL_RB_BLKSZ_SIZE 6 +#define CP_RB_CNTL_UNUSED1_SIZE 2 +#define CP_RB_CNTL_BUF_SWAP_SIZE 2 +#define CP_RB_CNTL_UNUSED2_SIZE 2 +#define CP_RB_CNTL_RB_POLL_EN_SIZE 1 +#define CP_RB_CNTL_UNUSED3_SIZE 6 +#define CP_RB_CNTL_RB_NO_UPDATE_SIZE 1 +#define CP_RB_CNTL_UNUSED4_SIZE 3 +#define CP_RB_CNTL_RB_RPTR_WR_ENA_SIZE 1 + +struct cp_rb_cntl_t { + unsigned int rb_bufsz:CP_RB_CNTL_RB_BUFSZ_SIZE; + unsigned int unused0:CP_RB_CNTL_UNUSED0_SIZE; + unsigned int rb_blksz:CP_RB_CNTL_RB_BLKSZ_SIZE; + unsigned int unused1:CP_RB_CNTL_UNUSED1_SIZE; + unsigned int buf_swap:CP_RB_CNTL_BUF_SWAP_SIZE; + unsigned int unused2:CP_RB_CNTL_UNUSED2_SIZE; + unsigned int rb_poll_en:CP_RB_CNTL_RB_POLL_EN_SIZE; + unsigned int unused3:CP_RB_CNTL_UNUSED3_SIZE; + unsigned int rb_no_update:CP_RB_CNTL_RB_NO_UPDATE_SIZE; + unsigned int unused4:CP_RB_CNTL_UNUSED4_SIZE; + unsigned int rb_rptr_wr_ena:CP_RB_CNTL_RB_RPTR_WR_ENA_SIZE; +}; + +union reg_cp_rb_cntl { + unsigned int val:32; + struct cp_rb_cntl_t f; +}; + +#define RB_COLOR_INFO__COLOR_FORMAT_MASK 0x0000000fL +#define RB_COPY_DEST_INFO__COPY_DEST_FORMAT__SHIFT 0x00000004 + + +#define SQ_INT_CNTL__PS_WATCHDOG_MASK 0x00000001L +#define SQ_INT_CNTL__VS_WATCHDOG_MASK 0x00000002L + +#define RBBM_INT_CNTL__RDERR_INT_MASK 0x00000001L +#define RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK 0x00000002L +#define RBBM_INT_CNTL__GUI_IDLE_INT_MASK 0x00080000L + +#define RBBM_STATUS__CMDFIFO_AVAIL_MASK 0x0000001fL +#define RBBM_STATUS__TC_BUSY_MASK 0x00000020L +#define RBBM_STATUS__HIRQ_PENDING_MASK 0x00000100L +#define RBBM_STATUS__CPRQ_PENDING_MASK 0x00000200L +#define RBBM_STATUS__CFRQ_PENDING_MASK 0x00000400L +#define RBBM_STATUS__PFRQ_PENDING_MASK 0x00000800L +#define RBBM_STATUS__VGT_BUSY_NO_DMA_MASK 0x00001000L +#define RBBM_STATUS__RBBM_WU_BUSY_MASK 0x00004000L +#define RBBM_STATUS__CP_NRT_BUSY_MASK 0x00010000L +#define RBBM_STATUS__MH_BUSY_MASK 0x00040000L +#define RBBM_STATUS__MH_COHERENCY_BUSY_MASK 0x00080000L +#define RBBM_STATUS__SX_BUSY_MASK 0x00200000L +#define RBBM_STATUS__TPC_BUSY_MASK 0x00400000L +#define RBBM_STATUS__SC_CNTX_BUSY_MASK 0x01000000L +#define RBBM_STATUS__PA_BUSY_MASK 0x02000000L +#define RBBM_STATUS__VGT_BUSY_MASK 0x04000000L +#define RBBM_STATUS__SQ_CNTX17_BUSY_MASK 0x08000000L +#define RBBM_STATUS__SQ_CNTX0_BUSY_MASK 0x10000000L +#define RBBM_STATUS__RB_CNTX_BUSY_MASK 0x40000000L +#define RBBM_STATUS__GUI_ACTIVE_MASK 0x80000000L + +#define CP_INT_CNTL__SW_INT_MASK 0x00080000L +#define CP_INT_CNTL__T0_PACKET_IN_IB_MASK 0x00800000L +#define CP_INT_CNTL__OPCODE_ERROR_MASK 0x01000000L +#define CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK 0x02000000L +#define CP_INT_CNTL__RESERVED_BIT_ERROR_MASK 0x04000000L +#define CP_INT_CNTL__IB_ERROR_MASK 0x08000000L +#define CP_INT_CNTL__IB2_INT_MASK 0x20000000L +#define CP_INT_CNTL__IB1_INT_MASK 0x40000000L +#define CP_INT_CNTL__RB_INT_MASK 0x80000000L + +#define MASTER_INT_SIGNAL__MH_INT_STAT 0x00000020L +#define MASTER_INT_SIGNAL__SQ_INT_STAT 0x04000000L +#define MASTER_INT_SIGNAL__CP_INT_STAT 0x40000000L +#define MASTER_INT_SIGNAL__RBBM_INT_STAT 0x80000000L + +#define RB_EDRAM_INFO__EDRAM_SIZE_MASK 0x0000000fL +#define RB_EDRAM_INFO__EDRAM_RANGE_MASK 0xffffc000L + +#define MH_ARBITER_CONFIG__SAME_PAGE_GRANULARITY__SHIFT 0x00000006 +#define MH_ARBITER_CONFIG__L1_ARB_ENABLE__SHIFT 0x00000007 +#define MH_ARBITER_CONFIG__L1_ARB_HOLD_ENABLE__SHIFT 0x00000008 +#define MH_ARBITER_CONFIG__L2_ARB_CONTROL__SHIFT 0x00000009 +#define MH_ARBITER_CONFIG__PAGE_SIZE__SHIFT 0x0000000a +#define MH_ARBITER_CONFIG__TC_REORDER_ENABLE__SHIFT 0x0000000d +#define MH_ARBITER_CONFIG__TC_ARB_HOLD_ENABLE__SHIFT 0x0000000e +#define MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT_ENABLE__SHIFT 0x0000000f +#define MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT__SHIFT 0x00000010 +#define MH_ARBITER_CONFIG__CP_CLNT_ENABLE__SHIFT 0x00000016 +#define MH_ARBITER_CONFIG__VGT_CLNT_ENABLE__SHIFT 0x00000017 +#define MH_ARBITER_CONFIG__TC_CLNT_ENABLE__SHIFT 0x00000018 +#define MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT 0x00000019 +#define MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT 0x0000001a + +#define CP_RB_CNTL__RB_BUFSZ__SHIFT 0x00000000 +#define CP_RB_CNTL__RB_BLKSZ__SHIFT 0x00000008 +#define CP_RB_CNTL__RB_POLL_EN__SHIFT 0x00000014 +#define CP_RB_CNTL__RB_NO_UPDATE__SHIFT 0x0000001b + +#define RB_COLOR_INFO__COLOR_FORMAT__SHIFT 0x00000000 +#define RB_EDRAM_INFO__EDRAM_MAPPING_MODE__SHIFT 0x00000004 +#define RB_EDRAM_INFO__EDRAM_RANGE__SHIFT 0x0000000e + +#define REG_CP_CSQ_IB1_STAT 0x01FE +#define REG_CP_CSQ_IB2_STAT 0x01FF +#define REG_CP_CSQ_RB_STAT 0x01FD +#define REG_CP_DEBUG 0x01FC +#define REG_CP_IB1_BASE 0x0458 +#define REG_CP_IB1_BUFSZ 0x0459 +#define REG_CP_IB2_BASE 0x045A +#define REG_CP_IB2_BUFSZ 0x045B +#define REG_CP_INT_ACK 0x01F4 +#define REG_CP_INT_CNTL 0x01F2 +#define REG_CP_INT_STATUS 0x01F3 +#define REG_CP_ME_CNTL 0x01F6 +#define REG_CP_ME_RAM_DATA 0x01FA +#define REG_CP_ME_RAM_WADDR 0x01F8 +#define REG_CP_ME_STATUS 0x01F7 +#define REG_CP_PFP_UCODE_ADDR 0x00C0 +#define REG_CP_PFP_UCODE_DATA 0x00C1 +#define REG_CP_QUEUE_THRESHOLDS 0x01D5 +#define REG_CP_RB_BASE 0x01C0 +#define REG_CP_RB_CNTL 0x01C1 +#define REG_CP_RB_RPTR 0x01C4 +#define REG_CP_RB_RPTR_ADDR 0x01C3 +#define REG_CP_RB_RPTR_WR 0x01C7 +#define REG_CP_RB_WPTR 0x01C5 +#define REG_CP_RB_WPTR_BASE 0x01C8 +#define REG_CP_RB_WPTR_DELAY 0x01C6 +#define REG_CP_STAT 0x047F +#define REG_CP_STATE_DEBUG_DATA 0x01ED +#define REG_CP_STATE_DEBUG_INDEX 0x01EC +#define REG_CP_ST_BASE 0x044D +#define REG_CP_ST_BUFSZ 0x044E + +#define REG_CP_PERFMON_CNTL 0x0444 +#define REG_CP_PERFCOUNTER_SELECT 0x0445 +#define REG_CP_PERFCOUNTER_LO 0x0446 +#define REG_CP_PERFCOUNTER_HI 0x0447 + +#define REG_RBBM_PERFCOUNTER1_SELECT 0x0395 +#define REG_RBBM_PERFCOUNTER1_HI 0x0398 +#define REG_RBBM_PERFCOUNTER1_LO 0x0397 + +#define REG_MASTER_INT_SIGNAL 0x03B7 + +#define REG_PA_CL_VPORT_XSCALE 0x210F +#define REG_PA_CL_VPORT_ZOFFSET 0x2114 +#define REG_PA_CL_VPORT_ZSCALE 0x2113 +#define REG_PA_CL_CLIP_CNTL 0x2204 +#define REG_PA_CL_VTE_CNTL 0x2206 +#define REG_PA_SC_AA_MASK 0x2312 +#define REG_PA_SC_LINE_CNTL 0x2300 +#define REG_PA_SC_SCREEN_SCISSOR_BR 0x200F +#define REG_PA_SC_SCREEN_SCISSOR_TL 0x200E +#define REG_PA_SC_VIZ_QUERY 0x2293 +#define REG_PA_SC_VIZ_QUERY_STATUS 0x0C44 +#define REG_PA_SC_WINDOW_OFFSET 0x2080 +#define REG_PA_SC_WINDOW_SCISSOR_BR 0x2082 +#define REG_PA_SC_WINDOW_SCISSOR_TL 0x2081 +#define REG_PA_SU_FACE_DATA 0x0C86 +#define REG_PA_SU_POINT_SIZE 0x2280 +#define REG_PA_SU_LINE_CNTL 0x2282 +#define REG_PA_SU_POLY_OFFSET_BACK_OFFSET 0x2383 +#define REG_PA_SU_POLY_OFFSET_FRONT_SCALE 0x2380 +#define REG_PA_SU_SC_MODE_CNTL 0x2205 + +#define REG_PC_INDEX_OFFSET 0x2102 + +#define REG_RBBM_CNTL 0x003B +#define REG_RBBM_INT_ACK 0x03B6 +#define REG_RBBM_INT_CNTL 0x03B4 +#define REG_RBBM_INT_STATUS 0x03B5 +#define REG_RBBM_PATCH_RELEASE 0x0001 +#define REG_RBBM_PERIPHID1 0x03F9 +#define REG_RBBM_PERIPHID2 0x03FA +#define REG_RBBM_DEBUG 0x039B +#define REG_RBBM_DEBUG_OUT 0x03A0 +#define REG_RBBM_DEBUG_CNTL 0x03A1 +#define REG_RBBM_PM_OVERRIDE1 0x039C +#define REG_RBBM_PM_OVERRIDE2 0x039D +#define REG_RBBM_READ_ERROR 0x03B3 +#define REG_RBBM_SOFT_RESET 0x003C +#define REG_RBBM_STATUS 0x05D0 + +#define REG_RB_COLORCONTROL 0x2202 +#define REG_RB_COLOR_DEST_MASK 0x2326 +#define REG_RB_COLOR_MASK 0x2104 +#define REG_RB_COPY_CONTROL 0x2318 +#define REG_RB_DEPTHCONTROL 0x2200 +#define REG_RB_EDRAM_INFO 0x0F02 +#define REG_RB_MODECONTROL 0x2208 +#define REG_RB_SURFACE_INFO 0x2000 +#define REG_RB_SAMPLE_POS 0x220a + +#define REG_SCRATCH_ADDR 0x01DD +#define REG_SCRATCH_REG0 0x0578 +#define REG_SCRATCH_REG2 0x057A +#define REG_SCRATCH_UMSK 0x01DC + +#define REG_SQ_CF_BOOLEANS 0x4900 +#define REG_SQ_CF_LOOP 0x4908 +#define REG_SQ_GPR_MANAGEMENT 0x0D00 +#define REG_SQ_FLOW_CONTROL 0x0D01 +#define REG_SQ_INST_STORE_MANAGMENT 0x0D02 +#define REG_SQ_INT_ACK 0x0D36 +#define REG_SQ_INT_CNTL 0x0D34 +#define REG_SQ_INT_STATUS 0x0D35 +#define REG_SQ_PROGRAM_CNTL 0x2180 +#define REG_SQ_PS_PROGRAM 0x21F6 +#define REG_SQ_VS_PROGRAM 0x21F7 +#define REG_SQ_WRAPPING_0 0x2183 +#define REG_SQ_WRAPPING_1 0x2184 + +#define REG_VGT_ENHANCE 0x2294 +#define REG_VGT_INDX_OFFSET 0x2102 +#define REG_VGT_MAX_VTX_INDX 0x2100 +#define REG_VGT_MIN_VTX_INDX 0x2101 + +#define REG_TP0_CHICKEN 0x0E1E +#define REG_TC_CNTL_STATUS 0x0E00 +#define REG_PA_SC_AA_CONFIG 0x2301 +#define REG_VGT_VERTEX_REUSE_BLOCK_CNTL 0x2316 +#define REG_SQ_INTERPOLATOR_CNTL 0x2182 +#define REG_RB_DEPTH_INFO 0x2002 +#define REG_COHER_DEST_BASE_0 0x2006 +#define REG_RB_FOG_COLOR 0x2109 +#define REG_RB_STENCILREFMASK_BF 0x210C +#define REG_PA_SC_LINE_STIPPLE 0x2283 +#define REG_SQ_PS_CONST 0x2308 +#define REG_RB_DEPTH_CLEAR 0x231D +#define REG_RB_SAMPLE_COUNT_CTL 0x2324 +#define REG_SQ_CONSTANT_0 0x4000 +#define REG_SQ_FETCH_0 0x4800 + +#define REG_COHER_BASE_PM4 0xA2A +#define REG_COHER_STATUS_PM4 0xA2B +#define REG_COHER_SIZE_PM4 0xA29 + +/*registers added in adreno220*/ +#define REG_A220_PC_INDX_OFFSET REG_VGT_INDX_OFFSET +#define REG_A220_PC_VERTEX_REUSE_BLOCK_CNTL REG_VGT_VERTEX_REUSE_BLOCK_CNTL +#define REG_A220_PC_MAX_VTX_INDX REG_VGT_MAX_VTX_INDX +#define REG_A220_RB_LRZ_VSC_CONTROL 0x2209 +#define REG_A220_GRAS_CONTROL 0x2210 +#define REG_A220_VSC_BIN_SIZE 0x0C01 +#define REG_A220_VSC_PIPE_DATA_LENGTH_7 0x0C1D + +/*registers added in adreno225*/ +#define REG_A225_RB_COLOR_INFO3 0x2005 +#define REG_A225_PC_MULTI_PRIM_IB_RESET_INDX 0x2103 +#define REG_A225_GRAS_UCP0X 0x2340 +#define REG_A225_GRAS_UCP_ENABLED 0x2360 + +#endif /* __A200_REG_H */ diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index db3d9782d34e6..247abf5bc1892 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -16,24 +16,24 @@ #include #include +#include + #include "kgsl.h" #include "kgsl_pwrscale.h" #include "kgsl_cffdump.h" +#include "kgsl_sharedmem.h" #include "adreno.h" #include "adreno_pm4types.h" #include "adreno_debugfs.h" #include "adreno_postmortem.h" -#include "a200_reg.h" +#include "a2xx_reg.h" +#include "kgsl_mmu.h" #define DRIVER_VERSION_MAJOR 3 #define DRIVER_VERSION_MINOR 1 -#define GSL_RBBM_INT_MASK \ - (RBBM_INT_CNTL__RDERR_INT_MASK | \ - RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK) - /* Adreno MH arbiter config*/ #define ADRENO_CFG_MHARB \ (0x10 \ @@ -69,43 +69,47 @@ /* max msecs to wait for gpu to finish its operation(s) */ #define MAX_WAITGPU_SECS (HZ + HZ/2) +static const struct kgsl_functable adreno_functable; + static struct adreno_device device_3d0 = { .dev = { .name = DEVICE_3D0_NAME, .id = KGSL_DEVICE_3D0, .ver_major = DRIVER_VERSION_MAJOR, .ver_minor = DRIVER_VERSION_MINOR, - .mmu = { - .config = ADRENO_MMU_CONFIG, + .mh = { + .mharb = ADRENO_CFG_MHARB, + /* Remove 1k boundary check in z470 to avoid a GPU + * hang. Notice that this solution won't work if + * both EBI and SMI are used + */ + .mh_intf_cfg1 = 0x00032f07, /* turn off memory protection unit by setting acceptable physical address range to include all pages. */ .mpu_base = 0x00000000, .mpu_range = 0xFFFFF000, - .reg = { - .config = REG_MH_MMU_CONFIG, - .mpu_base = REG_MH_MMU_MPU_BASE, - .mpu_end = REG_MH_MMU_MPU_END, - .va_range = REG_MH_MMU_VA_RANGE, - .pt_page = REG_MH_MMU_PT_BASE, - .page_fault = REG_MH_MMU_PAGE_FAULT, - .tran_error = REG_MH_MMU_TRAN_ERROR, - .invalidate = REG_MH_MMU_INVALIDATE, - .interrupt_mask = REG_MH_INTERRUPT_MASK, - .interrupt_status = REG_MH_INTERRUPT_STATUS, - .interrupt_clear = REG_MH_INTERRUPT_CLEAR, - .axi_error = REG_MH_AXI_ERROR, - }, + }, + .mmu = { + .config = ADRENO_MMU_CONFIG, }, .pwrctrl = { .regulator_name = "fs_gfx3d", .irq_name = KGSL_3D0_IRQ, - .src_clk_name = "grp_src_clk", + .src_clk_name = "src_clk", }, .mutex = __MUTEX_INITIALIZER(device_3d0.dev.mutex), .state = KGSL_STATE_INIT, .active_cnt = 0, .iomemname = KGSL_3D0_REG_MEMORY, + .ftbl = &adreno_functable, +#ifdef CONFIG_HAS_EARLYSUSPEND + .display_off = { + .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING, + .suspend = kgsl_early_suspend_driver, + .resume = kgsl_late_resume_driver, + }, +#endif }, .gmemspace = { .gpu_base = 0, @@ -113,12 +117,33 @@ static struct adreno_device device_3d0 = { }, .pfp_fw = NULL, .pm4_fw = NULL, - .mharb = ADRENO_CFG_MHARB, }; -static void __devinit adreno_getfunctable(struct kgsl_functable *ftbl); +/* + * This is the master list of all GPU cores that are supported by this + * driver. + */ -static int adreno_gmeminit(struct adreno_device *adreno_dev) +#define ANY_ID (~0) + +static const struct { + enum adreno_gpurev gpurev; + unsigned int core, major, minor; + const char *pm4fw; + const char *pfpfw; + struct adreno_gpudev *gpudev; +} adreno_gpulist[] = { + { ADRENO_REV_A200, 0, 2, ANY_ID, + "yamato_pm4.fw", "yamato_pfp.fw", &adreno_a2xx_gpudev }, + { ADRENO_REV_A205, 0, 1, 0, + "yamato_pm4.fw", "yamato_pfp.fw", &adreno_a2xx_gpudev }, + { ADRENO_REV_A220, 2, 1, ANY_ID, + "leia_pm4_470.fw", "leia_pfp_470.fw", &adreno_a2xx_gpudev }, + { ADRENO_REV_A225, 2, 2, ANY_ID, + "a225_pm4.fw", "a225_pfp.fw", &adreno_a2xx_gpudev }, +}; + +static void adreno_gmeminit(struct adreno_device *adreno_dev) { struct kgsl_device *device = &adreno_dev->dev; union reg_rb_edram_info rb_edram_info; @@ -137,90 +162,27 @@ static int adreno_gmeminit(struct adreno_device *adreno_dev) rb_edram_info.val = 0; rb_edram_info.f.edram_size = edram_value; - if (!adreno_is_a220(adreno_dev)) - rb_edram_info.f.edram_mapping_mode = 0; /* EDRAM_MAP_UPPER */ + rb_edram_info.f.edram_mapping_mode = 0; /* EDRAM_MAP_UPPER */ /* must be aligned to size */ rb_edram_info.f.edram_range = (adreno_dev->gmemspace.gpu_base >> 14); adreno_regwrite(device, REG_RB_EDRAM_INFO, rb_edram_info.val); - - return 0; } -static int adreno_gmemclose(struct kgsl_device *device) +static irqreturn_t adreno_isr(int irq, void *data) { - adreno_regwrite(device, REG_RB_EDRAM_INFO, 0x00000000); - - return 0; -} - -static void adreno_rbbm_intrcallback(struct kgsl_device *device) -{ - unsigned int status = 0; - unsigned int rderr = 0; - - adreno_regread_isr(device, REG_RBBM_INT_STATUS, &status); - - if (status & RBBM_INT_CNTL__RDERR_INT_MASK) { - union rbbm_read_error_u rerr; - adreno_regread_isr(device, REG_RBBM_READ_ERROR, &rderr); - rerr.val = rderr; - if (rerr.f.read_address == REG_CP_INT_STATUS && - rerr.f.read_error && - rerr.f.read_requester) - KGSL_DRV_WARN(device, - "rbbm read error interrupt: %08x\n", rderr); - else - KGSL_DRV_CRIT(device, - "rbbm read error interrupt: %08x\n", rderr); - } else if (status & RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK) { - KGSL_DRV_INFO(device, "rbbm display update interrupt\n"); - } else if (status & RBBM_INT_CNTL__GUI_IDLE_INT_MASK) { - KGSL_DRV_INFO(device, "rbbm gui idle interrupt\n"); - } else { - KGSL_CMD_WARN(device, - "bad bits in REG_CP_INT_STATUS %08x\n", status); - } - - status &= GSL_RBBM_INT_MASK; - adreno_regwrite_isr(device, REG_RBBM_INT_ACK, status); -} - -irqreturn_t adreno_isr(int irq, void *data) -{ - irqreturn_t result = IRQ_NONE; - struct kgsl_device *device; - unsigned int status; - - device = (struct kgsl_device *) data; - - BUG_ON(device == NULL); - BUG_ON(device->regspace.sizebytes == 0); - BUG_ON(device->regspace.mmio_virt_base == 0); - - adreno_regread_isr(device, REG_MASTER_INT_SIGNAL, &status); - - if (status & MASTER_INT_SIGNAL__MH_INT_STAT) { - kgsl_mh_intrcallback(device); - result = IRQ_HANDLED; - } - - if (status & MASTER_INT_SIGNAL__CP_INT_STAT) { - kgsl_cp_intrcallback(device); - result = IRQ_HANDLED; - } + irqreturn_t result; + struct kgsl_device *device = data; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - if (status & MASTER_INT_SIGNAL__RBBM_INT_STAT) { - adreno_rbbm_intrcallback(device); - result = IRQ_HANDLED; - } + result = adreno_dev->gpudev->irq_handler(adreno_dev); if (device->requested_state == KGSL_STATE_NONE) { if (device->pwrctrl.nap_allowed == true) { device->requested_state = KGSL_STATE_NAP; queue_work(device->work_queue, &device->idle_check_ws); - } else if (device->pwrctrl.idle_pass == true) { + } else if (device->pwrscale.policy != NULL) { queue_work(device->work_queue, &device->idle_check_ws); } } @@ -231,7 +193,7 @@ irqreturn_t adreno_isr(int irq, void *data) return result; } -static int adreno_cleanup_pt(struct kgsl_device *device, +static void adreno_cleanup_pt(struct kgsl_device *device, struct kgsl_pagetable *pagetable) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); @@ -243,9 +205,7 @@ static int adreno_cleanup_pt(struct kgsl_device *device, kgsl_mmu_unmap(pagetable, &device->memstore); - kgsl_mmu_unmap(pagetable, &device->mmu.dummyspace); - - return 0; + kgsl_mmu_unmap(pagetable, &device->mmu.setstate_memory); } static int adreno_setup_pt(struct kgsl_device *device, @@ -255,12 +215,6 @@ static int adreno_setup_pt(struct kgsl_device *device, struct adreno_device *adreno_dev = ADRENO_DEVICE(device); struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; - BUG_ON(rb->buffer_desc.physaddr == 0); - BUG_ON(rb->memptrs_desc.physaddr == 0); - BUG_ON(device->memstore.physaddr == 0); -#ifdef CONFIG_MSM_KGSL_MMU - BUG_ON(device->mmu.dummyspace.physaddr == 0); -#endif result = kgsl_mmu_map_global(pagetable, &rb->buffer_desc, GSL_PT_PAGE_RV); if (result) @@ -276,7 +230,7 @@ static int adreno_setup_pt(struct kgsl_device *device, if (result) goto unmap_memptrs_desc; - result = kgsl_mmu_map_global(pagetable, &device->mmu.dummyspace, + result = kgsl_mmu_map_global(pagetable, &device->mmu.setstate_memory, GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); if (result) goto unmap_memstore_desc; @@ -296,7 +250,8 @@ static int adreno_setup_pt(struct kgsl_device *device, return result; } -static int adreno_setstate(struct kgsl_device *device, uint32_t flags) +static void adreno_setstate(struct kgsl_device *device, + uint32_t flags) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); unsigned int link[32]; @@ -304,38 +259,36 @@ static int adreno_setstate(struct kgsl_device *device, uint32_t flags) int sizedwords = 0; unsigned int mh_mmu_invalidate = 0x00000003; /*invalidate all and tc */ -#ifndef CONFIG_MSM_KGSL_MMU - return 0; -#endif - /* if possible, set via command stream, - * otherwise set via direct register writes - */ + /* If possible, then set the state via the command stream to avoid + a CPU idle. Otherwise, use the default setstate which uses register + writes */ if (adreno_dev->drawctxt_active) { if (flags & KGSL_MMUFLAGS_PTUPDATE) { /* wait for graphics pipe to be idle */ - *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); *cmds++ = 0x00000000; /* set page table base */ - *cmds++ = pm4_type0_packet(REG_MH_MMU_PT_BASE, 1); - *cmds++ = device->mmu.hwpagetable->base.gpuaddr; + *cmds++ = cp_type0_packet(MH_MMU_PT_BASE, 1); + *cmds++ = kgsl_pt_get_base_addr( + device->mmu.hwpagetable); sizedwords += 4; } if (flags & KGSL_MMUFLAGS_TLBFLUSH) { if (!(flags & KGSL_MMUFLAGS_PTUPDATE)) { - *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, + *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); *cmds++ = 0x00000000; sizedwords += 2; } - *cmds++ = pm4_type0_packet(REG_MH_MMU_INVALIDATE, 1); + *cmds++ = cp_type0_packet(MH_MMU_INVALIDATE, 1); *cmds++ = mh_mmu_invalidate; sizedwords += 2; } if (flags & KGSL_MMUFLAGS_PTUPDATE && - !adreno_is_a220(adreno_dev)) { + adreno_is_a20x(adreno_dev)) { /* HW workaround: to resolve MMU page fault interrupts * caused by the VGT.It prevents the CP PFP from filling * the VGT DMA request fifo too early,thereby ensuring @@ -348,34 +301,36 @@ static int adreno_setstate(struct kgsl_device *device, uint32_t flags) * VGT DMA request fifo and prevent any further * vertex/bin updates from occurring until the wait * has finished. */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); *cmds++ = (0x4 << 16) | (REG_PA_SU_SC_MODE_CNTL - 0x2000); *cmds++ = 0; /* disable faceness generation */ - *cmds++ = pm4_type3_packet(PM4_SET_BIN_BASE_OFFSET, 1); - *cmds++ = device->mmu.dummyspace.gpuaddr; - *cmds++ = pm4_type3_packet(PM4_DRAW_INDX_BIN, 6); + *cmds++ = cp_type3_packet(CP_SET_BIN_BASE_OFFSET, 1); + *cmds++ = device->mmu.setstate_memory.gpuaddr; + *cmds++ = cp_type3_packet(CP_DRAW_INDX_BIN, 6); *cmds++ = 0; /* viz query info */ *cmds++ = 0x0003C004; /* draw indicator */ *cmds++ = 0; /* bin base */ *cmds++ = 3; /* bin size */ - *cmds++ = device->mmu.dummyspace.gpuaddr; /* dma base */ + *cmds++ = + device->mmu.setstate_memory.gpuaddr; /* dma base */ *cmds++ = 6; /* dma size */ - *cmds++ = pm4_type3_packet(PM4_DRAW_INDX_BIN, 6); + *cmds++ = cp_type3_packet(CP_DRAW_INDX_BIN, 6); *cmds++ = 0; /* viz query info */ *cmds++ = 0x0003C004; /* draw indicator */ *cmds++ = 0; /* bin base */ *cmds++ = 3; /* bin size */ /* dma base */ - *cmds++ = device->mmu.dummyspace.gpuaddr; + *cmds++ = device->mmu.setstate_memory.gpuaddr; *cmds++ = 6; /* dma size */ - *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); + *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); *cmds++ = 0x00000000; sizedwords += 21; } + if (flags & (KGSL_MMUFLAGS_PTUPDATE | KGSL_MMUFLAGS_TLBFLUSH)) { - *cmds++ = pm4_type3_packet(PM4_INVALIDATE_STATE, 1); + *cmds++ = cp_type3_packet(CP_INVALIDATE_STATE, 1); *cmds++ = 0x7fff; /* invalidate all base pointers */ sizedwords += 2; } @@ -383,25 +338,13 @@ static int adreno_setstate(struct kgsl_device *device, uint32_t flags) adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, &link[0], sizedwords); } else { - if (flags & KGSL_MMUFLAGS_PTUPDATE) { - adreno_idle(device, KGSL_TIMEOUT_DEFAULT); - adreno_regwrite(device, REG_MH_MMU_PT_BASE, - device->mmu.hwpagetable->base.gpuaddr); - } - - if (flags & KGSL_MMUFLAGS_TLBFLUSH) { - adreno_regwrite(device, REG_MH_MMU_INVALIDATE, - mh_mmu_invalidate); - } + kgsl_mmu_device_setstate(device, flags); } - - return 0; } static unsigned int adreno_getchipid(struct kgsl_device *device) { - /* XXX: drewis edit: only for 8x50 */ unsigned int chipid = 0; unsigned int coreid, majorid, minorid, patchid, revid; @@ -409,57 +352,68 @@ adreno_getchipid(struct kgsl_device *device) adreno_regread(device, REG_RBBM_PERIPHID2, &majorid); adreno_regread(device, REG_RBBM_PATCH_RELEASE, &revid); - chipid = (coreid & 0xF) << 24; + /* + * adreno 22x gpus are indicated by coreid 2, + * but REG_RBBM_PERIPHID1 always contains 0 for this field + */ + if (cpu_is_msm8960() || cpu_is_msm8x60()) + chipid = 2 << 24; + else + chipid = (coreid & 0xF) << 24; + + if (cpu_is_msm8960()) { + KGSL_DRV_ERR(device, "forcing a220 chipid\n"); + majorid = 1<<4; + } chipid |= ((majorid >> 4) & 0xF) << 16; minorid = ((revid >> 0) & 0xFF); - patchid = 1; + patchid = ((revid >> 16) & 0xFF); + + /* 8x50 returns 0 for patch release, but it should be 1 */ + if (cpu_is_qsd8x50()) + patchid = 1; chipid |= (minorid << 8) | patchid; return chipid; } -/* all chipid fields are 8 bits wide so 256 won't occur in a real chipid */ -#define DONT_CARE 256 -static const struct { - unsigned int core; - unsigned int major; - unsigned int minor; - enum adreno_gpurev gpurev; -} gpurev_table[] = { - /* major and minor may be DONT_CARE, but core must not be */ - {0, 2, DONT_CARE, ADRENO_REV_A200}, - {0, 1, 0, ADRENO_REV_A205}, - {2, 1, DONT_CARE, ADRENO_REV_A220}, - {2, 2, DONT_CARE, ADRENO_REV_A225}, -}; - static inline bool _rev_match(unsigned int id, unsigned int entry) { - return (entry == DONT_CARE || entry == id); + return (entry == ANY_ID || entry == id); } -#undef DONT_CARE -enum adreno_gpurev adreno_get_rev(struct adreno_device *adreno_dev) +static void +adreno_identify_gpu(struct adreno_device *adreno_dev) { - enum adreno_gpurev gpurev = ADRENO_REV_UNKNOWN; unsigned int i, core, major, minor; + + adreno_dev->chip_id = adreno_getchipid(&adreno_dev->dev); + core = (adreno_dev->chip_id >> 24) & 0xff; major = (adreno_dev->chip_id >> 16) & 0xff; minor = (adreno_dev->chip_id >> 8) & 0xff; - for (i = 0; i < ARRAY_SIZE(gpurev_table); i++) { - if (core == gpurev_table[i].core && - _rev_match(major, gpurev_table[i].major) && - _rev_match(minor, gpurev_table[i].minor)) { - gpurev = gpurev_table[i].gpurev; + for (i = 0; i < ARRAY_SIZE(adreno_gpulist); i++) { + if (core == adreno_gpulist[i].core && + _rev_match(major, adreno_gpulist[i].major) && + _rev_match(minor, adreno_gpulist[i].minor)) { break; } } - return gpurev; + + if (i == ARRAY_SIZE(adreno_gpulist)) { + adreno_dev->gpurev = ADRENO_REV_UNKNOWN; + return; + } + + adreno_dev->gpurev = adreno_gpulist[i].gpurev; + adreno_dev->gpudev = adreno_gpulist[i].gpudev; + adreno_dev->pfp_fwfile = adreno_gpulist[i].pfpfw; + adreno_dev->pm4_fwfile = adreno_gpulist[i].pm4fw; } static int __devinit @@ -475,8 +429,6 @@ adreno_probe(struct platform_device *pdev) init_completion(&device->recovery_gate); - adreno_getfunctable(&device->ftbl); - status = adreno_ringbuffer_init(device); if (status != 0) goto error; @@ -487,6 +439,9 @@ adreno_probe(struct platform_device *pdev) adreno_debugfs_init(device); + kgsl_pwrscale_init(device); + kgsl_pwrscale_attach_policy(device, ADRENO_DEFAULT_PWRSCALE_POLICY); + device->flags &= ~KGSL_FLAGS_SOFT_RESET; return 0; @@ -505,6 +460,9 @@ static int __devexit adreno_remove(struct platform_device *pdev) device = (struct kgsl_device *)pdev->id_entry->driver_data; adreno_dev = ADRENO_DEVICE(device); + kgsl_pwrscale_detach_policy(device); + kgsl_pwrscale_close(device); + adreno_ringbuffer_close(&adreno_dev->ringbuffer); kgsl_device_platform_remove(device); @@ -523,23 +481,38 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) /* Power up the device */ kgsl_pwrctrl_enable(device); - if (kgsl_mmu_start(device)) + /* Identify the specific GPU */ + adreno_identify_gpu(adreno_dev); + + if (adreno_dev->gpurev == ADRENO_REV_UNKNOWN) { + KGSL_DRV_ERR(device, "Unknown chip ID %x\n", + adreno_dev->chip_id); goto error_clk_off; + } - adreno_dev->chip_id = adreno_getchipid(device); + if (adreno_is_a20x(adreno_dev)) { + /* + * the MH_CLNT_INTF_CTRL_CONFIG registers aren't present + * on older gpus + */ + device->mh.mh_intf_cfg1 = 0; + device->mh.mh_intf_cfg2 = 0; + } + + kgsl_mh_start(device); + + if (kgsl_mmu_start(device)) + goto error_clk_off; /*We need to make sure all blocks are powered up and clocked before *issuing a soft reset. The overrides will then be turned off (set to 0) */ adreno_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0xfffffffe); - if (adreno_dev->chip_id == CHIP_REV_251) - adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0x000000ff); - else - adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xffffffff); + adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xffffffff); /* Only reset CP block if all blocks have previously been reset */ if (!(device->flags & KGSL_FLAGS_SOFT_RESET) || - !adreno_is_a220(adreno_dev)) { + !adreno_is_a22x(adreno_dev)) { adreno_regwrite(device, REG_RBBM_SOFT_RESET, 0xFFFFFFFF); device->flags |= KGSL_FLAGS_SOFT_RESET; } else @@ -554,44 +527,42 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) adreno_regwrite(device, REG_RBBM_CNTL, 0x00004442); - adreno_regwrite(device, REG_MH_ARBITER_CONFIG, - adreno_dev->mharb); - - if (!adreno_is_a220(adreno_dev)) { - adreno_regwrite(device, - REG_MH_CLNT_INTF_CTRL_CONFIG1, 0x00030f27); - adreno_regwrite(device, - REG_MH_CLNT_INTF_CTRL_CONFIG2, 0x00472747); - } - - /* Remove 1k boundary check in z470 to avoid GPU hang. - Notice that, this solution won't work if both EBI and SMI are used */ - if (adreno_is_a220(adreno_dev)) { - adreno_regwrite(device, REG_MH_CLNT_INTF_CTRL_CONFIG1, - 0x00032f07); + if (adreno_is_a225(adreno_dev)) { + /* Enable large instruction store for A225 */ + adreno_regwrite(device, REG_SQ_FLOW_CONTROL, 0x18000000); } adreno_regwrite(device, REG_SQ_VS_PROGRAM, 0x00000000); adreno_regwrite(device, REG_SQ_PS_PROGRAM, 0x00000000); - adreno_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0); - if (!adreno_is_a220(adreno_dev)) + if (cpu_is_msm8960()) + adreno_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0x200); + else + adreno_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0); + + if (!adreno_is_a22x(adreno_dev)) adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0); else adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0x80); + kgsl_sharedmem_set(&device->memstore, 0, 0, device->memstore.size); + kgsl_sharedmem_writel(&device->memstore, KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), init_reftimestamp); - adreno_regwrite(device, REG_RBBM_DEBUG, 0x000C0000); + if (adreno_is_a20x(adreno_dev)) + adreno_regwrite(device, REG_RBBM_DEBUG, 0x000C0000); + else + adreno_regwrite(device, REG_RBBM_DEBUG, 0x00080000); - adreno_regwrite(device, REG_RBBM_INT_CNTL, GSL_RBBM_INT_MASK); + /* Make sure interrupts are disabled */ - /* make sure SQ interrupts are disabled */ + adreno_regwrite(device, REG_RBBM_INT_CNTL, 0); + adreno_regwrite(device, REG_CP_INT_CNTL, 0); adreno_regwrite(device, REG_SQ_INT_CNTL, 0); - if (adreno_is_a220(adreno_dev)) + if (adreno_is_a22x(adreno_dev)) adreno_dev->gmemspace.sizebytes = SZ_512K; else adreno_dev->gmemspace.sizebytes = SZ_256K; @@ -608,9 +579,9 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) error_irq_off: kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); + kgsl_mmu_stop(device); error_clk_off: kgsl_pwrctrl_disable(device); - kgsl_mmu_stop(device); return status; } @@ -618,19 +589,15 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) static int adreno_stop(struct kgsl_device *device) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - del_timer(&device->idle_timer); - adreno_regwrite(device, REG_RBBM_INT_CNTL, 0); adreno_dev->drawctxt_active = NULL; adreno_ringbuffer_stop(&adreno_dev->ringbuffer); - adreno_gmemclose(device); - kgsl_mmu_stop(device); - /* Disable the clocks before the power rail. */ kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); + del_timer_sync(&device->idle_timer); /* Power down the device */ kgsl_pwrctrl_disable(device); @@ -786,7 +753,7 @@ static int adreno_getproperty(struct kgsl_device *device, devinfo.device_id = device->id+1; devinfo.chip_id = adreno_dev->chip_id; devinfo.mmu_enabled = kgsl_mmu_enabled(); - devinfo.gpu_id = adreno_get_rev(adreno_dev); + devinfo.gpu_id = adreno_dev->gpurev; devinfo.gmem_gpubaseaddr = adreno_dev->gmemspace. gpu_base; devinfo.gmem_sizebytes = adreno_dev->gmemspace. @@ -829,16 +796,13 @@ static int adreno_getproperty(struct kgsl_device *device, break; case KGSL_PROP_MMU_ENABLE: { -#ifdef CONFIG_MSM_KGSL_MMU - int mmuProp = 1; -#else - int mmuProp = 0; -#endif + int mmu_prop = kgsl_mmu_enabled(); + if (sizebytes != sizeof(int)) { status = -EINVAL; break; } - if (copy_to_user(value, &mmuProp, sizeof(mmuProp))) { + if (copy_to_user(value, &mmu_prop, sizeof(mmu_prop))) { status = -EFAULT; break; } @@ -932,34 +896,14 @@ static unsigned int adreno_isidle(struct kgsl_device *device) return status; } - -/******************************************************************/ -/* Caller must hold the driver mutex. */ -static int adreno_resume_context(struct kgsl_device *device) -{ - int status = 0; - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - - if (device->pwrctrl.suspended_ctxt != NULL) { - adreno_drawctxt_switch(adreno_dev, - device->pwrctrl.suspended_ctxt, 0); - status = adreno_idle(device, 0); - - } - - return status; -} - -/******************************************************************/ /* Caller must hold the device mutex. */ static int adreno_suspend_context(struct kgsl_device *device) { int status = 0; struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - /* save ctxt ptr and switch to NULL ctxt */ - device->pwrctrl.suspended_ctxt = adreno_dev->drawctxt_active; - if (device->pwrctrl.suspended_ctxt != NULL) { + /* switch to NULL ctxt */ + if (adreno_dev->drawctxt_active != NULL) { adreno_drawctxt_switch(adreno_dev, NULL, 0); status = adreno_idle(device, KGSL_TIMEOUT_DEFAULT); } @@ -993,12 +937,8 @@ uint8_t *kgsl_sharedmem_convertaddr(struct kgsl_device *device, mutex_lock(&kgsl_driver.process_mutex); list_for_each_entry(priv, &kgsl_driver.process_list, list) { - if (pt_base != 0 - && priv->pagetable - && priv->pagetable->base.gpuaddr != pt_base) { + if (!kgsl_mmu_pt_equal(priv->pagetable, pt_base)) continue; - } - spin_lock(&priv->mem_lock); entry = kgsl_sharedmem_find_region(priv, gpuaddr, sizeof(unsigned int)); @@ -1025,42 +965,33 @@ uint8_t *kgsl_sharedmem_convertaddr(struct kgsl_device *device, return result; } -static void _adreno_regread(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int *value) +void adreno_regread(struct kgsl_device *device, unsigned int offsetwords, + unsigned int *value) { unsigned int *reg; BUG_ON(offsetwords*sizeof(uint32_t) >= device->regspace.sizebytes); reg = (unsigned int *)(device->regspace.mmio_virt_base + (offsetwords << 2)); + + if (!in_interrupt()) + kgsl_pre_hwaccess(device); + /*ensure this read finishes before the next one. * i.e. act like normal readl() */ *value = __raw_readl(reg); rmb(); } -void adreno_regread(struct kgsl_device *device, unsigned int offsetwords, - unsigned int *value) -{ - kgsl_pre_hwaccess(device); - _adreno_regread(device, offsetwords, value); -} - -void adreno_regread_isr(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int *value) -{ - _adreno_regread(device, offsetwords, value); -} - -static void _adreno_regwrite(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int value) +void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords, + unsigned int value) { unsigned int *reg; BUG_ON(offsetwords*sizeof(uint32_t) >= device->regspace.sizebytes); + if (!in_interrupt()) + kgsl_pre_hwaccess(device); + kgsl_cffdump_regwrite(device->id, offsetwords << 2, value); reg = (unsigned int *)(device->regspace.mmio_virt_base + (offsetwords << 2)); @@ -1071,20 +1002,6 @@ static void _adreno_regwrite(struct kgsl_device *device, __raw_writel(value, reg); } -void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords, - unsigned int value) -{ - kgsl_pre_hwaccess(device); - _adreno_regwrite(device, offsetwords, value); -} - -void adreno_regwrite_isr(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int value) -{ - _adreno_regwrite(device, offsetwords, value); -} - static int kgsl_check_interrupt_timestamp(struct kgsl_device *device, unsigned int timestamp) { @@ -1121,7 +1038,7 @@ static int kgsl_check_interrupt_timestamp(struct kgsl_device *device, /* submit a dummy packet so that even if all * commands upto timestamp get executed we will still * get an interrupt */ - cmds[0] = pm4_type3_packet(PM4_NOP, 1); + cmds[0] = cp_type3_packet(CP_NOP, 1); cmds[1] = 0; adreno_ringbuffer_issuecmds(device, 0, &cmds[0], 2); } @@ -1132,15 +1049,18 @@ static int kgsl_check_interrupt_timestamp(struct kgsl_device *device, } /* - wait_io_event_interruptible_timeout checks for the exit condition before + wait_event_interruptible_timeout checks for the exit condition before placing a process in wait q. For conditional interrupts we expect the process to already be in its wait q when its exit condition checking function is called. */ -#define kgsl_wait_io_event_interruptible_timeout(wq, condition, timeout)\ +#define kgsl_wait_event_interruptible_timeout(wq, condition, timeout, io)\ ({ \ long __ret = timeout; \ - __wait_io_event_interruptible_timeout(wq, condition, __ret); \ + if (io) \ + __wait_io_event_interruptible_timeout(wq, condition, __ret);\ + else \ + __wait_event_interruptible_timeout(wq, condition, __ret);\ __ret; \ }) @@ -1150,7 +1070,9 @@ static int adreno_waittimestamp(struct kgsl_device *device, unsigned int msecs) { long status = 0; + uint io = 1; struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct kgsl_pwrctrl *pwr = &device->pwrctrl; if (timestamp != adreno_dev->ringbuffer.timestamp && timestamp_cmp(timestamp, @@ -1162,13 +1084,20 @@ static int adreno_waittimestamp(struct kgsl_device *device, goto done; } if (!kgsl_check_timestamp(device, timestamp)) { + if (pwr->active_pwrlevel) { + int low_pwrlevel = pwr->num_pwrlevels - + KGSL_PWRLEVEL_LOW_OFFSET; + if (pwr->active_pwrlevel == low_pwrlevel) + io = 0; + } mutex_unlock(&device->mutex); /* We need to make sure that the process is placed in wait-q * before its condition is called */ - status = kgsl_wait_io_event_interruptible_timeout( + status = kgsl_wait_event_interruptible_timeout( device->wait_queue, kgsl_check_interrupt_timestamp(device, - timestamp), msecs_to_jiffies(msecs)); + timestamp), + msecs_to_jiffies(msecs), io); mutex_lock(&device->mutex); if (status > 0) @@ -1227,10 +1156,8 @@ static long adreno_ioctl(struct kgsl_device_private *dev_priv, context = kgsl_find_context(dev_priv, binbase->drawctxt_id); if (context) { - result = adreno_drawctxt_set_bin_base_offset( - dev_priv->device, - context, - binbase->offset); + adreno_drawctxt_set_bin_base_offset( + dev_priv->device, context, binbase->offset); } else { result = -EINVAL; KGSL_DRV_ERR(dev_priv->device, @@ -1299,33 +1226,36 @@ static void adreno_power_stats(struct kgsl_device *device, REG_PERF_MODE_CNT | REG_PERF_STATE_ENABLE); } -static void __devinit adreno_getfunctable(struct kgsl_functable *ftbl) +void adreno_irqctrl(struct kgsl_device *device, int state) { - if (ftbl == NULL) - return; - ftbl->device_regread = adreno_regread; - ftbl->device_regwrite = adreno_regwrite; - ftbl->device_regread_isr = adreno_regread_isr; - ftbl->device_regwrite_isr = adreno_regwrite_isr; - ftbl->device_setstate = adreno_setstate; - ftbl->device_idle = adreno_idle; - ftbl->device_isidle = adreno_isidle; - ftbl->device_suspend_context = adreno_suspend_context; - ftbl->device_resume_context = adreno_resume_context; - ftbl->device_start = adreno_start; - ftbl->device_stop = adreno_stop; - ftbl->device_getproperty = adreno_getproperty; - ftbl->device_waittimestamp = adreno_waittimestamp; - ftbl->device_readtimestamp = adreno_readtimestamp; - ftbl->device_issueibcmds = adreno_ringbuffer_issueibcmds; - ftbl->device_drawctxt_create = adreno_drawctxt_create; - ftbl->device_drawctxt_destroy = adreno_drawctxt_destroy; - ftbl->device_ioctl = adreno_ioctl; - ftbl->device_setup_pt = adreno_setup_pt; - ftbl->device_cleanup_pt = adreno_cleanup_pt; - ftbl->device_power_stats = adreno_power_stats; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + adreno_dev->gpudev->irq_control(adreno_dev, state); } +static const struct kgsl_functable adreno_functable = { + /* Mandatory functions */ + .regread = adreno_regread, + .regwrite = adreno_regwrite, + .idle = adreno_idle, + .isidle = adreno_isidle, + .suspend_context = adreno_suspend_context, + .start = adreno_start, + .stop = adreno_stop, + .getproperty = adreno_getproperty, + .waittimestamp = adreno_waittimestamp, + .readtimestamp = adreno_readtimestamp, + .issueibcmds = adreno_ringbuffer_issueibcmds, + .ioctl = adreno_ioctl, + .setup_pt = adreno_setup_pt, + .cleanup_pt = adreno_cleanup_pt, + .power_stats = adreno_power_stats, + .irqctrl = adreno_irqctrl, + /* Optional functions */ + .setstate = adreno_setstate, + .drawctxt_create = adreno_drawctxt_create, + .drawctxt_destroy = adreno_drawctxt_destroy, +}; + static struct platform_device_id adreno_id_table[] = { { DEVICE_3D0_NAME, (kernel_ulong_t)&device_3d0.dev, }, { }, diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index 74b3a845723ee..94b96a1d00da9 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -1,34 +1,19 @@ /* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 __ADRENO_H #define __ADRENO_H +#include "kgsl_device.h" #include "adreno_drawctxt.h" #include "adreno_ringbuffer.h" @@ -47,75 +32,98 @@ #define KGSL_CONTEXT_TO_MEM_IDENTIFIER 0xDEADBEEF #define KGSL_CMD_IDENTIFIER 0xFEEDFACE +#ifdef CONFIG_MSM_SCM +#define ADRENO_DEFAULT_PWRSCALE_POLICY (&kgsl_pwrscale_policy_tz) +#else +#define ADRENO_DEFAULT_PWRSCALE_POLICY NULL +#endif + +enum adreno_gpurev { + ADRENO_REV_UNKNOWN = 0, + ADRENO_REV_A200 = 200, + ADRENO_REV_A205 = 205, + ADRENO_REV_A220 = 220, + ADRENO_REV_A225 = 225, +}; + +struct adreno_gpudev; + struct adreno_device { struct kgsl_device dev; /* Must be first field in this struct */ unsigned int chip_id; + enum adreno_gpurev gpurev; struct kgsl_memregion gmemspace; struct adreno_context *drawctxt_active; wait_queue_head_t ib1_wq; + const char *pfp_fwfile; unsigned int *pfp_fw; size_t pfp_fw_size; + const char *pm4_fwfile; unsigned int *pm4_fw; size_t pm4_fw_size; struct adreno_ringbuffer ringbuffer; unsigned int mharb; + struct adreno_gpudev *gpudev; }; +struct adreno_gpudev { + int (*ctxt_gpustate_shadow)(struct adreno_device *, + struct adreno_context *); + int (*ctxt_gmem_shadow)(struct adreno_device *, + struct adreno_context *); + void (*ctxt_save)(struct adreno_device *, struct adreno_context *); + void (*ctxt_restore)(struct adreno_device *, struct adreno_context *); + irqreturn_t (*irq_handler)(struct adreno_device *); + void (*irq_control)(struct adreno_device *, int); +}; + +extern struct adreno_gpudev adreno_a2xx_gpudev; + int adreno_idle(struct kgsl_device *device, unsigned int timeout); void adreno_regread(struct kgsl_device *device, unsigned int offsetwords, unsigned int *value); void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords, unsigned int value); -void adreno_regread_isr(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int *value); -void adreno_regwrite_isr(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int value); uint8_t *kgsl_sharedmem_convertaddr(struct kgsl_device *device, unsigned int pt_base, unsigned int gpuaddr, unsigned int *size); -enum adreno_gpurev { - ADRENO_REV_UNKNOWN = 0, - ADRENO_REV_A200 = 200, - ADRENO_REV_A205 = 205, - ADRENO_REV_A220 = 220, - ADRENO_REV_A225 = 225, -}; - -enum adreno_gpurev adreno_get_rev(struct adreno_device *adreno_dev); - static inline int adreno_is_a200(struct adreno_device *adreno_dev) { - return (adreno_get_rev(adreno_dev) == ADRENO_REV_A200); + return (adreno_dev->gpurev == ADRENO_REV_A200); } static inline int adreno_is_a205(struct adreno_device *adreno_dev) { - return (adreno_get_rev(adreno_dev) == ADRENO_REV_A200); + return (adreno_dev->gpurev == ADRENO_REV_A200); } static inline int adreno_is_a20x(struct adreno_device *adreno_dev) { - enum adreno_gpurev rev = adreno_get_rev(adreno_dev); - return (rev == ADRENO_REV_A200 || rev == ADRENO_REV_A205); + return (adreno_dev->gpurev == ADRENO_REV_A200 || + adreno_dev->gpurev == ADRENO_REV_A205); } static inline int adreno_is_a220(struct adreno_device *adreno_dev) { - return (adreno_get_rev(adreno_dev) == ADRENO_REV_A220); + return (adreno_dev->gpurev == ADRENO_REV_A220); } static inline int adreno_is_a225(struct adreno_device *adreno_dev) { - return (adreno_get_rev(adreno_dev) == ADRENO_REV_A225); + return (adreno_dev->gpurev == ADRENO_REV_A225); } static inline int adreno_is_a22x(struct adreno_device *adreno_dev) { - enum adreno_gpurev rev = adreno_get_rev(adreno_dev); - return (rev == ADRENO_REV_A220 || rev == ADRENO_REV_A225); + return (adreno_dev->gpurev == ADRENO_REV_A220 || + adreno_dev->gpurev == ADRENO_REV_A225); +} + +static inline int adreno_is_a2xx(struct adreno_device *adreno_dev) +{ + return (adreno_dev->gpurev <= ADRENO_REV_A225); } + #endif /*__ADRENO_H */ diff --git a/drivers/gpu/msm/adreno_a2xx.c b/drivers/gpu/msm/adreno_a2xx.c new file mode 100644 index 0000000000000..8a7ab3506b1c8 --- /dev/null +++ b/drivers/gpu/msm/adreno_a2xx.c @@ -0,0 +1,1606 @@ +/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "kgsl.h" +#include "kgsl_sharedmem.h" +#include "kgsl_cffdump.h" +#include "adreno.h" + +/* + * + * Memory Map for Register, Constant & Instruction Shadow, and Command Buffers + * (34.5KB) + * + * +---------------------+------------+-------------+---+---------------------+ + * | ALU Constant Shadow | Reg Shadow | C&V Buffers |Tex| Shader Instr Shadow | + * +---------------------+------------+-------------+---+---------------------+ + * ________________________________/ \____________________ + * / | + * +--------------+-----------+------+-----------+------------------------+ + * | Restore Regs | Save Regs | Quad | Gmem Save | Gmem Restore | unused | + * +--------------+-----------+------+-----------+------------------------+ + * + * 8K - ALU Constant Shadow (8K aligned) + * 4K - H/W Register Shadow (8K aligned) + * 4K - Command and Vertex Buffers + * - Indirect command buffer : Const/Reg restore + * - includes Loop & Bool const shadows + * - Indirect command buffer : Const/Reg save + * - Quad vertices & texture coordinates + * - Indirect command buffer : Gmem save + * - Indirect command buffer : Gmem restore + * - Unused (padding to 8KB boundary) + * <1K - Texture Constant Shadow (768 bytes) (8K aligned) + * 18K - Shader Instruction Shadow + * - 6K vertex (32 byte aligned) + * - 6K pixel (32 byte aligned) + * - 6K shared (32 byte aligned) + * + * Note: Reading constants into a shadow, one at a time using REG_TO_MEM, takes + * 3 DWORDS per DWORD transfered, plus 1 DWORD for the shadow, for a total of + * 16 bytes per constant. If the texture constants were transfered this way, + * the Command & Vertex Buffers section would extend past the 16K boundary. + * By moving the texture constant shadow area to start at 16KB boundary, we + * only require approximately 40 bytes more memory, but are able to use the + * LOAD_CONSTANT_CONTEXT shadowing feature for the textures, speeding up + * context switching. + * + * [Using LOAD_CONSTANT_CONTEXT shadowing feature for the Loop and/or Bool + * constants would require an additional 8KB each, for alignment.] + * + */ + +/* Constants */ + +#define ALU_CONSTANTS 2048 /* DWORDS */ +#define NUM_REGISTERS 1024 /* DWORDS */ +#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES +#define CMD_BUFFER_LEN 9216 /* DWORDS */ +#else +#define CMD_BUFFER_LEN 3072 /* DWORDS */ +#endif +#define TEX_CONSTANTS (32*6) /* DWORDS */ +#define BOOL_CONSTANTS 8 /* DWORDS */ +#define LOOP_CONSTANTS 56 /* DWORDS */ +#define SHADER_INSTRUCT_LOG2 9U /* 2^n == SHADER_INSTRUCTIONS */ + +/* 96-bit instructions */ +#define SHADER_INSTRUCT (1< sys) */ + +/* pre-compiled vertex shader program +* +* attribute vec4 P; +* void main(void) +* { +* gl_Position = P; +* } +*/ +#define GMEM2SYS_VTX_PGM_LEN 0x12 + +static unsigned int gmem2sys_vtx_pgm[GMEM2SYS_VTX_PGM_LEN] = { + 0x00011003, 0x00001000, 0xc2000000, + 0x00001004, 0x00001000, 0xc4000000, + 0x00001005, 0x00002000, 0x00000000, + 0x1cb81000, 0x00398a88, 0x00000003, + 0x140f803e, 0x00000000, 0xe2010100, + 0x14000000, 0x00000000, 0xe2000000 +}; + +/* pre-compiled fragment shader program +* +* precision highp float; +* uniform vec4 clear_color; +* void main(void) +* { +* gl_FragColor = clear_color; +* } +*/ + +#define GMEM2SYS_FRAG_PGM_LEN 0x0c + +static unsigned int gmem2sys_frag_pgm[GMEM2SYS_FRAG_PGM_LEN] = { + 0x00000000, 0x1002c400, 0x10000000, + 0x00001003, 0x00002000, 0x00000000, + 0x140f8000, 0x00000000, 0x22000000, + 0x14000000, 0x00000000, 0xe2000000 +}; + +/* context restore (sys -> gmem) */ +/* pre-compiled vertex shader program +* +* attribute vec4 position; +* attribute vec4 texcoord; +* varying vec4 texcoord0; +* void main() +* { +* gl_Position = position; +* texcoord0 = texcoord; +* } +*/ + +#define SYS2GMEM_VTX_PGM_LEN 0x18 + +static unsigned int sys2gmem_vtx_pgm[SYS2GMEM_VTX_PGM_LEN] = { + 0x00052003, 0x00001000, 0xc2000000, 0x00001005, + 0x00001000, 0xc4000000, 0x00001006, 0x10071000, + 0x20000000, 0x18981000, 0x0039ba88, 0x00000003, + 0x12982000, 0x40257b08, 0x00000002, 0x140f803e, + 0x00000000, 0xe2010100, 0x140f8000, 0x00000000, + 0xe2020200, 0x14000000, 0x00000000, 0xe2000000 +}; + +/* pre-compiled fragment shader program +* +* precision mediump float; +* uniform sampler2D tex0; +* varying vec4 texcoord0; +* void main() +* { +* gl_FragColor = texture2D(tex0, texcoord0.xy); +* } +*/ + +#define SYS2GMEM_FRAG_PGM_LEN 0x0f + +static unsigned int sys2gmem_frag_pgm[SYS2GMEM_FRAG_PGM_LEN] = { + 0x00011002, 0x00001000, 0xc4000000, 0x00001003, + 0x10041000, 0x20000000, 0x10000001, 0x1ffff688, + 0x00000002, 0x140f8000, 0x00000000, 0xe2000000, + 0x14000000, 0x00000000, 0xe2000000 +}; + +/* shader texture constants (sysmem -> gmem) */ +#define SYS2GMEM_TEX_CONST_LEN 6 + +static unsigned int sys2gmem_tex_const[SYS2GMEM_TEX_CONST_LEN] = { + /* Texture, FormatXYZW=Unsigned, ClampXYZ=Wrap/Repeat, + * RFMode=ZeroClamp-1, Dim=1:2d + */ + 0x00000002, /* Pitch = TBD */ + + /* Format=6:8888_WZYX, EndianSwap=0:None, ReqSize=0:256bit, DimHi=0, + * NearestClamp=1:OGL Mode + */ + 0x00000800, /* Address[31:12] = TBD */ + + /* Width, Height, EndianSwap=0:None */ + 0, /* Width & Height = TBD */ + + /* NumFormat=0:RF, DstSelXYZW=XYZW, ExpAdj=0, MagFilt=MinFilt=0:Point, + * Mip=2:BaseMap + */ + 0 << 1 | 1 << 4 | 2 << 7 | 3 << 10 | 2 << 23, + + /* VolMag=VolMin=0:Point, MinMipLvl=0, MaxMipLvl=1, LodBiasH=V=0, + * Dim3d=0 + */ + 0, + + /* BorderColor=0:ABGRBlack, ForceBC=0:diable, TriJuice=0, Aniso=0, + * Dim=1:2d, MipPacking=0 + */ + 1 << 9 /* Mip Address[31:12] = TBD */ +}; + +#define NUM_COLOR_FORMATS 13 + +static enum SURFACEFORMAT surface_format_table[NUM_COLOR_FORMATS] = { + FMT_4_4_4_4, /* COLORX_4_4_4_4 */ + FMT_1_5_5_5, /* COLORX_1_5_5_5 */ + FMT_5_6_5, /* COLORX_5_6_5 */ + FMT_8, /* COLORX_8 */ + FMT_8_8, /* COLORX_8_8 */ + FMT_8_8_8_8, /* COLORX_8_8_8_8 */ + FMT_8_8_8_8, /* COLORX_S8_8_8_8 */ + FMT_16_FLOAT, /* COLORX_16_FLOAT */ + FMT_16_16_FLOAT, /* COLORX_16_16_FLOAT */ + FMT_16_16_16_16_FLOAT, /* COLORX_16_16_16_16_FLOAT */ + FMT_32_FLOAT, /* COLORX_32_FLOAT */ + FMT_32_32_FLOAT, /* COLORX_32_32_FLOAT */ + FMT_32_32_32_32_FLOAT, /* COLORX_32_32_32_32_FLOAT */ +}; + +static unsigned int format2bytesperpixel[NUM_COLOR_FORMATS] = { + 2, /* COLORX_4_4_4_4 */ + 2, /* COLORX_1_5_5_5 */ + 2, /* COLORX_5_6_5 */ + 1, /* COLORX_8 */ + 2, /* COLORX_8_8 8*/ + 4, /* COLORX_8_8_8_8 */ + 4, /* COLORX_S8_8_8_8 */ + 2, /* COLORX_16_FLOAT */ + 4, /* COLORX_16_16_FLOAT */ + 8, /* COLORX_16_16_16_16_FLOAT */ + 4, /* COLORX_32_FLOAT */ + 8, /* COLORX_32_32_FLOAT */ + 16, /* COLORX_32_32_32_32_FLOAT */ +}; + +/* shader linkage info */ +#define SHADER_CONST_ADDR (11 * 6 + 3) + + +static unsigned int *program_shader(unsigned int *cmds, int vtxfrag, + unsigned int *shader_pgm, int dwords) +{ + /* load the patched vertex shader stream */ + *cmds++ = cp_type3_packet(CP_IM_LOAD_IMMEDIATE, 2 + dwords); + /* 0=vertex shader, 1=fragment shader */ + *cmds++ = vtxfrag; + /* instruction start & size (in 32-bit words) */ + *cmds++ = ((0 << 16) | dwords); + + memcpy(cmds, shader_pgm, dwords << 2); + cmds += dwords; + + return cmds; +} + +static unsigned int *reg_to_mem(unsigned int *cmds, uint32_t dst, + uint32_t src, int dwords) +{ + while (dwords-- > 0) { + *cmds++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmds++ = src++; + *cmds++ = dst; + dst += 4; + } + + return cmds; +} + +#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES + +static void build_reg_to_mem_range(unsigned int start, unsigned int end, + unsigned int **cmd, + struct adreno_context *drawctxt) +{ + unsigned int i = start; + + for (i = start; i <= end; i++) { + *(*cmd)++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *(*cmd)++ = i; + *(*cmd)++ = + ((drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000) + + (i - 0x2000) * 4; + } +} + +#endif + +/* chicken restore */ +static unsigned int *build_chicken_restore_cmds( + struct adreno_context *drawctxt) +{ + unsigned int *start = tmp_ctx.cmd; + unsigned int *cmds = start; + + *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmds++ = 0; + + *cmds++ = cp_type0_packet(REG_TP0_CHICKEN, 1); + tmp_ctx.chicken_restore = virt2gpu(cmds, &drawctxt->gpustate); + *cmds++ = 0x00000000; + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->chicken_restore, start, cmds); + + return cmds; +} + +/****************************************************************************/ +/* context save */ +/****************************************************************************/ + +static const unsigned int register_ranges_a20x[] = { + REG_RB_SURFACE_INFO, REG_RB_DEPTH_INFO, + REG_COHER_DEST_BASE_0, REG_PA_SC_SCREEN_SCISSOR_BR, + REG_PA_SC_WINDOW_OFFSET, REG_PA_SC_WINDOW_SCISSOR_BR, + REG_RB_STENCILREFMASK_BF, REG_PA_CL_VPORT_ZOFFSET, + REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1, + REG_PA_SC_LINE_CNTL, REG_SQ_PS_CONST, + REG_PA_SC_AA_MASK, REG_PA_SC_AA_MASK, + REG_RB_SAMPLE_COUNT_CTL, REG_RB_COLOR_DEST_MASK, + REG_PA_SU_POLY_OFFSET_FRONT_SCALE, REG_PA_SU_POLY_OFFSET_BACK_OFFSET, + REG_VGT_MAX_VTX_INDX, REG_RB_FOG_COLOR, + REG_RB_DEPTHCONTROL, REG_RB_MODECONTROL, + REG_PA_SU_POINT_SIZE, REG_PA_SC_LINE_STIPPLE, + REG_PA_SC_VIZ_QUERY, REG_PA_SC_VIZ_QUERY, + REG_VGT_VERTEX_REUSE_BLOCK_CNTL, REG_RB_DEPTH_CLEAR +}; + +static const unsigned int register_ranges_a220[] = { + REG_RB_SURFACE_INFO, REG_RB_DEPTH_INFO, + REG_COHER_DEST_BASE_0, REG_PA_SC_SCREEN_SCISSOR_BR, + REG_PA_SC_WINDOW_OFFSET, REG_PA_SC_WINDOW_SCISSOR_BR, + REG_RB_STENCILREFMASK_BF, REG_PA_CL_VPORT_ZOFFSET, + REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1, + REG_PA_SC_LINE_CNTL, REG_SQ_PS_CONST, + REG_PA_SC_AA_MASK, REG_PA_SC_AA_MASK, + REG_RB_SAMPLE_COUNT_CTL, REG_RB_COLOR_DEST_MASK, + REG_PA_SU_POLY_OFFSET_FRONT_SCALE, REG_PA_SU_POLY_OFFSET_BACK_OFFSET, + REG_A220_PC_MAX_VTX_INDX, REG_A220_PC_INDX_OFFSET, + REG_RB_COLOR_MASK, REG_RB_FOG_COLOR, + REG_RB_DEPTHCONTROL, REG_RB_COLORCONTROL, + REG_PA_CL_CLIP_CNTL, REG_PA_CL_VTE_CNTL, + REG_RB_MODECONTROL, REG_RB_SAMPLE_POS, + REG_PA_SU_POINT_SIZE, REG_PA_SU_LINE_CNTL, + REG_A220_PC_VERTEX_REUSE_BLOCK_CNTL, + REG_A220_PC_VERTEX_REUSE_BLOCK_CNTL, + REG_RB_COPY_CONTROL, REG_RB_DEPTH_CLEAR +}; + +static const unsigned int register_ranges_a225[] = { + REG_RB_SURFACE_INFO, REG_A225_RB_COLOR_INFO3, + REG_COHER_DEST_BASE_0, REG_PA_SC_SCREEN_SCISSOR_BR, + REG_PA_SC_WINDOW_OFFSET, REG_PA_SC_WINDOW_SCISSOR_BR, + REG_RB_STENCILREFMASK_BF, REG_PA_CL_VPORT_ZOFFSET, + REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1, + REG_PA_SC_LINE_CNTL, REG_SQ_PS_CONST, + REG_PA_SC_AA_MASK, REG_PA_SC_AA_MASK, + REG_RB_SAMPLE_COUNT_CTL, REG_RB_COLOR_DEST_MASK, + REG_PA_SU_POLY_OFFSET_FRONT_SCALE, REG_PA_SU_POLY_OFFSET_BACK_OFFSET, + REG_A220_PC_MAX_VTX_INDX, REG_A225_PC_MULTI_PRIM_IB_RESET_INDX, + REG_RB_COLOR_MASK, REG_RB_FOG_COLOR, + REG_RB_DEPTHCONTROL, REG_RB_COLORCONTROL, + REG_PA_CL_CLIP_CNTL, REG_PA_CL_VTE_CNTL, + REG_RB_MODECONTROL, REG_RB_SAMPLE_POS, + REG_PA_SU_POINT_SIZE, REG_PA_SU_LINE_CNTL, + REG_A220_PC_VERTEX_REUSE_BLOCK_CNTL, + REG_A220_PC_VERTEX_REUSE_BLOCK_CNTL, + REG_RB_COPY_CONTROL, REG_RB_DEPTH_CLEAR, + REG_A225_GRAS_UCP0X, REG_A225_GRAS_UCP_ENABLED +}; + + +/* save h/w regs, alu constants, texture contants, etc. ... +* requires: bool_shadow_gpuaddr, loop_shadow_gpuaddr +*/ +static void build_regsave_cmds(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt) +{ + unsigned int *start = tmp_ctx.cmd; + unsigned int *cmd = start; + + *cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + +#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES + /* Make sure the HW context has the correct register values + * before reading them. */ + *cmd++ = cp_type3_packet(CP_CONTEXT_UPDATE, 1); + *cmd++ = 0; + + { + unsigned int i = 0; + unsigned int reg_array_size = 0; + const unsigned int *ptr_register_ranges; + + /* Based on chip id choose the register ranges */ + if (adreno_is_a220(adreno_dev)) { + ptr_register_ranges = register_ranges_a220; + reg_array_size = ARRAY_SIZE(register_ranges_a220); + } else if (adreno_is_a225(adreno_dev)) { + ptr_register_ranges = register_ranges_a225; + reg_array_size = ARRAY_SIZE(register_ranges_a225); + } else { + ptr_register_ranges = register_ranges_a20x; + reg_array_size = ARRAY_SIZE(register_ranges_a20x); + } + + + /* Write HW registers into shadow */ + for (i = 0; i < (reg_array_size/2) ; i++) { + build_reg_to_mem_range(ptr_register_ranges[i*2], + ptr_register_ranges[i*2+1], + &cmd, drawctxt); + } + } + + /* Copy ALU constants */ + cmd = + reg_to_mem(cmd, (drawctxt->gpustate.gpuaddr) & 0xFFFFE000, + REG_SQ_CONSTANT_0, ALU_CONSTANTS); + + /* Copy Tex constants */ + cmd = + reg_to_mem(cmd, + (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000, + REG_SQ_FETCH_0, TEX_CONSTANTS); +#else + + /* Insert a wait for idle packet before reading the registers. + * This is to fix a hang/reset seen during stress testing. In this + * hang, CP encountered a timeout reading SQ's boolean constant + * register. There is logic in the HW that blocks reading of this + * register when the SQ block is not idle, which we believe is + * contributing to the hang.*/ + *cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + + /* H/w registers are already shadowed; just need to disable shadowing + * to prevent corruption. + */ + *cmd++ = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, 3); + *cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000; + *cmd++ = 4 << 16; /* regs, start=0 */ + *cmd++ = 0x0; /* count = 0 */ + + /* ALU constants are already shadowed; just need to disable shadowing + * to prevent corruption. + */ + *cmd++ = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, 3); + *cmd++ = drawctxt->gpustate.gpuaddr & 0xFFFFE000; + *cmd++ = 0 << 16; /* ALU, start=0 */ + *cmd++ = 0x0; /* count = 0 */ + + /* Tex constants are already shadowed; just need to disable shadowing + * to prevent corruption. + */ + *cmd++ = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, 3); + *cmd++ = (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000; + *cmd++ = 1 << 16; /* Tex, start=0 */ + *cmd++ = 0x0; /* count = 0 */ +#endif + + /* Need to handle some of the registers separately */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = REG_SQ_GPR_MANAGEMENT; + *cmd++ = tmp_ctx.reg_values[0]; + + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = REG_TP0_CHICKEN; + *cmd++ = tmp_ctx.reg_values[1]; + + if (adreno_is_a22x(adreno_dev)) { + unsigned int i; + unsigned int j = 2; + for (i = REG_A220_VSC_BIN_SIZE; i <= + REG_A220_VSC_PIPE_DATA_LENGTH_7; i++) { + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = i; + *cmd++ = tmp_ctx.reg_values[j]; + j++; + } + } + + /* Copy Boolean constants */ + cmd = reg_to_mem(cmd, tmp_ctx.bool_shadow, REG_SQ_CF_BOOLEANS, + BOOL_CONSTANTS); + + /* Copy Loop constants */ + cmd = reg_to_mem(cmd, tmp_ctx.loop_shadow, + REG_SQ_CF_LOOP, LOOP_CONSTANTS); + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->reg_save, start, cmd); + + tmp_ctx.cmd = cmd; +} + +/*copy colour, depth, & stencil buffers from graphics memory to system memory*/ +static unsigned int *build_gmem2sys_cmds(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt, + struct gmem_shadow_t *shadow) +{ + unsigned int *cmds = shadow->gmem_save_commands; + unsigned int *start = cmds; + /* Calculate the new offset based on the adjusted base */ + unsigned int bytesperpixel = format2bytesperpixel[shadow->format]; + unsigned int addr = shadow->gmemshadow.gpuaddr; + unsigned int offset = (addr - (addr & 0xfffff000)) / bytesperpixel; + + /* Store TP0_CHICKEN register */ + *cmds++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmds++ = REG_TP0_CHICKEN; + + *cmds++ = tmp_ctx.chicken_restore; + + *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmds++ = 0; + + /* Set TP0_CHICKEN to zero */ + *cmds++ = cp_type0_packet(REG_TP0_CHICKEN, 1); + *cmds++ = 0x00000000; + + /* Set PA_SC_AA_CONFIG to 0 */ + *cmds++ = cp_type0_packet(REG_PA_SC_AA_CONFIG, 1); + *cmds++ = 0x00000000; + + /* program shader */ + + /* load shader vtx constants ... 5 dwords */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 4); + *cmds++ = (0x1 << 16) | SHADER_CONST_ADDR; + *cmds++ = 0; + /* valid(?) vtx constant flag & addr */ + *cmds++ = shadow->quad_vertices.gpuaddr | 0x3; + /* limit = 12 dwords */ + *cmds++ = 0x00000030; + + /* Invalidate L2 cache to make sure vertices are updated */ + *cmds++ = cp_type0_packet(REG_TC_CNTL_STATUS, 1); + *cmds++ = 0x1; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 4); + *cmds++ = CP_REG(REG_VGT_MAX_VTX_INDX); + *cmds++ = 0x00ffffff; /* REG_VGT_MAX_VTX_INDX */ + *cmds++ = 0x0; /* REG_VGT_MIN_VTX_INDX */ + *cmds++ = 0x00000000; /* REG_VGT_INDX_OFFSET */ + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_PA_SC_AA_MASK); + *cmds++ = 0x0000ffff; /* REG_PA_SC_AA_MASK */ + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_RB_COLORCONTROL); + *cmds++ = 0x00000c20; + + /* Repartition shaders */ + *cmds++ = cp_type0_packet(REG_SQ_INST_STORE_MANAGMENT, 1); + *cmds++ = 0x180; + + /* Invalidate Vertex & Pixel instruction code address and sizes */ + *cmds++ = cp_type3_packet(CP_INVALIDATE_STATE, 1); + *cmds++ = 0x00003F00; + + *cmds++ = cp_type3_packet(CP_SET_SHADER_BASES, 1); + *cmds++ = (0x80000000) | 0x180; + + /* load the patched vertex shader stream */ + cmds = program_shader(cmds, 0, gmem2sys_vtx_pgm, GMEM2SYS_VTX_PGM_LEN); + + /* Load the patched fragment shader stream */ + cmds = + program_shader(cmds, 1, gmem2sys_frag_pgm, GMEM2SYS_FRAG_PGM_LEN); + + /* SQ_PROGRAM_CNTL / SQ_CONTEXT_MISC */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(REG_SQ_PROGRAM_CNTL); + if (adreno_is_a22x(adreno_dev)) + *cmds++ = 0x10018001; + else + *cmds++ = 0x10010001; + *cmds++ = 0x00000008; + + /* resolve */ + + /* PA_CL_VTE_CNTL */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_PA_CL_VTE_CNTL); + /* disable X/Y/Z transforms, X/Y/Z are premultiplied by W */ + *cmds++ = 0x00000b00; + + /* program surface info */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(REG_RB_SURFACE_INFO); + *cmds++ = shadow->gmem_pitch; /* pitch, MSAA = 1 */ + + /* RB_COLOR_INFO Endian=none, Linear, Format=RGBA8888, Swap=0, + * Base=gmem_base + */ + /* gmem base assumed 4K aligned. */ + BUG_ON(tmp_ctx.gmem_base & 0xFFF); + *cmds++ = + (shadow-> + format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT) | tmp_ctx.gmem_base; + + /* disable Z */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_RB_DEPTHCONTROL); + if (adreno_is_a22x(adreno_dev)) + *cmds++ = 0x08; + else + *cmds++ = 0; + + /* set REG_PA_SU_SC_MODE_CNTL + * Front_ptype = draw triangles + * Back_ptype = draw triangles + * Provoking vertex = last + */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_PA_SU_SC_MODE_CNTL); + *cmds++ = 0x00080240; + + /* Use maximum scissor values -- quad vertices already have the + * correct bounds */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(REG_PA_SC_SCREEN_SCISSOR_TL); + *cmds++ = (0 << 16) | 0; + *cmds++ = (0x1fff << 16) | (0x1fff); + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(REG_PA_SC_WINDOW_SCISSOR_TL); + *cmds++ = (unsigned int)((1U << 31) | (0 << 16) | 0); + *cmds++ = (0x1fff << 16) | (0x1fff); + + /* load the viewport so that z scale = clear depth and + * z offset = 0.0f + */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(REG_PA_CL_VPORT_ZSCALE); + *cmds++ = 0xbf800000; /* -1.0f */ + *cmds++ = 0x0; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_RB_COLOR_MASK); + *cmds++ = 0x0000000f; /* R = G = B = 1:enabled */ + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_RB_COLOR_DEST_MASK); + *cmds++ = 0xffffffff; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(REG_SQ_WRAPPING_0); + *cmds++ = 0x00000000; + *cmds++ = 0x00000000; + + /* load the stencil ref value + * $AAM - do this later + */ + + /* load the COPY state */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 6); + *cmds++ = CP_REG(REG_RB_COPY_CONTROL); + *cmds++ = 0; /* RB_COPY_CONTROL */ + *cmds++ = addr & 0xfffff000; /* RB_COPY_DEST_BASE */ + *cmds++ = shadow->pitch >> 5; /* RB_COPY_DEST_PITCH */ + + /* Endian=none, Linear, Format=RGBA8888,Swap=0,!Dither, + * MaskWrite:R=G=B=A=1 + */ + *cmds++ = 0x0003c008 | + (shadow->format << RB_COPY_DEST_INFO__COPY_DEST_FORMAT__SHIFT); + /* Make sure we stay in offsetx field. */ + BUG_ON(offset & 0xfffff000); + *cmds++ = offset; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_RB_MODECONTROL); + *cmds++ = 0x6; /* EDRAM copy */ + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_PA_CL_CLIP_CNTL); + *cmds++ = 0x00010000; + + if (adreno_is_a22x(adreno_dev)) { + *cmds++ = cp_type3_packet(CP_SET_DRAW_INIT_FLAGS, 1); + *cmds++ = 0; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_A220_RB_LRZ_VSC_CONTROL); + *cmds++ = 0x0000000; + + *cmds++ = cp_type3_packet(CP_DRAW_INDX, 3); + *cmds++ = 0; /* viz query info. */ + /* PrimType=RectList, SrcSel=AutoIndex, VisCullMode=Ignore*/ + *cmds++ = 0x00004088; + *cmds++ = 3; /* NumIndices=3 */ + } else { + /* queue the draw packet */ + *cmds++ = cp_type3_packet(CP_DRAW_INDX, 2); + *cmds++ = 0; /* viz query info. */ + /* PrimType=RectList, NumIndices=3, SrcSel=AutoIndex */ + *cmds++ = 0x00030088; + } + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, shadow->gmem_save, start, cmds); + + return cmds; +} + +/* context restore */ + +/*copy colour, depth, & stencil buffers from system memory to graphics memory*/ +static unsigned int *build_sys2gmem_cmds(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt, + struct gmem_shadow_t *shadow) +{ + unsigned int *cmds = shadow->gmem_restore_commands; + unsigned int *start = cmds; + + /* Store TP0_CHICKEN register */ + *cmds++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmds++ = REG_TP0_CHICKEN; + *cmds++ = tmp_ctx.chicken_restore; + + *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmds++ = 0; + + /* Set TP0_CHICKEN to zero */ + *cmds++ = cp_type0_packet(REG_TP0_CHICKEN, 1); + *cmds++ = 0x00000000; + + /* Set PA_SC_AA_CONFIG to 0 */ + *cmds++ = cp_type0_packet(REG_PA_SC_AA_CONFIG, 1); + *cmds++ = 0x00000000; + /* shader constants */ + + /* vertex buffer constants */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 7); + + *cmds++ = (0x1 << 16) | (9 * 6); + /* valid(?) vtx constant flag & addr */ + *cmds++ = shadow->quad_vertices.gpuaddr | 0x3; + /* limit = 12 dwords */ + *cmds++ = 0x00000030; + /* valid(?) vtx constant flag & addr */ + *cmds++ = shadow->quad_texcoords.gpuaddr | 0x3; + /* limit = 8 dwords */ + *cmds++ = 0x00000020; + *cmds++ = 0; + *cmds++ = 0; + + /* Invalidate L2 cache to make sure vertices are updated */ + *cmds++ = cp_type0_packet(REG_TC_CNTL_STATUS, 1); + *cmds++ = 0x1; + + cmds = program_shader(cmds, 0, sys2gmem_vtx_pgm, SYS2GMEM_VTX_PGM_LEN); + + /* Repartition shaders */ + *cmds++ = cp_type0_packet(REG_SQ_INST_STORE_MANAGMENT, 1); + *cmds++ = 0x180; + + /* Invalidate Vertex & Pixel instruction code address and sizes */ + *cmds++ = cp_type3_packet(CP_INVALIDATE_STATE, 1); + *cmds++ = 0x00000300; /* 0x100 = Vertex, 0x200 = Pixel */ + + *cmds++ = cp_type3_packet(CP_SET_SHADER_BASES, 1); + *cmds++ = (0x80000000) | 0x180; + + /* Load the patched fragment shader stream */ + cmds = + program_shader(cmds, 1, sys2gmem_frag_pgm, SYS2GMEM_FRAG_PGM_LEN); + + /* SQ_PROGRAM_CNTL / SQ_CONTEXT_MISC */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(REG_SQ_PROGRAM_CNTL); + *cmds++ = 0x10030002; + *cmds++ = 0x00000008; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_PA_SC_AA_MASK); + *cmds++ = 0x0000ffff; /* REG_PA_SC_AA_MASK */ + + if (!adreno_is_a22x(adreno_dev)) { + /* PA_SC_VIZ_QUERY */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_PA_SC_VIZ_QUERY); + *cmds++ = 0x0; /*REG_PA_SC_VIZ_QUERY */ + } + + /* RB_COLORCONTROL */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_RB_COLORCONTROL); + *cmds++ = 0x00000c20; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 4); + *cmds++ = CP_REG(REG_VGT_MAX_VTX_INDX); + *cmds++ = 0x00ffffff; /* mmVGT_MAX_VTX_INDX */ + *cmds++ = 0x0; /* mmVGT_MIN_VTX_INDX */ + *cmds++ = 0x00000000; /* mmVGT_INDX_OFFSET */ + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(REG_VGT_VERTEX_REUSE_BLOCK_CNTL); + *cmds++ = 0x00000002; /* mmVGT_VERTEX_REUSE_BLOCK_CNTL */ + *cmds++ = 0x00000002; /* mmVGT_OUT_DEALLOC_CNTL */ + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_SQ_INTERPOLATOR_CNTL); + *cmds++ = 0xffffffff; /* mmSQ_INTERPOLATOR_CNTL */ + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_PA_SC_AA_CONFIG); + *cmds++ = 0x00000000; /* REG_PA_SC_AA_CONFIG */ + + /* set REG_PA_SU_SC_MODE_CNTL + * Front_ptype = draw triangles + * Back_ptype = draw triangles + * Provoking vertex = last + */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_PA_SU_SC_MODE_CNTL); + *cmds++ = 0x00080240; + + /* texture constants */ + *cmds++ = + cp_type3_packet(CP_SET_CONSTANT, (SYS2GMEM_TEX_CONST_LEN + 1)); + *cmds++ = (0x1 << 16) | (0 * 6); + memcpy(cmds, sys2gmem_tex_const, SYS2GMEM_TEX_CONST_LEN << 2); + cmds[0] |= (shadow->pitch >> 5) << 22; + cmds[1] |= + shadow->gmemshadow.gpuaddr | surface_format_table[shadow->format]; + cmds[2] |= (shadow->width - 1) | (shadow->height - 1) << 13; + cmds += SYS2GMEM_TEX_CONST_LEN; + + /* program surface info */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(REG_RB_SURFACE_INFO); + *cmds++ = shadow->gmem_pitch; /* pitch, MSAA = 1 */ + + /* RB_COLOR_INFO Endian=none, Linear, Format=RGBA8888, Swap=0, + * Base=gmem_base + */ + *cmds++ = + (shadow-> + format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT) | tmp_ctx.gmem_base; + + /* RB_DEPTHCONTROL */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_RB_DEPTHCONTROL); + + if (adreno_is_a22x(adreno_dev)) + *cmds++ = 8; /* disable Z */ + else + *cmds++ = 0; /* disable Z */ + + /* Use maximum scissor values -- quad vertices already + * have the correct bounds */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(REG_PA_SC_SCREEN_SCISSOR_TL); + *cmds++ = (0 << 16) | 0; + *cmds++ = ((0x1fff) << 16) | 0x1fff; + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(REG_PA_SC_WINDOW_SCISSOR_TL); + *cmds++ = (unsigned int)((1U << 31) | (0 << 16) | 0); + *cmds++ = ((0x1fff) << 16) | 0x1fff; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_PA_CL_VTE_CNTL); + /* disable X/Y/Z transforms, X/Y/Z are premultiplied by W */ + *cmds++ = 0x00000b00; + + /*load the viewport so that z scale = clear depth and z offset = 0.0f */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(REG_PA_CL_VPORT_ZSCALE); + *cmds++ = 0xbf800000; + *cmds++ = 0x0; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_RB_COLOR_MASK); + *cmds++ = 0x0000000f; /* R = G = B = 1:enabled */ + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_RB_COLOR_DEST_MASK); + *cmds++ = 0xffffffff; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3); + *cmds++ = CP_REG(REG_SQ_WRAPPING_0); + *cmds++ = 0x00000000; + *cmds++ = 0x00000000; + + /* load the stencil ref value + * $AAM - do this later + */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_RB_MODECONTROL); + /* draw pixels with color and depth/stencil component */ + *cmds++ = 0x4; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_PA_CL_CLIP_CNTL); + *cmds++ = 0x00010000; + + if (adreno_is_a22x(adreno_dev)) { + *cmds++ = cp_type3_packet(CP_SET_DRAW_INIT_FLAGS, 1); + *cmds++ = 0; + + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = CP_REG(REG_A220_RB_LRZ_VSC_CONTROL); + *cmds++ = 0x0000000; + + *cmds++ = cp_type3_packet(CP_DRAW_INDX, 3); + *cmds++ = 0; /* viz query info. */ + /* PrimType=RectList, SrcSel=AutoIndex, VisCullMode=Ignore*/ + *cmds++ = 0x00004088; + *cmds++ = 3; /* NumIndices=3 */ + } else { + /* queue the draw packet */ + *cmds++ = cp_type3_packet(CP_DRAW_INDX, 2); + *cmds++ = 0; /* viz query info. */ + /* PrimType=RectList, NumIndices=3, SrcSel=AutoIndex */ + *cmds++ = 0x00030088; + } + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, shadow->gmem_restore, start, cmds); + + return cmds; +} + +static void build_regrestore_cmds(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt) +{ + unsigned int *start = tmp_ctx.cmd; + unsigned int *cmd = start; + + unsigned int i = 0; + unsigned int reg_array_size = 0; + const unsigned int *ptr_register_ranges; + + *cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + + /* H/W Registers */ + /* deferred cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, ???); */ + cmd++; +#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES + /* Force mismatch */ + *cmd++ = ((drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000) | 1; +#else + *cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000; +#endif + + /* Based on chip id choose the registers ranges*/ + if (adreno_is_a220(adreno_dev)) { + ptr_register_ranges = register_ranges_a220; + reg_array_size = ARRAY_SIZE(register_ranges_a220); + } else if (adreno_is_a225(adreno_dev)) { + ptr_register_ranges = register_ranges_a225; + reg_array_size = ARRAY_SIZE(register_ranges_a225); + } else { + ptr_register_ranges = register_ranges_a20x; + reg_array_size = ARRAY_SIZE(register_ranges_a20x); + } + + + for (i = 0; i < (reg_array_size/2); i++) { + cmd = reg_range(cmd, ptr_register_ranges[i*2], + ptr_register_ranges[i*2+1]); + } + + /* Now we know how many register blocks we have, we can compute command + * length + */ + start[2] = + cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, (cmd - start) - 3); + /* Enable shadowing for the entire register block. */ +#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES + start[4] |= (0 << 24) | (4 << 16); /* Disable shadowing. */ +#else + start[4] |= (1 << 24) | (4 << 16); +#endif + + /* Need to handle some of the registers separately */ + *cmd++ = cp_type0_packet(REG_SQ_GPR_MANAGEMENT, 1); + tmp_ctx.reg_values[0] = virt2gpu(cmd, &drawctxt->gpustate); + *cmd++ = 0x00040400; + + *cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + *cmd++ = cp_type0_packet(REG_TP0_CHICKEN, 1); + tmp_ctx.reg_values[1] = virt2gpu(cmd, &drawctxt->gpustate); + *cmd++ = 0x00000000; + + if (adreno_is_a22x(adreno_dev)) { + unsigned int i; + unsigned int j = 2; + for (i = REG_A220_VSC_BIN_SIZE; i <= + REG_A220_VSC_PIPE_DATA_LENGTH_7; i++) { + *cmd++ = cp_type0_packet(i, 1); + tmp_ctx.reg_values[j] = virt2gpu(cmd, + &drawctxt->gpustate); + *cmd++ = 0x00000000; + j++; + } + } + + /* ALU Constants */ + *cmd++ = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, 3); + *cmd++ = drawctxt->gpustate.gpuaddr & 0xFFFFE000; +#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES + *cmd++ = (0 << 24) | (0 << 16) | 0; /* Disable shadowing */ +#else + *cmd++ = (1 << 24) | (0 << 16) | 0; +#endif + *cmd++ = ALU_CONSTANTS; + + /* Texture Constants */ + *cmd++ = cp_type3_packet(CP_LOAD_CONSTANT_CONTEXT, 3); + *cmd++ = (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000; +#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES + /* Disable shadowing */ + *cmd++ = (0 << 24) | (1 << 16) | 0; +#else + *cmd++ = (1 << 24) | (1 << 16) | 0; +#endif + *cmd++ = TEX_CONSTANTS; + + /* Boolean Constants */ + *cmd++ = cp_type3_packet(CP_SET_CONSTANT, 1 + BOOL_CONSTANTS); + *cmd++ = (2 << 16) | 0; + + /* the next BOOL_CONSTANT dwords is the shadow area for + * boolean constants. + */ + tmp_ctx.bool_shadow = virt2gpu(cmd, &drawctxt->gpustate); + cmd += BOOL_CONSTANTS; + + /* Loop Constants */ + *cmd++ = cp_type3_packet(CP_SET_CONSTANT, 1 + LOOP_CONSTANTS); + *cmd++ = (3 << 16) | 0; + + /* the next LOOP_CONSTANTS dwords is the shadow area for + * loop constants. + */ + tmp_ctx.loop_shadow = virt2gpu(cmd, &drawctxt->gpustate); + cmd += LOOP_CONSTANTS; + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->reg_restore, start, cmd); + + tmp_ctx.cmd = cmd; +} + +static void +build_shader_save_restore_cmds(struct adreno_context *drawctxt) +{ + unsigned int *cmd = tmp_ctx.cmd; + unsigned int *save, *restore, *fixup; + unsigned int *startSizeVtx, *startSizePix, *startSizeShared; + unsigned int *partition1; + unsigned int *shaderBases, *partition2; + + /* compute vertex, pixel and shared instruction shadow GPU addresses */ + tmp_ctx.shader_vertex = drawctxt->gpustate.gpuaddr + SHADER_OFFSET; + tmp_ctx.shader_pixel = tmp_ctx.shader_vertex + SHADER_SHADOW_SIZE; + tmp_ctx.shader_shared = tmp_ctx.shader_pixel + SHADER_SHADOW_SIZE; + + /* restore shader partitioning and instructions */ + + restore = cmd; /* start address */ + + /* Invalidate Vertex & Pixel instruction code address and sizes */ + *cmd++ = cp_type3_packet(CP_INVALIDATE_STATE, 1); + *cmd++ = 0x00000300; /* 0x100 = Vertex, 0x200 = Pixel */ + + /* Restore previous shader vertex & pixel instruction bases. */ + *cmd++ = cp_type3_packet(CP_SET_SHADER_BASES, 1); + shaderBases = cmd++; /* TBD #5: shader bases (from fixup) */ + + /* write the shader partition information to a scratch register */ + *cmd++ = cp_type0_packet(REG_SQ_INST_STORE_MANAGMENT, 1); + partition1 = cmd++; /* TBD #4a: partition info (from save) */ + + /* load vertex shader instructions from the shadow. */ + *cmd++ = cp_type3_packet(CP_IM_LOAD, 2); + *cmd++ = tmp_ctx.shader_vertex + 0x0; /* 0x0 = Vertex */ + startSizeVtx = cmd++; /* TBD #1: start/size (from save) */ + + /* load pixel shader instructions from the shadow. */ + *cmd++ = cp_type3_packet(CP_IM_LOAD, 2); + *cmd++ = tmp_ctx.shader_pixel + 0x1; /* 0x1 = Pixel */ + startSizePix = cmd++; /* TBD #2: start/size (from save) */ + + /* load shared shader instructions from the shadow. */ + *cmd++ = cp_type3_packet(CP_IM_LOAD, 2); + *cmd++ = tmp_ctx.shader_shared + 0x2; /* 0x2 = Shared */ + startSizeShared = cmd++; /* TBD #3: start/size (from save) */ + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->shader_restore, restore, cmd); + + /* + * fixup SET_SHADER_BASES data + * + * since self-modifying PM4 code is being used here, a seperate + * command buffer is used for this fixup operation, to ensure the + * commands are not read by the PM4 engine before the data fields + * have been written. + */ + + fixup = cmd; /* start address */ + + /* write the shader partition information to a scratch register */ + *cmd++ = cp_type0_packet(REG_SCRATCH_REG2, 1); + partition2 = cmd++; /* TBD #4b: partition info (from save) */ + + /* mask off unused bits, then OR with shader instruction memory size */ + *cmd++ = cp_type3_packet(CP_REG_RMW, 3); + *cmd++ = REG_SCRATCH_REG2; + /* AND off invalid bits. */ + *cmd++ = 0x0FFF0FFF; + /* OR in instruction memory size */ + *cmd++ = (unsigned int)((SHADER_INSTRUCT_LOG2 - 5U) << 29); + + /* write the computed value to the SET_SHADER_BASES data field */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = REG_SCRATCH_REG2; + /* TBD #5: shader bases (to restore) */ + *cmd++ = virt2gpu(shaderBases, &drawctxt->gpustate); + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->shader_fixup, fixup, cmd); + + /* save shader partitioning and instructions */ + + save = cmd; /* start address */ + + *cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + + /* fetch the SQ_INST_STORE_MANAGMENT register value, + * store the value in the data fields of the SET_CONSTANT commands + * above. + */ + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = REG_SQ_INST_STORE_MANAGMENT; + /* TBD #4a: partition info (to restore) */ + *cmd++ = virt2gpu(partition1, &drawctxt->gpustate); + *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmd++ = REG_SQ_INST_STORE_MANAGMENT; + /* TBD #4b: partition info (to fixup) */ + *cmd++ = virt2gpu(partition2, &drawctxt->gpustate); + + + /* store the vertex shader instructions */ + *cmd++ = cp_type3_packet(CP_IM_STORE, 2); + *cmd++ = tmp_ctx.shader_vertex + 0x0; /* 0x0 = Vertex */ + /* TBD #1: start/size (to restore) */ + *cmd++ = virt2gpu(startSizeVtx, &drawctxt->gpustate); + + /* store the pixel shader instructions */ + *cmd++ = cp_type3_packet(CP_IM_STORE, 2); + *cmd++ = tmp_ctx.shader_pixel + 0x1; /* 0x1 = Pixel */ + /* TBD #2: start/size (to restore) */ + *cmd++ = virt2gpu(startSizePix, &drawctxt->gpustate); + + /* store the shared shader instructions if vertex base is nonzero */ + + *cmd++ = cp_type3_packet(CP_IM_STORE, 2); + *cmd++ = tmp_ctx.shader_shared + 0x2; /* 0x2 = Shared */ + /* TBD #3: start/size (to restore) */ + *cmd++ = virt2gpu(startSizeShared, &drawctxt->gpustate); + + + *cmd++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmd++ = 0; + + /* create indirect buffer command for above command sequence */ + create_ib1(drawctxt, drawctxt->shader_save, save, cmd); + + tmp_ctx.cmd = cmd; +} + +/* create buffers for saving/restoring registers, constants, & GMEM */ +static int a2xx_ctxt_gpustate_shadow(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt) +{ + int result; + + /* Allocate vmalloc memory to store the gpustate */ + result = kgsl_allocate(&drawctxt->gpustate, + drawctxt->pagetable, CONTEXT_SIZE); + + if (result) + return result; + + drawctxt->flags |= CTXT_FLAGS_STATE_SHADOW; + + /* Blank out h/w register, constant, and command buffer shadows. */ + kgsl_sharedmem_set(&drawctxt->gpustate, 0, 0, CONTEXT_SIZE); + + /* set-up command and vertex buffer pointers */ + tmp_ctx.cmd = tmp_ctx.start + = (unsigned int *)((char *)drawctxt->gpustate.hostptr + CMD_OFFSET); + + /* build indirect command buffers to save & restore regs/constants */ + adreno_idle(&adreno_dev->dev, KGSL_TIMEOUT_DEFAULT); + build_regrestore_cmds(adreno_dev, drawctxt); + build_regsave_cmds(adreno_dev, drawctxt); + + build_shader_save_restore_cmds(drawctxt); + + kgsl_cache_range_op(&drawctxt->gpustate, + KGSL_CACHE_OP_FLUSH); + + kgsl_cffdump_syncmem(NULL, &drawctxt->gpustate, + drawctxt->gpustate.gpuaddr, + drawctxt->gpustate.size, false); + return 0; +} + +/* create buffers for saving/restoring registers, constants, & GMEM */ +static int a2xx_ctxt_gmem_shadow(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt) +{ + int result; + + calc_gmemsize(&drawctxt->context_gmem_shadow, + adreno_dev->gmemspace.sizebytes); + tmp_ctx.gmem_base = adreno_dev->gmemspace.gpu_base; + + result = kgsl_allocate(&drawctxt->context_gmem_shadow.gmemshadow, + drawctxt->pagetable, drawctxt->context_gmem_shadow.size); + + if (result) + return result; + + /* we've allocated the shadow, when swapped out, GMEM must be saved. */ + drawctxt->flags |= CTXT_FLAGS_GMEM_SHADOW | CTXT_FLAGS_GMEM_SAVE; + + /* blank out gmem shadow. */ + kgsl_sharedmem_set(&drawctxt->context_gmem_shadow.gmemshadow, 0, 0, + drawctxt->context_gmem_shadow.size); + + /* build quad vertex buffer */ + build_quad_vtxbuff(drawctxt, &drawctxt->context_gmem_shadow, + &tmp_ctx.cmd); + + /* build TP0_CHICKEN register restore command buffer */ + tmp_ctx.cmd = build_chicken_restore_cmds(drawctxt); + + /* build indirect command buffers to save & restore gmem */ + /* Idle because we are reading PM override registers */ + adreno_idle(&adreno_dev->dev, KGSL_TIMEOUT_DEFAULT); + drawctxt->context_gmem_shadow.gmem_save_commands = tmp_ctx.cmd; + tmp_ctx.cmd = + build_gmem2sys_cmds(adreno_dev, drawctxt, + &drawctxt->context_gmem_shadow); + drawctxt->context_gmem_shadow.gmem_restore_commands = tmp_ctx.cmd; + tmp_ctx.cmd = + build_sys2gmem_cmds(adreno_dev, drawctxt, + &drawctxt->context_gmem_shadow); + + kgsl_cache_range_op(&drawctxt->context_gmem_shadow.gmemshadow, + KGSL_CACHE_OP_FLUSH); + + kgsl_cffdump_syncmem(NULL, + &drawctxt->context_gmem_shadow.gmemshadow, + drawctxt->context_gmem_shadow.gmemshadow.gpuaddr, + drawctxt->context_gmem_shadow.gmemshadow.size, false); + + return 0; +} + +static void a2xx_ctxt_save(struct adreno_device *adreno_dev, + struct adreno_context *context) +{ + struct kgsl_device *device = &adreno_dev->dev; + + if (context == NULL) + return; + + if (context->flags & CTXT_FLAGS_GPU_HANG) + KGSL_CTXT_WARN(device, + "Current active context has caused gpu hang\n"); + + KGSL_CTXT_INFO(device, + "active context flags %08x\n", context->flags); + + /* save registers and constants. */ + adreno_ringbuffer_issuecmds(device, 0, context->reg_save, 3); + + if (context->flags & CTXT_FLAGS_SHADER_SAVE) { + /* save shader partitioning and instructions. */ + adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, + context->shader_save, 3); + + /* fixup shader partitioning parameter for + * SET_SHADER_BASES. + */ + adreno_ringbuffer_issuecmds(device, 0, + context->shader_fixup, 3); + + context->flags |= CTXT_FLAGS_SHADER_RESTORE; + } + + if ((context->flags & CTXT_FLAGS_GMEM_SAVE) && + (context->flags & CTXT_FLAGS_GMEM_SHADOW)) { + /* save gmem. + * (note: changes shader. shader must already be saved.) + */ + adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, + context->context_gmem_shadow.gmem_save, 3); + + /* Restore TP0_CHICKEN */ + adreno_ringbuffer_issuecmds(device, 0, + context->chicken_restore, 3); + + context->flags |= CTXT_FLAGS_GMEM_RESTORE; + } +} + +static void a2xx_ctxt_restore(struct adreno_device *adreno_dev, + struct adreno_context *context) +{ + struct kgsl_device *device = &adreno_dev->dev; + unsigned int cmds[5]; + + if (context == NULL) { + /* No context - set the default apgetable and thats it */ + kgsl_mmu_setstate(device, device->mmu.defaultpagetable); + return; + } + + KGSL_CTXT_INFO(device, "context flags %08x\n", context->flags); + + cmds[0] = cp_nop_packet(1); + cmds[1] = KGSL_CONTEXT_TO_MEM_IDENTIFIER; + cmds[2] = cp_type3_packet(CP_MEM_WRITE, 2); + cmds[3] = device->memstore.gpuaddr + + KGSL_DEVICE_MEMSTORE_OFFSET(current_context); + cmds[4] = (unsigned int) context; + adreno_ringbuffer_issuecmds(device, 0, cmds, 5); + kgsl_mmu_setstate(device, context->pagetable); + +#ifndef CONFIG_MSM_KGSL_CFF_DUMP_NO_CONTEXT_MEM_DUMP + kgsl_cffdump_syncmem(NULL, &context->gpustate, + context->gpustate.gpuaddr, LCC_SHADOW_SIZE + + REG_SHADOW_SIZE + CMD_BUFFER_SIZE + TEX_SHADOW_SIZE, false); +#endif + + /* restore gmem. + * (note: changes shader. shader must not already be restored.) + */ + if (context->flags & CTXT_FLAGS_GMEM_RESTORE) { + adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, + context->context_gmem_shadow.gmem_restore, 3); + + /* Restore TP0_CHICKEN */ + adreno_ringbuffer_issuecmds(device, 0, + context->chicken_restore, 3); + + context->flags &= ~CTXT_FLAGS_GMEM_RESTORE; + } + + /* restore registers and constants. */ + adreno_ringbuffer_issuecmds(device, 0, + context->reg_restore, 3); + + /* restore shader instructions & partitioning. */ + if (context->flags & CTXT_FLAGS_SHADER_RESTORE) { + adreno_ringbuffer_issuecmds(device, 0, + context->shader_restore, 3); + } + + if (adreno_is_a20x(adreno_dev)) { + cmds[0] = cp_type3_packet(CP_SET_BIN_BASE_OFFSET, 1); + cmds[1] = context->bin_base_offset; + adreno_ringbuffer_issuecmds(device, 0, cmds, 2); + } +} + +/* + * Interrupt management + * + * a2xx interrupt control is distributed among the various + * hardware components (RB, CP, MMU). The main interrupt + * tells us which component fired the interrupt, but one needs + * to go to the individual component to find out why. The + * following functions provide the broken out support for + * managing the interrupts + */ + +#define RBBM_INT_MASK RBBM_INT_CNTL__RDERR_INT_MASK + +#define CP_INT_MASK \ + (CP_INT_CNTL__T0_PACKET_IN_IB_MASK | \ + CP_INT_CNTL__OPCODE_ERROR_MASK | \ + CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK | \ + CP_INT_CNTL__RESERVED_BIT_ERROR_MASK | \ + CP_INT_CNTL__IB_ERROR_MASK | \ + CP_INT_CNTL__IB1_INT_MASK | \ + CP_INT_CNTL__RB_INT_MASK) + +#define VALID_STATUS_COUNT_MAX 10 + +static struct { + unsigned int mask; + const char *message; +} kgsl_cp_error_irqs[] = { + { CP_INT_CNTL__T0_PACKET_IN_IB_MASK, + "ringbuffer TO packet in IB interrupt" }, + { CP_INT_CNTL__OPCODE_ERROR_MASK, + "ringbuffer opcode error interrupt" }, + { CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK, + "ringbuffer protected mode error interrupt" }, + { CP_INT_CNTL__RESERVED_BIT_ERROR_MASK, + "ringbuffer reserved bit error interrupt" }, + { CP_INT_CNTL__IB_ERROR_MASK, + "ringbuffer IB error interrupt" }, +}; + +static void a2xx_cp_intrcallback(struct kgsl_device *device) +{ + unsigned int status = 0, num_reads = 0, master_status = 0; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; + int i; + + adreno_regread(device, REG_MASTER_INT_SIGNAL, &master_status); + while (!status && (num_reads < VALID_STATUS_COUNT_MAX) && + (master_status & MASTER_INT_SIGNAL__CP_INT_STAT)) { + adreno_regread(device, REG_CP_INT_STATUS, &status); + adreno_regread(device, REG_MASTER_INT_SIGNAL, + &master_status); + num_reads++; + } + if (num_reads > 1) + KGSL_DRV_WARN(device, + "Looped %d times to read REG_CP_INT_STATUS\n", + num_reads); + if (!status) { + if (master_status & MASTER_INT_SIGNAL__CP_INT_STAT) { + /* This indicates that we could not read CP_INT_STAT. + * As a precaution just wake up processes so + * they can check their timestamps. Since, we + * did not ack any interrupts this interrupt will + * be generated again */ + KGSL_DRV_WARN(device, "Unable to read CP_INT_STATUS\n"); + wake_up_interruptible_all(&device->wait_queue); + } else + KGSL_DRV_WARN(device, "Spurious interrput detected\n"); + return; + } + + if (status & CP_INT_CNTL__RB_INT_MASK) { + /* signal intr completion event */ + unsigned int enableflag = 0; + kgsl_sharedmem_writel(&rb->device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable), + enableflag); + wmb(); + KGSL_CMD_WARN(rb->device, "ringbuffer rb interrupt\n"); + } + + for (i = 0; i < ARRAY_SIZE(kgsl_cp_error_irqs); i++) { + if (status & kgsl_cp_error_irqs[i].mask) { + KGSL_CMD_CRIT(rb->device, "%s\n", + kgsl_cp_error_irqs[i].message); + /* + * on fatal errors, turn off the interrupts to + * avoid storming. This has the side effect of + * forcing a PM dump when the timestamp times out + */ + + kgsl_pwrctrl_irq(rb->device, KGSL_PWRFLAGS_OFF); + } + } + + /* only ack bits we understand */ + status &= CP_INT_MASK; + adreno_regwrite(device, REG_CP_INT_ACK, status); + + if (status & (CP_INT_CNTL__IB1_INT_MASK | CP_INT_CNTL__RB_INT_MASK)) { + KGSL_CMD_WARN(rb->device, "ringbuffer ib1/rb interrupt\n"); + wake_up_interruptible_all(&device->wait_queue); + atomic_notifier_call_chain(&(device->ts_notifier_list), + device->id, + NULL); + } +} + +static void a2xx_rbbm_intrcallback(struct kgsl_device *device) +{ + unsigned int status = 0; + unsigned int rderr = 0; + + adreno_regread(device, REG_RBBM_INT_STATUS, &status); + + if (status & RBBM_INT_CNTL__RDERR_INT_MASK) { + union rbbm_read_error_u rerr; + adreno_regread(device, REG_RBBM_READ_ERROR, &rderr); + rerr.val = rderr; + if (rerr.f.read_address == REG_CP_INT_STATUS && + rerr.f.read_error && + rerr.f.read_requester) + KGSL_DRV_WARN(device, + "rbbm read error interrupt: %08x\n", rderr); + else + KGSL_DRV_CRIT(device, + "rbbm read error interrupt: %08x\n", rderr); + } + + status &= RBBM_INT_MASK; + adreno_regwrite(device, REG_RBBM_INT_ACK, status); +} + +irqreturn_t a2xx_irq_handler(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = &adreno_dev->dev; + irqreturn_t result = IRQ_NONE; + unsigned int status; + + adreno_regread(device, REG_MASTER_INT_SIGNAL, &status); + + if (status & MASTER_INT_SIGNAL__MH_INT_STAT) { + kgsl_mh_intrcallback(device); + result = IRQ_HANDLED; + } + + if (status & MASTER_INT_SIGNAL__CP_INT_STAT) { + a2xx_cp_intrcallback(device); + result = IRQ_HANDLED; + } + + if (status & MASTER_INT_SIGNAL__RBBM_INT_STAT) { + a2xx_rbbm_intrcallback(device); + result = IRQ_HANDLED; + } + + return result; +} + +static void a2xx_irq_control(struct adreno_device *adreno_dev, int state) +{ + struct kgsl_device *device = &adreno_dev->dev; + + if (state) { + adreno_regwrite(device, REG_RBBM_INT_CNTL, RBBM_INT_MASK); + adreno_regwrite(device, REG_CP_INT_CNTL, CP_INT_MASK); + adreno_regwrite(device, MH_INTERRUPT_MASK, KGSL_MMU_INT_MASK); + } else { + adreno_regwrite(device, REG_RBBM_INT_CNTL, 0); + adreno_regwrite(device, REG_CP_INT_CNTL, 0); + adreno_regwrite(device, MH_INTERRUPT_MASK, 0); + } + + /* Force the writes to post before touching the IRQ line */ + wmb(); +} + +struct adreno_gpudev adreno_a2xx_gpudev = { + .ctxt_gpustate_shadow = a2xx_ctxt_gpustate_shadow, + .ctxt_gmem_shadow = a2xx_ctxt_gmem_shadow, + .ctxt_save = a2xx_ctxt_save, + .ctxt_restore = a2xx_ctxt_restore, + .irq_handler = a2xx_irq_handler, + .irq_control = a2xx_irq_control, +}; diff --git a/drivers/gpu/msm/adreno_debugfs.c b/drivers/gpu/msm/adreno_debugfs.c index 487e1f7fe10fe..b9178495adc76 100644 --- a/drivers/gpu/msm/adreno_debugfs.c +++ b/drivers/gpu/msm/adreno_debugfs.c @@ -14,12 +14,13 @@ #include #include #include +#include #include "kgsl.h" #include "adreno_postmortem.h" #include "adreno.h" -#include "a200_reg.h" +#include "a2xx_reg.h" unsigned int kgsl_cff_dump_enable; int kgsl_pm_regs_enabled; @@ -130,7 +131,7 @@ static ssize_t kgsl_ib_dump_read( if (!ppos || !device || !kgsl_ib_base) return 0; - kgsl_regread(device, REG_MH_MMU_PT_BASE, &pt_base); + kgsl_regread(device, MH_MMU_PT_BASE, &pt_base); base_addr = kgsl_sharedmem_convertaddr(device, pt_base, kgsl_ib_base, &ib_memsize); @@ -395,8 +396,8 @@ static void kgsl_mh_reg_read_fill(struct kgsl_device *device, int i, int j; for (j = 0; j < linec; ++j) { - kgsl_regwrite(device, REG_MH_DEBUG_CTRL, i+j); - kgsl_regread(device, REG_MH_DEBUG_DATA, vals+j); + kgsl_regwrite(device, MH_DEBUG_CTRL, i+j); + kgsl_regread(device, MH_DEBUG_DATA, vals+j); } } diff --git a/drivers/gpu/msm/adreno_debugfs.h b/drivers/gpu/msm/adreno_debugfs.h index 680eb849fc4ce..0356ac6e11909 100644 --- a/drivers/gpu/msm/adreno_debugfs.h +++ b/drivers/gpu/msm/adreno_debugfs.h @@ -1,29 +1,13 @@ /* Copyright (c) 2002,2008-2011, Code Aurora Forum. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 __ADRENO_DEBUGFS_H diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c index ab28b0d2362e1..b7b0ea46007a3 100644 --- a/drivers/gpu/msm/adreno_drawctxt.c +++ b/drivers/gpu/msm/adreno_drawctxt.c @@ -10,287 +10,15 @@ * GNU General Public License for more details. * */ + #include #include "kgsl.h" - +#include "kgsl_sharedmem.h" #include "adreno.h" -#include "adreno_pm4types.h" -#include "adreno_drawctxt.h" - -/* - * - * Memory Map for Register, Constant & Instruction Shadow, and Command Buffers - * (34.5KB) - * - * +---------------------+------------+-------------+---+---------------------+ - * | ALU Constant Shadow | Reg Shadow | C&V Buffers |Tex| Shader Instr Shadow | - * +---------------------+------------+-------------+---+---------------------+ - * ________________________________/ \____________________ - * / | - * +--------------+-----------+------+-----------+------------------------+ - * | Restore Regs | Save Regs | Quad | Gmem Save | Gmem Restore | unused | - * +--------------+-----------+------+-----------+------------------------+ - * - * 8K - ALU Constant Shadow (8K aligned) - * 4K - H/W Register Shadow (8K aligned) - * 4K - Command and Vertex Buffers - * - Indirect command buffer : Const/Reg restore - * - includes Loop & Bool const shadows - * - Indirect command buffer : Const/Reg save - * - Quad vertices & texture coordinates - * - Indirect command buffer : Gmem save - * - Indirect command buffer : Gmem restore - * - Unused (padding to 8KB boundary) - * <1K - Texture Constant Shadow (768 bytes) (8K aligned) - * 18K - Shader Instruction Shadow - * - 6K vertex (32 byte aligned) - * - 6K pixel (32 byte aligned) - * - 6K shared (32 byte aligned) - * - * Note: Reading constants into a shadow, one at a time using REG_TO_MEM, takes - * 3 DWORDS per DWORD transfered, plus 1 DWORD for the shadow, for a total of - * 16 bytes per constant. If the texture constants were transfered this way, - * the Command & Vertex Buffers section would extend past the 16K boundary. - * By moving the texture constant shadow area to start at 16KB boundary, we - * only require approximately 40 bytes more memory, but are able to use the - * LOAD_CONSTANT_CONTEXT shadowing feature for the textures, speeding up - * context switching. - * - * [Using LOAD_CONSTANT_CONTEXT shadowing feature for the Loop and/or Bool - * constants would require an additional 8KB each, for alignment.] - * - */ - -/* Constants */ - -#define ALU_CONSTANTS 2048 /* DWORDS */ -#define NUM_REGISTERS 1024 /* DWORDS */ -#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES -#define CMD_BUFFER_LEN 9216 /* DWORDS */ -#else -#define CMD_BUFFER_LEN 3072 /* DWORDS */ -#endif -#define TEX_CONSTANTS (32*6) /* DWORDS */ -#define BOOL_CONSTANTS 8 /* DWORDS */ -#define LOOP_CONSTANTS 56 /* DWORDS */ -#define SHADER_INSTRUCT_LOG2 9U /* 2^n == SHADER_INSTRUCTIONS */ - -#if defined(PM4_IM_STORE) -/* 96-bit instructions */ -#define SHADER_INSTRUCT (1<= 0x10000) { - exp += 16; - u >>= 16; - } - if (u >= 0x100) { - exp += 8; - u >>= 8; - } - if (u >= 0x10) { - exp += 4; - u >>= 4; - } - if (u >= 0x4) { - exp += 2; - u >>= 2; - } - if (u >= 0x2) { - exp += 1; - u >>= 1; - } - - /* Calculate fraction */ - if (23 > exp) - frac = (uintval & (~(1 << exp))) << (23 - exp); - - /* Exp is biased by 127 and shifted 23 bits */ - exp = (exp + 127) << 23; - - return exp | frac; -} - -/* context save (gmem -> sys) */ - -/* pre-compiled vertex shader program -* -* attribute vec4 P; -* void main(void) -* { -* gl_Position = P; -* } -*/ -#define GMEM2SYS_VTX_PGM_LEN 0x12 - -static unsigned int gmem2sys_vtx_pgm[GMEM2SYS_VTX_PGM_LEN] = { - 0x00011003, 0x00001000, 0xc2000000, - 0x00001004, 0x00001000, 0xc4000000, - 0x00001005, 0x00002000, 0x00000000, - 0x1cb81000, 0x00398a88, 0x00000003, - 0x140f803e, 0x00000000, 0xe2010100, - 0x14000000, 0x00000000, 0xe2000000 -}; - -/* pre-compiled fragment shader program -* -* precision highp float; -* uniform vec4 clear_color; -* void main(void) -* { -* gl_FragColor = clear_color; -* } -*/ - -#define GMEM2SYS_FRAG_PGM_LEN 0x0c - -static unsigned int gmem2sys_frag_pgm[GMEM2SYS_FRAG_PGM_LEN] = { - 0x00000000, 0x1002c400, 0x10000000, - 0x00001003, 0x00002000, 0x00000000, - 0x140f8000, 0x00000000, 0x22000000, - 0x14000000, 0x00000000, 0xe2000000 -}; - -/* context restore (sys -> gmem) */ -/* pre-compiled vertex shader program -* -* attribute vec4 position; -* attribute vec4 texcoord; -* varying vec4 texcoord0; -* void main() -* { -* gl_Position = position; -* texcoord0 = texcoord; -* } -*/ - -#define SYS2GMEM_VTX_PGM_LEN 0x18 - -static unsigned int sys2gmem_vtx_pgm[SYS2GMEM_VTX_PGM_LEN] = { - 0x00052003, 0x00001000, 0xc2000000, 0x00001005, - 0x00001000, 0xc4000000, 0x00001006, 0x10071000, - 0x20000000, 0x18981000, 0x0039ba88, 0x00000003, - 0x12982000, 0x40257b08, 0x00000002, 0x140f803e, - 0x00000000, 0xe2010100, 0x140f8000, 0x00000000, - 0xe2020200, 0x14000000, 0x00000000, 0xe2000000 -}; - -/* pre-compiled fragment shader program -* -* precision mediump float; -* uniform sampler2D tex0; -* varying vec4 texcoord0; -* void main() -* { -* gl_FragColor = texture2D(tex0, texcoord0.xy); -* } -*/ - -#define SYS2GMEM_FRAG_PGM_LEN 0x0f - -static unsigned int sys2gmem_frag_pgm[SYS2GMEM_FRAG_PGM_LEN] = { - 0x00011002, 0x00001000, 0xc4000000, 0x00001003, - 0x10041000, 0x20000000, 0x10000001, 0x1ffff688, - 0x00000002, 0x140f8000, 0x00000000, 0xe2000000, - 0x14000000, 0x00000000, 0xe2000000 -}; - -/* shader texture constants (sysmem -> gmem) */ -#define SYS2GMEM_TEX_CONST_LEN 6 - -static unsigned int sys2gmem_tex_const[SYS2GMEM_TEX_CONST_LEN] = { - /* Texture, FormatXYZW=Unsigned, ClampXYZ=Wrap/Repeat, - * RFMode=ZeroClamp-1, Dim=1:2d - */ - 0x00000002, /* Pitch = TBD */ - - /* Format=6:8888_WZYX, EndianSwap=0:None, ReqSize=0:256bit, DimHi=0, - * NearestClamp=1:OGL Mode - */ - 0x00000800, /* Address[31:12] = TBD */ - - /* Width, Height, EndianSwap=0:None */ - 0, /* Width & Height = TBD */ - - /* NumFormat=0:RF, DstSelXYZW=XYZW, ExpAdj=0, MagFilt=MinFilt=0:Point, - * Mip=2:BaseMap - */ - 0 << 1 | 1 << 4 | 2 << 7 | 3 << 10 | 2 << 23, - - /* VolMag=VolMin=0:Point, MinMipLvl=0, MaxMipLvl=1, LodBiasH=V=0, - * Dim3d=0 - */ - 0, - - /* BorderColor=0:ABGRBlack, ForceBC=0:diable, TriJuice=0, Aniso=0, - * Dim=1:2d, MipPacking=0 - */ - 1 << 9 /* Mip Address[31:12] = TBD */ -}; /* quad for copying GMEM to context shadow */ -#define QUAD_LEN 12 +#define QUAD_LEN 12 static unsigned int gmem_copy_quad[QUAD_LEN] = { 0x00000000, 0x00000000, 0x3f800000, @@ -299,7 +27,7 @@ static unsigned int gmem_copy_quad[QUAD_LEN] = { 0x00000000, 0x00000000, 0x3f800000 }; -#define TEXCOORD_LEN 8 +#define TEXCOORD_LEN 8 static unsigned int gmem_copy_texcoord[TEXCOORD_LEN] = { 0x00000000, 0x3f800000, @@ -308,882 +36,35 @@ static unsigned int gmem_copy_texcoord[TEXCOORD_LEN] = { 0x3f800000, 0x00000000 }; -#define NUM_COLOR_FORMATS 13 - -static enum SURFACEFORMAT surface_format_table[NUM_COLOR_FORMATS] = { - FMT_4_4_4_4, /* COLORX_4_4_4_4 */ - FMT_1_5_5_5, /* COLORX_1_5_5_5 */ - FMT_5_6_5, /* COLORX_5_6_5 */ - FMT_8, /* COLORX_8 */ - FMT_8_8, /* COLORX_8_8 */ - FMT_8_8_8_8, /* COLORX_8_8_8_8 */ - FMT_8_8_8_8, /* COLORX_S8_8_8_8 */ - FMT_16_FLOAT, /* COLORX_16_FLOAT */ - FMT_16_16_FLOAT, /* COLORX_16_16_FLOAT */ - FMT_16_16_16_16_FLOAT, /* COLORX_16_16_16_16_FLOAT */ - FMT_32_FLOAT, /* COLORX_32_FLOAT */ - FMT_32_32_FLOAT, /* COLORX_32_32_FLOAT */ - FMT_32_32_32_32_FLOAT, /* COLORX_32_32_32_32_FLOAT */ -}; - -static unsigned int format2bytesperpixel[NUM_COLOR_FORMATS] = { - 2, /* COLORX_4_4_4_4 */ - 2, /* COLORX_1_5_5_5 */ - 2, /* COLORX_5_6_5 */ - 1, /* COLORX_8 */ - 2, /* COLORX_8_8 8*/ - 4, /* COLORX_8_8_8_8 */ - 4, /* COLORX_S8_8_8_8 */ - 2, /* COLORX_16_FLOAT */ - 4, /* COLORX_16_16_FLOAT */ - 8, /* COLORX_16_16_16_16_FLOAT */ - 4, /* COLORX_32_FLOAT */ - 8, /* COLORX_32_32_FLOAT */ - 16, /* COLORX_32_32_32_32_FLOAT */ -}; - -/* shader linkage info */ -#define SHADER_CONST_ADDR (11 * 6 + 3) - -/* gmem command buffer length */ -#define PM4_REG(reg) ((0x4 << 16) | (GSL_HAL_SUBBLOCK_OFFSET(reg))) - -/* functions */ -static void config_gmemsize(struct gmem_shadow_t *shadow, int gmem_size) -{ - int w = 64, h = 64; /* 16KB surface, minimum */ - - shadow->format = COLORX_8_8_8_8; - /* convert from bytes to 32-bit words */ - gmem_size = (gmem_size + 3) / 4; - - /* find the right surface size, close to a square. */ - while (w * h < gmem_size) - if (w < h) - w *= 2; - else - h *= 2; - - shadow->width = w; - shadow->pitch = w; - shadow->height = h; - shadow->gmem_pitch = shadow->pitch; - - shadow->size = shadow->pitch * shadow->height * 4; -} - -static unsigned int gpuaddr(unsigned int *cmd, struct kgsl_memdesc *memdesc) -{ - return memdesc->gpuaddr + ((char *)cmd - (char *)memdesc->hostptr); -} - -static void -create_ib1(struct adreno_context *drawctxt, unsigned int *cmd, - unsigned int *start, unsigned int *end) -{ - cmd[0] = PM4_HDR_INDIRECT_BUFFER_PFD; - cmd[1] = gpuaddr(start, &drawctxt->gpustate); - cmd[2] = end - start; -} - -static unsigned int *program_shader(unsigned int *cmds, int vtxfrag, - unsigned int *shader_pgm, int dwords) -{ - /* load the patched vertex shader stream */ - *cmds++ = pm4_type3_packet(PM4_IM_LOAD_IMMEDIATE, 2 + dwords); - /* 0=vertex shader, 1=fragment shader */ - *cmds++ = vtxfrag; - /* instruction start & size (in 32-bit words) */ - *cmds++ = ((0 << 16) | dwords); - - memcpy(cmds, shader_pgm, dwords << 2); - cmds += dwords; - - return cmds; -} - -static unsigned int *reg_to_mem(unsigned int *cmds, uint32_t dst, - uint32_t src, int dwords) -{ - while (dwords-- > 0) { - *cmds++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmds++ = src++; - *cmds++ = dst; - dst += 4; - } - - return cmds; -} - -#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES - -static void build_reg_to_mem_range(unsigned int start, unsigned int end, - unsigned int **cmd, - struct adreno_context *drawctxt) -{ - unsigned int i = start; - - for (i = start; i <= end; i++) { - *(*cmd)++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *(*cmd)++ = i; - *(*cmd)++ = - ((drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000) + - (i - 0x2000) * 4; - } -} - -#endif - -/* chicken restore */ -static unsigned int *build_chicken_restore_cmds( - struct adreno_context *drawctxt, - struct tmp_ctx *ctx) -{ - unsigned int *start = ctx->cmd; - unsigned int *cmds = start; - - *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmds++ = 0; - - *cmds++ = pm4_type0_packet(REG_TP0_CHICKEN, 1); - ctx->chicken_restore = gpuaddr(cmds, &drawctxt->gpustate); - *cmds++ = 0x00000000; - - /* create indirect buffer command for above command sequence */ - create_ib1(drawctxt, drawctxt->chicken_restore, start, cmds); - - return cmds; -} - -/* save h/w regs, alu constants, texture contants, etc. ... -* requires: bool_shadow_gpuaddr, loop_shadow_gpuaddr -*/ -static void build_regsave_cmds(struct adreno_device *adreno_dev, - struct adreno_context *drawctxt, - struct tmp_ctx *ctx) -{ - unsigned int *start = ctx->cmd; - unsigned int *cmd = start; - - *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmd++ = 0; - -#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES - /* Make sure the HW context has the correct register values - * before reading them. */ - *cmd++ = pm4_type3_packet(PM4_CONTEXT_UPDATE, 1); - *cmd++ = 0; -#endif - -#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES - /* Write HW registers into shadow */ - build_reg_to_mem_range(REG_RB_SURFACE_INFO, REG_RB_DEPTH_INFO, - &cmd, drawctxt); - build_reg_to_mem_range(REG_COHER_DEST_BASE_0, - REG_PA_SC_SCREEN_SCISSOR_BR, - &cmd, drawctxt); - build_reg_to_mem_range(REG_PA_SC_WINDOW_OFFSET, - REG_PA_SC_WINDOW_SCISSOR_BR, - &cmd, drawctxt); - if (!adreno_is_a220(adreno_dev)) { - build_reg_to_mem_range(REG_VGT_MAX_VTX_INDX, REG_RB_FOG_COLOR, - &cmd, drawctxt); - } else { - build_reg_to_mem_range(REG_LEIA_PC_MAX_VTX_INDX, - REG_LEIA_PC_INDX_OFFSET, - &cmd, drawctxt); - build_reg_to_mem_range(REG_RB_COLOR_MASK, - REG_RB_FOG_COLOR, - &cmd, drawctxt); - } - build_reg_to_mem_range(REG_RB_STENCILREFMASK_BF, - REG_PA_CL_VPORT_ZOFFSET, - &cmd, drawctxt); - build_reg_to_mem_range(REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1, - &cmd, drawctxt); - if (!adreno_is_a220(adreno_dev)) { - build_reg_to_mem_range(REG_RB_DEPTHCONTROL, REG_RB_MODECONTROL, - &cmd, drawctxt); - build_reg_to_mem_range(REG_PA_SU_POINT_SIZE, - REG_PA_SC_LINE_STIPPLE, - &cmd, drawctxt); - build_reg_to_mem_range(REG_PA_SC_VIZ_QUERY, REG_PA_SC_VIZ_QUERY, - &cmd, drawctxt); - } else { - build_reg_to_mem_range(REG_RB_DEPTHCONTROL, - REG_RB_COLORCONTROL, - &cmd, drawctxt); - build_reg_to_mem_range(REG_PA_CL_CLIP_CNTL, - REG_PA_CL_VTE_CNTL, - &cmd, drawctxt); - build_reg_to_mem_range(REG_RB_MODECONTROL, - REG_LEIA_GRAS_CONTROL, - &cmd, drawctxt); - build_reg_to_mem_range(REG_PA_SU_POINT_SIZE, - REG_PA_SU_LINE_CNTL, - &cmd, drawctxt); - } - build_reg_to_mem_range(REG_PA_SC_LINE_CNTL, REG_SQ_PS_CONST, - &cmd, drawctxt); - build_reg_to_mem_range(REG_PA_SC_AA_MASK, REG_PA_SC_AA_MASK, - &cmd, drawctxt); - if (!adreno_is_a220(adreno_dev)) { - build_reg_to_mem_range(REG_VGT_VERTEX_REUSE_BLOCK_CNTL, - REG_RB_DEPTH_CLEAR, - &cmd, drawctxt); - } else { - build_reg_to_mem_range(REG_LEIA_PC_VERTEX_REUSE_BLOCK_CNTL, - REG_LEIA_PC_VERTEX_REUSE_BLOCK_CNTL, - &cmd, drawctxt); - build_reg_to_mem_range(REG_RB_COPY_CONTROL, - REG_RB_DEPTH_CLEAR, - &cmd, drawctxt); - } - build_reg_to_mem_range(REG_RB_SAMPLE_COUNT_CTL, - REG_RB_COLOR_DEST_MASK, - &cmd, drawctxt); - build_reg_to_mem_range(REG_PA_SU_POLY_OFFSET_FRONT_SCALE, - REG_PA_SU_POLY_OFFSET_BACK_OFFSET, - &cmd, drawctxt); - - /* Copy ALU constants */ - cmd = - reg_to_mem(cmd, (drawctxt->gpustate.gpuaddr) & 0xFFFFE000, - REG_SQ_CONSTANT_0, ALU_CONSTANTS); - - /* Copy Tex constants */ - cmd = - reg_to_mem(cmd, - (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000, - REG_SQ_FETCH_0, TEX_CONSTANTS); -#else - - /* Insert a wait for idle packet before reading the registers. - * This is to fix a hang/reset seen during stress testing. In this - * hang, CP encountered a timeout reading SQ's boolean constant - * register. There is logic in the HW that blocks reading of this - * register when the SQ block is not idle, which we believe is - * contributing to the hang.*/ - *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmd++ = 0; - - /* H/w registers are already shadowed; just need to disable shadowing - * to prevent corruption. - */ - *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); - *cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000; - *cmd++ = 4 << 16; /* regs, start=0 */ - *cmd++ = 0x0; /* count = 0 */ - - /* ALU constants are already shadowed; just need to disable shadowing - * to prevent corruption. - */ - *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); - *cmd++ = drawctxt->gpustate.gpuaddr & 0xFFFFE000; - *cmd++ = 0 << 16; /* ALU, start=0 */ - *cmd++ = 0x0; /* count = 0 */ - - /* Tex constants are already shadowed; just need to disable shadowing - * to prevent corruption. - */ - *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); - *cmd++ = (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000; - *cmd++ = 1 << 16; /* Tex, start=0 */ - *cmd++ = 0x0; /* count = 0 */ -#endif - - /* Need to handle some of the registers separately */ - *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmd++ = REG_SQ_GPR_MANAGEMENT; - *cmd++ = ctx->reg_values[0]; - - *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmd++ = REG_TP0_CHICKEN; - *cmd++ = ctx->reg_values[1]; - - *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmd++ = REG_RBBM_PM_OVERRIDE2; - *cmd++ = ctx->reg_values[2]; - - if (adreno_is_a220(adreno_dev)) { - unsigned int i; - unsigned int j = 3; - for (i = REG_LEIA_VSC_BIN_SIZE; i <= - REG_LEIA_VSC_PIPE_DATA_LENGTH_7; i++) { - *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmd++ = i; - *cmd++ = ctx->reg_values[j]; - j++; - } - } - - /* Copy Boolean constants */ - cmd = reg_to_mem(cmd, ctx->bool_shadow, REG_SQ_CF_BOOLEANS, - BOOL_CONSTANTS); - - /* Copy Loop constants */ - cmd = reg_to_mem(cmd, ctx->loop_shadow, REG_SQ_CF_LOOP, LOOP_CONSTANTS); - - /* create indirect buffer command for above command sequence */ - create_ib1(drawctxt, drawctxt->reg_save, start, cmd); - - ctx->cmd = cmd; -} - -/*copy colour, depth, & stencil buffers from graphics memory to system memory*/ -static unsigned int *build_gmem2sys_cmds(struct adreno_device *adreno_dev, - struct adreno_context *drawctxt, - struct tmp_ctx *ctx, - struct gmem_shadow_t *shadow) -{ - unsigned int *cmds = shadow->gmem_save_commands; - unsigned int *start = cmds; - /* Calculate the new offset based on the adjusted base */ - unsigned int bytesperpixel = format2bytesperpixel[shadow->format]; - unsigned int addr = shadow->gmemshadow.gpuaddr; - unsigned int offset = (addr - (addr & 0xfffff000)) / bytesperpixel; - - /* Store TP0_CHICKEN register */ - *cmds++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmds++ = REG_TP0_CHICKEN; - if (ctx) - *cmds++ = ctx->chicken_restore; - else - cmds++; - - *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmds++ = 0; - - /* Set TP0_CHICKEN to zero */ - *cmds++ = pm4_type0_packet(REG_TP0_CHICKEN, 1); - *cmds++ = 0x00000000; - - /* Set PA_SC_AA_CONFIG to 0 */ - *cmds++ = pm4_type0_packet(REG_PA_SC_AA_CONFIG, 1); - *cmds++ = 0x00000000; - - /* program shader */ - - /* load shader vtx constants ... 5 dwords */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 4); - *cmds++ = (0x1 << 16) | SHADER_CONST_ADDR; - *cmds++ = 0; - /* valid(?) vtx constant flag & addr */ - *cmds++ = shadow->quad_vertices.gpuaddr | 0x3; - /* limit = 12 dwords */ - *cmds++ = 0x00000030; - - /* Invalidate L2 cache to make sure vertices are updated */ - *cmds++ = pm4_type0_packet(REG_TC_CNTL_STATUS, 1); - *cmds++ = 0x1; - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 4); - *cmds++ = PM4_REG(REG_VGT_MAX_VTX_INDX); - *cmds++ = 0x00ffffff; /* REG_VGT_MAX_VTX_INDX */ - *cmds++ = 0x0; /* REG_VGT_MIN_VTX_INDX */ - *cmds++ = 0x00000000; /* REG_VGT_INDX_OFFSET */ - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_PA_SC_AA_MASK); - *cmds++ = 0x0000ffff; /* REG_PA_SC_AA_MASK */ - - /* load the patched vertex shader stream */ - cmds = program_shader(cmds, 0, gmem2sys_vtx_pgm, GMEM2SYS_VTX_PGM_LEN); - - /* Load the patched fragment shader stream */ - cmds = - program_shader(cmds, 1, gmem2sys_frag_pgm, GMEM2SYS_FRAG_PGM_LEN); - - /* SQ_PROGRAM_CNTL / SQ_CONTEXT_MISC */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_SQ_PROGRAM_CNTL); - if (adreno_is_a220(adreno_dev)) - *cmds++ = 0x10018001; - else - *cmds++ = 0x10010001; - *cmds++ = 0x00000008; - - /* resolve */ - - /* PA_CL_VTE_CNTL */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_PA_CL_VTE_CNTL); - /* disable X/Y/Z transforms, X/Y/Z are premultiplied by W */ - *cmds++ = 0x00000b00; - - /* program surface info */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_RB_SURFACE_INFO); - *cmds++ = shadow->gmem_pitch; /* pitch, MSAA = 1 */ - - /* RB_COLOR_INFO Endian=none, Linear, Format=RGBA8888, Swap=0, - * Base=gmem_base - */ - /* gmem base assumed 4K aligned. */ - if (ctx) { - BUG_ON(ctx->gmem_base & 0xFFF); - *cmds++ = - (shadow-> - format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT) | ctx-> - gmem_base; - } else { - unsigned int temp = *cmds; - *cmds++ = (temp & ~RB_COLOR_INFO__COLOR_FORMAT_MASK) | - (shadow->format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT); - } - - /* disable Z */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_RB_DEPTHCONTROL); - if (adreno_is_a220(adreno_dev)) - *cmds++ = 0x08; - else - *cmds++ = 0; - - /* set REG_PA_SU_SC_MODE_CNTL - * Front_ptype = draw triangles - * Back_ptype = draw triangles - * Provoking vertex = last - */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_PA_SU_SC_MODE_CNTL); - *cmds++ = 0x00080240; - - /* Use maximum scissor values -- quad vertices already have the - * correct bounds */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_PA_SC_SCREEN_SCISSOR_TL); - *cmds++ = (0 << 16) | 0; - *cmds++ = (0x1fff << 16) | (0x1fff); - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_PA_SC_WINDOW_SCISSOR_TL); - *cmds++ = (unsigned int)((1U << 31) | (0 << 16) | 0); - *cmds++ = (0x1fff << 16) | (0x1fff); - - /* load the viewport so that z scale = clear depth and - * z offset = 0.0f - */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_PA_CL_VPORT_ZSCALE); - *cmds++ = 0xbf800000; /* -1.0f */ - *cmds++ = 0x0; - - /* load the stencil ref value - * $AAM - do this later - */ - - /* load the COPY state */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 6); - *cmds++ = PM4_REG(REG_RB_COPY_CONTROL); - *cmds++ = 0; /* RB_COPY_CONTROL */ - *cmds++ = addr & 0xfffff000; /* RB_COPY_DEST_BASE */ - *cmds++ = shadow->pitch >> 5; /* RB_COPY_DEST_PITCH */ - - /* Endian=none, Linear, Format=RGBA8888,Swap=0,!Dither, - * MaskWrite:R=G=B=A=1 - */ - *cmds++ = 0x0003c008 | - (shadow->format << RB_COPY_DEST_INFO__COPY_DEST_FORMAT__SHIFT); - /* Make sure we stay in offsetx field. */ - BUG_ON(offset & 0xfffff000); - *cmds++ = offset; - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_RB_MODECONTROL); - *cmds++ = 0x6; /* EDRAM copy */ - - if (adreno_is_a220(adreno_dev)) { - *cmds++ = 0xc0043600; /* packet 3 3D_DRAW_INDX_2 */ - *cmds++ = 0x0; - *cmds++ = 0x00004046; /* tristrip */ - *cmds++ = 0x00000004; /* NUM_INDICES */ - *cmds++ = 0x00010000; /* index: 0x00, 0x01 */ - *cmds++ = 0x00030002; /* index: 0x02, 0x03 */ - } else { - /* queue the draw packet */ - *cmds++ = pm4_type3_packet(PM4_DRAW_INDX, 2); - *cmds++ = 0; /* viz query info. */ - /* PrimType=RectList, NumIndices=3, SrcSel=AutoIndex */ - *cmds++ = 0x00030088; - } - - /* create indirect buffer command for above command sequence */ - create_ib1(drawctxt, shadow->gmem_save, start, cmds); - - return cmds; -} - -/* context restore */ - -/*copy colour, depth, & stencil buffers from system memory to graphics memory*/ -static unsigned int *build_sys2gmem_cmds(struct adreno_device *adreno_dev, - struct adreno_context *drawctxt, - struct tmp_ctx *ctx, - struct gmem_shadow_t *shadow) -{ - unsigned int *cmds = shadow->gmem_restore_commands; - unsigned int *start = cmds; - - /* Store TP0_CHICKEN register */ - *cmds++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmds++ = REG_TP0_CHICKEN; - if (ctx) - *cmds++ = ctx->chicken_restore; - else - cmds++; - - *cmds++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmds++ = 0; - - /* Set TP0_CHICKEN to zero */ - *cmds++ = pm4_type0_packet(REG_TP0_CHICKEN, 1); - *cmds++ = 0x00000000; - - /* Set PA_SC_AA_CONFIG to 0 */ - *cmds++ = pm4_type0_packet(REG_PA_SC_AA_CONFIG, 1); - *cmds++ = 0x00000000; - /* shader constants */ - - /* vertex buffer constants */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 7); - - *cmds++ = (0x1 << 16) | (9 * 6); - /* valid(?) vtx constant flag & addr */ - *cmds++ = shadow->quad_vertices.gpuaddr | 0x3; - /* limit = 12 dwords */ - *cmds++ = 0x00000030; - /* valid(?) vtx constant flag & addr */ - *cmds++ = shadow->quad_texcoords.gpuaddr | 0x3; - /* limit = 8 dwords */ - *cmds++ = 0x00000020; - *cmds++ = 0; - *cmds++ = 0; - - /* Invalidate L2 cache to make sure vertices are updated */ - *cmds++ = pm4_type0_packet(REG_TC_CNTL_STATUS, 1); - *cmds++ = 0x1; - - cmds = program_shader(cmds, 0, sys2gmem_vtx_pgm, SYS2GMEM_VTX_PGM_LEN); - - /* Load the patched fragment shader stream */ - cmds = - program_shader(cmds, 1, sys2gmem_frag_pgm, SYS2GMEM_FRAG_PGM_LEN); - - /* SQ_PROGRAM_CNTL / SQ_CONTEXT_MISC */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_SQ_PROGRAM_CNTL); - *cmds++ = 0x10030002; - *cmds++ = 0x00000008; - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_PA_SC_AA_MASK); - *cmds++ = 0x0000ffff; /* REG_PA_SC_AA_MASK */ - - if (!adreno_is_a220(adreno_dev)) { - /* PA_SC_VIZ_QUERY */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_PA_SC_VIZ_QUERY); - *cmds++ = 0x0; /*REG_PA_SC_VIZ_QUERY */ - } - - /* RB_COLORCONTROL */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_RB_COLORCONTROL); - *cmds++ = 0x00000c20; - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 4); - *cmds++ = PM4_REG(REG_VGT_MAX_VTX_INDX); - *cmds++ = 0x00ffffff; /* mmVGT_MAX_VTX_INDX */ - *cmds++ = 0x0; /* mmVGT_MIN_VTX_INDX */ - *cmds++ = 0x00000000; /* mmVGT_INDX_OFFSET */ - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_VGT_VERTEX_REUSE_BLOCK_CNTL); - *cmds++ = 0x00000002; /* mmVGT_VERTEX_REUSE_BLOCK_CNTL */ - *cmds++ = 0x00000002; /* mmVGT_OUT_DEALLOC_CNTL */ - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_SQ_INTERPOLATOR_CNTL); - *cmds++ = 0xffffffff; /* mmSQ_INTERPOLATOR_CNTL */ - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_PA_SC_AA_CONFIG); - *cmds++ = 0x00000000; /* REG_PA_SC_AA_CONFIG */ - - /* set REG_PA_SU_SC_MODE_CNTL - * Front_ptype = draw triangles - * Back_ptype = draw triangles - * Provoking vertex = last - */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_PA_SU_SC_MODE_CNTL); - *cmds++ = 0x00080240; - - /* texture constants */ - *cmds++ = - pm4_type3_packet(PM4_SET_CONSTANT, (SYS2GMEM_TEX_CONST_LEN + 1)); - *cmds++ = (0x1 << 16) | (0 * 6); - memcpy(cmds, sys2gmem_tex_const, SYS2GMEM_TEX_CONST_LEN << 2); - cmds[0] |= (shadow->pitch >> 5) << 22; - cmds[1] |= - shadow->gmemshadow.gpuaddr | surface_format_table[shadow->format]; - cmds[2] |= (shadow->width - 1) | (shadow->height - 1) << 13; - cmds += SYS2GMEM_TEX_CONST_LEN; - - /* program surface info */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_RB_SURFACE_INFO); - *cmds++ = shadow->gmem_pitch; /* pitch, MSAA = 1 */ - - /* RB_COLOR_INFO Endian=none, Linear, Format=RGBA8888, Swap=0, - * Base=gmem_base - */ - if (ctx) - *cmds++ = - (shadow-> - format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT) | ctx-> - gmem_base; - else { - unsigned int temp = *cmds; - *cmds++ = (temp & ~RB_COLOR_INFO__COLOR_FORMAT_MASK) | - (shadow->format << RB_COLOR_INFO__COLOR_FORMAT__SHIFT); - } - - /* RB_DEPTHCONTROL */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_RB_DEPTHCONTROL); - - if (adreno_is_a220(adreno_dev)) - *cmds++ = 8; /* disable Z */ - else - *cmds++ = 0; /* disable Z */ - - /* Use maximum scissor values -- quad vertices already - * have the correct bounds */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_PA_SC_SCREEN_SCISSOR_TL); - *cmds++ = (0 << 16) | 0; - *cmds++ = ((0x1fff) << 16) | 0x1fff; - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_PA_SC_WINDOW_SCISSOR_TL); - *cmds++ = (unsigned int)((1U << 31) | (0 << 16) | 0); - *cmds++ = ((0x1fff) << 16) | 0x1fff; - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_PA_CL_VTE_CNTL); - /* disable X/Y/Z transforms, X/Y/Z are premultiplied by W */ - *cmds++ = 0x00000b00; - - /*load the viewport so that z scale = clear depth and z offset = 0.0f */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_PA_CL_VPORT_ZSCALE); - *cmds++ = 0xbf800000; - *cmds++ = 0x0; - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_RB_COLOR_MASK); - *cmds++ = 0x0000000f; /* R = G = B = 1:enabled */ - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_RB_COLOR_DEST_MASK); - *cmds++ = 0xffffffff; - - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 3); - *cmds++ = PM4_REG(REG_SQ_WRAPPING_0); - *cmds++ = 0x00000000; - *cmds++ = 0x00000000; - - /* load the stencil ref value - * $AAM - do this later - */ - *cmds++ = pm4_type3_packet(PM4_SET_CONSTANT, 2); - *cmds++ = PM4_REG(REG_RB_MODECONTROL); - /* draw pixels with color and depth/stencil component */ - *cmds++ = 0x4; - - if (adreno_is_a220(adreno_dev)) { - *cmds++ = 0xc0043600; /* packet 3 3D_DRAW_INDX_2 */ - *cmds++ = 0x0; - *cmds++ = 0x00004046; /* tristrip */ - *cmds++ = 0x00000004; /* NUM_INDICES */ - *cmds++ = 0x00010000; /* index: 0x00, 0x01 */ - *cmds++ = 0x00030002; /* index: 0x02, 0x03 */ - } else { - /* queue the draw packet */ - *cmds++ = pm4_type3_packet(PM4_DRAW_INDX, 2); - *cmds++ = 0; /* viz query info. */ - /* PrimType=RectList, NumIndices=3, SrcSel=AutoIndex */ - *cmds++ = 0x00030088; - } - - /* create indirect buffer command for above command sequence */ - create_ib1(drawctxt, shadow->gmem_restore, start, cmds); - - return cmds; -} +/* + * Helper functions + * These are global helper functions used by the GPUs during context switch + */ -/* restore h/w regs, alu constants, texture constants, etc. ... */ -static unsigned *reg_range(unsigned int *cmd, unsigned int start, - unsigned int end) -{ - *cmd++ = PM4_REG(start); /* h/w regs, start addr */ - *cmd++ = end - start + 1; /* count */ - return cmd; -} +/** + * uint2float - convert a uint to IEEE754 single precision float + * @ uintval - value to convert + */ -static void build_regrestore_cmds(struct adreno_device *adreno_dev, - struct adreno_context *drawctxt, - struct tmp_ctx *ctx) +unsigned int uint2float(unsigned int uintval) { - unsigned int *start = ctx->cmd; - unsigned int *cmd = start; - - *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmd++ = 0; - - /* H/W Registers */ - /* deferred pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, ???); */ - cmd++; -#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES - /* Force mismatch */ - *cmd++ = ((drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000) | 1; -#else - *cmd++ = (drawctxt->gpustate.gpuaddr + REG_OFFSET) & 0xFFFFE000; -#endif + unsigned int exp, frac = 0; - if (!adreno_is_a220(adreno_dev)) { - cmd = reg_range(cmd, REG_RB_SURFACE_INFO, - REG_PA_SC_SCREEN_SCISSOR_BR); - } else { - cmd = reg_range(cmd, REG_RB_SURFACE_INFO, REG_RB_DEPTH_INFO); - cmd = reg_range(cmd, REG_COHER_DEST_BASE_0, - REG_PA_SC_SCREEN_SCISSOR_BR); - } - cmd = reg_range(cmd, REG_PA_SC_WINDOW_OFFSET, - REG_PA_SC_WINDOW_SCISSOR_BR); - if (!adreno_is_a220(adreno_dev)) { - cmd = reg_range(cmd, REG_VGT_MAX_VTX_INDX, - REG_PA_CL_VPORT_ZOFFSET); - } else { - cmd = reg_range(cmd, REG_LEIA_PC_MAX_VTX_INDX, - REG_LEIA_PC_INDX_OFFSET); - cmd = reg_range(cmd, REG_RB_COLOR_MASK, REG_RB_FOG_COLOR); - cmd = reg_range(cmd, REG_RB_STENCILREFMASK_BF, - REG_PA_CL_VPORT_ZOFFSET); - } - cmd = reg_range(cmd, REG_SQ_PROGRAM_CNTL, REG_SQ_WRAPPING_1); - if (!adreno_is_a220(adreno_dev)) { - cmd = reg_range(cmd, REG_RB_DEPTHCONTROL, REG_RB_MODECONTROL); - cmd = reg_range(cmd, REG_PA_SU_POINT_SIZE, - REG_PA_SC_VIZ_QUERY); /*REG_VGT_ENHANCE */ - cmd = reg_range(cmd, REG_PA_SC_LINE_CNTL, - REG_RB_COLOR_DEST_MASK); - } else { - cmd = reg_range(cmd, REG_RB_DEPTHCONTROL, REG_RB_COLORCONTROL); - cmd = reg_range(cmd, REG_PA_CL_CLIP_CNTL, REG_PA_CL_VTE_CNTL); - cmd = reg_range(cmd, REG_RB_MODECONTROL, REG_LEIA_GRAS_CONTROL); - cmd = reg_range(cmd, REG_PA_SU_POINT_SIZE, REG_PA_SU_LINE_CNTL); - cmd = reg_range(cmd, REG_PA_SC_LINE_CNTL, REG_SQ_PS_CONST); - cmd = reg_range(cmd, REG_PA_SC_AA_MASK, REG_PA_SC_AA_MASK); - cmd = reg_range(cmd, REG_LEIA_PC_VERTEX_REUSE_BLOCK_CNTL, - REG_LEIA_PC_VERTEX_REUSE_BLOCK_CNTL); - cmd = reg_range(cmd, REG_RB_COPY_CONTROL, REG_RB_DEPTH_CLEAR); - cmd = reg_range(cmd, REG_RB_SAMPLE_COUNT_CTL, - REG_RB_COLOR_DEST_MASK); - } - cmd = reg_range(cmd, REG_PA_SU_POLY_OFFSET_FRONT_SCALE, - REG_PA_SU_POLY_OFFSET_BACK_OFFSET); - - /* Now we know how many register blocks we have, we can compute command - * length - */ - start[2] = - pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, (cmd - start) - 3); - /* Enable shadowing for the entire register block. */ -#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES - start[4] |= (0 << 24) | (4 << 16); /* Disable shadowing. */ -#else - start[4] |= (1 << 24) | (4 << 16); -#endif - - /* Need to handle some of the registers separately */ - *cmd++ = pm4_type0_packet(REG_SQ_GPR_MANAGEMENT, 1); - ctx->reg_values[0] = gpuaddr(cmd, &drawctxt->gpustate); - *cmd++ = 0x00040400; - - *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmd++ = 0; - *cmd++ = pm4_type0_packet(REG_TP0_CHICKEN, 1); - ctx->reg_values[1] = gpuaddr(cmd, &drawctxt->gpustate); - *cmd++ = 0x00000000; - - *cmd++ = pm4_type0_packet(REG_RBBM_PM_OVERRIDE2, 1); - ctx->reg_values[2] = gpuaddr(cmd, &drawctxt->gpustate); - if (!adreno_is_a220(adreno_dev)) - *cmd++ = 0x00000000; - else - *cmd++ = 0x80; - - if (adreno_is_a220(adreno_dev)) { - unsigned int i; - unsigned int j = 3; - for (i = REG_LEIA_VSC_BIN_SIZE; i <= - REG_LEIA_VSC_PIPE_DATA_LENGTH_7; i++) { - *cmd++ = pm4_type0_packet(i, 1); - ctx->reg_values[j] = gpuaddr(cmd, &drawctxt->gpustate); - *cmd++ = 0x00000000; - j++; - } - } - - /* ALU Constants */ - *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); - *cmd++ = drawctxt->gpustate.gpuaddr & 0xFFFFE000; -#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES - *cmd++ = (0 << 24) | (0 << 16) | 0; /* Disable shadowing */ -#else - *cmd++ = (1 << 24) | (0 << 16) | 0; -#endif - *cmd++ = ALU_CONSTANTS; - - /* Texture Constants */ - *cmd++ = pm4_type3_packet(PM4_LOAD_CONSTANT_CONTEXT, 3); - *cmd++ = (drawctxt->gpustate.gpuaddr + TEX_OFFSET) & 0xFFFFE000; -#ifdef CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES - /* Disable shadowing */ - *cmd++ = (0 << 24) | (1 << 16) | 0; -#else - *cmd++ = (1 << 24) | (1 << 16) | 0; -#endif - *cmd++ = TEX_CONSTANTS; - - /* Boolean Constants */ - *cmd++ = pm4_type3_packet(PM4_SET_CONSTANT, 1 + BOOL_CONSTANTS); - *cmd++ = (2 << 16) | 0; - - /* the next BOOL_CONSTANT dwords is the shadow area for - * boolean constants. - */ - ctx->bool_shadow = gpuaddr(cmd, &drawctxt->gpustate); - cmd += BOOL_CONSTANTS; + if (uintval == 0) + return 0; - /* Loop Constants */ - *cmd++ = pm4_type3_packet(PM4_SET_CONSTANT, 1 + LOOP_CONSTANTS); - *cmd++ = (3 << 16) | 0; + exp = ilog2(uintval); - /* the next LOOP_CONSTANTS dwords is the shadow area for - * loop constants. - */ - ctx->loop_shadow = gpuaddr(cmd, &drawctxt->gpustate); - cmd += LOOP_CONSTANTS; + /* Calculate fraction */ + if (23 > exp) + frac = (uintval & (~(1 << exp))) << (23 - exp); - /* create indirect buffer command for above command sequence */ - create_ib1(drawctxt, drawctxt->reg_restore, start, cmd); + /* Exp is biased by 127 and shifted 23 bits */ + exp = (exp + 127) << 23; - ctx->cmd = cmd; + return exp | frac; } -/* quad for saving/restoring gmem */ static void set_gmem_copy_quad(struct gmem_shadow_t *shadow) { /* set vertex buffer values */ @@ -1192,280 +73,62 @@ static void set_gmem_copy_quad(struct gmem_shadow_t *shadow) gmem_copy_quad[4] = uint2float(shadow->height); gmem_copy_quad[9] = uint2float(shadow->width); - gmem_copy_quad[0] = uint2float(0); - gmem_copy_quad[6] = uint2float(0); - gmem_copy_quad[7] = uint2float(0); - gmem_copy_quad[10] = uint2float(0); + gmem_copy_quad[0] = 0; + gmem_copy_quad[6] = 0; + gmem_copy_quad[7] = 0; + gmem_copy_quad[10] = 0; memcpy(shadow->quad_vertices.hostptr, gmem_copy_quad, QUAD_LEN << 2); memcpy(shadow->quad_texcoords.hostptr, gmem_copy_texcoord, - TEXCOORD_LEN << 2); + TEXCOORD_LEN << 2); } +/** + * build_quad_vtxbuff - Create a quad for saving/restoring GMEM + * @ context - Pointer to the context being created + * @ shadow - Pointer to the GMEM shadow structure + * @ incmd - Pointer to pointer to the temporary command buffer + */ + /* quad for saving/restoring gmem */ -static void build_quad_vtxbuff(struct adreno_context *drawctxt, - struct tmp_ctx *ctx, struct gmem_shadow_t *shadow) +void build_quad_vtxbuff(struct adreno_context *drawctxt, + struct gmem_shadow_t *shadow, unsigned int **incmd) { - unsigned int *cmd = ctx->cmd; + unsigned int *cmd = *incmd; /* quad vertex buffer location (in GPU space) */ shadow->quad_vertices.hostptr = cmd; - shadow->quad_vertices.gpuaddr = gpuaddr(cmd, &drawctxt->gpustate); + shadow->quad_vertices.gpuaddr = virt2gpu(cmd, &drawctxt->gpustate); cmd += QUAD_LEN; /* tex coord buffer location (in GPU space) */ shadow->quad_texcoords.hostptr = cmd; - shadow->quad_texcoords.gpuaddr = gpuaddr(cmd, &drawctxt->gpustate); + shadow->quad_texcoords.gpuaddr = virt2gpu(cmd, &drawctxt->gpustate); cmd += TEXCOORD_LEN; set_gmem_copy_quad(shadow); - - ctx->cmd = cmd; + *incmd = cmd; } -static void -build_shader_save_restore_cmds(struct adreno_context *drawctxt, - struct tmp_ctx *ctx) -{ - unsigned int *cmd = ctx->cmd; - unsigned int *save, *restore, *fixup; -#if defined(PM4_IM_STORE) - unsigned int *startSizeVtx, *startSizePix, *startSizeShared; -#endif - unsigned int *partition1; - unsigned int *shaderBases, *partition2; - -#if defined(PM4_IM_STORE) - /* compute vertex, pixel and shared instruction shadow GPU addresses */ - ctx->shader_vertex = drawctxt->gpustate.gpuaddr + SHADER_OFFSET; - ctx->shader_pixel = ctx->shader_vertex + SHADER_SHADOW_SIZE; - ctx->shader_shared = ctx->shader_pixel + SHADER_SHADOW_SIZE; -#endif - - /* restore shader partitioning and instructions */ - - restore = cmd; /* start address */ - - /* Invalidate Vertex & Pixel instruction code address and sizes */ - *cmd++ = pm4_type3_packet(PM4_INVALIDATE_STATE, 1); - *cmd++ = 0x00000300; /* 0x100 = Vertex, 0x200 = Pixel */ - - /* Restore previous shader vertex & pixel instruction bases. */ - *cmd++ = pm4_type3_packet(PM4_SET_SHADER_BASES, 1); - shaderBases = cmd++; /* TBD #5: shader bases (from fixup) */ - - /* write the shader partition information to a scratch register */ - *cmd++ = pm4_type0_packet(REG_SQ_INST_STORE_MANAGMENT, 1); - partition1 = cmd++; /* TBD #4a: partition info (from save) */ - -#if defined(PM4_IM_STORE) - /* load vertex shader instructions from the shadow. */ - *cmd++ = pm4_type3_packet(PM4_IM_LOAD, 2); - *cmd++ = ctx->shader_vertex + 0x0; /* 0x0 = Vertex */ - startSizeVtx = cmd++; /* TBD #1: start/size (from save) */ - - /* load pixel shader instructions from the shadow. */ - *cmd++ = pm4_type3_packet(PM4_IM_LOAD, 2); - *cmd++ = ctx->shader_pixel + 0x1; /* 0x1 = Pixel */ - startSizePix = cmd++; /* TBD #2: start/size (from save) */ - - /* load shared shader instructions from the shadow. */ - *cmd++ = pm4_type3_packet(PM4_IM_LOAD, 2); - *cmd++ = ctx->shader_shared + 0x2; /* 0x2 = Shared */ - startSizeShared = cmd++; /* TBD #3: start/size (from save) */ -#endif - - /* create indirect buffer command for above command sequence */ - create_ib1(drawctxt, drawctxt->shader_restore, restore, cmd); - - /* - * fixup SET_SHADER_BASES data - * - * since self-modifying PM4 code is being used here, a seperate - * command buffer is used for this fixup operation, to ensure the - * commands are not read by the PM4 engine before the data fields - * have been written. - */ - - fixup = cmd; /* start address */ - - /* write the shader partition information to a scratch register */ - *cmd++ = pm4_type0_packet(REG_SCRATCH_REG2, 1); - partition2 = cmd++; /* TBD #4b: partition info (from save) */ - - /* mask off unused bits, then OR with shader instruction memory size */ - *cmd++ = pm4_type3_packet(PM4_REG_RMW, 3); - *cmd++ = REG_SCRATCH_REG2; - /* AND off invalid bits. */ - *cmd++ = 0x0FFF0FFF; - /* OR in instruction memory size */ - *cmd++ = (unsigned int)((SHADER_INSTRUCT_LOG2 - 5U) << 29); - - /* write the computed value to the SET_SHADER_BASES data field */ - *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmd++ = REG_SCRATCH_REG2; - /* TBD #5: shader bases (to restore) */ - *cmd++ = gpuaddr(shaderBases, &drawctxt->gpustate); - - /* create indirect buffer command for above command sequence */ - create_ib1(drawctxt, drawctxt->shader_fixup, fixup, cmd); - - /* save shader partitioning and instructions */ - - save = cmd; /* start address */ - - *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmd++ = 0; - - /* fetch the SQ_INST_STORE_MANAGMENT register value, - * store the value in the data fields of the SET_CONSTANT commands - * above. - */ - *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmd++ = REG_SQ_INST_STORE_MANAGMENT; - /* TBD #4a: partition info (to restore) */ - *cmd++ = gpuaddr(partition1, &drawctxt->gpustate); - *cmd++ = pm4_type3_packet(PM4_REG_TO_MEM, 2); - *cmd++ = REG_SQ_INST_STORE_MANAGMENT; - /* TBD #4b: partition info (to fixup) */ - *cmd++ = gpuaddr(partition2, &drawctxt->gpustate); - -#if defined(PM4_IM_STORE) - - /* store the vertex shader instructions */ - *cmd++ = pm4_type3_packet(PM4_IM_STORE, 2); - *cmd++ = ctx->shader_vertex + 0x0; /* 0x0 = Vertex */ - /* TBD #1: start/size (to restore) */ - *cmd++ = gpuaddr(startSizeVtx, &drawctxt->gpustate); - - /* store the pixel shader instructions */ - *cmd++ = pm4_type3_packet(PM4_IM_STORE, 2); - *cmd++ = ctx->shader_pixel + 0x1; /* 0x1 = Pixel */ - /* TBD #2: start/size (to restore) */ - *cmd++ = gpuaddr(startSizePix, &drawctxt->gpustate); - - /* store the shared shader instructions if vertex base is nonzero */ - - *cmd++ = pm4_type3_packet(PM4_IM_STORE, 2); - *cmd++ = ctx->shader_shared + 0x2; /* 0x2 = Shared */ - /* TBD #3: start/size (to restore) */ - *cmd++ = gpuaddr(startSizeShared, &drawctxt->gpustate); - -#endif - - *cmd++ = pm4_type3_packet(PM4_WAIT_FOR_IDLE, 1); - *cmd++ = 0; - - /* create indirect buffer command for above command sequence */ - create_ib1(drawctxt, drawctxt->shader_save, save, cmd); - - ctx->cmd = cmd; -} - -/* create buffers for saving/restoring registers, constants, & GMEM */ -static int -create_gpustate_shadow(struct kgsl_device *device, - struct adreno_context *drawctxt, - struct tmp_ctx *ctx) -{ - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - int result; - - /* Allocate vmalloc memory to store the gpustate */ - result = kgsl_sharedmem_vmalloc(&drawctxt->gpustate, - drawctxt->pagetable, CONTEXT_SIZE); - - if (result) - return result; - - drawctxt->flags |= CTXT_FLAGS_STATE_SHADOW; - - /* Blank out h/w register, constant, and command buffer shadows. */ - kgsl_sharedmem_set(&drawctxt->gpustate, 0, 0, CONTEXT_SIZE); - - /* set-up command and vertex buffer pointers */ - ctx->cmd = ctx->start - = (unsigned int *)((char *)drawctxt->gpustate.hostptr + CMD_OFFSET); - - /* build indirect command buffers to save & restore regs/constants */ - adreno_idle(device, KGSL_TIMEOUT_DEFAULT); - build_regrestore_cmds(adreno_dev, drawctxt, ctx); - build_regsave_cmds(adreno_dev, drawctxt, ctx); - - build_shader_save_restore_cmds(drawctxt, ctx); - - kgsl_cache_range_op(&drawctxt->gpustate, - KGSL_CACHE_OP_FLUSH); - - return 0; -} - -/* create buffers for saving/restoring registers, constants, & GMEM */ -static int -create_gmem_shadow(struct adreno_device *adreno_dev, - struct adreno_context *drawctxt, - struct tmp_ctx *ctx) -{ - struct kgsl_device *device = &adreno_dev->dev; - int result; - - config_gmemsize(&drawctxt->context_gmem_shadow, - adreno_dev->gmemspace.sizebytes); - ctx->gmem_base = adreno_dev->gmemspace.gpu_base; - - result = kgsl_sharedmem_vmalloc( - &drawctxt->context_gmem_shadow.gmemshadow, - drawctxt->pagetable, - drawctxt->context_gmem_shadow.size); - - if (result) - return result; - - /* we've allocated the shadow, when swapped out, GMEM must be saved. */ - drawctxt->flags |= CTXT_FLAGS_GMEM_SHADOW | CTXT_FLAGS_GMEM_SAVE; - - /* blank out gmem shadow. */ - kgsl_sharedmem_set(&drawctxt->context_gmem_shadow.gmemshadow, 0, 0, - drawctxt->context_gmem_shadow.size); - - /* build quad vertex buffer */ - build_quad_vtxbuff(drawctxt, ctx, &drawctxt->context_gmem_shadow); - - /* build TP0_CHICKEN register restore command buffer */ - ctx->cmd = build_chicken_restore_cmds(drawctxt, ctx); - - /* build indirect command buffers to save & restore gmem */ - /* Idle because we are reading PM override registers */ - adreno_idle(device, KGSL_TIMEOUT_DEFAULT); - drawctxt->context_gmem_shadow.gmem_save_commands = ctx->cmd; - ctx->cmd = - build_gmem2sys_cmds(adreno_dev, drawctxt, ctx, - &drawctxt->context_gmem_shadow); - drawctxt->context_gmem_shadow.gmem_restore_commands = ctx->cmd; - ctx->cmd = - build_sys2gmem_cmds(adreno_dev, drawctxt, ctx, - &drawctxt->context_gmem_shadow); - - kgsl_cache_range_op(&drawctxt->context_gmem_shadow.gmemshadow, - KGSL_CACHE_OP_FLUSH); - - return 0; -} - -/* create a new drawing context */ - -int -adreno_drawctxt_create(struct kgsl_device_private *dev_priv, uint32_t flags, - struct kgsl_context *context) +/** + * adreno_drawctxt_create - create a new adreno draw context + * @device - KGSL device to create the context on + * @pagetable - Pagetable for the context + * @context- Generic KGSL context structure + * @flags - flags for the context (passed from user space) + * + * Create a new draw context for the 3D core. Return 0 on success, + * or error code on failure. + */ +int adreno_drawctxt_create(struct kgsl_device *device, + struct kgsl_pagetable *pagetable, + struct kgsl_context *context, uint32_t flags) { struct adreno_context *drawctxt; - struct kgsl_device *device = dev_priv->device; struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct kgsl_pagetable *pagetable = dev_priv->process_priv->pagetable; - struct tmp_ctx ctx; int ret; drawctxt = kzalloc(sizeof(struct adreno_context), GFP_KERNEL); @@ -1476,25 +139,23 @@ adreno_drawctxt_create(struct kgsl_device_private *dev_priv, uint32_t flags, drawctxt->pagetable = pagetable; drawctxt->bin_base_offset = 0; - ret = create_gpustate_shadow(device, drawctxt, &ctx); + /* FIXME: Deal with preambles */ + + ret = adreno_dev->gpudev->ctxt_gpustate_shadow(adreno_dev, drawctxt); if (ret) goto err; /* Save the shader instruction memory on context switching */ drawctxt->flags |= CTXT_FLAGS_SHADER_SAVE; - memset(&drawctxt->context_gmem_shadow.gmemshadow, - 0, sizeof(struct kgsl_memdesc)); - if (!(flags & KGSL_CONTEXT_NO_GMEM_ALLOC)) { /* create gmem shadow */ - ret = create_gmem_shadow(adreno_dev, drawctxt, &ctx); + ret = adreno_dev->gpudev->ctxt_gmem_shadow(adreno_dev, + drawctxt); if (ret != 0) goto err; } - BUG_ON(ctx.cmd - ctx.start > CMD_BUFFER_LEN); - context->devctxt = drawctxt; return 0; err: @@ -1503,16 +164,25 @@ adreno_drawctxt_create(struct kgsl_device_private *dev_priv, uint32_t flags, return ret; } +/** + * adreno_drawctxt_destroy - destroy a draw context + * @device - KGSL device that owns the context + * @context- Generic KGSL context container for the context + * + * Destroy an existing context. Return 0 on success or error + * code on failure. + */ + /* destroy a drawing context */ -int adreno_drawctxt_destroy(struct kgsl_device *device, +void adreno_drawctxt_destroy(struct kgsl_device *device, struct kgsl_context *context) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); struct adreno_context *drawctxt = context->devctxt; if (drawctxt == NULL) - return -EINVAL; + return; /* deactivate context */ if (adreno_dev->drawctxt_active == drawctxt) { @@ -1534,35 +204,41 @@ int adreno_drawctxt_destroy(struct kgsl_device *device, kfree(drawctxt); context->devctxt = NULL; - - return 0; } -/* set bin base offset */ -int adreno_drawctxt_set_bin_base_offset(struct kgsl_device *device, +/** + * adreno_drawctxt_set_bin_base_offset - set bin base offset for the context + * @device - KGSL device that owns the context + * @context- Generic KGSL context container for the context + * @offset - Offset to set + * + * Set the bin base offset for A2XX devices. Not valid for A3XX devices. + */ + +void adreno_drawctxt_set_bin_base_offset(struct kgsl_device *device, struct kgsl_context *context, unsigned int offset) { struct adreno_context *drawctxt = context->devctxt; - if (drawctxt == NULL) - return -EINVAL; - - drawctxt->bin_base_offset = offset; - - return 0; + if (drawctxt) + drawctxt->bin_base_offset = offset; } -/* switch drawing contexts */ -void -adreno_drawctxt_switch(struct adreno_device *adreno_dev, - struct adreno_context *drawctxt, - unsigned int flags) +/** + * adreno_drawctxt_switch - switch the current draw context + * @adreno_dev - The 3D device that owns the context + * @drawctxt - the 3D context to switch to + * @flags - Flags to accompany the switch (from user space) + * + * Switch the current draw context + */ + +void adreno_drawctxt_switch(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt, + unsigned int flags) { - struct adreno_context *active_ctxt = - adreno_dev->drawctxt_active; struct kgsl_device *device = &adreno_dev->dev; - unsigned int cmds[5]; if (drawctxt) { if (flags & KGSL_CONTEXT_SAVE_GMEM) @@ -1573,109 +249,18 @@ adreno_drawctxt_switch(struct adreno_device *adreno_dev, /* Remove GMEM saving flag from the context */ drawctxt->flags &= ~CTXT_FLAGS_GMEM_SAVE; } + /* already current? */ - if (active_ctxt == drawctxt) + if (adreno_dev->drawctxt_active == drawctxt) return; KGSL_CTXT_INFO(device, "from %p to %p flags %d\n", adreno_dev->drawctxt_active, drawctxt, flags); - /* save old context*/ - if (active_ctxt && active_ctxt->flags & CTXT_FLAGS_GPU_HANG) - KGSL_CTXT_WARN(device, - "Current active context has caused gpu hang\n"); - - if (active_ctxt != NULL) { - KGSL_CTXT_INFO(device, - "active_ctxt flags %08x\n", active_ctxt->flags); - /* save registers and constants. */ - adreno_ringbuffer_issuecmds(device, 0, - active_ctxt->reg_save, 3); - - if (active_ctxt->flags & CTXT_FLAGS_SHADER_SAVE) { - /* save shader partitioning and instructions. */ - adreno_ringbuffer_issuecmds(device, - KGSL_CMD_FLAGS_PMODE, - active_ctxt->shader_save, 3); - - /* fixup shader partitioning parameter for - * SET_SHADER_BASES. - */ - adreno_ringbuffer_issuecmds(device, 0, - active_ctxt->shader_fixup, 3); - active_ctxt->flags |= CTXT_FLAGS_SHADER_RESTORE; - } - - if (active_ctxt->flags & CTXT_FLAGS_GMEM_SAVE - && active_ctxt->flags & CTXT_FLAGS_GMEM_SHADOW) { - /* save gmem. - * (note: changes shader. shader must already be saved.) - */ - adreno_ringbuffer_issuecmds(device, - KGSL_CMD_FLAGS_PMODE, - active_ctxt->context_gmem_shadow.gmem_save, 3); - - /* Restore TP0_CHICKEN */ - adreno_ringbuffer_issuecmds(device, 0, - active_ctxt->chicken_restore, 3); - - active_ctxt->flags |= CTXT_FLAGS_GMEM_RESTORE; - } - } + /* Save the old context */ + adreno_dev->gpudev->ctxt_save(adreno_dev, adreno_dev->drawctxt_active); + /* Set the new context */ adreno_dev->drawctxt_active = drawctxt; - - /* restore new context */ - if (drawctxt != NULL) { - - KGSL_CTXT_INFO(device, - "drawctxt flags %08x\n", drawctxt->flags); - cmds[0] = pm4_nop_packet(1); - cmds[1] = KGSL_CONTEXT_TO_MEM_IDENTIFIER; - cmds[2] = pm4_type3_packet(PM4_MEM_WRITE, 2); - cmds[3] = device->memstore.gpuaddr + - KGSL_DEVICE_MEMSTORE_OFFSET(current_context); - cmds[4] = (unsigned int)adreno_dev->drawctxt_active; - adreno_ringbuffer_issuecmds(device, 0, cmds, 5); - kgsl_mmu_setstate(device, drawctxt->pagetable); - -#ifndef CONFIG_MSM_KGSL_CFF_DUMP_NO_CONTEXT_MEM_DUMP - kgsl_cffdump_syncmem(NULL, &drawctxt->gpustate, - drawctxt->gpustate.gpuaddr, LCC_SHADOW_SIZE + - REG_SHADOW_SIZE + CMD_BUFFER_SIZE + TEX_SHADOW_SIZE, - false); -#endif - - /* restore gmem. - * (note: changes shader. shader must not already be restored.) - */ - if (drawctxt->flags & CTXT_FLAGS_GMEM_RESTORE) { - adreno_ringbuffer_issuecmds(device, - KGSL_CMD_FLAGS_PMODE, - drawctxt->context_gmem_shadow.gmem_restore, 3); - - /* Restore TP0_CHICKEN */ - adreno_ringbuffer_issuecmds(device, 0, - drawctxt->chicken_restore, 3); - - drawctxt->flags &= ~CTXT_FLAGS_GMEM_RESTORE; - } - - /* restore registers and constants. */ - adreno_ringbuffer_issuecmds(device, 0, - drawctxt->reg_restore, 3); - - /* restore shader instructions & partitioning. */ - if (drawctxt->flags & CTXT_FLAGS_SHADER_RESTORE) { - adreno_ringbuffer_issuecmds(device, 0, - drawctxt->shader_restore, 3); - } - - cmds[0] = pm4_type3_packet(PM4_SET_BIN_BASE_OFFSET, 1); - cmds[1] = drawctxt->bin_base_offset; - if (!adreno_is_a220(adreno_dev)) - adreno_ringbuffer_issuecmds(device, 0, cmds, 2); - - } else - kgsl_mmu_setstate(device, device->mmu.defaultpagetable); + adreno_dev->gpudev->ctxt_restore(adreno_dev, drawctxt); } diff --git a/drivers/gpu/msm/adreno_drawctxt.h b/drivers/gpu/msm/adreno_drawctxt.h index 8ea4043623a49..3c3a8536136a7 100644 --- a/drivers/gpu/msm/adreno_drawctxt.h +++ b/drivers/gpu/msm/adreno_drawctxt.h @@ -1,36 +1,20 @@ /* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 __ADRENO_DRAWCTXT_H #define __ADRENO_DRAWCTXT_H -#include "a200_reg.h" -#include "a220_reg.h" +#include "adreno_pm4types.h" +#include "a2xx_reg.h" /* Flags */ @@ -95,19 +79,73 @@ struct adreno_context { struct gmem_shadow_t context_gmem_shadow; }; +int adreno_drawctxt_create(struct kgsl_device *device, + struct kgsl_pagetable *pagetable, + struct kgsl_context *context, + uint32_t flags); -int adreno_drawctxt_create(struct kgsl_device_private *dev_priv, - uint32_t flags, - struct kgsl_context *context); - -int adreno_drawctxt_destroy(struct kgsl_device *device, +void adreno_drawctxt_destroy(struct kgsl_device *device, struct kgsl_context *context); void adreno_drawctxt_switch(struct adreno_device *adreno_dev, struct adreno_context *drawctxt, unsigned int flags); -int adreno_drawctxt_set_bin_base_offset(struct kgsl_device *device, +void adreno_drawctxt_set_bin_base_offset(struct kgsl_device *device, struct kgsl_context *context, unsigned int offset); +/* GPU context switch helper functions */ + +void build_quad_vtxbuff(struct adreno_context *drawctxt, + struct gmem_shadow_t *shadow, unsigned int **incmd); + +unsigned int uint2float(unsigned int); + +static inline unsigned int virt2gpu(unsigned int *cmd, + struct kgsl_memdesc *memdesc) +{ + return memdesc->gpuaddr + ((char *) cmd - (char *) memdesc->hostptr); +} + +static inline void create_ib1(struct adreno_context *drawctxt, + unsigned int *cmd, + unsigned int *start, + unsigned int *end) +{ + cmd[0] = CP_HDR_INDIRECT_BUFFER_PFD; + cmd[1] = virt2gpu(start, &drawctxt->gpustate); + cmd[2] = end - start; +} + + +static inline unsigned int *reg_range(unsigned int *cmd, unsigned int start, + unsigned int end) +{ + *cmd++ = CP_REG(start); /* h/w regs, start addr */ + *cmd++ = end - start + 1; /* count */ + return cmd; +} + +static inline void calc_gmemsize(struct gmem_shadow_t *shadow, int gmem_size) +{ + int w = 64, h = 64; + + shadow->format = COLORX_8_8_8_8; + + /* convert from bytes to 32-bit words */ + gmem_size = (gmem_size + 3) / 4; + + while ((w * h) < gmem_size) { + if (w < h) + w *= 2; + else + h *= 2; + } + + shadow->pitch = shadow->width = w; + shadow->height = h; + shadow->gmem_pitch = shadow->pitch; + shadow->size = shadow->pitch * shadow->height * 4; +} + #endif /* __ADRENO_DRAWCTXT_H */ diff --git a/drivers/gpu/msm/adreno_pm4types.h b/drivers/gpu/msm/adreno_pm4types.h index 246315b66095f..8aea58c958b64 100644 --- a/drivers/gpu/msm/adreno_pm4types.h +++ b/drivers/gpu/msm/adreno_pm4types.h @@ -1,193 +1,193 @@ /* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 __ADRENO_PM4TYPES_H #define __ADRENO_PM4TYPES_H -#define PM4_PKT_MASK 0xc0000000 +#define CP_PKT_MASK 0xc0000000 -#define PM4_TYPE0_PKT ((unsigned int)0 << 30) -#define PM4_TYPE1_PKT ((unsigned int)1 << 30) -#define PM4_TYPE2_PKT ((unsigned int)2 << 30) -#define PM4_TYPE3_PKT ((unsigned int)3 << 30) +#define CP_TYPE0_PKT ((unsigned int)0 << 30) +#define CP_TYPE1_PKT ((unsigned int)1 << 30) +#define CP_TYPE2_PKT ((unsigned int)2 << 30) +#define CP_TYPE3_PKT ((unsigned int)3 << 30) /* type3 packets */ /* initialize CP's micro-engine */ -#define PM4_ME_INIT 0x48 +#define CP_ME_INIT 0x48 /* skip N 32-bit words to get to the next packet */ -#define PM4_NOP 0x10 +#define CP_NOP 0x10 /* indirect buffer dispatch. prefetch parser uses this packet type to determine * whether to pre-fetch the IB */ -#define PM4_INDIRECT_BUFFER 0x3f +#define CP_INDIRECT_BUFFER 0x3f /* indirect buffer dispatch. same as IB, but init is pipelined */ -#define PM4_INDIRECT_BUFFER_PFD 0x37 +#define CP_INDIRECT_BUFFER_PFD 0x37 /* wait for the IDLE state of the engine */ -#define PM4_WAIT_FOR_IDLE 0x26 +#define CP_WAIT_FOR_IDLE 0x26 /* wait until a register or memory location is a specific value */ -#define PM4_WAIT_REG_MEM 0x3c +#define CP_WAIT_REG_MEM 0x3c /* wait until a register location is equal to a specific value */ -#define PM4_WAIT_REG_EQ 0x52 +#define CP_WAIT_REG_EQ 0x52 /* wait until a register location is >= a specific value */ -#define PM4_WAT_REG_GTE 0x53 +#define CP_WAT_REG_GTE 0x53 /* wait until a read completes */ -#define PM4_WAIT_UNTIL_READ 0x5c +#define CP_WAIT_UNTIL_READ 0x5c /* wait until all base/size writes from an IB_PFD packet have completed */ -#define PM4_WAIT_IB_PFD_COMPLETE 0x5d +#define CP_WAIT_IB_PFD_COMPLETE 0x5d /* register read/modify/write */ -#define PM4_REG_RMW 0x21 +#define CP_REG_RMW 0x21 /* reads register in chip and writes to memory */ -#define PM4_REG_TO_MEM 0x3e +#define CP_REG_TO_MEM 0x3e /* write N 32-bit words to memory */ -#define PM4_MEM_WRITE 0x3d +#define CP_MEM_WRITE 0x3d /* write CP_PROG_COUNTER value to memory */ -#define PM4_MEM_WRITE_CNTR 0x4f +#define CP_MEM_WRITE_CNTR 0x4f /* conditional execution of a sequence of packets */ -#define PM4_COND_EXEC 0x44 +#define CP_COND_EXEC 0x44 /* conditional write to memory or register */ -#define PM4_COND_WRITE 0x45 +#define CP_COND_WRITE 0x45 /* generate an event that creates a write to memory when completed */ -#define PM4_EVENT_WRITE 0x46 +#define CP_EVENT_WRITE 0x46 /* generate a VS|PS_done event */ -#define PM4_EVENT_WRITE_SHD 0x58 +#define CP_EVENT_WRITE_SHD 0x58 /* generate a cache flush done event */ -#define PM4_EVENT_WRITE_CFL 0x59 +#define CP_EVENT_WRITE_CFL 0x59 /* generate a z_pass done event */ -#define PM4_EVENT_WRITE_ZPD 0x5b +#define CP_EVENT_WRITE_ZPD 0x5b /* initiate fetch of index buffer and draw */ -#define PM4_DRAW_INDX 0x22 +#define CP_DRAW_INDX 0x22 /* draw using supplied indices in packet */ -#define PM4_DRAW_INDX_2 0x36 +#define CP_DRAW_INDX_2 0x36 /* initiate fetch of index buffer and binIDs and draw */ -#define PM4_DRAW_INDX_BIN 0x34 +#define CP_DRAW_INDX_BIN 0x34 /* initiate fetch of bin IDs and draw using supplied indices */ -#define PM4_DRAW_INDX_2_BIN 0x35 +#define CP_DRAW_INDX_2_BIN 0x35 /* begin/end initiator for viz query extent processing */ -#define PM4_VIZ_QUERY 0x23 +#define CP_VIZ_QUERY 0x23 /* fetch state sub-blocks and initiate shader code DMAs */ -#define PM4_SET_STATE 0x25 +#define CP_SET_STATE 0x25 /* load constant into chip and to memory */ -#define PM4_SET_CONSTANT 0x2d +#define CP_SET_CONSTANT 0x2d /* load sequencer instruction memory (pointer-based) */ -#define PM4_IM_LOAD 0x27 +#define CP_IM_LOAD 0x27 /* load sequencer instruction memory (code embedded in packet) */ -#define PM4_IM_LOAD_IMMEDIATE 0x2b +#define CP_IM_LOAD_IMMEDIATE 0x2b /* load constants from a location in memory */ -#define PM4_LOAD_CONSTANT_CONTEXT 0x2e +#define CP_LOAD_CONSTANT_CONTEXT 0x2e /* selective invalidation of state pointers */ -#define PM4_INVALIDATE_STATE 0x3b +#define CP_INVALIDATE_STATE 0x3b /* dynamically changes shader instruction memory partition */ -#define PM4_SET_SHADER_BASES 0x4A +#define CP_SET_SHADER_BASES 0x4A /* sets the 64-bit BIN_MASK register in the PFP */ -#define PM4_SET_BIN_MASK 0x50 +#define CP_SET_BIN_MASK 0x50 /* sets the 64-bit BIN_SELECT register in the PFP */ -#define PM4_SET_BIN_SELECT 0x51 +#define CP_SET_BIN_SELECT 0x51 /* updates the current context, if needed */ -#define PM4_CONTEXT_UPDATE 0x5e +#define CP_CONTEXT_UPDATE 0x5e /* generate interrupt from the command stream */ -#define PM4_INTERRUPT 0x40 +#define CP_INTERRUPT 0x40 /* copy sequencer instruction memory to system memory */ -#define PM4_IM_STORE 0x2c +#define CP_IM_STORE 0x2c -/* program an offset that will added to the BIN_BASE value of - * the 3D_DRAW_INDX_BIN packet */ -#define PM4_SET_BIN_BASE_OFFSET 0x4B +/* + * for a20x + * program an offset that will added to the BIN_BASE value of + * the 3D_DRAW_INDX_BIN packet + */ +#define CP_SET_BIN_BASE_OFFSET 0x4B + +/* + * for a22x + * sets draw initiator flags register in PFP, gets bitwise-ORed into + * every draw initiator + */ +#define CP_SET_DRAW_INIT_FLAGS 0x4B -#define PM4_SET_PROTECTED_MODE 0x5f /* sets the register protection mode */ +#define CP_SET_PROTECTED_MODE 0x5f /* sets the register protection mode */ /* packet header building macros */ -#define pm4_type0_packet(regindx, cnt) \ - (PM4_TYPE0_PKT | (((cnt)-1) << 16) | ((regindx) & 0x7FFF)) +#define cp_type0_packet(regindx, cnt) \ + (CP_TYPE0_PKT | (((cnt)-1) << 16) | ((regindx) & 0x7FFF)) -#define pm4_type0_packet_for_sameregister(regindx, cnt) \ - ((PM4_TYPE0_PKT | (((cnt)-1) << 16) | ((1 << 15) | \ +#define cp_type0_packet_for_sameregister(regindx, cnt) \ + ((CP_TYPE0_PKT | (((cnt)-1) << 16) | ((1 << 15) | \ ((regindx) & 0x7FFF))) -#define pm4_type1_packet(reg0, reg1) \ - (PM4_TYPE1_PKT | ((reg1) << 12) | (reg0)) +#define cp_type1_packet(reg0, reg1) \ + (CP_TYPE1_PKT | ((reg1) << 12) | (reg0)) -#define pm4_type3_packet(opcode, cnt) \ - (PM4_TYPE3_PKT | (((cnt)-1) << 16) | (((opcode) & 0xFF) << 8)) +#define cp_type3_packet(opcode, cnt) \ + (CP_TYPE3_PKT | (((cnt)-1) << 16) | (((opcode) & 0xFF) << 8)) -#define pm4_predicated_type3_packet(opcode, cnt) \ - (PM4_TYPE3_PKT | (((cnt)-1) << 16) | (((opcode) & 0xFF) << 8) | 0x1) +#define cp_predicated_type3_packet(opcode, cnt) \ + (CP_TYPE3_PKT | (((cnt)-1) << 16) | (((opcode) & 0xFF) << 8) | 0x1) -#define pm4_nop_packet(cnt) \ - (PM4_TYPE3_PKT | (((cnt)-1) << 16) | (PM4_NOP << 8)) +#define cp_nop_packet(cnt) \ + (CP_TYPE3_PKT | (((cnt)-1) << 16) | (CP_NOP << 8)) /* packet headers */ -#define PM4_HDR_ME_INIT pm4_type3_packet(PM4_ME_INIT, 18) -#define PM4_HDR_INDIRECT_BUFFER_PFD pm4_type3_packet(PM4_INDIRECT_BUFFER_PFD, 2) -#define PM4_HDR_INDIRECT_BUFFER pm4_type3_packet(PM4_INDIRECT_BUFFER, 2) +#define CP_HDR_ME_INIT cp_type3_packet(CP_ME_INIT, 18) +#define CP_HDR_INDIRECT_BUFFER_PFD cp_type3_packet(CP_INDIRECT_BUFFER_PFD, 2) +#define CP_HDR_INDIRECT_BUFFER cp_type3_packet(CP_INDIRECT_BUFFER, 2) + +/* dword base address of the GFX decode space */ +#define SUBBLOCK_OFFSET(reg) ((unsigned int)((reg) - (0x2000))) + +/* gmem command buffer length */ +#define CP_REG(reg) ((0x4 << 16) | (SUBBLOCK_OFFSET(reg))) #endif /* __ADRENO_PM4TYPES_H */ diff --git a/drivers/gpu/msm/adreno_postmortem.c b/drivers/gpu/msm/adreno_postmortem.c index 4911e93775901..7c7f0dcc4b57f 100644 --- a/drivers/gpu/msm/adreno_postmortem.c +++ b/drivers/gpu/msm/adreno_postmortem.c @@ -22,7 +22,7 @@ #include "adreno_debugfs.h" #include "kgsl_cffdump.h" -#include "a200_reg.h" +#include "a2xx_reg.h" #define INVALID_RB_CMD 0xaaaaaaaa @@ -43,28 +43,28 @@ static const struct pm_id_name pm0_types[] = { }; static const struct pm_id_name pm3_types[] = { - {PM4_COND_EXEC, "CND_EXEC"}, - {PM4_CONTEXT_UPDATE, "CX__UPDT"}, - {PM4_DRAW_INDX, "DRW_NDX_"}, - {PM4_DRAW_INDX_BIN, "DRW_NDXB"}, - {PM4_EVENT_WRITE, "EVENT_WT"}, - {PM4_IM_LOAD, "IN__LOAD"}, - {PM4_IM_LOAD_IMMEDIATE, "IM_LOADI"}, - {PM4_IM_STORE, "IM_STORE"}, - {PM4_INDIRECT_BUFFER, "IND_BUF_"}, - {PM4_INDIRECT_BUFFER_PFD, "IND_BUFP"}, - {PM4_INTERRUPT, "PM4_INTR"}, - {PM4_INVALIDATE_STATE, "INV_STAT"}, - {PM4_LOAD_CONSTANT_CONTEXT, "LD_CN_CX"}, - {PM4_ME_INIT, "ME__INIT"}, - {PM4_NOP, "PM4__NOP"}, - {PM4_REG_RMW, "REG__RMW"}, - {PM4_REG_TO_MEM, "REG2_MEM"}, - {PM4_SET_BIN_BASE_OFFSET, "ST_BIN_O"}, - {PM4_SET_CONSTANT, "ST_CONST"}, - {PM4_SET_PROTECTED_MODE, "ST_PRT_M"}, - {PM4_SET_SHADER_BASES, "ST_SHD_B"}, - {PM4_WAIT_FOR_IDLE, "WAIT4IDL"}, + {CP_COND_EXEC, "CND_EXEC"}, + {CP_CONTEXT_UPDATE, "CX__UPDT"}, + {CP_DRAW_INDX, "DRW_NDX_"}, + {CP_DRAW_INDX_BIN, "DRW_NDXB"}, + {CP_EVENT_WRITE, "EVENT_WT"}, + {CP_IM_LOAD, "IN__LOAD"}, + {CP_IM_LOAD_IMMEDIATE, "IM_LOADI"}, + {CP_IM_STORE, "IM_STORE"}, + {CP_INDIRECT_BUFFER, "IND_BUF_"}, + {CP_INDIRECT_BUFFER_PFD, "IND_BUFP"}, + {CP_INTERRUPT, "PM4_INTR"}, + {CP_INVALIDATE_STATE, "INV_STAT"}, + {CP_LOAD_CONSTANT_CONTEXT, "LD_CN_CX"}, + {CP_ME_INIT, "ME__INIT"}, + {CP_NOP, "PM4__NOP"}, + {CP_REG_RMW, "REG__RMW"}, + {CP_REG_TO_MEM, "REG2_MEM"}, + {CP_SET_BIN_BASE_OFFSET, "ST_BIN_O"}, + {CP_SET_CONSTANT, "ST_CONST"}, + {CP_SET_PROTECTED_MODE, "ST_PRT_M"}, + {CP_SET_SHADER_BASES, "ST_SHD_B"}, + {CP_WAIT_FOR_IDLE, "WAIT4IDL"}, }; /* Offset address pairs: start, end of range to dump (inclusive) */ @@ -174,14 +174,14 @@ static bool adreno_is_pm4_type(uint32_t word) if (adreno_is_pm4_len(word) > 16) return 0; - if ((word & (3<<30)) == PM4_TYPE0_PKT) { + if ((word & (3<<30)) == CP_TYPE0_PKT) { for (i = 0; i < ARRAY_SIZE(pm0_types); ++i) { if ((word & 0x7FFF) == pm0_types[i].id) return 1; } return 0; } - if ((word & (3<<30)) == PM4_TYPE3_PKT) { + if ((word & (3<<30)) == CP_TYPE3_PKT) { for (i = 0; i < ARRAY_SIZE(pm3_types); ++i) { if ((word & 0xFFFF) == (pm3_types[i].id << 8)) return 1; @@ -198,14 +198,14 @@ static const char *adreno_pm4_name(uint32_t word) if (word == INVALID_RB_CMD) return "--------"; - if ((word & (3<<30)) == PM4_TYPE0_PKT) { + if ((word & (3<<30)) == CP_TYPE0_PKT) { for (i = 0; i < ARRAY_SIZE(pm0_types); ++i) { if ((word & 0x7FFF) == pm0_types[i].id) return pm0_types[i].name; } return "????????"; } - if ((word & (3<<30)) == PM4_TYPE3_PKT) { + if ((word & (3<<30)) == CP_TYPE3_PKT) { for (i = 0; i < ARRAY_SIZE(pm3_types); ++i) { if ((word & 0xFFFF) == (pm3_types[i].id << 8)) return pm3_types[i].name; @@ -289,7 +289,7 @@ static void dump_ib1(struct kgsl_device *device, uint32_t pt_base, for (i = 0; i+3 < ib1_size; ) { value = ib1_addr[i++]; - if (value == pm4_type3_packet(PM4_INDIRECT_BUFFER_PFD, 2)) { + if (value == cp_type3_packet(CP_INDIRECT_BUFFER_PFD, 2)) { uint32_t ib2_base = ib1_addr[i++]; uint32_t ib2_size = ib1_addr[i++]; @@ -456,7 +456,7 @@ static int adreno_dump(struct kgsl_device *device) unsigned int r1, r2, r3, rbbm_status; unsigned int cp_ib1_base, cp_ib1_bufsz, cp_stat; unsigned int cp_ib2_base, cp_ib2_bufsz; - unsigned int pt_base; + unsigned int pt_base, cur_pt_base; unsigned int cp_rb_base, rb_count; unsigned int cp_rb_wptr, cp_rb_rptr; unsigned int i; @@ -628,38 +628,37 @@ static int adreno_dump(struct kgsl_device *device) "COHER: SIZE_PM4 = %08X | BASE_PM4 = %08X | STATUS_PM4" " = %08X\n", r1, r2, r3); - kgsl_regread(device, REG_MH_AXI_ERROR, &r1); + kgsl_regread(device, MH_AXI_ERROR, &r1); KGSL_LOG_DUMP(device, "MH: AXI_ERROR = %08X\n", r1); - kgsl_regread(device, REG_MH_MMU_PAGE_FAULT, &r1); - kgsl_regread(device, REG_MH_MMU_CONFIG, &r2); - kgsl_regread(device, REG_MH_MMU_MPU_BASE, &r3); + kgsl_regread(device, MH_MMU_PAGE_FAULT, &r1); + kgsl_regread(device, MH_MMU_CONFIG, &r2); + kgsl_regread(device, MH_MMU_MPU_BASE, &r3); KGSL_LOG_DUMP(device, "MH_MMU: PAGE_FAULT = %08X | CONFIG = %08X | MPU_BASE =" " %08X\n", r1, r2, r3); - kgsl_regread(device, REG_MH_MMU_MPU_END, &r1); - kgsl_regread(device, REG_MH_MMU_VA_RANGE, &r2); - kgsl_regread(device, REG_MH_MMU_PT_BASE, &pt_base); + kgsl_regread(device, MH_MMU_MPU_END, &r1); + kgsl_regread(device, MH_MMU_VA_RANGE, &r2); + pt_base = kgsl_mmu_get_current_ptbase(device); KGSL_LOG_DUMP(device, " MPU_END = %08X | VA_RANGE = %08X | PT_BASE =" " %08X\n", r1, r2, pt_base); + cur_pt_base = pt_base; KGSL_LOG_DUMP(device, "PAGETABLE SIZE: %08X ", KGSL_PAGETABLE_SIZE); - kgsl_regread(device, REG_MH_MMU_TRAN_ERROR, &r1); + kgsl_regread(device, MH_MMU_TRAN_ERROR, &r1); KGSL_LOG_DUMP(device, " TRAN_ERROR = %08X\n", r1); - kgsl_regread(device, REG_MH_INTERRUPT_MASK, &r1); - kgsl_regread(device, REG_MH_INTERRUPT_STATUS, &r2); + kgsl_regread(device, MH_INTERRUPT_MASK, &r1); + kgsl_regread(device, MH_INTERRUPT_STATUS, &r2); KGSL_LOG_DUMP(device, "MH_INTERRUPT: MASK = %08X | STATUS = %08X\n", r1, r2); - if (device->ftbl.device_readtimestamp != NULL) { - ts_processed = device->ftbl.device_readtimestamp( - device, KGSL_TIMESTAMP_RETIRED); - KGSL_LOG_DUMP(device, "TIMESTM RTRD: %08X\n", ts_processed); - } + ts_processed = device->ftbl->readtimestamp(device, + KGSL_TIMESTAMP_RETIRED); + KGSL_LOG_DUMP(device, "TIMESTM RTRD: %08X\n", ts_processed); num_item = adreno_ringbuffer_count(&adreno_dev->ringbuffer, cp_rb_rptr); @@ -676,8 +675,8 @@ static int adreno_dump(struct kgsl_device *device) KGSL_LOG_DUMP(device, "RB: rd_addr:%8.8x rb_size:%d num_item:%d\n", cp_rb_base, rb_count<<2, num_item); - rb_vaddr = (const uint32_t *)kgsl_sharedmem_convertaddr(device, pt_base, - cp_rb_base, &rb_memsize); + rb_vaddr = (const uint32_t *)kgsl_sharedmem_convertaddr(device, + cur_pt_base, cp_rb_base, &rb_memsize); if (!rb_vaddr) { KGSL_LOG_POSTMORTEM_WRITE(device, "Can't fetch vaddr for CP_RB_BASE\n"); @@ -706,19 +705,37 @@ static int adreno_dump(struct kgsl_device *device) i = 0; for (read_idx = 0; read_idx < num_item; ) { uint32_t this_cmd = rb_copy[read_idx++]; - if (this_cmd == pm4_type3_packet(PM4_INDIRECT_BUFFER_PFD, 2)) { + if (this_cmd == cp_type3_packet(CP_INDIRECT_BUFFER_PFD, 2)) { uint32_t ib_addr = rb_copy[read_idx++]; uint32_t ib_size = rb_copy[read_idx++]; - dump_ib1(device, pt_base, (read_idx-3)<<2, ib_addr, + dump_ib1(device, cur_pt_base, (read_idx-3)<<2, ib_addr, ib_size, &ib_list, 0); for (; i < ib_list.count; ++i) - dump_ib(device, "IB2:", pt_base, + dump_ib(device, "IB2:", cur_pt_base, ib_list.offsets[i], ib_list.bases[i], ib_list.sizes[i], 0); + } else if (this_cmd == cp_type0_packet(MH_MMU_PT_BASE, 1)) { + + KGSL_LOG_DUMP(device, "Current pagetable: %x\t" + "pagetable base: %x\n", + kgsl_mmu_get_ptname_from_ptbase(cur_pt_base), + cur_pt_base); + + /* Set cur_pt_base to the new pagetable base */ + cur_pt_base = rb_copy[read_idx++]; + + KGSL_LOG_DUMP(device, "New pagetable: %x\t" + "pagetable base: %x\n", + kgsl_mmu_get_ptname_from_ptbase(cur_pt_base), + cur_pt_base); } } + /* Restore cur_pt_base back to the pt_base of + the process in whose context the GPU hung */ + cur_pt_base = pt_base; + read_idx = (int)cp_rb_rptr - 64; if (read_idx < 0) read_idx += rb_count; @@ -730,15 +747,15 @@ static int adreno_dump(struct kgsl_device *device) if (adreno_ib_dump_enabled()) { for (read_idx = 64; read_idx >= 0; --read_idx) { uint32_t this_cmd = rb_copy[read_idx]; - if (this_cmd == pm4_type3_packet( - PM4_INDIRECT_BUFFER_PFD, 2)) { + if (this_cmd == cp_type3_packet( + CP_INDIRECT_BUFFER_PFD, 2)) { uint32_t ib_addr = rb_copy[read_idx+1]; uint32_t ib_size = rb_copy[read_idx+2]; if (cp_ib1_bufsz && cp_ib1_base == ib_addr) { KGSL_LOG_DUMP(device, "IB1: base:%8.8X " "count:%d\n", ib_addr, ib_size); - dump_ib(device, "IB1: ", pt_base, + dump_ib(device, "IB1: ", cur_pt_base, read_idx<<2, ib_addr, ib_size, 1); } @@ -751,7 +768,7 @@ static int adreno_dump(struct kgsl_device *device) KGSL_LOG_DUMP(device, "IB2: base:%8.8X count:%d\n", cp_ib2_base, ib_size); - dump_ib(device, "IB2: ", pt_base, ib_offset, + dump_ib(device, "IB2: ", cur_pt_base, ib_offset, ib_list.bases[i], ib_size, 1); } } @@ -802,7 +819,7 @@ int adreno_postmortem_dump(struct kgsl_device *device, int manual) } /* Disable the idle timer so we don't get interrupted */ - del_timer(&device->idle_timer); + del_timer_sync(&device->idle_timer); /* Turn off napping to make sure we have the clocks full attention through the following process */ diff --git a/drivers/gpu/msm/adreno_postmortem.h b/drivers/gpu/msm/adreno_postmortem.h index 1a432489ba8e7..b677800678f98 100644 --- a/drivers/gpu/msm/adreno_postmortem.h +++ b/drivers/gpu/msm/adreno_postmortem.h @@ -1,29 +1,13 @@ /* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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. * */ diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index 4aaa2c6c33570..639f2196d2972 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -16,14 +16,15 @@ #include #include "kgsl.h" +#include "kgsl_sharedmem.h" +#include "kgsl_cffdump.h" #include "adreno.h" #include "adreno_pm4types.h" #include "adreno_ringbuffer.h" -#include "a200_reg.h" +#include "a2xx_reg.h" -#define VALID_STATUS_COUNT_MAX 10 #define GSL_RB_NOP_SIZEDWORDS 2 /* protected mode error checking below register address 0x800 * note: if CP_INTERRUPT packet is used then checking needs @@ -31,17 +32,6 @@ */ #define GSL_RB_PROTECTED_MODE_CONTROL 0x200001F2 -#define GSL_CP_INT_MASK \ - (CP_INT_CNTL__SW_INT_MASK | \ - CP_INT_CNTL__T0_PACKET_IN_IB_MASK | \ - CP_INT_CNTL__OPCODE_ERROR_MASK | \ - CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK | \ - CP_INT_CNTL__RESERVED_BIT_ERROR_MASK | \ - CP_INT_CNTL__IB_ERROR_MASK | \ - CP_INT_CNTL__IB2_INT_MASK | \ - CP_INT_CNTL__IB1_INT_MASK | \ - CP_INT_CNTL__RB_INT_MASK) - /* Firmware file names * Legacy names must remain but replacing macro names to * match current kgsl model. @@ -52,102 +42,17 @@ #define A200_PM4_FW "yamato_pm4.fw" #define A220_PFP_470_FW "leia_pfp_470.fw" #define A220_PM4_470_FW "leia_pm4_470.fw" - -/* functions */ -void kgsl_cp_intrcallback(struct kgsl_device *device) -{ - unsigned int status = 0, num_reads = 0, master_status = 0; - struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; - - adreno_regread_isr(device, REG_MASTER_INT_SIGNAL, &master_status); - while (!status && (num_reads < VALID_STATUS_COUNT_MAX) && - (master_status & MASTER_INT_SIGNAL__CP_INT_STAT)) { - adreno_regread_isr(device, REG_CP_INT_STATUS, &status); - adreno_regread_isr(device, REG_MASTER_INT_SIGNAL, - &master_status); - num_reads++; - } - if (num_reads > 1) - KGSL_DRV_WARN(device, - "Looped %d times to read REG_CP_INT_STATUS\n", - num_reads); - if (!status) { - if (master_status & MASTER_INT_SIGNAL__CP_INT_STAT) { - /* This indicates that we could not read CP_INT_STAT. - * As a precaution just wake up processes so - * they can check their timestamps. Since, we - * did not ack any interrupts this interrupt will - * be generated again */ - KGSL_DRV_WARN(device, "Unable to read CP_INT_STATUS\n"); - wake_up_interruptible_all(&device->wait_queue); - } else - KGSL_DRV_WARN(device, "Spurious interrput detected\n"); - return; - } - - if (status & CP_INT_CNTL__RB_INT_MASK) { - /* signal intr completion event */ - unsigned int enableflag = 0; - kgsl_sharedmem_writel(&rb->device->memstore, - KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable), - enableflag); - wmb(); - KGSL_CMD_WARN(rb->device, "ringbuffer rb interrupt\n"); - } - - if (status & CP_INT_CNTL__T0_PACKET_IN_IB_MASK) { - KGSL_CMD_CRIT(rb->device, - "ringbuffer TO packet in IB interrupt\n"); - adreno_regwrite_isr(rb->device, REG_CP_INT_CNTL, 0); - } - if (status & CP_INT_CNTL__OPCODE_ERROR_MASK) { - KGSL_CMD_CRIT(rb->device, - "ringbuffer opcode error interrupt\n"); - adreno_regwrite_isr(rb->device, REG_CP_INT_CNTL, 0); - } - if (status & CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK) { - KGSL_CMD_CRIT(rb->device, - "ringbuffer protected mode error interrupt\n"); - adreno_regwrite_isr(rb->device, REG_CP_INT_CNTL, 0); - } - if (status & CP_INT_CNTL__RESERVED_BIT_ERROR_MASK) { - KGSL_CMD_CRIT(rb->device, - "ringbuffer reserved bit error interrupt\n"); - adreno_regwrite_isr(rb->device, REG_CP_INT_CNTL, 0); - } - if (status & CP_INT_CNTL__IB_ERROR_MASK) { - KGSL_CMD_CRIT(rb->device, - "ringbuffer IB error interrupt\n"); - adreno_regwrite_isr(rb->device, REG_CP_INT_CNTL, 0); - } - if (status & CP_INT_CNTL__SW_INT_MASK) - KGSL_CMD_INFO(rb->device, "ringbuffer software interrupt\n"); - - if (status & CP_INT_CNTL__IB2_INT_MASK) - KGSL_CMD_INFO(rb->device, "ringbuffer ib2 interrupt\n"); - - if (status & (~GSL_CP_INT_MASK)) - KGSL_CMD_WARN(rb->device, - "bad bits in REG_CP_INT_STATUS %08x\n", status); - - /* only ack bits we understand */ - status &= GSL_CP_INT_MASK; - adreno_regwrite_isr(device, REG_CP_INT_ACK, status); - - if (status & (CP_INT_CNTL__IB1_INT_MASK | CP_INT_CNTL__RB_INT_MASK)) { - KGSL_CMD_WARN(rb->device, "ringbuffer ib1/rb interrupt\n"); - wake_up_interruptible_all(&device->wait_queue); - atomic_notifier_call_chain(&(device->ts_notifier_list), - device->id, - NULL); - } -} +#define A225_PFP_FW "a225_pfp.fw" +#define A225_PM4_FW "a225_pm4.fw" static void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb) { BUG_ON(rb->wptr == 0); + /* Let the pwrscale policy know that new commands have + been submitted. */ + kgsl_pwrscale_busy(rb->device); + /*synchronize memory before informing the hardware of the *new commands. */ @@ -173,7 +78,7 @@ adreno_ringbuffer_waitspace(struct adreno_ringbuffer *rb, unsigned int numcmds, cmds = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr; cmds_gpu = rb->buffer_desc.gpuaddr + sizeof(uint)*rb->wptr; - GSL_RB_WRITE(cmds, cmds_gpu, pm4_nop_packet(nopcount)); + GSL_RB_WRITE(cmds, cmds_gpu, cp_nop_packet(nopcount)); /* Make sure that rptr is not 0 before submitting * commands at the end of ringbuffer. We do not @@ -266,19 +171,15 @@ static int _load_firmware(struct kgsl_device *device, const char *fwfile, static int adreno_ringbuffer_load_pm4_ucode(struct kgsl_device *device) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - const char *fwfile; int i, ret = 0; - if (adreno_is_a220(adreno_dev)) - fwfile = A220_PM4_470_FW; - else - fwfile = A200_PM4_FW; - if (adreno_dev->pm4_fw == NULL) { int len; - unsigned int *ptr; + void *ptr; + + ret = _load_firmware(device, adreno_dev->pm4_fwfile, + &ptr, &len); - ret = _load_firmware(device, fwfile, (void *) &ptr, &len); if (ret) goto err; @@ -309,19 +210,14 @@ static int adreno_ringbuffer_load_pm4_ucode(struct kgsl_device *device) static int adreno_ringbuffer_load_pfp_ucode(struct kgsl_device *device) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - const char *fwfile; int i, ret = 0; - if (adreno_is_a220(adreno_dev)) - fwfile = A220_PFP_470_FW; - else - fwfile = A200_PFP_FW; - if (adreno_dev->pfp_fw == NULL) { int len; - unsigned int *ptr; + void *ptr; - ret = _load_firmware(device, fwfile, (void *) &ptr, &len); + ret = _load_firmware(device, adreno_dev->pfp_fwfile, + &ptr, &len); if (ret) goto err; @@ -441,7 +337,7 @@ int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, unsigned int init_ram) cmds = adreno_ringbuffer_allocspace(rb, 19); cmds_gpu = rb->buffer_desc.gpuaddr + sizeof(uint)*(rb->wptr-19); - GSL_RB_WRITE(cmds, cmds_gpu, PM4_HDR_ME_INIT); + GSL_RB_WRITE(cmds, cmds_gpu, CP_HDR_ME_INIT); /* All fields present (bits 9:0) */ GSL_RB_WRITE(cmds, cmds_gpu, 0x000003ff); /* Disable/Enable Real-Time Stream processing (present but ignored) */ @@ -450,21 +346,21 @@ int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, unsigned int init_ram) GSL_RB_WRITE(cmds, cmds_gpu, 0x00000000); GSL_RB_WRITE(cmds, cmds_gpu, - GSL_HAL_SUBBLOCK_OFFSET(REG_RB_SURFACE_INFO)); + SUBBLOCK_OFFSET(REG_RB_SURFACE_INFO)); GSL_RB_WRITE(cmds, cmds_gpu, - GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SC_WINDOW_OFFSET)); + SUBBLOCK_OFFSET(REG_PA_SC_WINDOW_OFFSET)); GSL_RB_WRITE(cmds, cmds_gpu, - GSL_HAL_SUBBLOCK_OFFSET(REG_VGT_MAX_VTX_INDX)); + SUBBLOCK_OFFSET(REG_VGT_MAX_VTX_INDX)); GSL_RB_WRITE(cmds, cmds_gpu, - GSL_HAL_SUBBLOCK_OFFSET(REG_SQ_PROGRAM_CNTL)); + SUBBLOCK_OFFSET(REG_SQ_PROGRAM_CNTL)); GSL_RB_WRITE(cmds, cmds_gpu, - GSL_HAL_SUBBLOCK_OFFSET(REG_RB_DEPTHCONTROL)); + SUBBLOCK_OFFSET(REG_RB_DEPTHCONTROL)); GSL_RB_WRITE(cmds, cmds_gpu, - GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SU_POINT_SIZE)); + SUBBLOCK_OFFSET(REG_PA_SU_POINT_SIZE)); GSL_RB_WRITE(cmds, cmds_gpu, - GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SC_LINE_CNTL)); + SUBBLOCK_OFFSET(REG_PA_SC_LINE_CNTL)); GSL_RB_WRITE(cmds, cmds_gpu, - GSL_HAL_SUBBLOCK_OFFSET(REG_PA_SU_POLY_OFFSET_FRONT_SCALE)); + SUBBLOCK_OFFSET(REG_PA_SU_POLY_OFFSET_FRONT_SCALE)); /* Vertex and Pixel Shader Start Addresses in instructions * (3 DWORDS per instruction) */ @@ -489,7 +385,6 @@ int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, unsigned int init_ram) /* idle device to validate ME INIT */ status = adreno_idle(device, KGSL_TIMEOUT_DEFAULT); - adreno_regwrite(rb->device, REG_CP_INT_CNTL, GSL_CP_INT_MASK); if (status == 0) rb->flags |= KGSL_FLAGS_STARTED; @@ -499,8 +394,6 @@ int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, unsigned int init_ram) int adreno_ringbuffer_stop(struct adreno_ringbuffer *rb) { if (rb->flags & KGSL_FLAGS_STARTED) { - adreno_regwrite(rb->device, REG_CP_INT_CNTL, 0); - /* ME_HALT */ adreno_regwrite(rb->device, REG_CP_ME_CNTL, 0x10000000); @@ -525,7 +418,8 @@ int adreno_ringbuffer_init(struct kgsl_device *device) rb->sizedwords = KGSL_RB_SIZE >> 2; /* allocate memory for ringbuffer */ - status = kgsl_allocate_contig(&rb->buffer_desc, (rb->sizedwords << 2)); + status = kgsl_allocate_contiguous(&rb->buffer_desc, + (rb->sizedwords << 2)); if (status != 0) { adreno_ringbuffer_close(rb); @@ -535,7 +429,7 @@ int adreno_ringbuffer_init(struct kgsl_device *device) /* allocate memory for polling and timestamps */ /* This really can be at 4 byte alignment boundry but for using MMU * we need to make it at page boundary */ - status = kgsl_allocate_contig(&rb->memptrs_desc, + status = kgsl_allocate_contiguous(&rb->memptrs_desc, sizeof(struct kgsl_rbmemptrs)); if (status != 0) { @@ -590,13 +484,13 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, + sizeof(uint)*(rb->wptr-total_sizedwords); if (!(flags & KGSL_CMD_FLAGS_NOT_KERNEL_CMD)) { - GSL_RB_WRITE(ringcmds, rcmd_gpu, pm4_nop_packet(1)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_nop_packet(1)); GSL_RB_WRITE(ringcmds, rcmd_gpu, KGSL_CMD_IDENTIFIER); } if (flags & KGSL_CMD_FLAGS_PMODE) { /* disable protected mode error checking */ GSL_RB_WRITE(ringcmds, rcmd_gpu, - pm4_type3_packet(PM4_SET_PROTECTED_MODE, 1)); + cp_type3_packet(CP_SET_PROTECTED_MODE, 1)); GSL_RB_WRITE(ringcmds, rcmd_gpu, 0); } @@ -608,7 +502,7 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, if (flags & KGSL_CMD_FLAGS_PMODE) { /* re-enable protected mode error checking */ GSL_RB_WRITE(ringcmds, rcmd_gpu, - pm4_type3_packet(PM4_SET_PROTECTED_MODE, 1)); + cp_type3_packet(CP_SET_PROTECTED_MODE, 1)); GSL_RB_WRITE(ringcmds, rcmd_gpu, 1); } @@ -616,9 +510,9 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, timestamp = rb->timestamp; /* start-of-pipeline and end-of-pipeline timestamps */ - GSL_RB_WRITE(ringcmds, rcmd_gpu, pm4_type0_packet(REG_CP_TIMESTAMP, 1)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_type0_packet(REG_CP_TIMESTAMP, 1)); GSL_RB_WRITE(ringcmds, rcmd_gpu, rb->timestamp); - GSL_RB_WRITE(ringcmds, rcmd_gpu, pm4_type3_packet(PM4_EVENT_WRITE, 3)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_type3_packet(CP_EVENT_WRITE, 3)); GSL_RB_WRITE(ringcmds, rcmd_gpu, CACHE_FLUSH_TS); GSL_RB_WRITE(ringcmds, rcmd_gpu, (rb->device->memstore.gpuaddr + @@ -628,7 +522,7 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, if (!(flags & KGSL_CMD_FLAGS_NO_TS_CMP)) { /* Conditional execution based on memory values */ GSL_RB_WRITE(ringcmds, rcmd_gpu, - pm4_type3_packet(PM4_COND_EXEC, 4)); + cp_type3_packet(CP_COND_EXEC, 4)); GSL_RB_WRITE(ringcmds, rcmd_gpu, (rb->device->memstore.gpuaddr + KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable)) >> 2); GSL_RB_WRITE(ringcmds, rcmd_gpu, (rb->device->memstore.gpuaddr + @@ -637,7 +531,7 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, /* # of conditional command DWORDs */ GSL_RB_WRITE(ringcmds, rcmd_gpu, 2); GSL_RB_WRITE(ringcmds, rcmd_gpu, - pm4_type3_packet(PM4_INTERRUPT, 1)); + cp_type3_packet(CP_INTERRUPT, 1)); GSL_RB_WRITE(ringcmds, rcmd_gpu, CP_INT_CNTL__RB_INT_MASK); } @@ -701,13 +595,13 @@ adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv, (void)kgsl_cffdump_parse_ibs(dev_priv, NULL, ibdesc[i].gpuaddr, ibdesc[i].sizedwords, false); - *cmds++ = PM4_HDR_INDIRECT_BUFFER_PFD; + *cmds++ = CP_HDR_INDIRECT_BUFFER_PFD; *cmds++ = ibdesc[i].gpuaddr; *cmds++ = ibdesc[i].sizedwords; } kgsl_setstate(device, - kgsl_pt_get_flags(device->mmu.hwpagetable, + kgsl_mmu_pt_get_flags(device->mmu.hwpagetable, device->id)); adreno_drawctxt_switch(adreno_dev, drawctxt, flags); @@ -751,13 +645,8 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, GSL_RB_GET_READPTR(rb, &rb->rptr); -/* drewis: still not sure where this struct was changed */ -#if 0 retired_timestamp = device->ftbl->readtimestamp(device, KGSL_TIMESTAMP_RETIRED); -#endif - retired_timestamp = device->ftbl.device_readtimestamp( - device, KGSL_TIMESTAMP_RETIRED); KGSL_DRV_ERR(device, "GPU successfully executed till ts: %x\n", retired_timestamp); /* @@ -786,9 +675,9 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, kgsl_sharedmem_readl(&rb->buffer_desc, &val3, rb_rptr); /* match the pattern found at the end of a command */ if ((val1 == 2 && - val2 == pm4_type3_packet(PM4_INTERRUPT, 1) + val2 == cp_type3_packet(CP_INTERRUPT, 1) && val3 == CP_INT_CNTL__RB_INT_MASK) || - (val1 == pm4_type3_packet(PM4_EVENT_WRITE, 3) + (val1 == cp_type3_packet(CP_EVENT_WRITE, 3) && val2 == CACHE_FLUSH_TS && val3 == (rb->device->memstore.gpuaddr + KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp)))) { @@ -830,7 +719,7 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, kgsl_sharedmem_readl(&rb->buffer_desc, &val2, adreno_ringbuffer_inc_wrapped(rb_rptr, rb->buffer_desc.size)); - if (val1 == pm4_nop_packet(1) && val2 == KGSL_CMD_IDENTIFIER) { + if (val1 == cp_nop_packet(1) && val2 == KGSL_CMD_IDENTIFIER) { KGSL_DRV_ERR(device, "GPU recovery from hang not possible because " "of hang in kgsl command\n"); @@ -850,7 +739,7 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, rb->buffer_desc.size); - BUG_ON(value != pm4_type3_packet(PM4_MEM_WRITE, 2)); + BUG_ON(value != cp_type3_packet(CP_MEM_WRITE, 2)); kgsl_sharedmem_readl(&rb->buffer_desc, &val1, rb_rptr); rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, rb->buffer_desc.size); @@ -873,14 +762,14 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, * commands can be executed */ if (value != cur_context) { copy_rb_contents = 1; - temp_rb_buffer[temp_idx++] = pm4_nop_packet(1); + temp_rb_buffer[temp_idx++] = cp_nop_packet(1); temp_rb_buffer[temp_idx++] = KGSL_CMD_IDENTIFIER; - temp_rb_buffer[temp_idx++] = pm4_nop_packet(1); + temp_rb_buffer[temp_idx++] = cp_nop_packet(1); temp_rb_buffer[temp_idx++] = KGSL_CONTEXT_TO_MEM_IDENTIFIER; temp_rb_buffer[temp_idx++] = - pm4_type3_packet(PM4_MEM_WRITE, 2); + cp_type3_packet(CP_MEM_WRITE, 2); temp_rb_buffer[temp_idx++] = val1; temp_rb_buffer[temp_idx++] = value; } else { diff --git a/drivers/gpu/msm/adreno_ringbuffer.h b/drivers/gpu/msm/adreno_ringbuffer.h index 9162dea2cae33..487a74858ee64 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.h +++ b/drivers/gpu/msm/adreno_ringbuffer.h @@ -1,29 +1,13 @@ /* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 __ADRENO_RINGBUFFER_H @@ -77,8 +61,6 @@ struct adreno_ringbuffer { uint32_t timestamp; }; -/* dword base address of the GFX decode space */ -#define GSL_HAL_SUBBLOCK_OFFSET(reg) ((unsigned int)((reg) - (0x2000))) #define GSL_RB_WRITE(ring, gpuaddr, data) \ do { \ diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index d8749fa87f52b..5d20d9b1414a1 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -27,14 +27,21 @@ #include "kgsl.h" #include "kgsl_debugfs.h" #include "kgsl_cffdump.h" +#include "kgsl_log.h" +#include "kgsl_sharedmem.h" +#include "kgsl_device.h" #undef MODULE_PARAM_PREFIX #define MODULE_PARAM_PREFIX "kgsl." static int kgsl_pagetable_count = KGSL_PAGETABLE_COUNT; +static char *ksgl_mmu_type; module_param_named(ptcount, kgsl_pagetable_count, int, 0); MODULE_PARM_DESC(kgsl_pagetable_count, "Minimum number of pagetables for KGSL to allocate at initialization time"); +module_param_named(mmutype, ksgl_mmu_type, charp, 0); +MODULE_PARM_DESC(ksgl_mmu_type, +"Type of MMU to be used for graphics. Valid values are 'iommu' or 'gpummu' or 'nommu'"); static inline struct kgsl_mem_entry * kgsl_mem_entry_create(void) @@ -181,9 +188,8 @@ static void kgsl_memqueue_drain(struct kgsl_device *device) BUG_ON(!mutex_is_locked(&device->mutex)); /* get current EOP timestamp */ - ts_processed = device->ftbl.device_readtimestamp( - device, - KGSL_TIMESTAMP_RETIRED); + ts_processed = device->ftbl->readtimestamp(device, + KGSL_TIMESTAMP_RETIRED); list_for_each_entry_safe(entry, entry_tmp, &device->memqueue, list) { KGSL_MEM_INFO(device, @@ -280,43 +286,19 @@ EXPORT_SYMBOL(kgsl_unregister_ts_notifier); int kgsl_check_timestamp(struct kgsl_device *device, unsigned int timestamp) { unsigned int ts_processed; - BUG_ON(device->ftbl.device_readtimestamp == NULL); - ts_processed = device->ftbl.device_readtimestamp( - device, KGSL_TIMESTAMP_RETIRED); + ts_processed = device->ftbl->readtimestamp(device, + KGSL_TIMESTAMP_RETIRED); return timestamp_cmp(ts_processed, timestamp); } EXPORT_SYMBOL(kgsl_check_timestamp); -int kgsl_setstate(struct kgsl_device *device, uint32_t flags) -{ - int status = -ENXIO; - - if (flags && device->ftbl.device_setstate) { - status = device->ftbl.device_setstate(device, flags); - } else - status = 0; - - return status; -} -EXPORT_SYMBOL(kgsl_setstate); - -int kgsl_idle(struct kgsl_device *device, unsigned int timeout) -{ - int status = -ENXIO; - - if (device->ftbl.device_idle) - status = device->ftbl.device_idle(device, timeout); - - return status; -} -EXPORT_SYMBOL(kgsl_idle); - static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state) { int status = -EINVAL; unsigned int nap_allowed_saved; + struct kgsl_pwrscale_policy *policy_saved; if (!device) return -EINVAL; @@ -326,6 +308,8 @@ static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state) mutex_lock(&device->mutex); nap_allowed_saved = device->pwrctrl.nap_allowed; device->pwrctrl.nap_allowed = false; + policy_saved = device->pwrscale.policy; + device->pwrscale.policy = NULL; device->requested_state = KGSL_STATE_SUSPEND; /* Make sure no user process is waiting for a timestamp * * before supending */ @@ -335,19 +319,19 @@ static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state) mutex_lock(&device->mutex); } /* Don't let the timer wake us during suspended sleep. */ - del_timer(&device->idle_timer); + del_timer_sync(&device->idle_timer); switch (device->state) { case KGSL_STATE_INIT: break; case KGSL_STATE_ACTIVE: /* Wait for the device to become idle */ - device->ftbl.device_idle(device, KGSL_TIMEOUT_DEFAULT); + device->ftbl->idle(device, KGSL_TIMEOUT_DEFAULT); case KGSL_STATE_NAP: case KGSL_STATE_SLEEP: /* Get the completion ready to be waited upon. */ INIT_COMPLETION(device->hwaccess_gate); - device->ftbl.device_suspend_context(device); - device->ftbl.device_stop(device); + device->ftbl->suspend_context(device); + device->ftbl->stop(device); device->state = KGSL_STATE_SUSPEND; KGSL_PWR_WARN(device, "state -> SUSPEND, device %d\n", device->id); @@ -359,6 +343,7 @@ static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state) } device->requested_state = KGSL_STATE_NONE; device->pwrctrl.nap_allowed = nap_allowed_saved; + device->pwrscale.policy = policy_saved; status = 0; end: @@ -378,7 +363,8 @@ static int kgsl_resume_device(struct kgsl_device *device) mutex_lock(&device->mutex); if (device->state == KGSL_STATE_SUSPEND) { device->requested_state = KGSL_STATE_ACTIVE; - status = device->ftbl.device_start(device, 0); + kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_NOMINAL); + status = device->ftbl->start(device, 0); if (status == 0) { device->state = KGSL_STATE_ACTIVE; KGSL_PWR_WARN(device, @@ -391,13 +377,13 @@ static int kgsl_resume_device(struct kgsl_device *device) device->state = KGSL_STATE_INIT; goto end; } - status = device->ftbl.device_resume_context(device); complete_all(&device->hwaccess_gate); } device->requested_state = KGSL_STATE_NONE; end: mutex_unlock(&device->mutex); + kgsl_check_idle(device); KGSL_PWR_WARN(device, "resume end\n"); return status; } @@ -434,6 +420,16 @@ const struct dev_pm_ops kgsl_pm_ops = { }; EXPORT_SYMBOL(kgsl_pm_ops); +void kgsl_early_suspend_driver(struct early_suspend *h) +{ + struct kgsl_device *device = container_of(h, + struct kgsl_device, display_off); + mutex_lock(&device->mutex); + kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_NOMINAL); + mutex_unlock(&device->mutex); +} +EXPORT_SYMBOL(kgsl_early_suspend_driver); + int kgsl_suspend_driver(struct platform_device *pdev, pm_message_t state) { @@ -449,6 +445,16 @@ int kgsl_resume_driver(struct platform_device *pdev) } EXPORT_SYMBOL(kgsl_resume_driver); +void kgsl_late_resume_driver(struct early_suspend *h) +{ + struct kgsl_device *device = container_of(h, + struct kgsl_device, display_off); + mutex_lock(&device->mutex); + kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO); + mutex_unlock(&device->mutex); +} +EXPORT_SYMBOL(kgsl_late_resume_driver); + /* file operations */ static struct kgsl_process_private * kgsl_get_process_private(struct kgsl_device_private *cur_dev_priv) @@ -477,15 +483,11 @@ kgsl_get_process_private(struct kgsl_device_private *cur_dev_priv) INIT_LIST_HEAD(&private->mem_list); -#ifdef CONFIG_MSM_KGSL_MMU + if (kgsl_mmu_enabled()) { unsigned long pt_name; -#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE pt_name = task_tgid_nr(current); -#else - pt_name = KGSL_MMU_GLOBAL_PT; -#endif private->pagetable = kgsl_mmu_getpagetable(pt_name); if (private->pagetable == NULL) { kfree(private); @@ -493,7 +495,6 @@ kgsl_get_process_private(struct kgsl_device_private *cur_dev_priv) goto out; } } -#endif list_add(&private->list, &kgsl_driver.process_list); @@ -559,7 +560,7 @@ static int kgsl_release(struct inode *inodep, struct file *filep) break; if (context->dev_priv == dev_priv) { - device->ftbl.device_drawctxt_destroy(device, context); + device->ftbl->drawctxt_destroy(device, context); kgsl_destroy_context(dev_priv, context); } @@ -568,7 +569,7 @@ static int kgsl_release(struct inode *inodep, struct file *filep) device->open_count--; if (device->open_count == 0) { - result = device->ftbl.device_stop(device); + result = device->ftbl->stop(device); device->state = KGSL_STATE_INIT; KGSL_PWR_WARN(device, "state -> INIT, device %d\n", device->id); } @@ -632,7 +633,7 @@ static int kgsl_open(struct inode *inodep, struct file *filep) kgsl_check_suspended(device); if (device->open_count == 0) { - result = device->ftbl.device_start(device, true); + result = device->ftbl->start(device, true); if (result) { mutex_unlock(&device->mutex); @@ -647,7 +648,7 @@ static int kgsl_open(struct inode *inodep, struct file *filep) KGSL_DRV_INFO(device, "Initialized %s: mmu=%s pagetable_count=%d\n", device->name, kgsl_mmu_enabled() ? "on" : "off", - KGSL_PAGETABLE_COUNT); + kgsl_pagetable_count); return result; @@ -745,7 +746,7 @@ static long kgsl_ioctl_device_getproperty(struct kgsl_device_private *dev_priv, break; } default: - result = dev_priv->device->ftbl.device_getproperty( + result = dev_priv->device->ftbl->getproperty( dev_priv->device, param->type, param->value, param->sizebytes); } @@ -770,7 +771,7 @@ static long kgsl_ioctl_device_waittimestamp(struct kgsl_device_private if (param->timeout == -1) param->timeout = 10 * MSEC_PER_SEC; - result = dev_priv->device->ftbl.device_waittimestamp(dev_priv->device, + result = dev_priv->device->ftbl->waittimestamp(dev_priv->device, param->timestamp, param->timeout); @@ -893,15 +894,7 @@ static long kgsl_ioctl_rb_issueibcmds(struct kgsl_device_private *dev_priv, goto free_ibdesc; } - /* Let the pwrscale policy know that a new command buffer - is being issued */ - - kgsl_pwrscale_busy(dev_priv->device); - -/* drewis: don't know what changed this...diff from cherry-pick - f3c1074d1539be20cecbb82f37705bd16058418e */ -/* result = dev_priv->device->ftbl->issueibcmds(dev_priv,*/ - result = dev_priv->device->ftbl.device_issueibcmds(dev_priv, + result = dev_priv->device->ftbl->issueibcmds(dev_priv, context, ibdesc, param->numibs, @@ -938,8 +931,8 @@ static long kgsl_ioctl_cmdstream_readtimestamp(struct kgsl_device_private struct kgsl_cmdstream_readtimestamp *param = data; param->timestamp = - dev_priv->device->ftbl.device_readtimestamp( - dev_priv->device, param->type); + dev_priv->device->ftbl->readtimestamp(dev_priv->device, + param->type); return 0; } @@ -985,10 +978,10 @@ static long kgsl_ioctl_drawctxt_create(struct kgsl_device_private *dev_priv, goto done; } - if (dev_priv->device->ftbl.device_drawctxt_create != NULL) - result = dev_priv->device->ftbl.device_drawctxt_create(dev_priv, - param->flags, - context); + if (dev_priv->device->ftbl->drawctxt_create) + result = dev_priv->device->ftbl->drawctxt_create( + dev_priv->device, dev_priv->process_priv->pagetable, + context, param->flags); param->drawctxt_id = context->id; @@ -1013,9 +1006,9 @@ static long kgsl_ioctl_drawctxt_destroy(struct kgsl_device_private *dev_priv, goto done; } - result = dev_priv->device->ftbl.device_drawctxt_destroy( - dev_priv->device, - context); + if (dev_priv->device->ftbl->drawctxt_destroy) + dev_priv->device->ftbl->drawctxt_destroy(dev_priv->device, + context); kgsl_destroy_context(dev_priv, context); @@ -1250,7 +1243,7 @@ static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, entry->memdesc.size = size; entry->memdesc.physaddr = phys + (offset & PAGE_MASK); entry->memdesc.hostptr = (void *) (virt + (offset & PAGE_MASK)); - entry->memdesc.ops = &kgsl_contig_ops; + entry->memdesc.ops = &kgsl_contiguous_ops; return 0; err: @@ -1675,7 +1668,13 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) func = kgsl_ioctl_funcs[nr].func; lock = kgsl_ioctl_funcs[nr].lock; } else { - func = dev_priv->device->ftbl.device_ioctl; + func = dev_priv->device->ftbl->ioctl; + if (!func) { + KGSL_DRV_INFO(dev_priv->device, + "invalid ioctl code %08x\n", cmd); + ret = -EINVAL; + goto done; + } lock = 1; } @@ -1772,7 +1771,7 @@ static int kgsl_mmap(struct file *file, struct vm_area_struct *vma) unsigned long vma_offset = vma->vm_pgoff << PAGE_SHIFT; struct kgsl_device_private *dev_priv = file->private_data; struct kgsl_process_private *private = dev_priv->process_priv; - struct kgsl_mem_entry *entry; + struct kgsl_mem_entry *tmp, *entry = NULL; struct kgsl_device *device = dev_priv->device; /* Handle leagacy behavior for memstore */ @@ -1783,9 +1782,10 @@ static int kgsl_mmap(struct file *file, struct vm_area_struct *vma) /* Find a chunk of GPU memory */ spin_lock(&private->mem_lock); - list_for_each_entry(entry, &private->mem_list, list) { - if (vma_offset == entry->memdesc.gpuaddr) { - kgsl_mem_entry_get(entry); + list_for_each_entry(tmp, &private->mem_list, list) { + if (vma_offset == tmp->memdesc.gpuaddr) { + kgsl_mem_entry_get(tmp); + entry = tmp; break; } } @@ -1817,7 +1817,7 @@ static const struct file_operations kgsl_fops = { struct kgsl_driver kgsl_driver = { .process_mutex = __MUTEX_INITIALIZER(kgsl_driver.process_mutex), - .pt_mutex = __MUTEX_INITIALIZER(kgsl_driver.pt_mutex), + .ptlock = __SPIN_LOCK_UNLOCKED(kgsl_driver.ptlock), .devlock = __MUTEX_INITIALIZER(kgsl_driver.devlock), }; EXPORT_SYMBOL(kgsl_driver); @@ -1841,6 +1841,8 @@ void kgsl_unregister_device(struct kgsl_device *device) kgsl_pwrctrl_uninit_sysfs(device); wake_lock_destroy(&device->idle_wakelock); + pm_qos_remove_request(&device->pm_qos_req_dma); + idr_destroy(&device->context_idr); if (device->memstore.hostptr) @@ -1859,8 +1861,6 @@ void kgsl_unregister_device(struct kgsl_device *device) mutex_lock(&kgsl_driver.devlock); kgsl_driver.devp[minor] = NULL; mutex_unlock(&kgsl_driver.devlock); - - atomic_dec(&kgsl_driver.device_count); } EXPORT_SYMBOL(kgsl_unregister_device); @@ -1903,8 +1903,6 @@ kgsl_register_device(struct kgsl_device *device) dev_set_drvdata(device->parentdev, device); /* Generic device initialization */ - atomic_inc(&kgsl_driver.device_count); - init_waitqueue_head(&device->wait_queue); kgsl_cffdump_open(device->id); @@ -1927,15 +1925,16 @@ kgsl_register_device(struct kgsl_device *device) if (ret != 0) goto err_dest_work_q; - ret = kgsl_allocate_contig(&device->memstore, + ret = kgsl_allocate_contiguous(&device->memstore, sizeof(struct kgsl_devmemstore)); if (ret != 0) goto err_close_mmu; - kgsl_sharedmem_set(&device->memstore, 0, 0, device->memstore.size); - wake_lock_init(&device->idle_wakelock, WAKE_LOCK_IDLE, device->name); + pm_qos_add_request(&device->pm_qos_req_dma, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + idr_init(&device->context_idr); /* sysfs and debugfs initalization - failure here is non fatal */ @@ -2065,17 +2064,19 @@ EXPORT_SYMBOL(kgsl_device_platform_remove); static int __devinit kgsl_ptdata_init(void) { - INIT_LIST_HEAD(&kgsl_driver.pagetable_list); - - return kgsl_ptpool_init(&kgsl_driver.ptpool, KGSL_PAGETABLE_SIZE, - kgsl_pagetable_count); + kgsl_driver.ptpool = kgsl_mmu_ptpool_init(KGSL_PAGETABLE_SIZE, + kgsl_pagetable_count); + if (!kgsl_driver.ptpool) + return -ENOMEM; + return 0; } static void kgsl_core_exit(void) { unregister_chrdev_region(kgsl_driver.major, KGSL_DEVICE_MAX); - kgsl_ptpool_destroy(&kgsl_driver.ptpool); + kgsl_mmu_ptpool_destroy(&kgsl_driver.ptpool); + kgsl_driver.ptpool = NULL; device_unregister(&kgsl_driver.virtdev); @@ -2093,7 +2094,6 @@ static void kgsl_core_exit(void) static int __init kgsl_core_init(void) { int result = 0; - /* alloc major and minor device numbers */ result = alloc_chrdev_region(&kgsl_driver.major, 0, KGSL_DEVICE_MAX, KGSL_NAME); @@ -2147,20 +2147,25 @@ static int __init kgsl_core_init(void) kgsl_sharedmem_init_sysfs(); kgsl_cffdump_init(); - /* Generic device initialization */ - atomic_set(&kgsl_driver.device_count, -1); - INIT_LIST_HEAD(&kgsl_driver.process_list); - result = kgsl_ptdata_init(); - if (result) - goto err; + INIT_LIST_HEAD(&kgsl_driver.pagetable_list); + + kgsl_mmu_set_mmutype(ksgl_mmu_type); + + if (KGSL_MMU_TYPE_GPU == kgsl_mmu_get_mmutype()) { + result = kgsl_ptdata_init(); + if (result) + goto err; + } result = kgsl_drm_init(NULL); if (result) goto err; + kgsl_mmu_set_mmutype(ksgl_mmu_type); + return 0; err: diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 6696c7a7be3a3..8db2cb4b0f7bb 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -1,29 +1,13 @@ /* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 __KGSL_H @@ -38,26 +22,8 @@ #include #include -#include - -#include "kgsl_device.h" -#include "kgsl_pwrctrl.h" -#include "kgsl_sharedmem.h" -#include "kgsl_log.h" -#include "kgsl_cffdump.h" - #define KGSL_NAME "kgsl" -#define CHIP_REV_251 0x020501 - -/* Flags to control whether to flush or invalidate a cached memory range */ -#define KGSL_CACHE_INV 0x00000000 -#define KGSL_CACHE_CLEAN 0x00000001 -#define KGSL_CACHE_FLUSH 0x00000002 - -#define KGSL_CACHE_USER_ADDR 0x00000010 -#define KGSL_CACHE_VMALLOC_ADDR 0x00000020 - /*cache coherency ops */ #define DRM_KGSL_GEM_CACHE_OP_TO_DEV 0x0001 #define DRM_KGSL_GEM_CACHE_OP_FROM_DEV 0x0002 @@ -74,13 +40,9 @@ #define KGSL_PAGETABLE_ENTRIES(_sz) (((_sz) >> PAGE_SHIFT) + \ KGSL_PT_EXTRA_ENTRIES) -#ifdef CONFIG_MSM_KGSL_MMU #define KGSL_PAGETABLE_SIZE \ ALIGN(KGSL_PAGETABLE_ENTRIES(CONFIG_MSM_KGSL_PAGE_TABLE_SIZE) * \ KGSL_PAGETABLE_ENTRY_SIZE, PAGE_SIZE) -#else -#define KGSL_PAGETABLE_SIZE 0 -#endif #ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE #define KGSL_PAGETABLE_COUNT (CONFIG_MSM_KGSL_PAGE_TABLE_COUNT) @@ -99,6 +61,8 @@ KGSL_PAGETABLE_ENTRY_SIZE, PAGE_SIZE) #define KGSL_STATS_ADD(_size, _stat, _max) \ do { _stat += (_size); if (_stat > _max) _max = _stat; } while (0) +struct kgsl_device; + struct kgsl_driver { struct cdev cdev; dev_t major; @@ -108,24 +72,21 @@ struct kgsl_driver { /* Kobjects for storing pagetable and process statistics */ struct kobject *ptkobj; struct kobject *prockobj; - atomic_t device_count; struct kgsl_device *devp[KGSL_DEVICE_MAX]; - uint32_t flags_debug; - /* Global lilst of open processes */ struct list_head process_list; /* Global list of pagetables */ struct list_head pagetable_list; - /* Mutex for accessing the pagetable list */ - struct mutex pt_mutex; + /* Spinlock for accessing the pagetable list */ + spinlock_t ptlock; /* Mutex for accessing the process list */ struct mutex process_mutex; /* Mutex for protecting the device list */ struct mutex devlock; - struct kgsl_ptpool ptpool; + void *ptpool; struct { unsigned int vmalloc; @@ -143,6 +104,20 @@ extern struct kgsl_driver kgsl_driver; #define KGSL_USER_MEMORY 1 #define KGSL_MAPPED_MEMORY 2 +struct kgsl_pagetable; +struct kgsl_memdesc_ops; + +/* shared memory allocation */ +struct kgsl_memdesc { + struct kgsl_pagetable *pagetable; + void *hostptr; + unsigned int gpuaddr; + unsigned int physaddr; + unsigned int size; + unsigned int priv; + struct kgsl_memdesc_ops *ops; +}; + struct kgsl_mem_entry { struct kref refcount; struct kgsl_memdesc memdesc; @@ -167,53 +142,14 @@ uint8_t *kgsl_gpuaddr_to_vaddr(const struct kgsl_memdesc *memdesc, struct kgsl_mem_entry *kgsl_sharedmem_find_region( struct kgsl_process_private *private, unsigned int gpuaddr, size_t size); -int kgsl_idle(struct kgsl_device *device, unsigned int timeout); -int kgsl_setstate(struct kgsl_device *device, uint32_t flags); - -static inline void kgsl_regread(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int *value) -{ - device->ftbl.device_regread(device, offsetwords, value); -} - -static inline void kgsl_regwrite(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int value) -{ - device->ftbl.device_regwrite(device, offsetwords, value); -} - -static inline void kgsl_regread_isr(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int *value) -{ - device->ftbl.device_regread_isr(device, offsetwords, value); -} - -static inline void kgsl_regwrite_isr(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int value) -{ - device->ftbl.device_regwrite_isr(device, offsetwords, value); -} - -int kgsl_check_timestamp(struct kgsl_device *device, unsigned int timestamp); - -int kgsl_register_ts_notifier(struct kgsl_device *device, - struct notifier_block *nb); - -int kgsl_unregister_ts_notifier(struct kgsl_device *device, - struct notifier_block *nb); - -int kgsl_device_platform_probe(struct kgsl_device *device, - irqreturn_t (*dev_isr) (int, void*)); -void kgsl_device_platform_remove(struct kgsl_device *device); extern const struct dev_pm_ops kgsl_pm_ops; +struct early_suspend; int kgsl_suspend_driver(struct platform_device *pdev, pm_message_t state); int kgsl_resume_driver(struct platform_device *pdev); +void kgsl_early_suspend_driver(struct early_suspend *h); +void kgsl_late_resume_driver(struct early_suspend *h); #ifdef CONFIG_MSM_KGSL_DRM extern int kgsl_drm_init(struct platform_device *dev); @@ -240,18 +176,6 @@ static inline int kgsl_gpuaddr_in_memdesc(const struct kgsl_memdesc *memdesc, return 0; } -static inline struct kgsl_device *kgsl_device_from_dev(struct device *dev) -{ - int i; - - for (i = 0; i < KGSL_DEVICE_MAX; i++) { - if (kgsl_driver.devp[i] && kgsl_driver.devp[i]->dev == dev) - return kgsl_driver.devp[i]; - } - - return NULL; -} - static inline bool timestamp_cmp(unsigned int new, unsigned int old) { int ts_diff = new - old; @@ -270,21 +194,4 @@ kgsl_mem_entry_put(struct kgsl_mem_entry *entry) kref_put(&entry->refcount, kgsl_mem_entry_destroy); } -static inline int kgsl_create_device_sysfs_files(struct device *root, - const struct device_attribute **list) -{ - int ret = 0, i; - for (i = 0; list[i] != NULL; i++) - ret |= device_create_file(root, list[i]); - return ret; -} - -static inline void kgsl_remove_device_sysfs_files(struct device *root, - const struct device_attribute **list) -{ - int i; - for (i = 0; list[i] != NULL; i++) - device_remove_file(root, list[i]); -} - #endif /* __KGSL_H */ diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c index 702e1ac7428ad..d4195927cceaa 100644 --- a/drivers/gpu/msm/kgsl_cffdump.c +++ b/drivers/gpu/msm/kgsl_cffdump.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "kgsl.h" #include "kgsl_cffdump.h" @@ -362,8 +363,10 @@ void kgsl_cffdump_open(enum kgsl_deviceid device_id) /*TODO: move this to where we can report correct gmemsize*/ unsigned int va_base; - /* XXX: drewis edit: only for 8x50 */ - va_base = 0x20000000; + if (cpu_is_msm8x60() || cpu_is_msm8960()) + va_base = 0x40000000; + else + va_base = 0x20000000; kgsl_cffdump_memory_base(device_id, va_base, CONFIG_MSM_KGSL_PAGE_TABLE_SIZE, SZ_256K); @@ -523,8 +526,8 @@ static bool kgsl_cffdump_handle_type3(struct kgsl_device_private *dev_priv, static uint size_stack[ADDRESS_STACK_SIZE]; switch (GET_PM4_TYPE3_OPCODE(hostaddr)) { - case PM4_INDIRECT_BUFFER_PFD: - case PM4_INDIRECT_BUFFER: + case CP_INDIRECT_BUFFER_PFD: + case CP_INDIRECT_BUFFER: { /* traverse indirect buffers */ int i; @@ -607,7 +610,6 @@ bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv, if (!memdesc->physaddr) { KGSL_CORE_ERR("no physaddr"); - return true; } else { mb(); kgsl_cache_range_op((struct kgsl_memdesc *)memdesc, diff --git a/drivers/gpu/msm/kgsl_cffdump.h b/drivers/gpu/msm/kgsl_cffdump.h index d2f9d172811e6..140e4868e0ce1 100644 --- a/drivers/gpu/msm/kgsl_cffdump.h +++ b/drivers/gpu/msm/kgsl_cffdump.h @@ -1,29 +1,13 @@ /* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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. * */ diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c index d98586a9a9598..f5eeb3fb9d962 100644 --- a/drivers/gpu/msm/kgsl_debugfs.c +++ b/drivers/gpu/msm/kgsl_debugfs.c @@ -14,6 +14,7 @@ #include #include "kgsl.h" +#include "kgsl_device.h" /*default log levels is error for everything*/ #define KGSL_LOG_LEVEL_DEFAULT 3 diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index bb0f2b35ab3d6..10e345aeeeaec 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -1,29 +1,13 @@ /* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 __KGSL_DEVICE_H @@ -31,7 +15,10 @@ #include #include +#include +#include +#include "kgsl.h" #include "kgsl_mmu.h" #include "kgsl_pwrctrl.h" #include "kgsl_log.h" @@ -71,55 +58,48 @@ struct kgsl_context; struct kgsl_power_stats; struct kgsl_functable { - void (*device_regread) (struct kgsl_device *device, - unsigned int offsetwords, - unsigned int *value); - void (*device_regwrite) (struct kgsl_device *device, - unsigned int offsetwords, - unsigned int value); - void (*device_regread_isr) (struct kgsl_device *device, - unsigned int offsetwords, - unsigned int *value); - void (*device_regwrite_isr) (struct kgsl_device *device, - unsigned int offsetwords, - unsigned int value); - int (*device_setstate) (struct kgsl_device *device, uint32_t flags); - int (*device_idle) (struct kgsl_device *device, unsigned int timeout); - unsigned int (*device_isidle) (struct kgsl_device *device); - int (*device_suspend_context) (struct kgsl_device *device); - int (*device_resume_context) (struct kgsl_device *device); - int (*device_start) (struct kgsl_device *device, unsigned int init_ram); - int (*device_stop) (struct kgsl_device *device); - int (*device_getproperty) (struct kgsl_device *device, - enum kgsl_property_type type, - void *value, - unsigned int sizebytes); - int (*device_waittimestamp) (struct kgsl_device *device, - unsigned int timestamp, - unsigned int msecs); - unsigned int (*device_readtimestamp) ( - struct kgsl_device *device, - enum kgsl_timestamp_type type); - int (*device_issueibcmds) (struct kgsl_device_private *dev_priv, - struct kgsl_context *context, - struct kgsl_ibdesc *ibdesc, - unsigned int sizedwords, - uint32_t *timestamp, - unsigned int flags); - int (*device_drawctxt_create) (struct kgsl_device_private *dev_priv, - uint32_t flags, - struct kgsl_context *context); - int (*device_drawctxt_destroy) (struct kgsl_device *device, - struct kgsl_context *context); - long (*device_ioctl) (struct kgsl_device_private *dev_priv, - unsigned int cmd, void *data); - int (*device_setup_pt)(struct kgsl_device *device, - struct kgsl_pagetable *pagetable); - - int (*device_cleanup_pt)(struct kgsl_device *device, - struct kgsl_pagetable *pagetable); - void (*device_power_stats)(struct kgsl_device *device, + /* Mandatory functions - these functions must be implemented + by the client device. The driver will not check for a NULL + pointer before calling the hook. + */ + void (*regread) (struct kgsl_device *device, + unsigned int offsetwords, unsigned int *value); + void (*regwrite) (struct kgsl_device *device, + unsigned int offsetwords, unsigned int value); + int (*idle) (struct kgsl_device *device, unsigned int timeout); + unsigned int (*isidle) (struct kgsl_device *device); + int (*suspend_context) (struct kgsl_device *device); + int (*start) (struct kgsl_device *device, unsigned int init_ram); + int (*stop) (struct kgsl_device *device); + int (*getproperty) (struct kgsl_device *device, + enum kgsl_property_type type, void *value, + unsigned int sizebytes); + int (*waittimestamp) (struct kgsl_device *device, + unsigned int timestamp, unsigned int msecs); + unsigned int (*readtimestamp) (struct kgsl_device *device, + enum kgsl_timestamp_type type); + int (*issueibcmds) (struct kgsl_device_private *dev_priv, + struct kgsl_context *context, struct kgsl_ibdesc *ibdesc, + unsigned int sizedwords, uint32_t *timestamp, + unsigned int flags); + int (*setup_pt)(struct kgsl_device *device, + struct kgsl_pagetable *pagetable); + void (*cleanup_pt)(struct kgsl_device *device, + struct kgsl_pagetable *pagetable); + void (*power_stats)(struct kgsl_device *device, struct kgsl_power_stats *stats); + void (*irqctrl)(struct kgsl_device *device, int state); + /* Optional functions - these functions are not mandatory. The + driver will check that the function pointer is not NULL before + calling the hook */ + void (*setstate) (struct kgsl_device *device, uint32_t flags); + int (*drawctxt_create) (struct kgsl_device *device, + struct kgsl_pagetable *pagetable, struct kgsl_context *context, + uint32_t flags); + void (*drawctxt_destroy) (struct kgsl_device *device, + struct kgsl_context *context); + long (*ioctl) (struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data); }; struct kgsl_memregion { @@ -129,6 +109,15 @@ struct kgsl_memregion { unsigned int sizebytes; }; +/* MH register values */ +struct kgsl_mh { + unsigned int mharb; + unsigned int mh_intf_cfg1; + unsigned int mh_intf_cfg2; + uint32_t mpu_base; + int mpu_range; +}; + struct kgsl_device { struct device *dev; const char *name; @@ -140,9 +129,10 @@ struct kgsl_device { struct kgsl_memdesc memstore; const char *iomemname; + struct kgsl_mh mh; struct kgsl_mmu mmu; struct completion hwaccess_gate; - struct kgsl_functable ftbl; + const struct kgsl_functable *ftbl; struct work_struct idle_check_ws; struct timer_list idle_timer; struct kgsl_pwrctrl pwrctrl; @@ -163,6 +153,7 @@ struct kgsl_device { struct completion recovery_gate; struct dentry *d_debugfs; struct idr context_idr; + struct early_suspend display_off; /* Logging levels */ int cmd_log; @@ -173,6 +164,7 @@ struct kgsl_device { struct wake_lock idle_wakelock; struct kgsl_pwrscale pwrscale; struct kobject pwrscale_kobj; + struct pm_qos_request_list pm_qos_req_dma; }; struct kgsl_context { @@ -215,12 +207,60 @@ struct kgsl_power_stats { struct kgsl_device *kgsl_get_device(int dev_idx); +static inline void kgsl_regread(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int *value) +{ + device->ftbl->regread(device, offsetwords, value); +} + +static inline void kgsl_regwrite(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int value) +{ + device->ftbl->regwrite(device, offsetwords, value); +} + +static inline int kgsl_idle(struct kgsl_device *device, unsigned int timeout) +{ + return device->ftbl->idle(device, timeout); +} + +static inline int kgsl_create_device_sysfs_files(struct device *root, + const struct device_attribute **list) +{ + int ret = 0, i; + for (i = 0; list[i] != NULL; i++) + ret |= device_create_file(root, list[i]); + return ret; +} + +static inline void kgsl_remove_device_sysfs_files(struct device *root, + const struct device_attribute **list) +{ + int i; + for (i = 0; list[i] != NULL; i++) + device_remove_file(root, list[i]); +} + static inline struct kgsl_mmu * kgsl_get_mmu(struct kgsl_device *device) { return (struct kgsl_mmu *) (device ? &device->mmu : NULL); } +static inline struct kgsl_device *kgsl_device_from_dev(struct device *dev) +{ + int i; + + for (i = 0; i < KGSL_DEVICE_MAX; i++) { + if (kgsl_driver.devp[i] && kgsl_driver.devp[i]->dev == dev) + return kgsl_driver.devp[i]; + } + + return NULL; +} + static inline int kgsl_create_device_workqueue(struct kgsl_device *device) { device->work_queue = create_workqueue(device->name); @@ -244,4 +284,16 @@ kgsl_find_context(struct kgsl_device_private *dev_priv, uint32_t id) return (ctxt && ctxt->dev_priv == dev_priv) ? ctxt : NULL; } +int kgsl_check_timestamp(struct kgsl_device *device, unsigned int timestamp); + +int kgsl_register_ts_notifier(struct kgsl_device *device, + struct notifier_block *nb); + +int kgsl_unregister_ts_notifier(struct kgsl_device *device, + struct notifier_block *nb); + +int kgsl_device_platform_probe(struct kgsl_device *device, + irqreturn_t (*dev_isr) (int, void*)); +void kgsl_device_platform_remove(struct kgsl_device *device); + #endif /* __KGSL_DEVICE_H */ diff --git a/drivers/gpu/msm/kgsl_drm.c b/drivers/gpu/msm/kgsl_drm.c index 1e878e158d0a0..202783b3dc73f 100644 --- a/drivers/gpu/msm/kgsl_drm.c +++ b/drivers/gpu/msm/kgsl_drm.c @@ -293,7 +293,7 @@ kgsl_gem_alloc_memory(struct drm_gem_object *obj) } priv->memdesc.size = obj->size * priv->bufcount; - priv->memdesc.ops = &kgsl_contig_ops; + priv->memdesc.ops = &kgsl_contiguous_ops; } else if (TYPE_IS_MEM(priv->type)) { priv->memdesc.hostptr = diff --git a/drivers/gpu/msm/kgsl_gpummu.c b/drivers/gpu/msm/kgsl_gpummu.c new file mode 100644 index 0000000000000..383b910605942 --- /dev/null +++ b/drivers/gpu/msm/kgsl_gpummu.c @@ -0,0 +1,787 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include + +#include "kgsl.h" +#include "kgsl_mmu.h" +#include "kgsl_device.h" +#include "kgsl_sharedmem.h" + +static ssize_t +sysfs_show_ptpool_entries(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct kgsl_ptpool *pool = (struct kgsl_ptpool *) + kgsl_driver.ptpool; + return snprintf(buf, PAGE_SIZE, "%d\n", pool->entries); +} + +static ssize_t +sysfs_show_ptpool_min(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct kgsl_ptpool *pool = (struct kgsl_ptpool *) + kgsl_driver.ptpool; + return snprintf(buf, PAGE_SIZE, "%d\n", + pool->static_entries); +} + +static ssize_t +sysfs_show_ptpool_chunks(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct kgsl_ptpool *pool = (struct kgsl_ptpool *) + kgsl_driver.ptpool; + return snprintf(buf, PAGE_SIZE, "%d\n", pool->chunks); +} + +static ssize_t +sysfs_show_ptpool_ptsize(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct kgsl_ptpool *pool = (struct kgsl_ptpool *) + kgsl_driver.ptpool; + return snprintf(buf, PAGE_SIZE, "%d\n", pool->ptsize); +} + +static struct kobj_attribute attr_ptpool_entries = { + .attr = { .name = "ptpool_entries", .mode = 0444 }, + .show = sysfs_show_ptpool_entries, + .store = NULL, +}; + +static struct kobj_attribute attr_ptpool_min = { + .attr = { .name = "ptpool_min", .mode = 0444 }, + .show = sysfs_show_ptpool_min, + .store = NULL, +}; + +static struct kobj_attribute attr_ptpool_chunks = { + .attr = { .name = "ptpool_chunks", .mode = 0444 }, + .show = sysfs_show_ptpool_chunks, + .store = NULL, +}; + +static struct kobj_attribute attr_ptpool_ptsize = { + .attr = { .name = "ptpool_ptsize", .mode = 0444 }, + .show = sysfs_show_ptpool_ptsize, + .store = NULL, +}; + +static struct attribute *ptpool_attrs[] = { + &attr_ptpool_entries.attr, + &attr_ptpool_min.attr, + &attr_ptpool_chunks.attr, + &attr_ptpool_ptsize.attr, + NULL, +}; + +static struct attribute_group ptpool_attr_group = { + .attrs = ptpool_attrs, +}; + +static int +_kgsl_ptpool_add_entries(struct kgsl_ptpool *pool, int count, int dynamic) +{ + struct kgsl_ptpool_chunk *chunk; + size_t size = ALIGN(count * pool->ptsize, PAGE_SIZE); + + BUG_ON(count == 0); + + if (get_order(size) >= MAX_ORDER) { + KGSL_CORE_ERR("ptpool allocation is too big: %d\n", size); + return -EINVAL; + } + + chunk = kzalloc(sizeof(*chunk), GFP_KERNEL); + if (chunk == NULL) { + KGSL_CORE_ERR("kzalloc(%d) failed\n", sizeof(*chunk)); + return -ENOMEM; + } + + chunk->size = size; + chunk->count = count; + chunk->dynamic = dynamic; + + chunk->data = dma_alloc_coherent(NULL, size, + &chunk->phys, GFP_KERNEL); + + if (chunk->data == NULL) { + KGSL_CORE_ERR("dma_alloc_coherent(%d) failed\n", size); + goto err; + } + + chunk->bitmap = kzalloc(BITS_TO_LONGS(count) * 4, GFP_KERNEL); + + if (chunk->bitmap == NULL) { + KGSL_CORE_ERR("kzalloc(%d) failed\n", + BITS_TO_LONGS(count) * 4); + goto err_dma; + } + + list_add_tail(&chunk->list, &pool->list); + + pool->chunks++; + pool->entries += count; + + if (!dynamic) + pool->static_entries += count; + + return 0; + +err_dma: + dma_free_coherent(NULL, chunk->size, chunk->data, chunk->phys); +err: + kfree(chunk); + return -ENOMEM; +} + +static void * +_kgsl_ptpool_get_entry(struct kgsl_ptpool *pool, unsigned int *physaddr) +{ + struct kgsl_ptpool_chunk *chunk; + + list_for_each_entry(chunk, &pool->list, list) { + int bit = find_first_zero_bit(chunk->bitmap, chunk->count); + + if (bit >= chunk->count) + continue; + + set_bit(bit, chunk->bitmap); + *physaddr = chunk->phys + (bit * pool->ptsize); + + return chunk->data + (bit * pool->ptsize); + } + + return NULL; +} + +/** + * kgsl_ptpool_add + * @pool: A pointer to a ptpool structure + * @entries: Number of entries to add + * + * Add static entries to the pagetable pool. + */ + +static int +kgsl_ptpool_add(struct kgsl_ptpool *pool, int count) +{ + int ret = 0; + BUG_ON(count == 0); + + mutex_lock(&pool->lock); + + /* Only 4MB can be allocated in one chunk, so larger allocations + need to be split into multiple sections */ + + while (count) { + int entries = ((count * pool->ptsize) > SZ_4M) ? + SZ_4M / pool->ptsize : count; + + /* Add the entries as static, i.e. they don't ever stand + a chance of being removed */ + + ret = _kgsl_ptpool_add_entries(pool, entries, 0); + if (ret) + break; + + count -= entries; + } + + mutex_unlock(&pool->lock); + return ret; +} + +/** + * kgsl_ptpool_alloc + * @pool: A pointer to a ptpool structure + * @addr: A pointer to store the physical address of the chunk + * + * Allocate a pagetable from the pool. Returns the virtual address + * of the pagetable, the physical address is returned in physaddr + */ + +static void *kgsl_ptpool_alloc(struct kgsl_ptpool *pool, + unsigned int *physaddr) +{ + void *addr = NULL; + int ret; + + mutex_lock(&pool->lock); + addr = _kgsl_ptpool_get_entry(pool, physaddr); + if (addr) + goto done; + + /* Add a chunk for 1 more pagetable and mark it as dynamic */ + ret = _kgsl_ptpool_add_entries(pool, 1, 1); + + if (ret) + goto done; + + addr = _kgsl_ptpool_get_entry(pool, physaddr); +done: + mutex_unlock(&pool->lock); + return addr; +} + +static inline void _kgsl_ptpool_rm_chunk(struct kgsl_ptpool_chunk *chunk) +{ + list_del(&chunk->list); + + if (chunk->data) + dma_free_coherent(NULL, chunk->size, chunk->data, + chunk->phys); + kfree(chunk->bitmap); + kfree(chunk); +} + +/** + * kgsl_ptpool_free + * @pool: A pointer to a ptpool structure + * @addr: A pointer to the virtual address to free + * + * Free a pagetable allocated from the pool + */ + +static void kgsl_ptpool_free(struct kgsl_ptpool *pool, void *addr) +{ + struct kgsl_ptpool_chunk *chunk, *tmp; + + if (pool == NULL || addr == NULL) + return; + + mutex_lock(&pool->lock); + list_for_each_entry_safe(chunk, tmp, &pool->list, list) { + if (addr >= chunk->data && + addr < chunk->data + chunk->size) { + int bit = ((unsigned long) (addr - chunk->data)) / + pool->ptsize; + + clear_bit(bit, chunk->bitmap); + memset(addr, 0, pool->ptsize); + + if (chunk->dynamic && + bitmap_empty(chunk->bitmap, chunk->count)) + _kgsl_ptpool_rm_chunk(chunk); + + break; + } + } + + mutex_unlock(&pool->lock); +} + +void kgsl_gpummu_ptpool_destroy(void *ptpool) +{ + struct kgsl_ptpool *pool = (struct kgsl_ptpool *)ptpool; + struct kgsl_ptpool_chunk *chunk, *tmp; + + if (pool == NULL) + return; + + mutex_lock(&pool->lock); + list_for_each_entry_safe(chunk, tmp, &pool->list, list) + _kgsl_ptpool_rm_chunk(chunk); + mutex_unlock(&pool->lock); + + kfree(pool); +} + +/** + * kgsl_ptpool_init + * @pool: A pointer to a ptpool structure to initialize + * @ptsize: The size of each pagetable entry + * @entries: The number of inital entries to add to the pool + * + * Initalize a pool and allocate an initial chunk of entries. + */ +void *kgsl_gpummu_ptpool_init(int ptsize, int entries) +{ + struct kgsl_ptpool *pool; + int ret = 0; + BUG_ON(ptsize == 0); + + pool = kzalloc(sizeof(struct kgsl_ptpool), GFP_KERNEL); + if (!pool) { + KGSL_CORE_ERR("Failed to allocate memory " + "for ptpool\n"); + return NULL; + } + + pool->ptsize = ptsize; + mutex_init(&pool->lock); + INIT_LIST_HEAD(&pool->list); + + if (entries) { + ret = kgsl_ptpool_add(pool, entries); + if (ret) + goto err_ptpool_remove; + } + + ret = sysfs_create_group(kgsl_driver.ptkobj, &ptpool_attr_group); + if (ret) { + KGSL_CORE_ERR("sysfs_create_group failed for ptpool " + "statistics: %d\n", ret); + goto err_ptpool_remove; + } + return (void *)pool; + +err_ptpool_remove: + kgsl_gpummu_ptpool_destroy(pool); + return NULL; +} + +int kgsl_gpummu_pt_equal(struct kgsl_pagetable *pt, + unsigned int pt_base) +{ + struct kgsl_gpummu_pt *gpummu_pt = pt->priv; + return pt && pt_base && (gpummu_pt->base.gpuaddr == pt_base); +} + +void kgsl_gpummu_destroy_pagetable(void *mmu_specific_pt) +{ + struct kgsl_gpummu_pt *gpummu_pt = (struct kgsl_gpummu_pt *) + mmu_specific_pt; + kgsl_ptpool_free((struct kgsl_ptpool *)kgsl_driver.ptpool, + gpummu_pt->base.hostptr); + + kgsl_driver.stats.coherent -= KGSL_PAGETABLE_SIZE; + + kfree(gpummu_pt->tlbflushfilter.base); + + kfree(gpummu_pt); +} + +static inline uint32_t +kgsl_pt_entry_get(unsigned int va_base, uint32_t va) +{ + return (va - va_base) >> PAGE_SHIFT; +} + +static inline void +kgsl_pt_map_set(struct kgsl_gpummu_pt *pt, uint32_t pte, uint32_t val) +{ + uint32_t *baseptr = (uint32_t *)pt->base.hostptr; + + writel_relaxed(val, &baseptr[pte]); +} + +static inline uint32_t +kgsl_pt_map_get(struct kgsl_gpummu_pt *pt, uint32_t pte) +{ + uint32_t *baseptr = (uint32_t *)pt->base.hostptr; + return readl_relaxed(&baseptr[pte]) & GSL_PT_PAGE_ADDR_MASK; +} + +static unsigned int kgsl_gpummu_pt_get_flags(struct kgsl_pagetable *pt, + enum kgsl_deviceid id) +{ + unsigned int result = 0; + struct kgsl_gpummu_pt *gpummu_pt = (struct kgsl_gpummu_pt *) + pt->priv; + + if (pt == NULL) + return 0; + + spin_lock(&pt->lock); + if (gpummu_pt->tlb_flags && (1<tlb_flags &= ~(1<lock); + return result; +} + +static void kgsl_gpummu_pagefault(struct kgsl_device *device) +{ + unsigned int reg; + unsigned int ptbase; + + kgsl_regread(device, MH_MMU_PAGE_FAULT, ®); + kgsl_regread(device, MH_MMU_PT_BASE, &ptbase); + + KGSL_MEM_CRIT(device, + "mmu page fault: page=0x%lx pt=%d op=%s axi=%d\n", + reg & ~(PAGE_SIZE - 1), + kgsl_mmu_get_ptname_from_ptbase(ptbase), + reg & 0x02 ? "WRITE" : "READ", (reg >> 4) & 0xF); +} + +static void *kgsl_gpummu_create_pagetable(void) +{ + struct kgsl_gpummu_pt *gpummu_pt; + + gpummu_pt = kzalloc(sizeof(struct kgsl_gpummu_pt), + GFP_KERNEL); + if (!gpummu_pt) + return NULL; + + gpummu_pt->tlb_flags = 0; + gpummu_pt->last_superpte = 0; + + gpummu_pt->tlbflushfilter.size = (CONFIG_MSM_KGSL_PAGE_TABLE_SIZE / + (PAGE_SIZE * GSL_PT_SUPER_PTE * 8)) + 1; + gpummu_pt->tlbflushfilter.base = (unsigned int *) + kzalloc(gpummu_pt->tlbflushfilter.size, GFP_KERNEL); + if (!gpummu_pt->tlbflushfilter.base) { + KGSL_CORE_ERR("kzalloc(%d) failed\n", + gpummu_pt->tlbflushfilter.size); + goto err_free_gpummu; + } + GSL_TLBFLUSH_FILTER_RESET(); + + gpummu_pt->base.hostptr = kgsl_ptpool_alloc((struct kgsl_ptpool *) + kgsl_driver.ptpool, + &gpummu_pt->base.physaddr); + + if (gpummu_pt->base.hostptr == NULL) + goto err_flushfilter; + + /* ptpool allocations are from coherent memory, so update the + device statistics acordingly */ + + KGSL_STATS_ADD(KGSL_PAGETABLE_SIZE, kgsl_driver.stats.coherent, + kgsl_driver.stats.coherent_max); + + gpummu_pt->base.gpuaddr = gpummu_pt->base.physaddr; + gpummu_pt->base.size = KGSL_PAGETABLE_SIZE; + + return (void *)gpummu_pt; + +err_flushfilter: + kfree(gpummu_pt->tlbflushfilter.base); +err_free_gpummu: + kfree(gpummu_pt); + + return NULL; +} + +static void kgsl_gpummu_default_setstate(struct kgsl_device *device, + uint32_t flags) +{ + struct kgsl_gpummu_pt *gpummu_pt; + if (!kgsl_mmu_enabled()) + return; + + if (flags & KGSL_MMUFLAGS_PTUPDATE) { + kgsl_idle(device, KGSL_TIMEOUT_DEFAULT); + gpummu_pt = device->mmu.hwpagetable->priv; + kgsl_regwrite(device, MH_MMU_PT_BASE, + gpummu_pt->base.gpuaddr); + } + + if (flags & KGSL_MMUFLAGS_TLBFLUSH) { + /* Invalidate all and tc */ + kgsl_regwrite(device, MH_MMU_INVALIDATE, 0x00000003); + } +} + +static void kgsl_gpummu_setstate(struct kgsl_device *device, + struct kgsl_pagetable *pagetable) +{ + struct kgsl_mmu *mmu = &device->mmu; + struct kgsl_gpummu_pt *gpummu_pt; + + if (mmu->flags & KGSL_FLAGS_STARTED) { + /* page table not current, then setup mmu to use new + * specified page table + */ + if (mmu->hwpagetable != pagetable) { + mmu->hwpagetable = pagetable; + spin_lock(&mmu->hwpagetable->lock); + gpummu_pt = mmu->hwpagetable->priv; + gpummu_pt->tlb_flags &= ~(1<id); + spin_unlock(&mmu->hwpagetable->lock); + + /* call device specific set page table */ + kgsl_setstate(mmu->device, KGSL_MMUFLAGS_TLBFLUSH | + KGSL_MMUFLAGS_PTUPDATE); + } + } +} + +static int kgsl_gpummu_init(struct kgsl_device *device) +{ + /* + * intialize device mmu + * + * call this with the global lock held + */ + int status = 0; + struct kgsl_mmu *mmu = &device->mmu; + + mmu->device = device; + + /* sub-client MMU lookups require address translation */ + if ((mmu->config & ~0x1) > 0) { + /*make sure virtual address range is a multiple of 64Kb */ + if (CONFIG_MSM_KGSL_PAGE_TABLE_SIZE & ((1 << 16) - 1)) { + KGSL_CORE_ERR("Invalid pagetable size requested " + "for GPUMMU: %x\n", CONFIG_MSM_KGSL_PAGE_TABLE_SIZE); + return -EINVAL; + } + + /* allocate memory used for completing r/w operations that + * cannot be mapped by the MMU + */ + status = kgsl_allocate_contiguous(&mmu->setstate_memory, 64); + if (!status) + kgsl_sharedmem_set(&mmu->setstate_memory, 0, 0, + mmu->setstate_memory.size); + } + + dev_info(device->dev, "|%s| MMU type set for device is GPUMMU\n", + __func__); + return status; +} + +static int kgsl_gpummu_start(struct kgsl_device *device) +{ + /* + * intialize device mmu + * + * call this with the global lock held + */ + + struct kgsl_mmu *mmu = &device->mmu; + struct kgsl_gpummu_pt *gpummu_pt; + + if (mmu->flags & KGSL_FLAGS_STARTED) + return 0; + + /* MMU not enabled */ + if ((mmu->config & 0x1) == 0) + return 0; + + /* setup MMU and sub-client behavior */ + kgsl_regwrite(device, MH_MMU_CONFIG, mmu->config); + + /* idle device */ + kgsl_idle(device, KGSL_TIMEOUT_DEFAULT); + + /* enable axi interrupts */ + kgsl_regwrite(device, MH_INTERRUPT_MASK, + GSL_MMU_INT_MASK | MH_INTERRUPT_MASK__MMU_PAGE_FAULT); + + kgsl_sharedmem_set(&mmu->setstate_memory, 0, 0, + mmu->setstate_memory.size); + + /* TRAN_ERROR needs a 32 byte (32 byte aligned) chunk of memory + * to complete transactions in case of an MMU fault. Note that + * we'll leave the bottom 32 bytes of the setstate_memory for other + * purposes (e.g. use it when dummy read cycles are needed + * for other blocks) */ + kgsl_regwrite(device, MH_MMU_TRAN_ERROR, + mmu->setstate_memory.physaddr + 32); + + if (mmu->defaultpagetable == NULL) + mmu->defaultpagetable = + kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT); + + /* Return error if the default pagetable doesn't exist */ + if (mmu->defaultpagetable == NULL) + return -ENOMEM; + + mmu->hwpagetable = mmu->defaultpagetable; + gpummu_pt = mmu->hwpagetable->priv; + kgsl_regwrite(device, MH_MMU_PT_BASE, + gpummu_pt->base.gpuaddr); + kgsl_regwrite(device, MH_MMU_VA_RANGE, + (KGSL_PAGETABLE_BASE | + (CONFIG_MSM_KGSL_PAGE_TABLE_SIZE >> 16))); + kgsl_setstate(device, KGSL_MMUFLAGS_TLBFLUSH); + mmu->flags |= KGSL_FLAGS_STARTED; + + return 0; +} + +static int +kgsl_gpummu_unmap(void *mmu_specific_pt, + struct kgsl_memdesc *memdesc) +{ + unsigned int numpages; + unsigned int pte, ptefirst, ptelast, superpte; + unsigned int range = memdesc->size; + struct kgsl_gpummu_pt *gpummu_pt = mmu_specific_pt; + + /* All GPU addresses as assigned are page aligned, but some + functions purturb the gpuaddr with an offset, so apply the + mask here to make sure we have the right address */ + + unsigned int gpuaddr = memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK; + + numpages = (range >> PAGE_SHIFT); + if (range & (PAGE_SIZE - 1)) + numpages++; + + ptefirst = kgsl_pt_entry_get(KGSL_PAGETABLE_BASE, gpuaddr); + ptelast = ptefirst + numpages; + + superpte = ptefirst - (ptefirst & (GSL_PT_SUPER_PTE-1)); + GSL_TLBFLUSH_FILTER_SETDIRTY(superpte / GSL_PT_SUPER_PTE); + for (pte = ptefirst; pte < ptelast; pte++) { +#ifdef VERBOSE_DEBUG + /* check if PTE exists */ + if (!kgsl_pt_map_get(gpummu_pt, pte)) + KGSL_CORE_ERR("pt entry %x is already " + "unmapped for pagetable %p\n", pte, gpummu_pt); +#endif + kgsl_pt_map_set(gpummu_pt, pte, GSL_PT_PAGE_DIRTY); + superpte = pte - (pte & (GSL_PT_SUPER_PTE - 1)); + if (pte == superpte) + GSL_TLBFLUSH_FILTER_SETDIRTY(superpte / + GSL_PT_SUPER_PTE); + } + + /* Post all writes to the pagetable */ + wmb(); + + return 0; +} + +static int +kgsl_gpummu_map(void *mmu_specific_pt, + struct kgsl_memdesc *memdesc, + unsigned int protflags) +{ + int numpages; + unsigned int pte, ptefirst, ptelast, physaddr; + int flushtlb; + unsigned int offset = 0; + struct kgsl_gpummu_pt *gpummu_pt = mmu_specific_pt; + + if (!protflags || + protflags & ~(GSL_PT_PAGE_RV | GSL_PT_PAGE_WV)) { + KGSL_CORE_ERR("Invalid protflags for " + "kgsl_mmu_specific_map: %x", protflags); + return -EINVAL; + } + + numpages = (memdesc->size >> PAGE_SHIFT); + + ptefirst = kgsl_pt_entry_get(KGSL_PAGETABLE_BASE, memdesc->gpuaddr); + ptelast = ptefirst + numpages; + + pte = ptefirst; + flushtlb = 0; + + /* tlb needs to be flushed when the first and last pte are not at + * superpte boundaries */ + if ((ptefirst & (GSL_PT_SUPER_PTE - 1)) != 0 || + ((ptelast + 1) & (GSL_PT_SUPER_PTE-1)) != 0) + flushtlb = 1; + + for (pte = ptefirst; pte < ptelast; pte++, offset += PAGE_SIZE) { +#ifdef VERBOSE_DEBUG + /* check if PTE exists */ + uint32_t val = kgsl_pt_map_get(gpummu_pt, pte); + if (val != 0 && val != GSL_PT_PAGE_DIRTY) { + KGSL_CORE_ERR("pt entry %x is already set with " + "value %x for pagetable %p\n", pte, val, gpummu_pt); + return -EINVAL; + } +#endif + if ((pte & (GSL_PT_SUPER_PTE-1)) == 0) + if (GSL_TLBFLUSH_FILTER_ISDIRTY(pte / GSL_PT_SUPER_PTE)) + flushtlb = 1; + /* mark pte as in use */ + + physaddr = memdesc->ops->physaddr(memdesc, offset); + if (!physaddr) { + KGSL_CORE_ERR("Failed to convert %x address to " + "physical", (unsigned int)memdesc->hostptr + offset); + kgsl_gpummu_unmap(mmu_specific_pt, memdesc); + return -EFAULT; + } + kgsl_pt_map_set(gpummu_pt, pte, physaddr | protflags); + } + + /* Post all writes to the pagetable */ + wmb(); + + /* Invalidate tlb only if current page table used by GPU is the + * pagetable that we used to allocate */ + if (flushtlb) { + /*set all devices as needing flushing*/ + gpummu_pt->tlb_flags = UINT_MAX; + GSL_TLBFLUSH_FILTER_RESET(); + } + + return 0; +} + +static int kgsl_gpummu_stop(struct kgsl_device *device) +{ + struct kgsl_mmu *mmu = &device->mmu; + + kgsl_regwrite(device, MH_MMU_CONFIG, 0x00000000); + mmu->flags &= ~KGSL_FLAGS_STARTED; + + return 0; +} + +static int kgsl_gpummu_close(struct kgsl_device *device) +{ + /* + * close device mmu + * + * call this with the global lock held + */ + struct kgsl_mmu *mmu = &device->mmu; + + if (mmu->setstate_memory.gpuaddr) + kgsl_sharedmem_free(&mmu->setstate_memory); + + if (mmu->defaultpagetable) + kgsl_mmu_putpagetable(mmu->defaultpagetable); + + return 0; +} + +static unsigned int +kgsl_gpummu_get_current_ptbase(struct kgsl_device *device) +{ + unsigned int ptbase; + kgsl_regread(device, MH_MMU_PT_BASE, &ptbase); + return ptbase; +} + +struct kgsl_mmu_ops gpummu_ops = { + .mmu_init = kgsl_gpummu_init, + .mmu_close = kgsl_gpummu_close, + .mmu_start = kgsl_gpummu_start, + .mmu_stop = kgsl_gpummu_stop, + .mmu_setstate = kgsl_gpummu_setstate, + .mmu_device_setstate = kgsl_gpummu_default_setstate, + .mmu_pagefault = kgsl_gpummu_pagefault, + .mmu_get_current_ptbase = kgsl_gpummu_get_current_ptbase, +}; + +struct kgsl_mmu_pt_ops gpummu_pt_ops = { + .mmu_map = kgsl_gpummu_map, + .mmu_unmap = kgsl_gpummu_unmap, + .mmu_create_pagetable = kgsl_gpummu_create_pagetable, + .mmu_destroy_pagetable = kgsl_gpummu_destroy_pagetable, + .mmu_pt_equal = kgsl_gpummu_pt_equal, + .mmu_pt_get_flags = kgsl_gpummu_pt_get_flags, +}; diff --git a/drivers/gpu/msm/kgsl_gpummu.h b/drivers/gpu/msm/kgsl_gpummu.h new file mode 100644 index 0000000000000..46466a8d2e68f --- /dev/null +++ b/drivers/gpu/msm/kgsl_gpummu.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __KGSL_GPUMMU_H +#define __KGSL_GPUMMU_H + +#define GSL_PT_PAGE_BITS_MASK 0x00000007 +#define GSL_PT_PAGE_ADDR_MASK PAGE_MASK + +#define GSL_MMU_INT_MASK \ + (MH_INTERRUPT_MASK__AXI_READ_ERROR | \ + MH_INTERRUPT_MASK__AXI_WRITE_ERROR) + +/* Macros to manage TLB flushing */ +#define GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS (sizeof(unsigned char) * 8) +#define GSL_TLBFLUSH_FILTER_GET(superpte) \ + (*((unsigned char *) \ + (((unsigned int)gpummu_pt->tlbflushfilter.base) \ + + (superpte / GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS)))) +#define GSL_TLBFLUSH_FILTER_SETDIRTY(superpte) \ + (GSL_TLBFLUSH_FILTER_GET((superpte)) |= 1 << \ + (superpte % GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS)) +#define GSL_TLBFLUSH_FILTER_ISDIRTY(superpte) \ + (GSL_TLBFLUSH_FILTER_GET((superpte)) & \ + (1 << (superpte % GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS))) +#define GSL_TLBFLUSH_FILTER_RESET() memset(gpummu_pt->tlbflushfilter.base,\ + 0, gpummu_pt->tlbflushfilter.size) + +extern struct kgsl_mmu_ops gpummu_ops; +extern struct kgsl_mmu_pt_ops gpummu_pt_ops; + +struct kgsl_tlbflushfilter { + unsigned int *base; + unsigned int size; +}; + +struct kgsl_gpummu_pt { + struct kgsl_memdesc base; + unsigned int last_superpte; + unsigned int tlb_flags; + /* Maintain filter to manage tlb flushing */ + struct kgsl_tlbflushfilter tlbflushfilter; +}; + +struct kgsl_ptpool_chunk { + size_t size; + unsigned int count; + int dynamic; + + void *data; + unsigned int phys; + + unsigned long *bitmap; + struct list_head list; +}; + +struct kgsl_ptpool { + size_t ptsize; + struct mutex lock; + struct list_head list; + int entries; + int static_entries; + int chunks; +}; + +void *kgsl_gpummu_ptpool_init(int ptsize, + int entries); +void kgsl_gpummu_ptpool_destroy(void *ptpool); + +static inline unsigned int kgsl_pt_get_base_addr(struct kgsl_pagetable *pt) +{ + struct kgsl_gpummu_pt *gpummu_pt = pt->priv; + return gpummu_pt->base.gpuaddr; +} +#endif /* __KGSL_GPUMMU_H */ diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c new file mode 100644 index 0000000000000..f9b9b4a18a796 --- /dev/null +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -0,0 +1,353 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kgsl.h" +#include "kgsl_device.h" +#include "kgsl_mmu.h" +#include "kgsl_sharedmem.h" + +struct kgsl_iommu { + struct device *iommu_user_dev; + int iommu_user_dev_attached; + struct device *iommu_priv_dev; + int iommu_priv_dev_attached; +}; + +static int kgsl_iommu_pt_equal(struct kgsl_pagetable *pt, + unsigned int pt_base) +{ + struct iommu_domain *domain = pt->priv; + return pt && pt_base && ((unsigned int)domain == pt_base); +} + +static void kgsl_iommu_destroy_pagetable(void *mmu_specific_pt) +{ + struct iommu_domain *domain = mmu_specific_pt; + if (domain) + iommu_domain_free(domain); +} + +void *kgsl_iommu_create_pagetable(void) +{ + struct iommu_domain *domain = iommu_domain_alloc(0); + if (!domain) + KGSL_CORE_ERR("Failed to create iommu domain\n"); + + return domain; +} + +static void kgsl_detach_pagetable_iommu_domain(struct kgsl_mmu *mmu) +{ + struct iommu_domain *domain; + struct kgsl_iommu *iommu = mmu->priv; + + BUG_ON(mmu->hwpagetable == NULL); + BUG_ON(mmu->hwpagetable->priv == NULL); + + domain = mmu->hwpagetable->priv; + + if (iommu->iommu_user_dev_attached) { + iommu_detach_device(domain, iommu->iommu_user_dev); + iommu->iommu_user_dev_attached = 0; + KGSL_MEM_INFO(mmu->device, + "iommu %p detached from user dev of MMU: %p\n", + domain, mmu); + } + if (iommu->iommu_priv_dev_attached) { + iommu_detach_device(domain, iommu->iommu_priv_dev); + iommu->iommu_priv_dev_attached = 0; + KGSL_MEM_INFO(mmu->device, + "iommu %p detached from priv dev of MMU: %p\n", + domain, mmu); + } +} + +static int kgsl_attach_pagetable_iommu_domain(struct kgsl_mmu *mmu) +{ + struct iommu_domain *domain; + int ret = 0; + struct kgsl_iommu *iommu = mmu->priv; + + BUG_ON(mmu->hwpagetable == NULL); + BUG_ON(mmu->hwpagetable->priv == NULL); + + domain = mmu->hwpagetable->priv; + + if (iommu->iommu_user_dev && !iommu->iommu_user_dev_attached) { + ret = iommu_attach_device(domain, iommu->iommu_user_dev); + if (ret) { + KGSL_MEM_ERR(mmu->device, + "Failed to attach device, err %d\n", ret); + goto done; + } + iommu->iommu_user_dev_attached = 1; + KGSL_MEM_INFO(mmu->device, + "iommu %p attached to user dev of MMU: %p\n", + domain, mmu); + } + if (iommu->iommu_priv_dev && !iommu->iommu_priv_dev_attached) { + ret = iommu_attach_device(domain, iommu->iommu_priv_dev); + if (ret) { + KGSL_MEM_ERR(mmu->device, + "Failed to attach device, err %d\n", ret); + iommu_detach_device(domain, iommu->iommu_user_dev); + iommu->iommu_user_dev_attached = 0; + goto done; + } + iommu->iommu_priv_dev_attached = 1; + KGSL_MEM_INFO(mmu->device, + "iommu %p attached to priv dev of MMU: %p\n", + domain, mmu); + } +done: + return ret; +} + +static int kgsl_get_iommu_ctxt(struct kgsl_iommu *iommu, + struct kgsl_device *device) +{ + int status = 0; + struct platform_device *pdev = + container_of(device->parentdev, struct platform_device, dev); + struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data; + if (pdata_dev->iommu_user_ctx_name) + iommu->iommu_user_dev = msm_iommu_get_ctx( + pdata_dev->iommu_user_ctx_name); + if (pdata_dev->iommu_priv_ctx_name) + iommu->iommu_priv_dev = msm_iommu_get_ctx( + pdata_dev->iommu_priv_ctx_name); + if (!iommu->iommu_user_dev) { + KGSL_CORE_ERR("Failed to get user iommu dev handle for " + "device %s\n", + pdata_dev->iommu_user_ctx_name); + status = -EINVAL; + } + return status; +} + +static void kgsl_iommu_setstate(struct kgsl_device *device, + struct kgsl_pagetable *pagetable) +{ + struct kgsl_mmu *mmu = &device->mmu; + + if (mmu->flags & KGSL_FLAGS_STARTED) { + /* page table not current, then setup mmu to use new + * specified page table + */ + if (mmu->hwpagetable != pagetable) { + kgsl_idle(device, KGSL_TIMEOUT_DEFAULT); + kgsl_detach_pagetable_iommu_domain(mmu); + mmu->hwpagetable = pagetable; + if (mmu->hwpagetable) + kgsl_attach_pagetable_iommu_domain(mmu); + } + } +} + +static int kgsl_iommu_init(struct kgsl_device *device) +{ + /* + * intialize device mmu + * + * call this with the global lock held + */ + int status = 0; + struct kgsl_mmu *mmu = &device->mmu; + struct kgsl_iommu *iommu; + + mmu->device = device; + + iommu = kzalloc(sizeof(struct kgsl_iommu), GFP_KERNEL); + if (!iommu) { + KGSL_CORE_ERR("kzalloc(%d) failed\n", + sizeof(struct kgsl_iommu)); + return -ENOMEM; + } + + iommu->iommu_priv_dev_attached = 0; + iommu->iommu_user_dev_attached = 0; + status = kgsl_get_iommu_ctxt(iommu, device); + if (status) { + kfree(iommu); + iommu = NULL; + } + mmu->priv = iommu; + + dev_info(device->dev, "|%s| MMU type set for device is IOMMU\n", + __func__); + return status; +} + +static int kgsl_iommu_start(struct kgsl_device *device) +{ + int status; + struct kgsl_mmu *mmu = &device->mmu; + + if (mmu->flags & KGSL_FLAGS_STARTED) + return 0; + + kgsl_regwrite(device, MH_MMU_CONFIG, 0x00000000); + if (mmu->defaultpagetable == NULL) + mmu->defaultpagetable = + kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT); + /* Return error if the default pagetable doesn't exist */ + if (mmu->defaultpagetable == NULL) + return -ENOMEM; + mmu->hwpagetable = mmu->defaultpagetable; + + status = kgsl_attach_pagetable_iommu_domain(mmu); + if (!status) + mmu->flags |= KGSL_FLAGS_STARTED; + + return status; +} + +static int +kgsl_iommu_unmap(void *mmu_specific_pt, + struct kgsl_memdesc *memdesc) +{ + int ret; + unsigned int range = memdesc->size; + unsigned int iommu_map_addr; + int map_order = get_order(SZ_4K); + struct iommu_domain *domain = (struct iommu_domain *) + mmu_specific_pt; + + /* All GPU addresses as assigned are page aligned, but some + functions purturb the gpuaddr with an offset, so apply the + mask here to make sure we have the right address */ + + unsigned int gpuaddr = memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK; + + if (range == 0 || gpuaddr == 0) + return 0; + + for (iommu_map_addr = gpuaddr; iommu_map_addr < (gpuaddr + range); + iommu_map_addr += SZ_4K) { + ret = iommu_unmap(domain, iommu_map_addr, map_order); + if (ret) + KGSL_CORE_ERR("iommu_unmap(%p, %x, %d) failed " + "with err: %d\n", domain, iommu_map_addr, + map_order, ret); + } + + return 0; +} + +static int +kgsl_iommu_map(void *mmu_specific_pt, + struct kgsl_memdesc *memdesc, + unsigned int protflags) +{ + int ret = 0; + unsigned int physaddr; + unsigned int iommu_virt_addr; + unsigned int offset = 0; + int map_order; + struct iommu_domain *domain = (struct iommu_domain *) + mmu_specific_pt; + + BUG_ON(NULL == domain); + + map_order = get_order(SZ_4K); + + for (iommu_virt_addr = memdesc->gpuaddr; + iommu_virt_addr < (memdesc->gpuaddr + memdesc->size); + iommu_virt_addr += SZ_4K, offset += PAGE_SIZE) { + physaddr = memdesc->ops->physaddr(memdesc, offset); + if (!physaddr) { + KGSL_CORE_ERR("Failed to convert %x address to " + "physical\n", (unsigned int)memdesc->hostptr + offset); + kgsl_iommu_unmap(mmu_specific_pt, memdesc); + return -EFAULT; + } + ret = iommu_map(domain, iommu_virt_addr, physaddr, + map_order, MSM_IOMMU_ATTR_NONCACHED); + if (ret) { + KGSL_CORE_ERR("iommu_map(%p, %x, %x, %d, %d) " + "failed with err: %d\n", domain, + iommu_virt_addr, physaddr, map_order, + MSM_IOMMU_ATTR_NONCACHED, ret); + kgsl_iommu_unmap(mmu_specific_pt, memdesc); + return ret; + } + } + + return ret; +} + +static int kgsl_iommu_stop(struct kgsl_device *device) +{ + /* + * stop device mmu + * + * call this with the global lock held + */ + struct kgsl_mmu *mmu = &device->mmu; + + if (mmu->flags & KGSL_FLAGS_STARTED) { + /* detach iommu attachment */ + kgsl_detach_pagetable_iommu_domain(mmu); + + mmu->flags &= ~KGSL_FLAGS_STARTED; + } + + return 0; +} + +static int kgsl_iommu_close(struct kgsl_device *device) +{ + struct kgsl_mmu *mmu = &device->mmu; + if (mmu->defaultpagetable) + kgsl_mmu_putpagetable(mmu->defaultpagetable); + + return 0; +} + +static unsigned int +kgsl_iommu_get_current_ptbase(struct kgsl_device *device) +{ + /* Current base is always the hwpagetables domain as we + * do not use per process pagetables right not for iommu. + * This will change when we switch to per process pagetables. + */ + return (unsigned int)device->mmu.hwpagetable->priv; +} + +struct kgsl_mmu_ops iommu_ops = { + .mmu_init = kgsl_iommu_init, + .mmu_close = kgsl_iommu_close, + .mmu_start = kgsl_iommu_start, + .mmu_stop = kgsl_iommu_stop, + .mmu_setstate = kgsl_iommu_setstate, + .mmu_device_setstate = NULL, + .mmu_pagefault = NULL, + .mmu_get_current_ptbase = kgsl_iommu_get_current_ptbase, +}; + +struct kgsl_mmu_pt_ops iommu_pt_ops = { + .mmu_map = kgsl_iommu_map, + .mmu_unmap = kgsl_iommu_unmap, + .mmu_create_pagetable = kgsl_iommu_create_pagetable, + .mmu_destroy_pagetable = kgsl_iommu_destroy_pagetable, + .mmu_pt_equal = kgsl_iommu_pt_equal, + .mmu_pt_get_flags = NULL, +}; diff --git a/drivers/gpu/msm/kgsl_log.h b/drivers/gpu/msm/kgsl_log.h index e816e568d4629..9fafcf4d684b1 100644 --- a/drivers/gpu/msm/kgsl_log.h +++ b/drivers/gpu/msm/kgsl_log.h @@ -1,29 +1,13 @@ /* Copyright (c) 2002,2008-2011, Code Aurora Forum. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 __KGSL_LOG_H diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c index bc35c411164a8..7eec9e5ddcac0 100644 --- a/drivers/gpu/msm/kgsl_mmu.c +++ b/drivers/gpu/msm/kgsl_mmu.c @@ -16,335 +16,76 @@ #include #include #include +#include #include "kgsl.h" #include "kgsl_mmu.h" +#include "kgsl_device.h" +#include "kgsl_sharedmem.h" #define KGSL_MMU_ALIGN_SHIFT 13 #define KGSL_MMU_ALIGN_MASK (~((1 << KGSL_MMU_ALIGN_SHIFT) - 1)) -#define GSL_PT_PAGE_BITS_MASK 0x00000007 -#define GSL_PT_PAGE_ADDR_MASK PAGE_MASK +static enum kgsl_mmutype kgsl_mmu_type; -#define GSL_MMU_INT_MASK \ - (MH_INTERRUPT_MASK__AXI_READ_ERROR | \ - MH_INTERRUPT_MASK__AXI_WRITE_ERROR) +static void pagetable_remove_sysfs_objects(struct kgsl_pagetable *pagetable); -static ssize_t -sysfs_show_ptpool_entries(struct kobject *kobj, - struct kobj_attribute *attr, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", kgsl_driver.ptpool.entries); -} - -static ssize_t -sysfs_show_ptpool_min(struct kobject *kobj, - struct kobj_attribute *attr, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", - kgsl_driver.ptpool.static_entries); -} - -static ssize_t -sysfs_show_ptpool_chunks(struct kobject *kobj, - struct kobj_attribute *attr, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", kgsl_driver.ptpool.chunks); -} - -static ssize_t -sysfs_show_ptpool_ptsize(struct kobject *kobj, - struct kobj_attribute *attr, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", kgsl_driver.ptpool.ptsize); -} - -static struct kobj_attribute attr_ptpool_entries = { - .attr = { .name = "ptpool_entries", .mode = 0444 }, - .show = sysfs_show_ptpool_entries, - .store = NULL, -}; - -static struct kobj_attribute attr_ptpool_min = { - .attr = { .name = "ptpool_min", .mode = 0444 }, - .show = sysfs_show_ptpool_min, - .store = NULL, -}; - -static struct kobj_attribute attr_ptpool_chunks = { - .attr = { .name = "ptpool_chunks", .mode = 0444 }, - .show = sysfs_show_ptpool_chunks, - .store = NULL, -}; - -static struct kobj_attribute attr_ptpool_ptsize = { - .attr = { .name = "ptpool_ptsize", .mode = 0444 }, - .show = sysfs_show_ptpool_ptsize, - .store = NULL, -}; - -static struct attribute *ptpool_attrs[] = { - &attr_ptpool_entries.attr, - &attr_ptpool_min.attr, - &attr_ptpool_chunks.attr, - &attr_ptpool_ptsize.attr, - NULL, -}; - -static struct attribute_group ptpool_attr_group = { - .attrs = ptpool_attrs, -}; - -static int -_kgsl_ptpool_add_entries(struct kgsl_ptpool *pool, int count, int dynamic) +static int kgsl_cleanup_pt(struct kgsl_pagetable *pt) { - struct kgsl_ptpool_chunk *chunk; - size_t size = ALIGN(count * pool->ptsize, PAGE_SIZE); - - BUG_ON(count == 0); - - if (get_order(size) >= MAX_ORDER) { - KGSL_CORE_ERR("ptpool allocation is too big: %d\n", size); - return -EINVAL; - } - - chunk = kzalloc(sizeof(*chunk), GFP_KERNEL); - if (chunk == NULL) { - KGSL_CORE_ERR("kzalloc(%d) failed\n", sizeof(*chunk)); - return -ENOMEM; - } - - chunk->size = size; - chunk->count = count; - chunk->dynamic = dynamic; - - chunk->data = dma_alloc_coherent(NULL, size, - &chunk->phys, GFP_KERNEL); - - if (chunk->data == NULL) { - KGSL_CORE_ERR("dma_alloc_coherent(%d) failed\n", size); - goto err; - } - - chunk->bitmap = kzalloc(BITS_TO_LONGS(count) * 4, GFP_KERNEL); - - if (chunk->bitmap == NULL) { - KGSL_CORE_ERR("kzalloc(%d) failed\n", - BITS_TO_LONGS(count) * 4); - goto err_dma; + int i; + for (i = 0; i < KGSL_DEVICE_MAX; i++) { + struct kgsl_device *device = kgsl_driver.devp[i]; + if (device) + device->ftbl->cleanup_pt(device, pt); } - - list_add_tail(&chunk->list, &pool->list); - - pool->chunks++; - pool->entries += count; - - if (!dynamic) - pool->static_entries += count; - return 0; - -err_dma: - dma_free_coherent(NULL, chunk->size, chunk->data, chunk->phys); -err: - kfree(chunk); - return -ENOMEM; -} - -static void * -_kgsl_ptpool_get_entry(struct kgsl_ptpool *pool, unsigned int *physaddr) -{ - struct kgsl_ptpool_chunk *chunk; - - list_for_each_entry(chunk, &pool->list, list) { - int bit = find_first_zero_bit(chunk->bitmap, chunk->count); - - if (bit >= chunk->count) - continue; - - set_bit(bit, chunk->bitmap); - *physaddr = chunk->phys + (bit * pool->ptsize); - - return chunk->data + (bit * pool->ptsize); - } - - return NULL; -} - -/** - * kgsl_ptpool_add - * @pool: A pointer to a ptpool structure - * @entries: Number of entries to add - * - * Add static entries to the pagetable pool. - */ - -int -kgsl_ptpool_add(struct kgsl_ptpool *pool, int count) -{ - int ret = 0; - BUG_ON(count == 0); - - mutex_lock(&pool->lock); - - /* Only 4MB can be allocated in one chunk, so larger allocations - need to be split into multiple sections */ - - while (count) { - int entries = ((count * pool->ptsize) > SZ_4M) ? - SZ_4M / pool->ptsize : count; - - /* Add the entries as static, i.e. they don't ever stand - a chance of being removed */ - - ret = _kgsl_ptpool_add_entries(pool, entries, 0); - if (ret) - break; - - count -= entries; - } - - mutex_unlock(&pool->lock); - return ret; -} - -/** - * kgsl_ptpool_alloc - * @pool: A pointer to a ptpool structure - * @addr: A pointer to store the physical address of the chunk - * - * Allocate a pagetable from the pool. Returns the virtual address - * of the pagetable, the physical address is returned in physaddr - */ - -void *kgsl_ptpool_alloc(struct kgsl_ptpool *pool, unsigned int *physaddr) -{ - void *addr = NULL; - int ret; - - mutex_lock(&pool->lock); - addr = _kgsl_ptpool_get_entry(pool, physaddr); - if (addr) - goto done; - - /* Add a chunk for 1 more pagetable and mark it as dynamic */ - ret = _kgsl_ptpool_add_entries(pool, 1, 1); - - if (ret) - goto done; - - addr = _kgsl_ptpool_get_entry(pool, physaddr); -done: - mutex_unlock(&pool->lock); - return addr; -} - -static inline void _kgsl_ptpool_rm_chunk(struct kgsl_ptpool_chunk *chunk) -{ - list_del(&chunk->list); - - if (chunk->data) - dma_free_coherent(NULL, chunk->size, chunk->data, - chunk->phys); - kfree(chunk->bitmap); - kfree(chunk); } -/** - * kgsl_ptpool_free - * @pool: A pointer to a ptpool structure - * @addr: A pointer to the virtual address to free - * - * Free a pagetable allocated from the pool - */ - -void kgsl_ptpool_free(struct kgsl_ptpool *pool, void *addr) +static void kgsl_destroy_pagetable(struct kref *kref) { - struct kgsl_ptpool_chunk *chunk, *tmp; - - if (pool == NULL || addr == NULL) - return; - - mutex_lock(&pool->lock); - list_for_each_entry_safe(chunk, tmp, &pool->list, list) { - if (addr >= chunk->data && - addr < chunk->data + chunk->size) { - int bit = ((unsigned long) (addr - chunk->data)) / - pool->ptsize; - - clear_bit(bit, chunk->bitmap); - memset(addr, 0, pool->ptsize); + struct kgsl_pagetable *pagetable = container_of(kref, + struct kgsl_pagetable, refcount); + unsigned long flags; - if (chunk->dynamic && - bitmap_empty(chunk->bitmap, chunk->count)) - _kgsl_ptpool_rm_chunk(chunk); - - break; - } - } + spin_lock_irqsave(&kgsl_driver.ptlock, flags); + list_del(&pagetable->list); + spin_unlock_irqrestore(&kgsl_driver.ptlock, flags); - mutex_unlock(&pool->lock); -} + pagetable_remove_sysfs_objects(pagetable); -void kgsl_ptpool_destroy(struct kgsl_ptpool *pool) -{ - struct kgsl_ptpool_chunk *chunk, *tmp; + kgsl_cleanup_pt(pagetable); - if (pool == NULL) - return; + if (pagetable->pool) + gen_pool_destroy(pagetable->pool); - mutex_lock(&pool->lock); - list_for_each_entry_safe(chunk, tmp, &pool->list, list) - _kgsl_ptpool_rm_chunk(chunk); - mutex_unlock(&pool->lock); + pagetable->pt_ops->mmu_destroy_pagetable(pagetable->priv); - memset(pool, 0, sizeof(*pool)); + kfree(pagetable); } -/** - * kgsl_ptpool_init - * @pool: A pointer to a ptpool structure to initialize - * @ptsize: The size of each pagetable entry - * @entries: The number of inital entries to add to the pool - * - * Initalize a pool and allocate an initial chunk of entries. - */ - -int kgsl_ptpool_init(struct kgsl_ptpool *pool, int ptsize, int entries) +static inline void kgsl_put_pagetable(struct kgsl_pagetable *pagetable) { - int ret = 0; - BUG_ON(ptsize == 0); - - pool->ptsize = ptsize; - mutex_init(&pool->lock); - INIT_LIST_HEAD(&pool->list); - - if (entries) { - ret = kgsl_ptpool_add(pool, entries); - if (ret) - return ret; - } - - return sysfs_create_group(kgsl_driver.ptkobj, &ptpool_attr_group); + if (pagetable) + kref_put(&pagetable->refcount, kgsl_destroy_pagetable); } -/* pt_mutex needs to be held in this function */ - static struct kgsl_pagetable * kgsl_get_pagetable(unsigned long name) { - struct kgsl_pagetable *pt; + struct kgsl_pagetable *pt, *ret = NULL; + unsigned long flags; - list_for_each_entry(pt, &kgsl_driver.pagetable_list, list) { - if (pt->name == name) - return pt; + spin_lock_irqsave(&kgsl_driver.ptlock, flags); + list_for_each_entry(pt, &kgsl_driver.pagetable_list, list) { + if (pt->name == name) { + ret = pt; + kref_get(&ret->refcount); + break; + } } - return NULL; + spin_unlock_irqrestore(&kgsl_driver.ptlock, flags); + return ret; } static struct kgsl_pagetable * @@ -369,13 +110,12 @@ sysfs_show_entries(struct kobject *kobj, struct kgsl_pagetable *pt; int ret = 0; - mutex_lock(&kgsl_driver.pt_mutex); pt = _get_pt_from_kobj(kobj); if (pt) ret += snprintf(buf, PAGE_SIZE, "%d\n", pt->stats.entries); - mutex_unlock(&kgsl_driver.pt_mutex); + kgsl_put_pagetable(pt); return ret; } @@ -387,13 +127,12 @@ sysfs_show_mapped(struct kobject *kobj, struct kgsl_pagetable *pt; int ret = 0; - mutex_lock(&kgsl_driver.pt_mutex); pt = _get_pt_from_kobj(kobj); if (pt) ret += snprintf(buf, PAGE_SIZE, "%d\n", pt->stats.mapped); - mutex_unlock(&kgsl_driver.pt_mutex); + kgsl_put_pagetable(pt); return ret; } @@ -405,13 +144,13 @@ sysfs_show_va_range(struct kobject *kobj, struct kgsl_pagetable *pt; int ret = 0; - mutex_lock(&kgsl_driver.pt_mutex); pt = _get_pt_from_kobj(kobj); if (pt) - ret += snprintf(buf, PAGE_SIZE, "0x%x\n", pt->va_range); + ret += snprintf(buf, PAGE_SIZE, "0x%x\n", + CONFIG_MSM_KGSL_PAGE_TABLE_SIZE); - mutex_unlock(&kgsl_driver.pt_mutex); + kgsl_put_pagetable(pt); return ret; } @@ -423,13 +162,12 @@ sysfs_show_max_mapped(struct kobject *kobj, struct kgsl_pagetable *pt; int ret = 0; - mutex_lock(&kgsl_driver.pt_mutex); pt = _get_pt_from_kobj(kobj); if (pt) ret += snprintf(buf, PAGE_SIZE, "%d\n", pt->stats.max_mapped); - mutex_unlock(&kgsl_driver.pt_mutex); + kgsl_put_pagetable(pt); return ret; } @@ -441,13 +179,12 @@ sysfs_show_max_entries(struct kobject *kobj, struct kgsl_pagetable *pt; int ret = 0; - mutex_lock(&kgsl_driver.pt_mutex); pt = _get_pt_from_kobj(kobj); if (pt) ret += snprintf(buf, PAGE_SIZE, "%d\n", pt->stats.max_entries); - mutex_unlock(&kgsl_driver.pt_mutex); + kgsl_put_pagetable(pt); return ret; } @@ -529,55 +266,97 @@ pagetable_add_sysfs_objects(struct kgsl_pagetable *pagetable) return ret; } -static inline uint32_t -kgsl_pt_entry_get(struct kgsl_pagetable *pt, uint32_t va) +unsigned int kgsl_mmu_get_current_ptbase(struct kgsl_device *device) +{ + struct kgsl_mmu *mmu = &device->mmu; + if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type) + return 0; + else + return mmu->mmu_ops->mmu_get_current_ptbase(device); +} +EXPORT_SYMBOL(kgsl_mmu_get_current_ptbase); + +int +kgsl_mmu_get_ptname_from_ptbase(unsigned int pt_base) { - return (va - pt->va_base) >> PAGE_SHIFT; + struct kgsl_pagetable *pt; + int ptid = -1; + + spin_lock(&kgsl_driver.ptlock); + list_for_each_entry(pt, &kgsl_driver.pagetable_list, list) { + if (pt->pt_ops->mmu_pt_equal(pt, pt_base)) { + ptid = (int) pt->name; + break; + } + } + spin_unlock(&kgsl_driver.ptlock); + + return ptid; } +EXPORT_SYMBOL(kgsl_mmu_get_ptname_from_ptbase); -static inline void -kgsl_pt_map_set(struct kgsl_pagetable *pt, uint32_t pte, uint32_t val) +void kgsl_mmu_setstate(struct kgsl_device *device, + struct kgsl_pagetable *pagetable) { - uint32_t *baseptr = (uint32_t *)pt->base.hostptr; + struct kgsl_mmu *mmu = &device->mmu; - writel_relaxed(val, &baseptr[pte]); + if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type) + return; + else + mmu->mmu_ops->mmu_setstate(device, + pagetable); } +EXPORT_SYMBOL(kgsl_mmu_setstate); -static inline uint32_t -kgsl_pt_map_getaddr(struct kgsl_pagetable *pt, uint32_t pte) +int kgsl_mmu_init(struct kgsl_device *device) { - uint32_t *baseptr = (uint32_t *)pt->base.hostptr; - uint32_t ret = readl_relaxed(&baseptr[pte]) & GSL_PT_PAGE_ADDR_MASK; - return ret; + struct kgsl_mmu *mmu = &device->mmu; + + mmu->device = device; + + if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type) { + dev_info(device->dev, "|%s| MMU type set for device is " + "NOMMU\n", __func__); + return 0; + } else if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type) + mmu->mmu_ops = &gpummu_ops; + else if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type) + mmu->mmu_ops = &iommu_ops; + + return mmu->mmu_ops->mmu_init(device); } +EXPORT_SYMBOL(kgsl_mmu_init); + +int kgsl_mmu_start(struct kgsl_device *device) +{ + struct kgsl_mmu *mmu = &device->mmu; + + if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) { + kgsl_regwrite(device, MH_MMU_CONFIG, 0); + return 0; + } else { + return mmu->mmu_ops->mmu_start(device); + } +} +EXPORT_SYMBOL(kgsl_mmu_start); void kgsl_mh_intrcallback(struct kgsl_device *device) { unsigned int status = 0; unsigned int reg; - kgsl_regread_isr(device, device->mmu.reg.interrupt_status, &status); + kgsl_regread(device, MH_INTERRUPT_STATUS, &status); + kgsl_regread(device, MH_AXI_ERROR, ®); - if (status & MH_INTERRUPT_MASK__AXI_READ_ERROR) { - kgsl_regread_isr(device, device->mmu.reg.axi_error, ®); + if (status & MH_INTERRUPT_MASK__AXI_READ_ERROR) KGSL_MEM_CRIT(device, "axi read error interrupt: %08x\n", reg); - } else if (status & MH_INTERRUPT_MASK__AXI_WRITE_ERROR) { - kgsl_regread_isr(device, device->mmu.reg.axi_error, ®); + if (status & MH_INTERRUPT_MASK__AXI_WRITE_ERROR) KGSL_MEM_CRIT(device, "axi write error interrupt: %08x\n", reg); - } else if (status & MH_INTERRUPT_MASK__MMU_PAGE_FAULT) { - kgsl_regread_isr(device, device->mmu.reg.page_fault, ®); - KGSL_MEM_CRIT(device, "mmu page fault interrupt: %08x\n", reg); - } else { - KGSL_MEM_WARN(device, - "bad bits in REG_MH_INTERRUPT_STATUS %08x\n", status); - } - - kgsl_regwrite_isr(device, device->mmu.reg.interrupt_clear, status); + if (status & MH_INTERRUPT_MASK__MMU_PAGE_FAULT) + device->mmu.mmu_ops->mmu_pagefault(device); - /*TODO: figure out how to handle errror interupts. - * specifically, page faults should probably nuke the client that - * caused them, but we don't have enough info to figure that out yet. - */ + status &= KGSL_MMU_INT_MASK; + kgsl_regwrite(device, MH_INTERRUPT_CLEAR, status); } EXPORT_SYMBOL(kgsl_mh_intrcallback); @@ -589,7 +368,7 @@ static int kgsl_setup_pt(struct kgsl_pagetable *pt) for (i = 0; i < KGSL_DEVICE_MAX; i++) { struct kgsl_device *device = kgsl_driver.devp[i]; if (device) { - status = device->ftbl.device_setup_pt(device, pt); + status = device->ftbl->setup_pt(device, pt); if (status) goto error_pt; } @@ -599,28 +378,18 @@ static int kgsl_setup_pt(struct kgsl_pagetable *pt) while (i >= 0) { struct kgsl_device *device = kgsl_driver.devp[i]; if (device) - device->ftbl.device_cleanup_pt(device, pt); + device->ftbl->cleanup_pt(device, pt); i--; } return status; } -static int kgsl_cleanup_pt(struct kgsl_pagetable *pt) -{ - int i; - for (i = 0; i < KGSL_DEVICE_MAX; i++) { - struct kgsl_device *device = kgsl_driver.devp[i]; - if (device) - device->ftbl.device_cleanup_pt(device, pt); - } - return 0; -} - static struct kgsl_pagetable *kgsl_mmu_createpagetableobject( unsigned int name) { int status = 0; struct kgsl_pagetable *pagetable = NULL; + unsigned long flags; pagetable = kzalloc(sizeof(struct kgsl_pagetable), GFP_KERNEL); if (pagetable == NULL) { @@ -629,281 +398,132 @@ static struct kgsl_pagetable *kgsl_mmu_createpagetableobject( return NULL; } - pagetable->refcnt = 1; + kref_init(&pagetable->refcount); spin_lock_init(&pagetable->lock); - pagetable->tlb_flags = 0; pagetable->name = name; - pagetable->va_base = KGSL_PAGETABLE_BASE; - pagetable->va_range = CONFIG_MSM_KGSL_PAGE_TABLE_SIZE; - pagetable->last_superpte = 0; - pagetable->max_entries = KGSL_PAGETABLE_ENTRIES(pagetable->va_range); - - pagetable->tlbflushfilter.size = (pagetable->va_range / - (PAGE_SIZE * GSL_PT_SUPER_PTE * 8)) + 1; - pagetable->tlbflushfilter.base = (unsigned int *) - kzalloc(pagetable->tlbflushfilter.size, GFP_KERNEL); - if (!pagetable->tlbflushfilter.base) { - KGSL_CORE_ERR("kzalloc(%d) failed\n", - pagetable->tlbflushfilter.size); - goto err_alloc; - } - GSL_TLBFLUSH_FILTER_RESET(); + pagetable->max_entries = KGSL_PAGETABLE_ENTRIES( + CONFIG_MSM_KGSL_PAGE_TABLE_SIZE); pagetable->pool = gen_pool_create(PAGE_SHIFT, -1); if (pagetable->pool == NULL) { KGSL_CORE_ERR("gen_pool_create(%d) failed\n", PAGE_SHIFT); - goto err_flushfilter; + goto err_alloc; } - if (gen_pool_add(pagetable->pool, pagetable->va_base, - pagetable->va_range, -1)) { + if (gen_pool_add(pagetable->pool, KGSL_PAGETABLE_BASE, + CONFIG_MSM_KGSL_PAGE_TABLE_SIZE, -1)) { KGSL_CORE_ERR("gen_pool_add failed\n"); goto err_pool; } - pagetable->base.hostptr = kgsl_ptpool_alloc(&kgsl_driver.ptpool, - &pagetable->base.physaddr); + if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type) + pagetable->pt_ops = &gpummu_pt_ops; + else if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type) + pagetable->pt_ops = &iommu_pt_ops; - if (pagetable->base.hostptr == NULL) + pagetable->priv = pagetable->pt_ops->mmu_create_pagetable(); + if (!pagetable->priv) goto err_pool; - /* ptpool allocations are from coherent memory, so update the - device statistics acordingly */ - - KGSL_STATS_ADD(KGSL_PAGETABLE_SIZE, kgsl_driver.stats.coherent, - kgsl_driver.stats.coherent_max); - - pagetable->base.gpuaddr = pagetable->base.physaddr; - pagetable->base.size = KGSL_PAGETABLE_SIZE; - status = kgsl_setup_pt(pagetable); if (status) - goto err_free_sharedmem; + goto err_mmu_create; + spin_lock_irqsave(&kgsl_driver.ptlock, flags); list_add(&pagetable->list, &kgsl_driver.pagetable_list); + spin_unlock_irqrestore(&kgsl_driver.ptlock, flags); /* Create the sysfs entries */ pagetable_add_sysfs_objects(pagetable); return pagetable; -err_free_sharedmem: - kgsl_ptpool_free(&kgsl_driver.ptpool, &pagetable->base.hostptr); +err_mmu_create: + pagetable->pt_ops->mmu_destroy_pagetable(pagetable->priv); err_pool: gen_pool_destroy(pagetable->pool); -err_flushfilter: - kfree(pagetable->tlbflushfilter.base); err_alloc: kfree(pagetable); return NULL; } -static void kgsl_mmu_destroypagetable(struct kgsl_pagetable *pagetable) -{ - list_del(&pagetable->list); - - pagetable_remove_sysfs_objects(pagetable); - - kgsl_cleanup_pt(pagetable); - - kgsl_ptpool_free(&kgsl_driver.ptpool, pagetable->base.hostptr); - - kgsl_driver.stats.coherent -= KGSL_PAGETABLE_SIZE; - - if (pagetable->pool) { - gen_pool_destroy(pagetable->pool); - pagetable->pool = NULL; - } - - if (pagetable->tlbflushfilter.base) { - pagetable->tlbflushfilter.size = 0; - kfree(pagetable->tlbflushfilter.base); - pagetable->tlbflushfilter.base = NULL; - } - - kfree(pagetable); -} - struct kgsl_pagetable *kgsl_mmu_getpagetable(unsigned long name) { struct kgsl_pagetable *pt; - mutex_lock(&kgsl_driver.pt_mutex); + if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type) + return (void *)(-1); +#ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE + if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type) + name = KGSL_MMU_GLOBAL_PT; +#else + name = KGSL_MMU_GLOBAL_PT; +#endif pt = kgsl_get_pagetable(name); - if (pt) { - spin_lock(&pt->lock); - pt->refcnt++; - spin_unlock(&pt->lock); - goto done; - } + if (pt == NULL) + pt = kgsl_mmu_createpagetableobject(name); - pt = kgsl_mmu_createpagetableobject(name); - -done: - mutex_unlock(&kgsl_driver.pt_mutex); return pt; } void kgsl_mmu_putpagetable(struct kgsl_pagetable *pagetable) { - bool dead; - if (pagetable == NULL) - return; - - mutex_lock(&kgsl_driver.pt_mutex); - - spin_lock(&pagetable->lock); - dead = (--pagetable->refcnt) == 0; - spin_unlock(&pagetable->lock); - - if (dead) - kgsl_mmu_destroypagetable(pagetable); - - mutex_unlock(&kgsl_driver.pt_mutex); + kgsl_put_pagetable(pagetable); } +EXPORT_SYMBOL(kgsl_mmu_putpagetable); -int kgsl_mmu_setstate(struct kgsl_device *device, - struct kgsl_pagetable *pagetable) +void kgsl_setstate(struct kgsl_device *device, uint32_t flags) { - int status = 0; struct kgsl_mmu *mmu = &device->mmu; - - if (mmu->flags & KGSL_FLAGS_STARTED) { - /* page table not current, then setup mmu to use new - * specified page table - */ - if (mmu->hwpagetable != pagetable) { - mmu->hwpagetable = pagetable; - spin_lock(&mmu->hwpagetable->lock); - mmu->hwpagetable->tlb_flags &= ~(1<id); - spin_unlock(&mmu->hwpagetable->lock); - - /* call device specific set page table */ - status = kgsl_setstate(mmu->device, - KGSL_MMUFLAGS_TLBFLUSH | - KGSL_MMUFLAGS_PTUPDATE); - - } - } - - return status; + if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type) + return; + else if (device->ftbl->setstate) + device->ftbl->setstate(device, flags); + else if (mmu->mmu_ops->mmu_device_setstate) + mmu->mmu_ops->mmu_device_setstate(device, flags); } -EXPORT_SYMBOL(kgsl_mmu_setstate); +EXPORT_SYMBOL(kgsl_setstate); -int kgsl_mmu_init(struct kgsl_device *device) +void kgsl_mmu_device_setstate(struct kgsl_device *device, uint32_t flags) { - /* - * intialize device mmu - * - * call this with the global lock held - */ - int status = 0; struct kgsl_mmu *mmu = &device->mmu; - - mmu->device = device; - - /* make sure aligned to pagesize */ - BUG_ON(mmu->mpu_base & (PAGE_SIZE - 1)); - BUG_ON((mmu->mpu_base + mmu->mpu_range) & (PAGE_SIZE - 1)); - - /* sub-client MMU lookups require address translation */ - if ((mmu->config & ~0x1) > 0) { - /*make sure virtual address range is a multiple of 64Kb */ - BUG_ON(CONFIG_MSM_KGSL_PAGE_TABLE_SIZE & ((1 << 16) - 1)); - - /* allocate memory used for completing r/w operations that - * cannot be mapped by the MMU - */ - status = kgsl_allocate_contig(&mmu->dummyspace, 64); - if (!status) - kgsl_sharedmem_set(&mmu->dummyspace, 0, 0, - mmu->dummyspace.size); - } - - return status; + if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type) + return; + else if (mmu->mmu_ops->mmu_device_setstate) + mmu->mmu_ops->mmu_device_setstate(device, flags); } +EXPORT_SYMBOL(kgsl_mmu_device_setstate); -int kgsl_mmu_start(struct kgsl_device *device) +void kgsl_mh_start(struct kgsl_device *device) { - /* - * intialize device mmu - * - * call this with the global lock held - */ - int status; - struct kgsl_mmu *mmu = &device->mmu; - - if (mmu->flags & KGSL_FLAGS_STARTED) - return 0; - - /* MMU not enabled */ - if ((mmu->config & 0x1) == 0) - return 0; - - mmu->flags |= KGSL_FLAGS_STARTED; - - /* setup MMU and sub-client behavior */ - kgsl_regwrite(device, device->mmu.reg.config, mmu->config); - - /* enable axi interrupts */ - kgsl_regwrite(device, device->mmu.reg.interrupt_mask, - GSL_MMU_INT_MASK); - - /* idle device */ + struct kgsl_mh *mh = &device->mh; + /* force mmu off to for now*/ + kgsl_regwrite(device, MH_MMU_CONFIG, 0); kgsl_idle(device, KGSL_TIMEOUT_DEFAULT); /* define physical memory range accessible by the core */ - kgsl_regwrite(device, device->mmu.reg.mpu_base, mmu->mpu_base); - kgsl_regwrite(device, device->mmu.reg.mpu_end, - mmu->mpu_base + mmu->mpu_range); - - /* enable axi interrupts */ - kgsl_regwrite(device, device->mmu.reg.interrupt_mask, - GSL_MMU_INT_MASK | MH_INTERRUPT_MASK__MMU_PAGE_FAULT); - - /* sub-client MMU lookups require address translation */ - if ((mmu->config & ~0x1) > 0) { - - kgsl_sharedmem_set(&mmu->dummyspace, 0, 0, - mmu->dummyspace.size); - - /* TRAN_ERROR needs a 32 byte (32 byte aligned) chunk of memory - * to complete transactions in case of an MMU fault. Note that - * we'll leave the bottom 32 bytes of the dummyspace for other - * purposes (e.g. use it when dummy read cycles are needed - * for other blocks */ - kgsl_regwrite(device, device->mmu.reg.tran_error, - mmu->dummyspace.physaddr + 32); - - if (mmu->defaultpagetable == NULL) - mmu->defaultpagetable = - kgsl_mmu_getpagetable(KGSL_MMU_GLOBAL_PT); - mmu->hwpagetable = mmu->defaultpagetable; - - kgsl_regwrite(device, device->mmu.reg.pt_page, - mmu->hwpagetable->base.gpuaddr); - kgsl_regwrite(device, device->mmu.reg.va_range, - (mmu->hwpagetable->va_base | - (mmu->hwpagetable->va_range >> 16))); - status = kgsl_setstate(device, KGSL_MMUFLAGS_TLBFLUSH); - if (status) { - KGSL_MEM_ERR(device, "Failed to setstate TLBFLUSH\n"); - goto error; - } - } + kgsl_regwrite(device, MH_MMU_MPU_BASE, mh->mpu_base); + kgsl_regwrite(device, MH_MMU_MPU_END, + mh->mpu_base + mh->mpu_range); + kgsl_regwrite(device, MH_ARBITER_CONFIG, mh->mharb); - return 0; -error: - /* disable MMU */ - kgsl_regwrite(device, device->mmu.reg.interrupt_mask, 0); - kgsl_regwrite(device, device->mmu.reg.config, 0x00000000); - return status; + if (mh->mh_intf_cfg1 != 0) + kgsl_regwrite(device, MH_CLNT_INTF_CTRL_CONFIG1, + mh->mh_intf_cfg1); + + if (mh->mh_intf_cfg2 != 0) + kgsl_regwrite(device, MH_CLNT_INTF_CTRL_CONFIG2, + mh->mh_intf_cfg2); + + /* + * Interrupts are enabled on a per-device level when + * kgsl_pwrctrl_irq() is called + */ } -EXPORT_SYMBOL(kgsl_mmu_start); unsigned int kgsl_virtaddr_to_physaddr(void *virtaddr) { @@ -941,14 +561,12 @@ kgsl_mmu_map(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc, unsigned int protflags) { - int numpages; - unsigned int pte, ptefirst, ptelast, physaddr; - int flushtlb; - unsigned int offset = 0; - - BUG_ON(protflags & ~(GSL_PT_PAGE_RV | GSL_PT_PAGE_WV)); - BUG_ON(protflags == 0); + int ret; + if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) { + memdesc->gpuaddr = memdesc->physaddr; + return 0; + } memdesc->gpuaddr = gen_pool_alloc_aligned(pagetable->pool, memdesc->size, KGSL_MMU_ALIGN_SHIFT); @@ -960,36 +578,11 @@ kgsl_mmu_map(struct kgsl_pagetable *pagetable, return -ENOMEM; } - numpages = (memdesc->size >> PAGE_SHIFT); - - ptefirst = kgsl_pt_entry_get(pagetable, memdesc->gpuaddr); - ptelast = ptefirst + numpages; - - pte = ptefirst; - flushtlb = 0; - - /* tlb needs to be flushed when the first and last pte are not at - * superpte boundaries */ - if ((ptefirst & (GSL_PT_SUPER_PTE - 1)) != 0 || - ((ptelast + 1) & (GSL_PT_SUPER_PTE-1)) != 0) - flushtlb = 1; - spin_lock(&pagetable->lock); - for (pte = ptefirst; pte < ptelast; pte++, offset += PAGE_SIZE) { -#ifdef VERBOSE_DEBUG - /* check if PTE exists */ - uint32_t val = kgsl_pt_map_getaddr(pagetable, pte); - BUG_ON(val != 0 && val != GSL_PT_PAGE_DIRTY); -#endif - if ((pte & (GSL_PT_SUPER_PTE-1)) == 0) - if (GSL_TLBFLUSH_FILTER_ISDIRTY(pte / GSL_PT_SUPER_PTE)) - flushtlb = 1; - /* mark pte as in use */ - - physaddr = memdesc->ops->physaddr(memdesc, offset); - BUG_ON(physaddr == 0); - kgsl_pt_map_set(pagetable, pte, physaddr | protflags); - } + ret = pagetable->pt_ops->mmu_map(pagetable->priv, memdesc, protflags); + + if (ret) + goto err_free_gpuaddr; /* Keep track of the statistics for the sysfs files */ @@ -999,70 +592,40 @@ kgsl_mmu_map(struct kgsl_pagetable *pagetable, KGSL_STATS_ADD(memdesc->size, pagetable->stats.mapped, pagetable->stats.max_mapped); - /* Post all writes to the pagetable */ - wmb(); - - /* Invalidate tlb only if current page table used by GPU is the - * pagetable that we used to allocate */ - if (flushtlb) { - /*set all devices as needing flushing*/ - pagetable->tlb_flags = UINT_MAX; - GSL_TLBFLUSH_FILTER_RESET(); - } spin_unlock(&pagetable->lock); return 0; + +err_free_gpuaddr: + spin_unlock(&pagetable->lock); + gen_pool_free(pagetable->pool, memdesc->gpuaddr, memdesc->size); + memdesc->gpuaddr = 0; + return ret; } +EXPORT_SYMBOL(kgsl_mmu_map); int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc) { - unsigned int numpages; - unsigned int pte, ptefirst, ptelast, superpte; - unsigned int range = memdesc->size; - - /* All GPU addresses as assigned are page aligned, but some - functions purturb the gpuaddr with an offset, so apply the - mask here to make sure we have the right address */ - - unsigned int gpuaddr = memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK; - - if (range == 0 || gpuaddr == 0) + if (memdesc->size == 0 || memdesc->gpuaddr == 0) return 0; - numpages = (range >> PAGE_SHIFT); - if (range & (PAGE_SIZE - 1)) - numpages++; - - ptefirst = kgsl_pt_entry_get(pagetable, gpuaddr); - ptelast = ptefirst + numpages; - - spin_lock(&pagetable->lock); - superpte = ptefirst - (ptefirst & (GSL_PT_SUPER_PTE-1)); - GSL_TLBFLUSH_FILTER_SETDIRTY(superpte / GSL_PT_SUPER_PTE); - for (pte = ptefirst; pte < ptelast; pte++) { -#ifdef VERBOSE_DEBUG - /* check if PTE exists */ - BUG_ON(!kgsl_pt_map_getaddr(pagetable, pte)); -#endif - kgsl_pt_map_set(pagetable, pte, GSL_PT_PAGE_DIRTY); - superpte = pte - (pte & (GSL_PT_SUPER_PTE - 1)); - if (pte == superpte) - GSL_TLBFLUSH_FILTER_SETDIRTY(superpte / - GSL_PT_SUPER_PTE); + if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) { + memdesc->gpuaddr = 0; + return 0; } - + spin_lock(&pagetable->lock); + pagetable->pt_ops->mmu_unmap(pagetable->priv, memdesc); /* Remove the statistics */ pagetable->stats.entries--; - pagetable->stats.mapped -= range; - - /* Post all writes to the pagetable */ - wmb(); + pagetable->stats.mapped -= memdesc->size; spin_unlock(&pagetable->lock); - gen_pool_free(pagetable->pool, gpuaddr, range); + gen_pool_free(pagetable->pool, + memdesc->gpuaddr & KGSL_MMU_ALIGN_MASK, + memdesc->size); return 0; } @@ -1078,6 +641,9 @@ int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable, KGSL_CORE_ERR("invalid memdesc\n"); goto error; } + /* Not all global mappings are needed for all MMU types */ + if (!memdesc->size) + return 0; gpuaddr = memdesc->gpuaddr; @@ -1102,40 +668,92 @@ EXPORT_SYMBOL(kgsl_mmu_map_global); int kgsl_mmu_stop(struct kgsl_device *device) { - /* - * stop device mmu - * - * call this with the global lock held - */ struct kgsl_mmu *mmu = &device->mmu; - if (mmu->flags & KGSL_FLAGS_STARTED) { - /* disable mh interrupts */ - /* disable MMU */ - kgsl_regwrite(device, device->mmu.reg.interrupt_mask, 0); - kgsl_regwrite(device, device->mmu.reg.config, 0x00000000); - - mmu->flags &= ~KGSL_FLAGS_STARTED; - } - - return 0; + if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) + return 0; + else + return mmu->mmu_ops->mmu_stop(device); } EXPORT_SYMBOL(kgsl_mmu_stop); int kgsl_mmu_close(struct kgsl_device *device) { - /* - * close device mmu - * - * call this with the global lock held - */ struct kgsl_mmu *mmu = &device->mmu; - if (mmu->dummyspace.gpuaddr) - kgsl_sharedmem_free(&mmu->dummyspace); + if (kgsl_mmu_type == KGSL_MMU_TYPE_NONE) + return 0; + else + return mmu->mmu_ops->mmu_close(device); +} +EXPORT_SYMBOL(kgsl_mmu_close); - if (mmu->defaultpagetable) - kgsl_mmu_putpagetable(mmu->defaultpagetable); +int kgsl_mmu_pt_get_flags(struct kgsl_pagetable *pt, + enum kgsl_deviceid id) +{ + if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type) + return pt->pt_ops->mmu_pt_get_flags(pt, id); + else + return 0; +} +EXPORT_SYMBOL(kgsl_mmu_pt_get_flags); - return 0; +void kgsl_mmu_ptpool_destroy(void *ptpool) +{ + if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type) + kgsl_gpummu_ptpool_destroy(ptpool); + ptpool = 0; +} +EXPORT_SYMBOL(kgsl_mmu_ptpool_destroy); + +void *kgsl_mmu_ptpool_init(int ptsize, int entries) +{ + if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type) + return kgsl_gpummu_ptpool_init(ptsize, entries); + else + return (void *)(-1); +} +EXPORT_SYMBOL(kgsl_mmu_ptpool_init); + +int kgsl_mmu_enabled(void) +{ + if (KGSL_MMU_TYPE_NONE != kgsl_mmu_type) + return 1; + else + return 0; } +EXPORT_SYMBOL(kgsl_mmu_enabled); + +int kgsl_mmu_pt_equal(struct kgsl_pagetable *pt, + unsigned int pt_base) +{ + if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type) + return true; + else + return pt->pt_ops->mmu_pt_equal(pt, pt_base); +} +EXPORT_SYMBOL(kgsl_mmu_pt_equal); + +enum kgsl_mmutype kgsl_mmu_get_mmutype(void) +{ + return kgsl_mmu_type; +} +EXPORT_SYMBOL(kgsl_mmu_get_mmutype); + +void kgsl_mmu_set_mmutype(char *mmutype) +{ + kgsl_mmu_type = KGSL_MMU_TYPE_NONE; +#ifdef CONFIG_MSM_KGSL_GPUMMU + kgsl_mmu_type = KGSL_MMU_TYPE_GPU; +#elif defined(CONFIG_MSM_KGSL_IOMMU) + if (iommu_found()) + kgsl_mmu_type = KGSL_MMU_TYPE_IOMMU; +#endif + if (mmutype && !strncmp(mmutype, "gpummu", 6)) + kgsl_mmu_type = KGSL_MMU_TYPE_GPU; + if (iommu_found() && mmutype && !strncmp(mmutype, "iommu", 5)) + kgsl_mmu_type = KGSL_MMU_TYPE_IOMMU; + if (mmutype && !strncmp(mmutype, "nommu", 5)) + kgsl_mmu_type = KGSL_MMU_TYPE_NONE; +} +EXPORT_SYMBOL(kgsl_mmu_set_mmutype); diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h index 4a67ea6794541..4af073a7b2cf6 100644 --- a/drivers/gpu/msm/kgsl_mmu.h +++ b/drivers/gpu/msm/kgsl_mmu.h @@ -1,34 +1,20 @@ /* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 __KGSL_MMU_H #define __KGSL_MMU_H -#include "kgsl_sharedmem.h" + +#define KGSL_MMU_ALIGN_SHIFT 13 +#define KGSL_MMU_ALIGN_MASK (~((1 << KGSL_MMU_ALIGN_SHIFT) - 1)) /* Identifier for the global page table */ /* Per process page tables will probably pass in the thread group @@ -36,10 +22,52 @@ #define KGSL_MMU_GLOBAL_PT 0 +struct kgsl_device; + #define GSL_PT_SUPER_PTE 8 #define GSL_PT_PAGE_WV 0x00000001 #define GSL_PT_PAGE_RV 0x00000002 #define GSL_PT_PAGE_DIRTY 0x00000004 + +/* MMU registers - the register locations for all cores are the + same. The method for getting to those locations differs between + 2D and 3D, but the 2D and 3D register functions do that magic + for us */ + +#define MH_MMU_CONFIG 0x0040 +#define MH_MMU_VA_RANGE 0x0041 +#define MH_MMU_PT_BASE 0x0042 +#define MH_MMU_PAGE_FAULT 0x0043 +#define MH_MMU_TRAN_ERROR 0x0044 +#define MH_MMU_INVALIDATE 0x0045 +#define MH_MMU_MPU_BASE 0x0046 +#define MH_MMU_MPU_END 0x0047 + +#define MH_INTERRUPT_MASK 0x0A42 +#define MH_INTERRUPT_STATUS 0x0A43 +#define MH_INTERRUPT_CLEAR 0x0A44 +#define MH_AXI_ERROR 0x0A45 +#define MH_ARBITER_CONFIG 0x0A40 +#define MH_DEBUG_CTRL 0x0A4E +#define MH_DEBUG_DATA 0x0A4F +#define MH_AXI_HALT_CONTROL 0x0A50 +#define MH_CLNT_INTF_CTRL_CONFIG1 0x0A54 +#define MH_CLNT_INTF_CTRL_CONFIG2 0x0A55 + +/* MH_MMU_CONFIG bit definitions */ + +#define MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT 0x00000004 +#define MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT 0x00000006 +#define MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT 0x00000008 +#define MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT 0x0000000a +#define MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT 0x0000000c +#define MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT 0x0000000e +#define MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT 0x00000010 +#define MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT 0x00000012 +#define MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT 0x00000014 +#define MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT 0x00000016 +#define MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT 0x00000018 + /* MMU Flags */ #define KGSL_MMUFLAGS_TLBFLUSH 0x10000000 #define KGSL_MMUFLAGS_PTUPDATE 0x20000000 @@ -48,43 +76,30 @@ #define MH_INTERRUPT_MASK__AXI_WRITE_ERROR 0x00000002L #define MH_INTERRUPT_MASK__MMU_PAGE_FAULT 0x00000004L -/* Macros to manage TLB flushing */ -#define GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS (sizeof(unsigned char) * 8) -#define GSL_TLBFLUSH_FILTER_GET(superpte) \ - (*((unsigned char *) \ - (((unsigned int)pagetable->tlbflushfilter.base) \ - + (superpte / GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS)))) -#define GSL_TLBFLUSH_FILTER_SETDIRTY(superpte) \ - (GSL_TLBFLUSH_FILTER_GET((superpte)) |= 1 << \ - (superpte % GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS)) -#define GSL_TLBFLUSH_FILTER_ISDIRTY(superpte) \ - (GSL_TLBFLUSH_FILTER_GET((superpte)) & \ - (1 << (superpte % GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS))) -#define GSL_TLBFLUSH_FILTER_RESET() memset(pagetable->tlbflushfilter.base,\ - 0, pagetable->tlbflushfilter.size) - - -struct kgsl_device; +#ifdef CONFIG_MSM_KGSL_MMU +#define KGSL_MMU_INT_MASK \ + (MH_INTERRUPT_MASK__AXI_READ_ERROR | \ + MH_INTERRUPT_MASK__AXI_WRITE_ERROR | \ + MH_INTERRUPT_MASK__MMU_PAGE_FAULT) +#else +#define KGSL_MMU_INT_MASK \ + (MH_INTERRUPT_MASK__AXI_READ_ERROR | \ + MH_INTERRUPT_MASK__AXI_WRITE_ERROR) +#endif -struct kgsl_tlbflushfilter { - unsigned int *base; - unsigned int size; +enum kgsl_mmutype { + KGSL_MMU_TYPE_GPU = 0, + KGSL_MMU_TYPE_IOMMU, + KGSL_MMU_TYPE_NONE }; struct kgsl_pagetable { spinlock_t lock; - unsigned int refcnt; - struct kgsl_memdesc base; - uint32_t va_base; - unsigned int va_range; - unsigned int last_superpte; + struct kref refcount; unsigned int max_entries; struct gen_pool *pool; struct list_head list; unsigned int name; - /* Maintain filter to manage tlb flushing */ - struct kgsl_tlbflushfilter tlbflushfilter; - unsigned int tlb_flags; struct kobject *kobj; struct { @@ -93,22 +108,36 @@ struct kgsl_pagetable { unsigned int max_mapped; unsigned int max_entries; } stats; + const struct kgsl_mmu_pt_ops *pt_ops; + void *priv; }; -struct kgsl_mmu_reg { +struct kgsl_mmu_ops { + int (*mmu_init) (struct kgsl_device *device); + int (*mmu_close) (struct kgsl_device *device); + int (*mmu_start) (struct kgsl_device *device); + int (*mmu_stop) (struct kgsl_device *device); + void (*mmu_setstate) (struct kgsl_device *device, + struct kgsl_pagetable *pagetable); + void (*mmu_device_setstate) (struct kgsl_device *device, + uint32_t flags); + void (*mmu_pagefault) (struct kgsl_device *device); + unsigned int (*mmu_get_current_ptbase) + (struct kgsl_device *device); +}; - uint32_t config; - uint32_t mpu_base; - uint32_t mpu_end; - uint32_t va_range; - uint32_t pt_page; - uint32_t page_fault; - uint32_t tran_error; - uint32_t invalidate; - uint32_t interrupt_mask; - uint32_t interrupt_status; - uint32_t interrupt_clear; - uint32_t axi_error; +struct kgsl_mmu_pt_ops { + int (*mmu_map) (void *mmu_pt, + struct kgsl_memdesc *memdesc, + unsigned int protflags); + int (*mmu_unmap) (void *mmu_pt, + struct kgsl_memdesc *memdesc); + void *(*mmu_create_pagetable) (void); + void (*mmu_destroy_pagetable) (void *pt); + int (*mmu_pt_equal) (struct kgsl_pagetable *pt, + unsigned int pt_base); + unsigned int (*mmu_pt_get_flags) (struct kgsl_pagetable *pt, + enum kgsl_deviceid id); }; struct kgsl_mmu { @@ -116,46 +145,27 @@ struct kgsl_mmu { uint32_t flags; struct kgsl_device *device; unsigned int config; - uint32_t mpu_base; - int mpu_range; - struct kgsl_memdesc dummyspace; - struct kgsl_mmu_reg reg; + struct kgsl_memdesc setstate_memory; /* current page table object being used by device mmu */ struct kgsl_pagetable *defaultpagetable; struct kgsl_pagetable *hwpagetable; + const struct kgsl_mmu_ops *mmu_ops; + void *priv; }; -struct kgsl_ptpool_chunk { - size_t size; - unsigned int count; - int dynamic; +#include "kgsl_gpummu.h" - void *data; - unsigned int phys; - - unsigned long *bitmap; - struct list_head list; -}; - -struct kgsl_ptpool { - size_t ptsize; - struct mutex lock; - struct list_head list; - int entries; - int static_entries; - int chunks; -}; +extern struct kgsl_mmu_ops iommu_ops; +extern struct kgsl_mmu_pt_ops iommu_pt_ops; struct kgsl_pagetable *kgsl_mmu_getpagetable(unsigned long name); - -#ifdef CONFIG_MSM_KGSL_MMU - +void kgsl_mmu_putpagetable(struct kgsl_pagetable *pagetable); +void kgsl_mh_start(struct kgsl_device *device); +void kgsl_mh_intrcallback(struct kgsl_device *device); int kgsl_mmu_init(struct kgsl_device *device); int kgsl_mmu_start(struct kgsl_device *device); int kgsl_mmu_stop(struct kgsl_device *device); int kgsl_mmu_close(struct kgsl_device *device); -int kgsl_mmu_setstate(struct kgsl_device *device, - struct kgsl_pagetable *pagetable); int kgsl_mmu_map(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc, unsigned int protflags); @@ -163,105 +173,21 @@ int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc, unsigned int protflags); int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc); -void kgsl_ptpool_destroy(struct kgsl_ptpool *pool); -int kgsl_ptpool_init(struct kgsl_ptpool *pool, int ptsize, int entries); -void kgsl_mh_intrcallback(struct kgsl_device *device); -void kgsl_mmu_putpagetable(struct kgsl_pagetable *pagetable); unsigned int kgsl_virtaddr_to_physaddr(void *virtaddr); - -static inline int kgsl_mmu_enabled(void) -{ - return 1; -} - -#else - -static inline int kgsl_mmu_enabled(void) -{ - return 0; -} - -static inline int kgsl_mmu_init(struct kgsl_device *device) -{ - return 0; -} - -static inline int kgsl_mmu_start(struct kgsl_device *device) -{ - return 0; -} - -static inline int kgsl_mmu_stop(struct kgsl_device *device) -{ - return 0; -} - -static inline int kgsl_mmu_close(struct kgsl_device *device) -{ - return 0; -} - -static inline int kgsl_mmu_setstate(struct kgsl_device *device, - struct kgsl_pagetable *pagetable) -{ - return 0; -} - -static inline int kgsl_mmu_map(struct kgsl_pagetable *pagetable, - struct kgsl_memdesc *memdesc, - unsigned int protflags) -{ - memdesc->gpuaddr = memdesc->physaddr; - return 0; -} - -static inline int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, - struct kgsl_memdesc *memdesc) -{ - return 0; -} - -static inline int kgsl_ptpool_init(struct kgsl_ptpool *pool, int ptsize, - int entries) -{ - return 0; -} - -static inline int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable, - struct kgsl_memdesc *memdesc, unsigned int protflags) -{ - /* gpuaddr is the same that gets passed in */ - return 0; -} - -static inline void kgsl_ptpool_destroy(struct kgsl_ptpool *pool) { } - -static inline void kgsl_mh_intrcallback(struct kgsl_device *device) { } - -static inline void kgsl_mmu_putpagetable(struct kgsl_pagetable *pagetable) { } - -static inline unsigned int kgsl_virtaddr_to_physaddr(void *virtaddr) -{ - return 0; -} - -#endif - -static inline unsigned int kgsl_pt_get_flags(struct kgsl_pagetable *pt, - enum kgsl_deviceid id) -{ - unsigned int result = 0; - - if (pt == NULL) - return 0; - - spin_lock(&pt->lock); - if (pt->tlb_flags && (1<tlb_flags &= ~(1<lock); - return result; -} - +void kgsl_setstate(struct kgsl_device *device, uint32_t flags); +void kgsl_mmu_device_setstate(struct kgsl_device *device, uint32_t flags); +void kgsl_mmu_setstate(struct kgsl_device *device, + struct kgsl_pagetable *pt); +int kgsl_mmu_get_ptname_from_ptbase(unsigned int pt_base); +int kgsl_mmu_pt_get_flags(struct kgsl_pagetable *pt, + enum kgsl_deviceid id); + +void kgsl_mmu_ptpool_destroy(void *ptpool); +void *kgsl_mmu_ptpool_init(int ptsize, int entries); +int kgsl_mmu_enabled(void); +int kgsl_mmu_pt_equal(struct kgsl_pagetable *pt, + unsigned int pt_base); +void kgsl_mmu_set_mmutype(char *mmutype); +unsigned int kgsl_mmu_get_current_ptbase(struct kgsl_device *device); +enum kgsl_mmutype kgsl_mmu_get_mmutype(void); #endif /* __KGSL_MMU_H */ diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 5840dc00c60fa..115f073254243 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -15,49 +15,12 @@ #include #include "kgsl.h" +#include "kgsl_pwrscale.h" +#include "kgsl_device.h" -#define SWITCH_OFF 200 -#define TZ_UPDATE_ID 0x01404000 -#define TZ_RESET_ID 0x01403000 - -#ifdef CONFIG_MSM_SECURE_IO -/* Trap into the TrustZone, and call funcs there. */ -static int __secure_tz_entry(u32 cmd, u32 val) -{ - register u32 r0 asm("r0") = cmd; - register u32 r1 asm("r1") = 0x0; - register u32 r2 asm("r2") = val; - - __iowmb(); - asm( - __asmeq("%0", "r0") - __asmeq("%1", "r0") - __asmeq("%2", "r1") - __asmeq("%3", "r2") - "smc #0 @ switch to secure world\n" - : "=r" (r0) - : "r" (r0), "r" (r1), "r" (r2) - ); - return r0; -} -#else -static int __secure_tz_entry(u32 cmd, u32 val) -{ - return 0; -} -#endif /* CONFIG_MSM_SECURE_IO */ - -/* Returns the requested update to our power level. * - * Either up/down (-1/1) a level, or stay the same (0). */ -static inline int kgsl_pwrctrl_tz_update(u32 idle) -{ - return __secure_tz_entry(TZ_UPDATE_ID, idle); -} - -static inline void kgsl_pwrctrl_tz_reset(void) -{ - __secure_tz_entry(TZ_RESET_ID, 0); -} +#define GPU_SWFI_LATENCY 3 +#define UPDATE_BUSY_VAL 1000000 +#define UPDATE_BUSY 50 void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, unsigned int new_level) @@ -67,16 +30,22 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, new_level >= pwr->thermal_pwrlevel && new_level != pwr->active_pwrlevel) { pwr->active_pwrlevel = new_level; - if (test_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->power_flags)) + if ((test_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->power_flags)) || + (device->state == KGSL_STATE_NAP)) clk_set_rate(pwr->grp_clks[0], pwr->pwrlevels[pwr->active_pwrlevel]. gpu_freq); - if (test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)) + if (test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)) { if (pwr->pcl) msm_bus_scale_client_update_request(pwr->pcl, pwr->pwrlevels[pwr->active_pwrlevel]. bus_freq); - KGSL_PWR_WARN(device, "pwr level changed to %d\n", + else if (pwr->ebi1_clk) + clk_set_rate(pwr->ebi1_clk, + pwr->pwrlevels[pwr->active_pwrlevel]. + bus_freq); + } + KGSL_PWR_WARN(device, "kgsl pwr level changed to %d\n", pwr->active_pwrlevel); } } @@ -257,41 +226,20 @@ static int kgsl_pwrctrl_idle_timer_show(struct device *dev, device->pwrctrl.interval_timeout); } -static int kgsl_pwrctrl_scaling_governor_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - char temp[20]; - struct kgsl_device *device = kgsl_device_from_dev(dev); - struct kgsl_pwrctrl *pwr = &device->pwrctrl; - unsigned int reset = pwr->idle_pass; - - snprintf(temp, sizeof(temp), "%.*s", - (int)min(count, sizeof(temp) - 1), buf); - if (strncmp(temp, "ondemand", 8) == 0) - reset = 1; - else if (strncmp(temp, "performance", 11) == 0) - reset = 0; - - mutex_lock(&device->mutex); - pwr->idle_pass = reset; - if (pwr->idle_pass == 0) - kgsl_pwrctrl_pwrlevel_change(device, pwr->thermal_pwrlevel); - mutex_unlock(&device->mutex); - - return count; -} - -static int kgsl_pwrctrl_scaling_governor_show(struct device *dev, +static int kgsl_pwrctrl_gpubusy_show(struct device *dev, struct device_attribute *attr, char *buf) { + int ret; struct kgsl_device *device = kgsl_device_from_dev(dev); - struct kgsl_pwrctrl *pwr = &device->pwrctrl; - if (pwr->idle_pass) - return snprintf(buf, 10, "ondemand\n"); - else - return snprintf(buf, 13, "performance\n"); + struct kgsl_busy *b = &device->pwrctrl.busy; + ret = snprintf(buf, 17, "%7d %7d\n", + b->on_time_old, b->time_old); + if (!test_bit(KGSL_PWRFLAGS_AXI_ON, &device->pwrctrl.power_flags)) { + b->on_time_old = 0; + b->time_old = 0; + } + return ret; } DEVICE_ATTR(gpuclk, 0644, kgsl_pwrctrl_gpuclk_show, kgsl_pwrctrl_gpuclk_store); @@ -300,15 +248,15 @@ DEVICE_ATTR(max_gpuclk, 0644, kgsl_pwrctrl_max_gpuclk_show, DEVICE_ATTR(pwrnap, 0644, kgsl_pwrctrl_pwrnap_show, kgsl_pwrctrl_pwrnap_store); DEVICE_ATTR(idle_timer, 0644, kgsl_pwrctrl_idle_timer_show, kgsl_pwrctrl_idle_timer_store); -DEVICE_ATTR(scaling_governor, 0644, kgsl_pwrctrl_scaling_governor_show, - kgsl_pwrctrl_scaling_governor_store); +DEVICE_ATTR(gpubusy, 0644, kgsl_pwrctrl_gpubusy_show, + NULL); static const struct device_attribute *pwrctrl_attr_list[] = { &dev_attr_gpuclk, &dev_attr_max_gpuclk, &dev_attr_pwrnap, &dev_attr_idle_timer, - &dev_attr_scaling_governor, + &dev_attr_gpubusy, NULL }; @@ -322,28 +270,29 @@ void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device) kgsl_remove_device_sysfs_files(device->dev, pwrctrl_attr_list); } -static void kgsl_pwrctrl_idle_calc(struct kgsl_device *device) -{ - int val; - struct kgsl_pwrctrl *pwr = &device->pwrctrl; - struct kgsl_power_stats stats; - - device->ftbl.device_power_stats(device, &stats); - - if (stats.total_time == 0) - return; - - /* If the GPU has stayed in turbo mode for a while, * - * stop writing out values. */ - if (pwr->active_pwrlevel) - pwr->no_switch_cnt = 0; - else if (pwr->no_switch_cnt > SWITCH_OFF) - return; - pwr->no_switch_cnt++; - val = kgsl_pwrctrl_tz_update(stats.total_time - stats.busy_time); - if (val) - kgsl_pwrctrl_pwrlevel_change(device, - pwr->active_pwrlevel + val); +/* Track the amount of time the gpu is on vs the total system time. * + * Regularly update the percentage of busy time displayed by sysfs. */ +static void kgsl_pwrctrl_busy_time(struct kgsl_device *device, bool on_time) +{ + struct kgsl_busy *b = &device->pwrctrl.busy; + int elapsed; + if (b->start.tv_sec == 0) + do_gettimeofday(&(b->start)); + do_gettimeofday(&(b->stop)); + elapsed = (b->stop.tv_sec - b->start.tv_sec) * 1000000; + elapsed += b->stop.tv_usec - b->start.tv_usec; + b->time += elapsed; + if (on_time) + b->on_time += elapsed; + /* Update the output regularly and reset the counters. */ + if ((b->time > UPDATE_BUSY_VAL) || + !test_bit(KGSL_PWRFLAGS_AXI_ON, &device->pwrctrl.power_flags)) { + b->on_time_old = b->on_time; + b->time_old = b->time; + b->on_time = 0; + b->time = 0; + } + do_gettimeofday(&(b->start)); } void kgsl_pwrctrl_clk(struct kgsl_device *device, int state) @@ -363,6 +312,7 @@ void kgsl_pwrctrl_clk(struct kgsl_device *device, int state) clk_set_rate(pwr->grp_clks[0], pwr->pwrlevels[pwr->num_pwrlevels - 1]. gpu_freq); + kgsl_pwrctrl_busy_time(device, true); } } else if (state == KGSL_PWRFLAGS_ON) { if (!test_and_set_bit(KGSL_PWRFLAGS_CLK_ON, @@ -381,6 +331,7 @@ void kgsl_pwrctrl_clk(struct kgsl_device *device, int state) for (i = KGSL_MAX_CLKS - 1; i > 0; i--) if (pwr->grp_clks[i]) clk_enable(pwr->grp_clks[i]); + kgsl_pwrctrl_busy_time(device, false); } } } @@ -458,13 +409,18 @@ void kgsl_pwrctrl_irq(struct kgsl_device *device, int state) KGSL_PWR_INFO(device, "irq on, device %d\n", device->id); enable_irq(pwr->interrupt_num); + device->ftbl->irqctrl(device, 1); } } else if (state == KGSL_PWRFLAGS_OFF) { if (test_and_clear_bit(KGSL_PWRFLAGS_IRQ_ON, &pwr->power_flags)) { KGSL_PWR_INFO(device, "irq off, device %d\n", device->id); - disable_irq(pwr->interrupt_num); + device->ftbl->irqctrl(device, 0); + if (in_interrupt()) + disable_irq_nosync(pwr->interrupt_num); + else + disable_irq(pwr->interrupt_num); } } } @@ -531,11 +487,8 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) pwr->power_flags = 0; pwr->nap_allowed = pdata_pwr->nap_allowed; -/* drewis: below was removed at some point before i cherry-picked the below commit */ - pwr->idle_pass = pdata_pwr->idle_pass; -/*dc14311... msm: kgsl: Replace internal_power_rail API calls with regulator APIs*/ pwr->interval_timeout = pdata_pwr->idle_timeout; - pwr->ebi1_clk = clk_get(NULL, "ebi1_kgsl_clk"); + pwr->ebi1_clk = clk_get(&pdev->dev, "bus_clk"); if (IS_ERR(pwr->ebi1_clk)) pwr->ebi1_clk = NULL; else @@ -566,6 +519,8 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) result = -EINVAL; goto done; } + + register_early_suspend(&device->display_off); return result; clk_err: @@ -584,6 +539,8 @@ void kgsl_pwrctrl_close(struct kgsl_device *device) KGSL_PWR_INFO(device, "close device %d\n", device->id); + unregister_early_suspend(&device->display_off); + if (pwr->interrupt_num > 0) { if (pwr->have_irq) { free_irq(pwr->interrupt_num, NULL); @@ -620,15 +577,22 @@ void kgsl_idle_check(struct work_struct *work) idle_check_ws); mutex_lock(&device->mutex); - if ((device->pwrctrl.idle_pass) && - (device->requested_state != KGSL_STATE_SLEEP)) - kgsl_pwrctrl_idle_calc(device); + if (device->requested_state != KGSL_STATE_SLEEP) + kgsl_pwrscale_idle(device); if (device->state & (KGSL_STATE_ACTIVE | KGSL_STATE_NAP)) { - if (kgsl_pwrctrl_sleep(device) != 0) + if (kgsl_pwrctrl_sleep(device) != 0) { mod_timer(&device->idle_timer, jiffies + device->pwrctrl.interval_timeout); + /* If the GPU has been too busy to sleep, make sure * + * that is acurately reflected in the % busy numbers. */ + device->pwrctrl.busy.no_nap_cnt++; + if (device->pwrctrl.busy.no_nap_cnt > UPDATE_BUSY) { + kgsl_pwrctrl_busy_time(device, true); + device->pwrctrl.busy.no_nap_cnt = 0; + } + } } else if (device->state & (KGSL_STATE_HUNG | KGSL_STATE_DUMP_AND_RECOVER)) { device->requested_state = KGSL_STATE_NONE; @@ -682,11 +646,11 @@ int kgsl_pwrctrl_sleep(struct kgsl_device *device) /* Work through the legal state transitions */ if (device->requested_state == KGSL_STATE_NAP) { - if (device->ftbl.device_isidle(device)) + if (device->ftbl->isidle(device)) goto nap; } else if (device->requested_state == KGSL_STATE_SLEEP) { if (device->state == KGSL_STATE_NAP || - device->ftbl.device_isidle(device)) + device->ftbl->isidle(device)) goto sleep; } @@ -700,9 +664,11 @@ int kgsl_pwrctrl_sleep(struct kgsl_device *device) clk_set_rate(pwr->grp_clks[0], pwr->pwrlevels[pwr->num_pwrlevels - 1]. gpu_freq); - device->pwrctrl.no_switch_cnt = 0; + kgsl_pwrctrl_busy_time(device, false); + pwr->busy.start.tv_sec = 0; device->pwrctrl.time = 0; - kgsl_pwrctrl_tz_reset(); + + kgsl_pwrscale_sleep(device); goto clk_off; nap: @@ -713,6 +679,8 @@ int kgsl_pwrctrl_sleep(struct kgsl_device *device) device->state = device->requested_state; device->requested_state = KGSL_STATE_NONE; wake_unlock(&device->idle_wakelock); + pm_qos_update_request(&device->pm_qos_req_dma, + PM_QOS_DEFAULT_VALUE); KGSL_PWR_WARN(device, "state -> NAP/SLEEP(%d), device %d\n", device->state, device->id); @@ -728,11 +696,10 @@ void kgsl_pwrctrl_wake(struct kgsl_device *device) return; if (device->state != KGSL_STATE_NAP) { - if (device->pwrctrl.idle_pass) - kgsl_pwrctrl_pwrlevel_change(device, - device->pwrctrl.thermal_pwrlevel); kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON); + kgsl_pwrscale_wake(device); } + /* Turn on the core clocks */ kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON); @@ -746,6 +713,7 @@ void kgsl_pwrctrl_wake(struct kgsl_device *device) jiffies + device->pwrctrl.interval_timeout); wake_lock(&device->idle_wakelock); + pm_qos_update_request(&device->pm_qos_req_dma, GPU_SWFI_LATENCY); KGSL_PWR_INFO(device, "wake return for device %d\n", device->id); } EXPORT_SYMBOL(kgsl_pwrctrl_wake); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 108cc3093955a..794a8955a3f67 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -1,29 +1,13 @@ /* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 __KGSL_PWRCTRL_H @@ -40,11 +24,24 @@ #define KGSL_PWRFLAGS_ON 1 #define KGSL_PWRFLAGS_OFF 0 -#define KGSL_DEFAULT_PWRLEVEL 1 +#define KGSL_PWRLEVEL_TURBO 0 +#define KGSL_PWRLEVEL_NOMINAL 1 +#define KGSL_PWRLEVEL_LOW_OFFSET 2 + #define KGSL_MAX_CLKS 5 struct platform_device; +struct kgsl_busy { + struct timeval start; + struct timeval stop; + int on_time; + int time; + int on_time_old; + int time_old; + unsigned int no_nap_cnt; +}; + struct kgsl_pwrctrl { int interrupt_num; int have_irq; @@ -59,13 +56,11 @@ struct kgsl_pwrctrl { struct regulator *gpu_reg; uint32_t pcl; unsigned int nap_allowed; - struct adreno_context *suspended_ctxt; const char *regulator_name; const char *irq_name; const char *src_clk_name; s64 time; - unsigned int no_switch_cnt; - unsigned int idle_pass; + struct kgsl_busy busy; }; void kgsl_pwrctrl_clk(struct kgsl_device *device, int state); diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c index f2250283655ef..fa7203deb0bd6 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.c +++ b/drivers/gpu/msm/kgsl_pwrscale.c @@ -15,6 +15,7 @@ #include "kgsl.h" #include "kgsl_pwrscale.h" +#include "kgsl_device.h" struct kgsl_pwrscale_attribute { struct attribute attr; @@ -38,6 +39,12 @@ __ATTR(_name, _mode, _show, _store) /* Master list of available policies */ static struct kgsl_pwrscale_policy *kgsl_pwrscale_policies[] = { +#ifdef CONFIG_MSM_SCM + &kgsl_pwrscale_policy_tz, +#endif +#ifdef CONFIG_MSM_SLEEP_STATS + &kgsl_pwrscale_policy_idlestats, +#endif NULL }; @@ -141,9 +148,6 @@ static ssize_t policy_sysfs_store(struct kobject *kobj, static void policy_sysfs_release(struct kobject *kobj) { - struct kgsl_pwrscale *pwrscale = to_pwrscale(kobj); - - complete(&pwrscale->kobj_unregister); } static ssize_t pwrscale_sysfs_show(struct kobject *kobj, @@ -220,13 +224,17 @@ EXPORT_SYMBOL(kgsl_pwrscale_wake); void kgsl_pwrscale_busy(struct kgsl_device *device) { if (device->pwrscale.policy && device->pwrscale.policy->busy) - device->pwrscale.policy->busy(device, &device->pwrscale); + if (!device->pwrscale.gpu_busy) + device->pwrscale.policy->busy(device, + &device->pwrscale); + device->pwrscale.gpu_busy = 1; } void kgsl_pwrscale_idle(struct kgsl_device *device) { if (device->pwrscale.policy && device->pwrscale.policy->idle) device->pwrscale.policy->idle(device, &device->pwrscale); + device->pwrscale.gpu_busy = 0; } EXPORT_SYMBOL(kgsl_pwrscale_idle); @@ -236,12 +244,8 @@ int kgsl_pwrscale_policy_add_files(struct kgsl_device *device, { int ret; - init_completion(&pwrscale->kobj_unregister); - - ret = kobject_init_and_add(&pwrscale->kobj, - &ktype_pwrscale_policy, - &device->pwrscale_kobj, - "%s", pwrscale->policy->name); + ret = kobject_add(&pwrscale->kobj, &device->pwrscale_kobj, + "%s", pwrscale->policy->name); if (ret) return ret; @@ -249,8 +253,8 @@ int kgsl_pwrscale_policy_add_files(struct kgsl_device *device, ret = sysfs_create_group(&pwrscale->kobj, attr_group); if (ret) { + kobject_del(&pwrscale->kobj); kobject_put(&pwrscale->kobj); - wait_for_completion(&pwrscale->kobj_unregister); } return ret; @@ -261,16 +265,22 @@ void kgsl_pwrscale_policy_remove_files(struct kgsl_device *device, struct attribute_group *attr_group) { sysfs_remove_group(&pwrscale->kobj, attr_group); + kobject_del(&pwrscale->kobj); kobject_put(&pwrscale->kobj); - wait_for_completion(&pwrscale->kobj_unregister); + pwrscale->kobj.state_initialized = 0; } -void kgsl_pwrscale_detach_policy(struct kgsl_device *device) +static void _kgsl_pwrscale_detach_policy(struct kgsl_device *device) { - mutex_lock(&device->mutex); if (device->pwrscale.policy != NULL) device->pwrscale.policy->close(device, &device->pwrscale); device->pwrscale.policy = NULL; +} + +void kgsl_pwrscale_detach_policy(struct kgsl_device *device) +{ + mutex_lock(&device->mutex); + _kgsl_pwrscale_detach_policy(device); mutex_unlock(&device->mutex); } EXPORT_SYMBOL(kgsl_pwrscale_detach_policy); @@ -278,16 +288,25 @@ EXPORT_SYMBOL(kgsl_pwrscale_detach_policy); int kgsl_pwrscale_attach_policy(struct kgsl_device *device, struct kgsl_pwrscale_policy *policy) { - int ret; + int ret = 0; + + mutex_lock(&device->mutex); + + if (device->pwrscale.policy == policy) + goto done; if (device->pwrscale.policy != NULL) - kgsl_pwrscale_detach_policy(device); + _kgsl_pwrscale_detach_policy(device); - mutex_lock(&device->mutex); device->pwrscale.policy = policy; - ret = device->pwrscale.policy->init(device, &device->pwrscale); - if (ret) - device->pwrscale.policy = NULL; + + if (policy) { + ret = device->pwrscale.policy->init(device, &device->pwrscale); + if (ret) + device->pwrscale.policy = NULL; + } + +done: mutex_unlock(&device->mutex); return ret; @@ -296,8 +315,16 @@ EXPORT_SYMBOL(kgsl_pwrscale_attach_policy); int kgsl_pwrscale_init(struct kgsl_device *device) { - return kobject_init_and_add(&device->pwrscale_kobj, &ktype_pwrscale, - &device->dev->kobj, "pwrscale"); + int ret; + + ret = kobject_init_and_add(&device->pwrscale_kobj, &ktype_pwrscale, + &device->dev->kobj, "pwrscale"); + + if (ret) + return ret; + + kobject_init(&device->pwrscale.kobj, &ktype_pwrscale_policy); + return ret; } EXPORT_SYMBOL(kgsl_pwrscale_init); diff --git a/drivers/gpu/msm/kgsl_pwrscale.h b/drivers/gpu/msm/kgsl_pwrscale.h index f05adbd5411f0..b4f831ee1bd2f 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.h +++ b/drivers/gpu/msm/kgsl_pwrscale.h @@ -1,29 +1,13 @@ /* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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. * */ @@ -51,8 +35,8 @@ struct kgsl_pwrscale_policy { struct kgsl_pwrscale { struct kgsl_pwrscale_policy *policy; struct kobject kobj; - struct completion kobj_unregister; void *priv; + int gpu_busy; }; struct kgsl_pwrscale_policy_attribute { @@ -68,8 +52,12 @@ struct kgsl_pwrscale_policy_attribute { struct kgsl_pwrscale_policy_attribute policy_attr_##_name = \ __ATTR(_name, _mode, _show, _store) +extern struct kgsl_pwrscale_policy kgsl_pwrscale_policy_tz; +extern struct kgsl_pwrscale_policy kgsl_pwrscale_policy_idlestats; + int kgsl_pwrscale_init(struct kgsl_device *device); void kgsl_pwrscale_close(struct kgsl_device *device); + int kgsl_pwrscale_attach_policy(struct kgsl_device *device, struct kgsl_pwrscale_policy *policy); void kgsl_pwrscale_detach_policy(struct kgsl_device *device); diff --git a/drivers/gpu/msm/kgsl_pwrscale_idlestats.c b/drivers/gpu/msm/kgsl_pwrscale_idlestats.c new file mode 100644 index 0000000000000..c4dcb22f9be42 --- /dev/null +++ b/drivers/gpu/msm/kgsl_pwrscale_idlestats.c @@ -0,0 +1,134 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +#include "kgsl.h" +#include "kgsl_pwrscale.h" +#include "kgsl_device.h" + +struct idlestats_priv { + char name[32]; + struct msm_idle_stats_device idledev; + struct kgsl_device *device; + struct msm_idle_pulse pulse; +}; + +static void idlestats_get_sample(struct msm_idle_stats_device *idledev, + struct msm_idle_pulse *pulse) +{ + struct kgsl_power_stats stats; + struct idlestats_priv *priv = container_of(idledev, + struct idlestats_priv, idledev); + struct kgsl_device *device = priv->device; + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + + mutex_lock(&device->mutex); + /* If the GPU is asleep, don't wake it up - assume that we + are idle */ + + if (!(device->state & (KGSL_STATE_SLEEP | KGSL_STATE_NAP))) { + device->ftbl->power_stats(device, &stats); + pulse->busy_start_time = pwr->time - stats.busy_time; + pulse->busy_interval = stats.busy_time; + } else { + pulse->busy_start_time = pwr->time; + pulse->busy_interval = 0; + } + pulse->wait_interval = 0; + mutex_unlock(&device->mutex); +} + +static void idlestats_busy(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale) +{ + struct idlestats_priv *priv = pwrscale->priv; + if (priv->pulse.busy_start_time != 0) + msm_idle_stats_idle_end(&priv->idledev, &priv->pulse); + priv->pulse.busy_start_time = ktime_to_us(ktime_get()); +} + +static void idlestats_idle(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale) +{ + struct kgsl_power_stats stats; + struct idlestats_priv *priv = pwrscale->priv; + + /* This is called from within a mutex protected function, so + no additional locking required */ + device->ftbl->power_stats(device, &stats); + + /* If total_time is zero, then we don't have + any interesting statistics to store */ + if (stats.total_time == 0) { + priv->pulse.busy_start_time = 0; + return; + } + + priv->pulse.busy_interval = stats.busy_time; + priv->pulse.wait_interval = 0; + msm_idle_stats_idle_start(&priv->idledev); +} + +static int idlestats_init(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale) +{ + struct idlestats_priv *priv; + int ret; + + priv = pwrscale->priv = kzalloc(sizeof(struct idlestats_priv), + GFP_KERNEL); + if (pwrscale->priv == NULL) + return -ENOMEM; + + snprintf(priv->name, sizeof(priv->name), "idle_stats_%s", + device->name); + + priv->device = device; + + priv->idledev.name = (const char *) priv->name; + priv->idledev.get_sample = idlestats_get_sample; + + ret = msm_idle_stats_register_device(&priv->idledev); + + if (ret) { + kfree(pwrscale->priv); + pwrscale->priv = NULL; + } + + return ret; +} + +static void idlestats_close(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale) +{ + struct idlestats_priv *priv = pwrscale->priv; + + if (pwrscale->priv == NULL) + return; + + msm_idle_stats_deregister_device(&priv->idledev); + + kfree(pwrscale->priv); + pwrscale->priv = NULL; +} + +struct kgsl_pwrscale_policy kgsl_pwrscale_policy_idlestats = { + .name = "idlestats", + .init = idlestats_init, + .idle = idlestats_idle, + .busy = idlestats_busy, + .close = idlestats_close +}; diff --git a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c new file mode 100644 index 0000000000000..45f76070d25ee --- /dev/null +++ b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c @@ -0,0 +1,197 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#include "kgsl.h" +#include "kgsl_pwrscale.h" +#include "kgsl_device.h" + +#define TZ_GOVERNOR_PERFORMANCE 0 +#define TZ_GOVERNOR_ONDEMAND 1 + +struct tz_priv { + int governor; + unsigned int no_switch_cnt; + unsigned int skip_cnt; +}; + +#define SWITCH_OFF 200 +#define SWITCH_OFF_RESET_TH 40 +#define SKIP_COUNTER 500 +#define TZ_RESET_ID 0x3 +#define TZ_UPDATE_ID 0x4 + +#ifdef CONFIG_MSM_SCM +/* Trap into the TrustZone, and call funcs there. */ +static int __secure_tz_entry(u32 cmd, u32 val) +{ + __iowmb(); + return scm_call_atomic1(SCM_SVC_IO, cmd, val); +} +#else +static int __secure_tz_entry(u32 cmd, u32 val) +{ + return 0; +} +#endif /* CONFIG_MSM_SCM */ + +static ssize_t tz_governor_show(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale, + char *buf) +{ + struct tz_priv *priv = pwrscale->priv; + int ret; + + if (priv->governor == TZ_GOVERNOR_ONDEMAND) + ret = snprintf(buf, 10, "ondemand\n"); + else + ret = snprintf(buf, 13, "performance\n"); + + return ret; +} + +static ssize_t tz_governor_store(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale, + const char *buf, size_t count) +{ + char str[20]; + struct tz_priv *priv = pwrscale->priv; + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + int ret; + + ret = sscanf(buf, "%20s", str); + if (ret != 1) + return -EINVAL; + + mutex_lock(&device->mutex); + + if (!strncmp(str, "ondemand", 8)) + priv->governor = TZ_GOVERNOR_ONDEMAND; + else if (!strncmp(str, "performance", 11)) + priv->governor = TZ_GOVERNOR_PERFORMANCE; + + if (priv->governor == TZ_GOVERNOR_PERFORMANCE) + kgsl_pwrctrl_pwrlevel_change(device, pwr->thermal_pwrlevel); + + mutex_unlock(&device->mutex); + return count; +} + +PWRSCALE_POLICY_ATTR(governor, 0644, tz_governor_show, tz_governor_store); + +static struct attribute *tz_attrs[] = { + &policy_attr_governor.attr, + NULL +}; + +static struct attribute_group tz_attr_group = { + .attrs = tz_attrs, +}; + +static void tz_wake(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) +{ + struct tz_priv *priv = pwrscale->priv; + if (device->state != KGSL_STATE_NAP && + priv->governor == TZ_GOVERNOR_ONDEMAND) + kgsl_pwrctrl_pwrlevel_change(device, + device->pwrctrl.thermal_pwrlevel); +} + +static void tz_idle(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) +{ + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + struct tz_priv *priv = pwrscale->priv; + struct kgsl_power_stats stats; + int val; + + /* In "performance" mode the clock speed always stays + the same */ + + if (priv->governor == TZ_GOVERNOR_PERFORMANCE) + return; + + device->ftbl->power_stats(device, &stats); + if (stats.total_time == 0) + return; + + /* If the GPU has stayed in turbo mode for a while, * + * stop writing out values. */ + if (pwr->active_pwrlevel == 0) { + if (priv->no_switch_cnt > SWITCH_OFF) { + priv->skip_cnt++; + if (priv->skip_cnt > SKIP_COUNTER) { + priv->no_switch_cnt -= SWITCH_OFF_RESET_TH; + priv->skip_cnt = 0; + } + return; + } + priv->no_switch_cnt++; + } else { + priv->no_switch_cnt = 0; + } + + val = __secure_tz_entry(TZ_UPDATE_ID, + stats.total_time - stats.busy_time); + if (val) + kgsl_pwrctrl_pwrlevel_change(device, + pwr->active_pwrlevel + val); +} + +static void tz_sleep(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale) +{ + struct tz_priv *priv = pwrscale->priv; + + __secure_tz_entry(TZ_RESET_ID, 0); + priv->no_switch_cnt = 0; +} + +static int tz_init(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) +{ + struct tz_priv *priv; + + /* Trustzone is only valid for some SOCs */ + if (!(cpu_is_msm8x60() || cpu_is_msm8960())) + return -EINVAL; + + priv = pwrscale->priv = kzalloc(sizeof(struct tz_priv), GFP_KERNEL); + if (pwrscale->priv == NULL) + return -ENOMEM; + + priv->governor = TZ_GOVERNOR_ONDEMAND; + kgsl_pwrscale_policy_add_files(device, pwrscale, &tz_attr_group); + + return 0; +} + +static void tz_close(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) +{ + kgsl_pwrscale_policy_remove_files(device, pwrscale, &tz_attr_group); + kfree(pwrscale->priv); + pwrscale->priv = NULL; +} + +struct kgsl_pwrscale_policy kgsl_pwrscale_policy_tz = { + .name = "trustzone", + .init = tz_init, + .idle = tz_idle, + .sleep = tz_sleep, + .wake = tz_wake, + .close = tz_close +}; +EXPORT_SYMBOL(kgsl_pwrscale_policy_tz); diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index 36ff19c7e8800..09070e4e16112 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -11,11 +11,13 @@ * */ #include +#include #include #include "kgsl.h" #include "kgsl_sharedmem.h" #include "kgsl_cffdump.h" +#include "kgsl_device.h" static struct kgsl_process_private * _get_priv_from_kobj(struct kobject *kobj) @@ -261,6 +263,42 @@ static void kgsl_vmalloc_free(struct kgsl_memdesc *memdesc) vfree(memdesc->hostptr); } +static int kgsl_contiguous_vmflags(struct kgsl_memdesc *memdesc) +{ + return VM_RESERVED | VM_IO | VM_PFNMAP | VM_DONTEXPAND; +} + +static int kgsl_contiguous_vmfault(struct kgsl_memdesc *memdesc, + struct vm_area_struct *vma, + struct vm_fault *vmf) +{ + unsigned long offset, pfn; + int ret; + + offset = ((unsigned long) vmf->virtual_address - vma->vm_start) >> + PAGE_SHIFT; + + pfn = (memdesc->physaddr >> PAGE_SHIFT) + offset; + ret = vm_insert_pfn(vma, (unsigned long) vmf->virtual_address, pfn); + + if (ret == -ENOMEM || ret == -EAGAIN) + return VM_FAULT_OOM; + else if (ret == -EFAULT) + return VM_FAULT_SIGBUS; + + return VM_FAULT_NOPAGE; +} + +static void kgsl_ebimem_free(struct kgsl_memdesc *memdesc) + +{ + kgsl_driver.stats.coherent -= memdesc->size; + if (memdesc->hostptr) + iounmap(memdesc->hostptr); + + free_contiguous_memory_by_paddr(memdesc->physaddr); +} + static void kgsl_coherent_free(struct kgsl_memdesc *memdesc) { kgsl_driver.stats.coherent -= memdesc->size; @@ -268,8 +306,8 @@ static void kgsl_coherent_free(struct kgsl_memdesc *memdesc) memdesc->hostptr, memdesc->physaddr); } -static unsigned long kgsl_contig_physaddr(struct kgsl_memdesc *memdesc, - unsigned int offset) +static unsigned long kgsl_contiguous_physaddr(struct kgsl_memdesc *memdesc, + unsigned int offset) { if (offset > memdesc->size) return 0; @@ -278,7 +316,7 @@ static unsigned long kgsl_contig_physaddr(struct kgsl_memdesc *memdesc, } #ifdef CONFIG_OUTER_CACHE -static void kgsl_contig_outer_cache(struct kgsl_memdesc *memdesc, int op) +static void kgsl_contiguous_outer_cache(struct kgsl_memdesc *memdesc, int op) { _outer_cache_range_op(op, memdesc->physaddr, memdesc->size); } @@ -314,22 +352,32 @@ struct kgsl_memdesc_ops kgsl_vmalloc_ops = { }; EXPORT_SYMBOL(kgsl_vmalloc_ops); +static struct kgsl_memdesc_ops kgsl_ebimem_ops = { + .physaddr = kgsl_contiguous_physaddr, + .free = kgsl_ebimem_free, + .vmflags = kgsl_contiguous_vmflags, + .vmfault = kgsl_contiguous_vmfault, +#ifdef CONFIG_OUTER_CACHE + .outer_cache = kgsl_contiguous_outer_cache, +#endif +}; + static struct kgsl_memdesc_ops kgsl_coherent_ops = { - .physaddr = kgsl_contig_physaddr, + .physaddr = kgsl_contiguous_physaddr, .free = kgsl_coherent_free, #ifdef CONFIG_OUTER_CACHE - .outer_cache = kgsl_contig_outer_cache, + .outer_cache = kgsl_contiguous_outer_cache, #endif }; /* Global - also used by kgsl.c and kgsl_drm.c */ -struct kgsl_memdesc_ops kgsl_contig_ops = { - .physaddr = kgsl_contig_physaddr, +struct kgsl_memdesc_ops kgsl_contiguous_ops = { + .physaddr = kgsl_contiguous_physaddr, #ifdef CONFIG_OUTER_CACHE - .outer_cache = kgsl_contig_outer_cache + .outer_cache = kgsl_contiguous_outer_cache #endif }; -EXPORT_SYMBOL(kgsl_contig_ops); +EXPORT_SYMBOL(kgsl_contiguous_ops); /* Global - also used by kgsl.c */ struct kgsl_memdesc_ops kgsl_userptr_ops = { @@ -482,6 +530,69 @@ void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc) } EXPORT_SYMBOL(kgsl_sharedmem_free); +static int +_kgsl_sharedmem_ebimem(struct kgsl_memdesc *memdesc, + struct kgsl_pagetable *pagetable, size_t size) +{ + int result; + + memdesc->physaddr = allocate_contiguous_ebi_nomap(size, SZ_8K); + + if (memdesc->physaddr == 0) { + KGSL_CORE_ERR("allocate_contiguous_ebi_nomap(%d) failed\n", + size); + return -ENOMEM; + } + + memdesc->size = size; + memdesc->pagetable = pagetable; + memdesc->ops = &kgsl_ebimem_ops; + + result = kgsl_mmu_map(pagetable, memdesc, + GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); + + if (result) + kgsl_sharedmem_free(memdesc); + + KGSL_STATS_ADD(size, kgsl_driver.stats.coherent, + kgsl_driver.stats.coherent_max); + + return result; +} + +int +kgsl_sharedmem_ebimem_user(struct kgsl_memdesc *memdesc, + struct kgsl_pagetable *pagetable, + size_t size, int flags) +{ + size = ALIGN(size, PAGE_SIZE); + return _kgsl_sharedmem_ebimem(memdesc, pagetable, size); +} +EXPORT_SYMBOL(kgsl_sharedmem_ebimem_user); + +int +kgsl_sharedmem_ebimem(struct kgsl_memdesc *memdesc, + struct kgsl_pagetable *pagetable, size_t size) +{ + int result; + size = ALIGN(size, 8192); + result = _kgsl_sharedmem_ebimem(memdesc, pagetable, size); + + if (result) + return result; + + memdesc->hostptr = ioremap(memdesc->physaddr, size); + + if (memdesc->hostptr == NULL) { + KGSL_CORE_ERR("ioremap failed\n"); + kgsl_sharedmem_free(memdesc); + return -ENOMEM; + } + + return 0; +} +EXPORT_SYMBOL(kgsl_sharedmem_ebimem); + int kgsl_sharedmem_readl(const struct kgsl_memdesc *memdesc, uint32_t *dst, diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h index d0070584a3b11..9e57e78c061db 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.h +++ b/drivers/gpu/msm/kgsl_sharedmem.h @@ -1,29 +1,13 @@ /* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 __KGSL_SHAREDMEM_H @@ -31,7 +15,6 @@ #include -struct kgsl_pagetable; struct kgsl_device; struct kgsl_process_private; @@ -42,8 +25,6 @@ struct kgsl_process_private; /** Set if the memdesc describes cached memory */ #define KGSL_MEMFLAGS_CACHED 0x00000001 -struct kgsl_memdesc; - struct kgsl_memdesc_ops { unsigned long (*physaddr)(struct kgsl_memdesc *, unsigned int); void (*outer_cache)(struct kgsl_memdesc *, int); @@ -53,19 +34,8 @@ struct kgsl_memdesc_ops { void (*free)(struct kgsl_memdesc *memdesc); }; -/* shared memory allocation */ -struct kgsl_memdesc { - struct kgsl_pagetable *pagetable; - void *hostptr; - unsigned int gpuaddr; - unsigned int physaddr; - unsigned int size; - unsigned int priv; - struct kgsl_memdesc_ops *ops; -}; - extern struct kgsl_memdesc_ops kgsl_vmalloc_ops; -extern struct kgsl_memdesc_ops kgsl_contig_ops; +extern struct kgsl_memdesc_ops kgsl_contiguous_ops; extern struct kgsl_memdesc_ops kgsl_userptr_ops; int kgsl_sharedmem_vmalloc(struct kgsl_memdesc *memdesc, @@ -77,6 +47,14 @@ int kgsl_sharedmem_vmalloc_user(struct kgsl_memdesc *memdesc, int kgsl_sharedmem_alloc_coherent(struct kgsl_memdesc *memdesc, size_t size); +int kgsl_sharedmem_ebimem_user(struct kgsl_memdesc *memdesc, + struct kgsl_pagetable *pagetable, + size_t size, int flags); + +int kgsl_sharedmem_ebimem(struct kgsl_memdesc *memdesc, + struct kgsl_pagetable *pagetable, + size_t size); + void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc); int kgsl_sharedmem_readl(const struct kgsl_memdesc *memdesc, @@ -99,18 +77,38 @@ void kgsl_process_uninit_sysfs(struct kgsl_process_private *private); int kgsl_sharedmem_init_sysfs(void); void kgsl_sharedmem_uninit_sysfs(void); +static inline int +kgsl_allocate(struct kgsl_memdesc *memdesc, + struct kgsl_pagetable *pagetable, size_t size) +{ +#ifdef CONFIG_MSM_KGSL_MMU + return kgsl_sharedmem_vmalloc(memdesc, pagetable, size); +#else + return kgsl_sharedmem_ebimem(memdesc, pagetable, size); +#endif +} + static inline int kgsl_allocate_user(struct kgsl_memdesc *memdesc, struct kgsl_pagetable *pagetable, size_t size, unsigned int flags) { +#ifdef CONFIG_MSM_KGSL_MMU return kgsl_sharedmem_vmalloc_user(memdesc, pagetable, size, flags); +#else + return kgsl_sharedmem_ebimem_user(memdesc, pagetable, size, flags); +#endif } static inline int -kgsl_allocate_contig(struct kgsl_memdesc *memdesc, size_t size) +kgsl_allocate_contiguous(struct kgsl_memdesc *memdesc, size_t size) { - return kgsl_sharedmem_alloc_coherent(memdesc, size); + int ret = kgsl_sharedmem_alloc_coherent(memdesc, size); +#ifndef CONFIG_MSM_KGSL_MMU + if (!ret) + memdesc->gpuaddr = memdesc->physaddr; +#endif + return ret; } #endif /* __KGSL_SHAREDMEM_H */ diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c index 27da432ed8336..37ba6219efb3f 100644 --- a/drivers/gpu/msm/z180.c +++ b/drivers/gpu/msm/z180.c @@ -14,6 +14,7 @@ #include "kgsl.h" #include "kgsl_cffdump.h" +#include "kgsl_sharedmem.h" #include "z180.h" #include "z180_reg.h" @@ -86,6 +87,11 @@ #define Z180_TIMESTAMP_EPSILON 20000 #define Z180_IDLE_COUNT_MAX 1000000 +enum z180_cmdwindow_type { + Z180_CMDWINDOW_2D = 0x00000000, + Z180_CMDWINDOW_MMU = 0x00000002, +}; + #define Z180_CMDWINDOW_TARGET_MASK 0x000000FF #define Z180_CMDWINDOW_ADDR_MASK 0x00FFFF00 #define Z180_CMDWINDOW_TARGET_SHIFT 0 @@ -102,17 +108,9 @@ static void z180_regread(struct kgsl_device *device, static void z180_regwrite(struct kgsl_device *device, unsigned int offsetwords, unsigned int value); -static int z180_cmdwindow_write(struct kgsl_device *device, - enum kgsl_cmdwindow_type target, +static void z180_cmdwindow_write(struct kgsl_device *device, unsigned int addr, unsigned int data); -static void z180_regread_isr(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int *value); -static void z180_regwrite_isr(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int value); -static void __devinit z180_getfunctable(struct kgsl_functable *ftbl); #define Z180_MMU_CONFIG \ (0x01 \ @@ -128,33 +126,26 @@ static void __devinit z180_getfunctable(struct kgsl_functable *ftbl); | (MMU_CONFIG << MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT) \ | (MMU_CONFIG << MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT)) +static const struct kgsl_functable z180_functable; + static struct z180_device device_2d0 = { .dev = { .name = DEVICE_2D0_NAME, .id = KGSL_DEVICE_2D0, .ver_major = DRIVER_VERSION_MAJOR, .ver_minor = DRIVER_VERSION_MINOR, - .mmu = { - .config = Z180_MMU_CONFIG, + .mh = { + .mharb = Z180_CFG_MHARB, + .mh_intf_cfg1 = 0x00032f07, + .mh_intf_cfg2 = 0x004b274f, /* turn off memory protection unit by setting acceptable physical address range to include all pages. */ .mpu_base = 0x00000000, .mpu_range = 0xFFFFF000, - .reg = { - .config = ADDR_MH_MMU_CONFIG, - .mpu_base = ADDR_MH_MMU_MPU_BASE, - .mpu_end = ADDR_MH_MMU_MPU_END, - .va_range = ADDR_MH_MMU_VA_RANGE, - .pt_page = ADDR_MH_MMU_PT_BASE, - .page_fault = ADDR_MH_MMU_PAGE_FAULT, - .tran_error = ADDR_MH_MMU_TRAN_ERROR, - .invalidate = ADDR_MH_MMU_INVALIDATE, - .interrupt_mask = ADDR_MH_INTERRUPT_MASK, - .interrupt_status = ADDR_MH_INTERRUPT_STATUS, - .interrupt_clear = ADDR_MH_INTERRUPT_CLEAR, - .axi_error = ADDR_MH_AXI_ERROR, - }, + }, + .mmu = { + .config = Z180_MMU_CONFIG, }, .pwrctrl = { .regulator_name = "fs_gfx2d0", @@ -164,6 +155,14 @@ static struct z180_device device_2d0 = { .state = KGSL_STATE_INIT, .active_cnt = 0, .iomemname = KGSL_2D0_REG_MEMORY, + .ftbl = &z180_functable, +#ifdef CONFIG_HAS_EARLYSUSPEND + .display_off = { + .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING, + .suspend = kgsl_early_suspend_driver, + .resume = kgsl_late_resume_driver, + }, +#endif }, }; @@ -173,27 +172,18 @@ static struct z180_device device_2d1 = { .id = KGSL_DEVICE_2D1, .ver_major = DRIVER_VERSION_MAJOR, .ver_minor = DRIVER_VERSION_MINOR, - .mmu = { - .config = Z180_MMU_CONFIG, + .mh = { + .mharb = Z180_CFG_MHARB, + .mh_intf_cfg1 = 0x00032f07, + .mh_intf_cfg2 = 0x004b274f, /* turn off memory protection unit by setting acceptable physical address range to include all pages. */ .mpu_base = 0x00000000, .mpu_range = 0xFFFFF000, - .reg = { - .config = ADDR_MH_MMU_CONFIG, - .mpu_base = ADDR_MH_MMU_MPU_BASE, - .mpu_end = ADDR_MH_MMU_MPU_END, - .va_range = ADDR_MH_MMU_VA_RANGE, - .pt_page = ADDR_MH_MMU_PT_BASE, - .page_fault = ADDR_MH_MMU_PAGE_FAULT, - .tran_error = ADDR_MH_MMU_TRAN_ERROR, - .invalidate = ADDR_MH_MMU_INVALIDATE, - .interrupt_mask = ADDR_MH_INTERRUPT_MASK, - .interrupt_status = ADDR_MH_INTERRUPT_STATUS, - .interrupt_clear = ADDR_MH_INTERRUPT_CLEAR, - .axi_error = ADDR_MH_AXI_ERROR, - }, + }, + .mmu = { + .config = Z180_MMU_CONFIG, }, .pwrctrl = { .regulator_name = "fs_gfx2d1", @@ -203,6 +193,14 @@ static struct z180_device device_2d1 = { .state = KGSL_STATE_INIT, .active_cnt = 0, .iomemname = KGSL_2D1_REG_MEMORY, + .ftbl = &z180_functable, + .display_off = { +#ifdef CONFIG_HAS_EARLYSUSPEND + .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING, + .suspend = kgsl_early_suspend_driver, + .resume = kgsl_late_resume_driver, +#endif + }, }, }; @@ -213,10 +211,10 @@ static irqreturn_t z180_isr(int irq, void *data) struct kgsl_device *device = (struct kgsl_device *) data; struct z180_device *z180_dev = Z180_DEVICE(device); - z180_regread_isr(device, ADDR_VGC_IRQSTATUS >> 2, &status); + z180_regread(device, ADDR_VGC_IRQSTATUS >> 2, &status); if (status & GSL_VGC_INT_MASK) { - z180_regwrite_isr(device, + z180_regwrite(device, ADDR_VGC_IRQSTATUS >> 2, status & GSL_VGC_INT_MASK); result = IRQ_HANDLED; @@ -228,7 +226,7 @@ static irqreturn_t z180_isr(int irq, void *data) if (status & REG_VGC_IRQSTATUS__G2D_MASK) { int count; - z180_regread_isr(device, + z180_regread(device, ADDR_VGC_IRQ_ACTIVE_CNT >> 2, &count); @@ -255,18 +253,16 @@ static irqreturn_t z180_isr(int irq, void *data) return result; } -static int z180_cleanup_pt(struct kgsl_device *device, +static void z180_cleanup_pt(struct kgsl_device *device, struct kgsl_pagetable *pagetable) { struct z180_device *z180_dev = Z180_DEVICE(device); - kgsl_mmu_unmap(pagetable, &device->mmu.dummyspace); + kgsl_mmu_unmap(pagetable, &device->mmu.setstate_memory); kgsl_mmu_unmap(pagetable, &device->memstore); kgsl_mmu_unmap(pagetable, &z180_dev->ringbuffer.cmdbufdesc); - - return 0; } static int z180_setup_pt(struct kgsl_device *device, @@ -275,7 +271,7 @@ static int z180_setup_pt(struct kgsl_device *device, int result = 0; struct z180_device *z180_dev = Z180_DEVICE(device); - result = kgsl_mmu_map_global(pagetable, &device->mmu.dummyspace, + result = kgsl_mmu_map_global(pagetable, &device->mmu.setstate_memory, GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); if (result) @@ -294,7 +290,7 @@ static int z180_setup_pt(struct kgsl_device *device, return result; error_unmap_dummy: - kgsl_mmu_unmap(pagetable, &device->mmu.dummyspace); + kgsl_mmu_unmap(pagetable, &device->mmu.setstate_memory); error_unmap_memstore: kgsl_mmu_unmap(pagetable, &device->memstore); @@ -339,10 +335,9 @@ static void addcmd(struct z180_ringbuffer *rb, unsigned int index, *p++ = ADDR_VGV3_LAST << 24; } -static int z180_cmdstream_start(struct kgsl_device *device) +static void z180_cmdstream_start(struct kgsl_device *device) { struct z180_device *z180_dev = Z180_DEVICE(device); - int result; unsigned int cmd = VGV3_NEXTCMD_JUMP << VGV3_NEXTCMD_NEXTCMD_FSHIFT; z180_dev->timestamp = 0; @@ -350,43 +345,22 @@ static int z180_cmdstream_start(struct kgsl_device *device) addmarker(&z180_dev->ringbuffer, 0); - result = z180_cmdwindow_write(device, KGSL_CMDWINDOW_2D, - ADDR_VGV3_MODE, 4); - if (result != 0) - return result; + z180_cmdwindow_write(device, ADDR_VGV3_MODE, 4); - result = z180_cmdwindow_write(device, KGSL_CMDWINDOW_2D, - ADDR_VGV3_NEXTADDR, + z180_cmdwindow_write(device, ADDR_VGV3_NEXTADDR, z180_dev->ringbuffer.cmdbufdesc.gpuaddr); - if (result != 0) - return result; - - result = z180_cmdwindow_write(device, KGSL_CMDWINDOW_2D, - ADDR_VGV3_NEXTCMD, cmd | 5); - if (result != 0) - return result; - result = z180_cmdwindow_write(device, KGSL_CMDWINDOW_2D, - ADDR_VGV3_WRITEADDR, device->memstore.gpuaddr); + z180_cmdwindow_write(device, ADDR_VGV3_NEXTCMD, cmd | 5); - if (result != 0) - return result; + z180_cmdwindow_write(device, ADDR_VGV3_WRITEADDR, + device->memstore.gpuaddr); cmd = (int)(((1) & VGV3_CONTROL_MARKADD_FMASK) << VGV3_CONTROL_MARKADD_FSHIFT); - result = z180_cmdwindow_write(device, KGSL_CMDWINDOW_2D, - ADDR_VGV3_CONTROL, cmd); + z180_cmdwindow_write(device, ADDR_VGV3_CONTROL, cmd); - if (result != 0) - return result; - - result = z180_cmdwindow_write(device, KGSL_CMDWINDOW_2D, - ADDR_VGV3_CONTROL, 0); - if (result != 0) - return result; - - return result; + z180_cmdwindow_write(device, ADDR_VGV3_CONTROL, 0); } static int room_in_rb(struct z180_device *device) @@ -413,30 +387,6 @@ static int z180_idle(struct kgsl_device *device, unsigned int timeout) return status; } -static int z180_setstate(struct kgsl_device *device, uint32_t flags) -{ -#ifdef CONFIG_MSM_KGSL_MMU - unsigned int mh_mmu_invalidate = 0x00000003; /*invalidate all and tc */ - - if (flags & KGSL_MMUFLAGS_PTUPDATE) { - z180_idle(device, KGSL_TIMEOUT_DEFAULT); - z180_regwrite(device, ADDR_MH_MMU_PT_BASE, - device->mmu.hwpagetable->base.gpuaddr); - z180_regwrite(device, ADDR_MH_MMU_VA_RANGE, - (device->mmu.hwpagetable-> - va_base | (device->mmu.hwpagetable-> - va_range >> 16))); - z180_regwrite(device, ADDR_MH_MMU_INVALIDATE, - mh_mmu_invalidate); - } - - if (flags & KGSL_MMUFLAGS_TLBFLUSH) - z180_regwrite(device, ADDR_MH_MMU_INVALIDATE, - mh_mmu_invalidate); -#endif - return 0; -} - int z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv, struct kgsl_context *context, @@ -445,7 +395,7 @@ z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv, uint32_t *timestamp, unsigned int ctrl) { - unsigned int result = 0; + long result = 0; unsigned int ofs = PACKETSIZE_STATESTREAM * sizeof(unsigned int); unsigned int cnt = 5; unsigned int nextaddr = 0; @@ -484,7 +434,7 @@ z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv, cnt = PACKETSIZE_STATESTREAM; ofs = 0; } - z180_setstate(device, kgsl_pt_get_flags(device->mmu.hwpagetable, + kgsl_setstate(device, kgsl_mmu_pt_get_flags(device->mmu.hwpagetable, device->id)); result = wait_event_interruptible_timeout(device->wait_queue, @@ -492,7 +442,7 @@ z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv, msecs_to_jiffies(KGSL_TIMEOUT_DEFAULT)); if (result < 0) { KGSL_CMD_ERR(device, "wait_event_interruptible_timeout " - "failed: %d\n", result); + "failed: %ld\n", result); goto error; } result = 0; @@ -525,12 +475,10 @@ z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv, cmd = (int)(((2) & VGV3_CONTROL_MARKADD_FMASK) << VGV3_CONTROL_MARKADD_FSHIFT); - z180_cmdwindow_write(device, - KGSL_CMDWINDOW_2D, ADDR_VGV3_CONTROL, cmd); - z180_cmdwindow_write(device, - KGSL_CMDWINDOW_2D, ADDR_VGV3_CONTROL, 0); + z180_cmdwindow_write(device, ADDR_VGV3_CONTROL, cmd); + z180_cmdwindow_write(device, ADDR_VGV3_CONTROL, 0); error: - return result; + return (int)result; } static int z180_ringbuffer_init(struct kgsl_device *device) @@ -538,9 +486,8 @@ static int z180_ringbuffer_init(struct kgsl_device *device) struct z180_device *z180_dev = Z180_DEVICE(device); memset(&z180_dev->ringbuffer, 0, sizeof(struct z180_ringbuffer)); z180_dev->ringbuffer.prevctx = Z180_INVALID_CONTEXT; - return kgsl_sharedmem_alloc_coherent( - &z180_dev->ringbuffer.cmdbufdesc, - Z180_RB_SIZE); + return kgsl_allocate_contiguous(&z180_dev->ringbuffer.cmdbufdesc, + Z180_RB_SIZE); } static void z180_ringbuffer_close(struct kgsl_device *device) @@ -559,8 +506,6 @@ static int __devinit z180_probe(struct platform_device *pdev) device = (struct kgsl_device *)pdev->id_entry->driver_data; device->parentdev = &pdev->dev; - z180_getfunctable(&device->ftbl); - z180_dev = Z180_DEVICE(device); spin_lock_init(&z180_dev->cmdwin_lock); @@ -572,6 +517,8 @@ static int __devinit z180_probe(struct platform_device *pdev) if (status) goto error_close_ringbuffer; + kgsl_pwrscale_init(device); + return status; error_close_ringbuffer: @@ -587,6 +534,7 @@ static int __devexit z180_remove(struct platform_device *pdev) device = (struct kgsl_device *)pdev->id_entry->driver_data; + kgsl_pwrscale_close(device); kgsl_device_platform_remove(device); z180_ringbuffer_close(device); @@ -604,31 +552,24 @@ static int z180_start(struct kgsl_device *device, unsigned int init_ram) kgsl_pwrctrl_enable(device); - /* Set up MH arbiter. MH offsets are considered to be dword - * based, therefore no down shift. */ - z180_regwrite(device, ADDR_MH_ARBITER_CONFIG, Z180_CFG_MHARB); - - z180_regwrite(device, ADDR_MH_CLNT_INTF_CTRL_CONFIG1, 0x00030F27); - z180_regwrite(device, ADDR_MH_CLNT_INTF_CTRL_CONFIG2, 0x004B274F); + /* Set interrupts to 0 to ensure a good state */ + z180_regwrite(device, (ADDR_VGC_IRQENABLE >> 2), 0x0); - z180_regwrite(device, (ADDR_VGC_IRQENABLE >> 2), 0x3); + kgsl_mh_start(device); status = kgsl_mmu_start(device); if (status) goto error_clk_off; - status = z180_cmdstream_start(device); - if (status) - goto error_mmu_stop; + z180_cmdstream_start(device); mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT); kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_ON); return 0; + error_clk_off: z180_regwrite(device, (ADDR_VGC_IRQENABLE >> 2), 0); kgsl_pwrctrl_disable(device); -error_mmu_stop: - kgsl_mmu_stop(device); return status; } @@ -636,7 +577,7 @@ static int z180_stop(struct kgsl_device *device) { z180_idle(device, KGSL_TIMEOUT_DEFAULT); - del_timer(&device->idle_timer); + del_timer_sync(&device->idle_timer); kgsl_mmu_stop(device); @@ -680,16 +621,12 @@ static int z180_getproperty(struct kgsl_device *device, break; case KGSL_PROP_MMU_ENABLE: { -#ifdef CONFIG_MSM_KGSL_MMU - int mmuProp = 1; -#else - int mmuProp = 0; -#endif + int mmu_prop = kgsl_mmu_enabled(); if (sizebytes != sizeof(int)) { status = -EINVAL; break; } - if (copy_to_user(value, &mmuProp, sizeof(mmuProp))) { + if (copy_to_user(value, &mmu_prop, sizeof(mmu_prop))) { status = -EFAULT; break; } @@ -717,13 +654,6 @@ static unsigned int z180_isidle(struct kgsl_device *device) return status; } -static int z180_resume_context(struct kgsl_device *device) -{ - /* Context is in the pre-amble, automatically restored. */ - - return 0; -} - static int z180_suspend_context(struct kgsl_device *device) { struct z180_device *z180_dev = Z180_DEVICE(device); @@ -800,7 +730,7 @@ static void _z180_regwrite_mmu(struct kgsl_device *device, unsigned int cmdwinaddr; unsigned long flags; - cmdwinaddr = ((KGSL_CMDWINDOW_MMU << Z180_CMDWINDOW_TARGET_SHIFT) & + cmdwinaddr = ((Z180_CMDWINDOW_MMU << Z180_CMDWINDOW_TARGET_SHIFT) & Z180_CMDWINDOW_TARGET_MASK); cmdwinaddr |= ((offsetwords << Z180_CMDWINDOW_ADDR_SHIFT) & Z180_CMDWINDOW_ADDR_MASK); @@ -815,91 +745,52 @@ static void _z180_regwrite_mmu(struct kgsl_device *device, /* the rest of the code doesn't want to think about if it is writing mmu * registers or normal registers so handle it here */ -static void _z180_regread(struct kgsl_device *device, unsigned int offsetwords, - unsigned int *value) +static void z180_regread(struct kgsl_device *device, + unsigned int offsetwords, + unsigned int *value) { - if ((offsetwords >= ADDR_MH_ARBITER_CONFIG && - offsetwords <= ADDR_MH_AXI_HALT_CONTROL) || - (offsetwords >= ADDR_MH_MMU_CONFIG && - offsetwords <= ADDR_MH_MMU_MPU_END)) { + if (!in_interrupt()) + kgsl_pre_hwaccess(device); + + if ((offsetwords >= MH_ARBITER_CONFIG && + offsetwords <= MH_AXI_HALT_CONTROL) || + (offsetwords >= MH_MMU_CONFIG && + offsetwords <= MH_MMU_MPU_END)) { _z180_regread_mmu(device, offsetwords, value); } else { _z180_regread_simple(device, offsetwords, value); } } -static void _z180_regwrite(struct kgsl_device *device, unsigned int offsetwords, +static void z180_regwrite(struct kgsl_device *device, + unsigned int offsetwords, unsigned int value) { - if ((offsetwords >= ADDR_MH_ARBITER_CONFIG && - offsetwords <= ADDR_MH_CLNT_INTF_CTRL_CONFIG2) || - (offsetwords >= ADDR_MH_MMU_CONFIG && - offsetwords <= ADDR_MH_MMU_MPU_END)) { - _z180_regwrite_mmu(device, offsetwords, value); + if (!in_interrupt()) + kgsl_pre_hwaccess(device); + if ((offsetwords >= MH_ARBITER_CONFIG && + offsetwords <= MH_CLNT_INTF_CTRL_CONFIG2) || + (offsetwords >= MH_MMU_CONFIG && + offsetwords <= MH_MMU_MPU_END)) { + _z180_regwrite_mmu(device, offsetwords, value); } else { _z180_regwrite_simple(device, offsetwords, value); } } - -static void z180_regread(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int *value) -{ - kgsl_pre_hwaccess(device); - _z180_regread(device, offsetwords, value); -} - -static void z180_regread_isr(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int *value) -{ - _z180_regread(device, offsetwords, value); -} - -static void z180_regwrite(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int value) -{ - kgsl_pre_hwaccess(device); - _z180_regwrite(device, offsetwords, value); -} - -static void z180_regwrite_isr(struct kgsl_device *device, - unsigned int offsetwords, - unsigned int value) -{ - _z180_regwrite(device, offsetwords, value); -} - -static int z180_cmdwindow_write(struct kgsl_device *device, - enum kgsl_cmdwindow_type target, unsigned int addr, - unsigned int data) +static void z180_cmdwindow_write(struct kgsl_device *device, + unsigned int addr, unsigned int data) { unsigned int cmdwinaddr; - unsigned int cmdstream; - - if (target < KGSL_CMDWINDOW_MIN || - target > KGSL_CMDWINDOW_MAX) { - KGSL_DRV_ERR(device, "invalid target\n"); - return -EINVAL; - } - - if (target == KGSL_CMDWINDOW_MMU) - cmdstream = ADDR_VGC_MMUCOMMANDSTREAM; - else - cmdstream = ADDR_VGC_COMMANDSTREAM; - cmdwinaddr = ((target << Z180_CMDWINDOW_TARGET_SHIFT) & + cmdwinaddr = ((Z180_CMDWINDOW_2D << Z180_CMDWINDOW_TARGET_SHIFT) & Z180_CMDWINDOW_TARGET_MASK); cmdwinaddr |= ((addr << Z180_CMDWINDOW_ADDR_SHIFT) & Z180_CMDWINDOW_ADDR_MASK); - z180_regwrite(device, cmdstream >> 2, cmdwinaddr); - z180_regwrite(device, cmdstream >> 2, data); - - return 0; + z180_regwrite(device, ADDR_VGC_COMMANDSTREAM >> 2, cmdwinaddr); + z180_regwrite(device, ADDR_VGC_COMMANDSTREAM >> 2, data); } static unsigned int z180_readtimestamp(struct kgsl_device *device, @@ -946,19 +837,7 @@ static int z180_wait(struct kgsl_device *device, return status; } -static long -z180_ioctl_cmdwindow_write(struct kgsl_device_private *dev_priv, - void *data) -{ - struct kgsl_cmdwindow_write *param = data; - - return z180_cmdwindow_write(dev_priv->device, - param->target, - param->addr, - param->data); -} - -static int +static void z180_drawctxt_destroy(struct kgsl_device *device, struct kgsl_context *context) { @@ -971,63 +850,62 @@ z180_drawctxt_destroy(struct kgsl_device *device, device->mmu.hwpagetable = device->mmu.defaultpagetable; kgsl_setstate(device, KGSL_MMUFLAGS_PTUPDATE); } - - return 0; } -static long z180_ioctl(struct kgsl_device_private *dev_priv, - unsigned int cmd, void *data) +static void z180_power_stats(struct kgsl_device *device, + struct kgsl_power_stats *stats) { - int result = 0; + struct kgsl_pwrctrl *pwr = &device->pwrctrl; - switch (cmd) { - case IOCTL_KGSL_CMDWINDOW_WRITE: - result = z180_ioctl_cmdwindow_write(dev_priv, data); - break; - default: - KGSL_DRV_INFO(dev_priv->device, - "invalid ioctl code %08x\n", cmd); - result = -EINVAL; - break; + if (pwr->time == 0) { + pwr->time = ktime_to_us(ktime_get()); + stats->total_time = 0; + stats->busy_time = 0; + } else { + s64 tmp; + tmp = ktime_to_us(ktime_get()); + stats->total_time = tmp - pwr->time; + stats->busy_time = tmp - pwr->time; + pwr->time = tmp; } - return result; - } -static void z180_power_stats(struct kgsl_device *device, - struct kgsl_power_stats *stats) +static void z180_irqctrl(struct kgsl_device *device, int state) { - stats->total_time = 0; - stats->busy_time = 0; -} + /* Control interrupts for Z180 and the Z180 MMU */ -static void __devinit z180_getfunctable(struct kgsl_functable *ftbl) -{ - if (ftbl == NULL) - return; - ftbl->device_regread = z180_regread; - ftbl->device_regwrite = z180_regwrite; - ftbl->device_regread_isr = z180_regread_isr; - ftbl->device_regwrite_isr = z180_regwrite_isr; - ftbl->device_setstate = z180_setstate; - ftbl->device_idle = z180_idle; - ftbl->device_isidle = z180_isidle; - ftbl->device_suspend_context = z180_suspend_context; - ftbl->device_resume_context = z180_resume_context; - ftbl->device_start = z180_start; - ftbl->device_stop = z180_stop; - ftbl->device_getproperty = z180_getproperty; - ftbl->device_waittimestamp = z180_waittimestamp; - ftbl->device_readtimestamp = z180_readtimestamp; - ftbl->device_issueibcmds = z180_cmdstream_issueibcmds; - ftbl->device_drawctxt_create = NULL; - ftbl->device_drawctxt_destroy = z180_drawctxt_destroy; - ftbl->device_ioctl = z180_ioctl; - ftbl->device_setup_pt = z180_setup_pt; - ftbl->device_cleanup_pt = z180_cleanup_pt; - ftbl->device_power_stats = z180_power_stats, + if (state) { + z180_regwrite(device, (ADDR_VGC_IRQENABLE >> 2), 3); + z180_regwrite(device, MH_INTERRUPT_MASK, KGSL_MMU_INT_MASK); + } else { + z180_regwrite(device, (ADDR_VGC_IRQENABLE >> 2), 0); + z180_regwrite(device, MH_INTERRUPT_MASK, 0); + } } +static const struct kgsl_functable z180_functable = { + /* Mandatory functions */ + .regread = z180_regread, + .regwrite = z180_regwrite, + .idle = z180_idle, + .isidle = z180_isidle, + .suspend_context = z180_suspend_context, + .start = z180_start, + .stop = z180_stop, + .getproperty = z180_getproperty, + .waittimestamp = z180_waittimestamp, + .readtimestamp = z180_readtimestamp, + .issueibcmds = z180_cmdstream_issueibcmds, + .setup_pt = z180_setup_pt, + .cleanup_pt = z180_cleanup_pt, + .power_stats = z180_power_stats, + .irqctrl = z180_irqctrl, + /* Optional functions */ + .drawctxt_create = NULL, + .drawctxt_destroy = z180_drawctxt_destroy, + .ioctl = NULL, +}; + static struct platform_device_id z180_id_table[] = { { DEVICE_2D0_NAME, (kernel_ulong_t)&device_2d0.dev, }, { DEVICE_2D1_NAME, (kernel_ulong_t)&device_2d1.dev, }, diff --git a/drivers/gpu/msm/z180.h b/drivers/gpu/msm/z180.h index c62398a523f68..28b1cc6b336ce 100644 --- a/drivers/gpu/msm/z180.h +++ b/drivers/gpu/msm/z180.h @@ -1,34 +1,20 @@ /* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 __Z180_H #define __Z180_H +#include "kgsl_device.h" + #define DEVICE_2D_NAME "kgsl-2d" #define DEVICE_2D0_NAME "kgsl-2d0" #define DEVICE_2D1_NAME "kgsl-2d1" diff --git a/drivers/gpu/msm/z180_reg.h b/drivers/gpu/msm/z180_reg.h index f5625535f10c4..5b6c0017f03a0 100644 --- a/drivers/gpu/msm/z180_reg.h +++ b/drivers/gpu/msm/z180_reg.h @@ -1,29 +1,13 @@ /* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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 __Z80_REG_H @@ -48,36 +32,8 @@ #define MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT 0x00000019 #define MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT 0x0000001a -#define MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT 0x00000004 -#define MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT 0x00000006 -#define MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT 0x00000008 -#define MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT 0x0000000a -#define MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT 0x0000000c -#define MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT 0x0000000e -#define MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT 0x00000010 -#define MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT 0x00000012 -#define MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT 0x00000014 -#define MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT 0x00000016 -#define MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT 0x00000018 - -#define ADDR_MH_ARBITER_CONFIG 0x0A40 -#define ADDR_MH_INTERRUPT_CLEAR 0x0A44 -#define ADDR_MH_INTERRUPT_MASK 0x0A42 -#define ADDR_MH_INTERRUPT_STATUS 0x0A43 -#define ADDR_MH_AXI_ERROR 0x0A45 -#define ADDR_MH_AXI_HALT_CONTROL 0x0A50 -#define ADDR_MH_CLNT_INTF_CTRL_CONFIG1 0x0A54 -#define ADDR_MH_CLNT_INTF_CTRL_CONFIG2 0x0A55 -#define ADDR_MH_MMU_CONFIG 0x0040 -#define ADDR_MH_MMU_INVALIDATE 0x0045 -#define ADDR_MH_MMU_MPU_BASE 0x0046 -#define ADDR_MH_MMU_MPU_END 0x0047 -#define ADDR_MH_MMU_PT_BASE 0x0042 -#define ADDR_MH_MMU_TRAN_ERROR 0x0044 -#define ADDR_MH_MMU_VA_RANGE 0x0041 #define ADDR_VGC_MH_READ_ADDR 0x0510 #define ADDR_VGC_MH_DATA_ADDR 0x0518 -#define ADDR_MH_MMU_PAGE_FAULT 0x0043 #define ADDR_VGC_COMMANDSTREAM 0x0000 #define ADDR_VGC_IRQENABLE 0x0438 #define ADDR_VGC_IRQSTATUS 0x0418 diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index ceada7850c1ff..ae26f2700b35b 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -1,36 +1,3 @@ -/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, and the entire permission notice in its entirety, - * including the disclaimer of warranties. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * ALTERNATIVELY, this product may be distributed under the terms of - * the GNU General Public License, version 2, in which case the provisions - * of the GPL version 2 are required INSTEAD OF the BSD license. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF - * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT - * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - */ #ifndef _MSM_KGSL_H #define _MSM_KGSL_H @@ -170,7 +137,6 @@ struct kgsl_device_pwr_data { int (*set_grp_async)(void); unsigned int idle_timeout; unsigned int nap_allowed; - unsigned int idle_pass; }; struct kgsl_clk_data { @@ -183,6 +149,8 @@ struct kgsl_device_platform_data { struct kgsl_clk_data clk; /* imem_clk_name is for 3d only, not used in 2d devices */ struct kgsl_grp_clk_name imem_clk_name; + const char *iommu_user_ctx_name; + const char *iommu_priv_ctx_name; }; #endif From 201ab5f310167bf717cec5a8fa6891f17ee33a97 Mon Sep 17 00:00:00 2001 From: Stepan Moskovchenko Date: Thu, 11 Aug 2011 19:32:27 -0700 Subject: [PATCH 2234/2556] drivers: iommu: Add map/unmap range ops Add IOMMU ops functions to allow mapping and unmapping whole ranges of address space based on a scatterlist. Change-Id: Ifd1c4479264e902e1e8b60f87ed114212896a7ed Signed-off-by: Stepan Moskovchenko Conflicts: include/linux/iommu.h --- drivers/base/iommu.c | 19 +++++++++++++++++++ include/linux/iommu.h | 23 +++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/drivers/base/iommu.c b/drivers/base/iommu.c index 6e6b6a11b3ced..6490bfe90d8b9 100644 --- a/drivers/base/iommu.c +++ b/drivers/base/iommu.c @@ -22,6 +22,7 @@ #include #include #include +#include static struct iommu_ops *iommu_ops; @@ -122,3 +123,21 @@ int iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order) return iommu_ops->unmap(domain, iova, gfp_order); } EXPORT_SYMBOL_GPL(iommu_unmap); + +int iommu_map_range(struct iommu_domain *domain, unsigned int iova, + struct scatterlist *sg, unsigned int len, int prot) +{ + BUG_ON(iova & (~PAGE_MASK)); + + return iommu_ops->map_range(domain, iova, sg, len, prot); +} +EXPORT_SYMBOL_GPL(iommu_map_range); + +int iommu_unmap_range(struct iommu_domain *domain, unsigned int iova, + unsigned int len) +{ + BUG_ON(iova & (~PAGE_MASK)); + + return iommu_ops->unmap_range(domain, iova, len); +} +EXPORT_SYMBOL_GPL(iommu_unmap_range); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 0a2ba4098996d..34b027c4fd5ac 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -19,6 +19,8 @@ #ifndef __LINUX_IOMMU_H #define __LINUX_IOMMU_H +#include + #define IOMMU_READ (1) #define IOMMU_WRITE (2) #define IOMMU_CACHE (4) /* DMA cache coherency */ @@ -41,6 +43,10 @@ struct iommu_ops { phys_addr_t paddr, int gfp_order, int prot); int (*unmap)(struct iommu_domain *domain, unsigned long iova, int gfp_order); + int (*map_range)(struct iommu_domain *domain, unsigned int iova, + struct scatterlist *sg, unsigned int len, int prot); + int (*unmap_range)(struct iommu_domain *domain, unsigned int iova, + unsigned int len); phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, unsigned long iova); int (*domain_has_cap)(struct iommu_domain *domain, @@ -61,6 +67,10 @@ extern int iommu_map(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, int gfp_order, int prot); extern int iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order); +extern int iommu_map_range(struct iommu_domain *domain, unsigned int iova, + struct scatterlist *sg, unsigned int len, int prot); +extern int iommu_unmap_range(struct iommu_domain *domain, unsigned int iova, + unsigned int len); extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, unsigned long iova); extern int iommu_domain_has_cap(struct iommu_domain *domain, @@ -109,6 +119,19 @@ static inline int iommu_unmap(struct iommu_domain *domain, unsigned long iova, return -ENODEV; } +static inline int iommu_map_range(struct iommu_domain *domain, + unsigned int iova, struct scatterlist *sg, + unsigned int len, int prot) +{ + return -ENODEV; +} + +static inline int iommu_unmap_range(struct iommu_domain *domain, + unsigned int iova, unsigned int len) +{ + return -ENODEV; +} + static inline phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, unsigned long iova) { From 7b27180dd33e89f13cdeca99b88d57acacc3eb99 Mon Sep 17 00:00:00 2001 From: Stepan Moskovchenko Date: Wed, 31 Aug 2011 17:13:32 -0700 Subject: [PATCH 2235/2556] drivers: iommu: Add flags to iommu_domain_alloc Add the ability to pass flags into the iommu_domain_alloc function to specify domain attributes. Change-Id: Ia33aa8d887a57f76d97ab8921385dcc551716846 Signed-off-by: Stepan Moskovchenko Conflicts: arch/arm/mach-msm/iommu_domains.c --- arch/arm/mach-msm/iommu.c | 2 +- arch/arm/mach-msm/iommu_domains.c | 253 ++++++++++++++++++++++++++++++ drivers/base/iommu.c | 4 +- include/linux/iommu.h | 6 +- virt/kvm/iommu.c | 2 +- 5 files changed, 260 insertions(+), 7 deletions(-) create mode 100644 arch/arm/mach-msm/iommu_domains.c diff --git a/arch/arm/mach-msm/iommu.c b/arch/arm/mach-msm/iommu.c index 329867f8547ed..446102453d7ee 100644 --- a/arch/arm/mach-msm/iommu.c +++ b/arch/arm/mach-msm/iommu.c @@ -176,7 +176,7 @@ static void __program_context(void __iomem *base, int ctx, phys_addr_t pgtable) SET_M(base, ctx, 1); } -static int msm_iommu_domain_init(struct iommu_domain *domain) +static int msm_iommu_domain_init(struct iommu_domain *domain, int flags) { struct msm_priv *priv = kzalloc(sizeof(*priv), GFP_KERNEL); diff --git a/arch/arm/mach-msm/iommu_domains.c b/arch/arm/mach-msm/iommu_domains.c new file mode 100644 index 0000000000000..290ba332b86d6 --- /dev/null +++ b/arch/arm/mach-msm/iommu_domains.c @@ -0,0 +1,253 @@ +/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct msm_iommu_domain { + int domain_idx; + int iova_pool_idx; +}; + +enum { + GLOBAL_DOMAIN, + VIDEO_DOMAIN, + EMPTY_DOMAIN, + MAX_DOMAINS +}; + +enum { + GLOBAL_MEMORY_POOL, + VIDEO_FIRMWARE_POOL, + VIDEO_ALLOC_POOL, +}; + +struct { + char *name; + int domain; +} msm_iommu_ctx_names[] = { + /* Camera */ + { + .name = "vpe_src", + .domain = GLOBAL_DOMAIN, + }, + /* Camera */ + { + .name = "vpe_dst", + .domain = GLOBAL_DOMAIN, + }, + /* Camera */ + { + .name = "vfe_imgwr", + .domain = GLOBAL_DOMAIN, + }, + /* Camera */ + { + .name = "vfe_misc", + .domain = GLOBAL_DOMAIN, + }, + /* Camera */ + { + .name = "ijpeg_src", + .domain = GLOBAL_DOMAIN, + }, + /* Camera */ + { + .name = "ijpeg_dst", + .domain = GLOBAL_DOMAIN, + }, + /* Camera */ + { + .name = "jpegd_src", + .domain = GLOBAL_DOMAIN, + }, + /* Camera */ + { + .name = "jpegd_dst", + .domain = GLOBAL_DOMAIN, + }, + /* Display */ + { + .name = "mdp_vg1", + .domain = GLOBAL_DOMAIN, + }, + /* Display */ + { + .name = "mdp_vg2", + .domain = GLOBAL_DOMAIN, + }, + /* Display */ + { + .name = "mdp_rgb1", + .domain = GLOBAL_DOMAIN, + }, + /* Display */ + { + .name = "mdp_rgb2", + .domain = GLOBAL_DOMAIN, + }, + /* Rotator */ + { + .name = "rot_src", + .domain = GLOBAL_DOMAIN, + }, + /* Rotator */ + { + .name = "rot_dst", + .domain = GLOBAL_DOMAIN, + }, + /* Video */ + { + .name = "vcodec_a_mm1", + .domain = VIDEO_DOMAIN, + }, + /* Video */ + { + .name = "vcodec_b_mm2", + .domain = VIDEO_DOMAIN, + }, + /* Video */ + { + .name = "vcodec_a_stream", + .domain = VIDEO_DOMAIN, + }, +}; + +static struct iommu_domain *msm_iommu_domains[MAX_DOMAINS]; + +static struct mem_pool msm_iommu_iova_pools[] = { + [GLOBAL_MEMORY_POOL] = { + .paddr = SZ_4K, + .size = SZ_2G - SZ_4K, + }, + /* + * The video hardware has several constraints: + * 1) The start address for firmware must be 128K aligned + * 2) The video firmware must exist at a lower address than + * all other video allocations + * 3) Video allocations cannot be more than 256MB away from the + * firmware + * + * Splitting the video pools makes sure that firmware will + * always be lower than regular allocations and the maximum + * size of 256MB will be enforced. + */ + [VIDEO_FIRMWARE_POOL] = { + .paddr = SZ_128K, + .size = SZ_16M - SZ_128K, + }, + [VIDEO_ALLOC_POOL] = { + .paddr = SZ_16M, + .size = SZ_256M - SZ_16M - SZ_128K, + } +}; + +static struct msm_iommu_domain msm_iommu_subsystems[] = { + [MSM_SUBSYSTEM_VIDEO] = { + .domain_idx = VIDEO_DOMAIN, + .iova_pool_idx = VIDEO_ALLOC_POOL, + }, + [MSM_SUBSYSTEM_VIDEO_FWARE] = { + .domain_idx = VIDEO_DOMAIN, + .iova_pool_idx = VIDEO_FIRMWARE_POOL, + }, + [MSM_SUBSYSTEM_CAMERA] = { + .domain_idx = GLOBAL_DOMAIN, + .iova_pool_idx = GLOBAL_MEMORY_POOL, + }, + [MSM_SUBSYSTEM_DISPLAY] = { + .domain_idx = GLOBAL_DOMAIN, + .iova_pool_idx = GLOBAL_MEMORY_POOL, + }, + [MSM_SUBSYSTEM_ROTATOR] = { + .domain_idx = GLOBAL_DOMAIN, + .iova_pool_idx = GLOBAL_MEMORY_POOL, + }, +}; + +struct iommu_domain *msm_subsystem_get_domain(int subsys_id) +{ + int id = msm_iommu_subsystems[subsys_id].domain_idx; + + return msm_iommu_domains[id]; +} + +struct mem_pool *msm_subsystem_get_pool(int subsys_id) +{ + int id = msm_iommu_subsystems[subsys_id].iova_pool_idx; + + return &msm_iommu_iova_pools[id]; +} + +static int __init msm_subsystem_iommu_init(void) +{ + int i; + + for (i = 0; i < (ARRAY_SIZE(msm_iommu_domains) - 1); i++) + msm_iommu_domains[i] = iommu_domain_alloc(0); + + for (i = 0; i < ARRAY_SIZE(msm_iommu_iova_pools); i++) { + mutex_init(&msm_iommu_iova_pools[i].pool_mutex); + msm_iommu_iova_pools[i].gpool = gen_pool_create(PAGE_SHIFT, -1); + + if (!msm_iommu_iova_pools[i].gpool) { + pr_err("%s: could not allocate iova pool. iommu" + " programming will not work with iova space" + " %d\n", __func__, i); + continue; + } + + if (gen_pool_add(msm_iommu_iova_pools[i].gpool, + msm_iommu_iova_pools[i].paddr, + msm_iommu_iova_pools[i].size, + -1)) { + pr_err("%s: could not add memory to iova pool. iommu" + " programming will not work with iova space" + " %d\n", __func__, i); + gen_pool_destroy(msm_iommu_iova_pools[i].gpool); + msm_iommu_iova_pools[i].gpool = NULL; + continue; + } + } + + for (i = 0; i < ARRAY_SIZE(msm_iommu_ctx_names); i++) { + int domain_idx; + struct device *ctx = msm_iommu_get_ctx( + msm_iommu_ctx_names[i].name); + + if (!ctx) + continue; + + domain_idx = msm_iommu_ctx_names[i].domain; + + if (!msm_iommu_domains[domain_idx]) + continue; + + if (iommu_attach_device(msm_iommu_domains[domain_idx], ctx)) { + pr_err("%s: could not attach domain %d to context %s." + " iommu programming will not occur.\n", + __func__, domain_idx, + msm_iommu_ctx_names[i].name); + msm_iommu_subsystems[i].domain_idx = EMPTY_DOMAIN; + continue; + } + } + + return 0; +} +device_initcall(msm_subsystem_iommu_init); diff --git a/drivers/base/iommu.c b/drivers/base/iommu.c index 6490bfe90d8b9..1377e854e05ba 100644 --- a/drivers/base/iommu.c +++ b/drivers/base/iommu.c @@ -40,7 +40,7 @@ bool iommu_found(void) } EXPORT_SYMBOL_GPL(iommu_found); -struct iommu_domain *iommu_domain_alloc(void) +struct iommu_domain *iommu_domain_alloc(int flags) { struct iommu_domain *domain; int ret; @@ -49,7 +49,7 @@ struct iommu_domain *iommu_domain_alloc(void) if (!domain) return NULL; - ret = iommu_ops->domain_init(domain); + ret = iommu_ops->domain_init(domain, flags); if (ret) goto out_free; diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 34b027c4fd5ac..d5330973a9fea 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -35,7 +35,7 @@ struct iommu_domain { #define IOMMU_CAP_INTR_REMAP 0x2 /* isolates device intrs */ struct iommu_ops { - int (*domain_init)(struct iommu_domain *domain); + int (*domain_init)(struct iommu_domain *domain, int flags); void (*domain_destroy)(struct iommu_domain *domain); int (*attach_dev)(struct iommu_domain *domain, struct device *dev); void (*detach_dev)(struct iommu_domain *domain, struct device *dev); @@ -57,7 +57,7 @@ struct iommu_ops { extern void register_iommu(struct iommu_ops *ops); extern bool iommu_found(void); -extern struct iommu_domain *iommu_domain_alloc(void); +extern struct iommu_domain *iommu_domain_alloc(int flags); extern void iommu_domain_free(struct iommu_domain *domain); extern int iommu_attach_device(struct iommu_domain *domain, struct device *dev); @@ -87,7 +87,7 @@ static inline bool iommu_found(void) return false; } -static inline struct iommu_domain *iommu_domain_alloc(void) +static inline struct iommu_domain *iommu_domain_alloc(int flags) { return NULL; } diff --git a/virt/kvm/iommu.c b/virt/kvm/iommu.c index 62a9caf0563c2..58a5e2b5b7e11 100644 --- a/virt/kvm/iommu.c +++ b/virt/kvm/iommu.c @@ -227,7 +227,7 @@ int kvm_iommu_map_guest(struct kvm *kvm) return -ENODEV; } - kvm->arch.iommu_domain = iommu_domain_alloc(); + kvm->arch.iommu_domain = iommu_domain_alloc(0); if (!kvm->arch.iommu_domain) return -ENOMEM; From 39a35ed596fcac445e6b59e328015bd9fa48f449 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 17 May 2012 00:23:25 -0500 Subject: [PATCH 2236/2556] msm: iommu: fix build for new kgsl --- arch/arm/mach-msm/include/mach/iommu.h | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/iommu.h b/arch/arm/mach-msm/include/mach/iommu.h index 296c0f10f230e..305af33e28a48 100644 --- a/arch/arm/mach-msm/include/mach/iommu.h +++ b/arch/arm/mach-msm/include/mach/iommu.h @@ -99,13 +99,6 @@ struct msm_iommu_ctx_drvdata { struct list_head attached_elm; }; -/* - * Look up an IOMMU context device by its context name. NULL if none found. - * Useful for testing and drivers that do not yet fully have IOMMU stuff in - * their platform devices. - */ -struct device *msm_iommu_get_ctx(const char *ctx_name); - /* * Interrupt handler for the IOMMU context fault interrupt. Hooking the * interrupt is not supported in the API yet, but this will print an error @@ -113,4 +106,18 @@ struct device *msm_iommu_get_ctx(const char *ctx_name); */ irqreturn_t msm_iommu_fault_handler(int irq, void *dev_id); +#ifdef CONFIG_MSM_IOMMU +/* + * Look up an IOMMU context device by its context name. NULL if none found. + * Useful for testing and drivers that do not yet fully have IOMMU stuff in + * their platform devices. + */ +struct device *msm_iommu_get_ctx(const char *ctx_name); +#else +static inline struct device *msm_iommu_get_ctx(const char *ctx_name) +{ + return NULL; +} +#endif + #endif From 2a5ab5cb0747ac787d803d707d5371d3aae4ced6 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 17 May 2012 00:39:51 -0500 Subject: [PATCH 2237/2556] kgsl: fastforward --> 3.8 commit d4bc9d2e0c3222c208f170ccf375bacfe9beb8fc Author: Jordan Crouse Date: Thu Nov 17 13:39:21 2011 -0700 msm: kgsl: Add timestamp events Add support for triggering asynchronous events when a timestamp expires. The infrastructure is generic enough to support different sorts of events and callbacks. The first user of the event infrastructure is a way to release a genlock locks on behalf of a user space process. Change-Id: Ic0dedbadfe67d98a5678453cbe0ac6996ba5c68f Signed-off-by: Jordan Crouse --- drivers/gpu/msm/Kconfig | 17 +- drivers/gpu/msm/adreno.c | 173 ++++---- drivers/gpu/msm/adreno.h | 33 +- drivers/gpu/msm/adreno_a2xx.c | 42 +- drivers/gpu/msm/adreno_debugfs.c | 15 +- drivers/gpu/msm/adreno_postmortem.c | 25 +- drivers/gpu/msm/adreno_ringbuffer.c | 33 +- drivers/gpu/msm/adreno_ringbuffer.h | 4 +- drivers/gpu/msm/kgsl.c | 469 +++++++++++++++++----- drivers/gpu/msm/kgsl.h | 24 +- drivers/gpu/msm/kgsl_cffdump.c | 2 +- drivers/gpu/msm/kgsl_device.h | 32 +- drivers/gpu/msm/kgsl_drm.c | 27 -- drivers/gpu/msm/kgsl_gpummu.c | 69 ++-- drivers/gpu/msm/kgsl_iommu.c | 56 +-- drivers/gpu/msm/kgsl_mmu.c | 39 +- drivers/gpu/msm/kgsl_mmu.h | 6 - drivers/gpu/msm/kgsl_pwrctrl.c | 120 ++++-- drivers/gpu/msm/kgsl_pwrctrl.h | 10 +- drivers/gpu/msm/kgsl_pwrscale.c | 5 +- drivers/gpu/msm/kgsl_pwrscale_idlestats.c | 95 ++++- drivers/gpu/msm/kgsl_pwrscale_trustzone.c | 2 +- drivers/gpu/msm/kgsl_sharedmem.c | 350 +++++++++------- drivers/gpu/msm/kgsl_sharedmem.h | 39 +- drivers/gpu/msm/z180.c | 22 +- include/linux/msm_kgsl.h | 33 +- 26 files changed, 1120 insertions(+), 622 deletions(-) diff --git a/drivers/gpu/msm/Kconfig b/drivers/gpu/msm/Kconfig index 5852e2698b33e..ba63fbcbbb408 100644 --- a/drivers/gpu/msm/Kconfig +++ b/drivers/gpu/msm/Kconfig @@ -64,24 +64,10 @@ config MSM_KGSL_DRM bool "Build a DRM interface for the MSM_KGSL driver" depends on MSM_KGSL && DRM -config MSM_KGSL_GPUMMU - bool "Enable the GPU MMU in the MSM_KGSL driver" - depends on MSM_KGSL && !MSM_KGSL_CFF_DUMP - default y - -config MSM_KGSL_IOMMU - bool "Enable the use of IOMMU in the MSM_KGSL driver" - depends on MSM_KGSL && MSM_IOMMU && !MSM_KGSL_GPUMMU && !MSM_KGSL_CFF_DUMP - -config MSM_KGSL_MMU - bool - depends on MSM_KGSL_GPUMMU || MSM_KGSL_IOMMU - default y - config KGSL_PER_PROCESS_PAGE_TABLE bool "Enable Per Process page tables for the KGSL driver" default n - depends on MSM_KGSL_GPUMMU && !MSM_KGSL_DRM + depends on !MSM_KGSL_DRM ---help--- The MMU will use per process pagetables when enabled. @@ -105,7 +91,6 @@ config MSM_KGSL_PAGE_TABLE_COUNT config MSM_KGSL_MMU_PAGE_FAULT bool "Force the GPU MMU to page fault for unmapped regions" default y - depends on MSM_KGSL_GPUMMU config MSM_KGSL_DISABLE_SHADOW_WRITES bool "Disable register shadow writes for context switches" diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 247abf5bc1892..d485cd2d0945e 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -66,9 +66,6 @@ | (MMU_CONFIG << MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT) \ | (MMU_CONFIG << MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT)) -/* max msecs to wait for gpu to finish its operation(s) */ -#define MAX_WAITGPU_SECS (HZ + HZ/2) - static const struct kgsl_functable adreno_functable; static struct adreno_device device_3d0 = { @@ -117,8 +114,10 @@ static struct adreno_device device_3d0 = { }, .pfp_fw = NULL, .pm4_fw = NULL, + .wait_timeout = 10000, /* in milliseconds */ }; + /* * This is the master list of all GPU cores that are supported by this * driver. @@ -128,19 +127,32 @@ static struct adreno_device device_3d0 = { static const struct { enum adreno_gpurev gpurev; - unsigned int core, major, minor; + unsigned int core, major, minor, patchid; const char *pm4fw; const char *pfpfw; struct adreno_gpudev *gpudev; + unsigned int istore_size; + unsigned int pix_shader_start; } adreno_gpulist[] = { - { ADRENO_REV_A200, 0, 2, ANY_ID, - "yamato_pm4.fw", "yamato_pfp.fw", &adreno_a2xx_gpudev }, - { ADRENO_REV_A205, 0, 1, 0, - "yamato_pm4.fw", "yamato_pfp.fw", &adreno_a2xx_gpudev }, - { ADRENO_REV_A220, 2, 1, ANY_ID, - "leia_pm4_470.fw", "leia_pfp_470.fw", &adreno_a2xx_gpudev }, - { ADRENO_REV_A225, 2, 2, ANY_ID, - "a225_pm4.fw", "a225_pfp.fw", &adreno_a2xx_gpudev }, + { ADRENO_REV_A200, 0, 2, ANY_ID, ANY_ID, + "yamato_pm4.fw", "yamato_pfp.fw", &adreno_a2xx_gpudev, + 512, 384}, + { ADRENO_REV_A205, 0, 1, 0, ANY_ID, + "yamato_pm4.fw", "yamato_pfp.fw", &adreno_a2xx_gpudev, + 512, 384}, + { ADRENO_REV_A220, 2, 1, ANY_ID, ANY_ID, + "leia_pm4_470.fw", "leia_pfp_470.fw", &adreno_a2xx_gpudev, + 512, 384}, + /* + * patchlevel 5 (8960v2) needs special pm4 firmware to work around + * a hardware problem. + */ + { ADRENO_REV_A225, 2, 2, 0, 5, + "a225p5_pm4.fw", "a225_pfp.fw", &adreno_a2xx_gpudev, + 1536, 768 }, + { ADRENO_REV_A225, 2, 2, ANY_ID, ANY_ID, + "a225_pm4.fw", "a225_pfp.fw", &adreno_a2xx_gpudev, + 1536, 768 }, }; static void adreno_gmeminit(struct adreno_device *adreno_dev) @@ -356,16 +368,11 @@ adreno_getchipid(struct kgsl_device *device) * adreno 22x gpus are indicated by coreid 2, * but REG_RBBM_PERIPHID1 always contains 0 for this field */ - if (cpu_is_msm8960() || cpu_is_msm8x60()) + if (cpu_is_msm8960() || cpu_is_msm8x60() || cpu_is_msm8930()) chipid = 2 << 24; else chipid = (coreid & 0xF) << 24; - if (cpu_is_msm8960()) { - KGSL_DRV_ERR(device, "forcing a220 chipid\n"); - majorid = 1<<4; - } - chipid |= ((majorid >> 4) & 0xF) << 16; minorid = ((revid >> 0) & 0xFF); @@ -389,20 +396,21 @@ static inline bool _rev_match(unsigned int id, unsigned int entry) static void adreno_identify_gpu(struct adreno_device *adreno_dev) { - unsigned int i, core, major, minor; + unsigned int i, core, major, minor, patchid; adreno_dev->chip_id = adreno_getchipid(&adreno_dev->dev); core = (adreno_dev->chip_id >> 24) & 0xff; major = (adreno_dev->chip_id >> 16) & 0xff; minor = (adreno_dev->chip_id >> 8) & 0xff; + patchid = (adreno_dev->chip_id & 0xff); for (i = 0; i < ARRAY_SIZE(adreno_gpulist); i++) { if (core == adreno_gpulist[i].core && _rev_match(major, adreno_gpulist[i].major) && - _rev_match(minor, adreno_gpulist[i].minor)) { + _rev_match(minor, adreno_gpulist[i].minor) && + _rev_match(patchid, adreno_gpulist[i].patchid)) break; - } } if (i == ARRAY_SIZE(adreno_gpulist)) { @@ -414,6 +422,8 @@ adreno_identify_gpu(struct adreno_device *adreno_dev) adreno_dev->gpudev = adreno_gpulist[i].gpudev; adreno_dev->pfp_fwfile = adreno_gpulist[i].pfpfw; adreno_dev->pm4_fwfile = adreno_gpulist[i].pm4fw; + adreno_dev->istore_size = adreno_gpulist[i].istore_size; + adreno_dev->pix_shader_start = adreno_gpulist[i].pix_shader_start; } static int __devinit @@ -535,7 +545,7 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) adreno_regwrite(device, REG_SQ_VS_PROGRAM, 0x00000000); adreno_regwrite(device, REG_SQ_PS_PROGRAM, 0x00000000); - if (cpu_is_msm8960()) + if (cpu_is_msm8960() || cpu_is_msm8930()) adreno_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0x200); else adreno_regwrite(device, REG_RBBM_PM_OVERRIDE1, 0); @@ -551,10 +561,7 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), init_reftimestamp); - if (adreno_is_a20x(adreno_dev)) - adreno_regwrite(device, REG_RBBM_DEBUG, 0x000C0000); - else - adreno_regwrite(device, REG_RBBM_DEBUG, 0x00080000); + adreno_regwrite(device, REG_RBBM_DEBUG, 0x00080000); /* Make sure interrupts are disabled */ @@ -830,13 +837,21 @@ static int adreno_getproperty(struct kgsl_device *device, return status; } +static inline void adreno_poke(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + adreno_regwrite(device, REG_CP_RB_WPTR, adreno_dev->ringbuffer.wptr); +} + /* Caller must hold the device mutex. */ int adreno_idle(struct kgsl_device *device, unsigned int timeout) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; unsigned int rbbm_status; - unsigned long wait_time = jiffies + MAX_WAITGPU_SECS; + unsigned long wait_timeout = + msecs_to_jiffies(adreno_dev->wait_timeout); + unsigned long wait_time = jiffies + wait_timeout; kgsl_cffdump_regpoll(device->id, REG_RBBM_STATUS << 2, 0x00000000, 0x80000000); @@ -846,6 +861,7 @@ int adreno_idle(struct kgsl_device *device, unsigned int timeout) retry: if (rb->flags & KGSL_FLAGS_STARTED) { do { + adreno_poke(device); GSL_RB_GET_READPTR(rb, &rb->rptr); if (time_after(jiffies, wait_time)) { KGSL_DRV_ERR(device, "rptr: %x, wptr: %x\n", @@ -856,7 +872,7 @@ int adreno_idle(struct kgsl_device *device, unsigned int timeout) } /* now, wait for the GPU to finish its operations */ - wait_time = jiffies + MAX_WAITGPU_SECS; + wait_time = jiffies + wait_timeout; while (time_before(jiffies, wait_time)) { adreno_regread(device, REG_RBBM_STATUS, &rbbm_status); if (rbbm_status == 0x110) @@ -866,7 +882,7 @@ int adreno_idle(struct kgsl_device *device, unsigned int timeout) err: KGSL_DRV_ERR(device, "spun too long waiting for RB to idle\n"); if (!adreno_dump_and_recover(device)) { - wait_time = jiffies + MAX_WAITGPU_SECS; + wait_time = jiffies + wait_timeout; goto retry; } return -ETIMEDOUT; @@ -1019,7 +1035,7 @@ static int kgsl_check_interrupt_timestamp(struct kgsl_device *device, kgsl_sharedmem_readl(&device->memstore, &ref_ts, KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts)); mb(); - if (timestamp_cmp(ref_ts, timestamp)) { + if (timestamp_cmp(ref_ts, timestamp) >= 0) { kgsl_sharedmem_writel(&device->memstore, KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), timestamp); @@ -1071,58 +1087,77 @@ static int adreno_waittimestamp(struct kgsl_device *device, { long status = 0; uint io = 1; + static uint io_cnt; struct adreno_device *adreno_dev = ADRENO_DEVICE(device); struct kgsl_pwrctrl *pwr = &device->pwrctrl; + int retries; + unsigned int msecs_first; + unsigned int msecs_part; + + /* Don't wait forever, set a max value for now */ + if (msecs == -1) + msecs = adreno_dev->wait_timeout; - if (timestamp != adreno_dev->ringbuffer.timestamp && - timestamp_cmp(timestamp, - adreno_dev->ringbuffer.timestamp)) { + if (timestamp_cmp(timestamp, adreno_dev->ringbuffer.timestamp) > 0) { KGSL_DRV_ERR(device, "Cannot wait for invalid ts: %x, " "rb->timestamp: %x\n", timestamp, adreno_dev->ringbuffer.timestamp); status = -EINVAL; goto done; } - if (!kgsl_check_timestamp(device, timestamp)) { - if (pwr->active_pwrlevel) { - int low_pwrlevel = pwr->num_pwrlevels - - KGSL_PWRLEVEL_LOW_OFFSET; - if (pwr->active_pwrlevel == low_pwrlevel) - io = 0; - } - mutex_unlock(&device->mutex); - /* We need to make sure that the process is placed in wait-q - * before its condition is called */ - status = kgsl_wait_event_interruptible_timeout( - device->wait_queue, - kgsl_check_interrupt_timestamp(device, - timestamp), - msecs_to_jiffies(msecs), io); - mutex_lock(&device->mutex); - if (status > 0) - status = 0; - else if (status == 0) { - if (!kgsl_check_timestamp(device, timestamp)) { - status = -ETIMEDOUT; - KGSL_DRV_ERR(device, - "Device hang detected while waiting " - "for timestamp: %x, last " - "submitted(rb->timestamp): %x, wptr: " - "%x\n", timestamp, - adreno_dev->ringbuffer.timestamp, - adreno_dev->ringbuffer.wptr); - if (!adreno_dump_and_recover(device)) { - /* wait for idle after recovery as the - * timestamp that this process wanted - * to wait on may be invalid */ - if (!adreno_idle(device, - KGSL_TIMEOUT_DEFAULT)) - status = 0; - } + /* Keep the first timeout as 100msecs before rewriting + * the WPTR. Less visible impact if the WPTR has not + * been updated properly. + */ + msecs_first = (msecs <= 100) ? ((msecs + 4) / 5) : 100; + msecs_part = (msecs - msecs_first + 3) / 4; + for (retries = 0; retries < 5; retries++) { + if (!kgsl_check_timestamp(device, timestamp)) { + adreno_poke(device); + io_cnt = (io_cnt + 1) % 100; + if (io_cnt < + pwr->pwrlevels[pwr->active_pwrlevel]. + io_fraction) + io = 0; + mutex_unlock(&device->mutex); + /* We need to make sure that the process is + * placed in wait-q before its condition is called + */ + status = kgsl_wait_event_interruptible_timeout( + device->wait_queue, + kgsl_check_interrupt_timestamp(device, + timestamp), + msecs_to_jiffies(retries ? + msecs_part : msecs_first), io); + mutex_lock(&device->mutex); + + if (status > 0) { + status = 0; + goto done; } } } + if (!kgsl_check_timestamp(device, timestamp)) { + status = -ETIMEDOUT; + KGSL_DRV_ERR(device, + "Device hang detected while waiting " + "for timestamp: %x, last " + "submitted(rb->timestamp): %x, wptr: " + "%x\n", timestamp, + adreno_dev->ringbuffer.timestamp, + adreno_dev->ringbuffer.wptr); + if (!adreno_dump_and_recover(device)) { + /* wait for idle after recovery as the + * timestamp that this process wanted + * to wait on may be invalid */ + if (!adreno_idle(device, + KGSL_TIMEOUT_DEFAULT)) + status = 0; + } + } else { + status = 0; + } done: return (int)status; diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index 94b96a1d00da9..0776a240a1867 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -38,6 +38,12 @@ #define ADRENO_DEFAULT_PWRSCALE_POLICY NULL #endif +/* + * constants for the size of shader instructions + */ +#define ADRENO_ISTORE_BYTES 12 +#define ADRENO_ISTORE_WORDS 3 + enum adreno_gpurev { ADRENO_REV_UNKNOWN = 0, ADRENO_REV_A200 = 200, @@ -54,7 +60,6 @@ struct adreno_device { enum adreno_gpurev gpurev; struct kgsl_memregion gmemspace; struct adreno_context *drawctxt_active; - wait_queue_head_t ib1_wq; const char *pfp_fwfile; unsigned int *pfp_fw; size_t pfp_fw_size; @@ -64,6 +69,9 @@ struct adreno_device { struct adreno_ringbuffer ringbuffer; unsigned int mharb; struct adreno_gpudev *gpudev; + unsigned int wait_timeout; + unsigned int istore_size; + unsigned int pix_shader_start; }; struct adreno_gpudev { @@ -125,5 +133,28 @@ static inline int adreno_is_a2xx(struct adreno_device *adreno_dev) return (adreno_dev->gpurev <= ADRENO_REV_A225); } +/** + * adreno_encode_istore_size - encode istore size in CP format + * @adreno_dev - The 3D device. + * + * Encode the istore size into the format expected that the + * CP_SET_SHADER_BASES and CP_ME_INIT commands: + * bits 31:29 - istore size as encoded by this function + * bits 27:16 - vertex shader start offset in instructions + * bits 11:0 - pixel shader start offset in instructions. + */ +static inline int adreno_encode_istore_size(struct adreno_device *adreno_dev) +{ + unsigned int size; + /* in a225 the CP microcode multiplies the encoded + * value by 3 while decoding. + */ + if (adreno_is_a225(adreno_dev)) + size = adreno_dev->istore_size/3; + else + size = adreno_dev->istore_size; + + return (ilog2(size) - 5) << 29; +} #endif /*__ADRENO_H */ diff --git a/drivers/gpu/msm/adreno_a2xx.c b/drivers/gpu/msm/adreno_a2xx.c index 8a7ab3506b1c8..600384612f646 100644 --- a/drivers/gpu/msm/adreno_a2xx.c +++ b/drivers/gpu/msm/adreno_a2xx.c @@ -72,10 +72,6 @@ #define TEX_CONSTANTS (32*6) /* DWORDS */ #define BOOL_CONSTANTS 8 /* DWORDS */ #define LOOP_CONSTANTS 56 /* DWORDS */ -#define SHADER_INSTRUCT_LOG2 9U /* 2^n == SHADER_INSTRUCTIONS */ - -/* 96-bit instructions */ -#define SHADER_INSTRUCT (1<istore_size*ADRENO_ISTORE_BYTES; +} + +static inline int _context_size(struct adreno_device *adreno_dev) +{ + return SHADER_OFFSET + 3*_shader_shadow_size(adreno_dev); +} /* A scratchpad used to build commands during context create */ @@ -601,7 +604,8 @@ static unsigned int *build_gmem2sys_cmds(struct adreno_device *adreno_dev, *cmds++ = 0x00003F00; *cmds++ = cp_type3_packet(CP_SET_SHADER_BASES, 1); - *cmds++ = (0x80000000) | 0x180; + *cmds++ = adreno_encode_istore_size(adreno_dev) + | adreno_dev->pix_shader_start; /* load the patched vertex shader stream */ cmds = program_shader(cmds, 0, gmem2sys_vtx_pgm, GMEM2SYS_VTX_PGM_LEN); @@ -802,7 +806,8 @@ static unsigned int *build_sys2gmem_cmds(struct adreno_device *adreno_dev, *cmds++ = 0x00000300; /* 0x100 = Vertex, 0x200 = Pixel */ *cmds++ = cp_type3_packet(CP_SET_SHADER_BASES, 1); - *cmds++ = (0x80000000) | 0x180; + *cmds++ = adreno_encode_istore_size(adreno_dev) + | adreno_dev->pix_shader_start; /* Load the patched fragment shader stream */ cmds = @@ -1089,7 +1094,8 @@ static void build_regrestore_cmds(struct adreno_device *adreno_dev, } static void -build_shader_save_restore_cmds(struct adreno_context *drawctxt) +build_shader_save_restore_cmds(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt) { unsigned int *cmd = tmp_ctx.cmd; unsigned int *save, *restore, *fixup; @@ -1099,8 +1105,10 @@ build_shader_save_restore_cmds(struct adreno_context *drawctxt) /* compute vertex, pixel and shared instruction shadow GPU addresses */ tmp_ctx.shader_vertex = drawctxt->gpustate.gpuaddr + SHADER_OFFSET; - tmp_ctx.shader_pixel = tmp_ctx.shader_vertex + SHADER_SHADOW_SIZE; - tmp_ctx.shader_shared = tmp_ctx.shader_pixel + SHADER_SHADOW_SIZE; + tmp_ctx.shader_pixel = tmp_ctx.shader_vertex + + _shader_shadow_size(adreno_dev); + tmp_ctx.shader_shared = tmp_ctx.shader_pixel + + _shader_shadow_size(adreno_dev); /* restore shader partitioning and instructions */ @@ -1156,8 +1164,8 @@ build_shader_save_restore_cmds(struct adreno_context *drawctxt) *cmd++ = REG_SCRATCH_REG2; /* AND off invalid bits. */ *cmd++ = 0x0FFF0FFF; - /* OR in instruction memory size */ - *cmd++ = (unsigned int)((SHADER_INSTRUCT_LOG2 - 5U) << 29); + /* OR in instruction memory size. */ + *cmd++ = adreno_encode_istore_size(adreno_dev); /* write the computed value to the SET_SHADER_BASES data field */ *cmd++ = cp_type3_packet(CP_REG_TO_MEM, 2); @@ -1226,7 +1234,7 @@ static int a2xx_ctxt_gpustate_shadow(struct adreno_device *adreno_dev, /* Allocate vmalloc memory to store the gpustate */ result = kgsl_allocate(&drawctxt->gpustate, - drawctxt->pagetable, CONTEXT_SIZE); + drawctxt->pagetable, _context_size(adreno_dev)); if (result) return result; @@ -1234,7 +1242,8 @@ static int a2xx_ctxt_gpustate_shadow(struct adreno_device *adreno_dev, drawctxt->flags |= CTXT_FLAGS_STATE_SHADOW; /* Blank out h/w register, constant, and command buffer shadows. */ - kgsl_sharedmem_set(&drawctxt->gpustate, 0, 0, CONTEXT_SIZE); + kgsl_sharedmem_set(&drawctxt->gpustate, 0, 0, + _context_size(adreno_dev)); /* set-up command and vertex buffer pointers */ tmp_ctx.cmd = tmp_ctx.start @@ -1245,7 +1254,7 @@ static int a2xx_ctxt_gpustate_shadow(struct adreno_device *adreno_dev, build_regrestore_cmds(adreno_dev, drawctxt); build_regsave_cmds(adreno_dev, drawctxt); - build_shader_save_restore_cmds(drawctxt); + build_shader_save_restore_cmds(adreno_dev, drawctxt); kgsl_cache_range_op(&drawctxt->gpustate, KGSL_CACHE_OP_FLUSH); @@ -1520,6 +1529,7 @@ static void a2xx_cp_intrcallback(struct kgsl_device *device) if (status & (CP_INT_CNTL__IB1_INT_MASK | CP_INT_CNTL__RB_INT_MASK)) { KGSL_CMD_WARN(rb->device, "ringbuffer ib1/rb interrupt\n"); + queue_work(device->work_queue, &device->ts_expired_ws); wake_up_interruptible_all(&device->wait_queue); atomic_notifier_call_chain(&(device->ts_notifier_list), device->id, diff --git a/drivers/gpu/msm/adreno_debugfs.c b/drivers/gpu/msm/adreno_debugfs.c index b9178495adc76..419ce9d2287b7 100644 --- a/drivers/gpu/msm/adreno_debugfs.c +++ b/drivers/gpu/msm/adreno_debugfs.c @@ -223,21 +223,23 @@ static int kgsl_regread_nolock(struct kgsl_device *device, return 0; } -#define KGSL_ISTORE_START 0x5000 -#define KGSL_ISTORE_LENGTH 0x600 +#define ADRENO_ISTORE_START 0x5000 static ssize_t kgsl_istore_read( struct file *file, char __user *buff, size_t buff_count, loff_t *ppos) { - int i, count = KGSL_ISTORE_LENGTH, remaining, pos = 0, tot = 0; + int i, count, remaining, pos = 0, tot = 0; struct kgsl_device *device = file->private_data; const int rowc = 8; + struct adreno_device *adreno_dev; if (!ppos || !device) return 0; + adreno_dev = ADRENO_DEVICE(device); + count = adreno_dev->istore_size * ADRENO_ISTORE_WORDS; remaining = count; for (i = 0; i < count; i += rowc) { unsigned int vals[rowc]; @@ -248,7 +250,8 @@ static ssize_t kgsl_istore_read( if (pos >= *ppos) { for (j = 0; j < linec; ++j) kgsl_regread_nolock(device, - KGSL_ISTORE_START+i+j, vals+j); + ADRENO_ISTORE_START + i + j, + vals + j); } else memset(vals, 0, sizeof(vals)); @@ -421,6 +424,8 @@ static const struct file_operations kgsl_mh_debug_fops = { void adreno_debugfs_init(struct kgsl_device *device) { + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + if (!device->d_debugfs || IS_ERR(device->d_debugfs)) return; @@ -436,6 +441,8 @@ void adreno_debugfs_init(struct kgsl_device *device) &kgsl_mh_debug_fops); debugfs_create_file("cff_dump", 0644, device->d_debugfs, device, &kgsl_cff_dump_enable_fops); + debugfs_create_u32("wait_timeout", 0644, device->d_debugfs, + &adreno_dev->wait_timeout); /* Create post mortem control files */ diff --git a/drivers/gpu/msm/adreno_postmortem.c b/drivers/gpu/msm/adreno_postmortem.c index 7c7f0dcc4b57f..cc693609b795b 100644 --- a/drivers/gpu/msm/adreno_postmortem.c +++ b/drivers/gpu/msm/adreno_postmortem.c @@ -25,6 +25,7 @@ #include "a2xx_reg.h" #define INVALID_RB_CMD 0xaaaaaaaa +#define NUM_DWORDS_OF_RINGBUFFER_HISTORY 100 struct pm_id_name { uint32_t id; @@ -536,7 +537,12 @@ static int adreno_dump(struct kgsl_device *device) kgsl_regread(device, REG_CP_RB_RPTR_ADDR, &r3); KGSL_LOG_DUMP(device, "CP_RB: BASE = %08X | CNTL = %08X | RPTR_ADDR = %08X" - "\n", cp_rb_base, r2, r3); + " | rb_count = %08X\n", cp_rb_base, r2, r3, rb_count); + { + struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; + if (rb->sizedwords != rb_count) + rb_count = rb->sizedwords; + } kgsl_regread(device, REG_CP_RB_RPTR, &cp_rb_rptr); kgsl_regread(device, REG_CP_RB_WPTR, &cp_rb_wptr); @@ -683,13 +689,13 @@ static int adreno_dump(struct kgsl_device *device) goto error_vfree; } - read_idx = (int)cp_rb_rptr - 64; + read_idx = (int)cp_rb_rptr - NUM_DWORDS_OF_RINGBUFFER_HISTORY; if (read_idx < 0) read_idx += rb_count; write_idx = (int)cp_rb_wptr + 16; if (write_idx > rb_count) write_idx -= rb_count; - num_item += 64+16; + num_item += NUM_DWORDS_OF_RINGBUFFER_HISTORY+16; if (num_item > rb_count) num_item = rb_count; if (write_idx >= read_idx) @@ -736,7 +742,7 @@ static int adreno_dump(struct kgsl_device *device) the process in whose context the GPU hung */ cur_pt_base = pt_base; - read_idx = (int)cp_rb_rptr - 64; + read_idx = (int)cp_rb_rptr - NUM_DWORDS_OF_RINGBUFFER_HISTORY; if (read_idx < 0) read_idx += rb_count; KGSL_LOG_DUMP(device, @@ -745,13 +751,14 @@ static int adreno_dump(struct kgsl_device *device) adreno_dump_rb(device, rb_copy, num_item<<2, read_idx, rb_count); if (adreno_ib_dump_enabled()) { - for (read_idx = 64; read_idx >= 0; --read_idx) { + for (read_idx = NUM_DWORDS_OF_RINGBUFFER_HISTORY; + read_idx >= 0; --read_idx) { uint32_t this_cmd = rb_copy[read_idx]; if (this_cmd == cp_type3_packet( CP_INDIRECT_BUFFER_PFD, 2)) { uint32_t ib_addr = rb_copy[read_idx+1]; uint32_t ib_size = rb_copy[read_idx+2]; - if (cp_ib1_bufsz && cp_ib1_base == ib_addr) { + if (ib_size && cp_ib1_base == ib_addr) { KGSL_LOG_DUMP(device, "IB1: base:%8.8X " "count:%d\n", ib_addr, ib_size); @@ -762,9 +769,9 @@ static int adreno_dump(struct kgsl_device *device) } } for (i = 0; i < ib_list.count; ++i) { - if (cp_ib2_bufsz && cp_ib2_base == ib_list.bases[i]) { - uint32_t ib_size = ib_list.sizes[i]; - uint32_t ib_offset = ib_list.offsets[i]; + uint32_t ib_size = ib_list.sizes[i]; + uint32_t ib_offset = ib_list.offsets[i]; + if (ib_size && cp_ib2_base == ib_list.bases[i]) { KGSL_LOG_DUMP(device, "IB2: base:%8.8X count:%d\n", cp_ib2_base, ib_size); diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index 639f2196d2972..a0982006798fa 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -61,7 +61,7 @@ static void adreno_ringbuffer_submit(struct adreno_ringbuffer *rb) adreno_regwrite(rb->device, REG_CP_RB_WPTR, rb->wptr); } -static int +static void adreno_ringbuffer_waitspace(struct adreno_ringbuffer *rb, unsigned int numcmds, int wptr_ahead) { @@ -102,8 +102,6 @@ adreno_ringbuffer_waitspace(struct adreno_ringbuffer *rb, unsigned int numcmds, freecmds = rb->rptr - rb->wptr; } while ((freecmds != 0) && (freecmds <= numcmds)); - - return 0; } @@ -111,7 +109,6 @@ static unsigned int *adreno_ringbuffer_allocspace(struct adreno_ringbuffer *rb, unsigned int numcmds) { unsigned int *ptr = NULL; - int status = 0; BUG_ON(numcmds >= rb->sizedwords); @@ -122,22 +119,20 @@ static unsigned int *adreno_ringbuffer_allocspace(struct adreno_ringbuffer *rb, /* reserve dwords for nop packet */ if ((rb->wptr + numcmds) > (rb->sizedwords - GSL_RB_NOP_SIZEDWORDS)) - status = adreno_ringbuffer_waitspace(rb, numcmds, 1); + adreno_ringbuffer_waitspace(rb, numcmds, 1); } else { /* wptr behind rptr */ if ((rb->wptr + numcmds) >= rb->rptr) - status = adreno_ringbuffer_waitspace(rb, numcmds, 0); + adreno_ringbuffer_waitspace(rb, numcmds, 0); /* check for remaining space */ /* reserve dwords for nop packet */ if ((rb->wptr + numcmds) > (rb->sizedwords - GSL_RB_NOP_SIZEDWORDS)) - status = adreno_ringbuffer_waitspace(rb, numcmds, 1); + adreno_ringbuffer_waitspace(rb, numcmds, 1); } - if (status == 0) { - ptr = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr; - rb->wptr += numcmds; - } + ptr = (unsigned int *)rb->buffer_desc.hostptr + rb->wptr; + rb->wptr += numcmds; return ptr; } @@ -251,6 +246,7 @@ int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, unsigned int init_ram) union reg_cp_rb_cntl cp_rb_cntl; unsigned int *cmds, rb_cntl; struct kgsl_device *device = rb->device; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); uint cmds_gpu; if (rb->flags & KGSL_FLAGS_STARTED) @@ -362,9 +358,10 @@ int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, unsigned int init_ram) GSL_RB_WRITE(cmds, cmds_gpu, SUBBLOCK_OFFSET(REG_PA_SU_POLY_OFFSET_FRONT_SCALE)); - /* Vertex and Pixel Shader Start Addresses in instructions - * (3 DWORDS per instruction) */ - GSL_RB_WRITE(cmds, cmds_gpu, 0x80000180); + /* Instruction memory size: */ + GSL_RB_WRITE(cmds, cmds_gpu, + (adreno_encode_istore_size(adreno_dev) + | adreno_dev->pix_shader_start)); /* Maximum Contexts */ GSL_RB_WRITE(cmds, cmds_gpu, 0x00000001); /* Write Confirm Interval and The CP will wait the @@ -391,7 +388,7 @@ int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, unsigned int init_ram) return status; } -int adreno_ringbuffer_stop(struct adreno_ringbuffer *rb) +void adreno_ringbuffer_stop(struct adreno_ringbuffer *rb) { if (rb->flags & KGSL_FLAGS_STARTED) { /* ME_HALT */ @@ -399,8 +396,6 @@ int adreno_ringbuffer_stop(struct adreno_ringbuffer *rb) rb->flags &= ~KGSL_FLAGS_STARTED; } - - return 0; } int adreno_ringbuffer_init(struct kgsl_device *device) @@ -443,7 +438,7 @@ int adreno_ringbuffer_init(struct kgsl_device *device) return 0; } -int adreno_ringbuffer_close(struct adreno_ringbuffer *rb) +void adreno_ringbuffer_close(struct adreno_ringbuffer *rb) { struct adreno_device *adreno_dev = ADRENO_DEVICE(rb->device); @@ -457,8 +452,6 @@ int adreno_ringbuffer_close(struct adreno_ringbuffer *rb) adreno_dev->pm4_fw = NULL; memset(rb, 0, sizeof(struct adreno_ringbuffer)); - - return 0; } static uint32_t diff --git a/drivers/gpu/msm/adreno_ringbuffer.h b/drivers/gpu/msm/adreno_ringbuffer.h index 487a74858ee64..a90b0cb9b8b8b 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.h +++ b/drivers/gpu/msm/adreno_ringbuffer.h @@ -117,9 +117,9 @@ int adreno_ringbuffer_init(struct kgsl_device *device); int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, unsigned int init_ram); -int adreno_ringbuffer_stop(struct adreno_ringbuffer *rb); +void adreno_ringbuffer_stop(struct adreno_ringbuffer *rb); -int adreno_ringbuffer_close(struct adreno_ringbuffer *rb); +void adreno_ringbuffer_close(struct adreno_ringbuffer *rb); void adreno_ringbuffer_issuecmds(struct kgsl_device *device, unsigned int flags, diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 5d20d9b1414a1..53b9e8a99f0e4 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -20,9 +20,12 @@ #include #include #include +#include #include #include +#include +#include #include "kgsl.h" #include "kgsl_debugfs.h" @@ -43,6 +46,65 @@ module_param_named(mmutype, ksgl_mmu_type, charp, 0); MODULE_PARM_DESC(ksgl_mmu_type, "Type of MMU to be used for graphics. Valid values are 'iommu' or 'gpummu' or 'nommu'"); +static struct ion_client *kgsl_ion_client; + +#ifdef CONFIG_GENLOCK + +/** + * kgsl_add_event - Add a new timstamp event for the KGSL device + * @device - KGSL device for the new event + * @ts - the timestamp to trigger the event on + * @cb - callback function to call when the timestamp expires + * @priv - private data for the specific event type + * + * @returns - 0 on success or error code on failure + */ + +static int kgsl_add_event(struct kgsl_device *device, u32 ts, + void (*cb)(struct kgsl_device *, void *, u32), void *priv) +{ + struct kgsl_event *event; + struct list_head *n; + unsigned int cur = device->ftbl->readtimestamp(device, + KGSL_TIMESTAMP_RETIRED); + + if (cb == NULL) + return -EINVAL; + + /* Check to see if the requested timestamp has already fired */ + + if (timestamp_cmp(cur, ts) >= 0) { + cb(device, priv, cur); + return 0; + } + + event = kzalloc(sizeof(*event), GFP_KERNEL); + if (event == NULL) + return -ENOMEM; + + event->timestamp = ts; + event->priv = priv; + event->func = cb; + + /* Add the event in order to the list */ + + for (n = device->events.next ; n != &device->events; n = n->next) { + struct kgsl_event *e = + list_entry(n, struct kgsl_event, list); + + if (timestamp_cmp(e->timestamp, ts) > 0) { + list_add(&event->list, n->prev); + break; + } + } + + if (n == &device->events) + list_add_tail(&event->list, &device->events); + + return 0; +} +#endif + static inline struct kgsl_mem_entry * kgsl_mem_entry_create(void) { @@ -62,18 +124,34 @@ kgsl_mem_entry_destroy(struct kref *kref) struct kgsl_mem_entry *entry = container_of(kref, struct kgsl_mem_entry, refcount); - size_t size = entry->memdesc.size; - kgsl_sharedmem_free(&entry->memdesc); + entry->priv->stats[entry->memtype].cur -= entry->memdesc.size; - if (entry->memtype == KGSL_USER_MEMORY) - entry->priv->stats.user -= size; - else if (entry->memtype == KGSL_MAPPED_MEMORY) { - if (entry->file_ptr) - fput(entry->file_ptr); + if (entry->memtype != KGSL_MEM_ENTRY_KERNEL) + kgsl_driver.stats.mapped -= entry->memdesc.size; - kgsl_driver.stats.mapped -= size; - entry->priv->stats.mapped -= size; + /* + * Ion takes care of freeing the sglist for us (how nice ) so + * unmap the dma before freeing the sharedmem so kgsl_sharedmem_free + * doesn't try to free it again + */ + + if (entry->memtype == KGSL_MEM_ENTRY_ION) { + ion_unmap_dma(kgsl_ion_client, entry->priv_data); + entry->memdesc.sg = NULL; + } + + kgsl_sharedmem_free(&entry->memdesc); + + switch (entry->memtype) { + case KGSL_MEM_ENTRY_PMEM: + case KGSL_MEM_ENTRY_ASHMEM: + if (entry->priv_data) + fput(entry->priv_data); + break; + case KGSL_MEM_ENTRY_ION: + ion_free(kgsl_ion_client, entry->priv_data); + break; } kfree(entry); @@ -180,35 +258,41 @@ static void kgsl_memqueue_freememontimestamp(struct kgsl_device *device, list_add_tail(&entry->list, &device->memqueue); } -static void kgsl_memqueue_drain(struct kgsl_device *device) +static void kgsl_timestamp_expired(struct work_struct *work) { + struct kgsl_device *device = container_of(work, struct kgsl_device, + ts_expired_ws); struct kgsl_mem_entry *entry, *entry_tmp; + struct kgsl_event *event, *event_tmp; uint32_t ts_processed; - BUG_ON(!mutex_is_locked(&device->mutex)); + mutex_lock(&device->mutex); /* get current EOP timestamp */ ts_processed = device->ftbl->readtimestamp(device, KGSL_TIMESTAMP_RETIRED); + /* Flush the freememontimestamp queue */ list_for_each_entry_safe(entry, entry_tmp, &device->memqueue, list) { - KGSL_MEM_INFO(device, - "ts_processed %d ts_free %d gpuaddr %x)\n", - ts_processed, entry->free_timestamp, - entry->memdesc.gpuaddr); - if (!timestamp_cmp(ts_processed, entry->free_timestamp)) + if (timestamp_cmp(ts_processed, entry->free_timestamp) < 0) break; list_del(&entry->list); kgsl_mem_entry_put(entry); } -} -static void kgsl_memqueue_drain_unlocked(struct kgsl_device *device) -{ - mutex_lock(&device->mutex); - kgsl_check_suspended(device); - kgsl_memqueue_drain(device); + /* Process expired events */ + list_for_each_entry_safe(event, event_tmp, &device->events, list) { + if (timestamp_cmp(ts_processed, event->timestamp) < 0) + break; + + if (event->func) + event->func(device, event->priv, ts_processed); + + list_del(&event->list); + kfree(event); + } + mutex_unlock(&device->mutex); } @@ -290,7 +374,7 @@ int kgsl_check_timestamp(struct kgsl_device *device, unsigned int timestamp) ts_processed = device->ftbl->readtimestamp(device, KGSL_TIMESTAMP_RETIRED); - return timestamp_cmp(ts_processed, timestamp); + return (timestamp_cmp(ts_processed, timestamp) >= 0); } EXPORT_SYMBOL(kgsl_check_timestamp); @@ -336,6 +420,12 @@ static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state) KGSL_PWR_WARN(device, "state -> SUSPEND, device %d\n", device->id); break; + case KGSL_STATE_SLUMBER: + INIT_COMPLETION(device->hwaccess_gate); + device->state = KGSL_STATE_SUSPEND; + KGSL_PWR_WARN(device, "state -> SUSPEND, device %d\n", + device->id); + break; default: KGSL_PWR_ERR(device, "suspend fail, device %d\n", device->id); @@ -362,28 +452,16 @@ static int kgsl_resume_device(struct kgsl_device *device) KGSL_PWR_WARN(device, "resume start\n"); mutex_lock(&device->mutex); if (device->state == KGSL_STATE_SUSPEND) { - device->requested_state = KGSL_STATE_ACTIVE; - kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_NOMINAL); - status = device->ftbl->start(device, 0); - if (status == 0) { - device->state = KGSL_STATE_ACTIVE; - KGSL_PWR_WARN(device, - "state -> ACTIVE, device %d\n", - device->id); - } else { - KGSL_PWR_ERR(device, - "resume failed, device %d\n", - device->id); - device->state = KGSL_STATE_INIT; - goto end; - } + device->state = KGSL_STATE_SLUMBER; + status = 0; + KGSL_PWR_WARN(device, + "state -> SLUMBER, device %d\n", + device->id); complete_all(&device->hwaccess_gate); } device->requested_state = KGSL_STATE_NONE; -end: mutex_unlock(&device->mutex); - kgsl_check_idle(device); KGSL_PWR_WARN(device, "resume end\n"); return status; } @@ -424,9 +502,12 @@ void kgsl_early_suspend_driver(struct early_suspend *h) { struct kgsl_device *device = container_of(h, struct kgsl_device, display_off); + KGSL_PWR_WARN(device, "early suspend start\n"); mutex_lock(&device->mutex); - kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_NOMINAL); + device->requested_state = KGSL_STATE_SLUMBER; + kgsl_pwrctrl_sleep(device); mutex_unlock(&device->mutex); + KGSL_PWR_WARN(device, "early suspend end\n"); } EXPORT_SYMBOL(kgsl_early_suspend_driver); @@ -449,9 +530,13 @@ void kgsl_late_resume_driver(struct early_suspend *h) { struct kgsl_device *device = container_of(h, struct kgsl_device, display_off); + KGSL_PWR_WARN(device, "late resume start\n"); mutex_lock(&device->mutex); - kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO); + kgsl_pwrctrl_wake(device); + device->pwrctrl.restore_slumber = 0; mutex_unlock(&device->mutex); + kgsl_check_idle(device); + KGSL_PWR_WARN(device, "late resume end\n"); } EXPORT_SYMBOL(kgsl_late_resume_driver); @@ -520,11 +605,6 @@ kgsl_put_process_private(struct kgsl_device *device, if (--private->refcnt) goto unlock; - KGSL_MEM_INFO(device, - "Memory usage: user (%d/%d) mapped (%d/%d)\n", - private->stats.user, private->stats.user_max, - private->stats.mapped, private->stats.mapped_max); - kgsl_process_uninit_sysfs(private); list_del(&private->list); @@ -767,16 +847,10 @@ static long kgsl_ioctl_device_waittimestamp(struct kgsl_device_private dev_priv->device->active_cnt++; - /* Don't wait forever, set a max value for now */ - if (param->timeout == -1) - param->timeout = 10 * MSEC_PER_SEC; - result = dev_priv->device->ftbl->waittimestamp(dev_priv->device, param->timestamp, param->timeout); - kgsl_memqueue_drain(dev_priv->device); - /* Fire off any pending suspend operations that are in flight */ INIT_COMPLETION(dev_priv->device->suspend_gate); @@ -954,7 +1028,6 @@ static long kgsl_ioctl_cmdstream_freememontimestamp(struct kgsl_device_private if (entry) { kgsl_memqueue_freememontimestamp(dev_priv->device, entry, param->timestamp, param->type); - kgsl_memqueue_drain(dev_priv->device); } else { KGSL_DRV_ERR(dev_priv->device, "invalid gpuaddr %08x\n", param->gpuaddr); @@ -1066,9 +1139,6 @@ kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_device_private *dev_priv, if (!kgsl_mmu_enabled()) return -ENODEV; - /* Make sure all pending freed memory is collected */ - kgsl_memqueue_drain_unlocked(dev_priv->device); - if (!param->hostptr) { KGSL_CORE_ERR("invalid hostptr %x\n", param->hostptr); result = -EINVAL; @@ -1130,13 +1200,12 @@ kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_device_private *dev_priv, param->gpuaddr = entry->memdesc.gpuaddr; - entry->memtype = KGSL_USER_MEMORY; + entry->memtype = KGSL_MEM_ENTRY_KERNEL; kgsl_mem_entry_attach_process(entry, private); /* Process specific statistics */ - KGSL_STATS_ADD(len, private->stats.user, - private->stats.user_max); + kgsl_process_add_stats(private, entry->memtype, len); kgsl_check_idle(dev_priv->device); return 0; @@ -1237,13 +1306,17 @@ static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, } - entry->file_ptr = filep; + entry->priv_data = filep; entry->memdesc.pagetable = pagetable; entry->memdesc.size = size; entry->memdesc.physaddr = phys + (offset & PAGE_MASK); entry->memdesc.hostptr = (void *) (virt + (offset & PAGE_MASK)); - entry->memdesc.ops = &kgsl_contiguous_ops; + + ret = memdesc_sg_phys(&entry->memdesc, + phys + (offset & PAGE_MASK), size); + if (ret) + goto err; return 0; err: @@ -1253,6 +1326,60 @@ static int kgsl_setup_phys_file(struct kgsl_mem_entry *entry, return ret; } +static int memdesc_sg_virt(struct kgsl_memdesc *memdesc, + void *addr, int size) +{ + int i; + int sglen = PAGE_ALIGN(size) / PAGE_SIZE; + unsigned long paddr = (unsigned long) addr; + + memdesc->sg = kmalloc(sglen * sizeof(struct scatterlist), + GFP_KERNEL); + if (memdesc->sg == NULL) + return -ENOMEM; + + memdesc->sglen = sglen; + sg_init_table(memdesc->sg, sglen); + + spin_lock(¤t->mm->page_table_lock); + + for (i = 0; i < sglen; i++, paddr += PAGE_SIZE) { + struct page *page; + pmd_t *ppmd; + pte_t *ppte; + pgd_t *ppgd = pgd_offset(current->mm, paddr); + + if (pgd_none(*ppgd) || pgd_bad(*ppgd)) + goto err; + + ppmd = pmd_offset(ppgd, paddr); + if (pmd_none(*ppmd) || pmd_bad(*ppmd)) + goto err; + + ppte = pte_offset_map(ppmd, paddr); + if (ppte == NULL) + goto err; + + page = pfn_to_page(pte_pfn(*ppte)); + if (!page) + goto err; + + sg_set_page(&memdesc->sg[i], page, PAGE_SIZE, 0); + pte_unmap(ppte); + } + + spin_unlock(¤t->mm->page_table_lock); + + return 0; + +err: + spin_unlock(¤t->mm->page_table_lock); + kfree(memdesc->sg); + memdesc->sg = NULL; + + return -EINVAL; +} + static int kgsl_setup_hostptr(struct kgsl_mem_entry *entry, struct kgsl_pagetable *pagetable, void *hostptr, unsigned int offset, @@ -1302,9 +1429,9 @@ static int kgsl_setup_hostptr(struct kgsl_mem_entry *entry, entry->memdesc.pagetable = pagetable; entry->memdesc.size = size; entry->memdesc.hostptr = hostptr + (offset & PAGE_MASK); - entry->memdesc.ops = &kgsl_userptr_ops; - return 0; + return memdesc_sg_virt(&entry->memdesc, + hostptr + (offset & PAGE_MASK), size); } #ifdef CONFIG_ASHMEM @@ -1351,12 +1478,14 @@ static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry, goto err; } - entry->file_ptr = filep; - + entry->priv_data = filep; entry->memdesc.pagetable = pagetable; entry->memdesc.size = ALIGN(size, PAGE_SIZE); entry->memdesc.hostptr = hostptr; - entry->memdesc.ops = &kgsl_userptr_ops; + + ret = memdesc_sg_virt(&entry->memdesc, hostptr, size); + if (ret) + goto err; return 0; @@ -1373,6 +1502,51 @@ static int kgsl_setup_ashmem(struct kgsl_mem_entry *entry, } #endif +static int kgsl_setup_ion(struct kgsl_mem_entry *entry, + struct kgsl_pagetable *pagetable, int fd) +{ + struct ion_handle *handle; + struct scatterlist *s; + unsigned long flags; + + if (kgsl_ion_client == NULL) { + kgsl_ion_client = msm_ion_client_create(UINT_MAX, KGSL_NAME); + if (kgsl_ion_client == NULL) + return -ENODEV; + } + + handle = ion_import_fd(kgsl_ion_client, fd); + if (IS_ERR_OR_NULL(handle)) + return PTR_ERR(handle); + + entry->memtype = KGSL_MEM_ENTRY_ION; + entry->priv_data = handle; + entry->memdesc.pagetable = pagetable; + entry->memdesc.size = 0; + + if (ion_handle_get_flags(kgsl_ion_client, handle, &flags)) + goto err; + + entry->memdesc.sg = ion_map_dma(kgsl_ion_client, handle, flags); + + if (IS_ERR_OR_NULL(entry->memdesc.sg)) + goto err; + + /* Calculate the size of the memdesc from the sglist */ + + entry->memdesc.sglen = 0; + + for (s = entry->memdesc.sg; s != NULL; s = sg_next(s)) { + entry->memdesc.size += s->length; + entry->memdesc.sglen++; + } + + return 0; +err: + ion_free(kgsl_ion_client, handle); + return -ENOMEM; +} + static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, unsigned int cmd, void *data) { @@ -1387,8 +1561,6 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, if (entry == NULL) return -ENOMEM; - kgsl_memqueue_drain_unlocked(dev_priv->device); - if (_IOC_SIZE(cmd) == sizeof(struct kgsl_sharedmem_from_pmem)) memtype = KGSL_USER_MEM_TYPE_PMEM; else @@ -1402,6 +1574,7 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, result = kgsl_setup_phys_file(entry, private->pagetable, param->fd, param->offset, param->len); + entry->memtype = KGSL_MEM_ENTRY_PMEM; break; case KGSL_USER_MEM_TYPE_ADDR: @@ -1418,6 +1591,7 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, result = kgsl_setup_hostptr(entry, private->pagetable, (void *) param->hostptr, param->offset, param->len); + entry->memtype = KGSL_MEM_ENTRY_USER; break; case KGSL_USER_MEM_TYPE_ASHMEM: @@ -1434,6 +1608,12 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, result = kgsl_setup_ashmem(entry, private->pagetable, param->fd, (void *) param->hostptr, param->len); + + entry->memtype = KGSL_MEM_ENTRY_ASHMEM; + break; + case KGSL_USER_MEM_TYPE_ION: + result = kgsl_setup_ion(entry, private->pagetable, + param->fd); break; default: KGSL_CORE_ERR("Invalid memory type: %x\n", memtype); @@ -1453,14 +1633,10 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, /* Adjust the returned value for a non 4k aligned offset */ param->gpuaddr = entry->memdesc.gpuaddr + (param->offset & ~PAGE_MASK); - entry->memtype = KGSL_MAPPED_MEMORY; - KGSL_STATS_ADD(param->len, kgsl_driver.stats.mapped, - kgsl_driver.stats.mapped_max); + kgsl_driver.stats.mapped_max); - /* Statistics */ - KGSL_STATS_ADD(param->len, private->stats.mapped, - private->stats.mapped_max); + kgsl_process_add_stats(private, entry->memtype, param->len); kgsl_mem_entry_attach_process(entry, private); @@ -1468,8 +1644,8 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, return result; error_put_file_ptr: - if (entry->file_ptr) - fput(entry->file_ptr); + if (entry->priv_data) + fput(entry->priv_data); error: kfree(entry); @@ -1507,9 +1683,6 @@ kgsl_ioctl_sharedmem_flush_cache(struct kgsl_device_private *dev_priv, } kgsl_cache_range_op(&entry->memdesc, KGSL_CACHE_OP_CLEAN); - - /* Statistics - keep track of how many flushes each process does */ - private->stats.flushes++; done: spin_unlock(&private->mem_lock); return result; @@ -1528,19 +1701,15 @@ kgsl_ioctl_gpumem_alloc(struct kgsl_device_private *dev_priv, if (entry == NULL) return -ENOMEM; - /* Make sure all pending freed memory is collected */ - kgsl_memqueue_drain_unlocked(dev_priv->device); - result = kgsl_allocate_user(&entry->memdesc, private->pagetable, param->size, param->flags); if (result == 0) { - entry->memtype = KGSL_USER_MEMORY; + entry->memtype = KGSL_MEM_ENTRY_KERNEL; kgsl_mem_entry_attach_process(entry, private); param->gpuaddr = entry->memdesc.gpuaddr; - KGSL_STATS_ADD(entry->memdesc.size, private->stats.user, - private->stats.user_max); + kgsl_process_add_stats(private, entry->memtype, param->size); } else kfree(entry); @@ -1578,6 +1747,114 @@ static long kgsl_ioctl_cff_user_event(struct kgsl_device_private *dev_priv, return result; } +#ifdef CONFIG_GENLOCK +struct kgsl_genlock_event_priv { + struct genlock_handle *handle; + struct genlock *lock; +}; + +/** + * kgsl_genlock_event_cb - Event callback for a genlock timestamp event + * @device - The KGSL device that expired the timestamp + * @priv - private data for the event + * @timestamp - the timestamp that triggered the event + * + * Release a genlock lock following the expiration of a timestamp + */ + +static void kgsl_genlock_event_cb(struct kgsl_device *device, + void *priv, u32 timestamp) +{ + struct kgsl_genlock_event_priv *ev = priv; + int ret; + + ret = genlock_lock(ev->handle, GENLOCK_UNLOCK, 0, 0); + if (ret) + KGSL_CORE_ERR("Error while unlocking genlock: %d\n", ret); + + genlock_put_handle(ev->handle); + + kfree(ev); +} + +/** + * kgsl_add_genlock-event - Create a new genlock event + * @device - KGSL device to create the event on + * @timestamp - Timestamp to trigger the event + * @data - User space buffer containing struct kgsl_genlock_event_priv + * @len - length of the userspace buffer + * @returns 0 on success or error code on error + * + * Attack to a genlock handle and register an event to release the + * genlock lock when the timestamp expires + */ + +static int kgsl_add_genlock_event(struct kgsl_device *device, + u32 timestamp, void __user *data, int len) +{ + struct kgsl_genlock_event_priv *event; + struct kgsl_timestamp_event_genlock priv; + int ret; + + if (len != sizeof(priv)) + return -EINVAL; + + if (copy_from_user(&priv, data, sizeof(priv))) + return -EFAULT; + + event = kzalloc(sizeof(*event), GFP_KERNEL); + + if (event == NULL) + return -ENOMEM; + + event->handle = genlock_get_handle_fd(priv.handle); + + if (IS_ERR(event->handle)) { + int ret = PTR_ERR(event->handle); + kfree(event); + return ret; + } + + ret = kgsl_add_event(device, timestamp, kgsl_genlock_event_cb, event); + if (ret) + kfree(event); + + return ret; +} +#else +static long kgsl_add_genlock_event(struct kgsl_device *device, + u32 timestamp, void __user *data, int len) +{ + return -EINVAL; +} +#endif + +/** + * kgsl_ioctl_timestamp_event - Register a new timestamp event from userspace + * @dev_priv - pointer to the private device structure + * @cmd - the ioctl cmd passed from kgsl_ioctl + * @data - the user data buffer from kgsl_ioctl + * @returns 0 on success or error code on failure + */ + +static long kgsl_ioctl_timestamp_event(struct kgsl_device_private *dev_priv, + unsigned int cmd, void *data) +{ + struct kgsl_timestamp_event *param = data; + int ret; + + switch (param->type) { + case KGSL_TIMESTAMP_EVENT_GENLOCK: + ret = kgsl_add_genlock_event(dev_priv->device, + param->timestamp, param->priv, param->len); + break; + default: + ret = -EINVAL; + } + + return ret; +} + typedef long (*kgsl_ioctl_func_t)(struct kgsl_device_private *, unsigned int, void *); @@ -1619,6 +1896,8 @@ static const struct { kgsl_ioctl_cff_syncmem, 0), KGSL_IOCTL_FUNC(IOCTL_KGSL_CFF_USER_EVENT, kgsl_ioctl_cff_user_event, 0), + KGSL_IOCTL_FUNC(IOCTL_KGSL_TIMESTAMP_EVENT, + kgsl_ioctl_timestamp_event, 0), }; static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) @@ -1747,7 +2026,7 @@ kgsl_gpumem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct kgsl_mem_entry *entry = vma->vm_private_data; - if (!entry->memdesc.ops->vmfault) + if (!entry->memdesc.ops || !entry->memdesc.ops->vmfault) return VM_FAULT_SIGBUS; return entry->memdesc.ops->vmfault(&entry->memdesc, vma, vmf); @@ -1794,7 +2073,9 @@ static int kgsl_mmap(struct file *file, struct vm_area_struct *vma) if (entry == NULL) return -EINVAL; - if (!entry->memdesc.ops->vmflags || !entry->memdesc.ops->vmfault) + if (!entry->memdesc.ops || + !entry->memdesc.ops->vmflags || + !entry->memdesc.ops->vmfault) return -EINVAL; vma->vm_flags |= entry->memdesc.ops->vmflags(&entry->memdesc); @@ -1840,8 +2121,8 @@ void kgsl_unregister_device(struct kgsl_device *device) kgsl_cffdump_close(device->id); kgsl_pwrctrl_uninit_sysfs(device); - wake_lock_destroy(&device->idle_wakelock); - pm_qos_remove_request(&device->pm_qos_req_dma); + if (cpu_is_msm8x60()) + wake_lock_destroy(&device->idle_wakelock); idr_destroy(&device->context_idr); @@ -1918,8 +2199,10 @@ kgsl_register_device(struct kgsl_device *device) goto err_devlist; INIT_WORK(&device->idle_check_ws, kgsl_idle_check); + INIT_WORK(&device->ts_expired_ws, kgsl_timestamp_expired); INIT_LIST_HEAD(&device->memqueue); + INIT_LIST_HEAD(&device->events); ret = kgsl_mmu_init(device); if (ret != 0) @@ -1931,9 +2214,9 @@ kgsl_register_device(struct kgsl_device *device) if (ret != 0) goto err_close_mmu; - wake_lock_init(&device->idle_wakelock, WAKE_LOCK_IDLE, device->name); - pm_qos_add_request(&device->pm_qos_req_dma, PM_QOS_CPU_DMA_LATENCY, - PM_QOS_DEFAULT_VALUE); + if (cpu_is_msm8x60()) + wake_lock_init(&device->idle_wakelock, + WAKE_LOCK_IDLE, device->name); idr_init(&device->context_idr); diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 8db2cb4b0f7bb..3bb722da45816 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -101,9 +101,6 @@ struct kgsl_driver { extern struct kgsl_driver kgsl_driver; -#define KGSL_USER_MEMORY 1 -#define KGSL_MAPPED_MEMORY 2 - struct kgsl_pagetable; struct kgsl_memdesc_ops; @@ -115,14 +112,25 @@ struct kgsl_memdesc { unsigned int physaddr; unsigned int size; unsigned int priv; + struct scatterlist *sg; + unsigned int sglen; struct kgsl_memdesc_ops *ops; }; +/* List of different memory entry types */ + +#define KGSL_MEM_ENTRY_KERNEL 0 +#define KGSL_MEM_ENTRY_PMEM 1 +#define KGSL_MEM_ENTRY_ASHMEM 2 +#define KGSL_MEM_ENTRY_USER 3 +#define KGSL_MEM_ENTRY_ION 4 +#define KGSL_MEM_ENTRY_MAX 5 + struct kgsl_mem_entry { struct kref refcount; struct kgsl_memdesc memdesc; int memtype; - struct file *file_ptr; + void *priv_data; struct list_head list; uint32_t free_timestamp; /* back pointer to private structure under whose context this @@ -176,10 +184,14 @@ static inline int kgsl_gpuaddr_in_memdesc(const struct kgsl_memdesc *memdesc, return 0; } -static inline bool timestamp_cmp(unsigned int new, unsigned int old) +static inline int timestamp_cmp(unsigned int new, unsigned int old) { int ts_diff = new - old; - return (ts_diff >= 0) || (ts_diff < -20000); + + if (ts_diff == 0) + return 0; + + return ((ts_diff > 0) || (ts_diff < -20000)) ? 1 : -1; } static inline void diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c index d4195927cceaa..aa33152cce445 100644 --- a/drivers/gpu/msm/kgsl_cffdump.c +++ b/drivers/gpu/msm/kgsl_cffdump.c @@ -363,7 +363,7 @@ void kgsl_cffdump_open(enum kgsl_deviceid device_id) /*TODO: move this to where we can report correct gmemsize*/ unsigned int va_base; - if (cpu_is_msm8x60() || cpu_is_msm8960()) + if (cpu_is_msm8x60() || cpu_is_msm8960() || cpu_is_msm8930()) va_base = 0x40000000; else va_base = 0x20000000; diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index 10e345aeeeaec..c88f16ef6b759 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -15,7 +15,6 @@ #include #include -#include #include #include "kgsl.h" @@ -46,6 +45,7 @@ #define KGSL_STATE_SUSPEND 0x00000010 #define KGSL_STATE_HUNG 0x00000020 #define KGSL_STATE_DUMP_AND_RECOVER 0x00000040 +#define KGSL_STATE_SLUMBER 0x00000080 #define KGSL_GRAPHICS_MEMORY_LOW_WATERMARK 0x1000000 @@ -118,6 +118,14 @@ struct kgsl_mh { int mpu_range; }; +struct kgsl_event { + uint32_t timestamp; + void (*func)(struct kgsl_device *, void *, u32); + void *priv; + struct list_head list; +}; + + struct kgsl_device { struct device *dev; const char *name; @@ -164,7 +172,8 @@ struct kgsl_device { struct wake_lock idle_wakelock; struct kgsl_pwrscale pwrscale; struct kobject pwrscale_kobj; - struct pm_qos_request_list pm_qos_req_dma; + struct work_struct ts_expired_ws; + struct list_head events; }; struct kgsl_context { @@ -184,15 +193,12 @@ struct kgsl_process_private { struct list_head mem_list; struct kgsl_pagetable *pagetable; struct list_head list; - struct kobject *kobj; + struct kobject kobj; struct { - unsigned int user; - unsigned int user_max; - unsigned int mapped; - unsigned int mapped_max; - unsigned int flushes; - } stats; + unsigned int cur; + unsigned int max; + } stats[KGSL_MEM_ENTRY_MAX]; }; struct kgsl_device_private { @@ -207,6 +213,14 @@ struct kgsl_power_stats { struct kgsl_device *kgsl_get_device(int dev_idx); +static inline void kgsl_process_add_stats(struct kgsl_process_private *priv, + unsigned int type, size_t size) +{ + priv->stats[type].cur += size; + if (priv->stats[type].max < priv->stats[type].cur) + priv->stats[type].max = priv->stats[type].cur; +} + static inline void kgsl_regread(struct kgsl_device *device, unsigned int offsetwords, unsigned int *value) diff --git a/drivers/gpu/msm/kgsl_drm.c b/drivers/gpu/msm/kgsl_drm.c index 202783b3dc73f..dba2dfcfb4a59 100644 --- a/drivers/gpu/msm/kgsl_drm.c +++ b/drivers/gpu/msm/kgsl_drm.c @@ -293,7 +293,6 @@ kgsl_gem_alloc_memory(struct drm_gem_object *obj) } priv->memdesc.size = obj->size * priv->bufcount; - priv->memdesc.ops = &kgsl_contiguous_ops; } else if (TYPE_IS_MEM(priv->type)) { priv->memdesc.hostptr = @@ -316,7 +315,6 @@ kgsl_gem_alloc_memory(struct drm_gem_object *obj) return 0; } -#ifdef CONFIG_MSM_KGSL_MMU static void kgsl_gem_unmap(struct drm_gem_object *obj) { @@ -336,12 +334,6 @@ kgsl_gem_unmap(struct drm_gem_object *obj) priv->flags &= ~DRM_KGSL_GEM_FLAG_MAPPED; } -#else -static void -kgsl_gem_unmap(struct drm_gem_object *obj) -{ -} -#endif static void kgsl_gem_free_memory(struct drm_gem_object *obj) @@ -725,7 +717,6 @@ kgsl_gem_unbind_gpu_ioctl(struct drm_device *dev, void *data, return 0; } -#ifdef CONFIG_MSM_KGSL_MMU static int kgsl_gem_map(struct drm_gem_object *obj) { @@ -770,24 +761,6 @@ kgsl_gem_map(struct drm_gem_object *obj) return ret; } -#else -static int -kgsl_gem_map(struct drm_gem_object *obj) -{ - struct drm_kgsl_gem_object *priv = obj->driver_private; - int index; - - if (TYPE_IS_PMEM(priv->type)) { - for (index = 0; index < priv->bufcount; index++) - priv->bufs[index].gpuaddr = - priv->memdesc.physaddr + priv->bufs[index].offset; - - return 0; - } - - return -EINVAL; -} -#endif int kgsl_gem_bind_gpu_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/gpu/msm/kgsl_gpummu.c b/drivers/gpu/msm/kgsl_gpummu.c index 383b910605942..fe5677e556fc1 100644 --- a/drivers/gpu/msm/kgsl_gpummu.c +++ b/drivers/gpu/msm/kgsl_gpummu.c @@ -659,68 +659,45 @@ kgsl_gpummu_unmap(void *mmu_specific_pt, return 0; } +#define SUPERPTE_IS_DIRTY(_p) \ +(((_p) & (GSL_PT_SUPER_PTE - 1)) == 0 && \ +GSL_TLBFLUSH_FILTER_ISDIRTY((_p) / GSL_PT_SUPER_PTE)) + static int kgsl_gpummu_map(void *mmu_specific_pt, struct kgsl_memdesc *memdesc, unsigned int protflags) { - int numpages; - unsigned int pte, ptefirst, ptelast, physaddr; - int flushtlb; - unsigned int offset = 0; + unsigned int pte; struct kgsl_gpummu_pt *gpummu_pt = mmu_specific_pt; + struct scatterlist *s; + int flushtlb = 0; + int i; - if (!protflags || - protflags & ~(GSL_PT_PAGE_RV | GSL_PT_PAGE_WV)) { - KGSL_CORE_ERR("Invalid protflags for " - "kgsl_mmu_specific_map: %x", protflags); - return -EINVAL; - } - - numpages = (memdesc->size >> PAGE_SHIFT); + pte = kgsl_pt_entry_get(KGSL_PAGETABLE_BASE, memdesc->gpuaddr); - ptefirst = kgsl_pt_entry_get(KGSL_PAGETABLE_BASE, memdesc->gpuaddr); - ptelast = ptefirst + numpages; - - pte = ptefirst; - flushtlb = 0; - - /* tlb needs to be flushed when the first and last pte are not at - * superpte boundaries */ - if ((ptefirst & (GSL_PT_SUPER_PTE - 1)) != 0 || - ((ptelast + 1) & (GSL_PT_SUPER_PTE-1)) != 0) + /* Flush the TLB if the first PTE isn't at the superpte boundary */ + if (pte & (GSL_PT_SUPER_PTE - 1)) flushtlb = 1; - for (pte = ptefirst; pte < ptelast; pte++, offset += PAGE_SIZE) { -#ifdef VERBOSE_DEBUG - /* check if PTE exists */ - uint32_t val = kgsl_pt_map_get(gpummu_pt, pte); - if (val != 0 && val != GSL_PT_PAGE_DIRTY) { - KGSL_CORE_ERR("pt entry %x is already set with " - "value %x for pagetable %p\n", pte, val, gpummu_pt); - return -EINVAL; - } -#endif - if ((pte & (GSL_PT_SUPER_PTE-1)) == 0) - if (GSL_TLBFLUSH_FILTER_ISDIRTY(pte / GSL_PT_SUPER_PTE)) + for_each_sg(memdesc->sg, s, memdesc->sglen, i) { + unsigned int paddr = sg_phys(s); + unsigned int j; + + /* Each sg entry might be multiple pages long */ + for (j = paddr; j < paddr + s->length; pte++, j += PAGE_SIZE) { + if (SUPERPTE_IS_DIRTY(pte)) flushtlb = 1; - /* mark pte as in use */ - - physaddr = memdesc->ops->physaddr(memdesc, offset); - if (!physaddr) { - KGSL_CORE_ERR("Failed to convert %x address to " - "physical", (unsigned int)memdesc->hostptr + offset); - kgsl_gpummu_unmap(mmu_specific_pt, memdesc); - return -EFAULT; + kgsl_pt_map_set(gpummu_pt, pte, j | protflags); } - kgsl_pt_map_set(gpummu_pt, pte, physaddr | protflags); } - /* Post all writes to the pagetable */ + /* Flush the TLB if the last PTE isn't at the superpte boundary */ + if ((pte + 1) & (GSL_PT_SUPER_PTE - 1)) + flushtlb = 1; + wmb(); - /* Invalidate tlb only if current page table used by GPU is the - * pagetable that we used to allocate */ if (flushtlb) { /*set all devices as needing flushing*/ gpummu_pt->tlb_flags = UINT_MAX; diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index f9b9b4a18a796..e4e561cef50c4 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -226,8 +226,6 @@ kgsl_iommu_unmap(void *mmu_specific_pt, { int ret; unsigned int range = memdesc->size; - unsigned int iommu_map_addr; - int map_order = get_order(SZ_4K); struct iommu_domain *domain = (struct iommu_domain *) mmu_specific_pt; @@ -240,14 +238,11 @@ kgsl_iommu_unmap(void *mmu_specific_pt, if (range == 0 || gpuaddr == 0) return 0; - for (iommu_map_addr = gpuaddr; iommu_map_addr < (gpuaddr + range); - iommu_map_addr += SZ_4K) { - ret = iommu_unmap(domain, iommu_map_addr, map_order); - if (ret) - KGSL_CORE_ERR("iommu_unmap(%p, %x, %d) failed " - "with err: %d\n", domain, iommu_map_addr, - map_order, ret); - } + ret = iommu_unmap_range(domain, gpuaddr, range); + if (ret) + KGSL_CORE_ERR("iommu_unmap_range(%p, %x, %d) failed " + "with err: %d\n", domain, gpuaddr, + range, ret); return 0; } @@ -257,38 +252,23 @@ kgsl_iommu_map(void *mmu_specific_pt, struct kgsl_memdesc *memdesc, unsigned int protflags) { - int ret = 0; - unsigned int physaddr; + int ret; unsigned int iommu_virt_addr; - unsigned int offset = 0; - int map_order; - struct iommu_domain *domain = (struct iommu_domain *) - mmu_specific_pt; + struct iommu_domain *domain = mmu_specific_pt; BUG_ON(NULL == domain); - map_order = get_order(SZ_4K); - - for (iommu_virt_addr = memdesc->gpuaddr; - iommu_virt_addr < (memdesc->gpuaddr + memdesc->size); - iommu_virt_addr += SZ_4K, offset += PAGE_SIZE) { - physaddr = memdesc->ops->physaddr(memdesc, offset); - if (!physaddr) { - KGSL_CORE_ERR("Failed to convert %x address to " - "physical\n", (unsigned int)memdesc->hostptr + offset); - kgsl_iommu_unmap(mmu_specific_pt, memdesc); - return -EFAULT; - } - ret = iommu_map(domain, iommu_virt_addr, physaddr, - map_order, MSM_IOMMU_ATTR_NONCACHED); - if (ret) { - KGSL_CORE_ERR("iommu_map(%p, %x, %x, %d, %d) " - "failed with err: %d\n", domain, - iommu_virt_addr, physaddr, map_order, - MSM_IOMMU_ATTR_NONCACHED, ret); - kgsl_iommu_unmap(mmu_specific_pt, memdesc); - return ret; - } + + iommu_virt_addr = memdesc->gpuaddr; + + ret = iommu_map_range(domain, iommu_virt_addr, memdesc->sg, + memdesc->size, 0); + if (ret) { + KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) " + "failed with err: %d\n", domain, + iommu_virt_addr, memdesc->sg, memdesc->size, + 0, ret); + return ret; } return ret; diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c index 7eec9e5ddcac0..715b9d63d8dce 100644 --- a/drivers/gpu/msm/kgsl_mmu.c +++ b/drivers/gpu/msm/kgsl_mmu.c @@ -525,37 +525,6 @@ void kgsl_mh_start(struct kgsl_device *device) */ } -unsigned int kgsl_virtaddr_to_physaddr(void *virtaddr) -{ - unsigned int physaddr = 0; - pgd_t *pgd_ptr = NULL; - pmd_t *pmd_ptr = NULL; - pte_t *pte_ptr = NULL, pte; - - pgd_ptr = pgd_offset(current->mm, (unsigned long) virtaddr); - if (pgd_none(*pgd) || pgd_bad(*pgd)) { - KGSL_CORE_ERR("Invalid pgd entry\n"); - return 0; - } - - pmd_ptr = pmd_offset(pgd_ptr, (unsigned long) virtaddr); - if (pmd_none(*pmd_ptr) || pmd_bad(*pmd_ptr)) { - KGSL_CORE_ERR("Invalid pmd entry\n"); - return 0; - } - - pte_ptr = pte_offset_map(pmd_ptr, (unsigned long) virtaddr); - if (!pte_ptr) { - KGSL_CORE_ERR("pt_offset_map failed\n"); - return 0; - } - pte = *pte_ptr; - physaddr = pte_pfn(pte); - pte_unmap(pte_ptr); - physaddr <<= PAGE_SHIFT; - return physaddr; -} - int kgsl_mmu_map(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc, @@ -742,13 +711,7 @@ EXPORT_SYMBOL(kgsl_mmu_get_mmutype); void kgsl_mmu_set_mmutype(char *mmutype) { - kgsl_mmu_type = KGSL_MMU_TYPE_NONE; -#ifdef CONFIG_MSM_KGSL_GPUMMU - kgsl_mmu_type = KGSL_MMU_TYPE_GPU; -#elif defined(CONFIG_MSM_KGSL_IOMMU) - if (iommu_found()) - kgsl_mmu_type = KGSL_MMU_TYPE_IOMMU; -#endif + kgsl_mmu_type = iommu_found() ? KGSL_MMU_TYPE_IOMMU : KGSL_MMU_TYPE_GPU; if (mmutype && !strncmp(mmutype, "gpummu", 6)) kgsl_mmu_type = KGSL_MMU_TYPE_GPU; if (iommu_found() && mmutype && !strncmp(mmutype, "iommu", 5)) diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h index 4af073a7b2cf6..1338d0d4b0083 100644 --- a/drivers/gpu/msm/kgsl_mmu.h +++ b/drivers/gpu/msm/kgsl_mmu.h @@ -76,16 +76,10 @@ struct kgsl_device; #define MH_INTERRUPT_MASK__AXI_WRITE_ERROR 0x00000002L #define MH_INTERRUPT_MASK__MMU_PAGE_FAULT 0x00000004L -#ifdef CONFIG_MSM_KGSL_MMU #define KGSL_MMU_INT_MASK \ (MH_INTERRUPT_MASK__AXI_READ_ERROR | \ MH_INTERRUPT_MASK__AXI_WRITE_ERROR | \ MH_INTERRUPT_MASK__MMU_PAGE_FAULT) -#else -#define KGSL_MMU_INT_MASK \ - (MH_INTERRUPT_MASK__AXI_READ_ERROR | \ - MH_INTERRUPT_MASK__AXI_WRITE_ERROR) -#endif enum kgsl_mmutype { KGSL_MMU_TYPE_GPU = 0, diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 115f073254243..7034fd884d86d 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -13,12 +13,17 @@ #include #include #include +#include #include "kgsl.h" #include "kgsl_pwrscale.h" #include "kgsl_device.h" -#define GPU_SWFI_LATENCY 3 +#define KGSL_PWRFLAGS_POWER_ON 0 +#define KGSL_PWRFLAGS_CLK_ON 1 +#define KGSL_PWRFLAGS_AXI_ON 2 +#define KGSL_PWRFLAGS_IRQ_ON 3 + #define UPDATE_BUSY_VAL 1000000 #define UPDATE_BUSY 50 @@ -335,7 +340,6 @@ void kgsl_pwrctrl_clk(struct kgsl_device *device, int state) } } } -EXPORT_SYMBOL(kgsl_pwrctrl_clk); void kgsl_pwrctrl_axi(struct kgsl_device *device, int state) { @@ -372,8 +376,6 @@ void kgsl_pwrctrl_axi(struct kgsl_device *device, int state) } } } -EXPORT_SYMBOL(kgsl_pwrctrl_axi); - void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, int state) { @@ -397,7 +399,6 @@ void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, int state) } } } -EXPORT_SYMBOL(kgsl_pwrctrl_pwrrail); void kgsl_pwrctrl_irq(struct kgsl_device *device, int state) { @@ -474,6 +475,8 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) gpu_freq) : 0; pwr->pwrlevels[i].bus_freq = pdata_pwr->pwrlevel[i].bus_freq; + pwr->pwrlevels[i].io_fraction = + pdata_pwr->pwrlevel[i].io_fraction; } /* Do not set_rate for targets in sync with AXI */ if (pwr->pwrlevels[0].gpu_freq > 0) @@ -577,10 +580,11 @@ void kgsl_idle_check(struct work_struct *work) idle_check_ws); mutex_lock(&device->mutex); - if (device->requested_state != KGSL_STATE_SLEEP) - kgsl_pwrscale_idle(device); - if (device->state & (KGSL_STATE_ACTIVE | KGSL_STATE_NAP)) { + if ((device->requested_state != KGSL_STATE_SLEEP) && + (device->requested_state != KGSL_STATE_SLUMBER)) + kgsl_pwrscale_idle(device); + if (kgsl_pwrctrl_sleep(device) != 0) { mod_timer(&device->idle_timer, jiffies + @@ -616,7 +620,8 @@ void kgsl_timer(unsigned long data) void kgsl_pre_hwaccess(struct kgsl_device *device) { BUG_ON(!mutex_is_locked(&device->mutex)); - if (device->state & (KGSL_STATE_SLEEP | KGSL_STATE_NAP)) + if (device->state & (KGSL_STATE_SLEEP | KGSL_STATE_NAP | + KGSL_STATE_SLUMBER)) kgsl_pwrctrl_wake(device); } EXPORT_SYMBOL(kgsl_pre_hwaccess); @@ -628,14 +633,46 @@ void kgsl_check_suspended(struct kgsl_device *device) mutex_unlock(&device->mutex); wait_for_completion(&device->hwaccess_gate); mutex_lock(&device->mutex); - } - if (device->state == KGSL_STATE_DUMP_AND_RECOVER) { + } else if (device->state == KGSL_STATE_DUMP_AND_RECOVER) { mutex_unlock(&device->mutex); wait_for_completion(&device->recovery_gate); mutex_lock(&device->mutex); - } - } + } else if (device->state == KGSL_STATE_SLUMBER) + kgsl_pwrctrl_wake(device); +} +static int +_slumber(struct kgsl_device *device) +{ + int status = -EINVAL; + if (!device) + return -EINVAL; + KGSL_PWR_WARN(device, "Slumber start\n"); + + device->requested_state = KGSL_STATE_SLUMBER; + del_timer(&device->idle_timer); + switch (device->state) { + case KGSL_STATE_ACTIVE: + /* Wait for the device to become idle */ + device->ftbl->idle(device, KGSL_TIMEOUT_DEFAULT); + case KGSL_STATE_NAP: + case KGSL_STATE_SLEEP: + device->ftbl->suspend_context(device); + device->ftbl->stop(device); + device->state = KGSL_STATE_SLUMBER; + device->pwrctrl.restore_slumber = 1; + KGSL_PWR_WARN(device, "state -> SLUMBER, device %d\n", + device->id); + break; + default: + break; + } + status = 0; + /* Don't set requested state to NONE + It's done in kgsl_pwrctrl_sleep*/ + KGSL_PWR_WARN(device, "Done going to slumber\n"); + return status; +} /******************************************************************/ /* Caller must hold the device mutex. */ @@ -645,18 +682,34 @@ int kgsl_pwrctrl_sleep(struct kgsl_device *device) KGSL_PWR_INFO(device, "sleep device %d\n", device->id); /* Work through the legal state transitions */ - if (device->requested_state == KGSL_STATE_NAP) { - if (device->ftbl->isidle(device)) + if ((device->requested_state == KGSL_STATE_NAP)) { + if (device->pwrctrl.restore_slumber) { + device->requested_state = KGSL_STATE_NONE; + return 0; + } else if (device->ftbl->isidle(device)) goto nap; } else if (device->requested_state == KGSL_STATE_SLEEP) { if (device->state == KGSL_STATE_NAP || - device->ftbl->isidle(device)) - goto sleep; + device->ftbl->isidle(device)) { + if (!device->pwrctrl.restore_slumber) + goto sleep; + else + goto slumber; + } + } else if (device->requested_state == KGSL_STATE_SLUMBER) { + if (device->state == KGSL_STATE_INIT) + return 0; + if (device->ftbl->isidle(device)) + goto slumber; } device->requested_state = KGSL_STATE_NONE; return -EBUSY; + +slumber: + _slumber(device); + sleep: kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_OFF); @@ -678,9 +731,8 @@ int kgsl_pwrctrl_sleep(struct kgsl_device *device) device->state = device->requested_state; device->requested_state = KGSL_STATE_NONE; - wake_unlock(&device->idle_wakelock); - pm_qos_update_request(&device->pm_qos_req_dma, - PM_QOS_DEFAULT_VALUE); + if (device->idle_wakelock.name) + wake_unlock(&device->idle_wakelock); KGSL_PWR_WARN(device, "state -> NAP/SLEEP(%d), device %d\n", device->state, device->id); @@ -688,13 +740,34 @@ int kgsl_pwrctrl_sleep(struct kgsl_device *device) } EXPORT_SYMBOL(kgsl_pwrctrl_sleep); +static int +_wake_from_slumber(struct kgsl_device *device) +{ + int status = -EINVAL; + if (!device) + return -EINVAL; + + KGSL_PWR_WARN(device, "wake from slumber start\n"); + + device->requested_state = KGSL_STATE_ACTIVE; + kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_NOMINAL); + status = device->ftbl->start(device, 0); + device->requested_state = KGSL_STATE_NONE; + + KGSL_PWR_WARN(device, "Done waking from slumber\n"); + return status; +} + /******************************************************************/ /* Caller must hold the device mutex. */ void kgsl_pwrctrl_wake(struct kgsl_device *device) { - if (device->state == KGSL_STATE_SUSPEND) + if (device->state & (KGSL_STATE_SUSPEND | KGSL_STATE_INIT)) return; + if (device->state == KGSL_STATE_SLUMBER) + _wake_from_slumber(device); + if (device->state != KGSL_STATE_NAP) { kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON); kgsl_pwrscale_wake(device); @@ -712,8 +785,9 @@ void kgsl_pwrctrl_wake(struct kgsl_device *device) mod_timer(&device->idle_timer, jiffies + device->pwrctrl.interval_timeout); - wake_lock(&device->idle_wakelock); - pm_qos_update_request(&device->pm_qos_req_dma, GPU_SWFI_LATENCY); + if (device->idle_wakelock.name) + wake_lock(&device->idle_wakelock); + KGSL_PWR_INFO(device, "wake return for device %d\n", device->id); } EXPORT_SYMBOL(kgsl_pwrctrl_wake); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 794a8955a3f67..9bf3c0fff1047 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -16,17 +16,11 @@ /***************************************************************************** ** power flags *****************************************************************************/ -#define KGSL_PWRFLAGS_POWER_ON 0 -#define KGSL_PWRFLAGS_CLK_ON 1 -#define KGSL_PWRFLAGS_AXI_ON 2 -#define KGSL_PWRFLAGS_IRQ_ON 3 - #define KGSL_PWRFLAGS_ON 1 #define KGSL_PWRFLAGS_OFF 0 #define KGSL_PWRLEVEL_TURBO 0 #define KGSL_PWRLEVEL_NOMINAL 1 -#define KGSL_PWRLEVEL_LOW_OFFSET 2 #define KGSL_MAX_CLKS 5 @@ -61,11 +55,9 @@ struct kgsl_pwrctrl { const char *src_clk_name; s64 time; struct kgsl_busy busy; + unsigned int restore_slumber; }; -void kgsl_pwrctrl_clk(struct kgsl_device *device, int state); -void kgsl_pwrctrl_axi(struct kgsl_device *device, int state); -void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, int state); void kgsl_pwrctrl_irq(struct kgsl_device *device, int state); int kgsl_pwrctrl_init(struct kgsl_device *device); void kgsl_pwrctrl_close(struct kgsl_device *device); diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c index fa7203deb0bd6..b5c6876702fcc 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.c +++ b/drivers/gpu/msm/kgsl_pwrscale.c @@ -272,8 +272,11 @@ void kgsl_pwrscale_policy_remove_files(struct kgsl_device *device, static void _kgsl_pwrscale_detach_policy(struct kgsl_device *device) { - if (device->pwrscale.policy != NULL) + if (device->pwrscale.policy != NULL) { device->pwrscale.policy->close(device, &device->pwrscale); + kgsl_pwrctrl_pwrlevel_change(device, + device->pwrctrl.thermal_pwrlevel); + } device->pwrscale.policy = NULL; } diff --git a/drivers/gpu/msm/kgsl_pwrscale_idlestats.c b/drivers/gpu/msm/kgsl_pwrscale_idlestats.c index c4dcb22f9be42..d5fa84edec989 100644 --- a/drivers/gpu/msm/kgsl_pwrscale_idlestats.c +++ b/drivers/gpu/msm/kgsl_pwrscale_idlestats.c @@ -14,18 +14,52 @@ #include #include #include +#include +#include +#include +#include #include "kgsl.h" #include "kgsl_pwrscale.h" #include "kgsl_device.h" +#define MAX_CORES 4 +struct _cpu_info { + spinlock_t lock; + struct notifier_block cpu_nb; + u64 start[MAX_CORES]; + u64 end[MAX_CORES]; + int curr_freq[MAX_CORES]; + int max_freq[MAX_CORES]; +}; + struct idlestats_priv { char name[32]; struct msm_idle_stats_device idledev; struct kgsl_device *device; struct msm_idle_pulse pulse; + struct _cpu_info cpu_info; }; +static int idlestats_cpufreq_notifier( + struct notifier_block *nb, + unsigned long val, void *data) +{ + struct _cpu_info *cpu = container_of(nb, + struct _cpu_info, cpu_nb); + struct cpufreq_freqs *freq = data; + + if (val != CPUFREQ_POSTCHANGE) + return 0; + + spin_lock(&cpu->lock); + if (freq->cpu < num_possible_cpus()) + cpu->curr_freq[freq->cpu] = freq->new / 1000; + spin_unlock(&cpu->lock); + + return 0; +} + static void idlestats_get_sample(struct msm_idle_stats_device *idledev, struct msm_idle_pulse *pulse) { @@ -55,14 +89,37 @@ static void idlestats_busy(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) { struct idlestats_priv *priv = pwrscale->priv; - if (priv->pulse.busy_start_time != 0) + int i, busy, nr_cpu = 1; + + if (priv->pulse.busy_start_time != 0) { + priv->pulse.wait_interval = 0; + /* Calculate the total CPU busy time for this GPU pulse */ + for (i = 0; i < num_possible_cpus(); i++) { + spin_lock(&priv->cpu_info.lock); + if (cpu_online(i)) { + priv->cpu_info.end[i] = + (u64)ktime_to_us(ktime_get()) - + get_cpu_idle_time_us(i, NULL); + busy = priv->cpu_info.end[i] - + priv->cpu_info.start[i]; + /* Normalize the busy time by frequency */ + busy = priv->cpu_info.curr_freq[i] * + (busy / priv->cpu_info.max_freq[i]); + priv->pulse.wait_interval += busy; + nr_cpu++; + } + spin_unlock(&priv->cpu_info.lock); + } + priv->pulse.wait_interval /= nr_cpu; msm_idle_stats_idle_end(&priv->idledev, &priv->pulse); + } priv->pulse.busy_start_time = ktime_to_us(ktime_get()); } static void idlestats_idle(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) { + int i, nr_cpu; struct kgsl_power_stats stats; struct idlestats_priv *priv = pwrscale->priv; @@ -78,15 +135,29 @@ static void idlestats_idle(struct kgsl_device *device, } priv->pulse.busy_interval = stats.busy_time; - priv->pulse.wait_interval = 0; + nr_cpu = num_possible_cpus(); + for (i = 0; i < nr_cpu; i++) + if (cpu_online(i)) + priv->cpu_info.start[i] = + (u64)ktime_to_us(ktime_get()) - + get_cpu_idle_time_us(i, NULL); + msm_idle_stats_idle_start(&priv->idledev); } +static void idlestats_sleep(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale) +{ + struct idlestats_priv *priv = pwrscale->priv; + priv->idledev.stats->event |= MSM_IDLE_STATS_EVENT_IDLE_TIMER_EXPIRED; +} + static int idlestats_init(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) { struct idlestats_priv *priv; - int ret; + struct cpufreq_policy cpu_policy; + int ret, i; priv = pwrscale->priv = kzalloc(sizeof(struct idlestats_priv), GFP_KERNEL); @@ -101,8 +172,21 @@ static int idlestats_init(struct kgsl_device *device, priv->idledev.name = (const char *) priv->name; priv->idledev.get_sample = idlestats_get_sample; + spin_lock_init(&priv->cpu_info.lock); + priv->cpu_info.cpu_nb.notifier_call = + idlestats_cpufreq_notifier; + ret = cpufreq_register_notifier(&priv->cpu_info.cpu_nb, + CPUFREQ_TRANSITION_NOTIFIER); + if (ret) + goto err; + for (i = 0; i < num_possible_cpus(); i++) { + cpufreq_frequency_table_cpuinfo(&cpu_policy, + cpufreq_frequency_get_table(i)); + priv->cpu_info.max_freq[i] = cpu_policy.max / 1000; + priv->cpu_info.curr_freq[i] = cpu_policy.max / 1000; + } ret = msm_idle_stats_register_device(&priv->idledev); - +err: if (ret) { kfree(pwrscale->priv); pwrscale->priv = NULL; @@ -119,6 +203,8 @@ static void idlestats_close(struct kgsl_device *device, if (pwrscale->priv == NULL) return; + cpufreq_unregister_notifier(&priv->cpu_info.cpu_nb, + CPUFREQ_TRANSITION_NOTIFIER); msm_idle_stats_deregister_device(&priv->idledev); kfree(pwrscale->priv); @@ -130,5 +216,6 @@ struct kgsl_pwrscale_policy kgsl_pwrscale_policy_idlestats = { .init = idlestats_init, .idle = idlestats_idle, .busy = idlestats_busy, + .sleep = idlestats_sleep, .close = idlestats_close }; diff --git a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c index 45f76070d25ee..f3e84e45bef44 100644 --- a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c +++ b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c @@ -166,7 +166,7 @@ static int tz_init(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) struct tz_priv *priv; /* Trustzone is only valid for some SOCs */ - if (!(cpu_is_msm8x60() || cpu_is_msm8960())) + if (!(cpu_is_msm8x60() || cpu_is_msm8960() || cpu_is_msm8930())) return -EINVAL; priv = pwrscale->priv = kzalloc(sizeof(struct tz_priv), GFP_KERNEL); diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index 09070e4e16112..b59761d0d69fe 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -19,6 +19,52 @@ #include "kgsl_cffdump.h" #include "kgsl_device.h" +/* An attribute for showing per-process memory statistics */ +struct kgsl_mem_entry_attribute { + struct attribute attr; + int memtype; + ssize_t (*show)(struct kgsl_process_private *priv, + int type, char *buf); +}; + +#define to_mem_entry_attr(a) \ +container_of(a, struct kgsl_mem_entry_attribute, attr) + +#define __MEM_ENTRY_ATTR(_type, _name, _show) \ +{ \ + .attr = { .name = __stringify(_name), .mode = 0444 }, \ + .memtype = _type, \ + .show = _show, \ +} + +/* + * A structure to hold the attributes for a particular memory type. + * For each memory type in each process we store the current and maximum + * memory usage and display the counts in sysfs. This structure and + * the following macro allow us to simplify the definition for those + * adding new memory types + */ + +struct mem_entry_stats { + int memtype; + struct kgsl_mem_entry_attribute attr; + struct kgsl_mem_entry_attribute max_attr; +}; + + +#define MEM_ENTRY_STAT(_type, _name) \ +{ \ + .memtype = _type, \ + .attr = __MEM_ENTRY_ATTR(_type, _name, mem_entry_show), \ + .max_attr = __MEM_ENTRY_ATTR(_type, _name##_max, \ + mem_entry_max_show), \ +} + + +/** + * Given a kobj, find the process structure attached to it + */ + static struct kgsl_process_private * _get_priv_from_kobj(struct kobject *kobj) { @@ -39,87 +85,109 @@ _get_priv_from_kobj(struct kobject *kobj) return NULL; } -/* sharedmem / memory sysfs files */ +/** + * Show the current amount of memory allocated for the given memtype + */ static ssize_t -process_show(struct kobject *kobj, - struct kobj_attribute *attr, - char *buf) +mem_entry_show(struct kgsl_process_private *priv, int type, char *buf) { + return snprintf(buf, PAGE_SIZE, "%d\n", priv->stats[type].cur); +} + +/** + * Show the maximum memory allocated for the given memtype through the life of + * the process + */ + +static ssize_t +mem_entry_max_show(struct kgsl_process_private *priv, int type, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", priv->stats[type].max); +} + + +static void mem_entry_sysfs_release(struct kobject *kobj) +{ +} + +static ssize_t mem_entry_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct kgsl_mem_entry_attribute *pattr = to_mem_entry_attr(attr); struct kgsl_process_private *priv; - unsigned int val = 0; + ssize_t ret; mutex_lock(&kgsl_driver.process_mutex); priv = _get_priv_from_kobj(kobj); - if (priv == NULL) { - mutex_unlock(&kgsl_driver.process_mutex); - return 0; - } - - if (!strncmp(attr->attr.name, "user", 4)) - val = priv->stats.user; - if (!strncmp(attr->attr.name, "user_max", 8)) - val = priv->stats.user_max; - if (!strncmp(attr->attr.name, "mapped", 6)) - val = priv->stats.mapped; - if (!strncmp(attr->attr.name, "mapped_max", 10)) - val = priv->stats.mapped_max; - if (!strncmp(attr->attr.name, "flushes", 7)) - val = priv->stats.flushes; + if (priv && pattr->show) + ret = pattr->show(priv, pattr->memtype, buf); + else + ret = -EIO; mutex_unlock(&kgsl_driver.process_mutex); - return snprintf(buf, PAGE_SIZE, "%u\n", val); + return ret; } -#define KGSL_MEMSTAT_ATTR(_name, _show) \ - static struct kobj_attribute attr_##_name = \ - __ATTR(_name, 0444, _show, NULL) - -KGSL_MEMSTAT_ATTR(user, process_show); -KGSL_MEMSTAT_ATTR(user_max, process_show); -KGSL_MEMSTAT_ATTR(mapped, process_show); -KGSL_MEMSTAT_ATTR(mapped_max, process_show); -KGSL_MEMSTAT_ATTR(flushes, process_show); +static const struct sysfs_ops mem_entry_sysfs_ops = { + .show = mem_entry_sysfs_show, +}; -static struct attribute *process_attrs[] = { - &attr_user.attr, - &attr_user_max.attr, - &attr_mapped.attr, - &attr_mapped_max.attr, - &attr_flushes.attr, - NULL +static struct kobj_type ktype_mem_entry = { + .sysfs_ops = &mem_entry_sysfs_ops, + .default_attrs = NULL, + .release = mem_entry_sysfs_release }; -static struct attribute_group process_attr_group = { - .attrs = process_attrs, +static struct mem_entry_stats mem_stats[] = { + MEM_ENTRY_STAT(KGSL_MEM_ENTRY_KERNEL, kernel), +#ifdef CONFIG_ANDROID_PMEM + MEM_ENTRY_STAT(KGSL_MEM_ENTRY_PMEM, pmem), +#endif +#ifdef CONFIG_ASHMEM + MEM_ENTRY_STAT(KGSL_MEM_ENTRY_ASHMEM, ashmem), +#endif + MEM_ENTRY_STAT(KGSL_MEM_ENTRY_USER, user), +#ifdef CONFIG_ION + MEM_ENTRY_STAT(KGSL_MEM_ENTRY_USER, ion), +#endif }; void kgsl_process_uninit_sysfs(struct kgsl_process_private *private) { - /* Remove the sysfs entry */ - if (private->kobj) { - sysfs_remove_group(private->kobj, &process_attr_group); - kobject_put(private->kobj); + int i; + + for (i = 0; i < ARRAY_SIZE(mem_stats); i++) { + sysfs_remove_file(&private->kobj, &mem_stats[i].attr.attr); + sysfs_remove_file(&private->kobj, + &mem_stats[i].max_attr.attr); } + + kobject_put(&private->kobj); } void kgsl_process_init_sysfs(struct kgsl_process_private *private) { unsigned char name[16]; + int i, ret; - /* Add a entry to the sysfs device */ snprintf(name, sizeof(name), "%d", private->pid); - private->kobj = kobject_create_and_add(name, kgsl_driver.prockobj); - /* sysfs failure isn't fatal, just annoying */ - if (private->kobj != NULL) { - if (sysfs_create_group(private->kobj, &process_attr_group)) { - kobject_put(private->kobj); - private->kobj = NULL; - } + if (kobject_init_and_add(&private->kobj, &ktype_mem_entry, + kgsl_driver.prockobj, name)) + return; + + for (i = 0; i < ARRAY_SIZE(mem_stats); i++) { + /* We need to check the value of sysfs_create_file, but we + * don't really care if it passed or not */ + + ret = sysfs_create_file(&private->kobj, + &mem_stats[i].attr.attr); + ret = sysfs_create_file(&private->kobj, + &mem_stats[i].max_attr.attr); } } @@ -207,28 +275,21 @@ static void _outer_cache_range_op(int op, unsigned long addr, size_t size) break; } } -#endif -static unsigned long kgsl_vmalloc_physaddr(struct kgsl_memdesc *memdesc, - unsigned int offset) +static void outer_cache_range_op_sg(struct scatterlist *sg, int sglen, int op) { - unsigned int addr; - - if (offset > memdesc->size) - return 0; + struct scatterlist *s; + int i; - addr = vmalloc_to_pfn(memdesc->hostptr + offset); - return addr << PAGE_SHIFT; + for_each_sg(sg, s, sglen, i) { + unsigned int paddr = sg_phys(s); + _outer_cache_range_op(op, paddr, s->length); + } } -#ifdef CONFIG_OUTER_CACHE -static void kgsl_vmalloc_outer_cache(struct kgsl_memdesc *memdesc, int op) +#else +static void outer_cache_range_op_sg(struct scatterlist *sg, int sglen, int op) { - void *vaddr = memdesc->hostptr; - for (; vaddr < (memdesc->hostptr + memdesc->size); vaddr += PAGE_SIZE) { - unsigned long paddr = page_to_phys(vmalloc_to_page(vaddr)); - _outer_cache_range_op(op, paddr, PAGE_SIZE); - } } #endif @@ -306,88 +367,24 @@ static void kgsl_coherent_free(struct kgsl_memdesc *memdesc) memdesc->hostptr, memdesc->physaddr); } -static unsigned long kgsl_contiguous_physaddr(struct kgsl_memdesc *memdesc, - unsigned int offset) -{ - if (offset > memdesc->size) - return 0; - - return memdesc->physaddr + offset; -} - -#ifdef CONFIG_OUTER_CACHE -static void kgsl_contiguous_outer_cache(struct kgsl_memdesc *memdesc, int op) -{ - _outer_cache_range_op(op, memdesc->physaddr, memdesc->size); -} -#endif - -#ifdef CONFIG_OUTER_CACHE -static void kgsl_userptr_outer_cache(struct kgsl_memdesc *memdesc, int op) -{ - void *vaddr = memdesc->hostptr; - for (; vaddr < (memdesc->hostptr + memdesc->size); vaddr += PAGE_SIZE) { - unsigned long paddr = kgsl_virtaddr_to_physaddr(vaddr); - if (paddr) - _outer_cache_range_op(op, paddr, PAGE_SIZE); - } -} -#endif - -static unsigned long kgsl_userptr_physaddr(struct kgsl_memdesc *memdesc, - unsigned int offset) -{ - return kgsl_virtaddr_to_physaddr(memdesc->hostptr + offset); -} - /* Global - also used by kgsl_drm.c */ struct kgsl_memdesc_ops kgsl_vmalloc_ops = { - .physaddr = kgsl_vmalloc_physaddr, .free = kgsl_vmalloc_free, .vmflags = kgsl_vmalloc_vmflags, .vmfault = kgsl_vmalloc_vmfault, -#ifdef CONFIG_OUTER_CACHE - .outer_cache = kgsl_vmalloc_outer_cache, -#endif }; EXPORT_SYMBOL(kgsl_vmalloc_ops); static struct kgsl_memdesc_ops kgsl_ebimem_ops = { - .physaddr = kgsl_contiguous_physaddr, .free = kgsl_ebimem_free, .vmflags = kgsl_contiguous_vmflags, .vmfault = kgsl_contiguous_vmfault, -#ifdef CONFIG_OUTER_CACHE - .outer_cache = kgsl_contiguous_outer_cache, -#endif }; static struct kgsl_memdesc_ops kgsl_coherent_ops = { - .physaddr = kgsl_contiguous_physaddr, .free = kgsl_coherent_free, -#ifdef CONFIG_OUTER_CACHE - .outer_cache = kgsl_contiguous_outer_cache, -#endif }; -/* Global - also used by kgsl.c and kgsl_drm.c */ -struct kgsl_memdesc_ops kgsl_contiguous_ops = { - .physaddr = kgsl_contiguous_physaddr, -#ifdef CONFIG_OUTER_CACHE - .outer_cache = kgsl_contiguous_outer_cache -#endif -}; -EXPORT_SYMBOL(kgsl_contiguous_ops); - -/* Global - also used by kgsl.c */ -struct kgsl_memdesc_ops kgsl_userptr_ops = { - .physaddr = kgsl_userptr_physaddr, -#ifdef CONFIG_OUTER_CACHE - .outer_cache = kgsl_userptr_outer_cache, -#endif -}; -EXPORT_SYMBOL(kgsl_userptr_ops); - void kgsl_cache_range_op(struct kgsl_memdesc *memdesc, int op) { void *addr = memdesc->hostptr; @@ -405,8 +402,7 @@ void kgsl_cache_range_op(struct kgsl_memdesc *memdesc, int op) break; } - if (memdesc->ops->outer_cache) - memdesc->ops->outer_cache(memdesc, op); + outer_cache_range_op_sg(memdesc->sg, memdesc->sglen, op); } EXPORT_SYMBOL(kgsl_cache_range_op); @@ -415,7 +411,9 @@ _kgsl_sharedmem_vmalloc(struct kgsl_memdesc *memdesc, struct kgsl_pagetable *pagetable, void *ptr, size_t size, unsigned int protflags) { - int result; + int order, ret = 0; + int sglen = PAGE_ALIGN(size) / PAGE_SIZE; + int i; memdesc->size = size; memdesc->pagetable = pagetable; @@ -423,25 +421,44 @@ _kgsl_sharedmem_vmalloc(struct kgsl_memdesc *memdesc, memdesc->ops = &kgsl_vmalloc_ops; memdesc->hostptr = (void *) ptr; + memdesc->sg = kmalloc(sglen * sizeof(struct scatterlist), GFP_KERNEL); + if (memdesc->sg == NULL) { + ret = -ENOMEM; + goto done; + } + + memdesc->sglen = sglen; + sg_init_table(memdesc->sg, sglen); + + for (i = 0; i < memdesc->sglen; i++, ptr += PAGE_SIZE) { + struct page *page = vmalloc_to_page(ptr); + if (!page) { + ret = -EINVAL; + goto done; + } + sg_set_page(&memdesc->sg[i], page, PAGE_SIZE, 0); + } + kgsl_cache_range_op(memdesc, KGSL_CACHE_OP_INV); - result = kgsl_mmu_map(pagetable, memdesc, protflags); + ret = kgsl_mmu_map(pagetable, memdesc, protflags); - if (result) { - kgsl_sharedmem_free(memdesc); - } else { - int order; + if (ret) + goto done; - KGSL_STATS_ADD(size, kgsl_driver.stats.vmalloc, - kgsl_driver.stats.vmalloc_max); + KGSL_STATS_ADD(size, kgsl_driver.stats.vmalloc, + kgsl_driver.stats.vmalloc_max); - order = get_order(size); + order = get_order(size); - if (order < 16) - kgsl_driver.stats.histogram[order]++; - } + if (order < 16) + kgsl_driver.stats.histogram[order]++; - return result; +done: + if (ret) + kgsl_sharedmem_free(memdesc); + + return ret; } int @@ -494,24 +511,35 @@ EXPORT_SYMBOL(kgsl_sharedmem_vmalloc_user); int kgsl_sharedmem_alloc_coherent(struct kgsl_memdesc *memdesc, size_t size) { + int result = 0; + size = ALIGN(size, PAGE_SIZE); + memdesc->size = size; + memdesc->ops = &kgsl_coherent_ops; + memdesc->hostptr = dma_alloc_coherent(NULL, size, &memdesc->physaddr, GFP_KERNEL); if (memdesc->hostptr == NULL) { KGSL_CORE_ERR("dma_alloc_coherent(%d) failed\n", size); - return -ENOMEM; + result = -ENOMEM; + goto err; } - memdesc->size = size; - memdesc->ops = &kgsl_coherent_ops; + result = memdesc_sg_phys(memdesc, memdesc->physaddr, size); + if (result) + goto err; /* Record statistics */ KGSL_STATS_ADD(size, kgsl_driver.stats.coherent, kgsl_driver.stats.coherent_max); - return 0; +err: + if (result) + kgsl_sharedmem_free(memdesc); + + return result; } EXPORT_SYMBOL(kgsl_sharedmem_alloc_coherent); @@ -523,9 +551,11 @@ void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc) if (memdesc->gpuaddr) kgsl_mmu_unmap(memdesc->pagetable, memdesc); - if (memdesc->ops->free) + if (memdesc->ops && memdesc->ops->free) memdesc->ops->free(memdesc); + kfree(memdesc->sg); + memset(memdesc, 0, sizeof(*memdesc)); } EXPORT_SYMBOL(kgsl_sharedmem_free); @@ -534,8 +564,11 @@ static int _kgsl_sharedmem_ebimem(struct kgsl_memdesc *memdesc, struct kgsl_pagetable *pagetable, size_t size) { - int result; + int result = 0; + memdesc->size = size; + memdesc->pagetable = pagetable; + memdesc->ops = &kgsl_ebimem_ops; memdesc->physaddr = allocate_contiguous_ebi_nomap(size, SZ_8K); if (memdesc->physaddr == 0) { @@ -544,19 +577,24 @@ _kgsl_sharedmem_ebimem(struct kgsl_memdesc *memdesc, return -ENOMEM; } - memdesc->size = size; - memdesc->pagetable = pagetable; - memdesc->ops = &kgsl_ebimem_ops; + result = memdesc_sg_phys(memdesc, memdesc->physaddr, size); + + if (result) + goto err; result = kgsl_mmu_map(pagetable, memdesc, GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); if (result) - kgsl_sharedmem_free(memdesc); + goto err; KGSL_STATS_ADD(size, kgsl_driver.stats.coherent, kgsl_driver.stats.coherent_max); +err: + if (result) + kgsl_sharedmem_free(memdesc); + return result; } diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h index 9e57e78c061db..d345ff340725f 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.h +++ b/drivers/gpu/msm/kgsl_sharedmem.h @@ -13,7 +13,9 @@ #ifndef __KGSL_SHAREDMEM_H #define __KGSL_SHAREDMEM_H +#include #include +#include "kgsl_mmu.h" struct kgsl_device; struct kgsl_process_private; @@ -26,8 +28,6 @@ struct kgsl_process_private; #define KGSL_MEMFLAGS_CACHED 0x00000001 struct kgsl_memdesc_ops { - unsigned long (*physaddr)(struct kgsl_memdesc *, unsigned int); - void (*outer_cache)(struct kgsl_memdesc *, int); int (*vmflags)(struct kgsl_memdesc *); int (*vmfault)(struct kgsl_memdesc *, struct vm_area_struct *, struct vm_fault *); @@ -35,8 +35,6 @@ struct kgsl_memdesc_ops { }; extern struct kgsl_memdesc_ops kgsl_vmalloc_ops; -extern struct kgsl_memdesc_ops kgsl_contiguous_ops; -extern struct kgsl_memdesc_ops kgsl_userptr_ops; int kgsl_sharedmem_vmalloc(struct kgsl_memdesc *memdesc, struct kgsl_pagetable *pagetable, size_t size); @@ -77,15 +75,29 @@ void kgsl_process_uninit_sysfs(struct kgsl_process_private *private); int kgsl_sharedmem_init_sysfs(void); void kgsl_sharedmem_uninit_sysfs(void); +static inline int +memdesc_sg_phys(struct kgsl_memdesc *memdesc, + unsigned int physaddr, unsigned int size) +{ + struct page *page = phys_to_page(physaddr); + + memdesc->sg = kmalloc(sizeof(struct scatterlist) * 1, GFP_KERNEL); + if (memdesc->sg == NULL) + return -ENOMEM; + + memdesc->sglen = 1; + sg_init_table(memdesc->sg, 1); + sg_set_page(&memdesc->sg[0], page, size, 0); + return 0; +} + static inline int kgsl_allocate(struct kgsl_memdesc *memdesc, struct kgsl_pagetable *pagetable, size_t size) { -#ifdef CONFIG_MSM_KGSL_MMU + if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE) + return kgsl_sharedmem_ebimem(memdesc, pagetable, size); return kgsl_sharedmem_vmalloc(memdesc, pagetable, size); -#else - return kgsl_sharedmem_ebimem(memdesc, pagetable, size); -#endif } static inline int @@ -93,21 +105,18 @@ kgsl_allocate_user(struct kgsl_memdesc *memdesc, struct kgsl_pagetable *pagetable, size_t size, unsigned int flags) { -#ifdef CONFIG_MSM_KGSL_MMU + if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE) + return kgsl_sharedmem_ebimem_user(memdesc, pagetable, size, + flags); return kgsl_sharedmem_vmalloc_user(memdesc, pagetable, size, flags); -#else - return kgsl_sharedmem_ebimem_user(memdesc, pagetable, size, flags); -#endif } static inline int kgsl_allocate_contiguous(struct kgsl_memdesc *memdesc, size_t size) { int ret = kgsl_sharedmem_alloc_coherent(memdesc, size); -#ifndef CONFIG_MSM_KGSL_MMU - if (!ret) + if (!ret && (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_NONE)) memdesc->gpuaddr = memdesc->physaddr; -#endif return ret; } diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c index 37ba6219efb3f..cdb9c235c7efa 100644 --- a/drivers/gpu/msm/z180.c +++ b/drivers/gpu/msm/z180.c @@ -234,6 +234,7 @@ static irqreturn_t z180_isr(int irq, void *data) count &= 255; z180_dev->timestamp += count; + queue_work(device->work_queue, &device->ts_expired_ws); wake_up_interruptible(&device->wait_queue); atomic_notifier_call_chain( @@ -377,7 +378,8 @@ static int z180_idle(struct kgsl_device *device, unsigned int timeout) int status = 0; struct z180_device *z180_dev = Z180_DEVICE(device); - if (z180_dev->current_timestamp > z180_dev->timestamp) + if (timestamp_cmp(z180_dev->current_timestamp, + z180_dev->timestamp) > 0) status = z180_wait(device, z180_dev->current_timestamp, timeout); @@ -410,7 +412,7 @@ z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv, unsigned int sizedwords; if (device->state & KGSL_STATE_HUNG) { - return -EINVAL; + result = -EINVAL; goto error; } if (numibs != 1) { @@ -564,7 +566,7 @@ static int z180_start(struct kgsl_device *device, unsigned int init_ram) z180_cmdstream_start(device); mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT); - kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_IRQ_ON); + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON); return 0; error_clk_off: @@ -643,15 +645,10 @@ static int z180_getproperty(struct kgsl_device *device, static unsigned int z180_isidle(struct kgsl_device *device) { - int status = false; struct z180_device *z180_dev = Z180_DEVICE(device); - int timestamp = z180_dev->timestamp; - - if (timestamp == z180_dev->current_timestamp) - status = true; - - return status; + return (timestamp_cmp(z180_dev->timestamp, + z180_dev->current_timestamp) == 0) ? true : false; } static int z180_suspend_context(struct kgsl_device *device) @@ -806,6 +803,11 @@ static int z180_waittimestamp(struct kgsl_device *device, unsigned int msecs) { int status = -EINVAL; + + /* Don't wait forever, set a max (10 sec) value for now */ + if (msecs == -1) + msecs = 10 * MSEC_PER_SEC; + mutex_unlock(&device->mutex); status = z180_wait(device, timestamp, msecs); mutex_lock(&device->mutex); diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index ae26f2700b35b..273850ff7045b 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -2,7 +2,7 @@ #define _MSM_KGSL_H #define KGSL_VERSION_MAJOR 3 -#define KGSL_VERSION_MINOR 7 +#define KGSL_VERSION_MINOR 8 /*context flags */ #define KGSL_CONTEXT_SAVE_GMEM 1 @@ -27,6 +27,9 @@ #define KGSL_MAX_PWRLEVELS 5 +#define KGSL_CONVERT_TO_MBPS(val) \ + (val*1000*1000U) + /* device id */ enum kgsl_deviceid { KGSL_DEVICE_3D0 = 0x00000000, @@ -38,7 +41,8 @@ enum kgsl_deviceid { enum kgsl_user_mem_type { KGSL_USER_MEM_TYPE_PMEM = 0x00000000, KGSL_USER_MEM_TYPE_ASHMEM = 0x00000001, - KGSL_USER_MEM_TYPE_ADDR = 0x00000002 + KGSL_USER_MEM_TYPE_ADDR = 0x00000002, + KGSL_USER_MEM_TYPE_ION = 0x00000003, }; struct kgsl_devinfo { @@ -107,6 +111,7 @@ struct kgsl_shadowprop { struct kgsl_pwrlevel { unsigned int gpu_freq; unsigned int bus_freq; + unsigned int io_fraction; }; struct kgsl_version { @@ -422,6 +427,30 @@ struct kgsl_cff_syncmem { #define IOCTL_KGSL_CFF_SYNCMEM \ _IOW(KGSL_IOC_TYPE, 0x30, struct kgsl_cff_syncmem) +/* + * A timestamp event allows the user space to register an action following an + * expired timestamp. + */ + +struct kgsl_timestamp_event { + int type; /* Type of event (see list below) */ + unsigned int timestamp; /* Timestamp to trigger event on */ + unsigned int context_id; /* Context for the timestamp */ + void *priv; /* Pointer to the event specific blob */ + size_t len; /* Size of the event specific blob */ +}; + +#define IOCTL_KGSL_TIMESTAMP_EVENT \ + _IOW(KGSL_IOC_TYPE, 0x31, struct kgsl_timestamp_event) + +/* A genlock timestamp event releases an existing lock on timestamp expire */ + +#define KGSL_TIMESTAMP_EVENT_GENLOCK 1 + +struct kgsl_timestamp_event_genlock { + int handle; /* Handle of the genlock lock to release */ +}; + #ifdef __KERNEL__ #ifdef CONFIG_MSM_KGSL_DRM int kgsl_gem_obj_addr(int drm_fd, int handle, unsigned long *start, From 6460b70887ab5a651b02cac00a19b7f6304d3bf6 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 17 May 2012 00:42:22 -0500 Subject: [PATCH 2238/2556] msm: socinfo update for cpu_is_msm8930 --- arch/arm/mach-msm/include/mach/socinfo.h | 54 ++++++++++++------------ arch/arm/mach-msm/socinfo.c | 39 ++++++++++++----- 2 files changed, 56 insertions(+), 37 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/socinfo.h b/arch/arm/mach-msm/include/mach/socinfo.h index 4d68132964a80..fba6efe251014 100644 --- a/arch/arm/mach-msm/include/mach/socinfo.h +++ b/arch/arm/mach-msm/include/mach/socinfo.h @@ -1,29 +1,13 @@ /* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * 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. * */ @@ -32,6 +16,9 @@ #include #include +#include +#include +#include #include #include @@ -44,6 +31,16 @@ #define SOCINFO_VERSION_MAJOR(ver) ((ver & 0xffff0000) >> 16) #define SOCINFO_VERSION_MINOR(ver) (ver & 0x0000ffff) +#ifdef CONFIG_OF +#define early_machine_is_copper() \ + of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msmcopper") +#define machine_is_copper() \ + of_machine_is_compatible("qcom,msmcopper") +#else +#define early_machine_is_copper() 0 +#define machine_is_copper() 0 +#endif + enum msm_cpu { MSM_CPU_UNKNOWN = 0, MSM_CPU_7X01, @@ -60,9 +57,10 @@ enum msm_cpu { MSM_CPU_7X25A, MSM_CPU_7X25AA, MSM_CPU_8064, - MSM_CPU_8X30, + MSM_CPU_8930, MSM_CPU_7X27AA, MSM_CPU_9615, + MSM_CPU_COPPER, }; enum msm_cpu socinfo_get_msm_cpu(void); @@ -224,9 +222,13 @@ static inline int cpu_is_apq8064(void) #endif } -static inline int cpu_is_msm8x30(void) +static inline int cpu_is_msm8930(void) { - return read_msm_cpu_type() == MSM_CPU_8X30; +#ifdef CONFIG_ARCH_MSM8930 + return read_msm_cpu_type() == MSM_CPU_8930; +#else + return 0; +#endif } static inline int cpu_is_fsm9xxx(void) diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c index 1e65e92054fbe..c4bc76c09968a 100644 --- a/arch/arm/mach-msm/socinfo.c +++ b/arch/arm/mach-msm/socinfo.c @@ -31,6 +31,7 @@ enum { HW_PLATFORM_FLUID = 3, HW_PLATFORM_SVLTE_FFA = 4, HW_PLATFORM_SVLTE_SURF = 5, + HW_PLATFORM_LIQUID = 9, /* Dragonboard platform id is assigned as 10 in CDT */ HW_PLATFORM_DRAGON = 10, HW_PLATFORM_INVALID @@ -41,6 +42,7 @@ const char *hw_platform[] = { [HW_PLATFORM_SURF] = "Surf", [HW_PLATFORM_FFA] = "FFA", [HW_PLATFORM_FLUID] = "Fluid", + [HW_PLATFORM_LIQUID] = "Liquid", [HW_PLATFORM_SVLTE_FFA] = "SVLTE_FFA", [HW_PLATFORM_SVLTE_SURF] = "SLVTE_SURF", [HW_PLATFORM_DRAGON] = "Dragon" @@ -164,10 +166,6 @@ static enum msm_cpu cpu_of_id[] = { [37] = MSM_CPU_8X50, [38] = MSM_CPU_8X50, - /* 8x50A IDs */ - [64] = MSM_CPU_8X50A, - [65] = MSM_CPU_8X50A, - /* 7x30 IDs */ [59] = MSM_CPU_7X30, [60] = MSM_CPU_7X30, @@ -214,9 +212,24 @@ static enum msm_cpu cpu_of_id[] = { [104] = MSM_CPU_9615, [105] = MSM_CPU_9615, - /* 8064 IDs*/ + /* 8064 IDs */ [109] = MSM_CPU_8064, + /* 8930 IDs */ + [116] = MSM_CPU_8930, + + /* 8660A ID */ + [122] = MSM_CPU_8960, + + /* 8260A ID */ + [123] = MSM_CPU_8960, + + /* 8060A ID */ + [124] = MSM_CPU_8960, + + /* Copper IDs */ + [126] = MSM_CPU_COPPER, + /* Uninitialized IDs are not known to run Linux. MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are considered as unknown CPU. */ @@ -583,6 +596,8 @@ void *setup_dummy_socinfo(void) dummy_socinfo.id = 109; else if (machine_is_msm9615_mtp() || machine_is_msm9615_cdp()) dummy_socinfo.id = 104; + else if (early_machine_is_copper()) + dummy_socinfo.id = 126; return (void *) &dummy_socinfo; } @@ -704,9 +719,12 @@ const int get_core_count(void) const int read_msm_cpu_type(void) { - if (machine_is_msm8960_sim()) + if (machine_is_msm8960_sim() || machine_is_msm8960_rumi3()) return MSM_CPU_8960; + if (socinfo_get_msm_cpu() != MSM_CPU_UNKNOWN) + return socinfo_get_msm_cpu(); + switch (read_cpuid_id()) { case 0x510F02D0: case 0x510F02D2: @@ -716,13 +734,12 @@ const int read_msm_cpu_type(void) case 0x510F04D0: case 0x510F04D1: case 0x510F04D2: + case 0x511F04D0: + case 0x512F04D0: return MSM_CPU_8960; - case 0x511F04D0: - if (get_core_count() == 2) - return MSM_CPU_8960; - else - return MSM_CPU_8X30; + case 0x51404D11: /* We can't get here unless we are in bringup */ + return MSM_CPU_8930; case 0x510F06F0: return MSM_CPU_8064; From a2ef2748c67794b032b7ce16418ce40ac1b7dfdd Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Zavin Date: Wed, 29 Jun 2011 19:44:29 -0700 Subject: [PATCH 2239/2556] gpu: ion: Add ION Memory Manager Signed-off-by: Rebecca Schultz Zavin Conflicts: drivers/gpu/Makefile drivers/video/Kconfig also add msm ion to fix kgsl build Signed-off-by: Andrew Sutherland --- arch/arm/mach-msm/include/mach/ion.h | 23 + drivers/gpu/Makefile | 2 +- drivers/gpu/ion/Kconfig | 17 + drivers/gpu/ion/Makefile | 3 + drivers/gpu/ion/ion.c | 1464 ++++++++++++++++++++++++++ drivers/gpu/ion/ion_carveout_heap.c | 253 +++++ drivers/gpu/ion/ion_heap.c | 72 ++ drivers/gpu/ion/ion_priv.h | 192 ++++ drivers/gpu/ion/ion_system_heap.c | 317 ++++++ drivers/gpu/ion/ion_system_mapper.c | 114 ++ drivers/gpu/ion/msm/Makefile | 1 + drivers/gpu/ion/msm/msm_ion.c | 134 +++ drivers/gpu/ion/tegra/Makefile | 1 + drivers/gpu/ion/tegra/tegra_ion.c | 96 ++ drivers/video/Kconfig | 2 + include/linux/ion.h | 556 ++++++++++ 16 files changed, 3246 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-msm/include/mach/ion.h create mode 100644 drivers/gpu/ion/Kconfig create mode 100644 drivers/gpu/ion/Makefile create mode 100644 drivers/gpu/ion/ion.c create mode 100644 drivers/gpu/ion/ion_carveout_heap.c create mode 100644 drivers/gpu/ion/ion_heap.c create mode 100644 drivers/gpu/ion/ion_priv.h create mode 100644 drivers/gpu/ion/ion_system_heap.c create mode 100644 drivers/gpu/ion/ion_system_mapper.c create mode 100644 drivers/gpu/ion/msm/Makefile create mode 100644 drivers/gpu/ion/msm/msm_ion.c create mode 100644 drivers/gpu/ion/tegra/Makefile create mode 100644 drivers/gpu/ion/tegra/tegra_ion.c create mode 100644 include/linux/ion.h diff --git a/arch/arm/mach-msm/include/mach/ion.h b/arch/arm/mach-msm/include/mach/ion.h new file mode 100644 index 0000000000000..4d12249d567a9 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/ion.h @@ -0,0 +1,23 @@ +/** + * + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MACH_ION_H_ +#define __MACH_ION_H_ + +enum ion_memory_types { + ION_EBI_TYPE, + ION_SMI_TYPE, +}; + +#endif diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile index 5cde134d19db6..01cef64bf4d29 100644 --- a/drivers/gpu/Makefile +++ b/drivers/gpu/Makefile @@ -1,2 +1,2 @@ -obj-y += drm/ vga/ stub/ +obj-y += drm/ vga/ stub/ ion/ obj-$(CONFIG_MSM_KGSL) += msm/ diff --git a/drivers/gpu/ion/Kconfig b/drivers/gpu/ion/Kconfig new file mode 100644 index 0000000000000..f4affe413f5c4 --- /dev/null +++ b/drivers/gpu/ion/Kconfig @@ -0,0 +1,17 @@ +menuconfig ION + tristate "Ion Memory Manager" + select GENERIC_ALLOCATOR + help + Chose this option to enable the ION Memory Manager. + +config ION_TEGRA + tristate "Ion for Tegra" + depends on ARCH_TEGRA && ION + help + Choose this option if you wish to use ion on an nVidia Tegra. + +config ION_MSM + tristate "Ion for MSM" + depends on ARCH_MSM && ION + help + Choose this option if you wish to use ion on an MSM target. diff --git a/drivers/gpu/ion/Makefile b/drivers/gpu/ion/Makefile new file mode 100644 index 0000000000000..c0a47d81be77b --- /dev/null +++ b/drivers/gpu/ion/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_ION) += ion.o ion_heap.o ion_system_heap.o ion_carveout_heap.o +obj-$(CONFIG_ION_TEGRA) += tegra/ +obj-$(CONFIG_ION_MSM) += msm/ diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c new file mode 100644 index 0000000000000..50420ba5c7e7b --- /dev/null +++ b/drivers/gpu/ion/ion.c @@ -0,0 +1,1464 @@ +/* + * drivers/gpu/ion/ion.c + * + * Copyright (C) 2011 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ion_priv.h" +#define DEBUG + +/** + * struct ion_device - the metadata of the ion device node + * @dev: the actual misc device + * @buffers: an rb tree of all the existing buffers + * @lock: lock protecting the buffers & heaps trees + * @heaps: list of all the heaps in the system + * @user_clients: list of all the clients created from userspace + */ +struct ion_device { + struct miscdevice dev; + struct rb_root buffers; + struct mutex lock; + struct rb_root heaps; + long (*custom_ioctl) (struct ion_client *client, unsigned int cmd, + unsigned long arg); + struct rb_root user_clients; + struct rb_root kernel_clients; + struct dentry *debug_root; +}; + +/** + * struct ion_client - a process/hw block local address space + * @ref: for reference counting the client + * @node: node in the tree of all clients + * @dev: backpointer to ion device + * @handles: an rb tree of all the handles in this client + * @lock: lock protecting the tree of handles + * @heap_mask: mask of all supported heaps + * @name: used for debugging + * @task: used for debugging + * + * A client represents a list of buffers this client may access. + * The mutex stored here is used to protect both handles tree + * as well as the handles themselves, and should be held while modifying either. + */ +struct ion_client { + struct kref ref; + struct rb_node node; + struct ion_device *dev; + struct rb_root handles; + struct mutex lock; + unsigned int heap_mask; + const char *name; + struct task_struct *task; + pid_t pid; + struct dentry *debug_root; +}; + +/** + * ion_handle - a client local reference to a buffer + * @ref: reference count + * @client: back pointer to the client the buffer resides in + * @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 + * @usermap_cnt: count of times this client has mapped for userspace + * + * Modifications to node, map_cnt or mapping should be protected by the + * lock in the client. Other fields are never changed after initialization. + */ +struct ion_handle { + struct kref ref; + struct ion_client *client; + struct ion_buffer *buffer; + struct rb_node node; + unsigned int kmap_cnt; + unsigned int dmap_cnt; + unsigned int usermap_cnt; +}; + +/* this function should only be called while dev->lock is held */ +static void ion_buffer_add(struct ion_device *dev, + struct ion_buffer *buffer) +{ + struct rb_node **p = &dev->buffers.rb_node; + struct rb_node *parent = NULL; + struct ion_buffer *entry; + + while (*p) { + parent = *p; + entry = rb_entry(parent, struct ion_buffer, node); + + if (buffer < entry) { + p = &(*p)->rb_left; + } else if (buffer > entry) { + p = &(*p)->rb_right; + } else { + pr_err("%s: buffer already found.", __func__); + BUG(); + } + } + + rb_link_node(&buffer->node, parent, p); + rb_insert_color(&buffer->node, &dev->buffers); +} + +/* 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, + unsigned long len, + unsigned long align, + unsigned long flags) +{ + struct ion_buffer *buffer; + int ret; + + buffer = kzalloc(sizeof(struct ion_buffer), GFP_KERNEL); + if (!buffer) + return ERR_PTR(-ENOMEM); + + buffer->heap = heap; + kref_init(&buffer->ref); + + ret = heap->ops->allocate(heap, buffer, len, align, flags); + if (ret) { + kfree(buffer); + return ERR_PTR(ret); + } + buffer->dev = dev; + buffer->size = len; + mutex_init(&buffer->lock); + ion_buffer_add(dev, buffer); + return buffer; +} + +static void ion_buffer_destroy(struct kref *kref) +{ + struct ion_buffer *buffer = container_of(kref, struct ion_buffer, ref); + struct ion_device *dev = buffer->dev; + + buffer->heap->ops->free(buffer); + mutex_lock(&dev->lock); + rb_erase(&buffer->node, &dev->buffers); + mutex_unlock(&dev->lock); + kfree(buffer); +} + +static void ion_buffer_get(struct ion_buffer *buffer) +{ + kref_get(&buffer->ref); +} + +static int ion_buffer_put(struct ion_buffer *buffer) +{ + return kref_put(&buffer->ref, ion_buffer_destroy); +} + +static struct ion_handle *ion_handle_create(struct ion_client *client, + struct ion_buffer *buffer) +{ + struct ion_handle *handle; + + handle = kzalloc(sizeof(struct ion_handle), GFP_KERNEL); + if (!handle) + return ERR_PTR(-ENOMEM); + kref_init(&handle->ref); + rb_init_node(&handle->node); + handle->client = client; + ion_buffer_get(buffer); + handle->buffer = buffer; + + return handle; +} + +static void ion_handle_destroy(struct kref *kref) +{ + struct ion_handle *handle = container_of(kref, struct ion_handle, ref); + /* XXX Can a handle be destroyed while it's map count is non-zero?: + if (handle->map_cnt) unmap + */ + WARN_ON(handle->kmap_cnt || handle->dmap_cnt || handle->usermap_cnt); + ion_buffer_put(handle->buffer); + mutex_lock(&handle->client->lock); + if (!RB_EMPTY_NODE(&handle->node)) + rb_erase(&handle->node, &handle->client->handles); + mutex_unlock(&handle->client->lock); + kfree(handle); +} + +struct ion_buffer *ion_handle_buffer(struct ion_handle *handle) +{ + return handle->buffer; +} + +static void ion_handle_get(struct ion_handle *handle) +{ + kref_get(&handle->ref); +} + +static int ion_handle_put(struct ion_handle *handle) +{ + return kref_put(&handle->ref, ion_handle_destroy); +} + +static struct ion_handle *ion_handle_lookup(struct ion_client *client, + struct ion_buffer *buffer) +{ + struct rb_node *n; + + 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; + } + return NULL; +} + +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; +} + +static void ion_handle_add(struct ion_client *client, struct ion_handle *handle) +{ + struct rb_node **p = &client->handles.rb_node; + struct rb_node *parent = NULL; + struct ion_handle *entry; + + while (*p) { + parent = *p; + entry = rb_entry(parent, struct ion_handle, node); + + if (handle < entry) + p = &(*p)->rb_left; + else if (handle > entry) + p = &(*p)->rb_right; + else + WARN(1, "%s: buffer already found.", __func__); + } + + rb_link_node(&handle->node, parent, p); + rb_insert_color(&handle->node, &client->handles); +} + +struct ion_handle *ion_alloc(struct ion_client *client, size_t len, + size_t align, unsigned int flags) +{ + struct rb_node *n; + struct ion_handle *handle; + struct ion_device *dev = client->dev; + struct ion_buffer *buffer = NULL; + + /* + * traverse the list of heaps available in this system in priority + * order. If the heap type is supported by the client, and matches the + * request of the caller allocate from it. Repeat until allocate has + * succeeded or all heaps have been tried + */ + mutex_lock(&dev->lock); + for (n = rb_first(&dev->heaps); n != NULL; n = rb_next(n)) { + struct ion_heap *heap = rb_entry(n, struct ion_heap, node); + /* if the client doesn't support this heap type */ + if (!((1 << heap->type) & client->heap_mask)) + continue; + /* if the caller didn't specify this heap type */ + if (!((1 << heap->id) & flags)) + continue; + buffer = ion_buffer_create(heap, dev, len, align, flags); + if (!IS_ERR_OR_NULL(buffer)) + break; + } + mutex_unlock(&dev->lock); + + if (IS_ERR_OR_NULL(buffer)) + return ERR_PTR(PTR_ERR(buffer)); + + handle = ion_handle_create(client, buffer); + + if (IS_ERR_OR_NULL(handle)) + goto end; + + /* + * ion_buffer_create will create a buffer with a ref_cnt of 1, + * and ion_handle_create will take a second reference, drop one here + */ + ion_buffer_put(buffer); + + mutex_lock(&client->lock); + ion_handle_add(client, handle); + mutex_unlock(&client->lock); + return handle; + +end: + ion_buffer_put(buffer); + return handle; +} + +void ion_free(struct ion_client *client, struct ion_handle *handle) +{ + bool valid_handle; + + BUG_ON(client != handle->client); + + mutex_lock(&client->lock); + valid_handle = ion_handle_validate(client, handle); + mutex_unlock(&client->lock); + + if (!valid_handle) { + WARN("%s: invalid handle passed to free.\n", __func__); + return; + } + ion_handle_put(handle); +} + +static void ion_client_get(struct ion_client *client); +static int ion_client_put(struct ion_client *client); + +static bool _ion_map(int *buffer_cnt, int *handle_cnt) +{ + bool map; + + BUG_ON(*handle_cnt != 0 && *buffer_cnt == 0); + + if (*buffer_cnt) + map = false; + else + map = true; + if (*handle_cnt == 0) + (*buffer_cnt)++; + (*handle_cnt)++; + return map; +} + +static bool _ion_unmap(int *buffer_cnt, int *handle_cnt) +{ + BUG_ON(*handle_cnt == 0); + (*handle_cnt)--; + if (*handle_cnt != 0) + return false; + BUG_ON(*buffer_cnt == 0); + (*buffer_cnt)--; + if (*buffer_cnt == 0) + return true; + return false; +} + +int ion_phys(struct ion_client *client, struct ion_handle *handle, + ion_phys_addr_t *addr, size_t *len) +{ + struct ion_buffer *buffer; + int ret; + + mutex_lock(&client->lock); + if (!ion_handle_validate(client, handle)) { + mutex_unlock(&client->lock); + return -EINVAL; + } + + buffer = handle->buffer; + + if (!buffer->heap->ops->phys) { + pr_err("%s: ion_phys is not implemented by this heap.\n", + __func__); + mutex_unlock(&client->lock); + return -ENODEV; + } + mutex_unlock(&client->lock); + ret = buffer->heap->ops->phys(buffer->heap, buffer, addr, len); + return ret; +} + +void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle, + unsigned long flags) +{ + struct ion_buffer *buffer; + void *vaddr; + + mutex_lock(&client->lock); + if (!ion_handle_validate(client, handle)) { + pr_err("%s: invalid handle passed to map_kernel.\n", + __func__); + mutex_unlock(&client->lock); + return ERR_PTR(-EINVAL); + } + + buffer = handle->buffer; + mutex_lock(&buffer->lock); + + if (!handle->buffer->heap->ops->map_kernel) { + pr_err("%s: map_kernel is not implemented by this heap.\n", + __func__); + mutex_unlock(&buffer->lock); + mutex_unlock(&client->lock); + return ERR_PTR(-ENODEV); + } + + if (buffer->kmap_cnt || buffer->dmap_cnt || buffer->umap_cnt) { + if (buffer->flags != flags) { + pr_err("%s: buffer was already mapped with flags %lx," + " cannot map with flags %lx\n", __func__, + buffer->flags, flags); + vaddr = ERR_PTR(-EEXIST); + goto out; + } + + } else { + buffer->flags = flags; + } + + if (_ion_map(&buffer->kmap_cnt, &handle->kmap_cnt)) { + vaddr = buffer->heap->ops->map_kernel(buffer->heap, buffer, + flags); + if (IS_ERR_OR_NULL(vaddr)) + _ion_unmap(&buffer->kmap_cnt, &handle->kmap_cnt); + buffer->vaddr = vaddr; + } else { + vaddr = buffer->vaddr; + } + +out: + mutex_unlock(&buffer->lock); + mutex_unlock(&client->lock); + return vaddr; +} + +struct scatterlist *ion_map_dma(struct ion_client *client, + struct ion_handle *handle, + unsigned long flags) +{ + struct ion_buffer *buffer; + struct scatterlist *sglist; + + mutex_lock(&client->lock); + if (!ion_handle_validate(client, handle)) { + pr_err("%s: invalid handle passed to map_dma.\n", + __func__); + mutex_unlock(&client->lock); + return ERR_PTR(-EINVAL); + } + buffer = handle->buffer; + mutex_lock(&buffer->lock); + + if (!handle->buffer->heap->ops->map_dma) { + pr_err("%s: map_kernel is not implemented by this heap.\n", + __func__); + mutex_unlock(&buffer->lock); + mutex_unlock(&client->lock); + return ERR_PTR(-ENODEV); + } + + if (buffer->kmap_cnt || buffer->dmap_cnt || buffer->umap_cnt) { + if (buffer->flags != flags) { + pr_err("%s: buffer was already mapped with flags %lx," + " cannot map with flags %lx\n", __func__, + buffer->flags, flags); + sglist = ERR_PTR(-EEXIST); + goto out; + } + + } else { + buffer->flags = flags; + } + + if (_ion_map(&buffer->dmap_cnt, &handle->dmap_cnt)) { + sglist = buffer->heap->ops->map_dma(buffer->heap, buffer); + if (IS_ERR_OR_NULL(sglist)) + _ion_unmap(&buffer->dmap_cnt, &handle->dmap_cnt); + buffer->sglist = sglist; + } else { + sglist = buffer->sglist; + } + +out: + mutex_unlock(&buffer->lock); + mutex_unlock(&client->lock); + return sglist; +} + +void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle) +{ + struct ion_buffer *buffer; + + mutex_lock(&client->lock); + buffer = handle->buffer; + mutex_lock(&buffer->lock); + if (_ion_unmap(&buffer->kmap_cnt, &handle->kmap_cnt)) { + buffer->heap->ops->unmap_kernel(buffer->heap, buffer); + buffer->vaddr = NULL; + } + mutex_unlock(&buffer->lock); + mutex_unlock(&client->lock); +} + +void ion_unmap_dma(struct ion_client *client, struct ion_handle *handle) +{ + struct ion_buffer *buffer; + + mutex_lock(&client->lock); + buffer = handle->buffer; + mutex_lock(&buffer->lock); + if (_ion_unmap(&buffer->dmap_cnt, &handle->dmap_cnt)) { + buffer->heap->ops->unmap_dma(buffer->heap, buffer); + buffer->sglist = NULL; + } + mutex_unlock(&buffer->lock); + mutex_unlock(&client->lock); +} + + +struct ion_buffer *ion_share(struct ion_client *client, + struct ion_handle *handle) +{ + bool valid_handle; + + mutex_lock(&client->lock); + valid_handle = ion_handle_validate(client, handle); + mutex_unlock(&client->lock); + if (!valid_handle) { + WARN("%s: invalid handle passed to share.\n", __func__); + return ERR_PTR(-EINVAL); + } + + /* do not take an extra reference here, the burden is on the caller + * to make sure the buffer doesn't go away while it's passing it + * to another client -- ion_free should not be called on this handle + * until the buffer has been imported into the other client + */ + return handle->buffer; +} + +struct ion_handle *ion_import(struct ion_client *client, + struct ion_buffer *buffer) +{ + struct ion_handle *handle = NULL; + + 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)) { + ion_handle_get(handle); + goto end; + } + handle = ion_handle_create(client, buffer); + if (IS_ERR_OR_NULL(handle)) + goto end; + ion_handle_add(client, handle); +end: + mutex_unlock(&client->lock); + return handle; +} + +static int check_vaddr_bounds(unsigned long start, unsigned long end) +{ + struct mm_struct *mm = current->active_mm; + struct vm_area_struct *vma; + int ret = 1; + + if (end < start) + goto out; + + down_read(&mm->mmap_sem); + vma = find_vma(mm, start); + if (vma && vma->vm_start < end) { + if (start < vma->vm_start) + goto out_up; + if (end > vma->vm_end) + goto out_up; + ret = 0; + } + +out_up: + up_read(&mm->mmap_sem); +out: + return ret; +} + +int ion_do_cache_op(struct ion_client *client, struct ion_handle *handle, + void *uaddr, unsigned long offset, unsigned long len, + unsigned int cmd) +{ + struct ion_buffer *buffer; + int ret = -EINVAL; + + mutex_lock(&client->lock); + if (!ion_handle_validate(client, handle)) { + pr_err("%s: invalid handle passed to do_cache_op.\n", + __func__); + mutex_unlock(&client->lock); + return -EINVAL; + } + buffer = handle->buffer; + mutex_lock(&buffer->lock); + + if (!ION_IS_CACHED(buffer->flags)) { + ret = 0; + goto out; + } + + if (!handle->buffer->heap->ops->cache_op) { + pr_err("%s: cache_op is not implemented by this heap.\n", + __func__); + ret = -ENODEV; + goto out; + } + + + ret = buffer->heap->ops->cache_op(buffer->heap, buffer, uaddr, + offset, len, cmd); + +out: + mutex_unlock(&buffer->lock); + mutex_unlock(&client->lock); + return ret; + +} + +static const struct file_operations ion_share_fops; + +struct ion_handle *ion_import_fd(struct ion_client *client, int fd) +{ + struct file *file = fget(fd); + struct ion_handle *handle; + + if (!file) { + pr_err("%s: imported fd not found in file table.\n", __func__); + return ERR_PTR(-EINVAL); + } + if (file->f_op != &ion_share_fops) { + pr_err("%s: imported file %s is not a shared ion" + " file.", __func__, file->f_dentry->d_name.name); + handle = ERR_PTR(-EINVAL); + goto end; + } + handle = ion_import(client, file->private_data); +end: + fput(file); + return handle; +} + +static int ion_debug_client_show(struct seq_file *s, void *unused) +{ + struct ion_client *client = s->private; + struct rb_node *n; + + seq_printf(s, "%16.16s: %16.16s : %16.16s : %16.16s\n", "heap_name", + "size_in_bytes", "handle refcount", "buffer"); + mutex_lock(&client->lock); + for (n = rb_first(&client->handles); n; n = rb_next(n)) { + struct ion_handle *handle = rb_entry(n, struct ion_handle, + node); + + seq_printf(s, "%16.16s: %16x : %16d : %16p\n", + handle->buffer->heap->name, + handle->buffer->size, + atomic_read(&handle->ref.refcount), + handle->buffer); + } + + seq_printf(s, "%16.16s %d\n", "client refcount:", + atomic_read(&client->ref.refcount)); + mutex_unlock(&client->lock); + + return 0; +} + +static int ion_debug_client_open(struct inode *inode, struct file *file) +{ + return single_open(file, ion_debug_client_show, inode->i_private); +} + +static const struct file_operations debug_client_fops = { + .open = ion_debug_client_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct ion_client *ion_client_lookup(struct ion_device *dev, + struct task_struct *task) +{ + struct rb_node *n = dev->user_clients.rb_node; + struct ion_client *client; + + mutex_lock(&dev->lock); + while (n) { + client = rb_entry(n, struct ion_client, node); + if (task == client->task) { + ion_client_get(client); + mutex_unlock(&dev->lock); + return client; + } else if (task < client->task) { + n = n->rb_left; + } else if (task > client->task) { + n = n->rb_right; + } + } + mutex_unlock(&dev->lock); + return NULL; +} + +struct ion_client *ion_client_create(struct ion_device *dev, + unsigned int heap_mask, + const char *name) +{ + struct ion_client *client; + struct task_struct *task; + struct rb_node **p; + struct rb_node *parent = NULL; + struct ion_client *entry; + char debug_name[64]; + pid_t pid; + + get_task_struct(current->group_leader); + task_lock(current->group_leader); + pid = task_pid_nr(current->group_leader); + /* don't bother to store task struct for kernel threads, + they can't be killed anyway */ + if (current->group_leader->flags & PF_KTHREAD) { + put_task_struct(current->group_leader); + task = NULL; + } else { + task = current->group_leader; + } + task_unlock(current->group_leader); + + /* if this isn't a kernel thread, see if a client already + exists */ + if (task) { + client = ion_client_lookup(dev, task); + if (!IS_ERR_OR_NULL(client)) { + put_task_struct(current->group_leader); + return client; + } + } + + client = kzalloc(sizeof(struct ion_client), GFP_KERNEL); + if (!client) { + put_task_struct(current->group_leader); + return ERR_PTR(-ENOMEM); + } + + client->dev = dev; + client->handles = RB_ROOT; + mutex_init(&client->lock); + client->name = name; + client->heap_mask = heap_mask; + client->task = task; + client->pid = pid; + kref_init(&client->ref); + + mutex_lock(&dev->lock); + if (task) { + p = &dev->user_clients.rb_node; + while (*p) { + parent = *p; + entry = rb_entry(parent, struct ion_client, node); + + if (task < entry->task) + p = &(*p)->rb_left; + else if (task > entry->task) + p = &(*p)->rb_right; + } + rb_link_node(&client->node, parent, p); + rb_insert_color(&client->node, &dev->user_clients); + } else { + p = &dev->kernel_clients.rb_node; + while (*p) { + parent = *p; + entry = rb_entry(parent, struct ion_client, node); + + if (client < entry) + p = &(*p)->rb_left; + else if (client > entry) + p = &(*p)->rb_right; + } + rb_link_node(&client->node, parent, p); + rb_insert_color(&client->node, &dev->kernel_clients); + } + + snprintf(debug_name, 64, "%u", client->pid); + client->debug_root = debugfs_create_file(debug_name, 0664, + dev->debug_root, client, + &debug_client_fops); + mutex_unlock(&dev->lock); + + return client; +} + +static void _ion_client_destroy(struct kref *kref) +{ + struct ion_client *client = container_of(kref, struct ion_client, ref); + struct ion_device *dev = client->dev; + struct rb_node *n; + + pr_debug("%s: %d\n", __func__, __LINE__); + while ((n = rb_first(&client->handles))) { + struct ion_handle *handle = rb_entry(n, struct ion_handle, + node); + ion_handle_destroy(&handle->ref); + } + mutex_lock(&dev->lock); + if (client->task) { + rb_erase(&client->node, &dev->user_clients); + put_task_struct(client->task); + } else { + rb_erase(&client->node, &dev->kernel_clients); + } + debugfs_remove_recursive(client->debug_root); + mutex_unlock(&dev->lock); + + kfree(client); +} + +static void ion_client_get(struct ion_client *client) +{ + kref_get(&client->ref); +} + +static int ion_client_put(struct ion_client *client) +{ + return kref_put(&client->ref, _ion_client_destroy); +} + +void ion_client_destroy(struct ion_client *client) +{ + if (client) + ion_client_put(client); +} + +int ion_handle_get_flags(struct ion_client *client, struct ion_handle *handle, + unsigned long *flags) +{ + struct ion_buffer *buffer; + + mutex_lock(&client->lock); + if (!ion_handle_validate(client, handle)) { + pr_err("%s: invalid handle passed to %s.\n", + __func__, __func__); + mutex_unlock(&client->lock); + return -EINVAL; + } + buffer = handle->buffer; + mutex_lock(&buffer->lock); + *flags = buffer->flags; + mutex_unlock(&buffer->lock); + mutex_unlock(&client->lock); + + return 0; +} +EXPORT_SYMBOL(ion_handle_get_flags); + +static int ion_share_release(struct inode *inode, struct file* file) +{ + struct ion_buffer *buffer = file->private_data; + + pr_debug("%s: %d\n", __func__, __LINE__); + mutex_lock(&buffer->lock); + buffer->umap_cnt--; + mutex_unlock(&buffer->lock); + /* drop the reference to the buffer -- this prevents the + buffer from going away because the client holding it exited + while it was being passed */ + ion_buffer_put(buffer); + return 0; +} + +static void ion_vma_open(struct vm_area_struct *vma) +{ + + struct ion_buffer *buffer = vma->vm_file->private_data; + struct ion_handle *handle = vma->vm_private_data; + struct ion_client *client; + + pr_debug("%s: %d\n", __func__, __LINE__); + /* check that the client still exists and take a reference so + it can't go away until this vma is closed */ + client = ion_client_lookup(buffer->dev, current->group_leader); + if (IS_ERR_OR_NULL(client)) { + vma->vm_private_data = NULL; + return; + } + pr_debug("%s: %d client_cnt %d handle_cnt %d alloc_cnt %d\n", + __func__, __LINE__, + atomic_read(&client->ref.refcount), + atomic_read(&handle->ref.refcount), + atomic_read(&buffer->ref.refcount)); +} + +static void ion_vma_close(struct vm_area_struct *vma) +{ + struct ion_handle *handle = vma->vm_private_data; + struct ion_buffer *buffer = vma->vm_file->private_data; + struct ion_client *client; + + pr_debug("%s: %d\n", __func__, __LINE__); + /* this indicates the client is gone, nothing to do here */ + if (!handle) + return; + client = handle->client; + pr_debug("%s: %d client_cnt %d handle_cnt %d alloc_cnt %d\n", + __func__, __LINE__, + atomic_read(&client->ref.refcount), + atomic_read(&handle->ref.refcount), + atomic_read(&buffer->ref.refcount)); + ion_handle_put(handle); + ion_client_put(client); + pr_debug("%s: %d client_cnt %d handle_cnt %d alloc_cnt %d\n", + __func__, __LINE__, + atomic_read(&client->ref.refcount), + atomic_read(&handle->ref.refcount), + atomic_read(&buffer->ref.refcount)); +} + +static struct vm_operations_struct ion_vm_ops = { + .open = ion_vma_open, + .close = ion_vma_close, +}; + +static int ion_share_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ion_buffer *buffer = file->private_data; + unsigned long size = vma->vm_end - vma->vm_start; + struct ion_client *client; + struct ion_handle *handle; + int ret; + unsigned long flags = file->f_flags & O_DSYNC ? + ION_SET_CACHE(UNCACHED) : + ION_SET_CACHE(CACHED); + + + pr_debug("%s: %d\n", __func__, __LINE__); + /* make sure the client still exists, it's possible for the client to + have gone away but the map/share fd still to be around, take + a reference to it so it can't go away while this mapping exists */ + client = ion_client_lookup(buffer->dev, current->group_leader); + if (IS_ERR_OR_NULL(client)) { + pr_err("%s: trying to mmap an ion handle in a process with no " + "ion client\n", __func__); + return -EINVAL; + } + + if ((size > buffer->size) || (size + (vma->vm_pgoff << PAGE_SHIFT) > + buffer->size)) { + pr_err("%s: trying to map larger area than handle has available" + "\n", __func__); + ret = -EINVAL; + goto err; + } + + /* find the handle and take a reference to it */ + handle = ion_import(client, buffer); + if (IS_ERR_OR_NULL(handle)) { + ret = -EINVAL; + goto err; + } + + if (!handle->buffer->heap->ops->map_user) { + pr_err("%s: this heap does not define a method for mapping " + "to userspace\n", __func__); + ret = -EINVAL; + goto err1; + } + + mutex_lock(&buffer->lock); + if (buffer->kmap_cnt || buffer->dmap_cnt || buffer->umap_cnt) { + if (buffer->flags != flags) { + pr_err("%s: buffer was already mapped with flags %lx," + " cannot map with flags %lx\n", __func__, + buffer->flags, flags); + ret = -EEXIST; + mutex_unlock(&buffer->lock); + goto err1; + } + + } else { + buffer->flags = flags; + } + /* now map it to userspace */ + ret = buffer->heap->ops->map_user(buffer->heap, buffer, vma, + flags); + buffer->umap_cnt++; + mutex_unlock(&buffer->lock); + if (ret) { + pr_err("%s: failure mapping buffer to userspace\n", + __func__); + goto err2; + } + + vma->vm_ops = &ion_vm_ops; + /* move the handle into the vm_private_data so we can access it from + vma_open/close */ + vma->vm_private_data = handle; + pr_debug("%s: %d client_cnt %d handle_cnt %d alloc_cnt %d\n", + __func__, __LINE__, + atomic_read(&client->ref.refcount), + atomic_read(&handle->ref.refcount), + atomic_read(&buffer->ref.refcount)); + return 0; + +err2: + buffer->umap_cnt--; + /* drop the reference to the handle */ +err1: + ion_handle_put(handle); +err: + /* drop the reference to the client */ + ion_client_put(client); + return ret; +} + +static const struct file_operations ion_share_fops = { + .owner = THIS_MODULE, + .release = ion_share_release, + .mmap = ion_share_mmap, +}; + +static int ion_ioctl_share(struct file *parent, struct ion_client *client, + struct ion_handle *handle) +{ + int fd = get_unused_fd(); + struct file *file; + + if (fd < 0) + return -ENFILE; + + file = anon_inode_getfile("ion_share_fd", &ion_share_fops, + handle->buffer, O_RDWR); + if (IS_ERR_OR_NULL(file)) + goto err; + + if (parent->f_flags & O_DSYNC) + file->f_flags |= O_DSYNC; + + ion_buffer_get(handle->buffer); + fd_install(fd, file); + + return fd; + +err: + put_unused_fd(fd); + return -ENFILE; +} + +static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct ion_client *client = filp->private_data; + + switch (cmd) { + case ION_IOC_ALLOC: + { + struct ion_allocation_data data; + + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) + return -EFAULT; + data.handle = ion_alloc(client, data.len, data.align, + data.flags); + + if (IS_ERR_OR_NULL(data.handle)) + return PTR_ERR(data.handle); + + if (copy_to_user((void __user *)arg, &data, sizeof(data))) + return -EFAULT; + break; + } + case ION_IOC_FREE: + { + struct ion_handle_data data; + bool valid; + + 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); + mutex_unlock(&client->lock); + if (!valid) + return -EINVAL; + ion_free(client, data.handle); + break; + } + case ION_IOC_MAP: + case ION_IOC_SHARE: + { + struct ion_fd_data data; + + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) + return -EFAULT; + mutex_lock(&client->lock); + if (!ion_handle_validate(client, data.handle)) { + pr_err("%s: invalid handle passed to share ioctl.\n", + __func__); + mutex_unlock(&client->lock); + return -EINVAL; + } + data.fd = ion_ioctl_share(filp, client, data.handle); + mutex_unlock(&client->lock); + if (copy_to_user((void __user *)arg, &data, sizeof(data))) + return -EFAULT; + break; + } + case ION_IOC_IMPORT: + { + struct ion_fd_data data; + if (copy_from_user(&data, (void __user *)arg, + sizeof(struct ion_fd_data))) + return -EFAULT; + + data.handle = ion_import_fd(client, data.fd); + if (IS_ERR(data.handle)) + data.handle = NULL; + if (copy_to_user((void __user *)arg, &data, + sizeof(struct ion_fd_data))) + return -EFAULT; + break; + } + case ION_IOC_CUSTOM: + { + struct ion_device *dev = client->dev; + struct ion_custom_data data; + + if (!dev->custom_ioctl) + return -ENOTTY; + if (copy_from_user(&data, (void __user *)arg, + sizeof(struct ion_custom_data))) + return -EFAULT; + return dev->custom_ioctl(client, data.cmd, data.arg); + } + case ION_IOC_CLEAN_CACHES: + case ION_IOC_INV_CACHES: + case ION_IOC_CLEAN_INV_CACHES: + { + struct ion_flush_data data; + unsigned long start, end; + + if (copy_from_user(&data, (void __user *)arg, + sizeof(struct ion_flush_data))) + return -EFAULT; + + start = (unsigned long) data.vaddr; + end = (unsigned long) data.vaddr + data.length; + + if (check_vaddr_bounds(start, end)) { + pr_err("%s: virtual address %p is out of bounds\n", + __func__, data.vaddr); + return -EINVAL; + } + + return ion_do_cache_op(client, data.handle, data.vaddr, + data.offset, data.length, cmd); + + } + case ION_IOC_GET_FLAGS: + { + struct ion_flag_data data; + int ret; + if (copy_from_user(&data, (void __user *)arg, + sizeof(struct ion_flag_data))) + return -EFAULT; + + ret = ion_handle_get_flags(client, data.handle, &data.flags); + if (ret < 0) + return ret; + if (copy_to_user((void __user *)arg, &data, + sizeof(struct ion_flag_data))) + return -EFAULT; + break; + } + default: + return -ENOTTY; + } + return 0; +} + +static int ion_release(struct inode *inode, struct file *file) +{ + struct ion_client *client = file->private_data; + + pr_debug("%s: %d\n", __func__, __LINE__); + ion_client_put(client); + return 0; +} + +static int ion_open(struct inode *inode, struct file *file) +{ + struct miscdevice *miscdev = file->private_data; + struct ion_device *dev = container_of(miscdev, struct ion_device, dev); + struct ion_client *client; + + pr_debug("%s: %d\n", __func__, __LINE__); + client = ion_client_create(dev, -1, "user"); + if (IS_ERR_OR_NULL(client)) + return PTR_ERR(client); + file->private_data = client; + + return 0; +} + +static const struct file_operations ion_fops = { + .owner = THIS_MODULE, + .open = ion_open, + .release = ion_release, + .unlocked_ioctl = ion_ioctl, +}; + +static size_t ion_debug_heap_total(struct ion_client *client, + enum ion_heap_ids id) +{ + size_t size = 0; + struct rb_node *n; + + mutex_lock(&client->lock); + 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->heap->id == id) + size += handle->buffer->size; + } + mutex_unlock(&client->lock); + return size; +} + +static int ion_debug_heap_show(struct seq_file *s, void *unused) +{ + struct ion_heap *heap = s->private; + struct ion_device *dev = heap->dev; + struct rb_node *n; + + seq_printf(s, "%16.s %16.s %16.s\n", "client", "pid", "size"); + for (n = rb_first(&dev->user_clients); n; n = rb_next(n)) { + struct ion_client *client = rb_entry(n, struct ion_client, + node); + char task_comm[TASK_COMM_LEN]; + size_t size = ion_debug_heap_total(client, heap->id); + if (!size) + continue; + + get_task_comm(task_comm, client->task); + seq_printf(s, "%16.s %16u %16x\n", task_comm, client->pid, + size); + } + + for (n = rb_first(&dev->kernel_clients); n; n = rb_next(n)) { + struct ion_client *client = rb_entry(n, struct ion_client, + node); + size_t size = ion_debug_heap_total(client, heap->id); + if (!size) + continue; + seq_printf(s, "%16.s %16u %16x\n", client->name, client->pid, + size); + } + if (heap->ops->get_allocated) { + seq_printf(s, "total bytes currently allocated: %lx\n", + heap->ops->get_allocated(heap)); + } + if (heap->ops->get_total) { + seq_printf(s, "total heap size: %lx\n", + heap->ops->get_total(heap)); + } + return 0; +} + +static int ion_debug_heap_open(struct inode *inode, struct file *file) +{ + return single_open(file, ion_debug_heap_show, inode->i_private); +} + +static const struct file_operations debug_heap_fops = { + .open = ion_debug_heap_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap) +{ + struct rb_node **p = &dev->heaps.rb_node; + struct rb_node *parent = NULL; + struct ion_heap *entry; + + heap->dev = dev; + mutex_lock(&dev->lock); + while (*p) { + parent = *p; + entry = rb_entry(parent, struct ion_heap, node); + + if (heap->id < entry->id) { + p = &(*p)->rb_left; + } else if (heap->id > entry->id ) { + p = &(*p)->rb_right; + } else { + pr_err("%s: can not insert multiple heaps with " + "id %d\n", __func__, heap->id); + goto end; + } + } + + rb_link_node(&heap->node, parent, p); + rb_insert_color(&heap->node, &dev->heaps); + debugfs_create_file(heap->name, 0664, dev->debug_root, heap, + &debug_heap_fops); +end: + mutex_unlock(&dev->lock); +} + +static int ion_debug_leak_show(struct seq_file *s, void *unused) +{ + struct ion_device *dev = s->private; + struct rb_node *n; + struct rb_node *n2; + + /* mark all buffers as 1 */ + seq_printf(s, "%16.s %16.s %16.s %16.s\n", "buffer", "heap", "size", + "ref cnt"); + mutex_lock(&dev->lock); + for (n = rb_first(&dev->buffers); n; n = rb_next(n)) { + struct ion_buffer *buf = rb_entry(n, struct ion_buffer, + node); + + buf->marked = 1; + } + + /* now see which buffers we can access */ + for (n = rb_first(&dev->kernel_clients); n; n = rb_next(n)) { + struct ion_client *client = rb_entry(n, struct ion_client, + node); + + mutex_lock(&client->lock); + for (n2 = rb_first(&client->handles); n2; n2 = rb_next(n2)) { + struct ion_handle *handle = rb_entry(n2, + struct ion_handle, node); + + handle->buffer->marked = 0; + + } + mutex_unlock(&client->lock); + + } + + for (n = rb_first(&dev->user_clients); n; n = rb_next(n)) { + struct ion_client *client = rb_entry(n, struct ion_client, + node); + + mutex_lock(&client->lock); + for (n2 = rb_first(&client->handles); n2; n2 = rb_next(n2)) { + struct ion_handle *handle = rb_entry(n2, + struct ion_handle, node); + + handle->buffer->marked = 0; + + } + mutex_unlock(&client->lock); + + } + /* And anyone still marked as a 1 means a leaked handle somewhere */ + for (n = rb_first(&dev->buffers); n; n = rb_next(n)) { + struct ion_buffer *buf = rb_entry(n, struct ion_buffer, + node); + + if (buf->marked == 1) + seq_printf(s, "%16.x %16.s %16.x %16.d\n", + (int)buf, buf->heap->name, buf->size, + atomic_read(&buf->ref.refcount)); + } + mutex_unlock(&dev->lock); + return 0; +} + +static int ion_debug_leak_open(struct inode *inode, struct file *file) +{ + return single_open(file, ion_debug_leak_show, inode->i_private); +} + +static const struct file_operations debug_leak_fops = { + .open = ion_debug_leak_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + + + +struct ion_device *ion_device_create(long (*custom_ioctl) + (struct ion_client *client, + unsigned int cmd, + unsigned long arg)) +{ + struct ion_device *idev; + int ret; + + idev = kzalloc(sizeof(struct ion_device), GFP_KERNEL); + if (!idev) + return ERR_PTR(-ENOMEM); + + idev->dev.minor = MISC_DYNAMIC_MINOR; + idev->dev.name = "ion"; + idev->dev.fops = &ion_fops; + idev->dev.parent = NULL; + ret = misc_register(&idev->dev); + if (ret) { + pr_err("ion: failed to register misc device.\n"); + return ERR_PTR(ret); + } + + idev->debug_root = debugfs_create_dir("ion", NULL); + if (IS_ERR_OR_NULL(idev->debug_root)) + pr_err("ion: failed to create debug files.\n"); + + idev->custom_ioctl = custom_ioctl; + idev->buffers = RB_ROOT; + mutex_init(&idev->lock); + idev->heaps = RB_ROOT; + idev->user_clients = RB_ROOT; + idev->kernel_clients = RB_ROOT; + debugfs_create_file("check_leaked_fds", 0664, idev->debug_root, idev, + &debug_leak_fops); + return idev; +} + +void ion_device_destroy(struct ion_device *dev) +{ + misc_deregister(&dev->dev); + /* XXX need to free the heaps and clients ? */ + kfree(dev); +} diff --git a/drivers/gpu/ion/ion_carveout_heap.c b/drivers/gpu/ion/ion_carveout_heap.c new file mode 100644 index 0000000000000..71dea89674e78 --- /dev/null +++ b/drivers/gpu/ion/ion_carveout_heap.c @@ -0,0 +1,253 @@ +/* + * drivers/gpu/ion/ion_carveout_heap.c + * + * Copyright (C) 2011 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 +#include +#include +#include +#include +#include "ion_priv.h" + +#include + +struct ion_carveout_heap { + struct ion_heap heap; + struct gen_pool *pool; + ion_phys_addr_t base; + unsigned long allocated_bytes; + unsigned long total_size; +}; + +ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap, + unsigned long size, + unsigned long align) +{ + struct ion_carveout_heap *carveout_heap = + container_of(heap, struct ion_carveout_heap, heap); + unsigned long offset = gen_pool_alloc_aligned(carveout_heap->pool, + size, ilog2(align)); + + if (!offset) { + if ((carveout_heap->total_size - + carveout_heap->allocated_bytes) > size) + pr_debug("%s: heap %s has enough memory (%lx) but" + " the allocation of size %lx still failed." + " Memory is probably fragmented.", + __func__, heap->name, + carveout_heap->total_size - + carveout_heap->allocated_bytes, size); + return ION_CARVEOUT_ALLOCATE_FAIL; + } + + carveout_heap->allocated_bytes += size; + return offset; +} + +void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr, + unsigned long size) +{ + struct ion_carveout_heap *carveout_heap = + container_of(heap, struct ion_carveout_heap, heap); + + if (addr == ION_CARVEOUT_ALLOCATE_FAIL) + return; + gen_pool_free(carveout_heap->pool, addr, size); + carveout_heap->allocated_bytes -= size; +} + +static int ion_carveout_heap_phys(struct ion_heap *heap, + struct ion_buffer *buffer, + ion_phys_addr_t *addr, size_t *len) +{ + *addr = buffer->priv_phys; + *len = buffer->size; + return 0; +} + +static int ion_carveout_heap_allocate(struct ion_heap *heap, + struct ion_buffer *buffer, + unsigned long size, unsigned long align, + unsigned long flags) +{ + buffer->priv_phys = ion_carveout_allocate(heap, size, align); + return buffer->priv_phys == ION_CARVEOUT_ALLOCATE_FAIL ? -ENOMEM : 0; +} + +static void ion_carveout_heap_free(struct ion_buffer *buffer) +{ + struct ion_heap *heap = buffer->heap; + + ion_carveout_free(heap, buffer->priv_phys, buffer->size); + buffer->priv_phys = ION_CARVEOUT_ALLOCATE_FAIL; +} + +struct scatterlist *ion_carveout_heap_map_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + struct scatterlist *sglist; + struct page *page = phys_to_page(buffer->priv_phys); + + if (page == NULL) + return NULL; + + sglist = vmalloc(sizeof(struct scatterlist)); + if (!sglist) + return ERR_PTR(-ENOMEM); + + sg_init_table(sglist, 1); + sg_set_page(sglist, page, buffer->size, 0); + + return sglist; +} + +void ion_carveout_heap_unmap_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + if (buffer->sglist) + vfree(buffer->sglist); +} + +void *ion_carveout_heap_map_kernel(struct ion_heap *heap, + struct ion_buffer *buffer, + unsigned long flags) +{ + if (ION_IS_CACHED(flags)) + return ioremap_cached(buffer->priv_phys, buffer->size); + else + return ioremap(buffer->priv_phys, buffer->size); +} + +void ion_carveout_heap_unmap_kernel(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + __arch_iounmap(buffer->vaddr); + buffer->vaddr = NULL; + return; +} + +int ion_carveout_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, + struct vm_area_struct *vma, unsigned long flags) +{ + if (ION_IS_CACHED(flags)) + return remap_pfn_range(vma, vma->vm_start, + __phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff, + buffer->size, + vma->vm_page_prot); + else + return remap_pfn_range(vma, vma->vm_start, + __phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff, + buffer->size, + pgprot_noncached(vma->vm_page_prot)); +} + +int ion_carveout_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer, + void *vaddr, unsigned int offset, unsigned int length, + unsigned int cmd) +{ + unsigned long vstart, pstart; + + pstart = buffer->priv_phys + offset; + vstart = (unsigned long)vaddr; + + switch (cmd) { + case ION_IOC_CLEAN_CACHES: + clean_caches(vstart, length, pstart); + break; + case ION_IOC_INV_CACHES: + invalidate_caches(vstart, length, pstart); + break; + case ION_IOC_CLEAN_INV_CACHES: + clean_and_invalidate_caches(vstart, length, pstart); + break; + default: + return -EINVAL; + } + + return 0; +} + +static unsigned long ion_carveout_get_allocated(struct ion_heap *heap) +{ + struct ion_carveout_heap *carveout_heap = + container_of(heap, struct ion_carveout_heap, heap); + + return carveout_heap->allocated_bytes; +} + +static unsigned long ion_carveout_get_total(struct ion_heap *heap) +{ + struct ion_carveout_heap *carveout_heap = + container_of(heap, struct ion_carveout_heap, heap); + + return carveout_heap->total_size; +} + +static struct ion_heap_ops carveout_heap_ops = { + .allocate = ion_carveout_heap_allocate, + .free = ion_carveout_heap_free, + .phys = ion_carveout_heap_phys, + .map_user = ion_carveout_heap_map_user, + .map_kernel = ion_carveout_heap_map_kernel, + .unmap_kernel = ion_carveout_heap_unmap_kernel, + .map_dma = ion_carveout_heap_map_dma, + .unmap_dma = ion_carveout_heap_unmap_dma, + .cache_op = ion_carveout_cache_ops, + .get_allocated = ion_carveout_get_allocated, + .get_total = ion_carveout_get_total, +}; + +struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data) +{ + struct ion_carveout_heap *carveout_heap; + int ret; + + carveout_heap = kzalloc(sizeof(struct ion_carveout_heap), GFP_KERNEL); + if (!carveout_heap) + return ERR_PTR(-ENOMEM); + + carveout_heap->pool = gen_pool_create(12, -1); + if (!carveout_heap->pool) { + kfree(carveout_heap); + return ERR_PTR(-ENOMEM); + } + carveout_heap->base = heap_data->base; + ret = gen_pool_add(carveout_heap->pool, carveout_heap->base, + heap_data->size, -1); + if (ret < 0) { + kfree(carveout_heap); + return ERR_PTR(-EINVAL); + } + carveout_heap->heap.ops = &carveout_heap_ops; + carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT; + carveout_heap->allocated_bytes = 0; + carveout_heap->total_size = heap_data->size; + + return &carveout_heap->heap; +} + +void ion_carveout_heap_destroy(struct ion_heap *heap) +{ + struct ion_carveout_heap *carveout_heap = + container_of(heap, struct ion_carveout_heap, heap); + + gen_pool_destroy(carveout_heap->pool); + kfree(carveout_heap); + carveout_heap = NULL; +} diff --git a/drivers/gpu/ion/ion_heap.c b/drivers/gpu/ion/ion_heap.c new file mode 100644 index 0000000000000..8ce3c1907badc --- /dev/null +++ b/drivers/gpu/ion/ion_heap.c @@ -0,0 +1,72 @@ +/* + * drivers/gpu/ion/ion_heap.c + * + * Copyright (C) 2011 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 "ion_priv.h" + +struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data) +{ + struct ion_heap *heap = NULL; + + switch (heap_data->type) { + case ION_HEAP_TYPE_SYSTEM_CONTIG: + heap = ion_system_contig_heap_create(heap_data); + break; + case ION_HEAP_TYPE_SYSTEM: + heap = ion_system_heap_create(heap_data); + break; + case ION_HEAP_TYPE_CARVEOUT: + heap = ion_carveout_heap_create(heap_data); + break; + default: + pr_err("%s: Invalid heap type %d\n", __func__, + heap_data->type); + return ERR_PTR(-EINVAL); + } + + if (IS_ERR_OR_NULL(heap)) { + pr_err("%s: error creating heap %s type %d base %lu size %u\n", + __func__, heap_data->name, heap_data->type, + heap_data->base, heap_data->size); + return ERR_PTR(-EINVAL); + } + + heap->name = heap_data->name; + heap->id = heap_data->id; + return heap; +} + +void ion_heap_destroy(struct ion_heap *heap) +{ + if (!heap) + return; + + switch (heap->type) { + case ION_HEAP_TYPE_SYSTEM_CONTIG: + ion_system_contig_heap_destroy(heap); + break; + case ION_HEAP_TYPE_SYSTEM: + ion_system_heap_destroy(heap); + break; + case ION_HEAP_TYPE_CARVEOUT: + ion_carveout_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 new file mode 100644 index 0000000000000..888b59924ff76 --- /dev/null +++ b/drivers/gpu/ion/ion_priv.h @@ -0,0 +1,192 @@ +/* + * drivers/gpu/ion/ion_priv.h + * + * Copyright (C) 2011 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 _ION_PRIV_H +#define _ION_PRIV_H + +#include +#include +#include +#include +#include + +struct ion_mapping; + +struct ion_dma_mapping { + struct kref ref; + struct scatterlist *sglist; +}; + +struct ion_kernel_mapping { + struct kref ref; + void *vaddr; +}; + +struct ion_buffer *ion_handle_buffer(struct ion_handle *handle); + +/** + * struct ion_buffer - metadata for a particular buffer + * @ref: refernce count + * @node: node in the ion_device buffers tree + * @dev: back pointer to the ion_device + * @heap: back pointer to the heap the buffer came from + * @flags: buffer specific flags + * @size: size of the buffer + * @priv_virt: private data to the buffer representable as + * a void * + * @priv_phys: private data to the buffer representable as + * an ion_phys_addr_t (and someday a phys_addr_t) + * @lock: protects the buffers cnt fields + * @kmap_cnt: number of times the buffer is mapped to the kernel + * @vaddr: the kenrel mapping if kmap_cnt is not zero + * @dmap_cnt: number of times the buffer is mapped for dma + * @sglist: the scatterlist for the buffer is dmap_cnt is not zero +*/ +struct ion_buffer { + struct kref ref; + struct rb_node node; + struct ion_device *dev; + struct ion_heap *heap; + unsigned long flags; + size_t size; + union { + void *priv_virt; + ion_phys_addr_t priv_phys; + }; + struct mutex lock; + int kmap_cnt; + void *vaddr; + int dmap_cnt; + struct scatterlist *sglist; + int umap_cnt; + int marked; +}; + +/** + * struct ion_heap_ops - ops to operate on a given heap + * @allocate: allocate memory + * @free: free memory + * @phys get physical address of a buffer (only define on + * physically contiguous heaps) + * @map_dma map the memory for dma to a scatterlist + * @unmap_dma unmap the memory for dma + * @map_kernel map memory to the kernel + * @unmap_kernel unmap memory to the kernel + * @map_user map memory to userspace + */ +struct ion_heap_ops { + int (*allocate) (struct ion_heap *heap, + struct ion_buffer *buffer, unsigned long len, + unsigned long align, unsigned long flags); + void (*free) (struct ion_buffer *buffer); + int (*phys) (struct ion_heap *heap, struct ion_buffer *buffer, + ion_phys_addr_t *addr, size_t *len); + struct scatterlist *(*map_dma) (struct ion_heap *heap, + struct ion_buffer *buffer); + void (*unmap_dma) (struct ion_heap *heap, struct ion_buffer *buffer); + void * (*map_kernel) (struct ion_heap *heap, struct ion_buffer *buffer, + unsigned long flags); + void (*unmap_kernel) (struct ion_heap *heap, struct ion_buffer *buffer); + int (*map_user) (struct ion_heap *mapper, struct ion_buffer *buffer, + struct vm_area_struct *vma, unsigned long flags); + int (*cache_op)(struct ion_heap *heap, struct ion_buffer *buffer, + void *vaddr, unsigned int offset, + unsigned int length, unsigned int cmd); + unsigned long (*get_allocated)(struct ion_heap *heap); + unsigned long (*get_total)(struct ion_heap *heap); +}; + +/** + * struct ion_heap - represents a heap in the system + * @node: rb node to put the heap on the device's tree of heaps + * @dev: back pointer to the ion_device + * @type: type of heap + * @ops: ops struct as above + * @id: id of heap, also indicates priority of this heap when + * allocating. These are specified by platform data and + * MUST be unique + * @name: used for debugging + * + * Represents a pool of memory from which buffers can be made. In some + * systems the only heap is regular system memory allocated via vmalloc. + * On others, some blocks might require large physically contiguous buffers + * that are allocated from a specially reserved heap. + */ +struct ion_heap { + struct rb_node node; + struct ion_device *dev; + enum ion_heap_type type; + struct ion_heap_ops *ops; + int id; + const char *name; +}; + +/** + * ion_device_create - allocates and returns an ion device + * @custom_ioctl: arch specific ioctl function if applicable + * + * returns a valid device or -PTR_ERR + */ +struct ion_device *ion_device_create(long (*custom_ioctl) + (struct ion_client *client, + unsigned int cmd, + unsigned long arg)); + +/** + * ion_device_destroy - free and device and it's resource + * @dev: the device + */ +void ion_device_destroy(struct ion_device *dev); + +/** + * ion_device_add_heap - adds a heap to the ion device + * @dev: the device + * @heap: the heap to add + */ +void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap); + +/** + * functions for creating and destroying the built in ion heaps. + * architectures can add their own custom architecture specific + * heaps as appropriate. + */ + +struct ion_heap *ion_heap_create(struct ion_platform_heap *); +void ion_heap_destroy(struct ion_heap *); + +struct ion_heap *ion_system_heap_create(struct ion_platform_heap *); +void ion_system_heap_destroy(struct ion_heap *); + +struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *); +void ion_system_contig_heap_destroy(struct ion_heap *); + +struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *); +void ion_carveout_heap_destroy(struct ion_heap *); +/** + * kernel api to allocate/free from carveout -- used when carveout is + * used to back an architecture specific custom heap + */ +ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap, unsigned long size, + unsigned long align); +void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr, + unsigned long size); +/** + * The carveout heap returns physical addresses, since 0 may be a valid + * physical address, this is used to indicate allocation failed + */ +#define ION_CARVEOUT_ALLOCATE_FAIL -1 + +#endif /* _ION_PRIV_H */ diff --git a/drivers/gpu/ion/ion_system_heap.c b/drivers/gpu/ion/ion_system_heap.c new file mode 100644 index 0000000000000..b26d48cca7883 --- /dev/null +++ b/drivers/gpu/ion/ion_system_heap.c @@ -0,0 +1,317 @@ +/* + * drivers/gpu/ion/ion_system_heap.c + * + * Copyright (C) 2011 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 +#include +#include "ion_priv.h" +#include + +static atomic_t system_heap_allocated; +static atomic_t system_contig_heap_allocated; + +static int ion_system_heap_allocate(struct ion_heap *heap, + struct ion_buffer *buffer, + unsigned long size, unsigned long align, + unsigned long flags) +{ + buffer->priv_virt = vmalloc_user(size); + if (!buffer->priv_virt) + return -ENOMEM; + + atomic_add(size, &system_heap_allocated); + return 0; +} + +void ion_system_heap_free(struct ion_buffer *buffer) +{ + vfree(buffer->priv_virt); + atomic_sub(buffer->size, &system_heap_allocated); +} + +struct scatterlist *ion_system_heap_map_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + struct scatterlist *sglist; + struct page *page; + int i; + int npages = PAGE_ALIGN(buffer->size) / PAGE_SIZE; + void *vaddr = buffer->priv_virt; + + sglist = vmalloc(npages * sizeof(struct scatterlist)); + if (!sglist) + return ERR_PTR(-ENOMEM); + memset(sglist, 0, npages * sizeof(struct scatterlist)); + sg_init_table(sglist, npages); + for (i = 0; i < npages; i++) { + page = vmalloc_to_page(vaddr); + if (!page) + goto end; + sg_set_page(&sglist[i], page, PAGE_SIZE, 0); + vaddr += PAGE_SIZE; + } + /* XXX do cache maintenance for dma? */ + return sglist; +end: + vfree(sglist); + return NULL; +} + +void ion_system_heap_unmap_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + /* XXX undo cache maintenance for dma? */ + if (buffer->sglist) + vfree(buffer->sglist); +} + +void *ion_system_heap_map_kernel(struct ion_heap *heap, + struct ion_buffer *buffer, + unsigned long flags) +{ + if (ION_IS_CACHED(flags)) + return buffer->priv_virt; + else { + pr_err("%s: cannot map system heap uncached\n", __func__); + return ERR_PTR(-EINVAL); + } +} + +void ion_system_heap_unmap_kernel(struct ion_heap *heap, + struct ion_buffer *buffer) +{ +} + +int ion_system_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, + struct vm_area_struct *vma, unsigned long flags) +{ + if (ION_IS_CACHED(flags)) + return remap_vmalloc_range(vma, buffer->priv_virt, + vma->vm_pgoff); + else { + pr_err("%s: cannot map system heap uncached\n", __func__); + return -EINVAL; + } +} + +int ion_system_heap_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer, + void *vaddr, unsigned int offset, unsigned int length, + unsigned int cmd) +{ + unsigned long vstart, pstart; + void *vtemp; + unsigned long ln = 0; + void (*op)(unsigned long, unsigned long, unsigned long); + + switch (cmd) { + case ION_IOC_CLEAN_CACHES: + op = clean_caches; + break; + case ION_IOC_INV_CACHES: + op = invalidate_caches; + break; + case ION_IOC_CLEAN_INV_CACHES: + op = clean_and_invalidate_caches; + break; + default: + return -EINVAL; + } + + for (vtemp = buffer->priv_virt + offset, + vstart = (unsigned long) vaddr; + ln < length; + vtemp += PAGE_SIZE, ln += PAGE_SIZE, + vstart += PAGE_SIZE) { + pstart = page_to_phys(vmalloc_to_page(vtemp)); + /* + * If vmalloc -> page -> phys is returning NULL, something + * has really gone wrong... + */ + if (!pstart) { + WARN(1, "Could not translate %p to physical address\n", + vtemp); + return -EINVAL; + } + + op(vstart, PAGE_SIZE, pstart); + } + + return 0; +} + +static unsigned long ion_system_heap_get_allocated(struct ion_heap *heap) +{ + return atomic_read(&system_heap_allocated); +} + +static struct ion_heap_ops vmalloc_ops = { + .allocate = ion_system_heap_allocate, + .free = ion_system_heap_free, + .map_dma = ion_system_heap_map_dma, + .unmap_dma = ion_system_heap_unmap_dma, + .map_kernel = ion_system_heap_map_kernel, + .unmap_kernel = ion_system_heap_unmap_kernel, + .map_user = ion_system_heap_map_user, + .cache_op = ion_system_heap_cache_ops, + .get_allocated = ion_system_heap_get_allocated, +}; + +struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused) +{ + struct ion_heap *heap; + + heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL); + if (!heap) + return ERR_PTR(-ENOMEM); + heap->ops = &vmalloc_ops; + heap->type = ION_HEAP_TYPE_SYSTEM; + return heap; +} + +void ion_system_heap_destroy(struct ion_heap *heap) +{ + kfree(heap); +} + +static int ion_system_contig_heap_allocate(struct ion_heap *heap, + struct ion_buffer *buffer, + unsigned long len, + unsigned long align, + unsigned long flags) +{ + buffer->priv_virt = kzalloc(len, GFP_KERNEL); + if (!buffer->priv_virt) + return -ENOMEM; + atomic_add(len, &system_contig_heap_allocated); + return 0; +} + +void ion_system_contig_heap_free(struct ion_buffer *buffer) +{ + kfree(buffer->priv_virt); + atomic_sub(buffer->size, &system_contig_heap_allocated); +} + +static int ion_system_contig_heap_phys(struct ion_heap *heap, + struct ion_buffer *buffer, + ion_phys_addr_t *addr, size_t *len) +{ + *addr = virt_to_phys(buffer->priv_virt); + *len = buffer->size; + return 0; +} + +struct scatterlist *ion_system_contig_heap_map_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + struct scatterlist *sglist; + + sglist = vmalloc(sizeof(struct scatterlist)); + if (!sglist) + return ERR_PTR(-ENOMEM); + sg_init_table(sglist, 1); + sg_set_page(sglist, virt_to_page(buffer->priv_virt), buffer->size, 0); + return sglist; +} + +int ion_system_contig_heap_map_user(struct ion_heap *heap, + struct ion_buffer *buffer, + struct vm_area_struct *vma, + unsigned long flags) +{ + unsigned long pfn = __phys_to_pfn(virt_to_phys(buffer->priv_virt)); + + if (ION_IS_CACHED(flags)) + return remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); + else { + pr_err("%s: cannot map system heap uncached\n", __func__); + return -EINVAL; + } +} + +int ion_system_contig_heap_cache_ops(struct ion_heap *heap, + struct ion_buffer *buffer, void *vaddr, + unsigned int offset, unsigned int length, + unsigned int cmd) +{ + unsigned long vstart, pstart; + + pstart = virt_to_phys(buffer->priv_virt) + offset; + if (!pstart) { + WARN(1, "Could not do virt to phys translation on %p\n", + buffer->priv_virt); + return -EINVAL; + } + + vstart = (unsigned long) vaddr; + + switch (cmd) { + case ION_IOC_CLEAN_CACHES: + clean_caches(vstart, length, pstart); + break; + case ION_IOC_INV_CACHES: + invalidate_caches(vstart, length, pstart); + break; + case ION_IOC_CLEAN_INV_CACHES: + clean_and_invalidate_caches(vstart, length, pstart); + break; + default: + return -EINVAL; + } + + return 0; +} + +static unsigned long ion_system_contig_heap_get_allocated(struct ion_heap *heap) +{ + return atomic_read(&system_contig_heap_allocated); +} + +static struct ion_heap_ops kmalloc_ops = { + .allocate = ion_system_contig_heap_allocate, + .free = ion_system_contig_heap_free, + .phys = ion_system_contig_heap_phys, + .map_dma = ion_system_contig_heap_map_dma, + .unmap_dma = ion_system_heap_unmap_dma, + .map_kernel = ion_system_heap_map_kernel, + .unmap_kernel = ion_system_heap_unmap_kernel, + .map_user = ion_system_contig_heap_map_user, + .cache_op = ion_system_contig_heap_cache_ops, + .get_allocated = ion_system_contig_heap_get_allocated, +}; + +struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *unused) +{ + struct ion_heap *heap; + + heap = kzalloc(sizeof(struct ion_heap), GFP_KERNEL); + if (!heap) + return ERR_PTR(-ENOMEM); + heap->ops = &kmalloc_ops; + heap->type = ION_HEAP_TYPE_SYSTEM_CONTIG; + return heap; +} + +void ion_system_contig_heap_destroy(struct ion_heap *heap) +{ + kfree(heap); +} + diff --git a/drivers/gpu/ion/ion_system_mapper.c b/drivers/gpu/ion/ion_system_mapper.c new file mode 100644 index 0000000000000..692458e07b5e8 --- /dev/null +++ b/drivers/gpu/ion/ion_system_mapper.c @@ -0,0 +1,114 @@ +/* + * drivers/gpu/ion/ion_system_mapper.c + * + * Copyright (C) 2011 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 +#include +#include "ion_priv.h" +/* + * This mapper is valid for any heap that allocates memory that already has + * a kernel mapping, this includes vmalloc'd memory, kmalloc'd memory, + * pages obtained via io_remap, etc. + */ +static void *ion_kernel_mapper_map(struct ion_mapper *mapper, + struct ion_buffer *buffer, + struct ion_mapping **mapping) +{ + if (!((1 << buffer->heap->type) & mapper->heap_mask)) { + pr_err("%s: attempting to map an unsupported heap\n", __func__); + return ERR_PTR(-EINVAL); + } + /* XXX REVISIT ME!!! */ + *((unsigned long *)mapping) = (unsigned long)buffer->priv; + return buffer->priv; +} + +static void ion_kernel_mapper_unmap(struct ion_mapper *mapper, + struct ion_buffer *buffer, + struct ion_mapping *mapping) +{ + if (!((1 << buffer->heap->type) & mapper->heap_mask)) + pr_err("%s: attempting to unmap an unsupported heap\n", + __func__); +} + +static void *ion_kernel_mapper_map_kernel(struct ion_mapper *mapper, + struct ion_buffer *buffer, + struct ion_mapping *mapping) +{ + if (!((1 << buffer->heap->type) & mapper->heap_mask)) { + pr_err("%s: attempting to unmap an unsupported heap\n", + __func__); + return ERR_PTR(-EINVAL); + } + return buffer->priv; +} + +static int ion_kernel_mapper_map_user(struct ion_mapper *mapper, + struct ion_buffer *buffer, + struct vm_area_struct *vma, + struct ion_mapping *mapping) +{ + int ret; + + switch (buffer->heap->type) { + case ION_HEAP_KMALLOC: + { + unsigned long pfn = __phys_to_pfn(virt_to_phys(buffer->priv)); + ret = remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); + break; + } + case ION_HEAP_VMALLOC: + ret = remap_vmalloc_range(vma, buffer->priv, vma->vm_pgoff); + break; + default: + pr_err("%s: attempting to map unsupported heap to userspace\n", + __func__); + return -EINVAL; + } + + return ret; +} + +static struct ion_mapper_ops ops = { + .map = ion_kernel_mapper_map, + .map_kernel = ion_kernel_mapper_map_kernel, + .map_user = ion_kernel_mapper_map_user, + .unmap = ion_kernel_mapper_unmap, +}; + +struct ion_mapper *ion_system_mapper_create(void) +{ + struct ion_mapper *mapper; + mapper = kzalloc(sizeof(struct ion_mapper), GFP_KERNEL); + if (!mapper) + return ERR_PTR(-ENOMEM); + mapper->type = ION_SYSTEM_MAPPER; + mapper->ops = &ops; + mapper->heap_mask = (1 << ION_HEAP_VMALLOC) | (1 << ION_HEAP_KMALLOC); + return mapper; +} + +void ion_system_mapper_destroy(struct ion_mapper *mapper) +{ + kfree(mapper); +} + diff --git a/drivers/gpu/ion/msm/Makefile b/drivers/gpu/ion/msm/Makefile new file mode 100644 index 0000000000000..bedd8d22779e1 --- /dev/null +++ b/drivers/gpu/ion/msm/Makefile @@ -0,0 +1 @@ +obj-y += msm_ion.o diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c new file mode 100644 index 0000000000000..54dd056986316 --- /dev/null +++ b/drivers/gpu/ion/msm/msm_ion.c @@ -0,0 +1,134 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include "../ion_priv.h" + +static struct ion_device *idev; +static int num_heaps; +static struct ion_heap **heaps; + +struct ion_client *msm_ion_client_create(unsigned int heap_mask, + const char *name) +{ + return ion_client_create(idev, heap_mask, name); +} +EXPORT_SYMBOL(msm_ion_client_create); + +static unsigned long msm_ion_get_base(unsigned long size, int memory_type) +{ + switch (memory_type) { + case ION_EBI_TYPE: + return allocate_contiguous_ebi_nomap(size, PAGE_SIZE); + break; + case ION_SMI_TYPE: + return allocate_contiguous_memory_nomap(size, MEMTYPE_SMI, + PAGE_SIZE); + break; + default: + return 0; + } +} + +static int msm_ion_probe(struct platform_device *pdev) +{ + struct ion_platform_data *pdata = pdev->dev.platform_data; + int err; + int i; + + num_heaps = pdata->nr; + + heaps = kcalloc(pdata->nr, sizeof(struct ion_heap *), GFP_KERNEL); + + if (!heaps) { + err = -ENOMEM; + goto out; + } + + idev = ion_device_create(NULL); + if (IS_ERR_OR_NULL(idev)) { + err = PTR_ERR(idev); + goto freeheaps; + } + + /* create the heaps as specified in the board file */ + for (i = 0; i < num_heaps; i++) { + struct ion_platform_heap *heap_data = &pdata->heaps[i]; + + if (heap_data->type == ION_HEAP_TYPE_CARVEOUT) { + heap_data->base = msm_ion_get_base(heap_data->size, + heap_data->memory_type); + if (!heap_data->base) { + pr_err("%s: could not get memory for heap %s" + " (id %x)\n", __func__, heap_data->name, + heap_data->id); + continue; + } + } + + heaps[i] = ion_heap_create(heap_data); + if (IS_ERR_OR_NULL(heaps[i])) { + pr_err("%s: could not create ion heap %s" + " (id %x)\n", __func__, heap_data->name, + heap_data->id); + heaps[i] = 0; + continue; + } + ion_device_add_heap(idev, heaps[i]); + } + platform_set_drvdata(pdev, idev); + return 0; + +freeheaps: + kfree(heaps); +out: + return err; +} + +static int msm_ion_remove(struct platform_device *pdev) +{ + struct ion_device *idev = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < num_heaps; i++) + ion_heap_destroy(heaps[i]); + + ion_device_destroy(idev); + kfree(heaps); + return 0; +} + +static struct platform_driver msm_ion_driver = { + .probe = msm_ion_probe, + .remove = msm_ion_remove, + .driver = { .name = "ion-msm" } +}; + +static int __init msm_ion_init(void) +{ + return platform_driver_register(&msm_ion_driver); +} + +static void __exit msm_ion_exit(void) +{ + platform_driver_unregister(&msm_ion_driver); +} + +subsys_initcall(msm_ion_init); +module_exit(msm_ion_exit); + diff --git a/drivers/gpu/ion/tegra/Makefile b/drivers/gpu/ion/tegra/Makefile new file mode 100644 index 0000000000000..11cd003fb08f0 --- /dev/null +++ b/drivers/gpu/ion/tegra/Makefile @@ -0,0 +1 @@ +obj-y += tegra_ion.o diff --git a/drivers/gpu/ion/tegra/tegra_ion.c b/drivers/gpu/ion/tegra/tegra_ion.c new file mode 100644 index 0000000000000..7af6e168ff4cf --- /dev/null +++ b/drivers/gpu/ion/tegra/tegra_ion.c @@ -0,0 +1,96 @@ +/* + * drivers/gpu/tegra/tegra_ion.c + * + * Copyright (C) 2011 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 "../ion_priv.h" + +struct ion_device *idev; +struct ion_mapper *tegra_user_mapper; +int num_heaps; +struct ion_heap **heaps; + +int tegra_ion_probe(struct platform_device *pdev) +{ + struct ion_platform_data *pdata = pdev->dev.platform_data; + int err; + int i; + + num_heaps = pdata->nr; + + heaps = kzalloc(sizeof(struct ion_heap *) * pdata->nr, GFP_KERNEL); + + idev = ion_device_create(NULL); + if (IS_ERR_OR_NULL(idev)) { + kfree(heaps); + return PTR_ERR(idev); + } + + /* create the heaps as specified in the board file */ + for (i = 0; i < num_heaps; i++) { + struct ion_platform_heap *heap_data = &pdata->heaps[i]; + + heaps[i] = ion_heap_create(heap_data); + if (IS_ERR_OR_NULL(heaps[i])) { + err = PTR_ERR(heaps[i]); + goto err; + } + ion_device_add_heap(idev, heaps[i]); + } + platform_set_drvdata(pdev, idev); + return 0; +err: + for (i = 0; i < num_heaps; i++) { + if (heaps[i]) + ion_heap_destroy(heaps[i]); + } + kfree(heaps); + return err; +} + +int tegra_ion_remove(struct platform_device *pdev) +{ + struct ion_device *idev = platform_get_drvdata(pdev); + int i; + + ion_device_destroy(idev); + for (i = 0; i < num_heaps; i++) + ion_heap_destroy(heaps[i]); + kfree(heaps); + return 0; +} + +static struct platform_driver ion_driver = { + .probe = tegra_ion_probe, + .remove = tegra_ion_remove, + .driver = { .name = "ion-tegra" } +}; + +static int __init ion_init(void) +{ + return platform_driver_register(&ion_driver); +} + +static void __exit ion_exit(void) +{ + platform_driver_unregister(&ion_driver); +} + +module_init(ion_init); +module_exit(ion_exit); + diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 62334c77ca7ef..3f402e4587b00 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -26,6 +26,8 @@ source "drivers/gpu/drm/Kconfig" source "drivers/gpu/stub/Kconfig" +source "drivers/gpu/ion/Kconfig" + source "drivers/gpu/msm/Kconfig" config VGASTATE diff --git a/include/linux/ion.h b/include/linux/ion.h new file mode 100644 index 0000000000000..7de40d4345f99 --- /dev/null +++ b/include/linux/ion.h @@ -0,0 +1,556 @@ +/* + * include/linux/ion.h + * + * Copyright (C) 2011 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_ION_H +#define _LINUX_ION_H + +#include +#include + + +struct ion_handle; +/** + * enum ion_heap_types - list of all possible types of heaps + * @ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc + * @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc + * @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved + * carveout heap, allocations are physically + * contiguous + * @ION_HEAP_END: helper for iterating over heaps + */ +enum ion_heap_type { + ION_HEAP_TYPE_SYSTEM, + ION_HEAP_TYPE_SYSTEM_CONTIG, + ION_HEAP_TYPE_CARVEOUT, + ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always + are at the end of this enum */ + ION_NUM_HEAPS, +}; + +#define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM) +#define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG) +#define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT) + + +/** + * These are the only ids that should be used for Ion heap ids. + * The ids listed are the order in which allocation will be attempted + * if specified. Don't swap the order of heap ids unless you know what + * you are doing! + */ + +enum ion_heap_ids { + ION_HEAP_SYSTEM_ID, + ION_HEAP_SYSTEM_CONTIG_ID, + ION_HEAP_EBI_ID, + ION_HEAP_SMI_ID, + ION_HEAP_ADSP_ID, + ION_HEAP_AUDIO_ID, +}; + +#define ION_KMALLOC_HEAP_NAME "kmalloc" +#define ION_VMALLOC_HEAP_NAME "vmalloc" +#define ION_EBI1_HEAP_NAME "EBI1" +#define ION_ADSP_HEAP_NAME "adsp" +#define ION_SMI_HEAP_NAME "smi" + +#define CACHED 1 +#define UNCACHED 0 + +#define ION_CACHE_SHIFT 0 + +#define ION_SET_CACHE(__cache) ((__cache) << ION_CACHE_SHIFT) + +#define ION_IS_CACHED(__flags) ((__flags) & (1 << ION_CACHE_SHIFT)) + +#ifdef __KERNEL__ +#include +#include +struct ion_device; +struct ion_heap; +struct ion_mapper; +struct ion_client; +struct ion_buffer; + +/* This should be removed some day when phys_addr_t's are fully + plumbed in the kernel, and all instances of ion_phys_addr_t should + be converted to phys_addr_t. For the time being many kernel interfaces + do not accept phys_addr_t's that would have to */ +#define ion_phys_addr_t unsigned long + +/** + * struct ion_platform_heap - defines a heap in the given platform + * @type: type of the heap from ion_heap_type enum + * @id: unique identifier for heap. When allocating (lower numbers + * will be allocated from first) + * @name: used for debug purposes + * @base: base address of heap in physical memory if applicable + * @size: size of the heap in bytes if applicable + * + * Provided by the board file. + */ +struct ion_platform_heap { + enum ion_heap_type type; + unsigned int id; + const char *name; + ion_phys_addr_t base; + size_t size; + enum ion_memory_types memory_type; +}; + +/** + * struct ion_platform_data - array of platform heaps passed from board file + * @nr: number of structures in the array + * @request_region: function to be called when the number of allocations goes + * from 0 -> 1 + * @release_region: function to be called when the number of allocations goes + * from 1 -> 0 + * @setup_region: function to be called upon ion registration + * @heaps: array of platform_heap structions + * + * Provided by the board file in the form of platform data to a platform device. + */ +struct ion_platform_data { + int nr; + void (*request_region)(void *); + void (*release_region)(void *); + void *(*setup_region)(void); + struct ion_platform_heap heaps[]; +}; + +#ifdef CONFIG_ION + +/** + * ion_client_create() - allocate a client and returns it + * @dev: the global ion device + * @heap_mask: mask of heaps this client can allocate from + * @name: used for debugging + */ +struct ion_client *ion_client_create(struct ion_device *dev, + unsigned int heap_mask, const char *name); + +/** + * msm_ion_client_create - allocate a client using the ion_device specified in + * drivers/gpu/ion/msm/msm_ion.c + * + * heap_mask and name are the same as ion_client_create, return values + * are the same as ion_client_create. + */ + +struct ion_client *msm_ion_client_create(unsigned int heap_mask, + const char *name); + +/** + * ion_client_destroy() - free's a client and all it's handles + * @client: the client + * + * Free the provided client and all it's resources including + * any handles it is holding. + */ +void ion_client_destroy(struct ion_client *client); + +/** + * ion_alloc - allocate ion memory + * @client: the client + * @len: size of the allocation + * @align: requested allocation alignment, lots of hardware blocks have + * alignment requirements of some kind + * @flags: mask of heaps to allocate from, if multiple bits are set + * heaps will be tried in order from lowest to highest order bit + * + * Allocate memory in one of the heaps provided in heap mask and return + * an opaque handle to it. + */ +struct ion_handle *ion_alloc(struct ion_client *client, size_t len, + size_t align, unsigned int flags); + +/** + * ion_free - free a handle + * @client: the client + * @handle: the handle to free + * + * Free the provided handle. + */ +void ion_free(struct ion_client *client, struct ion_handle *handle); + +/** + * ion_phys - returns the physical address and len of a handle + * @client: the client + * @handle: the handle + * @addr: a pointer to put the address in + * @len: a pointer to put the length in + * + * This function queries the heap for a particular handle to get the + * handle's physical address. It't output is only correct if + * a heap returns physically contiguous memory -- in other cases + * this api should not be implemented -- ion_map_dma should be used + * instead. Returns -EINVAL if the handle is invalid. This has + * no implications on the reference counting of the handle -- + * the returned value may not be valid if the caller is not + * holding a reference. + */ +int ion_phys(struct ion_client *client, struct ion_handle *handle, + ion_phys_addr_t *addr, size_t *len); + +/** + * ion_map_kernel - create mapping for the given handle + * @client: the client + * @handle: handle to map + * @flags: flags for this mapping + * + * Map the given handle into the kernel and return a kernel address that + * can be used to access this address. If no flags are specified, this + * will return a non-secure uncached mapping. + */ +void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle, + unsigned long flags); + +/** + * ion_unmap_kernel() - destroy a kernel mapping for a handle + * @client: the client + * @handle: handle to unmap + */ +void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle); + +/** + * ion_map_dma - create a dma mapping for a given handle + * @client: the client + * @handle: handle to map + * + * Return an sglist describing the given handle + */ +struct scatterlist *ion_map_dma(struct ion_client *client, + struct ion_handle *handle, + unsigned long flags); + +/** + * ion_unmap_dma() - destroy a dma mapping for a handle + * @client: the client + * @handle: handle to unmap + */ +void ion_unmap_dma(struct ion_client *client, struct ion_handle *handle); + +/** + * ion_share() - given a handle, obtain a buffer to pass to other clients + * @client: the client + * @handle: the handle to share + * + * Given a handle, return a buffer, which exists in a global name + * space, and can be passed to other clients. Should be passed into ion_import + * to obtain a new handle for this buffer. + * + * NOTE: This function does do not an extra reference. The burden is on the + * caller to make sure the buffer doesn't go away while it's being passed to + * another client. That is, ion_free should not be called on this handle until + * the buffer has been imported into the other client. + */ +struct ion_buffer *ion_share(struct ion_client *client, + struct ion_handle *handle); + +/** + * ion_import() - given an buffer in another client, import it + * @client: this blocks client + * @buffer: the buffer to import (as obtained from ion_share) + * + * Given a buffer, add it to the client and return the handle to use to refer + * to it further. This is called to share a handle from one kernel client to + * another. + */ +struct ion_handle *ion_import(struct ion_client *client, + struct ion_buffer *buffer); + +/** + * ion_import_fd() - given an fd obtained via ION_IOC_SHARE ioctl, import it + * @client: this blocks client + * @fd: the fd + * + * A helper function for drivers that will be recieving ion buffers shared + * with them from userspace. These buffers are represented by a file + * descriptor obtained as the return from the ION_IOC_SHARE ioctl. + * This function coverts that fd into the underlying buffer, and returns + * the handle to use to refer to it further. + */ +struct ion_handle *ion_import_fd(struct ion_client *client, int fd); + +/** + * ion_handle_get_flags - get the flags for a given handle + * + * @client - client who allocated the handle + * @handle - handle to get the flags + * @flags - pointer to store the flags + * + * Gets the current flags for a handle. These flags indicate various options + * of the buffer (caching, security, etc.) + */ +int ion_handle_get_flags(struct ion_client *client, struct ion_handle *handle, + unsigned long *flags); + +#else +static inline struct ion_client *ion_client_create(struct ion_device *dev, + unsigned int heap_mask, const char *name) +{ + return ERR_PTR(-ENODEV); +} + +static inline struct ion_client *msm_ion_client_create(unsigned int heap_mask, + const char *name) +{ + return ERR_PTR(-ENODEV); +} + +static inline void ion_client_destroy(struct ion_client *client) { } + +static inline struct ion_handle *ion_alloc(struct ion_client *client, + size_t len, size_t align, unsigned int flags) +{ + return ERR_PTR(-ENODEV); +} + +static inline void ion_free(struct ion_client *client, + struct ion_handle *handle) { } + + +static inline int ion_phys(struct ion_client *client, + struct ion_handle *handle, ion_phys_addr_t *addr, size_t *len) +{ + return -ENODEV; +} + +static inline void *ion_map_kernel(struct ion_client *client, + struct ion_handle *handle, unsigned long flags) +{ + return ERR_PTR(-ENODEV); +} + +static inline void ion_unmap_kernel(struct ion_client *client, + struct ion_handle *handle) { } + +static inline struct scatterlist *ion_map_dma(struct ion_client *client, + struct ion_handle *handle, unsigned long flags) +{ + return ERR_PTR(-ENODEV); +} + +static inline void ion_unmap_dma(struct ion_client *client, + struct ion_handle *handle) { } + +static inline struct ion_buffer *ion_share(struct ion_client *client, + struct ion_handle *handle) +{ + return ERR_PTR(-ENODEV); +} + +static inline struct ion_handle *ion_import(struct ion_client *client, + struct ion_buffer *buffer) +{ + return ERR_PTR(-ENODEV); +} + +static inline struct ion_handle *ion_import_fd(struct ion_client *client, + int fd) +{ + return ERR_PTR(-ENODEV); +} + +static inline int ion_handle_get_flags(struct ion_client *client, + struct ion_handle *handle, unsigned long *flags) +{ + return -ENODEV; +} +#endif /* CONFIG_ION */ +#endif /* __KERNEL__ */ + +/** + * DOC: Ion Userspace API + * + * create a client by opening /dev/ion + * most operations handled via following ioctls + * + */ + +/** + * struct ion_allocation_data - metadata passed from userspace for allocations + * @len: size of the allocation + * @align: required alignment of the allocation + * @flags: flags passed to heap + * @handle: pointer that will be populated with a cookie to use to refer + * to this allocation + * + * Provided by userspace as an argument to the ioctl + */ +struct ion_allocation_data { + size_t len; + size_t align; + unsigned int flags; + struct ion_handle *handle; +}; + +/** + * struct ion_fd_data - metadata passed to/from userspace for a handle/fd pair + * @handle: a handle + * @fd: a file descriptor representing that handle + * + * For ION_IOC_SHARE or ION_IOC_MAP userspace populates the handle field with + * the handle returned from ion alloc, and the kernel returns the file + * descriptor to share or map in the fd field. For ION_IOC_IMPORT, userspace + * provides the file descriptor and the kernel returns the handle. + */ +struct ion_fd_data { + struct ion_handle *handle; + int fd; +}; + +/** + * struct ion_handle_data - a handle passed to/from the kernel + * @handle: a handle + */ +struct ion_handle_data { + struct ion_handle *handle; +}; + +/** + * struct ion_custom_data - metadata passed to/from userspace for a custom ioctl + * @cmd: the custom ioctl function to call + * @arg: additional data to pass to the custom ioctl, typically a user + * pointer to a predefined structure + * + * This works just like the regular cmd and arg fields of an ioctl. + */ +struct ion_custom_data { + unsigned int cmd; + unsigned long arg; +}; + + +/* struct ion_flush_data - data passed to ion for flushing caches + * + * @handle: handle with data to flush + * @vaddr: userspace virtual address mapped with mmap + * @offset: offset into the handle to flush + * @length: length of handle to flush + * + * Performs cache operations on the handle. If p is the start address + * of the handle, p + offset through p + offset + length will have + * the cache operations performed + */ +struct ion_flush_data { + struct ion_handle *handle; + void *vaddr; + unsigned int offset; + unsigned int length; +}; + +/* struct ion_flag_data - information about flags for this buffer + * + * @handle: handle to get flags from + * @flags: flags of this handle + * + * Takes handle as an input and outputs the flags from the handle + * in the flag field. + */ +struct ion_flag_data { + struct ion_handle *handle; + unsigned long flags; +}; + +#define ION_IOC_MAGIC 'I' + +/** + * DOC: ION_IOC_ALLOC - allocate memory + * + * Takes an ion_allocation_data struct and returns it with the handle field + * populated with the opaque handle for the allocation. + */ +#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \ + struct ion_allocation_data) + +/** + * DOC: ION_IOC_FREE - free memory + * + * Takes an ion_handle_data struct and frees the handle. + */ +#define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data) + +/** + * DOC: ION_IOC_MAP - get a file descriptor to mmap + * + * Takes an ion_fd_data struct with the handle field populated with a valid + * opaque handle. Returns the struct with the fd field set to a file + * descriptor open in the current address space. This file descriptor + * can then be used as an argument to mmap. + */ +#define ION_IOC_MAP _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data) + +/** + * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation + * + * Takes an ion_fd_data struct with the handle field populated with a valid + * opaque handle. Returns the struct with the fd field set to a file + * descriptor open in the current address space. This file descriptor + * can then be passed to another process. The corresponding opaque handle can + * be retrieved via ION_IOC_IMPORT. + */ +#define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data) + +/** + * DOC: ION_IOC_IMPORT - imports a shared file descriptor + * + * Takes an ion_fd_data struct with the fd field populated with a valid file + * descriptor obtained from ION_IOC_SHARE and returns the struct with the handle + * filed set to the corresponding opaque handle. + */ +#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, int) + +/** + * DOC: ION_IOC_CUSTOM - call architecture specific ion ioctl + * + * Takes the argument of the architecture specific ioctl to call and + * passes appropriate userdata for that ioctl + */ +#define ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data) + + +/** + * DOC: ION_IOC_CLEAN_CACHES - clean the caches + * + * Clean the caches of the handle specified. + */ +#define ION_IOC_CLEAN_CACHES _IOWR(ION_IOC_MAGIC, 7, \ + struct ion_flush_data) +/** + * DOC: ION_MSM_IOC_INV_CACHES - invalidate the caches + * + * Invalidate the caches of the handle specified. + */ +#define ION_IOC_INV_CACHES _IOWR(ION_IOC_MAGIC, 8, \ + struct ion_flush_data) +/** + * DOC: ION_MSM_IOC_CLEAN_CACHES - clean and invalidate the caches + * + * Clean and invalidate the caches of the handle specified. + */ +#define ION_IOC_CLEAN_INV_CACHES _IOWR(ION_IOC_MAGIC, 9, \ + struct ion_flush_data) + +/** + * DOC: ION_IOC_GET_FLAGS - get the flags of the handle + * + * Gets the flags of the current handle which indicate cachability, + * secure state etc. + */ +#define ION_IOC_GET_FLAGS _IOWR(ION_IOC_MAGIC, 10, \ + struct ion_flag_data) +#endif /* _LINUX_ION_H */ From 149bb33ffc28765d36fcfc32aa66dbe9c56f3bc1 Mon Sep 17 00:00:00 2001 From: Paolo Valente Date: Wed, 22 Dec 2010 14:51:24 +0100 Subject: [PATCH 2240/2556] block: introduce the BFQ-v3r2 I/O sched for 2.6.38 Add the BFQ-v3r2 I/O scheduler to 2.6.38. The general structure is borrowed from CFQ, as much of the code. A (bfq_)queue is associated to each task doing I/O on a device, and each time a scheduling decision has to be taken a queue is selected and it is served until it expires. The main differences are: - Slices are given in the service domain: tasks are assigned budgets, measured in number of sectors. Once got the disk, a task must however consume its assigned budget within a configurable maximum time (by default, the maximum possible value of the budgets is automatically computed to comply with this timeout). This allows the desired latency vs "throughput boosting" tradeoff to be set. - Budgets are scheduled according to a variant of WF2Q+, implemented using an augmented rb-tree to take eligibility into account while preserving an O(log N) overall complexity. - A low-latency tunable is provided; if enabled, both interactive and soft real-time applications are guaranteed very low latency. - Latency guarantees are preserved also in presence of NCQ. - Useful features borrowed from CFQ: cooperating-queues merging (with some additional optimizations with respect to the original CFQ version), static fallback queue for OOM. - BFQ supports full hierarchical scheduling, exporting a cgroups interface. Each node has a full scheduler, so each group can be assigned its own ioprio and an ioprio_class. - If the cgroups interface is used, weights can be explictly assigned, otherwise ioprio values are mapped to weights using the relation weight = IOPRIO_BE_NR - ioprio. - ioprio classes are served in strict priority order, i.e., lower priority queues are not served as long as there are higher priority queues. Among queues in the same class the bandwidth is distributed in proportion to the weights of each queue. A very thin extra bandwidth is however guaranteed to the Idle class, to prevent it from starving. Regarding what has not changed it is worth noting: - the handling of cfq_io_contexts to associate queues to tasks. Much of the code has been reused just renaming it. (There is room for code sharing with CFQ but we wanted to minimize the impact of this patch.) - The handling of async queues. - The handling of idle windows. - The handling of merging. - The heuristics to assert that a task is worth an idle window (with minor modifications to hw_tag/CIC_SEEKY detection). Signed-off-by: Paolo Valente Signed-off-by: Arianna Avanzini --- block/bfq-ioc.c | 8 +- block/bfq-iosched.c | 994 ++++++++++++++++++++++++++++++++++---------- block/bfq-sched.c | 27 ++ block/bfq.h | 166 ++++---- 4 files changed, 912 insertions(+), 283 deletions(-) diff --git a/block/bfq-ioc.c b/block/bfq-ioc.c index 9d06cebde960e..01f8313320eec 100644 --- a/block/bfq-ioc.c +++ b/block/bfq-ioc.c @@ -106,8 +106,14 @@ static void __bfq_exit_single_io_context(struct bfq_data *bfqd, * No write-side locking as no task is using @ioc (they're exited * or bfqd is being deallocated. */ - if (ioc->ioc_data == cic) + rcu_read_lock(); + if (rcu_dereference(ioc->ioc_data) == cic) { + rcu_read_unlock(); + spin_lock(&ioc->lock); rcu_assign_pointer(ioc->ioc_data, NULL); + spin_unlock(&ioc->lock); + } else + rcu_read_unlock(); if (cic->cfqq[BLK_RW_ASYNC] != NULL) { bfq_exit_bfqq(bfqd, cic->cfqq[BLK_RW_ASYNC]); diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c index 7ec2b12db7a3d..bc805ff326312 100644 --- a/block/bfq-iosched.c +++ b/block/bfq-iosched.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include "bfq.h" @@ -108,7 +109,8 @@ static DEFINE_IDA(cic_index_ida); #define BFQ_HW_QUEUE_THRESHOLD 4 #define BFQ_HW_QUEUE_SAMPLES 32 -#define BFQQ_SEEKY(bfqq) ((bfqq)->seek_mean > (8 * 1024)) +#define BFQQ_SEEK_THR (sector_t)(8 * 1024) +#define BFQQ_SEEKY(bfqq) ((bfqq)->seek_mean > BFQQ_SEEK_THR) /* Min samples used for peak rate estimation (for autotuning). */ #define BFQ_PEAK_RATE_SAMPLES 32 @@ -127,8 +129,10 @@ static DEFINE_IDA(cic_index_ida); #include "bfq-sched.c" #include "bfq-cgroup.c" -#define bfq_class_idle(cfqq) ((bfqq)->entity.ioprio_class ==\ +#define bfq_class_idle(bfqq) ((bfqq)->entity.ioprio_class ==\ IOPRIO_CLASS_IDLE) +#define bfq_class_rt(bfqq) ((bfqq)->entity.ioprio_class ==\ + IOPRIO_CLASS_RT) #define bfq_sample_valid(samples) ((samples) > 80) @@ -170,9 +174,10 @@ static inline int bfq_queue_empty(struct request_queue *q) */ static struct request *bfq_choose_req(struct bfq_data *bfqd, struct request *rq1, - struct request *rq2) + struct request *rq2, + sector_t last) { - sector_t last, s1, s2, d1 = 0, d2 = 0; + sector_t s1, s2, d1 = 0, d2 = 0; unsigned long back_max; #define BFQ_RQ1_WRAP 0x01 /* request 1 wraps */ #define BFQ_RQ2_WRAP 0x02 /* request 2 wraps */ @@ -195,8 +200,6 @@ static struct request *bfq_choose_req(struct bfq_data *bfqd, s1 = blk_rq_pos(rq1); s2 = blk_rq_pos(rq2); - last = bfqd->last_position; - /* * By definition, 1KiB is 2 sectors. */ @@ -259,6 +262,71 @@ static struct request *bfq_choose_req(struct bfq_data *bfqd, } } +static struct bfq_queue * +bfq_rq_pos_tree_lookup(struct bfq_data *bfqd, struct rb_root *root, + sector_t sector, struct rb_node **ret_parent, + struct rb_node ***rb_link) +{ + struct rb_node **p, *parent; + struct bfq_queue *bfqq = NULL; + + parent = NULL; + p = &root->rb_node; + while (*p) { + struct rb_node **n; + + parent = *p; + bfqq = rb_entry(parent, struct bfq_queue, pos_node); + + /* + * Sort strictly based on sector. Smallest to the left, + * largest to the right. + */ + if (sector > blk_rq_pos(bfqq->next_rq)) + n = &(*p)->rb_right; + else if (sector < blk_rq_pos(bfqq->next_rq)) + n = &(*p)->rb_left; + else + break; + p = n; + bfqq = NULL; + } + + *ret_parent = parent; + if (rb_link) + *rb_link = p; + + bfq_log(bfqd, "rq_pos_tree_lookup %llu: returning %lu", sector, + bfqq != NULL ? bfqq->pid : 0); + + return bfqq; +} + +static void bfq_rq_pos_tree_add(struct bfq_data *bfqd, struct bfq_queue *bfqq) +{ + struct rb_node **p, *parent; + struct bfq_queue *__bfqq; + + if (bfqq->pos_root != NULL) { + rb_erase(&bfqq->pos_node, bfqq->pos_root); + bfqq->pos_root = NULL; + } + + if (bfq_class_idle(bfqq)) + return; + if (!bfqq->next_rq) + return; + + bfqq->pos_root = &bfqd->rq_pos_tree; + __bfqq = bfq_rq_pos_tree_lookup(bfqd, bfqq->pos_root, + blk_rq_pos(bfqq->next_rq), &parent, &p); + if (__bfqq == NULL) { + rb_link_node(&bfqq->pos_node, parent, p); + rb_insert_color(&bfqq->pos_node, bfqq->pos_root); + } else + bfqq->pos_root = NULL; +} + static struct request *bfq_find_next_rq(struct bfq_data *bfqd, struct bfq_queue *bfqq, struct request *last) @@ -280,7 +348,7 @@ static struct request *bfq_find_next_rq(struct bfq_data *bfqd, next = rb_entry_rq(rbnext); } - return bfq_choose_req(bfqd, next, prev); + return bfq_choose_req(bfqd, next, prev, blk_rq_pos(last)); } static void bfq_del_rq_rb(struct request *rq) @@ -295,9 +363,17 @@ static void bfq_del_rq_rb(struct request *rq) elv_rb_del(&bfqq->sort_list, rq); - if (bfq_bfqq_busy(bfqq) && bfqq != bfqd->active_queue && - RB_EMPTY_ROOT(&bfqq->sort_list)) - bfq_del_bfqq_busy(bfqd, bfqq, 1); + if (RB_EMPTY_ROOT(&bfqq->sort_list)) { + if (bfq_bfqq_busy(bfqq) && bfqq != bfqd->active_queue) + bfq_del_bfqq_busy(bfqd, bfqq, 1); + /* + * Remove queue from request-position tree as it is empty. + */ + if (bfqq->pos_root != NULL) { + rb_erase(&bfqq->pos_node, bfqq->pos_root); + bfqq->pos_root = NULL; + } + } } /* see the definition of bfq_async_charge_factor for details */ @@ -305,7 +381,8 @@ static inline unsigned long bfq_serv_to_charge(struct request *rq, struct bfq_queue *bfqq) { return blk_rq_sectors(rq) * - (1 + ((!bfq_bfqq_sync(bfqq)) * bfq_async_charge_factor)); + (1 + ((!bfq_bfqq_sync(bfqq)) * (bfqq->raising_coeff == 1) * + bfq_async_charge_factor)); } /** @@ -352,8 +429,10 @@ static void bfq_add_rq_rb(struct request *rq) struct bfq_queue *bfqq = RQ_BFQQ(rq); struct bfq_entity *entity = &bfqq->entity; struct bfq_data *bfqd = bfqq->bfqd; - struct request *__alias, *next_rq; + struct request *__alias, *next_rq, *prev; unsigned long old_raising_coeff = bfqq->raising_coeff; + int idle_for_long_time = bfqq->budget_timeout + + bfqd->bfq_raising_min_idle_time < jiffies; bfq_log_bfqq(bfqd, bfqq, "add_rq_rb %d", rq_is_sync(rq)); bfqq->queued[rq_is_sync(rq)]++; @@ -369,11 +448,20 @@ static void bfq_add_rq_rb(struct request *rq) /* * Check if this request is a better next-serve candidate. */ - next_rq = bfq_choose_req(bfqd, bfqq->next_rq, rq); + prev = bfqq->next_rq; + next_rq = bfq_choose_req(bfqd, bfqq->next_rq, rq, bfqd->last_position); BUG_ON(next_rq == NULL); bfqq->next_rq = next_rq; + /* + * Adjust priority tree position, if next_rq changes. + */ + if (prev != bfqq->next_rq) + bfq_rq_pos_tree_add(bfqd, bfqq); + if (!bfq_bfqq_busy(bfqq)) { + int soft_rt = bfqd->bfq_raising_max_softrt_rate > 0 && + bfqq->soft_rt_next_start < jiffies; entity->budget = max_t(unsigned long, bfqq->max_budget, bfq_serv_to_charge(next_rq, bfqq)); @@ -384,26 +472,58 @@ static void bfq_add_rq_rb(struct request *rq) * If the queue is not being boosted and has been idle * for enough time, start a weight-raising period */ - if(old_raising_coeff == 1 && - bfqq->last_rais_start_finish + - bfqd->bfq_raising_min_idle_time < jiffies) { + if(old_raising_coeff == 1 && (idle_for_long_time || soft_rt)) { bfqq->raising_coeff = bfqd->bfq_raising_coeff; - entity->ioprio_changed = 1; + bfqq->raising_cur_max_time = idle_for_long_time ? + bfqd->bfq_raising_max_time : + bfqd->bfq_raising_rt_max_time; bfq_log_bfqq(bfqd, bfqq, - "wrais starting at %lu msec", - bfqq->last_rais_start_finish); + "wrais starting at %llu msec," + "rais_max_time %u", + bfqq->last_rais_start_finish, + jiffies_to_msecs(bfqq-> + raising_cur_max_time)); + } else if (old_raising_coeff > 1) { + if (idle_for_long_time) + bfqq->raising_cur_max_time = + bfqd->bfq_raising_max_time; + else if (bfqq->raising_cur_max_time == + bfqd->bfq_raising_rt_max_time && + !soft_rt) { + bfqq->raising_coeff = 1; + bfq_log_bfqq(bfqd, bfqq, + "wrais ending at %llu msec," + "rais_max_time %u", + bfqq->last_rais_start_finish, + jiffies_to_msecs(bfqq-> + raising_cur_max_time)); + } } + if (old_raising_coeff != bfqq->raising_coeff) + entity->ioprio_changed = 1; add_bfqq_busy: bfq_add_bfqq_busy(bfqd, bfqq); - } else - bfq_updated_next_req(bfqd, bfqq); + } else { + if(bfqd->low_latency && old_raising_coeff == 1 && + !rq_is_sync(rq) && + bfqq->last_rais_start_finish + + bfqd->bfq_raising_min_idle_time < jiffies) { + bfqq->raising_coeff = bfqd->bfq_raising_coeff; - if (! bfqd->low_latency) - return; + entity->ioprio_changed = 1; + bfq_log_bfqq(bfqd, bfqq, + "non-idle wrais starting at %llu msec," + "rais_max_time %u", + bfqq->last_rais_start_finish, + jiffies_to_msecs(bfqq-> + raising_cur_max_time)); + } + bfq_updated_next_req(bfqd, bfqq); + } - if(old_raising_coeff == 1 || - (bfqd->bfq_raising_max_softrt_rate > 0 && - bfqq->soft_rt_next_start < jiffies)) + if(bfqd->low_latency && + (old_raising_coeff == 1 || bfqq->raising_coeff == 1 || + idle_for_long_time)) bfqq->last_rais_start_finish = jiffies; } @@ -442,6 +562,8 @@ static void bfq_activate_request(struct request_queue *q, struct request *rq) bfqd->rq_in_driver++; bfqd->last_position = blk_rq_pos(rq) + blk_rq_sectors(rq); + bfq_log(bfqd, "activate_request: new bfqd->last_position %llu", + bfqd->last_position); } static void bfq_deactivate_request(struct request_queue *q, struct request *rq) @@ -499,12 +621,19 @@ static void bfq_merged_request(struct request_queue *q, struct request *req, static void bfq_merged_requests(struct request_queue *q, struct request *rq, struct request *next) { + struct bfq_queue *bfqq = RQ_BFQQ(rq); + /* * Reposition in fifo if next is older than rq. */ if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist) && - time_before(next->start_time, rq->start_time)) + time_before(rq_fifo_time(next), rq_fifo_time(rq))) { list_move(&rq->queuelist, &next->queuelist); + rq_set_fifo_time(rq, rq_fifo_time(next)); + } + + if (bfqq->next_rq == next) + bfqq->next_rq = rq; bfq_remove_request(next); } @@ -552,15 +681,146 @@ static void __bfq_set_active_queue(struct bfq_data *bfqd, /* * Get and set a new active queue for service. */ -static struct bfq_queue *bfq_set_active_queue(struct bfq_data *bfqd) +static struct bfq_queue *bfq_set_active_queue(struct bfq_data *bfqd, + struct bfq_queue *bfqq) { - struct bfq_queue *bfqq; + if (!bfqq) + bfqq = bfq_get_next_queue(bfqd); + else + bfq_get_next_queue_forced(bfqd, bfqq); - bfqq = bfq_get_next_queue(bfqd); __bfq_set_active_queue(bfqd, bfqq); return bfqq; } +static inline sector_t bfq_dist_from_last(struct bfq_data *bfqd, + struct request *rq) +{ + if (blk_rq_pos(rq) >= bfqd->last_position) + return blk_rq_pos(rq) - bfqd->last_position; + else + return bfqd->last_position - blk_rq_pos(rq); +} + +/* + * Return true if bfqq has no request pending and rq is close enough to + * bfqd->last_position, or if rq is closer to bfqd->last_position than + * bfqq->next_rq + */ +static inline int bfq_rq_close(struct bfq_data *bfqd, struct bfq_queue *bfqq, + struct request *rq) +{ + sector_t sdist = bfqq->seek_mean; + + if (!bfq_sample_valid(bfqq->seek_samples)) + sdist = BFQQ_SEEK_THR; + + /* If seek_mean is large, using it as close criteria is meaningless */ + if (sdist > BFQQ_SEEK_THR) + sdist = BFQQ_SEEK_THR; + + return bfq_dist_from_last(bfqd, rq) <= sdist; +} + +static struct bfq_queue *bfqq_close(struct bfq_data *bfqd, + struct bfq_queue *cur_bfqq) +{ + struct rb_root *root = &bfqd->rq_pos_tree; + struct rb_node *parent, *node; + struct bfq_queue *__bfqq; + sector_t sector = bfqd->last_position; + + if (RB_EMPTY_ROOT(root)) + return NULL; + + /* + * First, if we find a request starting at the end of the last + * request, choose it. + */ + __bfqq = bfq_rq_pos_tree_lookup(bfqd, root, sector, &parent, NULL); + if (__bfqq != NULL) + return __bfqq; + + /* + * If the exact sector wasn't found, the parent of the NULL leaf + * will contain the closest sector (rq_pos_tree sorted by next_request + * position). + */ + __bfqq = rb_entry(parent, struct bfq_queue, pos_node); + if (bfq_rq_close(bfqd, cur_bfqq, __bfqq->next_rq)) + return __bfqq; + + if (blk_rq_pos(__bfqq->next_rq) < sector) + node = rb_next(&__bfqq->pos_node); + else + node = rb_prev(&__bfqq->pos_node); + if (node == NULL) + return NULL; + + __bfqq = rb_entry(node, struct bfq_queue, pos_node); + if (bfq_rq_close(bfqd, cur_bfqq, __bfqq->next_rq)) + return __bfqq; + + return NULL; +} + +/* + * bfqd - obvious + * cur_bfqq - passed in so that we don't decide that the current queue + * is closely cooperating with itself. + * + * We are assuming that cur_bfqq has dispatched at least one request, + * and that bfqd->last_position reflects a position on the disk associated + * with the I/O issued by cur_bfqq. + */ +static struct bfq_queue *bfq_close_cooperator(struct bfq_data *bfqd, + struct bfq_queue *cur_bfqq) +{ + struct bfq_queue *bfqq; + + if (bfq_class_idle(cur_bfqq)) + return NULL; + if (!bfq_bfqq_sync(cur_bfqq)) + return NULL; + if (BFQQ_SEEKY(cur_bfqq)) + return NULL; + + /* If device has only one backlogged bfq_queue, don't search. */ + if (bfqd->busy_queues == 1) + return NULL; + + /* + * We should notice if some of the queues are cooperating, e.g. + * working closely on the same area of the disk. In that case, + * we can group them together and don't waste time idling. + */ + bfqq = bfqq_close(bfqd, cur_bfqq); + if (bfqq == NULL || bfqq == cur_bfqq) + return NULL; + + /* + * Do not merge queues from different bfq_groups. + */ + if (bfqq->entity.parent != cur_bfqq->entity.parent) + return NULL; + + /* + * It only makes sense to merge sync queues. + */ + if (!bfq_bfqq_sync(bfqq)) + return NULL; + if (BFQQ_SEEKY(bfqq)) + return NULL; + + /* + * Do not merge queues of different priority classes. + */ + if (bfq_class_rt(bfqq) != bfq_class_rt(cur_bfqq)) + return NULL; + + return bfqq; +} + /* * If enough samples have been computed, return the current max budget * stored in bfqd, which is dynamically updated according to the @@ -617,7 +877,7 @@ static void bfq_arm_slice_timer(struct bfq_data *bfqd) bfqq->raising_coeff == 1) sl = min(sl, msecs_to_jiffies(BFQ_MIN_TT)); else if (bfqq->raising_coeff > 1) - sl = sl * 4; + sl = sl * 3; bfqd->last_idling_start = ktime_get(); mod_timer(&bfqd->idle_slice_timer, jiffies + sl); bfq_log(bfqd, "arm idle: %lu/%lu ms", @@ -632,13 +892,19 @@ static void bfq_arm_slice_timer(struct bfq_data *bfqd) static void bfq_set_budget_timeout(struct bfq_data *bfqd) { struct bfq_queue *bfqq = bfqd->active_queue; + unsigned int timeout_coeff = + bfqq->raising_cur_max_time == bfqd->bfq_raising_rt_max_time ? + 1 : (bfqq->entity.weight / bfqq->entity.orig_weight); bfqd->last_budget_start = ktime_get(); bfq_clear_bfqq_budget_new(bfqq); bfqq->budget_timeout = jiffies + - bfqd->bfq_timeout[bfq_bfqq_sync(bfqq)] * - (bfqq->entity.weight / bfqq->entity.orig_weight); + bfqd->bfq_timeout[bfq_bfqq_sync(bfqq)] * timeout_coeff; + + bfq_log_bfqq(bfqd, bfqq, "set budget_timeout %u", + jiffies_to_msecs(bfqd->bfq_timeout[bfq_bfqq_sync(bfqq)] * + timeout_coeff)); } /* @@ -662,9 +928,7 @@ static void bfq_dispatch_insert(struct request_queue *q, struct request *rq) */ static struct request *bfq_check_fifo(struct bfq_queue *bfqq) { - struct bfq_data *bfqd = bfqq->bfqd; - struct request *rq; - int fifo; + struct request *rq = NULL; if (bfq_bfqq_fifo_expire(bfqq)) return NULL; @@ -674,15 +938,71 @@ static struct request *bfq_check_fifo(struct bfq_queue *bfqq) if (list_empty(&bfqq->fifo)) return NULL; - fifo = bfq_bfqq_sync(bfqq); rq = rq_entry_fifo(bfqq->fifo.next); - if (time_before(jiffies, rq->start_time + bfqd->bfq_fifo_expire[fifo])) + if (time_before(jiffies, rq_fifo_time(rq))) return NULL; return rq; } +/* + * Must be called with the queue_lock held. + */ +static int bfqq_process_refs(struct bfq_queue *bfqq) +{ + int process_refs, io_refs; + + io_refs = bfqq->allocated[READ] + bfqq->allocated[WRITE]; + process_refs = atomic_read(&bfqq->ref) - io_refs; + BUG_ON(process_refs < 0); + return process_refs; +} + +static void bfq_setup_merge(struct bfq_queue *bfqq, struct bfq_queue *new_bfqq) +{ + int process_refs, new_process_refs; + struct bfq_queue *__bfqq; + + /* + * If there are no process references on the new_bfqq, then it is + * unsafe to follow the ->new_bfqq chain as other bfqq's in the chain + * may have dropped their last reference (not just their last process + * reference). + */ + if (!bfqq_process_refs(new_bfqq)) + return; + + /* Avoid a circular list and skip interim queue merges. */ + while ((__bfqq = new_bfqq->new_bfqq)) { + if (__bfqq == bfqq) + return; + new_bfqq = __bfqq; + } + + process_refs = bfqq_process_refs(bfqq); + new_process_refs = bfqq_process_refs(new_bfqq); + /* + * If the process for the bfqq has gone away, there is no + * sense in merging the queues. + */ + if (process_refs == 0 || new_process_refs == 0) + return; + + /* + * Merge in the direction of the lesser amount of work. + */ + if (new_process_refs >= process_refs) { + bfqq->new_bfqq = new_bfqq; + atomic_add(process_refs, &new_bfqq->ref); + } else { + new_bfqq->new_bfqq = bfqq; + atomic_add(new_process_refs, &bfqq->ref); + } + bfq_log_bfqq(bfqq->bfqd, bfqq, "scheduling merge with queue %d", + new_bfqq->pid); +} + static inline unsigned long bfq_bfqq_budget_left(struct bfq_queue *bfqq) { struct bfq_entity *entity = &bfqq->entity; @@ -695,10 +1015,31 @@ static void __bfq_bfqq_expire(struct bfq_data *bfqd, struct bfq_queue *bfqq) __bfq_bfqd_reset_active(bfqd); - if (RB_EMPTY_ROOT(&bfqq->sort_list)) + if (RB_EMPTY_ROOT(&bfqq->sort_list)) { bfq_del_bfqq_busy(bfqd, bfqq, 1); - else + /* + * overloading budget_timeout field to store when + * the queue remains with no backlog, used by + * the weight-raising mechanism + */ + bfqq->budget_timeout = jiffies ; + } + else { bfq_activate_bfqq(bfqd, bfqq); + /* + * Resort priority tree of potential close cooperators. + */ + bfq_rq_pos_tree_add(bfqd, bfqq); + } + + /* + * If this bfqq is shared between multiple processes, check + * to make sure that those processes are still issuing I/Os + * within the mean seek distance. If not, it may be time to + * break the queues apart again. + */ + if (bfq_bfqq_coop(bfqq) && BFQQ_SEEKY(bfqq)) + bfq_mark_bfqq_split_coop(bfqq); } /** @@ -1055,6 +1396,12 @@ static int bfq_bfqq_budget_timeout(struct bfq_queue *bfqq) */ static inline int bfq_may_expire_for_budg_timeout(struct bfq_queue *bfqq) { + bfq_log_bfqq(bfqq->bfqd, bfqq, + "may_budget_timeout: wr %d left %d timeout %d", + bfq_bfqq_wait_request(bfqq), + bfq_bfqq_budget_left(bfqq) >= bfqq->entity.budget / 3, + bfq_bfqq_budget_timeout(bfqq)); + return (!bfq_bfqq_wait_request(bfqq) || bfq_bfqq_budget_left(bfqq) >= bfqq->entity.budget / 3) && @@ -1067,7 +1414,7 @@ static inline int bfq_may_expire_for_budg_timeout(struct bfq_queue *bfqq) */ static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd) { - struct bfq_queue *bfqq; + struct bfq_queue *bfqq, *new_bfqq = NULL; struct request *next_rq; enum bfqq_expiration reason = BFQ_BFQQ_BUDGET_TIMEOUT; @@ -1077,6 +1424,17 @@ static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd) bfq_log_bfqq(bfqd, bfqq, "select_queue: already active queue"); + /* + * If another queue has a request waiting within our mean seek + * distance, let it run. The expire code will check for close + * cooperators and put the close queue at the front of the + * service tree. If possible, merge the expiring queue with the + * new bfqq. + */ + new_bfqq = bfq_close_cooperator(bfqd, bfqq); + if (new_bfqq != NULL && bfqq->new_bfqq == NULL) + bfq_setup_merge(bfqq, new_bfqq); + if (bfq_may_expire_for_budg_timeout(bfqq)) goto expire; @@ -1090,116 +1448,172 @@ static struct bfq_queue *bfq_select_queue(struct bfq_data *bfqd) bfq_bfqq_budget_left(bfqq)) { reason = BFQ_BFQQ_BUDGET_EXHAUSTED; goto expire; - } else - goto keep_queue; + } else { + /* + * The idle timer may be pending because we may not + * disable disk idling even when a new request arrives + */ + if (timer_pending(&bfqd->idle_slice_timer)) { + /* + * If we get here: 1) at least a new request + * has arrived but we have not disabled the + * timer because the request was too small, + * 2) then the block layer has unplugged the + * device, causing the dispatch to be invoked. + * + * Since the device is unplugged, now the + * requests are probably large enough to + * provide a reasonable throughput. + * So we disable idling. + */ + bfq_clear_bfqq_wait_request(bfqq); + del_timer(&bfqd->idle_slice_timer); + } + if (new_bfqq == NULL) + goto keep_queue; + else + goto expire; + } } /* - * No requests pending. If the active queue still has - * requests in flight or is idling for a new request, then keep it. + * No requests pending. If there is no cooperator, and the active + * queue still has requests in flight or is idling for a new request, + * then keep it. */ - if (timer_pending(&bfqd->idle_slice_timer) || - (bfqq->dispatched != 0 && bfq_bfqq_idle_window(bfqq))) { + if (new_bfqq == NULL && (timer_pending(&bfqd->idle_slice_timer) || + (bfqq->dispatched != 0 && bfq_bfqq_idle_window(bfqq)))) { bfqq = NULL; goto keep_queue; + } else if (new_bfqq != NULL && timer_pending(&bfqd->idle_slice_timer)) { + /* + * Expiring the queue because there is a close cooperator, + * cancel timer. + */ + bfq_clear_bfqq_wait_request(bfqq); + del_timer(&bfqd->idle_slice_timer); } reason = BFQ_BFQQ_NO_MORE_REQUESTS; expire: bfq_bfqq_expire(bfqd, bfqq, 0, reason); new_queue: - bfqq = bfq_set_active_queue(bfqd); - bfq_log(bfqd, "select_queue: new queue returned (possibly NULL)"); + bfqq = bfq_set_active_queue(bfqd, new_bfqq); + bfq_log(bfqd, "select_queue: new queue %lu returned", + bfqq != NULL ? bfqq->pid : 0); keep_queue: return bfqq; } -/* - * Dispatch some requests from bfqq, moving them to the request queue - * dispatch list. - */ -static int __bfq_dispatch_requests(struct bfq_data *bfqd, - struct bfq_queue *bfqq, - int max_dispatch) +static void update_raising_data(struct bfq_data *bfqd, struct bfq_queue *bfqq) { - int dispatched = 0; - - BUG_ON(RB_EMPTY_ROOT(&bfqq->sort_list)); - - do { - struct request *rq; - unsigned long service_to_charge; + if (bfqq->raising_coeff > 1) { /* queue is being boosted */ + struct bfq_entity *entity = &bfqq->entity; - /* Follow expired path, else get first next available. */ - rq = bfq_check_fifo(bfqq); - if (rq == NULL) - rq = bfqq->next_rq; - service_to_charge = bfq_serv_to_charge(rq, bfqq); - - if (service_to_charge > bfq_bfqq_budget_left(bfqq)) { - /* - * Expire the queue for budget exhaustion, and - * make sure that the next act_budget is enough - * to serve the next request, even if it comes - * from the fifo expired path. - */ - bfqq->next_rq = rq; - goto expire; - } - - /* Finally, insert request into driver dispatch list. */ - bfq_bfqq_served(bfqq, service_to_charge); - bfq_dispatch_insert(bfqd->queue, rq); - - if (bfqq->raising_coeff > 1) { /* queue is being boosted */ - struct bfq_entity *entity = &bfqq->entity; + bfq_log_bfqq(bfqd, bfqq, + "raising period dur %u/%u msec, " + "old raising coeff %u, w %d(%d)", + jiffies_to_msecs(jiffies - + bfqq->last_rais_start_finish), + jiffies_to_msecs(bfqq->raising_cur_max_time), + bfqq->raising_coeff, + bfqq->entity.weight, bfqq->entity.orig_weight); + BUG_ON(bfqq != bfqd->active_queue && entity->weight != + entity->orig_weight * bfqq->raising_coeff); + if(entity->ioprio_changed) bfq_log_bfqq(bfqd, bfqq, - "raising period dur %llu/%lu msec, " - "old raising coeff %lu, w %lu(%lu)", - jiffies - bfqq->last_rais_start_finish, - bfqd->bfq_raising_max_time, - bfqq->raising_coeff, - bfqq->entity.weight, bfqq->entity.orig_weight); - - BUG_ON(entity->weight != - entity->orig_weight * bfqq->raising_coeff); - if(entity->ioprio_changed) - bfq_log_bfqq(bfqd, bfqq, - "WARN: pending prio change"); - /* - * If too much time has elapsed from the beginning - * of this weight-raising period, stop it - */ - if (jiffies - bfqq->last_rais_start_finish > - bfqd->bfq_raising_max_time) { + "WARN: pending prio change"); + /* + * If too much time has elapsed from the beginning + * of this weight-raising period and process is not soft + * real-time, stop it + */ + if (jiffies - bfqq->last_rais_start_finish > + bfqq->raising_cur_max_time) { + int soft_rt = bfqd->bfq_raising_max_softrt_rate > 0 && + bfqq->soft_rt_next_start < jiffies; + + bfqq->last_rais_start_finish = jiffies; + if (soft_rt) + bfqq->raising_cur_max_time = + bfqd->bfq_raising_rt_max_time; + else { bfqq->raising_coeff = 1; - bfqq->last_rais_start_finish = jiffies; - entity->ioprio_changed = 1; __bfq_entity_update_weight_prio( bfq_entity_service_tree(entity), entity); } } + } +} - bfq_log_bfqq(bfqd, bfqq, "dispatched %lu sec req (%llu), " - "budg left %lu", - blk_rq_sectors(rq), blk_rq_pos(rq), - bfq_bfqq_budget_left(bfqq)); - dispatched++; +/* + * Dispatch one request from bfqq, moving it to the request queue + * dispatch list. + */ +static int bfq_dispatch_request(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + int dispatched = 0; + struct request *rq; + unsigned long service_to_charge; - if (bfqd->active_cic == NULL) { - atomic_long_inc(&RQ_CIC(rq)->ioc->refcount); - bfqd->active_cic = RQ_CIC(rq); - } + BUG_ON(RB_EMPTY_ROOT(&bfqq->sort_list)); - if (RB_EMPTY_ROOT(&bfqq->sort_list)) - break; - } while (dispatched < max_dispatch); + /* Follow expired path, else get first next available. */ + rq = bfq_check_fifo(bfqq); + if (rq == NULL) + rq = bfqq->next_rq; + service_to_charge = bfq_serv_to_charge(rq, bfqq); + + if (service_to_charge > bfq_bfqq_budget_left(bfqq)) { + /* + * This may happen if the next rq is chosen + * in fifo order instead of sector order. + * The budget is properly dimensioned + * to be always sufficient to serve the next request + * only if it is chosen in sector order. The reason is + * that it would be quite inefficient and little useful + * to always make sure that the budget is large enough + * to serve even the possible next rq in fifo order. + * In fact, requests are seldom served in fifo order. + * + * Expire the queue for budget exhaustion, and + * make sure that the next act_budget is enough + * to serve the next request, even if it comes + * from the fifo expired path. + */ + bfqq->next_rq = rq; + /* + * Since this dispatch is failed, make sure that + * a new one will be performed + */ + if (!bfqd->rq_in_driver) + bfq_schedule_dispatch(bfqd); + goto expire; + } + + /* Finally, insert request into driver dispatch list. */ + bfq_bfqq_served(bfqq, service_to_charge); + bfq_dispatch_insert(bfqd->queue, rq); + + update_raising_data(bfqd, bfqq); + + bfq_log_bfqq(bfqd, bfqq, "dispatched %u sec req (%llu), " + "budg left %lu", + blk_rq_sectors(rq), + (long long unsigned)blk_rq_pos(rq), + bfq_bfqq_budget_left(bfqq)); - bfq_log_bfqq(bfqd, bfqq, "dispatched %d reqs", dispatched); + dispatched++; + + if (bfqd->active_cic == NULL) { + atomic_long_inc(&RQ_CIC(rq)->ioc->refcount); + bfqd->active_cic = RQ_CIC(rq); + } if (bfqd->busy_queues > 1 && ((!bfq_bfqq_sync(bfqq) && dispatched >= bfqd->bfq_max_budget_async_rq) || @@ -1263,7 +1677,7 @@ static int bfq_dispatch_requests(struct request_queue *q, int force) { struct bfq_data *bfqd = q->elevator->elevator_data; struct bfq_queue *bfqq; - int dispatched; + int max_dispatch; bfq_log(bfqd, "dispatch requests: %d busy queues", bfqd->busy_queues); if (bfqd->busy_queues == 0) @@ -1272,37 +1686,36 @@ static int bfq_dispatch_requests(struct request_queue *q, int force) if (unlikely(force)) return bfq_forced_dispatch(bfqd); - dispatched = 0; - while ((bfqq = bfq_select_queue(bfqd)) != NULL) { - int max_dispatch; + if((bfqq = bfq_select_queue(bfqd)) == NULL) + return 0; - max_dispatch = bfqd->bfq_quantum; - if (bfq_class_idle(bfqq)) - max_dispatch = 1; + max_dispatch = bfqd->bfq_quantum; + if (bfq_class_idle(bfqq)) + max_dispatch = 1; - if (!bfq_bfqq_sync(bfqq)) - max_dispatch = bfqd->bfq_max_budget_async_rq; + if (!bfq_bfqq_sync(bfqq)) + max_dispatch = bfqd->bfq_max_budget_async_rq; - if (bfqq->dispatched >= max_dispatch) { - if (bfqd->busy_queues > 1) - break; - if (bfqq->dispatched >= 4 * max_dispatch) - break; - } + if (bfqq->dispatched >= max_dispatch) { + if (bfqd->busy_queues > 1) + return 0; + if (bfqq->dispatched >= 4 * max_dispatch) + return 0; + } - if (bfqd->sync_flight != 0 && !bfq_bfqq_sync(bfqq)) - break; + if (bfqd->sync_flight != 0 && !bfq_bfqq_sync(bfqq)) + return 0; - bfq_clear_bfqq_wait_request(bfqq); - BUG_ON(timer_pending(&bfqd->idle_slice_timer)); + bfq_clear_bfqq_wait_request(bfqq); + BUG_ON(timer_pending(&bfqd->idle_slice_timer)); - dispatched += __bfq_dispatch_requests(bfqd, bfqq, max_dispatch); - bfq_log_bfqq(bfqd, bfqq, "total dispatched increased to %d " - "(max_disp %d)", dispatched, max_dispatch); - } + if (! bfq_dispatch_request(bfqd, bfqq)) + return 0; - bfq_log(bfqd, "final total dispatched=%d", dispatched); - return dispatched; + bfq_log_bfqq(bfqd, bfqq, "dispatched one request of %d" + "(max_disp %d)", bfqq->pid, max_dispatch); + + return 1; } /* @@ -1332,6 +1745,27 @@ static void bfq_put_queue(struct bfq_queue *bfqq) kmem_cache_free(bfq_pool, bfqq); } +static void bfq_put_cooperator(struct bfq_queue *bfqq) +{ + struct bfq_queue *__bfqq, *next; + + /* + * If this queue was scheduled to merge with another queue, be + * sure to drop the reference taken on that queue (and others in + * the merge chain). See bfq_setup_merge and bfq_merge_bfqqs. + */ + __bfqq = bfqq->new_bfqq; + while (__bfqq) { + if (__bfqq == bfqq) { + WARN(1, "bfqq->new_bfqq loop detected.\n"); + break; + } + next = __bfqq->new_bfqq; + bfq_put_queue(__bfqq); + __bfqq = next; + } +} + static void bfq_exit_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq) { if (bfqq == bfqd->active_queue) { @@ -1340,6 +1774,9 @@ static void bfq_exit_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq) } bfq_log_bfqq(bfqd, bfqq, "exit_bfqq: %p, %d", bfqq, bfqq->ref); + + bfq_put_cooperator(bfqq); + bfq_put_queue(bfqq); } @@ -1426,6 +1863,32 @@ static void bfq_changed_ioprio(struct io_context *ioc, bfq_put_bfqd_unlock(bfqd, &flags); } +static void bfq_init_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq, + pid_t pid, int is_sync) +{ + RB_CLEAR_NODE(&bfqq->entity.rb_node); + INIT_LIST_HEAD(&bfqq->fifo); + + atomic_set(&bfqq->ref, 0); + bfqq->bfqd = bfqd; + + bfq_mark_bfqq_prio_changed(bfqq); + + if (is_sync) { + if (!bfq_class_idle(bfqq)) + bfq_mark_bfqq_idle_window(bfqq); + bfq_mark_bfqq_sync(bfqq); + } + + /* Tentative initial value to trade off between thr and lat */ + bfqq->max_budget = (2 * bfq_max_budget(bfqd)) / 3; + bfqq->pid = pid; + + bfqq->raising_coeff = 1; + bfqq->last_rais_start_finish = 0; + bfqq->soft_rt_next_start = -1; +} + static struct bfq_queue *bfq_find_alloc_queue(struct bfq_data *bfqd, struct bfq_group *bfqg, int is_sync, @@ -1440,63 +1903,44 @@ static struct bfq_queue *bfq_find_alloc_queue(struct bfq_data *bfqd, /* cic always exists here */ bfqq = cic_to_bfqq(cic, is_sync); - if (bfqq == NULL) { + /* + * Always try a new alloc if we fall back to the OOM bfqq + * originally, since it should just be a temporary situation. + */ + if (bfqq == NULL || bfqq == &bfqd->oom_bfqq) { + bfqq = NULL; if (new_bfqq != NULL) { bfqq = new_bfqq; new_bfqq = NULL; } else if (gfp_mask & __GFP_WAIT) { - /* - * Inform the allocator of the fact that we will - * just repeat this allocation if it fails, to allow - * the allocator to do whatever it needs to attempt to - * free memory. - */ spin_unlock_irq(bfqd->queue->queue_lock); new_bfqq = kmem_cache_alloc_node(bfq_pool, - gfp_mask | __GFP_NOFAIL | __GFP_ZERO, + gfp_mask | __GFP_ZERO, bfqd->queue->node); spin_lock_irq(bfqd->queue->queue_lock); - goto retry; + if (new_bfqq != NULL) + goto retry; } else { bfqq = kmem_cache_alloc_node(bfq_pool, gfp_mask | __GFP_ZERO, bfqd->queue->node); - if (bfqq == NULL) - goto out; } - RB_CLEAR_NODE(&bfqq->entity.rb_node); - INIT_LIST_HEAD(&bfqq->fifo); - - atomic_set(&bfqq->ref, 0); - bfqq->bfqd = bfqd; - - bfq_mark_bfqq_prio_changed(bfqq); + if (bfqq != NULL) { + bfq_init_bfqq(bfqd, bfqq, current->pid, is_sync); + bfq_log_bfqq(bfqd, bfqq, "allocated"); + } else { + bfqq = &bfqd->oom_bfqq; + bfq_log_bfqq(bfqd, bfqq, "using oom bfqq"); + } bfq_init_prio_data(bfqq, ioc); bfq_init_entity(&bfqq->entity, bfqg); - - if (is_sync) { - if (!bfq_class_idle(bfqq)) - bfq_mark_bfqq_idle_window(bfqq); - bfq_mark_bfqq_sync(bfqq); - } - /* Tentative initial value to trade off between thr and lat */ - bfqq->max_budget = (2 * bfq_max_budget(bfqd)) / 3; - bfqq->pid = current->pid; - - bfqq->raising_coeff = 1; - bfqq->last_rais_start_finish = 0; - bfqq->soft_rt_next_start = -1; - - bfq_log_bfqq(bfqd, bfqq, "allocated"); } if (new_bfqq != NULL) kmem_cache_free(bfq_pool, new_bfqq); -out: - WARN_ON((gfp_mask & __GFP_WAIT) && bfqq == NULL); return bfqq; } @@ -1531,11 +1975,8 @@ static struct bfq_queue *bfq_get_queue(struct bfq_data *bfqd, bfqq = *async_bfqq; } - if (bfqq == NULL) { + if (bfqq == NULL) bfqq = bfq_find_alloc_queue(bfqd, bfqg, is_sync, ioc, gfp_mask); - if (bfqq == NULL) - return NULL; - } /* * Pin the queue now that it's allocated, scheduler exit will prune it. @@ -1590,6 +2031,19 @@ static void bfq_update_io_seektime(struct bfq_data *bfqd, bfqq->seek_total = (7*bfqq->seek_total + (u64)256*sdist) / 8; total = bfqq->seek_total + (bfqq->seek_samples/2); do_div(total, bfqq->seek_samples); + if (bfq_bfqq_coop(bfqq)) { + /* + * If the mean seektime increases for a (non-seeky) shared + * queue, some cooperator is likely to be idling too much. + * On the contrary, if it decreases, some cooperator has + * probably waked up. + * + */ + if ((sector_t)total < bfqq->seek_mean) + bfq_mark_bfqq_some_coop_idle(bfqq) ; + else if ((sector_t)total > bfqq->seek_mean) + bfq_clear_bfqq_some_coop_idle(bfqq) ; + } bfqq->seek_mean = (sector_t)total; bfq_log_bfqq(bfqd, bfqq, "dist=%llu mean=%llu", (u64)sdist, @@ -1618,7 +2072,8 @@ static void bfq_update_idle_window(struct bfq_data *bfqd, bfqq->raising_coeff == 1)) enable_idle = 0; else if (bfq_sample_valid(cic->ttime_samples)) { - if (cic->ttime_mean > bfqd->bfq_slice_idle) + if (cic->ttime_mean > bfqd->bfq_slice_idle && + bfqq->raising_coeff == 1) enable_idle = 0; else enable_idle = 1; @@ -1658,6 +2113,25 @@ static void bfq_rq_enqueued(struct bfq_data *bfqd, struct bfq_queue *bfqq, bfqq->last_request_pos = blk_rq_pos(rq) + blk_rq_sectors(rq); if (bfqq == bfqd->active_queue) { + /* + * If there is just this request queued and the request + * is small, just make sure the queue is plugged and exit. + * In this way, if the disk is being idled to wait for a new + * request from the active queue, we avoid unplugging the + * device for this request. + * + * By doing so, we spare the disk to be committed + * to serve just a small request. On the contrary, we wait for + * the block layer to decide when to unplug the device: + * hopefully, new requests will be merged to this + * one quickly, then the device will be unplugged + * and larger requests will be dispatched. + */ + if (bfqq->queued[rq_is_sync(rq)] == 1 && + blk_rq_sectors(rq) < 32) { + blk_plug_device(bfqd->queue); + return; + } if (bfq_bfqq_wait_request(bfqq)) { /* * If we are waiting for a request for this queue, let @@ -1689,6 +2163,7 @@ static void bfq_insert_request(struct request_queue *q, struct request *rq) bfq_add_rq_rb(rq); + rq_set_fifo_time(rq, jiffies + bfqd->bfq_fifo_expire[rq_is_sync(rq)]); list_add_tail(&rq->queuelist, &bfqq->fifo); bfq_rq_enqueued(bfqd, bfqq, rq); @@ -1746,12 +2221,24 @@ static void bfq_completed_request(struct request_queue *q, struct request *rq) if (bfq_bfqq_budget_new(bfqq)) bfq_set_budget_timeout(bfqd); + /* Idling is disabled also for cooperation issues: + * 1) there is a close cooperator for the queue, or + * 2) the queue is shared and some cooperator is likely + * to be idle (in this case, by not arming the idle timer, + * we try to slow down the queue, to prevent the zones + * of the disk accessed by the active cooperators to become + * too distant from the zone that will be accessed by the + * currently idle cooperators) + */ if (bfq_may_expire_for_budg_timeout(bfqq)) bfq_bfqq_expire(bfqd, bfqq, 0, BFQ_BFQQ_BUDGET_TIMEOUT); else if (sync && (bfqd->rq_in_driver == 0 || bfqq->raising_coeff > 1) - && RB_EMPTY_ROOT(&bfqq->sort_list)) + && RB_EMPTY_ROOT(&bfqq->sort_list) + && !bfq_close_cooperator(bfqd, bfqq) + && (!bfq_bfqq_coop(bfqq) || + !bfq_bfqq_some_coop_idle(bfqq))) bfq_arm_slice_timer(bfqd); } @@ -1776,12 +2263,10 @@ static void bfq_prio_boost(struct bfq_queue *bfqq) bfqq->entity.new_ioprio = IOPRIO_NORM; } else { /* - * Check if we need to unboost the queue + * Unboost the queue (if needed) */ - if (bfqq->entity.new_ioprio_class != bfqq->org_ioprio_class) - bfqq->entity.new_ioprio_class = bfqq->org_ioprio_class; - if (bfqq->entity.new_ioprio != bfqq->org_ioprio) - bfqq->entity.new_ioprio = bfqq->org_ioprio; + bfqq->entity.new_ioprio_class = bfqq->org_ioprio_class; + bfqq->entity.new_ioprio = bfqq->org_ioprio; } } @@ -1812,7 +2297,7 @@ static int bfq_may_queue(struct request_queue *q, int rw) if (cic == NULL) return ELV_MQUEUE_MAY; - bfqq = cic_to_bfqq(cic, rw & REQ_SYNC); + bfqq = cic_to_bfqq(cic, rw_is_sync(rw)); if (bfqq != NULL) { bfq_init_prio_data(bfqq, cic->ioc); bfq_prio_boost(bfqq); @@ -1847,6 +2332,42 @@ static void bfq_put_request(struct request *rq) } } +static struct bfq_queue * +bfq_merge_bfqqs(struct bfq_data *bfqd, struct cfq_io_context *cic, + struct bfq_queue *bfqq) +{ + bfq_log_bfqq(bfqd, bfqq, "merging with queue %lu", + bfqq->new_bfqq->pid); + cic_set_bfqq(cic, bfqq->new_bfqq, 1); + bfq_mark_bfqq_coop(bfqq->new_bfqq); + bfq_put_queue(bfqq); + return cic_to_bfqq(cic, 1); +} + +/* + * Returns NULL if a new bfqq should be allocated, or the old bfqq if this + * was the last process referring to said bfqq. + */ +static struct bfq_queue * +bfq_split_bfqq(struct cfq_io_context *cic, struct bfq_queue *bfqq) +{ + bfq_log_bfqq(bfqq->bfqd, bfqq, "splitting queue"); + if (bfqq_process_refs(bfqq) == 1) { + bfqq->pid = current->pid; + bfq_clear_bfqq_some_coop_idle(bfqq); + bfq_clear_bfqq_coop(bfqq); + bfq_clear_bfqq_split_coop(bfqq); + return bfqq; + } + + cic_set_bfqq(cic, NULL, 1); + + bfq_put_cooperator(bfqq); + + bfq_put_queue(bfqq); + return NULL; +} + /* * Allocate bfq data structures associated with this request. */ @@ -1872,13 +2393,30 @@ static int bfq_set_request(struct request_queue *q, struct request *rq, bfqg = bfq_cic_update_cgroup(cic); +new_queue: bfqq = cic_to_bfqq(cic, is_sync); - if (bfqq == NULL) { + if (bfqq == NULL || bfqq == &bfqd->oom_bfqq) { bfqq = bfq_get_queue(bfqd, bfqg, is_sync, cic->ioc, gfp_mask); - if (bfqq == NULL) - goto queue_fail; - cic_set_bfqq(cic, bfqq, is_sync); + } else { + /* + * If the queue was seeky for too long, break it apart. + */ + if (bfq_bfqq_coop(bfqq) && bfq_bfqq_split_coop(bfqq)) { + bfq_log_bfqq(bfqd, bfqq, "breaking apart bfqq"); + bfqq = bfq_split_bfqq(cic, bfqq); + if (!bfqq) + goto new_queue; + } + + /* + * Check to see if this queue is scheduled to merge with + * another closely cooperating queue. The merging of queues + * happens here as it must be done in process context. + * The reference on new_bfqq was taken in merge_bfqqs. + */ + if (bfqq->new_bfqq != NULL) + bfqq = bfq_merge_bfqqs(bfqd, cic, bfqq); } bfqq->allocated[rw]++; @@ -1907,11 +2445,10 @@ static void bfq_kick_queue(struct work_struct *work) struct bfq_data *bfqd = container_of(work, struct bfq_data, unplug_work); struct request_queue *q = bfqd->queue; - unsigned long flags; - spin_lock_irqsave(q->queue_lock, flags); + spin_lock_irq(q->queue_lock); __blk_run_queue(q, false); - spin_unlock_irqrestore(q->queue_lock, flags); + spin_unlock_irq(q->queue_lock); } /* @@ -1938,7 +2475,6 @@ static void bfq_idle_slice_timer(unsigned long data) */ if (bfqq != NULL) { bfq_log_bfqq(bfqd, bfqq, "slice_timer expired"); - reason = BFQ_BFQQ_TOO_IDLE; if (bfq_bfqq_budget_timeout(bfqq)) /* * Also here the queue can be safely expired @@ -1946,10 +2482,21 @@ static void bfq_idle_slice_timer(unsigned long data) * guarantees */ reason = BFQ_BFQQ_BUDGET_TIMEOUT; + else if (bfqq->queued[0] == 0 && bfqq->queued[1] == 0) + /* + * The queue may not be empty upon timer expiration, + * because we may not disable the timer when the first + * request of the active queue arrives during + * disk idling + */ + reason = BFQ_BFQQ_TOO_IDLE; + else + goto schedule_dispatch; bfq_bfqq_expire(bfqd, bfqq, 1, reason); } +schedule_dispatch: bfq_schedule_dispatch(bfqd); spin_unlock_irqrestore(bfqd->queue->queue_lock, flags); @@ -2067,6 +2614,14 @@ static void *bfq_init_queue(struct request_queue *q) bfqd->cic_index = i; + /* + * Our fallback bfqq if bfq_find_alloc_queue() runs into OOM issues. + * Grab a permanent reference to it, so that the normal code flow + * will not attempt to free it. + */ + bfq_init_bfqq(bfqd, &bfqd->oom_bfqq, 1, 0); + atomic_inc(&bfqd->oom_bfqq.ref); + INIT_LIST_HEAD(&bfqd->cic_list); bfqd->queue = q; @@ -2083,6 +2638,8 @@ static void *bfq_init_queue(struct request_queue *q) bfqd->idle_slice_timer.function = bfq_idle_slice_timer; bfqd->idle_slice_timer.data = (unsigned long)bfqd; + bfqd->rq_pos_tree = RB_ROOT; + INIT_WORK(&bfqd->unplug_work, bfq_kick_queue); INIT_LIST_HEAD(&bfqd->active_list); @@ -2106,7 +2663,8 @@ static void *bfq_init_queue(struct request_queue *q) bfqd->low_latency = true; bfqd->bfq_raising_coeff = 20; - bfqd->bfq_raising_max_time = msecs_to_jiffies(8000); + bfqd->bfq_raising_rt_max_time = msecs_to_jiffies(300); + bfqd->bfq_raising_max_time = msecs_to_jiffies(7500); bfqd->bfq_raising_min_idle_time = msecs_to_jiffies(2000); bfqd->bfq_raising_max_softrt_rate = 7000; @@ -2158,22 +2716,30 @@ static ssize_t bfq_var_store(unsigned long *var, const char *page, size_t count) static ssize_t bfq_weights_show(struct elevator_queue *e, char *page) { - struct bfq_queue *bfqq; + struct bfq_queue *bfqq; struct bfq_data *bfqd = e->elevator_data; ssize_t num_char = 0; - num_char += sprintf(page + num_char, "Active:\n"); - list_for_each_entry(bfqq, &bfqd->active_list, bfqq_list) { - num_char += sprintf(page + num_char, "pid%d: weight %hu\n", + num_char += sprintf(page + num_char, "Active:\n"); + list_for_each_entry(bfqq, &bfqd->active_list, bfqq_list) { + num_char += sprintf(page + num_char, + "pid%d: weight %hu, dur %d/%u\n", bfqq->pid, - bfqq->entity.weight); - } - num_char += sprintf(page + num_char, "Idle:\n"); - list_for_each_entry(bfqq, &bfqd->idle_list, bfqq_list) { - num_char += sprintf(page + num_char, "pid%d: weight %hu\n", - bfqq->pid, - bfqq->entity.weight); - } + bfqq->entity.weight, + jiffies_to_msecs(jiffies - + bfqq->last_rais_start_finish), + jiffies_to_msecs(bfqq->raising_cur_max_time)); + } + num_char += sprintf(page + num_char, "Idle:\n"); + list_for_each_entry(bfqq, &bfqd->idle_list, bfqq_list) { + num_char += sprintf(page + num_char, + "pid%d: weight %hu, dur %d/%u\n", + bfqq->pid, + bfqq->entity.weight, + jiffies_to_msecs(jiffies - + bfqq->last_rais_start_finish), + jiffies_to_msecs(bfqq->raising_cur_max_time)); + } return num_char; } @@ -2199,6 +2765,7 @@ SHOW_FUNCTION(bfq_timeout_async_show, bfqd->bfq_timeout[BLK_RW_ASYNC], 1); SHOW_FUNCTION(bfq_low_latency_show, bfqd->low_latency, 0); SHOW_FUNCTION(bfq_raising_coeff_show, bfqd->bfq_raising_coeff, 0); SHOW_FUNCTION(bfq_raising_max_time_show, bfqd->bfq_raising_max_time, 1); +SHOW_FUNCTION(bfq_raising_rt_max_time_show, bfqd->bfq_raising_rt_max_time, 1); SHOW_FUNCTION(bfq_raising_min_idle_time_show, bfqd->bfq_raising_min_idle_time, 1); SHOW_FUNCTION(bfq_raising_max_softrt_rate_show, @@ -2239,6 +2806,8 @@ STORE_FUNCTION(bfq_raising_coeff_store, &bfqd->bfq_raising_coeff, 1, INT_MAX, 0); STORE_FUNCTION(bfq_raising_max_time_store, &bfqd->bfq_raising_max_time, 0, INT_MAX, 1); +STORE_FUNCTION(bfq_raising_rt_max_time_store, &bfqd->bfq_raising_rt_max_time, 0, + INT_MAX, 1); STORE_FUNCTION(bfq_raising_min_idle_time_store, &bfqd->bfq_raising_min_idle_time, 0, INT_MAX, 1); STORE_FUNCTION(bfq_raising_max_softrt_rate_store, @@ -2332,6 +2901,7 @@ static struct elv_fs_entry bfq_attrs[] = { BFQ_ATTR(low_latency), BFQ_ATTR(raising_coeff), BFQ_ATTR(raising_max_time), + BFQ_ATTR(raising_rt_max_time), BFQ_ATTR(raising_min_idle_time), BFQ_ATTR(raising_max_softrt_rate), BFQ_ATTR(weights), diff --git a/block/bfq-sched.c b/block/bfq-sched.c index 64b23ef4caed6..ea26d05fe1f03 100644 --- a/block/bfq-sched.c +++ b/block/bfq-sched.c @@ -944,6 +944,33 @@ static struct bfq_queue *bfq_get_next_queue(struct bfq_data *bfqd) return bfqq; } +/* + * Forced extraction of the given queue. + */ +static void bfq_get_next_queue_forced(struct bfq_data *bfqd, + struct bfq_queue *bfqq) +{ + struct bfq_entity *entity; + struct bfq_sched_data *sd; + + BUG_ON(bfqd->active_queue != NULL); + + entity = &bfqq->entity; + /* + * Bubble up extraction/update from the leaf to the root. + */ + for_each_entity(entity) { + sd = entity->sched_data; + bfq_update_vtime(bfq_entity_service_tree(entity)); + bfq_active_extract(bfq_entity_service_tree(entity), entity); + sd->active_entity = entity; + sd->next_active = NULL; + entity->service = 0; + } + + return; +} + static void __bfq_bfqd_reset_active(struct bfq_data *bfqd) { if (bfqd->active_cic != NULL) { diff --git a/block/bfq.h b/block/bfq.h index 8435821a666e1..0e8ac689af0a2 100644 --- a/block/bfq.h +++ b/block/bfq.h @@ -1,5 +1,5 @@ /* - * BFQ-v2 for 2.6.37: data structures and common functions prototypes. + * BFQ-v3r2 for 2.6.38: data structures and common functions prototypes. * * Based on ideas and code from CFQ: * Copyright (C) 2003 Jens Axboe @@ -158,10 +158,93 @@ struct bfq_entity { struct bfq_group; +/** + * struct bfq_queue - leaf schedulable entity. + * @ref: reference counter. + * @bfqd: parent bfq_data. + * @new_bfqq: shared bfq_queue if queue is cooperating with + * one or more other queues. + * @pos_node: request-position tree member (see bfq_data's @rq_pos_tree). + * @pos_root: request-position tree root (see bfq_data's @rq_pos_tree). + * @sort_list: sorted list of pending requests. + * @next_rq: if fifo isn't expired, next request to serve. + * @queued: nr of requests queued in @sort_list. + * @allocated: currently allocated requests. + * @meta_pending: pending metadata requests. + * @fifo: fifo list of requests in sort_list. + * @entity: entity representing this queue in the scheduler. + * @max_budget: maximum budget allowed from the feedback mechanism. + * @budget_timeout: budget expiration (in jiffies). + * @dispatched: number of requests on the dispatch list or inside driver. + * @org_ioprio: saved ioprio during boosted periods. + * @org_ioprio_class: saved ioprio_class during boosted periods. + * @flags: status flags. + * @bfqq_list: node for active/idle bfqq list inside our bfqd. + * @seek_samples: number of seeks sampled + * @seek_total: sum of the distances of the seeks sampled + * @seek_mean: mean seek distance + * @last_request_pos: position of the last request enqueued + * @pid: pid of the process owning the queue, used for logging purposes. + * @last_rais_start_time: last (idle -> weight-raised) transition attempt + * @raising_cur_max_time: current max raising time for this queue + * + * A bfq_queue is a leaf request queue; it can be associated to an io_context + * or more (if it is an async one). @cgroup holds a reference to the + * cgroup, to be sure that it does not disappear while a bfqq still + * references it (mostly to avoid races between request issuing and task + * migration followed by cgroup distruction). + * All the fields are protected by the queue lock of the containing bfqd. + */ +struct bfq_queue { + atomic_t ref; + struct bfq_data *bfqd; + + /* fields for cooperating queues handling */ + struct bfq_queue *new_bfqq; + struct rb_node pos_node; + struct rb_root *pos_root; + + struct rb_root sort_list; + struct request *next_rq; + int queued[2]; + int allocated[2]; + int meta_pending; + struct list_head fifo; + + struct bfq_entity entity; + + unsigned long max_budget; + unsigned long budget_timeout; + + int dispatched; + + unsigned short org_ioprio; + unsigned short org_ioprio_class; + + unsigned int flags; + + struct list_head bfqq_list; + + unsigned int seek_samples; + u64 seek_total; + sector_t seek_mean; + sector_t last_request_pos; + + pid_t pid; + + /* weight-raising fields */ + unsigned int raising_cur_max_time; + u64 last_rais_start_finish, soft_rt_next_start; + unsigned int raising_coeff; +}; + /** * struct bfq_data - per device data structure. * @queue: request queue for the managed device. * @root_group: root bfq_group for the device. + * @rq_pos_tree: rbtree sorted by next_request position, + * used when determining if two or more queues + * have interleaving requests (see bfq_close_cooperator). * @busy_queues: number of bfq_queues containing requests (including the * queue under service, even if it is idling). * @queued: number of queued requests. @@ -207,10 +290,12 @@ struct bfq_group; * @bfq_raising_coeff: Maximum factor by which the weight of a boosted * queue is multiplied * @bfq_raising_max_time: maximum duration of a weight-raising period (jiffies) + * @bfq_raising_rt_max_time: maximum duration for soft real-time processes * @bfq_raising_min_idle_time: minimum idle period after which weight-raising * may be reactivated for a queue (in jiffies) * @bfq_raising_max_softrt_rate: max service-rate for a soft real-time queue, * sectors per seconds + * @oom_bfqq: fallback dummy bfqq for extreme OOM conditions * * All the fields are protected by the @queue lock. */ @@ -219,6 +304,8 @@ struct bfq_data { struct bfq_group *root_group; + struct rb_root rq_pos_tree; + int busy_queues; int queued; int rq_in_driver; @@ -266,78 +353,11 @@ struct bfq_data { /* parameters of the low_latency heuristics */ unsigned int bfq_raising_coeff; unsigned int bfq_raising_max_time; + unsigned int bfq_raising_rt_max_time; unsigned int bfq_raising_min_idle_time; unsigned int bfq_raising_max_softrt_rate; -}; - -/** - * struct bfq_queue - leaf schedulable entity. - * @ref: reference counter. - * @bfqd: parent bfq_data. - * @sort_list: sorted list of pending requests. - * @next_rq: if fifo isn't expired, next request to serve. - * @queued: nr of requests queued in @sort_list. - * @allocated: currently allocated requests. - * @meta_pending: pending metadata requests. - * @fifo: fifo list of requests in sort_list. - * @entity: entity representing this queue in the scheduler. - * @max_budget: maximum budget allowed from the feedback mechanism. - * @budget_timeout: budget expiration (in jiffies). - * @dispatched: number of requests on the dispatch list or inside driver. - * @org_ioprio: saved ioprio during boosted periods. - * @org_ioprio_class: saved ioprio_class during boosted periods. - * @flags: status flags. - * @bfqq_list: node for active/idle bfqq list inside our bfqd. - * @seek_samples: number of seeks sampled - * @seek_total: sum of the distances of the seeks sampled - * @seek_mean: mean seek distance - * @last_request_pos: position of the last request enqueued - * @pid: pid of the process owning the queue, used for logging purposes. - * @last_rais_start_time: last (idle -> weight-raised) transition attempt - * @high_weight_budget: number of sectors left to serve with boosted weight - * - * A bfq_queue is a leaf request queue; it can be associated to an io_context - * or more (if it is an async one). @cgroup holds a reference to the - * cgroup, to be sure that it does not disappear while a bfqq still - * references it (mostly to avoid races between request issuing and task - * migration followed by cgroup distruction). - * All the fields are protected by the queue lock of the containing bfqd. - */ -struct bfq_queue { - atomic_t ref; - struct bfq_data *bfqd; - - struct rb_root sort_list; - struct request *next_rq; - int queued[2]; - int allocated[2]; - int meta_pending; - struct list_head fifo; - - struct bfq_entity entity; - - unsigned long max_budget; - unsigned long budget_timeout; - - int dispatched; - unsigned short org_ioprio; - unsigned short org_ioprio_class; - - unsigned int flags; - - struct list_head bfqq_list; - - unsigned int seek_samples; - u64 seek_total; - sector_t seek_mean; - sector_t last_request_pos; - - pid_t pid; - - /* weight-raising fileds */ - u64 last_rais_start_finish, soft_rt_next_start; - unsigned int raising_coeff; + struct bfq_queue oom_bfqq; }; enum bfqq_state_flags { @@ -349,6 +369,9 @@ enum bfqq_state_flags { BFQ_BFQQ_FLAG_prio_changed, /* task priority has changed */ BFQ_BFQQ_FLAG_sync, /* synchronous queue */ BFQ_BFQQ_FLAG_budget_new, /* no completion with this budget */ + BFQ_BFQQ_FLAG_coop, /* bfqq is shared */ + BFQ_BFQQ_FLAG_split_coop, /* shared bfqq will be splitted */ + BFQ_BFQQ_FLAG_some_coop_idle, /* some cooperator is inactive */ }; #define BFQ_BFQQ_FNS(name) \ @@ -373,6 +396,9 @@ BFQ_BFQQ_FNS(idle_window); BFQ_BFQQ_FNS(prio_changed); BFQ_BFQQ_FNS(sync); BFQ_BFQQ_FNS(budget_new); +BFQ_BFQQ_FNS(coop); +BFQ_BFQQ_FNS(split_coop); +BFQ_BFQQ_FNS(some_coop_idle); #undef BFQ_BFQQ_FNS /* Logging facilities. */ From 15e2b8d645b31e223d93c80f76332d4b051735c3 Mon Sep 17 00:00:00 2001 From: Michel Lespinasse Date: Thu, 10 Mar 2011 18:47:31 -0800 Subject: [PATCH 2241/2556] futex: Remove redundant pagefault_disable in futex_atomic_cmpxchg_inatomic() kernel/futex.c disables page faults before calling futex_atomic_cmpxchg_inatomic(), so there is no need to do it again within that function. Change-Id: I91ee5ba156c3a4581dd7c7ffbcba87b0a7885573 Signed-off-by: Michel Lespinasse Cc: Darren Hart Cc: Peter Zijlstra Cc: Matt Turner Cc: Russell King Cc: David Howells Cc: Tony Luck Cc: Michal Simek Cc: Ralf Baechle Cc: "James E.J. Bottomley" Cc: Benjamin Herrenschmidt Cc: Martin Schwidefsky Cc: Paul Mundt Cc: "David S. Miller" Cc: Chris Metcalf Cc: Linus Torvalds LKML-Reference: <20110311024731.GB26122@google.com> Signed-off-by: Thomas Gleixner Signed-off-by: Jin Hong --- arch/arm/include/asm/futex.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm/include/asm/futex.h b/arch/arm/include/asm/futex.h index b33fe7065b386..7133a86208309 100644 --- a/arch/arm/include/asm/futex.h +++ b/arch/arm/include/asm/futex.h @@ -95,7 +95,8 @@ futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) return -EFAULT; - pagefault_disable(); /* implies preempt_disable() */ + /* Note that preemption is disabled by futex_atomic_cmpxchg_inatomic + * call sites. */ __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" "1: " T(ldr) " %0, [%3]\n" @@ -115,8 +116,6 @@ futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) : "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT) : "cc", "memory"); - pagefault_enable(); /* subsumes preempt_enable() */ - return val; } From 86ed99695263a68b64753e2a9e03eae518f4bb3c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 11 Mar 2011 11:51:22 +0100 Subject: [PATCH 2242/2556] futex: Avoid redudant evaluation of task_pid_vnr() The result is not going to change under us, so no need to reevaluate this over and over. Seems to be a leftover from the mechanical mass conversion of task->pid to task_pid_vnr(tsk). Change-Id: Ia8fc21c4722a352a70dcf0c08897d7812b5e260a Signed-off-by: Thomas Gleixner Signed-off-by: Jin Hong --- kernel/futex.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/kernel/futex.c b/kernel/futex.c index 3464fa3b0968b..3b4cacb73c763 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -706,7 +706,7 @@ static int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb, struct task_struct *task, int set_waiters) { int lock_taken, ret, ownerdied = 0; - u32 uval, newval, curval; + u32 uval, newval, curval, vpid = task_pid_vnr(task); retry: ret = lock_taken = 0; @@ -716,7 +716,7 @@ static int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb, * (by doing a 0 -> TID atomic cmpxchg), while holding all * the locks. It will most likely not succeed. */ - newval = task_pid_vnr(task); + newval = vpid; if (set_waiters) newval |= FUTEX_WAITERS; @@ -728,7 +728,7 @@ static int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb, /* * Detect deadlocks. */ - if ((unlikely((curval & FUTEX_TID_MASK) == task_pid_vnr(task)))) + if ((unlikely((curval & FUTEX_TID_MASK) == vpid))) return -EDEADLK; /* @@ -755,7 +755,7 @@ static int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb, */ if (unlikely(ownerdied || !(curval & FUTEX_TID_MASK))) { /* Keep the OWNER_DIED bit */ - newval = (curval & ~FUTEX_TID_MASK) | task_pid_vnr(task); + newval = (curval & ~FUTEX_TID_MASK) | vpid; ownerdied = 0; lock_taken = 1; } @@ -2078,9 +2078,9 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) { struct futex_hash_bucket *hb; struct futex_q *this, *next; - u32 uval; struct plist_head *head; union futex_key key = FUTEX_KEY_INIT; + u32 uval, vpid = task_pid_vnr(current); int ret; retry: @@ -2089,7 +2089,7 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) /* * We release only a lock we actually own: */ - if ((uval & FUTEX_TID_MASK) != task_pid_vnr(current)) + if ((uval & FUTEX_TID_MASK) != vpid) return -EPERM; ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key); @@ -2105,7 +2105,7 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) * anyone else up: */ if (!(uval & FUTEX_OWNER_DIED)) - uval = cmpxchg_futex_value_locked(uaddr, task_pid_vnr(current), 0); + uval = cmpxchg_futex_value_locked(uaddr, vpid, 0); if (unlikely(uval == -EFAULT)) @@ -2114,7 +2114,7 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) * Rare case: we managed to release the lock atomically, * no need to wake anyone else up: */ - if (unlikely(uval == task_pid_vnr(current))) + if (unlikely(uval == vpid)) goto out_unlock; /* From c51ce0a4662f47a24f4d2f1f4604e8462beebe60 Mon Sep 17 00:00:00 2001 From: Michel Lespinasse Date: Thu, 10 Mar 2011 18:48:51 -0800 Subject: [PATCH 2243/2556] futex: Sanitize cmpxchg_futex_value_locked API The cmpxchg_futex_value_locked API was funny in that it returned either the original, user-exposed futex value OR an error code such as -EFAULT. This was confusing at best, and could be a source of livelocks in places that retry the cmpxchg_futex_value_locked after trying to fix the issue by running fault_in_user_writeable(). This change makes the cmpxchg_futex_value_locked API more similar to the get_futex_value_locked one, returning an error code and updating the original value through a reference argument. Change-Id: Id063da82c806405efdd21c1d80db1197c6982513 Signed-off-by: Michel Lespinasse Acked-by: Chris Metcalf [tile] Acked-by: Tony Luck [ia64] Acked-by: Thomas Gleixner Tested-by: Michal Simek [microblaze] Acked-by: David Howells [frv] Cc: Darren Hart Cc: Peter Zijlstra Cc: Matt Turner Cc: Russell King Cc: Ralf Baechle Cc: "James E.J. Bottomley" Cc: Benjamin Herrenschmidt Cc: Martin Schwidefsky Cc: Paul Mundt Cc: "David S. Miller" Cc: Linus Torvalds LKML-Reference: <20110311024851.GC26122@google.com> Signed-off-by: Thomas Gleixner Signed-off-by: Jin Hong --- arch/alpha/include/asm/futex.h | 22 +++++++------- arch/arm/include/asm/futex.h | 18 +++++++----- arch/frv/include/asm/futex.h | 3 +- arch/ia64/include/asm/futex.h | 9 ++++-- arch/microblaze/include/asm/futex.h | 24 ++++++++------- arch/mips/include/asm/futex.h | 32 ++++++++++---------- arch/parisc/include/asm/futex.h | 18 ++++++------ arch/powerpc/include/asm/futex.h | 20 +++++++------ arch/s390/include/asm/futex.h | 4 +-- arch/s390/include/asm/uaccess.h | 2 +- arch/s390/lib/uaccess.h | 4 +-- arch/s390/lib/uaccess_pt.c | 13 +++++---- arch/s390/lib/uaccess_std.c | 6 ++-- arch/sh/include/asm/futex-irq.h | 9 +++--- arch/sh/include/asm/futex.h | 5 ++-- arch/sparc/include/asm/futex_64.h | 16 ++++++---- arch/tile/include/asm/futex.h | 7 +++-- arch/x86/include/asm/futex.h | 16 +++++----- include/asm-generic/futex.h | 3 +- kernel/futex.c | 45 ++++++++++------------------- 20 files changed, 144 insertions(+), 132 deletions(-) diff --git a/arch/alpha/include/asm/futex.h b/arch/alpha/include/asm/futex.h index 945de222ab91a..c4e5c2850cce1 100644 --- a/arch/alpha/include/asm/futex.h +++ b/arch/alpha/include/asm/futex.h @@ -81,21 +81,22 @@ static inline int futex_atomic_op_inuser (int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) +futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, + int oldval, int newval) { - int prev, cmp; + int ret = 0, prev, cmp; if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) return -EFAULT; __asm__ __volatile__ ( __ASM_SMP_MB - "1: ldl_l %0,0(%2)\n" - " cmpeq %0,%3,%1\n" - " beq %1,3f\n" - " mov %4,%1\n" - "2: stl_c %1,0(%2)\n" - " beq %1,4f\n" + "1: ldl_l %1,0(%3)\n" + " cmpeq %1,%4,%2\n" + " beq %2,3f\n" + " mov %5,%2\n" + "2: stl_c %2,0(%3)\n" + " beq %2,4f\n" "3: .subsection 2\n" "4: br 1b\n" " .previous\n" @@ -105,11 +106,12 @@ futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) " .long 2b-.\n" " lda $31,3b-2b(%0)\n" " .previous\n" - : "=&r"(prev), "=&r"(cmp) + : "+r"(ret), "=&r"(prev), "=&r"(cmp) : "r"(uaddr), "r"((long)oldval), "r"(newval) : "memory"); - return prev; + *uval = prev; + return ret; } #endif /* __KERNEL__ */ diff --git a/arch/arm/include/asm/futex.h b/arch/arm/include/asm/futex.h index 7133a86208309..d20b78fce758f 100644 --- a/arch/arm/include/asm/futex.h +++ b/arch/arm/include/asm/futex.h @@ -88,9 +88,10 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) +futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, + int oldval, int newval) { - int val; + int ret = 0, val; if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) return -EFAULT; @@ -99,24 +100,25 @@ futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) * call sites. */ __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" - "1: " T(ldr) " %0, [%3]\n" - " teq %0, %1\n" + "1: " T(ldr) " %1, [%4]\n" + " teq %1, %2\n" " it eq @ explicit IT needed for the 2b label\n" - "2: " T(streq) " %2, [%3]\n" + "2: " T(streq) " %3, [%4]\n" "3:\n" " .pushsection __ex_table,\"a\"\n" " .align 3\n" " .long 1b, 4f, 2b, 4f\n" " .popsection\n" " .pushsection .fixup,\"ax\"\n" - "4: mov %0, %4\n" + "4: mov %0, %5\n" " b 3b\n" " .popsection" - : "=&r" (val) + : "+r" (ret), "=&r" (val) : "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT) : "cc", "memory"); - return val; + *uval = val; + return ret; } #endif /* !SMP */ diff --git a/arch/frv/include/asm/futex.h b/arch/frv/include/asm/futex.h index 08b3d1da35839..0548f8e4d11ed 100644 --- a/arch/frv/include/asm/futex.h +++ b/arch/frv/include/asm/futex.h @@ -10,7 +10,8 @@ extern int futex_atomic_op_inuser(int encoded_op, int __user *uaddr); static inline int -futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) +futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, + int oldval, int newval) { return -ENOSYS; } diff --git a/arch/ia64/include/asm/futex.h b/arch/ia64/include/asm/futex.h index c7f0f062239cd..b0728404dad05 100644 --- a/arch/ia64/include/asm/futex.h +++ b/arch/ia64/include/asm/futex.h @@ -100,23 +100,26 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) +futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, + int oldval, int newval) { if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) return -EFAULT; { - register unsigned long r8 __asm ("r8"); + register unsigned long r8 __asm ("r8") = 0; + unsigned long prev; __asm__ __volatile__( " mf;; \n" " mov ar.ccv=%3;; \n" "[1:] cmpxchg4.acq %0=[%1],%2,ar.ccv \n" " .xdata4 \"__ex_table\", 1b-., 2f-. \n" "[2:]" - : "=r" (r8) + : "=r" (prev) : "r" (uaddr), "r" (newval), "rO" ((long) (unsigned) oldval) : "memory"); + *uval = prev; return r8; } } diff --git a/arch/microblaze/include/asm/futex.h b/arch/microblaze/include/asm/futex.h index ad3fd61b2fe7e..fa019ed65dfb3 100644 --- a/arch/microblaze/include/asm/futex.h +++ b/arch/microblaze/include/asm/futex.h @@ -94,31 +94,33 @@ futex_atomic_op_inuser(int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) +futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, + int oldval, int newval) { - int prev, cmp; + int ret = 0, prev, cmp; if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) return -EFAULT; - __asm__ __volatile__ ("1: lwx %0, %2, r0; \ - cmp %1, %0, %3; \ - beqi %1, 3f; \ - 2: swx %4, %2, r0; \ - addic %1, r0, 0; \ - bnei %1, 1b; \ + __asm__ __volatile__ ("1: lwx %1, %3, r0; \ + cmp %2, %1, %4; \ + beqi %2, 3f; \ + 2: swx %5, %3, r0; \ + addic %2, r0, 0; \ + bnei %2, 1b; \ 3: \ .section .fixup,\"ax\"; \ 4: brid 3b; \ - addik %0, r0, %5; \ + addik %0, r0, %6; \ .previous; \ .section __ex_table,\"a\"; \ .word 1b,4b,2b,4b; \ .previous;" \ - : "=&r" (prev), "=&r"(cmp) \ + : "+r" (ret), "=&r" (prev), "=&r"(cmp) \ : "r" (uaddr), "r" (oldval), "r" (newval), "i" (-EFAULT)); - return prev; + *uval = prev; + return ret; } #endif /* __KERNEL__ */ diff --git a/arch/mips/include/asm/futex.h b/arch/mips/include/asm/futex.h index b9cce90346cfc..692a24bd83b7a 100644 --- a/arch/mips/include/asm/futex.h +++ b/arch/mips/include/asm/futex.h @@ -132,9 +132,10 @@ futex_atomic_op_inuser(int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) +futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, + int oldval, int newval) { - int retval; + int ret = 0, val; if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) return -EFAULT; @@ -145,25 +146,25 @@ futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) " .set push \n" " .set noat \n" " .set mips3 \n" - "1: ll %0, %2 \n" - " bne %0, %z3, 3f \n" + "1: ll %1, %3 \n" + " bne %1, %z4, 3f \n" " .set mips0 \n" - " move $1, %z4 \n" + " move $1, %z5 \n" " .set mips3 \n" - "2: sc $1, %1 \n" + "2: sc $1, %2 \n" " beqzl $1, 1b \n" __WEAK_LLSC_MB "3: \n" " .set pop \n" " .section .fixup,\"ax\" \n" - "4: li %0, %5 \n" + "4: li %0, %6 \n" " j 3b \n" " .previous \n" " .section __ex_table,\"a\" \n" " "__UA_ADDR "\t1b, 4b \n" " "__UA_ADDR "\t2b, 4b \n" " .previous \n" - : "=&r" (retval), "=R" (*uaddr) + : "+r" (ret), "=&r" (val), "=R" (*uaddr) : "R" (*uaddr), "Jr" (oldval), "Jr" (newval), "i" (-EFAULT) : "memory"); } else if (cpu_has_llsc) { @@ -172,31 +173,32 @@ futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) " .set push \n" " .set noat \n" " .set mips3 \n" - "1: ll %0, %2 \n" - " bne %0, %z3, 3f \n" + "1: ll %1, %3 \n" + " bne %1, %z4, 3f \n" " .set mips0 \n" - " move $1, %z4 \n" + " move $1, %z5 \n" " .set mips3 \n" - "2: sc $1, %1 \n" + "2: sc $1, %2 \n" " beqz $1, 1b \n" __WEAK_LLSC_MB "3: \n" " .set pop \n" " .section .fixup,\"ax\" \n" - "4: li %0, %5 \n" + "4: li %0, %6 \n" " j 3b \n" " .previous \n" " .section __ex_table,\"a\" \n" " "__UA_ADDR "\t1b, 4b \n" " "__UA_ADDR "\t2b, 4b \n" " .previous \n" - : "=&r" (retval), "=R" (*uaddr) + : "+r" (ret), "=&r" (val), "=R" (*uaddr) : "R" (*uaddr), "Jr" (oldval), "Jr" (newval), "i" (-EFAULT) : "memory"); } else return -ENOSYS; - return retval; + *uval = val; + return ret; } #endif diff --git a/arch/parisc/include/asm/futex.h b/arch/parisc/include/asm/futex.h index 0c705c3a55efc..4c6d8672325b8 100644 --- a/arch/parisc/include/asm/futex.h +++ b/arch/parisc/include/asm/futex.h @@ -51,10 +51,10 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) /* Non-atomic version */ static inline int -futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) +futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, + int oldval, int newval) { - int err = 0; - int uval; + int val; /* futex.c wants to do a cmpxchg_inatomic on kernel NULL, which is * our gateway page, and causes no end of trouble... @@ -65,12 +65,12 @@ futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) return -EFAULT; - err = get_user(uval, uaddr); - if (err) return -EFAULT; - if (uval == oldval) - err = put_user(newval, uaddr); - if (err) return -EFAULT; - return uval; + if (get_user(val, uaddr)) + return -EFAULT; + if (val == oldval && put_user(newval, uaddr)) + return -EFAULT; + *uval = val; + return 0; } #endif /*__KERNEL__*/ diff --git a/arch/powerpc/include/asm/futex.h b/arch/powerpc/include/asm/futex.h index 7c589ef81fb0e..631e8da600648 100644 --- a/arch/powerpc/include/asm/futex.h +++ b/arch/powerpc/include/asm/futex.h @@ -82,35 +82,37 @@ static inline int futex_atomic_op_inuser (int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) +futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, + int oldval, int newval) { - int prev; + int ret = 0, prev; if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) return -EFAULT; __asm__ __volatile__ ( PPC_RELEASE_BARRIER -"1: lwarx %0,0,%2 # futex_atomic_cmpxchg_inatomic\n\ - cmpw 0,%0,%3\n\ +"1: lwarx %1,0,%3 # futex_atomic_cmpxchg_inatomic\n\ + cmpw 0,%1,%4\n\ bne- 3f\n" - PPC405_ERR77(0,%2) -"2: stwcx. %4,0,%2\n\ + PPC405_ERR77(0,%3) +"2: stwcx. %5,0,%3\n\ bne- 1b\n" PPC_ACQUIRE_BARRIER "3: .section .fixup,\"ax\"\n\ -4: li %0,%5\n\ +4: li %0,%6\n\ b 3b\n\ .previous\n\ .section __ex_table,\"a\"\n\ .align 3\n\ " PPC_LONG "1b,4b,2b,4b\n\ .previous" \ - : "=&r" (prev), "+m" (*uaddr) + : "+r" (ret), "=&r" (prev), "+m" (*uaddr) : "r" (uaddr), "r" (oldval), "r" (newval), "i" (-EFAULT) : "cc", "memory"); - return prev; + *uval = prev; + return ret; } #endif /* __KERNEL__ */ diff --git a/arch/s390/include/asm/futex.h b/arch/s390/include/asm/futex.h index 5c5d02de49e9a..27ac515ef59c9 100644 --- a/arch/s390/include/asm/futex.h +++ b/arch/s390/include/asm/futex.h @@ -39,13 +39,13 @@ static inline int futex_atomic_op_inuser (int encoded_op, int __user *uaddr) return ret; } -static inline int futex_atomic_cmpxchg_inatomic(int __user *uaddr, +static inline int futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, int oldval, int newval) { if (! access_ok (VERIFY_WRITE, uaddr, sizeof(int))) return -EFAULT; - return uaccess.futex_atomic_cmpxchg(uaddr, oldval, newval); + return uaccess.futex_atomic_cmpxchg(uval, uaddr, oldval, newval); } #endif /* __KERNEL__ */ diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h index d6b1ed0ec52b3..549adf6a9b8b3 100644 --- a/arch/s390/include/asm/uaccess.h +++ b/arch/s390/include/asm/uaccess.h @@ -84,7 +84,7 @@ struct uaccess_ops { size_t (*strnlen_user)(size_t, const char __user *); size_t (*strncpy_from_user)(size_t, const char __user *, char *); int (*futex_atomic_op)(int op, int __user *, int oparg, int *old); - int (*futex_atomic_cmpxchg)(int __user *, int old, int new); + int (*futex_atomic_cmpxchg)(int *, int __user *, int old, int new); }; extern struct uaccess_ops uaccess; diff --git a/arch/s390/lib/uaccess.h b/arch/s390/lib/uaccess.h index 126011df14f1e..89a80674e44b0 100644 --- a/arch/s390/lib/uaccess.h +++ b/arch/s390/lib/uaccess.h @@ -12,12 +12,12 @@ extern size_t copy_from_user_std(size_t, const void __user *, void *); extern size_t copy_to_user_std(size_t, void __user *, const void *); extern size_t strnlen_user_std(size_t, const char __user *); extern size_t strncpy_from_user_std(size_t, const char __user *, char *); -extern int futex_atomic_cmpxchg_std(int __user *, int, int); +extern int futex_atomic_cmpxchg_std(int *, int __user *, int, int); extern int futex_atomic_op_std(int, int __user *, int, int *); extern size_t copy_from_user_pt(size_t, const void __user *, void *); extern size_t copy_to_user_pt(size_t, void __user *, const void *); extern int futex_atomic_op_pt(int, int __user *, int, int *); -extern int futex_atomic_cmpxchg_pt(int __user *, int, int); +extern int futex_atomic_cmpxchg_pt(int *, int __user *, int, int); #endif /* __ARCH_S390_LIB_UACCESS_H */ diff --git a/arch/s390/lib/uaccess_pt.c b/arch/s390/lib/uaccess_pt.c index 404f2de296dca..b3cebcd52f5c1 100644 --- a/arch/s390/lib/uaccess_pt.c +++ b/arch/s390/lib/uaccess_pt.c @@ -354,26 +354,29 @@ int futex_atomic_op_pt(int op, int __user *uaddr, int oparg, int *old) return ret; } -static int __futex_atomic_cmpxchg_pt(int __user *uaddr, int oldval, int newval) +static int __futex_atomic_cmpxchg_pt(int *uval, int __user *uaddr, + int oldval, int newval) { int ret; asm volatile("0: cs %1,%4,0(%5)\n" - "1: lr %0,%1\n" + "1: la %0,0\n" "2:\n" EX_TABLE(0b,2b) EX_TABLE(1b,2b) : "=d" (ret), "+d" (oldval), "=m" (*uaddr) : "0" (-EFAULT), "d" (newval), "a" (uaddr), "m" (*uaddr) : "cc", "memory" ); + *uval = oldval; return ret; } -int futex_atomic_cmpxchg_pt(int __user *uaddr, int oldval, int newval) +int futex_atomic_cmpxchg_pt(int *uval, int __user *uaddr, + int oldval, int newval) { int ret; if (segment_eq(get_fs(), KERNEL_DS)) - return __futex_atomic_cmpxchg_pt(uaddr, oldval, newval); + return __futex_atomic_cmpxchg_pt(uval, uaddr, oldval, newval); spin_lock(¤t->mm->page_table_lock); uaddr = (int __user *) __dat_user_addr((unsigned long) uaddr); if (!uaddr) { @@ -382,7 +385,7 @@ int futex_atomic_cmpxchg_pt(int __user *uaddr, int oldval, int newval) } get_page(virt_to_page(uaddr)); spin_unlock(¤t->mm->page_table_lock); - ret = __futex_atomic_cmpxchg_pt(uaddr, oldval, newval); + ret = __futex_atomic_cmpxchg_pt(uval, uaddr, oldval, newval); put_page(virt_to_page(uaddr)); return ret; } diff --git a/arch/s390/lib/uaccess_std.c b/arch/s390/lib/uaccess_std.c index a6c4f7ed24a49..1d6643c0b95f9 100644 --- a/arch/s390/lib/uaccess_std.c +++ b/arch/s390/lib/uaccess_std.c @@ -287,19 +287,21 @@ int futex_atomic_op_std(int op, int __user *uaddr, int oparg, int *old) return ret; } -int futex_atomic_cmpxchg_std(int __user *uaddr, int oldval, int newval) +int futex_atomic_cmpxchg_std(int *uval, int __user *uaddr, + int oldval, int newval) { int ret; asm volatile( " sacf 256\n" "0: cs %1,%4,0(%5)\n" - "1: lr %0,%1\n" + "1: la %0,0\n" "2: sacf 0\n" EX_TABLE(0b,2b) EX_TABLE(1b,2b) : "=d" (ret), "+d" (oldval), "=m" (*uaddr) : "0" (-EFAULT), "d" (newval), "a" (uaddr), "m" (*uaddr) : "cc", "memory" ); + *uval = oldval; return ret; } diff --git a/arch/sh/include/asm/futex-irq.h b/arch/sh/include/asm/futex-irq.h index a9f16a7f9aeaf..7b701cbd1e84a 100644 --- a/arch/sh/include/asm/futex-irq.h +++ b/arch/sh/include/asm/futex-irq.h @@ -88,7 +88,8 @@ static inline int atomic_futex_op_xchg_xor(int oparg, int __user *uaddr, return ret; } -static inline int atomic_futex_op_cmpxchg_inatomic(int __user *uaddr, +static inline int atomic_futex_op_cmpxchg_inatomic(int *uval, + int __user *uaddr, int oldval, int newval) { unsigned long flags; @@ -102,10 +103,8 @@ static inline int atomic_futex_op_cmpxchg_inatomic(int __user *uaddr, local_irq_restore(flags); - if (ret) - return ret; - - return prev; + *uval = prev; + return ret; } #endif /* __ASM_SH_FUTEX_IRQ_H */ diff --git a/arch/sh/include/asm/futex.h b/arch/sh/include/asm/futex.h index 68256ec5fa35b..a8a5125dc9b43 100644 --- a/arch/sh/include/asm/futex.h +++ b/arch/sh/include/asm/futex.h @@ -65,12 +65,13 @@ static inline int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) +futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, + int oldval, int newval) { if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) return -EFAULT; - return atomic_futex_op_cmpxchg_inatomic(uaddr, oldval, newval); + return atomic_futex_op_cmpxchg_inatomic(uval, uaddr, oldval, newval); } #endif /* __KERNEL__ */ diff --git a/arch/sparc/include/asm/futex_64.h b/arch/sparc/include/asm/futex_64.h index 47f95839dc695..e0862200d6a14 100644 --- a/arch/sparc/include/asm/futex_64.h +++ b/arch/sparc/include/asm/futex_64.h @@ -85,26 +85,30 @@ static inline int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) +futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, + int oldval, int newval) { + int ret = 0; + __asm__ __volatile__( - "\n1: casa [%3] %%asi, %2, %0\n" + "\n1: casa [%4] %%asi, %3, %1\n" "2:\n" " .section .fixup,#alloc,#execinstr\n" " .align 4\n" "3: sethi %%hi(2b), %0\n" " jmpl %0 + %%lo(2b), %%g0\n" - " mov %4, %0\n" + " mov %5, %0\n" " .previous\n" " .section __ex_table,\"a\"\n" " .align 4\n" " .word 1b, 3b\n" " .previous\n" - : "=r" (newval) - : "0" (newval), "r" (oldval), "r" (uaddr), "i" (-EFAULT) + : "+r" (ret), "=r" (newval) + : "1" (newval), "r" (oldval), "r" (uaddr), "i" (-EFAULT) : "memory"); - return newval; + *uval = newval; + return ret; } #endif /* !(_SPARC64_FUTEX_H) */ diff --git a/arch/tile/include/asm/futex.h b/arch/tile/include/asm/futex.h index fe0d10dcae57a..664b20aa2584f 100644 --- a/arch/tile/include/asm/futex.h +++ b/arch/tile/include/asm/futex.h @@ -119,8 +119,8 @@ static inline int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) return ret; } -static inline int futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, - int newval) +static inline int futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, + int oldval, int newval) { struct __get_user asm_ret; @@ -128,7 +128,8 @@ static inline int futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, return -EFAULT; asm_ret = futex_cmpxchg(uaddr, oldval, newval); - return asm_ret.err ? asm_ret.err : asm_ret.val; + *uval = asm_ret.val; + return asm_ret.err; } #ifndef __tilegx__ diff --git a/arch/x86/include/asm/futex.h b/arch/x86/include/asm/futex.h index 1f11ce44e956d..884c0b5676f4b 100644 --- a/arch/x86/include/asm/futex.h +++ b/arch/x86/include/asm/futex.h @@ -109,9 +109,10 @@ static inline int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) return ret; } -static inline int futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, - int newval) +static inline int futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, + int oldval, int newval) { + int ret = 0; #if defined(CONFIG_X86_32) && !defined(CONFIG_X86_BSWAP) /* Real i386 machines have no cmpxchg instruction */ @@ -122,18 +123,19 @@ static inline int futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) return -EFAULT; - asm volatile("1:\t" LOCK_PREFIX "cmpxchgl %3, %1\n" + asm volatile("1:\t" LOCK_PREFIX "cmpxchgl %4, %2\n" "2:\t.section .fixup, \"ax\"\n" - "3:\tmov %2, %0\n" + "3:\tmov %3, %0\n" "\tjmp 2b\n" "\t.previous\n" _ASM_EXTABLE(1b, 3b) - : "=a" (oldval), "+m" (*uaddr) - : "i" (-EFAULT), "r" (newval), "0" (oldval) + : "+r" (ret), "=a" (oldval), "+m" (*uaddr) + : "i" (-EFAULT), "r" (newval), "1" (oldval) : "memory" ); - return oldval; + *uval = oldval; + return ret; } #endif diff --git a/include/asm-generic/futex.h b/include/asm-generic/futex.h index 3c2344f48136d..132bf5227b448 100644 --- a/include/asm-generic/futex.h +++ b/include/asm-generic/futex.h @@ -48,7 +48,8 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) +futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, + int oldval, int newval) { return -ENOSYS; } diff --git a/kernel/futex.c b/kernel/futex.c index 3b4cacb73c763..902fc39067a29 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -413,15 +413,16 @@ static struct futex_q *futex_top_waiter(struct futex_hash_bucket *hb, return NULL; } -static u32 cmpxchg_futex_value_locked(u32 __user *uaddr, u32 uval, u32 newval) +static int cmpxchg_futex_value_locked(u32 *curval, u32 __user *uaddr, + u32 uval, u32 newval) { - u32 curval; + int ret; pagefault_disable(); - curval = futex_atomic_cmpxchg_inatomic(uaddr, uval, newval); + ret = futex_atomic_cmpxchg_inatomic(curval, uaddr, uval, newval); pagefault_enable(); - return curval; + return ret; } static int get_futex_value_locked(u32 *dest, u32 __user *from) @@ -720,9 +721,7 @@ static int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb, if (set_waiters) newval |= FUTEX_WAITERS; - curval = cmpxchg_futex_value_locked(uaddr, 0, newval); - - if (unlikely(curval == -EFAULT)) + if (unlikely(cmpxchg_futex_value_locked(&curval, uaddr, 0, newval))) return -EFAULT; /* @@ -760,9 +759,7 @@ static int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb, lock_taken = 1; } - curval = cmpxchg_futex_value_locked(uaddr, uval, newval); - - if (unlikely(curval == -EFAULT)) + if (unlikely(cmpxchg_futex_value_locked(&curval, uaddr, uval, newval))) return -EFAULT; if (unlikely(curval != uval)) goto retry; @@ -875,9 +872,7 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this) newval = FUTEX_WAITERS | task_pid_vnr(new_owner); - curval = cmpxchg_futex_value_locked(uaddr, uval, newval); - - if (curval == -EFAULT) + if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) ret = -EFAULT; else if (curval != uval) ret = -EINVAL; @@ -912,10 +907,8 @@ static int unlock_futex_pi(u32 __user *uaddr, u32 uval) * There is no waiter, so we unlock the futex. The owner died * bit has not to be preserved here. We are the owner: */ - oldval = cmpxchg_futex_value_locked(uaddr, uval, 0); - - if (oldval == -EFAULT) - return oldval; + if (cmpxchg_futex_value_locked(&oldval, uaddr, uval, 0)) + return -EFAULT; if (oldval != uval) return -EAGAIN; @@ -1610,9 +1603,7 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q, while (1) { newval = (uval & FUTEX_OWNER_DIED) | newtid; - curval = cmpxchg_futex_value_locked(uaddr, uval, newval); - - if (curval == -EFAULT) + if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) goto handle_fault; if (curval == uval) break; @@ -2104,11 +2095,8 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags) * again. If it succeeds then we can return without waking * anyone else up: */ - if (!(uval & FUTEX_OWNER_DIED)) - uval = cmpxchg_futex_value_locked(uaddr, vpid, 0); - - - if (unlikely(uval == -EFAULT)) + if (!(uval & FUTEX_OWNER_DIED) && + cmpxchg_futex_value_locked(&uval, uaddr, vpid, 0)) goto pi_faulted; /* * Rare case: we managed to release the lock atomically, @@ -2495,9 +2483,7 @@ int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, int pi) * userspace. */ mval = (uval & FUTEX_WAITERS) | FUTEX_OWNER_DIED; - nval = futex_atomic_cmpxchg_inatomic(uaddr, uval, mval); - - if (nval == -EFAULT) + if (futex_atomic_cmpxchg_inatomic(&nval, uaddr, uval, mval)) return -1; if (nval != uval) @@ -2710,8 +2696,7 @@ static int __init futex_init(void) * implementation, the non-functional ones will return * -ENOSYS. */ - curval = cmpxchg_futex_value_locked(NULL, 0, 0); - if (curval == -EFAULT) + if (cmpxchg_futex_value_locked(&curval, NULL, 0, 0) == -EFAULT) futex_cmpxchg_enabled = 1; for (i = 0; i < ARRAY_SIZE(futex_queues); i++) { From 3326598b4abddb27d6ff92a2457c33b067c8f31d Mon Sep 17 00:00:00 2001 From: Michel Lespinasse Date: Thu, 10 Mar 2011 18:50:58 -0800 Subject: [PATCH 2244/2556] futex: Sanitize futex ops argument types Change futex_atomic_op_inuser and futex_atomic_cmpxchg_inatomic prototypes to use u32 types for the futex as this is the data type the futex core code uses all over the place. Change-Id: Icf8604d9fa6a6c3ab2562a1d20b88e934036c6ed Signed-off-by: Michel Lespinasse Cc: Darren Hart Cc: Peter Zijlstra Cc: Matt Turner Cc: Russell King Cc: David Howells Cc: Tony Luck Cc: Michal Simek Cc: Ralf Baechle Cc: "James E.J. Bottomley" Cc: Benjamin Herrenschmidt Cc: Martin Schwidefsky Cc: Paul Mundt Cc: "David S. Miller" Cc: Chris Metcalf Cc: Linus Torvalds LKML-Reference: <20110311025058.GD26122@google.com> Signed-off-by: Thomas Gleixner Signed-off-by: Jin Hong --- arch/alpha/include/asm/futex.h | 13 +++++++------ arch/arm/include/asm/futex.h | 13 +++++++------ arch/frv/include/asm/futex.h | 6 +++--- arch/frv/kernel/futex.c | 14 +++++++------- arch/ia64/include/asm/futex.h | 10 +++++----- arch/microblaze/include/asm/futex.h | 13 +++++++------ arch/mips/include/asm/futex.h | 13 +++++++------ arch/parisc/include/asm/futex.h | 12 ++++++------ arch/powerpc/include/asm/futex.h | 13 +++++++------ arch/s390/include/asm/futex.h | 10 +++++----- arch/s390/include/asm/uaccess.h | 4 ++-- arch/s390/lib/uaccess.h | 8 ++++---- arch/s390/lib/uaccess_pt.c | 12 ++++++------ arch/s390/lib/uaccess_std.c | 6 +++--- arch/sh/include/asm/futex-irq.h | 19 ++++++++++--------- arch/sh/include/asm/futex.h | 10 +++++----- arch/sparc/include/asm/futex_64.h | 8 ++++---- arch/tile/include/asm/futex.h | 24 ++++++++++++------------ arch/x86/include/asm/futex.h | 10 +++++----- include/asm-generic/futex.h | 8 ++++---- 20 files changed, 116 insertions(+), 110 deletions(-) diff --git a/arch/alpha/include/asm/futex.h b/arch/alpha/include/asm/futex.h index c4e5c2850cce1..e8a761aee088a 100644 --- a/arch/alpha/include/asm/futex.h +++ b/arch/alpha/include/asm/futex.h @@ -29,7 +29,7 @@ : "r" (uaddr), "r"(oparg) \ : "memory") -static inline int futex_atomic_op_inuser (int encoded_op, int __user *uaddr) +static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) { int op = (encoded_op >> 28) & 7; int cmp = (encoded_op >> 24) & 15; @@ -39,7 +39,7 @@ static inline int futex_atomic_op_inuser (int encoded_op, int __user *uaddr) if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; pagefault_disable(); @@ -81,12 +81,13 @@ static inline int futex_atomic_op_inuser (int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, - int oldval, int newval) +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) { - int ret = 0, prev, cmp; + int ret = 0, cmp; + u32 prev; - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; __asm__ __volatile__ ( diff --git a/arch/arm/include/asm/futex.h b/arch/arm/include/asm/futex.h index d20b78fce758f..0e29d8e6a5c2a 100644 --- a/arch/arm/include/asm/futex.h +++ b/arch/arm/include/asm/futex.h @@ -35,7 +35,7 @@ : "cc", "memory") static inline int -futex_atomic_op_inuser (int encoded_op, int __user *uaddr) +futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) { int op = (encoded_op >> 28) & 7; int cmp = (encoded_op >> 24) & 15; @@ -46,7 +46,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; pagefault_disable(); /* implies preempt_disable() */ @@ -88,12 +88,13 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, - int oldval, int newval) +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) { - int ret = 0, val; + int ret = 0; + u32 val; - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; /* Note that preemption is disabled by futex_atomic_cmpxchg_inatomic diff --git a/arch/frv/include/asm/futex.h b/arch/frv/include/asm/futex.h index 0548f8e4d11ed..4bea27f50a7ab 100644 --- a/arch/frv/include/asm/futex.h +++ b/arch/frv/include/asm/futex.h @@ -7,11 +7,11 @@ #include #include -extern int futex_atomic_op_inuser(int encoded_op, int __user *uaddr); +extern int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr); static inline int -futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, - int oldval, int newval) +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) { return -ENOSYS; } diff --git a/arch/frv/kernel/futex.c b/arch/frv/kernel/futex.c index 14f64b054c7eb..d155ca9e5098c 100644 --- a/arch/frv/kernel/futex.c +++ b/arch/frv/kernel/futex.c @@ -18,7 +18,7 @@ * the various futex operations; MMU fault checking is ignored under no-MMU * conditions */ -static inline int atomic_futex_op_xchg_set(int oparg, int __user *uaddr, int *_oldval) +static inline int atomic_futex_op_xchg_set(int oparg, u32 __user *uaddr, int *_oldval) { int oldval, ret; @@ -50,7 +50,7 @@ static inline int atomic_futex_op_xchg_set(int oparg, int __user *uaddr, int *_o return ret; } -static inline int atomic_futex_op_xchg_add(int oparg, int __user *uaddr, int *_oldval) +static inline int atomic_futex_op_xchg_add(int oparg, u32 __user *uaddr, int *_oldval) { int oldval, ret; @@ -83,7 +83,7 @@ static inline int atomic_futex_op_xchg_add(int oparg, int __user *uaddr, int *_o return ret; } -static inline int atomic_futex_op_xchg_or(int oparg, int __user *uaddr, int *_oldval) +static inline int atomic_futex_op_xchg_or(int oparg, u32 __user *uaddr, int *_oldval) { int oldval, ret; @@ -116,7 +116,7 @@ static inline int atomic_futex_op_xchg_or(int oparg, int __user *uaddr, int *_ol return ret; } -static inline int atomic_futex_op_xchg_and(int oparg, int __user *uaddr, int *_oldval) +static inline int atomic_futex_op_xchg_and(int oparg, u32 __user *uaddr, int *_oldval) { int oldval, ret; @@ -149,7 +149,7 @@ static inline int atomic_futex_op_xchg_and(int oparg, int __user *uaddr, int *_o return ret; } -static inline int atomic_futex_op_xchg_xor(int oparg, int __user *uaddr, int *_oldval) +static inline int atomic_futex_op_xchg_xor(int oparg, u32 __user *uaddr, int *_oldval) { int oldval, ret; @@ -186,7 +186,7 @@ static inline int atomic_futex_op_xchg_xor(int oparg, int __user *uaddr, int *_o /* * do the futex operations */ -int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) +int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) { int op = (encoded_op >> 28) & 7; int cmp = (encoded_op >> 24) & 15; @@ -197,7 +197,7 @@ int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; pagefault_disable(); diff --git a/arch/ia64/include/asm/futex.h b/arch/ia64/include/asm/futex.h index b0728404dad05..8428525ddb225 100644 --- a/arch/ia64/include/asm/futex.h +++ b/arch/ia64/include/asm/futex.h @@ -46,7 +46,7 @@ do { \ } while (0) static inline int -futex_atomic_op_inuser (int encoded_op, int __user *uaddr) +futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) { int op = (encoded_op >> 28) & 7; int cmp = (encoded_op >> 24) & 15; @@ -56,7 +56,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; - if (! access_ok (VERIFY_WRITE, uaddr, sizeof(int))) + if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; pagefault_disable(); @@ -100,10 +100,10 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, - int oldval, int newval) +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) { - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; { diff --git a/arch/microblaze/include/asm/futex.h b/arch/microblaze/include/asm/futex.h index fa019ed65dfb3..b0526d2716fa7 100644 --- a/arch/microblaze/include/asm/futex.h +++ b/arch/microblaze/include/asm/futex.h @@ -29,7 +29,7 @@ }) static inline int -futex_atomic_op_inuser(int encoded_op, int __user *uaddr) +futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) { int op = (encoded_op >> 28) & 7; int cmp = (encoded_op >> 24) & 15; @@ -39,7 +39,7 @@ futex_atomic_op_inuser(int encoded_op, int __user *uaddr) if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; pagefault_disable(); @@ -94,12 +94,13 @@ futex_atomic_op_inuser(int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, - int oldval, int newval) +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) { - int ret = 0, prev, cmp; + int ret = 0, cmp; + u32 prev; - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; __asm__ __volatile__ ("1: lwx %1, %3, r0; \ diff --git a/arch/mips/include/asm/futex.h b/arch/mips/include/asm/futex.h index 692a24bd83b7a..6ebf1734b411b 100644 --- a/arch/mips/include/asm/futex.h +++ b/arch/mips/include/asm/futex.h @@ -75,7 +75,7 @@ } static inline int -futex_atomic_op_inuser(int encoded_op, int __user *uaddr) +futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) { int op = (encoded_op >> 28) & 7; int cmp = (encoded_op >> 24) & 15; @@ -85,7 +85,7 @@ futex_atomic_op_inuser(int encoded_op, int __user *uaddr) if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; - if (! access_ok (VERIFY_WRITE, uaddr, sizeof(int))) + if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; pagefault_disable(); @@ -132,12 +132,13 @@ futex_atomic_op_inuser(int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, - int oldval, int newval) +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) { - int ret = 0, val; + int ret = 0; + u32 val; - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; if (cpu_has_llsc && R10000_LLSC_WAR) { diff --git a/arch/parisc/include/asm/futex.h b/arch/parisc/include/asm/futex.h index 4c6d8672325b8..67a33cc27ef27 100644 --- a/arch/parisc/include/asm/futex.h +++ b/arch/parisc/include/asm/futex.h @@ -8,7 +8,7 @@ #include static inline int -futex_atomic_op_inuser (int encoded_op, int __user *uaddr) +futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) { int op = (encoded_op >> 28) & 7; int cmp = (encoded_op >> 24) & 15; @@ -18,7 +18,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; - if (! access_ok (VERIFY_WRITE, uaddr, sizeof(int))) + if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; pagefault_disable(); @@ -51,10 +51,10 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) /* Non-atomic version */ static inline int -futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, - int oldval, int newval) +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) { - int val; + u32 val; /* futex.c wants to do a cmpxchg_inatomic on kernel NULL, which is * our gateway page, and causes no end of trouble... @@ -62,7 +62,7 @@ futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, if (segment_eq(KERNEL_DS, get_fs()) && !uaddr) return -EFAULT; - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; if (get_user(val, uaddr)) diff --git a/arch/powerpc/include/asm/futex.h b/arch/powerpc/include/asm/futex.h index 631e8da600648..c94e4a3fe2ef3 100644 --- a/arch/powerpc/include/asm/futex.h +++ b/arch/powerpc/include/asm/futex.h @@ -30,7 +30,7 @@ : "b" (uaddr), "i" (-EFAULT), "r" (oparg) \ : "cr0", "memory") -static inline int futex_atomic_op_inuser (int encoded_op, int __user *uaddr) +static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) { int op = (encoded_op >> 28) & 7; int cmp = (encoded_op >> 24) & 15; @@ -40,7 +40,7 @@ static inline int futex_atomic_op_inuser (int encoded_op, int __user *uaddr) if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; - if (! access_ok (VERIFY_WRITE, uaddr, sizeof(int))) + if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; pagefault_disable(); @@ -82,12 +82,13 @@ static inline int futex_atomic_op_inuser (int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, - int oldval, int newval) +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) { - int ret = 0, prev; + int ret = 0; + u32 prev; - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; __asm__ __volatile__ ( diff --git a/arch/s390/include/asm/futex.h b/arch/s390/include/asm/futex.h index 27ac515ef59c9..81cf36b691f1d 100644 --- a/arch/s390/include/asm/futex.h +++ b/arch/s390/include/asm/futex.h @@ -7,7 +7,7 @@ #include #include -static inline int futex_atomic_op_inuser (int encoded_op, int __user *uaddr) +static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) { int op = (encoded_op >> 28) & 7; int cmp = (encoded_op >> 24) & 15; @@ -18,7 +18,7 @@ static inline int futex_atomic_op_inuser (int encoded_op, int __user *uaddr) if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; - if (! access_ok (VERIFY_WRITE, uaddr, sizeof(int))) + if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; pagefault_disable(); @@ -39,10 +39,10 @@ static inline int futex_atomic_op_inuser (int encoded_op, int __user *uaddr) return ret; } -static inline int futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, - int oldval, int newval) +static inline int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) { - if (! access_ok (VERIFY_WRITE, uaddr, sizeof(int))) + if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; return uaccess.futex_atomic_cmpxchg(uval, uaddr, oldval, newval); diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h index 549adf6a9b8b3..2d9ea11f919ad 100644 --- a/arch/s390/include/asm/uaccess.h +++ b/arch/s390/include/asm/uaccess.h @@ -83,8 +83,8 @@ struct uaccess_ops { size_t (*clear_user)(size_t, void __user *); size_t (*strnlen_user)(size_t, const char __user *); size_t (*strncpy_from_user)(size_t, const char __user *, char *); - int (*futex_atomic_op)(int op, int __user *, int oparg, int *old); - int (*futex_atomic_cmpxchg)(int *, int __user *, int old, int new); + int (*futex_atomic_op)(int op, u32 __user *, int oparg, int *old); + int (*futex_atomic_cmpxchg)(u32 *, u32 __user *, u32 old, u32 new); }; extern struct uaccess_ops uaccess; diff --git a/arch/s390/lib/uaccess.h b/arch/s390/lib/uaccess.h index 89a80674e44b0..1d2536cb630bc 100644 --- a/arch/s390/lib/uaccess.h +++ b/arch/s390/lib/uaccess.h @@ -12,12 +12,12 @@ extern size_t copy_from_user_std(size_t, const void __user *, void *); extern size_t copy_to_user_std(size_t, void __user *, const void *); extern size_t strnlen_user_std(size_t, const char __user *); extern size_t strncpy_from_user_std(size_t, const char __user *, char *); -extern int futex_atomic_cmpxchg_std(int *, int __user *, int, int); -extern int futex_atomic_op_std(int, int __user *, int, int *); +extern int futex_atomic_cmpxchg_std(u32 *, u32 __user *, u32, u32); +extern int futex_atomic_op_std(int, u32 __user *, int, int *); extern size_t copy_from_user_pt(size_t, const void __user *, void *); extern size_t copy_to_user_pt(size_t, void __user *, const void *); -extern int futex_atomic_op_pt(int, int __user *, int, int *); -extern int futex_atomic_cmpxchg_pt(int *, int __user *, int, int); +extern int futex_atomic_op_pt(int, u32 __user *, int, int *); +extern int futex_atomic_cmpxchg_pt(u32 *, u32 __user *, u32, u32); #endif /* __ARCH_S390_LIB_UACCESS_H */ diff --git a/arch/s390/lib/uaccess_pt.c b/arch/s390/lib/uaccess_pt.c index b3cebcd52f5c1..74833831417fc 100644 --- a/arch/s390/lib/uaccess_pt.c +++ b/arch/s390/lib/uaccess_pt.c @@ -302,7 +302,7 @@ static size_t copy_in_user_pt(size_t n, void __user *to, : "0" (-EFAULT), "d" (oparg), "a" (uaddr), \ "m" (*uaddr) : "cc" ); -static int __futex_atomic_op_pt(int op, int __user *uaddr, int oparg, int *old) +static int __futex_atomic_op_pt(int op, u32 __user *uaddr, int oparg, int *old) { int oldval = 0, newval, ret; @@ -335,7 +335,7 @@ static int __futex_atomic_op_pt(int op, int __user *uaddr, int oparg, int *old) return ret; } -int futex_atomic_op_pt(int op, int __user *uaddr, int oparg, int *old) +int futex_atomic_op_pt(int op, u32 __user *uaddr, int oparg, int *old) { int ret; @@ -354,8 +354,8 @@ int futex_atomic_op_pt(int op, int __user *uaddr, int oparg, int *old) return ret; } -static int __futex_atomic_cmpxchg_pt(int *uval, int __user *uaddr, - int oldval, int newval) +static int __futex_atomic_cmpxchg_pt(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) { int ret; @@ -370,8 +370,8 @@ static int __futex_atomic_cmpxchg_pt(int *uval, int __user *uaddr, return ret; } -int futex_atomic_cmpxchg_pt(int *uval, int __user *uaddr, - int oldval, int newval) +int futex_atomic_cmpxchg_pt(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) { int ret; diff --git a/arch/s390/lib/uaccess_std.c b/arch/s390/lib/uaccess_std.c index 1d6643c0b95f9..bb1a7eed42ce4 100644 --- a/arch/s390/lib/uaccess_std.c +++ b/arch/s390/lib/uaccess_std.c @@ -255,7 +255,7 @@ size_t strncpy_from_user_std(size_t size, const char __user *src, char *dst) : "0" (-EFAULT), "d" (oparg), "a" (uaddr), \ "m" (*uaddr) : "cc"); -int futex_atomic_op_std(int op, int __user *uaddr, int oparg, int *old) +int futex_atomic_op_std(int op, u32 __user *uaddr, int oparg, int *old) { int oldval = 0, newval, ret; @@ -287,8 +287,8 @@ int futex_atomic_op_std(int op, int __user *uaddr, int oparg, int *old) return ret; } -int futex_atomic_cmpxchg_std(int *uval, int __user *uaddr, - int oldval, int newval) +int futex_atomic_cmpxchg_std(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) { int ret; diff --git a/arch/sh/include/asm/futex-irq.h b/arch/sh/include/asm/futex-irq.h index 7b701cbd1e84a..6cb9f193a95ea 100644 --- a/arch/sh/include/asm/futex-irq.h +++ b/arch/sh/include/asm/futex-irq.h @@ -3,7 +3,7 @@ #include -static inline int atomic_futex_op_xchg_set(int oparg, int __user *uaddr, +static inline int atomic_futex_op_xchg_set(int oparg, u32 __user *uaddr, int *oldval) { unsigned long flags; @@ -20,7 +20,7 @@ static inline int atomic_futex_op_xchg_set(int oparg, int __user *uaddr, return ret; } -static inline int atomic_futex_op_xchg_add(int oparg, int __user *uaddr, +static inline int atomic_futex_op_xchg_add(int oparg, u32 __user *uaddr, int *oldval) { unsigned long flags; @@ -37,7 +37,7 @@ static inline int atomic_futex_op_xchg_add(int oparg, int __user *uaddr, return ret; } -static inline int atomic_futex_op_xchg_or(int oparg, int __user *uaddr, +static inline int atomic_futex_op_xchg_or(int oparg, u32 __user *uaddr, int *oldval) { unsigned long flags; @@ -54,7 +54,7 @@ static inline int atomic_futex_op_xchg_or(int oparg, int __user *uaddr, return ret; } -static inline int atomic_futex_op_xchg_and(int oparg, int __user *uaddr, +static inline int atomic_futex_op_xchg_and(int oparg, u32 __user *uaddr, int *oldval) { unsigned long flags; @@ -71,7 +71,7 @@ static inline int atomic_futex_op_xchg_and(int oparg, int __user *uaddr, return ret; } -static inline int atomic_futex_op_xchg_xor(int oparg, int __user *uaddr, +static inline int atomic_futex_op_xchg_xor(int oparg, u32 __user *uaddr, int *oldval) { unsigned long flags; @@ -88,12 +88,13 @@ static inline int atomic_futex_op_xchg_xor(int oparg, int __user *uaddr, return ret; } -static inline int atomic_futex_op_cmpxchg_inatomic(int *uval, - int __user *uaddr, - int oldval, int newval) +static inline int atomic_futex_op_cmpxchg_inatomic(u32 *uval, + u32 __user *uaddr, + u32 oldval, u32 newval) { unsigned long flags; - int ret, prev = 0; + int ret; + u32 prev = 0; local_irq_save(flags); diff --git a/arch/sh/include/asm/futex.h b/arch/sh/include/asm/futex.h index a8a5125dc9b43..7be39a646fbd0 100644 --- a/arch/sh/include/asm/futex.h +++ b/arch/sh/include/asm/futex.h @@ -10,7 +10,7 @@ /* XXX: UP variants, fix for SH-4A and SMP.. */ #include -static inline int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) +static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) { int op = (encoded_op >> 28) & 7; int cmp = (encoded_op >> 24) & 15; @@ -21,7 +21,7 @@ static inline int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; pagefault_disable(); @@ -65,10 +65,10 @@ static inline int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, - int oldval, int newval) +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) { - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; return atomic_futex_op_cmpxchg_inatomic(uval, uaddr, oldval, newval); diff --git a/arch/sparc/include/asm/futex_64.h b/arch/sparc/include/asm/futex_64.h index e0862200d6a14..444e7bea23bcb 100644 --- a/arch/sparc/include/asm/futex_64.h +++ b/arch/sparc/include/asm/futex_64.h @@ -30,7 +30,7 @@ : "r" (uaddr), "r" (oparg), "i" (-EFAULT) \ : "memory") -static inline int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) +static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) { int op = (encoded_op >> 28) & 7; int cmp = (encoded_op >> 24) & 15; @@ -38,7 +38,7 @@ static inline int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) int cmparg = (encoded_op << 20) >> 20; int oldval = 0, ret, tem; - if (unlikely(!access_ok(VERIFY_WRITE, uaddr, sizeof(int)))) + if (unlikely(!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))) return -EFAULT; if (unlikely((((unsigned long) uaddr) & 0x3UL))) return -EINVAL; @@ -85,8 +85,8 @@ static inline int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, - int oldval, int newval) +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) { int ret = 0; diff --git a/arch/tile/include/asm/futex.h b/arch/tile/include/asm/futex.h index 664b20aa2584f..d03ec124a598b 100644 --- a/arch/tile/include/asm/futex.h +++ b/arch/tile/include/asm/futex.h @@ -29,16 +29,16 @@ #include #include -extern struct __get_user futex_set(int __user *v, int i); -extern struct __get_user futex_add(int __user *v, int n); -extern struct __get_user futex_or(int __user *v, int n); -extern struct __get_user futex_andn(int __user *v, int n); -extern struct __get_user futex_cmpxchg(int __user *v, int o, int n); +extern struct __get_user futex_set(u32 __user *v, int i); +extern struct __get_user futex_add(u32 __user *v, int n); +extern struct __get_user futex_or(u32 __user *v, int n); +extern struct __get_user futex_andn(u32 __user *v, int n); +extern struct __get_user futex_cmpxchg(u32 __user *v, int o, int n); #ifndef __tilegx__ -extern struct __get_user futex_xor(int __user *v, int n); +extern struct __get_user futex_xor(u32 __user *v, int n); #else -static inline struct __get_user futex_xor(int __user *uaddr, int n) +static inline struct __get_user futex_xor(u32 __user *uaddr, int n) { struct __get_user asm_ret = __get_user_4(uaddr); if (!asm_ret.err) { @@ -53,7 +53,7 @@ static inline struct __get_user futex_xor(int __user *uaddr, int n) } #endif -static inline int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) +static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) { int op = (encoded_op >> 28) & 7; int cmp = (encoded_op >> 24) & 15; @@ -65,7 +65,7 @@ static inline int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; pagefault_disable(); @@ -119,12 +119,12 @@ static inline int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) return ret; } -static inline int futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, - int oldval, int newval) +static inline int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) { struct __get_user asm_ret; - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; asm_ret = futex_cmpxchg(uaddr, oldval, newval); diff --git a/arch/x86/include/asm/futex.h b/arch/x86/include/asm/futex.h index 884c0b5676f4b..d09bb03653f02 100644 --- a/arch/x86/include/asm/futex.h +++ b/arch/x86/include/asm/futex.h @@ -37,7 +37,7 @@ "+m" (*uaddr), "=&r" (tem) \ : "r" (oparg), "i" (-EFAULT), "1" (0)) -static inline int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) +static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) { int op = (encoded_op >> 28) & 7; int cmp = (encoded_op >> 24) & 15; @@ -48,7 +48,7 @@ static inline int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; #if defined(CONFIG_X86_32) && !defined(CONFIG_X86_BSWAP) @@ -109,8 +109,8 @@ static inline int futex_atomic_op_inuser(int encoded_op, int __user *uaddr) return ret; } -static inline int futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, - int oldval, int newval) +static inline int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) { int ret = 0; @@ -120,7 +120,7 @@ static inline int futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, return -ENOSYS; #endif - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; asm volatile("1:\t" LOCK_PREFIX "cmpxchgl %4, %2\n" diff --git a/include/asm-generic/futex.h b/include/asm-generic/futex.h index 132bf5227b448..01f227e14254f 100644 --- a/include/asm-generic/futex.h +++ b/include/asm-generic/futex.h @@ -6,7 +6,7 @@ #include static inline int -futex_atomic_op_inuser (int encoded_op, int __user *uaddr) +futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) { int op = (encoded_op >> 28) & 7; int cmp = (encoded_op >> 24) & 15; @@ -16,7 +16,7 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; - if (! access_ok (VERIFY_WRITE, uaddr, sizeof(int))) + if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; pagefault_disable(); @@ -48,8 +48,8 @@ futex_atomic_op_inuser (int encoded_op, int __user *uaddr) } static inline int -futex_atomic_cmpxchg_inatomic(int *uval, int __user *uaddr, - int oldval, int newval) +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) { return -ENOSYS; } From 5d1c7bad12a1f1454a7154542fa0c3f50e829840 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 14 Mar 2011 20:00:30 +0100 Subject: [PATCH 2245/2556] arm: Remove bogus comment in futex_atomic_cmpxchg_inatomic() commit 522d7dec(futex: Remove redundant pagefault_disable in futex_atomic_cmpxchg_inatomic()) added a bogus comment. /* Note that preemption is disabled by futex_atomic_cmpxchg_inatomic * call sites. */ Bogus in two aspects: 1) pagefault_disable != preempt_disable even if the mechanism we use is the same 2) we have a call site which deliberately does not disable pagefaults as it wants the possible fault to be handled - though that has been changed for consistency reasons now. Sigh. I really should have seen that when committing the above. :( Change-Id: Icee34929438f9f1b978d0ebb6c305527306542c6 Catched-by-and-rightfully-ranted-at-by: Linus Torvalds Signed-off-by: Thomas Gleixner LKML-Reference: Cc: Michel Lespinasse Cc: Darren Hart Signed-off-by: Jin Hong --- arch/arm/include/asm/futex.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/arm/include/asm/futex.h b/arch/arm/include/asm/futex.h index 0e29d8e6a5c2a..199a6b6de7f48 100644 --- a/arch/arm/include/asm/futex.h +++ b/arch/arm/include/asm/futex.h @@ -97,9 +97,6 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) return -EFAULT; - /* Note that preemption is disabled by futex_atomic_cmpxchg_inatomic - * call sites. */ - __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" "1: " T(ldr) " %1, [%4]\n" " teq %1, %2\n" From b161632a38238c3185b5ebe9d216e834de38bce5 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 28 Apr 2011 18:43:01 +0100 Subject: [PATCH 2246/2556] ARM: 6889/1: futex: add SMP futex support when !CPU_USE_DOMAINS This patch uses the load/store exclusive instructions to add SMP futex support for ARM. Since the ARM architecture does not provide instructions for unprivileged exclusive memory accesses, we can only provide SMP futexes when CPU domain support is disabled. Change-Id: Ib860c4febad574910a35ff5040af0d8a62fa9e6d Cc: Nicolas Pitre Signed-off-by: Will Deacon Signed-off-by: Russell King Signed-off-by: Jin Hong --- arch/arm/include/asm/futex.h | 137 +++++++++++++++++++++++------------ 1 file changed, 90 insertions(+), 47 deletions(-) diff --git a/arch/arm/include/asm/futex.h b/arch/arm/include/asm/futex.h index 199a6b6de7f48..8c73900da9ed0 100644 --- a/arch/arm/include/asm/futex.h +++ b/arch/arm/include/asm/futex.h @@ -3,16 +3,74 @@ #ifdef __KERNEL__ +#if defined(CONFIG_CPU_USE_DOMAINS) && defined(CONFIG_SMP) +/* ARM doesn't provide unprivileged exclusive memory accessors */ +#include +#else + +#include +#include +#include + +#define __futex_atomic_ex_table(err_reg) \ + "3:\n" \ + " .pushsection __ex_table,\"a\"\n" \ + " .align 3\n" \ + " .long 1b, 4f, 2b, 4f\n" \ + " .popsection\n" \ + " .pushsection .fixup,\"ax\"\n" \ + "4: mov %0, " err_reg "\n" \ + " b 3b\n" \ + " .popsection" + #ifdef CONFIG_SMP -#include +#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \ + smp_mb(); \ + __asm__ __volatile__( \ + "1: ldrex %1, [%2]\n" \ + " " insn "\n" \ + "2: strex %1, %0, [%2]\n" \ + " teq %1, #0\n" \ + " bne 1b\n" \ + " mov %0, #0\n" \ + __futex_atomic_ex_table("%4") \ + : "=&r" (ret), "=&r" (oldval) \ + : "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \ + : "cc", "memory") + +static inline int +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) +{ + int ret; + u32 val; + + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) + return -EFAULT; + + smp_mb(); + __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" + "1: ldrex %1, [%4]\n" + " teq %1, %2\n" + " ite eq @ explicit IT needed for the 2b label\n" + "2: strexeq %0, %3, [%4]\n" + " movne %0, #0\n" + " teq %0, #0\n" + " bne 1b\n" + __futex_atomic_ex_table("%5") + : "=&r" (ret), "=&r" (val) + : "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT) + : "cc", "memory"); + smp_mb(); + + *uval = val; + return ret; +} #else /* !SMP, we can work around lack of atomic ops by disabling preemption */ -#include #include -#include -#include #include #define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \ @@ -21,19 +79,37 @@ " " insn "\n" \ "2: " T(str) " %0, [%2]\n" \ " mov %0, #0\n" \ - "3:\n" \ - " .pushsection __ex_table,\"a\"\n" \ - " .align 3\n" \ - " .long 1b, 4f, 2b, 4f\n" \ - " .popsection\n" \ - " .pushsection .fixup,\"ax\"\n" \ - "4: mov %0, %4\n" \ - " b 3b\n" \ - " .popsection" \ + __futex_atomic_ex_table("%4") \ : "=&r" (ret), "=&r" (oldval) \ : "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \ : "cc", "memory") +static inline int +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) +{ + int ret = 0; + u32 val; + + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) + return -EFAULT; + + __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" + "1: " T(ldr) " %1, [%4]\n" + " teq %1, %2\n" + " it eq @ explicit IT needed for the 2b label\n" + "2: " T(streq) " %3, [%4]\n" + __futex_atomic_ex_table("%5") + : "+r" (ret), "=&r" (val) + : "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT) + : "cc", "memory"); + + *uval = val; + return ret; +} + +#endif /* !SMP */ + static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) { @@ -87,39 +163,6 @@ futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) return ret; } -static inline int -futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, - u32 oldval, u32 newval) -{ - int ret = 0; - u32 val; - - if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) - return -EFAULT; - - __asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n" - "1: " T(ldr) " %1, [%4]\n" - " teq %1, %2\n" - " it eq @ explicit IT needed for the 2b label\n" - "2: " T(streq) " %3, [%4]\n" - "3:\n" - " .pushsection __ex_table,\"a\"\n" - " .align 3\n" - " .long 1b, 4f, 2b, 4f\n" - " .popsection\n" - " .pushsection .fixup,\"ax\"\n" - "4: mov %0, %5\n" - " b 3b\n" - " .popsection" - : "+r" (ret), "=&r" (val) - : "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT) - : "cc", "memory"); - - *uval = val; - return ret; -} - -#endif /* !SMP */ - +#endif /* !(CPU_USE_DOMAINS && SMP) */ #endif /* __KERNEL__ */ #endif /* _ASM_ARM_FUTEX_H */ From 9627844fb2e4bc7775907e1b2a57beab2e17794d Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Thu, 16 Jun 2011 15:36:38 -0400 Subject: [PATCH 2247/2556] [CPUFREQ] Don't set stat->last_index to -1 if the pol->cur has incorrect value. If the driver submitted an non-existing pol>cur value (say it used the default initialized value of zero), when the cpufreq stats tries to setup its initial values it incorrectly sets stat->last_index to -1 (or 0xfffff...). And cpufreq_stats_update tries to update at that index location and fails. This can be caused by: stat->last_index = freq_table_get_index(stat, policy->cur); not finding the appropiate frequency in the table (b/c the policy->cur is wrong) and we end up crashing. The fix however is concentrated in the 'cpufreq_stats_update' as the last_index (and old_index) are updated there. Which means it can reset the last_index to -1 again and on the next iteration cause a crash. Without this patch, the following crash is observed: powernow-k8: Found 1 AMD Athlon(tm) 64 Processor 3700+ (1 cpu cores) (version 2.20.00) powernow-k8: fid 0x2 (1000 MHz), vid 0x12 powernow-k8: fid 0xa (1800 MHz), vid 0xa powernow-k8: fid 0xc (2000 MHz), vid 0x8 powernow-k8: fid 0xe (2200 MHz), vid 0x8 Marking TSC unstable due to cpufreq changes powernow-k8: fid trans failed, fid 0x2, curr 0x0 BUG: unable to handle kernel paging request at ffff880807e07b78 IP: [] cpufreq_stats_update+0x46/0x5b .. snip.. Pid: 1, comm: swapper Not tainted 3.0.0-rc2 #45 MICRO-STAR INTERNATIONAL CO., LTD MS-7094/MS-7094 ..snip.. Call Trace: [] cpufreq_stat_notifier_trans+0x48/0x7c [] notifier_call_chain+0x32/0x5e [] __srcu_notifier_call_chain+0x47/0x63 [] srcu_notifier_call_chain+0xf/0x11 [] cpufreq_notify_transition+0x111/0x134 [] powernowk8_target+0x53b/0x617 [] __cpufreq_driver_target+0x2e/0x30 [] cpufreq_governor_dbs+0x339/0x356 [] __cpufreq_governor+0xa8/0xe9 [] __cpufreq_set_policy+0x132/0x13e [] cpufreq_add_dev_interface+0x272/0x28c Reported-by: Tobias Diedrich Tested-by: Tobias Diedrich Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: Dave Jones --- drivers/cpufreq/cpufreq_stats.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 39b00d362597b..d36be2db7b0ec 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -298,11 +298,13 @@ static int cpufreq_stat_notifier_trans(struct notifier_block *nb, old_index = stat->last_index; new_index = freq_table_get_index(stat, freq->new); - cpufreq_stats_update(freq->cpu); - if (old_index == new_index) + /* We can't do stat->time_in_state[-1]= .. */ + if (old_index == -1 || new_index == -1) return 0; - if (old_index == -1 || new_index == -1) + cpufreq_stats_update(freq->cpu); + + if (old_index == new_index) return 0; spin_lock(&cpufreq_stats_lock); From 5b6babc40d8a775ad5afbacbc0d6ea09af536137 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Fri, 18 May 2012 20:10:41 -0500 Subject: [PATCH 2248/2556] Revert "clean up merge error" This reverts commit 9bb51f3a9b4fabfd5b9ec38c4a66a7a1c1f87809. --- drivers/cpufreq/cpufreq_stats.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index d36be2db7b0ec..f6fba49450e71 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -351,6 +351,10 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, break; case CPU_DOWN_PREPARE: case CPU_DOWN_PREPARE_FROZEN: + cpufreq_stats_free_sysfs(cpu); + break; + case CPU_DEAD: + case CPU_DEAD_FROZEN: cpufreq_stats_free_table(cpu); break; case CPU_DOWN_FAILED: From ae21fe41e2a32d00ef24c3b515619985e162d838 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Tue, 20 Mar 2012 14:09:29 -0600 Subject: [PATCH 2249/2556] base: genlock: Use a signed long for the result from wait_timeout interruptible_wait_timeout returns a signed long, so make sure that we use a signed long to hold the result. Using an unsigned value would horribly misinterpet an error such as -ERESTARTSYS. CRs-Fixed: 341347 Signed-off-by: Jordan Crouse (cherry picked from commit be669bc92358653a46c63f2352e6d981f9ed3bdf) Change-Id: Ifcf555f8719f9c74fdb6d8b5cf2a32582988335b Signed-off-by: Ram Kumar Chakravarthy Chebathini --- drivers/base/genlock.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/base/genlock.c b/drivers/base/genlock.c index 150791550c4dd..d9cd600282464 100644 --- a/drivers/base/genlock.c +++ b/drivers/base/genlock.c @@ -288,7 +288,7 @@ static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle, { unsigned long irqflags; int ret = 0; - unsigned int ticks = msecs_to_jiffies(timeout); + unsigned long ticks = msecs_to_jiffies(timeout); spin_lock_irqsave(&lock->lock, irqflags); @@ -359,7 +359,7 @@ static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle, /* Wait while the lock remains in an incompatible state */ while (lock->state != _UNLOCKED) { - unsigned int elapsed; + signed long elapsed; spin_unlock_irqrestore(&lock->lock, irqflags); @@ -373,7 +373,7 @@ static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle, goto done; } - ticks = elapsed; + ticks = (unsigned long) elapsed; } dolock: @@ -447,7 +447,7 @@ int genlock_wait(struct genlock_handle *handle, uint32_t timeout) struct genlock *lock; unsigned long irqflags; int ret = 0; - unsigned int ticks = msecs_to_jiffies(timeout); + unsigned long ticks = msecs_to_jiffies(timeout); if (IS_ERR_OR_NULL(handle)) { GENLOCK_LOG_ERR("Invalid handle\n"); @@ -474,7 +474,7 @@ int genlock_wait(struct genlock_handle *handle, uint32_t timeout) } while (lock->state != _UNLOCKED) { - unsigned int elapsed; + signed long elapsed; spin_unlock_irqrestore(&lock->lock, irqflags); @@ -488,7 +488,7 @@ int genlock_wait(struct genlock_handle *handle, uint32_t timeout) break; } - ticks = elapsed; + ticks = (unsigned long) elapsed; } done: From ba4a8de1dd3c63dd8a2e529b282ebc22efd090c2 Mon Sep 17 00:00:00 2001 From: Jeff Boody Date: Thu, 26 Apr 2012 11:12:44 -0600 Subject: [PATCH 2250/2556] base: genlock: allow synchronization with a single gralloc handle In order to support synchronization in a process with a single gralloc handle we require the ability to write lock a buffer while it is already read locked by the same handle. This change extends the concept of an exclusive write lock or recursive read locks to a genlock handle (rather than the genlock lock). Genlock cannot provide deadlock protection because the same handle can be used simultaneously by a producer and consumer. In practice an error will still be generated when the timeout expires. CRs-fixed: 356263 Signed-off-by: Jeff Boody (cherry picked from commit 6d9076fe46bcba71986a25cf357e71a4d83958c1) Change-Id: I5d02591586023f4b622915522e2e038c16c9f2f2 Signed-off-by: Ram Kumar Chakravarthy Chebathini --- Documentation/genlock.txt | 13 +++- drivers/base/genlock.c | 144 +++++++++++++++++++++++++++++++------- include/linux/genlock.h | 9 ++- 3 files changed, 137 insertions(+), 29 deletions(-) diff --git a/Documentation/genlock.txt b/Documentation/genlock.txt index 6f24a7695615d..cd8261467748a 100644 --- a/Documentation/genlock.txt +++ b/Documentation/genlock.txt @@ -34,6 +34,12 @@ instance for a lock known as a handle. Handles can be shared between user space and kernel space to allow a kernel driver to unlock or lock a buffer on behalf of a user process. +Locks within a process using a single genlock handle follow the same rules for +exclusive write locks with multiple readers. Genlock cannot provide deadlock +protection because the same handle can be used simultaneously by a producer and +consumer. In practice in the event that the client creates a deadlock an error +will still be generated when the timeout expires. + Kernel API Access to the genlock API can either be via the in-kernel API or via an @@ -137,7 +143,12 @@ genlock_lock.op: * GENLOCK_UNLOCK - unlock an existing lock Pass flags in genlock_lock.flags: - * GENLOCK_NOBLOCK - Do not block if the lock is already taken + * GENLOCK_NOBLOCK - Do not block if the lock is already taken + * GENLOCK_WRITE_TO_READ - Convert a write lock that the handle owns to a read + lock. For instance graphics may hold a write lock + while rendering the back buffer then when swapping + convert the lock to a read lock to copy the front + buffer in the next frame for preserved buffers. Pass a timeout value in milliseconds in genlock_lock.timeout. genlock_lock.flags and genlock_lock.timeout are not used for UNLOCK. diff --git a/drivers/base/genlock.c b/drivers/base/genlock.c index d9cd600282464..5e1d7af5e5295 100644 --- a/drivers/base/genlock.c +++ b/drivers/base/genlock.c @@ -307,12 +307,15 @@ static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle, if (handle_has_lock(lock, handle)) { /* - * If the handle already holds the lock and the type matches, - * then just increment the active pointer. This allows the - * handle to do recursive locks + * If the handle already holds the lock and the lock type is + * a read lock then just increment the active pointer. This + * allows the handle to do recursive read locks. Recursive + * write locks are not allowed in order to support + * synchronization within a process using a single gralloc + * handle. */ - if (lock->state == op) { + if (lock->state == _RDLOCK && op == _RDLOCK) { handle->active++; goto done; } @@ -321,32 +324,45 @@ static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle, * If the handle holds a write lock then the owner can switch * to a read lock if they want. Do the transition atomically * then wake up any pending waiters in case they want a read - * lock too. + * lock too. In order to support synchronization within a + * process the caller must explicity request to convert the + * lock type with the GENLOCK_WRITE_TO_READ flag. */ - if (op == _RDLOCK && handle->active == 1) { - lock->state = _RDLOCK; - wake_up(&lock->queue); - goto done; + if (flags & GENLOCK_WRITE_TO_READ) { + if (lock->state == _WRLOCK && op == _RDLOCK) { + lock->state = _RDLOCK; + wake_up(&lock->queue); + goto done; + } else { + GENLOCK_LOG_ERR("Invalid state to convert" + "write to read\n"); + ret = -EINVAL; + goto done; + } } + } else { /* - * Otherwise the user tried to turn a read into a write, and we - * don't allow that. + * Check to ensure the caller has not attempted to convert a + * write to a read without holding the lock. */ - GENLOCK_LOG_ERR("Trying to upgrade a read lock to a write" - "lock\n"); - ret = -EINVAL; - goto done; - } - /* - * If we request a read and the lock is held by a read, then go - * ahead and share the lock - */ + if (flags & GENLOCK_WRITE_TO_READ) { + GENLOCK_LOG_ERR("Handle must have lock to convert" + "write to read\n"); + ret = -EINVAL; + goto done; + } - if (op == GENLOCK_RDLOCK && lock->state == _RDLOCK) - goto dolock; + /* + * If we request a read and the lock is held by a read, then go + * ahead and share the lock + */ + + if (op == GENLOCK_RDLOCK && lock->state == _RDLOCK) + goto dolock; + } /* Treat timeout 0 just like a NOBLOCK flag and return if the lock cannot be aquired without blocking */ @@ -356,15 +372,26 @@ static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle, goto done; } - /* Wait while the lock remains in an incompatible state */ + /* + * Wait while the lock remains in an incompatible state + * state op wait + * ------------------- + * unlocked n/a no + * read read no + * read write yes + * write n/a yes + */ - while (lock->state != _UNLOCKED) { + while ((lock->state == _RDLOCK && op == _WRLOCK) || + lock->state == _WRLOCK) { signed long elapsed; spin_unlock_irqrestore(&lock->lock, irqflags); elapsed = wait_event_interruptible_timeout(lock->queue, - lock->state == _UNLOCKED, ticks); + lock->state == _UNLOCKED || + (lock->state == _RDLOCK && op == _RDLOCK), + ticks); spin_lock_irqsave(&lock->lock, irqflags); @@ -381,7 +408,7 @@ static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle, list_add_tail(&handle->entry, &lock->active); lock->state = op; - handle->active = 1; + handle->active++; done: spin_unlock_irqrestore(&lock->lock, irqflags); @@ -390,7 +417,7 @@ static int _genlock_lock(struct genlock *lock, struct genlock_handle *handle, } /** - * genlock_lock - Acquire or release a lock + * genlock_lock - Acquire or release a lock (depreciated) * @handle - pointer to the genlock handle that is requesting the lock * @op - the operation to perform (RDLOCK, WRLOCK, UNLOCK) * @flags - flags to control the operation @@ -403,6 +430,7 @@ int genlock_lock(struct genlock_handle *handle, int op, int flags, uint32_t timeout) { struct genlock *lock; + unsigned long irqflags; int ret = 0; @@ -423,6 +451,13 @@ int genlock_lock(struct genlock_handle *handle, int op, int flags, ret = _genlock_unlock(lock, handle); break; case GENLOCK_RDLOCK: + spin_lock_irqsave(&lock->lock, irqflags); + if (handle_has_lock(lock, handle)) { + /* request the WRITE_TO_READ flag for compatibility */ + flags |= GENLOCK_WRITE_TO_READ; + } + spin_unlock_irqrestore(&lock->lock, irqflags); + /* fall through to take lock */ case GENLOCK_WRLOCK: ret = _genlock_lock(lock, handle, op, flags, timeout); break; @@ -436,6 +471,53 @@ int genlock_lock(struct genlock_handle *handle, int op, int flags, } EXPORT_SYMBOL(genlock_lock); +/** + * genlock_dreadlock - Acquire or release a lock + * @handle - pointer to the genlock handle that is requesting the lock + * @op - the operation to perform (RDLOCK, WRLOCK, UNLOCK) + * @flags - flags to control the operation + * @timeout - optional timeout to wait for the lock to come free + * + * Returns: 0 on success or error code on failure + */ + +int genlock_dreadlock(struct genlock_handle *handle, int op, int flags, + uint32_t timeout) +{ + struct genlock *lock; + + int ret = 0; + + if (IS_ERR_OR_NULL(handle)) { + GENLOCK_LOG_ERR("Invalid handle\n"); + return -EINVAL; + } + + lock = handle->lock; + + if (lock == NULL) { + GENLOCK_LOG_ERR("Handle does not have a lock attached\n"); + return -EINVAL; + } + + switch (op) { + case GENLOCK_UNLOCK: + ret = _genlock_unlock(lock, handle); + break; + case GENLOCK_RDLOCK: + case GENLOCK_WRLOCK: + ret = _genlock_lock(lock, handle, op, flags, timeout); + break; + default: + GENLOCK_LOG_ERR("Invalid lock operation\n"); + ret = -EINVAL; + break; + } + + return ret; +} +EXPORT_SYMBOL(genlock_dreadlock); + /** * genlock_wait - Wait for the lock to be released * @handle - pointer to the genlock handle that is waiting for the lock @@ -657,6 +739,14 @@ static long genlock_dev_ioctl(struct file *filep, unsigned int cmd, return genlock_lock(handle, param.op, param.flags, param.timeout); } + case GENLOCK_IOC_DREADLOCK: { + if (copy_from_user(¶m, (void __user *) arg, + sizeof(param))) + return -EFAULT; + + return genlock_dreadlock(handle, param.op, param.flags, + param.timeout); + } case GENLOCK_IOC_WAIT: { if (copy_from_user(¶m, (void __user *) arg, sizeof(param))) diff --git a/include/linux/genlock.h b/include/linux/genlock.h index 9351a15626b60..60bc84c5363a1 100644 --- a/include/linux/genlock.h +++ b/include/linux/genlock.h @@ -1,6 +1,8 @@ #ifndef _GENLOCK_H_ #define _GENLOCK_H_ +#include + #ifdef __KERNEL__ struct genlock; @@ -21,7 +23,8 @@ int genlock_lock(struct genlock_handle *handle, int op, int flags, #define GENLOCK_WRLOCK 1 #define GENLOCK_RDLOCK 2 -#define GENLOCK_NOBLOCK (1 << 0) +#define GENLOCK_NOBLOCK BIT(0) +#define GENLOCK_WRITE_TO_READ BIT(1) struct genlock_lock { int fd; @@ -37,6 +40,8 @@ struct genlock_lock { struct genlock_lock) #define GENLOCK_IOC_ATTACH _IOW(GENLOCK_IOC_MAGIC, 2, \ struct genlock_lock) + +/* Deprecated */ #define GENLOCK_IOC_LOCK _IOW(GENLOCK_IOC_MAGIC, 3, \ struct genlock_lock) @@ -44,4 +49,6 @@ struct genlock_lock { #define GENLOCK_IOC_RELEASE _IO(GENLOCK_IOC_MAGIC, 4) #define GENLOCK_IOC_WAIT _IOW(GENLOCK_IOC_MAGIC, 5, \ struct genlock_lock) +#define GENLOCK_IOC_DREADLOCK _IOW(GENLOCK_IOC_MAGIC, 6, \ + struct genlock_lock) #endif From fcdff210ad633880d6f94f05b4dec216a63a0b87 Mon Sep 17 00:00:00 2001 From: Yong Zhang Date: Sun, 20 Feb 2011 15:08:14 +0800 Subject: [PATCH 2251/2556] sched, autogroup: Stop going ahead if autogroup is disabled when autogroup is disable from the beginning, sched_autogroup_create_attach() autogroup_move_group() <== 1 sched_move_task() <== 2 task_move_group_fair() set_task_rq() task_group() autogroup_task_group() We go the whole path without doing anything useful. Then stop going further if autogroup is disabled. But there will be a race window between 1 and 2, in which sysctl_sched_autogroup_enabled is enabled. This issue will be toke by following patch. Signed-off-by: Yong Zhang Signed-off-by: Peter Zijlstra Cc: Mike Galbraith LKML-Reference: <1298185696-4403-4-git-send-email-yong.zhang0@gmail.com> Signed-off-by: Ingo Molnar --- kernel/sched_autogroup.c | 4 ++++ kernel/sched_autogroup.h | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/kernel/sched_autogroup.c b/kernel/sched_autogroup.c index 9fb6562831570..137a096ae9d85 100644 --- a/kernel/sched_autogroup.c +++ b/kernel/sched_autogroup.c @@ -161,11 +161,15 @@ autogroup_move_group(struct task_struct *p, struct autogroup *ag) p->signal->autogroup = autogroup_kref_get(ag); + if (!ACCESS_ONCE(sysctl_sched_autogroup_enabled)) + goto out; + t = p; do { sched_move_task(t); } while_each_thread(p, t); +out: unlock_task_sighand(p, &flags); autogroup_kref_put(prev); } diff --git a/kernel/sched_autogroup.h b/kernel/sched_autogroup.h index 7b859ffe5dadd..05577055cfcaa 100644 --- a/kernel/sched_autogroup.h +++ b/kernel/sched_autogroup.h @@ -1,6 +1,11 @@ #ifdef CONFIG_SCHED_AUTOGROUP struct autogroup { + /* + * reference doesn't mean how many thread attach to this + * autogroup now. It just stands for the number of task + * could use this autogroup. + */ struct kref kref; struct task_group *tg; struct rw_semaphore lock; From c1e7e1bcfc56e2b49d66f4884ec622e33a18ec81 Mon Sep 17 00:00:00 2001 From: Mike Galbraith Date: Tue, 22 Feb 2011 15:02:00 +0100 Subject: [PATCH 2252/2556] sched, autogroup: Stop claiming ownership of the root task group Disown it, and only display autogroup association if one exists. Signed-off-by: Mike Galbraith Reviewed-by: Yong Zhang Signed-off-by: Peter Zijlstra LKML-Reference: <1298383320.8036.5.camel@marge.simson.net> Signed-off-by: Ingo Molnar --- kernel/sched_autogroup.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/kernel/sched_autogroup.c b/kernel/sched_autogroup.c index 137a096ae9d85..5946ac5156024 100644 --- a/kernel/sched_autogroup.c +++ b/kernel/sched_autogroup.c @@ -12,7 +12,6 @@ static atomic_t autogroup_seq_nr; static void __init autogroup_init(struct task_struct *init_task) { autogroup_default.tg = &root_task_group; - root_task_group.autogroup = &autogroup_default; kref_init(&autogroup_default.kref); init_rwsem(&autogroup_default.lock); init_task->signal->autogroup = &autogroup_default; @@ -130,7 +129,7 @@ task_wants_autogroup(struct task_struct *p, struct task_group *tg) static inline bool task_group_is_autogroup(struct task_group *tg) { - return tg != &root_task_group && tg->autogroup; + return !!tg->autogroup; } static inline struct task_group * @@ -251,10 +250,14 @@ void proc_sched_autogroup_show_task(struct task_struct *p, struct seq_file *m) { struct autogroup *ag = autogroup_task_get(p); + if (!task_group_is_autogroup(ag->tg)) + goto out; + down_read(&ag->lock); seq_printf(m, "/autogroup-%ld nice %d\n", ag->id, ag->nice); up_read(&ag->lock); +out: autogroup_kref_put(ag); } #endif /* CONFIG_PROC_FS */ @@ -262,9 +265,7 @@ void proc_sched_autogroup_show_task(struct task_struct *p, struct seq_file *m) #ifdef CONFIG_SCHED_DEBUG static inline int autogroup_path(struct task_group *tg, char *buf, int buflen) { - int enabled = ACCESS_ONCE(sysctl_sched_autogroup_enabled); - - if (!enabled || !tg->autogroup) + if (!task_group_is_autogroup(tg)) return 0; return snprintf(buf, buflen, "%s-%ld", "/autogroup", tg->autogroup->id); From ca9e67596c73de975576b85272921eb3deaa63c2 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Mon, 21 May 2012 22:58:48 -0500 Subject: [PATCH 2253/2556] Revert "Revert "drivers: misc: pass miscdevice pointer via file private data"" This reverts commit 69884b95102c2e187d04ce17142b31f0c8ce2cdb. --- drivers/char/misc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/char/misc.c b/drivers/char/misc.c index d8efcf1716933..778273c93242f 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -144,6 +144,7 @@ static int misc_open(struct inode * inode, struct file * file) old_fops = file->f_op; file->f_op = new_fops; if (file->f_op->open) { + file->private_data = c; err=file->f_op->open(inode,file); if (err) { fops_put(file->f_op); From 5af33ef3369dba4d553b9b7bf885181070d2d96c Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Mon, 21 May 2012 22:50:02 -0500 Subject: [PATCH 2254/2556] more memory changes from cafs/msm-2.6.38 --- drivers/base/memory.c | 172 +++++++++++++++++++++++++++++++++ include/linux/memory_hotplug.h | 13 +++ mm/memory_hotplug.c | 72 +++++++++++++- 3 files changed, 252 insertions(+), 5 deletions(-) diff --git a/drivers/base/memory.c b/drivers/base/memory.c index cafeaaf0428fc..4969f2f5dc329 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -54,6 +54,9 @@ static const struct kset_uevent_ops memory_uevent_ops = { static BLOCKING_NOTIFIER_HEAD(memory_chain); +unsigned long movable_reserved_start, movable_reserved_size; +unsigned long low_power_memory_start, low_power_memory_size; + int register_memory_notifier(struct notifier_block *nb) { return blocking_notifier_chain_register(&memory_chain, nb); @@ -326,6 +329,64 @@ static int block_size_init(void) &attr_block_size_bytes.attr); } +static ssize_t +print_movable_size(struct class *class, struct class_attribute *attr, char *buf) +{ + return sprintf(buf, "%lx\n", movable_reserved_size); +} + +static CLASS_ATTR(movable_size_bytes, 0444, print_movable_size, NULL); + +static int movable_size_init(void) +{ + return sysfs_create_file(&memory_sysdev_class.kset.kobj, + &class_attr_movable_size_bytes.attr); +} + +static ssize_t +print_movable_start(struct class *class, struct class_attribute *attr, char *buf) +{ + return sprintf(buf, "%lx\n", movable_reserved_start); +} + +static CLASS_ATTR(movable_start_bytes, 0444, print_movable_start, NULL); + +static int movable_start_init(void) +{ + return sysfs_create_file(&memory_sysdev_class.kset.kobj, + &class_attr_movable_start_bytes.attr); +} + +static ssize_t +print_low_power_memory_size(struct class *class, struct class_attribute *attr, char *buf) +{ + return sprintf(buf, "%lx\n", low_power_memory_size); +} + +static CLASS_ATTR(low_power_memory_size_bytes, 0444, + print_low_power_memory_size, NULL); + +static int low_power_memory_size_init(void) +{ + return sysfs_create_file(&memory_sysdev_class.kset.kobj, + &class_attr_low_power_memory_size_bytes.attr); +} + +static ssize_t +print_low_power_memory_start(struct class *class, struct class_attribute *attr, char *buf) +{ + return sprintf(buf, "%lx\n", low_power_memory_start); +} + +static CLASS_ATTR(low_power_memory_start_bytes, 0444, + print_low_power_memory_start, NULL); + +static int low_power_memory_start_init(void) +{ + return sysfs_create_file(&memory_sysdev_class.kset.kobj, + &class_attr_low_power_memory_start_bytes.attr); +} + /* * Some architectures will have custom drivers to do this, and * will not need to do it from userspace. The fake hot-add code @@ -427,6 +488,96 @@ static inline int memory_fail_init(void) } #endif +#ifdef CONFIG_ARCH_MEMORY_REMOVE +static ssize_t +memory_remove_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t count) +{ + u64 phys_addr; + int ret; + + phys_addr = simple_strtoull(buf, NULL, 0); + + ret = physical_remove_memory(phys_addr, + PAGES_PER_SECTION << PAGE_SHIFT); + + if (ret) + count = ret; + + return count; +} +static CLASS_ATTR(remove, S_IWUSR, NULL, memory_remove_store); + +static int memory_remove_init(void) +{ + return sysfs_create_file(&memory_sysdev_class.kset.kobj, + &class_attr_remove.attr); +} + +static ssize_t +memory_active_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t count) +{ + u64 phys_addr; + int ret; + + phys_addr = simple_strtoull(buf, NULL, 0); + + ret = physical_active_memory(phys_addr, + PAGES_PER_SECTION << PAGE_SHIFT); + + if (ret) + count = ret; + + return count; +} +static CLASS_ATTR(active, S_IWUSR, NULL, memory_active_store); + +static int memory_active_init(void) +{ + return sysfs_create_file(&memory_sysdev_class.kset.kobj, + &class_attr_active.attr); +} + +static ssize_t +memory_low_power_store(struct class *class, struct class_attribute *attr, + const char *buf, size_t count) +{ + u64 phys_addr; + int ret; + + phys_addr = simple_strtoull(buf, NULL, 0); + + ret = physical_low_power_memory(phys_addr, + PAGES_PER_SECTION << PAGE_SHIFT); + + if (ret) + count = ret; + + return count; +} +static CLASS_ATTR(low_power, S_IWUSR, NULL, memory_low_power_store); + +static int memory_low_power_init(void) +{ + return sysfs_create_file(&memory_sysdev_class.kset.kobj, + &class_attr_low_power.attr); +} +#else +static inline int memory_remove_init(void) +{ + return 0; +} +static inline int memory_active_init(void) +{ + return 0; +} +static inline int memory_low_power_init(void) +{ + return 0; +} +#endif + /* * Note that phys_device is optional. It is here to allow for * differentiation between which *physical* devices each @@ -583,11 +734,32 @@ int __init memory_dev_init(void) if (!ret) ret = err; err = memory_fail_init(); + if (!ret) + ret = err; + err = memory_remove_init(); + if (!ret) + ret = err; + err = memory_active_init(); + if (!ret) + ret = err; + err = memory_low_power_init(); if (!ret) ret = err; err = block_size_init(); if (!ret) ret = err; + err = movable_size_init(); + if (!ret) + ret = err; + err = movable_start_init(); + if (!ret) + ret = err; + err = low_power_memory_size_init(); + if (!ret) + ret = err; + err = low_power_memory_start_init(); + if (!ret) + ret = err; out: if (ret) printk(KERN_ERR "%s() failed: %d\n", __func__, ret); diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index 8122018d30002..2d2414be44c46 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -10,6 +10,9 @@ struct zone; struct pglist_data; struct mem_section; +extern unsigned long movable_reserved_start, movable_reserved_size; +extern unsigned long low_power_memory_start, low_power_memory_size; + #ifdef CONFIG_MEMORY_HOTPLUG /* @@ -232,4 +235,14 @@ extern void sparse_remove_one_section(struct zone *zone, struct mem_section *ms) extern struct page *sparse_decode_mem_map(unsigned long coded_mem_map, unsigned long pnum); +extern void reserve_hotplug_pages(unsigned long start_pfn, + unsigned long nr_pages); +extern void unreserve_hotplug_pages(unsigned long start_pfn, + unsigned long nr_pages); #endif /* __LINUX_MEMORY_HOTPLUG_H */ +extern int physical_remove_memory(u64 start, u64 size); +extern int arch_physical_remove_memory(u64 start, u64 size); +extern int physical_low_power_memory(u64 start, u64 size); +extern int arch_physical_low_power_memory(u64 start, u64 size); +extern int physical_active_memory(u64 start, u64 size); +extern int arch_physical_active_memory(u64 start, u64 size); diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 321fc7455df73..7aa5343ba9c38 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -112,9 +112,10 @@ void __ref put_page_bootmem(struct page *page) static void register_page_bootmem_info_section(unsigned long start_pfn) { - unsigned long *usemap, mapsize, section_nr, i; + unsigned long *usemap, mapsize, page_mapsize, section_nr, i, j; struct mem_section *ms; - struct page *page, *memmap; + struct page *page, *memmap, *page_page; + int memmap_page_valid; if (!pfn_valid(start_pfn)) return; @@ -133,9 +134,21 @@ static void register_page_bootmem_info_section(unsigned long start_pfn) mapsize = sizeof(struct page) * PAGES_PER_SECTION; mapsize = PAGE_ALIGN(mapsize) >> PAGE_SHIFT; - /* remember memmap's page */ - for (i = 0; i < mapsize; i++, page++) - get_page_bootmem(section_nr, page, SECTION_INFO); + page_mapsize = PAGE_SIZE/sizeof(struct page); + + /* remember memmap's page, except those that reference only holes */ + for (i = 0; i < mapsize; i++, page++) { + memmap_page_valid = 0; + page_page = __va(page_to_pfn(page) << PAGE_SHIFT); + for (j = 0; j < page_mapsize; j++, page_page++) { + if (early_pfn_valid(page_to_pfn(page_page))) { + memmap_page_valid = 1; + break; + } + } + if (memmap_page_valid) + get_page_bootmem(section_nr, page, SECTION_INFO); + } usemap = __nr_to_section(section_nr)->pageblock_flags; page = virt_to_page(usemap); @@ -591,6 +604,54 @@ int __ref add_memory(int nid, u64 start, u64 size) } EXPORT_SYMBOL_GPL(add_memory); +int __ref physical_remove_memory(u64 start, u64 size) +{ + int ret; + struct resource *res, *res_old; + res = kzalloc(sizeof(struct resource), GFP_KERNEL); + BUG_ON(!res); + + ret = arch_physical_remove_memory(start, size); + if (!ret) { + kfree(res); + return 0; + } + + res->name = "System RAM"; + res->start = start; + res->end = start + size - 1; + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; + + res_old = locate_resource(&iomem_resource, res); + if (res_old) { + release_resource(res_old); + if (PageSlab(virt_to_head_page(res_old))) + kfree(res_old); + } + kfree(res); + + return ret; +} +EXPORT_SYMBOL_GPL(physical_remove_memory); + +int __ref physical_active_memory(u64 start, u64 size) +{ + int ret; + + ret = arch_physical_active_memory(start, size); + return ret; +} +EXPORT_SYMBOL_GPL(physical_active_memory); + +int __ref physical_low_power_memory(u64 start, u64 size) +{ + int ret; + + ret = arch_physical_low_power_memory(start, size); + return ret; +} +EXPORT_SYMBOL_GPL(physical_low_power_memory); + #ifdef CONFIG_MEMORY_HOTREMOVE /* * A free page on the buddy free lists (not the per-cpu lists) has PageBuddy @@ -927,6 +988,7 @@ int remove_memory(u64 start, u64 size) end_pfn = start_pfn + PFN_DOWN(size); return offline_pages(start_pfn, end_pfn, 120 * HZ); } + #else int remove_memory(u64 start, u64 size) { From ceeeaae6f74d7642b9f84025a352f492536aa586 Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Fri, 18 Mar 2011 11:15:47 +0800 Subject: [PATCH 2255/2556] rcu: introduce kfree_rcu() Many rcu callbacks functions just call kfree() on the base structure. These functions are trivial, but their size adds up, and furthermore when they are used in a kernel module, that module must invoke the high-latency rcu_barrier() function at module-unload time. The kfree_rcu() function introduced by this commit addresses this issue. Rather than encoding a function address in the embedded rcu_head structure, kfree_rcu() instead encodes the offset of the rcu_head structure within the base structure. Because the functions are not allowed in the low-order 4096 bytes of kernel virtual memory, offsets up to 4095 bytes can be accommodated. If the offset is larger than 4095 bytes, a compile-time error will be generated in __kfree_rcu(). If this error is triggered, you can either fall back to use of call_rcu() or rearrange the structure to position the rcu_head structure into the first 4096 bytes. Note that the allowable offset might decrease in the future, for example, to allow something like kmem_cache_free_rcu(). The new kfree_rcu() function can replace code as follows: call_rcu(&p->rcu, simple_kfree_callback); where "simple_kfree_callback()" might be defined as follows: void simple_kfree_callback(struct rcu_head *p) { struct foo *q = container_of(p, struct foo, rcu); kfree(q); } with the following: kfree_rcu(&p->rcu, rcu); Note that the "rcu" is the name of a field in the structure being freed. The reason for using this rather than passing in a pointer to the base structure is that the above approach allows better type checking. This commit is based on earlier work by Lai Jiangshan and Manfred Spraul: Lai's V1 patch: http://lkml.org/lkml/2008/9/18/1 Manfred's patch: http://lkml.org/lkml/2009/1/2/115 Signed-off-by: Lai Jiangshan Signed-off-by: Manfred Spraul Signed-off-by: Paul E. McKenney Reviewed-by: David Howells Reviewed-by: Josh Triplett --- include/linux/rcupdate.h | 56 ++++++++++++++++++++++++++++++++++++++++ kernel/rcutiny.c | 2 +- kernel/rcutree.c | 2 +- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index af5614856285d..70c932fa675c0 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -777,4 +777,60 @@ static inline void debug_rcu_head_unqueue(struct rcu_head *head) } #endif /* #else !CONFIG_DEBUG_OBJECTS_RCU_HEAD */ +static __always_inline bool __is_kfree_rcu_offset(unsigned long offset) +{ + return offset < 4096; +} + +static __always_inline +void __kfree_rcu(struct rcu_head *head, unsigned long offset) +{ + typedef void (*rcu_callback)(struct rcu_head *); + + BUILD_BUG_ON(!__builtin_constant_p(offset)); + + /* See the kfree_rcu() header comment. */ + BUILD_BUG_ON(!__is_kfree_rcu_offset(offset)); + + call_rcu(head, (rcu_callback)offset); +} + +extern void kfree(const void *); + +static inline void __rcu_reclaim(struct rcu_head *head) +{ + unsigned long offset = (unsigned long)head->func; + + if (__is_kfree_rcu_offset(offset)) + kfree((void *)head - offset); + else + head->func(head); +} + +/** + * kfree_rcu() - kfree an object after a grace period. + * @ptr: pointer to kfree + * @rcu_head: the name of the struct rcu_head within the type of @ptr. + * + * Many rcu callbacks functions just call kfree() on the base structure. + * These functions are trivial, but their size adds up, and furthermore + * when they are used in a kernel module, that module must invoke the + * high-latency rcu_barrier() function at module-unload time. + * + * The kfree_rcu() function handles this issue. Rather than encoding a + * function address in the embedded rcu_head structure, kfree_rcu() instead + * encodes the offset of the rcu_head structure within the base structure. + * Because the functions are not allowed in the low-order 4096 bytes of + * kernel virtual memory, offsets up to 4095 bytes can be accommodated. + * If the offset is larger than 4095 bytes, a compile-time error will + * be generated in __kfree_rcu(). If this error is triggered, you can + * either fall back to use of call_rcu() or rearrange the structure to + * position the rcu_head structure into the first 4096 bytes. + * + * Note that the allowable offset might decrease in the future, for example, + * to allow something like kmem_cache_free_rcu(). + */ +#define kfree_rcu(ptr, rcu_head) \ + __kfree_rcu(&((ptr)->rcu_head), offsetof(typeof(*(ptr)), rcu_head)) + #endif /* __LINUX_RCUPDATE_H */ diff --git a/kernel/rcutiny.c b/kernel/rcutiny.c index 0c343b9a46d56..4d60fbc9c64cb 100644 --- a/kernel/rcutiny.c +++ b/kernel/rcutiny.c @@ -167,7 +167,7 @@ static void rcu_process_callbacks(struct rcu_ctrlblk *rcp) prefetch(next); debug_rcu_head_unqueue(list); local_bh_disable(); - list->func(list); + __rcu_reclaim(list); local_bh_enable(); list = next; RCU_TRACE(cb_count++); diff --git a/kernel/rcutree.c b/kernel/rcutree.c index dd4aea806f8ef..b3c1aede66d57 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -1143,7 +1143,7 @@ static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp) next = list->next; prefetch(next); debug_rcu_head_unqueue(list); - list->func(list); + __rcu_reclaim(list); list = next; if (++count >= rdp->blimit) break; From 0be987dd4a3795aceb1eb68ca0641eddf427ae89 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 28 Apr 2011 07:23:45 +0200 Subject: [PATCH 2256/2556] rcu: optimize rcutiny rcu_sched_qs() currently calls local_irq_save()/local_irq_restore() up to three times. Remove irq masking from rcu_qsctr_help() / invoke_rcu_kthread() and do it once in rcu_sched_qs() / rcu_bh_qs() This generates smaller code as well. text data bss dec hex filename 2314 156 24 2494 9be kernel/rcutiny.old.o 2250 156 24 2430 97e kernel/rcutiny.new.o Fix an outdated comment for rcu_qsctr_help() Move invoke_rcu_kthread() definition before its use. Signed-off-by: Eric Dumazet Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- kernel/rcutiny.c | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/kernel/rcutiny.c b/kernel/rcutiny.c index 4d60fbc9c64cb..421abfd3641d2 100644 --- a/kernel/rcutiny.c +++ b/kernel/rcutiny.c @@ -40,10 +40,10 @@ static struct task_struct *rcu_kthread_task; static DECLARE_WAIT_QUEUE_HEAD(rcu_kthread_wq); static unsigned long have_rcu_kthread_work; -static void invoke_rcu_kthread(void); /* Forward declarations for rcutiny_plugin.h. */ struct rcu_ctrlblk; +static void invoke_rcu_kthread(void); static void rcu_process_callbacks(struct rcu_ctrlblk *rcp); static int rcu_kthread(void *arg); static void __call_rcu(struct rcu_head *head, @@ -79,26 +79,31 @@ void rcu_exit_nohz(void) #endif /* #ifdef CONFIG_NO_HZ */ /* - * Helper function for rcu_qsctr_inc() and rcu_bh_qsctr_inc(). - * Also disable irqs to avoid confusion due to interrupt handlers + * Helper function for rcu_sched_qs() and rcu_bh_qs(). + * Also irqs are disabled to avoid confusion due to interrupt handlers * invoking call_rcu(). */ static int rcu_qsctr_help(struct rcu_ctrlblk *rcp) { - unsigned long flags; - - local_irq_save(flags); if (rcp->rcucblist != NULL && rcp->donetail != rcp->curtail) { rcp->donetail = rcp->curtail; - local_irq_restore(flags); return 1; } - local_irq_restore(flags); return 0; } +/* + * Wake up rcu_kthread() to process callbacks now eligible for invocation + * or to boost readers. + */ +static void invoke_rcu_kthread(void) +{ + have_rcu_kthread_work = 1; + wake_up(&rcu_kthread_wq); +} + /* * Record an rcu quiescent state. And an rcu_bh quiescent state while we * are at it, given that any rcu quiescent state is also an rcu_bh @@ -106,9 +111,13 @@ static int rcu_qsctr_help(struct rcu_ctrlblk *rcp) */ void rcu_sched_qs(int cpu) { + unsigned long flags; + + local_irq_save(flags); if (rcu_qsctr_help(&rcu_sched_ctrlblk) + rcu_qsctr_help(&rcu_bh_ctrlblk)) invoke_rcu_kthread(); + local_irq_restore(flags); } /* @@ -116,8 +125,12 @@ void rcu_sched_qs(int cpu) */ void rcu_bh_qs(int cpu) { + unsigned long flags; + + local_irq_save(flags); if (rcu_qsctr_help(&rcu_bh_ctrlblk)) invoke_rcu_kthread(); + local_irq_restore(flags); } /* @@ -207,20 +220,6 @@ static int rcu_kthread(void *arg) return 0; /* Not reached, but needed to shut gcc up. */ } -/* - * Wake up rcu_kthread() to process callbacks now eligible for invocation - * or to boost readers. - */ -static void invoke_rcu_kthread(void) -{ - unsigned long flags; - - local_irq_save(flags); - have_rcu_kthread_work = 1; - wake_up(&rcu_kthread_wq); - local_irq_restore(flags); -} - /* * Wait for a grace period to elapse. But it is illegal to invoke * synchronize_sched() from within an RCU read-side critical section. From 9472c27273feeb8d0c8324fc626b468491a1489c Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 22 May 2012 01:10:01 -0500 Subject: [PATCH 2257/2556] update pmem per caf use the older 38 version (before the big update) add system allocator and removes htc hack inherited with last pmem update --- drivers/misc/pmem.c | 368 +++++++++++++++++++++++------------ include/linux/android_pmem.h | 3 +- 2 files changed, 249 insertions(+), 122 deletions(-) diff --git a/drivers/misc/pmem.c b/drivers/misc/pmem.c index d65d507cb1949..c19241a1ae56b 100644 --- a/drivers/misc/pmem.c +++ b/drivers/misc/pmem.c @@ -33,7 +33,6 @@ #include #include #include -#include #define PMEM_MAX_USER_SPACE_DEVICES (10) #define PMEM_MAX_KERNEL_SPACE_DEVICES (2) @@ -54,6 +53,8 @@ #define PMEM_DEBUG 0 #endif +#define SYSTEM_ALLOC_RETRY 10 + /* indicates that a refernce to this file has been taken via get_pmem_file, * the file should not be released until put_pmem_file is called */ #define PMEM_FLAGS_BUSY 0x1 @@ -140,6 +141,14 @@ unsigned long unstable_pmem_start; /* size of unstable PMEM physical memory */ unsigned long unstable_pmem_size; +struct alloc_list { + void *addr; /* physical addr of allocation */ + void *aaddr; /* aligned physical addr */ + unsigned int size; /* total size of allocation */ + unsigned char __iomem *vaddr; /* Virtual addr */ + struct list_head allocs; +}; + struct pmem_info { struct miscdevice dev; /* physical start address of the remaped pmem space */ @@ -204,6 +213,11 @@ struct pmem_info { unsigned short quanta; } *bitm_alloc; } bitmap; + + struct { + unsigned long used; /* Bytes currently allocated */ + struct list_head alist; /* List of allocations */ + } system_mem; } allocator; int id; @@ -310,28 +324,7 @@ static struct pmem_attr pmem_attr_## name = \ #define WO_PMEM_ATTR(name) \ static struct pmem_attr pmem_attr_## name = \ PMEM_ATTR(name, S_IWUSR, NULL, store_pmem_## name) -/*HTC_START*/ -static struct dentry *root = NULL; -u32 misc_msg_pmem_qcom = 0; -static struct dentry *vidc_debugfs_root; - -static struct dentry *vidc_get_debugfs_root(void) -{ - if (vidc_debugfs_root == NULL) - vidc_debugfs_root = debugfs_create_dir("misc", NULL); - return vidc_debugfs_root; -} - -static void vidc_debugfs_file_create(struct dentry *root, const char *name, - u32 *var) -{ - struct dentry *vidc_debugfs_file = - debugfs_create_u32(name, S_IRUGO | S_IWUSR, root, var); - if (!vidc_debugfs_file) - pr_info("%s(): Error creating/opening file %s\n", __func__, name); -} -/*HTC_END*/ static ssize_t show_pmem(struct kobject *kobj, struct attribute *attr, char *buf) @@ -375,6 +368,8 @@ static ssize_t show_pmem_allocator_type(int id, char *buf) return scnprintf(buf, PAGE_SIZE, "%s\n", "Buddy Bestfit"); case PMEM_ALLOCATORTYPE_BITMAP: return scnprintf(buf, PAGE_SIZE, "%s\n", "Bitmap"); + case PMEM_ALLOCATORTYPE_SYSTEM: + return scnprintf(buf, PAGE_SIZE, "%s\n", "System heap"); default: return scnprintf(buf, PAGE_SIZE, "??? Invalid allocator type (%d) for this region! " @@ -549,11 +544,22 @@ static struct attribute *pmem_bitmap_attrs[] = { NULL }; +static struct attribute *pmem_system_attrs[] = { + PMEM_COMMON_SYSFS_ATTRS, + + NULL +}; + static struct kobj_type pmem_bitmap_ktype = { .sysfs_ops = &pmem_ops, .default_attrs = pmem_bitmap_attrs, }; +static struct kobj_type pmem_system_ktype = { + .sysfs_ops = &pmem_ops, + .default_attrs = pmem_system_attrs, +}; + static int get_id(struct file *file) { return MINOR(file->f_dentry->d_inode->i_rdev); @@ -585,7 +591,7 @@ static int has_allocation(struct file *file) * means that file is guaranteed not to be NULL upon entry!! * check is_pmem_file first if not accessed via pmem_file_ops */ struct pmem_data *pdata = file->private_data; - return pdata && pdata->index >= 0; + return pdata && pdata->index != -1; } static int is_master_owner(struct file *file) @@ -601,7 +607,8 @@ static int is_master_owner(struct file *file) master_file = fget_light(data->master_fd, &put_needed); if (master_file && data->master_file == master_file) ret = 1; - fput_light(master_file, put_needed); + if (master_file) + fput_light(master_file, put_needed); return ret; } @@ -727,10 +734,9 @@ static int pmem_free_bitmap(int id, int bitnum) /* caller should hold the lock on arena_mutex! */ int i; char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1]; -/*HTC_START*/ - if (misc_msg_pmem_qcom) - pr_info("[PME][%s] pmem_free_bitmap, bitnum %d\n", pmem[id].name, bitnum); -/*HTC_END*/ + + DLOG("bitnum %d\n", bitnum); + for (i = 0; i < pmem[id].allocator.bitmap.bitmap_allocs; i++) { const int curr_bit = pmem[id].allocator.bitmap.bitm_alloc[i].bit; @@ -743,6 +749,7 @@ static int pmem_free_bitmap(int id, int bitnum) curr_bit, curr_bit + curr_quanta); pmem[id].allocator.bitmap.bitmap_free += curr_quanta; pmem[id].allocator.bitmap.bitm_alloc[i].bit = -1; + pmem[id].allocator.bitmap.bitm_alloc[i].quanta = 0; return 0; } } @@ -753,6 +760,27 @@ static int pmem_free_bitmap(int id, int bitnum) return -1; } +static int pmem_free_system(int id, int index) +{ + /* caller should hold the lock on arena_mutex! */ + struct alloc_list *item; + + DLOG("index %d\n", index); + if (index != 0) + item = (struct alloc_list *)index; + else + return 0; + + if (item->vaddr != NULL) { + iounmap(item->vaddr); + kfree(__va(item->addr)); + list_del(&item->allocs); + kfree(item); + } + + return 0; +} + static int pmem_free_space_bitmap(int id, struct pmem_freespace *fs) { int i, j; @@ -775,12 +803,12 @@ static int pmem_free_space_bitmap(int id, struct pmem_freespace *fs) const int curr_alloc = pmem[id].allocator. bitmap.bitm_alloc[j].bit; if (curr_alloc != -1) { + if (alloc_start == curr_alloc) + alloc_idx = j; if (alloc_start >= curr_alloc) continue; - if (curr_alloc < next_alloc) { + if (curr_alloc < next_alloc) next_alloc = curr_alloc; - alloc_idx = j; - } } } alloc_quanta = pmem[id].allocator.bitmap. @@ -801,6 +829,14 @@ static int pmem_free_space_bitmap(int id, struct pmem_freespace *fs) return 0; } +static int pmem_free_space_system(int id, struct pmem_freespace *fs) +{ + fs->total = pmem[id].size; + fs->largest = pmem[id].size; + + return 0; +} + static void pmem_revoke(struct file *file, struct pmem_data *data); static int pmem_release(struct inode *inode, struct file *file) @@ -885,10 +921,6 @@ static int pmem_open(struct inode *inode, struct file *file) DLOG("pid %u(%s) file %p(%ld) dev %s(id: %d)\n", current->pid, get_task_comm(currtask_name, current), file, file_count(file), get_name(file), id); - /* setup file->private_data to indicate its unmapped */ - /* you can only open a pmem device one time */ - if (file->private_data != NULL) - return -EINVAL; data = kmalloc(sizeof(struct pmem_data), GFP_KERNEL); if (!data) { printk(KERN_ALERT "pmem: %s: unable to allocate memory for " @@ -1150,35 +1182,28 @@ static int pmem_allocator_bitmap(const int id, /* caller should hold the lock on arena_mutex! */ int bitnum, i; unsigned int quanta_needed; -/*HTC_START*/ - if (misc_msg_pmem_qcom) - pr_info("[PME][%s] pmem_allocator_bitmap, len %ld\n", pmem[id].name, len); + DLOG("bitmap id %d, len %ld, align %u\n", id, len, align); if (!pmem[id].allocator.bitmap.bitm_alloc) { - if (misc_msg_pmem_qcom) { - printk(KERN_ALERT "[PME][%s] bitm_alloc not present! \n", - pmem[id].name); - } -/*HTC_END*/ - bitnum = -1; goto leave; +#if PMEM_DEBUG + printk(KERN_ALERT "pmem: bitm_alloc not present! id: %d\n", + id); +#endif + return -1; } quanta_needed = (len + pmem[id].quantum - 1) / pmem[id].quantum; -/*HTC_START*/ - if (misc_msg_pmem_qcom) { - pr_info("[PME][%s] quantum size %u quanta needed %u free %u\n", - pmem[id].name, pmem[id].quantum, quanta_needed, - pmem[id].allocator.bitmap.bitmap_free); - } + DLOG("quantum size %u quanta needed %u free %u id %d\n", + pmem[id].quantum, quanta_needed, + pmem[id].allocator.bitmap.bitmap_free, id); if (pmem[id].allocator.bitmap.bitmap_free < quanta_needed) { - if (misc_msg_pmem_qcom) { - printk(KERN_ALERT "[PME][%s] memory allocation failure. " - "PMEM memory region exhausted." - " Unable to comply with allocation request.\n", pmem[id].name); - } -/*HTC_END*/ - bitnum = -1; goto leave; +#if PMEM_DEBUG + printk(KERN_ALERT "pmem: memory allocation failure. " + "PMEM memory region exhausted, id %d." + " Unable to comply with allocation request.\n", id); +#endif + return -1; } bitnum = reserve_quanta(quanta_needed, id, align); @@ -1198,36 +1223,35 @@ static int pmem_allocator_bitmap(const int id, int j; if (!new_bitmap_allocs) { /* failed sanity check!! */ -/*HTC_START*/ - if (misc_msg_pmem_qcom) { - pr_alert("[PME][%s] pmem: bitmap_allocs number" - " wrapped around to zero! Something " - "is VERY wrong.\n", pmem[id].name); - } - bitnum = -1; goto leave; +#if PMEM_DEBUG + pr_alert("pmem: bitmap_allocs number" + " wrapped around to zero! Something " + "is VERY wrong.\n"); +#endif + return -1; } + if (new_bitmap_allocs > pmem[id].num_entries) { /* failed sanity check!! */ - if (misc_msg_pmem_qcom) { - pr_alert("[PME][%s] pmem: required bitmap_allocs" - " number exceeds maximum entries possible" - " for current quanta\n", pmem[id].name); - } - - bitnum = -1; goto leave; +#if PMEM_DEBUG + pr_alert("pmem: required bitmap_allocs" + " number exceeds maximum entries possible" + " for current quanta\n"); +#endif + return -1; } + temp = krealloc(pmem[id].allocator.bitmap.bitm_alloc, new_bitmap_allocs * sizeof(*pmem[id].allocator.bitmap.bitm_alloc), GFP_KERNEL); if (!temp) { - if (misc_msg_pmem_qcom) { - pr_alert("[PME][%s] can't realloc bitmap_allocs," - " current num bitmap allocs %d\n", - pmem[id].name, pmem[id].allocator.bitmap.bitmap_allocs); - } -/*HTC_END*/ - bitnum = -1; goto leave; +#if PMEM_DEBUG + pr_alert("pmem: can't realloc bitmap_allocs," + "id %d, current num bitmap allocs %d\n", + id, pmem[id].allocator.bitmap.bitmap_allocs); +#endif + return -1; } pmem[id].allocator.bitmap.bitmap_allocs = new_bitmap_allocs; pmem[id].allocator.bitmap.bitm_alloc = temp; @@ -1236,38 +1260,76 @@ static int pmem_allocator_bitmap(const int id, pmem[id].allocator.bitmap.bitm_alloc[j].bit = -1; pmem[id].allocator.bitmap.bitm_alloc[i].quanta = 0; } -/*HTC_START*/ - if (misc_msg_pmem_qcom) { - pr_info("[PME][%s] increased # of allocated regions to %d for \n", - pmem[id].name, pmem[id].allocator.bitmap.bitmap_allocs); - } + + DLOG("increased # of allocated regions to %d for id %d\n", + pmem[id].allocator.bitmap.bitmap_allocs, id); } - if (misc_msg_pmem_qcom) - pr_info("[PME][%s] bitnum %d, bitm_alloc index %d\n", pmem[id].name, bitnum, i); -/*HTC_END*/ + + DLOG("bitnum %d, bitm_alloc index %d\n", bitnum, i); + pmem[id].allocator.bitmap.bitmap_free -= quanta_needed; pmem[id].allocator.bitmap.bitm_alloc[i].bit = bitnum; pmem[id].allocator.bitmap.bitm_alloc[i].quanta = quanta_needed; leave: - if (-1 == bitnum) { - pr_err("[PME][%s] error: pmem_allocator_bitmap failed\n", pmem[id].name); - for (i = 0; i < pmem[id].allocator.bitmap.bitmap_allocs; i++) { - if (pmem[id].allocator.bitmap.bitm_alloc[i].bit != -1) { - /*HTC_START*/ - if (misc_msg_pmem_qcom) { - pr_info("[PME][%s] bitm_alloc[%d].bit: %u bitm_alloc[%d].quanta: %u\n", - pmem[id].name, - i, - pmem[id].allocator.bitmap.bitm_alloc[i].bit, - i, - pmem[id].allocator.bitmap.bitm_alloc[i].quanta - ); - } - /*HTC_END*/ - } + return bitnum; +} + +static int pmem_allocator_system(const int id, + const unsigned long len, + const unsigned int align) +{ + /* caller should hold the lock on arena_mutex! */ + struct alloc_list *list; + unsigned long aligned_len; + int count = SYSTEM_ALLOC_RETRY; + void *buf; + + DLOG("system id %d, len %ld, align %u\n", id, len, align); + + if ((pmem[id].allocator.system_mem.used + len) > pmem[id].size) { + DLOG("requested size would be larger than quota\n"); + return -1; + } + + /* Handle alignment */ + aligned_len = len + align; + + /* Attempt allocation */ + list = kmalloc(sizeof(struct alloc_list), GFP_KERNEL); + if (list == NULL) { + printk(KERN_ERR "pmem: failed to allocate system metadata\n"); + return -1; + } + list->vaddr = NULL; + + buf = NULL; + while ((buf == NULL) && count--) { + buf = kmalloc((aligned_len), GFP_KERNEL); + if (buf == NULL) { + DLOG("pmem: kmalloc %d temporarily failed len= %ld\n", + count, aligned_len); } } - return bitnum; + if (!buf) { + printk(KERN_CRIT "pmem: kmalloc failed for id= %d len= %ld\n", + id, aligned_len); + kfree(list); + return -1; + } + list->size = aligned_len; + list->addr = (void *)__pa(buf); + list->aaddr = (void *)(((unsigned int)(list->addr) + (align - 1)) & + ~(align - 1)); + + if (!pmem[id].cached) + list->vaddr = ioremap(__pa(buf), aligned_len); + else + list->vaddr = ioremap_cached(__pa(buf), aligned_len); + + INIT_LIST_HEAD(&list->allocs); + list_add(&list->allocs, &pmem[id].allocator.system_mem.alist); + + return (int)list; } static pgprot_t pmem_phys_mem_access_prot(struct file *file, pgprot_t vma_prot) @@ -1302,8 +1364,16 @@ static unsigned long pmem_start_addr_bitmap(int id, struct pmem_data *data) return data->index * pmem[id].quantum + pmem[id].base; } +static unsigned long pmem_start_addr_system(int id, struct pmem_data *data) +{ + return (unsigned long)(((struct alloc_list *)(data->index))->aaddr); +} + static void *pmem_start_vaddr(int id, struct pmem_data *data) { + if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_SYSTEM) + return ((struct alloc_list *)(data->index))->vaddr; + else return pmem[id].start_addr(id, data) - pmem[id].base + pmem[id].vbase; } @@ -1341,6 +1411,18 @@ static unsigned long pmem_len_bitmap(int id, struct pmem_data *data) return ret; } +static unsigned long pmem_len_system(int id, struct pmem_data *data) +{ + unsigned long ret = 0; + + mutex_lock(&pmem[id].arena_mutex); + + ret = ((struct alloc_list *)data->index)->size; + mutex_unlock(&pmem[id].arena_mutex); + + return ret; +} + static int pmem_map_garbage(int id, struct vm_area_struct *vma, struct pmem_data *data, unsigned long offset, unsigned long len) @@ -1478,6 +1560,10 @@ static int pmem_mmap(struct file *file, struct vm_area_struct *vma) unsigned long vma_size = vma->vm_end - vma->vm_start; int ret = 0, id = get_id(file); + if (!data) { + pr_err("pmem: Invalid file descriptor, no private data\n"); + return -EINVAL; + } #if PMEM_DEBUG_MSGS char currtask_name[FIELD_SIZEOF(struct task_struct, comm) + 1]; #endif @@ -1506,24 +1592,21 @@ static int pmem_mmap(struct file *file, struct vm_area_struct *vma) goto error; } /* if file->private_data == unalloced, alloc*/ - if (data && data->index == -1) { + if (data->index == -1) { mutex_lock(&pmem[id].arena_mutex); index = pmem[id].allocate(id, vma->vm_end - vma->vm_start, SZ_4K); mutex_unlock(&pmem[id].arena_mutex); - data->index = index; - if (data->index < 0) { + /* either no space was available or an error occured */ + if (index == -1) { pr_err("pmem: mmap unable to allocate memory" "on %s\n", get_name(file)); + ret = -ENOMEM; + goto error; } - } - - /* either no space was available or an error occured */ - if (!has_allocation(file)) { - ret = -ENOMEM; - pr_err("pmem: could not find allocation for map.\n"); - goto error; + /* store the index of a successful allocation */ + data->index = index; } if (pmem[id].len(id, data) < vma_size) { @@ -1767,6 +1850,21 @@ void flush_pmem_file(struct file *file, unsigned long offset, unsigned long len) goto end; vaddr = pmem_start_vaddr(id, data); + + if (pmem[id].allocator_type == PMEM_ALLOCATORTYPE_SYSTEM) { + dmac_flush_range(vaddr, + (void *)((unsigned long)vaddr + + ((struct alloc_list *)(data->index))->size)); +#ifdef CONFIG_OUTER_CACHE + phy_start = pmem_start_addr_system(id, data); + + phy_end = phy_start + + ((struct alloc_list *)(data->index))->size; + + outer_flush_range(phy_start, phy_end); +#endif + goto end; + } /* if this isn't a submmapped file, flush the whole thing */ if (unlikely(!(data->flags & PMEM_FLAGS_CONNECTED))) { dmac_flush_range(vaddr, vaddr + pmem[id].len(id, data)); @@ -1955,6 +2053,11 @@ static int pmem_kapi_free_index_bitmap(const int32_t physaddr, int id) bit_from_paddr(id, physaddr) : -1; } +static int pmem_kapi_free_index_system(const int32_t physaddr, int id) +{ + return 0; +} + int pmem_kfree(const int32_t physaddr) { int i; @@ -2888,6 +2991,35 @@ int pmem_setup(struct android_pmem_platform_data *pdata, pmem[id].size, pmem[id].quantum); break; + case PMEM_ALLOCATORTYPE_SYSTEM: + +#ifdef CONFIG_MEMORY_HOTPLUG + goto err_no_mem; +#endif + + INIT_LIST_HEAD(&pmem[id].allocator.system_mem.alist); + + pmem[id].allocator.system_mem.used = 0; + pmem[id].vbase = NULL; + + if (kobject_init_and_add(&pmem[id].kobj, + &pmem_system_ktype, NULL, + "%s", pdata->name)) + goto out_put_kobj; + + pmem[id].allocate = pmem_allocator_system; + pmem[id].free = pmem_free_system; + pmem[id].free_space = pmem_free_space_system; + pmem[id].kapi_free_index = pmem_kapi_free_index_system; + pmem[id].len = pmem_len_system; + pmem[id].start_addr = pmem_start_addr_system; + pmem[id].num_entries = 0; + pmem[id].quantum = PAGE_SIZE; + + DLOG("system allocator id %d (%s), raw size %lu\n", + id, pdata->name, pmem[id].size); + break; + default: pr_alert("Invalid allocator type (%d) for pmem driver\n", pdata->allocator_type); @@ -2920,7 +3052,8 @@ int pmem_setup(struct android_pmem_platform_data *pdata, if (pmem[id].memory_state == MEMORY_UNSTABLE_NO_MEMORY_ALLOCATED) return 0; - if (!is_kernel_memtype) { + if ((!is_kernel_memtype) && + (pmem[id].allocator_type != PMEM_ALLOCATORTYPE_SYSTEM)) { ioremap_pmem(id); if (pmem[id].vbase == 0) { pr_err("pmem: ioremap failed for device %s\n", @@ -3014,14 +3147,7 @@ static int __init pmem_init(void) pr_err("pmem(%s):kset_create_and_add fail\n", __func__); return -ENOMEM; } -/*HTC_START*/ -root = vidc_get_debugfs_root(); - if (root) { - vidc_debugfs_file_create(root, "misc_msg_pmem_qcom", - (u32 *) &misc_msg_pmem_qcom); - } -/*HTC_END*/ #ifdef CONFIG_MEMORY_HOTPLUG hotplug_memory_notifier(pmem_memory_callback, 0); #endif diff --git a/include/linux/android_pmem.h b/include/linux/android_pmem.h index 0d1e3e0afbc5a..09edec114b4ed 100644 --- a/include/linux/android_pmem.h +++ b/include/linux/android_pmem.h @@ -52,8 +52,8 @@ * start of the mapped gpu regs (the vaddr returned by mmap) as the argument. */ #define HW3D_REVOKE_GPU _IOW(PMEM_IOCTL_MAGIC, 8, unsigned int) -#define PMEM_CACHE_FLUSH _IOW(PMEM_IOCTL_MAGIC, 8, unsigned int) #define HW3D_GRANT_GPU _IOW(PMEM_IOCTL_MAGIC, 9, unsigned int) +#define HW3D_WAIT_FOR_INTERRUPT _IOW(PMEM_IOCTL_MAGIC, 10, unsigned int) #define PMEM_CLEAN_INV_CACHES _IOW(PMEM_IOCTL_MAGIC, 11, unsigned int) #define PMEM_CLEAN_CACHES _IOW(PMEM_IOCTL_MAGIC, 12, unsigned int) @@ -101,6 +101,7 @@ enum pmem_allocator_type { * defined */ PMEM_ALLOCATORTYPE_BITMAP = 0, /* forced to be zero here */ + PMEM_ALLOCATORTYPE_SYSTEM, PMEM_ALLOCATORTYPE_ALLORNOTHING, PMEM_ALLOCATORTYPE_BUDDYBESTFIT, From 52e40d9ba3e463a6456f8e36c4501b1584909cd7 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 22 May 2012 01:52:43 -0500 Subject: [PATCH 2258/2556] msm: remove support for hw3d fixes warning: In file included from drivers/video/msm/mdp_ppp.c:22: include/linux/android_pmem.h:56:1: warning: "HW3D_WAIT_FOR_INTERRUPT" redefined In file included from drivers/video/msm/mdp_ppp.c:19: include/linux/msm_hw3d.h:27:1: warning: this is the location of the previous definition also sets put_img back to static --- arch/arm/mach-msm/Kconfig | 8 - arch/arm/mach-msm/Makefile | 1 - arch/arm/mach-msm/hw3d.c | 889 ------------------------------------ drivers/video/msm/mdp_ppp.c | 17 +- include/linux/msm_hw3d.h | 61 --- 5 files changed, 5 insertions(+), 971 deletions(-) delete mode 100644 arch/arm/mach-msm/hw3d.c delete mode 100644 include/linux/msm_hw3d.h diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index f977a069fe906..b3993683e12bc 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -648,14 +648,6 @@ config AXI_SCREEN_POLICY help Simple AXI scaling based on screen ON/OFF and PWRC. -config MSM_HW3D - tristate "MSM Hardware 3D Register Driver" - depends on EARLYSUSPEND - default y - help - Provides access to registers needed by the userspace OpenGL|ES - library. - config MSM_ADSP depends on ARCH_MSM7X00A tristate "MSM ADSP driver" diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index f381929de1c10..c768bd1d45d33 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -64,7 +64,6 @@ obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_time_remote.o obj-$(CONFIG_MSM_ADSP) += qdsp5/ obj-$(CONFIG_MSM_QDSP5V2) += qdsp5v2/ obj-$(CONFIG_MSM_QDSP6) += qdsp6/ -obj-$(CONFIG_MSM_HW3D) += hw3d.o obj-$(CONFIG_MSM_REMOTE_SPINLOCK) += remote_spinlock.o obj-$(CONFIG_MSM_SSBI) += ssbi.o ifdef CONFIG_PM diff --git a/arch/arm/mach-msm/hw3d.c b/arch/arm/mach-msm/hw3d.c deleted file mode 100644 index 05d562129e613..0000000000000 --- a/arch/arm/mach-msm/hw3d.c +++ /dev/null @@ -1,889 +0,0 @@ -/* arch/arm/mach-msm/hw3d.c - * - * Register/Interrupt access for userspace 3D library. - * - * Copyright (C) 2007 Google, Inc. - * Author: Brian Swetland - * Heavily modified: Dima Zavin - * - * 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. - * - */ - -/* #define DEBUG */ -/* #define VERBOSE */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#if defined(VERBOSE) -#define VDBG(x...) pr_debug(x) -#else -#define VDBG(x...) do {} while(0) -#endif - -struct mem_region { - unsigned long pbase; - unsigned long size; - void __iomem *vbase; -}; - -/* Device minor numbers for master and client */ -#define MINOR_MASTER 0 -#define MINOR_CLIENT 1 - -struct hw3d_info { - dev_t devno; - struct cdev master_cdev; - struct cdev client_cdev; - - struct clk *grp_clk; - struct clk *imem_clk; - int irq; - - struct mem_region regions[HW3D_NUM_REGIONS]; - - wait_queue_head_t irq_wq; - bool irq_pending; - bool irq_en; - bool suspending; - bool revoking; - bool enabled; - - struct timer_list revoke_timer; - wait_queue_head_t revoke_wq; - wait_queue_head_t revoke_done_wq; - unsigned int waiter_cnt; - - spinlock_t lock; - - struct file *client_file; - struct task_struct *client_task; - - struct early_suspend early_suspend; - struct wake_lock wake_lock; -}; -static struct hw3d_info *hw3d_info; - -struct hw3d_data { - struct vm_area_struct *vmas[HW3D_NUM_REGIONS]; - struct mutex mutex; - bool closing; -}; - -#define REGION_PAGE_ID(addr) \ - ((((uint32_t)(addr)) >> (28 - PAGE_SHIFT)) & 0xf) -#define REGION_PAGE_OFFS(addr) \ - ((((uint32_t)(addr)) & ~(0xf << (28 - PAGE_SHIFT)))) - -static int hw3d_open(struct inode *, struct file *); -static int hw3d_release(struct inode *, struct file *); -static int hw3d_mmap(struct file *, struct vm_area_struct *); -static int hw3d_flush(struct file *, fl_owner_t); -static long hw3d_ioctl(struct file *, unsigned int, unsigned long); - -static void hw3d_vma_open(struct vm_area_struct *); -static void hw3d_vma_close(struct vm_area_struct *); - -static struct file_operations hw3d_fops = { - .open = hw3d_open, - .release = hw3d_release, - .mmap = hw3d_mmap, - .flush = hw3d_flush, - .unlocked_ioctl = hw3d_ioctl, -}; - -static struct vm_operations_struct hw3d_vm_ops = { - .open = hw3d_vma_open, - .close = hw3d_vma_close, -}; - -static bool is_master(struct hw3d_info *info, struct file *file) -{ - int fmin = MINOR(file->f_dentry->d_inode->i_rdev); - return fmin == MINOR(info->master_cdev.dev); -} - -static bool is_client(struct hw3d_info *info, struct file *file) -{ - int fmin = MINOR(file->f_dentry->d_inode->i_rdev); - return fmin == MINOR(info->client_cdev.dev); -} - -inline static void locked_hw3d_irq_disable(struct hw3d_info *info) -{ - if (info->irq_en) { - disable_irq_nosync(info->irq); - info->irq_en = 0; - } -} - -inline static void locked_hw3d_irq_enable(struct hw3d_info *info) -{ - if (!info->irq_en) { - enable_irq(info->irq); - info->irq_en = 1; - } -} - -static void hw3d_disable_interrupt(struct hw3d_info *info) -{ - unsigned long flags; - - spin_lock_irqsave(&info->lock, flags); - locked_hw3d_irq_disable(info); - spin_unlock_irqrestore(&info->lock, flags); -} - -static irqreturn_t hw3d_irq_handler(int irq, void *data) -{ - struct hw3d_info *info = data; - unsigned long flags; - - spin_lock_irqsave(&info->lock, flags); - locked_hw3d_irq_disable(info); - info->irq_pending = 1; - spin_unlock_irqrestore(&info->lock, flags); - - wake_up(&info->irq_wq); - - return IRQ_HANDLED; -} - -static long hw3d_wait_for_interrupt(struct hw3d_info *info, struct file *filp) -{ - struct hw3d_data *data = filp->private_data; - unsigned long flags; - int ret; - - if (is_master(info, filp)) { - pr_err("%s: cannot wait for irqs on master node\n", __func__); - return -EPERM; - } - - for (;;) { - spin_lock_irqsave(&info->lock, flags); - if (info->irq_pending) { - info->irq_pending = 0; - spin_unlock_irqrestore(&info->lock, flags); - return 0; - } - locked_hw3d_irq_enable(info); - spin_unlock_irqrestore(&info->lock, flags); - - ret = wait_event_interruptible(info->irq_wq, - info->irq_pending || - info->revoking || - data->closing); - /* always make sure the irq gets disabled */ - if (ret == 0 && !info->irq_pending) - ret = -EPIPE; - if (ret < 0) { - hw3d_disable_interrupt(info); - return ret; - } - } - - return 0; -} - -static long hw3d_wait_for_revoke(struct hw3d_info *info, struct file *filp) -{ - struct hw3d_data *data = filp->private_data; - int ret; - - if (is_master(info, filp)) { - pr_err("%s: cannot revoke on master node\n", __func__); - return -EPERM; - } - - ret = wait_event_interruptible(info->revoke_wq, - info->revoking || - data->closing); - if (ret == 0 && data->closing) - ret = -EPIPE; - if (ret < 0) - return ret; - return 0; -} - -static void locked_hw3d_client_done(struct hw3d_info *info, int had_timer) -{ - if (info->enabled) { - pr_debug("hw3d: was enabled\n"); - info->enabled = 0; - clk_disable(info->grp_clk); - clk_disable(info->imem_clk); - } - info->revoking = 0; - - /* double check that the irqs are disabled */ - locked_hw3d_irq_disable(info); - - if (had_timer) - wake_unlock(&info->wake_lock); - wake_up(&info->revoke_done_wq); -} - -static void do_force_revoke(struct hw3d_info *info) -{ - unsigned long flags; - - /* at this point, the task had a chance to relinquish the gpu, but - * it hasn't. So, we kill it */ - spin_lock_irqsave(&info->lock, flags); - pr_debug("hw3d: forcing revoke\n"); - locked_hw3d_irq_disable(info); - if (info->client_task) { - pr_info("hw3d: force revoke from pid=%d\n", - info->client_task->pid); - force_sig(SIGKILL, info->client_task); - put_task_struct(info->client_task); - info->client_task = NULL; - } - locked_hw3d_client_done(info, 1); - pr_debug("hw3d: done forcing revoke\n"); - spin_unlock_irqrestore(&info->lock, flags); -} - -#define REVOKE_TIMEOUT (2 * HZ) -static void locked_hw3d_revoke(struct hw3d_info *info) -{ - /* force us to wait to suspend until the revoke is done. If the - * user doesn't release the gpu, the timer will turn off the gpu, - * and force kill the process. */ - wake_lock(&info->wake_lock); - info->revoking = 1; - wake_up(&info->revoke_wq); - mod_timer(&info->revoke_timer, jiffies + REVOKE_TIMEOUT); -} - -bool is_msm_hw3d_file(struct file *file) -{ - struct hw3d_info *info = hw3d_info; - if (MAJOR(file->f_dentry->d_inode->i_rdev) == MAJOR(info->devno) && - (is_master(info, file) || is_client(info, file))) - return 1; - return 0; -} - -void put_msm_hw3d_file(struct file *file) -{ - if (!is_msm_hw3d_file(file)) - return; - fput(file); -} - -int get_msm_hw3d_file(int fd, uint32_t *offs, unsigned long *pbase, - unsigned long *len, struct file **filp) -{ - struct hw3d_info *info = hw3d_info; - struct file *file; - struct hw3d_data *data; - uint32_t offset = HW3D_OFFSET_IN_REGION(*offs); - int region = HW3D_REGION_ID(*offs); - int ret = 0; - - if (unlikely(region >= HW3D_NUM_REGIONS)) { - VDBG("hw3d: invalid region %d requested\n", region); - return -EINVAL; - } else if (unlikely(offset >= info->regions[region].size)) { - VDBG("hw3d: offset %08x outside of the requested region %d\n", - offset, region); - return -EINVAL; - } - - file = fget(fd); - if (unlikely(file == NULL)) { - pr_info("%s: requested data from file descriptor that doesn't " - "exist.", __func__); - return -EINVAL; - } else if (!is_msm_hw3d_file(file)) { - ret = -1; - goto err; - } - - data = file->private_data; - if (unlikely(!data)) { - VDBG("hw3d: invalid file\n"); - ret = -EINVAL; - goto err; - } - - mutex_lock(&data->mutex); - if (unlikely(!data->vmas[region])) { - mutex_unlock(&data->mutex); - VDBG("hw3d: requested hw3d region is not mapped\n"); - ret = -ENOENT; - goto err; - } - - *offs = offset; - *pbase = info->regions[region].pbase; - *filp = file; - *len = data->vmas[region]->vm_end - data->vmas[region]->vm_start; - mutex_unlock(&data->mutex); - return 0; - -err: - fput(file); - return ret; -} - -static int hw3d_flush(struct file *filp, fl_owner_t id) -{ - struct hw3d_info *info = hw3d_info; - struct hw3d_data *data = filp->private_data; - - if (!data) { - pr_err("%s: no private data\n", __func__); - return -EINVAL; - } - - if (is_master(info, filp)) - return 0; - pr_debug("hw3d: closing\n"); - /* releases any blocked ioctls */ - data->closing = 1; - wake_up(&info->revoke_wq); - wake_up(&info->irq_wq); - return 0; -} - -static int should_wakeup(struct hw3d_info *info, unsigned int cnt) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&info->lock, flags); - ret = (cnt != info->waiter_cnt) || - (!info->suspending && !info->client_file); - spin_unlock_irqrestore(&info->lock, flags); - - return ret; -} - -static int locked_open_wait_for_gpu(struct hw3d_info *info, - unsigned long *flags) -{ - unsigned int my_cnt; - int ret; - - my_cnt = ++info->waiter_cnt; - pr_debug("%s: wait_for_open %d\n", __func__, my_cnt); - - /* in case there are other waiters, wake and release them. */ - wake_up(&info->revoke_done_wq); - - if (info->suspending) - pr_info("%s: suspended, waiting (%d %d)\n", __func__, - current->group_leader->pid, current->pid); - if (info->client_file) - pr_info("%s: has client, waiting (%d %d)\n", __func__, - current->group_leader->pid, current->pid); - spin_unlock_irqrestore(&info->lock, *flags); - - ret = wait_event_interruptible(info->revoke_done_wq, - should_wakeup(info, my_cnt)); - - spin_lock_irqsave(&info->lock, *flags); - pr_debug("%s: woke up (%d %d %p)\n", __func__, - info->waiter_cnt, info->suspending, info->client_file); - - if (ret >= 0) { - if (my_cnt != info->waiter_cnt) { - pr_info("%s: someone else asked for gpu after us %d:%d" - "(%d %d)\n", __func__, - current->group_leader->pid, current->pid, - my_cnt, info->waiter_cnt); - ret = -EBUSY; - } else if (info->suspending || info->client_file) { - pr_err("%s: couldn't get the gpu for %d:%d (%d %p)\n", - __func__, current->group_leader->pid, - current->pid, info->suspending, - info->client_file); - ret = -EBUSY; - } else - ret = 0; - } - return ret; -} - -static int hw3d_open(struct inode *inode, struct file *file) -{ - struct hw3d_info *info = hw3d_info; - struct hw3d_data *data; - unsigned long flags; - int ret = 0; - - pr_info("%s: pid %d tid %d opening %s node\n", __func__, - current->group_leader->pid, current->pid, - is_master(info, file) ? "master" : "client"); - - if (file->private_data != NULL) - return -EINVAL; - - data = kzalloc(sizeof(struct hw3d_data), GFP_KERNEL); - if (!data) { - pr_err("%s: unable to allocate memory for hw3d_data.\n", - __func__); - return -ENOMEM; - } - - mutex_init(&data->mutex); - file->private_data = data; - - /* master always succeeds, so we are done */ - if (is_master(info, file)) - return 0; - - spin_lock_irqsave(&info->lock, flags); - if (info->client_file) { - pr_debug("hw3d: have client_file, need revoke\n"); - locked_hw3d_revoke(info); - } - - ret = locked_open_wait_for_gpu(info, &flags); - if (ret < 0) { - spin_unlock_irqrestore(&info->lock, flags); - goto err; - } - pr_info("%s: pid %d tid %d got gpu\n", __func__, - current->group_leader->pid, current->pid); - - info->client_file = file; - get_task_struct(current->group_leader); - info->client_task = current->group_leader; - - /* XXX: we enable these clocks if the client connects.. - * probably not right? Should only turn the clocks on when the user - * tries to map the registers? */ - clk_enable(info->imem_clk); - clk_enable(info->grp_clk); - info->enabled = 1; - - spin_unlock_irqrestore(&info->lock, flags); - return 0; - -err: - file->private_data = NULL; - kfree(data); - return ret; - -} - -static int hw3d_release(struct inode *inode, struct file *file) -{ - struct hw3d_info *info = hw3d_info; - struct hw3d_data *data = file->private_data; - unsigned long flags; - - BUG_ON(!data); - - file->private_data = NULL; - - if (is_master(info, file)) - goto done; - - pr_info("%s: in release for pid=%d tid=%d\n", __func__, - current->group_leader->pid, current->pid); - spin_lock_irqsave(&info->lock, flags); - - if (info->client_task && info->client_task == current->group_leader) { - pr_debug("hw3d: releasing %d\n", info->client_task->pid); - put_task_struct(info->client_task); - info->client_task = NULL; - } - - if (info->client_file && info->client_file == file) { - int pending; - /* this will be true if we are still the "owner" of the gpu */ - pr_debug("hw3d: had file\n"); - pending = del_timer(&info->revoke_timer); - locked_hw3d_client_done(info, pending); - info->client_file = NULL; - } else - pr_warning("hw3d: release without client_file.\n"); - spin_unlock_irqrestore(&info->lock, flags); - -done: - kfree(data); - return 0; -} - -static void hw3d_vma_open(struct vm_area_struct *vma) -{ - /* XXX: should the master be allowed to fork and keep the mappings? */ - - /* TODO: remap garbage page into here. - * - * For now, just pull the mapping. The user shouldn't be forking - * and using it anyway. */ - zap_page_range(vma, vma->vm_start, vma->vm_end - vma->vm_start, NULL); -} - -static void hw3d_vma_close(struct vm_area_struct *vma) -{ - struct file *file = vma->vm_file; - struct hw3d_data *data = file->private_data; - int i; - - pr_debug("hw3d: current %u ppid %u file %p count %ld\n", - current->pid, current->parent->pid, file, file_count(file)); - - BUG_ON(!data); - - mutex_lock(&data->mutex); - for (i = 0; i < HW3D_NUM_REGIONS; ++i) { - if (data->vmas[i] == vma) { - data->vmas[i] = NULL; - goto done; - } - } - pr_warning("%s: vma %p not of ours during vma_close\n", __func__, vma); -done: - mutex_unlock(&data->mutex); -} - -static int hw3d_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct hw3d_info *info = hw3d_info; - struct hw3d_data *data = file->private_data; - unsigned long vma_size = vma->vm_end - vma->vm_start; - int ret = 0; - int region = REGION_PAGE_ID(vma->vm_pgoff); - - if (region >= HW3D_NUM_REGIONS) { - pr_err("%s: Trying to mmap unknown region %d\n", __func__, - region); - return -EINVAL; - } else if (vma_size > info->regions[region].size) { - pr_err("%s: VMA size %ld exceeds region %d size %ld\n", - __func__, vma_size, region, - info->regions[region].size); - return -EINVAL; - } else if (REGION_PAGE_OFFS(vma->vm_pgoff) != 0 || - (vma_size & ~PAGE_MASK)) { - pr_err("%s: Can't remap part of the region %d\n", __func__, - region); - return -EINVAL; - } else if (!is_master(info, file) && - current->group_leader != info->client_task) { - pr_err("%s: current(%d) != client_task(%d)\n", __func__, - current->group_leader->pid, info->client_task->pid); - return -EPERM; - } else if (!is_master(info, file) && - (info->revoking || info->suspending)) { - pr_err("%s: cannot mmap while revoking(%d) or suspending(%d)\n", - __func__, info->revoking, info->suspending); - return -EPERM; - } - - mutex_lock(&data->mutex); - if (data->vmas[region] != NULL) { - pr_err("%s: Region %d already mapped (pid=%d tid=%d)\n", - __func__, region, current->group_leader->pid, - current->pid); - ret = -EBUSY; - goto done; - } - - /* our mappings are always noncached */ -#ifdef pgprot_noncached - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); -#endif - - ret = io_remap_pfn_range(vma, vma->vm_start, - info->regions[region].pbase >> PAGE_SHIFT, - vma_size, vma->vm_page_prot); - if (ret) { - pr_err("%s: Cannot remap page range for region %d!\n", __func__, - region); - ret = -EAGAIN; - goto done; - } - - /* Prevent a malicious client from stealing another client's data - * by forcing a revoke on it and then mmapping the GPU buffers. - */ - if (region != HW3D_REGS) - memset(info->regions[region].vbase, 0, - info->regions[region].size); - - vma->vm_ops = &hw3d_vm_ops; - - /* mark this region as mapped */ - data->vmas[region] = vma; - -done: - mutex_unlock(&data->mutex); - return ret; -} - -static long hw3d_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct hw3d_info *info = hw3d_info; - struct hw3d_region regions[HW3D_NUM_REGIONS]; - int i; - - if (!file->private_data) - return -EINVAL; - - switch (cmd) { - case HW3D_WAIT_FOR_REVOKE: - return hw3d_wait_for_revoke(info, file); - - case HW3D_WAIT_FOR_INTERRUPT: - return hw3d_wait_for_interrupt(info, file); - - case HW3D_GET_REGIONS: - for (i = 0; i < HW3D_NUM_REGIONS; ++i) { - regions[i].phys = info->regions[i].pbase; - regions[i].map_offset = HW3D_REGION_OFFSET(i); - regions[i].len = info->regions[i].size; - } - if (copy_to_user((void __user *)arg, regions, sizeof(regions))) - return -EFAULT; - break; - - default: - return -EINVAL; - } - - return 0; -} - -static void hw3d_early_suspend(struct early_suspend *h) -{ - unsigned long flags; - struct hw3d_info *info; - info = container_of(h, struct hw3d_info, early_suspend); - - spin_lock_irqsave(&info->lock, flags); - info->suspending = 1; - if (info->client_file) { - pr_info("hw3d: Requesting revoke for suspend\n"); - locked_hw3d_revoke(info); - } - spin_unlock_irqrestore(&info->lock, flags); -} - -static void hw3d_late_resume(struct early_suspend *h) -{ - unsigned long flags; - struct hw3d_info *info; - info = container_of(h, struct hw3d_info, early_suspend); - - spin_lock_irqsave(&info->lock, flags); - if (info->suspending) - pr_info("%s: resuming\n", __func__); - info->suspending = 0; - wake_up(&info->revoke_done_wq); - spin_unlock_irqrestore(&info->lock, flags); -} - -static int hw3d_resume(struct platform_device *pdev) -{ - struct hw3d_info *info = platform_get_drvdata(pdev); - unsigned long flags; - - spin_lock_irqsave(&info->lock, flags); - if (info->suspending) - pr_info("%s: resuming\n", __func__); - info->suspending = 0; - spin_unlock_irqrestore(&info->lock, flags); - return 0; -} - -static int __devinit hw3d_probe(struct platform_device *pdev) -{ - struct hw3d_info *info; -#define DEV_MASTER MKDEV(MAJOR(info->devno), MINOR_MASTER) -#define DEV_CLIENT MKDEV(MAJOR(info->devno), MINOR_CLIENT) - struct class *hw3d_class; - struct device *master_dev; - struct device *client_dev; - struct resource *res[HW3D_NUM_REGIONS]; - int i; - int irq; - int ret = 0; - - res[HW3D_REGS] = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "regs"); - res[HW3D_SMI] = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "smi"); - res[HW3D_EBI] = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "ebi"); - irq = platform_get_irq(pdev, 0); - if (!res[HW3D_REGS] || !res[HW3D_SMI] || !res[HW3D_EBI] || irq < 0) { - pr_err("%s: incomplete resources\n", __func__); - return -EINVAL; - } - - info = kzalloc(sizeof(struct hw3d_info), GFP_KERNEL); - if (info == NULL) { - pr_err("%s: Cannot allocate memory for hw3d_info\n", __func__); - ret = -ENOMEM; - goto err_alloc; - } - - info->irq = irq; - wake_lock_init(&info->wake_lock, WAKE_LOCK_SUSPEND, "hw3d_revoke_lock"); - spin_lock_init(&info->lock); - init_waitqueue_head(&info->irq_wq); - init_waitqueue_head(&info->revoke_wq); - init_waitqueue_head(&info->revoke_done_wq); - setup_timer(&info->revoke_timer, - (void (*)(unsigned long))do_force_revoke, - (unsigned long)info); - - platform_set_drvdata(pdev, info); - - info->grp_clk = clk_get(NULL, "grp_clk"); - if (IS_ERR(info->grp_clk)) { - pr_err("%s: Cannot get grp_clk\n", __func__); - ret = PTR_ERR(info->grp_clk); - goto err_get_grp_clk; - } - - info->imem_clk = clk_get(NULL, "imem_clk"); - if (IS_ERR(info->imem_clk)) { - pr_err("%s: Cannot get imem_clk\n", __func__); - ret = PTR_ERR(info->imem_clk); - goto err_get_imem_clk; - } - - for (i = 0; i < HW3D_NUM_REGIONS; ++i) { - info->regions[i].pbase = res[i]->start; - info->regions[i].size = res[i]->end - res[i]->start + 1; - info->regions[i].vbase = ioremap(info->regions[i].pbase, - info->regions[i].size); - if (info->regions[i].vbase == 0) { - pr_err("%s: Cannot remap region %d\n", __func__, i); - goto err_remap_region; - } - } - - hw3d_class = class_create(THIS_MODULE, "msm_hw3d"); - if (IS_ERR(hw3d_class)) - goto err_fail_create_class; - - ret = alloc_chrdev_region(&info->devno, 0, 2, "msm_hw3d"); - if (ret < 0) - goto err_fail_alloc_region; - - /* register the master/client devices */ - master_dev = device_create(hw3d_class, &pdev->dev, - DEV_MASTER, "%s", "msm_hw3dm"); - if (IS_ERR(master_dev)) - goto err_dev_master; - cdev_init(&info->master_cdev, &hw3d_fops); - info->master_cdev.owner = THIS_MODULE; - ret = cdev_add(&info->master_cdev, DEV_MASTER, 1); - if (ret < 0) { - pr_err("%s: Cannot register master device node\n", __func__); - goto err_reg_master; - } - - client_dev = device_create(hw3d_class, &pdev->dev, - DEV_CLIENT, "%s", "msm_hw3dc"); - if (IS_ERR(client_dev)) - goto err_dev_client; - cdev_init(&info->client_cdev, &hw3d_fops); - info->client_cdev.owner = THIS_MODULE; - ret = cdev_add(&info->client_cdev, DEV_CLIENT, 1); - if (ret < 0) { - pr_err("%s: Cannot register client device node\n", __func__); - goto err_reg_client; - } - - info->early_suspend.suspend = hw3d_early_suspend; - info->early_suspend.resume = hw3d_late_resume; - info->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; - register_early_suspend(&info->early_suspend); - - info->irq_en = 1; - ret = request_irq(info->irq, hw3d_irq_handler, IRQF_TRIGGER_HIGH, - "hw3d", info); - if (ret != 0) { - pr_err("%s: Cannot request irq\n", __func__); - goto err_req_irq; - } - hw3d_disable_interrupt(info); - - hw3d_info = info; - - return 0; - -err_req_irq: - unregister_early_suspend(&info->early_suspend); - cdev_del(&info->client_cdev); -err_reg_client: - device_destroy(hw3d_class, DEV_CLIENT); -err_dev_client: - cdev_del(&info->master_cdev); -err_reg_master: - device_destroy(hw3d_class, DEV_MASTER); -err_dev_master: - unregister_chrdev_region(info->devno, 2); -err_fail_alloc_region: - class_unregister(hw3d_class); -err_fail_create_class: -err_remap_region: - for (i = 0; i < HW3D_NUM_REGIONS; ++i) - if (info->regions[i].vbase != 0) - iounmap(info->regions[i].vbase); - clk_put(info->imem_clk); -err_get_imem_clk: - clk_put(info->grp_clk); -err_get_grp_clk: - wake_lock_destroy(&info->wake_lock); - kfree(info); - platform_set_drvdata(pdev, NULL); -err_alloc: - hw3d_info = NULL; - return ret; -} - -static struct platform_driver msm_hw3d_driver = { - .probe = hw3d_probe, - .resume = hw3d_resume, - .driver = { - .name = "msm_hw3d", - .owner = THIS_MODULE, - }, -}; - -static int __init hw3d_init(void) -{ - return platform_driver_register(&msm_hw3d_driver); -} - -device_initcall(hw3d_init); diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c index ea1581a199d25..4c18cfbdd515f 100644 --- a/drivers/video/msm/mdp_ppp.c +++ b/drivers/video/msm/mdp_ppp.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -701,9 +700,6 @@ static int get_img(struct mdp_img *img, struct fb_info *info, if (!get_pmem_file(img->memory_id, start, &vstart, len, filep)) return 0; - else if (!get_msm_hw3d_file(img->memory_id, &img->offset, start, len, - filep)) - return 0; file = fget_light(img->memory_id, &put_needed); if (file == NULL) @@ -719,25 +715,22 @@ static int get_img(struct mdp_img *img, struct fb_info *info, return ret; } -/* + +#if 0 static void put_img(struct file *file) { if (file) { if (is_pmem_file(file)) put_pmem_file(file); - else if (is_msm_hw3d_file(file)) - put_msm_hw3d_file(file); } } -*/ -void put_img(struct file *p_src_file) +#endif + +static void put_img(struct file *p_src_file) { #ifdef CONFIG_ANDROID_PMEM if (p_src_file) put_pmem_file(p_src_file); -#else - if (is_msm_hw3d_file(file)) - put_msm_hw3d_file(file); #endif } diff --git a/include/linux/msm_hw3d.h b/include/linux/msm_hw3d.h deleted file mode 100644 index 9178526952d60..0000000000000 --- a/include/linux/msm_hw3d.h +++ /dev/null @@ -1,61 +0,0 @@ -/* include/linux/msm_hw3d.h - * - * Copyright (C) 2007 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 _MSM_HW3D_H_ -#define _MSM_HW3D_H_ - -#include -#include - -struct hw3d_region; - -#define HW3D_IOCTL_MAGIC 'h' -#define HW3D_WAIT_FOR_REVOKE _IO(HW3D_IOCTL_MAGIC, 0x80) -#define HW3D_WAIT_FOR_INTERRUPT _IO(HW3D_IOCTL_MAGIC, 0x81) -#define HW3D_GET_REGIONS \ - _IOR(HW3D_IOCTL_MAGIC, 0x82, struct hw3d_region *) - -#define HW3D_REGION_OFFSET(id) ((((uint32_t)(id)) & 0xf) << 28) -#define HW3D_REGION_ID(addr) (((uint32_t)(addr) >> 28) & 0xf) -#define HW3D_OFFSET_IN_REGION(addr) ((uint32_t)(addr) & ~(0xfUL << 28)) - -enum { - HW3D_EBI = 0, - HW3D_SMI = 1, - HW3D_REGS = 2, - - HW3D_NUM_REGIONS = HW3D_REGS + 1, -}; - -struct hw3d_region { - unsigned long phys; - unsigned long map_offset; - unsigned long len; -}; - -#ifdef CONFIG_MSM_HW3D -int get_msm_hw3d_file(int fd, uint32_t *offs, unsigned long *pbase, - unsigned long *len, struct file **filp); -void put_msm_hw3d_file(struct file *file); -bool is_msm_hw3d_file(struct file *file); -#else -int get_msm_hw3d_file(int fd, uint32_t *offs, unsigned long *pbase, - unsigned long *len, struct file **filp) { return -1; } -void put_msm_hw3d_file(struct file *file) {} -bool is_msm_hw3d_file(struct file *file) { return false; } -#endif - -#endif /* _MSM_HW3D_H_ */ From 79eb87546e23d0d5e52fa2cf06244ed0bc833d9c Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 22 May 2012 02:02:28 -0500 Subject: [PATCH 2259/2556] configs: evervolv: enable AUTOGROUP --- arch/arm/configs/evervolv_bravo_defconfig | 12 ++++++++---- arch/arm/configs/evervolv_incrediblec_defconfig | 12 ++++++++---- arch/arm/configs/evervolv_mahimahi_defconfig | 7 +++---- arch/arm/configs/evervolv_supersonic_defconfig | 10 +++++++--- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index d99e1635ace42..30ac730c59bf5 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Mon Apr 23 23:59:04 2012 +# Mon May 21 22:17:34 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -91,7 +91,7 @@ CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y # CONFIG_BLK_CGROUP is not set # CONFIG_NAMESPACES is not set -# CONFIG_SCHED_AUTOGROUP is not set +CONFIG_SCHED_AUTOGROUP=y # CONFIG_SYSFS_DEPRECATED is not set CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y @@ -341,7 +341,6 @@ CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y CONFIG_MSM_CPU_FREQ_MAX=998400 CONFIG_MSM_CPU_FREQ_MIN=245000 # CONFIG_AXI_SCREEN_POLICY is not set -# CONFIG_MSM_HW3D is not set CONFIG_HTC_ACOUSTIC_QSD=y CONFIG_MSM_QDSP6=y CONFIG_WIFI_CONTROL_FUNC=y @@ -384,11 +383,15 @@ CONFIG_ARM_THUMBEE=y # CONFIG_CPU_BPREDICT_DISABLE is not set CONFIG_ARM_L1_CACHE_SHIFT=5 CONFIG_ARM_DMA_MEM_BUFFERABLE=y +# CONFIG_VCM is not set +# CONFIG_STRICT_MEMORY_RWX is not set +# CONFIG_RESERVE_FIRST_PAGE is not set CONFIG_CPU_HAS_PMU=y CONFIG_ARM_ERRATA_430973=y CONFIG_ARM_ERRATA_458693=y CONFIG_ARM_ERRATA_460075=y CONFIG_ARM_ERRATA_743622=y +# CONFIG_KSAPI is not set # CONFIG_FIQ_DEBUGGER is not set # @@ -440,6 +443,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_SECCOMP is not set # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_CP_ACCESS is not set # # Boot options @@ -1458,12 +1462,12 @@ CONFIG_S5K3E2FX=y # Graphics support # # CONFIG_DRM is not set +# CONFIG_ION is not set CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set -CONFIG_MSM_KGSL_MMU=y # CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index 0c2807df4b0d5..b4568a31e645f 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Tue Apr 24 00:02:27 2012 +# Mon May 21 22:18:06 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -90,7 +90,7 @@ CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y # CONFIG_BLK_CGROUP is not set # CONFIG_NAMESPACES is not set -# CONFIG_SCHED_AUTOGROUP is not set +CONFIG_SCHED_AUTOGROUP=y # CONFIG_SYSFS_DEPRECATED is not set CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y @@ -342,7 +342,6 @@ CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y CONFIG_MSM_CPU_FREQ_MAX=998000 CONFIG_MSM_CPU_FREQ_MIN=245760 # CONFIG_AXI_SCREEN_POLICY is not set -# CONFIG_MSM_HW3D is not set CONFIG_HTC_ACOUSTIC_QSD=y CONFIG_MSM_QDSP6=y CONFIG_WIFI_CONTROL_FUNC=y @@ -387,11 +386,15 @@ CONFIG_ARM_THUMBEE=y # CONFIG_CPU_BPREDICT_DISABLE is not set CONFIG_ARM_L1_CACHE_SHIFT=5 CONFIG_ARM_DMA_MEM_BUFFERABLE=y +# CONFIG_VCM is not set +# CONFIG_STRICT_MEMORY_RWX is not set +# CONFIG_RESERVE_FIRST_PAGE is not set CONFIG_CPU_HAS_PMU=y # CONFIG_ARM_ERRATA_430973 is not set # CONFIG_ARM_ERRATA_458693 is not set # CONFIG_ARM_ERRATA_460075 is not set # CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_KSAPI is not set # CONFIG_FIQ_DEBUGGER is not set # @@ -443,6 +446,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_SECCOMP is not set # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_CP_ACCESS is not set # # Boot options @@ -1480,12 +1484,12 @@ CONFIG_OV8810=y # Graphics support # # CONFIG_DRM is not set +# CONFIG_ION is not set CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set -CONFIG_MSM_KGSL_MMU=y # CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index 0811553308b72..8093c90bd6d84 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Wed May 16 23:42:48 2012 +# Tue May 22 02:06:04 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -91,7 +91,7 @@ CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y # CONFIG_BLK_CGROUP is not set # CONFIG_NAMESPACES is not set -# CONFIG_SCHED_AUTOGROUP is not set +CONFIG_SCHED_AUTOGROUP=y # CONFIG_SYSFS_DEPRECATED is not set CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y @@ -341,7 +341,6 @@ CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y CONFIG_MSM_CPU_FREQ_MAX=998400 CONFIG_MSM_CPU_FREQ_MIN=245000 # CONFIG_AXI_SCREEN_POLICY is not set -# CONFIG_MSM_HW3D is not set CONFIG_HTC_ACOUSTIC_QSD=y CONFIG_MSM_QDSP6=y CONFIG_WIFI_CONTROL_FUNC=y @@ -1460,12 +1459,12 @@ CONFIG_S5K3E2FX=y # Graphics support # # CONFIG_DRM is not set +# CONFIG_ION is not set CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set -CONFIG_MSM_KGSL_MMU=y # CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index f69d61a5d0220..f864dd86ad5ae 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Tue Apr 24 00:00:24 2012 +# Mon May 21 22:18:23 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -346,7 +346,6 @@ CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y CONFIG_MSM_CPU_FREQ_MAX=998400 CONFIG_MSM_CPU_FREQ_MIN=245000 # CONFIG_AXI_SCREEN_POLICY is not set -# CONFIG_MSM_HW3D is not set CONFIG_HTC_ACOUSTIC_QSD=y CONFIG_MSM_QDSP6=y CONFIG_WIFI_CONTROL_FUNC=y @@ -391,11 +390,15 @@ CONFIG_ARM_THUMBEE=y # CONFIG_CPU_BPREDICT_DISABLE is not set CONFIG_ARM_L1_CACHE_SHIFT=5 CONFIG_ARM_DMA_MEM_BUFFERABLE=y +# CONFIG_VCM is not set +# CONFIG_STRICT_MEMORY_RWX is not set +# CONFIG_RESERVE_FIRST_PAGE is not set CONFIG_CPU_HAS_PMU=y # CONFIG_ARM_ERRATA_430973 is not set # CONFIG_ARM_ERRATA_458693 is not set # CONFIG_ARM_ERRATA_460075 is not set # CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_KSAPI is not set # CONFIG_FIQ_DEBUGGER is not set # @@ -447,6 +450,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_SECCOMP is not set # CONFIG_CC_STACKPROTECTOR is not set # CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_CP_ACCESS is not set # # Boot options @@ -1462,12 +1466,12 @@ CONFIG_S5K3H1GX=y # Graphics support # # CONFIG_DRM is not set +# CONFIG_ION is not set CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set -CONFIG_MSM_KGSL_MMU=y # CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y From 774c649d1debb64f319f70a02b33f0a5f21b0151 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 22 May 2012 13:02:28 -0500 Subject: [PATCH 2260/2556] configs: evervolv unset KALLSYMS reduces kernel size by .15mb --- arch/arm/configs/cayniarb_supersonic_defconfig | 4 +--- arch/arm/configs/evervolv_bravo_defconfig | 4 +--- arch/arm/configs/evervolv_incrediblec_defconfig | 4 +--- arch/arm/configs/evervolv_mahimahi_defconfig | 4 +--- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/arch/arm/configs/cayniarb_supersonic_defconfig b/arch/arm/configs/cayniarb_supersonic_defconfig index 9599e992308e9..233df2be183b4 100644 --- a/arch/arm/configs/cayniarb_supersonic_defconfig +++ b/arch/arm/configs/cayniarb_supersonic_defconfig @@ -113,9 +113,7 @@ CONFIG_EXPERT=y CONFIG_EMBEDDED=y CONFIG_UID16=y # CONFIG_SYSCTL_SYSCALL is not set -CONFIG_KALLSYMS=y -# CONFIG_KALLSYMS_ALL is not set -# CONFIG_KALLSYMS_EXTRA_PASS is not set +# CONFIG_KALLSYMS is not set CONFIG_HOTPLUG=y CONFIG_PRINTK=y CONFIG_BUG=y diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index 30ac730c59bf5..ec2f001e7234f 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -109,9 +109,7 @@ CONFIG_EXPERT=y CONFIG_EMBEDDED=y CONFIG_UID16=y # CONFIG_SYSCTL_SYSCALL is not set -CONFIG_KALLSYMS=y -# CONFIG_KALLSYMS_ALL is not set -# CONFIG_KALLSYMS_EXTRA_PASS is not set +# CONFIG_KALLSYMS is not set CONFIG_HOTPLUG=y CONFIG_PRINTK=y CONFIG_BUG=y diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index b4568a31e645f..30871259d7a03 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -108,9 +108,7 @@ CONFIG_EXPERT=y CONFIG_EMBEDDED=y CONFIG_UID16=y # CONFIG_SYSCTL_SYSCALL is not set -CONFIG_KALLSYMS=y -# CONFIG_KALLSYMS_ALL is not set -# CONFIG_KALLSYMS_EXTRA_PASS is not set +# CONFIG_KALLSYMS is not set CONFIG_HOTPLUG=y CONFIG_PRINTK=y CONFIG_BUG=y diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index 8093c90bd6d84..34f00e21dbb0a 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -109,9 +109,7 @@ CONFIG_EXPERT=y CONFIG_EMBEDDED=y CONFIG_UID16=y # CONFIG_SYSCTL_SYSCALL is not set -CONFIG_KALLSYMS=y -# CONFIG_KALLSYMS_ALL is not set -# CONFIG_KALLSYMS_EXTRA_PASS is not set +# CONFIG_KALLSYMS is not set CONFIG_HOTPLUG=y CONFIG_PRINTK=y CONFIG_BUG=y From b8aa6b2296c050c6b713c3fb14373f916731f21d Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 22 May 2012 21:09:50 -0500 Subject: [PATCH 2261/2556] kgsl: cleanup --- drivers/gpu/msm/a200_reg.h | 448 ------------------------------------- drivers/gpu/msm/a220_reg.h | 39 ---- 2 files changed, 487 deletions(-) delete mode 100644 drivers/gpu/msm/a200_reg.h delete mode 100644 drivers/gpu/msm/a220_reg.h diff --git a/drivers/gpu/msm/a200_reg.h b/drivers/gpu/msm/a200_reg.h deleted file mode 100644 index 4df6e14cf4dfc..0000000000000 --- a/drivers/gpu/msm/a200_reg.h +++ /dev/null @@ -1,448 +0,0 @@ -/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ -#ifndef __A200_REG_H -#define __A200_REG_H - -enum VGT_EVENT_TYPE { - VS_DEALLOC = 0, - PS_DEALLOC = 1, - VS_DONE_TS = 2, - PS_DONE_TS = 3, - CACHE_FLUSH_TS = 4, - CONTEXT_DONE = 5, - CACHE_FLUSH = 6, - VIZQUERY_START = 7, - VIZQUERY_END = 8, - SC_WAIT_WC = 9, - RST_PIX_CNT = 13, - RST_VTX_CNT = 14, - TILE_FLUSH = 15, - CACHE_FLUSH_AND_INV_TS_EVENT = 20, - ZPASS_DONE = 21, - CACHE_FLUSH_AND_INV_EVENT = 22, - PERFCOUNTER_START = 23, - PERFCOUNTER_STOP = 24, - VS_FETCH_DONE = 27, - FACENESS_FLUSH = 28, -}; - -enum COLORFORMATX { - COLORX_4_4_4_4 = 0, - COLORX_1_5_5_5 = 1, - COLORX_5_6_5 = 2, - COLORX_8 = 3, - COLORX_8_8 = 4, - COLORX_8_8_8_8 = 5, - COLORX_S8_8_8_8 = 6, - COLORX_16_FLOAT = 7, - COLORX_16_16_FLOAT = 8, - COLORX_16_16_16_16_FLOAT = 9, - COLORX_32_FLOAT = 10, - COLORX_32_32_FLOAT = 11, - COLORX_32_32_32_32_FLOAT = 12, - COLORX_2_3_3 = 13, - COLORX_8_8_8 = 14, -}; - -enum SURFACEFORMAT { - FMT_1_REVERSE = 0, - FMT_1 = 1, - FMT_8 = 2, - FMT_1_5_5_5 = 3, - FMT_5_6_5 = 4, - FMT_6_5_5 = 5, - FMT_8_8_8_8 = 6, - FMT_2_10_10_10 = 7, - FMT_8_A = 8, - FMT_8_B = 9, - FMT_8_8 = 10, - FMT_Cr_Y1_Cb_Y0 = 11, - FMT_Y1_Cr_Y0_Cb = 12, - FMT_5_5_5_1 = 13, - FMT_8_8_8_8_A = 14, - FMT_4_4_4_4 = 15, - FMT_10_11_11 = 16, - FMT_11_11_10 = 17, - FMT_DXT1 = 18, - FMT_DXT2_3 = 19, - FMT_DXT4_5 = 20, - FMT_24_8 = 22, - FMT_24_8_FLOAT = 23, - FMT_16 = 24, - FMT_16_16 = 25, - FMT_16_16_16_16 = 26, - FMT_16_EXPAND = 27, - FMT_16_16_EXPAND = 28, - FMT_16_16_16_16_EXPAND = 29, - FMT_16_FLOAT = 30, - FMT_16_16_FLOAT = 31, - FMT_16_16_16_16_FLOAT = 32, - FMT_32 = 33, - FMT_32_32 = 34, - FMT_32_32_32_32 = 35, - FMT_32_FLOAT = 36, - FMT_32_32_FLOAT = 37, - FMT_32_32_32_32_FLOAT = 38, - FMT_32_AS_8 = 39, - FMT_32_AS_8_8 = 40, - FMT_16_MPEG = 41, - FMT_16_16_MPEG = 42, - FMT_8_INTERLACED = 43, - FMT_32_AS_8_INTERLACED = 44, - FMT_32_AS_8_8_INTERLACED = 45, - FMT_16_INTERLACED = 46, - FMT_16_MPEG_INTERLACED = 47, - FMT_16_16_MPEG_INTERLACED = 48, - FMT_DXN = 49, - FMT_8_8_8_8_AS_16_16_16_16 = 50, - FMT_DXT1_AS_16_16_16_16 = 51, - FMT_DXT2_3_AS_16_16_16_16 = 52, - FMT_DXT4_5_AS_16_16_16_16 = 53, - FMT_2_10_10_10_AS_16_16_16_16 = 54, - FMT_10_11_11_AS_16_16_16_16 = 55, - FMT_11_11_10_AS_16_16_16_16 = 56, - FMT_32_32_32_FLOAT = 57, - FMT_DXT3A = 58, - FMT_DXT5A = 59, - FMT_CTX1 = 60, - FMT_DXT3A_AS_1_1_1_1 = 61 -}; - -#define REG_PERF_MODE_CNT 0x0 -#define REG_PERF_STATE_RESET 0x0 -#define REG_PERF_STATE_ENABLE 0x1 -#define REG_PERF_STATE_FREEZE 0x2 - -#define RB_EDRAM_INFO_EDRAM_SIZE_SIZE 4 -#define RB_EDRAM_INFO_EDRAM_MAPPING_MODE_SIZE 2 -#define RB_EDRAM_INFO_UNUSED0_SIZE 8 -#define RB_EDRAM_INFO_EDRAM_RANGE_SIZE 18 - -struct rb_edram_info_t { - unsigned int edram_size:RB_EDRAM_INFO_EDRAM_SIZE_SIZE; - unsigned int edram_mapping_mode:RB_EDRAM_INFO_EDRAM_MAPPING_MODE_SIZE; - unsigned int unused0:RB_EDRAM_INFO_UNUSED0_SIZE; - unsigned int edram_range:RB_EDRAM_INFO_EDRAM_RANGE_SIZE; -}; - -union reg_rb_edram_info { - unsigned int val; - struct rb_edram_info_t f; -}; - -#define RBBM_READ_ERROR_UNUSED0_SIZE 2 -#define RBBM_READ_ERROR_READ_ADDRESS_SIZE 15 -#define RBBM_READ_ERROR_UNUSED1_SIZE 13 -#define RBBM_READ_ERROR_READ_REQUESTER_SIZE 1 -#define RBBM_READ_ERROR_READ_ERROR_SIZE 1 - -struct rbbm_read_error_t { - unsigned int unused0:RBBM_READ_ERROR_UNUSED0_SIZE; - unsigned int read_address:RBBM_READ_ERROR_READ_ADDRESS_SIZE; - unsigned int unused1:RBBM_READ_ERROR_UNUSED1_SIZE; - unsigned int read_requester:RBBM_READ_ERROR_READ_REQUESTER_SIZE; - unsigned int read_error:RBBM_READ_ERROR_READ_ERROR_SIZE; -}; - -union rbbm_read_error_u { - unsigned int val:32; - struct rbbm_read_error_t f; -}; - -#define CP_RB_CNTL_RB_BUFSZ_SIZE 6 -#define CP_RB_CNTL_UNUSED0_SIZE 2 -#define CP_RB_CNTL_RB_BLKSZ_SIZE 6 -#define CP_RB_CNTL_UNUSED1_SIZE 2 -#define CP_RB_CNTL_BUF_SWAP_SIZE 2 -#define CP_RB_CNTL_UNUSED2_SIZE 2 -#define CP_RB_CNTL_RB_POLL_EN_SIZE 1 -#define CP_RB_CNTL_UNUSED3_SIZE 6 -#define CP_RB_CNTL_RB_NO_UPDATE_SIZE 1 -#define CP_RB_CNTL_UNUSED4_SIZE 3 -#define CP_RB_CNTL_RB_RPTR_WR_ENA_SIZE 1 - -struct cp_rb_cntl_t { - unsigned int rb_bufsz:CP_RB_CNTL_RB_BUFSZ_SIZE; - unsigned int unused0:CP_RB_CNTL_UNUSED0_SIZE; - unsigned int rb_blksz:CP_RB_CNTL_RB_BLKSZ_SIZE; - unsigned int unused1:CP_RB_CNTL_UNUSED1_SIZE; - unsigned int buf_swap:CP_RB_CNTL_BUF_SWAP_SIZE; - unsigned int unused2:CP_RB_CNTL_UNUSED2_SIZE; - unsigned int rb_poll_en:CP_RB_CNTL_RB_POLL_EN_SIZE; - unsigned int unused3:CP_RB_CNTL_UNUSED3_SIZE; - unsigned int rb_no_update:CP_RB_CNTL_RB_NO_UPDATE_SIZE; - unsigned int unused4:CP_RB_CNTL_UNUSED4_SIZE; - unsigned int rb_rptr_wr_ena:CP_RB_CNTL_RB_RPTR_WR_ENA_SIZE; -}; - -union reg_cp_rb_cntl { - unsigned int val:32; - struct cp_rb_cntl_t f; -}; - -#define RB_COLOR_INFO__COLOR_FORMAT_MASK 0x0000000fL -#define RB_COPY_DEST_INFO__COPY_DEST_FORMAT__SHIFT 0x00000004 - - -#define SQ_INT_CNTL__PS_WATCHDOG_MASK 0x00000001L -#define SQ_INT_CNTL__VS_WATCHDOG_MASK 0x00000002L - -#define RBBM_INT_CNTL__RDERR_INT_MASK 0x00000001L -#define RBBM_INT_CNTL__DISPLAY_UPDATE_INT_MASK 0x00000002L -#define RBBM_INT_CNTL__GUI_IDLE_INT_MASK 0x00080000L - -#define RBBM_STATUS__CMDFIFO_AVAIL_MASK 0x0000001fL -#define RBBM_STATUS__TC_BUSY_MASK 0x00000020L -#define RBBM_STATUS__HIRQ_PENDING_MASK 0x00000100L -#define RBBM_STATUS__CPRQ_PENDING_MASK 0x00000200L -#define RBBM_STATUS__CFRQ_PENDING_MASK 0x00000400L -#define RBBM_STATUS__PFRQ_PENDING_MASK 0x00000800L -#define RBBM_STATUS__VGT_BUSY_NO_DMA_MASK 0x00001000L -#define RBBM_STATUS__RBBM_WU_BUSY_MASK 0x00004000L -#define RBBM_STATUS__CP_NRT_BUSY_MASK 0x00010000L -#define RBBM_STATUS__MH_BUSY_MASK 0x00040000L -#define RBBM_STATUS__MH_COHERENCY_BUSY_MASK 0x00080000L -#define RBBM_STATUS__SX_BUSY_MASK 0x00200000L -#define RBBM_STATUS__TPC_BUSY_MASK 0x00400000L -#define RBBM_STATUS__SC_CNTX_BUSY_MASK 0x01000000L -#define RBBM_STATUS__PA_BUSY_MASK 0x02000000L -#define RBBM_STATUS__VGT_BUSY_MASK 0x04000000L -#define RBBM_STATUS__SQ_CNTX17_BUSY_MASK 0x08000000L -#define RBBM_STATUS__SQ_CNTX0_BUSY_MASK 0x10000000L -#define RBBM_STATUS__RB_CNTX_BUSY_MASK 0x40000000L -#define RBBM_STATUS__GUI_ACTIVE_MASK 0x80000000L - -#define CP_INT_CNTL__SW_INT_MASK 0x00080000L -#define CP_INT_CNTL__T0_PACKET_IN_IB_MASK 0x00800000L -#define CP_INT_CNTL__OPCODE_ERROR_MASK 0x01000000L -#define CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK 0x02000000L -#define CP_INT_CNTL__RESERVED_BIT_ERROR_MASK 0x04000000L -#define CP_INT_CNTL__IB_ERROR_MASK 0x08000000L -#define CP_INT_CNTL__IB2_INT_MASK 0x20000000L -#define CP_INT_CNTL__IB1_INT_MASK 0x40000000L -#define CP_INT_CNTL__RB_INT_MASK 0x80000000L - -#define MASTER_INT_SIGNAL__MH_INT_STAT 0x00000020L -#define MASTER_INT_SIGNAL__SQ_INT_STAT 0x04000000L -#define MASTER_INT_SIGNAL__CP_INT_STAT 0x40000000L -#define MASTER_INT_SIGNAL__RBBM_INT_STAT 0x80000000L - -#define RB_EDRAM_INFO__EDRAM_SIZE_MASK 0x0000000fL -#define RB_EDRAM_INFO__EDRAM_RANGE_MASK 0xffffc000L - -#define MH_ARBITER_CONFIG__SAME_PAGE_GRANULARITY__SHIFT 0x00000006 -#define MH_ARBITER_CONFIG__L1_ARB_ENABLE__SHIFT 0x00000007 -#define MH_ARBITER_CONFIG__L1_ARB_HOLD_ENABLE__SHIFT 0x00000008 -#define MH_ARBITER_CONFIG__L2_ARB_CONTROL__SHIFT 0x00000009 -#define MH_ARBITER_CONFIG__PAGE_SIZE__SHIFT 0x0000000a -#define MH_ARBITER_CONFIG__TC_REORDER_ENABLE__SHIFT 0x0000000d -#define MH_ARBITER_CONFIG__TC_ARB_HOLD_ENABLE__SHIFT 0x0000000e -#define MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT_ENABLE__SHIFT 0x0000000f -#define MH_ARBITER_CONFIG__IN_FLIGHT_LIMIT__SHIFT 0x00000010 -#define MH_ARBITER_CONFIG__CP_CLNT_ENABLE__SHIFT 0x00000016 -#define MH_ARBITER_CONFIG__VGT_CLNT_ENABLE__SHIFT 0x00000017 -#define MH_ARBITER_CONFIG__TC_CLNT_ENABLE__SHIFT 0x00000018 -#define MH_ARBITER_CONFIG__RB_CLNT_ENABLE__SHIFT 0x00000019 -#define MH_ARBITER_CONFIG__PA_CLNT_ENABLE__SHIFT 0x0000001a - -#define MH_MMU_CONFIG__RB_W_CLNT_BEHAVIOR__SHIFT 0x00000004 -#define MH_MMU_CONFIG__CP_W_CLNT_BEHAVIOR__SHIFT 0x00000006 -#define MH_MMU_CONFIG__CP_R0_CLNT_BEHAVIOR__SHIFT 0x00000008 -#define MH_MMU_CONFIG__CP_R1_CLNT_BEHAVIOR__SHIFT 0x0000000a -#define MH_MMU_CONFIG__CP_R2_CLNT_BEHAVIOR__SHIFT 0x0000000c -#define MH_MMU_CONFIG__CP_R3_CLNT_BEHAVIOR__SHIFT 0x0000000e -#define MH_MMU_CONFIG__CP_R4_CLNT_BEHAVIOR__SHIFT 0x00000010 -#define MH_MMU_CONFIG__VGT_R0_CLNT_BEHAVIOR__SHIFT 0x00000012 -#define MH_MMU_CONFIG__VGT_R1_CLNT_BEHAVIOR__SHIFT 0x00000014 -#define MH_MMU_CONFIG__TC_R_CLNT_BEHAVIOR__SHIFT 0x00000016 -#define MH_MMU_CONFIG__PA_W_CLNT_BEHAVIOR__SHIFT 0x00000018 - -#define CP_RB_CNTL__RB_BUFSZ__SHIFT 0x00000000 -#define CP_RB_CNTL__RB_BLKSZ__SHIFT 0x00000008 -#define CP_RB_CNTL__RB_POLL_EN__SHIFT 0x00000014 -#define CP_RB_CNTL__RB_NO_UPDATE__SHIFT 0x0000001b - -#define RB_COLOR_INFO__COLOR_FORMAT__SHIFT 0x00000000 -#define RB_EDRAM_INFO__EDRAM_MAPPING_MODE__SHIFT 0x00000004 -#define RB_EDRAM_INFO__EDRAM_RANGE__SHIFT 0x0000000e - -#define REG_CP_CSQ_IB1_STAT 0x01FE -#define REG_CP_CSQ_IB2_STAT 0x01FF -#define REG_CP_CSQ_RB_STAT 0x01FD -#define REG_CP_DEBUG 0x01FC -#define REG_CP_IB1_BASE 0x0458 -#define REG_CP_IB1_BUFSZ 0x0459 -#define REG_CP_IB2_BASE 0x045A -#define REG_CP_IB2_BUFSZ 0x045B -#define REG_CP_INT_ACK 0x01F4 -#define REG_CP_INT_CNTL 0x01F2 -#define REG_CP_INT_STATUS 0x01F3 -#define REG_CP_ME_CNTL 0x01F6 -#define REG_CP_ME_RAM_DATA 0x01FA -#define REG_CP_ME_RAM_WADDR 0x01F8 -#define REG_CP_ME_STATUS 0x01F7 -#define REG_CP_PFP_UCODE_ADDR 0x00C0 -#define REG_CP_PFP_UCODE_DATA 0x00C1 -#define REG_CP_QUEUE_THRESHOLDS 0x01D5 -#define REG_CP_RB_BASE 0x01C0 -#define REG_CP_RB_CNTL 0x01C1 -#define REG_CP_RB_RPTR 0x01C4 -#define REG_CP_RB_RPTR_ADDR 0x01C3 -#define REG_CP_RB_RPTR_WR 0x01C7 -#define REG_CP_RB_WPTR 0x01C5 -#define REG_CP_RB_WPTR_BASE 0x01C8 -#define REG_CP_RB_WPTR_DELAY 0x01C6 -#define REG_CP_STAT 0x047F -#define REG_CP_STATE_DEBUG_DATA 0x01ED -#define REG_CP_STATE_DEBUG_INDEX 0x01EC -#define REG_CP_ST_BASE 0x044D -#define REG_CP_ST_BUFSZ 0x044E - -#define REG_CP_PERFMON_CNTL 0x0444 -#define REG_CP_PERFCOUNTER_SELECT 0x0445 -#define REG_CP_PERFCOUNTER_LO 0x0446 -#define REG_CP_PERFCOUNTER_HI 0x0447 - -#define REG_RBBM_PERFCOUNTER1_SELECT 0x0395 -#define REG_RBBM_PERFCOUNTER1_HI 0x0398 -#define REG_RBBM_PERFCOUNTER1_LO 0x0397 - -#define REG_MASTER_INT_SIGNAL 0x03B7 - -#define REG_MH_ARBITER_CONFIG 0x0A40 -#define REG_MH_INTERRUPT_CLEAR 0x0A44 -#define REG_MH_INTERRUPT_MASK 0x0A42 -#define REG_MH_INTERRUPT_STATUS 0x0A43 -#define REG_MH_MMU_CONFIG 0x0040 -#define REG_MH_MMU_INVALIDATE 0x0045 -#define REG_MH_MMU_MPU_BASE 0x0046 -#define REG_MH_MMU_MPU_END 0x0047 -#define REG_MH_MMU_PAGE_FAULT 0x0043 -#define REG_MH_MMU_PT_BASE 0x0042 -#define REG_MH_MMU_TRAN_ERROR 0x0044 -#define REG_MH_MMU_VA_RANGE 0x0041 -#define REG_MH_CLNT_INTF_CTRL_CONFIG1 0x0A54 -#define REG_MH_CLNT_INTF_CTRL_CONFIG2 0x0A55 - -#define REG_PA_CL_VPORT_XSCALE 0x210F -#define REG_PA_CL_VPORT_ZOFFSET 0x2114 -#define REG_PA_CL_VPORT_ZSCALE 0x2113 -#define REG_PA_CL_CLIP_CNTL 0x2204 -#define REG_PA_CL_VTE_CNTL 0x2206 -#define REG_PA_SC_AA_MASK 0x2312 -#define REG_PA_SC_LINE_CNTL 0x2300 -#define REG_PA_SC_SCREEN_SCISSOR_BR 0x200F -#define REG_PA_SC_SCREEN_SCISSOR_TL 0x200E -#define REG_PA_SC_VIZ_QUERY 0x2293 -#define REG_PA_SC_VIZ_QUERY_STATUS 0x0C44 -#define REG_PA_SC_WINDOW_OFFSET 0x2080 -#define REG_PA_SC_WINDOW_SCISSOR_BR 0x2082 -#define REG_PA_SC_WINDOW_SCISSOR_TL 0x2081 -#define REG_PA_SU_FACE_DATA 0x0C86 -#define REG_PA_SU_POINT_SIZE 0x2280 -#define REG_PA_SU_LINE_CNTL 0x2282 -#define REG_PA_SU_POLY_OFFSET_BACK_OFFSET 0x2383 -#define REG_PA_SU_POLY_OFFSET_FRONT_SCALE 0x2380 -#define REG_PA_SU_SC_MODE_CNTL 0x2205 - -#define REG_PC_INDEX_OFFSET 0x2102 - -#define REG_RBBM_CNTL 0x003B -#define REG_RBBM_INT_ACK 0x03B6 -#define REG_RBBM_INT_CNTL 0x03B4 -#define REG_RBBM_INT_STATUS 0x03B5 -#define REG_RBBM_PATCH_RELEASE 0x0001 -#define REG_RBBM_PERIPHID1 0x03F9 -#define REG_RBBM_PERIPHID2 0x03FA -#define REG_RBBM_DEBUG 0x039B -#define REG_RBBM_DEBUG_OUT 0x03A0 -#define REG_RBBM_DEBUG_CNTL 0x03A1 -#define REG_RBBM_PM_OVERRIDE1 0x039C -#define REG_RBBM_PM_OVERRIDE2 0x039D -#define REG_RBBM_READ_ERROR 0x03B3 -#define REG_RBBM_SOFT_RESET 0x003C -#define REG_RBBM_STATUS 0x05D0 - -#define REG_RB_COLORCONTROL 0x2202 -#define REG_RB_COLOR_DEST_MASK 0x2326 -#define REG_RB_COLOR_MASK 0x2104 -#define REG_RB_COPY_CONTROL 0x2318 -#define REG_RB_DEPTHCONTROL 0x2200 -#define REG_RB_EDRAM_INFO 0x0F02 -#define REG_RB_MODECONTROL 0x2208 -#define REG_RB_SURFACE_INFO 0x2000 -#define REG_RB_SAMPLE_POS 0x220a - -#define REG_SCRATCH_ADDR 0x01DD -#define REG_SCRATCH_REG0 0x0578 -#define REG_SCRATCH_REG2 0x057A -#define REG_SCRATCH_UMSK 0x01DC - -#define REG_SQ_CF_BOOLEANS 0x4900 -#define REG_SQ_CF_LOOP 0x4908 -#define REG_SQ_GPR_MANAGEMENT 0x0D00 -#define REG_SQ_INST_STORE_MANAGMENT 0x0D02 -#define REG_SQ_INT_ACK 0x0D36 -#define REG_SQ_INT_CNTL 0x0D34 -#define REG_SQ_INT_STATUS 0x0D35 -#define REG_SQ_PROGRAM_CNTL 0x2180 -#define REG_SQ_PS_PROGRAM 0x21F6 -#define REG_SQ_VS_PROGRAM 0x21F7 -#define REG_SQ_WRAPPING_0 0x2183 -#define REG_SQ_WRAPPING_1 0x2184 - -#define REG_VGT_ENHANCE 0x2294 -#define REG_VGT_INDX_OFFSET 0x2102 -#define REG_VGT_MAX_VTX_INDX 0x2100 -#define REG_VGT_MIN_VTX_INDX 0x2101 - -#define REG_TP0_CHICKEN 0x0E1E -#define REG_TC_CNTL_STATUS 0x0E00 -#define REG_PA_SC_AA_CONFIG 0x2301 -#define REG_VGT_VERTEX_REUSE_BLOCK_CNTL 0x2316 -#define REG_SQ_INTERPOLATOR_CNTL 0x2182 -#define REG_RB_DEPTH_INFO 0x2002 -#define REG_COHER_DEST_BASE_0 0x2006 -#define REG_RB_FOG_COLOR 0x2109 -#define REG_RB_STENCILREFMASK_BF 0x210C -#define REG_PA_SC_LINE_STIPPLE 0x2283 -#define REG_SQ_PS_CONST 0x2308 -#define REG_RB_DEPTH_CLEAR 0x231D -#define REG_RB_SAMPLE_COUNT_CTL 0x2324 -#define REG_SQ_CONSTANT_0 0x4000 -#define REG_SQ_FETCH_0 0x4800 - -#define REG_MH_AXI_ERROR 0xA45 -#define REG_MH_DEBUG_CTRL 0xA4E -#define REG_MH_DEBUG_DATA 0xA4F -#define REG_COHER_BASE_PM4 0xA2A -#define REG_COHER_STATUS_PM4 0xA2B -#define REG_COHER_SIZE_PM4 0xA29 - -#endif /* __A200_REG_H */ diff --git a/drivers/gpu/msm/a220_reg.h b/drivers/gpu/msm/a220_reg.h deleted file mode 100644 index 9542a9bae10c7..0000000000000 --- a/drivers/gpu/msm/a220_reg.h +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Code Aurora Forum, Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ -#ifndef __A205_REG_H -#define __A205_REG_H - -#define REG_LEIA_PC_INDX_OFFSET REG_VGT_INDX_OFFSET -#define REG_LEIA_PC_VERTEX_REUSE_BLOCK_CNTL REG_VGT_VERTEX_REUSE_BLOCK_CNTL -#define REG_LEIA_PC_MAX_VTX_INDX REG_VGT_MAX_VTX_INDX -#define REG_LEIA_GRAS_CONTROL 0x2210 -#define REG_LEIA_VSC_BIN_SIZE 0x0C01 -#define REG_LEIA_VSC_PIPE_DATA_LENGTH_7 0x0C1D - -#endif /*__A205_REG_H */ From b540135305771480d315ce103159d185aa60ff98 Mon Sep 17 00:00:00 2001 From: Eran Mizrahi Date: Fri, 2 Sep 2011 11:58:30 +0300 Subject: [PATCH 2262/2556] [cpufreq] add smartassV2 governor --- Documentation/cpu-freq/governors.txt | 35 ++ drivers/cpufreq/Kconfig | 13 + drivers/cpufreq/Makefile | 1 + drivers/cpufreq/cpufreq_smartass2.c | 868 +++++++++++++++++++++++++++ include/linux/cpufreq.h | 3 + 5 files changed, 920 insertions(+) create mode 100644 drivers/cpufreq/cpufreq_smartass2.c diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index fdf1b2cca0e95..dcc3cdf794dc1 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -29,6 +29,7 @@ Contents: 2.4 Ondemand 2.5 Conservative 2.6 Interactive +2.7 SmartassV2 3. The Governor Interface in the CPUfreq Core @@ -220,6 +221,40 @@ is 85. timer_rate: Sample rate for reevaluating cpu load when the system is not idle. Default is 30000 uS. +2.7 SmartassV2 +--------------- + +The CPUfreq governor "smartassV2", like other governors, aims to balance +performance vs battery life by using low frequencies when load is low and +ramping the frequency when necessary, fast enough to ensure responsiveness. + +The implementation of the governor is roughtly based on the idea of interactive. +The idle loop is used to track when the CPU has idle cycles. The idle loop will +set a relatively high rate timer to sample the load when appropriate, the timer +will measure the load since it was set and schedule a work queue task to do the +actual frequency change when necessary. + +The most important tunable is the "ideal" frequency: this governor will aim +for this frequency, in the sense that it will ramp towards this frequency much +more aggresively than beyond it - both when ramping up from below this frequency +and when ramping down from above this frequency. Still, note, that when load is +low enough the governor should choose the lowest available frequency regardless +of the ideal frequency and similarly when load is consistently high enough the +highest available frequency will be used. + +Smartass also tracks the state of the screen, and when screen is off (a.k.a +sleep or suspended in the terms of this governor) a different ideal frequency +is used. This is the only difference between the screen on and screen off +states. Proper tuning of the awake_ideal_freq and sleep_ideal_freq should +allow both high responsiveness when screen is on and utilizing the low +frequency range when load is low, especially when screen is off. + +Finally, smartass is a highly customizable governor with almost everything +tweakable through the sysfs. For a detailed explaination of each tunable, +please see the inline comments at the begging of the code (smartass2.c). + + + 3. The Governor Interface in the CPUfreq Core ============================================= diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 8dfd9ff915f9a..4391df9a8c19f 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -107,6 +107,12 @@ config CPU_FREQ_DEFAULT_GOV_INTERACTIVE loading your cpufreq low-level hardware driver, using the 'interactive' governor for latency-sensitive workloads. +config CPU_FREQ_DEFAULT_GOV_SMARTASS2 + bool "smartass2" + select CPU_FREQ_GOV_SMARTASS2 + help + Use the CPUFreq governor 'smartassV2' as default. + endchoice config CPU_FREQ_GOV_PERFORMANCE @@ -181,6 +187,13 @@ config CPU_FREQ_GOV_INTERACTIVE If in doubt, say N. +config CPU_FREQ_GOV_SMARTASS2 + tristate "'smartassV2' cpufreq governor" + depends on CPU_FREQ + help + 'smartassV2' - a "smart" governor + If in doubt, say N. + config CPU_FREQ_GOV_CONSERVATIVE tristate "'conservative' cpufreq governor" depends on CPU_FREQ diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 30629f7dc747c..35aa00903236f 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE) += cpufreq_userspace.o obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o +obj-$(CONFIG_CPU_FREQ_GOV_SMARTASS2) += cpufreq_smartass2.o # CPUfreq cross-arch helpers obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o diff --git a/drivers/cpufreq/cpufreq_smartass2.c b/drivers/cpufreq/cpufreq_smartass2.c new file mode 100644 index 0000000000000..36480b8957421 --- /dev/null +++ b/drivers/cpufreq/cpufreq_smartass2.c @@ -0,0 +1,868 @@ +/* + * drivers/cpufreq/cpufreq_smartass2.c + * + * Copyright (C) 2010 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. + * + * Author: Erasmux + * + * Based on the interactive governor By Mike Chan (mike@android.com) + * which was adaptated to 2.6.29 kernel by Nadlabak (pavel@doshaska.net) + * + * SMP support based on mod by faux123 + * + * For a general overview of smartassV2 see the relavent part in + * Documentation/cpu-freq/governors.txt + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/******************** Tunable parameters: ********************/ + +/* + * The "ideal" frequency to use when awake. The governor will ramp up faster + * towards the ideal frequency and slower after it has passed it. Similarly, + * lowering the frequency towards the ideal frequency is faster than below it. + */ +#define DEFAULT_AWAKE_IDEAL_FREQ 768000 +static unsigned int awake_ideal_freq; + +/* + * The "ideal" frequency to use when suspended. + * When set to 0, the governor will not track the suspended state (meaning + * that practically when sleep_ideal_freq==0 the awake_ideal_freq is used + * also when suspended). + */ +#define DEFAULT_SLEEP_IDEAL_FREQ 245000 +static unsigned int sleep_ideal_freq; + +/* + * Freqeuncy delta when ramping up above the ideal freqeuncy. + * Zero disables and causes to always jump straight to max frequency. + * When below the ideal freqeuncy we always ramp up to the ideal freq. + */ +#define DEFAULT_RAMP_UP_STEP 256000 +static unsigned int ramp_up_step; + +/* + * Freqeuncy delta when ramping down below the ideal freqeuncy. + * Zero disables and will calculate ramp down according to load heuristic. + * When above the ideal freqeuncy we always ramp down to the ideal freq. + */ +#define DEFAULT_RAMP_DOWN_STEP 256000 +static unsigned int ramp_down_step; + +/* + * CPU freq will be increased if measured load > max_cpu_load; + */ +#define DEFAULT_MAX_CPU_LOAD 50 +static unsigned long max_cpu_load; + +/* + * CPU freq will be decreased if measured load < min_cpu_load; + */ +#define DEFAULT_MIN_CPU_LOAD 25 +static unsigned long min_cpu_load; + +/* + * The minimum amount of time to spend at a frequency before we can ramp up. + * Notice we ignore this when we are below the ideal frequency. + */ +#define DEFAULT_UP_RATE_US 48000; +static unsigned long up_rate_us; + +/* + * The minimum amount of time to spend at a frequency before we can ramp down. + * Notice we ignore this when we are above the ideal frequency. + */ +#define DEFAULT_DOWN_RATE_US 99000; +static unsigned long down_rate_us; + +/* + * The frequency to set when waking up from sleep. + * When sleep_ideal_freq=0 this will have no effect. + */ +#define DEFAULT_SLEEP_WAKEUP_FREQ 99999999 +static unsigned int sleep_wakeup_freq; + +/* + * Sampling rate, I highly recommend to leave it at 2. + */ +#define DEFAULT_SAMPLE_RATE_JIFFIES 2 +static unsigned int sample_rate_jiffies; + + +/*************** End of tunables ***************/ + + +static void (*pm_idle_old)(void); +static atomic_t active_count = ATOMIC_INIT(0); + +struct smartass_info_s { + struct cpufreq_policy *cur_policy; + struct cpufreq_frequency_table *freq_table; + struct timer_list timer; + u64 time_in_idle; + u64 idle_exit_time; + u64 freq_change_time; + u64 freq_change_time_in_idle; + int cur_cpu_load; + int old_freq; + int ramp_dir; + unsigned int enable; + int ideal_speed; +}; +static DEFINE_PER_CPU(struct smartass_info_s, smartass_info); + +/* Workqueues handle frequency scaling */ +static struct workqueue_struct *up_wq; +static struct workqueue_struct *down_wq; +static struct work_struct freq_scale_work; + +static cpumask_t work_cpumask; +static spinlock_t cpumask_lock; + +static unsigned int suspended; + +#define dprintk(flag,msg...) do { \ + if (debug_mask & flag) printk(KERN_DEBUG msg); \ + } while (0) + +enum { + SMARTASS_DEBUG_JUMPS=1, + SMARTASS_DEBUG_LOAD=2, + SMARTASS_DEBUG_ALG=4 +}; + +/* + * Combination of the above debug flags. + */ +static unsigned long debug_mask; + +static int cpufreq_governor_smartass(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 +static +#endif +struct cpufreq_governor cpufreq_gov_smartass2 = { + .name = "smartassV2", + .governor = cpufreq_governor_smartass, + .max_transition_latency = 9000000, + .owner = THIS_MODULE, +}; + +inline static void smartass_update_min_max(struct smartass_info_s *this_smartass, struct cpufreq_policy *policy, int suspend) { + if (suspend) { + this_smartass->ideal_speed = // sleep_ideal_freq; but make sure it obeys the policy min/max + policy->max > sleep_ideal_freq ? + (sleep_ideal_freq > policy->min ? sleep_ideal_freq : policy->min) : policy->max; + } else { + this_smartass->ideal_speed = // awake_ideal_freq; but make sure it obeys the policy min/max + policy->min < awake_ideal_freq ? + (awake_ideal_freq < policy->max ? awake_ideal_freq : policy->max) : policy->min; + } +} + +inline static void smartass_update_min_max_allcpus(void) { + unsigned int i; + for_each_online_cpu(i) { + struct smartass_info_s *this_smartass = &per_cpu(smartass_info, i); + if (this_smartass->enable) + smartass_update_min_max(this_smartass,this_smartass->cur_policy,suspended); + } +} + +inline static unsigned int validate_freq(struct cpufreq_policy *policy, int freq) { + if (freq > (int)policy->max) + return policy->max; + if (freq < (int)policy->min) + return policy->min; + return freq; +} + +inline static void reset_timer(unsigned long cpu, struct smartass_info_s *this_smartass) { + this_smartass->time_in_idle = get_cpu_idle_time_us(cpu, &this_smartass->idle_exit_time); + mod_timer(&this_smartass->timer, jiffies + sample_rate_jiffies); +} + +inline static void work_cpumask_set(unsigned long cpu) { + unsigned long flags; + spin_lock_irqsave(&cpumask_lock, flags); + cpumask_set_cpu(cpu, &work_cpumask); + spin_unlock_irqrestore(&cpumask_lock, flags); +} + +inline static int work_cpumask_test_and_clear(unsigned long cpu) { + unsigned long flags; + int res = 0; + spin_lock_irqsave(&cpumask_lock, flags); + res = cpumask_test_and_clear_cpu(cpu, &work_cpumask); + spin_unlock_irqrestore(&cpumask_lock, flags); + return res; +} + +inline static int target_freq(struct cpufreq_policy *policy, struct smartass_info_s *this_smartass, + int new_freq, int old_freq, int prefered_relation) { + int index, target; + struct cpufreq_frequency_table *table = this_smartass->freq_table; + + if (new_freq == old_freq) + return 0; + new_freq = validate_freq(policy,new_freq); + if (new_freq == old_freq) + return 0; + + if (table && + !cpufreq_frequency_table_target(policy,table,new_freq,prefered_relation,&index)) + { + target = table[index].frequency; + if (target == old_freq) { + // if for example we are ramping up to *at most* current + ramp_up_step + // but there is no such frequency higher than the current, try also + // to ramp up to *at least* current + ramp_up_step. + if (new_freq > old_freq && prefered_relation==CPUFREQ_RELATION_H + && !cpufreq_frequency_table_target(policy,table,new_freq, + CPUFREQ_RELATION_L,&index)) + target = table[index].frequency; + // simlarly for ramping down: + else if (new_freq < old_freq && prefered_relation==CPUFREQ_RELATION_L + && !cpufreq_frequency_table_target(policy,table,new_freq, + CPUFREQ_RELATION_H,&index)) + target = table[index].frequency; + } + + if (target == old_freq) { + // We should not get here: + // If we got here we tried to change to a validated new_freq which is different + // from old_freq, so there is no reason for us to remain at same frequency. + printk(KERN_WARNING "Smartass: frequency change failed: %d to %d => %d\n", + old_freq,new_freq,target); + return 0; + } + } + else target = new_freq; + + __cpufreq_driver_target(policy, target, prefered_relation); + + dprintk(SMARTASS_DEBUG_JUMPS,"SmartassQ: jumping from %d to %d => %d (%d)\n", + old_freq,new_freq,target,policy->cur); + + return target; +} + +static void cpufreq_smartass_timer(unsigned long cpu) +{ + u64 delta_idle; + u64 delta_time; + int cpu_load; + int old_freq; + u64 update_time; + u64 now_idle; + int queued_work = 0; + struct smartass_info_s *this_smartass = &per_cpu(smartass_info, cpu); + struct cpufreq_policy *policy = this_smartass->cur_policy; + + now_idle = get_cpu_idle_time_us(cpu, &update_time); + old_freq = policy->cur; + + if (this_smartass->idle_exit_time == 0 || update_time == this_smartass->idle_exit_time) + return; + + delta_idle = cputime64_sub(now_idle, this_smartass->time_in_idle); + delta_time = cputime64_sub(update_time, this_smartass->idle_exit_time); + + // If timer ran less than 1ms after short-term sample started, retry. + if (delta_time < 1000) { + if (!timer_pending(&this_smartass->timer)) + reset_timer(cpu,this_smartass); + return; + } + + if (delta_idle > delta_time) + cpu_load = 0; + else + cpu_load = 100 * (unsigned int)(delta_time - delta_idle) / (unsigned int)delta_time; + + dprintk(SMARTASS_DEBUG_LOAD,"smartassT @ %d: load %d (delta_time %llu)\n", + old_freq,cpu_load,delta_time); + + this_smartass->cur_cpu_load = cpu_load; + this_smartass->old_freq = old_freq; + + // Scale up if load is above max or if there where no idle cycles since coming out of idle, + // additionally, if we are at or above the ideal_speed, verify we have been at this frequency + // for at least up_rate_us: + if (cpu_load > max_cpu_load || delta_idle == 0) + { + if (old_freq < policy->max && + (old_freq < this_smartass->ideal_speed || delta_idle == 0 || + cputime64_sub(update_time, this_smartass->freq_change_time) >= up_rate_us)) + { + dprintk(SMARTASS_DEBUG_ALG,"smartassT @ %d ramp up: load %d (delta_idle %llu)\n", + old_freq,cpu_load,delta_idle); + this_smartass->ramp_dir = 1; + work_cpumask_set(cpu); + queue_work(up_wq, &freq_scale_work); + queued_work = 1; + } + else this_smartass->ramp_dir = 0; + } + // Similarly for scale down: load should be below min and if we are at or below ideal + // frequency we require that we have been at this frequency for at least down_rate_us: + else if (cpu_load < min_cpu_load && old_freq > policy->min && + (old_freq > this_smartass->ideal_speed || + cputime64_sub(update_time, this_smartass->freq_change_time) >= down_rate_us)) + { + dprintk(SMARTASS_DEBUG_ALG,"smartassT @ %d ramp down: load %d (delta_idle %llu)\n", + old_freq,cpu_load,delta_idle); + this_smartass->ramp_dir = -1; + work_cpumask_set(cpu); + queue_work(down_wq, &freq_scale_work); + queued_work = 1; + } + else this_smartass->ramp_dir = 0; + + // To avoid unnecessary load when the CPU is already at high load, we don't + // reset ourselves if we are at max speed. If and when there are idle cycles, + // the idle loop will activate the timer. + // Additionally, if we queued some work, the work task will reset the timer + // after it has done its adjustments. + if (!queued_work && old_freq < policy->max) + reset_timer(cpu,this_smartass); +} + +static void cpufreq_idle(void) +{ + struct smartass_info_s *this_smartass = &per_cpu(smartass_info, smp_processor_id()); + struct cpufreq_policy *policy = this_smartass->cur_policy; + + if (!this_smartass->enable) { + pm_idle_old(); + return; + } + + if (policy->cur == policy->min && timer_pending(&this_smartass->timer)) + del_timer(&this_smartass->timer); + + pm_idle_old(); + + if (!timer_pending(&this_smartass->timer)) + reset_timer(smp_processor_id(), this_smartass); +} + +/* We use the same work function to sale up and down */ +static void cpufreq_smartass_freq_change_time_work(struct work_struct *work) +{ + unsigned int cpu; + int new_freq; + int old_freq; + int ramp_dir; + struct smartass_info_s *this_smartass; + struct cpufreq_policy *policy; + unsigned int relation = CPUFREQ_RELATION_L; + for_each_possible_cpu(cpu) { + this_smartass = &per_cpu(smartass_info, cpu); + if (!work_cpumask_test_and_clear(cpu)) + continue; + + ramp_dir = this_smartass->ramp_dir; + this_smartass->ramp_dir = 0; + + old_freq = this_smartass->old_freq; + policy = this_smartass->cur_policy; + + if (old_freq != policy->cur) { + // frequency was changed by someone else? + printk(KERN_WARNING "Smartass: frequency changed by 3rd party: %d to %d\n", + old_freq,policy->cur); + new_freq = old_freq; + } + else if (ramp_dir > 0 && nr_running() > 1) { + // ramp up logic: + if (old_freq < this_smartass->ideal_speed) + new_freq = this_smartass->ideal_speed; + else if (ramp_up_step) { + new_freq = old_freq + ramp_up_step; + relation = CPUFREQ_RELATION_H; + } + else { + new_freq = policy->max; + relation = CPUFREQ_RELATION_H; + } + dprintk(SMARTASS_DEBUG_ALG,"smartassQ @ %d ramp up: ramp_dir=%d ideal=%d\n", + old_freq,ramp_dir,this_smartass->ideal_speed); + } + else if (ramp_dir < 0) { + // ramp down logic: + if (old_freq > this_smartass->ideal_speed) { + new_freq = this_smartass->ideal_speed; + relation = CPUFREQ_RELATION_H; + } + else if (ramp_down_step) + new_freq = old_freq - ramp_down_step; + else { + // Load heuristics: Adjust new_freq such that, assuming a linear + // scaling of load vs. frequency, the load in the new frequency + // will be max_cpu_load: + new_freq = old_freq * this_smartass->cur_cpu_load / max_cpu_load; + if (new_freq > old_freq) // min_cpu_load > max_cpu_load ?! + new_freq = old_freq -1; + } + dprintk(SMARTASS_DEBUG_ALG,"smartassQ @ %d ramp down: ramp_dir=%d ideal=%d\n", + old_freq,ramp_dir,this_smartass->ideal_speed); + } + else { // ramp_dir==0 ?! Could the timer change its mind about a queued ramp up/down + // before the work task gets to run? + // This may also happen if we refused to ramp up because the nr_running()==1 + new_freq = old_freq; + dprintk(SMARTASS_DEBUG_ALG,"smartassQ @ %d nothing: ramp_dir=%d nr_running=%lu\n", + old_freq,ramp_dir,nr_running()); + } + + // do actual ramp up (returns 0, if frequency change failed): + new_freq = target_freq(policy,this_smartass,new_freq,old_freq,relation); + if (new_freq) + this_smartass->freq_change_time_in_idle = + get_cpu_idle_time_us(cpu,&this_smartass->freq_change_time); + + // reset timer: + if (new_freq < policy->max) + reset_timer(cpu,this_smartass); + // if we are maxed out, it is pointless to use the timer + // (idle cycles wake up the timer when the timer comes) + else if (timer_pending(&this_smartass->timer)) + del_timer(&this_smartass->timer); + } +} + +static ssize_t show_debug_mask(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", debug_mask); +} + +static ssize_t store_debug_mask(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0) + debug_mask = input; + return res; +} + +static ssize_t show_up_rate_us(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", up_rate_us); +} + +static ssize_t store_up_rate_us(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0 && input <= 100000000) + up_rate_us = input; + return res; +} + +static ssize_t show_down_rate_us(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", down_rate_us); +} + +static ssize_t store_down_rate_us(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0 && input <= 100000000) + down_rate_us = input; + return res; +} + +static ssize_t show_sleep_ideal_freq(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", sleep_ideal_freq); +} + +static ssize_t store_sleep_ideal_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0) { + sleep_ideal_freq = input; + if (suspended) + smartass_update_min_max_allcpus(); + } + return res; +} + +static ssize_t show_sleep_wakeup_freq(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", sleep_wakeup_freq); +} + +static ssize_t store_sleep_wakeup_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0) + sleep_wakeup_freq = input; + return res; +} + +static ssize_t show_awake_ideal_freq(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", awake_ideal_freq); +} + +static ssize_t store_awake_ideal_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0) { + awake_ideal_freq = input; + if (!suspended) + smartass_update_min_max_allcpus(); + } + return res; +} + +static ssize_t show_sample_rate_jiffies(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", sample_rate_jiffies); +} + +static ssize_t store_sample_rate_jiffies(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input > 0 && input <= 1000) + sample_rate_jiffies = input; + return res; +} + +static ssize_t show_ramp_up_step(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", ramp_up_step); +} + +static ssize_t store_ramp_up_step(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0) + ramp_up_step = input; + return res; +} + +static ssize_t show_ramp_down_step(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", ramp_down_step); +} + +static ssize_t store_ramp_down_step(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0) + ramp_down_step = input; + return res; +} + +static ssize_t show_max_cpu_load(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", max_cpu_load); +} + +static ssize_t store_max_cpu_load(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input > 0 && input <= 100) + max_cpu_load = input; + return res; +} + +static ssize_t show_min_cpu_load(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", min_cpu_load); +} + +static ssize_t store_min_cpu_load(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input > 0 && input < 100) + min_cpu_load = input; + return res; +} + +#define define_global_rw_attr(_name) \ +static struct global_attr _name##_attr = \ + __ATTR(_name, 0644, show_##_name, store_##_name) + +define_global_rw_attr(debug_mask); +define_global_rw_attr(up_rate_us); +define_global_rw_attr(down_rate_us); +define_global_rw_attr(sleep_ideal_freq); +define_global_rw_attr(sleep_wakeup_freq); +define_global_rw_attr(awake_ideal_freq); +define_global_rw_attr(sample_rate_jiffies); +define_global_rw_attr(ramp_up_step); +define_global_rw_attr(ramp_down_step); +define_global_rw_attr(max_cpu_load); +define_global_rw_attr(min_cpu_load); + +static struct attribute * smartass_attributes[] = { + &debug_mask_attr.attr, + &up_rate_us_attr.attr, + &down_rate_us_attr.attr, + &sleep_ideal_freq_attr.attr, + &sleep_wakeup_freq_attr.attr, + &awake_ideal_freq_attr.attr, + &sample_rate_jiffies_attr.attr, + &ramp_up_step_attr.attr, + &ramp_down_step_attr.attr, + &max_cpu_load_attr.attr, + &min_cpu_load_attr.attr, + NULL, +}; + +static struct attribute_group smartass_attr_group = { + .attrs = smartass_attributes, + .name = "smartass", +}; + +static int cpufreq_governor_smartass(struct cpufreq_policy *new_policy, + unsigned int event) +{ + unsigned int cpu = new_policy->cpu; + int rc; + struct smartass_info_s *this_smartass = &per_cpu(smartass_info, cpu); + + switch (event) { + case CPUFREQ_GOV_START: + if ((!cpu_online(cpu)) || (!new_policy->cur)) + return -EINVAL; + + this_smartass->cur_policy = new_policy; + + this_smartass->enable = 1; + + smartass_update_min_max(this_smartass,new_policy,suspended); + + this_smartass->freq_table = cpufreq_frequency_get_table(cpu); + if (!this_smartass->freq_table) + printk(KERN_WARNING "Smartass: no frequency table for cpu %d?!\n",cpu); + + smp_wmb(); + + // Do not register the idle hook and create sysfs + // entries if we have already done so. + if (atomic_inc_return(&active_count) <= 1) { + rc = sysfs_create_group(cpufreq_global_kobject, + &smartass_attr_group); + if (rc) + return rc; + + pm_idle_old = pm_idle; + pm_idle = cpufreq_idle; + } + + if (this_smartass->cur_policy->cur < new_policy->max && !timer_pending(&this_smartass->timer)) + reset_timer(cpu,this_smartass); + + break; + + case CPUFREQ_GOV_LIMITS: + smartass_update_min_max(this_smartass,new_policy,suspended); + + if (this_smartass->cur_policy->cur > new_policy->max) { + dprintk(SMARTASS_DEBUG_JUMPS,"SmartassI: jumping to new max freq: %d\n",new_policy->max); + __cpufreq_driver_target(this_smartass->cur_policy, + new_policy->max, CPUFREQ_RELATION_H); + } + else if (this_smartass->cur_policy->cur < new_policy->min) { + dprintk(SMARTASS_DEBUG_JUMPS,"SmartassI: jumping to new min freq: %d\n",new_policy->min); + __cpufreq_driver_target(this_smartass->cur_policy, + new_policy->min, CPUFREQ_RELATION_L); + } + + if (this_smartass->cur_policy->cur < new_policy->max && !timer_pending(&this_smartass->timer)) + reset_timer(cpu,this_smartass); + + break; + + case CPUFREQ_GOV_STOP: + this_smartass->enable = 0; + smp_wmb(); + del_timer(&this_smartass->timer); + flush_work(&freq_scale_work); + this_smartass->idle_exit_time = 0; + + if (atomic_dec_return(&active_count) <= 1) { + sysfs_remove_group(cpufreq_global_kobject, + &smartass_attr_group); + pm_idle = pm_idle_old; + } + break; + } + + return 0; +} + +static void smartass_suspend(int cpu, int suspend) +{ + struct smartass_info_s *this_smartass = &per_cpu(smartass_info, smp_processor_id()); + struct cpufreq_policy *policy = this_smartass->cur_policy; + unsigned int new_freq; + + if (!this_smartass->enable) + return; + + smartass_update_min_max(this_smartass,policy,suspend); + if (!suspend) { // resume at max speed: + new_freq = validate_freq(policy,sleep_wakeup_freq); + + dprintk(SMARTASS_DEBUG_JUMPS,"SmartassS: awaking at %d\n",new_freq); + + __cpufreq_driver_target(policy, new_freq, + CPUFREQ_RELATION_L); + } else { + // to avoid wakeup issues with quick sleep/wakeup don't change actual frequency when entering sleep + // to allow some time to settle down. Instead we just reset our statistics (and reset the timer). + // Eventually, the timer will adjust the frequency if necessary. + + this_smartass->freq_change_time_in_idle = + get_cpu_idle_time_us(cpu,&this_smartass->freq_change_time); + + dprintk(SMARTASS_DEBUG_JUMPS,"SmartassS: suspending at %d\n",policy->cur); + } + + reset_timer(smp_processor_id(),this_smartass); +} + +static void smartass_early_suspend(struct early_suspend *handler) { + int i; + if (suspended || sleep_ideal_freq==0) // disable behavior for sleep_ideal_freq==0 + return; + suspended = 1; + for_each_online_cpu(i) + smartass_suspend(i,1); +} + +static void smartass_late_resume(struct early_suspend *handler) { + int i; + if (!suspended) // already not suspended so nothing to do + return; + suspended = 0; + for_each_online_cpu(i) + smartass_suspend(i,0); +} + +static struct early_suspend smartass_power_suspend = { + .suspend = smartass_early_suspend, + .resume = smartass_late_resume, +#ifdef CONFIG_MACH_HERO + .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, +#endif +}; + +static int __init cpufreq_smartass_init(void) +{ + unsigned int i; + struct smartass_info_s *this_smartass; + debug_mask = 0; + up_rate_us = DEFAULT_UP_RATE_US; + down_rate_us = DEFAULT_DOWN_RATE_US; + sleep_ideal_freq = DEFAULT_SLEEP_IDEAL_FREQ; + sleep_wakeup_freq = DEFAULT_SLEEP_WAKEUP_FREQ; + awake_ideal_freq = DEFAULT_AWAKE_IDEAL_FREQ; + sample_rate_jiffies = DEFAULT_SAMPLE_RATE_JIFFIES; + ramp_up_step = DEFAULT_RAMP_UP_STEP; + ramp_down_step = DEFAULT_RAMP_DOWN_STEP; + max_cpu_load = DEFAULT_MAX_CPU_LOAD; + min_cpu_load = DEFAULT_MIN_CPU_LOAD; + + spin_lock_init(&cpumask_lock); + + suspended = 0; + + /* Initalize per-cpu data: */ + for_each_possible_cpu(i) { + this_smartass = &per_cpu(smartass_info, i); + this_smartass->enable = 0; + this_smartass->cur_policy = 0; + this_smartass->ramp_dir = 0; + this_smartass->time_in_idle = 0; + this_smartass->idle_exit_time = 0; + this_smartass->freq_change_time = 0; + this_smartass->freq_change_time_in_idle = 0; + this_smartass->cur_cpu_load = 0; + // intialize timer: + init_timer_deferrable(&this_smartass->timer); + this_smartass->timer.function = cpufreq_smartass_timer; + this_smartass->timer.data = i; + work_cpumask_test_and_clear(i); + } + + // Scale up is high priority + up_wq = alloc_workqueue("ksmartass_up", WQ_HIGHPRI, 1); + down_wq = alloc_workqueue("ksmartass_down", 0, 1); + if (!up_wq || !down_wq) + return -ENOMEM; + + INIT_WORK(&freq_scale_work, cpufreq_smartass_freq_change_time_work); + + register_early_suspend(&smartass_power_suspend); + + return cpufreq_register_governor(&cpufreq_gov_smartass2); +} + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 +fs_initcall(cpufreq_smartass_init); +#else +module_init(cpufreq_smartass_init); +#endif + +static void __exit cpufreq_smartass_exit(void) +{ + cpufreq_unregister_governor(&cpufreq_gov_smartass2); + destroy_workqueue(up_wq); + destroy_workqueue(down_wq); +} + +module_exit(cpufreq_smartass_exit); + +MODULE_AUTHOR ("Erasmux"); +MODULE_DESCRIPTION ("'cpufreq_smartass2' - A smart cpufreq governor"); +MODULE_LICENSE ("GPL"); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 6fc1e824cdb83..31f9557e72b7b 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -358,6 +358,9 @@ extern struct cpufreq_governor cpufreq_gov_conservative; #elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE) extern struct cpufreq_governor cpufreq_gov_interactive; #define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_interactive) +#elif defined(CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2) +extern struct cpufreq_governor cpufreq_gov_smartass2; +#define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_smartass2) #endif From 0f409d7d336a428544d64e0b91b5473286a27ef2 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 23 May 2012 19:21:36 -0500 Subject: [PATCH 2263/2556] configs: evervolv build smartassv2 --- arch/arm/configs/evervolv_bravo_defconfig | 2 ++ arch/arm/configs/evervolv_incrediblec_defconfig | 2 ++ arch/arm/configs/evervolv_mahimahi_defconfig | 2 ++ arch/arm/configs/evervolv_supersonic_defconfig | 2 ++ 4 files changed, 8 insertions(+) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index ec2f001e7234f..7893f96dc3b47 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -468,11 +468,13 @@ CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y # CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_SMARTASS2=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y # CONFIG_CPU_IDLE is not set CONFIG_CPU_FREQ_MSM=y diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index 30871259d7a03..b1725229cc98e 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -471,11 +471,13 @@ CONFIG_CPU_FREQ_STAT_DETAILS=y CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_SMARTASS2=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y # CONFIG_CPU_IDLE is not set CONFIG_CPU_FREQ_MSM=y diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index 34f00e21dbb0a..69a0f79cb2523 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -468,11 +468,13 @@ CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y # CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_SMARTASS2=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y # CONFIG_CPU_IDLE is not set CONFIG_CPU_FREQ_MSM=y diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index f864dd86ad5ae..b6eafa315312d 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -477,11 +477,13 @@ CONFIG_CPU_FREQ_STAT_DETAILS=y CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_SMARTASS2=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y # CONFIG_CPU_IDLE is not set CONFIG_CPU_FREQ_MSM=y From 877c036ba8f81e05d10f5920a41c69321264d9df Mon Sep 17 00:00:00 2001 From: Jeff Boody Date: Wed, 9 May 2012 11:36:13 -0600 Subject: [PATCH 2264/2556] base: genlock: remove BIT macro usage The BIT macro is not defined for !_KERNEL_ resulting in compile errors. CRs-fixed: 356263 Signed-off-by: Jeff Boody (cherry picked from commit 0977510e0f72b2f1a24f41d5154c2f1b80c96d9a) Change-Id: Iaf188a1af838c98ca30ce216d7be8ddf85db700c Signed-off-by: Ram Kumar Chakravarthy Chebathini --- include/linux/genlock.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/linux/genlock.h b/include/linux/genlock.h index 60bc84c5363a1..587c49df7444a 100644 --- a/include/linux/genlock.h +++ b/include/linux/genlock.h @@ -1,8 +1,6 @@ #ifndef _GENLOCK_H_ #define _GENLOCK_H_ -#include - #ifdef __KERNEL__ struct genlock; @@ -23,8 +21,8 @@ int genlock_lock(struct genlock_handle *handle, int op, int flags, #define GENLOCK_WRLOCK 1 #define GENLOCK_RDLOCK 2 -#define GENLOCK_NOBLOCK BIT(0) -#define GENLOCK_WRITE_TO_READ BIT(1) +#define GENLOCK_NOBLOCK (1 << 0) +#define GENLOCK_WRITE_TO_READ (1 << 1) struct genlock_lock { int fd; From eccd802b70272c0b84af7eb98780b714e5022f4b Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 24 May 2012 12:05:20 -0500 Subject: [PATCH 2265/2556] kgsl: fastforward -> ics_chocolate 3.8 commit bcf5ae04a9bf744a7a3d119c93d439a682404877 Author: Olav Haugan Date: Tue May 8 08:42:49 2012 -0700 iommu: Fix flags passed to iommu map functions. iommu map functions requires clients to specify whether the mappings should be read only or read-write (write only is not supported). Ensure clients specify the appropriate flags when calling map functions. Change-Id: If42b4b0367b4311aa36d3063979ef04af3a43fc6 Signed-off-by: Olav Haugan --- drivers/gpu/msm/Makefile | 15 +- drivers/gpu/msm/a2xx_reg.h | 36 +- drivers/gpu/msm/adreno.c | 296 ++++++++----- drivers/gpu/msm/adreno.h | 28 +- drivers/gpu/msm/adreno_a2xx.c | 305 ++++++++++---- drivers/gpu/msm/adreno_a2xx_snapshot.c | 356 ++++++++++++++++ drivers/gpu/msm/adreno_a2xx_trace.c | 19 + drivers/gpu/msm/adreno_a2xx_trace.h | 78 ++++ drivers/gpu/msm/adreno_debugfs.c | 98 ----- drivers/gpu/msm/adreno_drawctxt.c | 22 +- drivers/gpu/msm/adreno_drawctxt.h | 8 +- drivers/gpu/msm/adreno_pm4types.h | 20 + drivers/gpu/msm/adreno_postmortem.c | 180 ++------ drivers/gpu/msm/adreno_ringbuffer.c | 36 +- drivers/gpu/msm/adreno_ringbuffer.h | 4 +- drivers/gpu/msm/adreno_snapshot.c | 429 +++++++++++++++++++ drivers/gpu/msm/kgsl.c | 275 ++++++------ drivers/gpu/msm/kgsl.h | 54 ++- drivers/gpu/msm/kgsl_cffdump.c | 41 +- drivers/gpu/msm/kgsl_device.h | 42 +- drivers/gpu/msm/kgsl_drm.c | 7 +- drivers/gpu/msm/kgsl_gpummu.c | 19 +- drivers/gpu/msm/kgsl_iommu.c | 6 +- drivers/gpu/msm/kgsl_mmu.c | 1 + drivers/gpu/msm/kgsl_pwrctrl.c | 487 +++++++++++++-------- drivers/gpu/msm/kgsl_pwrctrl.h | 5 +- drivers/gpu/msm/kgsl_pwrscale.c | 12 +- drivers/gpu/msm/kgsl_pwrscale_idlestats.c | 7 +- drivers/gpu/msm/kgsl_pwrscale_trustzone.c | 32 +- drivers/gpu/msm/kgsl_sharedmem.c | 164 ++++++-- drivers/gpu/msm/kgsl_sharedmem.h | 38 +- drivers/gpu/msm/kgsl_snapshot.c | 489 ++++++++++++++++++++++ drivers/gpu/msm/kgsl_snapshot.h | 259 ++++++++++++ drivers/gpu/msm/kgsl_trace.c | 19 + drivers/gpu/msm/kgsl_trace.h | 263 ++++++++++++ drivers/gpu/msm/z180.c | 53 +-- drivers/gpu/msm/z180.h | 2 + drivers/gpu/msm/z180_trace.c | 20 + drivers/gpu/msm/z180_trace.h | 60 +++ include/linux/msm_kgsl.h | 42 +- 40 files changed, 3389 insertions(+), 938 deletions(-) create mode 100644 drivers/gpu/msm/adreno_a2xx_snapshot.c create mode 100644 drivers/gpu/msm/adreno_a2xx_trace.c create mode 100644 drivers/gpu/msm/adreno_a2xx_trace.h create mode 100644 drivers/gpu/msm/adreno_snapshot.c create mode 100644 drivers/gpu/msm/kgsl_snapshot.c create mode 100644 drivers/gpu/msm/kgsl_snapshot.h create mode 100644 drivers/gpu/msm/kgsl_trace.c create mode 100644 drivers/gpu/msm/kgsl_trace.h create mode 100644 drivers/gpu/msm/z180_trace.c create mode 100644 drivers/gpu/msm/z180_trace.h diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile index b4cd286c86626..7b8f3e633d1bd 100644 --- a/drivers/gpu/msm/Makefile +++ b/drivers/gpu/msm/Makefile @@ -1,30 +1,37 @@ -ccflags-y := -Iinclude/drm +ccflags-y := -Iinclude/drm -Idrivers/gpu/msm msm_kgsl_core-y = \ kgsl.o \ + kgsl_trace.o \ kgsl_sharedmem.o \ kgsl_pwrctrl.o \ kgsl_pwrscale.o \ kgsl_mmu.o \ kgsl_gpummu.o \ - kgsl_iommu.o + kgsl_iommu.o \ + kgsl_snapshot.o msm_kgsl_core-$(CONFIG_DEBUG_FS) += kgsl_debugfs.o msm_kgsl_core-$(CONFIG_MSM_KGSL_CFF_DUMP) += kgsl_cffdump.o msm_kgsl_core-$(CONFIG_MSM_KGSL_DRM) += kgsl_drm.o msm_kgsl_core-$(CONFIG_MSM_SCM) += kgsl_pwrscale_trustzone.o -msm_kgsl_core-$(CONFIG_MSM_SLEEP_STATS) += kgsl_pwrscale_idlestats.o +msm_kgsl_core-$(CONFIG_MSM_SLEEP_STATS_DEVICE) += kgsl_pwrscale_idlestats.o msm_adreno-y += \ adreno_ringbuffer.o \ adreno_drawctxt.o \ adreno_postmortem.o \ + adreno_snapshot.o \ adreno_a2xx.o \ + adreno_a2xx_trace.o \ + adreno_a2xx_snapshot.o \ adreno.o msm_adreno-$(CONFIG_DEBUG_FS) += adreno_debugfs.o -msm_z180-y += z180.o +msm_z180-y += \ + z180.o \ + z180_trace.o msm_kgsl_core-objs = $(msm_kgsl_core-y) msm_adreno-objs = $(msm_adreno-y) diff --git a/drivers/gpu/msm/a2xx_reg.h b/drivers/gpu/msm/a2xx_reg.h index d859d61c4742c..50b2745bed0fd 100644 --- a/drivers/gpu/msm/a2xx_reg.h +++ b/drivers/gpu/msm/a2xx_reg.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -413,6 +413,40 @@ union reg_cp_rb_cntl { #define REG_A225_RB_COLOR_INFO3 0x2005 #define REG_A225_PC_MULTI_PRIM_IB_RESET_INDX 0x2103 #define REG_A225_GRAS_UCP0X 0x2340 +#define REG_A225_GRAS_UCP5W 0x2357 #define REG_A225_GRAS_UCP_ENABLED 0x2360 +/* Debug registers used by snapshot */ +#define REG_PA_SU_DEBUG_CNTL 0x0C80 +#define REG_PA_SU_DEBUG_DATA 0x0C81 +#define REG_RB_DEBUG_CNTL 0x0F26 +#define REG_RB_DEBUG_DATA 0x0F27 +#define REG_PC_DEBUG_CNTL 0x0C38 +#define REG_PC_DEBUG_DATA 0x0C39 +#define REG_GRAS_DEBUG_CNTL 0x0C80 +#define REG_GRAS_DEBUG_DATA 0x0C81 +#define REG_SQ_DEBUG_MISC 0x0D05 +#define REG_SQ_DEBUG_INPUT_FSM 0x0DAE +#define REG_SQ_DEBUG_CONST_MGR_FSM 0x0DAF +#define REG_SQ_DEBUG_EXP_ALLOC 0x0DB3 +#define REG_SQ_DEBUG_FSM_ALU_0 0x0DB1 +#define REG_SQ_DEBUG_FSM_ALU_1 0x0DB2 +#define REG_SQ_DEBUG_PTR_BUFF 0x0DB4 +#define REG_SQ_DEBUG_GPR_VTX 0x0DB5 +#define REG_SQ_DEBUG_GPR_PIX 0x0DB6 +#define REG_SQ_DEBUG_TB_STATUS_SEL 0x0DB7 +#define REG_SQ_DEBUG_VTX_TB_0 0x0DB8 +#define REG_SQ_DEBUG_VTX_TB_1 0x0DB9 +#define REG_SQ_DEBUG_VTX_TB_STATE_MEM 0x0DBB +#define REG_SQ_DEBUG_TP_FSM 0x0DB0 +#define REG_SQ_DEBUG_VTX_TB_STATUS_REG 0x0DBA +#define REG_SQ_DEBUG_PIX_TB_0 0x0DBC +#define REG_SQ_DEBUG_PIX_TB_STATUS_REG_0 0x0DBD +#define REG_SQ_DEBUG_PIX_TB_STATUS_REG_1 0x0DBE +#define REG_SQ_DEBUG_PIX_TB_STATUS_REG_2 0x0DBF +#define REG_SQ_DEBUG_PIX_TB_STATUS_REG_3 0x0DC0 +#define REG_SQ_DEBUG_PIX_TB_STATE_MEM 0x0DC1 +#define REG_SQ_DEBUG_MISC_0 0x2309 +#define REG_SQ_DEBUG_MISC_1 0x230A + #endif /* __A200_REG_H */ diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index d485cd2d0945e..0cb8d2b7c31da 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -93,7 +93,6 @@ static struct adreno_device device_3d0 = { .pwrctrl = { .regulator_name = "fs_gfx3d", .irq_name = KGSL_3D0_IRQ, - .src_clk_name = "src_clk", }, .mutex = __MUTEX_INITIALIZER(device_3d0.dev.mutex), .state = KGSL_STATE_INIT, @@ -150,6 +149,9 @@ static const struct { { ADRENO_REV_A225, 2, 2, 0, 5, "a225p5_pm4.fw", "a225_pfp.fw", &adreno_a2xx_gpudev, 1536, 768 }, + { ADRENO_REV_A225, 2, 2, 0, 6, + "a225_pm4.fw", "a225_pfp.fw", &adreno_a2xx_gpudev, + 1536, 768 }, { ADRENO_REV_A225, 2, 2, ANY_ID, ANY_ID, "a225_pm4.fw", "a225_pfp.fw", &adreno_a2xx_gpudev, 1536, 768 }, @@ -192,7 +194,7 @@ static irqreturn_t adreno_isr(int irq, void *data) if (device->requested_state == KGSL_STATE_NONE) { if (device->pwrctrl.nap_allowed == true) { - device->requested_state = KGSL_STATE_NAP; + kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP); queue_work(device->work_queue, &device->idle_check_ws); } else if (device->pwrscale.policy != NULL) { queue_work(device->work_queue, &device->idle_check_ws); @@ -200,7 +202,7 @@ static irqreturn_t adreno_isr(int irq, void *data) } /* Reset the time-out in our idle timer */ - mod_timer(&device->idle_timer, + mod_timer_pending(&device->idle_timer, jiffies + device->pwrctrl.interval_timeout); return result; } @@ -271,10 +273,13 @@ static void adreno_setstate(struct kgsl_device *device, int sizedwords = 0; unsigned int mh_mmu_invalidate = 0x00000003; /*invalidate all and tc */ - /* If possible, then set the state via the command stream to avoid - a CPU idle. Otherwise, use the default setstate which uses register - writes */ - if (adreno_dev->drawctxt_active) { + /* + * If possible, then set the state via the command stream to avoid + * a CPU idle. Otherwise, use the default setstate which uses register + * writes For CFF dump we must idle and use the registers so that it is + * easier to filter out the mmu accesses from the dump + */ + if (!kgsl_cff_dump_enable && adreno_dev->drawctxt_active) { if (flags & KGSL_MMUFLAGS_PTUPDATE) { /* wait for graphics pipe to be idle */ *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); @@ -359,6 +364,7 @@ adreno_getchipid(struct kgsl_device *device) { unsigned int chipid = 0; unsigned int coreid, majorid, minorid, patchid, revid; + uint32_t soc_platform_version = socinfo_get_version(); adreno_regread(device, REG_RBBM_PERIPHID1, &coreid); adreno_regread(device, REG_RBBM_PERIPHID2, &majorid); @@ -380,8 +386,12 @@ adreno_getchipid(struct kgsl_device *device) patchid = ((revid >> 16) & 0xFF); /* 8x50 returns 0 for patch release, but it should be 1 */ + /* 8960v3 returns 5 for patch release, but it should be 6 */ if (cpu_is_qsd8x50()) patchid = 1; + else if (cpu_is_msm8960() && + SOCINFO_VERSION_MAJOR(soc_platform_version) == 3) + patchid = 6; chipid |= (minorid << 8) | patchid; @@ -485,8 +495,7 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) struct adreno_device *adreno_dev = ADRENO_DEVICE(device); int init_reftimestamp = 0x7fffffff; - device->state = KGSL_STATE_INIT; - device->requested_state = KGSL_STATE_NONE; + kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT); /* Power up the device */ kgsl_pwrctrl_enable(device); @@ -576,6 +585,7 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) adreno_gmeminit(adreno_dev); kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON); + device->ftbl->irqctrl(device, 1); status = adreno_ringbuffer_start(&adreno_dev->ringbuffer, init_ram); if (status != 0) @@ -603,6 +613,7 @@ static int adreno_stop(struct kgsl_device *device) kgsl_mmu_stop(device); + device->ftbl->irqctrl(device, 0); kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); del_timer_sync(&device->idle_timer); @@ -627,6 +638,8 @@ adreno_recover_hang(struct kgsl_device *device) unsigned int soptimestamp; unsigned int eoptimestamp; struct adreno_context *drawctxt; + struct kgsl_context *context; + int next = 0; KGSL_DRV_ERR(device, "Starting recovery from 3D GPU hang....\n"); rb_buffer = vmalloc(rb->buffer_desc.size); @@ -695,6 +708,24 @@ adreno_recover_hang(struct kgsl_device *device) drawctxt->flags |= CTXT_FLAGS_GPU_HANG; + /* + * Set the reset status of all contexts to + * INNOCENT_CONTEXT_RESET_EXT except for the bad context + * since thats the guilty party + */ + while ((context = idr_get_next(&device->context_idr, &next))) { + if (KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT != + context->reset_status) { + if (context->devctxt != drawctxt) + context->reset_status = + KGSL_CTX_STAT_INNOCENT_CONTEXT_RESET_EXT; + else + context->reset_status = + KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT; + } + next = next + 1; + } + /* Restore valid commands in ringbuffer */ adreno_ringbuffer_restore(rb, rb_buffer, num_rb_contents); rb->timestamp = timestamp; @@ -706,33 +737,40 @@ adreno_recover_hang(struct kgsl_device *device) static int adreno_dump_and_recover(struct kgsl_device *device) { - static int recovery; int result = -ETIMEDOUT; if (device->state == KGSL_STATE_HUNG) goto done; - if (device->state == KGSL_STATE_DUMP_AND_RECOVER && !recovery) { + if (device->state == KGSL_STATE_DUMP_AND_RECOVER) { mutex_unlock(&device->mutex); wait_for_completion(&device->recovery_gate); mutex_lock(&device->mutex); - if (!(device->state & KGSL_STATE_HUNG)) - /* recovery success */ + if (device->state != KGSL_STATE_HUNG) result = 0; } else { + kgsl_pwrctrl_set_state(device, KGSL_STATE_DUMP_AND_RECOVER); INIT_COMPLETION(device->recovery_gate); - /* Detected a hang - trigger an automatic dump */ + /* Detected a hang */ + + + /* + * Trigger an automatic dump of the state to + * the console + */ adreno_postmortem_dump(device, 0); - if (!recovery) { - recovery = 1; - result = adreno_recover_hang(device); - if (result) - device->state = KGSL_STATE_HUNG; - recovery = 0; - complete_all(&device->recovery_gate); - } else - KGSL_DRV_ERR(device, - "Cannot recover from another hang while " - "recovering from a hang\n"); + + /* + * Make a GPU snapshot. For now, do it after the PM dump so we + * can at least be sure the PM dump will work as it always has + */ + kgsl_device_snapshot(device, 1); + + result = adreno_recover_hang(device); + if (result) + kgsl_pwrctrl_set_state(device, KGSL_STATE_HUNG); + else + kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE); + complete_all(&device->recovery_gate); } done: return result; @@ -851,7 +889,11 @@ int adreno_idle(struct kgsl_device *device, unsigned int timeout) unsigned int rbbm_status; unsigned long wait_timeout = msecs_to_jiffies(adreno_dev->wait_timeout); - unsigned long wait_time = jiffies + wait_timeout; + unsigned long wait_time; + unsigned long wait_time_part; + unsigned int msecs; + unsigned int msecs_first; + unsigned int msecs_part; kgsl_cffdump_regpoll(device->id, REG_RBBM_STATUS << 2, 0x00000000, 0x80000000); @@ -860,8 +902,18 @@ int adreno_idle(struct kgsl_device *device, unsigned int timeout) */ retry: if (rb->flags & KGSL_FLAGS_STARTED) { + msecs = adreno_dev->wait_timeout; + msecs_first = (msecs <= 100) ? ((msecs + 4) / 5) : 100; + msecs_part = (msecs - msecs_first + 3) / 4; + wait_time = jiffies + wait_timeout; + wait_time_part = jiffies + msecs_to_jiffies(msecs_first); + adreno_poke(device); do { - adreno_poke(device); + if (time_after(jiffies, wait_time_part)) { + adreno_poke(device); + wait_time_part = jiffies + + msecs_to_jiffies(msecs_part); + } GSL_RB_GET_READPTR(rb, &rb->rptr); if (time_after(jiffies, wait_time)) { KGSL_DRV_ERR(device, "rptr: %x, wptr: %x\n", @@ -895,7 +947,9 @@ static unsigned int adreno_isidle(struct kgsl_device *device) struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; unsigned int rbbm_status; - if (rb->flags & KGSL_FLAGS_STARTED) { + WARN_ON(device->state == KGSL_STATE_INIT); + /* If the device isn't active, don't force it on. */ + if (device->state == KGSL_STATE_ACTIVE) { /* Is the ring buffer is empty? */ GSL_RB_GET_READPTR(rb, &rb->rptr); if (!device->active_cnt && (rb->rptr == rb->wptr)) { @@ -906,8 +960,7 @@ static unsigned int adreno_isidle(struct kgsl_device *device) status = true; } } else { - KGSL_DRV_ERR(device, "ringbuffer not started\n"); - BUG(); + status = true; } return status; } @@ -927,40 +980,36 @@ static int adreno_suspend_context(struct kgsl_device *device) return status; } -uint8_t *kgsl_sharedmem_convertaddr(struct kgsl_device *device, - unsigned int pt_base, unsigned int gpuaddr, unsigned int *size) +struct kgsl_memdesc *adreno_find_region(struct kgsl_device *device, + unsigned int pt_base, + unsigned int gpuaddr, + unsigned int size) { - uint8_t *result = NULL; + struct kgsl_memdesc *result = NULL; struct kgsl_mem_entry *entry; struct kgsl_process_private *priv; struct adreno_device *adreno_dev = ADRENO_DEVICE(device); struct adreno_ringbuffer *ringbuffer = &adreno_dev->ringbuffer; + struct kgsl_context *context; + int next = 0; - if (kgsl_gpuaddr_in_memdesc(&ringbuffer->buffer_desc, gpuaddr)) { - return kgsl_gpuaddr_to_vaddr(&ringbuffer->buffer_desc, - gpuaddr, size); - } + if (kgsl_gpuaddr_in_memdesc(&ringbuffer->buffer_desc, gpuaddr, size)) + return &ringbuffer->buffer_desc; - if (kgsl_gpuaddr_in_memdesc(&ringbuffer->memptrs_desc, gpuaddr)) { - return kgsl_gpuaddr_to_vaddr(&ringbuffer->memptrs_desc, - gpuaddr, size); - } + if (kgsl_gpuaddr_in_memdesc(&ringbuffer->memptrs_desc, gpuaddr, size)) + return &ringbuffer->memptrs_desc; - if (kgsl_gpuaddr_in_memdesc(&device->memstore, gpuaddr)) { - return kgsl_gpuaddr_to_vaddr(&device->memstore, - gpuaddr, size); - } + if (kgsl_gpuaddr_in_memdesc(&device->memstore, gpuaddr, size)) + return &device->memstore; mutex_lock(&kgsl_driver.process_mutex); list_for_each_entry(priv, &kgsl_driver.process_list, list) { if (!kgsl_mmu_pt_equal(priv->pagetable, pt_base)) continue; spin_lock(&priv->mem_lock); - entry = kgsl_sharedmem_find_region(priv, gpuaddr, - sizeof(unsigned int)); + entry = kgsl_sharedmem_find_region(priv, gpuaddr, size); if (entry) { - result = kgsl_gpuaddr_to_vaddr(&entry->memdesc, - gpuaddr, size); + result = &entry->memdesc; spin_unlock(&priv->mem_lock); mutex_unlock(&kgsl_driver.process_mutex); return result; @@ -969,16 +1018,44 @@ uint8_t *kgsl_sharedmem_convertaddr(struct kgsl_device *device, } mutex_unlock(&kgsl_driver.process_mutex); - BUG_ON(!mutex_is_locked(&device->mutex)); - list_for_each_entry(entry, &device->memqueue, list) { - if (kgsl_gpuaddr_in_memdesc(&entry->memdesc, gpuaddr)) { - result = kgsl_gpuaddr_to_vaddr(&entry->memdesc, - gpuaddr, size); + while (1) { + struct adreno_context *adreno_context = NULL; + context = idr_get_next(&device->context_idr, &next); + if (context == NULL) break; - } + adreno_context = (struct adreno_context *)context->devctxt; + + if (kgsl_mmu_pt_equal(adreno_context->pagetable, pt_base)) { + struct kgsl_memdesc *desc; + + desc = &adreno_context->gpustate; + if (kgsl_gpuaddr_in_memdesc(desc, gpuaddr, size)) { + result = desc; + return result; + } + + desc = &adreno_context->context_gmem_shadow.gmemshadow; + if (kgsl_gpuaddr_in_memdesc(desc, gpuaddr, size)) { + result = desc; + return result; + } + } + next = next + 1; } - return result; + + return NULL; + +} + +uint8_t *adreno_convertaddr(struct kgsl_device *device, unsigned int pt_base, + unsigned int gpuaddr, unsigned int size) +{ + struct kgsl_memdesc *memdesc; + + memdesc = adreno_find_region(device, pt_base, gpuaddr, size); + + return memdesc ? kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr) : NULL; } void adreno_regread(struct kgsl_device *device, unsigned int offsetwords, @@ -1056,7 +1133,8 @@ static int kgsl_check_interrupt_timestamp(struct kgsl_device *device, * get an interrupt */ cmds[0] = cp_type3_packet(CP_NOP, 1); cmds[1] = 0; - adreno_ringbuffer_issuecmds(device, 0, &cmds[0], 2); + adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, + &cmds[0], 2); } mutex_unlock(&device->mutex); } @@ -1113,52 +1191,56 @@ static int adreno_waittimestamp(struct kgsl_device *device, msecs_first = (msecs <= 100) ? ((msecs + 4) / 5) : 100; msecs_part = (msecs - msecs_first + 3) / 4; for (retries = 0; retries < 5; retries++) { - if (!kgsl_check_timestamp(device, timestamp)) { - adreno_poke(device); - io_cnt = (io_cnt + 1) % 100; - if (io_cnt < - pwr->pwrlevels[pwr->active_pwrlevel]. - io_fraction) - io = 0; - mutex_unlock(&device->mutex); - /* We need to make sure that the process is - * placed in wait-q before its condition is called + if (kgsl_check_timestamp(device, timestamp)) { + /* if the timestamp happens while we're not + * waiting, there's a chance that an interrupt + * will not be generated and thus the timestamp + * work needs to be queued. */ - status = kgsl_wait_event_interruptible_timeout( - device->wait_queue, - kgsl_check_interrupt_timestamp(device, - timestamp), - msecs_to_jiffies(retries ? - msecs_part : msecs_first), io); - mutex_lock(&device->mutex); - - if (status > 0) { - status = 0; - goto done; - } + queue_work(device->work_queue, &device->ts_expired_ws); + status = 0; + goto done; } - } - if (!kgsl_check_timestamp(device, timestamp)) { - status = -ETIMEDOUT; - KGSL_DRV_ERR(device, - "Device hang detected while waiting " - "for timestamp: %x, last " - "submitted(rb->timestamp): %x, wptr: " - "%x\n", timestamp, - adreno_dev->ringbuffer.timestamp, - adreno_dev->ringbuffer.wptr); - if (!adreno_dump_and_recover(device)) { - /* wait for idle after recovery as the - * timestamp that this process wanted - * to wait on may be invalid */ - if (!adreno_idle(device, - KGSL_TIMEOUT_DEFAULT)) - status = 0; + adreno_poke(device); + io_cnt = (io_cnt + 1) % 100; + if (io_cnt < + pwr->pwrlevels[pwr->active_pwrlevel].io_fraction) + io = 0; + mutex_unlock(&device->mutex); + /* We need to make sure that the process is + * placed in wait-q before its condition is called + */ + status = kgsl_wait_event_interruptible_timeout( + device->wait_queue, + kgsl_check_interrupt_timestamp(device, + timestamp), + msecs_to_jiffies(retries ? + msecs_part : msecs_first), io); + mutex_lock(&device->mutex); + + if (status > 0) { + /*completed before the wait finished */ + status = 0; + goto done; + } else if (status < 0) { + /*an error occurred*/ + goto done; } - } else { - status = 0; + /*this wait timed out*/ + } + status = -ETIMEDOUT; + KGSL_DRV_ERR(device, + "Device hang detected while waiting for timestamp: %x," + "last submitted(rb->timestamp): %x, wptr: %x\n", + timestamp, adreno_dev->ringbuffer.timestamp, + adreno_dev->ringbuffer.wptr); + if (!adreno_dump_and_recover(device)) { + /* wait for idle after recovery as the + * timestamp that this process wanted + * to wait on may be invalid */ + if (!adreno_idle(device, KGSL_TIMEOUT_DEFAULT)) + status = 0; } - done: return (int)status; } @@ -1267,6 +1349,18 @@ void adreno_irqctrl(struct kgsl_device *device, int state) adreno_dev->gpudev->irq_control(adreno_dev, state); } +static unsigned int adreno_gpuid(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + + /* Standard KGSL gpuid format: + * top word is 0x0002 for 2D or 0x0003 for 3D + * Bottom word is core specific identifer + */ + + return (0x0003 << 16) | ((int) adreno_dev->gpurev); +} + static const struct kgsl_functable adreno_functable = { /* Mandatory functions */ .regread = adreno_regread, @@ -1285,6 +1379,8 @@ static const struct kgsl_functable adreno_functable = { .cleanup_pt = adreno_cleanup_pt, .power_stats = adreno_power_stats, .irqctrl = adreno_irqctrl, + .gpuid = adreno_gpuid, + .snapshot = adreno_snapshot, /* Optional functions */ .setstate = adreno_setstate, .drawctxt_create = adreno_drawctxt_create, diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index 0776a240a1867..a7fc1c5701285 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -24,6 +24,7 @@ KGSL_CONTAINER_OF(device, struct adreno_device, dev) /* Flags to control command packet settings */ +#define KGSL_CMD_FLAGS_NONE 0x00000000 #define KGSL_CMD_FLAGS_PMODE 0x00000001 #define KGSL_CMD_FLAGS_NO_TS_CMP 0x00000002 #define KGSL_CMD_FLAGS_NOT_KERNEL_CMD 0x00000004 @@ -43,6 +44,7 @@ */ #define ADRENO_ISTORE_BYTES 12 #define ADRENO_ISTORE_WORDS 3 +#define ADRENO_ISTORE_START 0x5000 enum adreno_gpurev { ADRENO_REV_UNKNOWN = 0, @@ -75,26 +77,38 @@ struct adreno_device { }; struct adreno_gpudev { - int (*ctxt_gpustate_shadow)(struct adreno_device *, - struct adreno_context *); - int (*ctxt_gmem_shadow)(struct adreno_device *, - struct adreno_context *); + int (*ctxt_create)(struct adreno_device *, struct adreno_context *); void (*ctxt_save)(struct adreno_device *, struct adreno_context *); void (*ctxt_restore)(struct adreno_device *, struct adreno_context *); irqreturn_t (*irq_handler)(struct adreno_device *); void (*irq_control)(struct adreno_device *, int); + void * (*snapshot)(struct adreno_device *, void *, int *, int); }; extern struct adreno_gpudev adreno_a2xx_gpudev; +/* A2XX register sets defined in adreno_a2xx.c */ +extern const unsigned int a200_registers[]; +extern const unsigned int a220_registers[]; +extern const unsigned int a200_registers_count; +extern const unsigned int a220_registers_count; + int adreno_idle(struct kgsl_device *device, unsigned int timeout); void adreno_regread(struct kgsl_device *device, unsigned int offsetwords, unsigned int *value); void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords, unsigned int value); -uint8_t *kgsl_sharedmem_convertaddr(struct kgsl_device *device, - unsigned int pt_base, unsigned int gpuaddr, unsigned int *size); +struct kgsl_memdesc *adreno_find_region(struct kgsl_device *device, + unsigned int pt_base, + unsigned int gpuaddr, + unsigned int size); + +uint8_t *adreno_convertaddr(struct kgsl_device *device, + unsigned int pt_base, unsigned int gpuaddr, unsigned int size); + +void *adreno_snapshot(struct kgsl_device *device, void *snapshot, int *remain, + int hang); static inline int adreno_is_a200(struct adreno_device *adreno_dev) { diff --git a/drivers/gpu/msm/adreno_a2xx.c b/drivers/gpu/msm/adreno_a2xx.c index 600384612f646..5ce9cf85bc2ec 100644 --- a/drivers/gpu/msm/adreno_a2xx.c +++ b/drivers/gpu/msm/adreno_a2xx.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -15,6 +15,75 @@ #include "kgsl_sharedmem.h" #include "kgsl_cffdump.h" #include "adreno.h" +#include "adreno_a2xx_trace.h" + +/* + * These are the registers that are dumped with GPU snapshot + * and postmortem. The lists are dword offset pairs in the + * form of {start offset, end offset} inclusive. + */ + +/* A200, A205 */ +const unsigned int a200_registers[] = { + 0x0000, 0x0002, 0x0004, 0x000B, 0x003B, 0x003D, 0x0040, 0x0044, + 0x0046, 0x0047, 0x01C0, 0x01C1, 0x01C3, 0x01C8, 0x01D5, 0x01D9, + 0x01DC, 0x01DD, 0x01EA, 0x01EA, 0x01EE, 0x01F3, 0x01F6, 0x01F7, + 0x01FC, 0x01FF, 0x0391, 0x0392, 0x039B, 0x039E, 0x03B2, 0x03B5, + 0x03B7, 0x03B7, 0x03F8, 0x03FB, 0x0440, 0x0440, 0x0443, 0x0444, + 0x044B, 0x044B, 0x044D, 0x044F, 0x0452, 0x0452, 0x0454, 0x045B, + 0x047F, 0x047F, 0x0578, 0x0587, 0x05C9, 0x05C9, 0x05D0, 0x05D0, + 0x0601, 0x0604, 0x0606, 0x0609, 0x060B, 0x060E, 0x0613, 0x0614, + 0x0A29, 0x0A2B, 0x0A2F, 0x0A31, 0x0A40, 0x0A43, 0x0A45, 0x0A45, + 0x0A4E, 0x0A4F, 0x0C2C, 0x0C2C, 0x0C30, 0x0C30, 0x0C38, 0x0C3C, + 0x0C40, 0x0C40, 0x0C44, 0x0C44, 0x0C80, 0x0C86, 0x0C88, 0x0C94, + 0x0C99, 0x0C9A, 0x0CA4, 0x0CA5, 0x0D00, 0x0D03, 0x0D06, 0x0D06, + 0x0D08, 0x0D0B, 0x0D34, 0x0D35, 0x0DAE, 0x0DC1, 0x0DC8, 0x0DD4, + 0x0DD8, 0x0DD9, 0x0E00, 0x0E00, 0x0E02, 0x0E04, 0x0E17, 0x0E1E, + 0x0EC0, 0x0EC9, 0x0ECB, 0x0ECC, 0x0ED0, 0x0ED0, 0x0ED4, 0x0ED7, + 0x0EE0, 0x0EE2, 0x0F01, 0x0F02, 0x0F0C, 0x0F0C, 0x0F0E, 0x0F12, + 0x0F26, 0x0F2A, 0x0F2C, 0x0F2C, 0x2000, 0x2002, 0x2006, 0x200F, + 0x2080, 0x2082, 0x2100, 0x2109, 0x210C, 0x2114, 0x2180, 0x2184, + 0x21F5, 0x21F7, 0x2200, 0x2208, 0x2280, 0x2283, 0x2293, 0x2294, + 0x2300, 0x2308, 0x2312, 0x2312, 0x2316, 0x231D, 0x2324, 0x2326, + 0x2380, 0x2383, 0x2400, 0x2402, 0x2406, 0x240F, 0x2480, 0x2482, + 0x2500, 0x2509, 0x250C, 0x2514, 0x2580, 0x2584, 0x25F5, 0x25F7, + 0x2600, 0x2608, 0x2680, 0x2683, 0x2693, 0x2694, 0x2700, 0x2708, + 0x2712, 0x2712, 0x2716, 0x271D, 0x2724, 0x2726, 0x2780, 0x2783, + 0x4000, 0x4003, 0x4800, 0x4805, 0x4900, 0x4900, 0x4908, 0x4908, +}; + +/* A220, A225 */ +const unsigned int a220_registers[] = { + 0x0000, 0x0002, 0x0004, 0x000B, 0x003B, 0x003D, 0x0040, 0x0044, + 0x0046, 0x0047, 0x01C0, 0x01C1, 0x01C3, 0x01C8, 0x01D5, 0x01D9, + 0x01DC, 0x01DD, 0x01EA, 0x01EA, 0x01EE, 0x01F3, 0x01F6, 0x01F7, + 0x01FC, 0x01FF, 0x0391, 0x0392, 0x039B, 0x039E, 0x03B2, 0x03B5, + 0x03B7, 0x03B7, 0x03F8, 0x03FB, 0x0440, 0x0440, 0x0443, 0x0444, + 0x044B, 0x044B, 0x044D, 0x044F, 0x0452, 0x0452, 0x0454, 0x045B, + 0x047F, 0x047F, 0x0578, 0x0587, 0x05C9, 0x05C9, 0x05D0, 0x05D0, + 0x0601, 0x0604, 0x0606, 0x0609, 0x060B, 0x060E, 0x0613, 0x0614, + 0x0A29, 0x0A2B, 0x0A2F, 0x0A31, 0x0A40, 0x0A40, 0x0A42, 0x0A43, + 0x0A45, 0x0A45, 0x0A4E, 0x0A4F, 0x0C30, 0x0C30, 0x0C38, 0x0C39, + 0x0C3C, 0x0C3C, 0x0C80, 0x0C81, 0x0C88, 0x0C93, 0x0D00, 0x0D03, + 0x0D05, 0x0D06, 0x0D08, 0x0D0B, 0x0D34, 0x0D35, 0x0DAE, 0x0DC1, + 0x0DC8, 0x0DD4, 0x0DD8, 0x0DD9, 0x0E00, 0x0E00, 0x0E02, 0x0E04, + 0x0E17, 0x0E1E, 0x0EC0, 0x0EC9, 0x0ECB, 0x0ECC, 0x0ED0, 0x0ED0, + 0x0ED4, 0x0ED7, 0x0EE0, 0x0EE2, 0x0F01, 0x0F02, 0x2000, 0x2002, + 0x2006, 0x200F, 0x2080, 0x2082, 0x2100, 0x2102, 0x2104, 0x2109, + 0x210C, 0x2114, 0x2180, 0x2184, 0x21F5, 0x21F7, 0x2200, 0x2202, + 0x2204, 0x2204, 0x2208, 0x2208, 0x2280, 0x2282, 0x2294, 0x2294, + 0x2300, 0x2308, 0x2309, 0x230A, 0x2312, 0x2312, 0x2316, 0x2316, + 0x2318, 0x231D, 0x2324, 0x2326, 0x2380, 0x2383, 0x2400, 0x2402, + 0x2406, 0x240F, 0x2480, 0x2482, 0x2500, 0x2502, 0x2504, 0x2509, + 0x250C, 0x2514, 0x2580, 0x2584, 0x25F5, 0x25F7, 0x2600, 0x2602, + 0x2604, 0x2606, 0x2608, 0x2608, 0x2680, 0x2682, 0x2694, 0x2694, + 0x2700, 0x2708, 0x2712, 0x2712, 0x2716, 0x2716, 0x2718, 0x271D, + 0x2724, 0x2726, 0x2780, 0x2783, 0x4000, 0x4003, 0x4800, 0x4805, + 0x4900, 0x4900, 0x4908, 0x4908, +}; + +const unsigned int a200_registers_count = ARRAY_SIZE(a200_registers) / 2; +const unsigned int a220_registers_count = ARRAY_SIZE(a220_registers) / 2; /* * @@ -408,7 +477,8 @@ static const unsigned int register_ranges_a225[] = { REG_A220_PC_VERTEX_REUSE_BLOCK_CNTL, REG_A220_PC_VERTEX_REUSE_BLOCK_CNTL, REG_RB_COPY_CONTROL, REG_RB_DEPTH_CLEAR, - REG_A225_GRAS_UCP0X, REG_A225_GRAS_UCP_ENABLED + REG_A225_GRAS_UCP0X, REG_A225_GRAS_UCP5W, + REG_A225_GRAS_UCP_ENABLED, REG_A225_GRAS_UCP_ENABLED }; @@ -549,14 +619,16 @@ static unsigned int *build_gmem2sys_cmds(struct adreno_device *adreno_dev, unsigned int addr = shadow->gmemshadow.gpuaddr; unsigned int offset = (addr - (addr & 0xfffff000)) / bytesperpixel; - /* Store TP0_CHICKEN register */ - *cmds++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmds++ = REG_TP0_CHICKEN; + if (!(drawctxt->flags & CTXT_FLAGS_PREAMBLE)) { + /* Store TP0_CHICKEN register */ + *cmds++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmds++ = REG_TP0_CHICKEN; - *cmds++ = tmp_ctx.chicken_restore; + *cmds++ = tmp_ctx.chicken_restore; - *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); - *cmds++ = 0; + *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmds++ = 0; + } /* Set TP0_CHICKEN to zero */ *cmds++ = cp_type0_packet(REG_TP0_CHICKEN, 1); @@ -597,7 +669,7 @@ static unsigned int *build_gmem2sys_cmds(struct adreno_device *adreno_dev, /* Repartition shaders */ *cmds++ = cp_type0_packet(REG_SQ_INST_STORE_MANAGMENT, 1); - *cmds++ = 0x180; + *cmds++ = adreno_dev->pix_shader_start; /* Invalidate Vertex & Pixel instruction code address and sizes */ *cmds++ = cp_type3_packet(CP_INVALIDATE_STATE, 1); @@ -759,13 +831,15 @@ static unsigned int *build_sys2gmem_cmds(struct adreno_device *adreno_dev, unsigned int *cmds = shadow->gmem_restore_commands; unsigned int *start = cmds; - /* Store TP0_CHICKEN register */ - *cmds++ = cp_type3_packet(CP_REG_TO_MEM, 2); - *cmds++ = REG_TP0_CHICKEN; - *cmds++ = tmp_ctx.chicken_restore; + if (!(drawctxt->flags & CTXT_FLAGS_PREAMBLE)) { + /* Store TP0_CHICKEN register */ + *cmds++ = cp_type3_packet(CP_REG_TO_MEM, 2); + *cmds++ = REG_TP0_CHICKEN; + *cmds++ = tmp_ctx.chicken_restore; - *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); - *cmds++ = 0; + *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmds++ = 0; + } /* Set TP0_CHICKEN to zero */ *cmds++ = cp_type0_packet(REG_TP0_CHICKEN, 1); @@ -799,7 +873,7 @@ static unsigned int *build_sys2gmem_cmds(struct adreno_device *adreno_dev, /* Repartition shaders */ *cmds++ = cp_type0_packet(REG_SQ_INST_STORE_MANAGMENT, 1); - *cmds++ = 0x180; + *cmds++ = adreno_dev->pix_shader_start; /* Invalidate Vertex & Pixel instruction code address and sizes */ *cmds++ = cp_type3_packet(CP_INVALIDATE_STATE, 1); @@ -1227,46 +1301,22 @@ build_shader_save_restore_cmds(struct adreno_device *adreno_dev, } /* create buffers for saving/restoring registers, constants, & GMEM */ -static int a2xx_ctxt_gpustate_shadow(struct adreno_device *adreno_dev, +static int a2xx_create_gpustate_shadow(struct adreno_device *adreno_dev, struct adreno_context *drawctxt) { - int result; - - /* Allocate vmalloc memory to store the gpustate */ - result = kgsl_allocate(&drawctxt->gpustate, - drawctxt->pagetable, _context_size(adreno_dev)); - - if (result) - return result; - drawctxt->flags |= CTXT_FLAGS_STATE_SHADOW; - /* Blank out h/w register, constant, and command buffer shadows. */ - kgsl_sharedmem_set(&drawctxt->gpustate, 0, 0, - _context_size(adreno_dev)); - - /* set-up command and vertex buffer pointers */ - tmp_ctx.cmd = tmp_ctx.start - = (unsigned int *)((char *)drawctxt->gpustate.hostptr + CMD_OFFSET); - /* build indirect command buffers to save & restore regs/constants */ - adreno_idle(&adreno_dev->dev, KGSL_TIMEOUT_DEFAULT); build_regrestore_cmds(adreno_dev, drawctxt); build_regsave_cmds(adreno_dev, drawctxt); build_shader_save_restore_cmds(adreno_dev, drawctxt); - kgsl_cache_range_op(&drawctxt->gpustate, - KGSL_CACHE_OP_FLUSH); - - kgsl_cffdump_syncmem(NULL, &drawctxt->gpustate, - drawctxt->gpustate.gpuaddr, - drawctxt->gpustate.size, false); return 0; } /* create buffers for saving/restoring registers, constants, & GMEM */ -static int a2xx_ctxt_gmem_shadow(struct adreno_device *adreno_dev, +static int a2xx_create_gmem_shadow(struct adreno_device *adreno_dev, struct adreno_context *drawctxt) { int result; @@ -1293,11 +1343,10 @@ static int a2xx_ctxt_gmem_shadow(struct adreno_device *adreno_dev, &tmp_ctx.cmd); /* build TP0_CHICKEN register restore command buffer */ - tmp_ctx.cmd = build_chicken_restore_cmds(drawctxt); + if (!(drawctxt->flags & CTXT_FLAGS_PREAMBLE)) + tmp_ctx.cmd = build_chicken_restore_cmds(drawctxt); /* build indirect command buffers to save & restore gmem */ - /* Idle because we are reading PM override registers */ - adreno_idle(&adreno_dev->dev, KGSL_TIMEOUT_DEFAULT); drawctxt->context_gmem_shadow.gmem_save_commands = tmp_ctx.cmd; tmp_ctx.cmd = build_gmem2sys_cmds(adreno_dev, drawctxt, @@ -1318,10 +1367,65 @@ static int a2xx_ctxt_gmem_shadow(struct adreno_device *adreno_dev, return 0; } -static void a2xx_ctxt_save(struct adreno_device *adreno_dev, +static int a2xx_drawctxt_create(struct adreno_device *adreno_dev, + struct adreno_context *drawctxt) +{ + int ret; + + /* + * Allocate memory for the GPU state and the context commands. + * Despite the name, this is much more then just storage for + * the gpustate. This contains command space for gmem save + * and texture and vertex buffer storage too + */ + + ret = kgsl_allocate(&drawctxt->gpustate, + drawctxt->pagetable, _context_size(adreno_dev)); + + if (ret) + return ret; + + kgsl_sharedmem_set(&drawctxt->gpustate, 0, 0, + _context_size(adreno_dev)); + + tmp_ctx.cmd = tmp_ctx.start + = (unsigned int *)((char *)drawctxt->gpustate.hostptr + CMD_OFFSET); + + if (!(drawctxt->flags & CTXT_FLAGS_PREAMBLE)) { + ret = a2xx_create_gpustate_shadow(adreno_dev, drawctxt); + if (ret) + goto done; + + drawctxt->flags |= CTXT_FLAGS_SHADER_SAVE; + } + + if (!(drawctxt->flags & CTXT_FLAGS_NOGMEMALLOC)) { + ret = a2xx_create_gmem_shadow(adreno_dev, drawctxt); + if (ret) + goto done; + } + + /* Flush and sync the gpustate memory */ + + kgsl_cache_range_op(&drawctxt->gpustate, + KGSL_CACHE_OP_FLUSH); + + kgsl_cffdump_syncmem(NULL, &drawctxt->gpustate, + drawctxt->gpustate.gpuaddr, + drawctxt->gpustate.size, false); + +done: + if (ret) + kgsl_sharedmem_free(&drawctxt->gpustate); + + return ret; +} + +static void a2xx_drawctxt_save(struct adreno_device *adreno_dev, struct adreno_context *context) { struct kgsl_device *device = &adreno_dev->dev; + unsigned int cmd[22]; if (context == NULL) return; @@ -1330,24 +1434,27 @@ static void a2xx_ctxt_save(struct adreno_device *adreno_dev, KGSL_CTXT_WARN(device, "Current active context has caused gpu hang\n"); - KGSL_CTXT_INFO(device, - "active context flags %08x\n", context->flags); + if (!(context->flags & CTXT_FLAGS_PREAMBLE)) { - /* save registers and constants. */ - adreno_ringbuffer_issuecmds(device, 0, context->reg_save, 3); + /* save registers and constants. */ + adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, + context->reg_save, 3); - if (context->flags & CTXT_FLAGS_SHADER_SAVE) { - /* save shader partitioning and instructions. */ - adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, - context->shader_save, 3); + if (context->flags & CTXT_FLAGS_SHADER_SAVE) { + /* save shader partitioning and instructions. */ + adreno_ringbuffer_issuecmds(device, + KGSL_CMD_FLAGS_PMODE, + context->shader_save, 3); - /* fixup shader partitioning parameter for - * SET_SHADER_BASES. - */ - adreno_ringbuffer_issuecmds(device, 0, - context->shader_fixup, 3); + /* + * fixup shader partitioning parameter for + * SET_SHADER_BASES. + */ + adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, + context->shader_fixup, 3); - context->flags |= CTXT_FLAGS_SHADER_RESTORE; + context->flags |= CTXT_FLAGS_SHADER_RESTORE; + } } if ((context->flags & CTXT_FLAGS_GMEM_SAVE) && @@ -1359,14 +1466,40 @@ static void a2xx_ctxt_save(struct adreno_device *adreno_dev, context->context_gmem_shadow.gmem_save, 3); /* Restore TP0_CHICKEN */ - adreno_ringbuffer_issuecmds(device, 0, - context->chicken_restore, 3); + if (!(context->flags & CTXT_FLAGS_PREAMBLE)) { + adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, + context->chicken_restore, 3); + } context->flags |= CTXT_FLAGS_GMEM_RESTORE; + } else if (adreno_is_a225(adreno_dev)) { + unsigned int *cmds = &cmd[0]; + /* + * Issue an empty draw call to avoid possible hangs due to + * repeated idles without intervening draw calls. + * On adreno 225 the PC block has a cache that is only + * flushed on draw calls and repeated idles can make it + * overflow. The gmem save path contains draw calls so + * this workaround isn't needed there. + */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = (0x4 << 16) | (REG_PA_SU_SC_MODE_CNTL - 0x2000); + *cmds++ = 0; + *cmds++ = cp_type3_packet(CP_DRAW_INDX, 5); + *cmds++ = 0; + *cmds++ = 1<<14; + *cmds++ = 0; + *cmds++ = device->mmu.setstate_memory.gpuaddr; + *cmds++ = 0; + *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmds++ = 0x00000000; + + adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, + &cmd[0], 11); } } -static void a2xx_ctxt_restore(struct adreno_device *adreno_dev, +static void a2xx_drawctxt_restore(struct adreno_device *adreno_dev, struct adreno_context *context) { struct kgsl_device *device = &adreno_dev->dev; @@ -1386,7 +1519,7 @@ static void a2xx_ctxt_restore(struct adreno_device *adreno_dev, cmds[3] = device->memstore.gpuaddr + KGSL_DEVICE_MEMSTORE_OFFSET(current_context); cmds[4] = (unsigned int) context; - adreno_ringbuffer_issuecmds(device, 0, cmds, 5); + adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, cmds, 5); kgsl_mmu_setstate(device, context->pagetable); #ifndef CONFIG_MSM_KGSL_CFF_DUMP_NO_CONTEXT_MEM_DUMP @@ -1402,27 +1535,34 @@ static void a2xx_ctxt_restore(struct adreno_device *adreno_dev, adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, context->context_gmem_shadow.gmem_restore, 3); - /* Restore TP0_CHICKEN */ - adreno_ringbuffer_issuecmds(device, 0, - context->chicken_restore, 3); + if (!(context->flags & CTXT_FLAGS_PREAMBLE)) { + /* Restore TP0_CHICKEN */ + adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, + context->chicken_restore, 3); + } context->flags &= ~CTXT_FLAGS_GMEM_RESTORE; } - /* restore registers and constants. */ - adreno_ringbuffer_issuecmds(device, 0, - context->reg_restore, 3); + if (!(context->flags & CTXT_FLAGS_PREAMBLE)) { - /* restore shader instructions & partitioning. */ - if (context->flags & CTXT_FLAGS_SHADER_RESTORE) { - adreno_ringbuffer_issuecmds(device, 0, - context->shader_restore, 3); + /* restore registers and constants. */ + adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, + context->reg_restore, 3); + + /* restore shader instructions & partitioning. */ + if (context->flags & CTXT_FLAGS_SHADER_RESTORE) { + adreno_ringbuffer_issuecmds(device, + KGSL_CMD_FLAGS_NONE, + context->shader_restore, 3); + } } if (adreno_is_a20x(adreno_dev)) { cmds[0] = cp_type3_packet(CP_SET_BIN_BASE_OFFSET, 1); cmds[1] = context->bin_base_offset; - adreno_ringbuffer_issuecmds(device, 0, cmds, 2); + adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, + cmds, 2); } } @@ -1485,6 +1625,9 @@ static void a2xx_cp_intrcallback(struct kgsl_device *device) KGSL_DRV_WARN(device, "Looped %d times to read REG_CP_INT_STATUS\n", num_reads); + + trace_kgsl_a2xx_irq_status(device, master_status, status); + if (!status) { if (master_status & MASTER_INT_SIGNAL__CP_INT_STAT) { /* This indicates that we could not read CP_INT_STAT. @@ -1606,11 +1749,15 @@ static void a2xx_irq_control(struct adreno_device *adreno_dev, int state) wmb(); } +/* Defined in adreno_a2xx_snapshot.c */ +void *a2xx_snapshot(struct adreno_device *adreno_dev, void *snapshot, + int *remain, int hang); + struct adreno_gpudev adreno_a2xx_gpudev = { - .ctxt_gpustate_shadow = a2xx_ctxt_gpustate_shadow, - .ctxt_gmem_shadow = a2xx_ctxt_gmem_shadow, - .ctxt_save = a2xx_ctxt_save, - .ctxt_restore = a2xx_ctxt_restore, + .ctxt_create = a2xx_drawctxt_create, + .ctxt_save = a2xx_drawctxt_save, + .ctxt_restore = a2xx_drawctxt_restore, .irq_handler = a2xx_irq_handler, .irq_control = a2xx_irq_control, + .snapshot = a2xx_snapshot, }; diff --git a/drivers/gpu/msm/adreno_a2xx_snapshot.c b/drivers/gpu/msm/adreno_a2xx_snapshot.c new file mode 100644 index 0000000000000..30d692b9f61fc --- /dev/null +++ b/drivers/gpu/msm/adreno_a2xx_snapshot.c @@ -0,0 +1,356 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "kgsl.h" +#include "adreno.h" +#include "kgsl_snapshot.h" + +#define DEBUG_SECTION_SZ(_dwords) (((_dwords) * sizeof(unsigned int)) \ + + sizeof(struct kgsl_snapshot_debug)) + +/* Dump the SX debug registers into a GPU snapshot debug section */ + +#define SXDEBUG_COUNT 0x1B + +static int a2xx_snapshot_sxdebug(struct kgsl_device *device, void *snapshot, + int remain, void *priv) +{ + struct kgsl_snapshot_debug *header = snapshot; + unsigned int *data = snapshot + sizeof(*header); + int i; + + if (remain < DEBUG_SECTION_SZ(SXDEBUG_COUNT)) { + SNAPSHOT_ERR_NOMEM(device, "SX DEBUG"); + return 0; + } + + header->type = SNAPSHOT_DEBUG_SX; + header->size = SXDEBUG_COUNT; + + for (i = 0; i < SXDEBUG_COUNT; i++) { + adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1B00 | i); + adreno_regread(device, REG_RBBM_DEBUG_OUT, &data[i]); + } + + adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0); + + return DEBUG_SECTION_SZ(SXDEBUG_COUNT); +} + +#define CPDEBUG_COUNT 0x20 + +static int a2xx_snapshot_cpdebug(struct kgsl_device *device, void *snapshot, + int remain, void *priv) +{ + struct kgsl_snapshot_debug *header = snapshot; + unsigned int *data = snapshot + sizeof(*header); + int i; + + if (remain < DEBUG_SECTION_SZ(CPDEBUG_COUNT)) { + SNAPSHOT_ERR_NOMEM(device, "CP DEBUG"); + return 0; + } + + header->type = SNAPSHOT_DEBUG_CP; + header->size = CPDEBUG_COUNT; + + for (i = 0; i < CPDEBUG_COUNT; i++) { + adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1628); + adreno_regread(device, REG_RBBM_DEBUG_OUT, &data[i]); + } + + adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0); + + return DEBUG_SECTION_SZ(CPDEBUG_COUNT); +} + +/* + * The contents of the SQ debug sections are dword pairs: + * [register offset]:[value] + * This macro writes both dwords for the given register + */ + +#define SQ_DEBUG_WRITE(_device, _reg, _data, _offset) \ + do { _data[(_offset)++] = (_reg); \ + adreno_regread(_device, (_reg), &_data[(_offset)++]); } while (0) + +#define SQ_DEBUG_BANK_SIZE 23 + +static int a2xx_snapshot_sqdebug(struct kgsl_device *device, void *snapshot, + int remain, void *priv) +{ + struct kgsl_snapshot_debug *header = snapshot; + unsigned int *data = snapshot + sizeof(*header); + int i, offset = 0; + int size = SQ_DEBUG_BANK_SIZE * 2 * 2; + + if (remain < DEBUG_SECTION_SZ(size)) { + SNAPSHOT_ERR_NOMEM(device, "SQ Debug"); + return 0; + } + + header->type = SNAPSHOT_DEBUG_SQ; + header->size = size; + + for (i = 0; i < 2; i++) { + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_CONST_MGR_FSM+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_EXP_ALLOC+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_FSM_ALU_0+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_FSM_ALU_1+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_GPR_PIX+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_GPR_VTX+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_INPUT_FSM+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_MISC+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_MISC_0+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_MISC_1+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_0+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATE_MEM+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, + REG_SQ_DEBUG_PIX_TB_STATUS_REG_0+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, + REG_SQ_DEBUG_PIX_TB_STATUS_REG_1+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, + REG_SQ_DEBUG_PIX_TB_STATUS_REG_2+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, + REG_SQ_DEBUG_PIX_TB_STATUS_REG_3+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PTR_BUFF+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_TB_STATUS_SEL+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_TP_FSM+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_0+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_1+i*0x1000, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_STATE_MEM+i*0x1000, + data, offset); + } + + return DEBUG_SECTION_SZ(size); +} + +#define SQ_DEBUG_THREAD_SIZE 7 + +static int a2xx_snapshot_sqthreaddebug(struct kgsl_device *device, + void *snapshot, int remain, void *priv) +{ + struct kgsl_snapshot_debug *header = snapshot; + unsigned int *data = snapshot + sizeof(*header); + int i, offset = 0; + int size = SQ_DEBUG_THREAD_SIZE * 2 * 16; + + if (remain < DEBUG_SECTION_SZ(size)) { + SNAPSHOT_ERR_NOMEM(device, "SQ THREAD DEBUG"); + return 0; + } + + header->type = SNAPSHOT_DEBUG_SQTHREAD; + header->size = size; + + for (i = 0; i < 16; i++) { + adreno_regwrite(device, REG_SQ_DEBUG_TB_STATUS_SEL, + i | (6<<4) | (i<<7) | (1<<11) | (1<<12) + | (i<<16) | (6<<20) | (i<<23)); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_STATE_MEM, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_VTX_TB_STATUS_REG, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATE_MEM, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATUS_REG_0, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATUS_REG_1, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATUS_REG_2, + data, offset); + SQ_DEBUG_WRITE(device, REG_SQ_DEBUG_PIX_TB_STATUS_REG_3, + data, offset); + } + + return DEBUG_SECTION_SZ(size); +} + +#define MIUDEBUG_COUNT 0x10 + +static int a2xx_snapshot_miudebug(struct kgsl_device *device, void *snapshot, + int remain, void *priv) +{ + struct kgsl_snapshot_debug *header = snapshot; + unsigned int *data = snapshot + sizeof(*header); + int i; + + if (remain < DEBUG_SECTION_SZ(MIUDEBUG_COUNT)) { + SNAPSHOT_ERR_NOMEM(device, "MIU DEBUG"); + return 0; + } + + header->type = SNAPSHOT_DEBUG_MIU; + header->size = MIUDEBUG_COUNT; + + for (i = 0; i < MIUDEBUG_COUNT; i++) { + adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0x1600 | i); + adreno_regread(device, REG_RBBM_DEBUG_OUT, &data[i]); + } + + adreno_regwrite(device, REG_RBBM_DEBUG_CNTL, 0); + + return DEBUG_SECTION_SZ(MIUDEBUG_COUNT); +} + +/* Helper function to snapshot a section of indexed registers */ + +static void *a2xx_snapshot_indexed_registers(struct kgsl_device *device, + void *snapshot, int *remain, + unsigned int index, unsigned int data, unsigned int start, + unsigned int count) +{ + struct kgsl_snapshot_indexed_registers iregs; + iregs.index = index; + iregs.data = data; + iregs.start = start; + iregs.count = count; + + return kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_INDEXED_REGS, snapshot, + remain, kgsl_snapshot_dump_indexed_regs, &iregs); +} + +/* A2XX GPU snapshot function - this is where all of the A2XX specific + * bits and pieces are grabbed into the snapshot memory + */ + +void *a2xx_snapshot(struct adreno_device *adreno_dev, void *snapshot, + int *remain, int hang) +{ + struct kgsl_device *device = &adreno_dev->dev; + struct kgsl_snapshot_registers regs; + unsigned int pmoverride; + + /* Choose the register set to dump */ + + if (adreno_is_a20x(adreno_dev)) { + regs.regs = (unsigned int *) a200_registers; + regs.count = a200_registers_count; + } else { + regs.regs = (unsigned int *) a220_registers; + regs.count = a220_registers_count; + } + + /* Master set of (non debug) registers */ + snapshot = kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_REGS, snapshot, remain, + kgsl_snapshot_dump_regs, ®s); + + /* CP_STATE_DEBUG indexed registers */ + snapshot = a2xx_snapshot_indexed_registers(device, snapshot, + remain, REG_CP_STATE_DEBUG_INDEX, + REG_CP_STATE_DEBUG_DATA, 0x0, 0x14); + + /* CP_ME indexed registers */ + snapshot = a2xx_snapshot_indexed_registers(device, snapshot, + remain, REG_CP_ME_CNTL, REG_CP_ME_STATUS, + 64, 44); + + /* + * Need to temporarily turn off clock gating for the debug bus to + * work + */ + + adreno_regread(device, REG_RBBM_PM_OVERRIDE2, &pmoverride); + adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, 0xFF); + + /* SX debug registers */ + snapshot = kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, + a2xx_snapshot_sxdebug, NULL); + + /* SU debug indexed registers (only for < 470) */ + if (!adreno_is_a22x(adreno_dev)) + snapshot = a2xx_snapshot_indexed_registers(device, snapshot, + remain, REG_PA_SU_DEBUG_CNTL, + REG_PA_SU_DEBUG_DATA, + 0, 0x1B); + + /* CP debug registers */ + snapshot = kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, + a2xx_snapshot_cpdebug, NULL); + + /* MH debug indexed registers */ + snapshot = a2xx_snapshot_indexed_registers(device, snapshot, + remain, MH_DEBUG_CTRL, MH_DEBUG_DATA, 0x0, 0x40); + + /* Leia only register sets */ + if (adreno_is_a22x(adreno_dev)) { + /* RB DEBUG indexed regisers */ + snapshot = a2xx_snapshot_indexed_registers(device, snapshot, + remain, REG_RB_DEBUG_CNTL, REG_RB_DEBUG_DATA, 0, 8); + + /* RB DEBUG indexed registers bank 2 */ + snapshot = a2xx_snapshot_indexed_registers(device, snapshot, + remain, REG_RB_DEBUG_CNTL, REG_RB_DEBUG_DATA + 0x1000, + 0, 8); + + /* PC_DEBUG indexed registers */ + snapshot = a2xx_snapshot_indexed_registers(device, snapshot, + remain, REG_PC_DEBUG_CNTL, REG_PC_DEBUG_DATA, 0, 8); + + /* GRAS_DEBUG indexed registers */ + snapshot = a2xx_snapshot_indexed_registers(device, snapshot, + remain, REG_GRAS_DEBUG_CNTL, REG_GRAS_DEBUG_DATA, 0, 4); + + /* MIU debug registers */ + snapshot = kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, + a2xx_snapshot_miudebug, NULL); + + /* SQ DEBUG debug registers */ + snapshot = kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, + a2xx_snapshot_sqdebug, NULL); + + /* + * Reading SQ THREAD causes bad things to happen on a running + * system, so only read it if the GPU is already hung + */ + + if (hang) { + /* SQ THREAD debug registers */ + snapshot = kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain, + a2xx_snapshot_sqthreaddebug, NULL); + } + } + + /* Reset the clock gating */ + adreno_regwrite(device, REG_RBBM_PM_OVERRIDE2, pmoverride); + + return snapshot; +} diff --git a/drivers/gpu/msm/adreno_a2xx_trace.c b/drivers/gpu/msm/adreno_a2xx_trace.c new file mode 100644 index 0000000000000..c91d1a04ad89a --- /dev/null +++ b/drivers/gpu/msm/adreno_a2xx_trace.c @@ -0,0 +1,19 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "kgsl.h" +#include "adreno.h" + +/* Instantiate tracepoints */ +#define CREATE_TRACE_POINTS +#include "adreno_a2xx_trace.h" diff --git a/drivers/gpu/msm/adreno_a2xx_trace.h b/drivers/gpu/msm/adreno_a2xx_trace.h new file mode 100644 index 0000000000000..2528e153dbdd2 --- /dev/null +++ b/drivers/gpu/msm/adreno_a2xx_trace.h @@ -0,0 +1,78 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#if !defined(_ADRENO_A2XX_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _ADRENO_A2XX_TRACE_H + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM kgsl +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE adreno_a2xx_trace + +#include + +struct kgsl_device; + +/* + * Tracepoint for a2xx irq. Includes status info + */ +TRACE_EVENT(kgsl_a2xx_irq_status, + + TP_PROTO(struct kgsl_device *device, unsigned int master_status, + unsigned int status), + + TP_ARGS(device, master_status, status), + + TP_STRUCT__entry( + __string(device_name, device->name) + __field(unsigned int, master_status) + __field(unsigned int, status) + ), + + TP_fast_assign( + __assign_str(device_name, device->name); + __entry->master_status = master_status; + __entry->status = status; + ), + + TP_printk( + "d_name=%s master=%s status=%s", + __get_str(device_name), + __entry->master_status ? __print_flags(__entry->master_status, + "|", + { MASTER_INT_SIGNAL__MH_INT_STAT, "MH" }, + { MASTER_INT_SIGNAL__SQ_INT_STAT, "SQ" }, + { MASTER_INT_SIGNAL__CP_INT_STAT, "CP" }, + { MASTER_INT_SIGNAL__RBBM_INT_STAT, "RBBM" }) : "None", + __entry->status ? __print_flags(__entry->status, "|", + { CP_INT_CNTL__SW_INT_MASK, "SW" }, + { CP_INT_CNTL__T0_PACKET_IN_IB_MASK, + "T0_PACKET_IN_IB" }, + { CP_INT_CNTL__OPCODE_ERROR_MASK, "OPCODE_ERROR" }, + { CP_INT_CNTL__PROTECTED_MODE_ERROR_MASK, + "PROTECTED_MODE_ERROR" }, + { CP_INT_CNTL__RESERVED_BIT_ERROR_MASK, + "RESERVED_BIT_ERROR" }, + { CP_INT_CNTL__IB_ERROR_MASK, "IB_ERROR" }, + { CP_INT_CNTL__IB2_INT_MASK, "IB2" }, + { CP_INT_CNTL__IB1_INT_MASK, "IB1" }, + { CP_INT_CNTL__RB_INT_MASK, "RB" }) : "None" + ) +); + +#endif /* _ADRENO_A2XX_TRACE_H */ + +/* This part must be outside protection */ +#include diff --git a/drivers/gpu/msm/adreno_debugfs.c b/drivers/gpu/msm/adreno_debugfs.c index 419ce9d2287b7..c1b9e4ce24b2e 100644 --- a/drivers/gpu/msm/adreno_debugfs.c +++ b/drivers/gpu/msm/adreno_debugfs.c @@ -25,9 +25,6 @@ unsigned int kgsl_cff_dump_enable; int kgsl_pm_regs_enabled; -static uint32_t kgsl_ib_base; -static uint32_t kgsl_ib_size; - static struct dentry *pm_d_debugfs; static int pm_dump_set(void *data, u64 val) @@ -115,98 +112,6 @@ static int kgsl_hex_dump(const char *prefix, int c, uint8_t *data, return ss; } -static ssize_t kgsl_ib_dump_read( - struct file *file, - char __user *buff, - size_t buff_count, - loff_t *ppos) -{ - int i, count = kgsl_ib_size, remaining, pos = 0, tot = 0, ss; - struct kgsl_device *device = file->private_data; - const int rowc = 32; - unsigned int pt_base, ib_memsize; - uint8_t *base_addr; - char linebuf[80]; - - if (!ppos || !device || !kgsl_ib_base) - return 0; - - kgsl_regread(device, MH_MMU_PT_BASE, &pt_base); - base_addr = kgsl_sharedmem_convertaddr(device, pt_base, kgsl_ib_base, - &ib_memsize); - - if (!base_addr) - return 0; - - pr_info("%s ppos=%ld, buff_count=%d, count=%d\n", __func__, (long)*ppos, - buff_count, count); - ss = snprintf(linebuf, sizeof(linebuf), "IB: base=%08x(%08x" - "), size=%d, memsize=%d\n", kgsl_ib_base, - (uint32_t)base_addr, kgsl_ib_size, ib_memsize); - if (*ppos == 0) { - if (copy_to_user(buff, linebuf, ss+1)) - return -EFAULT; - tot += ss; - buff += ss; - *ppos += ss; - } - pos += ss; - remaining = count; - for (i = 0; i < count; i += rowc) { - int linec = min(remaining, rowc); - - remaining -= rowc; - ss = kgsl_hex_dump("IB: %05x: ", i, base_addr, rowc, linec, - buff); - if (ss < 0) - return ss; - - if (pos >= *ppos) { - if (tot+ss >= buff_count) { - ss = copy_to_user(buff, "", 1); - return tot; - } - tot += ss; - buff += ss; - *ppos += ss; - } - pos += ss; - base_addr += linec; - } - - return tot; -} - -static ssize_t kgsl_ib_dump_write( - struct file *file, - const char __user *buff, - size_t count, - loff_t *ppos) -{ - char local_buff[64]; - - if (count >= sizeof(local_buff)) - return -EFAULT; - - if (copy_from_user(local_buff, buff, count)) - return -EFAULT; - - local_buff[count] = 0; /* end of string */ - sscanf(local_buff, "%x %d", &kgsl_ib_base, &kgsl_ib_size); - - pr_info("%s: base=%08X size=%d\n", __func__, kgsl_ib_base, - kgsl_ib_size); - - return count; -} - -static const struct file_operations kgsl_ib_dump_fops = { - .open = kgsl_dbgfs_open, - .release = kgsl_dbgfs_release, - .read = kgsl_ib_dump_read, - .write = kgsl_ib_dump_write, -}; - static int kgsl_regread_nolock(struct kgsl_device *device, unsigned int offsetwords, unsigned int *value) { @@ -223,7 +128,6 @@ static int kgsl_regread_nolock(struct kgsl_device *device, return 0; } -#define ADRENO_ISTORE_START 0x5000 static ssize_t kgsl_istore_read( struct file *file, char __user *buff, @@ -429,8 +333,6 @@ void adreno_debugfs_init(struct kgsl_device *device) if (!device->d_debugfs || IS_ERR(device->d_debugfs)) return; - debugfs_create_file("ib_dump", 0600, device->d_debugfs, device, - &kgsl_ib_dump_fops); debugfs_create_file("istore", 0400, device->d_debugfs, device, &kgsl_istore_fops); debugfs_create_file("sx_debug", 0400, device->d_debugfs, device, diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c index b7b0ea46007a3..206a678eed877 100644 --- a/drivers/gpu/msm/adreno_drawctxt.c +++ b/drivers/gpu/msm/adreno_drawctxt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -139,27 +139,19 @@ int adreno_drawctxt_create(struct kgsl_device *device, drawctxt->pagetable = pagetable; drawctxt->bin_base_offset = 0; - /* FIXME: Deal with preambles */ + if (flags & KGSL_CONTEXT_PREAMBLE) + drawctxt->flags |= CTXT_FLAGS_PREAMBLE; - ret = adreno_dev->gpudev->ctxt_gpustate_shadow(adreno_dev, drawctxt); + if (flags & KGSL_CONTEXT_NO_GMEM_ALLOC) + drawctxt->flags |= CTXT_FLAGS_NOGMEMALLOC; + + ret = adreno_dev->gpudev->ctxt_create(adreno_dev, drawctxt); if (ret) goto err; - /* Save the shader instruction memory on context switching */ - drawctxt->flags |= CTXT_FLAGS_SHADER_SAVE; - - if (!(flags & KGSL_CONTEXT_NO_GMEM_ALLOC)) { - /* create gmem shadow */ - ret = adreno_dev->gpudev->ctxt_gmem_shadow(adreno_dev, - drawctxt); - if (ret != 0) - goto err; - } - context->devctxt = drawctxt; return 0; err: - kgsl_sharedmem_free(&drawctxt->gpustate); kfree(drawctxt); return ret; } diff --git a/drivers/gpu/msm/adreno_drawctxt.h b/drivers/gpu/msm/adreno_drawctxt.h index 3c3a8536136a7..30476603e04dc 100644 --- a/drivers/gpu/msm/adreno_drawctxt.h +++ b/drivers/gpu/msm/adreno_drawctxt.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -30,12 +30,16 @@ #define CTXT_FLAGS_GMEM_SAVE 0x00000200 /* gmem can be restored from shadow */ #define CTXT_FLAGS_GMEM_RESTORE 0x00000400 +/* preamble packed in cmdbuffer for context switching */ +#define CTXT_FLAGS_PREAMBLE 0x00000800 /* shader must be copied to shadow */ #define CTXT_FLAGS_SHADER_SAVE 0x00002000 /* shader can be restored from shadow */ #define CTXT_FLAGS_SHADER_RESTORE 0x00004000 /* Context has caused a GPU hang */ #define CTXT_FLAGS_GPU_HANG 0x00008000 +/* Specifies there is no need to save GMEM */ +#define CTXT_FLAGS_NOGMEMALLOC 0x00010000 struct kgsl_device; struct adreno_device; @@ -91,7 +95,7 @@ void adreno_drawctxt_switch(struct adreno_device *adreno_dev, struct adreno_context *drawctxt, unsigned int flags); void adreno_drawctxt_set_bin_base_offset(struct kgsl_device *device, - struct kgsl_context *context, + struct kgsl_context *context, unsigned int offset); /* GPU context switch helper functions */ diff --git a/drivers/gpu/msm/adreno_pm4types.h b/drivers/gpu/msm/adreno_pm4types.h index 8aea58c958b64..aa7652951fcc2 100644 --- a/drivers/gpu/msm/adreno_pm4types.h +++ b/drivers/gpu/msm/adreno_pm4types.h @@ -157,6 +157,16 @@ #define CP_SET_PROTECTED_MODE 0x5f /* sets the register protection mode */ +/* + * for a3xx + */ + +/* Conditionally load a IB based on a flag */ +#define CP_COND_INDIRECT_BUFFER_PFE 0x3A /* prefetch enabled */ +#define CP_COND_INDIRECT_BUFFER_PFD 0x32 /* prefetch disabled */ + +/* Load a buffer with pre-fetch enabled */ +#define CP_INDIRECT_BUFFER_PFE 0x3F /* packet header building macros */ #define cp_type0_packet(regindx, cnt) \ @@ -190,4 +200,14 @@ /* gmem command buffer length */ #define CP_REG(reg) ((0x4 << 16) | (SUBBLOCK_OFFSET(reg))) + +/* Return 1 if the command is an indirect buffer of any kind */ +static inline int adreno_cmd_is_ib(unsigned int cmd) +{ + return (cmd == cp_type3_packet(CP_INDIRECT_BUFFER_PFE, 2) || + cmd == cp_type3_packet(CP_INDIRECT_BUFFER_PFD, 2) || + cmd == cp_type3_packet(CP_COND_INDIRECT_BUFFER_PFE, 2) || + cmd == cp_type3_packet(CP_COND_INDIRECT_BUFFER_PFD, 2)); +} + #endif /* __ADRENO_PM4TYPES_H */ diff --git a/drivers/gpu/msm/adreno_postmortem.c b/drivers/gpu/msm/adreno_postmortem.c index cc693609b795b..78adb2f90daa7 100644 --- a/drivers/gpu/msm/adreno_postmortem.c +++ b/drivers/gpu/msm/adreno_postmortem.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -21,6 +21,7 @@ #include "adreno_postmortem.h" #include "adreno_debugfs.h" #include "kgsl_cffdump.h" +#include "kgsl_pwrctrl.h" #include "a2xx_reg.h" @@ -68,95 +69,6 @@ static const struct pm_id_name pm3_types[] = { {CP_WAIT_FOR_IDLE, "WAIT4IDL"}, }; -/* Offset address pairs: start, end of range to dump (inclusive) */ - -/* GPU < Z470 */ - -static const int a200_registers[] = { - 0x0000, 0x0008, 0x0010, 0x002c, 0x00ec, 0x00f4, - 0x0100, 0x0110, 0x0118, 0x011c, - 0x0700, 0x0704, 0x070c, 0x0720, 0x0754, 0x0764, - 0x0770, 0x0774, 0x07a8, 0x07a8, 0x07b8, 0x07cc, - 0x07d8, 0x07dc, 0x07f0, 0x07fc, 0x0e44, 0x0e48, - 0x0e6c, 0x0e78, 0x0ec8, 0x0ed4, 0x0edc, 0x0edc, - 0x0fe0, 0x0fec, 0x1100, 0x1100, - - 0x110c, 0x1110, 0x112c, 0x112c, 0x1134, 0x113c, - 0x1148, 0x1148, 0x1150, 0x116c, 0x11fc, 0x11fc, - 0x15e0, 0x161c, 0x1724, 0x1724, 0x1740, 0x1740, - 0x1804, 0x1810, 0x1818, 0x1824, 0x182c, 0x1838, - 0x184c, 0x1850, 0x28a4, 0x28ac, 0x28bc, 0x28c4, - 0x2900, 0x290c, 0x2914, 0x2914, 0x2938, 0x293c, - 0x30b0, 0x30b0, 0x30c0, 0x30c0, 0x30e0, 0x30f0, - 0x3100, 0x3100, 0x3110, 0x3110, 0x3200, 0x3218, - 0x3220, 0x3250, 0x3264, 0x3268, 0x3290, 0x3294, - 0x3400, 0x340c, 0x3418, 0x3418, 0x3420, 0x342c, - 0x34d0, 0x34d4, 0x36b8, 0x3704, 0x3720, 0x3750, - 0x3760, 0x3764, 0x3800, 0x3800, 0x3808, 0x3810, - 0x385c, 0x3878, 0x3b00, 0x3b24, 0x3b2c, 0x3b30, - 0x3b40, 0x3b40, 0x3b50, 0x3b5c, 0x3b80, 0x3b88, - 0x3c04, 0x3c08, 0x3c30, 0x3c30, 0x3c38, 0x3c48, - 0x3c98, 0x3ca8, 0x3cb0, 0x3cb0, - - 0x8000, 0x8008, 0x8018, 0x803c, 0x8200, 0x8208, - 0x8400, 0x8424, 0x8430, 0x8450, 0x8600, 0x8610, - 0x87d4, 0x87dc, 0x8800, 0x8820, 0x8a00, 0x8a0c, - 0x8a4c, 0x8a50, 0x8c00, 0x8c20, 0x8c48, 0x8c48, - 0x8c58, 0x8c74, 0x8c90, 0x8c98, 0x8e00, 0x8e0c, - - 0x9000, 0x9008, 0x9018, 0x903c, 0x9200, 0x9208, - 0x9400, 0x9424, 0x9430, 0x9450, 0x9600, 0x9610, - 0x97d4, 0x97dc, 0x9800, 0x9820, 0x9a00, 0x9a0c, - 0x9a4c, 0x9a50, 0x9c00, 0x9c20, 0x9c48, 0x9c48, - 0x9c58, 0x9c74, 0x9c90, 0x9c98, 0x9e00, 0x9e0c, - - 0x10000, 0x1000c, 0x12000, 0x12014, - 0x12400, 0x12400, 0x12420, 0x12420 -}; - -/* GPU = Z470 */ - -static const int a220_registers[] = { - 0x0000, 0x0008, 0x0010, 0x002c, 0x00ec, 0x00f4, - 0x0100, 0x0110, 0x0118, 0x011c, - 0x0700, 0x0704, 0x070c, 0x0720, 0x0754, 0x0764, - 0x0770, 0x0774, 0x07a8, 0x07a8, 0x07b8, 0x07cc, - 0x07d8, 0x07dc, 0x07f0, 0x07fc, 0x0e44, 0x0e48, - 0x0e6c, 0x0e78, 0x0ec8, 0x0ed4, 0x0edc, 0x0edc, - 0x0fe0, 0x0fec, 0x1100, 0x1100, - - 0x110c, 0x1110, 0x112c, 0x112c, 0x1134, 0x113c, - 0x1148, 0x1148, 0x1150, 0x116c, 0x11fc, 0x11fc, - 0x15e0, 0x161c, 0x1724, 0x1724, 0x1740, 0x1740, - 0x1804, 0x1810, 0x1818, 0x1824, 0x182c, 0x1838, - 0x184c, 0x1850, 0x28a4, 0x28ac, 0x28bc, 0x28c4, - 0x2900, 0x2900, 0x2908, 0x290c, 0x2914, 0x2914, - 0x2938, 0x293c, 0x30c0, 0x30c0, 0x30e0, 0x30e4, - 0x30f0, 0x30f0, 0x3200, 0x3204, 0x3220, 0x324c, - 0x3400, 0x340c, 0x3414, 0x3418, 0x3420, 0x342c, - 0x34d0, 0x34d4, 0x36b8, 0x3704, 0x3720, 0x3750, - 0x3760, 0x3764, 0x3800, 0x3800, 0x3808, 0x3810, - 0x385c, 0x3878, 0x3b00, 0x3b24, 0x3b2c, 0x3b30, - 0x3b40, 0x3b40, 0x3b50, 0x3b5c, 0x3b80, 0x3b88, - 0x3c04, 0x3c08, 0x8000, 0x8008, 0x8018, 0x803c, - 0x8200, 0x8208, 0x8400, 0x8408, 0x8410, 0x8424, - 0x8430, 0x8450, 0x8600, 0x8610, 0x87d4, 0x87dc, - 0x8800, 0x8808, 0x8810, 0x8810, 0x8820, 0x8820, - 0x8a00, 0x8a08, 0x8a50, 0x8a50, - 0x8c00, 0x8c20, 0x8c24, 0x8c28, 0x8c48, 0x8c48, - 0x8c58, 0x8c58, 0x8c60, 0x8c74, 0x8c90, 0x8c98, - 0x8e00, 0x8e0c, 0x9000, 0x9008, 0x9018, 0x903c, - 0x9200, 0x9208, 0x9400, 0x9408, 0x9410, 0x9424, - 0x9430, 0x9450, 0x9600, 0x9610, 0x97d4, 0x97dc, - 0x9800, 0x9808, 0x9810, 0x9818, 0x9820, 0x9820, - 0x9a00, 0x9a08, 0x9a50, 0x9a50, 0x9c00, 0x9c20, - 0x9c48, 0x9c48, 0x9c58, 0x9c58, 0x9c60, 0x9c74, - 0x9c90, 0x9c98, 0x9e00, 0x9e0c, - - 0x10000, 0x1000c, 0x12000, 0x12014, - 0x12400, 0x12400, 0x12420, 0x12420 -}; - static uint32_t adreno_is_pm4_len(uint32_t word) { if (word == INVALID_RB_CMD) @@ -223,8 +135,8 @@ static void adreno_dump_regs(struct kgsl_device *device, for (range = 0; range < size; range++) { /* start and end are in dword offsets */ - int start = registers[range * 2] / 4; - int end = registers[range * 2 + 1] / 4; + int start = registers[range * 2]; + int end = registers[range * 2 + 1]; unsigned char linebuf[32 * 3 + 2 + 32 + 1]; int linelen, i; @@ -239,7 +151,7 @@ static void adreno_dump_regs(struct kgsl_device *device, hex_dump_to_buffer(regvals, linelen*4, 32, 4, linebuf, sizeof(linebuf), 0); KGSL_LOG_DUMP(device, - "REG: %5.5X: %s\n", offset<<2, linebuf); + "REG: %5.5X: %s\n", offset, linebuf); } } } @@ -247,9 +159,8 @@ static void adreno_dump_regs(struct kgsl_device *device, static void dump_ib(struct kgsl_device *device, char* buffId, uint32_t pt_base, uint32_t base_offset, uint32_t ib_base, uint32_t ib_size, bool dump) { - unsigned int memsize; - uint8_t *base_addr = kgsl_sharedmem_convertaddr(device, pt_base, - ib_base, &memsize); + uint8_t *base_addr = adreno_convertaddr(device, pt_base, + ib_base, ib_size*sizeof(uint32_t)); if (base_addr && dump) print_hex_dump(KERN_ERR, buffId, DUMP_PREFIX_OFFSET, @@ -277,20 +188,19 @@ static void dump_ib1(struct kgsl_device *device, uint32_t pt_base, int i, j; uint32_t value; uint32_t *ib1_addr; - unsigned int memsize; dump_ib(device, "IB1:", pt_base, base_offset, ib1_base, ib1_size, dump); /* fetch virtual address for given IB base */ - ib1_addr = (uint32_t *)kgsl_sharedmem_convertaddr(device, pt_base, - ib1_base, &memsize); + ib1_addr = (uint32_t *)adreno_convertaddr(device, pt_base, + ib1_base, ib1_size*sizeof(uint32_t)); if (!ib1_addr) return; for (i = 0; i+3 < ib1_size; ) { value = ib1_addr[i++]; - if (value == cp_type3_packet(CP_INDIRECT_BUFFER_PFD, 2)) { + if (adreno_cmd_is_ib(value)) { uint32_t ib2_base = ib1_addr[i++]; uint32_t ib2_size = ib1_addr[i++]; @@ -466,29 +376,14 @@ static int adreno_dump(struct kgsl_device *device) const uint32_t *rb_vaddr; int num_item = 0; int read_idx, write_idx; - unsigned int ts_processed, rb_memsize; + unsigned int ts_processed; static struct ib_list ib_list; struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct kgsl_pwrctrl *pwr = &device->pwrctrl; - mb(); - KGSL_LOG_DUMP(device, "POWER: FLAGS = %08lX | ACTIVE POWERLEVEL = %08X", - pwr->power_flags, pwr->active_pwrlevel); - - KGSL_LOG_DUMP(device, "POWER: INTERVAL TIMEOUT = %08X ", - pwr->interval_timeout); - - KGSL_LOG_DUMP(device, "GRP_CLK = %lu ", - kgsl_get_clkrate(pwr->grp_clks[0])); - - KGSL_LOG_DUMP(device, "BUS CLK = %lu ", - kgsl_get_clkrate(pwr->ebi1_clk)); - - kgsl_regread(device, REG_RBBM_STATUS, &rbbm_status); kgsl_regread(device, REG_RBBM_PM_OVERRIDE1, &r2); kgsl_regread(device, REG_RBBM_PM_OVERRIDE2, &r3); @@ -681,11 +576,16 @@ static int adreno_dump(struct kgsl_device *device) KGSL_LOG_DUMP(device, "RB: rd_addr:%8.8x rb_size:%d num_item:%d\n", cp_rb_base, rb_count<<2, num_item); - rb_vaddr = (const uint32_t *)kgsl_sharedmem_convertaddr(device, - cur_pt_base, cp_rb_base, &rb_memsize); + + if (adreno_dev->ringbuffer.buffer_desc.gpuaddr != cp_rb_base) + KGSL_LOG_POSTMORTEM_WRITE(device, + "rb address mismatch, should be 0x%08x\n", + adreno_dev->ringbuffer.buffer_desc.gpuaddr); + + rb_vaddr = adreno_dev->ringbuffer.buffer_desc.hostptr; if (!rb_vaddr) { KGSL_LOG_POSTMORTEM_WRITE(device, - "Can't fetch vaddr for CP_RB_BASE\n"); + "rb has no kernel mapping!\n"); goto error_vfree; } @@ -711,7 +611,7 @@ static int adreno_dump(struct kgsl_device *device) i = 0; for (read_idx = 0; read_idx < num_item; ) { uint32_t this_cmd = rb_copy[read_idx++]; - if (this_cmd == cp_type3_packet(CP_INDIRECT_BUFFER_PFD, 2)) { + if (adreno_cmd_is_ib(this_cmd)) { uint32_t ib_addr = rb_copy[read_idx++]; uint32_t ib_size = rb_copy[read_idx++]; dump_ib1(device, cur_pt_base, (read_idx-3)<<2, ib_addr, @@ -754,8 +654,7 @@ static int adreno_dump(struct kgsl_device *device) for (read_idx = NUM_DWORDS_OF_RINGBUFFER_HISTORY; read_idx >= 0; --read_idx) { uint32_t this_cmd = rb_copy[read_idx]; - if (this_cmd == cp_type3_packet( - CP_INDIRECT_BUFFER_PFD, 2)) { + if (adreno_cmd_is_ib(this_cmd)) { uint32_t ib_addr = rb_copy[read_idx+1]; uint32_t ib_size = rb_copy[read_idx+2]; if (ib_size && cp_ib1_base == ib_addr) { @@ -785,10 +684,10 @@ static int adreno_dump(struct kgsl_device *device) if (adreno_is_a20x(adreno_dev)) adreno_dump_regs(device, a200_registers, - ARRAY_SIZE(a200_registers) / 2); + a200_registers_count); else if (adreno_is_a22x(adreno_dev)) adreno_dump_regs(device, a220_registers, - ARRAY_SIZE(a220_registers) / 2); + a220_registers_count); error_vfree: vfree(rb_copy); @@ -807,6 +706,7 @@ static int adreno_dump(struct kgsl_device *device) int adreno_postmortem_dump(struct kgsl_device *device, int manual) { bool saved_nap; + struct kgsl_pwrctrl *pwr = &device->pwrctrl; BUG_ON(device == NULL); @@ -825,8 +725,23 @@ int adreno_postmortem_dump(struct kgsl_device *device, int manual) kgsl_idle(device, KGSL_TIMEOUT_DEFAULT); } + KGSL_LOG_DUMP(device, "POWER: FLAGS = %08lX | ACTIVE POWERLEVEL = %08X", + pwr->power_flags, pwr->active_pwrlevel); + + KGSL_LOG_DUMP(device, "POWER: INTERVAL TIMEOUT = %08X ", + pwr->interval_timeout); + + KGSL_LOG_DUMP(device, "GRP_CLK = %lu ", + kgsl_get_clkrate(pwr->grp_clks[0])); + + KGSL_LOG_DUMP(device, "BUS CLK = %lu ", + kgsl_get_clkrate(pwr->ebi1_clk)); + /* Disable the idle timer so we don't get interrupted */ del_timer_sync(&device->idle_timer); + mutex_unlock(&device->mutex); + flush_workqueue(device->work_queue); + mutex_lock(&device->mutex); /* Turn off napping to make sure we have the clocks full attention through the following process */ @@ -839,21 +754,6 @@ int adreno_postmortem_dump(struct kgsl_device *device, int manual) /* Disable the irq */ kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); - /* If this is not a manual trigger, then set up the - state to try to recover */ - - if (!manual) { - device->state = KGSL_STATE_DUMP_AND_RECOVER; - KGSL_PWR_WARN(device, - "state -> DUMP_AND_RECOVER, device %d\n", - device->id); - } - - KGSL_DRV_ERR(device, - "wait for work in workqueue to complete\n"); - mutex_unlock(&device->mutex); - flush_workqueue(device->work_queue); - mutex_lock(&device->mutex); adreno_dump(device); /* Restore nap mode */ @@ -868,7 +768,7 @@ int adreno_postmortem_dump(struct kgsl_device *device, int manual) kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON); /* try to go into a sleep mode until the next event */ - device->requested_state = KGSL_STATE_SLEEP; + kgsl_pwrctrl_request_state(device, KGSL_STATE_SLEEP); kgsl_pwrctrl_sleep(device); } diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index a0982006798fa..cff6ed7b1c328 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -310,6 +310,11 @@ int adreno_ringbuffer_start(struct adreno_ringbuffer *rb, unsigned int init_ram) adreno_regwrite(device, REG_SCRATCH_UMSK, GSL_RB_MEMPTRS_SCRATCH_MASK); + /* update the eoptimestamp field with the last retired timestamp */ + kgsl_sharedmem_writel(&device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp), + rb->timestamp); + /* load the CP ucode */ status = adreno_ringbuffer_load_pm4_ucode(device); @@ -393,7 +398,6 @@ void adreno_ringbuffer_stop(struct adreno_ringbuffer *rb) if (rb->flags & KGSL_FLAGS_STARTED) { /* ME_HALT */ adreno_regwrite(rb->device, REG_CP_ME_CNTL, 0x10000000); - rb->flags &= ~KGSL_FLAGS_STARTED; } } @@ -562,6 +566,7 @@ adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv, unsigned int *cmds; unsigned int i; struct adreno_context *drawctxt; + unsigned int start_index = 0; if (device->state & KGSL_STATE_HUNG) return -EBUSY; @@ -584,7 +589,16 @@ adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv, " submission, size %x\n", numibs * 3); return -ENOMEM; } - for (i = 0; i < numibs; i++) { + + /*When preamble is enabled, the preamble buffer with state restoration + commands are stored in the first node of the IB chain. We can skip that + if a context switch hasn't occured */ + + if (drawctxt->flags & CTXT_FLAGS_PREAMBLE && + adreno_dev->drawctxt_active == drawctxt) + start_index = 1; + + for (i = start_index; i < numibs; i++) { (void)kgsl_cffdump_parse_ibs(dev_priv, NULL, ibdesc[i].gpuaddr, ibdesc[i].sizedwords, false); @@ -741,8 +755,20 @@ int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, rb->buffer_desc.size); - BUG_ON((copy_rb_contents == 0) && - (value == cur_context)); + + /* + * If other context switches were already lost and + * and the current context is the one that is hanging, + * then we cannot recover. Print an error message + * and leave. + */ + + if ((copy_rb_contents == 0) && (value == cur_context)) { + KGSL_DRV_ERR(device, "GPU recovery could not " + "find the previous context\n"); + return -EINVAL; + } + /* * If we were copying the commands and got to this point * then we need to remove the 3 commands that appear diff --git a/drivers/gpu/msm/adreno_ringbuffer.h b/drivers/gpu/msm/adreno_ringbuffer.h index a90b0cb9b8b8b..6c4926dc51581 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.h +++ b/drivers/gpu/msm/adreno_ringbuffer.h @@ -64,7 +64,7 @@ struct adreno_ringbuffer { #define GSL_RB_WRITE(ring, gpuaddr, data) \ do { \ - writel_relaxed(data, ring); \ + *ring = data; \ wmb(); \ kgsl_cffdump_setmem(gpuaddr, data, 4); \ ring++; \ @@ -93,7 +93,7 @@ struct adreno_ringbuffer { #define GSL_RB_CNTL_NO_UPDATE 0x0 /* enable */ #define GSL_RB_GET_READPTR(rb, data) \ do { \ - *(data) = readl_relaxed(&(rb)->memptrs->rptr); \ + *(data) = rb->memptrs->rptr; \ } while (0) #else #define GSL_RB_CNTL_NO_UPDATE 0x1 /* disable */ diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c new file mode 100644 index 0000000000000..c45dbff489426 --- /dev/null +++ b/drivers/gpu/msm/adreno_snapshot.c @@ -0,0 +1,429 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "kgsl.h" +#include "kgsl_sharedmem.h" +#include "kgsl_snapshot.h" + +#include "adreno.h" +#include "adreno_pm4types.h" +#include "a2xx_reg.h" + +/* Number of dwords of ringbuffer history to record */ +#define NUM_DWORDS_OF_RINGBUFFER_HISTORY 100 + +/* Maintain a list of the objects we see during parsing */ + +#define SNAPSHOT_OBJ_BUFSIZE 64 + +#define SNAPSHOT_OBJ_TYPE_IB 0 + +static struct kgsl_snapshot_obj { + int type; + uint32_t gpuaddr; + uint32_t ptbase; + void *ptr; + int dwords; +} objbuf[SNAPSHOT_OBJ_BUFSIZE]; + +/* Pointer to the next open entry in the object list */ +static int objbufptr; + +/* Push a new buffer object onto the list */ +static void push_object(struct kgsl_device *device, int type, uint32_t ptbase, + uint32_t gpuaddr, int dwords) +{ + int index; + void *ptr; + + /* + * Sometimes IBs can be reused in the same dump. Because we parse from + * oldest to newest, if we come across an IB that has already been used, + * assume that it has been reused and update the list with the newest + * size. + */ + + for (index = 0; index < objbufptr; index++) { + if (objbuf[index].gpuaddr == gpuaddr && + objbuf[index].ptbase == ptbase) { + objbuf[index].dwords = dwords; + return; + } + } + + if (objbufptr == SNAPSHOT_OBJ_BUFSIZE) { + KGSL_DRV_ERR(device, "snapshot: too many snapshot objects\n"); + return; + } + + /* + * adreno_convertaddr verifies that the IB size is valid - at least in + * the context of it being smaller then the allocated memory space + */ + ptr = adreno_convertaddr(device, ptbase, gpuaddr, dwords << 2); + + if (ptr == NULL) { + KGSL_DRV_ERR(device, + "snapshot: Can't find GPU address for %x\n", gpuaddr); + return; + } + + /* Put it on the list of things to parse */ + objbuf[objbufptr].type = type; + objbuf[objbufptr].gpuaddr = gpuaddr; + objbuf[objbufptr].ptbase = ptbase; + objbuf[objbufptr].dwords = dwords; + objbuf[objbufptr++].ptr = ptr; +} + +/* + * Return a 1 if the specified object is already on the list of buffers + * to be dumped + */ + +static int find_object(int type, unsigned int gpuaddr, unsigned int ptbase) +{ + int index; + + for (index = 0; index < objbufptr; index++) { + if (objbuf[index].gpuaddr == gpuaddr && + objbuf[index].ptbase == ptbase && + objbuf[index].type == type) + return 1; + } + + return 0; +} + +/* Snapshot the istore memory */ +static int snapshot_istore(struct kgsl_device *device, void *snapshot, + int remain, void *priv) +{ + struct kgsl_snapshot_istore *header = snapshot; + unsigned int *data = snapshot + sizeof(*header); + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + int count, i; + + count = adreno_dev->istore_size * ADRENO_ISTORE_WORDS; + + if (remain < (count * 4) + sizeof(*header)) { + KGSL_DRV_ERR(device, + "snapshot: Not enough memory for the istore section"); + return 0; + } + + header->count = adreno_dev->istore_size; + + for (i = 0; i < count; i++) + kgsl_regread(device, ADRENO_ISTORE_START + i, &data[i]); + + return (count * 4) + sizeof(*header); +} + +/* Snapshot the ringbuffer memory */ +static int snapshot_rb(struct kgsl_device *device, void *snapshot, + int remain, void *priv) +{ + struct kgsl_snapshot_rb *header = snapshot; + unsigned int *data = snapshot + sizeof(*header); + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; + unsigned int rbbase, ptbase, rptr, *rbptr; + int start, stop, index; + int numitems, size; + int parse_ibs = 0, ib_parse_start; + + /* Get the GPU address of the ringbuffer */ + kgsl_regread(device, REG_CP_RB_BASE, &rbbase); + + /* Get the physical address of the MMU pagetable */ + ptbase = kgsl_mmu_get_current_ptbase(device); + + /* Get the current read pointers for the RB */ + kgsl_regread(device, REG_CP_RB_RPTR, &rptr); + + /* start the dump at the rptr minus some history */ + start = (int) rptr - NUM_DWORDS_OF_RINGBUFFER_HISTORY; + if (start < 0) + start += rb->sizedwords; + + /* + * Stop the dump at the point where the software last wrote. Don't use + * the hardware value here on the chance that it didn't get properly + * updated + */ + + stop = (int) rb->wptr + 16; + if (stop > rb->sizedwords) + stop -= rb->sizedwords; + + /* Set up the header for the section */ + + numitems = (stop > start) ? stop - start : + (rb->sizedwords - start) + stop; + + size = (numitems << 2); + + if (remain < size + sizeof(*header)) { + KGSL_DRV_ERR(device, + "snapshot: Not enough memory for the rb section"); + return 0; + } + + /* Write the sub-header for the section */ + header->start = start; + header->end = stop; + header->wptr = rb->wptr; + header->rbsize = rb->sizedwords; + header->count = numitems; + + /* + * We can only reliably dump IBs from the beginning of the context, + * and it turns out that for the vast majority of the time we really + * only care about the current context when it comes to diagnosing + * a hang. So, with an eye to limiting the buffer dumping to what is + * really useful find the beginning of the context and only dump + * IBs from that point + */ + + index = rptr; + ib_parse_start = start; + rbptr = rb->buffer_desc.hostptr; + + while (index != start) { + index--; + + if (index < 0) { + /* + * The marker we are looking for is 2 dwords long, so + * when wrapping, go back 2 from the end so we don't + * access out of range in the if statement below + */ + index = rb->sizedwords - 2; + + /* + * Account for the possibility that start might be at + * rb->sizedwords - 1 + */ + + if (start == rb->sizedwords - 1) + break; + } + + /* + * Look for a NOP packet with the context switch identifier in + * the second dword + */ + + if (rbptr[index] == cp_nop_packet(1) && + rbptr[index + 1] == KGSL_CONTEXT_TO_MEM_IDENTIFIER) { + ib_parse_start = index; + break; + } + } + + index = start; + + /* + * Loop through the RB, copying the data and looking for indirect + * buffers and MMU pagetable changes + */ + + while (index != rb->wptr) { + *data = rbptr[index]; + + /* Only parse IBs between the context start and the rptr */ + + if (index == ib_parse_start) + parse_ibs = 1; + + if (index == rptr) + parse_ibs = 0; + + if (parse_ibs && adreno_cmd_is_ib(rbptr[index])) + push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase, + rbptr[index + 1], rbptr[index + 2]); + + index = index + 1; + + if (index == rb->sizedwords) + index = 0; + + data++; + } + + /* Dump 16 dwords past the wptr, but don't bother interpeting it */ + + while (index != stop) { + *data = rbptr[index]; + index = index + 1; + + if (index == rb->sizedwords) + index = 0; + + data++; + } + + /* Return the size of the section */ + return size + sizeof(*header); +} + +/* Snapshot the memory for an indirect buffer */ +static int snapshot_ib(struct kgsl_device *device, void *snapshot, + int remain, void *priv) +{ + struct kgsl_snapshot_ib *header = snapshot; + struct kgsl_snapshot_obj *obj = priv; + unsigned int *src = obj->ptr; + unsigned int *dst = snapshot + sizeof(*header); + int i; + + if (remain < (obj->dwords << 2) + sizeof(*header)) { + KGSL_DRV_ERR(device, + "snapshot: Not enough memory for the ib section"); + return 0; + } + + /* Write the sub-header for the section */ + header->gpuaddr = obj->gpuaddr; + header->ptbase = obj->ptbase; + header->size = obj->dwords; + + /* Write the contents of the ib */ + for (i = 0; i < obj->dwords; i++) { + *dst = *src; + /* If another IB is discovered, then push it on the list too */ + + if (adreno_cmd_is_ib(*src)) + push_object(device, SNAPSHOT_OBJ_TYPE_IB, obj->ptbase, + *(src + 1), *(src + 2)); + + src++; + dst++; + } + + return (obj->dwords << 2) + sizeof(*header); +} + +/* Dump another item on the current pending list */ +static void *dump_object(struct kgsl_device *device, int obj, void *snapshot, + int *remain) +{ + switch (objbuf[obj].type) { + case SNAPSHOT_OBJ_TYPE_IB: + snapshot = kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_IB, snapshot, remain, + snapshot_ib, &objbuf[obj]); + break; + default: + KGSL_DRV_ERR(device, + "snapshot: Invalid snapshot object type: %d\n", + objbuf[obj].type); + break; + } + + return snapshot; +} + +/* adreno_snapshot - Snapshot the Adreno GPU state + * @device - KGSL device to snapshot + * @snapshot - Pointer to the start of memory to write into + * @remain - A pointer to how many bytes of memory are remaining in the snapshot + * @hang - set if this snapshot was automatically triggered by a GPU hang + * This is a hook function called by kgsl_snapshot to snapshot the + * Adreno specific information for the GPU snapshot. In turn, this function + * calls the GPU specific snapshot function to get core specific information. + */ + +void *adreno_snapshot(struct kgsl_device *device, void *snapshot, int *remain, + int hang) +{ + int i; + uint32_t ptbase, ibbase, ibsize; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + + /* Reset the list of objects */ + objbufptr = 0; + + /* Get the physical address of the MMU pagetable */ + ptbase = kgsl_mmu_get_current_ptbase(device); + + /* Dump the ringbuffer */ + snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_RB, + snapshot, remain, snapshot_rb, NULL); + + /* + * Make sure that the last IB1 that was being executed is dumped. + * Since this was the last IB1 that was processed, we should have + * already added it to the list during the ringbuffer parse but we + * want to be double plus sure. + */ + + kgsl_regread(device, REG_CP_IB1_BASE, &ibbase); + kgsl_regread(device, REG_CP_IB1_BUFSZ, &ibsize); + + /* + * The problem is that IB size from the register is the unprocessed size + * of the buffer not the original size, so if we didn't catch this + * buffer being directly used in the RB, then we might not be able to + * dump the whle thing. Print a warning message so we can try to + * figure how often this really happens. + */ + + if (!find_object(SNAPSHOT_OBJ_TYPE_IB, ibbase, ptbase) && ibsize) { + push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase, + ibbase, ibsize); + KGSL_DRV_ERR(device, "CP_IB1_BASE not found in the ringbuffer. " + "Dumping %x dwords of the buffer.\n", ibsize); + } + + kgsl_regread(device, REG_CP_IB2_BASE, &ibbase); + kgsl_regread(device, REG_CP_IB2_BUFSZ, &ibsize); + + /* + * Add the last parsed IB2 to the list. The IB2 should be found as we + * parse the objects below, but we try to add it to the list first, so + * it too can be parsed. Don't print an error message in this case - if + * the IB2 is found during parsing, the list will be updated with the + * correct size. + */ + + if (!find_object(SNAPSHOT_OBJ_TYPE_IB, ibbase, ptbase) && ibsize) { + push_object(device, SNAPSHOT_OBJ_TYPE_IB, ptbase, + ibbase, ibsize); + } + + /* + * Go through the list of found objects and dump each one. As the IBs + * are parsed, more objects might be found, and objbufptr will increase + */ + for (i = 0; i < objbufptr; i++) + snapshot = dump_object(device, i, snapshot, remain); + + /* + * Only dump the istore on a hang - reading it on a running system + * has a non 0 chance of hanging the GPU + */ + + if (hang) { + snapshot = kgsl_snapshot_add_section(device, + KGSL_SNAPSHOT_SECTION_ISTORE, snapshot, remain, + snapshot_istore, NULL); + } + + /* Add GPU specific sections - registers mainly, but other stuff too */ + if (adreno_dev->gpudev->snapshot) + snapshot = adreno_dev->gpudev->snapshot(adreno_dev, snapshot, + remain, hang); + + return snapshot; +} diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 53b9e8a99f0e4..8f67997f8f25e 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -25,7 +25,6 @@ #include #include #include -#include #include "kgsl.h" #include "kgsl_debugfs.h" @@ -33,6 +32,7 @@ #include "kgsl_log.h" #include "kgsl_sharedmem.h" #include "kgsl_device.h" +#include "kgsl_trace.h" #undef MODULE_PARAM_PREFIX #define MODULE_PARAM_PREFIX "kgsl." @@ -48,20 +48,20 @@ MODULE_PARM_DESC(ksgl_mmu_type, static struct ion_client *kgsl_ion_client; -#ifdef CONFIG_GENLOCK - /** * kgsl_add_event - Add a new timstamp event for the KGSL device * @device - KGSL device for the new event * @ts - the timestamp to trigger the event on * @cb - callback function to call when the timestamp expires * @priv - private data for the specific event type + * @owner - driver instance that owns this event * * @returns - 0 on success or error code on failure */ static int kgsl_add_event(struct kgsl_device *device, u32 ts, - void (*cb)(struct kgsl_device *, void *, u32), void *priv) + void (*cb)(struct kgsl_device *, void *, u32), void *priv, + struct kgsl_device_private *owner) { struct kgsl_event *event; struct list_head *n; @@ -85,6 +85,7 @@ static int kgsl_add_event(struct kgsl_device *device, u32 ts, event->timestamp = ts; event->priv = priv; event->func = cb; + event->owner = owner; /* Add the event in order to the list */ @@ -101,9 +102,39 @@ static int kgsl_add_event(struct kgsl_device *device, u32 ts, if (n == &device->events) list_add_tail(&event->list, &device->events); + queue_work(device->work_queue, &device->ts_expired_ws); return 0; } -#endif + +/** + * kgsl_cancel_events - Cancel all events for a process + * @device - KGSL device for the events to cancel + * @owner - driver instance that owns the events to cancel + * + */ +static void kgsl_cancel_events(struct kgsl_device *device, + struct kgsl_device_private *owner) +{ + struct kgsl_event *event, *event_tmp; + unsigned int cur = device->ftbl->readtimestamp(device, + KGSL_TIMESTAMP_RETIRED); + + list_for_each_entry_safe(event, event_tmp, &device->events, list) { + if (event->owner != owner) + continue; + /* + * "cancel" the events by calling their callback. + * Currently, events are used for lock and memory + * management, so if the process is dying the right + * thing to do is release or free. + */ + if (event->func) + event->func(device, event->priv, cur); + + list_del(&event->list); + kfree(event); + } +} static inline struct kgsl_mem_entry * kgsl_mem_entry_create(void) @@ -225,44 +256,10 @@ kgsl_destroy_context(struct kgsl_device_private *dev_priv, idr_remove(&dev_priv->device->context_idr, id); } -/* to be called when a process is destroyed, this walks the memqueue and - * frees any entryies that belong to the dying process - */ -static void kgsl_memqueue_cleanup(struct kgsl_device *device, - struct kgsl_process_private *private) -{ - struct kgsl_mem_entry *entry, *entry_tmp; - - if (!private) - return; - - BUG_ON(!mutex_is_locked(&device->mutex)); - - list_for_each_entry_safe(entry, entry_tmp, &device->memqueue, list) { - if (entry->priv == private) { - list_del(&entry->list); - kgsl_mem_entry_put(entry); - } - } -} - -static void kgsl_memqueue_freememontimestamp(struct kgsl_device *device, - struct kgsl_mem_entry *entry, - uint32_t timestamp, - enum kgsl_timestamp_type type) -{ - BUG_ON(!mutex_is_locked(&device->mutex)); - - entry->free_timestamp = timestamp; - - list_add_tail(&entry->list, &device->memqueue); -} - static void kgsl_timestamp_expired(struct work_struct *work) { struct kgsl_device *device = container_of(work, struct kgsl_device, ts_expired_ws); - struct kgsl_mem_entry *entry, *entry_tmp; struct kgsl_event *event, *event_tmp; uint32_t ts_processed; @@ -272,15 +269,6 @@ static void kgsl_timestamp_expired(struct work_struct *work) ts_processed = device->ftbl->readtimestamp(device, KGSL_TIMESTAMP_RETIRED); - /* Flush the freememontimestamp queue */ - list_for_each_entry_safe(entry, entry_tmp, &device->memqueue, list) { - if (timestamp_cmp(ts_processed, entry->free_timestamp) < 0) - break; - - list_del(&entry->list); - kgsl_mem_entry_put(entry); - } - /* Process expired events */ list_for_each_entry_safe(event, event_tmp, &device->events, list) { if (timestamp_cmp(ts_processed, event->timestamp) < 0) @@ -301,7 +289,7 @@ static void kgsl_check_idle_locked(struct kgsl_device *device) if (device->pwrctrl.nap_allowed == true && device->state == KGSL_STATE_ACTIVE && device->requested_state == KGSL_STATE_NONE) { - device->requested_state = KGSL_STATE_NAP; + kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP); if (kgsl_pwrctrl_sleep(device) != 0) mod_timer(&device->idle_timer, jiffies + @@ -394,7 +382,7 @@ static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state) device->pwrctrl.nap_allowed = false; policy_saved = device->pwrscale.policy; device->pwrscale.policy = NULL; - device->requested_state = KGSL_STATE_SUSPEND; + kgsl_pwrctrl_request_state(device, KGSL_STATE_SUSPEND); /* Make sure no user process is waiting for a timestamp * * before supending */ if (device->active_cnt != 0) { @@ -416,22 +404,22 @@ static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state) INIT_COMPLETION(device->hwaccess_gate); device->ftbl->suspend_context(device); device->ftbl->stop(device); - device->state = KGSL_STATE_SUSPEND; - KGSL_PWR_WARN(device, "state -> SUSPEND, device %d\n", - device->id); + if (device->idle_wakelock.name) + wake_unlock(&device->idle_wakelock); + pm_qos_update_request(&device->pm_qos_req_dma, + PM_QOS_DEFAULT_VALUE); + kgsl_pwrctrl_set_state(device, KGSL_STATE_SUSPEND); break; case KGSL_STATE_SLUMBER: INIT_COMPLETION(device->hwaccess_gate); - device->state = KGSL_STATE_SUSPEND; - KGSL_PWR_WARN(device, "state -> SUSPEND, device %d\n", - device->id); + kgsl_pwrctrl_set_state(device, KGSL_STATE_SUSPEND); break; default: KGSL_PWR_ERR(device, "suspend fail, device %d\n", device->id); goto end; } - device->requested_state = KGSL_STATE_NONE; + kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); device->pwrctrl.nap_allowed = nap_allowed_saved; device->pwrscale.policy = policy_saved; status = 0; @@ -452,14 +440,11 @@ static int kgsl_resume_device(struct kgsl_device *device) KGSL_PWR_WARN(device, "resume start\n"); mutex_lock(&device->mutex); if (device->state == KGSL_STATE_SUSPEND) { - device->state = KGSL_STATE_SLUMBER; + kgsl_pwrctrl_set_state(device, KGSL_STATE_SLUMBER); status = 0; - KGSL_PWR_WARN(device, - "state -> SLUMBER, device %d\n", - device->id); complete_all(&device->hwaccess_gate); } - device->requested_state = KGSL_STATE_NONE; + kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); mutex_unlock(&device->mutex); KGSL_PWR_WARN(device, "resume end\n"); @@ -504,7 +489,7 @@ void kgsl_early_suspend_driver(struct early_suspend *h) struct kgsl_device, display_off); KGSL_PWR_WARN(device, "early suspend start\n"); mutex_lock(&device->mutex); - device->requested_state = KGSL_STATE_SLUMBER; + kgsl_pwrctrl_request_state(device, KGSL_STATE_SLUMBER); kgsl_pwrctrl_sleep(device); mutex_unlock(&device->mutex); KGSL_PWR_WARN(device, "early suspend end\n"); @@ -532,8 +517,9 @@ void kgsl_late_resume_driver(struct early_suspend *h) struct kgsl_device, display_off); KGSL_PWR_WARN(device, "late resume start\n"); mutex_lock(&device->mutex); - kgsl_pwrctrl_wake(device); device->pwrctrl.restore_slumber = 0; + kgsl_pwrctrl_wake(device); + kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO); mutex_unlock(&device->mutex); kgsl_check_idle(device); KGSL_PWR_WARN(device, "late resume end\n"); @@ -650,13 +636,12 @@ static int kgsl_release(struct inode *inodep, struct file *filep) device->open_count--; if (device->open_count == 0) { result = device->ftbl->stop(device); - device->state = KGSL_STATE_INIT; - KGSL_PWR_WARN(device, "state -> INIT, device %d\n", device->id); + kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT); } /* clean up any to-be-freed entries that belong to this * process and this device */ - kgsl_memqueue_cleanup(device, private); + kgsl_cancel_events(device, dev_priv); mutex_unlock(&device->mutex); kfree(dev_priv); @@ -719,9 +704,7 @@ static int kgsl_open(struct inode *inodep, struct file *filep) mutex_unlock(&device->mutex); goto err_putprocess; } - device->state = KGSL_STATE_ACTIVE; - KGSL_PWR_WARN(device, - "state -> ACTIVE, device %d\n", minor); + kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE); } device->open_count++; mutex_unlock(&device->mutex); @@ -773,9 +756,7 @@ kgsl_sharedmem_find_region(struct kgsl_process_private *private, BUG_ON(private == NULL); list_for_each_entry(entry, &private->mem_list, list) { - if (gpuaddr >= entry->memdesc.gpuaddr && - ((gpuaddr + size) <= - (entry->memdesc.gpuaddr + entry->memdesc.size))) { + if (kgsl_gpuaddr_in_memdesc(&entry->memdesc, gpuaddr, size)) { result = entry; break; } @@ -785,20 +766,6 @@ kgsl_sharedmem_find_region(struct kgsl_process_private *private, } EXPORT_SYMBOL(kgsl_sharedmem_find_region); -uint8_t *kgsl_gpuaddr_to_vaddr(const struct kgsl_memdesc *memdesc, - unsigned int gpuaddr, unsigned int *size) -{ - BUG_ON(memdesc->hostptr == NULL); - - if (memdesc->gpuaddr == 0 || (gpuaddr < memdesc->gpuaddr || - gpuaddr >= memdesc->gpuaddr + memdesc->size)) - return NULL; - - *size = memdesc->size - (gpuaddr - memdesc->gpuaddr); - return memdesc->hostptr + (gpuaddr - memdesc->gpuaddr); -} -EXPORT_SYMBOL(kgsl_gpuaddr_to_vaddr); - /*call all ioctl sub functions with driver locked*/ static long kgsl_ioctl_device_getproperty(struct kgsl_device_private *dev_priv, unsigned int cmd, void *data) @@ -825,6 +792,40 @@ static long kgsl_ioctl_device_getproperty(struct kgsl_device_private *dev_priv, break; } + case KGSL_PROP_GPU_RESET_STAT: + { + /* Return reset status of given context and clear it */ + uint32_t id; + struct kgsl_context *context; + + if (param->sizebytes != sizeof(unsigned int)) { + result = -EINVAL; + break; + } + /* We expect the value passed in to contain the context id */ + if (copy_from_user(&id, param->value, + sizeof(unsigned int))) { + result = -EFAULT; + break; + } + context = kgsl_find_context(dev_priv, id); + if (!context) { + result = -EINVAL; + break; + } + /* + * Copy the reset status to value which also serves as + * the out parameter + */ + if (copy_to_user(param->value, &(context->reset_status), + sizeof(unsigned int))) { + result = -EFAULT; + break; + } + /* Clear reset status once its been queried */ + context->reset_status = KGSL_CTX_STAT_NO_ERROR; + break; + } default: result = dev_priv->device->ftbl->getproperty( dev_priv->device, param->type, @@ -847,10 +848,14 @@ static long kgsl_ioctl_device_waittimestamp(struct kgsl_device_private dev_priv->device->active_cnt++; + trace_kgsl_waittimestamp_entry(dev_priv->device, param); + result = dev_priv->device->ftbl->waittimestamp(dev_priv->device, param->timestamp, param->timeout); + trace_kgsl_waittimestamp_exit(dev_priv->device, result); + /* Fire off any pending suspend operations that are in flight */ INIT_COMPLETION(dev_priv->device->suspend_gate); @@ -975,6 +980,8 @@ static long kgsl_ioctl_rb_issueibcmds(struct kgsl_device_private *dev_priv, ¶m->timestamp, param->flags); + trace_kgsl_issueibcmds(dev_priv->device, param, result); + if (result != 0) goto free_ibdesc; @@ -1008,9 +1015,21 @@ static long kgsl_ioctl_cmdstream_readtimestamp(struct kgsl_device_private dev_priv->device->ftbl->readtimestamp(dev_priv->device, param->type); + trace_kgsl_readtimestamp(dev_priv->device, param); + return 0; } +static void kgsl_freemem_event_cb(struct kgsl_device *device, + void *priv, u32 timestamp) +{ + struct kgsl_mem_entry *entry = priv; + spin_lock(&entry->priv->mem_lock); + list_del(&entry->list); + spin_unlock(&entry->priv->mem_lock); + kgsl_mem_entry_put(entry); +} + static long kgsl_ioctl_cmdstream_freememontimestamp(struct kgsl_device_private *dev_priv, unsigned int cmd, void *data) @@ -1021,13 +1040,11 @@ static long kgsl_ioctl_cmdstream_freememontimestamp(struct kgsl_device_private spin_lock(&dev_priv->process_priv->mem_lock); entry = kgsl_sharedmem_find(dev_priv->process_priv, param->gpuaddr); - if (entry) - list_del(&entry->list); spin_unlock(&dev_priv->process_priv->mem_lock); if (entry) { - kgsl_memqueue_freememontimestamp(dev_priv->device, entry, - param->timestamp, param->type); + result = kgsl_add_event(dev_priv->device, param->timestamp, + kgsl_freemem_event_cb, entry, dev_priv); } else { KGSL_DRV_ERR(dev_priv->device, "invalid gpuaddr %08x\n", param->gpuaddr); @@ -1192,9 +1209,9 @@ kgsl_ioctl_sharedmem_from_vmalloc(struct kgsl_device_private *dev_priv, vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); - result = remap_vmalloc_range(vma, (void *) entry->memdesc.hostptr, 0); + result = kgsl_sharedmem_map_vma(vma, &entry->memdesc); if (result) { - KGSL_CORE_ERR("remap_vmalloc_range failed: %d\n", result); + KGSL_CORE_ERR("kgsl_sharedmem_map_vma failed: %d\n", result); goto error_free_vmalloc; } @@ -1333,8 +1350,7 @@ static int memdesc_sg_virt(struct kgsl_memdesc *memdesc, int sglen = PAGE_ALIGN(size) / PAGE_SIZE; unsigned long paddr = (unsigned long) addr; - memdesc->sg = kmalloc(sglen * sizeof(struct scatterlist), - GFP_KERNEL); + memdesc->sg = vmalloc(sglen * sizeof(struct scatterlist)); if (memdesc->sg == NULL) return -ENOMEM; @@ -1374,7 +1390,7 @@ static int memdesc_sg_virt(struct kgsl_memdesc *memdesc, err: spin_unlock(¤t->mm->page_table_lock); - kfree(memdesc->sg); + vfree(memdesc->sg); memdesc->sg = NULL; return -EINVAL; @@ -1509,11 +1525,8 @@ static int kgsl_setup_ion(struct kgsl_mem_entry *entry, struct scatterlist *s; unsigned long flags; - if (kgsl_ion_client == NULL) { - kgsl_ion_client = msm_ion_client_create(UINT_MAX, KGSL_NAME); - if (kgsl_ion_client == NULL) - return -ENODEV; - } + if (IS_ERR_OR_NULL(kgsl_ion_client)) + return -ENODEV; handle = ion_import_fd(kgsl_ion_client, fd); if (IS_ERR_OR_NULL(handle)) @@ -1643,10 +1656,20 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv, kgsl_check_idle(dev_priv->device); return result; - error_put_file_ptr: - if (entry->priv_data) - fput(entry->priv_data); - +error_put_file_ptr: + switch (entry->memtype) { + case KGSL_MEM_ENTRY_PMEM: + case KGSL_MEM_ENTRY_ASHMEM: + if (entry->priv_data) + fput(entry->priv_data); + break; + case KGSL_MEM_ENTRY_ION: + ion_unmap_dma(kgsl_ion_client, entry->priv_data); + ion_free(kgsl_ion_client, entry->priv_data); + break; + default: + break; + } error: kfree(entry); kgsl_check_idle(dev_priv->device); @@ -1671,11 +1694,6 @@ kgsl_ioctl_sharedmem_flush_cache(struct kgsl_device_private *dev_priv, result = -EINVAL; goto done; } - if (!entry->memdesc.hostptr) - entry->memdesc.hostptr = - kgsl_gpuaddr_to_vaddr(&entry->memdesc, - param->gpuaddr, &entry->memdesc.size); - if (!entry->memdesc.hostptr) { KGSL_CORE_ERR("invalid hostptr with gpuaddr %08x\n", param->gpuaddr); @@ -1783,6 +1801,7 @@ static void kgsl_genlock_event_cb(struct kgsl_device *device, * @timestamp - Timestamp to trigger the event * @data - User space buffer containing struct kgsl_genlock_event_priv * @len - length of the userspace buffer + * @owner - driver instance that owns this event * @returns 0 on success or error code on error * * Attack to a genlock handle and register an event to release the @@ -1790,7 +1809,8 @@ static void kgsl_genlock_event_cb(struct kgsl_device *device, */ static int kgsl_add_genlock_event(struct kgsl_device *device, - u32 timestamp, void __user *data, int len) + u32 timestamp, void __user *data, int len, + struct kgsl_device_private *owner) { struct kgsl_genlock_event_priv *event; struct kgsl_timestamp_event_genlock priv; @@ -1815,7 +1835,8 @@ static int kgsl_add_genlock_event(struct kgsl_device *device, return ret; } - ret = kgsl_add_event(device, timestamp, kgsl_genlock_event_cb, event); + ret = kgsl_add_event(device, timestamp, kgsl_genlock_event_cb, event, + owner); if (ret) kfree(event); @@ -1823,7 +1844,8 @@ static int kgsl_add_genlock_event(struct kgsl_device *device, } #else static long kgsl_add_genlock_event(struct kgsl_device *device, - u32 timestamp, void __user *data, int len) + u32 timestamp, void __user *data, int len, + struct kgsl_device_private *owner) { return -EINVAL; } @@ -1846,7 +1868,8 @@ static long kgsl_ioctl_timestamp_event(struct kgsl_device_private *dev_priv, switch (param->type) { case KGSL_TIMESTAMP_EVENT_GENLOCK: ret = kgsl_add_genlock_event(dev_priv->device, - param->timestamp, param->priv, param->len); + param->timestamp, param->priv, param->len, + dev_priv); break; default: ret = -EINVAL; @@ -1897,7 +1920,7 @@ static const struct { KGSL_IOCTL_FUNC(IOCTL_KGSL_CFF_USER_EVENT, kgsl_ioctl_cff_user_event, 0), KGSL_IOCTL_FUNC(IOCTL_KGSL_TIMESTAMP_EVENT, - kgsl_ioctl_timestamp_event, 0), + kgsl_ioctl_timestamp_event, 1), }; static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) @@ -2118,11 +2141,13 @@ void kgsl_unregister_device(struct kgsl_device *device) if (minor == KGSL_DEVICE_MAX) return; + kgsl_device_snapshot_close(device); + kgsl_cffdump_close(device->id); kgsl_pwrctrl_uninit_sysfs(device); - if (cpu_is_msm8x60()) - wake_lock_destroy(&device->idle_wakelock); + wake_lock_destroy(&device->idle_wakelock); + pm_qos_remove_request(&device->pm_qos_req_dma); idr_destroy(&device->context_idr); @@ -2201,7 +2226,6 @@ kgsl_register_device(struct kgsl_device *device) INIT_WORK(&device->idle_check_ws, kgsl_idle_check); INIT_WORK(&device->ts_expired_ws, kgsl_timestamp_expired); - INIT_LIST_HEAD(&device->memqueue); INIT_LIST_HEAD(&device->events); ret = kgsl_mmu_init(device); @@ -2214,12 +2238,15 @@ kgsl_register_device(struct kgsl_device *device) if (ret != 0) goto err_close_mmu; - if (cpu_is_msm8x60()) - wake_lock_init(&device->idle_wakelock, - WAKE_LOCK_IDLE, device->name); + wake_lock_init(&device->idle_wakelock, WAKE_LOCK_IDLE, device->name); + pm_qos_add_request(&device->pm_qos_req_dma, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); idr_init(&device->context_idr); + /* Initalize the snapshot engine */ + kgsl_device_snapshot_init(device); + /* sysfs and debugfs initalization - failure here is non fatal */ /* Initialize logging */ @@ -2259,6 +2286,8 @@ int kgsl_device_platform_probe(struct kgsl_device *device, if (status) goto error; + kgsl_ion_client = msm_ion_client_create(UINT_MAX, KGSL_NAME); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, device->iomemname); if (res == NULL) { @@ -2447,8 +2476,6 @@ static int __init kgsl_core_init(void) if (result) goto err; - kgsl_mmu_set_mmutype(ksgl_mmu_type); - return 0; err: diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 3bb722da45816..c3635b8cacf27 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -21,9 +21,13 @@ #include #include #include +#include #define KGSL_NAME "kgsl" +/* Timestamp window used to detect rollovers (half of integer range) */ +#define KGSL_TIMESTAMP_WINDOW 0x80000000 + /*cache coherency ops */ #define DRM_KGSL_GEM_CACHE_OP_TO_DEV 0x0001 #define DRM_KGSL_GEM_CACHE_OP_FROM_DEV 0x0002 @@ -102,7 +106,15 @@ struct kgsl_driver { extern struct kgsl_driver kgsl_driver; struct kgsl_pagetable; -struct kgsl_memdesc_ops; +struct kgsl_memdesc; + +struct kgsl_memdesc_ops { + int (*vmflags)(struct kgsl_memdesc *); + int (*vmfault)(struct kgsl_memdesc *, struct vm_area_struct *, + struct vm_fault *); + void (*free)(struct kgsl_memdesc *memdesc); + int (*map_kernel_mem)(struct kgsl_memdesc *); +}; /* shared memory allocation */ struct kgsl_memdesc { @@ -145,8 +157,6 @@ struct kgsl_mem_entry { #endif void kgsl_mem_entry_destroy(struct kref *kref); -uint8_t *kgsl_gpuaddr_to_vaddr(const struct kgsl_memdesc *memdesc, - unsigned int gpuaddr, unsigned int *size); struct kgsl_mem_entry *kgsl_sharedmem_find_region( struct kgsl_process_private *private, unsigned int gpuaddr, size_t size); @@ -175,23 +185,45 @@ static inline void kgsl_drm_exit(void) #endif static inline int kgsl_gpuaddr_in_memdesc(const struct kgsl_memdesc *memdesc, - unsigned int gpuaddr) + unsigned int gpuaddr, unsigned int size) { - if (gpuaddr >= memdesc->gpuaddr && (gpuaddr + sizeof(unsigned int)) <= - (memdesc->gpuaddr + memdesc->size)) { + if (gpuaddr >= memdesc->gpuaddr && + ((gpuaddr + size) <= (memdesc->gpuaddr + memdesc->size))) { return 1; } return 0; } - -static inline int timestamp_cmp(unsigned int new, unsigned int old) +static inline uint8_t *kgsl_gpuaddr_to_vaddr(struct kgsl_memdesc *memdesc, + unsigned int gpuaddr) { - int ts_diff = new - old; + if (memdesc->gpuaddr == 0 || + gpuaddr < memdesc->gpuaddr || + gpuaddr >= (memdesc->gpuaddr + memdesc->size) || + (NULL == memdesc->hostptr && memdesc->ops->map_kernel_mem && + memdesc->ops->map_kernel_mem(memdesc))) + return NULL; + + return memdesc->hostptr + (gpuaddr - memdesc->gpuaddr); +} - if (ts_diff == 0) +static inline int timestamp_cmp(unsigned int a, unsigned int b) +{ + /* check for equal */ + if (a == b) return 0; - return ((ts_diff > 0) || (ts_diff < -20000)) ? 1 : -1; + /* check for greater-than for non-rollover case */ + if ((a > b) && (a - b < KGSL_TIMESTAMP_WINDOW)) + return 1; + + /* check for greater-than for rollover case + * note that <= is required to ensure that consistent + * results are returned for values whose difference is + * equal to the window size + */ + a += KGSL_TIMESTAMP_WINDOW; + b += KGSL_TIMESTAMP_WINDOW; + return ((a > b) && (a - b <= KGSL_TIMESTAMP_WINDOW)) ? 1 : -1; } static inline void diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c index aa33152cce445..e9455cb827945 100644 --- a/drivers/gpu/msm/kgsl_cffdump.c +++ b/drivers/gpu/msm/kgsl_cffdump.c @@ -231,8 +231,6 @@ static void cffdump_printline(int id, uint opcode, uint op1, uint op2, spin_lock(&cffdump_lock); if (opcode == CFF_OP_WRITE_MEM) { - if (op1 < 0x40000000 || op1 >= 0x60000000) - KGSL_CORE_ERR("addr out-of-range: op1=%08x", op1); if ((cff_op_write_membuf.addr != op1 && cff_op_write_membuf.count) || (cff_op_write_membuf.count == MEMBUF_SIZE)) @@ -360,15 +358,7 @@ void kgsl_cffdump_destroy() void kgsl_cffdump_open(enum kgsl_deviceid device_id) { - /*TODO: move this to where we can report correct gmemsize*/ - unsigned int va_base; - - if (cpu_is_msm8x60() || cpu_is_msm8960() || cpu_is_msm8930()) - va_base = 0x40000000; - else - va_base = 0x20000000; - - kgsl_cffdump_memory_base(device_id, va_base, + kgsl_cffdump_memory_base(device_id, KGSL_PAGETABLE_BASE, CONFIG_MSM_KGSL_PAGE_TABLE_SIZE, SZ_256K); } @@ -401,8 +391,6 @@ void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv, bool clean_cache) { const void *src; - uint host_size; - uint physaddr; if (!kgsl_cff_dump_enable) return; @@ -422,13 +410,9 @@ void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv, } memdesc = &entry->memdesc; } - BUG_ON(memdesc->gpuaddr == 0); - BUG_ON(gpuaddr == 0); - physaddr = kgsl_get_realaddr(memdesc) + (gpuaddr - memdesc->gpuaddr); - - src = kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr, &host_size); - if (src == NULL || host_size < sizebytes) { - KGSL_CORE_ERR("did not find mapping for " + src = (uint *)kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr); + if (memdesc->hostptr == NULL) { + KGSL_CORE_ERR("no kernel mapping for " "gpuaddr: 0x%08x, m->host: 0x%p, phys: 0x%08x\n", gpuaddr, memdesc->hostptr, memdesc->physaddr); return; @@ -444,7 +428,6 @@ void kgsl_cffdump_syncmem(struct kgsl_device_private *dev_priv, KGSL_CACHE_OP_INV); } - BUG_ON(physaddr > 0x66000000 && physaddr < 0x66ffffff); while (sizebytes > 3) { cffdump_printline(-1, CFF_OP_WRITE_MEM, gpuaddr, *(uint *)src, 0, 0, 0); @@ -462,7 +445,6 @@ void kgsl_cffdump_setmem(uint addr, uint value, uint sizebytes) if (!kgsl_cff_dump_enable) return; - BUG_ON(addr > 0x66000000 && addr < 0x66ffffff); while (sizebytes > 3) { /* Use 32bit memory writes as long as there's at least * 4 bytes left */ @@ -575,7 +557,6 @@ bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv, { static uint level; /* recursion level */ bool ret = true; - uint host_size; uint *hostaddr, *hoststart; int dwords_left = sizedwords; /* dwords left in the current command buffer */ @@ -596,10 +577,9 @@ bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv, } memdesc = &entry->memdesc; } - - hostaddr = (uint *)kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr, &host_size); + hostaddr = (uint *)kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr); if (hostaddr == NULL) { - KGSL_CORE_ERR("did not find mapping for " + KGSL_CORE_ERR("no kernel mapping for " "gpuaddr: 0x%08x\n", gpuaddr); return true; } @@ -608,14 +588,9 @@ bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv, level++; - if (!memdesc->physaddr) { - KGSL_CORE_ERR("no physaddr"); - } else { - mb(); - kgsl_cache_range_op((struct kgsl_memdesc *)memdesc, + mb(); + kgsl_cache_range_op((struct kgsl_memdesc *)memdesc, KGSL_CACHE_OP_INV); - } - #ifdef DEBUG pr_info("kgsl: cffdump: ib: gpuaddr:0x%08x, wc:%d, hptr:%p\n", gpuaddr, sizedwords, hostaddr); diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index c88f16ef6b759..d7a25a121fcb4 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -15,6 +15,7 @@ #include #include +#include #include #include "kgsl.h" @@ -89,6 +90,9 @@ struct kgsl_functable { void (*power_stats)(struct kgsl_device *device, struct kgsl_power_stats *stats); void (*irqctrl)(struct kgsl_device *device, int state); + unsigned int (*gpuid)(struct kgsl_device *device); + void * (*snapshot)(struct kgsl_device *device, void *snapshot, + int *remain, int hang); /* Optional functions - these functions are not mandatory. The driver will check that the function pointer is not NULL before calling the hook */ @@ -123,6 +127,7 @@ struct kgsl_event { void (*func)(struct kgsl_device *, void *, u32); void *priv; struct list_head list; + struct kgsl_device_private *owner; }; @@ -151,7 +156,6 @@ struct kgsl_device { uint32_t state; uint32_t requested_state; - struct list_head memqueue; unsigned int active_cnt; struct completion suspend_gate; @@ -163,6 +167,15 @@ struct kgsl_device { struct idr context_idr; struct early_suspend display_off; + void *snapshot; /* Pointer to the snapshot memory region */ + int snapshot_maxsize; /* Max size of the snapshot region */ + int snapshot_size; /* Current size of the snapshot region */ + u32 snapshot_timestamp; /* Timestamp of the last valid snapshot */ + int snapshot_frozen; /* 1 if the snapshot output is frozen until + it gets read by the user. This avoids + losing the output on multiple hangs */ + struct kobject snapshot_kobj; + /* Logging levels */ int cmd_log; int ctxt_log; @@ -172,8 +185,10 @@ struct kgsl_device { struct wake_lock idle_wakelock; struct kgsl_pwrscale pwrscale; struct kobject pwrscale_kobj; + struct pm_qos_request_list pm_qos_req_dma; struct work_struct ts_expired_ws; struct list_head events; + s64 on_time; }; struct kgsl_context { @@ -184,6 +199,11 @@ struct kgsl_context { /* Pointer to the device specific context information */ void *devctxt; + /* + * Status indicating whether a gpu reset occurred and whether this + * context was responsible for causing it + */ + unsigned int reset_status; }; struct kgsl_process_private { @@ -240,6 +260,11 @@ static inline int kgsl_idle(struct kgsl_device *device, unsigned int timeout) return device->ftbl->idle(device, timeout); } +static inline unsigned int kgsl_gpuid(struct kgsl_device *device) +{ + return device->ftbl->gpuid(device); +} + static inline int kgsl_create_device_sysfs_files(struct device *root, const struct device_attribute **list) { @@ -277,10 +302,11 @@ static inline struct kgsl_device *kgsl_device_from_dev(struct device *dev) static inline int kgsl_create_device_workqueue(struct kgsl_device *device) { - device->work_queue = create_workqueue(device->name); + device->work_queue = create_singlethread_workqueue(device->name); if (!device->work_queue) { - KGSL_DRV_ERR(device, "create_workqueue(%s) failed\n", - device->name); + KGSL_DRV_ERR(device, + "create_singlethread_workqueue(%s) failed\n", + device->name); return -EINVAL; } return 0; @@ -310,4 +336,10 @@ int kgsl_device_platform_probe(struct kgsl_device *device, irqreturn_t (*dev_isr) (int, void*)); void kgsl_device_platform_remove(struct kgsl_device *device); +const char *kgsl_pwrstate_to_str(unsigned int state); + +int kgsl_device_snapshot_init(struct kgsl_device *device); +int kgsl_device_snapshot(struct kgsl_device *device, int hang); +void kgsl_device_snapshot_close(struct kgsl_device *device); + #endif /* __KGSL_DEVICE_H */ diff --git a/drivers/gpu/msm/kgsl_drm.c b/drivers/gpu/msm/kgsl_drm.c index dba2dfcfb4a59..051fcfce86eea 100644 --- a/drivers/gpu/msm/kgsl_drm.c +++ b/drivers/gpu/msm/kgsl_drm.c @@ -1042,17 +1042,18 @@ int kgsl_gem_kmem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) struct drm_gem_object *obj = vma->vm_private_data; struct drm_device *dev = obj->dev; struct drm_kgsl_gem_object *priv; - unsigned long offset, pg; + unsigned long offset; struct page *page; + int i; mutex_lock(&dev->struct_mutex); priv = obj->driver_private; offset = (unsigned long) vmf->virtual_address - vma->vm_start; - pg = (unsigned long) priv->memdesc.hostptr + offset; + i = offset >> PAGE_SHIFT; + page = sg_page(&(priv->memdesc.sg[i])); - page = vmalloc_to_page((void *) pg); if (!page) { mutex_unlock(&dev->struct_mutex); return VM_FAULT_SIGBUS; diff --git a/drivers/gpu/msm/kgsl_gpummu.c b/drivers/gpu/msm/kgsl_gpummu.c index fe5677e556fc1..ba8a719e42dd1 100644 --- a/drivers/gpu/msm/kgsl_gpummu.c +++ b/drivers/gpu/msm/kgsl_gpummu.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -354,8 +354,8 @@ void *kgsl_gpummu_ptpool_init(int ptsize, int entries) int kgsl_gpummu_pt_equal(struct kgsl_pagetable *pt, unsigned int pt_base) { - struct kgsl_gpummu_pt *gpummu_pt = pt->priv; - return pt && pt_base && (gpummu_pt->base.gpuaddr == pt_base); + struct kgsl_gpummu_pt *gpummu_pt = pt ? pt->priv : NULL; + return gpummu_pt && pt_base && (gpummu_pt->base.gpuaddr == pt_base); } void kgsl_gpummu_destroy_pagetable(void *mmu_specific_pt) @@ -382,26 +382,27 @@ static inline void kgsl_pt_map_set(struct kgsl_gpummu_pt *pt, uint32_t pte, uint32_t val) { uint32_t *baseptr = (uint32_t *)pt->base.hostptr; - - writel_relaxed(val, &baseptr[pte]); + BUG_ON(pte*sizeof(uint32_t) >= pt->base.size); + baseptr[pte] = val; } static inline uint32_t kgsl_pt_map_get(struct kgsl_gpummu_pt *pt, uint32_t pte) { uint32_t *baseptr = (uint32_t *)pt->base.hostptr; - return readl_relaxed(&baseptr[pte]) & GSL_PT_PAGE_ADDR_MASK; + BUG_ON(pte*sizeof(uint32_t) >= pt->base.size); + return baseptr[pte] & GSL_PT_PAGE_ADDR_MASK; } static unsigned int kgsl_gpummu_pt_get_flags(struct kgsl_pagetable *pt, enum kgsl_deviceid id) { unsigned int result = 0; - struct kgsl_gpummu_pt *gpummu_pt = (struct kgsl_gpummu_pt *) - pt->priv; + struct kgsl_gpummu_pt *gpummu_pt; if (pt == NULL) return 0; + gpummu_pt = pt->priv; spin_lock(&pt->lock); if (gpummu_pt->tlb_flags && (1<sg, s, memdesc->sglen, i) { - unsigned int paddr = sg_phys(s); + unsigned int paddr = kgsl_get_sg_pa(s); unsigned int j; /* Each sg entry might be multiple pages long */ diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index e4e561cef50c4..5646d682a0d78 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -34,8 +34,8 @@ struct kgsl_iommu { static int kgsl_iommu_pt_equal(struct kgsl_pagetable *pt, unsigned int pt_base) { - struct iommu_domain *domain = pt->priv; - return pt && pt_base && ((unsigned int)domain == pt_base); + struct iommu_domain *domain = pt ? pt->priv : NULL; + return domain && pt_base && ((unsigned int)domain == pt_base); } static void kgsl_iommu_destroy_pagetable(void *mmu_specific_pt) @@ -262,7 +262,7 @@ kgsl_iommu_map(void *mmu_specific_pt, iommu_virt_addr = memdesc->gpuaddr; ret = iommu_map_range(domain, iommu_virt_addr, memdesc->sg, - memdesc->size, 0); + memdesc->size, (IOMMU_READ | IOMMU_WRITE)); if (ret) { KGSL_CORE_ERR("iommu_map_range(%p, %x, %p, %d, %d) " "failed with err: %d\n", domain, diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c index 715b9d63d8dce..36248ef2f757a 100644 --- a/drivers/gpu/msm/kgsl_mmu.c +++ b/drivers/gpu/msm/kgsl_mmu.c @@ -22,6 +22,7 @@ #include "kgsl_mmu.h" #include "kgsl_device.h" #include "kgsl_sharedmem.h" +#include "adreno_postmortem.h" #define KGSL_MMU_ALIGN_SHIFT 13 #define KGSL_MMU_ALIGN_MASK (~((1 << KGSL_MMU_ALIGN_SHIFT) - 1)) diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 7034fd884d86d..08b8c8cd62530 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -13,20 +13,49 @@ #include #include #include -#include #include "kgsl.h" #include "kgsl_pwrscale.h" #include "kgsl_device.h" +#include "kgsl_trace.h" #define KGSL_PWRFLAGS_POWER_ON 0 #define KGSL_PWRFLAGS_CLK_ON 1 #define KGSL_PWRFLAGS_AXI_ON 2 #define KGSL_PWRFLAGS_IRQ_ON 3 +#define GPU_SWFI_LATENCY 3 #define UPDATE_BUSY_VAL 1000000 #define UPDATE_BUSY 50 +struct clk_pair { + const char *name; + uint map; +}; + +struct clk_pair clks[KGSL_MAX_CLKS] = { + { + .name = "src_clk", + .map = KGSL_CLK_SRC, + }, + { + .name = "core_clk", + .map = KGSL_CLK_CORE, + }, + { + .name = "iface_clk", + .map = KGSL_CLK_IFACE, + }, + { + .name = "mem_clk", + .map = KGSL_CLK_MEM, + }, + { + .name = "mem_iface_clk", + .map = KGSL_CLK_MEM_IFACE, + }, +}; + void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, unsigned int new_level) { @@ -34,24 +63,29 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, if (new_level < (pwr->num_pwrlevels - 1) && new_level >= pwr->thermal_pwrlevel && new_level != pwr->active_pwrlevel) { + struct kgsl_pwrlevel *pwrlevel = &pwr->pwrlevels[new_level]; pwr->active_pwrlevel = new_level; if ((test_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->power_flags)) || - (device->state == KGSL_STATE_NAP)) - clk_set_rate(pwr->grp_clks[0], - pwr->pwrlevels[pwr->active_pwrlevel]. - gpu_freq); + (device->state == KGSL_STATE_NAP)) { + /* + * On some platforms, instability is caused on + * changing clock freq when the core is busy. + * Idle the gpu core before changing the clock freq. + */ + if (pwr->idle_needed == true) + device->ftbl->idle(device, + KGSL_TIMEOUT_DEFAULT); + clk_set_rate(pwr->grp_clks[0], pwrlevel->gpu_freq); + } if (test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)) { if (pwr->pcl) msm_bus_scale_client_update_request(pwr->pcl, - pwr->pwrlevels[pwr->active_pwrlevel]. - bus_freq); + pwrlevel->bus_freq); else if (pwr->ebi1_clk) - clk_set_rate(pwr->ebi1_clk, - pwr->pwrlevels[pwr->active_pwrlevel]. - bus_freq); + clk_set_rate(pwr->ebi1_clk, pwrlevel->bus_freq); } - KGSL_PWR_WARN(device, "kgsl pwr level changed to %d\n", - pwr->active_pwrlevel); + trace_kgsl_pwrlevel(device, pwr->active_pwrlevel, + pwrlevel->gpu_freq); } } EXPORT_SYMBOL(kgsl_pwrctrl_pwrlevel_change); @@ -250,7 +284,7 @@ static int kgsl_pwrctrl_gpubusy_show(struct device *dev, DEVICE_ATTR(gpuclk, 0644, kgsl_pwrctrl_gpuclk_show, kgsl_pwrctrl_gpuclk_store); DEVICE_ATTR(max_gpuclk, 0644, kgsl_pwrctrl_max_gpuclk_show, kgsl_pwrctrl_max_gpuclk_store); -DEVICE_ATTR(pwrnap, 0644, kgsl_pwrctrl_pwrnap_show, kgsl_pwrctrl_pwrnap_store); +DEVICE_ATTR(pwrnap, 0664, kgsl_pwrctrl_pwrnap_show, kgsl_pwrctrl_pwrnap_store); DEVICE_ATTR(idle_timer, 0644, kgsl_pwrctrl_idle_timer_show, kgsl_pwrctrl_idle_timer_store); DEVICE_ATTR(gpubusy, 0644, kgsl_pwrctrl_gpubusy_show, @@ -300,20 +334,20 @@ static void kgsl_pwrctrl_busy_time(struct kgsl_device *device, bool on_time) do_gettimeofday(&(b->start)); } -void kgsl_pwrctrl_clk(struct kgsl_device *device, int state) +void kgsl_pwrctrl_clk(struct kgsl_device *device, int state, + int requested_state) { struct kgsl_pwrctrl *pwr = &device->pwrctrl; int i = 0; if (state == KGSL_PWRFLAGS_OFF) { if (test_and_clear_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->power_flags)) { - KGSL_PWR_INFO(device, - "clocks off, device %d\n", device->id); + trace_kgsl_clk(device, state); for (i = KGSL_MAX_CLKS - 1; i > 0; i--) if (pwr->grp_clks[i]) clk_disable(pwr->grp_clks[i]); if ((pwr->pwrlevels[0].gpu_freq > 0) && - (device->requested_state != KGSL_STATE_NAP)) + (requested_state != KGSL_STATE_NAP)) clk_set_rate(pwr->grp_clks[0], pwr->pwrlevels[pwr->num_pwrlevels - 1]. gpu_freq); @@ -322,9 +356,7 @@ void kgsl_pwrctrl_clk(struct kgsl_device *device, int state) } else if (state == KGSL_PWRFLAGS_ON) { if (!test_and_set_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->power_flags)) { - KGSL_PWR_INFO(device, - "clocks on, device %d\n", device->id); - + trace_kgsl_clk(device, state); if ((pwr->pwrlevels[0].gpu_freq > 0) && (device->state != KGSL_STATE_NAP)) clk_set_rate(pwr->grp_clks[0], @@ -348,8 +380,7 @@ void kgsl_pwrctrl_axi(struct kgsl_device *device, int state) if (state == KGSL_PWRFLAGS_OFF) { if (test_and_clear_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)) { - KGSL_PWR_INFO(device, - "axi off, device %d\n", device->id); + trace_kgsl_bus(device, state); if (pwr->ebi1_clk) { clk_set_rate(pwr->ebi1_clk, 0); clk_disable(pwr->ebi1_clk); @@ -361,8 +392,7 @@ void kgsl_pwrctrl_axi(struct kgsl_device *device, int state) } else if (state == KGSL_PWRFLAGS_ON) { if (!test_and_set_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)) { - KGSL_PWR_INFO(device, - "axi on, device %d\n", device->id); + trace_kgsl_bus(device, state); if (pwr->ebi1_clk) { clk_enable(pwr->ebi1_clk); clk_set_rate(pwr->ebi1_clk, @@ -384,16 +414,14 @@ void kgsl_pwrctrl_pwrrail(struct kgsl_device *device, int state) if (state == KGSL_PWRFLAGS_OFF) { if (test_and_clear_bit(KGSL_PWRFLAGS_POWER_ON, &pwr->power_flags)) { - KGSL_PWR_INFO(device, - "power off, device %d\n", device->id); + trace_kgsl_rail(device, state); if (pwr->gpu_reg) regulator_disable(pwr->gpu_reg); } } else if (state == KGSL_PWRFLAGS_ON) { if (!test_and_set_bit(KGSL_PWRFLAGS_POWER_ON, &pwr->power_flags)) { - KGSL_PWR_INFO(device, - "power on, device %d\n", device->id); + trace_kgsl_rail(device, state); if (pwr->gpu_reg) regulator_enable(pwr->gpu_reg); } @@ -407,17 +435,13 @@ void kgsl_pwrctrl_irq(struct kgsl_device *device, int state) if (state == KGSL_PWRFLAGS_ON) { if (!test_and_set_bit(KGSL_PWRFLAGS_IRQ_ON, &pwr->power_flags)) { - KGSL_PWR_INFO(device, - "irq on, device %d\n", device->id); + trace_kgsl_irq(device, state); enable_irq(pwr->interrupt_num); - device->ftbl->irqctrl(device, 1); } } else if (state == KGSL_PWRFLAGS_OFF) { if (test_and_clear_bit(KGSL_PWRFLAGS_IRQ_ON, &pwr->power_flags)) { - KGSL_PWR_INFO(device, - "irq off, device %d\n", device->id); - device->ftbl->irqctrl(device, 0); + trace_kgsl_irq(device, state); if (in_interrupt()) disable_irq_nosync(pwr->interrupt_num); else @@ -434,49 +458,43 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) struct platform_device *pdev = container_of(device->parentdev, struct platform_device, dev); struct kgsl_pwrctrl *pwr = &device->pwrctrl; - struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data; - struct kgsl_device_pwr_data *pdata_pwr = &pdata_dev->pwr_data; - const char *clk_names[KGSL_MAX_CLKS] = {pwr->src_clk_name, - pdata_dev->clk.name.clk, - pdata_dev->clk.name.pclk, - pdata_dev->imem_clk_name.clk, - pdata_dev->imem_clk_name.pclk}; + struct kgsl_device_platform_data *pdata = pdev->dev.platform_data; /*acquire clocks */ - for (i = 1; i < KGSL_MAX_CLKS; i++) { - if (clk_names[i]) { - clk = clk_get(&pdev->dev, clk_names[i]); + for (i = 0; i < KGSL_MAX_CLKS; i++) { + if (pdata->clk_map & clks[i].map) { + clk = clk_get(&pdev->dev, clks[i].name); if (IS_ERR(clk)) goto clk_err; pwr->grp_clks[i] = clk; } } /* Make sure we have a source clk for freq setting */ - clk = clk_get(&pdev->dev, clk_names[0]); - pwr->grp_clks[0] = (IS_ERR(clk)) ? pwr->grp_clks[1] : clk; + if (pwr->grp_clks[0] == NULL) + pwr->grp_clks[0] = pwr->grp_clks[1]; /* put the AXI bus into asynchronous mode with the graphics cores */ - if (pdata_pwr->set_grp_async != NULL) - pdata_pwr->set_grp_async(); + if (pdata->set_grp_async != NULL) + pdata->set_grp_async(); - if (pdata_pwr->num_levels > KGSL_MAX_PWRLEVELS) { + if (pdata->num_levels > KGSL_MAX_PWRLEVELS) { KGSL_PWR_ERR(device, "invalid power level count: %d\n", - pdata_pwr->num_levels); + pdata->num_levels); result = -EINVAL; goto done; } - pwr->num_pwrlevels = pdata_pwr->num_levels; - pwr->active_pwrlevel = pdata_pwr->init_level; - for (i = 0; i < pdata_pwr->num_levels; i++) { + pwr->num_pwrlevels = pdata->num_levels; + pwr->active_pwrlevel = pdata->init_level; + for (i = 0; i < pdata->num_levels; i++) { pwr->pwrlevels[i].gpu_freq = - (pdata_pwr->pwrlevel[i].gpu_freq > 0) ? + (pdata->pwrlevel[i].gpu_freq > 0) ? clk_round_rate(pwr->grp_clks[0], - pdata_pwr->pwrlevel[i]. + pdata->pwrlevel[i]. gpu_freq) : 0; pwr->pwrlevels[i].bus_freq = - pdata_pwr->pwrlevel[i].bus_freq; + pdata->pwrlevel[i].bus_freq; pwr->pwrlevels[i].io_fraction = - pdata_pwr->pwrlevel[i].io_fraction; + pdata->pwrlevel[i].io_fraction; } /* Do not set_rate for targets in sync with AXI */ if (pwr->pwrlevels[0].gpu_freq > 0) @@ -489,8 +507,10 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) pwr->power_flags = 0; - pwr->nap_allowed = pdata_pwr->nap_allowed; - pwr->interval_timeout = pdata_pwr->idle_timeout; + pwr->nap_allowed = pdata->nap_allowed; + pwr->idle_needed = pdata->idle_needed; + pwr->interval_timeout = pdata->idle_timeout; + pwr->strtstp_sleepwake = pdata->strtstp_sleepwake; pwr->ebi1_clk = clk_get(&pdev->dev, "bus_clk"); if (IS_ERR(pwr->ebi1_clk)) pwr->ebi1_clk = NULL; @@ -498,15 +518,14 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) clk_set_rate(pwr->ebi1_clk, pwr->pwrlevels[pwr->active_pwrlevel]. bus_freq); - if (pdata_dev->clk.bus_scale_table != NULL) { - pwr->pcl = - msm_bus_scale_register_client(pdata_dev->clk. + if (pdata->bus_scale_table != NULL) { + pwr->pcl = msm_bus_scale_register_client(pdata-> bus_scale_table); if (!pwr->pcl) { KGSL_PWR_ERR(device, "msm_bus_scale_register_client failed: " "id %d table %p", device->id, - pdata_dev->clk.bus_scale_table); + pdata->bus_scale_table); result = -EINVAL; goto done; } @@ -529,7 +548,7 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) clk_err: result = PTR_ERR(clk); KGSL_PWR_ERR(device, "clk_get(%s) failed: %d\n", - clk_names[i], result); + clks[i].name, result); done: return result; @@ -578,6 +597,9 @@ void kgsl_idle_check(struct work_struct *work) { struct kgsl_device *device = container_of(work, struct kgsl_device, idle_check_ws); + WARN_ON(device == NULL); + if (device == NULL) + return; mutex_lock(&device->mutex); if (device->state & (KGSL_STATE_ACTIVE | KGSL_STATE_NAP)) { @@ -599,7 +621,7 @@ void kgsl_idle_check(struct work_struct *work) } } else if (device->state & (KGSL_STATE_HUNG | KGSL_STATE_DUMP_AND_RECOVER)) { - device->requested_state = KGSL_STATE_NONE; + kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); } mutex_unlock(&device->mutex); @@ -611,7 +633,11 @@ void kgsl_timer(unsigned long data) KGSL_PWR_INFO(device, "idle timer expired device %d\n", device->id); if (device->requested_state != KGSL_STATE_SUSPEND) { - device->requested_state = KGSL_STATE_SLEEP; + if (device->pwrctrl.restore_slumber || + device->pwrctrl.strtstp_sleepwake) + kgsl_pwrctrl_request_state(device, KGSL_STATE_SLUMBER); + else + kgsl_pwrctrl_request_state(device, KGSL_STATE_SLEEP); /* Have work run in a non-interrupt context. */ queue_work(device->work_queue, &device->idle_check_ws); } @@ -620,9 +646,33 @@ void kgsl_timer(unsigned long data) void kgsl_pre_hwaccess(struct kgsl_device *device) { BUG_ON(!mutex_is_locked(&device->mutex)); - if (device->state & (KGSL_STATE_SLEEP | KGSL_STATE_NAP | - KGSL_STATE_SLUMBER)) + switch (device->state) { + case KGSL_STATE_ACTIVE: + return; + case KGSL_STATE_NAP: + case KGSL_STATE_SLEEP: + case KGSL_STATE_SLUMBER: kgsl_pwrctrl_wake(device); + break; + case KGSL_STATE_SUSPEND: + kgsl_check_suspended(device); + break; + case KGSL_STATE_INIT: + case KGSL_STATE_HUNG: + case KGSL_STATE_DUMP_AND_RECOVER: + if (test_bit(KGSL_PWRFLAGS_CLK_ON, + &device->pwrctrl.power_flags)) + break; + else + KGSL_PWR_ERR(device, + "hw access while clocks off from state %d\n", + device->state); + break; + default: + KGSL_PWR_ERR(device, "hw access while in unknown state %d\n", + device->state); + break; + } } EXPORT_SYMBOL(kgsl_pre_hwaccess); @@ -642,153 +692,179 @@ void kgsl_check_suspended(struct kgsl_device *device) } static int -_slumber(struct kgsl_device *device) +_nap(struct kgsl_device *device) { - int status = -EINVAL; - if (!device) - return -EINVAL; - KGSL_PWR_WARN(device, "Slumber start\n"); - - device->requested_state = KGSL_STATE_SLUMBER; - del_timer(&device->idle_timer); switch (device->state) { case KGSL_STATE_ACTIVE: - /* Wait for the device to become idle */ - device->ftbl->idle(device, KGSL_TIMEOUT_DEFAULT); + if (!device->ftbl->isidle(device)) { + kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); + return -EBUSY; + } + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF, KGSL_STATE_NAP); + kgsl_pwrctrl_set_state(device, KGSL_STATE_NAP); + if (device->idle_wakelock.name) + wake_unlock(&device->idle_wakelock); case KGSL_STATE_NAP: case KGSL_STATE_SLEEP: - device->ftbl->suspend_context(device); - device->ftbl->stop(device); - device->state = KGSL_STATE_SLUMBER; - device->pwrctrl.restore_slumber = 1; - KGSL_PWR_WARN(device, "state -> SLUMBER, device %d\n", - device->id); + case KGSL_STATE_SLUMBER: break; default: + kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); break; } - status = 0; - /* Don't set requested state to NONE - It's done in kgsl_pwrctrl_sleep*/ - KGSL_PWR_WARN(device, "Done going to slumber\n"); - return status; + return 0; } -/******************************************************************/ -/* Caller must hold the device mutex. */ -int kgsl_pwrctrl_sleep(struct kgsl_device *device) +static void +_sleep_accounting(struct kgsl_device *device) { - struct kgsl_pwrctrl *pwr = &device->pwrctrl; - KGSL_PWR_INFO(device, "sleep device %d\n", device->id); - - /* Work through the legal state transitions */ - if ((device->requested_state == KGSL_STATE_NAP)) { - if (device->pwrctrl.restore_slumber) { - device->requested_state = KGSL_STATE_NONE; - return 0; - } else if (device->ftbl->isidle(device)) - goto nap; - } else if (device->requested_state == KGSL_STATE_SLEEP) { - if (device->state == KGSL_STATE_NAP || - device->ftbl->isidle(device)) { - if (!device->pwrctrl.restore_slumber) - goto sleep; - else - goto slumber; - } - } else if (device->requested_state == KGSL_STATE_SLUMBER) { - if (device->state == KGSL_STATE_INIT) - return 0; - if (device->ftbl->isidle(device)) - goto slumber; - } - - device->requested_state = KGSL_STATE_NONE; - return -EBUSY; - - -slumber: - _slumber(device); - -sleep: - kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); - kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_OFF); - if (pwr->pwrlevels[0].gpu_freq > 0) - clk_set_rate(pwr->grp_clks[0], - pwr->pwrlevels[pwr->num_pwrlevels - 1]. - gpu_freq); kgsl_pwrctrl_busy_time(device, false); - pwr->busy.start.tv_sec = 0; + device->pwrctrl.busy.start.tv_sec = 0; device->pwrctrl.time = 0; - kgsl_pwrscale_sleep(device); - goto clk_off; - -nap: - kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); -clk_off: - kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF); +} - device->state = device->requested_state; - device->requested_state = KGSL_STATE_NONE; - if (device->idle_wakelock.name) +static int +_sleep(struct kgsl_device *device) +{ + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + switch (device->state) { + case KGSL_STATE_ACTIVE: + if (!device->ftbl->isidle(device)) { + kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); + return -EBUSY; + } + /* fall through */ + case KGSL_STATE_NAP: + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); + kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_OFF); + if (pwr->pwrlevels[0].gpu_freq > 0) + clk_set_rate(pwr->grp_clks[0], + pwr->pwrlevels[pwr->num_pwrlevels - 1]. + gpu_freq); + _sleep_accounting(device); + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF, KGSL_STATE_SLEEP); + kgsl_pwrctrl_set_state(device, KGSL_STATE_SLEEP); wake_unlock(&device->idle_wakelock); - KGSL_PWR_WARN(device, "state -> NAP/SLEEP(%d), device %d\n", - device->state, device->id); - + pm_qos_update_request(&device->pm_qos_req_dma, + PM_QOS_DEFAULT_VALUE); + break; + case KGSL_STATE_SLEEP: + case KGSL_STATE_SLUMBER: + break; + default: + KGSL_PWR_WARN(device, "unhandled state %s\n", + kgsl_pwrstate_to_str(device->state)); + break; + } return 0; } -EXPORT_SYMBOL(kgsl_pwrctrl_sleep); static int -_wake_from_slumber(struct kgsl_device *device) +_slumber(struct kgsl_device *device) { - int status = -EINVAL; - if (!device) - return -EINVAL; - - KGSL_PWR_WARN(device, "wake from slumber start\n"); + switch (device->state) { + case KGSL_STATE_ACTIVE: + if (!device->ftbl->isidle(device)) { + kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); + device->pwrctrl.restore_slumber = true; + return -EBUSY; + } + /* fall through */ + case KGSL_STATE_NAP: + case KGSL_STATE_SLEEP: + del_timer_sync(&device->idle_timer); + kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_NOMINAL); + device->pwrctrl.restore_slumber = true; + device->ftbl->suspend_context(device); + device->ftbl->stop(device); + _sleep_accounting(device); + kgsl_pwrctrl_set_state(device, KGSL_STATE_SLUMBER); + if (device->idle_wakelock.name) + wake_unlock(&device->idle_wakelock); + pm_qos_update_request(&device->pm_qos_req_dma, + PM_QOS_DEFAULT_VALUE); + break; + case KGSL_STATE_SLUMBER: + break; + default: + KGSL_PWR_WARN(device, "unhandled state %s\n", + kgsl_pwrstate_to_str(device->state)); + break; + } + return 0; +} - device->requested_state = KGSL_STATE_ACTIVE; - kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_NOMINAL); - status = device->ftbl->start(device, 0); - device->requested_state = KGSL_STATE_NONE; +/******************************************************************/ +/* Caller must hold the device mutex. */ +int kgsl_pwrctrl_sleep(struct kgsl_device *device) +{ + int status = 0; + KGSL_PWR_INFO(device, "sleep device %d\n", device->id); - KGSL_PWR_WARN(device, "Done waking from slumber\n"); + /* Work through the legal state transitions */ + switch (device->requested_state) { + case KGSL_STATE_NAP: + status = _nap(device); + break; + case KGSL_STATE_SLEEP: + status = _sleep(device); + break; + case KGSL_STATE_SLUMBER: + status = _slumber(device); + break; + default: + KGSL_PWR_INFO(device, "bad state request 0x%x\n", + device->requested_state); + kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); + status = -EINVAL; + break; + } return status; } +EXPORT_SYMBOL(kgsl_pwrctrl_sleep); /******************************************************************/ /* Caller must hold the device mutex. */ void kgsl_pwrctrl_wake(struct kgsl_device *device) { - if (device->state & (KGSL_STATE_SUSPEND | KGSL_STATE_INIT)) - return; - - if (device->state == KGSL_STATE_SLUMBER) - _wake_from_slumber(device); - - if (device->state != KGSL_STATE_NAP) { + int status; + kgsl_pwrctrl_request_state(device, KGSL_STATE_ACTIVE); + switch (device->state) { + case KGSL_STATE_SLUMBER: + status = device->ftbl->start(device, 0); + if (status) { + kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); + KGSL_DRV_ERR(device, "start failed %d\n", status); + break; + } + /* fall through */ + case KGSL_STATE_SLEEP: kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON); kgsl_pwrscale_wake(device); - } - - /* Turn on the core clocks */ - kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON); - - /* Enable state before turning on irq */ - device->state = KGSL_STATE_ACTIVE; - KGSL_PWR_WARN(device, "state -> ACTIVE, device %d\n", device->id); - kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON); - - /* Re-enable HW access */ - mod_timer(&device->idle_timer, + /* fall through */ + case KGSL_STATE_NAP: + /* Turn on the core clocks */ + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON, KGSL_STATE_ACTIVE); + /* Enable state before turning on irq */ + kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE); + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON); + /* Re-enable HW access */ + mod_timer(&device->idle_timer, jiffies + device->pwrctrl.interval_timeout); - - if (device->idle_wakelock.name) wake_lock(&device->idle_wakelock); - - KGSL_PWR_INFO(device, "wake return for device %d\n", device->id); + if (device->pwrctrl.restore_slumber == false) + pm_qos_update_request(&device->pm_qos_req_dma, + GPU_SWFI_LATENCY); + case KGSL_STATE_ACTIVE: + break; + default: + KGSL_PWR_WARN(device, "unhandled state %s\n", + kgsl_pwrstate_to_str(device->state)); + kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); + break; + } } EXPORT_SYMBOL(kgsl_pwrctrl_wake); @@ -796,7 +872,7 @@ void kgsl_pwrctrl_enable(struct kgsl_device *device) { /* Order pwrrail/clk sequence based upon platform */ kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_ON); - kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON); + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON, KGSL_STATE_ACTIVE); kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON); } EXPORT_SYMBOL(kgsl_pwrctrl_enable); @@ -805,7 +881,52 @@ void kgsl_pwrctrl_disable(struct kgsl_device *device) { /* Order pwrrail/clk sequence based upon platform */ kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_OFF); - kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF); + kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF, KGSL_STATE_SLEEP); kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_OFF); } EXPORT_SYMBOL(kgsl_pwrctrl_disable); + +void kgsl_pwrctrl_set_state(struct kgsl_device *device, unsigned int state) +{ + trace_kgsl_pwr_set_state(device, state); + device->state = state; + device->requested_state = KGSL_STATE_NONE; +} +EXPORT_SYMBOL(kgsl_pwrctrl_set_state); + +void kgsl_pwrctrl_request_state(struct kgsl_device *device, unsigned int state) +{ + if (state != KGSL_STATE_NONE && state != device->requested_state) + trace_kgsl_pwr_request_state(device, state); + device->requested_state = state; +} +EXPORT_SYMBOL(kgsl_pwrctrl_request_state); + +const char *kgsl_pwrstate_to_str(unsigned int state) +{ + switch (state) { + case KGSL_STATE_NONE: + return "NONE"; + case KGSL_STATE_INIT: + return "INIT"; + case KGSL_STATE_ACTIVE: + return "ACTIVE"; + case KGSL_STATE_NAP: + return "NAP"; + case KGSL_STATE_SLEEP: + return "SLEEP"; + case KGSL_STATE_SUSPEND: + return "SUSPEND"; + case KGSL_STATE_HUNG: + return "HUNG"; + case KGSL_STATE_DUMP_AND_RECOVER: + return "DNR"; + case KGSL_STATE_SLUMBER: + return "SLUMBER"; + default: + break; + } + return "UNKNOWN"; +} +EXPORT_SYMBOL(kgsl_pwrstate_to_str); + diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 9bf3c0fff1047..caaed92c840b1 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -47,12 +47,13 @@ struct kgsl_pwrctrl { int thermal_pwrlevel; unsigned int num_pwrlevels; unsigned int interval_timeout; + bool strtstp_sleepwake; struct regulator *gpu_reg; uint32_t pcl; unsigned int nap_allowed; + unsigned int idle_needed; const char *regulator_name; const char *irq_name; - const char *src_clk_name; s64 time; struct kgsl_busy busy; unsigned int restore_slumber; @@ -78,4 +79,6 @@ static inline unsigned long kgsl_get_clkrate(struct clk *clk) return (clk != NULL) ? clk_get_rate(clk) : 0; } +void kgsl_pwrctrl_set_state(struct kgsl_device *device, unsigned int state); +void kgsl_pwrctrl_request_state(struct kgsl_device *device, unsigned int state); #endif /* __KGSL_PWRCTRL_H */ diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c index b5c6876702fcc..d0b2a412cae90 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.c +++ b/drivers/gpu/msm/kgsl_pwrscale.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -42,7 +42,7 @@ static struct kgsl_pwrscale_policy *kgsl_pwrscale_policies[] = { #ifdef CONFIG_MSM_SCM &kgsl_pwrscale_policy_tz, #endif -#ifdef CONFIG_MSM_SLEEP_STATS +#ifdef CONFIG_MSM_SLEEP_STATS_DEVICE &kgsl_pwrscale_policy_idlestats, #endif NULL @@ -90,7 +90,7 @@ static ssize_t pwrscale_policy_show(struct kgsl_device *device, char *buf) return ret; } -PWRSCALE_ATTR(policy, 0644, pwrscale_policy_show, pwrscale_policy_store); +PWRSCALE_ATTR(policy, 0664, pwrscale_policy_show, pwrscale_policy_store); static ssize_t pwrscale_avail_policies_show(struct kgsl_device *device, char *buf) @@ -267,7 +267,6 @@ void kgsl_pwrscale_policy_remove_files(struct kgsl_device *device, sysfs_remove_group(&pwrscale->kobj, attr_group); kobject_del(&pwrscale->kobj); kobject_put(&pwrscale->kobj); - pwrscale->kobj.state_initialized = 0; } static void _kgsl_pwrscale_detach_policy(struct kgsl_device *device) @@ -298,6 +297,11 @@ int kgsl_pwrscale_attach_policy(struct kgsl_device *device, if (device->pwrscale.policy == policy) goto done; + if (device->pwrctrl.num_pwrlevels < 3) { + ret = -EINVAL; + goto done; + } + if (device->pwrscale.policy != NULL) _kgsl_pwrscale_detach_policy(device); diff --git a/drivers/gpu/msm/kgsl_pwrscale_idlestats.c b/drivers/gpu/msm/kgsl_pwrscale_idlestats.c index d5fa84edec989..2976b0bccaa72 100644 --- a/drivers/gpu/msm/kgsl_pwrscale_idlestats.c +++ b/drivers/gpu/msm/kgsl_pwrscale_idlestats.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -73,7 +73,7 @@ static void idlestats_get_sample(struct msm_idle_stats_device *idledev, /* If the GPU is asleep, don't wake it up - assume that we are idle */ - if (!(device->state & (KGSL_STATE_SLEEP | KGSL_STATE_NAP))) { + if (device->state == KGSL_STATE_ACTIVE) { device->ftbl->power_stats(device, &stats); pulse->busy_start_time = pwr->time - stats.busy_time; pulse->busy_interval = stats.busy_time; @@ -149,7 +149,8 @@ static void idlestats_sleep(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) { struct idlestats_priv *priv = pwrscale->priv; - priv->idledev.stats->event |= MSM_IDLE_STATS_EVENT_IDLE_TIMER_EXPIRED; + msm_idle_stats_update_event(&priv->idledev, + MSM_IDLE_STATS_EVENT_IDLE_TIMER_EXPIRED); } static int idlestats_init(struct kgsl_device *device, diff --git a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c index f3e84e45bef44..931ef48b49544 100644 --- a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c +++ b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,7 @@ struct tz_priv { unsigned int no_switch_cnt; unsigned int skip_cnt; }; +spinlock_t tz_lock; #define SWITCH_OFF 200 #define SWITCH_OFF_RESET_TH 40 @@ -38,13 +40,17 @@ struct tz_priv { #ifdef CONFIG_MSM_SCM /* Trap into the TrustZone, and call funcs there. */ -static int __secure_tz_entry(u32 cmd, u32 val) +static int __secure_tz_entry(u32 cmd, u32 val, u32 id) { + int ret; + spin_lock(&tz_lock); __iowmb(); - return scm_call_atomic1(SCM_SVC_IO, cmd, val); + ret = scm_call_atomic2(SCM_SVC_IO, cmd, val, id); + spin_unlock(&tz_lock); + return ret; } #else -static int __secure_tz_entry(u32 cmd, u32 val) +static int __secure_tz_entry(u32 cmd, u32 val, u32 id) { return 0; } @@ -107,7 +113,8 @@ static void tz_wake(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) { struct tz_priv *priv = pwrscale->priv; if (device->state != KGSL_STATE_NAP && - priv->governor == TZ_GOVERNOR_ONDEMAND) + priv->governor == TZ_GOVERNOR_ONDEMAND && + device->pwrctrl.restore_slumber == 0) kgsl_pwrctrl_pwrlevel_change(device, device->pwrctrl.thermal_pwrlevel); } @@ -117,7 +124,7 @@ static void tz_idle(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) struct kgsl_pwrctrl *pwr = &device->pwrctrl; struct tz_priv *priv = pwrscale->priv; struct kgsl_power_stats stats; - int val; + int val, idle; /* In "performance" mode the clock speed always stays the same */ @@ -145,19 +152,26 @@ static void tz_idle(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) priv->no_switch_cnt = 0; } - val = __secure_tz_entry(TZ_UPDATE_ID, - stats.total_time - stats.busy_time); + idle = stats.total_time - stats.busy_time; + idle = (idle > 0) ? idle : 0; + val = __secure_tz_entry(TZ_UPDATE_ID, idle, device->id); if (val) kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel + val); } +static void tz_busy(struct kgsl_device *device, + struct kgsl_pwrscale *pwrscale) +{ + device->on_time = ktime_to_us(ktime_get()); +} + static void tz_sleep(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) { struct tz_priv *priv = pwrscale->priv; - __secure_tz_entry(TZ_RESET_ID, 0); + __secure_tz_entry(TZ_RESET_ID, 0, device->id); priv->no_switch_cnt = 0; } @@ -174,6 +188,7 @@ static int tz_init(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) return -ENOMEM; priv->governor = TZ_GOVERNOR_ONDEMAND; + spin_lock_init(&tz_lock); kgsl_pwrscale_policy_add_files(device, pwrscale, &tz_attr_group); return 0; @@ -189,6 +204,7 @@ static void tz_close(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) struct kgsl_pwrscale_policy kgsl_pwrscale_policy_tz = { .name = "trustzone", .init = tz_init, + .busy = tz_busy, .idle = tz_idle, .sleep = tz_sleep, .wake = tz_wake, diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index b59761d0d69fe..57cd683348222 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include "kgsl.h" #include "kgsl_sharedmem.h" @@ -282,7 +284,7 @@ static void outer_cache_range_op_sg(struct scatterlist *sg, int sglen, int op) int i; for_each_sg(sg, s, sglen, i) { - unsigned int paddr = sg_phys(s); + unsigned int paddr = kgsl_get_sg_pa(s); _outer_cache_range_op(op, paddr, s->length); } } @@ -297,13 +299,14 @@ static int kgsl_vmalloc_vmfault(struct kgsl_memdesc *memdesc, struct vm_area_struct *vma, struct vm_fault *vmf) { - unsigned long offset, pg; + unsigned long offset; struct page *page; + int i; offset = (unsigned long) vmf->virtual_address - vma->vm_start; - pg = (unsigned long) memdesc->hostptr + offset; - page = vmalloc_to_page((void *) pg); + i = offset >> PAGE_SHIFT; + page = sg_page(&memdesc->sg[i]); if (page == NULL) return VM_FAULT_SIGBUS; @@ -320,8 +323,14 @@ static int kgsl_vmalloc_vmflags(struct kgsl_memdesc *memdesc) static void kgsl_vmalloc_free(struct kgsl_memdesc *memdesc) { + int i = 0; + struct scatterlist *sg; kgsl_driver.stats.vmalloc -= memdesc->size; - vfree(memdesc->hostptr); + if (memdesc->hostptr) + vunmap(memdesc->hostptr); + if (memdesc->sg) + for_each_sg(memdesc->sg, sg, memdesc->sglen, i) + __free_page(sg_page(sg)); } static int kgsl_contiguous_vmflags(struct kgsl_memdesc *memdesc) @@ -329,6 +338,39 @@ static int kgsl_contiguous_vmflags(struct kgsl_memdesc *memdesc) return VM_RESERVED | VM_IO | VM_PFNMAP | VM_DONTEXPAND; } +/* + * kgsl_vmalloc_map_kernel - Map the memory in memdesc to kernel address space + * + * @memdesc - The memory descriptor which contains information about the memory + * + * Return: 0 on success else error code + */ +static int kgsl_vmalloc_map_kernel(struct kgsl_memdesc *memdesc) +{ + if (!memdesc->hostptr) { + pgprot_t page_prot = pgprot_writecombine(PAGE_KERNEL); + struct page **pages = NULL; + struct scatterlist *sg; + int i; + /* create a list of pages to call vmap */ + pages = vmalloc(memdesc->sglen * sizeof(struct page *)); + if (!pages) { + KGSL_CORE_ERR("vmalloc(%d) failed\n", + memdesc->sglen * sizeof(struct page *)); + return -ENOMEM; + } + for_each_sg(memdesc->sg, sg, memdesc->sglen, i) + pages[i] = sg_page(sg); + memdesc->hostptr = vmap(pages, memdesc->sglen, + VM_IOREMAP, page_prot); + vfree(pages); + } + if (!memdesc->hostptr) + return -ENOMEM; + + return 0; +} + static int kgsl_contiguous_vmfault(struct kgsl_memdesc *memdesc, struct vm_area_struct *vma, struct vm_fault *vmf) @@ -372,6 +414,7 @@ struct kgsl_memdesc_ops kgsl_vmalloc_ops = { .free = kgsl_vmalloc_free, .vmflags = kgsl_vmalloc_vmflags, .vmfault = kgsl_vmalloc_vmfault, + .map_kernel_mem = kgsl_vmalloc_map_kernel, }; EXPORT_SYMBOL(kgsl_vmalloc_ops); @@ -409,7 +452,7 @@ EXPORT_SYMBOL(kgsl_cache_range_op); static int _kgsl_sharedmem_vmalloc(struct kgsl_memdesc *memdesc, struct kgsl_pagetable *pagetable, - void *ptr, size_t size, unsigned int protflags) + size_t size, unsigned int protflags) { int order, ret = 0; int sglen = PAGE_ALIGN(size) / PAGE_SIZE; @@ -419,27 +462,31 @@ _kgsl_sharedmem_vmalloc(struct kgsl_memdesc *memdesc, memdesc->pagetable = pagetable; memdesc->priv = KGSL_MEMFLAGS_CACHED; memdesc->ops = &kgsl_vmalloc_ops; - memdesc->hostptr = (void *) ptr; - memdesc->sg = kmalloc(sglen * sizeof(struct scatterlist), GFP_KERNEL); + memdesc->sg = vmalloc(sglen * sizeof(struct scatterlist)); if (memdesc->sg == NULL) { ret = -ENOMEM; goto done; } + kmemleak_not_leak(memdesc->sg); + memdesc->sglen = sglen; sg_init_table(memdesc->sg, sglen); - for (i = 0; i < memdesc->sglen; i++, ptr += PAGE_SIZE) { - struct page *page = vmalloc_to_page(ptr); + for (i = 0; i < memdesc->sglen; i++) { + struct page *page = alloc_page(GFP_KERNEL | __GFP_ZERO | + __GFP_HIGHMEM); if (!page) { - ret = -EINVAL; + ret = -ENOMEM; + memdesc->sglen = i; goto done; } + flush_dcache_page(page); sg_set_page(&memdesc->sg[i], page, PAGE_SIZE, 0); } - - kgsl_cache_range_op(memdesc, KGSL_CACHE_OP_INV); + outer_cache_range_op_sg(memdesc->sg, memdesc->sglen, + KGSL_CACHE_OP_FLUSH); ret = kgsl_mmu_map(pagetable, memdesc, protflags); @@ -465,20 +512,18 @@ int kgsl_sharedmem_vmalloc(struct kgsl_memdesc *memdesc, struct kgsl_pagetable *pagetable, size_t size) { - void *ptr; - + int ret = 0; BUG_ON(size == 0); size = ALIGN(size, PAGE_SIZE * 2); - ptr = vmalloc(size); - if (ptr == NULL) { - KGSL_CORE_ERR("vmalloc(%d) failed\n", size); - return -ENOMEM; - } - - return _kgsl_sharedmem_vmalloc(memdesc, pagetable, ptr, size, + ret = _kgsl_sharedmem_vmalloc(memdesc, pagetable, size, GSL_PT_PAGE_RV | GSL_PT_PAGE_WV); + if (!ret) + ret = kgsl_vmalloc_map_kernel(memdesc); + if (ret) + kgsl_sharedmem_free(memdesc); + return ret; } EXPORT_SYMBOL(kgsl_sharedmem_vmalloc); @@ -487,23 +532,15 @@ kgsl_sharedmem_vmalloc_user(struct kgsl_memdesc *memdesc, struct kgsl_pagetable *pagetable, size_t size, int flags) { - void *ptr; unsigned int protflags; BUG_ON(size == 0); - ptr = vmalloc_user(size); - - if (ptr == NULL) { - KGSL_CORE_ERR("vmalloc_user(%d) failed: allocated=%d\n", - size, kgsl_driver.stats.vmalloc); - return -ENOMEM; - } protflags = GSL_PT_PAGE_RV; if (!(flags & KGSL_MEMFLAGS_GPUREADONLY)) protflags |= GSL_PT_PAGE_WV; - return _kgsl_sharedmem_vmalloc(memdesc, pagetable, ptr, size, + return _kgsl_sharedmem_vmalloc(memdesc, pagetable, size, protflags); } EXPORT_SYMBOL(kgsl_sharedmem_vmalloc_user); @@ -554,7 +591,7 @@ void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc) if (memdesc->ops && memdesc->ops->free) memdesc->ops->free(memdesc); - kfree(memdesc->sg); + vfree(memdesc->sg); memset(memdesc, 0, sizeof(*memdesc)); } @@ -636,13 +673,17 @@ kgsl_sharedmem_readl(const struct kgsl_memdesc *memdesc, uint32_t *dst, unsigned int offsetbytes) { + uint32_t *src; BUG_ON(memdesc == NULL || memdesc->hostptr == NULL || dst == NULL); - WARN_ON(offsetbytes + sizeof(unsigned int) > memdesc->size); + WARN_ON(offsetbytes % sizeof(uint32_t) != 0); + if (offsetbytes % sizeof(uint32_t) != 0) + return -EINVAL; - if (offsetbytes + sizeof(unsigned int) > memdesc->size) + WARN_ON(offsetbytes + sizeof(uint32_t) > memdesc->size); + if (offsetbytes + sizeof(uint32_t) > memdesc->size) return -ERANGE; - - *dst = readl_relaxed(memdesc->hostptr + offsetbytes); + src = (uint32_t *)(memdesc->hostptr + offsetbytes); + *dst = *src; return 0; } EXPORT_SYMBOL(kgsl_sharedmem_readl); @@ -652,12 +693,19 @@ kgsl_sharedmem_writel(const struct kgsl_memdesc *memdesc, unsigned int offsetbytes, uint32_t src) { + uint32_t *dst; BUG_ON(memdesc == NULL || memdesc->hostptr == NULL); - BUG_ON(offsetbytes + sizeof(unsigned int) > memdesc->size); + WARN_ON(offsetbytes % sizeof(uint32_t) != 0); + if (offsetbytes % sizeof(uint32_t) != 0) + return -EINVAL; - kgsl_cffdump_setmem(memdesc->physaddr + offsetbytes, - src, sizeof(uint)); - writel_relaxed(src, memdesc->hostptr + offsetbytes); + WARN_ON(offsetbytes + sizeof(uint32_t) > memdesc->size); + if (offsetbytes + sizeof(uint32_t) > memdesc->size) + return -ERANGE; + kgsl_cffdump_setmem(memdesc->gpuaddr + offsetbytes, + src, sizeof(uint32_t)); + dst = (uint32_t *)(memdesc->hostptr + offsetbytes); + *dst = src; return 0; } EXPORT_SYMBOL(kgsl_sharedmem_writel); @@ -669,9 +717,39 @@ kgsl_sharedmem_set(const struct kgsl_memdesc *memdesc, unsigned int offsetbytes, BUG_ON(memdesc == NULL || memdesc->hostptr == NULL); BUG_ON(offsetbytes + sizebytes > memdesc->size); - kgsl_cffdump_setmem(memdesc->physaddr + offsetbytes, value, - sizebytes); + kgsl_cffdump_setmem(memdesc->gpuaddr + offsetbytes, value, + sizebytes); memset(memdesc->hostptr + offsetbytes, value, sizebytes); return 0; } EXPORT_SYMBOL(kgsl_sharedmem_set); + +/* + * kgsl_sharedmem_map_vma - Map a user vma to physical memory + * + * @vma - The user vma to map + * @memdesc - The memory descriptor which contains information about the + * physical memory + * + * Return: 0 on success else error code + */ +int +kgsl_sharedmem_map_vma(struct vm_area_struct *vma, + const struct kgsl_memdesc *memdesc) +{ + unsigned long addr = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + int ret, i = 0; + + if (!memdesc->sg || (size != memdesc->size) || + (memdesc->sglen != (size / PAGE_SIZE))) + return -EINVAL; + + for (; addr < vma->vm_end; addr += PAGE_SIZE, i++) { + ret = vm_insert_page(vma, addr, sg_page(&memdesc->sg[i])); + if (ret) + return ret; + } + return 0; +} +EXPORT_SYMBOL(kgsl_sharedmem_map_vma); diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h index d345ff340725f..57779fda3f251 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.h +++ b/drivers/gpu/msm/kgsl_sharedmem.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -15,7 +15,10 @@ #include #include +#include #include "kgsl_mmu.h" +#include +#include struct kgsl_device; struct kgsl_process_private; @@ -27,13 +30,6 @@ struct kgsl_process_private; /** Set if the memdesc describes cached memory */ #define KGSL_MEMFLAGS_CACHED 0x00000001 -struct kgsl_memdesc_ops { - int (*vmflags)(struct kgsl_memdesc *); - int (*vmfault)(struct kgsl_memdesc *, struct vm_area_struct *, - struct vm_fault *); - void (*free)(struct kgsl_memdesc *memdesc); -}; - extern struct kgsl_memdesc_ops kgsl_vmalloc_ops; int kgsl_sharedmem_vmalloc(struct kgsl_memdesc *memdesc, @@ -75,19 +71,37 @@ void kgsl_process_uninit_sysfs(struct kgsl_process_private *private); int kgsl_sharedmem_init_sysfs(void); void kgsl_sharedmem_uninit_sysfs(void); +static inline unsigned int kgsl_get_sg_pa(struct scatterlist *sg) +{ + /* + * Try sg_dma_address first to support ion carveout + * regions which do not work with sg_phys(). + */ + unsigned int pa = sg_dma_address(sg); + if (pa == 0) + pa = sg_phys(sg); + return pa; +} + +int +kgsl_sharedmem_map_vma(struct vm_area_struct *vma, + const struct kgsl_memdesc *memdesc); + static inline int memdesc_sg_phys(struct kgsl_memdesc *memdesc, unsigned int physaddr, unsigned int size) { - struct page *page = phys_to_page(physaddr); - - memdesc->sg = kmalloc(sizeof(struct scatterlist) * 1, GFP_KERNEL); + memdesc->sg = vmalloc(sizeof(struct scatterlist) * 1); if (memdesc->sg == NULL) return -ENOMEM; + kmemleak_not_leak(memdesc->sg); + memdesc->sglen = 1; sg_init_table(memdesc->sg, 1); - sg_set_page(&memdesc->sg[0], page, size, 0); + memdesc->sg[0].length = size; + memdesc->sg[0].offset = 0; + memdesc->sg[0].dma_address = physaddr; return 0; } diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c new file mode 100644 index 0000000000000..394bc83bdd061 --- /dev/null +++ b/drivers/gpu/msm/kgsl_snapshot.c @@ -0,0 +1,489 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include "kgsl.h" +#include "kgsl_log.h" +#include "kgsl_device.h" +#include "kgsl_sharedmem.h" +#include "kgsl_snapshot.h" + +/* idr_for_each function to count the number of contexts */ + +static int snapshot_context_count(int id, void *ptr, void *data) +{ + int *count = data; + *count = *count + 1; + + return 0; +} + +/* + * To simplify the iterator loop use a global pointer instead of trying + * to pass around double star references to the snapshot data + */ + +static void *_ctxtptr; + +static int snapshot_context_info(int id, void *ptr, void *data) +{ + struct kgsl_snapshot_linux_context *header = _ctxtptr; + struct kgsl_context *context = ptr; + struct kgsl_device *device = context->dev_priv->device; + + header->id = id; + + /* Future-proof for per-context timestamps - for now, just + * return the global timestamp for all contexts + */ + + header->timestamp_queued = -1; + header->timestamp_retired = device->ftbl->readtimestamp(device, + KGSL_TIMESTAMP_RETIRED); + + _ctxtptr += sizeof(struct kgsl_snapshot_linux_context); + + return 0; +} + +/* Snapshot the Linux specific information */ +static int snapshot_os(struct kgsl_device *device, + void *snapshot, int remain, void *priv) +{ + struct kgsl_snapshot_linux *header = snapshot; + struct kgsl_pwrctrl *pwr = &device->pwrctrl; + struct task_struct *task; + pid_t pid; + int hang = (int) priv; + int ctxtcount = 0; + int size = sizeof(*header); + + /* Figure out how many active contexts there are - these will + * be appended on the end of the structure */ + + idr_for_each(&device->context_idr, snapshot_context_count, &ctxtcount); + + size += ctxtcount * sizeof(struct kgsl_snapshot_linux_context); + + /* Make sure there is enough room for the data */ + if (remain < size) { + SNAPSHOT_ERR_NOMEM(device, "OS"); + return 0; + } + + memset(header, 0, sizeof(*header)); + + header->osid = KGSL_SNAPSHOT_OS_LINUX; + + header->state = hang ? SNAPSHOT_STATE_HUNG : SNAPSHOT_STATE_RUNNING; + + /* Get the kernel build information */ + strlcpy(header->release, utsname()->release, sizeof(header->release)); + strlcpy(header->version, utsname()->version, sizeof(header->version)); + + /* Get the Unix time for the timestamp */ + header->seconds = get_seconds(); + + /* Remember the power information */ + header->power_flags = pwr->power_flags; + header->power_level = pwr->active_pwrlevel; + header->power_interval_timeout = pwr->interval_timeout; + header->grpclk = kgsl_get_clkrate(pwr->grp_clks[0]); + header->busclk = kgsl_get_clkrate(pwr->ebi1_clk); + + /* Future proof for per-context timestamps */ + header->current_context = -1; + + /* Get the current PT base */ + header->ptbase = kgsl_mmu_get_current_ptbase(device); + /* And the PID for the task leader */ + pid = header->pid = kgsl_mmu_get_ptname_from_ptbase(header->ptbase); + + task = find_task_by_vpid(pid); + + if (task) + get_task_comm(header->comm, task); + + header->ctxtcount = ctxtcount; + + /* append information for each context */ + _ctxtptr = snapshot + sizeof(*header); + idr_for_each(&device->context_idr, snapshot_context_info, NULL); + + /* Return the size of the data segment */ + return size; +} +/* + * kgsl_snapshot_dump_indexed_regs - helper function to dump indexed registers + * @device - the device to dump registers from + * @snapshot - pointer to the start of the region of memory for the snapshot + * @remain - a pointer to the number of bytes remaining in the snapshot + * @priv - A pointer to the kgsl_snapshot_indexed_registers data + * + * Given a indexed register cmd/data pair and a count, dump each indexed + * register + */ + +int kgsl_snapshot_dump_indexed_regs(struct kgsl_device *device, + void *snapshot, int remain, void *priv) +{ + struct kgsl_snapshot_indexed_registers *iregs = priv; + struct kgsl_snapshot_indexed_regs *header = snapshot; + unsigned int *data = snapshot + sizeof(*header); + int i; + + if (remain < (iregs->count * 4) + sizeof(*header)) { + SNAPSHOT_ERR_NOMEM(device, "INDEXED REGS"); + return 0; + } + + header->index_reg = iregs->index; + header->data_reg = iregs->data; + header->count = iregs->count; + header->start = iregs->start; + + for (i = 0; i < iregs->count; i++) { + kgsl_regwrite(device, iregs->index, iregs->start + i); + kgsl_regread(device, iregs->data, &data[i]); + } + + return (iregs->count * 4) + sizeof(*header); +} +EXPORT_SYMBOL(kgsl_snapshot_dump_indexed_regs); + +/* + * kgsl_snapshot_dump_regs - helper function to dump device registers + * @device - the device to dump registers from + * @snapshot - pointer to the start of the region of memory for the snapshot + * @remain - a pointer to the number of bytes remaining in the snapshot + * @priv - A pointer to the kgsl_snapshot_registers data + * + * Given an array of register ranges pairs (start,end [inclusive]), dump the + * registers into a snapshot register section. The snapshot region stores a + * part of dwords for each register - the word address of the register, and + * the value. + */ +int kgsl_snapshot_dump_regs(struct kgsl_device *device, void *snapshot, + int remain, void *priv) +{ + struct kgsl_snapshot_regs *header = snapshot; + struct kgsl_snapshot_registers *regs = priv; + unsigned int *data = snapshot + sizeof(*header); + int count = 0, i, j; + + /* Figure out how many registers we are going to dump */ + + for (i = 0; i < regs->count; i++) { + int start = regs->regs[i * 2]; + int end = regs->regs[i * 2 + 1]; + + count += (end - start + 1); + } + + if (remain < (count * 8) + sizeof(*header)) { + SNAPSHOT_ERR_NOMEM(device, "REGISTERS"); + return 0; + } + + for (i = 0; i < regs->count; i++) { + unsigned int start = regs->regs[i * 2]; + unsigned int end = regs->regs[i * 2 + 1]; + + for (j = start; j <= end; j++) { + unsigned int val; + + kgsl_regread(device, j, &val); + *data++ = j; + *data++ = val; + } + } + + header->count = count; + + /* Return the size of the section */ + return (count * 8) + sizeof(*header); +} +EXPORT_SYMBOL(kgsl_snapshot_dump_regs); + +/* + * kgsl_snapshot - construct a device snapshot + * @device - device to snapshot + * @hang - set to 1 if the snapshot was triggered following a hnag + * Given a device, construct a binary snapshot dump of the current device state + * and store it in the device snapshot memory. + */ +int kgsl_device_snapshot(struct kgsl_device *device, int hang) +{ + struct kgsl_snapshot_header *header = device->snapshot; + int remain = device->snapshot_maxsize - sizeof(*header); + void *snapshot; + + /* + * The first hang is always the one we are interested in. To + * avoid a subsequent hang blowing away the first, the snapshot + * is frozen until it is dumped via sysfs. + * + * Note that triggered snapshots are always taken regardless + * of the state and never frozen. + */ + + if (hang && device->snapshot_frozen == 1) + return 0; + + if (device->snapshot == NULL) { + KGSL_DRV_ERR(device, + "snapshot: No snapshot memory available\n"); + return -ENOMEM; + } + + if (remain < sizeof(*header)) { + KGSL_DRV_ERR(device, + "snapshot: Not enough memory for the header\n"); + return -ENOMEM; + } + + header->magic = SNAPSHOT_MAGIC; + + header->gpuid = kgsl_gpuid(device); + + /* Get a pointer to the first section (right after the header) */ + snapshot = ((void *) device->snapshot) + sizeof(*header); + + /* Build the Linux specific header */ + snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_OS, + snapshot, &remain, snapshot_os, (void *) hang); + + /* Get the device specific sections */ + if (device->ftbl->snapshot) + snapshot = device->ftbl->snapshot(device, snapshot, &remain, + hang); + + /* Add the empty end section to let the parser know we are done */ + snapshot = kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_END, + snapshot, &remain, NULL, NULL); + + device->snapshot_timestamp = get_seconds(); + device->snapshot_size = (int) (snapshot - device->snapshot); + + /* Freeze the snapshot on a hang until it gets read */ + device->snapshot_frozen = (hang) ? 1 : 0; + + /* log buffer info to aid in ramdump recovery */ + KGSL_DRV_ERR(device, "snapshot created at va %p pa %lx size %d\n", + device->snapshot, __pa(device->snapshot), + device->snapshot_size); + if (hang) + sysfs_notify(&device->snapshot_kobj, NULL, "timestamp"); + return 0; +} +EXPORT_SYMBOL(kgsl_device_snapshot); + +/* An attribute for showing snapshot details */ +struct kgsl_snapshot_attribute { + struct attribute attr; + ssize_t (*show)(struct kgsl_device *device, char *buf); + ssize_t (*store)(struct kgsl_device *device, const char *buf, + size_t count); +}; + +#define to_snapshot_attr(a) \ +container_of(a, struct kgsl_snapshot_attribute, attr) + +#define kobj_to_device(a) \ +container_of(a, struct kgsl_device, snapshot_kobj) + +/* Dump the sysfs binary data to the user */ +static ssize_t snapshot_show(struct file *filep, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, + size_t count) +{ + struct kgsl_device *device = kobj_to_device(kobj); + + if (device == NULL) + return 0; + + /* Return nothing if we haven't taken a snapshot yet */ + if (device->snapshot_timestamp == 0) + return 0; + + /* Get the mutex to keep things from changing while we are dumping */ + mutex_lock(&device->mutex); + + /* + * Release the freeze on the snapshot the first time the buffer is read + */ + + device->snapshot_frozen = 0; + + if (off >= device->snapshot_size) { + count = 0; + goto exit; + } + + if (off + count > device->snapshot_size) + count = device->snapshot_size - off; + + memcpy(buf, device->snapshot + off, count); + +exit: + mutex_unlock(&device->mutex); + return count; +} + +/* Show the timestamp of the last collected snapshot */ +static ssize_t timestamp_show(struct kgsl_device *device, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%x\n", device->snapshot_timestamp); +} + +/* manually trigger a new snapshot to be collected */ +static ssize_t trigger_store(struct kgsl_device *device, const char *buf, + size_t count) +{ + if (device && count > 0) { + mutex_lock(&device->mutex); + kgsl_device_snapshot(device, 0); + mutex_unlock(&device->mutex); + } + + return count; +} + +static struct bin_attribute snapshot_attr = { + .attr.name = "dump", + .attr.mode = 0444, + .size = 0, + .read = snapshot_show +}; + +#define SNAPSHOT_ATTR(_name, _mode, _show, _store) \ +struct kgsl_snapshot_attribute attr_##_name = { \ + .attr = { .name = __stringify(_name), .mode = _mode }, \ + .show = _show, \ + .store = _store, \ +} + +SNAPSHOT_ATTR(trigger, 0600, NULL, trigger_store); +SNAPSHOT_ATTR(timestamp, 0444, timestamp_show, NULL); + +static void snapshot_sysfs_release(struct kobject *kobj) +{ +} + +static ssize_t snapshot_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct kgsl_snapshot_attribute *pattr = to_snapshot_attr(attr); + struct kgsl_device *device = kobj_to_device(kobj); + ssize_t ret; + + if (device && pattr->show) + ret = pattr->show(device, buf); + else + ret = -EIO; + + return ret; +} + +static ssize_t snapshot_sysfs_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + struct kgsl_snapshot_attribute *pattr = to_snapshot_attr(attr); + struct kgsl_device *device = kobj_to_device(kobj); + ssize_t ret; + + if (device && pattr->store) + ret = pattr->store(device, buf, count); + else + ret = -EIO; + + return ret; +} + +static const struct sysfs_ops snapshot_sysfs_ops = { + .show = snapshot_sysfs_show, + .store = snapshot_sysfs_store, +}; + +static struct kobj_type ktype_snapshot = { + .sysfs_ops = &snapshot_sysfs_ops, + .default_attrs = NULL, + .release = snapshot_sysfs_release, +}; + +/* kgsl_device_snapshot_init - Add resources for the device GPU snapshot + * @device - The device to initalize + * + * Allocate memory for a GPU snapshot for the specified device, + * and create the sysfs files to manage it + */ + +int kgsl_device_snapshot_init(struct kgsl_device *device) +{ + int ret; + + if (device->snapshot == NULL) + device->snapshot = kzalloc(KGSL_SNAPSHOT_MEMSIZE, GFP_KERNEL); + + if (device->snapshot == NULL) + return -ENOMEM; + + device->snapshot_maxsize = KGSL_SNAPSHOT_MEMSIZE; + device->snapshot_timestamp = 0; + + ret = kobject_init_and_add(&device->snapshot_kobj, &ktype_snapshot, + &device->dev->kobj, "snapshot"); + if (ret) + goto done; + + ret = sysfs_create_bin_file(&device->snapshot_kobj, &snapshot_attr); + if (ret) + goto done; + + ret = sysfs_create_file(&device->snapshot_kobj, &attr_trigger.attr); + if (ret) + goto done; + + ret = sysfs_create_file(&device->snapshot_kobj, &attr_timestamp.attr); + +done: + return ret; +} +EXPORT_SYMBOL(kgsl_device_snapshot_init); + +/* kgsl_device_snapshot_close - Take down snapshot memory for a device + * @device - Pointer to the kgsl_device + * + * Remove the sysfs files and free the memory allocated for the GPU + * snapshot + */ + +void kgsl_device_snapshot_close(struct kgsl_device *device) +{ + sysfs_remove_bin_file(&device->snapshot_kobj, &snapshot_attr); + sysfs_remove_file(&device->snapshot_kobj, &attr_trigger.attr); + sysfs_remove_file(&device->snapshot_kobj, &attr_timestamp.attr); + + kobject_put(&device->snapshot_kobj); + + kfree(device->snapshot); + + device->snapshot = NULL; + device->snapshot_maxsize = 0; + device->snapshot_timestamp = 0; +} +EXPORT_SYMBOL(kgsl_device_snapshot_close); diff --git a/drivers/gpu/msm/kgsl_snapshot.h b/drivers/gpu/msm/kgsl_snapshot.h new file mode 100644 index 0000000000000..4fdc8a1b665ac --- /dev/null +++ b/drivers/gpu/msm/kgsl_snapshot.h @@ -0,0 +1,259 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _KGSL_SNAPSHOT_H_ +#define _KGSL_SNAPSHOT_H_ + +#include + +/* Snapshot header */ + +#define SNAPSHOT_MAGIC 0x504D0001 + +/* GPU ID scheme: + * [16:31] - core identifer (0x0002 for 2D or 0x0003 for 3D) + * [00:16] - GPU specific identifier + */ + +struct kgsl_snapshot_header { + __u32 magic; /* Magic identifier */ + __u32 gpuid; /* GPU ID - see above */ +} __packed; + +/* Section header */ +#define SNAPSHOT_SECTION_MAGIC 0xABCD + +struct kgsl_snapshot_section_header { + __u16 magic; /* Magic identifier */ + __u16 id; /* Type of section */ + __u32 size; /* Size of the section including this header */ +} __packed; + +/* Section identifiers */ +#define KGSL_SNAPSHOT_SECTION_OS 0x0101 +#define KGSL_SNAPSHOT_SECTION_REGS 0x0201 +#define KGSL_SNAPSHOT_SECTION_RB 0x0301 +#define KGSL_SNAPSHOT_SECTION_IB 0x0401 +#define KGSL_SNAPSHOT_SECTION_INDEXED_REGS 0x0501 +#define KGSL_SNAPSHOT_SECTION_ISTORE 0x0801 +#define KGSL_SNAPSHOT_SECTION_DEBUG 0x0901 +#define KGSL_SNAPSHOT_SECTION_END 0xFFFF + +/* OS sub-section header */ +#define KGSL_SNAPSHOT_OS_LINUX 0x0001 + +/* Linux OS specific information */ + +#define SNAPSHOT_STATE_HUNG 0 +#define SNAPSHOT_STATE_RUNNING 1 + +struct kgsl_snapshot_linux { + int osid; /* subsection OS identifier */ + int state; /* 1 if the thread is running, 0 for hung */ + __u32 seconds; /* Unix timestamp for the snapshot */ + __u32 power_flags; /* Current power flags */ + __u32 power_level; /* Current power level */ + __u32 power_interval_timeout; /* Power interval timeout */ + __u32 grpclk; /* Current GP clock value */ + __u32 busclk; /* Current busclk value */ + __u32 ptbase; /* Current ptbase */ + __u32 pid; /* PID of the process that owns the PT */ + __u32 current_context; /* ID of the current context */ + __u32 ctxtcount; /* Number of contexts appended to section */ + unsigned char release[32]; /* kernel release */ + unsigned char version[32]; /* kernel version */ + unsigned char comm[16]; /* Name of the process that owns the PT */ +} __packed; + +/* + * This structure contains a record of an active context. + * These are appended one after another in the OS section below + * the header above + */ + +struct kgsl_snapshot_linux_context { + __u32 id; /* The context ID */ + __u32 timestamp_queued; /* The last queued timestamp */ + __u32 timestamp_retired; /* The last timestamp retired by HW */ +}; + +/* Ringbuffer sub-section header */ +struct kgsl_snapshot_rb { + int start; /* dword at the start of the dump */ + int end; /* dword at the end of the dump */ + int rbsize; /* Size (in dwords) of the ringbuffer */ + int wptr; /* Current index of the CPU write pointer */ + int rptr; /* Current index of the GPU read pointer */ + int count; /* Number of dwords in the dump */ +} __packed; + +/* Indirect buffer sub-section header */ +struct kgsl_snapshot_ib { + __u32 gpuaddr; /* GPU address of the the IB */ + __u32 ptbase; /* Base for the pagetable the GPU address is valid in */ + int size; /* Size of the IB */ +} __packed; + +/* Register sub-section header */ +struct kgsl_snapshot_regs { + __u32 count; /* Number of register pairs in the section */ +} __packed; + +/* Indexed register sub-section header */ +struct kgsl_snapshot_indexed_regs { + __u32 index_reg; /* Offset of the index register for this section */ + __u32 data_reg; /* Offset of the data register for this section */ + int start; /* Starting index */ + int count; /* Number of dwords in the data */ +} __packed; + +/* Istore sub-section header */ +struct kgsl_snapshot_istore { + int count; /* Number of instructions in the istore */ +} __packed; + +/* Debug data sub-section header */ + +#define SNAPSHOT_DEBUG_SX 1 +#define SNAPSHOT_DEBUG_CP 2 +#define SNAPSHOT_DEBUG_SQ 3 +#define SNAPSHOT_DEBUG_SQTHREAD 4 +#define SNAPSHOT_DEBUG_MIU 5 + +struct kgsl_snapshot_debug { + int type; /* Type identifier for the attached tata */ + int size; /* Size of the section in bytes */ +} __packed; + +#ifdef __KERNEL__ + +/* Allocate 512K for each device snapshot */ +#define KGSL_SNAPSHOT_MEMSIZE (512 * 1024) + +struct kgsl_device; +/* + * A helper macro to print out "not enough memory functions" - this + * makes it easy to standardize the messages as well as cut down on + * the number of strings in the binary + */ + +#define SNAPSHOT_ERR_NOMEM(_d, _s) \ + KGSL_DRV_ERR((_d), \ + "snapshot: not enough snapshot memory for section %s\n", (_s)) + +/* + * kgsl_snapshot_add_section - Add a new section to the GPU snapshot + * @device - the KGSL device being snapshotted + * @id - the section id + * @snapshot - pointer to the memory for the snapshot + * @remain - pointer to the number of bytes left in the snapshot region + * @func - Function pointer to fill the section + * @priv - Priv pointer to pass to the function + * + * Set up a KGSL snapshot header by filling the memory with the callback + * function and adding the standard section header + */ + +static inline void *kgsl_snapshot_add_section(struct kgsl_device *device, + u16 id, void *snapshot, int *remain, + int (*func)(struct kgsl_device *, void *, int, void *), void *priv) +{ + struct kgsl_snapshot_section_header *header = snapshot; + void *data = snapshot + sizeof(*header); + int ret = 0; + + /* + * Sanity check to make sure there is enough for the header. The + * callback will check to make sure there is enough for the rest + * of the data. If there isn't enough room then don't advance the + * pointer. + */ + + if (*remain < sizeof(*header)) + return snapshot; + + /* It is legal to have no function (i.e. - make an empty section) */ + + if (func) { + ret = func(device, data, *remain, priv); + + /* + * If there wasn't enough room for the data then don't bother + * setting up the header. + */ + + if (ret == 0) + return snapshot; + } + + header->magic = SNAPSHOT_SECTION_MAGIC; + header->id = id; + header->size = ret + sizeof(*header); + + /* Decrement the room left in the snapshot region */ + *remain -= header->size; + /* Advance the pointer to the end of the next function */ + return snapshot + header->size; +} + +/* A common helper function to dump a range of registers. This will be used in + * the GPU specific devices like this: + * + * struct kgsl_snapshot_registers priv; + * priv.regs = registers_array;; + * priv.count = num_registers; + * + * kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS, snapshot, + * remain, kgsl_snapshot_dump_regs, &priv). + * + * Pass in an array of register range pairs in the form of: + * start reg, stop reg + * All the registers between start and stop inclusive will be dumped + */ + +struct kgsl_snapshot_registers { + unsigned int *regs; /* Pointer to the array of register ranges */ + int count; /* Number of entries in the array */ +}; + +int kgsl_snapshot_dump_regs(struct kgsl_device *device, void *snapshot, + int remain, void *priv); + +/* + * A common helper function to dump a set of indexed registers. Use it + * like this: + * + * struct kgsl_snapshot_indexed_registers priv; + * priv.index = REG_INDEX; + * priv.data = REG_DATA; + * priv.count = num_registers + * + * kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_INDEXED_REGS, + * snapshot, remain, kgsl_snapshot_dump_indexed_regs, &priv). + * + * The callback function will write an index from 0 to priv.count to + * the index register and read the data from the data register. + */ + +struct kgsl_snapshot_indexed_registers { + unsigned int index; /* Offset of the index register */ + unsigned int data; /* Offset of the data register */ + unsigned int start; /* Index to start with */ + unsigned int count; /* Number of values to read from the pair */ +}; + +int kgsl_snapshot_dump_indexed_regs(struct kgsl_device *device, + void *snapshot, int remain, void *priv); + +#endif +#endif diff --git a/drivers/gpu/msm/kgsl_trace.c b/drivers/gpu/msm/kgsl_trace.c new file mode 100644 index 0000000000000..2bcca15414bb3 --- /dev/null +++ b/drivers/gpu/msm/kgsl_trace.c @@ -0,0 +1,19 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "kgsl.h" +#include "kgsl_device.h" + +/* Instantiate tracepoints */ +#define CREATE_TRACE_POINTS +#include "kgsl_trace.h" diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h new file mode 100644 index 0000000000000..86a9adc058606 --- /dev/null +++ b/drivers/gpu/msm/kgsl_trace.h @@ -0,0 +1,263 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#if !defined(_KGSL_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _KGSL_TRACE_H + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM kgsl +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE kgsl_trace + +#include + +struct kgsl_device; +struct kgsl_ringbuffer_issueibcmds; +struct kgsl_device_waittimestamp; + +/* + * Tracepoint for kgsl issue ib commands + */ +TRACE_EVENT(kgsl_issueibcmds, + + TP_PROTO(struct kgsl_device *device, + struct kgsl_ringbuffer_issueibcmds *cmd, int result), + + TP_ARGS(device, cmd, result), + + TP_STRUCT__entry( + __string(device_name, device->name) + __field(unsigned int, drawctxt_id) + __field(unsigned int, ibdesc_addr) + __field(unsigned int, numibs) + __field(unsigned int, timestamp) + __field(unsigned int, flags) + __field(int, result) + ), + + TP_fast_assign( + __assign_str(device_name, device->name); + __entry->drawctxt_id = cmd->drawctxt_id; + __entry->ibdesc_addr = cmd->ibdesc_addr; + __entry->numibs = cmd->numibs; + __entry->timestamp = cmd->timestamp; + __entry->flags = cmd->flags; + __entry->result = result; + ), + + TP_printk( + "d_name=%s ctx=%u ib=%u numibs=%u timestamp=%u " + "flags=%u result=%d", + __get_str(device_name), + __entry->drawctxt_id, + __entry->ibdesc_addr, + __entry->numibs, + __entry->timestamp, + __entry->flags, + __entry->result + ) +); + +/* + * Tracepoint for kgsl readtimestamp + */ +TRACE_EVENT(kgsl_readtimestamp, + + TP_PROTO(struct kgsl_device *device, + struct kgsl_cmdstream_readtimestamp *cmd), + + TP_ARGS(device, cmd), + + TP_STRUCT__entry( + __string(device_name, device->name) + __field(unsigned int, type) + __field(unsigned int, timestamp) + ), + + TP_fast_assign( + __assign_str(device_name, device->name); + __entry->type = cmd->type; + __entry->timestamp = cmd->timestamp; + ), + + TP_printk( + "d_name=%s type=%u timestamp=%u", + __get_str(device_name), + __entry->type, + __entry->timestamp + ) +); + +/* + * Tracepoint for kgsl waittimestamp entry + */ +TRACE_EVENT(kgsl_waittimestamp_entry, + + TP_PROTO(struct kgsl_device *device, + struct kgsl_device_waittimestamp *cmd), + + TP_ARGS(device, cmd), + + TP_STRUCT__entry( + __string(device_name, device->name) + __field(unsigned int, timestamp) + __field(unsigned int, timeout) + ), + + TP_fast_assign( + __assign_str(device_name, device->name); + __entry->timestamp = cmd->timestamp; + __entry->timeout = cmd->timeout; + ), + + TP_printk( + "d_name=%s timestamp=%u timeout=%u", + __get_str(device_name), + __entry->timestamp, + __entry->timeout + ) +); + +/* + * Tracepoint for kgsl waittimestamp exit + */ +TRACE_EVENT(kgsl_waittimestamp_exit, + + TP_PROTO(struct kgsl_device *device, int result), + + TP_ARGS(device, result), + + TP_STRUCT__entry( + __string(device_name, device->name) + __field(int, result) + ), + + TP_fast_assign( + __assign_str(device_name, device->name); + __entry->result = result; + ), + + TP_printk( + "d_name=%s result=%d", + __get_str(device_name), + __entry->result + ) +); + +DECLARE_EVENT_CLASS(kgsl_pwr_template, + TP_PROTO(struct kgsl_device *device, int on), + + TP_ARGS(device, on), + + TP_STRUCT__entry( + __string(device_name, device->name) + __field(int, on) + ), + + TP_fast_assign( + __assign_str(device_name, device->name); + __entry->on = on; + ), + + TP_printk( + "d_name=%s %s", + __get_str(device_name), + __entry->on ? "on" : "off" + ) +); + +DEFINE_EVENT(kgsl_pwr_template, kgsl_clk, + TP_PROTO(struct kgsl_device *device, int on), + TP_ARGS(device, on) +); + +DEFINE_EVENT(kgsl_pwr_template, kgsl_irq, + TP_PROTO(struct kgsl_device *device, int on), + TP_ARGS(device, on) +); + +DEFINE_EVENT(kgsl_pwr_template, kgsl_bus, + TP_PROTO(struct kgsl_device *device, int on), + TP_ARGS(device, on) +); + +DEFINE_EVENT(kgsl_pwr_template, kgsl_rail, + TP_PROTO(struct kgsl_device *device, int on), + TP_ARGS(device, on) +); + +TRACE_EVENT(kgsl_pwrlevel, + + TP_PROTO(struct kgsl_device *device, unsigned int pwrlevel, + unsigned int freq), + + TP_ARGS(device, pwrlevel, freq), + + TP_STRUCT__entry( + __string(device_name, device->name) + __field(unsigned int, pwrlevel) + __field(unsigned int, freq) + ), + + TP_fast_assign( + __assign_str(device_name, device->name); + __entry->pwrlevel = pwrlevel; + __entry->freq = freq; + ), + + TP_printk( + "d_name=%s pwrlevel=%d freq=%d", + __get_str(device_name), + __entry->pwrlevel, + __entry->freq + ) +); + +DECLARE_EVENT_CLASS(kgsl_pwrstate_template, + TP_PROTO(struct kgsl_device *device, unsigned int state), + + TP_ARGS(device, state), + + TP_STRUCT__entry( + __string(device_name, device->name) + __field(unsigned int, state) + ), + + TP_fast_assign( + __assign_str(device_name, device->name); + __entry->state = state; + ), + + TP_printk( + "d_name=%s %s", + __get_str(device_name), + kgsl_pwrstate_to_str(__entry->state) + ) +); + +DEFINE_EVENT(kgsl_pwrstate_template, kgsl_pwr_set_state, + TP_PROTO(struct kgsl_device *device, unsigned int state), + TP_ARGS(device, state) +); + +DEFINE_EVENT(kgsl_pwrstate_template, kgsl_pwr_request_state, + TP_PROTO(struct kgsl_device *device, unsigned int state), + TP_ARGS(device, state) +); + +#endif /* _KGSL_TRACE_H */ + +/* This part must be outside protection */ +#include diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c index cdb9c235c7efa..d6f3badac01e9 100644 --- a/drivers/gpu/msm/z180.c +++ b/drivers/gpu/msm/z180.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -18,6 +18,7 @@ #include "z180.h" #include "z180_reg.h" +#include "z180_trace.h" #define DRIVER_VERSION_MAJOR 3 #define DRIVER_VERSION_MINOR 1 @@ -156,13 +157,6 @@ static struct z180_device device_2d0 = { .active_cnt = 0, .iomemname = KGSL_2D0_REG_MEMORY, .ftbl = &z180_functable, -#ifdef CONFIG_HAS_EARLYSUSPEND - .display_off = { - .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING, - .suspend = kgsl_early_suspend_driver, - .resume = kgsl_late_resume_driver, - }, -#endif }, }; @@ -194,13 +188,6 @@ static struct z180_device device_2d1 = { .active_cnt = 0, .iomemname = KGSL_2D1_REG_MEMORY, .ftbl = &z180_functable, - .display_off = { -#ifdef CONFIG_HAS_EARLYSUSPEND - .level = EARLY_SUSPEND_LEVEL_STOP_DRAWING, - .suspend = kgsl_early_suspend_driver, - .resume = kgsl_late_resume_driver, -#endif - }, }, }; @@ -213,6 +200,8 @@ static irqreturn_t z180_isr(int irq, void *data) z180_regread(device, ADDR_VGC_IRQSTATUS >> 2, &status); + trace_kgsl_z180_irq_status(device, status); + if (status & GSL_VGC_INT_MASK) { z180_regwrite(device, ADDR_VGC_IRQSTATUS >> 2, status & GSL_VGC_INT_MASK); @@ -245,10 +234,10 @@ static irqreturn_t z180_isr(int irq, void *data) if ((device->pwrctrl.nap_allowed == true) && (device->requested_state == KGSL_STATE_NONE)) { - device->requested_state = KGSL_STATE_NAP; + kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP); queue_work(device->work_queue, &device->idle_check_ws); } - mod_timer(&device->idle_timer, + mod_timer_pending(&device->idle_timer, jiffies + device->pwrctrl.interval_timeout); return result; @@ -457,6 +446,7 @@ z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv, z180_dev->ringbuffer.prevctx = context->id; addcmd(&z180_dev->ringbuffer, index, cmd + ofs, cnt); + kgsl_pwrscale_busy(device); /* Make sure the next ringbuffer entry has a marker */ addmarker(&z180_dev->ringbuffer, nextindex); @@ -520,6 +510,7 @@ static int __devinit z180_probe(struct platform_device *pdev) goto error_close_ringbuffer; kgsl_pwrscale_init(device); + kgsl_pwrscale_attach_policy(device, Z180_DEFAULT_PWRSCALE_POLICY); return status; @@ -548,9 +539,7 @@ static int z180_start(struct kgsl_device *device, unsigned int init_ram) { int status = 0; - device->state = KGSL_STATE_INIT; - device->requested_state = KGSL_STATE_NONE; - KGSL_PWR_WARN(device, "state -> INIT, device %d\n", device->id); + kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT); kgsl_pwrctrl_enable(device); @@ -567,6 +556,7 @@ static int z180_start(struct kgsl_device *device, unsigned int init_ram) mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT); kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON); + device->ftbl->irqctrl(device, 1); return 0; error_clk_off: @@ -577,6 +567,7 @@ static int z180_start(struct kgsl_device *device, unsigned int init_ram) static int z180_stop(struct kgsl_device *device) { + device->ftbl->irqctrl(device, 0); z180_idle(device, KGSL_TIMEOUT_DEFAULT); del_timer_sync(&device->idle_timer); @@ -831,8 +822,7 @@ static int z180_wait(struct kgsl_device *device, status = 0; else if (timeout == 0) { status = -ETIMEDOUT; - device->state = KGSL_STATE_HUNG; - KGSL_PWR_WARN(device, "state -> HUNG, device %d\n", device->id); + kgsl_pwrctrl_set_state(device, KGSL_STATE_HUNG); } else status = timeout; @@ -858,17 +848,17 @@ static void z180_power_stats(struct kgsl_device *device, struct kgsl_power_stats *stats) { struct kgsl_pwrctrl *pwr = &device->pwrctrl; + s64 tmp = ktime_to_us(ktime_get()); if (pwr->time == 0) { - pwr->time = ktime_to_us(ktime_get()); + pwr->time = tmp; stats->total_time = 0; stats->busy_time = 0; } else { - s64 tmp; - tmp = ktime_to_us(ktime_get()); stats->total_time = tmp - pwr->time; - stats->busy_time = tmp - pwr->time; pwr->time = tmp; + stats->busy_time = tmp - device->on_time; + device->on_time = tmp; } } @@ -885,6 +875,16 @@ static void z180_irqctrl(struct kgsl_device *device, int state) } } +static unsigned int z180_gpuid(struct kgsl_device *device) +{ + /* Standard KGSL gpuid format: + * top word is 0x0002 for 2D or 0x0003 for 3D + * Bottom word is core specific identifer + */ + + return (0x0002 << 16) | 180; +} + static const struct kgsl_functable z180_functable = { /* Mandatory functions */ .regread = z180_regread, @@ -902,6 +902,7 @@ static const struct kgsl_functable z180_functable = { .cleanup_pt = z180_cleanup_pt, .power_stats = z180_power_stats, .irqctrl = z180_irqctrl, + .gpuid = z180_gpuid, /* Optional functions */ .drawctxt_create = NULL, .drawctxt_destroy = z180_drawctxt_destroy, diff --git a/drivers/gpu/msm/z180.h b/drivers/gpu/msm/z180.h index 28b1cc6b336ce..e5c5ef303c57d 100644 --- a/drivers/gpu/msm/z180.h +++ b/drivers/gpu/msm/z180.h @@ -19,6 +19,8 @@ #define DEVICE_2D0_NAME "kgsl-2d0" #define DEVICE_2D1_NAME "kgsl-2d1" +#define Z180_DEFAULT_PWRSCALE_POLICY NULL + struct z180_ringbuffer { unsigned int prevctx; struct kgsl_memdesc cmdbufdesc; diff --git a/drivers/gpu/msm/z180_trace.c b/drivers/gpu/msm/z180_trace.c new file mode 100644 index 0000000000000..29b519c400535 --- /dev/null +++ b/drivers/gpu/msm/z180_trace.c @@ -0,0 +1,20 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "kgsl.h" +#include "z180.h" +#include "z180_reg.h" + +/* Instantiate tracepoints */ +#define CREATE_TRACE_POINTS +#include "z180_trace.h" diff --git a/drivers/gpu/msm/z180_trace.h b/drivers/gpu/msm/z180_trace.h new file mode 100644 index 0000000000000..fbe1fe54ee219 --- /dev/null +++ b/drivers/gpu/msm/z180_trace.h @@ -0,0 +1,60 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#if !defined(_Z180_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _Z180_TRACE_H + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM kgsl +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE z180_trace + +#include + +struct kgsl_device; + +/* + * Tracepoint for z180 irq. Includes status info + */ +TRACE_EVENT(kgsl_z180_irq_status, + + TP_PROTO(struct kgsl_device *device, unsigned int status), + + TP_ARGS(device, status), + + TP_STRUCT__entry( + __string(device_name, device->name) + __field(unsigned int, status) + ), + + TP_fast_assign( + __assign_str(device_name, device->name); + __entry->status = status; + ), + + TP_printk( + "d_name=%s status=%s", + __get_str(device_name), + __entry->status ? __print_flags(__entry->status, "|", + { REG_VGC_IRQSTATUS__MH_MASK, "MH" }, + { REG_VGC_IRQSTATUS__G2D_MASK, "G2D" }, + { REG_VGC_IRQSTATUS__FIFO_MASK, "FIFO" }) : "None" + ) +); + +#endif /* _Z180_TRACE_H */ + +/* This part must be outside protection */ +#include diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index 273850ff7045b..7837bad2180ac 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -9,6 +9,7 @@ #define KGSL_CONTEXT_NO_GMEM_ALLOC 2 #define KGSL_CONTEXT_SUBMIT_IB_LIST 4 #define KGSL_CONTEXT_CTX_SWITCH 8 +#define KGSL_CONTEXT_PREAMBLE 16 /* Memory allocayion flags */ #define KGSL_MEMFLAGS_GPUREADONLY 0x01000000 @@ -25,6 +26,24 @@ #define KGSL_FLAGS_RESERVED2 0x00000080 #define KGSL_FLAGS_SOFT_RESET 0x00000100 +/* Clock flags to show which clocks should be controled by a given platform */ +#define KGSL_CLK_SRC 0x00000001 +#define KGSL_CLK_CORE 0x00000002 +#define KGSL_CLK_IFACE 0x00000004 +#define KGSL_CLK_MEM 0x00000008 +#define KGSL_CLK_MEM_IFACE 0x00000010 +#define KGSL_CLK_AXI 0x00000020 + +/* + * Reset status values for context + */ +enum kgsl_ctx_reset_stat { + KGSL_CTX_STAT_NO_ERROR = 0x00000000, + KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT = 0x00000001, + KGSL_CTX_STAT_INNOCENT_CONTEXT_RESET_EXT = 0x00000002, + KGSL_CTX_STAT_UNKNOWN_CONTEXT_RESET_EXT = 0x00000003 +}; + #define KGSL_MAX_PWRLEVELS 5 #define KGSL_CONVERT_TO_MBPS(val) \ @@ -43,6 +62,7 @@ enum kgsl_user_mem_type { KGSL_USER_MEM_TYPE_ASHMEM = 0x00000001, KGSL_USER_MEM_TYPE_ADDR = 0x00000002, KGSL_USER_MEM_TYPE_ION = 0x00000003, + KGSL_USER_MEM_TYPE_MAX = 0x00000004, }; struct kgsl_devinfo { @@ -100,6 +120,7 @@ enum kgsl_property_type { KGSL_PROP_MMU_ENABLE = 0x00000006, KGSL_PROP_INTERRUPT_WAITS = 0x00000007, KGSL_PROP_VERSION = 0x00000008, + KGSL_PROP_GPU_RESET_STAT = 0x00000009 }; struct kgsl_shadowprop { @@ -130,30 +151,17 @@ struct kgsl_version { #define KGSL_2D1_REG_MEMORY "kgsl_2d1_reg_memory" #define KGSL_2D1_IRQ "kgsl_2d1_irq" -struct kgsl_grp_clk_name { - const char *clk; - const char *pclk; -}; - -struct kgsl_device_pwr_data { +struct kgsl_device_platform_data { struct kgsl_pwrlevel pwrlevel[KGSL_MAX_PWRLEVELS]; int init_level; int num_levels; int (*set_grp_async)(void); unsigned int idle_timeout; + bool strtstp_sleepwake; unsigned int nap_allowed; -}; - -struct kgsl_clk_data { - struct kgsl_grp_clk_name name; + unsigned int clk_map; + unsigned int idle_needed; struct msm_bus_scale_pdata *bus_scale_table; -}; - -struct kgsl_device_platform_data { - struct kgsl_device_pwr_data pwr_data; - struct kgsl_clk_data clk; - /* imem_clk_name is for 3d only, not used in 2d devices */ - struct kgsl_grp_clk_name imem_clk_name; const char *iommu_user_ctx_name; const char *iommu_priv_ctx_name; }; From 01ad8e1742a3240c7270487f9e9c2b59f6054550 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 24 May 2012 12:24:57 -0500 Subject: [PATCH 2266/2556] msm: boards: update kgsl_3d0_pdata --- arch/arm/mach-msm/board-bravo.c | 27 +++++++++------------------ arch/arm/mach-msm/board-incrediblec.c | 27 +++++++++------------------ arch/arm/mach-msm/board-mahimahi.c | 27 +++++++++------------------ arch/arm/mach-msm/board-supersonic.c | 27 +++++++++------------------ 4 files changed, 36 insertions(+), 72 deletions(-) diff --git a/arch/arm/mach-msm/board-bravo.c b/arch/arm/mach-msm/board-bravo.c index 0ddfd5146780f..1881966d72c06 100644 --- a/arch/arm/mach-msm/board-bravo.c +++ b/arch/arm/mach-msm/board-bravo.c @@ -302,26 +302,17 @@ static struct resource kgsl_3d0_resources[] = { }; static struct kgsl_device_platform_data kgsl_3d0_pdata = { - .pwr_data = { - .pwrlevel = { - { - .gpu_freq = 0, - .bus_freq = 128000000, - }, + .pwrlevel = { + { + .gpu_freq = 0, + .bus_freq = 128000000, }, - .init_level = 0, - .num_levels = 1, - .set_grp_async = NULL, - .idle_timeout = HZ/5, - }, - .clk = { - .name = { - .clk = "grp_clk", - }, - }, - .imem_clk_name = { - .clk = "imem_clk", }, + .init_level = 0, + .num_levels = 1, + .set_grp_async = NULL, + .idle_timeout = HZ/5, + .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM, }; struct platform_device msm_kgsl_3d0 = { diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index b30039d7d4610..9ebe391ef39e1 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -506,26 +506,17 @@ static struct resource kgsl_3d0_resources[] = { }; static struct kgsl_device_platform_data kgsl_3d0_pdata = { - .pwr_data = { - .pwrlevel = { - { - .gpu_freq = 0, - .bus_freq = 128000000, - }, + .pwrlevel = { + { + .gpu_freq = 0, + .bus_freq = 128000000, }, - .init_level = 0, - .num_levels = 1, - .set_grp_async = NULL, - .idle_timeout = HZ/5, - }, - .clk = { - .name = { - .clk = "grp_clk", - }, - }, - .imem_clk_name = { - .clk = "imem_clk", }, + .init_level = 0, + .num_levels = 1, + .set_grp_async = NULL, + .idle_timeout = HZ/5, + .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM, }; struct platform_device msm_kgsl_3d0 = { diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index c290faf9dc8fe..a600ac33dceff 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -294,26 +294,17 @@ static struct resource kgsl_3d0_resources[] = { }; static struct kgsl_device_platform_data kgsl_3d0_pdata = { - .pwr_data = { - .pwrlevel = { - { - .gpu_freq = 0, - .bus_freq = 128000000, - }, + .pwrlevel = { + { + .gpu_freq = 0, + .bus_freq = 128000000, }, - .init_level = 0, - .num_levels = 1, - .set_grp_async = NULL, - .idle_timeout = HZ/5, - }, - .clk = { - .name = { - .clk = "grp_clk", - }, - }, - .imem_clk_name = { - .clk = "imem_clk", }, + .init_level = 0, + .num_levels = 1, + .set_grp_async = NULL, + .idle_timeout = HZ/5, + .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM, }; struct platform_device msm_kgsl_3d0 = { diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index 8a02380955d58..01e2d3a1745b5 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -539,26 +539,17 @@ static struct resource kgsl_3d0_resources[] = { }; static struct kgsl_device_platform_data kgsl_3d0_pdata = { - .pwr_data = { - .pwrlevel = { - { - .gpu_freq = 0, - .bus_freq = 128000000, - }, - }, - .init_level = 0, - .num_levels = 1, - .set_grp_async = NULL, - .idle_timeout = HZ/5, - }, - .clk = { - .name = { - .clk = "grp_clk", + .pwrlevel = { + { + .gpu_freq = 0, + .bus_freq = 128000000, }, }, - .imem_clk_name = { - .clk = "imem_clk", - }, + .init_level = 0, + .num_levels = 1, + .set_grp_async = NULL, + .idle_timeout = HZ/5, + .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM, }; struct platform_device msm_kgsl_3d0 = { From 59d0da22c1ced48eb68c5d7d0ffec6ee8638d243 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 24 May 2012 14:51:35 -0500 Subject: [PATCH 2267/2556] kgsl: support grp/imem clk for qsd8k dirty hack to initialize the kgsl-3d0 device caf changes the clock names to be uniform across platforms but we are still using googles clock, this way is easier until i feel like doing it proper. --- arch/arm/mach-msm/board-bravo.c | 2 +- arch/arm/mach-msm/board-incrediblec.c | 2 +- arch/arm/mach-msm/board-mahimahi.c | 2 +- arch/arm/mach-msm/board-supersonic.c | 2 +- drivers/gpu/msm/kgsl_pwrctrl.c | 8 ++++++++ drivers/gpu/msm/kgsl_pwrctrl.h | 2 +- include/linux/msm_kgsl.h | 4 ++++ 7 files changed, 17 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-msm/board-bravo.c b/arch/arm/mach-msm/board-bravo.c index 1881966d72c06..0515eaf321cca 100644 --- a/arch/arm/mach-msm/board-bravo.c +++ b/arch/arm/mach-msm/board-bravo.c @@ -312,7 +312,7 @@ static struct kgsl_device_platform_data kgsl_3d0_pdata = { .num_levels = 1, .set_grp_async = NULL, .idle_timeout = HZ/5, - .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM, + .clk_map = KGSL_CLK_GRP | KGSL_CLK_IMEM, }; struct platform_device msm_kgsl_3d0 = { diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index 9ebe391ef39e1..64d27d34ead51 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -516,7 +516,7 @@ static struct kgsl_device_platform_data kgsl_3d0_pdata = { .num_levels = 1, .set_grp_async = NULL, .idle_timeout = HZ/5, - .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM, + .clk_map = KGSL_CLK_GRP | KGSL_CLK_IMEM, }; struct platform_device msm_kgsl_3d0 = { diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index a600ac33dceff..9825b4e6297a8 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -304,7 +304,7 @@ static struct kgsl_device_platform_data kgsl_3d0_pdata = { .num_levels = 1, .set_grp_async = NULL, .idle_timeout = HZ/5, - .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM, + .clk_map = KGSL_CLK_GRP | KGSL_CLK_IMEM, }; struct platform_device msm_kgsl_3d0 = { diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index 01e2d3a1745b5..7b7ecb769bfe0 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -549,7 +549,7 @@ static struct kgsl_device_platform_data kgsl_3d0_pdata = { .num_levels = 1, .set_grp_async = NULL, .idle_timeout = HZ/5, - .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM, + .clk_map = KGSL_CLK_GRP | KGSL_CLK_IMEM, }; struct platform_device msm_kgsl_3d0 = { diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 08b8c8cd62530..f3457ce6e0895 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -54,6 +54,14 @@ struct clk_pair clks[KGSL_MAX_CLKS] = { .name = "mem_iface_clk", .map = KGSL_CLK_MEM_IFACE, }, + { + .name = "grp_clk", + .map = KGSL_CLK_GRP, + }, + { + .name = "imem_clk", + .map = KGSL_CLK_IMEM, + }, }; void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index caaed92c840b1..76457228a1aff 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -22,7 +22,7 @@ #define KGSL_PWRLEVEL_TURBO 0 #define KGSL_PWRLEVEL_NOMINAL 1 -#define KGSL_MAX_CLKS 5 +#define KGSL_MAX_CLKS 7 struct platform_device; diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index 7837bad2180ac..ef72609f1712b 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -34,6 +34,10 @@ #define KGSL_CLK_MEM_IFACE 0x00000010 #define KGSL_CLK_AXI 0x00000020 +/* Arbitrary defines for 8x50 devices using google clock naming / implementation */ +#define KGSL_CLK_GRP 0x00001000 +#define KGSL_CLK_IMEM 0x00010000 + /* * Reset status values for context */ From da40f5234f3cb87ca222069476a0a606f9d81013 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 24 May 2012 21:37:06 -0500 Subject: [PATCH 2268/2556] msm: boards: set ALLORNOTHING allocator for mdp heap --- arch/arm/mach-msm/board-bravo.c | 2 +- arch/arm/mach-msm/board-incrediblec.c | 2 +- arch/arm/mach-msm/board-mahimahi.c | 2 +- arch/arm/mach-msm/board-supersonic.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-msm/board-bravo.c b/arch/arm/mach-msm/board-bravo.c index 0515eaf321cca..e268b400e1445 100644 --- a/arch/arm/mach-msm/board-bravo.c +++ b/arch/arm/mach-msm/board-bravo.c @@ -339,7 +339,7 @@ static struct android_pmem_platform_data mdp_pmem_pdata = { .start = MSM_PMEM_MDP_BASE, .size = MSM_PMEM_MDP_SIZE, /* .no_allocator = 0,*/ - .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .allocator_type = PMEM_ALLOCATORTYPE_ALLORNOTHING, .cached = 1, }; diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index 64d27d34ead51..43e1078d24921 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -543,7 +543,7 @@ static struct android_pmem_platform_data mdp_pmem_pdata = { .start = MSM_PMEM_MDP_BASE, .size = MSM_PMEM_MDP_SIZE, /* .no_allocator = 0,*/ - .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .allocator_type = PMEM_ALLOCATORTYPE_ALLORNOTHING, .cached = 1, }; diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index 9825b4e6297a8..b98a27b747d85 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -331,7 +331,7 @@ static struct android_pmem_platform_data mdp_pmem_pdata = { .start = MSM_PMEM_MDP_BASE, .size = MSM_PMEM_MDP_SIZE, /* .no_allocator = 0,*/ - .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .allocator_type = PMEM_ALLOCATORTYPE_ALLORNOTHING, .cached = 1, }; diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index 7b7ecb769bfe0..b4d58aa30b405 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -576,7 +576,7 @@ static struct android_pmem_platform_data mdp_pmem_pdata = { .start = MSM_PMEM_MDP_BASE, .size = MSM_PMEM_MDP_SIZE, /* .no_allocator = 0,*/ - .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, + .allocator_type = PMEM_ALLOCATORTYPE_ALLORNOTHING, .cached = 1, }; From 7c22af9f2875db8cdc663ef9f347f23e197db3ba Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 27 May 2012 00:38:23 -0500 Subject: [PATCH 2269/2556] kgsl: fix building incrediblec drivers/built-in.o: In function `adreno_setstate': /home/drew/android/kernels/cayniarb-8x50/drivers/gpu/msm/adreno.c:360: undefined reference to `kgsl_cff_dump_enable' make: *** [vmlinux] Error 1 fucking whack shit but i dont care about cffdump anyway. --- drivers/gpu/msm/adreno.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 0cb8d2b7c31da..f52543dc15b6e 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -279,7 +279,7 @@ static void adreno_setstate(struct kgsl_device *device, * writes For CFF dump we must idle and use the registers so that it is * easier to filter out the mmu accesses from the dump */ - if (!kgsl_cff_dump_enable && adreno_dev->drawctxt_active) { + if (adreno_dev->drawctxt_active) { if (flags & KGSL_MMUFLAGS_PTUPDATE) { /* wait for graphics pipe to be idle */ *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); From 30b88872f7a75d358ae1389d36d5dd18cce63a8b Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 17 Jun 2012 14:43:54 -0500 Subject: [PATCH 2270/2556] arm: allow building with gcc-4.7 boot/compressed changes taken from cm's tuna kernel --- arch/arm/boot/compressed/Makefile | 24 ++++++++++++++++++------ drivers/net/wireless/bcm4329/Makefile | 1 + 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index 2bf7a65518b6a..2d9ab571eb0dd 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -4,9 +4,18 @@ # create a compressed vmlinuz image from the original vmlinux # -AFLAGS_head.o += -DTEXT_OFFSET=$(TEXT_OFFSET) -HEAD = head.o -OBJS = misc.o decompress.o +plus_sec := $(call as-instr,.arch_extension sec,+sec) + +AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET) +AFLAGS_head.o += -Wa,-march=armv7-a$(plus_sec) +HEAD = head.o + +AFLAGS_misc.o += -Wa,-march=armv7-a$(plus_sec) +MISC = misc.o + +AFLAGS_decompress.o += -Wa,-march=armv7-a$(plus_sec) +DECOMPRESS = decompress.o + FONTC = $(srctree)/drivers/video/console/font_acorn_8x8.c # @@ -71,7 +80,7 @@ suffix_$(CONFIG_KERNEL_XZ) = xzkern targets := vmlinux vmlinux.lds \ piggy.$(suffix_y) piggy.$(suffix_y).o \ - font.o font.c head.o misc.o $(OBJS) + font.o font.c head.o misc.o decompress.o $(OBJS) # Make sure files are removed during clean extra-y += piggy.gzip piggy.lzo piggy.lzma piggy.xzkern lib1funcs.S ashldi3.S @@ -81,7 +90,7 @@ ORIG_CFLAGS := $(KBUILD_CFLAGS) KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS)) endif -EXTRA_CFLAGS := -fpic -fno-builtin +EXTRA_CFLAGS := -fpic -fno-builtin -Wno-error=coverage-mismatch EXTRA_AFLAGS := -Wa,-march=all # Supply ZRELADDR to the decompressor via a linker symbol. @@ -101,18 +110,20 @@ LDFLAGS_vmlinux += -X LDFLAGS_vmlinux += -T # For __aeabi_uidivmod +AFLAGS_lib1funcs.o += -Wa,-march=armv7-a$(plus_sec) lib1funcs = $(obj)/lib1funcs.o $(obj)/lib1funcs.S: $(srctree)/arch/$(SRCARCH)/lib/lib1funcs.S FORCE $(call cmd,shipped) # For __aeabi_llsl +AFLAGS_ashldi3.o += -Wa,-march=armv7-a$(plus_sec) ashldi3 = $(obj)/ashldi3.o $(obj)/ashldi3.S: $(srctree)/arch/$(SRCARCH)/lib/ashldi3.S FORCE $(call cmd,shipped) -$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.$(suffix_y).o \ +$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/$(MISC) $(obj)/$(DECOMPRESS) $(obj)/piggy.$(suffix_y).o \ $(addprefix $(obj)/, $(OBJS)) $(lib1funcs) $(ashldi3) FORCE $(call if_changed,ld) @: @@ -120,6 +131,7 @@ $(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.$(suffix_y).o \ $(obj)/piggy.$(suffix_y): $(obj)/../Image FORCE $(call if_changed,$(suffix_y)) +AFLAGS_piggy.$(suffix_y).o += -Wa,-march=armv7-a$(plus_sec) $(obj)/piggy.$(suffix_y).o: $(obj)/piggy.$(suffix_y) FORCE CFLAGS_font.o := -Dstatic= diff --git a/drivers/net/wireless/bcm4329/Makefile b/drivers/net/wireless/bcm4329/Makefile index 5a662be7fc532..02634bdc1fabd 100644 --- a/drivers/net/wireless/bcm4329/Makefile +++ b/drivers/net/wireless/bcm4329/Makefile @@ -8,6 +8,7 @@ DHDCFLAGS = -DLINUX -DBCMDRIVER -DBCMDONGLEHOST -DDHDTHREAD -DBCMWPA2 \ -DEMBEDDED_PLATFORM -DARP_OFFLOAD_SUPPORT -DPKT_FILTER_SUPPORT \ -DGET_CUSTOM_MAC_ENABLE -DSET_RANDOM_MAC_SOFTAP -DCSCAN -DHW_OOB \ -DKEEP_ALIVE -DPNO_SUPPORT \ + -Wno-error=unused-but-set-variable -Wno-error=array-bounds \ -Idrivers/net/wireless/bcm4329 -Idrivers/net/wireless/bcm4329/include DHDOFILES = dhd_linux.o linux_osl.o bcmutils.o dhd_common.o dhd_custom_gpio.o \ From 21754725c7c42d4cab6970802562e1f69d1f3adf Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Mon, 7 Nov 2011 18:05:53 +0100 Subject: [PATCH 2271/2556] ARM: 7150/1: Allow kernel unaligned accesses on ARMv6+ processors Recent gcc versions generate unaligned accesses by default on ARMv6 and later processors. This patch ensures that the SCTLR.A bit is always cleared on such processors to avoid kernel traping before alignment_init() is called. Signed-off-by: Catalin Marinas Tested-by: John Linn Acked-by: Nicolas Pitre Cc: stable@vger.kernel.org Signed-off-by: Russell King --- arch/arm/kernel/head.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index b9f689a340708..67a243e7da9fa 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S @@ -342,7 +342,7 @@ __secondary_data: * r13 = *virtual* address to jump to upon completion */ __enable_mmu: -#ifdef CONFIG_ALIGNMENT_TRAP +#if defined(CONFIG_ALIGNMENT_TRAP) && __LINUX_ARM_ARCH__ < 6 orr r0, r0, #CR_A #else bic r0, r0, #CR_A From 42c664f92be0ca340a6f459ee3435dbc20793666 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 20 Jun 2012 19:17:27 -0500 Subject: [PATCH 2272/2556] bcm4329: unbreak gcc 4.4.3 --- drivers/net/wireless/bcm4329/Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/bcm4329/Makefile b/drivers/net/wireless/bcm4329/Makefile index 02634bdc1fabd..0c84dd57d3642 100644 --- a/drivers/net/wireless/bcm4329/Makefile +++ b/drivers/net/wireless/bcm4329/Makefile @@ -3,12 +3,11 @@ DHDCFLAGS = -DLINUX -DBCMDRIVER -DBCMDONGLEHOST -DDHDTHREAD -DBCMWPA2 \ -DUNRELEASEDCHIP -Dlinux -DDHD_SDALIGN=64 -DMAX_HDR_READ=64 \ -DDHD_FIRSTREAD=64 -DDHD_GPL -DDHD_SCHED -DBDC -DTOE -DDHD_BCMEVENTS \ -DSHOW_EVENTS -DBCMSDIO -DDHD_GPL -DBCMLXSDMMC -DBCMPLATFORM_BUS \ - -Wall -Wstrict-prototypes -Werror -DOOB_INTR_ONLY -DCUSTOMER_HW2 \ + -Wall -Wstrict-prototypes -DOOB_INTR_ONLY -DCUSTOMER_HW2 \ -DDHD_USE_STATIC_BUF -DMMC_SDIO_ABORT -DDHD_DEBUG_TRAP -DSOFTAP \ -DEMBEDDED_PLATFORM -DARP_OFFLOAD_SUPPORT -DPKT_FILTER_SUPPORT \ -DGET_CUSTOM_MAC_ENABLE -DSET_RANDOM_MAC_SOFTAP -DCSCAN -DHW_OOB \ -DKEEP_ALIVE -DPNO_SUPPORT \ - -Wno-error=unused-but-set-variable -Wno-error=array-bounds \ -Idrivers/net/wireless/bcm4329 -Idrivers/net/wireless/bcm4329/include DHDOFILES = dhd_linux.o linux_osl.o bcmutils.o dhd_common.o dhd_custom_gpio.o \ From 63b989d8c7108f4b17583ed861f8bdba23ccbe85 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 1 Mar 2011 09:35:29 +0100 Subject: [PATCH 2273/2556] kbuild: Add extra gcc checks Add a 'W=1' Makefile switch which adds additional checking per build object. The idea behind this option is targeted at developers who, in the process of writing their code, want to do the occasional make W=1 [target.o] and let gcc do more extensive code checking for them. Then, they could eyeball the output for valid gcc warnings about various bugs/discrepancies which are not reported during the normal build process. For more background information and a use case, read through this thread: http://marc.info/?l=kernel-janitors&m=129802065918147&w=2 Cc: Michal Marek Cc: linux-kbuild@vger.kernel.org Acked-by: Sam Ravnborg Acked-by: Ingo Molnar Signed-off-by: Borislav Petkov Signed-off-by: Michal Marek --- Documentation/kbuild/kbuild.txt | 5 +++++ Makefile | 5 +++++ scripts/Makefile.build | 35 ++++++++++++++++++++++++++++++++- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/Documentation/kbuild/kbuild.txt b/Documentation/kbuild/kbuild.txt index 4a990317b84a7..376538c984cef 100644 --- a/Documentation/kbuild/kbuild.txt +++ b/Documentation/kbuild/kbuild.txt @@ -196,3 +196,8 @@ to be included in the databases, separated by blank space. E.g.: To get all available archs you can also specify all. E.g.: $ make ALLSOURCE_ARCHS=all tags + +KBUILD_ENABLE_EXTRA_GCC_CHECKS +-------------------------------------------------- +If enabled over the make command line with "W=1", it turns on additional +gcc -W... options for more extensive build-time checking. diff --git a/Makefile b/Makefile index 719bedc63e298..de03aeec4679f 100644 --- a/Makefile +++ b/Makefile @@ -102,6 +102,10 @@ ifeq ("$(origin O)", "command line") KBUILD_OUTPUT := $(O) endif +ifeq ("$(origin W)", "command line") + export KBUILD_ENABLE_EXTRA_GCC_CHECKS := 1 +endif + # That's our default target when none is given on the command line PHONY := _all _all: @@ -1264,6 +1268,7 @@ help: @echo ' make O=dir [targets] Locate all output files in "dir", including .config' @echo ' make C=1 [targets] Check all c source with $$CHECK (sparse by default)' @echo ' make C=2 [targets] Force check of all c source with $$CHECK' + @echo ' make W=1 [targets] Enable extra gcc checks' @echo '' @echo 'Execute "make" or "make all" to build all targets marked with [*] ' @echo 'For further info see the ./README file' diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 4eb99ab340537..d5f925abe4d29 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -49,6 +49,40 @@ ifeq ($(KBUILD_NOPEDANTIC),) $(error CFLAGS was changed in "$(kbuild-file)". Fix it to use EXTRA_CFLAGS) endif endif + +# +# make W=1 settings +# +# $(call cc-option... ) handles gcc -W.. options which +# are not supported by all versions of the compiler +ifdef KBUILD_ENABLE_EXTRA_GCC_CHECKS +KBUILD_EXTRA_WARNINGS := -Wextra +KBUILD_EXTRA_WARNINGS += -Wunused -Wno-unused-parameter +KBUILD_EXTRA_WARNINGS += -Waggregate-return +KBUILD_EXTRA_WARNINGS += -Wbad-function-cast +KBUILD_EXTRA_WARNINGS += -Wcast-qual +KBUILD_EXTRA_WARNINGS += -Wcast-align +KBUILD_EXTRA_WARNINGS += -Wconversion +KBUILD_EXTRA_WARNINGS += -Wdisabled-optimization +KBUILD_EXTRA_WARNINGS += -Wlogical-op +KBUILD_EXTRA_WARNINGS += -Wmissing-declarations +KBUILD_EXTRA_WARNINGS += -Wmissing-format-attribute +KBUILD_EXTRA_WARNINGS += $(call cc-option, -Wmissing-include-dirs,) +KBUILD_EXTRA_WARNINGS += -Wmissing-prototypes +KBUILD_EXTRA_WARNINGS += -Wnested-externs +KBUILD_EXTRA_WARNINGS += -Wold-style-definition +KBUILD_EXTRA_WARNINGS += $(call cc-option, -Woverlength-strings,) +KBUILD_EXTRA_WARNINGS += -Wpacked +KBUILD_EXTRA_WARNINGS += -Wpacked-bitfield-compat +KBUILD_EXTRA_WARNINGS += -Wpadded +KBUILD_EXTRA_WARNINGS += -Wpointer-arith +KBUILD_EXTRA_WARNINGS += -Wredundant-decls +KBUILD_EXTRA_WARNINGS += -Wshadow +KBUILD_EXTRA_WARNINGS += -Wswitch-default +KBUILD_EXTRA_WARNINGS += $(call cc-option, -Wvla,) +KBUILD_CFLAGS += $(KBUILD_EXTRA_WARNINGS) +endif + include scripts/Makefile.lib ifdef host-progs @@ -403,7 +437,6 @@ ifneq ($(cmd_files),) include $(cmd_files) endif - # Declare the contents of the .PHONY variable as phony. We keep that # information in a variable se we can use it in if_changed and friends. From 7488d3187a857229514453a4f644af5931c386c5 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Wed, 27 Apr 2011 22:15:27 +0200 Subject: [PATCH 2274/2556] kbuild: implement several W= levels Building a kernel with "make W=1" produces far too much noise to be useful. Divide the warning options in three groups: W=1 - warnings that may be relevant and does not occur too often W=2 - warnings that occur quite often but may still be relevant W=3 - the more obscure warnings, can most likely be ignored When building the whole kernel, those levels produce: W=1 - 4859 warnings W=2 - 1394 warnings W=3 - 86666 warnings respectively. Warnings have been counted with Geert's script at http://www.kernel.org/pub/linux/kernel/people/geert/linux-log/linux-log-summary.pl Many warnings occur from .h files so fixing one file may have a nice effect on the total number of warnings. With these changes I am actually tempted to try W=1 now and then. Previously there was just too much noise. Borislav: - make the W= levels exclusive - move very noisy and making little sense for the kernel warnings to W=3 - drop -Woverlength-strings due to useless warning message - copy explanatory text for the different warning levels to 'make help' - recount warnings per level Signed-off-by: Sam Ravnborg Signed-off-by: Borislav Petkov Cc: Dave Jones Cc: Geert Uytterhoeven Signed-off-by: Michal Marek --- Makefile | 8 ++++-- scripts/Makefile.build | 65 ++++++++++++++++++++++++------------------ 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/Makefile b/Makefile index de03aeec4679f..7e7407b5e6416 100644 --- a/Makefile +++ b/Makefile @@ -103,7 +103,7 @@ ifeq ("$(origin O)", "command line") endif ifeq ("$(origin W)", "command line") - export KBUILD_ENABLE_EXTRA_GCC_CHECKS := 1 + export KBUILD_ENABLE_EXTRA_GCC_CHECKS := $(W) endif # That's our default target when none is given on the command line @@ -1268,7 +1268,11 @@ help: @echo ' make O=dir [targets] Locate all output files in "dir", including .config' @echo ' make C=1 [targets] Check all c source with $$CHECK (sparse by default)' @echo ' make C=2 [targets] Force check of all c source with $$CHECK' - @echo ' make W=1 [targets] Enable extra gcc checks' + @echo ' make W=n [targets] Enable extra gcc checks, n=1,2,3 where' + @echo ' 1: warnings which may be relevant and do not occur too often' + @echo ' 2: warnings which occur quite often but may still be relevant' + @echo ' 3: more obscure warnings, can most likely be ignored' + @echo '' @echo 'Execute "make" or "make all" to build all targets marked with [*] ' @echo 'For further info see the ./README file' diff --git a/scripts/Makefile.build b/scripts/Makefile.build index d5f925abe4d29..ffb383cddba50 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -51,36 +51,47 @@ ifeq ($(KBUILD_NOPEDANTIC),) endif # -# make W=1 settings +# make W=... settings # -# $(call cc-option... ) handles gcc -W.. options which +# W=1 - warnings that may be relevant and does not occur too often +# W=2 - warnings that occur quite often but may still be relevant +# W=3 - the more obscure warnings, can most likely be ignored +# +# $(call cc-option, -W...) handles gcc -W.. options which # are not supported by all versions of the compiler ifdef KBUILD_ENABLE_EXTRA_GCC_CHECKS -KBUILD_EXTRA_WARNINGS := -Wextra -KBUILD_EXTRA_WARNINGS += -Wunused -Wno-unused-parameter -KBUILD_EXTRA_WARNINGS += -Waggregate-return -KBUILD_EXTRA_WARNINGS += -Wbad-function-cast -KBUILD_EXTRA_WARNINGS += -Wcast-qual -KBUILD_EXTRA_WARNINGS += -Wcast-align -KBUILD_EXTRA_WARNINGS += -Wconversion -KBUILD_EXTRA_WARNINGS += -Wdisabled-optimization -KBUILD_EXTRA_WARNINGS += -Wlogical-op -KBUILD_EXTRA_WARNINGS += -Wmissing-declarations -KBUILD_EXTRA_WARNINGS += -Wmissing-format-attribute -KBUILD_EXTRA_WARNINGS += $(call cc-option, -Wmissing-include-dirs,) -KBUILD_EXTRA_WARNINGS += -Wmissing-prototypes -KBUILD_EXTRA_WARNINGS += -Wnested-externs -KBUILD_EXTRA_WARNINGS += -Wold-style-definition -KBUILD_EXTRA_WARNINGS += $(call cc-option, -Woverlength-strings,) -KBUILD_EXTRA_WARNINGS += -Wpacked -KBUILD_EXTRA_WARNINGS += -Wpacked-bitfield-compat -KBUILD_EXTRA_WARNINGS += -Wpadded -KBUILD_EXTRA_WARNINGS += -Wpointer-arith -KBUILD_EXTRA_WARNINGS += -Wredundant-decls -KBUILD_EXTRA_WARNINGS += -Wshadow -KBUILD_EXTRA_WARNINGS += -Wswitch-default -KBUILD_EXTRA_WARNINGS += $(call cc-option, -Wvla,) -KBUILD_CFLAGS += $(KBUILD_EXTRA_WARNINGS) +warning-1 := -Wextra -Wunused -Wno-unused-parameter +warning-1 += -Wmissing-declarations +warning-1 += -Wmissing-format-attribute +warning-1 += -Wmissing-prototypes +warning-1 += -Wold-style-definition +warning-1 += $(call cc-option, -Wmissing-include-dirs) + +warning-2 := -Waggregate-return +warning-2 += -Wcast-align +warning-2 += -Wdisabled-optimization +warning-2 += -Wnested-externs +warning-2 += -Wshadow +warning-2 += $(call cc-option, -Wlogical-op) + +warning-3 := -Wbad-function-cast +warning-3 += -Wcast-qual +warning-3 += -Wconversion +warning-3 += -Wpacked +warning-3 += -Wpadded +warning-3 += -Wpointer-arith +warning-3 += -Wredundant-decls +warning-3 += -Wswitch-default +warning-3 += $(call cc-option, -Wpacked-bitfield-compat) +warning-3 += $(call cc-option, -Wvla) + +warning := $(warning-$(KBUILD_ENABLE_EXTRA_GCC_CHECKS)) + +ifeq ("$(warning)","") + $(error W=$(KBUILD_ENABLE_EXTRA_GCC_CHECKS) is unknown) +endif + +KBUILD_CFLAGS += $(warning) endif include scripts/Makefile.lib From 1c46fdf01b089d53dd56280ce01e523318ea8c13 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Thu, 21 Apr 2011 17:28:13 -0400 Subject: [PATCH 2275/2556] kbuild: Disable -Wunused-but-set-variable for gcc 4.6.0 Disable the new -Wunused-but-set-variable that was added in gcc 4.6.0 It produces more false positives than useful warnings. This can still be enabled using W=1 Signed-off-by: Dave Jones Acked-by: Sam Ravnborg Tested-by: Sam Ravnborg Signed-off-by: Michal Marek --- Makefile | 4 ++++ scripts/Makefile.build | 1 + 2 files changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 7e7407b5e6416..10798d8dec270 100644 --- a/Makefile +++ b/Makefile @@ -561,6 +561,10 @@ ifndef CONFIG_CC_STACKPROTECTOR KBUILD_CFLAGS += $(call cc-option, -fno-stack-protector) endif +# This warning generated too much noise in a regular build. +# Use make W=1 to enable this warning (see scripts/Makefile.build) +KBUILD_CFLAGS += $(call cc-option, -Wno-unused-but-set-variable) + ifdef CONFIG_FRAME_POINTER KBUILD_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls else diff --git a/scripts/Makefile.build b/scripts/Makefile.build index ffb383cddba50..9438275d19d5b 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -66,6 +66,7 @@ warning-1 += -Wmissing-format-attribute warning-1 += -Wmissing-prototypes warning-1 += -Wold-style-definition warning-1 += $(call cc-option, -Wmissing-include-dirs) +warning-1 += $(call cc-option, -Wunused-but-set-variable) warning-2 := -Waggregate-return warning-2 += -Wcast-align From 2231c55710a44fc25951cb6d05c53e68402d2bea Mon Sep 17 00:00:00 2001 From: Michal Marek Date: Fri, 29 Apr 2011 14:45:31 +0200 Subject: [PATCH 2276/2556] kbuild: Allow to combine multiple W= levels Add support for make W=12, make W=123 and so on, to enable warnings from multiple W= levels. Normally, make W= does not include warnings from the previous level. Signed-off-by: Michal Marek Acked-by: Sam Ravnborg Reviewed-By: Valdis Kletnieks --- Makefile | 2 +- scripts/Makefile.build | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 10798d8dec270..f8f759f07d54b 100644 --- a/Makefile +++ b/Makefile @@ -1276,7 +1276,7 @@ help: @echo ' 1: warnings which may be relevant and do not occur too often' @echo ' 2: warnings which occur quite often but may still be relevant' @echo ' 3: more obscure warnings, can most likely be ignored' - + @echo ' Multiple levels can be combined with W=12 or W=123' @echo '' @echo 'Execute "make" or "make all" to build all targets marked with [*] ' @echo 'For further info see the ./README file' diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 9438275d19d5b..d4ba0734822c9 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -60,6 +60,8 @@ endif # $(call cc-option, -W...) handles gcc -W.. options which # are not supported by all versions of the compiler ifdef KBUILD_ENABLE_EXTRA_GCC_CHECKS +warning- := $(empty) + warning-1 := -Wextra -Wunused -Wno-unused-parameter warning-1 += -Wmissing-declarations warning-1 += -Wmissing-format-attribute @@ -86,9 +88,11 @@ warning-3 += -Wswitch-default warning-3 += $(call cc-option, -Wpacked-bitfield-compat) warning-3 += $(call cc-option, -Wvla) -warning := $(warning-$(KBUILD_ENABLE_EXTRA_GCC_CHECKS)) +warning := $(warning-$(findstring 1, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS))) +warning += $(warning-$(findstring 2, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS))) +warning += $(warning-$(findstring 3, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS))) -ifeq ("$(warning)","") +ifeq ("$(strip $(warning))","") $(error W=$(KBUILD_ENABLE_EXTRA_GCC_CHECKS) is unknown) endif From 33f0ecf05fc759a09f31866d7066ea13bbce27d2 Mon Sep 17 00:00:00 2001 From: Michal Marek Date: Mon, 2 May 2011 12:51:15 +0200 Subject: [PATCH 2277/2556] kbuild: Fix passing -Wno-* options to gcc 4.4+ Starting with 4.4, gcc will happily accept -Wno- in the cc-option test and complain later when compiling a file that has some other warning. This rather unexpected behavior is intentional as per http://gcc.gnu.org/PR28322, so work around it by testing for support of the opposite option (without the no-). Introduce a new Makefile function cc-disable-warning that does this and update two uses of cc-option in the toplevel Makefile. Reported-and-tested-by: Stephen Rothwell Signed-off-by: Michal Marek --- Documentation/kbuild/makefiles.txt | 12 ++++++++++++ Makefile | 4 ++-- scripts/Kbuild.include | 5 +++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Documentation/kbuild/makefiles.txt b/Documentation/kbuild/makefiles.txt index 86e3cd0d26a08..d0f4d1d9717b7 100644 --- a/Documentation/kbuild/makefiles.txt +++ b/Documentation/kbuild/makefiles.txt @@ -499,6 +499,18 @@ more details, with real examples. gcc >= 3.00. For gcc < 3.00, -malign-functions=4 is used. Note: cc-option-align uses KBUILD_CFLAGS for $(CC) options + cc-disable-warning + cc-disable-warning checks if gcc supports a given warning and returns + the commandline switch to disable it. This special function is needed, + because gcc 4.4 and later accept any unknown -Wno-* option and only + warn about it if there is another warning in the source file. + + Example: + KBUILD_CFLAGS += $(call cc-disable-warning, unused-but-set-variable) + + In the above example, -Wno-unused-but-set-variable will be added to + KBUILD_CFLAGS only if gcc really accepts it. + cc-version cc-version returns a numerical version of the $(CC) compiler version. The format is where both are two digits. So for example diff --git a/Makefile b/Makefile index f8f759f07d54b..3792a7921faea 100644 --- a/Makefile +++ b/Makefile @@ -563,7 +563,7 @@ endif # This warning generated too much noise in a regular build. # Use make W=1 to enable this warning (see scripts/Makefile.build) -KBUILD_CFLAGS += $(call cc-option, -Wno-unused-but-set-variable) +KBUILD_CFLAGS += $(call cc-disable-warning, unused-but-set-variable) ifdef CONFIG_FRAME_POINTER KBUILD_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls @@ -610,7 +610,7 @@ CHECKFLAGS += $(NOSTDINC_FLAGS) KBUILD_CFLAGS += $(call cc-option,-Wdeclaration-after-statement,) # disable pointer signed / unsigned warnings in gcc 4.0 -KBUILD_CFLAGS += $(call cc-option,-Wno-pointer-sign,) +KBUILD_CFLAGS += $(call cc-disable-warning, pointer-sign) # disable invalid "can't wrap" optimizations for signed / pointers KBUILD_CFLAGS += $(call cc-option,-fno-strict-overflow) diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index ed2773edfe71b..ba25c440f9f6a 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -118,6 +118,11 @@ cc-option-yn = $(call try-run,\ cc-option-align = $(subst -functions=0,,\ $(call cc-option,-falign-functions=0,-malign-functions=0)) +# cc-disable-warning +# Usage: cflags-y += $(call cc-disable-warning,unused-but-set-variable) +cc-disable-warning = $(call try-run,\ + $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -W$(strip $(1)) -c -xc /dev/null -o "$$TMP",-Wno-$(strip $(1))) + # cc-version # Usage gcc-ver := $(call cc-version) cc-version = $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh $(CC)) From e970c82a2aad08baf586ded6a1ea0816a8163524 Mon Sep 17 00:00:00 2001 From: showp1984 Date: Mon, 19 Mar 2012 20:16:09 +0100 Subject: [PATCH 2278/2556] Makefile: Hide -Wunitialized warnings for now --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 3792a7921faea..0c6ab694aa687 100644 --- a/Makefile +++ b/Makefile @@ -564,6 +564,7 @@ endif # This warning generated too much noise in a regular build. # Use make W=1 to enable this warning (see scripts/Makefile.build) KBUILD_CFLAGS += $(call cc-disable-warning, unused-but-set-variable) +KBUILD_CFLAGS += $(call cc-disable-warning, uninitialized) ifdef CONFIG_FRAME_POINTER KBUILD_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls From 288ca6f3d3ec5a9beba86fefe31481edc7c690ab Mon Sep 17 00:00:00 2001 From: Alessio Igor Bogani Date: Thu, 14 Apr 2011 14:59:39 +0200 Subject: [PATCH 2279/2556] module: Sort exported symbols This patch places every exported symbol in its own section (i.e. "___ksymtab+printk"). Thus the linker will use its SORT() directive to sort and finally merge all symbol in the right and final section (i.e. "__ksymtab"). The symbol prefixed archs use an underscore as prefix for symbols. To avoid collision we use a different character to create the temporary section names. This work was supported by a hardware donation from the CE Linux Forum. Signed-off-by: Alessio Igor Bogani Signed-off-by: Rusty Russell (folded in '+' fixup) Tested-by: Dirk Behme --- include/asm-generic/vmlinux.lds.h | 20 ++++++++++---------- include/linux/module.h | 4 ++-- scripts/module-common.lds | 11 +++++++++++ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index fe77e3395b40b..91ee08bb48579 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -274,70 +274,70 @@ /* Kernel symbol table: Normal symbols */ \ __ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___ksymtab) = .; \ - *(__ksymtab) \ + *(SORT(___ksymtab+*)) \ VMLINUX_SYMBOL(__stop___ksymtab) = .; \ } \ \ /* Kernel symbol table: GPL-only symbols */ \ __ksymtab_gpl : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___ksymtab_gpl) = .; \ - *(__ksymtab_gpl) \ + *(SORT(___ksymtab_gpl+*)) \ VMLINUX_SYMBOL(__stop___ksymtab_gpl) = .; \ } \ \ /* Kernel symbol table: Normal unused symbols */ \ __ksymtab_unused : AT(ADDR(__ksymtab_unused) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___ksymtab_unused) = .; \ - *(__ksymtab_unused) \ + *(SORT(___ksymtab_unused+*)) \ VMLINUX_SYMBOL(__stop___ksymtab_unused) = .; \ } \ \ /* Kernel symbol table: GPL-only unused symbols */ \ __ksymtab_unused_gpl : AT(ADDR(__ksymtab_unused_gpl) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___ksymtab_unused_gpl) = .; \ - *(__ksymtab_unused_gpl) \ + *(SORT(___ksymtab_unused_gpl+*)) \ VMLINUX_SYMBOL(__stop___ksymtab_unused_gpl) = .; \ } \ \ /* Kernel symbol table: GPL-future-only symbols */ \ __ksymtab_gpl_future : AT(ADDR(__ksymtab_gpl_future) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___ksymtab_gpl_future) = .; \ - *(__ksymtab_gpl_future) \ + *(SORT(___ksymtab_gpl_future+*)) \ VMLINUX_SYMBOL(__stop___ksymtab_gpl_future) = .; \ } \ \ /* Kernel symbol table: Normal symbols */ \ __kcrctab : AT(ADDR(__kcrctab) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___kcrctab) = .; \ - *(__kcrctab) \ + *(SORT(___kcrctab+*)) \ VMLINUX_SYMBOL(__stop___kcrctab) = .; \ } \ \ /* Kernel symbol table: GPL-only symbols */ \ __kcrctab_gpl : AT(ADDR(__kcrctab_gpl) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___kcrctab_gpl) = .; \ - *(__kcrctab_gpl) \ + *(SORT(___kcrctab_gpl+*)) \ VMLINUX_SYMBOL(__stop___kcrctab_gpl) = .; \ } \ \ /* Kernel symbol table: Normal unused symbols */ \ __kcrctab_unused : AT(ADDR(__kcrctab_unused) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___kcrctab_unused) = .; \ - *(__kcrctab_unused) \ + *(SORT(___kcrctab_unused+*)) \ VMLINUX_SYMBOL(__stop___kcrctab_unused) = .; \ } \ \ /* Kernel symbol table: GPL-only unused symbols */ \ __kcrctab_unused_gpl : AT(ADDR(__kcrctab_unused_gpl) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___kcrctab_unused_gpl) = .; \ - *(__kcrctab_unused_gpl) \ + *(SORT(___kcrctab_unused_gpl+*)) \ VMLINUX_SYMBOL(__stop___kcrctab_unused_gpl) = .; \ } \ \ /* Kernel symbol table: GPL-future-only symbols */ \ __kcrctab_gpl_future : AT(ADDR(__kcrctab_gpl_future) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___kcrctab_gpl_future) = .; \ - *(__kcrctab_gpl_future) \ + *(SORT(___kcrctab_gpl_future+*)) \ VMLINUX_SYMBOL(__stop___kcrctab_gpl_future) = .; \ } \ \ diff --git a/include/linux/module.h b/include/linux/module.h index 5de42043dff0c..e78aa8fee03ea 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -223,7 +223,7 @@ struct module_use { extern void *__crc_##sym __attribute__((weak)); \ static const unsigned long __kcrctab_##sym \ __used \ - __attribute__((section("__kcrctab" sec), unused)) \ + __attribute__((section("___kcrctab" sec "+" #sym), unused)) \ = (unsigned long) &__crc_##sym; #else #define __CRC_SYMBOL(sym, sec) @@ -238,7 +238,7 @@ struct module_use { = MODULE_SYMBOL_PREFIX #sym; \ static const struct kernel_symbol __ksymtab_##sym \ __used \ - __attribute__((section("__ksymtab" sec), unused)) \ + __attribute__((section("___ksymtab" sec "+" #sym), unused)) \ = { (unsigned long)&sym, __kstrtab_##sym } #define EXPORT_SYMBOL(sym) \ diff --git a/scripts/module-common.lds b/scripts/module-common.lds index 47a1f9ae0edeb..0865b3e752be8 100644 --- a/scripts/module-common.lds +++ b/scripts/module-common.lds @@ -5,4 +5,15 @@ */ SECTIONS { /DISCARD/ : { *(.discard) } + + __ksymtab : { *(SORT(___ksymtab+*)) } + __ksymtab_gpl : { *(SORT(___ksymtab_gpl+*)) } + __ksymtab_unused : { *(SORT(___ksymtab_unused+*)) } + __ksymtab_unused_gpl : { *(SORT(___ksymtab_unused_gpl+*)) } + __ksymtab_gpl_future : { *(SORT(___ksymtab_gpl_future+*)) } + __kcrctab : { *(SORT(___kcrctab+*)) } + __kcrctab_gpl : { *(SORT(___kcrctab_gpl+*)) } + __kcrctab_unused : { *(SORT(___kcrctab_unused+*)) } + __kcrctab_unused_gpl : { *(SORT(___kcrctab_unused_gpl+*)) } + __kcrctab_gpl_future : { *(SORT(___kcrctab_gpl_future+*)) } } From 8ebf6d58c3fee4cef28db1fb46a5eb5d7e8417a5 Mon Sep 17 00:00:00 2001 From: Andrew Vagin Date: Fri, 11 Nov 2011 23:04:09 -0800 Subject: [PATCH 2280/2556] sched: don't call task_group() many times in set_task_rq() It improves perfomance, especially if autogroup enabled. The size of set_task_rq() was 0x180 and now it is 0xa0. Signed-off-by: Andrew Vagin --- kernel/sched.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/kernel/sched.c b/kernel/sched.c index 3a2d949118185..ca02ddd609300 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -620,14 +620,18 @@ static inline struct task_group *task_group(struct task_struct *p) /* Change a task's cfs_rq and parent entity if it moves across CPUs/groups */ static inline void set_task_rq(struct task_struct *p, unsigned int cpu) { +#if defined(CONFIG_FAIR_GROUP_SCHED) || defined(CONFIG_RT_GROUP_SCHED) + struct task_group *tg = task_group(p); +#endif + #ifdef CONFIG_FAIR_GROUP_SCHED - p->se.cfs_rq = task_group(p)->cfs_rq[cpu]; - p->se.parent = task_group(p)->se[cpu]; + p->se.cfs_rq = tg->cfs_rq[cpu]; + p->se.parent = tg->se[cpu]; #endif #ifdef CONFIG_RT_GROUP_SCHED - p->rt.rt_rq = task_group(p)->rt_rq[cpu]; - p->rt.parent = task_group(p)->rt_se[cpu]; + p->rt.rt_rq = tg->rt_rq[cpu]; + p->rt.parent = tg->rt_se[cpu]; #endif } From 59ecae883edb69fe13dbacbb458ca4e026bb71cc Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Thu, 20 Jan 2011 11:12:26 -0600 Subject: [PATCH 2281/2556] mm: Remove support for kmem_cache_name() The last user was ext4 and Eric Sandeen removed the call in a recent patch. See the following URL for the discussion: http://marc.info/?l=linux-ext4&m=129546975702198&w=2 Signed-off-by: Christoph Lameter Signed-off-by: Pekka Enberg --- include/linux/slab.h | 1 - mm/slab.c | 8 -------- mm/slob.c | 6 ------ mm/slub.c | 6 ------ 4 files changed, 21 deletions(-) diff --git a/include/linux/slab.h b/include/linux/slab.h index fa9086647eb7a..ad4dd1c8d30aa 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -105,7 +105,6 @@ void kmem_cache_destroy(struct kmem_cache *); int kmem_cache_shrink(struct kmem_cache *); void kmem_cache_free(struct kmem_cache *, void *); unsigned int kmem_cache_size(struct kmem_cache *); -const char *kmem_cache_name(struct kmem_cache *); /* * Please use this macro to create slab caches. Simply specify the diff --git a/mm/slab.c b/mm/slab.c index 4c6e2e31ced05..2d5b92c09ae3b 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -2147,8 +2147,6 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp) * * @name must be valid until the cache is destroyed. This implies that * the module calling this has to destroy the cache before getting unloaded. - * Note that kmem_cache_name() is not guaranteed to return the same pointer, - * therefore applications must manage it themselves. * * The flags are * @@ -3840,12 +3838,6 @@ unsigned int kmem_cache_size(struct kmem_cache *cachep) } EXPORT_SYMBOL(kmem_cache_size); -const char *kmem_cache_name(struct kmem_cache *cachep) -{ - return cachep->name; -} -EXPORT_SYMBOL_GPL(kmem_cache_name); - /* * This initializes kmem_list3 or resizes various caches for all nodes. */ diff --git a/mm/slob.c b/mm/slob.c index 3588eaaef7267..46e0aee33a235 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -666,12 +666,6 @@ unsigned int kmem_cache_size(struct kmem_cache *c) } EXPORT_SYMBOL(kmem_cache_size); -const char *kmem_cache_name(struct kmem_cache *c) -{ - return c->name; -} -EXPORT_SYMBOL(kmem_cache_name); - int kmem_cache_shrink(struct kmem_cache *d) { return 0; diff --git a/mm/slub.c b/mm/slub.c index e15aa7f193c97..d2f343a54bad2 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2399,12 +2399,6 @@ unsigned int kmem_cache_size(struct kmem_cache *s) } EXPORT_SYMBOL(kmem_cache_size); -const char *kmem_cache_name(struct kmem_cache *s) -{ - return s->name; -} -EXPORT_SYMBOL(kmem_cache_name); - static void list_slab_objects(struct kmem_cache *s, struct page *page, const char *text) { From dec5bf8fa41cd290ff689c813b2a6e0dd17487f1 Mon Sep 17 00:00:00 2001 From: Lai Jiangshan Date: Thu, 10 Mar 2011 15:22:24 +0800 Subject: [PATCH 2282/2556] slab,rcu: don't assume the size of struct rcu_head The size of struct rcu_head may be changed. When it becomes larger, it may pollute the data after struct slab. Acked-by: Christoph Lameter Signed-off-by: Lai Jiangshan Signed-off-by: Pekka Enberg --- mm/slab.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/mm/slab.c b/mm/slab.c index 2d5b92c09ae3b..a18ba57517af4 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -190,22 +190,6 @@ typedef unsigned int kmem_bufctl_t; #define BUFCTL_ACTIVE (((kmem_bufctl_t)(~0U))-2) #define SLAB_LIMIT (((kmem_bufctl_t)(~0U))-3) -/* - * struct slab - * - * Manages the objs in a slab. Placed either at the beginning of mem allocated - * for a slab, or allocated from an general cache. - * Slabs are chained into three list: fully used, partial, fully free slabs. - */ -struct slab { - struct list_head list; - unsigned long colouroff; - void *s_mem; /* including colour offset */ - unsigned int inuse; /* num of objs active in slab */ - kmem_bufctl_t free; - unsigned short nodeid; -}; - /* * struct slab_rcu * @@ -219,8 +203,6 @@ struct slab { * * rcu_read_lock before reading the address, then rcu_read_unlock after * taking the spinlock within the structure expected at that address. - * - * We assume struct slab_rcu can overlay struct slab when destroying. */ struct slab_rcu { struct rcu_head head; @@ -228,6 +210,27 @@ struct slab_rcu { void *addr; }; +/* + * struct slab + * + * Manages the objs in a slab. Placed either at the beginning of mem allocated + * for a slab, or allocated from an general cache. + * Slabs are chained into three list: fully used, partial, fully free slabs. + */ +struct slab { + union { + struct { + struct list_head list; + unsigned long colouroff; + void *s_mem; /* including colour offset */ + unsigned int inuse; /* num of objs active in slab */ + kmem_bufctl_t free; + unsigned short nodeid; + }; + struct slab_rcu __slab_cover_slab_rcu; + }; +}; + /* * struct array_cache * From bbd2e7cdfcf5376b729daee617d3ee04d5928095 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Tue, 22 Mar 2011 16:30:49 -0700 Subject: [PATCH 2283/2556] mm: notifier_from_errno() cleanup While looking at some other notifier callbacks I noticed this code could use a simple cleanup. notifier_from_errno() no longer needs the if (ret)/else conditional. That same conditional is now done in notifier_from_errno(). Signed-off-by: Prarit Bhargava Cc: Paul Menage Cc: Li Zefan Acked-by: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_cgroup.c | 7 +------ mm/slab.c | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/mm/page_cgroup.c b/mm/page_cgroup.c index 5bffada7cde17..59a3cd4c799d5 100644 --- a/mm/page_cgroup.c +++ b/mm/page_cgroup.c @@ -243,12 +243,7 @@ static int __meminit page_cgroup_callback(struct notifier_block *self, break; } - if (ret) - ret = notifier_from_errno(ret); - else - ret = NOTIFY_OK; - - return ret; + return notifier_from_errno(ret); } #endif diff --git a/mm/slab.c b/mm/slab.c index a18ba57517af4..568803f121a87 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1390,7 +1390,7 @@ static int __meminit slab_memory_callback(struct notifier_block *self, break; } out: - return ret ? notifier_from_errno(ret) : NOTIFY_OK; + return notifier_from_errno(ret); } #endif /* CONFIG_NUMA && CONFIG_MEMORY_HOTPLUG */ From 4eb1a6cba9700bcd533975c066134b26b7346fba Mon Sep 17 00:00:00 2001 From: Suleiman Souhlal Date: Thu, 2 Jun 2011 00:16:42 -0700 Subject: [PATCH 2284/2556] SLAB: Record actual last user of freed objects. Currently, when using CONFIG_DEBUG_SLAB, we put in kfree() or kmem_cache_free() as the last user of free objects, which is not very useful, so change it to the caller of those functions instead. Acked-by: David Rientjes Acked-by: Christoph Lameter Signed-off-by: Suleiman Souhlal Signed-off-by: Pekka Enberg --- mm/slab.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mm/slab.c b/mm/slab.c index 568803f121a87..37f77ef4648bf 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -3603,13 +3603,14 @@ static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac) * Release an obj back to its cache. If the obj has a constructed state, it must * be in this state _before_ it is released. Called with disabled ints. */ -static inline void __cache_free(struct kmem_cache *cachep, void *objp) +static inline void __cache_free(struct kmem_cache *cachep, void *objp, + void *caller) { struct array_cache *ac = cpu_cache_get(cachep); check_irq_off(); kmemleak_free_recursive(objp, cachep->flags); - objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0)); + objp = cache_free_debugcheck(cachep, objp, caller); kmemcheck_slab_free(cachep, objp, obj_size(cachep)); @@ -3800,7 +3801,7 @@ void kmem_cache_free(struct kmem_cache *cachep, void *objp) debug_check_no_locks_freed(objp, obj_size(cachep)); if (!(cachep->flags & SLAB_DEBUG_OBJECTS)) debug_check_no_obj_freed(objp, obj_size(cachep)); - __cache_free(cachep, objp); + __cache_free(cachep, objp, __builtin_return_address(0)); local_irq_restore(flags); trace_kmem_cache_free(_RET_IP_, objp); @@ -3830,7 +3831,7 @@ void kfree(const void *objp) c = virt_to_cache(objp); debug_check_no_locks_freed(objp, obj_size(c)); debug_check_no_obj_freed(objp, obj_size(c)); - __cache_free(c, (void *)objp); + __cache_free(c, (void *)objp, __builtin_return_address(0)); local_irq_restore(flags); } EXPORT_SYMBOL(kfree); From 0a76aa497b3bf83eb42f30dafc28ecf96ccd45c1 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 26 Jun 2012 15:01:27 -0500 Subject: [PATCH 2285/2556] kbuild: add scorpion build flags let the compiler know the arch, -ffast-math seems needed for full benefit of -mfpu=neon --- Makefile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 0c6ab694aa687..5384539f88bf5 100644 --- a/Makefile +++ b/Makefile @@ -341,11 +341,12 @@ CHECK = sparse CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ -Wbitwise -Wno-return-void $(CF) -CFLAGS_MODULE = -AFLAGS_MODULE = -LDFLAGS_MODULE = -CFLAGS_KERNEL = -AFLAGS_KERNEL = +SCORPION_FLAGS = -march=armv7-a -mfpu=neon -ffast-math +CFLAGS_MODULE = -DMODULE $(SCORPION_FLAGS) +AFLAGS_MODULE = -DMODULE $(SCORPION_FLAGS) +LDFLAGS_MODULE = -T $(srctree)/scripts/module-common.lds +CFLAGS_KERNEL = $(SCORPION_FLAGS) +AFLAGS_KERNEL = $(SCORPION_FLAGS) CFLAGS_GCOV = -fprofile-arcs -ftest-coverage From e796747a6d9cf4c6232bb9fb1f019e8e9ff734c8 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 3 Jul 2012 21:13:25 -0500 Subject: [PATCH 2286/2556] msm: Add sysfs interface for mfreq Add mfreq interface in sysfs. This forces the system to use the maximum CPU clock on all CPU cores. (mfreq=1 enables, mfreq=0 disables) *other misc cleanup --- arch/arm/mach-msm/cpufreq.c | 73 ++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c index 48b6630b05091..f1d27ceca2e65 100644 --- a/arch/arm/mach-msm/cpufreq.c +++ b/arch/arm/mach-msm/cpufreq.c @@ -3,7 +3,7 @@ * MSM architecture cpufreq driver * * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2007 QUALCOMM Incorporated + * Copyright (c) 2007-2010, Code Aurora Forum. All rights reserved. * Author: Mike A. Chan * * This software is licensed under the terms of the GNU General Public @@ -17,17 +17,42 @@ * */ +#include #include #include #include +#include #include "acpuclock.h" +static int override_cpu; + +static int set_cpu_freq(struct cpufreq_policy *policy, unsigned int new_freq) +{ + int ret = 0; + struct cpufreq_freqs freqs; + + freqs.old = policy->cur; + if (override_cpu) { + if (policy->cur == policy->max) + return 0; + else + freqs.new = policy->max; + } else + freqs.new = new_freq; + freqs.cpu = policy->cpu; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + ret = acpuclk_set_rate(new_freq * 1000, 0); + if (!ret) + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return ret; +} + static int msm_cpufreq_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) { int index; - struct cpufreq_freqs freqs; struct cpufreq_frequency_table *table = cpufreq_frequency_get_table(policy->cpu); @@ -41,16 +66,12 @@ static int msm_cpufreq_target(struct cpufreq_policy *policy, return 0; #ifdef CONFIG_CPU_FREQ_DEBUG - printk("msm_cpufreq_target %d r %d (%d-%d) selected %d\n", target_freq, - relation, policy->min, policy->max, table[index].frequency); + pr_debug("CPU[%d] target %d relation %d (%d-%d) selected %d\n", + policy->cpu, target_freq, relation, + policy->min, policy->max, table[index].frequency); #endif - freqs.old = policy->cur; - freqs.new = table[index].frequency; - freqs.cpu = policy->cpu; - cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - acpuclk_set_rate(table[index].frequency * 1000, 0); - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); - return 0; + + return set_cpu_freq(policy, table[index].frequency); } static int msm_cpufreq_verify(struct cpufreq_policy *policy) @@ -68,14 +89,33 @@ static int msm_cpufreq_init(struct cpufreq_policy *policy) BUG_ON(cpufreq_frequency_table_cpuinfo(policy, table)); policy->cur = acpuclk_get_rate(); #ifdef CONFIG_MSM_CPU_FREQ_SET_MIN_MAX - policy->min = CONFIG_MSM_CPU_FREQ_MIN; - policy->max = CONFIG_MSM_CPU_FREQ_MAX; + policy->min = CONFIG_MSM_CPU_FREQ_MIN; + policy->max = CONFIG_MSM_CPU_FREQ_MAX; #endif policy->cpuinfo.transition_latency = acpuclk_get_switch_time() * NSEC_PER_USEC; return 0; } +static ssize_t store_mfreq(struct sysdev_class *class, + struct sysdev_class_attribute *attr, + const char *buf, size_t count) +{ + u64 val; + + if (strict_strtoull(buf, 0, &val) < 0) { + pr_err("Invalid parameter to mfreq\n"); + return 0; + } + if (val) + override_cpu = 1; + else + override_cpu = 0; + return count; +} + +static SYSDEV_CLASS_ATTR(mfreq, 0200, NULL, store_mfreq); + static struct freq_attr *msm_cpufreq_attr[] = { &cpufreq_freq_attr_scaling_available_freqs, NULL, @@ -93,7 +133,12 @@ static struct cpufreq_driver msm_cpufreq_driver = { static int __init msm_cpufreq_register(void) { + int err = sysfs_create_file(&cpu_sysdev_class.kset.kobj, + &attr_mfreq.attr); + if (err) + pr_err("Failed to create sysfs mfreq\n"); + return cpufreq_register_driver(&msm_cpufreq_driver); } -device_initcall(msm_cpufreq_register); +late_initcall(msm_cpufreq_register); From 11a45b7d36acf59b22670932fd84f35c2f8743c7 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sat, 7 Jul 2012 02:36:13 -0500 Subject: [PATCH 2287/2556] Revert "kbuild: add scorpion build flags" This reverts commit 0a76aa497b3bf83eb42f30dafc28ecf96ccd45c1. --- Makefile | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 5384539f88bf5..0c6ab694aa687 100644 --- a/Makefile +++ b/Makefile @@ -341,12 +341,11 @@ CHECK = sparse CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ -Wbitwise -Wno-return-void $(CF) -SCORPION_FLAGS = -march=armv7-a -mfpu=neon -ffast-math -CFLAGS_MODULE = -DMODULE $(SCORPION_FLAGS) -AFLAGS_MODULE = -DMODULE $(SCORPION_FLAGS) -LDFLAGS_MODULE = -T $(srctree)/scripts/module-common.lds -CFLAGS_KERNEL = $(SCORPION_FLAGS) -AFLAGS_KERNEL = $(SCORPION_FLAGS) +CFLAGS_MODULE = +AFLAGS_MODULE = +LDFLAGS_MODULE = +CFLAGS_KERNEL = +AFLAGS_KERNEL = CFLAGS_GCOV = -fprofile-arcs -ftest-coverage From 73d62e631f97bd934228fa902e8163c5c724f4fe Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 8 Jul 2012 00:22:40 -0500 Subject: [PATCH 2288/2556] configs: evervolv: update/ unify All: enable memory compaction / cgroup block bravo/mahimahi: remove cgroup debug/freezer supersonic: enable the other io schedulers / enable xz compression incrediblec: set bfq as default io scheduler / enable xz compression --- arch/arm/configs/evervolv_bravo_defconfig | 16 +++++++++----- .../configs/evervolv_incrediblec_defconfig | 22 +++++++++++-------- arch/arm/configs/evervolv_mahimahi_defconfig | 16 +++++++++----- .../arm/configs/evervolv_supersonic_defconfig | 16 +++++++++----- 4 files changed, 43 insertions(+), 27 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index 7893f96dc3b47..66b48abb8c6b5 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Mon May 21 22:17:34 2012 +# Sun Jul 8 00:00:30 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -78,9 +78,9 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=17 CONFIG_CGROUPS=y -CONFIG_CGROUP_DEBUG=y +# CONFIG_CGROUP_DEBUG is not set # CONFIG_CGROUP_NS is not set -CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_FREEZER is not set # CONFIG_CGROUP_DEVICE is not set # CONFIG_CPUSETS is not set CONFIG_CGROUP_CPUACCT=y @@ -89,7 +89,8 @@ CONFIG_RESOURCE_COUNTERS=y CONFIG_CGROUP_SCHED=y CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y -# CONFIG_BLK_CGROUP is not set +CONFIG_BLK_CGROUP=y +# CONFIG_DEBUG_BLK_CGROUP is not set # CONFIG_NAMESPACES is not set CONFIG_SCHED_AUTOGROUP=y # CONFIG_SYSFS_DEPRECATED is not set @@ -163,6 +164,7 @@ CONFIG_BLOCK=y CONFIG_LBDAF=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_BLK_DEV_INTEGRITY is not set +# CONFIG_BLK_DEV_THROTTLING is not set # # IO Schedulers @@ -170,8 +172,9 @@ CONFIG_LBDAF=y CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y +CONFIG_CFQ_GROUP_IOSCHED=y CONFIG_IOSCHED_BFQ=y -# CONFIG_CGROUP_BFQIO is not set +CONFIG_CGROUP_BFQIO=y # CONFIG_DEFAULT_DEADLINE is not set # CONFIG_DEFAULT_CFQ is not set CONFIG_DEFAULT_BFQ=y @@ -428,7 +431,8 @@ CONFIG_FLAT_NODE_MEM_MAP=y CONFIG_HAVE_MEMBLOCK=y CONFIG_PAGEFLAGS_EXTENDED=y CONFIG_SPLIT_PTLOCK_CPUS=4 -# CONFIG_COMPACTION is not set +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y # CONFIG_PHYS_ADDR_T_64BIT is not set CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index b1725229cc98e..e5e8611913d79 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Mon May 21 22:18:06 2012 +# Sun Jul 8 00:02:00 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -40,8 +40,8 @@ CONFIG_HAVE_KERNEL_LZMA=y CONFIG_HAVE_KERNEL_XZ=y CONFIG_HAVE_KERNEL_LZO=y # CONFIG_KERNEL_GZIP is not set -CONFIG_KERNEL_LZMA=y -# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y # CONFIG_KERNEL_LZO is not set CONFIG_SWAP=y # CONFIG_SYSVIPC is not set @@ -88,7 +88,8 @@ CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_SCHED=y CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y -# CONFIG_BLK_CGROUP is not set +CONFIG_BLK_CGROUP=y +# CONFIG_DEBUG_BLK_CGROUP is not set # CONFIG_NAMESPACES is not set CONFIG_SCHED_AUTOGROUP=y # CONFIG_SYSFS_DEPRECATED is not set @@ -161,6 +162,7 @@ CONFIG_BLOCK=y CONFIG_LBDAF=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_BLK_DEV_INTEGRITY is not set +# CONFIG_BLK_DEV_THROTTLING is not set # # IO Schedulers @@ -168,13 +170,14 @@ CONFIG_LBDAF=y CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y +CONFIG_CFQ_GROUP_IOSCHED=y CONFIG_IOSCHED_BFQ=y -# CONFIG_CGROUP_BFQIO is not set +CONFIG_CGROUP_BFQIO=y # CONFIG_DEFAULT_DEADLINE is not set -CONFIG_DEFAULT_CFQ=y -# CONFIG_DEFAULT_BFQ is not set +# CONFIG_DEFAULT_CFQ is not set +CONFIG_DEFAULT_BFQ=y # CONFIG_DEFAULT_NOOP is not set -CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_DEFAULT_IOSCHED="bfq" # CONFIG_INLINE_SPIN_TRYLOCK is not set # CONFIG_INLINE_SPIN_TRYLOCK_BH is not set # CONFIG_INLINE_SPIN_LOCK is not set @@ -431,7 +434,8 @@ CONFIG_FLAT_NODE_MEM_MAP=y CONFIG_HAVE_MEMBLOCK=y CONFIG_PAGEFLAGS_EXTENDED=y CONFIG_SPLIT_PTLOCK_CPUS=4 -# CONFIG_COMPACTION is not set +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y # CONFIG_PHYS_ADDR_T_64BIT is not set CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index 69a0f79cb2523..0f18cc03aa69f 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Tue May 22 02:06:04 2012 +# Sun Jul 8 00:04:26 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -78,9 +78,9 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=17 CONFIG_CGROUPS=y -CONFIG_CGROUP_DEBUG=y +# CONFIG_CGROUP_DEBUG is not set # CONFIG_CGROUP_NS is not set -CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_FREEZER is not set # CONFIG_CGROUP_DEVICE is not set # CONFIG_CPUSETS is not set CONFIG_CGROUP_CPUACCT=y @@ -89,7 +89,8 @@ CONFIG_RESOURCE_COUNTERS=y CONFIG_CGROUP_SCHED=y CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y -# CONFIG_BLK_CGROUP is not set +CONFIG_BLK_CGROUP=y +# CONFIG_DEBUG_BLK_CGROUP is not set # CONFIG_NAMESPACES is not set CONFIG_SCHED_AUTOGROUP=y # CONFIG_SYSFS_DEPRECATED is not set @@ -163,6 +164,7 @@ CONFIG_BLOCK=y CONFIG_LBDAF=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_BLK_DEV_INTEGRITY is not set +# CONFIG_BLK_DEV_THROTTLING is not set # # IO Schedulers @@ -170,8 +172,9 @@ CONFIG_LBDAF=y CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y +CONFIG_CFQ_GROUP_IOSCHED=y CONFIG_IOSCHED_BFQ=y -# CONFIG_CGROUP_BFQIO is not set +CONFIG_CGROUP_BFQIO=y # CONFIG_DEFAULT_DEADLINE is not set # CONFIG_DEFAULT_CFQ is not set CONFIG_DEFAULT_BFQ=y @@ -428,7 +431,8 @@ CONFIG_FLAT_NODE_MEM_MAP=y CONFIG_HAVE_MEMBLOCK=y CONFIG_PAGEFLAGS_EXTENDED=y CONFIG_SPLIT_PTLOCK_CPUS=4 -# CONFIG_COMPACTION is not set +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y # CONFIG_PHYS_ADDR_T_64BIT is not set CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index b6eafa315312d..caaf1fcb97988 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Mon May 21 22:18:23 2012 +# Sat Jul 7 23:58:28 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -41,8 +41,8 @@ CONFIG_HAVE_KERNEL_LZMA=y CONFIG_HAVE_KERNEL_XZ=y CONFIG_HAVE_KERNEL_LZO=y # CONFIG_KERNEL_GZIP is not set -CONFIG_KERNEL_LZMA=y -# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y # CONFIG_KERNEL_LZO is not set CONFIG_SWAP=y # CONFIG_SYSVIPC is not set @@ -177,10 +177,13 @@ CONFIG_BLK_DEV_THROTTLING=y # IO Schedulers # CONFIG_IOSCHED_NOOP=y -# CONFIG_IOSCHED_DEADLINE is not set -# CONFIG_IOSCHED_CFQ is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CFQ_GROUP_IOSCHED=y CONFIG_IOSCHED_BFQ=y CONFIG_CGROUP_BFQIO=y +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set CONFIG_DEFAULT_BFQ=y # CONFIG_DEFAULT_NOOP is not set CONFIG_DEFAULT_IOSCHED="bfq" @@ -437,7 +440,8 @@ CONFIG_FLAT_NODE_MEM_MAP=y CONFIG_HAVE_MEMBLOCK=y CONFIG_PAGEFLAGS_EXTENDED=y CONFIG_SPLIT_PTLOCK_CPUS=4 -# CONFIG_COMPACTION is not set +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y # CONFIG_PHYS_ADDR_T_64BIT is not set CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y From 43357614f25e6a048fb06a5451cb251c054e90fe Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 28 Jun 2011 10:59:12 -0700 Subject: [PATCH 2289/2556] cpufreq: expose a cpufreq_quick_get_max routine This allows drivers and other code to get the max reported CPU frequency. Initial use is for scaling ring frequency with GPU frequency in the i915 driver. Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/cpufreq/cpufreq.c | 20 ++++++++++++++++++++ include/linux/cpufreq.h | 5 +++++ 2 files changed, 25 insertions(+) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index df27d08200f20..efbc3cc4884b3 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1199,6 +1199,26 @@ unsigned int cpufreq_quick_get(unsigned int cpu) } EXPORT_SYMBOL(cpufreq_quick_get); +/** + * cpufreq_quick_get_max - get the max reported CPU frequency for this CPU + * @cpu: CPU number + * + * Just return the max possible frequency for a given CPU. + */ +unsigned int cpufreq_quick_get_max(unsigned int cpu) +{ + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); + unsigned int ret_freq = 0; + + if (policy) { + ret_freq = policy->max; + cpufreq_cpu_put(policy); + } + + return ret_freq; +} +EXPORT_SYMBOL(cpufreq_quick_get_max); + static unsigned int __cpufreq_get(unsigned int cpu) { diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 31f9557e72b7b..7bfaee242afb4 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -321,11 +321,16 @@ static inline unsigned int cpufreq_get(unsigned int cpu) /* query the last known CPU freq (in kHz). If zero, cpufreq couldn't detect it */ #ifdef CONFIG_CPU_FREQ unsigned int cpufreq_quick_get(unsigned int cpu); +unsigned int cpufreq_quick_get_max(unsigned int cpu); #else static inline unsigned int cpufreq_quick_get(unsigned int cpu) { return 0; } +static inline unsigned int cpufreq_quick_get_max(unsigned int cpu) +{ + return 0; +} #endif From fa6f74c1f01a5eb47c9207e4e169ed5d634443d8 Mon Sep 17 00:00:00 2001 From: Luming Yu Date: Fri, 8 Jul 2011 16:37:44 -0400 Subject: [PATCH 2290/2556] [CPUFREQ] fix cpumask memory leak in acpi-cpufreq on cpu hotplug. I came across a memory leak during a cyclic cpu-online-offline test. Signed-off-by: Yu Luming Cc: Len Brown Signed-off-by: Andrew Morton Signed-off-by: Dave Jones --- arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c index 4e04e12743887..596d5dd32f415 100644 --- a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -759,7 +759,7 @@ static void __exit acpi_cpufreq_exit(void) cpufreq_unregister_driver(&acpi_cpufreq_driver); - free_percpu(acpi_perf_data); + free_acpi_perf_data(); } module_param(acpi_pstate_strict, uint, 0644); From 74d166aefa669a88253e5e257577aea2d6c6a3f3 Mon Sep 17 00:00:00 2001 From: Peter Boonstoppel Date: Thu, 18 Aug 2011 11:59:58 -0700 Subject: [PATCH 2291/2556] cpufreq: prevent out-of-bounds write when stat->last_index = -1 When cpu runs at a frequency not in cpufreq table (because of other frequency governing mechanisms), freq_table_get_index() returns -1 which gets used as an array index. Original-Change-Id: Id8fa5d5125c3cd1e2aad8b48ff7bd619f39c57d8 Reviewed-on: http://git-master/r/47887 Reviewed-by: Peter Boonstoppel Tested-by: Peter Boonstoppel Reviewed-by: Diwakar Tundlam Reviewed-by: Aleksandr Frid Tested-by: Diwakar Tundlam Reviewed-by: Scott Williams Rebase-Id: R032fc27d227d3308e5afa17d909f64d6925e05cc --- drivers/cpufreq/cpufreq_stats.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index f6fba49450e71..0809fcd813c42 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -59,7 +59,7 @@ static int cpufreq_stats_update(unsigned int cpu) cur_time = get_jiffies_64(); spin_lock(&cpufreq_stats_lock); stat = per_cpu(cpufreq_stats_table, cpu); - if (stat->time_in_state) + if (stat->time_in_state && stat->last_index >= 0) stat->time_in_state[stat->last_index] = cputime64_add(stat->time_in_state[stat->last_index], cputime_sub(cur_time, stat->last_time)); @@ -298,10 +298,6 @@ static int cpufreq_stat_notifier_trans(struct notifier_block *nb, old_index = stat->last_index; new_index = freq_table_get_index(stat, freq->new); - /* We can't do stat->time_in_state[-1]= .. */ - if (old_index == -1 || new_index == -1) - return 0; - cpufreq_stats_update(freq->cpu); if (old_index == new_index) @@ -310,7 +306,8 @@ static int cpufreq_stat_notifier_trans(struct notifier_block *nb, spin_lock(&cpufreq_stats_lock); stat->last_index = new_index; #ifdef CONFIG_CPU_FREQ_STAT_DETAILS - stat->trans_table[old_index * stat->max_state + new_index]++; + if (old_index >= 0 && new_index >= 0) + stat->trans_table[old_index * stat->max_state + new_index]++; #endif stat->total_trans++; spin_unlock(&cpufreq_stats_lock); From 089fd1595f596506e908236e7c1088407b06514d Mon Sep 17 00:00:00 2001 From: Peter Boonstoppel Date: Thu, 18 Aug 2011 15:24:12 -0700 Subject: [PATCH 2292/2556] cpufreq: stats: snap freq to next lower freq when not in table When cpu runs at frequency not in cpufreq table (because of other frequency governing mechanisms), bill time spent at that frequency to next lower frequency in cpufreq stats table. Original-Change-Id: I9cfda4e7a223ca3f773f1adb145d242483209799 Reviewed-on: http://git-master/r/47929 Reviewed-by: Peter Boonstoppel Tested-by: Peter Boonstoppel Tested-by: Diwakar Tundlam Reviewed-by: Diwakar Tundlam Reviewed-by: Scott Williams Rebase-Id: Rd25650679bddec2837d92c75b18f8568c7569336 --- drivers/cpufreq/cpufreq_stats.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 0809fcd813c42..1957eee7549ee 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -159,10 +159,10 @@ static struct attribute_group stats_attr_group = { static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq) { int index; - for (index = 0; index < stat->max_state; index++) - if (stat->freq_table[index] == freq) - return index; - return -1; + for (index = 0; index < stat->state_num; index++) + if (stat->freq_table[index] > freq) + break; + return index - 1; /* below lowest freq in table: return -1 */ } /* should be called late in the CPU removal sequence so that the stats @@ -193,7 +193,7 @@ static void cpufreq_stats_free_sysfs(unsigned int cpu) static int cpufreq_stats_create_table(struct cpufreq_policy *policy, struct cpufreq_frequency_table *table) { - unsigned int i, j, count = 0, ret = 0; + unsigned int i, j, k, l, count = 0, ret = 0; struct cpufreq_stats *stat; struct cpufreq_policy *data; unsigned int alloc_size; @@ -245,8 +245,16 @@ static int cpufreq_stats_create_table(struct cpufreq_policy *policy, unsigned int freq = table[i].frequency; if (freq == CPUFREQ_ENTRY_INVALID) continue; - if (freq_table_get_index(stat, freq) == -1) - stat->freq_table[j++] = freq; + + /* Insert in sorted stat->freq_table */ + for (k = 0; k < j && stat->freq_table[k] < freq; k++) + ; + if (stat->freq_table[k] == freq) + continue; + for (l = j; l > k; l--) + stat->freq_table[l] = stat->freq_table[l - 1]; + stat->freq_table[k] = freq; + j++; } stat->state_num = j; spin_lock(&cpufreq_stats_lock); From 71d4679b6c54b51671e07984325c81446f472f70 Mon Sep 17 00:00:00 2001 From: Antti P Miettinen Date: Tue, 27 Dec 2011 12:09:56 +0200 Subject: [PATCH 2293/2556] cpufreq: Export user_policy min/max Add sysfs nodes for user_policy min and max settings. Bug 888312 Change-Id: Ife8ec07f8faebc0c04e7ecf6e24976a311bad73b Reviewed-on: http://git-master/r/72201 Signed-off-by: Antti P Miettinen Signed-off-by: Varun Wadekar Reviewed-on: http://git-master/r/75882 Reviewed-by: Automatic_Commit_Validation_User --- drivers/cpufreq/cpufreq.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index efbc3cc4884b3..addbb0a7b82f0 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -364,6 +364,8 @@ show_one(cpuinfo_transition_latency, cpuinfo.transition_latency); show_one(scaling_min_freq, min); show_one(scaling_max_freq, max); show_one(scaling_cur_freq, cur); +show_one(policy_min_freq, user_policy.min); +show_one(policy_max_freq, user_policy.max); static int __cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_policy *policy); @@ -582,6 +584,8 @@ cpufreq_freq_attr_rw(scaling_min_freq); cpufreq_freq_attr_rw(scaling_max_freq); cpufreq_freq_attr_rw(scaling_governor); cpufreq_freq_attr_rw(scaling_setspeed); +cpufreq_freq_attr_ro(policy_min_freq); +cpufreq_freq_attr_ro(policy_max_freq); static struct attribute *default_attrs[] = { &cpuinfo_min_freq.attr, @@ -595,6 +599,8 @@ static struct attribute *default_attrs[] = { &scaling_driver.attr, &scaling_available_governors.attr, &scaling_setspeed.attr, + &policy_min_freq.attr, + &policy_max_freq.attr, NULL }; From 542a8fefe57273aca8274d3bbf8c93061518808e Mon Sep 17 00:00:00 2001 From: Peter Boonstoppel Date: Tue, 28 Feb 2012 14:05:50 -0800 Subject: [PATCH 2294/2556] cpufreq: ondemand: Prevent sysfs create race Protecting sysfs_remove_group() in CPUFREQ_GOV_STOP with dbs_mutex Bug 946462 Signed-off-by: Peter Boonstoppel Reviewed-on: http://git-master/r/86426 (cherry picked from commit d1131158e2ad4d5ccc53b3008743c29385650d86) Change-Id: Iae810e83eaa6f0f7d970b56238cbcb61118af610 Reviewed-on: http://git-master/r/87392 Reviewed-by: Peter Boonstoppel Reviewed-by: Diwakar Tundlam Tested-by: Peter Boonstoppel --- drivers/cpufreq/cpufreq_ondemand.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 2511ebf4f10f0..2b260d0ed30a4 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -702,11 +702,10 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, mutex_lock(&dbs_mutex); mutex_destroy(&this_dbs_info->timer_mutex); dbs_enable--; - mutex_unlock(&dbs_mutex); if (!dbs_enable) sysfs_remove_group(cpufreq_global_kobject, &dbs_attr_group); - + mutex_unlock(&dbs_mutex); break; case CPUFREQ_GOV_LIMITS: From f8162eda64ea54d35e784ececb2262d920e702b0 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Fri, 13 Jul 2012 13:07:41 -0500 Subject: [PATCH 2295/2556] configs: evervolv: update localversion for jellybean --- arch/arm/configs/evervolv_bravo_defconfig | 2 +- arch/arm/configs/evervolv_incrediblec_defconfig | 2 +- arch/arm/configs/evervolv_mahimahi_defconfig | 2 +- arch/arm/configs/evervolv_supersonic_defconfig | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index 66b48abb8c6b5..f319c4750576a 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -33,7 +33,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" -CONFIG_LOCALVERSION="-evervolv-turba-ics" +CONFIG_LOCALVERSION="-evervolv-turba-jellybean" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index e5e8611913d79..47a9f6f7bd26d 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -33,7 +33,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="arm-eabi-" -CONFIG_LOCALVERSION="-evervolv-dives-ics" +CONFIG_LOCALVERSION="-evervolv-dives-jellybean" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index 0f18cc03aa69f..b5a76357b7074 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -33,7 +33,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" -CONFIG_LOCALVERSION="-evervolv-perdo-ics" +CONFIG_LOCALVERSION="-evervolv-perdo-jellybean" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index caaf1fcb97988..cfe045b1852ba 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -34,7 +34,7 @@ CONFIG_BROKEN_ON_SMP=y CONFIG_LOCK_KERNEL=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" -CONFIG_LOCALVERSION="-evervolv-acies-ics" +CONFIG_LOCALVERSION="-evervolv-acies-jellybean" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y From 98c15bf709c1181a9658fe46c4b140346e8ad276 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Fri, 13 Jul 2012 13:15:26 -0500 Subject: [PATCH 2296/2556] configs: evervolv: switch to slub and deadline as default --- arch/arm/configs/evervolv_bravo_defconfig | 16 ++++++++-------- arch/arm/configs/evervolv_incrediblec_defconfig | 16 ++++++++-------- arch/arm/configs/evervolv_mahimahi_defconfig | 16 ++++++++-------- arch/arm/configs/evervolv_supersonic_defconfig | 16 ++++++++-------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index f319c4750576a..d3a6364714101 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sun Jul 8 00:00:30 2012 +# Fri Jul 13 13:13:49 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -133,9 +133,10 @@ CONFIG_PERF_USE_VMALLOC=y # CONFIG_PERF_EVENTS is not set # CONFIG_PERF_COUNTERS is not set CONFIG_VM_EVENT_COUNTERS=y +# CONFIG_SLUB_DEBUG is not set CONFIG_COMPAT_BRK=y -CONFIG_SLAB=y -# CONFIG_SLUB is not set +# CONFIG_SLAB is not set +CONFIG_SLUB=y # CONFIG_SLOB is not set # CONFIG_PROFILING is not set CONFIG_HAVE_OPROFILE=y @@ -151,7 +152,6 @@ CONFIG_HAVE_DMA_API_DEBUG=y # # CONFIG_GCOV_KERNEL is not set CONFIG_HAVE_GENERIC_DMA_COHERENT=y -CONFIG_SLABINFO=y CONFIG_RT_MUTEXES=y CONFIG_BASE_SMALL=0 CONFIG_MODULES=y @@ -175,11 +175,11 @@ CONFIG_IOSCHED_CFQ=y CONFIG_CFQ_GROUP_IOSCHED=y CONFIG_IOSCHED_BFQ=y CONFIG_CGROUP_BFQIO=y -# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_DEADLINE=y # CONFIG_DEFAULT_CFQ is not set -CONFIG_DEFAULT_BFQ=y +# CONFIG_DEFAULT_BFQ is not set # CONFIG_DEFAULT_NOOP is not set -CONFIG_DEFAULT_IOSCHED="bfq" +CONFIG_DEFAULT_IOSCHED="deadline" # CONFIG_INLINE_SPIN_TRYLOCK is not set # CONFIG_INLINE_SPIN_TRYLOCK_BH is not set # CONFIG_INLINE_SPIN_LOCK is not set @@ -1935,7 +1935,7 @@ CONFIG_DEBUG_KERNEL=y CONFIG_SCHEDSTATS=y CONFIG_TIMER_STATS=y # CONFIG_DEBUG_OBJECTS is not set -# CONFIG_DEBUG_SLAB is not set +# CONFIG_SLUB_STATS is not set # CONFIG_DEBUG_KMEMLEAK is not set # CONFIG_DEBUG_PREEMPT is not set # CONFIG_DEBUG_RT_MUTEXES is not set diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index 47a9f6f7bd26d..99d33d6f74555 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sun Jul 8 00:02:00 2012 +# Fri Jul 13 13:13:13 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -132,9 +132,10 @@ CONFIG_PERF_USE_VMALLOC=y # CONFIG_PERF_EVENTS is not set # CONFIG_PERF_COUNTERS is not set CONFIG_VM_EVENT_COUNTERS=y +# CONFIG_SLUB_DEBUG is not set CONFIG_COMPAT_BRK=y -CONFIG_SLAB=y -# CONFIG_SLUB is not set +# CONFIG_SLAB is not set +CONFIG_SLUB=y # CONFIG_SLOB is not set # CONFIG_PROFILING is not set CONFIG_HAVE_OPROFILE=y @@ -149,7 +150,6 @@ CONFIG_HAVE_DMA_API_DEBUG=y # GCOV-based kernel profiling # CONFIG_HAVE_GENERIC_DMA_COHERENT=y -CONFIG_SLABINFO=y CONFIG_RT_MUTEXES=y CONFIG_BASE_SMALL=0 CONFIG_MODULES=y @@ -173,11 +173,11 @@ CONFIG_IOSCHED_CFQ=y CONFIG_CFQ_GROUP_IOSCHED=y CONFIG_IOSCHED_BFQ=y CONFIG_CGROUP_BFQIO=y -# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_DEADLINE=y # CONFIG_DEFAULT_CFQ is not set -CONFIG_DEFAULT_BFQ=y +# CONFIG_DEFAULT_BFQ is not set # CONFIG_DEFAULT_NOOP is not set -CONFIG_DEFAULT_IOSCHED="bfq" +CONFIG_DEFAULT_IOSCHED="deadline" # CONFIG_INLINE_SPIN_TRYLOCK is not set # CONFIG_INLINE_SPIN_TRYLOCK_BH is not set # CONFIG_INLINE_SPIN_LOCK is not set @@ -1972,7 +1972,7 @@ CONFIG_SCHED_DEBUG=y CONFIG_SCHEDSTATS=y CONFIG_TIMER_STATS=y # CONFIG_DEBUG_OBJECTS is not set -# CONFIG_DEBUG_SLAB is not set +# CONFIG_SLUB_STATS is not set # CONFIG_DEBUG_KMEMLEAK is not set # CONFIG_DEBUG_PREEMPT is not set # CONFIG_DEBUG_RT_MUTEXES is not set diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index b5a76357b7074..21f8e0509a9ce 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sun Jul 8 00:04:26 2012 +# Fri Jul 13 13:14:18 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -133,9 +133,10 @@ CONFIG_PERF_USE_VMALLOC=y # CONFIG_PERF_EVENTS is not set # CONFIG_PERF_COUNTERS is not set CONFIG_VM_EVENT_COUNTERS=y +# CONFIG_SLUB_DEBUG is not set CONFIG_COMPAT_BRK=y -CONFIG_SLAB=y -# CONFIG_SLUB is not set +# CONFIG_SLAB is not set +CONFIG_SLUB=y # CONFIG_SLOB is not set # CONFIG_PROFILING is not set CONFIG_HAVE_OPROFILE=y @@ -151,7 +152,6 @@ CONFIG_HAVE_DMA_API_DEBUG=y # # CONFIG_GCOV_KERNEL is not set CONFIG_HAVE_GENERIC_DMA_COHERENT=y -CONFIG_SLABINFO=y CONFIG_RT_MUTEXES=y CONFIG_BASE_SMALL=0 CONFIG_MODULES=y @@ -175,11 +175,11 @@ CONFIG_IOSCHED_CFQ=y CONFIG_CFQ_GROUP_IOSCHED=y CONFIG_IOSCHED_BFQ=y CONFIG_CGROUP_BFQIO=y -# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_DEADLINE=y # CONFIG_DEFAULT_CFQ is not set -CONFIG_DEFAULT_BFQ=y +# CONFIG_DEFAULT_BFQ is not set # CONFIG_DEFAULT_NOOP is not set -CONFIG_DEFAULT_IOSCHED="bfq" +CONFIG_DEFAULT_IOSCHED="deadline" # CONFIG_INLINE_SPIN_TRYLOCK is not set # CONFIG_INLINE_SPIN_TRYLOCK_BH is not set # CONFIG_INLINE_SPIN_LOCK is not set @@ -1934,7 +1934,7 @@ CONFIG_SCHED_DEBUG=y CONFIG_SCHEDSTATS=y CONFIG_TIMER_STATS=y # CONFIG_DEBUG_OBJECTS is not set -# CONFIG_DEBUG_SLAB is not set +# CONFIG_SLUB_STATS is not set # CONFIG_DEBUG_KMEMLEAK is not set # CONFIG_DEBUG_PREEMPT is not set # CONFIG_DEBUG_RT_MUTEXES is not set diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index cfe045b1852ba..016addce34e17 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sat Jul 7 23:58:28 2012 +# Fri Jul 13 13:11:36 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -140,9 +140,10 @@ CONFIG_PERF_USE_VMALLOC=y # CONFIG_PERF_EVENTS is not set # CONFIG_PERF_COUNTERS is not set CONFIG_VM_EVENT_COUNTERS=y +# CONFIG_SLUB_DEBUG is not set CONFIG_COMPAT_BRK=y -CONFIG_SLAB=y -# CONFIG_SLUB is not set +# CONFIG_SLAB is not set +CONFIG_SLUB=y # CONFIG_SLOB is not set # CONFIG_PROFILING is not set CONFIG_HAVE_OPROFILE=y @@ -158,7 +159,6 @@ CONFIG_HAVE_DMA_API_DEBUG=y # # CONFIG_GCOV_KERNEL is not set CONFIG_HAVE_GENERIC_DMA_COHERENT=y -CONFIG_SLABINFO=y CONFIG_RT_MUTEXES=y CONFIG_BASE_SMALL=0 CONFIG_MODULES=y @@ -182,11 +182,11 @@ CONFIG_IOSCHED_CFQ=y CONFIG_CFQ_GROUP_IOSCHED=y CONFIG_IOSCHED_BFQ=y CONFIG_CGROUP_BFQIO=y -# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_DEADLINE=y # CONFIG_DEFAULT_CFQ is not set -CONFIG_DEFAULT_BFQ=y +# CONFIG_DEFAULT_BFQ is not set # CONFIG_DEFAULT_NOOP is not set -CONFIG_DEFAULT_IOSCHED="bfq" +CONFIG_DEFAULT_IOSCHED="deadline" # CONFIG_INLINE_SPIN_TRYLOCK is not set # CONFIG_INLINE_SPIN_TRYLOCK_BH is not set # CONFIG_INLINE_SPIN_LOCK is not set @@ -1963,7 +1963,7 @@ CONFIG_SCHED_DEBUG=y CONFIG_SCHEDSTATS=y CONFIG_TIMER_STATS=y # CONFIG_DEBUG_OBJECTS is not set -# CONFIG_DEBUG_SLAB is not set +# CONFIG_SLUB_STATS is not set # CONFIG_DEBUG_KMEMLEAK is not set # CONFIG_DEBUG_PREEMPT is not set # CONFIG_DEBUG_RT_MUTEXES is not set From 63c877557539f15f3ba31f1961c7b1284d84c4e9 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sat, 14 Jul 2012 00:32:46 -0500 Subject: [PATCH 2297/2556] configs: evervolv: enable tracing --- arch/arm/configs/evervolv_bravo_defconfig | 31 ++++++++++++--- .../configs/evervolv_incrediblec_defconfig | 38 ++++++++++++++++--- arch/arm/configs/evervolv_mahimahi_defconfig | 31 ++++++++++++--- .../arm/configs/evervolv_supersonic_defconfig | 31 ++++++++++++--- 4 files changed, 110 insertions(+), 21 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index d3a6364714101..cd17a1bf9ec71 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Fri Jul 13 13:13:49 2012 +# Sat Jul 14 00:27:42 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -131,7 +131,7 @@ CONFIG_PERF_USE_VMALLOC=y # Kernel Performance Events And Counters # # CONFIG_PERF_EVENTS is not set -# CONFIG_PERF_COUNTERS is not set +CONFIG_PERF_COUNTERS=y CONFIG_VM_EVENT_COUNTERS=y # CONFIG_SLUB_DEBUG is not set CONFIG_COMPAT_BRK=y @@ -139,6 +139,7 @@ CONFIG_COMPAT_BRK=y CONFIG_SLUB=y # CONFIG_SLOB is not set # CONFIG_PROFILING is not set +CONFIG_TRACEPOINTS=y CONFIG_HAVE_OPROFILE=y # CONFIG_KPROBES is not set CONFIG_HAVE_KPROBES=y @@ -637,7 +638,7 @@ CONFIG_NETFILTER_XT_TARGET_CONNMARK=y # CONFIG_NETFILTER_XT_TARGET_CT is not set # CONFIG_NETFILTER_XT_TARGET_DSCP is not set CONFIG_NETFILTER_XT_TARGET_HL=y -# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set @@ -839,6 +840,7 @@ CONFIG_NET_SCH_FIFO=y # Network testing # # CONFIG_NET_PKTGEN is not set +# CONFIG_NET_DROP_MONITOR is not set # CONFIG_HAMRADIO is not set # CONFIG_CAN is not set # CONFIG_IRDA is not set @@ -1973,13 +1975,32 @@ CONFIG_FRAME_POINTER=y # CONFIG_LATENCYTOP is not set # CONFIG_SYSCTL_SYSCALL_CHECK is not set # CONFIG_PAGE_POISONING is not set +CONFIG_NOP_TRACER=y CONFIG_HAVE_FUNCTION_TRACER=y CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y CONFIG_HAVE_DYNAMIC_FTRACE=y CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACER_MAX_TRACE=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_EVENT_POWER_TRACING_DEPRECATED=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y CONFIG_TRACING_SUPPORT=y -# CONFIG_FTRACE is not set +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +CONFIG_SCHED_TRACER=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set # CONFIG_DYNAMIC_DEBUG is not set # CONFIG_DMA_API_DEBUG is not set # CONFIG_ATOMIC64_SELFTEST is not set @@ -2106,7 +2127,7 @@ CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_USER_API_HASH is not set # CONFIG_CRYPTO_USER_API_SKCIPHER is not set CONFIG_CRYPTO_HW=y -# CONFIG_BINARY_PRINTF is not set +CONFIG_BINARY_PRINTF=y # # Library routines diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index 99d33d6f74555..3a4da1f92be90 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Fri Jul 13 13:13:13 2012 +# Sat Jul 14 00:30:00 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -130,7 +130,7 @@ CONFIG_PERF_USE_VMALLOC=y # Kernel Performance Events And Counters # # CONFIG_PERF_EVENTS is not set -# CONFIG_PERF_COUNTERS is not set +CONFIG_PERF_COUNTERS=y CONFIG_VM_EVENT_COUNTERS=y # CONFIG_SLUB_DEBUG is not set CONFIG_COMPAT_BRK=y @@ -138,6 +138,7 @@ CONFIG_COMPAT_BRK=y CONFIG_SLUB=y # CONFIG_SLOB is not set # CONFIG_PROFILING is not set +CONFIG_TRACEPOINTS=y CONFIG_HAVE_OPROFILE=y # CONFIG_KPROBES is not set CONFIG_HAVE_KPROBES=y @@ -149,6 +150,7 @@ CONFIG_HAVE_DMA_API_DEBUG=y # # GCOV-based kernel profiling # +# CONFIG_GCOV_KERNEL is not set CONFIG_HAVE_GENERIC_DMA_COHERENT=y CONFIG_RT_MUTEXES=y CONFIG_BASE_SMALL=0 @@ -640,7 +642,7 @@ CONFIG_NETFILTER_XT_TARGET_CONNMARK=y # CONFIG_NETFILTER_XT_TARGET_CT is not set # CONFIG_NETFILTER_XT_TARGET_DSCP is not set CONFIG_NETFILTER_XT_TARGET_HL=y -# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set @@ -843,6 +845,7 @@ CONFIG_DNS_RESOLVER=y # Network testing # # CONFIG_NET_PKTGEN is not set +# CONFIG_NET_DROP_MONITOR is not set # CONFIG_HAMRADIO is not set # CONFIG_CAN is not set # CONFIG_IRDA is not set @@ -1571,6 +1574,7 @@ CONFIG_USB_ARCH_HAS_EHCI=y CONFIG_USB_GADGET=y # CONFIG_USB_GADGET_DEBUG is not set # CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_USB_GADGET_SELECTED=y # CONFIG_USB_GADGET_R8A66597 is not set @@ -1807,6 +1811,7 @@ CONFIG_EXT4_FS_XATTR=y # CONFIG_EXT4_FS_SECURITY is not set # CONFIG_EXT4_DEBUG is not set CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set CONFIG_FS_MBCACHE=y # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set @@ -1961,7 +1966,7 @@ CONFIG_FRAME_WARN=1024 CONFIG_MAGIC_SYSRQ=y # CONFIG_STRIP_ASM_SYMS is not set # CONFIG_UNUSED_SYMBOLS is not set -# CONFIG_DEBUG_FS is not set +CONFIG_DEBUG_FS=y # CONFIG_HEADERS_CHECK is not set CONFIG_DEBUG_KERNEL=y # CONFIG_DEBUG_SHIRQ is not set @@ -2004,17 +2009,38 @@ CONFIG_DEBUG_INFO=y # CONFIG_BACKTRACE_SELF_TEST is not set # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set # CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set # CONFIG_FAULT_INJECTION is not set # CONFIG_LATENCYTOP is not set # CONFIG_SYSCTL_SYSCALL_CHECK is not set # CONFIG_PAGE_POISONING is not set +CONFIG_NOP_TRACER=y CONFIG_HAVE_FUNCTION_TRACER=y CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y CONFIG_HAVE_DYNAMIC_FTRACE=y CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACER_MAX_TRACE=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_EVENT_POWER_TRACING_DEPRECATED=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y CONFIG_TRACING_SUPPORT=y -# CONFIG_FTRACE is not set +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +CONFIG_SCHED_TRACER=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_DYNAMIC_DEBUG is not set # CONFIG_DMA_API_DEBUG is not set # CONFIG_ATOMIC64_SELFTEST is not set # CONFIG_SAMPLES is not set @@ -2141,7 +2167,7 @@ CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_USER_API_HASH is not set # CONFIG_CRYPTO_USER_API_SKCIPHER is not set CONFIG_CRYPTO_HW=y -# CONFIG_BINARY_PRINTF is not set +CONFIG_BINARY_PRINTF=y # # Library routines diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index 21f8e0509a9ce..f15ff2f044cdb 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Fri Jul 13 13:14:18 2012 +# Fri Jul 13 21:30:59 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -131,7 +131,7 @@ CONFIG_PERF_USE_VMALLOC=y # Kernel Performance Events And Counters # # CONFIG_PERF_EVENTS is not set -# CONFIG_PERF_COUNTERS is not set +CONFIG_PERF_COUNTERS=y CONFIG_VM_EVENT_COUNTERS=y # CONFIG_SLUB_DEBUG is not set CONFIG_COMPAT_BRK=y @@ -139,6 +139,7 @@ CONFIG_COMPAT_BRK=y CONFIG_SLUB=y # CONFIG_SLOB is not set # CONFIG_PROFILING is not set +CONFIG_TRACEPOINTS=y CONFIG_HAVE_OPROFILE=y # CONFIG_KPROBES is not set CONFIG_HAVE_KPROBES=y @@ -637,7 +638,7 @@ CONFIG_NETFILTER_XT_TARGET_CONNMARK=y # CONFIG_NETFILTER_XT_TARGET_CT is not set # CONFIG_NETFILTER_XT_TARGET_DSCP is not set CONFIG_NETFILTER_XT_TARGET_HL=y -# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set @@ -839,6 +840,7 @@ CONFIG_NET_SCH_FIFO=y # Network testing # # CONFIG_NET_PKTGEN is not set +# CONFIG_NET_DROP_MONITOR is not set # CONFIG_HAMRADIO is not set # CONFIG_CAN is not set # CONFIG_IRDA is not set @@ -1972,13 +1974,32 @@ CONFIG_FRAME_POINTER=y # CONFIG_LATENCYTOP is not set # CONFIG_SYSCTL_SYSCALL_CHECK is not set # CONFIG_PAGE_POISONING is not set +CONFIG_NOP_TRACER=y CONFIG_HAVE_FUNCTION_TRACER=y CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y CONFIG_HAVE_DYNAMIC_FTRACE=y CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACER_MAX_TRACE=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_EVENT_POWER_TRACING_DEPRECATED=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y CONFIG_TRACING_SUPPORT=y -# CONFIG_FTRACE is not set +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +CONFIG_SCHED_TRACER=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set # CONFIG_DYNAMIC_DEBUG is not set # CONFIG_DMA_API_DEBUG is not set # CONFIG_ATOMIC64_SELFTEST is not set @@ -2105,7 +2126,7 @@ CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_USER_API_HASH is not set # CONFIG_CRYPTO_USER_API_SKCIPHER is not set CONFIG_CRYPTO_HW=y -# CONFIG_BINARY_PRINTF is not set +CONFIG_BINARY_PRINTF=y # # Library routines diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index 016addce34e17..ffbed07e84d91 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Fri Jul 13 13:11:36 2012 +# Sat Jul 14 00:31:49 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -138,7 +138,7 @@ CONFIG_PERF_USE_VMALLOC=y # Kernel Performance Events And Counters # # CONFIG_PERF_EVENTS is not set -# CONFIG_PERF_COUNTERS is not set +CONFIG_PERF_COUNTERS=y CONFIG_VM_EVENT_COUNTERS=y # CONFIG_SLUB_DEBUG is not set CONFIG_COMPAT_BRK=y @@ -146,6 +146,7 @@ CONFIG_COMPAT_BRK=y CONFIG_SLUB=y # CONFIG_SLOB is not set # CONFIG_PROFILING is not set +CONFIG_TRACEPOINTS=y CONFIG_HAVE_OPROFILE=y # CONFIG_KPROBES is not set CONFIG_HAVE_KPROBES=y @@ -648,7 +649,7 @@ CONFIG_NETFILTER_XT_TARGET_CONNMARK=y # CONFIG_NETFILTER_XT_TARGET_CT is not set # CONFIG_NETFILTER_XT_TARGET_DSCP is not set CONFIG_NETFILTER_XT_TARGET_HL=y -# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set @@ -853,6 +854,7 @@ CONFIG_NET_SCH_FIFO=y # Network testing # # CONFIG_NET_PKTGEN is not set +# CONFIG_NET_DROP_MONITOR is not set # CONFIG_HAMRADIO is not set # CONFIG_CAN is not set # CONFIG_IRDA is not set @@ -2000,13 +2002,32 @@ CONFIG_FRAME_POINTER=y # CONFIG_LATENCYTOP is not set # CONFIG_SYSCTL_SYSCALL_CHECK is not set # CONFIG_PAGE_POISONING is not set +CONFIG_NOP_TRACER=y CONFIG_HAVE_FUNCTION_TRACER=y CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y CONFIG_HAVE_DYNAMIC_FTRACE=y CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACER_MAX_TRACE=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_EVENT_POWER_TRACING_DEPRECATED=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y CONFIG_TRACING_SUPPORT=y -# CONFIG_FTRACE is not set +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +CONFIG_SCHED_TRACER=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set # CONFIG_DYNAMIC_DEBUG is not set # CONFIG_DMA_API_DEBUG is not set # CONFIG_ATOMIC64_SELFTEST is not set @@ -2133,7 +2154,7 @@ CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_USER_API_HASH is not set # CONFIG_CRYPTO_USER_API_SKCIPHER is not set CONFIG_CRYPTO_HW=y -# CONFIG_BINARY_PRINTF is not set +CONFIG_BINARY_PRINTF=y # # Library routines From ef4bfc6314f6ab694dca82f89226db1a93a74e33 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 15 Jul 2012 12:42:40 -0500 Subject: [PATCH 2298/2556] configs: evervolv reset inc/supersonic to 0f409d7 --- .../configs/evervolv_incrediblec_defconfig | 68 ++++++------------- .../arm/configs/evervolv_supersonic_defconfig | 59 +++++----------- 2 files changed, 36 insertions(+), 91 deletions(-) diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index 3a4da1f92be90..b1725229cc98e 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sat Jul 14 00:30:00 2012 +# Mon May 21 22:18:06 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -33,15 +33,15 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="arm-eabi-" -CONFIG_LOCALVERSION="-evervolv-dives-jellybean" +CONFIG_LOCALVERSION="-evervolv-dives-ics" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y CONFIG_HAVE_KERNEL_XZ=y CONFIG_HAVE_KERNEL_LZO=y # CONFIG_KERNEL_GZIP is not set -# CONFIG_KERNEL_LZMA is not set -CONFIG_KERNEL_XZ=y +CONFIG_KERNEL_LZMA=y +# CONFIG_KERNEL_XZ is not set # CONFIG_KERNEL_LZO is not set CONFIG_SWAP=y # CONFIG_SYSVIPC is not set @@ -88,8 +88,7 @@ CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_SCHED=y CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y -CONFIG_BLK_CGROUP=y -# CONFIG_DEBUG_BLK_CGROUP is not set +# CONFIG_BLK_CGROUP is not set # CONFIG_NAMESPACES is not set CONFIG_SCHED_AUTOGROUP=y # CONFIG_SYSFS_DEPRECATED is not set @@ -130,15 +129,13 @@ CONFIG_PERF_USE_VMALLOC=y # Kernel Performance Events And Counters # # CONFIG_PERF_EVENTS is not set -CONFIG_PERF_COUNTERS=y +# CONFIG_PERF_COUNTERS is not set CONFIG_VM_EVENT_COUNTERS=y -# CONFIG_SLUB_DEBUG is not set CONFIG_COMPAT_BRK=y -# CONFIG_SLAB is not set -CONFIG_SLUB=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set # CONFIG_SLOB is not set # CONFIG_PROFILING is not set -CONFIG_TRACEPOINTS=y CONFIG_HAVE_OPROFILE=y # CONFIG_KPROBES is not set CONFIG_HAVE_KPROBES=y @@ -150,8 +147,8 @@ CONFIG_HAVE_DMA_API_DEBUG=y # # GCOV-based kernel profiling # -# CONFIG_GCOV_KERNEL is not set CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y CONFIG_RT_MUTEXES=y CONFIG_BASE_SMALL=0 CONFIG_MODULES=y @@ -164,7 +161,6 @@ CONFIG_BLOCK=y CONFIG_LBDAF=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_BLK_DEV_INTEGRITY is not set -# CONFIG_BLK_DEV_THROTTLING is not set # # IO Schedulers @@ -172,14 +168,13 @@ CONFIG_LBDAF=y CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y -CONFIG_CFQ_GROUP_IOSCHED=y CONFIG_IOSCHED_BFQ=y -CONFIG_CGROUP_BFQIO=y -CONFIG_DEFAULT_DEADLINE=y -# CONFIG_DEFAULT_CFQ is not set +# CONFIG_CGROUP_BFQIO is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y # CONFIG_DEFAULT_BFQ is not set # CONFIG_DEFAULT_NOOP is not set -CONFIG_DEFAULT_IOSCHED="deadline" +CONFIG_DEFAULT_IOSCHED="cfq" # CONFIG_INLINE_SPIN_TRYLOCK is not set # CONFIG_INLINE_SPIN_TRYLOCK_BH is not set # CONFIG_INLINE_SPIN_LOCK is not set @@ -436,8 +431,7 @@ CONFIG_FLAT_NODE_MEM_MAP=y CONFIG_HAVE_MEMBLOCK=y CONFIG_PAGEFLAGS_EXTENDED=y CONFIG_SPLIT_PTLOCK_CPUS=4 -CONFIG_COMPACTION=y -CONFIG_MIGRATION=y +# CONFIG_COMPACTION is not set # CONFIG_PHYS_ADDR_T_64BIT is not set CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y @@ -642,7 +636,7 @@ CONFIG_NETFILTER_XT_TARGET_CONNMARK=y # CONFIG_NETFILTER_XT_TARGET_CT is not set # CONFIG_NETFILTER_XT_TARGET_DSCP is not set CONFIG_NETFILTER_XT_TARGET_HL=y -CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set @@ -845,7 +839,6 @@ CONFIG_DNS_RESOLVER=y # Network testing # # CONFIG_NET_PKTGEN is not set -# CONFIG_NET_DROP_MONITOR is not set # CONFIG_HAMRADIO is not set # CONFIG_CAN is not set # CONFIG_IRDA is not set @@ -1574,7 +1567,6 @@ CONFIG_USB_ARCH_HAS_EHCI=y CONFIG_USB_GADGET=y # CONFIG_USB_GADGET_DEBUG is not set # CONFIG_USB_GADGET_DEBUG_FILES is not set -# CONFIG_USB_GADGET_DEBUG_FS is not set CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_USB_GADGET_SELECTED=y # CONFIG_USB_GADGET_R8A66597 is not set @@ -1811,7 +1803,6 @@ CONFIG_EXT4_FS_XATTR=y # CONFIG_EXT4_FS_SECURITY is not set # CONFIG_EXT4_DEBUG is not set CONFIG_JBD2=y -# CONFIG_JBD2_DEBUG is not set CONFIG_FS_MBCACHE=y # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set @@ -1966,7 +1957,7 @@ CONFIG_FRAME_WARN=1024 CONFIG_MAGIC_SYSRQ=y # CONFIG_STRIP_ASM_SYMS is not set # CONFIG_UNUSED_SYMBOLS is not set -CONFIG_DEBUG_FS=y +# CONFIG_DEBUG_FS is not set # CONFIG_HEADERS_CHECK is not set CONFIG_DEBUG_KERNEL=y # CONFIG_DEBUG_SHIRQ is not set @@ -1977,7 +1968,7 @@ CONFIG_SCHED_DEBUG=y CONFIG_SCHEDSTATS=y CONFIG_TIMER_STATS=y # CONFIG_DEBUG_OBJECTS is not set -# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_SLAB is not set # CONFIG_DEBUG_KMEMLEAK is not set # CONFIG_DEBUG_PREEMPT is not set # CONFIG_DEBUG_RT_MUTEXES is not set @@ -2009,38 +2000,17 @@ CONFIG_DEBUG_INFO=y # CONFIG_BACKTRACE_SELF_TEST is not set # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set # CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set -# CONFIG_LKDTM is not set # CONFIG_FAULT_INJECTION is not set # CONFIG_LATENCYTOP is not set # CONFIG_SYSCTL_SYSCALL_CHECK is not set # CONFIG_PAGE_POISONING is not set -CONFIG_NOP_TRACER=y CONFIG_HAVE_FUNCTION_TRACER=y CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y CONFIG_HAVE_DYNAMIC_FTRACE=y CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y CONFIG_HAVE_C_RECORDMCOUNT=y -CONFIG_TRACER_MAX_TRACE=y -CONFIG_RING_BUFFER=y -CONFIG_EVENT_TRACING=y -CONFIG_EVENT_POWER_TRACING_DEPRECATED=y -CONFIG_CONTEXT_SWITCH_TRACER=y -CONFIG_TRACING=y -CONFIG_GENERIC_TRACER=y CONFIG_TRACING_SUPPORT=y -CONFIG_FTRACE=y -# CONFIG_FUNCTION_TRACER is not set -# CONFIG_IRQSOFF_TRACER is not set -# CONFIG_PREEMPT_TRACER is not set -CONFIG_SCHED_TRACER=y -CONFIG_BRANCH_PROFILE_NONE=y -# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set -# CONFIG_PROFILE_ALL_BRANCHES is not set -# CONFIG_STACK_TRACER is not set -# CONFIG_BLK_DEV_IO_TRACE is not set -# CONFIG_FTRACE_STARTUP_TEST is not set -# CONFIG_RING_BUFFER_BENCHMARK is not set -# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_FTRACE is not set # CONFIG_DMA_API_DEBUG is not set # CONFIG_ATOMIC64_SELFTEST is not set # CONFIG_SAMPLES is not set @@ -2167,7 +2137,7 @@ CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_USER_API_HASH is not set # CONFIG_CRYPTO_USER_API_SKCIPHER is not set CONFIG_CRYPTO_HW=y -CONFIG_BINARY_PRINTF=y +# CONFIG_BINARY_PRINTF is not set # # Library routines diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index ffbed07e84d91..b6eafa315312d 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sat Jul 14 00:31:49 2012 +# Mon May 21 22:18:23 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -34,15 +34,15 @@ CONFIG_BROKEN_ON_SMP=y CONFIG_LOCK_KERNEL=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" -CONFIG_LOCALVERSION="-evervolv-acies-jellybean" +CONFIG_LOCALVERSION="-evervolv-acies-ics" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y CONFIG_HAVE_KERNEL_XZ=y CONFIG_HAVE_KERNEL_LZO=y # CONFIG_KERNEL_GZIP is not set -# CONFIG_KERNEL_LZMA is not set -CONFIG_KERNEL_XZ=y +CONFIG_KERNEL_LZMA=y +# CONFIG_KERNEL_XZ is not set # CONFIG_KERNEL_LZO is not set CONFIG_SWAP=y # CONFIG_SYSVIPC is not set @@ -138,15 +138,13 @@ CONFIG_PERF_USE_VMALLOC=y # Kernel Performance Events And Counters # # CONFIG_PERF_EVENTS is not set -CONFIG_PERF_COUNTERS=y +# CONFIG_PERF_COUNTERS is not set CONFIG_VM_EVENT_COUNTERS=y -# CONFIG_SLUB_DEBUG is not set CONFIG_COMPAT_BRK=y -# CONFIG_SLAB is not set -CONFIG_SLUB=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set # CONFIG_SLOB is not set # CONFIG_PROFILING is not set -CONFIG_TRACEPOINTS=y CONFIG_HAVE_OPROFILE=y # CONFIG_KPROBES is not set CONFIG_HAVE_KPROBES=y @@ -160,6 +158,7 @@ CONFIG_HAVE_DMA_API_DEBUG=y # # CONFIG_GCOV_KERNEL is not set CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y CONFIG_RT_MUTEXES=y CONFIG_BASE_SMALL=0 CONFIG_MODULES=y @@ -178,16 +177,13 @@ CONFIG_BLK_DEV_THROTTLING=y # IO Schedulers # CONFIG_IOSCHED_NOOP=y -CONFIG_IOSCHED_DEADLINE=y -CONFIG_IOSCHED_CFQ=y -CONFIG_CFQ_GROUP_IOSCHED=y +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set CONFIG_IOSCHED_BFQ=y CONFIG_CGROUP_BFQIO=y -CONFIG_DEFAULT_DEADLINE=y -# CONFIG_DEFAULT_CFQ is not set -# CONFIG_DEFAULT_BFQ is not set +CONFIG_DEFAULT_BFQ=y # CONFIG_DEFAULT_NOOP is not set -CONFIG_DEFAULT_IOSCHED="deadline" +CONFIG_DEFAULT_IOSCHED="bfq" # CONFIG_INLINE_SPIN_TRYLOCK is not set # CONFIG_INLINE_SPIN_TRYLOCK_BH is not set # CONFIG_INLINE_SPIN_LOCK is not set @@ -441,8 +437,7 @@ CONFIG_FLAT_NODE_MEM_MAP=y CONFIG_HAVE_MEMBLOCK=y CONFIG_PAGEFLAGS_EXTENDED=y CONFIG_SPLIT_PTLOCK_CPUS=4 -CONFIG_COMPACTION=y -CONFIG_MIGRATION=y +# CONFIG_COMPACTION is not set # CONFIG_PHYS_ADDR_T_64BIT is not set CONFIG_ZONE_DMA_FLAG=0 CONFIG_VIRT_TO_BUS=y @@ -649,7 +644,7 @@ CONFIG_NETFILTER_XT_TARGET_CONNMARK=y # CONFIG_NETFILTER_XT_TARGET_CT is not set # CONFIG_NETFILTER_XT_TARGET_DSCP is not set CONFIG_NETFILTER_XT_TARGET_HL=y -CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set @@ -854,7 +849,6 @@ CONFIG_NET_SCH_FIFO=y # Network testing # # CONFIG_NET_PKTGEN is not set -# CONFIG_NET_DROP_MONITOR is not set # CONFIG_HAMRADIO is not set # CONFIG_CAN is not set # CONFIG_IRDA is not set @@ -1965,7 +1959,7 @@ CONFIG_SCHED_DEBUG=y CONFIG_SCHEDSTATS=y CONFIG_TIMER_STATS=y # CONFIG_DEBUG_OBJECTS is not set -# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_SLAB is not set # CONFIG_DEBUG_KMEMLEAK is not set # CONFIG_DEBUG_PREEMPT is not set # CONFIG_DEBUG_RT_MUTEXES is not set @@ -2002,32 +1996,13 @@ CONFIG_FRAME_POINTER=y # CONFIG_LATENCYTOP is not set # CONFIG_SYSCTL_SYSCALL_CHECK is not set # CONFIG_PAGE_POISONING is not set -CONFIG_NOP_TRACER=y CONFIG_HAVE_FUNCTION_TRACER=y CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y CONFIG_HAVE_DYNAMIC_FTRACE=y CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y CONFIG_HAVE_C_RECORDMCOUNT=y -CONFIG_TRACER_MAX_TRACE=y -CONFIG_RING_BUFFER=y -CONFIG_EVENT_TRACING=y -CONFIG_EVENT_POWER_TRACING_DEPRECATED=y -CONFIG_CONTEXT_SWITCH_TRACER=y -CONFIG_TRACING=y -CONFIG_GENERIC_TRACER=y CONFIG_TRACING_SUPPORT=y -CONFIG_FTRACE=y -# CONFIG_FUNCTION_TRACER is not set -# CONFIG_IRQSOFF_TRACER is not set -# CONFIG_PREEMPT_TRACER is not set -CONFIG_SCHED_TRACER=y -CONFIG_BRANCH_PROFILE_NONE=y -# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set -# CONFIG_PROFILE_ALL_BRANCHES is not set -# CONFIG_STACK_TRACER is not set -# CONFIG_BLK_DEV_IO_TRACE is not set -# CONFIG_FTRACE_STARTUP_TEST is not set -# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_FTRACE is not set # CONFIG_DYNAMIC_DEBUG is not set # CONFIG_DMA_API_DEBUG is not set # CONFIG_ATOMIC64_SELFTEST is not set @@ -2154,7 +2129,7 @@ CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_USER_API_HASH is not set # CONFIG_CRYPTO_USER_API_SKCIPHER is not set CONFIG_CRYPTO_HW=y -CONFIG_BINARY_PRINTF=y +# CONFIG_BINARY_PRINTF is not set # # Library routines From 0de3bb50a20425aea1fd85cad6cfb88eab2aaf64 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 15 Jul 2012 12:48:30 -0500 Subject: [PATCH 2299/2556] configs: evervolv: inc/supersonic update for jellybean --- .../configs/evervolv_incrediblec_defconfig | 40 +++++++++++++++---- .../arm/configs/evervolv_supersonic_defconfig | 31 +++++++++++--- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index b1725229cc98e..f474bf5571b6a 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Mon May 21 22:18:06 2012 +# Sun Jul 15 12:47:31 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -33,7 +33,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="arm-eabi-" -CONFIG_LOCALVERSION="-evervolv-dives-ics" +CONFIG_LOCALVERSION="-evervolv-dives-jellybean" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y @@ -129,13 +129,14 @@ CONFIG_PERF_USE_VMALLOC=y # Kernel Performance Events And Counters # # CONFIG_PERF_EVENTS is not set -# CONFIG_PERF_COUNTERS is not set +CONFIG_PERF_COUNTERS=y CONFIG_VM_EVENT_COUNTERS=y CONFIG_COMPAT_BRK=y CONFIG_SLAB=y # CONFIG_SLUB is not set # CONFIG_SLOB is not set # CONFIG_PROFILING is not set +CONFIG_TRACEPOINTS=y CONFIG_HAVE_OPROFILE=y # CONFIG_KPROBES is not set CONFIG_HAVE_KPROBES=y @@ -147,6 +148,7 @@ CONFIG_HAVE_DMA_API_DEBUG=y # # GCOV-based kernel profiling # +# CONFIG_GCOV_KERNEL is not set CONFIG_HAVE_GENERIC_DMA_COHERENT=y CONFIG_SLABINFO=y CONFIG_RT_MUTEXES=y @@ -636,7 +638,7 @@ CONFIG_NETFILTER_XT_TARGET_CONNMARK=y # CONFIG_NETFILTER_XT_TARGET_CT is not set # CONFIG_NETFILTER_XT_TARGET_DSCP is not set CONFIG_NETFILTER_XT_TARGET_HL=y -# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set @@ -839,6 +841,7 @@ CONFIG_DNS_RESOLVER=y # Network testing # # CONFIG_NET_PKTGEN is not set +# CONFIG_NET_DROP_MONITOR is not set # CONFIG_HAMRADIO is not set # CONFIG_CAN is not set # CONFIG_IRDA is not set @@ -1567,6 +1570,7 @@ CONFIG_USB_ARCH_HAS_EHCI=y CONFIG_USB_GADGET=y # CONFIG_USB_GADGET_DEBUG is not set # CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set CONFIG_USB_GADGET_VBUS_DRAW=500 CONFIG_USB_GADGET_SELECTED=y # CONFIG_USB_GADGET_R8A66597 is not set @@ -1803,6 +1807,7 @@ CONFIG_EXT4_FS_XATTR=y # CONFIG_EXT4_FS_SECURITY is not set # CONFIG_EXT4_DEBUG is not set CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set CONFIG_FS_MBCACHE=y # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set @@ -1957,7 +1962,7 @@ CONFIG_FRAME_WARN=1024 CONFIG_MAGIC_SYSRQ=y # CONFIG_STRIP_ASM_SYMS is not set # CONFIG_UNUSED_SYMBOLS is not set -# CONFIG_DEBUG_FS is not set +CONFIG_DEBUG_FS=y # CONFIG_HEADERS_CHECK is not set CONFIG_DEBUG_KERNEL=y # CONFIG_DEBUG_SHIRQ is not set @@ -2000,17 +2005,38 @@ CONFIG_DEBUG_INFO=y # CONFIG_BACKTRACE_SELF_TEST is not set # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set # CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set # CONFIG_FAULT_INJECTION is not set # CONFIG_LATENCYTOP is not set # CONFIG_SYSCTL_SYSCALL_CHECK is not set # CONFIG_PAGE_POISONING is not set +CONFIG_NOP_TRACER=y CONFIG_HAVE_FUNCTION_TRACER=y CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y CONFIG_HAVE_DYNAMIC_FTRACE=y CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACER_MAX_TRACE=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_EVENT_POWER_TRACING_DEPRECATED=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y CONFIG_TRACING_SUPPORT=y -# CONFIG_FTRACE is not set +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +CONFIG_SCHED_TRACER=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_DYNAMIC_DEBUG is not set # CONFIG_DMA_API_DEBUG is not set # CONFIG_ATOMIC64_SELFTEST is not set # CONFIG_SAMPLES is not set @@ -2137,7 +2163,7 @@ CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_USER_API_HASH is not set # CONFIG_CRYPTO_USER_API_SKCIPHER is not set CONFIG_CRYPTO_HW=y -# CONFIG_BINARY_PRINTF is not set +CONFIG_BINARY_PRINTF=y # # Library routines diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index b6eafa315312d..b070bc3a8e93d 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Mon May 21 22:18:23 2012 +# Sun Jul 15 12:45:43 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -138,13 +138,14 @@ CONFIG_PERF_USE_VMALLOC=y # Kernel Performance Events And Counters # # CONFIG_PERF_EVENTS is not set -# CONFIG_PERF_COUNTERS is not set +CONFIG_PERF_COUNTERS=y CONFIG_VM_EVENT_COUNTERS=y CONFIG_COMPAT_BRK=y CONFIG_SLAB=y # CONFIG_SLUB is not set # CONFIG_SLOB is not set # CONFIG_PROFILING is not set +CONFIG_TRACEPOINTS=y CONFIG_HAVE_OPROFILE=y # CONFIG_KPROBES is not set CONFIG_HAVE_KPROBES=y @@ -644,7 +645,7 @@ CONFIG_NETFILTER_XT_TARGET_CONNMARK=y # CONFIG_NETFILTER_XT_TARGET_CT is not set # CONFIG_NETFILTER_XT_TARGET_DSCP is not set CONFIG_NETFILTER_XT_TARGET_HL=y -# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y # CONFIG_NETFILTER_XT_TARGET_NFLOG is not set @@ -849,6 +850,7 @@ CONFIG_NET_SCH_FIFO=y # Network testing # # CONFIG_NET_PKTGEN is not set +# CONFIG_NET_DROP_MONITOR is not set # CONFIG_HAMRADIO is not set # CONFIG_CAN is not set # CONFIG_IRDA is not set @@ -1996,13 +1998,32 @@ CONFIG_FRAME_POINTER=y # CONFIG_LATENCYTOP is not set # CONFIG_SYSCTL_SYSCALL_CHECK is not set # CONFIG_PAGE_POISONING is not set +CONFIG_NOP_TRACER=y CONFIG_HAVE_FUNCTION_TRACER=y CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y CONFIG_HAVE_DYNAMIC_FTRACE=y CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACER_MAX_TRACE=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_EVENT_POWER_TRACING_DEPRECATED=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y CONFIG_TRACING_SUPPORT=y -# CONFIG_FTRACE is not set +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +CONFIG_SCHED_TRACER=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set # CONFIG_DYNAMIC_DEBUG is not set # CONFIG_DMA_API_DEBUG is not set # CONFIG_ATOMIC64_SELFTEST is not set @@ -2129,7 +2150,7 @@ CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_USER_API_HASH is not set # CONFIG_CRYPTO_USER_API_SKCIPHER is not set CONFIG_CRYPTO_HW=y -# CONFIG_BINARY_PRINTF is not set +CONFIG_BINARY_PRINTF=y # # Library routines From 49b658bf4ce5ab0ae3507fe64e1421392ccae313 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 15 Jul 2012 15:34:18 -0500 Subject: [PATCH 2300/2556] configs: evervolv unset PERF_COUNTERS --- arch/arm/configs/evervolv_bravo_defconfig | 2 +- arch/arm/configs/evervolv_incrediblec_defconfig | 2 +- arch/arm/configs/evervolv_mahimahi_defconfig | 2 +- arch/arm/configs/evervolv_supersonic_defconfig | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index cd17a1bf9ec71..b1b759ba76317 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -131,7 +131,7 @@ CONFIG_PERF_USE_VMALLOC=y # Kernel Performance Events And Counters # # CONFIG_PERF_EVENTS is not set -CONFIG_PERF_COUNTERS=y +# CONFIG_PERF_COUNTERS is not set CONFIG_VM_EVENT_COUNTERS=y # CONFIG_SLUB_DEBUG is not set CONFIG_COMPAT_BRK=y diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index f474bf5571b6a..a5fbe6c62af59 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -129,7 +129,7 @@ CONFIG_PERF_USE_VMALLOC=y # Kernel Performance Events And Counters # # CONFIG_PERF_EVENTS is not set -CONFIG_PERF_COUNTERS=y +# CONFIG_PERF_COUNTERS is not set CONFIG_VM_EVENT_COUNTERS=y CONFIG_COMPAT_BRK=y CONFIG_SLAB=y diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index f15ff2f044cdb..785b595b57a75 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -131,7 +131,7 @@ CONFIG_PERF_USE_VMALLOC=y # Kernel Performance Events And Counters # # CONFIG_PERF_EVENTS is not set -CONFIG_PERF_COUNTERS=y +# CONFIG_PERF_COUNTERS is not set CONFIG_VM_EVENT_COUNTERS=y # CONFIG_SLUB_DEBUG is not set CONFIG_COMPAT_BRK=y diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index b070bc3a8e93d..48e1160f38c57 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -138,7 +138,7 @@ CONFIG_PERF_USE_VMALLOC=y # Kernel Performance Events And Counters # # CONFIG_PERF_EVENTS is not set -CONFIG_PERF_COUNTERS=y +# CONFIG_PERF_COUNTERS is not set CONFIG_VM_EVENT_COUNTERS=y CONFIG_COMPAT_BRK=y CONFIG_SLAB=y From 766dad5ba525f2bc8288209696bea2bd7ea818c4 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Mon, 25 Jun 2012 01:08:33 -0500 Subject: [PATCH 2301/2556] drivers: wireless: bcmdhd: from grouper 3.1 per 02a7a4f net: wireless: bcmdhd: Combined P2P fixes --- drivers/net/wireless/Kconfig | 1 + drivers/net/wireless/Makefile | 1 + drivers/net/wireless/bcmdhd/Kconfig | 54 + drivers/net/wireless/bcmdhd/Makefile | 38 + drivers/net/wireless/bcmdhd/aiutils.c | 675 ++ drivers/net/wireless/bcmdhd/bcmevent.c | 127 + drivers/net/wireless/bcmdhd/bcmsdh.c | 690 ++ drivers/net/wireless/bcmdhd/bcmsdh_linux.c | 727 ++ drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c | 1407 +++ .../net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c | 368 + drivers/net/wireless/bcmdhd/bcmutils.c | 1965 ++++ drivers/net/wireless/bcmdhd/bcmwifi.c | 274 + drivers/net/wireless/bcmdhd/dhd.h | 758 ++ drivers/net/wireless/bcmdhd/dhd_bta.c | 335 + drivers/net/wireless/bcmdhd/dhd_bta.h | 39 + drivers/net/wireless/bcmdhd/dhd_bus.h | 99 + drivers/net/wireless/bcmdhd/dhd_cdc.c | 2534 +++++ drivers/net/wireless/bcmdhd/dhd_cfg80211.c | 655 ++ drivers/net/wireless/bcmdhd/dhd_cfg80211.h | 45 + drivers/net/wireless/bcmdhd/dhd_common.c | 2451 +++++ drivers/net/wireless/bcmdhd/dhd_custom_gpio.c | 293 + drivers/net/wireless/bcmdhd/dhd_dbg.h | 105 + drivers/net/wireless/bcmdhd/dhd_linux.c | 5366 ++++++++++ drivers/net/wireless/bcmdhd/dhd_linux_mon.c | 393 + drivers/net/wireless/bcmdhd/dhd_linux_sched.c | 39 + drivers/net/wireless/bcmdhd/dhd_proto.h | 105 + drivers/net/wireless/bcmdhd/dhd_sdio.c | 6333 ++++++++++++ drivers/net/wireless/bcmdhd/dhd_wlfc.h | 276 + drivers/net/wireless/bcmdhd/dngl_stats.h | 43 + drivers/net/wireless/bcmdhd/dngl_wlhdr.h | 40 + drivers/net/wireless/bcmdhd/hndpmu.c | 196 + drivers/net/wireless/bcmdhd/include/Makefile | 53 + drivers/net/wireless/bcmdhd/include/aidmp.h | 377 + drivers/net/wireless/bcmdhd/include/bcmcdc.h | 121 + drivers/net/wireless/bcmdhd/include/bcmdefs.h | 231 + drivers/net/wireless/bcmdhd/include/bcmdevs.h | 747 ++ .../net/wireless/bcmdhd/include/bcmendian.h | 279 + .../net/wireless/bcmdhd/include/bcmpcispi.h | 181 + drivers/net/wireless/bcmdhd/include/bcmperf.h | 36 + .../net/wireless/bcmdhd/include/bcmsdbus.h | 128 + drivers/net/wireless/bcmdhd/include/bcmsdh.h | 219 + .../wireless/bcmdhd/include/bcmsdh_sdmmc.h | 123 + .../net/wireless/bcmdhd/include/bcmsdpcm.h | 274 + .../net/wireless/bcmdhd/include/bcmsdspi.h | 135 + .../net/wireless/bcmdhd/include/bcmsdstd.h | 248 + drivers/net/wireless/bcmdhd/include/bcmspi.h | 40 + .../net/wireless/bcmdhd/include/bcmutils.h | 720 ++ drivers/net/wireless/bcmdhd/include/bcmwifi.h | 165 + .../net/wireless/bcmdhd/include/dhdioctl.h | 131 + drivers/net/wireless/bcmdhd/include/epivers.h | 49 + drivers/net/wireless/bcmdhd/include/hndpmu.h | 37 + .../wireless/bcmdhd/include/hndrte_armtrap.h | 88 + .../net/wireless/bcmdhd/include/hndrte_cons.h | 68 + drivers/net/wireless/bcmdhd/include/hndsoc.h | 207 + drivers/net/wireless/bcmdhd/include/htsf.h | 74 + .../net/wireless/bcmdhd/include/linux_osl.h | 431 + .../net/wireless/bcmdhd/include/linuxver.h | 614 ++ drivers/net/wireless/bcmdhd/include/miniopt.h | 77 + .../net/wireless/bcmdhd/include/msgtrace.h | 74 + drivers/net/wireless/bcmdhd/include/osl.h | 66 + .../bcmdhd/include/packed_section_end.h | 54 + .../bcmdhd/include/packed_section_start.h | 61 + drivers/net/wireless/bcmdhd/include/pcicfg.h | 78 + .../wireless/bcmdhd/include/proto/802.11.h | 2032 ++++ .../bcmdhd/include/proto/802.11_bta.h | 45 + .../wireless/bcmdhd/include/proto/802.11e.h | 131 + .../wireless/bcmdhd/include/proto/802.1d.h | 49 + .../wireless/bcmdhd/include/proto/bcmeth.h | 83 + .../wireless/bcmdhd/include/proto/bcmevent.h | 317 + .../net/wireless/bcmdhd/include/proto/bcmip.h | 154 + .../bcmdhd/include/proto/bt_amp_hci.h | 442 + .../net/wireless/bcmdhd/include/proto/eapol.h | 173 + .../wireless/bcmdhd/include/proto/ethernet.h | 163 + .../net/wireless/bcmdhd/include/proto/p2p.h | 512 + .../net/wireless/bcmdhd/include/proto/sdspi.h | 76 + .../net/wireless/bcmdhd/include/proto/vlan.h | 70 + .../net/wireless/bcmdhd/include/proto/wpa.h | 168 + drivers/net/wireless/bcmdhd/include/sbchipc.h | 1778 ++++ .../net/wireless/bcmdhd/include/sbconfig.h | 276 + .../net/wireless/bcmdhd/include/sbhnddma.h | 327 + .../net/wireless/bcmdhd/include/sbpcmcia.h | 109 + drivers/net/wireless/bcmdhd/include/sbsdio.h | 166 + .../net/wireless/bcmdhd/include/sbsdpcmdev.h | 293 + .../net/wireless/bcmdhd/include/sbsocram.h | 186 + drivers/net/wireless/bcmdhd/include/sdio.h | 612 ++ drivers/net/wireless/bcmdhd/include/sdioh.h | 442 + drivers/net/wireless/bcmdhd/include/sdiovar.h | 58 + drivers/net/wireless/bcmdhd/include/siutils.h | 277 + drivers/net/wireless/bcmdhd/include/trxhdr.h | 53 + .../net/wireless/bcmdhd/include/typedefs.h | 312 + .../net/wireless/bcmdhd/include/wlfc_proto.h | 198 + drivers/net/wireless/bcmdhd/include/wlioctl.h | 2811 ++++++ drivers/net/wireless/bcmdhd/linux_osl.c | 924 ++ drivers/net/wireless/bcmdhd/sbutils.c | 992 ++ drivers/net/wireless/bcmdhd/siutils.c | 1915 ++++ drivers/net/wireless/bcmdhd/siutils_priv.h | 235 + drivers/net/wireless/bcmdhd/uamp_api.h | 176 + drivers/net/wireless/bcmdhd/wl_android.c | 858 ++ drivers/net/wireless/bcmdhd/wl_android.h | 57 + drivers/net/wireless/bcmdhd/wl_cfg80211.c | 7730 ++++++++++++++ drivers/net/wireless/bcmdhd/wl_cfg80211.h | 673 ++ drivers/net/wireless/bcmdhd/wl_cfgp2p.c | 2000 ++++ drivers/net/wireless/bcmdhd/wl_cfgp2p.h | 283 + drivers/net/wireless/bcmdhd/wl_dbg.h | 49 + drivers/net/wireless/bcmdhd/wl_iw.c | 8894 +++++++++++++++++ drivers/net/wireless/bcmdhd/wl_iw.h | 319 + drivers/net/wireless/bcmdhd/wl_linux_mon.c | 409 + drivers/net/wireless/bcmdhd/wldev_common.c | 418 + drivers/net/wireless/bcmdhd/wldev_common.h | 112 + 109 files changed, 70975 insertions(+) create mode 100644 drivers/net/wireless/bcmdhd/Kconfig create mode 100644 drivers/net/wireless/bcmdhd/Makefile create mode 100644 drivers/net/wireless/bcmdhd/aiutils.c create mode 100644 drivers/net/wireless/bcmdhd/bcmevent.c create mode 100644 drivers/net/wireless/bcmdhd/bcmsdh.c create mode 100644 drivers/net/wireless/bcmdhd/bcmsdh_linux.c create mode 100644 drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c create mode 100644 drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c create mode 100644 drivers/net/wireless/bcmdhd/bcmutils.c create mode 100644 drivers/net/wireless/bcmdhd/bcmwifi.c create mode 100644 drivers/net/wireless/bcmdhd/dhd.h create mode 100644 drivers/net/wireless/bcmdhd/dhd_bta.c create mode 100644 drivers/net/wireless/bcmdhd/dhd_bta.h create mode 100644 drivers/net/wireless/bcmdhd/dhd_bus.h create mode 100644 drivers/net/wireless/bcmdhd/dhd_cdc.c create mode 100644 drivers/net/wireless/bcmdhd/dhd_cfg80211.c create mode 100644 drivers/net/wireless/bcmdhd/dhd_cfg80211.h create mode 100644 drivers/net/wireless/bcmdhd/dhd_common.c create mode 100644 drivers/net/wireless/bcmdhd/dhd_custom_gpio.c create mode 100644 drivers/net/wireless/bcmdhd/dhd_dbg.h create mode 100644 drivers/net/wireless/bcmdhd/dhd_linux.c create mode 100644 drivers/net/wireless/bcmdhd/dhd_linux_mon.c create mode 100644 drivers/net/wireless/bcmdhd/dhd_linux_sched.c create mode 100644 drivers/net/wireless/bcmdhd/dhd_proto.h create mode 100644 drivers/net/wireless/bcmdhd/dhd_sdio.c create mode 100644 drivers/net/wireless/bcmdhd/dhd_wlfc.h create mode 100644 drivers/net/wireless/bcmdhd/dngl_stats.h create mode 100644 drivers/net/wireless/bcmdhd/dngl_wlhdr.h create mode 100644 drivers/net/wireless/bcmdhd/hndpmu.c create mode 100644 drivers/net/wireless/bcmdhd/include/Makefile create mode 100644 drivers/net/wireless/bcmdhd/include/aidmp.h create mode 100644 drivers/net/wireless/bcmdhd/include/bcmcdc.h create mode 100644 drivers/net/wireless/bcmdhd/include/bcmdefs.h create mode 100644 drivers/net/wireless/bcmdhd/include/bcmdevs.h create mode 100644 drivers/net/wireless/bcmdhd/include/bcmendian.h create mode 100644 drivers/net/wireless/bcmdhd/include/bcmpcispi.h create mode 100644 drivers/net/wireless/bcmdhd/include/bcmperf.h create mode 100644 drivers/net/wireless/bcmdhd/include/bcmsdbus.h create mode 100644 drivers/net/wireless/bcmdhd/include/bcmsdh.h create mode 100644 drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h create mode 100644 drivers/net/wireless/bcmdhd/include/bcmsdpcm.h create mode 100644 drivers/net/wireless/bcmdhd/include/bcmsdspi.h create mode 100644 drivers/net/wireless/bcmdhd/include/bcmsdstd.h create mode 100644 drivers/net/wireless/bcmdhd/include/bcmspi.h create mode 100644 drivers/net/wireless/bcmdhd/include/bcmutils.h create mode 100644 drivers/net/wireless/bcmdhd/include/bcmwifi.h create mode 100644 drivers/net/wireless/bcmdhd/include/dhdioctl.h create mode 100644 drivers/net/wireless/bcmdhd/include/epivers.h create mode 100644 drivers/net/wireless/bcmdhd/include/hndpmu.h create mode 100644 drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h create mode 100644 drivers/net/wireless/bcmdhd/include/hndrte_cons.h create mode 100644 drivers/net/wireless/bcmdhd/include/hndsoc.h create mode 100644 drivers/net/wireless/bcmdhd/include/htsf.h create mode 100644 drivers/net/wireless/bcmdhd/include/linux_osl.h create mode 100644 drivers/net/wireless/bcmdhd/include/linuxver.h create mode 100644 drivers/net/wireless/bcmdhd/include/miniopt.h create mode 100644 drivers/net/wireless/bcmdhd/include/msgtrace.h create mode 100644 drivers/net/wireless/bcmdhd/include/osl.h create mode 100644 drivers/net/wireless/bcmdhd/include/packed_section_end.h create mode 100644 drivers/net/wireless/bcmdhd/include/packed_section_start.h create mode 100644 drivers/net/wireless/bcmdhd/include/pcicfg.h create mode 100644 drivers/net/wireless/bcmdhd/include/proto/802.11.h create mode 100644 drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h create mode 100644 drivers/net/wireless/bcmdhd/include/proto/802.11e.h create mode 100644 drivers/net/wireless/bcmdhd/include/proto/802.1d.h create mode 100644 drivers/net/wireless/bcmdhd/include/proto/bcmeth.h create mode 100644 drivers/net/wireless/bcmdhd/include/proto/bcmevent.h create mode 100644 drivers/net/wireless/bcmdhd/include/proto/bcmip.h create mode 100644 drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h create mode 100644 drivers/net/wireless/bcmdhd/include/proto/eapol.h create mode 100644 drivers/net/wireless/bcmdhd/include/proto/ethernet.h create mode 100644 drivers/net/wireless/bcmdhd/include/proto/p2p.h create mode 100644 drivers/net/wireless/bcmdhd/include/proto/sdspi.h create mode 100644 drivers/net/wireless/bcmdhd/include/proto/vlan.h create mode 100644 drivers/net/wireless/bcmdhd/include/proto/wpa.h create mode 100644 drivers/net/wireless/bcmdhd/include/sbchipc.h create mode 100644 drivers/net/wireless/bcmdhd/include/sbconfig.h create mode 100644 drivers/net/wireless/bcmdhd/include/sbhnddma.h create mode 100644 drivers/net/wireless/bcmdhd/include/sbpcmcia.h create mode 100644 drivers/net/wireless/bcmdhd/include/sbsdio.h create mode 100644 drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h create mode 100644 drivers/net/wireless/bcmdhd/include/sbsocram.h create mode 100644 drivers/net/wireless/bcmdhd/include/sdio.h create mode 100644 drivers/net/wireless/bcmdhd/include/sdioh.h create mode 100644 drivers/net/wireless/bcmdhd/include/sdiovar.h create mode 100644 drivers/net/wireless/bcmdhd/include/siutils.h create mode 100644 drivers/net/wireless/bcmdhd/include/trxhdr.h create mode 100644 drivers/net/wireless/bcmdhd/include/typedefs.h create mode 100644 drivers/net/wireless/bcmdhd/include/wlfc_proto.h create mode 100644 drivers/net/wireless/bcmdhd/include/wlioctl.h create mode 100644 drivers/net/wireless/bcmdhd/linux_osl.c create mode 100644 drivers/net/wireless/bcmdhd/sbutils.c create mode 100644 drivers/net/wireless/bcmdhd/siutils.c create mode 100644 drivers/net/wireless/bcmdhd/siutils_priv.h create mode 100644 drivers/net/wireless/bcmdhd/uamp_api.h create mode 100644 drivers/net/wireless/bcmdhd/wl_android.c create mode 100644 drivers/net/wireless/bcmdhd/wl_android.h create mode 100644 drivers/net/wireless/bcmdhd/wl_cfg80211.c create mode 100644 drivers/net/wireless/bcmdhd/wl_cfg80211.h create mode 100644 drivers/net/wireless/bcmdhd/wl_cfgp2p.c create mode 100644 drivers/net/wireless/bcmdhd/wl_cfgp2p.h create mode 100644 drivers/net/wireless/bcmdhd/wl_dbg.h create mode 100644 drivers/net/wireless/bcmdhd/wl_iw.c create mode 100644 drivers/net/wireless/bcmdhd/wl_iw.h create mode 100644 drivers/net/wireless/bcmdhd/wl_linux_mon.c create mode 100644 drivers/net/wireless/bcmdhd/wldev_common.c create mode 100644 drivers/net/wireless/bcmdhd/wldev_common.h diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index f5fbadaaaf4a9..e57f85790c63f 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -272,6 +272,7 @@ source "drivers/net/wireless/ath/Kconfig" source "drivers/net/wireless/b43/Kconfig" source "drivers/net/wireless/b43legacy/Kconfig" source "drivers/net/wireless/bcm4329/Kconfig" +source "drivers/net/wireless/bcmdhd/Kconfig" source "drivers/net/wireless/hostap/Kconfig" source "drivers/net/wireless/ipw2x00/Kconfig" source "drivers/net/wireless/iwlwifi/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 8d3feccf712b0..03c4bec05ff4f 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -57,3 +57,4 @@ obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx/ obj-$(CONFIG_IWM) += iwmc3200wifi/ obj-$(CONFIG_BCM4329) += bcm4329/ +obj-$(CONFIG_BCMDHD) += bcmdhd/ diff --git a/drivers/net/wireless/bcmdhd/Kconfig b/drivers/net/wireless/bcmdhd/Kconfig new file mode 100644 index 0000000000000..8b6b92f624374 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/Kconfig @@ -0,0 +1,54 @@ +config BCMDHD + tristate "Broadcom 4329/30 wireless cards support" + depends on MMC + ---help--- + This module adds support for wireless adapters based on + Broadcom 4329/30 chipset. + + This driver uses the kernel's wireless extensions subsystem. + + If you choose to build a module, it'll be called dhd. Say M if + unsure. + +config BCMDHD_FW_PATH + depends on BCMDHD + string "Firmware path" + default "/system/etc/firmware/fw_bcmdhd.bin" + ---help--- + Path to the firmware file. + +config BCMDHD_NVRAM_PATH + depends on BCMDHD + string "NVRAM path" + default "/system/etc/wifi/bcmdhd.cal" + ---help--- + Path to the calibration file. + +config BCMDHD_WEXT + bool "Enable WEXT support" + depends on BCMDHD && CFG80211 = n + select WIRELESS_EXT + select WEXT_PRIV + help + Enables WEXT support + +config DHD_USE_STATIC_BUF + bool "Enable memory preallocation" + depends on BCMDHD + default n + ---help--- + Use memory preallocated in platform + +config DHD_USE_SCHED_SCAN + bool "Use CFG80211 sched scan" + depends on BCMDHD && CFG80211 + default n + ---help--- + Use CFG80211 sched scan + +config DHD_ENABLE_P2P + bool "Enable Wifi Direct" + depends on BCMDHD && CFG80211 + default n + ---help--- + Use Enable Wifi Direct diff --git a/drivers/net/wireless/bcmdhd/Makefile b/drivers/net/wireless/bcmdhd/Makefile new file mode 100644 index 0000000000000..eda803e3ca798 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/Makefile @@ -0,0 +1,38 @@ +# bcmdhd +DHDCFLAGS = -Wall -Wstrict-prototypes -Dlinux -DBCMDRIVER \ + -DBCMDONGLEHOST -DUNRELEASEDCHIP -DBCMDMA32 -DWLBTAMP -DBCMFILEIMAGE \ + -DDHDTHREAD -DDHD_GPL -DDHD_SCHED -DDHD_DEBUG -DSDTEST -DBDC -DTOE \ + -DDHD_BCMEVENTS -DSHOW_EVENTS -DDONGLEOVERLAYS -DBCMDBG \ + -DCUSTOMER_HW2 -DCUSTOM_OOB_GPIO_NUM=2 -DOOB_INTR_ONLY -DHW_OOB \ + -DMMC_SDIO_ABORT -DBCMSDIO -DBCMLXSDMMC -DBCMPLATFORM_BUS -DWLP2P \ + -DNEW_COMPAT_WIRELESS -DWIFI_ACT_FRAME -DARP_OFFLOAD_SUPPORT \ + -DKEEP_ALIVE -DCSCAN -DGET_CUSTOM_MAC_ENABLE -DPKT_FILTER_SUPPORT \ + -DEMBEDDED_PLATFORM -DENABLE_INSMOD_NO_FW_LOAD -DPNO_SUPPORT \ + -DSET_RANDOM_MAC_SOFTAP -DWL_CFG80211_STA_EVENT \ + -Idrivers/net/wireless/bcmdhd -Idrivers/net/wireless/bcmdhd/include + +DHDOFILES = aiutils.o bcmsdh_sdmmc_linux.o dhd_linux.o siutils.o bcmutils.o \ + dhd_linux_sched.o bcmwifi.o dhd_sdio.o bcmevent.o dhd_bta.o hndpmu.o \ + bcmsdh.o dhd_cdc.o bcmsdh_linux.o dhd_common.o linux_osl.o \ + bcmsdh_sdmmc.o dhd_custom_gpio.o sbutils.o wldev_common.o wl_android.o dhd_cfg80211.o + +obj-$(CONFIG_BCMDHD) += bcmdhd.o +bcmdhd-objs += $(DHDOFILES) +ifneq ($(CONFIG_WIRELESS_EXT),) +bcmdhd-objs += wl_iw.o +DHDCFLAGS += -DSOFTAP -DWL_WIRELESS_EXT +endif +ifneq ($(CONFIG_CFG80211),) +bcmdhd-objs += wl_cfg80211.o wl_cfgp2p.o wl_linux_mon.o +DHDCFLAGS += -DWL_CFG80211 +endif +ifneq ($(CONFIG_DHD_USE_SCHED_SCAN),) +DHDCFLAGS += -DWL_SCHED_SCAN +endif +ifneq ($(CONFIG_DHD_ENABLE_P2P),) +DHDCFLAGS += -DWL_ENABLE_P2P_IF +endif +EXTRA_CFLAGS = $(DHDCFLAGS) +ifeq ($(CONFIG_BCMDHD),m) +EXTRA_LDFLAGS += --strip-debug +endif diff --git a/drivers/net/wireless/bcmdhd/aiutils.c b/drivers/net/wireless/bcmdhd/aiutils.c new file mode 100644 index 0000000000000..5ca0993c93331 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/aiutils.c @@ -0,0 +1,675 @@ +/* + * Misc utility routines for accessing chip-specific features + * of the SiliconBackplane-based Broadcom chips. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: aiutils.c,v 1.26.2.1 2010-03-09 18:41:21 $ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "siutils_priv.h" + + + + + +static uint32 +get_erom_ent(si_t *sih, uint32 **eromptr, uint32 mask, uint32 match) +{ + uint32 ent; + uint inv = 0, nom = 0; + + while (TRUE) { + ent = R_REG(si_osh(sih), *eromptr); + (*eromptr)++; + + if (mask == 0) + break; + + if ((ent & ER_VALID) == 0) { + inv++; + continue; + } + + if (ent == (ER_END | ER_VALID)) + break; + + if ((ent & mask) == match) + break; + + nom++; + } + + SI_VMSG(("%s: Returning ent 0x%08x\n", __FUNCTION__, ent)); + if (inv + nom) { + SI_VMSG((" after %d invalid and %d non-matching entries\n", inv, nom)); + } + return ent; +} + +static uint32 +get_asd(si_t *sih, uint32 **eromptr, uint sp, uint ad, uint st, uint32 *addrl, uint32 *addrh, + uint32 *sizel, uint32 *sizeh) +{ + uint32 asd, sz, szd; + + asd = get_erom_ent(sih, eromptr, ER_VALID, ER_VALID); + if (((asd & ER_TAG1) != ER_ADD) || + (((asd & AD_SP_MASK) >> AD_SP_SHIFT) != sp) || + ((asd & AD_ST_MASK) != st)) { + + (*eromptr)--; + return 0; + } + *addrl = asd & AD_ADDR_MASK; + if (asd & AD_AG32) + *addrh = get_erom_ent(sih, eromptr, 0, 0); + else + *addrh = 0; + *sizeh = 0; + sz = asd & AD_SZ_MASK; + if (sz == AD_SZ_SZD) { + szd = get_erom_ent(sih, eromptr, 0, 0); + *sizel = szd & SD_SZ_MASK; + if (szd & SD_SG32) + *sizeh = get_erom_ent(sih, eromptr, 0, 0); + } else + *sizel = AD_SZ_BASE << (sz >> AD_SZ_SHIFT); + + SI_VMSG((" SP %d, ad %d: st = %d, 0x%08x_0x%08x @ 0x%08x_0x%08x\n", + sp, ad, st, *sizeh, *sizel, *addrh, *addrl)); + + return asd; +} + +static void +ai_hwfixup(si_info_t *sii) +{ +} + + +void +ai_scan(si_t *sih, void *regs, uint devid) +{ + si_info_t *sii = SI_INFO(sih); + chipcregs_t *cc = (chipcregs_t *)regs; + uint32 erombase, *eromptr, *eromlim; + + erombase = R_REG(sii->osh, &cc->eromptr); + + switch (BUSTYPE(sih->bustype)) { + case SI_BUS: + eromptr = (uint32 *)REG_MAP(erombase, SI_CORE_SIZE); + break; + + case PCI_BUS: + + sii->curwrap = (void *)((uintptr)regs + SI_CORE_SIZE); + + + OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, 4, erombase); + eromptr = regs; + break; + + case SPI_BUS: + case SDIO_BUS: + eromptr = (uint32 *)(uintptr)erombase; + break; + + case PCMCIA_BUS: + default: + SI_ERROR(("Don't know how to do AXI enumertion on bus %d\n", sih->bustype)); + ASSERT(0); + return; + } + eromlim = eromptr + (ER_REMAPCONTROL / sizeof(uint32)); + + SI_VMSG(("ai_scan: regs = 0x%p, erombase = 0x%08x, eromptr = 0x%p, eromlim = 0x%p\n", + regs, erombase, eromptr, eromlim)); + while (eromptr < eromlim) { + uint32 cia, cib, cid, mfg, crev, nmw, nsw, nmp, nsp; + uint32 mpd, asd, addrl, addrh, sizel, sizeh; + uint32 *base; + uint i, j, idx; + bool br; + + br = FALSE; + + + cia = get_erom_ent(sih, &eromptr, ER_TAG, ER_CI); + if (cia == (ER_END | ER_VALID)) { + SI_VMSG(("Found END of erom after %d cores\n", sii->numcores)); + ai_hwfixup(sii); + return; + } + base = eromptr - 1; + cib = get_erom_ent(sih, &eromptr, 0, 0); + + if ((cib & ER_TAG) != ER_CI) { + SI_ERROR(("CIA not followed by CIB\n")); + goto error; + } + + cid = (cia & CIA_CID_MASK) >> CIA_CID_SHIFT; + mfg = (cia & CIA_MFG_MASK) >> CIA_MFG_SHIFT; + crev = (cib & CIB_REV_MASK) >> CIB_REV_SHIFT; + nmw = (cib & CIB_NMW_MASK) >> CIB_NMW_SHIFT; + nsw = (cib & CIB_NSW_MASK) >> CIB_NSW_SHIFT; + nmp = (cib & CIB_NMP_MASK) >> CIB_NMP_SHIFT; + nsp = (cib & CIB_NSP_MASK) >> CIB_NSP_SHIFT; + + SI_VMSG(("Found component 0x%04x/0x%04x rev %d at erom addr 0x%p, with nmw = %d, " + "nsw = %d, nmp = %d & nsp = %d\n", + mfg, cid, crev, base, nmw, nsw, nmp, nsp)); + + if (((mfg == MFGID_ARM) && (cid == DEF_AI_COMP)) || (nsp == 0)) + continue; + if ((nmw + nsw == 0)) { + + if (cid == OOB_ROUTER_CORE_ID) { + asd = get_asd(sih, &eromptr, 0, 0, AD_ST_SLAVE, + &addrl, &addrh, &sizel, &sizeh); + if (asd != 0) { + sii->oob_router = addrl; + } + } + continue; + } + + idx = sii->numcores; + + sii->cia[idx] = cia; + sii->cib[idx] = cib; + sii->coreid[idx] = cid; + + for (i = 0; i < nmp; i++) { + mpd = get_erom_ent(sih, &eromptr, ER_VALID, ER_VALID); + if ((mpd & ER_TAG) != ER_MP) { + SI_ERROR(("Not enough MP entries for component 0x%x\n", cid)); + goto error; + } + SI_VMSG((" Master port %d, mp: %d id: %d\n", i, + (mpd & MPD_MP_MASK) >> MPD_MP_SHIFT, + (mpd & MPD_MUI_MASK) >> MPD_MUI_SHIFT)); + } + + + asd = get_asd(sih, &eromptr, 0, 0, AD_ST_SLAVE, &addrl, &addrh, &sizel, &sizeh); + if (asd == 0) { + + asd = get_asd(sih, &eromptr, 0, 0, AD_ST_BRIDGE, &addrl, &addrh, + &sizel, &sizeh); + if (asd != 0) + br = TRUE; + else + if ((addrh != 0) || (sizeh != 0) || (sizel != SI_CORE_SIZE)) { + SI_ERROR(("First Slave ASD for core 0x%04x malformed " + "(0x%08x)\n", cid, asd)); + goto error; + } + } + sii->coresba[idx] = addrl; + sii->coresba_size[idx] = sizel; + + j = 1; + do { + asd = get_asd(sih, &eromptr, 0, j, AD_ST_SLAVE, &addrl, &addrh, + &sizel, &sizeh); + if ((asd != 0) && (j == 1) && (sizel == SI_CORE_SIZE)) { + sii->coresba2[idx] = addrl; + sii->coresba2_size[idx] = sizel; + } + j++; + } while (asd != 0); + + + for (i = 1; i < nsp; i++) { + j = 0; + do { + asd = get_asd(sih, &eromptr, i, j++, AD_ST_SLAVE, &addrl, &addrh, + &sizel, &sizeh); + } while (asd != 0); + if (j == 0) { + SI_ERROR((" SP %d has no address descriptors\n", i)); + goto error; + } + } + + + for (i = 0; i < nmw; i++) { + asd = get_asd(sih, &eromptr, i, 0, AD_ST_MWRAP, &addrl, &addrh, + &sizel, &sizeh); + if (asd == 0) { + SI_ERROR(("Missing descriptor for MW %d\n", i)); + goto error; + } + if ((sizeh != 0) || (sizel != SI_CORE_SIZE)) { + SI_ERROR(("Master wrapper %d is not 4KB\n", i)); + goto error; + } + if (i == 0) + sii->wrapba[idx] = addrl; + } + + + for (i = 0; i < nsw; i++) { + uint fwp = (nsp == 1) ? 0 : 1; + asd = get_asd(sih, &eromptr, fwp + i, 0, AD_ST_SWRAP, &addrl, &addrh, + &sizel, &sizeh); + if (asd == 0) { + SI_ERROR(("Missing descriptor for SW %d\n", i)); + goto error; + } + if ((sizeh != 0) || (sizel != SI_CORE_SIZE)) { + SI_ERROR(("Slave wrapper %d is not 4KB\n", i)); + goto error; + } + if ((nmw == 0) && (i == 0)) + sii->wrapba[idx] = addrl; + } + + + if (br) + continue; + + + sii->numcores++; + } + + SI_ERROR(("Reached end of erom without finding END")); + +error: + sii->numcores = 0; + return; +} + + +void * +ai_setcoreidx(si_t *sih, uint coreidx) +{ + si_info_t *sii = SI_INFO(sih); + uint32 addr = sii->coresba[coreidx]; + uint32 wrap = sii->wrapba[coreidx]; + void *regs; + + if (coreidx >= sii->numcores) + return (NULL); + + + ASSERT((sii->intrsenabled_fn == NULL) || !(*(sii)->intrsenabled_fn)((sii)->intr_arg)); + + switch (BUSTYPE(sih->bustype)) { + case SI_BUS: + + if (!sii->regs[coreidx]) { + sii->regs[coreidx] = REG_MAP(addr, SI_CORE_SIZE); + ASSERT(GOODREGS(sii->regs[coreidx])); + } + sii->curmap = regs = sii->regs[coreidx]; + if (!sii->wrappers[coreidx]) { + sii->wrappers[coreidx] = REG_MAP(wrap, SI_CORE_SIZE); + ASSERT(GOODREGS(sii->wrappers[coreidx])); + } + sii->curwrap = sii->wrappers[coreidx]; + break; + + + case SPI_BUS: + case SDIO_BUS: + sii->curmap = regs = (void *)((uintptr)addr); + sii->curwrap = (void *)((uintptr)wrap); + break; + + case PCMCIA_BUS: + default: + ASSERT(0); + regs = NULL; + break; + } + + sii->curmap = regs; + sii->curidx = coreidx; + + return regs; +} + + +int +ai_numaddrspaces(si_t *sih) +{ + return 2; +} + + +uint32 +ai_addrspace(si_t *sih, uint asidx) +{ + si_info_t *sii; + uint cidx; + + sii = SI_INFO(sih); + cidx = sii->curidx; + + if (asidx == 0) + return sii->coresba[cidx]; + else if (asidx == 1) + return sii->coresba2[cidx]; + else { + SI_ERROR(("%s: Need to parse the erom again to find addr space %d\n", + __FUNCTION__, asidx)); + return 0; + } +} + + +uint32 +ai_addrspacesize(si_t *sih, uint asidx) +{ + si_info_t *sii; + uint cidx; + + sii = SI_INFO(sih); + cidx = sii->curidx; + + if (asidx == 0) + return sii->coresba_size[cidx]; + else if (asidx == 1) + return sii->coresba2_size[cidx]; + else { + SI_ERROR(("%s: Need to parse the erom again to find addr space %d\n", + __FUNCTION__, asidx)); + return 0; + } +} + +uint +ai_flag(si_t *sih) +{ + si_info_t *sii; + aidmp_t *ai; + + sii = SI_INFO(sih); + ai = sii->curwrap; + + return (R_REG(sii->osh, &ai->oobselouta30) & 0x1f); +} + +void +ai_setint(si_t *sih, int siflag) +{ +} + +uint +ai_wrap_reg(si_t *sih, uint32 offset, uint32 mask, uint32 val) +{ + si_info_t *sii = SI_INFO(sih); + uint32 *map = (uint32 *) sii->curwrap; + + if (mask || val) { + uint32 w = R_REG(sii->osh, map+(offset/4)); + w &= ~mask; + w |= val; + W_REG(sii->osh, map+(offset/4), val); + } + + return (R_REG(sii->osh, map+(offset/4))); +} + +uint +ai_corevendor(si_t *sih) +{ + si_info_t *sii; + uint32 cia; + + sii = SI_INFO(sih); + cia = sii->cia[sii->curidx]; + return ((cia & CIA_MFG_MASK) >> CIA_MFG_SHIFT); +} + +uint +ai_corerev(si_t *sih) +{ + si_info_t *sii; + uint32 cib; + + sii = SI_INFO(sih); + cib = sii->cib[sii->curidx]; + return ((cib & CIB_REV_MASK) >> CIB_REV_SHIFT); +} + +bool +ai_iscoreup(si_t *sih) +{ + si_info_t *sii; + aidmp_t *ai; + + sii = SI_INFO(sih); + ai = sii->curwrap; + + return (((R_REG(sii->osh, &ai->ioctrl) & (SICF_FGC | SICF_CLOCK_EN)) == SICF_CLOCK_EN) && + ((R_REG(sii->osh, &ai->resetctrl) & AIRC_RESET) == 0)); +} + + +uint +ai_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val) +{ + uint origidx = 0; + uint32 *r = NULL; + uint w; + uint intr_val = 0; + bool fast = FALSE; + si_info_t *sii; + + sii = SI_INFO(sih); + + ASSERT(GOODIDX(coreidx)); + ASSERT(regoff < SI_CORE_SIZE); + ASSERT((val & ~mask) == 0); + + if (coreidx >= SI_MAXCORES) + return 0; + + if (BUSTYPE(sih->bustype) == SI_BUS) { + + fast = TRUE; + + if (!sii->regs[coreidx]) { + sii->regs[coreidx] = REG_MAP(sii->coresba[coreidx], + SI_CORE_SIZE); + ASSERT(GOODREGS(sii->regs[coreidx])); + } + r = (uint32 *)((uchar *)sii->regs[coreidx] + regoff); + } else if (BUSTYPE(sih->bustype) == PCI_BUS) { + + + if ((sii->coreid[coreidx] == CC_CORE_ID) && SI_FAST(sii)) { + + + fast = TRUE; + r = (uint32 *)((char *)sii->curmap + PCI_16KB0_CCREGS_OFFSET + regoff); + } else if (sii->pub.buscoreidx == coreidx) { + + fast = TRUE; + if (SI_FAST(sii)) + r = (uint32 *)((char *)sii->curmap + + PCI_16KB0_PCIREGS_OFFSET + regoff); + else + r = (uint32 *)((char *)sii->curmap + + ((regoff >= SBCONFIGOFF) ? + PCI_BAR0_PCISBR_OFFSET : PCI_BAR0_PCIREGS_OFFSET) + + regoff); + } + } + + if (!fast) { + INTR_OFF(sii, intr_val); + + + origidx = si_coreidx(&sii->pub); + + + r = (uint32*) ((uchar*) ai_setcoreidx(&sii->pub, coreidx) + regoff); + } + ASSERT(r != NULL); + + + if (mask || val) { + w = (R_REG(sii->osh, r) & ~mask) | val; + W_REG(sii->osh, r, w); + } + + + w = R_REG(sii->osh, r); + + if (!fast) { + + if (origidx != coreidx) + ai_setcoreidx(&sii->pub, origidx); + + INTR_RESTORE(sii, intr_val); + } + + return (w); +} + +void +ai_core_disable(si_t *sih, uint32 bits) +{ + si_info_t *sii; + volatile uint32 dummy; + aidmp_t *ai; + + sii = SI_INFO(sih); + + ASSERT(GOODREGS(sii->curwrap)); + ai = sii->curwrap; + + + if (R_REG(sii->osh, &ai->resetctrl) & AIRC_RESET) + return; + + W_REG(sii->osh, &ai->ioctrl, bits); + dummy = R_REG(sii->osh, &ai->ioctrl); + OSL_DELAY(10); + + W_REG(sii->osh, &ai->resetctrl, AIRC_RESET); + OSL_DELAY(1); +} + + +void +ai_core_reset(si_t *sih, uint32 bits, uint32 resetbits) +{ + si_info_t *sii; + aidmp_t *ai; + volatile uint32 dummy; + + sii = SI_INFO(sih); + ASSERT(GOODREGS(sii->curwrap)); + ai = sii->curwrap; + + + ai_core_disable(sih, (bits | resetbits)); + + + W_REG(sii->osh, &ai->ioctrl, (bits | SICF_FGC | SICF_CLOCK_EN)); + dummy = R_REG(sii->osh, &ai->ioctrl); + W_REG(sii->osh, &ai->resetctrl, 0); + OSL_DELAY(1); + + W_REG(sii->osh, &ai->ioctrl, (bits | SICF_CLOCK_EN)); + dummy = R_REG(sii->osh, &ai->ioctrl); + OSL_DELAY(1); +} + + +void +ai_core_cflags_wo(si_t *sih, uint32 mask, uint32 val) +{ + si_info_t *sii; + aidmp_t *ai; + uint32 w; + + sii = SI_INFO(sih); + ASSERT(GOODREGS(sii->curwrap)); + ai = sii->curwrap; + + ASSERT((val & ~mask) == 0); + + if (mask || val) { + w = ((R_REG(sii->osh, &ai->ioctrl) & ~mask) | val); + W_REG(sii->osh, &ai->ioctrl, w); + } +} + +uint32 +ai_core_cflags(si_t *sih, uint32 mask, uint32 val) +{ + si_info_t *sii; + aidmp_t *ai; + uint32 w; + + sii = SI_INFO(sih); + ASSERT(GOODREGS(sii->curwrap)); + ai = sii->curwrap; + + ASSERT((val & ~mask) == 0); + + if (mask || val) { + w = ((R_REG(sii->osh, &ai->ioctrl) & ~mask) | val); + W_REG(sii->osh, &ai->ioctrl, w); + } + + return R_REG(sii->osh, &ai->ioctrl); +} + +uint32 +ai_core_sflags(si_t *sih, uint32 mask, uint32 val) +{ + si_info_t *sii; + aidmp_t *ai; + uint32 w; + + sii = SI_INFO(sih); + ASSERT(GOODREGS(sii->curwrap)); + ai = sii->curwrap; + + ASSERT((val & ~mask) == 0); + ASSERT((mask & ~SISF_CORE_BITS) == 0); + + if (mask || val) { + w = ((R_REG(sii->osh, &ai->iostatus) & ~mask) | val); + W_REG(sii->osh, &ai->iostatus, w); + } + + return R_REG(sii->osh, &ai->iostatus); +} diff --git a/drivers/net/wireless/bcmdhd/bcmevent.c b/drivers/net/wireless/bcmdhd/bcmevent.c new file mode 100644 index 0000000000000..6a25d9a5a57f2 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/bcmevent.c @@ -0,0 +1,127 @@ +/* + * bcmevent read-only data shared by kernel or app layers + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * $Id: bcmevent.c,v 1.8.2.7 2011-02-01 06:23:39 $ + */ + +#include +#include +#include +#include +#include + +#if WLC_E_LAST != 87 +#error "You need to add an entry to bcmevent_names[] for the new event" +#endif + +const bcmevent_name_t bcmevent_names[] = { + { WLC_E_SET_SSID, "SET_SSID" }, + { WLC_E_JOIN, "JOIN" }, + { WLC_E_START, "START" }, + { WLC_E_AUTH, "AUTH" }, + { WLC_E_AUTH_IND, "AUTH_IND" }, + { WLC_E_DEAUTH, "DEAUTH" }, + { WLC_E_DEAUTH_IND, "DEAUTH_IND" }, + { WLC_E_ASSOC, "ASSOC" }, + { WLC_E_ASSOC_IND, "ASSOC_IND" }, + { WLC_E_REASSOC, "REASSOC" }, + { WLC_E_REASSOC_IND, "REASSOC_IND" }, + { WLC_E_DISASSOC, "DISASSOC" }, + { WLC_E_DISASSOC_IND, "DISASSOC_IND" }, + { WLC_E_QUIET_START, "START_QUIET" }, + { WLC_E_QUIET_END, "END_QUIET" }, + { WLC_E_BEACON_RX, "BEACON_RX" }, + { WLC_E_LINK, "LINK" }, + { WLC_E_MIC_ERROR, "MIC_ERROR" }, + { WLC_E_NDIS_LINK, "NDIS_LINK" }, + { WLC_E_ROAM, "ROAM" }, + { WLC_E_TXFAIL, "TXFAIL" }, + { WLC_E_PMKID_CACHE, "PMKID_CACHE" }, + { WLC_E_RETROGRADE_TSF, "RETROGRADE_TSF" }, + { WLC_E_PRUNE, "PRUNE" }, + { WLC_E_AUTOAUTH, "AUTOAUTH" }, + { WLC_E_EAPOL_MSG, "EAPOL_MSG" }, + { WLC_E_SCAN_COMPLETE, "SCAN_COMPLETE" }, + { WLC_E_ADDTS_IND, "ADDTS_IND" }, + { WLC_E_DELTS_IND, "DELTS_IND" }, + { WLC_E_BCNSENT_IND, "BCNSENT_IND" }, + { WLC_E_BCNRX_MSG, "BCNRX_MSG" }, + { WLC_E_BCNLOST_MSG, "BCNLOST_IND" }, + { WLC_E_ROAM_PREP, "ROAM_PREP" }, + { WLC_E_PFN_NET_FOUND, "PFNFOUND_IND" }, + { WLC_E_PFN_NET_LOST, "PFNLOST_IND" }, +#if defined(IBSS_PEER_DISCOVERY_EVENT) + { WLC_E_IBSS_ASSOC, "IBSS_ASSOC" }, +#endif /* defined(IBSS_PEER_DISCOVERY_EVENT) */ + { WLC_E_RADIO, "RADIO" }, + { WLC_E_PSM_WATCHDOG, "PSM_WATCHDOG" }, + { WLC_E_PROBREQ_MSG, "PROBE_REQ_MSG" }, + { WLC_E_SCAN_CONFIRM_IND, "SCAN_CONFIRM_IND" }, + { WLC_E_PSK_SUP, "PSK_SUP" }, + { WLC_E_COUNTRY_CODE_CHANGED, "CNTRYCODE_IND" }, + { WLC_E_EXCEEDED_MEDIUM_TIME, "EXCEEDED_MEDIUM_TIME" }, + { WLC_E_ICV_ERROR, "ICV_ERROR" }, + { WLC_E_UNICAST_DECODE_ERROR, "UNICAST_DECODE_ERROR" }, + { WLC_E_MULTICAST_DECODE_ERROR, "MULTICAST_DECODE_ERROR" }, + { WLC_E_TRACE, "TRACE" }, + { WLC_E_BTA_HCI_EVENT, "BTA_HCI_EVENT" }, + { WLC_E_IF, "IF" }, +#ifdef WLP2P + { WLC_E_P2P_DISC_LISTEN_COMPLETE, "WLC_E_P2P_DISC_LISTEN_COMPLETE" }, +#endif + { WLC_E_RSSI, "RSSI" }, + { WLC_E_PFN_SCAN_COMPLETE, "SCAN_COMPLETE" }, + { WLC_E_EXTLOG_MSG, "EXTERNAL LOG MESSAGE" }, +#ifdef WIFI_ACT_FRAME + { WLC_E_ACTION_FRAME, "ACTION_FRAME" }, + { WLC_E_ACTION_FRAME_RX, "ACTION_FRAME_RX" }, + { WLC_E_ACTION_FRAME_COMPLETE, "ACTION_FRAME_COMPLETE" }, +#endif + { WLC_E_ESCAN_RESULT, "WLC_E_ESCAN_RESULT" }, + { WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE, "WLC_E_AF_OFF_CHAN_COMPLETE" }, +#ifdef WLP2P + { WLC_E_PROBRESP_MSG, "PROBE_RESP_MSG" }, + { WLC_E_P2P_PROBREQ_MSG, "P2P PROBE_REQ_MSG" }, +#endif +#ifdef PROP_TXSTATUS + { WLC_E_FIFO_CREDIT_MAP, "FIFO_CREDIT_MAP" }, +#endif + { WLC_E_WAKE_EVENT, "WAKE_EVENT" }, + { WLC_E_DCS_REQUEST, "DCS_REQUEST" }, + { WLC_E_RM_COMPLETE, "RM_COMPLETE" }, +#ifdef WLMEDIA_HTSF + { WLC_E_HTSFSYNC, "HTSF_SYNC_EVENT" }, +#endif + { WLC_E_OVERLAY_REQ, "OVERLAY_REQ_EVENT" }, + { WLC_E_CSA_COMPLETE_IND, "WLC_E_CSA_COMPLETE_IND" }, + { WLC_E_EXCESS_PM_WAKE_EVENT, "EXCESS_PM_WAKE_EVENT" }, + { WLC_E_PFN_SCAN_NONE, "PFN_SCAN_NONE" }, + { WLC_E_PFN_SCAN_ALLGONE, "PFN_SCAN_ALLGONE" }, +#ifdef SOFTAP + { WLC_E_GTK_PLUMBED, "GTK_PLUMBED" }, +#endif + { WLC_E_ASSOC_REQ_IE, "ASSOC_REQ_IE" }, + { WLC_E_ASSOC_RESP_IE, "ASSOC_RESP_IE" } +}; + + +const int bcmevent_names_size = ARRAYSIZE(bcmevent_names); diff --git a/drivers/net/wireless/bcmdhd/bcmsdh.c b/drivers/net/wireless/bcmdhd/bcmsdh.c new file mode 100644 index 0000000000000..f67b13a03a1c5 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/bcmsdh.c @@ -0,0 +1,690 @@ +/* + * BCMSDH interface glue + * implement bcmsdh API for SDIOH driver + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdh.c 300445 2011-12-03 05:37:20Z $ + */ + +/** + * @file bcmsdh.c + */ + +/* ****************** BCMSDH Interface Functions *************************** */ + +#include +#include +#include +#include +#include +#include +#include + +#include /* BRCM API for SDIO clients (such as wl, dhd) */ +#include /* common SDIO/controller interface */ +#include /* SDIO device core hardware definitions. */ + +#include /* SDIO Device and Protocol Specs */ + +#define SDIOH_API_ACCESS_RETRY_LIMIT 2 +const uint bcmsdh_msglevel = BCMSDH_ERROR_VAL; + +/** + * BCMSDH API context + */ +struct bcmsdh_info +{ + bool init_success; /* underlying driver successfully attached */ + void *sdioh; /* handler for sdioh */ + uint32 vendevid; /* Target Vendor and Device ID on SD bus */ + osl_t *osh; + bool regfail; /* Save status of last reg_read/reg_write call */ + uint32 sbwad; /* Save backplane window address */ +}; +/* local copy of bcm sd handler */ +bcmsdh_info_t * l_bcmsdh = NULL; + +#if defined(OOB_INTR_ONLY) && defined(HW_OOB) +extern int +sdioh_enable_hw_oob_intr(void *sdioh, bool enable); + +void +bcmsdh_enable_hw_oob_intr(bcmsdh_info_t *sdh, bool enable) +{ + sdioh_enable_hw_oob_intr(sdh->sdioh, enable); +} +#endif + +/* Attach BCMSDH layer to SDIO Host Controller Driver + * + * @param osh OSL Handle. + * @param cfghdl Configuration Handle. + * @param regsva Virtual address of controller registers. + * @param irq Interrupt number of SDIO controller. + * + * @return bcmsdh_info_t Handle to BCMSDH context. + */ +bcmsdh_info_t * +bcmsdh_attach(osl_t *osh, void *cfghdl, void **regsva, uint irq) +{ + bcmsdh_info_t *bcmsdh; + + if ((bcmsdh = (bcmsdh_info_t *)MALLOC(osh, sizeof(bcmsdh_info_t))) == NULL) { + BCMSDH_ERROR(("bcmsdh_attach: out of memory, malloced %d bytes\n", MALLOCED(osh))); + return NULL; + } + bzero((char *)bcmsdh, sizeof(bcmsdh_info_t)); + + /* save the handler locally */ + l_bcmsdh = bcmsdh; + + if (!(bcmsdh->sdioh = sdioh_attach(osh, cfghdl, irq))) { + bcmsdh_detach(osh, bcmsdh); + return NULL; + } + + bcmsdh->osh = osh; + bcmsdh->init_success = TRUE; + + *regsva = (uint32 *)SI_ENUM_BASE; + + /* Report the BAR, to fix if needed */ + bcmsdh->sbwad = SI_ENUM_BASE; + return bcmsdh; +} + +int +bcmsdh_detach(osl_t *osh, void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + if (bcmsdh != NULL) { + if (bcmsdh->sdioh) { + sdioh_detach(osh, bcmsdh->sdioh); + bcmsdh->sdioh = NULL; + } + MFREE(osh, bcmsdh, sizeof(bcmsdh_info_t)); + } + + l_bcmsdh = NULL; + return 0; +} + +int +bcmsdh_iovar_op(void *sdh, const char *name, + void *params, int plen, void *arg, int len, bool set) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + return sdioh_iovar_op(bcmsdh->sdioh, name, params, plen, arg, len, set); +} + +bool +bcmsdh_intr_query(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + bool on; + + ASSERT(bcmsdh); + status = sdioh_interrupt_query(bcmsdh->sdioh, &on); + if (SDIOH_API_SUCCESS(status)) + return FALSE; + else + return on; +} + +int +bcmsdh_intr_enable(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + ASSERT(bcmsdh); + + status = sdioh_interrupt_set(bcmsdh->sdioh, TRUE); + return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); +} + +int +bcmsdh_intr_disable(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + ASSERT(bcmsdh); + + status = sdioh_interrupt_set(bcmsdh->sdioh, FALSE); + return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); +} + +int +bcmsdh_intr_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + ASSERT(bcmsdh); + + status = sdioh_interrupt_register(bcmsdh->sdioh, fn, argh); + return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); +} + +int +bcmsdh_intr_dereg(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + ASSERT(bcmsdh); + + status = sdioh_interrupt_deregister(bcmsdh->sdioh); + return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); +} + +#if defined(DHD_DEBUG) +bool +bcmsdh_intr_pending(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + ASSERT(sdh); + return sdioh_interrupt_pending(bcmsdh->sdioh); +} +#endif + + +int +bcmsdh_devremove_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh) +{ + ASSERT(sdh); + + /* don't support yet */ + return BCME_UNSUPPORTED; +} + +/** + * Read from SDIO Configuration Space + * @param sdh SDIO Host context. + * @param func_num Function number to read from. + * @param addr Address to read from. + * @param err Error return. + * @return value read from SDIO configuration space. + */ +uint8 +bcmsdh_cfg_read(void *sdh, uint fnc_num, uint32 addr, int *err) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; +#ifdef SDIOH_API_ACCESS_RETRY_LIMIT + int32 retry = 0; +#endif + uint8 data = 0; + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + ASSERT(bcmsdh->init_success); + +#ifdef SDIOH_API_ACCESS_RETRY_LIMIT + do { + if (retry) /* wait for 1 ms till bus get settled down */ + OSL_DELAY(1000); +#endif + status = sdioh_cfg_read(bcmsdh->sdioh, fnc_num, addr, (uint8 *)&data); +#ifdef SDIOH_API_ACCESS_RETRY_LIMIT + } while (!SDIOH_API_SUCCESS(status) && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT)); +#endif + if (err) + *err = (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR); + + BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint8data = 0x%x\n", __FUNCTION__, + fnc_num, addr, data)); + + return data; +} + +void +bcmsdh_cfg_write(void *sdh, uint fnc_num, uint32 addr, uint8 data, int *err) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; +#ifdef SDIOH_API_ACCESS_RETRY_LIMIT + int32 retry = 0; +#endif + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + ASSERT(bcmsdh->init_success); + +#ifdef SDIOH_API_ACCESS_RETRY_LIMIT + do { + if (retry) /* wait for 1 ms till bus get settled down */ + OSL_DELAY(1000); +#endif + status = sdioh_cfg_write(bcmsdh->sdioh, fnc_num, addr, (uint8 *)&data); +#ifdef SDIOH_API_ACCESS_RETRY_LIMIT + } while (!SDIOH_API_SUCCESS(status) && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT)); +#endif + if (err) + *err = SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR; + + BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint8data = 0x%x\n", __FUNCTION__, + fnc_num, addr, data)); +} + +uint32 +bcmsdh_cfg_read_word(void *sdh, uint fnc_num, uint32 addr, int *err) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + uint32 data = 0; + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + ASSERT(bcmsdh->init_success); + + status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, SDIOH_READ, fnc_num, + addr, &data, 4); + + if (err) + *err = (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR); + + BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint32data = 0x%x\n", __FUNCTION__, + fnc_num, addr, data)); + + return data; +} + +void +bcmsdh_cfg_write_word(void *sdh, uint fnc_num, uint32 addr, uint32 data, int *err) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + ASSERT(bcmsdh->init_success); + + status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, SDIOH_WRITE, fnc_num, + addr, &data, 4); + + if (err) + *err = (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR); + + BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, uint32data = 0x%x\n", __FUNCTION__, fnc_num, + addr, data)); +} + + +int +bcmsdh_cis_read(void *sdh, uint func, uint8 *cis, uint length) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + + uint8 *tmp_buf, *tmp_ptr; + uint8 *ptr; + bool ascii = func & ~0xf; + func &= 0x7; + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + ASSERT(bcmsdh->init_success); + ASSERT(cis); + ASSERT(length <= SBSDIO_CIS_SIZE_LIMIT); + + status = sdioh_cis_read(bcmsdh->sdioh, func, cis, length); + + if (ascii) { + /* Move binary bits to tmp and format them into the provided buffer. */ + if ((tmp_buf = (uint8 *)MALLOC(bcmsdh->osh, length)) == NULL) { + BCMSDH_ERROR(("%s: out of memory\n", __FUNCTION__)); + return BCME_NOMEM; + } + bcopy(cis, tmp_buf, length); + for (tmp_ptr = tmp_buf, ptr = cis; ptr < (cis + length - 4); tmp_ptr++) { + ptr += sprintf((char*)ptr, "%.2x ", *tmp_ptr & 0xff); + if ((((tmp_ptr - tmp_buf) + 1) & 0xf) == 0) + ptr += sprintf((char *)ptr, "\n"); + } + MFREE(bcmsdh->osh, tmp_buf, length); + } + + return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); +} + + +int +bcmsdhsdio_set_sbaddr_window(void *sdh, uint32 address, bool force_set) +{ + int err = 0; + uint bar0 = address & ~SBSDIO_SB_OFT_ADDR_MASK; + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + if (bar0 != bcmsdh->sbwad || force_set) { + bcmsdh_cfg_write(bcmsdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW, + (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err); + if (!err) + bcmsdh_cfg_write(bcmsdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID, + (address >> 16) & SBSDIO_SBADDRMID_MASK, &err); + if (!err) + bcmsdh_cfg_write(bcmsdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH, + (address >> 24) & SBSDIO_SBADDRHIGH_MASK, &err); + + if (!err) + bcmsdh->sbwad = bar0; + else + /* invalidate cached window var */ + bcmsdh->sbwad = 0; + + } + + return err; +} + +uint32 +bcmsdh_reg_read(void *sdh, uint32 addr, uint size) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + uint32 word = 0; + + BCMSDH_INFO(("%s:fun = 1, addr = 0x%x, ", __FUNCTION__, addr)); + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + ASSERT(bcmsdh->init_success); + + if (bcmsdhsdio_set_sbaddr_window(bcmsdh, addr, FALSE)) + return 0xFFFFFFFF; + + addr &= SBSDIO_SB_OFT_ADDR_MASK; + if (size == 4) + addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + + status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, + SDIOH_READ, SDIO_FUNC_1, addr, &word, size); + + bcmsdh->regfail = !(SDIOH_API_SUCCESS(status)); + + BCMSDH_INFO(("uint32data = 0x%x\n", word)); + + /* if ok, return appropriately masked word */ + if (SDIOH_API_SUCCESS(status)) { + switch (size) { + case sizeof(uint8): + return (word & 0xff); + case sizeof(uint16): + return (word & 0xffff); + case sizeof(uint32): + return word; + default: + bcmsdh->regfail = TRUE; + + } + } + + /* otherwise, bad sdio access or invalid size */ + BCMSDH_ERROR(("%s: error reading addr 0x%04x size %d\n", __FUNCTION__, addr, size)); + return 0xFFFFFFFF; +} + +uint32 +bcmsdh_reg_write(void *sdh, uint32 addr, uint size, uint32 data) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + int err = 0; + + BCMSDH_INFO(("%s:fun = 1, addr = 0x%x, uint%ddata = 0x%x\n", + __FUNCTION__, addr, size*8, data)); + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + ASSERT(bcmsdh->init_success); + + if ((err = bcmsdhsdio_set_sbaddr_window(bcmsdh, addr, FALSE))) + return err; + + addr &= SBSDIO_SB_OFT_ADDR_MASK; + if (size == 4) + addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + status = sdioh_request_word(bcmsdh->sdioh, SDIOH_CMD_TYPE_NORMAL, SDIOH_WRITE, SDIO_FUNC_1, + addr, &data, size); + bcmsdh->regfail = !(SDIOH_API_SUCCESS(status)); + + if (SDIOH_API_SUCCESS(status)) + return 0; + + BCMSDH_ERROR(("%s: error writing 0x%08x to addr 0x%04x size %d\n", + __FUNCTION__, data, addr, size)); + return 0xFFFFFFFF; +} + +bool +bcmsdh_regfail(void *sdh) +{ + return ((bcmsdh_info_t *)sdh)->regfail; +} + +int +bcmsdh_recv_buf(void *sdh, uint32 addr, uint fn, uint flags, + uint8 *buf, uint nbytes, void *pkt, + bcmsdh_cmplt_fn_t complete_fn, void *handle) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + uint incr_fix; + uint width; + int err = 0; + + ASSERT(bcmsdh); + ASSERT(bcmsdh->init_success); + + BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, size = %d\n", + __FUNCTION__, fn, addr, nbytes)); + + /* Async not implemented yet */ + ASSERT(!(flags & SDIO_REQ_ASYNC)); + if (flags & SDIO_REQ_ASYNC) + return BCME_UNSUPPORTED; + + if ((err = bcmsdhsdio_set_sbaddr_window(bcmsdh, addr, FALSE))) + return err; + + addr &= SBSDIO_SB_OFT_ADDR_MASK; + + incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; + width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; + if (width == 4) + addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + + status = sdioh_request_buffer(bcmsdh->sdioh, SDIOH_DATA_PIO, incr_fix, + SDIOH_READ, fn, addr, width, nbytes, buf, pkt); + + return (SDIOH_API_SUCCESS(status) ? 0 : BCME_SDIO_ERROR); +} + +int +bcmsdh_send_buf(void *sdh, uint32 addr, uint fn, uint flags, + uint8 *buf, uint nbytes, void *pkt, + bcmsdh_cmplt_fn_t complete_fn, void *handle) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + uint incr_fix; + uint width; + int err = 0; + + ASSERT(bcmsdh); + ASSERT(bcmsdh->init_success); + + BCMSDH_INFO(("%s:fun = %d, addr = 0x%x, size = %d\n", + __FUNCTION__, fn, addr, nbytes)); + + /* Async not implemented yet */ + ASSERT(!(flags & SDIO_REQ_ASYNC)); + if (flags & SDIO_REQ_ASYNC) + return BCME_UNSUPPORTED; + + if ((err = bcmsdhsdio_set_sbaddr_window(bcmsdh, addr, FALSE))) + return err; + + addr &= SBSDIO_SB_OFT_ADDR_MASK; + + incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; + width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; + if (width == 4) + addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + + status = sdioh_request_buffer(bcmsdh->sdioh, SDIOH_DATA_PIO, incr_fix, + SDIOH_WRITE, fn, addr, width, nbytes, buf, pkt); + + return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); +} + +int +bcmsdh_rwdata(void *sdh, uint rw, uint32 addr, uint8 *buf, uint nbytes) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + SDIOH_API_RC status; + + ASSERT(bcmsdh); + ASSERT(bcmsdh->init_success); + ASSERT((addr & SBSDIO_SBWINDOW_MASK) == 0); + + addr &= SBSDIO_SB_OFT_ADDR_MASK; + addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + + status = sdioh_request_buffer(bcmsdh->sdioh, SDIOH_DATA_PIO, SDIOH_DATA_INC, + (rw ? SDIOH_WRITE : SDIOH_READ), SDIO_FUNC_1, + addr, 4, nbytes, buf, NULL); + + return (SDIOH_API_SUCCESS(status) ? 0 : BCME_ERROR); +} + +int +bcmsdh_abort(void *sdh, uint fn) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + return sdioh_abort(bcmsdh->sdioh, fn); +} + +int +bcmsdh_start(void *sdh, int stage) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + return sdioh_start(bcmsdh->sdioh, stage); +} + +int +bcmsdh_stop(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + return sdioh_stop(bcmsdh->sdioh); +} + +int +bcmsdh_waitlockfree(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + return sdioh_waitlockfree(bcmsdh->sdioh); +} + + +int +bcmsdh_query_device(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + bcmsdh->vendevid = (VENDOR_BROADCOM << 16) | 0; + return (bcmsdh->vendevid); +} + +uint +bcmsdh_query_iofnum(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + return (sdioh_query_iofnum(bcmsdh->sdioh)); +} + +int +bcmsdh_reset(bcmsdh_info_t *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + return sdioh_sdio_reset(bcmsdh->sdioh); +} + +void *bcmsdh_get_sdioh(bcmsdh_info_t *sdh) +{ + ASSERT(sdh); + return sdh->sdioh; +} + +/* Function to pass device-status bits to DHD. */ +uint32 +bcmsdh_get_dstatus(void *sdh) +{ + return 0; +} +uint32 +bcmsdh_cur_sbwad(void *sdh) +{ + bcmsdh_info_t *bcmsdh = (bcmsdh_info_t *)sdh; + + if (!bcmsdh) + bcmsdh = l_bcmsdh; + + return (bcmsdh->sbwad); +} + +void +bcmsdh_chipinfo(void *sdh, uint32 chip, uint32 chiprev) +{ + return; +} + + +int +bcmsdh_sleep(void *sdh, bool enab) +{ +#ifdef SDIOH_SLEEP_ENABLED + bcmsdh_info_t *p = (bcmsdh_info_t *)sdh; + sdioh_info_t *sd = (sdioh_info_t *)(p->sdioh); + + return sdioh_sleep(sd, enab); +#else + return BCME_UNSUPPORTED; +#endif +} diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_linux.c new file mode 100644 index 0000000000000..d257ddab84a3f --- /dev/null +++ b/drivers/net/wireless/bcmdhd/bcmsdh_linux.c @@ -0,0 +1,727 @@ +/* + * SDIO access interface for drivers - linux specific (pci only) + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdh_linux.c 312788 2012-02-03 23:06:32Z $ + */ + +/** + * @file bcmsdh_linux.c + */ + +#define __UNDEF_NO_VERSION__ + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#if defined(OOB_INTR_ONLY) +#include +extern void dhdsdio_isr(void * args); +#include +#include +#include +#endif /* defined(OOB_INTR_ONLY) */ + +/** + * SDIO Host Controller info + */ +typedef struct bcmsdh_hc bcmsdh_hc_t; + +struct bcmsdh_hc { + bcmsdh_hc_t *next; +#ifdef BCMPLATFORM_BUS + struct device *dev; /* platform device handle */ +#else + struct pci_dev *dev; /* pci device handle */ +#endif /* BCMPLATFORM_BUS */ + osl_t *osh; + void *regs; /* SDIO Host Controller address */ + bcmsdh_info_t *sdh; /* SDIO Host Controller handle */ + void *ch; + unsigned int oob_irq; + unsigned long oob_flags; /* OOB Host specifiction as edge and etc */ + bool oob_irq_registered; + bool oob_irq_enable_flag; +#if defined(OOB_INTR_ONLY) + spinlock_t irq_lock; +#endif +}; +static bcmsdh_hc_t *sdhcinfo = NULL; + +/* driver info, initialized when bcmsdh_register is called */ +static bcmsdh_driver_t drvinfo = {NULL, NULL}; + +/* debugging macros */ +#define SDLX_MSG(x) + +/** + * Checks to see if vendor and device IDs match a supported SDIO Host Controller. + */ +bool +bcmsdh_chipmatch(uint16 vendor, uint16 device) +{ + /* Add other vendors and devices as required */ + +#ifdef BCMSDIOH_STD + /* Check for Arasan host controller */ + if (vendor == VENDOR_SI_IMAGE) { + return (TRUE); + } + /* Check for BRCM 27XX Standard host controller */ + if (device == BCM27XX_SDIOH_ID && vendor == VENDOR_BROADCOM) { + return (TRUE); + } + /* Check for BRCM Standard host controller */ + if (device == SDIOH_FPGA_ID && vendor == VENDOR_BROADCOM) { + return (TRUE); + } + /* Check for TI PCIxx21 Standard host controller */ + if (device == PCIXX21_SDIOH_ID && vendor == VENDOR_TI) { + return (TRUE); + } + if (device == PCIXX21_SDIOH0_ID && vendor == VENDOR_TI) { + return (TRUE); + } + /* Ricoh R5C822 Standard SDIO Host */ + if (device == R5C822_SDIOH_ID && vendor == VENDOR_RICOH) { + return (TRUE); + } + /* JMicron Standard SDIO Host */ + if (device == JMICRON_SDIOH_ID && vendor == VENDOR_JMICRON) { + return (TRUE); + } + +#endif /* BCMSDIOH_STD */ +#ifdef BCMSDIOH_SPI + /* This is the PciSpiHost. */ + if (device == SPIH_FPGA_ID && vendor == VENDOR_BROADCOM) { + printf("Found PCI SPI Host Controller\n"); + return (TRUE); + } + +#endif /* BCMSDIOH_SPI */ + + return (FALSE); +} + +#if defined(BCMPLATFORM_BUS) +#if defined(BCMLXSDMMC) +/* forward declarations */ +int bcmsdh_probe(struct device *dev); +int bcmsdh_remove(struct device *dev); + +EXPORT_SYMBOL(bcmsdh_probe); +EXPORT_SYMBOL(bcmsdh_remove); + +#else +/* forward declarations */ +static int __devinit bcmsdh_probe(struct device *dev); +static int __devexit bcmsdh_remove(struct device *dev); +#endif /* BCMLXSDMMC */ + +#ifndef BCMLXSDMMC +static +#endif /* BCMLXSDMMC */ +int bcmsdh_probe(struct device *dev) +{ + osl_t *osh = NULL; + bcmsdh_hc_t *sdhc = NULL; + ulong regs = 0; + bcmsdh_info_t *sdh = NULL; +#if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS) + struct platform_device *pdev; + struct resource *r; +#endif /* BCMLXSDMMC */ + int irq = 0; + uint32 vendevid; + unsigned long irq_flags = 0; + +#if !defined(BCMLXSDMMC) && defined(BCMPLATFORM_BUS) + pdev = to_platform_device(dev); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!r || irq == NO_IRQ) + return -ENXIO; +#endif /* BCMLXSDMMC */ + +#if defined(OOB_INTR_ONLY) +#ifdef HW_OOB + irq_flags = + IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE; +#else + irq_flags = IRQF_TRIGGER_FALLING; +#endif /* HW_OOB */ + + /* Get customer specific OOB IRQ parametres: IRQ number as IRQ type */ + irq = dhd_customer_oob_irq_map(&irq_flags); + if (irq < 0) { + SDLX_MSG(("%s: Host irq is not defined\n", __FUNCTION__)); + return 1; + } +#endif /* defined(OOB_INTR_ONLY) */ + /* allocate SDIO Host Controller state info */ + if (!(osh = osl_attach(dev, PCI_BUS, FALSE))) { + SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__)); + goto err; + } + if (!(sdhc = MALLOC(osh, sizeof(bcmsdh_hc_t)))) { + SDLX_MSG(("%s: out of memory, allocated %d bytes\n", + __FUNCTION__, + MALLOCED(osh))); + goto err; + } + bzero(sdhc, sizeof(bcmsdh_hc_t)); + sdhc->osh = osh; + + sdhc->dev = (void *)dev; + +#ifdef BCMLXSDMMC + if (!(sdh = bcmsdh_attach(osh, (void *)0, + (void **)®s, irq))) { + SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__)); + goto err; + } +#else + if (!(sdh = bcmsdh_attach(osh, (void *)r->start, + (void **)®s, irq))) { + SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__)); + goto err; + } +#endif /* BCMLXSDMMC */ + sdhc->sdh = sdh; + sdhc->oob_irq = irq; + sdhc->oob_flags = irq_flags; + sdhc->oob_irq_registered = FALSE; /* to make sure.. */ + sdhc->oob_irq_enable_flag = FALSE; +#if defined(OOB_INTR_ONLY) + spin_lock_init(&sdhc->irq_lock); +#endif + + /* chain SDIO Host Controller info together */ + sdhc->next = sdhcinfo; + sdhcinfo = sdhc; + + /* Read the vendor/device ID from the CIS */ + vendevid = bcmsdh_query_device(sdh); + /* try to attach to the target device */ + if (!(sdhc->ch = drvinfo.attach((vendevid >> 16), + (vendevid & 0xFFFF), 0, 0, 0, 0, + (void *)regs, NULL, sdh))) { + SDLX_MSG(("%s: device attach failed\n", __FUNCTION__)); + goto err; + } + + return 0; + + /* error handling */ +err: + if (sdhc) { + if (sdhc->sdh) + bcmsdh_detach(sdhc->osh, sdhc->sdh); + MFREE(osh, sdhc, sizeof(bcmsdh_hc_t)); + } + if (osh) + osl_detach(osh); + return -ENODEV; +} + +#ifndef BCMLXSDMMC +static +#endif /* BCMLXSDMMC */ +int bcmsdh_remove(struct device *dev) +{ + bcmsdh_hc_t *sdhc, *prev; + osl_t *osh; + + sdhc = sdhcinfo; + drvinfo.detach(sdhc->ch); + bcmsdh_detach(sdhc->osh, sdhc->sdh); + + /* find the SDIO Host Controller state for this pdev and take it out from the list */ + for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) { + if (sdhc->dev == (void *)dev) { + if (prev) + prev->next = sdhc->next; + else + sdhcinfo = NULL; + break; + } + prev = sdhc; + } + if (!sdhc) { + SDLX_MSG(("%s: failed\n", __FUNCTION__)); + return 0; + } + + /* release SDIO Host Controller info */ + osh = sdhc->osh; + MFREE(osh, sdhc, sizeof(bcmsdh_hc_t)); + osl_detach(osh); + +#if !defined(BCMLXSDMMC) || defined(OOB_INTR_ONLY) + dev_set_drvdata(dev, NULL); +#endif /* !defined(BCMLXSDMMC) || defined(OOB_INTR_ONLY) */ + + return 0; +} + +#else /* BCMPLATFORM_BUS */ + +#if !defined(BCMLXSDMMC) +/* forward declarations for PCI probe and remove functions. */ +static int __devinit bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent); +static void __devexit bcmsdh_pci_remove(struct pci_dev *pdev); + +/** + * pci id table + */ +static struct pci_device_id bcmsdh_pci_devid[] __devinitdata = { + { vendor: PCI_ANY_ID, + device: PCI_ANY_ID, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + class: 0, + class_mask: 0, + driver_data: 0, + }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, bcmsdh_pci_devid); + +/** + * SDIO Host Controller pci driver info + */ +static struct pci_driver bcmsdh_pci_driver = { + node: {}, + name: "bcmsdh", + id_table: bcmsdh_pci_devid, + probe: bcmsdh_pci_probe, + remove: bcmsdh_pci_remove, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) + save_state: NULL, +#endif + suspend: NULL, + resume: NULL, + }; + + +extern uint sd_pci_slot; /* Force detection to a particular PCI */ + /* slot only . Allows for having multiple */ + /* WL devices at once in a PC */ + /* Only one instance of dhd will be */ + /* usable at a time */ + /* Upper word is bus number, */ + /* lower word is slot number */ + /* Default value of 0xffffffff turns this */ + /* off */ +module_param(sd_pci_slot, uint, 0); + + +/** + * Detect supported SDIO Host Controller and attach if found. + * + * Determine if the device described by pdev is a supported SDIO Host + * Controller. If so, attach to it and attach to the target device. + */ +static int __devinit +bcmsdh_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + osl_t *osh = NULL; + bcmsdh_hc_t *sdhc = NULL; + ulong regs; + bcmsdh_info_t *sdh = NULL; + int rc; + + if (sd_pci_slot != 0xFFFFffff) { + if (pdev->bus->number != (sd_pci_slot>>16) || + PCI_SLOT(pdev->devfn) != (sd_pci_slot&0xffff)) { + SDLX_MSG(("%s: %s: bus %X, slot %X, vend %X, dev %X\n", + __FUNCTION__, + bcmsdh_chipmatch(pdev->vendor, pdev->device) + ?"Found compatible SDIOHC" + :"Probing unknown device", + pdev->bus->number, PCI_SLOT(pdev->devfn), pdev->vendor, + pdev->device)); + return -ENODEV; + } + SDLX_MSG(("%s: %s: bus %X, slot %X, vendor %X, device %X (good PCI location)\n", + __FUNCTION__, + bcmsdh_chipmatch(pdev->vendor, pdev->device) + ?"Using compatible SDIOHC" + :"WARNING, forced use of unkown device", + pdev->bus->number, PCI_SLOT(pdev->devfn), pdev->vendor, pdev->device)); + } + + if ((pdev->vendor == VENDOR_TI) && ((pdev->device == PCIXX21_FLASHMEDIA_ID) || + (pdev->device == PCIXX21_FLASHMEDIA0_ID))) { + uint32 config_reg; + + SDLX_MSG(("%s: Disabling TI FlashMedia Controller.\n", __FUNCTION__)); + if (!(osh = osl_attach(pdev, PCI_BUS, FALSE))) { + SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__)); + goto err; + } + + config_reg = OSL_PCI_READ_CONFIG(osh, 0x4c, 4); + + /* + * Set MMC_SD_DIS bit in FlashMedia Controller. + * Disbling the SD/MMC Controller in the FlashMedia Controller + * allows the Standard SD Host Controller to take over control + * of the SD Slot. + */ + config_reg |= 0x02; + OSL_PCI_WRITE_CONFIG(osh, 0x4c, 4, config_reg); + osl_detach(osh); + } + /* match this pci device with what we support */ + /* we can't solely rely on this to believe it is our SDIO Host Controller! */ + if (!bcmsdh_chipmatch(pdev->vendor, pdev->device)) { + return -ENODEV; + } + + /* this is a pci device we might support */ + SDLX_MSG(("%s: Found possible SDIO Host Controller: bus %d slot %d func %d irq %d\n", + __FUNCTION__, + pdev->bus->number, PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn), pdev->irq)); + + /* use bcmsdh_query_device() to get the vendor ID of the target device so + * it will eventually appear in the Broadcom string on the console + */ + + /* allocate SDIO Host Controller state info */ + if (!(osh = osl_attach(pdev, PCI_BUS, FALSE))) { + SDLX_MSG(("%s: osl_attach failed\n", __FUNCTION__)); + goto err; + } + if (!(sdhc = MALLOC(osh, sizeof(bcmsdh_hc_t)))) { + SDLX_MSG(("%s: out of memory, allocated %d bytes\n", + __FUNCTION__, + MALLOCED(osh))); + goto err; + } + bzero(sdhc, sizeof(bcmsdh_hc_t)); + sdhc->osh = osh; + + sdhc->dev = pdev; + + /* map to address where host can access */ + pci_set_master(pdev); + rc = pci_enable_device(pdev); + if (rc) { + SDLX_MSG(("%s: Cannot enable PCI device\n", __FUNCTION__)); + goto err; + } + if (!(sdh = bcmsdh_attach(osh, (void *)(uintptr)pci_resource_start(pdev, 0), + (void **)®s, pdev->irq))) { + SDLX_MSG(("%s: bcmsdh_attach failed\n", __FUNCTION__)); + goto err; + } + + sdhc->sdh = sdh; + + /* try to attach to the target device */ + if (!(sdhc->ch = drvinfo.attach(VENDOR_BROADCOM, /* pdev->vendor, */ + bcmsdh_query_device(sdh) & 0xFFFF, 0, 0, 0, 0, + (void *)regs, NULL, sdh))) { + SDLX_MSG(("%s: device attach failed\n", __FUNCTION__)); + goto err; + } + + /* chain SDIO Host Controller info together */ + sdhc->next = sdhcinfo; + sdhcinfo = sdhc; + + return 0; + + /* error handling */ +err: + if (sdhc) { + if (sdhc->sdh) + bcmsdh_detach(sdhc->osh, sdhc->sdh); + MFREE(osh, sdhc, sizeof(bcmsdh_hc_t)); + } + if (osh) + osl_detach(osh); + return -ENODEV; +} + + +/** + * Detach from target devices and SDIO Host Controller + */ +static void __devexit +bcmsdh_pci_remove(struct pci_dev *pdev) +{ + bcmsdh_hc_t *sdhc, *prev; + osl_t *osh; + + /* find the SDIO Host Controller state for this pdev and take it out from the list */ + for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) { + if (sdhc->dev == pdev) { + if (prev) + prev->next = sdhc->next; + else + sdhcinfo = NULL; + break; + } + prev = sdhc; + } + if (!sdhc) + return; + + drvinfo.detach(sdhc->ch); + + bcmsdh_detach(sdhc->osh, sdhc->sdh); + + /* release SDIO Host Controller info */ + osh = sdhc->osh; + MFREE(osh, sdhc, sizeof(bcmsdh_hc_t)); + osl_detach(osh); +} +#endif /* BCMLXSDMMC */ +#endif /* BCMPLATFORM_BUS */ + +extern int sdio_function_init(void); + +int +bcmsdh_register(bcmsdh_driver_t *driver) +{ + int error = 0; + + drvinfo = *driver; + +#if defined(BCMPLATFORM_BUS) + SDLX_MSG(("Linux Kernel SDIO/MMC Driver\n")); + error = sdio_function_init(); + return error; +#endif /* defined(BCMPLATFORM_BUS) */ + +#if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC) +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) + if (!(error = pci_module_init(&bcmsdh_pci_driver))) + return 0; +#else + if (!(error = pci_register_driver(&bcmsdh_pci_driver))) + return 0; +#endif + + SDLX_MSG(("%s: pci_module_init failed 0x%x\n", __FUNCTION__, error)); +#endif /* BCMPLATFORM_BUS */ + + return error; +} + +extern void sdio_function_cleanup(void); + +void +bcmsdh_unregister(void) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) + if (bcmsdh_pci_driver.node.next) +#endif + +#if defined(BCMLXSDMMC) + sdio_function_cleanup(); +#endif /* BCMLXSDMMC */ + +#if !defined(BCMPLATFORM_BUS) && !defined(BCMLXSDMMC) + pci_unregister_driver(&bcmsdh_pci_driver); +#endif /* BCMPLATFORM_BUS */ +} + +#if defined(OOB_INTR_ONLY) +void bcmsdh_oob_intr_set(bool enable) +{ + static bool curstate = 1; + unsigned long flags; + + spin_lock_irqsave(&sdhcinfo->irq_lock, flags); + if (curstate != enable) { + if (enable) + enable_irq(sdhcinfo->oob_irq); + else + disable_irq_nosync(sdhcinfo->oob_irq); + curstate = enable; + } + spin_unlock_irqrestore(&sdhcinfo->irq_lock, flags); +} + +static irqreturn_t wlan_oob_irq(int irq, void *dev_id) +{ + dhd_pub_t *dhdp; + + dhdp = (dhd_pub_t *)dev_get_drvdata(sdhcinfo->dev); + + bcmsdh_oob_intr_set(0); + + if (dhdp == NULL) { + SDLX_MSG(("Out of band GPIO interrupt fired way too early\n")); + return IRQ_HANDLED; + } + + dhdsdio_isr((void *)dhdp->bus); + + return IRQ_HANDLED; +} + +int bcmsdh_register_oob_intr(void * dhdp) +{ + int error = 0; + + SDLX_MSG(("%s Enter \n", __FUNCTION__)); + + /* IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE; */ + + dev_set_drvdata(sdhcinfo->dev, dhdp); + + if (!sdhcinfo->oob_irq_registered) { + SDLX_MSG(("%s IRQ=%d Type=%X \n", __FUNCTION__, + (int)sdhcinfo->oob_irq, (int)sdhcinfo->oob_flags)); + /* Refer to customer Host IRQ docs about proper irqflags definition */ + error = request_irq(sdhcinfo->oob_irq, wlan_oob_irq, sdhcinfo->oob_flags, + "bcmsdh_sdmmc", NULL); + if (error) + return -ENODEV; + + enable_irq_wake(sdhcinfo->oob_irq); + sdhcinfo->oob_irq_registered = TRUE; + sdhcinfo->oob_irq_enable_flag = TRUE; + } + + return 0; +} + +void bcmsdh_set_irq(int flag) +{ + if (sdhcinfo->oob_irq_registered && sdhcinfo->oob_irq_enable_flag != flag) { + SDLX_MSG(("%s Flag = %d", __FUNCTION__, flag)); + sdhcinfo->oob_irq_enable_flag = flag; + if (flag) { + enable_irq(sdhcinfo->oob_irq); + enable_irq_wake(sdhcinfo->oob_irq); + } else { + disable_irq_wake(sdhcinfo->oob_irq); + disable_irq(sdhcinfo->oob_irq); + } + } +} + +void bcmsdh_unregister_oob_intr(void) +{ + SDLX_MSG(("%s: Enter\n", __FUNCTION__)); + + if (sdhcinfo->oob_irq_registered == TRUE) { + bcmsdh_set_irq(FALSE); + free_irq(sdhcinfo->oob_irq, NULL); + sdhcinfo->oob_irq_registered = FALSE; + } +} +#endif /* defined(OOB_INTR_ONLY) */ + +#if defined(BCMLXSDMMC) +void *bcmsdh_get_drvdata(void) +{ + if (!sdhcinfo) + return NULL; + return dev_get_drvdata(sdhcinfo->dev); +} +#endif + +/* Module parameters specific to each host-controller driver */ + +extern uint sd_msglevel; /* Debug message level */ +module_param(sd_msglevel, uint, 0); + +extern uint sd_power; /* 0 = SD Power OFF, 1 = SD Power ON. */ +module_param(sd_power, uint, 0); + +extern uint sd_clock; /* SD Clock Control, 0 = SD Clock OFF, 1 = SD Clock ON */ +module_param(sd_clock, uint, 0); + +extern uint sd_divisor; /* Divisor (-1 means external clock) */ +module_param(sd_divisor, uint, 0); + +extern uint sd_sdmode; /* Default is SD4, 0=SPI, 1=SD1, 2=SD4 */ +module_param(sd_sdmode, uint, 0); + +extern uint sd_hiok; /* Ok to use hi-speed mode */ +module_param(sd_hiok, uint, 0); + +extern uint sd_f2_blocksize; +module_param(sd_f2_blocksize, int, 0); + +#ifdef BCMSDIOH_STD +extern int sd_uhsimode; +module_param(sd_uhsimode, int, 0); +#endif + +#ifdef BCMSDH_MODULE +EXPORT_SYMBOL(bcmsdh_attach); +EXPORT_SYMBOL(bcmsdh_detach); +EXPORT_SYMBOL(bcmsdh_intr_query); +EXPORT_SYMBOL(bcmsdh_intr_enable); +EXPORT_SYMBOL(bcmsdh_intr_disable); +EXPORT_SYMBOL(bcmsdh_intr_reg); +EXPORT_SYMBOL(bcmsdh_intr_dereg); + +#if defined(DHD_DEBUG) +EXPORT_SYMBOL(bcmsdh_intr_pending); +#endif + +EXPORT_SYMBOL(bcmsdh_devremove_reg); +EXPORT_SYMBOL(bcmsdh_cfg_read); +EXPORT_SYMBOL(bcmsdh_cfg_write); +EXPORT_SYMBOL(bcmsdh_cis_read); +EXPORT_SYMBOL(bcmsdh_reg_read); +EXPORT_SYMBOL(bcmsdh_reg_write); +EXPORT_SYMBOL(bcmsdh_regfail); +EXPORT_SYMBOL(bcmsdh_send_buf); +EXPORT_SYMBOL(bcmsdh_recv_buf); + +EXPORT_SYMBOL(bcmsdh_rwdata); +EXPORT_SYMBOL(bcmsdh_abort); +EXPORT_SYMBOL(bcmsdh_query_device); +EXPORT_SYMBOL(bcmsdh_query_iofnum); +EXPORT_SYMBOL(bcmsdh_iovar_op); +EXPORT_SYMBOL(bcmsdh_register); +EXPORT_SYMBOL(bcmsdh_unregister); +EXPORT_SYMBOL(bcmsdh_chipmatch); +EXPORT_SYMBOL(bcmsdh_reset); +EXPORT_SYMBOL(bcmsdh_waitlockfree); + +EXPORT_SYMBOL(bcmsdh_get_dstatus); +EXPORT_SYMBOL(bcmsdh_cfg_read_word); +EXPORT_SYMBOL(bcmsdh_cfg_write_word); +EXPORT_SYMBOL(bcmsdh_cur_sbwad); +EXPORT_SYMBOL(bcmsdh_chipinfo); + +#endif /* BCMSDH_MODULE */ diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c new file mode 100644 index 0000000000000..374154fb12961 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c @@ -0,0 +1,1407 @@ +/* + * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdh_sdmmc.c 314904 2012-02-14 21:36:04Z $ + */ +#include + +#include +#include +#include +#include +#include /* SDIO Device and Protocol Specs */ +#include /* Standard SDIO Host Controller Specification */ +#include /* bcmsdh to/from specific controller APIs */ +#include /* ioctl/iovars */ + +#include +#include +#include +#include + +#include +#include + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) +#include +extern volatile bool dhd_mmc_suspend; +#endif +#include "bcmsdh_sdmmc.h" + +#ifndef BCMSDH_MODULE +extern int sdio_function_init(void); +extern void sdio_function_cleanup(void); +#endif /* BCMSDH_MODULE */ + +#if !defined(OOB_INTR_ONLY) +static void IRQHandler(struct sdio_func *func); +static void IRQHandlerF2(struct sdio_func *func); +#endif /* !defined(OOB_INTR_ONLY) */ +static int sdioh_sdmmc_get_cisaddr(sdioh_info_t *sd, uint32 regaddr); +extern int sdio_reset_comm(struct mmc_card *card); + +extern PBCMSDH_SDMMC_INSTANCE gInstance; + +uint sd_sdmode = SDIOH_MODE_SD4; /* Use SD4 mode by default */ +uint sd_f2_blocksize = 512; /* Default blocksize */ + +uint sd_divisor = 2; /* Default 48MHz/2 = 24MHz */ + +uint sd_power = 1; /* Default to SD Slot powered ON */ +uint sd_clock = 1; /* Default to SD Clock turned ON */ +uint sd_hiok = FALSE; /* Don't use hi-speed mode by default */ +uint sd_msglevel = 0x01; +uint sd_use_dma = TRUE; +DHD_PM_RESUME_WAIT_INIT(sdioh_request_byte_wait); +DHD_PM_RESUME_WAIT_INIT(sdioh_request_word_wait); +DHD_PM_RESUME_WAIT_INIT(sdioh_request_packet_wait); +DHD_PM_RESUME_WAIT_INIT(sdioh_request_buffer_wait); + +#define DMA_ALIGN_MASK 0x03 + +int sdioh_sdmmc_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data); + +static int +sdioh_sdmmc_card_enablefuncs(sdioh_info_t *sd) +{ + int err_ret; + uint32 fbraddr; + uint8 func; + + sd_trace(("%s\n", __FUNCTION__)); + + /* Get the Card's common CIS address */ + sd->com_cis_ptr = sdioh_sdmmc_get_cisaddr(sd, SDIOD_CCCR_CISPTR_0); + sd->func_cis_ptr[0] = sd->com_cis_ptr; + sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr)); + + /* Get the Card's function CIS (for each function) */ + for (fbraddr = SDIOD_FBR_STARTADDR, func = 1; + func <= sd->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) { + sd->func_cis_ptr[func] = sdioh_sdmmc_get_cisaddr(sd, SDIOD_FBR_CISPTR_0 + fbraddr); + sd_info(("%s: Function %d CIS Ptr = 0x%x\n", + __FUNCTION__, func, sd->func_cis_ptr[func])); + } + + sd->func_cis_ptr[0] = sd->com_cis_ptr; + sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __FUNCTION__, sd->com_cis_ptr)); + + /* Enable Function 1 */ + sdio_claim_host(gInstance->func[1]); + err_ret = sdio_enable_func(gInstance->func[1]); + sdio_release_host(gInstance->func[1]); + if (err_ret) { + sd_err(("bcmsdh_sdmmc: Failed to enable F1 Err: 0x%08x", err_ret)); + } + + return FALSE; +} + +/* + * Public entry points & extern's + */ +extern sdioh_info_t * +sdioh_attach(osl_t *osh, void *bar0, uint irq) +{ + sdioh_info_t *sd; + int err_ret; + + sd_trace(("%s\n", __FUNCTION__)); + + if (gInstance == NULL) { + sd_err(("%s: SDIO Device not present\n", __FUNCTION__)); + return NULL; + } + + if ((sd = (sdioh_info_t *)MALLOC(osh, sizeof(sdioh_info_t))) == NULL) { + sd_err(("sdioh_attach: out of memory, malloced %d bytes\n", MALLOCED(osh))); + return NULL; + } + bzero((char *)sd, sizeof(sdioh_info_t)); + sd->osh = osh; + if (sdioh_sdmmc_osinit(sd) != 0) { + sd_err(("%s:sdioh_sdmmc_osinit() failed\n", __FUNCTION__)); + MFREE(sd->osh, sd, sizeof(sdioh_info_t)); + return NULL; + } + + sd->num_funcs = 2; + sd->sd_blockmode = TRUE; + sd->use_client_ints = TRUE; + sd->client_block_size[0] = 64; + sd->use_rxchain = FALSE; + + gInstance->sd = sd; + + /* Claim host controller */ + sdio_claim_host(gInstance->func[1]); + + sd->client_block_size[1] = 64; + err_ret = sdio_set_block_size(gInstance->func[1], 64); + if (err_ret) { + sd_err(("bcmsdh_sdmmc: Failed to set F1 blocksize\n")); + } + + /* Release host controller F1 */ + sdio_release_host(gInstance->func[1]); + + if (gInstance->func[2]) { + /* Claim host controller F2 */ + sdio_claim_host(gInstance->func[2]); + + sd->client_block_size[2] = sd_f2_blocksize; + err_ret = sdio_set_block_size(gInstance->func[2], sd_f2_blocksize); + if (err_ret) { + sd_err(("bcmsdh_sdmmc: Failed to set F2 blocksize to %d\n", + sd_f2_blocksize)); + } + + /* Release host controller F2 */ + sdio_release_host(gInstance->func[2]); + } + + sdioh_sdmmc_card_enablefuncs(sd); + + sd_trace(("%s: Done\n", __FUNCTION__)); + return sd; +} + + +extern SDIOH_API_RC +sdioh_detach(osl_t *osh, sdioh_info_t *sd) +{ + sd_trace(("%s\n", __FUNCTION__)); + + if (sd) { + + /* Disable Function 2 */ + sdio_claim_host(gInstance->func[2]); + sdio_disable_func(gInstance->func[2]); + sdio_release_host(gInstance->func[2]); + + /* Disable Function 1 */ + if (gInstance->func[1]) { + sdio_claim_host(gInstance->func[1]); + sdio_disable_func(gInstance->func[1]); + sdio_release_host(gInstance->func[1]); + } + + gInstance->func[1] = NULL; + gInstance->func[2] = NULL; + + /* deregister irq */ + sdioh_sdmmc_osfree(sd); + + MFREE(sd->osh, sd, sizeof(sdioh_info_t)); + } + return SDIOH_API_RC_SUCCESS; +} + +#if defined(OOB_INTR_ONLY) && defined(HW_OOB) + +extern SDIOH_API_RC +sdioh_enable_func_intr(void) +{ + uint8 reg; + int err; + + if (gInstance->func[0]) { + sdio_claim_host(gInstance->func[0]); + + reg = sdio_readb(gInstance->func[0], SDIOD_CCCR_INTEN, &err); + if (err) { + sd_err(("%s: error for read SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err)); + sdio_release_host(gInstance->func[0]); + return SDIOH_API_RC_FAIL; + } + + /* Enable F1 and F2 interrupts, set master enable */ + reg |= (INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN | INTR_CTL_MASTER_EN); + + sdio_writeb(gInstance->func[0], reg, SDIOD_CCCR_INTEN, &err); + sdio_release_host(gInstance->func[0]); + + if (err) { + sd_err(("%s: error for write SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err)); + return SDIOH_API_RC_FAIL; + } + } + + return SDIOH_API_RC_SUCCESS; +} + +extern SDIOH_API_RC +sdioh_disable_func_intr(void) +{ + uint8 reg; + int err; + + if (gInstance->func[0]) { + sdio_claim_host(gInstance->func[0]); + reg = sdio_readb(gInstance->func[0], SDIOD_CCCR_INTEN, &err); + if (err) { + sd_err(("%s: error for read SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err)); + sdio_release_host(gInstance->func[0]); + return SDIOH_API_RC_FAIL; + } + + reg &= ~(INTR_CTL_FUNC1_EN | INTR_CTL_FUNC2_EN); + /* Disable master interrupt with the last function interrupt */ + if (!(reg & 0xFE)) + reg = 0; + sdio_writeb(gInstance->func[0], reg, SDIOD_CCCR_INTEN, &err); + + sdio_release_host(gInstance->func[0]); + if (err) { + sd_err(("%s: error for write SDIO_CCCR_IENx : 0x%x\n", __FUNCTION__, err)); + return SDIOH_API_RC_FAIL; + } + } + return SDIOH_API_RC_SUCCESS; +} +#endif /* defined(OOB_INTR_ONLY) && defined(HW_OOB) */ + +/* Configure callback to client when we recieve client interrupt */ +extern SDIOH_API_RC +sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh) +{ + sd_trace(("%s: Entering\n", __FUNCTION__)); + if (fn == NULL) { + sd_err(("%s: interrupt handler is NULL, not registering\n", __FUNCTION__)); + return SDIOH_API_RC_FAIL; + } +#if !defined(OOB_INTR_ONLY) + sd->intr_handler = fn; + sd->intr_handler_arg = argh; + sd->intr_handler_valid = TRUE; + + /* register and unmask irq */ + if (gInstance->func[2]) { + sdio_claim_host(gInstance->func[2]); + sdio_claim_irq(gInstance->func[2], IRQHandlerF2); + sdio_release_host(gInstance->func[2]); + } + + if (gInstance->func[1]) { + sdio_claim_host(gInstance->func[1]); + sdio_claim_irq(gInstance->func[1], IRQHandler); + sdio_release_host(gInstance->func[1]); + } +#elif defined(HW_OOB) + sdioh_enable_func_intr(); +#endif /* !defined(OOB_INTR_ONLY) */ + + return SDIOH_API_RC_SUCCESS; +} + +extern SDIOH_API_RC +sdioh_interrupt_deregister(sdioh_info_t *sd) +{ + sd_trace(("%s: Entering\n", __FUNCTION__)); + +#if !defined(OOB_INTR_ONLY) + if (gInstance->func[1]) { + /* register and unmask irq */ + sdio_claim_host(gInstance->func[1]); + sdio_release_irq(gInstance->func[1]); + sdio_release_host(gInstance->func[1]); + } + + if (gInstance->func[2]) { + /* Claim host controller F2 */ + sdio_claim_host(gInstance->func[2]); + sdio_release_irq(gInstance->func[2]); + /* Release host controller F2 */ + sdio_release_host(gInstance->func[2]); + } + + sd->intr_handler_valid = FALSE; + sd->intr_handler = NULL; + sd->intr_handler_arg = NULL; +#elif defined(HW_OOB) + sdioh_disable_func_intr(); +#endif /* !defined(OOB_INTR_ONLY) */ + return SDIOH_API_RC_SUCCESS; +} + +extern SDIOH_API_RC +sdioh_interrupt_query(sdioh_info_t *sd, bool *onoff) +{ + sd_trace(("%s: Entering\n", __FUNCTION__)); + *onoff = sd->client_intr_enabled; + return SDIOH_API_RC_SUCCESS; +} + +#if defined(DHD_DEBUG) +extern bool +sdioh_interrupt_pending(sdioh_info_t *sd) +{ + return (0); +} +#endif + +uint +sdioh_query_iofnum(sdioh_info_t *sd) +{ + return sd->num_funcs; +} + +/* IOVar table */ +enum { + IOV_MSGLEVEL = 1, + IOV_BLOCKMODE, + IOV_BLOCKSIZE, + IOV_DMA, + IOV_USEINTS, + IOV_NUMINTS, + IOV_NUMLOCALINTS, + IOV_HOSTREG, + IOV_DEVREG, + IOV_DIVISOR, + IOV_SDMODE, + IOV_HISPEED, + IOV_HCIREGS, + IOV_POWER, + IOV_CLOCK, + IOV_RXCHAIN +}; + +const bcm_iovar_t sdioh_iovars[] = { + {"sd_msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 }, + {"sd_blockmode", IOV_BLOCKMODE, 0, IOVT_BOOL, 0 }, + {"sd_blocksize", IOV_BLOCKSIZE, 0, IOVT_UINT32, 0 }, /* ((fn << 16) | size) */ + {"sd_dma", IOV_DMA, 0, IOVT_BOOL, 0 }, + {"sd_ints", IOV_USEINTS, 0, IOVT_BOOL, 0 }, + {"sd_numints", IOV_NUMINTS, 0, IOVT_UINT32, 0 }, + {"sd_numlocalints", IOV_NUMLOCALINTS, 0, IOVT_UINT32, 0 }, + {"sd_hostreg", IOV_HOSTREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, + {"sd_devreg", IOV_DEVREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, + {"sd_divisor", IOV_DIVISOR, 0, IOVT_UINT32, 0 }, + {"sd_power", IOV_POWER, 0, IOVT_UINT32, 0 }, + {"sd_clock", IOV_CLOCK, 0, IOVT_UINT32, 0 }, + {"sd_mode", IOV_SDMODE, 0, IOVT_UINT32, 100}, + {"sd_highspeed", IOV_HISPEED, 0, IOVT_UINT32, 0 }, + {"sd_rxchain", IOV_RXCHAIN, 0, IOVT_BOOL, 0 }, + {NULL, 0, 0, 0, 0 } +}; + +int +sdioh_iovar_op(sdioh_info_t *si, const char *name, + void *params, int plen, void *arg, int len, bool set) +{ + const bcm_iovar_t *vi = NULL; + int bcmerror = 0; + int val_size; + int32 int_val = 0; + bool bool_val; + uint32 actionid; + + ASSERT(name); + ASSERT(len >= 0); + + /* Get must have return space; Set does not take qualifiers */ + ASSERT(set || (arg && len)); + ASSERT(!set || (!params && !plen)); + + sd_trace(("%s: Enter (%s %s)\n", __FUNCTION__, (set ? "set" : "get"), name)); + + if ((vi = bcm_iovar_lookup(sdioh_iovars, name)) == NULL) { + bcmerror = BCME_UNSUPPORTED; + goto exit; + } + + if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, set)) != 0) + goto exit; + + /* Set up params so get and set can share the convenience variables */ + if (params == NULL) { + params = arg; + plen = len; + } + + if (vi->type == IOVT_VOID) + val_size = 0; + else if (vi->type == IOVT_BUFFER) + val_size = len; + else + val_size = sizeof(int); + + if (plen >= (int)sizeof(int_val)) + bcopy(params, &int_val, sizeof(int_val)); + + bool_val = (int_val != 0) ? TRUE : FALSE; + BCM_REFERENCE(bool_val); + + actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid); + switch (actionid) { + case IOV_GVAL(IOV_MSGLEVEL): + int_val = (int32)sd_msglevel; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_MSGLEVEL): + sd_msglevel = int_val; + break; + + case IOV_GVAL(IOV_BLOCKMODE): + int_val = (int32)si->sd_blockmode; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_BLOCKMODE): + si->sd_blockmode = (bool)int_val; + /* Haven't figured out how to make non-block mode with DMA */ + break; + + case IOV_GVAL(IOV_BLOCKSIZE): + if ((uint32)int_val > si->num_funcs) { + bcmerror = BCME_BADARG; + break; + } + int_val = (int32)si->client_block_size[int_val]; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_BLOCKSIZE): + { + uint func = ((uint32)int_val >> 16); + uint blksize = (uint16)int_val; + uint maxsize; + + if (func > si->num_funcs) { + bcmerror = BCME_BADARG; + break; + } + + switch (func) { + case 0: maxsize = 32; break; + case 1: maxsize = BLOCK_SIZE_4318; break; + case 2: maxsize = BLOCK_SIZE_4328; break; + default: maxsize = 0; + } + if (blksize > maxsize) { + bcmerror = BCME_BADARG; + break; + } + if (!blksize) { + blksize = maxsize; + } + + /* Now set it */ + si->client_block_size[func] = blksize; + + break; + } + + case IOV_GVAL(IOV_RXCHAIN): + int_val = (int32)si->use_rxchain; + bcopy(&int_val, arg, val_size); + break; + + case IOV_GVAL(IOV_DMA): + int_val = (int32)si->sd_use_dma; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_DMA): + si->sd_use_dma = (bool)int_val; + break; + + case IOV_GVAL(IOV_USEINTS): + int_val = (int32)si->use_client_ints; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_USEINTS): + si->use_client_ints = (bool)int_val; + if (si->use_client_ints) + si->intmask |= CLIENT_INTR; + else + si->intmask &= ~CLIENT_INTR; + + break; + + case IOV_GVAL(IOV_DIVISOR): + int_val = (uint32)sd_divisor; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_DIVISOR): + sd_divisor = int_val; + break; + + case IOV_GVAL(IOV_POWER): + int_val = (uint32)sd_power; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_POWER): + sd_power = int_val; + break; + + case IOV_GVAL(IOV_CLOCK): + int_val = (uint32)sd_clock; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_CLOCK): + sd_clock = int_val; + break; + + case IOV_GVAL(IOV_SDMODE): + int_val = (uint32)sd_sdmode; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_SDMODE): + sd_sdmode = int_val; + break; + + case IOV_GVAL(IOV_HISPEED): + int_val = (uint32)sd_hiok; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_HISPEED): + sd_hiok = int_val; + break; + + case IOV_GVAL(IOV_NUMINTS): + int_val = (int32)si->intrcount; + bcopy(&int_val, arg, val_size); + break; + + case IOV_GVAL(IOV_NUMLOCALINTS): + int_val = (int32)0; + bcopy(&int_val, arg, val_size); + break; + + case IOV_GVAL(IOV_HOSTREG): + { + sdreg_t *sd_ptr = (sdreg_t *)params; + + if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) { + sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset)); + bcmerror = BCME_BADARG; + break; + } + + sd_trace(("%s: rreg%d at offset %d\n", __FUNCTION__, + (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32), + sd_ptr->offset)); + if (sd_ptr->offset & 1) + int_val = 8; /* sdioh_sdmmc_rreg8(si, sd_ptr->offset); */ + else if (sd_ptr->offset & 2) + int_val = 16; /* sdioh_sdmmc_rreg16(si, sd_ptr->offset); */ + else + int_val = 32; /* sdioh_sdmmc_rreg(si, sd_ptr->offset); */ + + bcopy(&int_val, arg, sizeof(int_val)); + break; + } + + case IOV_SVAL(IOV_HOSTREG): + { + sdreg_t *sd_ptr = (sdreg_t *)params; + + if (sd_ptr->offset < SD_SysAddr || sd_ptr->offset > SD_MaxCurCap) { + sd_err(("%s: bad offset 0x%x\n", __FUNCTION__, sd_ptr->offset)); + bcmerror = BCME_BADARG; + break; + } + + sd_trace(("%s: wreg%d value 0x%08x at offset %d\n", __FUNCTION__, sd_ptr->value, + (sd_ptr->offset & 1) ? 8 : ((sd_ptr->offset & 2) ? 16 : 32), + sd_ptr->offset)); + break; + } + + case IOV_GVAL(IOV_DEVREG): + { + sdreg_t *sd_ptr = (sdreg_t *)params; + uint8 data = 0; + + if (sdioh_cfg_read(si, sd_ptr->func, sd_ptr->offset, &data)) { + bcmerror = BCME_SDIO_ERROR; + break; + } + + int_val = (int)data; + bcopy(&int_val, arg, sizeof(int_val)); + break; + } + + case IOV_SVAL(IOV_DEVREG): + { + sdreg_t *sd_ptr = (sdreg_t *)params; + uint8 data = (uint8)sd_ptr->value; + + if (sdioh_cfg_write(si, sd_ptr->func, sd_ptr->offset, &data)) { + bcmerror = BCME_SDIO_ERROR; + break; + } + break; + } + + default: + bcmerror = BCME_UNSUPPORTED; + break; + } +exit: + + return bcmerror; +} + +#if defined(OOB_INTR_ONLY) && defined(HW_OOB) + +SDIOH_API_RC +sdioh_enable_hw_oob_intr(sdioh_info_t *sd, bool enable) +{ + SDIOH_API_RC status; + uint8 data; + + if (enable) + data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE | SDIO_SEPINT_ACT_HI; + else + data = SDIO_SEPINT_ACT_HI; + + status = sdioh_request_byte(sd, SDIOH_WRITE, 0, SDIOD_CCCR_BRCM_SEPINT, &data); + return status; +} +#endif /* defined(OOB_INTR_ONLY) && defined(HW_OOB) */ + +extern SDIOH_API_RC +sdioh_cfg_read(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data) +{ + SDIOH_API_RC status; + /* No lock needed since sdioh_request_byte does locking */ + status = sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data); + return status; +} + +extern SDIOH_API_RC +sdioh_cfg_write(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data) +{ + /* No lock needed since sdioh_request_byte does locking */ + SDIOH_API_RC status; + status = sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data); + return status; +} + +static int +sdioh_sdmmc_get_cisaddr(sdioh_info_t *sd, uint32 regaddr) +{ + /* read 24 bits and return valid 17 bit addr */ + int i; + uint32 scratch, regdata; + uint8 *ptr = (uint8 *)&scratch; + for (i = 0; i < 3; i++) { + if ((sdioh_sdmmc_card_regread (sd, 0, regaddr, 1, ®data)) != SUCCESS) + sd_err(("%s: Can't read!\n", __FUNCTION__)); + + *ptr++ = (uint8) regdata; + regaddr++; + } + + /* Only the lower 17-bits are valid */ + scratch = ltoh32(scratch); + scratch &= 0x0001FFFF; + return (scratch); +} + +extern SDIOH_API_RC +sdioh_cis_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 length) +{ + uint32 count; + int offset; + uint32 foo; + uint8 *cis = cisd; + + sd_trace(("%s: Func = %d\n", __FUNCTION__, func)); + + if (!sd->func_cis_ptr[func]) { + bzero(cis, length); + sd_err(("%s: no func_cis_ptr[%d]\n", __FUNCTION__, func)); + return SDIOH_API_RC_FAIL; + } + + sd_err(("%s: func_cis_ptr[%d]=0x%04x\n", __FUNCTION__, func, sd->func_cis_ptr[func])); + + for (count = 0; count < length; count++) { + offset = sd->func_cis_ptr[func] + count; + if (sdioh_sdmmc_card_regread (sd, 0, offset, 1, &foo) < 0) { + sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__)); + return SDIOH_API_RC_FAIL; + } + + *cis = (uint8)(foo & 0xff); + cis++; + } + + return SDIOH_API_RC_SUCCESS; +} + +extern SDIOH_API_RC +sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *byte) +{ + int err_ret; + + sd_info(("%s: rw=%d, func=%d, addr=0x%05x\n", __FUNCTION__, rw, func, regaddr)); + + DHD_PM_RESUME_WAIT(sdioh_request_byte_wait); + DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); + if(rw) { /* CMD52 Write */ + if (func == 0) { + /* Can only directly write to some F0 registers. Handle F2 enable + * as a special case. + */ + if (regaddr == SDIOD_CCCR_IOEN) { + if (gInstance->func[2]) { + sdio_claim_host(gInstance->func[2]); + if (*byte & SDIO_FUNC_ENABLE_2) { + /* Enable Function 2 */ + err_ret = sdio_enable_func(gInstance->func[2]); + if (err_ret) { + sd_err(("bcmsdh_sdmmc: enable F2 failed:%d", + err_ret)); + } + } else { + /* Disable Function 2 */ + err_ret = sdio_disable_func(gInstance->func[2]); + if (err_ret) { + sd_err(("bcmsdh_sdmmc: Disab F2 failed:%d", + err_ret)); + } + } + sdio_release_host(gInstance->func[2]); + } + } +#if defined(MMC_SDIO_ABORT) + /* to allow abort command through F1 */ + else if (regaddr == SDIOD_CCCR_IOABORT) { + sdio_claim_host(gInstance->func[func]); + /* + * this sdio_f0_writeb() can be replaced with another api + * depending upon MMC driver change. + * As of this time, this is temporaray one + */ + sdio_writeb(gInstance->func[func], *byte, regaddr, &err_ret); + sdio_release_host(gInstance->func[func]); + } +#endif /* MMC_SDIO_ABORT */ + else if (regaddr < 0xF0) { + sd_err(("bcmsdh_sdmmc: F0 Wr:0x%02x: write disallowed\n", regaddr)); + } else { + /* Claim host controller, perform F0 write, and release */ + sdio_claim_host(gInstance->func[func]); + sdio_f0_writeb(gInstance->func[func], *byte, regaddr, &err_ret); + sdio_release_host(gInstance->func[func]); + } + } else { + /* Claim host controller, perform Fn write, and release */ + sdio_claim_host(gInstance->func[func]); + sdio_writeb(gInstance->func[func], *byte, regaddr, &err_ret); + sdio_release_host(gInstance->func[func]); + } + } else { /* CMD52 Read */ + /* Claim host controller, perform Fn read, and release */ + sdio_claim_host(gInstance->func[func]); + + if (func == 0) { + *byte = sdio_f0_readb(gInstance->func[func], regaddr, &err_ret); + } else { + *byte = sdio_readb(gInstance->func[func], regaddr, &err_ret); + } + + sdio_release_host(gInstance->func[func]); + } + + if (err_ret) { + sd_err(("bcmsdh_sdmmc: Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n", + rw ? "Write" : "Read", func, regaddr, *byte, err_ret)); + } + + return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); +} + +extern SDIOH_API_RC +sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr, + uint32 *word, uint nbytes) +{ + int err_ret = SDIOH_API_RC_FAIL; + + if (func == 0) { + sd_err(("%s: Only CMD52 allowed to F0.\n", __FUNCTION__)); + return SDIOH_API_RC_FAIL; + } + + sd_info(("%s: cmd_type=%d, rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", + __FUNCTION__, cmd_type, rw, func, addr, nbytes)); + + DHD_PM_RESUME_WAIT(sdioh_request_word_wait); + DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); + /* Claim host controller */ + sdio_claim_host(gInstance->func[func]); + + if(rw) { /* CMD52 Write */ + if (nbytes == 4) { + sdio_writel(gInstance->func[func], *word, addr, &err_ret); + } else if (nbytes == 2) { + sdio_writew(gInstance->func[func], (*word & 0xFFFF), addr, &err_ret); + } else { + sd_err(("%s: Invalid nbytes: %d\n", __FUNCTION__, nbytes)); + } + } else { /* CMD52 Read */ + if (nbytes == 4) { + *word = sdio_readl(gInstance->func[func], addr, &err_ret); + } else if (nbytes == 2) { + *word = sdio_readw(gInstance->func[func], addr, &err_ret) & 0xFFFF; + } else { + sd_err(("%s: Invalid nbytes: %d\n", __FUNCTION__, nbytes)); + } + } + + /* Release host controller */ + sdio_release_host(gInstance->func[func]); + + if (err_ret) { + sd_err(("bcmsdh_sdmmc: Failed to %s word, Err: 0x%08x", + rw ? "Write" : "Read", err_ret)); + } + + return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); +} + +static SDIOH_API_RC +sdioh_request_packet(sdioh_info_t *sd, uint fix_inc, uint write, uint func, + uint addr, void *pkt) +{ + bool fifo = (fix_inc == SDIOH_DATA_FIX); + uint32 SGCount = 0; + int err_ret = 0; + void *pnext, *pprev; + uint ttl_len, dma_len, lft_len, xfred_len, pkt_len; + uint blk_num; + struct mmc_request mmc_req; + struct mmc_command mmc_cmd; + struct mmc_data mmc_dat; + + sd_trace(("%s: Enter\n", __FUNCTION__)); + + ASSERT(pkt); + DHD_PM_RESUME_WAIT(sdioh_request_packet_wait); + DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); + + ttl_len = xfred_len = 0; + /* at least 4 bytes alignment of skb buff is guaranteed */ + for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) + ttl_len += PKTLEN(sd->osh, pnext); + + if (!sd->use_rxchain || ttl_len <= sd->client_block_size[func]) { + blk_num = 0; + dma_len = 0; + } else { + blk_num = ttl_len / sd->client_block_size[func]; + dma_len = blk_num * sd->client_block_size[func]; + } + lft_len = ttl_len - dma_len; + + sd_trace(("%s: %s %dB to func%d:%08x, %d blks with DMA, %dB leftover\n", + __FUNCTION__, write ? "W" : "R", + ttl_len, func, addr, blk_num, lft_len)); + + if (0 != dma_len) { + memset(&mmc_req, 0, sizeof(struct mmc_request)); + memset(&mmc_cmd, 0, sizeof(struct mmc_command)); + memset(&mmc_dat, 0, sizeof(struct mmc_data)); + + /* Set up DMA descriptors */ + pprev = pkt; + for (pnext = pkt; + pnext && dma_len; + pnext = PKTNEXT(sd->osh, pnext)) { + pkt_len = PKTLEN(sd->osh, pnext); + + if (dma_len > pkt_len) + dma_len -= pkt_len; + else { + pkt_len = xfred_len = dma_len; + dma_len = 0; + pkt = pnext; + } + + sg_set_buf(&sd->sg_list[SGCount++], + (uint8*)PKTDATA(sd->osh, pnext), + pkt_len); + + if (SGCount >= SDIOH_SDMMC_MAX_SG_ENTRIES) { + sd_err(("%s: sg list entries exceed limit\n", + __FUNCTION__)); + return (SDIOH_API_RC_FAIL); + } + } + + mmc_dat.sg = sd->sg_list; + mmc_dat.sg_len = SGCount; + mmc_dat.blksz = sd->client_block_size[func]; + mmc_dat.blocks = blk_num; + mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + + mmc_cmd.opcode = 53; /* SD_IO_RW_EXTENDED */ + mmc_cmd.arg = write ? 1<<31 : 0; + mmc_cmd.arg |= (func & 0x7) << 28; + mmc_cmd.arg |= 1<<27; + mmc_cmd.arg |= fifo ? 0 : 1<<26; + mmc_cmd.arg |= (addr & 0x1FFFF) << 9; + mmc_cmd.arg |= blk_num & 0x1FF; + mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + + mmc_req.cmd = &mmc_cmd; + mmc_req.data = &mmc_dat; + + sdio_claim_host(gInstance->func[func]); + mmc_set_data_timeout(&mmc_dat, gInstance->func[func]->card); + mmc_wait_for_req(gInstance->func[func]->card->host, &mmc_req); + sdio_release_host(gInstance->func[func]); + + err_ret = mmc_cmd.error? mmc_cmd.error : mmc_dat.error; + if (0 != err_ret) { + sd_err(("%s:CMD53 %s failed with code %d\n", + __FUNCTION__, + write ? "write" : "read", + err_ret)); + sd_err(("%s:Disabling rxchain and fire it with PIO\n", + __FUNCTION__)); + sd->use_rxchain = FALSE; + pkt = pprev; + lft_len = ttl_len; + } else if (!fifo) { + addr = addr + ttl_len - lft_len - dma_len; + } + } + + /* PIO mode */ + if (0 != lft_len) { + /* Claim host controller */ + sdio_claim_host(gInstance->func[func]); + for (pnext = pkt; pnext; pnext = PKTNEXT(sd->osh, pnext)) { + uint8 *buf = (uint8*)PKTDATA(sd->osh, pnext) + + xfred_len; + pkt_len = PKTLEN(sd->osh, pnext); + if (0 != xfred_len) { + pkt_len -= xfred_len; + xfred_len = 0; + } + pkt_len = (pkt_len + 3) & 0xFFFFFFFC; +#ifdef CONFIG_MMC_MSM7X00A + if ((pkt_len % 64) == 32) { + sd_trace(("%s: Rounding up TX packet +=32\n", __FUNCTION__)); + pkt_len += 32; + } +#endif /* CONFIG_MMC_MSM7X00A */ + + if ((write) && (!fifo)) + err_ret = sdio_memcpy_toio( + gInstance->func[func], + addr, buf, pkt_len); + else if (write) + err_ret = sdio_memcpy_toio( + gInstance->func[func], + addr, buf, pkt_len); + else if (fifo) + err_ret = sdio_readsb( + gInstance->func[func], + buf, addr, pkt_len); + else + err_ret = sdio_memcpy_fromio( + gInstance->func[func], + buf, addr, pkt_len); + + if (err_ret) + sd_err(("%s: %s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=%d\n", + __FUNCTION__, + (write) ? "TX" : "RX", + pnext, SGCount, addr, pkt_len, err_ret)); + else + sd_trace(("%s: %s xfr'd %p[%d], addr=0x%05x, len=%d\n", + __FUNCTION__, + (write) ? "TX" : "RX", + pnext, SGCount, addr, pkt_len)); + + if (!fifo) + addr += pkt_len; + SGCount ++; + } + sdio_release_host(gInstance->func[func]); + } + + sd_trace(("%s: Exit\n", __FUNCTION__)); + return ((err_ret == 0) ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); +} + + +/* + * This function takes a buffer or packet, and fixes everything up so that in the + * end, a DMA-able packet is created. + * + * A buffer does not have an associated packet pointer, and may or may not be aligned. + * A packet may consist of a single packet, or a packet chain. If it is a packet chain, + * then all the packets in the chain must be properly aligned. If the packet data is not + * aligned, then there may only be one packet, and in this case, it is copied to a new + * aligned packet. + * + */ +extern SDIOH_API_RC +sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint write, uint func, + uint addr, uint reg_width, uint buflen_u, uint8 *buffer, void *pkt) +{ + SDIOH_API_RC Status; + void *mypkt = NULL; + + sd_trace(("%s: Enter\n", __FUNCTION__)); + + DHD_PM_RESUME_WAIT(sdioh_request_buffer_wait); + DHD_PM_RESUME_RETURN_ERROR(SDIOH_API_RC_FAIL); + /* Case 1: we don't have a packet. */ + if (pkt == NULL) { + sd_data(("%s: Creating new %s Packet, len=%d\n", + __FUNCTION__, write ? "TX" : "RX", buflen_u)); +#ifdef CONFIG_DHD_USE_STATIC_BUF + if (!(mypkt = PKTGET_STATIC(sd->osh, buflen_u, write ? TRUE : FALSE))) { +#else + if (!(mypkt = PKTGET(sd->osh, buflen_u, write ? TRUE : FALSE))) { +#endif /* CONFIG_DHD_USE_STATIC_BUF */ + sd_err(("%s: PKTGET failed: len %d\n", + __FUNCTION__, buflen_u)); + return SDIOH_API_RC_FAIL; + } + + /* For a write, copy the buffer data into the packet. */ + if (write) { + bcopy(buffer, PKTDATA(sd->osh, mypkt), buflen_u); + } + + Status = sdioh_request_packet(sd, fix_inc, write, func, addr, mypkt); + + /* For a read, copy the packet data back to the buffer. */ + if (!write) { + bcopy(PKTDATA(sd->osh, mypkt), buffer, buflen_u); + } +#ifdef CONFIG_DHD_USE_STATIC_BUF + PKTFREE_STATIC(sd->osh, mypkt, write ? TRUE : FALSE); +#else + PKTFREE(sd->osh, mypkt, write ? TRUE : FALSE); +#endif /* CONFIG_DHD_USE_STATIC_BUF */ + } else if (((uint32)(PKTDATA(sd->osh, pkt)) & DMA_ALIGN_MASK) != 0) { + /* Case 2: We have a packet, but it is unaligned. */ + + /* In this case, we cannot have a chain. */ + ASSERT(PKTNEXT(sd->osh, pkt) == NULL); + + sd_data(("%s: Creating aligned %s Packet, len=%d\n", + __FUNCTION__, write ? "TX" : "RX", PKTLEN(sd->osh, pkt))); +#ifdef CONFIG_DHD_USE_STATIC_BUF + if (!(mypkt = PKTGET_STATIC(sd->osh, PKTLEN(sd->osh, pkt), write ? TRUE : FALSE))) { +#else + if (!(mypkt = PKTGET(sd->osh, PKTLEN(sd->osh, pkt), write ? TRUE : FALSE))) { +#endif /* CONFIG_DHD_USE_STATIC_BUF */ + sd_err(("%s: PKTGET failed: len %d\n", + __FUNCTION__, PKTLEN(sd->osh, pkt))); + return SDIOH_API_RC_FAIL; + } + + /* For a write, copy the buffer data into the packet. */ + if (write) { + bcopy(PKTDATA(sd->osh, pkt), + PKTDATA(sd->osh, mypkt), + PKTLEN(sd->osh, pkt)); + } + + Status = sdioh_request_packet(sd, fix_inc, write, func, addr, mypkt); + + /* For a read, copy the packet data back to the buffer. */ + if (!write) { + bcopy(PKTDATA(sd->osh, mypkt), + PKTDATA(sd->osh, pkt), + PKTLEN(sd->osh, mypkt)); + } +#ifdef CONFIG_DHD_USE_STATIC_BUF + PKTFREE_STATIC(sd->osh, mypkt, write ? TRUE : FALSE); +#else + PKTFREE(sd->osh, mypkt, write ? TRUE : FALSE); +#endif /* CONFIG_DHD_USE_STATIC_BUF */ + } else { /* case 3: We have a packet and it is aligned. */ + sd_data(("%s: Aligned %s Packet, direct DMA\n", + __FUNCTION__, write ? "Tx" : "Rx")); + Status = sdioh_request_packet(sd, fix_inc, write, func, addr, pkt); + } + + return (Status); +} + +/* this function performs "abort" for both of host & device */ +extern int +sdioh_abort(sdioh_info_t *sd, uint func) +{ +#if defined(MMC_SDIO_ABORT) + char t_func = (char) func; +#endif /* defined(MMC_SDIO_ABORT) */ + sd_trace(("%s: Enter\n", __FUNCTION__)); + +#if defined(MMC_SDIO_ABORT) + /* issue abort cmd52 command through F1 */ + sdioh_request_byte(sd, SD_IO_OP_WRITE, SDIO_FUNC_0, SDIOD_CCCR_IOABORT, &t_func); +#endif /* defined(MMC_SDIO_ABORT) */ + + sd_trace(("%s: Exit\n", __FUNCTION__)); + return SDIOH_API_RC_SUCCESS; +} + +/* Reset and re-initialize the device */ +int sdioh_sdio_reset(sdioh_info_t *si) +{ + sd_trace(("%s: Enter\n", __FUNCTION__)); + sd_trace(("%s: Exit\n", __FUNCTION__)); + return SDIOH_API_RC_SUCCESS; +} + +/* Disable device interrupt */ +void +sdioh_sdmmc_devintr_off(sdioh_info_t *sd) +{ + sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints)); + sd->intmask &= ~CLIENT_INTR; +} + +/* Enable device interrupt */ +void +sdioh_sdmmc_devintr_on(sdioh_info_t *sd) +{ + sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints)); + sd->intmask |= CLIENT_INTR; +} + +/* Read client card reg */ +int +sdioh_sdmmc_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data) +{ + + if ((func == 0) || (regsize == 1)) { + uint8 temp = 0; + + sdioh_request_byte(sd, SDIOH_READ, func, regaddr, &temp); + *data = temp; + *data &= 0xff; + sd_data(("%s: byte read data=0x%02x\n", + __FUNCTION__, *data)); + } else { + sdioh_request_word(sd, 0, SDIOH_READ, func, regaddr, data, regsize); + if (regsize == 2) + *data &= 0xffff; + + sd_data(("%s: word read data=0x%08x\n", + __FUNCTION__, *data)); + } + + return SUCCESS; +} + +#if !defined(OOB_INTR_ONLY) +/* bcmsdh_sdmmc interrupt handler */ +static void IRQHandler(struct sdio_func *func) +{ + sdioh_info_t *sd; + + sd_trace(("bcmsdh_sdmmc: ***IRQHandler\n")); + sd = gInstance->sd; + + ASSERT(sd != NULL); + sdio_release_host(gInstance->func[0]); + + if (sd->use_client_ints) { + sd->intrcount++; + ASSERT(sd->intr_handler); + ASSERT(sd->intr_handler_arg); + (sd->intr_handler)(sd->intr_handler_arg); + } else { + sd_err(("bcmsdh_sdmmc: ***IRQHandler\n")); + + sd_err(("%s: Not ready for intr: enabled %d, handler %p\n", + __FUNCTION__, sd->client_intr_enabled, sd->intr_handler)); + } + + sdio_claim_host(gInstance->func[0]); +} + +/* bcmsdh_sdmmc interrupt handler for F2 (dummy handler) */ +static void IRQHandlerF2(struct sdio_func *func) +{ + sdioh_info_t *sd; + + sd_trace(("bcmsdh_sdmmc: ***IRQHandlerF2\n")); + + sd = gInstance->sd; + + ASSERT(sd != NULL); + BCM_REFERENCE(sd); +} +#endif /* !defined(OOB_INTR_ONLY) */ + +#ifdef NOTUSED +/* Write client card reg */ +static int +sdioh_sdmmc_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 data) +{ + + if ((func == 0) || (regsize == 1)) { + uint8 temp; + + temp = data & 0xff; + sdioh_request_byte(sd, SDIOH_READ, func, regaddr, &temp); + sd_data(("%s: byte write data=0x%02x\n", + __FUNCTION__, data)); + } else { + if (regsize == 2) + data &= 0xffff; + + sdioh_request_word(sd, 0, SDIOH_READ, func, regaddr, &data, regsize); + + sd_data(("%s: word write data=0x%08x\n", + __FUNCTION__, data)); + } + + return SUCCESS; +} +#endif /* NOTUSED */ + +int +sdioh_start(sdioh_info_t *si, int stage) +{ + int ret; + sdioh_info_t *sd = gInstance->sd; + + /* Need to do this stages as we can't enable the interrupt till + downloading of the firmware is complete, other wise polling + sdio access will come in way + */ + if (gInstance->func[0]) { + if (stage == 0) { + /* Since the power to the chip is killed, we will have + re enumerate the device again. Set the block size + and enable the fucntion 1 for in preparation for + downloading the code + */ + /* sdio_reset_comm() - has been fixed in latest kernel/msm.git for Linux + 2.6.27. The implementation prior to that is buggy, and needs broadcom's + patch for it + */ + if ((ret = sdio_reset_comm(gInstance->func[0]->card))) { + sd_err(("%s Failed, error = %d\n", __FUNCTION__, ret)); + return ret; + } + else { + sd->num_funcs = 2; + sd->sd_blockmode = TRUE; + sd->use_client_ints = TRUE; + sd->client_block_size[0] = 64; + + /* Claim host controller */ + sdio_claim_host(gInstance->func[1]); + + sd->client_block_size[1] = 64; + if (sdio_set_block_size(gInstance->func[1], 64)) { + sd_err(("bcmsdh_sdmmc: Failed to set F1 blocksize\n")); + } + + /* Release host controller F1 */ + sdio_release_host(gInstance->func[1]); + + if (gInstance->func[2]) { + /* Claim host controller F2 */ + sdio_claim_host(gInstance->func[2]); + + sd->client_block_size[2] = sd_f2_blocksize; + if (sdio_set_block_size(gInstance->func[2], + sd_f2_blocksize)) { + sd_err(("bcmsdh_sdmmc: Failed to set F2 " + "blocksize to %d\n", sd_f2_blocksize)); + } + + /* Release host controller F2 */ + sdio_release_host(gInstance->func[2]); + } + + sdioh_sdmmc_card_enablefuncs(sd); + } + } else { +#if !defined(OOB_INTR_ONLY) + sdio_claim_host(gInstance->func[0]); + sdio_claim_irq(gInstance->func[2], IRQHandlerF2); + sdio_claim_irq(gInstance->func[1], IRQHandler); + sdio_release_host(gInstance->func[0]); +#else /* defined(OOB_INTR_ONLY) */ +#if defined(HW_OOB) + sdioh_enable_func_intr(); +#endif + bcmsdh_oob_intr_set(TRUE); +#endif /* !defined(OOB_INTR_ONLY) */ + } + } + else + sd_err(("%s Failed\n", __FUNCTION__)); + + return (0); +} + +int +sdioh_stop(sdioh_info_t *si) +{ + /* MSM7201A Android sdio stack has bug with interrupt + So internaly within SDIO stack they are polling + which cause issue when device is turned off. So + unregister interrupt with SDIO stack to stop the + polling + */ + if (gInstance->func[0]) { +#if !defined(OOB_INTR_ONLY) + sdio_claim_host(gInstance->func[0]); + sdio_release_irq(gInstance->func[1]); + sdio_release_irq(gInstance->func[2]); + sdio_release_host(gInstance->func[0]); +#else /* defined(OOB_INTR_ONLY) */ +#if defined(HW_OOB) + sdioh_disable_func_intr(); +#endif + bcmsdh_oob_intr_set(FALSE); +#endif /* !defined(OOB_INTR_ONLY) */ + } + else + sd_err(("%s Failed\n", __FUNCTION__)); + return (0); +} + +int +sdioh_waitlockfree(sdioh_info_t *sd) +{ + return (1); +} diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c new file mode 100644 index 0000000000000..a78faebe2e7ac --- /dev/null +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c @@ -0,0 +1,368 @@ +/* + * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdh_sdmmc_linux.c 312783 2012-02-03 22:53:56Z $ + */ + +#include +#include +#include /* SDIO Device and Protocol Specs */ +#include /* bcmsdh to/from specific controller APIs */ +#include /* to get msglevel bit values */ + +#include /* request_irq() */ + +#include +#include +#include +#include + +#if !defined(SDIO_VENDOR_ID_BROADCOM) +#define SDIO_VENDOR_ID_BROADCOM 0x02d0 +#endif /* !defined(SDIO_VENDOR_ID_BROADCOM) */ + +#define SDIO_DEVICE_ID_BROADCOM_DEFAULT 0x0000 + +#if !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) +#define SDIO_DEVICE_ID_BROADCOM_4325_SDGWB 0x0492 /* BCM94325SDGWB */ +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4325) +#define SDIO_DEVICE_ID_BROADCOM_4325 0x0493 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4329) +#define SDIO_DEVICE_ID_BROADCOM_4329 0x4329 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4319) +#define SDIO_DEVICE_ID_BROADCOM_4319 0x4319 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4319) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4330) +#define SDIO_DEVICE_ID_BROADCOM_4330 0x4330 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4330) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4334) +#define SDIO_DEVICE_ID_BROADCOM_4334 0x4334 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4334) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_4324) +#define SDIO_DEVICE_ID_BROADCOM_4324 0x4324 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_4324) */ +#if !defined(SDIO_DEVICE_ID_BROADCOM_43239) +#define SDIO_DEVICE_ID_BROADCOM_43239 43239 +#endif /* !defined(SDIO_DEVICE_ID_BROADCOM_43239) */ + +#include + +#include + +#ifdef WL_CFG80211 +extern void wl_cfg80211_set_parent_dev(void *dev); +#endif + +extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd); +extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd); +extern int dhd_os_check_wakelock(void *dhdp); +extern int dhd_os_check_if_up(void *dhdp); +extern void *bcmsdh_get_drvdata(void); + +int sdio_function_init(void); +void sdio_function_cleanup(void); + +#define DESCRIPTION "bcmsdh_sdmmc Driver" +#define AUTHOR "Broadcom Corporation" + +/* module param defaults */ +static int clockoverride = 0; + +module_param(clockoverride, int, 0644); +MODULE_PARM_DESC(clockoverride, "SDIO card clock override"); + +PBCMSDH_SDMMC_INSTANCE gInstance; + +/* Maximum number of bcmsdh_sdmmc devices supported by driver */ +#define BCMSDH_SDMMC_MAX_DEVICES 1 + +extern int bcmsdh_probe(struct device *dev); +extern int bcmsdh_remove(struct device *dev); +extern volatile bool dhd_mmc_suspend; + +static int bcmsdh_sdmmc_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + int ret = 0; + static struct sdio_func sdio_func_0; + sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__)); + sd_trace(("sdio_bcmsdh: func->class=%x\n", func->class)); + sd_trace(("sdio_vendor: 0x%04x\n", func->vendor)); + sd_trace(("sdio_device: 0x%04x\n", func->device)); + sd_trace(("Function#: 0x%04x\n", func->num)); + + if (func->num == 1) { + sdio_func_0.num = 0; + sdio_func_0.card = func->card; + gInstance->func[0] = &sdio_func_0; + if(func->device == 0x4) { /* 4318 */ + gInstance->func[2] = NULL; + sd_trace(("NIC found, calling bcmsdh_probe...\n")); + ret = bcmsdh_probe(&func->dev); + } + } + + gInstance->func[func->num] = func; + + if (func->num == 2) { +#ifdef WL_CFG80211 + wl_cfg80211_set_parent_dev(&func->dev); +#endif + sd_trace(("F2 found, calling bcmsdh_probe...\n")); + ret = bcmsdh_probe(&func->dev); + } + + return ret; +} + +static void bcmsdh_sdmmc_remove(struct sdio_func *func) +{ + sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__)); + sd_info(("sdio_bcmsdh: func->class=%x\n", func->class)); + sd_info(("sdio_vendor: 0x%04x\n", func->vendor)); + sd_info(("sdio_device: 0x%04x\n", func->device)); + sd_info(("Function#: 0x%04x\n", func->num)); + + if (func->num == 2) { + sd_trace(("F2 found, calling bcmsdh_remove...\n")); + bcmsdh_remove(&func->dev); + } else if (func->num == 1) { + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + gInstance->func[1] = NULL; + } +} + +/* devices we support, null terminated */ +static const struct sdio_device_id bcmsdh_sdmmc_ids[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4324) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43239) }, + { SDIO_DEVICE_CLASS(SDIO_CLASS_NONE) }, + { /* end: all zeroes */ }, +}; + +MODULE_DEVICE_TABLE(sdio, bcmsdh_sdmmc_ids); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) +static int bcmsdh_sdmmc_suspend(struct device *pdev) +{ + struct sdio_func *func = dev_to_sdio_func(pdev); + mmc_pm_flag_t sdio_flags; + int ret; + + if (func->num != 2) + return 0; + + sd_trace(("%s Enter\n", __FUNCTION__)); + + if (dhd_os_check_wakelock(bcmsdh_get_drvdata())) + return -EBUSY; + + sdio_flags = sdio_get_host_pm_caps(func); + + if (!(sdio_flags & MMC_PM_KEEP_POWER)) { + sd_err(("%s: can't keep power while host is suspended\n", __FUNCTION__)); + return -EINVAL; + } + + /* keep power while host suspended */ + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) { + sd_err(("%s: error while trying to keep power\n", __FUNCTION__)); + return ret; + } + +#if defined(OOB_INTR_ONLY) + bcmsdh_oob_intr_set(0); +#endif /* defined(OOB_INTR_ONLY) */ + dhd_mmc_suspend = TRUE; + smp_mb(); + + return 0; +} + +static int bcmsdh_sdmmc_resume(struct device *pdev) +{ +#if defined(OOB_INTR_ONLY) + struct sdio_func *func = dev_to_sdio_func(pdev); +#endif + sd_trace(("%s Enter\n", __FUNCTION__)); + dhd_mmc_suspend = FALSE; +#if defined(OOB_INTR_ONLY) + if ((func->num == 2) && dhd_os_check_if_up(bcmsdh_get_drvdata())) + bcmsdh_oob_intr_set(1); +#endif /* (OOB_INTR_ONLY) */ + + smp_mb(); + return 0; +} + +static const struct dev_pm_ops bcmsdh_sdmmc_pm_ops = { + .suspend = bcmsdh_sdmmc_suspend, + .resume = bcmsdh_sdmmc_resume, +}; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */ + +static struct sdio_driver bcmsdh_sdmmc_driver = { + .probe = bcmsdh_sdmmc_probe, + .remove = bcmsdh_sdmmc_remove, + .name = "bcmsdh_sdmmc", + .id_table = bcmsdh_sdmmc_ids, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) + .drv = { + .pm = &bcmsdh_sdmmc_pm_ops, + }, +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) && defined(CONFIG_PM) */ +}; + +struct sdos_info { + sdioh_info_t *sd; + spinlock_t lock; +}; + + +int +sdioh_sdmmc_osinit(sdioh_info_t *sd) +{ + struct sdos_info *sdos; + + sdos = (struct sdos_info*)MALLOC(sd->osh, sizeof(struct sdos_info)); + sd->sdos_info = (void*)sdos; + if (sdos == NULL) + return BCME_NOMEM; + + sdos->sd = sd; + spin_lock_init(&sdos->lock); + return BCME_OK; +} + +void +sdioh_sdmmc_osfree(sdioh_info_t *sd) +{ + struct sdos_info *sdos; + ASSERT(sd && sd->sdos_info); + + sdos = (struct sdos_info *)sd->sdos_info; + MFREE(sd->osh, sdos, sizeof(struct sdos_info)); +} + +/* Interrupt enable/disable */ +SDIOH_API_RC +sdioh_interrupt_set(sdioh_info_t *sd, bool enable) +{ + ulong flags; + struct sdos_info *sdos; + + sd_trace(("%s: %s\n", __FUNCTION__, enable ? "Enabling" : "Disabling")); + + sdos = (struct sdos_info *)sd->sdos_info; + ASSERT(sdos); + +#if !defined(OOB_INTR_ONLY) + if (enable && !(sd->intr_handler && sd->intr_handler_arg)) { + sd_err(("%s: no handler registered, will not enable\n", __FUNCTION__)); + return SDIOH_API_RC_FAIL; + } +#endif /* !defined(OOB_INTR_ONLY) */ + + /* Ensure atomicity for enable/disable calls */ + spin_lock_irqsave(&sdos->lock, flags); + + sd->client_intr_enabled = enable; + if (enable) { + sdioh_sdmmc_devintr_on(sd); + } else { + sdioh_sdmmc_devintr_off(sd); + } + + spin_unlock_irqrestore(&sdos->lock, flags); + + return SDIOH_API_RC_SUCCESS; +} + + +#ifdef BCMSDH_MODULE +static int __init +bcmsdh_module_init(void) +{ + int error = 0; + sdio_function_init(); + return error; +} + +static void __exit +bcmsdh_module_cleanup(void) +{ + sdio_function_cleanup(); +} + +module_init(bcmsdh_module_init); +module_exit(bcmsdh_module_cleanup); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION(DESCRIPTION); +MODULE_AUTHOR(AUTHOR); + +#endif /* BCMSDH_MODULE */ +/* + * module init +*/ +int sdio_function_init(void) +{ + int error = 0; + sd_trace(("bcmsdh_sdmmc: %s Enter\n", __FUNCTION__)); + + gInstance = kzalloc(sizeof(BCMSDH_SDMMC_INSTANCE), GFP_KERNEL); + if (!gInstance) + return -ENOMEM; + + error = sdio_register_driver(&bcmsdh_sdmmc_driver); + + return error; +} + +/* + * module cleanup +*/ +extern int bcmsdh_remove(struct device *dev); +void sdio_function_cleanup(void) +{ + sd_trace(("%s Enter\n", __FUNCTION__)); + + + sdio_unregister_driver(&bcmsdh_sdmmc_driver); + + if (gInstance) + kfree(gInstance); +} diff --git a/drivers/net/wireless/bcmdhd/bcmutils.c b/drivers/net/wireless/bcmdhd/bcmutils.c new file mode 100644 index 0000000000000..6b578e6536483 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/bcmutils.c @@ -0,0 +1,1965 @@ +/* + * Driver O/S-independent utility routines + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * $Id: bcmutils.c,v 1.277.2.18 2011-01-26 02:32:08 $ + */ + +#include +#include +#include + +#ifdef BCMDRIVER + +#include +#include +#include + +#else /* !BCMDRIVER */ + +#include +#include +#include + +#if defined(BCMEXTSUP) +#include +#endif + + +#endif /* !BCMDRIVER */ + +#include +#include +#include +#include +#include +#include +#include + +void *_bcmutils_dummy_fn = NULL; + +#ifdef BCMDRIVER + + + +/* copy a pkt buffer chain into a buffer */ +uint +pktcopy(osl_t *osh, void *p, uint offset, int len, uchar *buf) +{ + uint n, ret = 0; + + if (len < 0) + len = 4096; /* "infinite" */ + + /* skip 'offset' bytes */ + for (; p && offset; p = PKTNEXT(osh, p)) { + if (offset < (uint)PKTLEN(osh, p)) + break; + offset -= PKTLEN(osh, p); + } + + if (!p) + return 0; + + /* copy the data */ + for (; p && len; p = PKTNEXT(osh, p)) { + n = MIN((uint)PKTLEN(osh, p) - offset, (uint)len); + bcopy(PKTDATA(osh, p) + offset, buf, n); + buf += n; + len -= n; + ret += n; + offset = 0; + } + + return ret; +} + +/* copy a buffer into a pkt buffer chain */ +uint +pktfrombuf(osl_t *osh, void *p, uint offset, int len, uchar *buf) +{ + uint n, ret = 0; + + /* skip 'offset' bytes */ + for (; p && offset; p = PKTNEXT(osh, p)) { + if (offset < (uint)PKTLEN(osh, p)) + break; + offset -= PKTLEN(osh, p); + } + + if (!p) + return 0; + + /* copy the data */ + for (; p && len; p = PKTNEXT(osh, p)) { + n = MIN((uint)PKTLEN(osh, p) - offset, (uint)len); + bcopy(buf, PKTDATA(osh, p) + offset, n); + buf += n; + len -= n; + ret += n; + offset = 0; + } + + return ret; +} + + + +/* return total length of buffer chain */ +uint BCMFASTPATH +pkttotlen(osl_t *osh, void *p) +{ + uint total; + + total = 0; + for (; p; p = PKTNEXT(osh, p)) + total += PKTLEN(osh, p); + return (total); +} + +/* return the last buffer of chained pkt */ +void * +pktlast(osl_t *osh, void *p) +{ + for (; PKTNEXT(osh, p); p = PKTNEXT(osh, p)) + ; + + return (p); +} + +/* count segments of a chained packet */ +uint BCMFASTPATH +pktsegcnt(osl_t *osh, void *p) +{ + uint cnt; + + for (cnt = 0; p; p = PKTNEXT(osh, p)) + cnt++; + + return cnt; +} + + +/* + * osl multiple-precedence packet queue + * hi_prec is always >= the number of the highest non-empty precedence + */ +void * BCMFASTPATH +pktq_penq(struct pktq *pq, int prec, void *p) +{ + struct pktq_prec *q; + + ASSERT(prec >= 0 && prec < pq->num_prec); + ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */ + + ASSERT(!pktq_full(pq)); + ASSERT(!pktq_pfull(pq, prec)); + + q = &pq->q[prec]; + + if (q->head) + PKTSETLINK(q->tail, p); + else + q->head = p; + + q->tail = p; + q->len++; + + pq->len++; + + if (pq->hi_prec < prec) + pq->hi_prec = (uint8)prec; + + return p; +} + +void * BCMFASTPATH +pktq_penq_head(struct pktq *pq, int prec, void *p) +{ + struct pktq_prec *q; + + ASSERT(prec >= 0 && prec < pq->num_prec); + ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */ + + ASSERT(!pktq_full(pq)); + ASSERT(!pktq_pfull(pq, prec)); + + q = &pq->q[prec]; + + if (q->head == NULL) + q->tail = p; + + PKTSETLINK(p, q->head); + q->head = p; + q->len++; + + pq->len++; + + if (pq->hi_prec < prec) + pq->hi_prec = (uint8)prec; + + return p; +} + +void * BCMFASTPATH +pktq_pdeq(struct pktq *pq, int prec) +{ + struct pktq_prec *q; + void *p; + + ASSERT(prec >= 0 && prec < pq->num_prec); + + q = &pq->q[prec]; + + if ((p = q->head) == NULL) + return NULL; + + if ((q->head = PKTLINK(p)) == NULL) + q->tail = NULL; + + q->len--; + + pq->len--; + + PKTSETLINK(p, NULL); + + return p; +} + +void * BCMFASTPATH +pktq_pdeq_tail(struct pktq *pq, int prec) +{ + struct pktq_prec *q; + void *p, *prev; + + ASSERT(prec >= 0 && prec < pq->num_prec); + + q = &pq->q[prec]; + + if ((p = q->head) == NULL) + return NULL; + + for (prev = NULL; p != q->tail; p = PKTLINK(p)) + prev = p; + + if (prev) + PKTSETLINK(prev, NULL); + else + q->head = NULL; + + q->tail = prev; + q->len--; + + pq->len--; + + return p; +} + +void +pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir, ifpkt_cb_t fn, int arg) +{ + struct pktq_prec *q; + void *p, *prev = NULL; + + q = &pq->q[prec]; + p = q->head; + while (p) { + if (fn == NULL || (*fn)(p, arg)) { + bool head = (p == q->head); + if (head) + q->head = PKTLINK(p); + else + PKTSETLINK(prev, PKTLINK(p)); + PKTSETLINK(p, NULL); + PKTFREE(osh, p, dir); + q->len--; + pq->len--; + p = (head ? q->head : PKTLINK(prev)); + } else { + prev = p; + p = PKTLINK(p); + } + } + + if (q->head == NULL) { + ASSERT(q->len == 0); + q->tail = NULL; + } +} + +bool BCMFASTPATH +pktq_pdel(struct pktq *pq, void *pktbuf, int prec) +{ + struct pktq_prec *q; + void *p; + + ASSERT(prec >= 0 && prec < pq->num_prec); + + if (!pktbuf) + return FALSE; + + q = &pq->q[prec]; + + if (q->head == pktbuf) { + if ((q->head = PKTLINK(pktbuf)) == NULL) + q->tail = NULL; + } else { + for (p = q->head; p && PKTLINK(p) != pktbuf; p = PKTLINK(p)) + ; + if (p == NULL) + return FALSE; + + PKTSETLINK(p, PKTLINK(pktbuf)); + if (q->tail == pktbuf) + q->tail = p; + } + + q->len--; + pq->len--; + PKTSETLINK(pktbuf, NULL); + return TRUE; +} + +void +pktq_init(struct pktq *pq, int num_prec, int max_len) +{ + int prec; + + ASSERT(num_prec > 0 && num_prec <= PKTQ_MAX_PREC); + + /* pq is variable size; only zero out what's requested */ + bzero(pq, OFFSETOF(struct pktq, q) + (sizeof(struct pktq_prec) * num_prec)); + + pq->num_prec = (uint16)num_prec; + + pq->max = (uint16)max_len; + + for (prec = 0; prec < num_prec; prec++) + pq->q[prec].max = pq->max; +} + +void * BCMFASTPATH +pktq_deq(struct pktq *pq, int *prec_out) +{ + struct pktq_prec *q; + void *p; + int prec; + + if (pq->len == 0) + return NULL; + + while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL) + pq->hi_prec--; + + q = &pq->q[prec]; + + if ((p = q->head) == NULL) + return NULL; + + if ((q->head = PKTLINK(p)) == NULL) + q->tail = NULL; + + q->len--; + + pq->len--; + + if (prec_out) + *prec_out = prec; + + PKTSETLINK(p, NULL); + + return p; +} + +void * BCMFASTPATH +pktq_deq_tail(struct pktq *pq, int *prec_out) +{ + struct pktq_prec *q; + void *p, *prev; + int prec; + + if (pq->len == 0) + return NULL; + + for (prec = 0; prec < pq->hi_prec; prec++) + if (pq->q[prec].head) + break; + + q = &pq->q[prec]; + + if ((p = q->head) == NULL) + return NULL; + + for (prev = NULL; p != q->tail; p = PKTLINK(p)) + prev = p; + + if (prev) + PKTSETLINK(prev, NULL); + else + q->head = NULL; + + q->tail = prev; + q->len--; + + pq->len--; + + if (prec_out) + *prec_out = prec; + + PKTSETLINK(p, NULL); + + return p; +} + +void * +pktq_peek(struct pktq *pq, int *prec_out) +{ + int prec; + + if (pq->len == 0) + return NULL; + + while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL) + pq->hi_prec--; + + if (prec_out) + *prec_out = prec; + + return (pq->q[prec].head); +} + +void * +pktq_peek_tail(struct pktq *pq, int *prec_out) +{ + int prec; + + if (pq->len == 0) + return NULL; + + for (prec = 0; prec < pq->hi_prec; prec++) + if (pq->q[prec].head) + break; + + if (prec_out) + *prec_out = prec; + + return (pq->q[prec].tail); +} + +void +pktq_flush(osl_t *osh, struct pktq *pq, bool dir, ifpkt_cb_t fn, int arg) +{ + int prec; + for (prec = 0; prec < pq->num_prec; prec++) + pktq_pflush(osh, pq, prec, dir, fn, arg); + if (fn == NULL) + ASSERT(pq->len == 0); +} + +/* Return sum of lengths of a specific set of precedences */ +int +pktq_mlen(struct pktq *pq, uint prec_bmp) +{ + int prec, len; + + len = 0; + + for (prec = 0; prec <= pq->hi_prec; prec++) + if (prec_bmp & (1 << prec)) + len += pq->q[prec].len; + + return len; +} + +/* Priority dequeue from a specific set of precedences */ +void * BCMFASTPATH +pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out) +{ + struct pktq_prec *q; + void *p; + int prec; + + if (pq->len == 0) + return NULL; + + while ((prec = pq->hi_prec) > 0 && pq->q[prec].head == NULL) + pq->hi_prec--; + + while ((prec_bmp & (1 << prec)) == 0 || pq->q[prec].head == NULL) + if (prec-- == 0) + return NULL; + + q = &pq->q[prec]; + + if ((p = q->head) == NULL) + return NULL; + + if ((q->head = PKTLINK(p)) == NULL) + q->tail = NULL; + + q->len--; + + if (prec_out) + *prec_out = prec; + + pq->len--; + + PKTSETLINK(p, NULL); + + return p; +} + +#endif /* BCMDRIVER */ + +const unsigned char bcm_ctype[] = { + + _BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 0-7 */ + _BCM_C, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C|_BCM_S, _BCM_C, + _BCM_C, /* 8-15 */ + _BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 16-23 */ + _BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 24-31 */ + _BCM_S|_BCM_SP,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 32-39 */ + _BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 40-47 */ + _BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D, /* 48-55 */ + _BCM_D,_BCM_D,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 56-63 */ + _BCM_P, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, _BCM_U|_BCM_X, + _BCM_U|_BCM_X, _BCM_U, /* 64-71 */ + _BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U, /* 72-79 */ + _BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U, /* 80-87 */ + _BCM_U,_BCM_U,_BCM_U,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 88-95 */ + _BCM_P, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, _BCM_L|_BCM_X, + _BCM_L|_BCM_X, _BCM_L, /* 96-103 */ + _BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 104-111 */ + _BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 112-119 */ + _BCM_L,_BCM_L,_BCM_L,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_C, /* 120-127 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 128-143 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 144-159 */ + _BCM_S|_BCM_SP, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, + _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, /* 160-175 */ + _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, + _BCM_P, _BCM_P, _BCM_P, _BCM_P, _BCM_P, /* 176-191 */ + _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, + _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, /* 192-207 */ + _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_P, _BCM_U, _BCM_U, _BCM_U, + _BCM_U, _BCM_U, _BCM_U, _BCM_U, _BCM_L, /* 208-223 */ + _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, + _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, /* 224-239 */ + _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_P, _BCM_L, _BCM_L, _BCM_L, + _BCM_L, _BCM_L, _BCM_L, _BCM_L, _BCM_L /* 240-255 */ +}; + +ulong +bcm_strtoul(char *cp, char **endp, uint base) +{ + ulong result, last_result = 0, value; + bool minus; + + minus = FALSE; + + while (bcm_isspace(*cp)) + cp++; + + if (cp[0] == '+') + cp++; + else if (cp[0] == '-') { + minus = TRUE; + cp++; + } + + if (base == 0) { + if (cp[0] == '0') { + if ((cp[1] == 'x') || (cp[1] == 'X')) { + base = 16; + cp = &cp[2]; + } else { + base = 8; + cp = &cp[1]; + } + } else + base = 10; + } else if (base == 16 && (cp[0] == '0') && ((cp[1] == 'x') || (cp[1] == 'X'))) { + cp = &cp[2]; + } + + result = 0; + + while (bcm_isxdigit(*cp) && + (value = bcm_isdigit(*cp) ? *cp-'0' : bcm_toupper(*cp)-'A'+10) < base) + { + result = result*base + value; + /* Detected overflow */ + if (result < last_result && !minus) + return (ulong)-1; + last_result = result; + cp++; + } + + if (minus) + result = (ulong)(-(long)result); + + if (endp) + *endp = (char *)cp; + + return (result); +} + +int +bcm_atoi(char *s) +{ + return (int)bcm_strtoul(s, NULL, 10); +} + +/* return pointer to location of substring 'needle' in 'haystack' */ +char* +bcmstrstr(char *haystack, char *needle) +{ + int len, nlen; + int i; + + if ((haystack == NULL) || (needle == NULL)) + return (haystack); + + nlen = strlen(needle); + len = strlen(haystack) - nlen + 1; + + for (i = 0; i < len; i++) + if (memcmp(needle, &haystack[i], nlen) == 0) + return (&haystack[i]); + return (NULL); +} + +char* +bcmstrcat(char *dest, const char *src) +{ + char *p; + + p = dest + strlen(dest); + + while ((*p++ = *src++) != '\0') + ; + + return (dest); +} + +char* +bcmstrncat(char *dest, const char *src, uint size) +{ + char *endp; + char *p; + + p = dest + strlen(dest); + endp = p + size; + + while (p != endp && (*p++ = *src++) != '\0') + ; + + return (dest); +} + + +/**************************************************************************** +* Function: bcmstrtok +* +* Purpose: +* Tokenizes a string. This function is conceptually similiar to ANSI C strtok(), +* but allows strToken() to be used by different strings or callers at the same +* time. Each call modifies '*string' by substituting a NULL character for the +* first delimiter that is encountered, and updates 'string' to point to the char +* after the delimiter. Leading delimiters are skipped. +* +* Parameters: +* string (mod) Ptr to string ptr, updated by token. +* delimiters (in) Set of delimiter characters. +* tokdelim (out) Character that delimits the returned token. (May +* be set to NULL if token delimiter is not required). +* +* Returns: Pointer to the next token found. NULL when no more tokens are found. +***************************************************************************** +*/ +char * +bcmstrtok(char **string, const char *delimiters, char *tokdelim) +{ + unsigned char *str; + unsigned long map[8]; + int count; + char *nextoken; + + if (tokdelim != NULL) { + /* Prime the token delimiter */ + *tokdelim = '\0'; + } + + /* Clear control map */ + for (count = 0; count < 8; count++) { + map[count] = 0; + } + + /* Set bits in delimiter table */ + do { + map[*delimiters >> 5] |= (1 << (*delimiters & 31)); + } + while (*delimiters++); + + str = (unsigned char*)*string; + + /* Find beginning of token (skip over leading delimiters). Note that + * there is no token iff this loop sets str to point to the terminal + * null (*str == '\0') + */ + while (((map[*str >> 5] & (1 << (*str & 31))) && *str) || (*str == ' ')) { + str++; + } + + nextoken = (char*)str; + + /* Find the end of the token. If it is not the end of the string, + * put a null there. + */ + for (; *str; str++) { + if (map[*str >> 5] & (1 << (*str & 31))) { + if (tokdelim != NULL) { + *tokdelim = *str; + } + + *str++ = '\0'; + break; + } + } + + *string = (char*)str; + + /* Determine if a token has been found. */ + if (nextoken == (char *) str) { + return NULL; + } + else { + return nextoken; + } +} + + +#define xToLower(C) \ + ((C >= 'A' && C <= 'Z') ? (char)((int)C - (int)'A' + (int)'a') : C) + + +/**************************************************************************** +* Function: bcmstricmp +* +* Purpose: Compare to strings case insensitively. +* +* Parameters: s1 (in) First string to compare. +* s2 (in) Second string to compare. +* +* Returns: Return 0 if the two strings are equal, -1 if t1 < t2 and 1 if +* t1 > t2, when ignoring case sensitivity. +***************************************************************************** +*/ +int +bcmstricmp(const char *s1, const char *s2) +{ + char dc, sc; + + while (*s2 && *s1) { + dc = xToLower(*s1); + sc = xToLower(*s2); + if (dc < sc) return -1; + if (dc > sc) return 1; + s1++; + s2++; + } + + if (*s1 && !*s2) return 1; + if (!*s1 && *s2) return -1; + return 0; +} + + +/**************************************************************************** +* Function: bcmstrnicmp +* +* Purpose: Compare to strings case insensitively, upto a max of 'cnt' +* characters. +* +* Parameters: s1 (in) First string to compare. +* s2 (in) Second string to compare. +* cnt (in) Max characters to compare. +* +* Returns: Return 0 if the two strings are equal, -1 if t1 < t2 and 1 if +* t1 > t2, when ignoring case sensitivity. +***************************************************************************** +*/ +int +bcmstrnicmp(const char* s1, const char* s2, int cnt) +{ + char dc, sc; + + while (*s2 && *s1 && cnt) { + dc = xToLower(*s1); + sc = xToLower(*s2); + if (dc < sc) return -1; + if (dc > sc) return 1; + s1++; + s2++; + cnt--; + } + + if (!cnt) return 0; + if (*s1 && !*s2) return 1; + if (!*s1 && *s2) return -1; + return 0; +} + +/* parse a xx:xx:xx:xx:xx:xx format ethernet address */ +int +bcm_ether_atoe(char *p, struct ether_addr *ea) +{ + int i = 0; + + for (;;) { + ea->octet[i++] = (char) bcm_strtoul(p, &p, 16); + if (!*p++ || i == 6) + break; + } + + return (i == 6); +} + + +#if defined(CONFIG_USBRNDIS_RETAIL) || defined(NDIS_MINIPORT_DRIVER) +/* registry routine buffer preparation utility functions: + * parameter order is like strncpy, but returns count + * of bytes copied. Minimum bytes copied is null char(1)/wchar(2) + */ +ulong +wchar2ascii(char *abuf, ushort *wbuf, ushort wbuflen, ulong abuflen) +{ + ulong copyct = 1; + ushort i; + + if (abuflen == 0) + return 0; + + /* wbuflen is in bytes */ + wbuflen /= sizeof(ushort); + + for (i = 0; i < wbuflen; ++i) { + if (--abuflen == 0) + break; + *abuf++ = (char) *wbuf++; + ++copyct; + } + *abuf = '\0'; + + return copyct; +} +#endif /* CONFIG_USBRNDIS_RETAIL || NDIS_MINIPORT_DRIVER */ + +char * +bcm_ether_ntoa(const struct ether_addr *ea, char *buf) +{ + static const char template[] = "%02x:%02x:%02x:%02x:%02x:%02x"; + snprintf(buf, 18, template, + ea->octet[0]&0xff, ea->octet[1]&0xff, ea->octet[2]&0xff, + ea->octet[3]&0xff, ea->octet[4]&0xff, ea->octet[5]&0xff); + return (buf); +} + +char * +bcm_ip_ntoa(struct ipv4_addr *ia, char *buf) +{ + snprintf(buf, 16, "%d.%d.%d.%d", + ia->addr[0], ia->addr[1], ia->addr[2], ia->addr[3]); + return (buf); +} + +#ifdef BCMDRIVER + +void +bcm_mdelay(uint ms) +{ + uint i; + + for (i = 0; i < ms; i++) { + OSL_DELAY(1000); + } +} + + + + + +#if defined(DHD_DEBUG) +/* pretty hex print a pkt buffer chain */ +void +prpkt(const char *msg, osl_t *osh, void *p0) +{ + void *p; + + if (msg && (msg[0] != '\0')) + printf("%s:\n", msg); + + for (p = p0; p; p = PKTNEXT(osh, p)) + prhex(NULL, PKTDATA(osh, p), PKTLEN(osh, p)); +} +#endif + +/* Takes an Ethernet frame and sets out-of-bound PKTPRIO. + * Also updates the inplace vlan tag if requested. + * For debugging, it returns an indication of what it did. + */ +uint BCMFASTPATH +pktsetprio(void *pkt, bool update_vtag) +{ + struct ether_header *eh; + struct ethervlan_header *evh; + uint8 *pktdata; + int priority = 0; + int rc = 0; + + pktdata = (uint8 *) PKTDATA(NULL, pkt); + ASSERT(ISALIGNED((uintptr)pktdata, sizeof(uint16))); + + eh = (struct ether_header *) pktdata; + + if (ntoh16(eh->ether_type) == ETHER_TYPE_8021Q) { + uint16 vlan_tag; + int vlan_prio, dscp_prio = 0; + + evh = (struct ethervlan_header *)eh; + + vlan_tag = ntoh16(evh->vlan_tag); + vlan_prio = (int) (vlan_tag >> VLAN_PRI_SHIFT) & VLAN_PRI_MASK; + + if (ntoh16(evh->ether_type) == ETHER_TYPE_IP) { + uint8 *ip_body = pktdata + sizeof(struct ethervlan_header); + uint8 tos_tc = IP_TOS46(ip_body); + dscp_prio = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT); + } + + /* DSCP priority gets precedence over 802.1P (vlan tag) */ + if (dscp_prio != 0) { + priority = dscp_prio; + rc |= PKTPRIO_VDSCP; + } else { + priority = vlan_prio; + rc |= PKTPRIO_VLAN; + } + /* + * If the DSCP priority is not the same as the VLAN priority, + * then overwrite the priority field in the vlan tag, with the + * DSCP priority value. This is required for Linux APs because + * the VLAN driver on Linux, overwrites the skb->priority field + * with the priority value in the vlan tag + */ + if (update_vtag && (priority != vlan_prio)) { + vlan_tag &= ~(VLAN_PRI_MASK << VLAN_PRI_SHIFT); + vlan_tag |= (uint16)priority << VLAN_PRI_SHIFT; + evh->vlan_tag = hton16(vlan_tag); + rc |= PKTPRIO_UPD; + } + } else if (ntoh16(eh->ether_type) == ETHER_TYPE_IP) { + uint8 *ip_body = pktdata + sizeof(struct ether_header); + uint8 tos_tc = IP_TOS46(ip_body); + priority = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT); + rc |= PKTPRIO_DSCP; + } + + ASSERT(priority >= 0 && priority <= MAXPRIO); + PKTSETPRIO(pkt, priority); + return (rc | priority); +} + + +static char bcm_undeferrstr[32]; +static const char *bcmerrorstrtable[] = BCMERRSTRINGTABLE; + +/* Convert the error codes into related error strings */ +const char * +bcmerrorstr(int bcmerror) +{ + /* check if someone added a bcmerror code but forgot to add errorstring */ + ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(bcmerrorstrtable) - 1)); + + if (bcmerror > 0 || bcmerror < BCME_LAST) { + snprintf(bcm_undeferrstr, sizeof(bcm_undeferrstr), "Undefined error %d", bcmerror); + return bcm_undeferrstr; + } + + ASSERT(strlen(bcmerrorstrtable[-bcmerror]) < BCME_STRLEN); + + return bcmerrorstrtable[-bcmerror]; +} + + + + +/* iovar table lookup */ +const bcm_iovar_t* +bcm_iovar_lookup(const bcm_iovar_t *table, const char *name) +{ + const bcm_iovar_t *vi; + const char *lookup_name; + + /* skip any ':' delimited option prefixes */ + lookup_name = strrchr(name, ':'); + if (lookup_name != NULL) + lookup_name++; + else + lookup_name = name; + + ASSERT(table != NULL); + + for (vi = table; vi->name; vi++) { + if (!strcmp(vi->name, lookup_name)) + return vi; + } + /* ran to end of table */ + + return NULL; /* var name not found */ +} + +int +bcm_iovar_lencheck(const bcm_iovar_t *vi, void *arg, int len, bool set) +{ + int bcmerror = 0; + + /* length check on io buf */ + switch (vi->type) { + case IOVT_BOOL: + case IOVT_INT8: + case IOVT_INT16: + case IOVT_INT32: + case IOVT_UINT8: + case IOVT_UINT16: + case IOVT_UINT32: + /* all integers are int32 sized args at the ioctl interface */ + if (len < (int)sizeof(int)) { + bcmerror = BCME_BUFTOOSHORT; + } + break; + + case IOVT_BUFFER: + /* buffer must meet minimum length requirement */ + if (len < vi->minlen) { + bcmerror = BCME_BUFTOOSHORT; + } + break; + + case IOVT_VOID: + if (!set) { + /* Cannot return nil... */ + bcmerror = BCME_UNSUPPORTED; + } else if (len) { + /* Set is an action w/o parameters */ + bcmerror = BCME_BUFTOOLONG; + } + break; + + default: + /* unknown type for length check in iovar info */ + ASSERT(0); + bcmerror = BCME_UNSUPPORTED; + } + + return bcmerror; +} + +#endif /* BCMDRIVER */ + + +/******************************************************************************* + * crc8 + * + * Computes a crc8 over the input data using the polynomial: + * + * x^8 + x^7 +x^6 + x^4 + x^2 + 1 + * + * The caller provides the initial value (either CRC8_INIT_VALUE + * or the previous returned value) to allow for processing of + * discontiguous blocks of data. When generating the CRC the + * caller is responsible for complementing the final return value + * and inserting it into the byte stream. When checking, a final + * return value of CRC8_GOOD_VALUE indicates a valid CRC. + * + * Reference: Dallas Semiconductor Application Note 27 + * Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms", + * ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd., + * ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt + * + * **************************************************************************** + */ + +static const uint8 crc8_table[256] = { + 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B, + 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21, + 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF, + 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5, + 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14, + 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E, + 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80, + 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA, + 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95, + 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF, + 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01, + 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B, + 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA, + 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0, + 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E, + 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34, + 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0, + 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A, + 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54, + 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E, + 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF, + 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5, + 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B, + 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61, + 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E, + 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74, + 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA, + 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0, + 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41, + 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B, + 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5, + 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F +}; + +#define CRC_INNER_LOOP(n, c, x) \ + (c) = ((c) >> 8) ^ crc##n##_table[((c) ^ (x)) & 0xff] + +uint8 +hndcrc8( + uint8 *pdata, /* pointer to array of data to process */ + uint nbytes, /* number of input data bytes to process */ + uint8 crc /* either CRC8_INIT_VALUE or previous return value */ +) +{ + /* hard code the crc loop instead of using CRC_INNER_LOOP macro + * to avoid the undefined and unnecessary (uint8 >> 8) operation. + */ + while (nbytes-- > 0) + crc = crc8_table[(crc ^ *pdata++) & 0xff]; + + return crc; +} + +/******************************************************************************* + * crc16 + * + * Computes a crc16 over the input data using the polynomial: + * + * x^16 + x^12 +x^5 + 1 + * + * The caller provides the initial value (either CRC16_INIT_VALUE + * or the previous returned value) to allow for processing of + * discontiguous blocks of data. When generating the CRC the + * caller is responsible for complementing the final return value + * and inserting it into the byte stream. When checking, a final + * return value of CRC16_GOOD_VALUE indicates a valid CRC. + * + * Reference: Dallas Semiconductor Application Note 27 + * Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms", + * ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd., + * ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt + * + * **************************************************************************** + */ + +static const uint16 crc16_table[256] = { + 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, + 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7, + 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E, + 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876, + 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD, + 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5, + 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C, + 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974, + 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB, + 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3, + 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A, + 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72, + 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9, + 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1, + 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738, + 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70, + 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7, + 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF, + 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036, + 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E, + 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5, + 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD, + 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134, + 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C, + 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3, + 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB, + 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232, + 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A, + 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1, + 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9, + 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330, + 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78 +}; + +uint16 +hndcrc16( + uint8 *pdata, /* pointer to array of data to process */ + uint nbytes, /* number of input data bytes to process */ + uint16 crc /* either CRC16_INIT_VALUE or previous return value */ +) +{ + while (nbytes-- > 0) + CRC_INNER_LOOP(16, crc, *pdata++); + return crc; +} + +static const uint32 crc32_table[256] = { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + +/* + * crc input is CRC32_INIT_VALUE for a fresh start, or previous return value if + * accumulating over multiple pieces. + */ +uint32 +hndcrc32(uint8 *pdata, uint nbytes, uint32 crc) +{ + uint8 *pend; +#ifdef __mips__ + uint8 tmp[4]; + ulong *tptr = (ulong *)tmp; + + /* in case the beginning of the buffer isn't aligned */ + pend = (uint8 *)((uint)(pdata + 3) & 0xfffffffc); + nbytes -= (pend - pdata); + while (pdata < pend) + CRC_INNER_LOOP(32, crc, *pdata++); + + /* handle bulk of data as 32-bit words */ + pend = pdata + (nbytes & 0xfffffffc); + while (pdata < pend) { + *tptr = *(ulong *)pdata; + pdata += sizeof(ulong *); + CRC_INNER_LOOP(32, crc, tmp[0]); + CRC_INNER_LOOP(32, crc, tmp[1]); + CRC_INNER_LOOP(32, crc, tmp[2]); + CRC_INNER_LOOP(32, crc, tmp[3]); + } + + /* 1-3 bytes at end of buffer */ + pend = pdata + (nbytes & 0x03); + while (pdata < pend) + CRC_INNER_LOOP(32, crc, *pdata++); +#else + pend = pdata + nbytes; + while (pdata < pend) + CRC_INNER_LOOP(32, crc, *pdata++); +#endif /* __mips__ */ + + return crc; +} + +#ifdef notdef +#define CLEN 1499 /* CRC Length */ +#define CBUFSIZ (CLEN+4) +#define CNBUFS 5 /* # of bufs */ + +void +testcrc32(void) +{ + uint j, k, l; + uint8 *buf; + uint len[CNBUFS]; + uint32 crcr; + uint32 crc32tv[CNBUFS] = + {0xd2cb1faa, 0xd385c8fa, 0xf5b4f3f3, 0x55789e20, 0x00343110}; + + ASSERT((buf = MALLOC(CBUFSIZ*CNBUFS)) != NULL); + + /* step through all possible alignments */ + for (l = 0; l <= 4; l++) { + for (j = 0; j < CNBUFS; j++) { + len[j] = CLEN; + for (k = 0; k < len[j]; k++) + *(buf + j*CBUFSIZ + (k+l)) = (j+k) & 0xff; + } + + for (j = 0; j < CNBUFS; j++) { + crcr = crc32(buf + j*CBUFSIZ + l, len[j], CRC32_INIT_VALUE); + ASSERT(crcr == crc32tv[j]); + } + } + + MFREE(buf, CBUFSIZ*CNBUFS); + return; +} +#endif /* notdef */ + +/* + * Advance from the current 1-byte tag/1-byte length/variable-length value + * triple, to the next, returning a pointer to the next. + * If the current or next TLV is invalid (does not fit in given buffer length), + * NULL is returned. + * *buflen is not modified if the TLV elt parameter is invalid, or is decremented + * by the TLV parameter's length if it is valid. + */ +bcm_tlv_t * +bcm_next_tlv(bcm_tlv_t *elt, int *buflen) +{ + int len; + + /* validate current elt */ + if (!bcm_valid_tlv(elt, *buflen)) + return NULL; + + /* advance to next elt */ + len = elt->len; + elt = (bcm_tlv_t*)(elt->data + len); + *buflen -= (2 + len); + + /* validate next elt */ + if (!bcm_valid_tlv(elt, *buflen)) + return NULL; + + return elt; +} + +/* + * Traverse a string of 1-byte tag/1-byte length/variable-length value + * triples, returning a pointer to the substring whose first element + * matches tag + */ +bcm_tlv_t * +bcm_parse_tlvs(void *buf, int buflen, uint key) +{ + bcm_tlv_t *elt; + int totlen; + + elt = (bcm_tlv_t*)buf; + totlen = buflen; + + /* find tagged parameter */ + while (totlen >= 2) { + int len = elt->len; + + /* validate remaining totlen */ + if ((elt->id == key) && (totlen >= (len + 2))) + return (elt); + + elt = (bcm_tlv_t*)((uint8*)elt + (len + 2)); + totlen -= (len + 2); + } + + return NULL; +} + +/* + * Traverse a string of 1-byte tag/1-byte length/variable-length value + * triples, returning a pointer to the substring whose first element + * matches tag. Stop parsing when we see an element whose ID is greater + * than the target key. + */ +bcm_tlv_t * +bcm_parse_ordered_tlvs(void *buf, int buflen, uint key) +{ + bcm_tlv_t *elt; + int totlen; + + elt = (bcm_tlv_t*)buf; + totlen = buflen; + + /* find tagged parameter */ + while (totlen >= 2) { + uint id = elt->id; + int len = elt->len; + + /* Punt if we start seeing IDs > than target key */ + if (id > key) + return (NULL); + + /* validate remaining totlen */ + if ((id == key) && (totlen >= (len + 2))) + return (elt); + + elt = (bcm_tlv_t*)((uint8*)elt + (len + 2)); + totlen -= (len + 2); + } + return NULL; +} + +#if defined(WLMSG_PRHDRS) || defined(WLMSG_PRPKT) || defined(WLMSG_ASSOC) || \ + defined(DHD_DEBUG) +int +bcm_format_flags(const bcm_bit_desc_t *bd, uint32 flags, char* buf, int len) +{ + int i; + char* p = buf; + char hexstr[16]; + int slen = 0, nlen = 0; + uint32 bit; + const char* name; + + if (len < 2 || !buf) + return 0; + + buf[0] = '\0'; + + for (i = 0; flags != 0; i++) { + bit = bd[i].bit; + name = bd[i].name; + if (bit == 0 && flags != 0) { + /* print any unnamed bits */ + snprintf(hexstr, 16, "0x%X", flags); + name = hexstr; + flags = 0; /* exit loop */ + } else if ((flags & bit) == 0) + continue; + flags &= ~bit; + nlen = strlen(name); + slen += nlen; + /* count btwn flag space */ + if (flags != 0) + slen += 1; + /* need NULL char as well */ + if (len <= slen) + break; + /* copy NULL char but don't count it */ + strncpy(p, name, nlen + 1); + p += nlen; + /* copy btwn flag space and NULL char */ + if (flags != 0) + p += snprintf(p, 2, " "); + len -= slen; + } + + /* indicate the str was too short */ + if (flags != 0) { + if (len < 2) + p -= 2 - len; /* overwrite last char */ + p += snprintf(p, 2, ">"); + } + + return (int)(p - buf); +} +#endif + +#if defined(WLMSG_PRHDRS) || defined(WLMSG_PRPKT) || defined(WLMSG_ASSOC) || \ + defined(DHD_DEBUG) || defined(WLMEDIA_PEAKRATE) +/* print bytes formatted as hex to a string. return the resulting string length */ +int +bcm_format_hex(char *str, const void *bytes, int len) +{ + int i; + char *p = str; + const uint8 *src = (const uint8*)bytes; + + for (i = 0; i < len; i++) { + p += snprintf(p, 3, "%02X", *src); + src++; + } + return (int)(p - str); +} +#endif + +/* pretty hex print a contiguous buffer */ +void +prhex(const char *msg, uchar *buf, uint nbytes) +{ + char line[128], *p; + int len = sizeof(line); + int nchar; + uint i; + + if (msg && (msg[0] != '\0')) + printf("%s:\n", msg); + + p = line; + for (i = 0; i < nbytes; i++) { + if (i % 16 == 0) { + nchar = snprintf(p, len, " %04d: ", i); /* line prefix */ + p += nchar; + len -= nchar; + } + if (len > 0) { + nchar = snprintf(p, len, "%02x ", buf[i]); + p += nchar; + len -= nchar; + } + + if (i % 16 == 15) { + printf("%s\n", line); /* flush line */ + p = line; + len = sizeof(line); + } + } + + /* flush last partial line */ + if (p != line) + printf("%s\n", line); +} + +static const char *crypto_algo_names[] = { + "NONE", + "WEP1", + "TKIP", + "WEP128", + "AES_CCM", + "AES_OCB_MSDU", + "AES_OCB_MPDU", + "NALG" + "UNDEF", + "UNDEF", + "UNDEF", + "UNDEF" +}; + +const char * +bcm_crypto_algo_name(uint algo) +{ + return (algo < ARRAYSIZE(crypto_algo_names)) ? crypto_algo_names[algo] : "ERR"; +} + + +char * +bcm_chipname(uint chipid, char *buf, uint len) +{ + const char *fmt; + + fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; + snprintf(buf, len, fmt, chipid); + return buf; +} + +/* Produce a human-readable string for boardrev */ +char * +bcm_brev_str(uint32 brev, char *buf) +{ + if (brev < 0x100) + snprintf(buf, 8, "%d.%d", (brev & 0xf0) >> 4, brev & 0xf); + else + snprintf(buf, 8, "%c%03x", ((brev & 0xf000) == 0x1000) ? 'P' : 'A', brev & 0xfff); + + return (buf); +} + +#define BUFSIZE_TODUMP_ATONCE 512 /* Buffer size */ + +/* dump large strings to console */ +void +printbig(char *buf) +{ + uint len, max_len; + char c; + + len = strlen(buf); + + max_len = BUFSIZE_TODUMP_ATONCE; + + while (len > max_len) { + c = buf[max_len]; + buf[max_len] = '\0'; + printf("%s", buf); + buf[max_len] = c; + + buf += max_len; + len -= max_len; + } + /* print the remaining string */ + printf("%s\n", buf); + return; +} + +/* routine to dump fields in a fileddesc structure */ +uint +bcmdumpfields(bcmutl_rdreg_rtn read_rtn, void *arg0, uint arg1, struct fielddesc *fielddesc_array, + char *buf, uint32 bufsize) +{ + uint filled_len; + int len; + struct fielddesc *cur_ptr; + + filled_len = 0; + cur_ptr = fielddesc_array; + + while (bufsize > 1) { + if (cur_ptr->nameandfmt == NULL) + break; + len = snprintf(buf, bufsize, cur_ptr->nameandfmt, + read_rtn(arg0, arg1, cur_ptr->offset)); + /* check for snprintf overflow or error */ + if (len < 0 || (uint32)len >= bufsize) + len = bufsize - 1; + buf += len; + bufsize -= len; + filled_len += len; + cur_ptr++; + } + return filled_len; +} + +uint +bcm_mkiovar(char *name, char *data, uint datalen, char *buf, uint buflen) +{ + uint len; + + len = strlen(name) + 1; + + if ((len + datalen) > buflen) + return 0; + + strncpy(buf, name, buflen); + + /* append data onto the end of the name string */ + memcpy(&buf[len], data, datalen); + len += datalen; + + return len; +} + +/* Quarter dBm units to mW + * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153 + * Table is offset so the last entry is largest mW value that fits in + * a uint16. + */ + +#define QDBM_OFFSET 153 /* Offset for first entry */ +#define QDBM_TABLE_LEN 40 /* Table size */ + +/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET. + * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2 + */ +#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */ + +/* Largest mW value that will round down to the last table entry, + * QDBM_OFFSET + QDBM_TABLE_LEN-1. + * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) + mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2. + */ +#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */ + +static const uint16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = { +/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */ +/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000, +/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849, +/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119, +/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811, +/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096 +}; + +uint16 +bcm_qdbm_to_mw(uint8 qdbm) +{ + uint factor = 1; + int idx = qdbm - QDBM_OFFSET; + + if (idx >= QDBM_TABLE_LEN) { + /* clamp to max uint16 mW value */ + return 0xFFFF; + } + + /* scale the qdBm index up to the range of the table 0-40 + * where an offset of 40 qdBm equals a factor of 10 mW. + */ + while (idx < 0) { + idx += 40; + factor *= 10; + } + + /* return the mW value scaled down to the correct factor of 10, + * adding in factor/2 to get proper rounding. + */ + return ((nqdBm_to_mW_map[idx] + factor/2) / factor); +} + +uint8 +bcm_mw_to_qdbm(uint16 mw) +{ + uint8 qdbm; + int offset; + uint mw_uint = mw; + uint boundary; + + /* handle boundary case */ + if (mw_uint <= 1) + return 0; + + offset = QDBM_OFFSET; + + /* move mw into the range of the table */ + while (mw_uint < QDBM_TABLE_LOW_BOUND) { + mw_uint *= 10; + offset -= 40; + } + + for (qdbm = 0; qdbm < QDBM_TABLE_LEN-1; qdbm++) { + boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm+1] - + nqdBm_to_mW_map[qdbm])/2; + if (mw_uint < boundary) + break; + } + + qdbm += (uint8)offset; + + return (qdbm); +} + + +uint +bcm_bitcount(uint8 *bitmap, uint length) +{ + uint bitcount = 0, i; + uint8 tmp; + for (i = 0; i < length; i++) { + tmp = bitmap[i]; + while (tmp) { + bitcount++; + tmp &= (tmp - 1); + } + } + return bitcount; +} + +#ifdef BCMDRIVER + +/* Initialization of bcmstrbuf structure */ +void +bcm_binit(struct bcmstrbuf *b, char *buf, uint size) +{ + b->origsize = b->size = size; + b->origbuf = b->buf = buf; +} + +/* Buffer sprintf wrapper to guard against buffer overflow */ +int +bcm_bprintf(struct bcmstrbuf *b, const char *fmt, ...) +{ + va_list ap; + int r; + + va_start(ap, fmt); + r = vsnprintf(b->buf, b->size, fmt, ap); + + /* Non Ansi C99 compliant returns -1, + * Ansi compliant return r >= b->size, + * bcmstdlib returns 0, handle all + */ + if ((r == -1) || (r >= (int)b->size) || (r == 0)) { + b->size = 0; + } else { + b->size -= r; + b->buf += r; + } + + va_end(ap); + + return r; +} + +void +bcm_inc_bytes(uchar *num, int num_bytes, uint8 amount) +{ + int i; + + for (i = 0; i < num_bytes; i++) { + num[i] += amount; + if (num[i] >= amount) + break; + amount = 1; + } +} + +int +bcm_cmp_bytes(uchar *arg1, uchar *arg2, uint8 nbytes) +{ + int i; + + for (i = nbytes - 1; i >= 0; i--) { + if (arg1[i] != arg2[i]) + return (arg1[i] - arg2[i]); + } + return 0; +} + +void +bcm_print_bytes(char *name, const uchar *data, int len) +{ + int i; + int per_line = 0; + + printf("%s: %d \n", name ? name : "", len); + for (i = 0; i < len; i++) { + printf("%02x ", *data++); + per_line++; + if (per_line == 16) { + per_line = 0; + printf("\n"); + } + } + printf("\n"); +} +#if defined(WLTINYDUMP) || defined(WLMSG_INFORM) || defined(WLMSG_ASSOC) || \ + defined(WLMSG_PRPKT) || defined(WLMSG_WSEC) +#define SSID_FMT_BUF_LEN ((4 * DOT11_MAX_SSID_LEN) + 1) + +int +bcm_format_ssid(char* buf, const uchar ssid[], uint ssid_len) +{ + uint i, c; + char *p = buf; + char *endp = buf + SSID_FMT_BUF_LEN; + + if (ssid_len > DOT11_MAX_SSID_LEN) ssid_len = DOT11_MAX_SSID_LEN; + + for (i = 0; i < ssid_len; i++) { + c = (uint)ssid[i]; + if (c == '\\') { + *p++ = '\\'; + *p++ = '\\'; + } else if (bcm_isprint((uchar)c)) { + *p++ = (char)c; + } else { + p += snprintf(p, (endp - p), "\\x%02X", c); + } + } + *p = '\0'; + ASSERT(p < endp); + + return (int)(p - buf); +} +#endif + +#endif /* BCMDRIVER */ + +/* + * ProcessVars:Takes a buffer of "=\n" lines read from a file and ending in a NUL. + * also accepts nvram files which are already in the format of =\0\=\0 + * Removes carriage returns, empty lines, comment lines, and converts newlines to NULs. + * Shortens buffer as needed and pads with NULs. End of buffer is marked by two NULs. +*/ + +unsigned int +process_nvram_vars(char *varbuf, unsigned int len) +{ + char *dp; + bool findNewline; + int column; + unsigned int buf_len, n; + unsigned int pad = 0; + + dp = varbuf; + + findNewline = FALSE; + column = 0; + + for (n = 0; n < len; n++) { + if (varbuf[n] == '\r') + continue; + if (findNewline && varbuf[n] != '\n') + continue; + findNewline = FALSE; + if (varbuf[n] == '#') { + findNewline = TRUE; + continue; + } + if (varbuf[n] == '\n') { + if (column == 0) + continue; + *dp++ = 0; + column = 0; + continue; + } + *dp++ = varbuf[n]; + column++; + } + buf_len = (unsigned int)(dp - varbuf); + if (buf_len % 4) { + pad = 4 - buf_len % 4; + if (pad && (buf_len + pad <= len)) { + buf_len += pad; + } + } + + while (dp < varbuf + n) + *dp++ = 0; + + return buf_len; +} diff --git a/drivers/net/wireless/bcmdhd/bcmwifi.c b/drivers/net/wireless/bcmdhd/bcmwifi.c new file mode 100644 index 0000000000000..70722170bdfdc --- /dev/null +++ b/drivers/net/wireless/bcmdhd/bcmwifi.c @@ -0,0 +1,274 @@ +/* + * Misc utility routines used by kernel or app-level. + * Contents are wifi-specific, used by any kernel or app-level + * software that might want wifi things as it grows. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * $Id: bcmwifi.c,v 1.31.8.1 2010-08-03 17:47:05 Exp $ + */ + + +#include + +#ifdef BCMDRIVER +#include +#include +#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) +#define tolower(c) (bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c)) +#else +#include +#include +#include +#ifndef ASSERT +#define ASSERT(exp) +#endif +#endif +#include + +#if defined(WIN32) && (defined(BCMDLL) || defined(WLMDLL)) +#include +#endif + + + + + +char * +wf_chspec_ntoa(chanspec_t chspec, char *buf) +{ + const char *band, *bw, *sb; + uint channel; + + band = ""; + bw = ""; + sb = ""; + channel = CHSPEC_CHANNEL(chspec); + + if ((CHSPEC_IS2G(chspec) && channel > CH_MAX_2G_CHANNEL) || + (CHSPEC_IS5G(chspec) && channel <= CH_MAX_2G_CHANNEL)) + band = (CHSPEC_IS2G(chspec)) ? "b" : "a"; + if (CHSPEC_IS40(chspec)) { + if (CHSPEC_SB_UPPER(chspec)) { + sb = "u"; + channel += CH_10MHZ_APART; + } else { + sb = "l"; + channel -= CH_10MHZ_APART; + } + } else if (CHSPEC_IS10(chspec)) { + bw = "n"; + } + + + snprintf(buf, 6, "%d%s%s%s", channel, band, bw, sb); + return (buf); +} + + +chanspec_t +wf_chspec_aton(char *a) +{ + char *endp = NULL; + uint channel, band, bw, ctl_sb; + char c; + + channel = strtoul(a, &endp, 10); + + + if (endp == a) + return 0; + + if (channel > MAXCHANNEL) + return 0; + + band = ((channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G); + bw = WL_CHANSPEC_BW_20; + ctl_sb = WL_CHANSPEC_CTL_SB_NONE; + + a = endp; + + c = tolower(a[0]); + if (c == '\0') + goto done; + + + if (c == 'a' || c == 'b') { + band = (c == 'a') ? WL_CHANSPEC_BAND_5G : WL_CHANSPEC_BAND_2G; + a++; + c = tolower(a[0]); + if (c == '\0') + goto done; + } + + + if (c == 'n') { + bw = WL_CHANSPEC_BW_10; + } else if (c == 'l') { + bw = WL_CHANSPEC_BW_40; + ctl_sb = WL_CHANSPEC_CTL_SB_LOWER; + + if (channel <= (MAXCHANNEL - CH_20MHZ_APART)) + channel += CH_10MHZ_APART; + else + return 0; + } else if (c == 'u') { + bw = WL_CHANSPEC_BW_40; + ctl_sb = WL_CHANSPEC_CTL_SB_UPPER; + + if (channel > CH_20MHZ_APART) + channel -= CH_10MHZ_APART; + else + return 0; + } else { + return 0; + } + +done: + return (channel | band | bw | ctl_sb); +} + + +bool +wf_chspec_malformed(chanspec_t chanspec) +{ + + if (!CHSPEC_IS5G(chanspec) && !CHSPEC_IS2G(chanspec)) + return TRUE; + + if (!CHSPEC_IS40(chanspec) && !CHSPEC_IS20(chanspec)) + return TRUE; + + + if (CHSPEC_IS20_UNCOND(chanspec)) { + if (!CHSPEC_SB_NONE(chanspec)) + return TRUE; + } else { + if (!CHSPEC_SB_UPPER(chanspec) && !CHSPEC_SB_LOWER(chanspec)) + return TRUE; + } + + return FALSE; +} + + +uint8 +wf_chspec_ctlchan(chanspec_t chspec) +{ + uint8 ctl_chan; + + + if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) { + return CHSPEC_CHANNEL(chspec); + } else { + + ASSERT(CHSPEC_BW(chspec) == WL_CHANSPEC_BW_40); + + if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) { + + ctl_chan = UPPER_20_SB(CHSPEC_CHANNEL(chspec)); + } else { + ASSERT(CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_LOWER); + + ctl_chan = LOWER_20_SB(CHSPEC_CHANNEL(chspec)); + } + } + + return ctl_chan; +} + +chanspec_t +wf_chspec_ctlchspec(chanspec_t chspec) +{ + chanspec_t ctl_chspec = 0; + uint8 channel; + + ASSERT(!wf_chspec_malformed(chspec)); + + + if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_NONE) { + return chspec; + } else { + if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_UPPER) { + channel = UPPER_20_SB(CHSPEC_CHANNEL(chspec)); + } else { + channel = LOWER_20_SB(CHSPEC_CHANNEL(chspec)); + } + ctl_chspec = channel | WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE; + ctl_chspec |= CHSPEC_BAND(chspec); + } + return ctl_chspec; +} + + +int +wf_mhz2channel(uint freq, uint start_factor) +{ + int ch = -1; + uint base; + int offset; + + + if (start_factor == 0) { + if (freq >= 2400 && freq <= 2500) + start_factor = WF_CHAN_FACTOR_2_4_G; + else if (freq >= 5000 && freq <= 6000) + start_factor = WF_CHAN_FACTOR_5_G; + } + + if (freq == 2484 && start_factor == WF_CHAN_FACTOR_2_4_G) + return 14; + + base = start_factor / 2; + + + if ((freq < base) || (freq > base + 1000)) + return -1; + + offset = freq - base; + ch = offset / 5; + + + if (offset != (ch * 5)) + return -1; + + + if (start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 13)) + return -1; + + return ch; +} + + +int +wf_channel2mhz(uint ch, uint start_factor) +{ + int freq; + + if ((start_factor == WF_CHAN_FACTOR_2_4_G && (ch < 1 || ch > 14)) || + (ch > 200)) + freq = -1; + else if ((start_factor == WF_CHAN_FACTOR_2_4_G) && (ch == 14)) + freq = 2484; + else + freq = ch * 5 + start_factor / 2; + + return freq; +} diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h new file mode 100644 index 0000000000000..3b4dc7a220c07 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -0,0 +1,758 @@ +/* + * Header file describing the internal (inter-module) DHD interfaces. + * + * Provides type definitions and function prototypes used to link the + * DHD OS, bus, and protocol modules. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd.h 333052 2012-05-12 02:09:28Z $ + */ + +/**************** + * Common types * + */ + +#ifndef _dhd_h_ +#define _dhd_h_ + +#if defined(CHROMIUMOS_COMPAT_WIRELESS) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_HAS_WAKELOCK) +#include +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined (CONFIG_HAS_WAKELOCK) */ + +/* The kernel threading is sdio-specific */ +struct task_struct; +struct sched_param; +int setScheduler(struct task_struct *p, int policy, struct sched_param *param); + +#define ALL_INTERFACES 0xff + +#include + + +/* Forward decls */ +struct dhd_bus; +struct dhd_prot; +struct dhd_info; +struct dhd_cmn; + +/* The level of bus communication with the dongle */ +enum dhd_bus_state { + DHD_BUS_DOWN, /* Not ready for frame transfers */ + DHD_BUS_LOAD, /* Download access only (CPU reset) */ + DHD_BUS_DATA /* Ready for frame transfers */ +}; + +/* Firmware requested operation mode */ +#define STA_MASK 0x0001 +#define HOSTAPD_MASK 0x0002 +#define WFD_MASK 0x0004 +#define SOFTAP_FW_MASK 0x0008 +#define P2P_GO_ENABLED 0x0010 +#define P2P_GC_ENABLED 0x0020 +#define CONCURENT_MASK 0x00F0 + +#define MANUFACTRING_FW "WLTEST" + +/* max sequential rxcntl timeouts to set HANG event */ +#define MAX_CNTL_TIMEOUT 2 + +#define DHD_SCAN_ACTIVE_TIME 40 /* ms : Embedded default Active setting from DHD Driver */ +#define DHD_SCAN_PASSIVE_TIME 130 /* ms: Embedded default Passive setting from DHD Driver */ + +#define DHD_BEACON_TIMEOUT_NORMAL 4 +#define DHD_BEACON_TIMEOUT_HIGH 10 + +enum dhd_bus_wake_state { + WAKE_LOCK_OFF, + WAKE_LOCK_PRIV, + WAKE_LOCK_DPC, + WAKE_LOCK_IOCTL, + WAKE_LOCK_DOWNLOAD, + WAKE_LOCK_TMOUT, + WAKE_LOCK_WATCHDOG, + WAKE_LOCK_LINK_DOWN_TMOUT, + WAKE_LOCK_PNO_FIND_TMOUT, + WAKE_LOCK_SOFTAP_SET, + WAKE_LOCK_SOFTAP_STOP, + WAKE_LOCK_SOFTAP_START, + WAKE_LOCK_SOFTAP_THREAD, + WAKE_LOCK_MAX +}; + +enum dhd_prealloc_index { + DHD_PREALLOC_PROT = 0, + DHD_PREALLOC_RXBUF, + DHD_PREALLOC_DATABUF, + DHD_PREALLOC_OSL_BUF +}; + +typedef enum { + DHD_IF_NONE = 0, + DHD_IF_ADD, + DHD_IF_DEL, + DHD_IF_CHANGE, + DHD_IF_DELETING +} dhd_if_state_t; + +#if defined(CONFIG_DHD_USE_STATIC_BUF) + +uint8* dhd_os_prealloc(void *osh, int section, uint size); +void dhd_os_prefree(void *osh, void *addr, uint size); +#define DHD_OS_PREALLOC(osh, section, size) dhd_os_prealloc(osh, section, size) +#define DHD_OS_PREFREE(osh, addr, size) dhd_os_prefree(osh, addr, size) + +#else + +#define DHD_OS_PREALLOC(osh, section, size) MALLOC(osh, size) +#define DHD_OS_PREFREE(osh, addr, size) MFREE(osh, addr, size) + +#endif /* defined(CONFIG_DHD_USE_STATIC_BUF) */ + +/* Packet alignment for most efficient SDIO (can change based on platform) */ +#ifndef DHD_SDALIGN +#define DHD_SDALIGN 32 +#endif + +/* Common structure for module and instance linkage */ +typedef struct dhd_pub { + /* Linkage ponters */ + osl_t *osh; /* OSL handle */ + struct dhd_bus *bus; /* Bus module handle */ + struct dhd_prot *prot; /* Protocol module handle */ + struct dhd_info *info; /* Info module handle */ + struct dhd_cmn *cmn; /* dhd_common module handle */ + + /* Internal dhd items */ + bool up; /* Driver up/down (to OS) */ + bool txoff; /* Transmit flow-controlled */ + bool dongle_reset; /* TRUE = DEVRESET put dongle into reset */ + enum dhd_bus_state busstate; + uint hdrlen; /* Total DHD header length (proto + bus) */ + uint maxctl; /* Max size rxctl request from proto to bus */ + uint rxsz; /* Rx buffer size bus module should use */ + uint8 wme_dp; /* wme discard priority */ + + /* Dongle media info */ + bool iswl; /* Dongle-resident driver is wl */ + ulong drv_version; /* Version of dongle-resident driver */ + struct ether_addr mac; /* MAC address obtained from dongle */ + dngl_stats_t dstats; /* Stats for dongle-based data */ + + /* Additional stats for the bus level */ + ulong tx_packets; /* Data packets sent to dongle */ + ulong tx_multicast; /* Multicast data packets sent to dongle */ + ulong tx_errors; /* Errors in sending data to dongle */ + ulong tx_ctlpkts; /* Control packets sent to dongle */ + ulong tx_ctlerrs; /* Errors sending control frames to dongle */ + ulong rx_packets; /* Packets sent up the network interface */ + ulong rx_multicast; /* Multicast packets sent up the network interface */ + ulong rx_errors; /* Errors processing rx data packets */ + ulong rx_ctlpkts; /* Control frames processed from dongle */ + ulong rx_ctlerrs; /* Errors in processing rx control frames */ + ulong rx_dropped; /* Packets dropped locally (no memory) */ + ulong rx_flushed; /* Packets flushed due to unscheduled sendup thread */ + ulong wd_dpc_sched; /* Number of times dhd dpc scheduled by watchdog timer */ + + ulong rx_readahead_cnt; /* Number of packets where header read-ahead was used. */ + ulong tx_realloc; /* Number of tx packets we had to realloc for headroom */ + ulong fc_packets; /* Number of flow control pkts recvd */ + + /* Last error return */ + int bcmerror; + uint tickcnt; + + /* Last error from dongle */ + int dongle_error; + + /* Suspend disable flag and "in suspend" flag */ + int suspend_disable_flag; /* "1" to disable all extra powersaving during suspend */ + int in_suspend; /* flag set to 1 when early suspend called */ +#ifdef PNO_SUPPORT + int pno_enable; /* pno status : "1" is pno enable */ +#endif /* PNO_SUPPORT */ + int dtim_skip; /* dtim skip , default 0 means wake each dtim */ + + /* Pkt filter defination */ + char * pktfilter[100]; + int pktfilter_count; + + wl_country_t dhd_cspec; /* Current Locale info */ + char eventmask[WL_EVENTING_MASK_LEN]; + int op_mode; /* STA, HostAPD, WFD, SoftAP */ + +/* Set this to 1 to use a seperate interface (p2p0) for p2p operations. + * For ICS MR1 releases it should be disable to be compatable with ICS MR1 Framework + * see target dhd-cdc-sdmmc-panda-cfg80211-icsmr1-gpl-debug in Makefile + */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 + struct mutex wl_start_stop_lock; /* lock/unlock for Android start/stop */ + struct mutex wl_softap_lock; /* lock/unlock for any SoftAP/STA settings */ +#endif + + uint16 maxdatablks; +#ifdef PROP_TXSTATUS + int wlfc_enabled; + void* wlfc_state; +#endif + bool dongle_isolation; + int hang_was_sent; + int rxcnt_timeout; /* counter rxcnt timeout to send HANG */ + int txcnt_timeout; /* counter txcnt timeout to send HANG */ +#ifdef WLMEDIA_HTSF + uint8 htsfdlystat_sz; /* Size of delay stats, max 255B */ +#endif +} dhd_pub_t; + +typedef struct dhd_cmn { + osl_t *osh; /* OSL handle */ + dhd_pub_t *dhd; +} dhd_cmn_t; + + + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) + + #define DHD_PM_RESUME_WAIT_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a); + #define _DHD_PM_RESUME_WAIT(a, b) do {\ + int retry = 0; \ + SMP_RD_BARRIER_DEPENDS(); \ + while (dhd_mmc_suspend && retry++ != b) { \ + SMP_RD_BARRIER_DEPENDS(); \ + wait_event_interruptible_timeout(a, !dhd_mmc_suspend, HZ/100); \ + } \ + } while (0) + #define DHD_PM_RESUME_WAIT(a) _DHD_PM_RESUME_WAIT(a, 200) + #define DHD_PM_RESUME_WAIT_FOREVER(a) _DHD_PM_RESUME_WAIT(a, ~0) + #define DHD_PM_RESUME_RETURN_ERROR(a) do { if (dhd_mmc_suspend) return a; } while (0) + #define DHD_PM_RESUME_RETURN do { if (dhd_mmc_suspend) return; } while (0) + + #define DHD_SPINWAIT_SLEEP_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a); + #define SPINWAIT_SLEEP(a, exp, us) do { \ + uint countdown = (us) + 9999; \ + while ((exp) && (countdown >= 10000)) { \ + wait_event_interruptible_timeout(a, FALSE, HZ/100); \ + countdown -= 10000; \ + } \ + } while (0) + + #else + + #define DHD_PM_RESUME_WAIT_INIT(a) + #define DHD_PM_RESUME_WAIT(a) + #define DHD_PM_RESUME_WAIT_FOREVER(a) + #define DHD_PM_RESUME_RETURN_ERROR(a) + #define DHD_PM_RESUME_RETURN + + #define DHD_SPINWAIT_SLEEP_INIT(a) + #define SPINWAIT_SLEEP(a, exp, us) do { \ + uint countdown = (us) + 9; \ + while ((exp) && (countdown >= 10)) { \ + OSL_DELAY(10); \ + countdown -= 10; \ + } \ + } while (0) + + #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ +#ifndef DHDTHREAD +#undef SPINWAIT_SLEEP +#define SPINWAIT_SLEEP(a, exp, us) SPINWAIT(exp, us) +#endif /* DHDTHREAD */ +#define DHD_IF_VIF 0x01 /* Virtual IF (Hidden from user) */ + +unsigned long dhd_os_spin_lock(dhd_pub_t *pub); +void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags); + +/* Wakelock Functions */ +extern int dhd_os_wake_lock(dhd_pub_t *pub); +extern int dhd_os_wake_unlock(dhd_pub_t *pub); +extern int dhd_os_wake_lock_timeout(dhd_pub_t *pub); +extern int dhd_os_wake_lock_rx_timeout_enable(dhd_pub_t *pub, int val); +extern int dhd_os_wake_lock_ctrl_timeout_enable(dhd_pub_t *pub, int val); + +inline static void MUTEX_LOCK_SOFTAP_SET_INIT(dhd_pub_t * dhdp) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 + mutex_init(&dhdp->wl_softap_lock); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ +} + +inline static void MUTEX_LOCK_SOFTAP_SET(dhd_pub_t * dhdp) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 + mutex_lock(&dhdp->wl_softap_lock); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ +} + +inline static void MUTEX_UNLOCK_SOFTAP_SET(dhd_pub_t * dhdp) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 + mutex_unlock(&dhdp->wl_softap_lock); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ +} + +#define DHD_OS_WAKE_LOCK(pub) dhd_os_wake_lock(pub) +#define DHD_OS_WAKE_UNLOCK(pub) dhd_os_wake_unlock(pub) +#define DHD_OS_WAKE_LOCK_TIMEOUT(pub) dhd_os_wake_lock_timeout(pub) +#define DHD_OS_WAKE_LOCK_RX_TIMEOUT_ENABLE(pub, val) dhd_os_wake_lock_rx_timeout_enable(pub, val) +#define DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(pub, val) dhd_os_wake_lock_ctrl_timeout_enable(pub, val) +#define DHD_PACKET_TIMEOUT_MS 1000 +#define DHD_EVENT_TIMEOUT_MS 1500 + +/* interface operations (register, remove) should be atomic, use this lock to prevent race + * condition among wifi on/off and interface operation functions + */ +void dhd_net_if_lock(struct net_device *dev); +void dhd_net_if_unlock(struct net_device *dev); + +typedef struct dhd_if_event { + uint8 ifidx; + uint8 action; + uint8 flags; + uint8 bssidx; + uint8 is_AP; +} dhd_if_event_t; + +typedef enum dhd_attach_states +{ + DHD_ATTACH_STATE_INIT = 0x0, + DHD_ATTACH_STATE_NET_ALLOC = 0x1, + DHD_ATTACH_STATE_DHD_ALLOC = 0x2, + DHD_ATTACH_STATE_ADD_IF = 0x4, + DHD_ATTACH_STATE_PROT_ATTACH = 0x8, + DHD_ATTACH_STATE_WL_ATTACH = 0x10, + DHD_ATTACH_STATE_THREADS_CREATED = 0x20, + DHD_ATTACH_STATE_WAKELOCKS_INIT = 0x40, + DHD_ATTACH_STATE_CFG80211 = 0x80, + DHD_ATTACH_STATE_EARLYSUSPEND_DONE = 0x100, + DHD_ATTACH_STATE_DONE = 0x200 +} dhd_attach_states_t; + +/* Value -1 means we are unsuccessful in creating the kthread. */ +#define DHD_PID_KT_INVALID -1 +/* Value -2 means we are unsuccessful in both creating the kthread and tasklet */ +#define DHD_PID_KT_TL_INVALID -2 + +/* + * Exported from dhd OS modules (dhd_linux/dhd_ndis) + */ + +/* To allow osl_attach/detach calls from os-independent modules */ +osl_t *dhd_osl_attach(void *pdev, uint bustype); +void dhd_osl_detach(osl_t *osh); + +/* Indication from bus module regarding presence/insertion of dongle. + * Return dhd_pub_t pointer, used as handle to OS module in later calls. + * Returned structure should have bus and prot pointers filled in. + * bus_hdrlen specifies required headroom for bus module header. + */ +extern dhd_pub_t *dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen); +#if defined(WLP2P) && defined(WL_CFG80211) +/* To allow attach/detach calls corresponding to p2p0 interface */ +extern int dhd_attach_p2p(dhd_pub_t *); +extern int dhd_detach_p2p(dhd_pub_t *); +#endif /* WLP2P && WL_CFG80211 */ +extern int dhd_net_attach(dhd_pub_t *dhdp, int idx); + +/* Indication from bus module regarding removal/absence of dongle */ +extern void dhd_detach(dhd_pub_t *dhdp); +extern void dhd_free(dhd_pub_t *dhdp); + +/* Indication from bus module to change flow-control state */ +extern void dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool on); + +extern bool dhd_prec_enq(dhd_pub_t *dhdp, struct pktq *q, void *pkt, int prec); + +/* Receive frame for delivery to OS. Callee disposes of rxp. */ +extern void dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *rxp, int numpkt, uint8 chan); + +/* Return pointer to interface name */ +extern char *dhd_ifname(dhd_pub_t *dhdp, int idx); + +/* Request scheduling of the bus dpc */ +extern void dhd_sched_dpc(dhd_pub_t *dhdp); + +/* Notify tx completion */ +extern void dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success); + +/* OS independent layer functions */ +extern int dhd_os_proto_block(dhd_pub_t * pub); +extern int dhd_os_proto_unblock(dhd_pub_t * pub); +extern int dhd_os_ioctl_resp_wait(dhd_pub_t * pub, uint * condition, bool * pending); +extern int dhd_os_ioctl_resp_wake(dhd_pub_t * pub); +extern unsigned int dhd_os_get_ioctl_resp_timeout(void); +extern void dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec); +extern void * dhd_os_open_image(char * filename); +extern int dhd_os_get_image_block(char * buf, int len, void * image); +extern void dhd_os_close_image(void * image); +extern void dhd_os_wd_timer(void *bus, uint wdtick); +extern void dhd_os_sdlock(dhd_pub_t * pub); +extern void dhd_os_sdunlock(dhd_pub_t * pub); +extern void dhd_os_sdlock_txq(dhd_pub_t * pub); +extern void dhd_os_sdunlock_txq(dhd_pub_t * pub); +extern void dhd_os_sdlock_rxq(dhd_pub_t * pub); +extern void dhd_os_sdunlock_rxq(dhd_pub_t * pub); +extern void dhd_os_sdlock_sndup_rxq(dhd_pub_t * pub); +extern void dhd_customer_gpio_wlan_ctrl(int onoff); +extern int dhd_custom_get_mac_address(unsigned char *buf); +extern void dhd_os_sdunlock_sndup_rxq(dhd_pub_t * pub); +extern void dhd_os_sdlock_eventq(dhd_pub_t * pub); +extern void dhd_os_sdunlock_eventq(dhd_pub_t * pub); +extern bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret); + +#ifdef PNO_SUPPORT +extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); +extern int dhd_pno_clean(dhd_pub_t *dhd); +extern int dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, + ushort scan_fr, int pno_repeat, int pno_freq_expo_max); +extern int dhd_pno_set_ex(dhd_pub_t *dhd, wl_pfn_t* ssidnet, int nssid, + ushort pno_interval, int pno_repeat, int pno_expo_max, int pno_lost_time); +extern int dhd_pno_get_status(dhd_pub_t *dhd); +extern int dhd_dev_pno_reset(struct net_device *dev); +extern int dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, + int nssid, ushort scan_fr, int pno_repeat, int pno_freq_expo_max); +extern int dhd_dev_pno_set_ex(struct net_device *dev, wl_pfn_t* ssidnet, int nssid, + ushort pno_interval, int pno_repeat, int pno_expo_max, int pno_lost_time); +extern int dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled); +extern int dhd_dev_get_pno_status(struct net_device *dev); +#endif /* PNO_SUPPORT */ + +#define DHD_UNICAST_FILTER_NUM 0 +#define DHD_BROADCAST_FILTER_NUM 1 +#define DHD_MULTICAST4_FILTER_NUM 2 +#define DHD_MULTICAST6_FILTER_NUM 3 +#define DHD_MDNS_FILTER_NUM 4 +extern int dhd_os_set_packet_filter(dhd_pub_t *dhdp, int val); +extern int net_os_set_packet_filter(struct net_device *dev, int val); +extern int net_os_rxfilter_add_remove(struct net_device *dev, int val, int num); + +extern int dhd_get_dtim_skip(dhd_pub_t *dhd); +extern bool dhd_check_ap_wfd_mode_set(dhd_pub_t *dhd); + +#ifdef DHD_DEBUG +extern int write_to_file(dhd_pub_t *dhd, uint8 *buf, int size); +#endif /* DHD_DEBUG */ +#if defined(OOB_INTR_ONLY) +extern int dhd_customer_oob_irq_map(unsigned long *irq_flags_ptr); +#endif /* defined(OOB_INTR_ONLY) */ +extern void dhd_os_sdtxlock(dhd_pub_t * pub); +extern void dhd_os_sdtxunlock(dhd_pub_t * pub); + +typedef struct { + uint32 limit; /* Expiration time (usec) */ + uint32 increment; /* Current expiration increment (usec) */ + uint32 elapsed; /* Current elapsed time (usec) */ + uint32 tick; /* O/S tick time (usec) */ +} dhd_timeout_t; + +extern void dhd_timeout_start(dhd_timeout_t *tmo, uint usec); +extern int dhd_timeout_expired(dhd_timeout_t *tmo); + +extern int dhd_ifname2idx(struct dhd_info *dhd, char *name); +extern int dhd_net2idx(struct dhd_info *dhd, struct net_device *net); +extern struct net_device * dhd_idx2net(void *pub, int ifidx); +extern int wl_host_event(dhd_pub_t *dhd_pub, int *idx, void *pktdata, + wl_event_msg_t *, void **data_ptr); +extern void wl_event_to_host_order(wl_event_msg_t * evt); + +extern int dhd_wl_ioctl(dhd_pub_t *dhd_pub, int ifindex, wl_ioctl_t *ioc, void *buf, int len); +extern int dhd_wl_ioctl_cmd(dhd_pub_t *dhd_pub, int cmd, void *arg, int len, uint8 set, + int ifindex); + +extern struct dhd_cmn *dhd_common_init(osl_t *osh); +extern void dhd_common_deinit(dhd_pub_t *dhd_pub, dhd_cmn_t *sa_cmn); + +extern int dhd_do_driver_init(struct net_device *net); +extern int dhd_add_if(struct dhd_info *dhd, int ifidx, void *handle, + char *name, uint8 *mac_addr, uint32 flags, uint8 bssidx); +extern void dhd_del_if(struct dhd_info *dhd, int ifidx); + +extern void dhd_vif_add(struct dhd_info *dhd, int ifidx, char * name); +extern void dhd_vif_del(struct dhd_info *dhd, int ifidx); + +extern void dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx); +extern void dhd_vif_sendup(struct dhd_info *dhd, int ifidx, uchar *cp, int len); + + +/* Send packet to dongle via data channel */ +extern int dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pkt); + +/* send up locally generated event */ +extern void dhd_sendup_event_common(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data); +/* Send event to host */ +extern void dhd_sendup_event(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data); +extern int dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag); +extern uint dhd_bus_status(dhd_pub_t *dhdp); +extern int dhd_bus_start(dhd_pub_t *dhdp); +extern int dhd_bus_membytes(dhd_pub_t *dhdp, bool set, uint32 address, uint8 *data, uint size); +extern void dhd_print_buf(void *pbuf, int len, int bytes_per_line); +extern bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf, int *retval); +extern uint dhd_bus_chip_id(dhd_pub_t *dhdp); + +#if defined(KEEP_ALIVE) +extern int dhd_keep_alive_onoff(dhd_pub_t *dhd); +#endif /* KEEP_ALIVE */ + +#ifdef ARP_OFFLOAD_SUPPORT +extern void dhd_arp_offload_set(dhd_pub_t * dhd, int arp_mode); +extern void dhd_arp_offload_enable(dhd_pub_t * dhd, int arp_enable); +#endif /* ARP_OFFLOAD_SUPPORT */ + + +typedef enum cust_gpio_modes { + WLAN_RESET_ON, + WLAN_RESET_OFF, + WLAN_POWER_ON, + WLAN_POWER_OFF +} cust_gpio_modes_t; + +extern int wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag); +extern int wl_iw_send_priv_event(struct net_device *dev, char *flag); +/* + * Insmod parameters for debug/test + */ + +/* Watchdog timer interval */ +extern uint dhd_watchdog_ms; + +#if defined(DHD_DEBUG) +/* Console output poll interval */ +extern uint dhd_console_ms; +extern uint wl_msg_level; +#endif /* defined(DHD_DEBUG) */ + +/* Use interrupts */ +extern uint dhd_intr; + +/* Use polling */ +extern uint dhd_poll; + +/* ARP offload agent mode */ +extern uint dhd_arp_mode; + +/* ARP offload enable */ +extern uint dhd_arp_enable; + +/* Pkt filte enable control */ +extern uint dhd_pkt_filter_enable; + +/* Pkt filter init setup */ +extern uint dhd_pkt_filter_init; + +/* Pkt filter mode control */ +extern uint dhd_master_mode; + +/* Roaming mode control */ +extern uint dhd_roam_disable; + +/* Roaming mode control */ +extern uint dhd_radio_up; + +/* Initial idletime ticks (may be -1 for immediate idle, 0 for no idle) */ +extern int dhd_idletime; +#define DHD_IDLETIME_TICKS 1 + +/* SDIO Drive Strength */ +extern uint dhd_sdiod_drive_strength; + +/* Override to force tx queueing all the time */ +extern uint dhd_force_tx_queueing; +/* Default KEEP_ALIVE Period is 55 sec to prevent AP from sending Keep Alive probe frame */ +#define KEEP_ALIVE_PERIOD 55000 +#define NULL_PKT_STR "null_pkt" + +#ifdef SDTEST +/* Echo packet generator (SDIO), pkts/s */ +extern uint dhd_pktgen; + +/* Echo packet len (0 => sawtooth, max 1800) */ +extern uint dhd_pktgen_len; +#define MAX_PKTGEN_LEN 1800 +#endif + + +/* optionally set by a module_param_string() */ +#define MOD_PARAM_PATHLEN 2048 +extern char fw_path[MOD_PARAM_PATHLEN]; +extern char nv_path[MOD_PARAM_PATHLEN]; + +#ifdef SOFTAP +extern char fw_path2[MOD_PARAM_PATHLEN]; +#endif + +/* Flag to indicate if we should download firmware on driver load */ +extern uint dhd_download_fw_on_driverload; + +/* For supporting multiple interfaces */ +#define DHD_MAX_IFS 16 +#define DHD_DEL_IF -0xe +#define DHD_BAD_IF -0xf + +#ifdef PROP_TXSTATUS +/* Please be mindful that total pkttag space is 32 octets only */ +typedef struct dhd_pkttag { + /* + b[11 ] - 1 = this packet was sent in response to one time packet request, + do not increment credit on status for this one. [WLFC_CTL_TYPE_MAC_REQUEST_PACKET]. + b[10 ] - 1 = signal-only-packet to firmware [i.e. nothing to piggyback on] + b[9 ] - 1 = packet is host->firmware (transmit direction) + - 0 = packet received from firmware (firmware->host) + b[8 ] - 1 = packet was sent due to credit_request (pspoll), + packet does not count against FIFO credit. + - 0 = normal transaction, packet counts against FIFO credit + b[7 ] - 1 = AP, 0 = STA + b[6:4] - AC FIFO number + b[3:0] - interface index + */ + uint16 if_flags; + /* destination MAC address for this packet so that not every + module needs to open the packet to find this + */ + uint8 dstn_ether[ETHER_ADDR_LEN]; + /* + This 32-bit goes from host to device for every packet. + */ + uint32 htod_tag; + /* bus specific stuff */ + union { + struct { + void* stuff; + uint32 thing1; + uint32 thing2; + } sd; + struct { + void* bus; + void* urb; + } usb; + } bus_specific; +} dhd_pkttag_t; + +#define DHD_PKTTAG_SET_H2DTAG(tag, h2dvalue) ((dhd_pkttag_t*)(tag))->htod_tag = (h2dvalue) +#define DHD_PKTTAG_H2DTAG(tag) (((dhd_pkttag_t*)(tag))->htod_tag) + +#define DHD_PKTTAG_IFMASK 0xf +#define DHD_PKTTAG_IFTYPE_MASK 0x1 +#define DHD_PKTTAG_IFTYPE_SHIFT 7 +#define DHD_PKTTAG_FIFO_MASK 0x7 +#define DHD_PKTTAG_FIFO_SHIFT 4 + +#define DHD_PKTTAG_SIGNALONLY_MASK 0x1 +#define DHD_PKTTAG_SIGNALONLY_SHIFT 10 + +#define DHD_PKTTAG_ONETIMEPKTRQST_MASK 0x1 +#define DHD_PKTTAG_ONETIMEPKTRQST_SHIFT 11 + +#define DHD_PKTTAG_PKTDIR_MASK 0x1 +#define DHD_PKTTAG_PKTDIR_SHIFT 9 + +#define DHD_PKTTAG_CREDITCHECK_MASK 0x1 +#define DHD_PKTTAG_CREDITCHECK_SHIFT 8 + +#define DHD_PKTTAG_INVALID_FIFOID 0x7 + +#define DHD_PKTTAG_SETFIFO(tag, fifo) ((dhd_pkttag_t*)(tag))->if_flags = \ + (((dhd_pkttag_t*)(tag))->if_flags & ~(DHD_PKTTAG_FIFO_MASK << DHD_PKTTAG_FIFO_SHIFT)) | \ + (((fifo) & DHD_PKTTAG_FIFO_MASK) << DHD_PKTTAG_FIFO_SHIFT) +#define DHD_PKTTAG_FIFO(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ + DHD_PKTTAG_FIFO_SHIFT) & DHD_PKTTAG_FIFO_MASK) + +#define DHD_PKTTAG_SETIF(tag, if) ((dhd_pkttag_t*)(tag))->if_flags = \ + (((dhd_pkttag_t*)(tag))->if_flags & ~DHD_PKTTAG_IFMASK) | ((if) & DHD_PKTTAG_IFMASK) +#define DHD_PKTTAG_IF(tag) (((dhd_pkttag_t*)(tag))->if_flags & DHD_PKTTAG_IFMASK) + +#define DHD_PKTTAG_SETIFTYPE(tag, isAP) ((dhd_pkttag_t*)(tag))->if_flags = \ + (((dhd_pkttag_t*)(tag))->if_flags & \ + ~(DHD_PKTTAG_IFTYPE_MASK << DHD_PKTTAG_IFTYPE_SHIFT)) | \ + (((isAP) & DHD_PKTTAG_IFTYPE_MASK) << DHD_PKTTAG_IFTYPE_SHIFT) +#define DHD_PKTTAG_IFTYPE(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ + DHD_PKTTAG_IFTYPE_SHIFT) & DHD_PKTTAG_IFTYPE_MASK) + +#define DHD_PKTTAG_SETCREDITCHECK(tag, check) ((dhd_pkttag_t*)(tag))->if_flags = \ + (((dhd_pkttag_t*)(tag))->if_flags & \ + ~(DHD_PKTTAG_CREDITCHECK_MASK << DHD_PKTTAG_CREDITCHECK_SHIFT)) | \ + (((check) & DHD_PKTTAG_CREDITCHECK_MASK) << DHD_PKTTAG_CREDITCHECK_SHIFT) +#define DHD_PKTTAG_CREDITCHECK(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ + DHD_PKTTAG_CREDITCHECK_SHIFT) & DHD_PKTTAG_CREDITCHECK_MASK) + +#define DHD_PKTTAG_SETPKTDIR(tag, dir) ((dhd_pkttag_t*)(tag))->if_flags = \ + (((dhd_pkttag_t*)(tag))->if_flags & \ + ~(DHD_PKTTAG_PKTDIR_MASK << DHD_PKTTAG_PKTDIR_SHIFT)) | \ + (((dir) & DHD_PKTTAG_PKTDIR_MASK) << DHD_PKTTAG_PKTDIR_SHIFT) +#define DHD_PKTTAG_PKTDIR(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ + DHD_PKTTAG_PKTDIR_SHIFT) & DHD_PKTTAG_PKTDIR_MASK) + +#define DHD_PKTTAG_SETSIGNALONLY(tag, signalonly) ((dhd_pkttag_t*)(tag))->if_flags = \ + (((dhd_pkttag_t*)(tag))->if_flags & \ + ~(DHD_PKTTAG_SIGNALONLY_MASK << DHD_PKTTAG_SIGNALONLY_SHIFT)) | \ + (((signalonly) & DHD_PKTTAG_SIGNALONLY_MASK) << DHD_PKTTAG_SIGNALONLY_SHIFT) +#define DHD_PKTTAG_SIGNALONLY(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ + DHD_PKTTAG_SIGNALONLY_SHIFT) & DHD_PKTTAG_SIGNALONLY_MASK) + +#define DHD_PKTTAG_SETONETIMEPKTRQST(tag) ((dhd_pkttag_t*)(tag))->if_flags = \ + (((dhd_pkttag_t*)(tag))->if_flags & \ + ~(DHD_PKTTAG_ONETIMEPKTRQST_MASK << DHD_PKTTAG_ONETIMEPKTRQST_SHIFT)) | \ + (1 << DHD_PKTTAG_ONETIMEPKTRQST_SHIFT) +#define DHD_PKTTAG_ONETIMEPKTRQST(tag) ((((dhd_pkttag_t*)(tag))->if_flags >> \ + DHD_PKTTAG_ONETIMEPKTRQST_SHIFT) & DHD_PKTTAG_ONETIMEPKTRQST_MASK) + +#define DHD_PKTTAG_SETDSTN(tag, dstn_MAC_ea) memcpy(((dhd_pkttag_t*)((tag)))->dstn_ether, \ + (dstn_MAC_ea), ETHER_ADDR_LEN) +#define DHD_PKTTAG_DSTN(tag) ((dhd_pkttag_t*)(tag))->dstn_ether + +typedef int (*f_commitpkt_t)(void* ctx, void* p); + +#ifdef PROP_TXSTATUS_DEBUG +#define DHD_WLFC_CTRINC_MAC_CLOSE(entry) do { (entry)->closed_ct++; } while (0) +#define DHD_WLFC_CTRINC_MAC_OPEN(entry) do { (entry)->opened_ct++; } while (0) +#else +#define DHD_WLFC_CTRINC_MAC_CLOSE(entry) do {} while (0) +#define DHD_WLFC_CTRINC_MAC_OPEN(entry) do {} while (0) +#endif + +#endif /* PROP_TXSTATUS */ + +extern void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar); +extern void dhd_wait_event_wakeup(dhd_pub_t*dhd); + +#ifdef ARP_OFFLOAD_SUPPORT +#define MAX_IPV4_ENTRIES 8 +/* dhd_commn arp offload wrapers */ +void dhd_aoe_hostip_clr(dhd_pub_t *dhd); +void dhd_aoe_arp_clr(dhd_pub_t *dhd); +int dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen); +void dhd_arp_offload_add_ip(dhd_pub_t *dhd, uint32 ipaddr); +#endif /* ARP_OFFLOAD_SUPPORT */ + +#endif /* _dhd_h_ */ diff --git a/drivers/net/wireless/bcmdhd/dhd_bta.c b/drivers/net/wireless/bcmdhd/dhd_bta.c new file mode 100644 index 0000000000000..6b782ea4a4d2a --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_bta.c @@ -0,0 +1,335 @@ +/* + * BT-AMP support routines + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_bta.c,v 1.10.4.2 2010-12-22 23:47:23 Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#ifdef SEND_HCI_CMD_VIA_IOCTL +#define BTA_HCI_CMD_MAX_LEN HCI_CMD_PREAMBLE_SIZE + HCI_CMD_DATA_SIZE + +/* Send HCI cmd via wl iovar HCI_cmd to the dongle. */ +int +dhd_bta_docmd(dhd_pub_t *pub, void *cmd_buf, uint cmd_len) +{ + amp_hci_cmd_t *cmd = (amp_hci_cmd_t *)cmd_buf; + uint8 buf[BTA_HCI_CMD_MAX_LEN + 16]; + uint len = sizeof(buf); + wl_ioctl_t ioc; + + if (cmd_len < HCI_CMD_PREAMBLE_SIZE) + return BCME_BADLEN; + + if ((uint)cmd->plen + HCI_CMD_PREAMBLE_SIZE > cmd_len) + return BCME_BADLEN; + + len = bcm_mkiovar("HCI_cmd", + (char *)cmd, (uint)cmd->plen + HCI_CMD_PREAMBLE_SIZE, (char *)buf, len); + + + memset(&ioc, 0, sizeof(ioc)); + + ioc.cmd = WLC_SET_VAR; + ioc.buf = buf; + ioc.len = len; + ioc.set = TRUE; + + return dhd_wl_ioctl(pub, &ioc, ioc.buf, ioc.len); +} +#else /* !SEND_HCI_CMD_VIA_IOCTL */ + +static void +dhd_bta_flush_hcidata(dhd_pub_t *pub, uint16 llh) +{ + int prec; + struct pktq *q; + uint count = 0; + + q = dhd_bus_txq(pub->bus); + if (q == NULL) + return; + + DHD_BTA(("dhd: flushing HCI ACL data for logical link %u...\n", llh)); + + dhd_os_sdlock_txq(pub); + + /* Walk through the txq and toss all HCI ACL data packets */ + PKTQ_PREC_ITER(q, prec) { + void *head_pkt = NULL; + + while (pktq_ppeek(q, prec) != head_pkt) { + void *pkt = pktq_pdeq(q, prec); + int ifidx; + + PKTPULL(pub->osh, pkt, dhd_bus_hdrlen(pub->bus)); + dhd_prot_hdrpull(pub, &ifidx, pkt); + + if (PKTLEN(pub->osh, pkt) >= RFC1042_HDR_LEN) { + struct ether_header *eh = + (struct ether_header *)PKTDATA(pub->osh, pkt); + + if (ntoh16(eh->ether_type) < ETHER_TYPE_MIN) { + struct dot11_llc_snap_header *lsh = + (struct dot11_llc_snap_header *)&eh[1]; + + if (bcmp(lsh, BT_SIG_SNAP_MPROT, + DOT11_LLC_SNAP_HDR_LEN - 2) == 0 && + ntoh16(lsh->type) == BTA_PROT_L2CAP) { + amp_hci_ACL_data_t *ACL_data = + (amp_hci_ACL_data_t *)&lsh[1]; + uint16 handle = ltoh16(ACL_data->handle); + + if (HCI_ACL_DATA_HANDLE(handle) == llh) { + PKTFREE(pub->osh, pkt, TRUE); + count ++; + continue; + } + } + } + } + + dhd_prot_hdrpush(pub, ifidx, pkt); + PKTPUSH(pub->osh, pkt, dhd_bus_hdrlen(pub->bus)); + + if (head_pkt == NULL) + head_pkt = pkt; + pktq_penq(q, prec, pkt); + } + } + + dhd_os_sdunlock_txq(pub); + + DHD_BTA(("dhd: flushed %u packet(s) for logical link %u...\n", count, llh)); +} + +/* Handle HCI cmd locally. + * Return 0: continue to send the cmd across SDIO + * < 0: stop, fail + * > 0: stop, succuess + */ +static int +_dhd_bta_docmd(dhd_pub_t *pub, amp_hci_cmd_t *cmd) +{ + int status = 0; + + switch (ltoh16_ua((uint8 *)&cmd->opcode)) { + case HCI_Enhanced_Flush: { + eflush_cmd_parms_t *cmdparms = (eflush_cmd_parms_t *)cmd->parms; + dhd_bta_flush_hcidata(pub, ltoh16_ua(cmdparms->llh)); + break; + } + default: + break; + } + + return status; +} + +/* Send HCI cmd encapsulated in BT-SIG frame via data channel to the dongle. */ +int +dhd_bta_docmd(dhd_pub_t *pub, void *cmd_buf, uint cmd_len) +{ + amp_hci_cmd_t *cmd = (amp_hci_cmd_t *)cmd_buf; + struct ether_header *eh; + struct dot11_llc_snap_header *lsh; + osl_t *osh = pub->osh; + uint len; + void *p; + int status; + + if (cmd_len < HCI_CMD_PREAMBLE_SIZE) { + DHD_ERROR(("dhd_bta_docmd: short command, cmd_len %u\n", cmd_len)); + return BCME_BADLEN; + } + + if ((len = (uint)cmd->plen + HCI_CMD_PREAMBLE_SIZE) > cmd_len) { + DHD_ERROR(("dhd_bta_docmd: malformed command, len %u cmd_len %u\n", + len, cmd_len)); + /* return BCME_BADLEN; */ + } + + p = PKTGET(osh, pub->hdrlen + RFC1042_HDR_LEN + len, TRUE); + if (p == NULL) { + DHD_ERROR(("dhd_bta_docmd: out of memory\n")); + return BCME_NOMEM; + } + + + /* intercept and handle the HCI cmd locally */ + if ((status = _dhd_bta_docmd(pub, cmd)) > 0) + return 0; + else if (status < 0) + return status; + + /* copy in HCI cmd */ + PKTPULL(osh, p, pub->hdrlen + RFC1042_HDR_LEN); + bcopy(cmd, PKTDATA(osh, p), len); + + /* copy in partial Ethernet header with BT-SIG LLC/SNAP header */ + PKTPUSH(osh, p, RFC1042_HDR_LEN); + eh = (struct ether_header *)PKTDATA(osh, p); + bzero(eh->ether_dhost, ETHER_ADDR_LEN); + ETHER_SET_LOCALADDR(eh->ether_dhost); + bcopy(&pub->mac, eh->ether_shost, ETHER_ADDR_LEN); + eh->ether_type = hton16(len + DOT11_LLC_SNAP_HDR_LEN); + lsh = (struct dot11_llc_snap_header *)&eh[1]; + bcopy(BT_SIG_SNAP_MPROT, lsh, DOT11_LLC_SNAP_HDR_LEN - 2); + lsh->type = 0; + + return dhd_sendpkt(pub, 0, p); +} +#endif /* !SEND_HCI_CMD_VIA_IOCTL */ + +/* Send HCI ACL data to dongle via data channel */ +int +dhd_bta_tx_hcidata(dhd_pub_t *pub, void *data_buf, uint data_len) +{ + amp_hci_ACL_data_t *data = (amp_hci_ACL_data_t *)data_buf; + struct ether_header *eh; + struct dot11_llc_snap_header *lsh; + osl_t *osh = pub->osh; + uint len; + void *p; + + if (data_len < HCI_ACL_DATA_PREAMBLE_SIZE) { + DHD_ERROR(("dhd_bta_tx_hcidata: short data_buf, data_len %u\n", data_len)); + return BCME_BADLEN; + } + + if ((len = (uint)ltoh16(data->dlen) + HCI_ACL_DATA_PREAMBLE_SIZE) > data_len) { + DHD_ERROR(("dhd_bta_tx_hcidata: malformed hci data, len %u data_len %u\n", + len, data_len)); + /* return BCME_BADLEN; */ + } + + p = PKTGET(osh, pub->hdrlen + RFC1042_HDR_LEN + len, TRUE); + if (p == NULL) { + DHD_ERROR(("dhd_bta_tx_hcidata: out of memory\n")); + return BCME_NOMEM; + } + + + /* copy in HCI ACL data header and HCI ACL data */ + PKTPULL(osh, p, pub->hdrlen + RFC1042_HDR_LEN); + bcopy(data, PKTDATA(osh, p), len); + + /* copy in partial Ethernet header with BT-SIG LLC/SNAP header */ + PKTPUSH(osh, p, RFC1042_HDR_LEN); + eh = (struct ether_header *)PKTDATA(osh, p); + bzero(eh->ether_dhost, ETHER_ADDR_LEN); + bcopy(&pub->mac, eh->ether_shost, ETHER_ADDR_LEN); + eh->ether_type = hton16(len + DOT11_LLC_SNAP_HDR_LEN); + lsh = (struct dot11_llc_snap_header *)&eh[1]; + bcopy(BT_SIG_SNAP_MPROT, lsh, DOT11_LLC_SNAP_HDR_LEN - 2); + lsh->type = HTON16(BTA_PROT_L2CAP); + + return dhd_sendpkt(pub, 0, p); +} + +/* txcomplete callback */ +void +dhd_bta_tx_hcidata_complete(dhd_pub_t *dhdp, void *txp, bool success) +{ + uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, txp); + amp_hci_ACL_data_t *ACL_data = (amp_hci_ACL_data_t *)(pktdata + RFC1042_HDR_LEN); + uint16 handle = ltoh16(ACL_data->handle); + uint16 llh = HCI_ACL_DATA_HANDLE(handle); + + wl_event_msg_t event; + uint8 data[HCI_EVT_PREAMBLE_SIZE + sizeof(num_completed_data_blocks_evt_parms_t)]; + amp_hci_event_t *evt; + num_completed_data_blocks_evt_parms_t *parms; + + uint16 len = HCI_EVT_PREAMBLE_SIZE + sizeof(num_completed_data_blocks_evt_parms_t); + + /* update the event struct */ + memset(&event, 0, sizeof(event)); + event.version = hton16(BCM_EVENT_MSG_VERSION); + event.event_type = hton32(WLC_E_BTA_HCI_EVENT); + event.status = 0; + event.reason = 0; + event.auth_type = 0; + event.datalen = hton32(len); + event.flags = 0; + + /* generate Number of Completed Blocks event */ + evt = (amp_hci_event_t *)data; + evt->ecode = HCI_Number_of_Completed_Data_Blocks; + evt->plen = sizeof(num_completed_data_blocks_evt_parms_t); + + parms = (num_completed_data_blocks_evt_parms_t *)evt->parms; + htol16_ua_store(dhdp->maxdatablks, (uint8 *)&parms->num_blocks); + parms->num_handles = 1; + htol16_ua_store(llh, (uint8 *)&parms->completed[0].handle); + parms->completed[0].pkts = 1; + parms->completed[0].blocks = 1; + + dhd_sendup_event_common(dhdp, &event, data); +} + +/* event callback */ +void +dhd_bta_doevt(dhd_pub_t *dhdp, void *data_buf, uint data_len) +{ + amp_hci_event_t *evt = (amp_hci_event_t *)data_buf; + + switch (evt->ecode) { + case HCI_Command_Complete: { + cmd_complete_parms_t *parms = (cmd_complete_parms_t *)evt->parms; + switch (ltoh16_ua((uint8 *)&parms->opcode)) { + case HCI_Read_Data_Block_Size: { + read_data_block_size_evt_parms_t *parms2 = + (read_data_block_size_evt_parms_t *)parms->parms; + dhdp->maxdatablks = ltoh16_ua((uint8 *)&parms2->data_block_num); + break; + } + } + break; + } + + case HCI_Flush_Occurred: { + flush_occurred_evt_parms_t *evt_parms = (flush_occurred_evt_parms_t *)evt->parms; + dhd_bta_flush_hcidata(dhdp, ltoh16_ua((uint8 *)&evt_parms->handle)); + break; + } + default: + break; + } +} diff --git a/drivers/net/wireless/bcmdhd/dhd_bta.h b/drivers/net/wireless/bcmdhd/dhd_bta.h new file mode 100644 index 0000000000000..07d9cebb883a4 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_bta.h @@ -0,0 +1,39 @@ +/* + * BT-AMP support routines + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_bta.h,v 1.2 2009-02-26 22:35:56 Exp $ + */ +#ifndef __dhd_bta_h__ +#define __dhd_bta_h__ + +struct dhd_pub; + +extern int dhd_bta_docmd(struct dhd_pub *pub, void *cmd_buf, uint cmd_len); + +extern void dhd_bta_doevt(struct dhd_pub *pub, void *data_buf, uint data_len); + +extern int dhd_bta_tx_hcidata(struct dhd_pub *pub, void *data_buf, uint data_len); +extern void dhd_bta_tx_hcidata_complete(struct dhd_pub *dhdp, void *txp, bool success); + + +#endif /* __dhd_bta_h__ */ diff --git a/drivers/net/wireless/bcmdhd/dhd_bus.h b/drivers/net/wireless/bcmdhd/dhd_bus.h new file mode 100644 index 0000000000000..bccb8b6603f81 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_bus.h @@ -0,0 +1,99 @@ +/* + * Header file describing the internal (inter-module) DHD interfaces. + * + * Provides type definitions and function prototypes used to link the + * DHD OS, bus, and protocol modules. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_bus.h,v 1.14.28.1 2010-12-23 01:13:17 Exp $ + */ + +#ifndef _dhd_bus_h_ +#define _dhd_bus_h_ + +/* + * Exported from dhd bus module (dhd_usb, dhd_sdio) + */ + +/* Indicate (dis)interest in finding dongles. */ +extern int dhd_bus_register(void); +extern void dhd_bus_unregister(void); + +/* Download firmware image and nvram image */ +extern bool dhd_bus_download_firmware(struct dhd_bus *bus, osl_t *osh, + char *fw_path, char *nv_path); + +/* Stop bus module: clear pending frames, disable data flow */ +extern void dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex); + +/* Initialize bus module: prepare for communication w/dongle */ +extern int dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex); + +/* Get the Bus Idle Time */ +extern void dhd_bus_getidletime(dhd_pub_t *dhdp, int *idletime); + +/* Set the Bus Idle Time*/ +extern void dhd_bus_setidletime(dhd_pub_t *dhdp, int idle_time); +/* Send a data frame to the dongle. Callee disposes of txp. */ +extern int dhd_bus_txdata(struct dhd_bus *bus, void *txp); + +/* Send/receive a control message to/from the dongle. + * Expects caller to enforce a single outstanding transaction. + */ +extern int dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen); +extern int dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen); + +/* Watchdog timer function */ +extern bool dhd_bus_watchdog(dhd_pub_t *dhd); +extern void dhd_disable_intr(dhd_pub_t *dhd); + +#if defined(DHD_DEBUG) +/* Device console input function */ +extern int dhd_bus_console_in(dhd_pub_t *dhd, uchar *msg, uint msglen); +#endif /* defined(DHD_DEBUG) */ + +/* Deferred processing for the bus, return TRUE requests reschedule */ +extern bool dhd_bus_dpc(struct dhd_bus *bus); +extern void dhd_bus_isr(bool * InterruptRecognized, bool * QueueMiniportHandleInterrupt, void *arg); + + +/* Check for and handle local prot-specific iovar commands */ +extern int dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name, + void *params, int plen, void *arg, int len, bool set); + +/* Add bus dump output to a buffer */ +extern void dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf); + +/* Clear any bus counters */ +extern void dhd_bus_clearcounts(dhd_pub_t *dhdp); + +/* return the dongle chipid */ +extern uint dhd_bus_chip(struct dhd_bus *bus); + +/* Set user-specified nvram parameters. */ +extern void dhd_bus_set_nvram_params(struct dhd_bus * bus, const char *nvram_params); + +extern void *dhd_bus_pub(struct dhd_bus *bus); +extern void *dhd_bus_txq(struct dhd_bus *bus); +extern uint dhd_bus_hdrlen(struct dhd_bus *bus); + +#endif /* _dhd_bus_h_ */ diff --git a/drivers/net/wireless/bcmdhd/dhd_cdc.c b/drivers/net/wireless/bcmdhd/dhd_cdc.c new file mode 100644 index 0000000000000..f16d81c9e198b --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_cdc.c @@ -0,0 +1,2534 @@ +/* + * DHD Protocol Module for CDC and BDC. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_cdc.c 324280 2012-03-28 19:01:17Z $ + * + * BDC is like CDC, except it includes a header for data packets to convey + * packet priority over the bus, and flags (e.g. to indicate checksum status + * for dongle offload.) + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + + +#ifdef PROP_TXSTATUS +#include +#include +#endif + + +#define RETRIES 2 /* # of retries to retrieve matching ioctl response */ +#define BUS_HEADER_LEN (16+DHD_SDALIGN) /* Must be at least SDPCM_RESERVE + * defined in dhd_sdio.c (amount of header tha might be added) + * plus any space that might be needed for alignment padding. + */ +#define ROUND_UP_MARGIN 2048 /* Biggest SDIO block size possible for + * round off at the end of buffer + */ + +#define BUS_RETRIES 1 /* # of retries before aborting a bus tx operation */ + +#ifdef PROP_TXSTATUS +typedef struct dhd_wlfc_commit_info { + uint8 needs_hdr; + uint8 ac_fifo_credit_spent; + ewlfc_packet_state_t pkt_type; + wlfc_mac_descriptor_t* mac_entry; + void* p; +} dhd_wlfc_commit_info_t; +#endif /* PROP_TXSTATUS */ + +typedef struct dhd_prot { + uint16 reqid; + uint8 pending; + uint32 lastcmd; + uint8 bus_header[BUS_HEADER_LEN]; + cdc_ioctl_t msg; + unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN]; +} dhd_prot_t; + +extern int dhd_dbus_txdata(dhd_pub_t *dhdp, void *pktbuf); + +static int +dhdcdc_msg(dhd_pub_t *dhd) +{ + int err = 0; + dhd_prot_t *prot = dhd->prot; + int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t); + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + DHD_OS_WAKE_LOCK(dhd); + + /* NOTE : cdc->msg.len holds the desired length of the buffer to be + * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area + * is actually sent to the dongle + */ + if (len > CDC_MAX_MSG_SIZE) + len = CDC_MAX_MSG_SIZE; + + /* Send request */ + err = dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len); + + DHD_OS_WAKE_UNLOCK(dhd); + return err; +} + +static int +dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len) +{ + int ret; + int cdc_len = len+sizeof(cdc_ioctl_t); + dhd_prot_t *prot = dhd->prot; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + do { + ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, cdc_len); + if (ret < 0) + break; + } while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id); + + return ret; +} + +static int +dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uint8 action) +{ + dhd_prot_t *prot = dhd->prot; + cdc_ioctl_t *msg = &prot->msg; + void *info; + int ret = 0, retries = 0; + uint32 id, flags = 0; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len)); + + + /* Respond "bcmerror" and "bcmerrorstr" with local cache */ + if (cmd == WLC_GET_VAR && buf) + { + if (!strcmp((char *)buf, "bcmerrorstr")) + { + strncpy((char *)buf, bcmerrorstr(dhd->dongle_error), BCME_STRLEN); + goto done; + } + else if (!strcmp((char *)buf, "bcmerror")) + { + *(int *)buf = dhd->dongle_error; + goto done; + } + } + + memset(msg, 0, sizeof(cdc_ioctl_t)); + + msg->cmd = htol32(cmd); + msg->len = htol32(len); + msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT); + CDC_SET_IF_IDX(msg, ifidx); + /* add additional action bits */ + action &= WL_IOCTL_ACTION_MASK; + msg->flags |= (action << CDCF_IOC_ACTION_SHIFT); + msg->flags = htol32(msg->flags); + + if (buf) + memcpy(prot->buf, buf, len); + + if ((ret = dhdcdc_msg(dhd)) < 0) { + if (!dhd->hang_was_sent) + DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret)); + goto done; + } + +retry: + /* wait for interrupt and get first fragment */ + if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0) + goto done; + + flags = ltoh32(msg->flags); + id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT; + + if ((id < prot->reqid) && (++retries < RETRIES)) + goto retry; + if (id != prot->reqid) { + DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n", + dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid)); + ret = -EINVAL; + goto done; + } + + /* Check info buffer */ + info = (void*)&msg[1]; + + /* Copy info buffer */ + if (buf) + { + if (ret < (int)len) + len = ret; + memcpy(buf, info, len); + } + + /* Check the ERROR flag */ + if (flags & CDCF_IOC_ERROR) + { + ret = ltoh32(msg->status); + /* Cache error from dongle */ + dhd->dongle_error = ret; + } + +done: + return ret; +} + +static int +dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uint8 action) +{ + dhd_prot_t *prot = dhd->prot; + cdc_ioctl_t *msg = &prot->msg; + int ret = 0; + uint32 flags, id; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len)); + + if (dhd->busstate == DHD_BUS_DOWN) { + DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__)); + return -EIO; + } + + /* don't talk to the dongle if fw is about to be reloaded */ + if (dhd->hang_was_sent) { + DHD_ERROR(("%s: HANG was sent up earlier. Not talking to the chip\n", + __FUNCTION__)); + return -EIO; + } + + memset(msg, 0, sizeof(cdc_ioctl_t)); + + msg->cmd = htol32(cmd); + msg->len = htol32(len); + msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT); + CDC_SET_IF_IDX(msg, ifidx); + /* add additional action bits */ + action &= WL_IOCTL_ACTION_MASK; + msg->flags |= (action << CDCF_IOC_ACTION_SHIFT) | CDCF_IOC_SET; + msg->flags = htol32(msg->flags); + + if (buf) + memcpy(prot->buf, buf, len); + + if ((ret = dhdcdc_msg(dhd)) < 0) { + DHD_ERROR(("%s: dhdcdc_msg failed w/status %d\n", __FUNCTION__, ret)); + goto done; + } + + if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0) + goto done; + + flags = ltoh32(msg->flags); + id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT; + + if (id != prot->reqid) { + DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n", + dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid)); + ret = -EINVAL; + goto done; + } + + /* Check the ERROR flag */ + if (flags & CDCF_IOC_ERROR) + { + ret = ltoh32(msg->status); + /* Cache error from dongle */ + dhd->dongle_error = ret; + } + +done: + return ret; +} + + +int +dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len) +{ + dhd_prot_t *prot = dhd->prot; + int ret = -1; + uint8 action; + + if ((dhd->busstate == DHD_BUS_DOWN) || dhd->hang_was_sent) { + DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__)); + goto done; + } + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + ASSERT(len <= WLC_IOCTL_MAXLEN); + + if (len > WLC_IOCTL_MAXLEN) + goto done; + + if (prot->pending == TRUE) { + DHD_ERROR(("CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n", + ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd, + (unsigned long)prot->lastcmd)); + if ((ioc->cmd == WLC_SET_VAR) || (ioc->cmd == WLC_GET_VAR)) { + DHD_TRACE(("iovar cmd=%s\n", (char*)buf)); + } + goto done; + } + + prot->pending = TRUE; + prot->lastcmd = ioc->cmd; + action = ioc->set; + if (action & WL_IOCTL_ACTION_SET) + ret = dhdcdc_set_ioctl(dhd, ifidx, ioc->cmd, buf, len, action); + else { + ret = dhdcdc_query_ioctl(dhd, ifidx, ioc->cmd, buf, len, action); + if (ret > 0) + ioc->used = ret - sizeof(cdc_ioctl_t); + } + + /* Too many programs assume ioctl() returns 0 on success */ + if (ret >= 0) + ret = 0; + else { + cdc_ioctl_t *msg = &prot->msg; + ioc->needed = ltoh32(msg->len); /* len == needed when set/query fails from dongle */ + } + + /* Intercept the wme_dp ioctl here */ + if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) { + int slen, val = 0; + + slen = strlen("wme_dp") + 1; + if (len >= (int)(slen + sizeof(int))) + bcopy(((char *)buf + slen), &val, sizeof(int)); + dhd->wme_dp = (uint8) ltoh32(val); + } + + prot->pending = FALSE; + +done: + return ret; +} + +int +dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name, + void *params, int plen, void *arg, int len, bool set) +{ + return BCME_UNSUPPORTED; +} + +#ifdef PROP_TXSTATUS +void +dhd_wlfc_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf) +{ + int i; + uint8* ea; + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhdp->wlfc_state; + wlfc_hanger_t* h; + wlfc_mac_descriptor_t* mac_table; + wlfc_mac_descriptor_t* interfaces; + char* iftypes[] = {"STA", "AP", "WDS", "p2pGO", "p2pCL"}; + + if (wlfc == NULL) { + bcm_bprintf(strbuf, "wlfc not initialized yet\n"); + return; + } + h = (wlfc_hanger_t*)wlfc->hanger; + if (h == NULL) { + bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n"); + } + + mac_table = wlfc->destination_entries.nodes; + interfaces = wlfc->destination_entries.interfaces; + bcm_bprintf(strbuf, "---- wlfc stats ----\n"); + if (h) { + bcm_bprintf(strbuf, "wlfc hanger (pushed,popped,f_push," + "f_pop,f_slot, pending) = (%d,%d,%d,%d,%d,%d)\n", + h->pushed, + h->popped, + h->failed_to_push, + h->failed_to_pop, + h->failed_slotfind, + (h->pushed - h->popped)); + } + + bcm_bprintf(strbuf, "wlfc fail(tlv,credit_rqst,mac_update,psmode_update), " + "(dq_full,sendq_full, rollback_fail) = (%d,%d,%d,%d), (%d,%d,%d)\n", + wlfc->stats.tlv_parse_failed, + wlfc->stats.credit_request_failed, + wlfc->stats.mac_update_failed, + wlfc->stats.psmode_update_failed, + wlfc->stats.delayq_full_error, + wlfc->stats.sendq_full_error, + wlfc->stats.rollback_failed); + + bcm_bprintf(strbuf, "SENDQ (len,credit,sent) " + "(AC0[%d,%d,%d],AC1[%d,%d,%d],AC2[%d,%d,%d],AC3[%d,%d,%d],BC_MC[%d,%d,%d])\n", + wlfc->SENDQ.q[0].len, wlfc->FIFO_credit[0], wlfc->stats.sendq_pkts[0], + wlfc->SENDQ.q[1].len, wlfc->FIFO_credit[1], wlfc->stats.sendq_pkts[1], + wlfc->SENDQ.q[2].len, wlfc->FIFO_credit[2], wlfc->stats.sendq_pkts[2], + wlfc->SENDQ.q[3].len, wlfc->FIFO_credit[3], wlfc->stats.sendq_pkts[3], + wlfc->SENDQ.q[4].len, wlfc->FIFO_credit[4], wlfc->stats.sendq_pkts[4]); + +#ifdef PROP_TXSTATUS_DEBUG + bcm_bprintf(strbuf, "SENDQ dropped: AC[0-3]:(%d,%d,%d,%d), (bcmc,atim):(%d,%d)\n", + wlfc->stats.dropped_qfull[0], wlfc->stats.dropped_qfull[1], + wlfc->stats.dropped_qfull[2], wlfc->stats.dropped_qfull[3], + wlfc->stats.dropped_qfull[4], wlfc->stats.dropped_qfull[5]); +#endif + + bcm_bprintf(strbuf, "\n"); + for (i = 0; i < WLFC_MAX_IFNUM; i++) { + if (interfaces[i].occupied) { + char* iftype_desc; + + if (interfaces[i].iftype > WLC_E_IF_ROLE_P2P_CLIENT) + iftype_desc = "stats.latency_sample_count) { + moving_samples = sizeof(wlfc->stats.deltas)/sizeof(uint32); + + for (i = 0; i < moving_samples; i++) + moving_avg += wlfc->stats.deltas[i]; + moving_avg /= moving_samples; + + avg = (100 * wlfc->stats.total_status_latency) / + wlfc->stats.latency_sample_count; + bcm_bprintf(strbuf, "txstatus latency (average, last, moving[%d]) = " + "(%d.%d, %03d, %03d)\n", + moving_samples, avg/100, (avg - (avg/100)*100), + wlfc->stats.latency_most_recent, + moving_avg); + } + } + + bcm_bprintf(strbuf, "wlfc- fifo[0-5] credit stats: sent = (%d,%d,%d,%d,%d,%d), " + "back = (%d,%d,%d,%d,%d,%d)\n", + wlfc->stats.fifo_credits_sent[0], + wlfc->stats.fifo_credits_sent[1], + wlfc->stats.fifo_credits_sent[2], + wlfc->stats.fifo_credits_sent[3], + wlfc->stats.fifo_credits_sent[4], + wlfc->stats.fifo_credits_sent[5], + + wlfc->stats.fifo_credits_back[0], + wlfc->stats.fifo_credits_back[1], + wlfc->stats.fifo_credits_back[2], + wlfc->stats.fifo_credits_back[3], + wlfc->stats.fifo_credits_back[4], + wlfc->stats.fifo_credits_back[5]); + { + uint32 fifo_cr_sent = 0; + uint32 fifo_cr_acked = 0; + uint32 request_cr_sent = 0; + uint32 request_cr_ack = 0; + uint32 bc_mc_cr_ack = 0; + + for (i = 0; i < sizeof(wlfc->stats.fifo_credits_sent)/sizeof(uint32); i++) { + fifo_cr_sent += wlfc->stats.fifo_credits_sent[i]; + } + + for (i = 0; i < sizeof(wlfc->stats.fifo_credits_back)/sizeof(uint32); i++) { + fifo_cr_acked += wlfc->stats.fifo_credits_back[i]; + } + + for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) { + if (wlfc->destination_entries.nodes[i].occupied) { + request_cr_sent += + wlfc->destination_entries.nodes[i].dstncredit_sent_packets; + } + } + for (i = 0; i < WLFC_MAX_IFNUM; i++) { + if (wlfc->destination_entries.interfaces[i].occupied) { + request_cr_sent += + wlfc->destination_entries.interfaces[i].dstncredit_sent_packets; + } + } + for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) { + if (wlfc->destination_entries.nodes[i].occupied) { + request_cr_ack += + wlfc->destination_entries.nodes[i].dstncredit_acks; + } + } + for (i = 0; i < WLFC_MAX_IFNUM; i++) { + if (wlfc->destination_entries.interfaces[i].occupied) { + request_cr_ack += + wlfc->destination_entries.interfaces[i].dstncredit_acks; + } + } + bcm_bprintf(strbuf, "wlfc- (sent, status) => pq(%d,%d), vq(%d,%d)," + "other:%d, bc_mc:%d, signal-only, (sent,freed): (%d,%d)", + fifo_cr_sent, fifo_cr_acked, + request_cr_sent, request_cr_ack, + wlfc->destination_entries.other.dstncredit_acks, + bc_mc_cr_ack, + wlfc->stats.signal_only_pkts_sent, wlfc->stats.signal_only_pkts_freed); + } +#endif /* PROP_TXSTATUS_DEBUG */ + bcm_bprintf(strbuf, "\n"); + bcm_bprintf(strbuf, "wlfc- pkt((in,2bus,txstats,hdrpull),(dropped,hdr_only,wlc_tossed)" + "(freed,free_err,rollback)) = " + "((%d,%d,%d,%d),(%d,%d,%d),(%d,%d,%d))\n", + wlfc->stats.pktin, + wlfc->stats.pkt2bus, + wlfc->stats.txstatus_in, + wlfc->stats.dhd_hdrpulls, + + wlfc->stats.pktdropped, + wlfc->stats.wlfc_header_only_pkt, + wlfc->stats.wlc_tossed_pkts, + + wlfc->stats.pkt_freed, + wlfc->stats.pkt_free_err, wlfc->stats.rollback); + + bcm_bprintf(strbuf, "wlfc- suppress((d11,wlc,err),enq(d11,wl,hq,mac?),retx(d11,wlc,hq)) = " + "((%d,%d,%d),(%d,%d,%d,%d),(%d,%d,%d))\n", + + wlfc->stats.d11_suppress, + wlfc->stats.wl_suppress, + wlfc->stats.bad_suppress, + + wlfc->stats.psq_d11sup_enq, + wlfc->stats.psq_wlsup_enq, + wlfc->stats.psq_hostq_enq, + wlfc->stats.mac_handle_notfound, + + wlfc->stats.psq_d11sup_retx, + wlfc->stats.psq_wlsup_retx, + wlfc->stats.psq_hostq_retx); + return; +} + +/* Create a place to store all packet pointers submitted to the firmware until + a status comes back, suppress or otherwise. + + hang-er: noun, a contrivance on which things are hung, as a hook. +*/ +static void* +dhd_wlfc_hanger_create(osl_t *osh, int max_items) +{ + int i; + wlfc_hanger_t* hanger; + + /* allow only up to a specific size for now */ + ASSERT(max_items == WLFC_HANGER_MAXITEMS); + + if ((hanger = (wlfc_hanger_t*)MALLOC(osh, WLFC_HANGER_SIZE(max_items))) == NULL) + return NULL; + + memset(hanger, 0, WLFC_HANGER_SIZE(max_items)); + hanger->max_items = max_items; + + for (i = 0; i < hanger->max_items; i++) { + hanger->items[i].state = WLFC_HANGER_ITEM_STATE_FREE; + } + return hanger; +} + +static int +dhd_wlfc_hanger_delete(osl_t *osh, void* hanger) +{ + wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; + + if (h) { + MFREE(osh, h, WLFC_HANGER_SIZE(h->max_items)); + return BCME_OK; + } + return BCME_BADARG; +} + +static uint16 +dhd_wlfc_hanger_get_free_slot(void* hanger) +{ + int i; + wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; + + if (h) { + for (i = 0; i < h->max_items; i++) { + if (h->items[i].state == WLFC_HANGER_ITEM_STATE_FREE) + return (uint16)i; + } + h->failed_slotfind++; + } + return WLFC_HANGER_MAXITEMS; +} + +static int +dhd_wlfc_hanger_pushpkt(void* hanger, void* pkt, uint32 slot_id) +{ + int rc = BCME_OK; + wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; + + if (h && (slot_id < WLFC_HANGER_MAXITEMS)) { + if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_FREE) { + h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE; + h->items[slot_id].pkt = pkt; + h->items[slot_id].identifier = slot_id; + h->pushed++; + } + else { + h->failed_to_push++; + rc = BCME_NOTFOUND; + } + } + else + rc = BCME_BADARG; + return rc; +} + +static int +dhd_wlfc_hanger_poppkt(void* hanger, uint32 slot_id, void** pktout, int remove_from_hanger) +{ + int rc = BCME_OK; + wlfc_hanger_t* h = (wlfc_hanger_t*)hanger; + + /* this packet was not pushed at the time it went to the firmware */ + if (slot_id == WLFC_HANGER_MAXITEMS) + return BCME_NOTFOUND; + + if (h) { + if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE) { + *pktout = h->items[slot_id].pkt; + if (remove_from_hanger) { + h->items[slot_id].state = + WLFC_HANGER_ITEM_STATE_FREE; + h->items[slot_id].pkt = NULL; + h->items[slot_id].identifier = 0; + h->popped++; + } + } + else { + h->failed_to_pop++; + rc = BCME_NOTFOUND; + } + } + else + rc = BCME_BADARG; + return rc; +} + +static int +_dhd_wlfc_pushheader(athost_wl_status_info_t* ctx, void* p, bool tim_signal, + uint8 tim_bmp, uint8 mac_handle, uint32 htodtag) +{ + uint32 wl_pktinfo = 0; + uint8* wlh; + uint8 dataOffset; + uint8 fillers; + uint8 tim_signal_len = 0; + + struct bdc_header *h; + + if (tim_signal) { + tim_signal_len = 1 + 1 + WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP; + } + + /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */ + dataOffset = WLFC_CTL_VALUE_LEN_PKTTAG + 2 + tim_signal_len; + fillers = ROUNDUP(dataOffset, 4) - dataOffset; + dataOffset += fillers; + + PKTPUSH(ctx->osh, p, dataOffset); + wlh = (uint8*) PKTDATA(ctx->osh, p); + + wl_pktinfo = htol32(htodtag); + + wlh[0] = WLFC_CTL_TYPE_PKTTAG; + wlh[1] = WLFC_CTL_VALUE_LEN_PKTTAG; + memcpy(&wlh[2], &wl_pktinfo, sizeof(uint32)); + + if (tim_signal_len) { + wlh[dataOffset - fillers - tim_signal_len ] = + WLFC_CTL_TYPE_PENDING_TRAFFIC_BMP; + wlh[dataOffset - fillers - tim_signal_len + 1] = + WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP; + wlh[dataOffset - fillers - tim_signal_len + 2] = mac_handle; + wlh[dataOffset - fillers - tim_signal_len + 3] = tim_bmp; + } + if (fillers) + memset(&wlh[dataOffset - fillers], WLFC_CTL_TYPE_FILLER, fillers); + + PKTPUSH(ctx->osh, p, BDC_HEADER_LEN); + h = (struct bdc_header *)PKTDATA(ctx->osh, p); + h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT); + if (PKTSUMNEEDED(p)) + h->flags |= BDC_FLAG_SUM_NEEDED; + + + h->priority = (PKTPRIO(p) & BDC_PRIORITY_MASK); + h->flags2 = 0; + h->dataOffset = dataOffset >> 2; + BDC_SET_IF_IDX(h, DHD_PKTTAG_IF(PKTTAG(p))); + return BCME_OK; +} + +static int +_dhd_wlfc_pullheader(athost_wl_status_info_t* ctx, void* pktbuf) +{ + struct bdc_header *h; + + if (PKTLEN(ctx->osh, pktbuf) < BDC_HEADER_LEN) { + WLFC_DBGMESG(("%s: rx data too short (%d < %d)\n", __FUNCTION__, + PKTLEN(ctx->osh, pktbuf), BDC_HEADER_LEN)); + return BCME_ERROR; + } + h = (struct bdc_header *)PKTDATA(ctx->osh, pktbuf); + + /* pull BDC header */ + PKTPULL(ctx->osh, pktbuf, BDC_HEADER_LEN); + /* pull wl-header */ + PKTPULL(ctx->osh, pktbuf, (h->dataOffset << 2)); + return BCME_OK; +} + +static wlfc_mac_descriptor_t* +_dhd_wlfc_find_table_entry(athost_wl_status_info_t* ctx, void* p) +{ + int i; + wlfc_mac_descriptor_t* table = ctx->destination_entries.nodes; + uint8 ifid = DHD_PKTTAG_IF(PKTTAG(p)); + uint8* dstn = DHD_PKTTAG_DSTN(PKTTAG(p)); + + /* no lookup necessary, only if this packet belongs to STA interface */ + if (((ctx->destination_entries.interfaces[ifid].iftype == WLC_E_IF_ROLE_STA) || + ETHER_ISMULTI(dstn) || + (ctx->destination_entries.interfaces[ifid].iftype == WLC_E_IF_ROLE_P2P_CLIENT)) && + (ctx->destination_entries.interfaces[ifid].occupied)) { + return &ctx->destination_entries.interfaces[ifid]; + } + + for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) { + if (table[i].occupied) { + if (table[i].interface_id == ifid) { + if (!memcmp(table[i].ea, dstn, ETHER_ADDR_LEN)) + return &table[i]; + } + } + } + return &ctx->destination_entries.other; +} + +static int +_dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t* ctx, + void* p, ewlfc_packet_state_t pkt_type, uint32 hslot) +{ + /* + put the packet back to the head of queue + + - a packet from send-q will need to go back to send-q and not delay-q + since that will change the order of packets. + - suppressed packet goes back to suppress sub-queue + - pull out the header, if new or delayed packet + + Note: hslot is used only when header removal is done. + */ + wlfc_mac_descriptor_t* entry; + void* pktout; + int rc = BCME_OK; + int prec; + + entry = _dhd_wlfc_find_table_entry(ctx, p); + prec = DHD_PKTTAG_FIFO(PKTTAG(p)); + if (entry != NULL) { + if (pkt_type == eWLFC_PKTTYPE_SUPPRESSED) { + /* wl-header is saved for suppressed packets */ + if (WLFC_PKTQ_PENQ_HEAD(&entry->psq, ((prec << 1) + 1), p) == NULL) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + rc = BCME_ERROR; + } + } + else { + /* remove header first */ + _dhd_wlfc_pullheader(ctx, p); + + if (pkt_type == eWLFC_PKTTYPE_DELAYED) { + /* delay-q packets are going to delay-q */ + if (WLFC_PKTQ_PENQ_HEAD(&entry->psq, (prec << 1), p) == NULL) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + rc = BCME_ERROR; + } + } + else { + /* these are going to SENDQ */ + if (WLFC_PKTQ_PENQ_HEAD(&ctx->SENDQ, prec, p) == NULL) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + rc = BCME_ERROR; + } + } + /* free the hanger slot */ + dhd_wlfc_hanger_poppkt(ctx->hanger, hslot, &pktout, 1); + + /* decrement sequence count */ + WLFC_DECR_SEQCOUNT(entry, prec); + } + /* + if this packet did not count against FIFO credit, it must have + taken a requested_credit from the firmware (for pspoll etc.) + */ + if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) { + entry->requested_credit++; + } + } + else { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + rc = BCME_ERROR; + } + if (rc != BCME_OK) + ctx->stats.rollback_failed++; + else + ctx->stats.rollback++; + + return rc; +} + +static void +_dhd_wlfc_flow_control_check(athost_wl_status_info_t* ctx, struct pktq* pq, uint8 if_id) +{ + if ((pq->len <= WLFC_FLOWCONTROL_LOWATER) && (ctx->hostif_flow_state[if_id] == ON)) { + /* start traffic */ + ctx->hostif_flow_state[if_id] = OFF; + /* + WLFC_DBGMESG(("qlen:%02d, if:%02d, ->OFF, start traffic %s()\n", + pq->len, if_id, __FUNCTION__)); + */ + WLFC_DBGMESG(("F")); + /* dhd_txflowcontrol(ctx->dhdp, if_id, OFF); */ + ctx->toggle_host_if = 0; + } + if ((pq->len >= WLFC_FLOWCONTROL_HIWATER) && (ctx->hostif_flow_state[if_id] == OFF)) { + /* stop traffic */ + ctx->hostif_flow_state[if_id] = ON; + /* + WLFC_DBGMESG(("qlen:%02d, if:%02d, ->ON, stop traffic %s()\n", + pq->len, if_id, __FUNCTION__)); + */ + WLFC_DBGMESG(("N")); + /* dhd_txflowcontrol(ctx->dhdp, if_id, ON); */ + ctx->host_ifidx = if_id; + ctx->toggle_host_if = 1; + } + return; +} + +static int +_dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry, + uint8 ta_bmp) +{ + int rc = BCME_OK; + void* p = NULL; + int dummylen = ((dhd_pub_t *)ctx->dhdp)->hdrlen+ 12; + + /* allocate a dummy packet */ + p = PKTGET(ctx->osh, dummylen, TRUE); + if (p) { + PKTPULL(ctx->osh, p, dummylen); + DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), 0); + _dhd_wlfc_pushheader(ctx, p, TRUE, ta_bmp, entry->mac_handle, 0); + DHD_PKTTAG_SETSIGNALONLY(PKTTAG(p), 1); +#ifdef PROP_TXSTATUS_DEBUG + ctx->stats.signal_only_pkts_sent++; +#endif + rc = dhd_bus_txdata(((dhd_pub_t *)ctx->dhdp)->bus, p); + if (rc != BCME_OK) { + PKTFREE(ctx->osh, p, TRUE); + } + } + else { + DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n", + __FUNCTION__, dummylen)); + rc = BCME_NOMEM; + } + return rc; +} + +/* Return TRUE if traffic availability changed */ +static bool +_dhd_wlfc_traffic_pending_check(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry, + int prec) +{ + bool rc = FALSE; + + if (entry->state == WLFC_STATE_CLOSE) { + if ((pktq_plen(&entry->psq, (prec << 1)) == 0) && + (pktq_plen(&entry->psq, ((prec << 1) + 1)) == 0)) { + + if (entry->traffic_pending_bmp & NBITVAL(prec)) { + rc = TRUE; + entry->traffic_pending_bmp = + entry->traffic_pending_bmp & ~ NBITVAL(prec); + } + } + else { + if (!(entry->traffic_pending_bmp & NBITVAL(prec))) { + rc = TRUE; + entry->traffic_pending_bmp = + entry->traffic_pending_bmp | NBITVAL(prec); + } + } + } + if (rc) { + /* request a TIM update to firmware at the next piggyback opportunity */ + if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) { + entry->send_tim_signal = 1; + _dhd_wlfc_send_signalonly_packet(ctx, entry, entry->traffic_pending_bmp); + entry->traffic_lastreported_bmp = entry->traffic_pending_bmp; + entry->send_tim_signal = 0; + } + else { + rc = FALSE; + } + } + return rc; +} + +static int +_dhd_wlfc_enque_suppressed(athost_wl_status_info_t* ctx, int prec, void* p) +{ + wlfc_mac_descriptor_t* entry; + + entry = _dhd_wlfc_find_table_entry(ctx, p); + if (entry == NULL) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + return BCME_NOTFOUND; + } + /* + - suppressed packets go to sub_queue[2*prec + 1] AND + - delayed packets go to sub_queue[2*prec + 0] to ensure + order of delivery. + */ + if (WLFC_PKTQ_PENQ(&entry->psq, ((prec << 1) + 1), p) == NULL) { + ctx->stats.delayq_full_error++; + /* WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); */ + WLFC_DBGMESG(("s")); + return BCME_ERROR; + } + /* A packet has been pushed, update traffic availability bitmap, if applicable */ + _dhd_wlfc_traffic_pending_check(ctx, entry, prec); + _dhd_wlfc_flow_control_check(ctx, &entry->psq, DHD_PKTTAG_IF(PKTTAG(p))); + return BCME_OK; +} + +static int +_dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t* ctx, + wlfc_mac_descriptor_t* entry, void* p, int header_needed, uint32* slot) +{ + int rc = BCME_OK; + int hslot = WLFC_HANGER_MAXITEMS; + bool send_tim_update = FALSE; + uint32 htod = 0; + uint8 free_ctr; + + *slot = hslot; + + if (entry == NULL) { + entry = _dhd_wlfc_find_table_entry(ctx, p); + } + + if (entry == NULL) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + return BCME_ERROR; + } + if (entry->send_tim_signal) { + send_tim_update = TRUE; + entry->send_tim_signal = 0; + entry->traffic_lastreported_bmp = entry->traffic_pending_bmp; + } + if (header_needed) { + hslot = dhd_wlfc_hanger_get_free_slot(ctx->hanger); + free_ctr = WLFC_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p))); + DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod); + } + else { + hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); + free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); + } + WLFC_PKTID_HSLOT_SET(htod, hslot); + WLFC_PKTID_FREERUNCTR_SET(htod, free_ctr); + DHD_PKTTAG_SETPKTDIR(PKTTAG(p), 1); + WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST); + WL_TXSTATUS_SET_FIFO(htod, DHD_PKTTAG_FIFO(PKTTAG(p))); + WLFC_PKTFLAG_SET_GENERATION(htod, entry->generation); + + if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) { + /* + Indicate that this packet is being sent in response to an + explicit request from the firmware side. + */ + WLFC_PKTFLAG_SET_PKTREQUESTED(htod); + } + else { + WLFC_PKTFLAG_CLR_PKTREQUESTED(htod); + } + if (header_needed) { + rc = _dhd_wlfc_pushheader(ctx, p, send_tim_update, + entry->traffic_lastreported_bmp, entry->mac_handle, htod); + if (rc == BCME_OK) { + DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod); + /* + a new header was created for this packet. + push to hanger slot and scrub q. Since bus + send succeeded, increment seq number as well. + */ + rc = dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot); + if (rc == BCME_OK) { + /* increment free running sequence count */ + WLFC_INCR_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p))); +#ifdef PROP_TXSTATUS_DEBUG + ((wlfc_hanger_t*)(ctx->hanger))->items[hslot].push_time = + OSL_SYSUPTIME(); +#endif + } + else { + WLFC_DBGMESG(("%s() hanger_pushpkt() failed, rc: %d\n", + __FUNCTION__, rc)); + } + } + } + else { + /* remove old header */ + _dhd_wlfc_pullheader(ctx, p); + + hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); + free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p))); + /* push new header */ + _dhd_wlfc_pushheader(ctx, p, send_tim_update, + entry->traffic_lastreported_bmp, entry->mac_handle, htod); + } + *slot = hslot; + return rc; +} + +static int +_dhd_wlfc_is_destination_closed(athost_wl_status_info_t* ctx, + wlfc_mac_descriptor_t* entry, int prec) +{ + if (ctx->destination_entries.interfaces[entry->interface_id].iftype == + WLC_E_IF_ROLE_P2P_GO) { + /* - destination interface is of type p2p GO. + For a p2pGO interface, if the destination is OPEN but the interface is + CLOSEd, do not send traffic. But if the dstn is CLOSEd while there is + destination-specific-credit left send packets. This is because the + firmware storing the destination-specific-requested packet in queue. + */ + if ((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) && + (entry->requested_packet == 0)) + return 1; + } + /* AP, p2p_go -> unicast desc entry, STA/p2p_cl -> interface desc. entry */ + if (((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) && + (entry->requested_packet == 0)) || + (!(entry->ac_bitmap & (1 << prec)))) + return 1; + + return 0; +} + +static void* +_dhd_wlfc_deque_delayedq(athost_wl_status_info_t* ctx, + int prec, uint8* ac_credit_spent, uint8* needs_hdr, wlfc_mac_descriptor_t** entry_out) +{ + wlfc_mac_descriptor_t* entry; + wlfc_mac_descriptor_t* table; + uint8 token_pos; + int total_entries; + void* p = NULL; + int pout; + int i; + + *entry_out = NULL; + token_pos = ctx->token_pos[prec]; + /* most cases a packet will count against FIFO credit */ + *ac_credit_spent = 1; + *needs_hdr = 1; + + /* search all entries, include nodes as well as interfaces */ + table = (wlfc_mac_descriptor_t*)&ctx->destination_entries; + total_entries = sizeof(ctx->destination_entries)/sizeof(wlfc_mac_descriptor_t); + + for (i = 0; i < total_entries; i++) { + entry = &table[(token_pos + i) % total_entries]; + if (entry->occupied) { + if (!_dhd_wlfc_is_destination_closed(ctx, entry, prec)) { + p = pktq_mdeq(&entry->psq, + /* higher precedence will be picked up first, + i.e. suppressed packets before delayed ones + */ + (NBITVAL((prec << 1) + 1) | NBITVAL((prec << 1))), + &pout); + if (p != NULL) { + /* did the packet come from suppress sub-queue? */ + if (pout == ((prec << 1) + 1)) { + /* + this packet was suppressed and was sent on the bus + previously; this already has a header + */ + *needs_hdr = 0; + } + if (entry->requested_credit > 0) { + entry->requested_credit--; +#ifdef PROP_TXSTATUS_DEBUG + entry->dstncredit_sent_packets++; +#endif + /* + if the packet was pulled out while destination is in + closed state but had a non-zero packets requested, + then this should not count against the FIFO credit. + That is due to the fact that the firmware will + most likely hold onto this packet until a suitable + time later to push it to the appropriate AC FIFO. + */ + if (entry->state == WLFC_STATE_CLOSE) + *ac_credit_spent = 0; + } + else if (entry->requested_packet > 0) { + entry->requested_packet--; + DHD_PKTTAG_SETONETIMEPKTRQST(PKTTAG(p)); + if (entry->state == WLFC_STATE_CLOSE) + *ac_credit_spent = 0; + } + /* move token to ensure fair round-robin */ + ctx->token_pos[prec] = + (token_pos + i + 1) % total_entries; + *entry_out = entry; + _dhd_wlfc_flow_control_check(ctx, &entry->psq, + DHD_PKTTAG_IF(PKTTAG(p))); + /* + A packet has been picked up, update traffic + availability bitmap, if applicable + */ + _dhd_wlfc_traffic_pending_check(ctx, entry, prec); + return p; + } + } + } + } + return NULL; +} + +static void* +_dhd_wlfc_deque_sendq(athost_wl_status_info_t* ctx, int prec, uint8* ac_credit_spent) +{ + wlfc_mac_descriptor_t* entry; + void* p; + + /* most cases a packet will count against FIFO credit */ + *ac_credit_spent = 1; + + p = pktq_pdeq(&ctx->SENDQ, prec); + if (p != NULL) { + if (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(p)))) + /* bc/mc packets do not have a delay queue */ + return p; + + entry = _dhd_wlfc_find_table_entry(ctx, p); + + if (entry == NULL) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + return p; + } + + while ((p != NULL) && _dhd_wlfc_is_destination_closed(ctx, entry, prec)) { + /* + - suppressed packets go to sub_queue[2*prec + 1] AND + - delayed packets go to sub_queue[2*prec + 0] to ensure + order of delivery. + */ + if (WLFC_PKTQ_PENQ(&entry->psq, (prec << 1), p) == NULL) { + WLFC_DBGMESG(("D")); + /* dhd_txcomplete(ctx->dhdp, p, FALSE); */ + PKTFREE(ctx->osh, p, TRUE); + ctx->stats.delayq_full_error++; + } + /* + A packet has been pushed, update traffic availability bitmap, + if applicable + */ + _dhd_wlfc_traffic_pending_check(ctx, entry, prec); + _dhd_wlfc_flow_control_check(ctx, &entry->psq, DHD_PKTTAG_IF(PKTTAG(p))); + p = pktq_pdeq(&ctx->SENDQ, prec); + if (p == NULL) + break; + + entry = _dhd_wlfc_find_table_entry(ctx, p); + + if ((entry == NULL) || (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(p))))) { + return p; + } + } + if (p) { + if (entry->requested_packet == 0) { + if (entry->requested_credit > 0) + entry->requested_credit--; + } + else { + entry->requested_packet--; + DHD_PKTTAG_SETONETIMEPKTRQST(PKTTAG(p)); + } + if (entry->state == WLFC_STATE_CLOSE) + *ac_credit_spent = 0; +#ifdef PROP_TXSTATUS_DEBUG + entry->dstncredit_sent_packets++; +#endif + } + if (p) + _dhd_wlfc_flow_control_check(ctx, &ctx->SENDQ, DHD_PKTTAG_IF(PKTTAG(p))); + } + return p; +} + +static int +_dhd_wlfc_mac_entry_update(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry, + ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea) +{ + int rc = BCME_OK; + + if (action == eWLFC_MAC_ENTRY_ACTION_ADD) { + entry->occupied = 1; + entry->state = WLFC_STATE_OPEN; + entry->requested_credit = 0; + entry->interface_id = ifid; + entry->iftype = iftype; + entry->ac_bitmap = 0xff; /* update this when handling APSD */ + /* for an interface entry we may not care about the MAC address */ + if (ea != NULL) + memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN); + pktq_init(&entry->psq, WLFC_PSQ_PREC_COUNT, WLFC_PSQ_LEN); + } + else if (action == eWLFC_MAC_ENTRY_ACTION_DEL) { + entry->occupied = 0; + entry->state = WLFC_STATE_CLOSE; + entry->requested_credit = 0; + /* enable after packets are queued-deqeued properly. + pktq_flush(dhd->osh, &entry->psq, FALSE, NULL, 0); + */ + } + return rc; +} + +int +_dhd_wlfc_borrow_credit(athost_wl_status_info_t* ctx, uint8 available_credit_map, int borrower_ac) +{ + int lender_ac; + int rc = BCME_ERROR; + + if (ctx == NULL || available_credit_map == 0) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + return BCME_BADARG; + } + + /* Borrow from lowest priority available AC (including BC/MC credits) */ + for (lender_ac = 0; lender_ac <= AC_COUNT; lender_ac++) { + if ((available_credit_map && (1 << lender_ac)) && + (ctx->FIFO_credit[lender_ac] > 0)) { + ctx->credits_borrowed[borrower_ac][lender_ac]++; + ctx->FIFO_credit[lender_ac]--; + rc = BCME_OK; + break; + } + } + + return rc; +} + +int +dhd_wlfc_interface_entry_update(void* state, + ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea) +{ + athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state; + wlfc_mac_descriptor_t* entry; + + if (ifid >= WLFC_MAX_IFNUM) + return BCME_BADARG; + + entry = &ctx->destination_entries.interfaces[ifid]; + return _dhd_wlfc_mac_entry_update(ctx, entry, action, ifid, iftype, ea); +} + +int +dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits) +{ + athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state; + + /* update the AC FIFO credit map */ + ctx->FIFO_credit[0] = credits[0]; + ctx->FIFO_credit[1] = credits[1]; + ctx->FIFO_credit[2] = credits[2]; + ctx->FIFO_credit[3] = credits[3]; + /* credit for bc/mc packets */ + ctx->FIFO_credit[4] = credits[4]; + /* credit for ATIM FIFO is not used yet. */ + ctx->FIFO_credit[5] = 0; + return BCME_OK; +} + +int +dhd_wlfc_enque_sendq(void* state, int prec, void* p) +{ + athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state; + + if ((state == NULL) || + /* prec = AC_COUNT is used for bc/mc queue */ + (prec > AC_COUNT) || + (p == NULL)) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + return BCME_BADARG; + } + if (FALSE == dhd_prec_enq(ctx->dhdp, &ctx->SENDQ, p, prec)) { + ctx->stats.sendq_full_error++; + /* + WLFC_DBGMESG(("Error: %s():%d, qlen:%d\n", + __FUNCTION__, __LINE__, ctx->SENDQ.len)); + */ + WLFC_HOST_FIFO_DROPPEDCTR_INC(ctx, prec); + WLFC_DBGMESG(("Q")); + PKTFREE(ctx->osh, p, TRUE); + return BCME_ERROR; + } + ctx->stats.pktin++; + /* _dhd_wlfc_flow_control_check(ctx, &ctx->SENDQ, DHD_PKTTAG_IF(PKTTAG(p))); */ + return BCME_OK; +} + +int +_dhd_wlfc_handle_packet_commit(athost_wl_status_info_t* ctx, int ac, + dhd_wlfc_commit_info_t *commit_info, f_commitpkt_t fcommit, void* commit_ctx) +{ + uint32 hslot; + int rc; + + /* + if ac_fifo_credit_spent = 0 + + This packet will not count against the FIFO credit. + To ensure the txstatus corresponding to this packet + does not provide an implied credit (default behavior) + mark the packet accordingly. + + if ac_fifo_credit_spent = 1 + + This is a normal packet and it counts against the FIFO + credit count. + */ + DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), commit_info->ac_fifo_credit_spent); + rc = _dhd_wlfc_pretx_pktprocess(ctx, commit_info->mac_entry, commit_info->p, + commit_info->needs_hdr, &hslot); + + if (rc == BCME_OK) + rc = fcommit(commit_ctx, commit_info->p); + else + ctx->stats.generic_error++; + + if (rc == BCME_OK) { + ctx->stats.pkt2bus++; + if (commit_info->ac_fifo_credit_spent) { + ctx->stats.sendq_pkts[ac]++; + WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac); + } + } + else { + /* + bus commit has failed, rollback. + - remove wl-header for a delayed packet + - save wl-header header for suppressed packets + */ + rc = _dhd_wlfc_rollback_packet_toq(ctx, commit_info->p, + (commit_info->pkt_type), hslot); + if (rc != BCME_OK) + ctx->stats.rollback_failed++; + + rc = BCME_ERROR; + } + + return rc; +} + +int +dhd_wlfc_commit_packets(void* state, f_commitpkt_t fcommit, void* commit_ctx) +{ + int ac; + int credit; + int rc; + dhd_wlfc_commit_info_t commit_info; + athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state; + int credit_count = 0; + int bus_retry_count = 0; + uint8 ac_available = 0; /* Bitmask for 4 ACs + BC/MC */ + + if ((state == NULL) || + (fcommit == NULL)) { + WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); + return BCME_BADARG; + } + + memset(&commit_info, 0, sizeof(commit_info)); + + /* + Commit packets for regular AC traffic. Higher priority first. + First, use up FIFO credits available to each AC. Based on distribution + and credits left, borrow from other ACs as applicable + + -NOTE: + If the bus between the host and firmware is overwhelmed by the + traffic from host, it is possible that higher priority traffic + starves the lower priority queue. If that occurs often, we may + have to employ weighted round-robin or ucode scheme to avoid + low priority packet starvation. + */ + + for (ac = AC_COUNT; ac >= 0; ac--) { + + int initial_credit_count = ctx->FIFO_credit[ac]; + + for (credit = 0; credit < ctx->FIFO_credit[ac];) { + commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac, + &(commit_info.ac_fifo_credit_spent), + &(commit_info.needs_hdr), + &(commit_info.mac_entry)); + + if (commit_info.p == NULL) + break; + + commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED : + eWLFC_PKTTYPE_SUPPRESSED; + + rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info, + fcommit, commit_ctx); + + /* Bus commits may fail (e.g. flow control); abort after retries */ + if (rc == BCME_OK) { + if (commit_info.ac_fifo_credit_spent) { + credit++; + } + } + else { + bus_retry_count++; + if (bus_retry_count >= BUS_RETRIES) { + DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n")); + ctx->FIFO_credit[ac] -= credit; + return rc; + } + } + } + + ctx->FIFO_credit[ac] -= credit; + + /* packets from SENDQ are fresh and they'd need header and have no MAC entry */ + commit_info.needs_hdr = 1; + commit_info.mac_entry = NULL; + commit_info.pkt_type = eWLFC_PKTTYPE_NEW; + + for (credit = 0; credit < ctx->FIFO_credit[ac];) { + commit_info.p = _dhd_wlfc_deque_sendq(ctx, ac, + &(commit_info.ac_fifo_credit_spent)); + if (commit_info.p == NULL) + break; + + rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info, + fcommit, commit_ctx); + + /* Bus commits may fail (e.g. flow control); abort after retries */ + if (rc == BCME_OK) { + if (commit_info.ac_fifo_credit_spent) { + credit++; + } + } + else { + bus_retry_count++; + if (bus_retry_count >= BUS_RETRIES) { + DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n")); + ctx->FIFO_credit[ac] -= credit; + return rc; + } + } + } + + ctx->FIFO_credit[ac] -= credit; + + /* If no credits were used, the queue is idle and can be re-used + Note that resv credits cannot be borrowed + */ + if (initial_credit_count == ctx->FIFO_credit[ac]) { + ac_available |= (1 << ac); + credit_count += ctx->FIFO_credit[ac]; + } + } + + /* We borrow only for AC_BE and only if no other traffic seen for DEFER_PERIOD + + Note that (ac_available & WLFC_AC_BE_TRAFFIC_ONLY) is done to: + a) ignore BC/MC for deferring borrow + b) ignore AC_BE being available along with other ACs + (this should happen only for pure BC/MC traffic) + + i.e. AC_VI, AC_VO, AC_BK all MUST be available (i.e. no traffic) and + we do not care if AC_BE and BC/MC are available or not + */ + if ((ac_available & WLFC_AC_BE_TRAFFIC_ONLY) == WLFC_AC_BE_TRAFFIC_ONLY) { + + if (ctx->allow_credit_borrow) { + ac = 1; /* Set ac to AC_BE and borrow credits */ + } + else { + int delta; + int curr_t = OSL_SYSUPTIME(); + + if (curr_t > ctx->borrow_defer_timestamp) + delta = curr_t - ctx->borrow_defer_timestamp; + else + delta = 0xffffffff + curr_t - ctx->borrow_defer_timestamp; + + if (delta >= WLFC_BORROW_DEFER_PERIOD_MS) { + /* Reset borrow but defer to next iteration (defensive borrowing) */ + ctx->allow_credit_borrow = TRUE; + ctx->borrow_defer_timestamp = 0; + } + return BCME_OK; + } + } + else { + /* If we have multiple AC traffic, turn off borrowing, mark time and bail out */ + ctx->allow_credit_borrow = FALSE; + ctx->borrow_defer_timestamp = OSL_SYSUPTIME(); + return BCME_OK; + } + + /* At this point, borrow all credits only for "ac" (which should be set above to AC_BE) + Generically use "ac" only in case we extend to all ACs in future + */ + for (; (credit_count > 0);) { + + commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac, + &(commit_info.ac_fifo_credit_spent), + &(commit_info.needs_hdr), + &(commit_info.mac_entry)); + if (commit_info.p == NULL) + break; + + commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED : + eWLFC_PKTTYPE_SUPPRESSED; + + rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info, + fcommit, commit_ctx); + + /* Bus commits may fail (e.g. flow control); abort after retries */ + if (rc == BCME_OK) { + if (commit_info.ac_fifo_credit_spent) { + (void) _dhd_wlfc_borrow_credit(ctx, ac_available, ac); + credit_count--; + } + } + else { + bus_retry_count++; + if (bus_retry_count >= BUS_RETRIES) { + DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n")); + return rc; + } + } + } + + /* packets from SENDQ are fresh and they'd need header and have no MAC entry */ + commit_info.needs_hdr = 1; + commit_info.mac_entry = NULL; + commit_info.pkt_type = eWLFC_PKTTYPE_NEW; + + for (; (credit_count > 0);) { + + commit_info.p = _dhd_wlfc_deque_sendq(ctx, ac, + &(commit_info.ac_fifo_credit_spent)); + if (commit_info.p == NULL) + break; + + rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info, + fcommit, commit_ctx); + + /* Bus commits may fail (e.g. flow control); abort after retries */ + if (rc == BCME_OK) { + if (commit_info.ac_fifo_credit_spent) { + (void) _dhd_wlfc_borrow_credit(ctx, ac_available, ac); + credit_count--; + } + } + else { + bus_retry_count++; + if (bus_retry_count >= BUS_RETRIES) { + DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n")); + return rc; + } + } + } + + return BCME_OK; +} + +static uint8 +dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t *dhdp, uint8* ea) +{ + wlfc_mac_descriptor_t* table = + ((athost_wl_status_info_t*)dhdp->wlfc_state)->destination_entries.nodes; + uint8 table_index; + + if (ea != NULL) { + for (table_index = 0; table_index < WLFC_MAC_DESC_TABLE_SIZE; table_index++) { + if ((memcmp(ea, &table[table_index].ea[0], ETHER_ADDR_LEN) == 0) && + table[table_index].occupied) + return table_index; + } + } + return WLFC_MAC_DESC_ID_INVALID; +} + +void +dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success) +{ + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + void* p; + int fifo_id; + + if (DHD_PKTTAG_SIGNALONLY(PKTTAG(txp))) { +#ifdef PROP_TXSTATUS_DEBUG + wlfc->stats.signal_only_pkts_freed++; +#endif + /* is this a signal-only packet? */ + PKTFREE(wlfc->osh, txp, TRUE); + return; + } + if (!success) { + WLFC_DBGMESG(("At: %s():%d, bus_complete() failure for %p, htod_tag:0x%08x\n", + __FUNCTION__, __LINE__, txp, DHD_PKTTAG_H2DTAG(PKTTAG(txp)))); + dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG + (PKTTAG(txp))), &p, 1); + + /* indicate failure and free the packet */ + dhd_txcomplete(dhd, txp, FALSE); + + /* return the credit, if necessary */ + if (DHD_PKTTAG_CREDITCHECK(PKTTAG(txp))) { + int lender, credit_returned = 0; /* Note that borrower is fifo_id */ + + fifo_id = DHD_PKTTAG_FIFO(PKTTAG(txp)); + + /* Return credits to highest priority lender first */ + for (lender = AC_COUNT; lender >= 0; lender--) { + if (wlfc->credits_borrowed[fifo_id][lender] > 0) { + wlfc->FIFO_credit[lender]++; + wlfc->credits_borrowed[fifo_id][lender]--; + credit_returned = 1; + break; + } + } + + if (!credit_returned) { + wlfc->FIFO_credit[fifo_id]++; + } + } + + PKTFREE(wlfc->osh, txp, TRUE); + } + return; +} + +/* Handle discard or suppress indication */ +static int +dhd_wlfc_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info) +{ + uint8 status_flag; + uint32 status; + int ret; + int remove_from_hanger = 1; + void* pktbuf; + uint8 fifo_id; + wlfc_mac_descriptor_t* entry = NULL; + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + + memcpy(&status, pkt_info, sizeof(uint32)); + status_flag = WL_TXSTATUS_GET_FLAGS(status); + wlfc->stats.txstatus_in++; + + if (status_flag == WLFC_CTL_PKTFLAG_DISCARD) { + wlfc->stats.pkt_freed++; + } + + else if (status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) { + wlfc->stats.d11_suppress++; + remove_from_hanger = 0; + } + + else if (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS) { + wlfc->stats.wl_suppress++; + remove_from_hanger = 0; + } + + else if (status_flag == WLFC_CTL_PKTFLAG_TOSSED_BYWLC) { + wlfc->stats.wlc_tossed_pkts++; + } + + ret = dhd_wlfc_hanger_poppkt(wlfc->hanger, + WLFC_PKTID_HSLOT_GET(status), &pktbuf, remove_from_hanger); + if (ret != BCME_OK) { + /* do something */ + return ret; + } + + if (!remove_from_hanger) { + /* this packet was suppressed */ + + entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf); + entry->generation = WLFC_PKTID_GEN(status); + } + +#ifdef PROP_TXSTATUS_DEBUG + { + uint32 new_t = OSL_SYSUPTIME(); + uint32 old_t; + uint32 delta; + old_t = ((wlfc_hanger_t*)(wlfc->hanger))->items[ + WLFC_PKTID_HSLOT_GET(status)].push_time; + + + wlfc->stats.latency_sample_count++; + if (new_t > old_t) + delta = new_t - old_t; + else + delta = 0xffffffff + new_t - old_t; + wlfc->stats.total_status_latency += delta; + wlfc->stats.latency_most_recent = delta; + + wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta; + if (wlfc->stats.idx_delta == sizeof(wlfc->stats.deltas)/sizeof(uint32)) + wlfc->stats.idx_delta = 0; + } +#endif /* PROP_TXSTATUS_DEBUG */ + + fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf)); + + /* pick up the implicit credit from this packet */ + if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) { + if (wlfc->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) { + + int lender, credit_returned = 0; /* Note that borrower is fifo_id */ + + /* Return credits to highest priority lender first */ + for (lender = AC_COUNT; lender >= 0; lender--) { + if (wlfc->credits_borrowed[fifo_id][lender] > 0) { + wlfc->FIFO_credit[lender]++; + wlfc->credits_borrowed[fifo_id][lender]--; + credit_returned = 1; + break; + } + } + + if (!credit_returned) { + wlfc->FIFO_credit[fifo_id]++; + } + } + } + else { + /* + if this packet did not count against FIFO credit, it must have + taken a requested_credit from the destination entry (for pspoll etc.) + */ + if (!entry) { + + entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf); + } + if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf))) + entry->requested_credit++; +#ifdef PROP_TXSTATUS_DEBUG + entry->dstncredit_acks++; +#endif + } + if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) || + (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) { + ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf); + if (ret != BCME_OK) { + /* delay q is full, drop this packet */ + dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(status), + &pktbuf, 1); + + /* indicate failure and free the packet */ + dhd_txcomplete(dhd, pktbuf, FALSE); + PKTFREE(wlfc->osh, pktbuf, TRUE); + } + } + else { + dhd_txcomplete(dhd, pktbuf, TRUE); + /* free the packet */ + PKTFREE(wlfc->osh, pktbuf, TRUE); + } + return BCME_OK; +} + +static int +dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8* credits) +{ + int i; + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + for (i = 0; i < WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK; i++) { +#ifdef PROP_TXSTATUS_DEBUG + wlfc->stats.fifo_credits_back[i] += credits[i]; +#endif + /* update FIFO credits */ + if (wlfc->proptxstatus_mode == WLFC_FCMODE_EXPLICIT_CREDIT) + { + int lender; /* Note that borrower is i */ + + /* Return credits to highest priority lender first */ + for (lender = AC_COUNT; (lender >= 0) && (credits[i] > 0); lender--) { + if (wlfc->credits_borrowed[i][lender] > 0) { + if (credits[i] >= wlfc->credits_borrowed[i][lender]) { + credits[i] -= wlfc->credits_borrowed[i][lender]; + wlfc->FIFO_credit[lender] += + wlfc->credits_borrowed[i][lender]; + wlfc->credits_borrowed[i][lender] = 0; + } + else { + wlfc->credits_borrowed[i][lender] -= credits[i]; + wlfc->FIFO_credit[lender] += credits[i]; + credits[i] = 0; + } + } + } + + /* If we have more credits left over, these must belong to the AC */ + if (credits[i] > 0) { + wlfc->FIFO_credit[i] += credits[i]; + } + } + } + + return BCME_OK; +} + +static int +dhd_wlfc_rssi_indicate(dhd_pub_t *dhd, uint8* rssi) +{ + (void)dhd; + (void)rssi; + return BCME_OK; +} + +static int +dhd_wlfc_mac_table_update(dhd_pub_t *dhd, uint8* value, uint8 type) +{ + int rc; + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + wlfc_mac_descriptor_t* table; + uint8 existing_index; + uint8 table_index; + uint8 ifid; + uint8* ea; + + WLFC_DBGMESG(("%s(), mac [%02x:%02x:%02x:%02x:%02x:%02x],%s,idx:%d,id:0x%02x\n", + __FUNCTION__, value[2], value[3], value[4], value[5], value[6], value[7], + ((type == WLFC_CTL_TYPE_MACDESC_ADD) ? "ADD":"DEL"), + WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]), value[0])); + + table = wlfc->destination_entries.nodes; + table_index = WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]); + ifid = value[1]; + ea = &value[2]; + + if (type == WLFC_CTL_TYPE_MACDESC_ADD) { + existing_index = dhd_wlfc_find_mac_desc_id_from_mac(dhd, &value[2]); + if (existing_index == WLFC_MAC_DESC_ID_INVALID) { + /* this MAC entry does not exist, create one */ + if (!table[table_index].occupied) { + table[table_index].mac_handle = value[0]; + rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index], + eWLFC_MAC_ENTRY_ACTION_ADD, ifid, + wlfc->destination_entries.interfaces[ifid].iftype, + ea); + } + else { + /* the space should have been empty, but it's not */ + wlfc->stats.mac_update_failed++; + } + } + else { + /* + there is an existing entry, move it to new index + if necessary. + */ + if (existing_index != table_index) { + /* if we already have an entry, free the old one */ + table[existing_index].occupied = 0; + table[existing_index].state = WLFC_STATE_CLOSE; + table[existing_index].requested_credit = 0; + table[existing_index].interface_id = 0; + } + } + } + if (type == WLFC_CTL_TYPE_MACDESC_DEL) { + if (table[table_index].occupied) { + rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index], + eWLFC_MAC_ENTRY_ACTION_DEL, ifid, + wlfc->destination_entries.interfaces[ifid].iftype, + ea); + } + else { + /* the space should have been occupied, but it's not */ + wlfc->stats.mac_update_failed++; + } + } + return BCME_OK; +} + +static int +dhd_wlfc_psmode_update(dhd_pub_t *dhd, uint8* value, uint8 type) +{ + /* Handle PS on/off indication */ + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + wlfc_mac_descriptor_t* table; + wlfc_mac_descriptor_t* desc; + uint8 mac_handle = value[0]; + int i; + + table = wlfc->destination_entries.nodes; + desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)]; + if (desc->occupied) { + /* a fresh PS mode should wipe old ps credits? */ + desc->requested_credit = 0; + if (type == WLFC_CTL_TYPE_MAC_OPEN) { + desc->state = WLFC_STATE_OPEN; + DHD_WLFC_CTRINC_MAC_OPEN(desc); + } + else { + desc->state = WLFC_STATE_CLOSE; + DHD_WLFC_CTRINC_MAC_CLOSE(desc); + /* + Indicate to firmware if there is any traffic pending. + */ + for (i = AC_BE; i < AC_COUNT; i++) { + _dhd_wlfc_traffic_pending_check(wlfc, desc, i); + } + } + } + else { + wlfc->stats.psmode_update_failed++; + } + return BCME_OK; +} + +static int +dhd_wlfc_interface_update(dhd_pub_t *dhd, uint8* value, uint8 type) +{ + /* Handle PS on/off indication */ + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + wlfc_mac_descriptor_t* table; + uint8 if_id = value[0]; + + if (if_id < WLFC_MAX_IFNUM) { + table = wlfc->destination_entries.interfaces; + if (table[if_id].occupied) { + if (type == WLFC_CTL_TYPE_INTERFACE_OPEN) { + table[if_id].state = WLFC_STATE_OPEN; + /* WLFC_DBGMESG(("INTERFACE[%d] OPEN\n", if_id)); */ + } + else { + table[if_id].state = WLFC_STATE_CLOSE; + /* WLFC_DBGMESG(("INTERFACE[%d] CLOSE\n", if_id)); */ + } + return BCME_OK; + } + } + wlfc->stats.interface_update_failed++; + + return BCME_OK; +} + +static int +dhd_wlfc_credit_request(dhd_pub_t *dhd, uint8* value) +{ + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + wlfc_mac_descriptor_t* table; + wlfc_mac_descriptor_t* desc; + uint8 mac_handle; + uint8 credit; + + table = wlfc->destination_entries.nodes; + mac_handle = value[1]; + credit = value[0]; + + desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)]; + if (desc->occupied) { + desc->requested_credit = credit; + + desc->ac_bitmap = value[2]; + } + else { + wlfc->stats.credit_request_failed++; + } + return BCME_OK; +} + +static int +dhd_wlfc_packet_request(dhd_pub_t *dhd, uint8* value) +{ + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + wlfc_mac_descriptor_t* table; + wlfc_mac_descriptor_t* desc; + uint8 mac_handle; + uint8 packet_count; + + table = wlfc->destination_entries.nodes; + mac_handle = value[1]; + packet_count = value[0]; + + desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)]; + if (desc->occupied) { + desc->requested_packet = packet_count; + + desc->ac_bitmap = value[2]; + } + else { + wlfc->stats.packet_request_failed++; + } + return BCME_OK; +} + +static int +dhd_wlfc_parse_header_info(dhd_pub_t *dhd, void* pktbuf, int tlv_hdr_len) +{ + uint8 type, len; + uint8* value; + uint8* tmpbuf; + uint16 remainder = tlv_hdr_len; + uint16 processed = 0; + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + tmpbuf = (uint8*)PKTDATA(dhd->osh, pktbuf); + if (remainder) { + while ((processed < (WLFC_MAX_PENDING_DATALEN * 2)) && (remainder > 0)) { + type = tmpbuf[processed]; + if (type == WLFC_CTL_TYPE_FILLER) { + remainder -= 1; + processed += 1; + continue; + } + + len = tmpbuf[processed + 1]; + value = &tmpbuf[processed + 2]; + + if (remainder < (2 + len)) + break; + + remainder -= 2 + len; + processed += 2 + len; + if (type == WLFC_CTL_TYPE_TXSTATUS) + dhd_wlfc_txstatus_update(dhd, value); + + else if (type == WLFC_CTL_TYPE_FIFO_CREDITBACK) + dhd_wlfc_fifocreditback_indicate(dhd, value); + + else if (type == WLFC_CTL_TYPE_RSSI) + dhd_wlfc_rssi_indicate(dhd, value); + + else if (type == WLFC_CTL_TYPE_MAC_REQUEST_CREDIT) + dhd_wlfc_credit_request(dhd, value); + + else if (type == WLFC_CTL_TYPE_MAC_REQUEST_PACKET) + dhd_wlfc_packet_request(dhd, value); + + else if ((type == WLFC_CTL_TYPE_MAC_OPEN) || + (type == WLFC_CTL_TYPE_MAC_CLOSE)) + dhd_wlfc_psmode_update(dhd, value, type); + + else if ((type == WLFC_CTL_TYPE_MACDESC_ADD) || + (type == WLFC_CTL_TYPE_MACDESC_DEL)) + dhd_wlfc_mac_table_update(dhd, value, type); + + else if ((type == WLFC_CTL_TYPE_INTERFACE_OPEN) || + (type == WLFC_CTL_TYPE_INTERFACE_CLOSE)) { + dhd_wlfc_interface_update(dhd, value, type); + } + } + if (remainder != 0) { + /* trouble..., something is not right */ + wlfc->stats.tlv_parse_failed++; + } + } + return BCME_OK; +} + +int +dhd_wlfc_init(dhd_pub_t *dhd) +{ + char iovbuf[12]; /* Room for "tlv" + '\0' + parameter */ + /* enable all signals & indicate host proptxstatus logic is active */ + uint32 tlv = dhd->wlfc_enabled? + WLFC_FLAGS_RSSI_SIGNALS | + WLFC_FLAGS_XONXOFF_SIGNALS | + WLFC_FLAGS_CREDIT_STATUS_SIGNALS | + WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE : 0; + + dhd->wlfc_state = NULL; + + /* + try to enable/disable signaling by sending "tlv" iovar. if that fails, + fallback to no flow control? Print a message for now. + */ + + /* enable proptxtstatus signaling by default */ + bcm_mkiovar("tlv", (char *)&tlv, 4, iovbuf, sizeof(iovbuf)); + if (dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0) < 0) { + DHD_ERROR(("dhd_wlfc_init(): failed to enable/disable bdcv2 tlv signaling\n")); + } + else { + /* + Leaving the message for now, it should be removed after a while; once + the tlv situation is stable. + */ + DHD_ERROR(("dhd_wlfc_init(): successfully %s bdcv2 tlv signaling, %d\n", + dhd->wlfc_enabled?"enabled":"disabled", tlv)); + } + return BCME_OK; +} + +int +dhd_wlfc_enable(dhd_pub_t *dhd) +{ + int i; + athost_wl_status_info_t* wlfc; + + if (!dhd->wlfc_enabled || dhd->wlfc_state) + return BCME_OK; + + /* allocate space to track txstatus propagated from firmware */ + dhd->wlfc_state = MALLOC(dhd->osh, sizeof(athost_wl_status_info_t)); + if (dhd->wlfc_state == NULL) + return BCME_NOMEM; + + /* initialize state space */ + wlfc = (athost_wl_status_info_t*)dhd->wlfc_state; + memset(wlfc, 0, sizeof(athost_wl_status_info_t)); + + /* remember osh & dhdp */ + wlfc->osh = dhd->osh; + wlfc->dhdp = dhd; + + wlfc->hanger = + dhd_wlfc_hanger_create(dhd->osh, WLFC_HANGER_MAXITEMS); + if (wlfc->hanger == NULL) { + MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t)); + dhd->wlfc_state = NULL; + return BCME_NOMEM; + } + + /* initialize all interfaces to accept traffic */ + for (i = 0; i < WLFC_MAX_IFNUM; i++) { + wlfc->hostif_flow_state[i] = OFF; + } + + /* + create the SENDQ containing + sub-queues for all AC precedences + 1 for bc/mc traffic + */ + pktq_init(&wlfc->SENDQ, (AC_COUNT + 1), WLFC_SENDQ_LEN); + + wlfc->destination_entries.other.state = WLFC_STATE_OPEN; + /* bc/mc FIFO is always open [credit aside], i.e. b[5] */ + wlfc->destination_entries.other.ac_bitmap = 0x1f; + wlfc->destination_entries.other.interface_id = 0; + + wlfc->proptxstatus_mode = WLFC_FCMODE_EXPLICIT_CREDIT; + + wlfc->allow_credit_borrow = TRUE; + wlfc->borrow_defer_timestamp = 0; + + return BCME_OK; +} + +/* release all packet resources */ +void +dhd_wlfc_cleanup(dhd_pub_t *dhd) +{ + int i; + int total_entries; + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + wlfc_mac_descriptor_t* table; + wlfc_hanger_t* h; + + if (dhd->wlfc_state == NULL) + return; + + total_entries = sizeof(wlfc->destination_entries)/sizeof(wlfc_mac_descriptor_t); + /* search all entries, include nodes as well as interfaces */ + table = (wlfc_mac_descriptor_t*)&wlfc->destination_entries; + + for (i = 0; i < total_entries; i++) { + if (table[i].occupied) { + if (table[i].psq.len) { + WLFC_DBGMESG(("%s(): DELAYQ[%d].len = %d\n", + __FUNCTION__, i, table[i].psq.len)); + /* release packets held in DELAYQ */ + pktq_flush(wlfc->osh, &table[i].psq, TRUE, NULL, 0); + } + table[i].occupied = 0; + } + } + /* release packets held in SENDQ */ + if (wlfc->SENDQ.len) + pktq_flush(wlfc->osh, &wlfc->SENDQ, TRUE, NULL, 0); + /* any in the hanger? */ + h = (wlfc_hanger_t*)wlfc->hanger; + for (i = 0; i < h->max_items; i++) { + if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) { + PKTFREE(wlfc->osh, h->items[i].pkt, TRUE); + } + } + return; +} + +void +dhd_wlfc_deinit(dhd_pub_t *dhd) +{ + /* cleanup all psq related resources */ + athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*) + dhd->wlfc_state; + + if (dhd->wlfc_state == NULL) + return; + +#ifdef PROP_TXSTATUS_DEBUG + { + int i; + wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger; + for (i = 0; i < h->max_items; i++) { + if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) { + WLFC_DBGMESG(("%s() pkt[%d] = 0x%p, FIFO_credit_used:%d\n", + __FUNCTION__, i, h->items[i].pkt, + DHD_PKTTAG_CREDITCHECK(PKTTAG(h->items[i].pkt)))); + } + } + } +#endif + /* delete hanger */ + dhd_wlfc_hanger_delete(dhd->osh, wlfc->hanger); + + /* free top structure */ + MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t)); + dhd->wlfc_state = NULL; + return; +} +#endif /* PROP_TXSTATUS */ + +void +dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf) +{ + bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid); +#ifdef PROP_TXSTATUS + if (dhdp->wlfc_state) + dhd_wlfc_dump(dhdp, strbuf); +#endif +} + +void +dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf) +{ +#ifdef BDC + struct bdc_header *h; +#endif /* BDC */ + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + +#ifdef BDC + /* Push BDC header used to convey priority for buses that don't */ + + PKTPUSH(dhd->osh, pktbuf, BDC_HEADER_LEN); + + h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf); + + h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT); + if (PKTSUMNEEDED(pktbuf)) + h->flags |= BDC_FLAG_SUM_NEEDED; + + + h->priority = (PKTPRIO(pktbuf) & BDC_PRIORITY_MASK); + h->flags2 = 0; + h->dataOffset = 0; +#endif /* BDC */ + BDC_SET_IF_IDX(h, ifidx); +} + +int +dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf) +{ +#ifdef BDC + struct bdc_header *h; +#endif + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + +#ifdef BDC + /* Pop BDC header used to convey priority for buses that don't */ + + if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) { + DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__, + PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN)); + return BCME_ERROR; + } + + h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf); + + if ((*ifidx = BDC_GET_IF_IDX(h)) >= DHD_MAX_IFS) { + DHD_ERROR(("%s: rx data ifnum out of range (%d)\n", + __FUNCTION__, *ifidx)); + return BCME_ERROR; + } + + if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != BDC_PROTO_VER) { + DHD_ERROR(("%s: non-BDC packet received, flags = 0x%x\n", + dhd_ifname(dhd, *ifidx), h->flags)); + if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) == BDC_PROTO_VER_1) + h->dataOffset = 0; + else + return BCME_ERROR; + } + + if (h->flags & BDC_FLAG_SUM_GOOD) { + DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n", + dhd_ifname(dhd, *ifidx), h->flags)); + PKTSETSUMGOOD(pktbuf, TRUE); + } + + PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK)); + PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN); +#endif /* BDC */ + + if (PKTLEN(dhd->osh, pktbuf) < (uint32) (h->dataOffset << 2)) { + DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__, + PKTLEN(dhd->osh, pktbuf), (h->dataOffset * 4))); + return BCME_ERROR; + } + +#ifdef PROP_TXSTATUS + if (dhd->wlfc_state && + ((athost_wl_status_info_t*)dhd->wlfc_state)->proptxstatus_mode + != WLFC_FCMODE_NONE && + (!DHD_PKTTAG_PKTDIR(PKTTAG(pktbuf)))) { + /* + - parse txstatus only for packets that came from the firmware + */ + dhd_os_wlfc_block(dhd); + dhd_wlfc_parse_header_info(dhd, pktbuf, (h->dataOffset << 2)); + ((athost_wl_status_info_t*)dhd->wlfc_state)->stats.dhd_hdrpulls++; + dhd_wlfc_commit_packets(dhd->wlfc_state, (f_commitpkt_t)dhd_bus_txdata, + (void *)dhd->bus); + dhd_os_wlfc_unblock(dhd); + } +#endif /* PROP_TXSTATUS */ + PKTPULL(dhd->osh, pktbuf, (h->dataOffset << 2)); + return 0; +} + +int +dhd_prot_attach(dhd_pub_t *dhd) +{ + dhd_prot_t *cdc; + + if (!(cdc = (dhd_prot_t *)DHD_OS_PREALLOC(dhd->osh, DHD_PREALLOC_PROT, + sizeof(dhd_prot_t)))) { + DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); + goto fail; + } + memset(cdc, 0, sizeof(dhd_prot_t)); + + /* ensure that the msg buf directly follows the cdc msg struct */ + if ((uintptr)(&cdc->msg + 1) != (uintptr)cdc->buf) { + DHD_ERROR(("dhd_prot_t is not correctly defined\n")); + goto fail; + } + + dhd->prot = cdc; +#ifdef BDC + dhd->hdrlen += BDC_HEADER_LEN; +#endif + dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN; + return 0; + +fail: +#ifndef CONFIG_DHD_USE_STATIC_BUF + if (cdc != NULL) + MFREE(dhd->osh, cdc, sizeof(dhd_prot_t)); +#endif /* CONFIG_DHD_USE_STATIC_BUF */ + return BCME_NOMEM; +} + +/* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */ +void +dhd_prot_detach(dhd_pub_t *dhd) +{ +#ifdef PROP_TXSTATUS + dhd_wlfc_deinit(dhd); +#endif +#ifndef CONFIG_DHD_USE_STATIC_BUF + MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t)); +#endif /* CONFIG_DHD_USE_STATIC_BUF */ + dhd->prot = NULL; +} + +void +dhd_prot_dstats(dhd_pub_t *dhd) +{ + /* No stats from dongle added yet, copy bus stats */ + dhd->dstats.tx_packets = dhd->tx_packets; + dhd->dstats.tx_errors = dhd->tx_errors; + dhd->dstats.rx_packets = dhd->rx_packets; + dhd->dstats.rx_errors = dhd->rx_errors; + dhd->dstats.rx_dropped = dhd->rx_dropped; + dhd->dstats.multicast = dhd->rx_multicast; + return; +} + +int +dhd_prot_init(dhd_pub_t *dhd) +{ + int ret = 0; + wlc_rev_info_t revinfo; + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + + /* Get the device rev info */ + memset(&revinfo, 0, sizeof(revinfo)); + ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_REVINFO, &revinfo, sizeof(revinfo), FALSE, 0); + if (ret < 0) + goto done; + + +#ifdef PROP_TXSTATUS + ret = dhd_wlfc_init(dhd); +#endif + +#if defined(WL_CFG80211) + if (dhd_download_fw_on_driverload) +#endif /* defined(WL_CFG80211) */ + ret = dhd_preinit_ioctls(dhd); + + /* Always assumes wl for now */ + dhd->iswl = TRUE; + +done: + return ret; +} + +void +dhd_prot_stop(dhd_pub_t *dhd) +{ + /* Nothing to do for CDC */ +} diff --git a/drivers/net/wireless/bcmdhd/dhd_cfg80211.c b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c new file mode 100644 index 0000000000000..bc1be94802b4a --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c @@ -0,0 +1,655 @@ +/* + * Linux cfg80211 driver - Dongle Host Driver (DHD) related + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfg80211.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $ + */ + +#include + +#include +#include +#include +#include +extern struct wl_priv *wlcfg_drv_priv; +static int dhd_dongle_up = FALSE; + +#include +#include +#include +#include +#include + +static s32 wl_dongle_up(struct net_device *ndev, u32 up); + +/** + * Function implementations + */ + +s32 dhd_cfg80211_init(struct wl_priv *wl) +{ + dhd_dongle_up = FALSE; + return 0; +} + +s32 dhd_cfg80211_deinit(struct wl_priv *wl) +{ + dhd_dongle_up = FALSE; + return 0; +} + +s32 dhd_cfg80211_get_opmode(struct wl_priv *wl) +{ + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + return dhd->op_mode; +} + +s32 dhd_cfg80211_down(struct wl_priv *wl) +{ + dhd_dongle_up = FALSE; + return 0; +} + +s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val) +{ + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + int bcn_timeout = DHD_BEACON_TIMEOUT_HIGH; + char iovbuf[30]; + + dhd->op_mode |= val; + WL_ERR(("Set : op_mode=%d\n", dhd->op_mode)); + +#ifdef ARP_OFFLOAD_SUPPORT + /* IF P2P is enabled, disable arpoe */ + dhd_arp_offload_set(dhd, 0); + dhd_arp_offload_enable(dhd, false); +#endif /* ARP_OFFLOAD_SUPPORT */ + + dhd_os_set_packet_filter(dhd, 0); + + /* Setup timeout if Beacons are lost and roam is off to report link down */ + bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + + + return 0; +} + +s32 dhd_cfg80211_clean_p2p_info(struct wl_priv *wl) +{ + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + int bcn_timeout = DHD_BEACON_TIMEOUT_NORMAL; + char iovbuf[30]; + + dhd->op_mode &= ~CONCURENT_MASK; + WL_ERR(("Clean : op_mode=%d\n", dhd->op_mode)); + +#ifdef ARP_OFFLOAD_SUPPORT + /* IF P2P is disabled, enable arpoe back for STA mode. */ + dhd_arp_offload_set(dhd, dhd_arp_mode); + dhd_arp_offload_enable(dhd, true); +#endif /* ARP_OFFLOAD_SUPPORT */ + + dhd_os_set_packet_filter(dhd, 1); + + /* Setup timeout if Beacons are lost and roam is off to report link down */ + bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + + return 0; +} + +static s32 wl_dongle_up(struct net_device *ndev, u32 up) +{ + s32 err = 0; + + err = wldev_ioctl(ndev, WLC_UP, &up, sizeof(up), true); + if (unlikely(err)) { + WL_ERR(("WLC_UP error (%d)\n", err)); + } + return err; +} + +s32 dhd_config_dongle(struct wl_priv *wl, bool need_lock) +{ +#ifndef DHD_SDALIGN +#define DHD_SDALIGN 32 +#endif + struct net_device *ndev; + s32 err = 0; + + WL_TRACE(("In\n")); + if (dhd_dongle_up) { + WL_ERR(("Dongle is already up\n")); + return err; + } + + ndev = wl_to_prmry_ndev(wl); + + if (need_lock) + rtnl_lock(); + + err = wl_dongle_up(ndev, 0); + if (unlikely(err)) { + WL_ERR(("wl_dongle_up failed\n")); + goto default_conf_out; + } + dhd_dongle_up = true; + +default_conf_out: + if (need_lock) + rtnl_unlock(); + return err; + +} + + +/* TODO: clean up the BT-Coex code, it still have some legacy ioctl/iovar functions */ +#define COEX_DHCP + +#if defined(COEX_DHCP) + +/* use New SCO/eSCO smart YG suppression */ +#define BT_DHCP_eSCO_FIX +/* this flag boost wifi pkt priority to max, caution: -not fair to sco */ +#define BT_DHCP_USE_FLAGS +/* T1 start SCO/ESCo priority suppression */ +#define BT_DHCP_OPPR_WIN_TIME 2500 +/* T2 turn off SCO/SCO supperesion is (timeout) */ +#define BT_DHCP_FLAG_FORCE_TIME 5500 + +enum wl_cfg80211_btcoex_status { + BT_DHCP_IDLE, + BT_DHCP_START, + BT_DHCP_OPPR_WIN, + BT_DHCP_FLAG_FORCE_TIMEOUT +}; + +/* + * get named driver variable to uint register value and return error indication + * calling example: dev_wlc_intvar_get_reg(dev, "btc_params",66, ®_value) + */ +static int +dev_wlc_intvar_get_reg(struct net_device *dev, char *name, + uint reg, int *retval) +{ + union { + char buf[WLC_IOCTL_SMLEN]; + int val; + } var; + int error; + + bcm_mkiovar(name, (char *)(®), sizeof(reg), + (char *)(&var), sizeof(var.buf)); + error = wldev_ioctl(dev, WLC_GET_VAR, (char *)(&var), sizeof(var.buf), false); + + *retval = dtoh32(var.val); + return (error); +} + +static int +dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) + char ioctlbuf_local[1024]; +#else + static char ioctlbuf_local[1024]; +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */ + + bcm_mkiovar(name, buf, len, ioctlbuf_local, sizeof(ioctlbuf_local)); + + return (wldev_ioctl(dev, WLC_SET_VAR, ioctlbuf_local, sizeof(ioctlbuf_local), true)); +} +/* +get named driver variable to uint register value and return error indication +calling example: dev_wlc_intvar_set_reg(dev, "btc_params",66, value) +*/ +static int +dev_wlc_intvar_set_reg(struct net_device *dev, char *name, char *addr, char * val) +{ + char reg_addr[8]; + + memset(reg_addr, 0, sizeof(reg_addr)); + memcpy((char *)®_addr[0], (char *)addr, 4); + memcpy((char *)®_addr[4], (char *)val, 4); + + return (dev_wlc_bufvar_set(dev, name, (char *)®_addr[0], sizeof(reg_addr))); +} + +static bool btcoex_is_sco_active(struct net_device *dev) +{ + int ioc_res = 0; + bool res = FALSE; + int sco_id_cnt = 0; + int param27; + int i; + + for (i = 0; i < 12; i++) { + + ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 27, ¶m27); + + WL_TRACE(("%s, sample[%d], btc params: 27:%x\n", + __FUNCTION__, i, param27)); + + if (ioc_res < 0) { + WL_ERR(("%s ioc read btc params error\n", __FUNCTION__)); + break; + } + + if ((param27 & 0x6) == 2) { /* count both sco & esco */ + sco_id_cnt++; + } + + if (sco_id_cnt > 2) { + WL_TRACE(("%s, sco/esco detected, pkt id_cnt:%d samples:%d\n", + __FUNCTION__, sco_id_cnt, i)); + res = TRUE; + break; + } + + msleep(5); + } + + return res; +} + +#if defined(BT_DHCP_eSCO_FIX) +/* Enhanced BT COEX settings for eSCO compatibility during DHCP window */ +static int set_btc_esco_params(struct net_device *dev, bool trump_sco) +{ + static bool saved_status = FALSE; + + char buf_reg50va_dhcp_on[8] = + { 50, 00, 00, 00, 0x22, 0x80, 0x00, 0x00 }; + char buf_reg51va_dhcp_on[8] = + { 51, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg64va_dhcp_on[8] = + { 64, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg65va_dhcp_on[8] = + { 65, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg71va_dhcp_on[8] = + { 71, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + uint32 regaddr; + static uint32 saved_reg50; + static uint32 saved_reg51; + static uint32 saved_reg64; + static uint32 saved_reg65; + static uint32 saved_reg71; + + if (trump_sco) { + /* this should reduce eSCO agressive retransmit + * w/o breaking it + */ + + /* 1st save current */ + WL_TRACE(("Do new SCO/eSCO coex algo {save &" + "override}\n")); + if ((!dev_wlc_intvar_get_reg(dev, "btc_params", 50, &saved_reg50)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 51, &saved_reg51)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 64, &saved_reg64)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 65, &saved_reg65)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 71, &saved_reg71))) { + saved_status = TRUE; + WL_TRACE(("%s saved bt_params[50,51,64,65,71]:" + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + __FUNCTION__, saved_reg50, saved_reg51, + saved_reg64, saved_reg65, saved_reg71)); + } else { + WL_ERR((":%s: save btc_params failed\n", + __FUNCTION__)); + saved_status = FALSE; + return -1; + } + + WL_TRACE(("override with [50,51,64,65,71]:" + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + *(u32 *)(buf_reg50va_dhcp_on+4), + *(u32 *)(buf_reg51va_dhcp_on+4), + *(u32 *)(buf_reg64va_dhcp_on+4), + *(u32 *)(buf_reg65va_dhcp_on+4), + *(u32 *)(buf_reg71va_dhcp_on+4))); + + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg50va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg51va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg64va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg65va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg71va_dhcp_on[0], 8); + + saved_status = TRUE; + } else if (saved_status) { + /* restore previously saved bt params */ + WL_TRACE(("Do new SCO/eSCO coex algo {save &" + "override}\n")); + + regaddr = 50; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg50); + regaddr = 51; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg51); + regaddr = 64; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg64); + regaddr = 65; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg65); + regaddr = 71; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg71); + + WL_TRACE(("restore bt_params[50,51,64,65,71]:" + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + saved_reg50, saved_reg51, saved_reg64, + saved_reg65, saved_reg71)); + + saved_status = FALSE; + } else { + WL_ERR((":%s att to restore not saved BTCOEX params\n", + __FUNCTION__)); + return -1; + } + return 0; +} +#endif /* BT_DHCP_eSCO_FIX */ + +static void +wl_cfg80211_bt_setflag(struct net_device *dev, bool set) +{ +#if defined(BT_DHCP_USE_FLAGS) + char buf_flag7_dhcp_on[8] = { 7, 00, 00, 00, 0x1, 0x0, 0x00, 0x00 }; + char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; +#endif + + +#if defined(BT_DHCP_eSCO_FIX) + /* set = 1, save & turn on 0 - off & restore prev settings */ + set_btc_esco_params(dev, set); +#endif + +#if defined(BT_DHCP_USE_FLAGS) + WL_TRACE(("WI-FI priority boost via bt flags, set:%d\n", set)); + if (set == TRUE) + /* Forcing bt_flag7 */ + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_dhcp_on[0], + sizeof(buf_flag7_dhcp_on)); + else + /* Restoring default bt flag7 */ + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_default[0], + sizeof(buf_flag7_default)); +#endif +} + +static void wl_cfg80211_bt_timerfunc(ulong data) +{ + struct btcoex_info *bt_local = (struct btcoex_info *)data; + WL_TRACE(("%s\n", __FUNCTION__)); + bt_local->timer_on = 0; + schedule_work(&bt_local->work); +} + +static void wl_cfg80211_bt_handler(struct work_struct *work) +{ + struct btcoex_info *btcx_inf; + + btcx_inf = container_of(work, struct btcoex_info, work); + + if (btcx_inf->timer_on) { + btcx_inf->timer_on = 0; + del_timer_sync(&btcx_inf->timer); + } + + switch (btcx_inf->bt_state) { + case BT_DHCP_START: + /* DHCP started + * provide OPPORTUNITY window to get DHCP address + */ + WL_TRACE(("%s bt_dhcp stm: started \n", + __FUNCTION__)); + btcx_inf->bt_state = BT_DHCP_OPPR_WIN; + mod_timer(&btcx_inf->timer, + jiffies + BT_DHCP_OPPR_WIN_TIME*HZ/1000); + btcx_inf->timer_on = 1; + break; + + case BT_DHCP_OPPR_WIN: + if (btcx_inf->dhcp_done) { + WL_TRACE(("%s DHCP Done before T1 expiration\n", + __FUNCTION__)); + goto btc_coex_idle; + } + + /* DHCP is not over yet, start lowering BT priority + * enforce btc_params + flags if necessary + */ + WL_TRACE(("%s DHCP T1:%d expired\n", __FUNCTION__, + BT_DHCP_OPPR_WIN_TIME)); + if (btcx_inf->dev) + wl_cfg80211_bt_setflag(btcx_inf->dev, TRUE); + btcx_inf->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT; + mod_timer(&btcx_inf->timer, + jiffies + BT_DHCP_FLAG_FORCE_TIME*HZ/1000); + btcx_inf->timer_on = 1; + break; + + case BT_DHCP_FLAG_FORCE_TIMEOUT: + if (btcx_inf->dhcp_done) { + WL_TRACE(("%s DHCP Done before T2 expiration\n", + __FUNCTION__)); + } else { + /* Noo dhcp during T1+T2, restore BT priority */ + WL_TRACE(("%s DHCP wait interval T2:%d" + "msec expired\n", __FUNCTION__, + BT_DHCP_FLAG_FORCE_TIME)); + } + + /* Restoring default bt priority */ + if (btcx_inf->dev) + wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE); +btc_coex_idle: + btcx_inf->bt_state = BT_DHCP_IDLE; + btcx_inf->timer_on = 0; + break; + + default: + WL_ERR(("%s error g_status=%d !!!\n", __FUNCTION__, + btcx_inf->bt_state)); + if (btcx_inf->dev) + wl_cfg80211_bt_setflag(btcx_inf->dev, FALSE); + btcx_inf->bt_state = BT_DHCP_IDLE; + btcx_inf->timer_on = 0; + break; + } + + net_os_wake_unlock(btcx_inf->dev); +} + +int wl_cfg80211_btcoex_init(struct wl_priv *wl) +{ + struct btcoex_info *btco_inf = NULL; + + btco_inf = kmalloc(sizeof(struct btcoex_info), GFP_KERNEL); + if (!btco_inf) + return -ENOMEM; + + btco_inf->bt_state = BT_DHCP_IDLE; + btco_inf->ts_dhcp_start = 0; + btco_inf->ts_dhcp_ok = 0; + /* Set up timer for BT */ + btco_inf->timer_ms = 10; + init_timer(&btco_inf->timer); + btco_inf->timer.data = (ulong)btco_inf; + btco_inf->timer.function = wl_cfg80211_bt_timerfunc; + + btco_inf->dev = wl->wdev->netdev; + + INIT_WORK(&btco_inf->work, wl_cfg80211_bt_handler); + + wl->btcoex_info = btco_inf; + return 0; +} + +void wl_cfg80211_btcoex_deinit(struct wl_priv *wl) +{ + if (!wl->btcoex_info) + return; + + if (!wl->btcoex_info->timer_on) { + wl->btcoex_info->timer_on = 0; + del_timer_sync(&wl->btcoex_info->timer); + } + + cancel_work_sync(&wl->btcoex_info->work); + + kfree(wl->btcoex_info); + wl->btcoex_info = NULL; +} + +int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command) +{ + + struct wl_priv *wl = wlcfg_drv_priv; + char powermode_val = 0; + char buf_reg66va_dhcp_on[8] = { 66, 00, 00, 00, 0x10, 0x27, 0x00, 0x00 }; + char buf_reg41va_dhcp_on[8] = { 41, 00, 00, 00, 0x33, 0x00, 0x00, 0x00 }; + char buf_reg68va_dhcp_on[8] = { 68, 00, 00, 00, 0x90, 0x01, 0x00, 0x00 }; + + uint32 regaddr; + static uint32 saved_reg66; + static uint32 saved_reg41; + static uint32 saved_reg68; + static bool saved_status = FALSE; + +#ifdef COEX_DHCP + char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; + struct btcoex_info *btco_inf = wl->btcoex_info; +#endif /* COEX_DHCP */ + + /* Figure out powermode 1 or o command */ + strncpy((char *)&powermode_val, command + strlen("BTCOEXMODE") +1, 1); + + if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) { + + WL_TRACE(("%s: DHCP session starts\n", __FUNCTION__)); + + /* Retrieve and saved orig regs value */ + if ((saved_status == FALSE) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 66, &saved_reg66)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 41, &saved_reg41)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 68, &saved_reg68))) { + saved_status = TRUE; + WL_TRACE(("Saved 0x%x 0x%x 0x%x\n", + saved_reg66, saved_reg41, saved_reg68)); + + /* Disable PM mode during dhpc session */ + + /* Disable PM mode during dhpc session */ +#ifdef COEX_DHCP + /* Start BT timer only for SCO connection */ + if (btcoex_is_sco_active(dev)) { + /* btc_params 66 */ + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg66va_dhcp_on[0], + sizeof(buf_reg66va_dhcp_on)); + /* btc_params 41 0x33 */ + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg41va_dhcp_on[0], + sizeof(buf_reg41va_dhcp_on)); + /* btc_params 68 0x190 */ + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg68va_dhcp_on[0], + sizeof(buf_reg68va_dhcp_on)); + saved_status = TRUE; + + btco_inf->bt_state = BT_DHCP_START; + btco_inf->timer_on = 1; + mod_timer(&btco_inf->timer, btco_inf->timer.expires); + WL_TRACE(("%s enable BT DHCP Timer\n", + __FUNCTION__)); + } +#endif /* COEX_DHCP */ + } + else if (saved_status == TRUE) { + WL_ERR(("%s was called w/o DHCP OFF. Continue\n", __FUNCTION__)); + } + } + else if (strnicmp((char *)&powermode_val, "2", strlen("2")) == 0) { + + + /* Restoring PM mode */ + +#ifdef COEX_DHCP + /* Stop any bt timer because DHCP session is done */ + WL_TRACE(("%s disable BT DHCP Timer\n", __FUNCTION__)); + if (btco_inf->timer_on) { + btco_inf->timer_on = 0; + del_timer_sync(&btco_inf->timer); + + if (btco_inf->bt_state != BT_DHCP_IDLE) { + /* need to restore original btc flags & extra btc params */ + WL_TRACE(("%s bt->bt_state:%d\n", + __FUNCTION__, btco_inf->bt_state)); + /* wake up btcoex thread to restore btlags+params */ + schedule_work(&btco_inf->work); + } + } + + /* Restoring btc_flag paramter anyway */ + if (saved_status == TRUE) + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_default[0], sizeof(buf_flag7_default)); +#endif /* COEX_DHCP */ + + /* Restore original values */ + if (saved_status == TRUE) { + regaddr = 66; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg66); + regaddr = 41; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg41); + regaddr = 68; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg68); + + WL_TRACE(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n", + saved_reg66, saved_reg41, saved_reg68)); + } + saved_status = FALSE; + + } + else { + WL_ERR(("%s Unkwown yet power setting, ignored\n", + __FUNCTION__)); + } + + snprintf(command, 3, "OK"); + + return (strlen("OK")); +} +#endif diff --git a/drivers/net/wireless/bcmdhd/dhd_cfg80211.h b/drivers/net/wireless/bcmdhd/dhd_cfg80211.h new file mode 100644 index 0000000000000..ced46dbdb96c2 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_cfg80211.h @@ -0,0 +1,45 @@ +/* + * Linux cfg80211 driver - Dongle Host Driver (DHD) related + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfg80211.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $ + */ + + +#ifndef __DHD_CFG80211__ +#define __DHD_CFG80211__ + +#include +#include + +s32 dhd_cfg80211_init(struct wl_priv *wl); +s32 dhd_cfg80211_deinit(struct wl_priv *wl); +s32 dhd_cfg80211_get_opmode(struct wl_priv *wl); +s32 dhd_cfg80211_down(struct wl_priv *wl); +s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val); +s32 dhd_cfg80211_clean_p2p_info(struct wl_priv *wl); +s32 dhd_config_dongle(struct wl_priv *wl, bool need_lock); + +int wl_cfg80211_btcoex_init(struct wl_priv *wl); +void wl_cfg80211_btcoex_deinit(struct wl_priv *wl); + +#endif /* __DHD_CFG80211__ */ diff --git a/drivers/net/wireless/bcmdhd/dhd_common.c b/drivers/net/wireless/bcmdhd/dhd_common.c new file mode 100644 index 0000000000000..d5af27f40b76a --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_common.c @@ -0,0 +1,2451 @@ +/* + * Broadcom Dongle Host Driver (DHD), common DHD core. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_common.c 331276 2012-05-04 08:05:57Z $ + */ +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#ifdef WL_CFG80211 +#include +#endif +#include +#include +#ifdef SET_RANDOM_MAC_SOFTAP +#include +#include +#endif + +#ifdef PROP_TXSTATUS +#include +#include +#endif + + +#ifdef WLMEDIA_HTSF +extern void htsf_update(struct dhd_info *dhd, void *data); +#endif +int dhd_msg_level = DHD_ERROR_VAL; + + +#include + +char fw_path[MOD_PARAM_PATHLEN]; +char nv_path[MOD_PARAM_PATHLEN]; + +#ifdef SOFTAP +char fw_path2[MOD_PARAM_PATHLEN]; +extern bool softap_enabled; +#endif + +/* Last connection success/failure status */ +uint32 dhd_conn_event; +uint32 dhd_conn_status; +uint32 dhd_conn_reason; + +#define htod32(i) i +#define htod16(i) i +#define dtoh32(i) i +#define dtoh16(i) i +extern int dhd_iscan_request(void * dhdp, uint16 action); +extern void dhd_ind_scan_confirm(void *h, bool status); +extern int dhd_iscan_in_progress(void *h); +void dhd_iscan_lock(void); +void dhd_iscan_unlock(void); +extern int dhd_change_mtu(dhd_pub_t *dhd, int new_mtu, int ifidx); +bool ap_cfg_running = FALSE; +bool ap_fw_loaded = FALSE; + + +#ifdef DHD_DEBUG +const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR "\nCompiled on " + __DATE__ " at " __TIME__; +#else +const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR; +#endif + +void dhd_set_timer(void *bus, uint wdtick); + +/* IOVar table */ +enum { + IOV_VERSION = 1, + IOV_MSGLEVEL, + IOV_BCMERRORSTR, + IOV_BCMERROR, + IOV_WDTICK, + IOV_DUMP, + IOV_CLEARCOUNTS, + IOV_LOGDUMP, + IOV_LOGCAL, + IOV_LOGSTAMP, + IOV_GPIOOB, + IOV_IOCTLTIMEOUT, + IOV_HCI_CMD, /* HCI command */ + IOV_HCI_ACL_DATA, /* HCI data packet */ +#if defined(DHD_DEBUG) + IOV_CONS, + IOV_DCONSOLE_POLL, +#endif /* defined(DHD_DEBUG) */ +#ifdef PROP_TXSTATUS + IOV_PROPTXSTATUS_ENABLE, + IOV_PROPTXSTATUS_MODE, +#endif + IOV_BUS_TYPE, +#ifdef WLMEDIA_HTSF + IOV_WLPKTDLYSTAT_SZ, +#endif + IOV_CHANGEMTU, + IOV_LAST +}; + +const bcm_iovar_t dhd_iovars[] = { + {"version", IOV_VERSION, 0, IOVT_BUFFER, sizeof(dhd_version) }, +#ifdef DHD_DEBUG + {"msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 }, +#endif /* DHD_DEBUG */ + {"bcmerrorstr", IOV_BCMERRORSTR, 0, IOVT_BUFFER, BCME_STRLEN }, + {"bcmerror", IOV_BCMERROR, 0, IOVT_INT8, 0 }, + {"wdtick", IOV_WDTICK, 0, IOVT_UINT32, 0 }, + {"dump", IOV_DUMP, 0, IOVT_BUFFER, DHD_IOCTL_MAXLEN }, +#ifdef DHD_DEBUG + {"cons", IOV_CONS, 0, IOVT_BUFFER, 0 }, + {"dconpoll", IOV_DCONSOLE_POLL, 0, IOVT_UINT32, 0 }, +#endif + {"clearcounts", IOV_CLEARCOUNTS, 0, IOVT_VOID, 0 }, + {"gpioob", IOV_GPIOOB, 0, IOVT_UINT32, 0 }, + {"ioctl_timeout", IOV_IOCTLTIMEOUT, 0, IOVT_UINT32, 0 }, + {"HCI_cmd", IOV_HCI_CMD, 0, IOVT_BUFFER, 0}, + {"HCI_ACL_data", IOV_HCI_ACL_DATA, 0, IOVT_BUFFER, 0}, +#ifdef PROP_TXSTATUS + {"proptx", IOV_PROPTXSTATUS_ENABLE, 0, IOVT_UINT32, 0 }, + /* + set the proptxtstatus operation mode: + 0 - Do not do any proptxtstatus flow control + 1 - Use implied credit from a packet status + 2 - Use explicit credit + */ + {"ptxmode", IOV_PROPTXSTATUS_MODE, 0, IOVT_UINT32, 0 }, +#endif + {"bustype", IOV_BUS_TYPE, 0, IOVT_UINT32, 0}, +#ifdef WLMEDIA_HTSF + {"pktdlystatsz", IOV_WLPKTDLYSTAT_SZ, 0, IOVT_UINT8, 0 }, +#endif + {"changemtu", IOV_CHANGEMTU, 0, IOVT_UINT32, 0 }, + {NULL, 0, 0, 0, 0 } +}; + +struct dhd_cmn * +dhd_common_init(osl_t *osh) +{ + dhd_cmn_t *cmn; + + /* Init global variables at run-time, not as part of the declaration. + * This is required to support init/de-init of the driver. Initialization + * of globals as part of the declaration results in non-deterministic + * behavior since the value of the globals may be different on the + * first time that the driver is initialized vs subsequent initializations. + */ + /* Allocate private bus interface state */ + if (!(cmn = MALLOC(osh, sizeof(dhd_cmn_t)))) { + DHD_ERROR(("%s: MALLOC failed\n", __FUNCTION__)); + return NULL; + } + memset(cmn, 0, sizeof(dhd_cmn_t)); + cmn->osh = osh; + +#ifdef CONFIG_BCMDHD_FW_PATH + bcm_strncpy_s(fw_path, sizeof(fw_path), CONFIG_BCMDHD_FW_PATH, MOD_PARAM_PATHLEN-1); +#else /* CONFIG_BCMDHD_FW_PATH */ + fw_path[0] = '\0'; +#endif /* CONFIG_BCMDHD_FW_PATH */ +#ifdef CONFIG_BCMDHD_NVRAM_PATH + bcm_strncpy_s(nv_path, sizeof(nv_path), CONFIG_BCMDHD_NVRAM_PATH, MOD_PARAM_PATHLEN-1); +#else /* CONFIG_BCMDHD_NVRAM_PATH */ + nv_path[0] = '\0'; +#endif /* CONFIG_BCMDHD_NVRAM_PATH */ +#ifdef SOFTAP + fw_path2[0] = '\0'; +#endif + return cmn; +} + +void +dhd_common_deinit(dhd_pub_t *dhd_pub, dhd_cmn_t *sa_cmn) +{ + osl_t *osh; + dhd_cmn_t *cmn; + + if (dhd_pub != NULL) + cmn = dhd_pub->cmn; + else + cmn = sa_cmn; + + if (!cmn) + return; + + osh = cmn->osh; + + if (dhd_pub != NULL) + dhd_pub->cmn = NULL; + MFREE(osh, cmn, sizeof(dhd_cmn_t)); +} + +static int +dhd_dump(dhd_pub_t *dhdp, char *buf, int buflen) +{ + char eabuf[ETHER_ADDR_STR_LEN]; + + struct bcmstrbuf b; + struct bcmstrbuf *strbuf = &b; + + bcm_binit(strbuf, buf, buflen); + + /* Base DHD info */ + bcm_bprintf(strbuf, "%s\n", dhd_version); + bcm_bprintf(strbuf, "\n"); + bcm_bprintf(strbuf, "pub.up %d pub.txoff %d pub.busstate %d\n", + dhdp->up, dhdp->txoff, dhdp->busstate); + bcm_bprintf(strbuf, "pub.hdrlen %d pub.maxctl %d pub.rxsz %d\n", + dhdp->hdrlen, dhdp->maxctl, dhdp->rxsz); + bcm_bprintf(strbuf, "pub.iswl %d pub.drv_version %ld pub.mac %s\n", + dhdp->iswl, dhdp->drv_version, bcm_ether_ntoa(&dhdp->mac, eabuf)); + bcm_bprintf(strbuf, "pub.bcmerror %d tickcnt %d\n", dhdp->bcmerror, dhdp->tickcnt); + + bcm_bprintf(strbuf, "dongle stats:\n"); + bcm_bprintf(strbuf, "tx_packets %ld tx_bytes %ld tx_errors %ld tx_dropped %ld\n", + dhdp->dstats.tx_packets, dhdp->dstats.tx_bytes, + dhdp->dstats.tx_errors, dhdp->dstats.tx_dropped); + bcm_bprintf(strbuf, "rx_packets %ld rx_bytes %ld rx_errors %ld rx_dropped %ld\n", + dhdp->dstats.rx_packets, dhdp->dstats.rx_bytes, + dhdp->dstats.rx_errors, dhdp->dstats.rx_dropped); + bcm_bprintf(strbuf, "multicast %ld\n", dhdp->dstats.multicast); + + bcm_bprintf(strbuf, "bus stats:\n"); + bcm_bprintf(strbuf, "tx_packets %ld tx_multicast %ld tx_errors %ld\n", + dhdp->tx_packets, dhdp->tx_multicast, dhdp->tx_errors); + bcm_bprintf(strbuf, "tx_ctlpkts %ld tx_ctlerrs %ld\n", + dhdp->tx_ctlpkts, dhdp->tx_ctlerrs); + bcm_bprintf(strbuf, "rx_packets %ld rx_multicast %ld rx_errors %ld \n", + dhdp->rx_packets, dhdp->rx_multicast, dhdp->rx_errors); + bcm_bprintf(strbuf, "rx_ctlpkts %ld rx_ctlerrs %ld rx_dropped %ld\n", + dhdp->rx_ctlpkts, dhdp->rx_ctlerrs, dhdp->rx_dropped); + bcm_bprintf(strbuf, "rx_readahead_cnt %ld tx_realloc %ld\n", + dhdp->rx_readahead_cnt, dhdp->tx_realloc); + bcm_bprintf(strbuf, "\n"); + + /* Add any prot info */ + dhd_prot_dump(dhdp, strbuf); + bcm_bprintf(strbuf, "\n"); + + /* Add any bus info */ + dhd_bus_dump(dhdp, strbuf); + + return (!strbuf->size ? BCME_BUFTOOSHORT : 0); +} + +int +dhd_wl_ioctl_cmd(dhd_pub_t *dhd_pub, int cmd, void *arg, int len, uint8 set, int ifindex) +{ + wl_ioctl_t ioc; + + ioc.cmd = cmd; + ioc.buf = arg; + ioc.len = len; + ioc.set = set; + + return dhd_wl_ioctl(dhd_pub, ifindex, &ioc, arg, len); +} + + +int +dhd_wl_ioctl(dhd_pub_t *dhd_pub, int ifindex, wl_ioctl_t *ioc, void *buf, int len) +{ + int ret; + + dhd_os_proto_block(dhd_pub); + + ret = dhd_prot_ioctl(dhd_pub, ifindex, ioc, buf, len); + if (ret) + dhd_os_check_hang(dhd_pub, ifindex, ret); + + dhd_os_proto_unblock(dhd_pub); + return ret; +} + +static int +dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const char *name, + void *params, int plen, void *arg, int len, int val_size) +{ + int bcmerror = 0; + int32 int_val = 0; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + DHD_TRACE(("%s: actionid = %d; name %s\n", __FUNCTION__, actionid, name)); + + if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0) + goto exit; + + if (plen >= (int)sizeof(int_val)) + bcopy(params, &int_val, sizeof(int_val)); + + switch (actionid) { + case IOV_GVAL(IOV_VERSION): + /* Need to have checked buffer length */ + bcm_strncpy_s((char*)arg, len, dhd_version, len); + break; + + case IOV_GVAL(IOV_MSGLEVEL): + int_val = (int32)dhd_msg_level; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_MSGLEVEL): + dhd_msg_level = int_val; +#ifdef WL_CFG80211 + /* Enable DHD and WL logs in oneshot */ + if (dhd_msg_level & DHD_WL_VAL) + wl_cfg80211_enable_trace(dhd_msg_level); +#endif + break; + case IOV_GVAL(IOV_BCMERRORSTR): + bcm_strncpy_s((char *)arg, len, bcmerrorstr(dhd_pub->bcmerror), BCME_STRLEN); + ((char *)arg)[BCME_STRLEN - 1] = 0x00; + break; + + case IOV_GVAL(IOV_BCMERROR): + int_val = (int32)dhd_pub->bcmerror; + bcopy(&int_val, arg, val_size); + break; + + case IOV_GVAL(IOV_WDTICK): + int_val = (int32)dhd_watchdog_ms; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_WDTICK): + if (!dhd_pub->up) { + bcmerror = BCME_NOTUP; + break; + } + dhd_os_wd_timer(dhd_pub, (uint)int_val); + break; + + case IOV_GVAL(IOV_DUMP): + bcmerror = dhd_dump(dhd_pub, arg, len); + break; + +#ifdef DHD_DEBUG + case IOV_GVAL(IOV_DCONSOLE_POLL): + int_val = (int32)dhd_console_ms; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_DCONSOLE_POLL): + dhd_console_ms = (uint)int_val; + break; + + case IOV_SVAL(IOV_CONS): + if (len > 0) + bcmerror = dhd_bus_console_in(dhd_pub, arg, len - 1); + break; +#endif /* DHD_DEBUG */ + + case IOV_SVAL(IOV_CLEARCOUNTS): + dhd_pub->tx_packets = dhd_pub->rx_packets = 0; + dhd_pub->tx_errors = dhd_pub->rx_errors = 0; + dhd_pub->tx_ctlpkts = dhd_pub->rx_ctlpkts = 0; + dhd_pub->tx_ctlerrs = dhd_pub->rx_ctlerrs = 0; + dhd_pub->rx_dropped = 0; + dhd_pub->rx_readahead_cnt = 0; + dhd_pub->tx_realloc = 0; + dhd_pub->wd_dpc_sched = 0; + memset(&dhd_pub->dstats, 0, sizeof(dhd_pub->dstats)); + dhd_bus_clearcounts(dhd_pub); +#ifdef PROP_TXSTATUS + /* clear proptxstatus related counters */ + if (dhd_pub->wlfc_state) { + athost_wl_status_info_t *wlfc = + (athost_wl_status_info_t*)dhd_pub->wlfc_state; + wlfc_hanger_t* hanger; + + memset(&wlfc->stats, 0, sizeof(athost_wl_stat_counters_t)); + + hanger = (wlfc_hanger_t*)wlfc->hanger; + hanger->pushed = 0; + hanger->popped = 0; + hanger->failed_slotfind = 0; + hanger->failed_to_pop = 0; + hanger->failed_to_push = 0; + } +#endif /* PROP_TXSTATUS */ + break; + + + case IOV_GVAL(IOV_IOCTLTIMEOUT): { + int_val = (int32)dhd_os_get_ioctl_resp_timeout(); + bcopy(&int_val, arg, sizeof(int_val)); + break; + } + + case IOV_SVAL(IOV_IOCTLTIMEOUT): { + if (int_val <= 0) + bcmerror = BCME_BADARG; + else + dhd_os_set_ioctl_resp_timeout((unsigned int)int_val); + break; + } + + case IOV_SVAL(IOV_HCI_CMD): { + amp_hci_cmd_t *cmd = (amp_hci_cmd_t *)arg; + + /* sanity check: command preamble present */ + if (len < HCI_CMD_PREAMBLE_SIZE) + return BCME_BUFTOOSHORT; + + /* sanity check: command parameters are present */ + if (len < (int)(HCI_CMD_PREAMBLE_SIZE + cmd->plen)) + return BCME_BUFTOOSHORT; + + dhd_bta_docmd(dhd_pub, cmd, len); + break; + } + + case IOV_SVAL(IOV_HCI_ACL_DATA): { + amp_hci_ACL_data_t *ACL_data = (amp_hci_ACL_data_t *)arg; + + /* sanity check: HCI header present */ + if (len < HCI_ACL_DATA_PREAMBLE_SIZE) + return BCME_BUFTOOSHORT; + + /* sanity check: ACL data is present */ + if (len < (int)(HCI_ACL_DATA_PREAMBLE_SIZE + ACL_data->dlen)) + return BCME_BUFTOOSHORT; + + dhd_bta_tx_hcidata(dhd_pub, ACL_data, len); + break; + } + +#ifdef PROP_TXSTATUS + case IOV_GVAL(IOV_PROPTXSTATUS_ENABLE): + int_val = dhd_pub->wlfc_enabled? 1 : 0; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_PROPTXSTATUS_ENABLE): + dhd_pub->wlfc_enabled = int_val? 1 : 0; + break; + + case IOV_GVAL(IOV_PROPTXSTATUS_MODE): { + athost_wl_status_info_t *wlfc = + (athost_wl_status_info_t*)dhd_pub->wlfc_state; + int_val = dhd_pub->wlfc_state ? (int32)wlfc->proptxstatus_mode : 0; + bcopy(&int_val, arg, val_size); + break; + } + + case IOV_SVAL(IOV_PROPTXSTATUS_MODE): + if (dhd_pub->wlfc_state) { + athost_wl_status_info_t *wlfc = + (athost_wl_status_info_t*)dhd_pub->wlfc_state; + wlfc->proptxstatus_mode = int_val & 0xff; + } + break; +#endif /* PROP_TXSTATUS */ + + case IOV_GVAL(IOV_BUS_TYPE): + /* The dhd application queries the driver to check if its usb or sdio. */ +#ifdef BCMDHDUSB + int_val = BUS_TYPE_USB; +#endif + int_val = BUS_TYPE_SDIO; + bcopy(&int_val, arg, val_size); + break; + + +#ifdef WLMEDIA_HTSF + case IOV_GVAL(IOV_WLPKTDLYSTAT_SZ): + int_val = dhd_pub->htsfdlystat_sz; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_WLPKTDLYSTAT_SZ): + dhd_pub->htsfdlystat_sz = int_val & 0xff; + printf("Setting tsfdlystat_sz:%d\n", dhd_pub->htsfdlystat_sz); + break; +#endif + case IOV_SVAL(IOV_CHANGEMTU): + int_val &= 0xffff; + bcmerror = dhd_change_mtu(dhd_pub, int_val, 0); + break; + + default: + bcmerror = BCME_UNSUPPORTED; + break; + } + +exit: + DHD_TRACE(("%s: actionid %d, bcmerror %d\n", __FUNCTION__, actionid, bcmerror)); + return bcmerror; +} + +/* Store the status of a connection attempt for later retrieval by an iovar */ +void +dhd_store_conn_status(uint32 event, uint32 status, uint32 reason) +{ + /* Do not overwrite a WLC_E_PRUNE with a WLC_E_SET_SSID + * because an encryption/rsn mismatch results in both events, and + * the important information is in the WLC_E_PRUNE. + */ + if (!(event == WLC_E_SET_SSID && status == WLC_E_STATUS_FAIL && + dhd_conn_event == WLC_E_PRUNE)) { + dhd_conn_event = event; + dhd_conn_status = status; + dhd_conn_reason = reason; + } +} + +bool +dhd_prec_enq(dhd_pub_t *dhdp, struct pktq *q, void *pkt, int prec) +{ + void *p; + int eprec = -1; /* precedence to evict from */ + bool discard_oldest; + + /* Fast case, precedence queue is not full and we are also not + * exceeding total queue length + */ + if (!pktq_pfull(q, prec) && !pktq_full(q)) { + pktq_penq(q, prec, pkt); + return TRUE; + } + + /* Determine precedence from which to evict packet, if any */ + if (pktq_pfull(q, prec)) + eprec = prec; + else if (pktq_full(q)) { + p = pktq_peek_tail(q, &eprec); + ASSERT(p); + if (eprec > prec || eprec < 0) + return FALSE; + } + + /* Evict if needed */ + if (eprec >= 0) { + /* Detect queueing to unconfigured precedence */ + ASSERT(!pktq_pempty(q, eprec)); + discard_oldest = AC_BITMAP_TST(dhdp->wme_dp, eprec); + if (eprec == prec && !discard_oldest) + return FALSE; /* refuse newer (incoming) packet */ + /* Evict packet according to discard policy */ + p = discard_oldest ? pktq_pdeq(q, eprec) : pktq_pdeq_tail(q, eprec); + ASSERT(p); + + PKTFREE(dhdp->osh, p, TRUE); + } + + /* Enqueue */ + p = pktq_penq(q, prec, pkt); + ASSERT(p); + + return TRUE; +} + +static int +dhd_iovar_op(dhd_pub_t *dhd_pub, const char *name, + void *params, int plen, void *arg, int len, bool set) +{ + int bcmerror = 0; + int val_size; + const bcm_iovar_t *vi = NULL; + uint32 actionid; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + ASSERT(name); + ASSERT(len >= 0); + + /* Get MUST have return space */ + ASSERT(set || (arg && len)); + + /* Set does NOT take qualifiers */ + ASSERT(!set || (!params && !plen)); + + if ((vi = bcm_iovar_lookup(dhd_iovars, name)) == NULL) { + bcmerror = BCME_UNSUPPORTED; + goto exit; + } + + DHD_CTL(("%s: %s %s, len %d plen %d\n", __FUNCTION__, + name, (set ? "set" : "get"), len, plen)); + + /* set up 'params' pointer in case this is a set command so that + * the convenience int and bool code can be common to set and get + */ + if (params == NULL) { + params = arg; + plen = len; + } + + if (vi->type == IOVT_VOID) + val_size = 0; + else if (vi->type == IOVT_BUFFER) + val_size = len; + else + /* all other types are integer sized */ + val_size = sizeof(int); + + actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid); + + bcmerror = dhd_doiovar(dhd_pub, vi, actionid, name, params, plen, arg, len, val_size); + +exit: + return bcmerror; +} + +int +dhd_ioctl(dhd_pub_t * dhd_pub, dhd_ioctl_t *ioc, void * buf, uint buflen) +{ + int bcmerror = 0; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (!buf) { + return BCME_BADARG; + } + + switch (ioc->cmd) { + case DHD_GET_MAGIC: + if (buflen < sizeof(int)) + bcmerror = BCME_BUFTOOSHORT; + else + *(int*)buf = DHD_IOCTL_MAGIC; + break; + + case DHD_GET_VERSION: + if (buflen < sizeof(int)) + bcmerror = -BCME_BUFTOOSHORT; + else + *(int*)buf = DHD_IOCTL_VERSION; + break; + + case DHD_GET_VAR: + case DHD_SET_VAR: { + char *arg; + uint arglen; + + /* scan past the name to any arguments */ + for (arg = buf, arglen = buflen; *arg && arglen; arg++, arglen--) + ; + + if (*arg) { + bcmerror = BCME_BUFTOOSHORT; + break; + } + + /* account for the NUL terminator */ + arg++, arglen--; + + /* call with the appropriate arguments */ + if (ioc->cmd == DHD_GET_VAR) + bcmerror = dhd_iovar_op(dhd_pub, buf, arg, arglen, + buf, buflen, IOV_GET); + else + bcmerror = dhd_iovar_op(dhd_pub, buf, NULL, 0, arg, arglen, IOV_SET); + if (bcmerror != BCME_UNSUPPORTED) + break; + + /* not in generic table, try protocol module */ + if (ioc->cmd == DHD_GET_VAR) + bcmerror = dhd_prot_iovar_op(dhd_pub, buf, arg, + arglen, buf, buflen, IOV_GET); + else + bcmerror = dhd_prot_iovar_op(dhd_pub, buf, + NULL, 0, arg, arglen, IOV_SET); + if (bcmerror != BCME_UNSUPPORTED) + break; + + /* if still not found, try bus module */ + if (ioc->cmd == DHD_GET_VAR) { + bcmerror = dhd_bus_iovar_op(dhd_pub, buf, + arg, arglen, buf, buflen, IOV_GET); + } else { + bcmerror = dhd_bus_iovar_op(dhd_pub, buf, + NULL, 0, arg, arglen, IOV_SET); + } + + break; + } + + default: + bcmerror = BCME_UNSUPPORTED; + } + + return bcmerror; +} + +#ifdef SHOW_EVENTS +static void +wl_show_host_event(wl_event_msg_t *event, void *event_data) +{ + uint i, status, reason; + bool group = FALSE, flush_txq = FALSE, link = FALSE; + const char *auth_str; + const char *event_name; + uchar *buf; + char err_msg[256], eabuf[ETHER_ADDR_STR_LEN]; + uint event_type, flags, auth_type, datalen; + + event_type = ntoh32(event->event_type); + flags = ntoh16(event->flags); + status = ntoh32(event->status); + reason = ntoh32(event->reason); + auth_type = ntoh32(event->auth_type); + datalen = ntoh32(event->datalen); + + /* debug dump of event messages */ + sprintf(eabuf, "%02x:%02x:%02x:%02x:%02x:%02x", + (uchar)event->addr.octet[0]&0xff, + (uchar)event->addr.octet[1]&0xff, + (uchar)event->addr.octet[2]&0xff, + (uchar)event->addr.octet[3]&0xff, + (uchar)event->addr.octet[4]&0xff, + (uchar)event->addr.octet[5]&0xff); + + event_name = "UNKNOWN"; + for (i = 0; i < (uint)bcmevent_names_size; i++) + if (bcmevent_names[i].event == event_type) + event_name = bcmevent_names[i].name; + + if (flags & WLC_EVENT_MSG_LINK) + link = TRUE; + if (flags & WLC_EVENT_MSG_GROUP) + group = TRUE; + if (flags & WLC_EVENT_MSG_FLUSHTXQ) + flush_txq = TRUE; + + switch (event_type) { + case WLC_E_START: + case WLC_E_DEAUTH: + case WLC_E_DISASSOC: + DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf)); + break; + + case WLC_E_ASSOC_IND: + case WLC_E_REASSOC_IND: + + DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf)); + break; + + case WLC_E_ASSOC: + case WLC_E_REASSOC: + if (status == WLC_E_STATUS_SUCCESS) { + DHD_EVENT(("MACEVENT: %s, MAC %s, SUCCESS\n", event_name, eabuf)); + } else if (status == WLC_E_STATUS_TIMEOUT) { + DHD_EVENT(("MACEVENT: %s, MAC %s, TIMEOUT\n", event_name, eabuf)); + } else if (status == WLC_E_STATUS_FAIL) { + DHD_EVENT(("MACEVENT: %s, MAC %s, FAILURE, reason %d\n", + event_name, eabuf, (int)reason)); + } else { + DHD_EVENT(("MACEVENT: %s, MAC %s, unexpected status %d\n", + event_name, eabuf, (int)status)); + } + break; + + case WLC_E_DEAUTH_IND: + case WLC_E_DISASSOC_IND: + DHD_EVENT(("MACEVENT: %s, MAC %s, reason %d\n", event_name, eabuf, (int)reason)); + break; + + case WLC_E_AUTH: + case WLC_E_AUTH_IND: + if (auth_type == DOT11_OPEN_SYSTEM) + auth_str = "Open System"; + else if (auth_type == DOT11_SHARED_KEY) + auth_str = "Shared Key"; + else { + sprintf(err_msg, "AUTH unknown: %d", (int)auth_type); + auth_str = err_msg; + } + if (event_type == WLC_E_AUTH_IND) { + DHD_EVENT(("MACEVENT: %s, MAC %s, %s\n", event_name, eabuf, auth_str)); + } else if (status == WLC_E_STATUS_SUCCESS) { + DHD_EVENT(("MACEVENT: %s, MAC %s, %s, SUCCESS\n", + event_name, eabuf, auth_str)); + } else if (status == WLC_E_STATUS_TIMEOUT) { + DHD_EVENT(("MACEVENT: %s, MAC %s, %s, TIMEOUT\n", + event_name, eabuf, auth_str)); + } else if (status == WLC_E_STATUS_FAIL) { + DHD_EVENT(("MACEVENT: %s, MAC %s, %s, FAILURE, reason %d\n", + event_name, eabuf, auth_str, (int)reason)); + } + + break; + + case WLC_E_JOIN: + case WLC_E_ROAM: + case WLC_E_SET_SSID: + if (status == WLC_E_STATUS_SUCCESS) { + DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf)); + } else if (status == WLC_E_STATUS_FAIL) { + DHD_EVENT(("MACEVENT: %s, failed\n", event_name)); + } else if (status == WLC_E_STATUS_NO_NETWORKS) { + DHD_EVENT(("MACEVENT: %s, no networks found\n", event_name)); + } else { + DHD_EVENT(("MACEVENT: %s, unexpected status %d\n", + event_name, (int)status)); + } + break; + + case WLC_E_BEACON_RX: + if (status == WLC_E_STATUS_SUCCESS) { + DHD_EVENT(("MACEVENT: %s, SUCCESS\n", event_name)); + } else if (status == WLC_E_STATUS_FAIL) { + DHD_EVENT(("MACEVENT: %s, FAIL\n", event_name)); + } else { + DHD_EVENT(("MACEVENT: %s, status %d\n", event_name, status)); + } + break; + + case WLC_E_LINK: + DHD_EVENT(("MACEVENT: %s %s\n", event_name, link?"UP":"DOWN")); + break; + + case WLC_E_MIC_ERROR: + DHD_EVENT(("MACEVENT: %s, MAC %s, Group %d, Flush %d\n", + event_name, eabuf, group, flush_txq)); + break; + + case WLC_E_ICV_ERROR: + case WLC_E_UNICAST_DECODE_ERROR: + case WLC_E_MULTICAST_DECODE_ERROR: + DHD_EVENT(("MACEVENT: %s, MAC %s\n", + event_name, eabuf)); + break; + + case WLC_E_TXFAIL: + DHD_EVENT(("MACEVENT: %s, RA %s\n", event_name, eabuf)); + break; + + case WLC_E_SCAN_COMPLETE: + case WLC_E_ASSOC_REQ_IE: + case WLC_E_ASSOC_RESP_IE: + case WLC_E_PMKID_CACHE: + DHD_EVENT(("MACEVENT: %s\n", event_name)); + break; + + case WLC_E_PFN_NET_FOUND: + case WLC_E_PFN_NET_LOST: + case WLC_E_PFN_SCAN_COMPLETE: + case WLC_E_PFN_SCAN_NONE: + case WLC_E_PFN_SCAN_ALLGONE: + DHD_EVENT(("PNOEVENT: %s\n", event_name)); + break; + + case WLC_E_PSK_SUP: + case WLC_E_PRUNE: + DHD_EVENT(("MACEVENT: %s, status %d, reason %d\n", + event_name, (int)status, (int)reason)); + break; + +#ifdef WIFI_ACT_FRAME + case WLC_E_ACTION_FRAME: + DHD_TRACE(("MACEVENT: %s Bssid %s\n", event_name, eabuf)); + break; +#endif /* WIFI_ACT_FRAME */ + + case WLC_E_TRACE: { + static uint32 seqnum_prev = 0; + msgtrace_hdr_t hdr; + uint32 nblost; + char *s, *p; + + buf = (uchar *) event_data; + memcpy(&hdr, buf, MSGTRACE_HDRLEN); + + if (hdr.version != MSGTRACE_VERSION) { + printf("\nMACEVENT: %s [unsupported version --> " + "dhd version:%d dongle version:%d]\n", + event_name, MSGTRACE_VERSION, hdr.version); + /* Reset datalen to avoid display below */ + datalen = 0; + break; + } + + /* There are 2 bytes available at the end of data */ + buf[MSGTRACE_HDRLEN + ntoh16(hdr.len)] = '\0'; + + if (ntoh32(hdr.discarded_bytes) || ntoh32(hdr.discarded_printf)) { + printf("\nWLC_E_TRACE: [Discarded traces in dongle -->" + "discarded_bytes %d discarded_printf %d]\n", + ntoh32(hdr.discarded_bytes), ntoh32(hdr.discarded_printf)); + } + + nblost = ntoh32(hdr.seqnum) - seqnum_prev - 1; + if (nblost > 0) { + printf("\nWLC_E_TRACE: [Event lost --> seqnum %d nblost %d\n", + ntoh32(hdr.seqnum), nblost); + } + seqnum_prev = ntoh32(hdr.seqnum); + + /* Display the trace buffer. Advance from \n to \n to avoid display big + * printf (issue with Linux printk ) + */ + p = (char *)&buf[MSGTRACE_HDRLEN]; + while ((s = strstr(p, "\n")) != NULL) { + *s = '\0'; + printf("%s\n", p); + p = s+1; + } + printf("%s\n", p); + + /* Reset datalen to avoid display below */ + datalen = 0; + break; + } + + + case WLC_E_RSSI: + DHD_EVENT(("MACEVENT: %s %d\n", event_name, ntoh32(*((int *)event_data)))); + break; + + default: + DHD_EVENT(("MACEVENT: %s %d, MAC %s, status %d, reason %d, auth %d\n", + event_name, event_type, eabuf, (int)status, (int)reason, + (int)auth_type)); + break; + } + + /* show any appended data */ + if (datalen) { + buf = (uchar *) event_data; + DHD_EVENT((" data (%d) : ", datalen)); + for (i = 0; i < datalen; i++) + DHD_EVENT((" 0x%02x ", *buf++)); + DHD_EVENT(("\n")); + } +} +#endif /* SHOW_EVENTS */ + +int +wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, + wl_event_msg_t *event, void **data_ptr) +{ + /* check whether packet is a BRCM event pkt */ + bcm_event_t *pvt_data = (bcm_event_t *)pktdata; + uint8 *event_data; + uint32 type, status, reason, datalen; + uint16 flags; + int evlen; + + if (bcmp(BRCM_OUI, &pvt_data->bcm_hdr.oui[0], DOT11_OUI_LEN)) { + DHD_ERROR(("%s: mismatched OUI, bailing\n", __FUNCTION__)); + return (BCME_ERROR); + } + + /* BRCM event pkt may be unaligned - use xxx_ua to load user_subtype. */ + if (ntoh16_ua((void *)&pvt_data->bcm_hdr.usr_subtype) != BCMILCP_BCM_SUBTYPE_EVENT) { + DHD_ERROR(("%s: mismatched subtype, bailing\n", __FUNCTION__)); + return (BCME_ERROR); + } + + *data_ptr = &pvt_data[1]; + event_data = *data_ptr; + + /* memcpy since BRCM event pkt may be unaligned. */ + memcpy(event, &pvt_data->event, sizeof(wl_event_msg_t)); + + type = ntoh32_ua((void *)&event->event_type); + flags = ntoh16_ua((void *)&event->flags); + status = ntoh32_ua((void *)&event->status); + reason = ntoh32_ua((void *)&event->reason); + datalen = ntoh32_ua((void *)&event->datalen); + evlen = datalen + sizeof(bcm_event_t); + + DHD_TRACE(("RX: event_type:%d flags:%d status:%d reason:%d \n", + type, flags, status, reason)); + + switch (type) { +#ifdef PROP_TXSTATUS + case WLC_E_FIFO_CREDIT_MAP: + dhd_wlfc_event(dhd_pub->info); + dhd_wlfc_FIFOcreditmap_event(dhd_pub->info, event_data); + WLFC_DBGMESG(("WLC_E_FIFO_CREDIT_MAP:(AC0,AC1,AC2,AC3),(BC_MC),(OTHER): " + "(%d,%d,%d,%d),(%d),(%d)\n", event_data[0], event_data[1], + event_data[2], + event_data[3], event_data[4], event_data[5])); + break; +#endif + + case WLC_E_IF: + { + dhd_if_event_t *ifevent = (dhd_if_event_t *)event_data; +#ifdef PROP_TXSTATUS + { + uint8* ea = pvt_data->eth.ether_dhost; + WLFC_DBGMESG(("WLC_E_IF: idx:%d, action:%s, iftype:%s, " + "[%02x:%02x:%02x:%02x:%02x:%02x]\n", + ifevent->ifidx, + ((ifevent->action == WLC_E_IF_ADD) ? "ADD":"DEL"), + ((ifevent->is_AP == 0) ? "STA":"AP "), + ea[0], ea[1], ea[2], ea[3], ea[4], ea[5])); + (void)ea; + + dhd_wlfc_interface_event(dhd_pub->info, + ((ifevent->action == WLC_E_IF_ADD) ? + eWLFC_MAC_ENTRY_ACTION_ADD : eWLFC_MAC_ENTRY_ACTION_DEL), + ifevent->ifidx, ifevent->is_AP, ea); + + /* dhd already has created an interface by default, for 0 */ + if (ifevent->ifidx == 0) + break; + } +#endif /* PROP_TXSTATUS */ + +#ifdef WL_CFG80211 + if (wl_cfg80211_is_progress_ifchange()) { + DHD_ERROR(("%s: ifidx %d for %s action %d\n", + __FUNCTION__, ifevent->ifidx, + event->ifname, ifevent->action)); + if (ifevent->action == WLC_E_IF_ADD) + wl_cfg80211_notify_ifchange(); + return (BCME_OK); + } +#endif /* WL_CFG80211 */ + if (ifevent->ifidx > 0 && ifevent->ifidx < DHD_MAX_IFS) { + if (ifevent->action == WLC_E_IF_ADD) { + if (dhd_add_if(dhd_pub->info, ifevent->ifidx, + NULL, event->ifname, + event->addr.octet, + ifevent->flags, ifevent->bssidx)) { + DHD_ERROR(("%s: dhd_add_if failed!!" + " ifidx: %d for %s\n", + __FUNCTION__, + ifevent->ifidx, + event->ifname)); + return (BCME_ERROR); + } + } + else + dhd_del_if(dhd_pub->info, ifevent->ifidx); + } else { +#ifndef PROP_TXSTATUS + DHD_ERROR(("%s: Invalid ifidx %d for %s\n", + __FUNCTION__, ifevent->ifidx, event->ifname)); +#endif /* !PROP_TXSTATUS */ + } + } + /* send up the if event: btamp user needs it */ + *ifidx = dhd_ifname2idx(dhd_pub->info, event->ifname); + /* push up to external supp/auth */ + dhd_event(dhd_pub->info, (char *)pvt_data, evlen, *ifidx); + break; + + +#ifdef WLMEDIA_HTSF + case WLC_E_HTSFSYNC: + htsf_update(dhd_pub->info, event_data); + break; +#endif /* WLMEDIA_HTSF */ + case WLC_E_NDIS_LINK: { + uint32 temp = hton32(WLC_E_LINK); + + memcpy((void *)(&pvt_data->event.event_type), &temp, + sizeof(pvt_data->event.event_type)); + } + /* These are what external supplicant/authenticator wants */ + /* fall through */ + case WLC_E_LINK: + case WLC_E_DEAUTH: + case WLC_E_DEAUTH_IND: + case WLC_E_DISASSOC: + case WLC_E_DISASSOC_IND: + DHD_EVENT(("%s: Link event %d, flags %x, status %x\n", + __FUNCTION__, type, flags, status)); + /* fall through */ + default: + *ifidx = dhd_ifname2idx(dhd_pub->info, event->ifname); + /* push up to external supp/auth */ + dhd_event(dhd_pub->info, (char *)pvt_data, evlen, *ifidx); + DHD_TRACE(("%s: MAC event %d, flags %x, status %x\n", + __FUNCTION__, type, flags, status)); + + /* put it back to WLC_E_NDIS_LINK */ + if (type == WLC_E_NDIS_LINK) { + uint32 temp; + + temp = ntoh32_ua((void *)&event->event_type); + DHD_TRACE(("Converted to WLC_E_LINK type %d\n", temp)); + + temp = ntoh32(WLC_E_NDIS_LINK); + memcpy((void *)(&pvt_data->event.event_type), &temp, + sizeof(pvt_data->event.event_type)); + } + break; + } + +#ifdef SHOW_EVENTS + wl_show_host_event(event, (void *)event_data); +#endif /* SHOW_EVENTS */ + + return (BCME_OK); +} + +void +wl_event_to_host_order(wl_event_msg_t * evt) +{ + /* Event struct members passed from dongle to host are stored in network + * byte order. Convert all members to host-order. + */ + evt->event_type = ntoh32(evt->event_type); + evt->flags = ntoh16(evt->flags); + evt->status = ntoh32(evt->status); + evt->reason = ntoh32(evt->reason); + evt->auth_type = ntoh32(evt->auth_type); + evt->datalen = ntoh32(evt->datalen); + evt->version = ntoh16(evt->version); +} + +void +dhd_print_buf(void *pbuf, int len, int bytes_per_line) +{ +#ifdef DHD_DEBUG + int i, j = 0; + unsigned char *buf = pbuf; + + if (bytes_per_line == 0) { + bytes_per_line = len; + } + + for (i = 0; i < len; i++) { + printf("%2.2x", *buf++); + j++; + if (j == bytes_per_line) { + printf("\n"); + j = 0; + } else { + printf(":"); + } + } + printf("\n"); +#endif /* DHD_DEBUG */ +} + +#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) + +/* Convert user's input in hex pattern to byte-size mask */ +static int +wl_pattern_atoh(char *src, char *dst) +{ + int i; + if (strncmp(src, "0x", 2) != 0 && + strncmp(src, "0X", 2) != 0) { + DHD_ERROR(("Mask invalid format. Needs to start with 0x\n")); + return -1; + } + src = src + 2; /* Skip past 0x */ + if (strlen(src) % 2 != 0) { + DHD_ERROR(("Mask invalid format. Needs to be of even length\n")); + return -1; + } + for (i = 0; *src != '\0'; i++) { + char num[3]; + bcm_strncpy_s(num, sizeof(num), src, 2); + num[2] = '\0'; + dst[i] = (uint8)strtoul(num, NULL, 16); + src += 2; + } + return i; +} + +void +dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode) +{ + char *argv[8]; + int i = 0; + const char *str; + int buf_len; + int str_len; + char *arg_save = 0, *arg_org = 0; + int rc; + char buf[128]; + wl_pkt_filter_enable_t enable_parm; + wl_pkt_filter_enable_t * pkt_filterp; + + if (!arg) + return; + + if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) { + DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); + goto fail; + } + arg_org = arg_save; + memcpy(arg_save, arg, strlen(arg) + 1); + + argv[i] = bcmstrtok(&arg_save, " ", 0); + + i = 0; + if (argv[i] == NULL) { + DHD_ERROR(("No args provided\n")); + goto fail; + } + + str = "pkt_filter_enable"; + str_len = strlen(str); + bcm_strncpy_s(buf, sizeof(buf), str, str_len); + buf[str_len] = '\0'; + buf_len = str_len + 1; + + pkt_filterp = (wl_pkt_filter_enable_t *)(buf + str_len + 1); + + /* Parse packet filter id. */ + enable_parm.id = htod32(strtoul(argv[i], NULL, 0)); + + /* Parse enable/disable value. */ + enable_parm.enable = htod32(enable); + + buf_len += sizeof(enable_parm); + memcpy((char *)pkt_filterp, + &enable_parm, + sizeof(enable_parm)); + + /* Enable/disable the specified filter. */ + rc = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, buf_len, TRUE, 0); + rc = rc >= 0 ? 0 : rc; + if (rc) + DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n", + __FUNCTION__, arg, rc)); + else + DHD_TRACE(("%s: successfully added pktfilter %s\n", + __FUNCTION__, arg)); + + /* Contorl the master mode */ + bcm_mkiovar("pkt_filter_mode", (char *)&master_mode, 4, buf, sizeof(buf)); + rc = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); + rc = rc >= 0 ? 0 : rc; + if (rc) + DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n", + __FUNCTION__, arg, rc)); + +fail: + if (arg_org) + MFREE(dhd->osh, arg_org, strlen(arg) + 1); +} + +void +dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg) +{ + const char *str; + wl_pkt_filter_t pkt_filter; + wl_pkt_filter_t *pkt_filterp; + int buf_len; + int str_len; + int rc; + uint32 mask_size; + uint32 pattern_size; + char *argv[8], * buf = 0; + int i = 0; + char *arg_save = 0, *arg_org = 0; +#define BUF_SIZE 2048 + + if (!arg) + return; + + if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) { + DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); + goto fail; + } + + arg_org = arg_save; + + if (!(buf = MALLOC(dhd->osh, BUF_SIZE))) { + DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__)); + goto fail; + } + + memcpy(arg_save, arg, strlen(arg) + 1); + + if (strlen(arg) > BUF_SIZE) { + DHD_ERROR(("Not enough buffer %d < %d\n", (int)strlen(arg), (int)sizeof(buf))); + goto fail; + } + + argv[i] = bcmstrtok(&arg_save, " ", 0); + while (argv[i++]) + argv[i] = bcmstrtok(&arg_save, " ", 0); + + i = 0; + if (argv[i] == NULL) { + DHD_ERROR(("No args provided\n")); + goto fail; + } + + str = "pkt_filter_add"; + str_len = strlen(str); + bcm_strncpy_s(buf, BUF_SIZE, str, str_len); + buf[ str_len ] = '\0'; + buf_len = str_len + 1; + + pkt_filterp = (wl_pkt_filter_t *) (buf + str_len + 1); + + /* Parse packet filter id. */ + pkt_filter.id = htod32(strtoul(argv[i], NULL, 0)); + + if (argv[++i] == NULL) { + DHD_ERROR(("Polarity not provided\n")); + goto fail; + } + + /* Parse filter polarity. */ + pkt_filter.negate_match = htod32(strtoul(argv[i], NULL, 0)); + + if (argv[++i] == NULL) { + DHD_ERROR(("Filter type not provided\n")); + goto fail; + } + + /* Parse filter type. */ + pkt_filter.type = htod32(strtoul(argv[i], NULL, 0)); + + if (argv[++i] == NULL) { + DHD_ERROR(("Offset not provided\n")); + goto fail; + } + + /* Parse pattern filter offset. */ + pkt_filter.u.pattern.offset = htod32(strtoul(argv[i], NULL, 0)); + + if (argv[++i] == NULL) { + DHD_ERROR(("Bitmask not provided\n")); + goto fail; + } + + /* Parse pattern filter mask. */ + mask_size = + htod32(wl_pattern_atoh(argv[i], (char *) pkt_filterp->u.pattern.mask_and_pattern)); + + if (argv[++i] == NULL) { + DHD_ERROR(("Pattern not provided\n")); + goto fail; + } + + /* Parse pattern filter pattern. */ + pattern_size = + htod32(wl_pattern_atoh(argv[i], + (char *) &pkt_filterp->u.pattern.mask_and_pattern[mask_size])); + + if (mask_size != pattern_size) { + DHD_ERROR(("Mask and pattern not the same size\n")); + goto fail; + } + + pkt_filter.u.pattern.size_bytes = mask_size; + buf_len += WL_PKT_FILTER_FIXED_LEN; + buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size); + + /* Keep-alive attributes are set in local variable (keep_alive_pkt), and + ** then memcpy'ed into buffer (keep_alive_pktp) since there is no + ** guarantee that the buffer is properly aligned. + */ + memcpy((char *)pkt_filterp, + &pkt_filter, + WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN); + + rc = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, buf_len, TRUE, 0); + rc = rc >= 0 ? 0 : rc; + + if (rc) + DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n", + __FUNCTION__, arg, rc)); + else + DHD_TRACE(("%s: successfully added pktfilter %s\n", + __FUNCTION__, arg)); + +fail: + if (arg_org) + MFREE(dhd->osh, arg_org, strlen(arg) + 1); + + if (buf) + MFREE(dhd->osh, buf, BUF_SIZE); +} + +/* ========================== */ +/* ==== ARP OFFLOAD SUPPORT = */ +/* ========================== */ +#ifdef ARP_OFFLOAD_SUPPORT +void +dhd_arp_offload_set(dhd_pub_t * dhd, int arp_mode) +{ + char iovbuf[32]; + int retcode; + + bcm_mkiovar("arp_ol", (char *)&arp_mode, 4, iovbuf, sizeof(iovbuf)); + retcode = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + retcode = retcode >= 0 ? 0 : retcode; + if (retcode) + DHD_TRACE(("%s: failed to set ARP offload mode to 0x%x, retcode = %d\n", + __FUNCTION__, arp_mode, retcode)); + else + DHD_TRACE(("%s: successfully set ARP offload mode to 0x%x\n", + __FUNCTION__, arp_mode)); +} + +void +dhd_arp_offload_enable(dhd_pub_t * dhd, int arp_enable) +{ + char iovbuf[32]; + int retcode; + + bcm_mkiovar("arpoe", (char *)&arp_enable, 4, iovbuf, sizeof(iovbuf)); + retcode = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + retcode = retcode >= 0 ? 0 : retcode; + if (retcode) + DHD_TRACE(("%s: failed to enabe ARP offload to %d, retcode = %d\n", + __FUNCTION__, arp_enable, retcode)); + else + DHD_TRACE(("%s: successfully enabed ARP offload to %d\n", + __FUNCTION__, arp_enable)); +} + +void +dhd_aoe_arp_clr(dhd_pub_t *dhd) +{ + int ret = 0; + int iov_len = 0; + char iovbuf[128]; + + if (dhd == NULL) return; + + iov_len = bcm_mkiovar("arp_table_clear", 0, 0, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, iov_len, TRUE, 0) < 0)) + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); +} + +void +dhd_aoe_hostip_clr(dhd_pub_t *dhd) +{ + int ret = 0; + int iov_len = 0; + char iovbuf[128]; + + if (dhd == NULL) return; + + iov_len = bcm_mkiovar("arp_hostip_clear", 0, 0, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, iov_len, TRUE, 0)) < 0) + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); +} + +void +dhd_arp_offload_add_ip(dhd_pub_t *dhd, uint32 ipaddr) +{ + int iov_len = 0; + char iovbuf[32]; + int retcode; + + iov_len = bcm_mkiovar("arp_hostip", (char *)&ipaddr, 4, iovbuf, sizeof(iovbuf)); + retcode = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, iov_len, TRUE, 0); + + if (retcode) + DHD_TRACE(("%s: ARP ip addr add failed, retcode = %d\n", + __FUNCTION__, retcode)); + else + DHD_TRACE(("%s: sARP H ipaddr entry added \n", + __FUNCTION__)); +} + +int +dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen) +{ + int retcode, i; + int iov_len = 0; + uint32 *ptr32 = buf; + bool clr_bottom = FALSE; + + if (!buf) + return -1; + + iov_len = bcm_mkiovar("arp_hostip", 0, 0, buf, buflen); + retcode = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, buflen, FALSE, 0); + + if (retcode) { + DHD_TRACE(("%s: ioctl WLC_GET_VAR error %d\n", + __FUNCTION__, retcode)); + + return -1; + } + + /* clean up the buf, ascii reminder */ + for (i = 0; i < MAX_IPV4_ENTRIES; i++) { + if (!clr_bottom) { + if (*ptr32 == 0) + clr_bottom = TRUE; + } else { + *ptr32 = 0; + } + ptr32++; + } + + return 0; +} +#endif /* ARP_OFFLOAD_SUPPORT */ + +/* send up locally generated event */ +void +dhd_sendup_event_common(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data) +{ + switch (ntoh32(event->event_type)) { + case WLC_E_BTA_HCI_EVENT: + break; + default: + break; + } + + /* Call per-port handler. */ + dhd_sendup_event(dhdp, event, data); +} + +#ifdef SIMPLE_ISCAN + +uint iscan_thread_id = 0; +iscan_buf_t * iscan_chain = 0; + +iscan_buf_t * +dhd_iscan_allocate_buf(dhd_pub_t *dhd, iscan_buf_t **iscanbuf) +{ + iscan_buf_t *iscanbuf_alloc = 0; + iscan_buf_t *iscanbuf_head; + + DHD_ISCAN(("%s: Entered\n", __FUNCTION__)); + dhd_iscan_lock(); + + iscanbuf_alloc = (iscan_buf_t*)MALLOC(dhd->osh, sizeof(iscan_buf_t)); + if (iscanbuf_alloc == NULL) + goto fail; + + iscanbuf_alloc->next = NULL; + iscanbuf_head = *iscanbuf; + + DHD_ISCAN(("%s: addr of allocated node = 0x%X" + "addr of iscanbuf_head = 0x%X dhd = 0x%X\n", + __FUNCTION__, iscanbuf_alloc, iscanbuf_head, dhd)); + + if (iscanbuf_head == NULL) { + *iscanbuf = iscanbuf_alloc; + DHD_ISCAN(("%s: Head is allocated\n", __FUNCTION__)); + goto fail; + } + + while (iscanbuf_head->next) + iscanbuf_head = iscanbuf_head->next; + + iscanbuf_head->next = iscanbuf_alloc; + +fail: + dhd_iscan_unlock(); + return iscanbuf_alloc; +} + +void +dhd_iscan_free_buf(void *dhdp, iscan_buf_t *iscan_delete) +{ + iscan_buf_t *iscanbuf_free = 0; + iscan_buf_t *iscanbuf_prv = 0; + iscan_buf_t *iscanbuf_cur; + dhd_pub_t *dhd = dhd_bus_pub(dhdp); + DHD_ISCAN(("%s: Entered\n", __FUNCTION__)); + + dhd_iscan_lock(); + + iscanbuf_cur = iscan_chain; + + /* If iscan_delete is null then delete the entire + * chain or else delete specific one provided + */ + if (!iscan_delete) { + while (iscanbuf_cur) { + iscanbuf_free = iscanbuf_cur; + iscanbuf_cur = iscanbuf_cur->next; + iscanbuf_free->next = 0; + MFREE(dhd->osh, iscanbuf_free, sizeof(iscan_buf_t)); + } + iscan_chain = 0; + } else { + while (iscanbuf_cur) { + if (iscanbuf_cur == iscan_delete) + break; + iscanbuf_prv = iscanbuf_cur; + iscanbuf_cur = iscanbuf_cur->next; + } + if (iscanbuf_prv) + iscanbuf_prv->next = iscan_delete->next; + + iscan_delete->next = 0; + MFREE(dhd->osh, iscan_delete, sizeof(iscan_buf_t)); + + if (!iscanbuf_prv) + iscan_chain = 0; + } + dhd_iscan_unlock(); +} + +iscan_buf_t * +dhd_iscan_result_buf(void) +{ + return iscan_chain; +} + +int +dhd_iscan_issue_request(void * dhdp, wl_iscan_params_t *pParams, uint32 size) +{ + int rc = -1; + dhd_pub_t *dhd = dhd_bus_pub(dhdp); + char *buf; + char iovar[] = "iscan"; + uint32 allocSize = 0; + wl_ioctl_t ioctl; + + if (pParams) { + allocSize = (size + strlen(iovar) + 1); + if ((allocSize < size) || (allocSize < strlen(iovar))) + { + DHD_ERROR(("%s: overflow - allocation size too large %d < %d + %d!\n", + __FUNCTION__, allocSize, size, strlen(iovar))); + goto cleanUp; + } + buf = MALLOC(dhd->osh, allocSize); + + if (buf == NULL) + { + DHD_ERROR(("%s: malloc of size %d failed!\n", __FUNCTION__, allocSize)); + goto cleanUp; + } + ioctl.cmd = WLC_SET_VAR; + bcm_mkiovar(iovar, (char *)pParams, size, buf, allocSize); + rc = dhd_wl_ioctl(dhd, 0, &ioctl, buf, allocSize); + } + +cleanUp: + if (buf) { + MFREE(dhd->osh, buf, allocSize); + } + + return rc; +} + +static int +dhd_iscan_get_partial_result(void *dhdp, uint *scan_count) +{ + wl_iscan_results_t *list_buf; + wl_iscan_results_t list; + wl_scan_results_t *results; + iscan_buf_t *iscan_cur; + int status = -1; + dhd_pub_t *dhd = dhd_bus_pub(dhdp); + int rc; + wl_ioctl_t ioctl; + + DHD_ISCAN(("%s: Enter\n", __FUNCTION__)); + + iscan_cur = dhd_iscan_allocate_buf(dhd, &iscan_chain); + if (!iscan_cur) { + DHD_ERROR(("%s: Failed to allocate node\n", __FUNCTION__)); + dhd_iscan_free_buf(dhdp, 0); + dhd_iscan_request(dhdp, WL_SCAN_ACTION_ABORT); + dhd_ind_scan_confirm(dhdp, FALSE); + goto fail; + } + + dhd_iscan_lock(); + + memset(iscan_cur->iscan_buf, 0, WLC_IW_ISCAN_MAXLEN); + list_buf = (wl_iscan_results_t*)iscan_cur->iscan_buf; + results = &list_buf->results; + results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE; + results->version = 0; + results->count = 0; + + memset(&list, 0, sizeof(list)); + list.results.buflen = htod32(WLC_IW_ISCAN_MAXLEN); + bcm_mkiovar("iscanresults", (char *)&list, WL_ISCAN_RESULTS_FIXED_SIZE, + iscan_cur->iscan_buf, WLC_IW_ISCAN_MAXLEN); + ioctl.cmd = WLC_GET_VAR; + ioctl.set = FALSE; + rc = dhd_wl_ioctl(dhd, 0, &ioctl, iscan_cur->iscan_buf, WLC_IW_ISCAN_MAXLEN); + + results->buflen = dtoh32(results->buflen); + results->version = dtoh32(results->version); + *scan_count = results->count = dtoh32(results->count); + status = dtoh32(list_buf->status); + DHD_ISCAN(("%s: Got %d resuls status = (%x)\n", __FUNCTION__, results->count, status)); + + dhd_iscan_unlock(); + + if (!(*scan_count)) { + /* TODO: race condition when FLUSH already called */ + dhd_iscan_free_buf(dhdp, 0); + } +fail: + return status; +} + +#endif /* SIMPLE_ISCAN */ + +/* + * returns = TRUE if associated, FALSE if not associated + * third paramter retval can return error from error + */ +bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf, int *retval) +{ + char bssid[6], zbuf[6]; + int ret; + + bzero(bssid, 6); + bzero(zbuf, 6); + + ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_BSSID, (char *)&bssid, ETHER_ADDR_LEN, FALSE, 0); + DHD_TRACE((" %s WLC_GET_BSSID ioctl res = %d\n", __FUNCTION__, ret)); + + if (retval) + *retval = ret; + + if (ret == BCME_NOTASSOCIATED) { + DHD_TRACE(("%s: not associated! res:%d\n", __FUNCTION__, ret)); + } + + if (ret < 0) + return FALSE; + + if ((memcmp(bssid, zbuf, ETHER_ADDR_LEN) != 0)) { + /* STA is assocoated BSSID is non zero */ + + if (bss_buf) { + /* return bss if caller provided buf */ + memcpy(bss_buf, bssid, ETHER_ADDR_LEN); + } + return TRUE; + } else { + DHD_TRACE(("%s: WLC_GET_BSSID ioctl returned zero bssid\n", __FUNCTION__)); + return FALSE; + } +} + +/* Function to estimate possible DTIM_SKIP value */ +int +dhd_get_dtim_skip(dhd_pub_t *dhd) +{ + int bcn_li_dtim; + int ret = -1; + int dtim_assoc = 0; + + if ((dhd->dtim_skip == 0) || (dhd->dtim_skip == 1)) + bcn_li_dtim = 3; + else + bcn_li_dtim = dhd->dtim_skip; + + /* Check if associated */ + if (dhd_is_associated(dhd, NULL, NULL) == FALSE) { + DHD_TRACE(("%s NOT assoc ret %d\n", __FUNCTION__, ret)); + goto exit; + } + + /* if assoc grab ap's dtim value */ + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_DTIMPRD, + &dtim_assoc, sizeof(dtim_assoc), FALSE, 0)) < 0) { + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); + goto exit; + } + + DHD_ERROR(("%s bcn_li_dtim=%d DTIM=%d Listen=%d\n", + __FUNCTION__, bcn_li_dtim, dtim_assoc, LISTEN_INTERVAL)); + + /* if not assocated just eixt */ + if (dtim_assoc == 0) { + goto exit; + } + + /* check if sta listen interval fits into AP dtim */ + if (dtim_assoc > LISTEN_INTERVAL) { + /* AP DTIM to big for our Listen Interval : no dtim skiping */ + bcn_li_dtim = 1; + DHD_ERROR(("%s DTIM=%d > Listen=%d : too big ...\n", + __FUNCTION__, dtim_assoc, LISTEN_INTERVAL)); + goto exit; + } + + if ((bcn_li_dtim * dtim_assoc) > LISTEN_INTERVAL) { + /* Round up dtim_skip to fit into STAs Listen Interval */ + bcn_li_dtim = (int)(LISTEN_INTERVAL / dtim_assoc); + DHD_TRACE(("%s agjust dtim_skip as %d\n", __FUNCTION__, bcn_li_dtim)); + } + +exit: + return bcn_li_dtim; +} + +/* Check if HostAPD or WFD mode setup */ +bool dhd_check_ap_wfd_mode_set(dhd_pub_t *dhd) +{ +#ifdef WL_CFG80211 +#ifndef WL_ENABLE_P2P_IF + /* To be back compatble with ICS MR1 release where p2p interface + * disable but wlan0 used for p2p + */ + if (((dhd->op_mode & HOSTAPD_MASK) == HOSTAPD_MASK) || + ((dhd->op_mode & WFD_MASK) == WFD_MASK)) { + return TRUE; + } + else +#else + /* concurent mode with p2p interface for wfd and wlan0 for sta */ + if (((dhd->op_mode & P2P_GO_ENABLED) == P2P_GO_ENABLED) || + ((dhd->op_mode & P2P_GC_ENABLED) == P2P_GC_ENABLED)) { + DHD_ERROR(("%s P2P enabled for mode=%d\n", __FUNCTION__, dhd->op_mode)); + return TRUE; + } + else +#endif /* WL_ENABLE_P2P_IF */ +#endif /* WL_CFG80211 */ + return FALSE; +} + +#ifdef PNO_SUPPORT +int +dhd_pno_clean(dhd_pub_t *dhd) +{ + char iovbuf[128]; + int pfn_enabled = 0; + int iov_len = 0; + int ret; + + /* Disable pfn */ + iov_len = bcm_mkiovar("pfn", (char *)&pfn_enabled, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) >= 0) { + /* clear pfn */ + iov_len = bcm_mkiovar("pfnclear", 0, 0, iovbuf, sizeof(iovbuf)); + if (iov_len) { + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + iov_len, TRUE, 0)) < 0) { + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); + } + } + else { + ret = -1; + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, iov_len)); + } + } + else + DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); + + return ret; +} + +int +dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled) +{ + char iovbuf[128]; + int ret = -1; + + if ((!dhd) && ((pfn_enabled != 0) || (pfn_enabled != 1))) { + DHD_ERROR(("%s error exit\n", __FUNCTION__)); + return ret; + } + + + memset(iovbuf, 0, sizeof(iovbuf)); + +#ifndef WL_SCHED_SCAN + if (dhd_check_ap_wfd_mode_set(dhd) == TRUE) + return (ret); + + if ((pfn_enabled) && (dhd_is_associated(dhd, NULL, NULL) == TRUE)) { + DHD_ERROR(("%s pno is NOT enable : called in assoc mode , ignore\n", __FUNCTION__)); + return ret; + } +#endif /* !WL_SCHED_SCAN */ + + /* Enable/disable PNO */ + if ((ret = bcm_mkiovar("pfn", (char *)&pfn_enabled, 4, iovbuf, sizeof(iovbuf))) > 0) { + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, + iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s failed for error=%d\n", __FUNCTION__, ret)); + return ret; + } + else { + dhd->pno_enable = pfn_enabled; + DHD_TRACE(("%s set pno as %s\n", + __FUNCTION__, dhd->pno_enable ? "Enable" : "Disable")); + } + } + else DHD_ERROR(("%s failed err=%d\n", __FUNCTION__, ret)); + + return ret; +} + +/* Function to execute combined scan */ +int +dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, ushort scan_fr, + int pno_repeat, int pno_freq_expo_max) +{ + int err = -1; + char iovbuf[128]; + int k, i; + wl_pfn_param_t pfn_param; + wl_pfn_t pfn_element; + uint len = 0; + + DHD_TRACE(("%s nssid=%d nchan=%d\n", __FUNCTION__, nssid, scan_fr)); + + if ((!dhd) && (!ssids_local)) { + DHD_ERROR(("%s error exit\n", __FUNCTION__)); + err = -1; + return err; + } +#ifndef WL_SCHED_SCAN + if (dhd_check_ap_wfd_mode_set(dhd) == TRUE) + return (err); +#endif /* !WL_SCHED_SCAN */ + + /* Check for broadcast ssid */ + for (k = 0; k < nssid; k++) { + if (!ssids_local[k].SSID_len) { + DHD_ERROR(("%d: Broadcast SSID is ilegal for PNO setting\n", k)); + return err; + } + } +/* #define PNO_DUMP 1 */ +#ifdef PNO_DUMP + { + int j; + for (j = 0; j < nssid; j++) { + DHD_ERROR(("%d: scan for %s size =%d\n", j, + ssids_local[j].SSID, ssids_local[j].SSID_len)); + } + } +#endif /* PNO_DUMP */ + + /* clean up everything */ + if ((err = dhd_pno_clean(dhd)) < 0) { + DHD_ERROR(("%s failed error=%d\n", __FUNCTION__, err)); + return err; + } + memset(iovbuf, 0, sizeof(iovbuf)); + memset(&pfn_param, 0, sizeof(pfn_param)); + memset(&pfn_element, 0, sizeof(pfn_element)); + + /* set pfn parameters */ + pfn_param.version = htod32(PFN_VERSION); + pfn_param.flags = htod16((PFN_LIST_ORDER << SORT_CRITERIA_BIT)); + + /* check and set extra pno params */ + if ((pno_repeat != 0) || (pno_freq_expo_max != 0)) { + pfn_param.flags |= htod16(ENABLE << ENABLE_ADAPTSCAN_BIT); + pfn_param.repeat = (uchar) (pno_repeat); + pfn_param.exp = (uchar) (pno_freq_expo_max); + } + /* set up pno scan fr */ + if (scan_fr != 0) + pfn_param.scan_freq = htod32(scan_fr); + + if (pfn_param.scan_freq > PNO_SCAN_MAX_FW_SEC) { + DHD_ERROR(("%s pno freq above %d sec\n", __FUNCTION__, PNO_SCAN_MAX_FW_SEC)); + return err; + } + if (pfn_param.scan_freq < PNO_SCAN_MIN_FW_SEC) { + DHD_ERROR(("%s pno freq less %d sec\n", __FUNCTION__, PNO_SCAN_MIN_FW_SEC)); + return err; + } + + len = bcm_mkiovar("pfn_set", (char *)&pfn_param, sizeof(pfn_param), iovbuf, sizeof(iovbuf)); + if ((err = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, len, TRUE, 0)) < 0) { + DHD_ERROR(("%s pfn_set failed for error=%d\n", + __FUNCTION__, err)); + return err; + } + + /* set all pfn ssid */ + for (i = 0; i < nssid; i++) { + + pfn_element.infra = htod32(DOT11_BSSTYPE_INFRASTRUCTURE); + pfn_element.auth = (DOT11_OPEN_SYSTEM); + pfn_element.wpa_auth = htod32(WPA_AUTH_PFN_ANY); + pfn_element.wsec = htod32(0); + pfn_element.infra = htod32(1); + pfn_element.flags = htod32(ENABLE << WL_PFN_HIDDEN_BIT); + memcpy((char *)pfn_element.ssid.SSID, ssids_local[i].SSID, ssids_local[i].SSID_len); + pfn_element.ssid.SSID_len = ssids_local[i].SSID_len; + + if ((len = + bcm_mkiovar("pfn_add", (char *)&pfn_element, + sizeof(pfn_element), iovbuf, sizeof(iovbuf))) > 0) { + if ((err = + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, len, TRUE, 0)) < 0) { + DHD_ERROR(("%s failed for i=%d error=%d\n", + __FUNCTION__, i, err)); + return err; + } + else + DHD_TRACE(("%s set OK with PNO time=%d repeat=%d max_adjust=%d\n", + __FUNCTION__, pfn_param.scan_freq, + pfn_param.repeat, pfn_param.exp)); + } + else DHD_ERROR(("%s failed err=%d\n", __FUNCTION__, err)); + } + + /* Enable PNO */ + /* dhd_pno_enable(dhd, 1); */ + return err; +} + +int +dhd_pno_set_ex(dhd_pub_t *dhd, wl_pfn_t* ssidnet, int nssid, ushort pno_interval, + int pno_repeat, int pno_expo_max, int pno_lost_time) +{ + int err = -1; + char iovbuf[128]; + int k, i; + wl_pfn_param_t pfn_param; + wl_pfn_t pfn_element; + uint len = 0; + + DHD_TRACE(("%s nssid=%d pno_interval=%d\n", __FUNCTION__, nssid, pno_interval)); + + if ((!dhd) && (!ssidnet)) { + DHD_ERROR(("%s error exit\n", __FUNCTION__)); + err = -1; + return err; + } + + if (dhd_check_ap_wfd_mode_set(dhd) == TRUE) + return (err); + + /* Check for broadcast ssid */ + for (k = 0; k < nssid; k++) { + if (!ssidnet[k].ssid.SSID_len) { + DHD_ERROR(("%d: Broadcast SSID is ilegal for PNO setting\n", k)); + return err; + } + } +/* #define PNO_DUMP 1 */ +#ifdef PNO_DUMP + { + int j; + for (j = 0; j < nssid; j++) { + DHD_ERROR(("%d: scan for %s size =%d\n", j, + ssidnet[j].ssid.SSID, ssidnet[j].ssid.SSID_len)); + } + } +#endif /* PNO_DUMP */ + + /* clean up everything */ + if ((err = dhd_pno_clean(dhd)) < 0) { + DHD_ERROR(("%s failed error=%d\n", __FUNCTION__, err)); + return err; + } + memset(iovbuf, 0, sizeof(iovbuf)); + memset(&pfn_param, 0, sizeof(pfn_param)); + memset(&pfn_element, 0, sizeof(pfn_element)); + + /* set pfn parameters */ + pfn_param.version = htod32(PFN_VERSION); + pfn_param.flags = htod16((PFN_LIST_ORDER << SORT_CRITERIA_BIT)); + + /* check and set extra pno params */ + if ((pno_repeat != 0) || (pno_expo_max != 0)) { + pfn_param.flags |= htod16(ENABLE << ENABLE_ADAPTSCAN_BIT); + pfn_param.repeat = (uchar) (pno_repeat); + pfn_param.exp = (uchar) (pno_expo_max); + } + + /* set up pno scan fr */ + if (pno_interval != 0) + pfn_param.scan_freq = htod32(pno_interval); + + if (pfn_param.scan_freq > PNO_SCAN_MAX_FW_SEC) { + DHD_ERROR(("%s pno freq above %d sec\n", __FUNCTION__, PNO_SCAN_MAX_FW_SEC)); + return err; + } + if (pfn_param.scan_freq < PNO_SCAN_MIN_FW_SEC) { + DHD_ERROR(("%s pno freq less %d sec\n", __FUNCTION__, PNO_SCAN_MIN_FW_SEC)); + return err; + } + + /* network lost time */ + pfn_param.lost_network_timeout = htod32(pno_lost_time); + + len = bcm_mkiovar("pfn_set", (char *)&pfn_param, sizeof(pfn_param), iovbuf, sizeof(iovbuf)); + if ((err = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, len, TRUE, 0)) < 0) { + DHD_ERROR(("%s pfn_set failed for error=%d\n", + __FUNCTION__, err)); + return err; + } else { + DHD_TRACE(("%s pfn_set OK with PNO time=%d repeat=%d max_adjust=%d\n", + __FUNCTION__, pfn_param.scan_freq, + pfn_param.repeat, pfn_param.exp)); + } + + /* set all pfn ssid */ + for (i = 0; i < nssid; i++) { + pfn_element.flags = htod32(ssidnet[i].flags); + pfn_element.infra = htod32(ssidnet[i].infra); + pfn_element.auth = htod32(ssidnet[i].auth); + pfn_element.wpa_auth = htod32(ssidnet[i].wpa_auth); + pfn_element.wsec = htod32(ssidnet[i].wsec); + + memcpy((char *)pfn_element.ssid.SSID, ssidnet[i].ssid.SSID, ssidnet[i].ssid.SSID_len); + pfn_element.ssid.SSID_len = htod32(ssidnet[i].ssid.SSID_len); + + if ((len = + bcm_mkiovar("pfn_add", (char *)&pfn_element, + sizeof(pfn_element), iovbuf, sizeof(iovbuf))) > 0) { + if ((err = + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, len, TRUE, 0)) < 0) { + DHD_ERROR(("%s pfn_add failed with ssidnet[%d] error=%d\n", + __FUNCTION__, i, err)); + return err; + } else { + DHD_TRACE(("%s pfn_add OK with ssidnet[%d]\n", __FUNCTION__, i)); + } + } else { + DHD_ERROR(("%s bcm_mkiovar failed with ssidnet[%d]\n", __FUNCTION__, i)); + } + } + + return err; +} + +int +dhd_pno_get_status(dhd_pub_t *dhd) +{ + int ret = -1; + + if (!dhd) + return ret; + else + return (dhd->pno_enable); +} + +#endif /* PNO_SUPPORT */ + +#if defined(KEEP_ALIVE) +int dhd_keep_alive_onoff(dhd_pub_t *dhd) +{ + char buf[256]; + const char *str; + wl_mkeep_alive_pkt_t mkeep_alive_pkt; + wl_mkeep_alive_pkt_t *mkeep_alive_pktp; + int buf_len; + int str_len; + int res = -1; + + if (dhd_check_ap_wfd_mode_set(dhd) == TRUE) + return (res); + + DHD_TRACE(("%s execution\n", __FUNCTION__)); + + str = "mkeep_alive"; + str_len = strlen(str); + strncpy(buf, str, str_len); + buf[ str_len ] = '\0'; + mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *) (buf + str_len + 1); + mkeep_alive_pkt.period_msec = KEEP_ALIVE_PERIOD; + buf_len = str_len + 1; + mkeep_alive_pkt.version = htod16(WL_MKEEP_ALIVE_VERSION); + mkeep_alive_pkt.length = htod16(WL_MKEEP_ALIVE_FIXED_LEN); + /* Setup keep alive zero for null packet generation */ + mkeep_alive_pkt.keep_alive_id = 0; + mkeep_alive_pkt.len_bytes = 0; + buf_len += WL_MKEEP_ALIVE_FIXED_LEN; + /* Keep-alive attributes are set in local variable (mkeep_alive_pkt), and + * then memcpy'ed into buffer (mkeep_alive_pktp) since there is no + * guarantee that the buffer is properly aligned. + */ + memcpy((char *)mkeep_alive_pktp, &mkeep_alive_pkt, WL_MKEEP_ALIVE_FIXED_LEN); + + res = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, buf_len, TRUE, 0); + + return res; +} +#endif /* defined(KEEP_ALIVE) */ + +/* Android ComboSCAN support */ + +/* + * data parsing from ComboScan tlv list +*/ +int +wl_iw_parse_data_tlv(char** list_str, void *dst, int dst_size, const char token, + int input_size, int *bytes_left) +{ + char* str = *list_str; + uint16 short_temp; + uint32 int_temp; + + if ((list_str == NULL) || (*list_str == NULL) ||(bytes_left == NULL) || (*bytes_left < 0)) { + DHD_ERROR(("%s error paramters\n", __FUNCTION__)); + return -1; + } + + /* Clean all dest bytes */ + memset(dst, 0, dst_size); + while (*bytes_left > 0) { + + if (str[0] != token) { + DHD_TRACE(("%s NOT Type=%d get=%d left_parse=%d \n", + __FUNCTION__, token, str[0], *bytes_left)); + return -1; + } + + *bytes_left -= 1; + str += 1; + + if (input_size == 1) { + memcpy(dst, str, input_size); + } + else if (input_size == 2) { + memcpy(dst, (char *)htod16(memcpy(&short_temp, str, input_size)), + input_size); + } + else if (input_size == 4) { + memcpy(dst, (char *)htod32(memcpy(&int_temp, str, input_size)), + input_size); + } + + *bytes_left -= input_size; + str += input_size; + *list_str = str; + return 1; + } + return 1; +} + +/* + * channel list parsing from cscan tlv list +*/ +int +wl_iw_parse_channel_list_tlv(char** list_str, uint16* channel_list, + int channel_num, int *bytes_left) +{ + char* str = *list_str; + int idx = 0; + + if ((list_str == NULL) || (*list_str == NULL) ||(bytes_left == NULL) || (*bytes_left < 0)) { + DHD_ERROR(("%s error paramters\n", __FUNCTION__)); + return -1; + } + + while (*bytes_left > 0) { + + if (str[0] != CSCAN_TLV_TYPE_CHANNEL_IE) { + *list_str = str; + DHD_TRACE(("End channel=%d left_parse=%d %d\n", idx, *bytes_left, str[0])); + return idx; + } + /* Get proper CSCAN_TLV_TYPE_CHANNEL_IE */ + *bytes_left -= 1; + str += 1; + + if (str[0] == 0) { + /* All channels */ + channel_list[idx] = 0x0; + } + else { + channel_list[idx] = (uint16)str[0]; + DHD_TRACE(("%s channel=%d \n", __FUNCTION__, channel_list[idx])); + } + *bytes_left -= 1; + str += 1; + + if (idx++ > 255) { + DHD_ERROR(("%s Too many channels \n", __FUNCTION__)); + return -1; + } + } + + *list_str = str; + return idx; +} + +/* + * SSIDs list parsing from cscan tlv list + */ +int +wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_t* ssid, int max, int *bytes_left) +{ + char* str; + int idx = 0; + + if ((list_str == NULL) || (*list_str == NULL) || (*bytes_left < 0)) { + DHD_ERROR(("%s error paramters\n", __FUNCTION__)); + return -1; + } + str = *list_str; + while (*bytes_left > 0) { + + if (str[0] != CSCAN_TLV_TYPE_SSID_IE) { + *list_str = str; + DHD_TRACE(("nssid=%d left_parse=%d %d\n", idx, *bytes_left, str[0])); + return idx; + } + + /* Get proper CSCAN_TLV_TYPE_SSID_IE */ + *bytes_left -= 1; + str += 1; + + if (str[0] == 0) { + /* Broadcast SSID */ + ssid[idx].SSID_len = 0; + memset((char*)ssid[idx].SSID, 0x0, DOT11_MAX_SSID_LEN); + *bytes_left -= 1; + str += 1; + + DHD_TRACE(("BROADCAST SCAN left=%d\n", *bytes_left)); + } + else if (str[0] <= DOT11_MAX_SSID_LEN) { + /* Get proper SSID size */ + ssid[idx].SSID_len = str[0]; + *bytes_left -= 1; + str += 1; + + /* Get SSID */ + if (ssid[idx].SSID_len > *bytes_left) { + DHD_ERROR(("%s out of memory range len=%d but left=%d\n", + __FUNCTION__, ssid[idx].SSID_len, *bytes_left)); + return -1; + } + + memcpy((char*)ssid[idx].SSID, str, ssid[idx].SSID_len); + + *bytes_left -= ssid[idx].SSID_len; + str += ssid[idx].SSID_len; + + DHD_TRACE(("%s :size=%d left=%d\n", + (char*)ssid[idx].SSID, ssid[idx].SSID_len, *bytes_left)); + } + else { + DHD_ERROR(("### SSID size more that %d\n", str[0])); + return -1; + } + + if (idx++ > max) { + DHD_ERROR(("%s number of SSIDs more that %d\n", __FUNCTION__, idx)); + return -1; + } + } + + *list_str = str; + return idx; +} + +/* Parse a comma-separated list from list_str into ssid array, starting + * at index idx. Max specifies size of the ssid array. Parses ssids + * and returns updated idx; if idx >= max not all fit, the excess have + * not been copied. Returns -1 on empty string, or on ssid too long. + */ +int +wl_iw_parse_ssid_list(char** list_str, wlc_ssid_t* ssid, int idx, int max) +{ + char* str, *ptr; + + if ((list_str == NULL) || (*list_str == NULL)) + return -1; + + for (str = *list_str; str != NULL; str = ptr) { + + /* check for next TAG */ + if (!strncmp(str, GET_CHANNEL, strlen(GET_CHANNEL))) { + *list_str = str + strlen(GET_CHANNEL); + return idx; + } + + if ((ptr = strchr(str, ',')) != NULL) { + *ptr++ = '\0'; + } + + if (strlen(str) > DOT11_MAX_SSID_LEN) { + DHD_ERROR(("ssid <%s> exceeds %d\n", str, DOT11_MAX_SSID_LEN)); + return -1; + } + + if (strlen(str) == 0) + ssid[idx].SSID_len = 0; + + if (idx < max) { + bcm_strcpy_s((char*)ssid[idx].SSID, sizeof(ssid[idx].SSID), str); + ssid[idx].SSID_len = strlen(str); + } + idx++; + } + return idx; +} + +/* + * Parse channel list from iwpriv CSCAN + */ +int +wl_iw_parse_channel_list(char** list_str, uint16* channel_list, int channel_num) +{ + int num; + int val; + char* str; + char* endptr = NULL; + + if ((list_str == NULL)||(*list_str == NULL)) + return -1; + + str = *list_str; + num = 0; + while (strncmp(str, GET_NPROBE, strlen(GET_NPROBE))) { + val = (int)strtoul(str, &endptr, 0); + if (endptr == str) { + printf("could not parse channel number starting at" + " substring \"%s\" in list:\n%s\n", + str, *list_str); + return -1; + } + str = endptr + strspn(endptr, " ,"); + + if (num == channel_num) { + DHD_ERROR(("too many channels (more than %d) in channel list:\n%s\n", + channel_num, *list_str)); + return -1; + } + + channel_list[num++] = (uint16)val; + } + *list_str = str; + return num; +} diff --git a/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c b/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c new file mode 100644 index 0000000000000..9750eeb23bce2 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c @@ -0,0 +1,293 @@ +/* +* Customer code to add GPIO control during WLAN start/stop +* Copyright (C) 1999-2011, Broadcom Corporation +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2 (the "GPL"), +* available at http://www.broadcom.com/licenses/GPLv2.php, with the +* following added to such license: +* +* As a special exception, the copyright holders of this software give you +* permission to link this software with independent modules, and to copy and +* distribute the resulting executable under terms of your choice, provided that +* you also meet, for each linked independent module, the terms and conditions of +* the license of that module. An independent module is a module which is not +* derived from this software. The special exception does not apply to any +* modifications of the software. +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a license +* other than the GPL, without Broadcom's express prior written consent. +* +* $Id: dhd_custom_gpio.c,v 1.2.42.1 2010-10-19 00:41:09 Exp $ +*/ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define WL_ERROR(x) printf x +#define WL_TRACE(x) + +#ifdef CUSTOMER_HW +extern void bcm_wlan_power_off(int); +extern void bcm_wlan_power_on(int); +#endif /* CUSTOMER_HW */ +#if defined(CUSTOMER_HW2) +#ifdef CONFIG_WIFI_CONTROL_FUNC +int wifi_set_power(int on, unsigned long msec); +int wifi_get_irq_number(unsigned long *irq_flags_ptr); +int wifi_get_mac_addr(unsigned char *buf); +void *wifi_get_country_code(char *ccode); +#else +int wifi_set_power(int on, unsigned long msec) { return -1; } +int wifi_get_irq_number(unsigned long *irq_flags_ptr) { return -1; } +int wifi_get_mac_addr(unsigned char *buf) { return -1; } +void *wifi_get_country_code(char *ccode) { return NULL; } +#endif /* CONFIG_WIFI_CONTROL_FUNC */ +#endif /* CUSTOMER_HW2 */ + +#if defined(OOB_INTR_ONLY) + +#if defined(BCMLXSDMMC) +extern int sdioh_mmc_irq(int irq); +#endif /* (BCMLXSDMMC) */ + +#ifdef CUSTOMER_HW3 +#include +#endif + +/* Customer specific Host GPIO defintion */ +static int dhd_oob_gpio_num = -1; + +module_param(dhd_oob_gpio_num, int, 0644); +MODULE_PARM_DESC(dhd_oob_gpio_num, "DHD oob gpio number"); + +/* This function will return: + * 1) return : Host gpio interrupt number per customer platform + * 2) irq_flags_ptr : Type of Host interrupt as Level or Edge + * + * NOTE : + * Customer should check his platform definitions + * and his Host Interrupt spec + * to figure out the proper setting for his platform. + * Broadcom provides just reference settings as example. + * + */ +int dhd_customer_oob_irq_map(unsigned long *irq_flags_ptr) +{ + int host_oob_irq = 0; + +#ifdef CUSTOMER_HW2 + host_oob_irq = wifi_get_irq_number(irq_flags_ptr); + +#else +#if defined(CUSTOM_OOB_GPIO_NUM) + if (dhd_oob_gpio_num < 0) { + dhd_oob_gpio_num = CUSTOM_OOB_GPIO_NUM; + } +#endif /* CUSTOMER_HW2 */ + + if (dhd_oob_gpio_num < 0) { + WL_ERROR(("%s: ERROR customer specific Host GPIO is NOT defined \n", + __FUNCTION__)); + return (dhd_oob_gpio_num); + } + + WL_ERROR(("%s: customer specific Host GPIO number is (%d)\n", + __FUNCTION__, dhd_oob_gpio_num)); + +#if defined CUSTOMER_HW + host_oob_irq = MSM_GPIO_TO_INT(dhd_oob_gpio_num); +#elif defined CUSTOMER_HW3 + gpio_request(dhd_oob_gpio_num, "oob irq"); + host_oob_irq = gpio_to_irq(dhd_oob_gpio_num); + gpio_direction_input(dhd_oob_gpio_num); +#endif /* CUSTOMER_HW */ +#endif /* CUSTOMER_HW2 */ + + return (host_oob_irq); +} +#endif /* defined(OOB_INTR_ONLY) */ + +/* Customer function to control hw specific wlan gpios */ +void +dhd_customer_gpio_wlan_ctrl(int onoff) +{ + switch (onoff) { + case WLAN_RESET_OFF: + WL_TRACE(("%s: call customer specific GPIO to insert WLAN RESET\n", + __FUNCTION__)); +#ifdef CUSTOMER_HW + bcm_wlan_power_off(2); +#endif /* CUSTOMER_HW */ +#ifdef CUSTOMER_HW2 + wifi_set_power(0, 0); +#endif + WL_ERROR(("=========== WLAN placed in RESET ========\n")); + break; + + case WLAN_RESET_ON: + WL_TRACE(("%s: callc customer specific GPIO to remove WLAN RESET\n", + __FUNCTION__)); +#ifdef CUSTOMER_HW + bcm_wlan_power_on(2); +#endif /* CUSTOMER_HW */ +#ifdef CUSTOMER_HW2 + wifi_set_power(1, 0); +#endif + WL_ERROR(("=========== WLAN going back to live ========\n")); + break; + + case WLAN_POWER_OFF: + WL_TRACE(("%s: call customer specific GPIO to turn off WL_REG_ON\n", + __FUNCTION__)); +#ifdef CUSTOMER_HW + bcm_wlan_power_off(1); +#endif /* CUSTOMER_HW */ + break; + + case WLAN_POWER_ON: + WL_TRACE(("%s: call customer specific GPIO to turn on WL_REG_ON\n", + __FUNCTION__)); +#ifdef CUSTOMER_HW + bcm_wlan_power_on(1); + /* Lets customer power to get stable */ + OSL_DELAY(200); +#endif /* CUSTOMER_HW */ + break; + } +} + +#ifdef GET_CUSTOM_MAC_ENABLE +/* Function to get custom MAC address */ +int +dhd_custom_get_mac_address(unsigned char *buf) +{ + int ret = 0; + + WL_TRACE(("%s Enter\n", __FUNCTION__)); + if (!buf) + return -EINVAL; + + /* Customer access to MAC address stored outside of DHD driver */ +#if defined(CUSTOMER_HW2) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) + ret = wifi_get_mac_addr(buf); +#endif + +#ifdef EXAMPLE_GET_MAC + /* EXAMPLE code */ + { + struct ether_addr ea_example = {{0x00, 0x11, 0x22, 0x33, 0x44, 0xFF}}; + bcopy((char *)&ea_example, buf, sizeof(struct ether_addr)); + } +#endif /* EXAMPLE_GET_MAC */ + + return ret; +} +#endif /* GET_CUSTOM_MAC_ENABLE */ + +/* Customized Locale table : OPTIONAL feature */ +const struct cntry_locales_custom translate_custom_table[] = { +/* Table should be filled out based on custom platform regulatory requirement */ +#ifdef EXAMPLE_TABLE + {"", "XY", 4}, /* Universal if Country code is unknown or empty */ + {"US", "US", 69}, /* input ISO "US" to : US regrev 69 */ + {"CA", "US", 69}, /* input ISO "CA" to : US regrev 69 */ + {"EU", "EU", 5}, /* European union countries to : EU regrev 05 */ + {"AT", "EU", 5}, + {"BE", "EU", 5}, + {"BG", "EU", 5}, + {"CY", "EU", 5}, + {"CZ", "EU", 5}, + {"DK", "EU", 5}, + {"EE", "EU", 5}, + {"FI", "EU", 5}, + {"FR", "EU", 5}, + {"DE", "EU", 5}, + {"GR", "EU", 5}, + {"HU", "EU", 5}, + {"IE", "EU", 5}, + {"IT", "EU", 5}, + {"LV", "EU", 5}, + {"LI", "EU", 5}, + {"LT", "EU", 5}, + {"LU", "EU", 5}, + {"MT", "EU", 5}, + {"NL", "EU", 5}, + {"PL", "EU", 5}, + {"PT", "EU", 5}, + {"RO", "EU", 5}, + {"SK", "EU", 5}, + {"SI", "EU", 5}, + {"ES", "EU", 5}, + {"SE", "EU", 5}, + {"GB", "EU", 5}, + {"KR", "XY", 3}, + {"AU", "XY", 3}, + {"CN", "XY", 3}, /* input ISO "CN" to : XY regrev 03 */ + {"TW", "XY", 3}, + {"AR", "XY", 3}, + {"MX", "XY", 3}, + {"IL", "IL", 0}, + {"CH", "CH", 0}, + {"TR", "TR", 0}, + {"NO", "NO", 0}, +#endif /* EXMAPLE_TABLE */ +}; + + +/* Customized Locale convertor +* input : ISO 3166-1 country abbreviation +* output: customized cspec +*/ +void get_customized_country_code(char *country_iso_code, wl_country_t *cspec) +{ +#if defined(CUSTOMER_HW2) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) + + struct cntry_locales_custom *cloc_ptr; + + if (!cspec) + return; + + cloc_ptr = wifi_get_country_code(country_iso_code); + if (cloc_ptr) { + strlcpy(cspec->ccode, cloc_ptr->custom_locale, WLC_CNTRY_BUF_SZ); + cspec->rev = cloc_ptr->custom_locale_rev; + } + return; +#else + int size, i; + + size = ARRAYSIZE(translate_custom_table); + + if (cspec == 0) + return; + + if (size == 0) + return; + + for (i = 0; i < size; i++) { + if (strcmp(country_iso_code, translate_custom_table[i].iso_abbrev) == 0) { + memcpy(cspec->ccode, + translate_custom_table[i].custom_locale, WLC_CNTRY_BUF_SZ); + cspec->rev = translate_custom_table[i].custom_locale_rev; + return; + } + } +#ifdef EXAMPLE_TABLE + /* if no country code matched return first universal code from translate_custom_table */ + memcpy(cspec->ccode, translate_custom_table[0].custom_locale, WLC_CNTRY_BUF_SZ); + cspec->rev = translate_custom_table[0].custom_locale_rev; +#endif /* EXMAPLE_TABLE */ + return; +#endif /* defined(CUSTOMER_HW2) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) */ +} diff --git a/drivers/net/wireless/bcmdhd/dhd_dbg.h b/drivers/net/wireless/bcmdhd/dhd_dbg.h new file mode 100644 index 0000000000000..01be6a1f056f5 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_dbg.h @@ -0,0 +1,105 @@ +/* + * Debug/trace/assert driver definitions for Dongle Host Driver. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_dbg.h 285933 2011-09-23 21:45:31Z $ + */ + +#ifndef _dhd_dbg_ +#define _dhd_dbg_ + +#if defined(DHD_DEBUG) + +#define DHD_ERROR(args) do {if ((dhd_msg_level & DHD_ERROR_VAL) && (net_ratelimit())) \ + printf args;} while (0) +#define DHD_TRACE(args) do {if (dhd_msg_level & DHD_TRACE_VAL) printf args;} while (0) +#define DHD_INFO(args) do {if (dhd_msg_level & DHD_INFO_VAL) printf args;} while (0) +#define DHD_DATA(args) do {if (dhd_msg_level & DHD_DATA_VAL) printf args;} while (0) +#define DHD_CTL(args) do {if (dhd_msg_level & DHD_CTL_VAL) printf args;} while (0) +#define DHD_TIMER(args) do {if (dhd_msg_level & DHD_TIMER_VAL) printf args;} while (0) +#define DHD_HDRS(args) do {if (dhd_msg_level & DHD_HDRS_VAL) printf args;} while (0) +#define DHD_BYTES(args) do {if (dhd_msg_level & DHD_BYTES_VAL) printf args;} while (0) +#define DHD_INTR(args) do {if (dhd_msg_level & DHD_INTR_VAL) printf args;} while (0) +#define DHD_GLOM(args) do {if (dhd_msg_level & DHD_GLOM_VAL) printf args;} while (0) +#define DHD_EVENT(args) do {if (dhd_msg_level & DHD_EVENT_VAL) printf args;} while (0) +#define DHD_BTA(args) do {if (dhd_msg_level & DHD_BTA_VAL) printf args;} while (0) +#define DHD_ISCAN(args) do {if (dhd_msg_level & DHD_ISCAN_VAL) printf args;} while (0) +#define DHD_ARPOE(args) do {if (dhd_msg_level & DHD_ARPOE_VAL) printf args;} while (0) + +#define DHD_ERROR_ON() (dhd_msg_level & DHD_ERROR_VAL) +#define DHD_TRACE_ON() (dhd_msg_level & DHD_TRACE_VAL) +#define DHD_INFO_ON() (dhd_msg_level & DHD_INFO_VAL) +#define DHD_DATA_ON() (dhd_msg_level & DHD_DATA_VAL) +#define DHD_CTL_ON() (dhd_msg_level & DHD_CTL_VAL) +#define DHD_TIMER_ON() (dhd_msg_level & DHD_TIMER_VAL) +#define DHD_HDRS_ON() (dhd_msg_level & DHD_HDRS_VAL) +#define DHD_BYTES_ON() (dhd_msg_level & DHD_BYTES_VAL) +#define DHD_INTR_ON() (dhd_msg_level & DHD_INTR_VAL) +#define DHD_GLOM_ON() (dhd_msg_level & DHD_GLOM_VAL) +#define DHD_EVENT_ON() (dhd_msg_level & DHD_EVENT_VAL) +#define DHD_BTA_ON() (dhd_msg_level & DHD_BTA_VAL) +#define DHD_ISCAN_ON() (dhd_msg_level & DHD_ISCAN_VAL) +#define DHD_ARPOE_ON() (dhd_msg_level & DHD_ARPOE_VAL) + +#else /* defined(BCMDBG) || defined(DHD_DEBUG) */ + +#define DHD_ERROR(args) do {if (net_ratelimit()) printf args;} while (0) +#define DHD_TRACE(args) +#define DHD_INFO(args) +#define DHD_DATA(args) +#define DHD_CTL(args) +#define DHD_TIMER(args) +#define DHD_HDRS(args) +#define DHD_BYTES(args) +#define DHD_INTR(args) +#define DHD_GLOM(args) +#define DHD_EVENT(args) +#define DHD_BTA(args) +#define DHD_ISCAN(args) +#define DHD_ARPOE(args) + +#define DHD_ERROR_ON() 0 +#define DHD_TRACE_ON() 0 +#define DHD_INFO_ON() 0 +#define DHD_DATA_ON() 0 +#define DHD_CTL_ON() 0 +#define DHD_TIMER_ON() 0 +#define DHD_HDRS_ON() 0 +#define DHD_BYTES_ON() 0 +#define DHD_INTR_ON() 0 +#define DHD_GLOM_ON() 0 +#define DHD_EVENT_ON() 0 +#define DHD_BTA_ON() 0 +#define DHD_ISCAN_ON() 0 +#define DHD_ARPOE_ON() 0 +#endif + +#define DHD_LOG(args) + +#define DHD_BLOG(cp, size) +#define DHD_NONE(args) +extern int dhd_msg_level; + +/* Defines msg bits */ +#include + +#endif /* _dhd_dbg_ */ diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c new file mode 100644 index 0000000000000..d8c1c1b008aa3 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -0,0 +1,5366 @@ +/* + * Broadcom Dongle Host Driver (DHD), Linux-specific network interface + * Basically selected code segments from usb-cdc.c and usb-rndis.c + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_linux.c 333885 2012-05-18 00:39:03Z $ + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_WAKELOCK +#include +#endif +#ifdef WL_CFG80211 +#include +#endif + +#include +#include +#include + +#ifdef WLMEDIA_HTSF +#include +#include + +#define HTSF_MINLEN 200 /* min. packet length to timestamp */ +#define HTSF_BUS_DELAY 150 /* assume a fix propagation in us */ +#define TSMAX 1000 /* max no. of timing record kept */ +#define NUMBIN 34 + +static uint32 tsidx = 0; +static uint32 htsf_seqnum = 0; +uint32 tsfsync; +struct timeval tsync; +static uint32 tsport = 5010; + +typedef struct histo_ { + uint32 bin[NUMBIN]; +} histo_t; + +#if !ISPOWEROF2(DHD_SDALIGN) +#error DHD_SDALIGN is not a power of 2! +#endif + +static histo_t vi_d1, vi_d2, vi_d3, vi_d4; +#endif /* WLMEDIA_HTSF */ + +#if defined(SOFTAP) +extern bool ap_cfg_running; +extern bool ap_fw_loaded; +#endif + +/* enable HOSTIP cache update from the host side when an eth0:N is up */ +#define AOE_IP_ALIAS_SUPPORT 1 + +#ifdef PROP_TXSTATUS +#include +#include +#endif + +#include + +#ifdef ARP_OFFLOAD_SUPPORT +void aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add); +static int dhd_device_event(struct notifier_block *this, + unsigned long event, + void *ptr); + +static struct notifier_block dhd_notifier = { + .notifier_call = dhd_device_event +}; +#endif /* ARP_OFFLOAD_SUPPORT */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) +#include +volatile bool dhd_mmc_suspend = FALSE; +DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ + +#if defined(OOB_INTR_ONLY) +extern void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable); +#endif /* defined(OOB_INTR_ONLY) */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +static void dhd_hang_process(struct work_struct *work); +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +MODULE_LICENSE("GPL v2"); +#endif /* LinuxVer */ + +#include + +#ifndef PROP_TXSTATUS +#define DBUS_RX_BUFFER_SIZE_DHD(net) (net->mtu + net->hard_header_len + dhd->pub.hdrlen) +#else +#define DBUS_RX_BUFFER_SIZE_DHD(net) (net->mtu + net->hard_header_len + dhd->pub.hdrlen + 128) +#endif + +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) +const char * +print_tainted() +{ + return ""; +} +#endif /* LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) */ + +/* Linux wireless extension support */ +#if defined(WL_WIRELESS_EXT) +#include +extern wl_iw_extra_params_t g_wl_iw_params; +#endif /* defined(WL_WIRELESS_EXT) */ + +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) +#include +#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ +extern int dhd_get_dtim_skip(dhd_pub_t *dhd); + +#ifdef PKT_FILTER_SUPPORT +extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg); +extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode); +#endif + +/* Interface control information */ +typedef struct dhd_if { + struct dhd_info *info; /* back pointer to dhd_info */ + /* OS/stack specifics */ + struct net_device *net; + struct net_device_stats stats; + int idx; /* iface idx in dongle */ + dhd_if_state_t state; /* interface state */ + uint subunit; /* subunit */ + uint8 mac_addr[ETHER_ADDR_LEN]; /* assigned MAC address */ + bool attached; /* Delayed attachment when unset */ + bool txflowcontrol; /* Per interface flow control indicator */ + char name[IFNAMSIZ+1]; /* linux interface name */ + uint8 bssidx; /* bsscfg index for the interface */ + bool set_multicast; +} dhd_if_t; + +#ifdef WLMEDIA_HTSF +typedef struct { + uint32 low; + uint32 high; +} tsf_t; + +typedef struct { + uint32 last_cycle; + uint32 last_sec; + uint32 last_tsf; + uint32 coef; /* scaling factor */ + uint32 coefdec1; /* first decimal */ + uint32 coefdec2; /* second decimal */ +} htsf_t; + +typedef struct { + uint32 t1; + uint32 t2; + uint32 t3; + uint32 t4; +} tstamp_t; + +static tstamp_t ts[TSMAX]; +static tstamp_t maxdelayts; +static uint32 maxdelay = 0, tspktcnt = 0, maxdelaypktno = 0; + +#endif /* WLMEDIA_HTSF */ + +/* Local private structure (extension of pub) */ +typedef struct dhd_info { +#if defined(WL_WIRELESS_EXT) + wl_iw_t iw; /* wireless extensions state (must be first) */ +#endif /* defined(WL_WIRELESS_EXT) */ + + dhd_pub_t pub; + + /* For supporting multiple interfaces */ + dhd_if_t *iflist[DHD_MAX_IFS]; + + struct semaphore proto_sem; +#ifdef PROP_TXSTATUS + spinlock_t wlfc_spinlock; +#endif /* PROP_TXSTATUS */ +#ifdef WLMEDIA_HTSF + htsf_t htsf; +#endif + wait_queue_head_t ioctl_resp_wait; + struct timer_list timer; + bool wd_timer_valid; + struct tasklet_struct tasklet; + spinlock_t sdlock; + spinlock_t txqlock; + spinlock_t dhd_lock; +#ifdef DHDTHREAD + /* Thread based operation */ + bool threads_only; + struct semaphore sdsem; + + tsk_ctl_t thr_dpc_ctl; + tsk_ctl_t thr_wdt_ctl; + +#else + bool dhd_tasklet_create; +#endif /* DHDTHREAD */ + tsk_ctl_t thr_sysioc_ctl; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + struct work_struct work_hang; +#endif + + /* Wakelocks */ +#if defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + struct wake_lock wl_wifi; /* Wifi wakelock */ + struct wake_lock wl_rxwake; /* Wifi rx wakelock */ + struct wake_lock wl_ctrlwake; /* Wifi ctrl wakelock */ +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + /* net_device interface lock, prevent race conditions among net_dev interface + * calls and wifi_on or wifi_off + */ + struct mutex dhd_net_if_mutex; + struct mutex dhd_suspend_mutex; +#endif + spinlock_t wakelock_spinlock; + int wakelock_counter; + int wakelock_rx_timeout_enable; + int wakelock_ctrl_timeout_enable; + + /* Thread to issue ioctl for multicast */ + bool set_macaddress; + struct ether_addr macvalue; + wait_queue_head_t ctrl_wait; + atomic_t pend_8021x_cnt; + dhd_attach_states_t dhd_state; + +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +#ifdef ARP_OFFLOAD_SUPPORT + u32 pend_ipaddr; +#endif /* ARP_OFFLOAD_SUPPORT */ +} dhd_info_t; + +/* Definitions to provide path to the firmware and nvram + * example nvram_path[MOD_PARAM_PATHLEN]="/projects/wlan/nvram.txt" + */ +char firmware_path[MOD_PARAM_PATHLEN]; +char nvram_path[MOD_PARAM_PATHLEN]; + +int op_mode = 0; +module_param(op_mode, int, 0644); +extern int wl_control_wl_start(struct net_device *dev); +extern int net_os_send_hang_message(struct net_device *dev); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +struct semaphore dhd_registration_sem; +#define DHD_REGISTRATION_TIMEOUT 12000 /* msec : allowed time to finished dhd registration */ +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ + +/* Spawn a thread for system ioctls (set mac, set mcast) */ +uint dhd_sysioc = TRUE; +module_param(dhd_sysioc, uint, 0); + +/* Error bits */ +module_param(dhd_msg_level, int, 0); + +/* load firmware and/or nvram values from the filesystem */ +module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0660); +module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0); + +/* Watchdog interval */ +uint dhd_watchdog_ms = 10; +module_param(dhd_watchdog_ms, uint, 0); + +#if defined(DHD_DEBUG) +/* Console poll interval */ +uint dhd_console_ms = 0; +module_param(dhd_console_ms, uint, 0644); +#endif /* defined(DHD_DEBUG) */ + +/* ARP offload agent mode : Enable ARP Host Auto-Reply and ARP Peer Auto-Reply */ +uint dhd_arp_mode = 0xb; +module_param(dhd_arp_mode, uint, 0); + +/* ARP offload enable */ +uint dhd_arp_enable = TRUE; +module_param(dhd_arp_enable, uint, 0); + +/* Global Pkt filter enable control */ +uint dhd_pkt_filter_enable = TRUE; +module_param(dhd_pkt_filter_enable, uint, 0); + +/* Pkt filter init setup */ +uint dhd_pkt_filter_init = 0; +module_param(dhd_pkt_filter_init, uint, 0); + +/* Pkt filter mode control */ +uint dhd_master_mode = TRUE; +module_param(dhd_master_mode, uint, 0); + +#ifdef DHDTHREAD +/* Watchdog thread priority, -1 to use kernel timer */ +int dhd_watchdog_prio = 97; +module_param(dhd_watchdog_prio, int, 0); + +/* DPC thread priority, -1 to use tasklet */ +int dhd_dpc_prio = 98; +module_param(dhd_dpc_prio, int, 0); + +/* DPC thread priority, -1 to use tasklet */ +extern int dhd_dongle_memsize; +module_param(dhd_dongle_memsize, int, 0); +#endif /* DHDTHREAD */ +/* Control fw roaming */ +uint dhd_roam_disable = 0; + +/* Control radio state */ +uint dhd_radio_up = 1; + +/* Network inteface name */ +char iface_name[IFNAMSIZ] = {'\0'}; +module_param_string(iface_name, iface_name, IFNAMSIZ, 0); + +/* The following are specific to the SDIO dongle */ + +/* IOCTL response timeout */ +int dhd_ioctl_timeout_msec = IOCTL_RESP_TIMEOUT; + +/* Idle timeout for backplane clock */ +int dhd_idletime = DHD_IDLETIME_TICKS; +module_param(dhd_idletime, int, 0); + +/* Use polling */ +uint dhd_poll = FALSE; +module_param(dhd_poll, uint, 0); + +/* Use interrupts */ +uint dhd_intr = TRUE; +module_param(dhd_intr, uint, 0); + +/* SDIO Drive Strength (in milliamps) */ +uint dhd_sdiod_drive_strength = 6; +module_param(dhd_sdiod_drive_strength, uint, 0); + +/* Tx/Rx bounds */ +extern uint dhd_txbound; +extern uint dhd_rxbound; +module_param(dhd_txbound, uint, 0); +module_param(dhd_rxbound, uint, 0); + +/* Deferred transmits */ +extern uint dhd_deferred_tx; +module_param(dhd_deferred_tx, uint, 0); + +#ifdef BCMDBGFS +extern void dhd_dbg_init(dhd_pub_t *dhdp); +extern void dhd_dbg_remove(void); +#endif /* BCMDBGFS */ + + + +#ifdef SDTEST +/* Echo packet generator (pkts/s) */ +uint dhd_pktgen = 0; +module_param(dhd_pktgen, uint, 0); + +/* Echo packet len (0 => sawtooth, max 2040) */ +uint dhd_pktgen_len = 0; +module_param(dhd_pktgen_len, uint, 0); +#endif /* SDTEST */ + +/* Version string to report */ +#ifdef DHD_DEBUG +#ifndef SRCBASE +#define SRCBASE "drivers/net/wireless/bcmdhd" +#endif +#define DHD_COMPILED "\nCompiled in " SRCBASE +#else +#define DHD_COMPILED +#endif /* DHD_DEBUG */ + +static char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR +#ifdef DHD_DEBUG +"\nCompiled in " SRCBASE " on " __DATE__ " at " __TIME__ +#endif +; +static void dhd_net_if_lock_local(dhd_info_t *dhd); +static void dhd_net_if_unlock_local(dhd_info_t *dhd); +static void dhd_suspend_lock(dhd_pub_t *dhdp); +static void dhd_suspend_unlock(dhd_pub_t *dhdp); +#if !defined(AP) && defined(WLP2P) && defined(WL_ENABLE_P2P_IF) +static u32 dhd_concurrent_fw(dhd_pub_t *dhd); +#endif + +#ifdef WLMEDIA_HTSF +void htsf_update(dhd_info_t *dhd, void *data); +tsf_t prev_tsf, cur_tsf; + +uint32 dhd_get_htsf(dhd_info_t *dhd, int ifidx); +static int dhd_ioctl_htsf_get(dhd_info_t *dhd, int ifidx); +static void dhd_dump_latency(void); +static void dhd_htsf_addtxts(dhd_pub_t *dhdp, void *pktbuf); +static void dhd_htsf_addrxts(dhd_pub_t *dhdp, void *pktbuf); +static void dhd_dump_htsfhisto(histo_t *his, char *s); +#endif /* WLMEDIA_HTSF */ + +/* Monitor interface */ +int dhd_monitor_init(void *dhd_pub); +int dhd_monitor_uninit(void); + + +#if defined(WL_WIRELESS_EXT) +struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev); +#endif /* defined(WL_WIRELESS_EXT) */ + +static void dhd_dpc(ulong data); +/* forward decl */ +extern int dhd_wait_pend8021x(struct net_device *dev); + +#ifdef TOE +#ifndef BDC +#error TOE requires BDC +#endif /* !BDC */ +static int dhd_toe_get(dhd_info_t *dhd, int idx, uint32 *toe_ol); +static int dhd_toe_set(dhd_info_t *dhd, int idx, uint32 toe_ol); +#endif /* TOE */ + +static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, + wl_event_msg_t *event_ptr, void **data_ptr); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) +static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long action, void *ignored) +{ + int ret = NOTIFY_DONE; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) + switch (action) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + dhd_mmc_suspend = TRUE; + ret = NOTIFY_OK; + break; + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + dhd_mmc_suspend = FALSE; + ret = NOTIFY_OK; + break; + } + smp_mb(); +#endif + return ret; +} + +static struct notifier_block dhd_sleep_pm_notifier = { + .notifier_call = dhd_sleep_pm_callback, + .priority = 10 +}; +extern int register_pm_notifier(struct notifier_block *nb); +extern int unregister_pm_notifier(struct notifier_block *nb); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ + +static void dhd_set_packet_filter(int value, dhd_pub_t *dhd) +{ +#ifdef PKT_FILTER_SUPPORT + DHD_TRACE(("%s: %d\n", __FUNCTION__, value)); + /* 1 - Enable packet filter, only allow unicast packet to send up */ + /* 0 - Disable packet filter */ + if (dhd_pkt_filter_enable && (!value || + (dhd_check_ap_wfd_mode_set(dhd) == FALSE))) { + int i; + + for (i = 0; i < dhd->pktfilter_count; i++) { + dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]); + dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i], + value, dhd_master_mode); + } + } +#endif +} + +static int dhd_set_suspend(int value, dhd_pub_t *dhd) +{ + int power_mode = PM_MAX; + /* wl_pkt_filter_enable_t enable_parm; */ + char iovbuf[32]; + int bcn_li_dtim = 3; + uint roamvar = 1; + + DHD_TRACE(("%s: enter, value = %d in_suspend=%d\n", + __FUNCTION__, value, dhd->in_suspend)); + + dhd_suspend_lock(dhd); + if (dhd && dhd->up) { + if (value && dhd->in_suspend) { + + /* Kernel suspended */ + DHD_ERROR(("%s: force extra Suspend setting \n", __FUNCTION__)); + + dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, + sizeof(power_mode), TRUE, 0); + + /* Enable packet filter, only allow unicast packet to send up */ + dhd_set_packet_filter(1, dhd); + + /* If DTIM skip is set up as default, force it to wake + * each third DTIM for better power savings. Note that + * one side effect is a chance to miss BC/MC packet. + */ + bcn_li_dtim = dhd_get_dtim_skip(dhd); + bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, + 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + + /* Disable firmware roaming during suspend */ + bcm_mkiovar("roam_off", (char *)&roamvar, 4, + iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + } else { + + /* Kernel resumed */ + DHD_TRACE(("%s: Remove extra suspend setting \n", __FUNCTION__)); + + power_mode = PM_FAST; + dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, + sizeof(power_mode), TRUE, 0); + + /* disable pkt filter */ + dhd_set_packet_filter(0, dhd); + + /* restore pre-suspend setting for dtim_skip */ + bcm_mkiovar("bcn_li_dtim", (char *)&dhd->dtim_skip, + 4, iovbuf, sizeof(iovbuf)); + + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + roamvar = dhd_roam_disable; + bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, + sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + } + } + dhd_suspend_unlock(dhd); + return 0; +} + +static int dhd_suspend_resume_helper(struct dhd_info *dhd, int val, int force) +{ + dhd_pub_t *dhdp = &dhd->pub; + int ret = 0; + + DHD_OS_WAKE_LOCK(dhdp); + /* Set flag when early suspend was called */ + dhdp->in_suspend = val; + if ((force || !dhdp->suspend_disable_flag) && + (dhd_check_ap_wfd_mode_set(dhdp) == FALSE)) { + ret = dhd_set_suspend(val, dhdp); + } + DHD_OS_WAKE_UNLOCK(dhdp); + return ret; +} + +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) +static void dhd_early_suspend(struct early_suspend *h) +{ + struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend); + + DHD_TRACE(("%s: enter\n", __FUNCTION__)); + + if (dhd) + dhd_suspend_resume_helper(dhd, 1, 0); +} + +static void dhd_late_resume(struct early_suspend *h) +{ + struct dhd_info *dhd = container_of(h, struct dhd_info, early_suspend); + + DHD_TRACE(("%s: enter\n", __FUNCTION__)); + + if (dhd) + dhd_suspend_resume_helper(dhd, 0, 0); +} +#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ + +/* + * Generalized timeout mechanism. Uses spin sleep with exponential back-off until + * the sleep time reaches one jiffy, then switches over to task delay. Usage: + * + * dhd_timeout_start(&tmo, usec); + * while (!dhd_timeout_expired(&tmo)) + * if (poll_something()) + * break; + * if (dhd_timeout_expired(&tmo)) + * fatal(); + */ + +void +dhd_timeout_start(dhd_timeout_t *tmo, uint usec) +{ + tmo->limit = usec; + tmo->increment = 0; + tmo->elapsed = 0; + tmo->tick = 1000000 / HZ; +} + +int +dhd_timeout_expired(dhd_timeout_t *tmo) +{ + /* Does nothing the first call */ + if (tmo->increment == 0) { + tmo->increment = 1; + return 0; + } + + if (tmo->elapsed >= tmo->limit) + return 1; + + /* Add the delay that's about to take place */ + tmo->elapsed += tmo->increment; + + if (tmo->increment < tmo->tick) { + OSL_DELAY(tmo->increment); + tmo->increment *= 2; + if (tmo->increment > tmo->tick) + tmo->increment = tmo->tick; + } else { + wait_queue_head_t delay_wait; + DECLARE_WAITQUEUE(wait, current); + init_waitqueue_head(&delay_wait); + add_wait_queue(&delay_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + remove_wait_queue(&delay_wait, &wait); + set_current_state(TASK_RUNNING); + } + + return 0; +} + +int +dhd_net2idx(dhd_info_t *dhd, struct net_device *net) +{ + int i = 0; + + ASSERT(dhd); + while (i < DHD_MAX_IFS) { + if (dhd->iflist[i] && (dhd->iflist[i]->net == net)) + return i; + i++; + } + + return DHD_BAD_IF; +} + +struct net_device * dhd_idx2net(void *pub, int ifidx) +{ + struct dhd_pub *dhd_pub = (struct dhd_pub *)pub; + struct dhd_info *dhd_info; + + if (!dhd_pub || ifidx < 0 || ifidx >= DHD_MAX_IFS) + return NULL; + dhd_info = dhd_pub->info; + if (dhd_info && dhd_info->iflist[ifidx]) + return dhd_info->iflist[ifidx]->net; + return NULL; +} + +int +dhd_ifname2idx(dhd_info_t *dhd, char *name) +{ + int i = DHD_MAX_IFS; + + ASSERT(dhd); + + if (name == NULL || *name == '\0') + return 0; + + while (--i > 0) + if (dhd->iflist[i] && !strncmp(dhd->iflist[i]->name, name, IFNAMSIZ)) + break; + + DHD_TRACE(("%s: return idx %d for \"%s\"\n", __FUNCTION__, i, name)); + + return i; /* default - the primary interface */ +} + +char * +dhd_ifname(dhd_pub_t *dhdp, int ifidx) +{ + dhd_info_t *dhd = (dhd_info_t *)dhdp->info; + + ASSERT(dhd); + + if (ifidx < 0 || ifidx >= DHD_MAX_IFS) { + DHD_ERROR(("%s: ifidx %d out of range\n", __FUNCTION__, ifidx)); + return ""; + } + + if (dhd->iflist[ifidx] == NULL) { + DHD_ERROR(("%s: null i/f %d\n", __FUNCTION__, ifidx)); + return ""; + } + + if (dhd->iflist[ifidx]->net) + return dhd->iflist[ifidx]->net->name; + + return ""; +} + +uint8 * +dhd_bssidx2bssid(dhd_pub_t *dhdp, int idx) +{ + int i; + dhd_info_t *dhd = (dhd_info_t *)dhdp; + + ASSERT(dhd); + for (i = 0; i < DHD_MAX_IFS; i++) + if (dhd->iflist[i] && dhd->iflist[i]->bssidx == idx) + return dhd->iflist[i]->mac_addr; + + return NULL; +} + + +static void +_dhd_set_multicast_list(dhd_info_t *dhd, int ifidx) +{ + struct net_device *dev; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + struct netdev_hw_addr *ha; +#else + struct dev_mc_list *mclist; +#endif + uint32 allmulti, cnt; + + wl_ioctl_t ioc; + char *buf, *bufp; + uint buflen; + int ret; + + ASSERT(dhd && dhd->iflist[ifidx]); + dev = dhd->iflist[ifidx]->net; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) + netif_addr_lock_bh(dev); +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + cnt = netdev_mc_count(dev); +#else + cnt = dev->mc_count; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) + netif_addr_unlock_bh(dev); +#endif + + /* Determine initial value of allmulti flag */ + allmulti = (dev->flags & IFF_ALLMULTI) ? TRUE : FALSE; + + /* Send down the multicast list first. */ + + + buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETHER_ADDR_LEN); + if (!(bufp = buf = MALLOC(dhd->pub.osh, buflen))) { + DHD_ERROR(("%s: out of memory for mcast_list, cnt %d\n", + dhd_ifname(&dhd->pub, ifidx), cnt)); + return; + } + + strcpy(bufp, "mcast_list"); + bufp += strlen("mcast_list") + 1; + + cnt = htol32(cnt); + memcpy(bufp, &cnt, sizeof(cnt)); + bufp += sizeof(cnt); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) + netif_addr_lock_bh(dev); +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + netdev_for_each_mc_addr(ha, dev) { + if (!cnt) + break; + memcpy(bufp, ha->addr, ETHER_ADDR_LEN); + bufp += ETHER_ADDR_LEN; + cnt--; + } +#else + for (mclist = dev->mc_list; (mclist && (cnt > 0)); cnt--, mclist = mclist->next) { + memcpy(bufp, (void *)mclist->dmi_addr, ETHER_ADDR_LEN); + bufp += ETHER_ADDR_LEN; + } +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) + netif_addr_unlock_bh(dev); +#endif + + memset(&ioc, 0, sizeof(ioc)); + ioc.cmd = WLC_SET_VAR; + ioc.buf = buf; + ioc.len = buflen; + ioc.set = TRUE; + + ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); + if (ret < 0) { + DHD_ERROR(("%s: set mcast_list failed, cnt %d\n", + dhd_ifname(&dhd->pub, ifidx), cnt)); + allmulti = cnt ? TRUE : allmulti; + } + + MFREE(dhd->pub.osh, buf, buflen); + + /* Now send the allmulti setting. This is based on the setting in the + * net_device flags, but might be modified above to be turned on if we + * were trying to set some addresses and dongle rejected it... + */ + + buflen = sizeof("allmulti") + sizeof(allmulti); + if (!(buf = MALLOC(dhd->pub.osh, buflen))) { + DHD_ERROR(("%s: out of memory for allmulti\n", dhd_ifname(&dhd->pub, ifidx))); + return; + } + allmulti = htol32(allmulti); + + if (!bcm_mkiovar("allmulti", (void*)&allmulti, sizeof(allmulti), buf, buflen)) { + DHD_ERROR(("%s: mkiovar failed for allmulti, datalen %d buflen %u\n", + dhd_ifname(&dhd->pub, ifidx), (int)sizeof(allmulti), buflen)); + MFREE(dhd->pub.osh, buf, buflen); + return; + } + + + memset(&ioc, 0, sizeof(ioc)); + ioc.cmd = WLC_SET_VAR; + ioc.buf = buf; + ioc.len = buflen; + ioc.set = TRUE; + + ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); + if (ret < 0) { + DHD_ERROR(("%s: set allmulti %d failed\n", + dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti))); + } + + MFREE(dhd->pub.osh, buf, buflen); + + /* Finally, pick up the PROMISC flag as well, like the NIC driver does */ + + allmulti = (dev->flags & IFF_PROMISC) ? TRUE : FALSE; + allmulti = htol32(allmulti); + + memset(&ioc, 0, sizeof(ioc)); + ioc.cmd = WLC_SET_PROMISC; + ioc.buf = &allmulti; + ioc.len = sizeof(allmulti); + ioc.set = TRUE; + + ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); + if (ret < 0) { + DHD_ERROR(("%s: set promisc %d failed\n", + dhd_ifname(&dhd->pub, ifidx), ltoh32(allmulti))); + } +} + +static int +_dhd_set_mac_address(dhd_info_t *dhd, int ifidx, struct ether_addr *addr) +{ + char buf[32]; + wl_ioctl_t ioc; + int ret; + + if (!bcm_mkiovar("cur_etheraddr", (char*)addr, ETHER_ADDR_LEN, buf, 32)) { + DHD_ERROR(("%s: mkiovar failed for cur_etheraddr\n", dhd_ifname(&dhd->pub, ifidx))); + return -1; + } + memset(&ioc, 0, sizeof(ioc)); + ioc.cmd = WLC_SET_VAR; + ioc.buf = buf; + ioc.len = 32; + ioc.set = TRUE; + + ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len); + if (ret < 0) { + DHD_ERROR(("%s: set cur_etheraddr failed\n", dhd_ifname(&dhd->pub, ifidx))); + } else { + memcpy(dhd->iflist[ifidx]->net->dev_addr, addr, ETHER_ADDR_LEN); + memcpy(dhd->pub.mac.octet, addr, ETHER_ADDR_LEN); + } + + return ret; +} + +#ifdef SOFTAP +extern struct net_device *ap_net_dev; +extern tsk_ctl_t ap_eth_ctl; /* ap netdev heper thread ctl */ +#endif + +static void +dhd_op_if(dhd_if_t *ifp) +{ + dhd_info_t *dhd; + int ret = 0, err = 0; +#ifdef SOFTAP + unsigned long flags; +#endif + + if (!ifp || !ifp->info || !ifp->idx) + return; + ASSERT(ifp && ifp->info && ifp->idx); /* Virtual interfaces only */ + dhd = ifp->info; + + DHD_TRACE(("%s: idx %d, state %d\n", __FUNCTION__, ifp->idx, ifp->state)); + +#ifdef WL_CFG80211 + if (wl_cfg80211_is_progress_ifchange()) + return; + +#endif + switch (ifp->state) { + case DHD_IF_ADD: + /* + * Delete the existing interface before overwriting it + * in case we missed the WLC_E_IF_DEL event. + */ + if (ifp->net != NULL) { + DHD_ERROR(("%s: ERROR: netdev:%s already exists, try free & unregister \n", + __FUNCTION__, ifp->net->name)); + netif_stop_queue(ifp->net); + unregister_netdev(ifp->net); + free_netdev(ifp->net); + } + /* Allocate etherdev, including space for private structure */ + if (!(ifp->net = alloc_etherdev(sizeof(dhd)))) { + DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__)); + ret = -ENOMEM; + } + if (ret == 0) { + strncpy(ifp->net->name, ifp->name, IFNAMSIZ); + ifp->net->name[IFNAMSIZ - 1] = '\0'; + memcpy(netdev_priv(ifp->net), &dhd, sizeof(dhd)); +#ifdef WL_CFG80211 + if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) + if (!wl_cfg80211_notify_ifadd(ifp->net, ifp->idx, ifp->bssidx, + (void*)dhd_net_attach)) { + ifp->state = DHD_IF_NONE; + return; + } +#endif + if ((err = dhd_net_attach(&dhd->pub, ifp->idx)) != 0) { + DHD_ERROR(("%s: dhd_net_attach failed, err %d\n", + __FUNCTION__, err)); + ret = -EOPNOTSUPP; + } else { +#if defined(SOFTAP) + if (ap_fw_loaded && !(dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) { + /* semaphore that the soft AP CODE waits on */ + flags = dhd_os_spin_lock(&dhd->pub); + + /* save ptr to wl0.1 netdev for use in wl_iw.c */ + ap_net_dev = ifp->net; + /* signal to the SOFTAP 'sleeper' thread, wl0.1 is ready */ + up(&ap_eth_ctl.sema); + dhd_os_spin_unlock(&dhd->pub, flags); + } +#endif + DHD_TRACE(("\n ==== pid:%x, net_device for if:%s created ===\n\n", + current->pid, ifp->net->name)); + ifp->state = DHD_IF_NONE; + } + } + break; + case DHD_IF_DEL: + /* Make sure that we don't enter again here if .. */ + /* dhd_op_if is called again from some other context */ + ifp->state = DHD_IF_DELETING; + if (ifp->net != NULL) { + DHD_TRACE(("\n%s: got 'DHD_IF_DEL' state\n", __FUNCTION__)); +#ifdef WL_CFG80211 + if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { + wl_cfg80211_ifdel_ops(ifp->net); + } +#endif + netif_stop_queue(ifp->net); + unregister_netdev(ifp->net); + ret = DHD_DEL_IF; + +#ifdef WL_CFG80211 + if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { + wl_cfg80211_notify_ifdel(); + } +#endif + } + break; + case DHD_IF_DELETING: + break; + default: + DHD_ERROR(("%s: bad op %d\n", __FUNCTION__, ifp->state)); + ASSERT(!ifp->state); + break; + } + + if (ret < 0) { + ifp->set_multicast = FALSE; + if (ifp->net) { + free_netdev(ifp->net); + ifp->net = NULL; + } + dhd->iflist[ifp->idx] = NULL; +#ifdef SOFTAP + flags = dhd_os_spin_lock(&dhd->pub); + if (ifp->net == ap_net_dev) + ap_net_dev = NULL; /* NULL SOFTAP global wl0.1 as well */ + dhd_os_spin_unlock(&dhd->pub, flags); +#endif /* SOFTAP */ + MFREE(dhd->pub.osh, ifp, sizeof(*ifp)); + } +} + +static int +_dhd_sysioc_thread(void *data) +{ + tsk_ctl_t *tsk = (tsk_ctl_t *)data; + dhd_info_t *dhd = (dhd_info_t *)tsk->parent; + + + int i; +#ifdef SOFTAP + bool in_ap = FALSE; + unsigned long flags; +#endif + + DAEMONIZE("dhd_sysioc"); + + complete(&tsk->completed); + + while (down_interruptible(&tsk->sema) == 0) { + + SMP_RD_BARRIER_DEPENDS(); + if (tsk->terminated) { + break; + } + + dhd_net_if_lock_local(dhd); + DHD_OS_WAKE_LOCK(&dhd->pub); + + for (i = 0; i < DHD_MAX_IFS; i++) { + if (dhd->iflist[i]) { + DHD_TRACE(("%s: interface %d\n", __FUNCTION__, i)); +#ifdef SOFTAP + flags = dhd_os_spin_lock(&dhd->pub); + in_ap = (ap_net_dev != NULL); + dhd_os_spin_unlock(&dhd->pub, flags); +#endif /* SOFTAP */ + if (dhd->iflist[i] && dhd->iflist[i]->state) + dhd_op_if(dhd->iflist[i]); + + if (dhd->iflist[i] == NULL) { + DHD_TRACE(("\n\n %s: interface %d just been removed," + "!\n\n", __FUNCTION__, i)); + continue; + } +#ifdef SOFTAP + if (in_ap && dhd->set_macaddress) { + DHD_TRACE(("attempt to set MAC for %s in AP Mode," + "blocked. \n", dhd->iflist[i]->net->name)); + dhd->set_macaddress = FALSE; + continue; + } + + if (in_ap && dhd->iflist[i]->set_multicast) { + DHD_TRACE(("attempt to set MULTICAST list for %s" + "in AP Mode, blocked. \n", dhd->iflist[i]->net->name)); + dhd->iflist[i]->set_multicast = FALSE; + continue; + } +#endif /* SOFTAP */ + if (dhd->iflist[i]->set_multicast) { + dhd->iflist[i]->set_multicast = FALSE; + _dhd_set_multicast_list(dhd, i); + } + if (dhd->set_macaddress) { + dhd->set_macaddress = FALSE; + _dhd_set_mac_address(dhd, i, &dhd->macvalue); + } + } + } + + DHD_OS_WAKE_UNLOCK(&dhd->pub); + dhd_net_if_unlock_local(dhd); + } + DHD_TRACE(("%s: stopped\n", __FUNCTION__)); + complete_and_exit(&tsk->completed, 0); +} + +static int +dhd_set_mac_address(struct net_device *dev, void *addr) +{ + int ret = 0; + + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + struct sockaddr *sa = (struct sockaddr *)addr; + int ifidx; + + ifidx = dhd_net2idx(dhd, dev); + if (ifidx == DHD_BAD_IF) + return -1; + + ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); + memcpy(&dhd->macvalue, sa->sa_data, ETHER_ADDR_LEN); + dhd->set_macaddress = TRUE; + up(&dhd->thr_sysioc_ctl.sema); + + return ret; +} + +static void +dhd_set_multicast_list(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ifidx; + + ifidx = dhd_net2idx(dhd, dev); + if (ifidx == DHD_BAD_IF) + return; + + ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); + dhd->iflist[ifidx]->set_multicast = TRUE; + up(&dhd->thr_sysioc_ctl.sema); +} + +#ifdef PROP_TXSTATUS +int +dhd_os_wlfc_block(dhd_pub_t *pub) +{ + dhd_info_t *di = (dhd_info_t *)(pub->info); + ASSERT(di != NULL); + + spin_lock_bh(&di->wlfc_spinlock); + return 1; +} + +int +dhd_os_wlfc_unblock(dhd_pub_t *pub) +{ + dhd_info_t *di = (dhd_info_t *)(pub->info); + ASSERT(di != NULL); + spin_unlock_bh(&di->wlfc_spinlock); + return 1; +} + +const uint8 wme_fifo2ac[] = { 0, 1, 2, 3, 1, 1 }; +uint8 prio2fifo[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; +#define WME_PRIO2AC(prio) wme_fifo2ac[prio2fifo[(prio)]] + +#endif /* PROP_TXSTATUS */ +int +dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf) +{ + int ret; + dhd_info_t *dhd = (dhd_info_t *)(dhdp->info); + struct ether_header *eh = NULL; + + /* Reject if down */ + if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) { + /* free the packet here since the caller won't */ + PKTFREE(dhdp->osh, pktbuf, TRUE); + return -ENODEV; + } + + /* Update multicast statistic */ + if (PKTLEN(dhdp->osh, pktbuf) >= ETHER_HDR_LEN) { + uint8 *pktdata = (uint8 *)PKTDATA(dhdp->osh, pktbuf); + eh = (struct ether_header *)pktdata; + + if (ETHER_ISMULTI(eh->ether_dhost)) + dhdp->tx_multicast++; + if (ntoh16(eh->ether_type) == ETHER_TYPE_802_1X) + atomic_inc(&dhd->pend_8021x_cnt); + } else { + PKTFREE(dhd->pub.osh, pktbuf, TRUE); + return BCME_ERROR; + } + + /* Look into the packet and update the packet priority */ + if (PKTPRIO(pktbuf) == 0) + pktsetprio(pktbuf, FALSE); + +#ifdef PROP_TXSTATUS + if (dhdp->wlfc_state) { + /* store the interface ID */ + DHD_PKTTAG_SETIF(PKTTAG(pktbuf), ifidx); + + /* store destination MAC in the tag as well */ + DHD_PKTTAG_SETDSTN(PKTTAG(pktbuf), eh->ether_dhost); + + /* decide which FIFO this packet belongs to */ + if (ETHER_ISMULTI(eh->ether_dhost)) + /* one additional queue index (highest AC + 1) is used for bc/mc queue */ + DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), AC_COUNT); + else + DHD_PKTTAG_SETFIFO(PKTTAG(pktbuf), WME_PRIO2AC(PKTPRIO(pktbuf))); + } else +#endif /* PROP_TXSTATUS */ + /* If the protocol uses a data header, apply it */ + dhd_prot_hdrpush(dhdp, ifidx, pktbuf); + + /* Use bus module to send data frame */ +#ifdef WLMEDIA_HTSF + dhd_htsf_addtxts(dhdp, pktbuf); +#endif +#ifdef PROP_TXSTATUS + if (dhdp->wlfc_state && ((athost_wl_status_info_t*)dhdp->wlfc_state)->proptxstatus_mode + != WLFC_FCMODE_NONE) { + dhd_os_wlfc_block(dhdp); + ret = dhd_wlfc_enque_sendq(dhdp->wlfc_state, DHD_PKTTAG_FIFO(PKTTAG(pktbuf)), + pktbuf); + dhd_wlfc_commit_packets(dhdp->wlfc_state, (f_commitpkt_t)dhd_bus_txdata, + dhdp->bus); + if (((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if) { + ((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if = 0; + } + dhd_os_wlfc_unblock(dhdp); + } + else + /* non-proptxstatus way */ + ret = dhd_bus_txdata(dhdp->bus, pktbuf); +#else + ret = dhd_bus_txdata(dhdp->bus, pktbuf); +#endif /* PROP_TXSTATUS */ + + + return ret; +} + +int +dhd_start_xmit(struct sk_buff *skb, struct net_device *net) +{ + int ret; + void *pktbuf; + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); + int ifidx; +#ifdef WLMEDIA_HTSF + uint8 htsfdlystat_sz = dhd->pub.htsfdlystat_sz; +#else + uint8 htsfdlystat_sz = 0; +#endif + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + DHD_OS_WAKE_LOCK(&dhd->pub); + + /* Reject if down */ + if (!dhd->pub.up || (dhd->pub.busstate == DHD_BUS_DOWN)) { + DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d \n", + __FUNCTION__, dhd->pub.up, dhd->pub.busstate)); + netif_stop_queue(net); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + /* Send Event when bus down detected during data session */ + if (dhd->pub.busstate == DHD_BUS_DOWN) { + DHD_ERROR(("%s: Event HANG sent up\n", __FUNCTION__)); + net_os_send_hang_message(net); + } +#endif + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return -ENODEV; + } + + ifidx = dhd_net2idx(dhd, net); + if (ifidx == DHD_BAD_IF) { + DHD_ERROR(("%s: bad ifidx %d\n", __FUNCTION__, ifidx)); + netif_stop_queue(net); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return -ENODEV; + } + + /* Make sure there's enough room for any header */ + + if (skb_headroom(skb) < dhd->pub.hdrlen + htsfdlystat_sz) { + struct sk_buff *skb2; + + DHD_INFO(("%s: insufficient headroom\n", + dhd_ifname(&dhd->pub, ifidx))); + dhd->pub.tx_realloc++; + + skb2 = skb_realloc_headroom(skb, dhd->pub.hdrlen + htsfdlystat_sz); + + dev_kfree_skb(skb); + if ((skb = skb2) == NULL) { + DHD_ERROR(("%s: skb_realloc_headroom failed\n", + dhd_ifname(&dhd->pub, ifidx))); + ret = -ENOMEM; + goto done; + } + } + + /* Convert to packet */ + if (!(pktbuf = PKTFRMNATIVE(dhd->pub.osh, skb))) { + DHD_ERROR(("%s: PKTFRMNATIVE failed\n", + dhd_ifname(&dhd->pub, ifidx))); + dev_kfree_skb_any(skb); + ret = -ENOMEM; + goto done; + } +#ifdef WLMEDIA_HTSF + if (htsfdlystat_sz && PKTLEN(dhd->pub.osh, pktbuf) >= ETHER_ADDR_LEN) { + uint8 *pktdata = (uint8 *)PKTDATA(dhd->pub.osh, pktbuf); + struct ether_header *eh = (struct ether_header *)pktdata; + + if (!ETHER_ISMULTI(eh->ether_dhost) && + (ntoh16(eh->ether_type) == ETHER_TYPE_IP)) { + eh->ether_type = hton16(ETHER_TYPE_BRCM_PKTDLYSTATS); + } + } +#endif + + ret = dhd_sendpkt(&dhd->pub, ifidx, pktbuf); + + +done: + if (ret) + dhd->pub.dstats.tx_dropped++; + else + dhd->pub.tx_packets++; + + DHD_OS_WAKE_UNLOCK(&dhd->pub); + + /* Return ok: we always eat the packet */ + return 0; +} + +void +dhd_txflowcontrol(dhd_pub_t *dhdp, int ifidx, bool state) +{ + struct net_device *net; + dhd_info_t *dhd = dhdp->info; + int i; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + dhdp->txoff = state; + ASSERT(dhd); + + if (ifidx == ALL_INTERFACES) { + /* Flow control on all active interfaces */ + for (i = 0; i < DHD_MAX_IFS; i++) { + if (dhd->iflist[i]) { + net = dhd->iflist[i]->net; + if (state == ON) + netif_stop_queue(net); + else + netif_wake_queue(net); + } + } + } + else { + if (dhd->iflist[ifidx]) { + net = dhd->iflist[ifidx]->net; + if (state == ON) + netif_stop_queue(net); + else + netif_wake_queue(net); + } + } +} + +void +dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) +{ + dhd_info_t *dhd = (dhd_info_t *)dhdp->info; + struct sk_buff *skb; + uchar *eth; + uint len; + void *data, *pnext = NULL, *save_pktbuf; + int i; + dhd_if_t *ifp; + wl_event_msg_t event; + int tout_rx = 0; + int tout_ctrl = 0; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + save_pktbuf = pktbuf; + + for (i = 0; pktbuf && i < numpkt; i++, pktbuf = pnext) { + struct ether_header *eh; + struct dot11_llc_snap_header *lsh; + + ifp = dhd->iflist[ifidx]; + if (ifp == NULL) { + DHD_ERROR(("%s: ifp is NULL. drop packet\n", + __FUNCTION__)); + PKTFREE(dhdp->osh, pktbuf, TRUE); + continue; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + /* Dropping packets before registering net device to avoid kernel panic */ + if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED || + !dhd->pub.up) { + DHD_ERROR(("%s: net device is NOT registered yet. drop packet\n", + __FUNCTION__)); + PKTFREE(dhdp->osh, pktbuf, TRUE); + continue; + } +#endif + + pnext = PKTNEXT(dhdp->osh, pktbuf); + PKTSETNEXT(wl->sh.osh, pktbuf, NULL); + + eh = (struct ether_header *)PKTDATA(wl->sh.osh, pktbuf); + lsh = (struct dot11_llc_snap_header *)&eh[1]; + + if ((ntoh16(eh->ether_type) < ETHER_TYPE_MIN) && + (PKTLEN(wl->sh.osh, pktbuf) >= RFC1042_HDR_LEN) && + bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 && + lsh->type == HTON16(BTA_PROT_L2CAP)) { + amp_hci_ACL_data_t *ACL_data = (amp_hci_ACL_data_t *) + ((uint8 *)eh + RFC1042_HDR_LEN); + ACL_data = NULL; + } + +#ifdef PROP_TXSTATUS + if (dhdp->wlfc_state && PKTLEN(wl->sh.osh, pktbuf) == 0) { + /* WLFC may send header only packet when + there is an urgent message but no packet to + piggy-back on + */ + ((athost_wl_status_info_t*)dhdp->wlfc_state)->stats.wlfc_header_only_pkt++; + PKTFREE(dhdp->osh, pktbuf, TRUE); + DHD_TRACE(("RX: wlfc header \n")); + continue; + } +#endif + + skb = PKTTONATIVE(dhdp->osh, pktbuf); + + /* Get the protocol, maintain skb around eth_type_trans() + * The main reason for this hack is for the limitation of + * Linux 2.4 where 'eth_type_trans' uses the 'net->hard_header_len' + * to perform skb_pull inside vs ETH_HLEN. Since to avoid + * coping of the packet coming from the network stack to add + * BDC, Hardware header etc, during network interface registration + * we set the 'net->hard_header_len' to ETH_HLEN + extra space required + * for BDC, Hardware header etc. and not just the ETH_HLEN + */ + eth = skb->data; + len = skb->len; + + ifp = dhd->iflist[ifidx]; + if (ifp == NULL) + ifp = dhd->iflist[0]; + + ASSERT(ifp); + skb->dev = ifp->net; + skb->protocol = eth_type_trans(skb, skb->dev); + + if (skb->pkt_type == PACKET_MULTICAST) { + dhd->pub.rx_multicast++; + } + + skb->data = eth; + skb->len = len; + +#ifdef WLMEDIA_HTSF + dhd_htsf_addrxts(dhdp, pktbuf); +#endif + /* Strip header, count, deliver upward */ + skb_pull(skb, ETH_HLEN); + + /* Process special event packets and then discard them */ + if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) { + dhd_wl_host_event(dhd, &ifidx, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + skb->mac_header, +#else + skb->mac.raw, +#endif + &event, + &data); + + wl_event_to_host_order(&event); + if (!tout_ctrl) + tout_ctrl = DHD_PACKET_TIMEOUT_MS; + if (event.event_type == WLC_E_BTA_HCI_EVENT) { + dhd_bta_doevt(dhdp, data, event.datalen); + } +#ifdef PNO_SUPPORT + if (event.event_type == WLC_E_PFN_NET_FOUND) { + tout_ctrl *= 2; + } +#endif /* PNO_SUPPORT */ + } else { + tout_rx = DHD_PACKET_TIMEOUT_MS; + } + + ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]); + if (dhd->iflist[ifidx] && !dhd->iflist[ifidx]->state) + ifp = dhd->iflist[ifidx]; + + if (ifp->net) + ifp->net->last_rx = jiffies; + + dhdp->dstats.rx_bytes += skb->len; + dhdp->rx_packets++; /* Local count */ + + if (in_interrupt()) { + netif_rx(skb); + } else { + /* If the receive is not processed inside an ISR, + * the softirqd must be woken explicitly to service + * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled + * by netif_rx_ni(), but in earlier kernels, we need + * to do it manually. + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) + netif_rx_ni(skb); +#else + ulong flags; + netif_rx(skb); + local_irq_save(flags); + RAISE_RX_SOFTIRQ(); + local_irq_restore(flags); +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */ + } + } + + DHD_OS_WAKE_LOCK_RX_TIMEOUT_ENABLE(dhdp, tout_rx); + DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(dhdp, tout_ctrl); +} + +void +dhd_event(struct dhd_info *dhd, char *evpkt, int evlen, int ifidx) +{ + /* Linux version has nothing to do */ + return; +} + +void +dhd_txcomplete(dhd_pub_t *dhdp, void *txp, bool success) +{ + uint ifidx; + dhd_info_t *dhd = (dhd_info_t *)(dhdp->info); + struct ether_header *eh; + uint16 type; + uint len; + + dhd_prot_hdrpull(dhdp, &ifidx, txp); + + eh = (struct ether_header *)PKTDATA(dhdp->osh, txp); + type = ntoh16(eh->ether_type); + + if (type == ETHER_TYPE_802_1X) + atomic_dec(&dhd->pend_8021x_cnt); + + /* Crack open the packet and check to see if it is BT HCI ACL data packet. + * If yes generate packet completion event. + */ + len = PKTLEN(dhdp->osh, txp); + + /* Generate ACL data tx completion event locally to avoid SDIO bus transaction */ + if ((type < ETHER_TYPE_MIN) && (len >= RFC1042_HDR_LEN)) { + struct dot11_llc_snap_header *lsh = (struct dot11_llc_snap_header *)&eh[1]; + + if (bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 && + ntoh16(lsh->type) == BTA_PROT_L2CAP) { + + dhd_bta_tx_hcidata_complete(dhdp, txp, success); + } + } +} + +static struct net_device_stats * +dhd_get_stats(struct net_device *net) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); + dhd_if_t *ifp; + int ifidx; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + ifidx = dhd_net2idx(dhd, net); + if (ifidx == DHD_BAD_IF) { + DHD_ERROR(("%s: BAD_IF\n", __FUNCTION__)); + return NULL; + } + + ifp = dhd->iflist[ifidx]; + ASSERT(dhd && ifp); + + if (dhd->pub.up) { + /* Use the protocol to get dongle stats */ + dhd_prot_dstats(&dhd->pub); + } + + /* Copy dongle stats to net device stats */ + ifp->stats.rx_packets = dhd->pub.dstats.rx_packets; + ifp->stats.tx_packets = dhd->pub.dstats.tx_packets; + ifp->stats.rx_bytes = dhd->pub.dstats.rx_bytes; + ifp->stats.tx_bytes = dhd->pub.dstats.tx_bytes; + ifp->stats.rx_errors = dhd->pub.dstats.rx_errors; + ifp->stats.tx_errors = dhd->pub.dstats.tx_errors; + ifp->stats.rx_dropped = dhd->pub.dstats.rx_dropped; + ifp->stats.tx_dropped = dhd->pub.dstats.tx_dropped; + ifp->stats.multicast = dhd->pub.dstats.multicast; + + return &ifp->stats; +} + +#ifdef DHDTHREAD +static int +dhd_watchdog_thread(void *data) +{ + tsk_ctl_t *tsk = (tsk_ctl_t *)data; + dhd_info_t *dhd = (dhd_info_t *)tsk->parent; + /* This thread doesn't need any user-level access, + * so get rid of all our resources + */ + if (dhd_watchdog_prio > 0) { + struct sched_param param; + param.sched_priority = (dhd_watchdog_prio < MAX_RT_PRIO)? + dhd_watchdog_prio:(MAX_RT_PRIO-1); + setScheduler(current, SCHED_FIFO, ¶m); + } + + DAEMONIZE("dhd_watchdog"); + + /* Run until signal received */ + complete(&tsk->completed); + + while (1) + if (down_interruptible (&tsk->sema) == 0) { + unsigned long flags; + + SMP_RD_BARRIER_DEPENDS(); + if (tsk->terminated) { + break; + } + + dhd_os_sdlock(&dhd->pub); + if (dhd->pub.dongle_reset == FALSE) { + DHD_TIMER(("%s:\n", __FUNCTION__)); + + /* Call the bus module watchdog */ + dhd_bus_watchdog(&dhd->pub); + + flags = dhd_os_spin_lock(&dhd->pub); + /* Count the tick for reference */ + dhd->pub.tickcnt++; + /* Reschedule the watchdog */ + if (dhd->wd_timer_valid) + mod_timer(&dhd->timer, + jiffies + dhd_watchdog_ms * HZ / 1000); + dhd_os_spin_unlock(&dhd->pub, flags); + } + dhd_os_sdunlock(&dhd->pub); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + } else { + break; + } + + complete_and_exit(&tsk->completed, 0); +} +#endif /* DHDTHREAD */ + +static void dhd_watchdog(ulong data) +{ + dhd_info_t *dhd = (dhd_info_t *)data; + unsigned long flags; + + DHD_OS_WAKE_LOCK(&dhd->pub); + if (dhd->pub.dongle_reset) { + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return; + } + +#ifdef DHDTHREAD + if (dhd->thr_wdt_ctl.thr_pid >= 0) { + up(&dhd->thr_wdt_ctl.sema); + return; + } +#endif /* DHDTHREAD */ + + dhd_os_sdlock(&dhd->pub); + /* Call the bus module watchdog */ + dhd_bus_watchdog(&dhd->pub); + + flags = dhd_os_spin_lock(&dhd->pub); + /* Count the tick for reference */ + dhd->pub.tickcnt++; + + /* Reschedule the watchdog */ + if (dhd->wd_timer_valid) + mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000); + dhd_os_spin_unlock(&dhd->pub, flags); + dhd_os_sdunlock(&dhd->pub); + DHD_OS_WAKE_UNLOCK(&dhd->pub); +} + +#ifdef DHDTHREAD +static int +dhd_dpc_thread(void *data) +{ + tsk_ctl_t *tsk = (tsk_ctl_t *)data; + dhd_info_t *dhd = (dhd_info_t *)tsk->parent; + + /* This thread doesn't need any user-level access, + * so get rid of all our resources + */ + if (dhd_dpc_prio > 0) + { + struct sched_param param; + param.sched_priority = (dhd_dpc_prio < MAX_RT_PRIO)?dhd_dpc_prio:(MAX_RT_PRIO-1); + setScheduler(current, SCHED_FIFO, ¶m); + } + + DAEMONIZE("dhd_dpc"); + /* DHD_OS_WAKE_LOCK is called in dhd_sched_dpc[dhd_linux.c] down below */ + + /* signal: thread has started */ + complete(&tsk->completed); + + /* Run until signal received */ + while (1) { + if (down_interruptible(&tsk->sema) == 0) { + + SMP_RD_BARRIER_DEPENDS(); + if (tsk->terminated) { + break; + } + + /* Call bus dpc unless it indicated down (then clean stop) */ + if (dhd->pub.busstate != DHD_BUS_DOWN) { + if (dhd_bus_dpc(dhd->pub.bus)) { + up(&tsk->sema); + } + else { + DHD_OS_WAKE_UNLOCK(&dhd->pub); + } + } else { + if (dhd->pub.up) + dhd_bus_stop(dhd->pub.bus, TRUE); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + } + } + else + break; + } + + complete_and_exit(&tsk->completed, 0); +} +#endif /* DHDTHREAD */ + +static void +dhd_dpc(ulong data) +{ + dhd_info_t *dhd; + + dhd = (dhd_info_t *)data; + + /* this (tasklet) can be scheduled in dhd_sched_dpc[dhd_linux.c] + * down below , wake lock is set, + * the tasklet is initialized in dhd_attach() + */ + /* Call bus dpc unless it indicated down (then clean stop) */ + if (dhd->pub.busstate != DHD_BUS_DOWN) { + if (dhd_bus_dpc(dhd->pub.bus)) + tasklet_schedule(&dhd->tasklet); + else + DHD_OS_WAKE_UNLOCK(&dhd->pub); + } else { + dhd_bus_stop(dhd->pub.bus, TRUE); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + } +} + +void +dhd_sched_dpc(dhd_pub_t *dhdp) +{ + dhd_info_t *dhd = (dhd_info_t *)dhdp->info; + + DHD_OS_WAKE_LOCK(dhdp); +#ifdef DHDTHREAD + if (dhd->thr_dpc_ctl.thr_pid >= 0) { + up(&dhd->thr_dpc_ctl.sema); + return; + } +#endif /* DHDTHREAD */ + + tasklet_schedule(&dhd->tasklet); +} + +#ifdef TOE +/* Retrieve current toe component enables, which are kept as a bitmap in toe_ol iovar */ +static int +dhd_toe_get(dhd_info_t *dhd, int ifidx, uint32 *toe_ol) +{ + wl_ioctl_t ioc; + char buf[32]; + int ret; + + memset(&ioc, 0, sizeof(ioc)); + + ioc.cmd = WLC_GET_VAR; + ioc.buf = buf; + ioc.len = (uint)sizeof(buf); + ioc.set = FALSE; + + strcpy(buf, "toe_ol"); + if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { + /* Check for older dongle image that doesn't support toe_ol */ + if (ret == -EIO) { + DHD_ERROR(("%s: toe not supported by device\n", + dhd_ifname(&dhd->pub, ifidx))); + return -EOPNOTSUPP; + } + + DHD_INFO(("%s: could not get toe_ol: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret)); + return ret; + } + + memcpy(toe_ol, buf, sizeof(uint32)); + return 0; +} + +/* Set current toe component enables in toe_ol iovar, and set toe global enable iovar */ +static int +dhd_toe_set(dhd_info_t *dhd, int ifidx, uint32 toe_ol) +{ + wl_ioctl_t ioc; + char buf[32]; + int toe, ret; + + memset(&ioc, 0, sizeof(ioc)); + + ioc.cmd = WLC_SET_VAR; + ioc.buf = buf; + ioc.len = (uint)sizeof(buf); + ioc.set = TRUE; + + /* Set toe_ol as requested */ + + strcpy(buf, "toe_ol"); + memcpy(&buf[sizeof("toe_ol")], &toe_ol, sizeof(uint32)); + + if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { + DHD_ERROR(("%s: could not set toe_ol: ret=%d\n", + dhd_ifname(&dhd->pub, ifidx), ret)); + return ret; + } + + /* Enable toe globally only if any components are enabled. */ + + toe = (toe_ol != 0); + + strcpy(buf, "toe"); + memcpy(&buf[sizeof("toe")], &toe, sizeof(uint32)); + + if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { + DHD_ERROR(("%s: could not set toe: ret=%d\n", dhd_ifname(&dhd->pub, ifidx), ret)); + return ret; + } + + return 0; +} +#endif /* TOE */ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) +static void +dhd_ethtool_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); + + sprintf(info->driver, "wl"); + sprintf(info->version, "%lu", dhd->pub.drv_version); +} + +struct ethtool_ops dhd_ethtool_ops = { + .get_drvinfo = dhd_ethtool_get_drvinfo +}; +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ + + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) +static int +dhd_ethtool(dhd_info_t *dhd, void *uaddr) +{ + struct ethtool_drvinfo info; + char drvname[sizeof(info.driver)]; + uint32 cmd; +#ifdef TOE + struct ethtool_value edata; + uint32 toe_cmpnt, csum_dir; + int ret; +#endif + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + /* all ethtool calls start with a cmd word */ + if (copy_from_user(&cmd, uaddr, sizeof (uint32))) + return -EFAULT; + + switch (cmd) { + case ETHTOOL_GDRVINFO: + /* Copy out any request driver name */ + if (copy_from_user(&info, uaddr, sizeof(info))) + return -EFAULT; + strncpy(drvname, info.driver, sizeof(info.driver)); + drvname[sizeof(info.driver)-1] = '\0'; + + /* clear struct for return */ + memset(&info, 0, sizeof(info)); + info.cmd = cmd; + + /* if dhd requested, identify ourselves */ + if (strcmp(drvname, "?dhd") == 0) { + sprintf(info.driver, "dhd"); + strcpy(info.version, EPI_VERSION_STR); + } + + /* otherwise, require dongle to be up */ + else if (!dhd->pub.up) { + DHD_ERROR(("%s: dongle is not up\n", __FUNCTION__)); + return -ENODEV; + } + + /* finally, report dongle driver type */ + else if (dhd->pub.iswl) + sprintf(info.driver, "wl"); + else + sprintf(info.driver, "xx"); + + sprintf(info.version, "%lu", dhd->pub.drv_version); + if (copy_to_user(uaddr, &info, sizeof(info))) + return -EFAULT; + DHD_CTL(("%s: given %*s, returning %s\n", __FUNCTION__, + (int)sizeof(drvname), drvname, info.driver)); + break; + +#ifdef TOE + /* Get toe offload components from dongle */ + case ETHTOOL_GRXCSUM: + case ETHTOOL_GTXCSUM: + if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0) + return ret; + + csum_dir = (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL; + + edata.cmd = cmd; + edata.data = (toe_cmpnt & csum_dir) ? 1 : 0; + + if (copy_to_user(uaddr, &edata, sizeof(edata))) + return -EFAULT; + break; + + /* Set toe offload components in dongle */ + case ETHTOOL_SRXCSUM: + case ETHTOOL_STXCSUM: + if (copy_from_user(&edata, uaddr, sizeof(edata))) + return -EFAULT; + + /* Read the current settings, update and write back */ + if ((ret = dhd_toe_get(dhd, 0, &toe_cmpnt)) < 0) + return ret; + + csum_dir = (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL; + + if (edata.data != 0) + toe_cmpnt |= csum_dir; + else + toe_cmpnt &= ~csum_dir; + + if ((ret = dhd_toe_set(dhd, 0, toe_cmpnt)) < 0) + return ret; + + /* If setting TX checksum mode, tell Linux the new mode */ + if (cmd == ETHTOOL_STXCSUM) { + if (edata.data) + dhd->iflist[0]->net->features |= NETIF_F_IP_CSUM; + else + dhd->iflist[0]->net->features &= ~NETIF_F_IP_CSUM; + } + + break; +#endif /* TOE */ + + default: + return -EOPNOTSUPP; + } + + return 0; +} +#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */ + +static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + if (!dhdp) + return FALSE; + if ((error == -ETIMEDOUT) || ((dhdp->busstate == DHD_BUS_DOWN) && + (!dhdp->dongle_reset))) { + DHD_ERROR(("%s: Event HANG send up due to re=%d te=%d e=%d s=%d\n", __FUNCTION__, + dhdp->rxcnt_timeout, dhdp->txcnt_timeout, error, dhdp->busstate)); + net_os_send_hang_message(net); + return TRUE; + } +#endif + return FALSE; +} + +static int +dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); + dhd_ioctl_t ioc; + int bcmerror = 0; + int buflen = 0; + void *buf = NULL; + uint driver = 0; + int ifidx; + int ret; + + DHD_OS_WAKE_LOCK(&dhd->pub); + + /* send to dongle only if we are not waiting for reload already */ + if (dhd->pub.hang_was_sent) { + DHD_ERROR(("%s: HANG was sent up earlier\n", __FUNCTION__)); + DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(&dhd->pub, DHD_EVENT_TIMEOUT_MS); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return OSL_ERROR(BCME_DONGLE_DOWN); + } + + ifidx = dhd_net2idx(dhd, net); + DHD_TRACE(("%s: ifidx %d, cmd 0x%04x\n", __FUNCTION__, ifidx, cmd)); + + if (ifidx == DHD_BAD_IF) { + DHD_ERROR(("%s: BAD IF\n", __FUNCTION__)); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return -1; + } + +#if defined(WL_WIRELESS_EXT) + /* linux wireless extensions */ + if ((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) { + /* may recurse, do NOT lock */ + ret = wl_iw_ioctl(net, ifr, cmd); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return ret; + } +#endif /* defined(WL_WIRELESS_EXT) */ + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) + if (cmd == SIOCETHTOOL) { + ret = dhd_ethtool(dhd, (void*)ifr->ifr_data); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return ret; + } +#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) */ + + if (cmd == SIOCDEVPRIVATE+1) { + ret = wl_android_priv_cmd(net, ifr, cmd); + dhd_check_hang(net, &dhd->pub, ret); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return ret; + } + + if (cmd != SIOCDEVPRIVATE) { + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return -EOPNOTSUPP; + } + + memset(&ioc, 0, sizeof(ioc)); + + /* Copy the ioc control structure part of ioctl request */ + if (copy_from_user(&ioc, ifr->ifr_data, sizeof(wl_ioctl_t))) { + bcmerror = -BCME_BADADDR; + goto done; + } + + /* Copy out any buffer passed */ + if (ioc.buf) { + buflen = MIN(ioc.len, DHD_IOCTL_MAXLEN); + /* optimization for direct ioctl calls from kernel */ + /* + if (segment_eq(get_fs(), KERNEL_DS)) { + buf = ioc.buf; + } else { + */ + { + if (!(buf = (char*)MALLOC(dhd->pub.osh, buflen))) { + bcmerror = -BCME_NOMEM; + goto done; + } + if (copy_from_user(buf, ioc.buf, buflen)) { + bcmerror = -BCME_BADADDR; + goto done; + } + } + } + + /* To differentiate between wl and dhd read 4 more byes */ + if ((copy_from_user(&driver, (char *)ifr->ifr_data + sizeof(wl_ioctl_t), + sizeof(uint)) != 0)) { + bcmerror = -BCME_BADADDR; + goto done; + } + + if (!capable(CAP_NET_ADMIN)) { + bcmerror = -BCME_EPERM; + goto done; + } + + /* check for local dhd ioctl and handle it */ + if (driver == DHD_IOCTL_MAGIC) { + bcmerror = dhd_ioctl((void *)&dhd->pub, &ioc, buf, buflen); + if (bcmerror) + dhd->pub.bcmerror = bcmerror; + goto done; + } + + /* send to dongle (must be up, and wl). */ + if (dhd->pub.busstate != DHD_BUS_DATA) { + bcmerror = BCME_DONGLE_DOWN; + goto done; + } + + if (!dhd->pub.iswl) { + bcmerror = BCME_DONGLE_DOWN; + goto done; + } + + /* + * Flush the TX queue if required for proper message serialization: + * Intercept WLC_SET_KEY IOCTL - serialize M4 send and set key IOCTL to + * prevent M4 encryption and + * intercept WLC_DISASSOC IOCTL - serialize WPS-DONE and WLC_DISASSOC IOCTL to + * prevent disassoc frame being sent before WPS-DONE frame. + */ + if (ioc.cmd == WLC_SET_KEY || + (ioc.cmd == WLC_SET_VAR && ioc.buf != NULL && + strncmp("wsec_key", ioc.buf, 9) == 0) || + (ioc.cmd == WLC_SET_VAR && ioc.buf != NULL && + strncmp("bsscfg:wsec_key", ioc.buf, 15) == 0) || + ioc.cmd == WLC_DISASSOC) + dhd_wait_pend8021x(net); + +#ifdef WLMEDIA_HTSF + if (ioc.buf) { + /* short cut wl ioctl calls here */ + if (strcmp("htsf", ioc.buf) == 0) { + dhd_ioctl_htsf_get(dhd, 0); + return BCME_OK; + } + + if (strcmp("htsflate", ioc.buf) == 0) { + if (ioc.set) { + memset(ts, 0, sizeof(tstamp_t)*TSMAX); + memset(&maxdelayts, 0, sizeof(tstamp_t)); + maxdelay = 0; + tspktcnt = 0; + maxdelaypktno = 0; + memset(&vi_d1.bin, 0, sizeof(uint32)*NUMBIN); + memset(&vi_d2.bin, 0, sizeof(uint32)*NUMBIN); + memset(&vi_d3.bin, 0, sizeof(uint32)*NUMBIN); + memset(&vi_d4.bin, 0, sizeof(uint32)*NUMBIN); + } else { + dhd_dump_latency(); + } + return BCME_OK; + } + if (strcmp("htsfclear", ioc.buf) == 0) { + memset(&vi_d1.bin, 0, sizeof(uint32)*NUMBIN); + memset(&vi_d2.bin, 0, sizeof(uint32)*NUMBIN); + memset(&vi_d3.bin, 0, sizeof(uint32)*NUMBIN); + memset(&vi_d4.bin, 0, sizeof(uint32)*NUMBIN); + htsf_seqnum = 0; + return BCME_OK; + } + if (strcmp("htsfhis", ioc.buf) == 0) { + dhd_dump_htsfhisto(&vi_d1, "H to D"); + dhd_dump_htsfhisto(&vi_d2, "D to D"); + dhd_dump_htsfhisto(&vi_d3, "D to H"); + dhd_dump_htsfhisto(&vi_d4, "H to H"); + return BCME_OK; + } + if (strcmp("tsport", ioc.buf) == 0) { + if (ioc.set) { + memcpy(&tsport, ioc.buf + 7, 4); + } else { + DHD_ERROR(("current timestamp port: %d \n", tsport)); + } + return BCME_OK; + } + } +#endif /* WLMEDIA_HTSF */ + + bcmerror = dhd_wl_ioctl(&dhd->pub, ifidx, (wl_ioctl_t *)&ioc, buf, buflen); + +done: + dhd_check_hang(net, &dhd->pub, bcmerror); + + if (!bcmerror && buf && ioc.buf) { + if (copy_to_user(ioc.buf, buf, buflen)) + bcmerror = -EFAULT; + } + + if (buf) + MFREE(dhd->pub.osh, buf, buflen); + + DHD_OS_WAKE_UNLOCK(&dhd->pub); + + return OSL_ERROR(bcmerror); +} + +#ifdef WL_CFG80211 +static int +dhd_cleanup_virt_ifaces(dhd_info_t *dhd) +{ + int i = 1; /* Leave ifidx 0 [Primary Interface] */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + int rollback_lock = FALSE; +#endif + + DHD_TRACE(("%s: Enter \n", __func__)); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + /* release lock for unregister_netdev */ + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = TRUE; + } +#endif + + for (i = 1; i < DHD_MAX_IFS; i++) { + dhd_net_if_lock_local(dhd); + if (dhd->iflist[i]) { + DHD_TRACE(("Deleting IF: %d \n", i)); + if ((dhd->iflist[i]->state != DHD_IF_DEL) && + (dhd->iflist[i]->state != DHD_IF_DELETING)) { + dhd->iflist[i]->state = DHD_IF_DEL; + dhd->iflist[i]->idx = i; + dhd_op_if(dhd->iflist[i]); + } + } + dhd_net_if_unlock_local(dhd); + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + if (rollback_lock) + rtnl_lock(); +#endif + + return 0; +} +#endif /* WL_CFG80211 */ + +static int +dhd_stop(struct net_device *net) +{ + int ifidx = 0; + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); + DHD_OS_WAKE_LOCK(&dhd->pub); + DHD_TRACE(("%s: Enter %p\n", __FUNCTION__, net)); + if (dhd->pub.up == 0) { + goto exit; + } + ifidx = dhd_net2idx(dhd, net); + +#ifdef WL_CFG80211 + if (ifidx == 0) { + wl_cfg80211_down(NULL); + + /* + * For CFG80211: Clean up all the left over virtual interfaces + * when the primary Interface is brought down. [ifconfig wlan0 down] + */ + if ((dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) && + (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) { + dhd_cleanup_virt_ifaces(dhd); + } + } +#endif + +#ifdef PROP_TXSTATUS + dhd_wlfc_cleanup(&dhd->pub); +#endif + /* Set state and stop OS transmissions */ + dhd->pub.up = 0; + netif_stop_queue(net); + + /* Stop the protocol module */ + dhd_prot_stop(&dhd->pub); + + OLD_MOD_DEC_USE_COUNT; +exit: +#if defined(WL_CFG80211) + if (ifidx == 0 && !dhd_download_fw_on_driverload) + wl_android_wifi_off(net); +#endif + dhd->pub.rxcnt_timeout = 0; + dhd->pub.txcnt_timeout = 0; + + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return 0; +} + +static int +dhd_open(struct net_device *net) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); + +#ifdef TOE + uint32 toe_ol; +#endif + int ifidx; + int32 ret = 0; + + DHD_OS_WAKE_LOCK(&dhd->pub); + /* Update FW path if it was changed */ + if ((firmware_path != NULL) && (firmware_path[0] != '\0')) { + if (firmware_path[strlen(firmware_path)-1] == '\n') + firmware_path[strlen(firmware_path)-1] = '\0'; + strcpy(fw_path, firmware_path); + firmware_path[0] = '\0'; + } + + dhd->pub.hang_was_sent = 0; +#if !defined(WL_CFG80211) + /* + * Force start if ifconfig_up gets called before START command + * We keep WEXT's wl_control_wl_start to provide backward compatibility + * This should be removed in the future + */ + ret = wl_control_wl_start(net); + if (ret != 0) { + DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); + ret = -1; + goto exit; + } +#endif + + ifidx = dhd_net2idx(dhd, net); + DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx)); + + if (ifidx < 0) { + DHD_ERROR(("%s: Error: called with invalid IF\n", __FUNCTION__)); + ret = -1; + goto exit; + } + + if (!dhd->iflist[ifidx] || dhd->iflist[ifidx]->state == DHD_IF_DEL) { + DHD_ERROR(("%s: Error: called when IF already deleted\n", __FUNCTION__)); + ret = -1; + goto exit; + } + + if (ifidx == 0) { + atomic_set(&dhd->pend_8021x_cnt, 0); +#if defined(WL_CFG80211) + DHD_ERROR(("\n%s\n", dhd_version)); + if (!dhd_download_fw_on_driverload) { + ret = wl_android_wifi_on(net); + if (ret != 0) { + DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); + ret = -1; + goto exit; + } + } +#endif /* defined(WL_CFG80211) */ + + if (dhd->pub.busstate != DHD_BUS_DATA) { + + /* try to bring up bus */ + if ((ret = dhd_bus_start(&dhd->pub)) != 0) { + DHD_ERROR(("%s: failed with code %d\n", __FUNCTION__, ret)); + ret = -1; + goto exit; + } + + } + + /* dhd_prot_init has been called in dhd_bus_start or wl_android_wifi_on */ + memcpy(net->dev_addr, dhd->pub.mac.octet, ETHER_ADDR_LEN); + +#ifdef TOE + /* Get current TOE mode from dongle */ + if (dhd_toe_get(dhd, ifidx, &toe_ol) >= 0 && (toe_ol & TOE_TX_CSUM_OL) != 0) + dhd->iflist[ifidx]->net->features |= NETIF_F_IP_CSUM; + else + dhd->iflist[ifidx]->net->features &= ~NETIF_F_IP_CSUM; +#endif /* TOE */ + +#if defined(WL_CFG80211) + if (unlikely(wl_cfg80211_up(NULL))) { + DHD_ERROR(("%s: failed to bring up cfg80211\n", __FUNCTION__)); + ret = -1; + goto exit; + } +#endif /* WL_CFG80211 */ + } + + /* Allow transmit calls */ + netif_start_queue(net); + dhd->pub.up = 1; + +#ifdef BCMDBGFS + dhd_dbg_init(&dhd->pub); +#endif + + OLD_MOD_INC_USE_COUNT; +exit: + if (ret) + dhd_stop(net); + + DHD_OS_WAKE_UNLOCK(&dhd->pub); + return ret; +} + +int dhd_do_driver_init(struct net_device *net) +{ + dhd_info_t *dhd = NULL; + + if (!net) { + DHD_ERROR(("Primary Interface not initialized \n")); + return -EINVAL; + } + + dhd = *(dhd_info_t **)netdev_priv(net); + + /* If driver is already initialized, do nothing + */ + if (dhd->pub.busstate == DHD_BUS_DATA) { + DHD_TRACE(("Driver already Inititalized. Nothing to do")); + return 0; + } + + if (dhd_open(net) < 0) { + DHD_ERROR(("Driver Init Failed \n")); + return -1; + } + + return 0; +} + +osl_t * +dhd_osl_attach(void *pdev, uint bustype) +{ + return osl_attach(pdev, bustype, TRUE); +} + +void +dhd_osl_detach(osl_t *osh) +{ + if (MALLOCED(osh)) { + DHD_ERROR(("%s: MEMORY LEAK %d bytes\n", __FUNCTION__, MALLOCED(osh))); + } + osl_detach(osh); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + up(&dhd_registration_sem); +#endif +} + +int +dhd_add_if(dhd_info_t *dhd, int ifidx, void *handle, char *name, + uint8 *mac_addr, uint32 flags, uint8 bssidx) +{ + dhd_if_t *ifp; + + DHD_TRACE(("%s: idx %d, handle->%p\n", __FUNCTION__, ifidx, handle)); + + ASSERT(dhd && (ifidx < DHD_MAX_IFS)); + + ifp = dhd->iflist[ifidx]; + if (ifp != NULL) { + if (ifp->net != NULL) { + netif_stop_queue(ifp->net); + unregister_netdev(ifp->net); + free_netdev(ifp->net); + } + } else + if ((ifp = MALLOC(dhd->pub.osh, sizeof(dhd_if_t))) == NULL) { + DHD_ERROR(("%s: OOM - dhd_if_t\n", __FUNCTION__)); + return -ENOMEM; + } + + memset(ifp, 0, sizeof(dhd_if_t)); + ifp->info = dhd; + dhd->iflist[ifidx] = ifp; + strncpy(ifp->name, name, IFNAMSIZ); + ifp->name[IFNAMSIZ] = '\0'; + if (mac_addr != NULL) + memcpy(&ifp->mac_addr, mac_addr, ETHER_ADDR_LEN); + + if (handle == NULL) { + ifp->state = DHD_IF_ADD; + ifp->idx = ifidx; + ifp->bssidx = bssidx; + ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); + up(&dhd->thr_sysioc_ctl.sema); + } else + ifp->net = (struct net_device *)handle; + + return 0; +} + +void +dhd_del_if(dhd_info_t *dhd, int ifidx) +{ + dhd_if_t *ifp; + + DHD_TRACE(("%s: idx %d\n", __FUNCTION__, ifidx)); + + ASSERT(dhd && ifidx && (ifidx < DHD_MAX_IFS)); + ifp = dhd->iflist[ifidx]; + if (!ifp) { + DHD_ERROR(("%s: Null interface\n", __FUNCTION__)); + return; + } + + ifp->state = DHD_IF_DEL; + ifp->idx = ifidx; + ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); + up(&dhd->thr_sysioc_ctl.sema); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) +static struct net_device_ops dhd_ops_pri = { + .ndo_open = dhd_open, + .ndo_stop = dhd_stop, + .ndo_get_stats = dhd_get_stats, + .ndo_do_ioctl = dhd_ioctl_entry, + .ndo_start_xmit = dhd_start_xmit, + .ndo_set_mac_address = dhd_set_mac_address, + .ndo_set_multicast_list = dhd_set_multicast_list, +}; + +static struct net_device_ops dhd_ops_virt = { + .ndo_get_stats = dhd_get_stats, + .ndo_do_ioctl = dhd_ioctl_entry, + .ndo_start_xmit = dhd_start_xmit, + .ndo_set_mac_address = dhd_set_mac_address, + .ndo_set_multicast_list = dhd_set_multicast_list, +}; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) */ + +dhd_pub_t * +dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) +{ + dhd_info_t *dhd = NULL; + struct net_device *net = NULL; + + dhd_attach_states_t dhd_state = DHD_ATTACH_STATE_INIT; + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + /* updates firmware nvram path if it was provided as module parameters */ + if ((firmware_path != NULL) && (firmware_path[0] != '\0')) + strcpy(fw_path, firmware_path); + if ((nvram_path != NULL) && (nvram_path[0] != '\0')) + strcpy(nv_path, nvram_path); + + /* Allocate etherdev, including space for private structure */ + if (!(net = alloc_etherdev(sizeof(dhd)))) { + DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__)); + goto fail; + } + dhd_state |= DHD_ATTACH_STATE_NET_ALLOC; + + /* Allocate primary dhd_info */ + if (!(dhd = MALLOC(osh, sizeof(dhd_info_t)))) { + DHD_ERROR(("%s: OOM - alloc dhd_info\n", __FUNCTION__)); + goto fail; + } + memset(dhd, 0, sizeof(dhd_info_t)); + +#ifdef DHDTHREAD + dhd->thr_dpc_ctl.thr_pid = DHD_PID_KT_TL_INVALID; + dhd->thr_wdt_ctl.thr_pid = DHD_PID_KT_INVALID; +#else + dhd->dhd_tasklet_create = FALSE; +#endif /* DHDTHREAD */ + dhd->thr_sysioc_ctl.thr_pid = DHD_PID_KT_INVALID; + dhd_state |= DHD_ATTACH_STATE_DHD_ALLOC; + + /* + * Save the dhd_info into the priv + */ + memcpy((void *)netdev_priv(net), &dhd, sizeof(dhd)); + dhd->pub.osh = osh; + + /* Link to info module */ + dhd->pub.info = dhd; + /* Link to bus module */ + dhd->pub.bus = bus; + dhd->pub.hdrlen = bus_hdrlen; + + /* Set network interface name if it was provided as module parameter */ + if (iface_name[0]) { + int len; + char ch; + strncpy(net->name, iface_name, IFNAMSIZ); + net->name[IFNAMSIZ - 1] = 0; + len = strlen(net->name); + ch = net->name[len - 1]; + if ((ch > '9' || ch < '0') && (len < IFNAMSIZ - 2)) + strcat(net->name, "%d"); + } + + if (dhd_add_if(dhd, 0, (void *)net, net->name, NULL, 0, 0) == DHD_BAD_IF) + goto fail; + dhd_state |= DHD_ATTACH_STATE_ADD_IF; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) + net->open = NULL; +#else + net->netdev_ops = NULL; +#endif + + sema_init(&dhd->proto_sem, 1); + +#ifdef PROP_TXSTATUS + spin_lock_init(&dhd->wlfc_spinlock); + dhd->pub.wlfc_enabled = TRUE; +#endif /* PROP_TXSTATUS */ + + /* Initialize other structure content */ + init_waitqueue_head(&dhd->ioctl_resp_wait); + init_waitqueue_head(&dhd->ctrl_wait); + + /* Initialize the spinlocks */ + spin_lock_init(&dhd->sdlock); + spin_lock_init(&dhd->txqlock); + spin_lock_init(&dhd->dhd_lock); + + /* Initialize Wakelock stuff */ + spin_lock_init(&dhd->wakelock_spinlock); + dhd->wakelock_counter = 0; + dhd->wakelock_rx_timeout_enable = 0; + dhd->wakelock_ctrl_timeout_enable = 0; +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&dhd->wl_wifi, WAKE_LOCK_SUSPEND, "wlan_wake"); + wake_lock_init(&dhd->wl_rxwake, WAKE_LOCK_SUSPEND, "wlan_rx_wake"); + wake_lock_init(&dhd->wl_ctrlwake, WAKE_LOCK_SUSPEND, "wlan_ctrl_wake"); +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + mutex_init(&dhd->dhd_net_if_mutex); + mutex_init(&dhd->dhd_suspend_mutex); +#endif + dhd_state |= DHD_ATTACH_STATE_WAKELOCKS_INIT; + + /* Attach and link in the protocol */ + if (dhd_prot_attach(&dhd->pub) != 0) { + DHD_ERROR(("dhd_prot_attach failed\n")); + goto fail; + } + dhd_state |= DHD_ATTACH_STATE_PROT_ATTACH; + +#ifdef WL_CFG80211 + /* Attach and link in the cfg80211 */ + if (unlikely(wl_cfg80211_attach(net, &dhd->pub))) { + DHD_ERROR(("wl_cfg80211_attach failed\n")); + goto fail; + } + + dhd_monitor_init(&dhd->pub); + dhd_state |= DHD_ATTACH_STATE_CFG80211; +#endif +#if defined(WL_WIRELESS_EXT) + /* Attach and link in the iw */ + if (!(dhd_state & DHD_ATTACH_STATE_CFG80211)) { + if (wl_iw_attach(net, (void *)&dhd->pub) != 0) { + DHD_ERROR(("wl_iw_attach failed\n")); + goto fail; + } + dhd_state |= DHD_ATTACH_STATE_WL_ATTACH; + } +#endif /* defined(WL_WIRELESS_EXT) */ + + + /* Set up the watchdog timer */ + init_timer(&dhd->timer); + dhd->timer.data = (ulong)dhd; + dhd->timer.function = dhd_watchdog; + +#ifdef DHDTHREAD + /* Initialize thread based operation and lock */ + sema_init(&dhd->sdsem, 1); + if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0)) { + dhd->threads_only = TRUE; + } + else { + dhd->threads_only = FALSE; + } + + if (dhd_dpc_prio >= 0) { + /* Initialize watchdog thread */ + PROC_START(dhd_watchdog_thread, dhd, &dhd->thr_wdt_ctl, 0); + } else { + dhd->thr_wdt_ctl.thr_pid = -1; + } + + /* Set up the bottom half handler */ + if (dhd_dpc_prio >= 0) { + /* Initialize DPC thread */ + PROC_START(dhd_dpc_thread, dhd, &dhd->thr_dpc_ctl, 0); + } else { + /* use tasklet for dpc */ + tasklet_init(&dhd->tasklet, dhd_dpc, (ulong)dhd); + dhd->thr_dpc_ctl.thr_pid = -1; + } +#else + /* Set up the bottom half handler */ + tasklet_init(&dhd->tasklet, dhd_dpc, (ulong)dhd); + dhd->dhd_tasklet_create = TRUE; +#endif /* DHDTHREAD */ + + if (dhd_sysioc) { + PROC_START(_dhd_sysioc_thread, dhd, &dhd->thr_sysioc_ctl, 0); + } else { + dhd->thr_sysioc_ctl.thr_pid = -1; + } + dhd_state |= DHD_ATTACH_STATE_THREADS_CREATED; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + INIT_WORK(&dhd->work_hang, dhd_hang_process); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ + /* + * Save the dhd_info into the priv + */ + memcpy(netdev_priv(net), &dhd, sizeof(dhd)); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) + register_pm_notifier(&dhd_sleep_pm_notifier); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ + +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) + dhd->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 20; + dhd->early_suspend.suspend = dhd_early_suspend; + dhd->early_suspend.resume = dhd_late_resume; + register_early_suspend(&dhd->early_suspend); + dhd_state |= DHD_ATTACH_STATE_EARLYSUSPEND_DONE; +#endif + +#ifdef ARP_OFFLOAD_SUPPORT + dhd->pend_ipaddr = 0; + register_inetaddr_notifier(&dhd_notifier); +#endif /* ARP_OFFLOAD_SUPPORT */ + + dhd_state |= DHD_ATTACH_STATE_DONE; + dhd->dhd_state = dhd_state; + return &dhd->pub; + +fail: + if (dhd_state < DHD_ATTACH_STATE_DHD_ALLOC) { + if (net) free_netdev(net); + } else { + DHD_TRACE(("%s: Calling dhd_detach dhd_state 0x%x &dhd->pub %p\n", + __FUNCTION__, dhd_state, &dhd->pub)); + dhd->dhd_state = dhd_state; + dhd_detach(&dhd->pub); + dhd_free(&dhd->pub); + } + + return NULL; +} + +int +dhd_bus_start(dhd_pub_t *dhdp) +{ + int ret = -1; + dhd_info_t *dhd = (dhd_info_t*)dhdp->info; + unsigned long flags; + + ASSERT(dhd); + + DHD_TRACE(("Enter %s:\n", __FUNCTION__)); + +#ifdef DHDTHREAD + if (dhd->threads_only) + dhd_os_sdlock(dhdp); +#endif /* DHDTHREAD */ + + /* try to download image and nvram to the dongle */ + if ((dhd->pub.busstate == DHD_BUS_DOWN) && + (fw_path != NULL) && (fw_path[0] != '\0') && + (nv_path != NULL) && (nv_path[0] != '\0')) { + /* wake lock moved to dhdsdio_download_firmware */ + if (!(dhd_bus_download_firmware(dhd->pub.bus, dhd->pub.osh, + fw_path, nv_path))) { + DHD_ERROR(("%s: dhdsdio_probe_download failed. firmware = %s nvram = %s\n", + __FUNCTION__, fw_path, nv_path)); +#ifdef DHDTHREAD + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ + return -1; + } + } + if (dhd->pub.busstate != DHD_BUS_LOAD) { +#ifdef DHDTHREAD + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ + return -ENETDOWN; + } + + /* Start the watchdog timer */ + dhd->pub.tickcnt = 0; + dhd_os_wd_timer(&dhd->pub, dhd_watchdog_ms); + + /* Bring up the bus */ + if ((ret = dhd_bus_init(&dhd->pub, FALSE)) != 0) { + + DHD_ERROR(("%s, dhd_bus_init failed %d\n", __FUNCTION__, ret)); +#ifdef DHDTHREAD + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ + return ret; + } +#if defined(OOB_INTR_ONLY) + /* Host registration for OOB interrupt */ + if (bcmsdh_register_oob_intr(dhdp)) { + /* deactivate timer and wait for the handler to finish */ + + flags = dhd_os_spin_lock(&dhd->pub); + dhd->wd_timer_valid = FALSE; + dhd_os_spin_unlock(&dhd->pub, flags); + del_timer_sync(&dhd->timer); + + DHD_ERROR(("%s Host failed to register for OOB\n", __FUNCTION__)); +#ifdef DHDTHREAD + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ + return -ENODEV; + } + + /* Enable oob at firmware */ + dhd_enable_oob_intr(dhd->pub.bus, TRUE); +#endif /* defined(OOB_INTR_ONLY) */ + + /* If bus is not ready, can't come up */ + if (dhd->pub.busstate != DHD_BUS_DATA) { + flags = dhd_os_spin_lock(&dhd->pub); + dhd->wd_timer_valid = FALSE; + dhd_os_spin_unlock(&dhd->pub, flags); + del_timer_sync(&dhd->timer); + DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__)); +#ifdef DHDTHREAD + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ + return -ENODEV; + } + +#ifdef DHDTHREAD + if (dhd->threads_only) + dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ + +#ifdef READ_MACADDR + dhd_read_macaddr(dhd); +#endif + + /* Bus is ready, do any protocol initialization */ + if ((ret = dhd_prot_init(&dhd->pub)) < 0) + return ret; + +#ifdef WRITE_MACADDR + dhd_write_macaddr(dhd->pub.mac.octet); +#endif + +#ifdef ARP_OFFLOAD_SUPPORT + if (dhd->pend_ipaddr) { +#ifdef AOE_IP_ALIAS_SUPPORT + aoe_update_host_ipv4_table(&dhd->pub, dhd->pend_ipaddr, TRUE); +#endif /* AOE_IP_ALIAS_SUPPORT */ + dhd->pend_ipaddr = 0; + } +#endif /* ARP_OFFLOAD_SUPPORT */ + + return 0; +} + +#if !defined(AP) && defined(WLP2P) && defined(WL_ENABLE_P2P_IF) +/* For Android ICS MR2 release, the concurrent mode is enabled by default and the firmware + * name would be fw_bcmdhd.bin. So we need to determine whether P2P is enabled in the STA + * firmware and accordingly enable concurrent mode (Apply P2P settings). SoftAP firmware + * would still be named as fw_bcmdhd_apsta. + */ +static u32 +dhd_concurrent_fw(dhd_pub_t *dhd) +{ + int ret = 0; + char buf[WLC_IOCTL_SMLEN]; + + if ((!op_mode) && (strstr(fw_path, "_p2p") == NULL) && + (strstr(fw_path, "_apsta") == NULL)) { + /* Given path is for the STA firmware. Check whether P2P support is present in + * the firmware. If so, set mode as P2P (concurrent support). + */ + memset(buf, 0, sizeof(buf)); + bcm_mkiovar("p2p", 0, 0, buf, sizeof(buf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), + FALSE, 0)) < 0) { + DHD_ERROR(("%s: Get P2P failed (error=%d)\n", __FUNCTION__, ret)); + } else if (buf[0] == 1) { + DHD_TRACE(("%s: P2P is supported\n", __FUNCTION__)); + return 1; + } + } + return ret; +} +#endif + +/* + * dhd_preinit_ioctls makes special pre-setting in the firmware before radio turns on + * returns : 0 if all settings passed or negative value if anything failed +*/ +int +dhd_preinit_ioctls(dhd_pub_t *dhd) +{ + int ret = 0; + char eventmask[WL_EVENTING_MASK_LEN]; + char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ +#if !defined(WL_CFG80211) + uint up = 0; +#endif /* defined(WL_CFG80211) */ + uint power_mode = PM_FAST; + uint32 dongle_align = DHD_SDALIGN; + uint32 glom = 0; + uint bcn_timeout = DHD_BEACON_TIMEOUT_NORMAL; + + uint retry_max = 3; +#if defined(ARP_OFFLOAD_SUPPORT) + int arpoe = 1; +#endif +#if defined(KEEP_ALIVE) + int res; +#endif /* defined(KEEP_ALIVE) */ + int scan_assoc_time = DHD_SCAN_ACTIVE_TIME; + int scan_unassoc_time = 40; + int scan_passive_time = DHD_SCAN_PASSIVE_TIME; + char buf[WLC_IOCTL_SMLEN]; + char *ptr; + uint32 listen_interval = LISTEN_INTERVAL; /* Default Listen Interval in Beacons */ + uint16 chipID; +#if defined(SOFTAP) + uint dtim = 1; +#endif +#if (defined(AP) && !defined(WLP2P)) || (!defined(AP) && defined(WL_CFG80211)) + uint32 mpc = 0; /* Turn MPC off for AP/APSTA mode */ +#endif +#if defined(AP) || defined(WLP2P) + uint32 apsta = 1; /* Enable APSTA mode */ +#endif /* defined(AP) || defined(WLP2P) */ +#ifdef GET_CUSTOM_MAC_ENABLE + struct ether_addr ea_addr; +#endif /* GET_CUSTOM_MAC_ENABLE */ + DHD_TRACE(("Enter %s\n", __FUNCTION__)); + dhd->op_mode = 0; +#ifdef GET_CUSTOM_MAC_ENABLE + ret = dhd_custom_get_mac_address(ea_addr.octet); + if (!ret) { + memset(buf, 0, sizeof(buf)); + bcm_mkiovar("cur_etheraddr", (void *)&ea_addr, ETHER_ADDR_LEN, buf, sizeof(buf)); + ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); + if (ret < 0) { + DHD_ERROR(("%s: can't set custom MAC address , error=%d\n", __FUNCTION__, ret)); + return BCME_NOTUP; + } + memcpy(dhd->mac.octet, ea_addr.octet, ETHER_ADDR_LEN); + } else { +#endif /* GET_CUSTOM_MAC_ENABLE */ + /* Get the default device MAC address directly from firmware */ + memset(buf, 0, sizeof(buf)); + bcm_mkiovar("cur_etheraddr", 0, 0, buf, sizeof(buf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), + FALSE, 0)) < 0) { + DHD_ERROR(("%s: can't get MAC address , error=%d\n", __FUNCTION__, ret)); + return BCME_NOTUP; + } + /* Update public MAC address after reading from Firmware */ + memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN); +#ifdef GET_CUSTOM_MAC_ENABLE + } +#endif /* GET_CUSTOM_MAC_ENABLE */ + +#ifdef SET_RANDOM_MAC_SOFTAP + if ((!op_mode && strstr(fw_path, "_apsta") != NULL) || (op_mode == HOSTAPD_MASK)) { + uint rand_mac; + + srandom32((uint)jiffies); + rand_mac = random32(); + iovbuf[0] = 0x02; /* locally administered bit */ + iovbuf[1] = 0x1A; + iovbuf[2] = 0x11; + iovbuf[3] = (unsigned char)(rand_mac & 0x0F) | 0xF0; + iovbuf[4] = (unsigned char)(rand_mac >> 8); + iovbuf[5] = (unsigned char)(rand_mac >> 16); + + bcm_mkiovar("cur_etheraddr", (void *)iovbuf, ETHER_ADDR_LEN, buf, sizeof(buf)); + ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); + if (ret < 0) { + DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret)); + } else + memcpy(dhd->mac.octet, iovbuf, ETHER_ADDR_LEN); + } +#endif /* SET_RANDOM_MAC_SOFTAP */ + + DHD_TRACE(("Firmware = %s\n", fw_path)); + +#if !defined(AP) && defined(WLP2P) + /* Check if firmware with WFD support used */ +#if defined(WL_ENABLE_P2P_IF) + if ((ret = dhd_concurrent_fw(dhd)) < 0) { + DHD_ERROR(("%s error : firmware can't support p2p mode\n", __FUNCTION__)); + goto done; + } +#endif /* (WL_ENABLE_P2P_IF) */ + + if ((!op_mode && strstr(fw_path, "_p2p") != NULL) +#if defined(WL_ENABLE_P2P_IF) + || (op_mode == WFD_MASK) || (dhd_concurrent_fw(dhd) == 1) +#endif + ) { + bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, + iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s APSTA setting failed ret= %d\n", __FUNCTION__, ret)); + } else { + dhd->op_mode |= WFD_MASK; +#if !defined(WL_ENABLE_P2P_IF) + /* ICS back capability : disable any packet filtering for p2p only mode */ + dhd_pkt_filter_enable = FALSE; +#endif /*!defined(WL_ENABLE_P2P_IF) */ + } + } +#endif + +#if !defined(AP) && defined(WL_CFG80211) + /* Check if firmware with HostAPD support used */ + if ((!op_mode && strstr(fw_path, "_apsta") != NULL) || (op_mode == HOSTAPD_MASK)) { + /* Disable A-band for HostAPD */ + uint band = WLC_BAND_2G; + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_BAND, (char *)&band, sizeof(band), + TRUE, 0)) < 0) { + DHD_ERROR(("%s:set band failed error (%d)\n", __FUNCTION__, ret)); + } + + /* Turn off wme if we are having only g ONLY firmware */ + bcm_mkiovar("nmode", 0, 0, buf, sizeof(buf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), + FALSE, 0)) < 0) { + DHD_ERROR(("%s:get nmode failed error (%d)\n", __FUNCTION__, ret)); + } + else { + DHD_TRACE(("%s:get nmode returned %d\n", __FUNCTION__,buf[0])); + } + if (buf[0] == 0) { + int wme = 0; + bcm_mkiovar("wme", (char *)&wme, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s set wme for HostAPD failed %d\n", __FUNCTION__, ret)); + } + else { + DHD_TRACE(("%s set wme succeeded for g ONLY firmware\n", __FUNCTION__)); + } + } + /* Turn off MPC in AP mode */ + bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s mpc for HostAPD failed %d\n", __FUNCTION__, ret)); + } else { + dhd->op_mode |= HOSTAPD_MASK; +#if defined(ARP_OFFLOAD_SUPPORT) + arpoe = 0; +#endif /* (ARP_OFFLOAD_SUPPORT) */ + /* disable any filtering for SoftAP mode */ + dhd_pkt_filter_enable = FALSE; + } + } +#endif + +#if !defined(WL_ENABLE_P2P_IF) + /* ICS mode setting for sta */ + if ((dhd->op_mode != WFD_MASK) && (dhd->op_mode != HOSTAPD_MASK)) { + /* STA only operation mode */ + dhd->op_mode |= STA_MASK; + dhd_pkt_filter_enable = TRUE; + } +#endif /* !defined(WL_ENABLE_P2P_IF) */ + + DHD_ERROR(("Firmware up: op_mode=%d, " + "Broadcom Dongle Host Driver mac=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", + dhd->op_mode, + dhd->mac.octet[0], dhd->mac.octet[1], dhd->mac.octet[2], + dhd->mac.octet[3], dhd->mac.octet[4], dhd->mac.octet[5])); + + /* Set Country code */ + if (dhd->dhd_cspec.ccode[0] != 0) { + bcm_mkiovar("country", (char *)&dhd->dhd_cspec, + sizeof(wl_country_t), iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) + DHD_ERROR(("%s: country code setting failed\n", __FUNCTION__)); + } + + /* Set Listen Interval */ + bcm_mkiovar("assoc_listen", (char *)&listen_interval, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) + DHD_ERROR(("%s assoc_listen failed %d\n", __FUNCTION__, ret)); + + /* Set PowerSave mode */ + dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode), TRUE, 0); + + /* Match Host and Dongle rx alignment */ + bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + + /* disable glom option for some chips */ + chipID = (uint16)dhd_bus_chip_id(dhd); + if ((chipID == BCM4330_CHIP_ID) || (chipID == BCM4329_CHIP_ID)) { + DHD_INFO(("%s disable glom for chipID=0x%X\n", __FUNCTION__, chipID)); + bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + } + + /* Setup timeout if Beacons are lost and roam is off to report link down */ + bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + /* Setup assoc_retry_max count to reconnect target AP in dongle */ + bcm_mkiovar("assoc_retry_max", (char *)&retry_max, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + +#if defined(AP) && !defined(WLP2P) + /* Turn off MPC in AP mode */ + bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +#endif /* defined(AP) && !defined(WLP2P) */ + +#if defined(SOFTAP) + if (ap_fw_loaded == TRUE) { + dhd_wl_ioctl_cmd(dhd, WLC_SET_DTIMPRD, (char *)&dtim, sizeof(dtim), TRUE, 0); + } +#endif + +#if defined(KEEP_ALIVE) + /* Set Keep Alive : be sure to use FW with -keepalive */ +#if defined(SOFTAP) + if (ap_fw_loaded == FALSE) +#endif + if ((res = dhd_keep_alive_onoff(dhd)) < 0) + DHD_ERROR(("%s set keeplive failed %d\n", + __FUNCTION__, res)); +#endif /* defined(KEEP_ALIVE) */ + + /* Read event_msgs mask */ + bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0)) < 0) { + DHD_ERROR(("%s read Event mask failed %d\n", __FUNCTION__, ret)); + goto done; + } + bcopy(iovbuf, eventmask, WL_EVENTING_MASK_LEN); + + /* Setup event_msgs */ + setbit(eventmask, WLC_E_SET_SSID); + setbit(eventmask, WLC_E_PRUNE); + setbit(eventmask, WLC_E_AUTH); + setbit(eventmask, WLC_E_REASSOC); + setbit(eventmask, WLC_E_REASSOC_IND); + setbit(eventmask, WLC_E_DEAUTH); + setbit(eventmask, WLC_E_DEAUTH_IND); + setbit(eventmask, WLC_E_DISASSOC_IND); + setbit(eventmask, WLC_E_DISASSOC); + setbit(eventmask, WLC_E_JOIN); + setbit(eventmask, WLC_E_ASSOC_IND); + setbit(eventmask, WLC_E_PSK_SUP); + setbit(eventmask, WLC_E_LINK); + setbit(eventmask, WLC_E_NDIS_LINK); + setbit(eventmask, WLC_E_MIC_ERROR); + setbit(eventmask, WLC_E_ASSOC_REQ_IE); + setbit(eventmask, WLC_E_ASSOC_RESP_IE); + setbit(eventmask, WLC_E_PMKID_CACHE); + setbit(eventmask, WLC_E_TXFAIL); + setbit(eventmask, WLC_E_JOIN_START); + setbit(eventmask, WLC_E_SCAN_COMPLETE); +#ifdef WLMEDIA_HTSF + setbit(eventmask, WLC_E_HTSFSYNC); +#endif /* WLMEDIA_HTSF */ +#ifdef PNO_SUPPORT + setbit(eventmask, WLC_E_PFN_NET_FOUND); +#endif /* PNO_SUPPORT */ + /* enable dongle roaming event */ + setbit(eventmask, WLC_E_ROAM); +#ifdef WL_CFG80211 + setbit(eventmask, WLC_E_ESCAN_RESULT); + if ((dhd->op_mode & WFD_MASK) == WFD_MASK) { + setbit(eventmask, WLC_E_ACTION_FRAME_RX); + setbit(eventmask, WLC_E_ACTION_FRAME_COMPLETE); + setbit(eventmask, WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE); + setbit(eventmask, WLC_E_P2P_PROBREQ_MSG); + setbit(eventmask, WLC_E_P2P_DISC_LISTEN_COMPLETE); + } +#endif /* WL_CFG80211 */ + + /* Write updated Event mask */ + bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s Set Event mask failed %d\n", __FUNCTION__, ret)); + goto done; + } + + dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_CHANNEL_TIME, (char *)&scan_assoc_time, + sizeof(scan_assoc_time), TRUE, 0); + dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_UNASSOC_TIME, (char *)&scan_unassoc_time, + sizeof(scan_unassoc_time), TRUE, 0); + dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_PASSIVE_TIME, (char *)&scan_passive_time, + sizeof(scan_passive_time), TRUE, 0); + +#ifdef ARP_OFFLOAD_SUPPORT + /* Set and enable ARP offload feature for STA only */ +#if defined(SOFTAP) + if (arpoe && !ap_fw_loaded) { +#else + if (arpoe) { +#endif + dhd_arp_offload_set(dhd, dhd_arp_mode); + dhd_arp_offload_enable(dhd, arpoe); + } else { + dhd_arp_offload_set(dhd, 0); + dhd_arp_offload_enable(dhd, FALSE); + } +#endif /* ARP_OFFLOAD_SUPPORT */ + +#ifdef PKT_FILTER_SUPPORT + /* Setup defintions for pktfilter , enable in suspend */ + dhd->pktfilter_count = 5; + /* Setup filter to allow only unicast */ + dhd->pktfilter[0] = "100 0 0 0 0x01 0x00"; + dhd->pktfilter[1] = NULL; + dhd->pktfilter[2] = NULL; + dhd->pktfilter[3] = NULL; + /* Add filter to pass multicastDNS packet and NOT filter out as Broadcast */ + dhd->pktfilter[4] = "104 0 0 0 0xFFFFFFFFFFFF 0x01005E0000FB"; +#if defined(SOFTAP) + if (ap_fw_loaded) { + int i; + for (i = 0; i < dhd->pktfilter_count; i++) { + dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i], + 0, dhd_master_mode); + } + } +#endif /* defined(SOFTAP) */ +#endif /* PKT_FILTER_SUPPORT */ + +#if !defined(WL_CFG80211) + /* Force STA UP */ + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&up, sizeof(up), TRUE, 0)) < 0) { + DHD_ERROR(("%s Setting WL UP failed %d\n", __FUNCTION__, ret)); + goto done; + } +#endif + /* query for 'ver' to get version info from firmware */ + memset(buf, 0, sizeof(buf)); + ptr = buf; + bcm_mkiovar("ver", (char *)&buf, 4, buf, sizeof(buf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), FALSE, 0)) < 0) + DHD_ERROR(("%s failed %d\n", __FUNCTION__, ret)); + else { + bcmstrtok(&ptr, "\n", 0); + /* Print fw version info */ + DHD_ERROR(("Firmware version = %s\n", buf)); + + DHD_BLOG(buf, strlen(buf) + 1); + DHD_BLOG(dhd_version, strlen(dhd_version) + 1); + + /* Check and adjust IOCTL response timeout for Manufactring firmware */ + if (strstr(buf, MANUFACTRING_FW) != NULL) { + dhd_os_set_ioctl_resp_timeout(IOCTL_RESP_TIMEOUT * 10); + DHD_ERROR(("%s : adjust IOCTL response time for Manufactring Firmware\n", __FUNCTION__)); + } + } + +done: + return ret; +} + + +int +dhd_iovar(dhd_pub_t *pub, int ifidx, char *name, char *cmd_buf, uint cmd_len, int set) +{ + char buf[strlen(name) + 1 + cmd_len]; + int len = sizeof(buf); + wl_ioctl_t ioc; + int ret; + + len = bcm_mkiovar(name, cmd_buf, cmd_len, buf, len); + + memset(&ioc, 0, sizeof(ioc)); + + ioc.cmd = set? WLC_SET_VAR : WLC_GET_VAR; + ioc.buf = buf; + ioc.len = len; + ioc.set = TRUE; + + ret = dhd_wl_ioctl(pub, ifidx, &ioc, ioc.buf, ioc.len); + if (!set && ret >= 0) + memcpy(cmd_buf, buf, cmd_len); + + return ret; +} + +int dhd_change_mtu(dhd_pub_t *dhdp, int new_mtu, int ifidx) +{ + struct dhd_info *dhd = dhdp->info; + struct net_device *dev = NULL; + + ASSERT(dhd && dhd->iflist[ifidx]); + dev = dhd->iflist[ifidx]->net; + ASSERT(dev); + + if (netif_running(dev)) { + DHD_ERROR(("%s: Must be down to change its MTU", dev->name)); + return BCME_NOTDOWN; + } + +#define DHD_MIN_MTU 1500 +#define DHD_MAX_MTU 1752 + + if ((new_mtu < DHD_MIN_MTU) || (new_mtu > DHD_MAX_MTU)) { + DHD_ERROR(("%s: MTU size %d is invalid.\n", __FUNCTION__, new_mtu)); + return BCME_BADARG; + } + + dev->mtu = new_mtu; + return 0; +} + +#ifdef ARP_OFFLOAD_SUPPORT +/* add or remove AOE host ip(s) (up to 8 IPs on the interface) */ +void +aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add) +{ + u32 ipv4_buf[MAX_IPV4_ENTRIES]; /* temp save for AOE host_ip table */ + int i; + int ret; + + bzero(ipv4_buf, sizeof(ipv4_buf)); + + /* display what we've got */ + ret = dhd_arp_get_arp_hostip_table(dhd_pub, ipv4_buf, sizeof(ipv4_buf)); + DHD_ARPOE(("%s: hostip table read from Dongle:\n", __FUNCTION__)); +#ifdef AOE_DBG + dhd_print_buf(ipv4_buf, 32, 4); /* max 8 IPs 4b each */ +#endif + /* now we saved hoste_ip table, clr it in the dongle AOE */ + dhd_aoe_hostip_clr(dhd_pub); + + if (ret) { + DHD_ERROR(("%s failed\n", __FUNCTION__)); + return; + } + + for (i = 0; i < MAX_IPV4_ENTRIES; i++) { + if (add && (ipv4_buf[i] == 0)) { + ipv4_buf[i] = ipa; + add = FALSE; /* added ipa to local table */ + DHD_ARPOE(("%s: Saved new IP in temp arp_hostip[%d]\n", + __FUNCTION__, i)); + } else if (ipv4_buf[i] == ipa) { + ipv4_buf[i] = 0; + DHD_ARPOE(("%s: removed IP:%x from temp table %d\n", + __FUNCTION__, ipa, i)); + } + + if (ipv4_buf[i] != 0) { + /* add back host_ip entries from our local cache */ + dhd_arp_offload_add_ip(dhd_pub, ipv4_buf[i]); + DHD_ARPOE(("%s: added IP:%x to dongle arp_hostip[%d]\n\n", + __FUNCTION__, ipv4_buf[i], i)); + } + } +#ifdef AOE_DBG + /* see the resulting hostip table */ + dhd_arp_get_arp_hostip_table(dhd_pub, ipv4_buf, sizeof(ipv4_buf)); + DHD_ARPOE(("%s: read back arp_hostip table:\n", __FUNCTION__)); + dhd_print_buf(ipv4_buf, 32, 4); /* max 8 IPs 4b each */ +#endif +} + +static int dhd_device_event(struct notifier_block *this, + unsigned long event, + void *ptr) +{ + struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; + + dhd_info_t *dhd; + dhd_pub_t *dhd_pub; + + if (!ifa) + return NOTIFY_DONE; + + dhd = *(dhd_info_t **)netdev_priv(ifa->ifa_dev->dev); + dhd_pub = &dhd->pub; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) + if (ifa->ifa_dev->dev->netdev_ops == &dhd_ops_pri) { +#else + if (ifa->ifa_dev->dev) { +#endif + switch (event) { + case NETDEV_UP: + DHD_ARPOE(("%s: [%s] Up IP: 0x%x\n", + __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); + + if (dhd->pub.busstate != DHD_BUS_DATA) { + DHD_ERROR(("%s: bus not ready, exit\n", __FUNCTION__)); + if (dhd->pend_ipaddr) { + DHD_ERROR(("%s: overwrite pending ipaddr: 0x%x\n", + __FUNCTION__, dhd->pend_ipaddr)); + } + dhd->pend_ipaddr = ifa->ifa_address; + break; + } + +#ifdef AOE_IP_ALIAS_SUPPORT + if (ifa->ifa_label[strlen(ifa->ifa_label)-2] == 0x3a) { + DHD_ARPOE(("%s:add aliased IP to AOE hostip cache\n", + __FUNCTION__)); + aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, TRUE); + } + else + aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, TRUE); +#endif + break; + + case NETDEV_DOWN: + DHD_ARPOE(("%s: [%s] Down IP: 0x%x\n", + __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); + dhd->pend_ipaddr = 0; +#ifdef AOE_IP_ALIAS_SUPPORT + if (!(ifa->ifa_label[strlen(ifa->ifa_label)-2] == 0x3a)) { + DHD_ARPOE(("%s: primary interface is down, AOE clr all\n", + __FUNCTION__)); + dhd_aoe_hostip_clr(&dhd->pub); + dhd_aoe_arp_clr(&dhd->pub); + } else + aoe_update_host_ipv4_table(dhd_pub, ifa->ifa_address, FALSE); +#else + dhd_aoe_hostip_clr(&dhd->pub); + dhd_aoe_arp_clr(&dhd->pub); +#endif + break; + + default: + DHD_ARPOE(("%s: do noting for [%s] Event: %lu\n", + __func__, ifa->ifa_label, event)); + break; + } + } + return NOTIFY_DONE; +} +#endif /* ARP_OFFLOAD_SUPPORT */ + +int +dhd_net_attach(dhd_pub_t *dhdp, int ifidx) +{ + dhd_info_t *dhd = (dhd_info_t *)dhdp->info; + struct net_device *net = NULL; + int err = 0; + uint8 temp_addr[ETHER_ADDR_LEN] = { 0x00, 0x90, 0x4c, 0x11, 0x22, 0x33 }; + + DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx)); + + ASSERT(dhd && dhd->iflist[ifidx]); + + net = dhd->iflist[ifidx]->net; + ASSERT(net); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) + ASSERT(!net->open); + net->get_stats = dhd_get_stats; + net->do_ioctl = dhd_ioctl_entry; + net->hard_start_xmit = dhd_start_xmit; + net->set_mac_address = dhd_set_mac_address; + net->set_multicast_list = dhd_set_multicast_list; + net->open = net->stop = NULL; +#else + ASSERT(!net->netdev_ops); + net->netdev_ops = &dhd_ops_virt; +#endif + + /* Ok, link into the network layer... */ + if (ifidx == 0) { + /* + * device functions for the primary interface only + */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) + net->open = dhd_open; + net->stop = dhd_stop; +#else + net->netdev_ops = &dhd_ops_pri; +#endif + } else { + /* + * We have to use the primary MAC for virtual interfaces + */ + memcpy(temp_addr, dhd->iflist[ifidx]->mac_addr, ETHER_ADDR_LEN); + /* + * Android sets the locally administered bit to indicate that this is a + * portable hotspot. This will not work in simultaneous AP/STA mode, + * nor with P2P. Need to set the Donlge's MAC address, and then use that. + */ + if (!memcmp(temp_addr, dhd->iflist[0]->mac_addr, + ETHER_ADDR_LEN)) { + DHD_ERROR(("%s interface [%s]: set locally administered bit in MAC\n", + __func__, net->name)); + temp_addr[0] |= 0x02; + } + } + + net->hard_header_len = ETH_HLEN + dhd->pub.hdrlen; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) + net->ethtool_ops = &dhd_ethtool_ops; +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ + +#if defined(WL_WIRELESS_EXT) +#if WIRELESS_EXT < 19 + net->get_wireless_stats = dhd_get_wireless_stats; +#endif /* WIRELESS_EXT < 19 */ +#if WIRELESS_EXT > 12 + net->wireless_handlers = (struct iw_handler_def *)&wl_iw_handler_def; +#endif /* WIRELESS_EXT > 12 */ +#endif /* defined(WL_WIRELESS_EXT) */ + + dhd->pub.rxsz = DBUS_RX_BUFFER_SIZE_DHD(net); + + memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN); + + if ((err = register_netdev(net)) != 0) { + DHD_ERROR(("couldn't register the net device, err %d\n", err)); + goto fail; + } + printf("Broadcom Dongle Host Driver: register interface [%s]" + " MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", + net->name, + net->dev_addr[0], net->dev_addr[1], net->dev_addr[2], + net->dev_addr[3], net->dev_addr[4], net->dev_addr[5]); + +#if defined(SOFTAP) && defined(WL_WIRELESS_EXT) && !defined(WL_CFG80211) + wl_iw_iscan_set_scan_broadcast_prep(net, 1); +#endif + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + if (ifidx == 0) { + up(&dhd_registration_sem); + } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ + return 0; + +fail: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) + net->open = NULL; +#else + net->netdev_ops = NULL; +#endif + return err; +} + +void +dhd_bus_detach(dhd_pub_t *dhdp) +{ + dhd_info_t *dhd; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (dhdp) { + dhd = (dhd_info_t *)dhdp->info; + if (dhd) { + + /* + * In case of Android cfg80211 driver, the bus is down in dhd_stop, + * calling stop again will cuase SD read/write errors. + */ + if (dhd->pub.busstate != DHD_BUS_DOWN) { + /* Stop the protocol module */ + dhd_prot_stop(&dhd->pub); + + /* Stop the bus module */ + dhd_bus_stop(dhd->pub.bus, TRUE); + } + +#if defined(OOB_INTR_ONLY) + bcmsdh_unregister_oob_intr(); +#endif /* defined(OOB_INTR_ONLY) */ + } + } +} + + +void dhd_detach(dhd_pub_t *dhdp) +{ + dhd_info_t *dhd; + unsigned long flags; + int timer_valid = FALSE; + + if (!dhdp) + return; + + dhd = (dhd_info_t *)dhdp->info; + if (!dhd) + return; + + DHD_TRACE(("%s: Enter state 0x%x\n", __FUNCTION__, dhd->dhd_state)); + + if (!(dhd->dhd_state & DHD_ATTACH_STATE_DONE)) { + /* Give sufficient time for threads to start running in case + * dhd_attach() has failed + */ + osl_delay(1000*100); + } + +#ifdef ARP_OFFLOAD_SUPPORT + unregister_inetaddr_notifier(&dhd_notifier); +#endif /* ARP_OFFLOAD_SUPPORT */ + +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) + if (dhd->dhd_state & DHD_ATTACH_STATE_EARLYSUSPEND_DONE) { + if (dhd->early_suspend.suspend) + unregister_early_suspend(&dhd->early_suspend); + } +#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + cancel_work_sync(&dhd->work_hang); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ + +#if defined(WL_WIRELESS_EXT) + if (dhd->dhd_state & DHD_ATTACH_STATE_WL_ATTACH) { + /* Detatch and unlink in the iw */ + wl_iw_detach(); + } +#endif /* defined(WL_WIRELESS_EXT) */ + + if (dhd->thr_sysioc_ctl.thr_pid >= 0) { + PROC_STOP(&dhd->thr_sysioc_ctl); + } + + /* delete all interfaces, start with virtual */ + if (dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) { + int i = 1; + dhd_if_t *ifp; + + /* Cleanup virtual interfaces */ + for (i = 1; i < DHD_MAX_IFS; i++) { + dhd_net_if_lock_local(dhd); + if (dhd->iflist[i]) { + dhd->iflist[i]->state = DHD_IF_DEL; + dhd->iflist[i]->idx = i; + dhd_op_if(dhd->iflist[i]); + } + dhd_net_if_unlock_local(dhd); + } + /* delete primary interface 0 */ + ifp = dhd->iflist[0]; + ASSERT(ifp); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) + if (ifp->net->open) +#else + if (ifp->net->netdev_ops == &dhd_ops_pri) +#endif + { + if (ifp->net) { + unregister_netdev(ifp->net); + free_netdev(ifp->net); + ifp->net = NULL; + } + MFREE(dhd->pub.osh, ifp, sizeof(*ifp)); + dhd->iflist[0] = NULL; + } + } + + /* Clear the watchdog timer */ + flags = dhd_os_spin_lock(&dhd->pub); + timer_valid = dhd->wd_timer_valid; + dhd->wd_timer_valid = FALSE; + dhd_os_spin_unlock(&dhd->pub, flags); + if (timer_valid) + del_timer_sync(&dhd->timer); + + if (dhd->dhd_state & DHD_ATTACH_STATE_THREADS_CREATED) { +#ifdef DHDTHREAD + if (dhd->thr_wdt_ctl.thr_pid >= 0) { + PROC_STOP(&dhd->thr_wdt_ctl); + } + + if (dhd->thr_dpc_ctl.thr_pid >= 0) { + PROC_STOP(&dhd->thr_dpc_ctl); + } + else +#endif /* DHDTHREAD */ + tasklet_kill(&dhd->tasklet); + } + if (dhd->dhd_state & DHD_ATTACH_STATE_PROT_ATTACH) { + dhd_bus_detach(dhdp); + + if (dhdp->prot) + dhd_prot_detach(dhdp); + } + +#ifdef WL_CFG80211 + if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { + wl_cfg80211_detach(NULL); + dhd_monitor_uninit(); + } +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) + unregister_pm_notifier(&dhd_sleep_pm_notifier); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ + + if (dhd->dhd_state & DHD_ATTACH_STATE_WAKELOCKS_INIT) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&dhd->wl_wifi); + wake_lock_destroy(&dhd->wl_rxwake); + wake_lock_destroy(&dhd->wl_ctrlwake); +#endif + } +} + + +void +dhd_free(dhd_pub_t *dhdp) +{ + dhd_info_t *dhd; + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (dhdp) { + dhd = (dhd_info_t *)dhdp->info; + if (dhd) + MFREE(dhd->pub.osh, dhd, sizeof(*dhd)); + } +} + +static void __exit +dhd_module_cleanup(void) +{ + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + dhd_bus_unregister(); + +#if defined(CONFIG_WIFI_CONTROL_FUNC) + wl_android_wifictrl_func_del(); +#endif /* CONFIG_WIFI_CONTROL_FUNC */ + wl_android_exit(); + + /* Call customer gpio to turn off power with WL_REG_ON signal */ + dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF); +} + +static int __init +dhd_module_init(void) +{ + int error = 0; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + wl_android_init(); + +#ifdef DHDTHREAD + /* Sanity check on the module parameters */ + do { + /* Both watchdog and DPC as tasklets are ok */ + if ((dhd_watchdog_prio < 0) && (dhd_dpc_prio < 0)) + break; + + /* If both watchdog and DPC are threads, TX must be deferred */ + if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0) && dhd_deferred_tx) + break; + + DHD_ERROR(("Invalid module parameters.\n")); + return -EINVAL; + } while (0); +#endif /* DHDTHREAD */ + + /* Call customer gpio to turn on power with WL_REG_ON signal */ + dhd_customer_gpio_wlan_ctrl(WLAN_POWER_ON); + +#if defined(CONFIG_WIFI_CONTROL_FUNC) + if (wl_android_wifictrl_func_add() < 0) + goto fail_1; +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + sema_init(&dhd_registration_sem, 0); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ + error = dhd_bus_register(); + + if (!error) + printf("\n%s\n", dhd_version); + else { + DHD_ERROR(("%s: sdio_register_driver failed\n", __FUNCTION__)); + goto fail_1; + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + /* + * Wait till MMC sdio_register_driver callback called and made driver attach. + * It's needed to make sync up exit from dhd insmod and + * Kernel MMC sdio device callback registration + */ + if (down_timeout(&dhd_registration_sem, msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT)) != 0) { + error = -ENODEV; + DHD_ERROR(("%s: sdio_register_driver timeout\n", __FUNCTION__)); + goto fail_2; + } +#endif +#if defined(WL_CFG80211) + wl_android_post_init(); +#endif /* defined(WL_CFG80211) */ + + return error; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +fail_2: + dhd_bus_unregister(); +#endif +fail_1: +#if defined(CONFIG_WIFI_CONTROL_FUNC) + wl_android_wifictrl_func_del(); +#endif + + /* Call customer gpio to turn off power with WL_REG_ON signal */ + dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF); + + return error; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +late_initcall(dhd_module_init); +#else +module_init(dhd_module_init); +#endif +module_exit(dhd_module_cleanup); + +/* + * OS specific functions required to implement DHD driver in OS independent way + */ +int +dhd_os_proto_block(dhd_pub_t *pub) +{ + dhd_info_t * dhd = (dhd_info_t *)(pub->info); + + if (dhd) { + down(&dhd->proto_sem); + return 1; + } + + return 0; +} + +int +dhd_os_proto_unblock(dhd_pub_t *pub) +{ + dhd_info_t * dhd = (dhd_info_t *)(pub->info); + + if (dhd) { + up(&dhd->proto_sem); + return 1; + } + + return 0; +} + +unsigned int +dhd_os_get_ioctl_resp_timeout(void) +{ + return ((unsigned int)dhd_ioctl_timeout_msec); +} + +void +dhd_os_set_ioctl_resp_timeout(unsigned int timeout_msec) +{ + dhd_ioctl_timeout_msec = (int)timeout_msec; +} + +int +dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending) +{ + dhd_info_t * dhd = (dhd_info_t *)(pub->info); + int timeout = dhd_ioctl_timeout_msec; + + /* Convert timeout in millsecond to jiffies */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + timeout = msecs_to_jiffies(timeout); +#else + timeout = timeout * HZ / 1000; +#endif + + timeout = wait_event_timeout(dhd->ioctl_resp_wait, (*condition), timeout); + return timeout; +} + +int +dhd_os_ioctl_resp_wake(dhd_pub_t *pub) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + + if (waitqueue_active(&dhd->ioctl_resp_wait)) { + wake_up(&dhd->ioctl_resp_wait); + } + + return 0; +} + +void +dhd_os_wd_timer(void *bus, uint wdtick) +{ + dhd_pub_t *pub = bus; + dhd_info_t *dhd = (dhd_info_t *)pub->info; + unsigned long flags; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + flags = dhd_os_spin_lock(pub); + + /* don't start the wd until fw is loaded */ + if (pub->busstate == DHD_BUS_DOWN) { + dhd_os_spin_unlock(pub, flags); + return; + } + + /* Totally stop the timer */ + if (!wdtick && dhd->wd_timer_valid == TRUE) { + dhd->wd_timer_valid = FALSE; + dhd_os_spin_unlock(pub, flags); +#ifdef DHDTHREAD + del_timer_sync(&dhd->timer); +#else + del_timer(&dhd->timer); +#endif /* DHDTHREAD */ + return; + } + + if (wdtick) { + dhd_watchdog_ms = (uint)wdtick; + /* Re arm the timer, at last watchdog period */ + mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000); + dhd->wd_timer_valid = TRUE; + } + dhd_os_spin_unlock(pub, flags); +} + +void * +dhd_os_open_image(char *filename) +{ + struct file *fp; + + fp = filp_open(filename, O_RDONLY, 0); + /* + * 2.6.11 (FC4) supports filp_open() but later revs don't? + * Alternative: + * fp = open_namei(AT_FDCWD, filename, O_RD, 0); + * ??? + */ + if (IS_ERR(fp)) + fp = NULL; + + return fp; +} + +int +dhd_os_get_image_block(char *buf, int len, void *image) +{ + struct file *fp = (struct file *)image; + int rdlen; + + if (!image) + return 0; + + rdlen = kernel_read(fp, fp->f_pos, buf, len); + if (rdlen > 0) + fp->f_pos += rdlen; + + return rdlen; +} + +void +dhd_os_close_image(void *image) +{ + if (image) + filp_close((struct file *)image, NULL); +} + + +void +dhd_os_sdlock(dhd_pub_t *pub) +{ + dhd_info_t *dhd; + + dhd = (dhd_info_t *)(pub->info); + +#ifdef DHDTHREAD + if (dhd->threads_only) + down(&dhd->sdsem); + else +#endif /* DHDTHREAD */ + spin_lock_bh(&dhd->sdlock); +} + +void +dhd_os_sdunlock(dhd_pub_t *pub) +{ + dhd_info_t *dhd; + + dhd = (dhd_info_t *)(pub->info); + +#ifdef DHDTHREAD + if (dhd->threads_only) + up(&dhd->sdsem); + else +#endif /* DHDTHREAD */ + spin_unlock_bh(&dhd->sdlock); +} + +void +dhd_os_sdlock_txq(dhd_pub_t *pub) +{ + dhd_info_t *dhd; + + dhd = (dhd_info_t *)(pub->info); + spin_lock_bh(&dhd->txqlock); +} + +void +dhd_os_sdunlock_txq(dhd_pub_t *pub) +{ + dhd_info_t *dhd; + + dhd = (dhd_info_t *)(pub->info); + spin_unlock_bh(&dhd->txqlock); +} + +void +dhd_os_sdlock_rxq(dhd_pub_t *pub) +{ +} + +void +dhd_os_sdunlock_rxq(dhd_pub_t *pub) +{ +} + +void +dhd_os_sdtxlock(dhd_pub_t *pub) +{ + dhd_os_sdlock(pub); +} + +void +dhd_os_sdtxunlock(dhd_pub_t *pub) +{ + dhd_os_sdunlock(pub); +} + +#if defined(CONFIG_DHD_USE_STATIC_BUF) +uint8* dhd_os_prealloc(void *osh, int section, uint size) +{ + return (uint8*)wl_android_prealloc(section, size); +} + +void dhd_os_prefree(void *osh, void *addr, uint size) +{ +} +#endif /* defined(CONFIG_DHD_USE_STATIC_BUF) */ + +#if defined(WL_WIRELESS_EXT) +struct iw_statistics * +dhd_get_wireless_stats(struct net_device *dev) +{ + int res = 0; + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + if (!dhd->pub.up) { + return NULL; + } + + res = wl_iw_get_wireless_stats(dev, &dhd->iw.wstats); + + if (res == 0) + return &dhd->iw.wstats; + else + return NULL; +} +#endif /* defined(WL_WIRELESS_EXT) */ + +static int +dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, + wl_event_msg_t *event, void **data) +{ + int bcmerror = 0; + ASSERT(dhd != NULL); + + bcmerror = wl_host_event(&dhd->pub, ifidx, pktdata, event, data); + if (bcmerror != BCME_OK) + return (bcmerror); + +#if defined(WL_WIRELESS_EXT) + if (event->bsscfgidx == 0) { + /* + * Wireless ext is on primary interface only + */ + + ASSERT(dhd->iflist[*ifidx] != NULL); + ASSERT(dhd->iflist[*ifidx]->net != NULL); + + if (dhd->iflist[*ifidx]->net) { + wl_iw_event(dhd->iflist[*ifidx]->net, event, *data); + } + } +#endif /* defined(WL_WIRELESS_EXT) */ + +#ifdef WL_CFG80211 + if ((ntoh32(event->event_type) == WLC_E_IF) && + (((dhd_if_event_t *)*data)->action == WLC_E_IF_ADD)) + /* If ADD_IF has been called directly by wl utility then we + * should not report this. In case if ADD_IF was called from + * CFG stack, then too this event need not be reported back + */ + return (BCME_OK); + if ((wl_cfg80211_is_progress_ifchange() || + wl_cfg80211_is_progress_ifadd()) && (*ifidx != 0)) { + /* + * If IF_ADD/CHANGE operation is going on, + * discard any event received on the virtual I/F + */ + return (BCME_OK); + } + + ASSERT(dhd->iflist[*ifidx] != NULL); + ASSERT(dhd->iflist[*ifidx]->net != NULL); + if (dhd->iflist[*ifidx]->net) { + wl_cfg80211_event(dhd->iflist[*ifidx]->net, event, *data); + } +#endif /* defined(WL_CFG80211) */ + + return (bcmerror); +} + +/* send up locally generated event */ +void +dhd_sendup_event(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data) +{ + switch (ntoh32(event->event_type)) { + /* Send up locally generated AMP HCI Events */ + case WLC_E_BTA_HCI_EVENT: { + struct sk_buff *p, *skb; + bcm_event_t *msg; + wl_event_msg_t *p_bcm_event; + char *ptr; + uint32 len; + uint32 pktlen; + dhd_if_t *ifp; + dhd_info_t *dhd; + uchar *eth; + int ifidx; + + len = ntoh32(event->datalen); + pktlen = sizeof(bcm_event_t) + len + 2; + dhd = dhdp->info; + ifidx = dhd_ifname2idx(dhd, event->ifname); + + if ((p = PKTGET(dhdp->osh, pktlen, FALSE))) { + ASSERT(ISALIGNED((uintptr)PKTDATA(dhdp->osh, p), sizeof(uint32))); + + msg = (bcm_event_t *) PKTDATA(dhdp->osh, p); + + bcopy(&dhdp->mac, &msg->eth.ether_dhost, ETHER_ADDR_LEN); + bcopy(&dhdp->mac, &msg->eth.ether_shost, ETHER_ADDR_LEN); + ETHER_TOGGLE_LOCALADDR(&msg->eth.ether_shost); + + msg->eth.ether_type = hton16(ETHER_TYPE_BRCM); + + /* BCM Vendor specific header... */ + msg->bcm_hdr.subtype = hton16(BCMILCP_SUBTYPE_VENDOR_LONG); + msg->bcm_hdr.version = BCMILCP_BCM_SUBTYPEHDR_VERSION; + bcopy(BRCM_OUI, &msg->bcm_hdr.oui[0], DOT11_OUI_LEN); + + /* vendor spec header length + pvt data length (private indication + * hdr + actual message itself) + */ + msg->bcm_hdr.length = hton16(BCMILCP_BCM_SUBTYPEHDR_MINLENGTH + + BCM_MSG_LEN + sizeof(wl_event_msg_t) + (uint16)len); + msg->bcm_hdr.usr_subtype = hton16(BCMILCP_BCM_SUBTYPE_EVENT); + + PKTSETLEN(dhdp->osh, p, (sizeof(bcm_event_t) + len + 2)); + + /* copy wl_event_msg_t into sk_buf */ + + /* pointer to wl_event_msg_t in sk_buf */ + p_bcm_event = &msg->event; + bcopy(event, p_bcm_event, sizeof(wl_event_msg_t)); + + /* copy hci event into sk_buf */ + bcopy(data, (p_bcm_event + 1), len); + + msg->bcm_hdr.length = hton16(sizeof(wl_event_msg_t) + + ntoh16(msg->bcm_hdr.length)); + PKTSETLEN(dhdp->osh, p, (sizeof(bcm_event_t) + len + 2)); + + ptr = (char *)(msg + 1); + /* Last 2 bytes of the message are 0x00 0x00 to signal that there + * are no ethertypes which are following this + */ + ptr[len+0] = 0x00; + ptr[len+1] = 0x00; + + skb = PKTTONATIVE(dhdp->osh, p); + eth = skb->data; + len = skb->len; + + ifp = dhd->iflist[ifidx]; + if (ifp == NULL) + ifp = dhd->iflist[0]; + + ASSERT(ifp); + skb->dev = ifp->net; + skb->protocol = eth_type_trans(skb, skb->dev); + + skb->data = eth; + skb->len = len; + + /* Strip header, count, deliver upward */ + skb_pull(skb, ETH_HLEN); + + /* Send the packet */ + if (in_interrupt()) { + netif_rx(skb); + } else { + netif_rx_ni(skb); + } + } + else { + /* Could not allocate a sk_buf */ + DHD_ERROR(("%s: unable to alloc sk_buf", __FUNCTION__)); + } + break; + } /* case WLC_E_BTA_HCI_EVENT */ + + default: + break; + } +} + +void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) + struct dhd_info *dhdinfo = dhd->info; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + int timeout = msecs_to_jiffies(2000); +#else + int timeout = 2 * HZ; +#endif + dhd_os_sdunlock(dhd); + wait_event_timeout(dhdinfo->ctrl_wait, (*lockvar == FALSE), timeout); + dhd_os_sdlock(dhd); +#endif + return; +} + +void dhd_wait_event_wakeup(dhd_pub_t *dhd) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) + struct dhd_info *dhdinfo = dhd->info; + if (waitqueue_active(&dhdinfo->ctrl_wait)) + wake_up(&dhdinfo->ctrl_wait); +#endif + return; +} + +int +dhd_dev_reset(struct net_device *dev, uint8 flag) +{ + int ret; + + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + ret = dhd_bus_devreset(&dhd->pub, flag); + if (ret) { + DHD_ERROR(("%s: dhd_bus_devreset: %d\n", __FUNCTION__, ret)); + return ret; + } + + return ret; +} + +int net_os_set_suspend_disable(struct net_device *dev, int val) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) { + ret = dhd->pub.suspend_disable_flag; + dhd->pub.suspend_disable_flag = val; + } + return ret; +} + +int net_os_set_suspend(struct net_device *dev, int val, int force) +{ + int ret = 0; + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + if (dhd) { +#if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) + ret = dhd_set_suspend(val, &dhd->pub); +#else + ret = dhd_suspend_resume_helper(dhd, val, force); +#endif + } + return ret; +} + +int net_os_set_dtim_skip(struct net_device *dev, int val) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + if (dhd) + dhd->pub.dtim_skip = val; + + return 0; +} + +int net_os_rxfilter_add_remove(struct net_device *dev, int add_remove, int num) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + char *filterp = NULL; + int ret = 0; + + if (!dhd || (num == DHD_UNICAST_FILTER_NUM) || + (num == DHD_MDNS_FILTER_NUM)) + return ret; + if (num >= dhd->pub.pktfilter_count) + return -EINVAL; + if (add_remove) { + switch (num) { + case DHD_BROADCAST_FILTER_NUM: + filterp = "101 0 0 0 0xFFFFFFFFFFFF 0xFFFFFFFFFFFF"; + break; + case DHD_MULTICAST4_FILTER_NUM: + filterp = "102 0 0 0 0xFFFFFF 0x01005E"; + break; + case DHD_MULTICAST6_FILTER_NUM: + filterp = "103 0 0 0 0xFFFF 0x3333"; + break; + default: + return -EINVAL; + } + } + dhd->pub.pktfilter[num] = filterp; + return ret; +} + +int dhd_os_set_packet_filter(dhd_pub_t *dhdp, int val) +{ + int ret = 0; + + /* Packet filtering is set only if we still in early-suspend and + * we need either to turn it ON or turn it OFF + * We can always turn it OFF in case of early-suspend, but we turn it + * back ON only if suspend_disable_flag was not set + */ + if (dhdp && dhdp->up) { + if (dhdp->in_suspend) { + if (!val || (val && !dhdp->suspend_disable_flag)) + dhd_set_packet_filter(val, dhdp); + } + } + return ret; + +} + +int net_os_set_packet_filter(struct net_device *dev, int val) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return dhd_os_set_packet_filter(&dhd->pub, val); +} + +int +dhd_dev_init_ioctl(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return dhd_preinit_ioctls(&dhd->pub); +} + +#ifdef PNO_SUPPORT +/* Linux wrapper to call common dhd_pno_clean */ +int +dhd_dev_pno_reset(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return (dhd_pno_clean(&dhd->pub)); +} + + +/* Linux wrapper to call common dhd_pno_enable */ +int +dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return (dhd_pno_enable(&dhd->pub, pfn_enabled)); +} + + +/* Linux wrapper to call common dhd_pno_set */ +int +dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, + ushort scan_fr, int pno_repeat, int pno_freq_expo_max) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return (dhd_pno_set(&dhd->pub, ssids_local, nssid, scan_fr, pno_repeat, pno_freq_expo_max)); +} + +/* Linux wrapper to call common dhd_pno_set_ex */ +int +dhd_dev_pno_set_ex(struct net_device *dev, wl_pfn_t* ssidnet, int nssid, + ushort pno_interval, int pno_repeat, int pno_expo_max, int pno_lost_time) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return (dhd_pno_set_ex(&dhd->pub, ssidnet, nssid, + pno_interval, pno_repeat, pno_expo_max, pno_lost_time)); +} + +/* Linux wrapper to get pno status */ +int +dhd_dev_get_pno_status(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + return (dhd_pno_get_status(&dhd->pub)); +} + +#endif /* PNO_SUPPORT */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +static void dhd_hang_process(struct work_struct *work) +{ + dhd_info_t *dhd; + struct net_device *dev; + + dhd = (dhd_info_t *)container_of(work, dhd_info_t, work_hang); + dev = dhd->iflist[0]->net; + + if (dev) { + rtnl_lock(); + dev_close(dev); + rtnl_unlock(); +#if defined(WL_WIRELESS_EXT) + wl_iw_send_priv_event(dev, "HANG"); +#endif +#if defined(WL_CFG80211) + wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); +#endif + } +} + +int net_os_send_hang_message(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) { + if (!dhd->pub.hang_was_sent) { + dhd->pub.hang_was_sent = 1; + schedule_work(&dhd->work_hang); + } + } + return ret; +} +#endif + +void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + if (dhd && dhd->pub.up) + memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); +} + +void dhd_net_if_lock(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + dhd_net_if_lock_local(dhd); +} + +void dhd_net_if_unlock(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + dhd_net_if_unlock_local(dhd); +} + +static void dhd_net_if_lock_local(dhd_info_t *dhd) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + if (dhd) + mutex_lock(&dhd->dhd_net_if_mutex); +#endif +} + +static void dhd_net_if_unlock_local(dhd_info_t *dhd) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + if (dhd) + mutex_unlock(&dhd->dhd_net_if_mutex); +#endif +} + +static void dhd_suspend_lock(dhd_pub_t *pub) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + if (dhd) + mutex_lock(&dhd->dhd_suspend_mutex); +#endif +} + +static void dhd_suspend_unlock(dhd_pub_t *pub) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + if (dhd) + mutex_unlock(&dhd->dhd_suspend_mutex); +#endif +} + +unsigned long dhd_os_spin_lock(dhd_pub_t *pub) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags = 0; + + if (dhd) + spin_lock_irqsave(&dhd->dhd_lock, flags); + + return flags; +} + +void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + + if (dhd) + spin_unlock_irqrestore(&dhd->dhd_lock, flags); +} + +static int +dhd_get_pend_8021x_cnt(dhd_info_t *dhd) +{ + return (atomic_read(&dhd->pend_8021x_cnt)); +} + +#define MAX_WAIT_FOR_8021X_TX 10 + +int +dhd_wait_pend8021x(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int timeout = 10 * HZ / 1000; + int ntimes = MAX_WAIT_FOR_8021X_TX; + int pend = dhd_get_pend_8021x_cnt(dhd); + + while (ntimes && pend) { + if (pend) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(timeout); + set_current_state(TASK_RUNNING); + ntimes--; + } + pend = dhd_get_pend_8021x_cnt(dhd); + } + return pend; +} + +#ifdef DHD_DEBUG +int +write_to_file(dhd_pub_t *dhd, uint8 *buf, int size) +{ + int ret = 0; + struct file *fp; + mm_segment_t old_fs; + loff_t pos = 0; + + /* change to KERNEL_DS address limit */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + + /* open file to write */ + fp = filp_open("/tmp/mem_dump", O_WRONLY|O_CREAT, 0640); + if (!fp) { + printf("%s: open file error\n", __FUNCTION__); + ret = -1; + goto exit; + } + + /* Write buf to file */ + fp->f_op->write(fp, buf, size, &pos); + +exit: + /* free buf before return */ + MFREE(dhd->osh, buf, size); + /* close file before return */ + if (fp) + filp_close(fp, current->files); + /* restore previous address limit */ + set_fs(old_fs); + + return ret; +} +#endif /* DHD_DEBUG */ + +int dhd_os_wake_lock_timeout(dhd_pub_t *pub) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags; + int ret = 0; + + if (dhd) { + spin_lock_irqsave(&dhd->wakelock_spinlock, flags); + ret = dhd->wakelock_rx_timeout_enable > dhd->wakelock_ctrl_timeout_enable ? + dhd->wakelock_rx_timeout_enable : dhd->wakelock_ctrl_timeout_enable; +#ifdef CONFIG_HAS_WAKELOCK + if (dhd->wakelock_rx_timeout_enable) + wake_lock_timeout(&dhd->wl_rxwake, + msecs_to_jiffies(dhd->wakelock_rx_timeout_enable)); + if (dhd->wakelock_ctrl_timeout_enable) + wake_lock_timeout(&dhd->wl_ctrlwake, + msecs_to_jiffies(dhd->wakelock_ctrl_timeout_enable)); +#endif + dhd->wakelock_rx_timeout_enable = 0; + dhd->wakelock_ctrl_timeout_enable = 0; + spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); + } + return ret; +} + +int net_os_wake_lock_timeout(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) + ret = dhd_os_wake_lock_timeout(&dhd->pub); + return ret; +} + +int dhd_os_wake_lock_rx_timeout_enable(dhd_pub_t *pub, int val) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags; + + if (dhd) { + spin_lock_irqsave(&dhd->wakelock_spinlock, flags); + if (val > dhd->wakelock_rx_timeout_enable) + dhd->wakelock_rx_timeout_enable = val; + spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); + } + return 0; +} + +int dhd_os_wake_lock_ctrl_timeout_enable(dhd_pub_t *pub, int val) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags; + + if (dhd) { + spin_lock_irqsave(&dhd->wakelock_spinlock, flags); + if (val > dhd->wakelock_ctrl_timeout_enable) + dhd->wakelock_ctrl_timeout_enable = val; + spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); + } + return 0; +} + +int net_os_wake_lock_rx_timeout_enable(struct net_device *dev, int val) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) + ret = dhd_os_wake_lock_rx_timeout_enable(&dhd->pub, val); + return ret; +} + +int net_os_wake_lock_ctrl_timeout_enable(struct net_device *dev, int val) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) + ret = dhd_os_wake_lock_ctrl_timeout_enable(&dhd->pub, val); + return ret; +} + +int dhd_os_wake_lock(dhd_pub_t *pub) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags; + int ret = 0; + + if (dhd) { + spin_lock_irqsave(&dhd->wakelock_spinlock, flags); +#ifdef CONFIG_HAS_WAKELOCK + if (!dhd->wakelock_counter) + wake_lock(&dhd->wl_wifi); +#endif + dhd->wakelock_counter++; + ret = dhd->wakelock_counter; + spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); + } + return ret; +} + +int net_os_wake_lock(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) + ret = dhd_os_wake_lock(&dhd->pub); + return ret; +} + +int dhd_os_wake_unlock(dhd_pub_t *pub) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags; + int ret = 0; + + dhd_os_wake_lock_timeout(pub); + if (dhd) { + spin_lock_irqsave(&dhd->wakelock_spinlock, flags); + if (dhd->wakelock_counter) { + dhd->wakelock_counter--; +#ifdef CONFIG_HAS_WAKELOCK + if (!dhd->wakelock_counter) + wake_unlock(&dhd->wl_wifi); +#endif + ret = dhd->wakelock_counter; + } + spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); + } + return ret; +} + +int dhd_os_check_wakelock(void *dhdp) +{ +#ifdef CONFIG_HAS_WAKELOCK + dhd_pub_t *pub = (dhd_pub_t *)dhdp; + dhd_info_t *dhd; + + if (!pub) + return 0; + dhd = (dhd_info_t *)(pub->info); + + if (dhd && wake_lock_active(&dhd->wl_wifi)) + return 1; +#endif + return 0; +} + +int net_os_wake_unlock(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) + ret = dhd_os_wake_unlock(&dhd->pub); + return ret; +} + +int dhd_os_check_if_up(void *dhdp) +{ + dhd_pub_t *pub = (dhd_pub_t *)dhdp; + + if (!pub) + return 0; + return pub->up; +} + +int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd) +{ + int ifidx; + int ret = 0; + dhd_info_t *dhd = NULL; + + if (!net || !netdev_priv(net)) { + DHD_ERROR(("%s invalid parameter\n", __FUNCTION__)); + return -EINVAL; + } + + dhd = *(dhd_info_t **)netdev_priv(net); + ifidx = dhd_net2idx(dhd, net); + if (ifidx == DHD_BAD_IF) { + DHD_ERROR(("%s bad ifidx\n", __FUNCTION__)); + return -ENODEV; + } + + DHD_OS_WAKE_LOCK(&dhd->pub); + ret = dhd_wl_ioctl(&dhd->pub, ifidx, ioc, ioc->buf, ioc->len); + dhd_check_hang(net, &dhd->pub, ret); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + + return ret; +} + +bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret) +{ + struct net_device *net; + + net = dhd_idx2net(dhdp, ifidx); + return dhd_check_hang(net, dhdp, ret); +} + +#ifdef PROP_TXSTATUS +extern int dhd_wlfc_interface_entry_update(void* state, ewlfc_mac_entry_action_t action, uint8 ifid, + uint8 iftype, uint8* ea); +extern int dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits); + +int dhd_wlfc_interface_event(struct dhd_info *dhd, + ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea) +{ + if (dhd->pub.wlfc_state == NULL) + return BCME_OK; + + return dhd_wlfc_interface_entry_update(dhd->pub.wlfc_state, action, ifid, iftype, ea); +} + +int dhd_wlfc_FIFOcreditmap_event(struct dhd_info *dhd, uint8* event_data) +{ + if (dhd->pub.wlfc_state == NULL) + return BCME_OK; + + return dhd_wlfc_FIFOcreditmap_update(dhd->pub.wlfc_state, event_data); +} + +int dhd_wlfc_event(struct dhd_info *dhd) +{ + return dhd_wlfc_enable(&dhd->pub); +} +#endif /* PROP_TXSTATUS */ + +#ifdef BCMDBGFS + +#include + +extern uint32 dhd_readregl(void *bp, uint32 addr); +extern uint32 dhd_writeregl(void *bp, uint32 addr, uint32 data); + +typedef struct dhd_dbgfs { + struct dentry *debugfs_dir; + struct dentry *debugfs_mem; + dhd_pub_t *dhdp; + uint32 size; +} dhd_dbgfs_t; + +dhd_dbgfs_t g_dbgfs; + +static int +dhd_dbg_state_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t +dhd_dbg_state_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + ssize_t rval; + uint32 tmp; + loff_t pos = *ppos; + size_t ret; + + if (pos < 0) + return -EINVAL; + if (pos >= g_dbgfs.size || !count) + return 0; + if (count > g_dbgfs.size - pos) + count = g_dbgfs.size - pos; + + /* Basically enforce aligned 4 byte reads. It's up to the user to work out the details */ + tmp = dhd_readregl(g_dbgfs.dhdp->bus, file->f_pos & (~3)); + + ret = copy_to_user(ubuf, &tmp, 4); + if (ret == count) + return -EFAULT; + + count -= ret; + *ppos = pos + count; + rval = count; + + return rval; +} + + +static ssize_t +dhd_debugfs_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) +{ + loff_t pos = *ppos; + size_t ret; + uint32 buf; + + if (pos < 0) + return -EINVAL; + if (pos >= g_dbgfs.size || !count) + return 0; + if (count > g_dbgfs.size - pos) + count = g_dbgfs.size - pos; + + ret = copy_from_user(&buf, ubuf, sizeof(uint32)); + if (ret == count) + return -EFAULT; + + /* Basically enforce aligned 4 byte writes. It's up to the user to work out the details */ + dhd_writeregl(g_dbgfs.dhdp->bus, file->f_pos & (~3), buf); + + return count; +} + + +loff_t +dhd_debugfs_lseek(struct file *file, loff_t off, int whence) +{ + loff_t pos = -1; + + switch (whence) { + case 0: + pos = off; + break; + case 1: + pos = file->f_pos + off; + break; + case 2: + pos = g_dbgfs.size - off; + } + return (pos < 0 || pos > g_dbgfs.size) ? -EINVAL : (file->f_pos = pos); +} + +static const struct file_operations dhd_dbg_state_ops = { + .read = dhd_dbg_state_read, + .write = dhd_debugfs_write, + .open = dhd_dbg_state_open, + .llseek = dhd_debugfs_lseek +}; + +static void dhd_dbg_create(void) +{ + if (g_dbgfs.debugfs_dir) { + g_dbgfs.debugfs_mem = debugfs_create_file("mem", 0644, g_dbgfs.debugfs_dir, + NULL, &dhd_dbg_state_ops); + } +} + +void dhd_dbg_init(dhd_pub_t *dhdp) +{ + int err; + + g_dbgfs.dhdp = dhdp; + g_dbgfs.size = 0x20000000; /* Allow access to various cores regs */ + + g_dbgfs.debugfs_dir = debugfs_create_dir("dhd", 0); + if (IS_ERR(g_dbgfs.debugfs_dir)) { + err = PTR_ERR(g_dbgfs.debugfs_dir); + g_dbgfs.debugfs_dir = NULL; + return; + } + + dhd_dbg_create(); + + return; +} + +void dhd_dbg_remove(void) +{ + debugfs_remove(g_dbgfs.debugfs_mem); + debugfs_remove(g_dbgfs.debugfs_dir); + + bzero((unsigned char *) &g_dbgfs, sizeof(g_dbgfs)); + +} +#endif /* ifdef BCMDBGFS */ + +#ifdef WLMEDIA_HTSF + +static +void dhd_htsf_addtxts(dhd_pub_t *dhdp, void *pktbuf) +{ + dhd_info_t *dhd = (dhd_info_t *)(dhdp->info); + struct sk_buff *skb; + uint32 htsf = 0; + uint16 dport = 0, oldmagic = 0xACAC; + char *p1; + htsfts_t ts; + + /* timestamp packet */ + + p1 = (char*) PKTDATA(dhdp->osh, pktbuf); + + if (PKTLEN(dhdp->osh, pktbuf) > HTSF_MINLEN) { +/* memcpy(&proto, p1+26, 4); */ + memcpy(&dport, p1+40, 2); +/* proto = ((ntoh32(proto))>> 16) & 0xFF; */ + dport = ntoh16(dport); + } + + /* timestamp only if icmp or udb iperf with port 5555 */ +/* if (proto == 17 && dport == tsport) { */ + if (dport >= tsport && dport <= tsport + 20) { + + skb = (struct sk_buff *) pktbuf; + + htsf = dhd_get_htsf(dhd, 0); + memset(skb->data + 44, 0, 2); /* clear checksum */ + memcpy(skb->data+82, &oldmagic, 2); + memcpy(skb->data+84, &htsf, 4); + + memset(&ts, 0, sizeof(htsfts_t)); + ts.magic = HTSFMAGIC; + ts.prio = PKTPRIO(pktbuf); + ts.seqnum = htsf_seqnum++; + ts.c10 = get_cycles(); + ts.t10 = htsf; + ts.endmagic = HTSFENDMAGIC; + + memcpy(skb->data + HTSF_HOSTOFFSET, &ts, sizeof(ts)); + } +} + +static void dhd_dump_htsfhisto(histo_t *his, char *s) +{ + int pktcnt = 0, curval = 0, i; + for (i = 0; i < (NUMBIN-2); i++) { + curval += 500; + printf("%d ", his->bin[i]); + pktcnt += his->bin[i]; + } + printf(" max: %d TotPkt: %d neg: %d [%s]\n", his->bin[NUMBIN-2], pktcnt, + his->bin[NUMBIN-1], s); +} + +static +void sorttobin(int value, histo_t *histo) +{ + int i, binval = 0; + + if (value < 0) { + histo->bin[NUMBIN-1]++; + return; + } + if (value > histo->bin[NUMBIN-2]) /* store the max value */ + histo->bin[NUMBIN-2] = value; + + for (i = 0; i < (NUMBIN-2); i++) { + binval += 500; /* 500m s bins */ + if (value <= binval) { + histo->bin[i]++; + return; + } + } + histo->bin[NUMBIN-3]++; +} + +static +void dhd_htsf_addrxts(dhd_pub_t *dhdp, void *pktbuf) +{ + dhd_info_t *dhd = (dhd_info_t *)dhdp->info; + struct sk_buff *skb; + char *p1; + uint16 old_magic; + int d1, d2, d3, end2end; + htsfts_t *htsf_ts; + uint32 htsf; + + skb = PKTTONATIVE(dhdp->osh, pktbuf); + p1 = (char*)PKTDATA(dhdp->osh, pktbuf); + + if (PKTLEN(osh, pktbuf) > HTSF_MINLEN) { + memcpy(&old_magic, p1+78, 2); + htsf_ts = (htsfts_t*) (p1 + HTSF_HOSTOFFSET - 4); + } + else + return; + + if (htsf_ts->magic == HTSFMAGIC) { + htsf_ts->tE0 = dhd_get_htsf(dhd, 0); + htsf_ts->cE0 = get_cycles(); + } + + if (old_magic == 0xACAC) { + + tspktcnt++; + htsf = dhd_get_htsf(dhd, 0); + memcpy(skb->data+92, &htsf, sizeof(uint32)); + + memcpy(&ts[tsidx].t1, skb->data+80, 16); + + d1 = ts[tsidx].t2 - ts[tsidx].t1; + d2 = ts[tsidx].t3 - ts[tsidx].t2; + d3 = ts[tsidx].t4 - ts[tsidx].t3; + end2end = ts[tsidx].t4 - ts[tsidx].t1; + + sorttobin(d1, &vi_d1); + sorttobin(d2, &vi_d2); + sorttobin(d3, &vi_d3); + sorttobin(end2end, &vi_d4); + + if (end2end > 0 && end2end > maxdelay) { + maxdelay = end2end; + maxdelaypktno = tspktcnt; + memcpy(&maxdelayts, &ts[tsidx], 16); + } + if (++tsidx >= TSMAX) + tsidx = 0; + } +} + +uint32 dhd_get_htsf(dhd_info_t *dhd, int ifidx) +{ + uint32 htsf = 0, cur_cycle, delta, delta_us; + uint32 factor, baseval, baseval2; + cycles_t t; + + t = get_cycles(); + cur_cycle = t; + + if (cur_cycle > dhd->htsf.last_cycle) + delta = cur_cycle - dhd->htsf.last_cycle; + else { + delta = cur_cycle + (0xFFFFFFFF - dhd->htsf.last_cycle); + } + + delta = delta >> 4; + + if (dhd->htsf.coef) { + /* times ten to get the first digit */ + factor = (dhd->htsf.coef*10 + dhd->htsf.coefdec1); + baseval = (delta*10)/factor; + baseval2 = (delta*10)/(factor+1); + delta_us = (baseval - (((baseval - baseval2) * dhd->htsf.coefdec2)) / 10); + htsf = (delta_us << 4) + dhd->htsf.last_tsf + HTSF_BUS_DELAY; + } + else { + DHD_ERROR(("-------dhd->htsf.coef = 0 -------\n")); + } + + return htsf; +} + +static void dhd_dump_latency(void) +{ + int i, max = 0; + int d1, d2, d3, d4, d5; + + printf("T1 T2 T3 T4 d1 d2 t4-t1 i \n"); + for (i = 0; i < TSMAX; i++) { + d1 = ts[i].t2 - ts[i].t1; + d2 = ts[i].t3 - ts[i].t2; + d3 = ts[i].t4 - ts[i].t3; + d4 = ts[i].t4 - ts[i].t1; + d5 = ts[max].t4-ts[max].t1; + if (d4 > d5 && d4 > 0) { + max = i; + } + printf("%08X %08X %08X %08X \t%d %d %d %d i=%d\n", + ts[i].t1, ts[i].t2, ts[i].t3, ts[i].t4, + d1, d2, d3, d4, i); + } + + printf("current idx = %d \n", tsidx); + + printf("Highest latency %d pkt no.%d total=%d\n", maxdelay, maxdelaypktno, tspktcnt); + printf("%08X %08X %08X %08X \t%d %d %d %d\n", + maxdelayts.t1, maxdelayts.t2, maxdelayts.t3, maxdelayts.t4, + maxdelayts.t2 - maxdelayts.t1, + maxdelayts.t3 - maxdelayts.t2, + maxdelayts.t4 - maxdelayts.t3, + maxdelayts.t4 - maxdelayts.t1); +} + + +static int +dhd_ioctl_htsf_get(dhd_info_t *dhd, int ifidx) +{ + wl_ioctl_t ioc; + char buf[32]; + int ret; + uint32 s1, s2; + + struct tsf { + uint32 low; + uint32 high; + } tsf_buf; + + memset(&ioc, 0, sizeof(ioc)); + memset(&tsf_buf, 0, sizeof(tsf_buf)); + + ioc.cmd = WLC_GET_VAR; + ioc.buf = buf; + ioc.len = (uint)sizeof(buf); + ioc.set = FALSE; + + strcpy(buf, "tsf"); + s1 = dhd_get_htsf(dhd, 0); + if ((ret = dhd_wl_ioctl(&dhd->pub, ifidx, &ioc, ioc.buf, ioc.len)) < 0) { + if (ret == -EIO) { + DHD_ERROR(("%s: tsf is not supported by device\n", + dhd_ifname(&dhd->pub, ifidx))); + return -EOPNOTSUPP; + } + return ret; + } + s2 = dhd_get_htsf(dhd, 0); + + memcpy(&tsf_buf, buf, sizeof(tsf_buf)); + printf(" TSF_h=%04X lo=%08X Calc:htsf=%08X, coef=%d.%d%d delta=%d ", + tsf_buf.high, tsf_buf.low, s2, dhd->htsf.coef, dhd->htsf.coefdec1, + dhd->htsf.coefdec2, s2-tsf_buf.low); + printf("lasttsf=%08X lastcycle=%08X\n", dhd->htsf.last_tsf, dhd->htsf.last_cycle); + return 0; +} + +void htsf_update(dhd_info_t *dhd, void *data) +{ + static ulong cur_cycle = 0, prev_cycle = 0; + uint32 htsf, tsf_delta = 0; + uint32 hfactor = 0, cyc_delta, dec1 = 0, dec2, dec3, tmp; + ulong b, a; + cycles_t t; + + /* cycles_t in inlcude/mips/timex.h */ + + t = get_cycles(); + + prev_cycle = cur_cycle; + cur_cycle = t; + + if (cur_cycle > prev_cycle) + cyc_delta = cur_cycle - prev_cycle; + else { + b = cur_cycle; + a = prev_cycle; + cyc_delta = cur_cycle + (0xFFFFFFFF - prev_cycle); + } + + if (data == NULL) + printf(" tsf update ata point er is null \n"); + + memcpy(&prev_tsf, &cur_tsf, sizeof(tsf_t)); + memcpy(&cur_tsf, data, sizeof(tsf_t)); + + if (cur_tsf.low == 0) { + DHD_INFO((" ---- 0 TSF, do not update, return\n")); + return; + } + + if (cur_tsf.low > prev_tsf.low) + tsf_delta = (cur_tsf.low - prev_tsf.low); + else { + DHD_INFO((" ---- tsf low is smaller cur_tsf= %08X, prev_tsf=%08X, \n", + cur_tsf.low, prev_tsf.low)); + if (cur_tsf.high > prev_tsf.high) { + tsf_delta = cur_tsf.low + (0xFFFFFFFF - prev_tsf.low); + DHD_INFO((" ---- Wrap around tsf coutner adjusted TSF=%08X\n", tsf_delta)); + } + else + return; /* do not update */ + } + + if (tsf_delta) { + hfactor = cyc_delta / tsf_delta; + tmp = (cyc_delta - (hfactor * tsf_delta))*10; + dec1 = tmp/tsf_delta; + dec2 = ((tmp - dec1*tsf_delta)*10) / tsf_delta; + tmp = (tmp - (dec1*tsf_delta))*10; + dec3 = ((tmp - dec2*tsf_delta)*10) / tsf_delta; + + if (dec3 > 4) { + if (dec2 == 9) { + dec2 = 0; + if (dec1 == 9) { + dec1 = 0; + hfactor++; + } + else { + dec1++; + } + } + else + dec2++; + } + } + + if (hfactor) { + htsf = ((cyc_delta * 10) / (hfactor*10+dec1)) + prev_tsf.low; + dhd->htsf.coef = hfactor; + dhd->htsf.last_cycle = cur_cycle; + dhd->htsf.last_tsf = cur_tsf.low; + dhd->htsf.coefdec1 = dec1; + dhd->htsf.coefdec2 = dec2; + } + else { + htsf = prev_tsf.low; + } +} + +#endif /* WLMEDIA_HTSF */ diff --git a/drivers/net/wireless/bcmdhd/dhd_linux_mon.c b/drivers/net/wireless/bcmdhd/dhd_linux_mon.c new file mode 100644 index 0000000000000..dd9c71f75be66 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_linux_mon.c @@ -0,0 +1,393 @@ +/* + * Broadcom Dongle Host Driver (DHD), Linux monitor network interface + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_linux_mon.c,v 1.131.2.55 2011-02-09 05:31:56 Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +typedef enum monitor_states +{ + MONITOR_STATE_DEINIT = 0x0, + MONITOR_STATE_INIT = 0x1, + MONITOR_STATE_INTERFACE_ADDED = 0x2, + MONITOR_STATE_INTERFACE_DELETED = 0x4 +} monitor_states_t; +extern int dhd_start_xmit(struct sk_buff *skb, struct net_device *net); + +/** + * Local declarations and defintions (not exposed) + */ +#define MON_PRINT(format, ...) printk("DHD-MON: %s " format, __func__, ##__VA_ARGS__) +#define MON_TRACE MON_PRINT + +typedef struct monitor_interface { + int radiotap_enabled; + struct net_device* real_ndev; /* The real interface that the monitor is on */ + struct net_device* mon_ndev; +} monitor_interface; + +typedef struct dhd_linux_monitor { + void *dhd_pub; + monitor_states_t monitor_state; + monitor_interface mon_if[DHD_MAX_IFS]; + struct mutex lock; /* lock to protect mon_if */ +} dhd_linux_monitor_t; + +static dhd_linux_monitor_t g_monitor; + +static struct net_device* lookup_real_netdev(char *name); +static monitor_interface* ndev_to_monif(struct net_device *ndev); +static int dhd_mon_if_open(struct net_device *ndev); +static int dhd_mon_if_stop(struct net_device *ndev); +static int dhd_mon_if_subif_start_xmit(struct sk_buff *skb, struct net_device *ndev); +static void dhd_mon_if_set_multicast_list(struct net_device *ndev); +static int dhd_mon_if_change_mac(struct net_device *ndev, void *addr); + +static const struct net_device_ops dhd_mon_if_ops = { + .ndo_open = dhd_mon_if_open, + .ndo_stop = dhd_mon_if_stop, + .ndo_start_xmit = dhd_mon_if_subif_start_xmit, + .ndo_set_multicast_list = dhd_mon_if_set_multicast_list, + .ndo_set_mac_address = dhd_mon_if_change_mac, +}; + +/** + * Local static function defintions + */ + +/* Look up dhd's net device table to find a match (e.g. interface "eth0" is a match for "mon.eth0" + * "p2p-eth0-0" is a match for "mon.p2p-eth0-0") + */ +static struct net_device* lookup_real_netdev(char *name) +{ + int i; + int last_name_len = 0; + struct net_device *ndev; + struct net_device *ndev_found = NULL; + + /* We want to find interface "p2p-eth0-0" for monitor interface "mon.p2p-eth0-0", so + * we skip "eth0" even if "mon.p2p-eth0-0" contains "eth0" + */ + for (i = 0; i < DHD_MAX_IFS; i++) { + ndev = dhd_idx2net(g_monitor.dhd_pub, i); + if (ndev && strstr(name, ndev->name)) { + if (strlen(ndev->name) > last_name_len) { + ndev_found = ndev; + last_name_len = strlen(ndev->name); + } + } + } + + return ndev_found; +} + +static monitor_interface* ndev_to_monif(struct net_device *ndev) +{ + int i; + + for (i = 0; i < DHD_MAX_IFS; i++) { + if (g_monitor.mon_if[i].mon_ndev == ndev) + return &g_monitor.mon_if[i]; + } + + return NULL; +} + +static int dhd_mon_if_open(struct net_device *ndev) +{ + int ret = 0; + + MON_PRINT("enter\n"); + return ret; +} + +static int dhd_mon_if_stop(struct net_device *ndev) +{ + int ret = 0; + + MON_PRINT("enter\n"); + return ret; +} + +static int dhd_mon_if_subif_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + int ret = 0; + int rtap_len; + int qos_len = 0; + int dot11_hdr_len = 24; + int snap_len = 6; + unsigned char *pdata; + unsigned short frame_ctl; + unsigned char src_mac_addr[6]; + unsigned char dst_mac_addr[6]; + struct ieee80211_hdr *dot11_hdr; + struct ieee80211_radiotap_header *rtap_hdr; + monitor_interface* mon_if; + + MON_PRINT("enter\n"); + + mon_if = ndev_to_monif(ndev); + if (mon_if == NULL || mon_if->real_ndev == NULL) { + MON_PRINT(" cannot find matched net dev, skip the packet\n"); + goto fail; + } + + if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) + goto fail; + + rtap_hdr = (struct ieee80211_radiotap_header *)skb->data; + if (unlikely(rtap_hdr->it_version)) + goto fail; + + rtap_len = ieee80211_get_radiotap_len(skb->data); + if (unlikely(skb->len < rtap_len)) + goto fail; + + MON_PRINT("radiotap len (should be 14): %d\n", rtap_len); + + /* Skip the ratio tap header */ + skb_pull(skb, rtap_len); + + dot11_hdr = (struct ieee80211_hdr *)skb->data; + frame_ctl = le16_to_cpu(dot11_hdr->frame_control); + /* Check if the QoS bit is set */ + if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { + /* Check if this ia a Wireless Distribution System (WDS) frame + * which has 4 MAC addresses + */ + if (dot11_hdr->frame_control & 0x0080) + qos_len = 2; + if ((dot11_hdr->frame_control & 0x0300) == 0x0300) + dot11_hdr_len += 6; + + memcpy(dst_mac_addr, dot11_hdr->addr1, sizeof(dst_mac_addr)); + memcpy(src_mac_addr, dot11_hdr->addr2, sizeof(src_mac_addr)); + + /* Skip the 802.11 header, QoS (if any) and SNAP, but leave spaces for + * for two MAC addresses + */ + skb_pull(skb, dot11_hdr_len + qos_len + snap_len - sizeof(src_mac_addr) * 2); + pdata = (unsigned char*)skb->data; + memcpy(pdata, dst_mac_addr, sizeof(dst_mac_addr)); + memcpy(pdata + sizeof(dst_mac_addr), src_mac_addr, sizeof(src_mac_addr)); + + MON_PRINT("if name: %s, matched if name %s\n", ndev->name, mon_if->real_ndev->name); + + /* Use the real net device to transmit the packet */ + ret = dhd_start_xmit(skb, mon_if->real_ndev); + + return ret; + } +fail: + dev_kfree_skb(skb); + return 0; +} + +static void dhd_mon_if_set_multicast_list(struct net_device *ndev) +{ + monitor_interface* mon_if; + + mon_if = ndev_to_monif(ndev); + if (mon_if == NULL || mon_if->real_ndev == NULL) { + MON_PRINT(" cannot find matched net dev, skip the packet\n"); + } + + MON_PRINT("enter, if name: %s, matched if name %s\n", ndev->name, mon_if->real_ndev->name); +} + +static int dhd_mon_if_change_mac(struct net_device *ndev, void *addr) +{ + int ret = 0; + monitor_interface* mon_if; + + mon_if = ndev_to_monif(ndev); + if (mon_if == NULL || mon_if->real_ndev == NULL) { + MON_PRINT(" cannot find matched net dev, skip the packet\n"); + } + + MON_PRINT("enter, if name: %s, matched if name %s\n", ndev->name, mon_if->real_ndev->name); + return ret; +} + +/** + * Global function definitions (declared in dhd_linux_mon.h) + */ + +int dhd_add_monitor(char *name, struct net_device **new_ndev) +{ + int i; + int idx = -1; + int ret = 0; + struct net_device* ndev = NULL; + dhd_linux_monitor_t **dhd_mon; + + mutex_lock(&g_monitor.lock); + + MON_TRACE("enter, if name: %s\n", name); + if (!name || !new_ndev) { + MON_PRINT("invalid parameters\n"); + ret = -EINVAL; + goto out; + } + + /* + * Find a vacancy + */ + for (i = 0; i < DHD_MAX_IFS; i++) + if (g_monitor.mon_if[i].mon_ndev == NULL) { + idx = i; + break; + } + if (idx == -1) { + MON_PRINT("exceeds maximum interfaces\n"); + ret = -EFAULT; + goto out; + } + + ndev = alloc_etherdev(sizeof(dhd_linux_monitor_t*)); + if (!ndev) { + MON_PRINT("failed to allocate memory\n"); + ret = -ENOMEM; + goto out; + } + + ndev->type = ARPHRD_IEEE80211_RADIOTAP; + strncpy(ndev->name, name, IFNAMSIZ); + ndev->name[IFNAMSIZ - 1] = 0; + ndev->netdev_ops = &dhd_mon_if_ops; + + ret = register_netdevice(ndev); + if (ret) { + MON_PRINT(" register_netdevice failed (%d)\n", ret); + goto out; + } + + *new_ndev = ndev; + g_monitor.mon_if[idx].radiotap_enabled = TRUE; + g_monitor.mon_if[idx].mon_ndev = ndev; + g_monitor.mon_if[idx].real_ndev = lookup_real_netdev(name); + dhd_mon = (dhd_linux_monitor_t **)netdev_priv(ndev); + *dhd_mon = &g_monitor; + g_monitor.monitor_state = MONITOR_STATE_INTERFACE_ADDED; + MON_PRINT("net device returned: 0x%p\n", ndev); + MON_PRINT("found a matched net device, name %s\n", g_monitor.mon_if[idx].real_ndev->name); + +out: + if (ret && ndev) + free_netdev(ndev); + + mutex_unlock(&g_monitor.lock); + return ret; + +} + +int dhd_del_monitor(struct net_device *ndev) +{ + int i; + bool rollback_lock = false; + if (!ndev) + return -EINVAL; + mutex_lock(&g_monitor.lock); + for (i = 0; i < DHD_MAX_IFS; i++) { + if (g_monitor.mon_if[i].mon_ndev == ndev || + g_monitor.mon_if[i].real_ndev == ndev) { + g_monitor.mon_if[i].real_ndev = NULL; + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = true; + } + unregister_netdev(g_monitor.mon_if[i].mon_ndev); + free_netdev(g_monitor.mon_if[i].mon_ndev); + g_monitor.mon_if[i].mon_ndev = NULL; + g_monitor.monitor_state = MONITOR_STATE_INTERFACE_DELETED; + break; + } + } + if (rollback_lock) { + rtnl_lock(); + rollback_lock = false; + } + + if (g_monitor.monitor_state != + MONITOR_STATE_INTERFACE_DELETED) + MON_PRINT("interface not found in monitor IF array, is this a monitor IF? 0x%p\n", + ndev); + mutex_unlock(&g_monitor.lock); + + return 0; +} + +int dhd_monitor_init(void *dhd_pub) +{ + if (g_monitor.monitor_state == MONITOR_STATE_DEINIT) { + g_monitor.dhd_pub = dhd_pub; + mutex_init(&g_monitor.lock); + g_monitor.monitor_state = MONITOR_STATE_INIT; + } + return 0; +} + +int dhd_monitor_uninit(void) +{ + int i; + struct net_device *ndev; + bool rollback_lock = false; + mutex_lock(&g_monitor.lock); + if (g_monitor.monitor_state != MONITOR_STATE_DEINIT) { + for (i = 0; i < DHD_MAX_IFS; i++) { + ndev = g_monitor.mon_if[i].mon_ndev; + if (ndev) { + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = true; + } + unregister_netdev(ndev); + free_netdev(ndev); + g_monitor.mon_if[i].real_ndev = NULL; + g_monitor.mon_if[i].mon_ndev = NULL; + if (rollback_lock) { + rtnl_lock(); + rollback_lock = false; + } + } + } + g_monitor.monitor_state = MONITOR_STATE_DEINIT; + } + mutex_unlock(&g_monitor.lock); + return 0; +} diff --git a/drivers/net/wireless/bcmdhd/dhd_linux_sched.c b/drivers/net/wireless/bcmdhd/dhd_linux_sched.c new file mode 100644 index 0000000000000..aadd122f5b07f --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_linux_sched.c @@ -0,0 +1,39 @@ +/* + * Expose some of the kernel scheduler routines + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_linux_sched.c,v 1.3 2009-04-10 04:14:49 Exp $ + */ +#include +#include +#include +#include +#include + +int setScheduler(struct task_struct *p, int policy, struct sched_param *param) +{ + int rc = 0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) + rc = sched_setscheduler(p, policy, param); +#endif /* LinuxVer */ + return rc; +} diff --git a/drivers/net/wireless/bcmdhd/dhd_proto.h b/drivers/net/wireless/bcmdhd/dhd_proto.h new file mode 100644 index 0000000000000..bb1d7365ea941 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_proto.h @@ -0,0 +1,105 @@ +/* + * Header file describing the internal (inter-module) DHD interfaces. + * + * Provides type definitions and function prototypes used to link the + * DHD OS, bus, and protocol modules. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_proto.h,v 1.8.10.6 2010-12-22 23:47:24 Exp $ + */ + +#ifndef _dhd_proto_h_ +#define _dhd_proto_h_ + +#include +#include + +#ifndef IOCTL_RESP_TIMEOUT +#define IOCTL_RESP_TIMEOUT 2000 /* In milli second */ +#endif + +/* + * Exported from the dhd protocol module (dhd_cdc, dhd_rndis) + */ + +/* Linkage, sets prot link and updates hdrlen in pub */ +extern int dhd_prot_attach(dhd_pub_t *dhdp); + +/* Unlink, frees allocated protocol memory (including dhd_prot) */ +extern void dhd_prot_detach(dhd_pub_t *dhdp); + +/* Initialize protocol: sync w/dongle state. + * Sets dongle media info (iswl, drv_version, mac address). + */ +extern int dhd_prot_init(dhd_pub_t *dhdp); + +/* Stop protocol: sync w/dongle state. */ +extern void dhd_prot_stop(dhd_pub_t *dhdp); + +/* Add any protocol-specific data header. + * Caller must reserve prot_hdrlen prepend space. + */ +extern void dhd_prot_hdrpush(dhd_pub_t *, int ifidx, void *txp); + +/* Remove any protocol-specific data header. */ +extern int dhd_prot_hdrpull(dhd_pub_t *, int *ifidx, void *rxp); + +/* Use protocol to issue ioctl to dongle */ +extern int dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len); + +/* Handles a protocol control response asynchronously */ +extern int dhd_prot_ctl_complete(dhd_pub_t *dhd); + +/* Check for and handle local prot-specific iovar commands */ +extern int dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name, + void *params, int plen, void *arg, int len, bool set); + +/* Add prot dump output to a buffer */ +extern void dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf); + +/* Update local copy of dongle statistics */ +extern void dhd_prot_dstats(dhd_pub_t *dhdp); + +extern int dhd_ioctl(dhd_pub_t * dhd_pub, dhd_ioctl_t *ioc, void * buf, uint buflen); + +extern int dhd_preinit_ioctls(dhd_pub_t *dhd); + +#ifdef PROP_TXSTATUS +extern int dhd_wlfc_enque_sendq(void* state, int prec, void* p); +extern int dhd_wlfc_commit_packets(void* state, f_commitpkt_t fcommit, void* commit_ctx); +extern void dhd_wlfc_cleanup(dhd_pub_t *dhd); +#endif /* PROP_TXSTATUS */ + +/******************************** + * For version-string expansion * + */ +#if defined(BDC) +#define DHD_PROTOCOL "bdc" +#elif defined(CDC) +#define DHD_PROTOCOL "cdc" +#elif defined(RNDIS) +#define DHD_PROTOCOL "rndis" +#else +#define DHD_PROTOCOL "unknown" +#endif /* proto */ + +#endif /* _dhd_proto_h_ */ diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c new file mode 100644 index 0000000000000..4834ca2f9ed15 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_sdio.c @@ -0,0 +1,6333 @@ +/* + * DHD Bus Module for SDIO + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhd_sdio.c 326662 2012-04-10 06:38:08Z $ + */ + +#include +#include +#include + +#ifdef BCMEMBEDIMAGE +#include BCMEMBEDIMAGE +#endif /* BCMEMBEDIMAGE */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#if defined(DHD_DEBUG) +#include +#include +#endif /* defined(DHD_DEBUG) */ +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef DHDSDIO_MEM_DUMP_FNAME +#define DHDSDIO_MEM_DUMP_FNAME "mem_dump" +#endif + +#define QLEN 256 /* bulk rx and tx queue lengths */ +#define FCHI (QLEN - 10) +#define FCLOW (FCHI / 2) +#define PRIOMASK 7 + +#define TXRETRIES 2 /* # of retries for tx frames */ + +#define DHD_RXBOUND 50 /* Default for max rx frames in one scheduling */ + +#define DHD_TXBOUND 20 /* Default for max tx frames in one scheduling */ + +#define DHD_TXMINMAX 1 /* Max tx frames if rx still pending */ + +#define MEMBLOCK 2048 /* Block size used for downloading of dongle image */ +#define MAX_NVRAMBUF_SIZE 4096 /* max nvram buf size */ +#define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold biggest possible glom */ + +#ifndef DHD_FIRSTREAD +#define DHD_FIRSTREAD 32 +#endif +#if !ISPOWEROF2(DHD_FIRSTREAD) +#error DHD_FIRSTREAD is not a power of 2! +#endif + +/* Total length of frame header for dongle protocol */ +#define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN) +#ifdef SDTEST +#define SDPCM_RESERVE (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN) +#else +#define SDPCM_RESERVE (SDPCM_HDRLEN + DHD_SDALIGN) +#endif + +/* Space for header read, limit for data packets */ +#ifndef MAX_HDR_READ +#define MAX_HDR_READ 32 +#endif +#if !ISPOWEROF2(MAX_HDR_READ) +#error MAX_HDR_READ is not a power of 2! +#endif + +#define MAX_RX_DATASZ 2048 + +/* Maximum milliseconds to wait for F2 to come up */ +#define DHD_WAIT_F2RDY 3000 + +/* Bump up limit on waiting for HT to account for first startup; + * if the image is doing a CRC calculation before programming the PMU + * for HT availability, it could take a couple hundred ms more, so + * max out at a 1 second (1000000us). + */ +#if (PMU_MAX_TRANSITION_DLY <= 1000000) +#undef PMU_MAX_TRANSITION_DLY +#define PMU_MAX_TRANSITION_DLY 1000000 +#endif + +/* Value for ChipClockCSR during initial setup */ +#define DHD_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ) +#define DHD_INIT_CLKCTL2 (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP) + +/* Flags for SDH calls */ +#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED) + +/* Packet free applicable unconditionally for sdio and sdspi. Conditional if + * bufpool was present for gspi bus. + */ +#define PKTFREE2() if ((bus->bus != SPI_BUS) || bus->usebufpool) \ + PKTFREE(bus->dhd->osh, pkt, FALSE); +DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep); +#if defined(OOB_INTR_ONLY) +extern void bcmsdh_set_irq(int flag); +#endif /* defined(OOB_INTR_ONLY) */ +#ifdef PROP_TXSTATUS +extern void dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success); +#endif + +#ifdef DHD_DEBUG +/* Device console log buffer state */ +#define CONSOLE_LINE_MAX 192 +#define CONSOLE_BUFFER_MAX 2024 +typedef struct dhd_console { + uint count; /* Poll interval msec counter */ + uint log_addr; /* Log struct address (fixed) */ + hndrte_log_t log; /* Log struct (host copy) */ + uint bufsize; /* Size of log buffer */ + uint8 *buf; /* Log buffer (host copy) */ + uint last; /* Last buffer read index */ +} dhd_console_t; +#endif /* DHD_DEBUG */ + +/* Private data for SDIO bus interaction */ +typedef struct dhd_bus { + dhd_pub_t *dhd; + + bcmsdh_info_t *sdh; /* Handle for BCMSDH calls */ + si_t *sih; /* Handle for SI calls */ + char *vars; /* Variables (from CIS and/or other) */ + uint varsz; /* Size of variables buffer */ + uint32 sbaddr; /* Current SB window pointer (-1, invalid) */ + + sdpcmd_regs_t *regs; /* Registers for SDIO core */ + uint sdpcmrev; /* SDIO core revision */ + uint armrev; /* CPU core revision */ + uint ramrev; /* SOCRAM core revision */ + uint32 ramsize; /* Size of RAM in SOCRAM (bytes) */ + uint32 orig_ramsize; /* Size of RAM in SOCRAM (bytes) */ + + uint32 bus; /* gSPI or SDIO bus */ + uint32 hostintmask; /* Copy of Host Interrupt Mask */ + uint32 intstatus; /* Intstatus bits (events) pending */ + bool dpc_sched; /* Indicates DPC schedule (intrpt rcvd) */ + bool fcstate; /* State of dongle flow-control */ + + uint16 cl_devid; /* cached devid for dhdsdio_probe_attach() */ + char *fw_path; /* module_param: path to firmware image */ + char *nv_path; /* module_param: path to nvram vars file */ + const char *nvram_params; /* user specified nvram params. */ + + uint blocksize; /* Block size of SDIO transfers */ + uint roundup; /* Max roundup limit */ + + struct pktq txq; /* Queue length used for flow-control */ + uint8 flowcontrol; /* per prio flow control bitmask */ + uint8 tx_seq; /* Transmit sequence number (next) */ + uint8 tx_max; /* Maximum transmit sequence allowed */ + + uint8 hdrbuf[MAX_HDR_READ + DHD_SDALIGN]; + uint8 *rxhdr; /* Header of current rx frame (in hdrbuf) */ + uint16 nextlen; /* Next Read Len from last header */ + uint8 rx_seq; /* Receive sequence number (expected) */ + bool rxskip; /* Skip receive (awaiting NAK ACK) */ + + void *glomd; /* Packet containing glomming descriptor */ + void *glom; /* Packet chain for glommed superframe */ + uint glomerr; /* Glom packet read errors */ + + uint8 *rxbuf; /* Buffer for receiving control packets */ + uint rxblen; /* Allocated length of rxbuf */ + uint8 *rxctl; /* Aligned pointer into rxbuf */ + uint8 *databuf; /* Buffer for receiving big glom packet */ + uint8 *dataptr; /* Aligned pointer into databuf */ + uint rxlen; /* Length of valid data in buffer */ + + uint8 sdpcm_ver; /* Bus protocol reported by dongle */ + + bool intr; /* Use interrupts */ + bool poll; /* Use polling */ + bool ipend; /* Device interrupt is pending */ + bool intdis; /* Interrupts disabled by isr */ + uint intrcount; /* Count of device interrupt callbacks */ + uint lastintrs; /* Count as of last watchdog timer */ + uint spurious; /* Count of spurious interrupts */ + uint pollrate; /* Ticks between device polls */ + uint polltick; /* Tick counter */ + uint pollcnt; /* Count of active polls */ + +#ifdef DHD_DEBUG + dhd_console_t console; /* Console output polling support */ + uint console_addr; /* Console address from shared struct */ +#endif /* DHD_DEBUG */ + + uint regfails; /* Count of R_REG/W_REG failures */ + + uint clkstate; /* State of sd and backplane clock(s) */ + bool activity; /* Activity flag for clock down */ + int32 idletime; /* Control for activity timeout */ + int32 idlecount; /* Activity timeout counter */ + int32 idleclock; /* How to set bus driver when idle */ + int32 sd_divisor; /* Speed control to bus driver */ + int32 sd_mode; /* Mode control to bus driver */ + int32 sd_rxchain; /* If bcmsdh api accepts PKT chains */ + bool use_rxchain; /* If dhd should use PKT chains */ + bool sleeping; /* Is SDIO bus sleeping? */ + bool rxflow_mode; /* Rx flow control mode */ + bool rxflow; /* Is rx flow control on */ + uint prev_rxlim_hit; /* Is prev rx limit exceeded (per dpc schedule) */ + bool alp_only; /* Don't use HT clock (ALP only) */ + /* Field to decide if rx of control frames happen in rxbuf or lb-pool */ + bool usebufpool; + +#ifdef SDTEST + /* external loopback */ + bool ext_loop; + uint8 loopid; + + /* pktgen configuration */ + uint pktgen_freq; /* Ticks between bursts */ + uint pktgen_count; /* Packets to send each burst */ + uint pktgen_print; /* Bursts between count displays */ + uint pktgen_total; /* Stop after this many */ + uint pktgen_minlen; /* Minimum packet data len */ + uint pktgen_maxlen; /* Maximum packet data len */ + uint pktgen_mode; /* Configured mode: tx, rx, or echo */ + uint pktgen_stop; /* Number of tx failures causing stop */ + + /* active pktgen fields */ + uint pktgen_tick; /* Tick counter for bursts */ + uint pktgen_ptick; /* Burst counter for printing */ + uint pktgen_sent; /* Number of test packets generated */ + uint pktgen_rcvd; /* Number of test packets received */ + uint pktgen_fail; /* Number of failed send attempts */ + uint16 pktgen_len; /* Length of next packet to send */ +#define PKTGEN_RCV_IDLE (0) +#define PKTGEN_RCV_ONGOING (1) + uint16 pktgen_rcv_state; /* receive state */ + uint pktgen_rcvd_rcvsession; /* test pkts rcvd per rcv session. */ +#endif /* SDTEST */ + + /* Some additional counters */ + uint tx_sderrs; /* Count of tx attempts with sd errors */ + uint fcqueued; /* Tx packets that got queued */ + uint rxrtx; /* Count of rtx requests (NAK to dongle) */ + uint rx_toolong; /* Receive frames too long to receive */ + uint rxc_errors; /* SDIO errors when reading control frames */ + uint rx_hdrfail; /* SDIO errors on header reads */ + uint rx_badhdr; /* Bad received headers (roosync?) */ + uint rx_badseq; /* Mismatched rx sequence number */ + uint fc_rcvd; /* Number of flow-control events received */ + uint fc_xoff; /* Number which turned on flow-control */ + uint fc_xon; /* Number which turned off flow-control */ + uint rxglomfail; /* Failed deglom attempts */ + uint rxglomframes; /* Number of glom frames (superframes) */ + uint rxglompkts; /* Number of packets from glom frames */ + uint f2rxhdrs; /* Number of header reads */ + uint f2rxdata; /* Number of frame data reads */ + uint f2txdata; /* Number of f2 frame writes */ + uint f1regdata; /* Number of f1 register accesses */ + + uint8 *ctrl_frame_buf; + uint32 ctrl_frame_len; + bool ctrl_frame_stat; + uint32 rxint_mode; /* rx interrupt mode */ +} dhd_bus_t; + +/* clkstate */ +#define CLK_NONE 0 +#define CLK_SDONLY 1 +#define CLK_PENDING 2 /* Not used yet */ +#define CLK_AVAIL 3 + +#define DHD_NOPMU(dhd) (FALSE) + +#ifdef DHD_DEBUG +static int qcount[NUMPRIO]; +static int tx_packets[NUMPRIO]; +#endif /* DHD_DEBUG */ + +/* Deferred transmit */ +const uint dhd_deferred_tx = 1; + +extern uint dhd_watchdog_ms; +extern void dhd_os_wd_timer(void *bus, uint wdtick); + +/* Tx/Rx bounds */ +uint dhd_txbound; +uint dhd_rxbound; +uint dhd_txminmax = DHD_TXMINMAX; + +/* override the RAM size if possible */ +#define DONGLE_MIN_MEMSIZE (128 *1024) +int dhd_dongle_memsize; + +static bool dhd_doflow; +static bool dhd_alignctl; + +static bool sd1idle; + +static bool retrydata; +#define RETRYCHAN(chan) (((chan) == SDPCM_EVENT_CHANNEL) || retrydata) + +static const uint watermark = 8; +static const uint firstread = DHD_FIRSTREAD; + +#define HDATLEN (firstread - (SDPCM_HDRLEN)) + +/* Retry count for register access failures */ +static const uint retry_limit = 2; + +/* Force even SD lengths (some host controllers mess up on odd bytes) */ +static bool forcealign; + +/* Flag to indicate if we should download firmware on driver load */ +uint dhd_download_fw_on_driverload = TRUE; + +#define ALIGNMENT 4 + +#if defined(OOB_INTR_ONLY) && defined(HW_OOB) +extern void bcmsdh_enable_hw_oob_intr(void *sdh, bool enable); +#endif + +#if defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD) +#error OOB_INTR_ONLY is NOT working with SDIO_ISR_THREAD +#endif /* defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD) */ +#define PKTALIGN(osh, p, len, align) \ + do { \ + uint datalign; \ + datalign = (uintptr)PKTDATA((osh), (p)); \ + datalign = ROUNDUP(datalign, (align)) - datalign; \ + ASSERT(datalign < (align)); \ + ASSERT(PKTLEN((osh), (p)) >= ((len) + datalign)); \ + if (datalign) \ + PKTPULL((osh), (p), datalign); \ + PKTSETLEN((osh), (p), (len)); \ + } while (0) + +/* Limit on rounding up frames */ +static const uint max_roundup = 512; + +/* Try doing readahead */ +static bool dhd_readahead; + +/* To check if there's window offered */ +#define DATAOK(bus) \ + (((uint8)(bus->tx_max - bus->tx_seq) > 1) && \ + (((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0)) + +/* To check if there's window offered for ctrl frame */ +#define TXCTLOK(bus) \ + (((uint8)(bus->tx_max - bus->tx_seq) != 0) && \ + (((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0)) + +/* Macros to get register read/write status */ +/* NOTE: these assume a local dhdsdio_bus_t *bus! */ +#define R_SDREG(regvar, regaddr, retryvar) \ +do { \ + retryvar = 0; \ + do { \ + regvar = R_REG(bus->dhd->osh, regaddr); \ + } while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \ + if (retryvar) { \ + bus->regfails += (retryvar-1); \ + if (retryvar > retry_limit) { \ + DHD_ERROR(("%s: FAILED" #regvar "READ, LINE %d\n", \ + __FUNCTION__, __LINE__)); \ + regvar = 0; \ + } \ + } \ +} while (0) + +#define W_SDREG(regval, regaddr, retryvar) \ +do { \ + retryvar = 0; \ + do { \ + W_REG(bus->dhd->osh, regaddr, regval); \ + } while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \ + if (retryvar) { \ + bus->regfails += (retryvar-1); \ + if (retryvar > retry_limit) \ + DHD_ERROR(("%s: FAILED REGISTER WRITE, LINE %d\n", \ + __FUNCTION__, __LINE__)); \ + } \ +} while (0) + +#define BUS_WAKE(bus) \ + do { \ + if ((bus)->sleeping) \ + dhdsdio_bussleep((bus), FALSE); \ + } while (0); + +/* + * pktavail interrupts from dongle to host can be managed in 3 different ways + * whenever there is a packet available in dongle to transmit to host. + * + * Mode 0: Dongle writes the software host mailbox and host is interrupted. + * Mode 1: (sdiod core rev >= 4) + * Device sets a new bit in the intstatus whenever there is a packet + * available in fifo. Host can't clear this specific status bit until all the + * packets are read from the FIFO. No need to ack dongle intstatus. + * Mode 2: (sdiod core rev >= 4) + * Device sets a bit in the intstatus, and host acks this by writing + * one to this bit. Dongle won't generate anymore packet interrupts + * until host reads all the packets from the dongle and reads a zero to + * figure that there are no more packets. No need to disable host ints. + * Need to ack the intstatus. + */ + +#define SDIO_DEVICE_HMB_RXINT 0 /* default old way */ +#define SDIO_DEVICE_RXDATAINT_MODE_0 1 /* from sdiod rev 4 */ +#define SDIO_DEVICE_RXDATAINT_MODE_1 2 /* from sdiod rev 4 */ + + +#define FRAME_AVAIL_MASK(bus) \ + ((bus->rxint_mode == SDIO_DEVICE_HMB_RXINT) ? I_HMB_FRAME_IND : I_XMTDATA_AVAIL) + +#define DHD_BUS SDIO_BUS + +#define PKT_AVAILABLE(bus, intstatus) ((intstatus) & (FRAME_AVAIL_MASK(bus))) + +#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE) + +#define GSPI_PR55150_BAILOUT + + +#ifdef SDTEST +static void dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq); +static void dhdsdio_sdtest_set(dhd_bus_t *bus, uint8 count); +#endif + +#ifdef DHD_DEBUG +static int dhdsdio_checkdied(dhd_bus_t *bus, char *data, uint size); +static int dhd_serialconsole(dhd_bus_t *bus, bool get, bool enable, int *bcmerror); +#endif /* DHD_DEBUG */ + +static int dhdsdio_download_state(dhd_bus_t *bus, bool enter); + +static void dhdsdio_release(dhd_bus_t *bus, osl_t *osh); +static void dhdsdio_release_malloc(dhd_bus_t *bus, osl_t *osh); +static void dhdsdio_disconnect(void *ptr); +static bool dhdsdio_chipmatch(uint16 chipid); +static bool dhdsdio_probe_attach(dhd_bus_t *bus, osl_t *osh, void *sdh, + void * regsva, uint16 devid); +static bool dhdsdio_probe_malloc(dhd_bus_t *bus, osl_t *osh, void *sdh); +static bool dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh); +static void dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, bool dongle_isolation, + bool reset_flag); + +static void dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size); +static int dhd_bcmsdh_recv_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, + uint8 *buf, uint nbytes, + void *pkt, bcmsdh_cmplt_fn_t complete, void *handle); +static int dhd_bcmsdh_send_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, + uint8 *buf, uint nbytes, + void *pkt, bcmsdh_cmplt_fn_t complete, void *handle); + +static bool dhdsdio_download_firmware(dhd_bus_t *bus, osl_t *osh, void *sdh); +static int _dhdsdio_download_firmware(dhd_bus_t *bus); + +static int dhdsdio_download_code_file(dhd_bus_t *bus, char *image_path); +static int dhdsdio_download_nvram(dhd_bus_t *bus); +#ifdef BCMEMBEDIMAGE +static int dhdsdio_download_code_array(dhd_bus_t *bus); +#endif + +#ifdef WLMEDIA_HTSF +#include +extern uint32 dhd_get_htsf(void *dhd, int ifidx); +#endif /* WLMEDIA_HTSF */ + +static void +dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size) +{ + int32 min_size = DONGLE_MIN_MEMSIZE; + /* Restrict the memsize to user specified limit */ + DHD_ERROR(("user: Restrict the dongle ram size to %d, min accepted %d\n", + dhd_dongle_memsize, min_size)); + if ((dhd_dongle_memsize > min_size) && + (dhd_dongle_memsize < (int32)bus->orig_ramsize)) + bus->ramsize = dhd_dongle_memsize; +} + +static int +dhdsdio_set_siaddr_window(dhd_bus_t *bus, uint32 address) +{ + int err = 0; + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW, + (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err); + if (!err) + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID, + (address >> 16) & SBSDIO_SBADDRMID_MASK, &err); + if (!err) + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH, + (address >> 24) & SBSDIO_SBADDRHIGH_MASK, &err); + return err; +} + + +/* Turn backplane clock on or off */ +static int +dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok) +{ + int err; + uint8 clkctl, clkreq, devctl; + bcmsdh_info_t *sdh; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + +#if defined(OOB_INTR_ONLY) + pendok = FALSE; +#endif + clkctl = 0; + sdh = bus->sdh; + + + if (on) { + /* Request HT Avail */ + clkreq = bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ; + + + + + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err); + if (err) { + DHD_ERROR(("%s: HT Avail request error: %d\n", __FUNCTION__, err)); + return BCME_ERROR; + } + + if (pendok && + ((bus->sih->buscoretype == PCMCIA_CORE_ID) && (bus->sih->buscorerev == 9))) { + uint32 dummy, retries; + R_SDREG(dummy, &bus->regs->clockctlstatus, retries); + } + + /* Check current status */ + clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); + if (err) { + DHD_ERROR(("%s: HT Avail read error: %d\n", __FUNCTION__, err)); + return BCME_ERROR; + } + + /* Go to pending and await interrupt if appropriate */ + if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) { + /* Allow only clock-available interrupt */ + devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err); + if (err) { + DHD_ERROR(("%s: Devctl access error setting CA: %d\n", + __FUNCTION__, err)); + return BCME_ERROR; + } + + devctl |= SBSDIO_DEVCTL_CA_INT_ONLY; + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err); + DHD_INFO(("CLKCTL: set PENDING\n")); + bus->clkstate = CLK_PENDING; + return BCME_OK; + } else if (bus->clkstate == CLK_PENDING) { + /* Cancel CA-only interrupt filter */ + devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err); + devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err); + } + + /* Otherwise, wait here (polling) for HT Avail */ + if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { + SPINWAIT_SLEEP(sdioh_spinwait_sleep, + ((clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_CHIPCLKCSR, &err)), + !SBSDIO_CLKAV(clkctl, bus->alp_only)), PMU_MAX_TRANSITION_DLY); + } + if (err) { + DHD_ERROR(("%s: HT Avail request error: %d\n", __FUNCTION__, err)); + return BCME_ERROR; + } + if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { + DHD_ERROR(("%s: HT Avail timeout (%d): clkctl 0x%02x\n", + __FUNCTION__, PMU_MAX_TRANSITION_DLY, clkctl)); + return BCME_ERROR; + } + + + /* Mark clock available */ + bus->clkstate = CLK_AVAIL; + DHD_INFO(("CLKCTL: turned ON\n")); + +#if defined(DHD_DEBUG) + if (bus->alp_only == TRUE) { +#if !defined(BCMLXSDMMC) + if (!SBSDIO_ALPONLY(clkctl)) { + DHD_ERROR(("%s: HT Clock, when ALP Only\n", __FUNCTION__)); + } +#endif /* !defined(BCMLXSDMMC) */ + } else { + if (SBSDIO_ALPONLY(clkctl)) { + DHD_ERROR(("%s: HT Clock should be on.\n", __FUNCTION__)); + } + } +#endif /* defined (DHD_DEBUG) */ + + bus->activity = TRUE; + } else { + clkreq = 0; + + if (bus->clkstate == CLK_PENDING) { + /* Cancel CA-only interrupt filter */ + devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err); + devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err); + } + + bus->clkstate = CLK_SDONLY; + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err); + DHD_INFO(("CLKCTL: turned OFF\n")); + if (err) { + DHD_ERROR(("%s: Failed access turning clock off: %d\n", + __FUNCTION__, err)); + return BCME_ERROR; + } + } + return BCME_OK; +} + +/* Change idle/active SD state */ +static int +dhdsdio_sdclk(dhd_bus_t *bus, bool on) +{ + int err; + int32 iovalue; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (on) { + if (bus->idleclock == DHD_IDLE_STOP) { + /* Turn on clock and restore mode */ + iovalue = 1; + err = bcmsdh_iovar_op(bus->sdh, "sd_clock", NULL, 0, + &iovalue, sizeof(iovalue), TRUE); + if (err) { + DHD_ERROR(("%s: error enabling sd_clock: %d\n", + __FUNCTION__, err)); + return BCME_ERROR; + } + + iovalue = bus->sd_mode; + err = bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0, + &iovalue, sizeof(iovalue), TRUE); + if (err) { + DHD_ERROR(("%s: error changing sd_mode: %d\n", + __FUNCTION__, err)); + return BCME_ERROR; + } + } else if (bus->idleclock != DHD_IDLE_ACTIVE) { + /* Restore clock speed */ + iovalue = bus->sd_divisor; + err = bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0, + &iovalue, sizeof(iovalue), TRUE); + if (err) { + DHD_ERROR(("%s: error restoring sd_divisor: %d\n", + __FUNCTION__, err)); + return BCME_ERROR; + } + } + bus->clkstate = CLK_SDONLY; + } else { + /* Stop or slow the SD clock itself */ + if ((bus->sd_divisor == -1) || (bus->sd_mode == -1)) { + DHD_TRACE(("%s: can't idle clock, divisor %d mode %d\n", + __FUNCTION__, bus->sd_divisor, bus->sd_mode)); + return BCME_ERROR; + } + if (bus->idleclock == DHD_IDLE_STOP) { + if (sd1idle) { + /* Change to SD1 mode and turn off clock */ + iovalue = 1; + err = bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0, + &iovalue, sizeof(iovalue), TRUE); + if (err) { + DHD_ERROR(("%s: error changing sd_clock: %d\n", + __FUNCTION__, err)); + return BCME_ERROR; + } + } + + iovalue = 0; + err = bcmsdh_iovar_op(bus->sdh, "sd_clock", NULL, 0, + &iovalue, sizeof(iovalue), TRUE); + if (err) { + DHD_ERROR(("%s: error disabling sd_clock: %d\n", + __FUNCTION__, err)); + return BCME_ERROR; + } + } else if (bus->idleclock != DHD_IDLE_ACTIVE) { + /* Set divisor to idle value */ + iovalue = bus->idleclock; + err = bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0, + &iovalue, sizeof(iovalue), TRUE); + if (err) { + DHD_ERROR(("%s: error changing sd_divisor: %d\n", + __FUNCTION__, err)); + return BCME_ERROR; + } + } + bus->clkstate = CLK_NONE; + } + + return BCME_OK; +} + +/* Transition SD and backplane clock readiness */ +static int +dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok) +{ + int ret = BCME_OK; +#ifdef DHD_DEBUG + uint oldstate = bus->clkstate; +#endif /* DHD_DEBUG */ + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + /* Early exit if we're already there */ + if (bus->clkstate == target) { + if (target == CLK_AVAIL) { + dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms); + bus->activity = TRUE; + } + return ret; + } + + switch (target) { + case CLK_AVAIL: + /* Make sure SD clock is available */ + if (bus->clkstate == CLK_NONE) + dhdsdio_sdclk(bus, TRUE); + /* Now request HT Avail on the backplane */ + ret = dhdsdio_htclk(bus, TRUE, pendok); + if (ret == BCME_OK) { + dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms); + bus->activity = TRUE; + } + break; + + case CLK_SDONLY: + /* Remove HT request, or bring up SD clock */ + if (bus->clkstate == CLK_NONE) + ret = dhdsdio_sdclk(bus, TRUE); + else if (bus->clkstate == CLK_AVAIL) + ret = dhdsdio_htclk(bus, FALSE, FALSE); + else + DHD_ERROR(("dhdsdio_clkctl: request for %d -> %d\n", + bus->clkstate, target)); + if (ret == BCME_OK) { + dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms); + } + break; + + case CLK_NONE: + /* Make sure to remove HT request */ + if (bus->clkstate == CLK_AVAIL) + ret = dhdsdio_htclk(bus, FALSE, FALSE); + /* Now remove the SD clock */ + ret = dhdsdio_sdclk(bus, FALSE); +#ifdef DHD_DEBUG + if (dhd_console_ms == 0) +#endif /* DHD_DEBUG */ + if (bus->poll == 0) + dhd_os_wd_timer(bus->dhd, 0); + break; + } +#ifdef DHD_DEBUG + DHD_INFO(("dhdsdio_clkctl: %d -> %d\n", oldstate, bus->clkstate)); +#endif /* DHD_DEBUG */ + + return ret; +} + +static int +dhdsdio_bussleep(dhd_bus_t *bus, bool sleep) +{ + bcmsdh_info_t *sdh = bus->sdh; + sdpcmd_regs_t *regs = bus->regs; + uint retries = 0; + + DHD_INFO(("dhdsdio_bussleep: request %s (currently %s)\n", + (sleep ? "SLEEP" : "WAKE"), + (bus->sleeping ? "SLEEP" : "WAKE"))); + + /* Done if we're already in the requested state */ + if (sleep == bus->sleeping) + return BCME_OK; + + /* Going to sleep: set the alarm and turn off the lights... */ + if (sleep) { + /* Don't sleep if something is pending */ + if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq)) + return BCME_BUSY; + + + /* Disable SDIO interrupts (no longer interested) */ + bcmsdh_intr_disable(bus->sdh); + + /* Make sure the controller has the bus up */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + /* Tell device to start using OOB wakeup */ + W_SDREG(SMB_USE_OOB, ®s->tosbmailbox, retries); + if (retries > retry_limit) + DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n")); + + /* Turn off our contribution to the HT clock request */ + dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); + + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, + SBSDIO_FORCE_HW_CLKREQ_OFF, NULL); + + /* Isolate the bus */ + if (bus->sih->chip != BCM4329_CHIP_ID && bus->sih->chip != BCM4319_CHIP_ID) { + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, + SBSDIO_DEVCTL_PADS_ISO, NULL); + } + + /* Change state */ + bus->sleeping = TRUE; + + } else { + /* Waking up: bus power up is ok, set local state */ + + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, + 0, NULL); + + /* Force pad isolation off if possible (in case power never toggled) */ + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, 0, NULL); + + + /* Make sure the controller has the bus up */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + /* Send misc interrupt to indicate OOB not needed */ + W_SDREG(0, ®s->tosbmailboxdata, retries); + if (retries <= retry_limit) + W_SDREG(SMB_DEV_INT, ®s->tosbmailbox, retries); + + if (retries > retry_limit) + DHD_ERROR(("CANNOT SIGNAL CHIP TO CLEAR OOB!!\n")); + + /* Make sure we have SD bus access */ + dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); + + /* Change state */ + bus->sleeping = FALSE; + + /* Enable interrupts again */ + if (bus->intr && (bus->dhd->busstate == DHD_BUS_DATA)) { + bus->intdis = FALSE; + bcmsdh_intr_enable(bus->sdh); + } + } + + return BCME_OK; +} + +#if defined(OOB_INTR_ONLY) +void +dhd_enable_oob_intr(struct dhd_bus *bus, bool enable) +{ +#if defined(HW_OOB) + bcmsdh_enable_hw_oob_intr(bus->sdh, enable); +#else + sdpcmd_regs_t *regs = bus->regs; + uint retries = 0; + + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + if (enable == TRUE) { + + /* Tell device to start using OOB wakeup */ + W_SDREG(SMB_USE_OOB, ®s->tosbmailbox, retries); + if (retries > retry_limit) + DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n")); + + } else { + /* Send misc interrupt to indicate OOB not needed */ + W_SDREG(0, ®s->tosbmailboxdata, retries); + if (retries <= retry_limit) + W_SDREG(SMB_DEV_INT, ®s->tosbmailbox, retries); + } + + /* Turn off our contribution to the HT clock request */ + dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); +#endif /* !defined(HW_OOB) */ +} +#endif /* defined(OOB_INTR_ONLY) */ + +/* Writes a HW/SW header into the packet and sends it. */ +/* Assumes: (a) header space already there, (b) caller holds lock */ +static int +dhdsdio_txpkt(dhd_bus_t *bus, void *pkt, uint chan, bool free_pkt) +{ + int ret; + osl_t *osh; + uint8 *frame; + uint16 len, pad1 = 0; + uint32 swheader; + uint retries = 0; + bcmsdh_info_t *sdh; + void *new; + int i; +#ifdef WLMEDIA_HTSF + char *p; + htsfts_t *htsf_ts; +#endif + + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + sdh = bus->sdh; + osh = bus->dhd->osh; + + if (bus->dhd->dongle_reset) { + ret = BCME_NOTREADY; + goto done; + } + + frame = (uint8*)PKTDATA(osh, pkt); + +#ifdef WLMEDIA_HTSF + if (PKTLEN(osh, pkt) >= 100) { + p = PKTDATA(osh, pkt); + htsf_ts = (htsfts_t*) (p + HTSF_HOSTOFFSET + 12); + if (htsf_ts->magic == HTSFMAGIC) { + htsf_ts->c20 = get_cycles(); + htsf_ts->t20 = dhd_get_htsf(bus->dhd->info, 0); + } + } +#endif /* WLMEDIA_HTSF */ + + /* Add alignment padding, allocate new packet if needed */ + if ((pad1 = ((uintptr)frame % DHD_SDALIGN))) { + if (PKTHEADROOM(osh, pkt) < pad1) { + DHD_INFO(("%s: insufficient headroom %d for %d pad1\n", + __FUNCTION__, (int)PKTHEADROOM(osh, pkt), pad1)); + bus->dhd->tx_realloc++; + new = PKTGET(osh, (PKTLEN(osh, pkt) + DHD_SDALIGN), TRUE); + if (!new) { + DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n", + __FUNCTION__, PKTLEN(osh, pkt) + DHD_SDALIGN)); + ret = BCME_NOMEM; + goto done; + } + + PKTALIGN(osh, new, PKTLEN(osh, pkt), DHD_SDALIGN); + bcopy(PKTDATA(osh, pkt), PKTDATA(osh, new), PKTLEN(osh, pkt)); + if (free_pkt) + PKTFREE(osh, pkt, TRUE); + /* free the pkt if canned one is not used */ + free_pkt = TRUE; + pkt = new; + frame = (uint8*)PKTDATA(osh, pkt); + ASSERT(((uintptr)frame % DHD_SDALIGN) == 0); + pad1 = 0; + } else { + PKTPUSH(osh, pkt, pad1); + frame = (uint8*)PKTDATA(osh, pkt); + + ASSERT((pad1 + SDPCM_HDRLEN) <= (int) PKTLEN(osh, pkt)); + bzero(frame, pad1 + SDPCM_HDRLEN); + } + } + ASSERT(pad1 < DHD_SDALIGN); + + /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */ + len = (uint16)PKTLEN(osh, pkt); + *(uint16*)frame = htol16(len); + *(((uint16*)frame) + 1) = htol16(~len); + + /* Software tag: channel, sequence number, data offset */ + swheader = ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq | + (((pad1 + SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); + htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN); + htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader)); + +#ifdef DHD_DEBUG + if (PKTPRIO(pkt) < ARRAYSIZE(tx_packets)) { + tx_packets[PKTPRIO(pkt)]++; + } + if (DHD_BYTES_ON() && + (((DHD_CTL_ON() && (chan == SDPCM_CONTROL_CHANNEL)) || + (DHD_DATA_ON() && (chan != SDPCM_CONTROL_CHANNEL))))) { + prhex("Tx Frame", frame, len); + } else if (DHD_HDRS_ON()) { + prhex("TxHdr", frame, MIN(len, 16)); + } +#endif + + /* Raise len to next SDIO block to eliminate tail command */ + if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { + uint16 pad2 = bus->blocksize - (len % bus->blocksize); + if ((pad2 <= bus->roundup) && (pad2 < bus->blocksize)) +#ifdef NOTUSED + if (pad2 <= PKTTAILROOM(osh, pkt)) +#endif /* NOTUSED */ + len += pad2; + } else if (len % DHD_SDALIGN) { + len += DHD_SDALIGN - (len % DHD_SDALIGN); + } + + /* Some controllers have trouble with odd bytes -- round to even */ + if (forcealign && (len & (ALIGNMENT - 1))) { +#ifdef NOTUSED + if (PKTTAILROOM(osh, pkt)) +#endif + len = ROUNDUP(len, ALIGNMENT); +#ifdef NOTUSED + else + DHD_ERROR(("%s: sending unrounded %d-byte packet\n", __FUNCTION__, len)); +#endif + } + + do { + ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, + frame, len, pkt, NULL, NULL); + bus->f2txdata++; + ASSERT(ret != BCME_PENDING); + + if (ret < 0) { + /* On failure, abort the command and terminate the frame */ + DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n", + __FUNCTION__, ret)); + bus->tx_sderrs++; + + bcmsdh_abort(sdh, SDIO_FUNC_2); + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, + SFC_WF_TERM, NULL); + bus->f1regdata++; + + for (i = 0; i < 3; i++) { + uint8 hi, lo; + hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_WFRAMEBCHI, NULL); + lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_WFRAMEBCLO, NULL); + bus->f1regdata += 2; + if ((hi == 0) && (lo == 0)) + break; + } + + } + if (ret == 0) { + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; + } + } while ((ret < 0) && retrydata && retries++ < TXRETRIES); + +done: + /* restore pkt buffer pointer before calling tx complete routine */ + PKTPULL(osh, pkt, SDPCM_HDRLEN + pad1); +#ifdef PROP_TXSTATUS + if (bus->dhd->wlfc_state) { + dhd_os_sdunlock(bus->dhd); + dhd_wlfc_txcomplete(bus->dhd, pkt, ret == 0); + dhd_os_sdlock(bus->dhd); + } else { +#endif /* PROP_TXSTATUS */ + dhd_txcomplete(bus->dhd, pkt, ret != 0); + if (free_pkt) + PKTFREE(osh, pkt, TRUE); + +#ifdef PROP_TXSTATUS + } +#endif + return ret; +} + +int +dhd_bus_txdata(struct dhd_bus *bus, void *pkt) +{ + int ret = BCME_ERROR; + osl_t *osh; + uint datalen, prec; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + osh = bus->dhd->osh; + datalen = PKTLEN(osh, pkt); + +#ifdef SDTEST + /* Push the test header if doing loopback */ + if (bus->ext_loop) { + uint8* data; + PKTPUSH(osh, pkt, SDPCM_TEST_HDRLEN); + data = PKTDATA(osh, pkt); + *data++ = SDPCM_TEST_ECHOREQ; + *data++ = (uint8)bus->loopid++; + *data++ = (datalen >> 0); + *data++ = (datalen >> 8); + datalen += SDPCM_TEST_HDRLEN; + } +#endif /* SDTEST */ + + /* Add space for the header */ + PKTPUSH(osh, pkt, SDPCM_HDRLEN); + ASSERT(ISALIGNED((uintptr)PKTDATA(osh, pkt), 2)); + + prec = PRIO2PREC((PKTPRIO(pkt) & PRIOMASK)); +#ifndef DHDTHREAD + /* Lock: we're about to use shared data/code (and SDIO) */ + dhd_os_sdlock(bus->dhd); +#endif /* DHDTHREAD */ + + /* Check for existing queue, current flow-control, pending event, or pending clock */ + if (dhd_deferred_tx || bus->fcstate || pktq_len(&bus->txq) || bus->dpc_sched || + (!DATAOK(bus)) || (bus->flowcontrol & NBITVAL(prec)) || + (bus->clkstate != CLK_AVAIL)) { + DHD_TRACE(("%s: deferring pktq len %d\n", __FUNCTION__, + pktq_len(&bus->txq))); + bus->fcqueued++; + + /* Priority based enq */ + dhd_os_sdlock_txq(bus->dhd); + if (dhd_prec_enq(bus->dhd, &bus->txq, pkt, prec) == FALSE) { + PKTPULL(osh, pkt, SDPCM_HDRLEN); +#ifndef DHDTHREAD + /* Need to also release txqlock before releasing sdlock. + * This thread still has txqlock and releases sdlock. + * Deadlock happens when dpc() grabs sdlock first then + * attempts to grab txqlock. + */ + dhd_os_sdunlock_txq(bus->dhd); + dhd_os_sdunlock(bus->dhd); +#endif +#ifdef PROP_TXSTATUS + if (bus->dhd->wlfc_state) + dhd_wlfc_txcomplete(bus->dhd, pkt, FALSE); + else +#endif + dhd_txcomplete(bus->dhd, pkt, FALSE); +#ifndef DHDTHREAD + dhd_os_sdlock(bus->dhd); + dhd_os_sdlock_txq(bus->dhd); +#endif +#ifdef PROP_TXSTATUS + /* let the caller decide whether to free the packet */ + if (!bus->dhd->wlfc_state) +#endif + PKTFREE(osh, pkt, TRUE); + ret = BCME_NORESOURCE; + } + else + ret = BCME_OK; + dhd_os_sdunlock_txq(bus->dhd); + + if ((pktq_len(&bus->txq) >= FCHI) && dhd_doflow) + dhd_txflowcontrol(bus->dhd, ALL_INTERFACES, ON); + +#ifdef DHD_DEBUG + if (pktq_plen(&bus->txq, prec) > qcount[prec]) + qcount[prec] = pktq_plen(&bus->txq, prec); +#endif + /* Schedule DPC if needed to send queued packet(s) */ + if (dhd_deferred_tx && !bus->dpc_sched) { + bus->dpc_sched = TRUE; + dhd_sched_dpc(bus->dhd); + } + } else { +#ifdef DHDTHREAD + /* Lock: we're about to use shared data/code (and SDIO) */ + dhd_os_sdlock(bus->dhd); +#endif /* DHDTHREAD */ + + /* Otherwise, send it now */ + BUS_WAKE(bus); + /* Make sure back plane ht clk is on, no pending allowed */ + dhdsdio_clkctl(bus, CLK_AVAIL, TRUE); +#ifndef SDTEST + ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, TRUE); +#else + ret = dhdsdio_txpkt(bus, pkt, + (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), TRUE); +#endif + if (ret) + bus->dhd->tx_errors++; + else + bus->dhd->dstats.tx_bytes += datalen; + + if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { + bus->activity = FALSE; + dhdsdio_clkctl(bus, CLK_NONE, TRUE); + } + +#ifdef DHDTHREAD + dhd_os_sdunlock(bus->dhd); +#endif /* DHDTHREAD */ + } + +#ifndef DHDTHREAD + dhd_os_sdunlock(bus->dhd); +#endif /* DHDTHREAD */ + + return ret; +} + +static uint +dhdsdio_sendfromq(dhd_bus_t *bus, uint maxframes) +{ + void *pkt; + uint32 intstatus = 0; + uint retries = 0; + int ret = 0, prec_out; + uint cnt = 0; + uint datalen; + uint8 tx_prec_map; + + dhd_pub_t *dhd = bus->dhd; + sdpcmd_regs_t *regs = bus->regs; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + tx_prec_map = ~bus->flowcontrol; + + /* Send frames until the limit or some other event */ + for (cnt = 0; (cnt < maxframes) && DATAOK(bus); cnt++) { + dhd_os_sdlock_txq(bus->dhd); + if ((pkt = pktq_mdeq(&bus->txq, tx_prec_map, &prec_out)) == NULL) { + dhd_os_sdunlock_txq(bus->dhd); + break; + } + dhd_os_sdunlock_txq(bus->dhd); + datalen = PKTLEN(bus->dhd->osh, pkt) - SDPCM_HDRLEN; + +#ifndef SDTEST + ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, TRUE); +#else + ret = dhdsdio_txpkt(bus, pkt, + (bus->ext_loop ? SDPCM_TEST_CHANNEL : SDPCM_DATA_CHANNEL), TRUE); +#endif + if (ret) + bus->dhd->tx_errors++; + else + bus->dhd->dstats.tx_bytes += datalen; + + /* In poll mode, need to check for other events */ + if (!bus->intr && cnt) + { + /* Check device status, signal pending interrupt */ + R_SDREG(intstatus, ®s->intstatus, retries); + bus->f2txdata++; + if (bcmsdh_regfail(bus->sdh)) + break; + if (intstatus & bus->hostintmask) + bus->ipend = TRUE; + } + } + + /* Deflow-control stack if needed */ + if (dhd_doflow && dhd->up && (dhd->busstate == DHD_BUS_DATA) && + dhd->txoff && (pktq_len(&bus->txq) < FCLOW)) + dhd_txflowcontrol(dhd, ALL_INTERFACES, OFF); + + return cnt; +} + +int +dhd_bus_txctl(struct dhd_bus *bus, uchar *msg, uint msglen) +{ + uint8 *frame; + uint16 len; + uint32 swheader; + uint retries = 0; + bcmsdh_info_t *sdh = bus->sdh; + uint8 doff = 0; + int ret = -1; + int i; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (bus->dhd->dongle_reset) + return -EIO; + + /* Back the pointer to make a room for bus header */ + frame = msg - SDPCM_HDRLEN; + len = (msglen += SDPCM_HDRLEN); + + /* Add alignment padding (optional for ctl frames) */ + if (dhd_alignctl) { + if ((doff = ((uintptr)frame % DHD_SDALIGN))) { + frame -= doff; + len += doff; + msglen += doff; + bzero(frame, doff + SDPCM_HDRLEN); + } + ASSERT(doff < DHD_SDALIGN); + } + doff += SDPCM_HDRLEN; + + /* Round send length to next SDIO block */ + if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { + uint16 pad = bus->blocksize - (len % bus->blocksize); + if ((pad <= bus->roundup) && (pad < bus->blocksize)) + len += pad; + } else if (len % DHD_SDALIGN) { + len += DHD_SDALIGN - (len % DHD_SDALIGN); + } + + /* Satisfy length-alignment requirements */ + if (forcealign && (len & (ALIGNMENT - 1))) + len = ROUNDUP(len, ALIGNMENT); + + ASSERT(ISALIGNED((uintptr)frame, 2)); + + + /* Need to lock here to protect txseq and SDIO tx calls */ + dhd_os_sdlock(bus->dhd); + + BUS_WAKE(bus); + + /* Make sure backplane clock is on */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */ + *(uint16*)frame = htol16((uint16)msglen); + *(((uint16*)frame) + 1) = htol16(~msglen); + + /* Software tag: channel, sequence number, data offset */ + swheader = ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) + | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); + htol32_ua_store(swheader, frame + SDPCM_FRAMETAG_LEN); + htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader)); + + if (!TXCTLOK(bus)) { + DHD_INFO(("%s: No bus credit bus->tx_max %d, bus->tx_seq %d\n", + __FUNCTION__, bus->tx_max, bus->tx_seq)); + bus->ctrl_frame_stat = TRUE; + /* Send from dpc */ + bus->ctrl_frame_buf = frame; + bus->ctrl_frame_len = len; + dhd_wait_for_event(bus->dhd, &bus->ctrl_frame_stat); + if (bus->ctrl_frame_stat == FALSE) { + DHD_INFO(("%s: ctrl_frame_stat == FALSE\n", __FUNCTION__)); + ret = 0; + } else { + bus->dhd->txcnt_timeout++; + if (!bus->dhd->hang_was_sent) + DHD_ERROR(("%s: ctrl_frame_stat == TRUE txcnt_timeout=%d\n", + __FUNCTION__, bus->dhd->txcnt_timeout)); + ret = -1; + bus->ctrl_frame_stat = FALSE; + goto done; + } + } + + bus->dhd->txcnt_timeout = 0; + + if (ret == -1) { +#ifdef DHD_DEBUG + if (DHD_BYTES_ON() && DHD_CTL_ON()) { + prhex("Tx Frame", frame, len); + } else if (DHD_HDRS_ON()) { + prhex("TxHdr", frame, MIN(len, 16)); + } +#endif + + do { + ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, + frame, len, NULL, NULL, NULL); + ASSERT(ret != BCME_PENDING); + + if (ret < 0) { + /* On failure, abort the command and terminate the frame */ + DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n", + __FUNCTION__, ret)); + bus->tx_sderrs++; + + bcmsdh_abort(sdh, SDIO_FUNC_2); + + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, + SFC_WF_TERM, NULL); + bus->f1regdata++; + + for (i = 0; i < 3; i++) { + uint8 hi, lo; + hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_WFRAMEBCHI, NULL); + lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_WFRAMEBCLO, NULL); + bus->f1regdata += 2; + if ((hi == 0) && (lo == 0)) + break; + } + + } + if (ret == 0) { + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; + } + } while ((ret < 0) && retries++ < TXRETRIES); + } + +done: + if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { + bus->activity = FALSE; + dhdsdio_clkctl(bus, CLK_NONE, TRUE); + } + + dhd_os_sdunlock(bus->dhd); + + if (ret) + bus->dhd->tx_ctlerrs++; + else + bus->dhd->tx_ctlpkts++; + + if (bus->dhd->txcnt_timeout >= MAX_CNTL_TIMEOUT) + return -ETIMEDOUT; + + return ret ? -EIO : 0; +} + +int +dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen) +{ + int timeleft; + uint rxlen = 0; + bool pending = FALSE; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (bus->dhd->dongle_reset) + return -EIO; + + /* Wait until control frame is available */ + timeleft = dhd_os_ioctl_resp_wait(bus->dhd, &bus->rxlen, &pending); + + dhd_os_sdlock(bus->dhd); + rxlen = bus->rxlen; + bcopy(bus->rxctl, msg, MIN(msglen, rxlen)); + bus->rxlen = 0; + dhd_os_sdunlock(bus->dhd); + + if (rxlen) { + DHD_CTL(("%s: resumed on rxctl frame, got %d expected %d\n", + __FUNCTION__, rxlen, msglen)); + } else if (timeleft == 0) { + DHD_ERROR(("%s: resumed on timeout\n", __FUNCTION__)); +#ifdef DHD_DEBUG + dhd_os_sdlock(bus->dhd); + dhdsdio_checkdied(bus, NULL, 0); + dhd_os_sdunlock(bus->dhd); +#endif /* DHD_DEBUG */ + } else if (pending == TRUE) { + /* signal pending */ + DHD_ERROR(("%s: signal pending\n", __FUNCTION__)); + return -EINTR; + } else { + DHD_CTL(("%s: resumed for unknown reason?\n", __FUNCTION__)); +#ifdef DHD_DEBUG + dhd_os_sdlock(bus->dhd); + dhdsdio_checkdied(bus, NULL, 0); + dhd_os_sdunlock(bus->dhd); +#endif /* DHD_DEBUG */ + } + if (timeleft == 0) { + bus->dhd->rxcnt_timeout++; + DHD_ERROR(("%s: rxcnt_timeout=%d\n", __FUNCTION__, bus->dhd->rxcnt_timeout)); + } + else + bus->dhd->rxcnt_timeout = 0; + + if (rxlen) + bus->dhd->rx_ctlpkts++; + else + bus->dhd->rx_ctlerrs++; + + if (bus->dhd->rxcnt_timeout >= MAX_CNTL_TIMEOUT) + return -ETIMEDOUT; + + return rxlen ? (int)rxlen : -EIO; +} + +/* IOVar table */ +enum { + IOV_INTR = 1, + IOV_POLLRATE, + IOV_SDREG, + IOV_SBREG, + IOV_SDCIS, + IOV_MEMBYTES, + IOV_MEMSIZE, +#ifdef DHD_DEBUG + IOV_CHECKDIED, + IOV_SERIALCONS, +#endif /* DHD_DEBUG */ + IOV_DOWNLOAD, + IOV_SOCRAM_STATE, + IOV_FORCEEVEN, + IOV_SDIOD_DRIVE, + IOV_READAHEAD, + IOV_SDRXCHAIN, + IOV_ALIGNCTL, + IOV_SDALIGN, + IOV_DEVRESET, + IOV_CPU, +#ifdef SDTEST + IOV_PKTGEN, + IOV_EXTLOOP, +#endif /* SDTEST */ + IOV_SPROM, + IOV_TXBOUND, + IOV_RXBOUND, + IOV_TXMINMAX, + IOV_IDLETIME, + IOV_IDLECLOCK, + IOV_SD1IDLE, + IOV_SLEEP, + IOV_DONGLEISOLATION, + IOV_VARS, +#ifdef SOFTAP + IOV_FWPATH +#endif +}; + +const bcm_iovar_t dhdsdio_iovars[] = { + {"intr", IOV_INTR, 0, IOVT_BOOL, 0 }, + {"sleep", IOV_SLEEP, 0, IOVT_BOOL, 0 }, + {"pollrate", IOV_POLLRATE, 0, IOVT_UINT32, 0 }, + {"idletime", IOV_IDLETIME, 0, IOVT_INT32, 0 }, + {"idleclock", IOV_IDLECLOCK, 0, IOVT_INT32, 0 }, + {"sd1idle", IOV_SD1IDLE, 0, IOVT_BOOL, 0 }, + {"membytes", IOV_MEMBYTES, 0, IOVT_BUFFER, 2 * sizeof(int) }, + {"memsize", IOV_MEMSIZE, 0, IOVT_UINT32, 0 }, + {"download", IOV_DOWNLOAD, 0, IOVT_BOOL, 0 }, + {"socram_state", IOV_SOCRAM_STATE, 0, IOVT_BOOL, 0 }, + {"vars", IOV_VARS, 0, IOVT_BUFFER, 0 }, + {"sdiod_drive", IOV_SDIOD_DRIVE, 0, IOVT_UINT32, 0 }, + {"readahead", IOV_READAHEAD, 0, IOVT_BOOL, 0 }, + {"sdrxchain", IOV_SDRXCHAIN, 0, IOVT_BOOL, 0 }, + {"alignctl", IOV_ALIGNCTL, 0, IOVT_BOOL, 0 }, + {"sdalign", IOV_SDALIGN, 0, IOVT_BOOL, 0 }, + {"devreset", IOV_DEVRESET, 0, IOVT_BOOL, 0 }, +#ifdef DHD_DEBUG + {"sdreg", IOV_SDREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, + {"sbreg", IOV_SBREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, + {"sd_cis", IOV_SDCIS, 0, IOVT_BUFFER, DHD_IOCTL_MAXLEN }, + {"forcealign", IOV_FORCEEVEN, 0, IOVT_BOOL, 0 }, + {"txbound", IOV_TXBOUND, 0, IOVT_UINT32, 0 }, + {"rxbound", IOV_RXBOUND, 0, IOVT_UINT32, 0 }, + {"txminmax", IOV_TXMINMAX, 0, IOVT_UINT32, 0 }, + {"cpu", IOV_CPU, 0, IOVT_BOOL, 0 }, +#ifdef DHD_DEBUG + {"checkdied", IOV_CHECKDIED, 0, IOVT_BUFFER, 0 }, + {"serial", IOV_SERIALCONS, 0, IOVT_UINT32, 0 }, +#endif /* DHD_DEBUG */ +#endif /* DHD_DEBUG */ +#ifdef SDTEST + {"extloop", IOV_EXTLOOP, 0, IOVT_BOOL, 0 }, + {"pktgen", IOV_PKTGEN, 0, IOVT_BUFFER, sizeof(dhd_pktgen_t) }, +#endif /* SDTEST */ + {"dngl_isolation", IOV_DONGLEISOLATION, 0, IOVT_UINT32, 0 }, +#ifdef SOFTAP + {"fwpath", IOV_FWPATH, 0, IOVT_BUFFER, 0 }, +#endif + {NULL, 0, 0, 0, 0 } +}; + +static void +dhd_dump_pct(struct bcmstrbuf *strbuf, char *desc, uint num, uint div) +{ + uint q1, q2; + + if (!div) { + bcm_bprintf(strbuf, "%s N/A", desc); + } else { + q1 = num / div; + q2 = (100 * (num - (q1 * div))) / div; + bcm_bprintf(strbuf, "%s %d.%02d", desc, q1, q2); + } +} + +void +dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf) +{ + dhd_bus_t *bus = dhdp->bus; + + bcm_bprintf(strbuf, "Bus SDIO structure:\n"); + bcm_bprintf(strbuf, "hostintmask 0x%08x intstatus 0x%08x sdpcm_ver %d\n", + bus->hostintmask, bus->intstatus, bus->sdpcm_ver); + bcm_bprintf(strbuf, "fcstate %d qlen %d tx_seq %d, max %d, rxskip %d rxlen %d rx_seq %d\n", + bus->fcstate, pktq_len(&bus->txq), bus->tx_seq, bus->tx_max, bus->rxskip, + bus->rxlen, bus->rx_seq); + bcm_bprintf(strbuf, "intr %d intrcount %d lastintrs %d spurious %d\n", + bus->intr, bus->intrcount, bus->lastintrs, bus->spurious); + bcm_bprintf(strbuf, "pollrate %d pollcnt %d regfails %d\n", + bus->pollrate, bus->pollcnt, bus->regfails); + + bcm_bprintf(strbuf, "\nAdditional counters:\n"); + bcm_bprintf(strbuf, "tx_sderrs %d fcqueued %d rxrtx %d rx_toolong %d rxc_errors %d\n", + bus->tx_sderrs, bus->fcqueued, bus->rxrtx, bus->rx_toolong, + bus->rxc_errors); + bcm_bprintf(strbuf, "rx_hdrfail %d badhdr %d badseq %d\n", + bus->rx_hdrfail, bus->rx_badhdr, bus->rx_badseq); + bcm_bprintf(strbuf, "fc_rcvd %d, fc_xoff %d, fc_xon %d\n", + bus->fc_rcvd, bus->fc_xoff, bus->fc_xon); + bcm_bprintf(strbuf, "rxglomfail %d, rxglomframes %d, rxglompkts %d\n", + bus->rxglomfail, bus->rxglomframes, bus->rxglompkts); + bcm_bprintf(strbuf, "f2rx (hdrs/data) %d (%d/%d), f2tx %d f1regs %d\n", + (bus->f2rxhdrs + bus->f2rxdata), bus->f2rxhdrs, bus->f2rxdata, + bus->f2txdata, bus->f1regdata); + { + dhd_dump_pct(strbuf, "\nRx: pkts/f2rd", bus->dhd->rx_packets, + (bus->f2rxhdrs + bus->f2rxdata)); + dhd_dump_pct(strbuf, ", pkts/f1sd", bus->dhd->rx_packets, bus->f1regdata); + dhd_dump_pct(strbuf, ", pkts/sd", bus->dhd->rx_packets, + (bus->f2rxhdrs + bus->f2rxdata + bus->f1regdata)); + dhd_dump_pct(strbuf, ", pkts/int", bus->dhd->rx_packets, bus->intrcount); + bcm_bprintf(strbuf, "\n"); + + dhd_dump_pct(strbuf, "Rx: glom pct", (100 * bus->rxglompkts), + bus->dhd->rx_packets); + dhd_dump_pct(strbuf, ", pkts/glom", bus->rxglompkts, bus->rxglomframes); + bcm_bprintf(strbuf, "\n"); + + dhd_dump_pct(strbuf, "Tx: pkts/f2wr", bus->dhd->tx_packets, bus->f2txdata); + dhd_dump_pct(strbuf, ", pkts/f1sd", bus->dhd->tx_packets, bus->f1regdata); + dhd_dump_pct(strbuf, ", pkts/sd", bus->dhd->tx_packets, + (bus->f2txdata + bus->f1regdata)); + dhd_dump_pct(strbuf, ", pkts/int", bus->dhd->tx_packets, bus->intrcount); + bcm_bprintf(strbuf, "\n"); + + dhd_dump_pct(strbuf, "Total: pkts/f2rw", + (bus->dhd->tx_packets + bus->dhd->rx_packets), + (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata)); + dhd_dump_pct(strbuf, ", pkts/f1sd", + (bus->dhd->tx_packets + bus->dhd->rx_packets), bus->f1regdata); + dhd_dump_pct(strbuf, ", pkts/sd", + (bus->dhd->tx_packets + bus->dhd->rx_packets), + (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata + bus->f1regdata)); + dhd_dump_pct(strbuf, ", pkts/int", + (bus->dhd->tx_packets + bus->dhd->rx_packets), bus->intrcount); + bcm_bprintf(strbuf, "\n\n"); + } + +#ifdef SDTEST + if (bus->pktgen_count) { + bcm_bprintf(strbuf, "pktgen config and count:\n"); + bcm_bprintf(strbuf, "freq %d count %d print %d total %d min %d len %d\n", + bus->pktgen_freq, bus->pktgen_count, bus->pktgen_print, + bus->pktgen_total, bus->pktgen_minlen, bus->pktgen_maxlen); + bcm_bprintf(strbuf, "send attempts %d rcvd %d fail %d\n", + bus->pktgen_sent, bus->pktgen_rcvd, bus->pktgen_fail); + } +#endif /* SDTEST */ +#ifdef DHD_DEBUG + bcm_bprintf(strbuf, "dpc_sched %d host interrupt%spending\n", + bus->dpc_sched, (bcmsdh_intr_pending(bus->sdh) ? " " : " not ")); + bcm_bprintf(strbuf, "blocksize %d roundup %d\n", bus->blocksize, bus->roundup); +#endif /* DHD_DEBUG */ + bcm_bprintf(strbuf, "clkstate %d activity %d idletime %d idlecount %d sleeping %d\n", + bus->clkstate, bus->activity, bus->idletime, bus->idlecount, bus->sleeping); +} + +void +dhd_bus_clearcounts(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus = (dhd_bus_t *)dhdp->bus; + + bus->intrcount = bus->lastintrs = bus->spurious = bus->regfails = 0; + bus->rxrtx = bus->rx_toolong = bus->rxc_errors = 0; + bus->rx_hdrfail = bus->rx_badhdr = bus->rx_badseq = 0; + bus->tx_sderrs = bus->fc_rcvd = bus->fc_xoff = bus->fc_xon = 0; + bus->rxglomfail = bus->rxglomframes = bus->rxglompkts = 0; + bus->f2rxhdrs = bus->f2rxdata = bus->f2txdata = bus->f1regdata = 0; +} + +#ifdef SDTEST +static int +dhdsdio_pktgen_get(dhd_bus_t *bus, uint8 *arg) +{ + dhd_pktgen_t pktgen; + + pktgen.version = DHD_PKTGEN_VERSION; + pktgen.freq = bus->pktgen_freq; + pktgen.count = bus->pktgen_count; + pktgen.print = bus->pktgen_print; + pktgen.total = bus->pktgen_total; + pktgen.minlen = bus->pktgen_minlen; + pktgen.maxlen = bus->pktgen_maxlen; + pktgen.numsent = bus->pktgen_sent; + pktgen.numrcvd = bus->pktgen_rcvd; + pktgen.numfail = bus->pktgen_fail; + pktgen.mode = bus->pktgen_mode; + pktgen.stop = bus->pktgen_stop; + + bcopy(&pktgen, arg, sizeof(pktgen)); + + return 0; +} + +static int +dhdsdio_pktgen_set(dhd_bus_t *bus, uint8 *arg) +{ + dhd_pktgen_t pktgen; + uint oldcnt, oldmode; + + bcopy(arg, &pktgen, sizeof(pktgen)); + if (pktgen.version != DHD_PKTGEN_VERSION) + return BCME_BADARG; + + oldcnt = bus->pktgen_count; + oldmode = bus->pktgen_mode; + + bus->pktgen_freq = pktgen.freq; + bus->pktgen_count = pktgen.count; + bus->pktgen_print = pktgen.print; + bus->pktgen_total = pktgen.total; + bus->pktgen_minlen = pktgen.minlen; + bus->pktgen_maxlen = pktgen.maxlen; + bus->pktgen_mode = pktgen.mode; + bus->pktgen_stop = pktgen.stop; + + bus->pktgen_tick = bus->pktgen_ptick = 0; + bus->pktgen_len = MAX(bus->pktgen_len, bus->pktgen_minlen); + bus->pktgen_len = MIN(bus->pktgen_len, bus->pktgen_maxlen); + + /* Clear counts for a new pktgen (mode change, or was stopped) */ + if (bus->pktgen_count && (!oldcnt || oldmode != bus->pktgen_mode)) + bus->pktgen_sent = bus->pktgen_rcvd = bus->pktgen_fail = 0; + + return 0; +} +#endif /* SDTEST */ + +static int +dhdsdio_membytes(dhd_bus_t *bus, bool write, uint32 address, uint8 *data, uint size) +{ + int bcmerror = 0; + uint32 sdaddr; + uint dsize; + + /* Determine initial transfer parameters */ + sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK; + if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK) + dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr); + else + dsize = size; + + /* Set the backplane window to include the start address */ + if ((bcmerror = dhdsdio_set_siaddr_window(bus, address))) { + DHD_ERROR(("%s: window change failed\n", __FUNCTION__)); + goto xfer_done; + } + + /* Do the transfer(s) */ + while (size) { + DHD_INFO(("%s: %s %d bytes at offset 0x%08x in window 0x%08x\n", + __FUNCTION__, (write ? "write" : "read"), dsize, sdaddr, + (address & SBSDIO_SBWINDOW_MASK))); + if ((bcmerror = bcmsdh_rwdata(bus->sdh, write, sdaddr, data, dsize))) { + DHD_ERROR(("%s: membytes transfer failed\n", __FUNCTION__)); + break; + } + + /* Adjust for next transfer (if any) */ + if ((size -= dsize)) { + data += dsize; + address += dsize; + if ((bcmerror = dhdsdio_set_siaddr_window(bus, address))) { + DHD_ERROR(("%s: window change failed\n", __FUNCTION__)); + break; + } + sdaddr = 0; + dsize = MIN(SBSDIO_SB_OFT_ADDR_LIMIT, size); + } + + } + +xfer_done: + /* Return the window to backplane enumeration space for core access */ + if (dhdsdio_set_siaddr_window(bus, bcmsdh_cur_sbwad(bus->sdh))) { + DHD_ERROR(("%s: FAILED to set window back to 0x%x\n", __FUNCTION__, + bcmsdh_cur_sbwad(bus->sdh))); + } + + return bcmerror; +} + +#ifdef DHD_DEBUG +static int +dhdsdio_readshared(dhd_bus_t *bus, sdpcm_shared_t *sh) +{ + uint32 addr; + int rv; + + /* Read last word in memory to determine address of sdpcm_shared structure */ + if ((rv = dhdsdio_membytes(bus, FALSE, bus->ramsize - 4, (uint8 *)&addr, 4)) < 0) + return rv; + + addr = ltoh32(addr); + + DHD_INFO(("sdpcm_shared address 0x%08X\n", addr)); + + /* + * Check if addr is valid. + * NVRAM length at the end of memory should have been overwritten. + */ + if (addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)) { + DHD_ERROR(("%s: address (0x%08x) of sdpcm_shared invalid\n", __FUNCTION__, addr)); + return BCME_ERROR; + } + + /* Read hndrte_shared structure */ + if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)sh, sizeof(sdpcm_shared_t))) < 0) + return rv; + + /* Endianness */ + sh->flags = ltoh32(sh->flags); + sh->trap_addr = ltoh32(sh->trap_addr); + sh->assert_exp_addr = ltoh32(sh->assert_exp_addr); + sh->assert_file_addr = ltoh32(sh->assert_file_addr); + sh->assert_line = ltoh32(sh->assert_line); + sh->console_addr = ltoh32(sh->console_addr); + sh->msgtrace_addr = ltoh32(sh->msgtrace_addr); + + if ((sh->flags & SDPCM_SHARED_VERSION_MASK) == 3 && SDPCM_SHARED_VERSION == 1) + return BCME_OK; + + if ((sh->flags & SDPCM_SHARED_VERSION_MASK) != SDPCM_SHARED_VERSION) { + DHD_ERROR(("%s: sdpcm_shared version %d in dhd " + "is different than sdpcm_shared version %d in dongle\n", + __FUNCTION__, SDPCM_SHARED_VERSION, + sh->flags & SDPCM_SHARED_VERSION_MASK)); + return BCME_ERROR; + } + + return BCME_OK; +} + + +static int +dhdsdio_readconsole(dhd_bus_t *bus) +{ + dhd_console_t *c = &bus->console; + uint8 line[CONSOLE_LINE_MAX], ch; + uint32 n, idx, addr; + int rv; + + /* Don't do anything until FWREADY updates console address */ + if (bus->console_addr == 0) + return 0; + + /* Read console log struct */ + addr = bus->console_addr + OFFSETOF(hndrte_cons_t, log); + if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)&c->log, sizeof(c->log))) < 0) + return rv; + + /* Allocate console buffer (one time only) */ + if (c->buf == NULL) { + c->bufsize = ltoh32(c->log.buf_size); + if ((c->buf = MALLOC(bus->dhd->osh, c->bufsize)) == NULL) + return BCME_NOMEM; + } + + idx = ltoh32(c->log.idx); + + /* Protect against corrupt value */ + if (idx > c->bufsize) + return BCME_ERROR; + + /* Skip reading the console buffer if the index pointer has not moved */ + if (idx == c->last) + return BCME_OK; + + /* Read the console buffer */ + addr = ltoh32(c->log.buf); + if ((rv = dhdsdio_membytes(bus, FALSE, addr, c->buf, c->bufsize)) < 0) + return rv; + + while (c->last != idx) { + for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) { + if (c->last == idx) { + /* This would output a partial line. Instead, back up + * the buffer pointer and output this line next time around. + */ + if (c->last >= n) + c->last -= n; + else + c->last = c->bufsize - n; + goto break2; + } + ch = c->buf[c->last]; + c->last = (c->last + 1) % c->bufsize; + if (ch == '\n') + break; + line[n] = ch; + } + + if (n > 0) { + if (line[n - 1] == '\r') + n--; + line[n] = 0; + printf("CONSOLE: %s\n", line); + } + } +break2: + + return BCME_OK; +} + +static int +dhdsdio_checkdied(dhd_bus_t *bus, char *data, uint size) +{ + int bcmerror = 0; + uint msize = 512; + char *mbuffer = NULL; + char *console_buffer = NULL; + uint maxstrlen = 256; + char *str = NULL; + trap_t tr; + sdpcm_shared_t sdpcm_shared; + struct bcmstrbuf strbuf; + uint32 console_ptr, console_size, console_index; + uint8 line[CONSOLE_LINE_MAX], ch; + uint32 n, i, addr; + int rv; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (data == NULL) { + /* + * Called after a rx ctrl timeout. "data" is NULL. + * allocate memory to trace the trap or assert. + */ + size = msize; + mbuffer = data = MALLOC(bus->dhd->osh, msize); + if (mbuffer == NULL) { + DHD_ERROR(("%s: MALLOC(%d) failed \n", __FUNCTION__, msize)); + bcmerror = BCME_NOMEM; + goto done; + } + } + + if ((str = MALLOC(bus->dhd->osh, maxstrlen)) == NULL) { + DHD_ERROR(("%s: MALLOC(%d) failed \n", __FUNCTION__, maxstrlen)); + bcmerror = BCME_NOMEM; + goto done; + } + + if ((bcmerror = dhdsdio_readshared(bus, &sdpcm_shared)) < 0) + goto done; + + bcm_binit(&strbuf, data, size); + + bcm_bprintf(&strbuf, "msgtrace address : 0x%08X\nconsole address : 0x%08X\n", + sdpcm_shared.msgtrace_addr, sdpcm_shared.console_addr); + + if ((sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT) == 0) { + /* NOTE: Misspelled assert is intentional - DO NOT FIX. + * (Avoids conflict with real asserts for programmatic parsing of output.) + */ + bcm_bprintf(&strbuf, "Assrt not built in dongle\n"); + } + + if ((sdpcm_shared.flags & (SDPCM_SHARED_ASSERT|SDPCM_SHARED_TRAP)) == 0) { + /* NOTE: Misspelled assert is intentional - DO NOT FIX. + * (Avoids conflict with real asserts for programmatic parsing of output.) + */ + bcm_bprintf(&strbuf, "No trap%s in dongle", + (sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT) + ?"/assrt" :""); + } else { + if (sdpcm_shared.flags & SDPCM_SHARED_ASSERT) { + /* Download assert */ + bcm_bprintf(&strbuf, "Dongle assert"); + if (sdpcm_shared.assert_exp_addr != 0) { + str[0] = '\0'; + if ((bcmerror = dhdsdio_membytes(bus, FALSE, + sdpcm_shared.assert_exp_addr, + (uint8 *)str, maxstrlen)) < 0) + goto done; + + str[maxstrlen - 1] = '\0'; + bcm_bprintf(&strbuf, " expr \"%s\"", str); + } + + if (sdpcm_shared.assert_file_addr != 0) { + str[0] = '\0'; + if ((bcmerror = dhdsdio_membytes(bus, FALSE, + sdpcm_shared.assert_file_addr, + (uint8 *)str, maxstrlen)) < 0) + goto done; + + str[maxstrlen - 1] = '\0'; + bcm_bprintf(&strbuf, " file \"%s\"", str); + } + + bcm_bprintf(&strbuf, " line %d ", sdpcm_shared.assert_line); + } + + if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) { + if ((bcmerror = dhdsdio_membytes(bus, FALSE, + sdpcm_shared.trap_addr, + (uint8*)&tr, sizeof(trap_t))) < 0) + goto done; + + bcm_bprintf(&strbuf, + "Dongle trap type 0x%x @ epc 0x%x, cpsr 0x%x, spsr 0x%x, sp 0x%x," + "lp 0x%x, rpc 0x%x Trap offset 0x%x, " + "r0 0x%x, r1 0x%x, r2 0x%x, r3 0x%x, " + "r4 0x%x, r5 0x%x, r6 0x%x, r7 0x%x\n\n", + ltoh32(tr.type), ltoh32(tr.epc), ltoh32(tr.cpsr), ltoh32(tr.spsr), + ltoh32(tr.r13), ltoh32(tr.r14), ltoh32(tr.pc), + ltoh32(sdpcm_shared.trap_addr), + ltoh32(tr.r0), ltoh32(tr.r1), ltoh32(tr.r2), ltoh32(tr.r3), + ltoh32(tr.r4), ltoh32(tr.r5), ltoh32(tr.r6), ltoh32(tr.r7)); + + addr = sdpcm_shared.console_addr + OFFSETOF(hndrte_cons_t, log); + if ((rv = dhdsdio_membytes(bus, FALSE, addr, + (uint8 *)&console_ptr, sizeof(console_ptr))) < 0) + goto printbuf; + + addr = sdpcm_shared.console_addr + OFFSETOF(hndrte_cons_t, log.buf_size); + if ((rv = dhdsdio_membytes(bus, FALSE, addr, + (uint8 *)&console_size, sizeof(console_size))) < 0) + goto printbuf; + + addr = sdpcm_shared.console_addr + OFFSETOF(hndrte_cons_t, log.idx); + if ((rv = dhdsdio_membytes(bus, FALSE, addr, + (uint8 *)&console_index, sizeof(console_index))) < 0) + goto printbuf; + + console_ptr = ltoh32(console_ptr); + console_size = ltoh32(console_size); + console_index = ltoh32(console_index); + + if (console_size > CONSOLE_BUFFER_MAX || + !(console_buffer = MALLOC(bus->dhd->osh, console_size))) + goto printbuf; + + if ((rv = dhdsdio_membytes(bus, FALSE, console_ptr, + (uint8 *)console_buffer, console_size)) < 0) + goto printbuf; + + for (i = 0, n = 0; i < console_size; i += n + 1) { + for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) { + ch = console_buffer[(console_index + i + n) % console_size]; + if (ch == '\n') + break; + line[n] = ch; + } + + + if (n > 0) { + if (line[n - 1] == '\r') + n--; + line[n] = 0; + /* Don't use DHD_ERROR macro since we print + * a lot of information quickly. The macro + * will truncate a lot of the printfs + */ + + if (dhd_msg_level & DHD_ERROR_VAL) { + printf("CONSOLE: %s\n", line); + DHD_BLOG(line, strlen(line) + 1); + } + } + } + } + } + +printbuf: + if (sdpcm_shared.flags & (SDPCM_SHARED_ASSERT | SDPCM_SHARED_TRAP)) { + DHD_ERROR(("%s: %s\n", __FUNCTION__, strbuf.origbuf)); + } + + +done: + if (mbuffer) + MFREE(bus->dhd->osh, mbuffer, msize); + if (str) + MFREE(bus->dhd->osh, str, maxstrlen); + if (console_buffer) + MFREE(bus->dhd->osh, console_buffer, console_size); + + return bcmerror; +} +#endif /* #ifdef DHD_DEBUG */ + + +int +dhdsdio_downloadvars(dhd_bus_t *bus, void *arg, int len) +{ + int bcmerror = BCME_OK; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + /* Basic sanity checks */ + if (bus->dhd->up) { + bcmerror = BCME_NOTDOWN; + goto err; + } + if (!len) { + bcmerror = BCME_BUFTOOSHORT; + goto err; + } + + /* Free the old ones and replace with passed variables */ + if (bus->vars) + MFREE(bus->dhd->osh, bus->vars, bus->varsz); + + bus->vars = MALLOC(bus->dhd->osh, len); + bus->varsz = bus->vars ? len : 0; + if (bus->vars == NULL) { + bcmerror = BCME_NOMEM; + goto err; + } + + /* Copy the passed variables, which should include the terminating double-null */ + bcopy(arg, bus->vars, bus->varsz); +err: + return bcmerror; +} + +#ifdef DHD_DEBUG + +#define CC_PLL_CHIPCTRL_SERIAL_ENAB (1 << 24) +static int +dhd_serialconsole(dhd_bus_t *bus, bool set, bool enable, int *bcmerror) +{ + int int_val; + uint32 addr, data; + + + addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_addr); + data = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol_data); + *bcmerror = 0; + + bcmsdh_reg_write(bus->sdh, addr, 4, 1); + if (bcmsdh_regfail(bus->sdh)) { + *bcmerror = BCME_SDIO_ERROR; + return -1; + } + int_val = bcmsdh_reg_read(bus->sdh, data, 4); + if (bcmsdh_regfail(bus->sdh)) { + *bcmerror = BCME_SDIO_ERROR; + return -1; + } + if (!set) + return (int_val & CC_PLL_CHIPCTRL_SERIAL_ENAB); + if (enable) + int_val |= CC_PLL_CHIPCTRL_SERIAL_ENAB; + else + int_val &= ~CC_PLL_CHIPCTRL_SERIAL_ENAB; + bcmsdh_reg_write(bus->sdh, data, 4, int_val); + if (bcmsdh_regfail(bus->sdh)) { + *bcmerror = BCME_SDIO_ERROR; + return -1; + } + if (bus->sih->chip == BCM4330_CHIP_ID) { + uint32 chipcontrol; + addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, chipcontrol); + chipcontrol = bcmsdh_reg_read(bus->sdh, addr, 4); + chipcontrol &= ~0x8; + if (enable) { + chipcontrol |= 0x8; + chipcontrol &= ~0x3; + } + bcmsdh_reg_write(bus->sdh, addr, 4, chipcontrol); + } + + return (int_val & CC_PLL_CHIPCTRL_SERIAL_ENAB); +} +#endif + +static int +dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, uint32 actionid, const char *name, + void *params, int plen, void *arg, int len, int val_size) +{ + int bcmerror = 0; + int32 int_val = 0; + bool bool_val = 0; + + DHD_TRACE(("%s: Enter, action %d name %s params %p plen %d arg %p len %d val_size %d\n", + __FUNCTION__, actionid, name, params, plen, arg, len, val_size)); + + if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0) + goto exit; + + if (plen >= (int)sizeof(int_val)) + bcopy(params, &int_val, sizeof(int_val)); + + bool_val = (int_val != 0) ? TRUE : FALSE; + + + /* Some ioctls use the bus */ + dhd_os_sdlock(bus->dhd); + + /* Check if dongle is in reset. If so, only allow DEVRESET iovars */ + if (bus->dhd->dongle_reset && !(actionid == IOV_SVAL(IOV_DEVRESET) || + actionid == IOV_GVAL(IOV_DEVRESET))) { + bcmerror = BCME_NOTREADY; + goto exit; + } + + /* Handle sleep stuff before any clock mucking */ + if (vi->varid == IOV_SLEEP) { + if (IOV_ISSET(actionid)) { + bcmerror = dhdsdio_bussleep(bus, bool_val); + } else { + int_val = (int32)bus->sleeping; + bcopy(&int_val, arg, val_size); + } + goto exit; + } + + /* Request clock to allow SDIO accesses */ + if (!bus->dhd->dongle_reset) { + BUS_WAKE(bus); + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + } + + switch (actionid) { + case IOV_GVAL(IOV_INTR): + int_val = (int32)bus->intr; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_INTR): + bus->intr = bool_val; + bus->intdis = FALSE; + if (bus->dhd->up) { + if (bus->intr) { + DHD_INTR(("%s: enable SDIO device interrupts\n", __FUNCTION__)); + bcmsdh_intr_enable(bus->sdh); + } else { + DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__)); + bcmsdh_intr_disable(bus->sdh); + } + } + break; + + case IOV_GVAL(IOV_POLLRATE): + int_val = (int32)bus->pollrate; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_POLLRATE): + bus->pollrate = (uint)int_val; + bus->poll = (bus->pollrate != 0); + break; + + case IOV_GVAL(IOV_IDLETIME): + int_val = bus->idletime; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_IDLETIME): + if ((int_val < 0) && (int_val != DHD_IDLE_IMMEDIATE)) { + bcmerror = BCME_BADARG; + } else { + bus->idletime = int_val; + } + break; + + case IOV_GVAL(IOV_IDLECLOCK): + int_val = (int32)bus->idleclock; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_IDLECLOCK): + bus->idleclock = int_val; + break; + + case IOV_GVAL(IOV_SD1IDLE): + int_val = (int32)sd1idle; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_SD1IDLE): + sd1idle = bool_val; + break; + + + case IOV_SVAL(IOV_MEMBYTES): + case IOV_GVAL(IOV_MEMBYTES): + { + uint32 address; + uint size, dsize; + uint8 *data; + + bool set = (actionid == IOV_SVAL(IOV_MEMBYTES)); + + ASSERT(plen >= 2*sizeof(int)); + + address = (uint32)int_val; + bcopy((char *)params + sizeof(int_val), &int_val, sizeof(int_val)); + size = (uint)int_val; + + /* Do some validation */ + dsize = set ? plen - (2 * sizeof(int)) : len; + if (dsize < size) { + DHD_ERROR(("%s: error on %s membytes, addr 0x%08x size %d dsize %d\n", + __FUNCTION__, (set ? "set" : "get"), address, size, dsize)); + bcmerror = BCME_BADARG; + break; + } + + DHD_INFO(("%s: Request to %s %d bytes at address 0x%08x\n", __FUNCTION__, + (set ? "write" : "read"), size, address)); + + /* If we know about SOCRAM, check for a fit */ + if ((bus->orig_ramsize) && + ((address > bus->orig_ramsize) || (address + size > bus->orig_ramsize))) + { + uint8 enable, protect; + si_socdevram(bus->sih, FALSE, &enable, &protect); + if (!enable || protect) { + DHD_ERROR(("%s: ramsize 0x%08x doesn't have %d bytes at 0x%08x\n", + __FUNCTION__, bus->orig_ramsize, size, address)); + DHD_ERROR(("%s: socram enable %d, protect %d\n", + __FUNCTION__, enable, protect)); + bcmerror = BCME_BADARG; + break; + } + if (enable && (bus->sih->chip == BCM4330_CHIP_ID)) { + uint32 devramsize = si_socdevram_size(bus->sih); + if ((address < SOCDEVRAM_4330_ARM_ADDR) || + (address + size > (SOCDEVRAM_4330_ARM_ADDR + devramsize))) { + DHD_ERROR(("%s: bad address 0x%08x, size 0x%08x\n", + __FUNCTION__, address, size)); + DHD_ERROR(("%s: socram range 0x%08x,size 0x%08x\n", + __FUNCTION__, SOCDEVRAM_4330_ARM_ADDR, devramsize)); + bcmerror = BCME_BADARG; + break; + } + /* move it such that address is real now */ + address -= SOCDEVRAM_4330_ARM_ADDR; + address += SOCDEVRAM_4330_BP_ADDR; + DHD_INFO(("%s: Request to %s %d bytes @ Mapped address 0x%08x\n", + __FUNCTION__, (set ? "write" : "read"), size, address)); + } + } + + /* Generate the actual data pointer */ + data = set ? (uint8*)params + 2 * sizeof(int): (uint8*)arg; + + /* Call to do the transfer */ + bcmerror = dhdsdio_membytes(bus, set, address, data, size); + + break; + } + + case IOV_GVAL(IOV_MEMSIZE): + int_val = (int32)bus->ramsize; + bcopy(&int_val, arg, val_size); + break; + + case IOV_GVAL(IOV_SDIOD_DRIVE): + int_val = (int32)dhd_sdiod_drive_strength; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_SDIOD_DRIVE): + dhd_sdiod_drive_strength = int_val; + si_sdiod_drive_strength_init(bus->sih, bus->dhd->osh, dhd_sdiod_drive_strength); + break; + + case IOV_SVAL(IOV_DOWNLOAD): + bcmerror = dhdsdio_download_state(bus, bool_val); + break; + + case IOV_SVAL(IOV_SOCRAM_STATE): + bcmerror = dhdsdio_download_state(bus, bool_val); + break; + + case IOV_SVAL(IOV_VARS): + bcmerror = dhdsdio_downloadvars(bus, arg, len); + break; + + case IOV_GVAL(IOV_READAHEAD): + int_val = (int32)dhd_readahead; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_READAHEAD): + if (bool_val && !dhd_readahead) + bus->nextlen = 0; + dhd_readahead = bool_val; + break; + + case IOV_GVAL(IOV_SDRXCHAIN): + int_val = (int32)bus->use_rxchain; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_SDRXCHAIN): + if (bool_val && !bus->sd_rxchain) + bcmerror = BCME_UNSUPPORTED; + else + bus->use_rxchain = bool_val; + break; + case IOV_GVAL(IOV_ALIGNCTL): + int_val = (int32)dhd_alignctl; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_ALIGNCTL): + dhd_alignctl = bool_val; + break; + + case IOV_GVAL(IOV_SDALIGN): + int_val = DHD_SDALIGN; + bcopy(&int_val, arg, val_size); + break; + +#ifdef DHD_DEBUG + case IOV_GVAL(IOV_VARS): + if (bus->varsz < (uint)len) + bcopy(bus->vars, arg, bus->varsz); + else + bcmerror = BCME_BUFTOOSHORT; + break; +#endif /* DHD_DEBUG */ + +#ifdef DHD_DEBUG + case IOV_GVAL(IOV_SDREG): + { + sdreg_t *sd_ptr; + uint32 addr, size; + + sd_ptr = (sdreg_t *)params; + + addr = (uintptr)bus->regs + sd_ptr->offset; + size = sd_ptr->func; + int_val = (int32)bcmsdh_reg_read(bus->sdh, addr, size); + if (bcmsdh_regfail(bus->sdh)) + bcmerror = BCME_SDIO_ERROR; + bcopy(&int_val, arg, sizeof(int32)); + break; + } + + case IOV_SVAL(IOV_SDREG): + { + sdreg_t *sd_ptr; + uint32 addr, size; + + sd_ptr = (sdreg_t *)params; + + addr = (uintptr)bus->regs + sd_ptr->offset; + size = sd_ptr->func; + bcmsdh_reg_write(bus->sdh, addr, size, sd_ptr->value); + if (bcmsdh_regfail(bus->sdh)) + bcmerror = BCME_SDIO_ERROR; + break; + } + + /* Same as above, but offset is not backplane (not SDIO core) */ + case IOV_GVAL(IOV_SBREG): + { + sdreg_t sdreg; + uint32 addr, size; + + bcopy(params, &sdreg, sizeof(sdreg)); + + addr = SI_ENUM_BASE + sdreg.offset; + size = sdreg.func; + int_val = (int32)bcmsdh_reg_read(bus->sdh, addr, size); + if (bcmsdh_regfail(bus->sdh)) + bcmerror = BCME_SDIO_ERROR; + bcopy(&int_val, arg, sizeof(int32)); + break; + } + + case IOV_SVAL(IOV_SBREG): + { + sdreg_t sdreg; + uint32 addr, size; + + bcopy(params, &sdreg, sizeof(sdreg)); + + addr = SI_ENUM_BASE + sdreg.offset; + size = sdreg.func; + bcmsdh_reg_write(bus->sdh, addr, size, sdreg.value); + if (bcmsdh_regfail(bus->sdh)) + bcmerror = BCME_SDIO_ERROR; + break; + } + + case IOV_GVAL(IOV_SDCIS): + { + *(char *)arg = 0; + + bcmstrcat(arg, "\nFunc 0\n"); + bcmsdh_cis_read(bus->sdh, 0x10, (uint8 *)arg + strlen(arg), SBSDIO_CIS_SIZE_LIMIT); + bcmstrcat(arg, "\nFunc 1\n"); + bcmsdh_cis_read(bus->sdh, 0x11, (uint8 *)arg + strlen(arg), SBSDIO_CIS_SIZE_LIMIT); + bcmstrcat(arg, "\nFunc 2\n"); + bcmsdh_cis_read(bus->sdh, 0x12, (uint8 *)arg + strlen(arg), SBSDIO_CIS_SIZE_LIMIT); + break; + } + + case IOV_GVAL(IOV_FORCEEVEN): + int_val = (int32)forcealign; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_FORCEEVEN): + forcealign = bool_val; + break; + + case IOV_GVAL(IOV_TXBOUND): + int_val = (int32)dhd_txbound; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_TXBOUND): + dhd_txbound = (uint)int_val; + break; + + case IOV_GVAL(IOV_RXBOUND): + int_val = (int32)dhd_rxbound; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_RXBOUND): + dhd_rxbound = (uint)int_val; + break; + + case IOV_GVAL(IOV_TXMINMAX): + int_val = (int32)dhd_txminmax; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_TXMINMAX): + dhd_txminmax = (uint)int_val; + break; + + case IOV_GVAL(IOV_SERIALCONS): + int_val = dhd_serialconsole(bus, FALSE, 0, &bcmerror); + if (bcmerror != 0) + break; + + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_SERIALCONS): + dhd_serialconsole(bus, TRUE, bool_val, &bcmerror); + break; + + + +#endif /* DHD_DEBUG */ + + +#ifdef SDTEST + case IOV_GVAL(IOV_EXTLOOP): + int_val = (int32)bus->ext_loop; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_EXTLOOP): + bus->ext_loop = bool_val; + break; + + case IOV_GVAL(IOV_PKTGEN): + bcmerror = dhdsdio_pktgen_get(bus, arg); + break; + + case IOV_SVAL(IOV_PKTGEN): + bcmerror = dhdsdio_pktgen_set(bus, arg); + break; +#endif /* SDTEST */ + + + case IOV_GVAL(IOV_DONGLEISOLATION): + int_val = bus->dhd->dongle_isolation; + bcopy(&int_val, arg, val_size); + break; + + case IOV_SVAL(IOV_DONGLEISOLATION): + bus->dhd->dongle_isolation = bool_val; + break; + + case IOV_SVAL(IOV_DEVRESET): + DHD_TRACE(("%s: Called set IOV_DEVRESET=%d dongle_reset=%d busstate=%d\n", + __FUNCTION__, bool_val, bus->dhd->dongle_reset, + bus->dhd->busstate)); + + ASSERT(bus->dhd->osh); + /* ASSERT(bus->cl_devid); */ + + dhd_bus_devreset(bus->dhd, (uint8)bool_val); + + break; +#ifdef SOFTAP + case IOV_GVAL(IOV_FWPATH): + { + uint32 fw_path_len; + + fw_path_len = strlen(bus->fw_path); + DHD_INFO(("[softap] get fwpath, l=%d\n", len)); + + if (fw_path_len > len-1) { + bcmerror = BCME_BUFTOOSHORT; + break; + } + + if (fw_path_len) { + bcopy(bus->fw_path, arg, fw_path_len); + ((uchar*)arg)[fw_path_len] = 0; + } + break; + } + + case IOV_SVAL(IOV_FWPATH): + DHD_INFO(("[softap] set fwpath, idx=%d\n", int_val)); + + switch (int_val) { + case 1: + bus->fw_path = fw_path; /* ordinary one */ + break; + case 2: + bus->fw_path = fw_path2; + break; + default: + bcmerror = BCME_BADARG; + break; + } + + DHD_INFO(("[softap] new fw path: %s\n", (bus->fw_path[0] ? bus->fw_path : "NULL"))); + break; + +#endif /* SOFTAP */ + case IOV_GVAL(IOV_DEVRESET): + DHD_TRACE(("%s: Called get IOV_DEVRESET\n", __FUNCTION__)); + + /* Get its status */ + int_val = (bool) bus->dhd->dongle_reset; + bcopy(&int_val, arg, val_size); + + break; + + default: + bcmerror = BCME_UNSUPPORTED; + break; + } + +exit: + if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { + bus->activity = FALSE; + dhdsdio_clkctl(bus, CLK_NONE, TRUE); + } + + dhd_os_sdunlock(bus->dhd); + + if (actionid == IOV_SVAL(IOV_DEVRESET) && bool_val == FALSE) + dhd_preinit_ioctls((dhd_pub_t *) bus->dhd); + + return bcmerror; +} + +static int +dhdsdio_write_vars(dhd_bus_t *bus) +{ + int bcmerror = 0; + uint32 varsize; + uint32 varaddr; + uint8 *vbuffer; + uint32 varsizew; +#ifdef DHD_DEBUG + uint8 *nvram_ularray; +#endif /* DHD_DEBUG */ + + /* Even if there are no vars are to be written, we still need to set the ramsize. */ + varsize = bus->varsz ? ROUNDUP(bus->varsz, 4) : 0; + varaddr = (bus->ramsize - 4) - varsize; + + if (bus->vars) { + if ((bus->sih->buscoretype == SDIOD_CORE_ID) && (bus->sdpcmrev == 7)) { + if (((varaddr & 0x3C) == 0x3C) && (varsize > 4)) { + DHD_ERROR(("PR85623WAR in place\n")); + varsize += 4; + varaddr -= 4; + } + } + + vbuffer = (uint8 *)MALLOC(bus->dhd->osh, varsize); + if (!vbuffer) + return BCME_NOMEM; + + bzero(vbuffer, varsize); + bcopy(bus->vars, vbuffer, bus->varsz); + + /* Write the vars list */ + bcmerror = dhdsdio_membytes(bus, TRUE, varaddr, vbuffer, varsize); +#ifdef DHD_DEBUG + /* Verify NVRAM bytes */ + DHD_INFO(("Compare NVRAM dl & ul; varsize=%d\n", varsize)); + nvram_ularray = (uint8*)MALLOC(bus->dhd->osh, varsize); + if (!nvram_ularray) + return BCME_NOMEM; + + /* Upload image to verify downloaded contents. */ + memset(nvram_ularray, 0xaa, varsize); + + /* Read the vars list to temp buffer for comparison */ + bcmerror = dhdsdio_membytes(bus, FALSE, varaddr, nvram_ularray, varsize); + if (bcmerror) { + DHD_ERROR(("%s: error %d on reading %d nvram bytes at 0x%08x\n", + __FUNCTION__, bcmerror, varsize, varaddr)); + } + /* Compare the org NVRAM with the one read from RAM */ + if (memcmp(vbuffer, nvram_ularray, varsize)) { + DHD_ERROR(("%s: Downloaded NVRAM image is corrupted.\n", __FUNCTION__)); + } else + DHD_ERROR(("%s: Download, Upload and compare of NVRAM succeeded.\n", + __FUNCTION__)); + + MFREE(bus->dhd->osh, nvram_ularray, varsize); +#endif /* DHD_DEBUG */ + + MFREE(bus->dhd->osh, vbuffer, varsize); + } + + /* adjust to the user specified RAM */ + DHD_INFO(("Physical memory size: %d, usable memory size: %d\n", + bus->orig_ramsize, bus->ramsize)); + DHD_INFO(("Vars are at %d, orig varsize is %d\n", + varaddr, varsize)); + varsize = ((bus->orig_ramsize - 4) - varaddr); + + /* + * Determine the length token: + * Varsize, converted to words, in lower 16-bits, checksum in upper 16-bits. + */ + if (bcmerror) { + varsizew = 0; + } else { + varsizew = varsize / 4; + varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF); + varsizew = htol32(varsizew); + } + + DHD_INFO(("New varsize is %d, length token=0x%08x\n", varsize, varsizew)); + + /* Write the length token to the last word */ + bcmerror = dhdsdio_membytes(bus, TRUE, (bus->orig_ramsize - 4), + (uint8*)&varsizew, 4); + + return bcmerror; +} + +static int +dhdsdio_download_state(dhd_bus_t *bus, bool enter) +{ + uint retries; + int bcmerror = 0; + + if (!bus->sih) + return BCME_ERROR; + + /* To enter download state, disable ARM and reset SOCRAM. + * To exit download state, simply reset ARM (default is RAM boot). + */ + if (enter) { + bus->alp_only = TRUE; + + if (!(si_setcore(bus->sih, ARM7S_CORE_ID, 0)) && + !(si_setcore(bus->sih, ARMCM3_CORE_ID, 0))) { + DHD_ERROR(("%s: Failed to find ARM core!\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + goto fail; + } + + si_core_disable(bus->sih, 0); + if (bcmsdh_regfail(bus->sdh)) { + bcmerror = BCME_SDIO_ERROR; + goto fail; + } + + if (!(si_setcore(bus->sih, SOCRAM_CORE_ID, 0))) { + DHD_ERROR(("%s: Failed to find SOCRAM core!\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + goto fail; + } + + si_core_reset(bus->sih, 0, 0); + if (bcmsdh_regfail(bus->sdh)) { + DHD_ERROR(("%s: Failure trying reset SOCRAM core?\n", __FUNCTION__)); + bcmerror = BCME_SDIO_ERROR; + goto fail; + } + + /* Clear the top bit of memory */ + if (bus->ramsize) { + uint32 zeros = 0; + if (dhdsdio_membytes(bus, TRUE, bus->ramsize - 4, (uint8*)&zeros, 4) < 0) { + bcmerror = BCME_SDIO_ERROR; + goto fail; + } + } + } else { + if (!(si_setcore(bus->sih, SOCRAM_CORE_ID, 0))) { + DHD_ERROR(("%s: Failed to find SOCRAM core!\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + goto fail; + } + + if (!si_iscoreup(bus->sih)) { + DHD_ERROR(("%s: SOCRAM core is down after reset?\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + goto fail; + } + + if ((bcmerror = dhdsdio_write_vars(bus))) { + DHD_ERROR(("%s: could not write vars to RAM\n", __FUNCTION__)); + goto fail; + } + + if (!si_setcore(bus->sih, PCMCIA_CORE_ID, 0) && + !si_setcore(bus->sih, SDIOD_CORE_ID, 0)) { + DHD_ERROR(("%s: Can't change back to SDIO core?\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + goto fail; + } + W_SDREG(0xFFFFFFFF, &bus->regs->intstatus, retries); + + + if (!(si_setcore(bus->sih, ARM7S_CORE_ID, 0)) && + !(si_setcore(bus->sih, ARMCM3_CORE_ID, 0))) { + DHD_ERROR(("%s: Failed to find ARM core!\n", __FUNCTION__)); + bcmerror = BCME_ERROR; + goto fail; + } + + si_core_reset(bus->sih, 0, 0); + if (bcmsdh_regfail(bus->sdh)) { + DHD_ERROR(("%s: Failure trying to reset ARM core?\n", __FUNCTION__)); + bcmerror = BCME_SDIO_ERROR; + goto fail; + } + + /* Allow HT Clock now that the ARM is running. */ + bus->alp_only = FALSE; + + bus->dhd->busstate = DHD_BUS_LOAD; + } + +fail: + /* Always return to SDIOD core */ + if (!si_setcore(bus->sih, PCMCIA_CORE_ID, 0)) + si_setcore(bus->sih, SDIOD_CORE_ID, 0); + + return bcmerror; +} + +int +dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name, + void *params, int plen, void *arg, int len, bool set) +{ + dhd_bus_t *bus = dhdp->bus; + const bcm_iovar_t *vi = NULL; + int bcmerror = 0; + int val_size; + uint32 actionid; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + ASSERT(name); + ASSERT(len >= 0); + + /* Get MUST have return space */ + ASSERT(set || (arg && len)); + + /* Set does NOT take qualifiers */ + ASSERT(!set || (!params && !plen)); + + /* Look up var locally; if not found pass to host driver */ + if ((vi = bcm_iovar_lookup(dhdsdio_iovars, name)) == NULL) { + dhd_os_sdlock(bus->dhd); + + BUS_WAKE(bus); + + /* Turn on clock in case SD command needs backplane */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + bcmerror = bcmsdh_iovar_op(bus->sdh, name, params, plen, arg, len, set); + + /* Check for bus configuration changes of interest */ + + /* If it was divisor change, read the new one */ + if (set && strcmp(name, "sd_divisor") == 0) { + if (bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0, + &bus->sd_divisor, sizeof(int32), FALSE) != BCME_OK) { + bus->sd_divisor = -1; + DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, name)); + } else { + DHD_INFO(("%s: noted %s update, value now %d\n", + __FUNCTION__, name, bus->sd_divisor)); + } + } + /* If it was a mode change, read the new one */ + if (set && strcmp(name, "sd_mode") == 0) { + if (bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0, + &bus->sd_mode, sizeof(int32), FALSE) != BCME_OK) { + bus->sd_mode = -1; + DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, name)); + } else { + DHD_INFO(("%s: noted %s update, value now %d\n", + __FUNCTION__, name, bus->sd_mode)); + } + } + /* Similar check for blocksize change */ + if (set && strcmp(name, "sd_blocksize") == 0) { + int32 fnum = 2; + if (bcmsdh_iovar_op(bus->sdh, "sd_blocksize", &fnum, sizeof(int32), + &bus->blocksize, sizeof(int32), FALSE) != BCME_OK) { + bus->blocksize = 0; + DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_blocksize")); + } else { + DHD_INFO(("%s: noted %s update, value now %d\n", + __FUNCTION__, "sd_blocksize", bus->blocksize)); + } + } + bus->roundup = MIN(max_roundup, bus->blocksize); + + if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { + bus->activity = FALSE; + dhdsdio_clkctl(bus, CLK_NONE, TRUE); + } + + dhd_os_sdunlock(bus->dhd); + goto exit; + } + + DHD_CTL(("%s: %s %s, len %d plen %d\n", __FUNCTION__, + name, (set ? "set" : "get"), len, plen)); + + /* set up 'params' pointer in case this is a set command so that + * the convenience int and bool code can be common to set and get + */ + if (params == NULL) { + params = arg; + plen = len; + } + + if (vi->type == IOVT_VOID) + val_size = 0; + else if (vi->type == IOVT_BUFFER) + val_size = len; + else + /* all other types are integer sized */ + val_size = sizeof(int); + + actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid); + bcmerror = dhdsdio_doiovar(bus, vi, actionid, name, params, plen, arg, len, val_size); + +exit: + return bcmerror; +} + +void +dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex) +{ + osl_t *osh; + uint32 local_hostintmask; + uint8 saveclk; + uint retries; + int err; + if (!bus->dhd) + return; + + osh = bus->dhd->osh; + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + bcmsdh_waitlockfree(NULL); + + if (enforce_mutex) + dhd_os_sdlock(bus->dhd); + + BUS_WAKE(bus); + + /* Change our idea of bus state */ + bus->dhd->busstate = DHD_BUS_DOWN; + + /* Enable clock for device interrupts */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + /* Disable and clear interrupts at the chip level also */ + W_SDREG(0, &bus->regs->hostintmask, retries); + local_hostintmask = bus->hostintmask; + bus->hostintmask = 0; + + /* Force clocks on backplane to be sure F2 interrupt propagates */ + saveclk = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); + if (!err) { + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, + (saveclk | SBSDIO_FORCE_HT), &err); + } + if (err) { + DHD_ERROR(("%s: Failed to force clock for F2: err %d\n", __FUNCTION__, err)); + } + + /* Turn off the bus (F2), free any pending packets */ + DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__)); + bcmsdh_intr_disable(bus->sdh); + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, SDIO_FUNC_ENABLE_1, NULL); + + /* Clear any pending interrupts now that F2 is disabled */ + W_SDREG(local_hostintmask, &bus->regs->intstatus, retries); + + /* Turn off the backplane clock (only) */ + dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); + + /* Clear the data packet queues */ + pktq_flush(osh, &bus->txq, TRUE, NULL, 0); + + /* Clear any held glomming stuff */ + if (bus->glomd) + PKTFREE(osh, bus->glomd, FALSE); + + if (bus->glom) + PKTFREE(osh, bus->glom, FALSE); + + bus->glom = bus->glomd = NULL; + + /* Clear rx control and wake any waiters */ + bus->rxlen = 0; + dhd_os_ioctl_resp_wake(bus->dhd); + + /* Reset some F2 state stuff */ + bus->rxskip = FALSE; + bus->tx_seq = bus->rx_seq = 0; + + /* Set to a safe default. It gets updated when we + * receive a packet from the fw but when we reset, + * we need a safe default to be able to send the + * initial mac address. + */ + bus->tx_max = 4; + + if (enforce_mutex) + dhd_os_sdunlock(bus->dhd); +} + + +int +dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex) +{ + dhd_bus_t *bus = dhdp->bus; + dhd_timeout_t tmo; + uint retries = 0; + uint8 ready, enable; + int err, ret = 0; + uint8 saveclk; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + ASSERT(bus->dhd); + if (!bus->dhd) + return 0; + + if (enforce_mutex) + dhd_os_sdlock(bus->dhd); + + /* Make sure backplane clock is on, needed to generate F2 interrupt */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + if (bus->clkstate != CLK_AVAIL) { + DHD_ERROR(("%s: clock state is wrong. state = %d\n", __FUNCTION__, bus->clkstate)); + goto exit; + } + + + /* Force clocks on backplane to be sure F2 interrupt propagates */ + saveclk = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); + if (!err) { + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, + (saveclk | SBSDIO_FORCE_HT), &err); + } + if (err) { + DHD_ERROR(("%s: Failed to force clock for F2: err %d\n", __FUNCTION__, err)); + goto exit; + } + + /* Enable function 2 (frame transfers) */ + W_SDREG((SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT), + &bus->regs->tosbmailboxdata, retries); + enable = (SDIO_FUNC_ENABLE_1 | SDIO_FUNC_ENABLE_2); + + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, enable, NULL); + + /* Give the dongle some time to do its thing and set IOR2 */ + dhd_timeout_start(&tmo, DHD_WAIT_F2RDY * 1000); + + ready = 0; + do { + ready = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IORDY, NULL); + } while (ready != enable && !dhd_timeout_expired(&tmo)); + + DHD_INFO(("%s: enable 0x%02x, ready 0x%02x (waited %uus)\n", + __FUNCTION__, enable, ready, tmo.elapsed)); + + + /* If F2 successfully enabled, set core and enable interrupts */ + if (ready == enable) { + /* Make sure we're talking to the core. */ + if (!(bus->regs = si_setcore(bus->sih, PCMCIA_CORE_ID, 0))) + bus->regs = si_setcore(bus->sih, SDIOD_CORE_ID, 0); + ASSERT(bus->regs != NULL); + + /* Set up the interrupt mask and enable interrupts */ + bus->hostintmask = HOSTINTMASK; + /* corerev 4 could use the newer interrupt logic to detect the frames */ + if ((bus->sih->buscoretype == SDIOD_CORE_ID) && (bus->sdpcmrev == 4) && + (bus->rxint_mode != SDIO_DEVICE_HMB_RXINT)) { + bus->hostintmask &= ~I_HMB_FRAME_IND; + bus->hostintmask |= I_XMTDATA_AVAIL; + } + W_SDREG(bus->hostintmask, &bus->regs->hostintmask, retries); + + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_WATERMARK, (uint8)watermark, &err); + + /* Set bus state according to enable result */ + dhdp->busstate = DHD_BUS_DATA; + + /* bcmsdh_intr_unmask(bus->sdh); */ + + bus->intdis = FALSE; + if (bus->intr) { + DHD_INTR(("%s: enable SDIO device interrupts\n", __FUNCTION__)); + bcmsdh_intr_enable(bus->sdh); + } else { + DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__)); + bcmsdh_intr_disable(bus->sdh); + } + + } + + + else { + /* Disable F2 again */ + enable = SDIO_FUNC_ENABLE_1; + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, enable, NULL); + } + + /* Restore previous clock setting */ + bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, saveclk, &err); + + + /* If we didn't come up, turn off backplane clock */ + if (dhdp->busstate != DHD_BUS_DATA) + dhdsdio_clkctl(bus, CLK_NONE, FALSE); + +exit: + if (enforce_mutex) + dhd_os_sdunlock(bus->dhd); + + return ret; +} + +static void +dhdsdio_rxfail(dhd_bus_t *bus, bool abort, bool rtx) +{ + bcmsdh_info_t *sdh = bus->sdh; + sdpcmd_regs_t *regs = bus->regs; + uint retries = 0; + uint16 lastrbc; + uint8 hi, lo; + int err; + + DHD_ERROR(("%s: %sterminate frame%s\n", __FUNCTION__, + (abort ? "abort command, " : ""), (rtx ? ", send NAK" : ""))); + + if (abort) { + bcmsdh_abort(sdh, SDIO_FUNC_2); + } + + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, SFC_RF_TERM, &err); + bus->f1regdata++; + + /* Wait until the packet has been flushed (device/FIFO stable) */ + for (lastrbc = retries = 0xffff; retries > 0; retries--) { + hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_RFRAMEBCHI, NULL); + lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_RFRAMEBCLO, NULL); + bus->f1regdata += 2; + + if ((hi == 0) && (lo == 0)) + break; + + if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) { + DHD_ERROR(("%s: count growing: last 0x%04x now 0x%04x\n", + __FUNCTION__, lastrbc, ((hi << 8) + lo))); + } + lastrbc = (hi << 8) + lo; + } + + if (!retries) { + DHD_ERROR(("%s: count never zeroed: last 0x%04x\n", __FUNCTION__, lastrbc)); + } else { + DHD_INFO(("%s: flush took %d iterations\n", __FUNCTION__, (0xffff - retries))); + } + + if (rtx) { + bus->rxrtx++; + W_SDREG(SMB_NAK, ®s->tosbmailbox, retries); + bus->f1regdata++; + if (retries <= retry_limit) { + bus->rxskip = TRUE; + } + } + + /* Clear partial in any case */ + bus->nextlen = 0; + + /* If we can't reach the device, signal failure */ + if (err || bcmsdh_regfail(sdh)) + bus->dhd->busstate = DHD_BUS_DOWN; +} + +static void +dhdsdio_read_control(dhd_bus_t *bus, uint8 *hdr, uint len, uint doff) +{ + bcmsdh_info_t *sdh = bus->sdh; + uint rdlen, pad; + + int sdret; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + /* Control data already received in aligned rxctl */ + if ((bus->bus == SPI_BUS) && (!bus->usebufpool)) + goto gotpkt; + + ASSERT(bus->rxbuf); + /* Set rxctl for frame (w/optional alignment) */ + bus->rxctl = bus->rxbuf; + if (dhd_alignctl) { + bus->rxctl += firstread; + if ((pad = ((uintptr)bus->rxctl % DHD_SDALIGN))) + bus->rxctl += (DHD_SDALIGN - pad); + bus->rxctl -= firstread; + } + ASSERT(bus->rxctl >= bus->rxbuf); + + /* Copy the already-read portion over */ + bcopy(hdr, bus->rxctl, firstread); + if (len <= firstread) + goto gotpkt; + + /* Copy the full data pkt in gSPI case and process ioctl. */ + if (bus->bus == SPI_BUS) { + bcopy(hdr, bus->rxctl, len); + goto gotpkt; + } + + /* Raise rdlen to next SDIO block to avoid tail command */ + rdlen = len - firstread; + if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) { + pad = bus->blocksize - (rdlen % bus->blocksize); + if ((pad <= bus->roundup) && (pad < bus->blocksize) && + ((len + pad) < bus->dhd->maxctl)) + rdlen += pad; + } else if (rdlen % DHD_SDALIGN) { + rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN); + } + + /* Satisfy length-alignment requirements */ + if (forcealign && (rdlen & (ALIGNMENT - 1))) + rdlen = ROUNDUP(rdlen, ALIGNMENT); + + /* Drop if the read is too big or it exceeds our maximum */ + if ((rdlen + firstread) > bus->dhd->maxctl) { + DHD_ERROR(("%s: %d-byte control read exceeds %d-byte buffer\n", + __FUNCTION__, rdlen, bus->dhd->maxctl)); + bus->dhd->rx_errors++; + dhdsdio_rxfail(bus, FALSE, FALSE); + goto done; + } + + if ((len - doff) > bus->dhd->maxctl) { + DHD_ERROR(("%s: %d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n", + __FUNCTION__, len, (len - doff), bus->dhd->maxctl)); + bus->dhd->rx_errors++; bus->rx_toolong++; + dhdsdio_rxfail(bus, FALSE, FALSE); + goto done; + } + + + /* Read remainder of frame body into the rxctl buffer */ + sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, + (bus->rxctl + firstread), rdlen, NULL, NULL, NULL); + bus->f2rxdata++; + ASSERT(sdret != BCME_PENDING); + + /* Control frame failures need retransmission */ + if (sdret < 0) { + DHD_ERROR(("%s: read %d control bytes failed: %d\n", __FUNCTION__, rdlen, sdret)); + bus->rxc_errors++; /* dhd.rx_ctlerrs is higher level */ + dhdsdio_rxfail(bus, TRUE, TRUE); + goto done; + } + +gotpkt: + +#ifdef DHD_DEBUG + if (DHD_BYTES_ON() && DHD_CTL_ON()) { + prhex("RxCtrl", bus->rxctl, len); + } +#endif + + /* Point to valid data and indicate its length */ + bus->rxctl += doff; + bus->rxlen = len - doff; + +done: + /* Awake any waiters */ + dhd_os_ioctl_resp_wake(bus->dhd); +} + +static uint8 +dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq) +{ + uint16 dlen, totlen; + uint8 *dptr, num = 0; + + uint16 sublen, check; + void *pfirst, *plast, *pnext, *save_pfirst; + osl_t *osh = bus->dhd->osh; + + int errcode; + uint8 chan, seq, doff, sfdoff; + uint8 txmax; + + int ifidx = 0; + bool usechain = bus->use_rxchain; + + /* If packets, issue read(s) and send up packet chain */ + /* Return sequence numbers consumed? */ + + DHD_TRACE(("dhdsdio_rxglom: start: glomd %p glom %p\n", bus->glomd, bus->glom)); + + /* If there's a descriptor, generate the packet chain */ + if (bus->glomd) { + dhd_os_sdlock_rxq(bus->dhd); + + pfirst = plast = pnext = NULL; + dlen = (uint16)PKTLEN(osh, bus->glomd); + dptr = PKTDATA(osh, bus->glomd); + if (!dlen || (dlen & 1)) { + DHD_ERROR(("%s: bad glomd len (%d), ignore descriptor\n", + __FUNCTION__, dlen)); + dlen = 0; + } + + for (totlen = num = 0; dlen; num++) { + /* Get (and move past) next length */ + sublen = ltoh16_ua(dptr); + dlen -= sizeof(uint16); + dptr += sizeof(uint16); + if ((sublen < SDPCM_HDRLEN) || + ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) { + DHD_ERROR(("%s: descriptor len %d bad: %d\n", + __FUNCTION__, num, sublen)); + pnext = NULL; + break; + } + if (sublen % DHD_SDALIGN) { + DHD_ERROR(("%s: sublen %d not a multiple of %d\n", + __FUNCTION__, sublen, DHD_SDALIGN)); + usechain = FALSE; + } + totlen += sublen; + + /* For last frame, adjust read len so total is a block multiple */ + if (!dlen) { + sublen += (ROUNDUP(totlen, bus->blocksize) - totlen); + totlen = ROUNDUP(totlen, bus->blocksize); + } + + /* Allocate/chain packet for next subframe */ + if ((pnext = PKTGET(osh, sublen + DHD_SDALIGN, FALSE)) == NULL) { + DHD_ERROR(("%s: PKTGET failed, num %d len %d\n", + __FUNCTION__, num, sublen)); + break; + } + ASSERT(!PKTLINK(pnext)); + if (!pfirst) { + ASSERT(!plast); + pfirst = plast = pnext; + } else { + ASSERT(plast); + PKTSETNEXT(osh, plast, pnext); + plast = pnext; + } + + /* Adhere to start alignment requirements */ + PKTALIGN(osh, pnext, sublen, DHD_SDALIGN); + } + + /* If all allocations succeeded, save packet chain in bus structure */ + if (pnext) { + DHD_GLOM(("%s: allocated %d-byte packet chain for %d subframes\n", + __FUNCTION__, totlen, num)); + if (DHD_GLOM_ON() && bus->nextlen) { + if (totlen != bus->nextlen) { + DHD_GLOM(("%s: glomdesc mismatch: nextlen %d glomdesc %d " + "rxseq %d\n", __FUNCTION__, bus->nextlen, + totlen, rxseq)); + } + } + bus->glom = pfirst; + pfirst = pnext = NULL; + } else { + if (pfirst) + PKTFREE(osh, pfirst, FALSE); + bus->glom = NULL; + num = 0; + } + + /* Done with descriptor packet */ + PKTFREE(osh, bus->glomd, FALSE); + bus->glomd = NULL; + bus->nextlen = 0; + + dhd_os_sdunlock_rxq(bus->dhd); + } + + /* Ok -- either we just generated a packet chain, or had one from before */ + if (bus->glom) { + if (DHD_GLOM_ON()) { + DHD_GLOM(("%s: attempt superframe read, packet chain:\n", __FUNCTION__)); + for (pnext = bus->glom; pnext; pnext = PKTNEXT(osh, pnext)) { + DHD_GLOM((" %p: %p len 0x%04x (%d)\n", + pnext, (uint8*)PKTDATA(osh, pnext), + PKTLEN(osh, pnext), PKTLEN(osh, pnext))); + } + } + + pfirst = bus->glom; + dlen = (uint16)pkttotlen(osh, pfirst); + + /* Do an SDIO read for the superframe. Configurable iovar to + * read directly into the chained packet, or allocate a large + * packet and and copy into the chain. + */ + if (usechain) { + errcode = dhd_bcmsdh_recv_buf(bus, + bcmsdh_cur_sbwad(bus->sdh), SDIO_FUNC_2, + F2SYNC, (uint8*)PKTDATA(osh, pfirst), + dlen, pfirst, NULL, NULL); + } else if (bus->dataptr) { + errcode = dhd_bcmsdh_recv_buf(bus, + bcmsdh_cur_sbwad(bus->sdh), SDIO_FUNC_2, + F2SYNC, bus->dataptr, + dlen, NULL, NULL, NULL); + sublen = (uint16)pktfrombuf(osh, pfirst, 0, dlen, bus->dataptr); + if (sublen != dlen) { + DHD_ERROR(("%s: FAILED TO COPY, dlen %d sublen %d\n", + __FUNCTION__, dlen, sublen)); + errcode = -1; + } + pnext = NULL; + } else { + DHD_ERROR(("COULDN'T ALLOC %d-BYTE GLOM, FORCE FAILURE\n", dlen)); + errcode = -1; + } + bus->f2rxdata++; + ASSERT(errcode != BCME_PENDING); + + /* On failure, kill the superframe, allow a couple retries */ + if (errcode < 0) { + DHD_ERROR(("%s: glom read of %d bytes failed: %d\n", + __FUNCTION__, dlen, errcode)); + bus->dhd->rx_errors++; + + if (bus->glomerr++ < 3) { + dhdsdio_rxfail(bus, TRUE, TRUE); + } else { + bus->glomerr = 0; + dhdsdio_rxfail(bus, TRUE, FALSE); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE(osh, bus->glom, FALSE); + dhd_os_sdunlock_rxq(bus->dhd); + bus->rxglomfail++; + bus->glom = NULL; + } + return 0; + } + +#ifdef DHD_DEBUG + if (DHD_GLOM_ON()) { + prhex("SUPERFRAME", PKTDATA(osh, pfirst), + MIN(PKTLEN(osh, pfirst), 48)); + } +#endif + + + /* Validate the superframe header */ + dptr = (uint8 *)PKTDATA(osh, pfirst); + sublen = ltoh16_ua(dptr); + check = ltoh16_ua(dptr + sizeof(uint16)); + + chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]); + seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]); + bus->nextlen = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; + if ((bus->nextlen << 4) > MAX_RX_DATASZ) { + DHD_INFO(("%s: got frame w/nextlen too large (%d) seq %d\n", + __FUNCTION__, bus->nextlen, seq)); + bus->nextlen = 0; + } + doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); + txmax = SDPCM_WINDOW_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); + + errcode = 0; + if ((uint16)~(sublen^check)) { + DHD_ERROR(("%s (superframe): HW hdr error: len/check 0x%04x/0x%04x\n", + __FUNCTION__, sublen, check)); + errcode = -1; + } else if (ROUNDUP(sublen, bus->blocksize) != dlen) { + DHD_ERROR(("%s (superframe): len 0x%04x, rounded 0x%04x, expect 0x%04x\n", + __FUNCTION__, sublen, ROUNDUP(sublen, bus->blocksize), dlen)); + errcode = -1; + } else if (SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]) != SDPCM_GLOM_CHANNEL) { + DHD_ERROR(("%s (superframe): bad channel %d\n", __FUNCTION__, + SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]))); + errcode = -1; + } else if (SDPCM_GLOMDESC(&dptr[SDPCM_FRAMETAG_LEN])) { + DHD_ERROR(("%s (superframe): got second descriptor?\n", __FUNCTION__)); + errcode = -1; + } else if ((doff < SDPCM_HDRLEN) || + (doff > (PKTLEN(osh, pfirst) - SDPCM_HDRLEN))) { + DHD_ERROR(("%s (superframe): Bad data offset %d: HW %d pkt %d min %d\n", + __FUNCTION__, doff, sublen, PKTLEN(osh, pfirst), SDPCM_HDRLEN)); + errcode = -1; + } + + /* Check sequence number of superframe SW header */ + if (rxseq != seq) { + DHD_INFO(("%s: (superframe) rx_seq %d, expected %d\n", + __FUNCTION__, seq, rxseq)); + bus->rx_badseq++; + rxseq = seq; + } + + /* Check window for sanity */ + if ((uint8)(txmax - bus->tx_seq) > 0x40) { + DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", + __FUNCTION__, txmax, bus->tx_seq)); + txmax = bus->tx_max; + } + bus->tx_max = txmax; + + /* Remove superframe header, remember offset */ + PKTPULL(osh, pfirst, doff); + sfdoff = doff; + + /* Validate all the subframe headers */ + for (num = 0, pnext = pfirst; pnext && !errcode; + num++, pnext = PKTNEXT(osh, pnext)) { + dptr = (uint8 *)PKTDATA(osh, pnext); + dlen = (uint16)PKTLEN(osh, pnext); + sublen = ltoh16_ua(dptr); + check = ltoh16_ua(dptr + sizeof(uint16)); + chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]); + doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); +#ifdef DHD_DEBUG + if (DHD_GLOM_ON()) { + prhex("subframe", dptr, 32); + } +#endif + + if ((uint16)~(sublen^check)) { + DHD_ERROR(("%s (subframe %d): HW hdr error: " + "len/check 0x%04x/0x%04x\n", + __FUNCTION__, num, sublen, check)); + errcode = -1; + } else if ((sublen > dlen) || (sublen < SDPCM_HDRLEN)) { + DHD_ERROR(("%s (subframe %d): length mismatch: " + "len 0x%04x, expect 0x%04x\n", + __FUNCTION__, num, sublen, dlen)); + errcode = -1; + } else if ((chan != SDPCM_DATA_CHANNEL) && + (chan != SDPCM_EVENT_CHANNEL)) { + DHD_ERROR(("%s (subframe %d): bad channel %d\n", + __FUNCTION__, num, chan)); + errcode = -1; + } else if ((doff < SDPCM_HDRLEN) || (doff > sublen)) { + DHD_ERROR(("%s (subframe %d): Bad data offset %d: HW %d min %d\n", + __FUNCTION__, num, doff, sublen, SDPCM_HDRLEN)); + errcode = -1; + } + } + + if (errcode) { + /* Terminate frame on error, request a couple retries */ + if (bus->glomerr++ < 3) { + /* Restore superframe header space */ + PKTPUSH(osh, pfirst, sfdoff); + dhdsdio_rxfail(bus, TRUE, TRUE); + } else { + bus->glomerr = 0; + dhdsdio_rxfail(bus, TRUE, FALSE); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE(osh, bus->glom, FALSE); + dhd_os_sdunlock_rxq(bus->dhd); + bus->rxglomfail++; + bus->glom = NULL; + } + bus->nextlen = 0; + return 0; + } + + /* Basic SD framing looks ok - process each packet (header) */ + save_pfirst = pfirst; + bus->glom = NULL; + plast = NULL; + + dhd_os_sdlock_rxq(bus->dhd); + for (num = 0; pfirst; rxseq++, pfirst = pnext) { + pnext = PKTNEXT(osh, pfirst); + PKTSETNEXT(osh, pfirst, NULL); + + dptr = (uint8 *)PKTDATA(osh, pfirst); + sublen = ltoh16_ua(dptr); + chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]); + seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]); + doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); + + DHD_GLOM(("%s: Get subframe %d, %p(%p/%d), sublen %d chan %d seq %d\n", + __FUNCTION__, num, pfirst, PKTDATA(osh, pfirst), + PKTLEN(osh, pfirst), sublen, chan, seq)); + + ASSERT((chan == SDPCM_DATA_CHANNEL) || (chan == SDPCM_EVENT_CHANNEL)); + + if (rxseq != seq) { + DHD_GLOM(("%s: rx_seq %d, expected %d\n", + __FUNCTION__, seq, rxseq)); + bus->rx_badseq++; + rxseq = seq; + } + +#ifdef DHD_DEBUG + if (DHD_BYTES_ON() && DHD_DATA_ON()) { + prhex("Rx Subframe Data", dptr, dlen); + } +#endif + + PKTSETLEN(osh, pfirst, sublen); + PKTPULL(osh, pfirst, doff); + + if (PKTLEN(osh, pfirst) == 0) { + PKTFREE(bus->dhd->osh, pfirst, FALSE); + if (plast) { + PKTSETNEXT(osh, plast, pnext); + } else { + ASSERT(save_pfirst == pfirst); + save_pfirst = pnext; + } + continue; + } else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pfirst) != 0) { + DHD_ERROR(("%s: rx protocol error\n", __FUNCTION__)); + bus->dhd->rx_errors++; + PKTFREE(osh, pfirst, FALSE); + if (plast) { + PKTSETNEXT(osh, plast, pnext); + } else { + ASSERT(save_pfirst == pfirst); + save_pfirst = pnext; + } + continue; + } + + /* this packet will go up, link back into chain and count it */ + PKTSETNEXT(osh, pfirst, pnext); + plast = pfirst; + num++; + +#ifdef DHD_DEBUG + if (DHD_GLOM_ON()) { + DHD_GLOM(("%s subframe %d to stack, %p(%p/%d) nxt/lnk %p/%p\n", + __FUNCTION__, num, pfirst, + PKTDATA(osh, pfirst), PKTLEN(osh, pfirst), + PKTNEXT(osh, pfirst), PKTLINK(pfirst))); + prhex("", (uint8 *)PKTDATA(osh, pfirst), + MIN(PKTLEN(osh, pfirst), 32)); + } +#endif /* DHD_DEBUG */ + } + dhd_os_sdunlock_rxq(bus->dhd); + if (num) { + dhd_os_sdunlock(bus->dhd); + dhd_rx_frame(bus->dhd, ifidx, save_pfirst, num, 0); + dhd_os_sdlock(bus->dhd); + } + + bus->rxglomframes++; + bus->rxglompkts += num; + } + return num; +} + +/* Return TRUE if there may be more frames to read */ +static uint +dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) +{ + osl_t *osh = bus->dhd->osh; + bcmsdh_info_t *sdh = bus->sdh; + + uint16 len, check; /* Extracted hardware header fields */ + uint8 chan, seq, doff; /* Extracted software header fields */ + uint8 fcbits; /* Extracted fcbits from software header */ + uint8 delta; + + void *pkt; /* Packet for event or data frames */ + uint16 pad; /* Number of pad bytes to read */ + uint16 rdlen; /* Total number of bytes to read */ + uint8 rxseq; /* Next sequence number to expect */ + uint rxleft = 0; /* Remaining number of frames allowed */ + int sdret; /* Return code from bcmsdh calls */ + uint8 txmax; /* Maximum tx sequence offered */ + bool len_consistent; /* Result of comparing readahead len and len from hw-hdr */ + uint8 *rxbuf; + int ifidx = 0; + uint rxcount = 0; /* Total frames read */ + +#if defined(DHD_DEBUG) || defined(SDTEST) + bool sdtest = FALSE; /* To limit message spew from test mode */ +#endif + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + ASSERT(maxframes); + +#ifdef SDTEST + /* Allow pktgen to override maxframes */ + if (bus->pktgen_count && (bus->pktgen_mode == DHD_PKTGEN_RECV)) { + maxframes = bus->pktgen_count; + sdtest = TRUE; + } +#endif + + /* Not finished unless we encounter no more frames indication */ + *finished = FALSE; + + + for (rxseq = bus->rx_seq, rxleft = maxframes; + !bus->rxskip && rxleft && bus->dhd->busstate != DHD_BUS_DOWN; + rxseq++, rxleft--) { + +#ifdef DHDTHREAD + /* tx more to improve rx performance */ + if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate && + pktq_mlen(&bus->txq, ~bus->flowcontrol) && DATAOK(bus)) { + dhdsdio_sendfromq(bus, dhd_txbound); + } +#endif /* DHDTHREAD */ + + /* Handle glomming separately */ + if (bus->glom || bus->glomd) { + uint8 cnt; + DHD_GLOM(("%s: calling rxglom: glomd %p, glom %p\n", + __FUNCTION__, bus->glomd, bus->glom)); + cnt = dhdsdio_rxglom(bus, rxseq); + DHD_GLOM(("%s: rxglom returned %d\n", __FUNCTION__, cnt)); + rxseq += cnt - 1; + rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1; + continue; + } + + /* Try doing single read if we can */ + if (dhd_readahead && bus->nextlen) { + uint16 nextlen = bus->nextlen; + bus->nextlen = 0; + + if (bus->bus == SPI_BUS) { + rdlen = len = nextlen; + } + else { + rdlen = len = nextlen << 4; + + /* Pad read to blocksize for efficiency */ + if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) { + pad = bus->blocksize - (rdlen % bus->blocksize); + if ((pad <= bus->roundup) && (pad < bus->blocksize) && + ((rdlen + pad + firstread) < MAX_RX_DATASZ)) + rdlen += pad; + } else if (rdlen % DHD_SDALIGN) { + rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN); + } + } + + /* We use bus->rxctl buffer in WinXP for initial control pkt receives. + * Later we use buffer-poll for data as well as control packets. + * This is required because dhd receives full frame in gSPI unlike SDIO. + * After the frame is received we have to distinguish whether it is data + * or non-data frame. + */ + /* Allocate a packet buffer */ + dhd_os_sdlock_rxq(bus->dhd); + if (!(pkt = PKTGET(osh, rdlen + DHD_SDALIGN, FALSE))) { + if (bus->bus == SPI_BUS) { + bus->usebufpool = FALSE; + bus->rxctl = bus->rxbuf; + if (dhd_alignctl) { + bus->rxctl += firstread; + if ((pad = ((uintptr)bus->rxctl % DHD_SDALIGN))) + bus->rxctl += (DHD_SDALIGN - pad); + bus->rxctl -= firstread; + } + ASSERT(bus->rxctl >= bus->rxbuf); + rxbuf = bus->rxctl; + /* Read the entire frame */ + sdret = dhd_bcmsdh_recv_buf(bus, + bcmsdh_cur_sbwad(sdh), + SDIO_FUNC_2, + F2SYNC, rxbuf, rdlen, + NULL, NULL, NULL); + bus->f2rxdata++; + ASSERT(sdret != BCME_PENDING); + + + /* Control frame failures need retransmission */ + if (sdret < 0) { + DHD_ERROR(("%s: read %d control bytes failed: %d\n", + __FUNCTION__, rdlen, sdret)); + /* dhd.rx_ctlerrs is higher level */ + bus->rxc_errors++; + dhd_os_sdunlock_rxq(bus->dhd); + dhdsdio_rxfail(bus, TRUE, + (bus->bus == SPI_BUS) ? FALSE : TRUE); + continue; + } + } else { + /* Give up on data, request rtx of events */ + DHD_ERROR(("%s (nextlen): PKTGET failed: len %d rdlen %d " + "expected rxseq %d\n", + __FUNCTION__, len, rdlen, rxseq)); + /* Just go try again w/normal header read */ + dhd_os_sdunlock_rxq(bus->dhd); + continue; + } + } else { + if (bus->bus == SPI_BUS) + bus->usebufpool = TRUE; + + ASSERT(!PKTLINK(pkt)); + PKTALIGN(osh, pkt, rdlen, DHD_SDALIGN); + rxbuf = (uint8 *)PKTDATA(osh, pkt); + /* Read the entire frame */ + sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), + SDIO_FUNC_2, + F2SYNC, rxbuf, rdlen, + pkt, NULL, NULL); + bus->f2rxdata++; + ASSERT(sdret != BCME_PENDING); + + if (sdret < 0) { + DHD_ERROR(("%s (nextlen): read %d bytes failed: %d\n", + __FUNCTION__, rdlen, sdret)); + PKTFREE(bus->dhd->osh, pkt, FALSE); + bus->dhd->rx_errors++; + dhd_os_sdunlock_rxq(bus->dhd); + /* Force retry w/normal header read. Don't attempt NAK for + * gSPI + */ + dhdsdio_rxfail(bus, TRUE, + (bus->bus == SPI_BUS) ? FALSE : TRUE); + continue; + } + } + dhd_os_sdunlock_rxq(bus->dhd); + + /* Now check the header */ + bcopy(rxbuf, bus->rxhdr, SDPCM_HDRLEN); + + /* Extract hardware header fields */ + len = ltoh16_ua(bus->rxhdr); + check = ltoh16_ua(bus->rxhdr + sizeof(uint16)); + + /* All zeros means readahead info was bad */ + if (!(len|check)) { + DHD_INFO(("%s (nextlen): read zeros in HW header???\n", + __FUNCTION__)); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE2(); + dhd_os_sdunlock_rxq(bus->dhd); + GSPI_PR55150_BAILOUT; + continue; + } + + /* Validate check bytes */ + if ((uint16)~(len^check)) { + DHD_ERROR(("%s (nextlen): HW hdr error: nextlen/len/check" + " 0x%04x/0x%04x/0x%04x\n", __FUNCTION__, nextlen, + len, check)); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE2(); + dhd_os_sdunlock_rxq(bus->dhd); + bus->rx_badhdr++; + dhdsdio_rxfail(bus, FALSE, FALSE); + GSPI_PR55150_BAILOUT; + continue; + } + + /* Validate frame length */ + if (len < SDPCM_HDRLEN) { + DHD_ERROR(("%s (nextlen): HW hdr length invalid: %d\n", + __FUNCTION__, len)); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE2(); + dhd_os_sdunlock_rxq(bus->dhd); + GSPI_PR55150_BAILOUT; + continue; + } + + /* Check for consistency with readahead info */ + len_consistent = (nextlen != (ROUNDUP(len, 16) >> 4)); + if (len_consistent) { + /* Mismatch, force retry w/normal header (may be >4K) */ + DHD_ERROR(("%s (nextlen): mismatch, nextlen %d len %d rnd %d; " + "expected rxseq %d\n", + __FUNCTION__, nextlen, len, ROUNDUP(len, 16), rxseq)); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE2(); + dhd_os_sdunlock_rxq(bus->dhd); + dhdsdio_rxfail(bus, TRUE, (bus->bus == SPI_BUS) ? FALSE : TRUE); + GSPI_PR55150_BAILOUT; + continue; + } + + + /* Extract software header fields */ + chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + + bus->nextlen = + bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; + if ((bus->nextlen << 4) > MAX_RX_DATASZ) { + DHD_INFO(("%s (nextlen): got frame w/nextlen too large" + " (%d), seq %d\n", __FUNCTION__, bus->nextlen, + seq)); + bus->nextlen = 0; + } + + bus->dhd->rx_readahead_cnt ++; + /* Handle Flow Control */ + fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + + delta = 0; + if (~bus->flowcontrol & fcbits) { + bus->fc_xoff++; + delta = 1; + } + if (bus->flowcontrol & ~fcbits) { + bus->fc_xon++; + delta = 1; + } + + if (delta) { + bus->fc_rcvd++; + bus->flowcontrol = fcbits; + } + + /* Check and update sequence number */ + if (rxseq != seq) { + DHD_INFO(("%s (nextlen): rx_seq %d, expected %d\n", + __FUNCTION__, seq, rxseq)); + bus->rx_badseq++; + rxseq = seq; + } + + /* Check window for sanity */ + if ((uint8)(txmax - bus->tx_seq) > 0x40) { + DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", + __FUNCTION__, txmax, bus->tx_seq)); + txmax = bus->tx_max; + } + bus->tx_max = txmax; + +#ifdef DHD_DEBUG + if (DHD_BYTES_ON() && DHD_DATA_ON()) { + prhex("Rx Data", rxbuf, len); + } else if (DHD_HDRS_ON()) { + prhex("RxHdr", bus->rxhdr, SDPCM_HDRLEN); + } +#endif + + if (chan == SDPCM_CONTROL_CHANNEL) { + if (bus->bus == SPI_BUS) { + dhdsdio_read_control(bus, rxbuf, len, doff); + if (bus->usebufpool) { + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE(bus->dhd->osh, pkt, FALSE); + dhd_os_sdunlock_rxq(bus->dhd); + } + continue; + } else { + DHD_ERROR(("%s (nextlen): readahead on control" + " packet %d?\n", __FUNCTION__, seq)); + /* Force retry w/normal header read */ + bus->nextlen = 0; + dhdsdio_rxfail(bus, FALSE, TRUE); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE2(); + dhd_os_sdunlock_rxq(bus->dhd); + continue; + } + } + + if ((bus->bus == SPI_BUS) && !bus->usebufpool) { + DHD_ERROR(("Received %d bytes on %d channel. Running out of " + "rx pktbuf's or not yet malloced.\n", len, chan)); + continue; + } + + /* Validate data offset */ + if ((doff < SDPCM_HDRLEN) || (doff > len)) { + DHD_ERROR(("%s (nextlen): bad data offset %d: HW len %d min %d\n", + __FUNCTION__, doff, len, SDPCM_HDRLEN)); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE2(); + dhd_os_sdunlock_rxq(bus->dhd); + ASSERT(0); + dhdsdio_rxfail(bus, FALSE, FALSE); + continue; + } + + /* All done with this one -- now deliver the packet */ + goto deliver; + } + /* gSPI frames should not be handled in fractions */ + if (bus->bus == SPI_BUS) { + break; + } + + /* Read frame header (hardware and software) */ + sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, + bus->rxhdr, firstread, NULL, NULL, NULL); + bus->f2rxhdrs++; + ASSERT(sdret != BCME_PENDING); + + if (sdret < 0) { + DHD_ERROR(("%s: RXHEADER FAILED: %d\n", __FUNCTION__, sdret)); + bus->rx_hdrfail++; + dhdsdio_rxfail(bus, TRUE, TRUE); + continue; + } + +#ifdef DHD_DEBUG + if (DHD_BYTES_ON() || DHD_HDRS_ON()) { + prhex("RxHdr", bus->rxhdr, SDPCM_HDRLEN); + } +#endif + + /* Extract hardware header fields */ + len = ltoh16_ua(bus->rxhdr); + check = ltoh16_ua(bus->rxhdr + sizeof(uint16)); + + /* All zeros means no more frames */ + if (!(len|check)) { + *finished = TRUE; + break; + } + + /* Validate check bytes */ + if ((uint16)~(len^check)) { + DHD_ERROR(("%s: HW hdr error: len/check 0x%04x/0x%04x\n", + __FUNCTION__, len, check)); + bus->rx_badhdr++; + dhdsdio_rxfail(bus, FALSE, FALSE); + continue; + } + + /* Validate frame length */ + if (len < SDPCM_HDRLEN) { + DHD_ERROR(("%s: HW hdr length invalid: %d\n", __FUNCTION__, len)); + continue; + } + + /* Extract software header fields */ + chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + + /* Validate data offset */ + if ((doff < SDPCM_HDRLEN) || (doff > len)) { + DHD_ERROR(("%s: Bad data offset %d: HW len %d, min %d seq %d\n", + __FUNCTION__, doff, len, SDPCM_HDRLEN, seq)); + bus->rx_badhdr++; + ASSERT(0); + dhdsdio_rxfail(bus, FALSE, FALSE); + continue; + } + + /* Save the readahead length if there is one */ + bus->nextlen = bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; + if ((bus->nextlen << 4) > MAX_RX_DATASZ) { + DHD_INFO(("%s (nextlen): got frame w/nextlen too large (%d), seq %d\n", + __FUNCTION__, bus->nextlen, seq)); + bus->nextlen = 0; + } + + /* Handle Flow Control */ + fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); + + delta = 0; + if (~bus->flowcontrol & fcbits) { + bus->fc_xoff++; + delta = 1; + } + if (bus->flowcontrol & ~fcbits) { + bus->fc_xon++; + delta = 1; + } + + if (delta) { + bus->fc_rcvd++; + bus->flowcontrol = fcbits; + } + + /* Check and update sequence number */ + if (rxseq != seq) { + DHD_INFO(("%s: rx_seq %d, expected %d\n", __FUNCTION__, seq, rxseq)); + bus->rx_badseq++; + rxseq = seq; + } + + /* Check window for sanity */ + if ((uint8)(txmax - bus->tx_seq) > 0x40) { + DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", + __FUNCTION__, txmax, bus->tx_seq)); + txmax = bus->tx_max; + } + bus->tx_max = txmax; + + /* Call a separate function for control frames */ + if (chan == SDPCM_CONTROL_CHANNEL) { + dhdsdio_read_control(bus, bus->rxhdr, len, doff); + continue; + } + + ASSERT((chan == SDPCM_DATA_CHANNEL) || (chan == SDPCM_EVENT_CHANNEL) || + (chan == SDPCM_TEST_CHANNEL) || (chan == SDPCM_GLOM_CHANNEL)); + + /* Length to read */ + rdlen = (len > firstread) ? (len - firstread) : 0; + + /* May pad read to blocksize for efficiency */ + if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) { + pad = bus->blocksize - (rdlen % bus->blocksize); + if ((pad <= bus->roundup) && (pad < bus->blocksize) && + ((rdlen + pad + firstread) < MAX_RX_DATASZ)) + rdlen += pad; + } else if (rdlen % DHD_SDALIGN) { + rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN); + } + + /* Satisfy length-alignment requirements */ + if (forcealign && (rdlen & (ALIGNMENT - 1))) + rdlen = ROUNDUP(rdlen, ALIGNMENT); + + if ((rdlen + firstread) > MAX_RX_DATASZ) { + /* Too long -- skip this frame */ + DHD_ERROR(("%s: too long: len %d rdlen %d\n", __FUNCTION__, len, rdlen)); + bus->dhd->rx_errors++; bus->rx_toolong++; + dhdsdio_rxfail(bus, FALSE, FALSE); + continue; + } + + dhd_os_sdlock_rxq(bus->dhd); + if (!(pkt = PKTGET(osh, (rdlen + firstread + DHD_SDALIGN), FALSE))) { + /* Give up on data, request rtx of events */ + DHD_ERROR(("%s: PKTGET failed: rdlen %d chan %d\n", + __FUNCTION__, rdlen, chan)); + bus->dhd->rx_dropped++; + dhd_os_sdunlock_rxq(bus->dhd); + dhdsdio_rxfail(bus, FALSE, RETRYCHAN(chan)); + continue; + } + dhd_os_sdunlock_rxq(bus->dhd); + + ASSERT(!PKTLINK(pkt)); + + /* Leave room for what we already read, and align remainder */ + ASSERT(firstread < (PKTLEN(osh, pkt))); + PKTPULL(osh, pkt, firstread); + PKTALIGN(osh, pkt, rdlen, DHD_SDALIGN); + + /* Read the remaining frame data */ + sdret = dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, + ((uint8 *)PKTDATA(osh, pkt)), rdlen, pkt, NULL, NULL); + bus->f2rxdata++; + ASSERT(sdret != BCME_PENDING); + + if (sdret < 0) { + DHD_ERROR(("%s: read %d %s bytes failed: %d\n", __FUNCTION__, rdlen, + ((chan == SDPCM_EVENT_CHANNEL) ? "event" : + ((chan == SDPCM_DATA_CHANNEL) ? "data" : "test")), sdret)); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE(bus->dhd->osh, pkt, FALSE); + dhd_os_sdunlock_rxq(bus->dhd); + bus->dhd->rx_errors++; + dhdsdio_rxfail(bus, TRUE, RETRYCHAN(chan)); + continue; + } + + /* Copy the already-read portion */ + PKTPUSH(osh, pkt, firstread); + bcopy(bus->rxhdr, PKTDATA(osh, pkt), firstread); + +#ifdef DHD_DEBUG + if (DHD_BYTES_ON() && DHD_DATA_ON()) { + prhex("Rx Data", PKTDATA(osh, pkt), len); + } +#endif + +deliver: + /* Save superframe descriptor and allocate packet frame */ + if (chan == SDPCM_GLOM_CHANNEL) { + if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) { + DHD_GLOM(("%s: got glom descriptor, %d bytes:\n", + __FUNCTION__, len)); +#ifdef DHD_DEBUG + if (DHD_GLOM_ON()) { + prhex("Glom Data", PKTDATA(osh, pkt), len); + } +#endif + PKTSETLEN(osh, pkt, len); + ASSERT(doff == SDPCM_HDRLEN); + PKTPULL(osh, pkt, SDPCM_HDRLEN); + bus->glomd = pkt; + } else { + DHD_ERROR(("%s: glom superframe w/o descriptor!\n", __FUNCTION__)); + dhdsdio_rxfail(bus, FALSE, FALSE); + } + continue; + } + + /* Fill in packet len and prio, deliver upward */ + PKTSETLEN(osh, pkt, len); + PKTPULL(osh, pkt, doff); + +#ifdef SDTEST + /* Test channel packets are processed separately */ + if (chan == SDPCM_TEST_CHANNEL) { + dhdsdio_testrcv(bus, pkt, seq); + continue; + } +#endif /* SDTEST */ + + if (PKTLEN(osh, pkt) == 0) { + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE(bus->dhd->osh, pkt, FALSE); + dhd_os_sdunlock_rxq(bus->dhd); + continue; + } else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pkt) != 0) { + DHD_ERROR(("%s: rx protocol error\n", __FUNCTION__)); + dhd_os_sdlock_rxq(bus->dhd); + PKTFREE(bus->dhd->osh, pkt, FALSE); + dhd_os_sdunlock_rxq(bus->dhd); + bus->dhd->rx_errors++; + continue; + } + + + /* Unlock during rx call */ + dhd_os_sdunlock(bus->dhd); + dhd_rx_frame(bus->dhd, ifidx, pkt, 1, chan); + dhd_os_sdlock(bus->dhd); + } + rxcount = maxframes - rxleft; +#ifdef DHD_DEBUG + /* Message if we hit the limit */ + if (!rxleft && !sdtest) + DHD_DATA(("%s: hit rx limit of %d frames\n", __FUNCTION__, maxframes)); + else +#endif /* DHD_DEBUG */ + DHD_DATA(("%s: processed %d frames\n", __FUNCTION__, rxcount)); + /* Back off rxseq if awaiting rtx, update rx_seq */ + if (bus->rxskip) + rxseq--; + bus->rx_seq = rxseq; + + return rxcount; +} + +static uint32 +dhdsdio_hostmail(dhd_bus_t *bus) +{ + sdpcmd_regs_t *regs = bus->regs; + uint32 intstatus = 0; + uint32 hmb_data; + uint8 fcbits; + uint retries = 0; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + /* Read mailbox data and ack that we did so */ + R_SDREG(hmb_data, ®s->tohostmailboxdata, retries); + if (retries <= retry_limit) + W_SDREG(SMB_INT_ACK, ®s->tosbmailbox, retries); + bus->f1regdata += 2; + + /* Dongle recomposed rx frames, accept them again */ + if (hmb_data & HMB_DATA_NAKHANDLED) { + DHD_INFO(("Dongle reports NAK handled, expect rtx of %d\n", bus->rx_seq)); + if (!bus->rxskip) { + DHD_ERROR(("%s: unexpected NAKHANDLED!\n", __FUNCTION__)); + } + bus->rxskip = FALSE; + intstatus |= FRAME_AVAIL_MASK(bus); + } + + /* + * DEVREADY does not occur with gSPI. + */ + if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) { + bus->sdpcm_ver = (hmb_data & HMB_DATA_VERSION_MASK) >> HMB_DATA_VERSION_SHIFT; + if (bus->sdpcm_ver != SDPCM_PROT_VERSION) + DHD_ERROR(("Version mismatch, dongle reports %d, expecting %d\n", + bus->sdpcm_ver, SDPCM_PROT_VERSION)); + else + DHD_INFO(("Dongle ready, protocol version %d\n", bus->sdpcm_ver)); + /* make sure for the SDIO_DEVICE_RXDATAINT_MODE_1 corecontrol is proper */ + if ((bus->sih->buscoretype == SDIOD_CORE_ID) && (bus->sdpcmrev >= 4) && + (bus->rxint_mode == SDIO_DEVICE_RXDATAINT_MODE_1)) { + uint32 val; + + val = R_REG(bus->dhd->osh, &bus->regs->corecontrol); + val &= ~CC_XMTDATAAVAIL_MODE; + val |= CC_XMTDATAAVAIL_CTRL; + W_REG(bus->dhd->osh, &bus->regs->corecontrol, val); + + val = R_REG(bus->dhd->osh, &bus->regs->corecontrol); + } + +#ifdef DHD_DEBUG + /* Retrieve console state address now that firmware should have updated it */ + { + sdpcm_shared_t shared; + if (dhdsdio_readshared(bus, &shared) == 0) + bus->console_addr = shared.console_addr; + } +#endif /* DHD_DEBUG */ + } + + /* + * Flow Control has been moved into the RX headers and this out of band + * method isn't used any more. Leave this here for possibly remaining backward + * compatible with older dongles + */ + if (hmb_data & HMB_DATA_FC) { + fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >> HMB_DATA_FCDATA_SHIFT; + + if (fcbits & ~bus->flowcontrol) + bus->fc_xoff++; + if (bus->flowcontrol & ~fcbits) + bus->fc_xon++; + + bus->fc_rcvd++; + bus->flowcontrol = fcbits; + } + +#ifdef DHD_DEBUG + /* At least print a message if FW halted */ + if (hmb_data & HMB_DATA_FWHALT) { + DHD_ERROR(("INTERNAL ERROR: FIRMWARE HALTED\n")); + dhdsdio_checkdied(bus, NULL, 0); + } +#endif /* DHD_DEBUG */ + + /* Shouldn't be any others */ + if (hmb_data & ~(HMB_DATA_DEVREADY | + HMB_DATA_FWHALT | + HMB_DATA_NAKHANDLED | + HMB_DATA_FC | + HMB_DATA_FWREADY | + HMB_DATA_FCDATA_MASK | + HMB_DATA_VERSION_MASK)) { + DHD_ERROR(("Unknown mailbox data content: 0x%02x\n", hmb_data)); + } + + return intstatus; +} + +static bool +dhdsdio_dpc(dhd_bus_t *bus) +{ + bcmsdh_info_t *sdh = bus->sdh; + sdpcmd_regs_t *regs = bus->regs; + uint32 intstatus, newstatus = 0; + uint retries = 0; + uint rxlimit = dhd_rxbound; /* Rx frames to read before resched */ + uint txlimit = dhd_txbound; /* Tx frames to send before resched */ + uint framecnt = 0; /* Temporary counter of tx/rx frames */ + bool rxdone = TRUE; /* Flag for no more read data */ + bool resched = FALSE; /* Flag indicating resched wanted */ + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (bus->dhd->busstate == DHD_BUS_DOWN) { + DHD_ERROR(("%s: Bus down, ret\n", __FUNCTION__)); + bus->intstatus = 0; + return 0; + } + + /* Start with leftover status bits */ + intstatus = bus->intstatus; + + dhd_os_sdlock(bus->dhd); + + /* If waiting for HTAVAIL, check status */ + if (bus->clkstate == CLK_PENDING) { + int err; + uint8 clkctl, devctl = 0; + +#ifdef DHD_DEBUG + /* Check for inconsistent device control */ + devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err); + if (err) { + DHD_ERROR(("%s: error reading DEVCTL: %d\n", __FUNCTION__, err)); + bus->dhd->busstate = DHD_BUS_DOWN; + } else { + ASSERT(devctl & SBSDIO_DEVCTL_CA_INT_ONLY); + } +#endif /* DHD_DEBUG */ + + /* Read CSR, if clock on switch to AVAIL, else ignore */ + clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); + if (err) { + DHD_ERROR(("%s: error reading CSR: %d\n", __FUNCTION__, err)); + bus->dhd->busstate = DHD_BUS_DOWN; + } + + DHD_INFO(("DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", devctl, clkctl)); + + if (SBSDIO_HTAV(clkctl)) { + devctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err); + if (err) { + DHD_ERROR(("%s: error reading DEVCTL: %d\n", + __FUNCTION__, err)); + bus->dhd->busstate = DHD_BUS_DOWN; + } + devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, devctl, &err); + if (err) { + DHD_ERROR(("%s: error writing DEVCTL: %d\n", + __FUNCTION__, err)); + bus->dhd->busstate = DHD_BUS_DOWN; + } + bus->clkstate = CLK_AVAIL; + } else { + goto clkwait; + } + } + + BUS_WAKE(bus); + + /* Make sure backplane clock is on */ + dhdsdio_clkctl(bus, CLK_AVAIL, TRUE); + if (bus->clkstate != CLK_AVAIL) + goto clkwait; + + /* Pending interrupt indicates new device status */ + if (bus->ipend) { + bus->ipend = FALSE; + R_SDREG(newstatus, ®s->intstatus, retries); + bus->f1regdata++; + if (bcmsdh_regfail(bus->sdh)) + newstatus = 0; + newstatus &= bus->hostintmask; + bus->fcstate = !!(newstatus & I_HMB_FC_STATE); + if (newstatus) { + bus->f1regdata++; + if ((bus->rxint_mode == SDIO_DEVICE_RXDATAINT_MODE_0) && + (newstatus == I_XMTDATA_AVAIL)) { + } + else + W_SDREG(newstatus, ®s->intstatus, retries); + } + } + + /* Merge new bits with previous */ + intstatus |= newstatus; + bus->intstatus = 0; + + /* Handle flow-control change: read new state in case our ack + * crossed another change interrupt. If change still set, assume + * FC ON for safety, let next loop through do the debounce. + */ + if (intstatus & I_HMB_FC_CHANGE) { + intstatus &= ~I_HMB_FC_CHANGE; + W_SDREG(I_HMB_FC_CHANGE, ®s->intstatus, retries); + R_SDREG(newstatus, ®s->intstatus, retries); + bus->f1regdata += 2; + bus->fcstate = !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE)); + intstatus |= (newstatus & bus->hostintmask); + } + + /* Just being here means nothing more to do for chipactive */ + if (intstatus & I_CHIPACTIVE) { + /* ASSERT(bus->clkstate == CLK_AVAIL); */ + intstatus &= ~I_CHIPACTIVE; + } + + /* Handle host mailbox indication */ + if (intstatus & I_HMB_HOST_INT) { + intstatus &= ~I_HMB_HOST_INT; + intstatus |= dhdsdio_hostmail(bus); + } + + /* Generally don't ask for these, can get CRC errors... */ + if (intstatus & I_WR_OOSYNC) { + DHD_ERROR(("Dongle reports WR_OOSYNC\n")); + intstatus &= ~I_WR_OOSYNC; + } + + if (intstatus & I_RD_OOSYNC) { + DHD_ERROR(("Dongle reports RD_OOSYNC\n")); + intstatus &= ~I_RD_OOSYNC; + } + + if (intstatus & I_SBINT) { + DHD_ERROR(("Dongle reports SBINT\n")); + intstatus &= ~I_SBINT; + } + + /* Would be active due to wake-wlan in gSPI */ + if (intstatus & I_CHIPACTIVE) { + DHD_INFO(("Dongle reports CHIPACTIVE\n")); + intstatus &= ~I_CHIPACTIVE; + } + + /* Ignore frame indications if rxskip is set */ + if (bus->rxskip) { + intstatus &= ~FRAME_AVAIL_MASK(bus); + } + + /* On frame indication, read available frames */ + if (PKT_AVAILABLE(bus, intstatus)) { + framecnt = dhdsdio_readframes(bus, rxlimit, &rxdone); + if (rxdone || bus->rxskip) + intstatus &= ~FRAME_AVAIL_MASK(bus); + rxlimit -= MIN(framecnt, rxlimit); + } + + /* Keep still-pending events for next scheduling */ + bus->intstatus = intstatus; + +clkwait: + /* Re-enable interrupts to detect new device events (mailbox, rx frame) + * or clock availability. (Allows tx loop to check ipend if desired.) + * (Unless register access seems hosed, as we may not be able to ACK...) + */ + if (bus->intr && bus->intdis && !bcmsdh_regfail(sdh)) { + DHD_INTR(("%s: enable SDIO interrupts, rxdone %d framecnt %d\n", + __FUNCTION__, rxdone, framecnt)); + bus->intdis = FALSE; +#if defined(OOB_INTR_ONLY) + bcmsdh_oob_intr_set(1); +#endif /* (OOB_INTR_ONLY) */ + bcmsdh_intr_enable(sdh); + } + +#if defined(OOB_INTR_ONLY) && !defined(HW_OOB) + /* In case of SW-OOB(using edge trigger), + * Check interrupt status in the dongle again after enable irq on the host. + * and rechedule dpc if interrupt is pended in the dongle. + * There is a chance to miss OOB interrupt while irq is disabled on the host. + * No need to do this with HW-OOB(level trigger) + */ + R_SDREG(newstatus, ®s->intstatus, retries); + if (bcmsdh_regfail(bus->sdh)) + newstatus = 0; + if (newstatus & bus->hostintmask) { + bus->ipend = TRUE; + resched = TRUE; + } +#endif /* defined(OOB_INTR_ONLY) && !defined(HW_OOB) */ + + if (TXCTLOK(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) { + int ret, i; + uint8* frame_seq = bus->ctrl_frame_buf + SDPCM_FRAMETAG_LEN; + + if (*frame_seq != bus->tx_seq) { + DHD_INFO(("%s IOCTL frame seq lag detected!" + " frm_seq:%d != bus->tx_seq:%d, corrected\n", + __FUNCTION__, *frame_seq, bus->tx_seq)); + *frame_seq = bus->tx_seq; + } + + ret = dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC, + (uint8 *)bus->ctrl_frame_buf, (uint32)bus->ctrl_frame_len, + NULL, NULL, NULL); + ASSERT(ret != BCME_PENDING); + + if (ret < 0) { + /* On failure, abort the command and terminate the frame */ + DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n", + __FUNCTION__, ret)); + bus->tx_sderrs++; + + bcmsdh_abort(sdh, SDIO_FUNC_2); + + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, + SFC_WF_TERM, NULL); + bus->f1regdata++; + + for (i = 0; i < 3; i++) { + uint8 hi, lo; + hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_WFRAMEBCHI, NULL); + lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_WFRAMEBCLO, NULL); + bus->f1regdata += 2; + if ((hi == 0) && (lo == 0)) + break; + } + } + if (ret == 0) { + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; + } + + bus->ctrl_frame_stat = FALSE; + dhd_wait_event_wakeup(bus->dhd); + } + /* Send queued frames (limit 1 if rx may still be pending) */ + else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate && + pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && DATAOK(bus)) { + framecnt = rxdone ? txlimit : MIN(txlimit, dhd_txminmax); + framecnt = dhdsdio_sendfromq(bus, framecnt); + txlimit -= framecnt; + } + /* Resched the DPC if ctrl cmd is pending on bus credit */ + if (bus->ctrl_frame_stat) + resched = TRUE; + + /* Resched if events or tx frames are pending, else await next interrupt */ + /* On failed register access, all bets are off: no resched or interrupts */ + if ((bus->dhd->busstate == DHD_BUS_DOWN) || bcmsdh_regfail(sdh)) { + DHD_ERROR(("%s: failed backplane access over SDIO, halting operation %d \n", + __FUNCTION__, bcmsdh_regfail(sdh))); + bus->dhd->busstate = DHD_BUS_DOWN; + bus->intstatus = 0; + } else if (bus->clkstate == CLK_PENDING) { + /* Awaiting I_CHIPACTIVE; don't resched */ + } else if (bus->intstatus || bus->ipend || + (!bus->fcstate && pktq_mlen(&bus->txq, ~bus->flowcontrol) && DATAOK(bus)) || + PKT_AVAILABLE(bus, bus->intstatus)) { /* Read multiple frames */ + resched = TRUE; + } + + bus->dpc_sched = resched; + + /* If we're done for now, turn off clock request. */ + if ((bus->idletime == DHD_IDLE_IMMEDIATE) && (bus->clkstate != CLK_PENDING)) { + bus->activity = FALSE; + dhdsdio_clkctl(bus, CLK_NONE, FALSE); + } + + dhd_os_sdunlock(bus->dhd); + return resched; +} + +bool +dhd_bus_dpc(struct dhd_bus *bus) +{ + bool resched; + + /* Call the DPC directly. */ + DHD_TRACE(("Calling dhdsdio_dpc() from %s\n", __FUNCTION__)); + resched = dhdsdio_dpc(bus); + + return resched; +} + +void +dhdsdio_isr(void *arg) +{ + dhd_bus_t *bus = (dhd_bus_t*)arg; + bcmsdh_info_t *sdh; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (!bus) { + DHD_ERROR(("%s : bus is null pointer , exit \n", __FUNCTION__)); + return; + } + sdh = bus->sdh; + + if (bus->dhd->busstate == DHD_BUS_DOWN) { + DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__)); + return; + } + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + /* Count the interrupt call */ + bus->intrcount++; + bus->ipend = TRUE; + + /* Shouldn't get this interrupt if we're sleeping? */ + if (bus->sleeping) { + DHD_ERROR(("INTERRUPT WHILE SLEEPING??\n")); + return; + } + + /* Disable additional interrupts (is this needed now)? */ + if (bus->intr) { + DHD_INTR(("%s: disable SDIO interrupts\n", __FUNCTION__)); + } else { + DHD_ERROR(("dhdsdio_isr() w/o interrupt configured!\n")); + } + + bcmsdh_intr_disable(sdh); + bus->intdis = TRUE; + +#if defined(SDIO_ISR_THREAD) + DHD_TRACE(("Calling dhdsdio_dpc() from %s\n", __FUNCTION__)); + DHD_OS_WAKE_LOCK(bus->dhd); + while (dhdsdio_dpc(bus)); + DHD_OS_WAKE_UNLOCK(bus->dhd); +#else + bus->dpc_sched = TRUE; + dhd_sched_dpc(bus->dhd); +#endif + +} + +#ifdef SDTEST +static void +dhdsdio_pktgen_init(dhd_bus_t *bus) +{ + /* Default to specified length, or full range */ + if (dhd_pktgen_len) { + bus->pktgen_maxlen = MIN(dhd_pktgen_len, MAX_PKTGEN_LEN); + bus->pktgen_minlen = bus->pktgen_maxlen; + } else { + bus->pktgen_maxlen = MAX_PKTGEN_LEN; + bus->pktgen_minlen = 0; + } + bus->pktgen_len = (uint16)bus->pktgen_minlen; + + /* Default to per-watchdog burst with 10s print time */ + bus->pktgen_freq = 1; + bus->pktgen_print = 10000 / dhd_watchdog_ms; + bus->pktgen_count = (dhd_pktgen * dhd_watchdog_ms + 999) / 1000; + + /* Default to echo mode */ + bus->pktgen_mode = DHD_PKTGEN_ECHO; + bus->pktgen_stop = 1; +} + +static void +dhdsdio_pktgen(dhd_bus_t *bus) +{ + void *pkt; + uint8 *data; + uint pktcount; + uint fillbyte; + osl_t *osh = bus->dhd->osh; + uint16 len; + + /* Display current count if appropriate */ + if (bus->pktgen_print && (++bus->pktgen_ptick >= bus->pktgen_print)) { + bus->pktgen_ptick = 0; + printf("%s: send attempts %d rcvd %d\n", + __FUNCTION__, bus->pktgen_sent, bus->pktgen_rcvd); + } + + /* For recv mode, just make sure dongle has started sending */ + if (bus->pktgen_mode == DHD_PKTGEN_RECV) { + if (bus->pktgen_rcv_state == PKTGEN_RCV_IDLE) { + bus->pktgen_rcv_state = PKTGEN_RCV_ONGOING; + dhdsdio_sdtest_set(bus, (uint8)bus->pktgen_total); + } + return; + } + + /* Otherwise, generate or request the specified number of packets */ + for (pktcount = 0; pktcount < bus->pktgen_count; pktcount++) { + /* Stop if total has been reached */ + if (bus->pktgen_total && (bus->pktgen_sent >= bus->pktgen_total)) { + bus->pktgen_count = 0; + break; + } + + /* Allocate an appropriate-sized packet */ + len = bus->pktgen_len; + if (!(pkt = PKTGET(osh, (len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN), + TRUE))) {; + DHD_ERROR(("%s: PKTGET failed!\n", __FUNCTION__)); + break; + } + PKTALIGN(osh, pkt, (len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN), DHD_SDALIGN); + data = (uint8*)PKTDATA(osh, pkt) + SDPCM_HDRLEN; + + /* Write test header cmd and extra based on mode */ + switch (bus->pktgen_mode) { + case DHD_PKTGEN_ECHO: + *data++ = SDPCM_TEST_ECHOREQ; + *data++ = (uint8)bus->pktgen_sent; + break; + + case DHD_PKTGEN_SEND: + *data++ = SDPCM_TEST_DISCARD; + *data++ = (uint8)bus->pktgen_sent; + break; + + case DHD_PKTGEN_RXBURST: + *data++ = SDPCM_TEST_BURST; + *data++ = (uint8)bus->pktgen_count; + break; + + default: + DHD_ERROR(("Unrecognized pktgen mode %d\n", bus->pktgen_mode)); + PKTFREE(osh, pkt, TRUE); + bus->pktgen_count = 0; + return; + } + + /* Write test header length field */ + *data++ = (len >> 0); + *data++ = (len >> 8); + + /* Then fill in the remainder -- N/A for burst, but who cares... */ + for (fillbyte = 0; fillbyte < len; fillbyte++) + *data++ = SDPCM_TEST_FILL(fillbyte, (uint8)bus->pktgen_sent); + +#ifdef DHD_DEBUG + if (DHD_BYTES_ON() && DHD_DATA_ON()) { + data = (uint8*)PKTDATA(osh, pkt) + SDPCM_HDRLEN; + prhex("dhdsdio_pktgen: Tx Data", data, PKTLEN(osh, pkt) - SDPCM_HDRLEN); + } +#endif + + /* Send it */ + if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE)) { + bus->pktgen_fail++; + if (bus->pktgen_stop && bus->pktgen_stop == bus->pktgen_fail) + bus->pktgen_count = 0; + } + bus->pktgen_sent++; + + /* Bump length if not fixed, wrap at max */ + if (++bus->pktgen_len > bus->pktgen_maxlen) + bus->pktgen_len = (uint16)bus->pktgen_minlen; + + /* Special case for burst mode: just send one request! */ + if (bus->pktgen_mode == DHD_PKTGEN_RXBURST) + break; + } +} + +static void +dhdsdio_sdtest_set(dhd_bus_t *bus, uint8 count) +{ + void *pkt; + uint8 *data; + osl_t *osh = bus->dhd->osh; + + /* Allocate the packet */ + if (!(pkt = PKTGET(osh, SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN, TRUE))) { + DHD_ERROR(("%s: PKTGET failed!\n", __FUNCTION__)); + return; + } + PKTALIGN(osh, pkt, (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN), DHD_SDALIGN); + data = (uint8*)PKTDATA(osh, pkt) + SDPCM_HDRLEN; + + /* Fill in the test header */ + *data++ = SDPCM_TEST_SEND; + *data++ = count; + *data++ = (bus->pktgen_maxlen >> 0); + *data++ = (bus->pktgen_maxlen >> 8); + + /* Send it */ + if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE)) + bus->pktgen_fail++; +} + + +static void +dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq) +{ + osl_t *osh = bus->dhd->osh; + uint8 *data; + uint pktlen; + + uint8 cmd; + uint8 extra; + uint16 len; + uint16 offset; + + /* Check for min length */ + if ((pktlen = PKTLEN(osh, pkt)) < SDPCM_TEST_HDRLEN) { + DHD_ERROR(("dhdsdio_restrcv: toss runt frame, pktlen %d\n", pktlen)); + PKTFREE(osh, pkt, FALSE); + return; + } + + /* Extract header fields */ + data = PKTDATA(osh, pkt); + cmd = *data++; + extra = *data++; + len = *data++; len += *data++ << 8; + DHD_TRACE(("%s:cmd:%d, xtra:%d,len:%d\n", __FUNCTION__, cmd, extra, len)); + /* Check length for relevant commands */ + if (cmd == SDPCM_TEST_DISCARD || cmd == SDPCM_TEST_ECHOREQ || cmd == SDPCM_TEST_ECHORSP) { + if (pktlen != len + SDPCM_TEST_HDRLEN) { + DHD_ERROR(("dhdsdio_testrcv: frame length mismatch, pktlen %d seq %d" + " cmd %d extra %d len %d\n", pktlen, seq, cmd, extra, len)); + PKTFREE(osh, pkt, FALSE); + return; + } + } + + /* Process as per command */ + switch (cmd) { + case SDPCM_TEST_ECHOREQ: + /* Rx->Tx turnaround ok (even on NDIS w/current implementation) */ + *(uint8 *)(PKTDATA(osh, pkt)) = SDPCM_TEST_ECHORSP; + if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, TRUE) == 0) { + bus->pktgen_sent++; + } else { + bus->pktgen_fail++; + PKTFREE(osh, pkt, FALSE); + } + bus->pktgen_rcvd++; + break; + + case SDPCM_TEST_ECHORSP: + if (bus->ext_loop) { + PKTFREE(osh, pkt, FALSE); + bus->pktgen_rcvd++; + break; + } + + for (offset = 0; offset < len; offset++, data++) { + if (*data != SDPCM_TEST_FILL(offset, extra)) { + DHD_ERROR(("dhdsdio_testrcv: echo data mismatch: " + "offset %d (len %d) expect 0x%02x rcvd 0x%02x\n", + offset, len, SDPCM_TEST_FILL(offset, extra), *data)); + break; + } + } + PKTFREE(osh, pkt, FALSE); + bus->pktgen_rcvd++; + break; + + case SDPCM_TEST_DISCARD: + { + int i = 0; + uint8 *prn = data; + uint8 testval = extra; + for (i = 0; i < len; i++) { + if (*prn != testval) { + DHD_ERROR(("DIErr@Pkt#:%d,Ix:%d, expected:0x%x, got:0x%x\n", + i, bus->pktgen_rcvd_rcvsession, testval, *prn)); + prn++; testval++; + } + } + } + PKTFREE(osh, pkt, FALSE); + bus->pktgen_rcvd++; + break; + + case SDPCM_TEST_BURST: + case SDPCM_TEST_SEND: + default: + DHD_INFO(("dhdsdio_testrcv: unsupported or unknown command, pktlen %d seq %d" + " cmd %d extra %d len %d\n", pktlen, seq, cmd, extra, len)); + PKTFREE(osh, pkt, FALSE); + break; + } + + /* For recv mode, stop at limit (and tell dongle to stop sending) */ + if (bus->pktgen_mode == DHD_PKTGEN_RECV) { + if (bus->pktgen_rcv_state != PKTGEN_RCV_IDLE) { + bus->pktgen_rcvd_rcvsession++; + + if (bus->pktgen_total && + (bus->pktgen_rcvd_rcvsession >= bus->pktgen_total)) { + bus->pktgen_count = 0; + DHD_ERROR(("Pktgen:rcv test complete!\n")); + bus->pktgen_rcv_state = PKTGEN_RCV_IDLE; + dhdsdio_sdtest_set(bus, FALSE); + bus->pktgen_rcvd_rcvsession = 0; + } + } + } +} +#endif /* SDTEST */ + +extern void +dhd_disable_intr(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus; + bus = dhdp->bus; + bcmsdh_intr_disable(bus->sdh); +} + +extern bool +dhd_bus_watchdog(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus; + + DHD_TIMER(("%s: Enter\n", __FUNCTION__)); + + bus = dhdp->bus; + + if (bus->dhd->dongle_reset) + return FALSE; + + /* Ignore the timer if simulating bus down */ + if (bus->sleeping) + return FALSE; + + if (dhdp->busstate == DHD_BUS_DOWN) + return FALSE; + + /* Poll period: check device if appropriate. */ + if (bus->poll && (++bus->polltick >= bus->pollrate)) { + uint32 intstatus = 0; + + /* Reset poll tick */ + bus->polltick = 0; + + /* Check device if no interrupts */ + if (!bus->intr || (bus->intrcount == bus->lastintrs)) { + + if (!bus->dpc_sched) { + uint8 devpend; + devpend = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, + SDIOD_CCCR_INTPEND, NULL); + intstatus = devpend & (INTR_STATUS_FUNC1 | INTR_STATUS_FUNC2); + } + + /* If there is something, make like the ISR and schedule the DPC */ + if (intstatus) { + bus->pollcnt++; + bus->ipend = TRUE; + if (bus->intr) { + bcmsdh_intr_disable(bus->sdh); + } + bus->dpc_sched = TRUE; + dhd_sched_dpc(bus->dhd); + + } + } + + /* Update interrupt tracking */ + bus->lastintrs = bus->intrcount; + } + +#ifdef DHD_DEBUG + /* Poll for console output periodically */ + if (dhdp->busstate == DHD_BUS_DATA && dhd_console_ms != 0) { + bus->console.count += dhd_watchdog_ms; + if (bus->console.count >= dhd_console_ms) { + bus->console.count -= dhd_console_ms; + /* Make sure backplane clock is on */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + if (dhdsdio_readconsole(bus) < 0) + dhd_console_ms = 0; /* On error, stop trying */ + } + } +#endif /* DHD_DEBUG */ + +#ifdef SDTEST + /* Generate packets if configured */ + if (bus->pktgen_count && (++bus->pktgen_tick >= bus->pktgen_freq)) { + /* Make sure backplane clock is on */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + bus->pktgen_tick = 0; + dhdsdio_pktgen(bus); + } +#endif + + /* On idle timeout clear activity flag and/or turn off clock */ + if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { + if (++bus->idlecount >= bus->idletime) { + bus->idlecount = 0; + if (bus->activity) { + bus->activity = FALSE; + dhdsdio_clkctl(bus, CLK_NONE, FALSE); + } + } + } + + return bus->ipend; +} + +#ifdef DHD_DEBUG +extern int +dhd_bus_console_in(dhd_pub_t *dhdp, uchar *msg, uint msglen) +{ + dhd_bus_t *bus = dhdp->bus; + uint32 addr, val; + int rv; + void *pkt; + + /* Address could be zero if CONSOLE := 0 in dongle Makefile */ + if (bus->console_addr == 0) + return BCME_UNSUPPORTED; + + /* Exclusive bus access */ + dhd_os_sdlock(bus->dhd); + + /* Don't allow input if dongle is in reset */ + if (bus->dhd->dongle_reset) { + dhd_os_sdunlock(bus->dhd); + return BCME_NOTREADY; + } + + /* Request clock to allow SDIO accesses */ + BUS_WAKE(bus); + /* No pend allowed since txpkt is called later, ht clk has to be on */ + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + /* Zero cbuf_index */ + addr = bus->console_addr + OFFSETOF(hndrte_cons_t, cbuf_idx); + val = htol32(0); + if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)&val, sizeof(val))) < 0) + goto done; + + /* Write message into cbuf */ + addr = bus->console_addr + OFFSETOF(hndrte_cons_t, cbuf); + if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)msg, msglen)) < 0) + goto done; + + /* Write length into vcons_in */ + addr = bus->console_addr + OFFSETOF(hndrte_cons_t, vcons_in); + val = htol32(msglen); + if ((rv = dhdsdio_membytes(bus, TRUE, addr, (uint8 *)&val, sizeof(val))) < 0) + goto done; + + /* Bump dongle by sending an empty packet on the event channel. + * sdpcm_sendup (RX) checks for virtual console input. + */ + if ((pkt = PKTGET(bus->dhd->osh, 4 + SDPCM_RESERVE, TRUE)) != NULL) + dhdsdio_txpkt(bus, pkt, SDPCM_EVENT_CHANNEL, TRUE); + +done: + if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) { + bus->activity = FALSE; + dhdsdio_clkctl(bus, CLK_NONE, TRUE); + } + + dhd_os_sdunlock(bus->dhd); + + return rv; +} +#endif /* DHD_DEBUG */ + +#ifdef DHD_DEBUG +static void +dhd_dump_cis(uint fn, uint8 *cis) +{ + uint byte, tag, tdata; + DHD_INFO(("Function %d CIS:\n", fn)); + + for (tdata = byte = 0; byte < SBSDIO_CIS_SIZE_LIMIT; byte++) { + if ((byte % 16) == 0) + DHD_INFO((" ")); + DHD_INFO(("%02x ", cis[byte])); + if ((byte % 16) == 15) + DHD_INFO(("\n")); + if (!tdata--) { + tag = cis[byte]; + if (tag == 0xff) + break; + else if (!tag) + tdata = 0; + else if ((byte + 1) < SBSDIO_CIS_SIZE_LIMIT) + tdata = cis[byte + 1] + 1; + else + DHD_INFO(("]")); + } + } + if ((byte % 16) != 15) + DHD_INFO(("\n")); +} +#endif /* DHD_DEBUG */ + +static bool +dhdsdio_chipmatch(uint16 chipid) +{ + if (chipid == BCM4325_CHIP_ID) + return TRUE; + if (chipid == BCM4329_CHIP_ID) + return TRUE; + if (chipid == BCM4315_CHIP_ID) + return TRUE; + if (chipid == BCM4319_CHIP_ID) + return TRUE; + if (chipid == BCM4330_CHIP_ID) + return TRUE; + if (chipid == BCM43239_CHIP_ID) + return TRUE; + if (chipid == BCM4336_CHIP_ID) + return TRUE; + if (chipid == BCM43237_CHIP_ID) + return TRUE; + if (chipid == BCM43362_CHIP_ID) + return TRUE; + + return FALSE; +} + +static void * +dhdsdio_probe(uint16 venid, uint16 devid, uint16 bus_no, uint16 slot, + uint16 func, uint bustype, void *regsva, osl_t * osh, void *sdh) +{ + int ret; + dhd_bus_t *bus; + dhd_cmn_t *cmn; +#ifdef GET_CUSTOM_MAC_ENABLE + struct ether_addr ea_addr; +#endif /* GET_CUSTOM_MAC_ENABLE */ +#ifdef PROP_TXSTATUS + uint up = 0; +#endif + + /* Init global variables at run-time, not as part of the declaration. + * This is required to support init/de-init of the driver. Initialization + * of globals as part of the declaration results in non-deterministic + * behavior since the value of the globals may be different on the + * first time that the driver is initialized vs subsequent initializations. + */ + dhd_txbound = DHD_TXBOUND; + dhd_rxbound = DHD_RXBOUND; + dhd_alignctl = TRUE; + sd1idle = TRUE; + dhd_readahead = TRUE; + retrydata = FALSE; + dhd_doflow = FALSE; + dhd_dongle_memsize = 0; + dhd_txminmax = DHD_TXMINMAX; + + forcealign = TRUE; + + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + DHD_INFO(("%s: venid 0x%04x devid 0x%04x\n", __FUNCTION__, venid, devid)); + + /* We make assumptions about address window mappings */ + ASSERT((uintptr)regsva == SI_ENUM_BASE); + + /* BCMSDH passes venid and devid based on CIS parsing -- but low-power start + * means early parse could fail, so here we should get either an ID + * we recognize OR (-1) indicating we must request power first. + */ + /* Check the Vendor ID */ + switch (venid) { + case 0x0000: + case VENDOR_BROADCOM: + break; + default: + DHD_ERROR(("%s: unknown vendor: 0x%04x\n", + __FUNCTION__, venid)); + return NULL; + } + + /* Check the Device ID and make sure it's one that we support */ + switch (devid) { + case BCM4325_D11DUAL_ID: /* 4325 802.11a/g id */ + case BCM4325_D11G_ID: /* 4325 802.11g 2.4Ghz band id */ + case BCM4325_D11A_ID: /* 4325 802.11a 5Ghz band id */ + DHD_INFO(("%s: found 4325 Dongle\n", __FUNCTION__)); + break; + case BCM4329_D11N_ID: /* 4329 802.11n dualband device */ + case BCM4329_D11N2G_ID: /* 4329 802.11n 2.4G device */ + case BCM4329_D11N5G_ID: /* 4329 802.11n 5G device */ + case 0x4329: + DHD_INFO(("%s: found 4329 Dongle\n", __FUNCTION__)); + break; + case BCM4315_D11DUAL_ID: /* 4315 802.11a/g id */ + case BCM4315_D11G_ID: /* 4315 802.11g id */ + case BCM4315_D11A_ID: /* 4315 802.11a id */ + DHD_INFO(("%s: found 4315 Dongle\n", __FUNCTION__)); + break; + case BCM4319_D11N_ID: /* 4319 802.11n id */ + case BCM4319_D11N2G_ID: /* 4319 802.11n2g id */ + case BCM4319_D11N5G_ID: /* 4319 802.11n5g id */ + DHD_INFO(("%s: found 4319 Dongle\n", __FUNCTION__)); + break; + case 0: + DHD_INFO(("%s: allow device id 0, will check chip internals\n", + __FUNCTION__)); + break; + + default: + DHD_ERROR(("%s: skipping 0x%04x/0x%04x, not a dongle\n", + __FUNCTION__, venid, devid)); + return NULL; + } + + if (osh == NULL) { + /* Ask the OS interface part for an OSL handle */ + if (!(osh = dhd_osl_attach(sdh, DHD_BUS))) { + DHD_ERROR(("%s: osl_attach failed!\n", __FUNCTION__)); + return NULL; + } + } + + /* Allocate private bus interface state */ + if (!(bus = MALLOC(osh, sizeof(dhd_bus_t)))) { + DHD_ERROR(("%s: MALLOC of dhd_bus_t failed\n", __FUNCTION__)); + goto fail; + } + bzero(bus, sizeof(dhd_bus_t)); + bus->sdh = sdh; + bus->cl_devid = (uint16)devid; + bus->bus = DHD_BUS; + bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1; + bus->usebufpool = FALSE; /* Use bufpool if allocated, else use locally malloced rxbuf */ + + /* attach the common module */ + if (!(cmn = dhd_common_init(osh))) { + DHD_ERROR(("%s: dhd_common_init failed\n", __FUNCTION__)); + goto fail; + } + + /* attempt to attach to the dongle */ + if (!(dhdsdio_probe_attach(bus, osh, sdh, regsva, devid))) { + DHD_ERROR(("%s: dhdsdio_probe_attach failed\n", __FUNCTION__)); + dhd_common_deinit(NULL, cmn); + goto fail; + } + + /* Attach to the dhd/OS/network interface */ + if (!(bus->dhd = dhd_attach(osh, bus, SDPCM_RESERVE))) { + DHD_ERROR(("%s: dhd_attach failed\n", __FUNCTION__)); + goto fail; + } + + bus->dhd->cmn = cmn; + cmn->dhd = bus->dhd; + + /* Allocate buffers */ + if (!(dhdsdio_probe_malloc(bus, osh, sdh))) { + DHD_ERROR(("%s: dhdsdio_probe_malloc failed\n", __FUNCTION__)); + goto fail; + } + + if (!(dhdsdio_probe_init(bus, osh, sdh))) { + DHD_ERROR(("%s: dhdsdio_probe_init failed\n", __FUNCTION__)); + goto fail; + } + + if (bus->intr) { + /* Register interrupt callback, but mask it (not operational yet). */ + DHD_INTR(("%s: disable SDIO interrupts (not interested yet)\n", __FUNCTION__)); + bcmsdh_intr_disable(sdh); + if ((ret = bcmsdh_intr_reg(sdh, dhdsdio_isr, bus)) != 0) { + DHD_ERROR(("%s: FAILED: bcmsdh_intr_reg returned %d\n", + __FUNCTION__, ret)); + goto fail; + } + DHD_INTR(("%s: registered SDIO interrupt function ok\n", __FUNCTION__)); + } else { + DHD_INFO(("%s: SDIO interrupt function is NOT registered due to polling mode\n", + __FUNCTION__)); + } + + DHD_INFO(("%s: completed!!\n", __FUNCTION__)); + +#ifdef GET_CUSTOM_MAC_ENABLE + /* Read MAC address from external customer place */ + memset(&ea_addr, 0, sizeof(ea_addr)); + ret = dhd_custom_get_mac_address(ea_addr.octet); + if (!ret) { + memcpy(bus->dhd->mac.octet, (void *)&ea_addr, ETHER_ADDR_LEN); + } +#endif /* GET_CUSTOM_MAC_ENABLE */ + + /* if firmware path present try to download and bring up bus */ + if (dhd_download_fw_on_driverload && (ret = dhd_bus_start(bus->dhd)) != 0) { + DHD_ERROR(("%s: dhd_bus_start failed\n", __FUNCTION__)); + if (ret == BCME_NOTUP) + goto fail; + } + + /* Ok, have the per-port tell the stack we're open for business */ + if (dhd_net_attach(bus->dhd, 0) != 0) { + DHD_ERROR(("%s: Net attach failed!!\n", __FUNCTION__)); + goto fail; + } + +#ifdef PROP_TXSTATUS + if (dhd_download_fw_on_driverload) + dhd_wl_ioctl_cmd(bus->dhd, WLC_UP, (char *)&up, sizeof(up), TRUE, 0); +#endif + return bus; + +fail: + dhdsdio_release(bus, osh); + return NULL; +} + +static bool +dhdsdio_probe_attach(struct dhd_bus *bus, osl_t *osh, void *sdh, void *regsva, + uint16 devid) +{ + int err = 0; + uint8 clkctl = 0; + + bus->alp_only = TRUE; + + /* Return the window to backplane enumeration space for core access */ + if (dhdsdio_set_siaddr_window(bus, SI_ENUM_BASE)) { + DHD_ERROR(("%s: FAILED to return to SI_ENUM_BASE\n", __FUNCTION__)); + } + +#ifdef DHD_DEBUG + DHD_ERROR(("F1 signature read @0x18000000=0x%4x\n", + bcmsdh_reg_read(bus->sdh, SI_ENUM_BASE, 4))); + +#endif /* DHD_DEBUG */ + + + /* Force PLL off until si_attach() programs PLL control regs */ + + + + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, DHD_INIT_CLKCTL1, &err); + if (!err) + clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, &err); + + if (err || ((clkctl & ~SBSDIO_AVBITS) != DHD_INIT_CLKCTL1)) { + DHD_ERROR(("dhdsdio_probe: ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n", + err, DHD_INIT_CLKCTL1, clkctl)); + goto fail; + } + + +#ifdef DHD_DEBUG + if (DHD_INFO_ON()) { + uint fn, numfn; + uint8 *cis[SDIOD_MAX_IOFUNCS]; + int err = 0; + + numfn = bcmsdh_query_iofnum(sdh); + ASSERT(numfn <= SDIOD_MAX_IOFUNCS); + + /* Make sure ALP is available before trying to read CIS */ + SPINWAIT(((clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_CHIPCLKCSR, NULL)), + !SBSDIO_ALPAV(clkctl)), PMU_MAX_TRANSITION_DLY); + + /* Now request ALP be put on the bus */ + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, + DHD_INIT_CLKCTL2, &err); + OSL_DELAY(65); + + for (fn = 0; fn <= numfn; fn++) { + if (!(cis[fn] = MALLOC(osh, SBSDIO_CIS_SIZE_LIMIT))) { + DHD_INFO(("dhdsdio_probe: fn %d cis malloc failed\n", fn)); + break; + } + bzero(cis[fn], SBSDIO_CIS_SIZE_LIMIT); + + if ((err = bcmsdh_cis_read(sdh, fn, cis[fn], SBSDIO_CIS_SIZE_LIMIT))) { + DHD_INFO(("dhdsdio_probe: fn %d cis read err %d\n", fn, err)); + MFREE(osh, cis[fn], SBSDIO_CIS_SIZE_LIMIT); + break; + } + dhd_dump_cis(fn, cis[fn]); + } + + while (fn-- > 0) { + ASSERT(cis[fn]); + MFREE(osh, cis[fn], SBSDIO_CIS_SIZE_LIMIT); + } + + if (err) { + DHD_ERROR(("dhdsdio_probe: failure reading or parsing CIS\n")); + goto fail; + } + } +#endif /* DHD_DEBUG */ + + /* si_attach() will provide an SI handle and scan the backplane */ + if (!(bus->sih = si_attach((uint)devid, osh, regsva, DHD_BUS, sdh, + &bus->vars, &bus->varsz))) { + DHD_ERROR(("%s: si_attach failed!\n", __FUNCTION__)); + goto fail; + } + + bcmsdh_chipinfo(sdh, bus->sih->chip, bus->sih->chiprev); + + if (!dhdsdio_chipmatch((uint16)bus->sih->chip)) { + DHD_ERROR(("%s: unsupported chip: 0x%04x\n", + __FUNCTION__, bus->sih->chip)); + goto fail; + } + + + si_sdiod_drive_strength_init(bus->sih, osh, dhd_sdiod_drive_strength); + + + /* Get info on the ARM and SOCRAM cores... */ + if (!DHD_NOPMU(bus)) { + if ((si_setcore(bus->sih, ARM7S_CORE_ID, 0)) || + (si_setcore(bus->sih, ARMCM3_CORE_ID, 0))) { + bus->armrev = si_corerev(bus->sih); + } else { + DHD_ERROR(("%s: failed to find ARM core!\n", __FUNCTION__)); + goto fail; + } + if (!(bus->orig_ramsize = si_socram_size(bus->sih))) { + DHD_ERROR(("%s: failed to find SOCRAM memory!\n", __FUNCTION__)); + goto fail; + } + bus->ramsize = bus->orig_ramsize; + if (dhd_dongle_memsize) + dhd_dongle_setmemsize(bus, dhd_dongle_memsize); + + DHD_ERROR(("DHD: dongle ram size is set to %d(orig %d)\n", + bus->ramsize, bus->orig_ramsize)); + } + + /* ...but normally deal with the SDPCMDEV core */ + if (!(bus->regs = si_setcore(bus->sih, PCMCIA_CORE_ID, 0)) && + !(bus->regs = si_setcore(bus->sih, SDIOD_CORE_ID, 0))) { + DHD_ERROR(("%s: failed to find SDIODEV core!\n", __FUNCTION__)); + goto fail; + } + bus->sdpcmrev = si_corerev(bus->sih); + + /* Set core control so an SDIO reset does a backplane reset */ + OR_REG(osh, &bus->regs->corecontrol, CC_BPRESEN); + bus->rxint_mode = SDIO_DEVICE_HMB_RXINT; + + if ((bus->sih->buscoretype == SDIOD_CORE_ID) && (bus->sdpcmrev >= 4) && + (bus->rxint_mode == SDIO_DEVICE_RXDATAINT_MODE_1)) + { + uint32 val; + + val = R_REG(osh, &bus->regs->corecontrol); + val &= ~CC_XMTDATAAVAIL_MODE; + val |= CC_XMTDATAAVAIL_CTRL; + W_REG(osh, &bus->regs->corecontrol, val); + } + + + pktq_init(&bus->txq, (PRIOMASK + 1), QLEN); + + /* Locate an appropriately-aligned portion of hdrbuf */ + bus->rxhdr = (uint8 *)ROUNDUP((uintptr)&bus->hdrbuf[0], DHD_SDALIGN); + + /* Set the poll and/or interrupt flags */ + bus->intr = (bool)dhd_intr; + if ((bus->poll = (bool)dhd_poll)) + bus->pollrate = 1; + + return TRUE; + +fail: + if (bus->sih != NULL) { + si_detach(bus->sih); + bus->sih = NULL; + } + return FALSE; +} + +static bool +dhdsdio_probe_malloc(dhd_bus_t *bus, osl_t *osh, void *sdh) +{ + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (bus->dhd->maxctl) { + bus->rxblen = ROUNDUP((bus->dhd->maxctl + SDPCM_HDRLEN), ALIGNMENT) + DHD_SDALIGN; + if (!(bus->rxbuf = DHD_OS_PREALLOC(osh, DHD_PREALLOC_RXBUF, bus->rxblen))) { + DHD_ERROR(("%s: MALLOC of %d-byte rxbuf failed\n", + __FUNCTION__, bus->rxblen)); + goto fail; + } + } + /* Allocate buffer to receive glomed packet */ + if (!(bus->databuf = DHD_OS_PREALLOC(osh, DHD_PREALLOC_DATABUF, MAX_DATA_BUF))) { + DHD_ERROR(("%s: MALLOC of %d-byte databuf failed\n", + __FUNCTION__, MAX_DATA_BUF)); + /* release rxbuf which was already located as above */ + if (!bus->rxblen) + DHD_OS_PREFREE(osh, bus->rxbuf, bus->rxblen); + goto fail; + } + + /* Align the buffer */ + if ((uintptr)bus->databuf % DHD_SDALIGN) + bus->dataptr = bus->databuf + (DHD_SDALIGN - ((uintptr)bus->databuf % DHD_SDALIGN)); + else + bus->dataptr = bus->databuf; + + return TRUE; + +fail: + return FALSE; +} + +static bool +dhdsdio_probe_init(dhd_bus_t *bus, osl_t *osh, void *sdh) +{ + int32 fnum; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + +#ifdef SDTEST + dhdsdio_pktgen_init(bus); +#endif /* SDTEST */ + + /* Disable F2 to clear any intermediate frame state on the dongle */ + bcmsdh_cfg_write(sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, SDIO_FUNC_ENABLE_1, NULL); + + bus->dhd->busstate = DHD_BUS_DOWN; + bus->sleeping = FALSE; + bus->rxflow = FALSE; + bus->prev_rxlim_hit = 0; + + + /* Done with backplane-dependent accesses, can drop clock... */ + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); + + /* ...and initialize clock/power states */ + bus->clkstate = CLK_SDONLY; + bus->idletime = (int32)dhd_idletime; + bus->idleclock = DHD_IDLE_ACTIVE; + + /* Query the SD clock speed */ + if (bcmsdh_iovar_op(sdh, "sd_divisor", NULL, 0, + &bus->sd_divisor, sizeof(int32), FALSE) != BCME_OK) { + DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_divisor")); + bus->sd_divisor = -1; + } else { + DHD_INFO(("%s: Initial value for %s is %d\n", + __FUNCTION__, "sd_divisor", bus->sd_divisor)); + } + + /* Query the SD bus mode */ + if (bcmsdh_iovar_op(sdh, "sd_mode", NULL, 0, + &bus->sd_mode, sizeof(int32), FALSE) != BCME_OK) { + DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_mode")); + bus->sd_mode = -1; + } else { + DHD_INFO(("%s: Initial value for %s is %d\n", + __FUNCTION__, "sd_mode", bus->sd_mode)); + } + + /* Query the F2 block size, set roundup accordingly */ + fnum = 2; + if (bcmsdh_iovar_op(sdh, "sd_blocksize", &fnum, sizeof(int32), + &bus->blocksize, sizeof(int32), FALSE) != BCME_OK) { + bus->blocksize = 0; + DHD_ERROR(("%s: fail on %s get\n", __FUNCTION__, "sd_blocksize")); + } else { + DHD_INFO(("%s: Initial value for %s is %d\n", + __FUNCTION__, "sd_blocksize", bus->blocksize)); + } + bus->roundup = MIN(max_roundup, bus->blocksize); + + /* Query if bus module supports packet chaining, default to use if supported */ + if (bcmsdh_iovar_op(sdh, "sd_rxchain", NULL, 0, + &bus->sd_rxchain, sizeof(int32), FALSE) != BCME_OK) { + bus->sd_rxchain = FALSE; + } else { + DHD_INFO(("%s: bus module (through bcmsdh API) %s chaining\n", + __FUNCTION__, (bus->sd_rxchain ? "supports" : "does not support"))); + } + bus->use_rxchain = (bool)bus->sd_rxchain; + + return TRUE; +} + +bool +dhd_bus_download_firmware(struct dhd_bus *bus, osl_t *osh, + char *pfw_path, char *pnv_path) +{ + bool ret; + bus->fw_path = pfw_path; + bus->nv_path = pnv_path; + + ret = dhdsdio_download_firmware(bus, osh, bus->sdh); + + + return ret; +} + +static bool +dhdsdio_download_firmware(struct dhd_bus *bus, osl_t *osh, void *sdh) +{ + bool ret; + + /* Download the firmware */ + DHD_OS_WAKE_LOCK(bus->dhd); + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + + ret = _dhdsdio_download_firmware(bus) == 0; + + dhdsdio_clkctl(bus, CLK_SDONLY, FALSE); + DHD_OS_WAKE_UNLOCK(bus->dhd); + return ret; +} + +/* Detach and free everything */ +static void +dhdsdio_release(dhd_bus_t *bus, osl_t *osh) +{ + bool dongle_isolation = FALSE; + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (bus) { + ASSERT(osh); + + /* De-register interrupt handler */ + bcmsdh_intr_disable(bus->sdh); + bcmsdh_intr_dereg(bus->sdh); + + if (bus->dhd) { + dhd_common_deinit(bus->dhd, NULL); + dongle_isolation = bus->dhd->dongle_isolation; + dhd_detach(bus->dhd); + dhdsdio_release_dongle(bus, osh, dongle_isolation, TRUE); + dhd_free(bus->dhd); + bus->dhd = NULL; + } + + dhdsdio_release_malloc(bus, osh); + +#ifdef DHD_DEBUG + if (bus->console.buf != NULL) + MFREE(osh, bus->console.buf, bus->console.bufsize); +#endif + + MFREE(osh, bus, sizeof(dhd_bus_t)); + } + + if (osh) + dhd_osl_detach(osh); + + DHD_TRACE(("%s: Disconnected\n", __FUNCTION__)); +} + +static void +dhdsdio_release_malloc(dhd_bus_t *bus, osl_t *osh) +{ + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (bus->dhd && bus->dhd->dongle_reset) + return; + + if (bus->rxbuf) { +#ifndef CONFIG_DHD_USE_STATIC_BUF + MFREE(osh, bus->rxbuf, bus->rxblen); +#endif + bus->rxctl = bus->rxbuf = NULL; + bus->rxlen = 0; + } + + if (bus->databuf) { +#ifndef CONFIG_DHD_USE_STATIC_BUF + MFREE(osh, bus->databuf, MAX_DATA_BUF); +#endif + bus->databuf = NULL; + } + + if (bus->vars && bus->varsz) { + MFREE(osh, bus->vars, bus->varsz); + bus->vars = NULL; + } + +} + + +static void +dhdsdio_release_dongle(dhd_bus_t *bus, osl_t *osh, bool dongle_isolation, bool reset_flag) +{ + DHD_TRACE(("%s: Enter bus->dhd %p bus->dhd->dongle_reset %d \n", __FUNCTION__, + bus->dhd, bus->dhd->dongle_reset)); + + if ((bus->dhd && bus->dhd->dongle_reset) && reset_flag) + return; + + if (bus->sih) { + if (bus->dhd) { + dhdsdio_clkctl(bus, CLK_AVAIL, FALSE); + } +#if !defined(BCMLXSDMMC) + if (dongle_isolation == FALSE) + si_watchdog(bus->sih, 4); +#endif /* !defined(BCMLXSDMMC) */ + if (bus->dhd) { + dhdsdio_clkctl(bus, CLK_NONE, FALSE); + } + si_detach(bus->sih); + bus->sih = NULL; + if (bus->vars && bus->varsz) + MFREE(osh, bus->vars, bus->varsz); + bus->vars = NULL; + } + + DHD_TRACE(("%s: Disconnected\n", __FUNCTION__)); +} + +static void +dhdsdio_disconnect(void *ptr) +{ + dhd_bus_t *bus = (dhd_bus_t *)ptr; + + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + if (bus) { + ASSERT(bus->dhd); + dhdsdio_release(bus, bus->dhd->osh); + } + + DHD_TRACE(("%s: Disconnected\n", __FUNCTION__)); +} + + +/* Register/Unregister functions are called by the main DHD entry + * point (e.g. module insertion) to link with the bus driver, in + * order to look for or await the device. + */ + +static bcmsdh_driver_t dhd_sdio = { + dhdsdio_probe, + dhdsdio_disconnect +}; + +int +dhd_bus_register(void) +{ + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + return bcmsdh_register(&dhd_sdio); +} + +void +dhd_bus_unregister(void) +{ + DHD_TRACE(("%s: Enter\n", __FUNCTION__)); + + bcmsdh_unregister(); +} + +#ifdef BCMEMBEDIMAGE +static int +dhdsdio_download_code_array(struct dhd_bus *bus) +{ + int bcmerror = -1; + int offset = 0; + unsigned char *ularray = NULL; + + DHD_INFO(("%s: download embedded firmware...\n", __FUNCTION__)); + + /* Download image */ + while ((offset + MEMBLOCK) < sizeof(dlarray)) { + bcmerror = dhdsdio_membytes(bus, TRUE, offset, + (uint8 *) (dlarray + offset), MEMBLOCK); + if (bcmerror) { + DHD_ERROR(("%s: error %d on writing %d membytes at 0x%08x\n", + __FUNCTION__, bcmerror, MEMBLOCK, offset)); + goto err; + } + + offset += MEMBLOCK; + } + + if (offset < sizeof(dlarray)) { + bcmerror = dhdsdio_membytes(bus, TRUE, offset, + (uint8 *) (dlarray + offset), sizeof(dlarray) - offset); + if (bcmerror) { + DHD_ERROR(("%s: error %d on writing %d membytes at 0x%08x\n", + __FUNCTION__, bcmerror, sizeof(dlarray) - offset, offset)); + goto err; + } + } + +#ifdef DHD_DEBUG + /* Upload and compare the downloaded code */ + { + ularray = MALLOC(bus->dhd->osh, bus->ramsize); + /* Upload image to verify downloaded contents. */ + offset = 0; + memset(ularray, 0xaa, bus->ramsize); + while ((offset + MEMBLOCK) < sizeof(dlarray)) { + bcmerror = dhdsdio_membytes(bus, FALSE, offset, ularray + offset, MEMBLOCK); + if (bcmerror) { + DHD_ERROR(("%s: error %d on reading %d membytes at 0x%08x\n", + __FUNCTION__, bcmerror, MEMBLOCK, offset)); + goto err; + } + + offset += MEMBLOCK; + } + + if (offset < sizeof(dlarray)) { + bcmerror = dhdsdio_membytes(bus, FALSE, offset, + ularray + offset, sizeof(dlarray) - offset); + if (bcmerror) { + DHD_ERROR(("%s: error %d on reading %d membytes at 0x%08x\n", + __FUNCTION__, bcmerror, sizeof(dlarray) - offset, offset)); + goto err; + } + } + + if (memcmp(dlarray, ularray, sizeof(dlarray))) { + DHD_ERROR(("%s: Downloaded image is corrupted (%s, %s, %s).\n", + __FUNCTION__, dlimagename, dlimagever, dlimagedate)); + goto err; + } else + DHD_ERROR(("%s: Download, Upload and compare succeeded (%s, %s, %s).\n", + __FUNCTION__, dlimagename, dlimagever, dlimagedate)); + + } +#endif /* DHD_DEBUG */ + +err: + if (ularray) + MFREE(bus->dhd->osh, ularray, bus->ramsize); + return bcmerror; +} +#endif /* BCMEMBEDIMAGE */ + +static int +dhdsdio_download_code_file(struct dhd_bus *bus, char *pfw_path) +{ + int bcmerror = -1; + int offset = 0; + uint len; + void *image = NULL; + uint8 *memblock = NULL, *memptr; + + DHD_INFO(("%s: download firmware %s\n", __FUNCTION__, pfw_path)); + + image = dhd_os_open_image(pfw_path); + if (image == NULL) + goto err; + + memptr = memblock = MALLOC(bus->dhd->osh, MEMBLOCK + DHD_SDALIGN); + if (memblock == NULL) { + DHD_ERROR(("%s: Failed to allocate memory %d bytes\n", __FUNCTION__, MEMBLOCK)); + goto err; + } + if ((uint32)(uintptr)memblock % DHD_SDALIGN) + memptr += (DHD_SDALIGN - ((uint32)(uintptr)memblock % DHD_SDALIGN)); + + /* Download image */ + while ((len = dhd_os_get_image_block((char*)memptr, MEMBLOCK, image))) { + bcmerror = dhdsdio_membytes(bus, TRUE, offset, memptr, len); + if (bcmerror) { + DHD_ERROR(("%s: error %d on writing %d membytes at 0x%08x\n", + __FUNCTION__, bcmerror, MEMBLOCK, offset)); + goto err; + } + + offset += MEMBLOCK; + } + +err: + if (memblock) + MFREE(bus->dhd->osh, memblock, MEMBLOCK + DHD_SDALIGN); + + if (image) + dhd_os_close_image(image); + + return bcmerror; +} + +/* + EXAMPLE: nvram_array + nvram_arry format: + name=value + Use carriage return at the end of each assignment, and an empty string with + carriage return at the end of array. + + For example: + unsigned char nvram_array[] = {"name1=value1\n", "name2=value2\n", "\n"}; + Hex values start with 0x, and mac addr format: xx:xx:xx:xx:xx:xx. + + Search "EXAMPLE: nvram_array" to see how the array is activated. +*/ + +void +dhd_bus_set_nvram_params(struct dhd_bus * bus, const char *nvram_params) +{ + bus->nvram_params = nvram_params; +} + +static int +dhdsdio_download_nvram(struct dhd_bus *bus) +{ + int bcmerror = -1; + uint len; + void * image = NULL; + char * memblock = NULL; + char *bufp; + char *pnv_path; + bool nvram_file_exists; + + pnv_path = bus->nv_path; + + nvram_file_exists = ((pnv_path != NULL) && (pnv_path[0] != '\0')); + if (!nvram_file_exists && (bus->nvram_params == NULL)) + return (0); + + if (nvram_file_exists) { + image = dhd_os_open_image(pnv_path); + if (image == NULL) + goto err; + } + + memblock = MALLOC(bus->dhd->osh, MAX_NVRAMBUF_SIZE); + if (memblock == NULL) { + DHD_ERROR(("%s: Failed to allocate memory %d bytes\n", + __FUNCTION__, MAX_NVRAMBUF_SIZE)); + goto err; + } + + /* Download variables */ + if (nvram_file_exists) { + len = dhd_os_get_image_block(memblock, MAX_NVRAMBUF_SIZE, image); + } + else { + len = strlen(bus->nvram_params); + ASSERT(len <= MAX_NVRAMBUF_SIZE); + memcpy(memblock, bus->nvram_params, len); + } + if (len > 0 && len < MAX_NVRAMBUF_SIZE) { + bufp = (char *)memblock; + bufp[len] = 0; + len = process_nvram_vars(bufp, len); + if (len % 4) { + len += 4 - (len % 4); + } + bufp += len; + *bufp++ = 0; + if (len) + bcmerror = dhdsdio_downloadvars(bus, memblock, len + 1); + if (bcmerror) { + DHD_ERROR(("%s: error downloading vars: %d\n", + __FUNCTION__, bcmerror)); + } + } + else { + DHD_ERROR(("%s: error reading nvram file: %d\n", + __FUNCTION__, len)); + bcmerror = BCME_SDIO_ERROR; + } + +err: + if (memblock) + MFREE(bus->dhd->osh, memblock, MAX_NVRAMBUF_SIZE); + + if (image) + dhd_os_close_image(image); + + return bcmerror; +} + +static int +_dhdsdio_download_firmware(struct dhd_bus *bus) +{ + int bcmerror = -1; + + bool embed = FALSE; /* download embedded firmware */ + bool dlok = FALSE; /* download firmware succeeded */ + + /* Out immediately if no image to download */ + if ((bus->fw_path == NULL) || (bus->fw_path[0] == '\0')) { +#ifdef BCMEMBEDIMAGE + embed = TRUE; +#else + return 0; +#endif + } + + /* Keep arm in reset */ + if (dhdsdio_download_state(bus, TRUE)) { + DHD_ERROR(("%s: error placing ARM core in reset\n", __FUNCTION__)); + goto err; + } + + /* External image takes precedence if specified */ + if ((bus->fw_path != NULL) && (bus->fw_path[0] != '\0')) { + if (dhdsdio_download_code_file(bus, bus->fw_path)) { + DHD_ERROR(("%s: dongle image file download failed\n", __FUNCTION__)); +#ifdef BCMEMBEDIMAGE + embed = TRUE; +#else + goto err; +#endif + } + else { + embed = FALSE; + dlok = TRUE; + } + } +#ifdef BCMEMBEDIMAGE + if (embed) { + if (dhdsdio_download_code_array(bus)) { + DHD_ERROR(("%s: dongle image array download failed\n", __FUNCTION__)); + goto err; + } + else { + dlok = TRUE; + } + } +#endif + if (!dlok) { + DHD_ERROR(("%s: dongle image download failed\n", __FUNCTION__)); + goto err; + } + + /* EXAMPLE: nvram_array */ + /* If a valid nvram_arry is specified as above, it can be passed down to dongle */ + /* dhd_bus_set_nvram_params(bus, (char *)&nvram_array); */ + + /* External nvram takes precedence if specified */ + if (dhdsdio_download_nvram(bus)) { + DHD_ERROR(("%s: dongle nvram file download failed\n", __FUNCTION__)); + goto err; + } + + /* Take arm out of reset */ + if (dhdsdio_download_state(bus, FALSE)) { + DHD_ERROR(("%s: error getting out of ARM core reset\n", __FUNCTION__)); + goto err; + } + + bcmerror = 0; + +err: + return bcmerror; +} + +static int +dhd_bcmsdh_recv_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf, uint nbytes, + void *pkt, bcmsdh_cmplt_fn_t complete, void *handle) +{ + int status; + + status = bcmsdh_recv_buf(bus->sdh, addr, fn, flags, buf, nbytes, pkt, complete, handle); + + return status; +} + +static int +dhd_bcmsdh_send_buf(dhd_bus_t *bus, uint32 addr, uint fn, uint flags, uint8 *buf, uint nbytes, + void *pkt, bcmsdh_cmplt_fn_t complete, void *handle) +{ + return (bcmsdh_send_buf(bus->sdh, addr, fn, flags, buf, nbytes, pkt, complete, handle)); +} + +uint +dhd_bus_chip(struct dhd_bus *bus) +{ + ASSERT(bus); + ASSERT(bus->sih != NULL); + return bus->sih->chip; +} + +void * +dhd_bus_pub(struct dhd_bus *bus) +{ + ASSERT(bus); + return bus->dhd; +} + +void * +dhd_bus_txq(struct dhd_bus *bus) +{ + return &bus->txq; +} + +uint +dhd_bus_hdrlen(struct dhd_bus *bus) +{ + return SDPCM_HDRLEN; +} + +int +dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) +{ + int bcmerror = 0; + dhd_bus_t *bus; + + bus = dhdp->bus; + + if (flag == TRUE) { + if (!bus->dhd->dongle_reset) { + dhd_os_sdlock(dhdp); + dhd_os_wd_timer(dhdp, 0); +#if !defined(IGNORE_ETH0_DOWN) + /* Force flow control as protection when stop come before ifconfig_down */ + dhd_txflowcontrol(bus->dhd, ALL_INTERFACES, ON); +#endif /* !defined(IGNORE_ETH0_DOWN) */ + /* Expect app to have torn down any connection before calling */ + /* Stop the bus, disable F2 */ + dhd_bus_stop(bus, FALSE); + +#if defined(OOB_INTR_ONLY) + /* Clean up any pending IRQ */ + bcmsdh_set_irq(FALSE); +#endif /* defined(OOB_INTR_ONLY) */ + + /* Clean tx/rx buffer pointers, detach from the dongle */ + dhdsdio_release_dongle(bus, bus->dhd->osh, TRUE, TRUE); + + bus->dhd->dongle_reset = TRUE; + bus->dhd->up = FALSE; + dhd_os_sdunlock(dhdp); + DHD_TRACE(("%s: WLAN OFF DONE\n", __FUNCTION__)); + /* App can now remove power from device */ + } else + bcmerror = BCME_SDIO_ERROR; + } else { + /* App must have restored power to device before calling */ + + DHD_TRACE(("\n\n%s: == WLAN ON ==\n", __FUNCTION__)); + + if (bus->dhd->dongle_reset) { + /* Turn on WLAN */ +#ifdef DHDTHREAD + dhd_os_sdlock(dhdp); +#endif /* DHDTHREAD */ + /* Reset SD client */ + bcmsdh_reset(bus->sdh); + + /* Attempt to re-attach & download */ + if (dhdsdio_probe_attach(bus, bus->dhd->osh, bus->sdh, + (uint32 *)SI_ENUM_BASE, + bus->cl_devid)) { + /* Attempt to download binary to the dongle */ + if (dhdsdio_probe_init(bus, bus->dhd->osh, bus->sdh) && + dhdsdio_download_firmware(bus, bus->dhd->osh, bus->sdh)) { + + /* Re-init bus, enable F2 transfer */ + bcmerror = dhd_bus_init((dhd_pub_t *) bus->dhd, FALSE); + if (bcmerror == BCME_OK) { +#if defined(OOB_INTR_ONLY) + bcmsdh_set_irq(TRUE); + dhd_enable_oob_intr(bus, TRUE); +#endif /* defined(OOB_INTR_ONLY) */ + + bus->dhd->dongle_reset = FALSE; + bus->dhd->up = TRUE; + +#if !defined(IGNORE_ETH0_DOWN) + /* Restore flow control */ + dhd_txflowcontrol(bus->dhd, ALL_INTERFACES, OFF); +#endif + dhd_os_wd_timer(dhdp, dhd_watchdog_ms); + + DHD_TRACE(("%s: WLAN ON DONE\n", __FUNCTION__)); + } else { + dhd_bus_stop(bus, FALSE); + dhdsdio_release_dongle(bus, bus->dhd->osh, + TRUE, FALSE); + } + } else + bcmerror = BCME_SDIO_ERROR; + } else + bcmerror = BCME_SDIO_ERROR; + +#ifdef DHDTHREAD + dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ + } else { + bcmerror = BCME_SDIO_ERROR; + DHD_INFO(("%s called when dongle is not in reset\n", + __FUNCTION__)); + DHD_INFO(("Will call dhd_bus_start instead\n")); + sdioh_start(NULL, 1); + if ((bcmerror = dhd_bus_start(dhdp)) != 0) + DHD_ERROR(("%s: dhd_bus_start fail with %d\n", + __FUNCTION__, bcmerror)); + } + } + return bcmerror; +} + +/* Get Chip ID version */ +uint dhd_bus_chip_id(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus = dhdp->bus; + + return bus->sih->chip; +} + +int +dhd_bus_membytes(dhd_pub_t *dhdp, bool set, uint32 address, uint8 *data, uint size) +{ + dhd_bus_t *bus; + + bus = dhdp->bus; + return dhdsdio_membytes(bus, set, address, data, size); +} diff --git a/drivers/net/wireless/bcmdhd/dhd_wlfc.h b/drivers/net/wireless/bcmdhd/dhd_wlfc.h new file mode 100644 index 0000000000000..c4d251806d780 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dhd_wlfc.h @@ -0,0 +1,276 @@ +/* +* Copyright (C) 1999-2011, Broadcom Corporation +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2 (the "GPL"), +* available at http://www.broadcom.com/licenses/GPLv2.php, with the +* following added to such license: +* +* As a special exception, the copyright holders of this software give you +* permission to link this software with independent modules, and to copy and +* distribute the resulting executable under terms of your choice, provided that +* you also meet, for each linked independent module, the terms and conditions of +* the license of that module. An independent module is a module which is not +* derived from this software. The special exception does not apply to any +* modifications of the software. +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a license +* other than the GPL, without Broadcom's express prior written consent. +* $Id: dhd_wlfc.h 286994 2011-09-29 21:27:44Z $ +* +*/ +#ifndef __wlfc_host_driver_definitions_h__ +#define __wlfc_host_driver_definitions_h__ + +/* 16 bits will provide an absolute max of 65536 slots */ +#define WLFC_HANGER_MAXITEMS 1024 + +#define WLFC_HANGER_ITEM_STATE_FREE 1 +#define WLFC_HANGER_ITEM_STATE_INUSE 2 + +#define WLFC_PKTID_HSLOT_MASK 0xffff /* allow 16 bits only */ +#define WLFC_PKTID_HSLOT_SHIFT 8 + +/* x -> TXSTATUS TAG to/from firmware */ +#define WLFC_PKTID_HSLOT_GET(x) \ + (((x) >> WLFC_PKTID_HSLOT_SHIFT) & WLFC_PKTID_HSLOT_MASK) +#define WLFC_PKTID_HSLOT_SET(var, slot) \ + ((var) = ((var) & ~(WLFC_PKTID_HSLOT_MASK << WLFC_PKTID_HSLOT_SHIFT)) | \ + (((slot) & WLFC_PKTID_HSLOT_MASK) << WLFC_PKTID_HSLOT_SHIFT)) + +#define WLFC_PKTID_FREERUNCTR_MASK 0xff + +#define WLFC_PKTID_FREERUNCTR_GET(x) ((x) & WLFC_PKTID_FREERUNCTR_MASK) +#define WLFC_PKTID_FREERUNCTR_SET(var, ctr) \ + ((var) = (((var) & ~WLFC_PKTID_FREERUNCTR_MASK) | \ + (((ctr) & WLFC_PKTID_FREERUNCTR_MASK)))) + +#define WLFC_PKTQ_PENQ(pq, prec, p) ((pktq_full((pq)) || pktq_pfull((pq), (prec)))? \ + NULL : pktq_penq((pq), (prec), (p))) +#define WLFC_PKTQ_PENQ_HEAD(pq, prec, p) ((pktq_full((pq)) || pktq_pfull((pq), (prec))) ? \ + NULL : pktq_penq_head((pq), (prec), (p))) + +typedef enum ewlfc_packet_state { + eWLFC_PKTTYPE_NEW, + eWLFC_PKTTYPE_DELAYED, + eWLFC_PKTTYPE_SUPPRESSED, + eWLFC_PKTTYPE_MAX +} ewlfc_packet_state_t; + +typedef enum ewlfc_mac_entry_action { + eWLFC_MAC_ENTRY_ACTION_ADD, + eWLFC_MAC_ENTRY_ACTION_DEL, + eWLFC_MAC_ENTRY_ACTION_MAX +} ewlfc_mac_entry_action_t; + +typedef struct wlfc_hanger_item { + uint8 state; + uint8 pad[3]; + uint32 identifier; + void* pkt; +#ifdef PROP_TXSTATUS_DEBUG + uint32 push_time; +#endif +} wlfc_hanger_item_t; + +typedef struct wlfc_hanger { + int max_items; + uint32 pushed; + uint32 popped; + uint32 failed_to_push; + uint32 failed_to_pop; + uint32 failed_slotfind; + wlfc_hanger_item_t items[1]; +} wlfc_hanger_t; + +#define WLFC_HANGER_SIZE(n) ((sizeof(wlfc_hanger_t) - \ + sizeof(wlfc_hanger_item_t)) + ((n)*sizeof(wlfc_hanger_item_t))) + +#define WLFC_STATE_OPEN 1 +#define WLFC_STATE_CLOSE 2 + +#define WLFC_PSQ_PREC_COUNT ((AC_COUNT + 1) * 2) /* 2 for each AC traffic and bc/mc */ +#define WLFC_PSQ_LEN 64 +#define WLFC_SENDQ_LEN 256 + +#define WLFC_FLOWCONTROL_DELTA 8 +#define WLFC_FLOWCONTROL_HIWATER (WLFC_PSQ_LEN - WLFC_FLOWCONTROL_DELTA) +#define WLFC_FLOWCONTROL_LOWATER (WLFC_FLOWCONTROL_HIWATER - WLFC_FLOWCONTROL_DELTA) + +typedef struct wlfc_mac_descriptor { + uint8 occupied; + uint8 interface_id; + uint8 iftype; + uint8 state; + uint8 ac_bitmap; /* for APSD */ + uint8 requested_credit; + uint8 requested_packet; + uint8 ea[ETHER_ADDR_LEN]; + /* + maintain (MAC,AC) based seq count for + packets going to the device. As well as bc/mc. + */ + uint8 seq[AC_COUNT + 1]; + uint8 generation; + struct pktq psq; + /* The AC pending bitmap that was reported to the fw at last change */ + uint8 traffic_lastreported_bmp; + /* The new AC pending bitmap */ + uint8 traffic_pending_bmp; + /* 1= send on next opportunity */ + uint8 send_tim_signal; + uint8 mac_handle; +#ifdef PROP_TXSTATUS_DEBUG + uint32 dstncredit_sent_packets; + uint32 dstncredit_acks; + uint32 opened_ct; + uint32 closed_ct; +#endif +} wlfc_mac_descriptor_t; + +#define WLFC_DECR_SEQCOUNT(entry, prec) do { if (entry->seq[(prec)] == 0) {\ + entry->seq[prec] = 0xff; } else entry->seq[prec]--;} while (0) + +#define WLFC_INCR_SEQCOUNT(entry, prec) entry->seq[(prec)]++ +#define WLFC_SEQCOUNT(entry, prec) entry->seq[(prec)] + +typedef struct athost_wl_stat_counters { + uint32 pktin; + uint32 pkt2bus; + uint32 pktdropped; + uint32 tlv_parse_failed; + uint32 rollback; + uint32 rollback_failed; + uint32 sendq_full_error; + uint32 delayq_full_error; + uint32 credit_request_failed; + uint32 packet_request_failed; + uint32 mac_update_failed; + uint32 psmode_update_failed; + uint32 interface_update_failed; + uint32 wlfc_header_only_pkt; + uint32 txstatus_in; + uint32 d11_suppress; + uint32 wl_suppress; + uint32 bad_suppress; + uint32 pkt_freed; + uint32 pkt_free_err; + uint32 psq_wlsup_retx; + uint32 psq_wlsup_enq; + uint32 psq_d11sup_retx; + uint32 psq_d11sup_enq; + uint32 psq_hostq_retx; + uint32 psq_hostq_enq; + uint32 mac_handle_notfound; + uint32 wlc_tossed_pkts; + uint32 dhd_hdrpulls; + uint32 generic_error; + /* an extra one for bc/mc traffic */ + uint32 sendq_pkts[AC_COUNT + 1]; +#ifdef PROP_TXSTATUS_DEBUG + /* all pkt2bus -> txstatus latency accumulated */ + uint32 latency_sample_count; + uint32 total_status_latency; + uint32 latency_most_recent; + int idx_delta; + uint32 deltas[10]; + uint32 fifo_credits_sent[6]; + uint32 fifo_credits_back[6]; + uint32 dropped_qfull[6]; + uint32 signal_only_pkts_sent; + uint32 signal_only_pkts_freed; +#endif +} athost_wl_stat_counters_t; + +#ifdef PROP_TXSTATUS_DEBUG +#define WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac) do { \ + (ctx)->stats.fifo_credits_sent[(ac)]++;} while (0) +#define WLFC_HOST_FIFO_CREDIT_INC_BACKCTRS(ctx, ac) do { \ + (ctx)->stats.fifo_credits_back[(ac)]++;} while (0) +#define WLFC_HOST_FIFO_DROPPEDCTR_INC(ctx, ac) do { \ + (ctx)->stats.dropped_qfull[(ac)]++;} while (0) +#else +#define WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac) do {} while (0) +#define WLFC_HOST_FIFO_CREDIT_INC_BACKCTRS(ctx, ac) do {} while (0) +#define WLFC_HOST_FIFO_DROPPEDCTR_INC(ctx, ac) do {} while (0) +#endif + +#define WLFC_FCMODE_NONE 0 +#define WLFC_FCMODE_IMPLIED_CREDIT 1 +#define WLFC_FCMODE_EXPLICIT_CREDIT 2 + +/* How long to defer borrowing in milliseconds */ +#define WLFC_BORROW_DEFER_PERIOD_MS 100 + +/* Mask to represent available ACs (note: BC/MC is ignored */ +#define WLFC_AC_MASK 0xF + +/* Mask to check for only on-going AC_BE traffic */ +#define WLFC_AC_BE_TRAFFIC_ONLY 0xD + +typedef struct athost_wl_status_info { + uint8 last_seqid_to_wlc; + + /* OSL handle */ + osl_t* osh; + /* dhd pub */ + void* dhdp; + + /* stats */ + athost_wl_stat_counters_t stats; + + /* the additional ones are for bc/mc and ATIM FIFO */ + int FIFO_credit[AC_COUNT + 2]; + + /* Credit borrow counts for each FIFO from each of the other FIFOs */ + int credits_borrowed[AC_COUNT + 2][AC_COUNT + 2]; + + struct pktq SENDQ; + + /* packet hanger and MAC->handle lookup table */ + void* hanger; + struct { + /* table for individual nodes */ + wlfc_mac_descriptor_t nodes[WLFC_MAC_DESC_TABLE_SIZE]; + /* table for interfaces */ + wlfc_mac_descriptor_t interfaces[WLFC_MAX_IFNUM]; + /* OS may send packets to unknown (unassociated) destinations */ + /* A place holder for bc/mc and packets to unknown destinations */ + wlfc_mac_descriptor_t other; + } destination_entries; + /* token position for different priority packets */ + uint8 token_pos[AC_COUNT+1]; + /* ON/OFF state for flow control to the host network interface */ + uint8 hostif_flow_state[WLFC_MAX_IFNUM]; + uint8 host_ifidx; + /* to flow control an OS interface */ + uint8 toggle_host_if; + + /* + Mode in which the dhd flow control shall operate. Must be set before + traffic starts to the device. + 0 - Do not do any proptxtstatus flow control + 1 - Use implied credit from a packet status + 2 - Use explicit credit + */ + uint8 proptxstatus_mode; + + /* To borrow credits */ + uint8 allow_credit_borrow; + + /* Timestamp to compute how long to defer borrowing for */ + uint32 borrow_defer_timestamp; + +} athost_wl_status_info_t; + +int dhd_wlfc_enable(dhd_pub_t *dhd); +int dhd_wlfc_interface_event(struct dhd_info *, + ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea); +int dhd_wlfc_FIFOcreditmap_event(struct dhd_info *dhd, uint8* event_data); +int dhd_wlfc_event(struct dhd_info *dhd); +int dhd_os_wlfc_block(dhd_pub_t *pub); +int dhd_os_wlfc_unblock(dhd_pub_t *pub); + +#endif /* __wlfc_host_driver_definitions_h__ */ diff --git a/drivers/net/wireless/bcmdhd/dngl_stats.h b/drivers/net/wireless/bcmdhd/dngl_stats.h new file mode 100644 index 0000000000000..9cdf718b39906 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dngl_stats.h @@ -0,0 +1,43 @@ +/* + * Common stats definitions for clients of dongle + * ports + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dngl_stats.h,v 1.5 2008-06-02 16:56:20 Exp $ + */ + +#ifndef _dngl_stats_h_ +#define _dngl_stats_h_ + +typedef struct { + unsigned long rx_packets; /* total packets received */ + unsigned long tx_packets; /* total packets transmitted */ + unsigned long rx_bytes; /* total bytes received */ + unsigned long tx_bytes; /* total bytes transmitted */ + unsigned long rx_errors; /* bad packets received */ + unsigned long tx_errors; /* packet transmit problems */ + unsigned long rx_dropped; /* packets dropped by dongle */ + unsigned long tx_dropped; /* packets dropped by dongle */ + unsigned long multicast; /* multicast packets received */ +} dngl_stats_t; + +#endif /* _dngl_stats_h_ */ diff --git a/drivers/net/wireless/bcmdhd/dngl_wlhdr.h b/drivers/net/wireless/bcmdhd/dngl_wlhdr.h new file mode 100644 index 0000000000000..8b39b9ecb5847 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/dngl_wlhdr.h @@ -0,0 +1,40 @@ +/* + * Dongle WL Header definitions + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dngl_wlhdr.h,v 1.1 2009-01-08 01:21:12 Exp $ + */ + +#ifndef _dngl_wlhdr_h_ +#define _dngl_wlhdr_h_ + +typedef struct wl_header { + uint8 type; /* Header type */ + uint8 version; /* Header version */ + int8 rssi; /* RSSI */ + uint8 pad; /* Unused */ +} wl_header_t; + +#define WL_HEADER_LEN sizeof(wl_header_t) +#define WL_HEADER_TYPE 0 +#define WL_HEADER_VER 1 +#endif /* _dngl_wlhdr_h_ */ diff --git a/drivers/net/wireless/bcmdhd/hndpmu.c b/drivers/net/wireless/bcmdhd/hndpmu.c new file mode 100644 index 0000000000000..0e493343c8069 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/hndpmu.c @@ -0,0 +1,196 @@ +/* + * Misc utility routines for accessing PMU corerev specific features + * of the SiliconBackplane-based Broadcom chips. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: hndpmu.c,v 1.228.2.56 2011-02-11 22:49:07 $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PMU_ERROR(args) + +#define PMU_MSG(args) + +/* To check in verbose debugging messages not intended + * to be on except on private builds. + */ +#define PMU_NONE(args) + + +/* SDIO Pad drive strength to select value mappings. + * The last strength value in each table must be 0 (the tri-state value). + */ +typedef struct { + uint8 strength; /* Pad Drive Strength in mA */ + uint8 sel; /* Chip-specific select value */ +} sdiod_drive_str_t; + +/* SDIO Drive Strength to sel value table for PMU Rev 1 */ +static const sdiod_drive_str_t sdiod_drive_strength_tab1[] = { + {4, 0x2}, + {2, 0x3}, + {1, 0x0}, + {0, 0x0} }; + +/* SDIO Drive Strength to sel value table for PMU Rev 2, 3 */ +static const sdiod_drive_str_t sdiod_drive_strength_tab2[] = { + {12, 0x7}, + {10, 0x6}, + {8, 0x5}, + {6, 0x4}, + {4, 0x2}, + {2, 0x1}, + {0, 0x0} }; + +/* SDIO Drive Strength to sel value table for PMU Rev 8 (1.8V) */ +static const sdiod_drive_str_t sdiod_drive_strength_tab3[] = { + {32, 0x7}, + {26, 0x6}, + {22, 0x5}, + {16, 0x4}, + {12, 0x3}, + {8, 0x2}, + {4, 0x1}, + {0, 0x0} }; + +/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8v) */ +static const sdiod_drive_str_t sdiod_drive_strength_tab4_1v8[] = { + {32, 0x6}, + {26, 0x7}, + {22, 0x4}, + {16, 0x5}, + {12, 0x2}, + {8, 0x3}, + {4, 0x0}, + {0, 0x1} }; + +/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.2v) */ + +/* SDIO Drive Strength to sel value table for PMU Rev 11 (2.5v) */ + +/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */ +static const sdiod_drive_str_t sdiod_drive_strength_tab5_1v8[] = { + {6, 0x7}, + {5, 0x6}, + {4, 0x5}, + {3, 0x4}, + {2, 0x2}, + {1, 0x1}, + {0, 0x0} }; + +/* SDIO Drive Strength to sel value table for PMU Rev 13 (3.3v) */ + + +#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) + +void +si_sdiod_drive_strength_init(si_t *sih, osl_t *osh, uint32 drivestrength) +{ + chipcregs_t *cc; + uint origidx, intr_val = 0; + sdiod_drive_str_t *str_tab = NULL; + uint32 str_mask = 0; + uint32 str_shift = 0; + + if (!(sih->cccaps & CC_CAP_PMU)) { + return; + } + + /* Remember original core before switch to chipc */ + cc = (chipcregs_t *) si_switch_core(sih, CC_CORE_ID, &origidx, &intr_val); + + switch (SDIOD_DRVSTR_KEY(sih->chip, sih->pmurev)) { + case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 1): + str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab1; + str_mask = 0x30000000; + str_shift = 28; + break; + case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 2): + case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 3): + case SDIOD_DRVSTR_KEY(BCM4315_CHIP_ID, 4): + str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab2; + str_mask = 0x00003800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BCM4336_CHIP_ID, 8): + case SDIOD_DRVSTR_KEY(BCM4336_CHIP_ID, 11): + if (sih->pmurev == 8) { + str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab3; + } + else if (sih->pmurev == 11) { + str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab4_1v8; + } + str_mask = 0x00003800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12): + str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab4_1v8; + str_mask = 0x00003800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13): + str_tab = (sdiod_drive_str_t *)&sdiod_drive_strength_tab5_1v8; + str_mask = 0x00003800; + str_shift = 11; + break; + default: + PMU_MSG(("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", + bcm_chipname(sih->chip, chn, 8), sih->chiprev, sih->pmurev)); + + break; + } + + if (str_tab != NULL) { + uint32 cc_data_temp; + int i; + + /* Pick the lowest available drive strength equal or greater than the + * requested strength. Drive strength of 0 requests tri-state. + */ + for (i = 0; drivestrength < str_tab[i].strength; i++) + ; + + if (i > 0 && drivestrength > str_tab[i].strength) + i--; + + W_REG(osh, &cc->chipcontrol_addr, 1); + cc_data_temp = R_REG(osh, &cc->chipcontrol_data); + cc_data_temp &= ~str_mask; + cc_data_temp |= str_tab[i].sel << str_shift; + W_REG(osh, &cc->chipcontrol_data, cc_data_temp); + + PMU_MSG(("SDIO: %dmA drive strength requested; set to %dmA\n", + drivestrength, str_tab[i].strength)); + } + + /* Return to original core */ + si_restore_core(sih, origidx, intr_val); +} diff --git a/drivers/net/wireless/bcmdhd/include/Makefile b/drivers/net/wireless/bcmdhd/include/Makefile new file mode 100644 index 0000000000000..67c4906f58897 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/Makefile @@ -0,0 +1,53 @@ +#!/bin/bash +# +# This script serves following purpose: +# +# 1. It generates native version information by querying +# automerger maintained database to see where src/include +# came from +# 2. For select components, as listed in compvers.sh +# it generates component version files +# +# Copyright 2005, Broadcom, Inc. +# +# $Id: Makefile 241702 2011-02-19 00:41:03Z $ +# + +SRCBASE := .. + +TARGETS := epivers.h + +ifdef VERBOSE +export VERBOSE +endif + +all release: epivers compvers + +# Generate epivers.h for native branch version +epivers: + bash epivers.sh + +# Generate epivers.h for native branch version +compvers: + @if [ -s "compvers.sh" ]; then \ + echo "Generating component versions, if any"; \ + bash compvers.sh; \ + else \ + echo "Skipping component version generation"; \ + fi + +# Generate epivers.h for native branch version +clean_compvers: + @if [ -s "compvers.sh" ]; then \ + echo "bash compvers.sh clean"; \ + bash compvers.sh clean; \ + else \ + echo "Skipping component version clean"; \ + fi + +clean: + rm -f $(TARGETS) *.prev + +clean_all: clean clean_compvers + +.PHONY: all release clean epivers compvers clean_compvers diff --git a/drivers/net/wireless/bcmdhd/include/aidmp.h b/drivers/net/wireless/bcmdhd/include/aidmp.h new file mode 100644 index 0000000000000..b993a033abc20 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/aidmp.h @@ -0,0 +1,377 @@ +/* + * Broadcom AMBA Interconnect definitions. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: aidmp.h 277737 2011-08-16 17:54:59Z $ + */ + + +#ifndef _AIDMP_H +#define _AIDMP_H + + +#define MFGID_ARM 0x43b +#define MFGID_BRCM 0x4bf +#define MFGID_MIPS 0x4a7 + + +#define CC_SIM 0 +#define CC_EROM 1 +#define CC_CORESIGHT 9 +#define CC_VERIF 0xb +#define CC_OPTIMO 0xd +#define CC_GEN 0xe +#define CC_PRIMECELL 0xf + + +#define ER_EROMENTRY 0x000 +#define ER_REMAPCONTROL 0xe00 +#define ER_REMAPSELECT 0xe04 +#define ER_MASTERSELECT 0xe10 +#define ER_ITCR 0xf00 +#define ER_ITIP 0xf04 + + +#define ER_TAG 0xe +#define ER_TAG1 0x6 +#define ER_VALID 1 +#define ER_CI 0 +#define ER_MP 2 +#define ER_ADD 4 +#define ER_END 0xe +#define ER_BAD 0xffffffff + + +#define CIA_MFG_MASK 0xfff00000 +#define CIA_MFG_SHIFT 20 +#define CIA_CID_MASK 0x000fff00 +#define CIA_CID_SHIFT 8 +#define CIA_CCL_MASK 0x000000f0 +#define CIA_CCL_SHIFT 4 + + +#define CIB_REV_MASK 0xff000000 +#define CIB_REV_SHIFT 24 +#define CIB_NSW_MASK 0x00f80000 +#define CIB_NSW_SHIFT 19 +#define CIB_NMW_MASK 0x0007c000 +#define CIB_NMW_SHIFT 14 +#define CIB_NSP_MASK 0x00003e00 +#define CIB_NSP_SHIFT 9 +#define CIB_NMP_MASK 0x000001f0 +#define CIB_NMP_SHIFT 4 + + +#define MPD_MUI_MASK 0x0000ff00 +#define MPD_MUI_SHIFT 8 +#define MPD_MP_MASK 0x000000f0 +#define MPD_MP_SHIFT 4 + + +#define AD_ADDR_MASK 0xfffff000 +#define AD_SP_MASK 0x00000f00 +#define AD_SP_SHIFT 8 +#define AD_ST_MASK 0x000000c0 +#define AD_ST_SHIFT 6 +#define AD_ST_SLAVE 0x00000000 +#define AD_ST_BRIDGE 0x00000040 +#define AD_ST_SWRAP 0x00000080 +#define AD_ST_MWRAP 0x000000c0 +#define AD_SZ_MASK 0x00000030 +#define AD_SZ_SHIFT 4 +#define AD_SZ_4K 0x00000000 +#define AD_SZ_8K 0x00000010 +#define AD_SZ_16K 0x00000020 +#define AD_SZ_SZD 0x00000030 +#define AD_AG32 0x00000008 +#define AD_ADDR_ALIGN 0x00000fff +#define AD_SZ_BASE 0x00001000 + + +#define SD_SZ_MASK 0xfffff000 +#define SD_SG32 0x00000008 +#define SD_SZ_ALIGN 0x00000fff + + +#ifndef _LANGUAGE_ASSEMBLY + +typedef volatile struct _aidmp { + uint32 oobselina30; + uint32 oobselina74; + uint32 PAD[6]; + uint32 oobselinb30; + uint32 oobselinb74; + uint32 PAD[6]; + uint32 oobselinc30; + uint32 oobselinc74; + uint32 PAD[6]; + uint32 oobselind30; + uint32 oobselind74; + uint32 PAD[38]; + uint32 oobselouta30; + uint32 oobselouta74; + uint32 PAD[6]; + uint32 oobseloutb30; + uint32 oobseloutb74; + uint32 PAD[6]; + uint32 oobseloutc30; + uint32 oobseloutc74; + uint32 PAD[6]; + uint32 oobseloutd30; + uint32 oobseloutd74; + uint32 PAD[38]; + uint32 oobsynca; + uint32 oobseloutaen; + uint32 PAD[6]; + uint32 oobsyncb; + uint32 oobseloutben; + uint32 PAD[6]; + uint32 oobsyncc; + uint32 oobseloutcen; + uint32 PAD[6]; + uint32 oobsyncd; + uint32 oobseloutden; + uint32 PAD[38]; + uint32 oobaextwidth; + uint32 oobainwidth; + uint32 oobaoutwidth; + uint32 PAD[5]; + uint32 oobbextwidth; + uint32 oobbinwidth; + uint32 oobboutwidth; + uint32 PAD[5]; + uint32 oobcextwidth; + uint32 oobcinwidth; + uint32 oobcoutwidth; + uint32 PAD[5]; + uint32 oobdextwidth; + uint32 oobdinwidth; + uint32 oobdoutwidth; + uint32 PAD[37]; + uint32 ioctrlset; + uint32 ioctrlclear; + uint32 ioctrl; + uint32 PAD[61]; + uint32 iostatus; + uint32 PAD[127]; + uint32 ioctrlwidth; + uint32 iostatuswidth; + uint32 PAD[62]; + uint32 resetctrl; + uint32 resetstatus; + uint32 resetreadid; + uint32 resetwriteid; + uint32 PAD[60]; + uint32 errlogctrl; + uint32 errlogdone; + uint32 errlogstatus; + uint32 errlogaddrlo; + uint32 errlogaddrhi; + uint32 errlogid; + uint32 errloguser; + uint32 errlogflags; + uint32 PAD[56]; + uint32 intstatus; + uint32 PAD[127]; + uint32 config; + uint32 PAD[63]; + uint32 itcr; + uint32 PAD[3]; + uint32 itipooba; + uint32 itipoobb; + uint32 itipoobc; + uint32 itipoobd; + uint32 PAD[4]; + uint32 itipoobaout; + uint32 itipoobbout; + uint32 itipoobcout; + uint32 itipoobdout; + uint32 PAD[4]; + uint32 itopooba; + uint32 itopoobb; + uint32 itopoobc; + uint32 itopoobd; + uint32 PAD[4]; + uint32 itopoobain; + uint32 itopoobbin; + uint32 itopoobcin; + uint32 itopoobdin; + uint32 PAD[4]; + uint32 itopreset; + uint32 PAD[15]; + uint32 peripherialid4; + uint32 peripherialid5; + uint32 peripherialid6; + uint32 peripherialid7; + uint32 peripherialid0; + uint32 peripherialid1; + uint32 peripherialid2; + uint32 peripherialid3; + uint32 componentid0; + uint32 componentid1; + uint32 componentid2; + uint32 componentid3; +} aidmp_t; + +#endif + + +#define OOB_BUSCONFIG 0x020 +#define OOB_STATUSA 0x100 +#define OOB_STATUSB 0x104 +#define OOB_STATUSC 0x108 +#define OOB_STATUSD 0x10c +#define OOB_ENABLEA0 0x200 +#define OOB_ENABLEA1 0x204 +#define OOB_ENABLEA2 0x208 +#define OOB_ENABLEA3 0x20c +#define OOB_ENABLEB0 0x280 +#define OOB_ENABLEB1 0x284 +#define OOB_ENABLEB2 0x288 +#define OOB_ENABLEB3 0x28c +#define OOB_ENABLEC0 0x300 +#define OOB_ENABLEC1 0x304 +#define OOB_ENABLEC2 0x308 +#define OOB_ENABLEC3 0x30c +#define OOB_ENABLED0 0x380 +#define OOB_ENABLED1 0x384 +#define OOB_ENABLED2 0x388 +#define OOB_ENABLED3 0x38c +#define OOB_ITCR 0xf00 +#define OOB_ITIPOOBA 0xf10 +#define OOB_ITIPOOBB 0xf14 +#define OOB_ITIPOOBC 0xf18 +#define OOB_ITIPOOBD 0xf1c +#define OOB_ITOPOOBA 0xf30 +#define OOB_ITOPOOBB 0xf34 +#define OOB_ITOPOOBC 0xf38 +#define OOB_ITOPOOBD 0xf3c + + +#define AI_OOBSELINA30 0x000 +#define AI_OOBSELINA74 0x004 +#define AI_OOBSELINB30 0x020 +#define AI_OOBSELINB74 0x024 +#define AI_OOBSELINC30 0x040 +#define AI_OOBSELINC74 0x044 +#define AI_OOBSELIND30 0x060 +#define AI_OOBSELIND74 0x064 +#define AI_OOBSELOUTA30 0x100 +#define AI_OOBSELOUTA74 0x104 +#define AI_OOBSELOUTB30 0x120 +#define AI_OOBSELOUTB74 0x124 +#define AI_OOBSELOUTC30 0x140 +#define AI_OOBSELOUTC74 0x144 +#define AI_OOBSELOUTD30 0x160 +#define AI_OOBSELOUTD74 0x164 +#define AI_OOBSYNCA 0x200 +#define AI_OOBSELOUTAEN 0x204 +#define AI_OOBSYNCB 0x220 +#define AI_OOBSELOUTBEN 0x224 +#define AI_OOBSYNCC 0x240 +#define AI_OOBSELOUTCEN 0x244 +#define AI_OOBSYNCD 0x260 +#define AI_OOBSELOUTDEN 0x264 +#define AI_OOBAEXTWIDTH 0x300 +#define AI_OOBAINWIDTH 0x304 +#define AI_OOBAOUTWIDTH 0x308 +#define AI_OOBBEXTWIDTH 0x320 +#define AI_OOBBINWIDTH 0x324 +#define AI_OOBBOUTWIDTH 0x328 +#define AI_OOBCEXTWIDTH 0x340 +#define AI_OOBCINWIDTH 0x344 +#define AI_OOBCOUTWIDTH 0x348 +#define AI_OOBDEXTWIDTH 0x360 +#define AI_OOBDINWIDTH 0x364 +#define AI_OOBDOUTWIDTH 0x368 + + +#define AI_IOCTRLSET 0x400 +#define AI_IOCTRLCLEAR 0x404 +#define AI_IOCTRL 0x408 +#define AI_IOSTATUS 0x500 +#define AI_RESETCTRL 0x800 +#define AI_RESETSTATUS 0x804 + + +#define AI_IOCTRLWIDTH 0x700 +#define AI_IOSTATUSWIDTH 0x704 + +#define AI_RESETREADID 0x808 +#define AI_RESETWRITEID 0x80c +#define AI_ERRLOGCTRL 0xa00 +#define AI_ERRLOGDONE 0xa04 +#define AI_ERRLOGSTATUS 0xa08 +#define AI_ERRLOGADDRLO 0xa0c +#define AI_ERRLOGADDRHI 0xa10 +#define AI_ERRLOGID 0xa14 +#define AI_ERRLOGUSER 0xa18 +#define AI_ERRLOGFLAGS 0xa1c +#define AI_INTSTATUS 0xa00 +#define AI_CONFIG 0xe00 +#define AI_ITCR 0xf00 +#define AI_ITIPOOBA 0xf10 +#define AI_ITIPOOBB 0xf14 +#define AI_ITIPOOBC 0xf18 +#define AI_ITIPOOBD 0xf1c +#define AI_ITIPOOBAOUT 0xf30 +#define AI_ITIPOOBBOUT 0xf34 +#define AI_ITIPOOBCOUT 0xf38 +#define AI_ITIPOOBDOUT 0xf3c +#define AI_ITOPOOBA 0xf50 +#define AI_ITOPOOBB 0xf54 +#define AI_ITOPOOBC 0xf58 +#define AI_ITOPOOBD 0xf5c +#define AI_ITOPOOBAIN 0xf70 +#define AI_ITOPOOBBIN 0xf74 +#define AI_ITOPOOBCIN 0xf78 +#define AI_ITOPOOBDIN 0xf7c +#define AI_ITOPRESET 0xf90 +#define AI_PERIPHERIALID4 0xfd0 +#define AI_PERIPHERIALID5 0xfd4 +#define AI_PERIPHERIALID6 0xfd8 +#define AI_PERIPHERIALID7 0xfdc +#define AI_PERIPHERIALID0 0xfe0 +#define AI_PERIPHERIALID1 0xfe4 +#define AI_PERIPHERIALID2 0xfe8 +#define AI_PERIPHERIALID3 0xfec +#define AI_COMPONENTID0 0xff0 +#define AI_COMPONENTID1 0xff4 +#define AI_COMPONENTID2 0xff8 +#define AI_COMPONENTID3 0xffc + + +#define AIRC_RESET 1 + + +#define AICFG_OOB 0x00000020 +#define AICFG_IOS 0x00000010 +#define AICFG_IOC 0x00000008 +#define AICFG_TO 0x00000004 +#define AICFG_ERRL 0x00000002 +#define AICFG_RST 0x00000001 + + +#define OOB_SEL_OUTEN_B_5 15 +#define OOB_SEL_OUTEN_B_6 23 + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/bcmcdc.h b/drivers/net/wireless/bcmdhd/include/bcmcdc.h new file mode 100644 index 0000000000000..77a20f87b7eaf --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmcdc.h @@ -0,0 +1,121 @@ +/* + * CDC network driver ioctl/indication encoding + * Broadcom 802.11abg Networking Device Driver + * + * Definitions subject to change without notice. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmcdc.h 277737 2011-08-16 17:54:59Z $ + */ + +#ifndef _bcmcdc_h_ +#define _bcmcdc_h_ +#include + +typedef struct cdc_ioctl { + uint32 cmd; + uint32 len; + uint32 flags; + uint32 status; +} cdc_ioctl_t; + + +#define CDC_MAX_MSG_SIZE ETHER_MAX_LEN + + +#define CDCL_IOC_OUTLEN_MASK 0x0000FFFF + +#define CDCL_IOC_OUTLEN_SHIFT 0 +#define CDCL_IOC_INLEN_MASK 0xFFFF0000 +#define CDCL_IOC_INLEN_SHIFT 16 + + +#define CDCF_IOC_ERROR 0x01 +#define CDCF_IOC_SET 0x02 +#define CDCF_IOC_OVL_IDX_MASK 0x3c +#define CDCF_IOC_OVL_RSV 0x40 +#define CDCF_IOC_OVL 0x80 +#define CDCF_IOC_ACTION_MASK 0xfe +#define CDCF_IOC_ACTION_SHIFT 1 +#define CDCF_IOC_IF_MASK 0xF000 +#define CDCF_IOC_IF_SHIFT 12 +#define CDCF_IOC_ID_MASK 0xFFFF0000 +#define CDCF_IOC_ID_SHIFT 16 + +#define CDC_IOC_IF_IDX(flags) (((flags) & CDCF_IOC_IF_MASK) >> CDCF_IOC_IF_SHIFT) +#define CDC_IOC_ID(flags) (((flags) & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT) + +#define CDC_GET_IF_IDX(hdr) \ + ((int)((((hdr)->flags) & CDCF_IOC_IF_MASK) >> CDCF_IOC_IF_SHIFT)) +#define CDC_SET_IF_IDX(hdr, idx) \ + ((hdr)->flags = (((hdr)->flags & ~CDCF_IOC_IF_MASK) | ((idx) << CDCF_IOC_IF_SHIFT))) + + + +#define BDC_HEADER_LEN 4 + +#define BDC_PROTO_VER_1 1 +#define BDC_PROTO_VER 2 + +#define BDC_FLAG_VER_MASK 0xf0 +#define BDC_FLAG_VER_SHIFT 4 + +#define BDC_FLAG__UNUSED 0x03 +#define BDC_FLAG_SUM_GOOD 0x04 +#define BDC_FLAG_SUM_NEEDED 0x08 + +#define BDC_PRIORITY_MASK 0x7 + +#define BDC_FLAG2_FC_FLAG 0x10 + +#define BDC_PRIORITY_FC_SHIFT 4 + +#define BDC_FLAG2_IF_MASK 0x0f +#define BDC_FLAG2_IF_SHIFT 0 +#define BDC_FLAG2_PAD_MASK 0xf0 +#define BDC_FLAG_PAD_MASK 0x03 +#define BDC_FLAG2_PAD_SHIFT 2 +#define BDC_FLAG_PAD_SHIFT 0 +#define BDC_FLAG2_PAD_IDX 0x3c +#define BDC_FLAG_PAD_IDX 0x03 +#define BDC_GET_PAD_LEN(hdr) \ + ((int)(((((hdr)->flags2) & BDC_FLAG2_PAD_MASK) >> BDC_FLAG2_PAD_SHIFT) | \ + ((((hdr)->flags) & BDC_FLAG_PAD_MASK) >> BDC_FLAG_PAD_SHIFT))) +#define BDC_SET_PAD_LEN(hdr, idx) \ + ((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_PAD_MASK) | \ + (((idx) & BDC_FLAG2_PAD_IDX) << BDC_FLAG2_PAD_SHIFT))); \ + ((hdr)->flags = (((hdr)->flags & ~BDC_FLAG_PAD_MASK) | \ + (((idx) & BDC_FLAG_PAD_IDX) << BDC_FLAG_PAD_SHIFT))) + +#define BDC_GET_IF_IDX(hdr) \ + ((int)((((hdr)->flags2) & BDC_FLAG2_IF_MASK) >> BDC_FLAG2_IF_SHIFT)) +#define BDC_SET_IF_IDX(hdr, idx) \ + ((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | ((idx) << BDC_FLAG2_IF_SHIFT))) + +struct bdc_header { + uint8 flags; + uint8 priority; + uint8 flags2; + uint8 dataOffset; +}; + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/bcmdefs.h b/drivers/net/wireless/bcmdhd/include/bcmdefs.h new file mode 100644 index 0000000000000..17cc0e955f62a --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmdefs.h @@ -0,0 +1,231 @@ +/* + * Misc system wide definitions + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmdefs.h 279282 2011-08-23 22:44:02Z $ + */ + + +#ifndef _bcmdefs_h_ +#define _bcmdefs_h_ + + + + +#define BCM_REFERENCE(data) ((void)(data)) + + + +#define bcmreclaimed 0 +#define _data _data +#define _fn _fn +#define BCMPREATTACHDATA(_data) _data +#define BCMPREATTACHFN(_fn) _fn +#define _data _data +#define _fn _fn +#define _fn _fn +#define BCMNMIATTACHFN(_fn) _fn +#define BCMNMIATTACHDATA(_data) _data +#define BCMOVERLAY0DATA(_sym) _sym +#define BCMOVERLAY0FN(_fn) _fn +#define BCMOVERLAY1DATA(_sym) _sym +#define BCMOVERLAY1FN(_fn) _fn +#define BCMOVERLAYERRFN(_fn) _fn +#define CONST const +#define BCMFASTPATH + + + + +#define _data _data +#define BCMROMDAT_NAME(_data) _data +#define _fn _fn +#define _fn _fn +#define STATIC static +#define BCMROMDAT_ARYSIZ(data) ARRAYSIZE(data) +#define BCMROMDAT_SIZEOF(data) sizeof(data) +#define BCMROMDAT_APATCH(data) +#define BCMROMDAT_SPATCH(data) + + + +#define OVERLAY_INLINE +#define OSTATIC static +#define BCMOVERLAYDATA(_ovly, _sym) _sym +#define BCMOVERLAYFN(_ovly, _fn) _fn +#define BCMOVERLAYERRFN(_fn) _fn +#define BCMROMOVERLAYDATA(_ovly, _data) _data +#define BCMROMOVERLAYFN(_ovly, _fn) _fn +#define BCMATTACHOVERLAYDATA(_ovly, _sym) _sym +#define BCMATTACHOVERLAYFN(_ovly, _fn) _fn +#define BCMINITOVERLAYDATA(_ovly, _sym) _sym +#define BCMINITOVERLAYFN(_ovly, _fn) _fn +#define BCMUNINITOVERLAYFN(_ovly, _fn) _fn + + + +#define SI_BUS 0 +#define PCI_BUS 1 +#define PCMCIA_BUS 2 +#define SDIO_BUS 3 +#define JTAG_BUS 4 +#define USB_BUS 5 +#define SPI_BUS 6 +#define RPC_BUS 7 + + +#ifdef BCMBUSTYPE +#define BUSTYPE(bus) (BCMBUSTYPE) +#else +#define BUSTYPE(bus) (bus) +#endif + + +#ifdef BCMCHIPTYPE +#define CHIPTYPE(bus) (BCMCHIPTYPE) +#else +#define CHIPTYPE(bus) (bus) +#endif + + + +#if defined(BCMSPROMBUS) +#define SPROMBUS (BCMSPROMBUS) +#elif defined(SI_PCMCIA_SROM) +#define SPROMBUS (PCMCIA_BUS) +#else +#define SPROMBUS (PCI_BUS) +#endif + + +#ifdef BCMCHIPID +#define CHIPID(chip) (BCMCHIPID) +#else +#define CHIPID(chip) (chip) +#endif + +#ifdef BCMCHIPREV +#define CHIPREV(rev) (BCMCHIPREV) +#else +#define CHIPREV(rev) (rev) +#endif + + +#define DMADDR_MASK_32 0x0 +#define DMADDR_MASK_30 0xc0000000 +#define DMADDR_MASK_0 0xffffffff + +#define DMADDRWIDTH_30 30 +#define DMADDRWIDTH_32 32 +#define DMADDRWIDTH_63 63 +#define DMADDRWIDTH_64 64 + +#ifdef BCMDMA64OSL +typedef struct { + uint32 loaddr; + uint32 hiaddr; +} dma64addr_t; + +typedef dma64addr_t dmaaddr_t; +#define PHYSADDRHI(_pa) ((_pa).hiaddr) +#define PHYSADDRHISET(_pa, _val) \ + do { \ + (_pa).hiaddr = (_val); \ + } while (0) +#define PHYSADDRLO(_pa) ((_pa).loaddr) +#define PHYSADDRLOSET(_pa, _val) \ + do { \ + (_pa).loaddr = (_val); \ + } while (0) + +#else +typedef unsigned long dmaaddr_t; +#define PHYSADDRHI(_pa) (0) +#define PHYSADDRHISET(_pa, _val) +#define PHYSADDRLO(_pa) ((_pa)) +#define PHYSADDRLOSET(_pa, _val) \ + do { \ + (_pa) = (_val); \ + } while (0) +#endif + + +typedef struct { + dmaaddr_t addr; + uint32 length; +} hnddma_seg_t; + +#define MAX_DMA_SEGS 4 + + +typedef struct { + void *oshdmah; + uint origsize; + uint nsegs; + hnddma_seg_t segs[MAX_DMA_SEGS]; +} hnddma_seg_map_t; + + + + +#if defined(BCM_RPC_NOCOPY) || defined(BCM_RCP_TXNOCOPY) + +#define BCMEXTRAHDROOM 220 +#else +#define BCMEXTRAHDROOM 172 +#endif + + +#define BCMDONGLEHDRSZ 12 +#define BCMDONGLEPADSZ 16 + +#define BCMDONGLEOVERHEAD (BCMDONGLEHDRSZ + BCMDONGLEPADSZ) + + +#if defined(BCMASSERT_LOG) +#define BCMASSERT_SUPPORT +#endif + + +#define BITFIELD_MASK(width) \ + (((unsigned)1 << (width)) - 1) +#define GFIELD(val, field) \ + (((val) >> field ## _S) & field ## _M) +#define SFIELD(val, field, bits) \ + (((val) & (~(field ## _M << field ## _S))) | \ + ((unsigned)(bits) << field ## _S)) + + +#ifdef BCMSMALL +#undef BCMSPACE +#define bcmspace FALSE +#else +#define BCMSPACE +#define bcmspace TRUE +#endif + + +#define MAXSZ_NVRAM_VARS 4096 + +#define LOCATOR_EXTERN static + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/bcmdevs.h b/drivers/net/wireless/bcmdhd/include/bcmdevs.h new file mode 100644 index 0000000000000..cdfc5fe6c8fda --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmdevs.h @@ -0,0 +1,747 @@ +/* + * Broadcom device-specific manifest constants. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmdevs.h 332966 2012-05-11 22:40:21Z $ + */ + + +#ifndef _BCMDEVS_H +#define _BCMDEVS_H + + +#define VENDOR_EPIGRAM 0xfeda +#define VENDOR_BROADCOM 0x14e4 +#define VENDOR_3COM 0x10b7 +#define VENDOR_NETGEAR 0x1385 +#define VENDOR_DIAMOND 0x1092 +#define VENDOR_INTEL 0x8086 +#define VENDOR_DELL 0x1028 +#define VENDOR_HP 0x103c +#define VENDOR_HP_COMPAQ 0x0e11 +#define VENDOR_APPLE 0x106b +#define VENDOR_SI_IMAGE 0x1095 +#define VENDOR_BUFFALO 0x1154 +#define VENDOR_TI 0x104c +#define VENDOR_RICOH 0x1180 +#define VENDOR_JMICRON 0x197b + + + +#define VENDOR_BROADCOM_PCMCIA 0x02d0 + + +#define VENDOR_BROADCOM_SDIO 0x00BF + + +#define BCM_DNGL_VID 0x0a5c +#define BCM_DNGL_BL_PID_4328 0xbd12 +#define BCM_DNGL_BL_PID_4322 0xbd13 +#define BCM_DNGL_BL_PID_4319 0xbd16 +#define BCM_DNGL_BL_PID_43236 0xbd17 +#define BCM_DNGL_BL_PID_4332 0xbd18 +#define BCM_DNGL_BL_PID_4330 0xbd19 +#define BCM_DNGL_BL_PID_43239 0xbd1b +#define BCM_DNGL_BDC_PID 0x0bdc +#define BCM_DNGL_JTAG_PID 0x4a44 +#define BCM_DNGL_BL_PID_4324 0xbd1c + + +#define BCM_HWUSB_PID_43239 43239 + + +#define BCM4210_DEVICE_ID 0x1072 +#define BCM4230_DEVICE_ID 0x1086 +#define BCM4401_ENET_ID 0x170c +#define BCM3352_DEVICE_ID 0x3352 +#define BCM3360_DEVICE_ID 0x3360 +#define BCM4211_DEVICE_ID 0x4211 +#define BCM4231_DEVICE_ID 0x4231 +#define BCM4303_D11B_ID 0x4303 +#define BCM4311_D11G_ID 0x4311 +#define BCM4311_D11DUAL_ID 0x4312 +#define BCM4311_D11A_ID 0x4313 +#define BCM4328_D11DUAL_ID 0x4314 +#define BCM4328_D11G_ID 0x4315 +#define BCM4328_D11A_ID 0x4316 +#define BCM4318_D11G_ID 0x4318 +#define BCM4318_D11DUAL_ID 0x4319 +#define BCM4318_D11A_ID 0x431a +#define BCM4325_D11DUAL_ID 0x431b +#define BCM4325_D11G_ID 0x431c +#define BCM4325_D11A_ID 0x431d +#define BCM4306_D11G_ID 0x4320 +#define BCM4306_D11A_ID 0x4321 +#define BCM4306_UART_ID 0x4322 +#define BCM4306_V90_ID 0x4323 +#define BCM4306_D11DUAL_ID 0x4324 +#define BCM4306_D11G_ID2 0x4325 +#define BCM4321_D11N_ID 0x4328 +#define BCM4321_D11N2G_ID 0x4329 +#define BCM4321_D11N5G_ID 0x432a +#define BCM4322_D11N_ID 0x432b +#define BCM4322_D11N2G_ID 0x432c +#define BCM4322_D11N5G_ID 0x432d +#define BCM4329_D11N_ID 0x432e +#define BCM4329_D11N2G_ID 0x432f +#define BCM4329_D11N5G_ID 0x4330 +#define BCM4315_D11DUAL_ID 0x4334 +#define BCM4315_D11G_ID 0x4335 +#define BCM4315_D11A_ID 0x4336 +#define BCM4319_D11N_ID 0x4337 +#define BCM4319_D11N2G_ID 0x4338 +#define BCM4319_D11N5G_ID 0x4339 +#define BCM43231_D11N2G_ID 0x4340 +#define BCM43221_D11N2G_ID 0x4341 +#define BCM43222_D11N_ID 0x4350 +#define BCM43222_D11N2G_ID 0x4351 +#define BCM43222_D11N5G_ID 0x4352 +#define BCM43224_D11N_ID 0x4353 +#define BCM43224_D11N_ID_VEN1 0x0576 +#define BCM43226_D11N_ID 0x4354 +#define BCM43236_D11N_ID 0x4346 +#define BCM43236_D11N2G_ID 0x4347 +#define BCM43236_D11N5G_ID 0x4348 +#define BCM43225_D11N2G_ID 0x4357 +#define BCM43421_D11N_ID 0xA99D +#define BCM4313_D11N2G_ID 0x4727 +#define BCM4330_D11N_ID 0x4360 +#define BCM4330_D11N2G_ID 0x4361 +#define BCM4330_D11N5G_ID 0x4362 +#define BCM4336_D11N_ID 0x4343 +#define BCM6362_D11N_ID 0x435f +#define BCM4331_D11N_ID 0x4331 +#define BCM4331_D11N2G_ID 0x4332 +#define BCM4331_D11N5G_ID 0x4333 +#define BCM43237_D11N_ID 0x4355 +#define BCM43237_D11N5G_ID 0x4356 +#define BCM43227_D11N2G_ID 0x4358 +#define BCM43228_D11N_ID 0x4359 +#define BCM43228_D11N5G_ID 0x435a +#define BCM43362_D11N_ID 0x4363 +#define BCM43239_D11N_ID 0x4370 +#define BCM4324_D11N_ID 0x4374 +#define BCM43217_D11N2G_ID 0x43a9 +#define BCM43131_D11N2G_ID 0x43aa + +#define BCM4314_D11N2G_ID 0x4364 +#define BCM43142_D11N2G_ID 0x4365 + +#define BCMGPRS_UART_ID 0x4333 +#define BCMGPRS2_UART_ID 0x4344 +#define FPGA_JTAGM_ID 0x43f0 +#define BCM_JTAGM_ID 0x43f1 +#define SDIOH_FPGA_ID 0x43f2 +#define BCM_SDIOH_ID 0x43f3 +#define SDIOD_FPGA_ID 0x43f4 +#define SPIH_FPGA_ID 0x43f5 +#define BCM_SPIH_ID 0x43f6 +#define MIMO_FPGA_ID 0x43f8 +#define BCM_JTAGM2_ID 0x43f9 +#define SDHCI_FPGA_ID 0x43fa +#define BCM4402_ENET_ID 0x4402 +#define BCM4402_V90_ID 0x4403 +#define BCM4410_DEVICE_ID 0x4410 +#define BCM4412_DEVICE_ID 0x4412 +#define BCM4430_DEVICE_ID 0x4430 +#define BCM4432_DEVICE_ID 0x4432 +#define BCM4704_ENET_ID 0x4706 +#define BCM4710_DEVICE_ID 0x4710 +#define BCM47XX_AUDIO_ID 0x4711 +#define BCM47XX_V90_ID 0x4712 +#define BCM47XX_ENET_ID 0x4713 +#define BCM47XX_EXT_ID 0x4714 +#define BCM47XX_GMAC_ID 0x4715 +#define BCM47XX_USBH_ID 0x4716 +#define BCM47XX_USBD_ID 0x4717 +#define BCM47XX_IPSEC_ID 0x4718 +#define BCM47XX_ROBO_ID 0x4719 +#define BCM47XX_USB20H_ID 0x471a +#define BCM47XX_USB20D_ID 0x471b +#define BCM47XX_ATA100_ID 0x471d +#define BCM47XX_SATAXOR_ID 0x471e +#define BCM47XX_GIGETH_ID 0x471f +#define BCM4712_MIPS_ID 0x4720 +#define BCM4716_DEVICE_ID 0x4722 +#define BCM47XX_SMBUS_EMU_ID 0x47fe +#define BCM47XX_XOR_EMU_ID 0x47ff +#define EPI41210_DEVICE_ID 0xa0fa +#define EPI41230_DEVICE_ID 0xa10e +#define JINVANI_SDIOH_ID 0x4743 +#define BCM27XX_SDIOH_ID 0x2702 +#define PCIXX21_FLASHMEDIA_ID 0x803b +#define PCIXX21_SDIOH_ID 0x803c +#define R5C822_SDIOH_ID 0x0822 +#define JMICRON_SDIOH_ID 0x2381 + + +#define BCM4306_CHIP_ID 0x4306 +#define BCM4311_CHIP_ID 0x4311 +#define BCM43111_CHIP_ID 43111 +#define BCM43112_CHIP_ID 43112 +#define BCM4312_CHIP_ID 0x4312 +#define BCM4313_CHIP_ID 0x4313 +#define BCM43131_CHIP_ID 43131 +#define BCM4315_CHIP_ID 0x4315 +#define BCM4318_CHIP_ID 0x4318 +#define BCM4319_CHIP_ID 0x4319 +#define BCM4320_CHIP_ID 0x4320 +#define BCM4321_CHIP_ID 0x4321 +#define BCM43217_CHIP_ID 43217 +#define BCM4322_CHIP_ID 0x4322 +#define BCM43221_CHIP_ID 43221 +#define BCM43222_CHIP_ID 43222 +#define BCM43224_CHIP_ID 43224 +#define BCM43225_CHIP_ID 43225 +#define BCM43227_CHIP_ID 43227 +#define BCM43228_CHIP_ID 43228 +#define BCM43226_CHIP_ID 43226 +#define BCM43231_CHIP_ID 43231 +#define BCM43234_CHIP_ID 43234 +#define BCM43235_CHIP_ID 43235 +#define BCM43236_CHIP_ID 43236 +#define BCM43237_CHIP_ID 43237 +#define BCM43238_CHIP_ID 43238 +#define BCM43239_CHIP_ID 43239 +#define BCM43420_CHIP_ID 43420 +#define BCM43421_CHIP_ID 43421 +#define BCM43428_CHIP_ID 43428 +#define BCM43431_CHIP_ID 43431 +#define BCM4325_CHIP_ID 0x4325 +#define BCM4328_CHIP_ID 0x4328 +#define BCM4329_CHIP_ID 0x4329 +#define BCM4331_CHIP_ID 0x4331 +#define BCM4336_CHIP_ID 0x4336 +#define BCM43362_CHIP_ID 43362 +#define BCM4330_CHIP_ID 0x4330 +#define BCM6362_CHIP_ID 0x6362 +#define BCM4314_CHIP_ID 0x4314 +#define BCM43142_CHIP_ID 43142 +#define BCM4324_CHIP_ID 0x4324 + +#define BCM4342_CHIP_ID 4342 +#define BCM4402_CHIP_ID 0x4402 +#define BCM4704_CHIP_ID 0x4704 +#define BCM4710_CHIP_ID 0x4710 +#define BCM4712_CHIP_ID 0x4712 +#define BCM4716_CHIP_ID 0x4716 +#define BCM47162_CHIP_ID 47162 +#define BCM4748_CHIP_ID 0x4748 +#define BCM4749_CHIP_ID 0x4749 +#define BCM4785_CHIP_ID 0x4785 +#define BCM5350_CHIP_ID 0x5350 +#define BCM5352_CHIP_ID 0x5352 +#define BCM5354_CHIP_ID 0x5354 +#define BCM5365_CHIP_ID 0x5365 +#define BCM5356_CHIP_ID 0x5356 +#define BCM5357_CHIP_ID 0x5357 +#define BCM53572_CHIP_ID 53572 + + +#define BCM4303_PKG_ID 2 +#define BCM4309_PKG_ID 1 +#define BCM4712LARGE_PKG_ID 0 +#define BCM4712SMALL_PKG_ID 1 +#define BCM4712MID_PKG_ID 2 +#define BCM4328USBD11G_PKG_ID 2 +#define BCM4328USBDUAL_PKG_ID 3 +#define BCM4328SDIOD11G_PKG_ID 4 +#define BCM4328SDIODUAL_PKG_ID 5 +#define BCM4329_289PIN_PKG_ID 0 +#define BCM4329_182PIN_PKG_ID 1 +#define BCM5354E_PKG_ID 1 +#define BCM4716_PKG_ID 8 +#define BCM4717_PKG_ID 9 +#define BCM4718_PKG_ID 10 +#define BCM5356_PKG_NONMODE 1 +#define BCM5358U_PKG_ID 8 +#define BCM5358_PKG_ID 9 +#define BCM47186_PKG_ID 10 +#define BCM5357_PKG_ID 11 +#define BCM5356U_PKG_ID 12 +#define BCM53572_PKG_ID 8 +#define BCM47188_PKG_ID 9 +#define BCM4331TT_PKG_ID 8 +#define BCM4331TN_PKG_ID 9 +#define BCM4331TNA0_PKG_ID 0xb + + +#define HDLSIM5350_PKG_ID 1 +#define HDLSIM_PKG_ID 14 +#define HWSIM_PKG_ID 15 +#define BCM43224_FAB_CSM 0x8 +#define BCM43224_FAB_SMIC 0xa +#define BCM4336_WLBGA_PKG_ID 0x8 +#define BCM4330_WLBGA_PKG_ID 0x0 +#define BCM4314PCIE_ARM_PKG_ID (8 | 0) +#define BCM4314SDIO_PKG_ID (8 | 1) +#define BCM4314PCIE_PKG_ID (8 | 2) +#define BCM4314SDIO_ARM_PKG_ID (8 | 3) +#define BCM4314SDIO_FPBGA_PKG_ID (8 | 4) +#define BCM4314DEV_PKG_ID (8 | 6) + +#define PCIXX21_FLASHMEDIA0_ID 0x8033 +#define PCIXX21_SDIOH0_ID 0x8034 + + +#define BFL_BTC2WIRE 0x00000001 +#define BFL_BTCOEX 0x00000001 +#define BFL_PACTRL 0x00000002 +#define BFL_AIRLINEMODE 0x00000004 +#define BFL_ADCDIV 0x00000008 +#define BFL_ENETROBO 0x00000010 +#define BFL_NOPLLDOWN 0x00000020 +#define BFL_CCKHIPWR 0x00000040 +#define BFL_ENETADM 0x00000080 +#define BFL_ENETVLAN 0x00000100 +#ifdef WLAFTERBURNER +#define BFL_AFTERBURNER 0x00000200 +#endif +#define BFL_NOPCI 0x00000400 +#define BFL_FEM 0x00000800 +#define BFL_EXTLNA 0x00001000 +#define BFL_HGPA 0x00002000 +#define BFL_BTC2WIRE_ALTGPIO 0x00004000 +#define BFL_ALTIQ 0x00008000 +#define BFL_NOPA 0x00010000 +#define BFL_RSSIINV 0x00020000 +#define BFL_PAREF 0x00040000 +#define BFL_3TSWITCH 0x00080000 +#define BFL_PHASESHIFT 0x00100000 +#define BFL_BUCKBOOST 0x00200000 +#define BFL_FEM_BT 0x00400000 +#define BFL_NOCBUCK 0x00800000 +#define BFL_CCKFAVOREVM 0x01000000 +#define BFL_PALDO 0x02000000 +#define BFL_LNLDO2_2P5 0x04000000 +#define BFL_FASTPWR 0x08000000 +#define BFL_UCPWRCTL_MININDX 0x08000000 +#define BFL_EXTLNA_5GHz 0x10000000 +#define BFL_TRSW_1by2 0x20000000 +#define BFL_LO_TRSW_R_5GHz 0x40000000 +#define BFL_ELNA_GAINDEF 0x80000000 +#define BFL_EXTLNA_TX 0x20000000 + + +#define BFL2_RXBB_INT_REG_DIS 0x00000001 +#define BFL2_APLL_WAR 0x00000002 +#define BFL2_TXPWRCTRL_EN 0x00000004 +#define BFL2_2X4_DIV 0x00000008 +#define BFL2_5G_PWRGAIN 0x00000010 +#define BFL2_PCIEWAR_OVR 0x00000020 +#define BFL2_CAESERS_BRD 0x00000040 +#define BFL2_BTC3WIRE 0x00000080 +#define BFL2_BTCLEGACY 0x00000080 +#define BFL2_SKWRKFEM_BRD 0x00000100 +#define BFL2_SPUR_WAR 0x00000200 +#define BFL2_GPLL_WAR 0x00000400 +#define BFL2_TRISTATE_LED 0x00000800 +#define BFL2_SINGLEANT_CCK 0x00001000 +#define BFL2_2G_SPUR_WAR 0x00002000 +#define BFL2_BPHY_ALL_TXCORES 0x00004000 +#define BFL2_FCC_BANDEDGE_WAR 0x00008000 +#define BFL2_GPLL_WAR2 0x00010000 +#define BFL2_IPALVLSHIFT_3P3 0x00020000 +#define BFL2_INTERNDET_TXIQCAL 0x00040000 +#define BFL2_XTALBUFOUTEN 0x00080000 +#define BFL2_ANAPACTRL_2G 0x00100000 +#define BFL2_ANAPACTRL_5G 0x00200000 +#define BFL2_ELNACTRL_TRSW_2G 0x00400000 +#define BFL2_BT_SHARE_ANT0 0x00800000 +#define BFL2_TEMPSENSE_HIGHER 0x01000000 +#define BFL2_BTC3WIREONLY 0x02000000 +#define BFL2_PWR_NOMINAL 0x04000000 +#define BFL2_EXTLNA_TX 0x08000000 + +#define BFL2_4313_RADIOREG 0x10000000 +#define BFL2_SECI_LOPWR_DIS 0x20000000 + + + +#define BOARD_GPIO_BTC3W_IN 0x850 +#define BOARD_GPIO_BTC3W_OUT 0x020 +#define BOARD_GPIO_BTCMOD_IN 0x010 +#define BOARD_GPIO_BTCMOD_OUT 0x020 +#define BOARD_GPIO_BTC_IN 0x080 +#define BOARD_GPIO_BTC_OUT 0x100 +#define BOARD_GPIO_PACTRL 0x200 +#define BOARD_GPIO_12 0x1000 +#define BOARD_GPIO_13 0x2000 +#define BOARD_GPIO_BTC4_IN 0x0800 +#define BOARD_GPIO_BTC4_BT 0x2000 +#define BOARD_GPIO_BTC4_STAT 0x4000 +#define BOARD_GPIO_BTC4_WLAN 0x8000 +#define BOARD_GPIO_1_WLAN_PWR 0x2 +#define BOARD_GPIO_4_WLAN_PWR 0x10 + +#define GPIO_BTC4W_OUT_4312 0x010 +#define GPIO_BTC4W_OUT_43224 0x020 +#define GPIO_BTC4W_OUT_43224_SHARED 0x0e0 +#define GPIO_BTC4W_OUT_43225 0x0e0 +#define GPIO_BTC4W_OUT_43421 0x020 +#define GPIO_BTC4W_OUT_4313 0x060 + +#define PCI_CFG_GPIO_SCS 0x10 +#define PCI_CFG_GPIO_HWRAD 0x20 +#define PCI_CFG_GPIO_XTAL 0x40 +#define PCI_CFG_GPIO_PLL 0x80 + + +#define PLL_DELAY 150 +#define FREF_DELAY 200 +#define MIN_SLOW_CLK 32 +#define XTAL_ON_DELAY 1000 + + +#define BU4710_BOARD 0x0400 +#define VSIM4710_BOARD 0x0401 +#define QT4710_BOARD 0x0402 + +#define BU4309_BOARD 0x040a +#define BCM94309CB_BOARD 0x040b +#define BCM94309MP_BOARD 0x040c +#define BCM4309AP_BOARD 0x040d + +#define BCM94302MP_BOARD 0x040e + +#define BU4306_BOARD 0x0416 +#define BCM94306CB_BOARD 0x0417 +#define BCM94306MP_BOARD 0x0418 + +#define BCM94710D_BOARD 0x041a +#define BCM94710R1_BOARD 0x041b +#define BCM94710R4_BOARD 0x041c +#define BCM94710AP_BOARD 0x041d + +#define BU2050_BOARD 0x041f + +#define BCM94306P50_BOARD 0x0420 + +#define BCM94309G_BOARD 0x0421 + +#define BU4704_BOARD 0x0423 +#define BU4702_BOARD 0x0424 + +#define BCM94306PC_BOARD 0x0425 + +#define MPSG4306_BOARD 0x0427 + +#define BCM94702MN_BOARD 0x0428 + + +#define BCM94702CPCI_BOARD 0x0429 + + +#define BCM95380RR_BOARD 0x042a + + +#define BCM94306CBSG_BOARD 0x042b + + +#define PCSG94306_BOARD 0x042d + + +#define BU4704SD_BOARD 0x042e + + +#define BCM94704AGR_BOARD 0x042f + + +#define BCM94308MP_BOARD 0x0430 + + +#define BCM94306GPRS_BOARD 0x0432 + + +#define BU5365_FPGA_BOARD 0x0433 + +#define BU4712_BOARD 0x0444 +#define BU4712SD_BOARD 0x045d +#define BU4712L_BOARD 0x045f + + +#define BCM94712AP_BOARD 0x0445 +#define BCM94712P_BOARD 0x0446 + + +#define BU4318_BOARD 0x0447 +#define CB4318_BOARD 0x0448 +#define MPG4318_BOARD 0x0449 +#define MP4318_BOARD 0x044a +#define SD4318_BOARD 0x044b + + +#define BCM94313BU_BOARD 0x050f +#define BCM94313HM_BOARD 0x0510 +#define BCM94313EPA_BOARD 0x0511 +#define BCM94313HMG_BOARD 0x051C + + +#define BCM96338_BOARD 0x6338 +#define BCM96348_BOARD 0x6348 +#define BCM96358_BOARD 0x6358 +#define BCM96368_BOARD 0x6368 + + +#define BCM94306P_BOARD 0x044c + + +#define BCM94303MP_BOARD 0x044e + + +#define BCM94306MPSGH_BOARD 0x044f + + +#define BCM94306MPM 0x0450 +#define BCM94306MPL 0x0453 + + +#define BCM94712AGR_BOARD 0x0451 + + +#define PC4303_BOARD 0x0454 + + +#define BCM95350K_BOARD 0x0455 + + +#define BCM95350R_BOARD 0x0456 + + +#define BCM94306MPLNA_BOARD 0x0457 + + +#define BU4320_BOARD 0x0458 +#define BU4320S_BOARD 0x0459 +#define BCM94320PH_BOARD 0x045a + + +#define BCM94306MPH_BOARD 0x045b + + +#define BCM94306PCIV_BOARD 0x045c + +#define BU4712SD_BOARD 0x045d + +#define BCM94320PFLSH_BOARD 0x045e + +#define BU4712L_BOARD 0x045f +#define BCM94712LGR_BOARD 0x0460 +#define BCM94320R_BOARD 0x0461 + +#define BU5352_BOARD 0x0462 + +#define BCM94318MPGH_BOARD 0x0463 + +#define BU4311_BOARD 0x0464 +#define BCM94311MC_BOARD 0x0465 +#define BCM94311MCAG_BOARD 0x0466 + +#define BCM95352GR_BOARD 0x0467 + + +#define BCM95351AGR_BOARD 0x0470 + + +#define BCM94704MPCB_BOARD 0x0472 + + +#define BU4785_BOARD 0x0478 + + +#define BU4321_BOARD 0x046b +#define BU4321E_BOARD 0x047c +#define MP4321_BOARD 0x046c +#define CB2_4321_BOARD 0x046d +#define CB2_4321_AG_BOARD 0x0066 +#define MC4321_BOARD 0x046e + + +#define BU4328_BOARD 0x0481 +#define BCM4328SDG_BOARD 0x0482 +#define BCM4328SDAG_BOARD 0x0483 +#define BCM4328UG_BOARD 0x0484 +#define BCM4328UAG_BOARD 0x0485 +#define BCM4328PC_BOARD 0x0486 +#define BCM4328CF_BOARD 0x0487 + + +#define BCM94325DEVBU_BOARD 0x0490 +#define BCM94325BGABU_BOARD 0x0491 + +#define BCM94325SDGWB_BOARD 0x0492 + +#define BCM94325SDGMDL_BOARD 0x04aa +#define BCM94325SDGMDL2_BOARD 0x04c6 +#define BCM94325SDGMDL3_BOARD 0x04c9 + +#define BCM94325SDABGWBA_BOARD 0x04e1 + + +#define BCM94322MC_SSID 0x04a4 +#define BCM94322USB_SSID 0x04a8 +#define BCM94322HM_SSID 0x04b0 +#define BCM94322USB2D_SSID 0x04bf + + +#define BCM4312MCGSG_BOARD 0x04b5 + + +#define BCM94315DEVBU_SSID 0x04c2 +#define BCM94315USBGP_SSID 0x04c7 +#define BCM94315BGABU_SSID 0x04ca +#define BCM94315USBGP41_SSID 0x04cb + + +#define BCM94319DEVBU_SSID 0X04e5 +#define BCM94319USB_SSID 0X04e6 +#define BCM94319SD_SSID 0X04e7 + + +#define BCM94716NR2_SSID 0x04cd + + +#define BCM94319DEVBU_SSID 0X04e5 +#define BCM94319USBNP4L_SSID 0X04e6 +#define BCM94319WLUSBN4L_SSID 0X04e7 +#define BCM94319SDG_SSID 0X04ea +#define BCM94319LCUSBSDN4L_SSID 0X04eb +#define BCM94319USBB_SSID 0x04ee +#define BCM94319LCSDN4L_SSID 0X0507 +#define BCM94319LSUSBN4L_SSID 0X0508 +#define BCM94319SDNA4L_SSID 0X0517 +#define BCM94319SDELNA4L_SSID 0X0518 +#define BCM94319SDELNA6L_SSID 0X0539 +#define BCM94319ARCADYAN_SSID 0X0546 +#define BCM94319WINDSOR_SSID 0x0561 +#define BCM94319MLAP_SSID 0x0562 +#define BCM94319SDNA_SSID 0x058b +#define BCM94319BHEMU3_SSID 0x0563 +#define BCM94319SDHMB_SSID 0x058c +#define BCM94319SDBREF_SSID 0x05a1 +#define BCM94319USBSDB_SSID 0x05a2 + + + +#define BCM94329AGB_SSID 0X04b9 +#define BCM94329TDKMDL1_SSID 0X04ba +#define BCM94329TDKMDL11_SSID 0X04fc +#define BCM94329OLYMPICN18_SSID 0X04fd +#define BCM94329OLYMPICN90_SSID 0X04fe +#define BCM94329OLYMPICN90U_SSID 0X050c +#define BCM94329OLYMPICN90M_SSID 0X050b +#define BCM94329AGBF_SSID 0X04ff +#define BCM94329OLYMPICX17_SSID 0X0504 +#define BCM94329OLYMPICX17M_SSID 0X050a +#define BCM94329OLYMPICX17U_SSID 0X0509 +#define BCM94329OLYMPICUNO_SSID 0X0564 +#define BCM94329MOTOROLA_SSID 0X0565 +#define BCM94329OLYMPICLOCO_SSID 0X0568 + +#define BCM94336SD_WLBGABU_SSID 0x0511 +#define BCM94336SD_WLBGAREF_SSID 0x0519 +#define BCM94336SDGP_SSID 0x0538 +#define BCM94336SDG_SSID 0x0519 +#define BCM94336SDGN_SSID 0x0538 +#define BCM94336SDGFC_SSID 0x056B + + +#define BCM94330SDG_SSID 0x0528 +#define BCM94330SD_FCBGABU_SSID 0x052e +#define BCM94330SD_WLBGABU_SSID 0x052f +#define BCM94330SD_FCBGA_SSID 0x0530 +#define BCM94330FCSDAGB_SSID 0x0532 +#define BCM94330OLYMPICAMG_SSID 0x0549 +#define BCM94330OLYMPICAMGEPA_SSID 0x054F +#define BCM94330OLYMPICUNO3_SSID 0x0551 +#define BCM94330WLSDAGB_SSID 0x0547 +#define BCM94330CSPSDAGBB_SSID 0x054A + + +#define BCM943224X21 0x056e +#define BCM943224X21_FCC 0x00d1 + + +#define BCM943228BU8_SSID 0x0540 +#define BCM943228BU9_SSID 0x0541 +#define BCM943228BU_SSID 0x0542 +#define BCM943227HM4L_SSID 0x0543 +#define BCM943227HMB_SSID 0x0544 +#define BCM943228HM4L_SSID 0x0545 +#define BCM943228SD_SSID 0x0573 + + +#define BCM943239MOD_SSID 0x05ac +#define BCM943239REF_SSID 0x05aa + + +#define BCM94331X19 0x00D6 +#define BCM94331PCIEBT3Ax_SSID 0x00E4 +#define BCM94331X12_2G_SSID 0x00EC +#define BCM94331X12_5G_SSID 0x00ED +#define BCM94331X29B 0x00EF +#define BCM94331BU_SSID 0x0523 +#define BCM94331S9BU_SSID 0x0524 +#define BCM94331MC_SSID 0x0525 +#define BCM94331MCI_SSID 0x0526 +#define BCM94331PCIEBT4_SSID 0x0527 +#define BCM94331HM_SSID 0x0574 +#define BCM94331PCIEDUAL_SSID 0x059B +#define BCM94331MCH5_SSID 0x05A9 +#define BCM94331PCIEDUALV2_SSID 0x05B7 +#define BCM94331CS_SSID 0x05C6 +#define BCM94331CSAX_SSID 0x00EF + + +#define BCM953572BU_SSID 0x058D +#define BCM953572NR2_SSID 0x058E +#define BCM947188NR2_SSID 0x058F +#define BCM953572SDRNR2_SSID 0x0590 + + +#define BCM943236OLYMPICSULLEY_SSID 0x594 +#define BCM943236PREPROTOBLU2O3_SSID 0x5b9 +#define BCM943236USBELNA_SSID 0x5f8 + + +#define GPIO_NUMPINS 32 + + +#define RDL_RAM_BASE_4319 0x60000000 +#define RDL_RAM_BASE_4329 0x60000000 +#define RDL_RAM_SIZE_4319 0x48000 +#define RDL_RAM_SIZE_4329 0x48000 +#define RDL_RAM_SIZE_43236 0x70000 +#define RDL_RAM_BASE_43236 0x60000000 +#define RDL_RAM_SIZE_4328 0x60000 +#define RDL_RAM_BASE_4328 0x80000000 +#define RDL_RAM_SIZE_4322 0x60000 +#define RDL_RAM_BASE_4322 0x60000000 + + +#define MUXENAB_UART 0x00000001 +#define MUXENAB_GPIO 0x00000002 +#define MUXENAB_ERCX 0x00000004 +#define MUXENAB_JTAG 0x00000008 +#define MUXENAB_HOST_WAKE 0x00000010 + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/bcmendian.h b/drivers/net/wireless/bcmdhd/include/bcmendian.h new file mode 100644 index 0000000000000..f3356a724b44e --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmendian.h @@ -0,0 +1,279 @@ +/* + * Byte order utilities + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmendian.h 277737 2011-08-16 17:54:59Z $ + * + * This file by default provides proper behavior on little-endian architectures. + * On big-endian architectures, IL_BIGENDIAN should be defined. + */ + + +#ifndef _BCMENDIAN_H_ +#define _BCMENDIAN_H_ + +#include + + +#define BCMSWAP16(val) \ + ((uint16)((((uint16)(val) & (uint16)0x00ffU) << 8) | \ + (((uint16)(val) & (uint16)0xff00U) >> 8))) + + +#define BCMSWAP32(val) \ + ((uint32)((((uint32)(val) & (uint32)0x000000ffU) << 24) | \ + (((uint32)(val) & (uint32)0x0000ff00U) << 8) | \ + (((uint32)(val) & (uint32)0x00ff0000U) >> 8) | \ + (((uint32)(val) & (uint32)0xff000000U) >> 24))) + + +#define BCMSWAP32BY16(val) \ + ((uint32)((((uint32)(val) & (uint32)0x0000ffffU) << 16) | \ + (((uint32)(val) & (uint32)0xffff0000U) >> 16))) + + +#ifndef hton16 +#define HTON16(i) BCMSWAP16(i) +#define hton16(i) bcmswap16(i) +#define HTON32(i) BCMSWAP32(i) +#define hton32(i) bcmswap32(i) +#define NTOH16(i) BCMSWAP16(i) +#define ntoh16(i) bcmswap16(i) +#define NTOH32(i) BCMSWAP32(i) +#define ntoh32(i) bcmswap32(i) +#define LTOH16(i) (i) +#define ltoh16(i) (i) +#define LTOH32(i) (i) +#define ltoh32(i) (i) +#define HTOL16(i) (i) +#define htol16(i) (i) +#define HTOL32(i) (i) +#define htol32(i) (i) +#endif + +#define ltoh16_buf(buf, i) +#define htol16_buf(buf, i) + + +#define load32_ua(a) ltoh32_ua(a) +#define store32_ua(a, v) htol32_ua_store(v, a) +#define load16_ua(a) ltoh16_ua(a) +#define store16_ua(a, v) htol16_ua_store(v, a) + +#define _LTOH16_UA(cp) ((cp)[0] | ((cp)[1] << 8)) +#define _LTOH32_UA(cp) ((cp)[0] | ((cp)[1] << 8) | ((cp)[2] << 16) | ((cp)[3] << 24)) +#define _NTOH16_UA(cp) (((cp)[0] << 8) | (cp)[1]) +#define _NTOH32_UA(cp) (((cp)[0] << 24) | ((cp)[1] << 16) | ((cp)[2] << 8) | (cp)[3]) + +#define ltoh_ua(ptr) \ + (sizeof(*(ptr)) == sizeof(uint8) ? *(const uint8 *)(ptr) : \ + sizeof(*(ptr)) == sizeof(uint16) ? _LTOH16_UA((const uint8 *)(ptr)) : \ + sizeof(*(ptr)) == sizeof(uint32) ? _LTOH32_UA((const uint8 *)(ptr)) : \ + *(uint8 *)0) + +#define ntoh_ua(ptr) \ + (sizeof(*(ptr)) == sizeof(uint8) ? *(const uint8 *)(ptr) : \ + sizeof(*(ptr)) == sizeof(uint16) ? _NTOH16_UA((const uint8 *)(ptr)) : \ + sizeof(*(ptr)) == sizeof(uint32) ? _NTOH32_UA((const uint8 *)(ptr)) : \ + *(uint8 *)0) + +#ifdef __GNUC__ + + + +#define bcmswap16(val) ({ \ + uint16 _val = (val); \ + BCMSWAP16(_val); \ +}) + +#define bcmswap32(val) ({ \ + uint32 _val = (val); \ + BCMSWAP32(_val); \ +}) + +#define bcmswap32by16(val) ({ \ + uint32 _val = (val); \ + BCMSWAP32BY16(_val); \ +}) + +#define bcmswap16_buf(buf, len) ({ \ + uint16 *_buf = (uint16 *)(buf); \ + uint _wds = (len) / 2; \ + while (_wds--) { \ + *_buf = bcmswap16(*_buf); \ + _buf++; \ + } \ +}) + +#define htol16_ua_store(val, bytes) ({ \ + uint16 _val = (val); \ + uint8 *_bytes = (uint8 *)(bytes); \ + _bytes[0] = _val & 0xff; \ + _bytes[1] = _val >> 8; \ +}) + +#define htol32_ua_store(val, bytes) ({ \ + uint32 _val = (val); \ + uint8 *_bytes = (uint8 *)(bytes); \ + _bytes[0] = _val & 0xff; \ + _bytes[1] = (_val >> 8) & 0xff; \ + _bytes[2] = (_val >> 16) & 0xff; \ + _bytes[3] = _val >> 24; \ +}) + +#define hton16_ua_store(val, bytes) ({ \ + uint16 _val = (val); \ + uint8 *_bytes = (uint8 *)(bytes); \ + _bytes[0] = _val >> 8; \ + _bytes[1] = _val & 0xff; \ +}) + +#define hton32_ua_store(val, bytes) ({ \ + uint32 _val = (val); \ + uint8 *_bytes = (uint8 *)(bytes); \ + _bytes[0] = _val >> 24; \ + _bytes[1] = (_val >> 16) & 0xff; \ + _bytes[2] = (_val >> 8) & 0xff; \ + _bytes[3] = _val & 0xff; \ +}) + +#define ltoh16_ua(bytes) ({ \ + const uint8 *_bytes = (const uint8 *)(bytes); \ + _LTOH16_UA(_bytes); \ +}) + +#define ltoh32_ua(bytes) ({ \ + const uint8 *_bytes = (const uint8 *)(bytes); \ + _LTOH32_UA(_bytes); \ +}) + +#define ntoh16_ua(bytes) ({ \ + const uint8 *_bytes = (const uint8 *)(bytes); \ + _NTOH16_UA(_bytes); \ +}) + +#define ntoh32_ua(bytes) ({ \ + const uint8 *_bytes = (const uint8 *)(bytes); \ + _NTOH32_UA(_bytes); \ +}) + +#else + + +static INLINE uint16 +bcmswap16(uint16 val) +{ + return BCMSWAP16(val); +} + +static INLINE uint32 +bcmswap32(uint32 val) +{ + return BCMSWAP32(val); +} + +static INLINE uint32 +bcmswap32by16(uint32 val) +{ + return BCMSWAP32BY16(val); +} + + + + +static INLINE void +bcmswap16_buf(uint16 *buf, uint len) +{ + len = len / 2; + + while (len--) { + *buf = bcmswap16(*buf); + buf++; + } +} + + +static INLINE void +htol16_ua_store(uint16 val, uint8 *bytes) +{ + bytes[0] = val & 0xff; + bytes[1] = val >> 8; +} + + +static INLINE void +htol32_ua_store(uint32 val, uint8 *bytes) +{ + bytes[0] = val & 0xff; + bytes[1] = (val >> 8) & 0xff; + bytes[2] = (val >> 16) & 0xff; + bytes[3] = val >> 24; +} + + +static INLINE void +hton16_ua_store(uint16 val, uint8 *bytes) +{ + bytes[0] = val >> 8; + bytes[1] = val & 0xff; +} + + +static INLINE void +hton32_ua_store(uint32 val, uint8 *bytes) +{ + bytes[0] = val >> 24; + bytes[1] = (val >> 16) & 0xff; + bytes[2] = (val >> 8) & 0xff; + bytes[3] = val & 0xff; +} + + +static INLINE uint16 +ltoh16_ua(const void *bytes) +{ + return _LTOH16_UA((const uint8 *)bytes); +} + + +static INLINE uint32 +ltoh32_ua(const void *bytes) +{ + return _LTOH32_UA((const uint8 *)bytes); +} + + +static INLINE uint16 +ntoh16_ua(const void *bytes) +{ + return _NTOH16_UA((const uint8 *)bytes); +} + + +static INLINE uint32 +ntoh32_ua(const void *bytes) +{ + return _NTOH32_UA((const uint8 *)bytes); +} + +#endif +#endif diff --git a/drivers/net/wireless/bcmdhd/include/bcmpcispi.h b/drivers/net/wireless/bcmdhd/include/bcmpcispi.h new file mode 100644 index 0000000000000..51e0427e7f605 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmpcispi.h @@ -0,0 +1,181 @@ +/* + * Broadcom PCI-SPI Host Controller Register Definitions + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmpcispi.h 277737 2011-08-16 17:54:59Z $ + */ +#ifndef _BCM_PCI_SPI_H +#define _BCM_PCI_SPI_H + +/* cpp contortions to concatenate w/arg prescan */ +#ifndef PAD +#define _PADLINE(line) pad ## line +#define _XSTR(line) _PADLINE(line) +#define PAD _XSTR(__LINE__) +#endif /* PAD */ + + +typedef volatile struct { + uint32 spih_ctrl; /* 0x00 SPI Control Register */ + uint32 spih_stat; /* 0x04 SPI Status Register */ + uint32 spih_data; /* 0x08 SPI Data Register, 32-bits wide */ + uint32 spih_ext; /* 0x0C SPI Extension Register */ + uint32 PAD[4]; /* 0x10-0x1F PADDING */ + + uint32 spih_gpio_ctrl; /* 0x20 SPI GPIO Control Register */ + uint32 spih_gpio_data; /* 0x24 SPI GPIO Data Register */ + uint32 PAD[6]; /* 0x28-0x3F PADDING */ + + uint32 spih_int_edge; /* 0x40 SPI Interrupt Edge Register (0=Level, 1=Edge) */ + uint32 spih_int_pol; /* 0x44 SPI Interrupt Polarity Register (0=Active Low, */ + /* 1=Active High) */ + uint32 spih_int_mask; /* 0x48 SPI Interrupt Mask */ + uint32 spih_int_status; /* 0x4C SPI Interrupt Status */ + uint32 PAD[4]; /* 0x50-0x5F PADDING */ + + uint32 spih_hex_disp; /* 0x60 SPI 4-digit hex display value */ + uint32 spih_current_ma; /* 0x64 SPI SD card current consumption in mA */ + uint32 PAD[1]; /* 0x68 PADDING */ + uint32 spih_disp_sel; /* 0x6c SPI 4-digit hex display mode select (1=current) */ + uint32 PAD[4]; /* 0x70-0x7F PADDING */ + uint32 PAD[8]; /* 0x80-0x9F PADDING */ + uint32 PAD[8]; /* 0xA0-0xBF PADDING */ + uint32 spih_pll_ctrl; /* 0xC0 PLL Control Register */ + uint32 spih_pll_status; /* 0xC4 PLL Status Register */ + uint32 spih_xtal_freq; /* 0xC8 External Clock Frequency in units of 10000Hz */ + uint32 spih_clk_count; /* 0xCC External Clock Count Register */ + +} spih_regs_t; + +typedef volatile struct { + uint32 cfg_space[0x40]; /* 0x000-0x0FF PCI Configuration Space (Read Only) */ + uint32 P_IMG_CTRL0; /* 0x100 PCI Image0 Control Register */ + + uint32 P_BA0; /* 0x104 32 R/W PCI Image0 Base Address register */ + uint32 P_AM0; /* 0x108 32 R/W PCI Image0 Address Mask register */ + uint32 P_TA0; /* 0x10C 32 R/W PCI Image0 Translation Address register */ + uint32 P_IMG_CTRL1; /* 0x110 32 R/W PCI Image1 Control register */ + uint32 P_BA1; /* 0x114 32 R/W PCI Image1 Base Address register */ + uint32 P_AM1; /* 0x118 32 R/W PCI Image1 Address Mask register */ + uint32 P_TA1; /* 0x11C 32 R/W PCI Image1 Translation Address register */ + uint32 P_IMG_CTRL2; /* 0x120 32 R/W PCI Image2 Control register */ + uint32 P_BA2; /* 0x124 32 R/W PCI Image2 Base Address register */ + uint32 P_AM2; /* 0x128 32 R/W PCI Image2 Address Mask register */ + uint32 P_TA2; /* 0x12C 32 R/W PCI Image2 Translation Address register */ + uint32 P_IMG_CTRL3; /* 0x130 32 R/W PCI Image3 Control register */ + uint32 P_BA3; /* 0x134 32 R/W PCI Image3 Base Address register */ + uint32 P_AM3; /* 0x138 32 R/W PCI Image3 Address Mask register */ + uint32 P_TA3; /* 0x13C 32 R/W PCI Image3 Translation Address register */ + uint32 P_IMG_CTRL4; /* 0x140 32 R/W PCI Image4 Control register */ + uint32 P_BA4; /* 0x144 32 R/W PCI Image4 Base Address register */ + uint32 P_AM4; /* 0x148 32 R/W PCI Image4 Address Mask register */ + uint32 P_TA4; /* 0x14C 32 R/W PCI Image4 Translation Address register */ + uint32 P_IMG_CTRL5; /* 0x150 32 R/W PCI Image5 Control register */ + uint32 P_BA5; /* 0x154 32 R/W PCI Image5 Base Address register */ + uint32 P_AM5; /* 0x158 32 R/W PCI Image5 Address Mask register */ + uint32 P_TA5; /* 0x15C 32 R/W PCI Image5 Translation Address register */ + uint32 P_ERR_CS; /* 0x160 32 R/W PCI Error Control and Status register */ + uint32 P_ERR_ADDR; /* 0x164 32 R PCI Erroneous Address register */ + uint32 P_ERR_DATA; /* 0x168 32 R PCI Erroneous Data register */ + + uint32 PAD[5]; /* 0x16C-0x17F PADDING */ + + uint32 WB_CONF_SPC_BAR; /* 0x180 32 R WISHBONE Configuration Space Base Address */ + uint32 W_IMG_CTRL1; /* 0x184 32 R/W WISHBONE Image1 Control register */ + uint32 W_BA1; /* 0x188 32 R/W WISHBONE Image1 Base Address register */ + uint32 W_AM1; /* 0x18C 32 R/W WISHBONE Image1 Address Mask register */ + uint32 W_TA1; /* 0x190 32 R/W WISHBONE Image1 Translation Address reg */ + uint32 W_IMG_CTRL2; /* 0x194 32 R/W WISHBONE Image2 Control register */ + uint32 W_BA2; /* 0x198 32 R/W WISHBONE Image2 Base Address register */ + uint32 W_AM2; /* 0x19C 32 R/W WISHBONE Image2 Address Mask register */ + uint32 W_TA2; /* 0x1A0 32 R/W WISHBONE Image2 Translation Address reg */ + uint32 W_IMG_CTRL3; /* 0x1A4 32 R/W WISHBONE Image3 Control register */ + uint32 W_BA3; /* 0x1A8 32 R/W WISHBONE Image3 Base Address register */ + uint32 W_AM3; /* 0x1AC 32 R/W WISHBONE Image3 Address Mask register */ + uint32 W_TA3; /* 0x1B0 32 R/W WISHBONE Image3 Translation Address reg */ + uint32 W_IMG_CTRL4; /* 0x1B4 32 R/W WISHBONE Image4 Control register */ + uint32 W_BA4; /* 0x1B8 32 R/W WISHBONE Image4 Base Address register */ + uint32 W_AM4; /* 0x1BC 32 R/W WISHBONE Image4 Address Mask register */ + uint32 W_TA4; /* 0x1C0 32 R/W WISHBONE Image4 Translation Address reg */ + uint32 W_IMG_CTRL5; /* 0x1C4 32 R/W WISHBONE Image5 Control register */ + uint32 W_BA5; /* 0x1C8 32 R/W WISHBONE Image5 Base Address register */ + uint32 W_AM5; /* 0x1CC 32 R/W WISHBONE Image5 Address Mask register */ + uint32 W_TA5; /* 0x1D0 32 R/W WISHBONE Image5 Translation Address reg */ + uint32 W_ERR_CS; /* 0x1D4 32 R/W WISHBONE Error Control and Status reg */ + uint32 W_ERR_ADDR; /* 0x1D8 32 R WISHBONE Erroneous Address register */ + uint32 W_ERR_DATA; /* 0x1DC 32 R WISHBONE Erroneous Data register */ + uint32 CNF_ADDR; /* 0x1E0 32 R/W Configuration Cycle register */ + uint32 CNF_DATA; /* 0x1E4 32 R/W Configuration Cycle Generation Data reg */ + + uint32 INT_ACK; /* 0x1E8 32 R Interrupt Acknowledge register */ + uint32 ICR; /* 0x1EC 32 R/W Interrupt Control register */ + uint32 ISR; /* 0x1F0 32 R/W Interrupt Status register */ +} spih_pciregs_t; + +/* + * PCI Core interrupt enable and status bit definitions. + */ + +/* PCI Core ICR Register bit definitions */ +#define PCI_INT_PROP_EN (1 << 0) /* Interrupt Propagation Enable */ +#define PCI_WB_ERR_INT_EN (1 << 1) /* Wishbone Error Interrupt Enable */ +#define PCI_PCI_ERR_INT_EN (1 << 2) /* PCI Error Interrupt Enable */ +#define PCI_PAR_ERR_INT_EN (1 << 3) /* Parity Error Interrupt Enable */ +#define PCI_SYS_ERR_INT_EN (1 << 4) /* System Error Interrupt Enable */ +#define PCI_SOFTWARE_RESET (1U << 31) /* Software reset of the PCI Core. */ + + +/* PCI Core ISR Register bit definitions */ +#define PCI_INT_PROP_ST (1 << 0) /* Interrupt Propagation Status */ +#define PCI_WB_ERR_INT_ST (1 << 1) /* Wishbone Error Interrupt Status */ +#define PCI_PCI_ERR_INT_ST (1 << 2) /* PCI Error Interrupt Status */ +#define PCI_PAR_ERR_INT_ST (1 << 3) /* Parity Error Interrupt Status */ +#define PCI_SYS_ERR_INT_ST (1 << 4) /* System Error Interrupt Status */ + + +/* Registers on the Wishbone bus */ +#define SPIH_CTLR_INTR (1 << 0) /* SPI Host Controller Core Interrupt */ +#define SPIH_DEV_INTR (1 << 1) /* SPI Device Interrupt */ +#define SPIH_WFIFO_INTR (1 << 2) /* SPI Tx FIFO Empty Intr (FPGA Rev >= 8) */ + +/* GPIO Bit definitions */ +#define SPIH_CS (1 << 0) /* SPI Chip Select (active low) */ +#define SPIH_SLOT_POWER (1 << 1) /* SD Card Slot Power Enable */ +#define SPIH_CARD_DETECT (1 << 2) /* SD Card Detect */ + +/* SPI Status Register Bit definitions */ +#define SPIH_STATE_MASK 0x30 /* SPI Transfer State Machine state mask */ +#define SPIH_STATE_SHIFT 4 /* SPI Transfer State Machine state shift */ +#define SPIH_WFFULL (1 << 3) /* SPI Write FIFO Full */ +#define SPIH_WFEMPTY (1 << 2) /* SPI Write FIFO Empty */ +#define SPIH_RFFULL (1 << 1) /* SPI Read FIFO Full */ +#define SPIH_RFEMPTY (1 << 0) /* SPI Read FIFO Empty */ + +#define SPIH_EXT_CLK (1U << 31) /* Use External Clock as PLL Clock source. */ + +#define SPIH_PLL_NO_CLK (1 << 1) /* Set to 1 if the PLL's input clock is lost. */ +#define SPIH_PLL_LOCKED (1 << 3) /* Set to 1 when the PLL is locked. */ + +/* Spin bit loop bound check */ +#define SPI_SPIN_BOUND 0xf4240 /* 1 million */ + +#endif /* _BCM_PCI_SPI_H */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmperf.h b/drivers/net/wireless/bcmdhd/include/bcmperf.h new file mode 100644 index 0000000000000..a503edbd62261 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmperf.h @@ -0,0 +1,36 @@ +/* + * Performance counters software interface. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmperf.h 277737 2011-08-16 17:54:59Z $ + */ +/* essai */ +#ifndef _BCMPERF_H_ +#define _BCMPERF_H_ +/* get cache hits and misses */ +#define BCMPERF_ENABLE_INSTRCOUNT() +#define BCMPERF_ENABLE_ICACHE_MISS() +#define BCMPERF_ENABLE_ICACHE_HIT() +#define BCMPERF_GETICACHE_MISS(x) ((x) = 0) +#define BCMPERF_GETICACHE_HIT(x) ((x) = 0) +#define BCMPERF_GETINSTRCOUNT(x) ((x) = 0) +#endif /* _BCMPERF_H_ */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdbus.h b/drivers/net/wireless/bcmdhd/include/bcmsdbus.h new file mode 100644 index 0000000000000..21a58b473e913 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmsdbus.h @@ -0,0 +1,128 @@ +/* + * Definitions for API from sdio common code (bcmsdh) to individual + * host controller drivers. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdbus.h 300017 2011-12-01 20:30:27Z $ + */ + +#ifndef _sdio_api_h_ +#define _sdio_api_h_ + + +#define SDIOH_API_RC_SUCCESS (0x00) +#define SDIOH_API_RC_FAIL (0x01) +#define SDIOH_API_SUCCESS(status) (status == 0) + +#define SDIOH_READ 0 /* Read request */ +#define SDIOH_WRITE 1 /* Write request */ + +#define SDIOH_DATA_FIX 0 /* Fixed addressing */ +#define SDIOH_DATA_INC 1 /* Incremental addressing */ + +#define SDIOH_CMD_TYPE_NORMAL 0 /* Normal command */ +#define SDIOH_CMD_TYPE_APPEND 1 /* Append command */ +#define SDIOH_CMD_TYPE_CUTTHRU 2 /* Cut-through command */ + +#define SDIOH_DATA_PIO 0 /* PIO mode */ +#define SDIOH_DATA_DMA 1 /* DMA mode */ + + +typedef int SDIOH_API_RC; + +/* SDio Host structure */ +typedef struct sdioh_info sdioh_info_t; + +/* callback function, taking one arg */ +typedef void (*sdioh_cb_fn_t)(void *); + +/* attach, return handler on success, NULL if failed. + * The handler shall be provided by all subsequent calls. No local cache + * cfghdl points to the starting address of pci device mapped memory + */ +extern sdioh_info_t * sdioh_attach(osl_t *osh, void *cfghdl, uint irq); +extern SDIOH_API_RC sdioh_detach(osl_t *osh, sdioh_info_t *si); +extern SDIOH_API_RC sdioh_interrupt_register(sdioh_info_t *si, sdioh_cb_fn_t fn, void *argh); +extern SDIOH_API_RC sdioh_interrupt_deregister(sdioh_info_t *si); + +/* query whether SD interrupt is enabled or not */ +extern SDIOH_API_RC sdioh_interrupt_query(sdioh_info_t *si, bool *onoff); + +/* enable or disable SD interrupt */ +extern SDIOH_API_RC sdioh_interrupt_set(sdioh_info_t *si, bool enable_disable); + +#if defined(DHD_DEBUG) +extern bool sdioh_interrupt_pending(sdioh_info_t *si); +#endif + +/* read or write one byte using cmd52 */ +extern SDIOH_API_RC sdioh_request_byte(sdioh_info_t *si, uint rw, uint fnc, uint addr, uint8 *byte); + +/* read or write 2/4 bytes using cmd53 */ +extern SDIOH_API_RC sdioh_request_word(sdioh_info_t *si, uint cmd_type, uint rw, uint fnc, + uint addr, uint32 *word, uint nbyte); + +/* read or write any buffer using cmd53 */ +extern SDIOH_API_RC sdioh_request_buffer(sdioh_info_t *si, uint pio_dma, uint fix_inc, + uint rw, uint fnc_num, uint32 addr, uint regwidth, uint32 buflen, uint8 *buffer, + void *pkt); + +/* get cis data */ +extern SDIOH_API_RC sdioh_cis_read(sdioh_info_t *si, uint fuc, uint8 *cis, uint32 length); + +extern SDIOH_API_RC sdioh_cfg_read(sdioh_info_t *si, uint fuc, uint32 addr, uint8 *data); +extern SDIOH_API_RC sdioh_cfg_write(sdioh_info_t *si, uint fuc, uint32 addr, uint8 *data); + +/* query number of io functions */ +extern uint sdioh_query_iofnum(sdioh_info_t *si); + +/* handle iovars */ +extern int sdioh_iovar_op(sdioh_info_t *si, const char *name, + void *params, int plen, void *arg, int len, bool set); + +/* Issue abort to the specified function and clear controller as needed */ +extern int sdioh_abort(sdioh_info_t *si, uint fnc); + +/* Start and Stop SDIO without re-enumerating the SD card. */ +extern int sdioh_start(sdioh_info_t *si, int stage); +extern int sdioh_stop(sdioh_info_t *si); + +/* Wait system lock free */ +extern int sdioh_waitlockfree(sdioh_info_t *si); + +/* Reset and re-initialize the device */ +extern int sdioh_sdio_reset(sdioh_info_t *si); + +/* Helper function */ +void *bcmsdh_get_sdioh(bcmsdh_info_t *sdh); + + + +extern SDIOH_API_RC sdioh_sleep(sdioh_info_t *si, bool enab); + +/* GPIO support */ +extern SDIOH_API_RC sdioh_gpio_init(sdioh_info_t *sd); +extern bool sdioh_gpioin(sdioh_info_t *sd, uint32 gpio); +extern SDIOH_API_RC sdioh_gpioouten(sdioh_info_t *sd, uint32 gpio); +extern SDIOH_API_RC sdioh_gpioout(sdioh_info_t *sd, uint32 gpio, bool enab); + +#endif /* _sdio_api_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdh.h b/drivers/net/wireless/bcmdhd/include/bcmsdh.h new file mode 100644 index 0000000000000..def3c0269279b --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmsdh.h @@ -0,0 +1,219 @@ +/* + * SDIO host client driver interface of Broadcom HNBU + * export functions to client drivers + * abstract OS and BUS specific details of SDIO + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdh.h 300017 2011-12-01 20:30:27Z $ + */ + +#ifndef _bcmsdh_h_ +#define _bcmsdh_h_ + +#define BCMSDH_ERROR_VAL 0x0001 /* Error */ +#define BCMSDH_INFO_VAL 0x0002 /* Info */ +extern const uint bcmsdh_msglevel; + +#define BCMSDH_ERROR(x) +#define BCMSDH_INFO(x) + +/* forward declarations */ +typedef struct bcmsdh_info bcmsdh_info_t; +typedef void (*bcmsdh_cb_fn_t)(void *); + +/* Attach and build an interface to the underlying SD host driver. + * - Allocates resources (structs, arrays, mem, OS handles, etc) needed by bcmsdh. + * - Returns the bcmsdh handle and virtual address base for register access. + * The returned handle should be used in all subsequent calls, but the bcmsh + * implementation may maintain a single "default" handle (e.g. the first or + * most recent one) to enable single-instance implementations to pass NULL. + */ +extern bcmsdh_info_t *bcmsdh_attach(osl_t *osh, void *cfghdl, void **regsva, uint irq); + +/* Detach - freeup resources allocated in attach */ +extern int bcmsdh_detach(osl_t *osh, void *sdh); + +/* Query if SD device interrupts are enabled */ +extern bool bcmsdh_intr_query(void *sdh); + +/* Enable/disable SD interrupt */ +extern int bcmsdh_intr_enable(void *sdh); +extern int bcmsdh_intr_disable(void *sdh); + +/* Register/deregister device interrupt handler. */ +extern int bcmsdh_intr_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh); +extern int bcmsdh_intr_dereg(void *sdh); + +#if defined(DHD_DEBUG) +/* Query pending interrupt status from the host controller */ +extern bool bcmsdh_intr_pending(void *sdh); +#endif + +#ifdef BCMLXSDMMC +extern int bcmsdh_claim_host_and_lock(void *sdh); +extern int bcmsdh_release_host_and_unlock(void *sdh); +#endif /* BCMLXSDMMC */ + +/* Register a callback to be called if and when bcmsdh detects + * device removal. No-op in the case of non-removable/hardwired devices. + */ +extern int bcmsdh_devremove_reg(void *sdh, bcmsdh_cb_fn_t fn, void *argh); + +/* Access SDIO address space (e.g. CCCR) using CMD52 (single-byte interface). + * fn: function number + * addr: unmodified SDIO-space address + * data: data byte to write + * err: pointer to error code (or NULL) + */ +extern uint8 bcmsdh_cfg_read(void *sdh, uint func, uint32 addr, int *err); +extern void bcmsdh_cfg_write(void *sdh, uint func, uint32 addr, uint8 data, int *err); + +/* Read/Write 4bytes from/to cfg space */ +extern uint32 bcmsdh_cfg_read_word(void *sdh, uint fnc_num, uint32 addr, int *err); +extern void bcmsdh_cfg_write_word(void *sdh, uint fnc_num, uint32 addr, uint32 data, int *err); + +/* Read CIS content for specified function. + * fn: function whose CIS is being requested (0 is common CIS) + * cis: pointer to memory location to place results + * length: number of bytes to read + * Internally, this routine uses the values from the cis base regs (0x9-0xB) + * to form an SDIO-space address to read the data from. + */ +extern int bcmsdh_cis_read(void *sdh, uint func, uint8 *cis, uint length); + +/* Synchronous access to device (client) core registers via CMD53 to F1. + * addr: backplane address (i.e. >= regsva from attach) + * size: register width in bytes (2 or 4) + * data: data for register write + */ +extern uint32 bcmsdh_reg_read(void *sdh, uint32 addr, uint size); +extern uint32 bcmsdh_reg_write(void *sdh, uint32 addr, uint size, uint32 data); + +/* Indicate if last reg read/write failed */ +extern bool bcmsdh_regfail(void *sdh); + +/* Buffer transfer to/from device (client) core via cmd53. + * fn: function number + * addr: backplane address (i.e. >= regsva from attach) + * flags: backplane width, address increment, sync/async + * buf: pointer to memory data buffer + * nbytes: number of bytes to transfer to/from buf + * pkt: pointer to packet associated with buf (if any) + * complete: callback function for command completion (async only) + * handle: handle for completion callback (first arg in callback) + * Returns 0 or error code. + * NOTE: Async operation is not currently supported. + */ +typedef void (*bcmsdh_cmplt_fn_t)(void *handle, int status, bool sync_waiting); +extern int bcmsdh_send_buf(void *sdh, uint32 addr, uint fn, uint flags, + uint8 *buf, uint nbytes, void *pkt, + bcmsdh_cmplt_fn_t complete, void *handle); +extern int bcmsdh_recv_buf(void *sdh, uint32 addr, uint fn, uint flags, + uint8 *buf, uint nbytes, void *pkt, + bcmsdh_cmplt_fn_t complete, void *handle); + +/* Flags bits */ +#define SDIO_REQ_4BYTE 0x1 /* Four-byte target (backplane) width (vs. two-byte) */ +#define SDIO_REQ_FIXED 0x2 /* Fixed address (FIFO) (vs. incrementing address) */ +#define SDIO_REQ_ASYNC 0x4 /* Async request (vs. sync request) */ + +/* Pending (non-error) return code */ +#define BCME_PENDING 1 + +/* Read/write to memory block (F1, no FIFO) via CMD53 (sync only). + * rw: read or write (0/1) + * addr: direct SDIO address + * buf: pointer to memory data buffer + * nbytes: number of bytes to transfer to/from buf + * Returns 0 or error code. + */ +extern int bcmsdh_rwdata(void *sdh, uint rw, uint32 addr, uint8 *buf, uint nbytes); + +/* Issue an abort to the specified function */ +extern int bcmsdh_abort(void *sdh, uint fn); + +/* Start SDIO Host Controller communication */ +extern int bcmsdh_start(void *sdh, int stage); + +/* Stop SDIO Host Controller communication */ +extern int bcmsdh_stop(void *sdh); + +/* Wait system lock free */ +extern int bcmsdh_waitlockfree(void *sdh); + +/* Returns the "Device ID" of target device on the SDIO bus. */ +extern int bcmsdh_query_device(void *sdh); + +/* Returns the number of IO functions reported by the device */ +extern uint bcmsdh_query_iofnum(void *sdh); + +/* Miscellaneous knob tweaker. */ +extern int bcmsdh_iovar_op(void *sdh, const char *name, + void *params, int plen, void *arg, int len, bool set); + +/* Reset and reinitialize the device */ +extern int bcmsdh_reset(bcmsdh_info_t *sdh); + +/* helper functions */ + +extern void *bcmsdh_get_sdioh(bcmsdh_info_t *sdh); + +/* callback functions */ +typedef struct { + /* attach to device */ + void *(*attach)(uint16 vend_id, uint16 dev_id, uint16 bus, uint16 slot, + uint16 func, uint bustype, void * regsva, osl_t * osh, + void * param); + /* detach from device */ + void (*detach)(void *ch); +} bcmsdh_driver_t; + +/* platform specific/high level functions */ +extern int bcmsdh_register(bcmsdh_driver_t *driver); +extern void bcmsdh_unregister(void); +extern bool bcmsdh_chipmatch(uint16 vendor, uint16 device); +extern void bcmsdh_device_remove(void * sdh); + +#if defined(OOB_INTR_ONLY) +extern int bcmsdh_register_oob_intr(void * dhdp); +extern void bcmsdh_unregister_oob_intr(void); +extern void bcmsdh_oob_intr_set(bool enable); +#endif /* defined(OOB_INTR_ONLY) */ +/* Function to pass device-status bits to DHD. */ +extern uint32 bcmsdh_get_dstatus(void *sdh); + +/* Function to return current window addr */ +extern uint32 bcmsdh_cur_sbwad(void *sdh); + +/* Function to pass chipid and rev to lower layers for controlling pr's */ +extern void bcmsdh_chipinfo(void *sdh, uint32 chip, uint32 chiprev); + + +extern int bcmsdh_sleep(void *sdh, bool enab); + +/* GPIO support */ +extern int bcmsdh_gpio_init(void *sd); +extern bool bcmsdh_gpioin(void *sd, uint32 gpio); +extern int bcmsdh_gpioouten(void *sd, uint32 gpio); +extern int bcmsdh_gpioout(void *sd, uint32 gpio, bool enab); + +#endif /* _bcmsdh_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h b/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h new file mode 100644 index 0000000000000..db8ea596304c9 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h @@ -0,0 +1,123 @@ +/* + * BCMSDH Function Driver for the native SDIO/MMC driver in the Linux Kernel + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdh_sdmmc.h 314048 2012-02-09 20:31:56Z $ + */ + +#ifndef __BCMSDH_SDMMC_H__ +#define __BCMSDH_SDMMC_H__ + +#define sd_err(x) +#define sd_trace(x) +#define sd_info(x) +#define sd_debug(x) +#define sd_data(x) +#define sd_ctrl(x) + +#define sd_sync_dma(sd, read, nbytes) +#define sd_init_dma(sd) +#define sd_ack_intr(sd) +#define sd_wakeup(sd); + +/* Allocate/init/free per-OS private data */ +extern int sdioh_sdmmc_osinit(sdioh_info_t *sd); +extern void sdioh_sdmmc_osfree(sdioh_info_t *sd); + +#define sd_log(x) + +#define SDIOH_ASSERT(exp) \ + do { if (!(exp)) \ + printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \ + } while (0) + +#define BLOCK_SIZE_4318 64 +#define BLOCK_SIZE_4328 512 + +/* internal return code */ +#define SUCCESS 0 +#define ERROR 1 + +/* private bus modes */ +#define SDIOH_MODE_SD4 2 +#define CLIENT_INTR 0x100 /* Get rid of this! */ + +struct sdioh_info { + osl_t *osh; /* osh handler */ + bool client_intr_enabled; /* interrupt connnected flag */ + bool intr_handler_valid; /* client driver interrupt handler valid */ + sdioh_cb_fn_t intr_handler; /* registered interrupt handler */ + void *intr_handler_arg; /* argument to call interrupt handler */ + uint16 intmask; /* Current active interrupts */ + void *sdos_info; /* Pointer to per-OS private data */ + + uint irq; /* Client irq */ + int intrcount; /* Client interrupts */ + + bool sd_use_dma; /* DMA on CMD53 */ + bool sd_blockmode; /* sd_blockmode == FALSE => 64 Byte Cmd 53s. */ + /* Must be on for sd_multiblock to be effective */ + bool use_client_ints; /* If this is false, make sure to restore */ + int sd_mode; /* SD1/SD4/SPI */ + int client_block_size[SDIOD_MAX_IOFUNCS]; /* Blocksize */ + uint8 num_funcs; /* Supported funcs on client */ + uint32 com_cis_ptr; + uint32 func_cis_ptr[SDIOD_MAX_IOFUNCS]; + +#define SDIOH_SDMMC_MAX_SG_ENTRIES 32 + struct scatterlist sg_list[SDIOH_SDMMC_MAX_SG_ENTRIES]; + bool use_rxchain; +}; + +/************************************************************ + * Internal interfaces: per-port references into bcmsdh_sdmmc.c + */ + +/* Global message bits */ +extern uint sd_msglevel; + +/* OS-independent interrupt handler */ +extern bool check_client_intr(sdioh_info_t *sd); + +/* Core interrupt enable/disable of device interrupts */ +extern void sdioh_sdmmc_devintr_on(sdioh_info_t *sd); +extern void sdioh_sdmmc_devintr_off(sdioh_info_t *sd); + + +/************************************************************** + * Internal interfaces: bcmsdh_sdmmc.c references to per-port code + */ + +/* Register mapping routines */ +extern uint32 *sdioh_sdmmc_reg_map(osl_t *osh, int32 addr, int size); +extern void sdioh_sdmmc_reg_unmap(osl_t *osh, int32 addr, int size); + +/* Interrupt (de)registration routines */ +extern int sdioh_sdmmc_register_irq(sdioh_info_t *sd, uint irq); +extern void sdioh_sdmmc_free_irq(uint irq, sdioh_info_t *sd); + +typedef struct _BCMSDH_SDMMC_INSTANCE { + sdioh_info_t *sd; + struct sdio_func *func[SDIOD_MAX_IOFUNCS]; +} BCMSDH_SDMMC_INSTANCE, *PBCMSDH_SDMMC_INSTANCE; + +#endif /* __BCMSDH_SDMMC_H__ */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdpcm.h b/drivers/net/wireless/bcmdhd/include/bcmsdpcm.h new file mode 100644 index 0000000000000..1b9d39fee8fc7 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmsdpcm.h @@ -0,0 +1,274 @@ +/* + * Broadcom SDIO/PCMCIA + * Software-specific definitions shared between device and host side + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdpcm.h 277737 2011-08-16 17:54:59Z $ + */ + +#ifndef _bcmsdpcm_h_ +#define _bcmsdpcm_h_ + +/* + * Software allocation of To SB Mailbox resources + */ + +/* intstatus bits */ +#define I_SMB_NAK I_SMB_SW0 /* To SB Mailbox Frame NAK */ +#define I_SMB_INT_ACK I_SMB_SW1 /* To SB Mailbox Host Interrupt ACK */ +#define I_SMB_USE_OOB I_SMB_SW2 /* To SB Mailbox Use OOB Wakeup */ +#define I_SMB_DEV_INT I_SMB_SW3 /* To SB Mailbox Miscellaneous Interrupt */ + +#define I_TOSBMAIL (I_SMB_NAK | I_SMB_INT_ACK | I_SMB_USE_OOB | I_SMB_DEV_INT) + +/* tosbmailbox bits corresponding to intstatus bits */ +#define SMB_NAK (1 << 0) /* To SB Mailbox Frame NAK */ +#define SMB_INT_ACK (1 << 1) /* To SB Mailbox Host Interrupt ACK */ +#define SMB_USE_OOB (1 << 2) /* To SB Mailbox Use OOB Wakeup */ +#define SMB_DEV_INT (1 << 3) /* To SB Mailbox Miscellaneous Interrupt */ +#define SMB_MASK 0x0000000f /* To SB Mailbox Mask */ + +/* tosbmailboxdata */ +#define SMB_DATA_VERSION_MASK 0x00ff0000 /* host protocol version (sent with F2 enable) */ +#define SMB_DATA_VERSION_SHIFT 16 /* host protocol version (sent with F2 enable) */ + +/* + * Software allocation of To Host Mailbox resources + */ + +/* intstatus bits */ +#define I_HMB_FC_STATE I_HMB_SW0 /* To Host Mailbox Flow Control State */ +#define I_HMB_FC_CHANGE I_HMB_SW1 /* To Host Mailbox Flow Control State Changed */ +#define I_HMB_FRAME_IND I_HMB_SW2 /* To Host Mailbox Frame Indication */ +#define I_HMB_HOST_INT I_HMB_SW3 /* To Host Mailbox Miscellaneous Interrupt */ + +#define I_TOHOSTMAIL (I_HMB_FC_CHANGE | I_HMB_FRAME_IND | I_HMB_HOST_INT) + +/* tohostmailbox bits corresponding to intstatus bits */ +#define HMB_FC_ON (1 << 0) /* To Host Mailbox Flow Control State */ +#define HMB_FC_CHANGE (1 << 1) /* To Host Mailbox Flow Control State Changed */ +#define HMB_FRAME_IND (1 << 2) /* To Host Mailbox Frame Indication */ +#define HMB_HOST_INT (1 << 3) /* To Host Mailbox Miscellaneous Interrupt */ +#define HMB_MASK 0x0000000f /* To Host Mailbox Mask */ + +/* tohostmailboxdata */ +#define HMB_DATA_NAKHANDLED 0x01 /* we're ready to retransmit NAK'd frame to host */ +#define HMB_DATA_DEVREADY 0x02 /* we're ready to to talk to host after enable */ +#define HMB_DATA_FC 0x04 /* per prio flowcontrol update flag to host */ +#define HMB_DATA_FWREADY 0x08 /* firmware is ready for protocol activity */ +#define HMB_DATA_FWHALT 0x10 /* firmware has halted operation */ + +#define HMB_DATA_FCDATA_MASK 0xff000000 /* per prio flowcontrol data */ +#define HMB_DATA_FCDATA_SHIFT 24 /* per prio flowcontrol data */ + +#define HMB_DATA_VERSION_MASK 0x00ff0000 /* device protocol version (with devready) */ +#define HMB_DATA_VERSION_SHIFT 16 /* device protocol version (with devready) */ + +/* + * Software-defined protocol header + */ + +/* Current protocol version */ +#define SDPCM_PROT_VERSION 4 + +/* SW frame header */ +#define SDPCM_SEQUENCE_MASK 0x000000ff /* Sequence Number Mask */ +#define SDPCM_PACKET_SEQUENCE(p) (((uint8 *)p)[0] & 0xff) /* p starts w/SW Header */ + +#define SDPCM_CHANNEL_MASK 0x00000f00 /* Channel Number Mask */ +#define SDPCM_CHANNEL_SHIFT 8 /* Channel Number Shift */ +#define SDPCM_PACKET_CHANNEL(p) (((uint8 *)p)[1] & 0x0f) /* p starts w/SW Header */ + +#define SDPCM_FLAGS_MASK 0x0000f000 /* Mask of flag bits */ +#define SDPCM_FLAGS_SHIFT 12 /* Flag bits shift */ +#define SDPCM_PACKET_FLAGS(p) ((((uint8 *)p)[1] & 0xf0) >> 4) /* p starts w/SW Header */ + +/* Next Read Len: lookahead length of next frame, in 16-byte units (rounded up) */ +#define SDPCM_NEXTLEN_MASK 0x00ff0000 /* Next Read Len Mask */ +#define SDPCM_NEXTLEN_SHIFT 16 /* Next Read Len Shift */ +#define SDPCM_NEXTLEN_VALUE(p) ((((uint8 *)p)[2] & 0xff) << 4) /* p starts w/SW Header */ +#define SDPCM_NEXTLEN_OFFSET 2 + +/* Data Offset from SOF (HW Tag, SW Tag, Pad) */ +#define SDPCM_DOFFSET_OFFSET 3 /* Data Offset */ +#define SDPCM_DOFFSET_VALUE(p) (((uint8 *)p)[SDPCM_DOFFSET_OFFSET] & 0xff) +#define SDPCM_DOFFSET_MASK 0xff000000 +#define SDPCM_DOFFSET_SHIFT 24 + +#define SDPCM_FCMASK_OFFSET 4 /* Flow control */ +#define SDPCM_FCMASK_VALUE(p) (((uint8 *)p)[SDPCM_FCMASK_OFFSET ] & 0xff) +#define SDPCM_WINDOW_OFFSET 5 /* Credit based fc */ +#define SDPCM_WINDOW_VALUE(p) (((uint8 *)p)[SDPCM_WINDOW_OFFSET] & 0xff) +#define SDPCM_VERSION_OFFSET 6 /* Version # */ +#define SDPCM_VERSION_VALUE(p) (((uint8 *)p)[SDPCM_VERSION_OFFSET] & 0xff) +#define SDPCM_UNUSED_OFFSET 7 /* Spare */ +#define SDPCM_UNUSED_VALUE(p) (((uint8 *)p)[SDPCM_UNUSED_OFFSET] & 0xff) + +#define SDPCM_SWHEADER_LEN 8 /* SW header is 64 bits */ + +/* logical channel numbers */ +#define SDPCM_CONTROL_CHANNEL 0 /* Control Request/Response Channel Id */ +#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication Channel Id */ +#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv Channel Id */ +#define SDPCM_GLOM_CHANNEL 3 /* For coalesced packets (superframes) */ +#define SDPCM_TEST_CHANNEL 15 /* Reserved for test/debug packets */ +#define SDPCM_MAX_CHANNEL 15 + +#define SDPCM_SEQUENCE_WRAP 256 /* wrap-around val for eight-bit frame seq number */ + +#define SDPCM_FLAG_RESVD0 0x01 +#define SDPCM_FLAG_RESVD1 0x02 +#define SDPCM_FLAG_GSPI_TXENAB 0x04 +#define SDPCM_FLAG_GLOMDESC 0x08 /* Superframe descriptor mask */ + +/* For GLOM_CHANNEL frames, use a flag to indicate descriptor frame */ +#define SDPCM_GLOMDESC_FLAG (SDPCM_FLAG_GLOMDESC << SDPCM_FLAGS_SHIFT) + +#define SDPCM_GLOMDESC(p) (((uint8 *)p)[1] & 0x80) + +/* For TEST_CHANNEL packets, define another 4-byte header */ +#define SDPCM_TEST_HDRLEN 4 /* Generally: Cmd(1), Ext(1), Len(2); + * Semantics of Ext byte depend on command. + * Len is current or requested frame length, not + * including test header; sent little-endian. + */ +#define SDPCM_TEST_DISCARD 0x01 /* Receiver discards. Ext is a pattern id. */ +#define SDPCM_TEST_ECHOREQ 0x02 /* Echo request. Ext is a pattern id. */ +#define SDPCM_TEST_ECHORSP 0x03 /* Echo response. Ext is a pattern id. */ +#define SDPCM_TEST_BURST 0x04 /* Receiver to send a burst. Ext is a frame count */ +#define SDPCM_TEST_SEND 0x05 /* Receiver sets send mode. Ext is boolean on/off */ + +/* Handy macro for filling in datagen packets with a pattern */ +#define SDPCM_TEST_FILL(byteno, id) ((uint8)(id + byteno)) + +/* + * Software counters (first part matches hardware counters) + */ + +typedef volatile struct { + uint32 cmd52rd; /* Cmd52RdCount, SDIO: cmd52 reads */ + uint32 cmd52wr; /* Cmd52WrCount, SDIO: cmd52 writes */ + uint32 cmd53rd; /* Cmd53RdCount, SDIO: cmd53 reads */ + uint32 cmd53wr; /* Cmd53WrCount, SDIO: cmd53 writes */ + uint32 abort; /* AbortCount, SDIO: aborts */ + uint32 datacrcerror; /* DataCrcErrorCount, SDIO: frames w/CRC error */ + uint32 rdoutofsync; /* RdOutOfSyncCount, SDIO/PCMCIA: Rd Frm out of sync */ + uint32 wroutofsync; /* RdOutOfSyncCount, SDIO/PCMCIA: Wr Frm out of sync */ + uint32 writebusy; /* WriteBusyCount, SDIO: device asserted "busy" */ + uint32 readwait; /* ReadWaitCount, SDIO: no data ready for a read cmd */ + uint32 readterm; /* ReadTermCount, SDIO: read frame termination cmds */ + uint32 writeterm; /* WriteTermCount, SDIO: write frames termination cmds */ + uint32 rxdescuflo; /* receive descriptor underflows */ + uint32 rxfifooflo; /* receive fifo overflows */ + uint32 txfifouflo; /* transmit fifo underflows */ + uint32 runt; /* runt (too short) frames recv'd from bus */ + uint32 badlen; /* frame's rxh len does not match its hw tag len */ + uint32 badcksum; /* frame's hw tag chksum doesn't agree with len value */ + uint32 seqbreak; /* break in sequence # space from one rx frame to the next */ + uint32 rxfcrc; /* frame rx header indicates crc error */ + uint32 rxfwoos; /* frame rx header indicates write out of sync */ + uint32 rxfwft; /* frame rx header indicates write frame termination */ + uint32 rxfabort; /* frame rx header indicates frame aborted */ + uint32 woosint; /* write out of sync interrupt */ + uint32 roosint; /* read out of sync interrupt */ + uint32 rftermint; /* read frame terminate interrupt */ + uint32 wftermint; /* write frame terminate interrupt */ +} sdpcmd_cnt_t; + +/* + * Register Access Macros + */ + +#define SDIODREV_IS(var, val) ((var) == (val)) +#define SDIODREV_GE(var, val) ((var) >= (val)) +#define SDIODREV_GT(var, val) ((var) > (val)) +#define SDIODREV_LT(var, val) ((var) < (val)) +#define SDIODREV_LE(var, val) ((var) <= (val)) + +#define SDIODDMAREG32(h, dir, chnl) \ + ((dir) == DMA_TX ? \ + (void *)(uintptr)&((h)->regs->dma.sdiod32.dma32regs[chnl].xmt) : \ + (void *)(uintptr)&((h)->regs->dma.sdiod32.dma32regs[chnl].rcv)) + +#define SDIODDMAREG64(h, dir, chnl) \ + ((dir) == DMA_TX ? \ + (void *)(uintptr)&((h)->regs->dma.sdiod64.dma64regs[chnl].xmt) : \ + (void *)(uintptr)&((h)->regs->dma.sdiod64.dma64regs[chnl].rcv)) + +#define SDIODDMAREG(h, dir, chnl) \ + (SDIODREV_LT((h)->corerev, 1) ? \ + SDIODDMAREG32((h), (dir), (chnl)) : \ + SDIODDMAREG64((h), (dir), (chnl))) + +#define PCMDDMAREG(h, dir, chnl) \ + ((dir) == DMA_TX ? \ + (void *)(uintptr)&((h)->regs->dma.pcm32.dmaregs.xmt) : \ + (void *)(uintptr)&((h)->regs->dma.pcm32.dmaregs.rcv)) + +#define SDPCMDMAREG(h, dir, chnl, coreid) \ + ((coreid) == SDIOD_CORE_ID ? \ + SDIODDMAREG(h, dir, chnl) : \ + PCMDDMAREG(h, dir, chnl)) + +#define SDIODFIFOREG(h, corerev) \ + (SDIODREV_LT((corerev), 1) ? \ + ((dma32diag_t *)(uintptr)&((h)->regs->dma.sdiod32.dmafifo)) : \ + ((dma32diag_t *)(uintptr)&((h)->regs->dma.sdiod64.dmafifo))) + +#define PCMDFIFOREG(h) \ + ((dma32diag_t *)(uintptr)&((h)->regs->dma.pcm32.dmafifo)) + +#define SDPCMFIFOREG(h, coreid, corerev) \ + ((coreid) == SDIOD_CORE_ID ? \ + SDIODFIFOREG(h, corerev) : \ + PCMDFIFOREG(h)) + +/* + * Shared structure between dongle and the host. + * The structure contains pointers to trap or assert information. + */ +#define SDPCM_SHARED_VERSION 0x0001 +#define SDPCM_SHARED_VERSION_MASK 0x00FF +#define SDPCM_SHARED_ASSERT_BUILT 0x0100 +#define SDPCM_SHARED_ASSERT 0x0200 +#define SDPCM_SHARED_TRAP 0x0400 +#define SDPCM_SHARED_IN_BRPT 0x0800 +#define SDPCM_SHARED_SET_BRPT 0x1000 +#define SDPCM_SHARED_PENDING_BRPT 0x2000 + +typedef struct { + uint32 flags; + uint32 trap_addr; + uint32 assert_exp_addr; + uint32 assert_file_addr; + uint32 assert_line; + uint32 console_addr; /* Address of hndrte_cons_t */ + uint32 msgtrace_addr; + uint32 brpt_addr; +} sdpcm_shared_t; + +extern sdpcm_shared_t sdpcm_shared; + +/* Function can be used to notify host of FW halt */ +extern void sdpcmd_fwhalt(void); + +#endif /* _bcmsdpcm_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdspi.h b/drivers/net/wireless/bcmdhd/include/bcmsdspi.h new file mode 100644 index 0000000000000..a62bee42b2ba8 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmsdspi.h @@ -0,0 +1,135 @@ +/* + * SD-SPI Protocol Conversion - BCMSDH->SPI Translation Layer + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdspi.h 277737 2011-08-16 17:54:59Z $ + */ +#ifndef _BCM_SD_SPI_H +#define _BCM_SD_SPI_H + +/* global msglevel for debug messages - bitvals come from sdiovar.h */ + +#define sd_err(x) +#define sd_trace(x) +#define sd_info(x) +#define sd_debug(x) +#define sd_data(x) +#define sd_ctrl(x) + +#define sd_log(x) + +#define SDIOH_ASSERT(exp) \ + do { if (!(exp)) \ + printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \ + } while (0) + +#define BLOCK_SIZE_4318 64 +#define BLOCK_SIZE_4328 512 + +/* internal return code */ +#define SUCCESS 0 +#undef ERROR +#define ERROR 1 + +/* private bus modes */ +#define SDIOH_MODE_SPI 0 + +#define USE_BLOCKMODE 0x2 /* Block mode can be single block or multi */ +#define USE_MULTIBLOCK 0x4 + +struct sdioh_info { + uint cfg_bar; /* pci cfg address for bar */ + uint32 caps; /* cached value of capabilities reg */ + uint bar0; /* BAR0 for PCI Device */ + osl_t *osh; /* osh handler */ + void *controller; /* Pointer to SPI Controller's private data struct */ + + uint lockcount; /* nest count of sdspi_lock() calls */ + bool client_intr_enabled; /* interrupt connnected flag */ + bool intr_handler_valid; /* client driver interrupt handler valid */ + sdioh_cb_fn_t intr_handler; /* registered interrupt handler */ + void *intr_handler_arg; /* argument to call interrupt handler */ + bool initialized; /* card initialized */ + uint32 target_dev; /* Target device ID */ + uint32 intmask; /* Current active interrupts */ + void *sdos_info; /* Pointer to per-OS private data */ + + uint32 controller_type; /* Host controller type */ + uint8 version; /* Host Controller Spec Compliance Version */ + uint irq; /* Client irq */ + uint32 intrcount; /* Client interrupts */ + uint32 local_intrcount; /* Controller interrupts */ + bool host_init_done; /* Controller initted */ + bool card_init_done; /* Client SDIO interface initted */ + bool polled_mode; /* polling for command completion */ + + bool sd_use_dma; /* DMA on CMD53 */ + bool sd_blockmode; /* sd_blockmode == FALSE => 64 Byte Cmd 53s. */ + /* Must be on for sd_multiblock to be effective */ + bool use_client_ints; /* If this is false, make sure to restore */ + bool got_hcint; /* Host Controller interrupt. */ + /* polling hack in wl_linux.c:wl_timer() */ + int adapter_slot; /* Maybe dealing with multiple slots/controllers */ + int sd_mode; /* SD1/SD4/SPI */ + int client_block_size[SDIOD_MAX_IOFUNCS]; /* Blocksize */ + uint32 data_xfer_count; /* Current register transfer size */ + uint32 cmd53_wr_data; /* Used to pass CMD53 write data */ + uint32 card_response; /* Used to pass back response status byte */ + uint32 card_rsp_data; /* Used to pass back response data word */ + uint16 card_rca; /* Current Address */ + uint8 num_funcs; /* Supported funcs on client */ + uint32 com_cis_ptr; + uint32 func_cis_ptr[SDIOD_MAX_IOFUNCS]; + void *dma_buf; + ulong dma_phys; + int r_cnt; /* rx count */ + int t_cnt; /* tx_count */ +}; + +/************************************************************ + * Internal interfaces: per-port references into bcmsdspi.c + */ + +/* Global message bits */ +extern uint sd_msglevel; + +/************************************************************** + * Internal interfaces: bcmsdspi.c references to per-port code + */ + +/* Register mapping routines */ +extern uint32 *spi_reg_map(osl_t *osh, uintptr addr, int size); +extern void spi_reg_unmap(osl_t *osh, uintptr addr, int size); + +/* Interrupt (de)registration routines */ +extern int spi_register_irq(sdioh_info_t *sd, uint irq); +extern void spi_free_irq(uint irq, sdioh_info_t *sd); + +/* OS-specific interrupt wrappers (atomic interrupt enable/disable) */ +extern void spi_lock(sdioh_info_t *sd); +extern void spi_unlock(sdioh_info_t *sd); + +/* Allocate/init/free per-OS private data */ +extern int spi_osinit(sdioh_info_t *sd); +extern void spi_osfree(sdioh_info_t *sd); + +#endif /* _BCM_SD_SPI_H */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmsdstd.h b/drivers/net/wireless/bcmdhd/include/bcmsdstd.h new file mode 100644 index 0000000000000..c7382540b84fa --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmsdstd.h @@ -0,0 +1,248 @@ +/* + * 'Standard' SDIO HOST CONTROLLER driver + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmsdstd.h 324819 2012-03-30 12:15:19Z $ + */ +#ifndef _BCM_SD_STD_H +#define _BCM_SD_STD_H + +/* global msglevel for debug messages - bitvals come from sdiovar.h */ +#define sd_err(x) do { if (sd_msglevel & SDH_ERROR_VAL) printf x; } while (0) +#define sd_trace(x) +#define sd_info(x) +#define sd_debug(x) +#define sd_data(x) +#define sd_ctrl(x) +#define sd_dma(x) + +#define sd_sync_dma(sd, read, nbytes) +#define sd_init_dma(sd) +#define sd_ack_intr(sd) +#define sd_wakeup(sd); +/* Allocate/init/free per-OS private data */ +extern int sdstd_osinit(sdioh_info_t *sd); +extern void sdstd_osfree(sdioh_info_t *sd); + +#define sd_log(x) + +#define SDIOH_ASSERT(exp) \ + do { if (!(exp)) \ + printf("!!!ASSERT fail: file %s lines %d", __FILE__, __LINE__); \ + } while (0) + +#define BLOCK_SIZE_4318 64 +#define BLOCK_SIZE_4328 512 + +/* internal return code */ +#define SUCCESS 0 +#define ERROR 1 + +/* private bus modes */ +#define SDIOH_MODE_SPI 0 +#define SDIOH_MODE_SD1 1 +#define SDIOH_MODE_SD4 2 + +#define MAX_SLOTS 6 /* For PCI: Only 6 BAR entries => 6 slots */ +#define SDIOH_REG_WINSZ 0x100 /* Number of registers in Standard Host Controller */ + +#define SDIOH_TYPE_ARASAN_HDK 1 +#define SDIOH_TYPE_BCM27XX 2 +#define SDIOH_TYPE_TI_PCIXX21 4 /* TI PCIxx21 Standard Host Controller */ +#define SDIOH_TYPE_RICOH_R5C822 5 /* Ricoh Co Ltd R5C822 SD/SDIO/MMC/MS/MSPro Host Adapter */ +#define SDIOH_TYPE_JMICRON 6 /* JMicron Standard SDIO Host Controller */ + +/* For linux, allow yielding for dongle */ +#define BCMSDYIELD + +/* Expected card status value for CMD7 */ +#define SDIOH_CMD7_EXP_STATUS 0x00001E00 + +#define RETRIES_LARGE 100000 +#define sdstd_os_yield(sd) do {} while (0) +#define RETRIES_SMALL 100 + + +#define USE_BLOCKMODE 0x2 /* Block mode can be single block or multi */ +#define USE_MULTIBLOCK 0x4 + +#define USE_FIFO 0x8 /* Fifo vs non-fifo */ + +#define CLIENT_INTR 0x100 /* Get rid of this! */ + +#define HC_INTR_RETUNING 0x1000 + + +struct sdioh_info { + uint cfg_bar; /* pci cfg address for bar */ + uint32 caps; /* cached value of capabilities reg */ + uint32 curr_caps; /* max current capabilities reg */ + + osl_t *osh; /* osh handler */ + volatile char *mem_space; /* pci device memory va */ + uint lockcount; /* nest count of sdstd_lock() calls */ + bool client_intr_enabled; /* interrupt connnected flag */ + bool intr_handler_valid; /* client driver interrupt handler valid */ + sdioh_cb_fn_t intr_handler; /* registered interrupt handler */ + void *intr_handler_arg; /* argument to call interrupt handler */ + bool initialized; /* card initialized */ + uint target_dev; /* Target device ID */ + uint16 intmask; /* Current active interrupts */ + void *sdos_info; /* Pointer to per-OS private data */ + + uint32 controller_type; /* Host controller type */ + uint8 version; /* Host Controller Spec Compliance Version */ + uint irq; /* Client irq */ + int intrcount; /* Client interrupts */ + int local_intrcount; /* Controller interrupts */ + bool host_init_done; /* Controller initted */ + bool card_init_done; /* Client SDIO interface initted */ + bool polled_mode; /* polling for command completion */ + + bool sd_blockmode; /* sd_blockmode == FALSE => 64 Byte Cmd 53s. */ + /* Must be on for sd_multiblock to be effective */ + bool use_client_ints; /* If this is false, make sure to restore */ + /* polling hack in wl_linux.c:wl_timer() */ + int adapter_slot; /* Maybe dealing with multiple slots/controllers */ + int sd_mode; /* SD1/SD4/SPI */ + int client_block_size[SDIOD_MAX_IOFUNCS]; /* Blocksize */ + uint32 data_xfer_count; /* Current transfer */ + uint16 card_rca; /* Current Address */ + int8 sd_dma_mode; /* DMA Mode (PIO, SDMA, ... ADMA2) on CMD53 */ + uint8 num_funcs; /* Supported funcs on client */ + uint32 com_cis_ptr; + uint32 func_cis_ptr[SDIOD_MAX_IOFUNCS]; + void *dma_buf; /* DMA Buffer virtual address */ + ulong dma_phys; /* DMA Buffer physical address */ + void *adma2_dscr_buf; /* ADMA2 Descriptor Buffer virtual address */ + ulong adma2_dscr_phys; /* ADMA2 Descriptor Buffer physical address */ + + /* adjustments needed to make the dma align properly */ + void *dma_start_buf; + ulong dma_start_phys; + uint alloced_dma_size; + void *adma2_dscr_start_buf; + ulong adma2_dscr_start_phys; + uint alloced_adma2_dscr_size; + + int r_cnt; /* rx count */ + int t_cnt; /* tx_count */ + bool got_hcint; /* local interrupt flag */ + uint16 last_intrstatus; /* to cache intrstatus */ + int host_UHSISupported; /* whether UHSI is supported for HC. */ + int card_UHSI_voltage_Supported; /* whether UHSI is supported for + * Card in terms of Voltage [1.8 or 3.3]. + */ + int global_UHSI_Supp; /* type of UHSI support in both host and card. + * HOST_SDR_UNSUPP: capabilities not supported/matched + * HOST_SDR_12_25: SDR12 and SDR25 supported + * HOST_SDR_50_104_DDR: one of SDR50/SDR104 or DDR50 supptd + */ + int sd3_dat_state; /* data transfer state used for retuning check */ + int sd3_tun_state; /* tuning state used for retuning check */ + bool sd3_tuning_reqd; /* tuning requirement parameter */ + uint32 caps3; /* cached value of 32 MSbits capabilities reg (SDIO 3.0) */ +}; + +#define DMA_MODE_NONE 0 +#define DMA_MODE_SDMA 1 +#define DMA_MODE_ADMA1 2 +#define DMA_MODE_ADMA2 3 +#define DMA_MODE_ADMA2_64 4 +#define DMA_MODE_AUTO -1 + +#define USE_DMA(sd) ((bool)((sd->sd_dma_mode > 0) ? TRUE : FALSE)) + +/* States for Tuning and corr data */ +#define TUNING_IDLE 0 +#define TUNING_START 1 +#define TUNING_START_AFTER_DAT 2 +#define TUNING_ONGOING 3 + +#define DATA_TRANSFER_IDLE 0 +#define DATA_TRANSFER_ONGOING 1 +#define CHECK_TUNING_PRE_DATA 1 +#define CHECK_TUNING_POST_DATA 2 + + +/************************************************************ + * Internal interfaces: per-port references into bcmsdstd.c + */ + +/* Global message bits */ +extern uint sd_msglevel; + +/* OS-independent interrupt handler */ +extern bool check_client_intr(sdioh_info_t *sd); + +/* Core interrupt enable/disable of device interrupts */ +extern void sdstd_devintr_on(sdioh_info_t *sd); +extern void sdstd_devintr_off(sdioh_info_t *sd); + +/* Enable/disable interrupts for local controller events */ +extern void sdstd_intrs_on(sdioh_info_t *sd, uint16 norm, uint16 err); +extern void sdstd_intrs_off(sdioh_info_t *sd, uint16 norm, uint16 err); + +/* Wait for specified interrupt and error bits to be set */ +extern void sdstd_spinbits(sdioh_info_t *sd, uint16 norm, uint16 err); + + +/************************************************************** + * Internal interfaces: bcmsdstd.c references to per-port code + */ + +/* Register mapping routines */ +extern uint32 *sdstd_reg_map(osl_t *osh, int32 addr, int size); +extern void sdstd_reg_unmap(osl_t *osh, int32 addr, int size); + +/* Interrupt (de)registration routines */ +extern int sdstd_register_irq(sdioh_info_t *sd, uint irq); +extern void sdstd_free_irq(uint irq, sdioh_info_t *sd); + +/* OS-specific interrupt wrappers (atomic interrupt enable/disable) */ +extern void sdstd_lock(sdioh_info_t *sd); +extern void sdstd_unlock(sdioh_info_t *sd); +extern void sdstd_waitlockfree(sdioh_info_t *sd); + +/* OS-specific wait-for-interrupt-or-status */ +extern int sdstd_waitbits(sdioh_info_t *sd, uint16 norm, uint16 err, bool yield, uint16 *bits); + +/* used by bcmsdstd_linux [implemented in sdstd] */ +extern void sdstd_3_enable_retuning_int(sdioh_info_t *sd); +extern void sdstd_3_disable_retuning_int(sdioh_info_t *sd); +extern bool sdstd_3_is_retuning_int_set(sdioh_info_t *sd); +extern void sdstd_3_check_and_do_tuning(sdioh_info_t *sd, int tuning_param); +extern bool sdstd_3_check_and_set_retuning(sdioh_info_t *sd); +extern int sdstd_3_get_tune_state(sdioh_info_t *sd); +extern int sdstd_3_get_data_state(sdioh_info_t *sd); +extern void sdstd_3_set_tune_state(sdioh_info_t *sd, int state); +extern void sdstd_3_set_data_state(sdioh_info_t *sd, int state); +extern uint8 sdstd_3_get_tuning_exp(sdioh_info_t *sd); +extern uint32 sdstd_3_get_uhsi_clkmode(sdioh_info_t *sd); +extern int sdstd_3_clk_tuning(sdioh_info_t *sd, uint32 sd3ClkMode); + +/* used by sdstd [implemented in bcmsdstd_linux/ndis] */ +extern void sdstd_3_start_tuning(sdioh_info_t *sd); +extern void sdstd_3_osinit_tuning(sdioh_info_t *sd); +extern void sdstd_3_osclean_tuning(sdioh_info_t *sd); + +#endif /* _BCM_SD_STD_H */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmspi.h b/drivers/net/wireless/bcmdhd/include/bcmspi.h new file mode 100644 index 0000000000000..34a02d00c6bd0 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmspi.h @@ -0,0 +1,40 @@ +/* + * Broadcom SPI Low-Level Hardware Driver API + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmspi.h 277737 2011-08-16 17:54:59Z $ + */ +#ifndef _BCM_SPI_H +#define _BCM_SPI_H + +extern void spi_devintr_off(sdioh_info_t *sd); +extern void spi_devintr_on(sdioh_info_t *sd); +extern bool spi_start_clock(sdioh_info_t *sd, uint16 new_sd_divisor); +extern bool spi_controller_highspeed_mode(sdioh_info_t *sd, bool hsmode); +extern bool spi_check_client_intr(sdioh_info_t *sd, int *is_dev_intr); +extern bool spi_hw_attach(sdioh_info_t *sd); +extern bool spi_hw_detach(sdioh_info_t *sd); +extern void spi_sendrecv(sdioh_info_t *sd, uint8 *msg_out, uint8 *msg_in, int msglen); +extern void spi_spinbits(sdioh_info_t *sd); +extern void spi_waitbits(sdioh_info_t *sd, bool yield); + +#endif /* _BCM_SPI_H */ diff --git a/drivers/net/wireless/bcmdhd/include/bcmutils.h b/drivers/net/wireless/bcmdhd/include/bcmutils.h new file mode 100644 index 0000000000000..6849c26da8372 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmutils.h @@ -0,0 +1,720 @@ +/* + * Misc useful os-independent macros and functions. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmutils.h 294991 2011-11-09 00:17:28Z $ + */ + + +#ifndef _bcmutils_h_ +#define _bcmutils_h_ + +#define bcm_strcpy_s(dst, noOfElements, src) strcpy((dst), (src)) +#define bcm_strncpy_s(dst, noOfElements, src, count) strncpy((dst), (src), (count)) +#define bcm_strcat_s(dst, noOfElements, src) strcat((dst), (src)) + +#ifdef __cplusplus +extern "C" { +#endif + + +#define _BCM_U 0x01 +#define _BCM_L 0x02 +#define _BCM_D 0x04 +#define _BCM_C 0x08 +#define _BCM_P 0x10 +#define _BCM_S 0x20 +#define _BCM_X 0x40 +#define _BCM_SP 0x80 + +extern const unsigned char bcm_ctype[]; +#define bcm_ismask(x) (bcm_ctype[(int)(unsigned char)(x)]) + +#define bcm_isalnum(c) ((bcm_ismask(c)&(_BCM_U|_BCM_L|_BCM_D)) != 0) +#define bcm_isalpha(c) ((bcm_ismask(c)&(_BCM_U|_BCM_L)) != 0) +#define bcm_iscntrl(c) ((bcm_ismask(c)&(_BCM_C)) != 0) +#define bcm_isdigit(c) ((bcm_ismask(c)&(_BCM_D)) != 0) +#define bcm_isgraph(c) ((bcm_ismask(c)&(_BCM_P|_BCM_U|_BCM_L|_BCM_D)) != 0) +#define bcm_islower(c) ((bcm_ismask(c)&(_BCM_L)) != 0) +#define bcm_isprint(c) ((bcm_ismask(c)&(_BCM_P|_BCM_U|_BCM_L|_BCM_D|_BCM_SP)) != 0) +#define bcm_ispunct(c) ((bcm_ismask(c)&(_BCM_P)) != 0) +#define bcm_isspace(c) ((bcm_ismask(c)&(_BCM_S)) != 0) +#define bcm_isupper(c) ((bcm_ismask(c)&(_BCM_U)) != 0) +#define bcm_isxdigit(c) ((bcm_ismask(c)&(_BCM_D|_BCM_X)) != 0) +#define bcm_tolower(c) (bcm_isupper((c)) ? ((c) + 'a' - 'A') : (c)) +#define bcm_toupper(c) (bcm_islower((c)) ? ((c) + 'A' - 'a') : (c)) + + + +struct bcmstrbuf { + char *buf; + unsigned int size; + char *origbuf; + unsigned int origsize; +}; + + +#ifdef BCMDRIVER +#include + +#define GPIO_PIN_NOTDEFINED 0x20 + + +#define SPINWAIT(exp, us) { \ + uint countdown = (us) + 9; \ + while ((exp) && (countdown >= 10)) {\ + OSL_DELAY(10); \ + countdown -= 10; \ + } \ +} + + +#ifndef PKTQ_LEN_DEFAULT +#define PKTQ_LEN_DEFAULT 128 +#endif +#ifndef PKTQ_MAX_PREC +#define PKTQ_MAX_PREC 16 +#endif + +typedef struct pktq_prec { + void *head; + void *tail; + uint16 len; + uint16 max; +} pktq_prec_t; + + + +struct pktq { + uint16 num_prec; + uint16 hi_prec; + uint16 max; + uint16 len; + + struct pktq_prec q[PKTQ_MAX_PREC]; +}; + + +struct spktq { + uint16 num_prec; + uint16 hi_prec; + uint16 max; + uint16 len; + + struct pktq_prec q[1]; +}; + +#define PKTQ_PREC_ITER(pq, prec) for (prec = (pq)->num_prec - 1; prec >= 0; prec--) + + +typedef bool (*ifpkt_cb_t)(void*, int); + +#ifdef BCMPKTPOOL +#define POOL_ENAB(pool) ((pool) && (pool)->inited) +#if defined(BCM4329C0) +#define SHARED_POOL (pktpool_shared_ptr) +#else +#define SHARED_POOL (pktpool_shared) +#endif +#else +#define POOL_ENAB(bus) 0 +#define SHARED_POOL ((struct pktpool *)NULL) +#endif + +#ifndef PKTPOOL_LEN_MAX +#define PKTPOOL_LEN_MAX 40 +#endif +#define PKTPOOL_CB_MAX 3 + +struct pktpool; +typedef void (*pktpool_cb_t)(struct pktpool *pool, void *arg); +typedef struct { + pktpool_cb_t cb; + void *arg; +} pktpool_cbinfo_t; + +#ifdef BCMDBG_POOL + +#define POOL_IDLE 0 +#define POOL_RXFILL 1 +#define POOL_RXDH 2 +#define POOL_RXD11 3 +#define POOL_TXDH 4 +#define POOL_TXD11 5 +#define POOL_AMPDU 6 +#define POOL_TXENQ 7 + +typedef struct { + void *p; + uint32 cycles; + uint32 dur; +} pktpool_dbg_t; + +typedef struct { + uint8 txdh; + uint8 txd11; + uint8 enq; + uint8 rxdh; + uint8 rxd11; + uint8 rxfill; + uint8 idle; +} pktpool_stats_t; +#endif + +typedef struct pktpool { + bool inited; + uint16 r; + uint16 w; + uint16 len; + uint16 maxlen; + uint16 plen; + bool istx; + bool empty; + uint8 cbtoggle; + uint8 cbcnt; + uint8 ecbcnt; + bool emptycb_disable; + pktpool_cbinfo_t cbs[PKTPOOL_CB_MAX]; + pktpool_cbinfo_t ecbs[PKTPOOL_CB_MAX]; + void *q[PKTPOOL_LEN_MAX + 1]; + +#ifdef BCMDBG_POOL + uint8 dbg_cbcnt; + pktpool_cbinfo_t dbg_cbs[PKTPOOL_CB_MAX]; + uint16 dbg_qlen; + pktpool_dbg_t dbg_q[PKTPOOL_LEN_MAX + 1]; +#endif +} pktpool_t; + +#if defined(BCM4329C0) +extern pktpool_t *pktpool_shared_ptr; +#else +extern pktpool_t *pktpool_shared; +#endif + +extern int pktpool_init(osl_t *osh, pktpool_t *pktp, int *pktplen, int plen, bool istx); +extern int pktpool_deinit(osl_t *osh, pktpool_t *pktp); +extern int pktpool_fill(osl_t *osh, pktpool_t *pktp, bool minimal); +extern void* pktpool_get(pktpool_t *pktp); +extern void pktpool_free(pktpool_t *pktp, void *p); +extern int pktpool_add(pktpool_t *pktp, void *p); +extern uint16 pktpool_avail(pktpool_t *pktp); +extern int pktpool_avail_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg); +extern int pktpool_empty_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg); +extern int pktpool_setmaxlen(pktpool_t *pktp, uint16 maxlen); +extern int pktpool_setmaxlen_strict(osl_t *osh, pktpool_t *pktp, uint16 maxlen); +extern void pktpool_emptycb_disable(pktpool_t *pktp, bool disable); +extern bool pktpool_emptycb_disabled(pktpool_t *pktp); + +#define POOLPTR(pp) ((pktpool_t *)(pp)) +#define pktpool_len(pp) (POOLPTR(pp)->len - 1) +#define pktpool_plen(pp) (POOLPTR(pp)->plen) +#define pktpool_maxlen(pp) (POOLPTR(pp)->maxlen) + +#ifdef BCMDBG_POOL +extern int pktpool_dbg_register(pktpool_t *pktp, pktpool_cb_t cb, void *arg); +extern int pktpool_start_trigger(pktpool_t *pktp, void *p); +extern int pktpool_dbg_dump(pktpool_t *pktp); +extern int pktpool_dbg_notify(pktpool_t *pktp); +extern int pktpool_stats_dump(pktpool_t *pktp, pktpool_stats_t *stats); +#endif + + + +struct ether_addr; + +extern int ether_isbcast(const void *ea); +extern int ether_isnulladdr(const void *ea); + + + +#define pktq_psetmax(pq, prec, _max) ((pq)->q[prec].max = (_max)) +#define pktq_plen(pq, prec) ((pq)->q[prec].len) +#define pktq_pavail(pq, prec) ((pq)->q[prec].max - (pq)->q[prec].len) +#define pktq_pfull(pq, prec) ((pq)->q[prec].len >= (pq)->q[prec].max) +#define pktq_pempty(pq, prec) ((pq)->q[prec].len == 0) + +#define pktq_ppeek(pq, prec) ((pq)->q[prec].head) +#define pktq_ppeek_tail(pq, prec) ((pq)->q[prec].tail) + +extern void *pktq_penq(struct pktq *pq, int prec, void *p); +extern void *pktq_penq_head(struct pktq *pq, int prec, void *p); +extern void *pktq_pdeq(struct pktq *pq, int prec); +extern void *pktq_pdeq_tail(struct pktq *pq, int prec); + +extern void pktq_pflush(osl_t *osh, struct pktq *pq, int prec, bool dir, + ifpkt_cb_t fn, int arg); + +extern bool pktq_pdel(struct pktq *pq, void *p, int prec); + + + +extern int pktq_mlen(struct pktq *pq, uint prec_bmp); +extern void *pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out); + + + +#define pktq_len(pq) ((int)(pq)->len) +#define pktq_max(pq) ((int)(pq)->max) +#define pktq_avail(pq) ((int)((pq)->max - (pq)->len)) +#define pktq_full(pq) ((pq)->len >= (pq)->max) +#define pktq_empty(pq) ((pq)->len == 0) + + +#define pktenq(pq, p) pktq_penq(((struct pktq *)pq), 0, (p)) +#define pktenq_head(pq, p) pktq_penq_head(((struct pktq *)pq), 0, (p)) +#define pktdeq(pq) pktq_pdeq(((struct pktq *)pq), 0) +#define pktdeq_tail(pq) pktq_pdeq_tail(((struct pktq *)pq), 0) +#define pktqinit(pq, len) pktq_init(((struct pktq *)pq), 1, len) + +extern void pktq_init(struct pktq *pq, int num_prec, int max_len); + +extern void *pktq_deq(struct pktq *pq, int *prec_out); +extern void *pktq_deq_tail(struct pktq *pq, int *prec_out); +extern void *pktq_peek(struct pktq *pq, int *prec_out); +extern void *pktq_peek_tail(struct pktq *pq, int *prec_out); +extern void pktq_flush(osl_t *osh, struct pktq *pq, bool dir, ifpkt_cb_t fn, int arg); + + + +extern uint pktcopy(osl_t *osh, void *p, uint offset, int len, uchar *buf); +extern uint pktfrombuf(osl_t *osh, void *p, uint offset, int len, uchar *buf); +extern uint pkttotlen(osl_t *osh, void *p); +extern void *pktlast(osl_t *osh, void *p); +extern uint pktsegcnt(osl_t *osh, void *p); + + +extern uint pktsetprio(void *pkt, bool update_vtag); +#define PKTPRIO_VDSCP 0x100 +#define PKTPRIO_VLAN 0x200 +#define PKTPRIO_UPD 0x400 +#define PKTPRIO_DSCP 0x800 + + +extern int bcm_atoi(char *s); +extern ulong bcm_strtoul(char *cp, char **endp, uint base); +extern char *bcmstrstr(char *haystack, char *needle); +extern char *bcmstrcat(char *dest, const char *src); +extern char *bcmstrncat(char *dest, const char *src, uint size); +extern ulong wchar2ascii(char *abuf, ushort *wbuf, ushort wbuflen, ulong abuflen); +char* bcmstrtok(char **string, const char *delimiters, char *tokdelim); +int bcmstricmp(const char *s1, const char *s2); +int bcmstrnicmp(const char* s1, const char* s2, int cnt); + + + +extern char *bcm_ether_ntoa(const struct ether_addr *ea, char *buf); +extern int bcm_ether_atoe(char *p, struct ether_addr *ea); + + +struct ipv4_addr; +extern char *bcm_ip_ntoa(struct ipv4_addr *ia, char *buf); + + +extern void bcm_mdelay(uint ms); + +#define NVRAM_RECLAIM_CHECK(name) + +extern char *getvar(char *vars, const char *name); +extern int getintvar(char *vars, const char *name); +extern int getintvararray(char *vars, const char *name, int index); +extern int getintvararraysize(char *vars, const char *name); +extern uint getgpiopin(char *vars, char *pin_name, uint def_pin); +#define bcm_perf_enable() +#define bcmstats(fmt) +#define bcmlog(fmt, a1, a2) +#define bcmdumplog(buf, size) *buf = '\0' +#define bcmdumplogent(buf, idx) -1 + +#define bcmtslog(tstamp, fmt, a1, a2) +#define bcmprinttslogs() +#define bcmprinttstamp(us) + +extern char *bcm_nvram_vars(uint *length); +extern int bcm_nvram_cache(void *sih); + + + + +typedef struct bcm_iovar { + const char *name; + uint16 varid; + uint16 flags; + uint16 type; + uint16 minlen; +} bcm_iovar_t; + + + + +#define IOV_GET 0 +#define IOV_SET 1 + + +#define IOV_GVAL(id) ((id)*2) +#define IOV_SVAL(id) (((id)*2)+IOV_SET) +#define IOV_ISSET(actionid) ((actionid & IOV_SET) == IOV_SET) +#define IOV_ID(actionid) (actionid >> 1) + + + +extern const bcm_iovar_t *bcm_iovar_lookup(const bcm_iovar_t *table, const char *name); +extern int bcm_iovar_lencheck(const bcm_iovar_t *table, void *arg, int len, bool set); +#if defined(WLTINYDUMP) || defined(WLMSG_INFORM) || defined(WLMSG_ASSOC) || \ + defined(WLMSG_PRPKT) || defined(WLMSG_WSEC) +extern int bcm_format_ssid(char* buf, const uchar ssid[], uint ssid_len); +#endif +#endif + + +#define IOVT_VOID 0 +#define IOVT_BOOL 1 +#define IOVT_INT8 2 +#define IOVT_UINT8 3 +#define IOVT_INT16 4 +#define IOVT_UINT16 5 +#define IOVT_INT32 6 +#define IOVT_UINT32 7 +#define IOVT_BUFFER 8 +#define BCM_IOVT_VALID(type) (((unsigned int)(type)) <= IOVT_BUFFER) + + +#define BCM_IOV_TYPE_INIT { \ + "void", \ + "bool", \ + "int8", \ + "uint8", \ + "int16", \ + "uint16", \ + "int32", \ + "uint32", \ + "buffer", \ + "" } + +#define BCM_IOVT_IS_INT(type) (\ + (type == IOVT_BOOL) || \ + (type == IOVT_INT8) || \ + (type == IOVT_UINT8) || \ + (type == IOVT_INT16) || \ + (type == IOVT_UINT16) || \ + (type == IOVT_INT32) || \ + (type == IOVT_UINT32)) + + + +#define BCME_STRLEN 64 +#define VALID_BCMERROR(e) ((e <= 0) && (e >= BCME_LAST)) + + + + +#define BCME_OK 0 +#define BCME_ERROR -1 +#define BCME_BADARG -2 +#define BCME_BADOPTION -3 +#define BCME_NOTUP -4 +#define BCME_NOTDOWN -5 +#define BCME_NOTAP -6 +#define BCME_NOTSTA -7 +#define BCME_BADKEYIDX -8 +#define BCME_RADIOOFF -9 +#define BCME_NOTBANDLOCKED -10 +#define BCME_NOCLK -11 +#define BCME_BADRATESET -12 +#define BCME_BADBAND -13 +#define BCME_BUFTOOSHORT -14 +#define BCME_BUFTOOLONG -15 +#define BCME_BUSY -16 +#define BCME_NOTASSOCIATED -17 +#define BCME_BADSSIDLEN -18 +#define BCME_OUTOFRANGECHAN -19 +#define BCME_BADCHAN -20 +#define BCME_BADADDR -21 +#define BCME_NORESOURCE -22 +#define BCME_UNSUPPORTED -23 +#define BCME_BADLEN -24 +#define BCME_NOTREADY -25 +#define BCME_EPERM -26 +#define BCME_NOMEM -27 +#define BCME_ASSOCIATED -28 +#define BCME_RANGE -29 +#define BCME_NOTFOUND -30 +#define BCME_WME_NOT_ENABLED -31 +#define BCME_TSPEC_NOTFOUND -32 +#define BCME_ACM_NOTSUPPORTED -33 +#define BCME_NOT_WME_ASSOCIATION -34 +#define BCME_SDIO_ERROR -35 +#define BCME_DONGLE_DOWN -36 +#define BCME_VERSION -37 +#define BCME_TXFAIL -38 +#define BCME_RXFAIL -39 +#define BCME_NODEVICE -40 +#define BCME_NMODE_DISABLED -41 +#define BCME_NONRESIDENT -42 +#define BCME_LAST BCME_NONRESIDENT + + +#define BCMERRSTRINGTABLE { \ + "OK", \ + "Undefined error", \ + "Bad Argument", \ + "Bad Option", \ + "Not up", \ + "Not down", \ + "Not AP", \ + "Not STA", \ + "Bad Key Index", \ + "Radio Off", \ + "Not band locked", \ + "No clock", \ + "Bad Rate valueset", \ + "Bad Band", \ + "Buffer too short", \ + "Buffer too long", \ + "Busy", \ + "Not Associated", \ + "Bad SSID len", \ + "Out of Range Channel", \ + "Bad Channel", \ + "Bad Address", \ + "Not Enough Resources", \ + "Unsupported", \ + "Bad length", \ + "Not Ready", \ + "Not Permitted", \ + "No Memory", \ + "Associated", \ + "Not In Range", \ + "Not Found", \ + "WME Not Enabled", \ + "TSPEC Not Found", \ + "ACM Not Supported", \ + "Not WME Association", \ + "SDIO Bus Error", \ + "Dongle Not Accessible", \ + "Incorrect version", \ + "TX Failure", \ + "RX Failure", \ + "Device Not Present", \ + "NMODE Disabled", \ + "Nonresident overlay access", \ +} + +#ifndef ABS +#define ABS(a) (((a) < 0)?-(a):(a)) +#endif + +#ifndef MIN +#define MIN(a, b) (((a) < (b))?(a):(b)) +#endif + +#ifndef MAX +#define MAX(a, b) (((a) > (b))?(a):(b)) +#endif + +#define CEIL(x, y) (((x) + ((y)-1)) / (y)) +#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) +#define ISALIGNED(a, x) (((uintptr)(a) & ((x)-1)) == 0) +#define ALIGN_ADDR(addr, boundary) (void *)(((uintptr)(addr) + (boundary) - 1) \ + & ~((boundary) - 1)) +#define ISPOWEROF2(x) ((((x)-1)&(x)) == 0) +#define VALID_MASK(mask) !((mask) & ((mask) + 1)) + +#ifndef OFFSETOF +#ifdef __ARMCC_VERSION + +#include +#define OFFSETOF(type, member) offsetof(type, member) +#else +#define OFFSETOF(type, member) ((uint)(uintptr)&((type *)0)->member) +#endif +#endif + +#ifndef ARRAYSIZE +#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) +#endif + + +extern void *_bcmutils_dummy_fn; +#define REFERENCE_FUNCTION(f) (_bcmutils_dummy_fn = (void *)(f)) + + +#ifndef setbit +#ifndef NBBY +#define NBBY 8 +#endif +#define setbit(a, i) (((uint8 *)a)[(i)/NBBY] |= 1<<((i)%NBBY)) +#define clrbit(a, i) (((uint8 *)a)[(i)/NBBY] &= ~(1<<((i)%NBBY))) +#define isset(a, i) (((const uint8 *)a)[(i)/NBBY] & (1<<((i)%NBBY))) +#define isclr(a, i) ((((const uint8 *)a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0) +#endif + +#define NBITS(type) (sizeof(type) * 8) +#define NBITVAL(nbits) (1 << (nbits)) +#define MAXBITVAL(nbits) ((1 << (nbits)) - 1) +#define NBITMASK(nbits) MAXBITVAL(nbits) +#define MAXNBVAL(nbyte) MAXBITVAL((nbyte) * 8) + + +#define MUX(pred, true, false) ((pred) ? (true) : (false)) + + +#define MODDEC(x, bound) MUX((x) == 0, (bound) - 1, (x) - 1) +#define MODINC(x, bound) MUX((x) == (bound) - 1, 0, (x) + 1) + + +#define MODDEC_POW2(x, bound) (((x) - 1) & ((bound) - 1)) +#define MODINC_POW2(x, bound) (((x) + 1) & ((bound) - 1)) + + +#define MODADD(x, y, bound) \ + MUX((x) + (y) >= (bound), (x) + (y) - (bound), (x) + (y)) +#define MODSUB(x, y, bound) \ + MUX(((int)(x)) - ((int)(y)) < 0, (x) - (y) + (bound), (x) - (y)) + + +#define MODADD_POW2(x, y, bound) (((x) + (y)) & ((bound) - 1)) +#define MODSUB_POW2(x, y, bound) (((x) - (y)) & ((bound) - 1)) + + +#define CRC8_INIT_VALUE 0xff +#define CRC8_GOOD_VALUE 0x9f +#define CRC16_INIT_VALUE 0xffff +#define CRC16_GOOD_VALUE 0xf0b8 +#define CRC32_INIT_VALUE 0xffffffff +#define CRC32_GOOD_VALUE 0xdebb20e3 + + +typedef struct bcm_bit_desc { + uint32 bit; + const char* name; +} bcm_bit_desc_t; + + +typedef struct bcm_tlv { + uint8 id; + uint8 len; + uint8 data[1]; +} bcm_tlv_t; + + +#define bcm_valid_tlv(elt, buflen) ((buflen) >= 2 && (int)(buflen) >= (int)(2 + (elt)->len)) + + +#define ETHER_ADDR_STR_LEN 18 + + + +static INLINE void +xor_128bit_block(const uint8 *src1, const uint8 *src2, uint8 *dst) +{ + if ( +#ifdef __i386__ + 1 || +#endif + (((uintptr)src1 | (uintptr)src2 | (uintptr)dst) & 3) == 0) { + + + ((uint32 *)dst)[0] = ((const uint32 *)src1)[0] ^ ((const uint32 *)src2)[0]; + ((uint32 *)dst)[1] = ((const uint32 *)src1)[1] ^ ((const uint32 *)src2)[1]; + ((uint32 *)dst)[2] = ((const uint32 *)src1)[2] ^ ((const uint32 *)src2)[2]; + ((uint32 *)dst)[3] = ((const uint32 *)src1)[3] ^ ((const uint32 *)src2)[3]; + } else { + + int k; + for (k = 0; k < 16; k++) + dst[k] = src1[k] ^ src2[k]; + } +} + + + +extern uint8 hndcrc8(uint8 *p, uint nbytes, uint8 crc); +extern uint16 hndcrc16(uint8 *p, uint nbytes, uint16 crc); +extern uint32 hndcrc32(uint8 *p, uint nbytes, uint32 crc); + +#if defined(DHD_DEBUG) || defined(WLMSG_PRHDRS) || defined(WLMSG_PRPKT) || \ + defined(WLMSG_ASSOC) +extern int bcm_format_flags(const bcm_bit_desc_t *bd, uint32 flags, char* buf, int len); +#endif + +#if defined(DHD_DEBUG) || defined(WLMSG_PRHDRS) || defined(WLMSG_PRPKT) || \ + defined(WLMSG_ASSOC) || defined(WLMEDIA_PEAKRATE) +extern int bcm_format_hex(char *str, const void *bytes, int len); +#endif + +extern const char *bcm_crypto_algo_name(uint algo); +extern char *bcm_chipname(uint chipid, char *buf, uint len); +extern char *bcm_brev_str(uint32 brev, char *buf); +extern void printbig(char *buf); +extern void prhex(const char *msg, uchar *buf, uint len); + + +extern bcm_tlv_t *bcm_next_tlv(bcm_tlv_t *elt, int *buflen); +extern bcm_tlv_t *bcm_parse_tlvs(void *buf, int buflen, uint key); +extern bcm_tlv_t *bcm_parse_ordered_tlvs(void *buf, int buflen, uint key); + + +extern const char *bcmerrorstr(int bcmerror); + + +typedef uint32 mbool; +#define mboolset(mb, bit) ((mb) |= (bit)) +#define mboolclr(mb, bit) ((mb) &= ~(bit)) +#define mboolisset(mb, bit) (((mb) & (bit)) != 0) +#define mboolmaskset(mb, mask, val) ((mb) = (((mb) & ~(mask)) | (val))) + + +extern uint16 bcm_qdbm_to_mw(uint8 qdbm); +extern uint8 bcm_mw_to_qdbm(uint16 mw); + + +struct fielddesc { + const char *nameandfmt; + uint32 offset; + uint32 len; +}; + +extern void bcm_binit(struct bcmstrbuf *b, char *buf, uint size); +extern int bcm_bprintf(struct bcmstrbuf *b, const char *fmt, ...); +extern void bcm_inc_bytes(uchar *num, int num_bytes, uint8 amount); +extern int bcm_cmp_bytes(uchar *arg1, uchar *arg2, uint8 nbytes); +extern void bcm_print_bytes(char *name, const uchar *cdata, int len); + +typedef uint32 (*bcmutl_rdreg_rtn)(void *arg0, uint arg1, uint32 offset); +extern uint bcmdumpfields(bcmutl_rdreg_rtn func_ptr, void *arg0, uint arg1, struct fielddesc *str, + char *buf, uint32 bufsize); + +extern uint bcm_mkiovar(char *name, char *data, uint datalen, char *buf, uint len); +extern uint bcm_bitcount(uint8 *bitmap, uint bytelength); + + + +#define SSID_FMT_BUF_LEN ((4 * DOT11_MAX_SSID_LEN) + 1) + +unsigned int process_nvram_vars(char *varbuf, unsigned int len); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/bcmwifi.h b/drivers/net/wireless/bcmdhd/include/bcmwifi.h new file mode 100644 index 0000000000000..e5207e9c40861 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/bcmwifi.h @@ -0,0 +1,165 @@ +/* + * Misc utility routines for WL and Apps + * This header file housing the define and function prototype use by + * both the wl driver, tools & Apps. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmwifi.h 277737 2011-08-16 17:54:59Z $ + */ + + +#ifndef _bcmwifi_h_ +#define _bcmwifi_h_ + + + +typedef uint16 chanspec_t; + + +#define CH_UPPER_SB 0x01 +#define CH_LOWER_SB 0x02 +#define CH_EWA_VALID 0x04 +#define CH_20MHZ_APART 4 +#define CH_10MHZ_APART 2 +#define CH_5MHZ_APART 1 +#define CH_MAX_2G_CHANNEL 14 +#define WLC_MAX_2G_CHANNEL CH_MAX_2G_CHANNEL +#define MAXCHANNEL 224 + +#define WL_CHANSPEC_CHAN_MASK 0x00ff +#define WL_CHANSPEC_CHAN_SHIFT 0 + +#define WL_CHANSPEC_CTL_SB_MASK 0x0300 +#define WL_CHANSPEC_CTL_SB_SHIFT 8 +#define WL_CHANSPEC_CTL_SB_LOWER 0x0100 +#define WL_CHANSPEC_CTL_SB_UPPER 0x0200 +#define WL_CHANSPEC_CTL_SB_NONE 0x0300 + +#define WL_CHANSPEC_BW_MASK 0x0C00 +#define WL_CHANSPEC_BW_SHIFT 10 +#define WL_CHANSPEC_BW_10 0x0400 +#define WL_CHANSPEC_BW_20 0x0800 +#define WL_CHANSPEC_BW_40 0x0C00 + +#define WL_CHANSPEC_BAND_MASK 0xf000 +#define WL_CHANSPEC_BAND_SHIFT 12 +#define WL_CHANSPEC_BAND_5G 0x1000 +#define WL_CHANSPEC_BAND_2G 0x2000 +#define INVCHANSPEC 255 + + +#define WF_CHAN_FACTOR_2_4_G 4814 +#define WF_CHAN_FACTOR_5_G 10000 +#define WF_CHAN_FACTOR_4_G 8000 + + +#define LOWER_20_SB(channel) (((channel) > CH_10MHZ_APART) ? ((channel) - CH_10MHZ_APART) : 0) +#define UPPER_20_SB(channel) (((channel) < (MAXCHANNEL - CH_10MHZ_APART)) ? \ + ((channel) + CH_10MHZ_APART) : 0) +#define CHSPEC_WLCBANDUNIT(chspec) (CHSPEC_IS5G(chspec) ? BAND_5G_INDEX : BAND_2G_INDEX) +#define CH20MHZ_CHSPEC(channel) (chanspec_t)((chanspec_t)(channel) | WL_CHANSPEC_BW_20 | \ + WL_CHANSPEC_CTL_SB_NONE | (((channel) <= CH_MAX_2G_CHANNEL) ? \ + WL_CHANSPEC_BAND_2G : WL_CHANSPEC_BAND_5G)) +#define NEXT_20MHZ_CHAN(channel) (((channel) < (MAXCHANNEL - CH_20MHZ_APART)) ? \ + ((channel) + CH_20MHZ_APART) : 0) +#define CH40MHZ_CHSPEC(channel, ctlsb) (chanspec_t) \ + ((channel) | (ctlsb) | WL_CHANSPEC_BW_40 | \ + ((channel) <= CH_MAX_2G_CHANNEL ? WL_CHANSPEC_BAND_2G : \ + WL_CHANSPEC_BAND_5G)) +#define CHSPEC_CHANNEL(chspec) ((uint8)((chspec) & WL_CHANSPEC_CHAN_MASK)) +#define CHSPEC_BAND(chspec) ((chspec) & WL_CHANSPEC_BAND_MASK) + + +#define CHSPEC_CTL_SB(chspec) (chspec & WL_CHANSPEC_CTL_SB_MASK) +#define CHSPEC_BW(chspec) (chspec & WL_CHANSPEC_BW_MASK) + +#ifdef WL11N_20MHZONLY + +#define CHSPEC_IS10(chspec) 0 +#define CHSPEC_IS20(chspec) 1 +#ifndef CHSPEC_IS40 +#define CHSPEC_IS40(chspec) 0 +#endif + +#else + +#define CHSPEC_IS10(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_10) +#define CHSPEC_IS20(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20) +#ifndef CHSPEC_IS40 +#define CHSPEC_IS40(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_40) +#endif + +#endif + +#define CHSPEC_IS20_UNCOND(chspec) (((chspec) & WL_CHANSPEC_BW_MASK) == WL_CHANSPEC_BW_20) + +#define CHSPEC_IS5G(chspec) (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_5G) +#define CHSPEC_IS2G(chspec) (((chspec) & WL_CHANSPEC_BAND_MASK) == WL_CHANSPEC_BAND_2G) +#define CHSPEC_SB_NONE(chspec) (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_NONE) +#define CHSPEC_SB_UPPER(chspec) (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_UPPER) +#define CHSPEC_SB_LOWER(chspec) (((chspec) & WL_CHANSPEC_CTL_SB_MASK) == WL_CHANSPEC_CTL_SB_LOWER) +#define CHSPEC_CTL_CHAN(chspec) ((CHSPEC_SB_LOWER(chspec)) ? \ + (LOWER_20_SB(((chspec) & WL_CHANSPEC_CHAN_MASK))) : \ + (UPPER_20_SB(((chspec) & WL_CHANSPEC_CHAN_MASK)))) +#define CHSPEC2WLC_BAND(chspec) (CHSPEC_IS5G(chspec) ? WLC_BAND_5G : WLC_BAND_2G) + +#define CHANSPEC_STR_LEN 8 + + +#define WLC_MAXRATE 108 +#define WLC_RATE_1M 2 +#define WLC_RATE_2M 4 +#define WLC_RATE_5M5 11 +#define WLC_RATE_11M 22 +#define WLC_RATE_6M 12 +#define WLC_RATE_9M 18 +#define WLC_RATE_12M 24 +#define WLC_RATE_18M 36 +#define WLC_RATE_24M 48 +#define WLC_RATE_36M 72 +#define WLC_RATE_48M 96 +#define WLC_RATE_54M 108 + +#define WLC_2G_25MHZ_OFFSET 5 + + +extern char * wf_chspec_ntoa(chanspec_t chspec, char *buf); + + +extern chanspec_t wf_chspec_aton(char *a); + + +extern bool wf_chspec_malformed(chanspec_t chanspec); + + +extern uint8 wf_chspec_ctlchan(chanspec_t chspec); + + +extern chanspec_t wf_chspec_ctlchspec(chanspec_t chspec); + + +extern int wf_mhz2channel(uint freq, uint start_factor); + + +extern int wf_channel2mhz(uint channel, uint start_factor); + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/dhdioctl.h b/drivers/net/wireless/bcmdhd/include/dhdioctl.h new file mode 100644 index 0000000000000..175ff8545a0ce --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/dhdioctl.h @@ -0,0 +1,131 @@ +/* + * Definitions for ioctls to access DHD iovars. + * Based on wlioctl.h (for Broadcom 802.11abg driver). + * (Moves towards generic ioctls for BCM drivers/iovars.) + * + * Definitions subject to change without notice. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: dhdioctl.h 323572 2012-03-26 06:28:14Z $ + */ + +#ifndef _dhdioctl_h_ +#define _dhdioctl_h_ + +#include + + +/* require default structure packing */ +#define BWL_DEFAULT_PACKING +#include + + +/* Linux network driver ioctl encoding */ +typedef struct dhd_ioctl { + uint cmd; /* common ioctl definition */ + void *buf; /* pointer to user buffer */ + uint len; /* length of user buffer */ + bool set; /* get or set request (optional) */ + uint used; /* bytes read or written (optional) */ + uint needed; /* bytes needed (optional) */ + uint driver; /* to identify target driver */ +} dhd_ioctl_t; + +/* Underlying BUS definition */ +enum { + BUS_TYPE_USB = 0, /* for USB dongles */ + BUS_TYPE_SDIO /* for SDIO dongles */ +}; + +/* per-driver magic numbers */ +#define DHD_IOCTL_MAGIC 0x00444944 + +/* bump this number if you change the ioctl interface */ +#define DHD_IOCTL_VERSION 1 + +#define DHD_IOCTL_MAXLEN 8192 /* max length ioctl buffer required */ +#define DHD_IOCTL_SMLEN 256 /* "small" length ioctl buffer required */ + +/* common ioctl definitions */ +#define DHD_GET_MAGIC 0 +#define DHD_GET_VERSION 1 +#define DHD_GET_VAR 2 +#define DHD_SET_VAR 3 + +/* message levels */ +#define DHD_ERROR_VAL 0x0001 +#define DHD_TRACE_VAL 0x0002 +#define DHD_INFO_VAL 0x0004 +#define DHD_DATA_VAL 0x0008 +#define DHD_CTL_VAL 0x0010 +#define DHD_TIMER_VAL 0x0020 +#define DHD_HDRS_VAL 0x0040 +#define DHD_BYTES_VAL 0x0080 +#define DHD_INTR_VAL 0x0100 +#define DHD_LOG_VAL 0x0200 +#define DHD_GLOM_VAL 0x0400 +#define DHD_EVENT_VAL 0x0800 +#define DHD_BTA_VAL 0x1000 +#define DHD_ISCAN_VAL 0x2000 +#define DHD_ARPOE_VAL 0x4000 +#define DHD_REORDER_VAL 0x8000 +#define DHD_WL_VAL 0x10000 + +#ifdef SDTEST +/* For pktgen iovar */ +typedef struct dhd_pktgen { + uint version; /* To allow structure change tracking */ + uint freq; /* Max ticks between tx/rx attempts */ + uint count; /* Test packets to send/rcv each attempt */ + uint print; /* Print counts every attempts */ + uint total; /* Total packets (or bursts) */ + uint minlen; /* Minimum length of packets to send */ + uint maxlen; /* Maximum length of packets to send */ + uint numsent; /* Count of test packets sent */ + uint numrcvd; /* Count of test packets received */ + uint numfail; /* Count of test send failures */ + uint mode; /* Test mode (type of test packets) */ + uint stop; /* Stop after this many tx failures */ +} dhd_pktgen_t; + +/* Version in case structure changes */ +#define DHD_PKTGEN_VERSION 2 + +/* Type of test packets to use */ +#define DHD_PKTGEN_ECHO 1 /* Send echo requests */ +#define DHD_PKTGEN_SEND 2 /* Send discard packets */ +#define DHD_PKTGEN_RXBURST 3 /* Request dongle send N packets */ +#define DHD_PKTGEN_RECV 4 /* Continuous rx from continuous tx dongle */ +#endif /* SDTEST */ + +/* Enter idle immediately (no timeout) */ +#define DHD_IDLE_IMMEDIATE (-1) + +/* Values for idleclock iovar: other values are the sd_divisor to use when idle */ +#define DHD_IDLE_ACTIVE 0 /* Do not request any SD clock change when idle */ +#define DHD_IDLE_STOP (-1) /* Request SD clock be stopped (and use SD1 mode) */ + + +/* require default structure packing */ +#include + +#endif /* _dhdioctl_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/epivers.h b/drivers/net/wireless/bcmdhd/include/epivers.h new file mode 100644 index 0000000000000..5df25c16b7d9b --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/epivers.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: epivers.h.in 277737 2011-08-16 17:54:59Z $ + * +*/ + + +#ifndef _epivers_h_ +#define _epivers_h_ + +#define EPI_MAJOR_VERSION 5 + +#define EPI_MINOR_VERSION 90 + +#define EPI_RC_NUMBER 195 + +#define EPI_INCREMENTAL_NUMBER 75 + +#define EPI_BUILD_NUMBER 0 + +#define EPI_VERSION 5, 90, 195, 75 + +#define EPI_VERSION_NUM 0x055ac34b + +#define EPI_VERSION_DEV 5.90.195 + + +#define EPI_VERSION_STR "5.90.195.75" + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/hndpmu.h b/drivers/net/wireless/bcmdhd/include/hndpmu.h new file mode 100644 index 0000000000000..9bfc8c9275a9b --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/hndpmu.h @@ -0,0 +1,37 @@ +/* + * HND SiliconBackplane PMU support. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: hndpmu.h 335486 2012-05-28 09:47:55Z $ + */ + +#ifndef _hndpmu_h_ +#define _hndpmu_h_ + + +extern void si_pmu_otp_power(si_t *sih, osl_t *osh, bool on); +extern void si_sdiod_drive_strength_init(si_t *sih, osl_t *osh, uint32 drivestrength); + +extern void si_pmu_set_otp_wr_volts(si_t *sih); +extern void si_pmu_set_otp_rd_volts(si_t *sih); + +#endif /* _hndpmu_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h b/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h new file mode 100644 index 0000000000000..7d862c4deb21f --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/hndrte_armtrap.h @@ -0,0 +1,88 @@ +/* + * HNDRTE arm trap handling. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: hndrte_armtrap.h 277737 2011-08-16 17:54:59Z $ + */ + +#ifndef _hndrte_armtrap_h +#define _hndrte_armtrap_h + + +/* ARM trap handling */ + +/* Trap types defined by ARM (see arminc.h) */ + +/* Trap locations in lo memory */ +#define TRAP_STRIDE 4 +#define FIRST_TRAP TR_RST +#define LAST_TRAP (TR_FIQ * TRAP_STRIDE) + +#if defined(__ARM_ARCH_4T__) +#define MAX_TRAP_TYPE (TR_FIQ + 1) +#elif defined(__ARM_ARCH_7M__) +#define MAX_TRAP_TYPE (TR_ISR + ARMCM3_NUMINTS) +#endif /* __ARM_ARCH_7M__ */ + +/* The trap structure is defined here as offsets for assembly */ +#define TR_TYPE 0x00 +#define TR_EPC 0x04 +#define TR_CPSR 0x08 +#define TR_SPSR 0x0c +#define TR_REGS 0x10 +#define TR_REG(n) (TR_REGS + (n) * 4) +#define TR_SP TR_REG(13) +#define TR_LR TR_REG(14) +#define TR_PC TR_REG(15) + +#define TRAP_T_SIZE 80 + +#ifndef _LANGUAGE_ASSEMBLY + +#include + +typedef struct _trap_struct { + uint32 type; + uint32 epc; + uint32 cpsr; + uint32 spsr; + uint32 r0; + uint32 r1; + uint32 r2; + uint32 r3; + uint32 r4; + uint32 r5; + uint32 r6; + uint32 r7; + uint32 r8; + uint32 r9; + uint32 r10; + uint32 r11; + uint32 r12; + uint32 r13; + uint32 r14; + uint32 pc; +} trap_t; + +#endif /* !_LANGUAGE_ASSEMBLY */ + +#endif /* _hndrte_armtrap_h */ diff --git a/drivers/net/wireless/bcmdhd/include/hndrte_cons.h b/drivers/net/wireless/bcmdhd/include/hndrte_cons.h new file mode 100644 index 0000000000000..859ddc8953a87 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/hndrte_cons.h @@ -0,0 +1,68 @@ +/* + * Console support for hndrte. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: hndrte_cons.h 277737 2011-08-16 17:54:59Z $ + */ + +#ifndef _HNDRTE_CONS_H +#define _HNDRTE_CONS_H + +#include + +#define CBUF_LEN (128) + +#define LOG_BUF_LEN 1024 + +typedef struct { + uint32 buf; /* Can't be pointer on (64-bit) hosts */ + uint buf_size; + uint idx; + char *_buf_compat; /* redundant pointer for backward compat. */ +} hndrte_log_t; + +typedef struct { + /* Virtual UART + * When there is no UART (e.g. Quickturn), the host should write a complete + * input line directly into cbuf and then write the length into vcons_in. + * This may also be used when there is a real UART (at risk of conflicting with + * the real UART). vcons_out is currently unused. + */ + volatile uint vcons_in; + volatile uint vcons_out; + + /* Output (logging) buffer + * Console output is written to a ring buffer log_buf at index log_idx. + * The host may read the output when it sees log_idx advance. + * Output will be lost if the output wraps around faster than the host polls. + */ + hndrte_log_t log; + + /* Console input line buffer + * Characters are read one at a time into cbuf until is received, then + * the buffer is processed as a command line. Also used for virtual UART. + */ + uint cbuf_idx; + char cbuf[CBUF_LEN]; +} hndrte_cons_t; + +#endif /* _HNDRTE_CONS_H */ diff --git a/drivers/net/wireless/bcmdhd/include/hndsoc.h b/drivers/net/wireless/bcmdhd/include/hndsoc.h new file mode 100644 index 0000000000000..34f927c6af806 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/hndsoc.h @@ -0,0 +1,207 @@ +/* + * Broadcom HND chip & on-chip-interconnect-related definitions. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: hndsoc.h 277737 2011-08-16 17:54:59Z $ + */ + +#ifndef _HNDSOC_H +#define _HNDSOC_H + +/* Include the soci specific files */ +#include +#include + +/* + * SOC Interconnect Address Map. + * All regions may not exist on all chips. + */ +#define SI_SDRAM_BASE 0x00000000 /* Physical SDRAM */ +#define SI_PCI_MEM 0x08000000 /* Host Mode sb2pcitranslation0 (64 MB) */ +#define SI_PCI_MEM_SZ (64 * 1024 * 1024) +#define SI_PCI_CFG 0x0c000000 /* Host Mode sb2pcitranslation1 (64 MB) */ +#define SI_SDRAM_SWAPPED 0x10000000 /* Byteswapped Physical SDRAM */ +#define SI_SDRAM_R2 0x80000000 /* Region 2 for sdram (512 MB) */ + +#define SI_ENUM_BASE 0x18000000 /* Enumeration space base */ + +#define SI_WRAP_BASE 0x18100000 /* Wrapper space base */ +#define SI_CORE_SIZE 0x1000 /* each core gets 4Kbytes for registers */ +#define SI_MAXCORES 16 /* Max cores (this is arbitrary, for software + * convenience and could be changed if we + * make any larger chips + */ + +#define SI_FASTRAM 0x19000000 /* On-chip RAM on chips that also have DDR */ +#define SI_FASTRAM_SWAPPED 0x19800000 + +#define SI_FLASH2 0x1c000000 /* Flash Region 2 (region 1 shadowed here) */ +#define SI_FLASH2_SZ 0x02000000 /* Size of Flash Region 2 */ +#define SI_ARMCM3_ROM 0x1e000000 /* ARM Cortex-M3 ROM */ +#define SI_FLASH1 0x1fc00000 /* MIPS Flash Region 1 */ +#define SI_FLASH1_SZ 0x00400000 /* MIPS Size of Flash Region 1 */ +#define SI_ARM7S_ROM 0x20000000 /* ARM7TDMI-S ROM */ +#define SI_ARMCM3_SRAM2 0x60000000 /* ARM Cortex-M3 SRAM Region 2 */ +#define SI_ARM7S_SRAM2 0x80000000 /* ARM7TDMI-S SRAM Region 2 */ +#define SI_ARM_FLASH1 0xffff0000 /* ARM Flash Region 1 */ +#define SI_ARM_FLASH1_SZ 0x00010000 /* ARM Size of Flash Region 1 */ + +#define SI_PCI_DMA 0x40000000 /* Client Mode sb2pcitranslation2 (1 GB) */ +#define SI_PCI_DMA2 0x80000000 /* Client Mode sb2pcitranslation2 (1 GB) */ +#define SI_PCI_DMA_SZ 0x40000000 /* Client Mode sb2pcitranslation2 size in bytes */ +#define SI_PCIE_DMA_L32 0x00000000 /* PCIE Client Mode sb2pcitranslation2 + * (2 ZettaBytes), low 32 bits + */ +#define SI_PCIE_DMA_H32 0x80000000 /* PCIE Client Mode sb2pcitranslation2 + * (2 ZettaBytes), high 32 bits + */ + +/* core codes */ +#define NODEV_CORE_ID 0x700 /* Invalid coreid */ +#define CC_CORE_ID 0x800 /* chipcommon core */ +#define ILINE20_CORE_ID 0x801 /* iline20 core */ +#define SRAM_CORE_ID 0x802 /* sram core */ +#define SDRAM_CORE_ID 0x803 /* sdram core */ +#define PCI_CORE_ID 0x804 /* pci core */ +#define MIPS_CORE_ID 0x805 /* mips core */ +#define ENET_CORE_ID 0x806 /* enet mac core */ +#define CODEC_CORE_ID 0x807 /* v90 codec core */ +#define USB_CORE_ID 0x808 /* usb 1.1 host/device core */ +#define ADSL_CORE_ID 0x809 /* ADSL core */ +#define ILINE100_CORE_ID 0x80a /* iline100 core */ +#define IPSEC_CORE_ID 0x80b /* ipsec core */ +#define UTOPIA_CORE_ID 0x80c /* utopia core */ +#define PCMCIA_CORE_ID 0x80d /* pcmcia core */ +#define SOCRAM_CORE_ID 0x80e /* internal memory core */ +#define MEMC_CORE_ID 0x80f /* memc sdram core */ +#define OFDM_CORE_ID 0x810 /* OFDM phy core */ +#define EXTIF_CORE_ID 0x811 /* external interface core */ +#define D11_CORE_ID 0x812 /* 802.11 MAC core */ +#define APHY_CORE_ID 0x813 /* 802.11a phy core */ +#define BPHY_CORE_ID 0x814 /* 802.11b phy core */ +#define GPHY_CORE_ID 0x815 /* 802.11g phy core */ +#define MIPS33_CORE_ID 0x816 /* mips3302 core */ +#define USB11H_CORE_ID 0x817 /* usb 1.1 host core */ +#define USB11D_CORE_ID 0x818 /* usb 1.1 device core */ +#define USB20H_CORE_ID 0x819 /* usb 2.0 host core */ +#define USB20D_CORE_ID 0x81a /* usb 2.0 device core */ +#define SDIOH_CORE_ID 0x81b /* sdio host core */ +#define ROBO_CORE_ID 0x81c /* roboswitch core */ +#define ATA100_CORE_ID 0x81d /* parallel ATA core */ +#define SATAXOR_CORE_ID 0x81e /* serial ATA & XOR DMA core */ +#define GIGETH_CORE_ID 0x81f /* gigabit ethernet core */ +#define PCIE_CORE_ID 0x820 /* pci express core */ +#define NPHY_CORE_ID 0x821 /* 802.11n 2x2 phy core */ +#define SRAMC_CORE_ID 0x822 /* SRAM controller core */ +#define MINIMAC_CORE_ID 0x823 /* MINI MAC/phy core */ +#define ARM11_CORE_ID 0x824 /* ARM 1176 core */ +#define ARM7S_CORE_ID 0x825 /* ARM7tdmi-s core */ +#define LPPHY_CORE_ID 0x826 /* 802.11a/b/g phy core */ +#define PMU_CORE_ID 0x827 /* PMU core */ +#define SSNPHY_CORE_ID 0x828 /* 802.11n single-stream phy core */ +#define SDIOD_CORE_ID 0x829 /* SDIO device core */ +#define ARMCM3_CORE_ID 0x82a /* ARM Cortex M3 core */ +#define HTPHY_CORE_ID 0x82b /* 802.11n 4x4 phy core */ +#define MIPS74K_CORE_ID 0x82c /* mips 74k core */ +#define GMAC_CORE_ID 0x82d /* Gigabit MAC core */ +#define DMEMC_CORE_ID 0x82e /* DDR1/2 memory controller core */ +#define PCIERC_CORE_ID 0x82f /* PCIE Root Complex core */ +#define OCP_CORE_ID 0x830 /* OCP2OCP bridge core */ +#define SC_CORE_ID 0x831 /* shared common core */ +#define AHB_CORE_ID 0x832 /* OCP2AHB bridge core */ +#define SPIH_CORE_ID 0x833 /* SPI host core */ +#define I2S_CORE_ID 0x834 /* I2S core */ +#define DMEMS_CORE_ID 0x835 /* SDR/DDR1 memory controller core */ +#define DEF_SHIM_COMP 0x837 /* SHIM component in ubus/6362 */ +#define OOB_ROUTER_CORE_ID 0x367 /* OOB router core ID */ +#define DEF_AI_COMP 0xfff /* Default component, in ai chips it maps all + * unused address ranges + */ + +/* There are TWO constants on all HND chips: SI_ENUM_BASE above, + * and chipcommon being the first core: + */ +#define SI_CC_IDX 0 + +/* SOC Interconnect types (aka chip types) */ +#define SOCI_SB 0 +#define SOCI_AI 1 +#define SOCI_UBUS 2 + +/* Common core control flags */ +#define SICF_BIST_EN 0x8000 +#define SICF_PME_EN 0x4000 +#define SICF_CORE_BITS 0x3ffc +#define SICF_FGC 0x0002 +#define SICF_CLOCK_EN 0x0001 + +/* Common core status flags */ +#define SISF_BIST_DONE 0x8000 +#define SISF_BIST_ERROR 0x4000 +#define SISF_GATED_CLK 0x2000 +#define SISF_DMA64 0x1000 +#define SISF_CORE_BITS 0x0fff + +/* A register that is common to all cores to + * communicate w/PMU regarding clock control. + */ +#define SI_CLK_CTL_ST 0x1e0 /* clock control and status */ + +/* clk_ctl_st register */ +#define CCS_FORCEALP 0x00000001 /* force ALP request */ +#define CCS_FORCEHT 0x00000002 /* force HT request */ +#define CCS_FORCEILP 0x00000004 /* force ILP request */ +#define CCS_ALPAREQ 0x00000008 /* ALP Avail Request */ +#define CCS_HTAREQ 0x00000010 /* HT Avail Request */ +#define CCS_FORCEHWREQOFF 0x00000020 /* Force HW Clock Request Off */ +#define CCS_ERSRC_REQ_MASK 0x00000700 /* external resource requests */ +#define CCS_ERSRC_REQ_SHIFT 8 +#define CCS_ALPAVAIL 0x00010000 /* ALP is available */ +#define CCS_HTAVAIL 0x00020000 /* HT is available */ +#define CCS_BP_ON_APL 0x00040000 /* RO: Backplane is running on ALP clock */ +#define CCS_BP_ON_HT 0x00080000 /* RO: Backplane is running on HT clock */ +#define CCS_ERSRC_STS_MASK 0x07000000 /* external resource status */ +#define CCS_ERSRC_STS_SHIFT 24 + +#define CCS0_HTAVAIL 0x00010000 /* HT avail in chipc and pcmcia on 4328a0 */ +#define CCS0_ALPAVAIL 0x00020000 /* ALP avail in chipc and pcmcia on 4328a0 */ + +/* Not really related to SOC Interconnect, but a couple of software + * conventions for the use the flash space: + */ + +/* Minumum amount of flash we support */ +#define FLASH_MIN 0x00020000 /* Minimum flash size */ + +/* A boot/binary may have an embedded block that describes its size */ +#define BISZ_OFFSET 0x3e0 /* At this offset into the binary */ +#define BISZ_MAGIC 0x4249535a /* Marked with this value: 'BISZ' */ +#define BISZ_MAGIC_IDX 0 /* Word 0: magic */ +#define BISZ_TXTST_IDX 1 /* 1: text start */ +#define BISZ_TXTEND_IDX 2 /* 2: text end */ +#define BISZ_DATAST_IDX 3 /* 3: data start */ +#define BISZ_DATAEND_IDX 4 /* 4: data end */ +#define BISZ_BSSST_IDX 5 /* 5: bss start */ +#define BISZ_BSSEND_IDX 6 /* 6: bss end */ +#define BISZ_SIZE 7 /* descriptor size in 32-bit integers */ + +#endif /* _HNDSOC_H */ diff --git a/drivers/net/wireless/bcmdhd/include/htsf.h b/drivers/net/wireless/bcmdhd/include/htsf.h new file mode 100644 index 0000000000000..d875edb816c9c --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/htsf.h @@ -0,0 +1,74 @@ +/* + * Time stamps for latency measurements + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: htsf.h 277737 2011-08-16 17:54:59Z $ + */ +#ifndef _HTSF_H_ +#define _HTSF_H_ + +#define HTSFMAGIC 0xCDCDABAB /* in network order for tcpdump */ +#define HTSFENDMAGIC 0xEFEFABAB /* to distinguish from RT2 magic */ +#define HTSF_HOSTOFFSET 102 +#define HTSF_DNGLOFFSET HTSF_HOSTOFFSET - 4 +#define HTSF_DNGLOFFSET2 HTSF_HOSTOFFSET + 106 +#define HTSF_MIN_PKTLEN 200 +#define ETHER_TYPE_BRCM_PKTDLYSTATS 0x886d + +typedef enum htsfts_type { + T10, + T20, + T30, + T40, + T50, + T60, + T70, + T80, + T90, + TA0, + TE0 +} htsf_timestamp_t; + +typedef struct { + uint32 magic; + uint32 prio; + uint32 seqnum; + uint32 misc; + uint32 c10; + uint32 t10; + uint32 c20; + uint32 t20; + uint32 t30; + uint32 t40; + uint32 t50; + uint32 t60; + uint32 t70; + uint32 t80; + uint32 t90; + uint32 cA0; + uint32 tA0; + uint32 cE0; + uint32 tE0; + uint32 endmagic; +} htsfts_t; + +#endif /* _HTSF_H_ */ diff --git a/drivers/net/wireless/bcmdhd/include/linux_osl.h b/drivers/net/wireless/bcmdhd/include/linux_osl.h new file mode 100644 index 0000000000000..830d351d882b4 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/linux_osl.h @@ -0,0 +1,431 @@ +/* + * Linux OS Independent Layer + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: linux_osl.h 301794 2011-12-08 20:41:35Z $ + */ + + +#ifndef _linux_osl_h_ +#define _linux_osl_h_ + +#include + + +extern void * osl_os_open_image(char * filename); +extern int osl_os_get_image_block(char * buf, int len, void * image); +extern void osl_os_close_image(void * image); + + +#ifdef BCMDRIVER + + +extern osl_t *osl_attach(void *pdev, uint bustype, bool pkttag); +extern void osl_detach(osl_t *osh); + + +extern uint32 g_assert_type; + + +#if defined(BCMASSERT_LOG) + #define ASSERT(exp) \ + do { if (!(exp)) osl_assert(#exp, __FILE__, __LINE__); } while (0) +extern void osl_assert(char *exp, char *file, int line); +#else + #ifdef __GNUC__ + #define GCC_VERSION \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) + #if GCC_VERSION > 30100 + #define ASSERT(exp) do {} while (0) + #else + + #define ASSERT(exp) + #endif + #endif +#endif + + +#define OSL_DELAY(usec) osl_delay(usec) +extern void osl_delay(uint usec); + +#define OSL_PCMCIA_READ_ATTR(osh, offset, buf, size) \ + osl_pcmcia_read_attr((osh), (offset), (buf), (size)) +#define OSL_PCMCIA_WRITE_ATTR(osh, offset, buf, size) \ + osl_pcmcia_write_attr((osh), (offset), (buf), (size)) +extern void osl_pcmcia_read_attr(osl_t *osh, uint offset, void *buf, int size); +extern void osl_pcmcia_write_attr(osl_t *osh, uint offset, void *buf, int size); + + +#define OSL_PCI_READ_CONFIG(osh, offset, size) \ + osl_pci_read_config((osh), (offset), (size)) +#define OSL_PCI_WRITE_CONFIG(osh, offset, size, val) \ + osl_pci_write_config((osh), (offset), (size), (val)) +extern uint32 osl_pci_read_config(osl_t *osh, uint offset, uint size); +extern void osl_pci_write_config(osl_t *osh, uint offset, uint size, uint val); + + +#define OSL_PCI_BUS(osh) osl_pci_bus(osh) +#define OSL_PCI_SLOT(osh) osl_pci_slot(osh) +extern uint osl_pci_bus(osl_t *osh); +extern uint osl_pci_slot(osl_t *osh); + + +typedef struct { + bool pkttag; + uint pktalloced; + bool mmbus; + pktfree_cb_fn_t tx_fn; + void *tx_ctx; +} osl_pubinfo_t; + +#define PKTFREESETCB(osh, _tx_fn, _tx_ctx) \ + do { \ + ((osl_pubinfo_t*)osh)->tx_fn = _tx_fn; \ + ((osl_pubinfo_t*)osh)->tx_ctx = _tx_ctx; \ + } while (0) + + + +#define BUS_SWAP32(v) (v) + + #define MALLOC(osh, size) osl_malloc((osh), (size)) + #define MFREE(osh, addr, size) osl_mfree((osh), (addr), (size)) + #define MALLOCED(osh) osl_malloced((osh)) + extern void *osl_malloc(osl_t *osh, uint size); + extern void osl_mfree(osl_t *osh, void *addr, uint size); + extern uint osl_malloced(osl_t *osh); + +#define NATIVE_MALLOC(osh, size) kmalloc(size, GFP_ATOMIC) +#define NATIVE_MFREE(osh, addr, size) kfree(addr) + +#define MALLOC_FAILED(osh) osl_malloc_failed((osh)) +extern uint osl_malloc_failed(osl_t *osh); + + +#define DMA_CONSISTENT_ALIGN osl_dma_consistent_align() +#define DMA_ALLOC_CONSISTENT(osh, size, align, tot, pap, dmah) \ + osl_dma_alloc_consistent((osh), (size), (align), (tot), (pap)) +#define DMA_FREE_CONSISTENT(osh, va, size, pa, dmah) \ + osl_dma_free_consistent((osh), (void*)(va), (size), (pa)) +extern uint osl_dma_consistent_align(void); +extern void *osl_dma_alloc_consistent(osl_t *osh, uint size, uint16 align, uint *tot, ulong *pap); +extern void osl_dma_free_consistent(osl_t *osh, void *va, uint size, ulong pa); + + +#define DMA_TX 1 +#define DMA_RX 2 + + +#define DMA_MAP(osh, va, size, direction, p, dmah) \ + osl_dma_map((osh), (va), (size), (direction)) +#define DMA_UNMAP(osh, pa, size, direction, p, dmah) \ + osl_dma_unmap((osh), (pa), (size), (direction)) +extern uint osl_dma_map(osl_t *osh, void *va, uint size, int direction); +extern void osl_dma_unmap(osl_t *osh, uint pa, uint size, int direction); + + +#define OSL_DMADDRWIDTH(osh, addrwidth) do {} while (0) + + + #include + #define OSL_WRITE_REG(osh, r, v) (bcmsdh_reg_write(NULL, (uintptr)(r), sizeof(*(r)), (v))) + #define OSL_READ_REG(osh, r) (bcmsdh_reg_read(NULL, (uintptr)(r), sizeof(*(r)))) + + #define SELECT_BUS_WRITE(osh, mmap_op, bus_op) if (((osl_pubinfo_t*)(osh))->mmbus) \ + mmap_op else bus_op + #define SELECT_BUS_READ(osh, mmap_op, bus_op) (((osl_pubinfo_t*)(osh))->mmbus) ? \ + mmap_op : bus_op + +#define OSL_ERROR(bcmerror) osl_error(bcmerror) +extern int osl_error(int bcmerror); + + +#define PKTBUFSZ 2048 + + + +#define OSL_SYSUPTIME() ((uint32)jiffies * (1000 / HZ)) +#define printf(fmt, args...) printk(fmt , ## args) +#include +#include + +#define bcopy(src, dst, len) memcpy((dst), (src), (len)) +#define bcmp(b1, b2, len) memcmp((b1), (b2), (len)) +#define bzero(b, len) memset((b), '\0', (len)) + + + +#ifndef __mips__ +#define R_REG(osh, r) (\ + SELECT_BUS_READ(osh, sizeof(*(r)) == sizeof(uint8) ? readb((volatile uint8*)(r)) : \ + sizeof(*(r)) == sizeof(uint16) ? readw((volatile uint16*)(r)) : \ + readl((volatile uint32*)(r)), OSL_READ_REG(osh, r)) \ +) +#else +#define R_REG(osh, r) (\ + SELECT_BUS_READ(osh, \ + ({ \ + __typeof(*(r)) __osl_v; \ + __asm__ __volatile__("sync"); \ + switch (sizeof(*(r))) { \ + case sizeof(uint8): __osl_v = \ + readb((volatile uint8*)(r)); break; \ + case sizeof(uint16): __osl_v = \ + readw((volatile uint16*)(r)); break; \ + case sizeof(uint32): __osl_v = \ + readl((volatile uint32*)(r)); break; \ + } \ + __asm__ __volatile__("sync"); \ + __osl_v; \ + }), \ + ({ \ + __typeof(*(r)) __osl_v; \ + __asm__ __volatile__("sync"); \ + __osl_v = OSL_READ_REG(osh, r); \ + __asm__ __volatile__("sync"); \ + __osl_v; \ + })) \ +) +#endif + +#define W_REG(osh, r, v) do { \ + SELECT_BUS_WRITE(osh, \ + switch (sizeof(*(r))) { \ + case sizeof(uint8): writeb((uint8)(v), (volatile uint8*)(r)); break; \ + case sizeof(uint16): writew((uint16)(v), (volatile uint16*)(r)); break; \ + case sizeof(uint32): writel((uint32)(v), (volatile uint32*)(r)); break; \ + }, \ + (OSL_WRITE_REG(osh, r, v))); \ + } while (0) + + +#define AND_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) & (v)) +#define OR_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) | (v)) + + +#define bcopy(src, dst, len) memcpy((dst), (src), (len)) +#define bcmp(b1, b2, len) memcmp((b1), (b2), (len)) +#define bzero(b, len) memset((b), '\0', (len)) + + +#ifdef __mips__ +#include +#define OSL_UNCACHED(va) ((void *)KSEG1ADDR((va))) +#define OSL_CACHED(va) ((void *)KSEG0ADDR((va))) +#else +#define OSL_UNCACHED(va) ((void *)va) +#define OSL_CACHED(va) ((void *)va) +#endif + + +#if defined(__i386__) +#define OSL_GETCYCLES(x) rdtscl((x)) +#else +#define OSL_GETCYCLES(x) ((x) = 0) +#endif + + +#define BUSPROBE(val, addr) ({ (val) = R_REG(NULL, (addr)); 0; }) + + +#if !defined(CONFIG_MMC_MSM7X00A) +#define REG_MAP(pa, size) ioremap_nocache((unsigned long)(pa), (unsigned long)(size)) +#else +#define REG_MAP(pa, size) (void *)(0) +#endif +#define REG_UNMAP(va) iounmap((va)) + + +#define R_SM(r) *(r) +#define W_SM(r, v) (*(r) = (v)) +#define BZERO_SM(r, len) memset((r), '\0', (len)) + + +#include + + +#define PKTGET(osh, len, send) osl_pktget((osh), (len)) +#define PKTDUP(osh, skb) osl_pktdup((osh), (skb)) +#define PKTLIST_DUMP(osh, buf) +#define PKTDBG_TRACE(osh, pkt, bit) +#define PKTFREE(osh, skb, send) osl_pktfree((osh), (skb), (send)) +#ifdef CONFIG_DHD_USE_STATIC_BUF +#define PKTGET_STATIC(osh, len, send) osl_pktget_static((osh), (len)) +#define PKTFREE_STATIC(osh, skb, send) osl_pktfree_static((osh), (skb), (send)) +#endif +#define PKTDATA(osh, skb) (((struct sk_buff*)(skb))->data) +#define PKTLEN(osh, skb) (((struct sk_buff*)(skb))->len) +#define PKTHEADROOM(osh, skb) (PKTDATA(osh, skb)-(((struct sk_buff*)(skb))->head)) +#define PKTTAILROOM(osh, skb) ((((struct sk_buff*)(skb))->end)-(((struct sk_buff*)(skb))->tail)) +#define PKTNEXT(osh, skb) (((struct sk_buff*)(skb))->next) +#define PKTSETNEXT(osh, skb, x) (((struct sk_buff*)(skb))->next = (struct sk_buff*)(x)) +#define PKTSETLEN(osh, skb, len) __skb_trim((struct sk_buff*)(skb), (len)) +#define PKTPUSH(osh, skb, bytes) skb_push((struct sk_buff*)(skb), (bytes)) +#define PKTPULL(osh, skb, bytes) skb_pull((struct sk_buff*)(skb), (bytes)) +#define PKTTAG(skb) ((void*)(((struct sk_buff*)(skb))->cb)) +#define PKTALLOCED(osh) ((osl_pubinfo_t *)(osh))->pktalloced +#define PKTSETPOOL(osh, skb, x, y) do {} while (0) +#define PKTPOOL(osh, skb) FALSE +#define PKTSHRINK(osh, m) (m) + +#ifdef CTFPOOL +#define CTFPOOL_REFILL_THRESH 3 +typedef struct ctfpool { + void *head; + spinlock_t lock; + uint max_obj; + uint curr_obj; + uint obj_size; + uint refills; + uint fast_allocs; + uint fast_frees; + uint slow_allocs; +} ctfpool_t; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) +#define FASTBUF (1 << 4) +#define CTFBUF (1 << 5) +#define PKTSETFAST(osh, skb) ((((struct sk_buff*)(skb))->mac_len) |= FASTBUF) +#define PKTCLRFAST(osh, skb) ((((struct sk_buff*)(skb))->mac_len) &= (~FASTBUF)) +#define PKTSETCTF(osh, skb) ((((struct sk_buff*)(skb))->mac_len) |= CTFBUF) +#define PKTCLRCTF(osh, skb) ((((struct sk_buff*)(skb))->mac_len) &= (~CTFBUF)) +#define PKTISFAST(osh, skb) ((((struct sk_buff*)(skb))->mac_len) & FASTBUF) +#define PKTISCTF(osh, skb) ((((struct sk_buff*)(skb))->mac_len) & CTFBUF) +#define PKTFAST(osh, skb) (((struct sk_buff*)(skb))->mac_len) +#else +#define FASTBUF (1 << 0) +#define CTFBUF (1 << 1) +#define PKTSETFAST(osh, skb) ((((struct sk_buff*)(skb))->__unused) |= FASTBUF) +#define PKTCLRFAST(osh, skb) ((((struct sk_buff*)(skb))->__unused) &= (~FASTBUF)) +#define PKTSETCTF(osh, skb) ((((struct sk_buff*)(skb))->__unused) |= CTFBUF) +#define PKTCLRCTF(osh, skb) ((((struct sk_buff*)(skb))->__unused) &= (~CTFBUF)) +#define PKTISFAST(osh, skb) ((((struct sk_buff*)(skb))->__unused) & FASTBUF) +#define PKTISCTF(osh, skb) ((((struct sk_buff*)(skb))->__unused) & CTFBUF) +#define PKTFAST(osh, skb) (((struct sk_buff*)(skb))->__unused) +#endif + +#define CTFPOOLPTR(osh, skb) (((struct sk_buff*)(skb))->sk) +#define CTFPOOLHEAD(osh, skb) (((ctfpool_t *)((struct sk_buff*)(skb))->sk)->head) + +extern void *osl_ctfpool_add(osl_t *osh); +extern void osl_ctfpool_replenish(osl_t *osh, uint thresh); +extern int32 osl_ctfpool_init(osl_t *osh, uint numobj, uint size); +extern void osl_ctfpool_cleanup(osl_t *osh); +extern void osl_ctfpool_stats(osl_t *osh, void *b); +#endif + +#ifdef HNDCTF +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) +#define SKIPCT (1 << 6) +#define PKTSETSKIPCT(osh, skb) (((struct sk_buff*)(skb))->mac_len |= SKIPCT) +#define PKTCLRSKIPCT(osh, skb) (((struct sk_buff*)(skb))->mac_len &= (~SKIPCT)) +#define PKTSKIPCT(osh, skb) (((struct sk_buff*)(skb))->mac_len & SKIPCT) +#else +#define SKIPCT (1 << 2) +#define PKTSETSKIPCT(osh, skb) (((struct sk_buff*)(skb))->__unused |= SKIPCT) +#define PKTCLRSKIPCT(osh, skb) (((struct sk_buff*)(skb))->__unused &= (~SKIPCT)) +#define PKTSKIPCT(osh, skb) (((struct sk_buff*)(skb))->__unused & SKIPCT) +#endif +#else +#define PKTSETSKIPCT(osh, skb) +#define PKTCLRSKIPCT(osh, skb) +#define PKTSKIPCT(osh, skb) +#endif + +extern void osl_pktfree(osl_t *osh, void *skb, bool send); +extern void *osl_pktget_static(osl_t *osh, uint len); +extern void osl_pktfree_static(osl_t *osh, void *skb, bool send); + +extern void *osl_pktget(osl_t *osh, uint len); +extern void *osl_pktdup(osl_t *osh, void *skb); + + +static INLINE void * +osl_pkt_frmnative(osl_pubinfo_t *osh, void *pkt) +{ + struct sk_buff *nskb; + + if (osh->pkttag) + bzero((void*)((struct sk_buff*)pkt)->cb, OSL_PKTTAG_SZ); + + + for (nskb = (struct sk_buff *)pkt; nskb; nskb = nskb->next) { + osh->pktalloced++; + } + + return (void *)pkt; +} +#define PKTFRMNATIVE(osh, skb) osl_pkt_frmnative(((osl_pubinfo_t *)osh), (struct sk_buff*)(skb)) + + +static INLINE struct sk_buff * +osl_pkt_tonative(osl_pubinfo_t *osh, void *pkt) +{ + struct sk_buff *nskb; + + if (osh->pkttag) + bzero(((struct sk_buff*)pkt)->cb, OSL_PKTTAG_SZ); + + + for (nskb = (struct sk_buff *)pkt; nskb; nskb = nskb->next) { + osh->pktalloced--; + } + + return (struct sk_buff *)pkt; +} +#define PKTTONATIVE(osh, pkt) osl_pkt_tonative((osl_pubinfo_t *)(osh), (pkt)) + +#define PKTLINK(skb) (((struct sk_buff*)(skb))->prev) +#define PKTSETLINK(skb, x) (((struct sk_buff*)(skb))->prev = (struct sk_buff*)(x)) +#define PKTPRIO(skb) (((struct sk_buff*)(skb))->priority) +#define PKTSETPRIO(skb, x) (((struct sk_buff*)(skb))->priority = (x)) +#define PKTSUMNEEDED(skb) (((struct sk_buff*)(skb))->ip_summed == CHECKSUM_HW) +#define PKTSETSUMGOOD(skb, x) (((struct sk_buff*)(skb))->ip_summed = \ + ((x) ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE)) + +#define PKTSHARED(skb) (((struct sk_buff*)(skb))->cloned) + + + +#else + + + + #define ASSERT(exp) do {} while (0) + + +#define MALLOC(o, l) malloc(l) +#define MFREE(o, p, l) free(p) +#include + + +#include + + +#include + + +extern void bcopy(const void *src, void *dst, size_t len); +extern int bcmp(const void *b1, const void *b2, size_t len); +extern void bzero(void *b, size_t len); +#endif + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/linuxver.h b/drivers/net/wireless/bcmdhd/include/linuxver.h new file mode 100644 index 0000000000000..54d88ee923b26 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/linuxver.h @@ -0,0 +1,614 @@ +/* + * Linux-specific abstractions to gain some independence from linux kernel versions. + * Pave over some 2.2 versus 2.4 versus 2.6 kernel differences. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: linuxver.h 312264 2012-02-02 00:49:43Z $ + */ + + +#ifndef _linuxver_h_ +#define _linuxver_h_ + +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) +#include +#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)) +#include +#else +#include +#endif +#endif +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 0)) + +#ifdef __UNDEF_NO_VERSION__ +#undef __NO_VERSION__ +#else +#define __NO_VERSION__ +#endif +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) +#define module_param(_name_, _type_, _perm_) MODULE_PARM(_name_, "i") +#define module_param_string(_name_, _string_, _size_, _perm_) \ + MODULE_PARM(_string_, "c" __MODULE_STRING(_size_)) +#endif + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 9)) +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +#include +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) +#undef IP_TOS +#endif +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 41)) +#include +#else +#include +#ifndef work_struct +#define work_struct tq_struct +#endif +#ifndef INIT_WORK +#define INIT_WORK(_work, _func, _data) INIT_TQUEUE((_work), (_func), (_data)) +#endif +#ifndef schedule_work +#define schedule_work(_work) schedule_task((_work)) +#endif +#ifndef flush_scheduled_work +#define flush_scheduled_work() flush_scheduled_tasks() +#endif +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) +#define MY_INIT_WORK(_work, _func) INIT_WORK(_work, _func) +#else +#define MY_INIT_WORK(_work, _func) INIT_WORK(_work, _func, _work) +typedef void (*work_func_t)(void *work); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) + +#ifndef IRQ_NONE +typedef void irqreturn_t; +#define IRQ_NONE +#define IRQ_HANDLED +#define IRQ_RETVAL(x) +#endif +#else +typedef irqreturn_t(*FN_ISR) (int irq, void *dev_id, struct pt_regs *ptregs); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) +#define IRQF_SHARED SA_SHIRQ +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 17) +#ifdef CONFIG_NET_RADIO +#define CONFIG_WIRELESS_EXT +#endif +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 67) +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) +#include +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) +#include +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) +#include +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) +#include +#endif +#endif + + +#ifndef __exit +#define __exit +#endif +#ifndef __devexit +#define __devexit +#endif +#ifndef __devinit +#define __devinit __init +#endif +#ifndef __devinitdata +#define __devinitdata +#endif +#ifndef __devexit_p +#define __devexit_p(x) x +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)) + +#define pci_get_drvdata(dev) (dev)->sysdata +#define pci_set_drvdata(dev, value) (dev)->sysdata = (value) + + + +struct pci_device_id { + unsigned int vendor, device; + unsigned int subvendor, subdevice; + unsigned int class, class_mask; + unsigned long driver_data; +}; + +struct pci_driver { + struct list_head node; + char *name; + const struct pci_device_id *id_table; + int (*probe)(struct pci_dev *dev, + const struct pci_device_id *id); + void (*remove)(struct pci_dev *dev); + void (*suspend)(struct pci_dev *dev); + void (*resume)(struct pci_dev *dev); +}; + +#define MODULE_DEVICE_TABLE(type, name) +#define PCI_ANY_ID (~0) + + +#define pci_module_init pci_register_driver +extern int pci_register_driver(struct pci_driver *drv); +extern void pci_unregister_driver(struct pci_driver *drv); + +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)) +#define pci_module_init pci_register_driver +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18)) +#ifdef MODULE +#define module_init(x) int init_module(void) { return x(); } +#define module_exit(x) void cleanup_module(void) { x(); } +#else +#define module_init(x) __initcall(x); +#define module_exit(x) __exitcall(x); +#endif +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) +#define WL_USE_NETDEV_OPS +#else +#undef WL_USE_NETDEV_OPS +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) && defined(CONFIG_RFKILL_INPUT) +#define WL_CONFIG_RFKILL_INPUT +#else +#undef WL_CONFIG_RFKILL_INPUT +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 48)) +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 13)) +#define pci_resource_start(dev, bar) ((dev)->base_address[(bar)]) +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 44)) +#define pci_resource_start(dev, bar) ((dev)->resource[(bar)].start) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 23)) +#define pci_enable_device(dev) do { } while (0) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 14)) +#define net_device device +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 42)) + + + +#ifndef PCI_DMA_TODEVICE +#define PCI_DMA_TODEVICE 1 +#define PCI_DMA_FROMDEVICE 2 +#endif + +typedef u32 dma_addr_t; + + +static inline int get_order(unsigned long size) +{ + int order; + + size = (size-1) >> (PAGE_SHIFT-1); + order = -1; + do { + size >>= 1; + order++; + } while (size); + return order; +} + +static inline void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + void *ret; + int gfp = GFP_ATOMIC | GFP_DMA; + + ret = (void *)__get_free_pages(gfp, get_order(size)); + + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_bus(ret); + } + return ret; +} +static inline void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + free_pages((unsigned long)vaddr, get_order(size)); +} +#define pci_map_single(cookie, address, size, dir) virt_to_bus(address) +#define pci_unmap_single(cookie, address, size, dir) + +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 3, 43)) + +#define dev_kfree_skb_any(a) dev_kfree_skb(a) +#define netif_down(dev) do { (dev)->start = 0; } while (0) + + +#ifndef _COMPAT_NETDEVICE_H + + + +#define dev_kfree_skb_irq(a) dev_kfree_skb(a) +#define netif_wake_queue(dev) \ + do { clear_bit(0, &(dev)->tbusy); mark_bh(NET_BH); } while (0) +#define netif_stop_queue(dev) set_bit(0, &(dev)->tbusy) + +static inline void netif_start_queue(struct net_device *dev) +{ + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; +} + +#define netif_queue_stopped(dev) (dev)->tbusy +#define netif_running(dev) (dev)->start + +#endif + +#define netif_device_attach(dev) netif_start_queue(dev) +#define netif_device_detach(dev) netif_stop_queue(dev) + + +#define tasklet_struct tq_struct +static inline void tasklet_schedule(struct tasklet_struct *tasklet) +{ + queue_task(tasklet, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static inline void tasklet_init(struct tasklet_struct *tasklet, + void (*func)(unsigned long), + unsigned long data) +{ + tasklet->next = NULL; + tasklet->sync = 0; + tasklet->routine = (void (*)(void *))func; + tasklet->data = (void *)data; +} +#define tasklet_kill(tasklet) { do {} while (0); } + + +#define del_timer_sync(timer) del_timer(timer) + +#else + +#define netif_down(dev) + +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 3)) + + +#define PREPARE_TQUEUE(_tq, _routine, _data) \ + do { \ + (_tq)->routine = _routine; \ + (_tq)->data = _data; \ + } while (0) + + +#define INIT_TQUEUE(_tq, _routine, _data) \ + do { \ + INIT_LIST_HEAD(&(_tq)->list); \ + (_tq)->sync = 0; \ + PREPARE_TQUEUE((_tq), (_routine), (_data)); \ + } while (0) + +#endif + + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 9) +#define PCI_SAVE_STATE(a, b) pci_save_state(a) +#define PCI_RESTORE_STATE(a, b) pci_restore_state(a) +#else +#define PCI_SAVE_STATE(a, b) pci_save_state(a, b) +#define PCI_RESTORE_STATE(a, b) pci_restore_state(a, b) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 6)) +static inline int +pci_save_state(struct pci_dev *dev, u32 *buffer) +{ + int i; + if (buffer) { + for (i = 0; i < 16; i++) + pci_read_config_dword(dev, i * 4, &buffer[i]); + } + return 0; +} + +static inline int +pci_restore_state(struct pci_dev *dev, u32 *buffer) +{ + int i; + + if (buffer) { + for (i = 0; i < 16; i++) + pci_write_config_dword(dev, i * 4, buffer[i]); + } + + else { + for (i = 0; i < 6; i ++) + pci_write_config_dword(dev, + PCI_BASE_ADDRESS_0 + (i * 4), + pci_resource_start(dev, i)); + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + } + return 0; +} +#endif + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 19)) +#define read_c0_count() read_32bit_cp0_register(CP0_COUNT) +#endif + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)) +#ifndef SET_MODULE_OWNER +#define SET_MODULE_OWNER(dev) do {} while (0) +#define OLD_MOD_INC_USE_COUNT MOD_INC_USE_COUNT +#define OLD_MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT +#else +#define OLD_MOD_INC_USE_COUNT do {} while (0) +#define OLD_MOD_DEC_USE_COUNT do {} while (0) +#endif +#else +#ifndef SET_MODULE_OWNER +#define SET_MODULE_OWNER(dev) do {} while (0) +#endif +#ifndef MOD_INC_USE_COUNT +#define MOD_INC_USE_COUNT do {} while (0) +#endif +#ifndef MOD_DEC_USE_COUNT +#define MOD_DEC_USE_COUNT do {} while (0) +#endif +#define OLD_MOD_INC_USE_COUNT MOD_INC_USE_COUNT +#define OLD_MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT +#endif + +#ifndef SET_NETDEV_DEV +#define SET_NETDEV_DEV(net, pdev) do {} while (0) +#endif + +#ifndef HAVE_FREE_NETDEV +#define free_netdev(dev) kfree(dev) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) + +#define af_packet_priv data +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) +#define DRV_SUSPEND_STATE_TYPE pm_message_t +#else +#define DRV_SUSPEND_STATE_TYPE uint32 +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) +#define CHECKSUM_HW CHECKSUM_PARTIAL +#endif + +typedef struct { + void *parent; + struct task_struct *p_task; + long thr_pid; + int prio; + struct semaphore sema; + bool terminated; + struct completion completed; +} tsk_ctl_t; + + + + +#ifdef DHD_DEBUG +#define DBG_THR(x) printk x +#else +#define DBG_THR(x) +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#define SMP_RD_BARRIER_DEPENDS(x) smp_read_barrier_depends(x) +#else +#define SMP_RD_BARRIER_DEPENDS(x) smp_rmb(x) +#endif + + +#define PROC_START(thread_func, owner, tsk_ctl, flags) \ +{ \ + sema_init(&((tsk_ctl)->sema), 0); \ + init_completion(&((tsk_ctl)->completed)); \ + (tsk_ctl)->parent = owner; \ + (tsk_ctl)->terminated = FALSE; \ + (tsk_ctl)->thr_pid = kernel_thread(thread_func, tsk_ctl, flags); \ + if ((tsk_ctl)->thr_pid > 0) \ + wait_for_completion(&((tsk_ctl)->completed)); \ + DBG_THR(("%s thr:%lx started\n", __FUNCTION__, (tsk_ctl)->thr_pid)); \ +} + +#define PROC_STOP(tsk_ctl) \ +{ \ + (tsk_ctl)->terminated = TRUE; \ + smp_wmb(); \ + up(&((tsk_ctl)->sema)); \ + wait_for_completion(&((tsk_ctl)->completed)); \ + DBG_THR(("%s thr:%lx terminated OK\n", __FUNCTION__, (tsk_ctl)->thr_pid)); \ + (tsk_ctl)->thr_pid = -1; \ +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#define DAEMONIZE(a) daemonize(a); \ + allow_signal(SIGKILL); \ + allow_signal(SIGTERM); +#else /* Linux 2.4 (w/o preemption patch) */ +#define RAISE_RX_SOFTIRQ() \ + cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ) +#define DAEMONIZE(a) daemonize(); \ + do { if (a) \ + strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \ + } while (0); +#endif /* LINUX_VERSION_CODE */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#define BLOCKABLE() (!in_atomic()) +#else +#define BLOCKABLE() (!in_interrupt()) +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) +#define KILL_PROC(nr, sig) \ +{ \ +struct task_struct *tsk; \ +struct pid *pid; \ +pid = find_get_pid((pid_t)nr); \ +tsk = pid_task(pid, PIDTYPE_PID); \ +if (tsk) send_sig(sig, tsk, 1); \ +} +#else +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (LINUX_VERSION_CODE <= \ + KERNEL_VERSION(2, 6, 30)) +#define KILL_PROC(pid, sig) \ +{ \ + struct task_struct *tsk; \ + tsk = find_task_by_vpid(pid); \ + if (tsk) send_sig(sig, tsk, 1); \ +} +#else +#define KILL_PROC(pid, sig) \ +{ \ + kill_proc(pid, sig, 1); \ +} +#endif +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#include +#include +#else +#include + +#define __wait_event_interruptible_timeout(wq, condition, ret) \ +do { \ + wait_queue_t __wait; \ + init_waitqueue_entry(&__wait, current); \ + \ + add_wait_queue(&wq, &__wait); \ + for (;;) { \ + set_current_state(TASK_INTERRUPTIBLE); \ + if (condition) \ + break; \ + if (!signal_pending(current)) { \ + ret = schedule_timeout(ret); \ + if (!ret) \ + break; \ + continue; \ + } \ + ret = -ERESTARTSYS; \ + break; \ + } \ + current->state = TASK_RUNNING; \ + remove_wait_queue(&wq, &__wait); \ +} while (0) + +#define wait_event_interruptible_timeout(wq, condition, timeout) \ +({ \ + long __ret = timeout; \ + if (!(condition)) \ + __wait_event_interruptible_timeout(wq, condition, __ret); \ + __ret; \ +}) + +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) +#define WL_DEV_IF(dev) ((wl_if_t*)netdev_priv(dev)) +#else +#define WL_DEV_IF(dev) ((wl_if_t*)(dev)->priv) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) +#define WL_ISR(i, d, p) wl_isr((i), (d)) +#else +#define WL_ISR(i, d, p) wl_isr((i), (d), (p)) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)) +#define netdev_priv(dev) dev->priv +#endif + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/miniopt.h b/drivers/net/wireless/bcmdhd/include/miniopt.h new file mode 100644 index 0000000000000..77eace6252d70 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/miniopt.h @@ -0,0 +1,77 @@ +/* + * Command line options parser. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * $Id: miniopt.h 277737 2011-08-16 17:54:59Z $ + */ + + +#ifndef MINI_OPT_H +#define MINI_OPT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* ---- Include Files ---------------------------------------------------- */ +/* ---- Constants and Types ---------------------------------------------- */ + +#define MINIOPT_MAXKEY 128 /* Max options */ +typedef struct miniopt { + + /* These are persistent after miniopt_init() */ + const char* name; /* name for prompt in error strings */ + const char* flags; /* option chars that take no args */ + bool longflags; /* long options may be flags */ + bool opt_end; /* at end of options (passed a "--") */ + + /* These are per-call to miniopt() */ + + int consumed; /* number of argv entries cosumed in + * the most recent call to miniopt() + */ + bool positional; + bool good_int; /* 'val' member is the result of a sucessful + * strtol conversion of the option value + */ + char opt; + char key[MINIOPT_MAXKEY]; + char* valstr; /* positional param, or value for the option, + * or null if the option had + * no accompanying value + */ + uint uval; /* strtol translation of valstr */ + int val; /* strtol translation of valstr */ +} miniopt_t; + +void miniopt_init(miniopt_t *t, const char* name, const char* flags, bool longflags); +int miniopt(miniopt_t *t, char **argv); + + +/* ---- Variable Externs ------------------------------------------------- */ +/* ---- Function Prototypes ---------------------------------------------- */ + + +#ifdef __cplusplus + } +#endif + +#endif /* MINI_OPT_H */ diff --git a/drivers/net/wireless/bcmdhd/include/msgtrace.h b/drivers/net/wireless/bcmdhd/include/msgtrace.h new file mode 100644 index 0000000000000..088f1e845a432 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/msgtrace.h @@ -0,0 +1,74 @@ +/* + * Trace messages sent over HBUS + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: msgtrace.h 277737 2011-08-16 17:54:59Z $ + */ + +#ifndef _MSGTRACE_H +#define _MSGTRACE_H + +#ifndef _TYPEDEFS_H_ +#include +#endif + + +/* This marks the start of a packed structure section. */ +#include + +#define MSGTRACE_VERSION 1 + +/* Message trace header */ +typedef BWL_PRE_PACKED_STRUCT struct msgtrace_hdr { + uint8 version; + uint8 spare; + uint16 len; /* Len of the trace */ + uint32 seqnum; /* Sequence number of message. Useful if the messsage has been lost + * because of DMA error or a bus reset (ex: SDIO Func2) + */ + uint32 discarded_bytes; /* Number of discarded bytes because of trace overflow */ + uint32 discarded_printf; /* Number of discarded printf because of trace overflow */ +} BWL_POST_PACKED_STRUCT msgtrace_hdr_t; + +#define MSGTRACE_HDRLEN sizeof(msgtrace_hdr_t) + +/* The hbus driver generates traces when sending a trace message. This causes endless traces. + * This flag must be set to TRUE in any hbus traces. The flag is reset in the function msgtrace_put. + * This prevents endless traces but generates hasardous lost of traces only in bus device code. + * It is recommendat to set this flag in macro SD_TRACE but not in SD_ERROR for avoiding missing + * hbus error traces. hbus error trace should not generates endless traces. + */ +extern bool msgtrace_hbus_trace; + +typedef void (*msgtrace_func_send_t)(void *hdl1, void *hdl2, uint8 *hdr, + uint16 hdrlen, uint8 *buf, uint16 buflen); +extern void msgtrace_start(void); +extern void msgtrace_stop(void); +extern void msgtrace_sent(void); +extern void msgtrace_put(char *buf, int count); +extern void msgtrace_init(void *hdl1, void *hdl2, msgtrace_func_send_t func_send); +extern bool msgtrace_event_enabled(void); + +/* This marks the end of a packed structure section. */ +#include + +#endif /* _MSGTRACE_H */ diff --git a/drivers/net/wireless/bcmdhd/include/osl.h b/drivers/net/wireless/bcmdhd/include/osl.h new file mode 100644 index 0000000000000..b8cc2569f5065 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/osl.h @@ -0,0 +1,66 @@ +/* + * OS Abstraction Layer + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: osl.h 277737 2011-08-16 17:54:59Z $ + */ + + +#ifndef _osl_h_ +#define _osl_h_ + + +typedef struct osl_info osl_t; +typedef struct osl_dmainfo osldma_t; + +#define OSL_PKTTAG_SZ 32 + + +typedef void (*pktfree_cb_fn_t)(void *ctx, void *pkt, unsigned int status); + + +#include + +#ifndef PKTDBG_TRACE +#define PKTDBG_TRACE(osh, pkt, bit) +#endif + + + +#define SET_REG(osh, r, mask, val) W_REG((osh), (r), ((R_REG((osh), r) & ~(mask)) | (val))) + +#ifndef AND_REG +#define AND_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) & (v)) +#endif + +#ifndef OR_REG +#define OR_REG(osh, r, v) W_REG(osh, (r), R_REG(osh, r) | (v)) +#endif + +#if !defined(OSL_SYSUPTIME) +#define OSL_SYSUPTIME() (0) +#define OSL_SYSUPTIME_SUPPORT FALSE +#else +#define OSL_SYSUPTIME_SUPPORT TRUE +#endif + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/packed_section_end.h b/drivers/net/wireless/bcmdhd/include/packed_section_end.h new file mode 100644 index 0000000000000..71f8b2e13b3bf --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/packed_section_end.h @@ -0,0 +1,54 @@ +/* + * Declare directives for structure packing. No padding will be provided + * between the members of packed structures, and therefore, there is no + * guarantee that structure members will be aligned. + * + * Declaring packed structures is compiler specific. In order to handle all + * cases, packed structures should be delared as: + * + * #include + * + * typedef BWL_PRE_PACKED_STRUCT struct foobar_t { + * some_struct_members; + * } BWL_POST_PACKED_STRUCT foobar_t; + * + * #include + * + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * $Id: packed_section_end.h 277737 2011-08-16 17:54:59Z $ + */ + + + + +#ifdef BWL_PACKED_SECTION + #undef BWL_PACKED_SECTION +#else + #error "BWL_PACKED_SECTION is NOT defined!" +#endif + + + + + +#undef BWL_PRE_PACKED_STRUCT +#undef BWL_POST_PACKED_STRUCT diff --git a/drivers/net/wireless/bcmdhd/include/packed_section_start.h b/drivers/net/wireless/bcmdhd/include/packed_section_start.h new file mode 100644 index 0000000000000..afc2ba32fd937 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/packed_section_start.h @@ -0,0 +1,61 @@ +/* + * Declare directives for structure packing. No padding will be provided + * between the members of packed structures, and therefore, there is no + * guarantee that structure members will be aligned. + * + * Declaring packed structures is compiler specific. In order to handle all + * cases, packed structures should be delared as: + * + * #include + * + * typedef BWL_PRE_PACKED_STRUCT struct foobar_t { + * some_struct_members; + * } BWL_POST_PACKED_STRUCT foobar_t; + * + * #include + * + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * $Id: packed_section_start.h 277737 2011-08-16 17:54:59Z $ + */ + + + + +#ifdef BWL_PACKED_SECTION + #error "BWL_PACKED_SECTION is already defined!" +#else + #define BWL_PACKED_SECTION +#endif + + + + + +#if defined(__GNUC__) + #define BWL_PRE_PACKED_STRUCT + #define BWL_POST_PACKED_STRUCT __attribute__ ((packed)) +#elif defined(__CC_ARM) + #define BWL_PRE_PACKED_STRUCT __packed + #define BWL_POST_PACKED_STRUCT +#else + #error "Unknown compiler!" +#endif diff --git a/drivers/net/wireless/bcmdhd/include/pcicfg.h b/drivers/net/wireless/bcmdhd/include/pcicfg.h new file mode 100644 index 0000000000000..66199431fb92f --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/pcicfg.h @@ -0,0 +1,78 @@ +/* + * pcicfg.h: PCI configuration constants and structures. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: pcicfg.h 277737 2011-08-16 17:54:59Z $ + */ + + +#ifndef _h_pcicfg_ +#define _h_pcicfg_ + + +#define PCI_CFG_VID 0 +#define PCI_CFG_CMD 4 +#define PCI_CFG_REV 8 +#define PCI_CFG_BAR0 0x10 +#define PCI_CFG_BAR1 0x14 +#define PCI_BAR0_WIN 0x80 +#define PCI_INT_STATUS 0x90 +#define PCI_INT_MASK 0x94 + +#define PCIE_EXTCFG_OFFSET 0x100 +#define PCI_SPROM_CONTROL 0x88 +#define PCI_BAR1_CONTROL 0x8c +#define PCI_TO_SB_MB 0x98 +#define PCI_BACKPLANE_ADDR 0xa0 +#define PCI_BACKPLANE_DATA 0xa4 +#define PCI_CLK_CTL_ST 0xa8 +#define PCI_BAR0_WIN2 0xac +#define PCI_GPIO_IN 0xb0 +#define PCI_GPIO_OUT 0xb4 +#define PCI_GPIO_OUTEN 0xb8 + +#define PCI_BAR0_SHADOW_OFFSET (2 * 1024) +#define PCI_BAR0_SPROM_OFFSET (4 * 1024) +#define PCI_BAR0_PCIREGS_OFFSET (6 * 1024) +#define PCI_BAR0_PCISBR_OFFSET (4 * 1024) + +#define PCI_BAR0_WINSZ (16 * 1024) + + +#define PCI_16KB0_PCIREGS_OFFSET (8 * 1024) +#define PCI_16KB0_CCREGS_OFFSET (12 * 1024) +#define PCI_16KBB0_WINSZ (16 * 1024) + + +#define PCI_16KB0_WIN2_OFFSET (4 * 1024) + + + +#define SPROM_SZ_MSK 0x02 +#define SPROM_LOCKED 0x08 +#define SPROM_BLANK 0x04 +#define SPROM_WRITEEN 0x10 +#define SPROM_BOOTROM_WE 0x20 +#define SPROM_BACKPLANE_EN 0x40 +#define SPROM_OTPIN_USE 0x80 + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.11.h b/drivers/net/wireless/bcmdhd/include/proto/802.11.h new file mode 100644 index 0000000000000..fd69aac413098 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/802.11.h @@ -0,0 +1,2032 @@ +/* + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * Fundamental types and constants relating to 802.11 + * + * $Id: 802.11.h 304058 2011-12-21 00:39:12Z $ + */ + + +#ifndef _802_11_H_ +#define _802_11_H_ + +#ifndef _TYPEDEFS_H_ +#include +#endif + +#ifndef _NET_ETHERNET_H_ +#include +#endif + +#include + + +#include + + +#define DOT11_TU_TO_US 1024 + + +#define DOT11_A3_HDR_LEN 24 +#define DOT11_A4_HDR_LEN 30 +#define DOT11_MAC_HDR_LEN DOT11_A3_HDR_LEN +#define DOT11_FCS_LEN 4 +#define DOT11_ICV_LEN 4 +#define DOT11_ICV_AES_LEN 8 +#define DOT11_QOS_LEN 2 +#define DOT11_HTC_LEN 4 + +#define DOT11_KEY_INDEX_SHIFT 6 +#define DOT11_IV_LEN 4 +#define DOT11_IV_TKIP_LEN 8 +#define DOT11_IV_AES_OCB_LEN 4 +#define DOT11_IV_AES_CCM_LEN 8 +#define DOT11_IV_MAX_LEN 8 + + +#define DOT11_MAX_MPDU_BODY_LEN 2304 + +#define DOT11_MAX_MPDU_LEN (DOT11_A4_HDR_LEN + \ + DOT11_QOS_LEN + \ + DOT11_IV_AES_CCM_LEN + \ + DOT11_MAX_MPDU_BODY_LEN + \ + DOT11_ICV_LEN + \ + DOT11_FCS_LEN) + +#define DOT11_MAX_SSID_LEN 32 + + +#define DOT11_DEFAULT_RTS_LEN 2347 +#define DOT11_MAX_RTS_LEN 2347 + + +#define DOT11_MIN_FRAG_LEN 256 +#define DOT11_MAX_FRAG_LEN 2346 +#define DOT11_DEFAULT_FRAG_LEN 2346 + + +#define DOT11_MIN_BEACON_PERIOD 1 +#define DOT11_MAX_BEACON_PERIOD 0xFFFF + + +#define DOT11_MIN_DTIM_PERIOD 1 +#define DOT11_MAX_DTIM_PERIOD 0xFF + + +#define DOT11_LLC_SNAP_HDR_LEN 8 +#define DOT11_OUI_LEN 3 +BWL_PRE_PACKED_STRUCT struct dot11_llc_snap_header { + uint8 dsap; + uint8 ssap; + uint8 ctl; + uint8 oui[DOT11_OUI_LEN]; + uint16 type; +} BWL_POST_PACKED_STRUCT; + + +#define RFC1042_HDR_LEN (ETHER_HDR_LEN + DOT11_LLC_SNAP_HDR_LEN) + + + +BWL_PRE_PACKED_STRUCT struct dot11_header { + uint16 fc; + uint16 durid; + struct ether_addr a1; + struct ether_addr a2; + struct ether_addr a3; + uint16 seq; + struct ether_addr a4; +} BWL_POST_PACKED_STRUCT; + + + +BWL_PRE_PACKED_STRUCT struct dot11_rts_frame { + uint16 fc; + uint16 durid; + struct ether_addr ra; + struct ether_addr ta; +} BWL_POST_PACKED_STRUCT; +#define DOT11_RTS_LEN 16 + +BWL_PRE_PACKED_STRUCT struct dot11_cts_frame { + uint16 fc; + uint16 durid; + struct ether_addr ra; +} BWL_POST_PACKED_STRUCT; +#define DOT11_CTS_LEN 10 + +BWL_PRE_PACKED_STRUCT struct dot11_ack_frame { + uint16 fc; + uint16 durid; + struct ether_addr ra; +} BWL_POST_PACKED_STRUCT; +#define DOT11_ACK_LEN 10 + +BWL_PRE_PACKED_STRUCT struct dot11_ps_poll_frame { + uint16 fc; + uint16 durid; + struct ether_addr bssid; + struct ether_addr ta; +} BWL_POST_PACKED_STRUCT; +#define DOT11_PS_POLL_LEN 16 + +BWL_PRE_PACKED_STRUCT struct dot11_cf_end_frame { + uint16 fc; + uint16 durid; + struct ether_addr ra; + struct ether_addr bssid; +} BWL_POST_PACKED_STRUCT; +#define DOT11_CS_END_LEN 16 + + +BWL_PRE_PACKED_STRUCT struct dot11_action_wifi_vendor_specific { + uint8 category; + uint8 OUI[3]; + uint8 type; + uint8 subtype; + uint8 data[1040]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_action_wifi_vendor_specific dot11_action_wifi_vendor_specific_t; + + +BWL_PRE_PACKED_STRUCT struct dot11_action_vs_frmhdr { + uint8 category; + uint8 OUI[3]; + uint8 type; + uint8 subtype; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_action_vs_frmhdr dot11_action_vs_frmhdr_t; +#define DOT11_ACTION_VS_HDR_LEN 6 + +#define BCM_ACTION_OUI_BYTE0 0x00 +#define BCM_ACTION_OUI_BYTE1 0x90 +#define BCM_ACTION_OUI_BYTE2 0x4c + + +#define DOT11_BA_CTL_POLICY_NORMAL 0x0000 +#define DOT11_BA_CTL_POLICY_NOACK 0x0001 +#define DOT11_BA_CTL_POLICY_MASK 0x0001 + +#define DOT11_BA_CTL_MTID 0x0002 +#define DOT11_BA_CTL_COMPRESSED 0x0004 + +#define DOT11_BA_CTL_NUMMSDU_MASK 0x0FC0 +#define DOT11_BA_CTL_NUMMSDU_SHIFT 6 + +#define DOT11_BA_CTL_TID_MASK 0xF000 +#define DOT11_BA_CTL_TID_SHIFT 12 + + +BWL_PRE_PACKED_STRUCT struct dot11_ctl_header { + uint16 fc; + uint16 durid; + struct ether_addr ra; + struct ether_addr ta; +} BWL_POST_PACKED_STRUCT; +#define DOT11_CTL_HDR_LEN 16 + + +BWL_PRE_PACKED_STRUCT struct dot11_bar { + uint16 bar_control; + uint16 seqnum; +} BWL_POST_PACKED_STRUCT; +#define DOT11_BAR_LEN 4 + +#define DOT11_BA_BITMAP_LEN 128 +#define DOT11_BA_CMP_BITMAP_LEN 8 + +BWL_PRE_PACKED_STRUCT struct dot11_ba { + uint16 ba_control; + uint16 seqnum; + uint8 bitmap[DOT11_BA_BITMAP_LEN]; +} BWL_POST_PACKED_STRUCT; +#define DOT11_BA_LEN 4 + + +BWL_PRE_PACKED_STRUCT struct dot11_management_header { + uint16 fc; + uint16 durid; + struct ether_addr da; + struct ether_addr sa; + struct ether_addr bssid; + uint16 seq; +} BWL_POST_PACKED_STRUCT; +#define DOT11_MGMT_HDR_LEN 24 + + + +BWL_PRE_PACKED_STRUCT struct dot11_bcn_prb { + uint32 timestamp[2]; + uint16 beacon_interval; + uint16 capability; +} BWL_POST_PACKED_STRUCT; +#define DOT11_BCN_PRB_LEN 12 +#define DOT11_BCN_PRB_FIXED_LEN 12 + +BWL_PRE_PACKED_STRUCT struct dot11_auth { + uint16 alg; + uint16 seq; + uint16 status; +} BWL_POST_PACKED_STRUCT; +#define DOT11_AUTH_FIXED_LEN 6 + +BWL_PRE_PACKED_STRUCT struct dot11_assoc_req { + uint16 capability; + uint16 listen; +} BWL_POST_PACKED_STRUCT; +#define DOT11_ASSOC_REQ_FIXED_LEN 4 + +BWL_PRE_PACKED_STRUCT struct dot11_reassoc_req { + uint16 capability; + uint16 listen; + struct ether_addr ap; +} BWL_POST_PACKED_STRUCT; +#define DOT11_REASSOC_REQ_FIXED_LEN 10 + +BWL_PRE_PACKED_STRUCT struct dot11_assoc_resp { + uint16 capability; + uint16 status; + uint16 aid; +} BWL_POST_PACKED_STRUCT; +#define DOT11_ASSOC_RESP_FIXED_LEN 6 + +BWL_PRE_PACKED_STRUCT struct dot11_action_measure { + uint8 category; + uint8 action; + uint8 token; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +#define DOT11_ACTION_MEASURE_LEN 3 + +BWL_PRE_PACKED_STRUCT struct dot11_action_ht_ch_width { + uint8 category; + uint8 action; + uint8 ch_width; +} BWL_POST_PACKED_STRUCT; + +BWL_PRE_PACKED_STRUCT struct dot11_action_ht_mimops { + uint8 category; + uint8 action; + uint8 control; +} BWL_POST_PACKED_STRUCT; + +#define SM_PWRSAVE_ENABLE 1 +#define SM_PWRSAVE_MODE 2 + + +BWL_PRE_PACKED_STRUCT struct dot11_power_cnst { + uint8 id; + uint8 len; + uint8 power; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_power_cnst dot11_power_cnst_t; + +BWL_PRE_PACKED_STRUCT struct dot11_power_cap { + uint8 min; + uint8 max; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_power_cap dot11_power_cap_t; + +BWL_PRE_PACKED_STRUCT struct dot11_tpc_rep { + uint8 id; + uint8 len; + uint8 tx_pwr; + uint8 margin; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_tpc_rep dot11_tpc_rep_t; +#define DOT11_MNG_IE_TPC_REPORT_LEN 2 + +BWL_PRE_PACKED_STRUCT struct dot11_supp_channels { + uint8 id; + uint8 len; + uint8 first_channel; + uint8 num_channels; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_supp_channels dot11_supp_channels_t; + + +BWL_PRE_PACKED_STRUCT struct dot11_extch { + uint8 id; + uint8 len; + uint8 extch; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_extch dot11_extch_ie_t; + +BWL_PRE_PACKED_STRUCT struct dot11_brcm_extch { + uint8 id; + uint8 len; + uint8 oui[3]; + uint8 type; + uint8 extch; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_brcm_extch dot11_brcm_extch_ie_t; + +#define BRCM_EXTCH_IE_LEN 5 +#define BRCM_EXTCH_IE_TYPE 53 +#define DOT11_EXTCH_IE_LEN 1 +#define DOT11_EXT_CH_MASK 0x03 +#define DOT11_EXT_CH_UPPER 0x01 +#define DOT11_EXT_CH_LOWER 0x03 +#define DOT11_EXT_CH_NONE 0x00 + +BWL_PRE_PACKED_STRUCT struct dot11_action_frmhdr { + uint8 category; + uint8 action; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +#define DOT11_ACTION_FRMHDR_LEN 2 + + +BWL_PRE_PACKED_STRUCT struct dot11_channel_switch { + uint8 id; + uint8 len; + uint8 mode; + uint8 channel; + uint8 count; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_channel_switch dot11_chan_switch_ie_t; + +#define DOT11_SWITCH_IE_LEN 3 + +#define DOT11_CSA_MODE_ADVISORY 0 +#define DOT11_CSA_MODE_NO_TX 1 + +BWL_PRE_PACKED_STRUCT struct dot11_action_switch_channel { + uint8 category; + uint8 action; + dot11_chan_switch_ie_t chan_switch_ie; + dot11_brcm_extch_ie_t extch_ie; +} BWL_POST_PACKED_STRUCT; + +BWL_PRE_PACKED_STRUCT struct dot11_csa_body { + uint8 mode; + uint8 reg; + uint8 channel; + uint8 count; +} BWL_POST_PACKED_STRUCT; + + +BWL_PRE_PACKED_STRUCT struct dot11_ext_csa { + uint8 id; + uint8 len; + struct dot11_csa_body b; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_ext_csa dot11_ext_csa_ie_t; +#define DOT11_EXT_CSA_IE_LEN 4 + +BWL_PRE_PACKED_STRUCT struct dot11_action_ext_csa { + uint8 category; + uint8 action; + dot11_ext_csa_ie_t chan_switch_ie; +} BWL_POST_PACKED_STRUCT; + +BWL_PRE_PACKED_STRUCT struct dot11y_action_ext_csa { + uint8 category; + uint8 action; + struct dot11_csa_body b; +} BWL_POST_PACKED_STRUCT; + +BWL_PRE_PACKED_STRUCT struct dot11_obss_coex { + uint8 id; + uint8 len; + uint8 info; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_obss_coex dot11_obss_coex_t; +#define DOT11_OBSS_COEXINFO_LEN 1 + +#define DOT11_OBSS_COEX_INFO_REQ 0x01 +#define DOT11_OBSS_COEX_40MHZ_INTOLERANT 0x02 +#define DOT11_OBSS_COEX_20MHZ_WIDTH_REQ 0x04 + +BWL_PRE_PACKED_STRUCT struct dot11_obss_chanlist { + uint8 id; + uint8 len; + uint8 regclass; + uint8 chanlist[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_obss_chanlist dot11_obss_chanlist_t; +#define DOT11_OBSS_CHANLIST_FIXED_LEN 1 + +BWL_PRE_PACKED_STRUCT struct dot11_extcap_ie { + uint8 id; + uint8 len; + uint8 cap[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_extcap_ie dot11_extcap_ie_t; +#define DOT11_EXTCAP_LEN 1 +#define DOT11_EXTCAP_LEN_TDLS 5 + +BWL_PRE_PACKED_STRUCT struct dot11_extcap { + uint8 extcap[DOT11_EXTCAP_LEN_TDLS]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_extcap dot11_extcap_t; + + +#define TDLS_CAP_TDLS 37 +#define TDLS_CAP_PU_BUFFER_STA 28 +#define TDLS_CAP_PEER_PSM 20 +#define TDLS_CAP_CH_SW 30 +#define TDLS_CAP_PROH 38 +#define TDLS_CAP_CH_SW_PROH 39 + +#define TDLS_CAP_MAX_BIT 39 + + + +#define DOT11_MEASURE_TYPE_BASIC 0 +#define DOT11_MEASURE_TYPE_CCA 1 +#define DOT11_MEASURE_TYPE_RPI 2 +#define DOT11_MEASURE_TYPE_CHLOAD 3 +#define DOT11_MEASURE_TYPE_NOISE 4 +#define DOT11_MEASURE_TYPE_BEACON 5 +#define DOT11_MEASURE_TYPE_FRAME 6 +#define DOT11_MEASURE_TYPE_STATS 7 +#define DOT11_MEASURE_TYPE_LCI 8 +#define DOT11_MEASURE_TYPE_TXSTREAM 9 +#define DOT11_MEASURE_TYPE_PAUSE 255 + + +#define DOT11_MEASURE_MODE_PARALLEL (1<<0) +#define DOT11_MEASURE_MODE_ENABLE (1<<1) +#define DOT11_MEASURE_MODE_REQUEST (1<<2) +#define DOT11_MEASURE_MODE_REPORT (1<<3) +#define DOT11_MEASURE_MODE_DUR (1<<4) + +#define DOT11_MEASURE_MODE_LATE (1<<0) +#define DOT11_MEASURE_MODE_INCAPABLE (1<<1) +#define DOT11_MEASURE_MODE_REFUSED (1<<2) + +#define DOT11_MEASURE_BASIC_MAP_BSS ((uint8)(1<<0)) +#define DOT11_MEASURE_BASIC_MAP_OFDM ((uint8)(1<<1)) +#define DOT11_MEASURE_BASIC_MAP_UKNOWN ((uint8)(1<<2)) +#define DOT11_MEASURE_BASIC_MAP_RADAR ((uint8)(1<<3)) +#define DOT11_MEASURE_BASIC_MAP_UNMEAS ((uint8)(1<<4)) + +BWL_PRE_PACKED_STRUCT struct dot11_meas_req { + uint8 id; + uint8 len; + uint8 token; + uint8 mode; + uint8 type; + uint8 channel; + uint8 start_time[8]; + uint16 duration; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_meas_req dot11_meas_req_t; +#define DOT11_MNG_IE_MREQ_LEN 14 + +#define DOT11_MNG_IE_MREQ_FIXED_LEN 3 + +BWL_PRE_PACKED_STRUCT struct dot11_meas_rep { + uint8 id; + uint8 len; + uint8 token; + uint8 mode; + uint8 type; + BWL_PRE_PACKED_STRUCT union + { + BWL_PRE_PACKED_STRUCT struct { + uint8 channel; + uint8 start_time[8]; + uint16 duration; + uint8 map; + } BWL_POST_PACKED_STRUCT basic; + uint8 data[1]; + } BWL_POST_PACKED_STRUCT rep; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_meas_rep dot11_meas_rep_t; + + +#define DOT11_MNG_IE_MREP_FIXED_LEN 3 + +BWL_PRE_PACKED_STRUCT struct dot11_meas_rep_basic { + uint8 channel; + uint8 start_time[8]; + uint16 duration; + uint8 map; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_meas_rep_basic dot11_meas_rep_basic_t; +#define DOT11_MEASURE_BASIC_REP_LEN 12 + +BWL_PRE_PACKED_STRUCT struct dot11_quiet { + uint8 id; + uint8 len; + uint8 count; + uint8 period; + uint16 duration; + uint16 offset; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_quiet dot11_quiet_t; + +BWL_PRE_PACKED_STRUCT struct chan_map_tuple { + uint8 channel; + uint8 map; +} BWL_POST_PACKED_STRUCT; +typedef struct chan_map_tuple chan_map_tuple_t; + +BWL_PRE_PACKED_STRUCT struct dot11_ibss_dfs { + uint8 id; + uint8 len; + uint8 eaddr[ETHER_ADDR_LEN]; + uint8 interval; + chan_map_tuple_t map[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_ibss_dfs dot11_ibss_dfs_t; + + +#define WME_OUI "\x00\x50\xf2" +#define WME_OUI_LEN 3 +#define WME_OUI_TYPE 2 +#define WME_VER 1 +#define WME_TYPE 2 +#define WME_SUBTYPE_IE 0 +#define WME_SUBTYPE_PARAM_IE 1 +#define WME_SUBTYPE_TSPEC 2 +#define WME_VERSION_LEN 1 +#define WME_PARAMETER_IE_LEN 24 + + + +#define AC_BE 0 +#define AC_BK 1 +#define AC_VI 2 +#define AC_VO 3 +#define AC_COUNT 4 + +typedef uint8 ac_bitmap_t; + +#define AC_BITMAP_NONE 0x0 +#define AC_BITMAP_ALL 0xf +#define AC_BITMAP_TST(ab, ac) (((ab) & (1 << (ac))) != 0) +#define AC_BITMAP_SET(ab, ac) (((ab) |= (1 << (ac)))) +#define AC_BITMAP_RESET(ab, ac) (((ab) &= ~(1 << (ac)))) + + +BWL_PRE_PACKED_STRUCT struct wme_ie { + uint8 oui[3]; + uint8 type; + uint8 subtype; + uint8 version; + uint8 qosinfo; +} BWL_POST_PACKED_STRUCT; +typedef struct wme_ie wme_ie_t; +#define WME_IE_LEN 7 + +BWL_PRE_PACKED_STRUCT struct edcf_acparam { + uint8 ACI; + uint8 ECW; + uint16 TXOP; +} BWL_POST_PACKED_STRUCT; +typedef struct edcf_acparam edcf_acparam_t; + + +BWL_PRE_PACKED_STRUCT struct wme_param_ie { + uint8 oui[3]; + uint8 type; + uint8 subtype; + uint8 version; + uint8 qosinfo; + uint8 rsvd; + edcf_acparam_t acparam[AC_COUNT]; +} BWL_POST_PACKED_STRUCT; +typedef struct wme_param_ie wme_param_ie_t; +#define WME_PARAM_IE_LEN 24 + + +#define WME_QI_AP_APSD_MASK 0x80 +#define WME_QI_AP_APSD_SHIFT 7 +#define WME_QI_AP_COUNT_MASK 0x0f +#define WME_QI_AP_COUNT_SHIFT 0 + + +#define WME_QI_STA_MAXSPLEN_MASK 0x60 +#define WME_QI_STA_MAXSPLEN_SHIFT 5 +#define WME_QI_STA_APSD_ALL_MASK 0xf +#define WME_QI_STA_APSD_ALL_SHIFT 0 +#define WME_QI_STA_APSD_BE_MASK 0x8 +#define WME_QI_STA_APSD_BE_SHIFT 3 +#define WME_QI_STA_APSD_BK_MASK 0x4 +#define WME_QI_STA_APSD_BK_SHIFT 2 +#define WME_QI_STA_APSD_VI_MASK 0x2 +#define WME_QI_STA_APSD_VI_SHIFT 1 +#define WME_QI_STA_APSD_VO_MASK 0x1 +#define WME_QI_STA_APSD_VO_SHIFT 0 + + +#define EDCF_AIFSN_MIN 1 +#define EDCF_AIFSN_MAX 15 +#define EDCF_AIFSN_MASK 0x0f +#define EDCF_ACM_MASK 0x10 +#define EDCF_ACI_MASK 0x60 +#define EDCF_ACI_SHIFT 5 +#define EDCF_AIFSN_SHIFT 12 + + +#define EDCF_ECW_MIN 0 +#define EDCF_ECW_MAX 15 +#define EDCF_ECW2CW(exp) ((1 << (exp)) - 1) +#define EDCF_ECWMIN_MASK 0x0f +#define EDCF_ECWMAX_MASK 0xf0 +#define EDCF_ECWMAX_SHIFT 4 + + +#define EDCF_TXOP_MIN 0 +#define EDCF_TXOP_MAX 65535 +#define EDCF_TXOP2USEC(txop) ((txop) << 5) + + +#define NON_EDCF_AC_BE_ACI_STA 0x02 + + +#define EDCF_AC_BE_ACI_STA 0x03 +#define EDCF_AC_BE_ECW_STA 0xA4 +#define EDCF_AC_BE_TXOP_STA 0x0000 +#define EDCF_AC_BK_ACI_STA 0x27 +#define EDCF_AC_BK_ECW_STA 0xA4 +#define EDCF_AC_BK_TXOP_STA 0x0000 +#define EDCF_AC_VI_ACI_STA 0x42 +#define EDCF_AC_VI_ECW_STA 0x43 +#define EDCF_AC_VI_TXOP_STA 0x005e +#define EDCF_AC_VO_ACI_STA 0x62 +#define EDCF_AC_VO_ECW_STA 0x32 +#define EDCF_AC_VO_TXOP_STA 0x002f + + +#define EDCF_AC_BE_ACI_AP 0x03 +#define EDCF_AC_BE_ECW_AP 0x64 +#define EDCF_AC_BE_TXOP_AP 0x0000 +#define EDCF_AC_BK_ACI_AP 0x27 +#define EDCF_AC_BK_ECW_AP 0xA4 +#define EDCF_AC_BK_TXOP_AP 0x0000 +#define EDCF_AC_VI_ACI_AP 0x41 +#define EDCF_AC_VI_ECW_AP 0x43 +#define EDCF_AC_VI_TXOP_AP 0x005e +#define EDCF_AC_VO_ACI_AP 0x61 +#define EDCF_AC_VO_ECW_AP 0x32 +#define EDCF_AC_VO_TXOP_AP 0x002f + + +BWL_PRE_PACKED_STRUCT struct edca_param_ie { + uint8 qosinfo; + uint8 rsvd; + edcf_acparam_t acparam[AC_COUNT]; +} BWL_POST_PACKED_STRUCT; +typedef struct edca_param_ie edca_param_ie_t; +#define EDCA_PARAM_IE_LEN 18 + + +BWL_PRE_PACKED_STRUCT struct qos_cap_ie { + uint8 qosinfo; +} BWL_POST_PACKED_STRUCT; +typedef struct qos_cap_ie qos_cap_ie_t; + +BWL_PRE_PACKED_STRUCT struct dot11_qbss_load_ie { + uint8 id; + uint8 length; + uint16 station_count; + uint8 channel_utilization; + uint16 aac; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_qbss_load_ie dot11_qbss_load_ie_t; + + +#define FIXED_MSDU_SIZE 0x8000 +#define MSDU_SIZE_MASK 0x7fff + + + +#define INTEGER_SHIFT 13 +#define FRACTION_MASK 0x1FFF + + +BWL_PRE_PACKED_STRUCT struct dot11_management_notification { + uint8 category; + uint8 action; + uint8 token; + uint8 status; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +#define DOT11_MGMT_NOTIFICATION_LEN 4 + + +BWL_PRE_PACKED_STRUCT struct ti_ie { + uint8 ti_type; + uint32 ti_val; +} BWL_POST_PACKED_STRUCT; +typedef struct ti_ie ti_ie_t; +#define TI_TYPE_REASSOC_DEADLINE 1 +#define TI_TYPE_KEY_LIFETIME 2 + + +#define WME_ADDTS_REQUEST 0 +#define WME_ADDTS_RESPONSE 1 +#define WME_DELTS_REQUEST 2 + + +#define WME_ADMISSION_ACCEPTED 0 +#define WME_INVALID_PARAMETERS 1 +#define WME_ADMISSION_REFUSED 3 + + +#define BCN_PRB_SSID(body) ((char*)(body) + DOT11_BCN_PRB_LEN) + + +#define DOT11_OPEN_SYSTEM 0 +#define DOT11_SHARED_KEY 1 +#define DOT11_FAST_BSS 2 +#define DOT11_CHALLENGE_LEN 128 + + +#define FC_PVER_MASK 0x3 +#define FC_PVER_SHIFT 0 +#define FC_TYPE_MASK 0xC +#define FC_TYPE_SHIFT 2 +#define FC_SUBTYPE_MASK 0xF0 +#define FC_SUBTYPE_SHIFT 4 +#define FC_TODS 0x100 +#define FC_TODS_SHIFT 8 +#define FC_FROMDS 0x200 +#define FC_FROMDS_SHIFT 9 +#define FC_MOREFRAG 0x400 +#define FC_MOREFRAG_SHIFT 10 +#define FC_RETRY 0x800 +#define FC_RETRY_SHIFT 11 +#define FC_PM 0x1000 +#define FC_PM_SHIFT 12 +#define FC_MOREDATA 0x2000 +#define FC_MOREDATA_SHIFT 13 +#define FC_WEP 0x4000 +#define FC_WEP_SHIFT 14 +#define FC_ORDER 0x8000 +#define FC_ORDER_SHIFT 15 + + +#define SEQNUM_SHIFT 4 +#define SEQNUM_MAX 0x1000 +#define FRAGNUM_MASK 0xF + + + + +#define FC_TYPE_MNG 0 +#define FC_TYPE_CTL 1 +#define FC_TYPE_DATA 2 + + +#define FC_SUBTYPE_ASSOC_REQ 0 +#define FC_SUBTYPE_ASSOC_RESP 1 +#define FC_SUBTYPE_REASSOC_REQ 2 +#define FC_SUBTYPE_REASSOC_RESP 3 +#define FC_SUBTYPE_PROBE_REQ 4 +#define FC_SUBTYPE_PROBE_RESP 5 +#define FC_SUBTYPE_BEACON 8 +#define FC_SUBTYPE_ATIM 9 +#define FC_SUBTYPE_DISASSOC 10 +#define FC_SUBTYPE_AUTH 11 +#define FC_SUBTYPE_DEAUTH 12 +#define FC_SUBTYPE_ACTION 13 +#define FC_SUBTYPE_ACTION_NOACK 14 + + +#define FC_SUBTYPE_CTL_WRAPPER 7 +#define FC_SUBTYPE_BLOCKACK_REQ 8 +#define FC_SUBTYPE_BLOCKACK 9 +#define FC_SUBTYPE_PS_POLL 10 +#define FC_SUBTYPE_RTS 11 +#define FC_SUBTYPE_CTS 12 +#define FC_SUBTYPE_ACK 13 +#define FC_SUBTYPE_CF_END 14 +#define FC_SUBTYPE_CF_END_ACK 15 + + +#define FC_SUBTYPE_DATA 0 +#define FC_SUBTYPE_DATA_CF_ACK 1 +#define FC_SUBTYPE_DATA_CF_POLL 2 +#define FC_SUBTYPE_DATA_CF_ACK_POLL 3 +#define FC_SUBTYPE_NULL 4 +#define FC_SUBTYPE_CF_ACK 5 +#define FC_SUBTYPE_CF_POLL 6 +#define FC_SUBTYPE_CF_ACK_POLL 7 +#define FC_SUBTYPE_QOS_DATA 8 +#define FC_SUBTYPE_QOS_DATA_CF_ACK 9 +#define FC_SUBTYPE_QOS_DATA_CF_POLL 10 +#define FC_SUBTYPE_QOS_DATA_CF_ACK_POLL 11 +#define FC_SUBTYPE_QOS_NULL 12 +#define FC_SUBTYPE_QOS_CF_POLL 14 +#define FC_SUBTYPE_QOS_CF_ACK_POLL 15 + + +#define FC_SUBTYPE_ANY_QOS(s) (((s) & 8) != 0) +#define FC_SUBTYPE_ANY_NULL(s) (((s) & 4) != 0) +#define FC_SUBTYPE_ANY_CF_POLL(s) (((s) & 2) != 0) +#define FC_SUBTYPE_ANY_CF_ACK(s) (((s) & 1) != 0) + + +#define FC_KIND_MASK (FC_TYPE_MASK | FC_SUBTYPE_MASK) + +#define FC_KIND(t, s) (((t) << FC_TYPE_SHIFT) | ((s) << FC_SUBTYPE_SHIFT)) + +#define FC_SUBTYPE(fc) (((fc) & FC_SUBTYPE_MASK) >> FC_SUBTYPE_SHIFT) +#define FC_TYPE(fc) (((fc) & FC_TYPE_MASK) >> FC_TYPE_SHIFT) + +#define FC_ASSOC_REQ FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ASSOC_REQ) +#define FC_ASSOC_RESP FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ASSOC_RESP) +#define FC_REASSOC_REQ FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_REASSOC_REQ) +#define FC_REASSOC_RESP FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_REASSOC_RESP) +#define FC_PROBE_REQ FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_PROBE_REQ) +#define FC_PROBE_RESP FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_PROBE_RESP) +#define FC_BEACON FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_BEACON) +#define FC_DISASSOC FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_DISASSOC) +#define FC_AUTH FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_AUTH) +#define FC_DEAUTH FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_DEAUTH) +#define FC_ACTION FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ACTION) +#define FC_ACTION_NOACK FC_KIND(FC_TYPE_MNG, FC_SUBTYPE_ACTION_NOACK) + +#define FC_CTL_WRAPPER FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CTL_WRAPPER) +#define FC_BLOCKACK_REQ FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_BLOCKACK_REQ) +#define FC_BLOCKACK FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_BLOCKACK) +#define FC_PS_POLL FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_PS_POLL) +#define FC_RTS FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_RTS) +#define FC_CTS FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CTS) +#define FC_ACK FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_ACK) +#define FC_CF_END FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CF_END) +#define FC_CF_END_ACK FC_KIND(FC_TYPE_CTL, FC_SUBTYPE_CF_END_ACK) + +#define FC_DATA FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_DATA) +#define FC_NULL_DATA FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_NULL) +#define FC_DATA_CF_ACK FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_DATA_CF_ACK) +#define FC_QOS_DATA FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_QOS_DATA) +#define FC_QOS_NULL FC_KIND(FC_TYPE_DATA, FC_SUBTYPE_QOS_NULL) + + + + +#define QOS_PRIO_SHIFT 0 +#define QOS_PRIO_MASK 0x0007 +#define QOS_PRIO(qos) (((qos) & QOS_PRIO_MASK) >> QOS_PRIO_SHIFT) + + +#define QOS_TID_SHIFT 0 +#define QOS_TID_MASK 0x000f +#define QOS_TID(qos) (((qos) & QOS_TID_MASK) >> QOS_TID_SHIFT) + + +#define QOS_EOSP_SHIFT 4 +#define QOS_EOSP_MASK 0x0010 +#define QOS_EOSP(qos) (((qos) & QOS_EOSP_MASK) >> QOS_EOSP_SHIFT) + + +#define QOS_ACK_NORMAL_ACK 0 +#define QOS_ACK_NO_ACK 1 +#define QOS_ACK_NO_EXP_ACK 2 +#define QOS_ACK_BLOCK_ACK 3 +#define QOS_ACK_SHIFT 5 +#define QOS_ACK_MASK 0x0060 +#define QOS_ACK(qos) (((qos) & QOS_ACK_MASK) >> QOS_ACK_SHIFT) + + +#define QOS_AMSDU_SHIFT 7 +#define QOS_AMSDU_MASK 0x0080 + + + + + + +#define DOT11_MNG_AUTH_ALGO_LEN 2 +#define DOT11_MNG_AUTH_SEQ_LEN 2 +#define DOT11_MNG_BEACON_INT_LEN 2 +#define DOT11_MNG_CAP_LEN 2 +#define DOT11_MNG_AP_ADDR_LEN 6 +#define DOT11_MNG_LISTEN_INT_LEN 2 +#define DOT11_MNG_REASON_LEN 2 +#define DOT11_MNG_AID_LEN 2 +#define DOT11_MNG_STATUS_LEN 2 +#define DOT11_MNG_TIMESTAMP_LEN 8 + + +#define DOT11_AID_MASK 0x3fff + + +#define DOT11_RC_RESERVED 0 +#define DOT11_RC_UNSPECIFIED 1 +#define DOT11_RC_AUTH_INVAL 2 +#define DOT11_RC_DEAUTH_LEAVING 3 +#define DOT11_RC_INACTIVITY 4 +#define DOT11_RC_BUSY 5 +#define DOT11_RC_INVAL_CLASS_2 6 +#define DOT11_RC_INVAL_CLASS_3 7 +#define DOT11_RC_DISASSOC_LEAVING 8 +#define DOT11_RC_NOT_AUTH 9 +#define DOT11_RC_BAD_PC 10 +#define DOT11_RC_BAD_CHANNELS 11 + + + +#define DOT11_RC_UNSPECIFIED_QOS 32 +#define DOT11_RC_INSUFFCIENT_BW 33 +#define DOT11_RC_EXCESSIVE_FRAMES 34 +#define DOT11_RC_TX_OUTSIDE_TXOP 35 +#define DOT11_RC_LEAVING_QBSS 36 +#define DOT11_RC_BAD_MECHANISM 37 +#define DOT11_RC_SETUP_NEEDED 38 +#define DOT11_RC_TIMEOUT 39 + +#define DOT11_RC_MAX 23 + +#define DOT11_RC_TDLS_PEER_UNREACH 25 +#define DOT11_RC_TDLS_DOWN_UNSPECIFIED 26 + + +#define DOT11_SC_SUCCESS 0 +#define DOT11_SC_FAILURE 1 +#define DOT11_SC_TDLS_WAKEUP_SCH_ALT 2 + +#define DOT11_SC_TDLS_WAKEUP_SCH_REJ 3 +#define DOT11_SC_TDLS_SEC_DISABLED 5 +#define DOT11_SC_LIFETIME_REJ 6 +#define DOT11_SC_NOT_SAME_BSS 7 +#define DOT11_SC_CAP_MISMATCH 10 +#define DOT11_SC_REASSOC_FAIL 11 +#define DOT11_SC_ASSOC_FAIL 12 +#define DOT11_SC_AUTH_MISMATCH 13 +#define DOT11_SC_AUTH_SEQ 14 +#define DOT11_SC_AUTH_CHALLENGE_FAIL 15 +#define DOT11_SC_AUTH_TIMEOUT 16 +#define DOT11_SC_ASSOC_BUSY_FAIL 17 +#define DOT11_SC_ASSOC_RATE_MISMATCH 18 +#define DOT11_SC_ASSOC_SHORT_REQUIRED 19 +#define DOT11_SC_ASSOC_PBCC_REQUIRED 20 +#define DOT11_SC_ASSOC_AGILITY_REQUIRED 21 +#define DOT11_SC_ASSOC_SPECTRUM_REQUIRED 22 +#define DOT11_SC_ASSOC_BAD_POWER_CAP 23 +#define DOT11_SC_ASSOC_BAD_SUP_CHANNELS 24 +#define DOT11_SC_ASSOC_SHORTSLOT_REQUIRED 25 +#define DOT11_SC_ASSOC_ERPBCC_REQUIRED 26 +#define DOT11_SC_ASSOC_DSSOFDM_REQUIRED 27 +#define DOT11_SC_ASSOC_R0KH_UNREACHABLE 28 +#define DOT11_SC_ASSOC_TRY_LATER 30 +#define DOT11_SC_ASSOC_MFP_VIOLATION 31 + +#define DOT11_SC_DECLINED 37 +#define DOT11_SC_INVALID_PARAMS 38 +#define DOT11_SC_INVALID_PAIRWISE_CIPHER 42 +#define DOT11_SC_INVALID_AKMP 43 +#define DOT11_SC_INVALID_RSNIE_CAP 45 +#define DOT11_SC_INVALID_PMKID 53 +#define DOT11_SC_INVALID_MDID 54 +#define DOT11_SC_INVALID_FTIE 55 + +#define DOT11_SC_UNEXP_MSG 70 +#define DOT11_SC_INVALID_SNONCE 71 +#define DOT11_SC_INVALID_RSNIE 72 + +#define DOT11_MNG_DS_PARAM_LEN 1 +#define DOT11_MNG_IBSS_PARAM_LEN 2 + + +#define DOT11_MNG_TIM_FIXED_LEN 3 +#define DOT11_MNG_TIM_DTIM_COUNT 0 +#define DOT11_MNG_TIM_DTIM_PERIOD 1 +#define DOT11_MNG_TIM_BITMAP_CTL 2 +#define DOT11_MNG_TIM_PVB 3 + + +#define TLV_TAG_OFF 0 +#define TLV_LEN_OFF 1 +#define TLV_HDR_LEN 2 +#define TLV_BODY_OFF 2 + + +#define DOT11_MNG_SSID_ID 0 +#define DOT11_MNG_RATES_ID 1 +#define DOT11_MNG_FH_PARMS_ID 2 +#define DOT11_MNG_DS_PARMS_ID 3 +#define DOT11_MNG_CF_PARMS_ID 4 +#define DOT11_MNG_TIM_ID 5 +#define DOT11_MNG_IBSS_PARMS_ID 6 +#define DOT11_MNG_COUNTRY_ID 7 +#define DOT11_MNG_HOPPING_PARMS_ID 8 +#define DOT11_MNG_HOPPING_TABLE_ID 9 +#define DOT11_MNG_REQUEST_ID 10 +#define DOT11_MNG_QBSS_LOAD_ID 11 +#define DOT11_MNG_EDCA_PARAM_ID 12 +#define DOT11_MNG_CHALLENGE_ID 16 +#define DOT11_MNG_PWR_CONSTRAINT_ID 32 +#define DOT11_MNG_PWR_CAP_ID 33 +#define DOT11_MNG_TPC_REQUEST_ID 34 +#define DOT11_MNG_TPC_REPORT_ID 35 +#define DOT11_MNG_SUPP_CHANNELS_ID 36 +#define DOT11_MNG_CHANNEL_SWITCH_ID 37 +#define DOT11_MNG_MEASURE_REQUEST_ID 38 +#define DOT11_MNG_MEASURE_REPORT_ID 39 +#define DOT11_MNG_QUIET_ID 40 +#define DOT11_MNG_IBSS_DFS_ID 41 +#define DOT11_MNG_ERP_ID 42 +#define DOT11_MNG_TS_DELAY_ID 43 +#define DOT11_MNG_HT_CAP 45 +#define DOT11_MNG_QOS_CAP_ID 46 +#define DOT11_MNG_NONERP_ID 47 +#define DOT11_MNG_RSN_ID 48 +#define DOT11_MNG_EXT_RATES_ID 50 +#define DOT11_MNG_AP_CHREP_ID 51 +#define DOT11_MNG_NBR_REP_ID 52 +#define DOT11_MNG_MDIE_ID 54 +#define DOT11_MNG_FTIE_ID 55 +#define DOT11_MNG_FT_TI_ID 56 +#define DOT11_MNG_RDE_ID 57 +#define DOT11_MNG_REGCLASS_ID 59 +#define DOT11_MNG_EXT_CSA_ID 60 +#define DOT11_MNG_HT_ADD 61 +#define DOT11_MNG_EXT_CHANNEL_OFFSET 62 + + +#define DOT11_MNG_RRM_CAP_ID 70 +#define DOT11_MNG_HT_BSS_COEXINFO_ID 72 +#define DOT11_MNG_HT_BSS_CHANNEL_REPORT_ID 73 +#define DOT11_MNG_HT_OBSS_ID 74 +#define DOT11_MNG_CHANNEL_USAGE 97 +#define DOT11_MNG_LINK_IDENTIFIER_ID 101 +#define DOT11_MNG_WAKEUP_SCHEDULE_ID 102 +#define DOT11_MNG_CHANNEL_SWITCH_TIMING_ID 104 +#define DOT11_MNG_PTI_CONTROL_ID 105 +#define DOT11_MNG_PU_BUFFER_STATUS_ID 106 +#define DOT11_MNG_EXT_CAP_ID 127 +#define DOT11_MNG_WPA_ID 221 +#define DOT11_MNG_PROPR_ID 221 + +#define DOT11_MNG_VS_ID 221 + + +#define DOT11_RATE_BASIC 0x80 +#define DOT11_RATE_MASK 0x7F + + +#define DOT11_MNG_ERP_LEN 1 +#define DOT11_MNG_NONERP_PRESENT 0x01 +#define DOT11_MNG_USE_PROTECTION 0x02 +#define DOT11_MNG_BARKER_PREAMBLE 0x04 + +#define DOT11_MGN_TS_DELAY_LEN 4 +#define TS_DELAY_FIELD_SIZE 4 + + +#define DOT11_CAP_ESS 0x0001 +#define DOT11_CAP_IBSS 0x0002 +#define DOT11_CAP_POLLABLE 0x0004 +#define DOT11_CAP_POLL_RQ 0x0008 +#define DOT11_CAP_PRIVACY 0x0010 +#define DOT11_CAP_SHORT 0x0020 +#define DOT11_CAP_PBCC 0x0040 +#define DOT11_CAP_AGILITY 0x0080 +#define DOT11_CAP_SPECTRUM 0x0100 +#define DOT11_CAP_SHORTSLOT 0x0400 +#define DOT11_CAP_RRM 0x1000 +#define DOT11_CAP_CCK_OFDM 0x2000 + + +#define DOT11_OBSS_COEX_MNG_SUPPORT 0x01 + + +#define DOT11_ACTION_HDR_LEN 2 +#define DOT11_ACTION_CAT_OFF 0 +#define DOT11_ACTION_ACT_OFF 1 + + +#define DOT11_ACTION_CAT_ERR_MASK 0x80 +#define DOT11_ACTION_CAT_MASK 0x7F +#define DOT11_ACTION_CAT_SPECT_MNG 0 +#define DOT11_ACTION_CAT_QOS 1 +#define DOT11_ACTION_CAT_DLS 2 +#define DOT11_ACTION_CAT_BLOCKACK 3 +#define DOT11_ACTION_CAT_PUBLIC 4 +#define DOT11_ACTION_CAT_RRM 5 +#define DOT11_ACTION_CAT_FBT 6 +#define DOT11_ACTION_CAT_HT 7 +#if defined(MFP) || defined(WLFBT) || defined(WLWNM) +#define DOT11_ACTION_CAT_SA_QUERY 8 +#define DOT11_ACTION_CAT_PDPA 9 +#define DOT11_ACTION_CAT_BSSMGMT 10 +#define DOT11_ACTION_NOTIFICATION 17 +#define DOT11_ACTION_CAT_VSP 126 +#endif +#define DOT11_ACTION_NOTIFICATION 17 +#define DOT11_ACTION_CAT_VS 127 + + +#define DOT11_SM_ACTION_M_REQ 0 +#define DOT11_SM_ACTION_M_REP 1 +#define DOT11_SM_ACTION_TPC_REQ 2 +#define DOT11_SM_ACTION_TPC_REP 3 +#define DOT11_SM_ACTION_CHANNEL_SWITCH 4 +#define DOT11_SM_ACTION_EXT_CSA 5 + + +#define DOT11_ACTION_ID_HT_CH_WIDTH 0 +#define DOT11_ACTION_ID_HT_MIMO_PS 1 + + +#define DOT11_PUB_ACTION_BSS_COEX_MNG 0 +#define DOT11_PUB_ACTION_CHANNEL_SWITCH 4 + + +#define DOT11_BA_ACTION_ADDBA_REQ 0 +#define DOT11_BA_ACTION_ADDBA_RESP 1 +#define DOT11_BA_ACTION_DELBA 2 + + +#define DOT11_ADDBA_PARAM_AMSDU_SUP 0x0001 +#define DOT11_ADDBA_PARAM_POLICY_MASK 0x0002 +#define DOT11_ADDBA_PARAM_POLICY_SHIFT 1 +#define DOT11_ADDBA_PARAM_TID_MASK 0x003c +#define DOT11_ADDBA_PARAM_TID_SHIFT 2 +#define DOT11_ADDBA_PARAM_BSIZE_MASK 0xffc0 +#define DOT11_ADDBA_PARAM_BSIZE_SHIFT 6 + +#define DOT11_ADDBA_POLICY_DELAYED 0 +#define DOT11_ADDBA_POLICY_IMMEDIATE 1 + + +#define DOT11_FT_ACTION_FT_RESERVED 0 +#define DOT11_FT_ACTION_FT_REQ 1 +#define DOT11_FT_ACTION_FT_RES 2 +#define DOT11_FT_ACTION_FT_CON 3 +#define DOT11_FT_ACTION_FT_ACK 4 + + + +#define DOT11_WNM_ACTION_EVENT_REQ 0 +#define DOT11_WNM_ACTION_EVENT_REP 1 +#define DOT11_WNM_ACTION_DIAG_REQ 2 +#define DOT11_WNM_ACTION_DIAG_REP 3 +#define DOT11_WNM_ACTION_LOC_CFG_REQ 4 +#define DOT11_WNM_ACTION_LOC_RFG_RESP 5 +#define DOT11_WNM_ACTION_BSS_TRANS_QURY 6 +#define DOT11_WNM_ACTION_BSS_TRANS_REQ 7 +#define DOT11_WNM_ACTION_BSS_TRANS_RESP 8 +#define DOT11_WNM_ACTION_FMS_REQ 9 +#define DOT11_WNM_ACTION_FMS_RESP 10 +#define DOT11_WNM_ACTION_COL_INTRFRNCE_REQ 11 +#define DOT11_WNM_ACTION_COL_INTRFRNCE_REP 12 +#define DOT11_WNM_ACTION_TFS_REQ 13 +#define DOT11_WNM_ACTION_TFS_RESP 14 +#define DOT11_WNM_ACTION_TFS_NOTIFY 15 +#define DOT11_WNM_ACTION_WNM_SLEEP_REQ 16 +#define DOT11_WNM_ACTION_WNM_SLEEP_RESP 17 +#define DOT11_WNM_ACTION_TIM_BCAST_REQ 18 +#define DOT11_WNM_ACTION_TIM_BCAST_RESP 19 +#define DOT11_WNM_ACTION_QOS_TRFC_CAP_UPD 20 +#define DOT11_WNM_ACTION_CHAN_USAGE_REQ 21 +#define DOT11_WNM_ACTION_CHAN_USAGE_RESP 22 +#define DOT11_WNM_ACTION_DMS_REQ 23 +#define DOT11_WNM_ACTION_DMS_RESP 24 +#define DOT11_WNM_ACTION_TMNG_MEASUR_REQ 25 +#define DOT11_WNM_ACTION_NOTFCTN_REQ 26 +#define DOT11_WNM_ACTION_NOTFCTN_RES 27 + + + +BWL_PRE_PACKED_STRUCT struct dot11_bss_trans_query { + uint8 category; + uint8 action; + uint8 token; + uint8 reason; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_bss_trans_query dot11_bss_trans_query_t; +#define DOT11_BSS_TRANS_QUERY_LEN 4 + + +BWL_PRE_PACKED_STRUCT struct dot11_bss_trans_req { + uint8 category; + uint8 action; + uint8 token; + uint8 reqmode; + uint16 disassoc_tmr; + uint8 validity_intrvl; + uint8 data[1]; + +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_bss_trans_req dot11_bss_trans_req_t; +#define DOT11_BSS_TRANS_REQ_LEN 7 + +#define DOT11_BSS_TERM_DUR_LEN 12 + + + +#define DOT11_BSS_TRNS_REQMODE_PREF_LIST_INCL 0x01 +#define DOT11_BSS_TRNS_REQMODE_ABRIDGED 0x02 +#define DOT11_BSS_TRNS_REQMODE_DISASSOC_IMMINENT 0x04 +#define DOT11_BSS_TRNS_REQMODE_BSS_TERM_INCL 0x08 +#define DOT11_BSS_TRNS_REQMODE_ESS_DISASSOC_IMNT 0x10 + + + +BWL_PRE_PACKED_STRUCT struct dot11_bss_trans_res { + uint8 category; + uint8 action; + uint8 token; + uint8 status; + uint8 term_delay; + uint8 data[1]; + +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_bss_trans_res dot11_bss_trans_res_t; +#define DOT11_BSS_TRANS_RES_LEN 5 + + +#define DOT11_BSS_TRNS_RES_STATUS_ACCEPT 0 +#define DOT11_BSS_TRNS_RES_STATUS_REJECT 1 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_INSUFF_BCN 2 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_INSUFF_CAP 3 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_TERM_UNDESIRED 4 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_TERM_DELAY_REQ 5 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_BSS_LIST_PROVIDED 6 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_NO_SUITABLE_BSS 7 +#define DOT11_BSS_TRNS_RES_STATUS_REJ_LEAVING_ESS 8 + + + +#define DOT11_NBR_RPRT_BSSID_INFO_REACHABILTY 0x0003 +#define DOT11_NBR_RPRT_BSSID_INFO_SEC 0x0004 +#define DOT11_NBR_RPRT_BSSID_INFO_KEY_SCOPE 0x0008 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP 0x03f0 + +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_SPEC_MGMT 0x0010 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_QOS 0x0020 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_APSD 0x0040 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_RDIO_MSMT 0x0080 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_DEL_BA 0x0100 +#define DOT11_NBR_RPRT_BSSID_INFO_CAP_IMM_BA 0x0200 + + +#define DOT11_NBR_RPRT_SUBELEM_BSS_CANDDT_PREF_ID 3 +BWL_PRE_PACKED_STRUCT struct dot11_addba_req { + uint8 category; + uint8 action; + uint8 token; + uint16 addba_param_set; + uint16 timeout; + uint16 start_seqnum; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_addba_req dot11_addba_req_t; +#define DOT11_ADDBA_REQ_LEN 9 + +BWL_PRE_PACKED_STRUCT struct dot11_addba_resp { + uint8 category; + uint8 action; + uint8 token; + uint16 status; + uint16 addba_param_set; + uint16 timeout; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_addba_resp dot11_addba_resp_t; +#define DOT11_ADDBA_RESP_LEN 9 + + +#define DOT11_DELBA_PARAM_INIT_MASK 0x0800 +#define DOT11_DELBA_PARAM_INIT_SHIFT 11 +#define DOT11_DELBA_PARAM_TID_MASK 0xf000 +#define DOT11_DELBA_PARAM_TID_SHIFT 12 + +BWL_PRE_PACKED_STRUCT struct dot11_delba { + uint8 category; + uint8 action; + uint16 delba_param_set; + uint16 reason; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_delba dot11_delba_t; +#define DOT11_DELBA_LEN 6 + + +#define SA_QUERY_REQUEST 0 +#define SA_QUERY_RESPONSE 1 + + + + +BWL_PRE_PACKED_STRUCT struct dot11_ft_req { + uint8 category; + uint8 action; + uint8 sta_addr[ETHER_ADDR_LEN]; + uint8 tgt_ap_addr[ETHER_ADDR_LEN]; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_ft_req dot11_ft_req_t; +#define DOT11_FT_REQ_FIXED_LEN 14 + + +BWL_PRE_PACKED_STRUCT struct dot11_ft_res { + uint8 category; + uint8 action; + uint8 sta_addr[ETHER_ADDR_LEN]; + uint8 tgt_ap_addr[ETHER_ADDR_LEN]; + uint16 status; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_ft_res dot11_ft_res_t; +#define DOT11_FT_RES_FIXED_LEN 16 + + +BWL_PRE_PACKED_STRUCT struct dot11_rde_ie { + uint8 id; + uint8 length; + uint8 rde_id; + uint8 rd_count; + uint16 status; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_rde_ie dot11_rde_ie_t; + + +#define DOT11_MNG_RDE_IE_LEN sizeof(dot11_rde_ie_t) + + + + + +#define DOT11_RRM_CAP_LEN 5 +BWL_PRE_PACKED_STRUCT struct dot11_rrm_cap_ie { + uint8 cap[DOT11_RRM_CAP_LEN]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_rrm_cap_ie dot11_rrm_cap_ie_t; + + +#define DOT11_RRM_CAP_LINK 0 +#define DOT11_RRM_CAP_NEIGHBOR_REPORT 1 +#define DOT11_RRM_CAP_PARALLEL 2 +#define DOT11_RRM_CAP_REPEATED 3 +#define DOT11_RRM_CAP_BCN_PASSIVE 4 +#define DOT11_RRM_CAP_BCN_ACTIVE 5 +#define DOT11_RRM_CAP_BCN_TABLE 6 +#define DOT11_RRM_CAP_BCN_REP_COND 7 +#define DOT11_RRM_CAP_AP_CHANREP 16 + + + +#define DOT11_EXT_CAP_LEN 4 +BWL_PRE_PACKED_STRUCT struct dot11_ext_cap_ie { + uint8 cap[DOT11_EXT_CAP_LEN]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_ext_cap_ie dot11_ext_cap_ie_t; + + +#define DOT11_EXT_CAP_BSS_TRANSITION_MGMT 19 + + +#define DOT11_OP_CLASS_NONE 255 + +BWL_PRE_PACKED_STRUCT struct do11_ap_chrep { + uint8 id; + uint8 len; + uint8 reg; + uint8 chanlist[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct do11_ap_chrep dot11_ap_chrep_t; + + +#define DOT11_RM_ACTION_RM_REQ 0 +#define DOT11_RM_ACTION_RM_REP 1 +#define DOT11_RM_ACTION_LM_REQ 2 +#define DOT11_RM_ACTION_LM_REP 3 +#define DOT11_RM_ACTION_NR_REQ 4 +#define DOT11_RM_ACTION_NR_REP 5 + + +BWL_PRE_PACKED_STRUCT struct dot11_rm_action { + uint8 category; + uint8 action; + uint8 token; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_rm_action dot11_rm_action_t; +#define DOT11_RM_ACTION_LEN 3 + +BWL_PRE_PACKED_STRUCT struct dot11_rmreq { + uint8 category; + uint8 action; + uint8 token; + uint16 reps; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_rmreq dot11_rmreq_t; +#define DOT11_RMREQ_LEN 5 + +BWL_PRE_PACKED_STRUCT struct dot11_rm_ie { + uint8 id; + uint8 len; + uint8 token; + uint8 mode; + uint8 type; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_rm_ie dot11_rm_ie_t; +#define DOT11_RM_IE_LEN 5 + + +#define DOT11_RMREQ_MODE_PARALLEL 1 +#define DOT11_RMREQ_MODE_ENABLE 2 +#define DOT11_RMREQ_MODE_REQUEST 4 +#define DOT11_RMREQ_MODE_REPORT 8 +#define DOT11_RMREQ_MODE_DURMAND 0x10 + + +#define DOT11_RMREP_MODE_LATE 1 +#define DOT11_RMREP_MODE_INCAPABLE 2 +#define DOT11_RMREP_MODE_REFUSED 4 + +BWL_PRE_PACKED_STRUCT struct dot11_rmreq_bcn { + uint8 id; + uint8 len; + uint8 token; + uint8 mode; + uint8 type; + uint8 reg; + uint8 channel; + uint16 interval; + uint16 duration; + uint8 bcn_mode; + struct ether_addr bssid; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_rmreq_bcn dot11_rmreq_bcn_t; +#define DOT11_RMREQ_BCN_LEN 18 + +BWL_PRE_PACKED_STRUCT struct dot11_rmrep_bcn { + uint8 reg; + uint8 channel; + uint32 starttime[2]; + uint16 duration; + uint8 frame_info; + uint8 rcpi; + uint8 rsni; + struct ether_addr bssid; + uint8 antenna_id; + uint32 parent_tsf; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_rmrep_bcn dot11_rmrep_bcn_t; +#define DOT11_RMREP_BCN_LEN 26 + + +#define DOT11_RMREQ_BCN_PASSIVE 0 +#define DOT11_RMREQ_BCN_ACTIVE 1 +#define DOT11_RMREQ_BCN_TABLE 2 + + +#define DOT11_RMREQ_BCN_SSID_ID 0 +#define DOT11_RMREQ_BCN_REPINFO_ID 1 +#define DOT11_RMREQ_BCN_REPDET_ID 2 +#define DOT11_RMREQ_BCN_REQUEST_ID 10 +#define DOT11_RMREQ_BCN_APCHREP_ID DOT11_MNG_AP_CHREP_ID + + +#define DOT11_RMREQ_BCN_REPDET_FIXED 0 +#define DOT11_RMREQ_BCN_REPDET_REQUEST 1 +#define DOT11_RMREQ_BCN_REPDET_ALL 2 + + +#define DOT11_RMREP_BCN_FRM_BODY 1 + + +BWL_PRE_PACKED_STRUCT struct dot11_rmrep_nbr { + struct ether_addr bssid; + uint32 bssid_info; + uint8 reg; + uint8 channel; + uint8 phytype; + uchar sub_elements[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_rmrep_nbr dot11_rmrep_nbr_t; +#define DOT11_RMREP_NBR_LEN 13 + + +#define DOT11_BSSTYPE_INFRASTRUCTURE 0 +#define DOT11_BSSTYPE_INDEPENDENT 1 +#define DOT11_BSSTYPE_ANY 2 +#define DOT11_SCANTYPE_ACTIVE 0 +#define DOT11_SCANTYPE_PASSIVE 1 + + +BWL_PRE_PACKED_STRUCT struct dot11_lmreq { + uint8 category; + uint8 action; + uint8 token; + uint8 txpwr; + uint8 maxtxpwr; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_lmreq dot11_lmreq_t; +#define DOT11_LMREQ_LEN 5 + +BWL_PRE_PACKED_STRUCT struct dot11_lmrep { + uint8 category; + uint8 action; + uint8 token; + dot11_tpc_rep_t tpc; + uint8 rxant; + uint8 txant; + uint8 rcpi; + uint8 rsni; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_lmrep dot11_lmrep_t; +#define DOT11_LMREP_LEN 11 + + +#define PREN_PREAMBLE 24 +#define PREN_MM_EXT 12 +#define PREN_PREAMBLE_EXT 4 + + +#define RIFS_11N_TIME 2 + + + +#define HT_SIG1_MCS_MASK 0x00007F +#define HT_SIG1_CBW 0x000080 +#define HT_SIG1_HT_LENGTH 0xFFFF00 + + +#define HT_SIG2_SMOOTHING 0x000001 +#define HT_SIG2_NOT_SOUNDING 0x000002 +#define HT_SIG2_RESERVED 0x000004 +#define HT_SIG2_AGGREGATION 0x000008 +#define HT_SIG2_STBC_MASK 0x000030 +#define HT_SIG2_STBC_SHIFT 4 +#define HT_SIG2_FEC_CODING 0x000040 +#define HT_SIG2_SHORT_GI 0x000080 +#define HT_SIG2_ESS_MASK 0x000300 +#define HT_SIG2_ESS_SHIFT 8 +#define HT_SIG2_CRC 0x03FC00 +#define HT_SIG2_TAIL 0x1C0000 + + +#define APHY_SLOT_TIME 9 +#define APHY_SIFS_TIME 16 +#define APHY_DIFS_TIME (APHY_SIFS_TIME + (2 * APHY_SLOT_TIME)) +#define APHY_PREAMBLE_TIME 16 +#define APHY_SIGNAL_TIME 4 +#define APHY_SYMBOL_TIME 4 +#define APHY_SERVICE_NBITS 16 +#define APHY_TAIL_NBITS 6 +#define APHY_CWMIN 15 + + +#define BPHY_SLOT_TIME 20 +#define BPHY_SIFS_TIME 10 +#define BPHY_DIFS_TIME 50 +#define BPHY_PLCP_TIME 192 +#define BPHY_PLCP_SHORT_TIME 96 +#define BPHY_CWMIN 31 + + +#define DOT11_OFDM_SIGNAL_EXTENSION 6 + +#define PHY_CWMAX 1023 + +#define DOT11_MAXNUMFRAGS 16 + + +typedef struct d11cnt { + uint32 txfrag; + uint32 txmulti; + uint32 txfail; + uint32 txretry; + uint32 txretrie; + uint32 rxdup; + uint32 txrts; + uint32 txnocts; + uint32 txnoack; + uint32 rxfrag; + uint32 rxmulti; + uint32 rxcrc; + uint32 txfrmsnt; + uint32 rxundec; +} d11cnt_t; + + +#define BRCM_PROP_OUI "\x00\x90\x4C" + + + +#define BRCM_OUI "\x00\x10\x18" + + +BWL_PRE_PACKED_STRUCT struct brcm_ie { + uint8 id; + uint8 len; + uint8 oui[3]; + uint8 ver; + uint8 assoc; + uint8 flags; + uint8 flags1; + uint16 amsdu_mtu_pref; +} BWL_POST_PACKED_STRUCT; +typedef struct brcm_ie brcm_ie_t; +#define BRCM_IE_LEN 11 +#define BRCM_IE_VER 2 +#define BRCM_IE_LEGACY_AES_VER 1 + + +#ifdef WLAFTERBURNER +#define BRF_ABCAP 0x1 +#define BRF_ABRQRD 0x2 +#define BRF_ABCOUNTER_MASK 0xf0 +#define BRF_ABCOUNTER_SHIFT 4 +#endif +#define BRF_LZWDS 0x4 +#define BRF_BLOCKACK 0x8 + + +#define BRF1_AMSDU 0x1 +#define BRF1_WMEPS 0x4 +#define BRF1_PSOFIX 0x8 +#define BRF1_RX_LARGE_AGG 0x10 +#define BRF1_SOFTAP 0x40 + +#ifdef WLAFTERBURNER +#define AB_WDS_TIMEOUT_MAX 15 +#define AB_WDS_TIMEOUT_MIN 1 +#endif + +#define AB_GUARDCOUNT 10 + + +BWL_PRE_PACKED_STRUCT struct vndr_ie { + uchar id; + uchar len; + uchar oui [3]; + uchar data [1]; +} BWL_POST_PACKED_STRUCT; +typedef struct vndr_ie vndr_ie_t; + +#define VNDR_IE_HDR_LEN 2 +#define VNDR_IE_MIN_LEN 3 +#define VNDR_IE_MAX_LEN 256 + + +#define MCSSET_LEN 16 +#define MAX_MCS_NUM (128) + +BWL_PRE_PACKED_STRUCT struct ht_cap_ie { + uint16 cap; + uint8 params; + uint8 supp_mcs[MCSSET_LEN]; + uint16 ext_htcap; + uint32 txbf_cap; + uint8 as_cap; +} BWL_POST_PACKED_STRUCT; +typedef struct ht_cap_ie ht_cap_ie_t; + + + +BWL_PRE_PACKED_STRUCT struct ht_prop_cap_ie { + uint8 id; + uint8 len; + uint8 oui[3]; + uint8 type; + ht_cap_ie_t cap_ie; +} BWL_POST_PACKED_STRUCT; +typedef struct ht_prop_cap_ie ht_prop_cap_ie_t; + +#define HT_PROP_IE_OVERHEAD 4 +#define HT_CAP_IE_LEN 26 +#define HT_CAP_IE_TYPE 51 + +#define HT_CAP_LDPC_CODING 0x0001 +#define HT_CAP_40MHZ 0x0002 +#define HT_CAP_MIMO_PS_MASK 0x000C +#define HT_CAP_MIMO_PS_SHIFT 0x0002 +#define HT_CAP_MIMO_PS_OFF 0x0003 +#define HT_CAP_MIMO_PS_RTS 0x0001 +#define HT_CAP_MIMO_PS_ON 0x0000 +#define HT_CAP_GF 0x0010 +#define HT_CAP_SHORT_GI_20 0x0020 +#define HT_CAP_SHORT_GI_40 0x0040 +#define HT_CAP_TX_STBC 0x0080 +#define HT_CAP_RX_STBC_MASK 0x0300 +#define HT_CAP_RX_STBC_SHIFT 8 +#define HT_CAP_DELAYED_BA 0x0400 +#define HT_CAP_MAX_AMSDU 0x0800 +#define HT_CAP_DSSS_CCK 0x1000 +#define HT_CAP_PSMP 0x2000 +#define HT_CAP_40MHZ_INTOLERANT 0x4000 +#define HT_CAP_LSIG_TXOP 0x8000 + +#define HT_CAP_RX_STBC_NO 0x0 +#define HT_CAP_RX_STBC_ONE_STREAM 0x1 +#define HT_CAP_RX_STBC_TWO_STREAM 0x2 +#define HT_CAP_RX_STBC_THREE_STREAM 0x3 + +#define HT_MAX_AMSDU 7935 +#define HT_MIN_AMSDU 3835 + +#define HT_PARAMS_RX_FACTOR_MASK 0x03 +#define HT_PARAMS_DENSITY_MASK 0x1C +#define HT_PARAMS_DENSITY_SHIFT 2 + + +#define AMPDU_MAX_MPDU_DENSITY 7 +#define AMPDU_RX_FACTOR_8K 0 +#define AMPDU_RX_FACTOR_16K 1 +#define AMPDU_RX_FACTOR_32K 2 +#define AMPDU_RX_FACTOR_64K 3 +#define AMPDU_RX_FACTOR_BASE 8*1024 + +#define AMPDU_DELIMITER_LEN 4 +#define AMPDU_DELIMITER_LEN_MAX 63 + +BWL_PRE_PACKED_STRUCT struct ht_add_ie { + uint8 ctl_ch; + uint8 byte1; + uint16 opmode; + uint16 misc_bits; + uint8 basic_mcs[MCSSET_LEN]; +} BWL_POST_PACKED_STRUCT; +typedef struct ht_add_ie ht_add_ie_t; + + + +BWL_PRE_PACKED_STRUCT struct ht_prop_add_ie { + uint8 id; + uint8 len; + uint8 oui[3]; + uint8 type; + ht_add_ie_t add_ie; +} BWL_POST_PACKED_STRUCT; +typedef struct ht_prop_add_ie ht_prop_add_ie_t; + +#define HT_ADD_IE_LEN 22 +#define HT_ADD_IE_TYPE 52 + + +#define HT_BW_ANY 0x04 +#define HT_RIFS_PERMITTED 0x08 + + +#define HT_OPMODE_MASK 0x0003 +#define HT_OPMODE_SHIFT 0 +#define HT_OPMODE_PURE 0x0000 +#define HT_OPMODE_OPTIONAL 0x0001 +#define HT_OPMODE_HT20IN40 0x0002 +#define HT_OPMODE_MIXED 0x0003 +#define HT_OPMODE_NONGF 0x0004 +#define DOT11N_TXBURST 0x0008 +#define DOT11N_OBSS_NONHT 0x0010 + + +#define HT_BASIC_STBC_MCS 0x007f +#define HT_DUAL_STBC_PROT 0x0080 +#define HT_SECOND_BCN 0x0100 +#define HT_LSIG_TXOP 0x0200 +#define HT_PCO_ACTIVE 0x0400 +#define HT_PCO_PHASE 0x0800 + + +#define DOT11N_2G_TXBURST_LIMIT 6160 +#define DOT11N_5G_TXBURST_LIMIT 3080 + + +#define GET_HT_OPMODE(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \ + >> HT_OPMODE_SHIFT) +#define HT_MIXEDMODE_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \ + == HT_OPMODE_MIXED) +#define HT_HT20_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \ + == HT_OPMODE_HT20IN40) +#define HT_OPTIONAL_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_MASK) \ + == HT_OPMODE_OPTIONAL) +#define HT_USE_PROTECTION(add_ie) (HT_HT20_PRESENT((add_ie)) || \ + HT_MIXEDMODE_PRESENT((add_ie))) +#define HT_NONGF_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & HT_OPMODE_NONGF) \ + == HT_OPMODE_NONGF) +#define DOT11N_TXBURST_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & DOT11N_TXBURST) \ + == DOT11N_TXBURST) +#define DOT11N_OBSS_NONHT_PRESENT(add_ie) ((ltoh16_ua(&add_ie->opmode) & DOT11N_OBSS_NONHT) \ + == DOT11N_OBSS_NONHT) + +BWL_PRE_PACKED_STRUCT struct obss_params { + uint16 passive_dwell; + uint16 active_dwell; + uint16 bss_widthscan_interval; + uint16 passive_total; + uint16 active_total; + uint16 chanwidth_transition_dly; + uint16 activity_threshold; +} BWL_POST_PACKED_STRUCT; +typedef struct obss_params obss_params_t; + +BWL_PRE_PACKED_STRUCT struct dot11_obss_ie { + uint8 id; + uint8 len; + obss_params_t obss_params; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_obss_ie dot11_obss_ie_t; +#define DOT11_OBSS_SCAN_IE_LEN sizeof(obss_params_t) + + +#define HT_CTRL_LA_TRQ 0x00000002 +#define HT_CTRL_LA_MAI 0x0000003C +#define HT_CTRL_LA_MAI_SHIFT 2 +#define HT_CTRL_LA_MAI_MRQ 0x00000004 +#define HT_CTRL_LA_MAI_MSI 0x00000038 +#define HT_CTRL_LA_MFSI 0x000001C0 +#define HT_CTRL_LA_MFSI_SHIFT 6 +#define HT_CTRL_LA_MFB_ASELC 0x0000FE00 +#define HT_CTRL_LA_MFB_ASELC_SH 9 +#define HT_CTRL_LA_ASELC_CMD 0x00000C00 +#define HT_CTRL_LA_ASELC_DATA 0x0000F000 +#define HT_CTRL_CAL_POS 0x00030000 +#define HT_CTRL_CAL_SEQ 0x000C0000 +#define HT_CTRL_CSI_STEERING 0x00C00000 +#define HT_CTRL_CSI_STEER_SHIFT 22 +#define HT_CTRL_CSI_STEER_NFB 0 +#define HT_CTRL_CSI_STEER_CSI 1 +#define HT_CTRL_CSI_STEER_NCOM 2 +#define HT_CTRL_CSI_STEER_COM 3 +#define HT_CTRL_NDP_ANNOUNCE 0x01000000 +#define HT_CTRL_AC_CONSTRAINT 0x40000000 +#define HT_CTRL_RDG_MOREPPDU 0x80000000 + +#define HT_OPMODE_OPTIONAL 0x0001 +#define HT_OPMODE_HT20IN40 0x0002 +#define HT_OPMODE_MIXED 0x0003 +#define HT_OPMODE_NONGF 0x0004 +#define DOT11N_TXBURST 0x0008 +#define DOT11N_OBSS_NONHT 0x0010 + + + +#define WPA_OUI "\x00\x50\xF2" +#define WPA_OUI_LEN 3 +#define WPA_OUI_TYPE 1 +#define WPA_VERSION 1 +#define WPA2_OUI "\x00\x0F\xAC" +#define WPA2_OUI_LEN 3 +#define WPA2_VERSION 1 +#define WPA2_VERSION_LEN 2 + + +#define WPS_OUI "\x00\x50\xF2" +#define WPS_OUI_LEN 3 +#define WPS_OUI_TYPE 4 + + +#define WFA_OUI "\x50\x6F\x9A" +#define WFA_OUI_LEN 3 + +#define WFA_OUI_TYPE_WPA 1 +#define WFA_OUI_TYPE_WPS 4 +#define WFA_OUI_TYPE_TPC 8 +#define WFA_OUI_TYPE_P2P 9 + + +#define RSN_AKM_NONE 0 +#define RSN_AKM_UNSPECIFIED 1 +#define RSN_AKM_PSK 2 +#define RSN_AKM_FBT_1X 3 +#define RSN_AKM_FBT_PSK 4 +#define RSN_AKM_MFP_1X 5 +#define RSN_AKM_MFP_PSK 6 +#define RSN_AKM_TPK 7 + + +#define DOT11_MAX_DEFAULT_KEYS 4 +#define DOT11_MAX_KEY_SIZE 32 +#define DOT11_MAX_IV_SIZE 16 +#define DOT11_EXT_IV_FLAG (1<<5) +#define DOT11_WPA_KEY_RSC_LEN 8 + +#define WEP1_KEY_SIZE 5 +#define WEP1_KEY_HEX_SIZE 10 +#define WEP128_KEY_SIZE 13 +#define WEP128_KEY_HEX_SIZE 26 +#define TKIP_MIC_SIZE 8 +#define TKIP_EOM_SIZE 7 +#define TKIP_EOM_FLAG 0x5a +#define TKIP_KEY_SIZE 32 +#define TKIP_MIC_AUTH_TX 16 +#define TKIP_MIC_AUTH_RX 24 +#define TKIP_MIC_SUP_RX TKIP_MIC_AUTH_TX +#define TKIP_MIC_SUP_TX TKIP_MIC_AUTH_RX +#define AES_KEY_SIZE 16 +#define AES_MIC_SIZE 8 + + +#define WCN_OUI "\x00\x50\xf2" +#define WCN_TYPE 4 + + + + + +BWL_PRE_PACKED_STRUCT struct dot11_mdid_ie { + uint8 id; + uint8 len; + uint16 mdid; + uint8 cap; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_mdid_ie dot11_mdid_ie_t; + +#define FBT_MDID_CAP_OVERDS 0x01 +#define FBT_MDID_CAP_RRP 0x02 + + +BWL_PRE_PACKED_STRUCT struct dot11_ft_ie { + uint8 id; + uint8 len; + uint16 mic_control; + uint8 mic[16]; + uint8 anonce[32]; + uint8 snonce[32]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_ft_ie dot11_ft_ie_t; + + +BWL_PRE_PACKED_STRUCT struct dot11_gtk_ie { + uint8 id; + uint8 len; + uint16 key_info; + uint8 key_len; + uint8 rsc[8]; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT; +typedef struct dot11_gtk_ie dot11_gtk_ie_t; + +#define BSSID_INVALID "\x00\x00\x00\x00\x00\x00" +#define BSSID_BROADCAST "\xFF\xFF\xFF\xFF\xFF\xFF" + + + +BWL_PRE_PACKED_STRUCT struct link_id_ie { + uint8 id; + uint8 len; + struct ether_addr bssid; + struct ether_addr tdls_init_mac; + struct ether_addr tdls_resp_mac; +} BWL_POST_PACKED_STRUCT; +typedef struct link_id_ie link_id_ie_t; +#define TDLS_LINK_ID_IE_LEN 18 + + +BWL_PRE_PACKED_STRUCT struct wakeup_sch_ie { + uint8 id; + uint8 len; + uint32 offset; + uint32 interval; + uint32 awake_win_slots; + uint32 max_wake_win; + uint16 idle_cnt; +} BWL_POST_PACKED_STRUCT; +typedef struct wakeup_sch_ie wakeup_sch_ie_t; +#define TDLS_WAKEUP_SCH_IE_LEN 18 + + +BWL_PRE_PACKED_STRUCT struct channel_switch_timing_ie { + uint8 id; + uint8 len; + uint16 switch_time; + uint16 switch_timeout; +} BWL_POST_PACKED_STRUCT; +typedef struct channel_switch_timing_ie channel_switch_timing_ie_t; +#define TDLS_CHANNEL_SWITCH_TIMING_IE_LEN 4 + + +BWL_PRE_PACKED_STRUCT struct pti_control_ie { + uint8 id; + uint8 len; + uint8 tid; + uint16 seq_control; +} BWL_POST_PACKED_STRUCT; +typedef struct pti_control_ie pti_control_ie_t; +#define TDLS_PTI_CONTROL_IE_LEN 3 + + +BWL_PRE_PACKED_STRUCT struct pu_buffer_status_ie { + uint8 id; + uint8 len; + uint8 status; +} BWL_POST_PACKED_STRUCT; +typedef struct pu_buffer_status_ie pu_buffer_status_ie_t; +#define TDLS_PU_BUFFER_STATUS_IE_LEN 1 +#define TDLS_PU_BUFFER_STATUS_AC_BK 1 +#define TDLS_PU_BUFFER_STATUS_AC_BE 2 +#define TDLS_PU_BUFFER_STATUS_AC_VI 4 +#define TDLS_PU_BUFFER_STATUS_AC_VO 8 + + +#include + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h b/drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h new file mode 100644 index 0000000000000..cbdd05e624bcc --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/802.11_bta.h @@ -0,0 +1,45 @@ +/* + * BT-AMP (BlueTooth Alternate Mac and Phy) 802.11 PAL (Protocol Adaptation Layer) + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: 802.11_bta.h 277737 2011-08-16 17:54:59Z $ +*/ + +#ifndef _802_11_BTA_H_ +#define _802_11_BTA_H_ + +#define BT_SIG_SNAP_MPROT "\xAA\xAA\x03\x00\x19\x58" + +/* BT-AMP 802.11 PAL Protocols */ +#define BTA_PROT_L2CAP 1 +#define BTA_PROT_ACTIVITY_REPORT 2 +#define BTA_PROT_SECURITY 3 +#define BTA_PROT_LINK_SUPERVISION_REQUEST 4 +#define BTA_PROT_LINK_SUPERVISION_REPLY 5 + +/* BT-AMP 802.11 PAL AMP_ASSOC Type IDs */ +#define BTA_TYPE_ID_MAC_ADDRESS 1 +#define BTA_TYPE_ID_PREFERRED_CHANNELS 2 +#define BTA_TYPE_ID_CONNECTED_CHANNELS 3 +#define BTA_TYPE_ID_CAPABILITIES 4 +#define BTA_TYPE_ID_VERSION 5 +#endif /* _802_11_bta_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.11e.h b/drivers/net/wireless/bcmdhd/include/proto/802.11e.h new file mode 100644 index 0000000000000..0e070a475b64c --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/802.11e.h @@ -0,0 +1,131 @@ +/* + * 802.11e protocol header file + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: 802.11e.h 277737 2011-08-16 17:54:59Z $ + */ + +#ifndef _802_11e_H_ +#define _802_11e_H_ + +#ifndef _TYPEDEFS_H_ +#include +#endif + +/* This marks the start of a packed structure section. */ +#include + + +/* WME Traffic Specification (TSPEC) element */ +#define WME_TSPEC_HDR_LEN 2 /* WME TSPEC header length */ +#define WME_TSPEC_BODY_OFF 2 /* WME TSPEC body offset */ + +#define WME_CATEGORY_CODE_OFFSET 0 /* WME Category code offset */ +#define WME_ACTION_CODE_OFFSET 1 /* WME Action code offset */ +#define WME_TOKEN_CODE_OFFSET 2 /* WME Token code offset */ +#define WME_STATUS_CODE_OFFSET 3 /* WME Status code offset */ + +BWL_PRE_PACKED_STRUCT struct tsinfo { + uint8 octets[3]; +} BWL_POST_PACKED_STRUCT; + +typedef struct tsinfo tsinfo_t; + +/* 802.11e TSPEC IE */ +typedef BWL_PRE_PACKED_STRUCT struct tspec { + uint8 oui[DOT11_OUI_LEN]; /* WME_OUI */ + uint8 type; /* WME_TYPE */ + uint8 subtype; /* WME_SUBTYPE_TSPEC */ + uint8 version; /* WME_VERSION */ + tsinfo_t tsinfo; /* TS Info bit field */ + uint16 nom_msdu_size; /* (Nominal or fixed) MSDU Size (bytes) */ + uint16 max_msdu_size; /* Maximum MSDU Size (bytes) */ + uint32 min_srv_interval; /* Minimum Service Interval (us) */ + uint32 max_srv_interval; /* Maximum Service Interval (us) */ + uint32 inactivity_interval; /* Inactivity Interval (us) */ + uint32 suspension_interval; /* Suspension Interval (us) */ + uint32 srv_start_time; /* Service Start Time (us) */ + uint32 min_data_rate; /* Minimum Data Rate (bps) */ + uint32 mean_data_rate; /* Mean Data Rate (bps) */ + uint32 peak_data_rate; /* Peak Data Rate (bps) */ + uint32 max_burst_size; /* Maximum Burst Size (bytes) */ + uint32 delay_bound; /* Delay Bound (us) */ + uint32 min_phy_rate; /* Minimum PHY Rate (bps) */ + uint16 surplus_bw; /* Surplus Bandwidth Allowance (range 1.0-8.0) */ + uint16 medium_time; /* Medium Time (32 us/s periods) */ +} BWL_POST_PACKED_STRUCT tspec_t; + +#define WME_TSPEC_LEN (sizeof(tspec_t)) /* not including 2-bytes of header */ + +/* ts_info */ +/* 802.1D priority is duplicated - bits 13-11 AND bits 3-1 */ +#define TS_INFO_TID_SHIFT 1 /* TS info. TID shift */ +#define TS_INFO_TID_MASK (0xf << TS_INFO_TID_SHIFT) /* TS info. TID mask */ +#define TS_INFO_CONTENTION_SHIFT 7 /* TS info. contention shift */ +#define TS_INFO_CONTENTION_MASK (0x1 << TS_INFO_CONTENTION_SHIFT) /* TS info. contention mask */ +#define TS_INFO_DIRECTION_SHIFT 5 /* TS info. direction shift */ +#define TS_INFO_DIRECTION_MASK (0x3 << TS_INFO_DIRECTION_SHIFT) /* TS info. direction mask */ +#define TS_INFO_PSB_SHIFT 2 /* TS info. PSB bit Shift */ +#define TS_INFO_PSB_MASK (1 << TS_INFO_PSB_SHIFT) /* TS info. PSB mask */ +#define TS_INFO_UPLINK (0 << TS_INFO_DIRECTION_SHIFT) /* TS info. uplink */ +#define TS_INFO_DOWNLINK (1 << TS_INFO_DIRECTION_SHIFT) /* TS info. downlink */ +#define TS_INFO_BIDIRECTIONAL (3 << TS_INFO_DIRECTION_SHIFT) /* TS info. bidirectional */ +#define TS_INFO_USER_PRIO_SHIFT 3 /* TS info. user priority shift */ +/* TS info. user priority mask */ +#define TS_INFO_USER_PRIO_MASK (0x7 << TS_INFO_USER_PRIO_SHIFT) + +/* Macro to get/set bit(s) field in TSINFO */ +#define WLC_CAC_GET_TID(pt) ((((pt).octets[0]) & TS_INFO_TID_MASK) >> TS_INFO_TID_SHIFT) +#define WLC_CAC_GET_DIR(pt) ((((pt).octets[0]) & \ + TS_INFO_DIRECTION_MASK) >> TS_INFO_DIRECTION_SHIFT) +#define WLC_CAC_GET_PSB(pt) ((((pt).octets[1]) & TS_INFO_PSB_MASK) >> TS_INFO_PSB_SHIFT) +#define WLC_CAC_GET_USER_PRIO(pt) ((((pt).octets[1]) & \ + TS_INFO_USER_PRIO_MASK) >> TS_INFO_USER_PRIO_SHIFT) + +#define WLC_CAC_SET_TID(pt, id) ((((pt).octets[0]) & (~TS_INFO_TID_MASK)) | \ + ((id) << TS_INFO_TID_SHIFT)) +#define WLC_CAC_SET_USER_PRIO(pt, prio) ((((pt).octets[0]) & (~TS_INFO_USER_PRIO_MASK)) | \ + ((prio) << TS_INFO_USER_PRIO_SHIFT)) + +/* 802.11e QBSS Load IE */ +#define QBSS_LOAD_IE_LEN 5 /* QBSS Load IE length */ +#define QBSS_LOAD_AAC_OFF 3 /* AAC offset in IE */ + +#define CAC_ADDTS_RESP_TIMEOUT 300 /* default ADDTS response timeout in ms */ + +/* 802.11e ADDTS status code */ +#define DOT11E_STATUS_ADMISSION_ACCEPTED 0 /* TSPEC Admission accepted status */ +#define DOT11E_STATUS_ADDTS_INVALID_PARAM 1 /* TSPEC invalid parameter status */ +#define DOT11E_STATUS_ADDTS_REFUSED_NSBW 3 /* ADDTS refused (non-sufficient BW) */ +#define DOT11E_STATUS_ADDTS_REFUSED_AWHILE 47 /* ADDTS refused but could retry later */ + +/* 802.11e DELTS status code */ +#define DOT11E_STATUS_QSTA_LEAVE_QBSS 36 /* STA leave QBSS */ +#define DOT11E_STATUS_END_TS 37 /* END TS */ +#define DOT11E_STATUS_UNKNOWN_TS 38 /* UNKNOWN TS */ +#define DOT11E_STATUS_QSTA_REQ_TIMEOUT 39 /* STA ADDTS request timeout */ + + +/* This marks the end of a packed structure section. */ +#include + +#endif /* _802_11e_CAC_H_ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/802.1d.h b/drivers/net/wireless/bcmdhd/include/proto/802.1d.h new file mode 100644 index 0000000000000..c7e07bd5e7c3f --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/802.1d.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * Fundamental types and constants relating to 802.1D + * + * $Id: 802.1d.h 277737 2011-08-16 17:54:59Z $ + */ + + +#ifndef _802_1_D_ +#define _802_1_D_ + + +#define PRIO_8021D_NONE 2 +#define PRIO_8021D_BK 1 +#define PRIO_8021D_BE 0 +#define PRIO_8021D_EE 3 +#define PRIO_8021D_CL 4 +#define PRIO_8021D_VI 5 +#define PRIO_8021D_VO 6 +#define PRIO_8021D_NC 7 +#define MAXPRIO 7 +#define NUMPRIO (MAXPRIO + 1) + +#define ALLPRIO -1 + + +#define PRIO2PREC(prio) \ + (((prio) == PRIO_8021D_NONE || (prio) == PRIO_8021D_BE) ? ((prio^2)) : (prio)) + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h b/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h new file mode 100644 index 0000000000000..0f75d3c8f1d60 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h @@ -0,0 +1,83 @@ +/* + * Broadcom Ethernettype protocol definitions + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bcmeth.h 277737 2011-08-16 17:54:59Z $ + */ + + + + +#ifndef _BCMETH_H_ +#define _BCMETH_H_ + +#ifndef _TYPEDEFS_H_ +#include +#endif + + +#include + + + + + + + +#define BCMILCP_SUBTYPE_RATE 1 +#define BCMILCP_SUBTYPE_LINK 2 +#define BCMILCP_SUBTYPE_CSA 3 +#define BCMILCP_SUBTYPE_LARQ 4 +#define BCMILCP_SUBTYPE_VENDOR 5 +#define BCMILCP_SUBTYPE_FLH 17 + +#define BCMILCP_SUBTYPE_VENDOR_LONG 32769 +#define BCMILCP_SUBTYPE_CERT 32770 +#define BCMILCP_SUBTYPE_SES 32771 + + +#define BCMILCP_BCM_SUBTYPE_RESERVED 0 +#define BCMILCP_BCM_SUBTYPE_EVENT 1 +#define BCMILCP_BCM_SUBTYPE_SES 2 + + +#define BCMILCP_BCM_SUBTYPE_DPT 4 + +#define BCMILCP_BCM_SUBTYPEHDR_MINLENGTH 8 +#define BCMILCP_BCM_SUBTYPEHDR_VERSION 0 + + +typedef BWL_PRE_PACKED_STRUCT struct bcmeth_hdr +{ + uint16 subtype; + uint16 length; + uint8 version; + uint8 oui[3]; + + uint16 usr_subtype; +} BWL_POST_PACKED_STRUCT bcmeth_hdr_t; + + + +#include + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h b/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h new file mode 100644 index 0000000000000..e8c2387dd10bb --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h @@ -0,0 +1,317 @@ +/* + * Broadcom Event protocol definitions + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * Dependencies: proto/bcmeth.h + * + * $Id: bcmevent.h 288077 2011-10-06 00:08:47Z $ + * + */ + + + + +#ifndef _BCMEVENT_H_ +#define _BCMEVENT_H_ + +#ifndef _TYPEDEFS_H_ +#include +#endif + + +#include + +#define BCM_EVENT_MSG_VERSION 2 +#define BCM_MSG_IFNAME_MAX 16 + + +#define WLC_EVENT_MSG_LINK 0x01 +#define WLC_EVENT_MSG_FLUSHTXQ 0x02 +#define WLC_EVENT_MSG_GROUP 0x04 +#define WLC_EVENT_MSG_UNKBSS 0x08 +#define WLC_EVENT_MSG_UNKIF 0x10 + + + + +typedef BWL_PRE_PACKED_STRUCT struct +{ + uint16 version; + uint16 flags; + uint32 event_type; + uint32 status; + uint32 reason; + uint32 auth_type; + uint32 datalen; + struct ether_addr addr; + char ifname[BCM_MSG_IFNAME_MAX]; +} BWL_POST_PACKED_STRUCT wl_event_msg_v1_t; + + +typedef BWL_PRE_PACKED_STRUCT struct +{ + uint16 version; + uint16 flags; + uint32 event_type; + uint32 status; + uint32 reason; + uint32 auth_type; + uint32 datalen; + struct ether_addr addr; + char ifname[BCM_MSG_IFNAME_MAX]; + uint8 ifidx; + uint8 bsscfgidx; +} BWL_POST_PACKED_STRUCT wl_event_msg_t; + + +typedef BWL_PRE_PACKED_STRUCT struct bcm_event { + struct ether_header eth; + bcmeth_hdr_t bcm_hdr; + wl_event_msg_t event; + +} BWL_POST_PACKED_STRUCT bcm_event_t; + +#define BCM_MSG_LEN (sizeof(bcm_event_t) - sizeof(bcmeth_hdr_t) - sizeof(struct ether_header)) + + +#define WLC_E_SET_SSID 0 +#define WLC_E_JOIN 1 +#define WLC_E_START 2 +#define WLC_E_AUTH 3 +#define WLC_E_AUTH_IND 4 +#define WLC_E_DEAUTH 5 +#define WLC_E_DEAUTH_IND 6 +#define WLC_E_ASSOC 7 +#define WLC_E_ASSOC_IND 8 +#define WLC_E_REASSOC 9 +#define WLC_E_REASSOC_IND 10 +#define WLC_E_DISASSOC 11 +#define WLC_E_DISASSOC_IND 12 +#define WLC_E_QUIET_START 13 +#define WLC_E_QUIET_END 14 +#define WLC_E_BEACON_RX 15 +#define WLC_E_LINK 16 +#define WLC_E_MIC_ERROR 17 +#define WLC_E_NDIS_LINK 18 +#define WLC_E_ROAM 19 +#define WLC_E_TXFAIL 20 +#define WLC_E_PMKID_CACHE 21 +#define WLC_E_RETROGRADE_TSF 22 +#define WLC_E_PRUNE 23 +#define WLC_E_AUTOAUTH 24 +#define WLC_E_EAPOL_MSG 25 +#define WLC_E_SCAN_COMPLETE 26 +#define WLC_E_ADDTS_IND 27 +#define WLC_E_DELTS_IND 28 +#define WLC_E_BCNSENT_IND 29 +#define WLC_E_BCNRX_MSG 30 +#define WLC_E_BCNLOST_MSG 31 +#define WLC_E_ROAM_PREP 32 +#define WLC_E_PFN_NET_FOUND 33 +#define WLC_E_PFN_NET_LOST 34 +#define WLC_E_RESET_COMPLETE 35 +#define WLC_E_JOIN_START 36 +#define WLC_E_ROAM_START 37 +#define WLC_E_ASSOC_START 38 +#define WLC_E_IBSS_ASSOC 39 +#define WLC_E_RADIO 40 +#define WLC_E_PSM_WATCHDOG 41 +#define WLC_E_PROBREQ_MSG 44 +#define WLC_E_SCAN_CONFIRM_IND 45 +#define WLC_E_PSK_SUP 46 +#define WLC_E_COUNTRY_CODE_CHANGED 47 +#define WLC_E_EXCEEDED_MEDIUM_TIME 48 +#define WLC_E_ICV_ERROR 49 +#define WLC_E_UNICAST_DECODE_ERROR 50 +#define WLC_E_MULTICAST_DECODE_ERROR 51 +#define WLC_E_TRACE 52 +#define WLC_E_BTA_HCI_EVENT 53 +#define WLC_E_IF 54 +#ifdef WLP2P +#define WLC_E_P2P_DISC_LISTEN_COMPLETE 55 +#endif +#define WLC_E_RSSI 56 +#define WLC_E_PFN_SCAN_COMPLETE 57 +#define WLC_E_EXTLOG_MSG 58 +#define WLC_E_ACTION_FRAME 59 +#define WLC_E_ACTION_FRAME_COMPLETE 60 +#define WLC_E_PRE_ASSOC_IND 61 +#define WLC_E_PRE_REASSOC_IND 62 +#define WLC_E_CHANNEL_ADOPTED 63 +#define WLC_E_AP_STARTED 64 +#define WLC_E_DFS_AP_STOP 65 +#define WLC_E_DFS_AP_RESUME 66 +#define WLC_E_WAI_STA_EVENT 67 +#define WLC_E_WAI_MSG 68 +#define WLC_E_ESCAN_RESULT 69 +#define WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE 70 +#if defined(WLP2P) +#define WLC_E_PROBRESP_MSG 71 +#define WLC_E_P2P_PROBREQ_MSG 72 +#endif +#define WLC_E_DCS_REQUEST 73 + +#define WLC_E_FIFO_CREDIT_MAP 74 + +#define WLC_E_ACTION_FRAME_RX 75 +#define WLC_E_WAKE_EVENT 76 +#define WLC_E_RM_COMPLETE 77 +#define WLC_E_HTSFSYNC 78 +#define WLC_E_OVERLAY_REQ 79 +#define WLC_E_CSA_COMPLETE_IND 80 +#define WLC_E_EXCESS_PM_WAKE_EVENT 81 +#define WLC_E_PFN_SCAN_NONE 82 +#define WLC_E_PFN_SCAN_ALLGONE 83 +#define WLC_E_GTK_PLUMBED 84 +#define WLC_E_ASSOC_REQ_IE 85 +#define WLC_E_ASSOC_RESP_IE 86 +#define WLC_E_LAST 87 + + + +typedef struct { + uint event; + const char *name; +} bcmevent_name_t; + +extern const bcmevent_name_t bcmevent_names[]; +extern const int bcmevent_names_size; + + +#define WLC_E_STATUS_SUCCESS 0 +#define WLC_E_STATUS_FAIL 1 +#define WLC_E_STATUS_TIMEOUT 2 +#define WLC_E_STATUS_NO_NETWORKS 3 +#define WLC_E_STATUS_ABORT 4 +#define WLC_E_STATUS_NO_ACK 5 +#define WLC_E_STATUS_UNSOLICITED 6 +#define WLC_E_STATUS_ATTEMPT 7 +#define WLC_E_STATUS_PARTIAL 8 +#define WLC_E_STATUS_NEWSCAN 9 +#define WLC_E_STATUS_NEWASSOC 10 +#define WLC_E_STATUS_11HQUIET 11 +#define WLC_E_STATUS_SUPPRESS 12 +#define WLC_E_STATUS_NOCHANS 13 +#define WLC_E_STATUS_CS_ABORT 15 +#define WLC_E_STATUS_ERROR 16 + + +#define WLC_E_REASON_INITIAL_ASSOC 0 +#define WLC_E_REASON_LOW_RSSI 1 +#define WLC_E_REASON_DEAUTH 2 +#define WLC_E_REASON_DISASSOC 3 +#define WLC_E_REASON_BCNS_LOST 4 +#define WLC_E_REASON_MINTXRATE 9 +#define WLC_E_REASON_TXFAIL 10 + + +#define WLC_E_REASON_FAST_ROAM_FAILED 5 +#define WLC_E_REASON_DIRECTED_ROAM 6 +#define WLC_E_REASON_TSPEC_REJECTED 7 +#define WLC_E_REASON_BETTER_AP 8 + +#define WLC_E_REASON_REQUESTED_ROAM 11 + + +#define WLC_E_PRUNE_ENCR_MISMATCH 1 +#define WLC_E_PRUNE_BCAST_BSSID 2 +#define WLC_E_PRUNE_MAC_DENY 3 +#define WLC_E_PRUNE_MAC_NA 4 +#define WLC_E_PRUNE_REG_PASSV 5 +#define WLC_E_PRUNE_SPCT_MGMT 6 +#define WLC_E_PRUNE_RADAR 7 +#define WLC_E_RSN_MISMATCH 8 +#define WLC_E_PRUNE_NO_COMMON_RATES 9 +#define WLC_E_PRUNE_BASIC_RATES 10 +#define WLC_E_PRUNE_CIPHER_NA 12 +#define WLC_E_PRUNE_KNOWN_STA 13 +#define WLC_E_PRUNE_WDS_PEER 15 +#define WLC_E_PRUNE_QBSS_LOAD 16 +#define WLC_E_PRUNE_HOME_AP 17 + + +#define WLC_E_SUP_OTHER 0 +#define WLC_E_SUP_DECRYPT_KEY_DATA 1 +#define WLC_E_SUP_BAD_UCAST_WEP128 2 +#define WLC_E_SUP_BAD_UCAST_WEP40 3 +#define WLC_E_SUP_UNSUP_KEY_LEN 4 +#define WLC_E_SUP_PW_KEY_CIPHER 5 +#define WLC_E_SUP_MSG3_TOO_MANY_IE 6 +#define WLC_E_SUP_MSG3_IE_MISMATCH 7 +#define WLC_E_SUP_NO_INSTALL_FLAG 8 +#define WLC_E_SUP_MSG3_NO_GTK 9 +#define WLC_E_SUP_GRP_KEY_CIPHER 10 +#define WLC_E_SUP_GRP_MSG1_NO_GTK 11 +#define WLC_E_SUP_GTK_DECRYPT_FAIL 12 +#define WLC_E_SUP_SEND_FAIL 13 +#define WLC_E_SUP_DEAUTH 14 +#define WLC_E_SUP_WPA_PSK_TMO 15 + + + +typedef BWL_PRE_PACKED_STRUCT struct wl_event_rx_frame_data { + uint16 version; + uint16 channel; + int32 rssi; + uint32 mactime; + uint32 rate; +} BWL_POST_PACKED_STRUCT wl_event_rx_frame_data_t; + +#define BCM_RX_FRAME_DATA_VERSION 1 + + +typedef struct wl_event_data_if { + uint8 ifidx; + uint8 opcode; + uint8 reserved; + uint8 bssidx; + uint8 role; +} wl_event_data_if_t; + + +#define WLC_E_IF_ADD 1 +#define WLC_E_IF_DEL 2 +#define WLC_E_IF_CHANGE 3 + + +#define WLC_E_IF_ROLE_STA 0 +#define WLC_E_IF_ROLE_AP 1 +#define WLC_E_IF_ROLE_WDS 2 +#define WLC_E_IF_ROLE_P2P_GO 3 +#define WLC_E_IF_ROLE_P2P_CLIENT 4 +#define WLC_E_IF_ROLE_BTA_CREATOR 5 +#define WLC_E_IF_ROLE_BTA_ACCEPTOR 6 + + +#define WLC_E_LINK_BCN_LOSS 1 +#define WLC_E_LINK_DISASSOC 2 +#define WLC_E_LINK_ASSOC_REC 3 +#define WLC_E_LINK_BSSCFG_DIS 4 + + +#define WLC_E_OVL_DOWNLOAD 0 +#define WLC_E_OVL_UPDATE_IND 1 + + +#include + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmip.h b/drivers/net/wireless/bcmdhd/include/proto/bcmip.h new file mode 100644 index 0000000000000..55eff247c492e --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/bcmip.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * Fundamental constants relating to IP Protocol + * + * $Id: bcmip.h 277737 2011-08-16 17:54:59Z $ + */ + + +#ifndef _bcmip_h_ +#define _bcmip_h_ + +#ifndef _TYPEDEFS_H_ +#include +#endif + + +#include + + + +#define IP_VER_OFFSET 0x0 +#define IP_VER_MASK 0xf0 +#define IP_VER_SHIFT 4 +#define IP_VER_4 4 +#define IP_VER_6 6 + +#define IP_VER(ip_body) \ + ((((uint8 *)(ip_body))[IP_VER_OFFSET] & IP_VER_MASK) >> IP_VER_SHIFT) + +#define IP_PROT_ICMP 0x1 +#define IP_PROT_TCP 0x6 +#define IP_PROT_UDP 0x11 + + +#define IPV4_VER_HL_OFFSET 0 +#define IPV4_TOS_OFFSET 1 +#define IPV4_PKTLEN_OFFSET 2 +#define IPV4_PKTFLAG_OFFSET 6 +#define IPV4_PROT_OFFSET 9 +#define IPV4_CHKSUM_OFFSET 10 +#define IPV4_SRC_IP_OFFSET 12 +#define IPV4_DEST_IP_OFFSET 16 +#define IPV4_OPTIONS_OFFSET 20 + + +#define IPV4_VER_MASK 0xf0 +#define IPV4_VER_SHIFT 4 + +#define IPV4_HLEN_MASK 0x0f +#define IPV4_HLEN(ipv4_body) (4 * (((uint8 *)(ipv4_body))[IPV4_VER_HL_OFFSET] & IPV4_HLEN_MASK)) + +#define IPV4_ADDR_LEN 4 + +#define IPV4_ADDR_NULL(a) ((((uint8 *)(a))[0] | ((uint8 *)(a))[1] | \ + ((uint8 *)(a))[2] | ((uint8 *)(a))[3]) == 0) + +#define IPV4_ADDR_BCAST(a) ((((uint8 *)(a))[0] & ((uint8 *)(a))[1] & \ + ((uint8 *)(a))[2] & ((uint8 *)(a))[3]) == 0xff) + +#define IPV4_TOS_DSCP_MASK 0xfc +#define IPV4_TOS_DSCP_SHIFT 2 + +#define IPV4_TOS(ipv4_body) (((uint8 *)(ipv4_body))[IPV4_TOS_OFFSET]) + +#define IPV4_TOS_PREC_MASK 0xe0 +#define IPV4_TOS_PREC_SHIFT 5 + +#define IPV4_TOS_LOWDELAY 0x10 +#define IPV4_TOS_THROUGHPUT 0x8 +#define IPV4_TOS_RELIABILITY 0x4 + +#define IPV4_PROT(ipv4_body) (((uint8 *)(ipv4_body))[IPV4_PROT_OFFSET]) + +#define IPV4_FRAG_RESV 0x8000 +#define IPV4_FRAG_DONT 0x4000 +#define IPV4_FRAG_MORE 0x2000 +#define IPV4_FRAG_OFFSET_MASK 0x1fff + +#define IPV4_ADDR_STR_LEN 16 + + +BWL_PRE_PACKED_STRUCT struct ipv4_addr { + uint8 addr[IPV4_ADDR_LEN]; +} BWL_POST_PACKED_STRUCT; + +BWL_PRE_PACKED_STRUCT struct ipv4_hdr { + uint8 version_ihl; + uint8 tos; + uint16 tot_len; + uint16 id; + uint16 frag; + uint8 ttl; + uint8 prot; + uint16 hdr_chksum; + uint8 src_ip[IPV4_ADDR_LEN]; + uint8 dst_ip[IPV4_ADDR_LEN]; +} BWL_POST_PACKED_STRUCT; + + +#define IPV6_PAYLOAD_LEN_OFFSET 4 +#define IPV6_NEXT_HDR_OFFSET 6 +#define IPV6_HOP_LIMIT_OFFSET 7 +#define IPV6_SRC_IP_OFFSET 8 +#define IPV6_DEST_IP_OFFSET 24 + + +#define IPV6_TRAFFIC_CLASS(ipv6_body) \ + (((((uint8 *)(ipv6_body))[0] & 0x0f) << 4) | \ + ((((uint8 *)(ipv6_body))[1] & 0xf0) >> 4)) + +#define IPV6_FLOW_LABEL(ipv6_body) \ + (((((uint8 *)(ipv6_body))[1] & 0x0f) << 16) | \ + (((uint8 *)(ipv6_body))[2] << 8) | \ + (((uint8 *)(ipv6_body))[3])) + +#define IPV6_PAYLOAD_LEN(ipv6_body) \ + ((((uint8 *)(ipv6_body))[IPV6_PAYLOAD_LEN_OFFSET + 0] << 8) | \ + ((uint8 *)(ipv6_body))[IPV6_PAYLOAD_LEN_OFFSET + 1]) + +#define IPV6_NEXT_HDR(ipv6_body) \ + (((uint8 *)(ipv6_body))[IPV6_NEXT_HDR_OFFSET]) + +#define IPV6_PROT(ipv6_body) IPV6_NEXT_HDR(ipv6_body) + +#define IPV6_ADDR_LEN 16 + + +#define IP_TOS46(ip_body) \ + (IP_VER(ip_body) == IP_VER_4 ? IPV4_TOS(ip_body) : \ + IP_VER(ip_body) == IP_VER_6 ? IPV6_TRAFFIC_CLASS(ip_body) : 0) + + +#include + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h b/drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h new file mode 100644 index 0000000000000..91ab4fe538f21 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/bt_amp_hci.h @@ -0,0 +1,442 @@ +/* + * BT-AMP (BlueTooth Alternate Mac and Phy) HCI (Host/Controller Interface) + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: bt_amp_hci.h 277737 2011-08-16 17:54:59Z $ +*/ + +#ifndef _bt_amp_hci_h +#define _bt_amp_hci_h + +/* This marks the start of a packed structure section. */ +#include + + +/* AMP HCI CMD packet format */ +typedef BWL_PRE_PACKED_STRUCT struct amp_hci_cmd { + uint16 opcode; + uint8 plen; + uint8 parms[1]; +} BWL_POST_PACKED_STRUCT amp_hci_cmd_t; + +#define HCI_CMD_PREAMBLE_SIZE OFFSETOF(amp_hci_cmd_t, parms) +#define HCI_CMD_DATA_SIZE 255 + +/* AMP HCI CMD opcode layout */ +#define HCI_CMD_OPCODE(ogf, ocf) ((((ogf) & 0x3F) << 10) | ((ocf) & 0x03FF)) +#define HCI_CMD_OGF(opcode) ((uint8)(((opcode) >> 10) & 0x3F)) +#define HCI_CMD_OCF(opcode) ((opcode) & 0x03FF) + +/* AMP HCI command opcodes */ +#define HCI_Read_Failed_Contact_Counter HCI_CMD_OPCODE(0x05, 0x0001) +#define HCI_Reset_Failed_Contact_Counter HCI_CMD_OPCODE(0x05, 0x0002) +#define HCI_Read_Link_Quality HCI_CMD_OPCODE(0x05, 0x0003) +#define HCI_Read_Local_AMP_Info HCI_CMD_OPCODE(0x05, 0x0009) +#define HCI_Read_Local_AMP_ASSOC HCI_CMD_OPCODE(0x05, 0x000A) +#define HCI_Write_Remote_AMP_ASSOC HCI_CMD_OPCODE(0x05, 0x000B) +#define HCI_Create_Physical_Link HCI_CMD_OPCODE(0x01, 0x0035) +#define HCI_Accept_Physical_Link_Request HCI_CMD_OPCODE(0x01, 0x0036) +#define HCI_Disconnect_Physical_Link HCI_CMD_OPCODE(0x01, 0x0037) +#define HCI_Create_Logical_Link HCI_CMD_OPCODE(0x01, 0x0038) +#define HCI_Accept_Logical_Link HCI_CMD_OPCODE(0x01, 0x0039) +#define HCI_Disconnect_Logical_Link HCI_CMD_OPCODE(0x01, 0x003A) +#define HCI_Logical_Link_Cancel HCI_CMD_OPCODE(0x01, 0x003B) +#define HCI_Flow_Spec_Modify HCI_CMD_OPCODE(0x01, 0x003C) +#define HCI_Write_Flow_Control_Mode HCI_CMD_OPCODE(0x01, 0x0067) +#define HCI_Read_Best_Effort_Flush_Timeout HCI_CMD_OPCODE(0x01, 0x0069) +#define HCI_Write_Best_Effort_Flush_Timeout HCI_CMD_OPCODE(0x01, 0x006A) +#define HCI_Short_Range_Mode HCI_CMD_OPCODE(0x01, 0x006B) +#define HCI_Reset HCI_CMD_OPCODE(0x03, 0x0003) +#define HCI_Read_Connection_Accept_Timeout HCI_CMD_OPCODE(0x03, 0x0015) +#define HCI_Write_Connection_Accept_Timeout HCI_CMD_OPCODE(0x03, 0x0016) +#define HCI_Read_Link_Supervision_Timeout HCI_CMD_OPCODE(0x03, 0x0036) +#define HCI_Write_Link_Supervision_Timeout HCI_CMD_OPCODE(0x03, 0x0037) +#define HCI_Enhanced_Flush HCI_CMD_OPCODE(0x03, 0x005F) +#define HCI_Read_Logical_Link_Accept_Timeout HCI_CMD_OPCODE(0x03, 0x0061) +#define HCI_Write_Logical_Link_Accept_Timeout HCI_CMD_OPCODE(0x03, 0x0062) +#define HCI_Set_Event_Mask_Page_2 HCI_CMD_OPCODE(0x03, 0x0063) +#define HCI_Read_Location_Data_Command HCI_CMD_OPCODE(0x03, 0x0064) +#define HCI_Write_Location_Data_Command HCI_CMD_OPCODE(0x03, 0x0065) +#define HCI_Read_Local_Version_Info HCI_CMD_OPCODE(0x04, 0x0001) +#define HCI_Read_Local_Supported_Commands HCI_CMD_OPCODE(0x04, 0x0002) +#define HCI_Read_Buffer_Size HCI_CMD_OPCODE(0x04, 0x0005) +#define HCI_Read_Data_Block_Size HCI_CMD_OPCODE(0x04, 0x000A) + +/* AMP HCI command parameters */ +typedef BWL_PRE_PACKED_STRUCT struct read_local_cmd_parms { + uint8 plh; + uint8 offset[2]; /* length so far */ + uint8 max_remote[2]; +} BWL_POST_PACKED_STRUCT read_local_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct write_remote_cmd_parms { + uint8 plh; + uint8 offset[2]; + uint8 len[2]; + uint8 frag[1]; +} BWL_POST_PACKED_STRUCT write_remote_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct phy_link_cmd_parms { + uint8 plh; + uint8 key_length; + uint8 key_type; + uint8 key[1]; +} BWL_POST_PACKED_STRUCT phy_link_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct dis_phy_link_cmd_parms { + uint8 plh; + uint8 reason; +} BWL_POST_PACKED_STRUCT dis_phy_link_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct log_link_cmd_parms { + uint8 plh; + uint8 txflow[16]; + uint8 rxflow[16]; +} BWL_POST_PACKED_STRUCT log_link_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct ext_flow_spec { + uint8 id; + uint8 service_type; + uint8 max_sdu[2]; + uint8 sdu_ia_time[4]; + uint8 access_latency[4]; + uint8 flush_timeout[4]; +} BWL_POST_PACKED_STRUCT ext_flow_spec_t; + +typedef BWL_PRE_PACKED_STRUCT struct log_link_cancel_cmd_parms { + uint8 plh; + uint8 tx_fs_ID; +} BWL_POST_PACKED_STRUCT log_link_cancel_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct flow_spec_mod_cmd_parms { + uint8 llh[2]; + uint8 txflow[16]; + uint8 rxflow[16]; +} BWL_POST_PACKED_STRUCT flow_spec_mod_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct plh_pad { + uint8 plh; + uint8 pad; +} BWL_POST_PACKED_STRUCT plh_pad_t; + +typedef BWL_PRE_PACKED_STRUCT union hci_handle { + uint16 bredr; + plh_pad_t amp; +} BWL_POST_PACKED_STRUCT hci_handle_t; + +typedef BWL_PRE_PACKED_STRUCT struct ls_to_cmd_parms { + hci_handle_t handle; + uint8 timeout[2]; +} BWL_POST_PACKED_STRUCT ls_to_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct befto_cmd_parms { + uint8 llh[2]; + uint8 befto[4]; +} BWL_POST_PACKED_STRUCT befto_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct srm_cmd_parms { + uint8 plh; + uint8 srm; +} BWL_POST_PACKED_STRUCT srm_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct ld_cmd_parms { + uint8 ld_aware; + uint8 ld[2]; + uint8 ld_opts; + uint8 l_opts; +} BWL_POST_PACKED_STRUCT ld_cmd_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct eflush_cmd_parms { + uint8 llh[2]; + uint8 packet_type; +} BWL_POST_PACKED_STRUCT eflush_cmd_parms_t; + +/* Generic AMP extended flow spec service types */ +#define EFS_SVCTYPE_NO_TRAFFIC 0 +#define EFS_SVCTYPE_BEST_EFFORT 1 +#define EFS_SVCTYPE_GUARANTEED 2 + +/* AMP HCI event packet format */ +typedef BWL_PRE_PACKED_STRUCT struct amp_hci_event { + uint8 ecode; + uint8 plen; + uint8 parms[1]; +} BWL_POST_PACKED_STRUCT amp_hci_event_t; + +#define HCI_EVT_PREAMBLE_SIZE OFFSETOF(amp_hci_event_t, parms) + +/* AMP HCI event codes */ +#define HCI_Command_Complete 0x0E +#define HCI_Command_Status 0x0F +#define HCI_Flush_Occurred 0x11 +#define HCI_Enhanced_Flush_Complete 0x39 +#define HCI_Physical_Link_Complete 0x40 +#define HCI_Channel_Select 0x41 +#define HCI_Disconnect_Physical_Link_Complete 0x42 +#define HCI_Logical_Link_Complete 0x45 +#define HCI_Disconnect_Logical_Link_Complete 0x46 +#define HCI_Flow_Spec_Modify_Complete 0x47 +#define HCI_Number_of_Completed_Data_Blocks 0x48 +#define HCI_Short_Range_Mode_Change_Complete 0x4C +#define HCI_Status_Change_Event 0x4D +#define HCI_Vendor_Specific 0xFF + +/* AMP HCI event mask bit positions */ +#define HCI_Physical_Link_Complete_Event_Mask 0x0001 +#define HCI_Channel_Select_Event_Mask 0x0002 +#define HCI_Disconnect_Physical_Link_Complete_Event_Mask 0x0004 +#define HCI_Logical_Link_Complete_Event_Mask 0x0020 +#define HCI_Disconnect_Logical_Link_Complete_Event_Mask 0x0040 +#define HCI_Flow_Spec_Modify_Complete_Event_Mask 0x0080 +#define HCI_Number_of_Completed_Data_Blocks_Event_Mask 0x0100 +#define HCI_Short_Range_Mode_Change_Complete_Event_Mask 0x1000 +#define HCI_Status_Change_Event_Mask 0x2000 +#define HCI_All_Event_Mask 0x31e7 + +/* AMP HCI event parameters */ +typedef BWL_PRE_PACKED_STRUCT struct cmd_status_parms { + uint8 status; + uint8 cmdpkts; + uint16 opcode; +} BWL_POST_PACKED_STRUCT cmd_status_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct cmd_complete_parms { + uint8 cmdpkts; + uint16 opcode; + uint8 parms[1]; +} BWL_POST_PACKED_STRUCT cmd_complete_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct flush_occurred_evt_parms { + uint16 handle; +} BWL_POST_PACKED_STRUCT flush_occurred_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct write_remote_evt_parms { + uint8 status; + uint8 plh; +} BWL_POST_PACKED_STRUCT write_remote_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct read_local_evt_parms { + uint8 status; + uint8 plh; + uint16 len; + uint8 frag[1]; +} BWL_POST_PACKED_STRUCT read_local_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct read_local_info_evt_parms { + uint8 status; + uint8 AMP_status; + uint32 bandwidth; + uint32 gbandwidth; + uint32 latency; + uint32 PDU_size; + uint8 ctrl_type; + uint16 PAL_cap; + uint16 AMP_ASSOC_len; + uint32 max_flush_timeout; + uint32 be_flush_timeout; +} BWL_POST_PACKED_STRUCT read_local_info_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct log_link_evt_parms { + uint8 status; + uint16 llh; + uint8 plh; + uint8 tx_fs_ID; +} BWL_POST_PACKED_STRUCT log_link_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct disc_log_link_evt_parms { + uint8 status; + uint16 llh; + uint8 reason; +} BWL_POST_PACKED_STRUCT disc_log_link_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct log_link_cancel_evt_parms { + uint8 status; + uint8 plh; + uint8 tx_fs_ID; +} BWL_POST_PACKED_STRUCT log_link_cancel_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct flow_spec_mod_evt_parms { + uint8 status; + uint16 llh; +} BWL_POST_PACKED_STRUCT flow_spec_mod_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct phy_link_evt_parms { + uint8 status; + uint8 plh; +} BWL_POST_PACKED_STRUCT phy_link_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct dis_phy_link_evt_parms { + uint8 status; + uint8 plh; + uint8 reason; +} BWL_POST_PACKED_STRUCT dis_phy_link_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct read_ls_to_evt_parms { + uint8 status; + hci_handle_t handle; + uint16 timeout; +} BWL_POST_PACKED_STRUCT read_ls_to_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct read_lla_ca_to_evt_parms { + uint8 status; + uint16 timeout; +} BWL_POST_PACKED_STRUCT read_lla_ca_to_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct read_data_block_size_evt_parms { + uint8 status; + uint16 ACL_pkt_len; + uint16 data_block_len; + uint16 data_block_num; +} BWL_POST_PACKED_STRUCT read_data_block_size_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct data_blocks { + uint16 handle; + uint16 pkts; + uint16 blocks; +} BWL_POST_PACKED_STRUCT data_blocks_t; + +typedef BWL_PRE_PACKED_STRUCT struct num_completed_data_blocks_evt_parms { + uint16 num_blocks; + uint8 num_handles; + data_blocks_t completed[1]; +} BWL_POST_PACKED_STRUCT num_completed_data_blocks_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct befto_evt_parms { + uint8 status; + uint32 befto; +} BWL_POST_PACKED_STRUCT befto_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct srm_evt_parms { + uint8 status; + uint8 plh; + uint8 srm; +} BWL_POST_PACKED_STRUCT srm_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct contact_counter_evt_parms { + uint8 status; + uint8 llh[2]; + uint16 counter; +} BWL_POST_PACKED_STRUCT contact_counter_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct contact_counter_reset_evt_parms { + uint8 status; + uint8 llh[2]; +} BWL_POST_PACKED_STRUCT contact_counter_reset_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct read_linkq_evt_parms { + uint8 status; + hci_handle_t handle; + uint8 link_quality; +} BWL_POST_PACKED_STRUCT read_linkq_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct ld_evt_parms { + uint8 status; + uint8 ld_aware; + uint8 ld[2]; + uint8 ld_opts; + uint8 l_opts; +} BWL_POST_PACKED_STRUCT ld_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct eflush_complete_evt_parms { + uint16 handle; +} BWL_POST_PACKED_STRUCT eflush_complete_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct vendor_specific_evt_parms { + uint8 len; + uint8 parms[1]; +} BWL_POST_PACKED_STRUCT vendor_specific_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct local_version_info_evt_parms { + uint8 status; + uint8 hci_version; + uint16 hci_revision; + uint8 pal_version; + uint16 mfg_name; + uint16 pal_subversion; +} BWL_POST_PACKED_STRUCT local_version_info_evt_parms_t; + +#define MAX_SUPPORTED_CMD_BYTE 64 +typedef BWL_PRE_PACKED_STRUCT struct local_supported_cmd_evt_parms { + uint8 status; + uint8 cmd[MAX_SUPPORTED_CMD_BYTE]; +} BWL_POST_PACKED_STRUCT local_supported_cmd_evt_parms_t; + +typedef BWL_PRE_PACKED_STRUCT struct status_change_evt_parms { + uint8 status; + uint8 amp_status; +} BWL_POST_PACKED_STRUCT status_change_evt_parms_t; + +/* AMP HCI error codes */ +#define HCI_SUCCESS 0x00 +#define HCI_ERR_ILLEGAL_COMMAND 0x01 +#define HCI_ERR_NO_CONNECTION 0x02 +#define HCI_ERR_MEMORY_FULL 0x07 +#define HCI_ERR_CONNECTION_TIMEOUT 0x08 +#define HCI_ERR_MAX_NUM_OF_CONNECTIONS 0x09 +#define HCI_ERR_CONNECTION_EXISTS 0x0B +#define HCI_ERR_CONNECTION_DISALLOWED 0x0C +#define HCI_ERR_CONNECTION_ACCEPT_TIMEOUT 0x10 +#define HCI_ERR_UNSUPPORTED_VALUE 0x11 +#define HCI_ERR_ILLEGAL_PARAMETER_FMT 0x12 +#define HCI_ERR_CONN_TERM_BY_LOCAL_HOST 0x16 +#define HCI_ERR_UNSPECIFIED 0x1F +#define HCI_ERR_UNIT_KEY_USED 0x26 +#define HCI_ERR_QOS_REJECTED 0x2D +#define HCI_ERR_PARAM_OUT_OF_RANGE 0x30 +#define HCI_ERR_NO_SUITABLE_CHANNEL 0x39 +#define HCI_ERR_CHANNEL_MOVE 0xFF + +/* AMP HCI ACL Data packet format */ +typedef BWL_PRE_PACKED_STRUCT struct amp_hci_ACL_data { + uint16 handle; /* 12-bit connection handle + 2-bit PB and 2-bit BC flags */ + uint16 dlen; /* data total length */ + uint8 data[1]; +} BWL_POST_PACKED_STRUCT amp_hci_ACL_data_t; + +#define HCI_ACL_DATA_PREAMBLE_SIZE OFFSETOF(amp_hci_ACL_data_t, data) + +#define HCI_ACL_DATA_BC_FLAGS (0x0 << 14) +#define HCI_ACL_DATA_PB_FLAGS (0x3 << 12) + +#define HCI_ACL_DATA_HANDLE(handle) ((handle) & 0x0fff) +#define HCI_ACL_DATA_FLAGS(handle) ((handle) >> 12) + +/* AMP Activity Report packet formats */ +typedef BWL_PRE_PACKED_STRUCT struct amp_hci_activity_report { + uint8 ScheduleKnown; + uint8 NumReports; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT amp_hci_activity_report_t; + +typedef BWL_PRE_PACKED_STRUCT struct amp_hci_activity_report_triple { + uint32 StartTime; + uint32 Duration; + uint32 Periodicity; +} BWL_POST_PACKED_STRUCT amp_hci_activity_report_triple_t; + +#define HCI_AR_SCHEDULE_KNOWN 0x01 + + +/* This marks the end of a packed structure section. */ +#include + +#endif /* _bt_amp_hci_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/eapol.h b/drivers/net/wireless/bcmdhd/include/proto/eapol.h new file mode 100644 index 0000000000000..92634c1221a64 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/eapol.h @@ -0,0 +1,173 @@ +/* + * 802.1x EAPOL definitions + * + * See + * IEEE Std 802.1X-2001 + * IEEE 802.1X RADIUS Usage Guidelines + * + * Copyright (C) 2002 Broadcom Corporation + * + * $Id: eapol.h 277737 2011-08-16 17:54:59Z $ + */ + +#ifndef _eapol_h_ +#define _eapol_h_ + +#ifndef _TYPEDEFS_H_ +#include +#endif + +/* This marks the start of a packed structure section. */ +#include + +#include + +/* EAPOL for 802.3/Ethernet */ +typedef struct { + struct ether_header eth; /* 802.3/Ethernet header */ + unsigned char version; /* EAPOL protocol version */ + unsigned char type; /* EAPOL type */ + unsigned short length; /* Length of body */ + unsigned char body[1]; /* Body (optional) */ +} eapol_header_t; + +#define EAPOL_HEADER_LEN 18 + +/* EAPOL version */ +#define WPA2_EAPOL_VERSION 2 +#define WPA_EAPOL_VERSION 1 +#define LEAP_EAPOL_VERSION 1 +#define SES_EAPOL_VERSION 1 + +/* EAPOL types */ +#define EAP_PACKET 0 +#define EAPOL_START 1 +#define EAPOL_LOGOFF 2 +#define EAPOL_KEY 3 +#define EAPOL_ASF 4 + +/* EAPOL-Key types */ +#define EAPOL_RC4_KEY 1 +#define EAPOL_WPA2_KEY 2 /* 802.11i/WPA2 */ +#define EAPOL_WPA_KEY 254 /* WPA */ + +/* RC4 EAPOL-Key header field sizes */ +#define EAPOL_KEY_REPLAY_LEN 8 +#define EAPOL_KEY_IV_LEN 16 +#define EAPOL_KEY_SIG_LEN 16 + +/* RC4 EAPOL-Key */ +typedef BWL_PRE_PACKED_STRUCT struct { + unsigned char type; /* Key Descriptor Type */ + unsigned short length; /* Key Length (unaligned) */ + unsigned char replay[EAPOL_KEY_REPLAY_LEN]; /* Replay Counter */ + unsigned char iv[EAPOL_KEY_IV_LEN]; /* Key IV */ + unsigned char index; /* Key Flags & Index */ + unsigned char signature[EAPOL_KEY_SIG_LEN]; /* Key Signature */ + unsigned char key[1]; /* Key (optional) */ +} BWL_POST_PACKED_STRUCT eapol_key_header_t; + +#define EAPOL_KEY_HEADER_LEN 44 + +/* RC4 EAPOL-Key flags */ +#define EAPOL_KEY_FLAGS_MASK 0x80 +#define EAPOL_KEY_BROADCAST 0 +#define EAPOL_KEY_UNICAST 0x80 + +/* RC4 EAPOL-Key index */ +#define EAPOL_KEY_INDEX_MASK 0x7f + +/* WPA/802.11i/WPA2 EAPOL-Key header field sizes */ +#define EAPOL_WPA_KEY_REPLAY_LEN 8 +#define EAPOL_WPA_KEY_NONCE_LEN 32 +#define EAPOL_WPA_KEY_IV_LEN 16 +#define EAPOL_WPA_KEY_RSC_LEN 8 +#define EAPOL_WPA_KEY_ID_LEN 8 +#define EAPOL_WPA_KEY_MIC_LEN 16 +#define EAPOL_WPA_KEY_DATA_LEN (EAPOL_WPA_MAX_KEY_SIZE + AKW_BLOCK_LEN) +#define EAPOL_WPA_MAX_KEY_SIZE 32 + +/* WPA EAPOL-Key */ +typedef BWL_PRE_PACKED_STRUCT struct { + unsigned char type; /* Key Descriptor Type */ + unsigned short key_info; /* Key Information (unaligned) */ + unsigned short key_len; /* Key Length (unaligned) */ + unsigned char replay[EAPOL_WPA_KEY_REPLAY_LEN]; /* Replay Counter */ + unsigned char nonce[EAPOL_WPA_KEY_NONCE_LEN]; /* Nonce */ + unsigned char iv[EAPOL_WPA_KEY_IV_LEN]; /* Key IV */ + unsigned char rsc[EAPOL_WPA_KEY_RSC_LEN]; /* Key RSC */ + unsigned char id[EAPOL_WPA_KEY_ID_LEN]; /* WPA:Key ID, 802.11i/WPA2: Reserved */ + unsigned char mic[EAPOL_WPA_KEY_MIC_LEN]; /* Key MIC */ + unsigned short data_len; /* Key Data Length */ + unsigned char data[EAPOL_WPA_KEY_DATA_LEN]; /* Key data */ +} BWL_POST_PACKED_STRUCT eapol_wpa_key_header_t; + +#define EAPOL_WPA_KEY_LEN 95 + +/* WPA/802.11i/WPA2 KEY KEY_INFO bits */ +#define WPA_KEY_DESC_V1 0x01 +#define WPA_KEY_DESC_V2 0x02 +#define WPA_KEY_DESC_V3 0x03 +#define WPA_KEY_PAIRWISE 0x08 +#define WPA_KEY_INSTALL 0x40 +#define WPA_KEY_ACK 0x80 +#define WPA_KEY_MIC 0x100 +#define WPA_KEY_SECURE 0x200 +#define WPA_KEY_ERROR 0x400 +#define WPA_KEY_REQ 0x800 + +/* WPA-only KEY KEY_INFO bits */ +#define WPA_KEY_INDEX_0 0x00 +#define WPA_KEY_INDEX_1 0x10 +#define WPA_KEY_INDEX_2 0x20 +#define WPA_KEY_INDEX_3 0x30 +#define WPA_KEY_INDEX_MASK 0x30 +#define WPA_KEY_INDEX_SHIFT 0x04 + +/* 802.11i/WPA2-only KEY KEY_INFO bits */ +#define WPA_KEY_ENCRYPTED_DATA 0x1000 + +/* Key Data encapsulation */ +typedef BWL_PRE_PACKED_STRUCT struct { + uint8 type; + uint8 length; + uint8 oui[3]; + uint8 subtype; + uint8 data[1]; +} BWL_POST_PACKED_STRUCT eapol_wpa2_encap_data_t; + +#define EAPOL_WPA2_ENCAP_DATA_HDR_LEN 6 + +#define WPA2_KEY_DATA_SUBTYPE_GTK 1 +#define WPA2_KEY_DATA_SUBTYPE_STAKEY 2 +#define WPA2_KEY_DATA_SUBTYPE_MAC 3 +#define WPA2_KEY_DATA_SUBTYPE_PMKID 4 + +/* GTK encapsulation */ +typedef BWL_PRE_PACKED_STRUCT struct { + uint8 flags; + uint8 reserved; + uint8 gtk[EAPOL_WPA_MAX_KEY_SIZE]; +} BWL_POST_PACKED_STRUCT eapol_wpa2_key_gtk_encap_t; + +#define EAPOL_WPA2_KEY_GTK_ENCAP_HDR_LEN 2 + +#define WPA2_GTK_INDEX_MASK 0x03 +#define WPA2_GTK_INDEX_SHIFT 0x00 + +#define WPA2_GTK_TRANSMIT 0x04 + +/* STAKey encapsulation */ +typedef BWL_PRE_PACKED_STRUCT struct { + uint8 reserved[2]; + uint8 mac[ETHER_ADDR_LEN]; + uint8 stakey[EAPOL_WPA_MAX_KEY_SIZE]; +} BWL_POST_PACKED_STRUCT eapol_wpa2_key_stakey_encap_t; + +#define WPA2_KEY_DATA_PAD 0xdd + + +/* This marks the end of a packed structure section. */ +#include + +#endif /* _eapol_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/ethernet.h b/drivers/net/wireless/bcmdhd/include/proto/ethernet.h new file mode 100644 index 0000000000000..20865dc5a2318 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/ethernet.h @@ -0,0 +1,163 @@ +/* + * From FreeBSD 2.2.7: Fundamental constants relating to ethernet. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: ethernet.h 285437 2011-09-21 22:16:56Z $ + */ + + +#ifndef _NET_ETHERNET_H_ +#define _NET_ETHERNET_H_ + +#ifndef _TYPEDEFS_H_ +#include "typedefs.h" +#endif + + +#include + + + +#define ETHER_ADDR_LEN 6 + + +#define ETHER_TYPE_LEN 2 + + +#define ETHER_CRC_LEN 4 + + +#define ETHER_HDR_LEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN) + + +#define ETHER_MIN_LEN 64 + + +#define ETHER_MIN_DATA 46 + + +#define ETHER_MAX_LEN 1518 + + +#define ETHER_MAX_DATA 1500 + + +#define ETHER_TYPE_MIN 0x0600 +#define ETHER_TYPE_IP 0x0800 +#define ETHER_TYPE_ARP 0x0806 +#define ETHER_TYPE_8021Q 0x8100 +#define ETHER_TYPE_BRCM 0x886c +#define ETHER_TYPE_802_1X 0x888e +#define ETHER_TYPE_802_1X_PREAUTH 0x88c7 +#define ETHER_TYPE_WAI 0x88b4 +#define ETHER_TYPE_89_0D 0x890d + + + +#define ETHER_BRCM_SUBTYPE_LEN 4 +#define ETHER_BRCM_CRAM 1 + + +#define ETHER_DEST_OFFSET (0 * ETHER_ADDR_LEN) +#define ETHER_SRC_OFFSET (1 * ETHER_ADDR_LEN) +#define ETHER_TYPE_OFFSET (2 * ETHER_ADDR_LEN) + + +#define ETHER_IS_VALID_LEN(foo) \ + ((foo) >= ETHER_MIN_LEN && (foo) <= ETHER_MAX_LEN) + +#define ETHER_FILL_MCAST_ADDR_FROM_IP(ea, mgrp_ip) { \ + ((uint8 *)ea)[0] = 0x01; \ + ((uint8 *)ea)[1] = 0x00; \ + ((uint8 *)ea)[2] = 0x5e; \ + ((uint8 *)ea)[3] = ((mgrp_ip) >> 16) & 0x7f; \ + ((uint8 *)ea)[4] = ((mgrp_ip) >> 8) & 0xff; \ + ((uint8 *)ea)[5] = ((mgrp_ip) >> 0) & 0xff; \ +} + +#ifndef __INCif_etherh + +BWL_PRE_PACKED_STRUCT struct ether_header { + uint8 ether_dhost[ETHER_ADDR_LEN]; + uint8 ether_shost[ETHER_ADDR_LEN]; + uint16 ether_type; +} BWL_POST_PACKED_STRUCT; + + +BWL_PRE_PACKED_STRUCT struct ether_addr { + uint8 octet[ETHER_ADDR_LEN]; +} BWL_POST_PACKED_STRUCT; +#endif + + +#define ETHER_SET_LOCALADDR(ea) (((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] | 2)) +#define ETHER_IS_LOCALADDR(ea) (((uint8 *)(ea))[0] & 2) +#define ETHER_CLR_LOCALADDR(ea) (((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] & 0xd)) +#define ETHER_TOGGLE_LOCALADDR(ea) (((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] ^ 2)) + + +#define ETHER_SET_UNICAST(ea) (((uint8 *)(ea))[0] = (((uint8 *)(ea))[0] & ~1)) + + +#define ETHER_ISMULTI(ea) (((const uint8 *)(ea))[0] & 1) + + + +#define ether_cmp(a, b) (!(((short*)a)[0] == ((short*)b)[0]) | \ + !(((short*)a)[1] == ((short*)b)[1]) | \ + !(((short*)a)[2] == ((short*)b)[2])) + + +#define ether_copy(s, d) { \ + ((short*)d)[0] = ((short*)s)[0]; \ + ((short*)d)[1] = ((short*)s)[1]; \ + ((short*)d)[2] = ((short*)s)[2]; } + + +static const struct ether_addr ether_bcast = {{255, 255, 255, 255, 255, 255}}; +static const struct ether_addr ether_null = {{0, 0, 0, 0, 0, 0}}; + +#define ETHER_ISBCAST(ea) ((((uint8 *)(ea))[0] & \ + ((uint8 *)(ea))[1] & \ + ((uint8 *)(ea))[2] & \ + ((uint8 *)(ea))[3] & \ + ((uint8 *)(ea))[4] & \ + ((uint8 *)(ea))[5]) == 0xff) +#define ETHER_ISNULLADDR(ea) ((((uint8 *)(ea))[0] | \ + ((uint8 *)(ea))[1] | \ + ((uint8 *)(ea))[2] | \ + ((uint8 *)(ea))[3] | \ + ((uint8 *)(ea))[4] | \ + ((uint8 *)(ea))[5]) == 0) + + +#define ETHER_MOVE_HDR(d, s) \ +do { \ + struct ether_header t; \ + t = *(struct ether_header *)(s); \ + *(struct ether_header *)(d) = t; \ +} while (0) + + +#include + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/p2p.h b/drivers/net/wireless/bcmdhd/include/proto/p2p.h new file mode 100644 index 0000000000000..d2bf3f20688c5 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/p2p.h @@ -0,0 +1,512 @@ +/* + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * Fundamental types and constants relating to WFA P2P (aka WiFi Direct) + * + * $Id: p2p.h 277737 2011-08-16 17:54:59Z $ + */ + +#ifndef _P2P_H_ +#define _P2P_H_ + +#ifndef _TYPEDEFS_H_ +#include +#endif +#include +#include + +/* This marks the start of a packed structure section. */ +#include + + +/* WiFi P2P OUI values */ +#define P2P_OUI WFA_OUI /* WiFi P2P OUI */ +#define P2P_VER WFA_OUI_TYPE_P2P /* P2P version: 9=WiFi P2P v1.0 */ + +#define P2P_IE_ID 0xdd /* P2P IE element ID */ + +/* WiFi P2P IE */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_ie { + uint8 id; /* IE ID: 0xDD */ + uint8 len; /* IE length */ + uint8 OUI[3]; /* WiFi P2P specific OUI: P2P_OUI */ + uint8 oui_type; /* Identifies P2P version: P2P_VER */ + uint8 subelts[1]; /* variable length subelements */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_ie wifi_p2p_ie_t; + +#define P2P_IE_FIXED_LEN 6 + +#define P2P_ATTR_ID_OFF 0 +#define P2P_ATTR_LEN_OFF 1 +#define P2P_ATTR_DATA_OFF 3 + +#define P2P_ATTR_HDR_LEN 3 /* ID + 2-byte length field spec 1.02 */ + +/* P2P IE Subelement IDs from WiFi P2P Technical Spec 1.00 */ +#define P2P_SEID_STATUS 0 /* Status */ +#define P2P_SEID_MINOR_RC 1 /* Minor Reason Code */ +#define P2P_SEID_P2P_INFO 2 /* P2P Capability (capabilities info) */ +#define P2P_SEID_DEV_ID 3 /* P2P Device ID */ +#define P2P_SEID_INTENT 4 /* Group Owner Intent */ +#define P2P_SEID_CFG_TIMEOUT 5 /* Configuration Timeout */ +#define P2P_SEID_CHANNEL 6 /* Channel */ +#define P2P_SEID_GRP_BSSID 7 /* P2P Group BSSID */ +#define P2P_SEID_XT_TIMING 8 /* Extended Listen Timing */ +#define P2P_SEID_INTINTADDR 9 /* Intended P2P Interface Address */ +#define P2P_SEID_P2P_MGBTY 10 /* P2P Manageability */ +#define P2P_SEID_CHAN_LIST 11 /* Channel List */ +#define P2P_SEID_ABSENCE 12 /* Notice of Absence */ +#define P2P_SEID_DEV_INFO 13 /* Device Info */ +#define P2P_SEID_GROUP_INFO 14 /* Group Info */ +#define P2P_SEID_GROUP_ID 15 /* Group ID */ +#define P2P_SEID_P2P_IF 16 /* P2P Interface */ +#define P2P_SEID_VNDR 221 /* Vendor-specific subelement */ + +#define P2P_SE_VS_ID_SERVICES 0x1b /* BRCM proprietary subel: L2 Services */ + + +/* WiFi P2P IE subelement: P2P Capability (capabilities info) */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_info_se_s { + uint8 eltId; /* SE ID: P2P_SEID_P2P_INFO */ + uint8 len[2]; /* SE length not including eltId, len fields */ + uint8 dev; /* Device Capability Bitmap */ + uint8 group; /* Group Capability Bitmap */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_info_se_s wifi_p2p_info_se_t; + +/* P2P Capability subelement's Device Capability Bitmap bit values */ +#define P2P_CAPSE_DEV_SERVICE_DIS 0x1 /* Service Discovery */ +#define P2P_CAPSE_DEV_CLIENT_DIS 0x2 /* Client Discoverability */ +#define P2P_CAPSE_DEV_CONCURRENT 0x4 /* Concurrent Operation */ +#define P2P_CAPSE_DEV_INFRA_MAN 0x8 /* P2P Infrastructure Managed */ +#define P2P_CAPSE_DEV_LIMIT 0x10 /* P2P Device Limit */ +#define P2P_CAPSE_INVITE_PROC 0x20 /* P2P Invitation Procedure */ + +/* P2P Capability subelement's Group Capability Bitmap bit values */ +#define P2P_CAPSE_GRP_OWNER 0x1 /* P2P Group Owner */ +#define P2P_CAPSE_PERSIST_GRP 0x2 /* Persistent P2P Group */ +#define P2P_CAPSE_GRP_LIMIT 0x4 /* P2P Group Limit */ +#define P2P_CAPSE_GRP_INTRA_BSS 0x8 /* Intra-BSS Distribution */ +#define P2P_CAPSE_GRP_X_CONNECT 0x10 /* Cross Connection */ +#define P2P_CAPSE_GRP_PERSISTENT 0x20 /* Persistent Reconnect */ +#define P2P_CAPSE_GRP_FORMATION 0x40 /* Group Formation */ + + +/* WiFi P2P IE subelement: Group Owner Intent */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_intent_se_s { + uint8 eltId; /* SE ID: P2P_SEID_INTENT */ + uint8 len[2]; /* SE length not including eltId, len fields */ + uint8 intent; /* Intent Value 0...15 (0=legacy 15=master only) */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_intent_se_s wifi_p2p_intent_se_t; + +/* WiFi P2P IE subelement: Configuration Timeout */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_cfg_tmo_se_s { + uint8 eltId; /* SE ID: P2P_SEID_CFG_TIMEOUT */ + uint8 len[2]; /* SE length not including eltId, len fields */ + uint8 go_tmo; /* GO config timeout in units of 10 ms */ + uint8 client_tmo; /* Client config timeout in units of 10 ms */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_cfg_tmo_se_s wifi_p2p_cfg_tmo_se_t; + + +/* WiFi P2P IE subelement: Status */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_status_se_s { + uint8 eltId; /* SE ID: P2P_SEID_STATUS */ + uint8 len[2]; /* SE length not including eltId, len fields */ + uint8 status; /* Status Code: P2P_STATSE_* */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_status_se_s wifi_p2p_status_se_t; + +/* Status subelement Status Code definitions */ +#define P2P_STATSE_SUCCESS 0 + /* Success */ +#define P2P_STATSE_FAIL_INFO_CURR_UNAVAIL 1 + /* Failed, information currently unavailable */ +#define P2P_STATSE_PASSED_UP P2P_STATSE_FAIL_INFO_CURR_UNAVAIL + /* Old name for above in P2P spec 1.08 and older */ +#define P2P_STATSE_FAIL_INCOMPAT_PARAMS 2 + /* Failed, incompatible parameters */ +#define P2P_STATSE_FAIL_LIMIT_REACHED 3 + /* Failed, limit reached */ +#define P2P_STATSE_FAIL_INVALID_PARAMS 4 + /* Failed, invalid parameters */ +#define P2P_STATSE_FAIL_UNABLE_TO_ACCOM 5 + /* Failed, unable to accomodate request */ +#define P2P_STATSE_FAIL_PROTO_ERROR 6 + /* Failed, previous protocol error or disruptive behaviour */ +#define P2P_STATSE_FAIL_NO_COMMON_CHAN 7 + /* Failed, no common channels */ +#define P2P_STATSE_FAIL_UNKNOWN_GROUP 8 + /* Failed, unknown P2P Group */ +#define P2P_STATSE_FAIL_INTENT 9 + /* Failed, both peers indicated Intent 15 in GO Negotiation */ +#define P2P_STATSE_FAIL_INCOMPAT_PROVIS 10 + /* Failed, incompatible provisioning method */ +#define P2P_STATSE_FAIL_USER_REJECT 11 + /* Failed, rejected by user */ + +/* WiFi P2P IE attribute: Extended Listen Timing */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_ext_se_s { + uint8 eltId; /* ID: P2P_SEID_EXT_TIMING */ + uint8 len[2]; /* length not including eltId, len fields */ + uint8 avail[2]; /* availibility period */ + uint8 interval[2]; /* availibility interval */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_ext_se_s wifi_p2p_ext_se_t; + +#define P2P_EXT_MIN 10 /* minimum 10ms */ + +/* WiFi P2P IE subelement: Intended P2P Interface Address */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_intintad_se_s { + uint8 eltId; /* SE ID: P2P_SEID_INTINTADDR */ + uint8 len[2]; /* SE length not including eltId, len fields */ + uint8 mac[6]; /* intended P2P interface MAC address */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_intintad_se_s wifi_p2p_intintad_se_t; + +/* WiFi P2P IE subelement: Channel */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_channel_se_s { + uint8 eltId; /* SE ID: P2P_SEID_STATUS */ + uint8 len[2]; /* SE length not including eltId, len fields */ + uint8 band; /* Regulatory Class (band) */ + uint8 channel; /* Channel */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_channel_se_s wifi_p2p_channel_se_t; + + +/* Channel Entry structure within the Channel List SE */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_chanlist_entry_s { + uint8 band; /* Regulatory Class (band) */ + uint8 num_channels; /* # of channels in the channel list */ + uint8 channels[WL_NUMCHANNELS]; /* Channel List */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_chanlist_entry_s wifi_p2p_chanlist_entry_t; +#define WIFI_P2P_CHANLIST_SE_MAX_ENTRIES 2 + +/* WiFi P2P IE subelement: Channel List */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_chanlist_se_s { + uint8 eltId; /* SE ID: P2P_SEID_STATUS */ + uint8 len[2]; /* SE length not including eltId, len fields */ + uint8 country[3]; /* Country String */ + uint8 num_entries; /* # of channel entries */ + wifi_p2p_chanlist_entry_t entries[WIFI_P2P_CHANLIST_SE_MAX_ENTRIES]; + /* Channel Entry List */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_chanlist_se_s wifi_p2p_chanlist_se_t; + +/* WiFi P2P IE's Device Info subelement */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_devinfo_se_s { + uint8 eltId; /* SE ID: P2P_SEID_DEVINFO */ + uint8 len[2]; /* SE length not including eltId, len fields */ + uint8 mac[6]; /* P2P Device MAC address */ + uint16 wps_cfg_meths; /* Config Methods: reg_prototlv.h WPS_CONFMET_* */ + uint8 pri_devtype[8]; /* Primary Device Type */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_devinfo_se_s wifi_p2p_devinfo_se_t; + +#define P2P_DEV_TYPE_LEN 8 + +/* WiFi P2P IE's Group Info subelement Client Info Descriptor */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_cid_fixed_s { + uint8 len; + uint8 devaddr[ETHER_ADDR_LEN]; /* P2P Device Address */ + uint8 ifaddr[ETHER_ADDR_LEN]; /* P2P Interface Address */ + uint8 devcap; /* Device Capability */ + uint8 cfg_meths[2]; /* Config Methods: reg_prototlv.h WPS_CONFMET_* */ + uint8 pridt[P2P_DEV_TYPE_LEN]; /* Primary Device Type */ + uint8 secdts; /* Number of Secondary Device Types */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_cid_fixed_s wifi_p2p_cid_fixed_t; + +/* WiFi P2P IE's Device ID subelement */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_devid_se_s { + uint8 eltId; + uint8 len[2]; + struct ether_addr addr; /* P2P Device MAC address */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_devid_se_s wifi_p2p_devid_se_t; + +/* WiFi P2P IE subelement: P2P Manageability */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_mgbt_se_s { + uint8 eltId; /* SE ID: P2P_SEID_P2P_MGBTY */ + uint8 len[2]; /* SE length not including eltId, len fields */ + uint8 mg_bitmap; /* manageability bitmap */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_mgbt_se_s wifi_p2p_mgbt_se_t; +/* mg_bitmap field bit values */ +#define P2P_MGBTSE_P2PDEVMGMT_FLAG 0x1 /* AP supports Managed P2P Device */ + +/* WiFi P2P IE subelement: Group Info */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_grpinfo_se_s { + uint8 eltId; /* SE ID: P2P_SEID_GROUP_INFO */ + uint8 len[2]; /* SE length not including eltId, len fields */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_grpinfo_se_s wifi_p2p_grpinfo_se_t; + + +/* WiFi P2P Action Frame */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_action_frame { + uint8 category; /* P2P_AF_CATEGORY */ + uint8 OUI[3]; /* OUI - P2P_OUI */ + uint8 type; /* OUI Type - P2P_VER */ + uint8 subtype; /* OUI Subtype - P2P_AF_* */ + uint8 dialog_token; /* nonzero, identifies req/resp tranaction */ + uint8 elts[1]; /* Variable length information elements. Max size = + * ACTION_FRAME_SIZE - sizeof(this structure) - 1 + */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_action_frame wifi_p2p_action_frame_t; +#define P2P_AF_CATEGORY 0x7f + +#define P2P_AF_FIXED_LEN 7 + +/* WiFi P2P Action Frame OUI Subtypes */ +#define P2P_AF_NOTICE_OF_ABSENCE 0 /* Notice of Absence */ +#define P2P_AF_PRESENCE_REQ 1 /* P2P Presence Request */ +#define P2P_AF_PRESENCE_RSP 2 /* P2P Presence Response */ +#define P2P_AF_GO_DISC_REQ 3 /* GO Discoverability Request */ + + +/* WiFi P2P Public Action Frame */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_pub_act_frame { + uint8 category; /* P2P_PUB_AF_CATEGORY */ + uint8 action; /* P2P_PUB_AF_ACTION */ + uint8 oui[3]; /* P2P_OUI */ + uint8 oui_type; /* OUI type - P2P_VER */ + uint8 subtype; /* OUI subtype - P2P_TYPE_* */ + uint8 dialog_token; /* nonzero, identifies req/rsp transaction */ + uint8 elts[1]; /* Variable length information elements. Max size = + * ACTION_FRAME_SIZE - sizeof(this structure) - 1 + */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_pub_act_frame wifi_p2p_pub_act_frame_t; +#define P2P_PUB_AF_FIXED_LEN 8 +#define P2P_PUB_AF_CATEGORY 0x04 +#define P2P_PUB_AF_ACTION 0x09 + +/* WiFi P2P Public Action Frame OUI Subtypes */ +#define P2P_PAF_GON_REQ 0 /* Group Owner Negotiation Req */ +#define P2P_PAF_GON_RSP 1 /* Group Owner Negotiation Rsp */ +#define P2P_PAF_GON_CONF 2 /* Group Owner Negotiation Confirm */ +#define P2P_PAF_INVITE_REQ 3 /* P2P Invitation Request */ +#define P2P_PAF_INVITE_RSP 4 /* P2P Invitation Response */ +#define P2P_PAF_DEVDIS_REQ 5 /* Device Discoverability Request */ +#define P2P_PAF_DEVDIS_RSP 6 /* Device Discoverability Response */ +#define P2P_PAF_PROVDIS_REQ 7 /* Provision Discovery Request */ +#define P2P_PAF_PROVDIS_RSP 8 /* Provision Discovery Request */ + +/* TODO: Stop using these obsolete aliases for P2P_PAF_GON_* */ +#define P2P_TYPE_MNREQ P2P_PAF_GON_REQ +#define P2P_TYPE_MNRSP P2P_PAF_GON_RSP +#define P2P_TYPE_MNCONF P2P_PAF_GON_CONF + +/* WiFi P2P IE subelement: Notice of Absence */ +BWL_PRE_PACKED_STRUCT struct wifi_p2p_noa_desc { + uint8 cnt_type; /* Count/Type */ + uint32 duration; /* Duration */ + uint32 interval; /* Interval */ + uint32 start; /* Start Time */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_noa_desc wifi_p2p_noa_desc_t; + +BWL_PRE_PACKED_STRUCT struct wifi_p2p_noa_se { + uint8 eltId; /* Subelement ID */ + uint8 len[2]; /* Length */ + uint8 index; /* Index */ + uint8 ops_ctw_parms; /* CTWindow and OppPS Parameters */ + wifi_p2p_noa_desc_t desc[1]; /* Notice of Absence Descriptor(s) */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2p_noa_se wifi_p2p_noa_se_t; + +#define P2P_NOA_SE_FIXED_LEN 5 + +/* cnt_type field values */ +#define P2P_NOA_DESC_CNT_RESERVED 0 /* reserved and should not be used */ +#define P2P_NOA_DESC_CNT_REPEAT 255 /* continuous schedule */ +#define P2P_NOA_DESC_TYPE_PREFERRED 1 /* preferred values */ +#define P2P_NOA_DESC_TYPE_ACCEPTABLE 2 /* acceptable limits */ + +/* ctw_ops_parms field values */ +#define P2P_NOA_CTW_MASK 0x7f +#define P2P_NOA_OPS_MASK 0x80 +#define P2P_NOA_OPS_SHIFT 7 + +#define P2P_CTW_MIN 10 /* minimum 10TU */ + +/* + * P2P Service Discovery related + */ +#define P2PSD_ACTION_CATEGORY 0x04 + /* Public action frame */ +#define P2PSD_ACTION_ID_GAS_IREQ 0x0a + /* Action value for GAS Initial Request AF */ +#define P2PSD_ACTION_ID_GAS_IRESP 0x0b + /* Action value for GAS Initial Response AF */ +#define P2PSD_ACTION_ID_GAS_CREQ 0x0c + /* Action value for GAS Comback Request AF */ +#define P2PSD_ACTION_ID_GAS_CRESP 0x0d + /* Action value for GAS Comback Response AF */ +#define P2PSD_AD_EID 0x6c + /* Advertisement Protocol IE ID */ +#define P2PSD_ADP_TUPLE_QLMT_PAMEBI 0x00 + /* Query Response Length Limit 7 bits plus PAME-BI 1 bit */ +#define P2PSD_ADP_PROTO_ID 0x00 + /* Advertisement Protocol ID. Always 0 for P2P SD */ +#define P2PSD_GAS_OUI P2P_OUI + /* WFA OUI */ +#define P2PSD_GAS_OUI_SUBTYPE P2P_VER + /* OUI Subtype for GAS IE */ +#define P2PSD_GAS_NQP_INFOID 0xDDDD + /* NQP Query Info ID: 56797 */ +#define P2PSD_GAS_COMEBACKDEALY 0x00 + /* Not used in the Native GAS protocol */ + +/* Service Protocol Type */ +typedef enum p2psd_svc_protype { + SVC_RPOTYPE_ALL = 0, + SVC_RPOTYPE_BONJOUR = 1, + SVC_RPOTYPE_UPNP = 2, + SVC_RPOTYPE_WSD = 3, + SVC_RPOTYPE_VENDOR = 255 +} p2psd_svc_protype_t; + +/* Service Discovery response status code */ +typedef enum { + P2PSD_RESP_STATUS_SUCCESS = 0, + P2PSD_RESP_STATUS_PROTYPE_NA = 1, + P2PSD_RESP_STATUS_DATA_NA = 2, + P2PSD_RESP_STATUS_BAD_REQUEST = 3 +} p2psd_resp_status_t; + +/* Advertisement Protocol IE tuple field */ +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_adp_tpl { + uint8 llm_pamebi; /* Query Response Length Limit bit 0-6, set to 0 plus + * Pre-Associated Message Exchange BSSID Independent bit 7, set to 0 + */ + uint8 adp_id; /* Advertisement Protocol ID: 0 for NQP Native Query Protocol */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_adp_tpl wifi_p2psd_adp_tpl_t; + +/* Advertisement Protocol IE */ +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_adp_ie { + uint8 id; /* IE ID: 0x6c - 108 */ + uint8 len; /* IE length */ + wifi_p2psd_adp_tpl_t adp_tpl; /* Advertisement Protocol Tuple field. Only one + * tuple is defined for P2P Service Discovery + */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_adp_ie wifi_p2psd_adp_ie_t; + +/* NQP Vendor-specific Content */ +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_nqp_query_vsc { + uint8 oui_subtype; /* OUI Subtype: 0x09 */ + uint16 svc_updi; /* Service Update Indicator */ + uint8 svc_tlvs[1]; /* wifi_p2psd_qreq_tlv_t type for service request, + * wifi_p2psd_qresp_tlv_t type for service response + */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_nqp_query_vsc wifi_p2psd_nqp_query_vsc_t; + +/* Service Request TLV */ +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_qreq_tlv { + uint16 len; /* Length: 5 plus size of Query Data */ + uint8 svc_prot; /* Service Protocol Type */ + uint8 svc_tscid; /* Service Transaction ID */ + uint8 query_data[1]; /* Query Data, passed in from above Layer 2 */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_qreq_tlv wifi_p2psd_qreq_tlv_t; + +/* Query Request Frame, defined in generic format, instead of NQP specific */ +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_qreq_frame { + uint16 info_id; /* Info ID: 0xDDDD */ + uint16 len; /* Length of service request TLV, 5 plus the size of request data */ + uint8 oui[3]; /* WFA OUI: 0x0050F2 */ + uint8 qreq_vsc[1]; /* Vendor-specific Content: wifi_p2psd_nqp_query_vsc_t type for NQP */ + +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_qreq_frame wifi_p2psd_qreq_frame_t; + +/* GAS Initial Request AF body, "elts" in wifi_p2p_pub_act_frame */ +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_gas_ireq_frame { + wifi_p2psd_adp_ie_t adp_ie; /* Advertisement Protocol IE */ + uint16 qreq_len; /* Query Request Length */ + uint8 qreq_frm[1]; /* Query Request Frame wifi_p2psd_qreq_frame_t */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_gas_ireq_frame wifi_p2psd_gas_ireq_frame_t; + +/* Service Response TLV */ +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_qresp_tlv { + uint16 len; /* Length: 5 plus size of Query Data */ + uint8 svc_prot; /* Service Protocol Type */ + uint8 svc_tscid; /* Service Transaction ID */ + uint8 status; /* Value defined in Table 57 of P2P spec. */ + uint8 query_data[1]; /* Response Data, passed in from above Layer 2 */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_qresp_tlv wifi_p2psd_qresp_tlv_t; + +/* Query Response Frame, defined in generic format, instead of NQP specific */ +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_qresp_frame { + uint16 info_id; /* Info ID: 0xDDDD */ + uint16 len; /* Lenth of service response TLV, 6 plus the size of resp data */ + uint8 oui[3]; /* WFA OUI: 0x0050F2 */ + uint8 qresp_vsc[1]; /* Vendor-specific Content: wifi_p2psd_qresp_tlv_t type for NQP */ + +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_qresp_frame wifi_p2psd_qresp_frame_t; + +/* GAS Initial Response AF body, "elts" in wifi_p2p_pub_act_frame */ +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_gas_iresp_frame { + uint16 status; /* Value defined in Table 7-23 of IEEE P802.11u */ + uint16 cb_delay; /* GAS Comeback Delay */ + wifi_p2psd_adp_ie_t adp_ie; /* Advertisement Protocol IE */ + uint16 qresp_len; /* Query Response Length */ + uint8 qresp_frm[1]; /* Query Response Frame wifi_p2psd_qresp_frame_t */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_gas_iresp_frame wifi_p2psd_gas_iresp_frame_t; + +/* GAS Comeback Response AF body, "elts" in wifi_p2p_pub_act_frame */ +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_gas_cresp_frame { + uint16 status; /* Value defined in Table 7-23 of IEEE P802.11u */ + uint8 fragment_id; /* Fragmentation ID */ + uint16 cb_delay; /* GAS Comeback Delay */ + wifi_p2psd_adp_ie_t adp_ie; /* Advertisement Protocol IE */ + uint16 qresp_len; /* Query Response Length */ + uint8 qresp_frm[1]; /* Query Response Frame wifi_p2psd_qresp_frame_t */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_gas_cresp_frame wifi_p2psd_gas_cresp_frame_t; + +/* Wi-Fi GAS Public Action Frame */ +BWL_PRE_PACKED_STRUCT struct wifi_p2psd_gas_pub_act_frame { + uint8 category; /* 0x04 Public Action Frame */ + uint8 action; /* 0x6c Advertisement Protocol */ + uint8 dialog_token; /* nonzero, identifies req/rsp transaction */ + uint8 query_data[1]; /* Query Data. wifi_p2psd_gas_ireq_frame_t + * or wifi_p2psd_gas_iresp_frame_t format + */ +} BWL_POST_PACKED_STRUCT; +typedef struct wifi_p2psd_gas_pub_act_frame wifi_p2psd_gas_pub_act_frame_t; + +/* This marks the end of a packed structure section. */ +#include + +#endif /* _P2P_H_ */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/sdspi.h b/drivers/net/wireless/bcmdhd/include/proto/sdspi.h new file mode 100644 index 0000000000000..7353ff0d7c73b --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/sdspi.h @@ -0,0 +1,76 @@ +/* + * SD-SPI Protocol Standard + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sdspi.h 277737 2011-08-16 17:54:59Z $ + */ + +#ifndef _SD_SPI_H +#define _SD_SPI_H + +#define SPI_START_M BITFIELD_MASK(1) /* Bit [31] - Start Bit */ +#define SPI_START_S 31 +#define SPI_DIR_M BITFIELD_MASK(1) /* Bit [30] - Direction */ +#define SPI_DIR_S 30 +#define SPI_CMD_INDEX_M BITFIELD_MASK(6) /* Bits [29:24] - Command number */ +#define SPI_CMD_INDEX_S 24 +#define SPI_RW_M BITFIELD_MASK(1) /* Bit [23] - Read=0, Write=1 */ +#define SPI_RW_S 23 +#define SPI_FUNC_M BITFIELD_MASK(3) /* Bits [22:20] - Function Number */ +#define SPI_FUNC_S 20 +#define SPI_RAW_M BITFIELD_MASK(1) /* Bit [19] - Read After Wr */ +#define SPI_RAW_S 19 +#define SPI_STUFF_M BITFIELD_MASK(1) /* Bit [18] - Stuff bit */ +#define SPI_STUFF_S 18 +#define SPI_BLKMODE_M BITFIELD_MASK(1) /* Bit [19] - Blockmode 1=blk */ +#define SPI_BLKMODE_S 19 +#define SPI_OPCODE_M BITFIELD_MASK(1) /* Bit [18] - OP Code */ +#define SPI_OPCODE_S 18 +#define SPI_ADDR_M BITFIELD_MASK(17) /* Bits [17:1] - Address */ +#define SPI_ADDR_S 1 +#define SPI_STUFF0_M BITFIELD_MASK(1) /* Bit [0] - Stuff bit */ +#define SPI_STUFF0_S 0 + +#define SPI_RSP_START_M BITFIELD_MASK(1) /* Bit [7] - Start Bit (always 0) */ +#define SPI_RSP_START_S 7 +#define SPI_RSP_PARAM_ERR_M BITFIELD_MASK(1) /* Bit [6] - Parameter Error */ +#define SPI_RSP_PARAM_ERR_S 6 +#define SPI_RSP_RFU5_M BITFIELD_MASK(1) /* Bit [5] - RFU (Always 0) */ +#define SPI_RSP_RFU5_S 5 +#define SPI_RSP_FUNC_ERR_M BITFIELD_MASK(1) /* Bit [4] - Function number error */ +#define SPI_RSP_FUNC_ERR_S 4 +#define SPI_RSP_CRC_ERR_M BITFIELD_MASK(1) /* Bit [3] - COM CRC Error */ +#define SPI_RSP_CRC_ERR_S 3 +#define SPI_RSP_ILL_CMD_M BITFIELD_MASK(1) /* Bit [2] - Illegal Command error */ +#define SPI_RSP_ILL_CMD_S 2 +#define SPI_RSP_RFU1_M BITFIELD_MASK(1) /* Bit [1] - RFU (Always 0) */ +#define SPI_RSP_RFU1_S 1 +#define SPI_RSP_IDLE_M BITFIELD_MASK(1) /* Bit [0] - In idle state */ +#define SPI_RSP_IDLE_S 0 + +/* SD-SPI Protocol Definitions */ +#define SDSPI_COMMAND_LEN 6 /* Number of bytes in an SD command */ +#define SDSPI_START_BLOCK 0xFE /* SD Start Block Token */ +#define SDSPI_IDLE_PAD 0xFF /* SD-SPI idle value for MOSI */ +#define SDSPI_START_BIT_MASK 0x80 + +#endif /* _SD_SPI_H */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/vlan.h b/drivers/net/wireless/bcmdhd/include/proto/vlan.h new file mode 100644 index 0000000000000..27f005537604a --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/vlan.h @@ -0,0 +1,70 @@ +/* + * 802.1Q VLAN protocol definitions + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: vlan.h 277737 2011-08-16 17:54:59Z $ + */ + + +#ifndef _vlan_h_ +#define _vlan_h_ + +#ifndef _TYPEDEFS_H_ +#include +#endif + + +#include + +#define VLAN_VID_MASK 0xfff +#define VLAN_CFI_SHIFT 12 +#define VLAN_PRI_SHIFT 13 + +#define VLAN_PRI_MASK 7 + +#define VLAN_TAG_LEN 4 +#define VLAN_TAG_OFFSET (2 * ETHER_ADDR_LEN) + +#define VLAN_TPID 0x8100 + +struct ethervlan_header { + uint8 ether_dhost[ETHER_ADDR_LEN]; + uint8 ether_shost[ETHER_ADDR_LEN]; + uint16 vlan_type; + uint16 vlan_tag; + uint16 ether_type; +}; + +#define ETHERVLAN_HDR_LEN (ETHER_HDR_LEN + VLAN_TAG_LEN) + + + +#include + +#define ETHERVLAN_MOVE_HDR(d, s) \ +do { \ + struct ethervlan_header t; \ + t = *(struct ethervlan_header *)(s); \ + *(struct ethervlan_header *)(d) = t; \ +} while (0) + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/proto/wpa.h b/drivers/net/wireless/bcmdhd/include/proto/wpa.h new file mode 100644 index 0000000000000..7361cbf20b065 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/proto/wpa.h @@ -0,0 +1,168 @@ +/* + * Fundamental types and constants relating to WPA + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wpa.h 285437 2011-09-21 22:16:56Z $ + */ + + +#ifndef _proto_wpa_h_ +#define _proto_wpa_h_ + +#include +#include + + + +#include + + + + +#define DOT11_RC_INVALID_WPA_IE 13 +#define DOT11_RC_MIC_FAILURE 14 +#define DOT11_RC_4WH_TIMEOUT 15 +#define DOT11_RC_GTK_UPDATE_TIMEOUT 16 +#define DOT11_RC_WPA_IE_MISMATCH 17 +#define DOT11_RC_INVALID_MC_CIPHER 18 +#define DOT11_RC_INVALID_UC_CIPHER 19 +#define DOT11_RC_INVALID_AKMP 20 +#define DOT11_RC_BAD_WPA_VERSION 21 +#define DOT11_RC_INVALID_WPA_CAP 22 +#define DOT11_RC_8021X_AUTH_FAIL 23 + +#define WPA2_PMKID_LEN 16 + + +typedef BWL_PRE_PACKED_STRUCT struct +{ + uint8 tag; + uint8 length; + uint8 oui[3]; + uint8 oui_type; + BWL_PRE_PACKED_STRUCT struct { + uint8 low; + uint8 high; + } BWL_POST_PACKED_STRUCT version; +} BWL_POST_PACKED_STRUCT wpa_ie_fixed_t; +#define WPA_IE_OUITYPE_LEN 4 +#define WPA_IE_FIXED_LEN 8 +#define WPA_IE_TAG_FIXED_LEN 6 + +typedef BWL_PRE_PACKED_STRUCT struct { + uint8 tag; + uint8 length; + BWL_PRE_PACKED_STRUCT struct { + uint8 low; + uint8 high; + } BWL_POST_PACKED_STRUCT version; +} BWL_POST_PACKED_STRUCT wpa_rsn_ie_fixed_t; +#define WPA_RSN_IE_FIXED_LEN 4 +#define WPA_RSN_IE_TAG_FIXED_LEN 2 +typedef uint8 wpa_pmkid_t[WPA2_PMKID_LEN]; + + +typedef BWL_PRE_PACKED_STRUCT struct +{ + uint8 oui[3]; + uint8 type; +} BWL_POST_PACKED_STRUCT wpa_suite_t, wpa_suite_mcast_t; +#define WPA_SUITE_LEN 4 + + +typedef BWL_PRE_PACKED_STRUCT struct +{ + BWL_PRE_PACKED_STRUCT struct { + uint8 low; + uint8 high; + } BWL_POST_PACKED_STRUCT count; + wpa_suite_t list[1]; +} BWL_POST_PACKED_STRUCT wpa_suite_ucast_t, wpa_suite_auth_key_mgmt_t; +#define WPA_IE_SUITE_COUNT_LEN 2 +typedef BWL_PRE_PACKED_STRUCT struct +{ + BWL_PRE_PACKED_STRUCT struct { + uint8 low; + uint8 high; + } BWL_POST_PACKED_STRUCT count; + wpa_pmkid_t list[1]; +} BWL_POST_PACKED_STRUCT wpa_pmkid_list_t; + + +#define WPA_CIPHER_NONE 0 +#define WPA_CIPHER_WEP_40 1 +#define WPA_CIPHER_TKIP 2 +#define WPA_CIPHER_AES_OCB 3 +#define WPA_CIPHER_AES_CCM 4 +#define WPA_CIPHER_WEP_104 5 +#define WPA_CIPHER_BIP 6 +#define WPA_CIPHER_TPK 7 + + +#define IS_WPA_CIPHER(cipher) ((cipher) == WPA_CIPHER_NONE || \ + (cipher) == WPA_CIPHER_WEP_40 || \ + (cipher) == WPA_CIPHER_WEP_104 || \ + (cipher) == WPA_CIPHER_TKIP || \ + (cipher) == WPA_CIPHER_AES_OCB || \ + (cipher) == WPA_CIPHER_AES_CCM || \ + (cipher) == WPA_CIPHER_TPK) + + + +#define WPA_TKIP_CM_DETECT 60 +#define WPA_TKIP_CM_BLOCK 60 + + +#define RSN_CAP_LEN 2 + + +#define RSN_CAP_PREAUTH 0x0001 +#define RSN_CAP_NOPAIRWISE 0x0002 +#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C +#define RSN_CAP_PTK_REPLAY_CNTR_SHIFT 2 +#define RSN_CAP_GTK_REPLAY_CNTR_MASK 0x0030 +#define RSN_CAP_GTK_REPLAY_CNTR_SHIFT 4 +#define RSN_CAP_1_REPLAY_CNTR 0 +#define RSN_CAP_2_REPLAY_CNTRS 1 +#define RSN_CAP_4_REPLAY_CNTRS 2 +#define RSN_CAP_16_REPLAY_CNTRS 3 + + +#define WPA_CAP_4_REPLAY_CNTRS RSN_CAP_4_REPLAY_CNTRS +#define WPA_CAP_16_REPLAY_CNTRS RSN_CAP_16_REPLAY_CNTRS +#define WPA_CAP_REPLAY_CNTR_SHIFT RSN_CAP_PTK_REPLAY_CNTR_SHIFT +#define WPA_CAP_REPLAY_CNTR_MASK RSN_CAP_PTK_REPLAY_CNTR_MASK + + +#define WPA_CAP_PEER_KEY_ENABLE (0x1 << 1) + + +#define WPA_CAP_LEN RSN_CAP_LEN +#define WPA_PMKID_CNT_LEN 2 + +#define WPA_CAP_WPA2_PREAUTH RSN_CAP_PREAUTH + + + +#include + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/sbchipc.h b/drivers/net/wireless/bcmdhd/include/sbchipc.h new file mode 100644 index 0000000000000..aa4df443e1398 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sbchipc.h @@ -0,0 +1,1778 @@ +/* + * SiliconBackplane Chipcommon core hardware definitions. + * + * The chipcommon core provides chip identification, SB control, + * JTAG, 0/1/2 UARTs, clock frequency control, a watchdog interrupt timer, + * GPIO interface, extbus, and support for serial and parallel flashes. + * + * $Id: sbchipc.h 333924 2012-05-18 04:48:52Z $ + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + */ + + +#ifndef _SBCHIPC_H +#define _SBCHIPC_H + +#ifndef _LANGUAGE_ASSEMBLY + + +#ifndef PAD +#define _PADLINE(line) pad ## line +#define _XSTR(line) _PADLINE(line) +#define PAD _XSTR(__LINE__) +#endif + +typedef struct eci_prerev35 { + uint32 eci_output; + uint32 eci_control; + uint32 eci_inputlo; + uint32 eci_inputmi; + uint32 eci_inputhi; + uint32 eci_inputintpolaritylo; + uint32 eci_inputintpolaritymi; + uint32 eci_inputintpolarityhi; + uint32 eci_intmasklo; + uint32 eci_intmaskmi; + uint32 eci_intmaskhi; + uint32 eci_eventlo; + uint32 eci_eventmi; + uint32 eci_eventhi; + uint32 eci_eventmasklo; + uint32 eci_eventmaskmi; + uint32 eci_eventmaskhi; + uint32 PAD[3]; +} eci_prerev35_t; + +typedef struct eci_rev35 { + uint32 eci_outputlo; + uint32 eci_outputhi; + uint32 eci_controllo; + uint32 eci_controlhi; + uint32 eci_inputlo; + uint32 eci_inputhi; + uint32 eci_inputintpolaritylo; + uint32 eci_inputintpolarityhi; + uint32 eci_intmasklo; + uint32 eci_intmaskhi; + uint32 eci_eventlo; + uint32 eci_eventhi; + uint32 eci_eventmasklo; + uint32 eci_eventmaskhi; + uint32 eci_auxtx; + uint32 eci_auxrx; + uint32 eci_datatag; + uint32 eci_uartescvalue; + uint32 eci_autobaudctr; + uint32 eci_uartfifolevel; +} eci_rev35_t; + +typedef volatile struct { + uint32 chipid; + uint32 capabilities; + uint32 corecontrol; + uint32 bist; + + + uint32 otpstatus; + uint32 otpcontrol; + uint32 otpprog; + uint32 otplayout; + + + uint32 intstatus; + uint32 intmask; + + + uint32 chipcontrol; + uint32 chipstatus; + + + uint32 jtagcmd; + uint32 jtagir; + uint32 jtagdr; + uint32 jtagctrl; + + + uint32 flashcontrol; + uint32 flashaddress; + uint32 flashdata; + uint32 PAD[1]; + + + uint32 broadcastaddress; + uint32 broadcastdata; + + + uint32 gpiopullup; + uint32 gpiopulldown; + uint32 gpioin; + uint32 gpioout; + uint32 gpioouten; + uint32 gpiocontrol; + uint32 gpiointpolarity; + uint32 gpiointmask; + + + uint32 gpioevent; + uint32 gpioeventintmask; + + + uint32 watchdog; + + + uint32 gpioeventintpolarity; + + + uint32 gpiotimerval; + uint32 gpiotimeroutmask; + + + uint32 clockcontrol_n; + uint32 clockcontrol_sb; + uint32 clockcontrol_pci; + uint32 clockcontrol_m2; + uint32 clockcontrol_m3; + uint32 clkdiv; + uint32 gpiodebugsel; + uint32 capabilities_ext; + + + uint32 pll_on_delay; + uint32 fref_sel_delay; + uint32 slow_clk_ctl; + uint32 PAD; + + + uint32 system_clk_ctl; + uint32 clkstatestretch; + uint32 PAD[2]; + + + uint32 bp_addrlow; + uint32 bp_addrhigh; + uint32 bp_data; + uint32 PAD; + uint32 bp_indaccess; + + uint32 gsioctrl; + uint32 gsioaddress; + uint32 gsiodata; + + + uint32 clkdiv2; + uint32 PAD[2]; + + + uint32 eromptr; + + + uint32 pcmcia_config; + uint32 pcmcia_memwait; + uint32 pcmcia_attrwait; + uint32 pcmcia_iowait; + uint32 ide_config; + uint32 ide_memwait; + uint32 ide_attrwait; + uint32 ide_iowait; + uint32 prog_config; + uint32 prog_waitcount; + uint32 flash_config; + uint32 flash_waitcount; + uint32 SECI_config; + uint32 SECI_status; + uint32 SECI_statusmask; + uint32 SECI_rxnibchanged; + + uint32 PAD[20]; + + + uint32 sromcontrol; + uint32 sromaddress; + uint32 sromdata; + uint32 PAD[9]; + uint32 seci_uart_data; + uint32 seci_uart_bauddiv; + uint32 seci_uart_fcr; + uint32 seci_uart_lcr; + uint32 seci_uart_mcr; + uint32 seci_uart_lsr; + uint32 seci_uart_msr; + uint32 seci_uart_baudadj; + + uint32 clk_ctl_st; + uint32 hw_war; + uint32 PAD[70]; + + + uint8 uart0data; + uint8 uart0imr; + uint8 uart0fcr; + uint8 uart0lcr; + uint8 uart0mcr; + uint8 uart0lsr; + uint8 uart0msr; + uint8 uart0scratch; + uint8 PAD[248]; + + uint8 uart1data; + uint8 uart1imr; + uint8 uart1fcr; + uint8 uart1lcr; + uint8 uart1mcr; + uint8 uart1lsr; + uint8 uart1msr; + uint8 uart1scratch; + uint32 PAD[126]; + + + + uint32 pmucontrol; + uint32 pmucapabilities; + uint32 pmustatus; + uint32 res_state; + uint32 res_pending; + uint32 pmutimer; + uint32 min_res_mask; + uint32 max_res_mask; + uint32 res_table_sel; + uint32 res_dep_mask; + uint32 res_updn_timer; + uint32 res_timer; + uint32 clkstretch; + uint32 pmuwatchdog; + uint32 gpiosel; + uint32 gpioenable; + uint32 res_req_timer_sel; + uint32 res_req_timer; + uint32 res_req_mask; + uint32 PAD; + uint32 chipcontrol_addr; + uint32 chipcontrol_data; + uint32 regcontrol_addr; + uint32 regcontrol_data; + uint32 pllcontrol_addr; + uint32 pllcontrol_data; + uint32 pmustrapopt; + uint32 pmu_xtalfreq; + uint32 PAD[100]; + uint16 sromotp[768]; +} chipcregs_t; + +#endif + + +#define CC_CHIPID 0 +#define CC_CAPABILITIES 4 +#define CC_CHIPST 0x2c +#define CC_EROMPTR 0xfc + + +#define CC_OTPST 0x10 +#define CC_JTAGCMD 0x30 +#define CC_JTAGIR 0x34 +#define CC_JTAGDR 0x38 +#define CC_JTAGCTRL 0x3c +#define CC_GPIOPU 0x58 +#define CC_GPIOPD 0x5c +#define CC_GPIOIN 0x60 +#define CC_GPIOOUT 0x64 +#define CC_GPIOOUTEN 0x68 +#define CC_GPIOCTRL 0x6c +#define CC_GPIOPOL 0x70 +#define CC_GPIOINTM 0x74 +#define CC_WATCHDOG 0x80 +#define CC_CLKC_N 0x90 +#define CC_CLKC_M0 0x94 +#define CC_CLKC_M1 0x98 +#define CC_CLKC_M2 0x9c +#define CC_CLKC_M3 0xa0 +#define CC_CLKDIV 0xa4 +#define CC_SYS_CLK_CTL 0xc0 +#define CC_CLK_CTL_ST SI_CLK_CTL_ST +#define PMU_CTL 0x600 +#define PMU_CAP 0x604 +#define PMU_ST 0x608 +#define PMU_RES_STATE 0x60c +#define PMU_TIMER 0x614 +#define PMU_MIN_RES_MASK 0x618 +#define PMU_MAX_RES_MASK 0x61c +#define CC_CHIPCTL_ADDR 0x650 +#define CC_CHIPCTL_DATA 0x654 +#define PMU_REG_CONTROL_ADDR 0x658 +#define PMU_REG_CONTROL_DATA 0x65C +#define PMU_PLL_CONTROL_ADDR 0x660 +#define PMU_PLL_CONTROL_DATA 0x664 +#define CC_SROM_OTP 0x800 + + +#define CID_ID_MASK 0x0000ffff +#define CID_REV_MASK 0x000f0000 +#define CID_REV_SHIFT 16 +#define CID_PKG_MASK 0x00f00000 +#define CID_PKG_SHIFT 20 +#define CID_CC_MASK 0x0f000000 +#define CID_CC_SHIFT 24 +#define CID_TYPE_MASK 0xf0000000 +#define CID_TYPE_SHIFT 28 + + +#define CC_CAP_UARTS_MASK 0x00000003 +#define CC_CAP_MIPSEB 0x00000004 +#define CC_CAP_UCLKSEL 0x00000018 +#define CC_CAP_UINTCLK 0x00000008 +#define CC_CAP_UARTGPIO 0x00000020 +#define CC_CAP_EXTBUS_MASK 0x000000c0 +#define CC_CAP_EXTBUS_NONE 0x00000000 +#define CC_CAP_EXTBUS_FULL 0x00000040 +#define CC_CAP_EXTBUS_PROG 0x00000080 +#define CC_CAP_FLASH_MASK 0x00000700 +#define CC_CAP_PLL_MASK 0x00038000 +#define CC_CAP_PWR_CTL 0x00040000 +#define CC_CAP_OTPSIZE 0x00380000 +#define CC_CAP_OTPSIZE_SHIFT 19 +#define CC_CAP_OTPSIZE_BASE 5 +#define CC_CAP_JTAGP 0x00400000 +#define CC_CAP_ROM 0x00800000 +#define CC_CAP_BKPLN64 0x08000000 +#define CC_CAP_PMU 0x10000000 +#define CC_CAP_ECI 0x20000000 +#define CC_CAP_SROM 0x40000000 +#define CC_CAP_NFLASH 0x80000000 + +#define CC_CAP2_SECI 0x00000001 +#define CC_CAP2_GSIO 0x00000002 + + +#define CC_CAP_EXT_SECI_PRESENT 0x00000001 + + +#define PLL_NONE 0x00000000 +#define PLL_TYPE1 0x00010000 +#define PLL_TYPE2 0x00020000 +#define PLL_TYPE3 0x00030000 +#define PLL_TYPE4 0x00008000 +#define PLL_TYPE5 0x00018000 +#define PLL_TYPE6 0x00028000 +#define PLL_TYPE7 0x00038000 + + +#define ILP_CLOCK 32000 + + +#define ALP_CLOCK 20000000 + + +#define HT_CLOCK 80000000 + + +#define CC_UARTCLKO 0x00000001 +#define CC_SE 0x00000002 +#define CC_ASYNCGPIO 0x00000004 +#define CC_UARTCLKEN 0x00000008 + + +#define CHIPCTRL_4321A0_DEFAULT 0x3a4 +#define CHIPCTRL_4321A1_DEFAULT 0x0a4 +#define CHIPCTRL_4321_PLL_DOWN 0x800000 + + +#define OTPS_OL_MASK 0x000000ff +#define OTPS_OL_MFG 0x00000001 +#define OTPS_OL_OR1 0x00000002 +#define OTPS_OL_OR2 0x00000004 +#define OTPS_OL_GU 0x00000008 +#define OTPS_GUP_MASK 0x00000f00 +#define OTPS_GUP_SHIFT 8 +#define OTPS_GUP_HW 0x00000100 +#define OTPS_GUP_SW 0x00000200 +#define OTPS_GUP_CI 0x00000400 +#define OTPS_GUP_FUSE 0x00000800 +#define OTPS_READY 0x00001000 +#define OTPS_RV(x) (1 << (16 + (x))) +#define OTPS_RV_MASK 0x0fff0000 + + +#define OTPC_PROGSEL 0x00000001 +#define OTPC_PCOUNT_MASK 0x0000000e +#define OTPC_PCOUNT_SHIFT 1 +#define OTPC_VSEL_MASK 0x000000f0 +#define OTPC_VSEL_SHIFT 4 +#define OTPC_TMM_MASK 0x00000700 +#define OTPC_TMM_SHIFT 8 +#define OTPC_ODM 0x00000800 +#define OTPC_PROGEN 0x80000000 + + +#define OTPP_COL_MASK 0x000000ff +#define OTPP_COL_SHIFT 0 +#define OTPP_ROW_MASK 0x0000ff00 +#define OTPP_ROW_SHIFT 8 +#define OTPP_OC_MASK 0x0f000000 +#define OTPP_OC_SHIFT 24 +#define OTPP_READERR 0x10000000 +#define OTPP_VALUE_MASK 0x20000000 +#define OTPP_VALUE_SHIFT 29 +#define OTPP_START_BUSY 0x80000000 +#define OTPP_READ 0x40000000 + + +#define OTP_CISFORMAT_NEW 0x80000000 + + +#define OTPPOC_READ 0 +#define OTPPOC_BIT_PROG 1 +#define OTPPOC_VERIFY 3 +#define OTPPOC_INIT 4 +#define OTPPOC_SET 5 +#define OTPPOC_RESET 6 +#define OTPPOC_OCST 7 +#define OTPPOC_ROW_LOCK 8 +#define OTPPOC_PRESCN_TEST 9 + + + +#define JTAGM_CREV_OLD 10 +#define JTAGM_CREV_IRP 22 +#define JTAGM_CREV_RTI 28 + + +#define JCMD_START 0x80000000 +#define JCMD_BUSY 0x80000000 +#define JCMD_STATE_MASK 0x60000000 +#define JCMD_STATE_TLR 0x00000000 +#define JCMD_STATE_PIR 0x20000000 +#define JCMD_STATE_PDR 0x40000000 +#define JCMD_STATE_RTI 0x60000000 +#define JCMD0_ACC_MASK 0x0000f000 +#define JCMD0_ACC_IRDR 0x00000000 +#define JCMD0_ACC_DR 0x00001000 +#define JCMD0_ACC_IR 0x00002000 +#define JCMD0_ACC_RESET 0x00003000 +#define JCMD0_ACC_IRPDR 0x00004000 +#define JCMD0_ACC_PDR 0x00005000 +#define JCMD0_IRW_MASK 0x00000f00 +#define JCMD_ACC_MASK 0x000f0000 +#define JCMD_ACC_IRDR 0x00000000 +#define JCMD_ACC_DR 0x00010000 +#define JCMD_ACC_IR 0x00020000 +#define JCMD_ACC_RESET 0x00030000 +#define JCMD_ACC_IRPDR 0x00040000 +#define JCMD_ACC_PDR 0x00050000 +#define JCMD_ACC_PIR 0x00060000 +#define JCMD_ACC_IRDR_I 0x00070000 +#define JCMD_ACC_DR_I 0x00080000 +#define JCMD_IRW_MASK 0x00001f00 +#define JCMD_IRW_SHIFT 8 +#define JCMD_DRW_MASK 0x0000003f + + +#define JCTRL_FORCE_CLK 4 +#define JCTRL_EXT_EN 2 +#define JCTRL_EN 1 + + +#define CLKD_SFLASH 0x0f000000 +#define CLKD_SFLASH_SHIFT 24 +#define CLKD_OTP 0x000f0000 +#define CLKD_OTP_SHIFT 16 +#define CLKD_JTAG 0x00000f00 +#define CLKD_JTAG_SHIFT 8 +#define CLKD_UART 0x000000ff + +#define CLKD2_SROM 0x00000003 + + +#define CI_GPIO 0x00000001 +#define CI_EI 0x00000002 +#define CI_TEMP 0x00000004 +#define CI_SIRQ 0x00000008 +#define CI_ECI 0x00000010 +#define CI_PMU 0x00000020 +#define CI_UART 0x00000040 +#define CI_WDRESET 0x80000000 + + +#define SCC_SS_MASK 0x00000007 +#define SCC_SS_LPO 0x00000000 +#define SCC_SS_XTAL 0x00000001 +#define SCC_SS_PCI 0x00000002 +#define SCC_LF 0x00000200 +#define SCC_LP 0x00000400 +#define SCC_FS 0x00000800 +#define SCC_IP 0x00001000 +#define SCC_XC 0x00002000 +#define SCC_XP 0x00004000 +#define SCC_CD_MASK 0xffff0000 +#define SCC_CD_SHIFT 16 + + +#define SYCC_IE 0x00000001 +#define SYCC_AE 0x00000002 +#define SYCC_FP 0x00000004 +#define SYCC_AR 0x00000008 +#define SYCC_HR 0x00000010 +#define SYCC_CD_MASK 0xffff0000 +#define SYCC_CD_SHIFT 16 + + +#define BPIA_BYTEEN 0x0000000f +#define BPIA_SZ1 0x00000001 +#define BPIA_SZ2 0x00000003 +#define BPIA_SZ4 0x00000007 +#define BPIA_SZ8 0x0000000f +#define BPIA_WRITE 0x00000100 +#define BPIA_START 0x00000200 +#define BPIA_BUSY 0x00000200 +#define BPIA_ERROR 0x00000400 + + +#define CF_EN 0x00000001 +#define CF_EM_MASK 0x0000000e +#define CF_EM_SHIFT 1 +#define CF_EM_FLASH 0 +#define CF_EM_SYNC 2 +#define CF_EM_PCMCIA 4 +#define CF_DS 0x00000010 +#define CF_BS 0x00000020 +#define CF_CD_MASK 0x000000c0 +#define CF_CD_SHIFT 6 +#define CF_CD_DIV2 0x00000000 +#define CF_CD_DIV3 0x00000040 +#define CF_CD_DIV4 0x00000080 +#define CF_CE 0x00000100 +#define CF_SB 0x00000200 + + +#define PM_W0_MASK 0x0000003f +#define PM_W1_MASK 0x00001f00 +#define PM_W1_SHIFT 8 +#define PM_W2_MASK 0x001f0000 +#define PM_W2_SHIFT 16 +#define PM_W3_MASK 0x1f000000 +#define PM_W3_SHIFT 24 + + +#define PA_W0_MASK 0x0000003f +#define PA_W1_MASK 0x00001f00 +#define PA_W1_SHIFT 8 +#define PA_W2_MASK 0x001f0000 +#define PA_W2_SHIFT 16 +#define PA_W3_MASK 0x1f000000 +#define PA_W3_SHIFT 24 + + +#define PI_W0_MASK 0x0000003f +#define PI_W1_MASK 0x00001f00 +#define PI_W1_SHIFT 8 +#define PI_W2_MASK 0x001f0000 +#define PI_W2_SHIFT 16 +#define PI_W3_MASK 0x1f000000 +#define PI_W3_SHIFT 24 + + +#define PW_W0_MASK 0x0000001f +#define PW_W1_MASK 0x00001f00 +#define PW_W1_SHIFT 8 +#define PW_W2_MASK 0x001f0000 +#define PW_W2_SHIFT 16 +#define PW_W3_MASK 0x1f000000 +#define PW_W3_SHIFT 24 + +#define PW_W0 0x0000000c +#define PW_W1 0x00000a00 +#define PW_W2 0x00020000 +#define PW_W3 0x01000000 + + +#define FW_W0_MASK 0x0000003f +#define FW_W1_MASK 0x00001f00 +#define FW_W1_SHIFT 8 +#define FW_W2_MASK 0x001f0000 +#define FW_W2_SHIFT 16 +#define FW_W3_MASK 0x1f000000 +#define FW_W3_SHIFT 24 + + +#define SRC_START 0x80000000 +#define SRC_BUSY 0x80000000 +#define SRC_OPCODE 0x60000000 +#define SRC_OP_READ 0x00000000 +#define SRC_OP_WRITE 0x20000000 +#define SRC_OP_WRDIS 0x40000000 +#define SRC_OP_WREN 0x60000000 +#define SRC_OTPSEL 0x00000010 +#define SRC_LOCK 0x00000008 +#define SRC_SIZE_MASK 0x00000006 +#define SRC_SIZE_1K 0x00000000 +#define SRC_SIZE_4K 0x00000002 +#define SRC_SIZE_16K 0x00000004 +#define SRC_SIZE_SHIFT 1 +#define SRC_PRESENT 0x00000001 + + +#define PCTL_ILP_DIV_MASK 0xffff0000 +#define PCTL_ILP_DIV_SHIFT 16 +#define PCTL_PLL_PLLCTL_UPD 0x00000400 +#define PCTL_NOILP_ON_WAIT 0x00000200 +#define PCTL_HT_REQ_EN 0x00000100 +#define PCTL_ALP_REQ_EN 0x00000080 +#define PCTL_XTALFREQ_MASK 0x0000007c +#define PCTL_XTALFREQ_SHIFT 2 +#define PCTL_ILP_DIV_EN 0x00000002 +#define PCTL_LPO_SEL 0x00000001 + + +#define CSTRETCH_HT 0xffff0000 +#define CSTRETCH_ALP 0x0000ffff + + +#define GPIO_ONTIME_SHIFT 16 + + +#define CN_N1_MASK 0x3f +#define CN_N2_MASK 0x3f00 +#define CN_N2_SHIFT 8 +#define CN_PLLC_MASK 0xf0000 +#define CN_PLLC_SHIFT 16 + + +#define CC_M1_MASK 0x3f +#define CC_M2_MASK 0x3f00 +#define CC_M2_SHIFT 8 +#define CC_M3_MASK 0x3f0000 +#define CC_M3_SHIFT 16 +#define CC_MC_MASK 0x1f000000 +#define CC_MC_SHIFT 24 + + +#define CC_F6_2 0x02 +#define CC_F6_3 0x03 +#define CC_F6_4 0x05 +#define CC_F6_5 0x09 +#define CC_F6_6 0x11 +#define CC_F6_7 0x21 + +#define CC_F5_BIAS 5 + +#define CC_MC_BYPASS 0x08 +#define CC_MC_M1 0x04 +#define CC_MC_M1M2 0x02 +#define CC_MC_M1M2M3 0x01 +#define CC_MC_M1M3 0x11 + + +#define CC_T2_BIAS 2 +#define CC_T2M2_BIAS 3 + +#define CC_T2MC_M1BYP 1 +#define CC_T2MC_M2BYP 2 +#define CC_T2MC_M3BYP 4 + + +#define CC_T6_MMASK 1 +#define CC_T6_M0 120000000 +#define CC_T6_M1 100000000 +#define SB2MIPS_T6(sb) (2 * (sb)) + + +#define CC_CLOCK_BASE1 24000000 +#define CC_CLOCK_BASE2 12500000 + + +#define CLKC_5350_N 0x0311 +#define CLKC_5350_M 0x04020009 + + +#define FLASH_NONE 0x000 +#define SFLASH_ST 0x100 +#define SFLASH_AT 0x200 +#define PFLASH 0x700 + + +#define CC_CFG_EN 0x0001 +#define CC_CFG_EM_MASK 0x000e +#define CC_CFG_EM_ASYNC 0x0000 +#define CC_CFG_EM_SYNC 0x0002 +#define CC_CFG_EM_PCMCIA 0x0004 +#define CC_CFG_EM_IDE 0x0006 +#define CC_CFG_DS 0x0010 +#define CC_CFG_CD_MASK 0x00e0 +#define CC_CFG_CE 0x0100 +#define CC_CFG_SB 0x0200 +#define CC_CFG_IS 0x0400 + + +#define CC_EB_BASE 0x1a000000 +#define CC_EB_PCMCIA_MEM 0x1a000000 +#define CC_EB_PCMCIA_IO 0x1a200000 +#define CC_EB_PCMCIA_CFG 0x1a400000 +#define CC_EB_IDE 0x1a800000 +#define CC_EB_PCMCIA1_MEM 0x1a800000 +#define CC_EB_PCMCIA1_IO 0x1aa00000 +#define CC_EB_PCMCIA1_CFG 0x1ac00000 +#define CC_EB_PROGIF 0x1b000000 + + + +#define SFLASH_OPCODE 0x000000ff +#define SFLASH_ACTION 0x00000700 +#define SFLASH_CS_ACTIVE 0x00001000 +#define SFLASH_START 0x80000000 +#define SFLASH_BUSY SFLASH_START + + +#define SFLASH_ACT_OPONLY 0x0000 +#define SFLASH_ACT_OP1D 0x0100 +#define SFLASH_ACT_OP3A 0x0200 +#define SFLASH_ACT_OP3A1D 0x0300 +#define SFLASH_ACT_OP3A4D 0x0400 +#define SFLASH_ACT_OP3A4X4D 0x0500 +#define SFLASH_ACT_OP3A1X4D 0x0700 + + +#define SFLASH_ST_WREN 0x0006 +#define SFLASH_ST_WRDIS 0x0004 +#define SFLASH_ST_RDSR 0x0105 +#define SFLASH_ST_WRSR 0x0101 +#define SFLASH_ST_READ 0x0303 +#define SFLASH_ST_PP 0x0302 +#define SFLASH_ST_SE 0x02d8 +#define SFLASH_ST_BE 0x00c7 +#define SFLASH_ST_DP 0x00b9 +#define SFLASH_ST_RES 0x03ab +#define SFLASH_ST_CSA 0x1000 +#define SFLASH_ST_SSE 0x0220 + + +#define SFLASH_ST_WIP 0x01 +#define SFLASH_ST_WEL 0x02 +#define SFLASH_ST_BP_MASK 0x1c +#define SFLASH_ST_BP_SHIFT 2 +#define SFLASH_ST_SRWD 0x80 + + +#define SFLASH_AT_READ 0x07e8 +#define SFLASH_AT_PAGE_READ 0x07d2 +#define SFLASH_AT_BUF1_READ +#define SFLASH_AT_BUF2_READ +#define SFLASH_AT_STATUS 0x01d7 +#define SFLASH_AT_BUF1_WRITE 0x0384 +#define SFLASH_AT_BUF2_WRITE 0x0387 +#define SFLASH_AT_BUF1_ERASE_PROGRAM 0x0283 +#define SFLASH_AT_BUF2_ERASE_PROGRAM 0x0286 +#define SFLASH_AT_BUF1_PROGRAM 0x0288 +#define SFLASH_AT_BUF2_PROGRAM 0x0289 +#define SFLASH_AT_PAGE_ERASE 0x0281 +#define SFLASH_AT_BLOCK_ERASE 0x0250 +#define SFLASH_AT_BUF1_WRITE_ERASE_PROGRAM 0x0382 +#define SFLASH_AT_BUF2_WRITE_ERASE_PROGRAM 0x0385 +#define SFLASH_AT_BUF1_LOAD 0x0253 +#define SFLASH_AT_BUF2_LOAD 0x0255 +#define SFLASH_AT_BUF1_COMPARE 0x0260 +#define SFLASH_AT_BUF2_COMPARE 0x0261 +#define SFLASH_AT_BUF1_REPROGRAM 0x0258 +#define SFLASH_AT_BUF2_REPROGRAM 0x0259 + + +#define SFLASH_AT_READY 0x80 +#define SFLASH_AT_MISMATCH 0x40 +#define SFLASH_AT_ID_MASK 0x38 +#define SFLASH_AT_ID_SHIFT 3 + + +#define GSIO_START 0x80000000 +#define GSIO_BUSY GSIO_START + + + +#define UART_RX 0 +#define UART_TX 0 +#define UART_DLL 0 +#define UART_IER 1 +#define UART_DLM 1 +#define UART_IIR 2 +#define UART_FCR 2 +#define UART_LCR 3 +#define UART_MCR 4 +#define UART_LSR 5 +#define UART_MSR 6 +#define UART_SCR 7 +#define UART_LCR_DLAB 0x80 +#define UART_LCR_WLEN8 0x03 +#define UART_MCR_OUT2 0x08 +#define UART_MCR_LOOP 0x10 +#define UART_LSR_RX_FIFO 0x80 +#define UART_LSR_TDHR 0x40 +#define UART_LSR_THRE 0x20 +#define UART_LSR_BREAK 0x10 +#define UART_LSR_FRAMING 0x08 +#define UART_LSR_PARITY 0x04 +#define UART_LSR_OVERRUN 0x02 +#define UART_LSR_RXRDY 0x01 +#define UART_FCR_FIFO_ENABLE 1 + + +#define UART_IIR_FIFO_MASK 0xc0 +#define UART_IIR_INT_MASK 0xf +#define UART_IIR_MDM_CHG 0x0 +#define UART_IIR_NOINT 0x1 +#define UART_IIR_THRE 0x2 +#define UART_IIR_RCVD_DATA 0x4 +#define UART_IIR_RCVR_STATUS 0x6 +#define UART_IIR_CHAR_TIME 0xc + + +#define UART_IER_EDSSI 8 +#define UART_IER_ELSI 4 +#define UART_IER_ETBEI 2 +#define UART_IER_ERBFI 1 + + +#define PST_EXTLPOAVAIL 0x0100 +#define PST_WDRESET 0x0080 +#define PST_INTPEND 0x0040 +#define PST_SBCLKST 0x0030 +#define PST_SBCLKST_ILP 0x0010 +#define PST_SBCLKST_ALP 0x0020 +#define PST_SBCLKST_HT 0x0030 +#define PST_ALPAVAIL 0x0008 +#define PST_HTAVAIL 0x0004 +#define PST_RESINIT 0x0003 + + +#define PCAP_REV_MASK 0x000000ff +#define PCAP_RC_MASK 0x00001f00 +#define PCAP_RC_SHIFT 8 +#define PCAP_TC_MASK 0x0001e000 +#define PCAP_TC_SHIFT 13 +#define PCAP_PC_MASK 0x001e0000 +#define PCAP_PC_SHIFT 17 +#define PCAP_VC_MASK 0x01e00000 +#define PCAP_VC_SHIFT 21 +#define PCAP_CC_MASK 0x1e000000 +#define PCAP_CC_SHIFT 25 +#define PCAP5_PC_MASK 0x003e0000 +#define PCAP5_PC_SHIFT 17 +#define PCAP5_VC_MASK 0x07c00000 +#define PCAP5_VC_SHIFT 22 +#define PCAP5_CC_MASK 0xf8000000 +#define PCAP5_CC_SHIFT 27 + + + +#define PRRT_TIME_MASK 0x03ff +#define PRRT_INTEN 0x0400 +#define PRRT_REQ_ACTIVE 0x0800 +#define PRRT_ALP_REQ 0x1000 +#define PRRT_HT_REQ 0x2000 + + +#define PMURES_BIT(bit) (1 << (bit)) + + +#define PMURES_MAX_RESNUM 30 + + +#define PMU_CHIPCTL0 0 + + +#define PMU_CC1_CLKREQ_TYPE_SHIFT 19 +#define PMU_CC1_CLKREQ_TYPE_MASK (1 << PMU_CC1_CLKREQ_TYPE_SHIFT) + +#define CLKREQ_TYPE_CONFIG_OPENDRAIN 0 +#define CLKREQ_TYPE_CONFIG_PUSHPULL 1 + + +#define PMU_CHIPCTL1 1 +#define PMU_CC1_RXC_DLL_BYPASS 0x00010000 + +#define PMU_CC1_IF_TYPE_MASK 0x00000030 +#define PMU_CC1_IF_TYPE_RMII 0x00000000 +#define PMU_CC1_IF_TYPE_MII 0x00000010 +#define PMU_CC1_IF_TYPE_RGMII 0x00000020 + +#define PMU_CC1_SW_TYPE_MASK 0x000000c0 +#define PMU_CC1_SW_TYPE_EPHY 0x00000000 +#define PMU_CC1_SW_TYPE_EPHYMII 0x00000040 +#define PMU_CC1_SW_TYPE_EPHYRMII 0x00000080 +#define PMU_CC1_SW_TYPE_RGMII 0x000000c0 + + + + + +#define PMU0_PLL0_PLLCTL0 0 +#define PMU0_PLL0_PC0_PDIV_MASK 1 +#define PMU0_PLL0_PC0_PDIV_FREQ 25000 +#define PMU0_PLL0_PC0_DIV_ARM_MASK 0x00000038 +#define PMU0_PLL0_PC0_DIV_ARM_SHIFT 3 +#define PMU0_PLL0_PC0_DIV_ARM_BASE 8 + + +#define PMU0_PLL0_PC0_DIV_ARM_110MHZ 0 +#define PMU0_PLL0_PC0_DIV_ARM_97_7MHZ 1 +#define PMU0_PLL0_PC0_DIV_ARM_88MHZ 2 +#define PMU0_PLL0_PC0_DIV_ARM_80MHZ 3 +#define PMU0_PLL0_PC0_DIV_ARM_73_3MHZ 4 +#define PMU0_PLL0_PC0_DIV_ARM_67_7MHZ 5 +#define PMU0_PLL0_PC0_DIV_ARM_62_9MHZ 6 +#define PMU0_PLL0_PC0_DIV_ARM_58_6MHZ 7 + + +#define PMU0_PLL0_PLLCTL1 1 +#define PMU0_PLL0_PC1_WILD_INT_MASK 0xf0000000 +#define PMU0_PLL0_PC1_WILD_INT_SHIFT 28 +#define PMU0_PLL0_PC1_WILD_FRAC_MASK 0x0fffff00 +#define PMU0_PLL0_PC1_WILD_FRAC_SHIFT 8 +#define PMU0_PLL0_PC1_STOP_MOD 0x00000040 + + +#define PMU0_PLL0_PLLCTL2 2 +#define PMU0_PLL0_PC2_WILD_INT_MASK 0xf +#define PMU0_PLL0_PC2_WILD_INT_SHIFT 4 + + + +#define PMU1_PLL0_PLLCTL0 0 +#define PMU1_PLL0_PC0_P1DIV_MASK 0x00f00000 +#define PMU1_PLL0_PC0_P1DIV_SHIFT 20 +#define PMU1_PLL0_PC0_P2DIV_MASK 0x0f000000 +#define PMU1_PLL0_PC0_P2DIV_SHIFT 24 + + +#define PMU1_PLL0_PLLCTL1 1 +#define PMU1_PLL0_PC1_M1DIV_MASK 0x000000ff +#define PMU1_PLL0_PC1_M1DIV_SHIFT 0 +#define PMU1_PLL0_PC1_M2DIV_MASK 0x0000ff00 +#define PMU1_PLL0_PC1_M2DIV_SHIFT 8 +#define PMU1_PLL0_PC1_M3DIV_MASK 0x00ff0000 +#define PMU1_PLL0_PC1_M3DIV_SHIFT 16 +#define PMU1_PLL0_PC1_M4DIV_MASK 0xff000000 +#define PMU1_PLL0_PC1_M4DIV_SHIFT 24 +#define PMU1_PLL0_PC1_M4DIV_BY_9 9 +#define PMU1_PLL0_PC1_M4DIV_BY_18 0x12 +#define PMU1_PLL0_PC1_M4DIV_BY_36 0x24 + +#define DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT 8 +#define DOT11MAC_880MHZ_CLK_DIVISOR_MASK (0xFF << DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT) +#define DOT11MAC_880MHZ_CLK_DIVISOR_VAL (0xE << DOT11MAC_880MHZ_CLK_DIVISOR_SHIFT) + + +#define PMU1_PLL0_PLLCTL2 2 +#define PMU1_PLL0_PC2_M5DIV_MASK 0x000000ff +#define PMU1_PLL0_PC2_M5DIV_SHIFT 0 +#define PMU1_PLL0_PC2_M5DIV_BY_12 0xc +#define PMU1_PLL0_PC2_M5DIV_BY_18 0x12 +#define PMU1_PLL0_PC2_M5DIV_BY_36 0x24 +#define PMU1_PLL0_PC2_M6DIV_MASK 0x0000ff00 +#define PMU1_PLL0_PC2_M6DIV_SHIFT 8 +#define PMU1_PLL0_PC2_M6DIV_BY_18 0x12 +#define PMU1_PLL0_PC2_M6DIV_BY_36 0x24 +#define PMU1_PLL0_PC2_NDIV_MODE_MASK 0x000e0000 +#define PMU1_PLL0_PC2_NDIV_MODE_SHIFT 17 +#define PMU1_PLL0_PC2_NDIV_MODE_MASH 1 +#define PMU1_PLL0_PC2_NDIV_MODE_MFB 2 +#define PMU1_PLL0_PC2_NDIV_INT_MASK 0x1ff00000 +#define PMU1_PLL0_PC2_NDIV_INT_SHIFT 20 + + +#define PMU1_PLL0_PLLCTL3 3 +#define PMU1_PLL0_PC3_NDIV_FRAC_MASK 0x00ffffff +#define PMU1_PLL0_PC3_NDIV_FRAC_SHIFT 0 + + +#define PMU1_PLL0_PLLCTL4 4 + + +#define PMU1_PLL0_PLLCTL5 5 +#define PMU1_PLL0_PC5_CLK_DRV_MASK 0xffffff00 +#define PMU1_PLL0_PC5_CLK_DRV_SHIFT 8 + + +#define PMU2_PHY_PLL_PLLCTL 4 +#define PMU2_SI_PLL_PLLCTL 10 + + + + +#define PMU2_PLL_PLLCTL0 0 +#define PMU2_PLL_PC0_P1DIV_MASK 0x00f00000 +#define PMU2_PLL_PC0_P1DIV_SHIFT 20 +#define PMU2_PLL_PC0_P2DIV_MASK 0x0f000000 +#define PMU2_PLL_PC0_P2DIV_SHIFT 24 + + +#define PMU2_PLL_PLLCTL1 1 +#define PMU2_PLL_PC1_M1DIV_MASK 0x000000ff +#define PMU2_PLL_PC1_M1DIV_SHIFT 0 +#define PMU2_PLL_PC1_M2DIV_MASK 0x0000ff00 +#define PMU2_PLL_PC1_M2DIV_SHIFT 8 +#define PMU2_PLL_PC1_M3DIV_MASK 0x00ff0000 +#define PMU2_PLL_PC1_M3DIV_SHIFT 16 +#define PMU2_PLL_PC1_M4DIV_MASK 0xff000000 +#define PMU2_PLL_PC1_M4DIV_SHIFT 24 + + +#define PMU2_PLL_PLLCTL2 2 +#define PMU2_PLL_PC2_M5DIV_MASK 0x000000ff +#define PMU2_PLL_PC2_M5DIV_SHIFT 0 +#define PMU2_PLL_PC2_M6DIV_MASK 0x0000ff00 +#define PMU2_PLL_PC2_M6DIV_SHIFT 8 +#define PMU2_PLL_PC2_NDIV_MODE_MASK 0x000e0000 +#define PMU2_PLL_PC2_NDIV_MODE_SHIFT 17 +#define PMU2_PLL_PC2_NDIV_INT_MASK 0x1ff00000 +#define PMU2_PLL_PC2_NDIV_INT_SHIFT 20 + + +#define PMU2_PLL_PLLCTL3 3 +#define PMU2_PLL_PC3_NDIV_FRAC_MASK 0x00ffffff +#define PMU2_PLL_PC3_NDIV_FRAC_SHIFT 0 + + +#define PMU2_PLL_PLLCTL4 4 + + +#define PMU2_PLL_PLLCTL5 5 +#define PMU2_PLL_PC5_CLKDRIVE_CH1_MASK 0x00000f00 +#define PMU2_PLL_PC5_CLKDRIVE_CH1_SHIFT 8 +#define PMU2_PLL_PC5_CLKDRIVE_CH2_MASK 0x0000f000 +#define PMU2_PLL_PC5_CLKDRIVE_CH2_SHIFT 12 +#define PMU2_PLL_PC5_CLKDRIVE_CH3_MASK 0x000f0000 +#define PMU2_PLL_PC5_CLKDRIVE_CH3_SHIFT 16 +#define PMU2_PLL_PC5_CLKDRIVE_CH4_MASK 0x00f00000 +#define PMU2_PLL_PC5_CLKDRIVE_CH4_SHIFT 20 +#define PMU2_PLL_PC5_CLKDRIVE_CH5_MASK 0x0f000000 +#define PMU2_PLL_PC5_CLKDRIVE_CH5_SHIFT 24 +#define PMU2_PLL_PC5_CLKDRIVE_CH6_MASK 0xf0000000 +#define PMU2_PLL_PC5_CLKDRIVE_CH6_SHIFT 28 + + +#define PMU5_PLL_P1P2_OFF 0 +#define PMU5_PLL_P1_MASK 0x0f000000 +#define PMU5_PLL_P1_SHIFT 24 +#define PMU5_PLL_P2_MASK 0x00f00000 +#define PMU5_PLL_P2_SHIFT 20 +#define PMU5_PLL_M14_OFF 1 +#define PMU5_PLL_MDIV_MASK 0x000000ff +#define PMU5_PLL_MDIV_WIDTH 8 +#define PMU5_PLL_NM5_OFF 2 +#define PMU5_PLL_NDIV_MASK 0xfff00000 +#define PMU5_PLL_NDIV_SHIFT 20 +#define PMU5_PLL_NDIV_MODE_MASK 0x000e0000 +#define PMU5_PLL_NDIV_MODE_SHIFT 17 +#define PMU5_PLL_FMAB_OFF 3 +#define PMU5_PLL_MRAT_MASK 0xf0000000 +#define PMU5_PLL_MRAT_SHIFT 28 +#define PMU5_PLL_ABRAT_MASK 0x08000000 +#define PMU5_PLL_ABRAT_SHIFT 27 +#define PMU5_PLL_FDIV_MASK 0x07ffffff +#define PMU5_PLL_PLLCTL_OFF 4 +#define PMU5_PLL_PCHI_OFF 5 +#define PMU5_PLL_PCHI_MASK 0x0000003f + + +#define PMU_XTALFREQ_REG_ILPCTR_MASK 0x00001FFF +#define PMU_XTALFREQ_REG_MEASURE_MASK 0x80000000 +#define PMU_XTALFREQ_REG_MEASURE_SHIFT 31 + + +#define PMU5_MAINPLL_CPU 1 +#define PMU5_MAINPLL_MEM 2 +#define PMU5_MAINPLL_SI 3 + +#define PMU7_PLL_PLLCTL7 7 +#define PMU7_PLL_CTL7_M4DIV_MASK 0xff000000 +#define PMU7_PLL_CTL7_M4DIV_SHIFT 24 +#define PMU7_PLL_CTL7_M4DIV_BY_6 6 +#define PMU7_PLL_CTL7_M4DIV_BY_12 0xc +#define PMU7_PLL_CTL7_M4DIV_BY_24 0x18 +#define PMU7_PLL_PLLCTL8 8 +#define PMU7_PLL_CTL8_M5DIV_MASK 0x000000ff +#define PMU7_PLL_CTL8_M5DIV_SHIFT 0 +#define PMU7_PLL_CTL8_M5DIV_BY_8 8 +#define PMU7_PLL_CTL8_M5DIV_BY_12 0xc +#define PMU7_PLL_CTL8_M5DIV_BY_24 0x18 +#define PMU7_PLL_CTL8_M6DIV_MASK 0x0000ff00 +#define PMU7_PLL_CTL8_M6DIV_SHIFT 8 +#define PMU7_PLL_CTL8_M6DIV_BY_12 0xc +#define PMU7_PLL_CTL8_M6DIV_BY_24 0x18 +#define PMU7_PLL_PLLCTL11 11 +#define PMU7_PLL_PLLCTL11_MASK 0xffffff00 +#define PMU7_PLL_PLLCTL11_VAL 0x22222200 + + +#define PMU4716_MAINPLL_PLL0 12 + + +#define PMU5356_MAINPLL_PLL0 0 +#define PMU5357_MAINPLL_PLL0 0 + + +#define RES4716_PROC_PLL_ON 0x00000040 +#define RES4716_PROC_HT_AVAIL 0x00000080 + + +#define CCTRL_471X_I2S_PINS_ENABLE 0x0080 + + + +#define CCTRL_5357_I2S_PINS_ENABLE 0x00040000 +#define CCTRL_5357_I2CSPI_PINS_ENABLE 0x00080000 + + +#define RES5354_EXT_SWITCHER_PWM 0 +#define RES5354_BB_SWITCHER_PWM 1 +#define RES5354_BB_SWITCHER_BURST 2 +#define RES5354_BB_EXT_SWITCHER_BURST 3 +#define RES5354_ILP_REQUEST 4 +#define RES5354_RADIO_SWITCHER_PWM 5 +#define RES5354_RADIO_SWITCHER_BURST 6 +#define RES5354_ROM_SWITCH 7 +#define RES5354_PA_REF_LDO 8 +#define RES5354_RADIO_LDO 9 +#define RES5354_AFE_LDO 10 +#define RES5354_PLL_LDO 11 +#define RES5354_BG_FILTBYP 12 +#define RES5354_TX_FILTBYP 13 +#define RES5354_RX_FILTBYP 14 +#define RES5354_XTAL_PU 15 +#define RES5354_XTAL_EN 16 +#define RES5354_BB_PLL_FILTBYP 17 +#define RES5354_RF_PLL_FILTBYP 18 +#define RES5354_BB_PLL_PU 19 + + +#define CCTRL5357_EXTPA (1<<14) +#define CCTRL5357_ANT_MUX_2o3 (1<<15) + + +#define RES4328_EXT_SWITCHER_PWM 0 +#define RES4328_BB_SWITCHER_PWM 1 +#define RES4328_BB_SWITCHER_BURST 2 +#define RES4328_BB_EXT_SWITCHER_BURST 3 +#define RES4328_ILP_REQUEST 4 +#define RES4328_RADIO_SWITCHER_PWM 5 +#define RES4328_RADIO_SWITCHER_BURST 6 +#define RES4328_ROM_SWITCH 7 +#define RES4328_PA_REF_LDO 8 +#define RES4328_RADIO_LDO 9 +#define RES4328_AFE_LDO 10 +#define RES4328_PLL_LDO 11 +#define RES4328_BG_FILTBYP 12 +#define RES4328_TX_FILTBYP 13 +#define RES4328_RX_FILTBYP 14 +#define RES4328_XTAL_PU 15 +#define RES4328_XTAL_EN 16 +#define RES4328_BB_PLL_FILTBYP 17 +#define RES4328_RF_PLL_FILTBYP 18 +#define RES4328_BB_PLL_PU 19 + + +#define RES4325_BUCK_BOOST_BURST 0 +#define RES4325_CBUCK_BURST 1 +#define RES4325_CBUCK_PWM 2 +#define RES4325_CLDO_CBUCK_BURST 3 +#define RES4325_CLDO_CBUCK_PWM 4 +#define RES4325_BUCK_BOOST_PWM 5 +#define RES4325_ILP_REQUEST 6 +#define RES4325_ABUCK_BURST 7 +#define RES4325_ABUCK_PWM 8 +#define RES4325_LNLDO1_PU 9 +#define RES4325_OTP_PU 10 +#define RES4325_LNLDO3_PU 11 +#define RES4325_LNLDO4_PU 12 +#define RES4325_XTAL_PU 13 +#define RES4325_ALP_AVAIL 14 +#define RES4325_RX_PWRSW_PU 15 +#define RES4325_TX_PWRSW_PU 16 +#define RES4325_RFPLL_PWRSW_PU 17 +#define RES4325_LOGEN_PWRSW_PU 18 +#define RES4325_AFE_PWRSW_PU 19 +#define RES4325_BBPLL_PWRSW_PU 20 +#define RES4325_HT_AVAIL 21 + + +#define RES4325B0_CBUCK_LPOM 1 +#define RES4325B0_CBUCK_BURST 2 +#define RES4325B0_CBUCK_PWM 3 +#define RES4325B0_CLDO_PU 4 + + +#define RES4325C1_LNLDO2_PU 12 + + +#define CST4325_SPROM_OTP_SEL_MASK 0x00000003 +#define CST4325_DEFCIS_SEL 0 +#define CST4325_SPROM_SEL 1 +#define CST4325_OTP_SEL 2 +#define CST4325_OTP_PWRDN 3 +#define CST4325_SDIO_USB_MODE_MASK 0x00000004 +#define CST4325_SDIO_USB_MODE_SHIFT 2 +#define CST4325_RCAL_VALID_MASK 0x00000008 +#define CST4325_RCAL_VALID_SHIFT 3 +#define CST4325_RCAL_VALUE_MASK 0x000001f0 +#define CST4325_RCAL_VALUE_SHIFT 4 +#define CST4325_PMUTOP_2B_MASK 0x00000200 +#define CST4325_PMUTOP_2B_SHIFT 9 + +#define RES4329_RESERVED0 0 +#define RES4329_CBUCK_LPOM 1 +#define RES4329_CBUCK_BURST 2 +#define RES4329_CBUCK_PWM 3 +#define RES4329_CLDO_PU 4 +#define RES4329_PALDO_PU 5 +#define RES4329_ILP_REQUEST 6 +#define RES4329_RESERVED7 7 +#define RES4329_RESERVED8 8 +#define RES4329_LNLDO1_PU 9 +#define RES4329_OTP_PU 10 +#define RES4329_RESERVED11 11 +#define RES4329_LNLDO2_PU 12 +#define RES4329_XTAL_PU 13 +#define RES4329_ALP_AVAIL 14 +#define RES4329_RX_PWRSW_PU 15 +#define RES4329_TX_PWRSW_PU 16 +#define RES4329_RFPLL_PWRSW_PU 17 +#define RES4329_LOGEN_PWRSW_PU 18 +#define RES4329_AFE_PWRSW_PU 19 +#define RES4329_BBPLL_PWRSW_PU 20 +#define RES4329_HT_AVAIL 21 + +#define CST4329_SPROM_OTP_SEL_MASK 0x00000003 +#define CST4329_DEFCIS_SEL 0 +#define CST4329_SPROM_SEL 1 +#define CST4329_OTP_SEL 2 +#define CST4329_OTP_PWRDN 3 +#define CST4329_SPI_SDIO_MODE_MASK 0x00000004 +#define CST4329_SPI_SDIO_MODE_SHIFT 2 + + +#define CST4312_SPROM_OTP_SEL_MASK 0x00000003 +#define CST4312_DEFCIS_SEL 0 +#define CST4312_SPROM_SEL 1 +#define CST4312_OTP_SEL 2 +#define CST4312_OTP_BAD 3 + + +#define RES4312_SWITCHER_BURST 0 +#define RES4312_SWITCHER_PWM 1 +#define RES4312_PA_REF_LDO 2 +#define RES4312_CORE_LDO_BURST 3 +#define RES4312_CORE_LDO_PWM 4 +#define RES4312_RADIO_LDO 5 +#define RES4312_ILP_REQUEST 6 +#define RES4312_BG_FILTBYP 7 +#define RES4312_TX_FILTBYP 8 +#define RES4312_RX_FILTBYP 9 +#define RES4312_XTAL_PU 10 +#define RES4312_ALP_AVAIL 11 +#define RES4312_BB_PLL_FILTBYP 12 +#define RES4312_RF_PLL_FILTBYP 13 +#define RES4312_HT_AVAIL 14 + + +#define RES4322_RF_LDO 0 +#define RES4322_ILP_REQUEST 1 +#define RES4322_XTAL_PU 2 +#define RES4322_ALP_AVAIL 3 +#define RES4322_SI_PLL_ON 4 +#define RES4322_HT_SI_AVAIL 5 +#define RES4322_PHY_PLL_ON 6 +#define RES4322_HT_PHY_AVAIL 7 +#define RES4322_OTP_PU 8 + + +#define CST4322_XTAL_FREQ_20_40MHZ 0x00000020 +#define CST4322_SPROM_OTP_SEL_MASK 0x000000c0 +#define CST4322_SPROM_OTP_SEL_SHIFT 6 +#define CST4322_NO_SPROM_OTP 0 +#define CST4322_SPROM_PRESENT 1 +#define CST4322_OTP_PRESENT 2 +#define CST4322_PCI_OR_USB 0x00000100 +#define CST4322_BOOT_MASK 0x00000600 +#define CST4322_BOOT_SHIFT 9 +#define CST4322_BOOT_FROM_SRAM 0 +#define CST4322_BOOT_FROM_ROM 1 +#define CST4322_BOOT_FROM_FLASH 2 +#define CST4322_BOOT_FROM_INVALID 3 +#define CST4322_ILP_DIV_EN 0x00000800 +#define CST4322_FLASH_TYPE_MASK 0x00001000 +#define CST4322_FLASH_TYPE_SHIFT 12 +#define CST4322_FLASH_TYPE_SHIFT_ST 0 +#define CST4322_FLASH_TYPE_SHIFT_ATMEL 1 +#define CST4322_ARM_TAP_SEL 0x00002000 +#define CST4322_RES_INIT_MODE_MASK 0x0000c000 +#define CST4322_RES_INIT_MODE_SHIFT 14 +#define CST4322_RES_INIT_MODE_ILPAVAIL 0 +#define CST4322_RES_INIT_MODE_ILPREQ 1 +#define CST4322_RES_INIT_MODE_ALPAVAIL 2 +#define CST4322_RES_INIT_MODE_HTAVAIL 3 +#define CST4322_PCIPLLCLK_GATING 0x00010000 +#define CST4322_CLK_SWITCH_PCI_TO_ALP 0x00020000 +#define CST4322_PCI_CARDBUS_MODE 0x00040000 + + +#define CCTRL43224_GPIO_TOGGLE 0x8000 +#define CCTRL_43224A0_12MA_LED_DRIVE 0x00F000F0 +#define CCTRL_43224B0_12MA_LED_DRIVE 0xF0 + + +#define RES43236_REGULATOR 0 +#define RES43236_ILP_REQUEST 1 +#define RES43236_XTAL_PU 2 +#define RES43236_ALP_AVAIL 3 +#define RES43236_SI_PLL_ON 4 +#define RES43236_HT_SI_AVAIL 5 + + +#define CCTRL43236_BT_COEXIST (1<<0) +#define CCTRL43236_SECI (1<<1) +#define CCTRL43236_EXT_LNA (1<<2) +#define CCTRL43236_ANT_MUX_2o3 (1<<3) +#define CCTRL43236_GSIO (1<<4) + + +#define CST43236_SFLASH_MASK 0x00000040 +#define CST43236_OTP_SEL_MASK 0x00000080 +#define CST43236_OTP_SEL_SHIFT 7 +#define CST43236_HSIC_MASK 0x00000100 +#define CST43236_BP_CLK 0x00000200 +#define CST43236_BOOT_MASK 0x00001800 +#define CST43236_BOOT_SHIFT 11 +#define CST43236_BOOT_FROM_SRAM 0 +#define CST43236_BOOT_FROM_ROM 1 +#define CST43236_BOOT_FROM_FLASH 2 +#define CST43236_BOOT_FROM_INVALID 3 + + +#define RES43237_REGULATOR 0 +#define RES43237_ILP_REQUEST 1 +#define RES43237_XTAL_PU 2 +#define RES43237_ALP_AVAIL 3 +#define RES43237_SI_PLL_ON 4 +#define RES43237_HT_SI_AVAIL 5 + + +#define CCTRL43237_BT_COEXIST (1<<0) +#define CCTRL43237_SECI (1<<1) +#define CCTRL43237_EXT_LNA (1<<2) +#define CCTRL43237_ANT_MUX_2o3 (1<<3) +#define CCTRL43237_GSIO (1<<4) + + +#define CST43237_SFLASH_MASK 0x00000040 +#define CST43237_OTP_SEL_MASK 0x00000080 +#define CST43237_OTP_SEL_SHIFT 7 +#define CST43237_HSIC_MASK 0x00000100 +#define CST43237_BP_CLK 0x00000200 +#define CST43237_BOOT_MASK 0x00001800 +#define CST43237_BOOT_SHIFT 11 +#define CST43237_BOOT_FROM_SRAM 0 +#define CST43237_BOOT_FROM_ROM 1 +#define CST43237_BOOT_FROM_FLASH 2 +#define CST43237_BOOT_FROM_INVALID 3 + + +#define RES43239_CBUCK_LPOM 0 +#define RES43239_CBUCK_BURST 1 +#define RES43239_CBUCK_LP_PWM 2 +#define RES43239_CBUCK_PWM 3 +#define RES43239_CLDO_PU 4 +#define RES43239_DIS_INT_RESET_PD 5 +#define RES43239_ILP_REQUEST 6 +#define RES43239_LNLDO_PU 7 +#define RES43239_LDO3P3_PU 8 +#define RES43239_OTP_PU 9 +#define RES43239_XTAL_PU 10 +#define RES43239_ALP_AVAIL 11 +#define RES43239_RADIO_PU 12 +#define RES43239_MACPHY_CLKAVAIL 23 +#define RES43239_HT_AVAIL 24 +#define RES43239_XOLDO_PU 25 +#define RES43239_WL_XTAL_CTL_SEL 26 +#define RES43239_SR_CLK_STABLE 27 +#define RES43239_SR_SAVE_RESTORE 28 +#define RES43239_SR_PHY_PIC 29 +#define RES43239_SR_PHY_PWR_SW 30 + + +#define CST43239_SPROM_MASK 0x00000002 +#define CST43239_SFLASH_MASK 0x00000004 +#define CST43239_RES_INIT_MODE_SHIFT 7 +#define CST43239_RES_INIT_MODE_MASK 0x000001f0 +#define CST43239_CHIPMODE_SDIOD(cs) ((cs) & (1 << 15)) +#define CST43239_CHIPMODE_USB20D(cs) (~(cs) & (1 << 15)) +#define CST43239_CHIPMODE_SDIO(cs) (((cs) & (1 << 0)) == 0) +#define CST43239_CHIPMODE_GSPI(cs) (((cs) & (1 << 0)) == (1 << 0)) + + +#define CCTRL43239_XTAL_STRENGTH(ctl) ((ctl & 0x3F) << 12) + + +#define RES4331_REGULATOR 0 +#define RES4331_ILP_REQUEST 1 +#define RES4331_XTAL_PU 2 +#define RES4331_ALP_AVAIL 3 +#define RES4331_SI_PLL_ON 4 +#define RES4331_HT_SI_AVAIL 5 + + +#define CCTRL4331_BT_COEXIST (1<<0) +#define CCTRL4331_SECI (1<<1) +#define CCTRL4331_EXT_LNA_G (1<<2) +#define CCTRL4331_SPROM_GPIO13_15 (1<<3) +#define CCTRL4331_EXTPA_EN (1<<4) +#define CCTRL4331_GPIOCLK_ON_SPROMCS (1<<5) +#define CCTRL4331_PCIE_MDIO_ON_SPROMCS (1<<6) +#define CCTRL4331_EXTPA_ON_GPIO2_5 (1<<7) +#define CCTRL4331_OVR_PIPEAUXCLKEN (1<<8) +#define CCTRL4331_OVR_PIPEAUXPWRDOWN (1<<9) +#define CCTRL4331_PCIE_AUXCLKEN (1<<10) +#define CCTRL4331_PCIE_PIPE_PLLDOWN (1<<11) +#define CCTRL4331_EXTPA_EN2 (1<<12) +#define CCTRL4331_EXT_LNA_A (1<<13) +#define CCTRL4331_BT_SHD0_ON_GPIO4 (1<<16) +#define CCTRL4331_BT_SHD1_ON_GPIO5 (1<<17) +#define CCTRL4331_EXTPA_ANA_EN (1<<24) + + +#define CST4331_XTAL_FREQ 0x00000001 +#define CST4331_SPROM_OTP_SEL_MASK 0x00000006 +#define CST4331_SPROM_OTP_SEL_SHIFT 1 +#define CST4331_SPROM_PRESENT 0x00000002 +#define CST4331_OTP_PRESENT 0x00000004 +#define CST4331_LDO_RF 0x00000008 +#define CST4331_LDO_PAR 0x00000010 + + +#define RES4315_CBUCK_LPOM 1 +#define RES4315_CBUCK_BURST 2 +#define RES4315_CBUCK_PWM 3 +#define RES4315_CLDO_PU 4 +#define RES4315_PALDO_PU 5 +#define RES4315_ILP_REQUEST 6 +#define RES4315_LNLDO1_PU 9 +#define RES4315_OTP_PU 10 +#define RES4315_LNLDO2_PU 12 +#define RES4315_XTAL_PU 13 +#define RES4315_ALP_AVAIL 14 +#define RES4315_RX_PWRSW_PU 15 +#define RES4315_TX_PWRSW_PU 16 +#define RES4315_RFPLL_PWRSW_PU 17 +#define RES4315_LOGEN_PWRSW_PU 18 +#define RES4315_AFE_PWRSW_PU 19 +#define RES4315_BBPLL_PWRSW_PU 20 +#define RES4315_HT_AVAIL 21 + + +#define CST4315_SPROM_OTP_SEL_MASK 0x00000003 +#define CST4315_DEFCIS_SEL 0x00000000 +#define CST4315_SPROM_SEL 0x00000001 +#define CST4315_OTP_SEL 0x00000002 +#define CST4315_OTP_PWRDN 0x00000003 +#define CST4315_SDIO_MODE 0x00000004 +#define CST4315_RCAL_VALID 0x00000008 +#define CST4315_RCAL_VALUE_MASK 0x000001f0 +#define CST4315_RCAL_VALUE_SHIFT 4 +#define CST4315_PALDO_EXTPNP 0x00000200 +#define CST4315_CBUCK_MODE_MASK 0x00000c00 +#define CST4315_CBUCK_MODE_BURST 0x00000400 +#define CST4315_CBUCK_MODE_LPBURST 0x00000c00 + + +#define RES4319_CBUCK_LPOM 1 +#define RES4319_CBUCK_BURST 2 +#define RES4319_CBUCK_PWM 3 +#define RES4319_CLDO_PU 4 +#define RES4319_PALDO_PU 5 +#define RES4319_ILP_REQUEST 6 +#define RES4319_LNLDO1_PU 9 +#define RES4319_OTP_PU 10 +#define RES4319_LNLDO2_PU 12 +#define RES4319_XTAL_PU 13 +#define RES4319_ALP_AVAIL 14 +#define RES4319_RX_PWRSW_PU 15 +#define RES4319_TX_PWRSW_PU 16 +#define RES4319_RFPLL_PWRSW_PU 17 +#define RES4319_LOGEN_PWRSW_PU 18 +#define RES4319_AFE_PWRSW_PU 19 +#define RES4319_BBPLL_PWRSW_PU 20 +#define RES4319_HT_AVAIL 21 + + +#define CST4319_SPI_CPULESSUSB 0x00000001 +#define CST4319_SPI_CLK_POL 0x00000002 +#define CST4319_SPI_CLK_PH 0x00000008 +#define CST4319_SPROM_OTP_SEL_MASK 0x000000c0 +#define CST4319_SPROM_OTP_SEL_SHIFT 6 +#define CST4319_DEFCIS_SEL 0x00000000 +#define CST4319_SPROM_SEL 0x00000040 +#define CST4319_OTP_SEL 0x00000080 +#define CST4319_OTP_PWRDN 0x000000c0 +#define CST4319_SDIO_USB_MODE 0x00000100 +#define CST4319_REMAP_SEL_MASK 0x00000600 +#define CST4319_ILPDIV_EN 0x00000800 +#define CST4319_XTAL_PD_POL 0x00001000 +#define CST4319_LPO_SEL 0x00002000 +#define CST4319_RES_INIT_MODE 0x0000c000 +#define CST4319_PALDO_EXTPNP 0x00010000 +#define CST4319_CBUCK_MODE_MASK 0x00060000 +#define CST4319_CBUCK_MODE_BURST 0x00020000 +#define CST4319_CBUCK_MODE_LPBURST 0x00060000 +#define CST4319_RCAL_VALID 0x01000000 +#define CST4319_RCAL_VALUE_MASK 0x3e000000 +#define CST4319_RCAL_VALUE_SHIFT 25 + +#define PMU1_PLL0_CHIPCTL0 0 +#define PMU1_PLL0_CHIPCTL1 1 +#define PMU1_PLL0_CHIPCTL2 2 +#define CCTL_4319USB_XTAL_SEL_MASK 0x00180000 +#define CCTL_4319USB_XTAL_SEL_SHIFT 19 +#define CCTL_4319USB_48MHZ_PLL_SEL 1 +#define CCTL_4319USB_24MHZ_PLL_SEL 2 + + +#define RES4336_CBUCK_LPOM 0 +#define RES4336_CBUCK_BURST 1 +#define RES4336_CBUCK_LP_PWM 2 +#define RES4336_CBUCK_PWM 3 +#define RES4336_CLDO_PU 4 +#define RES4336_DIS_INT_RESET_PD 5 +#define RES4336_ILP_REQUEST 6 +#define RES4336_LNLDO_PU 7 +#define RES4336_LDO3P3_PU 8 +#define RES4336_OTP_PU 9 +#define RES4336_XTAL_PU 10 +#define RES4336_ALP_AVAIL 11 +#define RES4336_RADIO_PU 12 +#define RES4336_BG_PU 13 +#define RES4336_VREG1p4_PU_PU 14 +#define RES4336_AFE_PWRSW_PU 15 +#define RES4336_RX_PWRSW_PU 16 +#define RES4336_TX_PWRSW_PU 17 +#define RES4336_BB_PWRSW_PU 18 +#define RES4336_SYNTH_PWRSW_PU 19 +#define RES4336_MISC_PWRSW_PU 20 +#define RES4336_LOGEN_PWRSW_PU 21 +#define RES4336_BBPLL_PWRSW_PU 22 +#define RES4336_MACPHY_CLKAVAIL 23 +#define RES4336_HT_AVAIL 24 +#define RES4336_RSVD 25 + + +#define CST4336_SPI_MODE_MASK 0x00000001 +#define CST4336_SPROM_PRESENT 0x00000002 +#define CST4336_OTP_PRESENT 0x00000004 +#define CST4336_ARMREMAP_0 0x00000008 +#define CST4336_ILPDIV_EN_MASK 0x00000010 +#define CST4336_ILPDIV_EN_SHIFT 4 +#define CST4336_XTAL_PD_POL_MASK 0x00000020 +#define CST4336_XTAL_PD_POL_SHIFT 5 +#define CST4336_LPO_SEL_MASK 0x00000040 +#define CST4336_LPO_SEL_SHIFT 6 +#define CST4336_RES_INIT_MODE_MASK 0x00000180 +#define CST4336_RES_INIT_MODE_SHIFT 7 +#define CST4336_CBUCK_MODE_MASK 0x00000600 +#define CST4336_CBUCK_MODE_SHIFT 9 + + +#define PCTL_4336_SERIAL_ENAB (1 << 24) + + +#define RES4330_CBUCK_LPOM 0 +#define RES4330_CBUCK_BURST 1 +#define RES4330_CBUCK_LP_PWM 2 +#define RES4330_CBUCK_PWM 3 +#define RES4330_CLDO_PU 4 +#define RES4330_DIS_INT_RESET_PD 5 +#define RES4330_ILP_REQUEST 6 +#define RES4330_LNLDO_PU 7 +#define RES4330_LDO3P3_PU 8 +#define RES4330_OTP_PU 9 +#define RES4330_XTAL_PU 10 +#define RES4330_ALP_AVAIL 11 +#define RES4330_RADIO_PU 12 +#define RES4330_BG_PU 13 +#define RES4330_VREG1p4_PU_PU 14 +#define RES4330_AFE_PWRSW_PU 15 +#define RES4330_RX_PWRSW_PU 16 +#define RES4330_TX_PWRSW_PU 17 +#define RES4330_BB_PWRSW_PU 18 +#define RES4330_SYNTH_PWRSW_PU 19 +#define RES4330_MISC_PWRSW_PU 20 +#define RES4330_LOGEN_PWRSW_PU 21 +#define RES4330_BBPLL_PWRSW_PU 22 +#define RES4330_MACPHY_CLKAVAIL 23 +#define RES4330_HT_AVAIL 24 +#define RES4330_5gRX_PWRSW_PU 25 +#define RES4330_5gTX_PWRSW_PU 26 +#define RES4330_5g_LOGEN_PWRSW_PU 27 + + +#define CST4330_CHIPMODE_SDIOD(cs) (((cs) & 0x7) < 6) +#define CST4330_CHIPMODE_USB20D(cs) (((cs) & 0x7) >= 6) +#define CST4330_CHIPMODE_SDIO(cs) (((cs) & 0x4) == 0) +#define CST4330_CHIPMODE_GSPI(cs) (((cs) & 0x6) == 4) +#define CST4330_CHIPMODE_USB(cs) (((cs) & 0x7) == 6) +#define CST4330_CHIPMODE_USBDA(cs) (((cs) & 0x7) == 7) +#define CST4330_OTP_PRESENT 0x00000010 +#define CST4330_LPO_AUTODET_EN 0x00000020 +#define CST4330_ARMREMAP_0 0x00000040 +#define CST4330_SPROM_PRESENT 0x00000080 +#define CST4330_ILPDIV_EN 0x00000100 +#define CST4330_LPO_SEL 0x00000200 +#define CST4330_RES_INIT_MODE_SHIFT 10 +#define CST4330_RES_INIT_MODE_MASK 0x00000c00 +#define CST4330_CBUCK_MODE_SHIFT 12 +#define CST4330_CBUCK_MODE_MASK 0x00003000 +#define CST4330_CBUCK_POWER_OK 0x00004000 +#define CST4330_BB_PLL_LOCKED 0x00008000 +#define SOCDEVRAM_4330_BP_ADDR 0x1E000000 +#define SOCDEVRAM_4330_ARM_ADDR 0x00800000 + + +#define PCTL_4330_SERIAL_ENAB (1 << 24) + + +#define CCTRL_4330_GPIO_SEL 0x00000001 +#define CCTRL_4330_ERCX_SEL 0x00000002 +#define CCTRL_4330_SDIO_HOST_WAKE 0x00000004 +#define CCTRL_4330_JTAG_DISABLE 0x00000008 + + +#define CCTRL_43239_GPIO_SEL 0x00000002 +#define CCTRL_43239_SDIO_HOST_WAKE 0x00000004 + +#define RES4313_BB_PU_RSRC 0 +#define RES4313_ILP_REQ_RSRC 1 +#define RES4313_XTAL_PU_RSRC 2 +#define RES4313_ALP_AVAIL_RSRC 3 +#define RES4313_RADIO_PU_RSRC 4 +#define RES4313_BG_PU_RSRC 5 +#define RES4313_VREG1P4_PU_RSRC 6 +#define RES4313_AFE_PWRSW_RSRC 7 +#define RES4313_RX_PWRSW_RSRC 8 +#define RES4313_TX_PWRSW_RSRC 9 +#define RES4313_BB_PWRSW_RSRC 10 +#define RES4313_SYNTH_PWRSW_RSRC 11 +#define RES4313_MISC_PWRSW_RSRC 12 +#define RES4313_BB_PLL_PWRSW_RSRC 13 +#define RES4313_HT_AVAIL_RSRC 14 +#define RES4313_MACPHY_CLK_AVAIL_RSRC 15 + + +#define CST4313_SPROM_PRESENT 1 +#define CST4313_OTP_PRESENT 2 +#define CST4313_SPROM_OTP_SEL_MASK 0x00000002 +#define CST4313_SPROM_OTP_SEL_SHIFT 0 + + +#define CCTRL_4313_12MA_LED_DRIVE 0x00000007 + + +#define RES43228_NOT_USED 0 +#define RES43228_ILP_REQUEST 1 +#define RES43228_XTAL_PU 2 +#define RES43228_ALP_AVAIL 3 +#define RES43228_PLL_EN 4 +#define RES43228_HT_PHY_AVAIL 5 + + +#define CST43228_ILP_DIV_EN 0x1 +#define CST43228_OTP_PRESENT 0x2 +#define CST43228_SERDES_REFCLK_PADSEL 0x4 +#define CST43228_SDIO_MODE 0x8 +#define CST43228_SDIO_OTP_PRESENT 0x10 +#define CST43228_SDIO_RESET 0x20 + + +#define PMU_MAX_TRANSITION_DLY 15000 + + +#define PMURES_UP_TRANSITION 2 + + + +#define SECI_MODE_UART 0x0 +#define SECI_MODE_SECI 0x1 +#define SECI_MODE_LEGACY_3WIRE_BT 0x2 +#define SECI_MODE_LEGACY_3WIRE_WLAN 0x3 +#define SECI_MODE_HALF_SECI 0x4 + +#define SECI_RESET (1 << 0) +#define SECI_RESET_BAR_UART (1 << 1) +#define SECI_ENAB_SECI_ECI (1 << 2) +#define SECI_ENAB_SECIOUT_DIS (1 << 3) +#define SECI_MODE_MASK 0x7 +#define SECI_MODE_SHIFT 4 +#define SECI_UPD_SECI (1 << 7) + + +#define CLKCTL_STS_SECI_CLK_REQ (1 << 8) +#define CLKCTL_STS_SECI_CLK_AVAIL (1 << 24) + +#define SECI_UART_MSR_CTS_STATE (1 << 0) +#define SECI_UART_MSR_RTS_STATE (1 << 1) +#define SECI_UART_SECI_IN_STATE (1 << 2) +#define SECI_UART_SECI_IN2_STATE (1 << 3) + + +#define SECI_UART_LCR_STOP_BITS (1 << 0) +#define SECI_UART_LCR_PARITY_EN (1 << 1) +#define SECI_UART_LCR_PARITY (1 << 2) +#define SECI_UART_LCR_RX_EN (1 << 3) +#define SECI_UART_LCR_LBRK_CTRL (1 << 4) +#define SECI_UART_LCR_TXO_EN (1 << 5) +#define SECI_UART_LCR_RTSO_EN (1 << 6) +#define SECI_UART_LCR_SLIPMODE_EN (1 << 7) +#define SECI_UART_LCR_RXCRC_CHK (1 << 8) +#define SECI_UART_LCR_TXCRC_INV (1 << 9) +#define SECI_UART_LCR_TXCRC_LSBF (1 << 10) +#define SECI_UART_LCR_TXCRC_EN (1 << 11) + +#define SECI_UART_MCR_TX_EN (1 << 0) +#define SECI_UART_MCR_PRTS (1 << 1) +#define SECI_UART_MCR_SWFLCTRL_EN (1 << 2) +#define SECI_UART_MCR_HIGHRATE_EN (1 << 3) +#define SECI_UART_MCR_LOOPBK_EN (1 << 4) +#define SECI_UART_MCR_AUTO_RTS (1 << 5) +#define SECI_UART_MCR_AUTO_TX_DIS (1 << 6) +#define SECI_UART_MCR_BAUD_ADJ_EN (1 << 7) +#define SECI_UART_MCR_XONOFF_RPT (1 << 9) + + + + +#define ECI_BW_20 0x0 +#define ECI_BW_25 0x1 +#define ECI_BW_30 0x2 +#define ECI_BW_35 0x3 +#define ECI_BW_40 0x4 +#define ECI_BW_45 0x5 +#define ECI_BW_50 0x6 +#define ECI_BW_ALL 0x7 + + +#define WLAN_NUM_ANT1 TXANT_0 +#define WLAN_NUM_ANT2 TXANT_1 + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/sbconfig.h b/drivers/net/wireless/bcmdhd/include/sbconfig.h new file mode 100644 index 0000000000000..f45351a586cb1 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sbconfig.h @@ -0,0 +1,276 @@ +/* + * Broadcom SiliconBackplane hardware register definitions. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sbconfig.h 277737 2011-08-16 17:54:59Z $ + */ + + +#ifndef _SBCONFIG_H +#define _SBCONFIG_H + + +#ifndef PAD +#define _PADLINE(line) pad ## line +#define _XSTR(line) _PADLINE(line) +#define PAD _XSTR(__LINE__) +#endif + + +#define SB_BUS_SIZE 0x10000 +#define SB_BUS_BASE(b) (SI_ENUM_BASE + (b) * SB_BUS_SIZE) +#define SB_BUS_MAXCORES (SB_BUS_SIZE / SI_CORE_SIZE) + + +#define SBCONFIGOFF 0xf00 +#define SBCONFIGSIZE 256 + +#define SBIPSFLAG 0x08 +#define SBTPSFLAG 0x18 +#define SBTMERRLOGA 0x48 +#define SBTMERRLOG 0x50 +#define SBADMATCH3 0x60 +#define SBADMATCH2 0x68 +#define SBADMATCH1 0x70 +#define SBIMSTATE 0x90 +#define SBINTVEC 0x94 +#define SBTMSTATELOW 0x98 +#define SBTMSTATEHIGH 0x9c +#define SBBWA0 0xa0 +#define SBIMCONFIGLOW 0xa8 +#define SBIMCONFIGHIGH 0xac +#define SBADMATCH0 0xb0 +#define SBTMCONFIGLOW 0xb8 +#define SBTMCONFIGHIGH 0xbc +#define SBBCONFIG 0xc0 +#define SBBSTATE 0xc8 +#define SBACTCNFG 0xd8 +#define SBFLAGST 0xe8 +#define SBIDLOW 0xf8 +#define SBIDHIGH 0xfc + + + +#define SBIMERRLOGA 0xea8 +#define SBIMERRLOG 0xeb0 +#define SBTMPORTCONNID0 0xed8 +#define SBTMPORTLOCK0 0xef8 + +#ifndef _LANGUAGE_ASSEMBLY + +typedef volatile struct _sbconfig { + uint32 PAD[2]; + uint32 sbipsflag; + uint32 PAD[3]; + uint32 sbtpsflag; + uint32 PAD[11]; + uint32 sbtmerrloga; + uint32 PAD; + uint32 sbtmerrlog; + uint32 PAD[3]; + uint32 sbadmatch3; + uint32 PAD; + uint32 sbadmatch2; + uint32 PAD; + uint32 sbadmatch1; + uint32 PAD[7]; + uint32 sbimstate; + uint32 sbintvec; + uint32 sbtmstatelow; + uint32 sbtmstatehigh; + uint32 sbbwa0; + uint32 PAD; + uint32 sbimconfiglow; + uint32 sbimconfighigh; + uint32 sbadmatch0; + uint32 PAD; + uint32 sbtmconfiglow; + uint32 sbtmconfighigh; + uint32 sbbconfig; + uint32 PAD; + uint32 sbbstate; + uint32 PAD[3]; + uint32 sbactcnfg; + uint32 PAD[3]; + uint32 sbflagst; + uint32 PAD[3]; + uint32 sbidlow; + uint32 sbidhigh; +} sbconfig_t; + +#endif + + +#define SBIPS_INT1_MASK 0x3f +#define SBIPS_INT1_SHIFT 0 +#define SBIPS_INT2_MASK 0x3f00 +#define SBIPS_INT2_SHIFT 8 +#define SBIPS_INT3_MASK 0x3f0000 +#define SBIPS_INT3_SHIFT 16 +#define SBIPS_INT4_MASK 0x3f000000 +#define SBIPS_INT4_SHIFT 24 + + +#define SBTPS_NUM0_MASK 0x3f +#define SBTPS_F0EN0 0x40 + + +#define SBTMEL_CM 0x00000007 +#define SBTMEL_CI 0x0000ff00 +#define SBTMEL_EC 0x0f000000 +#define SBTMEL_ME 0x80000000 + + +#define SBIM_PC 0xf +#define SBIM_AP_MASK 0x30 +#define SBIM_AP_BOTH 0x00 +#define SBIM_AP_TS 0x10 +#define SBIM_AP_TK 0x20 +#define SBIM_AP_RSV 0x30 +#define SBIM_IBE 0x20000 +#define SBIM_TO 0x40000 +#define SBIM_BY 0x01800000 +#define SBIM_RJ 0x02000000 + + +#define SBTML_RESET 0x0001 +#define SBTML_REJ_MASK 0x0006 +#define SBTML_REJ 0x0002 +#define SBTML_TMPREJ 0x0004 + +#define SBTML_SICF_SHIFT 16 + + +#define SBTMH_SERR 0x0001 +#define SBTMH_INT 0x0002 +#define SBTMH_BUSY 0x0004 +#define SBTMH_TO 0x0020 + +#define SBTMH_SISF_SHIFT 16 + + +#define SBBWA_TAB0_MASK 0xffff +#define SBBWA_TAB1_MASK 0xffff +#define SBBWA_TAB1_SHIFT 16 + + +#define SBIMCL_STO_MASK 0x7 +#define SBIMCL_RTO_MASK 0x70 +#define SBIMCL_RTO_SHIFT 4 +#define SBIMCL_CID_MASK 0xff0000 +#define SBIMCL_CID_SHIFT 16 + + +#define SBIMCH_IEM_MASK 0xc +#define SBIMCH_TEM_MASK 0x30 +#define SBIMCH_TEM_SHIFT 4 +#define SBIMCH_BEM_MASK 0xc0 +#define SBIMCH_BEM_SHIFT 6 + + +#define SBAM_TYPE_MASK 0x3 +#define SBAM_AD64 0x4 +#define SBAM_ADINT0_MASK 0xf8 +#define SBAM_ADINT0_SHIFT 3 +#define SBAM_ADINT1_MASK 0x1f8 +#define SBAM_ADINT1_SHIFT 3 +#define SBAM_ADINT2_MASK 0x1f8 +#define SBAM_ADINT2_SHIFT 3 +#define SBAM_ADEN 0x400 +#define SBAM_ADNEG 0x800 +#define SBAM_BASE0_MASK 0xffffff00 +#define SBAM_BASE0_SHIFT 8 +#define SBAM_BASE1_MASK 0xfffff000 +#define SBAM_BASE1_SHIFT 12 +#define SBAM_BASE2_MASK 0xffff0000 +#define SBAM_BASE2_SHIFT 16 + + +#define SBTMCL_CD_MASK 0xff +#define SBTMCL_CO_MASK 0xf800 +#define SBTMCL_CO_SHIFT 11 +#define SBTMCL_IF_MASK 0xfc0000 +#define SBTMCL_IF_SHIFT 18 +#define SBTMCL_IM_MASK 0x3000000 +#define SBTMCL_IM_SHIFT 24 + + +#define SBTMCH_BM_MASK 0x3 +#define SBTMCH_RM_MASK 0x3 +#define SBTMCH_RM_SHIFT 2 +#define SBTMCH_SM_MASK 0x30 +#define SBTMCH_SM_SHIFT 4 +#define SBTMCH_EM_MASK 0x300 +#define SBTMCH_EM_SHIFT 8 +#define SBTMCH_IM_MASK 0xc00 +#define SBTMCH_IM_SHIFT 10 + + +#define SBBC_LAT_MASK 0x3 +#define SBBC_MAX0_MASK 0xf0000 +#define SBBC_MAX0_SHIFT 16 +#define SBBC_MAX1_MASK 0xf00000 +#define SBBC_MAX1_SHIFT 20 + + +#define SBBS_SRD 0x1 +#define SBBS_HRD 0x2 + + +#define SBIDL_CS_MASK 0x3 +#define SBIDL_AR_MASK 0x38 +#define SBIDL_AR_SHIFT 3 +#define SBIDL_SYNCH 0x40 +#define SBIDL_INIT 0x80 +#define SBIDL_MINLAT_MASK 0xf00 +#define SBIDL_MINLAT_SHIFT 8 +#define SBIDL_MAXLAT 0xf000 +#define SBIDL_MAXLAT_SHIFT 12 +#define SBIDL_FIRST 0x10000 +#define SBIDL_CW_MASK 0xc0000 +#define SBIDL_CW_SHIFT 18 +#define SBIDL_TP_MASK 0xf00000 +#define SBIDL_TP_SHIFT 20 +#define SBIDL_IP_MASK 0xf000000 +#define SBIDL_IP_SHIFT 24 +#define SBIDL_RV_MASK 0xf0000000 +#define SBIDL_RV_SHIFT 28 +#define SBIDL_RV_2_2 0x00000000 +#define SBIDL_RV_2_3 0x10000000 + + +#define SBIDH_RC_MASK 0x000f +#define SBIDH_RCE_MASK 0x7000 +#define SBIDH_RCE_SHIFT 8 +#define SBCOREREV(sbidh) \ + ((((sbidh) & SBIDH_RCE_MASK) >> SBIDH_RCE_SHIFT) | ((sbidh) & SBIDH_RC_MASK)) +#define SBIDH_CC_MASK 0x8ff0 +#define SBIDH_CC_SHIFT 4 +#define SBIDH_VC_MASK 0xffff0000 +#define SBIDH_VC_SHIFT 16 + +#define SB_COMMIT 0xfd8 + + +#define SB_VEND_BCM 0x4243 + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/sbhnddma.h b/drivers/net/wireless/bcmdhd/include/sbhnddma.h new file mode 100644 index 0000000000000..77c413f75f0d4 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sbhnddma.h @@ -0,0 +1,327 @@ +/* + * Generic Broadcom Home Networking Division (HND) DMA engine HW interface + * This supports the following chips: BCM42xx, 44xx, 47xx . + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sbhnddma.h 278779 2011-08-19 22:07:18Z $ + */ + + +#ifndef _sbhnddma_h_ +#define _sbhnddma_h_ + + + + + + + +typedef volatile struct { + uint32 control; + uint32 addr; + uint32 ptr; + uint32 status; +} dma32regs_t; + +typedef volatile struct { + dma32regs_t xmt; + dma32regs_t rcv; +} dma32regp_t; + +typedef volatile struct { + uint32 fifoaddr; + uint32 fifodatalow; + uint32 fifodatahigh; + uint32 pad; +} dma32diag_t; + + +typedef volatile struct { + uint32 ctrl; + uint32 addr; +} dma32dd_t; + + +#define D32RINGALIGN_BITS 12 +#define D32MAXRINGSZ (1 << D32RINGALIGN_BITS) +#define D32RINGALIGN (1 << D32RINGALIGN_BITS) + +#define D32MAXDD (D32MAXRINGSZ / sizeof (dma32dd_t)) + + +#define XC_XE ((uint32)1 << 0) +#define XC_SE ((uint32)1 << 1) +#define XC_LE ((uint32)1 << 2) +#define XC_FL ((uint32)1 << 4) +#define XC_PD ((uint32)1 << 11) +#define XC_AE ((uint32)3 << 16) +#define XC_AE_SHIFT 16 +#define XC_BL_MASK 0x001C0000 +#define XC_BL_SHIFT 18 + + +#define XP_LD_MASK 0xfff + + +#define XS_CD_MASK 0x0fff +#define XS_XS_MASK 0xf000 +#define XS_XS_SHIFT 12 +#define XS_XS_DISABLED 0x0000 +#define XS_XS_ACTIVE 0x1000 +#define XS_XS_IDLE 0x2000 +#define XS_XS_STOPPED 0x3000 +#define XS_XS_SUSP 0x4000 +#define XS_XE_MASK 0xf0000 +#define XS_XE_SHIFT 16 +#define XS_XE_NOERR 0x00000 +#define XS_XE_DPE 0x10000 +#define XS_XE_DFU 0x20000 +#define XS_XE_BEBR 0x30000 +#define XS_XE_BEDA 0x40000 +#define XS_AD_MASK 0xfff00000 +#define XS_AD_SHIFT 20 + + +#define RC_RE ((uint32)1 << 0) +#define RC_RO_MASK 0xfe +#define RC_RO_SHIFT 1 +#define RC_FM ((uint32)1 << 8) +#define RC_SH ((uint32)1 << 9) +#define RC_OC ((uint32)1 << 10) +#define RC_PD ((uint32)1 << 11) +#define RC_AE ((uint32)3 << 16) +#define RC_AE_SHIFT 16 +#define RC_BL_MASK 0x001C0000 +#define RC_BL_SHIFT 18 + + +#define RP_LD_MASK 0xfff + + +#define RS_CD_MASK 0x0fff +#define RS_RS_MASK 0xf000 +#define RS_RS_SHIFT 12 +#define RS_RS_DISABLED 0x0000 +#define RS_RS_ACTIVE 0x1000 +#define RS_RS_IDLE 0x2000 +#define RS_RS_STOPPED 0x3000 +#define RS_RE_MASK 0xf0000 +#define RS_RE_SHIFT 16 +#define RS_RE_NOERR 0x00000 +#define RS_RE_DPE 0x10000 +#define RS_RE_DFO 0x20000 +#define RS_RE_BEBW 0x30000 +#define RS_RE_BEDA 0x40000 +#define RS_AD_MASK 0xfff00000 +#define RS_AD_SHIFT 20 + + +#define FA_OFF_MASK 0xffff +#define FA_SEL_MASK 0xf0000 +#define FA_SEL_SHIFT 16 +#define FA_SEL_XDD 0x00000 +#define FA_SEL_XDP 0x10000 +#define FA_SEL_RDD 0x40000 +#define FA_SEL_RDP 0x50000 +#define FA_SEL_XFD 0x80000 +#define FA_SEL_XFP 0x90000 +#define FA_SEL_RFD 0xc0000 +#define FA_SEL_RFP 0xd0000 +#define FA_SEL_RSD 0xe0000 +#define FA_SEL_RSP 0xf0000 + + +#define CTRL_BC_MASK 0x00001fff +#define CTRL_AE ((uint32)3 << 16) +#define CTRL_AE_SHIFT 16 +#define CTRL_PARITY ((uint32)3 << 18) +#define CTRL_EOT ((uint32)1 << 28) +#define CTRL_IOC ((uint32)1 << 29) +#define CTRL_EOF ((uint32)1 << 30) +#define CTRL_SOF ((uint32)1 << 31) + + +#define CTRL_CORE_MASK 0x0ff00000 + + + + +typedef volatile struct { + uint32 control; + uint32 ptr; + uint32 addrlow; + uint32 addrhigh; + uint32 status0; + uint32 status1; +} dma64regs_t; + +typedef volatile struct { + dma64regs_t tx; + dma64regs_t rx; +} dma64regp_t; + +typedef volatile struct { + uint32 fifoaddr; + uint32 fifodatalow; + uint32 fifodatahigh; + uint32 pad; +} dma64diag_t; + + +typedef volatile struct { + uint32 ctrl1; + uint32 ctrl2; + uint32 addrlow; + uint32 addrhigh; +} dma64dd_t; + + +#define D64RINGALIGN_BITS 13 +#define D64MAXRINGSZ (1 << D64RINGALIGN_BITS) +#define D64RINGALIGN (1 << D64RINGALIGN_BITS) + +#define D64MAXDD (D64MAXRINGSZ / sizeof (dma64dd_t)) + + +#define D64_DEF_USBBURSTLEN 2 +#define D64_DEF_SDIOBURSTLEN 1 + + +#define D64_XC_XE 0x00000001 +#define D64_XC_SE 0x00000002 +#define D64_XC_LE 0x00000004 +#define D64_XC_FL 0x00000010 +#define D64_XC_PD 0x00000800 +#define D64_XC_AE 0x00030000 +#define D64_XC_AE_SHIFT 16 +#define D64_XC_BL_MASK 0x001C0000 +#define D64_XC_BL_SHIFT 18 + + +#define D64_XP_LD_MASK 0x00001fff + + +#define D64_XS0_CD_MASK 0x00001fff +#define D64_XS0_XS_MASK 0xf0000000 +#define D64_XS0_XS_SHIFT 28 +#define D64_XS0_XS_DISABLED 0x00000000 +#define D64_XS0_XS_ACTIVE 0x10000000 +#define D64_XS0_XS_IDLE 0x20000000 +#define D64_XS0_XS_STOPPED 0x30000000 +#define D64_XS0_XS_SUSP 0x40000000 + +#define D64_XS1_AD_MASK 0x00001fff +#define D64_XS1_XE_MASK 0xf0000000 +#define D64_XS1_XE_SHIFT 28 +#define D64_XS1_XE_NOERR 0x00000000 +#define D64_XS1_XE_DPE 0x10000000 +#define D64_XS1_XE_DFU 0x20000000 +#define D64_XS1_XE_DTE 0x30000000 +#define D64_XS1_XE_DESRE 0x40000000 +#define D64_XS1_XE_COREE 0x50000000 + + +#define D64_RC_RE 0x00000001 +#define D64_RC_RO_MASK 0x000000fe +#define D64_RC_RO_SHIFT 1 +#define D64_RC_FM 0x00000100 +#define D64_RC_SH 0x00000200 +#define D64_RC_OC 0x00000400 +#define D64_RC_PD 0x00000800 +#define D64_RC_AE 0x00030000 +#define D64_RC_AE_SHIFT 16 +#define D64_RC_BL_MASK 0x001C0000 +#define D64_RC_BL_SHIFT 18 + + +#define DMA_CTRL_PEN (1 << 0) +#define DMA_CTRL_ROC (1 << 1) +#define DMA_CTRL_RXMULTI (1 << 2) +#define DMA_CTRL_UNFRAMED (1 << 3) +#define DMA_CTRL_USB_BOUNDRY4KB_WAR (1 << 4) + + +#define D64_RP_LD_MASK 0x00001fff + + +#define D64_RS0_CD_MASK 0x00001fff +#define D64_RS0_RS_MASK 0xf0000000 +#define D64_RS0_RS_SHIFT 28 +#define D64_RS0_RS_DISABLED 0x00000000 +#define D64_RS0_RS_ACTIVE 0x10000000 +#define D64_RS0_RS_IDLE 0x20000000 +#define D64_RS0_RS_STOPPED 0x30000000 +#define D64_RS0_RS_SUSP 0x40000000 + +#define D64_RS1_AD_MASK 0x0001ffff +#define D64_RS1_RE_MASK 0xf0000000 +#define D64_RS1_RE_SHIFT 28 +#define D64_RS1_RE_NOERR 0x00000000 +#define D64_RS1_RE_DPO 0x10000000 +#define D64_RS1_RE_DFU 0x20000000 +#define D64_RS1_RE_DTE 0x30000000 +#define D64_RS1_RE_DESRE 0x40000000 +#define D64_RS1_RE_COREE 0x50000000 + + +#define D64_FA_OFF_MASK 0xffff +#define D64_FA_SEL_MASK 0xf0000 +#define D64_FA_SEL_SHIFT 16 +#define D64_FA_SEL_XDD 0x00000 +#define D64_FA_SEL_XDP 0x10000 +#define D64_FA_SEL_RDD 0x40000 +#define D64_FA_SEL_RDP 0x50000 +#define D64_FA_SEL_XFD 0x80000 +#define D64_FA_SEL_XFP 0x90000 +#define D64_FA_SEL_RFD 0xc0000 +#define D64_FA_SEL_RFP 0xd0000 +#define D64_FA_SEL_RSD 0xe0000 +#define D64_FA_SEL_RSP 0xf0000 + + +#define D64_CTRL_COREFLAGS 0x0ff00000 +#define D64_CTRL1_EOT ((uint32)1 << 28) +#define D64_CTRL1_IOC ((uint32)1 << 29) +#define D64_CTRL1_EOF ((uint32)1 << 30) +#define D64_CTRL1_SOF ((uint32)1 << 31) + + +#define D64_CTRL2_BC_MASK 0x00007fff +#define D64_CTRL2_AE 0x00030000 +#define D64_CTRL2_AE_SHIFT 16 +#define D64_CTRL2_PARITY 0x00040000 + + +#define D64_CTRL_CORE_MASK 0x0ff00000 + +#define D64_RX_FRM_STS_LEN 0x0000ffff +#define D64_RX_FRM_STS_OVFL 0x00800000 +#define D64_RX_FRM_STS_DSCRCNT 0x0f000000 +#define D64_RX_FRM_STS_DATATYPE 0xf0000000 + + +typedef volatile struct { + uint16 len; + uint16 flags; +} dma_rxh_t; + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/sbpcmcia.h b/drivers/net/wireless/bcmdhd/include/sbpcmcia.h new file mode 100644 index 0000000000000..d84f69ab5617a --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sbpcmcia.h @@ -0,0 +1,109 @@ +/* + * BCM43XX Sonics SiliconBackplane PCMCIA core hardware definitions. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sbpcmcia.h 277737 2011-08-16 17:54:59Z $ + */ + + +#ifndef _SBPCMCIA_H +#define _SBPCMCIA_H + + + + +#define PCMCIA_FCR (0x700 / 2) + +#define FCR0_OFF 0 +#define FCR1_OFF (0x40 / 2) +#define FCR2_OFF (0x80 / 2) +#define FCR3_OFF (0xc0 / 2) + +#define PCMCIA_FCR0 (0x700 / 2) +#define PCMCIA_FCR1 (0x740 / 2) +#define PCMCIA_FCR2 (0x780 / 2) +#define PCMCIA_FCR3 (0x7c0 / 2) + + + +#define PCMCIA_COR 0 + +#define COR_RST 0x80 +#define COR_LEV 0x40 +#define COR_IRQEN 0x04 +#define COR_BLREN 0x01 +#define COR_FUNEN 0x01 + + +#define PCICIA_FCSR (2 / 2) +#define PCICIA_PRR (4 / 2) +#define PCICIA_SCR (6 / 2) +#define PCICIA_ESR (8 / 2) + + +#define PCM_MEMOFF 0x0000 +#define F0_MEMOFF 0x1000 +#define F1_MEMOFF 0x2000 +#define F2_MEMOFF 0x3000 +#define F3_MEMOFF 0x4000 + + +#define MEM_ADDR0 (0x728 / 2) +#define MEM_ADDR1 (0x72a / 2) +#define MEM_ADDR2 (0x72c / 2) + + +#define PCMCIA_ADDR0 (0x072e / 2) +#define PCMCIA_ADDR1 (0x0730 / 2) +#define PCMCIA_ADDR2 (0x0732 / 2) + +#define MEM_SEG (0x0734 / 2) +#define SROM_CS (0x0736 / 2) +#define SROM_DATAL (0x0738 / 2) +#define SROM_DATAH (0x073a / 2) +#define SROM_ADDRL (0x073c / 2) +#define SROM_ADDRH (0x073e / 2) +#define SROM_INFO2 (0x0772 / 2) +#define SROM_INFO (0x07be / 2) + + +#define SROM_IDLE 0 +#define SROM_WRITE 1 +#define SROM_READ 2 +#define SROM_WEN 4 +#define SROM_WDS 7 +#define SROM_DONE 8 + + +#define SRI_SZ_MASK 0x03 +#define SRI_BLANK 0x04 +#define SRI_OTP 0x80 + + + +#define SBTML_INT_ACK 0x40000 +#define SBTML_INT_EN 0x20000 + + +#define SBTMH_INT_STATUS 0x40000 + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/sbsdio.h b/drivers/net/wireless/bcmdhd/include/sbsdio.h new file mode 100644 index 0000000000000..7aaeb73f01003 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sbsdio.h @@ -0,0 +1,166 @@ +/* + * SDIO device core hardware definitions. + * sdio is a portion of the pcmcia core in core rev 3 - rev 8 + * + * SDIO core support 1bit, 4 bit SDIO mode as well as SPI mode. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sbsdio.h 277737 2011-08-16 17:54:59Z $ + */ + +#ifndef _SBSDIO_H +#define _SBSDIO_H + +#define SBSDIO_NUM_FUNCTION 3 /* as of sdiod rev 0, supports 3 functions */ + +/* function 1 miscellaneous registers */ +#define SBSDIO_SPROM_CS 0x10000 /* sprom command and status */ +#define SBSDIO_SPROM_INFO 0x10001 /* sprom info register */ +#define SBSDIO_SPROM_DATA_LOW 0x10002 /* sprom indirect access data byte 0 */ +#define SBSDIO_SPROM_DATA_HIGH 0x10003 /* sprom indirect access data byte 1 */ +#define SBSDIO_SPROM_ADDR_LOW 0x10004 /* sprom indirect access addr byte 0 */ +#define SBSDIO_SPROM_ADDR_HIGH 0x10005 /* sprom indirect access addr byte 0 */ +#define SBSDIO_CHIP_CTRL_DATA 0x10006 /* xtal_pu (gpio) output */ +#define SBSDIO_CHIP_CTRL_EN 0x10007 /* xtal_pu (gpio) enable */ +#define SBSDIO_WATERMARK 0x10008 /* rev < 7, watermark for sdio device */ +#define SBSDIO_DEVICE_CTL 0x10009 /* control busy signal generation */ + +/* registers introduced in rev 8, some content (mask/bits) defs in sbsdpcmdev.h */ +#define SBSDIO_FUNC1_SBADDRLOW 0x1000A /* SB Address Window Low (b15) */ +#define SBSDIO_FUNC1_SBADDRMID 0x1000B /* SB Address Window Mid (b23:b16) */ +#define SBSDIO_FUNC1_SBADDRHIGH 0x1000C /* SB Address Window High (b31:b24) */ +#define SBSDIO_FUNC1_FRAMECTRL 0x1000D /* Frame Control (frame term/abort) */ +#define SBSDIO_FUNC1_CHIPCLKCSR 0x1000E /* ChipClockCSR (ALP/HT ctl/status) */ +#define SBSDIO_FUNC1_SDIOPULLUP 0x1000F /* SdioPullUp (on cmd, d0-d2) */ +#define SBSDIO_FUNC1_WFRAMEBCLO 0x10019 /* Write Frame Byte Count Low */ +#define SBSDIO_FUNC1_WFRAMEBCHI 0x1001A /* Write Frame Byte Count High */ +#define SBSDIO_FUNC1_RFRAMEBCLO 0x1001B /* Read Frame Byte Count Low */ +#define SBSDIO_FUNC1_RFRAMEBCHI 0x1001C /* Read Frame Byte Count High */ + +#define SBSDIO_FUNC1_MISC_REG_START 0x10000 /* f1 misc register start */ +#define SBSDIO_FUNC1_MISC_REG_LIMIT 0x1001C /* f1 misc register end */ + +/* SBSDIO_SPROM_CS */ +#define SBSDIO_SPROM_IDLE 0 +#define SBSDIO_SPROM_WRITE 1 +#define SBSDIO_SPROM_READ 2 +#define SBSDIO_SPROM_WEN 4 +#define SBSDIO_SPROM_WDS 7 +#define SBSDIO_SPROM_DONE 8 + +/* SBSDIO_SPROM_INFO */ +#define SROM_SZ_MASK 0x03 /* SROM size, 1: 4k, 2: 16k */ +#define SROM_BLANK 0x04 /* depreciated in corerev 6 */ +#define SROM_OTP 0x80 /* OTP present */ + +/* SBSDIO_CHIP_CTRL */ +#define SBSDIO_CHIP_CTRL_XTAL 0x01 /* or'd with onchip xtal_pu, + * 1: power on oscillator + * (for 4318 only) + */ +/* SBSDIO_WATERMARK */ +#define SBSDIO_WATERMARK_MASK 0x7f /* number of words - 1 for sd device + * to wait before sending data to host + */ + +/* SBSDIO_DEVICE_CTL */ +#define SBSDIO_DEVCTL_SETBUSY 0x01 /* 1: device will assert busy signal when + * receiving CMD53 + */ +#define SBSDIO_DEVCTL_SPI_INTR_SYNC 0x02 /* 1: assertion of sdio interrupt is + * synchronous to the sdio clock + */ +#define SBSDIO_DEVCTL_CA_INT_ONLY 0x04 /* 1: mask all interrupts to host + * except the chipActive (rev 8) + */ +#define SBSDIO_DEVCTL_PADS_ISO 0x08 /* 1: isolate internal sdio signals, put + * external pads in tri-state; requires + * sdio bus power cycle to clear (rev 9) + */ +#define SBSDIO_DEVCTL_SB_RST_CTL 0x30 /* Force SD->SB reset mapping (rev 11) */ +#define SBSDIO_DEVCTL_RST_CORECTL 0x00 /* Determined by CoreControl bit */ +#define SBSDIO_DEVCTL_RST_BPRESET 0x10 /* Force backplane reset */ +#define SBSDIO_DEVCTL_RST_NOBPRESET 0x20 /* Force no backplane reset */ + + +/* SBSDIO_FUNC1_CHIPCLKCSR */ +#define SBSDIO_FORCE_ALP 0x01 /* Force ALP request to backplane */ +#define SBSDIO_FORCE_HT 0x02 /* Force HT request to backplane */ +#define SBSDIO_FORCE_ILP 0x04 /* Force ILP request to backplane */ +#define SBSDIO_ALP_AVAIL_REQ 0x08 /* Make ALP ready (power up xtal) */ +#define SBSDIO_HT_AVAIL_REQ 0x10 /* Make HT ready (power up PLL) */ +#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 /* Squelch clock requests from HW */ +#define SBSDIO_ALP_AVAIL 0x40 /* Status: ALP is ready */ +#define SBSDIO_HT_AVAIL 0x80 /* Status: HT is ready */ +/* In rev8, actual avail bits followed original docs */ +#define SBSDIO_Rev8_HT_AVAIL 0x40 +#define SBSDIO_Rev8_ALP_AVAIL 0x80 + +#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL) +#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS) +#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS) +#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval)) +#define SBSDIO_CLKAV(regval, alponly) (SBSDIO_ALPAV(regval) && \ + (alponly ? 1 : SBSDIO_HTAV(regval))) + +/* SBSDIO_FUNC1_SDIOPULLUP */ +#define SBSDIO_PULLUP_D0 0x01 /* Enable D0/MISO pullup */ +#define SBSDIO_PULLUP_D1 0x02 /* Enable D1/INT# pullup */ +#define SBSDIO_PULLUP_D2 0x04 /* Enable D2 pullup */ +#define SBSDIO_PULLUP_CMD 0x08 /* Enable CMD/MOSI pullup */ +#define SBSDIO_PULLUP_ALL 0x0f /* All valid bits */ + +/* function 1 OCP space */ +#define SBSDIO_SB_OFT_ADDR_MASK 0x07FFF /* sb offset addr is <= 15 bits, 32k */ +#define SBSDIO_SB_OFT_ADDR_LIMIT 0x08000 +#define SBSDIO_SB_ACCESS_2_4B_FLAG 0x08000 /* with b15, maps to 32-bit SB access */ + +/* some duplication with sbsdpcmdev.h here */ +/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */ +#define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid bits in SBADDRLOW */ +#define SBSDIO_SBADDRMID_MASK 0xff /* Valid bits in SBADDRMID */ +#define SBSDIO_SBADDRHIGH_MASK 0xffU /* Valid bits in SBADDRHIGH */ +#define SBSDIO_SBWINDOW_MASK 0xffff8000 /* Address bits from SBADDR regs */ + +/* direct(mapped) cis space */ +#define SBSDIO_CIS_BASE_COMMON 0x1000 /* MAPPED common CIS address */ +#define SBSDIO_CIS_SIZE_LIMIT 0x200 /* maximum bytes in one CIS */ +#define SBSDIO_OTP_CIS_SIZE_LIMIT 0x078 /* maximum bytes OTP CIS */ + +#define SBSDIO_CIS_OFT_ADDR_MASK 0x1FFFF /* cis offset addr is < 17 bits */ + +#define SBSDIO_CIS_MANFID_TUPLE_LEN 6 /* manfid tuple length, include tuple, + * link bytes + */ + +/* indirect cis access (in sprom) */ +#define SBSDIO_SPROM_CIS_OFFSET 0x8 /* 8 control bytes first, CIS starts from + * 8th byte + */ + +#define SBSDIO_BYTEMODE_DATALEN_MAX 64 /* sdio byte mode: maximum length of one + * data comamnd + */ + +#define SBSDIO_CORE_ADDR_MASK 0x1FFFF /* sdio core function one address mask */ + +#endif /* _SBSDIO_H */ diff --git a/drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h b/drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h new file mode 100644 index 0000000000000..e5176483e9a1d --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h @@ -0,0 +1,293 @@ +/* + * Broadcom SiliconBackplane SDIO/PCMCIA hardware-specific + * device core support + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sbsdpcmdev.h 282638 2011-09-08 21:18:10Z $ + */ + +#ifndef _sbsdpcmdev_h_ +#define _sbsdpcmdev_h_ + +/* cpp contortions to concatenate w/arg prescan */ +#ifndef PAD +#define _PADLINE(line) pad ## line +#define _XSTR(line) _PADLINE(line) +#define PAD _XSTR(__LINE__) +#endif /* PAD */ + + +typedef volatile struct { + dma64regs_t xmt; /* dma tx */ + uint32 PAD[2]; + dma64regs_t rcv; /* dma rx */ + uint32 PAD[2]; +} dma64p_t; + +/* dma64 sdiod corerev >= 1 */ +typedef volatile struct { + dma64p_t dma64regs[2]; + dma64diag_t dmafifo; /* DMA Diagnostic Regs, 0x280-0x28c */ + uint32 PAD[92]; +} sdiodma64_t; + +/* dma32 sdiod corerev == 0 */ +typedef volatile struct { + dma32regp_t dma32regs[2]; /* dma tx & rx, 0x200-0x23c */ + dma32diag_t dmafifo; /* DMA Diagnostic Regs, 0x240-0x24c */ + uint32 PAD[108]; +} sdiodma32_t; + +/* dma32 regs for pcmcia core */ +typedef volatile struct { + dma32regp_t dmaregs; /* DMA Regs, 0x200-0x21c, rev8 */ + dma32diag_t dmafifo; /* DMA Diagnostic Regs, 0x220-0x22c */ + uint32 PAD[116]; +} pcmdma32_t; + +/* core registers */ +typedef volatile struct { + uint32 corecontrol; /* CoreControl, 0x000, rev8 */ + uint32 corestatus; /* CoreStatus, 0x004, rev8 */ + uint32 PAD[1]; + uint32 biststatus; /* BistStatus, 0x00c, rev8 */ + + /* PCMCIA access */ + uint16 pcmciamesportaladdr; /* PcmciaMesPortalAddr, 0x010, rev8 */ + uint16 PAD[1]; + uint16 pcmciamesportalmask; /* PcmciaMesPortalMask, 0x014, rev8 */ + uint16 PAD[1]; + uint16 pcmciawrframebc; /* PcmciaWrFrameBC, 0x018, rev8 */ + uint16 PAD[1]; + uint16 pcmciaunderflowtimer; /* PcmciaUnderflowTimer, 0x01c, rev8 */ + uint16 PAD[1]; + + /* interrupt */ + uint32 intstatus; /* IntStatus, 0x020, rev8 */ + uint32 hostintmask; /* IntHostMask, 0x024, rev8 */ + uint32 intmask; /* IntSbMask, 0x028, rev8 */ + uint32 sbintstatus; /* SBIntStatus, 0x02c, rev8 */ + uint32 sbintmask; /* SBIntMask, 0x030, rev8 */ + uint32 funcintmask; /* SDIO Function Interrupt Mask, SDIO rev4 */ + uint32 PAD[2]; + uint32 tosbmailbox; /* ToSBMailbox, 0x040, rev8 */ + uint32 tohostmailbox; /* ToHostMailbox, 0x044, rev8 */ + uint32 tosbmailboxdata; /* ToSbMailboxData, 0x048, rev8 */ + uint32 tohostmailboxdata; /* ToHostMailboxData, 0x04c, rev8 */ + + /* synchronized access to registers in SDIO clock domain */ + uint32 sdioaccess; /* SdioAccess, 0x050, rev8 */ + uint32 PAD[3]; + + /* PCMCIA frame control */ + uint8 pcmciaframectrl; /* pcmciaFrameCtrl, 0x060, rev8 */ + uint8 PAD[3]; + uint8 pcmciawatermark; /* pcmciaWaterMark, 0x064, rev8 */ + uint8 PAD[155]; + + /* interrupt batching control */ + uint32 intrcvlazy; /* IntRcvLazy, 0x100, rev8 */ + uint32 PAD[3]; + + /* counters */ + uint32 cmd52rd; /* Cmd52RdCount, 0x110, rev8, SDIO: cmd52 reads */ + uint32 cmd52wr; /* Cmd52WrCount, 0x114, rev8, SDIO: cmd52 writes */ + uint32 cmd53rd; /* Cmd53RdCount, 0x118, rev8, SDIO: cmd53 reads */ + uint32 cmd53wr; /* Cmd53WrCount, 0x11c, rev8, SDIO: cmd53 writes */ + uint32 abort; /* AbortCount, 0x120, rev8, SDIO: aborts */ + uint32 datacrcerror; /* DataCrcErrorCount, 0x124, rev8, SDIO: frames w/bad CRC */ + uint32 rdoutofsync; /* RdOutOfSyncCount, 0x128, rev8, SDIO/PCMCIA: Rd Frm OOS */ + uint32 wroutofsync; /* RdOutOfSyncCount, 0x12c, rev8, SDIO/PCMCIA: Wr Frm OOS */ + uint32 writebusy; /* WriteBusyCount, 0x130, rev8, SDIO: dev asserted "busy" */ + uint32 readwait; /* ReadWaitCount, 0x134, rev8, SDIO: read: no data avail */ + uint32 readterm; /* ReadTermCount, 0x138, rev8, SDIO: rd frm terminates */ + uint32 writeterm; /* WriteTermCount, 0x13c, rev8, SDIO: wr frm terminates */ + uint32 PAD[40]; + uint32 clockctlstatus; /* ClockCtlStatus, 0x1e0, rev8 */ + uint32 PAD[7]; + + /* DMA engines */ + volatile union { + pcmdma32_t pcm32; + sdiodma32_t sdiod32; + sdiodma64_t sdiod64; + } dma; + + /* SDIO/PCMCIA CIS region */ + char cis[512]; /* 512 byte CIS, 0x400-0x5ff, rev6 */ + + /* PCMCIA function control registers */ + char pcmciafcr[256]; /* PCMCIA FCR, 0x600-6ff, rev6 */ + uint16 PAD[55]; + + /* PCMCIA backplane access */ + uint16 backplanecsr; /* BackplaneCSR, 0x76E, rev6 */ + uint16 backplaneaddr0; /* BackplaneAddr0, 0x770, rev6 */ + uint16 backplaneaddr1; /* BackplaneAddr1, 0x772, rev6 */ + uint16 backplaneaddr2; /* BackplaneAddr2, 0x774, rev6 */ + uint16 backplaneaddr3; /* BackplaneAddr3, 0x776, rev6 */ + uint16 backplanedata0; /* BackplaneData0, 0x778, rev6 */ + uint16 backplanedata1; /* BackplaneData1, 0x77a, rev6 */ + uint16 backplanedata2; /* BackplaneData2, 0x77c, rev6 */ + uint16 backplanedata3; /* BackplaneData3, 0x77e, rev6 */ + uint16 PAD[31]; + + /* sprom "size" & "blank" info */ + uint16 spromstatus; /* SPROMStatus, 0x7BE, rev2 */ + uint32 PAD[464]; + + /* Sonics SiliconBackplane registers */ + sbconfig_t sbconfig; /* SbConfig Regs, 0xf00-0xfff, rev8 */ +} sdpcmd_regs_t; + +/* corecontrol */ +#define CC_CISRDY (1 << 0) /* CIS Ready */ +#define CC_BPRESEN (1 << 1) /* CCCR RES signal causes backplane reset */ +#define CC_F2RDY (1 << 2) /* set CCCR IOR2 bit */ +#define CC_CLRPADSISO (1 << 3) /* clear SDIO pads isolation bit (rev 11) */ +#define CC_XMTDATAAVAIL_MODE (1 << 4) /* data avail generates an interrupt */ +#define CC_XMTDATAAVAIL_CTRL (1 << 5) /* data avail interrupt ctrl */ + +/* corestatus */ +#define CS_PCMCIAMODE (1 << 0) /* Device Mode; 0=SDIO, 1=PCMCIA */ +#define CS_SMARTDEV (1 << 1) /* 1=smartDev enabled */ +#define CS_F2ENABLED (1 << 2) /* 1=host has enabled the device */ + +#define PCMCIA_MES_PA_MASK 0x7fff /* PCMCIA Message Portal Address Mask */ +#define PCMCIA_MES_PM_MASK 0x7fff /* PCMCIA Message Portal Mask Mask */ +#define PCMCIA_WFBC_MASK 0xffff /* PCMCIA Write Frame Byte Count Mask */ +#define PCMCIA_UT_MASK 0x07ff /* PCMCIA Underflow Timer Mask */ + +/* intstatus */ +#define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */ +#define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */ +#define I_SMB_SW2 (1 << 2) /* To SB Mail S/W interrupt 2 */ +#define I_SMB_SW3 (1 << 3) /* To SB Mail S/W interrupt 3 */ +#define I_SMB_SW_MASK 0x0000000f /* To SB Mail S/W interrupts mask */ +#define I_SMB_SW_SHIFT 0 /* To SB Mail S/W interrupts shift */ +#define I_HMB_SW0 (1 << 4) /* To Host Mail S/W interrupt 0 */ +#define I_HMB_SW1 (1 << 5) /* To Host Mail S/W interrupt 1 */ +#define I_HMB_SW2 (1 << 6) /* To Host Mail S/W interrupt 2 */ +#define I_HMB_SW3 (1 << 7) /* To Host Mail S/W interrupt 3 */ +#define I_HMB_SW_MASK 0x000000f0 /* To Host Mail S/W interrupts mask */ +#define I_HMB_SW_SHIFT 4 /* To Host Mail S/W interrupts shift */ +#define I_WR_OOSYNC (1 << 8) /* Write Frame Out Of Sync */ +#define I_RD_OOSYNC (1 << 9) /* Read Frame Out Of Sync */ +#define I_PC (1 << 10) /* descriptor error */ +#define I_PD (1 << 11) /* data error */ +#define I_DE (1 << 12) /* Descriptor protocol Error */ +#define I_RU (1 << 13) /* Receive descriptor Underflow */ +#define I_RO (1 << 14) /* Receive fifo Overflow */ +#define I_XU (1 << 15) /* Transmit fifo Underflow */ +#define I_RI (1 << 16) /* Receive Interrupt */ +#define I_BUSPWR (1 << 17) /* SDIO Bus Power Change (rev 9) */ +#define I_XMTDATA_AVAIL (1 << 23) /* bits in fifo */ +#define I_XI (1 << 24) /* Transmit Interrupt */ +#define I_RF_TERM (1 << 25) /* Read Frame Terminate */ +#define I_WF_TERM (1 << 26) /* Write Frame Terminate */ +#define I_PCMCIA_XU (1 << 27) /* PCMCIA Transmit FIFO Underflow */ +#define I_SBINT (1 << 28) /* sbintstatus Interrupt */ +#define I_CHIPACTIVE (1 << 29) /* chip transitioned from doze to active state */ +#define I_SRESET (1 << 30) /* CCCR RES interrupt */ +#define I_IOE2 (1U << 31) /* CCCR IOE2 Bit Changed */ +#define I_ERRORS (I_PC | I_PD | I_DE | I_RU | I_RO | I_XU) /* DMA Errors */ +#define I_DMA (I_RI | I_XI | I_ERRORS) + +/* sbintstatus */ +#define I_SB_SERR (1 << 8) /* Backplane SError (write) */ +#define I_SB_RESPERR (1 << 9) /* Backplane Response Error (read) */ +#define I_SB_SPROMERR (1 << 10) /* Error accessing the sprom */ + +/* sdioaccess */ +#define SDA_DATA_MASK 0x000000ff /* Read/Write Data Mask */ +#define SDA_ADDR_MASK 0x000fff00 /* Read/Write Address Mask */ +#define SDA_ADDR_SHIFT 8 /* Read/Write Address Shift */ +#define SDA_WRITE 0x01000000 /* Write bit */ +#define SDA_READ 0x00000000 /* Write bit cleared for Read */ +#define SDA_BUSY 0x80000000 /* Busy bit */ + +/* sdioaccess-accessible register address spaces */ +#define SDA_CCCR_SPACE 0x000 /* sdioAccess CCCR register space */ +#define SDA_F1_FBR_SPACE 0x100 /* sdioAccess F1 FBR register space */ +#define SDA_F2_FBR_SPACE 0x200 /* sdioAccess F2 FBR register space */ +#define SDA_F1_REG_SPACE 0x300 /* sdioAccess F1 core-specific register space */ + +/* SDA_F1_REG_SPACE sdioaccess-accessible F1 reg space register offsets */ +#define SDA_CHIPCONTROLDATA 0x006 /* ChipControlData */ +#define SDA_CHIPCONTROLENAB 0x007 /* ChipControlEnable */ +#define SDA_F2WATERMARK 0x008 /* Function 2 Watermark */ +#define SDA_DEVICECONTROL 0x009 /* DeviceControl */ +#define SDA_SBADDRLOW 0x00a /* SbAddrLow */ +#define SDA_SBADDRMID 0x00b /* SbAddrMid */ +#define SDA_SBADDRHIGH 0x00c /* SbAddrHigh */ +#define SDA_FRAMECTRL 0x00d /* FrameCtrl */ +#define SDA_CHIPCLOCKCSR 0x00e /* ChipClockCSR */ +#define SDA_SDIOPULLUP 0x00f /* SdioPullUp */ +#define SDA_SDIOWRFRAMEBCLOW 0x019 /* SdioWrFrameBCLow */ +#define SDA_SDIOWRFRAMEBCHIGH 0x01a /* SdioWrFrameBCHigh */ +#define SDA_SDIORDFRAMEBCLOW 0x01b /* SdioRdFrameBCLow */ +#define SDA_SDIORDFRAMEBCHIGH 0x01c /* SdioRdFrameBCHigh */ + +/* SDA_F2WATERMARK */ +#define SDA_F2WATERMARK_MASK 0x7f /* F2Watermark Mask */ + +/* SDA_SBADDRLOW */ +#define SDA_SBADDRLOW_MASK 0x80 /* SbAddrLow Mask */ + +/* SDA_SBADDRMID */ +#define SDA_SBADDRMID_MASK 0xff /* SbAddrMid Mask */ + +/* SDA_SBADDRHIGH */ +#define SDA_SBADDRHIGH_MASK 0xff /* SbAddrHigh Mask */ + +/* SDA_FRAMECTRL */ +#define SFC_RF_TERM (1 << 0) /* Read Frame Terminate */ +#define SFC_WF_TERM (1 << 1) /* Write Frame Terminate */ +#define SFC_CRC4WOOS (1 << 2) /* HW reports CRC error for write out of sync */ +#define SFC_ABORTALL (1 << 3) /* Abort cancels all in-progress frames */ + +/* pcmciaframectrl */ +#define PFC_RF_TERM (1 << 0) /* Read Frame Terminate */ +#define PFC_WF_TERM (1 << 1) /* Write Frame Terminate */ + +/* intrcvlazy */ +#define IRL_TO_MASK 0x00ffffff /* timeout */ +#define IRL_FC_MASK 0xff000000 /* frame count */ +#define IRL_FC_SHIFT 24 /* frame count */ + +/* rx header */ +typedef volatile struct { + uint16 len; + uint16 flags; +} sdpcmd_rxh_t; + +/* rx header flags */ +#define RXF_CRC 0x0001 /* CRC error detected */ +#define RXF_WOOS 0x0002 /* write frame out of sync */ +#define RXF_WF_TERM 0x0004 /* write frame terminated */ +#define RXF_ABORT 0x0008 /* write frame aborted */ +#define RXF_DISCARD (RXF_CRC | RXF_WOOS | RXF_WF_TERM | RXF_ABORT) /* bad frame */ + +/* HW frame tag */ +#define SDPCM_FRAMETAG_LEN 4 /* HW frametag: 2 bytes len, 2 bytes check val */ + +#endif /* _sbsdpcmdev_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/sbsocram.h b/drivers/net/wireless/bcmdhd/include/sbsocram.h new file mode 100644 index 0000000000000..45c4dc208bda3 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sbsocram.h @@ -0,0 +1,186 @@ +/* + * BCM47XX Sonics SiliconBackplane embedded ram core + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sbsocram.h 277737 2011-08-16 17:54:59Z $ + */ + + +#ifndef _SBSOCRAM_H +#define _SBSOCRAM_H + +#ifndef _LANGUAGE_ASSEMBLY + + +#ifndef PAD +#define _PADLINE(line) pad ## line +#define _XSTR(line) _PADLINE(line) +#define PAD _XSTR(__LINE__) +#endif + + +typedef volatile struct sbsocramregs { + uint32 coreinfo; + uint32 bwalloc; + uint32 extracoreinfo; + uint32 biststat; + uint32 bankidx; + uint32 standbyctrl; + + uint32 errlogstatus; + uint32 errlogaddr; + + uint32 cambankidx; + uint32 cambankstandbyctrl; + uint32 cambankpatchctrl; + uint32 cambankpatchtblbaseaddr; + uint32 cambankcmdreg; + uint32 cambankdatareg; + uint32 cambankmaskreg; + uint32 PAD[1]; + uint32 bankinfo; + uint32 PAD[15]; + uint32 extmemconfig; + uint32 extmemparitycsr; + uint32 extmemparityerrdata; + uint32 extmemparityerrcnt; + uint32 extmemwrctrlandsize; + uint32 PAD[84]; + uint32 workaround; + uint32 pwrctl; + uint32 PAD[133]; + uint32 sr_control; + uint32 sr_status; + uint32 sr_address; + uint32 sr_data; +} sbsocramregs_t; + +#endif + + +#define SR_COREINFO 0x00 +#define SR_BWALLOC 0x04 +#define SR_BISTSTAT 0x0c +#define SR_BANKINDEX 0x10 +#define SR_BANKSTBYCTL 0x14 +#define SR_PWRCTL 0x1e8 + + +#define SRCI_PT_MASK 0x00070000 +#define SRCI_PT_SHIFT 16 + +#define SRCI_PT_OCP_OCP 0 +#define SRCI_PT_AXI_OCP 1 +#define SRCI_PT_ARM7AHB_OCP 2 +#define SRCI_PT_CM3AHB_OCP 3 +#define SRCI_PT_AXI_AXI 4 +#define SRCI_PT_AHB_AXI 5 + +#define SRCI_LSS_MASK 0x00f00000 +#define SRCI_LSS_SHIFT 20 +#define SRCI_LRS_MASK 0x0f000000 +#define SRCI_LRS_SHIFT 24 + + +#define SRCI_MS0_MASK 0xf +#define SR_MS0_BASE 16 + + +#define SRCI_ROMNB_MASK 0xf000 +#define SRCI_ROMNB_SHIFT 12 +#define SRCI_ROMBSZ_MASK 0xf00 +#define SRCI_ROMBSZ_SHIFT 8 +#define SRCI_SRNB_MASK 0xf0 +#define SRCI_SRNB_SHIFT 4 +#define SRCI_SRBSZ_MASK 0xf +#define SRCI_SRBSZ_SHIFT 0 + +#define SR_BSZ_BASE 14 + + +#define SRSC_SBYOVR_MASK 0x80000000 +#define SRSC_SBYOVR_SHIFT 31 +#define SRSC_SBYOVRVAL_MASK 0x60000000 +#define SRSC_SBYOVRVAL_SHIFT 29 +#define SRSC_SBYEN_MASK 0x01000000 +#define SRSC_SBYEN_SHIFT 24 + + +#define SRPC_PMU_STBYDIS_MASK 0x00000010 +#define SRPC_PMU_STBYDIS_SHIFT 4 +#define SRPC_STBYOVRVAL_MASK 0x00000008 +#define SRPC_STBYOVRVAL_SHIFT 3 +#define SRPC_STBYOVR_MASK 0x00000007 +#define SRPC_STBYOVR_SHIFT 0 + + +#define SRECC_NUM_BANKS_MASK 0x000000F0 +#define SRECC_NUM_BANKS_SHIFT 4 +#define SRECC_BANKSIZE_MASK 0x0000000F +#define SRECC_BANKSIZE_SHIFT 0 + +#define SRECC_BANKSIZE(value) (1 << (value)) + + +#define SRCBPC_PATCHENABLE 0x80000000 + +#define SRP_ADDRESS 0x0001FFFC +#define SRP_VALID 0x8000 + + +#define SRCMD_WRITE 0x00020000 +#define SRCMD_READ 0x00010000 +#define SRCMD_DONE 0x80000000 + +#define SRCMD_DONE_DLY 1000 + + +#define SOCRAM_BANKINFO_SZMASK 0x3f +#define SOCRAM_BANKIDX_ROM_MASK 0x100 + +#define SOCRAM_BANKIDX_MEMTYPE_SHIFT 8 + +#define SOCRAM_MEMTYPE_RAM 0 +#define SOCRAM_MEMTYPE_R0M 1 +#define SOCRAM_MEMTYPE_DEVRAM 2 + +#define SOCRAM_BANKINFO_REG 0x40 +#define SOCRAM_BANKIDX_REG 0x10 +#define SOCRAM_BANKINFO_STDBY_MASK 0x400 +#define SOCRAM_BANKINFO_STDBY_TIMER 0x800 + + +#define SOCRAM_BANKINFO_DEVRAMSEL_SHIFT 13 +#define SOCRAM_BANKINFO_DEVRAMSEL_MASK 0x2000 +#define SOCRAM_BANKINFO_DEVRAMPRO_SHIFT 14 +#define SOCRAM_BANKINFO_DEVRAMPRO_MASK 0x4000 + + +#define SOCRAM_DEVRAMBANK_MASK 0xF000 +#define SOCRAM_DEVRAMBANK_SHIFT 12 + + +#define SOCRAM_BANKINFO_SZBASE 8192 +#define SOCRAM_BANKSIZE_SHIFT 13 + + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/sdio.h b/drivers/net/wireless/bcmdhd/include/sdio.h new file mode 100644 index 0000000000000..c8ac7b773fb9a --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sdio.h @@ -0,0 +1,612 @@ +/* + * SDIO spec header file + * Protocol and standard (common) device definitions + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sdio.h 277737 2011-08-16 17:54:59Z $ + */ + +#ifndef _SDIO_H +#define _SDIO_H + + +/* CCCR structure for function 0 */ +typedef volatile struct { + uint8 cccr_sdio_rev; /* RO, cccr and sdio revision */ + uint8 sd_rev; /* RO, sd spec revision */ + uint8 io_en; /* I/O enable */ + uint8 io_rdy; /* I/O ready reg */ + uint8 intr_ctl; /* Master and per function interrupt enable control */ + uint8 intr_status; /* RO, interrupt pending status */ + uint8 io_abort; /* read/write abort or reset all functions */ + uint8 bus_inter; /* bus interface control */ + uint8 capability; /* RO, card capability */ + + uint8 cis_base_low; /* 0x9 RO, common CIS base address, LSB */ + uint8 cis_base_mid; + uint8 cis_base_high; /* 0xB RO, common CIS base address, MSB */ + + /* suspend/resume registers */ + uint8 bus_suspend; /* 0xC */ + uint8 func_select; /* 0xD */ + uint8 exec_flag; /* 0xE */ + uint8 ready_flag; /* 0xF */ + + uint8 fn0_blk_size[2]; /* 0x10(LSB), 0x11(MSB) */ + + uint8 power_control; /* 0x12 (SDIO version 1.10) */ + + uint8 speed_control; /* 0x13 */ +} sdio_regs_t; + +/* SDIO Device CCCR offsets */ +#define SDIOD_CCCR_REV 0x00 +#define SDIOD_CCCR_SDREV 0x01 +#define SDIOD_CCCR_IOEN 0x02 +#define SDIOD_CCCR_IORDY 0x03 +#define SDIOD_CCCR_INTEN 0x04 +#define SDIOD_CCCR_INTPEND 0x05 +#define SDIOD_CCCR_IOABORT 0x06 +#define SDIOD_CCCR_BICTRL 0x07 +#define SDIOD_CCCR_CAPABLITIES 0x08 +#define SDIOD_CCCR_CISPTR_0 0x09 +#define SDIOD_CCCR_CISPTR_1 0x0A +#define SDIOD_CCCR_CISPTR_2 0x0B +#define SDIOD_CCCR_BUSSUSP 0x0C +#define SDIOD_CCCR_FUNCSEL 0x0D +#define SDIOD_CCCR_EXECFLAGS 0x0E +#define SDIOD_CCCR_RDYFLAGS 0x0F +#define SDIOD_CCCR_BLKSIZE_0 0x10 +#define SDIOD_CCCR_BLKSIZE_1 0x11 +#define SDIOD_CCCR_POWER_CONTROL 0x12 +#define SDIOD_CCCR_SPEED_CONTROL 0x13 +#define SDIOD_CCCR_UHSI_SUPPORT 0x14 +#define SDIOD_CCCR_DRIVER_STRENGTH 0x15 +#define SDIOD_CCCR_INTR_EXTN 0x16 + +/* Broadcom extensions (corerev >= 1) */ +#define SDIOD_CCCR_BRCM_SEPINT 0xf2 + +/* cccr_sdio_rev */ +#define SDIO_REV_SDIOID_MASK 0xf0 /* SDIO spec revision number */ +#define SDIO_REV_CCCRID_MASK 0x0f /* CCCR format version number */ + +/* sd_rev */ +#define SD_REV_PHY_MASK 0x0f /* SD format version number */ + +/* io_en */ +#define SDIO_FUNC_ENABLE_1 0x02 /* function 1 I/O enable */ +#define SDIO_FUNC_ENABLE_2 0x04 /* function 2 I/O enable */ + +/* io_rdys */ +#define SDIO_FUNC_READY_1 0x02 /* function 1 I/O ready */ +#define SDIO_FUNC_READY_2 0x04 /* function 2 I/O ready */ + +/* intr_ctl */ +#define INTR_CTL_MASTER_EN 0x1 /* interrupt enable master */ +#define INTR_CTL_FUNC1_EN 0x2 /* interrupt enable for function 1 */ +#define INTR_CTL_FUNC2_EN 0x4 /* interrupt enable for function 2 */ + +/* intr_status */ +#define INTR_STATUS_FUNC1 0x2 /* interrupt pending for function 1 */ +#define INTR_STATUS_FUNC2 0x4 /* interrupt pending for function 2 */ + +/* io_abort */ +#define IO_ABORT_RESET_ALL 0x08 /* I/O card reset */ +#define IO_ABORT_FUNC_MASK 0x07 /* abort selction: function x */ + +/* bus_inter */ +#define BUS_CARD_DETECT_DIS 0x80 /* Card Detect disable */ +#define BUS_SPI_CONT_INTR_CAP 0x40 /* support continuous SPI interrupt */ +#define BUS_SPI_CONT_INTR_EN 0x20 /* continuous SPI interrupt enable */ +#define BUS_SD_DATA_WIDTH_MASK 0x03 /* bus width mask */ +#define BUS_SD_DATA_WIDTH_4BIT 0x02 /* bus width 4-bit mode */ +#define BUS_SD_DATA_WIDTH_1BIT 0x00 /* bus width 1-bit mode */ + +/* capability */ +#define SDIO_CAP_4BLS 0x80 /* 4-bit support for low speed card */ +#define SDIO_CAP_LSC 0x40 /* low speed card */ +#define SDIO_CAP_E4MI 0x20 /* enable interrupt between block of data in 4-bit mode */ +#define SDIO_CAP_S4MI 0x10 /* support interrupt between block of data in 4-bit mode */ +#define SDIO_CAP_SBS 0x08 /* support suspend/resume */ +#define SDIO_CAP_SRW 0x04 /* support read wait */ +#define SDIO_CAP_SMB 0x02 /* support multi-block transfer */ +#define SDIO_CAP_SDC 0x01 /* Support Direct commands during multi-byte transfer */ + +/* power_control */ +#define SDIO_POWER_SMPC 0x01 /* supports master power control (RO) */ +#define SDIO_POWER_EMPC 0x02 /* enable master power control (allow > 200mA) (RW) */ + +/* speed_control (control device entry into high-speed clocking mode) */ +#define SDIO_SPEED_SHS 0x01 /* supports high-speed [clocking] mode (RO) */ +#define SDIO_SPEED_EHS 0x02 /* enable high-speed [clocking] mode (RW) */ + +/* for setting bus speed in card: 0x13h */ +#define SDIO_BUS_SPEED_UHSISEL_M BITFIELD_MASK(3) +#define SDIO_BUS_SPEED_UHSISEL_S 1 + +/* for getting bus speed cap in card: 0x14h */ +#define SDIO_BUS_SPEED_UHSICAP_M BITFIELD_MASK(3) +#define SDIO_BUS_SPEED_UHSICAP_S 0 + +/* for getting driver type CAP in card: 0x15h */ +#define SDIO_BUS_DRVR_TYPE_CAP_M BITFIELD_MASK(3) +#define SDIO_BUS_DRVR_TYPE_CAP_S 0 + +/* for setting driver type selection in card: 0x15h */ +#define SDIO_BUS_DRVR_TYPE_SEL_M BITFIELD_MASK(2) +#define SDIO_BUS_DRVR_TYPE_SEL_S 4 + +/* for getting async int support in card: 0x16h */ +#define SDIO_BUS_ASYNCINT_CAP_M BITFIELD_MASK(1) +#define SDIO_BUS_ASYNCINT_CAP_S 0 + +/* for setting async int selection in card: 0x16h */ +#define SDIO_BUS_ASYNCINT_SEL_M BITFIELD_MASK(1) +#define SDIO_BUS_ASYNCINT_SEL_S 1 + +/* brcm sepint */ +#define SDIO_SEPINT_MASK 0x01 /* route sdpcmdev intr onto separate pad (chip-specific) */ +#define SDIO_SEPINT_OE 0x02 /* 1 asserts output enable for above pad */ +#define SDIO_SEPINT_ACT_HI 0x04 /* use active high interrupt level instead of active low */ + +/* FBR structure for function 1-7, FBR addresses and register offsets */ +typedef volatile struct { + uint8 devctr; /* device interface, CSA control */ + uint8 ext_dev; /* extended standard I/O device type code */ + uint8 pwr_sel; /* power selection support */ + uint8 PAD[6]; /* reserved */ + + uint8 cis_low; /* CIS LSB */ + uint8 cis_mid; + uint8 cis_high; /* CIS MSB */ + uint8 csa_low; /* code storage area, LSB */ + uint8 csa_mid; + uint8 csa_high; /* code storage area, MSB */ + uint8 csa_dat_win; /* data access window to function */ + + uint8 fnx_blk_size[2]; /* block size, little endian */ +} sdio_fbr_t; + +/* Maximum number of I/O funcs */ +#define SDIOD_MAX_IOFUNCS 7 + +/* SDIO Device FBR Start Address */ +#define SDIOD_FBR_STARTADDR 0x100 + +/* SDIO Device FBR Size */ +#define SDIOD_FBR_SIZE 0x100 + +/* Macro to calculate FBR register base */ +#define SDIOD_FBR_BASE(n) ((n) * 0x100) + +/* Function register offsets */ +#define SDIOD_FBR_DEVCTR 0x00 /* basic info for function */ +#define SDIOD_FBR_EXT_DEV 0x01 /* extended I/O device code */ +#define SDIOD_FBR_PWR_SEL 0x02 /* power selection bits */ + +/* SDIO Function CIS ptr offset */ +#define SDIOD_FBR_CISPTR_0 0x09 +#define SDIOD_FBR_CISPTR_1 0x0A +#define SDIOD_FBR_CISPTR_2 0x0B + +/* Code Storage Area pointer */ +#define SDIOD_FBR_CSA_ADDR_0 0x0C +#define SDIOD_FBR_CSA_ADDR_1 0x0D +#define SDIOD_FBR_CSA_ADDR_2 0x0E +#define SDIOD_FBR_CSA_DATA 0x0F + +/* SDIO Function I/O Block Size */ +#define SDIOD_FBR_BLKSIZE_0 0x10 +#define SDIOD_FBR_BLKSIZE_1 0x11 + +/* devctr */ +#define SDIOD_FBR_DEVCTR_DIC 0x0f /* device interface code */ +#define SDIOD_FBR_DECVTR_CSA 0x40 /* CSA support flag */ +#define SDIOD_FBR_DEVCTR_CSA_EN 0x80 /* CSA enabled */ +/* interface codes */ +#define SDIOD_DIC_NONE 0 /* SDIO standard interface is not supported */ +#define SDIOD_DIC_UART 1 +#define SDIOD_DIC_BLUETOOTH_A 2 +#define SDIOD_DIC_BLUETOOTH_B 3 +#define SDIOD_DIC_GPS 4 +#define SDIOD_DIC_CAMERA 5 +#define SDIOD_DIC_PHS 6 +#define SDIOD_DIC_WLAN 7 +#define SDIOD_DIC_EXT 0xf /* extended device interface, read ext_dev register */ + +/* pwr_sel */ +#define SDIOD_PWR_SEL_SPS 0x01 /* supports power selection */ +#define SDIOD_PWR_SEL_EPS 0x02 /* enable power selection (low-current mode) */ + +/* misc defines */ +#define SDIO_FUNC_0 0 +#define SDIO_FUNC_1 1 +#define SDIO_FUNC_2 2 +#define SDIO_FUNC_3 3 +#define SDIO_FUNC_4 4 +#define SDIO_FUNC_5 5 +#define SDIO_FUNC_6 6 +#define SDIO_FUNC_7 7 + +#define SD_CARD_TYPE_UNKNOWN 0 /* bad type or unrecognized */ +#define SD_CARD_TYPE_IO 1 /* IO only card */ +#define SD_CARD_TYPE_MEMORY 2 /* memory only card */ +#define SD_CARD_TYPE_COMBO 3 /* IO and memory combo card */ + +#define SDIO_MAX_BLOCK_SIZE 2048 /* maximum block size for block mode operation */ +#define SDIO_MIN_BLOCK_SIZE 1 /* minimum block size for block mode operation */ + +/* Card registers: status bit position */ +#define CARDREG_STATUS_BIT_OUTOFRANGE 31 +#define CARDREG_STATUS_BIT_COMCRCERROR 23 +#define CARDREG_STATUS_BIT_ILLEGALCOMMAND 22 +#define CARDREG_STATUS_BIT_ERROR 19 +#define CARDREG_STATUS_BIT_IOCURRENTSTATE3 12 +#define CARDREG_STATUS_BIT_IOCURRENTSTATE2 11 +#define CARDREG_STATUS_BIT_IOCURRENTSTATE1 10 +#define CARDREG_STATUS_BIT_IOCURRENTSTATE0 9 +#define CARDREG_STATUS_BIT_FUN_NUM_ERROR 4 + + + +#define SD_CMD_GO_IDLE_STATE 0 /* mandatory for SDIO */ +#define SD_CMD_SEND_OPCOND 1 +#define SD_CMD_MMC_SET_RCA 3 +#define SD_CMD_IO_SEND_OP_COND 5 /* mandatory for SDIO */ +#define SD_CMD_SELECT_DESELECT_CARD 7 +#define SD_CMD_SEND_CSD 9 +#define SD_CMD_SEND_CID 10 +#define SD_CMD_STOP_TRANSMISSION 12 +#define SD_CMD_SEND_STATUS 13 +#define SD_CMD_GO_INACTIVE_STATE 15 +#define SD_CMD_SET_BLOCKLEN 16 +#define SD_CMD_READ_SINGLE_BLOCK 17 +#define SD_CMD_READ_MULTIPLE_BLOCK 18 +#define SD_CMD_WRITE_BLOCK 24 +#define SD_CMD_WRITE_MULTIPLE_BLOCK 25 +#define SD_CMD_PROGRAM_CSD 27 +#define SD_CMD_SET_WRITE_PROT 28 +#define SD_CMD_CLR_WRITE_PROT 29 +#define SD_CMD_SEND_WRITE_PROT 30 +#define SD_CMD_ERASE_WR_BLK_START 32 +#define SD_CMD_ERASE_WR_BLK_END 33 +#define SD_CMD_ERASE 38 +#define SD_CMD_LOCK_UNLOCK 42 +#define SD_CMD_IO_RW_DIRECT 52 /* mandatory for SDIO */ +#define SD_CMD_IO_RW_EXTENDED 53 /* mandatory for SDIO */ +#define SD_CMD_APP_CMD 55 +#define SD_CMD_GEN_CMD 56 +#define SD_CMD_READ_OCR 58 +#define SD_CMD_CRC_ON_OFF 59 /* mandatory for SDIO */ +#define SD_ACMD_SD_STATUS 13 +#define SD_ACMD_SEND_NUM_WR_BLOCKS 22 +#define SD_ACMD_SET_WR_BLOCK_ERASE_CNT 23 +#define SD_ACMD_SD_SEND_OP_COND 41 +#define SD_ACMD_SET_CLR_CARD_DETECT 42 +#define SD_ACMD_SEND_SCR 51 + +/* argument for SD_CMD_IO_RW_DIRECT and SD_CMD_IO_RW_EXTENDED */ +#define SD_IO_OP_READ 0 /* Read_Write: Read */ +#define SD_IO_OP_WRITE 1 /* Read_Write: Write */ +#define SD_IO_RW_NORMAL 0 /* no RAW */ +#define SD_IO_RW_RAW 1 /* RAW */ +#define SD_IO_BYTE_MODE 0 /* Byte Mode */ +#define SD_IO_BLOCK_MODE 1 /* BlockMode */ +#define SD_IO_FIXED_ADDRESS 0 /* fix Address */ +#define SD_IO_INCREMENT_ADDRESS 1 /* IncrementAddress */ + +/* build SD_CMD_IO_RW_DIRECT Argument */ +#define SDIO_IO_RW_DIRECT_ARG(rw, raw, func, addr, data) \ + ((((rw) & 1) << 31) | (((func) & 0x7) << 28) | (((raw) & 1) << 27) | \ + (((addr) & 0x1FFFF) << 9) | ((data) & 0xFF)) + +/* build SD_CMD_IO_RW_EXTENDED Argument */ +#define SDIO_IO_RW_EXTENDED_ARG(rw, blk, func, addr, inc_addr, count) \ + ((((rw) & 1) << 31) | (((func) & 0x7) << 28) | (((blk) & 1) << 27) | \ + (((inc_addr) & 1) << 26) | (((addr) & 0x1FFFF) << 9) | ((count) & 0x1FF)) + +/* SDIO response parameters */ +#define SD_RSP_NO_NONE 0 +#define SD_RSP_NO_1 1 +#define SD_RSP_NO_2 2 +#define SD_RSP_NO_3 3 +#define SD_RSP_NO_4 4 +#define SD_RSP_NO_5 5 +#define SD_RSP_NO_6 6 + + /* Modified R6 response (to CMD3) */ +#define SD_RSP_MR6_COM_CRC_ERROR 0x8000 +#define SD_RSP_MR6_ILLEGAL_COMMAND 0x4000 +#define SD_RSP_MR6_ERROR 0x2000 + + /* Modified R1 in R4 Response (to CMD5) */ +#define SD_RSP_MR1_SBIT 0x80 +#define SD_RSP_MR1_PARAMETER_ERROR 0x40 +#define SD_RSP_MR1_RFU5 0x20 +#define SD_RSP_MR1_FUNC_NUM_ERROR 0x10 +#define SD_RSP_MR1_COM_CRC_ERROR 0x08 +#define SD_RSP_MR1_ILLEGAL_COMMAND 0x04 +#define SD_RSP_MR1_RFU1 0x02 +#define SD_RSP_MR1_IDLE_STATE 0x01 + + /* R5 response (to CMD52 and CMD53) */ +#define SD_RSP_R5_COM_CRC_ERROR 0x80 +#define SD_RSP_R5_ILLEGAL_COMMAND 0x40 +#define SD_RSP_R5_IO_CURRENTSTATE1 0x20 +#define SD_RSP_R5_IO_CURRENTSTATE0 0x10 +#define SD_RSP_R5_ERROR 0x08 +#define SD_RSP_R5_RFU 0x04 +#define SD_RSP_R5_FUNC_NUM_ERROR 0x02 +#define SD_RSP_R5_OUT_OF_RANGE 0x01 + +#define SD_RSP_R5_ERRBITS 0xCB + + +/* ------------------------------------------------ + * SDIO Commands and responses + * + * I/O only commands are: + * CMD0, CMD3, CMD5, CMD7, CMD14, CMD15, CMD52, CMD53 + * ------------------------------------------------ + */ + +/* SDIO Commands */ +#define SDIOH_CMD_0 0 +#define SDIOH_CMD_3 3 +#define SDIOH_CMD_5 5 +#define SDIOH_CMD_7 7 +#define SDIOH_CMD_11 11 +#define SDIOH_CMD_14 14 +#define SDIOH_CMD_15 15 +#define SDIOH_CMD_19 19 +#define SDIOH_CMD_52 52 +#define SDIOH_CMD_53 53 +#define SDIOH_CMD_59 59 + +/* SDIO Command Responses */ +#define SDIOH_RSP_NONE 0 +#define SDIOH_RSP_R1 1 +#define SDIOH_RSP_R2 2 +#define SDIOH_RSP_R3 3 +#define SDIOH_RSP_R4 4 +#define SDIOH_RSP_R5 5 +#define SDIOH_RSP_R6 6 + +/* + * SDIO Response Error flags + */ +#define SDIOH_RSP5_ERROR_FLAGS 0xCB + +/* ------------------------------------------------ + * SDIO Command structures. I/O only commands are: + * + * CMD0, CMD3, CMD5, CMD7, CMD15, CMD52, CMD53 + * ------------------------------------------------ + */ + +#define CMD5_OCR_M BITFIELD_MASK(24) +#define CMD5_OCR_S 0 + +#define CMD5_S18R_M BITFIELD_MASK(1) +#define CMD5_S18R_S 24 + +#define CMD7_RCA_M BITFIELD_MASK(16) +#define CMD7_RCA_S 16 + +#define CMD14_RCA_M BITFIELD_MASK(16) +#define CMD14_RCA_S 16 +#define CMD14_SLEEP_M BITFIELD_MASK(1) +#define CMD14_SLEEP_S 15 + +#define CMD_15_RCA_M BITFIELD_MASK(16) +#define CMD_15_RCA_S 16 + +#define CMD52_DATA_M BITFIELD_MASK(8) /* Bits [7:0] - Write Data/Stuff bits of CMD52 + */ +#define CMD52_DATA_S 0 +#define CMD52_REG_ADDR_M BITFIELD_MASK(17) /* Bits [25:9] - register address */ +#define CMD52_REG_ADDR_S 9 +#define CMD52_RAW_M BITFIELD_MASK(1) /* Bit 27 - Read after Write flag */ +#define CMD52_RAW_S 27 +#define CMD52_FUNCTION_M BITFIELD_MASK(3) /* Bits [30:28] - Function number */ +#define CMD52_FUNCTION_S 28 +#define CMD52_RW_FLAG_M BITFIELD_MASK(1) /* Bit 31 - R/W flag */ +#define CMD52_RW_FLAG_S 31 + + +#define CMD53_BYTE_BLK_CNT_M BITFIELD_MASK(9) /* Bits [8:0] - Byte/Block Count of CMD53 */ +#define CMD53_BYTE_BLK_CNT_S 0 +#define CMD53_REG_ADDR_M BITFIELD_MASK(17) /* Bits [25:9] - register address */ +#define CMD53_REG_ADDR_S 9 +#define CMD53_OP_CODE_M BITFIELD_MASK(1) /* Bit 26 - R/W Operation Code */ +#define CMD53_OP_CODE_S 26 +#define CMD53_BLK_MODE_M BITFIELD_MASK(1) /* Bit 27 - Block Mode */ +#define CMD53_BLK_MODE_S 27 +#define CMD53_FUNCTION_M BITFIELD_MASK(3) /* Bits [30:28] - Function number */ +#define CMD53_FUNCTION_S 28 +#define CMD53_RW_FLAG_M BITFIELD_MASK(1) /* Bit 31 - R/W flag */ +#define CMD53_RW_FLAG_S 31 + +/* ------------------------------------------------------ + * SDIO Command Response structures for SD1 and SD4 modes + * ----------------------------------------------------- + */ +#define RSP4_IO_OCR_M BITFIELD_MASK(24) /* Bits [23:0] - Card's OCR Bits [23:0] */ +#define RSP4_IO_OCR_S 0 + +#define RSP4_S18A_M BITFIELD_MASK(1) /* Bits [23:0] - Card's OCR Bits [23:0] */ +#define RSP4_S18A_S 24 + +#define RSP4_STUFF_M BITFIELD_MASK(3) /* Bits [26:24] - Stuff bits */ +#define RSP4_STUFF_S 24 +#define RSP4_MEM_PRESENT_M BITFIELD_MASK(1) /* Bit 27 - Memory present */ +#define RSP4_MEM_PRESENT_S 27 +#define RSP4_NUM_FUNCS_M BITFIELD_MASK(3) /* Bits [30:28] - Number of I/O funcs */ +#define RSP4_NUM_FUNCS_S 28 +#define RSP4_CARD_READY_M BITFIELD_MASK(1) /* Bit 31 - SDIO card ready */ +#define RSP4_CARD_READY_S 31 + +#define RSP6_STATUS_M BITFIELD_MASK(16) /* Bits [15:0] - Card status bits [19,22,23,12:0] + */ +#define RSP6_STATUS_S 0 +#define RSP6_IO_RCA_M BITFIELD_MASK(16) /* Bits [31:16] - RCA bits[31-16] */ +#define RSP6_IO_RCA_S 16 + +#define RSP1_AKE_SEQ_ERROR_M BITFIELD_MASK(1) /* Bit 3 - Authentication seq error */ +#define RSP1_AKE_SEQ_ERROR_S 3 +#define RSP1_APP_CMD_M BITFIELD_MASK(1) /* Bit 5 - Card expects ACMD */ +#define RSP1_APP_CMD_S 5 +#define RSP1_READY_FOR_DATA_M BITFIELD_MASK(1) /* Bit 8 - Ready for data (buff empty) */ +#define RSP1_READY_FOR_DATA_S 8 +#define RSP1_CURR_STATE_M BITFIELD_MASK(4) /* Bits [12:9] - State of card + * when Cmd was received + */ +#define RSP1_CURR_STATE_S 9 +#define RSP1_EARSE_RESET_M BITFIELD_MASK(1) /* Bit 13 - Erase seq cleared */ +#define RSP1_EARSE_RESET_S 13 +#define RSP1_CARD_ECC_DISABLE_M BITFIELD_MASK(1) /* Bit 14 - Card ECC disabled */ +#define RSP1_CARD_ECC_DISABLE_S 14 +#define RSP1_WP_ERASE_SKIP_M BITFIELD_MASK(1) /* Bit 15 - Partial blocks erased due to W/P */ +#define RSP1_WP_ERASE_SKIP_S 15 +#define RSP1_CID_CSD_OVERW_M BITFIELD_MASK(1) /* Bit 16 - Illegal write to CID or R/O bits + * of CSD + */ +#define RSP1_CID_CSD_OVERW_S 16 +#define RSP1_ERROR_M BITFIELD_MASK(1) /* Bit 19 - General/Unknown error */ +#define RSP1_ERROR_S 19 +#define RSP1_CC_ERROR_M BITFIELD_MASK(1) /* Bit 20 - Internal Card Control error */ +#define RSP1_CC_ERROR_S 20 +#define RSP1_CARD_ECC_FAILED_M BITFIELD_MASK(1) /* Bit 21 - Card internal ECC failed + * to correct data + */ +#define RSP1_CARD_ECC_FAILED_S 21 +#define RSP1_ILLEGAL_CMD_M BITFIELD_MASK(1) /* Bit 22 - Cmd not legal for the card state */ +#define RSP1_ILLEGAL_CMD_S 22 +#define RSP1_COM_CRC_ERROR_M BITFIELD_MASK(1) /* Bit 23 - CRC check of previous command failed + */ +#define RSP1_COM_CRC_ERROR_S 23 +#define RSP1_LOCK_UNLOCK_FAIL_M BITFIELD_MASK(1) /* Bit 24 - Card lock-unlock Cmd Seq error */ +#define RSP1_LOCK_UNLOCK_FAIL_S 24 +#define RSP1_CARD_LOCKED_M BITFIELD_MASK(1) /* Bit 25 - Card locked by the host */ +#define RSP1_CARD_LOCKED_S 25 +#define RSP1_WP_VIOLATION_M BITFIELD_MASK(1) /* Bit 26 - Attempt to program + * write-protected blocks + */ +#define RSP1_WP_VIOLATION_S 26 +#define RSP1_ERASE_PARAM_M BITFIELD_MASK(1) /* Bit 27 - Invalid erase blocks */ +#define RSP1_ERASE_PARAM_S 27 +#define RSP1_ERASE_SEQ_ERR_M BITFIELD_MASK(1) /* Bit 28 - Erase Cmd seq error */ +#define RSP1_ERASE_SEQ_ERR_S 28 +#define RSP1_BLK_LEN_ERR_M BITFIELD_MASK(1) /* Bit 29 - Block length error */ +#define RSP1_BLK_LEN_ERR_S 29 +#define RSP1_ADDR_ERR_M BITFIELD_MASK(1) /* Bit 30 - Misaligned address */ +#define RSP1_ADDR_ERR_S 30 +#define RSP1_OUT_OF_RANGE_M BITFIELD_MASK(1) /* Bit 31 - Cmd arg was out of range */ +#define RSP1_OUT_OF_RANGE_S 31 + + +#define RSP5_DATA_M BITFIELD_MASK(8) /* Bits [0:7] - data */ +#define RSP5_DATA_S 0 +#define RSP5_FLAGS_M BITFIELD_MASK(8) /* Bit [15:8] - Rsp flags */ +#define RSP5_FLAGS_S 8 +#define RSP5_STUFF_M BITFIELD_MASK(16) /* Bits [31:16] - Stuff bits */ +#define RSP5_STUFF_S 16 + +/* ---------------------------------------------- + * SDIO Command Response structures for SPI mode + * ---------------------------------------------- + */ +#define SPIRSP4_IO_OCR_M BITFIELD_MASK(16) /* Bits [15:0] - Card's OCR Bits [23:8] */ +#define SPIRSP4_IO_OCR_S 0 +#define SPIRSP4_STUFF_M BITFIELD_MASK(3) /* Bits [18:16] - Stuff bits */ +#define SPIRSP4_STUFF_S 16 +#define SPIRSP4_MEM_PRESENT_M BITFIELD_MASK(1) /* Bit 19 - Memory present */ +#define SPIRSP4_MEM_PRESENT_S 19 +#define SPIRSP4_NUM_FUNCS_M BITFIELD_MASK(3) /* Bits [22:20] - Number of I/O funcs */ +#define SPIRSP4_NUM_FUNCS_S 20 +#define SPIRSP4_CARD_READY_M BITFIELD_MASK(1) /* Bit 23 - SDIO card ready */ +#define SPIRSP4_CARD_READY_S 23 +#define SPIRSP4_IDLE_STATE_M BITFIELD_MASK(1) /* Bit 24 - idle state */ +#define SPIRSP4_IDLE_STATE_S 24 +#define SPIRSP4_ILLEGAL_CMD_M BITFIELD_MASK(1) /* Bit 26 - Illegal Cmd error */ +#define SPIRSP4_ILLEGAL_CMD_S 26 +#define SPIRSP4_COM_CRC_ERROR_M BITFIELD_MASK(1) /* Bit 27 - COM CRC error */ +#define SPIRSP4_COM_CRC_ERROR_S 27 +#define SPIRSP4_FUNC_NUM_ERROR_M BITFIELD_MASK(1) /* Bit 28 - Function number error + */ +#define SPIRSP4_FUNC_NUM_ERROR_S 28 +#define SPIRSP4_PARAM_ERROR_M BITFIELD_MASK(1) /* Bit 30 - Parameter Error Bit */ +#define SPIRSP4_PARAM_ERROR_S 30 +#define SPIRSP4_START_BIT_M BITFIELD_MASK(1) /* Bit 31 - Start Bit */ +#define SPIRSP4_START_BIT_S 31 + +#define SPIRSP5_DATA_M BITFIELD_MASK(8) /* Bits [23:16] - R/W Data */ +#define SPIRSP5_DATA_S 16 +#define SPIRSP5_IDLE_STATE_M BITFIELD_MASK(1) /* Bit 24 - Idle state */ +#define SPIRSP5_IDLE_STATE_S 24 +#define SPIRSP5_ILLEGAL_CMD_M BITFIELD_MASK(1) /* Bit 26 - Illegal Cmd error */ +#define SPIRSP5_ILLEGAL_CMD_S 26 +#define SPIRSP5_COM_CRC_ERROR_M BITFIELD_MASK(1) /* Bit 27 - COM CRC error */ +#define SPIRSP5_COM_CRC_ERROR_S 27 +#define SPIRSP5_FUNC_NUM_ERROR_M BITFIELD_MASK(1) /* Bit 28 - Function number error + */ +#define SPIRSP5_FUNC_NUM_ERROR_S 28 +#define SPIRSP5_PARAM_ERROR_M BITFIELD_MASK(1) /* Bit 30 - Parameter Error Bit */ +#define SPIRSP5_PARAM_ERROR_S 30 +#define SPIRSP5_START_BIT_M BITFIELD_MASK(1) /* Bit 31 - Start Bit */ +#define SPIRSP5_START_BIT_S 31 + +/* RSP6 card status format; Pg 68 Physical Layer spec v 1.10 */ +#define RSP6STAT_AKE_SEQ_ERROR_M BITFIELD_MASK(1) /* Bit 3 - Authentication seq error + */ +#define RSP6STAT_AKE_SEQ_ERROR_S 3 +#define RSP6STAT_APP_CMD_M BITFIELD_MASK(1) /* Bit 5 - Card expects ACMD */ +#define RSP6STAT_APP_CMD_S 5 +#define RSP6STAT_READY_FOR_DATA_M BITFIELD_MASK(1) /* Bit 8 - Ready for data + * (buff empty) + */ +#define RSP6STAT_READY_FOR_DATA_S 8 +#define RSP6STAT_CURR_STATE_M BITFIELD_MASK(4) /* Bits [12:9] - Card state at + * Cmd reception + */ +#define RSP6STAT_CURR_STATE_S 9 +#define RSP6STAT_ERROR_M BITFIELD_MASK(1) /* Bit 13 - General/Unknown error Bit 19 + */ +#define RSP6STAT_ERROR_S 13 +#define RSP6STAT_ILLEGAL_CMD_M BITFIELD_MASK(1) /* Bit 14 - Illegal cmd for + * card state Bit 22 + */ +#define RSP6STAT_ILLEGAL_CMD_S 14 +#define RSP6STAT_COM_CRC_ERROR_M BITFIELD_MASK(1) /* Bit 15 - CRC previous command + * failed Bit 23 + */ +#define RSP6STAT_COM_CRC_ERROR_S 15 + +#define SDIOH_XFER_TYPE_READ SD_IO_OP_READ +#define SDIOH_XFER_TYPE_WRITE SD_IO_OP_WRITE + +/* command issue options */ +#define CMD_OPTION_DEFAULT 0 +#define CMD_OPTION_TUNING 1 + +#endif /* _SDIO_H */ diff --git a/drivers/net/wireless/bcmdhd/include/sdioh.h b/drivers/net/wireless/bcmdhd/include/sdioh.h new file mode 100644 index 0000000000000..5f47d6f88ceae --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sdioh.h @@ -0,0 +1,442 @@ +/* + * SDIO Host Controller Spec header file + * Register map and definitions for the Standard Host Controller + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sdioh.h 300017 2011-12-01 20:30:27Z $ + */ + +#ifndef _SDIOH_H +#define _SDIOH_H + +#define SD_SysAddr 0x000 +#define SD_BlockSize 0x004 +#define SD_BlockCount 0x006 +#define SD_Arg0 0x008 +#define SD_Arg1 0x00A +#define SD_TransferMode 0x00C +#define SD_Command 0x00E +#define SD_Response0 0x010 +#define SD_Response1 0x012 +#define SD_Response2 0x014 +#define SD_Response3 0x016 +#define SD_Response4 0x018 +#define SD_Response5 0x01A +#define SD_Response6 0x01C +#define SD_Response7 0x01E +#define SD_BufferDataPort0 0x020 +#define SD_BufferDataPort1 0x022 +#define SD_PresentState 0x024 +#define SD_HostCntrl 0x028 +#define SD_PwrCntrl 0x029 +#define SD_BlockGapCntrl 0x02A +#define SD_WakeupCntrl 0x02B +#define SD_ClockCntrl 0x02C +#define SD_TimeoutCntrl 0x02E +#define SD_SoftwareReset 0x02F +#define SD_IntrStatus 0x030 +#define SD_ErrorIntrStatus 0x032 +#define SD_IntrStatusEnable 0x034 +#define SD_ErrorIntrStatusEnable 0x036 +#define SD_IntrSignalEnable 0x038 +#define SD_ErrorIntrSignalEnable 0x03A +#define SD_CMD12ErrorStatus 0x03C +#define SD_Capabilities 0x040 +#define SD_Capabilities3 0x044 +#define SD_MaxCurCap 0x048 +#define SD_MaxCurCap_Reserved 0x04C +#define SD_ADMA_ErrStatus 0x054 +#define SD_ADMA_SysAddr 0x58 +#define SD_SlotInterruptStatus 0x0FC +#define SD_HostControllerVersion 0x0FE +#define SD_GPIO_Reg 0x100 +#define SD_GPIO_OE 0x104 +#define SD_GPIO_Enable 0x108 + + +/* SD specific registers in PCI config space */ +#define SD_SlotInfo 0x40 + +/* HC 3.0 specific registers and offsets */ +#define SD3_HostCntrl2 0x03E +/* preset regsstart and count */ +#define SD3_PresetValStart 0x060 +#define SD3_PresetValCount 8 +/* preset-indiv regs */ +#define SD3_PresetVal_init 0x060 +#define SD3_PresetVal_default 0x062 +#define SD3_PresetVal_HS 0x064 +#define SD3_PresetVal_SDR12 0x066 +#define SD3_PresetVal_SDR25 0x068 +#define SD3_PresetVal_SDR50 0x06a +#define SD3_PresetVal_SDR104 0x06c +#define SD3_PresetVal_DDR50 0x06e + +/* preset value indices */ +#define SD3_PRESETVAL_INITIAL_IX 0 +#define SD3_PRESETVAL_DESPEED_IX 1 +#define SD3_PRESETVAL_HISPEED_IX 2 +#define SD3_PRESETVAL_SDR12_IX 3 +#define SD3_PRESETVAL_SDR25_IX 4 +#define SD3_PRESETVAL_SDR50_IX 5 +#define SD3_PRESETVAL_SDR104_IX 6 +#define SD3_PRESETVAL_DDR50_IX 7 + +/* SD_Capabilities reg (0x040) */ +#define CAP_TO_CLKFREQ_M BITFIELD_MASK(6) +#define CAP_TO_CLKFREQ_S 0 +#define CAP_TO_CLKUNIT_M BITFIELD_MASK(1) +#define CAP_TO_CLKUNIT_S 7 +/* Note: for sdio-2.0 case, this mask has to be 6 bits, but msb 2 + bits are reserved. going ahead with 8 bits, as it is req for 3.0 +*/ +#define CAP_BASECLK_M BITFIELD_MASK(8) +#define CAP_BASECLK_S 8 +#define CAP_MAXBLOCK_M BITFIELD_MASK(2) +#define CAP_MAXBLOCK_S 16 +#define CAP_ADMA2_M BITFIELD_MASK(1) +#define CAP_ADMA2_S 19 +#define CAP_ADMA1_M BITFIELD_MASK(1) +#define CAP_ADMA1_S 20 +#define CAP_HIGHSPEED_M BITFIELD_MASK(1) +#define CAP_HIGHSPEED_S 21 +#define CAP_DMA_M BITFIELD_MASK(1) +#define CAP_DMA_S 22 +#define CAP_SUSPEND_M BITFIELD_MASK(1) +#define CAP_SUSPEND_S 23 +#define CAP_VOLT_3_3_M BITFIELD_MASK(1) +#define CAP_VOLT_3_3_S 24 +#define CAP_VOLT_3_0_M BITFIELD_MASK(1) +#define CAP_VOLT_3_0_S 25 +#define CAP_VOLT_1_8_M BITFIELD_MASK(1) +#define CAP_VOLT_1_8_S 26 +#define CAP_64BIT_HOST_M BITFIELD_MASK(1) +#define CAP_64BIT_HOST_S 28 + +#define SDIO_OCR_READ_FAIL (2) + + +#define CAP_ASYNCINT_SUP_M BITFIELD_MASK(1) +#define CAP_ASYNCINT_SUP_S 29 + +#define CAP_SLOTTYPE_M BITFIELD_MASK(2) +#define CAP_SLOTTYPE_S 30 + +#define CAP3_MSBits_OFFSET (32) +/* note: following are caps MSB32 bits. + So the bits start from 0, instead of 32. that is why + CAP3_MSBits_OFFSET is subtracted. +*/ +#define CAP3_SDR50_SUP_M BITFIELD_MASK(1) +#define CAP3_SDR50_SUP_S (32 - CAP3_MSBits_OFFSET) + +#define CAP3_SDR104_SUP_M BITFIELD_MASK(1) +#define CAP3_SDR104_SUP_S (33 - CAP3_MSBits_OFFSET) + +#define CAP3_DDR50_SUP_M BITFIELD_MASK(1) +#define CAP3_DDR50_SUP_S (34 - CAP3_MSBits_OFFSET) + +/* for knowing the clk caps in a single read */ +#define CAP3_30CLKCAP_M BITFIELD_MASK(3) +#define CAP3_30CLKCAP_S (32 - CAP3_MSBits_OFFSET) + +#define CAP3_DRIVTYPE_A_M BITFIELD_MASK(1) +#define CAP3_DRIVTYPE_A_S (36 - CAP3_MSBits_OFFSET) + +#define CAP3_DRIVTYPE_C_M BITFIELD_MASK(1) +#define CAP3_DRIVTYPE_C_S (37 - CAP3_MSBits_OFFSET) + +#define CAP3_DRIVTYPE_D_M BITFIELD_MASK(1) +#define CAP3_DRIVTYPE_D_S (38 - CAP3_MSBits_OFFSET) + +#define CAP3_RETUNING_TC_M BITFIELD_MASK(4) +#define CAP3_RETUNING_TC_S (40 - CAP3_MSBits_OFFSET) + +#define CAP3_TUNING_SDR50_M BITFIELD_MASK(1) +#define CAP3_TUNING_SDR50_S (45 - CAP3_MSBits_OFFSET) + +#define CAP3_RETUNING_MODES_M BITFIELD_MASK(2) +#define CAP3_RETUNING_MODES_S (46 - CAP3_MSBits_OFFSET) + +#define CAP3_CLK_MULT_M BITFIELD_MASK(8) +#define CAP3_CLK_MULT_S (48 - CAP3_MSBits_OFFSET) + +#define PRESET_DRIVR_SELECT_M BITFIELD_MASK(2) +#define PRESET_DRIVR_SELECT_S 14 + +#define PRESET_CLK_DIV_M BITFIELD_MASK(10) +#define PRESET_CLK_DIV_S 0 + +/* SD_MaxCurCap reg (0x048) */ +#define CAP_CURR_3_3_M BITFIELD_MASK(8) +#define CAP_CURR_3_3_S 0 +#define CAP_CURR_3_0_M BITFIELD_MASK(8) +#define CAP_CURR_3_0_S 8 +#define CAP_CURR_1_8_M BITFIELD_MASK(8) +#define CAP_CURR_1_8_S 16 + +/* SD_SysAddr: Offset 0x0000, Size 4 bytes */ + +/* SD_BlockSize: Offset 0x004, Size 2 bytes */ +#define BLKSZ_BLKSZ_M BITFIELD_MASK(12) +#define BLKSZ_BLKSZ_S 0 +#define BLKSZ_BNDRY_M BITFIELD_MASK(3) +#define BLKSZ_BNDRY_S 12 + +/* SD_BlockCount: Offset 0x006, size 2 bytes */ + +/* SD_Arg0: Offset 0x008, size = 4 bytes */ +/* SD_TransferMode Offset 0x00C, size = 2 bytes */ +#define XFER_DMA_ENABLE_M BITFIELD_MASK(1) +#define XFER_DMA_ENABLE_S 0 +#define XFER_BLK_COUNT_EN_M BITFIELD_MASK(1) +#define XFER_BLK_COUNT_EN_S 1 +#define XFER_CMD_12_EN_M BITFIELD_MASK(1) +#define XFER_CMD_12_EN_S 2 +#define XFER_DATA_DIRECTION_M BITFIELD_MASK(1) +#define XFER_DATA_DIRECTION_S 4 +#define XFER_MULTI_BLOCK_M BITFIELD_MASK(1) +#define XFER_MULTI_BLOCK_S 5 + +/* SD_Command: Offset 0x00E, size = 2 bytes */ +/* resp_type field */ +#define RESP_TYPE_NONE 0 +#define RESP_TYPE_136 1 +#define RESP_TYPE_48 2 +#define RESP_TYPE_48_BUSY 3 +/* type field */ +#define CMD_TYPE_NORMAL 0 +#define CMD_TYPE_SUSPEND 1 +#define CMD_TYPE_RESUME 2 +#define CMD_TYPE_ABORT 3 + +#define CMD_RESP_TYPE_M BITFIELD_MASK(2) /* Bits [0-1] - Response type */ +#define CMD_RESP_TYPE_S 0 +#define CMD_CRC_EN_M BITFIELD_MASK(1) /* Bit 3 - CRC enable */ +#define CMD_CRC_EN_S 3 +#define CMD_INDEX_EN_M BITFIELD_MASK(1) /* Bit 4 - Enable index checking */ +#define CMD_INDEX_EN_S 4 +#define CMD_DATA_EN_M BITFIELD_MASK(1) /* Bit 5 - Using DAT line */ +#define CMD_DATA_EN_S 5 +#define CMD_TYPE_M BITFIELD_MASK(2) /* Bit [6-7] - Normal, abort, resume, etc + */ +#define CMD_TYPE_S 6 +#define CMD_INDEX_M BITFIELD_MASK(6) /* Bits [8-13] - Command number */ +#define CMD_INDEX_S 8 + +/* SD_BufferDataPort0 : Offset 0x020, size = 2 or 4 bytes */ +/* SD_BufferDataPort1 : Offset 0x022, size = 2 bytes */ +/* SD_PresentState : Offset 0x024, size = 4 bytes */ +#define PRES_CMD_INHIBIT_M BITFIELD_MASK(1) /* Bit 0 May use CMD */ +#define PRES_CMD_INHIBIT_S 0 +#define PRES_DAT_INHIBIT_M BITFIELD_MASK(1) /* Bit 1 May use DAT */ +#define PRES_DAT_INHIBIT_S 1 +#define PRES_DAT_BUSY_M BITFIELD_MASK(1) /* Bit 2 DAT is busy */ +#define PRES_DAT_BUSY_S 2 +#define PRES_PRESENT_RSVD_M BITFIELD_MASK(5) /* Bit [3-7] rsvd */ +#define PRES_PRESENT_RSVD_S 3 +#define PRES_WRITE_ACTIVE_M BITFIELD_MASK(1) /* Bit 8 Write is active */ +#define PRES_WRITE_ACTIVE_S 8 +#define PRES_READ_ACTIVE_M BITFIELD_MASK(1) /* Bit 9 Read is active */ +#define PRES_READ_ACTIVE_S 9 +#define PRES_WRITE_DATA_RDY_M BITFIELD_MASK(1) /* Bit 10 Write buf is avail */ +#define PRES_WRITE_DATA_RDY_S 10 +#define PRES_READ_DATA_RDY_M BITFIELD_MASK(1) /* Bit 11 Read buf data avail */ +#define PRES_READ_DATA_RDY_S 11 +#define PRES_CARD_PRESENT_M BITFIELD_MASK(1) /* Bit 16 Card present - debounced */ +#define PRES_CARD_PRESENT_S 16 +#define PRES_CARD_STABLE_M BITFIELD_MASK(1) /* Bit 17 Debugging */ +#define PRES_CARD_STABLE_S 17 +#define PRES_CARD_PRESENT_RAW_M BITFIELD_MASK(1) /* Bit 18 Not debounced */ +#define PRES_CARD_PRESENT_RAW_S 18 +#define PRES_WRITE_ENABLED_M BITFIELD_MASK(1) /* Bit 19 Write protected? */ +#define PRES_WRITE_ENABLED_S 19 +#define PRES_DAT_SIGNAL_M BITFIELD_MASK(4) /* Bit [20-23] Debugging */ +#define PRES_DAT_SIGNAL_S 20 +#define PRES_CMD_SIGNAL_M BITFIELD_MASK(1) /* Bit 24 Debugging */ +#define PRES_CMD_SIGNAL_S 24 + +/* SD_HostCntrl: Offset 0x028, size = 1 bytes */ +#define HOST_LED_M BITFIELD_MASK(1) /* Bit 0 LED On/Off */ +#define HOST_LED_S 0 +#define HOST_DATA_WIDTH_M BITFIELD_MASK(1) /* Bit 1 4 bit enable */ +#define HOST_DATA_WIDTH_S 1 +#define HOST_HI_SPEED_EN_M BITFIELD_MASK(1) /* Bit 2 High speed vs low speed */ +#define HOST_DMA_SEL_S 3 +#define HOST_DMA_SEL_M BITFIELD_MASK(2) /* Bit 4:3 DMA Select */ +#define HOST_HI_SPEED_EN_S 2 + +/* Host Control2: */ +#define HOSTCtrl2_PRESVAL_EN_M BITFIELD_MASK(1) /* 1 bit */ +#define HOSTCtrl2_PRESVAL_EN_S 15 /* bit# */ + +#define HOSTCtrl2_ASYINT_EN_M BITFIELD_MASK(1) /* 1 bit */ +#define HOSTCtrl2_ASYINT_EN_S 14 /* bit# */ + +#define HOSTCtrl2_SAMPCLK_SEL_M BITFIELD_MASK(1) /* 1 bit */ +#define HOSTCtrl2_SAMPCLK_SEL_S 7 /* bit# */ + +#define HOSTCtrl2_EXEC_TUNING_M BITFIELD_MASK(1) /* 1 bit */ +#define HOSTCtrl2_EXEC_TUNING_S 6 /* bit# */ + +#define HOSTCtrl2_DRIVSTRENGTH_SEL_M BITFIELD_MASK(2) /* 2 bit */ +#define HOSTCtrl2_DRIVSTRENGTH_SEL_S 4 /* bit# */ + +#define HOSTCtrl2_1_8SIG_EN_M BITFIELD_MASK(1) /* 1 bit */ +#define HOSTCtrl2_1_8SIG_EN_S 3 /* bit# */ + +#define HOSTCtrl2_UHSMODE_SEL_M BITFIELD_MASK(3) /* 3 bit */ +#define HOSTCtrl2_UHSMODE_SEL_S 0 /* bit# */ + +#define HOST_CONTR_VER_2 (1) +#define HOST_CONTR_VER_3 (2) + +/* misc defines */ +#define SD1_MODE 0x1 /* SD Host Cntrlr Spec */ +#define SD4_MODE 0x2 /* SD Host Cntrlr Spec */ + +/* SD_PwrCntrl: Offset 0x029, size = 1 bytes */ +#define PWR_BUS_EN_M BITFIELD_MASK(1) /* Bit 0 Power the bus */ +#define PWR_BUS_EN_S 0 +#define PWR_VOLTS_M BITFIELD_MASK(3) /* Bit [1-3] Voltage Select */ +#define PWR_VOLTS_S 1 + +/* SD_SoftwareReset: Offset 0x02F, size = 1 byte */ +#define SW_RESET_ALL_M BITFIELD_MASK(1) /* Bit 0 Reset All */ +#define SW_RESET_ALL_S 0 +#define SW_RESET_CMD_M BITFIELD_MASK(1) /* Bit 1 CMD Line Reset */ +#define SW_RESET_CMD_S 1 +#define SW_RESET_DAT_M BITFIELD_MASK(1) /* Bit 2 DAT Line Reset */ +#define SW_RESET_DAT_S 2 + +/* SD_IntrStatus: Offset 0x030, size = 2 bytes */ +/* Defs also serve SD_IntrStatusEnable and SD_IntrSignalEnable */ +#define INTSTAT_CMD_COMPLETE_M BITFIELD_MASK(1) /* Bit 0 */ +#define INTSTAT_CMD_COMPLETE_S 0 +#define INTSTAT_XFER_COMPLETE_M BITFIELD_MASK(1) +#define INTSTAT_XFER_COMPLETE_S 1 +#define INTSTAT_BLOCK_GAP_EVENT_M BITFIELD_MASK(1) +#define INTSTAT_BLOCK_GAP_EVENT_S 2 +#define INTSTAT_DMA_INT_M BITFIELD_MASK(1) +#define INTSTAT_DMA_INT_S 3 +#define INTSTAT_BUF_WRITE_READY_M BITFIELD_MASK(1) +#define INTSTAT_BUF_WRITE_READY_S 4 +#define INTSTAT_BUF_READ_READY_M BITFIELD_MASK(1) +#define INTSTAT_BUF_READ_READY_S 5 +#define INTSTAT_CARD_INSERTION_M BITFIELD_MASK(1) +#define INTSTAT_CARD_INSERTION_S 6 +#define INTSTAT_CARD_REMOVAL_M BITFIELD_MASK(1) +#define INTSTAT_CARD_REMOVAL_S 7 +#define INTSTAT_CARD_INT_M BITFIELD_MASK(1) +#define INTSTAT_CARD_INT_S 8 +#define INTSTAT_RETUNING_INT_M BITFIELD_MASK(1) /* Bit 12 */ +#define INTSTAT_RETUNING_INT_S 12 +#define INTSTAT_ERROR_INT_M BITFIELD_MASK(1) /* Bit 15 */ +#define INTSTAT_ERROR_INT_S 15 + +/* SD_ErrorIntrStatus: Offset 0x032, size = 2 bytes */ +/* Defs also serve SD_ErrorIntrStatusEnable and SD_ErrorIntrSignalEnable */ +#define ERRINT_CMD_TIMEOUT_M BITFIELD_MASK(1) +#define ERRINT_CMD_TIMEOUT_S 0 +#define ERRINT_CMD_CRC_M BITFIELD_MASK(1) +#define ERRINT_CMD_CRC_S 1 +#define ERRINT_CMD_ENDBIT_M BITFIELD_MASK(1) +#define ERRINT_CMD_ENDBIT_S 2 +#define ERRINT_CMD_INDEX_M BITFIELD_MASK(1) +#define ERRINT_CMD_INDEX_S 3 +#define ERRINT_DATA_TIMEOUT_M BITFIELD_MASK(1) +#define ERRINT_DATA_TIMEOUT_S 4 +#define ERRINT_DATA_CRC_M BITFIELD_MASK(1) +#define ERRINT_DATA_CRC_S 5 +#define ERRINT_DATA_ENDBIT_M BITFIELD_MASK(1) +#define ERRINT_DATA_ENDBIT_S 6 +#define ERRINT_CURRENT_LIMIT_M BITFIELD_MASK(1) +#define ERRINT_CURRENT_LIMIT_S 7 +#define ERRINT_AUTO_CMD12_M BITFIELD_MASK(1) +#define ERRINT_AUTO_CMD12_S 8 +#define ERRINT_VENDOR_M BITFIELD_MASK(4) +#define ERRINT_VENDOR_S 12 +#define ERRINT_ADMA_M BITFIELD_MASK(1) +#define ERRINT_ADMA_S 9 + +/* Also provide definitions in "normal" form to allow combined masks */ +#define ERRINT_CMD_TIMEOUT_BIT 0x0001 +#define ERRINT_CMD_CRC_BIT 0x0002 +#define ERRINT_CMD_ENDBIT_BIT 0x0004 +#define ERRINT_CMD_INDEX_BIT 0x0008 +#define ERRINT_DATA_TIMEOUT_BIT 0x0010 +#define ERRINT_DATA_CRC_BIT 0x0020 +#define ERRINT_DATA_ENDBIT_BIT 0x0040 +#define ERRINT_CURRENT_LIMIT_BIT 0x0080 +#define ERRINT_AUTO_CMD12_BIT 0x0100 +#define ERRINT_ADMA_BIT 0x0200 + +/* Masks to select CMD vs. DATA errors */ +#define ERRINT_CMD_ERRS (ERRINT_CMD_TIMEOUT_BIT | ERRINT_CMD_CRC_BIT |\ + ERRINT_CMD_ENDBIT_BIT | ERRINT_CMD_INDEX_BIT) +#define ERRINT_DATA_ERRS (ERRINT_DATA_TIMEOUT_BIT | ERRINT_DATA_CRC_BIT |\ + ERRINT_DATA_ENDBIT_BIT | ERRINT_ADMA_BIT) +#define ERRINT_TRANSFER_ERRS (ERRINT_CMD_ERRS | ERRINT_DATA_ERRS) + +/* SD_WakeupCntr_BlockGapCntrl : Offset 0x02A , size = bytes */ +/* SD_ClockCntrl : Offset 0x02C , size = bytes */ +/* SD_SoftwareReset_TimeoutCntrl : Offset 0x02E , size = bytes */ +/* SD_IntrStatus : Offset 0x030 , size = bytes */ +/* SD_ErrorIntrStatus : Offset 0x032 , size = bytes */ +/* SD_IntrStatusEnable : Offset 0x034 , size = bytes */ +/* SD_ErrorIntrStatusEnable : Offset 0x036 , size = bytes */ +/* SD_IntrSignalEnable : Offset 0x038 , size = bytes */ +/* SD_ErrorIntrSignalEnable : Offset 0x03A , size = bytes */ +/* SD_CMD12ErrorStatus : Offset 0x03C , size = bytes */ +/* SD_Capabilities : Offset 0x040 , size = bytes */ +/* SD_MaxCurCap : Offset 0x048 , size = bytes */ +/* SD_MaxCurCap_Reserved: Offset 0x04C , size = bytes */ +/* SD_SlotInterruptStatus: Offset 0x0FC , size = bytes */ +/* SD_HostControllerVersion : Offset 0x0FE , size = bytes */ + +/* SDIO Host Control Register DMA Mode Definitions */ +#define SDIOH_SDMA_MODE 0 +#define SDIOH_ADMA1_MODE 1 +#define SDIOH_ADMA2_MODE 2 +#define SDIOH_ADMA2_64_MODE 3 + +#define ADMA2_ATTRIBUTE_VALID (1 << 0) /* ADMA Descriptor line valid */ +#define ADMA2_ATTRIBUTE_END (1 << 1) /* End of Descriptor */ +#define ADMA2_ATTRIBUTE_INT (1 << 2) /* Interrupt when line is done */ +#define ADMA2_ATTRIBUTE_ACT_NOP (0 << 4) /* Skip current line, go to next. */ +#define ADMA2_ATTRIBUTE_ACT_RSV (1 << 4) /* Same as NOP */ +#define ADMA1_ATTRIBUTE_ACT_SET (1 << 4) /* ADMA1 Only - set transfer length */ +#define ADMA2_ATTRIBUTE_ACT_TRAN (2 << 4) /* Transfer Data of one descriptor line. */ +#define ADMA2_ATTRIBUTE_ACT_LINK (3 << 4) /* Link Descriptor */ + +/* ADMA2 Descriptor Table Entry for 32-bit Address */ +typedef struct adma2_dscr_32b { + uint32 len_attr; + uint32 phys_addr; +} adma2_dscr_32b_t; + +/* ADMA1 Descriptor Table Entry */ +typedef struct adma1_dscr { + uint32 phys_addr_attr; +} adma1_dscr_t; + +#endif /* _SDIOH_H */ diff --git a/drivers/net/wireless/bcmdhd/include/sdiovar.h b/drivers/net/wireless/bcmdhd/include/sdiovar.h new file mode 100644 index 0000000000000..55a3d3490c306 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/sdiovar.h @@ -0,0 +1,58 @@ +/* + * Structure used by apps whose drivers access SDIO drivers. + * Pulled out separately so dhdu and wlu can both use it. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sdiovar.h 277737 2011-08-16 17:54:59Z $ + */ + +#ifndef _sdiovar_h_ +#define _sdiovar_h_ + +#include + +/* require default structure packing */ +#define BWL_DEFAULT_PACKING +#include + +typedef struct sdreg { + int func; + int offset; + int value; +} sdreg_t; + +/* Common msglevel constants */ +#define SDH_ERROR_VAL 0x0001 /* Error */ +#define SDH_TRACE_VAL 0x0002 /* Trace */ +#define SDH_INFO_VAL 0x0004 /* Info */ +#define SDH_DEBUG_VAL 0x0008 /* Debug */ +#define SDH_DATA_VAL 0x0010 /* Data */ +#define SDH_CTRL_VAL 0x0020 /* Control Regs */ +#define SDH_LOG_VAL 0x0040 /* Enable bcmlog */ +#define SDH_DMA_VAL 0x0080 /* DMA */ + +#define NUM_PREV_TRANSACTIONS 16 + + +#include + +#endif /* _sdiovar_h_ */ diff --git a/drivers/net/wireless/bcmdhd/include/siutils.h b/drivers/net/wireless/bcmdhd/include/siutils.h new file mode 100644 index 0000000000000..4e7aeb71cb027 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/siutils.h @@ -0,0 +1,277 @@ +/* + * Misc utility routines for accessing the SOC Interconnects + * of Broadcom HNBU chips. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: siutils.h 335486 2012-05-28 09:47:55Z $ + */ + + +#ifndef _siutils_h_ +#define _siutils_h_ + + +struct si_pub { + uint socitype; + + uint bustype; + uint buscoretype; + uint buscorerev; + uint buscoreidx; + int ccrev; + uint32 cccaps; + uint32 cccaps_ext; + int pmurev; + uint32 pmucaps; + uint boardtype; + uint boardvendor; + uint boardflags; + uint boardflags2; + uint chip; + uint chiprev; + uint chippkg; + uint32 chipst; + bool issim; + uint socirev; + bool pci_pr32414; + +}; + + +typedef const struct si_pub si_t; + + + +#define SI_OSH NULL + +#define BADIDX (SI_MAXCORES + 1) + + +#define XTAL 0x1 +#define PLL 0x2 + + +#define CLK_FAST 0 +#define CLK_DYNAMIC 2 + + +#define GPIO_DRV_PRIORITY 0 +#define GPIO_APP_PRIORITY 1 +#define GPIO_HI_PRIORITY 2 + + +#define GPIO_PULLUP 0 +#define GPIO_PULLDN 1 + + +#define GPIO_REGEVT 0 +#define GPIO_REGEVT_INTMSK 1 +#define GPIO_REGEVT_INTPOL 2 + + +#define SI_DEVPATH_BUFSZ 16 + + +#define SI_DOATTACH 1 +#define SI_PCIDOWN 2 +#define SI_PCIUP 3 + +#define ISSIM_ENAB(sih) 0 + + +#if defined(BCMPMUCTL) +#define PMUCTL_ENAB(sih) (BCMPMUCTL) +#else +#define PMUCTL_ENAB(sih) ((sih)->cccaps & CC_CAP_PMU) +#endif + + +#if defined(BCMPMUCTL) && BCMPMUCTL +#define CCCTL_ENAB(sih) (0) +#define CCPLL_ENAB(sih) (0) +#else +#define CCCTL_ENAB(sih) ((sih)->cccaps & CC_CAP_PWR_CTL) +#define CCPLL_ENAB(sih) ((sih)->cccaps & CC_CAP_PLL_MASK) +#endif + +typedef void (*gpio_handler_t)(uint32 stat, void *arg); + + + +extern si_t *si_attach(uint pcidev, osl_t *osh, void *regs, uint bustype, + void *sdh, char **vars, uint *varsz); +extern si_t *si_kattach(osl_t *osh); +extern void si_detach(si_t *sih); +extern bool si_pci_war16165(si_t *sih); + +extern uint si_corelist(si_t *sih, uint coreid[]); +extern uint si_coreid(si_t *sih); +extern uint si_flag(si_t *sih); +extern uint si_intflag(si_t *sih); +extern uint si_coreidx(si_t *sih); +extern uint si_coreunit(si_t *sih); +extern uint si_corevendor(si_t *sih); +extern uint si_corerev(si_t *sih); +extern void *si_osh(si_t *sih); +extern void si_setosh(si_t *sih, osl_t *osh); +extern uint si_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val); +extern void *si_coreregs(si_t *sih); +extern uint si_wrapperreg(si_t *sih, uint32 offset, uint32 mask, uint32 val); +extern uint32 si_core_cflags(si_t *sih, uint32 mask, uint32 val); +extern void si_core_cflags_wo(si_t *sih, uint32 mask, uint32 val); +extern uint32 si_core_sflags(si_t *sih, uint32 mask, uint32 val); +extern bool si_iscoreup(si_t *sih); +extern uint si_findcoreidx(si_t *sih, uint coreid, uint coreunit); +extern void *si_setcoreidx(si_t *sih, uint coreidx); +extern void *si_setcore(si_t *sih, uint coreid, uint coreunit); +extern void *si_switch_core(si_t *sih, uint coreid, uint *origidx, uint *intr_val); +extern void si_restore_core(si_t *sih, uint coreid, uint intr_val); +extern int si_numaddrspaces(si_t *sih); +extern uint32 si_addrspace(si_t *sih, uint asidx); +extern uint32 si_addrspacesize(si_t *sih, uint asidx); +extern int si_corebist(si_t *sih); +extern void si_core_reset(si_t *sih, uint32 bits, uint32 resetbits); +extern void si_core_disable(si_t *sih, uint32 bits); +extern uint32 si_clock_rate(uint32 pll_type, uint32 n, uint32 m); +extern bool si_read_pmu_autopll(si_t *sih); +extern uint32 si_clock(si_t *sih); +extern uint32 si_alp_clock(si_t *sih); +extern uint32 si_ilp_clock(si_t *sih); +extern void si_pci_setup(si_t *sih, uint coremask); +extern void si_pcmcia_init(si_t *sih); +extern void si_setint(si_t *sih, int siflag); +extern bool si_backplane64(si_t *sih); +extern void si_register_intr_callback(si_t *sih, void *intrsoff_fn, void *intrsrestore_fn, + void *intrsenabled_fn, void *intr_arg); +extern void si_deregister_intr_callback(si_t *sih); +extern void si_clkctl_init(si_t *sih); +extern uint16 si_clkctl_fast_pwrup_delay(si_t *sih); +extern bool si_clkctl_cc(si_t *sih, uint mode); +extern int si_clkctl_xtal(si_t *sih, uint what, bool on); +extern uint32 si_gpiotimerval(si_t *sih, uint32 mask, uint32 val); +extern void si_btcgpiowar(si_t *sih); +extern bool si_deviceremoved(si_t *sih); +extern uint32 si_socram_size(si_t *sih); +extern uint32 si_socdevram_size(si_t *sih); +extern void si_socdevram(si_t *sih, bool set, uint8 *ennable, uint8 *protect); +extern bool si_socdevram_pkg(si_t *sih); + +extern void si_watchdog(si_t *sih, uint ticks); +extern void si_watchdog_ms(si_t *sih, uint32 ms); +extern void *si_gpiosetcore(si_t *sih); +extern uint32 si_gpiocontrol(si_t *sih, uint32 mask, uint32 val, uint8 priority); +extern uint32 si_gpioouten(si_t *sih, uint32 mask, uint32 val, uint8 priority); +extern uint32 si_gpioout(si_t *sih, uint32 mask, uint32 val, uint8 priority); +extern uint32 si_gpioin(si_t *sih); +extern uint32 si_gpiointpolarity(si_t *sih, uint32 mask, uint32 val, uint8 priority); +extern uint32 si_gpiointmask(si_t *sih, uint32 mask, uint32 val, uint8 priority); +extern uint32 si_gpioled(si_t *sih, uint32 mask, uint32 val); +extern uint32 si_gpioreserve(si_t *sih, uint32 gpio_num, uint8 priority); +extern uint32 si_gpiorelease(si_t *sih, uint32 gpio_num, uint8 priority); +extern uint32 si_gpiopull(si_t *sih, bool updown, uint32 mask, uint32 val); +extern uint32 si_gpioevent(si_t *sih, uint regtype, uint32 mask, uint32 val); +extern uint32 si_gpio_int_enable(si_t *sih, bool enable); + + +extern void *si_gpio_handler_register(si_t *sih, uint32 e, bool lev, gpio_handler_t cb, void *arg); +extern void si_gpio_handler_unregister(si_t *sih, void* gpioh); +extern void si_gpio_handler_process(si_t *sih); + + +extern bool si_pci_pmecap(si_t *sih); +struct osl_info; +extern bool si_pci_fastpmecap(struct osl_info *osh); +extern bool si_pci_pmestat(si_t *sih); +extern void si_pci_pmeclr(si_t *sih); +extern void si_pci_pmeen(si_t *sih); +extern uint si_pcie_readreg(void *sih, uint addrtype, uint offset); + +extern void si_sdio_init(si_t *sih); + +extern uint16 si_d11_devid(si_t *sih); +extern int si_corepciid(si_t *sih, uint func, uint16 *pcivendor, uint16 *pcidevice, + uint8 *pciclass, uint8 *pcisubclass, uint8 *pciprogif, uint8 *pciheader); + +#define si_eci(sih) 0 +#define si_eci_init(sih) (0) +#define si_eci_notify_bt(sih, type, val) (0) +#define si_seci(sih) 0 +static INLINE void * si_seci_init(si_t *sih, uint8 use_seci) {return NULL;} +#define si_seci_down(sih) do { } while (0) + + +extern bool si_is_otp_disabled(si_t *sih); +extern bool si_is_otp_powered(si_t *sih); +extern void si_otp_power(si_t *sih, bool on); +extern void si_set_otp_wr_volts(si_t *sih); +extern void si_set_otp_rd_volts(si_t *sih); + + +extern bool si_is_sprom_available(si_t *sih); +extern bool si_is_sprom_enabled(si_t *sih); +extern void si_sprom_enable(si_t *sih, bool enable); + + +extern int si_cis_source(si_t *sih); +#define CIS_DEFAULT 0 +#define CIS_SROM 1 +#define CIS_OTP 2 + + +#define DEFAULT_FAB 0x0 +#define CSM_FAB7 0x1 +#define TSMC_FAB12 0x2 +#define SMIC_FAB4 0x3 +extern int si_otp_fabid(si_t *sih, uint16 *fabid, bool rw); +extern uint16 si_fabid(si_t *sih); + + +extern int si_devpath(si_t *sih, char *path, int size); + +extern char *si_getdevpathvar(si_t *sih, const char *name); +extern int si_getdevpathintvar(si_t *sih, const char *name); + + +extern uint8 si_pcieclkreq(si_t *sih, uint32 mask, uint32 val); +extern uint32 si_pcielcreg(si_t *sih, uint32 mask, uint32 val); +extern void si_war42780_clkreq(si_t *sih, bool clkreq); +extern void si_pci_sleep(si_t *sih); +extern void si_pci_down(si_t *sih); +extern void si_pci_up(si_t *sih); +extern void si_pcie_war_ovr_update(si_t *sih, uint8 aspm); +extern void si_pcie_extendL1timer(si_t *sih, bool extend); +extern int si_pci_fixcfg(si_t *sih); +extern uint si_pll_reset(si_t *sih); + + + +extern bool si_taclear(si_t *sih, bool details); + + + +extern uint32 si_pciereg(si_t *sih, uint32 offset, uint32 mask, uint32 val, uint type); +extern uint32 si_pcieserdesreg(si_t *sih, uint32 mdioslave, uint32 offset, uint32 mask, uint32 val); + +char *si_getnvramflvar(si_t *sih, const char *name); + + +#endif diff --git a/drivers/net/wireless/bcmdhd/include/trxhdr.h b/drivers/net/wireless/bcmdhd/include/trxhdr.h new file mode 100644 index 0000000000000..b52fb15ba5c08 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/trxhdr.h @@ -0,0 +1,53 @@ +/* + * TRX image file header format. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: trxhdr.h 286295 2011-09-27 06:39:43Z $ + */ + +#ifndef _TRX_HDR_H_ +#define _TRX_HDR_H_ + +#include + +#define TRX_MAGIC 0x30524448 /* "HDR0" */ +#define TRX_VERSION 1 /* Version 1 */ +#define TRX_MAX_LEN 0x3B0000 /* Max length */ +#define TRX_NO_HEADER 1 /* Do not write TRX header */ +#define TRX_GZ_FILES 0x2 /* Contains up to TRX_MAX_OFFSET individual gzip files */ +#define TRX_OVERLAYS 0x4 /* Contains an overlay header after the trx header */ +#define TRX_MAX_OFFSET 3 /* Max number of individual files */ +#define TRX_UNCOMP_IMAGE 0x20 /* Trx contains uncompressed rtecdc.bin image */ +#define TRX_ROMSIM_IMAGE 0x10 /* Trx contains ROM simulation image */ + +struct trx_header { + uint32 magic; /* "HDR0" */ + uint32 len; /* Length of file including header */ + uint32 crc32; /* 32-bit CRC from flag_version to end of file */ + uint32 flag_version; /* 0:15 flags, 16:31 version */ + uint32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of header */ +}; + +/* Compatibility */ +typedef struct trx_header TRXHDR, *PTRXHDR; + +#endif /* _TRX_HDR_H_ */ diff --git a/drivers/net/wireless/bcmdhd/include/typedefs.h b/drivers/net/wireless/bcmdhd/include/typedefs.h new file mode 100644 index 0000000000000..d0902fe808915 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/typedefs.h @@ -0,0 +1,312 @@ +/* + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * $Id: typedefs.h 290055 2011-10-15 21:26:26Z $ + */ + + +#ifndef _TYPEDEFS_H_ +#define _TYPEDEFS_H_ + +#ifdef SITE_TYPEDEFS + + + +#include "site_typedefs.h" + +#else + + + +#ifdef __cplusplus + +#define TYPEDEF_BOOL +#ifndef FALSE +#define FALSE false +#endif +#ifndef TRUE +#define TRUE true +#endif + +#else + + +#endif + +#if defined(__x86_64__) +#define TYPEDEF_UINTPTR +typedef unsigned long long int uintptr; +#endif + + + + + +#if defined(_NEED_SIZE_T_) +typedef long unsigned int size_t; +#endif + + + + + +#if defined(__sparc__) +#define TYPEDEF_ULONG +#endif + + + +#if !defined(LINUX_HYBRID) || defined(LINUX_PORT) +#define TYPEDEF_UINT +#ifndef TARGETENV_android +#define TYPEDEF_USHORT +#define TYPEDEF_ULONG +#endif +#ifdef __KERNEL__ +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)) +#define TYPEDEF_BOOL +#endif + +#if (LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 18)) +#include +#ifdef noinline_for_stack +#define TYPEDEF_BOOL +#endif +#endif +#endif +#endif + + + + + +#if defined(__GNUC__) && defined(__STRICT_ANSI__) +#define TYPEDEF_INT64 +#define TYPEDEF_UINT64 +#endif + + +#if defined(__ICL) + +#define TYPEDEF_INT64 + +#if defined(__STDC__) +#define TYPEDEF_UINT64 +#endif + +#endif + +#if !defined(__DJGPP__) + + +#if defined(__KERNEL__) + + +#if !defined(LINUX_HYBRID) || defined(LINUX_PORT) +#include +#endif + +#else + + +#include + +#endif + +#endif + + + + +#define USE_TYPEDEF_DEFAULTS + +#endif + + + + +#ifdef USE_TYPEDEF_DEFAULTS +#undef USE_TYPEDEF_DEFAULTS + +#ifndef TYPEDEF_BOOL +typedef unsigned char bool; +#endif + + + +#ifndef TYPEDEF_UCHAR +typedef unsigned char uchar; +#endif + +#ifndef TYPEDEF_USHORT +typedef unsigned short ushort; +#endif + +#ifndef TYPEDEF_UINT +typedef unsigned int uint; +#endif + +#ifndef TYPEDEF_ULONG +typedef unsigned long ulong; +#endif + + + +#ifndef TYPEDEF_UINT8 +typedef unsigned char uint8; +#endif + +#ifndef TYPEDEF_UINT16 +typedef unsigned short uint16; +#endif + +#ifndef TYPEDEF_UINT32 +typedef unsigned int uint32; +#endif + +#ifndef TYPEDEF_UINT64 +typedef unsigned long long uint64; +#endif + +#ifndef TYPEDEF_UINTPTR +typedef unsigned int uintptr; +#endif + +#ifndef TYPEDEF_INT8 +typedef signed char int8; +#endif + +#ifndef TYPEDEF_INT16 +typedef signed short int16; +#endif + +#ifndef TYPEDEF_INT32 +typedef signed int int32; +#endif + +#ifndef TYPEDEF_INT64 +typedef signed long long int64; +#endif + + + +#ifndef TYPEDEF_FLOAT32 +typedef float float32; +#endif + +#ifndef TYPEDEF_FLOAT64 +typedef double float64; +#endif + + + +#ifndef TYPEDEF_FLOAT_T + +#if defined(FLOAT32) +typedef float32 float_t; +#else +typedef float64 float_t; +#endif + +#endif + + + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef OFF +#define OFF 0 +#endif + +#ifndef ON +#define ON 1 +#endif + +#define AUTO (-1) + + + +#ifndef PTRSZ +#define PTRSZ sizeof(char*) +#endif + + + +#if defined(__GNUC__) + #define BWL_COMPILER_GNU +#elif defined(__CC_ARM) && __CC_ARM + #define BWL_COMPILER_ARMCC +#else + #error "Unknown compiler!" +#endif + + +#ifndef INLINE + #if defined(BWL_COMPILER_MICROSOFT) + #define INLINE __inline + #elif defined(BWL_COMPILER_GNU) + #define INLINE __inline__ + #elif defined(BWL_COMPILER_ARMCC) + #define INLINE __inline + #else + #define INLINE + #endif +#endif + +#undef TYPEDEF_BOOL +#undef TYPEDEF_UCHAR +#undef TYPEDEF_USHORT +#undef TYPEDEF_UINT +#undef TYPEDEF_ULONG +#undef TYPEDEF_UINT8 +#undef TYPEDEF_UINT16 +#undef TYPEDEF_UINT32 +#undef TYPEDEF_UINT64 +#undef TYPEDEF_UINTPTR +#undef TYPEDEF_INT8 +#undef TYPEDEF_INT16 +#undef TYPEDEF_INT32 +#undef TYPEDEF_INT64 +#undef TYPEDEF_FLOAT32 +#undef TYPEDEF_FLOAT64 +#undef TYPEDEF_FLOAT_T + +#endif + + +#define UNUSED_PARAMETER(x) (void)(x) + + +#define DISCARD_QUAL(ptr, type) ((type *)(uintptr)(ptr)) + + +#include +#endif diff --git a/drivers/net/wireless/bcmdhd/include/wlfc_proto.h b/drivers/net/wireless/bcmdhd/include/wlfc_proto.h new file mode 100644 index 0000000000000..d37105165bab2 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/wlfc_proto.h @@ -0,0 +1,198 @@ +/* +* Copyright (C) 1999-2011, Broadcom Corporation +* +* Unless you and Broadcom execute a separate written software license +* agreement governing use of this software, this software is licensed to you +* under the terms of the GNU General Public License version 2 (the "GPL"), +* available at http://www.broadcom.com/licenses/GPLv2.php, with the +* following added to such license: +* +* As a special exception, the copyright holders of this software give you +* permission to link this software with independent modules, and to copy and +* distribute the resulting executable under terms of your choice, provided that +* you also meet, for each linked independent module, the terms and conditions of +* the license of that module. An independent module is a module which is not +* derived from this software. The special exception does not apply to any +* modifications of the software. +* +* Notwithstanding the above, under no circumstances may you combine this +* software in any way with any other Broadcom software provided under a license +* other than the GPL, without Broadcom's express prior written consent. +* $Id: wlfc_proto.h 277737 2011-08-16 17:54:59Z $ +* +*/ +#ifndef __wlfc_proto_definitions_h__ +#define __wlfc_proto_definitions_h__ + + /* Use TLV to convey WLFC information. + --------------------------------------------------------------------------- + | Type | Len | value | Description + --------------------------------------------------------------------------- + | 1 | 1 | (handle) | MAC OPEN + --------------------------------------------------------------------------- + | 2 | 1 | (handle) | MAC CLOSE + --------------------------------------------------------------------------- + | 3 | 2 | (count, handle, prec_bmp)| Set the credit depth for a MAC dstn + --------------------------------------------------------------------------- + | 4 | 4 | see pkttag comments | TXSTATUS + --------------------------------------------------------------------------- + | 5 | 4 | see pkttag comments | PKKTTAG [host->firmware] + --------------------------------------------------------------------------- + | 6 | 8 | (handle, ifid, MAC) | MAC ADD + --------------------------------------------------------------------------- + | 7 | 8 | (handle, ifid, MAC) | MAC DEL + --------------------------------------------------------------------------- + | 8 | 1 | (rssi) | RSSI - RSSI value for the packet. + --------------------------------------------------------------------------- + | 9 | 1 | (interface ID) | Interface OPEN + --------------------------------------------------------------------------- + | 10 | 1 | (interface ID) | Interface CLOSE + --------------------------------------------------------------------------- + | 11 | 8 | fifo credit returns map | FIFO credits back to the host + | | | | + | | | | -------------------------------------- + | | | | | ac0 | ac1 | ac2 | ac3 | bcmc | atim | + | | | | -------------------------------------- + | | | | + --------------------------------------------------------------------------- + | 12 | 2 | MAC handle, | Host provides a bitmap of pending + | | | AC[0-3] traffic bitmap | unicast traffic for MAC-handle dstn. + | | | | [host->firmware] + --------------------------------------------------------------------------- + | 13 | 3 | (count, handle, prec_bmp)| One time request for packet to a specific + | | | | MAC destination. + --------------------------------------------------------------------------- + | 255 | N/A | N/A | FILLER - This is a special type + | | | | that has no length or value. + | | | | Typically used for padding. + --------------------------------------------------------------------------- + */ + +#define WLFC_CTL_TYPE_MAC_OPEN 1 +#define WLFC_CTL_TYPE_MAC_CLOSE 2 +#define WLFC_CTL_TYPE_MAC_REQUEST_CREDIT 3 +#define WLFC_CTL_TYPE_TXSTATUS 4 +#define WLFC_CTL_TYPE_PKTTAG 5 + +#define WLFC_CTL_TYPE_MACDESC_ADD 6 +#define WLFC_CTL_TYPE_MACDESC_DEL 7 +#define WLFC_CTL_TYPE_RSSI 8 + +#define WLFC_CTL_TYPE_INTERFACE_OPEN 9 +#define WLFC_CTL_TYPE_INTERFACE_CLOSE 10 + +#define WLFC_CTL_TYPE_FIFO_CREDITBACK 11 + +#define WLFC_CTL_TYPE_PENDING_TRAFFIC_BMP 12 +#define WLFC_CTL_TYPE_MAC_REQUEST_PACKET 13 + +#define WLFC_CTL_TYPE_FILLER 255 + +#define WLFC_CTL_VALUE_LEN_MACDESC 8 /* handle, interface, MAC */ + +#define WLFC_CTL_VALUE_LEN_MAC 1 /* MAC-handle */ +#define WLFC_CTL_VALUE_LEN_RSSI 1 + +#define WLFC_CTL_VALUE_LEN_INTERFACE 1 +#define WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP 2 + +#define WLFC_CTL_VALUE_LEN_TXSTATUS 4 +#define WLFC_CTL_VALUE_LEN_PKTTAG 4 + +/* enough space to host all 4 ACs, bc/mc and atim fifo credit */ +#define WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK 6 + +#define WLFC_CTL_VALUE_LEN_REQUEST_CREDIT 3 /* credit, MAC-handle, prec_bitmap */ +#define WLFC_CTL_VALUE_LEN_REQUEST_PACKET 3 /* credit, MAC-handle, prec_bitmap */ + + + +#define WLFC_PKTID_GEN_MASK 0x80000000 +#define WLFC_PKTID_GEN_SHIFT 31 + +#define WLFC_PKTID_GEN(x) (((x) & WLFC_PKTID_GEN_MASK) >> WLFC_PKTID_GEN_SHIFT) +#define WLFC_PKTID_SETGEN(x, gen) (x) = ((x) & ~WLFC_PKTID_GEN_MASK) | \ + (((gen) << WLFC_PKTID_GEN_SHIFT) & WLFC_PKTID_GEN_MASK) + +#define WLFC_PKTFLAG_PKTFROMHOST 0x01 +#define WLFC_PKTFLAG_PKT_REQUESTED 0x02 + +#define WL_TXSTATUS_FLAGS_MASK 0xf /* allow 4 bits only */ +#define WL_TXSTATUS_FLAGS_SHIFT 27 + +#define WL_TXSTATUS_SET_FLAGS(x, flags) ((x) = \ + ((x) & ~(WL_TXSTATUS_FLAGS_MASK << WL_TXSTATUS_FLAGS_SHIFT)) | \ + (((flags) & WL_TXSTATUS_FLAGS_MASK) << WL_TXSTATUS_FLAGS_SHIFT)) +#define WL_TXSTATUS_GET_FLAGS(x) (((x) >> WL_TXSTATUS_FLAGS_SHIFT) & \ + WL_TXSTATUS_FLAGS_MASK) + +#define WL_TXSTATUS_FIFO_MASK 0x7 /* allow 3 bits for FIFO ID */ +#define WL_TXSTATUS_FIFO_SHIFT 24 + +#define WL_TXSTATUS_SET_FIFO(x, flags) ((x) = \ + ((x) & ~(WL_TXSTATUS_FIFO_MASK << WL_TXSTATUS_FIFO_SHIFT)) | \ + (((flags) & WL_TXSTATUS_FIFO_MASK) << WL_TXSTATUS_FIFO_SHIFT)) +#define WL_TXSTATUS_GET_FIFO(x) (((x) >> WL_TXSTATUS_FIFO_SHIFT) & WL_TXSTATUS_FIFO_MASK) + +#define WL_TXSTATUS_PKTID_MASK 0xffffff /* allow 24 bits */ +#define WL_TXSTATUS_SET_PKTID(x, num) ((x) = \ + ((x) & ~WL_TXSTATUS_PKTID_MASK) | (num)) +#define WL_TXSTATUS_GET_PKTID(x) ((x) & WL_TXSTATUS_PKTID_MASK) + +/* 32 STA should be enough??, 6 bits; Must be power of 2 */ +#define WLFC_MAC_DESC_TABLE_SIZE 32 +#define WLFC_MAX_IFNUM 16 +#define WLFC_MAC_DESC_ID_INVALID 0xff + +/* b[7:5] -reuse guard, b[4:0] -value */ +#define WLFC_MAC_DESC_GET_LOOKUP_INDEX(x) ((x) & 0x1f) + +#define WLFC_PKTFLAG_SET_PKTREQUESTED(x) (x) |= \ + (WLFC_PKTFLAG_PKT_REQUESTED << WL_TXSTATUS_FLAGS_SHIFT) + +#define WLFC_PKTFLAG_CLR_PKTREQUESTED(x) (x) &= \ + ~(WLFC_PKTFLAG_PKT_REQUESTED << WL_TXSTATUS_FLAGS_SHIFT) + +#define WL_TXSTATUS_GENERATION_MASK 1 +#define WL_TXSTATUS_GENERATION_SHIFT 31 + +#define WLFC_PKTFLAG_SET_GENERATION(x, gen) ((x) = \ + ((x) & ~(WL_TXSTATUS_GENERATION_MASK << WL_TXSTATUS_GENERATION_SHIFT)) | \ + (((gen) & WL_TXSTATUS_GENERATION_MASK) << WL_TXSTATUS_GENERATION_SHIFT)) + +#define WLFC_PKTFLAG_GENERATION(x) (((x) >> WL_TXSTATUS_GENERATION_SHIFT) & \ + WL_TXSTATUS_GENERATION_MASK) + +#define WLFC_MAX_PENDING_DATALEN 120 + +/* host is free to discard the packet */ +#define WLFC_CTL_PKTFLAG_DISCARD 0 +/* D11 suppressed a packet */ +#define WLFC_CTL_PKTFLAG_D11SUPPRESS 1 +/* WL firmware suppressed a packet because MAC is + already in PSMode (short time window) +*/ +#define WLFC_CTL_PKTFLAG_WLSUPPRESS 2 +/* Firmware tossed this packet */ +#define WLFC_CTL_PKTFLAG_TOSSED_BYWLC 3 + +#define WLFC_D11_STATUS_INTERPRET(txs) ((((txs)->status & TX_STATUS_SUPR_MASK) >> \ + TX_STATUS_SUPR_SHIFT)) ? WLFC_CTL_PKTFLAG_D11SUPPRESS : WLFC_CTL_PKTFLAG_DISCARD + +#ifdef PROP_TXSTATUS_DEBUG +#define WLFC_DBGMESG(x) printf x +/* wlfc-breadcrumb */ +#define WLFC_BREADCRUMB(x) do {if ((x) == NULL) \ + {printf("WLFC: %s():%d:caller:%p\n", \ + __FUNCTION__, __LINE__, __builtin_return_address(0));}} while (0) +#define WLFC_PRINTMAC(banner, ea) do {printf("%s MAC: [%02x:%02x:%02x:%02x:%02x:%02x]\n", \ + banner, ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]); } while (0) +#define WLFC_WHEREIS(s) printf("WLFC: at %s():%d, %s\n", __FUNCTION__, __LINE__, (s)) +#else +#define WLFC_DBGMESG(x) +#define WLFC_BREADCRUMB(x) +#define WLFC_PRINTMAC(banner, ea) +#define WLFC_WHEREIS(s) +#endif + +#endif /* __wlfc_proto_definitions_h__ */ diff --git a/drivers/net/wireless/bcmdhd/include/wlioctl.h b/drivers/net/wireless/bcmdhd/include/wlioctl.h new file mode 100644 index 0000000000000..891d15c03c927 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/include/wlioctl.h @@ -0,0 +1,2811 @@ +/* + * Custom OID/ioctl definitions for + * Broadcom 802.11abg Networking Device Driver + * + * Definitions subject to change without notice. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wlioctl.h 331292 2012-05-04 09:04:23Z $ + */ + + +#ifndef _wlioctl_h_ +#define _wlioctl_h_ + +#include +#include +#include +#include +#include +#include + +#include + +#ifndef INTF_NAME_SIZ +#define INTF_NAME_SIZ 16 +#endif + + +typedef struct remote_ioctl { + cdc_ioctl_t msg; + uint data_len; + char intf_name[INTF_NAME_SIZ]; +} rem_ioctl_t; +#define REMOTE_SIZE sizeof(rem_ioctl_t) + +#define ACTION_FRAME_SIZE 1040 + +typedef struct wl_action_frame { + struct ether_addr da; + uint16 len; + uint32 packetId; + uint8 data[ACTION_FRAME_SIZE]; +} wl_action_frame_t; + +#define WL_WIFI_ACTION_FRAME_SIZE sizeof(struct wl_action_frame) + +typedef struct ssid_info +{ + uint8 ssid_len; + uint8 ssid[32]; +} ssid_info_t; + +typedef struct wl_af_params { + uint32 channel; + int32 dwell_time; + struct ether_addr BSSID; + wl_action_frame_t action_frame; +} wl_af_params_t; + +#define WL_WIFI_AF_PARAMS_SIZE sizeof(struct wl_af_params) + + +#define BWL_DEFAULT_PACKING +#include + + + + + +#define LEGACY2_WL_BSS_INFO_VERSION 108 + + +typedef struct wl_bss_info_108 { + uint32 version; + uint32 length; + struct ether_addr BSSID; + uint16 beacon_period; + uint16 capability; + uint8 SSID_len; + uint8 SSID[32]; + struct { + uint count; + uint8 rates[16]; + } rateset; + chanspec_t chanspec; + uint16 atim_window; + uint8 dtim_period; + int16 RSSI; + int8 phy_noise; + + uint8 n_cap; + uint32 nbss_cap; + uint8 ctl_ch; + uint32 reserved32[1]; + uint8 flags; + uint8 reserved[3]; + uint8 basic_mcs[MCSSET_LEN]; + + uint16 ie_offset; + uint32 ie_length; + + +} wl_bss_info_108_t; + +#define WL_BSS_INFO_VERSION 109 + + +typedef struct wl_bss_info { + uint32 version; + uint32 length; + struct ether_addr BSSID; + uint16 beacon_period; + uint16 capability; + uint8 SSID_len; + uint8 SSID[32]; + struct { + uint count; + uint8 rates[16]; + } rateset; + chanspec_t chanspec; + uint16 atim_window; + uint8 dtim_period; + int16 RSSI; + int8 phy_noise; + + uint8 n_cap; + uint32 nbss_cap; + uint8 ctl_ch; + uint32 reserved32[1]; + uint8 flags; + uint8 reserved[3]; + uint8 basic_mcs[MCSSET_LEN]; + + uint16 ie_offset; + uint32 ie_length; + int16 SNR; + + +} wl_bss_info_t; + +typedef struct wl_bsscfg { + uint32 wsec; + uint32 WPA_auth; + uint32 wsec_index; + uint32 associated; + uint32 BSS; + uint32 phytest_on; + struct ether_addr prev_BSSID; + struct ether_addr BSSID; +} wl_bsscfg_t; + +typedef struct wl_bss_config { + uint32 atim_window; + uint32 beacon_period; + uint32 chanspec; +} wl_bss_config_t; + + +typedef struct wlc_ssid { + uint32 SSID_len; + uchar SSID[32]; +} wlc_ssid_t; + + +#define WL_BSSTYPE_INFRA 1 +#define WL_BSSTYPE_INDEP 0 +#define WL_BSSTYPE_ANY 2 + + +#define WL_SCANFLAGS_PASSIVE 0x01 +#define WL_SCANFLAGS_RESERVED 0x02 +#define WL_SCANFLAGS_PROHIBITED 0x04 + +#define WL_SCAN_PARAMS_SSID_MAX 10 + +typedef struct wl_scan_params { + wlc_ssid_t ssid; + struct ether_addr bssid; + int8 bss_type; + uint8 scan_type; + int32 nprobes; + int32 active_time; + int32 passive_time; + int32 home_time; + int32 channel_num; + uint16 channel_list[1]; +} wl_scan_params_t; + + +#define WL_SCAN_PARAMS_FIXED_SIZE 64 + + +#define WL_SCAN_PARAMS_COUNT_MASK 0x0000ffff +#define WL_SCAN_PARAMS_NSSID_SHIFT 16 + +#define WL_SCAN_ACTION_START 1 +#define WL_SCAN_ACTION_CONTINUE 2 +#define WL_SCAN_ACTION_ABORT 3 + +#define ISCAN_REQ_VERSION 1 + + +typedef struct wl_iscan_params { + uint32 version; + uint16 action; + uint16 scan_duration; + wl_scan_params_t params; +} wl_iscan_params_t; + + +#define WL_ISCAN_PARAMS_FIXED_SIZE (OFFSETOF(wl_iscan_params_t, params) + sizeof(wlc_ssid_t)) + +typedef struct wl_scan_results { + uint32 buflen; + uint32 version; + uint32 count; + wl_bss_info_t bss_info[1]; +} wl_scan_results_t; + + +#define WL_SCAN_RESULTS_FIXED_SIZE (sizeof(wl_scan_results_t) - sizeof(wl_bss_info_t)) + + +#define WL_SCAN_RESULTS_SUCCESS 0 +#define WL_SCAN_RESULTS_PARTIAL 1 +#define WL_SCAN_RESULTS_PENDING 2 +#define WL_SCAN_RESULTS_ABORTED 3 +#define WL_SCAN_RESULTS_NO_MEM 4 + + +#define DNGL_RXCTXT_SIZE 45 + +#if defined(SIMPLE_ISCAN) +#define ISCAN_RETRY_CNT 5 +#define ISCAN_STATE_IDLE 0 +#define ISCAN_STATE_SCANING 1 +#define ISCAN_STATE_PENDING 2 + + +#define WLC_IW_ISCAN_MAXLEN 2048 +typedef struct iscan_buf { + struct iscan_buf * next; + char iscan_buf[WLC_IW_ISCAN_MAXLEN]; +} iscan_buf_t; +#endif + +#define ESCAN_REQ_VERSION 1 + +typedef struct wl_escan_params { + uint32 version; + uint16 action; + uint16 sync_id; + wl_scan_params_t params; +} wl_escan_params_t; + +#define WL_ESCAN_PARAMS_FIXED_SIZE (OFFSETOF(wl_escan_params_t, params) + sizeof(wlc_ssid_t)) + +typedef struct wl_escan_result { + uint32 buflen; + uint32 version; + uint16 sync_id; + uint16 bss_count; + wl_bss_info_t bss_info[1]; +} wl_escan_result_t; + +#define WL_ESCAN_RESULTS_FIXED_SIZE (sizeof(wl_escan_result_t) - sizeof(wl_bss_info_t)) + + +typedef struct wl_iscan_results { + uint32 status; + wl_scan_results_t results; +} wl_iscan_results_t; + + +#define WL_ISCAN_RESULTS_FIXED_SIZE \ + (WL_SCAN_RESULTS_FIXED_SIZE + OFFSETOF(wl_iscan_results_t, results)) + +typedef struct wl_probe_params { + wlc_ssid_t ssid; + struct ether_addr bssid; + struct ether_addr mac; +} wl_probe_params_t; + +#define WL_NUMRATES 16 +typedef struct wl_rateset { + uint32 count; + uint8 rates[WL_NUMRATES]; +} wl_rateset_t; + +typedef struct wl_rateset_args { + uint32 count; + uint8 rates[WL_NUMRATES]; + uint8 mcs[MCSSET_LEN]; +} wl_rateset_args_t; + + +typedef struct wl_uint32_list { + + uint32 count; + + uint32 element[1]; +} wl_uint32_list_t; + + +typedef struct wl_assoc_params { + struct ether_addr bssid; + uint16 bssid_cnt; + int32 chanspec_num; + chanspec_t chanspec_list[1]; +} wl_assoc_params_t; +#define WL_ASSOC_PARAMS_FIXED_SIZE (sizeof(wl_assoc_params_t) - sizeof(chanspec_t)) + + +typedef wl_assoc_params_t wl_reassoc_params_t; +#define WL_REASSOC_PARAMS_FIXED_SIZE WL_ASSOC_PARAMS_FIXED_SIZE + + +typedef wl_assoc_params_t wl_join_assoc_params_t; +#define WL_JOIN_ASSOC_PARAMS_FIXED_SIZE WL_ASSOC_PARAMS_FIXED_SIZE + + +typedef struct wl_join_params { + wlc_ssid_t ssid; + wl_assoc_params_t params; +} wl_join_params_t; +#define WL_JOIN_PARAMS_FIXED_SIZE (sizeof(wl_join_params_t) - sizeof(chanspec_t)) + + +typedef struct wl_join_scan_params { + uint8 scan_type; + int32 nprobes; + int32 active_time; + int32 passive_time; + int32 home_time; +} wl_join_scan_params_t; + + +typedef struct wl_extjoin_params { + wlc_ssid_t ssid; + wl_join_scan_params_t scan; + wl_join_assoc_params_t assoc; +} wl_extjoin_params_t; +#define WL_EXTJOIN_PARAMS_FIXED_SIZE (sizeof(wl_extjoin_params_t) - sizeof(chanspec_t)) + +typedef struct { + uint32 num; + chanspec_t list[1]; +} chanspec_list_t; + + +#define NRATE_MCS_INUSE 0x00000080 +#define NRATE_RATE_MASK 0x0000007f +#define NRATE_STF_MASK 0x0000ff00 +#define NRATE_STF_SHIFT 8 +#define NRATE_OVERRIDE 0x80000000 +#define NRATE_OVERRIDE_MCS_ONLY 0x40000000 +#define NRATE_SGI_MASK 0x00800000 +#define NRATE_SGI_SHIFT 23 +#define NRATE_LDPC_CODING 0x00400000 +#define NRATE_LDPC_SHIFT 22 +#define NRATE_BCMC_OVERRIDE 0x00200000 +#define NRATE_BCMC_SHIFT 21 + +#define NRATE_STF_SISO 0 +#define NRATE_STF_CDD 1 +#define NRATE_STF_STBC 2 +#define NRATE_STF_SDM 3 + +#define ANTENNA_NUM_1 1 +#define ANTENNA_NUM_2 2 +#define ANTENNA_NUM_3 3 +#define ANTENNA_NUM_4 4 + +#define ANT_SELCFG_AUTO 0x80 +#define ANT_SELCFG_MASK 0x33 +#define ANT_SELCFG_MAX 4 +#define ANT_SELCFG_TX_UNICAST 0 +#define ANT_SELCFG_RX_UNICAST 1 +#define ANT_SELCFG_TX_DEF 2 +#define ANT_SELCFG_RX_DEF 3 + +#define MAX_STREAMS_SUPPORTED 4 + +typedef struct { + uint8 ant_config[ANT_SELCFG_MAX]; + uint8 num_antcfg; +} wlc_antselcfg_t; + +#define HIGHEST_SINGLE_STREAM_MCS 7 + +#define MAX_CCA_CHANNELS 38 +#define MAX_CCA_SECS 60 + +#define IBSS_MED 15 +#define IBSS_HI 25 +#define OBSS_MED 12 +#define OBSS_HI 25 +#define INTERFER_MED 5 +#define INTERFER_HI 10 + +#define CCA_FLAG_2G_ONLY 0x01 +#define CCA_FLAG_5G_ONLY 0x02 +#define CCA_FLAG_IGNORE_DURATION 0x04 +#define CCA_FLAGS_PREFER_1_6_11 0x10 +#define CCA_FLAG_IGNORE_INTERFER 0x20 + +#define CCA_ERRNO_BAND 1 +#define CCA_ERRNO_DURATION 2 +#define CCA_ERRNO_PREF_CHAN 3 +#define CCA_ERRNO_INTERFER 4 +#define CCA_ERRNO_TOO_FEW 5 + +typedef struct { + uint32 duration; + uint32 congest_ibss; + + uint32 congest_obss; + uint32 interference; + uint32 timestamp; +} cca_congest_t; + +typedef struct { + chanspec_t chanspec; + uint8 num_secs; + cca_congest_t secs[1]; +} cca_congest_channel_req_t; + +#define WLC_CNTRY_BUF_SZ 4 + +typedef struct wl_country { + char country_abbrev[WLC_CNTRY_BUF_SZ]; + int32 rev; + char ccode[WLC_CNTRY_BUF_SZ]; +} wl_country_t; + +typedef struct wl_channels_in_country { + uint32 buflen; + uint32 band; + char country_abbrev[WLC_CNTRY_BUF_SZ]; + uint32 count; + uint32 channel[1]; +} wl_channels_in_country_t; + +typedef struct wl_country_list { + uint32 buflen; + uint32 band_set; + uint32 band; + uint32 count; + char country_abbrev[1]; +} wl_country_list_t; + +#define WL_NUM_RPI_BINS 8 +#define WL_RM_TYPE_BASIC 1 +#define WL_RM_TYPE_CCA 2 +#define WL_RM_TYPE_RPI 3 + +#define WL_RM_FLAG_PARALLEL (1<<0) + +#define WL_RM_FLAG_LATE (1<<1) +#define WL_RM_FLAG_INCAPABLE (1<<2) +#define WL_RM_FLAG_REFUSED (1<<3) + +typedef struct wl_rm_req_elt { + int8 type; + int8 flags; + chanspec_t chanspec; + uint32 token; + uint32 tsf_h; + uint32 tsf_l; + uint32 dur; +} wl_rm_req_elt_t; + +typedef struct wl_rm_req { + uint32 token; + uint32 count; + void *cb; + void *cb_arg; + wl_rm_req_elt_t req[1]; +} wl_rm_req_t; +#define WL_RM_REQ_FIXED_LEN OFFSETOF(wl_rm_req_t, req) + +typedef struct wl_rm_rep_elt { + int8 type; + int8 flags; + chanspec_t chanspec; + uint32 token; + uint32 tsf_h; + uint32 tsf_l; + uint32 dur; + uint32 len; + uint8 data[1]; +} wl_rm_rep_elt_t; +#define WL_RM_REP_ELT_FIXED_LEN 24 + +#define WL_RPI_REP_BIN_NUM 8 +typedef struct wl_rm_rpi_rep { + uint8 rpi[WL_RPI_REP_BIN_NUM]; + int8 rpi_max[WL_RPI_REP_BIN_NUM]; +} wl_rm_rpi_rep_t; + +typedef struct wl_rm_rep { + uint32 token; + uint32 len; + wl_rm_rep_elt_t rep[1]; +} wl_rm_rep_t; +#define WL_RM_REP_FIXED_LEN 8 + + +typedef enum sup_auth_status { + + WLC_SUP_DISCONNECTED = 0, + WLC_SUP_CONNECTING, + WLC_SUP_IDREQUIRED, + WLC_SUP_AUTHENTICATING, + WLC_SUP_AUTHENTICATED, + WLC_SUP_KEYXCHANGE, + WLC_SUP_KEYED, + WLC_SUP_TIMEOUT, + WLC_SUP_LAST_BASIC_STATE, + + + + WLC_SUP_KEYXCHANGE_WAIT_M1 = WLC_SUP_AUTHENTICATED, + + WLC_SUP_KEYXCHANGE_PREP_M2 = WLC_SUP_KEYXCHANGE, + + WLC_SUP_KEYXCHANGE_WAIT_M3 = WLC_SUP_LAST_BASIC_STATE, + WLC_SUP_KEYXCHANGE_PREP_M4, + WLC_SUP_KEYXCHANGE_WAIT_G1, + WLC_SUP_KEYXCHANGE_PREP_G2 +} sup_auth_status_t; + + +#define CRYPTO_ALGO_OFF 0 +#define CRYPTO_ALGO_WEP1 1 +#define CRYPTO_ALGO_TKIP 2 +#define CRYPTO_ALGO_WEP128 3 +#define CRYPTO_ALGO_AES_CCM 4 +#define CRYPTO_ALGO_AES_OCB_MSDU 5 +#define CRYPTO_ALGO_AES_OCB_MPDU 6 +#define CRYPTO_ALGO_NALG 7 +#define CRYPTO_ALGO_PMK 12 + +#define WSEC_GEN_MIC_ERROR 0x0001 +#define WSEC_GEN_REPLAY 0x0002 +#define WSEC_GEN_ICV_ERROR 0x0004 + +#define WL_SOFT_KEY (1 << 0) +#define WL_PRIMARY_KEY (1 << 1) +#define WL_KF_RES_4 (1 << 4) +#define WL_KF_RES_5 (1 << 5) +#define WL_IBSS_PEER_GROUP_KEY (1 << 6) + +typedef struct wl_wsec_key { + uint32 index; + uint32 len; + uint8 data[DOT11_MAX_KEY_SIZE]; + uint32 pad_1[18]; + uint32 algo; + uint32 flags; + uint32 pad_2[2]; + int pad_3; + int iv_initialized; + int pad_4; + + struct { + uint32 hi; + uint16 lo; + } rxiv; + uint32 pad_5[2]; + struct ether_addr ea; +} wl_wsec_key_t; + +#define WSEC_MIN_PSK_LEN 8 +#define WSEC_MAX_PSK_LEN 64 + + +#define WSEC_PASSPHRASE (1<<0) + + +typedef struct { + ushort key_len; + ushort flags; + uint8 key[WSEC_MAX_PSK_LEN]; +} wsec_pmk_t; + + +#define WEP_ENABLED 0x0001 +#define TKIP_ENABLED 0x0002 +#define AES_ENABLED 0x0004 +#define WSEC_SWFLAG 0x0008 +#define SES_OW_ENABLED 0x0040 + + +#define WPA_AUTH_DISABLED 0x0000 +#define WPA_AUTH_NONE 0x0001 +#define WPA_AUTH_UNSPECIFIED 0x0002 +#define WPA_AUTH_PSK 0x0004 + +#define WPA2_AUTH_UNSPECIFIED 0x0040 +#define WPA2_AUTH_PSK 0x0080 +#define BRCM_AUTH_PSK 0x0100 +#define BRCM_AUTH_DPT 0x0200 +#define WPA2_AUTH_MFP 0x1000 +#define WPA2_AUTH_TPK 0x2000 +#define WPA2_AUTH_FT 0x4000 + + +#define MAXPMKID 16 + +typedef struct _pmkid { + struct ether_addr BSSID; + uint8 PMKID[WPA2_PMKID_LEN]; +} pmkid_t; + +typedef struct _pmkid_list { + uint32 npmkid; + pmkid_t pmkid[1]; +} pmkid_list_t; + +typedef struct _pmkid_cand { + struct ether_addr BSSID; + uint8 preauth; +} pmkid_cand_t; + +typedef struct _pmkid_cand_list { + uint32 npmkid_cand; + pmkid_cand_t pmkid_cand[1]; +} pmkid_cand_list_t; + +typedef struct wl_assoc_info { + uint32 req_len; + uint32 resp_len; + uint32 flags; + struct dot11_assoc_req req; + struct ether_addr reassoc_bssid; + struct dot11_assoc_resp resp; +} wl_assoc_info_t; + + +#define WLC_ASSOC_REQ_IS_REASSOC 0x01 + + +typedef struct { + uint16 ver; + uint16 len; + uint16 cap; + uint32 flags; + uint32 idle; + struct ether_addr ea; + wl_rateset_t rateset; + uint32 in; + uint32 listen_interval_inms; + uint32 tx_pkts; + uint32 tx_failures; + uint32 rx_ucast_pkts; + uint32 rx_mcast_pkts; + uint32 tx_rate; + uint32 rx_rate; + uint32 rx_decrypt_succeeds; + uint32 rx_decrypt_failures; +} sta_info_t; + +#define WL_OLD_STAINFO_SIZE OFFSETOF(sta_info_t, tx_pkts) + +#define WL_STA_VER 3 + + +#define WL_STA_BRCM 0x1 +#define WL_STA_WME 0x2 +#define WL_STA_ABCAP 0x4 +#define WL_STA_AUTHE 0x8 +#define WL_STA_ASSOC 0x10 +#define WL_STA_AUTHO 0x20 +#define WL_STA_WDS 0x40 +#define WL_STA_WDS_LINKUP 0x80 +#define WL_STA_PS 0x100 +#define WL_STA_APSD_BE 0x200 +#define WL_STA_APSD_BK 0x400 +#define WL_STA_APSD_VI 0x800 +#define WL_STA_APSD_VO 0x1000 +#define WL_STA_N_CAP 0x2000 +#define WL_STA_SCBSTATS 0x4000 + +#define WL_WDS_LINKUP WL_STA_WDS_LINKUP + + +#define WLC_TXFILTER_OVERRIDE_DISABLED 0 +#define WLC_TXFILTER_OVERRIDE_ENABLED 1 + + +typedef struct { + uint32 val; + struct ether_addr ea; +} scb_val_t; + + +typedef struct { + uint32 code; + scb_val_t ioctl_args; +} authops_t; + + +typedef struct channel_info { + int hw_channel; + int target_channel; + int scan_channel; +} channel_info_t; + + +struct maclist { + uint count; + struct ether_addr ea[1]; +}; + + +typedef struct get_pktcnt { + uint rx_good_pkt; + uint rx_bad_pkt; + uint tx_good_pkt; + uint tx_bad_pkt; + uint rx_ocast_good_pkt; +} get_pktcnt_t; + +#define WL_IOCTL_ACTION_GET 0x0 +#define WL_IOCTL_ACTION_SET 0x1 +#define WL_IOCTL_ACTION_OVL_IDX_MASK 0x1e +#define WL_IOCTL_ACTION_OVL_RSV 0x20 +#define WL_IOCTL_ACTION_OVL 0x40 +#define WL_IOCTL_ACTION_MASK 0x7e +#define WL_IOCTL_ACTION_OVL_SHIFT 1 + + +typedef struct wl_ioctl { + uint cmd; + void *buf; + uint len; + uint8 set; + uint used; + uint needed; +} wl_ioctl_t; + + +#define ioctl_subtype set +#define ioctl_pid used +#define ioctl_status needed + + +typedef struct wlc_rev_info { + uint vendorid; + uint deviceid; + uint radiorev; + uint chiprev; + uint corerev; + uint boardid; + uint boardvendor; + uint boardrev; + uint driverrev; + uint ucoderev; + uint bus; + uint chipnum; + uint phytype; + uint phyrev; + uint anarev; + uint chippkg; +} wlc_rev_info_t; + +#define WL_REV_INFO_LEGACY_LENGTH 48 + +#define WL_BRAND_MAX 10 +typedef struct wl_instance_info { + uint instance; + char brand[WL_BRAND_MAX]; +} wl_instance_info_t; + + +typedef struct wl_txfifo_sz { + uint16 magic; + uint16 fifo; + uint16 size; +} wl_txfifo_sz_t; + +#define WL_TXFIFO_SZ_MAGIC 0xa5a5 + + + +#define WLC_IOV_NAME_LEN 30 +typedef struct wlc_iov_trx_s { + uint8 module; + uint8 type; + char name[WLC_IOV_NAME_LEN]; +} wlc_iov_trx_t; + + +#define WLC_IOCTL_MAGIC 0x14e46c77 + + +#define WLC_IOCTL_VERSION 1 + +#define WLC_IOCTL_MAXLEN 8192 +#define WLC_IOCTL_SMLEN 256 +#define WLC_IOCTL_MEDLEN 1536 +#ifdef WLC_HIGH_ONLY +#define WLC_SAMPLECOLLECT_MAXLEN 1024 +#define WLC_SAMPLECOLLECT_MAXLEN_LCN40 1024 +#else +#if defined(LCNCONF) || defined(LCN40CONF) +#define WLC_SAMPLECOLLECT_MAXLEN 8192 +#else +#define WLC_SAMPLECOLLECT_MAXLEN 10240 +#endif +#define WLC_SAMPLECOLLECT_MAXLEN_LCN40 8192 +#endif + + +#define WLC_GET_MAGIC 0 +#define WLC_GET_VERSION 1 +#define WLC_UP 2 +#define WLC_DOWN 3 +#define WLC_GET_LOOP 4 +#define WLC_SET_LOOP 5 +#define WLC_DUMP 6 +#define WLC_GET_MSGLEVEL 7 +#define WLC_SET_MSGLEVEL 8 +#define WLC_GET_PROMISC 9 +#define WLC_SET_PROMISC 10 +#define WLC_OVERLAY_IOCTL 11 +#define WLC_GET_RATE 12 + +#define WLC_GET_INSTANCE 14 + + + + +#define WLC_GET_INFRA 19 +#define WLC_SET_INFRA 20 +#define WLC_GET_AUTH 21 +#define WLC_SET_AUTH 22 +#define WLC_GET_BSSID 23 +#define WLC_SET_BSSID 24 +#define WLC_GET_SSID 25 +#define WLC_SET_SSID 26 +#define WLC_RESTART 27 +#define WLC_TERMINATED 28 + +#define WLC_GET_CHANNEL 29 +#define WLC_SET_CHANNEL 30 +#define WLC_GET_SRL 31 +#define WLC_SET_SRL 32 +#define WLC_GET_LRL 33 +#define WLC_SET_LRL 34 +#define WLC_GET_PLCPHDR 35 +#define WLC_SET_PLCPHDR 36 +#define WLC_GET_RADIO 37 +#define WLC_SET_RADIO 38 +#define WLC_GET_PHYTYPE 39 +#define WLC_DUMP_RATE 40 +#define WLC_SET_RATE_PARAMS 41 +#define WLC_GET_FIXRATE 42 +#define WLC_SET_FIXRATE 43 + + +#define WLC_GET_KEY 44 +#define WLC_SET_KEY 45 +#define WLC_GET_REGULATORY 46 +#define WLC_SET_REGULATORY 47 +#define WLC_GET_PASSIVE_SCAN 48 +#define WLC_SET_PASSIVE_SCAN 49 +#define WLC_SCAN 50 +#define WLC_SCAN_RESULTS 51 +#define WLC_DISASSOC 52 +#define WLC_REASSOC 53 +#define WLC_GET_ROAM_TRIGGER 54 +#define WLC_SET_ROAM_TRIGGER 55 +#define WLC_GET_ROAM_DELTA 56 +#define WLC_SET_ROAM_DELTA 57 +#define WLC_GET_ROAM_SCAN_PERIOD 58 +#define WLC_SET_ROAM_SCAN_PERIOD 59 +#define WLC_EVM 60 +#define WLC_GET_TXANT 61 +#define WLC_SET_TXANT 62 +#define WLC_GET_ANTDIV 63 +#define WLC_SET_ANTDIV 64 + + +#define WLC_GET_CLOSED 67 +#define WLC_SET_CLOSED 68 +#define WLC_GET_MACLIST 69 +#define WLC_SET_MACLIST 70 +#define WLC_GET_RATESET 71 +#define WLC_SET_RATESET 72 + +#define WLC_LONGTRAIN 74 +#define WLC_GET_BCNPRD 75 +#define WLC_SET_BCNPRD 76 +#define WLC_GET_DTIMPRD 77 +#define WLC_SET_DTIMPRD 78 +#define WLC_GET_SROM 79 +#define WLC_SET_SROM 80 +#define WLC_GET_WEP_RESTRICT 81 +#define WLC_SET_WEP_RESTRICT 82 +#define WLC_GET_COUNTRY 83 +#define WLC_SET_COUNTRY 84 +#define WLC_GET_PM 85 +#define WLC_SET_PM 86 +#define WLC_GET_WAKE 87 +#define WLC_SET_WAKE 88 + +#define WLC_GET_FORCELINK 90 +#define WLC_SET_FORCELINK 91 +#define WLC_FREQ_ACCURACY 92 +#define WLC_CARRIER_SUPPRESS 93 +#define WLC_GET_PHYREG 94 +#define WLC_SET_PHYREG 95 +#define WLC_GET_RADIOREG 96 +#define WLC_SET_RADIOREG 97 +#define WLC_GET_REVINFO 98 +#define WLC_GET_UCANTDIV 99 +#define WLC_SET_UCANTDIV 100 +#define WLC_R_REG 101 +#define WLC_W_REG 102 + + +#define WLC_GET_MACMODE 105 +#define WLC_SET_MACMODE 106 +#define WLC_GET_MONITOR 107 +#define WLC_SET_MONITOR 108 +#define WLC_GET_GMODE 109 +#define WLC_SET_GMODE 110 +#define WLC_GET_LEGACY_ERP 111 +#define WLC_SET_LEGACY_ERP 112 +#define WLC_GET_RX_ANT 113 +#define WLC_GET_CURR_RATESET 114 +#define WLC_GET_SCANSUPPRESS 115 +#define WLC_SET_SCANSUPPRESS 116 +#define WLC_GET_AP 117 +#define WLC_SET_AP 118 +#define WLC_GET_EAP_RESTRICT 119 +#define WLC_SET_EAP_RESTRICT 120 +#define WLC_SCB_AUTHORIZE 121 +#define WLC_SCB_DEAUTHORIZE 122 +#define WLC_GET_WDSLIST 123 +#define WLC_SET_WDSLIST 124 +#define WLC_GET_ATIM 125 +#define WLC_SET_ATIM 126 +#define WLC_GET_RSSI 127 +#define WLC_GET_PHYANTDIV 128 +#define WLC_SET_PHYANTDIV 129 +#define WLC_AP_RX_ONLY 130 +#define WLC_GET_TX_PATH_PWR 131 +#define WLC_SET_TX_PATH_PWR 132 +#define WLC_GET_WSEC 133 +#define WLC_SET_WSEC 134 +#define WLC_GET_PHY_NOISE 135 +#define WLC_GET_BSS_INFO 136 +#define WLC_GET_PKTCNTS 137 +#define WLC_GET_LAZYWDS 138 +#define WLC_SET_LAZYWDS 139 +#define WLC_GET_BANDLIST 140 +#define WLC_GET_BAND 141 +#define WLC_SET_BAND 142 +#define WLC_SCB_DEAUTHENTICATE 143 +#define WLC_GET_SHORTSLOT 144 +#define WLC_GET_SHORTSLOT_OVERRIDE 145 +#define WLC_SET_SHORTSLOT_OVERRIDE 146 +#define WLC_GET_SHORTSLOT_RESTRICT 147 +#define WLC_SET_SHORTSLOT_RESTRICT 148 +#define WLC_GET_GMODE_PROTECTION 149 +#define WLC_GET_GMODE_PROTECTION_OVERRIDE 150 +#define WLC_SET_GMODE_PROTECTION_OVERRIDE 151 +#define WLC_UPGRADE 152 + + +#define WLC_GET_IGNORE_BCNS 155 +#define WLC_SET_IGNORE_BCNS 156 +#define WLC_GET_SCB_TIMEOUT 157 +#define WLC_SET_SCB_TIMEOUT 158 +#define WLC_GET_ASSOCLIST 159 +#define WLC_GET_CLK 160 +#define WLC_SET_CLK 161 +#define WLC_GET_UP 162 +#define WLC_OUT 163 +#define WLC_GET_WPA_AUTH 164 +#define WLC_SET_WPA_AUTH 165 +#define WLC_GET_UCFLAGS 166 +#define WLC_SET_UCFLAGS 167 +#define WLC_GET_PWRIDX 168 +#define WLC_SET_PWRIDX 169 +#define WLC_GET_TSSI 170 +#define WLC_GET_SUP_RATESET_OVERRIDE 171 +#define WLC_SET_SUP_RATESET_OVERRIDE 172 + + + + + +#define WLC_GET_PROTECTION_CONTROL 178 +#define WLC_SET_PROTECTION_CONTROL 179 +#define WLC_GET_PHYLIST 180 +#define WLC_ENCRYPT_STRENGTH 181 +#define WLC_DECRYPT_STATUS 182 +#define WLC_GET_KEY_SEQ 183 +#define WLC_GET_SCAN_CHANNEL_TIME 184 +#define WLC_SET_SCAN_CHANNEL_TIME 185 +#define WLC_GET_SCAN_UNASSOC_TIME 186 +#define WLC_SET_SCAN_UNASSOC_TIME 187 +#define WLC_GET_SCAN_HOME_TIME 188 +#define WLC_SET_SCAN_HOME_TIME 189 +#define WLC_GET_SCAN_NPROBES 190 +#define WLC_SET_SCAN_NPROBES 191 +#define WLC_GET_PRB_RESP_TIMEOUT 192 +#define WLC_SET_PRB_RESP_TIMEOUT 193 +#define WLC_GET_ATTEN 194 +#define WLC_SET_ATTEN 195 +#define WLC_GET_SHMEM 196 +#define WLC_SET_SHMEM 197 + + +#define WLC_SET_WSEC_TEST 200 +#define WLC_SCB_DEAUTHENTICATE_FOR_REASON 201 +#define WLC_TKIP_COUNTERMEASURES 202 +#define WLC_GET_PIOMODE 203 +#define WLC_SET_PIOMODE 204 +#define WLC_SET_ASSOC_PREFER 205 +#define WLC_GET_ASSOC_PREFER 206 +#define WLC_SET_ROAM_PREFER 207 +#define WLC_GET_ROAM_PREFER 208 +#define WLC_SET_LED 209 +#define WLC_GET_LED 210 +#define WLC_GET_INTERFERENCE_MODE 211 +#define WLC_SET_INTERFERENCE_MODE 212 +#define WLC_GET_CHANNEL_QA 213 +#define WLC_START_CHANNEL_QA 214 +#define WLC_GET_CHANNEL_SEL 215 +#define WLC_START_CHANNEL_SEL 216 +#define WLC_GET_VALID_CHANNELS 217 +#define WLC_GET_FAKEFRAG 218 +#define WLC_SET_FAKEFRAG 219 +#define WLC_GET_PWROUT_PERCENTAGE 220 +#define WLC_SET_PWROUT_PERCENTAGE 221 +#define WLC_SET_BAD_FRAME_PREEMPT 222 +#define WLC_GET_BAD_FRAME_PREEMPT 223 +#define WLC_SET_LEAP_LIST 224 +#define WLC_GET_LEAP_LIST 225 +#define WLC_GET_CWMIN 226 +#define WLC_SET_CWMIN 227 +#define WLC_GET_CWMAX 228 +#define WLC_SET_CWMAX 229 +#define WLC_GET_WET 230 +#define WLC_SET_WET 231 +#define WLC_GET_PUB 232 + + +#define WLC_GET_KEY_PRIMARY 235 +#define WLC_SET_KEY_PRIMARY 236 + +#define WLC_GET_ACI_ARGS 238 +#define WLC_SET_ACI_ARGS 239 +#define WLC_UNSET_CALLBACK 240 +#define WLC_SET_CALLBACK 241 +#define WLC_GET_RADAR 242 +#define WLC_SET_RADAR 243 +#define WLC_SET_SPECT_MANAGMENT 244 +#define WLC_GET_SPECT_MANAGMENT 245 +#define WLC_WDS_GET_REMOTE_HWADDR 246 +#define WLC_WDS_GET_WPA_SUP 247 +#define WLC_SET_CS_SCAN_TIMER 248 +#define WLC_GET_CS_SCAN_TIMER 249 +#define WLC_MEASURE_REQUEST 250 +#define WLC_INIT 251 +#define WLC_SEND_QUIET 252 +#define WLC_KEEPALIVE 253 +#define WLC_SEND_PWR_CONSTRAINT 254 +#define WLC_UPGRADE_STATUS 255 +#define WLC_CURRENT_PWR 256 +#define WLC_GET_SCAN_PASSIVE_TIME 257 +#define WLC_SET_SCAN_PASSIVE_TIME 258 +#define WLC_LEGACY_LINK_BEHAVIOR 259 +#define WLC_GET_CHANNELS_IN_COUNTRY 260 +#define WLC_GET_COUNTRY_LIST 261 +#define WLC_GET_VAR 262 +#define WLC_SET_VAR 263 +#define WLC_NVRAM_GET 264 +#define WLC_NVRAM_SET 265 +#define WLC_NVRAM_DUMP 266 +#define WLC_REBOOT 267 +#define WLC_SET_WSEC_PMK 268 +#define WLC_GET_AUTH_MODE 269 +#define WLC_SET_AUTH_MODE 270 +#define WLC_GET_WAKEENTRY 271 +#define WLC_SET_WAKEENTRY 272 +#define WLC_NDCONFIG_ITEM 273 +#define WLC_NVOTPW 274 +#define WLC_OTPW 275 +#define WLC_IOV_BLOCK_GET 276 +#define WLC_IOV_MODULES_GET 277 +#define WLC_SOFT_RESET 278 +#define WLC_GET_ALLOW_MODE 279 +#define WLC_SET_ALLOW_MODE 280 +#define WLC_GET_DESIRED_BSSID 281 +#define WLC_SET_DESIRED_BSSID 282 +#define WLC_DISASSOC_MYAP 283 +#define WLC_GET_NBANDS 284 +#define WLC_GET_BANDSTATES 285 +#define WLC_GET_WLC_BSS_INFO 286 +#define WLC_GET_ASSOC_INFO 287 +#define WLC_GET_OID_PHY 288 +#define WLC_SET_OID_PHY 289 +#define WLC_SET_ASSOC_TIME 290 +#define WLC_GET_DESIRED_SSID 291 +#define WLC_GET_CHANSPEC 292 +#define WLC_GET_ASSOC_STATE 293 +#define WLC_SET_PHY_STATE 294 +#define WLC_GET_SCAN_PENDING 295 +#define WLC_GET_SCANREQ_PENDING 296 +#define WLC_GET_PREV_ROAM_REASON 297 +#define WLC_SET_PREV_ROAM_REASON 298 +#define WLC_GET_BANDSTATES_PI 299 +#define WLC_GET_PHY_STATE 300 +#define WLC_GET_BSS_WPA_RSN 301 +#define WLC_GET_BSS_WPA2_RSN 302 +#define WLC_GET_BSS_BCN_TS 303 +#define WLC_GET_INT_DISASSOC 304 +#define WLC_SET_NUM_PEERS 305 +#define WLC_GET_NUM_BSS 306 +#define WLC_NPHY_SAMPLE_COLLECT 307 +#define WLC_UM_PRIV 308 +#define WLC_GET_CMD 309 + +#define WLC_SET_INTERFERENCE_OVERRIDE_MODE 311 +#define WLC_GET_INTERFERENCE_OVERRIDE_MODE 312 +#define WLC_GET_WAI_RESTRICT 313 +#define WLC_SET_WAI_RESTRICT 314 +#define WLC_SET_WAI_REKEY 315 +#define WLC_SET_PEAKRATE 316 +#define WLC_GET_PEAKRATE 317 +#define WLC_LAST 318 + +#ifndef EPICTRL_COOKIE +#define EPICTRL_COOKIE 0xABADCEDE +#endif + + +#define CMN_IOCTL_OFF 0x180 + + + + +#define WL_OID_BASE 0xFFE41420 + + +#define OID_WL_GETINSTANCE (WL_OID_BASE + WLC_GET_INSTANCE) +#define OID_WL_GET_FORCELINK (WL_OID_BASE + WLC_GET_FORCELINK) +#define OID_WL_SET_FORCELINK (WL_OID_BASE + WLC_SET_FORCELINK) +#define OID_WL_ENCRYPT_STRENGTH (WL_OID_BASE + WLC_ENCRYPT_STRENGTH) +#define OID_WL_DECRYPT_STATUS (WL_OID_BASE + WLC_DECRYPT_STATUS) +#define OID_LEGACY_LINK_BEHAVIOR (WL_OID_BASE + WLC_LEGACY_LINK_BEHAVIOR) +#define OID_WL_NDCONFIG_ITEM (WL_OID_BASE + WLC_NDCONFIG_ITEM) + + +#define OID_STA_CHANSPEC (WL_OID_BASE + WLC_GET_CHANSPEC) +#define OID_STA_NBANDS (WL_OID_BASE + WLC_GET_NBANDS) +#define OID_STA_GET_PHY (WL_OID_BASE + WLC_GET_OID_PHY) +#define OID_STA_SET_PHY (WL_OID_BASE + WLC_SET_OID_PHY) +#define OID_STA_ASSOC_TIME (WL_OID_BASE + WLC_SET_ASSOC_TIME) +#define OID_STA_DESIRED_SSID (WL_OID_BASE + WLC_GET_DESIRED_SSID) +#define OID_STA_SET_PHY_STATE (WL_OID_BASE + WLC_SET_PHY_STATE) +#define OID_STA_SCAN_PENDING (WL_OID_BASE + WLC_GET_SCAN_PENDING) +#define OID_STA_SCANREQ_PENDING (WL_OID_BASE + WLC_GET_SCANREQ_PENDING) +#define OID_STA_GET_ROAM_REASON (WL_OID_BASE + WLC_GET_PREV_ROAM_REASON) +#define OID_STA_SET_ROAM_REASON (WL_OID_BASE + WLC_SET_PREV_ROAM_REASON) +#define OID_STA_GET_PHY_STATE (WL_OID_BASE + WLC_GET_PHY_STATE) +#define OID_STA_INT_DISASSOC (WL_OID_BASE + WLC_GET_INT_DISASSOC) +#define OID_STA_SET_NUM_PEERS (WL_OID_BASE + WLC_SET_NUM_PEERS) +#define OID_STA_GET_NUM_BSS (WL_OID_BASE + WLC_GET_NUM_BSS) + +#define WL_DECRYPT_STATUS_SUCCESS 1 +#define WL_DECRYPT_STATUS_FAILURE 2 +#define WL_DECRYPT_STATUS_UNKNOWN 3 + + +#define WLC_UPGRADE_SUCCESS 0 +#define WLC_UPGRADE_PENDING 1 + +#ifdef CONFIG_USBRNDIS_RETAIL + +typedef struct { + char *name; + void *param; +} ndconfig_item_t; +#endif + + + +#define WL_AUTH_OPEN_SYSTEM 0 +#define WL_AUTH_SHARED_KEY 1 +#define WL_AUTH_OPEN_SHARED 2 + + +#define WL_RADIO_SW_DISABLE (1<<0) +#define WL_RADIO_HW_DISABLE (1<<1) +#define WL_RADIO_MPC_DISABLE (1<<2) +#define WL_RADIO_COUNTRY_DISABLE (1<<3) + +#define WL_SPURAVOID_OFF 0 +#define WL_SPURAVOID_ON1 1 +#define WL_SPURAVOID_ON2 2 + + +#define WL_TXPWR_OVERRIDE (1U<<31) +#define WL_TXPWR_NEG (1U<<30) + +#define WL_PHY_PAVARS_LEN 6 + +#define WL_PHY_PAVARS2_NUM 3 +#define WL_PHY_PAVAR_VER 1 +typedef struct wl_pavars2 { + uint16 ver; + uint16 len; + uint16 inuse; + uint16 phy_type; + uint16 bandrange; + uint16 chain; + uint16 inpa[WL_PHY_PAVARS2_NUM]; +} wl_pavars2_t; + +typedef struct wl_po { + uint16 phy_type; + uint16 band; + uint16 cckpo; + uint32 ofdmpo; + uint16 mcspo[8]; +} wl_po_t; + + +#define WLC_TXPWR_MAX (127) + + +#define WL_DIAG_INTERRUPT 1 +#define WL_DIAG_LOOPBACK 2 +#define WL_DIAG_MEMORY 3 +#define WL_DIAG_LED 4 +#define WL_DIAG_REG 5 +#define WL_DIAG_SROM 6 +#define WL_DIAG_DMA 7 + +#define WL_DIAGERR_SUCCESS 0 +#define WL_DIAGERR_FAIL_TO_RUN 1 +#define WL_DIAGERR_NOT_SUPPORTED 2 +#define WL_DIAGERR_INTERRUPT_FAIL 3 +#define WL_DIAGERR_LOOPBACK_FAIL 4 +#define WL_DIAGERR_SROM_FAIL 5 +#define WL_DIAGERR_SROM_BADCRC 6 +#define WL_DIAGERR_REG_FAIL 7 +#define WL_DIAGERR_MEMORY_FAIL 8 +#define WL_DIAGERR_NOMEM 9 +#define WL_DIAGERR_DMA_FAIL 10 + +#define WL_DIAGERR_MEMORY_TIMEOUT 11 +#define WL_DIAGERR_MEMORY_BADPATTERN 12 + + +#define WLC_BAND_AUTO 0 +#define WLC_BAND_5G 1 +#define WLC_BAND_2G 2 +#define WLC_BAND_ALL 3 + + +#define WL_CHAN_FREQ_RANGE_2G 0 +#define WL_CHAN_FREQ_RANGE_5GL 1 +#define WL_CHAN_FREQ_RANGE_5GM 2 +#define WL_CHAN_FREQ_RANGE_5GH 3 + +#define WL_CHAN_FREQ_RANGE_5GLL_5BAND 4 +#define WL_CHAN_FREQ_RANGE_5GLH_5BAND 5 +#define WL_CHAN_FREQ_RANGE_5GML_5BAND 6 +#define WL_CHAN_FREQ_RANGE_5GMH_5BAND 7 +#define WL_CHAN_FREQ_RANGE_5GH_5BAND 8 + +#define WL_CHAN_FREQ_RANGE_5G_BAND0 1 +#define WL_CHAN_FREQ_RANGE_5G_BAND1 2 +#define WL_CHAN_FREQ_RANGE_5G_BAND2 3 +#define WL_CHAN_FREQ_RANGE_5G_BAND3 4 + + +#define WLC_PHY_TYPE_A 0 +#define WLC_PHY_TYPE_B 1 +#define WLC_PHY_TYPE_G 2 +#define WLC_PHY_TYPE_N 4 +#define WLC_PHY_TYPE_LP 5 +#define WLC_PHY_TYPE_SSN 6 +#define WLC_PHY_TYPE_HT 7 +#define WLC_PHY_TYPE_LCN 8 +#define WLC_PHY_TYPE_NULL 0xf + + +#define WLC_MACMODE_DISABLED 0 +#define WLC_MACMODE_DENY 1 +#define WLC_MACMODE_ALLOW 2 + + +#define GMODE_LEGACY_B 0 +#define GMODE_AUTO 1 +#define GMODE_ONLY 2 +#define GMODE_B_DEFERRED 3 +#define GMODE_PERFORMANCE 4 +#define GMODE_LRS 5 +#define GMODE_MAX 6 + + +#define WLC_PLCP_AUTO -1 +#define WLC_PLCP_SHORT 0 +#define WLC_PLCP_LONG 1 + + +#define WLC_PROTECTION_AUTO -1 +#define WLC_PROTECTION_OFF 0 +#define WLC_PROTECTION_ON 1 +#define WLC_PROTECTION_MMHDR_ONLY 2 +#define WLC_PROTECTION_CTS_ONLY 3 + + +#define WLC_PROTECTION_CTL_OFF 0 +#define WLC_PROTECTION_CTL_LOCAL 1 +#define WLC_PROTECTION_CTL_OVERLAP 2 + + +#define WLC_N_PROTECTION_OFF 0 +#define WLC_N_PROTECTION_OPTIONAL 1 +#define WLC_N_PROTECTION_20IN40 2 +#define WLC_N_PROTECTION_MIXEDMODE 3 + + +#define WLC_N_PREAMBLE_MIXEDMODE 0 +#define WLC_N_PREAMBLE_GF 1 +#define WLC_N_PREAMBLE_GF_BRCM 2 + + +#define WLC_N_BW_20ALL 0 +#define WLC_N_BW_40ALL 1 +#define WLC_N_BW_20IN2G_40IN5G 2 + + +#define WLC_N_TXRX_CHAIN0 0 +#define WLC_N_TXRX_CHAIN1 1 + + +#define WLC_N_SGI_20 0x01 +#define WLC_N_SGI_40 0x02 + + +#define PM_OFF 0 +#define PM_MAX 1 +#define PM_FAST 2 + +#define LISTEN_INTERVAL 10 + +#define INTERFERE_OVRRIDE_OFF -1 +#define INTERFERE_NONE 0 +#define NON_WLAN 1 +#define WLAN_MANUAL 2 +#define WLAN_AUTO 3 +#define WLAN_AUTO_W_NOISE 4 +#define AUTO_ACTIVE (1 << 7) + +typedef struct wl_aci_args { + int enter_aci_thresh; + int exit_aci_thresh; + int usec_spin; + int glitch_delay; + uint16 nphy_adcpwr_enter_thresh; + uint16 nphy_adcpwr_exit_thresh; + uint16 nphy_repeat_ctr; + uint16 nphy_num_samples; + uint16 nphy_undetect_window_sz; + uint16 nphy_b_energy_lo_aci; + uint16 nphy_b_energy_md_aci; + uint16 nphy_b_energy_hi_aci; + uint16 nphy_noise_noassoc_glitch_th_up; + uint16 nphy_noise_noassoc_glitch_th_dn; + uint16 nphy_noise_assoc_glitch_th_up; + uint16 nphy_noise_assoc_glitch_th_dn; + uint16 nphy_noise_assoc_aci_glitch_th_up; + uint16 nphy_noise_assoc_aci_glitch_th_dn; + uint16 nphy_noise_assoc_enter_th; + uint16 nphy_noise_noassoc_enter_th; + uint16 nphy_noise_assoc_rx_glitch_badplcp_enter_th; + uint16 nphy_noise_noassoc_crsidx_incr; + uint16 nphy_noise_assoc_crsidx_incr; + uint16 nphy_noise_crsidx_decr; +} wl_aci_args_t; + +#define TRIGGER_NOW 0 +#define TRIGGER_CRS 0x01 +#define TRIGGER_CRSDEASSERT 0x02 +#define TRIGGER_GOODFCS 0x04 +#define TRIGGER_BADFCS 0x08 +#define TRIGGER_BADPLCP 0x10 +#define TRIGGER_CRSGLITCH 0x20 +#define WL_ACI_ARGS_LEGACY_LENGTH 16 +#define WL_SAMPLECOLLECT_T_VERSION 2 +typedef struct wl_samplecollect_args { + + uint8 coll_us; + int cores; + + uint16 version; + uint16 length; + int8 trigger; + uint16 timeout; + uint16 mode; + uint32 pre_dur; + uint32 post_dur; + uint8 gpio_sel; + bool downsamp; + bool be_deaf; + bool agc; + bool filter; + + uint8 trigger_state; + uint8 module_sel1; + uint8 module_sel2; + uint16 nsamps; +} wl_samplecollect_args_t; + +#define WL_SAMPLEDATA_HEADER_TYPE 1 +#define WL_SAMPLEDATA_HEADER_SIZE 80 +#define WL_SAMPLEDATA_TYPE 2 +#define WL_SAMPLEDATA_SEQ 0xff +#define WL_SAMPLEDATA_MORE_DATA 0x100 +#define WL_SAMPLEDATA_T_VERSION 1 + +#define WL_SAMPLEDATA_T_VERSION_SPEC_AN 2 + +typedef struct wl_sampledata { + uint16 version; + uint16 size; + uint16 tag; + uint16 length; + uint32 flag; +} wl_sampledata_t; + + +#define WL_CHAN_VALID_HW (1 << 0) +#define WL_CHAN_VALID_SW (1 << 1) +#define WL_CHAN_BAND_5G (1 << 2) +#define WL_CHAN_RADAR (1 << 3) +#define WL_CHAN_INACTIVE (1 << 4) +#define WL_CHAN_PASSIVE (1 << 5) +#define WL_CHAN_RESTRICTED (1 << 6) + + +#define WL_ERROR_VAL 0x00000001 +#define WL_TRACE_VAL 0x00000002 +#define WL_PRHDRS_VAL 0x00000004 +#define WL_PRPKT_VAL 0x00000008 +#define WL_INFORM_VAL 0x00000010 +#define WL_TMP_VAL 0x00000020 +#define WL_OID_VAL 0x00000040 +#define WL_RATE_VAL 0x00000080 +#define WL_ASSOC_VAL 0x00000100 +#define WL_PRUSR_VAL 0x00000200 +#define WL_PS_VAL 0x00000400 +#define WL_TXPWR_VAL 0x00000800 +#define WL_PORT_VAL 0x00001000 +#define WL_DUAL_VAL 0x00002000 +#define WL_WSEC_VAL 0x00004000 +#define WL_WSEC_DUMP_VAL 0x00008000 +#define WL_LOG_VAL 0x00010000 +#define WL_NRSSI_VAL 0x00020000 +#define WL_LOFT_VAL 0x00040000 +#define WL_REGULATORY_VAL 0x00080000 +#define WL_PHYCAL_VAL 0x00100000 +#define WL_RADAR_VAL 0x00200000 +#define WL_MPC_VAL 0x00400000 +#define WL_APSTA_VAL 0x00800000 +#define WL_DFS_VAL 0x01000000 +#define WL_BA_VAL 0x02000000 +#define WL_ACI_VAL 0x04000000 +#define WL_MBSS_VAL 0x04000000 +#define WL_CAC_VAL 0x08000000 +#define WL_AMSDU_VAL 0x10000000 +#define WL_AMPDU_VAL 0x20000000 +#define WL_FFPLD_VAL 0x40000000 + + +#define WL_DPT_VAL 0x00000001 +#define WL_SCAN_VAL 0x00000002 +#define WL_WOWL_VAL 0x00000004 +#define WL_COEX_VAL 0x00000008 +#define WL_RTDC_VAL 0x00000010 +#define WL_PROTO_VAL 0x00000020 +#define WL_BTA_VAL 0x00000040 +#define WL_CHANINT_VAL 0x00000080 +#define WL_THERMAL_VAL 0x00000100 +#define WL_P2P_VAL 0x00000200 +#define WL_TXRX_VAL 0x00000400 +#define WL_MCHAN_VAL 0x00000800 +#define WL_TDLS_VAL 0x00001000 + + +#define WL_LED_NUMGPIO 16 + + +#define WL_LED_OFF 0 +#define WL_LED_ON 1 +#define WL_LED_ACTIVITY 2 +#define WL_LED_RADIO 3 +#define WL_LED_ARADIO 4 +#define WL_LED_BRADIO 5 +#define WL_LED_BGMODE 6 +#define WL_LED_WI1 7 +#define WL_LED_WI2 8 +#define WL_LED_WI3 9 +#define WL_LED_ASSOC 10 +#define WL_LED_INACTIVE 11 +#define WL_LED_ASSOCACT 12 +#define WL_LED_WI4 13 +#define WL_LED_WI5 14 +#define WL_LED_BLINKSLOW 15 +#define WL_LED_BLINKMED 16 +#define WL_LED_BLINKFAST 17 +#define WL_LED_BLINKCUSTOM 18 +#define WL_LED_BLINKPERIODIC 19 +#define WL_LED_ASSOC_WITH_SEC 20 + +#define WL_LED_START_OFF 21 +#define WL_LED_NUMBEHAVIOR 22 + + +#define WL_LED_BEH_MASK 0x7f +#define WL_LED_AL_MASK 0x80 + + +#define WL_NUMCHANNELS 64 +#define WL_NUMCHANSPECS 100 + + +#define WL_WDS_WPA_ROLE_AUTH 0 +#define WL_WDS_WPA_ROLE_SUP 1 +#define WL_WDS_WPA_ROLE_AUTO 255 + + +#define WL_EVENTING_MASK_LEN 16 + + + + +#define WL_JOIN_PREF_RSSI 1 +#define WL_JOIN_PREF_WPA 2 +#define WL_JOIN_PREF_BAND 3 +#define WL_JOIN_PREF_RSSI_DELTA 4 +#define WL_JOIN_PREF_TRANS_PREF 5 + + +#define WLJP_BAND_ASSOC_PREF 255 + + +#define WL_WPA_ACP_MCS_ANY "\x00\x00\x00\x00" + +struct tsinfo_arg { + uint8 octets[3]; +}; + +#define NFIFO 6 + +#define WL_CNT_T_VERSION 6 + +typedef struct { + uint16 version; + uint16 length; + + + uint32 txframe; + uint32 txbyte; + uint32 txretrans; + uint32 txerror; + uint32 txctl; + uint32 txprshort; + uint32 txserr; + uint32 txnobuf; + uint32 txnoassoc; + uint32 txrunt; + uint32 txchit; + uint32 txcmiss; + + + uint32 txuflo; + uint32 txphyerr; + uint32 txphycrs; + + + uint32 rxframe; + uint32 rxbyte; + uint32 rxerror; + uint32 rxctl; + uint32 rxnobuf; + uint32 rxnondata; + uint32 rxbadds; + uint32 rxbadcm; + uint32 rxfragerr; + uint32 rxrunt; + uint32 rxgiant; + uint32 rxnoscb; + uint32 rxbadproto; + uint32 rxbadsrcmac; + uint32 rxbadda; + uint32 rxfilter; + + + uint32 rxoflo; + uint32 rxuflo[NFIFO]; + + uint32 d11cnt_txrts_off; + uint32 d11cnt_rxcrc_off; + uint32 d11cnt_txnocts_off; + + + uint32 dmade; + uint32 dmada; + uint32 dmape; + uint32 reset; + uint32 tbtt; + uint32 txdmawar; + uint32 pkt_callback_reg_fail; + + + uint32 txallfrm; + uint32 txrtsfrm; + uint32 txctsfrm; + uint32 txackfrm; + uint32 txdnlfrm; + uint32 txbcnfrm; + uint32 txfunfl[8]; + uint32 txtplunfl; + uint32 txphyerror; + uint32 rxfrmtoolong; + uint32 rxfrmtooshrt; + uint32 rxinvmachdr; + uint32 rxbadfcs; + uint32 rxbadplcp; + uint32 rxcrsglitch; + uint32 rxstrt; + uint32 rxdfrmucastmbss; + uint32 rxmfrmucastmbss; + uint32 rxcfrmucast; + uint32 rxrtsucast; + uint32 rxctsucast; + uint32 rxackucast; + uint32 rxdfrmocast; + uint32 rxmfrmocast; + uint32 rxcfrmocast; + uint32 rxrtsocast; + uint32 rxctsocast; + uint32 rxdfrmmcast; + uint32 rxmfrmmcast; + uint32 rxcfrmmcast; + uint32 rxbeaconmbss; + uint32 rxdfrmucastobss; + uint32 rxbeaconobss; + uint32 rxrsptmout; + uint32 bcntxcancl; + uint32 rxf0ovfl; + uint32 rxf1ovfl; + uint32 rxf2ovfl; + uint32 txsfovfl; + uint32 pmqovfl; + uint32 rxcgprqfrm; + uint32 rxcgprsqovfl; + uint32 txcgprsfail; + uint32 txcgprssuc; + uint32 prs_timeout; + uint32 rxnack; + uint32 frmscons; + uint32 txnack; + uint32 txglitch_nack; + uint32 txburst; + + + uint32 txfrag; + uint32 txmulti; + uint32 txfail; + uint32 txretry; + uint32 txretrie; + uint32 rxdup; + uint32 txrts; + uint32 txnocts; + uint32 txnoack; + uint32 rxfrag; + uint32 rxmulti; + uint32 rxcrc; + uint32 txfrmsnt; + uint32 rxundec; + + + uint32 tkipmicfaill; + uint32 tkipcntrmsr; + uint32 tkipreplay; + uint32 ccmpfmterr; + uint32 ccmpreplay; + uint32 ccmpundec; + uint32 fourwayfail; + uint32 wepundec; + uint32 wepicverr; + uint32 decsuccess; + uint32 tkipicverr; + uint32 wepexcluded; + + uint32 rxundec_mcst; + + + uint32 tkipmicfaill_mcst; + uint32 tkipcntrmsr_mcst; + uint32 tkipreplay_mcst; + uint32 ccmpfmterr_mcst; + uint32 ccmpreplay_mcst; + uint32 ccmpundec_mcst; + uint32 fourwayfail_mcst; + uint32 wepundec_mcst; + uint32 wepicverr_mcst; + uint32 decsuccess_mcst; + uint32 tkipicverr_mcst; + uint32 wepexcluded_mcst; + + uint32 txchanrej; + uint32 txexptime; + uint32 psmwds; + uint32 phywatchdog; + + + uint32 prq_entries_handled; + uint32 prq_undirected_entries; + uint32 prq_bad_entries; + uint32 atim_suppress_count; + uint32 bcn_template_not_ready; + uint32 bcn_template_not_ready_done; + uint32 late_tbtt_dpc; + + + uint32 rx1mbps; + uint32 rx2mbps; + uint32 rx5mbps5; + uint32 rx6mbps; + uint32 rx9mbps; + uint32 rx11mbps; + uint32 rx12mbps; + uint32 rx18mbps; + uint32 rx24mbps; + uint32 rx36mbps; + uint32 rx48mbps; + uint32 rx54mbps; + uint32 rx108mbps; + uint32 rx162mbps; + uint32 rx216mbps; + uint32 rx270mbps; + uint32 rx324mbps; + uint32 rx378mbps; + uint32 rx432mbps; + uint32 rx486mbps; + uint32 rx540mbps; + + + uint32 pktengrxducast; + uint32 pktengrxdmcast; + + uint32 rfdisable; + uint32 bphy_rxcrsglitch; + + uint32 txmpdu_sgi; + uint32 rxmpdu_sgi; + uint32 txmpdu_stbc; + uint32 rxmpdu_stbc; +} wl_cnt_t; + + +#define WL_WME_CNT_VERSION 1 + +typedef struct { + uint32 packets; + uint32 bytes; +} wl_traffic_stats_t; + +typedef struct { + uint16 version; + uint16 length; + + wl_traffic_stats_t tx[AC_COUNT]; + wl_traffic_stats_t tx_failed[AC_COUNT]; + wl_traffic_stats_t rx[AC_COUNT]; + wl_traffic_stats_t rx_failed[AC_COUNT]; + + wl_traffic_stats_t forward[AC_COUNT]; + + wl_traffic_stats_t tx_expired[AC_COUNT]; + +} wl_wme_cnt_t; + +struct wl_msglevel2 { + uint32 low; + uint32 high; +}; + +typedef struct wl_mkeep_alive_pkt { + uint16 version; + uint16 length; + uint32 period_msec; + uint16 len_bytes; + uint8 keep_alive_id; + uint8 data[1]; +} wl_mkeep_alive_pkt_t; + +#define WL_MKEEP_ALIVE_VERSION 1 +#define WL_MKEEP_ALIVE_FIXED_LEN OFFSETOF(wl_mkeep_alive_pkt_t, data) +#define WL_MKEEP_ALIVE_PRECISION 500 + + + +#define WLC_ROAM_TRIGGER_DEFAULT 0 +#define WLC_ROAM_TRIGGER_BANDWIDTH 1 +#define WLC_ROAM_TRIGGER_DISTANCE 2 +#define WLC_ROAM_TRIGGER_AUTO 3 +#define WLC_ROAM_TRIGGER_MAX_VALUE 3 + + +#define WPA_AUTH_PFN_ANY 0xffffffff + +enum { + PFN_LIST_ORDER, + PFN_RSSI +}; + +enum { + DISABLE, + ENABLE +}; + +enum { + OFF_ADAPT, + SMART_ADAPT, + STRICT_ADAPT, + SLOW_ADAPT +}; + +#define SORT_CRITERIA_BIT 0 +#define AUTO_NET_SWITCH_BIT 1 +#define ENABLE_BKGRD_SCAN_BIT 2 +#define IMMEDIATE_SCAN_BIT 3 +#define AUTO_CONNECT_BIT 4 +#define ENABLE_BD_SCAN_BIT 5 +#define ENABLE_ADAPTSCAN_BIT 6 +#define IMMEDIATE_EVENT_BIT 8 + +#define SORT_CRITERIA_MASK 0x0001 +#define AUTO_NET_SWITCH_MASK 0x0002 +#define ENABLE_BKGRD_SCAN_MASK 0x0004 +#define IMMEDIATE_SCAN_MASK 0x0008 +#define AUTO_CONNECT_MASK 0x0010 +#define ENABLE_BD_SCAN_MASK 0x0020 +#define ENABLE_ADAPTSCAN_MASK 0x00c0 +#define IMMEDIATE_EVENT_MASK 0x0100 + +#define PFN_VERSION 2 +#define PFN_SCANRESULT_VERSION 1 +#define MAX_PFN_LIST_COUNT 16 + +#define PFN_COMPLETE 1 +#define PFN_INCOMPLETE 0 + +#define DEFAULT_BESTN 2 +#define DEFAULT_MSCAN 0 +#define DEFAULT_REPEAT 10 +#define DEFAULT_EXP 2 + + +typedef struct wl_pfn_subnet_info { + struct ether_addr BSSID; + uint8 channel; + uint8 SSID_len; + uint8 SSID[32]; +} wl_pfn_subnet_info_t; + +typedef struct wl_pfn_net_info { + wl_pfn_subnet_info_t pfnsubnet; + int16 RSSI; + uint16 timestamp; +} wl_pfn_net_info_t; + +typedef struct wl_pfn_scanresults { + uint32 version; + uint32 status; + uint32 count; + wl_pfn_net_info_t netinfo[1]; +} wl_pfn_scanresults_t; + + +typedef struct wl_pfn_param { + int32 version; + int32 scan_freq; + int32 lost_network_timeout; + int16 flags; + int16 rssi_margin; + uint8 bestn; + uint8 mscan; + uint8 repeat; + uint8 exp; + int32 slow_freq; +} wl_pfn_param_t; + +typedef struct wl_pfn_bssid { + struct ether_addr macaddr; + + uint16 flags; +} wl_pfn_bssid_t; +#define WL_PFN_SUPPRESSFOUND_MASK 0x08 +#define WL_PFN_SUPPRESSLOST_MASK 0x10 + +typedef struct wl_pfn_cfg { + uint32 reporttype; + int32 channel_num; + uint16 channel_list[WL_NUMCHANNELS]; +} wl_pfn_cfg_t; +#define WL_PFN_REPORT_ALLNET 0 +#define WL_PFN_REPORT_SSIDNET 1 +#define WL_PFN_REPORT_BSSIDNET 2 + +typedef struct wl_pfn { + wlc_ssid_t ssid; + int32 flags; + int32 infra; + int32 auth; + int32 wpa_auth; + int32 wsec; +} wl_pfn_t; +#define WL_PFN_HIDDEN_BIT 2 +#define PNO_SCAN_MAX_FW 508*1000 +#define PNO_SCAN_MAX_FW_SEC PNO_SCAN_MAX_FW/1000 +#define PNO_SCAN_MIN_FW_SEC 10 +#define WL_PFN_HIDDEN_MASK 0x4 + + +#define TOE_TX_CSUM_OL 0x00000001 +#define TOE_RX_CSUM_OL 0x00000002 + + +#define TOE_ERRTEST_TX_CSUM 0x00000001 +#define TOE_ERRTEST_RX_CSUM 0x00000002 +#define TOE_ERRTEST_RX_CSUM2 0x00000004 + +struct toe_ol_stats_t { + + uint32 tx_summed; + + + uint32 tx_iph_fill; + uint32 tx_tcp_fill; + uint32 tx_udp_fill; + uint32 tx_icmp_fill; + + + uint32 rx_iph_good; + uint32 rx_iph_bad; + uint32 rx_tcp_good; + uint32 rx_tcp_bad; + uint32 rx_udp_good; + uint32 rx_udp_bad; + uint32 rx_icmp_good; + uint32 rx_icmp_bad; + + + uint32 tx_tcp_errinj; + uint32 tx_udp_errinj; + uint32 tx_icmp_errinj; + + + uint32 rx_tcp_errinj; + uint32 rx_udp_errinj; + uint32 rx_icmp_errinj; +}; + + +#define ARP_OL_AGENT 0x00000001 +#define ARP_OL_SNOOP 0x00000002 +#define ARP_OL_HOST_AUTO_REPLY 0x00000004 +#define ARP_OL_PEER_AUTO_REPLY 0x00000008 + + +#define ARP_ERRTEST_REPLY_PEER 0x1 +#define ARP_ERRTEST_REPLY_HOST 0x2 + +#define ARP_MULTIHOMING_MAX 8 + + +struct arp_ol_stats_t { + uint32 host_ip_entries; + uint32 host_ip_overflow; + + uint32 arp_table_entries; + uint32 arp_table_overflow; + + uint32 host_request; + uint32 host_reply; + uint32 host_service; + + uint32 peer_request; + uint32 peer_request_drop; + uint32 peer_reply; + uint32 peer_reply_drop; + uint32 peer_service; +}; + + + + +typedef struct wl_keep_alive_pkt { + uint32 period_msec; + uint16 len_bytes; + uint8 data[1]; +} wl_keep_alive_pkt_t; + +#define WL_KEEP_ALIVE_FIXED_LEN OFFSETOF(wl_keep_alive_pkt_t, data) + + + + +#define MAX_WAKE_PACKET_BYTES 128 + + +typedef struct pm_wake_packet { + uint32 status; + uint32 pattern_id; + uint32 original_packet_size; + uint32 saved_packet_size; + uchar packet[MAX_WAKE_PACKET_BYTES]; +} pm_wake_packet_t; + + + +#define PKT_FILTER_MODE_FORWARD_ON_MATCH 1 + +#define PKT_FILTER_MODE_DISABLE 2 + +#define PKT_FILTER_MODE_PKT_CACHE_ON_MATCH 4 + +#define PKT_FILTER_MODE_PKT_FORWARD_OFF_DEFAULT 8 + + +typedef enum wl_pkt_filter_type { + WL_PKT_FILTER_TYPE_PATTERN_MATCH, + WL_PKT_FILTER_TYPE_MAGIC_PATTERN_MATCH +} wl_pkt_filter_type_t; + +#define WL_PKT_FILTER_TYPE wl_pkt_filter_type_t + + +typedef struct wl_pkt_filter_pattern { + uint32 offset; + uint32 size_bytes; + uint8 mask_and_pattern[1]; +} wl_pkt_filter_pattern_t; + + +typedef struct wl_pkt_filter { + uint32 id; + uint32 type; + uint32 negate_match; + union { + wl_pkt_filter_pattern_t pattern; + } u; +} wl_pkt_filter_t; + +#define WL_PKT_FILTER_FIXED_LEN OFFSETOF(wl_pkt_filter_t, u) +#define WL_PKT_FILTER_PATTERN_FIXED_LEN OFFSETOF(wl_pkt_filter_pattern_t, mask_and_pattern) + + +typedef struct wl_pkt_filter_enable { + uint32 id; + uint32 enable; +} wl_pkt_filter_enable_t; + + +typedef struct wl_pkt_filter_list { + uint32 num; + wl_pkt_filter_t filter[1]; +} wl_pkt_filter_list_t; + +#define WL_PKT_FILTER_LIST_FIXED_LEN OFFSETOF(wl_pkt_filter_list_t, filter) + + +typedef struct wl_pkt_filter_stats { + uint32 num_pkts_matched; + uint32 num_pkts_forwarded; + uint32 num_pkts_discarded; +} wl_pkt_filter_stats_t; + + +typedef struct wl_seq_cmd_ioctl { + uint32 cmd; + uint32 len; +} wl_seq_cmd_ioctl_t; + +#define WL_SEQ_CMD_ALIGN_BYTES 4 + + +#define WL_SEQ_CMDS_GET_IOCTL_FILTER(cmd) \ + (((cmd) == WLC_GET_MAGIC) || \ + ((cmd) == WLC_GET_VERSION) || \ + ((cmd) == WLC_GET_AP) || \ + ((cmd) == WLC_GET_INSTANCE)) + + + +#define WL_PKTENG_PER_TX_START 0x01 +#define WL_PKTENG_PER_TX_STOP 0x02 +#define WL_PKTENG_PER_RX_START 0x04 +#define WL_PKTENG_PER_RX_WITH_ACK_START 0x05 +#define WL_PKTENG_PER_TX_WITH_ACK_START 0x06 +#define WL_PKTENG_PER_RX_STOP 0x08 +#define WL_PKTENG_PER_MASK 0xff + +#define WL_PKTENG_SYNCHRONOUS 0x100 + +typedef struct wl_pkteng { + uint32 flags; + uint32 delay; + uint32 nframes; + uint32 length; + uint8 seqno; + struct ether_addr dest; + struct ether_addr src; +} wl_pkteng_t; + +#define NUM_80211b_RATES 4 +#define NUM_80211ag_RATES 8 +#define NUM_80211n_RATES 32 +#define NUM_80211_RATES (NUM_80211b_RATES+NUM_80211ag_RATES+NUM_80211n_RATES) +typedef struct wl_pkteng_stats { + uint32 lostfrmcnt; + int32 rssi; + int32 snr; + uint16 rxpktcnt[NUM_80211_RATES+1]; +} wl_pkteng_stats_t; + + +#define WL_WOWL_MAGIC (1 << 0) +#define WL_WOWL_NET (1 << 1) +#define WL_WOWL_DIS (1 << 2) +#define WL_WOWL_RETR (1 << 3) +#define WL_WOWL_BCN (1 << 4) +#define WL_WOWL_TST (1 << 5) +#define WL_WOWL_M1 (1 << 6) +#define WL_WOWL_EAPID (1 << 7) +#define WL_WOWL_KEYROT (1 << 14) +#define WL_WOWL_BCAST (1 << 15) + +#define MAGIC_PKT_MINLEN 102 + +typedef struct { + uint masksize; + uint offset; + uint patternoffset; + uint patternsize; + ulong id; + + +} wl_wowl_pattern_t; + +typedef struct { + uint count; + wl_wowl_pattern_t pattern[1]; +} wl_wowl_pattern_list_t; + +typedef struct { + uint8 pci_wakeind; + uint16 ucode_wakeind; +} wl_wowl_wakeind_t; + + +typedef struct wl_txrate_class { + uint8 init_rate; + uint8 min_rate; + uint8 max_rate; +} wl_txrate_class_t; + + + + +#define WLC_OBSS_SCAN_PASSIVE_DWELL_DEFAULT 20 +#define WLC_OBSS_SCAN_PASSIVE_DWELL_MIN 5 +#define WLC_OBSS_SCAN_PASSIVE_DWELL_MAX 1000 +#define WLC_OBSS_SCAN_ACTIVE_DWELL_DEFAULT 10 +#define WLC_OBSS_SCAN_ACTIVE_DWELL_MIN 10 +#define WLC_OBSS_SCAN_ACTIVE_DWELL_MAX 1000 +#define WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_DEFAULT 300 +#define WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_MIN 10 +#define WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_MAX 900 +#define WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_DEFAULT 5 +#define WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_MIN 5 +#define WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_MAX 100 +#define WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_DEFAULT 200 +#define WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_MIN 200 +#define WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_MAX 10000 +#define WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_DEFAULT 20 +#define WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_MIN 20 +#define WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_MAX 10000 +#define WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_DEFAULT 25 +#define WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_MIN 0 +#define WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_MAX 100 + + +typedef struct wl_obss_scan_arg { + int16 passive_dwell; + int16 active_dwell; + int16 bss_widthscan_interval; + int16 passive_total; + int16 active_total; + int16 chanwidth_transition_delay; + int16 activity_threshold; +} wl_obss_scan_arg_t; + +#define WL_OBSS_SCAN_PARAM_LEN sizeof(wl_obss_scan_arg_t) +#define WL_MIN_NUM_OBSS_SCAN_ARG 7 + +#define WL_COEX_INFO_MASK 0x07 +#define WL_COEX_INFO_REQ 0x01 +#define WL_COEX_40MHZ_INTOLERANT 0x02 +#define WL_COEX_WIDTH20 0x04 + +#define WLC_RSSI_INVALID 0 + +#define MAX_RSSI_LEVELS 8 + + +typedef struct wl_rssi_event { + uint32 rate_limit_msec; + uint8 num_rssi_levels; + int8 rssi_levels[MAX_RSSI_LEVELS]; +} wl_rssi_event_t; + +typedef struct wl_action_obss_coex_req { + uint8 info; + uint8 num; + uint8 ch_list[1]; +} wl_action_obss_coex_req_t; + + +#define EXTLOG_CUR_VER 0x0100 + +#define MAX_ARGSTR_LEN 18 + + +#define LOG_MODULE_COMMON 0x0001 +#define LOG_MODULE_ASSOC 0x0002 +#define LOG_MODULE_EVENT 0x0004 +#define LOG_MODULE_MAX 3 + + +#define WL_LOG_LEVEL_DISABLE 0 +#define WL_LOG_LEVEL_ERR 1 +#define WL_LOG_LEVEL_WARN 2 +#define WL_LOG_LEVEL_INFO 3 +#define WL_LOG_LEVEL_MAX WL_LOG_LEVEL_INFO + + +#define LOG_FLAG_EVENT 1 + + +#define LOG_ARGTYPE_NULL 0 +#define LOG_ARGTYPE_STR 1 +#define LOG_ARGTYPE_INT 2 +#define LOG_ARGTYPE_INT_STR 3 +#define LOG_ARGTYPE_STR_INT 4 + +typedef struct wlc_extlog_cfg { + int max_number; + uint16 module; + uint8 level; + uint8 flag; + uint16 version; +} wlc_extlog_cfg_t; + +typedef struct log_record { + uint32 time; + uint16 module; + uint16 id; + uint8 level; + uint8 sub_unit; + uint8 seq_num; + int32 arg; + char str[MAX_ARGSTR_LEN]; +} log_record_t; + +typedef struct wlc_extlog_req { + uint32 from_last; + uint32 num; +} wlc_extlog_req_t; + +typedef struct wlc_extlog_results { + uint16 version; + uint16 record_len; + uint32 num; + log_record_t logs[1]; +} wlc_extlog_results_t; + +typedef struct log_idstr { + uint16 id; + uint16 flag; + uint8 arg_type; + const char *fmt_str; +} log_idstr_t; + +#define FMTSTRF_USER 1 + + +typedef enum { + FMTSTR_DRIVER_UP_ID = 0, + FMTSTR_DRIVER_DOWN_ID = 1, + FMTSTR_SUSPEND_MAC_FAIL_ID = 2, + FMTSTR_NO_PROGRESS_ID = 3, + FMTSTR_RFDISABLE_ID = 4, + FMTSTR_REG_PRINT_ID = 5, + FMTSTR_EXPTIME_ID = 6, + FMTSTR_JOIN_START_ID = 7, + FMTSTR_JOIN_COMPLETE_ID = 8, + FMTSTR_NO_NETWORKS_ID = 9, + FMTSTR_SECURITY_MISMATCH_ID = 10, + FMTSTR_RATE_MISMATCH_ID = 11, + FMTSTR_AP_PRUNED_ID = 12, + FMTSTR_KEY_INSERTED_ID = 13, + FMTSTR_DEAUTH_ID = 14, + FMTSTR_DISASSOC_ID = 15, + FMTSTR_LINK_UP_ID = 16, + FMTSTR_LINK_DOWN_ID = 17, + FMTSTR_RADIO_HW_OFF_ID = 18, + FMTSTR_RADIO_HW_ON_ID = 19, + FMTSTR_EVENT_DESC_ID = 20, + FMTSTR_PNP_SET_POWER_ID = 21, + FMTSTR_RADIO_SW_OFF_ID = 22, + FMTSTR_RADIO_SW_ON_ID = 23, + FMTSTR_PWD_MISMATCH_ID = 24, + FMTSTR_FATAL_ERROR_ID = 25, + FMTSTR_AUTH_FAIL_ID = 26, + FMTSTR_ASSOC_FAIL_ID = 27, + FMTSTR_IBSS_FAIL_ID = 28, + FMTSTR_EXTAP_FAIL_ID = 29, + FMTSTR_MAX_ID +} log_fmtstr_id_t; + +#ifdef DONGLEOVERLAYS +typedef struct { + uint32 flags_idx; + uint32 offset; + uint32 len; + +} wl_ioctl_overlay_t; + +#define OVERLAY_IDX_MASK 0x000000ff +#define OVERLAY_IDX_SHIFT 0 +#define OVERLAY_FLAGS_MASK 0xffffff00 +#define OVERLAY_FLAGS_SHIFT 8 + +#define OVERLAY_FLAG_POSTLOAD 0x100 + +#define OVERLAY_FLAG_DEFER_DL 0x200 + +#define OVERLAY_FLAG_PRESLEEP 0x400 + +#define OVERLAY_DOWNLOAD_CHUNKSIZE 1024 +#endif + + +#include + + +#include + +#define VNDR_IE_CMD_LEN 4 + + +#define VNDR_IE_BEACON_FLAG 0x1 +#define VNDR_IE_PRBRSP_FLAG 0x2 +#define VNDR_IE_ASSOCRSP_FLAG 0x4 +#define VNDR_IE_AUTHRSP_FLAG 0x8 +#define VNDR_IE_PRBREQ_FLAG 0x10 +#define VNDR_IE_ASSOCREQ_FLAG 0x20 +#define VNDR_IE_CUSTOM_FLAG 0x100 + +#define VNDR_IE_INFO_HDR_LEN (sizeof(uint32)) + +typedef BWL_PRE_PACKED_STRUCT struct { + uint32 pktflag; + vndr_ie_t vndr_ie_data; +} BWL_POST_PACKED_STRUCT vndr_ie_info_t; + +typedef BWL_PRE_PACKED_STRUCT struct { + int iecount; + vndr_ie_info_t vndr_ie_list[1]; +} BWL_POST_PACKED_STRUCT vndr_ie_buf_t; + +typedef BWL_PRE_PACKED_STRUCT struct { + char cmd[VNDR_IE_CMD_LEN]; + vndr_ie_buf_t vndr_ie_buffer; +} BWL_POST_PACKED_STRUCT vndr_ie_setbuf_t; + + + +typedef BWL_PRE_PACKED_STRUCT struct sta_prbreq_wps_ie_hdr { + struct ether_addr staAddr; + uint16 ieLen; +} BWL_POST_PACKED_STRUCT sta_prbreq_wps_ie_hdr_t; + +typedef BWL_PRE_PACKED_STRUCT struct sta_prbreq_wps_ie_data { + sta_prbreq_wps_ie_hdr_t hdr; + uint8 ieData[1]; +} BWL_POST_PACKED_STRUCT sta_prbreq_wps_ie_data_t; + +typedef BWL_PRE_PACKED_STRUCT struct sta_prbreq_wps_ie_list { + uint32 totLen; + uint8 ieDataList[1]; +} BWL_POST_PACKED_STRUCT sta_prbreq_wps_ie_list_t; + + +#ifdef WLMEDIA_TXFAILEVENT +typedef BWL_PRE_PACKED_STRUCT struct { + char dest[ETHER_ADDR_LEN]; + uint8 prio; + uint8 flags; + uint32 tsf_l; + uint32 tsf_h; + uint16 rates; + uint16 txstatus; +} BWL_POST_PACKED_STRUCT txfailinfo_t; +#endif + +#include + + +#define ASSERTLOG_CUR_VER 0x0100 +#define MAX_ASSRTSTR_LEN 64 + +typedef struct assert_record { + uint32 time; + uint8 seq_num; + char str[MAX_ASSRTSTR_LEN]; +} assert_record_t; + +typedef struct assertlog_results { + uint16 version; + uint16 record_len; + uint32 num; + assert_record_t logs[1]; +} assertlog_results_t; + +#define LOGRRC_FIX_LEN 8 +#define IOBUF_ALLOWED_NUM_OF_LOGREC(type, len) ((len - LOGRRC_FIX_LEN)/sizeof(type)) + + + + + +#define CHANIM_DISABLE 0 +#define CHANIM_DETECT 1 +#define CHANIM_ACT 2 +#define CHANIM_MODE_MAX 2 + + +#define APCS_IOCTL 1 +#define APCS_CHANIM 2 +#define APCS_CSTIMER 3 +#define APCS_BTA 4 + + +#define CHANIM_ACS_RECORD 10 + + +typedef struct { + bool valid; + uint8 trigger; + chanspec_t selected_chspc; + uint32 glitch_cnt; + uint8 ccastats; + uint timestamp; +} chanim_acs_record_t; + +typedef struct { + chanim_acs_record_t acs_record[CHANIM_ACS_RECORD]; + uint8 count; + uint timestamp; +} wl_acs_record_t; + + + +#define SMFS_VERSION 1 + +typedef struct wl_smfs_elem { + uint32 count; + uint16 code; +} wl_smfs_elem_t; + +typedef struct wl_smf_stats { + uint32 version; + uint16 length; + uint8 type; + uint8 codetype; + uint32 ignored_cnt; + uint32 malformed_cnt; + uint32 count_total; + wl_smfs_elem_t elem[1]; +} wl_smf_stats_t; + +#define WL_SMFSTATS_FIXED_LEN OFFSETOF(wl_smf_stats_t, elem); + +enum { + SMFS_CODETYPE_SC, + SMFS_CODETYPE_RC +}; + + +#define SMFS_CODE_MALFORMED 0xFFFE +#define SMFS_CODE_IGNORED 0xFFFD + +typedef enum smfs_type { + SMFS_TYPE_AUTH, + SMFS_TYPE_ASSOC, + SMFS_TYPE_REASSOC, + SMFS_TYPE_DISASSOC_TX, + SMFS_TYPE_DISASSOC_RX, + SMFS_TYPE_DEAUTH_TX, + SMFS_TYPE_DEAUTH_RX, + SMFS_TYPE_MAX +} smfs_type_t; + +#ifdef PHYMON + +#define PHYMON_VERSION 1 + +typedef struct wl_phycal_core_state { + + int16 tx_iqlocal_a; + int16 tx_iqlocal_b; + int8 tx_iqlocal_ci; + int8 tx_iqlocal_cq; + int8 tx_iqlocal_di; + int8 tx_iqlocal_dq; + int8 tx_iqlocal_ei; + int8 tx_iqlocal_eq; + int8 tx_iqlocal_fi; + int8 tx_iqlocal_fq; + + + int16 rx_iqcal_a; + int16 rx_iqcal_b; + + uint8 tx_iqlocal_pwridx; + uint32 papd_epsilon_table[64]; + int16 papd_epsilon_offset; + uint8 curr_tx_pwrindex; + int8 idle_tssi; + int8 est_tx_pwr; + int8 est_rx_pwr; + uint16 rx_gaininfo; + uint16 init_gaincode; + int8 estirr_tx; + int8 estirr_rx; + +} wl_phycal_core_state_t; + +typedef struct wl_phycal_state { + int version; + int8 num_phy_cores; + int8 curr_temperature; + chanspec_t chspec; + bool aci_state; + uint16 crsminpower; + uint16 crsminpowerl; + uint16 crsminpoweru; + wl_phycal_core_state_t phycal_core[1]; +} wl_phycal_state_t; + +#define WL_PHYCAL_STAT_FIXED_LEN OFFSETOF(wl_phycal_state_t, phycal_core) +#endif + +#ifdef WLDSTA +typedef struct wl_dsta_if { + struct ether_addr addr; +} wl_dsta_if_t; +#endif + +#ifdef WLP2P + +typedef struct wl_p2p_disc_st { + uint8 state; + chanspec_t chspec; + uint16 dwell; +} wl_p2p_disc_st_t; + + +#define WL_P2P_DISC_ST_SCAN 0 +#define WL_P2P_DISC_ST_LISTEN 1 +#define WL_P2P_DISC_ST_SEARCH 2 + + +typedef struct wl_p2p_scan { + uint8 type; + uint8 reserved[3]; + +} wl_p2p_scan_t; + + +typedef struct wl_p2p_if { + struct ether_addr addr; + uint8 type; + chanspec_t chspec; +} wl_p2p_if_t; + + +#define WL_P2P_IF_CLIENT 0 +#define WL_P2P_IF_GO 1 +#define WL_P2P_IF_DYNBCN_GO 2 +#define WL_P2P_IF_DEV 3 + + +typedef struct wl_p2p_ifq { + uint bsscfgidx; + char ifname[BCM_MSG_IFNAME_MAX]; +} wl_p2p_ifq_t; + + +typedef struct wl_p2p_ops { + uint8 ops; + uint8 ctw; +} wl_p2p_ops_t; + + +typedef struct wl_p2p_sched_desc { + uint32 start; + uint32 interval; + uint32 duration; + uint32 count; +} wl_p2p_sched_desc_t; + + +#define WL_P2P_SCHED_RSVD 0 +#define WL_P2P_SCHED_REPEAT 255 + +typedef struct wl_p2p_sched { + uint8 type; + uint8 action; + uint8 option; + wl_p2p_sched_desc_t desc[1]; +} wl_p2p_sched_t; +#define WL_P2P_SCHED_FIXED_LEN 3 + + +#define WL_P2P_SCHED_TYPE_ABS 0 +#define WL_P2P_SCHED_TYPE_REQ_ABS 1 + + +#define WL_P2P_SCHED_ACTION_NONE 0 +#define WL_P2P_SCHED_ACTION_DOZE 1 + +#define WL_P2P_SCHED_ACTION_GOOFF 2 + +#define WL_P2P_SCHED_ACTION_RESET 255 + + +#define WL_P2P_SCHED_OPTION_NORMAL 0 +#define WL_P2P_SCHED_OPTION_BCNPCT 1 + +#define WL_P2P_SCHED_OPTION_TSFOFS 2 + + +#define WL_P2P_FEAT_GO_CSA (1 << 0) +#define WL_P2P_FEAT_GO_NOLEGACY (1 << 1) +#define WL_P2P_FEAT_RESTRICT_DEV_RESP (1 << 2) +#endif + + +#define BCM_ACTION_RFAWARE 0x77 +#define BCM_ACTION_RFAWARE_DCS 0x01 + + + +#define WL_11N_2x2 1 +#define WL_11N_3x3 3 +#define WL_11N_4x4 4 + + +#define WLFEATURE_DISABLE_11N 0x00000001 +#define WLFEATURE_DISABLE_11N_STBC_TX 0x00000002 +#define WLFEATURE_DISABLE_11N_STBC_RX 0x00000004 +#define WLFEATURE_DISABLE_11N_SGI_TX 0x00000008 +#define WLFEATURE_DISABLE_11N_SGI_RX 0x00000010 +#define WLFEATURE_DISABLE_11N_AMPDU_TX 0x00000020 +#define WLFEATURE_DISABLE_11N_AMPDU_RX 0x00000040 +#define WLFEATURE_DISABLE_11N_GF 0x00000080 + + +#define LQ_IDX_LAST 3 +#define MCS_INDEX_SIZE 33 + +#define LQ_IDX_MIN 0 +#define LQ_IDX_MAX 1 +#define LQ_IDX_AVG 2 +#define LQ_IDX_SUM 2 +#define LQ_IDX_LAST 3 +#define LQ_STOP_MONITOR 0 +#define LQ_START_MONITOR 1 + +#define LINKQUAL_V1 0x01 + +struct wl_lq { + int32 enable; + int32 rssi[LQ_IDX_LAST]; + int32 rssicnt; + int32 snr[LQ_IDX_LAST]; + uint32 nsamples; + uint8 isvalid; + uint8 version; +}; + +typedef struct wl_lq wl_lq_t; +typedef struct wl_lq wl_lq_stats_t; + +typedef struct { + struct ether_addr ea; + uint8 ac_cat; + uint8 num_pkts; +} wl_mac_ratehisto_cmd_t; + + +typedef struct { + uint32 rate[WLC_MAXRATE + 1]; + uint32 mcs_index[MCS_INDEX_SIZE]; + uint32 tsf_timer[2][2]; +} wl_mac_ratehisto_res_t; + +#ifdef PROP_TXSTATUS + + +#define WLFC_FLAGS_RSSI_SIGNALS 1 + + +#define WLFC_FLAGS_XONXOFF_SIGNALS 2 + + +#define WLFC_FLAGS_CREDIT_STATUS_SIGNALS 4 + +#define WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE 8 +#define WLFC_FLAGS_PSQ_GENERATIONFSM_ENABLE 16 +#define WLFC_FLAGS_PSQ_ZERO_BUFFER_ENABLE 32 +#endif + +#define BTA_STATE_LOG_SZ 64 + + +enum { + HCIReset = 1, + HCIReadLocalAMPInfo, + HCIReadLocalAMPASSOC, + HCIWriteRemoteAMPASSOC, + HCICreatePhysicalLink, + HCIAcceptPhysicalLinkRequest, + HCIDisconnectPhysicalLink, + HCICreateLogicalLink, + HCIAcceptLogicalLink, + HCIDisconnectLogicalLink, + HCILogicalLinkCancel, + HCIAmpStateChange, + HCIWriteLogicalLinkAcceptTimeout +}; + +typedef struct flush_txfifo { + uint32 txfifobmp; + uint32 hwtxfifoflush; + struct ether_addr ea; +} flush_txfifo_t; + +#define CHANNEL_5G_LOW_START 36 +#define CHANNEL_5G_MID_START 52 +#define CHANNEL_5G_HIGH_START 100 +#define CHANNEL_5G_UPPER_START 149 + +enum { + SPATIAL_MODE_2G_IDX = 0, + SPATIAL_MODE_5G_LOW_IDX, + SPATIAL_MODE_5G_MID_IDX, + SPATIAL_MODE_5G_HIGH_IDX, + SPATIAL_MODE_5G_UPPER_IDX, + SPATIAL_MODE_MAX_IDX +}; + +#endif diff --git a/drivers/net/wireless/bcmdhd/linux_osl.c b/drivers/net/wireless/bcmdhd/linux_osl.c new file mode 100644 index 0000000000000..4ef7bf7b24dc7 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/linux_osl.c @@ -0,0 +1,924 @@ +/* + * Linux OS Independent Layer + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: linux_osl.c,v 1.168.2.7 2011-01-27 17:01:13 $ + */ + + +#define LINUX_PORT + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BCMASSERT_LOG +#include +#endif + +#include + +#define PCI_CFG_RETRY 10 + +#define OS_HANDLE_MAGIC 0x1234abcd +#define BCM_MEM_FILENAME_LEN 24 + +#ifdef CONFIG_DHD_USE_STATIC_BUF +#define STATIC_BUF_MAX_NUM 16 +#define STATIC_BUF_SIZE (PAGE_SIZE * 2) +#define STATIC_BUF_TOTAL_LEN (STATIC_BUF_MAX_NUM * STATIC_BUF_SIZE) + +typedef struct bcm_static_buf { + struct semaphore static_sem; + unsigned char *buf_ptr; + unsigned char buf_use[STATIC_BUF_MAX_NUM]; +} bcm_static_buf_t; + +static bcm_static_buf_t *bcm_static_buf = 0; + +#define STATIC_PKT_MAX_NUM 8 + +typedef struct bcm_static_pkt { + struct sk_buff *skb_4k[STATIC_PKT_MAX_NUM]; + struct sk_buff *skb_8k[STATIC_PKT_MAX_NUM]; + struct semaphore osl_pkt_sem; + unsigned char pkt_use[STATIC_PKT_MAX_NUM * 2]; +} bcm_static_pkt_t; + +static bcm_static_pkt_t *bcm_static_skb = 0; +#endif + +typedef struct bcm_mem_link { + struct bcm_mem_link *prev; + struct bcm_mem_link *next; + uint size; + int line; + char file[BCM_MEM_FILENAME_LEN]; +} bcm_mem_link_t; + +struct osl_info { + osl_pubinfo_t pub; +#ifdef CTFPOOL + ctfpool_t *ctfpool; +#endif + uint magic; + void *pdev; + atomic_t malloced; + uint failed; + uint bustype; + bcm_mem_link_t *dbgmem_list; +}; + + + + +uint32 g_assert_type = FALSE; + +static int16 linuxbcmerrormap[] = +{ 0, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -E2BIG, + -E2BIG, + -EBUSY, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EFAULT, + -ENOMEM, + -EOPNOTSUPP, + -EMSGSIZE, + -EINVAL, + -EPERM, + -ENOMEM, + -EINVAL, + -ERANGE, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EINVAL, + -EIO, + -ENODEV, + -EINVAL, + -EIO, + -EIO, + -ENODEV, + -EINVAL, + -ENODATA, + + + +#if BCME_LAST != -42 +#error "You need to add a OS error translation in the linuxbcmerrormap \ + for new error code defined in bcmutils.h" +#endif +}; + + +int +osl_error(int bcmerror) +{ + if (bcmerror > 0) + bcmerror = 0; + else if (bcmerror < BCME_LAST) + bcmerror = BCME_ERROR; + + + return linuxbcmerrormap[-bcmerror]; +} + +extern uint8* dhd_os_prealloc(void *osh, int section, int size); + +osl_t * +osl_attach(void *pdev, uint bustype, bool pkttag) +{ + osl_t *osh; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + gfp_t flags; + + flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; + osh = kmalloc(sizeof(osl_t), flags); +#else + osh = kmalloc(sizeof(osl_t), GFP_ATOMIC); +#endif + ASSERT(osh); + + bzero(osh, sizeof(osl_t)); + + + ASSERT(ABS(BCME_LAST) == (ARRAYSIZE(linuxbcmerrormap) - 1)); + + osh->magic = OS_HANDLE_MAGIC; + atomic_set(&osh->malloced, 0); + osh->failed = 0; + osh->dbgmem_list = NULL; + osh->pdev = pdev; + osh->pub.pkttag = pkttag; + osh->bustype = bustype; + + switch (bustype) { + case PCI_BUS: + case SI_BUS: + case PCMCIA_BUS: + osh->pub.mmbus = TRUE; + break; + case JTAG_BUS: + case SDIO_BUS: + case USB_BUS: + case SPI_BUS: + case RPC_BUS: + osh->pub.mmbus = FALSE; + break; + default: + ASSERT(FALSE); + break; + } + +#if defined(CONFIG_DHD_USE_STATIC_BUF) + if (!bcm_static_buf) { + if (!(bcm_static_buf = (bcm_static_buf_t *)dhd_os_prealloc(osh, 3, STATIC_BUF_SIZE+ + STATIC_BUF_TOTAL_LEN))) { + printk("can not alloc static buf!\n"); + } + else + printk("alloc static buf at %x!\n", (unsigned int)bcm_static_buf); + + sema_init(&bcm_static_buf->static_sem, 1); + + bcm_static_buf->buf_ptr = (unsigned char *)bcm_static_buf + STATIC_BUF_SIZE; + } + + if (!bcm_static_skb) { + int i; + void *skb_buff_ptr = 0; + bcm_static_skb = (bcm_static_pkt_t *)((char *)bcm_static_buf + 2048); + skb_buff_ptr = dhd_os_prealloc(osh, 4, 0); + + bcopy(skb_buff_ptr, bcm_static_skb, sizeof(struct sk_buff *) * 16); + for (i = 0; i < STATIC_PKT_MAX_NUM * 2; i++) + bcm_static_skb->pkt_use[i] = 0; + + sema_init(&bcm_static_skb->osl_pkt_sem, 1); + } +#endif + + return osh; +} + +void +osl_detach(osl_t *osh) +{ + if (osh == NULL) + return; + + ASSERT(osh->magic == OS_HANDLE_MAGIC); + kfree(osh); +} + +static struct sk_buff *osl_alloc_skb(unsigned int len) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) + gfp_t flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; + + return __dev_alloc_skb(len, flags); +#else + return dev_alloc_skb(len); +#endif +} + +#ifdef CTFPOOL + +void * +osl_ctfpool_add(osl_t *osh) +{ + struct sk_buff *skb; + + if ((osh == NULL) || (osh->ctfpool == NULL)) + return NULL; + + spin_lock_bh(&osh->ctfpool->lock); + ASSERT(osh->ctfpool->curr_obj <= osh->ctfpool->max_obj); + + + if (osh->ctfpool->curr_obj == osh->ctfpool->max_obj) { + spin_unlock_bh(&osh->ctfpool->lock); + return NULL; + } + + + skb = osl_alloc_skb(osh->ctfpool->obj_size); + if (skb == NULL) { + printf("%s: skb alloc of len %d failed\n", __FUNCTION__, + osh->ctfpool->obj_size); + spin_unlock_bh(&osh->ctfpool->lock); + return NULL; + } + + + skb->next = (struct sk_buff *)osh->ctfpool->head; + osh->ctfpool->head = skb; + osh->ctfpool->fast_frees++; + osh->ctfpool->curr_obj++; + + + CTFPOOLPTR(osh, skb) = (void *)osh->ctfpool; + + + PKTFAST(osh, skb) = FASTBUF; + + spin_unlock_bh(&osh->ctfpool->lock); + + return skb; +} + + +void +osl_ctfpool_replenish(osl_t *osh, uint thresh) +{ + if ((osh == NULL) || (osh->ctfpool == NULL)) + return; + + + while ((osh->ctfpool->refills > 0) && (thresh--)) { + osl_ctfpool_add(osh); + osh->ctfpool->refills--; + } +} + + +int32 +osl_ctfpool_init(osl_t *osh, uint numobj, uint size) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + gfp_t flags; + + flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; + osh->ctfpool = kmalloc(sizeof(ctfpool_t), flags); +#else + osh->ctfpool = kmalloc(sizeof(ctfpool_t), GFP_ATOMIC); +#endif + ASSERT(osh->ctfpool); + bzero(osh->ctfpool, sizeof(ctfpool_t)); + + osh->ctfpool->max_obj = numobj; + osh->ctfpool->obj_size = size; + + spin_lock_init(&osh->ctfpool->lock); + + while (numobj--) { + if (!osl_ctfpool_add(osh)) + return -1; + osh->ctfpool->fast_frees--; + } + + return 0; +} + + +void +osl_ctfpool_cleanup(osl_t *osh) +{ + struct sk_buff *skb, *nskb; + + if ((osh == NULL) || (osh->ctfpool == NULL)) + return; + + spin_lock_bh(&osh->ctfpool->lock); + + skb = osh->ctfpool->head; + + while (skb != NULL) { + nskb = skb->next; + dev_kfree_skb(skb); + skb = nskb; + osh->ctfpool->curr_obj--; + } + + ASSERT(osh->ctfpool->curr_obj == 0); + osh->ctfpool->head = NULL; + spin_unlock_bh(&osh->ctfpool->lock); + + kfree(osh->ctfpool); + osh->ctfpool = NULL; +} + +void +osl_ctfpool_stats(osl_t *osh, void *b) +{ + struct bcmstrbuf *bb; + + if ((osh == NULL) || (osh->ctfpool == NULL)) + return; + +#ifdef CONFIG_DHD_USE_STATIC_BUF + if (bcm_static_buf) { + bcm_static_buf = 0; + } + if (bcm_static_skb) { + bcm_static_skb = 0; + } +#endif + + bb = b; + + ASSERT((osh != NULL) && (bb != NULL)); + + bcm_bprintf(bb, "max_obj %d obj_size %d curr_obj %d refills %d\n", + osh->ctfpool->max_obj, osh->ctfpool->obj_size, + osh->ctfpool->curr_obj, osh->ctfpool->refills); + bcm_bprintf(bb, "fast_allocs %d fast_frees %d slow_allocs %d\n", + osh->ctfpool->fast_allocs, osh->ctfpool->fast_frees, + osh->ctfpool->slow_allocs); +} + +static inline struct sk_buff * +osl_pktfastget(osl_t *osh, uint len) +{ + struct sk_buff *skb; + + + if (osh->ctfpool == NULL) + return NULL; + + spin_lock_bh(&osh->ctfpool->lock); + if (osh->ctfpool->head == NULL) { + ASSERT(osh->ctfpool->curr_obj == 0); + osh->ctfpool->slow_allocs++; + spin_unlock_bh(&osh->ctfpool->lock); + return NULL; + } + + ASSERT(len <= osh->ctfpool->obj_size); + + + skb = (struct sk_buff *)osh->ctfpool->head; + osh->ctfpool->head = (void *)skb->next; + + osh->ctfpool->fast_allocs++; + osh->ctfpool->curr_obj--; + ASSERT(CTFPOOLHEAD(osh, skb) == (struct sock *)osh->ctfpool->head); + spin_unlock_bh(&osh->ctfpool->lock); + + + skb->next = skb->prev = NULL; + skb->data = skb->head + 16; + skb->tail = skb->head + 16; + + skb->len = 0; + skb->cloned = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + skb->list = NULL; +#endif + atomic_set(&skb->users, 1); + + return skb; +} +#endif + + +void * BCMFASTPATH +osl_pktget(osl_t *osh, uint len) +{ + struct sk_buff *skb; + +#ifdef CTFPOOL + skb = osl_pktfastget(osh, len); + if ((skb != NULL) || ((skb = osl_alloc_skb(len)) != NULL)) { +#else + if ((skb = osl_alloc_skb(len))) { +#endif + skb_put(skb, len); + skb->priority = 0; + + osh->pub.pktalloced++; + } + + return ((void*) skb); +} + +#ifdef CTFPOOL +static inline void +osl_pktfastfree(osl_t *osh, struct sk_buff *skb) +{ + ctfpool_t *ctfpool; + + ctfpool = (ctfpool_t *)CTFPOOLPTR(osh, skb); + ASSERT(ctfpool != NULL); + + + spin_lock_bh(&ctfpool->lock); + skb->next = (struct sk_buff *)ctfpool->head; + ctfpool->head = (void *)skb; + + ctfpool->fast_frees++; + ctfpool->curr_obj++; + + ASSERT(ctfpool->curr_obj <= ctfpool->max_obj); + spin_unlock_bh(&ctfpool->lock); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) + skb->tstamp.tv.sec = 0; +#else + skb->stamp.tv_sec = 0; +#endif + + + skb->dev = NULL; + skb->dst = NULL; + memset(skb->cb, 0, sizeof(skb->cb)); + skb->ip_summed = 0; + skb->destructor = NULL; +} +#endif + + +void BCMFASTPATH +osl_pktfree(osl_t *osh, void *p, bool send) +{ + struct sk_buff *skb, *nskb; + + skb = (struct sk_buff*) p; + + if (send && osh->pub.tx_fn) + osh->pub.tx_fn(osh->pub.tx_ctx, p, 0); + + + while (skb) { + nskb = skb->next; + skb->next = NULL; + + +#ifdef CTFPOOL + if (PKTISFAST(osh, skb)) + osl_pktfastfree(osh, skb); + else { +#else + { +#endif + + if (skb->destructor) + + dev_kfree_skb_any(skb); + else + + dev_kfree_skb(skb); + } + + osh->pub.pktalloced--; + + skb = nskb; + } +} + +#ifdef CONFIG_DHD_USE_STATIC_BUF +void * +osl_pktget_static(osl_t *osh, uint len) +{ + int i; + struct sk_buff *skb; + + if (!bcm_static_skb || (len > (PAGE_SIZE * 2))) { + printk("%s: attempt to allocate huge packet (0x%x)\n", __FUNCTION__, len); + return osl_pktget(osh, len); + } + + down(&bcm_static_skb->osl_pkt_sem); + + if (len <= PAGE_SIZE) { + for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { + if (bcm_static_skb->pkt_use[i] == 0) + break; + } + + if (i != STATIC_PKT_MAX_NUM) { + bcm_static_skb->pkt_use[i] = 1; + skb = bcm_static_skb->skb_4k[i]; + skb->tail = skb->data + len; + skb->len = len; + up(&bcm_static_skb->osl_pkt_sem); + return skb; + } + } + + + for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { + if (bcm_static_skb->pkt_use[i+STATIC_PKT_MAX_NUM] == 0) + break; + } + + if (i != STATIC_PKT_MAX_NUM) { + bcm_static_skb->pkt_use[i+STATIC_PKT_MAX_NUM] = 1; + skb = bcm_static_skb->skb_8k[i]; + skb->tail = skb->data + len; + skb->len = len; + up(&bcm_static_skb->osl_pkt_sem); + return skb; + } + + up(&bcm_static_skb->osl_pkt_sem); + printk("%s: all static pkt in use!\n", __FUNCTION__); + return osl_pktget(osh, len); +} + +void +osl_pktfree_static(osl_t *osh, void *p, bool send) +{ + int i; + + if (!bcm_static_skb) { + osl_pktfree(osh, p, send); + return; + } + + down(&bcm_static_skb->osl_pkt_sem); + for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { + if (p == bcm_static_skb->skb_4k[i]) { + bcm_static_skb->pkt_use[i] = 0; + up(&bcm_static_skb->osl_pkt_sem); + return; + } + } + + for (i = 0; i < STATIC_PKT_MAX_NUM; i++) { + if (p == bcm_static_skb->skb_8k[i]) { + bcm_static_skb->pkt_use[i + STATIC_PKT_MAX_NUM] = 0; + up(&bcm_static_skb->osl_pkt_sem); + return; + } + } + up(&bcm_static_skb->osl_pkt_sem); + + osl_pktfree(osh, p, send); + return; +} +#endif + +uint32 +osl_pci_read_config(osl_t *osh, uint offset, uint size) +{ + uint val = 0; + uint retry = PCI_CFG_RETRY; + + ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); + + + ASSERT(size == 4); + + do { + pci_read_config_dword(osh->pdev, offset, &val); + if (val != 0xffffffff) + break; + } while (retry--); + + + return (val); +} + +void +osl_pci_write_config(osl_t *osh, uint offset, uint size, uint val) +{ + uint retry = PCI_CFG_RETRY; + + ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); + + + ASSERT(size == 4); + + do { + pci_write_config_dword(osh->pdev, offset, val); + if (offset != PCI_BAR0_WIN) + break; + if (osl_pci_read_config(osh, offset, size) == val) + break; + } while (retry--); + +} + + +uint +osl_pci_bus(osl_t *osh) +{ + ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev); + + return ((struct pci_dev *)osh->pdev)->bus->number; +} + + +uint +osl_pci_slot(osl_t *osh) +{ + ASSERT(osh && (osh->magic == OS_HANDLE_MAGIC) && osh->pdev); + + return PCI_SLOT(((struct pci_dev *)osh->pdev)->devfn); +} + +static void +osl_pcmcia_attr(osl_t *osh, uint offset, char *buf, int size, bool write) +{ +} + +void +osl_pcmcia_read_attr(osl_t *osh, uint offset, void *buf, int size) +{ + osl_pcmcia_attr(osh, offset, (char *) buf, size, FALSE); +} + +void +osl_pcmcia_write_attr(osl_t *osh, uint offset, void *buf, int size) +{ + osl_pcmcia_attr(osh, offset, (char *) buf, size, TRUE); +} + +void * +osl_malloc(osl_t *osh, uint size) +{ + void *addr; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + gfp_t flags; + + + if (osh) + ASSERT(osh->magic == OS_HANDLE_MAGIC); + + flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; + if ((addr = kmalloc(size, flags)) == NULL) { +#else + if ((addr = kmalloc(size, GFP_ATOMIC)) == NULL) { +#endif + if (osh) + osh->failed++; + return (NULL); + } + if (osh) + atomic_add(size, &osh->malloced); + + return (addr); +} + +void +osl_mfree(osl_t *osh, void *addr, uint size) +{ + if (osh) { + ASSERT(osh->magic == OS_HANDLE_MAGIC); + atomic_sub(size, &osh->malloced); + } + kfree(addr); +} + +uint +osl_malloced(osl_t *osh) +{ + ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); + return (atomic_read(&osh->malloced)); +} + +uint +osl_malloc_failed(osl_t *osh) +{ + ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); + return (osh->failed); +} + + + +uint +osl_dma_consistent_align(void) +{ + return (PAGE_SIZE); +} + +void* +osl_dma_alloc_consistent(osl_t *osh, uint size, uint16 align_bits, uint *alloced, ulong *pap) +{ + uint16 align = (1 << align_bits); + ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); + + if (!ISALIGNED(DMA_CONSISTENT_ALIGN, align)) + size += align; + *alloced = size; + + return (pci_alloc_consistent(osh->pdev, size, (dma_addr_t*)pap)); +} + +void +osl_dma_free_consistent(osl_t *osh, void *va, uint size, ulong pa) +{ + ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); + + pci_free_consistent(osh->pdev, size, va, (dma_addr_t)pa); +} + +uint BCMFASTPATH +osl_dma_map(osl_t *osh, void *va, uint size, int direction) +{ + int dir; + + ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); + dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE; + return (pci_map_single(osh->pdev, va, size, dir)); +} + +void BCMFASTPATH +osl_dma_unmap(osl_t *osh, uint pa, uint size, int direction) +{ + int dir; + + ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); + dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE; + pci_unmap_single(osh->pdev, (uint32)pa, size, dir); +} + +#if defined(BCMASSERT_LOG) +void +osl_assert(char *exp, char *file, int line) +{ + char tempbuf[256]; + char *basename; + + basename = strrchr(file, '/'); + + if (basename) + basename++; + + if (!basename) + basename = file; + +#ifdef BCMASSERT_LOG + snprintf(tempbuf, 64, "\"%s\": file \"%s\", line %d\n", + exp, basename, line); + + bcm_assert_log(tempbuf); +#endif + + +} +#endif + +void +osl_delay(uint usec) +{ + uint d; + + while (usec > 0) { + d = MIN(usec, 1000); + udelay(d); + usec -= d; + } +} + + + +void * +osl_pktdup(osl_t *osh, void *skb) +{ + void * p; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + gfp_t flags; + + flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; + if ((p = skb_clone((struct sk_buff *)skb, flags)) == NULL) +#else + if ((p = skb_clone((struct sk_buff*)skb, GFP_ATOMIC)) == NULL) +#endif + return NULL; + +#ifdef CTFPOOL + if (PKTISFAST(osh, skb)) { + ctfpool_t *ctfpool; + + + ctfpool = (ctfpool_t *)CTFPOOLPTR(osh, skb); + ASSERT(ctfpool != NULL); + PKTCLRFAST(osh, p); + PKTCLRFAST(osh, skb); + ctfpool->refills++; + } +#endif + + + if (osh->pub.pkttag) + bzero((void*)((struct sk_buff *)p)->cb, OSL_PKTTAG_SZ); + + + osh->pub.pktalloced++; + return (p); +} + + + + + + + +void * +osl_os_open_image(char *filename) +{ + struct file *fp; + + fp = filp_open(filename, O_RDONLY, 0); + + if (IS_ERR(fp)) + fp = NULL; + + return fp; +} + +int +osl_os_get_image_block(char *buf, int len, void *image) +{ + struct file *fp = (struct file *)image; + int rdlen; + + if (!image) + return 0; + + rdlen = kernel_read(fp, fp->f_pos, buf, len); + if (rdlen > 0) + fp->f_pos += rdlen; + + return rdlen; +} + +void +osl_os_close_image(void *image) +{ + if (image) + filp_close((struct file *)image, NULL); +} diff --git a/drivers/net/wireless/bcmdhd/sbutils.c b/drivers/net/wireless/bcmdhd/sbutils.c new file mode 100644 index 0000000000000..02d1bc0a79d1a --- /dev/null +++ b/drivers/net/wireless/bcmdhd/sbutils.c @@ -0,0 +1,992 @@ +/* + * Misc utility routines for accessing chip-specific features + * of the SiliconBackplane-based Broadcom chips. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: sbutils.c,v 1.687.2.1 2010-11-29 20:21:56 Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "siutils_priv.h" + + +/* local prototypes */ +static uint _sb_coreidx(si_info_t *sii, uint32 sba); +static uint _sb_scan(si_info_t *sii, uint32 sba, void *regs, uint bus, uint32 sbba, + uint ncores); +static uint32 _sb_coresba(si_info_t *sii); +static void *_sb_setcoreidx(si_info_t *sii, uint coreidx); + +#define SET_SBREG(sii, r, mask, val) \ + W_SBREG((sii), (r), ((R_SBREG((sii), (r)) & ~(mask)) | (val))) +#define REGS2SB(va) (sbconfig_t*) ((int8*)(va) + SBCONFIGOFF) + +/* sonicsrev */ +#define SONICS_2_2 (SBIDL_RV_2_2 >> SBIDL_RV_SHIFT) +#define SONICS_2_3 (SBIDL_RV_2_3 >> SBIDL_RV_SHIFT) + +#define R_SBREG(sii, sbr) sb_read_sbreg((sii), (sbr)) +#define W_SBREG(sii, sbr, v) sb_write_sbreg((sii), (sbr), (v)) +#define AND_SBREG(sii, sbr, v) W_SBREG((sii), (sbr), (R_SBREG((sii), (sbr)) & (v))) +#define OR_SBREG(sii, sbr, v) W_SBREG((sii), (sbr), (R_SBREG((sii), (sbr)) | (v))) + +static uint32 +sb_read_sbreg(si_info_t *sii, volatile uint32 *sbr) +{ + uint8 tmp; + uint32 val, intr_val = 0; + + + /* + * compact flash only has 11 bits address, while we needs 12 bits address. + * MEM_SEG will be OR'd with other 11 bits address in hardware, + * so we program MEM_SEG with 12th bit when necessary(access sb regsiters). + * For normal PCMCIA bus(CFTable_regwinsz > 2k), do nothing special + */ + if (PCMCIA(sii)) { + INTR_OFF(sii, intr_val); + tmp = 1; + OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1); + sbr = (volatile uint32 *)((uintptr)sbr & ~(1 << 11)); /* mask out bit 11 */ + } + + val = R_REG(sii->osh, sbr); + + if (PCMCIA(sii)) { + tmp = 0; + OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1); + INTR_RESTORE(sii, intr_val); + } + + return (val); +} + +static void +sb_write_sbreg(si_info_t *sii, volatile uint32 *sbr, uint32 v) +{ + uint8 tmp; + volatile uint32 dummy; + uint32 intr_val = 0; + + + /* + * compact flash only has 11 bits address, while we needs 12 bits address. + * MEM_SEG will be OR'd with other 11 bits address in hardware, + * so we program MEM_SEG with 12th bit when necessary(access sb regsiters). + * For normal PCMCIA bus(CFTable_regwinsz > 2k), do nothing special + */ + if (PCMCIA(sii)) { + INTR_OFF(sii, intr_val); + tmp = 1; + OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1); + sbr = (volatile uint32 *)((uintptr)sbr & ~(1 << 11)); /* mask out bit 11 */ + } + + if (BUSTYPE(sii->pub.bustype) == PCMCIA_BUS) { + dummy = R_REG(sii->osh, sbr); + W_REG(sii->osh, (volatile uint16 *)sbr, (uint16)(v & 0xffff)); + dummy = R_REG(sii->osh, sbr); + W_REG(sii->osh, ((volatile uint16 *)sbr + 1), (uint16)((v >> 16) & 0xffff)); + } else + W_REG(sii->osh, sbr, v); + + if (PCMCIA(sii)) { + tmp = 0; + OSL_PCMCIA_WRITE_ATTR(sii->osh, MEM_SEG, &tmp, 1); + INTR_RESTORE(sii, intr_val); + } +} + +uint +sb_coreid(si_t *sih) +{ + si_info_t *sii; + sbconfig_t *sb; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + return ((R_SBREG(sii, &sb->sbidhigh) & SBIDH_CC_MASK) >> SBIDH_CC_SHIFT); +} + +uint +sb_intflag(si_t *sih) +{ + si_info_t *sii; + void *corereg; + sbconfig_t *sb; + uint origidx, intflag, intr_val = 0; + + sii = SI_INFO(sih); + + INTR_OFF(sii, intr_val); + origidx = si_coreidx(sih); + corereg = si_setcore(sih, CC_CORE_ID, 0); + ASSERT(corereg != NULL); + sb = REGS2SB(corereg); + intflag = R_SBREG(sii, &sb->sbflagst); + sb_setcoreidx(sih, origidx); + INTR_RESTORE(sii, intr_val); + + return intflag; +} + +uint +sb_flag(si_t *sih) +{ + si_info_t *sii; + sbconfig_t *sb; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + return R_SBREG(sii, &sb->sbtpsflag) & SBTPS_NUM0_MASK; +} + +void +sb_setint(si_t *sih, int siflag) +{ + si_info_t *sii; + sbconfig_t *sb; + uint32 vec; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + if (siflag == -1) + vec = 0; + else + vec = 1 << siflag; + W_SBREG(sii, &sb->sbintvec, vec); +} + +/* return core index of the core with address 'sba' */ +static uint +_sb_coreidx(si_info_t *sii, uint32 sba) +{ + uint i; + + for (i = 0; i < sii->numcores; i ++) + if (sba == sii->coresba[i]) + return i; + return BADIDX; +} + +/* return core address of the current core */ +static uint32 +_sb_coresba(si_info_t *sii) +{ + uint32 sbaddr; + + + switch (BUSTYPE(sii->pub.bustype)) { + case SI_BUS: { + sbconfig_t *sb = REGS2SB(sii->curmap); + sbaddr = sb_base(R_SBREG(sii, &sb->sbadmatch0)); + break; + } + + case PCI_BUS: + sbaddr = OSL_PCI_READ_CONFIG(sii->osh, PCI_BAR0_WIN, sizeof(uint32)); + break; + + case PCMCIA_BUS: { + uint8 tmp = 0; + OSL_PCMCIA_READ_ATTR(sii->osh, PCMCIA_ADDR0, &tmp, 1); + sbaddr = (uint32)tmp << 12; + OSL_PCMCIA_READ_ATTR(sii->osh, PCMCIA_ADDR1, &tmp, 1); + sbaddr |= (uint32)tmp << 16; + OSL_PCMCIA_READ_ATTR(sii->osh, PCMCIA_ADDR2, &tmp, 1); + sbaddr |= (uint32)tmp << 24; + break; + } + + case SPI_BUS: + case SDIO_BUS: + sbaddr = (uint32)(uintptr)sii->curmap; + break; + + + default: + sbaddr = BADCOREADDR; + break; + } + + return sbaddr; +} + +uint +sb_corevendor(si_t *sih) +{ + si_info_t *sii; + sbconfig_t *sb; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + return ((R_SBREG(sii, &sb->sbidhigh) & SBIDH_VC_MASK) >> SBIDH_VC_SHIFT); +} + +uint +sb_corerev(si_t *sih) +{ + si_info_t *sii; + sbconfig_t *sb; + uint sbidh; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + sbidh = R_SBREG(sii, &sb->sbidhigh); + + return (SBCOREREV(sbidh)); +} + +/* set core-specific control flags */ +void +sb_core_cflags_wo(si_t *sih, uint32 mask, uint32 val) +{ + si_info_t *sii; + sbconfig_t *sb; + uint32 w; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + ASSERT((val & ~mask) == 0); + + /* mask and set */ + w = (R_SBREG(sii, &sb->sbtmstatelow) & ~(mask << SBTML_SICF_SHIFT)) | + (val << SBTML_SICF_SHIFT); + W_SBREG(sii, &sb->sbtmstatelow, w); +} + +/* set/clear core-specific control flags */ +uint32 +sb_core_cflags(si_t *sih, uint32 mask, uint32 val) +{ + si_info_t *sii; + sbconfig_t *sb; + uint32 w; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + ASSERT((val & ~mask) == 0); + + /* mask and set */ + if (mask || val) { + w = (R_SBREG(sii, &sb->sbtmstatelow) & ~(mask << SBTML_SICF_SHIFT)) | + (val << SBTML_SICF_SHIFT); + W_SBREG(sii, &sb->sbtmstatelow, w); + } + + /* return the new value + * for write operation, the following readback ensures the completion of write opration. + */ + return (R_SBREG(sii, &sb->sbtmstatelow) >> SBTML_SICF_SHIFT); +} + +/* set/clear core-specific status flags */ +uint32 +sb_core_sflags(si_t *sih, uint32 mask, uint32 val) +{ + si_info_t *sii; + sbconfig_t *sb; + uint32 w; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + ASSERT((val & ~mask) == 0); + ASSERT((mask & ~SISF_CORE_BITS) == 0); + + /* mask and set */ + if (mask || val) { + w = (R_SBREG(sii, &sb->sbtmstatehigh) & ~(mask << SBTMH_SISF_SHIFT)) | + (val << SBTMH_SISF_SHIFT); + W_SBREG(sii, &sb->sbtmstatehigh, w); + } + + /* return the new value */ + return (R_SBREG(sii, &sb->sbtmstatehigh) >> SBTMH_SISF_SHIFT); +} + +bool +sb_iscoreup(si_t *sih) +{ + si_info_t *sii; + sbconfig_t *sb; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + return ((R_SBREG(sii, &sb->sbtmstatelow) & + (SBTML_RESET | SBTML_REJ_MASK | (SICF_CLOCK_EN << SBTML_SICF_SHIFT))) == + (SICF_CLOCK_EN << SBTML_SICF_SHIFT)); +} + +/* + * Switch to 'coreidx', issue a single arbitrary 32bit register mask&set operation, + * switch back to the original core, and return the new value. + * + * When using the silicon backplane, no fidleing with interrupts or core switches are needed. + * + * Also, when using pci/pcie, we can optimize away the core switching for pci registers + * and (on newer pci cores) chipcommon registers. + */ +uint +sb_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val) +{ + uint origidx = 0; + uint32 *r = NULL; + uint w; + uint intr_val = 0; + bool fast = FALSE; + si_info_t *sii; + + sii = SI_INFO(sih); + + ASSERT(GOODIDX(coreidx)); + ASSERT(regoff < SI_CORE_SIZE); + ASSERT((val & ~mask) == 0); + + if (coreidx >= SI_MAXCORES) + return 0; + + if (BUSTYPE(sii->pub.bustype) == SI_BUS) { + /* If internal bus, we can always get at everything */ + fast = TRUE; + /* map if does not exist */ + if (!sii->regs[coreidx]) { + sii->regs[coreidx] = REG_MAP(sii->coresba[coreidx], + SI_CORE_SIZE); + ASSERT(GOODREGS(sii->regs[coreidx])); + } + r = (uint32 *)((uchar *)sii->regs[coreidx] + regoff); + } else if (BUSTYPE(sii->pub.bustype) == PCI_BUS) { + /* If pci/pcie, we can get at pci/pcie regs and on newer cores to chipc */ + + if ((sii->coreid[coreidx] == CC_CORE_ID) && SI_FAST(sii)) { + /* Chipc registers are mapped at 12KB */ + + fast = TRUE; + r = (uint32 *)((char *)sii->curmap + PCI_16KB0_CCREGS_OFFSET + regoff); + } else if (sii->pub.buscoreidx == coreidx) { + /* pci registers are at either in the last 2KB of an 8KB window + * or, in pcie and pci rev 13 at 8KB + */ + fast = TRUE; + if (SI_FAST(sii)) + r = (uint32 *)((char *)sii->curmap + + PCI_16KB0_PCIREGS_OFFSET + regoff); + else + r = (uint32 *)((char *)sii->curmap + + ((regoff >= SBCONFIGOFF) ? + PCI_BAR0_PCISBR_OFFSET : PCI_BAR0_PCIREGS_OFFSET) + + regoff); + } + } + + if (!fast) { + INTR_OFF(sii, intr_val); + + /* save current core index */ + origidx = si_coreidx(&sii->pub); + + /* switch core */ + r = (uint32*) ((uchar*)sb_setcoreidx(&sii->pub, coreidx) + regoff); + } + ASSERT(r != NULL); + + /* mask and set */ + if (mask || val) { + if (regoff >= SBCONFIGOFF) { + w = (R_SBREG(sii, r) & ~mask) | val; + W_SBREG(sii, r, w); + } else { + w = (R_REG(sii->osh, r) & ~mask) | val; + W_REG(sii->osh, r, w); + } + } + + /* readback */ + if (regoff >= SBCONFIGOFF) + w = R_SBREG(sii, r); + else { + if ((CHIPID(sii->pub.chip) == BCM5354_CHIP_ID) && + (coreidx == SI_CC_IDX) && + (regoff == OFFSETOF(chipcregs_t, watchdog))) { + w = val; + } else + w = R_REG(sii->osh, r); + } + + if (!fast) { + /* restore core index */ + if (origidx != coreidx) + sb_setcoreidx(&sii->pub, origidx); + + INTR_RESTORE(sii, intr_val); + } + + return (w); +} + +/* Scan the enumeration space to find all cores starting from the given + * bus 'sbba'. Append coreid and other info to the lists in 'si'. 'sba' + * is the default core address at chip POR time and 'regs' is the virtual + * address that the default core is mapped at. 'ncores' is the number of + * cores expected on bus 'sbba'. It returns the total number of cores + * starting from bus 'sbba', inclusive. + */ +#define SB_MAXBUSES 2 +static uint +_sb_scan(si_info_t *sii, uint32 sba, void *regs, uint bus, uint32 sbba, uint numcores) +{ + uint next; + uint ncc = 0; + uint i; + + if (bus >= SB_MAXBUSES) { + SI_ERROR(("_sb_scan: bus 0x%08x at level %d is too deep to scan\n", sbba, bus)); + return 0; + } + SI_MSG(("_sb_scan: scan bus 0x%08x assume %u cores\n", sbba, numcores)); + + /* Scan all cores on the bus starting from core 0. + * Core addresses must be contiguous on each bus. + */ + for (i = 0, next = sii->numcores; i < numcores && next < SB_BUS_MAXCORES; i++, next++) { + sii->coresba[next] = sbba + (i * SI_CORE_SIZE); + + /* keep and reuse the initial register mapping */ + if ((BUSTYPE(sii->pub.bustype) == SI_BUS) && (sii->coresba[next] == sba)) { + SI_VMSG(("_sb_scan: reuse mapped regs %p for core %u\n", regs, next)); + sii->regs[next] = regs; + } + + /* change core to 'next' and read its coreid */ + sii->curmap = _sb_setcoreidx(sii, next); + sii->curidx = next; + + sii->coreid[next] = sb_coreid(&sii->pub); + + /* core specific processing... */ + /* chipc provides # cores */ + if (sii->coreid[next] == CC_CORE_ID) { + chipcregs_t *cc = (chipcregs_t *)sii->curmap; + uint32 ccrev = sb_corerev(&sii->pub); + + /* determine numcores - this is the total # cores in the chip */ + if (((ccrev == 4) || (ccrev >= 6))) + numcores = (R_REG(sii->osh, &cc->chipid) & CID_CC_MASK) >> + CID_CC_SHIFT; + else { + /* Older chips */ + uint chip = CHIPID(sii->pub.chip); + + if (chip == BCM4306_CHIP_ID) /* < 4306c0 */ + numcores = 6; + else if (chip == BCM4704_CHIP_ID) + numcores = 9; + else if (chip == BCM5365_CHIP_ID) + numcores = 7; + else { + SI_ERROR(("sb_chip2numcores: unsupported chip 0x%x\n", + chip)); + ASSERT(0); + numcores = 1; + } + } + SI_VMSG(("_sb_scan: there are %u cores in the chip %s\n", numcores, + sii->pub.issim ? "QT" : "")); + } + /* scan bridged SB(s) and add results to the end of the list */ + else if (sii->coreid[next] == OCP_CORE_ID) { + sbconfig_t *sb = REGS2SB(sii->curmap); + uint32 nsbba = R_SBREG(sii, &sb->sbadmatch1); + uint nsbcc; + + sii->numcores = next + 1; + + if ((nsbba & 0xfff00000) != SI_ENUM_BASE) + continue; + nsbba &= 0xfffff000; + if (_sb_coreidx(sii, nsbba) != BADIDX) + continue; + + nsbcc = (R_SBREG(sii, &sb->sbtmstatehigh) & 0x000f0000) >> 16; + nsbcc = _sb_scan(sii, sba, regs, bus + 1, nsbba, nsbcc); + if (sbba == SI_ENUM_BASE) + numcores -= nsbcc; + ncc += nsbcc; + } + } + + SI_MSG(("_sb_scan: found %u cores on bus 0x%08x\n", i, sbba)); + + sii->numcores = i + ncc; + return sii->numcores; +} + +/* scan the sb enumerated space to identify all cores */ +void +sb_scan(si_t *sih, void *regs, uint devid) +{ + si_info_t *sii; + uint32 origsba; + sbconfig_t *sb; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + sii->pub.socirev = (R_SBREG(sii, &sb->sbidlow) & SBIDL_RV_MASK) >> SBIDL_RV_SHIFT; + + /* Save the current core info and validate it later till we know + * for sure what is good and what is bad. + */ + origsba = _sb_coresba(sii); + + /* scan all SB(s) starting from SI_ENUM_BASE */ + sii->numcores = _sb_scan(sii, origsba, regs, 0, SI_ENUM_BASE, 1); +} + +/* + * This function changes logical "focus" to the indicated core; + * must be called with interrupts off. + * Moreover, callers should keep interrupts off during switching out of and back to d11 core + */ +void * +sb_setcoreidx(si_t *sih, uint coreidx) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + + if (coreidx >= sii->numcores) + return (NULL); + + /* + * If the user has provided an interrupt mask enabled function, + * then assert interrupts are disabled before switching the core. + */ + ASSERT((sii->intrsenabled_fn == NULL) || !(*(sii)->intrsenabled_fn)((sii)->intr_arg)); + + sii->curmap = _sb_setcoreidx(sii, coreidx); + sii->curidx = coreidx; + + return (sii->curmap); +} + +/* This function changes the logical "focus" to the indicated core. + * Return the current core's virtual address. + */ +static void * +_sb_setcoreidx(si_info_t *sii, uint coreidx) +{ + uint32 sbaddr = sii->coresba[coreidx]; + void *regs; + + switch (BUSTYPE(sii->pub.bustype)) { + case SI_BUS: + /* map new one */ + if (!sii->regs[coreidx]) { + sii->regs[coreidx] = REG_MAP(sbaddr, SI_CORE_SIZE); + ASSERT(GOODREGS(sii->regs[coreidx])); + } + regs = sii->regs[coreidx]; + break; + + case PCI_BUS: + /* point bar0 window */ + OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, 4, sbaddr); + regs = sii->curmap; + break; + + case PCMCIA_BUS: { + uint8 tmp = (sbaddr >> 12) & 0x0f; + OSL_PCMCIA_WRITE_ATTR(sii->osh, PCMCIA_ADDR0, &tmp, 1); + tmp = (sbaddr >> 16) & 0xff; + OSL_PCMCIA_WRITE_ATTR(sii->osh, PCMCIA_ADDR1, &tmp, 1); + tmp = (sbaddr >> 24) & 0xff; + OSL_PCMCIA_WRITE_ATTR(sii->osh, PCMCIA_ADDR2, &tmp, 1); + regs = sii->curmap; + break; + } + case SPI_BUS: + case SDIO_BUS: + /* map new one */ + if (!sii->regs[coreidx]) { + sii->regs[coreidx] = (void *)(uintptr)sbaddr; + ASSERT(GOODREGS(sii->regs[coreidx])); + } + regs = sii->regs[coreidx]; + break; + + + default: + ASSERT(0); + regs = NULL; + break; + } + + return regs; +} + +/* Return the address of sbadmatch0/1/2/3 register */ +static volatile uint32 * +sb_admatch(si_info_t *sii, uint asidx) +{ + sbconfig_t *sb; + volatile uint32 *addrm; + + sb = REGS2SB(sii->curmap); + + switch (asidx) { + case 0: + addrm = &sb->sbadmatch0; + break; + + case 1: + addrm = &sb->sbadmatch1; + break; + + case 2: + addrm = &sb->sbadmatch2; + break; + + case 3: + addrm = &sb->sbadmatch3; + break; + + default: + SI_ERROR(("%s: Address space index (%d) out of range\n", __FUNCTION__, asidx)); + return 0; + } + + return (addrm); +} + +/* Return the number of address spaces in current core */ +int +sb_numaddrspaces(si_t *sih) +{ + si_info_t *sii; + sbconfig_t *sb; + + sii = SI_INFO(sih); + sb = REGS2SB(sii->curmap); + + /* + 1 because of enumeration space */ + return ((R_SBREG(sii, &sb->sbidlow) & SBIDL_AR_MASK) >> SBIDL_AR_SHIFT) + 1; +} + +/* Return the address of the nth address space in the current core */ +uint32 +sb_addrspace(si_t *sih, uint asidx) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + + return (sb_base(R_SBREG(sii, sb_admatch(sii, asidx)))); +} + +/* Return the size of the nth address space in the current core */ +uint32 +sb_addrspacesize(si_t *sih, uint asidx) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + + return (sb_size(R_SBREG(sii, sb_admatch(sii, asidx)))); +} + + +/* do buffered registers update */ +void +sb_commit(si_t *sih) +{ + si_info_t *sii; + uint origidx; + uint intr_val = 0; + + sii = SI_INFO(sih); + + origidx = sii->curidx; + ASSERT(GOODIDX(origidx)); + + INTR_OFF(sii, intr_val); + + /* switch over to chipcommon core if there is one, else use pci */ + if (sii->pub.ccrev != NOREV) { + chipcregs_t *ccregs = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); + ASSERT(ccregs != NULL); + + /* do the buffer registers update */ + W_REG(sii->osh, &ccregs->broadcastaddress, SB_COMMIT); + W_REG(sii->osh, &ccregs->broadcastdata, 0x0); + } else + ASSERT(0); + + /* restore core index */ + sb_setcoreidx(sih, origidx); + INTR_RESTORE(sii, intr_val); +} + +void +sb_core_disable(si_t *sih, uint32 bits) +{ + si_info_t *sii; + volatile uint32 dummy; + sbconfig_t *sb; + + sii = SI_INFO(sih); + + ASSERT(GOODREGS(sii->curmap)); + sb = REGS2SB(sii->curmap); + + /* if core is already in reset, just return */ + if (R_SBREG(sii, &sb->sbtmstatelow) & SBTML_RESET) + return; + + /* if clocks are not enabled, put into reset and return */ + if ((R_SBREG(sii, &sb->sbtmstatelow) & (SICF_CLOCK_EN << SBTML_SICF_SHIFT)) == 0) + goto disable; + + /* set target reject and spin until busy is clear (preserve core-specific bits) */ + OR_SBREG(sii, &sb->sbtmstatelow, SBTML_REJ); + dummy = R_SBREG(sii, &sb->sbtmstatelow); + OSL_DELAY(1); + SPINWAIT((R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_BUSY), 100000); + if (R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_BUSY) + SI_ERROR(("%s: target state still busy\n", __FUNCTION__)); + + if (R_SBREG(sii, &sb->sbidlow) & SBIDL_INIT) { + OR_SBREG(sii, &sb->sbimstate, SBIM_RJ); + dummy = R_SBREG(sii, &sb->sbimstate); + OSL_DELAY(1); + SPINWAIT((R_SBREG(sii, &sb->sbimstate) & SBIM_BY), 100000); + } + + /* set reset and reject while enabling the clocks */ + W_SBREG(sii, &sb->sbtmstatelow, + (((bits | SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) | + SBTML_REJ | SBTML_RESET)); + dummy = R_SBREG(sii, &sb->sbtmstatelow); + OSL_DELAY(10); + + /* don't forget to clear the initiator reject bit */ + if (R_SBREG(sii, &sb->sbidlow) & SBIDL_INIT) + AND_SBREG(sii, &sb->sbimstate, ~SBIM_RJ); + +disable: + /* leave reset and reject asserted */ + W_SBREG(sii, &sb->sbtmstatelow, ((bits << SBTML_SICF_SHIFT) | SBTML_REJ | SBTML_RESET)); + OSL_DELAY(1); +} + +/* reset and re-enable a core + * inputs: + * bits - core specific bits that are set during and after reset sequence + * resetbits - core specific bits that are set only during reset sequence + */ +void +sb_core_reset(si_t *sih, uint32 bits, uint32 resetbits) +{ + si_info_t *sii; + sbconfig_t *sb; + volatile uint32 dummy; + + sii = SI_INFO(sih); + ASSERT(GOODREGS(sii->curmap)); + sb = REGS2SB(sii->curmap); + + /* + * Must do the disable sequence first to work for arbitrary current core state. + */ + sb_core_disable(sih, (bits | resetbits)); + + /* + * Now do the initialization sequence. + */ + + /* set reset while enabling the clock and forcing them on throughout the core */ + W_SBREG(sii, &sb->sbtmstatelow, + (((bits | resetbits | SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) | + SBTML_RESET)); + dummy = R_SBREG(sii, &sb->sbtmstatelow); + OSL_DELAY(1); + + if (R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_SERR) { + W_SBREG(sii, &sb->sbtmstatehigh, 0); + } + if ((dummy = R_SBREG(sii, &sb->sbimstate)) & (SBIM_IBE | SBIM_TO)) { + AND_SBREG(sii, &sb->sbimstate, ~(SBIM_IBE | SBIM_TO)); + } + + /* clear reset and allow it to propagate throughout the core */ + W_SBREG(sii, &sb->sbtmstatelow, + ((bits | resetbits | SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT)); + dummy = R_SBREG(sii, &sb->sbtmstatelow); + OSL_DELAY(1); + + /* leave clock enabled */ + W_SBREG(sii, &sb->sbtmstatelow, ((bits | SICF_CLOCK_EN) << SBTML_SICF_SHIFT)); + dummy = R_SBREG(sii, &sb->sbtmstatelow); + OSL_DELAY(1); +} + +/* + * Set the initiator timeout for the "master core". + * The master core is defined to be the core in control + * of the chip and so it issues accesses to non-memory + * locations (Because of dma *any* core can access memeory). + * + * The routine uses the bus to decide who is the master: + * SI_BUS => mips + * JTAG_BUS => chipc + * PCI_BUS => pci or pcie + * PCMCIA_BUS => pcmcia + * SDIO_BUS => pcmcia + * + * This routine exists so callers can disable initiator + * timeouts so accesses to very slow devices like otp + * won't cause an abort. The routine allows arbitrary + * settings of the service and request timeouts, though. + * + * Returns the timeout state before changing it or -1 + * on error. + */ + +#define TO_MASK (SBIMCL_RTO_MASK | SBIMCL_STO_MASK) + +uint32 +sb_set_initiator_to(si_t *sih, uint32 to, uint idx) +{ + si_info_t *sii; + uint origidx; + uint intr_val = 0; + uint32 tmp, ret = 0xffffffff; + sbconfig_t *sb; + + sii = SI_INFO(sih); + + if ((to & ~TO_MASK) != 0) + return ret; + + /* Figure out the master core */ + if (idx == BADIDX) { + switch (BUSTYPE(sii->pub.bustype)) { + case PCI_BUS: + idx = sii->pub.buscoreidx; + break; + case JTAG_BUS: + idx = SI_CC_IDX; + break; + case PCMCIA_BUS: + case SDIO_BUS: + idx = si_findcoreidx(sih, PCMCIA_CORE_ID, 0); + break; + case SI_BUS: + idx = si_findcoreidx(sih, MIPS33_CORE_ID, 0); + break; + default: + ASSERT(0); + } + if (idx == BADIDX) + return ret; + } + + INTR_OFF(sii, intr_val); + origidx = si_coreidx(sih); + + sb = REGS2SB(sb_setcoreidx(sih, idx)); + + tmp = R_SBREG(sii, &sb->sbimconfiglow); + ret = tmp & TO_MASK; + W_SBREG(sii, &sb->sbimconfiglow, (tmp & ~TO_MASK) | to); + + sb_commit(sih); + sb_setcoreidx(sih, origidx); + INTR_RESTORE(sii, intr_val); + return ret; +} + +uint32 +sb_base(uint32 admatch) +{ + uint32 base; + uint type; + + type = admatch & SBAM_TYPE_MASK; + ASSERT(type < 3); + + base = 0; + + if (type == 0) { + base = admatch & SBAM_BASE0_MASK; + } else if (type == 1) { + ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ + base = admatch & SBAM_BASE1_MASK; + } else if (type == 2) { + ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ + base = admatch & SBAM_BASE2_MASK; + } + + return (base); +} + +uint32 +sb_size(uint32 admatch) +{ + uint32 size; + uint type; + + type = admatch & SBAM_TYPE_MASK; + ASSERT(type < 3); + + size = 0; + + if (type == 0) { + size = 1 << (((admatch & SBAM_ADINT0_MASK) >> SBAM_ADINT0_SHIFT) + 1); + } else if (type == 1) { + ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ + size = 1 << (((admatch & SBAM_ADINT1_MASK) >> SBAM_ADINT1_SHIFT) + 1); + } else if (type == 2) { + ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ + size = 1 << (((admatch & SBAM_ADINT2_MASK) >> SBAM_ADINT2_SHIFT) + 1); + } + + return (size); +} diff --git a/drivers/net/wireless/bcmdhd/siutils.c b/drivers/net/wireless/bcmdhd/siutils.c new file mode 100644 index 0000000000000..a655ac4ef1411 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/siutils.c @@ -0,0 +1,1915 @@ +/* + * Misc utility routines for accessing chip-specific features + * of the SiliconBackplane-based Broadcom chips. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: siutils.c,v 1.813.2.36 2011-02-10 23:43:55 $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "siutils_priv.h" + +/* local prototypes */ +static si_info_t *si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs, + uint bustype, void *sdh, char **vars, uint *varsz); +static bool si_buscore_prep(si_info_t *sii, uint bustype, uint devid, void *sdh); +static bool si_buscore_setup(si_info_t *sii, chipcregs_t *cc, uint bustype, uint32 savewin, + uint *origidx, void *regs); + + +/* global variable to indicate reservation/release of gpio's */ +static uint32 si_gpioreservation = 0; + +/* global flag to prevent shared resources from being initialized multiple times in si_attach() */ + +/* + * Allocate a si handle. + * devid - pci device id (used to determine chip#) + * osh - opaque OS handle + * regs - virtual address of initial core registers + * bustype - pci/pcmcia/sb/sdio/etc + * vars - pointer to a pointer area for "environment" variables + * varsz - pointer to int to return the size of the vars + */ +si_t * +si_attach(uint devid, osl_t *osh, void *regs, + uint bustype, void *sdh, char **vars, uint *varsz) +{ + si_info_t *sii; + + /* alloc si_info_t */ + if ((sii = MALLOC(osh, sizeof (si_info_t))) == NULL) { + SI_ERROR(("si_attach: malloc failed! malloced %d bytes\n", MALLOCED(osh))); + return (NULL); + } + + if (si_doattach(sii, devid, osh, regs, bustype, sdh, vars, varsz) == NULL) { + MFREE(osh, sii, sizeof(si_info_t)); + return (NULL); + } + sii->vars = vars ? *vars : NULL; + sii->varsz = varsz ? *varsz : 0; + + return (si_t *)sii; +} + +/* global kernel resource */ +static si_info_t ksii; + +static uint32 wd_msticks; /* watchdog timer ticks normalized to ms */ + +/* generic kernel variant of si_attach() */ +si_t * +si_kattach(osl_t *osh) +{ + static bool ksii_attached = FALSE; + + if (!ksii_attached) { + void *regs; + regs = REG_MAP(SI_ENUM_BASE, SI_CORE_SIZE); + + if (si_doattach(&ksii, BCM4710_DEVICE_ID, osh, regs, + SI_BUS, NULL, + osh != SI_OSH ? &ksii.vars : NULL, + osh != SI_OSH ? &ksii.varsz : NULL) == NULL) { + SI_ERROR(("si_kattach: si_doattach failed\n")); + REG_UNMAP(regs); + return NULL; + } + REG_UNMAP(regs); + + /* save ticks normalized to ms for si_watchdog_ms() */ + if (PMUCTL_ENAB(&ksii.pub)) { + /* based on 32KHz ILP clock */ + wd_msticks = 32; + } else { + wd_msticks = ALP_CLOCK / 1000; + } + + ksii_attached = TRUE; + SI_MSG(("si_kattach done. ccrev = %d, wd_msticks = %d\n", + ksii.pub.ccrev, wd_msticks)); + } + + return &ksii.pub; +} + + +static bool +si_buscore_prep(si_info_t *sii, uint bustype, uint devid, void *sdh) +{ + /* need to set memseg flag for CF card first before any sb registers access */ + if (BUSTYPE(bustype) == PCMCIA_BUS) + sii->memseg = TRUE; + + + if (BUSTYPE(bustype) == SDIO_BUS) { + int err; + uint8 clkset; + + /* Try forcing SDIO core to do ALPAvail request only */ + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); + if (!err) { + uint8 clkval; + + /* If register supported, wait for ALPAvail and then force ALP */ + clkval = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, NULL); + if ((clkval & ~SBSDIO_AVBITS) == clkset) { + SPINWAIT(((clkval = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, + SBSDIO_FUNC1_CHIPCLKCSR, NULL)), !SBSDIO_ALPAV(clkval)), + PMU_MAX_TRANSITION_DLY); + if (!SBSDIO_ALPAV(clkval)) { + SI_ERROR(("timeout on ALPAV wait, clkval 0x%02x\n", + clkval)); + return FALSE; + } + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, + clkset, &err); + OSL_DELAY(65); + } + } + + /* Also, disable the extra SDIO pull-ups */ + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); + } + + + return TRUE; +} + +static bool +si_buscore_setup(si_info_t *sii, chipcregs_t *cc, uint bustype, uint32 savewin, + uint *origidx, void *regs) +{ + bool pci, pcie; + uint i; + uint pciidx, pcieidx, pcirev, pcierev; + + cc = si_setcoreidx(&sii->pub, SI_CC_IDX); + ASSERT((uintptr)cc); + + /* get chipcommon rev */ + sii->pub.ccrev = (int)si_corerev(&sii->pub); + + /* get chipcommon chipstatus */ + if (sii->pub.ccrev >= 11) + sii->pub.chipst = R_REG(sii->osh, &cc->chipstatus); + + /* get chipcommon capabilites */ + sii->pub.cccaps = R_REG(sii->osh, &cc->capabilities); + /* get chipcommon extended capabilities */ + + if (sii->pub.ccrev >= 35) + sii->pub.cccaps_ext = R_REG(sii->osh, &cc->capabilities_ext); + + /* get pmu rev and caps */ + if (sii->pub.cccaps & CC_CAP_PMU) { + sii->pub.pmucaps = R_REG(sii->osh, &cc->pmucapabilities); + sii->pub.pmurev = sii->pub.pmucaps & PCAP_REV_MASK; + } + + SI_MSG(("Chipc: rev %d, caps 0x%x, chipst 0x%x pmurev %d, pmucaps 0x%x\n", + sii->pub.ccrev, sii->pub.cccaps, sii->pub.chipst, sii->pub.pmurev, + sii->pub.pmucaps)); + + /* figure out bus/orignal core idx */ + sii->pub.buscoretype = NODEV_CORE_ID; + sii->pub.buscorerev = (uint)NOREV; + sii->pub.buscoreidx = BADIDX; + + pci = pcie = FALSE; + pcirev = pcierev = (uint)NOREV; + pciidx = pcieidx = BADIDX; + + for (i = 0; i < sii->numcores; i++) { + uint cid, crev; + + si_setcoreidx(&sii->pub, i); + cid = si_coreid(&sii->pub); + crev = si_corerev(&sii->pub); + + /* Display cores found */ + SI_VMSG(("CORE[%d]: id 0x%x rev %d base 0x%x regs 0x%p\n", + i, cid, crev, sii->coresba[i], sii->regs[i])); + + if (BUSTYPE(bustype) == PCI_BUS) { + if (cid == PCI_CORE_ID) { + pciidx = i; + pcirev = crev; + pci = TRUE; + } else if (cid == PCIE_CORE_ID) { + pcieidx = i; + pcierev = crev; + pcie = TRUE; + } + } else if ((BUSTYPE(bustype) == PCMCIA_BUS) && + (cid == PCMCIA_CORE_ID)) { + sii->pub.buscorerev = crev; + sii->pub.buscoretype = cid; + sii->pub.buscoreidx = i; + } + else if (((BUSTYPE(bustype) == SDIO_BUS) || + (BUSTYPE(bustype) == SPI_BUS)) && + ((cid == PCMCIA_CORE_ID) || + (cid == SDIOD_CORE_ID))) { + sii->pub.buscorerev = crev; + sii->pub.buscoretype = cid; + sii->pub.buscoreidx = i; + } + + /* find the core idx before entering this func. */ + if ((savewin && (savewin == sii->coresba[i])) || + (regs == sii->regs[i])) + *origidx = i; + } + + if (pci) { + sii->pub.buscoretype = PCI_CORE_ID; + sii->pub.buscorerev = pcirev; + sii->pub.buscoreidx = pciidx; + } else if (pcie) { + sii->pub.buscoretype = PCIE_CORE_ID; + sii->pub.buscorerev = pcierev; + sii->pub.buscoreidx = pcieidx; + } + + SI_VMSG(("Buscore id/type/rev %d/0x%x/%d\n", sii->pub.buscoreidx, sii->pub.buscoretype, + sii->pub.buscorerev)); + + if (BUSTYPE(sii->pub.bustype) == SI_BUS && (CHIPID(sii->pub.chip) == BCM4712_CHIP_ID) && + (sii->pub.chippkg != BCM4712LARGE_PKG_ID) && (CHIPREV(sii->pub.chiprev) <= 3)) + OR_REG(sii->osh, &cc->slow_clk_ctl, SCC_SS_XTAL); + + + /* Make sure any on-chip ARM is off (in case strapping is wrong), or downloaded code was + * already running. + */ + if ((BUSTYPE(bustype) == SDIO_BUS) || (BUSTYPE(bustype) == SPI_BUS)) { + if (si_setcore(&sii->pub, ARM7S_CORE_ID, 0) || + si_setcore(&sii->pub, ARMCM3_CORE_ID, 0)) + si_core_disable(&sii->pub, 0); + } + + /* return to the original core */ + si_setcoreidx(&sii->pub, *origidx); + + return TRUE; +} + + + +static si_info_t * +si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs, + uint bustype, void *sdh, char **vars, uint *varsz) +{ + struct si_pub *sih = &sii->pub; + uint32 w, savewin; + chipcregs_t *cc; + char *pvars = NULL; + uint origidx; + + ASSERT(GOODREGS(regs)); + + bzero((uchar*)sii, sizeof(si_info_t)); + + savewin = 0; + + sih->buscoreidx = BADIDX; + + sii->curmap = regs; + sii->sdh = sdh; + sii->osh = osh; + + + + /* find Chipcommon address */ + if (bustype == PCI_BUS) { + savewin = OSL_PCI_READ_CONFIG(sii->osh, PCI_BAR0_WIN, sizeof(uint32)); + if (!GOODCOREADDR(savewin, SI_ENUM_BASE)) + savewin = SI_ENUM_BASE; + OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, 4, SI_ENUM_BASE); + cc = (chipcregs_t *)regs; + } else if ((bustype == SDIO_BUS) || (bustype == SPI_BUS)) { + cc = (chipcregs_t *)sii->curmap; + } else { + cc = (chipcregs_t *)REG_MAP(SI_ENUM_BASE, SI_CORE_SIZE); + } + + sih->bustype = bustype; + if (bustype != BUSTYPE(bustype)) { + SI_ERROR(("si_doattach: bus type %d does not match configured bus type %d\n", + bustype, BUSTYPE(bustype))); + return NULL; + } + + /* bus/core/clk setup for register access */ + if (!si_buscore_prep(sii, bustype, devid, sdh)) { + SI_ERROR(("si_doattach: si_core_clk_prep failed %d\n", bustype)); + return NULL; + } + + /* ChipID recognition. + * We assume we can read chipid at offset 0 from the regs arg. + * If we add other chiptypes (or if we need to support old sdio hosts w/o chipcommon), + * some way of recognizing them needs to be added here. + */ + w = R_REG(osh, &cc->chipid); + sih->socitype = (w & CID_TYPE_MASK) >> CID_TYPE_SHIFT; + /* Might as wll fill in chip id rev & pkg */ + sih->chip = w & CID_ID_MASK; + sih->chiprev = (w & CID_REV_MASK) >> CID_REV_SHIFT; + sih->chippkg = (w & CID_PKG_MASK) >> CID_PKG_SHIFT; + if (CHIPID(sih->chip) == BCM4322_CHIP_ID && (((sih->chipst & CST4322_SPROM_OTP_SEL_MASK) + >> CST4322_SPROM_OTP_SEL_SHIFT) == (CST4322_OTP_PRESENT | + CST4322_SPROM_PRESENT))) { + SI_ERROR(("%s: Invalid setting: both SPROM and OTP strapped.\n", __FUNCTION__)); + return NULL; + } + +#if defined(HW_OOB) + if (CHIPID(sih->chip) == BCM43362_CHIP_ID) { + uint32 gpiocontrol, addr; + addr = SI_ENUM_BASE + OFFSETOF(chipcregs_t, gpiocontrol); + gpiocontrol = bcmsdh_reg_read(sdh, addr, 4); + gpiocontrol |= 0x2; + bcmsdh_reg_write(sdh, addr, 4, gpiocontrol); + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, 0x10005, 0xf, NULL); + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, 0x10006, 0x0, NULL); + bcmsdh_cfg_write(sdh, SDIO_FUNC_1, 0x10007, 0x2, NULL); + } +#endif + + if ((CHIPID(sih->chip) == BCM4329_CHIP_ID) && (sih->chiprev == 0) && + (sih->chippkg != BCM4329_289PIN_PKG_ID)) { + sih->chippkg = BCM4329_182PIN_PKG_ID; + } + + sih->issim = IS_SIM(sih->chippkg); + + /* scan for cores */ + if (CHIPTYPE(sii->pub.socitype) == SOCI_SB) { + SI_MSG(("Found chip type SB (0x%08x)\n", w)); + sb_scan(&sii->pub, regs, devid); + } else if (CHIPTYPE(sii->pub.socitype) == SOCI_AI) { + SI_MSG(("Found chip type AI (0x%08x)\n", w)); + /* pass chipc address instead of original core base */ + ai_scan(&sii->pub, (void *)(uintptr)cc, devid); + } else if (CHIPTYPE(sii->pub.socitype) == SOCI_UBUS) { + SI_MSG(("Found chip type UBUS (0x%08x), chip id = 0x%4x\n", w, sih->chip)); + /* pass chipc address instead of original core base */ + ub_scan(&sii->pub, (void *)(uintptr)cc, devid); + } else { + SI_ERROR(("Found chip of unknown type (0x%08x)\n", w)); + return NULL; + } + /* no cores found, bail out */ + if (sii->numcores == 0) { + SI_ERROR(("si_doattach: could not find any cores\n")); + return NULL; + } + /* bus/core/clk setup */ + origidx = SI_CC_IDX; + if (!si_buscore_setup(sii, cc, bustype, savewin, &origidx, regs)) { + SI_ERROR(("si_doattach: si_buscore_setup failed\n")); + goto exit; + } + + /* assume current core is CC */ + if ((sii->pub.ccrev == 0x25) && ((CHIPID(sih->chip) == BCM43234_CHIP_ID || + CHIPID(sih->chip) == BCM43235_CHIP_ID || + CHIPID(sih->chip) == BCM43236_CHIP_ID || + CHIPID(sih->chip) == BCM43238_CHIP_ID) && + (CHIPREV(sii->pub.chiprev) == 0))) { + + if ((cc->chipstatus & CST43236_BP_CLK) != 0) { + uint clkdiv; + clkdiv = R_REG(osh, &cc->clkdiv); + /* otp_clk_div is even number, 120/14 < 9mhz */ + clkdiv = (clkdiv & ~CLKD_OTP) | (14 << CLKD_OTP_SHIFT); + W_REG(osh, &cc->clkdiv, clkdiv); + SI_ERROR(("%s: set clkdiv to %x\n", __FUNCTION__, clkdiv)); + } + OSL_DELAY(10); + } + + + pvars = NULL; + + + + if (sii->pub.ccrev >= 20) { + cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); + ASSERT(cc != NULL); + W_REG(osh, &cc->gpiopullup, 0); + W_REG(osh, &cc->gpiopulldown, 0); + si_setcoreidx(sih, origidx); + } + + + + + return (sii); + +exit: + + return NULL; +} + +/* may be called with core in reset */ +void +si_detach(si_t *sih) +{ + si_info_t *sii; + uint idx; + + + sii = SI_INFO(sih); + + if (sii == NULL) + return; + + if (BUSTYPE(sih->bustype) == SI_BUS) + for (idx = 0; idx < SI_MAXCORES; idx++) + if (sii->regs[idx]) { + REG_UNMAP(sii->regs[idx]); + sii->regs[idx] = NULL; + } + + + +#if !defined(BCMBUSTYPE) || (BCMBUSTYPE == SI_BUS) + if (sii != &ksii) +#endif /* !BCMBUSTYPE || (BCMBUSTYPE == SI_BUS) */ + MFREE(sii->osh, sii, sizeof(si_info_t)); +} + +void * +si_osh(si_t *sih) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + return sii->osh; +} + +void +si_setosh(si_t *sih, osl_t *osh) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + if (sii->osh != NULL) { + SI_ERROR(("osh is already set....\n")); + ASSERT(!sii->osh); + } + sii->osh = osh; +} + +/* register driver interrupt disabling and restoring callback functions */ +void +si_register_intr_callback(si_t *sih, void *intrsoff_fn, void *intrsrestore_fn, + void *intrsenabled_fn, void *intr_arg) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + sii->intr_arg = intr_arg; + sii->intrsoff_fn = (si_intrsoff_t)intrsoff_fn; + sii->intrsrestore_fn = (si_intrsrestore_t)intrsrestore_fn; + sii->intrsenabled_fn = (si_intrsenabled_t)intrsenabled_fn; + /* save current core id. when this function called, the current core + * must be the core which provides driver functions(il, et, wl, etc.) + */ + sii->dev_coreid = sii->coreid[sii->curidx]; +} + +void +si_deregister_intr_callback(si_t *sih) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + sii->intrsoff_fn = NULL; +} + +uint +si_intflag(si_t *sih) +{ + si_info_t *sii = SI_INFO(sih); + + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_intflag(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return R_REG(sii->osh, ((uint32 *)(uintptr) + (sii->oob_router + OOB_STATUSA))); + else { + ASSERT(0); + return 0; + } +} + +uint +si_flag(si_t *sih) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_flag(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_flag(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_flag(sih); + else { + ASSERT(0); + return 0; + } +} + +void +si_setint(si_t *sih, int siflag) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + sb_setint(sih, siflag); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + ai_setint(sih, siflag); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + ub_setint(sih, siflag); + else + ASSERT(0); +} + +uint +si_coreid(si_t *sih) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + return sii->coreid[sii->curidx]; +} + +uint +si_coreidx(si_t *sih) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + return sii->curidx; +} + +/* return the core-type instantiation # of the current core */ +uint +si_coreunit(si_t *sih) +{ + si_info_t *sii; + uint idx; + uint coreid; + uint coreunit; + uint i; + + sii = SI_INFO(sih); + coreunit = 0; + + idx = sii->curidx; + + ASSERT(GOODREGS(sii->curmap)); + coreid = si_coreid(sih); + + /* count the cores of our type */ + for (i = 0; i < idx; i++) + if (sii->coreid[i] == coreid) + coreunit++; + + return (coreunit); +} + +uint +si_corevendor(si_t *sih) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_corevendor(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_corevendor(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_corevendor(sih); + else { + ASSERT(0); + return 0; + } +} + +bool +si_backplane64(si_t *sih) +{ + return ((sih->cccaps & CC_CAP_BKPLN64) != 0); +} + +uint +si_corerev(si_t *sih) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_corerev(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_corerev(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_corerev(sih); + else { + ASSERT(0); + return 0; + } +} + +/* return index of coreid or BADIDX if not found */ +uint +si_findcoreidx(si_t *sih, uint coreid, uint coreunit) +{ + si_info_t *sii; + uint found; + uint i; + + sii = SI_INFO(sih); + + found = 0; + + for (i = 0; i < sii->numcores; i++) + if (sii->coreid[i] == coreid) { + if (found == coreunit) + return (i); + found++; + } + + return (BADIDX); +} + +/* return list of found cores */ +uint +si_corelist(si_t *sih, uint coreid[]) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + + bcopy((uchar*)sii->coreid, (uchar*)coreid, (sii->numcores * sizeof(uint))); + return (sii->numcores); +} + +/* return current register mapping */ +void * +si_coreregs(si_t *sih) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + ASSERT(GOODREGS(sii->curmap)); + + return (sii->curmap); +} + +/* + * This function changes logical "focus" to the indicated core; + * must be called with interrupts off. + * Moreover, callers should keep interrupts off during switching out of and back to d11 core + */ +void * +si_setcore(si_t *sih, uint coreid, uint coreunit) +{ + uint idx; + + idx = si_findcoreidx(sih, coreid, coreunit); + if (!GOODIDX(idx)) + return (NULL); + + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_setcoreidx(sih, idx); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_setcoreidx(sih, idx); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_setcoreidx(sih, idx); + else { + ASSERT(0); + return NULL; + } +} + +void * +si_setcoreidx(si_t *sih, uint coreidx) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_setcoreidx(sih, coreidx); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_setcoreidx(sih, coreidx); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_setcoreidx(sih, coreidx); + else { + ASSERT(0); + return NULL; + } +} + +/* Turn off interrupt as required by sb_setcore, before switch core */ +void * +si_switch_core(si_t *sih, uint coreid, uint *origidx, uint *intr_val) +{ + void *cc; + si_info_t *sii; + + sii = SI_INFO(sih); + + if (SI_FAST(sii)) { + /* Overloading the origidx variable to remember the coreid, + * this works because the core ids cannot be confused with + * core indices. + */ + *origidx = coreid; + if (coreid == CC_CORE_ID) + return (void *)CCREGS_FAST(sii); + else if (coreid == sih->buscoretype) + return (void *)PCIEREGS(sii); + } + INTR_OFF(sii, *intr_val); + *origidx = sii->curidx; + cc = si_setcore(sih, coreid, 0); + ASSERT(cc != NULL); + + return cc; +} + +/* restore coreidx and restore interrupt */ +void +si_restore_core(si_t *sih, uint coreid, uint intr_val) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + if (SI_FAST(sii) && ((coreid == CC_CORE_ID) || (coreid == sih->buscoretype))) + return; + + si_setcoreidx(sih, coreid); + INTR_RESTORE(sii, intr_val); +} + +int +si_numaddrspaces(si_t *sih) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_numaddrspaces(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_numaddrspaces(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_numaddrspaces(sih); + else { + ASSERT(0); + return 0; + } +} + +uint32 +si_addrspace(si_t *sih, uint asidx) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_addrspace(sih, asidx); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_addrspace(sih, asidx); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_addrspace(sih, asidx); + else { + ASSERT(0); + return 0; + } +} + +uint32 +si_addrspacesize(si_t *sih, uint asidx) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_addrspacesize(sih, asidx); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_addrspacesize(sih, asidx); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_addrspacesize(sih, asidx); + else { + ASSERT(0); + return 0; + } +} + +uint32 +si_core_cflags(si_t *sih, uint32 mask, uint32 val) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_core_cflags(sih, mask, val); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_core_cflags(sih, mask, val); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_core_cflags(sih, mask, val); + else { + ASSERT(0); + return 0; + } +} + +void +si_core_cflags_wo(si_t *sih, uint32 mask, uint32 val) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + sb_core_cflags_wo(sih, mask, val); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + ai_core_cflags_wo(sih, mask, val); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + ub_core_cflags_wo(sih, mask, val); + else + ASSERT(0); +} + +uint32 +si_core_sflags(si_t *sih, uint32 mask, uint32 val) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_core_sflags(sih, mask, val); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_core_sflags(sih, mask, val); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_core_sflags(sih, mask, val); + else { + ASSERT(0); + return 0; + } +} + +bool +si_iscoreup(si_t *sih) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_iscoreup(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_iscoreup(sih); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_iscoreup(sih); + else { + ASSERT(0); + return FALSE; + } +} + +uint +si_wrapperreg(si_t *sih, uint32 offset, uint32 mask, uint32 val) +{ + /* only for AI back plane chips */ + if (CHIPTYPE(sih->socitype) == SOCI_AI) + return (ai_wrap_reg(sih, offset, mask, val)); + return 0; +} + +uint +si_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + return sb_corereg(sih, coreidx, regoff, mask, val); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + return ai_corereg(sih, coreidx, regoff, mask, val); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + return ub_corereg(sih, coreidx, regoff, mask, val); + else { + ASSERT(0); + return 0; + } +} + +void +si_core_disable(si_t *sih, uint32 bits) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + sb_core_disable(sih, bits); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + ai_core_disable(sih, bits); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + ub_core_disable(sih, bits); +} + +void +si_core_reset(si_t *sih, uint32 bits, uint32 resetbits) +{ + if (CHIPTYPE(sih->socitype) == SOCI_SB) + sb_core_reset(sih, bits, resetbits); + else if (CHIPTYPE(sih->socitype) == SOCI_AI) + ai_core_reset(sih, bits, resetbits); + else if (CHIPTYPE(sih->socitype) == SOCI_UBUS) + ub_core_reset(sih, bits, resetbits); +} + +/* Run bist on current core. Caller needs to take care of core-specific bist hazards */ +int +si_corebist(si_t *sih) +{ + uint32 cflags; + int result = 0; + + /* Read core control flags */ + cflags = si_core_cflags(sih, 0, 0); + + /* Set bist & fgc */ + si_core_cflags(sih, ~0, (SICF_BIST_EN | SICF_FGC)); + + /* Wait for bist done */ + SPINWAIT(((si_core_sflags(sih, 0, 0) & SISF_BIST_DONE) == 0), 100000); + + if (si_core_sflags(sih, 0, 0) & SISF_BIST_ERROR) + result = BCME_ERROR; + + /* Reset core control flags */ + si_core_cflags(sih, 0xffff, cflags); + + return result; +} + +static uint32 +factor6(uint32 x) +{ + switch (x) { + case CC_F6_2: return 2; + case CC_F6_3: return 3; + case CC_F6_4: return 4; + case CC_F6_5: return 5; + case CC_F6_6: return 6; + case CC_F6_7: return 7; + default: return 0; + } +} + +/* calculate the speed the SI would run at given a set of clockcontrol values */ +uint32 +si_clock_rate(uint32 pll_type, uint32 n, uint32 m) +{ + uint32 n1, n2, clock, m1, m2, m3, mc; + + n1 = n & CN_N1_MASK; + n2 = (n & CN_N2_MASK) >> CN_N2_SHIFT; + + if (pll_type == PLL_TYPE6) { + if (m & CC_T6_MMASK) + return CC_T6_M1; + else + return CC_T6_M0; + } else if ((pll_type == PLL_TYPE1) || + (pll_type == PLL_TYPE3) || + (pll_type == PLL_TYPE4) || + (pll_type == PLL_TYPE7)) { + n1 = factor6(n1); + n2 += CC_F5_BIAS; + } else if (pll_type == PLL_TYPE2) { + n1 += CC_T2_BIAS; + n2 += CC_T2_BIAS; + ASSERT((n1 >= 2) && (n1 <= 7)); + ASSERT((n2 >= 5) && (n2 <= 23)); + } else if (pll_type == PLL_TYPE5) { + return (100000000); + } else + ASSERT(0); + /* PLL types 3 and 7 use BASE2 (25Mhz) */ + if ((pll_type == PLL_TYPE3) || + (pll_type == PLL_TYPE7)) { + clock = CC_CLOCK_BASE2 * n1 * n2; + } else + clock = CC_CLOCK_BASE1 * n1 * n2; + + if (clock == 0) + return 0; + + m1 = m & CC_M1_MASK; + m2 = (m & CC_M2_MASK) >> CC_M2_SHIFT; + m3 = (m & CC_M3_MASK) >> CC_M3_SHIFT; + mc = (m & CC_MC_MASK) >> CC_MC_SHIFT; + + if ((pll_type == PLL_TYPE1) || + (pll_type == PLL_TYPE3) || + (pll_type == PLL_TYPE4) || + (pll_type == PLL_TYPE7)) { + m1 = factor6(m1); + if ((pll_type == PLL_TYPE1) || (pll_type == PLL_TYPE3)) + m2 += CC_F5_BIAS; + else + m2 = factor6(m2); + m3 = factor6(m3); + + switch (mc) { + case CC_MC_BYPASS: return (clock); + case CC_MC_M1: return (clock / m1); + case CC_MC_M1M2: return (clock / (m1 * m2)); + case CC_MC_M1M2M3: return (clock / (m1 * m2 * m3)); + case CC_MC_M1M3: return (clock / (m1 * m3)); + default: return (0); + } + } else { + ASSERT(pll_type == PLL_TYPE2); + + m1 += CC_T2_BIAS; + m2 += CC_T2M2_BIAS; + m3 += CC_T2_BIAS; + ASSERT((m1 >= 2) && (m1 <= 7)); + ASSERT((m2 >= 3) && (m2 <= 10)); + ASSERT((m3 >= 2) && (m3 <= 7)); + + if ((mc & CC_T2MC_M1BYP) == 0) + clock /= m1; + if ((mc & CC_T2MC_M2BYP) == 0) + clock /= m2; + if ((mc & CC_T2MC_M3BYP) == 0) + clock /= m3; + + return (clock); + } +} + + +/* set chip watchdog reset timer to fire in 'ticks' */ +void +si_watchdog(si_t *sih, uint ticks) +{ + uint nb, maxt; + + if (PMUCTL_ENAB(sih)) { + + if ((CHIPID(sih->chip) == BCM4319_CHIP_ID) && + (CHIPREV(sih->chiprev) == 0) && (ticks != 0)) { + si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, clk_ctl_st), ~0, 0x2); + si_setcore(sih, USB20D_CORE_ID, 0); + si_core_disable(sih, 1); + si_setcore(sih, CC_CORE_ID, 0); + } + + nb = (sih->ccrev < 26) ? 16 : ((sih->ccrev >= 37) ? 32 : 24); + /* The mips compiler uses the sllv instruction, + * so we specially handle the 32-bit case. + */ + if (nb == 32) + maxt = 0xffffffff; + else + maxt = ((1 << nb) - 1); + + if (ticks == 1) + ticks = 2; + else if (ticks > maxt) + ticks = maxt; + + si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, pmuwatchdog), ~0, ticks); + } else { + maxt = (1 << 28) - 1; + if (ticks > maxt) + ticks = maxt; + + si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, watchdog), ~0, ticks); + } +} + +/* trigger watchdog reset after ms milliseconds */ +void +si_watchdog_ms(si_t *sih, uint32 ms) +{ + si_watchdog(sih, wd_msticks * ms); +} + + + + +/* return the slow clock source - LPO, XTAL, or PCI */ +static uint +si_slowclk_src(si_info_t *sii) +{ + chipcregs_t *cc; + + ASSERT(SI_FAST(sii) || si_coreid(&sii->pub) == CC_CORE_ID); + + if (sii->pub.ccrev < 6) { + if ((BUSTYPE(sii->pub.bustype) == PCI_BUS) && + (OSL_PCI_READ_CONFIG(sii->osh, PCI_GPIO_OUT, sizeof(uint32)) & + PCI_CFG_GPIO_SCS)) + return (SCC_SS_PCI); + else + return (SCC_SS_XTAL); + } else if (sii->pub.ccrev < 10) { + cc = (chipcregs_t *)si_setcoreidx(&sii->pub, sii->curidx); + return (R_REG(sii->osh, &cc->slow_clk_ctl) & SCC_SS_MASK); + } else /* Insta-clock */ + return (SCC_SS_XTAL); +} + +/* return the ILP (slowclock) min or max frequency */ +static uint +si_slowclk_freq(si_info_t *sii, bool max_freq, chipcregs_t *cc) +{ + uint32 slowclk; + uint div; + + ASSERT(SI_FAST(sii) || si_coreid(&sii->pub) == CC_CORE_ID); + + /* shouldn't be here unless we've established the chip has dynamic clk control */ + ASSERT(R_REG(sii->osh, &cc->capabilities) & CC_CAP_PWR_CTL); + + slowclk = si_slowclk_src(sii); + if (sii->pub.ccrev < 6) { + if (slowclk == SCC_SS_PCI) + return (max_freq ? (PCIMAXFREQ / 64) : (PCIMINFREQ / 64)); + else + return (max_freq ? (XTALMAXFREQ / 32) : (XTALMINFREQ / 32)); + } else if (sii->pub.ccrev < 10) { + div = 4 * + (((R_REG(sii->osh, &cc->slow_clk_ctl) & SCC_CD_MASK) >> SCC_CD_SHIFT) + 1); + if (slowclk == SCC_SS_LPO) + return (max_freq ? LPOMAXFREQ : LPOMINFREQ); + else if (slowclk == SCC_SS_XTAL) + return (max_freq ? (XTALMAXFREQ / div) : (XTALMINFREQ / div)); + else if (slowclk == SCC_SS_PCI) + return (max_freq ? (PCIMAXFREQ / div) : (PCIMINFREQ / div)); + else + ASSERT(0); + } else { + /* Chipc rev 10 is InstaClock */ + div = R_REG(sii->osh, &cc->system_clk_ctl) >> SYCC_CD_SHIFT; + div = 4 * (div + 1); + return (max_freq ? XTALMAXFREQ : (XTALMINFREQ / div)); + } + return (0); +} + +static void +si_clkctl_setdelay(si_info_t *sii, void *chipcregs) +{ + chipcregs_t *cc = (chipcregs_t *)chipcregs; + uint slowmaxfreq, pll_delay, slowclk; + uint pll_on_delay, fref_sel_delay; + + pll_delay = PLL_DELAY; + + /* If the slow clock is not sourced by the xtal then add the xtal_on_delay + * since the xtal will also be powered down by dynamic clk control logic. + */ + + slowclk = si_slowclk_src(sii); + if (slowclk != SCC_SS_XTAL) + pll_delay += XTAL_ON_DELAY; + + /* Starting with 4318 it is ILP that is used for the delays */ + slowmaxfreq = si_slowclk_freq(sii, (sii->pub.ccrev >= 10) ? FALSE : TRUE, cc); + + pll_on_delay = ((slowmaxfreq * pll_delay) + 999999) / 1000000; + fref_sel_delay = ((slowmaxfreq * FREF_DELAY) + 999999) / 1000000; + + W_REG(sii->osh, &cc->pll_on_delay, pll_on_delay); + W_REG(sii->osh, &cc->fref_sel_delay, fref_sel_delay); +} + +/* initialize power control delay registers */ +void +si_clkctl_init(si_t *sih) +{ + si_info_t *sii; + uint origidx = 0; + chipcregs_t *cc; + bool fast; + + if (!CCCTL_ENAB(sih)) + return; + + sii = SI_INFO(sih); + fast = SI_FAST(sii); + if (!fast) { + origidx = sii->curidx; + if ((cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0)) == NULL) + return; + } else if ((cc = (chipcregs_t *)CCREGS_FAST(sii)) == NULL) + return; + ASSERT(cc != NULL); + + /* set all Instaclk chip ILP to 1 MHz */ + if (sih->ccrev >= 10) + SET_REG(sii->osh, &cc->system_clk_ctl, SYCC_CD_MASK, + (ILP_DIV_1MHZ << SYCC_CD_SHIFT)); + + si_clkctl_setdelay(sii, (void *)(uintptr)cc); + + if (!fast) + si_setcoreidx(sih, origidx); +} + +/* change logical "focus" to the gpio core for optimized access */ +void * +si_gpiosetcore(si_t *sih) +{ + return (si_setcoreidx(sih, SI_CC_IDX)); +} + +/* mask&set gpiocontrol bits */ +uint32 +si_gpiocontrol(si_t *sih, uint32 mask, uint32 val, uint8 priority) +{ + uint regoff; + + regoff = 0; + + /* gpios could be shared on router platforms + * ignore reservation if it's high priority (e.g., test apps) + */ + if ((priority != GPIO_HI_PRIORITY) && + (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { + mask = priority ? (si_gpioreservation & mask) : + ((si_gpioreservation | mask) & ~(si_gpioreservation)); + val &= mask; + } + + regoff = OFFSETOF(chipcregs_t, gpiocontrol); + return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); +} + +/* mask&set gpio output enable bits */ +uint32 +si_gpioouten(si_t *sih, uint32 mask, uint32 val, uint8 priority) +{ + uint regoff; + + regoff = 0; + + /* gpios could be shared on router platforms + * ignore reservation if it's high priority (e.g., test apps) + */ + if ((priority != GPIO_HI_PRIORITY) && + (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { + mask = priority ? (si_gpioreservation & mask) : + ((si_gpioreservation | mask) & ~(si_gpioreservation)); + val &= mask; + } + + regoff = OFFSETOF(chipcregs_t, gpioouten); + return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); +} + +/* mask&set gpio output bits */ +uint32 +si_gpioout(si_t *sih, uint32 mask, uint32 val, uint8 priority) +{ + uint regoff; + + regoff = 0; + + /* gpios could be shared on router platforms + * ignore reservation if it's high priority (e.g., test apps) + */ + if ((priority != GPIO_HI_PRIORITY) && + (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { + mask = priority ? (si_gpioreservation & mask) : + ((si_gpioreservation | mask) & ~(si_gpioreservation)); + val &= mask; + } + + regoff = OFFSETOF(chipcregs_t, gpioout); + return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); +} + +/* reserve one gpio */ +uint32 +si_gpioreserve(si_t *sih, uint32 gpio_bitmask, uint8 priority) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + + /* only cores on SI_BUS share GPIO's and only applcation users need to + * reserve/release GPIO + */ + if ((BUSTYPE(sih->bustype) != SI_BUS) || (!priority)) { + ASSERT((BUSTYPE(sih->bustype) == SI_BUS) && (priority)); + return 0xffffffff; + } + /* make sure only one bit is set */ + if ((!gpio_bitmask) || ((gpio_bitmask) & (gpio_bitmask - 1))) { + ASSERT((gpio_bitmask) && !((gpio_bitmask) & (gpio_bitmask - 1))); + return 0xffffffff; + } + + /* already reserved */ + if (si_gpioreservation & gpio_bitmask) + return 0xffffffff; + /* set reservation */ + si_gpioreservation |= gpio_bitmask; + + return si_gpioreservation; +} + +/* release one gpio */ +/* + * releasing the gpio doesn't change the current value on the GPIO last write value + * persists till some one overwrites it + */ + +uint32 +si_gpiorelease(si_t *sih, uint32 gpio_bitmask, uint8 priority) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + + /* only cores on SI_BUS share GPIO's and only applcation users need to + * reserve/release GPIO + */ + if ((BUSTYPE(sih->bustype) != SI_BUS) || (!priority)) { + ASSERT((BUSTYPE(sih->bustype) == SI_BUS) && (priority)); + return 0xffffffff; + } + /* make sure only one bit is set */ + if ((!gpio_bitmask) || ((gpio_bitmask) & (gpio_bitmask - 1))) { + ASSERT((gpio_bitmask) && !((gpio_bitmask) & (gpio_bitmask - 1))); + return 0xffffffff; + } + + /* already released */ + if (!(si_gpioreservation & gpio_bitmask)) + return 0xffffffff; + + /* clear reservation */ + si_gpioreservation &= ~gpio_bitmask; + + return si_gpioreservation; +} + +/* return the current gpioin register value */ +uint32 +si_gpioin(si_t *sih) +{ + si_info_t *sii; + uint regoff; + + sii = SI_INFO(sih); + regoff = 0; + + regoff = OFFSETOF(chipcregs_t, gpioin); + return (si_corereg(sih, SI_CC_IDX, regoff, 0, 0)); +} + +/* mask&set gpio interrupt polarity bits */ +uint32 +si_gpiointpolarity(si_t *sih, uint32 mask, uint32 val, uint8 priority) +{ + si_info_t *sii; + uint regoff; + + sii = SI_INFO(sih); + regoff = 0; + + /* gpios could be shared on router platforms */ + if ((BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { + mask = priority ? (si_gpioreservation & mask) : + ((si_gpioreservation | mask) & ~(si_gpioreservation)); + val &= mask; + } + + regoff = OFFSETOF(chipcregs_t, gpiointpolarity); + return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); +} + +/* mask&set gpio interrupt mask bits */ +uint32 +si_gpiointmask(si_t *sih, uint32 mask, uint32 val, uint8 priority) +{ + si_info_t *sii; + uint regoff; + + sii = SI_INFO(sih); + regoff = 0; + + /* gpios could be shared on router platforms */ + if ((BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) { + mask = priority ? (si_gpioreservation & mask) : + ((si_gpioreservation | mask) & ~(si_gpioreservation)); + val &= mask; + } + + regoff = OFFSETOF(chipcregs_t, gpiointmask); + return (si_corereg(sih, SI_CC_IDX, regoff, mask, val)); +} + +/* assign the gpio to an led */ +uint32 +si_gpioled(si_t *sih, uint32 mask, uint32 val) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + if (sih->ccrev < 16) + return 0xffffffff; + + /* gpio led powersave reg */ + return (si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, gpiotimeroutmask), mask, val)); +} + +/* mask&set gpio timer val */ +uint32 +si_gpiotimerval(si_t *sih, uint32 mask, uint32 gpiotimerval) +{ + si_info_t *sii; + + sii = SI_INFO(sih); + + if (sih->ccrev < 16) + return 0xffffffff; + + return (si_corereg(sih, SI_CC_IDX, + OFFSETOF(chipcregs_t, gpiotimerval), mask, gpiotimerval)); +} + +uint32 +si_gpiopull(si_t *sih, bool updown, uint32 mask, uint32 val) +{ + si_info_t *sii; + uint offs; + + sii = SI_INFO(sih); + if (sih->ccrev < 20) + return 0xffffffff; + + offs = (updown ? OFFSETOF(chipcregs_t, gpiopulldown) : OFFSETOF(chipcregs_t, gpiopullup)); + return (si_corereg(sih, SI_CC_IDX, offs, mask, val)); +} + +uint32 +si_gpioevent(si_t *sih, uint regtype, uint32 mask, uint32 val) +{ + si_info_t *sii; + uint offs; + + sii = SI_INFO(sih); + if (sih->ccrev < 11) + return 0xffffffff; + + if (regtype == GPIO_REGEVT) + offs = OFFSETOF(chipcregs_t, gpioevent); + else if (regtype == GPIO_REGEVT_INTMSK) + offs = OFFSETOF(chipcregs_t, gpioeventintmask); + else if (regtype == GPIO_REGEVT_INTPOL) + offs = OFFSETOF(chipcregs_t, gpioeventintpolarity); + else + return 0xffffffff; + + return (si_corereg(sih, SI_CC_IDX, offs, mask, val)); +} + +void * +si_gpio_handler_register(si_t *sih, uint32 event, + bool level, gpio_handler_t cb, void *arg) +{ + si_info_t *sii; + gpioh_item_t *gi; + + ASSERT(event); + ASSERT(cb != NULL); + + sii = SI_INFO(sih); + if (sih->ccrev < 11) + return NULL; + + if ((gi = MALLOC(sii->osh, sizeof(gpioh_item_t))) == NULL) + return NULL; + + bzero(gi, sizeof(gpioh_item_t)); + gi->event = event; + gi->handler = cb; + gi->arg = arg; + gi->level = level; + + gi->next = sii->gpioh_head; + sii->gpioh_head = gi; + + return (void *)(gi); +} + +void +si_gpio_handler_unregister(si_t *sih, void *gpioh) +{ + si_info_t *sii; + gpioh_item_t *p, *n; + + sii = SI_INFO(sih); + if (sih->ccrev < 11) + return; + + ASSERT(sii->gpioh_head != NULL); + if ((void*)sii->gpioh_head == gpioh) { + sii->gpioh_head = sii->gpioh_head->next; + MFREE(sii->osh, gpioh, sizeof(gpioh_item_t)); + return; + } else { + p = sii->gpioh_head; + n = p->next; + while (n) { + if ((void*)n == gpioh) { + p->next = n->next; + MFREE(sii->osh, gpioh, sizeof(gpioh_item_t)); + return; + } + p = n; + n = n->next; + } + } + + ASSERT(0); /* Not found in list */ +} + +void +si_gpio_handler_process(si_t *sih) +{ + si_info_t *sii; + gpioh_item_t *h; + uint32 level = si_gpioin(sih); + uint32 levelp = si_gpiointpolarity(sih, 0, 0, 0); + uint32 edge = si_gpioevent(sih, GPIO_REGEVT, 0, 0); + uint32 edgep = si_gpioevent(sih, GPIO_REGEVT_INTPOL, 0, 0); + + sii = SI_INFO(sih); + for (h = sii->gpioh_head; h != NULL; h = h->next) { + if (h->handler) { + uint32 status = (h->level ? level : edge) & h->event; + uint32 polarity = (h->level ? levelp : edgep) & h->event; + + /* polarity bitval is opposite of status bitval */ + if (status ^ polarity) + h->handler(status, h->arg); + } + } + + si_gpioevent(sih, GPIO_REGEVT, edge, edge); /* clear edge-trigger status */ +} + +uint32 +si_gpio_int_enable(si_t *sih, bool enable) +{ + si_info_t *sii; + uint offs; + + sii = SI_INFO(sih); + if (sih->ccrev < 11) + return 0xffffffff; + + offs = OFFSETOF(chipcregs_t, intmask); + return (si_corereg(sih, SI_CC_IDX, offs, CI_GPIO, (enable ? CI_GPIO : 0))); +} + + +/* Return the size of the specified SOCRAM bank */ +static uint +socram_banksize(si_info_t *sii, sbsocramregs_t *regs, uint8 index, uint8 mem_type) +{ + uint banksize, bankinfo; + uint bankidx = index | (mem_type << SOCRAM_BANKIDX_MEMTYPE_SHIFT); + + ASSERT(mem_type <= SOCRAM_MEMTYPE_DEVRAM); + + W_REG(sii->osh, ®s->bankidx, bankidx); + bankinfo = R_REG(sii->osh, ®s->bankinfo); + banksize = SOCRAM_BANKINFO_SZBASE * ((bankinfo & SOCRAM_BANKINFO_SZMASK) + 1); + return banksize; +} + +void +si_socdevram(si_t *sih, bool set, uint8 *enable, uint8 *protect) +{ + si_info_t *sii; + uint origidx; + uint intr_val = 0; + sbsocramregs_t *regs; + bool wasup; + uint corerev; + + sii = SI_INFO(sih); + + /* Block ints and save current core */ + INTR_OFF(sii, intr_val); + origidx = si_coreidx(sih); + + if (!set) + *enable = *protect = 0; + + /* Switch to SOCRAM core */ + if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) + goto done; + + /* Get info for determining size */ + if (!(wasup = si_iscoreup(sih))) + si_core_reset(sih, 0, 0); + + corerev = si_corerev(sih); + if (corerev >= 10) { + uint32 extcinfo; + uint8 nb; + uint8 i; + uint32 bankidx, bankinfo; + + extcinfo = R_REG(sii->osh, ®s->extracoreinfo); + nb = ((extcinfo & SOCRAM_DEVRAMBANK_MASK) >> SOCRAM_DEVRAMBANK_SHIFT); + for (i = 0; i < nb; i++) { + bankidx = i | (SOCRAM_MEMTYPE_DEVRAM << SOCRAM_BANKIDX_MEMTYPE_SHIFT); + W_REG(sii->osh, ®s->bankidx, bankidx); + bankinfo = R_REG(sii->osh, ®s->bankinfo); + if (set) { + bankinfo &= ~SOCRAM_BANKINFO_DEVRAMSEL_MASK; + bankinfo &= ~SOCRAM_BANKINFO_DEVRAMPRO_MASK; + if (*enable) { + bankinfo |= (1 << SOCRAM_BANKINFO_DEVRAMSEL_SHIFT); + if (*protect) + bankinfo |= (1 << SOCRAM_BANKINFO_DEVRAMPRO_SHIFT); + } + W_REG(sii->osh, ®s->bankinfo, bankinfo); + } + else if (i == 0) { + if (bankinfo & SOCRAM_BANKINFO_DEVRAMSEL_MASK) { + *enable = 1; + if (bankinfo & SOCRAM_BANKINFO_DEVRAMPRO_MASK) + *protect = 1; + } + } + } + } + + /* Return to previous state and core */ + if (!wasup) + si_core_disable(sih, 0); + si_setcoreidx(sih, origidx); + +done: + INTR_RESTORE(sii, intr_val); +} + +bool +si_socdevram_pkg(si_t *sih) +{ + if (si_socdevram_size(sih) > 0) + return TRUE; + else + return FALSE; +} + +uint32 +si_socdevram_size(si_t *sih) +{ + si_info_t *sii; + uint origidx; + uint intr_val = 0; + uint32 memsize = 0; + sbsocramregs_t *regs; + bool wasup; + uint corerev; + + sii = SI_INFO(sih); + + /* Block ints and save current core */ + INTR_OFF(sii, intr_val); + origidx = si_coreidx(sih); + + /* Switch to SOCRAM core */ + if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) + goto done; + + /* Get info for determining size */ + if (!(wasup = si_iscoreup(sih))) + si_core_reset(sih, 0, 0); + + corerev = si_corerev(sih); + if (corerev >= 10) { + uint32 extcinfo; + uint8 nb; + uint8 i; + + extcinfo = R_REG(sii->osh, ®s->extracoreinfo); + nb = (((extcinfo & SOCRAM_DEVRAMBANK_MASK) >> SOCRAM_DEVRAMBANK_SHIFT)); + for (i = 0; i < nb; i++) + memsize += socram_banksize(sii, regs, i, SOCRAM_MEMTYPE_DEVRAM); + } + + /* Return to previous state and core */ + if (!wasup) + si_core_disable(sih, 0); + si_setcoreidx(sih, origidx); + +done: + INTR_RESTORE(sii, intr_val); + + return memsize; +} + +/* Return the RAM size of the SOCRAM core */ +uint32 +si_socram_size(si_t *sih) +{ + si_info_t *sii; + uint origidx; + uint intr_val = 0; + + sbsocramregs_t *regs; + bool wasup; + uint corerev; + uint32 coreinfo; + uint memsize = 0; + + sii = SI_INFO(sih); + + /* Block ints and save current core */ + INTR_OFF(sii, intr_val); + origidx = si_coreidx(sih); + + /* Switch to SOCRAM core */ + if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0))) + goto done; + + /* Get info for determining size */ + if (!(wasup = si_iscoreup(sih))) + si_core_reset(sih, 0, 0); + corerev = si_corerev(sih); + coreinfo = R_REG(sii->osh, ®s->coreinfo); + + /* Calculate size from coreinfo based on rev */ + if (corerev == 0) + memsize = 1 << (16 + (coreinfo & SRCI_MS0_MASK)); + else if (corerev < 3) { + memsize = 1 << (SR_BSZ_BASE + (coreinfo & SRCI_SRBSZ_MASK)); + memsize *= (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + } else if ((corerev <= 7) || (corerev == 12)) { + uint nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + uint bsz = (coreinfo & SRCI_SRBSZ_MASK); + uint lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT; + if (lss != 0) + nb --; + memsize = nb * (1 << (bsz + SR_BSZ_BASE)); + if (lss != 0) + memsize += (1 << ((lss - 1) + SR_BSZ_BASE)); + } else { + uint8 i; + uint nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + for (i = 0; i < nb; i++) + memsize += socram_banksize(sii, regs, i, SOCRAM_MEMTYPE_RAM); + } + + /* Return to previous state and core */ + if (!wasup) + si_core_disable(sih, 0); + si_setcoreidx(sih, origidx); + +done: + INTR_RESTORE(sii, intr_val); + + return memsize; +} + + +void +si_btcgpiowar(si_t *sih) +{ + si_info_t *sii; + uint origidx; + uint intr_val = 0; + chipcregs_t *cc; + + sii = SI_INFO(sih); + + /* Make sure that there is ChipCommon core present && + * UART_TX is strapped to 1 + */ + if (!(sih->cccaps & CC_CAP_UARTGPIO)) + return; + + /* si_corereg cannot be used as we have to guarantee 8-bit read/writes */ + INTR_OFF(sii, intr_val); + + origidx = si_coreidx(sih); + + cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0); + ASSERT(cc != NULL); + + W_REG(sii->osh, &cc->uart0mcr, R_REG(sii->osh, &cc->uart0mcr) | 0x04); + + /* restore the original index */ + si_setcoreidx(sih, origidx); + + INTR_RESTORE(sii, intr_val); +} + +uint +si_pll_reset(si_t *sih) +{ + uint err = 0; + + return (err); +} + +/* check if the device is removed */ +bool +si_deviceremoved(si_t *sih) +{ + uint32 w; + si_info_t *sii; + + sii = SI_INFO(sih); + + switch (BUSTYPE(sih->bustype)) { + case PCI_BUS: + ASSERT(sii->osh != NULL); + w = OSL_PCI_READ_CONFIG(sii->osh, PCI_CFG_VID, sizeof(uint32)); + if ((w & 0xFFFF) != VENDOR_BROADCOM) + return TRUE; + break; + } + return FALSE; +} + +bool +si_is_sprom_available(si_t *sih) +{ + if (sih->ccrev >= 31) { + si_info_t *sii; + uint origidx; + chipcregs_t *cc; + uint32 sromctrl; + + if ((sih->cccaps & CC_CAP_SROM) == 0) + return FALSE; + + sii = SI_INFO(sih); + origidx = sii->curidx; + cc = si_setcoreidx(sih, SI_CC_IDX); + sromctrl = R_REG(sii->osh, &cc->sromcontrol); + si_setcoreidx(sih, origidx); + return (sromctrl & SRC_PRESENT); + } + + switch (CHIPID(sih->chip)) { + case BCM4312_CHIP_ID: + return ((sih->chipst & CST4312_SPROM_OTP_SEL_MASK) != CST4312_OTP_SEL); + case BCM4325_CHIP_ID: + return (sih->chipst & CST4325_SPROM_SEL) != 0; + case BCM4322_CHIP_ID: + case BCM43221_CHIP_ID: + case BCM43231_CHIP_ID: + case BCM43222_CHIP_ID: + case BCM43111_CHIP_ID: + case BCM43112_CHIP_ID: + case BCM4342_CHIP_ID: + { + uint32 spromotp; + spromotp = (sih->chipst & CST4322_SPROM_OTP_SEL_MASK) >> + CST4322_SPROM_OTP_SEL_SHIFT; + return (spromotp & CST4322_SPROM_PRESENT) != 0; + } + case BCM4329_CHIP_ID: + return (sih->chipst & CST4329_SPROM_SEL) != 0; + case BCM4315_CHIP_ID: + return (sih->chipst & CST4315_SPROM_SEL) != 0; + case BCM4319_CHIP_ID: + return (sih->chipst & CST4319_SPROM_SEL) != 0; + + case BCM4336_CHIP_ID: + case BCM43362_CHIP_ID: + return (sih->chipst & CST4336_SPROM_PRESENT) != 0; + + case BCM4330_CHIP_ID: + return (sih->chipst & CST4330_SPROM_PRESENT) != 0; + case BCM4313_CHIP_ID: + return (sih->chipst & CST4313_SPROM_PRESENT) != 0; + case BCM43239_CHIP_ID: + return ((sih->chipst & CST43239_SPROM_MASK) && + !(sih->chipst & CST43239_SFLASH_MASK)); + default: + return TRUE; + } +} diff --git a/drivers/net/wireless/bcmdhd/siutils_priv.h b/drivers/net/wireless/bcmdhd/siutils_priv.h new file mode 100644 index 0000000000000..d80246e01d1b6 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/siutils_priv.h @@ -0,0 +1,235 @@ +/* + * Include file private to the SOC Interconnect support files. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: siutils_priv.h,v 1.17.4.3 2010-10-25 16:56:56 Exp $ + */ + +#ifndef _siutils_priv_h_ +#define _siutils_priv_h_ + +#define SI_ERROR(args) + +#define SI_MSG(args) + +/* Define SI_VMSG to printf for verbose debugging, but don't check it in */ +#define SI_VMSG(args) + +#define IS_SIM(chippkg) ((chippkg == HDLSIM_PKG_ID) || (chippkg == HWSIM_PKG_ID)) + +typedef uint32 (*si_intrsoff_t)(void *intr_arg); +typedef void (*si_intrsrestore_t)(void *intr_arg, uint32 arg); +typedef bool (*si_intrsenabled_t)(void *intr_arg); + +typedef struct gpioh_item { + void *arg; + bool level; + gpio_handler_t handler; + uint32 event; + struct gpioh_item *next; +} gpioh_item_t; + +/* misc si info needed by some of the routines */ +typedef struct si_info { + struct si_pub pub; /* back plane public state (must be first field) */ + + void *osh; /* osl os handle */ + void *sdh; /* bcmsdh handle */ + + uint dev_coreid; /* the core provides driver functions */ + void *intr_arg; /* interrupt callback function arg */ + si_intrsoff_t intrsoff_fn; /* turns chip interrupts off */ + si_intrsrestore_t intrsrestore_fn; /* restore chip interrupts */ + si_intrsenabled_t intrsenabled_fn; /* check if interrupts are enabled */ + + void *pch; /* PCI/E core handle */ + + gpioh_item_t *gpioh_head; /* GPIO event handlers list */ + + bool memseg; /* flag to toggle MEM_SEG register */ + + char *vars; + uint varsz; + + void *curmap; /* current regs va */ + void *regs[SI_MAXCORES]; /* other regs va */ + + uint curidx; /* current core index */ + uint numcores; /* # discovered cores */ + uint coreid[SI_MAXCORES]; /* id of each core */ + uint32 coresba[SI_MAXCORES]; /* backplane address of each core */ + void *regs2[SI_MAXCORES]; /* va of each core second register set (usbh20) */ + uint32 coresba2[SI_MAXCORES]; /* address of each core second register set (usbh20) */ + uint32 coresba_size[SI_MAXCORES]; /* backplane address space size */ + uint32 coresba2_size[SI_MAXCORES]; /* second address space size */ + + void *curwrap; /* current wrapper va */ + void *wrappers[SI_MAXCORES]; /* other cores wrapper va */ + uint32 wrapba[SI_MAXCORES]; /* address of controlling wrapper */ + + uint32 cia[SI_MAXCORES]; /* erom cia entry for each core */ + uint32 cib[SI_MAXCORES]; /* erom cia entry for each core */ + uint32 oob_router; /* oob router registers for axi */ +} si_info_t; + +#define SI_INFO(sih) (si_info_t *)(uintptr)sih + +#define GOODCOREADDR(x, b) (((x) >= (b)) && ((x) < ((b) + SI_MAXCORES * SI_CORE_SIZE)) && \ + ISALIGNED((x), SI_CORE_SIZE)) +#define GOODREGS(regs) ((regs) != NULL && ISALIGNED((uintptr)(regs), SI_CORE_SIZE)) +#define BADCOREADDR 0 +#define GOODIDX(idx) (((uint)idx) < SI_MAXCORES) +#define NOREV -1 /* Invalid rev */ + +#define PCI(si) ((BUSTYPE((si)->pub.bustype) == PCI_BUS) && \ + ((si)->pub.buscoretype == PCI_CORE_ID)) +#define PCIE(si) ((BUSTYPE((si)->pub.bustype) == PCI_BUS) && \ + ((si)->pub.buscoretype == PCIE_CORE_ID)) +#define PCMCIA(si) ((BUSTYPE((si)->pub.bustype) == PCMCIA_BUS) && ((si)->memseg == TRUE)) + +/* Newer chips can access PCI/PCIE and CC core without requiring to change + * PCI BAR0 WIN + */ +#define SI_FAST(si) (((si)->pub.buscoretype == PCIE_CORE_ID) || \ + (((si)->pub.buscoretype == PCI_CORE_ID) && (si)->pub.buscorerev >= 13)) + +#define PCIEREGS(si) (((char *)((si)->curmap) + PCI_16KB0_PCIREGS_OFFSET)) +#define CCREGS_FAST(si) (((char *)((si)->curmap) + PCI_16KB0_CCREGS_OFFSET)) + +/* + * Macros to disable/restore function core(D11, ENET, ILINE20, etc) interrupts before/ + * after core switching to avoid invalid register accesss inside ISR. + */ +#define INTR_OFF(si, intr_val) \ + if ((si)->intrsoff_fn && (si)->coreid[(si)->curidx] == (si)->dev_coreid) { \ + intr_val = (*(si)->intrsoff_fn)((si)->intr_arg); } +#define INTR_RESTORE(si, intr_val) \ + if ((si)->intrsrestore_fn && (si)->coreid[(si)->curidx] == (si)->dev_coreid) { \ + (*(si)->intrsrestore_fn)((si)->intr_arg, intr_val); } + +/* dynamic clock control defines */ +#define LPOMINFREQ 25000 /* low power oscillator min */ +#define LPOMAXFREQ 43000 /* low power oscillator max */ +#define XTALMINFREQ 19800000 /* 20 MHz - 1% */ +#define XTALMAXFREQ 20200000 /* 20 MHz + 1% */ +#define PCIMINFREQ 25000000 /* 25 MHz */ +#define PCIMAXFREQ 34000000 /* 33 MHz + fudge */ + +#define ILP_DIV_5MHZ 0 /* ILP = 5 MHz */ +#define ILP_DIV_1MHZ 4 /* ILP = 1 MHz */ + +#define PCI_FORCEHT(si) \ + (((PCIE(si)) && (si->pub.chip == BCM4311_CHIP_ID) && ((si->pub.chiprev <= 1))) || \ + ((PCI(si) || PCIE(si)) && (si->pub.chip == BCM4321_CHIP_ID)) || \ + (PCIE(si) && (si->pub.chip == BCM4716_CHIP_ID))) + +/* GPIO Based LED powersave defines */ +#define DEFAULT_GPIO_ONTIME 10 /* Default: 10% on */ +#define DEFAULT_GPIO_OFFTIME 90 /* Default: 10% on */ + +#ifndef DEFAULT_GPIOTIMERVAL +#define DEFAULT_GPIOTIMERVAL ((DEFAULT_GPIO_ONTIME << GPIO_ONTIME_SHIFT) | DEFAULT_GPIO_OFFTIME) +#endif + +/* Silicon Backplane externs */ +extern void sb_scan(si_t *sih, void *regs, uint devid); +extern uint sb_coreid(si_t *sih); +extern uint sb_intflag(si_t *sih); +extern uint sb_flag(si_t *sih); +extern void sb_setint(si_t *sih, int siflag); +extern uint sb_corevendor(si_t *sih); +extern uint sb_corerev(si_t *sih); +extern uint sb_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val); +extern bool sb_iscoreup(si_t *sih); +extern void *sb_setcoreidx(si_t *sih, uint coreidx); +extern uint32 sb_core_cflags(si_t *sih, uint32 mask, uint32 val); +extern void sb_core_cflags_wo(si_t *sih, uint32 mask, uint32 val); +extern uint32 sb_core_sflags(si_t *sih, uint32 mask, uint32 val); +extern void sb_commit(si_t *sih); +extern uint32 sb_base(uint32 admatch); +extern uint32 sb_size(uint32 admatch); +extern void sb_core_reset(si_t *sih, uint32 bits, uint32 resetbits); +extern void sb_core_disable(si_t *sih, uint32 bits); +extern uint32 sb_addrspace(si_t *sih, uint asidx); +extern uint32 sb_addrspacesize(si_t *sih, uint asidx); +extern int sb_numaddrspaces(si_t *sih); + +extern uint32 sb_set_initiator_to(si_t *sih, uint32 to, uint idx); + +extern bool sb_taclear(si_t *sih, bool details); + + +/* Wake-on-wireless-LAN (WOWL) */ +extern bool sb_pci_pmecap(si_t *sih); +struct osl_info; +extern bool sb_pci_fastpmecap(struct osl_info *osh); +extern bool sb_pci_pmeclr(si_t *sih); +extern void sb_pci_pmeen(si_t *sih); +extern uint sb_pcie_readreg(void *sih, uint addrtype, uint offset); + +/* AMBA Interconnect exported externs */ +extern si_t *ai_attach(uint pcidev, osl_t *osh, void *regs, uint bustype, + void *sdh, char **vars, uint *varsz); +extern si_t *ai_kattach(osl_t *osh); +extern void ai_scan(si_t *sih, void *regs, uint devid); + +extern uint ai_flag(si_t *sih); +extern void ai_setint(si_t *sih, int siflag); +extern uint ai_coreidx(si_t *sih); +extern uint ai_corevendor(si_t *sih); +extern uint ai_corerev(si_t *sih); +extern bool ai_iscoreup(si_t *sih); +extern void *ai_setcoreidx(si_t *sih, uint coreidx); +extern uint32 ai_core_cflags(si_t *sih, uint32 mask, uint32 val); +extern void ai_core_cflags_wo(si_t *sih, uint32 mask, uint32 val); +extern uint32 ai_core_sflags(si_t *sih, uint32 mask, uint32 val); +extern uint ai_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val); +extern void ai_core_reset(si_t *sih, uint32 bits, uint32 resetbits); +extern void ai_core_disable(si_t *sih, uint32 bits); +extern int ai_numaddrspaces(si_t *sih); +extern uint32 ai_addrspace(si_t *sih, uint asidx); +extern uint32 ai_addrspacesize(si_t *sih, uint asidx); +extern uint ai_wrap_reg(si_t *sih, uint32 offset, uint32 mask, uint32 val); + + + +#define ub_scan(a, b, c) do {} while (0) +#define ub_flag(a) (0) +#define ub_setint(a, b) do {} while (0) +#define ub_coreidx(a) (0) +#define ub_corevendor(a) (0) +#define ub_corerev(a) (0) +#define ub_iscoreup(a) (0) +#define ub_setcoreidx(a, b) (0) +#define ub_core_cflags(a, b, c) (0) +#define ub_core_cflags_wo(a, b, c) do {} while (0) +#define ub_core_sflags(a, b, c) (0) +#define ub_corereg(a, b, c, d, e) (0) +#define ub_core_reset(a, b, c) do {} while (0) +#define ub_core_disable(a, b) do {} while (0) +#define ub_numaddrspaces(a) (0) +#define ub_addrspace(a, b) (0) +#define ub_addrspacesize(a, b) (0) +#define ub_view(a, b) do {} while (0) +#define ub_dumpregs(a, b) do {} while (0) + +#endif /* _siutils_priv_h_ */ diff --git a/drivers/net/wireless/bcmdhd/uamp_api.h b/drivers/net/wireless/bcmdhd/uamp_api.h new file mode 100644 index 0000000000000..c51c68cd0eede --- /dev/null +++ b/drivers/net/wireless/bcmdhd/uamp_api.h @@ -0,0 +1,176 @@ +/* + * Name: uamp_api.h + * + * Description: Universal AMP API + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: uamp_api.h,v 1.2.8.1 2011-02-05 00:16:14 Exp $ + * + */ +#ifndef UAMP_API_H +#define UAMP_API_H + + +#include "typedefs.h" + + +/***************************************************************************** +** Constant and Type Definitions +****************************************************************************** +*/ + +#define BT_API + +/* Types. */ +typedef bool BOOLEAN; +typedef uint8 UINT8; +typedef uint16 UINT16; + + +/* UAMP identifiers */ +#define UAMP_ID_1 1 +#define UAMP_ID_2 2 +typedef UINT8 tUAMP_ID; + +/* UAMP event ids (used by UAMP_CBACK) */ +#define UAMP_EVT_RX_READY 0 /* Data from AMP controller is ready to be read */ +#define UAMP_EVT_CTLR_REMOVED 1 /* Controller removed */ +#define UAMP_EVT_CTLR_READY 2 /* Controller added/ready */ +typedef UINT8 tUAMP_EVT; + + +/* UAMP Channels */ +#define UAMP_CH_HCI_CMD 0 /* HCI Command channel */ +#define UAMP_CH_HCI_EVT 1 /* HCI Event channel */ +#define UAMP_CH_HCI_DATA 2 /* HCI ACL Data channel */ +typedef UINT8 tUAMP_CH; + +/* tUAMP_EVT_DATA: union for event-specific data, used by UAMP_CBACK */ +typedef union { + tUAMP_CH channel; /* UAMP_EVT_RX_READY: channel for which rx occured */ +} tUAMP_EVT_DATA; + + +/***************************************************************************** +** +** Function: UAMP_CBACK +** +** Description: Callback for events. Register callback using UAMP_Init. +** +** Parameters amp_id: AMP device identifier that generated the event +** amp_evt: event id +** p_amp_evt_data: pointer to event-specific data +** +****************************************************************************** +*/ +typedef void (*tUAMP_CBACK)(tUAMP_ID amp_id, tUAMP_EVT amp_evt, tUAMP_EVT_DATA *p_amp_evt_data); + +/***************************************************************************** +** external function declarations +****************************************************************************** +*/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************************************************************** +** +** Function: UAMP_Init +** +** Description: Initialize UAMP driver +** +** Parameters p_cback: Callback function for UAMP event notification +** +****************************************************************************** +*/ +BT_API BOOLEAN UAMP_Init(tUAMP_CBACK p_cback); + + +/***************************************************************************** +** +** Function: UAMP_Open +** +** Description: Open connection to local AMP device. +** +** Parameters app_id: Application specific AMP identifer. This value +** will be included in AMP messages sent to the +** BTU task, to identify source of the message +** +****************************************************************************** +*/ +BT_API BOOLEAN UAMP_Open(tUAMP_ID amp_id); + +/***************************************************************************** +** +** Function: UAMP_Close +** +** Description: Close connection to local AMP device. +** +** Parameters app_id: Application specific AMP identifer. +** +****************************************************************************** +*/ +BT_API void UAMP_Close(tUAMP_ID amp_id); + + +/***************************************************************************** +** +** Function: UAMP_Write +** +** Description: Send buffer to AMP device. Frees GKI buffer when done. +** +** +** Parameters: app_id: AMP identifer. +** p_buf: pointer to buffer to write +** num_bytes: number of bytes to write +** channel: UAMP_CH_HCI_ACL, or UAMP_CH_HCI_CMD +** +** Returns: number of bytes written +** +****************************************************************************** +*/ +BT_API UINT16 UAMP_Write(tUAMP_ID amp_id, UINT8 *p_buf, UINT16 num_bytes, tUAMP_CH channel); + +/***************************************************************************** +** +** Function: UAMP_Read +** +** Description: Read incoming data from AMP. Call after receiving a +** UAMP_EVT_RX_READY callback event. +** +** Parameters: app_id: AMP identifer. +** p_buf: pointer to buffer for holding incoming AMP data +** buf_size: size of p_buf +** channel: UAMP_CH_HCI_ACL, or UAMP_CH_HCI_EVT +** +** Returns: number of bytes read +** +****************************************************************************** +*/ +BT_API UINT16 UAMP_Read(tUAMP_ID amp_id, UINT8 *p_buf, UINT16 buf_size, tUAMP_CH channel); + +#ifdef __cplusplus +} +#endif + +#endif /* UAMP_API_H */ diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c new file mode 100644 index 0000000000000..4fcdcd32a3ff8 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_android.c @@ -0,0 +1,858 @@ +/* + * Linux cfg80211 driver - Android related functions + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_android.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $ + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef WL_CFG80211 +#include +#endif +#if defined(CONFIG_WIFI_CONTROL_FUNC) +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) +#include +#else +#include +#endif +#endif /* CONFIG_WIFI_CONTROL_FUNC */ + +/* + * Android private command strings, PLEASE define new private commands here + * so they can be updated easily in the future (if needed) + */ + +#define CMD_START "START" +#define CMD_STOP "STOP" +#define CMD_SCAN_ACTIVE "SCAN-ACTIVE" +#define CMD_SCAN_PASSIVE "SCAN-PASSIVE" +#define CMD_RSSI "RSSI" +#define CMD_LINKSPEED "LINKSPEED" +#define CMD_RXFILTER_START "RXFILTER-START" +#define CMD_RXFILTER_STOP "RXFILTER-STOP" +#define CMD_RXFILTER_ADD "RXFILTER-ADD" +#define CMD_RXFILTER_REMOVE "RXFILTER-REMOVE" +#define CMD_BTCOEXSCAN_START "BTCOEXSCAN-START" +#define CMD_BTCOEXSCAN_STOP "BTCOEXSCAN-STOP" +#define CMD_BTCOEXMODE "BTCOEXMODE" +#define CMD_SETSUSPENDOPT "SETSUSPENDOPT" +#define CMD_SETSUSPENDMODE "SETSUSPENDMODE" +#define CMD_P2P_DEV_ADDR "P2P_DEV_ADDR" +#define CMD_SETFWPATH "SETFWPATH" +#define CMD_SETBAND "SETBAND" +#define CMD_GETBAND "GETBAND" +#define CMD_COUNTRY "COUNTRY" +#define CMD_P2P_SET_NOA "P2P_SET_NOA" +#if !defined WL_ENABLE_P2P_IF +#define CMD_P2P_GET_NOA "P2P_GET_NOA" +#endif +#define CMD_P2P_SET_PS "P2P_SET_PS" +#define CMD_SET_AP_WPS_P2P_IE "SET_AP_WPS_P2P_IE" + + +#ifdef PNO_SUPPORT +#define CMD_PNOSSIDCLR_SET "PNOSSIDCLR" +#define CMD_PNOSETUP_SET "PNOSETUP " +#define CMD_PNOENABLE_SET "PNOFORCE" +#define CMD_PNODEBUG_SET "PNODEBUG" + +#define PNO_TLV_PREFIX 'S' +#define PNO_TLV_VERSION '1' +#define PNO_TLV_SUBVERSION '2' +#define PNO_TLV_RESERVED '0' +#define PNO_TLV_TYPE_SSID_IE 'S' +#define PNO_TLV_TYPE_TIME 'T' +#define PNO_TLV_FREQ_REPEAT 'R' +#define PNO_TLV_FREQ_EXPO_MAX 'M' + +typedef struct cmd_tlv { + char prefix; + char version; + char subver; + char reserved; +} cmd_tlv_t; +#endif /* PNO_SUPPORT */ + +typedef struct android_wifi_priv_cmd { + char *buf; + int used_len; + int total_len; +} android_wifi_priv_cmd; + +/** + * Extern function declarations (TODO: move them to dhd_linux.h) + */ +void dhd_customer_gpio_wlan_ctrl(int onoff); +uint dhd_dev_reset(struct net_device *dev, uint8 flag); +int dhd_dev_init_ioctl(struct net_device *dev); +#ifdef WL_CFG80211 +int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr); +int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command); +#else +int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr) +{ return 0; } +int wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len) +{ return 0; } +int wl_cfg80211_get_p2p_noa(struct net_device *net, char* buf, int len) +{ return 0; } +int wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len) +{ return 0; } +#endif +extern int dhd_os_check_if_up(void *dhdp); +extern void *bcmsdh_get_drvdata(void); + +extern bool ap_fw_loaded; +#ifdef CUSTOMER_HW2 +extern char iface_name[IFNAMSIZ]; +#endif + +/** + * Local (static) functions and variables + */ + +/* Initialize g_wifi_on to 1 so dhd_bus_start will be called for the first + * time (only) in dhd_open, subsequential wifi on will be handled by + * wl_android_wifi_on + */ +static int g_wifi_on = TRUE; + +/** + * Local (static) function definitions + */ +static int wl_android_get_link_speed(struct net_device *net, char *command, int total_len) +{ + int link_speed; + int bytes_written; + int error; + + error = wldev_get_link_speed(net, &link_speed); + if (error) + return -1; + + /* Convert Kbps to Android Mbps */ + link_speed = link_speed / 1000; + bytes_written = snprintf(command, total_len, "LinkSpeed %d", link_speed); + DHD_INFO(("%s: command result is %s\n", __FUNCTION__, command)); + return bytes_written; +} + +static int wl_android_get_rssi(struct net_device *net, char *command, int total_len) +{ + wlc_ssid_t ssid = {0}; + int rssi; + int bytes_written = 0; + int error; + + error = wldev_get_rssi(net, &rssi); + if (error) + return -1; + + error = wldev_get_ssid(net, &ssid); + if (error) + return -1; + if ((ssid.SSID_len == 0) || (ssid.SSID_len > DOT11_MAX_SSID_LEN)) { + DHD_ERROR(("%s: wldev_get_ssid failed\n", __FUNCTION__)); + } else { + memcpy(command, ssid.SSID, ssid.SSID_len); + bytes_written = ssid.SSID_len; + } + bytes_written += snprintf(&command[bytes_written], total_len, " rssi %d", rssi); + DHD_INFO(("%s: command result is %s (%d)\n", __FUNCTION__, command, bytes_written)); + return bytes_written; +} + +static int wl_android_set_suspendopt(struct net_device *dev, char *command, int total_len) +{ + int suspend_flag; + int ret_now; + int ret = 0; + + suspend_flag = *(command + strlen(CMD_SETSUSPENDOPT) + 1) - '0'; + + if (suspend_flag != 0) + suspend_flag = 1; + ret_now = net_os_set_suspend_disable(dev, suspend_flag); + + if (ret_now != suspend_flag) { + if (!(ret = net_os_set_suspend(dev, ret_now, 1))) + DHD_INFO(("%s: Suspend Flag %d -> %d\n", + __FUNCTION__, ret_now, suspend_flag)); + else + DHD_ERROR(("%s: failed %d\n", __FUNCTION__, ret)); + } + return ret; +} + +static int wl_android_set_suspendmode(struct net_device *dev, char *command, int total_len) +{ + int ret = 0; + +#if !defined(CONFIG_HAS_EARLYSUSPEND) || !defined(DHD_USE_EARLYSUSPEND) + int suspend_flag; + + suspend_flag = *(command + strlen(CMD_SETSUSPENDMODE) + 1) - '0'; + + if (suspend_flag != 0) + suspend_flag = 1; + + if (!(ret = net_os_set_suspend(dev, suspend_flag, 0))) + DHD_INFO(("%s: Suspend Mode %d\n",__FUNCTION__,suspend_flag)); + else + DHD_ERROR(("%s: failed %d\n",__FUNCTION__,ret)); +#endif + return ret; +} + +static int wl_android_get_band(struct net_device *dev, char *command, int total_len) +{ + uint band; + int bytes_written; + int error; + + error = wldev_get_band(dev, &band); + if (error) + return -1; + bytes_written = snprintf(command, total_len, "Band %d", band); + return bytes_written; +} + +#if defined(PNO_SUPPORT) && !defined(WL_SCHED_SCAN) +static int wl_android_set_pno_setup(struct net_device *dev, char *command, int total_len) +{ + wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT]; + int res = -1; + int nssid = 0; + cmd_tlv_t *cmd_tlv_temp; + char *str_ptr; + int tlv_size_left; + int pno_time = 0; + int pno_repeat = 0; + int pno_freq_expo_max = 0; + +#ifdef PNO_SET_DEBUG + int i; + char pno_in_example[] = { + 'P', 'N', 'O', 'S', 'E', 'T', 'U', 'P', ' ', + 'S', '1', '2', '0', + 'S', + 0x05, + 'd', 'l', 'i', 'n', 'k', + 'S', + 0x04, + 'G', 'O', 'O', 'G', + 'T', + '0', 'B', + 'R', + '2', + 'M', + '2', + 0x00 + }; +#endif /* PNO_SET_DEBUG */ + + DHD_INFO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); + + if (total_len < (strlen(CMD_PNOSETUP_SET) + sizeof(cmd_tlv_t))) { + DHD_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len)); + goto exit_proc; + } + +#ifdef PNO_SET_DEBUG + memcpy(command, pno_in_example, sizeof(pno_in_example)); + for (i = 0; i < sizeof(pno_in_example); i++) + printf("%02X ", command[i]); + printf("\n"); + total_len = sizeof(pno_in_example); +#endif + + str_ptr = command + strlen(CMD_PNOSETUP_SET); + tlv_size_left = total_len - strlen(CMD_PNOSETUP_SET); + + cmd_tlv_temp = (cmd_tlv_t *)str_ptr; + memset(ssids_local, 0, sizeof(ssids_local)); + + if ((cmd_tlv_temp->prefix == PNO_TLV_PREFIX) && + (cmd_tlv_temp->version == PNO_TLV_VERSION) && + (cmd_tlv_temp->subver == PNO_TLV_SUBVERSION)) { + + str_ptr += sizeof(cmd_tlv_t); + tlv_size_left -= sizeof(cmd_tlv_t); + + if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local, + MAX_PFN_LIST_COUNT, &tlv_size_left)) <= 0) { + DHD_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid)); + goto exit_proc; + } else { + if ((str_ptr[0] != PNO_TLV_TYPE_TIME) || (tlv_size_left <= 1)) { + DHD_ERROR(("%s scan duration corrupted field size %d\n", + __FUNCTION__, tlv_size_left)); + goto exit_proc; + } + str_ptr++; + pno_time = simple_strtoul(str_ptr, &str_ptr, 16); + DHD_INFO(("%s: pno_time=%d\n", __FUNCTION__, pno_time)); + + if (str_ptr[0] != 0) { + if ((str_ptr[0] != PNO_TLV_FREQ_REPEAT)) { + DHD_ERROR(("%s pno repeat : corrupted field\n", + __FUNCTION__)); + goto exit_proc; + } + str_ptr++; + pno_repeat = simple_strtoul(str_ptr, &str_ptr, 16); + DHD_INFO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat)); + if (str_ptr[0] != PNO_TLV_FREQ_EXPO_MAX) { + DHD_ERROR(("%s FREQ_EXPO_MAX corrupted field size\n", + __FUNCTION__)); + goto exit_proc; + } + str_ptr++; + pno_freq_expo_max = simple_strtoul(str_ptr, &str_ptr, 16); + DHD_INFO(("%s: pno_freq_expo_max=%d\n", + __FUNCTION__, pno_freq_expo_max)); + } + } + } else { + DHD_ERROR(("%s get wrong TLV command\n", __FUNCTION__)); + goto exit_proc; + } + + res = dhd_dev_pno_set(dev, ssids_local, nssid, pno_time, pno_repeat, pno_freq_expo_max); + +exit_proc: + return res; +} +#endif /* PNO_SUPPORT && !WL_SCHED_SCAN */ + +static int wl_android_get_p2p_dev_addr(struct net_device *ndev, char *command, int total_len) +{ + int ret; + int bytes_written = 0; + + ret = wl_cfg80211_get_p2p_dev_addr(ndev, (struct ether_addr*)command); + if (ret) + return 0; + bytes_written = sizeof(struct ether_addr); + return bytes_written; +} + +/** + * Global function definitions (declared in wl_android.h) + */ + +int wl_android_wifi_on(struct net_device *dev) +{ + int ret = 0; + + printk("%s in\n", __FUNCTION__); + if (!dev) { + DHD_ERROR(("%s: dev is null\n", __FUNCTION__)); + return -EINVAL; + } + + dhd_net_if_lock(dev); + if (!g_wifi_on) { + dhd_customer_gpio_wlan_ctrl(WLAN_RESET_ON); + sdioh_start(NULL, 0); + ret = dhd_dev_reset(dev, FALSE); + sdioh_start(NULL, 1); + if (!ret) { + if (dhd_dev_init_ioctl(dev) < 0) + ret = -EFAULT; + } + g_wifi_on = 1; + } + dhd_net_if_unlock(dev); + + return ret; +} + +int wl_android_wifi_off(struct net_device *dev) +{ + int ret = 0; + + printk("%s in\n", __FUNCTION__); + if (!dev) { + DHD_TRACE(("%s: dev is null\n", __FUNCTION__)); + return -EINVAL; + } + + dhd_net_if_lock(dev); + if (g_wifi_on) { + ret = dhd_dev_reset(dev, TRUE); + sdioh_stop(NULL); + dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); + g_wifi_on = 0; + } + dhd_net_if_unlock(dev); + + return ret; +} + +static int wl_android_set_fwpath(struct net_device *net, char *command, int total_len) +{ + if ((strlen(command) - strlen(CMD_SETFWPATH)) > MOD_PARAM_PATHLEN) + return -1; + bcm_strncpy_s(fw_path, sizeof(fw_path), + command + strlen(CMD_SETFWPATH) + 1, MOD_PARAM_PATHLEN - 1); + if (strstr(fw_path, "apsta") != NULL) { + DHD_INFO(("GOT APSTA FIRMWARE\n")); + ap_fw_loaded = TRUE; + } else { + DHD_INFO(("GOT STA FIRMWARE\n")); + ap_fw_loaded = FALSE; + } + return 0; +} + +int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) +{ + int ret = 0; + char *command = NULL; + int bytes_written = 0; + android_wifi_priv_cmd priv_cmd; + + net_os_wake_lock(net); + + if (!ifr->ifr_data) { + ret = -EINVAL; + goto exit; + } + if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) { + ret = -EFAULT; + goto exit; + } + command = kmalloc(priv_cmd.total_len, GFP_KERNEL); + if (!command) + { + DHD_ERROR(("%s: failed to allocate memory\n", __FUNCTION__)); + ret = -ENOMEM; + goto exit; + } + if (copy_from_user(command, priv_cmd.buf, priv_cmd.total_len)) { + ret = -EFAULT; + goto exit; + } + + DHD_INFO(("%s: Android private cmd \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name)); + + if (strnicmp(command, CMD_START, strlen(CMD_START)) == 0) { + DHD_INFO(("%s, Received regular START command\n", __FUNCTION__)); + bytes_written = wl_android_wifi_on(net); + } + else if (strnicmp(command, CMD_SETFWPATH, strlen(CMD_SETFWPATH)) == 0) { + bytes_written = wl_android_set_fwpath(net, command, priv_cmd.total_len); + } + + if (!g_wifi_on) { + DHD_ERROR(("%s: Ignore private cmd \"%s\" - iface %s is down\n", + __FUNCTION__, command, ifr->ifr_name)); + ret = 0; + goto exit; + } + + if (strnicmp(command, CMD_STOP, strlen(CMD_STOP)) == 0) { + bytes_written = wl_android_wifi_off(net); + } + else if (strnicmp(command, CMD_SCAN_ACTIVE, strlen(CMD_SCAN_ACTIVE)) == 0) { + /* TBD: SCAN-ACTIVE */ + } + else if (strnicmp(command, CMD_SCAN_PASSIVE, strlen(CMD_SCAN_PASSIVE)) == 0) { + /* TBD: SCAN-PASSIVE */ + } + else if (strnicmp(command, CMD_RSSI, strlen(CMD_RSSI)) == 0) { + bytes_written = wl_android_get_rssi(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_LINKSPEED, strlen(CMD_LINKSPEED)) == 0) { + bytes_written = wl_android_get_link_speed(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_RXFILTER_START, strlen(CMD_RXFILTER_START)) == 0) { + bytes_written = net_os_set_packet_filter(net, 1); + } + else if (strnicmp(command, CMD_RXFILTER_STOP, strlen(CMD_RXFILTER_STOP)) == 0) { + bytes_written = net_os_set_packet_filter(net, 0); + } + else if (strnicmp(command, CMD_RXFILTER_ADD, strlen(CMD_RXFILTER_ADD)) == 0) { + int filter_num = *(command + strlen(CMD_RXFILTER_ADD) + 1) - '0'; + bytes_written = net_os_rxfilter_add_remove(net, TRUE, filter_num); + } + else if (strnicmp(command, CMD_RXFILTER_REMOVE, strlen(CMD_RXFILTER_REMOVE)) == 0) { + int filter_num = *(command + strlen(CMD_RXFILTER_REMOVE) + 1) - '0'; + bytes_written = net_os_rxfilter_add_remove(net, FALSE, filter_num); + } + else if (strnicmp(command, CMD_BTCOEXSCAN_START, strlen(CMD_BTCOEXSCAN_START)) == 0) { + /* TBD: BTCOEXSCAN-START */ + } + else if (strnicmp(command, CMD_BTCOEXSCAN_STOP, strlen(CMD_BTCOEXSCAN_STOP)) == 0) { + /* TBD: BTCOEXSCAN-STOP */ + } + else if (strnicmp(command, CMD_BTCOEXMODE, strlen(CMD_BTCOEXMODE)) == 0) { + uint mode = *(command + strlen(CMD_BTCOEXMODE) + 1) - '0'; + + if (mode == 1) + net_os_set_packet_filter(net, 0); /* DHCP starts */ + else + net_os_set_packet_filter(net, 1); /* DHCP ends */ +#ifdef WL_CFG80211 + bytes_written = wl_cfg80211_set_btcoex_dhcp(net, command); +#endif + } + else if (strnicmp(command, CMD_SETSUSPENDOPT, strlen(CMD_SETSUSPENDOPT)) == 0) { + bytes_written = wl_android_set_suspendopt(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_SETSUSPENDMODE, strlen(CMD_SETSUSPENDMODE)) == 0) { + bytes_written = wl_android_set_suspendmode(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_SETBAND, strlen(CMD_SETBAND)) == 0) { + uint band = *(command + strlen(CMD_SETBAND) + 1) - '0'; + bytes_written = wldev_set_band(net, band); + } + else if (strnicmp(command, CMD_GETBAND, strlen(CMD_GETBAND)) == 0) { + bytes_written = wl_android_get_band(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_COUNTRY, strlen(CMD_COUNTRY)) == 0) { + char *country_code = command + strlen(CMD_COUNTRY) + 1; + bytes_written = wldev_set_country(net, country_code); + } +#if defined(PNO_SUPPORT) && !defined(WL_SCHED_SCAN) + else if (strnicmp(command, CMD_PNOSSIDCLR_SET, strlen(CMD_PNOSSIDCLR_SET)) == 0) { + bytes_written = dhd_dev_pno_reset(net); + } + else if (strnicmp(command, CMD_PNOSETUP_SET, strlen(CMD_PNOSETUP_SET)) == 0) { + bytes_written = wl_android_set_pno_setup(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_PNOENABLE_SET, strlen(CMD_PNOENABLE_SET)) == 0) { + uint pfn_enabled = *(command + strlen(CMD_PNOENABLE_SET) + 1) - '0'; + bytes_written = dhd_dev_pno_enable(net, pfn_enabled); + } +#endif + else if (strnicmp(command, CMD_P2P_DEV_ADDR, strlen(CMD_P2P_DEV_ADDR)) == 0) { + bytes_written = wl_android_get_p2p_dev_addr(net, command, priv_cmd.total_len); + } + else if (strnicmp(command, CMD_P2P_SET_NOA, strlen(CMD_P2P_SET_NOA)) == 0) { + int skip = strlen(CMD_P2P_SET_NOA) + 1; + bytes_written = wl_cfg80211_set_p2p_noa(net, command + skip, + priv_cmd.total_len - skip); + } +#if !defined WL_ENABLE_P2P_IF + else if (strnicmp(command, CMD_P2P_GET_NOA, strlen(CMD_P2P_GET_NOA)) == 0) { + bytes_written = wl_cfg80211_get_p2p_noa(net, command, priv_cmd.total_len); + } +#endif + else if (strnicmp(command, CMD_P2P_SET_PS, strlen(CMD_P2P_SET_PS)) == 0) { + int skip = strlen(CMD_P2P_SET_PS) + 1; + bytes_written = wl_cfg80211_set_p2p_ps(net, command + skip, + priv_cmd.total_len - skip); + } +#ifdef WL_CFG80211 + else if (strnicmp(command, CMD_SET_AP_WPS_P2P_IE, + strlen(CMD_SET_AP_WPS_P2P_IE)) == 0) { + int skip = strlen(CMD_SET_AP_WPS_P2P_IE) + 3; + bytes_written = wl_cfg80211_set_wps_p2p_ie(net, command + skip, + priv_cmd.total_len - skip, *(command + skip - 2) - '0'); + } +#endif /* WL_CFG80211 */ + else { + DHD_ERROR(("Unknown PRIVATE command %s - ignored\n", command)); + snprintf(command, 3, "OK"); + bytes_written = strlen("OK"); + } + + if (bytes_written >= 0) { + if ((bytes_written == 0) && (priv_cmd.total_len > 0)) + command[0] = '\0'; + if (bytes_written >= priv_cmd.total_len) { + DHD_ERROR(("%s: bytes_written = %d\n", __FUNCTION__, bytes_written)); + bytes_written = priv_cmd.total_len; + } else { + bytes_written++; + } + priv_cmd.used_len = bytes_written; + if (copy_to_user(priv_cmd.buf, command, bytes_written)) { + DHD_ERROR(("%s: failed to copy data to user buffer\n", __FUNCTION__)); + ret = -EFAULT; + } + } + else { + ret = bytes_written; + } + +exit: + net_os_wake_unlock(net); + if (command) { + kfree(command); + } + + return ret; +} + +int wl_android_init(void) +{ + int ret = 0; + + dhd_msg_level |= DHD_ERROR_VAL; +#ifdef ENABLE_INSMOD_NO_FW_LOAD + dhd_download_fw_on_driverload = FALSE; +#endif /* ENABLE_INSMOD_NO_FW_LOAD */ +#ifdef CUSTOMER_HW2 + if (!iface_name[0]) { + memset(iface_name, 0, IFNAMSIZ); + bcm_strncpy_s(iface_name, IFNAMSIZ, "wlan", IFNAMSIZ); + } +#endif /* CUSTOMER_HW2 */ + return ret; +} + +int wl_android_exit(void) +{ + int ret = 0; + + return ret; +} + +void wl_android_post_init(void) +{ + if (!dhd_download_fw_on_driverload) { + /* Call customer gpio to turn off power with WL_REG_ON signal */ + dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); + g_wifi_on = 0; + } +} +/** + * Functions for Android WiFi card detection + */ +#if defined(CONFIG_WIFI_CONTROL_FUNC) + +static int g_wifidev_registered = 0; +static struct semaphore wifi_control_sem; +static struct wifi_platform_data *wifi_control_data = NULL; +static struct resource *wifi_irqres = NULL; + +static int wifi_add_dev(void); +static void wifi_del_dev(void); + +int wl_android_wifictrl_func_add(void) +{ + int ret = 0; + sema_init(&wifi_control_sem, 0); + + ret = wifi_add_dev(); + if (ret) { + DHD_ERROR(("%s: platform_driver_register failed\n", __FUNCTION__)); + return ret; + } + g_wifidev_registered = 1; + + /* Waiting callback after platform_driver_register is done or exit with error */ + if (down_timeout(&wifi_control_sem, msecs_to_jiffies(1000)) != 0) { + ret = -EINVAL; + DHD_ERROR(("%s: platform_driver_register timeout\n", __FUNCTION__)); + } + + return ret; +} + +void wl_android_wifictrl_func_del(void) +{ + if (g_wifidev_registered) + { + wifi_del_dev(); + g_wifidev_registered = 0; + } +} + +void* wl_android_prealloc(int section, unsigned long size) +{ + void *alloc_ptr = NULL; + if (wifi_control_data && wifi_control_data->mem_prealloc) { + alloc_ptr = wifi_control_data->mem_prealloc(section, size); + if (alloc_ptr) { + DHD_INFO(("success alloc section %d\n", section)); + if (size != 0L) + bzero(alloc_ptr, size); + return alloc_ptr; + } + } + + DHD_ERROR(("can't alloc section %d\n", section)); + return NULL; +} + +int wifi_get_irq_number(unsigned long *irq_flags_ptr) +{ + if (wifi_irqres) { + *irq_flags_ptr = wifi_irqres->flags & IRQF_TRIGGER_MASK; + return (int)wifi_irqres->start; + } +#ifdef CUSTOM_OOB_GPIO_NUM + return CUSTOM_OOB_GPIO_NUM; +#else + return -1; +#endif +} + +int wifi_set_power(int on, unsigned long msec) +{ + DHD_ERROR(("%s = %d\n", __FUNCTION__, on)); + if (wifi_control_data && wifi_control_data->set_power) { + wifi_control_data->set_power(on); + } + if (msec) + msleep(msec); + return 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) +int wifi_get_mac_addr(unsigned char *buf) +{ + DHD_ERROR(("%s\n", __FUNCTION__)); + if (!buf) + return -EINVAL; + if (wifi_control_data && wifi_control_data->get_mac_addr) { + return wifi_control_data->get_mac_addr(buf); + } + return -EOPNOTSUPP; +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)) */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) +void *wifi_get_country_code(char *ccode) +{ + DHD_TRACE(("%s\n", __FUNCTION__)); + if (!ccode) + return NULL; + if (wifi_control_data && wifi_control_data->get_country_code) { + return wifi_control_data->get_country_code(ccode); + } + return NULL; +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) */ + +static int wifi_set_carddetect(int on) +{ + DHD_ERROR(("%s = %d\n", __FUNCTION__, on)); + if (wifi_control_data && wifi_control_data->set_carddetect) { + wifi_control_data->set_carddetect(on); + } + return 0; +} + +static int wifi_probe(struct platform_device *pdev) +{ + struct wifi_platform_data *wifi_ctrl = + (struct wifi_platform_data *)(pdev->dev.platform_data); + + DHD_ERROR(("## %s\n", __FUNCTION__)); + wifi_irqres = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "bcmdhd_wlan_irq"); + if (wifi_irqres == NULL) + wifi_irqres = platform_get_resource_byname(pdev, + IORESOURCE_IRQ, "bcm4329_wlan_irq"); + wifi_control_data = wifi_ctrl; + + wifi_set_power(1, 0); /* Power On */ + wifi_set_carddetect(1); /* CardDetect (0->1) */ + + up(&wifi_control_sem); + return 0; +} + +static int wifi_remove(struct platform_device *pdev) +{ + struct wifi_platform_data *wifi_ctrl = + (struct wifi_platform_data *)(pdev->dev.platform_data); + + DHD_ERROR(("## %s\n", __FUNCTION__)); + wifi_control_data = wifi_ctrl; + + wifi_set_power(0, 0); /* Power Off */ + wifi_set_carddetect(0); /* CardDetect (1->0) */ + + up(&wifi_control_sem); + return 0; +} + +static int wifi_suspend(struct platform_device *pdev, pm_message_t state) +{ + DHD_TRACE(("##> %s\n", __FUNCTION__)); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) + bcmsdh_oob_intr_set(0); +#endif + return 0; +} + +static int wifi_resume(struct platform_device *pdev) +{ + DHD_TRACE(("##> %s\n", __FUNCTION__)); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) && defined(OOB_INTR_ONLY) + if (dhd_os_check_if_up(bcmsdh_get_drvdata())) + bcmsdh_oob_intr_set(1); +#endif + return 0; +} + +static struct platform_driver wifi_device = { + .probe = wifi_probe, + .remove = wifi_remove, + .suspend = wifi_suspend, + .resume = wifi_resume, + .driver = { + .name = "bcmdhd_wlan", + } +}; + +static struct platform_driver wifi_device_legacy = { + .probe = wifi_probe, + .remove = wifi_remove, + .suspend = wifi_suspend, + .resume = wifi_resume, + .driver = { + .name = "bcm4329_wlan", + } +}; + +static int wifi_add_dev(void) +{ + DHD_TRACE(("## Calling platform_driver_register\n")); + platform_driver_register(&wifi_device); + platform_driver_register(&wifi_device_legacy); + return 0; +} + +static void wifi_del_dev(void) +{ + DHD_TRACE(("## Unregister platform_driver_register\n")); + platform_driver_unregister(&wifi_device); + platform_driver_unregister(&wifi_device_legacy); +} +#endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ diff --git a/drivers/net/wireless/bcmdhd/wl_android.h b/drivers/net/wireless/bcmdhd/wl_android.h new file mode 100644 index 0000000000000..3983306cfe38e --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_android.h @@ -0,0 +1,57 @@ +/* + * Linux cfg80211 driver - Android related functions + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_android.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $ + */ + +#include +#include +#include + +/** + * Android platform dependent functions, feel free to add Android specific functions here + * (save the macros in dhd). Please do NOT declare functions that are NOT exposed to dhd + * or cfg, define them as static in wl_android.c + */ + +/** + * wl_android_init will be called from module init function (dhd_module_init now), similarly + * wl_android_exit will be called from module exit function (dhd_module_cleanup now) + */ +int wl_android_init(void); +int wl_android_exit(void); +void wl_android_post_init(void); +int wl_android_wifi_on(struct net_device *dev); +int wl_android_wifi_off(struct net_device *dev); +int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd); + +#if defined(CONFIG_WIFI_CONTROL_FUNC) +int wl_android_wifictrl_func_add(void); +void wl_android_wifictrl_func_del(void); +void* wl_android_prealloc(int section, unsigned long size); + +int wifi_get_irq_number(unsigned long *irq_flags_ptr); +int wifi_set_power(int on, unsigned long msec); +int wifi_get_mac_addr(unsigned char *buf); +void *wifi_get_country_code(char *ccode); +#endif /* CONFIG_WIFI_CONTROL_FUNC */ diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c new file mode 100644 index 0000000000000..cccc4d1df6d51 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -0,0 +1,7730 @@ +/* + * Linux cfg80211 driver + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfg80211.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static struct device *cfg80211_parent_dev = NULL; +static int vsdb_supported = 0; +struct wl_priv *wlcfg_drv_priv = NULL; + +u32 wl_dbg_level = WL_DBG_ERR; + +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#define MAX_WAIT_TIME 1500 +#define WL_SCAN_ACTIVE_TIME 40 +#define WL_SCAN_PASSIVE_TIME 130 +#define WL_FRAME_LEN 300 + +#define DNGL_FUNC(func, parameters) func parameters; +#define COEX_DHCP + + +/* This is to override regulatory domains defined in cfg80211 module (reg.c) + * By default world regulatory domain defined in reg.c puts the flags NL80211_RRF_PASSIVE_SCAN + * and NL80211_RRF_NO_IBSS for 5GHz channels (for 36..48 and 149..165). + * With respect to these flags, wpa_supplicant doesn't start p2p operations on 5GHz channels. + * All the chnages in world regulatory domain are to be done here. + */ +static const struct ieee80211_regdomain brcm_regdom = { + .n_reg_rules = 4, + .alpha2 = "99", + .reg_rules = { + /* IEEE 802.11b/g, channels 1..11 */ + REG_RULE(2412-10, 2472+10, 40, 6, 20, 0), + /* IEEE 802.11b/g, channels 12..13. No HT40 + * channel fits here. + */ + /* If any */ + /* + * IEEE 802.11 channel 14 - is for JP only, + * we need cfg80211 to allow it (reg_flags = 0); so that + * hostapd could request auto channel by sending down ch 14 + */ + REG_RULE(2484-10, 2484+10, 20, 6, 20, + NL80211_RRF_PASSIVE_SCAN | + NL80211_RRF_NO_IBSS | + NL80211_RRF_NO_OFDM), + /* IEEE 802.11a, channel 36..64 */ + REG_RULE(5150-10, 5350+10, 40, 6, 20, 0), + /* IEEE 802.11a, channel 100..165 */ + REG_RULE(5470-10, 5850+10, 40, 6, 20, 0), } +}; + + +/* Data Element Definitions */ +#define WPS_ID_CONFIG_METHODS 0x1008 +#define WPS_ID_REQ_TYPE 0x103A +#define WPS_ID_DEVICE_NAME 0x1011 +#define WPS_ID_VERSION 0x104A +#define WPS_ID_DEVICE_PWD_ID 0x1012 +#define WPS_ID_REQ_DEV_TYPE 0x106A +#define WPS_ID_SELECTED_REGISTRAR_CONFIG_METHODS 0x1053 +#define WPS_ID_PRIM_DEV_TYPE 0x1054 + +/* Device Password ID */ +#define DEV_PW_DEFAULT 0x0000 +#define DEV_PW_USER_SPECIFIED 0x0001, +#define DEV_PW_MACHINE_SPECIFIED 0x0002 +#define DEV_PW_REKEY 0x0003 +#define DEV_PW_PUSHBUTTON 0x0004 +#define DEV_PW_REGISTRAR_SPECIFIED 0x0005 + +/* Config Methods */ +#define WPS_CONFIG_USBA 0x0001 +#define WPS_CONFIG_ETHERNET 0x0002 +#define WPS_CONFIG_LABEL 0x0004 +#define WPS_CONFIG_DISPLAY 0x0008 +#define WPS_CONFIG_EXT_NFC_TOKEN 0x0010 +#define WPS_CONFIG_INT_NFC_TOKEN 0x0020 +#define WPS_CONFIG_NFC_INTERFACE 0x0040 +#define WPS_CONFIG_PUSHBUTTON 0x0080 +#define WPS_CONFIG_KEYPAD 0x0100 +#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280 +#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480 +#define WPS_CONFIG_VIRT_DISPLAY 0x2008 +#define WPS_CONFIG_PHY_DISPLAY 0x4008 + +/* + * cfg80211_ops api/callback list + */ +static s32 wl_frame_get_mgmt(u16 fc, const struct ether_addr *da, + const struct ether_addr *sa, const struct ether_addr *bssid, + u8 **pheader, u32 *body_len, u8 *pbody); +static s32 __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_scan_request *request, + struct cfg80211_ssid *this_ssid); +static s32 wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_scan_request *request); +static s32 wl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed); +static s32 wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params); +static s32 wl_cfg80211_leave_ibss(struct wiphy *wiphy, + struct net_device *dev); +static s32 wl_cfg80211_get_station(struct wiphy *wiphy, + struct net_device *dev, u8 *mac, + struct station_info *sinfo); +static s32 wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, bool enabled, + s32 timeout); +static int wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme); +static s32 wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code); +static s32 wl_cfg80211_set_tx_power(struct wiphy *wiphy, + enum nl80211_tx_power_setting type, + s32 dbm); +static s32 wl_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm); +static s32 wl_cfg80211_config_default_key(struct wiphy *wiphy, + struct net_device *dev, + u8 key_idx, bool unicast, bool multicast); +static s32 wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, const u8 *mac_addr, + struct key_params *params); +static s32 wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, const u8 *mac_addr); +static s32 wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, const u8 *mac_addr, + void *cookie, void (*callback) (void *cookie, + struct key_params *params)); +static s32 wl_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, + struct net_device *dev, u8 key_idx); +static s32 wl_cfg80211_resume(struct wiphy *wiphy); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) +static s32 wl_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow); +#else +static s32 wl_cfg80211_suspend(struct wiphy *wiphy); +#endif +static s32 wl_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa); +static s32 wl_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa); +static s32 wl_cfg80211_flush_pmksa(struct wiphy *wiphy, + struct net_device *dev); +static s32 wl_notify_escan_complete(struct wl_priv *wl, + struct net_device *ndev, bool aborted, bool fw_abort); +/* + * event & event Q handlers for cfg80211 interfaces + */ +static s32 wl_create_event_handler(struct wl_priv *wl); +static void wl_destroy_event_handler(struct wl_priv *wl); +static s32 wl_event_handler(void *data); +static void wl_init_eq(struct wl_priv *wl); +static void wl_flush_eq(struct wl_priv *wl); +static unsigned long wl_lock_eq(struct wl_priv *wl); +static void wl_unlock_eq(struct wl_priv *wl, unsigned long flags); +static void wl_init_eq_lock(struct wl_priv *wl); +static void wl_init_event_handler(struct wl_priv *wl); +static struct wl_event_q *wl_deq_event(struct wl_priv *wl); +static s32 wl_enq_event(struct wl_priv *wl, struct net_device *ndev, u32 type, + const wl_event_msg_t *msg, void *data); +static void wl_put_event(struct wl_event_q *e); +static void wl_wakeup_event(struct wl_priv *wl); +static s32 wl_notify_connect_status_ap(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +static s32 wl_notify_connect_status(struct wl_priv *wl, + struct net_device *ndev, + const wl_event_msg_t *e, void *data); +static s32 wl_notify_roaming_status(struct wl_priv *wl, + struct net_device *ndev, + const wl_event_msg_t *e, void *data); +static s32 wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +static s32 wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, bool completed); +static s32 wl_ibss_join_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, bool completed); +static s32 wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +static s32 wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +#ifdef WL_SCHED_SCAN +static s32 +wl_notify_sched_scan_results(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +#endif /* WL_SCHED_SCAN */ +#ifdef PNO_SUPPORT +static s32 wl_notify_pfn_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +#endif /* PNO_SUPPORT */ +/* + * register/deregister parent device + */ +static void wl_cfg80211_clear_parent_dev(void); + +/* + * cfg80211 set_wiphy_params utilities + */ +static s32 wl_set_frag(struct net_device *dev, u32 frag_threshold); +static s32 wl_set_rts(struct net_device *dev, u32 frag_threshold); +static s32 wl_set_retry(struct net_device *dev, u32 retry, bool l); + +/* + * wl profile utilities + */ +static s32 wl_update_prof(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, s32 item); +static void *wl_read_prof(struct wl_priv *wl, struct net_device *ndev, s32 item); +static void wl_init_prof(struct wl_priv *wl, struct net_device *ndev); + +/* + * cfg80211 connect utilites + */ +static s32 wl_set_wpa_version(struct net_device *dev, + struct cfg80211_connect_params *sme); +static s32 wl_set_auth_type(struct net_device *dev, + struct cfg80211_connect_params *sme); +static s32 wl_set_set_cipher(struct net_device *dev, + struct cfg80211_connect_params *sme); +static s32 wl_set_key_mgmt(struct net_device *dev, + struct cfg80211_connect_params *sme); +static s32 wl_set_set_sharedkey(struct net_device *dev, + struct cfg80211_connect_params *sme); +static s32 wl_get_assoc_ies(struct wl_priv *wl, struct net_device *ndev); +static void wl_ch_to_chanspec(int ch, + struct wl_join_params *join_params, size_t *join_params_size); + +/* + * information element utilities + */ +static void wl_rst_ie(struct wl_priv *wl); +static __used s32 wl_add_ie(struct wl_priv *wl, u8 t, u8 l, u8 *v); +static s32 wl_mrg_ie(struct wl_priv *wl, u8 *ie_stream, u16 ie_size); +static s32 wl_cp_ie(struct wl_priv *wl, u8 *dst, u16 dst_size); +static u32 wl_get_ielen(struct wl_priv *wl); + + +static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *dev); +static void wl_free_wdev(struct wl_priv *wl); + +static s32 wl_inform_bss(struct wl_priv *wl); +static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi); +static s32 wl_inform_ibss(struct wl_priv *wl, const u8 *bssid); +static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev); +static chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy); + +static s32 wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, const u8 *mac_addr, + struct key_params *params); +/* + * key indianess swap utilities + */ +static void swap_key_from_BE(struct wl_wsec_key *key); +static void swap_key_to_BE(struct wl_wsec_key *key); + +/* + * wl_priv memory init/deinit utilities + */ +static s32 wl_init_priv_mem(struct wl_priv *wl); +static void wl_deinit_priv_mem(struct wl_priv *wl); + +static void wl_delay(u32 ms); + +/* + * ibss mode utilities + */ +static bool wl_is_ibssmode(struct wl_priv *wl, struct net_device *ndev); +static __used bool wl_is_ibssstarter(struct wl_priv *wl); + +/* + * link up/down , default configuration utilities + */ +static s32 __wl_cfg80211_up(struct wl_priv *wl); +static s32 __wl_cfg80211_down(struct wl_priv *wl); +static s32 wl_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add); +static bool wl_is_linkdown(struct wl_priv *wl, const wl_event_msg_t *e); +static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e, struct net_device *ndev); +static bool wl_is_nonetwork(struct wl_priv *wl, const wl_event_msg_t *e); +static void wl_link_up(struct wl_priv *wl); +static void wl_link_down(struct wl_priv *wl); +static s32 wl_config_ifmode(struct wl_priv *wl, struct net_device *ndev, s32 iftype); +static void wl_init_conf(struct wl_conf *conf); + +/* + * iscan handler + */ +static void wl_iscan_timer(unsigned long data); +static void wl_term_iscan(struct wl_priv *wl); +static s32 wl_init_scan(struct wl_priv *wl); +static s32 wl_iscan_thread(void *data); +static s32 wl_run_iscan(struct wl_iscan_ctrl *iscan, struct cfg80211_scan_request *request, + u16 action); +static s32 wl_do_iscan(struct wl_priv *wl, struct cfg80211_scan_request *request); +static s32 wl_wakeup_iscan(struct wl_iscan_ctrl *iscan); +static s32 wl_invoke_iscan(struct wl_priv *wl); +static s32 wl_get_iscan_results(struct wl_iscan_ctrl *iscan, u32 *status, + struct wl_scan_results **bss_list); +static void wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted); +static void wl_init_iscan_handler(struct wl_iscan_ctrl *iscan); +static s32 wl_iscan_done(struct wl_priv *wl); +static s32 wl_iscan_pending(struct wl_priv *wl); +static s32 wl_iscan_inprogress(struct wl_priv *wl); +static s32 wl_iscan_aborted(struct wl_priv *wl); + +/* + * find most significant bit set + */ +static __used u32 wl_find_msb(u16 bit16); + +/* + * rfkill support + */ +static int wl_setup_rfkill(struct wl_priv *wl, bool setup); +static int wl_rfkill_set(void *data, bool blocked); + +static wl_scan_params_t *wl_cfg80211_scan_alloc_params(int channel, + int nprobes, int *out_params_size); +static void get_primary_mac(struct wl_priv *wl, struct ether_addr *mac); + +/* + * Some external functions, TODO: move them to dhd_linux.h + */ +int dhd_add_monitor(char *name, struct net_device **new_ndev); +int dhd_del_monitor(struct net_device *ndev); +int dhd_monitor_init(void *dhd_pub); +int dhd_monitor_uninit(void); +int dhd_start_xmit(struct sk_buff *skb, struct net_device *net); + +#define CHECK_SYS_UP(wlpriv) \ +do { \ + struct net_device *ndev = wl_to_prmry_ndev(wlpriv); \ + if (unlikely(!wl_get_drv_status(wlpriv, READY, ndev))) { \ + WL_INFO(("device is not ready\n")); \ + return -EIO; \ + } \ +} while (0) + + +#define IS_WPA_AKM(akm) ((akm) == RSN_AKM_NONE || \ + (akm) == RSN_AKM_UNSPECIFIED || \ + (akm) == RSN_AKM_PSK) + + +extern int dhd_wait_pend8021x(struct net_device *dev); + +#if (WL_DBG_LEVEL > 0) +#define WL_DBG_ESTR_MAX 50 +static s8 wl_dbg_estr[][WL_DBG_ESTR_MAX] = { + "SET_SSID", "JOIN", "START", "AUTH", "AUTH_IND", + "DEAUTH", "DEAUTH_IND", "ASSOC", "ASSOC_IND", "REASSOC", + "REASSOC_IND", "DISASSOC", "DISASSOC_IND", "QUIET_START", "QUIET_END", + "BEACON_RX", "LINK", "MIC_ERROR", "NDIS_LINK", "ROAM", + "TXFAIL", "PMKID_CACHE", "RETROGRADE_TSF", "PRUNE", "AUTOAUTH", + "EAPOL_MSG", "SCAN_COMPLETE", "ADDTS_IND", "DELTS_IND", "BCNSENT_IND", + "BCNRX_MSG", "BCNLOST_MSG", "ROAM_PREP", "PFN_NET_FOUND", + "PFN_NET_LOST", + "RESET_COMPLETE", "JOIN_START", "ROAM_START", "ASSOC_START", + "IBSS_ASSOC", + "RADIO", "PSM_WATCHDOG", "WLC_E_CCX_ASSOC_START", "WLC_E_CCX_ASSOC_ABORT", + "PROBREQ_MSG", + "SCAN_CONFIRM_IND", "PSK_SUP", "COUNTRY_CODE_CHANGED", + "EXCEEDED_MEDIUM_TIME", "ICV_ERROR", + "UNICAST_DECODE_ERROR", "MULTICAST_DECODE_ERROR", "TRACE", + "WLC_E_BTA_HCI_EVENT", "IF", "WLC_E_P2P_DISC_LISTEN_COMPLETE", + "RSSI", "PFN_SCAN_COMPLETE", "WLC_E_EXTLOG_MSG", + "ACTION_FRAME", "ACTION_FRAME_COMPLETE", "WLC_E_PRE_ASSOC_IND", + "WLC_E_PRE_REASSOC_IND", "WLC_E_CHANNEL_ADOPTED", "WLC_E_AP_STARTED", + "WLC_E_DFS_AP_STOP", "WLC_E_DFS_AP_RESUME", "WLC_E_WAI_STA_EVENT", + "WLC_E_WAI_MSG", "WLC_E_ESCAN_RESULT", "WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE", + "WLC_E_PROBRESP_MSG", "WLC_E_P2P_PROBREQ_MSG", "WLC_E_DCS_REQUEST", "WLC_E_FIFO_CREDIT_MAP", + "WLC_E_ACTION_FRAME_RX", "WLC_E_WAKE_EVENT", "WLC_E_RM_COMPLETE" +}; +#endif /* WL_DBG_LEVEL */ + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel, _flags) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 5000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) +#define RATETAB_ENT(_rateid, _flags) \ + { \ + .bitrate = RATE_TO_BASE100KBPS(_rateid), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ + } + +static struct ieee80211_rate __wl_rates[] = { + RATETAB_ENT(WLC_RATE_1M, 0), + RATETAB_ENT(WLC_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(WLC_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(WLC_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(WLC_RATE_6M, 0), + RATETAB_ENT(WLC_RATE_9M, 0), + RATETAB_ENT(WLC_RATE_12M, 0), + RATETAB_ENT(WLC_RATE_18M, 0), + RATETAB_ENT(WLC_RATE_24M, 0), + RATETAB_ENT(WLC_RATE_36M, 0), + RATETAB_ENT(WLC_RATE_48M, 0), + RATETAB_ENT(WLC_RATE_54M, 0) +}; + +#define wl_a_rates (__wl_rates + 4) +#define wl_a_rates_size 8 +#define wl_g_rates (__wl_rates + 0) +#define wl_g_rates_size 12 + +static struct ieee80211_channel __wl_2ghz_channels[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0) +}; + +static struct ieee80211_channel __wl_5ghz_a_channels[] = { + CHAN5G(34, 0), CHAN5G(36, 0), + CHAN5G(38, 0), CHAN5G(40, 0), + CHAN5G(42, 0), CHAN5G(44, 0), + CHAN5G(46, 0), CHAN5G(48, 0), + CHAN5G(52, 0), CHAN5G(56, 0), + CHAN5G(60, 0), CHAN5G(64, 0), + CHAN5G(100, 0), CHAN5G(104, 0), + CHAN5G(108, 0), CHAN5G(112, 0), + CHAN5G(116, 0), CHAN5G(120, 0), + CHAN5G(124, 0), CHAN5G(128, 0), + CHAN5G(132, 0), CHAN5G(136, 0), + CHAN5G(140, 0), CHAN5G(149, 0), + CHAN5G(153, 0), CHAN5G(157, 0), + CHAN5G(161, 0), CHAN5G(165, 0) +}; + +static struct ieee80211_supported_band __wl_band_2ghz = { + .band = IEEE80211_BAND_2GHZ, + .channels = __wl_2ghz_channels, + .n_channels = ARRAY_SIZE(__wl_2ghz_channels), + .bitrates = wl_g_rates, + .n_bitrates = wl_g_rates_size +}; + +static struct ieee80211_supported_band __wl_band_5ghz_a = { + .band = IEEE80211_BAND_5GHZ, + .channels = __wl_5ghz_a_channels, + .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels), + .bitrates = wl_a_rates, + .n_bitrates = wl_a_rates_size +}; + +static const u32 __wl_cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_AES_CMAC, +}; + +/* There isn't a lot of sense in it, but you can transmit anything you like */ +static const struct ieee80211_txrx_stypes +wl_cfg80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_ADHOC] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) + }, + [NL80211_IFTYPE_STATION] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_AP] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | + BIT(IEEE80211_STYPE_DISASSOC >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | + BIT(IEEE80211_STYPE_DEAUTH >> 4) | + BIT(IEEE80211_STYPE_ACTION >> 4) + }, + [NL80211_IFTYPE_AP_VLAN] = { + /* copy AP */ + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | + BIT(IEEE80211_STYPE_DISASSOC >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | + BIT(IEEE80211_STYPE_DEAUTH >> 4) | + BIT(IEEE80211_STYPE_ACTION >> 4) + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | + BIT(IEEE80211_STYPE_DISASSOC >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | + BIT(IEEE80211_STYPE_DEAUTH >> 4) | + BIT(IEEE80211_STYPE_ACTION >> 4) + } +}; + +static void swap_key_from_BE(struct wl_wsec_key *key) +{ + key->index = htod32(key->index); + key->len = htod32(key->len); + key->algo = htod32(key->algo); + key->flags = htod32(key->flags); + key->rxiv.hi = htod32(key->rxiv.hi); + key->rxiv.lo = htod16(key->rxiv.lo); + key->iv_initialized = htod32(key->iv_initialized); +} + +static void swap_key_to_BE(struct wl_wsec_key *key) +{ + key->index = dtoh32(key->index); + key->len = dtoh32(key->len); + key->algo = dtoh32(key->algo); + key->flags = dtoh32(key->flags); + key->rxiv.hi = dtoh32(key->rxiv.hi); + key->rxiv.lo = dtoh16(key->rxiv.lo); + key->iv_initialized = dtoh32(key->iv_initialized); +} + +/* For debug: Dump the contents of the encoded wps ie buffe */ +static void +wl_validate_wps_ie(char *wps_ie, bool *pbc) +{ + #define WPS_IE_FIXED_LEN 6 + u16 len = (u16) wps_ie[TLV_LEN_OFF]; + u8 *subel = wps_ie+ WPS_IE_FIXED_LEN; + u16 subelt_id; + u16 subelt_len; + u16 val; + u8 *valptr = (uint8*) &val; + + WL_DBG(("wps_ie len=%d\n", len)); + + len -= 4; /* for the WPS IE's OUI, oui_type fields */ + + while (len >= 4) { /* must have attr id, attr len fields */ + valptr[0] = *subel++; + valptr[1] = *subel++; + subelt_id = HTON16(val); + + valptr[0] = *subel++; + valptr[1] = *subel++; + subelt_len = HTON16(val); + + len -= 4; /* for the attr id, attr len fields */ + len -= subelt_len; /* for the remaining fields in this attribute */ + WL_DBG((" subel=%p, subelt_id=0x%x subelt_len=%u\n", + subel, subelt_id, subelt_len)); + + if (subelt_id == WPS_ID_VERSION) { + WL_DBG((" attr WPS_ID_VERSION: %u\n", *subel)); + } else if (subelt_id == WPS_ID_REQ_TYPE) { + WL_DBG((" attr WPS_ID_REQ_TYPE: %u\n", *subel)); + } else if (subelt_id == WPS_ID_CONFIG_METHODS) { + valptr[0] = *subel; + valptr[1] = *(subel + 1); + WL_DBG((" attr WPS_ID_CONFIG_METHODS: %x\n", HTON16(val))); + } else if (subelt_id == WPS_ID_DEVICE_NAME) { + char devname[100]; + memcpy(devname, subel, subelt_len); + devname[subelt_len] = '\0'; + WL_DBG((" attr WPS_ID_DEVICE_NAME: %s (len %u)\n", + devname, subelt_len)); + } else if (subelt_id == WPS_ID_DEVICE_PWD_ID) { + valptr[0] = *subel; + valptr[1] = *(subel + 1); + WL_DBG((" attr WPS_ID_DEVICE_PWD_ID: %u\n", HTON16(val))); + *pbc = (HTON16(val) == DEV_PW_PUSHBUTTON) ? true : false; + } else if (subelt_id == WPS_ID_PRIM_DEV_TYPE) { + valptr[0] = *subel; + valptr[1] = *(subel + 1); + WL_DBG((" attr WPS_ID_PRIM_DEV_TYPE: cat=%u \n", HTON16(val))); + valptr[0] = *(subel + 6); + valptr[1] = *(subel + 7); + WL_DBG((" attr WPS_ID_PRIM_DEV_TYPE: subcat=%u\n", HTON16(val))); + } else if (subelt_id == WPS_ID_REQ_DEV_TYPE) { + valptr[0] = *subel; + valptr[1] = *(subel + 1); + WL_DBG((" attr WPS_ID_REQ_DEV_TYPE: cat=%u\n", HTON16(val))); + valptr[0] = *(subel + 6); + valptr[1] = *(subel + 7); + WL_DBG((" attr WPS_ID_REQ_DEV_TYPE: subcat=%u\n", HTON16(val))); + } else if (subelt_id == WPS_ID_SELECTED_REGISTRAR_CONFIG_METHODS) { + valptr[0] = *subel; + valptr[1] = *(subel + 1); + WL_DBG((" attr WPS_ID_SELECTED_REGISTRAR_CONFIG_METHODS" + ": cat=%u\n", HTON16(val))); + } else { + WL_DBG((" unknown attr 0x%x\n", subelt_id)); + } + + subel += subelt_len; + } +} + +static chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy) +{ + if (vsdb_supported) { + return wf_chspec_aton(WL_P2P_TEMP_CHAN); + } + else { + chanspec_t chspec; + int err = 0; + struct wl_priv *wl = wiphy_priv(wiphy); + struct net_device *dev = wl_to_prmry_ndev(wl); + struct ether_addr bssid; + struct wl_bss_info *bss = NULL; + if ((err = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, sizeof(bssid), false))) { + /* STA interface is not associated. So start the new interface on a temp + * channel . Later proper channel will be applied by the above framework + * via set_channel (cfg80211 API). + */ + WL_DBG(("Not associated. Return a temp channel. \n")); + return wf_chspec_aton(WL_P2P_TEMP_CHAN); + } + + + *(u32 *) wl->extra_buf = htod32(WL_EXTRA_BUF_MAX); + if ((err = wldev_ioctl(dev, WLC_GET_BSS_INFO, wl->extra_buf, + WL_EXTRA_BUF_MAX, false))) { + WL_ERR(("Failed to get associated bss info, use temp channel \n")); + chspec = wf_chspec_aton(WL_P2P_TEMP_CHAN); + } + else { + bss = (struct wl_bss_info *) (wl->extra_buf + 4); + chspec = bss->chanspec; + WL_DBG(("Valid BSS Found. chanspec:%d \n", bss->chanspec)); + } + return chspec; + } +} + +static struct net_device* wl_cfg80211_add_monitor_if(char *name) +{ + int ret = 0; + struct net_device* ndev = NULL; + + ret = dhd_add_monitor(name, &ndev); + WL_INFO(("wl_cfg80211_add_monitor_if net device returned: 0x%p\n", ndev)); + return ndev; +} + +static struct net_device * +wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + s32 err; + s32 timeout = -1; + s32 wlif_type = -1; + s32 mode = 0; +#if defined(WL_ENABLE_P2P_IF) + s32 dhd_mode = 0; +#endif /* (WL_ENABLE_P2P_IF) */ + chanspec_t chspec; + struct wl_priv *wl = wiphy_priv(wiphy); + struct net_device *_ndev; + struct ether_addr primary_mac; + int (*net_attach)(void *dhdp, int ifidx); + bool rollback_lock = false; + + /* Use primary I/F for sending cmds down to firmware */ + _ndev = wl_to_prmry_ndev(wl); + + WL_DBG(("if name: %s, type: %d\n", name, type)); + switch (type) { + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_MESH_POINT: + WL_ERR(("Unsupported interface type\n")); + mode = WL_MODE_IBSS; + return NULL; + case NL80211_IFTYPE_MONITOR: + return wl_cfg80211_add_monitor_if(name); + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_STATION: + wlif_type = WL_P2P_IF_CLIENT; + mode = WL_MODE_BSS; + break; + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_AP: + wlif_type = WL_P2P_IF_GO; + mode = WL_MODE_AP; + break; + default: + WL_ERR(("Unsupported interface type\n")); + return NULL; + break; + } + + if (!name) { + WL_ERR(("name is NULL\n")); + return NULL; + } + if (wl->iface_cnt == IFACE_MAX_CNT) + return ERR_PTR(-ENOMEM); + if (wl->p2p_supported && (wlif_type != -1)) { + if (wl_get_p2p_status(wl, IF_DELETING)) { + /* wait till IF_DEL is complete + * release the lock for the unregister to proceed + */ + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = true; + } + WL_INFO(("%s: Released the lock and wait till IF_DEL is complete\n", + __func__)); + timeout = wait_event_interruptible_timeout(wl->netif_change_event, + (wl_get_p2p_status(wl, IF_DELETING) == false), + msecs_to_jiffies(MAX_WAIT_TIME)); + + /* put back the rtnl_lock again */ + if (rollback_lock) { + rtnl_lock(); + rollback_lock = false; + } + if (timeout > 0) { + WL_ERR(("IF DEL is Success\n")); + + } else { + WL_ERR(("timeount < 0, return -EAGAIN\n")); + return ERR_PTR(-EAGAIN); + } + } + if (wl->p2p && !wl->p2p->on && strstr(name, WL_P2P_INTERFACE_PREFIX)) { + p2p_on(wl) = true; + wl_cfgp2p_set_firm_p2p(wl); + wl_cfgp2p_init_discovery(wl); + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, + &wl->p2p->dev_addr, &wl->p2p->int_addr); + } + + memset(wl->p2p->vir_ifname, 0, IFNAMSIZ); + strncpy(wl->p2p->vir_ifname, name, IFNAMSIZ - 1); + + wldev_iovar_setint(_ndev, "mpc", 0); + wl_notify_escan_complete(wl, _ndev, true, true); + /* In concurrency case, STA may be already associated in a particular channel. + * so retrieve the current channel of primary interface and then start the virtual + * interface on that. + */ + chspec = wl_cfg80211_get_shared_freq(wiphy); + + /* For P2P mode, use P2P-specific driver features to create the + * bss: "wl p2p_ifadd" + */ + wl_set_p2p_status(wl, IF_ADD); + err = wl_cfgp2p_ifadd(wl, &wl->p2p->int_addr, htod32(wlif_type), chspec); + + if (unlikely(err)) { + WL_ERR((" virtual iface add failed (%d) \n", err)); + return ERR_PTR(-ENOMEM); + } + + timeout = wait_event_interruptible_timeout(wl->netif_change_event, + (wl_get_p2p_status(wl, IF_ADD) == false), + msecs_to_jiffies(MAX_WAIT_TIME)); + if (timeout > 0 && (!wl_get_p2p_status(wl, IF_ADD))) { + + struct wireless_dev *vwdev; + vwdev = kzalloc(sizeof(*vwdev), GFP_KERNEL); + if (unlikely(!vwdev)) { + WL_ERR(("Could not allocate wireless device\n")); + return ERR_PTR(-ENOMEM); + } + vwdev->wiphy = wl->wdev->wiphy; + WL_INFO((" virtual interface(%s) is created memalloc done \n", + wl->p2p->vir_ifname)); + vwdev->iftype = type; + _ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); + _ndev->ieee80211_ptr = vwdev; + SET_NETDEV_DEV(_ndev, wiphy_dev(vwdev->wiphy)); + vwdev->netdev = _ndev; + wl_set_drv_status(wl, READY, _ndev); + wl->p2p->vif_created = true; + wl_set_mode_by_netdev(wl, _ndev, mode); + net_attach = wl_to_p2p_bss_private(wl, P2PAPI_BSSCFG_CONNECTION); + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = true; + } + if (net_attach && !net_attach(wl->pub, _ndev->ifindex)) { + wl_alloc_netinfo(wl, _ndev, vwdev, mode); + WL_ERR((" virtual interface(%s) is " + "created net attach done\n", wl->p2p->vir_ifname)); +#if defined(WL_ENABLE_P2P_IF) + if (type == NL80211_IFTYPE_P2P_CLIENT) + dhd_mode = P2P_GC_ENABLED; + else if (type == NL80211_IFTYPE_P2P_GO) + dhd_mode = P2P_GO_ENABLED; + DNGL_FUNC(dhd_cfg80211_set_p2p_info, (wl, dhd_mode)); +#endif /* (WL_ENABLE_P2P_IF) */ + /* Start the P2P I/F with PM disabled. Enable PM from + * the framework + */ + if ((type == NL80211_IFTYPE_P2P_CLIENT) || ( + type == NL80211_IFTYPE_P2P_GO)) + vwdev->ps = NL80211_PS_DISABLED; + } else { + /* put back the rtnl_lock again */ + if (rollback_lock) + rtnl_lock(); + goto fail; + } + /* put back the rtnl_lock again */ + if (rollback_lock) + rtnl_lock(); + return _ndev; + + } else { + wl_clr_p2p_status(wl, IF_ADD); + WL_ERR((" virtual interface(%s) is not created \n", wl->p2p->vir_ifname)); + memset(wl->p2p->vir_ifname, '\0', IFNAMSIZ); + wl->p2p->vif_created = false; + } + } +fail: + return ERR_PTR(-ENODEV); +} + +static s32 +wl_cfg80211_del_virtual_iface(struct wiphy *wiphy, struct net_device *dev) +{ + struct ether_addr p2p_mac; + struct wl_priv *wl = wiphy_priv(wiphy); + s32 timeout = -1; + s32 ret = 0; + WL_DBG(("Enter\n")); + + if (wl->p2p_net == dev) { + /* Since there is no ifidx corresponding to p2p0, cmds to + * firmware should be routed through primary I/F + */ + dev = wl_to_prmry_ndev(wl); + } + + if (wl->p2p_supported) { + memcpy(p2p_mac.octet, wl->p2p->int_addr.octet, ETHER_ADDR_LEN); + + /* Clear GO_NEG_PHASE bit to take care of GO-NEG-FAIL cases + */ + WL_DBG(("P2P: GO_NEG_PHASE status cleared ")); + wl_clr_p2p_status(wl, GO_NEG_PHASE); + if (wl->p2p->vif_created) { + if (wl_get_drv_status(wl, SCANNING, dev)) { + wl_notify_escan_complete(wl, dev, true, true); + } + wldev_iovar_setint(dev, "mpc", 1); + wl_set_p2p_status(wl, IF_DELETING); + ret = wl_cfgp2p_ifdel(wl, &p2p_mac); + /* Firmware could not delete the interface so we will not get WLC_E_IF + * event for cleaning the dhd virtual nw interace + * So lets do it here. Failures from fw will ensure the application to do + * ifconfig down and up sequnce, which will reload the fw + * however we should cleanup the linux network virtual interfaces + */ + /* Request framework to RESET and clean up */ + if (ret) { + struct net_device *ndev = wl_to_prmry_ndev(wl); + WL_ERR(("Firmware returned an error (%d) from p2p_ifdel" + "HANG Notification sent to %s\n", ret, ndev->name)); + wl_cfg80211_hang(ndev, WLAN_REASON_UNSPECIFIED); + } + + /* Wait for any pending scan req to get aborted from the sysioc context */ + timeout = wait_event_interruptible_timeout(wl->netif_change_event, + (wl->p2p->vif_created == false), + msecs_to_jiffies(MAX_WAIT_TIME)); + if (timeout > 0 && (wl->p2p->vif_created == false)) { + WL_DBG(("IFDEL operation done\n")); +#if defined(WL_ENABLE_P2P_IF) + DNGL_FUNC(dhd_cfg80211_clean_p2p_info, (wl)); +#endif /* (WL_ENABLE_P2P_IF)) */ + } else { + WL_ERR(("IFDEL didn't complete properly\n")); + } + ret = dhd_del_monitor(dev); + } + } + return ret; +} + +static s32 +wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + s32 ap = 0; + s32 infra = 0; + s32 err = BCME_OK; + s32 timeout = -1; + s32 wlif_type; + s32 mode = 0; + chanspec_t chspec; + struct wl_priv *wl = wiphy_priv(wiphy); + + WL_DBG(("Enter type %d\n", type)); + switch (type) { + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_MESH_POINT: + ap = 1; + WL_ERR(("type (%d) : currently we do not support this type\n", + type)); + break; + case NL80211_IFTYPE_ADHOC: + mode = WL_MODE_IBSS; + break; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + mode = WL_MODE_BSS; + infra = 1; + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_P2P_GO: + mode = WL_MODE_AP; + ap = 1; + break; + default: + return -EINVAL; + } + WL_DBG(("%s : ap (%d), infra (%d), iftype: (%d)\n", ndev->name, ap, infra, type)); + + if (ap) { + wl_set_mode_by_netdev(wl, ndev, mode); + if (wl->p2p_supported && wl->p2p->vif_created) { + WL_DBG(("p2p_vif_created (%d) p2p_on (%d)\n", wl->p2p->vif_created, + p2p_on(wl))); + wldev_iovar_setint(ndev, "mpc", 0); + wl_notify_escan_complete(wl, ndev, true, true); + + /* In concurrency case, STA may be already associated in a particular + * channel. so retrieve the current channel of primary interface and + * then start the virtual interface on that. + */ + chspec = wl_cfg80211_get_shared_freq(wiphy); + + wlif_type = WL_P2P_IF_GO; + WL_ERR(("%s : ap (%d), infra (%d), iftype: (%d)\n", + ndev->name, ap, infra, type)); + wl_set_p2p_status(wl, IF_CHANGING); + wl_clr_p2p_status(wl, IF_CHANGED); + err = wl_cfgp2p_ifchange(wl, &wl->p2p->int_addr, htod32(wlif_type), chspec); + timeout = wait_event_interruptible_timeout(wl->netif_change_event, + (wl_get_p2p_status(wl, IF_CHANGED) == true), + msecs_to_jiffies(MAX_WAIT_TIME)); + wl_set_mode_by_netdev(wl, ndev, mode); + wl_clr_p2p_status(wl, IF_CHANGING); + wl_clr_p2p_status(wl, IF_CHANGED); + } else if (ndev == wl_to_prmry_ndev(wl) && + !wl_get_drv_status(wl, AP_CREATED, ndev)) { + wl_set_drv_status(wl, AP_CREATING, ndev); + if (!wl->ap_info && + !(wl->ap_info = kzalloc(sizeof(struct ap_info), GFP_KERNEL))) { + WL_ERR(("struct ap_saved_ie allocation failed\n")); + return -ENOMEM; + } + } else { + WL_ERR(("Cannot change the interface for GO or SOFTAP\n")); + return -EINVAL; + } + } else { + infra = htod32(infra); + err = wldev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(s32), true); + if (err) { + WL_ERR(("WLC_SET_INFRA error (%d)\n", err)); + return -EAGAIN; + } + wl_set_mode_by_netdev(wl, ndev, mode); + } + + ndev->ieee80211_ptr->iftype = type; + return 0; +} + +s32 +wl_cfg80211_notify_ifadd(struct net_device *ndev, s32 idx, s32 bssidx, + void* _net_attach) +{ + struct wl_priv *wl = wlcfg_drv_priv; + s32 ret = BCME_OK; + WL_DBG(("Enter")); + if (!ndev) { + WL_ERR(("net is NULL\n")); + return 0; + } + if (wl->p2p_supported && wl_get_p2p_status(wl, IF_ADD)) { + WL_DBG(("IF_ADD event called from dongle, old interface name: %s," + "new name: %s\n", ndev->name, wl->p2p->vir_ifname)); + /* Assign the net device to CONNECT BSSCFG */ + strncpy(ndev->name, wl->p2p->vir_ifname, IFNAMSIZ - 1); + wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION) = ndev; + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION) = bssidx; + wl_to_p2p_bss_private(wl, P2PAPI_BSSCFG_CONNECTION) = _net_attach; + ndev->ifindex = idx; + wl_clr_p2p_status(wl, IF_ADD); + + wake_up_interruptible(&wl->netif_change_event); + } else { + ret = BCME_NOTREADY; + } + return ret; +} + +s32 +wl_cfg80211_notify_ifdel(void) +{ + struct wl_priv *wl = wlcfg_drv_priv; + + WL_DBG(("Enter \n")); + wl_clr_p2p_status(wl, IF_DELETING); + + return 0; +} + +s32 +wl_cfg80211_ifdel_ops(struct net_device *ndev) +{ + struct wl_priv *wl = wlcfg_drv_priv; + bool rollback_lock = false; + s32 index = 0; + + if (!ndev || !ndev->name) { + WL_ERR(("net is NULL\n")); + return 0; + } + + if (p2p_is_on(wl) && wl->p2p->vif_created && + wl_get_p2p_status(wl, IF_DELETING)) { + if (wl->scan_request && + (wl->escan_info.ndev == ndev)) { + /* Abort any pending scan requests */ + wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + if (!rtnl_is_locked()) { + rtnl_lock(); + rollback_lock = true; + } + WL_DBG(("ESCAN COMPLETED\n")); + wl_notify_escan_complete(wl, ndev, true, false); + if (rollback_lock) + rtnl_unlock(); + } + WL_ERR(("IF_DEL event called from dongle, net %x, vif name: %s\n", + (unsigned int)ndev, wl->p2p->vir_ifname)); + + memset(wl->p2p->vir_ifname, '\0', IFNAMSIZ); + index = wl_cfgp2p_find_idx(wl, ndev); + wl_to_p2p_bss_ndev(wl, index) = NULL; + wl_to_p2p_bss_bssidx(wl, index) = 0; + wl->p2p->vif_created = false; + wl_cfgp2p_clear_management_ie(wl, + index); + WL_DBG(("index : %d\n", index)); + + } + + /* Wake up any waiting thread */ + wake_up_interruptible(&wl->netif_change_event); + + return 0; +} + +s32 +wl_cfg80211_is_progress_ifadd(void) +{ + s32 is_progress = 0; + struct wl_priv *wl = wlcfg_drv_priv; + if (wl_get_p2p_status(wl, IF_ADD)) + is_progress = 1; + return is_progress; +} + +s32 +wl_cfg80211_is_progress_ifchange(void) +{ + s32 is_progress = 0; + struct wl_priv *wl = wlcfg_drv_priv; + if (wl_get_p2p_status(wl, IF_CHANGING)) + is_progress = 1; + return is_progress; +} + + +s32 +wl_cfg80211_notify_ifchange(void) +{ + struct wl_priv *wl = wlcfg_drv_priv; + if (wl_get_p2p_status(wl, IF_CHANGING)) { + wl_set_p2p_status(wl, IF_CHANGED); + wake_up_interruptible(&wl->netif_change_event); + } + return 0; +} + +static void wl_scan_prep(struct wl_scan_params *params, struct cfg80211_scan_request *request) +{ + u32 n_ssids; + u32 n_channels; + u16 channel; + chanspec_t chanspec; + s32 i, offset; + char *ptr; + wlc_ssid_t ssid; + + memcpy(¶ms->bssid, ðer_bcast, ETHER_ADDR_LEN); + params->bss_type = DOT11_BSSTYPE_ANY; + params->scan_type = 0; + params->nprobes = -1; + params->active_time = -1; + params->passive_time = -1; + params->home_time = -1; + params->channel_num = 0; + memset(¶ms->ssid, 0, sizeof(wlc_ssid_t)); + + WL_SCAN(("Preparing Scan request\n")); + WL_SCAN(("nprobes=%d\n", params->nprobes)); + WL_SCAN(("active_time=%d\n", params->active_time)); + WL_SCAN(("passive_time=%d\n", params->passive_time)); + WL_SCAN(("home_time=%d\n", params->home_time)); + WL_SCAN(("scan_type=%d\n", params->scan_type)); + + params->nprobes = htod32(params->nprobes); + params->active_time = htod32(params->active_time); + params->passive_time = htod32(params->passive_time); + params->home_time = htod32(params->home_time); + + /* if request is null just exit so it will be all channel broadcast scan */ + if (!request) + return; + + n_ssids = request->n_ssids; + n_channels = request->n_channels; + + /* Copy channel array if applicable */ + WL_SCAN(("### List of channelspecs to scan ###\n")); + if (n_channels > 0) { + for (i = 0; i < n_channels; i++) { + chanspec = 0; + channel = ieee80211_frequency_to_channel(request->channels[i]->center_freq); + if (request->channels[i]->band == IEEE80211_BAND_2GHZ) + chanspec |= WL_CHANSPEC_BAND_2G; + else + chanspec |= WL_CHANSPEC_BAND_5G; + + if (request->channels[i]->flags & IEEE80211_CHAN_NO_HT40) { + chanspec |= WL_CHANSPEC_BW_20; + chanspec |= WL_CHANSPEC_CTL_SB_NONE; + } else { + chanspec |= WL_CHANSPEC_BW_40; + if (request->channels[i]->flags & IEEE80211_CHAN_NO_HT40PLUS) + chanspec |= WL_CHANSPEC_CTL_SB_LOWER; + else + chanspec |= WL_CHANSPEC_CTL_SB_UPPER; + } + + params->channel_list[i] = channel; + params->channel_list[i] &= WL_CHANSPEC_CHAN_MASK; + params->channel_list[i] |= chanspec; + WL_SCAN(("Chan : %d, Channel spec: %x \n", + channel, params->channel_list[i])); + params->channel_list[i] = htod16(params->channel_list[i]); + } + } else { + WL_SCAN(("Scanning all channels\n")); + } + + /* Copy ssid array if applicable */ + WL_SCAN(("### List of SSIDs to scan ###\n")); + if (n_ssids > 0) { + offset = offsetof(wl_scan_params_t, channel_list) + n_channels * sizeof(u16); + offset = roundup(offset, sizeof(u32)); + ptr = (char*)params + offset; + for (i = 0; i < n_ssids; i++) { + memset(&ssid, 0, sizeof(wlc_ssid_t)); + ssid.SSID_len = request->ssids[i].ssid_len; + memcpy(ssid.SSID, request->ssids[i].ssid, ssid.SSID_len); + if (!ssid.SSID_len) + WL_SCAN(("%d: Broadcast scan\n", i)); + else + WL_SCAN(("%d: scan for %s size =%d\n", i, + ssid.SSID, ssid.SSID_len)); + memcpy(ptr, &ssid, sizeof(wlc_ssid_t)); + ptr += sizeof(wlc_ssid_t); + } + } else { + WL_SCAN(("Broadcast scan\n")); + } + /* Adding mask to channel numbers */ + params->channel_num = + htod32((n_ssids << WL_SCAN_PARAMS_NSSID_SHIFT) | + (n_channels & WL_SCAN_PARAMS_COUNT_MASK)); +} + +static s32 +wl_run_iscan(struct wl_iscan_ctrl *iscan, struct cfg80211_scan_request *request, u16 action) +{ + u32 n_channels; + u32 n_ssids; + s32 params_size = + (WL_SCAN_PARAMS_FIXED_SIZE + offsetof(wl_iscan_params_t, params)); + struct wl_iscan_params *params; + s32 err = 0; + + if (request != NULL) { + n_channels = request->n_channels; + n_ssids = request->n_ssids; + /* Allocate space for populating ssids in wl_iscan_params struct */ + if (n_channels % 2) + /* If n_channels is odd, add a padd of u16 */ + params_size += sizeof(u16) * (n_channels + 1); + else + params_size += sizeof(u16) * n_channels; + + /* Allocate space for populating ssids in wl_iscan_params struct */ + params_size += sizeof(struct wlc_ssid) * n_ssids; + } + params = (struct wl_iscan_params *)kzalloc(params_size, GFP_KERNEL); + if (!params) { + return -ENOMEM; + } + + wl_scan_prep(¶ms->params, request); + + params->version = htod32(ISCAN_REQ_VERSION); + params->action = htod16(action); + params->scan_duration = htod16(0); + + if (params_size + sizeof("iscan") >= WLC_IOCTL_MEDLEN) { + WL_ERR(("ioctl buffer length is not sufficient\n")); + err = -ENOMEM; + goto done; + } + err = wldev_iovar_setbuf(iscan->dev, "iscan", params, params_size, + iscan->ioctl_buf, WLC_IOCTL_MEDLEN, NULL); + if (unlikely(err)) { + if (err == -EBUSY) { + WL_ERR(("system busy : iscan canceled\n")); + } else { + WL_ERR(("error (%d)\n", err)); + } + } +done: + kfree(params); + return err; +} + +static s32 wl_do_iscan(struct wl_priv *wl, struct cfg80211_scan_request *request) +{ + struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); + struct net_device *ndev = wl_to_prmry_ndev(wl); + s32 passive_scan; + s32 err = 0; + + iscan->state = WL_ISCAN_STATE_SCANING; + + passive_scan = wl->active_scan ? 0 : 1; + err = wldev_ioctl(ndev, WLC_SET_PASSIVE_SCAN, + &passive_scan, sizeof(passive_scan), false); + if (unlikely(err)) { + WL_DBG(("error (%d)\n", err)); + return err; + } + wl->iscan_kickstart = true; + wl_run_iscan(iscan, request, WL_SCAN_ACTION_START); + mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); + iscan->timer_on = 1; + + return err; +} + +static s32 +wl_get_valid_channels(struct net_device *ndev, u8 *valid_chan_list, s32 size) +{ + wl_uint32_list_t *list; + s32 err = BCME_OK; + if (valid_chan_list == NULL || size <= 0) + return -ENOMEM; + + memset(valid_chan_list, 0, size); + list = (wl_uint32_list_t *)(void *) valid_chan_list; + list->count = htod32(WL_NUMCHANNELS); + err = wldev_ioctl(ndev, WLC_GET_VALID_CHANNELS, valid_chan_list, size, false); + if (err != 0) { + WL_ERR(("get channels failed with %d\n", err)); + } + + return err; +} + +static s32 +wl_run_escan(struct wl_priv *wl, struct net_device *ndev, + struct cfg80211_scan_request *request, uint16 action) +{ + s32 err = BCME_OK; + u32 n_channels; + u32 n_ssids; + s32 params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_escan_params_t, params)); + wl_escan_params_t *params = NULL; + struct cfg80211_scan_request *scan_request = wl->scan_request; + u8 chan_buf[sizeof(u32)*(WL_NUMCHANNELS + 1)]; + u32 num_chans = 0; + s32 channel; + s32 n_valid_chan; + s32 search_state = WL_P2P_DISC_ST_SCAN; + u32 i, j, n_nodfs = 0; + u16 *default_chan_list = NULL; + wl_uint32_list_t *list; + struct net_device *dev = NULL; + WL_DBG(("Enter \n")); + + + if (!wl->p2p_supported || ((ndev == wl_to_prmry_ndev(wl)) && + !p2p_scan(wl))) { + /* LEGACY SCAN TRIGGER */ + WL_SCAN((" LEGACY E-SCAN START\n")); + + if (request != NULL) { + n_channels = request->n_channels; + n_ssids = request->n_ssids; + /* Allocate space for populating ssids in wl_iscan_params struct */ + if (n_channels % 2) + /* If n_channels is odd, add a padd of u16 */ + params_size += sizeof(u16) * (n_channels + 1); + else + params_size += sizeof(u16) * n_channels; + + /* Allocate space for populating ssids in wl_iscan_params struct */ + params_size += sizeof(struct wlc_ssid) * n_ssids; + } + params = (wl_escan_params_t *) kzalloc(params_size, GFP_KERNEL); + if (params == NULL) { + err = -ENOMEM; + goto exit; + } + + wl_scan_prep(¶ms->params, request); + params->version = htod32(ESCAN_REQ_VERSION); + params->action = htod16(action); + params->sync_id = htod16(0x1234); + if (params_size + sizeof("escan") >= WLC_IOCTL_MEDLEN) { + WL_ERR(("ioctl buffer length not sufficient\n")); + kfree(params); + err = -ENOMEM; + goto exit; + } + err = wldev_iovar_setbuf(ndev, "escan", params, params_size, + wl->escan_ioctl_buf, WLC_IOCTL_MEDLEN, NULL); + if (unlikely(err)) + WL_ERR((" Escan set error (%d)\n", err)); + kfree(params); + } + else if (p2p_is_on(wl) && p2p_scan(wl)) { + /* P2P SCAN TRIGGER */ + s32 _freq = 0; + n_nodfs = 0; + if (scan_request && scan_request->n_channels) { + num_chans = scan_request->n_channels; + WL_SCAN((" chann number : %d\n", num_chans)); + default_chan_list = kzalloc(num_chans * sizeof(*default_chan_list), + GFP_KERNEL); + if (default_chan_list == NULL) { + WL_ERR(("channel list allocation failed \n")); + err = -ENOMEM; + goto exit; + } + if (!wl_get_valid_channels(ndev, chan_buf, sizeof(chan_buf))) { + list = (wl_uint32_list_t *) chan_buf; + n_valid_chan = dtoh32(list->count); + for (i = 0; i < num_chans; i++) + { + _freq = scan_request->channels[i]->center_freq; + channel = ieee80211_frequency_to_channel(_freq); + /* remove DFS channels */ + if (channel < 52 || channel > 140) { + for (j = 0; j < n_valid_chan; j++) { + /* allows only supported channel on + * current reguatory + */ + if (channel == (dtoh32(list->element[j]))) + default_chan_list[n_nodfs++] = + channel; + } + } + + } + } + if (num_chans == 3 && ( + (default_chan_list[0] == SOCIAL_CHAN_1) && + (default_chan_list[1] == SOCIAL_CHAN_2) && + (default_chan_list[2] == SOCIAL_CHAN_3))) { + /* SOCIAL CHANNELS 1, 6, 11 */ + search_state = WL_P2P_DISC_ST_SEARCH; + WL_INFO(("P2P SEARCH PHASE START \n")); + } else if ((dev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION)) && + (wl_get_mode_by_netdev(wl, dev) == WL_MODE_AP)) { + /* If you are already a GO, then do SEARCH only */ + WL_INFO(("Already a GO. Do SEARCH Only")); + search_state = WL_P2P_DISC_ST_SEARCH; + num_chans = n_nodfs; + + } else { + WL_INFO(("P2P SCAN STATE START \n")); + num_chans = n_nodfs; + } + + } + err = wl_cfgp2p_escan(wl, ndev, wl->active_scan, num_chans, default_chan_list, + search_state, action, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + kfree(default_chan_list); + } +exit: + if (unlikely(err)) { + WL_ERR(("error (%d)\n", err)); + } + return err; +} + + +static s32 +wl_do_escan(struct wl_priv *wl, struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_scan_request *request) +{ + s32 err = BCME_OK; + s32 passive_scan; + wl_scan_results_t *results; + WL_SCAN(("Enter \n")); + wl->escan_info.ndev = ndev; + wl->escan_info.wiphy = wiphy; + wl->escan_info.escan_state = WL_ESCAN_STATE_SCANING; + passive_scan = wl->active_scan ? 0 : 1; + err = wldev_ioctl(ndev, WLC_SET_PASSIVE_SCAN, + &passive_scan, sizeof(passive_scan), false); + if (unlikely(err)) { + WL_ERR(("error (%d)\n", err)); + return err; + } + results = (wl_scan_results_t *) wl->escan_info.escan_buf; + results->version = 0; + results->count = 0; + results->buflen = WL_SCAN_RESULTS_FIXED_SIZE; + + err = wl_run_escan(wl, ndev, request, WL_SCAN_ACTION_START); + return err; +} + +static s32 +__wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_scan_request *request, + struct cfg80211_ssid *this_ssid) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + struct cfg80211_ssid *ssids; + struct wl_scan_req *sr = wl_to_sr(wl); + struct ether_addr primary_mac; + wpa_ie_fixed_t *wps_ie; + s32 passive_scan; + bool iscan_req; + bool escan_req = false; + bool p2p_ssid; + s32 err = 0; + s32 i; + u32 wpsie_len = 0; + u8 wpsie[IE_MAX_LEN]; + + /* If scan req comes for p2p0, send it over primary I/F + * Scan results will be delivered corresponding to cfg80211_scan_request + */ + if (ndev == wl->p2p_net) { + ndev = wl_to_prmry_ndev(wl); + } + + WL_DBG(("Enter wiphy (%p)\n", wiphy)); + if (wl_get_drv_status_all(wl, SCANNING)) { + WL_ERR(("Scanning already\n")); + return -EAGAIN; + } + if (wl_get_drv_status(wl, SCAN_ABORTING, ndev)) { + WL_ERR(("Scanning being aborted\n")); + return -EAGAIN; + } + if (request && request->n_ssids > WL_SCAN_PARAMS_SSID_MAX) { + WL_ERR(("request null or n_ssids > WL_SCAN_PARAMS_SSID_MAX\n")); + return -EOPNOTSUPP; + } + + /* Arm scan timeout timer */ + mod_timer(&wl->scan_timeout, jiffies + WL_SCAN_TIMER_INTERVAL_MS * HZ / 1000); + iscan_req = false; + if (request) { /* scan bss */ + ssids = request->ssids; + if (wl->iscan_on && (!ssids || !ssids->ssid_len || request->n_ssids != 1)) { + iscan_req = true; + } else if (wl->escan_on) { + escan_req = true; + p2p_ssid = false; + for (i = 0; i < request->n_ssids; i++) { + if (ssids[i].ssid_len && IS_P2P_SSID(ssids[i].ssid)) { + p2p_ssid = true; + break; + } + } + if (p2p_ssid) { + if (wl->p2p_supported) { + /* p2p scan trigger */ + if (p2p_on(wl) == false) { + /* p2p on at the first time */ + p2p_on(wl) = true; + wl_cfgp2p_set_firm_p2p(wl); + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, + &wl->p2p->dev_addr, &wl->p2p->int_addr); + } + wl_clr_p2p_status(wl, GO_NEG_PHASE); + WL_DBG(("P2P: GO_NEG_PHASE status cleared \n")); + p2p_scan(wl) = true; + } + } else { + /* legacy scan trigger + * So, we have to disable p2p discovery if p2p discovery is on + */ + if (wl->p2p_supported) { + p2p_scan(wl) = false; + /* If Netdevice is not equals to primary and p2p is on + * , we will do p2p scan using P2PAPI_BSSCFG_DEVICE. + */ + if (p2p_on(wl) && (ndev != wl_to_prmry_ndev(wl))) + p2p_scan(wl) = true; + + if (p2p_scan(wl) == false) { + if (wl_get_p2p_status(wl, DISCOVERY_ON)) { + err = wl_cfgp2p_discover_enable_search(wl, + false); + if (unlikely(err)) { + goto scan_out; + } + + } + } + } + if (!wl->p2p_supported || !p2p_scan(wl)) { + if (ndev == wl_to_prmry_ndev(wl)) { + /* find the WPSIE */ + memset(wpsie, 0, sizeof(wpsie)); + if ((wps_ie = wl_cfgp2p_find_wpsie( + (u8 *)request->ie, + request->ie_len)) != NULL) { + wpsie_len = + wps_ie->length + WPA_RSN_IE_TAG_FIXED_LEN; + memcpy(wpsie, wps_ie, wpsie_len); + } else { + wpsie_len = 0; + } + if (wpsie_len > 0) { + err = wl_cfgp2p_set_management_ie(wl, + ndev, -1, VNDR_IE_PRBREQ_FLAG, + wpsie, wpsie_len); + if (unlikely(err)) { + goto scan_out; + } + } + } + } + } + } + } else { /* scan in ibss */ + /* we don't do iscan in ibss */ + ssids = this_ssid; + } + wl->scan_request = request; + wl_set_drv_status(wl, SCANNING, ndev); + if (iscan_req) { + err = wl_do_iscan(wl, request); + if (likely(!err)) + return err; + else + goto scan_out; + } else if (escan_req) { + if (wl->p2p_supported) { + if (p2p_on(wl) && p2p_scan(wl)) { + + err = wl_cfgp2p_enable_discovery(wl, ndev, + request->ie, request->ie_len); + + if (unlikely(err)) { + goto scan_out; + } + } + } + err = wl_do_escan(wl, wiphy, ndev, request); + if (likely(!err)) + return err; + else + goto scan_out; + + + } else { + memset(&sr->ssid, 0, sizeof(sr->ssid)); + sr->ssid.SSID_len = + min_t(u8, sizeof(sr->ssid.SSID), ssids->ssid_len); + if (sr->ssid.SSID_len) { + memcpy(sr->ssid.SSID, ssids->ssid, sr->ssid.SSID_len); + sr->ssid.SSID_len = htod32(sr->ssid.SSID_len); + WL_SCAN(("Specific scan ssid=\"%s\" len=%d\n", + sr->ssid.SSID, sr->ssid.SSID_len)); + } else { + WL_SCAN(("Broadcast scan\n")); + } + WL_SCAN(("sr->ssid.SSID_len (%d)\n", sr->ssid.SSID_len)); + passive_scan = wl->active_scan ? 0 : 1; + err = wldev_ioctl(ndev, WLC_SET_PASSIVE_SCAN, + &passive_scan, sizeof(passive_scan), false); + if (unlikely(err)) { + WL_SCAN(("WLC_SET_PASSIVE_SCAN error (%d)\n", err)); + goto scan_out; + } + err = wldev_ioctl(ndev, WLC_SCAN, &sr->ssid, + sizeof(sr->ssid), false); + if (err) { + if (err == -EBUSY) { + WL_ERR(("system busy : scan for \"%s\" " + "canceled\n", sr->ssid.SSID)); + } else { + WL_ERR(("WLC_SCAN error (%d)\n", err)); + } + goto scan_out; + } + } + + return 0; + +scan_out: + wl_clr_drv_status(wl, SCANNING, ndev); + wl->scan_request = NULL; + return err; +} + +static s32 +wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_scan_request *request) +{ + s32 err = 0; + struct wl_priv *wl = wiphy_priv(wiphy); + + WL_DBG(("Enter \n")); + CHECK_SYS_UP(wl); + + err = __wl_cfg80211_scan(wiphy, ndev, request, NULL); + if (unlikely(err)) { + WL_ERR(("scan error (%d)\n", err)); + return err; + } + + return err; +} + +static s32 wl_set_rts(struct net_device *dev, u32 rts_threshold) +{ + s32 err = 0; + + err = wldev_iovar_setint(dev, "rtsthresh", rts_threshold); + if (unlikely(err)) { + WL_ERR(("Error (%d)\n", err)); + return err; + } + return err; +} + +static s32 wl_set_frag(struct net_device *dev, u32 frag_threshold) +{ + s32 err = 0; + + err = wldev_iovar_setint_bsscfg(dev, "fragthresh", frag_threshold, 0); + if (unlikely(err)) { + WL_ERR(("Error (%d)\n", err)); + return err; + } + return err; +} + +static s32 wl_set_retry(struct net_device *dev, u32 retry, bool l) +{ + s32 err = 0; + u32 cmd = (l ? WLC_SET_LRL : WLC_SET_SRL); + + retry = htod32(retry); + err = wldev_ioctl(dev, cmd, &retry, sizeof(retry), false); + if (unlikely(err)) { + WL_ERR(("cmd (%d) , error (%d)\n", cmd, err)); + return err; + } + return err; +} + +static s32 wl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct wl_priv *wl = (struct wl_priv *)wiphy_priv(wiphy); + struct net_device *ndev = wl_to_prmry_ndev(wl); + s32 err = 0; + + CHECK_SYS_UP(wl); + WL_DBG(("Enter\n")); + if (changed & WIPHY_PARAM_RTS_THRESHOLD && + (wl->conf->rts_threshold != wiphy->rts_threshold)) { + wl->conf->rts_threshold = wiphy->rts_threshold; + err = wl_set_rts(ndev, wl->conf->rts_threshold); + if (!err) + return err; + } + if (changed & WIPHY_PARAM_FRAG_THRESHOLD && + (wl->conf->frag_threshold != wiphy->frag_threshold)) { + wl->conf->frag_threshold = wiphy->frag_threshold; + err = wl_set_frag(ndev, wl->conf->frag_threshold); + if (!err) + return err; + } + if (changed & WIPHY_PARAM_RETRY_LONG && + (wl->conf->retry_long != wiphy->retry_long)) { + wl->conf->retry_long = wiphy->retry_long; + err = wl_set_retry(ndev, wl->conf->retry_long, true); + if (!err) + return err; + } + if (changed & WIPHY_PARAM_RETRY_SHORT && + (wl->conf->retry_short != wiphy->retry_short)) { + wl->conf->retry_short = wiphy->retry_short; + err = wl_set_retry(ndev, wl->conf->retry_short, false); + if (!err) { + return err; + } + } + return err; +} + +static s32 +wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + struct wl_join_params join_params; + struct wlc_ssid ssid; + struct ether_addr bssid; + size_t join_params_size = 0; + s32 wsec = 0; + s32 bcnprd; + s32 err = 0; + + WL_TRACE(("In\n")); + CHECK_SYS_UP(wl); + + /* + * Cancel ongoing scan to sync up with sme state machine of cfg80211. + */ + if (wl->scan_request) { + wl_notify_escan_complete(wl, dev, true, true); + } + /* Clean BSSID */ + bzero(&bssid, sizeof(bssid)); + wl_update_prof(wl, dev, NULL, (void *)&bssid, WL_PROF_BSSID); + + if (params->ssid) + WL_INFO(("SSID: %s\n", params->ssid)); + else { + WL_ERR(("SSID: NULL, Not supported\n")); + err = -EOPNOTSUPP; + goto CleanUp; + } + + if (params->bssid) + WL_INFO(("BSSID: %02X:%02X:%02X:%02X:%02X:%02X\n", + params->bssid[0], params->bssid[1], params->bssid[2], + params->bssid[3], params->bssid[4], params->bssid[5])); + + if (params->channel) + WL_INFO(("channel: %d\n", params->channel->center_freq)); + + if (params->channel_fixed) + WL_INFO(("fixed channel required\n")); + + if (params->ie && params->ie_len) + WL_INFO(("ie len: %d\n", params->ie_len)); + + if (params->beacon_interval) + WL_INFO(("beacon interval: %d\n", params->beacon_interval)); + + if (params->basic_rates) + WL_INFO(("basic rates: %08X\n", params->basic_rates)); + + if (params->privacy) + WL_INFO(("privacy required\n")); + + wl_set_drv_status(wl, CONNECTING, dev); + + /* Configure Privacy for starter */ + if (params->privacy) + wsec |= WEP_ENABLED; + + err = wldev_iovar_setint(dev, "wsec", wsec); + if (err) { + WL_ERR(("wsec failed (%d)\n", err)); + goto CleanUp; + } + + err = wldev_iovar_setint(dev, "auth", WL_AUTH_OPEN_SYSTEM); + if (err) { + WL_ERR(("auth failed (%d)\n", err)); + goto CleanUp; + } + + err = wldev_iovar_setint(dev, "wpa_auth", 0); + if (err) { + WL_ERR(("wpa_auth failed (%d)\n", err)); + goto CleanUp; + } + + /* Configure Beacon Interval for starter */ + if (params->beacon_interval) + bcnprd = params->beacon_interval; + else + bcnprd = 100; + + bcnprd = htod32(bcnprd); + err = wldev_ioctl(dev, WLC_SET_BCNPRD, &bcnprd, sizeof(bcnprd), true); + if (err) { + WL_ERR(("WLC_SET_BCNPRD failed (%d)\n", err)); + goto CleanUp; + } + + /* Configure required join parameter */ + memset(&join_params, 0, sizeof(struct wl_join_params)); + + /* SSID */ + memset(&ssid, 0, sizeof(struct wlc_ssid)); + ssid.SSID_len = MIN(params->ssid_len, 32); + join_params.ssid.SSID_len = htod32(ssid.SSID_len); + memcpy(ssid.SSID, params->ssid, ssid.SSID_len); + memcpy(join_params.ssid.SSID, params->ssid, ssid.SSID_len); + join_params_size = sizeof(join_params.ssid); + + wl_update_prof(wl, dev, NULL, &ssid, WL_PROF_SSID); + + /* BSSID */ + if (params->bssid) { + memcpy(&join_params.params.bssid, params->bssid, ETHER_ADDR_LEN); + join_params_size = sizeof(join_params.ssid) + + WL_ASSOC_PARAMS_FIXED_SIZE; + + wl_update_prof(wl, dev, NULL, params->bssid, WL_PROF_BSSID); + } else { + memcpy(&join_params.params.bssid, ðer_bcast, ETHER_ADDR_LEN); + } + + /* Channel */ + if (params->channel) { + u32 target_channel; + + target_channel = ieee80211_frequency_to_channel( + params->channel->center_freq); + if (params->channel_fixed) { + /* adding chanspec */ + wl_ch_to_chanspec(target_channel, + &join_params, &join_params_size); + } + + /* set channel for starter */ + target_channel = htod32(target_channel); + err = wldev_ioctl(dev, WLC_SET_CHANNEL, + &target_channel, sizeof(target_channel), true); + if (err) { + WL_ERR(("WLC_SET_CHANNEL failed (%d)\n", err)); + goto CleanUp; + } + } + + wl->ibss_starter = false; + + err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size, true); + if (err) { + WL_ERR(("WLC_SET_SSID failed (%d)\n", err)); + goto CleanUp; + } + +CleanUp: + + if (err) + wl_clr_drv_status(wl, CONNECTING, dev); + + WL_TRACE(("Exit\n")); + return err; +} + +static s32 wl_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + scb_val_t scbval; + bool act = false; + s32 err = 0; + u8 *curbssid; + + WL_TRACE(("Enter\n")); + + CHECK_SYS_UP(wl); + act = *(bool *) wl_read_prof(wl, dev, WL_PROF_ACT); + curbssid = wl_read_prof(wl, dev, WL_PROF_BSSID); + if (act) { + /* + * Cancel ongoing scan to sync up with sme state machine of cfg80211. + */ + if (wl->scan_request) { + wl_notify_escan_complete(wl, dev, true, true); + } + wl_set_drv_status(wl, DISCONNECTING, dev); + scbval.val = DOT11_RC_DISASSOC_LEAVING; + memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); + scbval.val = htod32(scbval.val); + err = wldev_ioctl(dev, WLC_DISASSOC, &scbval, + sizeof(scb_val_t), true); + if (unlikely(err)) { + wl_clr_drv_status(wl, DISCONNECTING, dev); + WL_ERR(("error (%d)\n", err)); + return err; + } + } + + WL_TRACE(("Exit\n")); + return err; +} + +static s32 +wl_set_wpa_version(struct net_device *dev, struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = wlcfg_drv_priv; + struct wl_security *sec; + s32 val = 0; + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + + if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) + val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED; + else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) + val = WPA2_AUTH_PSK| WPA2_AUTH_UNSPECIFIED; + else + val = WPA_AUTH_DISABLED; + + if (is_wps_conn(sme)) + val = WPA_AUTH_DISABLED; + + WL_DBG(("setting wpa_auth to 0x%0x\n", val)); + err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", val, bssidx); + if (unlikely(err)) { + WL_ERR(("set wpa_auth failed (%d)\n", err)); + return err; + } + sec = wl_read_prof(wl, dev, WL_PROF_SEC); + sec->wpa_versions = sme->crypto.wpa_versions; + return err; +} + +static s32 +wl_set_auth_type(struct net_device *dev, struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = wlcfg_drv_priv; + struct wl_security *sec; + s32 val = 0; + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + switch (sme->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + val = WL_AUTH_OPEN_SYSTEM; + WL_DBG(("open system\n")); + break; + case NL80211_AUTHTYPE_SHARED_KEY: + val = WL_AUTH_SHARED_KEY; + WL_DBG(("shared key\n")); + break; + case NL80211_AUTHTYPE_AUTOMATIC: + val = WL_AUTH_OPEN_SHARED; + WL_DBG(("automatic\n")); + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + WL_DBG(("network eap\n")); + default: + val = WL_AUTH_OPEN_SHARED; + WL_ERR(("invalid auth type (%d)\n", sme->auth_type)); + break; + } + + err = wldev_iovar_setint_bsscfg(dev, "auth", val, bssidx); + if (unlikely(err)) { + WL_ERR(("set auth failed (%d)\n", err)); + return err; + } + sec = wl_read_prof(wl, dev, WL_PROF_SEC); + sec->auth_type = sme->auth_type; + return err; +} + +static s32 +wl_set_set_cipher(struct net_device *dev, struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = wlcfg_drv_priv; + struct wl_security *sec; + s32 pval = 0; + s32 gval = 0; + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + + if (sme->crypto.n_ciphers_pairwise) { + switch (sme->crypto.ciphers_pairwise[0]) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + pval = WEP_ENABLED; + break; + case WLAN_CIPHER_SUITE_TKIP: + pval = TKIP_ENABLED; + break; + case WLAN_CIPHER_SUITE_CCMP: + pval = AES_ENABLED; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + pval = AES_ENABLED; + break; + default: + WL_ERR(("invalid cipher pairwise (%d)\n", + sme->crypto.ciphers_pairwise[0])); + return -EINVAL; + } + } + if (sme->crypto.cipher_group) { + switch (sme->crypto.cipher_group) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + gval = WEP_ENABLED; + break; + case WLAN_CIPHER_SUITE_TKIP: + gval = TKIP_ENABLED; + break; + case WLAN_CIPHER_SUITE_CCMP: + gval = AES_ENABLED; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + gval = AES_ENABLED; + break; + default: + WL_ERR(("invalid cipher group (%d)\n", + sme->crypto.cipher_group)); + return -EINVAL; + } + } + + WL_DBG(("pval (%d) gval (%d)\n", pval, gval)); + + if (is_wps_conn(sme)) { + if (sme->privacy) + err = wldev_iovar_setint_bsscfg(dev, "wsec", 4, bssidx); + else + /* WPS-2.0 allowes no security */ + err = wldev_iovar_setint_bsscfg(dev, "wsec", 0, bssidx); + } else { + WL_DBG((" NO, is_wps_conn, Set pval | gval to WSEC")); + err = wldev_iovar_setint_bsscfg(dev, "wsec", + pval | gval, bssidx); + } + if (unlikely(err)) { + WL_ERR(("error (%d)\n", err)); + return err; + } + + sec = wl_read_prof(wl, dev, WL_PROF_SEC); + sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0]; + sec->cipher_group = sme->crypto.cipher_group; + + return err; +} + +static s32 +wl_set_key_mgmt(struct net_device *dev, struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = wlcfg_drv_priv; + struct wl_security *sec; + s32 val = 0; + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + + if (sme->crypto.n_akm_suites) { + err = wldev_iovar_getint(dev, "wpa_auth", &val); + if (unlikely(err)) { + WL_ERR(("could not get wpa_auth (%d)\n", err)); + return err; + } + if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { + switch (sme->crypto.akm_suites[0]) { + case WLAN_AKM_SUITE_8021X: + val = WPA_AUTH_UNSPECIFIED; + break; + case WLAN_AKM_SUITE_PSK: + val = WPA_AUTH_PSK; + break; + default: + WL_ERR(("invalid cipher group (%d)\n", + sme->crypto.cipher_group)); + return -EINVAL; + } + } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { + switch (sme->crypto.akm_suites[0]) { + case WLAN_AKM_SUITE_8021X: + val = WPA2_AUTH_UNSPECIFIED; + break; + case WLAN_AKM_SUITE_PSK: + val = WPA2_AUTH_PSK; + break; + default: + WL_ERR(("invalid cipher group (%d)\n", + sme->crypto.cipher_group)); + return -EINVAL; + } + } + WL_DBG(("setting wpa_auth to %d\n", val)); + + err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", val, bssidx); + if (unlikely(err)) { + WL_ERR(("could not set wpa_auth (%d)\n", err)); + return err; + } + } + sec = wl_read_prof(wl, dev, WL_PROF_SEC); + sec->wpa_auth = sme->crypto.akm_suites[0]; + + return err; +} + +static s32 +wl_set_set_sharedkey(struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = wlcfg_drv_priv; + struct wl_security *sec; + struct wl_wsec_key key; + s32 val; + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + + WL_DBG(("key len (%d)\n", sme->key_len)); + if (sme->key_len) { + sec = wl_read_prof(wl, dev, WL_PROF_SEC); + WL_DBG(("wpa_versions 0x%x cipher_pairwise 0x%x\n", + sec->wpa_versions, sec->cipher_pairwise)); + if (!(sec->wpa_versions & (NL80211_WPA_VERSION_1 | + NL80211_WPA_VERSION_2)) && + (sec->cipher_pairwise & (WLAN_CIPHER_SUITE_WEP40 | + WLAN_CIPHER_SUITE_WEP104))) + { + memset(&key, 0, sizeof(key)); + key.len = (u32) sme->key_len; + key.index = (u32) sme->key_idx; + if (unlikely(key.len > sizeof(key.data))) { + WL_ERR(("Too long key length (%u)\n", key.len)); + return -EINVAL; + } + memcpy(key.data, sme->key, key.len); + key.flags = WL_PRIMARY_KEY; + switch (sec->cipher_pairwise) { + case WLAN_CIPHER_SUITE_WEP40: + key.algo = CRYPTO_ALGO_WEP1; + break; + case WLAN_CIPHER_SUITE_WEP104: + key.algo = CRYPTO_ALGO_WEP128; + break; + default: + WL_ERR(("Invalid algorithm (%d)\n", + sme->crypto.ciphers_pairwise[0])); + return -EINVAL; + } + /* Set the new key/index */ + WL_DBG(("key length (%d) key index (%d) algo (%d)\n", + key.len, key.index, key.algo)); + WL_DBG(("key \"%s\"\n", key.data)); + swap_key_from_BE(&key); + err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); + if (unlikely(err)) { + WL_ERR(("WLC_SET_KEY error (%d)\n", err)); + return err; + } + if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) { + WL_DBG(("set auth_type to shared key\n")); + val = WL_AUTH_SHARED_KEY; /* shared key */ + err = wldev_iovar_setint_bsscfg(dev, "auth", val, bssidx); + if (unlikely(err)) { + WL_ERR(("set auth failed (%d)\n", err)); + return err; + } + } + } + } + return err; +} + +static s32 +wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + struct ieee80211_channel *chan = sme->channel; + wl_extjoin_params_t *ext_join_params; + struct wl_join_params join_params; + size_t join_params_size; + s32 err = 0; + wpa_ie_fixed_t *wpa_ie; + wpa_ie_fixed_t *wps_ie; + bcm_tlv_t *wpa2_ie; + u8* wpaie = 0; + u32 wpaie_len = 0; + u32 wpsie_len = 0; + u32 chan_cnt = 0; + u8 wpsie[IE_MAX_LEN]; + struct ether_addr bssid; + + WL_DBG(("In\n")); + CHECK_SYS_UP(wl); + + /* + * Cancel ongoing scan to sync up with sme state machine of cfg80211. + */ + if (wl->scan_request) { + wl_notify_escan_complete(wl, dev, true, true); + } + /* Clean BSSID */ + bzero(&bssid, sizeof(bssid)); + wl_update_prof(wl, dev, NULL, (void *)&bssid, WL_PROF_BSSID); + + if (IS_P2P_SSID(sme->ssid) && (dev != wl_to_prmry_ndev(wl))) { + /* we only allow to connect using virtual interface in case of P2P */ + if (p2p_is_on(wl) && is_wps_conn(sme)) { + WL_DBG(("ASSOC1 p2p index : %d sme->ie_len %d\n", + wl_cfgp2p_find_idx(wl, dev), sme->ie_len)); + /* Have to apply WPS IE + P2P IE in assoc req frame */ + wl_cfgp2p_set_management_ie(wl, dev, + wl_cfgp2p_find_idx(wl, dev), VNDR_IE_PRBREQ_FLAG, + wl_to_p2p_bss_saved_ie(wl, P2PAPI_BSSCFG_DEVICE).p2p_probe_req_ie, + wl_to_p2p_bss_saved_ie(wl, + P2PAPI_BSSCFG_DEVICE).p2p_probe_req_ie_len); + wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), + VNDR_IE_ASSOCREQ_FLAG, sme->ie, sme->ie_len); + } else if (p2p_is_on(wl) && (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)) { + /* This is the connect req after WPS is done [credentials exchanged] + * currently identified with WPA_VERSION_2 . + * Update the previously set IEs with + * the newly received IEs from Supplicant. This will remove the WPS IE from + * the Assoc Req. + */ + WL_DBG(("ASSOC2 p2p index : %d sme->ie_len %d\n", + wl_cfgp2p_find_idx(wl, dev), sme->ie_len)); + wl_cfgp2p_set_management_ie(wl, dev, + wl_cfgp2p_find_idx(wl, dev), VNDR_IE_PRBREQ_FLAG, + sme->ie, sme->ie_len); + wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), + VNDR_IE_ASSOCREQ_FLAG, sme->ie, sme->ie_len); + } + + } else if (dev == wl_to_prmry_ndev(wl)) { + /* find the RSN_IE */ + if ((wpa2_ie = bcm_parse_tlvs((u8 *)sme->ie, sme->ie_len, + DOT11_MNG_RSN_ID)) != NULL) { + WL_DBG((" WPA2 IE is found\n")); + } + /* find the WPA_IE */ + if ((wpa_ie = wl_cfgp2p_find_wpaie((u8 *)sme->ie, + sme->ie_len)) != NULL) { + WL_DBG((" WPA IE is found\n")); + } + if (wpa_ie != NULL || wpa2_ie != NULL) { + wpaie = (wpa_ie != NULL) ? (u8 *)wpa_ie : (u8 *)wpa2_ie; + wpaie_len = (wpa_ie != NULL) ? wpa_ie->length : wpa2_ie->len; + wpaie_len += WPA_RSN_IE_TAG_FIXED_LEN; + wldev_iovar_setbuf(dev, "wpaie", wpaie, wpaie_len, + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + } else { + wldev_iovar_setbuf(dev, "wpaie", NULL, 0, + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + } + + /* find the WPSIE */ + memset(wpsie, 0, sizeof(wpsie)); + if ((wps_ie = wl_cfgp2p_find_wpsie((u8 *)sme->ie, + sme->ie_len)) != NULL) { + wpsie_len = wps_ie->length +WPA_RSN_IE_TAG_FIXED_LEN; + memcpy(wpsie, wps_ie, wpsie_len); + } else { + wpsie_len = 0; + } + err = wl_cfgp2p_set_management_ie(wl, dev, -1, + VNDR_IE_ASSOCREQ_FLAG, wpsie, wpsie_len); + if (unlikely(err)) { + return err; + } + } + if (unlikely(!sme->ssid)) { + WL_ERR(("Invalid ssid\n")); + return -EOPNOTSUPP; + } + if (chan) { + wl->channel = ieee80211_frequency_to_channel(chan->center_freq); + chan_cnt = 1; + WL_DBG(("channel (%d), center_req (%d)\n", wl->channel, + chan->center_freq)); + } else + wl->channel = 0; + WL_DBG(("ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len)); + err = wl_set_wpa_version(dev, sme); + if (unlikely(err)) { + WL_ERR(("Invalid wpa_version\n")); + return err; + } + + err = wl_set_auth_type(dev, sme); + if (unlikely(err)) { + WL_ERR(("Invalid auth type\n")); + return err; + } + + err = wl_set_set_cipher(dev, sme); + if (unlikely(err)) { + WL_ERR(("Invalid ciper\n")); + return err; + } + + err = wl_set_key_mgmt(dev, sme); + if (unlikely(err)) { + WL_ERR(("Invalid key mgmt\n")); + return err; + } + + err = wl_set_set_sharedkey(dev, sme); + if (unlikely(err)) { + WL_ERR(("Invalid shared key\n")); + return err; + } + + /* + * Join with specific BSSID and cached SSID + * If SSID is zero join based on BSSID only + */ + join_params_size = WL_EXTJOIN_PARAMS_FIXED_SIZE + + chan_cnt * sizeof(chanspec_t); + ext_join_params = (wl_extjoin_params_t*)kzalloc(join_params_size, GFP_KERNEL); + if (ext_join_params == NULL) { + err = -ENOMEM; + wl_clr_drv_status(wl, CONNECTING, dev); + goto exit; + } + ext_join_params->ssid.SSID_len = min(sizeof(ext_join_params->ssid.SSID), sme->ssid_len); + memcpy(&ext_join_params->ssid.SSID, sme->ssid, ext_join_params->ssid.SSID_len); + ext_join_params->ssid.SSID_len = htod32(ext_join_params->ssid.SSID_len); + /* Set up join scan parameters */ + ext_join_params->scan.scan_type = -1; + ext_join_params->scan.nprobes = 2; + /* increate dwell time to receive probe response or detect Beacon + * from target AP at a noisy air only during connect command + */ + ext_join_params->scan.active_time = WL_SCAN_ACTIVE_TIME*3; + ext_join_params->scan.passive_time = WL_SCAN_PASSIVE_TIME*3; + ext_join_params->scan.home_time = -1; + + if (sme->bssid) + memcpy(&ext_join_params->assoc.bssid, sme->bssid, ETH_ALEN); + else + memcpy(&ext_join_params->assoc.bssid, ðer_bcast, ETH_ALEN); + ext_join_params->assoc.chanspec_num = chan_cnt; + if (chan_cnt) { + u16 channel, band, bw, ctl_sb; + chanspec_t chspec; + channel = wl->channel; + band = (channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G + : WL_CHANSPEC_BAND_5G; + bw = WL_CHANSPEC_BW_20; + ctl_sb = WL_CHANSPEC_CTL_SB_NONE; + chspec = (channel | band | bw | ctl_sb); + ext_join_params->assoc.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK; + ext_join_params->assoc.chanspec_list[0] |= chspec; + ext_join_params->assoc.chanspec_list[0] = + htodchanspec(ext_join_params->assoc.chanspec_list[0]); + } + ext_join_params->assoc.chanspec_num = htod32(ext_join_params->assoc.chanspec_num); + if (ext_join_params->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) { + WL_INFO(("ssid \"%s\", len (%d)\n", ext_join_params->ssid.SSID, + ext_join_params->ssid.SSID_len)); + } + wl_set_drv_status(wl, CONNECTING, dev); + err = wldev_iovar_setbuf_bsscfg(dev, "join", ext_join_params, join_params_size, + wl->ioctl_buf, WLC_IOCTL_MAXLEN, wl_cfgp2p_find_idx(wl, dev), &wl->ioctl_buf_sync); + kfree(ext_join_params); + if (err) { + wl_clr_drv_status(wl, CONNECTING, dev); + if (err == BCME_UNSUPPORTED) { + WL_DBG(("join iovar is not supported\n")); + goto set_ssid; + } else + WL_ERR(("error (%d)\n", err)); + } else + goto exit; + +set_ssid: + memset(&join_params, 0, sizeof(join_params)); + join_params_size = sizeof(join_params.ssid); + + join_params.ssid.SSID_len = min(sizeof(join_params.ssid.SSID), sme->ssid_len); + memcpy(&join_params.ssid.SSID, sme->ssid, join_params.ssid.SSID_len); + join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len); + wl_update_prof(wl, dev, NULL, &join_params.ssid, WL_PROF_SSID); + if (sme->bssid) + memcpy(&join_params.params.bssid, sme->bssid, ETH_ALEN); + else + memcpy(&join_params.params.bssid, ðer_bcast, ETH_ALEN); + + wl_ch_to_chanspec(wl->channel, &join_params, &join_params_size); + WL_DBG(("join_param_size %d\n", join_params_size)); + + if (join_params.ssid.SSID_len < IEEE80211_MAX_SSID_LEN) { + WL_INFO(("ssid \"%s\", len (%d)\n", join_params.ssid.SSID, + join_params.ssid.SSID_len)); + } + wl_set_drv_status(wl, CONNECTING, dev); + err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size, true); + if (err) { + WL_ERR(("error (%d)\n", err)); + wl_clr_drv_status(wl, CONNECTING, dev); + } +exit: + return err; +} + +static s32 +wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + scb_val_t scbval; + bool act = false; + s32 err = 0; + u8 *curbssid; + WL_ERR(("Reason %d\n", reason_code)); + CHECK_SYS_UP(wl); + act = *(bool *) wl_read_prof(wl, dev, WL_PROF_ACT); + curbssid = wl_read_prof(wl, dev, WL_PROF_BSSID); + if (act) { + /* + * Cancel ongoing scan to sync up with sme state machine of cfg80211. + */ + if (wl->scan_request) { + wl_notify_escan_complete(wl, dev, true, true); + } + wl_set_drv_status(wl, DISCONNECTING, dev); + scbval.val = reason_code; + memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); + scbval.val = htod32(scbval.val); + err = wldev_ioctl(dev, WLC_DISASSOC, &scbval, + sizeof(scb_val_t), true); + if (unlikely(err)) { + wl_clr_drv_status(wl, DISCONNECTING, dev); + WL_ERR(("error (%d)\n", err)); + return err; + } + } + + return err; +} + +static s32 +wl_cfg80211_set_tx_power(struct wiphy *wiphy, + enum nl80211_tx_power_setting type, s32 dbm) +{ + + struct wl_priv *wl = wiphy_priv(wiphy); + struct net_device *ndev = wl_to_prmry_ndev(wl); + u16 txpwrmw; + s32 err = 0; + s32 disable = 0; + + CHECK_SYS_UP(wl); + switch (type) { + case NL80211_TX_POWER_AUTOMATIC: + break; + case NL80211_TX_POWER_LIMITED: + if (dbm < 0) { + WL_ERR(("TX_POWER_LIMITTED - dbm is negative\n")); + return -EINVAL; + } + break; + case NL80211_TX_POWER_FIXED: + if (dbm < 0) { + WL_ERR(("TX_POWER_FIXED - dbm is negative..\n")); + return -EINVAL; + } + break; + } + /* Make sure radio is off or on as far as software is concerned */ + disable = WL_RADIO_SW_DISABLE << 16; + disable = htod32(disable); + err = wldev_ioctl(ndev, WLC_SET_RADIO, &disable, sizeof(disable), true); + if (unlikely(err)) { + WL_ERR(("WLC_SET_RADIO error (%d)\n", err)); + return err; + } + + if (dbm > 0xffff) + txpwrmw = 0xffff; + else + txpwrmw = (u16) dbm; + err = wldev_iovar_setint(ndev, "qtxpower", + (s32) (bcm_mw_to_qdbm(txpwrmw))); + if (unlikely(err)) { + WL_ERR(("qtxpower error (%d)\n", err)); + return err; + } + wl->conf->tx_power = dbm; + + return err; +} + +static s32 wl_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + struct net_device *ndev = wl_to_prmry_ndev(wl); + s32 txpwrdbm; + u8 result; + s32 err = 0; + + CHECK_SYS_UP(wl); + err = wldev_iovar_getint(ndev, "qtxpower", &txpwrdbm); + if (unlikely(err)) { + WL_ERR(("error (%d)\n", err)); + return err; + } + result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE); + *dbm = (s32) bcm_qdbm_to_mw(result); + + return err; +} + +static s32 +wl_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool unicast, bool multicast) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + u32 index; + s32 wsec; + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + + WL_DBG(("key index (%d)\n", key_idx)); + CHECK_SYS_UP(wl); + err = wldev_iovar_getint_bsscfg(dev, "wsec", &wsec, bssidx); + if (unlikely(err)) { + WL_ERR(("WLC_GET_WSEC error (%d)\n", err)); + return err; + } + if (wsec & WEP_ENABLED) { + /* Just select a new current key */ + index = (u32) key_idx; + index = htod32(index); + err = wldev_ioctl(dev, WLC_SET_KEY_PRIMARY, &index, + sizeof(index), true); + if (unlikely(err)) { + WL_ERR(("error (%d)\n", err)); + } + } + return err; +} + +static s32 +wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, const u8 *mac_addr, struct key_params *params) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + struct wl_wsec_key key; + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + s32 mode = wl_get_mode_by_netdev(wl, dev); + memset(&key, 0, sizeof(key)); + key.index = (u32) key_idx; + + if (!ETHER_ISMULTI(mac_addr)) + memcpy((char *)&key.ea, (void *)mac_addr, ETHER_ADDR_LEN); + key.len = (u32) params->key_len; + + /* check for key index change */ + if (key.len == 0) { + /* key delete */ + swap_key_from_BE(&key); + wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); + if (unlikely(err)) { + WL_ERR(("key delete error (%d)\n", err)); + return err; + } + } else { + if (key.len > sizeof(key.data)) { + WL_ERR(("Invalid key length (%d)\n", key.len)); + return -EINVAL; + } + WL_DBG(("Setting the key index %d\n", key.index)); + memcpy(key.data, params->key, key.len); + + if ((mode == WL_MODE_BSS) && + (params->cipher == WLAN_CIPHER_SUITE_TKIP)) { + u8 keybuf[8]; + memcpy(keybuf, &key.data[24], sizeof(keybuf)); + memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); + memcpy(&key.data[16], keybuf, sizeof(keybuf)); + } + + /* if IW_ENCODE_EXT_RX_SEQ_VALID set */ + if (params->seq && params->seq_len == 6) { + /* rx iv */ + u8 *ivptr; + ivptr = (u8 *) params->seq; + key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) | + (ivptr[3] << 8) | ivptr[2]; + key.rxiv.lo = (ivptr[1] << 8) | ivptr[0]; + key.iv_initialized = true; + } + + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + key.algo = CRYPTO_ALGO_WEP1; + WL_DBG(("WLAN_CIPHER_SUITE_WEP40\n")); + break; + case WLAN_CIPHER_SUITE_WEP104: + key.algo = CRYPTO_ALGO_WEP128; + WL_DBG(("WLAN_CIPHER_SUITE_WEP104\n")); + break; + case WLAN_CIPHER_SUITE_TKIP: + key.algo = CRYPTO_ALGO_TKIP; + WL_DBG(("WLAN_CIPHER_SUITE_TKIP\n")); + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + key.algo = CRYPTO_ALGO_AES_CCM; + WL_DBG(("WLAN_CIPHER_SUITE_AES_CMAC\n")); + break; + case WLAN_CIPHER_SUITE_CCMP: + key.algo = CRYPTO_ALGO_AES_CCM; + WL_DBG(("WLAN_CIPHER_SUITE_CCMP\n")); + break; + default: + WL_ERR(("Invalid cipher (0x%x)\n", params->cipher)); + return -EINVAL; + } + swap_key_from_BE(&key); + wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); + if (unlikely(err)) { + WL_ERR(("WLC_SET_KEY error (%d)\n", err)); + return err; + } + } + return err; +} + +static s32 +wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, const u8 *mac_addr, + struct key_params *params) +{ + struct wl_wsec_key key; + s32 val = 0; + s32 wsec = 0; + s32 err = 0; + u8 keybuf[8]; + s32 bssidx = 0; + struct wl_priv *wl = wiphy_priv(wiphy); + s32 mode = wl_get_mode_by_netdev(wl, dev); + WL_DBG(("key index (%d)\n", key_idx)); + CHECK_SYS_UP(wl); + + bssidx = wl_cfgp2p_find_idx(wl, dev); + + if (mac_addr) { + wl_add_keyext(wiphy, dev, key_idx, mac_addr, params); + goto exit; + } + memset(&key, 0, sizeof(key)); + + key.len = (u32) params->key_len; + key.index = (u32) key_idx; + + if (unlikely(key.len > sizeof(key.data))) { + WL_ERR(("Too long key length (%u)\n", key.len)); + return -EINVAL; + } + memcpy(key.data, params->key, key.len); + + key.flags = WL_PRIMARY_KEY; + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + key.algo = CRYPTO_ALGO_WEP1; + val = WEP_ENABLED; + WL_DBG(("WLAN_CIPHER_SUITE_WEP40\n")); + break; + case WLAN_CIPHER_SUITE_WEP104: + key.algo = CRYPTO_ALGO_WEP128; + val = WEP_ENABLED; + WL_DBG(("WLAN_CIPHER_SUITE_WEP104\n")); + break; + case WLAN_CIPHER_SUITE_TKIP: + key.algo = CRYPTO_ALGO_TKIP; + val = TKIP_ENABLED; + /* wpa_supplicant switches the third and fourth quarters of the TKIP key */ + if (mode == WL_MODE_BSS) { + bcopy(&key.data[24], keybuf, sizeof(keybuf)); + bcopy(&key.data[16], &key.data[24], sizeof(keybuf)); + bcopy(keybuf, &key.data[16], sizeof(keybuf)); + } + WL_DBG(("WLAN_CIPHER_SUITE_TKIP\n")); + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + key.algo = CRYPTO_ALGO_AES_CCM; + val = AES_ENABLED; + WL_DBG(("WLAN_CIPHER_SUITE_AES_CMAC\n")); + break; + case WLAN_CIPHER_SUITE_CCMP: + key.algo = CRYPTO_ALGO_AES_CCM; + val = AES_ENABLED; + WL_DBG(("WLAN_CIPHER_SUITE_CCMP\n")); + break; + default: + WL_ERR(("Invalid cipher (0x%x)\n", params->cipher)); + return -EINVAL; + } + + /* Set the new key/index */ + swap_key_from_BE(&key); + err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), wl->ioctl_buf, + WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); + if (unlikely(err)) { + WL_ERR(("WLC_SET_KEY error (%d)\n", err)); + return err; + } + +exit: + err = wldev_iovar_getint_bsscfg(dev, "wsec", &wsec, bssidx); + if (unlikely(err)) { + WL_ERR(("get wsec error (%d)\n", err)); + return err; + } + + wsec |= val; + err = wldev_iovar_setint_bsscfg(dev, "wsec", wsec, bssidx); + if (unlikely(err)) { + WL_ERR(("set wsec error (%d)\n", err)); + return err; + } + + return err; +} + +static s32 +wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, const u8 *mac_addr) +{ + struct wl_wsec_key key; + struct wl_priv *wl = wiphy_priv(wiphy); + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + + WL_DBG(("Enter\n")); + CHECK_SYS_UP(wl); + memset(&key, 0, sizeof(key)); + + key.flags = WL_PRIMARY_KEY; + key.algo = CRYPTO_ALGO_OFF; + key.index = (u32) key_idx; + + WL_DBG(("key index (%d)\n", key_idx)); + /* Set the new key/index */ + swap_key_from_BE(&key); + wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &key, sizeof(key), wl->ioctl_buf, + WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); + if (unlikely(err)) { + if (err == -EINVAL) { + if (key.index >= DOT11_MAX_DEFAULT_KEYS) { + /* we ignore this key index in this case */ + WL_DBG(("invalid key index (%d)\n", key_idx)); + } + } else { + WL_ERR(("WLC_SET_KEY error (%d)\n", err)); + } + return err; + } + return err; +} + +static s32 +wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, + u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, + void (*callback) (void *cookie, struct key_params * params)) +{ + struct key_params params; + struct wl_wsec_key key; + struct wl_priv *wl = wiphy_priv(wiphy); + struct wl_security *sec; + s32 wsec; + s32 err = 0; + s32 bssidx = wl_cfgp2p_find_idx(wl, dev); + + WL_DBG(("key index (%d)\n", key_idx)); + CHECK_SYS_UP(wl); + memset(&key, 0, sizeof(key)); + key.index = key_idx; + swap_key_to_BE(&key); + memset(¶ms, 0, sizeof(params)); + params.key_len = (u8) min_t(u8, DOT11_MAX_KEY_SIZE, key.len); + memcpy(params.key, key.data, params.key_len); + + wldev_iovar_getint_bsscfg(dev, "wsec", &wsec, bssidx); + if (unlikely(err)) { + WL_ERR(("WLC_GET_WSEC error (%d)\n", err)); + return err; + } + switch (wsec & ~SES_OW_ENABLED) { + case WEP_ENABLED: + sec = wl_read_prof(wl, dev, WL_PROF_SEC); + if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) { + params.cipher = WLAN_CIPHER_SUITE_WEP40; + WL_DBG(("WLAN_CIPHER_SUITE_WEP40\n")); + } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) { + params.cipher = WLAN_CIPHER_SUITE_WEP104; + WL_DBG(("WLAN_CIPHER_SUITE_WEP104\n")); + } + break; + case TKIP_ENABLED: + params.cipher = WLAN_CIPHER_SUITE_TKIP; + WL_DBG(("WLAN_CIPHER_SUITE_TKIP\n")); + break; + case AES_ENABLED: + params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; + WL_DBG(("WLAN_CIPHER_SUITE_AES_CMAC\n")); + break; + default: + WL_ERR(("Invalid algo (0x%x)\n", wsec)); + return -EINVAL; + } + + callback(cookie, ¶ms); + return err; +} + +static s32 +wl_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, + struct net_device *dev, u8 key_idx) +{ + WL_INFO(("Not supported\n")); + return -EOPNOTSUPP; +} + +static s32 +wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac, struct station_info *sinfo) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + scb_val_t scb_val; + s32 rssi; + s32 rate; + s32 err = 0; + sta_info_t *sta; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + s8 eabuf[ETHER_ADDR_STR_LEN]; +#endif + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + + CHECK_SYS_UP(wl); + if (wl_get_mode_by_netdev(wl, dev) == WL_MODE_AP) { + err = wldev_iovar_getbuf(dev, "sta_info", (struct ether_addr *)mac, + ETHER_ADDR_LEN, wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + if (err < 0) { + WL_ERR(("GET STA INFO failed, %d\n", err)); + return err; + } + sinfo->filled = STATION_INFO_INACTIVE_TIME; + sta = (sta_info_t *)wl->ioctl_buf; + sta->len = dtoh16(sta->len); + sta->cap = dtoh16(sta->cap); + sta->flags = dtoh32(sta->flags); + sta->idle = dtoh32(sta->idle); + sta->in = dtoh32(sta->in); + sinfo->inactive_time = sta->idle * 1000; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + if (sta->flags & WL_STA_ASSOC) { + sinfo->filled |= STATION_INFO_CONNECTED_TIME; + sinfo->connected_time = sta->in; + } + WL_INFO(("STA %s : idle time : %d sec, connected time :%d ms\n", + bcm_ether_ntoa((const struct ether_addr *)mac, eabuf), sinfo->inactive_time, + sta->idle * 1000)); +#endif + } else if (wl_get_mode_by_netdev(wl, dev) == WL_MODE_BSS) { + u8 *curmacp = wl_read_prof(wl, dev, WL_PROF_BSSID); + err = -ENODEV; + if (!wl_get_drv_status(wl, CONNECTED, dev) || + (dhd_is_associated(dhd, NULL, &err) == FALSE)) { + WL_ERR(("NOT assoc: %d\n", err)); + goto get_station_err; + } + if (memcmp(mac, curmacp, ETHER_ADDR_LEN)) { + WL_ERR(("Wrong Mac address: "MACSTR" != "MACSTR"\n", + MAC2STR(mac), MAC2STR(curmacp))); + } + + /* Report the current tx rate */ + err = wldev_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate), false); + if (err) { + WL_ERR(("Could not get rate (%d)\n", err)); + } else { + rate = dtoh32(rate); + sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->txrate.legacy = rate * 5; + WL_DBG(("Rate %d Mbps\n", (rate / 2))); + } + + memset(&scb_val, 0, sizeof(scb_val)); + scb_val.val = 0; + err = wldev_ioctl(dev, WLC_GET_RSSI, &scb_val, + sizeof(scb_val_t), false); + if (err) { + WL_ERR(("Could not get rssi (%d)\n", err)); + goto get_station_err; + } + rssi = dtoh32(scb_val.val); + sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->signal = rssi; + WL_DBG(("RSSI %d dBm\n", rssi)); + +get_station_err: + if (err && (err != -ETIMEDOUT) && (err != -EIO)) { + /* Disconnect due to zero BSSID or error to get RSSI */ + WL_ERR(("force cfg80211_disconnected: %d\n", err)); + wl_clr_drv_status(wl, CONNECTED, dev); + cfg80211_disconnected(dev, 0, NULL, 0, GFP_KERNEL); + wl_link_down(wl); + } + } + + return err; +} + +static s32 +wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, + bool enabled, s32 timeout) +{ + s32 pm; + s32 err = 0; + struct wl_priv *wl = wiphy_priv(wiphy); + + CHECK_SYS_UP(wl); + + WL_DBG(("Enter : power save %s\n", (enabled ? "enable" : "disable"))); + if (wl->p2p_net == dev) { + return err; + } + + pm = enabled ? PM_FAST : PM_OFF; + pm = htod32(pm); + err = wldev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), true); + if (unlikely(err)) { + if (err == -ENODEV) + WL_DBG(("net_device is not ready yet\n")); + else + WL_ERR(("error (%d)\n", err)); + return err; + } + WL_DBG(("power save %s\n", (pm ? "enabled" : "disabled"))); + return err; +} + +static __used u32 wl_find_msb(u16 bit16) +{ + u32 ret = 0; + + if (bit16 & 0xff00) { + ret += 8; + bit16 >>= 8; + } + + if (bit16 & 0xf0) { + ret += 4; + bit16 >>= 4; + } + + if (bit16 & 0xc) { + ret += 2; + bit16 >>= 2; + } + + if (bit16 & 2) + ret += bit16 & 2; + else if (bit16) + ret += bit16; + + return ret; +} + +static s32 wl_cfg80211_resume(struct wiphy *wiphy) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + struct net_device *ndev = wl_to_prmry_ndev(wl); + s32 err = 0; + + if (unlikely(!wl_get_drv_status(wl, READY, ndev))) { + WL_INFO(("device is not ready\n")); + return 0; + } + + wl_invoke_iscan(wl); + + return err; +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) +static s32 wl_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow) +#else +static s32 wl_cfg80211_suspend(struct wiphy *wiphy) +#endif +{ +#ifdef DHD_CLEAR_ON_SUSPEND + struct wl_priv *wl = wiphy_priv(wiphy); + struct net_info *iter, *next; + struct net_device *ndev = wl_to_prmry_ndev(wl); + unsigned long flags; + + if (unlikely(!wl_get_drv_status(wl, READY, ndev))) { + WL_INFO(("device is not ready : status (%d)\n", + (int)wl->status)); + return 0; + } + for_each_ndev(wl, iter, next) + wl_set_drv_status(wl, SCAN_ABORTING, iter->ndev); + wl_term_iscan(wl); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + if (wl->scan_request) { + cfg80211_scan_done(wl->scan_request, true); + wl->scan_request = NULL; + } + for_each_ndev(wl, iter, next) { + wl_clr_drv_status(wl, SCANNING, iter->ndev); + wl_clr_drv_status(wl, SCAN_ABORTING, iter->ndev); + } + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + for_each_ndev(wl, iter, next) { + if (wl_get_drv_status(wl, CONNECTING, iter->ndev)) { + wl_bss_connect_done(wl, iter->ndev, NULL, NULL, false); + } + } +#endif /* DHD_CLEAR_ON_SUSPEND */ + return 0; +} + +static s32 +wl_update_pmklist(struct net_device *dev, struct wl_pmk_list *pmk_list, + s32 err) +{ + int i, j; + struct wl_priv *wl = wlcfg_drv_priv; + struct net_device *primary_dev = wl_to_prmry_ndev(wl); + + if (!pmk_list) { + printk("pmk_list is NULL\n"); + return -EINVAL; + } + /* pmk list is supported only for STA interface i.e. primary interface + * Refer code wlc_bsscfg.c->wlc_bsscfg_sta_init + */ + if (primary_dev != dev) { + WL_INFO(("Not supporting Flushing pmklist on virtual" + " interfaces than primary interface\n")); + return err; + } + + WL_DBG(("No of elements %d\n", pmk_list->pmkids.npmkid)); + for (i = 0; i < pmk_list->pmkids.npmkid; i++) { + WL_DBG(("PMKID[%d]: %pM =\n", i, + &pmk_list->pmkids.pmkid[i].BSSID)); + for (j = 0; j < WPA2_PMKID_LEN; j++) { + WL_DBG(("%02x\n", pmk_list->pmkids.pmkid[i].PMKID[j])); + } + } + if (likely(!err)) { + err = wldev_iovar_setbuf(dev, "pmkid_info", (char *)pmk_list, + sizeof(*pmk_list), wl->ioctl_buf, WLC_IOCTL_MAXLEN, NULL); + } + + return err; +} + +static s32 +wl_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + s32 err = 0; + int i; + + CHECK_SYS_UP(wl); + for (i = 0; i < wl->pmk_list->pmkids.npmkid; i++) + if (!memcmp(pmksa->bssid, &wl->pmk_list->pmkids.pmkid[i].BSSID, + ETHER_ADDR_LEN)) + break; + if (i < WL_NUM_PMKIDS_MAX) { + memcpy(&wl->pmk_list->pmkids.pmkid[i].BSSID, pmksa->bssid, + ETHER_ADDR_LEN); + memcpy(&wl->pmk_list->pmkids.pmkid[i].PMKID, pmksa->pmkid, + WPA2_PMKID_LEN); + if (i == wl->pmk_list->pmkids.npmkid) + wl->pmk_list->pmkids.npmkid++; + } else { + err = -EINVAL; + } + WL_DBG(("set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n", + &wl->pmk_list->pmkids.pmkid[wl->pmk_list->pmkids.npmkid - 1].BSSID)); + for (i = 0; i < WPA2_PMKID_LEN; i++) { + WL_DBG(("%02x\n", + wl->pmk_list->pmkids.pmkid[wl->pmk_list->pmkids.npmkid - 1]. + PMKID[i])); + } + + err = wl_update_pmklist(dev, wl->pmk_list, err); + + return err; +} + +static s32 +wl_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + struct _pmkid_list pmkid; + s32 err = 0; + int i; + + CHECK_SYS_UP(wl); + memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETHER_ADDR_LEN); + memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WPA2_PMKID_LEN); + + WL_DBG(("del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n", + &pmkid.pmkid[0].BSSID)); + for (i = 0; i < WPA2_PMKID_LEN; i++) { + WL_DBG(("%02x\n", pmkid.pmkid[0].PMKID[i])); + } + + for (i = 0; i < wl->pmk_list->pmkids.npmkid; i++) + if (!memcmp + (pmksa->bssid, &wl->pmk_list->pmkids.pmkid[i].BSSID, + ETHER_ADDR_LEN)) + break; + + if ((wl->pmk_list->pmkids.npmkid > 0) && + (i < wl->pmk_list->pmkids.npmkid)) { + memset(&wl->pmk_list->pmkids.pmkid[i], 0, sizeof(pmkid_t)); + for (; i < (wl->pmk_list->pmkids.npmkid - 1); i++) { + memcpy(&wl->pmk_list->pmkids.pmkid[i].BSSID, + &wl->pmk_list->pmkids.pmkid[i + 1].BSSID, + ETHER_ADDR_LEN); + memcpy(&wl->pmk_list->pmkids.pmkid[i].PMKID, + &wl->pmk_list->pmkids.pmkid[i + 1].PMKID, + WPA2_PMKID_LEN); + } + wl->pmk_list->pmkids.npmkid--; + } else { + err = -EINVAL; + } + + err = wl_update_pmklist(dev, wl->pmk_list, err); + + return err; + +} + +static s32 +wl_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + s32 err = 0; + CHECK_SYS_UP(wl); + memset(wl->pmk_list, 0, sizeof(*wl->pmk_list)); + err = wl_update_pmklist(dev, wl->pmk_list, err); + return err; + +} + +static wl_scan_params_t * +wl_cfg80211_scan_alloc_params(int channel, int nprobes, int *out_params_size) +{ + wl_scan_params_t *params; + int params_size; + int num_chans; + + *out_params_size = 0; + + /* Our scan params only need space for 1 channel and 0 ssids */ + params_size = WL_SCAN_PARAMS_FIXED_SIZE + 1 * sizeof(uint16); + params = (wl_scan_params_t*) kzalloc(params_size, GFP_KERNEL); + if (params == NULL) { + WL_ERR(("%s: mem alloc failed (%d bytes)\n", __func__, params_size)); + return params; + } + memset(params, 0, params_size); + params->nprobes = nprobes; + + num_chans = (channel == 0) ? 0 : 1; + + memcpy(¶ms->bssid, ðer_bcast, ETHER_ADDR_LEN); + params->bss_type = DOT11_BSSTYPE_ANY; + params->scan_type = DOT11_SCANTYPE_ACTIVE; + params->nprobes = htod32(1); + params->active_time = htod32(-1); + params->passive_time = htod32(-1); + params->home_time = htod32(10); + params->channel_list[0] = htodchanspec(channel); + + /* Our scan params have 1 channel and 0 ssids */ + params->channel_num = htod32((0 << WL_SCAN_PARAMS_NSSID_SHIFT) | + (num_chans & WL_SCAN_PARAMS_COUNT_MASK)); + + *out_params_size = params_size; /* rtn size to the caller */ + return params; +} + +static s32 +wl_cfg80211_remain_on_channel(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel * channel, + enum nl80211_channel_type channel_type, + unsigned int duration, u64 *cookie) +{ + s32 target_channel; + u32 id; + struct ether_addr primary_mac; + struct net_device *ndev = NULL; + + s32 err = BCME_OK; + struct wl_priv *wl = wiphy_priv(wiphy); + WL_DBG(("Enter, netdev_ifidx: %d \n", dev->ifindex)); + + if (wl->p2p_net == dev) { + ndev = wl_to_prmry_ndev(wl); + } else { + ndev = dev; + } + + if (wl_get_drv_status(wl, SCANNING, ndev)) { + wl_notify_escan_complete(wl, ndev, true, true); + } + target_channel = ieee80211_frequency_to_channel(channel->center_freq); + memcpy(&wl->remain_on_chan, channel, sizeof(struct ieee80211_channel)); + wl->remain_on_chan_type = channel_type; + id = ++wl->last_roc_id; + if (id == 0) + id = ++wl->last_roc_id; + *cookie = id; + cfg80211_ready_on_channel(dev, *cookie, channel, + channel_type, duration, GFP_KERNEL); + if (wl->p2p && !wl->p2p->on) { + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, &wl->p2p->dev_addr, &wl->p2p->int_addr); + + /* In case of p2p_listen command, supplicant send remain_on_channel + * without turning on P2P + */ + + p2p_on(wl) = true; + err = wl_cfgp2p_enable_discovery(wl, ndev, NULL, 0); + + if (unlikely(err)) { + goto exit; + } + } + if (p2p_is_on(wl)) + wl_cfgp2p_discover_listen(wl, target_channel, duration); + + +exit: + return err; +} + +static s32 +wl_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, struct net_device *dev, + u64 cookie) +{ + s32 err = 0; + WL_DBG((" enter ) netdev_ifidx: %d \n", dev->ifindex)); + return err; +} + +static s32 +wl_cfg80211_send_pending_tx_act_frm(struct wl_priv *wl) +{ + wl_af_params_t *tx_act_frm; + struct net_device *dev = wl->afx_hdl->dev; + if (!p2p_is_on(wl)) + return -1; + + if (dev == wl->p2p_net) { + dev = wl_to_prmry_ndev(wl); + } + + tx_act_frm = wl->afx_hdl->pending_tx_act_frm; + WL_DBG(("Sending the action frame\n")); + wl->afx_hdl->pending_tx_act_frm = NULL; + if (tx_act_frm != NULL) { + /* Suspend P2P discovery's search-listen to prevent it from + * starting a scan or changing the channel. + */ + wl_clr_drv_status(wl, SENDING_ACT_FRM, wl->afx_hdl->dev); + wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); + wl_notify_escan_complete(wl, dev, true, true); + wl_cfgp2p_discover_enable_search(wl, false); + tx_act_frm->channel = wl->afx_hdl->peer_chan; + wl->afx_hdl->ack_recv = (wl_cfgp2p_tx_action_frame(wl, dev, + tx_act_frm, wl->afx_hdl->bssidx)) ? false : true; + } + return 0; +} +static void +wl_cfg80211_afx_handler(struct work_struct *work) +{ + + struct afx_hdl *afx_instance; + struct wl_priv *wl = wlcfg_drv_priv; + afx_instance = container_of(work, struct afx_hdl, work); + if (afx_instance != NULL) { + wl_cfgp2p_act_frm_search(wl, wl->afx_hdl->dev, + wl->afx_hdl->bssidx, 0); + } +} + +static bool +wl_cfg80211_send_at_common_channel(struct wl_priv *wl, + struct net_device *dev, + wl_af_params_t *af_params) +{ + WL_DBG((" enter ) \n")); + /* initialize afx_hdl */ + wl->afx_hdl->pending_tx_act_frm = af_params; + wl->afx_hdl->bssidx = wl_cfgp2p_find_idx(wl, dev); + wl->afx_hdl->dev = dev; + wl->afx_hdl->retry = 0; + wl->afx_hdl->peer_chan = WL_INVALID; + wl->afx_hdl->ack_recv = false; + memcpy(wl->afx_hdl->pending_tx_dst_addr.octet, + af_params->action_frame.da.octet, + sizeof(wl->afx_hdl->pending_tx_dst_addr.octet)); + /* Loop to wait until we have sent the pending tx action frame or the + * pending action frame tx is cancelled. + */ + while ((wl->afx_hdl->retry < WL_CHANNEL_SYNC_RETRY) && + (wl->afx_hdl->peer_chan == WL_INVALID)) { + wl_set_drv_status(wl, SENDING_ACT_FRM, dev); + wl_set_drv_status(wl, SCANNING, dev); + WL_DBG(("Scheduling the action frame for sending.. retry %d\n", + wl->afx_hdl->retry)); + /* Do find_peer_for_action */ + schedule_work(&wl->afx_hdl->work); + wait_for_completion(&wl->act_frm_scan); + wl->afx_hdl->retry++; + } + if (wl->afx_hdl->peer_chan != WL_INVALID) + wl_cfg80211_send_pending_tx_act_frm(wl); + else { + WL_ERR(("Couldn't find the peer " MACSTR " after %d retries\n", + MAC2STR(wl->afx_hdl->pending_tx_dst_addr.octet), wl->afx_hdl->retry)); + } + wl->afx_hdl->dev = NULL; + wl->afx_hdl->bssidx = WL_INVALID; + wl_clr_drv_status(wl, SENDING_ACT_FRM, dev); + if (wl->afx_hdl->ack_recv) + return true; /* ACK */ + else + return false; /* NO ACK */ +} + +static s32 +wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, + struct ieee80211_channel *channel, bool offchan, + enum nl80211_channel_type channel_type, + bool channel_type_valid, unsigned int wait, + const u8* buf, size_t len, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + bool no_cck, +#endif + u64 *cookie) +{ + wl_action_frame_t *action_frame; + wl_af_params_t *af_params; + wifi_p2p_ie_t *p2p_ie; + wpa_ie_fixed_t *wps_ie; + scb_val_t scb_val; + const struct ieee80211_mgmt *mgmt; + struct wl_priv *wl = wiphy_priv(wiphy); + struct net_device *dev = NULL; + s32 err = BCME_OK; + s32 bssidx = 0; + u32 p2pie_len = 0; + u32 wpsie_len = 0; + u32 id; + u32 retry = 0; + bool ack = false; + wifi_p2p_pub_act_frame_t *act_frm = NULL; + wifi_p2p_action_frame_t *p2p_act_frm = NULL; + wifi_p2psd_gas_pub_act_frame_t *sd_act_frm = NULL; + s8 eabuf[ETHER_ADDR_STR_LEN]; + int retry_cnt = 0; + + WL_DBG(("Enter \n")); + + if (ndev == wl->p2p_net) { + dev = wl_to_prmry_ndev(wl); + } else { + /* If TX req is for any valid ifidx. Use as is */ + dev = ndev; + } + + /* find bssidx based on ndev */ + bssidx = wl_cfgp2p_find_idx(wl, dev); + if (bssidx == -1) { + + WL_ERR(("Can not find the bssidx for dev( %p )\n", dev)); + return -ENODEV; + } + if (p2p_is_on(wl)) { + /* Suspend P2P discovery search-listen to prevent it from changing the + * channel. + */ + if ((err = wl_cfgp2p_discover_enable_search(wl, false)) < 0) { + WL_ERR(("Can not disable discovery mode\n")); + return -EFAULT; + } + } + *cookie = 0; + id = wl->send_action_id++; + if (id == 0) + id = wl->send_action_id++; + *cookie = id; + mgmt = (const struct ieee80211_mgmt *)buf; + if (ieee80211_is_mgmt(mgmt->frame_control)) { + if (ieee80211_is_probe_resp(mgmt->frame_control)) { + s32 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; + s32 ie_len = len - ie_offset; + if ((p2p_ie = wl_cfgp2p_find_p2pie((u8 *)(buf + ie_offset), ie_len)) + != NULL) { + /* Total length of P2P Information Element */ + p2pie_len = p2p_ie->len + sizeof(p2p_ie->len) + sizeof(p2p_ie->id); + } + if ((wps_ie = wl_cfgp2p_find_wpsie((u8 *)(buf + ie_offset), ie_len)) + != NULL) { + /* Order of Vendor IE is 1) WPS IE + + * 2) P2P IE created by supplicant + * So, it is ok to find start address of WPS IE + * to save IEs + */ + wpsie_len = wps_ie->length + sizeof(wps_ie->length) + + sizeof(wps_ie->tag); + wl_cfgp2p_set_management_ie(wl, dev, bssidx, + VNDR_IE_PRBRSP_FLAG, + (u8 *)wps_ie, wpsie_len + p2pie_len); + } + cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, true, GFP_KERNEL); + goto exit; + } else if (ieee80211_is_disassoc(mgmt->frame_control) || + ieee80211_is_deauth(mgmt->frame_control)) { + memcpy(scb_val.ea.octet, mgmt->da, ETH_ALEN); + scb_val.val = mgmt->u.disassoc.reason_code; + wldev_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scb_val, + sizeof(scb_val_t), true); + WL_DBG(("Disconnect STA : %s scb_val.val %d\n", + bcm_ether_ntoa((const struct ether_addr *)mgmt->da, eabuf), + scb_val.val)); + /* Wait for the deauth event to come, supplicant will do the + * delete iface immediately and we will have problem in sending + * deauth frame if we delete the bss in firmware + */ + wl_delay(400); + cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, true, GFP_KERNEL); + goto exit; + + } else if (ieee80211_is_action(mgmt->frame_control)) { + /* Abort the dwell time of any previous off-channel + * action frame that may be still in effect. Sending + * off-channel action frames relies on the driver's + * scan engine. If a previous off-channel action frame + * tx is still in progress (including the dwell time), + * then this new action frame will not be sent out. + */ + wl_notify_escan_complete(wl, dev, true, true); + + } + + } else { + WL_ERR(("Driver only allows MGMT packet type\n")); + goto exit; + } + + af_params = (wl_af_params_t *) kzalloc(WL_WIFI_AF_PARAMS_SIZE, GFP_KERNEL); + + if (af_params == NULL) + { + WL_ERR(("unable to allocate frame\n")); + return -ENOMEM; + } + + action_frame = &af_params->action_frame; + + /* Add the packet Id */ + action_frame->packetId = *cookie; + WL_DBG(("action frame %d\n", action_frame->packetId)); + /* Add BSSID */ + memcpy(&action_frame->da, &mgmt->da[0], ETHER_ADDR_LEN); + memcpy(&af_params->BSSID, &mgmt->bssid[0], ETHER_ADDR_LEN); + + /* Add the length exepted for 802.11 header */ + action_frame->len = len - DOT11_MGMT_HDR_LEN; + WL_DBG(("action_frame->len: %d\n", action_frame->len)); + + /* Add the channel */ + af_params->channel = + ieee80211_frequency_to_channel(channel->center_freq); + + if (channel->band == IEEE80211_BAND_5GHZ) { + WL_DBG(("5GHz channel %d", af_params->channel)); + err = wldev_ioctl(dev, WLC_SET_CHANNEL, + &af_params->channel, sizeof(af_params->channel), true); + if (err < 0) { + WL_ERR(("WLC_SET_CHANNEL error %d\n", err)); + } + } + + /* Add the dwell time + * Dwell time to stay off-channel to wait for a response action frame + * after transmitting an GO Negotiation action frame + */ + af_params->dwell_time = WL_DWELL_TIME; + + memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN], action_frame->len); + if (wl_cfgp2p_is_pub_action(action_frame->data, action_frame->len)) { + act_frm = (wifi_p2p_pub_act_frame_t *) (action_frame->data); + WL_DBG(("P2P PUB action_frame->len: %d chan %d category %d subtype %d\n", + action_frame->len, af_params->channel, + act_frm->category, act_frm->subtype)); + if (act_frm && ((act_frm->subtype == P2P_PAF_GON_REQ) || + (act_frm->subtype == P2P_PAF_GON_RSP) || + (act_frm->subtype == P2P_PAF_GON_CONF) || + (act_frm->subtype == P2P_PAF_PROVDIS_REQ))) { + wldev_iovar_setint(dev, "mpc", 0); + } + + if (act_frm->subtype == P2P_PAF_GON_REQ) { + WL_DBG(("P2P: GO_NEG_PHASE status set \n")); + wl_set_p2p_status(wl, GO_NEG_PHASE); + } else if (act_frm->subtype == P2P_PAF_GON_CONF) { + /* If we reached till GO Neg confirmation + * reset the filter + */ + WL_DBG(("P2P: GO_NEG_PHASE status cleared \n")); + wl_clr_p2p_status(wl, GO_NEG_PHASE); + } + + if (act_frm->subtype == P2P_PAF_GON_RSP) + retry_cnt = 1; + else retry_cnt = WL_ACT_FRAME_RETRY; + + if (act_frm && act_frm->subtype == P2P_PAF_DEVDIS_REQ) { + af_params->dwell_time = WL_LONG_DWELL_TIME; + } else if (act_frm && + (act_frm->subtype == P2P_PAF_PROVDIS_REQ || + act_frm->subtype == P2P_PAF_PROVDIS_RSP || + act_frm->subtype == P2P_PAF_GON_RSP)) { + af_params->dwell_time = WL_MED_DWELL_TIME; + } + } else if (wl_cfgp2p_is_p2p_action(action_frame->data, action_frame->len)) { + p2p_act_frm = (wifi_p2p_action_frame_t *) (action_frame->data); + WL_DBG(("P2P action_frame->len: %d chan %d category %d subtype %d\n", + action_frame->len, af_params->channel, + p2p_act_frm->category, p2p_act_frm->subtype)); + } else if (wl_cfgp2p_is_gas_action(action_frame->data, action_frame->len)) { + sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *) (action_frame->data); + WL_DBG(("Service Discovery action_frame->len: %d chan %d category %d action %d\n", + action_frame->len, af_params->channel, + sd_act_frm->category, sd_act_frm->action)); + af_params->dwell_time = WL_MED_DWELL_TIME; + retry_cnt = WL_ACT_FRAME_RETRY; + } + wl_cfgp2p_print_actframe(true, action_frame->data, action_frame->len); + /* + * To make sure to send successfully action frame, we have to turn off mpc + */ + if (IS_P2P_SOCIAL(af_params->channel) && + (IS_P2P_PUB_ACT_REQ(act_frm, &act_frm->elts[0], action_frame->len) || + IS_GAS_REQ(sd_act_frm, action_frame->len)) && + wl_to_p2p_bss_saved_ie(wl, P2PAPI_BSSCFG_DEVICE).p2p_probe_req_ie_len) { + /* channel offload require P2P IE for Probe request + * otherwise, we will use wl_cfgp2p_tx_action_frame directly. + * channel offload for action request frame + */ + + /* channel offload for action request frame */ + ack = wl_cfg80211_send_at_common_channel(wl, dev, af_params); + /* We need to retry Service discovery frames as they don't get retried immediately by supplicant*/ + if ((!ack) && (IS_GAS_REQ(sd_act_frm, action_frame->len))) { + for (retry = 1; retry < retry_cnt; retry++) { + WL_DBG(("Service Discovery action_frame retry %d len: %d chan %d category %d action %d\n", + retry, action_frame->len, af_params->channel, + sd_act_frm->category, sd_act_frm->action)); + ack = (wl_cfgp2p_tx_action_frame(wl, dev, + af_params, bssidx)) ? false : true; + if (ack) + break; + } + } + } else { + ack = (wl_cfgp2p_tx_action_frame(wl, dev, af_params, bssidx)) ? false : true; + if (!ack) { + for (retry = 1; retry < retry_cnt; retry++) { + ack = (wl_cfgp2p_tx_action_frame(wl, dev, + af_params, bssidx)) ? false : true; + if (ack) + break; + } + } + + } + cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, ack, GFP_KERNEL); + if (act_frm && act_frm->subtype == P2P_PAF_GON_CONF) { + wldev_iovar_setint(dev, "mpc", 1); + } + kfree(af_params); +exit: + return err; +} + + +static void +wl_cfg80211_mgmt_frame_register(struct wiphy *wiphy, struct net_device *dev, + u16 frame_type, bool reg) +{ + + WL_DBG(("%s: frame_type: %x, reg: %d\n", __func__, frame_type, reg)); + + if (frame_type != (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ)) + return; + + return; +} + + +static s32 +wl_cfg80211_change_bss(struct wiphy *wiphy, + struct net_device *dev, + struct bss_parameters *params) +{ + if (params->use_cts_prot >= 0) { + } + + if (params->use_short_preamble >= 0) { + } + + if (params->use_short_slot_time >= 0) { + } + + if (params->basic_rates) { + } + + if (params->ap_isolate >= 0) { + } + + if (params->ht_opmode >= 0) { + } + + return 0; +} + +static s32 +wl_cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + s32 channel; + s32 err = BCME_OK; + struct wl_priv *wl = wiphy_priv(wiphy); + + if (wl->p2p_net == dev) { + dev = wl_to_prmry_ndev(wl); + } + channel = ieee80211_frequency_to_channel(chan->center_freq); + + if (wl_get_drv_status(wl, AP_CREATING, dev)) { + WL_TRACE(("<0> %s: as!!! in AP creating mode, save chan num:%d\n", + __FUNCTION__, channel)); + wl->hostapd_chan = channel; + if (channel == 14) + return err; /* hostapd requested ch auto-select, will be done later */ + } + + WL_DBG(("netdev_ifidx(%d), chan_type(%d) target channel(%d) \n", + dev->ifindex, channel_type, channel)); + err = wldev_ioctl(dev, WLC_SET_CHANNEL, &channel, sizeof(channel), true); + if (err < 0) { + WL_ERR(("WLC_SET_CHANNEL error %d chip may not be supporting this channel\n", err)); + } + return err; +} + +static s32 +wl_validate_wpa2ie(struct net_device *dev, bcm_tlv_t *wpa2ie, s32 bssidx) +{ + s32 len = 0; + s32 err = BCME_OK; + u16 auth = WL_AUTH_OPEN_SYSTEM; /* d11 open authentication */ + u32 wsec; + u32 pval = 0; + u32 gval = 0; + u32 wpa_auth = 0; + u8* tmp; + wpa_suite_mcast_t *mcast; + wpa_suite_ucast_t *ucast; + wpa_suite_auth_key_mgmt_t *mgmt; + if (wpa2ie == NULL) + goto exit; + + WL_DBG(("Enter \n")); + len = wpa2ie->len; + /* check the mcast cipher */ + mcast = (wpa_suite_mcast_t *)&wpa2ie->data[WPA2_VERSION_LEN]; + tmp = mcast->oui; + switch (tmp[DOT11_OUI_LEN]) { + case WPA_CIPHER_NONE: + gval = 0; + break; + case WPA_CIPHER_WEP_40: + case WPA_CIPHER_WEP_104: + gval = WEP_ENABLED; + break; + case WPA_CIPHER_TKIP: + gval = TKIP_ENABLED; + break; + case WPA_CIPHER_AES_CCM: + gval = AES_ENABLED; + break; + default: + WL_ERR(("No Security Info\n")); + break; + } + len -= WPA_SUITE_LEN; + /* check the unicast cipher */ + ucast = (wpa_suite_ucast_t *)&mcast[1]; + ltoh16_ua(&ucast->count); + tmp = ucast->list[0].oui; + switch (tmp[DOT11_OUI_LEN]) { + case WPA_CIPHER_NONE: + pval = 0; + break; + case WPA_CIPHER_WEP_40: + case WPA_CIPHER_WEP_104: + pval = WEP_ENABLED; + break; + case WPA_CIPHER_TKIP: + pval = TKIP_ENABLED; + break; + case WPA_CIPHER_AES_CCM: + pval = AES_ENABLED; + break; + default: + WL_ERR(("No Security Info\n")); + } + /* FOR WPS , set SEC_OW_ENABLED */ + wsec = (pval | gval | SES_OW_ENABLED); + /* check the AKM */ + mgmt = (wpa_suite_auth_key_mgmt_t *)&ucast->list[1]; + ltoh16_ua(&mgmt->count); + tmp = (u8 *)&mgmt->list[0]; + switch (tmp[DOT11_OUI_LEN]) { + case RSN_AKM_NONE: + wpa_auth = WPA_AUTH_NONE; + break; + case RSN_AKM_UNSPECIFIED: + wpa_auth = WPA2_AUTH_UNSPECIFIED; + break; + case RSN_AKM_PSK: + wpa_auth = WPA2_AUTH_PSK; + break; + default: + WL_ERR(("No Key Mgmt Info\n")); + } + /* set auth */ + err = wldev_iovar_setint_bsscfg(dev, "auth", auth, bssidx); + if (err < 0) { + WL_ERR(("auth error %d\n", err)); + return BCME_ERROR; + } + /* set wsec */ + err = wldev_iovar_setint_bsscfg(dev, "wsec", wsec, bssidx); + if (err < 0) { + WL_ERR(("wsec error %d\n", err)); + return BCME_ERROR; + } + /* set upper-layer auth */ + err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", wpa_auth, bssidx); + if (err < 0) { + WL_ERR(("wpa_auth error %d\n", err)); + return BCME_ERROR; + } +exit: + return 0; +} + +static s32 +wl_validate_wpaie(struct net_device *dev, wpa_ie_fixed_t *wpaie, s32 bssidx) +{ + wpa_suite_mcast_t *mcast; + wpa_suite_ucast_t *ucast; + wpa_suite_auth_key_mgmt_t *mgmt; + u16 auth = WL_AUTH_OPEN_SYSTEM; /* d11 open authentication */ + u16 count; + s32 err = BCME_OK; + s32 len = 0; + u32 i; + u32 wsec; + u32 pval = 0; + u32 gval = 0; + u32 wpa_auth = 0; + u32 tmp = 0; + + if (wpaie == NULL) + goto exit; + WL_DBG(("Enter \n")); + len = wpaie->length; /* value length */ + len -= WPA_IE_TAG_FIXED_LEN; + /* check for multicast cipher suite */ + if (len < WPA_SUITE_LEN) { + WL_INFO(("no multicast cipher suite\n")); + goto exit; + } + + /* pick up multicast cipher */ + mcast = (wpa_suite_mcast_t *)&wpaie[1]; + len -= WPA_SUITE_LEN; + if (!bcmp(mcast->oui, WPA_OUI, WPA_OUI_LEN)) { + if (IS_WPA_CIPHER(mcast->type)) { + tmp = 0; + switch (mcast->type) { + case WPA_CIPHER_NONE: + tmp = 0; + break; + case WPA_CIPHER_WEP_40: + case WPA_CIPHER_WEP_104: + tmp = WEP_ENABLED; + break; + case WPA_CIPHER_TKIP: + tmp = TKIP_ENABLED; + break; + case WPA_CIPHER_AES_CCM: + tmp = AES_ENABLED; + break; + default: + WL_ERR(("No Security Info\n")); + } + gval |= tmp; + } + } + /* Check for unicast suite(s) */ + if (len < WPA_IE_SUITE_COUNT_LEN) { + WL_INFO(("no unicast suite\n")); + goto exit; + } + /* walk thru unicast cipher list and pick up what we recognize */ + ucast = (wpa_suite_ucast_t *)&mcast[1]; + count = ltoh16_ua(&ucast->count); + len -= WPA_IE_SUITE_COUNT_LEN; + for (i = 0; i < count && len >= WPA_SUITE_LEN; + i++, len -= WPA_SUITE_LEN) { + if (!bcmp(ucast->list[i].oui, WPA_OUI, WPA_OUI_LEN)) { + if (IS_WPA_CIPHER(ucast->list[i].type)) { + tmp = 0; + switch (ucast->list[i].type) { + case WPA_CIPHER_NONE: + tmp = 0; + break; + case WPA_CIPHER_WEP_40: + case WPA_CIPHER_WEP_104: + tmp = WEP_ENABLED; + break; + case WPA_CIPHER_TKIP: + tmp = TKIP_ENABLED; + break; + case WPA_CIPHER_AES_CCM: + tmp = AES_ENABLED; + break; + default: + WL_ERR(("No Security Info\n")); + } + pval |= tmp; + } + } + } + len -= (count - i) * WPA_SUITE_LEN; + /* Check for auth key management suite(s) */ + if (len < WPA_IE_SUITE_COUNT_LEN) { + WL_INFO((" no auth key mgmt suite\n")); + goto exit; + } + /* walk thru auth management suite list and pick up what we recognize */ + mgmt = (wpa_suite_auth_key_mgmt_t *)&ucast->list[count]; + count = ltoh16_ua(&mgmt->count); + len -= WPA_IE_SUITE_COUNT_LEN; + for (i = 0; i < count && len >= WPA_SUITE_LEN; + i++, len -= WPA_SUITE_LEN) { + if (!bcmp(mgmt->list[i].oui, WPA_OUI, WPA_OUI_LEN)) { + if (IS_WPA_AKM(mgmt->list[i].type)) { + tmp = 0; + switch (mgmt->list[i].type) { + case RSN_AKM_NONE: + tmp = WPA_AUTH_NONE; + break; + case RSN_AKM_UNSPECIFIED: + tmp = WPA_AUTH_UNSPECIFIED; + break; + case RSN_AKM_PSK: + tmp = WPA_AUTH_PSK; + break; + default: + WL_ERR(("No Key Mgmt Info\n")); + } + wpa_auth |= tmp; + } + } + + } + /* FOR WPS , set SEC_OW_ENABLED */ + wsec = (pval | gval | SES_OW_ENABLED); + /* set auth */ + err = wldev_iovar_setint_bsscfg(dev, "auth", auth, bssidx); + if (err < 0) { + WL_ERR(("auth error %d\n", err)); + return BCME_ERROR; + } + /* set wsec */ + err = wldev_iovar_setint_bsscfg(dev, "wsec", wsec, bssidx); + if (err < 0) { + WL_ERR(("wsec error %d\n", err)); + return BCME_ERROR; + } + /* set upper-layer auth */ + err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", wpa_auth, bssidx); + if (err < 0) { + WL_ERR(("wpa_auth error %d\n", err)); + return BCME_ERROR; + } +exit: + return 0; +} + +static s32 +wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *info) +{ + s32 err = BCME_OK; + bcm_tlv_t *ssid_ie; + wlc_ssid_t ssid; + struct wl_priv *wl = wiphy_priv(wiphy); + struct wl_join_params join_params; + wpa_ie_fixed_t *wps_ie; + wpa_ie_fixed_t *wpa_ie; + bcm_tlv_t *wpa2_ie; + wifi_p2p_ie_t *p2p_ie; + bool is_bssup = false; + bool update_bss = false; + bool pbc = false; + u16 wpsie_len = 0; + u16 p2pie_len = 0; + u8 beacon_ie[IE_MAX_LEN]; + s32 ie_offset = 0; + s32 bssidx = 0; + s32 infra = 1; + s32 join_params_size = 0; + s32 ap = 0; + WL_DBG(("interval (%d) dtim_period (%d) head_len (%d) tail_len (%d)\n", + info->interval, info->dtim_period, info->head_len, info->tail_len)); + + if (wl->p2p_net == dev) { + dev = wl_to_prmry_ndev(wl); + } + + bssidx = wl_cfgp2p_find_idx(wl, dev); + if (p2p_is_on(wl) && + (bssidx == wl_to_p2p_bss_bssidx(wl, + P2PAPI_BSSCFG_CONNECTION))) { + memset(beacon_ie, 0, sizeof(beacon_ie)); + /* We don't need to set beacon for P2P_GO, + * but need to parse ssid from beacon_parameters + * because there is no way to set ssid + */ + ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; + /* find the SSID */ + if ((ssid_ie = bcm_parse_tlvs((u8 *)&info->head[ie_offset], + info->head_len - ie_offset, + DOT11_MNG_SSID_ID)) != NULL) { + memcpy(wl->p2p->ssid.SSID, ssid_ie->data, ssid_ie->len); + wl->p2p->ssid.SSID_len = ssid_ie->len; + WL_DBG(("SSID (%s) in Head \n", ssid_ie->data)); + + } else { + WL_ERR(("No SSID in beacon \n")); + } + + /* find the WPSIE */ + if ((wps_ie = wl_cfgp2p_find_wpsie((u8 *)info->tail, info->tail_len)) != NULL) { + wpsie_len = wps_ie->length + WPA_RSN_IE_TAG_FIXED_LEN; + /* + * Should be compared with saved ie before saving it + */ + wl_validate_wps_ie((char *) wps_ie, &pbc); + memcpy(beacon_ie, wps_ie, wpsie_len); + } else { + WL_ERR(("No WPSIE in beacon \n")); + } + + + /* find the P2PIE */ + if ((p2p_ie = wl_cfgp2p_find_p2pie((u8 *)info->tail, info->tail_len)) != NULL) { + /* Total length of P2P Information Element */ + p2pie_len = p2p_ie->len + sizeof(p2p_ie->len) + sizeof(p2p_ie->id); + memcpy(&beacon_ie[wpsie_len], p2p_ie, p2pie_len); + + } else { + WL_ERR(("No P2PIE in beacon \n")); + } + /* add WLC_E_PROBREQ_MSG event to respose probe_request from STA */ + wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); + wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_BEACON_FLAG, + beacon_ie, wpsie_len + p2pie_len); + + /* find the RSN_IE */ + if ((wpa2_ie = bcm_parse_tlvs((u8 *)info->tail, info->tail_len, + DOT11_MNG_RSN_ID)) != NULL) { + WL_DBG((" WPA2 IE is found\n")); + } + is_bssup = wl_cfgp2p_bss_isup(dev, bssidx); + + if (!is_bssup && (wpa2_ie != NULL)) { + wldev_iovar_setint(dev, "mpc", 0); + if ((err = wl_validate_wpa2ie(dev, wpa2_ie, bssidx)) < 0) { + WL_ERR(("WPA2 IE parsing error")); + goto exit; + } + err = wldev_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(s32), true); + if (err < 0) { + WL_ERR(("SET INFRA error %d\n", err)); + goto exit; + } + err = wldev_iovar_setbuf_bsscfg(dev, "ssid", &wl->p2p->ssid, + sizeof(wl->p2p->ssid), wl->ioctl_buf, WLC_IOCTL_MAXLEN, + bssidx, &wl->ioctl_buf_sync); + if (err < 0) { + WL_ERR(("GO SSID setting error %d\n", err)); + goto exit; + } + if ((err = wl_cfgp2p_bss(wl, dev, bssidx, 1)) < 0) { + WL_ERR(("GO Bring up error %d\n", err)); + goto exit; + } + } + } else if (wl_get_drv_status(wl, AP_CREATING, dev)) { + ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; + ap = 1; + /* find the SSID */ + if ((ssid_ie = bcm_parse_tlvs((u8 *)&info->head[ie_offset], + info->head_len - ie_offset, + DOT11_MNG_SSID_ID)) != NULL) { + memset(&ssid, 0, sizeof(wlc_ssid_t)); + memcpy(ssid.SSID, ssid_ie->data, ssid_ie->len); + WL_DBG(("SSID is (%s) in Head \n", ssid.SSID)); + ssid.SSID_len = ssid_ie->len; + wldev_iovar_setint(dev, "mpc", 0); + wldev_ioctl(dev, WLC_DOWN, &ap, sizeof(s32), true); + wldev_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(s32), true); + if ((err = wldev_ioctl(dev, WLC_SET_AP, &ap, sizeof(s32), true)) < 0) { + WL_ERR(("setting AP mode failed %d \n", err)); + return err; + } + + /* if requested, do softap ch autoselect */ + if (wl->hostapd_chan == 14) { + int auto_chan; + if ((err = wldev_get_auto_channel(dev, &auto_chan)) != 0) { + WL_ERR(("softap: auto chan select failed," + " will use ch 6\n")); + auto_chan = 6; + } else { + printf("<0>softap: got auto ch:%d\n", auto_chan); + } + err = wldev_ioctl(dev, WLC_SET_CHANNEL, + &auto_chan, sizeof(auto_chan), true); + if (err < 0) { + WL_ERR(("softap: WLC_SET_CHANNEL error %d chip" + " may not be supporting this channel\n", err)); + return err; + } + } + + /* find the RSN_IE */ + if ((wpa2_ie = bcm_parse_tlvs((u8 *)info->tail, info->tail_len, + DOT11_MNG_RSN_ID)) != NULL) { + WL_DBG((" WPA2 IE is found\n")); + } + /* find the WPA_IE */ + if ((wpa_ie = wl_cfgp2p_find_wpaie((u8 *)info->tail, + info->tail_len)) != NULL) { + WL_DBG((" WPA IE is found\n")); + } + if ((wpa_ie != NULL || wpa2_ie != NULL)) { + if (wl_validate_wpa2ie(dev, wpa2_ie, bssidx) < 0 || + wl_validate_wpaie(dev, wpa_ie, bssidx) < 0) { + wl->ap_info->security_mode = false; + return BCME_ERROR; + } + wl->ap_info->security_mode = true; + if (wl->ap_info->rsn_ie) { + kfree(wl->ap_info->rsn_ie); + wl->ap_info->rsn_ie = NULL; + } + if (wl->ap_info->wpa_ie) { + kfree(wl->ap_info->wpa_ie); + wl->ap_info->wpa_ie = NULL; + } + if (wl->ap_info->wps_ie) { + kfree(wl->ap_info->wps_ie); + wl->ap_info->wps_ie = NULL; + } + if (wpa_ie != NULL) { + /* WPAIE */ + wl->ap_info->rsn_ie = NULL; + wl->ap_info->wpa_ie = kmemdup(wpa_ie, + wpa_ie->length + WPA_RSN_IE_TAG_FIXED_LEN, + GFP_KERNEL); + } else { + /* RSNIE */ + wl->ap_info->wpa_ie = NULL; + wl->ap_info->rsn_ie = kmemdup(wpa2_ie, + wpa2_ie->len + WPA_RSN_IE_TAG_FIXED_LEN, + GFP_KERNEL); + } + } else + wl->ap_info->security_mode = false; + /* find the WPSIE */ + if ((wps_ie = wl_cfgp2p_find_wpsie((u8 *)info->tail, + info->tail_len)) != NULL) { + wpsie_len = wps_ie->length +WPA_RSN_IE_TAG_FIXED_LEN; + /* + * Should be compared with saved ie before saving it + */ + wl_validate_wps_ie((char *) wps_ie, &pbc); + memcpy(beacon_ie, wps_ie, wpsie_len); + wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_BEACON_FLAG, + beacon_ie, wpsie_len); + wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); + /* add WLC_E_PROBREQ_MSG event to respose probe_request from STA */ + wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); + } else { + WL_DBG(("No WPSIE in beacon \n")); + } + if (info->interval) { + if ((err = wldev_ioctl(dev, WLC_SET_BCNPRD, + &info->interval, sizeof(s32), true)) < 0) { + WL_ERR(("Beacon Interval Set Error, %d\n", err)); + return err; + } + } + if (info->dtim_period) { + if ((err = wldev_ioctl(dev, WLC_SET_DTIMPRD, + &info->dtim_period, sizeof(s32), true)) < 0) { + WL_ERR(("DTIM Interval Set Error, %d\n", err)); + return err; + } + } + err = wldev_ioctl(dev, WLC_UP, &ap, sizeof(s32), true); + if (unlikely(err)) { + WL_ERR(("WLC_UP error (%d)\n", err)); + return err; + } + memset(&join_params, 0, sizeof(join_params)); + /* join parameters starts with ssid */ + join_params_size = sizeof(join_params.ssid); + memcpy(join_params.ssid.SSID, ssid.SSID, ssid.SSID_len); + join_params.ssid.SSID_len = htod32(ssid.SSID_len); + /* create softap */ + if ((err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, + join_params_size, true)) == 0) { + wl_clr_drv_status(wl, AP_CREATING, dev); + wl_set_drv_status(wl, AP_CREATED, dev); + } + } + } else if (wl_get_drv_status(wl, AP_CREATED, dev)) { + ap = 1; + /* find the WPSIE */ + if ((wps_ie = wl_cfgp2p_find_wpsie((u8 *)info->tail, info->tail_len)) != NULL) { + wpsie_len = wps_ie->length + WPA_RSN_IE_TAG_FIXED_LEN; + /* + * Should be compared with saved ie before saving it + */ + wl_validate_wps_ie((char *) wps_ie, &pbc); + memcpy(beacon_ie, wps_ie, wpsie_len); + wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_BEACON_FLAG, + beacon_ie, wpsie_len); + if (wl->ap_info->wps_ie && + memcmp(wl->ap_info->wps_ie, wps_ie, wpsie_len)) { + WL_DBG((" WPS IE is changed\n")); + kfree(wl->ap_info->wps_ie); + wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); + /* add WLC_E_PROBREQ_MSG event to respose probe_request from STA */ + wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); + } else if (wl->ap_info->wps_ie == NULL) { + WL_DBG((" WPS IE is added\n")); + wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); + /* add WLC_E_PROBREQ_MSG event to respose probe_request from STA */ + wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); + } + /* find the RSN_IE */ + if ((wpa2_ie = bcm_parse_tlvs((u8 *)info->tail, info->tail_len, + DOT11_MNG_RSN_ID)) != NULL) { + WL_DBG((" WPA2 IE is found\n")); + } + /* find the WPA_IE */ + if ((wpa_ie = wl_cfgp2p_find_wpaie((u8 *)info->tail, + info->tail_len)) != NULL) { + WL_DBG((" WPA IE is found\n")); + } + if ((wpa_ie != NULL || wpa2_ie != NULL)) { + if (!wl->ap_info->security_mode) { + /* change from open mode to security mode */ + update_bss = true; + if (wpa_ie != NULL) { + wl->ap_info->wpa_ie = kmemdup(wpa_ie, + wpa_ie->length + WPA_RSN_IE_TAG_FIXED_LEN, + GFP_KERNEL); + } else { + wl->ap_info->rsn_ie = kmemdup(wpa2_ie, + wpa2_ie->len + WPA_RSN_IE_TAG_FIXED_LEN, + GFP_KERNEL); + } + } else if (wl->ap_info->wpa_ie) { + /* change from WPA mode to WPA2 mode */ + if (wpa2_ie != NULL) { + update_bss = true; + kfree(wl->ap_info->wpa_ie); + wl->ap_info->rsn_ie = kmemdup(wpa2_ie, + wpa2_ie->len + WPA_RSN_IE_TAG_FIXED_LEN, + GFP_KERNEL); + wl->ap_info->wpa_ie = NULL; + } + else if (memcmp(wl->ap_info->wpa_ie, + wpa_ie, wpa_ie->length + + WPA_RSN_IE_TAG_FIXED_LEN)) { + kfree(wl->ap_info->wpa_ie); + update_bss = true; + wl->ap_info->wpa_ie = kmemdup(wpa_ie, + wpa_ie->length + WPA_RSN_IE_TAG_FIXED_LEN, + GFP_KERNEL); + wl->ap_info->rsn_ie = NULL; + } + } else { + /* change from WPA2 mode to WPA mode */ + if (wpa_ie != NULL) { + update_bss = true; + kfree(wl->ap_info->rsn_ie); + wl->ap_info->rsn_ie = NULL; + wl->ap_info->wpa_ie = kmemdup(wpa_ie, + wpa_ie->length + WPA_RSN_IE_TAG_FIXED_LEN, + GFP_KERNEL); + } else if (memcmp(wl->ap_info->rsn_ie, + wpa2_ie, wpa2_ie->len + WPA_RSN_IE_TAG_FIXED_LEN)) { + update_bss = true; + kfree(wl->ap_info->rsn_ie); + wl->ap_info->rsn_ie = kmemdup(wpa2_ie, + wpa2_ie->len + WPA_RSN_IE_TAG_FIXED_LEN, + GFP_KERNEL); + wl->ap_info->wpa_ie = NULL; + } + } + if (update_bss) { + wl->ap_info->security_mode = true; + wl_cfgp2p_bss(wl, dev, bssidx, 0); + if (wl_validate_wpa2ie(dev, wpa2_ie, bssidx) < 0 || + wl_validate_wpaie(dev, wpa_ie, bssidx) < 0) { + return BCME_ERROR; + } + wl_cfgp2p_bss(wl, dev, bssidx, 1); + } + } + } else { + WL_ERR(("No WPSIE in beacon \n")); + } + } +exit: + if (err) + wldev_iovar_setint(dev, "mpc", 1); + return err; +} + +#ifdef WL_SCHED_SCAN +#define PNO_TIME 30 +#define PNO_REPEAT 4 +#define PNO_FREQ_EXPO_MAX 2 +int wl_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_sched_scan_request *request) +{ + ushort pno_time = PNO_TIME; + int pno_repeat = PNO_REPEAT; + int pno_freq_expo_max = PNO_FREQ_EXPO_MAX; + wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT]; + struct wl_priv *wl = wiphy_priv(wiphy); + struct cfg80211_ssid *ssid = NULL; + int ssid_count = 0; + int i; + int ret = 0; + + WL_DBG(("Enter n_match_sets:%d n_ssids:%d \n", + request->n_match_sets, request->n_ssids)); + WL_DBG(("ssids:%d pno_time:%d pno_repeat:%d pno_freq:%d \n", + request->n_ssids, pno_time, pno_repeat, pno_freq_expo_max)); + +#if defined(WL_ENABLE_P2P_IF) + /* While GO is operational, PNO is not supported */ + if (dhd_cfg80211_get_opmode(wl) & P2P_GO_ENABLED) { + WL_DBG(("PNO not enabled! op_mode: P2P GO")); + return -1; + } +#endif + + if (!request || !request->n_ssids || !request->n_match_sets) { + WL_ERR(("Invalid sched scan req!! n_ssids:%d \n", request->n_ssids)); + return -EINVAL; + } + + memset(&ssids_local, 0, sizeof(ssids_local)); + + if (request->n_match_sets > 0) { + for (i = 0; i < request->n_match_sets; i++) { + ssid = &request->match_sets[i].ssid; + memcpy(ssids_local[i].SSID, ssid->ssid, ssid->ssid_len); + ssids_local[i].SSID_len = ssid->ssid_len; + WL_DBG((">>> PNO filter set for ssid (%s) \n", ssid->ssid)); + ssid_count++; + } + } + + if (request->n_ssids > 0) { + for (i = 0; i < request->n_ssids; i++) { + /* Active scan req for ssids */ + WL_DBG((">>> Active scan req for ssid (%s) \n", request->ssids[i].ssid)); + + /* match_set ssids is a supert set of n_ssid list, so we need + * not add these set seperately + */ + } + } + + if (ssid_count) { + if ((ret = dhd_dev_pno_set(dev, ssids_local, request->n_match_sets, + pno_time, pno_repeat, pno_freq_expo_max)) < 0) { + WL_ERR(("PNO setup failed!! ret=%d \n", ret)); + return -EINVAL; + } + + /* Enable the PNO */ + if (dhd_dev_pno_enable(dev, 1) < 0) { + WL_ERR(("PNO enable failed!! ret=%d \n", ret)); + return -EINVAL; + } + wl->sched_scan_req = request; + } else { + return -EINVAL; + } + + return 0; +} + +int wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + + WL_DBG(("Enter \n")); + + if (dhd_dev_pno_enable(dev, 0) < 0) + WL_ERR(("PNO disable failed")); + + if (dhd_dev_pno_reset(dev) < 0) + WL_ERR(("PNO reset failed")); + + if (wl->scan_request && wl->sched_scan_running) { + wl_notify_escan_complete(wl, dev, true, true); + } + + wl->sched_scan_req = NULL; + wl->sched_scan_running = FALSE; + + return 0; +} +#endif /* WL_SCHED_SCAN */ + +static struct cfg80211_ops wl_cfg80211_ops = { + .add_virtual_intf = wl_cfg80211_add_virtual_iface, + .del_virtual_intf = wl_cfg80211_del_virtual_iface, + .change_virtual_intf = wl_cfg80211_change_virtual_iface, + .scan = wl_cfg80211_scan, + .set_wiphy_params = wl_cfg80211_set_wiphy_params, + .join_ibss = wl_cfg80211_join_ibss, + .leave_ibss = wl_cfg80211_leave_ibss, + .get_station = wl_cfg80211_get_station, + .set_tx_power = wl_cfg80211_set_tx_power, + .get_tx_power = wl_cfg80211_get_tx_power, + .add_key = wl_cfg80211_add_key, + .del_key = wl_cfg80211_del_key, + .get_key = wl_cfg80211_get_key, + .set_default_key = wl_cfg80211_config_default_key, + .set_default_mgmt_key = wl_cfg80211_config_default_mgmt_key, + .set_power_mgmt = wl_cfg80211_set_power_mgmt, + .connect = wl_cfg80211_connect, + .disconnect = wl_cfg80211_disconnect, + .suspend = wl_cfg80211_suspend, + .resume = wl_cfg80211_resume, + .set_pmksa = wl_cfg80211_set_pmksa, + .del_pmksa = wl_cfg80211_del_pmksa, + .flush_pmksa = wl_cfg80211_flush_pmksa, + .remain_on_channel = wl_cfg80211_remain_on_channel, + .cancel_remain_on_channel = wl_cfg80211_cancel_remain_on_channel, + .mgmt_tx = wl_cfg80211_mgmt_tx, + .mgmt_frame_register = wl_cfg80211_mgmt_frame_register, + .change_bss = wl_cfg80211_change_bss, + .set_channel = wl_cfg80211_set_channel, + .set_beacon = wl_cfg80211_add_set_beacon, + .add_beacon = wl_cfg80211_add_set_beacon, +#ifdef WL_SCHED_SCAN + .sched_scan_start = wl_cfg80211_sched_scan_start, + .sched_scan_stop = wl_cfg80211_sched_scan_stop, +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) */ +}; + +s32 wl_mode_to_nl80211_iftype(s32 mode) +{ + s32 err = 0; + + switch (mode) { + case WL_MODE_BSS: + return NL80211_IFTYPE_STATION; + case WL_MODE_IBSS: + return NL80211_IFTYPE_ADHOC; + case WL_MODE_AP: + return NL80211_IFTYPE_AP; + default: + return NL80211_IFTYPE_UNSPECIFIED; + } + + return err; +} + +static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *sdiofunc_dev) +{ + s32 err = 0; + wdev->wiphy = + wiphy_new(&wl_cfg80211_ops, sizeof(struct wl_priv)); + if (unlikely(!wdev->wiphy)) { + WL_ERR(("Couldn not allocate wiphy device\n")); + err = -ENOMEM; + return err; + } + set_wiphy_dev(wdev->wiphy, sdiofunc_dev); + wdev->wiphy->max_scan_ie_len = WL_SCAN_IE_LEN_MAX; + /* Report how many SSIDs Driver can support per Scan request */ + wdev->wiphy->max_scan_ssids = WL_SCAN_PARAMS_SSID_MAX; + wdev->wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; +#ifdef WL_SCHED_SCAN + wdev->wiphy->max_sched_scan_ssids = MAX_PFN_LIST_COUNT; + wdev->wiphy->max_match_sets = MAX_PFN_LIST_COUNT; + wdev->wiphy->max_sched_scan_ie_len = WL_SCAN_IE_LEN_MAX; + wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; +#endif /* WL_SCHED_SCAN */ + wdev->wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) + | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MONITOR); + + wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; + /* wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; - set in runtime */ + wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + wdev->wiphy->cipher_suites = __wl_cipher_suites; + wdev->wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); + wdev->wiphy->max_remain_on_channel_duration = 5000; + wdev->wiphy->mgmt_stypes = wl_cfg80211_default_mgmt_stypes; +#ifndef WL_POWERSAVE_DISABLED + wdev->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; +#else + wdev->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; +#endif /* !WL_POWERSAVE_DISABLED */ + wdev->wiphy->flags |= WIPHY_FLAG_NETNS_OK | + WIPHY_FLAG_4ADDR_AP | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39) + WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS | +#endif + WIPHY_FLAG_4ADDR_STATION; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; +#endif + WL_DBG(("Registering custom regulatory)\n")); + wdev->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + wiphy_apply_custom_regulatory(wdev->wiphy, &brcm_regdom); + /* Now we can register wiphy with cfg80211 module */ + err = wiphy_register(wdev->wiphy); + if (unlikely(err < 0)) { + WL_ERR(("Couldn not register wiphy device (%d)\n", err)); + wiphy_free(wdev->wiphy); + } + return err; +} + +static void wl_free_wdev(struct wl_priv *wl) +{ + struct wireless_dev *wdev = wl->wdev; + struct wiphy *wiphy; + if (!wdev) { + WL_ERR(("wdev is invalid\n")); + return; + } + wiphy = wdev->wiphy; + wiphy_unregister(wdev->wiphy); + wdev->wiphy->dev.parent = NULL; + + wl_delete_all_netinfo(wl); + wiphy_free(wiphy); + /* PLEASE do NOT call any function after wiphy_free, the driver's private structure "wl", + * which is the private part of wiphy, has been freed in wiphy_free !!!!!!!!!!! + */ +} + +static s32 wl_inform_bss(struct wl_priv *wl) +{ + struct wl_scan_results *bss_list; + struct wl_bss_info *bi = NULL; /* must be initialized */ + s32 err = 0; + s32 i; + + bss_list = wl->bss_list; + WL_DBG(("scanned AP count (%d)\n", bss_list->count)); + bi = next_bss(bss_list, bi); + for_each_bss(bss_list, bi, i) { + err = wl_inform_single_bss(wl, bi); + if (unlikely(err)) + break; + } + return err; +} + +static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) +{ + struct wiphy *wiphy = wiphy_from_scan(wl); + struct ieee80211_mgmt *mgmt; + struct ieee80211_channel *channel; + struct ieee80211_supported_band *band; + struct wl_cfg80211_bss_info *notif_bss_info; + struct wl_scan_req *sr = wl_to_sr(wl); + struct beacon_proberesp *beacon_proberesp; + struct cfg80211_bss *cbss = NULL; + s32 mgmt_type; + s32 signal; + u32 freq; + s32 err = 0; + + if (unlikely(dtoh32(bi->length) > WL_BSS_INFO_MAX)) { + WL_DBG(("Beacon is larger than buffer. Discarding\n")); + return err; + } + notif_bss_info = kzalloc(sizeof(*notif_bss_info) + sizeof(*mgmt) + - sizeof(u8) + WL_BSS_INFO_MAX, GFP_KERNEL); + if (unlikely(!notif_bss_info)) { + WL_ERR(("notif_bss_info alloc failed\n")); + return -ENOMEM; + } + mgmt = (struct ieee80211_mgmt *)notif_bss_info->frame_buf; + notif_bss_info->channel = + bi->ctl_ch ? bi->ctl_ch : CHSPEC_CHANNEL(bi->chanspec); + + if (notif_bss_info->channel <= CH_MAX_2G_CHANNEL) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (!band) { + WL_ERR(("No valid band")); + kfree(notif_bss_info); + return -EINVAL; + } + notif_bss_info->rssi = dtoh16(bi->RSSI); + memcpy(mgmt->bssid, &bi->BSSID, ETHER_ADDR_LEN); + mgmt_type = wl->active_scan ? + IEEE80211_STYPE_PROBE_RESP : IEEE80211_STYPE_BEACON; + if (!memcmp(bi->SSID, sr->ssid.SSID, bi->SSID_len)) { + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | mgmt_type); + } + beacon_proberesp = wl->active_scan ? + (struct beacon_proberesp *)&mgmt->u.probe_resp : + (struct beacon_proberesp *)&mgmt->u.beacon; + beacon_proberesp->timestamp = 0; + beacon_proberesp->beacon_int = cpu_to_le16(bi->beacon_period); + beacon_proberesp->capab_info = cpu_to_le16(bi->capability); + wl_rst_ie(wl); + + wl_mrg_ie(wl, ((u8 *) bi) + bi->ie_offset, bi->ie_length); + wl_cp_ie(wl, beacon_proberesp->variable, WL_BSS_INFO_MAX - + offsetof(struct wl_cfg80211_bss_info, frame_buf)); + notif_bss_info->frame_len = offsetof(struct ieee80211_mgmt, + u.beacon.variable) + wl_get_ielen(wl); +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) + freq = ieee80211_channel_to_frequency(notif_bss_info->channel); +#else + freq = ieee80211_channel_to_frequency(notif_bss_info->channel, band->band); +#endif + channel = ieee80211_get_channel(wiphy, freq); + if (!channel) { + WL_ERR(("No valid channel")); + kfree(notif_bss_info); + return -EINVAL; + } + + WL_DBG(("SSID : \"%s\", rssi %d, channel %d, capability : 0x04%x, bssid %pM " + "mgmt_type %d frame_len %d\n", bi->SSID, + notif_bss_info->rssi, notif_bss_info->channel, + mgmt->u.beacon.capab_info, &bi->BSSID, mgmt_type, + notif_bss_info->frame_len)); + + signal = notif_bss_info->rssi * 100; + +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + if (wl->p2p && wl->p2p_net && wl->scan_request && + ((wl->scan_request->dev == wl->p2p_net) || + (wl->scan_request->dev == wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION)))){ +#else + if (p2p_is_on(wl) && ( p2p_scan(wl) || + (wl->scan_request->dev == wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION)))) { +#endif + /* find the P2PIE, if we do not find it, we will discard this frame */ + wifi_p2p_ie_t * p2p_ie; + if ((p2p_ie = wl_cfgp2p_find_p2pie((u8 *)beacon_proberesp->variable, + wl_get_ielen(wl))) == NULL) { + WL_ERR(("Couldn't find P2PIE in probe response/beacon\n")); + kfree(notif_bss_info); + return err; + } + else if( wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_DEV_INFO) == NULL) + { + WL_DBG(("Couldn't find P2P_SEID_DEV_INFO in probe response/beacon\n")); + kfree(notif_bss_info); + return err; + } + } + if (!mgmt->u.probe_resp.timestamp) { + struct timeval tv; + + do_gettimeofday(&tv); + mgmt->u.probe_resp.timestamp = ((u64)tv.tv_sec * 1000000) + + tv.tv_usec; + } + + cbss = cfg80211_inform_bss_frame(wiphy, channel, mgmt, + le16_to_cpu(notif_bss_info->frame_len), signal, GFP_KERNEL); + if (unlikely(!cbss)) { + WL_ERR(("cfg80211_inform_bss_frame error\n")); + kfree(notif_bss_info); + return -EINVAL; + } + + cfg80211_put_bss(cbss); + kfree(notif_bss_info); + + return err; +} + +static s32 wl_inform_ibss(struct wl_priv *wl, const u8 *bssid) +{ + struct net_device *ndev = wl_to_prmry_ndev(wl); + struct wiphy *wiphy = wl_to_wiphy(wl); + struct wl_bss_info *bi = NULL; + struct ieee80211_channel *notify_channel; + struct ieee80211_supported_band *band; + struct cfg80211_bss *bss; + s32 err = 0; + u16 channel; + u32 freq; + u32 wsec = 0; + u16 notify_capability; + u16 notify_interval; + u8 *notify_ie; + size_t notify_ielen; + s32 notify_signal; + + WL_TRACE(("Enter\n")); + + if (wl->scan_request) { + wl_notify_escan_complete(wl, ndev, true, true); + } + + mutex_lock(&wl->usr_sync); + + *(u32 *)wl->extra_buf = htod32(WL_EXTRA_BUF_MAX); + err = wldev_ioctl(ndev, WLC_GET_BSS_INFO, wl->extra_buf, + WL_EXTRA_BUF_MAX, false); + if (err) { + WL_ERR(("Failed to get bss info for IBSS\n")); + err = -EIO; + goto CleanUp; + } + bi = (struct wl_bss_info *)(wl->extra_buf + 4); + + if (memcmp(bssid, &bi->BSSID, ETHER_ADDR_LEN)) { + WL_ERR(("BSSID mismatch: Inform %02x:%02x:%02x:%02x:%02x:%02x," + "%02x:%02x:%02x:%02x:%02x:%02x\n", + bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], + bi->BSSID.octet[0], bi->BSSID.octet[1], bi->BSSID.octet[2], + bi->BSSID.octet[3], bi->BSSID.octet[4], + bi->BSSID.octet[5])); + err = -EINVAL; + goto CleanUp; + } + + err = wldev_iovar_getint(ndev, "wsec", &wsec); + if (err) { + WL_ERR(("wsec failed: %d\n", err)); + err = -EIO; + goto CleanUp; + } + + channel = bi->ctl_ch ? bi->ctl_ch : + CHSPEC_CHANNEL(dtohchanspec(bi->chanspec)); + if (channel <= CH_MAX_2G_CHANNEL) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) + freq = ieee80211_channel_to_frequency(channel); + (void)band->band; +#else + freq = ieee80211_channel_to_frequency(channel, band->band); +#endif + notify_channel = ieee80211_get_channel(wiphy, freq); + + notify_capability = dtoh16(bi->capability); + notify_interval = dtoh16(bi->beacon_period); + notify_ie = (u8 *)bi + dtoh16(bi->ie_offset); + notify_ielen = dtoh32(bi->ie_length); + notify_signal = (int16)dtoh16(bi->RSSI) * 100; + + if (wl->p2p_supported) { + notify_capability |= DOT11_CAP_IBSS; + if (wsec) + notify_capability |= DOT11_CAP_PRIVACY; + } + + WL_DBG(("BSSID %02x:%02x:%02x:%02x:%02x:%02x", + bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5])); + WL_INFO(("channel: %d(%d)\n", channel, freq)); + WL_INFO(("capability: %X\n", notify_capability)); + WL_INFO(("beacon interval: %d ms\n", notify_interval)); + WL_INFO(("signal: %d dBm\n", notify_signal)); + WL_INFO(("ie_len: %d\n", notify_ielen)); + bss = cfg80211_inform_bss(wiphy, notify_channel, bssid, 0, + notify_capability, notify_interval, + notify_ie, notify_ielen, notify_signal, GFP_KERNEL); + if (!bss) { + WL_ERR(("cfg80211_inform_bss() Failed\n")); + err = -ENOMEM; + goto CleanUp; + } + + cfg80211_put_bss(bss); + err = 0; + +CleanUp: + + mutex_unlock(&wl->usr_sync); + + WL_TRACE(("Exit\n")); + return err; +} + +static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e, struct net_device *ndev) +{ + u32 event = ntoh32(e->event_type); + u32 status = ntoh32(e->status); + u16 flags = ntoh16(e->flags); + + WL_DBG(("event %d, status %d flags %x\n", event, status, flags)); + if (event == WLC_E_SET_SSID) { + if (status == WLC_E_STATUS_SUCCESS) { + return true; + } + } else if (event == WLC_E_LINK) { + if (flags & WLC_EVENT_MSG_LINK) + if (!wl_is_ibssmode(wl, ndev)) + return true; + } + + WL_DBG(("wl_is_linkup false\n")); + return false; +} + +static bool wl_is_linkdown(struct wl_priv *wl, const wl_event_msg_t *e) +{ + u32 event = ntoh32(e->event_type); + u16 flags = ntoh16(e->flags); + + if (event == WLC_E_DEAUTH_IND || + event == WLC_E_DISASSOC_IND || + event == WLC_E_DISASSOC || + event == WLC_E_DEAUTH) { + return true; + } else if (event == WLC_E_LINK) { + if (!(flags & WLC_EVENT_MSG_LINK)) + return true; + } + + return false; +} + +static bool wl_is_nonetwork(struct wl_priv *wl, const wl_event_msg_t *e) +{ + u32 event = ntoh32(e->event_type); + u32 status = ntoh32(e->status); + + if (event == WLC_E_LINK && status == WLC_E_STATUS_NO_NETWORKS) + return true; + if (event == WLC_E_SET_SSID && status != WLC_E_STATUS_SUCCESS) + return true; + + return false; +} + +/* The mainline kernel >= 3.2.0 has support for indicating new/del station + * to AP/P2P GO via events. If this change is backported to kernel for which + * this driver is being built, then define WL_CFG80211_STA_EVENT. You + * should use this new/del sta event mechanism for BRCM supplicant >= 22. + */ +static s32 +wl_notify_connect_status_ap(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + s32 err = 0; + u32 event = ntoh32(e->event_type); + u32 reason = ntoh32(e->reason); + u32 len = ntoh32(e->datalen); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) && !defined(WL_CFG80211_STA_EVENT) + bool isfree = false; + u8 *mgmt_frame; + u8 bsscfgidx = e->bsscfgidx; + s32 freq; + s32 channel; + u8 body[WL_FRAME_LEN]; + u16 fc = 0; + struct ieee80211_supported_band *band; + struct ether_addr da; + struct ether_addr bssid; + struct wiphy *wiphy = wl_to_wiphy(wl); + channel_info_t ci; +#else + struct station_info sinfo; +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) && !WL_CFG80211_STA_EVENT */ + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) && !defined(WL_CFG80211_STA_EVENT) + memset(body, 0, sizeof(body)); + memset(&bssid, 0, ETHER_ADDR_LEN); + WL_DBG(("Enter event %d ndev %p\n", event, ndev)); + if (wl_get_mode_by_netdev(wl, ndev) == WL_INVALID) + return WL_INVALID; + + if (len > WL_FRAME_LEN) { + WL_ERR(("Received frame length %d from dongle is greater than" + " allocated body buffer len %d", len, WL_FRAME_LEN)); + goto exit; + } + memcpy(body, data, len); + wldev_iovar_getbuf_bsscfg(ndev, "cur_etheraddr", + NULL, 0, wl->ioctl_buf, WLC_IOCTL_MAXLEN, bsscfgidx, &wl->ioctl_buf_sync); + memcpy(da.octet, wl->ioctl_buf, ETHER_ADDR_LEN); + err = wldev_ioctl(ndev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); + switch (event) { + case WLC_E_ASSOC_IND: + fc = FC_ASSOC_REQ; + break; + case WLC_E_REASSOC_IND: + fc = FC_REASSOC_REQ; + break; + case WLC_E_DISASSOC_IND: + fc = FC_DISASSOC; + break; + case WLC_E_DEAUTH_IND: + fc = FC_DISASSOC; + break; + case WLC_E_DEAUTH: + fc = FC_DISASSOC; + break; + default: + fc = 0; + goto exit; + } + if ((err = wldev_ioctl(ndev, WLC_GET_CHANNEL, &ci, sizeof(ci), false))) + return err; + + channel = dtoh32(ci.hw_channel); + if (channel <= CH_MAX_2G_CHANNEL) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (!band) { + WL_ERR(("No valid band")); + return -EINVAL; + } +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) + freq = ieee80211_channel_to_frequency(channel); +#else + freq = ieee80211_channel_to_frequency(channel, band->band); +#endif + + err = wl_frame_get_mgmt(fc, &da, &e->addr, &bssid, + &mgmt_frame, &len, body); + if (err < 0) + goto exit; + isfree = true; + + if (event == WLC_E_ASSOC_IND && reason == DOT11_SC_SUCCESS) { + cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); + } else if (event == WLC_E_DISASSOC_IND) { + cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); + } else if ((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DEAUTH)) { + cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC); + } + +exit: + if (isfree) + kfree(mgmt_frame); + return err; +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) && !WL_CFG80211_STA_EVENT */ + sinfo.filled = 0; + if (((event == WLC_E_ASSOC_IND) || (event == WLC_E_REASSOC_IND)) && + reason == DOT11_SC_SUCCESS) { + sinfo.filled = STATION_INFO_ASSOC_REQ_IES; + if (!data) { + WL_ERR(("No IEs present in ASSOC/REASSOC_IND")); + return -EINVAL; + } + sinfo.assoc_req_ies = data; + sinfo.assoc_req_ies_len = len; + cfg80211_new_sta(ndev, e->addr.octet, &sinfo, GFP_ATOMIC); + } else if (event == WLC_E_DISASSOC_IND) { + cfg80211_del_sta(ndev, e->addr.octet, GFP_ATOMIC); + } else if ((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DEAUTH)) { + cfg80211_del_sta(ndev, e->addr.octet, GFP_ATOMIC); + } +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) && !WL_CFG80211_STA_EVENT */ + return err; +} + +static s32 +wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + bool act; + s32 err = 0; + u32 event = ntoh32(e->event_type); + u32 reason; + + if (wl_get_mode_by_netdev(wl, ndev) == WL_MODE_AP) { + wl_notify_connect_status_ap(wl, ndev, e, data); + } else { + WL_DBG(("wl_notify_connect_status : event %d status : %d ndev %p\n", + ntoh32(e->event_type), ntoh32(e->status), ndev)); + if((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DISASSOC_IND)) { + reason = ntoh32(e->reason); + wl->deauth_reason = reason; + WL_ERR(("Received %s event with reason code: %d\n", + (event == WLC_E_DEAUTH_IND)? + "WLC_E_DEAUTH_IND":"WLC_E_DISASSOC_IND", reason)); + } + if (wl_is_linkup(wl, e, ndev)) { + wl_link_up(wl); + act = true; + wl_update_prof(wl, ndev, e, &act, WL_PROF_ACT); + wl_update_prof(wl, ndev, NULL, (void *)&e->addr, WL_PROF_BSSID); + wl->deauth_reason = 0; + if (wl_is_ibssmode(wl, ndev)) { + wl_ibss_join_done(wl, ndev, e, data, true); + WL_DBG(("wl_ibss_join_done succeeded\n")); + } else { + if (!wl_get_drv_status(wl, DISCONNECTING, ndev)) { + printk("wl_bss_connect_done succeeded\n"); + wl_bss_connect_done(wl, ndev, e, data, true); + WL_DBG(("joined in BSS network \"%s\"\n", + ((struct wlc_ssid *) + wl_read_prof(wl, ndev, WL_PROF_SSID))->SSID)); + } + } + } else if (wl_is_linkdown(wl, e)) { + if (wl->scan_request) { + if (wl->escan_on) { + wl_notify_escan_complete(wl, ndev, true, true); + } else { + del_timer_sync(&wl->scan_timeout); + wl_iscan_aborted(wl); + } + } + if (wl_get_drv_status(wl, CONNECTED, ndev)) { + scb_val_t scbval; + u8 *curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); + wl_clr_drv_status(wl, CONNECTED, ndev); + if (! wl_get_drv_status(wl, DISCONNECTING, ndev)) { + /* To make sure disconnect, explictly send dissassoc + * for BSSID 00:00:00:00:00:00 issue + */ + scbval.val = WLAN_REASON_DEAUTH_LEAVING; + + memcpy(&scbval.ea, curbssid, ETHER_ADDR_LEN); + scbval.val = htod32(scbval.val); + wldev_ioctl(ndev, WLC_DISASSOC, &scbval, + sizeof(scb_val_t), true); + WL_ERR(("link down, calling cfg80211_disconnected" + " with deauth_reason:%d\n", wl->deauth_reason)); + if (!wl_is_ibssmode(wl, ndev)) + cfg80211_disconnected(ndev, wl->deauth_reason, + NULL, 0, GFP_KERNEL); + wl_link_down(wl); + wl_init_prof(wl, ndev); + } + } + else if (wl_get_drv_status(wl, CONNECTING, ndev)) { + printk("link down, during connecting\n"); + if (wl_is_ibssmode(wl, ndev)) + wl_ibss_join_done(wl, ndev, e, data, false); + else + wl_bss_connect_done(wl, ndev, e, data, false); + } + wl_clr_drv_status(wl, DISCONNECTING, ndev); + + } else if (wl_is_nonetwork(wl, e)) { + printk("connect failed event=%d e->status 0x%x\n", + event, (int)ntoh32(e->status)); + /* Clean up any pending scan request */ + if (wl->scan_request) { + if (wl->escan_on) { + wl_notify_escan_complete(wl, ndev, true, true); + } else { + del_timer_sync(&wl->scan_timeout); + wl_iscan_aborted(wl); + } + } + if (wl_get_drv_status(wl, CONNECTING, ndev)) + wl_bss_connect_done(wl, ndev, e, data, false); + } else { + printk("%s nothing\n", __FUNCTION__); + } + } + return err; +} + +static s32 +wl_notify_roaming_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + bool act; + s32 err = 0; + u32 event = be32_to_cpu(e->event_type); + u32 status = be32_to_cpu(e->status); + WL_DBG(("Enter \n")); + if (event == WLC_E_ROAM && status == WLC_E_STATUS_SUCCESS) { + if (wl_get_drv_status(wl, CONNECTED, ndev)) + wl_bss_roaming_done(wl, ndev, e, data); + else + wl_bss_connect_done(wl, ndev, e, data, true); + act = true; + wl_update_prof(wl, ndev, e, &act, WL_PROF_ACT); + wl_update_prof(wl, ndev, NULL, (void *)&e->addr, WL_PROF_BSSID); + } + return err; +} + +static s32 wl_get_assoc_ies(struct wl_priv *wl, struct net_device *ndev) +{ + wl_assoc_info_t assoc_info; + struct wl_connect_info *conn_info = wl_to_conn(wl); + s32 err = 0; + + WL_DBG(("Enter \n")); + err = wldev_iovar_getbuf(ndev, "assoc_info", NULL, 0, wl->extra_buf, + WL_ASSOC_INFO_MAX, NULL); + if (unlikely(err)) { + WL_ERR(("could not get assoc info (%d)\n", err)); + return err; + } + memcpy(&assoc_info, wl->extra_buf, sizeof(wl_assoc_info_t)); + assoc_info.req_len = htod32(assoc_info.req_len); + assoc_info.resp_len = htod32(assoc_info.resp_len); + assoc_info.flags = htod32(assoc_info.flags); + if (conn_info->req_ie_len) { + conn_info->req_ie_len = 0; + bzero(conn_info->req_ie, sizeof(conn_info->req_ie)); + } + if (conn_info->resp_ie_len) { + conn_info->resp_ie_len = 0; + bzero(conn_info->resp_ie, sizeof(conn_info->resp_ie)); + } + if (assoc_info.req_len) { + err = wldev_iovar_getbuf(ndev, "assoc_req_ies", NULL, 0, wl->extra_buf, + WL_ASSOC_INFO_MAX, NULL); + if (unlikely(err)) { + WL_ERR(("could not get assoc req (%d)\n", err)); + return err; + } + conn_info->req_ie_len = assoc_info.req_len - sizeof(struct dot11_assoc_req); + if (assoc_info.flags & WLC_ASSOC_REQ_IS_REASSOC) { + conn_info->req_ie_len -= ETHER_ADDR_LEN; + } + if (conn_info->req_ie_len <= MAX_REQ_LINE) + memcpy(conn_info->req_ie, wl->extra_buf, conn_info->req_ie_len); + else { + WL_ERR(("%s IE size %d above max %d size \n", + __FUNCTION__, conn_info->req_ie_len, MAX_REQ_LINE)); + return err; + } + } else { + conn_info->req_ie_len = 0; + } + if (assoc_info.resp_len) { + err = wldev_iovar_getbuf(ndev, "assoc_resp_ies", NULL, 0, wl->extra_buf, + WL_ASSOC_INFO_MAX, NULL); + if (unlikely(err)) { + WL_ERR(("could not get assoc resp (%d)\n", err)); + return err; + } + conn_info->resp_ie_len = assoc_info.resp_len -sizeof(struct dot11_assoc_resp); + if (conn_info->resp_ie_len <= MAX_REQ_LINE) + memcpy(conn_info->resp_ie, wl->extra_buf, conn_info->resp_ie_len); + else { + WL_ERR(("%s IE size %d above max %d size \n", + __FUNCTION__, conn_info->resp_ie_len, MAX_REQ_LINE)); + return err; + } + } else { + conn_info->resp_ie_len = 0; + } + WL_DBG(("req len (%d) resp len (%d)\n", conn_info->req_ie_len, + conn_info->resp_ie_len)); + + return err; +} + +static void wl_ch_to_chanspec(int ch, struct wl_join_params *join_params, + size_t *join_params_size) +{ + chanspec_t chanspec = 0; + + if (ch != 0) { + join_params->params.chanspec_num = 1; + join_params->params.chanspec_list[0] = ch; + + if (join_params->params.chanspec_list[0] <= CH_MAX_2G_CHANNEL) + chanspec |= WL_CHANSPEC_BAND_2G; + else + chanspec |= WL_CHANSPEC_BAND_5G; + + chanspec |= WL_CHANSPEC_BW_20; + chanspec |= WL_CHANSPEC_CTL_SB_NONE; + + *join_params_size += WL_ASSOC_PARAMS_FIXED_SIZE + + join_params->params.chanspec_num * sizeof(chanspec_t); + + join_params->params.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK; + join_params->params.chanspec_list[0] |= chanspec; + join_params->params.chanspec_list[0] = + htodchanspec(join_params->params.chanspec_list[0]); + + join_params->params.chanspec_num = + htod32(join_params->params.chanspec_num); + + WL_DBG(("%s join_params->params.chanspec_list[0]= %X\n", + __FUNCTION__, join_params->params.chanspec_list[0])); + + } +} + +static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev) +{ + struct cfg80211_bss *bss; + struct wl_bss_info *bi; + struct wlc_ssid *ssid; + struct bcm_tlv *tim; + s32 beacon_interval; + s32 dtim_period; + size_t ie_len; + u8 *ie; + u8 *curbssid; + s32 err = 0; + struct wiphy *wiphy; + + wiphy = wl_to_wiphy(wl); + + if (wl_is_ibssmode(wl, ndev)) + return err; + + ssid = (struct wlc_ssid *)wl_read_prof(wl, ndev, WL_PROF_SSID); + curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); + bss = cfg80211_get_bss(wiphy, NULL, curbssid, + ssid->SSID, ssid->SSID_len, WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); + + mutex_lock(&wl->usr_sync); + if (!bss) { + WL_DBG(("Could not find the AP\n")); + *(u32 *) wl->extra_buf = htod32(WL_EXTRA_BUF_MAX); + err = wldev_ioctl(ndev, WLC_GET_BSS_INFO, + wl->extra_buf, WL_EXTRA_BUF_MAX, false); + if (unlikely(err)) { + WL_ERR(("Could not get bss info %d\n", err)); + goto update_bss_info_out; + } + bi = (struct wl_bss_info *)(wl->extra_buf + 4); + if (memcmp(bi->BSSID.octet, curbssid, ETHER_ADDR_LEN)) { + err = -EIO; + goto update_bss_info_out; + } + err = wl_inform_single_bss(wl, bi); + if (unlikely(err)) + goto update_bss_info_out; + + ie = ((u8 *)bi) + bi->ie_offset; + ie_len = bi->ie_length; + beacon_interval = cpu_to_le16(bi->beacon_period); + } else { + WL_DBG(("Found the AP in the list - BSSID %pM\n", bss->bssid)); + ie = bss->information_elements; + ie_len = bss->len_information_elements; + beacon_interval = bss->beacon_interval; + cfg80211_put_bss(bss); + } + + tim = bcm_parse_tlvs(ie, ie_len, WLAN_EID_TIM); + if (tim) { + dtim_period = tim->data[1]; + } else { + /* + * active scan was done so we could not get dtim + * information out of probe response. + * so we speficially query dtim information. + */ + err = wldev_ioctl(ndev, WLC_GET_DTIMPRD, + &dtim_period, sizeof(dtim_period), false); + if (unlikely(err)) { + WL_ERR(("WLC_GET_DTIMPRD error (%d)\n", err)); + goto update_bss_info_out; + } + } + + wl_update_prof(wl, ndev, NULL, &beacon_interval, WL_PROF_BEACONINT); + wl_update_prof(wl, ndev, NULL, &dtim_period, WL_PROF_DTIMPERIOD); + +update_bss_info_out: + mutex_unlock(&wl->usr_sync); + return err; +} + +static s32 +wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + struct wl_connect_info *conn_info = wl_to_conn(wl); + s32 err = 0; + u8 *curbssid; + + wl_get_assoc_ies(wl, ndev); + wl_update_prof(wl, ndev, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); + curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); + wl_update_bss_info(wl, ndev); + wl_update_pmklist(ndev, wl->pmk_list, err); + cfg80211_roamed(ndev, +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) + NULL, +#endif + curbssid, + conn_info->req_ie, conn_info->req_ie_len, + conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL); + WL_DBG(("Report roaming result\n")); + + wl_set_drv_status(wl, CONNECTED, ndev); + + return err; +} + +static s32 +wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, bool completed) +{ + struct wl_connect_info *conn_info = wl_to_conn(wl); + s32 err = 0; + u8 *curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); + + WL_DBG((" enter\n")); + if (wl->scan_request) { + wl_notify_escan_complete(wl, ndev, true, true); + } + if (wl_get_drv_status(wl, CONNECTING, ndev)) { + wl_clr_drv_status(wl, CONNECTING, ndev); + if (completed) { + wl_get_assoc_ies(wl, ndev); + wl_update_prof(wl, ndev, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); + curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); + wl_update_bss_info(wl, ndev); + wl_update_pmklist(ndev, wl->pmk_list, err); + wl_set_drv_status(wl, CONNECTED, ndev); + } + cfg80211_connect_result(ndev, + curbssid, + conn_info->req_ie, + conn_info->req_ie_len, + conn_info->resp_ie, + conn_info->resp_ie_len, + completed ? WLAN_STATUS_SUCCESS : WLAN_STATUS_AUTH_TIMEOUT, + GFP_KERNEL); + if (completed) + WL_INFO(("Report connect result - connection succeeded\n")); + else + WL_ERR(("Report connect result - connection failed\n")); + } + return err; +} + +static s32 +wl_ibss_join_done(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, bool completed) +{ + s32 err = 0; + + WL_TRACE(("Enter\n")); + + if (wl->scan_request) { + wl_notify_escan_complete(wl, ndev, true, true); + } + if (wl_get_drv_status(wl, CONNECTING, ndev)) { + wl_clr_drv_status(wl, CONNECTING, ndev); + if (completed) { + err = wl_inform_ibss(wl, (u8 *)&e->addr); + if (err) { + WL_ERR(("wl_inform_ibss() failed: %d\n", err)); + } + wl_set_drv_status(wl, CONNECTED, ndev); + + cfg80211_ibss_joined(ndev, (u8 *)&e->addr, GFP_KERNEL); + WL_DBG(("cfg80211_ibss_joined() called with valid BSSID\n")); + } + } + + WL_TRACE(("Exit\n")); + return err; +} + +static s32 +wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + u16 flags = ntoh16(e->flags); + enum nl80211_key_type key_type; + + mutex_lock(&wl->usr_sync); + if (flags & WLC_EVENT_MSG_GROUP) + key_type = NL80211_KEYTYPE_GROUP; + else + key_type = NL80211_KEYTYPE_PAIRWISE; + + cfg80211_michael_mic_failure(ndev, (u8 *)&e->addr, key_type, -1, + NULL, GFP_KERNEL); + mutex_unlock(&wl->usr_sync); + + return 0; +} + +#ifdef PNO_SUPPORT +static s32 +wl_notify_pfn_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + WL_ERR((" PNO Event\n")); + + mutex_lock(&wl->usr_sync); +#ifndef WL_SCHED_SCAN + /* TODO: Use cfg80211_sched_scan_results(wiphy); */ + cfg80211_disconnected(ndev, 0, NULL, 0, GFP_KERNEL); +#else + /* If cfg80211 scheduled scan is supported, report the pno results via sched + * scan results + */ + wl_notify_sched_scan_results(wl, ndev, e, data); +#endif /* WL_SCHED_SCAN */ + mutex_unlock(&wl->usr_sync); + return 0; +} +#endif /* PNO_SUPPORT */ + +static s32 +wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + struct channel_info channel_inform; + struct wl_scan_results *bss_list; + u32 len = WL_SCAN_BUF_MAX; + s32 err = 0; + unsigned long flags; + + WL_DBG(("Enter \n")); + if (!wl_get_drv_status(wl, SCANNING, ndev)) { + WL_ERR(("scan is not ready \n")); + return err; + } + if (wl->iscan_on && wl->iscan_kickstart) + return wl_wakeup_iscan(wl_to_iscan(wl)); + + mutex_lock(&wl->usr_sync); + wl_clr_drv_status(wl, SCANNING, ndev); + err = wldev_ioctl(ndev, WLC_GET_CHANNEL, &channel_inform, + sizeof(channel_inform), false); + if (unlikely(err)) { + WL_ERR(("scan busy (%d)\n", err)); + goto scan_done_out; + } + channel_inform.scan_channel = dtoh32(channel_inform.scan_channel); + if (unlikely(channel_inform.scan_channel)) { + + WL_DBG(("channel_inform.scan_channel (%d)\n", + channel_inform.scan_channel)); + } + wl->bss_list = wl->scan_results; + bss_list = wl->bss_list; + memset(bss_list, 0, len); + bss_list->buflen = htod32(len); + err = wldev_ioctl(ndev, WLC_SCAN_RESULTS, bss_list, len, false); + if (unlikely(err)) { + WL_ERR(("%s Scan_results error (%d)\n", ndev->name, err)); + err = -EINVAL; + goto scan_done_out; + } + bss_list->buflen = dtoh32(bss_list->buflen); + bss_list->version = dtoh32(bss_list->version); + bss_list->count = dtoh32(bss_list->count); + + err = wl_inform_bss(wl); + +scan_done_out: + del_timer_sync(&wl->scan_timeout); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + if (wl->scan_request) { + WL_DBG(("cfg80211_scan_done\n")); + cfg80211_scan_done(wl->scan_request, false); + wl->scan_request = NULL; + } + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + mutex_unlock(&wl->usr_sync); + return err; +} +static s32 +wl_frame_get_mgmt(u16 fc, const struct ether_addr *da, + const struct ether_addr *sa, const struct ether_addr *bssid, + u8 **pheader, u32 *body_len, u8 *pbody) +{ + struct dot11_management_header *hdr; + u32 totlen = 0; + s32 err = 0; + u8 *offset; + u32 prebody_len = *body_len; + switch (fc) { + case FC_ASSOC_REQ: + /* capability , listen interval */ + totlen = DOT11_ASSOC_REQ_FIXED_LEN; + *body_len += DOT11_ASSOC_REQ_FIXED_LEN; + break; + + case FC_REASSOC_REQ: + /* capability, listen inteval, ap address */ + totlen = DOT11_REASSOC_REQ_FIXED_LEN; + *body_len += DOT11_REASSOC_REQ_FIXED_LEN; + break; + } + totlen += DOT11_MGMT_HDR_LEN + prebody_len; + *pheader = kzalloc(totlen, GFP_KERNEL); + if (*pheader == NULL) { + WL_ERR(("memory alloc failed \n")); + return -ENOMEM; + } + hdr = (struct dot11_management_header *) (*pheader); + hdr->fc = htol16(fc); + hdr->durid = 0; + hdr->seq = 0; + offset = (u8*)(hdr + 1) + (totlen - DOT11_MGMT_HDR_LEN - prebody_len); + bcopy((const char*)da, (u8*)&hdr->da, ETHER_ADDR_LEN); + bcopy((const char*)sa, (u8*)&hdr->sa, ETHER_ADDR_LEN); + bcopy((const char*)bssid, (u8*)&hdr->bssid, ETHER_ADDR_LEN); + bcopy((const char*)pbody, offset, prebody_len); + *body_len = totlen; + return err; +} +static s32 +wl_notify_rx_mgmt_frame(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + struct ieee80211_supported_band *band; + struct wiphy *wiphy = wl_to_wiphy(wl); + struct ether_addr da; + struct ether_addr bssid; + bool isfree = false; + s32 err = 0; + s32 freq; + struct net_device *dev = NULL; + wifi_p2p_pub_act_frame_t *act_frm = NULL; + wifi_p2p_action_frame_t *p2p_act_frm = NULL; + wifi_p2psd_gas_pub_act_frame_t *sd_act_frm = NULL; + wl_event_rx_frame_data_t *rxframe = + (wl_event_rx_frame_data_t*)data; + u32 event = ntoh32(e->event_type); + u8 *mgmt_frame; + u8 bsscfgidx = e->bsscfgidx; + u32 mgmt_frame_len = ntoh32(e->datalen) - sizeof(wl_event_rx_frame_data_t); + u16 channel = ((ntoh16(rxframe->channel) & WL_CHANSPEC_CHAN_MASK)); + + memset(&bssid, 0, ETHER_ADDR_LEN); + + if (wl->p2p_net == ndev) { + dev = wl_to_prmry_ndev(wl); + } else { + dev = ndev; + } + + if (channel <= CH_MAX_2G_CHANNEL) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (!band) { + WL_ERR(("No valid band")); + return -EINVAL; + } + + if ((event == WLC_E_P2P_PROBREQ_MSG) && + wl->p2p && wl_get_p2p_status(wl, GO_NEG_PHASE)) { + WL_DBG(("Filtering P2P probe_req while being in GO-Neg state\n")); + goto exit; + } + +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) + freq = ieee80211_channel_to_frequency(channel); +#else + freq = ieee80211_channel_to_frequency(channel, band->band); +#endif + if (event == WLC_E_ACTION_FRAME_RX) { + wldev_iovar_getbuf_bsscfg(dev, "cur_etheraddr", + NULL, 0, wl->ioctl_buf, WLC_IOCTL_MAXLEN, bsscfgidx, &wl->ioctl_buf_sync); + + wldev_ioctl(dev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); + memcpy(da.octet, wl->ioctl_buf, ETHER_ADDR_LEN); + err = wl_frame_get_mgmt(FC_ACTION, &da, &e->addr, &bssid, + &mgmt_frame, &mgmt_frame_len, + (u8 *)((wl_event_rx_frame_data_t *)rxframe + 1)); + if (err < 0) { + WL_ERR(("%s: Error in receiving action frame len %d channel %d freq %d\n", + __func__, mgmt_frame_len, channel, freq)); + goto exit; + } + isfree = true; + if (wl_cfgp2p_is_pub_action(&mgmt_frame[DOT11_MGMT_HDR_LEN], + mgmt_frame_len - DOT11_MGMT_HDR_LEN)) { + act_frm = (wifi_p2p_pub_act_frame_t *) + (&mgmt_frame[DOT11_MGMT_HDR_LEN]); + } else if (wl_cfgp2p_is_p2p_action(&mgmt_frame[DOT11_MGMT_HDR_LEN], + mgmt_frame_len - DOT11_MGMT_HDR_LEN)) { + p2p_act_frm = (wifi_p2p_action_frame_t *) + (&mgmt_frame[DOT11_MGMT_HDR_LEN]); + (void) p2p_act_frm; + } else if (wl_cfgp2p_is_gas_action(&mgmt_frame[DOT11_MGMT_HDR_LEN], + mgmt_frame_len - DOT11_MGMT_HDR_LEN)) { + sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *) + (&mgmt_frame[DOT11_MGMT_HDR_LEN]); + (void) sd_act_frm; + } + wl_cfgp2p_print_actframe(false, &mgmt_frame[DOT11_MGMT_HDR_LEN], + mgmt_frame_len - DOT11_MGMT_HDR_LEN); + /* + * After complete GO Negotiation, roll back to mpc mode + */ + if (act_frm && ((act_frm->subtype == P2P_PAF_GON_CONF) || + (act_frm->subtype == P2P_PAF_PROVDIS_RSP))) { + wldev_iovar_setint(dev, "mpc", 1); + } + + if (act_frm && (act_frm->subtype == P2P_PAF_GON_CONF)) { + WL_DBG(("P2P: GO_NEG_PHASE status cleared \n")); + wl_clr_p2p_status(wl, GO_NEG_PHASE); + } + } else { + mgmt_frame = (u8 *)((wl_event_rx_frame_data_t *)rxframe + 1); + } + + cfg80211_rx_mgmt(ndev, freq, mgmt_frame, mgmt_frame_len, GFP_ATOMIC); + + WL_DBG(("%s: mgmt_frame_len (%d) , e->datalen (%d), channel (%d), freq (%d)\n", __func__, + mgmt_frame_len, ntoh32(e->datalen), channel, freq)); + + if (isfree) + kfree(mgmt_frame); +exit: + return 0; +} + +#ifdef WL_SCHED_SCAN +/* If target scan is not reliable, set the below define to "1" to do a + * full escan + */ +#define FULL_ESCAN_ON_PFN_NET_FOUND 0 +static s32 +wl_notify_sched_scan_results(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + wl_pfn_net_info_t *netinfo, *pnetinfo; + struct cfg80211_scan_request request; + struct wiphy *wiphy = wl_to_wiphy(wl); + int err = 0; + struct cfg80211_ssid ssid[MAX_PFN_LIST_COUNT]; + struct ieee80211_channel *channel = NULL; + int channel_req = 0; + int band = 0; + struct wl_pfn_scanresults *pfn_result = (struct wl_pfn_scanresults *)data; + + WL_DBG(("Enter\n")); + + if (e->event_type == WLC_E_PFN_NET_LOST) { + WL_DBG(("PFN NET LOST event. Do Nothing \n")); + return 0; + } + WL_DBG(("PFN NET FOUND event. count:%d \n", pfn_result->count)); + if (pfn_result->count > 0) { + int i; + + memset(&request, 0x00, sizeof(struct cfg80211_scan_request)); + memset(&ssid, 0x00, sizeof(ssid)); + request.wiphy = wiphy; + + pnetinfo = (wl_pfn_net_info_t *)(data + sizeof(wl_pfn_scanresults_t) + - sizeof(wl_pfn_net_info_t)); + channel = (struct ieee80211_channel *)kzalloc( + (sizeof(struct ieee80211_channel) * MAX_PFN_LIST_COUNT), + GFP_KERNEL); + if (!channel) { + WL_ERR(("No memory")); + err = -ENOMEM; + goto out_err; + } + + for (i = 0; i < pfn_result->count; i++) { + netinfo = &pnetinfo[i]; + if (!netinfo) { + WL_ERR(("Invalid netinfo ptr. index:%d", i)); + err = -EINVAL; + goto out_err; + } + WL_DBG(("SSID:%s Channel:%d \n", + netinfo->pfnsubnet.SSID, netinfo->pfnsubnet.channel)); + /* PFN result doesn't have all the info which are required by the supplicant + * (For e.g IEs) Do a target Escan so that sched scan results are reported + * via wl_inform_single_bss in the required format. Escan does require the + * scan request in the form of cfg80211_scan_request. For timebeing, create + * cfg80211_scan_request one out of the received PNO event. + */ + memcpy(ssid[i].ssid, netinfo->pfnsubnet.SSID, + netinfo->pfnsubnet.SSID_len); + ssid[i].ssid_len = netinfo->pfnsubnet.SSID_len; + request.n_ssids++; + + channel_req = netinfo->pfnsubnet.channel; + band = (channel_req <= CH_MAX_2G_CHANNEL) ? NL80211_BAND_2GHZ + : NL80211_BAND_5GHZ; + channel[i].center_freq = ieee80211_channel_to_frequency(channel_req, band); + channel[i].band = band; + channel[i].flags |= IEEE80211_CHAN_NO_HT40; + request.channels[i] = &channel[i]; + request.n_channels++; + } + + /* assign parsed ssid array */ + if (request.n_ssids) + request.ssids = &ssid[0]; + + if (wl_get_drv_status_all(wl, SCANNING)) { + /* Abort any on-going scan */ + wl_notify_escan_complete(wl, ndev, true, true); + } + + if (wl_get_p2p_status(wl, DISCOVERY_ON)) { + err = wl_cfgp2p_discover_enable_search(wl, false); + if (unlikely(err)) { + wl_clr_drv_status(wl, SCANNING, ndev); + goto out_err; + } + } + + wl_set_drv_status(wl, SCANNING, ndev); +#if FULL_ESCAN_ON_PFN_NET_FOUND + err = wl_do_escan(wl, wiphy, ndev, NULL); +#else + err = wl_do_escan(wl, wiphy, ndev, &request); +#endif + if (err) { + wl_clr_drv_status(wl, SCANNING, ndev); + goto out_err; + } + wl->sched_scan_running = TRUE; + } + else { + WL_ERR(("FALSE PNO Event. (pfn_count == 0) \n")); + } +out_err: + if (channel) + kfree(channel); + return err; +} +#endif /* WL_SCHED_SCAN */ + +static void wl_init_conf(struct wl_conf *conf) +{ + WL_DBG(("Enter \n")); + conf->frag_threshold = (u32)-1; + conf->rts_threshold = (u32)-1; + conf->retry_short = (u32)-1; + conf->retry_long = (u32)-1; + conf->tx_power = -1; +} + +static void wl_init_prof(struct wl_priv *wl, struct net_device *ndev) +{ + unsigned long flags; + struct wl_profile *profile = wl_get_profile_by_netdev(wl, ndev); + + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + memset(profile, 0, sizeof(struct wl_profile)); + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); +} + +static void wl_init_event_handler(struct wl_priv *wl) +{ + memset(wl->evt_handler, 0, sizeof(wl->evt_handler)); + + wl->evt_handler[WLC_E_SCAN_COMPLETE] = wl_notify_scan_status; + wl->evt_handler[WLC_E_LINK] = wl_notify_connect_status; + wl->evt_handler[WLC_E_DEAUTH_IND] = wl_notify_connect_status; + wl->evt_handler[WLC_E_DEAUTH] = wl_notify_connect_status; + wl->evt_handler[WLC_E_DISASSOC_IND] = wl_notify_connect_status; + wl->evt_handler[WLC_E_ASSOC_IND] = wl_notify_connect_status; + wl->evt_handler[WLC_E_REASSOC_IND] = wl_notify_connect_status; + wl->evt_handler[WLC_E_ROAM] = wl_notify_roaming_status; + wl->evt_handler[WLC_E_MIC_ERROR] = wl_notify_mic_status; + wl->evt_handler[WLC_E_SET_SSID] = wl_notify_connect_status; + wl->evt_handler[WLC_E_ACTION_FRAME_RX] = wl_notify_rx_mgmt_frame; + wl->evt_handler[WLC_E_PROBREQ_MSG] = wl_notify_rx_mgmt_frame; + wl->evt_handler[WLC_E_P2P_PROBREQ_MSG] = wl_notify_rx_mgmt_frame; + wl->evt_handler[WLC_E_P2P_DISC_LISTEN_COMPLETE] = wl_cfgp2p_listen_complete; + wl->evt_handler[WLC_E_ACTION_FRAME_COMPLETE] = wl_cfgp2p_action_tx_complete; + wl->evt_handler[WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE] = wl_cfgp2p_action_tx_complete; +#ifdef PNO_SUPPORT + wl->evt_handler[WLC_E_PFN_NET_FOUND] = wl_notify_pfn_status; +#endif /* PNO_SUPPORT */ +} + +static s32 wl_init_priv_mem(struct wl_priv *wl) +{ + WL_DBG(("Enter \n")); + wl->scan_results = (void *)kzalloc(WL_SCAN_BUF_MAX, GFP_KERNEL); + if (unlikely(!wl->scan_results)) { + WL_ERR(("Scan results alloc failed\n")); + goto init_priv_mem_out; + } + wl->conf = (void *)kzalloc(sizeof(*wl->conf), GFP_KERNEL); + if (unlikely(!wl->conf)) { + WL_ERR(("wl_conf alloc failed\n")); + goto init_priv_mem_out; + } + wl->scan_req_int = + (void *)kzalloc(sizeof(*wl->scan_req_int), GFP_KERNEL); + if (unlikely(!wl->scan_req_int)) { + WL_ERR(("Scan req alloc failed\n")); + goto init_priv_mem_out; + } + wl->ioctl_buf = (void *)kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL); + if (unlikely(!wl->ioctl_buf)) { + WL_ERR(("Ioctl buf alloc failed\n")); + goto init_priv_mem_out; + } + wl->escan_ioctl_buf = (void *)kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL); + if (unlikely(!wl->escan_ioctl_buf)) { + WL_ERR(("Ioctl buf alloc failed\n")); + goto init_priv_mem_out; + } + wl->extra_buf = (void *)kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); + if (unlikely(!wl->extra_buf)) { + WL_ERR(("Extra buf alloc failed\n")); + goto init_priv_mem_out; + } + wl->iscan = (void *)kzalloc(sizeof(*wl->iscan), GFP_KERNEL); + if (unlikely(!wl->iscan)) { + WL_ERR(("Iscan buf alloc failed\n")); + goto init_priv_mem_out; + } + wl->pmk_list = (void *)kzalloc(sizeof(*wl->pmk_list), GFP_KERNEL); + if (unlikely(!wl->pmk_list)) { + WL_ERR(("pmk list alloc failed\n")); + goto init_priv_mem_out; + } + wl->sta_info = (void *)kzalloc(sizeof(*wl->sta_info), GFP_KERNEL); + if (unlikely(!wl->sta_info)) { + WL_ERR(("sta info alloc failed\n")); + goto init_priv_mem_out; + } + wl->afx_hdl = (void *)kzalloc(sizeof(*wl->afx_hdl), GFP_KERNEL); + if (unlikely(!wl->afx_hdl)) { + WL_ERR(("afx hdl alloc failed\n")); + goto init_priv_mem_out; + } else { + init_completion(&wl->act_frm_scan); + INIT_WORK(&wl->afx_hdl->work, wl_cfg80211_afx_handler); + } + return 0; + +init_priv_mem_out: + wl_deinit_priv_mem(wl); + + return -ENOMEM; +} + +static void wl_deinit_priv_mem(struct wl_priv *wl) +{ + kfree(wl->scan_results); + wl->scan_results = NULL; + kfree(wl->conf); + wl->conf = NULL; + kfree(wl->scan_req_int); + wl->scan_req_int = NULL; + kfree(wl->ioctl_buf); + wl->ioctl_buf = NULL; + kfree(wl->escan_ioctl_buf); + wl->escan_ioctl_buf = NULL; + kfree(wl->extra_buf); + wl->extra_buf = NULL; + kfree(wl->iscan); + wl->iscan = NULL; + kfree(wl->pmk_list); + wl->pmk_list = NULL; + kfree(wl->sta_info); + wl->sta_info = NULL; + if (wl->afx_hdl) { + cancel_work_sync(&wl->afx_hdl->work); + kfree(wl->afx_hdl); + wl->afx_hdl = NULL; + } + + if (wl->ap_info) { + kfree(wl->ap_info->wpa_ie); + kfree(wl->ap_info->rsn_ie); + kfree(wl->ap_info->wps_ie); + kfree(wl->ap_info); + wl->ap_info = NULL; + } +} + +static s32 wl_create_event_handler(struct wl_priv *wl) +{ + int ret = 0; + WL_DBG(("Enter \n")); + + /* Do not use DHD in cfg driver */ + wl->event_tsk.thr_pid = -1; + PROC_START(wl_event_handler, wl, &wl->event_tsk, 0); + if (wl->event_tsk.thr_pid < 0) + ret = -ENOMEM; + return ret; +} + +static void wl_destroy_event_handler(struct wl_priv *wl) +{ + if (wl->event_tsk.thr_pid >= 0) + PROC_STOP(&wl->event_tsk); +} + +static void wl_term_iscan(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); + WL_TRACE(("In\n")); + if (wl->iscan_on && iscan->tsk) { + iscan->state = WL_ISCAN_STATE_IDLE; + WL_INFO(("SIGTERM\n")); + send_sig(SIGTERM, iscan->tsk, 1); + WL_DBG(("kthread_stop\n")); + kthread_stop(iscan->tsk); + iscan->tsk = NULL; + } +} + +static void wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted) +{ + struct wl_priv *wl = iscan_to_wl(iscan); + struct net_device *ndev = wl_to_prmry_ndev(wl); + unsigned long flags; + + WL_DBG(("Enter \n")); + if (!wl_get_drv_status(wl, SCANNING, ndev)) { + wl_clr_drv_status(wl, SCANNING, ndev); + WL_ERR(("Scan complete while device not scanning\n")); + return; + } + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + wl_clr_drv_status(wl, SCANNING, ndev); + if (likely(wl->scan_request)) { + cfg80211_scan_done(wl->scan_request, aborted); + wl->scan_request = NULL; + } + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + wl->iscan_kickstart = false; +} + +static s32 wl_wakeup_iscan(struct wl_iscan_ctrl *iscan) +{ + if (likely(iscan->state != WL_ISCAN_STATE_IDLE)) { + WL_DBG(("wake up iscan\n")); + up(&iscan->sync); + return 0; + } + + return -EIO; +} + +static s32 +wl_get_iscan_results(struct wl_iscan_ctrl *iscan, u32 *status, + struct wl_scan_results **bss_list) +{ + struct wl_iscan_results list; + struct wl_scan_results *results; + struct wl_iscan_results *list_buf; + s32 err = 0; + + WL_DBG(("Enter \n")); + memset(iscan->scan_buf, 0, WL_ISCAN_BUF_MAX); + list_buf = (struct wl_iscan_results *)iscan->scan_buf; + results = &list_buf->results; + results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE; + results->version = 0; + results->count = 0; + + memset(&list, 0, sizeof(list)); + list.results.buflen = htod32(WL_ISCAN_BUF_MAX); + err = wldev_iovar_getbuf(iscan->dev, "iscanresults", &list, + WL_ISCAN_RESULTS_FIXED_SIZE, iscan->scan_buf, + WL_ISCAN_BUF_MAX, NULL); + if (unlikely(err)) { + WL_ERR(("error (%d)\n", err)); + return err; + } + results->buflen = dtoh32(results->buflen); + results->version = dtoh32(results->version); + results->count = dtoh32(results->count); + WL_DBG(("results->count = %d\n", results->count)); + WL_DBG(("results->buflen = %d\n", results->buflen)); + *status = dtoh32(list_buf->status); + *bss_list = results; + + return err; +} + +static s32 wl_iscan_done(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl->iscan; + s32 err = 0; + + iscan->state = WL_ISCAN_STATE_IDLE; + mutex_lock(&wl->usr_sync); + wl_inform_bss(wl); + wl_notify_iscan_complete(iscan, false); + mutex_unlock(&wl->usr_sync); + + return err; +} + +static s32 wl_iscan_pending(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl->iscan; + s32 err = 0; + + /* Reschedule the timer */ + mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); + iscan->timer_on = 1; + + return err; +} + +static s32 wl_iscan_inprogress(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl->iscan; + s32 err = 0; + + mutex_lock(&wl->usr_sync); + wl_inform_bss(wl); + wl_run_iscan(iscan, NULL, WL_SCAN_ACTION_CONTINUE); + mutex_unlock(&wl->usr_sync); + /* Reschedule the timer */ + mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); + iscan->timer_on = 1; + + return err; +} + +static s32 wl_iscan_aborted(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl->iscan; + s32 err = 0; + + iscan->state = WL_ISCAN_STATE_IDLE; + mutex_lock(&wl->usr_sync); + wl_notify_iscan_complete(iscan, true); + mutex_unlock(&wl->usr_sync); + + return err; +} + +static s32 wl_iscan_thread(void *data) +{ + struct wl_iscan_ctrl *iscan = (struct wl_iscan_ctrl *)data; + struct wl_priv *wl = iscan_to_wl(iscan); + u32 status; + int err = 0; + + allow_signal(SIGTERM); + status = WL_SCAN_RESULTS_PARTIAL; + while (likely(!down_interruptible(&iscan->sync))) { + if (kthread_should_stop()) + break; + if (iscan->timer_on) { + del_timer_sync(&iscan->timer); + iscan->timer_on = 0; + } + mutex_lock(&wl->usr_sync); + err = wl_get_iscan_results(iscan, &status, &wl->bss_list); + if (unlikely(err)) { + status = WL_SCAN_RESULTS_ABORTED; + WL_ERR(("Abort iscan\n")); + } + mutex_unlock(&wl->usr_sync); + iscan->iscan_handler[status] (wl); + } + if (iscan->timer_on) { + del_timer_sync(&iscan->timer); + iscan->timer_on = 0; + } + WL_DBG(("%s was terminated\n", __func__)); + + return 0; +} + +static void wl_scan_timeout(unsigned long data) +{ + struct wl_priv *wl = (struct wl_priv *)data; + + if (wl->scan_request) { + WL_ERR(("timer expired\n")); + if (wl->escan_on) + wl_notify_escan_complete(wl, wl->escan_info.ndev, true, false); + else + wl_notify_iscan_complete(wl_to_iscan(wl), true); + } +} + +static void wl_iscan_timer(unsigned long data) +{ + struct wl_iscan_ctrl *iscan = (struct wl_iscan_ctrl *)data; + + if (iscan) { + iscan->timer_on = 0; + WL_DBG(("timer expired\n")); + wl_wakeup_iscan(iscan); + } +} + +static s32 wl_invoke_iscan(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); + int err = 0; + + if (wl->iscan_on && !iscan->tsk) { + iscan->state = WL_ISCAN_STATE_IDLE; + sema_init(&iscan->sync, 0); + iscan->tsk = kthread_run(wl_iscan_thread, iscan, "wl_iscan"); + if (IS_ERR(iscan->tsk)) { + WL_ERR(("Could not create iscan thread\n")); + iscan->tsk = NULL; + return -ENOMEM; + } + } + + return err; +} + +static void wl_init_iscan_handler(struct wl_iscan_ctrl *iscan) +{ + memset(iscan->iscan_handler, 0, sizeof(iscan->iscan_handler)); + iscan->iscan_handler[WL_SCAN_RESULTS_SUCCESS] = wl_iscan_done; + iscan->iscan_handler[WL_SCAN_RESULTS_PARTIAL] = wl_iscan_inprogress; + iscan->iscan_handler[WL_SCAN_RESULTS_PENDING] = wl_iscan_pending; + iscan->iscan_handler[WL_SCAN_RESULTS_ABORTED] = wl_iscan_aborted; + iscan->iscan_handler[WL_SCAN_RESULTS_NO_MEM] = wl_iscan_aborted; +} + +static s32 +wl_cfg80211_netdev_notifier_call(struct notifier_block * nb, + unsigned long state, + void *ndev) +{ + struct net_device *dev = ndev; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wl_priv *wl = wlcfg_drv_priv; + + WL_DBG(("Enter \n")); + if (!wdev || !wl || dev == wl_to_prmry_ndev(wl)) + return NOTIFY_DONE; + switch (state) { + case NETDEV_UNREGISTER: + /* after calling list_del_rcu(&wdev->list) */ + wl_dealloc_netinfo(wl, ndev); + break; + case NETDEV_GOING_DOWN: + /* At NETDEV_DOWN state, wdev_cleanup_work work will be called. + * In front of door, the function checks + * whether current scan is working or not. + * If the scanning is still working, wdev_cleanup_work call WARN_ON and + * make the scan done forcibly. + */ + if (wl_get_drv_status(wl, SCANNING, dev)) { + if (wl->escan_on) { + wl_notify_escan_complete(wl, dev, true, true); + } + } + break; + } + return NOTIFY_DONE; +} +static struct notifier_block wl_cfg80211_netdev_notifier = { + .notifier_call = wl_cfg80211_netdev_notifier_call, +}; + +static s32 wl_notify_escan_complete(struct wl_priv *wl, + struct net_device *ndev, + bool aborted, bool fw_abort) +{ + wl_scan_params_t *params = NULL; + s32 params_size = 0; + s32 err = BCME_OK; + unsigned long flags; + struct net_device *dev; + + WL_DBG(("Enter \n")); + + if (wl->scan_request) { + if (wl->scan_request->dev == wl->p2p_net) + dev = wl_to_prmry_ndev(wl); + else + dev = wl->scan_request->dev; + } + else { + WL_ERR(("wl->scan_request is NULL may be internal scan." + "doing scan_abort for ndev %p primary %p p2p_net %p", + ndev, wl_to_prmry_ndev(wl), wl->p2p_net)); + dev = ndev; + } + if (fw_abort && !in_atomic()) { + /* Our scan params only need space for 1 channel and 0 ssids */ + params = wl_cfg80211_scan_alloc_params(-1, 0, ¶ms_size); + if (params == NULL) { + WL_ERR(("scan params allocation failed \n")); + err = -ENOMEM; + } else { + /* Do a scan abort to stop the driver's scan engine */ + err = wldev_ioctl(dev, WLC_SCAN, params, params_size, true); + if (err < 0) { + WL_ERR(("scan abort failed \n")); + } + } + } + if (timer_pending(&wl->scan_timeout)) + del_timer_sync(&wl->scan_timeout); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + +#ifdef WL_SCHED_SCAN + if (wl->sched_scan_req && !wl->scan_request) { + WL_DBG((" REPORTING SCHED SCAN RESULTS \n")); + if (aborted) + cfg80211_sched_scan_stopped(wl->sched_scan_req->wiphy); + else + cfg80211_sched_scan_results(wl->sched_scan_req->wiphy); + wl->sched_scan_running = FALSE; + wl->sched_scan_req = NULL; + } +#endif /* WL_SCHED_SCAN */ + + if (likely(wl->scan_request)) { + cfg80211_scan_done(wl->scan_request, aborted); + wl->scan_request = NULL; + } + if (p2p_is_on(wl)) + wl_clr_p2p_status(wl, SCANNING); + wl_clr_drv_status(wl, SCANNING, dev); + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + if (params) + kfree(params); + + return err; +} + +static s32 wl_escan_handler(struct wl_priv *wl, + struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + s32 err = BCME_OK; + s32 status = ntoh32(e->status); + wl_bss_info_t *bi; + wl_escan_result_t *escan_result; + wl_bss_info_t *bss = NULL; + wl_scan_results_t *list; + u32 bi_length; + u32 i; + u8 *p2p_dev_addr = NULL; + WL_DBG((" enter event type : %d, status : %d \n", + ntoh32(e->event_type), ntoh32(e->status))); + /* P2P SCAN is coming from primary interface */ + if (wl_get_p2p_status(wl, SCANNING)) { + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) + ndev = wl->afx_hdl->dev; + else + ndev = wl->escan_info.ndev; + + } + if (!ndev || !wl->escan_on || + !wl_get_drv_status(wl, SCANNING, ndev)) { + WL_ERR(("escan is not ready ndev %p wl->escan_on %d drv_status 0x%x\n", + ndev, wl->escan_on, wl_get_drv_status(wl, SCANNING, ndev))); + return err; + } + + if (status == WLC_E_STATUS_PARTIAL) { + WL_INFO(("WLC_E_STATUS_PARTIAL \n")); + escan_result = (wl_escan_result_t *) data; + if (!escan_result) { + WL_ERR(("Invalid escan result (NULL pointer)\n")); + goto exit; + } + if (dtoh16(escan_result->bss_count) != 1) { + WL_ERR(("Invalid bss_count %d: ignoring\n", escan_result->bss_count)); + goto exit; + } + bi = escan_result->bss_info; + if (!bi) { + WL_ERR(("Invalid escan bss info (NULL pointer)\n")); + goto exit; + } + bi_length = dtoh32(bi->length); + if (bi_length != (dtoh32(escan_result->buflen) - WL_ESCAN_RESULTS_FIXED_SIZE)) { + WL_ERR(("Invalid bss_info length %d: ignoring\n", bi_length)); + goto exit; + } + + if (!(wl_to_wiphy(wl)->interface_modes & BIT(NL80211_IFTYPE_ADHOC))) { + if (dtoh16(bi->capability) & DOT11_CAP_IBSS) { + WL_DBG(("Ignoring IBSS result\n")); + goto exit; + } + } + + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) { + p2p_dev_addr = wl_cfgp2p_retreive_p2p_dev_addr(bi, bi_length); + if (p2p_dev_addr && !memcmp(p2p_dev_addr, + wl->afx_hdl->pending_tx_dst_addr.octet, ETHER_ADDR_LEN)) { + s32 channel = CHSPEC_CHANNEL(dtohchanspec(bi->chanspec)); + WL_DBG(("ACTION FRAME SCAN : Peer " MACSTR " found, channel : %d\n", + MAC2STR(wl->afx_hdl->pending_tx_dst_addr.octet), channel)); + wl_clr_p2p_status(wl, SCANNING); + wl->afx_hdl->peer_chan = channel; + complete(&wl->act_frm_scan); + goto exit; + } + + } else { + list = (wl_scan_results_t *)wl->escan_info.escan_buf; + if (bi_length > ESCAN_BUF_SIZE - list->buflen) { + WL_ERR(("Buffer is too small: ignoring\n")); + goto exit; + } +#define WLC_BSS_RSSI_ON_CHANNEL 0x0002 + for (i = 0; i < list->count; i++) { + bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length)) + : list->bss_info; + + if (!bcmp(&bi->BSSID, &bss->BSSID, ETHER_ADDR_LEN) && + CHSPEC_BAND(bi->chanspec) == CHSPEC_BAND(bss->chanspec) && + bi->SSID_len == bss->SSID_len && + !bcmp(bi->SSID, bss->SSID, bi->SSID_len)) { + if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) == + (bi->flags & WLC_BSS_RSSI_ON_CHANNEL)) { + /* preserve max RSSI if the measurements are + * both on-channel or both off-channel + */ + bss->RSSI = MAX(bss->RSSI, bi->RSSI); + } else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) && + (bi->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) { + /* preserve the on-channel rssi measurement + * if the new measurement is off channel + */ + bss->RSSI = bi->RSSI; + bss->flags |= WLC_BSS_RSSI_ON_CHANNEL; + } + + goto exit; + } + } + memcpy(&(wl->escan_info.escan_buf[list->buflen]), bi, bi_length); + list->version = dtoh32(bi->version); + list->buflen += bi_length; + list->count++; + + } + + } + else if (status == WLC_E_STATUS_SUCCESS) { + wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) { + WL_INFO(("ACTION FRAME SCAN DONE\n")); + wl_clr_p2p_status(wl, SCANNING); + wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); + if (wl->afx_hdl->peer_chan == WL_INVALID) + complete(&wl->act_frm_scan); + } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { + mutex_lock(&wl->usr_sync); + WL_INFO(("ESCAN COMPLETED\n")); + wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; + wl_inform_bss(wl); + wl_notify_escan_complete(wl, ndev, false, false); + mutex_unlock(&wl->usr_sync); + } + } + else if (status == WLC_E_STATUS_ABORT) { + wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) { + WL_INFO(("ACTION FRAME SCAN DONE\n")); + wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); + wl_clr_p2p_status(wl, SCANNING); + if (wl->afx_hdl->peer_chan == WL_INVALID) + complete(&wl->act_frm_scan); + } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { + mutex_lock(&wl->usr_sync); + WL_INFO(("ESCAN ABORTED\n")); + wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; + wl_inform_bss(wl); + wl_notify_escan_complete(wl, ndev, true, false); + mutex_unlock(&wl->usr_sync); + } + } + else { + WL_ERR(("unexpected Escan Event %d : abort\n", status)); + wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) { + WL_INFO(("ACTION FRAME SCAN DONE\n")); + wl_clr_p2p_status(wl, SCANNING); + wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); + if (wl->afx_hdl->peer_chan == WL_INVALID) + complete(&wl->act_frm_scan); + } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { + mutex_lock(&wl->usr_sync); + wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; + wl_inform_bss(wl); + wl_notify_escan_complete(wl, ndev, true, false); + mutex_unlock(&wl->usr_sync); + } + } +exit: + return err; +} + +static s32 wl_init_scan(struct wl_priv *wl) +{ + struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); + int err = 0; + + if (wl->iscan_on) { + iscan->dev = wl_to_prmry_ndev(wl); + iscan->state = WL_ISCAN_STATE_IDLE; + wl_init_iscan_handler(iscan); + iscan->timer_ms = WL_ISCAN_TIMER_INTERVAL_MS; + init_timer(&iscan->timer); + iscan->timer.data = (unsigned long) iscan; + iscan->timer.function = wl_iscan_timer; + sema_init(&iscan->sync, 0); + iscan->tsk = kthread_run(wl_iscan_thread, iscan, "wl_iscan"); + if (IS_ERR(iscan->tsk)) { + WL_ERR(("Could not create iscan thread\n")); + iscan->tsk = NULL; + return -ENOMEM; + } + iscan->data = wl; + } else if (wl->escan_on) { + wl->evt_handler[WLC_E_ESCAN_RESULT] = wl_escan_handler; + wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + } + /* Init scan_timeout timer */ + init_timer(&wl->scan_timeout); + wl->scan_timeout.data = (unsigned long) wl; + wl->scan_timeout.function = wl_scan_timeout; + + return err; +} + +static s32 wl_init_priv(struct wl_priv *wl) +{ + struct wiphy *wiphy = wl_to_wiphy(wl); + struct net_device *ndev = wl_to_prmry_ndev(wl); + s32 err = 0; + + wl->scan_request = NULL; + wl->pwr_save = !!(wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT); + wl->iscan_on = false; + wl->escan_on = true; + wl->roam_on = false; + wl->iscan_kickstart = false; + wl->active_scan = true; + wl->rf_blocked = false; + wl->deauth_reason = 0; + spin_lock_init(&wl->cfgdrv_lock); + mutex_init(&wl->ioctl_buf_sync); + init_waitqueue_head(&wl->netif_change_event); + wl_init_eq(wl); + err = wl_init_priv_mem(wl); + if (err) + return err; + if (wl_create_event_handler(wl)) + return -ENOMEM; + wl_init_event_handler(wl); + mutex_init(&wl->usr_sync); + err = wl_init_scan(wl); + if (err) + return err; + wl_init_conf(wl->conf); + wl_init_prof(wl, ndev); + wl_link_down(wl); + DNGL_FUNC(dhd_cfg80211_init, (wl)); + + return err; +} + +static void wl_deinit_priv(struct wl_priv *wl) +{ + DNGL_FUNC(dhd_cfg80211_deinit, (wl)); + wl_destroy_event_handler(wl); + wl_flush_eq(wl); + wl_link_down(wl); + del_timer_sync(&wl->scan_timeout); + wl_term_iscan(wl); + wl_deinit_priv_mem(wl); + unregister_netdevice_notifier(&wl_cfg80211_netdev_notifier); +} + +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) +static s32 wl_cfg80211_attach_p2p(void) +{ + struct wl_priv *wl = wlcfg_drv_priv; + + WL_TRACE(("Enter \n")); + + if (wl_cfgp2p_register_ndev(wl) < 0) { + WL_ERR(("%s: P2P attach failed. \n", __func__)); + return -ENODEV; + } + + return 0; +} + +static s32 wl_cfg80211_detach_p2p(void) +{ + struct wl_priv *wl = wlcfg_drv_priv; + struct wireless_dev *wdev = wl->p2p_wdev; + + WL_DBG(("Enter \n")); + if (!wdev || !wl) { + WL_ERR(("Invalid Ptr\n")); + return -EINVAL; + } + + wl_cfgp2p_unregister_ndev(wl); + + wl->p2p_wdev = NULL; + wl->p2p_net = NULL; + WL_DBG(("Freeing 0x%08x \n", (unsigned int)wdev)); + kfree(wdev); + + return 0; +} +#endif /* defined(WLP2P) && defined(WL_ENABLE_P2P_IF) */ + +s32 wl_cfg80211_attach_post(struct net_device *ndev) +{ + struct wl_priv * wl = NULL; + s32 err = 0; + WL_TRACE(("In\n")); + if (unlikely(!ndev)) { + WL_ERR(("ndev is invaild\n")); + return -ENODEV; + } + wl = wlcfg_drv_priv; + if (wl && !wl_get_drv_status(wl, READY, ndev)) { + if (wl->wdev && + wl_cfgp2p_supported(wl, ndev)) { +#if !defined(WL_ENABLE_P2P_IF) + wl->wdev->wiphy->interface_modes |= + (BIT(NL80211_IFTYPE_P2P_CLIENT)| + BIT(NL80211_IFTYPE_P2P_GO)); +#endif + if ((err = wl_cfgp2p_init_priv(wl)) != 0) + goto fail; + +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + if (wl->p2p_net) { + /* Update MAC addr for p2p0 interface here. */ + memcpy(wl->p2p_net->dev_addr, ndev->dev_addr, ETH_ALEN); + wl->p2p_net->dev_addr[0] |= 0x02; + printk("%s: p2p_dev_addr="MACSTR "\n", + wl->p2p_net->name, MAC2STR(wl->p2p_net->dev_addr)); + } else { + WL_ERR(("p2p_net not yet populated." + " Couldn't update the MAC Address for p2p0 \n")); + return -ENODEV; + } +#endif /* defined(WLP2P) && (WL_ENABLE_P2P_IF) */ + + wl->p2p_supported = true; + } + } else + return -ENODEV; + wl_set_drv_status(wl, READY, ndev); +fail: + return err; +} + +s32 wl_cfg80211_attach(struct net_device *ndev, void *data) +{ + struct wireless_dev *wdev; + struct wl_priv *wl; + s32 err = 0; + struct device *dev; + + WL_TRACE(("In\n")); + if (!ndev) { + WL_ERR(("ndev is invaild\n")); + return -ENODEV; + } + WL_DBG(("func %p\n", wl_cfg80211_get_parent_dev())); + dev = wl_cfg80211_get_parent_dev(); + + wdev = kzalloc(sizeof(*wdev), GFP_KERNEL); + if (unlikely(!wdev)) { + WL_ERR(("Could not allocate wireless device\n")); + return -ENOMEM; + } + err = wl_setup_wiphy(wdev, dev); + if (unlikely(err)) { + kfree(wdev); + return -ENOMEM; + } + wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_BSS); + wl = (struct wl_priv *)wiphy_priv(wdev->wiphy); + wl->wdev = wdev; + wl->pub = data; + INIT_LIST_HEAD(&wl->net_list); + ndev->ieee80211_ptr = wdev; + SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); + wdev->netdev = ndev; + err = wl_alloc_netinfo(wl, ndev, wdev, WL_MODE_BSS); + if (err) { + WL_ERR(("Failed to alloc net_info (%d)\n", err)); + goto cfg80211_attach_out; + } + err = wl_init_priv(wl); + if (err) { + WL_ERR(("Failed to init iwm_priv (%d)\n", err)); + goto cfg80211_attach_out; + } + + err = wl_setup_rfkill(wl, TRUE); + if (err) { + WL_ERR(("Failed to setup rfkill %d\n", err)); + goto cfg80211_attach_out; + } + err = register_netdevice_notifier(&wl_cfg80211_netdev_notifier); + if (err) { + WL_ERR(("Failed to register notifierl %d\n", err)); + goto cfg80211_attach_out; + } +#if defined(COEX_DHCP) + if (wl_cfg80211_btcoex_init(wl)) + goto cfg80211_attach_out; +#endif + + wlcfg_drv_priv = wl; + +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + err = wl_cfg80211_attach_p2p(); + if (err) + goto cfg80211_attach_out; +#endif + + return err; + +cfg80211_attach_out: + err = wl_setup_rfkill(wl, FALSE); + wl_free_wdev(wl); + return err; +} + +void wl_cfg80211_detach(void *para) +{ + struct wl_priv *wl; + + wl = wlcfg_drv_priv; + + WL_TRACE(("In\n")); + +#if defined(COEX_DHCP) + wl_cfg80211_btcoex_deinit(wl); +#endif + +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + wl_cfg80211_detach_p2p(); +#endif + wl_setup_rfkill(wl, FALSE); + if (wl->p2p_supported) + wl_cfgp2p_deinit_priv(wl); + wl_deinit_priv(wl); + wlcfg_drv_priv = NULL; + wl_cfg80211_clear_parent_dev(); + wl_free_wdev(wl); + /* PLEASE do NOT call any function after wl_free_wdev, the driver's private structure "wl", + * which is the private part of wiphy, has been freed in wl_free_wdev !!!!!!!!!!! + */ +} + +static void wl_wakeup_event(struct wl_priv *wl) +{ + if (wl->event_tsk.thr_pid >= 0) { + DHD_OS_WAKE_LOCK(wl->pub); + up(&wl->event_tsk.sema); + } +} + +static int wl_is_p2p_event(struct wl_event_q *e) +{ + switch (e->etype) { + /* We have to seperate out the P2P events received + * on primary interface so that it can be send up + * via p2p0 interface. + */ + case WLC_E_P2P_PROBREQ_MSG: + case WLC_E_P2P_DISC_LISTEN_COMPLETE: + case WLC_E_ACTION_FRAME_RX: + case WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE: + case WLC_E_ACTION_FRAME_COMPLETE: + + if (e->emsg.ifidx != 0) { + WL_TRACE(("P2P Event on Virtual I/F (ifidx:%d) \n", + e->emsg.ifidx)); + /* We are only bothered about the P2P events received + * on primary interface. For rest of them return false + * so that it is sent over the interface corresponding + * to the ifidx. + */ + return FALSE; + } else { + WL_TRACE(("P2P Event on Primary I/F (ifidx:%d)." + " Sent it to p2p0 \n", e->emsg.ifidx)); + return TRUE; + } + break; + + default: + WL_TRACE(("NON-P2P Event %d on ifidx (ifidx:%d) \n", + e->etype, e->emsg.ifidx)); + return FALSE; + } +} + +static s32 wl_event_handler(void *data) +{ + struct net_device *netdev; + struct wl_priv *wl = NULL; + struct wl_event_q *e; + tsk_ctl_t *tsk = (tsk_ctl_t *)data; + + wl = (struct wl_priv *)tsk->parent; + DAEMONIZE("dhd_cfg80211_event"); + complete(&tsk->completed); + + while (down_interruptible (&tsk->sema) == 0) { + SMP_RD_BARRIER_DEPENDS(); + if (tsk->terminated) + break; + while ((e = wl_deq_event(wl))) { + WL_DBG(("event type (%d), if idx: %d\n", e->etype, e->emsg.ifidx)); + /* All P2P device address related events comes on primary interface since + * there is no corresponding bsscfg for P2P interface. Map it to p2p0 + * interface. + */ + if ((wl_is_p2p_event(e) == TRUE) && (wl->p2p_net)) { + netdev = wl->p2p_net; + } else { + netdev = dhd_idx2net((struct dhd_pub *)(wl->pub), e->emsg.ifidx); + } + if (!netdev) + netdev = wl_to_prmry_ndev(wl); + if (e->etype < WLC_E_LAST && wl->evt_handler[e->etype]) { + wl->evt_handler[e->etype] (wl, netdev, &e->emsg, e->edata); + } else { + WL_DBG(("Unknown Event (%d): ignoring\n", e->etype)); + } + wl_put_event(e); + } + DHD_OS_WAKE_UNLOCK(wl->pub); + } + WL_ERR(("%s was terminated\n", __func__)); + complete_and_exit(&tsk->completed, 0); + return 0; +} + +void +wl_cfg80211_event(struct net_device *ndev, const wl_event_msg_t * e, void *data) +{ + u32 event_type = ntoh32(e->event_type); + struct wl_priv *wl = wlcfg_drv_priv; + +#if (WL_DBG_LEVEL > 0) + s8 *estr = (event_type <= sizeof(wl_dbg_estr) / WL_DBG_ESTR_MAX - 1) ? + wl_dbg_estr[event_type] : (s8 *) "Unknown"; + WL_DBG(("event_type (%d):" "WLC_E_" "%s\n", event_type, estr)); +#endif /* (WL_DBG_LEVEL > 0) */ + + if (likely(!wl_enq_event(wl, ndev, event_type, e, data))) + wl_wakeup_event(wl); +} + +static void wl_init_eq(struct wl_priv *wl) +{ + wl_init_eq_lock(wl); + INIT_LIST_HEAD(&wl->eq_list); +} + +static void wl_flush_eq(struct wl_priv *wl) +{ + struct wl_event_q *e; + unsigned long flags; + + flags = wl_lock_eq(wl); + while (!list_empty(&wl->eq_list)) { + e = list_first_entry(&wl->eq_list, struct wl_event_q, eq_list); + list_del(&e->eq_list); + kfree(e); + } + wl_unlock_eq(wl, flags); +} + +/* +* retrieve first queued event from head +*/ + +static struct wl_event_q *wl_deq_event(struct wl_priv *wl) +{ + struct wl_event_q *e = NULL; + unsigned long flags; + + flags = wl_lock_eq(wl); + if (likely(!list_empty(&wl->eq_list))) { + e = list_first_entry(&wl->eq_list, struct wl_event_q, eq_list); + list_del(&e->eq_list); + } + wl_unlock_eq(wl, flags); + + return e; +} + +/* + * push event to tail of the queue + */ + +static s32 +wl_enq_event(struct wl_priv *wl, struct net_device *ndev, u32 event, const wl_event_msg_t *msg, + void *data) +{ + struct wl_event_q *e; + s32 err = 0; + uint32 evtq_size; + uint32 data_len; + unsigned long flags; + gfp_t aflags; + + data_len = 0; + if (data) + data_len = ntoh32(msg->datalen); + evtq_size = sizeof(struct wl_event_q) + data_len; + aflags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; + e = kzalloc(evtq_size, aflags); + if (unlikely(!e)) { + WL_ERR(("event alloc failed\n")); + return -ENOMEM; + } + e->etype = event; + memcpy(&e->emsg, msg, sizeof(wl_event_msg_t)); + if (data) + memcpy(e->edata, data, data_len); + flags = wl_lock_eq(wl); + list_add_tail(&e->eq_list, &wl->eq_list); + wl_unlock_eq(wl, flags); + + return err; +} + +static void wl_put_event(struct wl_event_q *e) +{ + kfree(e); +} + +static s32 wl_config_ifmode(struct wl_priv *wl, struct net_device *ndev, s32 iftype) +{ + s32 infra = 0; + s32 err = 0; + s32 mode = 0; + switch (iftype) { + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_WDS: + WL_ERR(("type (%d) : currently we do not support this mode\n", + iftype)); + err = -EINVAL; + return err; + case NL80211_IFTYPE_ADHOC: + mode = WL_MODE_IBSS; + break; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + mode = WL_MODE_BSS; + infra = 1; + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + mode = WL_MODE_AP; + infra = 1; + break; + default: + err = -EINVAL; + WL_ERR(("invalid type (%d)\n", iftype)); + return err; + } + infra = htod32(infra); + err = wldev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(infra), true); + if (unlikely(err)) { + WL_ERR(("WLC_SET_INFRA error (%d)\n", err)); + return err; + } + + wl_set_mode_by_netdev(wl, ndev, mode); + + return 0; +} + +static s32 wl_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add) +{ + s8 iovbuf[WL_EVENTING_MASK_LEN + 12]; + + s8 eventmask[WL_EVENTING_MASK_LEN]; + s32 err = 0; + + /* Setup event_msgs */ + bcm_mkiovar("event_msgs", NULL, 0, iovbuf, + sizeof(iovbuf)); + err = wldev_ioctl(ndev, WLC_GET_VAR, iovbuf, sizeof(iovbuf), false); + if (unlikely(err)) { + WL_ERR(("Get event_msgs error (%d)\n", err)); + goto eventmsg_out; + } + memcpy(eventmask, iovbuf, WL_EVENTING_MASK_LEN); + if (add) { + setbit(eventmask, event); + } else { + clrbit(eventmask, event); + } + bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, + sizeof(iovbuf)); + err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); + if (unlikely(err)) { + WL_ERR(("Set event_msgs error (%d)\n", err)); + goto eventmsg_out; + } + +eventmsg_out: + return err; + +} + +static int wl_construct_reginfo(struct wl_priv *wl, s32 bw_cap) +{ + struct net_device *dev = wl_to_prmry_ndev(wl); + struct ieee80211_channel *band_chan_arr = NULL; + wl_uint32_list_t *list; + u32 i, j, index, n_2g, n_5g, band, channel, array_size; + u32 *n_cnt = NULL; + chanspec_t c = 0; + s32 err = BCME_OK; + bool update; + bool ht40_allowed; + u8 *pbuf = NULL; + +#define LOCAL_BUF_LEN 1024 + pbuf = kzalloc(LOCAL_BUF_LEN, GFP_KERNEL); + if (pbuf == NULL) { + WL_ERR(("failed to allocate local buf\n")); + return -ENOMEM; + } + + list = (wl_uint32_list_t *)(void *)pbuf; + list->count = htod32(WL_NUMCHANSPECS); + + err = wldev_iovar_getbuf_bsscfg(dev, "chanspecs", NULL, + 0, pbuf, LOCAL_BUF_LEN, 0, &wl->ioctl_buf_sync); + if (err != 0) { + WL_ERR(("get chanspecs failed with %d\n", err)); + kfree(pbuf); + return err; + } +#undef LOCAL_BUF_LEN + + band = array_size = n_2g = n_5g = 0; + for (i = 0; i < dtoh32(list->count); i++) { + index = 0; + update = FALSE; + ht40_allowed = FALSE; + c = (chanspec_t)dtoh32(list->element[i]); + channel = CHSPEC_CHANNEL(c); + if (CHSPEC_IS40(c)) { + if (CHSPEC_SB_UPPER(c)) + channel += CH_10MHZ_APART; + else + channel -= CH_10MHZ_APART; + } + + if (CHSPEC_IS2G(c) && channel <= CH_MAX_2G_CHANNEL) { + band_chan_arr = __wl_2ghz_channels; + array_size = ARRAYSIZE(__wl_2ghz_channels); + n_cnt = &n_2g; + band = IEEE80211_BAND_2GHZ; + ht40_allowed = (bw_cap == WLC_N_BW_40ALL) ? TRUE : FALSE; + } else if (CHSPEC_IS5G(c) && channel > CH_MAX_2G_CHANNEL) { + band_chan_arr = __wl_5ghz_a_channels; + array_size = ARRAYSIZE(__wl_5ghz_a_channels); + n_cnt = &n_5g; + band = IEEE80211_BAND_5GHZ; + ht40_allowed = (bw_cap == WLC_N_BW_20ALL) ? FALSE : TRUE; + } + + for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) { + if (band_chan_arr[j].hw_value == channel) { + update = TRUE; + break; + } + } + + if (update) + index = j; + else + index = *n_cnt; + + if (index < array_size) { +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 38) && !defined(WL_COMPAT_WIRELESS) + band_chan_arr[index].center_freq = + ieee80211_channel_to_frequency(channel); +#else + band_chan_arr[index].center_freq = + ieee80211_channel_to_frequency(channel, band); +#endif + band_chan_arr[index].hw_value = channel; + + if (CHSPEC_IS40(c) && ht40_allowed) { + u32 ht40_flag = band_chan_arr[index].flags & IEEE80211_CHAN_NO_HT40; + if (CHSPEC_SB_UPPER(c)) { + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + band_chan_arr[index].flags &= ~IEEE80211_CHAN_NO_HT40; + band_chan_arr[index].flags |= IEEE80211_CHAN_NO_HT40PLUS; + } else { + band_chan_arr[index].flags &= ~IEEE80211_CHAN_NO_HT40; + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + band_chan_arr[index].flags |= IEEE80211_CHAN_NO_HT40MINUS; + } + } else { + band_chan_arr[index].flags = IEEE80211_CHAN_NO_HT40; + if (band == IEEE80211_BAND_2GHZ) + channel |= WL_CHANSPEC_BAND_2G; + else + channel |= WL_CHANSPEC_BAND_5G; + err = wldev_iovar_getint(dev, "per_chan_info", &channel); + if (!err) { + if (channel & WL_CHAN_RADAR) { + band_chan_arr[index].flags |= IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS; + } + if (channel & WL_CHAN_PASSIVE) { + band_chan_arr[index].flags |= IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS; + } + } + } + + if (!update) + (*n_cnt)++; + } + } + + __wl_band_2ghz.n_channels = n_2g; + __wl_band_5ghz_a.n_channels = n_5g; + + kfree(pbuf); + return err; +} + +s32 wl_update_wiphybands(struct wl_priv *wl) +{ + struct wiphy *wiphy; + struct net_device *dev; + u32 bandlist[3]; + u32 nband = 0; + u32 i = 0; + s32 err = 0; + int nmode = 0; + int bw_cap = 0; + int index = 0; + + WL_DBG(("Entry")); + + if (wl == NULL) + wl = wlcfg_drv_priv; + dev = wl_to_prmry_ndev(wl); + + memset(bandlist, 0, sizeof(bandlist)); + err = wldev_ioctl(dev, WLC_GET_BANDLIST, bandlist, + sizeof(bandlist), false); + if (unlikely(err)) { + WL_ERR(("error read bandlist (%d)\n", err)); + return err; + } + wiphy = wl_to_wiphy(wl); + nband = bandlist[0]; + wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; + wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; + + err = wldev_iovar_getint(dev, "nmode", &nmode); + if (unlikely(err)) { + WL_ERR(("error reading nmode (%d)\n", err)); + } else { + /* For nmodeonly check bw cap */ + err = wldev_iovar_getint(dev, "mimo_bw_cap", &bw_cap); + if (unlikely(err)) { + WL_ERR(("error get mimo_bw_cap (%d)\n", err)); + } + } + + err = wl_construct_reginfo(wl, bw_cap); + if (err) { + WL_ERR(("wl_construct_reginfo() fails err=%d\n", err)); + return err; + } + for (i = 1; i <= nband && i < sizeof(bandlist)/sizeof(u32); i++) { + index = -1; + if (bandlist[i] == WLC_BAND_5G && __wl_band_5ghz_a.n_channels > 0) { + wiphy->bands[IEEE80211_BAND_5GHZ] = + &__wl_band_5ghz_a; + index = IEEE80211_BAND_5GHZ; + if (bw_cap == WLC_N_BW_40ALL || bw_cap == WLC_N_BW_20IN2G_40IN5G) + wiphy->bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + } else if (bandlist[i] == WLC_BAND_2G && __wl_band_2ghz.n_channels > 0) { + wiphy->bands[IEEE80211_BAND_2GHZ] = + &__wl_band_2ghz; + index = IEEE80211_BAND_2GHZ; + if (bandlist[i] == WLC_BAND_2G && bw_cap == WLC_N_BW_40ALL) + wiphy->bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + } + if ((index >= 0) && nmode) { + wiphy->bands[index]->ht_cap.cap |= + IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_DSSSCCK40; + wiphy->bands[index]->ht_cap.ht_supported = TRUE; + wiphy->bands[index]->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + wiphy->bands[index]->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; + } + } + + wiphy_apply_custom_regulatory(wiphy, &brcm_regdom); + return err; +} + +static s32 __wl_cfg80211_up(struct wl_priv *wl) +{ + s32 err = 0; + struct net_device *ndev = wl_to_prmry_ndev(wl); + struct wireless_dev *wdev = ndev->ieee80211_ptr; + + WL_DBG(("In\n")); + + err = dhd_config_dongle(wl, false); + if (unlikely(err)) + return err; + + err = wl_config_ifmode(wl, ndev, wdev->iftype); + if (unlikely(err && err != -EINPROGRESS)) { + WL_ERR(("wl_config_ifmode failed\n")); + } + err = wl_update_wiphybands(wl); + if (unlikely(err)) { + WL_ERR(("wl_update_wiphybands failed\n")); + } + + err = dhd_monitor_init(wl->pub); + err = wl_invoke_iscan(wl); + wl_set_drv_status(wl, READY, ndev); + return err; +} + +static s32 __wl_cfg80211_down(struct wl_priv *wl) +{ + s32 err = 0; + unsigned long flags; + struct net_info *iter, *next; + struct net_device *ndev = wl_to_prmry_ndev(wl); +#ifdef WL_ENABLE_P2P_IF + struct wiphy *wiphy = wl_to_prmry_ndev(wl)->ieee80211_ptr->wiphy; + struct net_device *p2p_net = wl->p2p_net; +#endif + + WL_DBG(("In\n")); + /* Check if cfg80211 interface is already down */ + if (!wl_get_drv_status(wl, READY, ndev)) + return err; /* it is even not ready */ + for_each_ndev(wl, iter, next) + wl_set_drv_status(wl, SCAN_ABORTING, iter->ndev); + + wl_term_iscan(wl); + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + if (wl->scan_request) { + cfg80211_scan_done(wl->scan_request, true); + wl->scan_request = NULL; + } + for_each_ndev(wl, iter, next) { + wl_clr_drv_status(wl, READY, iter->ndev); + wl_clr_drv_status(wl, SCANNING, iter->ndev); + wl_clr_drv_status(wl, SCAN_ABORTING, iter->ndev); + wl_clr_drv_status(wl, CONNECTING, iter->ndev); + wl_clr_drv_status(wl, CONNECTED, iter->ndev); + wl_clr_drv_status(wl, DISCONNECTING, iter->ndev); + wl_clr_drv_status(wl, AP_CREATED, iter->ndev); + wl_clr_drv_status(wl, AP_CREATING, iter->ndev); + } + wl_to_prmry_ndev(wl)->ieee80211_ptr->iftype = + NL80211_IFTYPE_STATION; +#ifdef WL_ENABLE_P2P_IF + wiphy->interface_modes = (wiphy->interface_modes) + & (~(BIT(NL80211_IFTYPE_P2P_CLIENT)| + BIT(NL80211_IFTYPE_P2P_GO))); + if ((p2p_net) && (p2p_net->flags & IFF_UP)) { + /* p2p0 interface is still UP. Bring it down */ + p2p_net->flags &= ~IFF_UP; + } +#endif /* WL_ENABLE_P2P_IF */ + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + + DNGL_FUNC(dhd_cfg80211_down, (wl)); + wl_flush_eq(wl); + wl_link_down(wl); + if (wl->p2p_supported) + wl_cfgp2p_down(wl); + dhd_monitor_uninit(); + + return err; +} + +s32 wl_cfg80211_up(void *para) +{ + struct wl_priv *wl; + s32 err = 0; + + WL_DBG(("In\n")); + wl = wlcfg_drv_priv; + mutex_lock(&wl->usr_sync); + wl_cfg80211_attach_post(wl_to_prmry_ndev(wl)); + err = __wl_cfg80211_up(wl); + if (err) + WL_ERR(("__wl_cfg80211_up failed\n")); + mutex_unlock(&wl->usr_sync); + return err; +} + +/* Private Event to Supplicant with indication that chip hangs */ +int wl_cfg80211_hang(struct net_device *dev, u16 reason) +{ + struct wl_priv *wl; + wl = wlcfg_drv_priv; + + WL_ERR(("In : chip crash eventing\n")); + cfg80211_disconnected(dev, reason, NULL, 0, GFP_KERNEL); + if (wl != NULL) { + wl_link_down(wl); + } + return 0; +} + +s32 wl_cfg80211_down(void *para) +{ + struct wl_priv *wl; + s32 err = 0; + + WL_DBG(("In\n")); + wl = wlcfg_drv_priv; + mutex_lock(&wl->usr_sync); + err = __wl_cfg80211_down(wl); + mutex_unlock(&wl->usr_sync); + + return err; +} + +static void *wl_read_prof(struct wl_priv *wl, struct net_device *ndev, s32 item) +{ + unsigned long flags; + void *rptr = NULL; + struct wl_profile *profile = wl_get_profile_by_netdev(wl, ndev); + + if (!profile) + return NULL; + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + switch (item) { + case WL_PROF_SEC: + rptr = &profile->sec; + break; + case WL_PROF_ACT: + rptr = &profile->active; + break; + case WL_PROF_BSSID: + rptr = profile->bssid; + break; + case WL_PROF_SSID: + rptr = &profile->ssid; + break; + } + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + if (!rptr) + WL_ERR(("invalid item (%d)\n", item)); + return rptr; +} + +static s32 +wl_update_prof(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data, s32 item) +{ + s32 err = 0; + struct wlc_ssid *ssid; + unsigned long flags; + struct wl_profile *profile = wl_get_profile_by_netdev(wl, ndev); + + if (!profile) + return WL_INVALID; + spin_lock_irqsave(&wl->cfgdrv_lock, flags); + switch (item) { + case WL_PROF_SSID: + ssid = (wlc_ssid_t *) data; + memset(profile->ssid.SSID, 0, + sizeof(profile->ssid.SSID)); + memcpy(profile->ssid.SSID, ssid->SSID, ssid->SSID_len); + profile->ssid.SSID_len = ssid->SSID_len; + break; + case WL_PROF_BSSID: + if (data) + memcpy(profile->bssid, data, ETHER_ADDR_LEN); + else + memset(profile->bssid, 0, ETHER_ADDR_LEN); + break; + case WL_PROF_SEC: + memcpy(&profile->sec, data, sizeof(profile->sec)); + break; + case WL_PROF_ACT: + profile->active = *(bool *)data; + break; + case WL_PROF_BEACONINT: + profile->beacon_interval = *(u16 *)data; + break; + case WL_PROF_DTIMPERIOD: + profile->dtim_period = *(u8 *)data; + break; + default: + WL_ERR(("unsupported item (%d)\n", item)); + err = -EOPNOTSUPP; + break; + } + spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + return err; +} + +void wl_cfg80211_dbg_level(u32 level) +{ + /* + * prohibit to change debug level + * by insmod parameter. + * eventually debug level will be configured + * in compile time by using CONFIG_XXX + */ + /* wl_dbg_level = level; */ +} + +static bool wl_is_ibssmode(struct wl_priv *wl, struct net_device *ndev) +{ + return wl_get_mode_by_netdev(wl, ndev) == WL_MODE_IBSS; +} + +static __used bool wl_is_ibssstarter(struct wl_priv *wl) +{ + return wl->ibss_starter; +} + +static void wl_rst_ie(struct wl_priv *wl) +{ + struct wl_ie *ie = wl_to_ie(wl); + + ie->offset = 0; +} + +static __used s32 wl_add_ie(struct wl_priv *wl, u8 t, u8 l, u8 *v) +{ + struct wl_ie *ie = wl_to_ie(wl); + s32 err = 0; + + if (unlikely(ie->offset + l + 2 > WL_TLV_INFO_MAX)) { + WL_ERR(("ei crosses buffer boundary\n")); + return -ENOSPC; + } + ie->buf[ie->offset] = t; + ie->buf[ie->offset + 1] = l; + memcpy(&ie->buf[ie->offset + 2], v, l); + ie->offset += l + 2; + + return err; +} + +static s32 wl_mrg_ie(struct wl_priv *wl, u8 *ie_stream, u16 ie_size) +{ + struct wl_ie *ie = wl_to_ie(wl); + s32 err = 0; + + if (unlikely(ie->offset + ie_size > WL_TLV_INFO_MAX)) { + WL_ERR(("ei_stream crosses buffer boundary\n")); + return -ENOSPC; + } + memcpy(&ie->buf[ie->offset], ie_stream, ie_size); + ie->offset += ie_size; + + return err; +} + +static s32 wl_cp_ie(struct wl_priv *wl, u8 *dst, u16 dst_size) +{ + struct wl_ie *ie = wl_to_ie(wl); + s32 err = 0; + + if (unlikely(ie->offset > dst_size)) { + WL_ERR(("dst_size is not enough\n")); + return -ENOSPC; + } + memcpy(dst, &ie->buf[0], ie->offset); + + return err; +} + +static u32 wl_get_ielen(struct wl_priv *wl) +{ + struct wl_ie *ie = wl_to_ie(wl); + + return ie->offset; +} + +static void wl_link_up(struct wl_priv *wl) +{ + wl->link_up = true; +} + +static void wl_link_down(struct wl_priv *wl) +{ + struct wl_connect_info *conn_info = wl_to_conn(wl); + + WL_DBG(("In\n")); + wl->link_up = false; + conn_info->req_ie_len = 0; + conn_info->resp_ie_len = 0; +} + +static unsigned long wl_lock_eq(struct wl_priv *wl) +{ + unsigned long flags; + + spin_lock_irqsave(&wl->eq_lock, flags); + return flags; +} + +static void wl_unlock_eq(struct wl_priv *wl, unsigned long flags) +{ + spin_unlock_irqrestore(&wl->eq_lock, flags); +} + +static void wl_init_eq_lock(struct wl_priv *wl) +{ + spin_lock_init(&wl->eq_lock); +} + +static void wl_delay(u32 ms) +{ + if (in_atomic() || ms < 1000 / HZ) { + mdelay(ms); + } else { + msleep(ms); + } +} + +s32 wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr) +{ + struct wl_priv *wl = wlcfg_drv_priv; + struct ether_addr p2pif_addr; + struct ether_addr primary_mac; + + if (!wl->p2p) + return -1; + if (!p2p_is_on(wl)) { + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, p2pdev_addr, &p2pif_addr); + } else { + memcpy(p2pdev_addr->octet, + wl->p2p->dev_addr.octet, ETHER_ADDR_LEN); + } + + return 0; +} +s32 wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len) +{ + struct wl_priv *wl; + + wl = wlcfg_drv_priv; + + return wl_cfgp2p_set_p2p_noa(wl, net, buf, len); +} + +s32 wl_cfg80211_get_p2p_noa(struct net_device *net, char* buf, int len) +{ + struct wl_priv *wl; + wl = wlcfg_drv_priv; + + return wl_cfgp2p_get_p2p_noa(wl, net, buf, len); +} + +s32 wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len) +{ + struct wl_priv *wl; + wl = wlcfg_drv_priv; + + return wl_cfgp2p_set_p2p_ps(wl, net, buf, len); +} + +s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len, + enum wl_management_type type) +{ + struct wl_priv *wl; + struct net_device *ndev = NULL; + struct ether_addr primary_mac; + s32 ret = 0; + s32 bssidx = 0; + s32 pktflag = 0; + wl = wlcfg_drv_priv; + + if (wl_get_drv_status(wl, AP_CREATING, net) || + wl_get_drv_status(wl, AP_CREATED, net)) { + ndev = net; + bssidx = 0; + } else if (wl->p2p) { + if (net == wl->p2p_net) { + net = wl_to_prmry_ndev(wl); + } + + if (!wl->p2p->on) { + get_primary_mac(wl, &primary_mac); + wl_cfgp2p_generate_bss_mac(&primary_mac, &wl->p2p->dev_addr, + &wl->p2p->int_addr); + /* In case of p2p_listen command, supplicant send remain_on_channel + * without turning on P2P + */ + p2p_on(wl) = true; + ret = wl_cfgp2p_enable_discovery(wl, ndev, NULL, 0); + + if (unlikely(ret)) { + goto exit; + } + } + if (net != wl_to_prmry_ndev(wl)) { + if (wl_get_mode_by_netdev(wl, net) == WL_MODE_AP) { + ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); + bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION); + } + } else { + ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); + bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); + } + } + if (ndev != NULL) { + switch (type) { + case WL_BEACON: + pktflag = VNDR_IE_BEACON_FLAG; + break; + case WL_PROBE_RESP: + pktflag = VNDR_IE_PRBRSP_FLAG; + break; + case WL_ASSOC_RESP: + pktflag = VNDR_IE_ASSOCRSP_FLAG; + break; + } + if (pktflag) + ret = wl_cfgp2p_set_management_ie(wl, ndev, bssidx, pktflag, buf, len); + } +exit: + return ret; +} + +static const struct rfkill_ops wl_rfkill_ops = { + .set_block = wl_rfkill_set +}; + +static int wl_rfkill_set(void *data, bool blocked) +{ + struct wl_priv *wl = (struct wl_priv *)data; + + WL_DBG(("Enter \n")); + WL_DBG(("RF %s\n", blocked ? "blocked" : "unblocked")); + + if (!wl) + return -EINVAL; + + wl->rf_blocked = blocked; + + return 0; +} + +static int wl_setup_rfkill(struct wl_priv *wl, bool setup) +{ + s32 err = 0; + + WL_DBG(("Enter \n")); + if (!wl) + return -EINVAL; + if (setup) { + wl->rfkill = rfkill_alloc("brcmfmac-wifi", + wl_cfg80211_get_parent_dev(), + RFKILL_TYPE_WLAN, &wl_rfkill_ops, (void *)wl); + + if (!wl->rfkill) { + err = -ENOMEM; + goto err_out; + } + + err = rfkill_register(wl->rfkill); + + if (err) + rfkill_destroy(wl->rfkill); + } else { + if (!wl->rfkill) { + err = -ENOMEM; + goto err_out; + } + + rfkill_unregister(wl->rfkill); + rfkill_destroy(wl->rfkill); + } + +err_out: + return err; +} + +struct device *wl_cfg80211_get_parent_dev(void) +{ + return cfg80211_parent_dev; +} + +void wl_cfg80211_set_parent_dev(void *dev) +{ + cfg80211_parent_dev = dev; +} + +static void wl_cfg80211_clear_parent_dev(void) +{ + cfg80211_parent_dev = NULL; +} + +static void get_primary_mac(struct wl_priv *wl, struct ether_addr *mac) +{ + wldev_iovar_getbuf_bsscfg(wl_to_prmry_ndev(wl), "cur_etheraddr", NULL, + 0, wl->ioctl_buf, WLC_IOCTL_MAXLEN, 0, &wl->ioctl_buf_sync); + memcpy(mac->octet, wl->ioctl_buf, ETHER_ADDR_LEN); +} + +int wl_cfg80211_do_driver_init(struct net_device *net) +{ + struct wl_priv *wl = *(struct wl_priv **)netdev_priv(net); + + if (!wl || !wl->wdev) + return -EINVAL; + + if (dhd_do_driver_init(wl->wdev->netdev) < 0) + return -1; + + return 0; +} + +void wl_cfg80211_enable_trace(int level) +{ + wl_dbg_level |= WL_DBG_DBG; +} diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h new file mode 100644 index 0000000000000..37c8e5850c449 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -0,0 +1,673 @@ +/* + * Linux cfg80211 driver + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfg80211.h,v 1.1.4.1.2.8 2011/02/09 01:37:52 Exp $ + */ + +#ifndef _wl_cfg80211_h_ +#define _wl_cfg80211_h_ + +#include +#include +#include +#include +#include +#include +#include + +#include + +struct wl_conf; +struct wl_iface; +struct wl_priv; +struct wl_security; +struct wl_ibss; + + +#define htod32(i) i +#define htod16(i) i +#define dtoh32(i) i +#define dtoh16(i) i +#define htodchanspec(i) i +#define dtohchanspec(i) i + +#define WL_DBG_NONE 0 +#define WL_DBG_TRACE (1 << 4) +#define WL_DBG_SCAN (1 << 3) +#define WL_DBG_DBG (1 << 2) +#define WL_DBG_INFO (1 << 1) +#define WL_DBG_ERR (1 << 0) + +/* 0 invalidates all debug messages. default is 1 */ +#define WL_DBG_LEVEL 0xFF + +#define WL_ERR(args) \ +do { \ + if (wl_dbg_level & WL_DBG_ERR) { \ + printk(KERN_ERR "CFG80211-ERROR) %s : ", __func__); \ + printk args; \ + } \ +} while (0) +#ifdef WL_INFO +#undef WL_INFO +#endif +#define WL_INFO(args) \ +do { \ + if (wl_dbg_level & WL_DBG_INFO) { \ + printk(KERN_ERR "CFG80211-INFO) %s : ", __func__); \ + printk args; \ + } \ +} while (0) +#ifdef WL_SCAN +#undef WL_SCAN +#endif +#define WL_SCAN(args) \ +do { \ + if (wl_dbg_level & WL_DBG_SCAN) { \ + printk(KERN_ERR "CFG80211-SCAN) %s :", __func__); \ + printk args; \ + } \ +} while (0) +#ifdef WL_TRACE +#undef WL_TRACE +#endif +#define WL_TRACE(args) \ +do { \ + if (wl_dbg_level & WL_DBG_TRACE) { \ + printk(KERN_ERR "CFG80211-TRACE) %s :", __func__); \ + printk args; \ + } \ +} while (0) +#if (WL_DBG_LEVEL > 0) +#define WL_DBG(args) \ +do { \ + if (wl_dbg_level & WL_DBG_DBG) { \ + printk(KERN_ERR "CFG80211-DEBUG) %s :", __func__); \ + printk args; \ + } \ +} while (0) +#else /* !(WL_DBG_LEVEL > 0) */ +#define WL_DBG(args) +#endif /* (WL_DBG_LEVEL > 0) */ + + +#define WL_SCAN_RETRY_MAX 3 +#define WL_NUM_PMKIDS_MAX MAXPMKID +#define WL_SCAN_BUF_MAX (1024 * 8) +#define WL_TLV_INFO_MAX 1024 +#define WL_SCAN_IE_LEN_MAX 2048 +#define WL_BSS_INFO_MAX 2048 +#define WL_ASSOC_INFO_MAX 512 +#define WL_IOCTL_LEN_MAX 1024 +#define WL_EXTRA_BUF_MAX 2048 +#define WL_ISCAN_BUF_MAX 2048 +#define WL_ISCAN_TIMER_INTERVAL_MS 3000 +#define WL_SCAN_ERSULTS_LAST (WL_SCAN_RESULTS_NO_MEM+1) +#define WL_AP_MAX 256 +#define WL_FILE_NAME_MAX 256 +#define WL_DWELL_TIME 200 +#define WL_MED_DWELL_TIME 400 +#define WL_LONG_DWELL_TIME 1000 +#define IFACE_MAX_CNT 2 + +#define WL_SCAN_TIMER_INTERVAL_MS 8000 /* Scan timeout */ +#define WL_CHANNEL_SYNC_RETRY 3 +#define WL_ACT_FRAME_RETRY 4 + +#define WL_INVALID -1 + + +/* Bring down SCB Timeout to 20secs from 60secs default */ +#ifndef WL_SCB_TIMEOUT +#define WL_SCB_TIMEOUT 20 +#endif + +/* driver status */ +enum wl_status { + WL_STATUS_READY = 0, + WL_STATUS_SCANNING, + WL_STATUS_SCAN_ABORTING, + WL_STATUS_CONNECTING, + WL_STATUS_CONNECTED, + WL_STATUS_DISCONNECTING, + WL_STATUS_AP_CREATING, + WL_STATUS_AP_CREATED, + WL_STATUS_SENDING_ACT_FRM +}; + +/* wi-fi mode */ +enum wl_mode { + WL_MODE_BSS, + WL_MODE_IBSS, + WL_MODE_AP +}; + +/* driver profile list */ +enum wl_prof_list { + WL_PROF_MODE, + WL_PROF_SSID, + WL_PROF_SEC, + WL_PROF_IBSS, + WL_PROF_BAND, + WL_PROF_BSSID, + WL_PROF_ACT, + WL_PROF_BEACONINT, + WL_PROF_DTIMPERIOD +}; + +/* driver iscan state */ +enum wl_iscan_state { + WL_ISCAN_STATE_IDLE, + WL_ISCAN_STATE_SCANING +}; + +/* donlge escan state */ +enum wl_escan_state { + WL_ESCAN_STATE_IDLE, + WL_ESCAN_STATE_SCANING +}; +/* fw downloading status */ +enum wl_fw_status { + WL_FW_LOADING_DONE, + WL_NVRAM_LOADING_DONE +}; + +enum wl_management_type { + WL_BEACON = 0x1, + WL_PROBE_RESP = 0x2, + WL_ASSOC_RESP = 0x4 +}; +/* beacon / probe_response */ +struct beacon_proberesp { + __le64 timestamp; + __le16 beacon_int; + __le16 capab_info; + u8 variable[0]; +} __attribute__ ((packed)); + +/* driver configuration */ +struct wl_conf { + u32 frag_threshold; + u32 rts_threshold; + u32 retry_short; + u32 retry_long; + s32 tx_power; + struct ieee80211_channel channel; +}; + +typedef s32(*EVENT_HANDLER) (struct wl_priv *wl, + struct net_device *ndev, const wl_event_msg_t *e, void *data); + +/* bss inform structure for cfg80211 interface */ +struct wl_cfg80211_bss_info { + u16 band; + u16 channel; + s16 rssi; + u16 frame_len; + u8 frame_buf[1]; +}; + +/* basic structure of scan request */ +struct wl_scan_req { + struct wlc_ssid ssid; +}; + +/* basic structure of information element */ +struct wl_ie { + u16 offset; + u8 buf[WL_TLV_INFO_MAX]; +}; + +/* event queue for cfg80211 main event */ +struct wl_event_q { + struct list_head eq_list; + u32 etype; + wl_event_msg_t emsg; + s8 edata[1]; +}; + +/* security information with currently associated ap */ +struct wl_security { + u32 wpa_versions; + u32 auth_type; + u32 cipher_pairwise; + u32 cipher_group; + u32 wpa_auth; +}; + +/* ibss information for currently joined ibss network */ +struct wl_ibss { + u8 beacon_interval; /* in millisecond */ + u8 atim; /* in millisecond */ + s8 join_only; + u8 band; + u8 channel; +}; + +/* wl driver profile */ +struct wl_profile { + u32 mode; + s32 band; + struct wlc_ssid ssid; + struct wl_security sec; + struct wl_ibss ibss; + u8 bssid[ETHER_ADDR_LEN]; + u16 beacon_interval; + u8 dtim_period; + bool active; +}; + +struct net_info { + struct net_device *ndev; + struct wireless_dev *wdev; + struct wl_profile profile; + s32 mode; + unsigned long sme_state; + struct list_head list; /* list of all net_info structure */ +}; +typedef s32(*ISCAN_HANDLER) (struct wl_priv *wl); + +/* iscan controller */ +struct wl_iscan_ctrl { + struct net_device *dev; + struct timer_list timer; + u32 timer_ms; + u32 timer_on; + s32 state; + struct task_struct *tsk; + struct semaphore sync; + ISCAN_HANDLER iscan_handler[WL_SCAN_ERSULTS_LAST]; + void *data; + s8 ioctl_buf[WLC_IOCTL_SMLEN]; + s8 scan_buf[WL_ISCAN_BUF_MAX]; +}; + +/* association inform */ +#define MAX_REQ_LINE 1024 +struct wl_connect_info { + u8 req_ie[MAX_REQ_LINE]; + s32 req_ie_len; + u8 resp_ie[MAX_REQ_LINE]; + s32 resp_ie_len; +}; + +/* firmware /nvram downloading controller */ +struct wl_fw_ctrl { + const struct firmware *fw_entry; + unsigned long status; + u32 ptr; + s8 fw_name[WL_FILE_NAME_MAX]; + s8 nvram_name[WL_FILE_NAME_MAX]; +}; + +/* assoc ie length */ +struct wl_assoc_ielen { + u32 req_len; + u32 resp_len; +}; + +/* wpa2 pmk list */ +struct wl_pmk_list { + pmkid_list_t pmkids; + pmkid_t foo[MAXPMKID - 1]; +}; + + +#define ESCAN_BUF_SIZE (64 * 1024) + +struct escan_info { + u32 escan_state; + u8 escan_buf[ESCAN_BUF_SIZE]; + struct wiphy *wiphy; + struct net_device *ndev; +}; + +struct ap_info { +/* Structure to hold WPS, WPA IEs for a AP */ + u8 probe_res_ie[IE_MAX_LEN]; + u8 beacon_ie[IE_MAX_LEN]; + u32 probe_res_ie_len; + u32 beacon_ie_len; + u8 *wpa_ie; + u8 *rsn_ie; + u8 *wps_ie; + bool security_mode; +}; +struct btcoex_info { + struct timer_list timer; + u32 timer_ms; + u32 timer_on; + u32 ts_dhcp_start; /* ms ts ecord time stats */ + u32 ts_dhcp_ok; /* ms ts ecord time stats */ + bool dhcp_done; /* flag, indicates that host done with + * dhcp before t1/t2 expiration + */ + s32 bt_state; + struct work_struct work; + struct net_device *dev; +}; + +struct sta_info { + /* Structure to hold WPS IE for a STA */ + u8 probe_req_ie[IE_MAX_LEN]; + u8 assoc_req_ie[IE_MAX_LEN]; + u32 probe_req_ie_len; + u32 assoc_req_ie_len; +}; + +struct afx_hdl { + wl_af_params_t *pending_tx_act_frm; + struct ether_addr pending_tx_dst_addr; + struct net_device *dev; + struct work_struct work; + u32 bssidx; + u32 retry; + s32 peer_chan; + bool ack_recv; +}; + +/* private data of cfg80211 interface */ +struct wl_priv { + struct wireless_dev *wdev; /* representing wl cfg80211 device */ + + struct wireless_dev *p2p_wdev; /* representing wl cfg80211 device for P2P */ + struct net_device *p2p_net; /* reference to p2p0 interface */ + + struct wl_conf *conf; + struct cfg80211_scan_request *scan_request; /* scan request object */ + EVENT_HANDLER evt_handler[WLC_E_LAST]; + struct list_head eq_list; /* used for event queue */ + struct list_head net_list; /* used for struct net_info */ + spinlock_t eq_lock; /* for event queue synchronization */ + spinlock_t cfgdrv_lock; /* to protect scan status (and others if needed) */ + struct completion act_frm_scan; + struct mutex usr_sync; /* maily for up/down synchronization */ + struct wl_scan_results *bss_list; + struct wl_scan_results *scan_results; + + /* scan request object for internal purpose */ + struct wl_scan_req *scan_req_int; + /* information element object for internal purpose */ + struct wl_ie ie; + struct wl_iscan_ctrl *iscan; /* iscan controller */ + + /* association information container */ + struct wl_connect_info conn_info; + + struct wl_pmk_list *pmk_list; /* wpa2 pmk list */ + tsk_ctl_t event_tsk; /* task of main event handler thread */ + void *pub; + u32 iface_cnt; + u32 channel; /* current channel */ + bool iscan_on; /* iscan on/off switch */ + bool iscan_kickstart; /* indicate iscan already started */ + bool escan_on; /* escan on/off switch */ + struct escan_info escan_info; /* escan information */ + bool active_scan; /* current scan mode */ + bool ibss_starter; /* indicates this sta is ibss starter */ + bool link_up; /* link/connection up flag */ + + /* indicate whether chip to support power save mode */ + bool pwr_save; + bool roam_on; /* on/off switch for self-roaming */ + bool scan_tried; /* indicates if first scan attempted */ + u8 *ioctl_buf; /* ioctl buffer */ + struct mutex ioctl_buf_sync; + u8 *escan_ioctl_buf; + u8 *extra_buf; /* maily to grab assoc information */ + struct dentry *debugfsdir; + struct rfkill *rfkill; + bool rf_blocked; + struct ieee80211_channel remain_on_chan; + enum nl80211_channel_type remain_on_chan_type; + u64 send_action_id; + u64 last_roc_id; + wait_queue_head_t netif_change_event; + struct afx_hdl *afx_hdl; + struct ap_info *ap_info; + struct sta_info *sta_info; + struct p2p_info *p2p; + bool p2p_supported; + struct btcoex_info *btcoex_info; + struct timer_list scan_timeout; /* Timer for catch scan event timeout */ +#ifdef WL_SCHED_SCAN + struct cfg80211_sched_scan_request *sched_scan_req; /* scheduled scan req */ +#endif /* WL_SCHED_SCAN */ + bool sched_scan_running; /* scheduled scan req status */ + u16 hostapd_chan; /* remember chan requested by framework for hostapd */ + u16 deauth_reason; /* Place holder to save deauth/disassoc reasons */ +}; + +static inline struct wl_bss_info *next_bss(struct wl_scan_results *list, struct wl_bss_info *bss) +{ + return bss = bss ? + (struct wl_bss_info *)((uintptr) bss + dtoh32(bss->length)) : list->bss_info; +} + +static inline s32 +wl_alloc_netinfo(struct wl_priv *wl, struct net_device *ndev, + struct wireless_dev * wdev, s32 mode) +{ + struct net_info *_net_info; + s32 err = 0; + if (wl->iface_cnt == IFACE_MAX_CNT) + return -ENOMEM; + _net_info = kzalloc(sizeof(struct net_info), GFP_KERNEL); + if (!_net_info) + err = -ENOMEM; + else { + _net_info->mode = mode; + _net_info->ndev = ndev; + _net_info->wdev = wdev; + wl->iface_cnt++; + list_add(&_net_info->list, &wl->net_list); + } + return err; +} + +static inline void +wl_dealloc_netinfo(struct wl_priv *wl, struct net_device *ndev) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) { + list_del(&_net_info->list); + wl->iface_cnt--; + if (_net_info->wdev) { + kfree(_net_info->wdev); + ndev->ieee80211_ptr = NULL; + } + kfree(_net_info); + } + } +} + +static inline void +wl_delete_all_netinfo(struct wl_priv *wl) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + list_del(&_net_info->list); + if (_net_info->wdev) + kfree(_net_info->wdev); + kfree(_net_info); + } + wl->iface_cnt = 0; +} + +static inline bool +wl_get_status_all(struct wl_priv *wl, s32 status) + +{ + struct net_info *_net_info, *next; + u32 cnt = 0; + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (_net_info->ndev && + test_bit(status, &_net_info->sme_state)) + cnt++; + } + return cnt? true: false; +} + +static inline void +wl_set_status_by_netdev(struct wl_priv *wl, s32 status, + struct net_device *ndev, u32 op) +{ + + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) { + switch (op) { + case 1: + set_bit(status, &_net_info->sme_state); + break; + case 2: + clear_bit(status, &_net_info->sme_state); + break; + case 4: + change_bit(status, &_net_info->sme_state); + break; + } + } + + } +} + +static inline u32 +wl_get_status_by_netdev(struct wl_priv *wl, s32 status, + struct net_device *ndev) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) + return test_bit(status, &_net_info->sme_state); + } + return 0; +} + +static inline s32 +wl_get_mode_by_netdev(struct wl_priv *wl, struct net_device *ndev) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) + return _net_info->mode; + } + return -1; +} + +static inline void +wl_set_mode_by_netdev(struct wl_priv *wl, struct net_device *ndev, + s32 mode) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) + _net_info->mode = mode; + } +} + +static inline struct wl_profile * +wl_get_profile_by_netdev(struct wl_priv *wl, struct net_device *ndev) +{ + struct net_info *_net_info, *next; + + list_for_each_entry_safe(_net_info, next, &wl->net_list, list) { + if (ndev && (_net_info->ndev == ndev)) + return &_net_info->profile; + } + return NULL; +} +#define wl_to_wiphy(w) (w->wdev->wiphy) +#define wl_to_prmry_ndev(w) (w->wdev->netdev) +#define ndev_to_wl(n) (wdev_to_wl(n->ieee80211_ptr)) +#define wl_to_sr(w) (w->scan_req_int) +#define wl_to_ie(w) (&w->ie) +#define iscan_to_wl(i) ((struct wl_priv *)(i->data)) +#define wl_to_iscan(w) (w->iscan) +#define wl_to_conn(w) (&w->conn_info) +#define wiphy_from_scan(w) (w->escan_info.wiphy) +#define wl_get_drv_status_all(wl, stat) \ + (wl_get_status_all(wl, WL_STATUS_ ## stat)) +#define wl_get_drv_status(wl, stat, ndev) \ + (wl_get_status_by_netdev(wl, WL_STATUS_ ## stat, ndev)) +#define wl_set_drv_status(wl, stat, ndev) \ + (wl_set_status_by_netdev(wl, WL_STATUS_ ## stat, ndev, 1)) +#define wl_clr_drv_status(wl, stat, ndev) \ + (wl_set_status_by_netdev(wl, WL_STATUS_ ## stat, ndev, 2)) +#define wl_chg_drv_status(wl, stat, ndev) \ + (wl_set_status_by_netdev(wl, WL_STATUS_ ## stat, ndev, 4)) + +#define for_each_bss(list, bss, __i) \ + for (__i = 0; __i < list->count && __i < WL_AP_MAX; __i++, bss = next_bss(list, bss)) + +#define for_each_ndev(wl, iter, next) \ + list_for_each_entry_safe(iter, next, &wl->net_list, list) + + +/* In case of WPS from wpa_supplicant, pairwise siute and group suite is 0. + * In addtion to that, wpa_version is WPA_VERSION_1 + */ +#define is_wps_conn(_sme) \ + ((wl_cfgp2p_find_wpsie((u8 *)_sme->ie, _sme->ie_len) != NULL) && \ + (!_sme->crypto.n_ciphers_pairwise) && \ + (!_sme->crypto.cipher_group)) +extern s32 wl_cfg80211_attach(struct net_device *ndev, void *data); +extern s32 wl_cfg80211_attach_post(struct net_device *ndev); +extern void wl_cfg80211_detach(void *para); + +extern void wl_cfg80211_event(struct net_device *ndev, const wl_event_msg_t *e, + void *data); +void wl_cfg80211_set_parent_dev(void *dev); +struct device *wl_cfg80211_get_parent_dev(void); + +extern s32 wl_cfg80211_up(void *para); +extern s32 wl_cfg80211_down(void *para); +extern s32 wl_cfg80211_notify_ifadd(struct net_device *ndev, s32 idx, s32 bssidx, + void* _net_attach); +extern s32 wl_cfg80211_ifdel_ops(struct net_device *net); +extern s32 wl_cfg80211_notify_ifdel(void); +extern s32 wl_cfg80211_is_progress_ifadd(void); +extern s32 wl_cfg80211_is_progress_ifchange(void); +extern s32 wl_cfg80211_is_progress_ifadd(void); +extern s32 wl_cfg80211_notify_ifchange(void); +extern void wl_cfg80211_dbg_level(u32 level); +extern s32 wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr); +extern s32 wl_cfg80211_set_p2p_noa(struct net_device *net, char* buf, int len); +extern s32 wl_cfg80211_get_p2p_noa(struct net_device *net, char* buf, int len); +extern s32 wl_cfg80211_set_wps_p2p_ie(struct net_device *net, char *buf, int len, + enum wl_management_type type); +extern s32 wl_cfg80211_set_p2p_ps(struct net_device *net, char* buf, int len); +extern int wl_cfg80211_hang(struct net_device *dev, u16 reason); +extern s32 wl_mode_to_nl80211_iftype(s32 mode); +int wl_cfg80211_do_driver_init(struct net_device *net); +void wl_cfg80211_enable_trace(int level); +extern s32 wl_update_wiphybands(struct wl_priv *wl); +extern s32 wl_cfg80211_if_is_group_owner(void); +#endif /* _wl_cfg80211_h_ */ diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c new file mode 100644 index 0000000000000..bdcfa95307c75 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c @@ -0,0 +1,2000 @@ +/* + * Linux cfgp2p driver + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfgp2p.c,v 1.1.4.1.2.14 2011-02-09 01:40:07 $ + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +static s8 scanparambuf[WLC_IOCTL_SMLEN]; + +static bool +wl_cfgp2p_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, const u8 *oui, u32 oui_len, u8 type); + +static s32 +wl_cfgp2p_vndr_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, s32 pktflag, + s8 *oui, s32 ie_id, s8 *data, s32 data_len, s32 delete); + +static int wl_cfgp2p_start_xmit(struct sk_buff *skb, struct net_device *ndev); +static int wl_cfgp2p_do_ioctl(struct net_device *net, struct ifreq *ifr, int cmd); +static int wl_cfgp2p_if_open(struct net_device *net); +static int wl_cfgp2p_if_stop(struct net_device *net); +static s32 wl_cfgp2p_cancel_listen(struct wl_priv *wl, struct net_device *ndev, + bool notify); + +static const struct net_device_ops wl_cfgp2p_if_ops = { + .ndo_open = wl_cfgp2p_if_open, + .ndo_stop = wl_cfgp2p_if_stop, + .ndo_do_ioctl = wl_cfgp2p_do_ioctl, + .ndo_start_xmit = wl_cfgp2p_start_xmit, +}; + +bool wl_cfgp2p_is_pub_action(void *frame, u32 frame_len) +{ + wifi_p2p_pub_act_frame_t *pact_frm; + + if (frame == NULL) + return false; + pact_frm = (wifi_p2p_pub_act_frame_t *)frame; + if (frame_len < sizeof(wifi_p2p_pub_act_frame_t) -1) + return false; + + if (pact_frm->category == P2P_PUB_AF_CATEGORY && + pact_frm->action == P2P_PUB_AF_ACTION && + pact_frm->oui_type == P2P_VER && + memcmp(pact_frm->oui, P2P_OUI, sizeof(pact_frm->oui)) == 0) { + return true; + } + + return false; +} + +bool wl_cfgp2p_is_p2p_action(void *frame, u32 frame_len) +{ + wifi_p2p_action_frame_t *act_frm; + + if (frame == NULL) + return false; + act_frm = (wifi_p2p_action_frame_t *)frame; + if (frame_len < sizeof(wifi_p2p_action_frame_t) -1) + return false; + + if (act_frm->category == P2P_AF_CATEGORY && + act_frm->type == P2P_VER && + memcmp(act_frm->OUI, P2P_OUI, DOT11_OUI_LEN) == 0) { + return true; + } + + return false; +} + +bool wl_cfgp2p_is_gas_action(void *frame, u32 frame_len) +{ + + wifi_p2psd_gas_pub_act_frame_t *sd_act_frm; + + if (frame == NULL) + return false; + + sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *)frame; + if (frame_len < sizeof(wifi_p2psd_gas_pub_act_frame_t) - 1) + return false; + if (sd_act_frm->category != P2PSD_ACTION_CATEGORY) + return false; + + if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IREQ || + sd_act_frm->action == P2PSD_ACTION_ID_GAS_IRESP || + sd_act_frm->action == P2PSD_ACTION_ID_GAS_CREQ || + sd_act_frm->action == P2PSD_ACTION_ID_GAS_CRESP) + return true; + else + return false; + +} + +void wl_cfgp2p_print_actframe(bool tx, void *frame, u32 frame_len) +{ + wifi_p2p_pub_act_frame_t *pact_frm; + wifi_p2p_action_frame_t *act_frm; + wifi_p2psd_gas_pub_act_frame_t *sd_act_frm; + if (!frame || frame_len <= 2) + return; + + if (wl_cfgp2p_is_pub_action(frame, frame_len)) { + pact_frm = (wifi_p2p_pub_act_frame_t *)frame; + switch (pact_frm->subtype) { + case P2P_PAF_GON_REQ: + CFGP2P_DBG(("%s P2P Group Owner Negotiation Req Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_GON_RSP: + CFGP2P_DBG(("%s P2P Group Owner Negotiation Rsp Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_GON_CONF: + CFGP2P_DBG(("%s P2P Group Owner Negotiation Confirm Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_INVITE_REQ: + CFGP2P_DBG(("%s P2P Invitation Request Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_INVITE_RSP: + CFGP2P_DBG(("%s P2P Invitation Response Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_DEVDIS_REQ: + CFGP2P_DBG(("%s P2P Device Discoverability Request Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_DEVDIS_RSP: + CFGP2P_DBG(("%s P2P Device Discoverability Response Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_PROVDIS_REQ: + CFGP2P_DBG(("%s P2P Provision Discovery Request Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_PAF_PROVDIS_RSP: + CFGP2P_DBG(("%s P2P Provision Discovery Response Frame\n", + (tx)? "TX": "RX")); + break; + default: + CFGP2P_DBG(("%s Unknown P2P Public Action Frame\n", + (tx)? "TX": "RX")); + + } + + } else if (wl_cfgp2p_is_p2p_action(frame, frame_len)) { + act_frm = (wifi_p2p_action_frame_t *)frame; + switch (act_frm->subtype) { + case P2P_AF_NOTICE_OF_ABSENCE: + CFGP2P_DBG(("%s P2P Notice of Absence Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_AF_PRESENCE_REQ: + CFGP2P_DBG(("%s P2P Presence Request Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_AF_PRESENCE_RSP: + CFGP2P_DBG(("%s P2P Presence Response Frame\n", + (tx)? "TX": "RX")); + break; + case P2P_AF_GO_DISC_REQ: + CFGP2P_DBG(("%s P2P Discoverability Request Frame\n", + (tx)? "TX": "RX")); + break; + default: + CFGP2P_DBG(("%s Unknown P2P Action Frame\n", + (tx)? "TX": "RX")); + } + + } else if (wl_cfgp2p_is_gas_action(frame, frame_len)) { + sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *)frame; + switch (sd_act_frm->action) { + case P2PSD_ACTION_ID_GAS_IREQ: + CFGP2P_DBG(("%s P2P GAS Initial Request\n", + (tx)? "TX" : "RX")); + break; + case P2PSD_ACTION_ID_GAS_IRESP: + CFGP2P_DBG(("%s P2P GAS Initial Response\n", + (tx)? "TX" : "RX")); + break; + case P2PSD_ACTION_ID_GAS_CREQ: + CFGP2P_DBG(("%s P2P GAS Comback Request\n", + (tx)? "TX" : "RX")); + break; + case P2PSD_ACTION_ID_GAS_CRESP: + CFGP2P_DBG(("%s P2P GAS Comback Response\n", + (tx)? "TX" : "RX")); + break; + default: + CFGP2P_DBG(("%s Unknown P2P GAS Frame\n", + (tx)? "TX" : "RX")); + } + } +} + +/* + * Initialize variables related to P2P + * + */ +s32 +wl_cfgp2p_init_priv(struct wl_priv *wl) +{ + if (!(wl->p2p = kzalloc(sizeof(struct p2p_info), GFP_KERNEL))) { + CFGP2P_ERR(("struct p2p_info allocation failed\n")); + return -ENOMEM; + } +#define INIT_IE(IE_TYPE, BSS_TYPE) \ + do { \ + memset(wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie, 0, \ + sizeof(wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie)); \ + wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie_len = 0; \ + } while (0); + + INIT_IE(probe_req, P2PAPI_BSSCFG_PRIMARY); + INIT_IE(probe_res, P2PAPI_BSSCFG_PRIMARY); + INIT_IE(assoc_req, P2PAPI_BSSCFG_PRIMARY); + INIT_IE(assoc_res, P2PAPI_BSSCFG_PRIMARY); + INIT_IE(beacon, P2PAPI_BSSCFG_PRIMARY); + INIT_IE(probe_req, P2PAPI_BSSCFG_DEVICE); + INIT_IE(probe_res, P2PAPI_BSSCFG_DEVICE); + INIT_IE(assoc_req, P2PAPI_BSSCFG_DEVICE); + INIT_IE(assoc_res, P2PAPI_BSSCFG_DEVICE); + INIT_IE(beacon, P2PAPI_BSSCFG_DEVICE); + INIT_IE(probe_req, P2PAPI_BSSCFG_CONNECTION); + INIT_IE(probe_res, P2PAPI_BSSCFG_CONNECTION); + INIT_IE(assoc_req, P2PAPI_BSSCFG_CONNECTION); + INIT_IE(assoc_res, P2PAPI_BSSCFG_CONNECTION); + INIT_IE(beacon, P2PAPI_BSSCFG_CONNECTION); +#undef INIT_IE + wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY) = wl_to_prmry_ndev(wl); + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_PRIMARY) = 0; + wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE) = NULL; + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = 0; + wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION) = NULL; + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION) = 0; + return BCME_OK; + +} +/* + * Deinitialize variables related to P2P + * + */ +void +wl_cfgp2p_deinit_priv(struct wl_priv *wl) +{ + CFGP2P_DBG(("In\n")); + + if (wl->p2p) { + kfree(wl->p2p); + wl->p2p = NULL; + } + wl->p2p_supported = 0; +} +/* + * Set P2P functions into firmware + */ +s32 +wl_cfgp2p_set_firm_p2p(struct wl_priv *wl) +{ + struct net_device *ndev = wl_to_prmry_ndev(wl); + struct ether_addr null_eth_addr = { { 0, 0, 0, 0, 0, 0 } }; + s32 ret = BCME_OK; + s32 val = 0; + /* Do we have to check whether APSTA is enabled or not ? */ + wldev_iovar_getint(ndev, "apsta", &val); + if (val == 0) { + val = 1; + wldev_ioctl(ndev, WLC_DOWN, &val, sizeof(s32), true); + wldev_iovar_setint(ndev, "apsta", val); + wldev_ioctl(ndev, WLC_UP, &val, sizeof(s32), true); + } + val = 1; + /* Disable firmware roaming for P2P */ + wldev_iovar_setint(ndev, "roam_off", val); + /* In case of COB type, firmware has default mac address + * After Initializing firmware, we have to set current mac address to + * firmware for P2P device address + */ + ret = wldev_iovar_setbuf_bsscfg(ndev, "p2p_da_override", &null_eth_addr, + sizeof(null_eth_addr), wl->ioctl_buf, WLC_IOCTL_MAXLEN, 0, &wl->ioctl_buf_sync); + if (ret && ret != BCME_UNSUPPORTED) { + CFGP2P_ERR(("failed to update device address ret %d\n", ret)); + } + return ret; +} + +/* Create a new P2P BSS. + * Parameters: + * @mac : MAC address of the BSS to create + * @if_type : interface type: WL_P2P_IF_GO or WL_P2P_IF_CLIENT + * @chspec : chspec to use if creating a GO BSS. + * Returns 0 if success. + */ +s32 +wl_cfgp2p_ifadd(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, + chanspec_t chspec) +{ + wl_p2p_if_t ifreq; + s32 err; + struct net_device *ndev = wl_to_prmry_ndev(wl); + u32 scb_timeout = WL_SCB_TIMEOUT; + + ifreq.type = if_type; + ifreq.chspec = chspec; + memcpy(ifreq.addr.octet, mac->octet, sizeof(ifreq.addr.octet)); + + CFGP2P_DBG(("---wl p2p_ifadd %02x:%02x:%02x:%02x:%02x:%02x %s %u\n", + ifreq.addr.octet[0], ifreq.addr.octet[1], ifreq.addr.octet[2], + ifreq.addr.octet[3], ifreq.addr.octet[4], ifreq.addr.octet[5], + (if_type == WL_P2P_IF_GO) ? "go" : "client", + (chspec & WL_CHANSPEC_CHAN_MASK) >> WL_CHANSPEC_CHAN_SHIFT)); + + err = wldev_iovar_setbuf(ndev, "p2p_ifadd", &ifreq, sizeof(ifreq), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + + if (unlikely(err < 0)) { + printk("'wl p2p_ifadd' error %d\n", err); + } else if (if_type == WL_P2P_IF_GO) { + err = wldev_ioctl(ndev, WLC_SET_SCB_TIMEOUT, &scb_timeout, sizeof(u32), true); + if (unlikely(err < 0)) + printk("'wl scb_timeout' error %d\n", err); + } + + return err; +} + +/* Delete a P2P BSS. + * Parameters: + * @mac : MAC address of the BSS to create + * Returns 0 if success. + */ +s32 +wl_cfgp2p_ifdel(struct wl_priv *wl, struct ether_addr *mac) +{ + s32 ret; + struct net_device *netdev = wl_to_prmry_ndev(wl); + + CFGP2P_INFO(("------primary idx %d : wl p2p_ifdel %02x:%02x:%02x:%02x:%02x:%02x\n", + netdev->ifindex, mac->octet[0], mac->octet[1], mac->octet[2], + mac->octet[3], mac->octet[4], mac->octet[5])); + ret = wldev_iovar_setbuf(netdev, "p2p_ifdel", mac, sizeof(*mac), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + if (unlikely(ret < 0)) { + printk("'wl p2p_ifdel' error %d\n", ret); + } + return ret; +} + +/* Change a P2P Role. + * Parameters: + * @mac : MAC address of the BSS to change a role + * Returns 0 if success. + */ +s32 +wl_cfgp2p_ifchange(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, + chanspec_t chspec) +{ + wl_p2p_if_t ifreq; + s32 err; + u32 scb_timeout = WL_SCB_TIMEOUT; + struct net_device *netdev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); + + ifreq.type = if_type; + ifreq.chspec = chspec; + memcpy(ifreq.addr.octet, mac->octet, sizeof(ifreq.addr.octet)); + + CFGP2P_INFO(("---wl p2p_ifchange %02x:%02x:%02x:%02x:%02x:%02x %s %u\n", + ifreq.addr.octet[0], ifreq.addr.octet[1], ifreq.addr.octet[2], + ifreq.addr.octet[3], ifreq.addr.octet[4], ifreq.addr.octet[5], + (if_type == WL_P2P_IF_GO) ? "go" : "client", + (chspec & WL_CHANSPEC_CHAN_MASK) >> WL_CHANSPEC_CHAN_SHIFT)); + + err = wldev_iovar_setbuf(netdev, "p2p_ifupd", &ifreq, sizeof(ifreq), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + + if (unlikely(err < 0)) { + printk("'wl p2p_ifupd' error %d\n", err); + } else if (if_type == WL_P2P_IF_GO) { + err = wldev_ioctl(netdev, WLC_SET_SCB_TIMEOUT, &scb_timeout, sizeof(u32), true); + if (unlikely(err < 0)) + printk("'wl scb_timeout' error %d\n", err); + } + return err; +} + + +/* Get the index of a created P2P BSS. + * Parameters: + * @mac : MAC address of the created BSS + * @index : output: index of created BSS + * Returns 0 if success. + */ +s32 +wl_cfgp2p_ifidx(struct wl_priv *wl, struct ether_addr *mac, s32 *index) +{ + s32 ret; + u8 getbuf[64]; + struct net_device *dev = wl_to_prmry_ndev(wl); + + CFGP2P_INFO(("---wl p2p_if %02x:%02x:%02x:%02x:%02x:%02x\n", + mac->octet[0], mac->octet[1], mac->octet[2], + mac->octet[3], mac->octet[4], mac->octet[5])); + + ret = wldev_iovar_getbuf_bsscfg(dev, "p2p_if", mac, sizeof(*mac), getbuf, + sizeof(getbuf), wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_PRIMARY), NULL); + + if (ret == 0) { + memcpy(index, getbuf, sizeof(index)); + CFGP2P_INFO(("---wl p2p_if ==> %d\n", *index)); + } + + return ret; +} + +static s32 +wl_cfgp2p_set_discovery(struct wl_priv *wl, s32 on) +{ + s32 ret = BCME_OK; + struct net_device *ndev = wl_to_prmry_ndev(wl); + CFGP2P_DBG(("enter\n")); + + ret = wldev_iovar_setint(ndev, "p2p_disc", on); + + if (unlikely(ret < 0)) { + CFGP2P_ERR(("p2p_disc %d error %d\n", on, ret)); + } + + return ret; +} + +/* Set the WL driver's P2P mode. + * Parameters : + * @mode : is one of WL_P2P_DISC_ST_{SCAN,LISTEN,SEARCH}. + * @channel : the channel to listen + * @listen_ms : the time (milli seconds) to wait + * @bssidx : bss index for BSSCFG + * Returns 0 if success + */ + +s32 +wl_cfgp2p_set_p2p_mode(struct wl_priv *wl, u8 mode, u32 channel, u16 listen_ms, int bssidx) +{ + wl_p2p_disc_st_t discovery_mode; + s32 ret; + struct net_device *dev; + CFGP2P_DBG(("enter\n")); + + if (unlikely(bssidx >= P2PAPI_BSSCFG_MAX)) { + CFGP2P_ERR((" %d index out of range\n", bssidx)); + return -1; + } + + dev = wl_to_p2p_bss_ndev(wl, bssidx); + if (unlikely(dev == NULL)) { + CFGP2P_ERR(("bssidx %d is not assigned\n", bssidx)); + return BCME_NOTFOUND; + } + + /* Put the WL driver into P2P Listen Mode to respond to P2P probe reqs */ + discovery_mode.state = mode; + discovery_mode.chspec = CH20MHZ_CHSPEC(channel); + discovery_mode.dwell = listen_ms; + ret = wldev_iovar_setbuf_bsscfg(dev, "p2p_state", &discovery_mode, + sizeof(discovery_mode), wl->ioctl_buf, WLC_IOCTL_MAXLEN, + bssidx, &wl->ioctl_buf_sync); + + return ret; +} + +/* Get the index of the P2P Discovery BSS */ +static s32 +wl_cfgp2p_get_disc_idx(struct wl_priv *wl, s32 *index) +{ + s32 ret; + struct net_device *dev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); + + ret = wldev_iovar_getint(dev, "p2p_dev", index); + CFGP2P_INFO(("p2p_dev bsscfg_idx=%d ret=%d\n", *index, ret)); + + if (unlikely(ret < 0)) { + CFGP2P_ERR(("'p2p_dev' error %d\n", ret)); + return ret; + } + return ret; +} + +s32 +wl_cfgp2p_init_discovery(struct wl_priv *wl) +{ + + s32 index = 0; + s32 ret = BCME_OK; + + CFGP2P_DBG(("enter\n")); + + if (wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) != 0) { + CFGP2P_ERR(("do nothing, already initialized\n")); + return ret; + } + + ret = wl_cfgp2p_set_discovery(wl, 1); + if (ret < 0) { + CFGP2P_ERR(("set discover error\n")); + return ret; + } + /* Enable P2P Discovery in the WL Driver */ + ret = wl_cfgp2p_get_disc_idx(wl, &index); + + if (ret < 0) { + return ret; + } + wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE) = + wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = index; + + /* Set the initial discovery state to SCAN */ + ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + + if (unlikely(ret != 0)) { + CFGP2P_ERR(("unable to set WL_P2P_DISC_ST_SCAN\n")); + wl_cfgp2p_set_discovery(wl, 0); + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = 0; + wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE) = NULL; + return 0; + } + return ret; +} + +/* Deinitialize P2P Discovery + * Parameters : + * @wl : wl_private data + * Returns 0 if succes + */ +static s32 +wl_cfgp2p_deinit_discovery(struct wl_priv *wl) +{ + s32 ret = BCME_OK; + CFGP2P_DBG(("enter\n")); + + if (wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) == 0) { + CFGP2P_ERR(("do nothing, not initialized\n")); + return -1; + } + /* Set the discovery state to SCAN */ + ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + /* Disable P2P discovery in the WL driver (deletes the discovery BSSCFG) */ + ret = wl_cfgp2p_set_discovery(wl, 0); + + /* Clear our saved WPS and P2P IEs for the discovery BSS. The driver + * deleted these IEs when wl_cfgp2p_set_discovery() deleted the discovery + * BSS. + */ + + /* Clear the saved bsscfg index of the discovery BSSCFG to indicate we + * have no discovery BSS. + */ + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = 0; + wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE) = NULL; + + return ret; + +} +/* Enable P2P Discovery + * Parameters: + * @wl : wl_private data + * @ie : probe request ie (WPS IE + P2P IE) + * @ie_len : probe request ie length + * Returns 0 if success. + */ +s32 +wl_cfgp2p_enable_discovery(struct wl_priv *wl, struct net_device *dev, + const u8 *ie, u32 ie_len) +{ + s32 ret = BCME_OK; + if (wl_get_p2p_status(wl, DISCOVERY_ON)) { + CFGP2P_INFO((" DISCOVERY is already initialized, we have nothing to do\n")); + goto set_ie; + } + + wl_set_p2p_status(wl, DISCOVERY_ON); + + CFGP2P_DBG(("enter\n")); + + ret = wl_cfgp2p_init_discovery(wl); + if (unlikely(ret < 0)) { + CFGP2P_ERR((" init discovery error %d\n", ret)); + goto exit; + } + /* Set wsec to any non-zero value in the discovery bsscfg to ensure our + * P2P probe responses have the privacy bit set in the 802.11 WPA IE. + * Some peer devices may not initiate WPS with us if this bit is not set. + */ + ret = wldev_iovar_setint_bsscfg(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE), + "wsec", AES_ENABLED, wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + if (unlikely(ret < 0)) { + CFGP2P_ERR((" wsec error %d\n", ret)); + } +set_ie: + ret = wl_cfgp2p_set_management_ie(wl, dev, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE), + VNDR_IE_PRBREQ_FLAG, ie, ie_len); + + if (unlikely(ret < 0)) { + CFGP2P_ERR(("set probreq ie occurs error %d\n", ret)); + goto exit; + } +exit: + return ret; +} + +/* Disable P2P Discovery + * Parameters: + * @wl : wl_private_data + * Returns 0 if success. + */ +s32 +wl_cfgp2p_disable_discovery(struct wl_priv *wl) +{ + s32 ret = BCME_OK; + CFGP2P_DBG((" enter\n")); + wl_clr_p2p_status(wl, DISCOVERY_ON); + + if (wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) == 0) { + CFGP2P_ERR((" do nothing, not initialized\n")); + goto exit; + } + + ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + + if (unlikely(ret < 0)) { + + CFGP2P_ERR(("unable to set WL_P2P_DISC_ST_SCAN\n")); + } + /* Do a scan abort to stop the driver's scan engine in case it is still + * waiting out an action frame tx dwell time. + */ +#ifdef NOT_YET + if (wl_get_p2p_status(wl, SCANNING)) { + p2pwlu_scan_abort(hdl, FALSE); + } +#endif + wl_clr_p2p_status(wl, DISCOVERY_ON); + ret = wl_cfgp2p_deinit_discovery(wl); + +exit: + return ret; +} + +s32 +wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, + u32 num_chans, u16 *channels, + s32 search_state, u16 action, u32 bssidx) +{ + s32 ret = BCME_OK; + s32 memsize; + s32 eparams_size; + u32 i; + s8 *memblk; + wl_p2p_scan_t *p2p_params; + wl_escan_params_t *eparams; + wlc_ssid_t ssid; + /* Scan parameters */ +#define P2PAPI_SCAN_NPROBES 1 +#define P2PAPI_SCAN_DWELL_TIME_MS 50 +#define P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS 40 +#define P2PAPI_SCAN_HOME_TIME_MS 60 + struct net_device *pri_dev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_PRIMARY); + wl_set_p2p_status(wl, SCANNING); + /* Allocate scan params which need space for 3 channels and 0 ssids */ + eparams_size = (WL_SCAN_PARAMS_FIXED_SIZE + + OFFSETOF(wl_escan_params_t, params)) + + num_chans * sizeof(eparams->params.channel_list[0]); + + memsize = sizeof(wl_p2p_scan_t) + eparams_size; + memblk = scanparambuf; + if (memsize > sizeof(scanparambuf)) { + CFGP2P_ERR((" scanpar buf too small (%u > %u)\n", + memsize, sizeof(scanparambuf))); + return -1; + } + memset(memblk, 0, memsize); + memset(wl->ioctl_buf, 0, WLC_IOCTL_MAXLEN); + if (search_state == WL_P2P_DISC_ST_SEARCH) { + /* + * If we in SEARCH STATE, we don't need to set SSID explictly + * because dongle use P2P WILDCARD internally by default + */ + wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SEARCH, 0, 0, bssidx); + ssid.SSID_len = htod32(0); + + } else if (search_state == WL_P2P_DISC_ST_SCAN) { + /* SCAN STATE 802.11 SCAN + * WFD Supplicant has p2p_find command with (type=progressive, type= full) + * So if P2P_find command with type=progressive, + * we have to set ssid to P2P WILDCARD because + * we just do broadcast scan unless setting SSID + */ + strcpy(ssid.SSID, WL_P2P_WILDCARD_SSID); + ssid.SSID_len = htod32(WL_P2P_WILDCARD_SSID_LEN); + wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, bssidx); + } + + + /* Fill in the P2P scan structure at the start of the iovar param block */ + p2p_params = (wl_p2p_scan_t*) memblk; + p2p_params->type = 'E'; + /* Fill in the Scan structure that follows the P2P scan structure */ + eparams = (wl_escan_params_t*) (p2p_params + 1); + eparams->params.bss_type = DOT11_BSSTYPE_ANY; + if (active) + eparams->params.scan_type = DOT11_SCANTYPE_ACTIVE; + else + eparams->params.scan_type = DOT11_SCANTYPE_PASSIVE; + + memcpy(&eparams->params.bssid, ðer_bcast, ETHER_ADDR_LEN); + if (ssid.SSID_len) + memcpy(&eparams->params.ssid, &ssid, sizeof(wlc_ssid_t)); + + eparams->params.nprobes = htod32(P2PAPI_SCAN_NPROBES); + eparams->params.home_time = htod32(P2PAPI_SCAN_HOME_TIME_MS); + if (wl_get_drv_status_all(wl, CONNECTED)) + eparams->params.active_time = htod32(-1); + else if (num_chans == 3) + eparams->params.active_time = htod32(P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS); + else + eparams->params.active_time = htod32(P2PAPI_SCAN_DWELL_TIME_MS); + eparams->params.passive_time = htod32(-1); + eparams->params.channel_num = htod32((0 << WL_SCAN_PARAMS_NSSID_SHIFT) | + (num_chans & WL_SCAN_PARAMS_COUNT_MASK)); + + for (i = 0; i < num_chans; i++) { + eparams->params.channel_list[i] = htodchanspec(channels[i]); + } + eparams->version = htod32(ESCAN_REQ_VERSION); + eparams->action = htod16(action); + eparams->sync_id = htod16(0x1234); + CFGP2P_INFO(("SCAN CHANNELS : ")); + + for (i = 0; i < num_chans; i++) { + if (i == 0) CFGP2P_INFO(("%d", channels[i])); + else CFGP2P_INFO((",%d", channels[i])); + } + + CFGP2P_INFO(("\n")); + + ret = wldev_iovar_setbuf_bsscfg(pri_dev, "p2p_scan", + memblk, memsize, wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); + return ret; +} + +/* search function to reach at common channel to send action frame + * Parameters: + * @wl : wl_private data + * @ndev : net device for bssidx + * @bssidx : bssidx for BSS + * Returns 0 if success. + */ +s32 +wl_cfgp2p_act_frm_search(struct wl_priv *wl, struct net_device *ndev, + s32 bssidx, s32 channel) +{ + s32 ret = 0; + u32 chan_cnt = 0; + u16 *default_chan_list = NULL; + if (!p2p_is_on(wl)) + return -BCME_ERROR; + CFGP2P_ERR((" Enter\n")); + if (bssidx == P2PAPI_BSSCFG_PRIMARY) + bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); + if (channel) + chan_cnt = 1; + else + chan_cnt = SOCIAL_CHAN_CNT; + default_chan_list = kzalloc(chan_cnt * sizeof(*default_chan_list), GFP_KERNEL); + if (default_chan_list == NULL) { + CFGP2P_ERR(("channel list allocation failed \n")); + ret = -ENOMEM; + goto exit; + } + if (channel) { + default_chan_list[0] = channel; + } else { + default_chan_list[0] = SOCIAL_CHAN_1; + default_chan_list[1] = SOCIAL_CHAN_2; + default_chan_list[2] = SOCIAL_CHAN_3; + } + ret = wl_cfgp2p_escan(wl, ndev, true, SOCIAL_CHAN_CNT, + default_chan_list, WL_P2P_DISC_ST_SEARCH, + WL_SCAN_ACTION_START, bssidx); + kfree(default_chan_list); +exit: + return ret; +} + +/* Check whether pointed-to IE looks like WPA. */ +#define wl_cfgp2p_is_wpa_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ + (const uint8 *)WPS_OUI, WPS_OUI_LEN, WPA_OUI_TYPE) +/* Check whether pointed-to IE looks like WPS. */ +#define wl_cfgp2p_is_wps_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ + (const uint8 *)WPS_OUI, WPS_OUI_LEN, WPS_OUI_TYPE) +/* Check whether the given IE looks like WFA P2P IE. */ +#define wl_cfgp2p_is_p2p_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ + (const uint8 *)WFA_OUI, WFA_OUI_LEN, WFA_OUI_TYPE_P2P) +/* Delete and Set a management vndr ie to firmware + * Parameters: + * @wl : wl_private data + * @ndev : net device for bssidx + * @bssidx : bssidx for BSS + * @pktflag : packet flag for IE (VNDR_IE_PRBREQ_FLAG,VNDR_IE_PRBRSP_FLAG, VNDR_IE_ASSOCRSP_FLAG, + * VNDR_IE_ASSOCREQ_FLAG) + * @ie : VNDR IE (such as P2P IE , WPS IE) + * @ie_len : VNDR IE Length + * Returns 0 if success. + */ + +s32 +wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, + s32 pktflag, const u8 *vndr_ie, u32 vndr_ie_len) +{ + /* Vendor-specific Information Element ID */ +#define VNDR_SPEC_ELEMENT_ID 0xdd + s32 ret = BCME_OK; + u32 pos; + u8 *ie_buf; + u8 *mgmt_ie_buf = NULL; + u32 mgmt_ie_buf_len = 0; + u32 *mgmt_ie_len = 0; + u8 ie_id, ie_len; + u8 delete = 0; +#define IE_TYPE(type, bsstype) (wl_to_p2p_bss_saved_ie(wl, bsstype).p2p_ ## type ## _ie) +#define IE_TYPE_LEN(type, bsstype) (wl_to_p2p_bss_saved_ie(wl, bsstype).p2p_ ## type ## _ie_len) + if (p2p_is_on(wl) && bssidx != -1) { + if (bssidx == P2PAPI_BSSCFG_PRIMARY) + bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); + switch (pktflag) { + case VNDR_IE_PRBREQ_FLAG : + mgmt_ie_buf = IE_TYPE(probe_req, bssidx); + mgmt_ie_len = &IE_TYPE_LEN(probe_req, bssidx); + mgmt_ie_buf_len = sizeof(IE_TYPE(probe_req, bssidx)); + break; + case VNDR_IE_PRBRSP_FLAG : + mgmt_ie_buf = IE_TYPE(probe_res, bssidx); + mgmt_ie_len = &IE_TYPE_LEN(probe_res, bssidx); + mgmt_ie_buf_len = sizeof(IE_TYPE(probe_res, bssidx)); + break; + case VNDR_IE_ASSOCREQ_FLAG : + mgmt_ie_buf = IE_TYPE(assoc_req, bssidx); + mgmt_ie_len = &IE_TYPE_LEN(assoc_req, bssidx); + mgmt_ie_buf_len = sizeof(IE_TYPE(assoc_req, bssidx)); + break; + case VNDR_IE_ASSOCRSP_FLAG : + mgmt_ie_buf = IE_TYPE(assoc_res, bssidx); + mgmt_ie_len = &IE_TYPE_LEN(assoc_res, bssidx); + mgmt_ie_buf_len = sizeof(IE_TYPE(assoc_res, bssidx)); + break; + case VNDR_IE_BEACON_FLAG : + mgmt_ie_buf = IE_TYPE(beacon, bssidx); + mgmt_ie_len = &IE_TYPE_LEN(beacon, bssidx); + mgmt_ie_buf_len = sizeof(IE_TYPE(beacon, bssidx)); + break; + default: + mgmt_ie_buf = NULL; + mgmt_ie_len = NULL; + CFGP2P_ERR(("not suitable type\n")); + return -1; + } + } else if (wl_get_mode_by_netdev(wl, ndev) == WL_MODE_AP) { + switch (pktflag) { + case VNDR_IE_PRBRSP_FLAG : + mgmt_ie_buf = wl->ap_info->probe_res_ie; + mgmt_ie_len = &wl->ap_info->probe_res_ie_len; + mgmt_ie_buf_len = sizeof(wl->ap_info->probe_res_ie); + break; + case VNDR_IE_BEACON_FLAG : + mgmt_ie_buf = wl->ap_info->beacon_ie; + mgmt_ie_len = &wl->ap_info->beacon_ie_len; + mgmt_ie_buf_len = sizeof(wl->ap_info->beacon_ie); + break; + default: + mgmt_ie_buf = NULL; + mgmt_ie_len = NULL; + CFGP2P_ERR(("not suitable type\n")); + return -1; + } + bssidx = 0; + } else if (bssidx == -1 && wl_get_mode_by_netdev(wl, ndev) == WL_MODE_BSS) { + switch (pktflag) { + case VNDR_IE_PRBREQ_FLAG : + mgmt_ie_buf = wl->sta_info->probe_req_ie; + mgmt_ie_len = &wl->sta_info->probe_req_ie_len; + mgmt_ie_buf_len = sizeof(wl->sta_info->probe_req_ie); + break; + case VNDR_IE_ASSOCREQ_FLAG : + mgmt_ie_buf = wl->sta_info->assoc_req_ie; + mgmt_ie_len = &wl->sta_info->assoc_req_ie_len; + mgmt_ie_buf_len = sizeof(wl->sta_info->assoc_req_ie); + break; + default: + mgmt_ie_buf = NULL; + mgmt_ie_len = NULL; + CFGP2P_ERR(("not suitable type\n")); + return -1; + } + bssidx = 0; + } else { + CFGP2P_ERR(("not suitable type\n")); + return -1; + } + + if (vndr_ie_len > mgmt_ie_buf_len) { + CFGP2P_ERR(("extra IE size too big\n")); + ret = -ENOMEM; + } else { + if (mgmt_ie_buf != NULL) { + if (vndr_ie_len && (vndr_ie_len == *mgmt_ie_len) && + (memcmp(mgmt_ie_buf, vndr_ie, vndr_ie_len) == 0)) { + CFGP2P_INFO(("Previous mgmt IE is equals to current IE")); + goto exit; + } + pos = 0; + delete = 1; + ie_buf = (u8 *) mgmt_ie_buf; + while (pos < *mgmt_ie_len) { + ie_id = ie_buf[pos++]; + ie_len = ie_buf[pos++]; + if ((ie_id == DOT11_MNG_VS_ID) && + (wl_cfgp2p_is_wps_ie(&ie_buf[pos-2], NULL, 0) || + wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0))) { + CFGP2P_INFO(("DELELED ID : %d, Len : %d , OUI :" + "%02x:%02x:%02x\n", ie_id, ie_len, ie_buf[pos], + ie_buf[pos+1], ie_buf[pos+2])); + ret = wl_cfgp2p_vndr_ie(wl, ndev, bssidx, pktflag, + ie_buf+pos, VNDR_SPEC_ELEMENT_ID, ie_buf+pos+3, + ie_len-3, delete); + } + pos += ie_len; + } + + } + *mgmt_ie_len = 0; + /* Add if there is any extra IE */ + if (vndr_ie && vndr_ie_len) { + /* save the current IE in wl struct */ + memcpy(mgmt_ie_buf, vndr_ie, vndr_ie_len); + *mgmt_ie_len = vndr_ie_len; + pos = 0; + ie_buf = (u8 *) vndr_ie; + delete = 0; + while (pos < vndr_ie_len) { + ie_id = ie_buf[pos++]; + ie_len = ie_buf[pos++]; + if ((ie_id == DOT11_MNG_VS_ID) && + (wl_cfgp2p_is_wps_ie(&ie_buf[pos-2], NULL, 0) || + wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0))) { + CFGP2P_INFO(("ADDED ID : %d, Len : %d , OUI :" + "%02x:%02x:%02x\n", ie_id, ie_len, ie_buf[pos], + ie_buf[pos+1], ie_buf[pos+2])); + ret = wl_cfgp2p_vndr_ie(wl, ndev, bssidx, pktflag, + ie_buf+pos, VNDR_SPEC_ELEMENT_ID, ie_buf+pos+3, + ie_len-3, delete); + } + pos += ie_len; + } + } + } +#undef IE_TYPE +#undef IE_TYPE_LEN +exit: + return ret; +} + +/* Clear the manament IE buffer of BSSCFG + * Parameters: + * @wl : wl_private data + * @bssidx : bssidx for BSS + * + * Returns 0 if success. + */ +s32 +wl_cfgp2p_clear_management_ie(struct wl_priv *wl, s32 bssidx) +{ +#define INIT_IE(IE_TYPE, BSS_TYPE) \ + do { \ + memset(wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie, 0, \ + sizeof(wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie)); \ + wl_to_p2p_bss_saved_ie(wl, BSS_TYPE).p2p_ ## IE_TYPE ## _ie_len = 0; \ + } while (0); + if (bssidx < 0) { + CFGP2P_ERR(("invalid bssidx\n")); + return BCME_BADARG; + } + INIT_IE(probe_req, bssidx); + INIT_IE(probe_res, bssidx); + INIT_IE(assoc_req, bssidx); + INIT_IE(assoc_res, bssidx); + INIT_IE(beacon, bssidx); + return BCME_OK; +} + + +/* Is any of the tlvs the expected entry? If + * not update the tlvs buffer pointer/length. + */ +static bool +wl_cfgp2p_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, const u8 *oui, u32 oui_len, u8 type) +{ + /* If the contents match the OUI and the type */ + if (ie[TLV_LEN_OFF] >= oui_len + 1 && + !bcmp(&ie[TLV_BODY_OFF], oui, oui_len) && + type == ie[TLV_BODY_OFF + oui_len]) { + return TRUE; + } + + if (tlvs == NULL) + return FALSE; + /* point to the next ie */ + ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN; + /* calculate the length of the rest of the buffer */ + *tlvs_len -= (int)(ie - *tlvs); + /* update the pointer to the start of the buffer */ + *tlvs = ie; + + return FALSE; +} + +wpa_ie_fixed_t * +wl_cfgp2p_find_wpaie(u8 *parse, u32 len) +{ + bcm_tlv_t *ie; + + while ((ie = bcm_parse_tlvs(parse, (u32)len, DOT11_MNG_VS_ID))) { + if (wl_cfgp2p_is_wpa_ie((u8*)ie, &parse, &len)) { + return (wpa_ie_fixed_t *)ie; + } + } + return NULL; +} + +wpa_ie_fixed_t * +wl_cfgp2p_find_wpsie(u8 *parse, u32 len) +{ + bcm_tlv_t *ie; + + while ((ie = bcm_parse_tlvs(parse, (u32)len, DOT11_MNG_VS_ID))) { + if (wl_cfgp2p_is_wps_ie((u8*)ie, &parse, &len)) { + return (wpa_ie_fixed_t *)ie; + } + } + return NULL; +} + +wifi_p2p_ie_t * +wl_cfgp2p_find_p2pie(u8 *parse, u32 len) +{ + bcm_tlv_t *ie; + + while ((ie = bcm_parse_tlvs(parse, (int)len, DOT11_MNG_VS_ID))) { + if (wl_cfgp2p_is_p2p_ie((uint8*)ie, &parse, &len)) { + return (wifi_p2p_ie_t *)ie; + } + } + return NULL; +} + +static s32 +wl_cfgp2p_vndr_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, s32 pktflag, + s8 *oui, s32 ie_id, s8 *data, s32 data_len, s32 delete) +{ + s32 err = BCME_OK; + s32 buf_len; + s32 iecount; + + vndr_ie_setbuf_t *ie_setbuf; + + /* Validate the pktflag parameter */ + if ((pktflag & ~(VNDR_IE_BEACON_FLAG | VNDR_IE_PRBRSP_FLAG | + VNDR_IE_ASSOCRSP_FLAG | VNDR_IE_AUTHRSP_FLAG | + VNDR_IE_PRBREQ_FLAG | VNDR_IE_ASSOCREQ_FLAG))) { + CFGP2P_ERR(("p2pwl_vndr_ie: Invalid packet flag 0x%x\n", pktflag)); + return -1; + } + + buf_len = sizeof(vndr_ie_setbuf_t) + data_len - 1; + ie_setbuf = (vndr_ie_setbuf_t *) kzalloc(buf_len, GFP_KERNEL); + + CFGP2P_INFO((" ie_id : %02x, data length : %d\n", ie_id, data_len)); + if (!ie_setbuf) { + + CFGP2P_ERR(("Error allocating buffer for IE\n")); + return -ENOMEM; + } + if (delete) + strcpy(ie_setbuf->cmd, "del"); + else + strcpy(ie_setbuf->cmd, "add"); + /* Buffer contains only 1 IE */ + iecount = htod32(1); + memcpy((void *)&ie_setbuf->vndr_ie_buffer.iecount, &iecount, sizeof(int)); + pktflag = htod32(pktflag); + memcpy((void *)&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].pktflag, + &pktflag, sizeof(uint32)); + ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.id = ie_id; + ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.len + = (uchar)(data_len + VNDR_IE_MIN_LEN); + memcpy(ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui, oui, 3); + memcpy(ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.data, data, data_len); + err = wldev_iovar_setbuf_bsscfg(ndev, "vndr_ie", ie_setbuf, buf_len, + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); + + CFGP2P_INFO(("vndr_ie iovar returns %d\n", err)); + kfree(ie_setbuf); + return err; +} + +/* + * Search the bssidx based on dev argument + * Parameters: + * @wl : wl_private data + * @ndev : net device to search bssidx + * Returns bssidx for ndev + */ +s32 +wl_cfgp2p_find_idx(struct wl_priv *wl, struct net_device *ndev) +{ + u32 i; + s32 index = -1; + + if (ndev == NULL) { + CFGP2P_ERR((" ndev is NULL\n")); + goto exit; + } + if (!wl->p2p_supported) { + return P2PAPI_BSSCFG_PRIMARY; + } + for (i = 0; i < P2PAPI_BSSCFG_MAX; i++) { + if (ndev == wl_to_p2p_bss_ndev(wl, i)) { + index = wl_to_p2p_bss_bssidx(wl, i); + break; + } + } + if (index == -1) + return P2PAPI_BSSCFG_PRIMARY; +exit: + return index; +} +/* + * Callback function for WLC_E_P2P_DISC_LISTEN_COMPLETE + */ +s32 +wl_cfgp2p_listen_complete(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + s32 ret = BCME_OK; + + CFGP2P_DBG((" Enter\n")); + + /* If p2p_info is de-initialized, do nothing */ + if (!wl->p2p) + return ret; + + if (wl_get_p2p_status(wl, LISTEN_EXPIRED) == 0) { + wl_set_p2p_status(wl, LISTEN_EXPIRED); + if (timer_pending(&wl->p2p->listen_timer)) { + del_timer_sync(&wl->p2p->listen_timer); + } + cfg80211_remain_on_channel_expired(ndev, wl->last_roc_id, &wl->remain_on_chan, + wl->remain_on_chan_type, GFP_KERNEL); + } else + wl_clr_p2p_status(wl, LISTEN_EXPIRED); + + return ret; + +} + +/* + * Timer expire callback function for LISTEN + * We can't report cfg80211_remain_on_channel_expired from Timer ISR context, + * so lets do it from thread context. + */ +static void +wl_cfgp2p_listen_expired(unsigned long data) +{ + wl_event_msg_t msg; + struct wl_priv *wl = (struct wl_priv *) data; + + CFGP2P_DBG((" Enter\n")); + memset(&msg, 0, sizeof(wl_event_msg_t)); + msg.event_type = hton32(WLC_E_P2P_DISC_LISTEN_COMPLETE); + wl_cfg80211_event(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE), &msg, NULL); +} + +/* + * Routine for cancelling the P2P LISTEN + */ +static s32 +wl_cfgp2p_cancel_listen(struct wl_priv *wl, struct net_device *ndev, + bool notify) +{ + WL_DBG(("Enter \n")); + + /* Irrespective of whether timer is running or not, reset + * the LISTEN state. + */ + wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + + if (timer_pending(&wl->p2p->listen_timer)) { + del_timer_sync(&wl->p2p->listen_timer); + + if (notify) + cfg80211_remain_on_channel_expired(ndev, wl->last_roc_id, + &wl->remain_on_chan, wl->remain_on_chan_type, GFP_KERNEL); + } + + + return 0; +} + +/* + * Do a P2P Listen on the given channel for the given duration. + * A listen consists of sitting idle and responding to P2P probe requests + * with a P2P probe response. + * + * This fn assumes dongle p2p device discovery is already enabled. + * Parameters : + * @wl : wl_private data + * @channel : channel to listen + * @duration_ms : the time (milli seconds) to wait + */ +s32 +wl_cfgp2p_discover_listen(struct wl_priv *wl, s32 channel, u32 duration_ms) +{ +#define INIT_TIMER(timer, func, duration, extra_delay) \ + do { \ + init_timer(timer); \ + timer->function = func; \ + timer->expires = jiffies + msecs_to_jiffies(duration + extra_delay); \ + timer->data = (unsigned long) wl; \ + add_timer(timer); \ + } while (0); + + s32 ret = BCME_OK; + struct timer_list *_timer; + CFGP2P_DBG((" Enter Channel : %d, Duration : %d\n", channel, duration_ms)); + if (unlikely(wl_get_p2p_status(wl, DISCOVERY_ON) == 0)) { + + CFGP2P_ERR((" Discovery is not set, so we have noting to do\n")); + + ret = BCME_NOTREADY; + goto exit; + } + if (timer_pending(&wl->p2p->listen_timer)) { + CFGP2P_DBG(("previous LISTEN is not completed yet\n")); + goto exit; + + } else + wl_clr_p2p_status(wl, LISTEN_EXPIRED); + + wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_LISTEN, channel, (u16) duration_ms, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + _timer = &wl->p2p->listen_timer; + + /* We will wait to receive WLC_E_P2P_DISC_LISTEN_COMPLETE from dongle , + * otherwise we will wait up to duration_ms + 200ms + */ + INIT_TIMER(_timer, wl_cfgp2p_listen_expired, duration_ms, 200); + +#undef INIT_TIMER +exit: + return ret; +} + + +s32 +wl_cfgp2p_discover_enable_search(struct wl_priv *wl, u8 enable) +{ + s32 ret = BCME_OK; + CFGP2P_DBG((" Enter\n")); + if (!wl_get_p2p_status(wl, DISCOVERY_ON)) { + + CFGP2P_DBG((" do nothing, discovery is off\n")); + return ret; + } + if (wl_get_p2p_status(wl, SEARCH_ENABLED) == enable) { + CFGP2P_DBG(("already : %d\n", enable)); + return ret; + } + + wl_chg_p2p_status(wl, SEARCH_ENABLED); + /* When disabling Search, reset the WL driver's p2p discovery state to + * WL_P2P_DISC_ST_SCAN. + */ + if (!enable) { + wl_clr_p2p_status(wl, SCANNING); + ret = wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + } + + return ret; +} + +/* + * Callback function for WLC_E_ACTION_FRAME_COMPLETE, WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE + */ +s32 +wl_cfgp2p_action_tx_complete(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + s32 ret = BCME_OK; + u32 event_type = ntoh32(e->event_type); + u32 status = ntoh32(e->status); + CFGP2P_DBG((" Enter\n")); + if (event_type == WLC_E_ACTION_FRAME_COMPLETE) { + + CFGP2P_INFO((" WLC_E_ACTION_FRAME_COMPLETE is received : %d\n", status)); + if (status == WLC_E_STATUS_SUCCESS) { + wl_set_p2p_status(wl, ACTION_TX_COMPLETED); + } + else { + wl_set_p2p_status(wl, ACTION_TX_NOACK); + CFGP2P_ERR(("WLC_E_ACTION_FRAME_COMPLETE : NO ACK\n")); + } + } else { + CFGP2P_INFO((" WLC_E_ACTION_FRAME_OFFCHAN_COMPLETE is received," + "status : %d\n", status)); + wake_up_interruptible(&wl->netif_change_event); + } + return ret; +} +/* Send an action frame immediately without doing channel synchronization. + * + * This function does not wait for a completion event before returning. + * The WLC_E_ACTION_FRAME_COMPLETE event will be received when the action + * frame is transmitted. + * The WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE event will be received when an + * 802.11 ack has been received for the sent action frame. + */ +s32 +wl_cfgp2p_tx_action_frame(struct wl_priv *wl, struct net_device *dev, + wl_af_params_t *af_params, s32 bssidx) +{ + s32 ret = BCME_OK; + s32 timeout = 0; + + + CFGP2P_INFO(("\n")); + CFGP2P_INFO(("channel : %u , dwell time : %u\n", + af_params->channel, af_params->dwell_time)); + + wl_clr_p2p_status(wl, ACTION_TX_COMPLETED); + wl_clr_p2p_status(wl, ACTION_TX_NOACK); +#define MAX_WAIT_TIME 2000 + if (bssidx == P2PAPI_BSSCFG_PRIMARY) + bssidx = wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE); + + ret = wldev_iovar_setbuf_bsscfg(dev, "actframe", af_params, sizeof(*af_params), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); + + if (ret < 0) { + CFGP2P_ERR((" sending action frame is failed\n")); + goto exit; + } + timeout = wait_event_interruptible_timeout(wl->netif_change_event, + (wl_get_p2p_status(wl, ACTION_TX_COMPLETED) || wl_get_p2p_status(wl, ACTION_TX_NOACK)), + msecs_to_jiffies(MAX_WAIT_TIME)); + + if (timeout > 0 && wl_get_p2p_status(wl, ACTION_TX_COMPLETED)) { + CFGP2P_INFO(("tx action frame operation is completed\n")); + ret = BCME_OK; + } else { + ret = BCME_ERROR; + CFGP2P_INFO(("tx action frame operation is failed\n")); + } +exit: + CFGP2P_INFO((" via act frame iovar : status = %d\n", ret)); +#undef MAX_WAIT_TIME + return ret; +} + +/* Generate our P2P Device Address and P2P Interface Address from our primary + * MAC address. + */ +void +wl_cfgp2p_generate_bss_mac(struct ether_addr *primary_addr, + struct ether_addr *out_dev_addr, struct ether_addr *out_int_addr) +{ + memset(out_dev_addr, 0, sizeof(*out_dev_addr)); + memset(out_int_addr, 0, sizeof(*out_int_addr)); + + /* Generate the P2P Device Address. This consists of the device's + * primary MAC address with the locally administered bit set. + */ + memcpy(out_dev_addr, primary_addr, sizeof(*out_dev_addr)); + out_dev_addr->octet[0] |= 0x02; + + /* Generate the P2P Interface Address. If the discovery and connection + * BSSCFGs need to simultaneously co-exist, then this address must be + * different from the P2P Device Address. + */ + memcpy(out_int_addr, out_dev_addr, sizeof(*out_int_addr)); + out_int_addr->octet[4] ^= 0x80; + +} + +/* P2P IF Address change to Virtual Interface MAC Address */ +void +wl_cfg80211_change_ifaddr(u8* buf, struct ether_addr *p2p_int_addr, u8 element_id) +{ + wifi_p2p_ie_t *ie = (wifi_p2p_ie_t*) buf; + u16 len = ie->len; + u8 *subel; + u8 subelt_id; + u16 subelt_len; + CFGP2P_DBG((" Enter\n")); + + /* Point subel to the P2P IE's subelt field. + * Subtract the preceding fields (id, len, OUI, oui_type) from the length. + */ + subel = ie->subelts; + len -= 4; /* exclude OUI + OUI_TYPE */ + + while (len >= 3) { + /* attribute id */ + subelt_id = *subel; + subel += 1; + len -= 1; + + /* 2-byte little endian */ + subelt_len = *subel++; + subelt_len |= *subel++ << 8; + + len -= 2; + len -= subelt_len; /* for the remaining subelt fields */ + + if (subelt_id == element_id) { + if (subelt_id == P2P_SEID_INTINTADDR) { + memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN); + CFGP2P_INFO(("Intended P2P Interface Address ATTR FOUND\n")); + } else if (subelt_id == P2P_SEID_DEV_ID) { + memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN); + CFGP2P_INFO(("Device ID ATTR FOUND\n")); + } else if (subelt_id == P2P_SEID_DEV_INFO) { + memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN); + CFGP2P_INFO(("Device INFO ATTR FOUND\n")); + } else if (subelt_id == P2P_SEID_GROUP_ID) { + memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN); + CFGP2P_INFO(("GROUP ID ATTR FOUND\n")); + } return; + } else { + CFGP2P_DBG(("OTHER id : %d\n", subelt_id)); + } + subel += subelt_len; + } +} +/* + * Check if a BSS is up. + * This is a common implementation called by most OSL implementations of + * p2posl_bss_isup(). DO NOT call this function directly from the + * common code -- call p2posl_bss_isup() instead to allow the OSL to + * override the common implementation if necessary. + */ +bool +wl_cfgp2p_bss_isup(struct net_device *ndev, int bsscfg_idx) +{ + s32 result, val; + bool isup = false; + s8 getbuf[64]; + + /* Check if the BSS is up */ + *(int*)getbuf = -1; + result = wldev_iovar_getbuf_bsscfg(ndev, "bss", &bsscfg_idx, + sizeof(bsscfg_idx), getbuf, sizeof(getbuf), 0, NULL); + if (result != 0) { + CFGP2P_ERR(("'wl bss -C %d' failed: %d\n", bsscfg_idx, result)); + CFGP2P_ERR(("NOTE: this ioctl error is normal " + "when the BSS has not been created yet.\n")); + } else { + val = *(int*)getbuf; + val = dtoh32(val); + CFGP2P_INFO(("---wl bss -C %d ==> %d\n", bsscfg_idx, val)); + isup = (val ? TRUE : FALSE); + } + return isup; +} + + +/* Bring up or down a BSS */ +s32 +wl_cfgp2p_bss(struct wl_priv *wl, struct net_device *ndev, s32 bsscfg_idx, s32 up) +{ + s32 ret = BCME_OK; + s32 val = up ? 1 : 0; + + struct { + s32 cfg; + s32 val; + } bss_setbuf; + + bss_setbuf.cfg = htod32(bsscfg_idx); + bss_setbuf.val = htod32(val); + CFGP2P_INFO(("---wl bss -C %d %s\n", bsscfg_idx, up ? "up" : "down")); + ret = wldev_iovar_setbuf(ndev, "bss", &bss_setbuf, sizeof(bss_setbuf), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + + if (ret != 0) { + CFGP2P_ERR(("'bss %d' failed with %d\n", up, ret)); + } + + return ret; +} + +/* Check if 'p2p' is supported in the driver */ +s32 +wl_cfgp2p_supported(struct wl_priv *wl, struct net_device *ndev) +{ + s32 ret = BCME_OK; + s32 p2p_supported = 0; + ret = wldev_iovar_getint(ndev, "p2p", + &p2p_supported); + if (ret < 0) { + CFGP2P_ERR(("wl p2p error %d\n", ret)); + return 0; + } + if (p2p_supported == 1) { + CFGP2P_INFO(("p2p is supported\n")); + } else { + CFGP2P_INFO(("p2p is unsupported\n")); + p2p_supported = 0; + } + return p2p_supported; +} + +/* Cleanup P2P resources */ +s32 +wl_cfgp2p_down(struct wl_priv *wl) +{ + + wl_cfgp2p_cancel_listen(wl, + wl->p2p_net ? wl->p2p_net : wl_to_prmry_ndev(wl), TRUE); + + wl_cfgp2p_deinit_priv(wl); + return 0; +} + +s32 +wl_cfgp2p_set_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int len) +{ + s32 ret = -1; + int count, start, duration; + wl_p2p_sched_t dongle_noa; + + CFGP2P_DBG((" Enter\n")); + + memset(&dongle_noa, 0, sizeof(dongle_noa)); + + if (wl->p2p && wl->p2p->vif_created) { + + wl->p2p->noa.desc[0].start = 0; + + sscanf(buf, "%d %d %d", &count, &start, &duration); + CFGP2P_DBG(("set_p2p_noa count %d start %d duration %d\n", + count, start, duration)); + if (count != -1) + wl->p2p->noa.desc[0].count = count; + + /* supplicant gives interval as start */ + if (start != -1) + wl->p2p->noa.desc[0].interval = start; + + if (duration != -1) + wl->p2p->noa.desc[0].duration = duration; + + if (wl->p2p->noa.desc[0].count != 255) { + wl->p2p->noa.desc[0].start = 200; + dongle_noa.type = WL_P2P_SCHED_TYPE_REQ_ABS; + dongle_noa.action = WL_P2P_SCHED_ACTION_GOOFF; + dongle_noa.option = WL_P2P_SCHED_OPTION_TSFOFS; + } + else { + /* Continuous NoA interval. */ + dongle_noa.action = WL_P2P_SCHED_ACTION_NONE; + dongle_noa.type = WL_P2P_SCHED_TYPE_ABS; + if ((wl->p2p->noa.desc[0].interval == 102) || + (wl->p2p->noa.desc[0].interval == 100)) { + wl->p2p->noa.desc[0].start = 100 - + wl->p2p->noa.desc[0].duration; + dongle_noa.option = WL_P2P_SCHED_OPTION_BCNPCT; + } + else { + dongle_noa.option = WL_P2P_SCHED_OPTION_NORMAL; + } + } + /* Put the noa descriptor in dongle format for dongle */ + dongle_noa.desc[0].count = htod32(wl->p2p->noa.desc[0].count); + if (dongle_noa.option == WL_P2P_SCHED_OPTION_BCNPCT) { + dongle_noa.desc[0].start = htod32(wl->p2p->noa.desc[0].start); + dongle_noa.desc[0].duration = htod32(wl->p2p->noa.desc[0].duration); + } + else { + dongle_noa.desc[0].start = htod32(wl->p2p->noa.desc[0].start*1000); + dongle_noa.desc[0].duration = htod32(wl->p2p->noa.desc[0].duration*1000); + } + dongle_noa.desc[0].interval = htod32(wl->p2p->noa.desc[0].interval*1000); + + ret = wldev_iovar_setbuf(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION), + "p2p_noa", &dongle_noa, sizeof(dongle_noa), wl->ioctl_buf, WLC_IOCTL_MAXLEN, + &wl->ioctl_buf_sync); + + if (ret < 0) { + CFGP2P_ERR(("fw set p2p_noa failed %d\n", ret)); + } + } + else { + CFGP2P_ERR(("ERROR: set_noa in non-p2p mode\n")); + } + return ret; +} + +s32 +wl_cfgp2p_get_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int buf_len) +{ + wifi_p2p_noa_desc_t *noa_desc; + int len = 0, i; + char _buf[200]; + + CFGP2P_DBG((" Enter\n")); + buf[0] = '\0'; + if (wl->p2p && wl->p2p->vif_created) { + if (wl->p2p->noa.desc[0].count || wl->p2p->ops.ops) { + _buf[0] = 1; /* noa index */ + _buf[1] = (wl->p2p->ops.ops ? 0x80: 0) | + (wl->p2p->ops.ctw & 0x7f); /* ops + ctw */ + len += 2; + if (wl->p2p->noa.desc[0].count) { + noa_desc = (wifi_p2p_noa_desc_t*)&_buf[len]; + noa_desc->cnt_type = wl->p2p->noa.desc[0].count; + noa_desc->duration = wl->p2p->noa.desc[0].duration; + noa_desc->interval = wl->p2p->noa.desc[0].interval; + noa_desc->start = wl->p2p->noa.desc[0].start; + len += sizeof(wifi_p2p_noa_desc_t); + } + if (buf_len <= len * 2) { + CFGP2P_ERR(("ERROR: buf_len %d in not enough for" + "returning noa in string format\n", buf_len)); + return -1; + } + /* We have to convert the buffer data into ASCII strings */ + for (i = 0; i < len; i++) { + sprintf(buf, "%02x", _buf[i]); + buf += 2; + } + buf[i*2] = '\0'; + } + } + else { + CFGP2P_ERR(("ERROR: get_noa in non-p2p mode\n")); + return -1; + } + return len * 2; +} + +s32 +wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, int len) +{ + int ps, ctw; + int ret = -1; + s32 legacy_ps; + + CFGP2P_DBG((" Enter\n")); + if (wl->p2p && wl->p2p->vif_created) { + sscanf(buf, "%d %d %d", &legacy_ps, &ps, &ctw); + CFGP2P_DBG((" Enter legacy_ps %d ps %d ctw %d\n", legacy_ps, ps, ctw)); + if (ctw != -1) { + wl->p2p->ops.ctw = ctw; + ret = 0; + } + if (ps != -1) { + wl->p2p->ops.ops = ps; + ret = wldev_iovar_setbuf(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION), + "p2p_ops", &wl->p2p->ops, sizeof(wl->p2p->ops), + wl->ioctl_buf, WLC_IOCTL_MAXLEN, &wl->ioctl_buf_sync); + if (ret < 0) { + CFGP2P_ERR(("fw set p2p_ops failed %d\n", ret)); + } + } + + if (legacy_ps != -1) { + s32 pm = legacy_ps ? PM_MAX : PM_OFF; + ret = wldev_ioctl(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION), + WLC_SET_PM, &pm, sizeof(pm), true); + if (unlikely(ret)) { + CFGP2P_ERR(("error (%d)\n", ret)); + } + } + } + else { + CFGP2P_ERR(("ERROR: set_p2p_ps in non-p2p mode\n")); + ret = -1; + } + return ret; +} + +u8 * +wl_cfgp2p_retreive_p2pattrib(void *buf, u8 element_id) +{ + wifi_p2p_ie_t *ie = NULL; + u16 len = 0; + u8 *subel; + u8 subelt_id; + u16 subelt_len; + + if (!buf) { + WL_ERR(("P2P IE not present")); + return 0; + } + + ie = (wifi_p2p_ie_t*) buf; + len = ie->len; + + /* Point subel to the P2P IE's subelt field. + * Subtract the preceding fields (id, len, OUI, oui_type) from the length. + */ + subel = ie->subelts; + len -= 4; /* exclude OUI + OUI_TYPE */ + + while (len >= 3) { + /* attribute id */ + subelt_id = *subel; + subel += 1; + len -= 1; + + /* 2-byte little endian */ + subelt_len = *subel++; + subelt_len |= *subel++ << 8; + + len -= 2; + len -= subelt_len; /* for the remaining subelt fields */ + + if (subelt_id == element_id) { + /* This will point to start of subelement attrib after + * attribute id & len + */ + return subel; + } + + /* Go to next subelement */ + subel += subelt_len; + } + + /* Not Found */ + return NULL; +} + +#define P2P_GROUP_CAPAB_GO_BIT 0x01 +u8 * +wl_cfgp2p_retreive_p2p_dev_addr(wl_bss_info_t *bi, u32 bi_length) +{ + wifi_p2p_ie_t * p2p_ie = NULL; + u8 *capability = NULL; + bool p2p_go = 0; + u8 *ptr = NULL; + + if (!(p2p_ie = wl_cfgp2p_find_p2pie(((u8 *) bi) + bi->ie_offset, bi->ie_length))) { + WL_ERR(("P2P IE not found")); + return NULL; + } + + if (!(capability = wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_P2P_INFO))) { + WL_ERR(("P2P Capability attribute not found")); + return NULL; + } + + /* Check Group capability for Group Owner bit */ + p2p_go = capability[1] & P2P_GROUP_CAPAB_GO_BIT; + if (!p2p_go) { + return bi->BSSID.octet; + } + + /* In probe responses, DEVICE INFO attribute will be present */ + if (!(ptr = wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_DEV_INFO))) { + /* If DEVICE_INFO is not found, this might be a beacon frame. + * check for DEVICE_ID in the beacon frame. + */ + ptr = wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_DEV_ID); + } + + if (!ptr) + WL_ERR((" Both DEVICE_ID & DEVICE_INFO attribute not present in P2P IE ")); + + return ptr; +} + +s32 +wl_cfgp2p_register_ndev(struct wl_priv *wl) +{ + int ret = 0; + struct net_device* net = NULL; + struct wireless_dev *wdev; + uint8 temp_addr[ETHER_ADDR_LEN] = { 0x00, 0x90, 0x4c, 0x33, 0x22, 0x11 }; + + /* Allocate etherdev, including space for private structure */ + if (!(net = alloc_etherdev(sizeof(wl)))) { + CFGP2P_ERR(("%s: OOM - alloc_etherdev\n", __FUNCTION__)); + goto fail; + } + + strcpy(net->name, "p2p%d"); + net->name[IFNAMSIZ - 1] = '\0'; + + /* Copy the reference to wl_priv */ + memcpy((void *)netdev_priv(net), &wl, sizeof(wl)); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) + ASSERT(!net->open); + net->do_ioctl = wl_cfgp2p_do_ioctl; + net->hard_start_xmit = wl_cfgp2p_start_xmit; + net->open = wl_cfgp2p_if_open; + net->stop = wl_cfgp2p_if_stop; +#else + ASSERT(!net->netdev_ops); + net->netdev_ops = &wl_cfgp2p_if_ops; +#endif + + /* Register with a dummy MAC addr */ + memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN); + + wdev = kzalloc(sizeof(*wdev), GFP_KERNEL); + if (unlikely(!wdev)) { + WL_ERR(("Could not allocate wireless device\n")); + return -ENOMEM; + } + + wdev->wiphy = wl->wdev->wiphy; + + wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_BSS); + + net->ieee80211_ptr = wdev; + + SET_NETDEV_DEV(net, wiphy_dev(wdev->wiphy)); + + /* Associate p2p0 network interface with new wdev */ + wdev->netdev = net; + + /* store p2p net ptr for further reference. Note that iflist won't have this + * entry as there corresponding firmware interface is a "Hidden" interface. + */ + if (wl->p2p_net) { + CFGP2P_ERR(("p2p_net defined already.\n")); + return -EINVAL; + } else { + wl->p2p_wdev = wdev; + wl->p2p_net = net; + } + + ret = register_netdev(net); + if (ret) { + CFGP2P_ERR((" register_netdevice failed (%d)\n", ret)); + goto fail; + } + + printk("%s: P2P Interface Registered\n", net->name); + + return ret; +fail: + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) + net->open = NULL; +#else + net->netdev_ops = NULL; +#endif + + if (net) { + unregister_netdev(net); + free_netdev(net); + } + + return -ENODEV; +} + +s32 +wl_cfgp2p_unregister_ndev(struct wl_priv *wl) +{ + + if (!wl || !wl->p2p_net) { + CFGP2P_ERR(("Invalid Ptr\n")); + return -EINVAL; + } + + unregister_netdev(wl->p2p_net); + free_netdev(wl->p2p_net); + + return 0; +} + +static int wl_cfgp2p_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + CFGP2P_DBG(("(%s) is not used for data operations. Droping the packet. \n", ndev->name)); + return 0; +} + +static int wl_cfgp2p_do_ioctl(struct net_device *net, struct ifreq *ifr, int cmd) +{ + int ret = 0; + struct wl_priv *wl = *(struct wl_priv **)netdev_priv(net); + struct net_device *ndev = wl_to_prmry_ndev(wl); + + /* There is no ifidx corresponding to p2p0 in our firmware. So we should + * not Handle any IOCTL cmds on p2p0 other than ANDROID PRIVATE CMDs. + * For Android PRIV CMD handling map it to primary I/F + */ + if (cmd == SIOCDEVPRIVATE+1) { + ret = wl_android_priv_cmd(ndev, ifr, cmd); + + } else { + CFGP2P_ERR(("%s: IOCTL req 0x%x on p2p0 I/F. Ignoring. \n", + __FUNCTION__, cmd)); + return -1; + } + + return ret; +} + +static int wl_cfgp2p_if_open(struct net_device *net) +{ + struct wireless_dev *wdev = net->ieee80211_ptr; + + if (!wdev) + return -EINVAL; + + /* If suppose F/W download (ifconfig wlan0 up) hasn't been done by now, + * do it here. This will make sure that in concurrent mode, supplicant + * is not dependent on a particular order of interface initialization. + * i.e you may give wpa_supp -iwlan0 -N -ip2p0 or wpa_supp -ip2p0 -N + * -iwlan0. + */ + wl_cfg80211_do_driver_init(net); + + wdev->wiphy->interface_modes |= (BIT(NL80211_IFTYPE_P2P_CLIENT) + | BIT(NL80211_IFTYPE_P2P_GO)); + + return 0; +} + +static int wl_cfgp2p_if_stop(struct net_device *net) +{ + struct wireless_dev *wdev = net->ieee80211_ptr; + + if (!wdev) + return -EINVAL; + + wdev->wiphy->interface_modes = (wdev->wiphy->interface_modes) + & (~(BIT(NL80211_IFTYPE_P2P_CLIENT)| + BIT(NL80211_IFTYPE_P2P_GO))); + return 0; +} diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h new file mode 100644 index 0000000000000..05323ed5d4817 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h @@ -0,0 +1,283 @@ +/* + * Linux cfgp2p driver + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_cfgp2p.h,v 1.1.4.1.2.8 2011/02/09 01:37:52 Exp $ + */ +#ifndef _wl_cfgp2p_h_ +#define _wl_cfgp2p_h_ +#include +#include + +struct wl_priv; +extern u32 wl_dbg_level; + +/* Enumeration of the usages of the BSSCFGs used by the P2P Library. Do not + * confuse this with a bsscfg index. This value is an index into the + * saved_ie[] array of structures which in turn contains a bsscfg index field. + */ +typedef enum { + P2PAPI_BSSCFG_PRIMARY, /* maps to driver's primary bsscfg */ + P2PAPI_BSSCFG_DEVICE, /* maps to driver's P2P device discovery bsscfg */ + P2PAPI_BSSCFG_CONNECTION, /* maps to driver's P2P connection bsscfg */ + P2PAPI_BSSCFG_MAX +} p2p_bsscfg_type_t; + +#define IE_MAX_LEN 512 +/* Structure to hold all saved P2P and WPS IEs for a BSSCFG */ +struct p2p_saved_ie { + u8 p2p_probe_req_ie[IE_MAX_LEN]; + u8 p2p_probe_res_ie[IE_MAX_LEN]; + u8 p2p_assoc_req_ie[IE_MAX_LEN]; + u8 p2p_assoc_res_ie[IE_MAX_LEN]; + u8 p2p_beacon_ie[IE_MAX_LEN]; + u32 p2p_probe_req_ie_len; + u32 p2p_probe_res_ie_len; + u32 p2p_assoc_req_ie_len; + u32 p2p_assoc_res_ie_len; + u32 p2p_beacon_ie_len; +}; + +struct p2p_bss { + u32 bssidx; + struct net_device *dev; + struct p2p_saved_ie saved_ie; + void *private_data; +}; + +struct p2p_info { + bool on; /* p2p on/off switch */ + bool scan; + bool vif_created; + s8 vir_ifname[IFNAMSIZ]; + unsigned long status; + struct ether_addr dev_addr; + struct ether_addr int_addr; + struct p2p_bss bss_idx[P2PAPI_BSSCFG_MAX]; + struct timer_list listen_timer; + wl_p2p_sched_t noa; + wl_p2p_ops_t ops; + wlc_ssid_t ssid; +}; + +/* dongle status */ +enum wl_cfgp2p_status { + WLP2P_STATUS_DISCOVERY_ON = 0, + WLP2P_STATUS_SEARCH_ENABLED, + WLP2P_STATUS_IF_ADD, + WLP2P_STATUS_IF_DEL, + WLP2P_STATUS_IF_DELETING, + WLP2P_STATUS_IF_CHANGING, + WLP2P_STATUS_IF_CHANGED, + WLP2P_STATUS_LISTEN_EXPIRED, + WLP2P_STATUS_ACTION_TX_COMPLETED, + WLP2P_STATUS_ACTION_TX_NOACK, + WLP2P_STATUS_SCANNING, + WLP2P_STATUS_GO_NEG_PHASE +}; + + +#define wl_to_p2p_bss_ndev(w, type) ((wl)->p2p->bss_idx[type].dev) +#define wl_to_p2p_bss_bssidx(w, type) ((wl)->p2p->bss_idx[type].bssidx) +#define wl_to_p2p_bss_saved_ie(w, type) ((wl)->p2p->bss_idx[type].saved_ie) +#define wl_to_p2p_bss_private(w, type) ((wl)->p2p->bss_idx[type].private_data) +#define wl_to_p2p_bss(wl, type) ((wl)->p2p->bss_idx[type]) +#define wl_get_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0:test_bit(WLP2P_STATUS_ ## stat, \ + &(wl)->p2p->status)) +#define wl_set_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0:set_bit(WLP2P_STATUS_ ## stat, \ + &(wl)->p2p->status)) +#define wl_clr_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0:clear_bit(WLP2P_STATUS_ ## stat, \ + &(wl)->p2p->status)) +#define wl_chg_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0:change_bit(WLP2P_STATUS_ ## stat, \ + &(wl)->p2p->status)) +#define p2p_on(wl) ((wl)->p2p->on) +#define p2p_scan(wl) ((wl)->p2p->scan) +#define p2p_is_on(wl) ((wl)->p2p && (wl)->p2p->on) + +/* dword align allocation */ +#define WLC_IOCTL_MAXLEN 8192 +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + +#define CFGP2P_ERR(args) \ + do { \ + if (wl_dbg_level & WL_DBG_ERR) { \ + printk(KERN_ERR "CFGP2P-ERROR) %s : ", __func__); \ + printk args; \ + } \ + } while (0) +#define CFGP2P_INFO(args) \ + do { \ + if (wl_dbg_level & WL_DBG_INFO) { \ + printk(KERN_ERR "CFGP2P-INFO) %s : ", __func__); \ + printk args; \ + } \ + } while (0) +#define CFGP2P_DBG(args) \ + do { \ + if (wl_dbg_level & WL_DBG_DBG) { \ + printk(KERN_ERR "CFGP2P-DEBUG) %s :", __func__); \ + printk args; \ + } \ + } while (0) + +extern bool +wl_cfgp2p_is_pub_action(void *frame, u32 frame_len); +extern bool +wl_cfgp2p_is_p2p_action(void *frame, u32 frame_len); +extern bool +wl_cfgp2p_is_gas_action(void *frame, u32 frame_len); +extern void +wl_cfgp2p_print_actframe(bool tx, void *frame, u32 frame_len); +extern s32 +wl_cfgp2p_init_priv(struct wl_priv *wl); +extern void +wl_cfgp2p_deinit_priv(struct wl_priv *wl); +extern s32 +wl_cfgp2p_set_firm_p2p(struct wl_priv *wl); +extern s32 +wl_cfgp2p_set_p2p_mode(struct wl_priv *wl, u8 mode, + u32 channel, u16 listen_ms, int bssidx); +extern s32 +wl_cfgp2p_ifadd(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, + chanspec_t chspec); +extern s32 +wl_cfgp2p_ifdel(struct wl_priv *wl, struct ether_addr *mac); +extern s32 +wl_cfgp2p_ifchange(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, chanspec_t chspec); + +extern s32 +wl_cfgp2p_ifidx(struct wl_priv *wl, struct ether_addr *mac, s32 *index); + +extern s32 +wl_cfgp2p_init_discovery(struct wl_priv *wl); +extern s32 +wl_cfgp2p_enable_discovery(struct wl_priv *wl, struct net_device *dev, const u8 *ie, u32 ie_len); +extern s32 +wl_cfgp2p_disable_discovery(struct wl_priv *wl); +extern s32 +wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, u32 num_chans, + u16 *channels, + s32 search_state, u16 action, u32 bssidx); + +extern s32 +wl_cfgp2p_act_frm_search(struct wl_priv *wl, struct net_device *ndev, + s32 bssidx, s32 channel); + +extern wpa_ie_fixed_t * +wl_cfgp2p_find_wpaie(u8 *parse, u32 len); + +extern wpa_ie_fixed_t * +wl_cfgp2p_find_wpsie(u8 *parse, u32 len); + +extern wifi_p2p_ie_t * +wl_cfgp2p_find_p2pie(u8 *parse, u32 len); + +extern s32 +wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, + s32 pktflag, const u8 *vndr_ie, u32 vndr_ie_len); +extern s32 +wl_cfgp2p_clear_management_ie(struct wl_priv *wl, s32 bssidx); + +extern s32 +wl_cfgp2p_find_idx(struct wl_priv *wl, struct net_device *ndev); + + +extern s32 +wl_cfgp2p_listen_complete(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +extern s32 +wl_cfgp2p_discover_listen(struct wl_priv *wl, s32 channel, u32 duration_ms); + +extern s32 +wl_cfgp2p_discover_enable_search(struct wl_priv *wl, u8 enable); + +extern s32 +wl_cfgp2p_action_tx_complete(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +extern s32 +wl_cfgp2p_tx_action_frame(struct wl_priv *wl, struct net_device *dev, + wl_af_params_t *af_params, s32 bssidx); + +extern void +wl_cfgp2p_generate_bss_mac(struct ether_addr *primary_addr, struct ether_addr *out_dev_addr, + struct ether_addr *out_int_addr); + +extern void +wl_cfg80211_change_ifaddr(u8* buf, struct ether_addr *p2p_int_addr, u8 element_id); +extern bool +wl_cfgp2p_bss_isup(struct net_device *ndev, int bsscfg_idx); + +extern s32 +wl_cfgp2p_bss(struct wl_priv *wl, struct net_device *ndev, s32 bsscfg_idx, s32 up); + + +extern s32 +wl_cfgp2p_supported(struct wl_priv *wl, struct net_device *ndev); + +extern s32 +wl_cfgp2p_down(struct wl_priv *wl); + +extern s32 +wl_cfgp2p_set_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int len); + +extern s32 +wl_cfgp2p_get_p2p_noa(struct wl_priv *wl, struct net_device *ndev, char* buf, int len); + +extern s32 +wl_cfgp2p_set_p2p_ps(struct wl_priv *wl, struct net_device *ndev, char* buf, int len); + +extern u8 * +wl_cfgp2p_retreive_p2pattrib(void *buf, u8 element_id); + +extern u8 * +wl_cfgp2p_retreive_p2p_dev_addr(wl_bss_info_t *bi, u32 bi_length); + +extern s32 +wl_cfgp2p_register_ndev(struct wl_priv *wl); + +extern s32 +wl_cfgp2p_unregister_ndev(struct wl_priv *wl); + +/* WiFi Direct */ +#define SOCIAL_CHAN_1 1 +#define SOCIAL_CHAN_2 6 +#define SOCIAL_CHAN_3 11 +#define SOCIAL_CHAN_CNT 3 +#define WL_P2P_WILDCARD_SSID "DIRECT-" +#define WL_P2P_WILDCARD_SSID_LEN 7 +#define WL_P2P_INTERFACE_PREFIX "p2p" +#define WL_P2P_TEMP_CHAN "11" + +/* If the provision discovery is for JOIN operations, then we need not do an internal scan to find GO */ +#define IS_PROV_DISC_WITHOUT_GROUP_ID(p2p_ie, len) (wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_GROUP_ID) == NULL ) + +#define IS_GAS_REQ(frame, len) (wl_cfgp2p_is_gas_action(frame, len) && \ + ((frame->action == P2PSD_ACTION_ID_GAS_IREQ) || \ + (frame->action == P2PSD_ACTION_ID_GAS_CREQ))) +#define IS_P2P_PUB_ACT_REQ(frame, p2p_ie, len) (wl_cfgp2p_is_pub_action(frame, len) && \ + ((frame->subtype == P2P_PAF_GON_REQ) || \ + (frame->subtype == P2P_PAF_INVITE_REQ) || \ + ((frame->subtype == P2P_PAF_PROVDIS_REQ) && IS_PROV_DISC_WITHOUT_GROUP_ID(p2p_ie, len)))) +#define IS_P2P_SOCIAL(ch) ((ch == SOCIAL_CHAN_1) || (ch == SOCIAL_CHAN_2) || (ch == SOCIAL_CHAN_3)) +#define IS_P2P_SSID(ssid) (memcmp(ssid, WL_P2P_WILDCARD_SSID, WL_P2P_WILDCARD_SSID_LEN) == 0) +#endif /* _wl_cfgp2p_h_ */ diff --git a/drivers/net/wireless/bcmdhd/wl_dbg.h b/drivers/net/wireless/bcmdhd/wl_dbg.h new file mode 100644 index 0000000000000..0b99557cbe8d4 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_dbg.h @@ -0,0 +1,49 @@ +/* + * Minimal debug/trace/assert driver definitions for + * Broadcom 802.11 Networking Adapter. + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_dbg.h,v 1.115.6.3 2010-12-15 21:42:23 Exp $ + */ + + + +#ifndef _wl_dbg_h_ +#define _wl_dbg_h_ + + +extern uint32 wl_msg_level; +extern uint32 wl_msg_level2; + +#define WL_PRINT(args) printf args + + + +#define WL_NONE(args) + +#define WL_ERROR(args) +#define WL_TRACE(args) + + +extern uint32 wl_msg_level; +extern uint32 wl_msg_level2; +#endif diff --git a/drivers/net/wireless/bcmdhd/wl_iw.c b/drivers/net/wireless/bcmdhd/wl_iw.c new file mode 100644 index 0000000000000..095dc86b08e7d --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_iw.c @@ -0,0 +1,8894 @@ +/* + * Linux Wireless Extensions support + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_iw.c,v 1.132.2.18 2011-02-05 01:44:47 $ + */ + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +typedef void wlc_info_t; +typedef void wl_info_t; +typedef const struct si_pub si_t; +#include + +#include +#include +#include +#define WL_ERROR(x) printf x +#define WL_TRACE(x) +#define WL_ASSOC(x) +#define WL_INFORM(x) +#define WL_WSEC(x) +#define WL_SCAN(x) + + +#ifdef PNO_SET_DEBUG +#define WL_PNO(x) printf x +#else +#define WL_PNO(x) +#endif + + +#define JF2MS ((((jiffies / HZ) * 1000) + ((jiffies % HZ) * 1000) / HZ)) + +#ifdef COEX_DBG +#define WL_TRACE_COEX(x) printf("TS:%lu ", JF2MS); \ + printf x +#else +#define WL_TRACE_COEX(x) +#endif + +#ifdef SCAN_DBG +#define WL_TRACE_SCAN(x) printf("TS:%lu ", JF2MS); \ + printf x +#else +#define WL_TRACE_SCAN(x) +#endif + + +#include + + + + +#define IW_WSEC_ENABLED(wsec) ((wsec) & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED)) + +#include + +#define WL_IW_USE_ISCAN 1 +#define ENABLE_ACTIVE_PASSIVE_SCAN_SUPPRESS 1 + +#ifdef OEM_CHROMIUMOS +bool g_set_essid_before_scan = TRUE; +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 + struct mutex g_wl_ss_scan_lock; +#endif + +#if defined(SOFTAP) +#define WL_SOFTAP(x) +static struct net_device *priv_dev; +extern bool ap_cfg_running; +extern bool ap_fw_loaded; +struct net_device *ap_net_dev = NULL; +tsk_ctl_t ap_eth_ctl; +static int wl_iw_set_ap_security(struct net_device *dev, struct ap_profile *ap); +static int wl_iw_softap_deassoc_stations(struct net_device *dev, u8 *mac); +#endif + + +#define WL_IW_IOCTL_CALL(func_call) \ + do { \ + func_call; \ + } while (0) + +#define RETURN_IF_EXTRA_NULL(extra) \ + if (!extra) { \ + WL_ERROR(("%s: error : extra is null pointer\n", __FUNCTION__)); \ + return -EINVAL; \ + } + +static int g_onoff = G_WLAN_SET_ON; +wl_iw_extra_params_t g_wl_iw_params; + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 + +static struct mutex wl_cache_lock; +static struct mutex wl_softap_lock; + +#define DHD_OS_MUTEX_INIT(a) mutex_init(a) +#define DHD_OS_MUTEX_LOCK(a) mutex_lock(a) +#define DHD_OS_MUTEX_UNLOCK(a) mutex_unlock(a) + +#else + +#define DHD_OS_MUTEX_INIT(a) +#define DHD_OS_MUTEX_LOCK(a) +#define DHD_OS_MUTEX_UNLOCK(a) + +#endif + +#include +extern void dhd_customer_gpio_wlan_ctrl(int onoff); +extern uint dhd_dev_reset(struct net_device *dev, uint8 flag); +extern int dhd_dev_init_ioctl(struct net_device *dev); + +uint wl_msg_level = WL_ERROR_VAL; + +#define MAX_WLIW_IOCTL_LEN 1024 + + +#define htod32(i) i +#define htod16(i) i +#define dtoh32(i) i +#define dtoh16(i) i +#define htodchanspec(i) i +#define dtohchanspec(i) i + +extern struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev); +extern int dhd_wait_pend8021x(struct net_device *dev); + +#if WIRELESS_EXT < 19 +#define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) +#define IW_EVENT_IDX(cmd) ((cmd) - IWEVFIRST) +#endif + +static void *g_scan = NULL; +static volatile uint g_scan_specified_ssid; +static wlc_ssid_t g_specific_ssid; + +static wlc_ssid_t g_ssid; + +#ifdef CONFIG_WPS2 +static char *g_wps_probe_req_ie; +static int g_wps_probe_req_ie_len; +#endif + +bool btcoex_is_sco_active(struct net_device *dev); +static wl_iw_ss_cache_ctrl_t g_ss_cache_ctrl; +#if defined(CONFIG_FIRST_SCAN) +static volatile uint g_first_broadcast_scan; +static volatile uint g_first_counter_scans; +#define MAX_ALLOWED_BLOCK_SCAN_FROM_FIRST_SCAN 3 +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#define DAEMONIZE(a) daemonize(a); \ + allow_signal(SIGKILL); \ + allow_signal(SIGTERM); +#else +#define RAISE_RX_SOFTIRQ() \ + cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ) +#define DAEMONIZE(a) daemonize(); \ + do { if (a) \ + strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \ + } while (0); +#endif + +#if defined(WL_IW_USE_ISCAN) +#if !defined(CSCAN) +static void wl_iw_free_ss_cache(void); +static int wl_iw_run_ss_cache_timer(int kick_off); +#endif +#if defined(CONFIG_FIRST_SCAN) +int wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag); +#endif +static int dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len); +#define ISCAN_STATE_IDLE 0 +#define ISCAN_STATE_SCANING 1 + + +#define WLC_IW_ISCAN_MAXLEN 2048 +typedef struct iscan_buf { + struct iscan_buf * next; + char iscan_buf[WLC_IW_ISCAN_MAXLEN]; +} iscan_buf_t; + +typedef struct iscan_info { + struct net_device *dev; + struct timer_list timer; + uint32 timer_ms; + uint32 timer_on; + int iscan_state; + iscan_buf_t * list_hdr; + iscan_buf_t * list_cur; + + + tsk_ctl_t tsk_ctl; + + uint32 scan_flag; +#if defined CSCAN + char ioctlbuf[WLC_IOCTL_MEDLEN]; +#else + char ioctlbuf[WLC_IOCTL_SMLEN]; +#endif + + wl_iscan_params_t *iscan_ex_params_p; + int iscan_ex_param_size; +} iscan_info_t; + + + +#define COEX_DHCP 1 +#ifdef COEX_DHCP + +#define BT_DHCP_eSCO_FIX +#define BT_DHCP_USE_FLAGS +#define BT_DHCP_OPPORTUNITY_WINDOW_TIME 2500 +#define BT_DHCP_FLAG_FORCE_TIME 5500 + + + +static int wl_iw_set_btcoex_dhcp( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +); + +static void wl_iw_bt_flag_set(struct net_device *dev, bool set); +static void wl_iw_bt_release(void); + +typedef enum bt_coex_status { + BT_DHCP_IDLE = 0, + BT_DHCP_START, + BT_DHCP_OPPORTUNITY_WINDOW, + BT_DHCP_FLAG_FORCE_TIMEOUT +} coex_status_t; + + +typedef struct bt_info { + struct net_device *dev; + struct timer_list timer; + uint32 timer_ms; + uint32 timer_on; + uint32 ts_dhcp_start; + uint32 ts_dhcp_ok; + bool dhcp_done; + int bt_state; + + + tsk_ctl_t tsk_ctl; + +} bt_info_t; + +bt_info_t *g_bt = NULL; +static void wl_iw_bt_timerfunc(ulong data); +#endif +iscan_info_t *g_iscan = NULL; +void dhd_print_buf(void *pbuf, int len, int bytes_per_line); +static void wl_iw_timerfunc(ulong data); +static void wl_iw_set_event_mask(struct net_device *dev); +static int +wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, uint16 action); +#endif + +static int +wl_iw_set_scan( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +); + +#ifndef CSCAN +static int +wl_iw_get_scan( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +); + +static uint +wl_iw_get_scan_prep( + wl_scan_results_t *list, + struct iw_request_info *info, + char *extra, + short max_size +); +#endif + +static void +swap_key_from_BE( + wl_wsec_key_t *key +) +{ + key->index = htod32(key->index); + key->len = htod32(key->len); + key->algo = htod32(key->algo); + key->flags = htod32(key->flags); + key->rxiv.hi = htod32(key->rxiv.hi); + key->rxiv.lo = htod16(key->rxiv.lo); + key->iv_initialized = htod32(key->iv_initialized); +} + +static void +swap_key_to_BE( + wl_wsec_key_t *key +) +{ + key->index = dtoh32(key->index); + key->len = dtoh32(key->len); + key->algo = dtoh32(key->algo); + key->flags = dtoh32(key->flags); + key->rxiv.hi = dtoh32(key->rxiv.hi); + key->rxiv.lo = dtoh16(key->rxiv.lo); + key->iv_initialized = dtoh32(key->iv_initialized); +} + +static int +dev_wlc_ioctl( + struct net_device *dev, + int cmd, + void *arg, + int len +) +{ + struct ifreq ifr; + wl_ioctl_t ioc; + mm_segment_t fs; + int ret = -EINVAL; + + if (!dev) { + WL_ERROR(("%s: dev is null\n", __FUNCTION__)); + return ret; + } + + net_os_wake_lock(dev); + + WL_INFORM(("%s, PID:%x: send Local IOCTL -> dhd: cmd:0x%x, buf:%p, len:%d ,\n", + __FUNCTION__, current->pid, cmd, arg, len)); + + if (g_onoff == G_WLAN_SET_ON) { + memset(&ioc, 0, sizeof(ioc)); + ioc.cmd = cmd; + ioc.buf = arg; + ioc.len = len; + + strcpy(ifr.ifr_name, dev->name); + ifr.ifr_data = (caddr_t) &ioc; + + + ret = dev_open(dev); + if (ret) { + WL_ERROR(("%s: Error dev_open: %d\n", __func__, ret)); + net_os_wake_unlock(dev); + return ret; + } + + fs = get_fs(); + set_fs(get_ds()); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31) + ret = dev->do_ioctl(dev, &ifr, SIOCDEVPRIVATE); +#else + ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, SIOCDEVPRIVATE); +#endif + set_fs(fs); + } + else { + WL_TRACE(("%s: call after driver stop : ignored\n", __FUNCTION__)); + } + + net_os_wake_unlock(dev); + + return ret; +} + + +static int +dev_wlc_intvar_get_reg( + struct net_device *dev, + char *name, + uint reg, + int *retval) +{ + union { + char buf[WLC_IOCTL_SMLEN]; + int val; + } var; + int error; + + uint len; + len = bcm_mkiovar(name, (char *)(®), sizeof(reg), (char *)(&var), sizeof(var.buf)); + ASSERT(len); + error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)&var, len); + + *retval = dtoh32(var.val); + return (error); +} + + +static int +dev_wlc_intvar_set_reg( + struct net_device *dev, + char *name, + char *addr, + char * val) +{ + char reg_addr[8]; + + memset(reg_addr, 0, sizeof(reg_addr)); + memcpy((char *)®_addr[0], (char *)addr, 4); + memcpy((char *)®_addr[4], (char *)val, 4); + + return (dev_wlc_bufvar_set(dev, name, (char *)®_addr[0], sizeof(reg_addr))); +} + + + + +static int +dev_wlc_intvar_set( + struct net_device *dev, + char *name, + int val) +{ + char buf[WLC_IOCTL_SMLEN]; + uint len; + + val = htod32(val); + len = bcm_mkiovar(name, (char *)(&val), sizeof(val), buf, sizeof(buf)); + ASSERT(len); + + return (dev_wlc_ioctl(dev, WLC_SET_VAR, buf, len)); +} + +#if defined(WL_IW_USE_ISCAN) +static int +dev_iw_iovar_setbuf( + struct net_device *dev, + char *iovar, + void *param, + int paramlen, + void *bufptr, + int buflen) +{ + int iolen; + + iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen); + ASSERT(iolen); + + if (iolen == 0) + return 0; + + return (dev_wlc_ioctl(dev, WLC_SET_VAR, bufptr, iolen)); +} + +static int +dev_iw_iovar_getbuf( + struct net_device *dev, + char *iovar, + void *param, + int paramlen, + void *bufptr, + int buflen) +{ + int iolen; + + iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen); + ASSERT(iolen); + + return (dev_wlc_ioctl(dev, WLC_GET_VAR, bufptr, buflen)); +} +#endif + + +#if WIRELESS_EXT > 17 +static int +dev_wlc_bufvar_set( + struct net_device *dev, + char *name, + char *buf, int len) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) + char ioctlbuf[MAX_WLIW_IOCTL_LEN]; +#else + static char ioctlbuf[MAX_WLIW_IOCTL_LEN]; +#endif + uint buflen; + + buflen = bcm_mkiovar(name, buf, len, ioctlbuf, sizeof(ioctlbuf)); + ASSERT(buflen); + + return (dev_wlc_ioctl(dev, WLC_SET_VAR, ioctlbuf, buflen)); +} +#endif + + +static int +dev_wlc_bufvar_get( + struct net_device *dev, + char *name, + char *buf, int buflen) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) + char ioctlbuf[MAX_WLIW_IOCTL_LEN]; +#else + static char ioctlbuf[MAX_WLIW_IOCTL_LEN]; +#endif + int error; + uint len; + + len = bcm_mkiovar(name, NULL, 0, ioctlbuf, sizeof(ioctlbuf)); + ASSERT(len); + error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)ioctlbuf, MAX_WLIW_IOCTL_LEN); + if (!error) + bcopy(ioctlbuf, buf, buflen); + + return (error); +} + + + +static int +dev_wlc_intvar_get( + struct net_device *dev, + char *name, + int *retval) +{ + union { + char buf[WLC_IOCTL_SMLEN]; + int val; + } var; + int error; + + uint len; + uint data_null; + + len = bcm_mkiovar(name, (char *)(&data_null), 0, (char *)(&var), sizeof(var.buf)); + ASSERT(len); + error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)&var, len); + + *retval = dtoh32(var.val); + + return (error); +} + + +#if WIRELESS_EXT > 12 +static int +wl_iw_set_active_scan( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int as = 0; + int error = 0; + char *p = extra; + +#if defined(WL_IW_USE_ISCAN) + if (g_iscan->iscan_state == ISCAN_STATE_IDLE) +#endif + error = dev_wlc_ioctl(dev, WLC_SET_PASSIVE_SCAN, &as, sizeof(as)); +#if defined(WL_IW_USE_ISCAN) + else + g_iscan->scan_flag = as; +#endif + p += snprintf(p, MAX_WX_STRING, "OK"); + + wrqu->data.length = p - extra + 1; + return error; +} + +static int +wl_iw_set_passive_scan( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int ps = 1; + int error = 0; + char *p = extra; + +#if defined(WL_IW_USE_ISCAN) + if (g_iscan->iscan_state == ISCAN_STATE_IDLE) { +#endif + + + if (g_scan_specified_ssid == 0) { + error = dev_wlc_ioctl(dev, WLC_SET_PASSIVE_SCAN, &ps, sizeof(ps)); + } +#if defined(WL_IW_USE_ISCAN) + } + else + g_iscan->scan_flag = ps; +#endif + + p += snprintf(p, MAX_WX_STRING, "OK"); + + wrqu->data.length = p - extra + 1; + return error; +} + + +static int +wl_iw_set_txpower( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = 0; + char *p = extra; + int txpower = -1; + + txpower = bcm_atoi(extra + strlen(TXPOWER_SET_CMD) + 1); + if ((txpower >= 0) && (txpower <= 127)) + { + txpower |= WL_TXPWR_OVERRIDE; + txpower = htod32(txpower); + + error = dev_wlc_intvar_set(dev, "qtxpower", txpower); + p += snprintf(p, MAX_WX_STRING, "OK"); + WL_TRACE(("%s: set TXpower 0x%X is OK\n", __FUNCTION__, txpower)); + } else { + WL_ERROR(("%s: set tx power failed\n", __FUNCTION__)); + p += snprintf(p, MAX_WX_STRING, "FAIL"); + } + + wrqu->data.length = p - extra + 1; + return error; +} + +static int +wl_iw_get_macaddr( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error; + char buf[128]; + struct ether_addr *id; + char *p = extra; + + + strcpy(buf, "cur_etheraddr"); + error = dev_wlc_ioctl(dev, WLC_GET_VAR, buf, sizeof(buf)); + id = (struct ether_addr *) buf; + p += snprintf(p, MAX_WX_STRING, "Macaddr = %02X:%02X:%02X:%02X:%02X:%02X\n", + id->octet[0], id->octet[1], id->octet[2], + id->octet[3], id->octet[4], id->octet[5]); + wrqu->data.length = p - extra + 1; + + return error; +} + + + +static int +wl_iw_set_country( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + char country_code[WLC_CNTRY_BUF_SZ]; + int error = 0; + char *p = extra; + int country_offset; + int country_code_size; + wl_country_t cspec = {{0}, 0, {0}}; + char smbuf[WLC_IOCTL_SMLEN]; + scb_val_t scbval; + + cspec.rev = -1; + memset(country_code, 0, sizeof(country_code)); + memset(smbuf, 0, sizeof(smbuf)); + + + country_offset = strcspn(extra, " "); + country_code_size = strlen(extra) - country_offset; + + + if (country_offset != 0) { + strncpy(country_code, extra + country_offset +1, + MIN(country_code_size, sizeof(country_code))); + + + bzero(&scbval, sizeof(scb_val_t)); + if ((error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t)))) { + WL_ERROR(("%s: set country failed due to Disassoc error\n", __FUNCTION__)); + goto exit_failed; + } + + memcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ); + memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ); + + get_customized_country_code((char *)&cspec.country_abbrev, &cspec); + + + if ((error = dev_iw_iovar_setbuf(dev, "country", &cspec, + sizeof(cspec), smbuf, sizeof(smbuf))) >= 0) { + p += snprintf(p, MAX_WX_STRING, "OK"); + WL_ERROR(("%s: set country for %s as %s rev %d is OK\n", + __FUNCTION__, country_code, cspec.ccode, cspec.rev)); + dhd_bus_country_set(dev, &cspec); + goto exit; + } + } + + WL_ERROR(("%s: set country for %s as %s rev %d failed\n", + __FUNCTION__, country_code, cspec.ccode, cspec.rev)); + +exit_failed: + p += snprintf(p, MAX_WX_STRING, "FAIL"); + +exit: + wrqu->data.length = p - extra + 1; + return error; +} + +static int +wl_iw_set_power_mode( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = 0; + char *p = extra; + static int pm = PM_FAST; + int pm_local = PM_OFF; + char powermode_val = 0; + + WL_TRACE_COEX(("%s: DHCP session cmd:%s\n", __FUNCTION__, extra)); + + strncpy((char *)&powermode_val, extra + strlen("POWERMODE") +1, 1); + + if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) { + + WL_TRACE(("%s: DHCP session starts\n", __FUNCTION__)); + + dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm)); + dev_wlc_ioctl(dev, WLC_SET_PM, &pm_local, sizeof(pm_local)); + + + net_os_set_packet_filter(dev, 0); + +#ifdef COEX_DHCP + g_bt->ts_dhcp_start = JF2MS; + g_bt->dhcp_done = FALSE; + WL_TRACE_COEX(("%s: DHCP start, pm:%d changed to pm:%d\n", + __FUNCTION__, pm, pm_local)); + +#endif + } else if (strnicmp((char *)&powermode_val, "0", strlen("0")) == 0) { + + + dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm)); + + + net_os_set_packet_filter(dev, 1); + +#ifdef COEX_DHCP + g_bt->dhcp_done = TRUE; + g_bt->ts_dhcp_ok = JF2MS; + WL_TRACE_COEX(("%s: DHCP done for:%d ms, restored pm:%d\n", + __FUNCTION__, (g_bt->ts_dhcp_ok - g_bt->ts_dhcp_start), pm)); +#endif + + } else { + WL_ERROR(("%s Unkwown yet power setting, ignored\n", + __FUNCTION__)); + } + + p += snprintf(p, MAX_WX_STRING, "OK"); + + wrqu->data.length = p - extra + 1; + + return error; +} + + +bool btcoex_is_sco_active(struct net_device *dev) +{ + int ioc_res = 0; + bool res = FALSE; + int sco_id_cnt = 0; + int param27; + int i; + + for (i = 0; i < 12; i++) { + + ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 27, ¶m27); + + WL_TRACE_COEX(("%s, sample[%d], btc params: 27:%x\n", + __FUNCTION__, i, param27)); + + if (ioc_res < 0) { + WL_ERROR(("%s ioc read btc params error\n", __FUNCTION__)); + break; + } + + if ((param27 & 0x6) == 2) { + sco_id_cnt++; + } + + if (sco_id_cnt > 2) { + WL_TRACE_COEX(("%s, sco/esco detected, pkt id_cnt:%d samples:%d\n", + __FUNCTION__, sco_id_cnt, i)); + res = TRUE; + break; + } + + msleep(5); + } + + return res; +} + +#if defined(BT_DHCP_eSCO_FIX) + +static int set_btc_esco_params(struct net_device *dev, bool trump_sco) +{ + static bool saved_status = FALSE; + + char buf_reg50va_dhcp_on[8] = { 50, 00, 00, 00, 0x22, 0x80, 0x00, 0x00 }; + char buf_reg51va_dhcp_on[8] = { 51, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg64va_dhcp_on[8] = { 64, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg65va_dhcp_on[8] = { 65, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + char buf_reg71va_dhcp_on[8] = { 71, 00, 00, 00, 0x00, 0x00, 0x00, 0x00 }; + + uint32 regaddr; + static uint32 saved_reg50; + static uint32 saved_reg51; + static uint32 saved_reg64; + static uint32 saved_reg65; + static uint32 saved_reg71; + + if (trump_sco) { + + + WL_TRACE_COEX(("Do new SCO/eSCO coex algo {save & override} \n")); + + + if ((!dev_wlc_intvar_get_reg(dev, "btc_params", 50, &saved_reg50)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 51, &saved_reg51)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 64, &saved_reg64)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 65, &saved_reg65)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 71, &saved_reg71))) { + + saved_status = TRUE; + WL_TRACE_COEX(("%s saved bt_params[50,51,64,65,71]:" + " 0x%x 0x%x 0x%x 0x%x 0x%x\n", + __FUNCTION__, saved_reg50, saved_reg51, + saved_reg64, saved_reg65, saved_reg71)); + + } else { + WL_ERROR((":%s: save btc_params failed\n", + __FUNCTION__)); + saved_status = FALSE; + return -1; + } + + WL_TRACE_COEX(("override with [50,51,64,65,71]:" + " 0x%x 0x%x 0x%x 0x%x 0x%x\n", + *(u32 *)(buf_reg50va_dhcp_on+4), + *(u32 *)(buf_reg51va_dhcp_on+4), + *(u32 *)(buf_reg64va_dhcp_on+4), + *(u32 *)(buf_reg65va_dhcp_on+4), + *(u32 *)(buf_reg71va_dhcp_on+4))); + + dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg50va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg51va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg64va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg65va_dhcp_on[0], 8); + dev_wlc_bufvar_set(dev, "btc_params", (char *)&buf_reg71va_dhcp_on[0], 8); + + saved_status = TRUE; + + } else if (saved_status) { + + WL_TRACE_COEX(("Do new SCO/eSCO coex algo {save & override} \n")); + + regaddr = 50; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg50); + regaddr = 51; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg51); + regaddr = 64; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg64); + regaddr = 65; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg65); + regaddr = 71; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg71); + + WL_TRACE_COEX(("restore bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n", + saved_reg50, saved_reg51, saved_reg64, + saved_reg65, saved_reg71)); + + saved_status = FALSE; + } else { + WL_ERROR((":%s att to restore not saved BTCOEX params\n", + __FUNCTION__)); + return -1; + } + return 0; +} +#endif + + +static int +wl_iw_get_power_mode( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = 0; + int pm_local; + char *p = extra; + + error = dev_wlc_ioctl(dev, WLC_GET_PM, &pm_local, sizeof(pm_local)); + if (!error) { + WL_TRACE(("%s: Powermode = %d\n", __func__, pm_local)); + if (pm_local == PM_OFF) + pm_local = 1; + else + pm_local = 0; + p += snprintf(p, MAX_WX_STRING, "powermode = %d", pm_local); + } + else { + WL_TRACE(("%s: Error = %d\n", __func__, error)); + p += snprintf(p, MAX_WX_STRING, "FAIL"); + } + wrqu->data.length = p - extra + 1; + return error; +} + +static int +wl_iw_set_btcoex_dhcp( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = 0; + char *p = extra; + char powermode_val = 0; + char buf_reg66va_dhcp_on[8] = { 66, 00, 00, 00, 0x10, 0x27, 0x00, 0x00 }; + char buf_reg41va_dhcp_on[8] = { 41, 00, 00, 00, 0x33, 0x00, 0x00, 0x00 }; + char buf_reg68va_dhcp_on[8] = { 68, 00, 00, 00, 0x90, 0x01, 0x00, 0x00 }; + + uint32 regaddr; + static uint32 saved_reg66; + static uint32 saved_reg41; + static uint32 saved_reg68; + static bool saved_status = FALSE; + +#ifdef COEX_DHCP + char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; +#endif + + + strncpy((char *)&powermode_val, extra + strlen("BTCOEXMODE") +1, 1); + + if (strnicmp((char *)&powermode_val, "1", strlen("1")) == 0) { + + WL_TRACE(("%s: DHCP session starts\n", __FUNCTION__)); + + + if ((saved_status == FALSE) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 66, &saved_reg66)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 41, &saved_reg41)) && + (!dev_wlc_intvar_get_reg(dev, "btc_params", 68, &saved_reg68))) { + saved_status = TRUE; + WL_TRACE(("Saved 0x%x 0x%x 0x%x\n", + saved_reg66, saved_reg41, saved_reg68)); + + + + +#ifdef COEX_DHCP + + if (btcoex_is_sco_active(dev)) { + + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg66va_dhcp_on[0], + sizeof(buf_reg66va_dhcp_on)); + + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg41va_dhcp_on[0], + sizeof(buf_reg41va_dhcp_on)); + + dev_wlc_bufvar_set(dev, "btc_params", + (char *)&buf_reg68va_dhcp_on[0], + sizeof(buf_reg68va_dhcp_on)); + saved_status = TRUE; + + g_bt->bt_state = BT_DHCP_START; + g_bt->timer_on = 1; + mod_timer(&g_bt->timer, g_bt->timer.expires); + WL_TRACE_COEX(("%s enable BT DHCP Timer\n", + __FUNCTION__)); + } +#endif + } + else if (saved_status == TRUE) { + WL_ERROR(("%s was called w/o DHCP OFF. Continue\n", __FUNCTION__)); + } + } + else if (strnicmp((char *)&powermode_val, "2", strlen("2")) == 0) { + + + + +#ifdef COEX_DHCP + + WL_TRACE(("%s disable BT DHCP Timer\n", __FUNCTION__)); + if (g_bt->timer_on) { + g_bt->timer_on = 0; + del_timer_sync(&g_bt->timer); + + if (g_bt->bt_state != BT_DHCP_IDLE) { + + WL_TRACE_COEX(("%s bt->bt_state:%d\n", + __FUNCTION__, g_bt->bt_state)); + + up(&g_bt->tsk_ctl.sema); + } + } + + + if (saved_status == TRUE) + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_default[0], sizeof(buf_flag7_default)); +#endif + + + if (saved_status == TRUE) { + regaddr = 66; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg66); + regaddr = 41; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg41); + regaddr = 68; + dev_wlc_intvar_set_reg(dev, "btc_params", + (char *)®addr, (char *)&saved_reg68); + + WL_TRACE_COEX(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n", + saved_reg66, saved_reg41, saved_reg68)); + } + saved_status = FALSE; + + } + else { + WL_ERROR(("%s Unkwown yet power setting, ignored\n", + __FUNCTION__)); + } + + p += snprintf(p, MAX_WX_STRING, "OK"); + + wrqu->data.length = p - extra + 1; + + return error; +} + +static int +wl_iw_set_suspend_opt( +struct net_device *dev, +struct iw_request_info *info, +union iwreq_data *wrqu, +char *extra +) +{ + int suspend_flag; + int ret_now; + int ret = 0; + + suspend_flag = *(extra + strlen(SETSUSPENDOPT_CMD) + 1) - '0'; + + if (suspend_flag != 0) + suspend_flag = 1; + + ret_now = net_os_set_suspend_disable(dev, suspend_flag); + + if (ret_now != suspend_flag) { + if (!(ret = net_os_set_suspend(dev, ret_now, 1))) + WL_ERROR(("%s: Suspend Flag %d -> %d\n", + __FUNCTION__, ret_now, suspend_flag)); + else + WL_ERROR(("%s: failed %d\n", __FUNCTION__, ret)); + } + + return ret; +} + +static int +wl_iw_set_suspend_mode( +struct net_device *dev, +struct iw_request_info *info, +union iwreq_data *wrqu, +char *extra +) +{ + int ret = 0; + +#if !defined(CONFIG_HAS_EARLYSUSPEND) || !defined(DHD_USE_EARLYSUSPEND) + int suspend_flag; + + suspend_flag = *(extra + strlen(SETSUSPENDMODE_CMD) + 1) - '0'; + + if (suspend_flag != 0) + suspend_flag = 1; + + if (!(ret = net_os_set_suspend(dev, suspend_flag, 0))) + WL_ERROR(("%s: Suspend Mode %d\n",__FUNCTION__,suspend_flag)); + else + WL_ERROR(("%s: failed %d\n", __FUNCTION__, ret)); +#endif + return ret; +} + +static int +wl_format_ssid(char* ssid_buf, uint8* ssid, int ssid_len) +{ + int i, c; + char *p = ssid_buf; + + if (ssid_len > 32) ssid_len = 32; + + for (i = 0; i < ssid_len; i++) { + c = (int)ssid[i]; + if (c == '\\') { + *p++ = '\\'; + *p++ = '\\'; + } else if (isprint((uchar)c)) { + *p++ = (char)c; + } else { + p += sprintf(p, "\\x%02X", c); + } + } + *p = '\0'; + + return p - ssid_buf; +} + +static int +wl_iw_get_link_speed( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = 0; + char *p = extra; + static int link_speed; + + + net_os_wake_lock(dev); + if (g_onoff == G_WLAN_SET_ON) { + error = dev_wlc_ioctl(dev, WLC_GET_RATE, &link_speed, sizeof(link_speed)); + link_speed *= 500000; + } + + p += snprintf(p, MAX_WX_STRING, "LinkSpeed %d", link_speed/1000000); + + wrqu->data.length = p - extra + 1; + + net_os_wake_unlock(dev); + return error; +} + + +static int +wl_iw_get_dtim_skip( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = -1; + char *p = extra; + char iovbuf[32]; + + net_os_wake_lock(dev); + if (g_onoff == G_WLAN_SET_ON) { + + memset(iovbuf, 0, sizeof(iovbuf)); + strcpy(iovbuf, "bcn_li_dtim"); + + if ((error = dev_wlc_ioctl(dev, WLC_GET_VAR, + &iovbuf, sizeof(iovbuf))) >= 0) { + + p += snprintf(p, MAX_WX_STRING, "Dtim_skip %d", iovbuf[0]); + WL_TRACE(("%s: get dtim_skip = %d\n", __FUNCTION__, iovbuf[0])); + wrqu->data.length = p - extra + 1; + } + else + WL_ERROR(("%s: get dtim_skip failed code %d\n", + __FUNCTION__, error)); + } + net_os_wake_unlock(dev); + return error; +} + + +static int +wl_iw_set_dtim_skip( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = -1; + char *p = extra; + int bcn_li_dtim; + char iovbuf[32]; + + net_os_wake_lock(dev); + if (g_onoff == G_WLAN_SET_ON) { + + bcn_li_dtim = htod32((uint)*(extra + strlen(DTIM_SKIP_SET_CMD) + 1) - '0'); + + if ((bcn_li_dtim >= 0) || ((bcn_li_dtim <= 5))) { + + memset(iovbuf, 0, sizeof(iovbuf)); + bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim, + 4, iovbuf, sizeof(iovbuf)); + + if ((error = dev_wlc_ioctl(dev, WLC_SET_VAR, + &iovbuf, sizeof(iovbuf))) >= 0) { + p += snprintf(p, MAX_WX_STRING, "OK"); + + + net_os_set_dtim_skip(dev, bcn_li_dtim); + + WL_TRACE(("%s: set dtim_skip %d OK\n", __FUNCTION__, + bcn_li_dtim)); + goto exit; + } + else WL_ERROR(("%s: set dtim_skip %d failed code %d\n", + __FUNCTION__, bcn_li_dtim, error)); + } + else WL_ERROR(("%s Incorrect dtim_skip setting %d, ignored\n", + __FUNCTION__, bcn_li_dtim)); + } + + p += snprintf(p, MAX_WX_STRING, "FAIL"); + +exit: + wrqu->data.length = p - extra + 1; + net_os_wake_unlock(dev); + return error; +} + + +static int +wl_iw_get_band( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = -1; + char *p = extra; + static int band; + + net_os_wake_lock(dev); + + if (g_onoff == G_WLAN_SET_ON) { + error = dev_wlc_ioctl(dev, WLC_GET_BAND, &band, sizeof(band)); + + p += snprintf(p, MAX_WX_STRING, "Band %d", band); + + wrqu->data.length = p - extra + 1; + } + + net_os_wake_unlock(dev); + return error; +} + + +static int +wl_iw_set_band( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = -1; + char *p = extra; + uint band; + + net_os_wake_lock(dev); + + if (g_onoff == G_WLAN_SET_ON) { + + band = htod32((uint)*(extra + strlen(BAND_SET_CMD) + 1) - '0'); + + if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_5G) || (band == WLC_BAND_2G)) { + + if ((error = dev_wlc_ioctl(dev, WLC_SET_BAND, + &band, sizeof(band))) >= 0) { + p += snprintf(p, MAX_WX_STRING, "OK"); + WL_TRACE(("%s: set band %d OK\n", __FUNCTION__, band)); + goto exit; + } else { + WL_ERROR(("%s: set band %d failed code %d\n", __FUNCTION__, + band, error)); + } + } else { + WL_ERROR(("%s Incorrect band setting %d, ignored\n", __FUNCTION__, band)); + } + } + + p += snprintf(p, MAX_WX_STRING, "FAIL"); + +exit: + wrqu->data.length = p - extra + 1; + net_os_wake_unlock(dev); + return error; +} + +#ifdef PNO_SUPPORT + +static int +wl_iw_set_pno_reset( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = -1; + char *p = extra; + + net_os_wake_lock(dev); + if ((g_onoff == G_WLAN_SET_ON) && (dev != NULL)) { + + if ((error = dhd_dev_pno_reset(dev)) >= 0) { + p += snprintf(p, MAX_WX_STRING, "OK"); + WL_TRACE(("%s: set OK\n", __FUNCTION__)); + goto exit; + } + else WL_ERROR(("%s: failed code %d\n", __FUNCTION__, error)); + } + + p += snprintf(p, MAX_WX_STRING, "FAIL"); + +exit: + wrqu->data.length = p - extra + 1; + net_os_wake_unlock(dev); + return error; +} + + + +static int +wl_iw_set_pno_enable( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error = -1; + char *p = extra; + int pfn_enabled; + + net_os_wake_lock(dev); + pfn_enabled = htod32((uint)*(extra + strlen(PNOENABLE_SET_CMD) + 1) - '0'); + + if ((g_onoff == G_WLAN_SET_ON) && (dev != NULL)) { + + if ((error = dhd_dev_pno_enable(dev, pfn_enabled)) >= 0) { + p += snprintf(p, MAX_WX_STRING, "OK"); + WL_TRACE(("%s: set OK\n", __FUNCTION__)); + goto exit; + } + else WL_ERROR(("%s: failed code %d\n", __FUNCTION__, error)); + } + + p += snprintf(p, MAX_WX_STRING, "FAIL"); + +exit: + wrqu->data.length = p - extra + 1; + net_os_wake_unlock(dev); + return error; +} + + + +static int +wl_iw_set_pno_set( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int res = -1; + wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT]; + int nssid = 0; + cmd_tlv_t *cmd_tlv_temp; + char *str_ptr; + int tlv_size_left; + int pno_time; + int pno_repeat; + int pno_freq_expo_max; +#ifdef PNO_SET_DEBUG + int i; + char pno_in_example[] = { + 'P', 'N', 'O', 'S', 'E', 'T', 'U', 'P', ' ', + 'S', '1', '2', '0', + 'S', + 0x04, + 'B', 'R', 'C', 'M', + 'S', + 0x04, + 'G', 'O', 'O', 'G', + 'T', + '1', 'E', + 'R', + '2', + 'M', + '2', + 0x00 + }; +#endif + + net_os_wake_lock(dev); + WL_ERROR(("\n### %s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n", + __FUNCTION__, info->cmd, info->flags, + wrqu->data.pointer, wrqu->data.length)); + + if (g_onoff == G_WLAN_SET_OFF) { + WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__)); + goto exit_proc; + } + + if (wrqu->data.length < (strlen(PNOSETUP_SET_CMD) + sizeof(cmd_tlv_t))) { + WL_ERROR(("%s argument=%d less %d\n", __FUNCTION__, + wrqu->data.length, (int)(strlen(PNOSETUP_SET_CMD) + sizeof(cmd_tlv_t)))); + goto exit_proc; + } + +#ifdef PNO_SET_DEBUG + if (!(extra = kmalloc(sizeof(pno_in_example) +100, GFP_KERNEL))) { + res = -ENOMEM; + goto exit_proc; + } + memcpy(extra, pno_in_example, sizeof(pno_in_example)); + wrqu->data.length = sizeof(pno_in_example); + for (i = 0; i < wrqu->data.length; i++) + printf("%02X ", extra[i]); + printf("\n"); +#endif + + str_ptr = extra; +#ifdef PNO_SET_DEBUG + str_ptr += strlen("PNOSETUP "); + tlv_size_left = wrqu->data.length - strlen("PNOSETUP "); +#else + str_ptr += strlen(PNOSETUP_SET_CMD); + tlv_size_left = wrqu->data.length - strlen(PNOSETUP_SET_CMD); +#endif + + cmd_tlv_temp = (cmd_tlv_t *)str_ptr; + memset(ssids_local, 0, sizeof(ssids_local)); + pno_repeat = pno_freq_expo_max = 0; + + if ((cmd_tlv_temp->prefix == PNO_TLV_PREFIX) && + (cmd_tlv_temp->version == PNO_TLV_VERSION) && + (cmd_tlv_temp->subver == PNO_TLV_SUBVERSION)) + { + str_ptr += sizeof(cmd_tlv_t); + tlv_size_left -= sizeof(cmd_tlv_t); + + + if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local, + MAX_PFN_LIST_COUNT, + &tlv_size_left)) <= 0) { + WL_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid)); + goto exit_proc; + } + else { + if ((str_ptr[0] != PNO_TLV_TYPE_TIME) || (tlv_size_left <= 1)) { + WL_ERROR(("%s scan duration corrupted field size %d\n", + __FUNCTION__, tlv_size_left)); + goto exit_proc; + } + str_ptr++; + pno_time = simple_strtoul(str_ptr, &str_ptr, 16); + WL_PNO(("%s: pno_time=%d\n", __FUNCTION__, pno_time)); + + + if (str_ptr[0] != 0) { + if ((str_ptr[0] != PNO_TLV_FREQ_REPEAT)) { + WL_ERROR(("%s pno repeat : corrupted field\n", + __FUNCTION__)); + goto exit_proc; + } + str_ptr++; + pno_repeat = simple_strtoul(str_ptr, &str_ptr, 16); + WL_PNO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat)); + if (str_ptr[0] != PNO_TLV_FREQ_EXPO_MAX) { + WL_ERROR(("%s FREQ_EXPO_MAX corrupted field size\n", + __FUNCTION__)); + goto exit_proc; + } + str_ptr++; + pno_freq_expo_max = simple_strtoul(str_ptr, &str_ptr, 16); + WL_PNO(("%s: pno_freq_expo_max=%d\n", + __FUNCTION__, pno_freq_expo_max)); + } + } + } + else { + WL_ERROR(("%s get wrong TLV command\n", __FUNCTION__)); + goto exit_proc; + } + + + res = dhd_dev_pno_set(dev, ssids_local, nssid, pno_time, pno_repeat, pno_freq_expo_max); + +exit_proc: + net_os_wake_unlock(dev); + return res; +} + +static int +wl_iw_set_pno_setadd( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int ret = -1; + char *tmp_ptr; + int size, tmp_size; + + net_os_wake_lock(dev); + WL_ERROR(("\n### %s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n", + __FUNCTION__, info->cmd, info->flags, + wrqu->data.pointer, wrqu->data.length)); + + if (g_onoff == G_WLAN_SET_OFF) { + WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__)); + goto exit_proc; + } + + if (wrqu->data.length <= strlen(PNOSETADD_SET_CMD) + sizeof(cmd_tlv_t)) { + WL_ERROR(("%s argument=%d less than %d\n", __FUNCTION__, + wrqu->data.length, (int)(strlen(PNOSETADD_SET_CMD) + sizeof(cmd_tlv_t)))); + goto exit_proc; + } + + + bcopy(PNOSETUP_SET_CMD, extra, strlen(PNOSETUP_SET_CMD)); + + tmp_ptr = extra + strlen(PNOSETUP_SET_CMD); + size = wrqu->data.length - strlen(PNOSETUP_SET_CMD); + tmp_size = size; + + while (*tmp_ptr && tmp_size > 0) { + if ((*tmp_ptr == 'S') && (size - tmp_size) >= sizeof(cmd_tlv_t)) { + *(tmp_ptr + 1) = ((*(tmp_ptr + 1) - '0') << 4) + (*(tmp_ptr + 2) - '0'); + memmove(tmp_ptr + 2, tmp_ptr + 3, tmp_size - 3); + tmp_size -= 2 + *(tmp_ptr + 1); + tmp_ptr += 2 + *(tmp_ptr + 1); + size--; + } else { + tmp_ptr++; + tmp_size--; + } + } + + wrqu->data.length = strlen(PNOSETUP_SET_CMD) + size; + ret = wl_iw_set_pno_set(dev, info, wrqu, extra); + +exit_proc: + net_os_wake_unlock(dev); + return ret; + +} +#endif + +static int +wl_iw_get_rssi( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + static int rssi = 0; + static wlc_ssid_t ssid = {0}; + int error = 0; + char *p = extra; + static char ssidbuf[SSID_FMT_BUF_LEN]; + scb_val_t scb_val; + + net_os_wake_lock(dev); + + bzero(&scb_val, sizeof(scb_val_t)); + + if (g_onoff == G_WLAN_SET_ON) { + error = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t)); + if (error) { + WL_ERROR(("%s: Fails %d\n", __FUNCTION__, error)); + net_os_wake_unlock(dev); + return error; + } + rssi = dtoh32(scb_val.val); + + error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid)); + if (!error) { + ssid.SSID_len = dtoh32(ssid.SSID_len); + wl_format_ssid(ssidbuf, ssid.SSID, dtoh32(ssid.SSID_len)); + } + } + + p += snprintf(p, MAX_WX_STRING, "%s rssi %d ", ssidbuf, rssi); + wrqu->data.length = p - extra + 1; + + net_os_wake_unlock(dev); + return error; +} + +int +wl_iw_send_priv_event( + struct net_device *dev, + char *flag +) +{ + union iwreq_data wrqu; + char extra[IW_CUSTOM_MAX + 1]; + int cmd; + + cmd = IWEVCUSTOM; + memset(&wrqu, 0, sizeof(wrqu)); + if (strlen(flag) > sizeof(extra)) + return -1; + + strcpy(extra, flag); + wrqu.data.length = strlen(extra); + wireless_send_event(dev, cmd, &wrqu, extra); + net_os_wake_lock_ctrl_timeout_enable(dev, DHD_EVENT_TIMEOUT_MS); + WL_TRACE(("Send IWEVCUSTOM Event as %s\n", extra)); + + return 0; +} + + +int +wl_control_wl_start(struct net_device *dev) +{ + wl_iw_t *iw; + int ret = 0; + + WL_TRACE(("Enter %s \n", __FUNCTION__)); + + if (!dev) { + WL_ERROR(("%s: dev is null\n", __FUNCTION__)); + return -1; + } + + iw = *(wl_iw_t **)netdev_priv(dev); + + if (!iw) { + WL_ERROR(("%s: wl is null\n", __FUNCTION__)); + return -1; + } + + dhd_net_if_lock(dev); + + if (g_onoff == G_WLAN_SET_OFF) { + dhd_customer_gpio_wlan_ctrl(WLAN_RESET_ON); + +#if defined(BCMLXSDMMC) + sdioh_start(NULL, 0); +#endif + + ret = dhd_dev_reset(dev, 0); + +#if defined(BCMLXSDMMC) + sdioh_start(NULL, 1); +#endif + if (!ret) + dhd_dev_init_ioctl(dev); + + g_onoff = G_WLAN_SET_ON; + } + WL_TRACE(("Exited %s\n", __FUNCTION__)); + + dhd_net_if_unlock(dev); + return ret; +} + + +static int +wl_iw_control_wl_off( + struct net_device *dev, + struct iw_request_info *info +) +{ + wl_iw_t *iw; + int ret = 0; + + WL_TRACE(("Enter %s\n", __FUNCTION__)); + + if (!dev) { + WL_ERROR(("%s: dev is null\n", __FUNCTION__)); + return -1; + } + + iw = *(wl_iw_t **)netdev_priv(dev); + + if (!iw) { + WL_ERROR(("%s: wl is null\n", __FUNCTION__)); + return -1; + } + + dhd_net_if_lock(dev); + +#ifdef SOFTAP + ap_cfg_running = FALSE; +#endif + + if (g_onoff == G_WLAN_SET_ON) { + g_onoff = G_WLAN_SET_OFF; + +#if defined(WL_IW_USE_ISCAN) + g_iscan->iscan_state = ISCAN_STATE_IDLE; +#endif + + ret = dhd_dev_reset(dev, 1); + +#if defined(WL_IW_USE_ISCAN) +#if !defined(CSCAN) + + wl_iw_free_ss_cache(); + wl_iw_run_ss_cache_timer(0); + + g_ss_cache_ctrl.m_link_down = 1; +#endif + memset(g_scan, 0, G_SCAN_RESULTS); + g_scan_specified_ssid = 0; +#if defined(CONFIG_FIRST_SCAN) + + g_first_broadcast_scan = BROADCAST_SCAN_FIRST_IDLE; + g_first_counter_scans = 0; +#endif +#endif + +#if defined(BCMLXSDMMC) + sdioh_stop(NULL); +#endif + + dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); + + wl_iw_send_priv_event(dev, "STOP"); + } + + dhd_net_if_unlock(dev); + + WL_TRACE(("Exited %s\n", __FUNCTION__)); + + return ret; +} + +static int +wl_iw_control_wl_on( + struct net_device *dev, + struct iw_request_info *info +) +{ + int ret = 0; + + WL_TRACE(("Enter %s \n", __FUNCTION__)); + + ret = wl_control_wl_start(dev); + + wl_iw_send_priv_event(dev, "START"); + +#ifdef SOFTAP + if (!ap_fw_loaded) { + wl_iw_iscan_set_scan_broadcast_prep(dev, 0); + } +#else + wl_iw_iscan_set_scan_broadcast_prep(dev, 0); +#endif + + WL_TRACE(("Exited %s\n", __FUNCTION__)); + + return ret; +} + +#ifdef SOFTAP +static struct ap_profile my_ap; +static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap); +static int get_assoc_sta_list(struct net_device *dev, char *buf, int len); +static int set_ap_mac_list(struct net_device *dev, void *buf); + +#define PTYPE_STRING 0 +#define PTYPE_INTDEC 1 +#define PTYPE_INTHEX 2 +#define PTYPE_STR_HEX 3 + +static int get_parameter_from_string( + char **str_ptr, const char *token, int param_type, void *dst, int param_max_len); + +static int +hex2num(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + + + +static int +hstr_2_buf(const char *txt, u8 *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) { + int a, b; + + a = hex2num(*txt++); + if (a < 0) + return -1; + b = hex2num(*txt++); + if (b < 0) + return -1; + *buf++ = (a << 4) | b; + } + + return 0; +} + + + +static int +init_ap_profile_from_string(char *param_str, struct ap_profile *ap_cfg) +{ + char *str_ptr = param_str; + char sub_cmd[16]; + int ret = 0; + + memset(sub_cmd, 0, sizeof(sub_cmd)); + memset(ap_cfg, 0, sizeof(struct ap_profile)); + + + if (get_parameter_from_string(&str_ptr, "ASCII_CMD=", + PTYPE_STRING, sub_cmd, SSID_LEN) != 0) { + return -1; + } + if (strncmp(sub_cmd, "AP_CFG", 6)) { + WL_ERROR(("ERROR: sub_cmd:%s != 'AP_CFG'!\n", sub_cmd)); + return -1; + } + + + + ret = get_parameter_from_string(&str_ptr, "SSID=", PTYPE_STRING, ap_cfg->ssid, SSID_LEN); + + ret |= get_parameter_from_string(&str_ptr, "SEC=", PTYPE_STRING, ap_cfg->sec, SEC_LEN); + + ret |= get_parameter_from_string(&str_ptr, "KEY=", PTYPE_STRING, ap_cfg->key, KEY_LEN); + + ret |= get_parameter_from_string(&str_ptr, "CHANNEL=", PTYPE_INTDEC, &ap_cfg->channel, 5); + + + get_parameter_from_string(&str_ptr, "PREAMBLE=", PTYPE_INTDEC, &ap_cfg->preamble, 5); + + + get_parameter_from_string(&str_ptr, "MAX_SCB=", PTYPE_INTDEC, &ap_cfg->max_scb, 5); + + + get_parameter_from_string(&str_ptr, "HIDDEN=", + PTYPE_INTDEC, &ap_cfg->closednet, 5); + + + get_parameter_from_string(&str_ptr, "COUNTRY=", + PTYPE_STRING, &ap_cfg->country_code, 3); + + return ret; +} +#endif + + + +#ifdef SOFTAP +static int +iwpriv_set_ap_config(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *ext) +{ + int res = 0; + char *extra = NULL; + struct ap_profile *ap_cfg = &my_ap; + + WL_TRACE(("> Got IWPRIV SET_AP IOCTL: info->cmd:%x, info->flags:%x, u.data:%p, u.len:%d\n", + info->cmd, info->flags, + wrqu->data.pointer, wrqu->data.length)); + + if (!ap_fw_loaded) { + WL_ERROR(("Can't execute %s(), SOFTAP fw is not Loaded\n", + __FUNCTION__)); + return -1; + } + + if (wrqu->data.length != 0) { + + char *str_ptr; + + if (!(extra = kmalloc(wrqu->data.length+1, GFP_KERNEL))) + return -ENOMEM; + + if (copy_from_user(extra, wrqu->data.pointer, wrqu->data.length)) { + kfree(extra); + return -EFAULT; + } + + extra[wrqu->data.length] = 0; + WL_SOFTAP((" Got str param in iw_point:\n %s\n", extra)); + + memset(ap_cfg, 0, sizeof(struct ap_profile)); + + + + str_ptr = extra; + + if ((res = init_ap_profile_from_string(extra, ap_cfg)) < 0) { + WL_ERROR(("%s failed to parse %d\n", __FUNCTION__, res)); + kfree(extra); + return -1; + } + + } else { + + WL_ERROR(("IWPRIV argument len = 0 \n")); + return -1; + } + + if ((res = set_ap_cfg(dev, ap_cfg)) < 0) + WL_ERROR(("%s failed to set_ap_cfg %d\n", __FUNCTION__, res)); + + kfree(extra); + + return res; +} +#endif + + + +#ifdef SOFTAP +static int iwpriv_get_assoc_list(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *p_iwrq, + char *extra) +{ + int i, ret = 0; + char mac_buf[256]; + struct maclist *sta_maclist = (struct maclist *)mac_buf; + + char mac_lst[384]; + char *p_mac_str; + char *p_mac_str_end; + wl_iw_t *iw; + + if ((!dev) || (!extra)) { + + return -EINVAL; + } + + + iw = *(wl_iw_t **)netdev_priv(dev); + + net_os_wake_lock(dev); + DHD_OS_MUTEX_LOCK(&wl_softap_lock); + + WL_TRACE(("\n %s: IWPRIV IOCTL: cmd:%hx, flags:%hx, extra:%p, iwp.len:%d," + "iwp.len:%p, iwp.flags:%x \n", __FUNCTION__, info->cmd, info->flags, + extra, p_iwrq->data.length, p_iwrq->data.pointer, p_iwrq->data.flags)); + + + memset(sta_maclist, 0, sizeof(mac_buf)); + + sta_maclist->count = 8; + + WL_SOFTAP(("%s: net device:%s, buf_sz:%d\n", + __FUNCTION__, dev->name, sizeof(mac_buf))); + + if ((ret = get_assoc_sta_list(dev, mac_buf, sizeof(mac_buf))) < 0) { + WL_ERROR(("%s: sta list ioctl error:%d\n", + __FUNCTION__, ret)); + goto func_exit; + } + + WL_SOFTAP(("%s: got %d stations\n", __FUNCTION__, + sta_maclist->count)); + + + + memset(mac_lst, 0, sizeof(mac_lst)); + p_mac_str = mac_lst; + p_mac_str_end = &mac_lst[sizeof(mac_lst)-1]; + + for (i = 0; i < 8; i++) { + struct ether_addr * id = &sta_maclist->ea[i]; + if (!ETHER_ISNULLADDR(id->octet)) { + scb_val_t scb_val; + int rssi = 0; + bzero(&scb_val, sizeof(scb_val_t)); + + + if ((p_mac_str_end - p_mac_str) <= 36) { + WL_ERROR(("%s: mac list buf is < 36 for item[%i] item\n", + __FUNCTION__, i)); + break; + } + + p_mac_str += snprintf(p_mac_str, MAX_WX_STRING, + "\nMac[%d]=%02X:%02X:%02X:%02X:%02X:%02X,", i, + id->octet[0], id->octet[1], id->octet[2], + id->octet[3], id->octet[4], id->octet[5]); + + + bcopy(id->octet, &scb_val.ea, 6); + ret = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t)); + if (ret < 0) { + snprintf(p_mac_str, MAX_WX_STRING, "RSSI:ERR"); + WL_ERROR(("%s: RSSI ioctl error:%d\n", + __FUNCTION__, ret)); + break; + } + + rssi = dtoh32(scb_val.val); + p_mac_str += snprintf(p_mac_str, MAX_WX_STRING, + "RSSI:%d", rssi); + } + } + + p_iwrq->data.length = strlen(mac_lst)+1; + + WL_SOFTAP(("%s: data to user:\n%s\n usr_ptr:%p\n", __FUNCTION__, + mac_lst, p_iwrq->data.pointer)); + + if (p_iwrq->data.length) { + bcopy(mac_lst, extra, p_iwrq->data.length); + } + +func_exit: + + DHD_OS_MUTEX_UNLOCK(&wl_softap_lock); + net_os_wake_unlock(dev); + + WL_SOFTAP(("%s: Exited\n", __FUNCTION__)); + return ret; +} +#endif + + +#ifdef SOFTAP + +#define MAC_FILT_MAX 8 +static int iwpriv_set_mac_filters(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *ext) +{ + int i, ret = -1; + char * extra = NULL; + int mac_cnt = 0; + int mac_mode = 0; + struct ether_addr *p_ea; + struct mac_list_set mflist_set; + + WL_SOFTAP((">>> Got IWPRIV SET_MAC_FILTER IOCTL: info->cmd:%x," + "info->flags:%x, u.data:%p, u.len:%d\n", + info->cmd, info->flags, + wrqu->data.pointer, wrqu->data.length)); + + if (wrqu->data.length != 0) { + + char *str_ptr; + + if (!(extra = kmalloc(wrqu->data.length+1, GFP_KERNEL))) + return -ENOMEM; + + if (copy_from_user(extra, wrqu->data.pointer, wrqu->data.length)) { + kfree(extra); + return -EFAULT; + } + + extra[wrqu->data.length] = 0; + WL_SOFTAP((" Got parameter string in iw_point:\n %s \n", extra)); + + memset(&mflist_set, 0, sizeof(mflist_set)); + + + str_ptr = extra; + + + + if (get_parameter_from_string(&str_ptr, "MAC_MODE=", + PTYPE_INTDEC, &mac_mode, 4) != 0) { + WL_ERROR(("ERROR: 'MAC_MODE=' token is missing\n")); + goto exit_proc; + } + + p_ea = &mflist_set.mac_list.ea[0]; + + if (get_parameter_from_string(&str_ptr, "MAC_CNT=", + PTYPE_INTDEC, &mac_cnt, 4) != 0) { + WL_ERROR(("ERROR: 'MAC_CNT=' token param is missing \n")); + goto exit_proc; + } + + if (mac_cnt > MAC_FILT_MAX) { + WL_ERROR(("ERROR: number of MAC filters > MAX\n")); + goto exit_proc; + } + + for (i=0; i< mac_cnt; i++) + if (get_parameter_from_string(&str_ptr, "MAC=", + PTYPE_STR_HEX, &p_ea[i], 12) != 0) { + WL_ERROR(("ERROR: MAC_filter[%d] is missing !\n", i)); + goto exit_proc; + } + + WL_SOFTAP(("MAC_MODE=:%d, MAC_CNT=%d, MACs:..\n", mac_mode, mac_cnt)); + for (i = 0; i < mac_cnt; i++) { + WL_SOFTAP(("mac_filt[%d]:", i)); + dhd_print_buf(&p_ea[i], 6, 0); + } + + + mflist_set.mode = mac_mode; + mflist_set.mac_list.count = mac_cnt; + set_ap_mac_list(dev, &mflist_set); + + + wrqu->data.pointer = NULL; + wrqu->data.length = 0; + ret = 0; + + } else { + + WL_ERROR(("IWPRIV argument len is 0\n")); + return -1; + } + + exit_proc: + kfree(extra); + return ret; +} +#endif + + +#ifdef SOFTAP + +static int iwpriv_set_ap_sta_disassoc(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *ext) +{ + int res = 0; + char sta_mac[6] = {0, 0, 0, 0, 0, 0}; + char cmd_buf[256]; + char *str_ptr = cmd_buf; + + WL_SOFTAP((">>%s called\n args: info->cmd:%x," + " info->flags:%x, u.data.p:%p, u.data.len:%d\n", + __FUNCTION__, info->cmd, info->flags, + wrqu->data.pointer, wrqu->data.length)); + + if (wrqu->data.length != 0) { + + if (copy_from_user(cmd_buf, wrqu->data.pointer, wrqu->data.length)) { + return -EFAULT; + } + + if (get_parameter_from_string(&str_ptr, + "MAC=", PTYPE_STR_HEX, sta_mac, 12) == 0) { + res = wl_iw_softap_deassoc_stations(dev, sta_mac); + } else { + WL_ERROR(("ERROR: STA_MAC= token not found\n")); + } + } + + return res; +} +#endif + +#endif + +#if WIRELESS_EXT < 13 +struct iw_request_info +{ + __u16 cmd; + __u16 flags; +}; + +typedef int (*iw_handler)(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra); +#endif + +static int +wl_iw_config_commit( + struct net_device *dev, + struct iw_request_info *info, + void *zwrq, + char *extra +) +{ + wlc_ssid_t ssid; + int error; + struct sockaddr bssid; + + WL_TRACE(("%s: SIOCSIWCOMMIT\n", dev->name)); + + if ((error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid)))) + return error; + + ssid.SSID_len = dtoh32(ssid.SSID_len); + + if (!ssid.SSID_len) + return 0; + + bzero(&bssid, sizeof(struct sockaddr)); + if ((error = dev_wlc_ioctl(dev, WLC_REASSOC, &bssid, ETHER_ADDR_LEN))) { + WL_ERROR(("%s: WLC_REASSOC to %s failed \n", __FUNCTION__, ssid.SSID)); + return error; + } + + return 0; +} + +static int +wl_iw_get_name( + struct net_device *dev, + struct iw_request_info *info, + char *cwrq, + char *extra +) +{ + WL_TRACE(("%s: SIOCGIWNAME\n", dev->name)); + + strcpy(cwrq, "IEEE 802.11-DS"); + + return 0; +} + +static int +wl_iw_set_freq( + struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *fwrq, + char *extra +) +{ + int error, chan; + uint sf = 0; + + WL_TRACE(("%s %s: SIOCSIWFREQ\n", __FUNCTION__, dev->name)); + +#if defined(SOFTAP) + if (ap_cfg_running) { + WL_TRACE(("%s:>> not executed, 'SOFT_AP is active' \n", __FUNCTION__)); + return 0; + } +#endif + + + if (fwrq->e == 0 && fwrq->m < MAXCHANNEL) { + chan = fwrq->m; + } + + else { + + if (fwrq->e >= 6) { + fwrq->e -= 6; + while (fwrq->e--) + fwrq->m *= 10; + } else if (fwrq->e < 6) { + while (fwrq->e++ < 6) + fwrq->m /= 10; + } + + if (fwrq->m > 4000 && fwrq->m < 5000) + sf = WF_CHAN_FACTOR_4_G; + + chan = wf_mhz2channel(fwrq->m, sf); + } + + chan = htod32(chan); + + if ((error = dev_wlc_ioctl(dev, WLC_SET_CHANNEL, &chan, sizeof(chan)))) + return error; + + g_wl_iw_params.target_channel = chan; + + + return -EINPROGRESS; +} + +static int +wl_iw_get_freq( + struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *fwrq, + char *extra +) +{ + channel_info_t ci; + int error; + + WL_TRACE(("%s: SIOCGIWFREQ\n", dev->name)); + + if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci)))) + return error; + + + fwrq->m = dtoh32(ci.hw_channel); + fwrq->e = dtoh32(0); + return 0; +} + +static int +wl_iw_set_mode( + struct net_device *dev, + struct iw_request_info *info, + __u32 *uwrq, + char *extra +) +{ + int infra = 0, ap = 0, error = 0; + + WL_TRACE(("%s: SIOCSIWMODE\n", dev->name)); + + switch (*uwrq) { + case IW_MODE_MASTER: + infra = ap = 1; + break; + case IW_MODE_ADHOC: + case IW_MODE_AUTO: + break; + case IW_MODE_INFRA: + infra = 1; + break; + default: + return -EINVAL; + } + infra = htod32(infra); + ap = htod32(ap); + + if ((error = dev_wlc_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(infra))) || + (error = dev_wlc_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap)))) + return error; + + + return -EINPROGRESS; +} + +static int +wl_iw_get_mode( + struct net_device *dev, + struct iw_request_info *info, + __u32 *uwrq, + char *extra +) +{ + int error, infra = 0, ap = 0; + + WL_TRACE(("%s: SIOCGIWMODE\n", dev->name)); + + if ((error = dev_wlc_ioctl(dev, WLC_GET_INFRA, &infra, sizeof(infra))) || + (error = dev_wlc_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap)))) + return error; + + infra = dtoh32(infra); + ap = dtoh32(ap); + *uwrq = infra ? ap ? IW_MODE_MASTER : IW_MODE_INFRA : IW_MODE_ADHOC; + + return 0; +} + +static int +wl_iw_get_range( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + struct iw_range *range = (struct iw_range *) extra; + wl_uint32_list_t *list; + wl_rateset_t rateset; + int8 *channels; + int error, i, k; + uint sf, ch; + + int phytype; + int bw_cap = 0, sgi_tx = 0, nmode = 0; + channel_info_t ci; + uint8 nrate_list2copy = 0; + uint16 nrate_list[4][8] = { {13, 26, 39, 52, 78, 104, 117, 130}, + {14, 29, 43, 58, 87, 116, 130, 144}, + {27, 54, 81, 108, 162, 216, 243, 270}, + {30, 60, 90, 120, 180, 240, 270, 300}}; + + WL_TRACE(("%s: SIOCGIWRANGE\n", dev->name)); + + if (!extra) + return -EINVAL; + + channels = kmalloc((MAXCHANNEL+1)*4, GFP_KERNEL); + if (!channels) { + WL_ERROR(("Could not alloc channels\n")); + return -ENOMEM; + } + list = (wl_uint32_list_t *)channels; + + dwrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(*range)); + + + range->min_nwid = range->max_nwid = 0; + + + list->count = htod32(MAXCHANNEL); + if ((error = dev_wlc_ioctl(dev, WLC_GET_VALID_CHANNELS, channels, (MAXCHANNEL+1)*4))) { + kfree(channels); + return error; + } + for (i = 0; i < dtoh32(list->count) && i < IW_MAX_FREQUENCIES; i++) { + range->freq[i].i = dtoh32(list->element[i]); + + ch = dtoh32(list->element[i]); + if (ch <= CH_MAX_2G_CHANNEL) + sf = WF_CHAN_FACTOR_2_4_G; + else + sf = WF_CHAN_FACTOR_5_G; + + range->freq[i].m = wf_channel2mhz(ch, sf); + range->freq[i].e = 6; + } + range->num_frequency = range->num_channels = i; + + + range->max_qual.qual = 5; + + range->max_qual.level = 0x100 - 200; + + range->max_qual.noise = 0x100 - 200; + + range->sensitivity = 65535; + +#if WIRELESS_EXT > 11 + + range->avg_qual.qual = 3; + + range->avg_qual.level = 0x100 + WL_IW_RSSI_GOOD; + + range->avg_qual.noise = 0x100 - 75; +#endif + + + if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset)))) { + kfree(channels); + return error; + } + rateset.count = dtoh32(rateset.count); + range->num_bitrates = rateset.count; + for (i = 0; i < rateset.count && i < IW_MAX_BITRATES; i++) + range->bitrate[i] = (rateset.rates[i]& 0x7f) * 500000; + dev_wlc_intvar_get(dev, "nmode", &nmode); + dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &phytype, sizeof(phytype)); + + if (nmode == 1 && phytype == WLC_PHY_TYPE_SSN) { + dev_wlc_intvar_get(dev, "mimo_bw_cap", &bw_cap); + dev_wlc_intvar_get(dev, "sgi_tx", &sgi_tx); + dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(channel_info_t)); + ci.hw_channel = dtoh32(ci.hw_channel); + + if (bw_cap == 0 || + (bw_cap == 2 && ci.hw_channel <= 14)) { + if (sgi_tx == 0) + nrate_list2copy = 0; + else + nrate_list2copy = 1; + } + if (bw_cap == 1 || + (bw_cap == 2 && ci.hw_channel >= 36)) { + if (sgi_tx == 0) + nrate_list2copy = 2; + else + nrate_list2copy = 3; + } + range->num_bitrates += 8; + for (k = 0; i < range->num_bitrates; k++, i++) { + + range->bitrate[i] = (nrate_list[nrate_list2copy][k]) * 500000; + } + } + + + if ((error = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &i, sizeof(i)))) { + kfree(channels); + return error; + } + i = dtoh32(i); + if (i == WLC_PHY_TYPE_A) + range->throughput = 24000000; + else + range->throughput = 1500000; + + + range->min_rts = 0; + range->max_rts = 2347; + range->min_frag = 256; + range->max_frag = 2346; + + range->max_encoding_tokens = DOT11_MAX_DEFAULT_KEYS; + range->num_encoding_sizes = 4; + range->encoding_size[0] = WEP1_KEY_SIZE; + range->encoding_size[1] = WEP128_KEY_SIZE; +#if WIRELESS_EXT > 17 + range->encoding_size[2] = TKIP_KEY_SIZE; +#else + range->encoding_size[2] = 0; +#endif + range->encoding_size[3] = AES_KEY_SIZE; + + + range->min_pmp = 0; + range->max_pmp = 0; + range->min_pmt = 0; + range->max_pmt = 0; + range->pmp_flags = 0; + range->pm_capa = 0; + + + range->num_txpower = 2; + range->txpower[0] = 1; + range->txpower[1] = 255; + range->txpower_capa = IW_TXPOW_MWATT; + +#if WIRELESS_EXT > 10 + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 19; + + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT; + range->r_time_flags = 0; + + range->min_retry = 1; + range->max_retry = 255; + + range->min_r_time = 0; + range->max_r_time = 0; +#endif + +#if WIRELESS_EXT > 17 + range->enc_capa = IW_ENC_CAPA_WPA; + range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP; + range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP; + range->enc_capa |= IW_ENC_CAPA_WPA2; + + + IW_EVENT_CAPA_SET_KERNEL(range->event_capa); + + IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); + IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); + IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP); + IW_EVENT_CAPA_SET(range->event_capa, IWEVMICHAELMICFAILURE); + IW_EVENT_CAPA_SET(range->event_capa, IWEVASSOCREQIE); + IW_EVENT_CAPA_SET(range->event_capa, IWEVASSOCRESPIE); + IW_EVENT_CAPA_SET(range->event_capa, IWEVPMKIDCAND); +#endif + + kfree(channels); + + return 0; +} + +static int +rssi_to_qual(int rssi) +{ + if (rssi <= WL_IW_RSSI_NO_SIGNAL) + return 0; + else if (rssi <= WL_IW_RSSI_VERY_LOW) + return 1; + else if (rssi <= WL_IW_RSSI_LOW) + return 2; + else if (rssi <= WL_IW_RSSI_GOOD) + return 3; + else if (rssi <= WL_IW_RSSI_VERY_GOOD) + return 4; + else + return 5; +} + +static int +wl_iw_set_spy( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_iw_t *iw = NETDEV_PRIV(dev); + struct sockaddr *addr = (struct sockaddr *) extra; + int i; + + WL_TRACE(("%s: SIOCSIWSPY\n", dev->name)); + + if (!extra) + return -EINVAL; + + iw->spy_num = MIN(ARRAYSIZE(iw->spy_addr), dwrq->length); + for (i = 0; i < iw->spy_num; i++) + memcpy(&iw->spy_addr[i], addr[i].sa_data, ETHER_ADDR_LEN); + memset(iw->spy_qual, 0, sizeof(iw->spy_qual)); + + return 0; +} + +static int +wl_iw_get_spy( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_iw_t *iw = NETDEV_PRIV(dev); + struct sockaddr *addr = (struct sockaddr *) extra; + struct iw_quality *qual = (struct iw_quality *) &addr[iw->spy_num]; + int i; + + WL_TRACE(("%s: SIOCGIWSPY\n", dev->name)); + + if (!extra) + return -EINVAL; + + dwrq->length = iw->spy_num; + for (i = 0; i < iw->spy_num; i++) { + memcpy(addr[i].sa_data, &iw->spy_addr[i], ETHER_ADDR_LEN); + addr[i].sa_family = AF_UNIX; + memcpy(&qual[i], &iw->spy_qual[i], sizeof(struct iw_quality)); + iw->spy_qual[i].updated = 0; + } + + return 0; +} + + +static int +wl_iw_ch_to_chanspec(int ch, wl_join_params_t *join_params, int *join_params_size) +{ + chanspec_t chanspec = 0; + + if (ch != 0) { + + join_params->params.chanspec_num = 1; + join_params->params.chanspec_list[0] = ch; + + if (join_params->params.chanspec_list[0]) + chanspec |= WL_CHANSPEC_BAND_2G; + else + chanspec |= WL_CHANSPEC_BAND_5G; + + chanspec |= WL_CHANSPEC_BW_20; + chanspec |= WL_CHANSPEC_CTL_SB_NONE; + + + *join_params_size += WL_ASSOC_PARAMS_FIXED_SIZE + + join_params->params.chanspec_num * sizeof(chanspec_t); + + + join_params->params.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK; + join_params->params.chanspec_list[0] |= chanspec; + join_params->params.chanspec_list[0] = + htodchanspec(join_params->params.chanspec_list[0]); + + join_params->params.chanspec_num = htod32(join_params->params.chanspec_num); + + WL_TRACE(("%s join_params->params.chanspec_list[0]= %X\n", + __FUNCTION__, join_params->params.chanspec_list[0])); + } + return 1; +} + +static int +wl_iw_set_wap( + struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *awrq, + char *extra +) +{ + int error = -EINVAL; + wl_join_params_t join_params; + int join_params_size; + + WL_TRACE(("%s: SIOCSIWAP\n", dev->name)); + + if (awrq->sa_family != ARPHRD_ETHER) { + WL_ERROR(("Invalid Header...sa_family\n")); + return -EINVAL; + } + + + if (ETHER_ISBCAST(awrq->sa_data) || ETHER_ISNULLADDR(awrq->sa_data)) { + scb_val_t scbval; + + bzero(&scbval, sizeof(scb_val_t)); + + (void) dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t)); + return 0; + } + + + + memset(&join_params, 0, sizeof(join_params)); + join_params_size = sizeof(join_params.ssid); + + memcpy(join_params.ssid.SSID, g_ssid.SSID, g_ssid.SSID_len); + join_params.ssid.SSID_len = htod32(g_ssid.SSID_len); + memcpy(&join_params.params.bssid, awrq->sa_data, ETHER_ADDR_LEN); + + + + WL_TRACE(("%s target_channel=%d\n", __FUNCTION__, g_wl_iw_params.target_channel)); + wl_iw_ch_to_chanspec(g_wl_iw_params.target_channel, &join_params, &join_params_size); + + if ((error = dev_wlc_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size))) { + WL_ERROR(("%s Invalid ioctl data=%d\n", __FUNCTION__, error)); + return error; + } + + if (g_ssid.SSID_len) { + WL_TRACE(("%s: join SSID=%s BSSID="MACSTR" ch=%d\n", __FUNCTION__, + g_ssid.SSID, MAC2STR((u8 *)awrq->sa_data), + g_wl_iw_params.target_channel)); + } + + + memset(&g_ssid, 0, sizeof(g_ssid)); + return 0; +} + +static int +wl_iw_get_wap( + struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *awrq, + char *extra +) +{ + WL_TRACE(("%s: SIOCGIWAP\n", dev->name)); + + awrq->sa_family = ARPHRD_ETHER; + memset(awrq->sa_data, 0, ETHER_ADDR_LEN); + + + (void) dev_wlc_ioctl(dev, WLC_GET_BSSID, awrq->sa_data, ETHER_ADDR_LEN); + + return 0; +} + +#if WIRELESS_EXT > 17 +static int +wl_iw_mlme( + struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *awrq, + char *extra +) +{ + struct iw_mlme *mlme; + scb_val_t scbval; + int error = -EINVAL; + + WL_TRACE(("%s: SIOCSIWMLME DISASSOC/DEAUTH\n", dev->name)); + + mlme = (struct iw_mlme *)extra; + if (mlme == NULL) { + WL_ERROR(("Invalid ioctl data.\n")); + return error; + } + + scbval.val = mlme->reason_code; + bcopy(&mlme->addr.sa_data, &scbval.ea, ETHER_ADDR_LEN); + + if (mlme->cmd == IW_MLME_DISASSOC) { + scbval.val = htod32(scbval.val); + error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t)); + } + else if (mlme->cmd == IW_MLME_DEAUTH) { + scbval.val = htod32(scbval.val); + error = dev_wlc_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scbval, + sizeof(scb_val_t)); + } + else { + WL_ERROR(("Invalid ioctl data.\n")); + return error; + } + + return error; +} +#endif + +#ifndef WL_IW_USE_ISCAN +static int +wl_iw_get_aplist( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_scan_results_t *list; + struct sockaddr *addr = (struct sockaddr *) extra; + struct iw_quality qual[IW_MAX_AP]; + wl_bss_info_t *bi = NULL; + int error, i; + uint buflen = dwrq->length; + + WL_TRACE(("%s: SIOCGIWAPLIST\n", dev->name)); + + if (!extra) + return -EINVAL; + + + list = kmalloc(buflen, GFP_KERNEL); + if (!list) + return -ENOMEM; + memset(list, 0, buflen); + list->buflen = htod32(buflen); + if ((error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, buflen))) { + WL_ERROR(("%d: Scan results error %d\n", __LINE__, error)); + kfree(list); + return error; + } + list->buflen = dtoh32(list->buflen); + list->version = dtoh32(list->version); + list->count = dtoh32(list->count); + if (list->version != WL_BSS_INFO_VERSION) { + WL_ERROR(("%s : list->version %d != WL_BSS_INFO_VERSION\n", + __FUNCTION__, list->version)); + kfree(list); + return -EINVAL; + } + + for (i = 0, dwrq->length = 0; i < list->count && dwrq->length < IW_MAX_AP; i++) { + bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info; + ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list + + buflen)); + + + if (!(dtoh16(bi->capability) & DOT11_CAP_ESS)) + continue; + + + memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN); + addr[dwrq->length].sa_family = ARPHRD_ETHER; + qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI)); + qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI); + qual[dwrq->length].noise = 0x100 + bi->phy_noise; + + +#if WIRELESS_EXT > 18 + qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; +#else + qual[dwrq->length].updated = 7; +#endif + + dwrq->length++; + } + + kfree(list); + + if (dwrq->length) { + memcpy(&addr[dwrq->length], qual, sizeof(struct iw_quality) * dwrq->length); + + dwrq->flags = 1; + } + + return 0; +} +#endif + +#ifdef WL_IW_USE_ISCAN +static int +wl_iw_iscan_get_aplist( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_scan_results_t *list; + iscan_buf_t * buf; + iscan_info_t *iscan = g_iscan; + + struct sockaddr *addr = (struct sockaddr *) extra; + struct iw_quality qual[IW_MAX_AP]; + wl_bss_info_t *bi = NULL; + int i; + + WL_TRACE(("%s: SIOCGIWAPLIST\n", dev->name)); + + if (!extra) + return -EINVAL; + + if ((!iscan) || (iscan->tsk_ctl.thr_pid < 0)) { + WL_ERROR(("%s error\n", __FUNCTION__)); + return 0; + } + + buf = iscan->list_hdr; + + while (buf) { + list = &((wl_iscan_results_t*)buf->iscan_buf)->results; + if (list->version != WL_BSS_INFO_VERSION) { + WL_ERROR(("%s : list->version %d != WL_BSS_INFO_VERSION\n", + __FUNCTION__, list->version)); + return -EINVAL; + } + + bi = NULL; + for (i = 0, dwrq->length = 0; i < list->count && dwrq->length < IW_MAX_AP; i++) { + bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) + : list->bss_info; + ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list + + WLC_IW_ISCAN_MAXLEN)); + + + if (!(dtoh16(bi->capability) & DOT11_CAP_ESS)) + continue; + + + memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN); + addr[dwrq->length].sa_family = ARPHRD_ETHER; + qual[dwrq->length].qual = rssi_to_qual(dtoh16(bi->RSSI)); + qual[dwrq->length].level = 0x100 + dtoh16(bi->RSSI); + qual[dwrq->length].noise = 0x100 + bi->phy_noise; + + +#if WIRELESS_EXT > 18 + qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; +#else + qual[dwrq->length].updated = 7; +#endif + + dwrq->length++; + } + buf = buf->next; + } + if (dwrq->length) { + memcpy(&addr[dwrq->length], qual, sizeof(struct iw_quality) * dwrq->length); + + dwrq->flags = 1; + } + + return 0; +} + +static int +wl_iw_iscan_prep(wl_scan_params_t *params, wlc_ssid_t *ssid) +{ + int err = 0; + + memcpy(¶ms->bssid, ðer_bcast, ETHER_ADDR_LEN); + params->bss_type = DOT11_BSSTYPE_ANY; + params->scan_type = 0; + params->nprobes = -1; + params->active_time = -1; + params->passive_time = -1; + params->home_time = -1; + params->channel_num = 0; + +#if defined(CONFIG_FIRST_SCAN) + + if (g_first_broadcast_scan == BROADCAST_SCAN_FIRST_STARTED) + params->passive_time = 30; +#endif + params->nprobes = htod32(params->nprobes); + params->active_time = htod32(params->active_time); + params->passive_time = htod32(params->passive_time); + params->home_time = htod32(params->home_time); + if (ssid && ssid->SSID_len) + memcpy(¶ms->ssid, ssid, sizeof(wlc_ssid_t)); + + return err; +} + +static int +wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, uint16 action) +{ + int err = 0; + + iscan->iscan_ex_params_p->version = htod32(ISCAN_REQ_VERSION); + iscan->iscan_ex_params_p->action = htod16(action); + iscan->iscan_ex_params_p->scan_duration = htod16(0); + + WL_SCAN(("%s : nprobes=%d\n", __FUNCTION__, iscan->iscan_ex_params_p->params.nprobes)); + WL_SCAN(("active_time=%d\n", iscan->iscan_ex_params_p->params.active_time)); + WL_SCAN(("passive_time=%d\n", iscan->iscan_ex_params_p->params.passive_time)); + WL_SCAN(("home_time=%d\n", iscan->iscan_ex_params_p->params.home_time)); + WL_SCAN(("scan_type=%d\n", iscan->iscan_ex_params_p->params.scan_type)); + WL_SCAN(("bss_type=%d\n", iscan->iscan_ex_params_p->params.bss_type)); + + if ((dev_iw_iovar_setbuf(iscan->dev, "iscan", iscan->iscan_ex_params_p, + iscan->iscan_ex_param_size, iscan->ioctlbuf, sizeof(iscan->ioctlbuf)))) { + WL_ERROR(("Set ISCAN for %s failed with %d\n", __FUNCTION__, err)); + err = -1; + } + + return err; +} + +static void +wl_iw_timerfunc(ulong data) +{ + iscan_info_t *iscan = (iscan_info_t *)data; + if (iscan) { + iscan->timer_on = 0; + if (iscan->iscan_state != ISCAN_STATE_IDLE) { + WL_TRACE(("timer trigger\n")); + up(&iscan->tsk_ctl.sema); + } + } +} + +static void +wl_iw_set_event_mask(struct net_device *dev) +{ + char eventmask[WL_EVENTING_MASK_LEN]; + char iovbuf[WL_EVENTING_MASK_LEN + 12]; + + dev_iw_iovar_getbuf(dev, "event_msgs", "", 0, iovbuf, sizeof(iovbuf)); + bcopy(iovbuf, eventmask, WL_EVENTING_MASK_LEN); + setbit(eventmask, WLC_E_SCAN_COMPLETE); + dev_iw_iovar_setbuf(dev, "event_msgs", eventmask, WL_EVENTING_MASK_LEN, + iovbuf, sizeof(iovbuf)); +} + +static uint32 +wl_iw_iscan_get(iscan_info_t *iscan) +{ + iscan_buf_t * buf; + iscan_buf_t * ptr; + wl_iscan_results_t * list_buf; + wl_iscan_results_t list; + wl_scan_results_t *results; + uint32 status; + int res = 0; + + DHD_OS_MUTEX_LOCK(&wl_cache_lock); + if (iscan->list_cur) { + buf = iscan->list_cur; + iscan->list_cur = buf->next; + } + else { + buf = kmalloc(sizeof(iscan_buf_t), GFP_KERNEL); + if (!buf) { + WL_ERROR(("%s can't alloc iscan_buf_t : going to abort currect iscan\n", + __FUNCTION__)); + DHD_OS_MUTEX_UNLOCK(&wl_cache_lock); + return WL_SCAN_RESULTS_NO_MEM; + } + buf->next = NULL; + if (!iscan->list_hdr) + iscan->list_hdr = buf; + else { + ptr = iscan->list_hdr; + while (ptr->next) { + ptr = ptr->next; + } + ptr->next = buf; + } + } + memset(buf->iscan_buf, 0, WLC_IW_ISCAN_MAXLEN); + list_buf = (wl_iscan_results_t*)buf->iscan_buf; + results = &list_buf->results; + results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE; + results->version = 0; + results->count = 0; + + memset(&list, 0, sizeof(list)); + list.results.buflen = htod32(WLC_IW_ISCAN_MAXLEN); + res = dev_iw_iovar_getbuf( + iscan->dev, + "iscanresults", + &list, + WL_ISCAN_RESULTS_FIXED_SIZE, + buf->iscan_buf, + WLC_IW_ISCAN_MAXLEN); + if (res == 0) { + results->buflen = dtoh32(results->buflen); + results->version = dtoh32(results->version); + results->count = dtoh32(results->count); + WL_TRACE(("results->count = %d\n", results->count)); + WL_TRACE(("results->buflen = %d\n", results->buflen)); + status = dtoh32(list_buf->status); + } else { + WL_ERROR(("%s returns error %d\n", __FUNCTION__, res)); + + status = WL_SCAN_RESULTS_NO_MEM; + } + DHD_OS_MUTEX_UNLOCK(&wl_cache_lock); + return status; +} + +static void +wl_iw_force_specific_scan(iscan_info_t *iscan) +{ + WL_TRACE(("%s force Specific SCAN for %s\n", __FUNCTION__, g_specific_ssid.SSID)); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + rtnl_lock(); +#endif + + (void) dev_wlc_ioctl(iscan->dev, WLC_SCAN, &g_specific_ssid, sizeof(g_specific_ssid)); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + rtnl_unlock(); +#endif +} + +static void +wl_iw_send_scan_complete(iscan_info_t *iscan) +{ + union iwreq_data wrqu; + + memset(&wrqu, 0, sizeof(wrqu)); + + + wireless_send_event(iscan->dev, SIOCGIWSCAN, &wrqu, NULL); +#if defined(CONFIG_FIRST_SCAN) + if (g_first_broadcast_scan == BROADCAST_SCAN_FIRST_STARTED) + g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_READY; +#endif + WL_TRACE(("Send Event ISCAN complete\n")); +} + +static int +_iscan_sysioc_thread(void *data) +{ + uint32 status; + + tsk_ctl_t *tsk_ctl = (tsk_ctl_t *)data; + iscan_info_t *iscan = (iscan_info_t *) tsk_ctl->parent; + + + static bool iscan_pass_abort = FALSE; + + DAEMONIZE("iscan_sysioc"); + + status = WL_SCAN_RESULTS_PARTIAL; + + + complete(&tsk_ctl->completed); + + while (down_interruptible(&tsk_ctl->sema) == 0) { + + SMP_RD_BARRIER_DEPENDS(); + if (tsk_ctl->terminated) { + break; + } +#if defined(SOFTAP) + + if (ap_cfg_running) { + WL_TRACE(("%s skipping SCAN ops in AP mode !!!\n", __FUNCTION__)); + net_os_wake_unlock(iscan->dev); + continue; + } +#endif + + if (iscan->timer_on) { + + iscan->timer_on = 0; + del_timer_sync(&iscan->timer); + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + rtnl_lock(); +#endif + status = wl_iw_iscan_get(iscan); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + rtnl_unlock(); +#endif + + if (g_scan_specified_ssid && (iscan_pass_abort == TRUE)) { + WL_TRACE(("%s Get results from specific scan status=%d\n", __FUNCTION__, status)); + wl_iw_send_scan_complete(iscan); + iscan_pass_abort = FALSE; + status = -1; + } + + switch (status) { + case WL_SCAN_RESULTS_PARTIAL: + WL_TRACE(("iscanresults incomplete\n")); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + rtnl_lock(); +#endif + + wl_iw_iscan(iscan, NULL, WL_SCAN_ACTION_CONTINUE); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + rtnl_unlock(); +#endif + + mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000); + iscan->timer_on = 1; + break; + case WL_SCAN_RESULTS_SUCCESS: + WL_TRACE(("iscanresults complete\n")); + iscan->iscan_state = ISCAN_STATE_IDLE; + wl_iw_send_scan_complete(iscan); + break; + case WL_SCAN_RESULTS_PENDING: + WL_TRACE(("iscanresults pending\n")); + + mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000); + iscan->timer_on = 1; + break; + case WL_SCAN_RESULTS_ABORTED: + WL_TRACE(("iscanresults aborted\n")); + iscan->iscan_state = ISCAN_STATE_IDLE; + if (g_scan_specified_ssid == 0) + wl_iw_send_scan_complete(iscan); + else { + iscan_pass_abort = TRUE; + wl_iw_force_specific_scan(iscan); + } + break; + case WL_SCAN_RESULTS_NO_MEM: + WL_TRACE(("iscanresults can't alloc memory: skip\n")); + iscan->iscan_state = ISCAN_STATE_IDLE; + break; + default: + WL_TRACE(("iscanresults returned unknown status %d\n", status)); + break; + } + + net_os_wake_unlock(iscan->dev); + } + + if (iscan->timer_on) { + iscan->timer_on = 0; + del_timer_sync(&iscan->timer); + } + complete_and_exit(&tsk_ctl->completed, 0); +} +#endif + +#if !defined(CSCAN) + +static void +wl_iw_set_ss_cache_timer_flag(void) +{ + g_ss_cache_ctrl.m_timer_expired = 1; + WL_TRACE(("%s called\n", __FUNCTION__)); +} + + +static int +wl_iw_init_ss_cache_ctrl(void) +{ + WL_TRACE(("%s :\n", __FUNCTION__)); + g_ss_cache_ctrl.m_prev_scan_mode = 0; + g_ss_cache_ctrl.m_cons_br_scan_cnt = 0; + g_ss_cache_ctrl.m_cache_head = NULL; + g_ss_cache_ctrl.m_link_down = 0; + g_ss_cache_ctrl.m_timer_expired = 0; + memset(g_ss_cache_ctrl.m_active_bssid, 0, ETHER_ADDR_LEN); + + g_ss_cache_ctrl.m_timer = kmalloc(sizeof(struct timer_list), GFP_KERNEL); + if (!g_ss_cache_ctrl.m_timer) { + return -ENOMEM; + } + g_ss_cache_ctrl.m_timer->function = (void *)wl_iw_set_ss_cache_timer_flag; + init_timer(g_ss_cache_ctrl.m_timer); + + return 0; +} + + + +static void +wl_iw_free_ss_cache(void) +{ + wl_iw_ss_cache_t *node, *cur; + wl_iw_ss_cache_t **spec_scan_head; + + WL_TRACE(("%s called\n", __FUNCTION__)); + + DHD_OS_MUTEX_LOCK(&wl_cache_lock); + spec_scan_head = &g_ss_cache_ctrl.m_cache_head; + node = *spec_scan_head; + + for (;node;) { + WL_TRACE(("%s : SSID - %s\n", __FUNCTION__, node->bss_info->SSID)); + cur = node; + node = cur->next; + kfree(cur); + } + *spec_scan_head = NULL; + DHD_OS_MUTEX_UNLOCK(&wl_cache_lock); +} + + + +static int +wl_iw_run_ss_cache_timer(int kick_off) +{ + struct timer_list **timer; + + timer = &g_ss_cache_ctrl.m_timer; + + if (*timer) { + if (kick_off) { +#ifdef CONFIG_PRESCANNED + (*timer)->expires = jiffies + 70000 * HZ / 1000; +#else + (*timer)->expires = jiffies + 30000 * HZ / 1000; +#endif + add_timer(*timer); + WL_TRACE(("%s : timer starts \n", __FUNCTION__)); + } else { + del_timer_sync(*timer); + WL_TRACE(("%s : timer stops \n", __FUNCTION__)); + } + } + + return 0; +} + + +static void +wl_iw_release_ss_cache_ctrl(void) +{ + WL_TRACE(("%s :\n", __FUNCTION__)); + wl_iw_free_ss_cache(); + wl_iw_run_ss_cache_timer(0); + if (g_ss_cache_ctrl.m_timer) { + kfree(g_ss_cache_ctrl.m_timer); + } +} + + + +static void +wl_iw_reset_ss_cache(void) +{ + wl_iw_ss_cache_t *node, *prev, *cur; + wl_iw_ss_cache_t **spec_scan_head; + + DHD_OS_MUTEX_LOCK(&wl_cache_lock); + spec_scan_head = &g_ss_cache_ctrl.m_cache_head; + node = *spec_scan_head; + prev = node; + + for (;node;) { + WL_TRACE(("%s : node SSID %s \n", __FUNCTION__, node->bss_info->SSID)); + if (!node->dirty) { + cur = node; + if (cur == *spec_scan_head) { + *spec_scan_head = cur->next; + prev = *spec_scan_head; + } + else { + prev->next = cur->next; + } + node = cur->next; + + WL_TRACE(("%s : Del node : SSID %s\n", __FUNCTION__, cur->bss_info->SSID)); + kfree(cur); + continue; + } + + node->dirty = 0; + prev = node; + node = node->next; + } + DHD_OS_MUTEX_UNLOCK(&wl_cache_lock); +} + + +static int +wl_iw_add_bss_to_ss_cache(wl_scan_results_t *ss_list) +{ + + wl_iw_ss_cache_t *node, *prev, *leaf; + wl_iw_ss_cache_t **spec_scan_head; + wl_bss_info_t *bi = NULL; + int i; + + + if (!ss_list->count) { + return 0; + } + + DHD_OS_MUTEX_LOCK(&wl_cache_lock); + spec_scan_head = &g_ss_cache_ctrl.m_cache_head; + + for (i = 0; i < ss_list->count; i++) { + + node = *spec_scan_head; + prev = node; + + bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info; + + WL_TRACE(("%s : find %d with specific SSID %s\n", __FUNCTION__, i, bi->SSID)); + for (;node;) { + if (!memcmp(&node->bss_info->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) { + + WL_TRACE(("dirty marked : SSID %s\n", bi->SSID)); + node->dirty = 1; + break; + } + prev = node; + node = node->next; + } + + if (node) { + continue; + } + + leaf = kmalloc(bi->length + WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN, GFP_KERNEL); + if (!leaf) { + WL_ERROR(("Memory alloc failure %d\n", + bi->length + WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN)); + DHD_OS_MUTEX_UNLOCK(&wl_cache_lock); + return -ENOMEM; + } + + memcpy(leaf->bss_info, bi, bi->length); + leaf->next = NULL; + leaf->dirty = 1; + leaf->count = 1; + leaf->version = ss_list->version; + + if (!prev) { + *spec_scan_head = leaf; + } + else { + prev->next = leaf; + } + } + DHD_OS_MUTEX_UNLOCK(&wl_cache_lock); + return 0; +} + + +static int +wl_iw_merge_scan_cache(struct iw_request_info *info, char *extra, uint buflen_from_user, +__u16 *merged_len) +{ + wl_iw_ss_cache_t *node; + wl_scan_results_t *list_merge; + + DHD_OS_MUTEX_LOCK(&wl_cache_lock); + node = g_ss_cache_ctrl.m_cache_head; + for (;node;) { + list_merge = (wl_scan_results_t *)&node->buflen; + WL_TRACE(("%s: Cached Specific APs list=%d\n", __FUNCTION__, list_merge->count)); + if (buflen_from_user - *merged_len > 0) { + *merged_len += (__u16) wl_iw_get_scan_prep(list_merge, info, + extra + *merged_len, buflen_from_user - *merged_len); + } + else { + WL_TRACE(("%s: exit with break\n", __FUNCTION__)); + break; + } + node = node->next; + } + DHD_OS_MUTEX_UNLOCK(&wl_cache_lock); + return 0; +} + + +static int +wl_iw_delete_bss_from_ss_cache(void *addr) +{ + + wl_iw_ss_cache_t *node, *prev; + wl_iw_ss_cache_t **spec_scan_head; + + DHD_OS_MUTEX_LOCK(&wl_cache_lock); + spec_scan_head = &g_ss_cache_ctrl.m_cache_head; + node = *spec_scan_head; + prev = node; + for (;node;) { + if (!memcmp(&node->bss_info->BSSID, addr, ETHER_ADDR_LEN)) { + if (node == *spec_scan_head) { + *spec_scan_head = node->next; + } + else { + prev->next = node->next; + } + + WL_TRACE(("%s : Del node : %s\n", __FUNCTION__, node->bss_info->SSID)); + kfree(node); + break; + } + + prev = node; + node = node->next; + } + + memset(addr, 0, ETHER_ADDR_LEN); + DHD_OS_MUTEX_UNLOCK(&wl_cache_lock); + return 0; +} + +#endif + +static int +wl_iw_set_scan( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int error; + WL_TRACE(("\n:%s dev:%s: SIOCSIWSCAN : SCAN\n", __FUNCTION__, dev->name)); + +#ifdef OEM_CHROMIUMOS + g_set_essid_before_scan = FALSE; +#endif + +#if defined(CSCAN) + WL_ERROR(("%s: Scan from SIOCGIWSCAN not supported\n", __FUNCTION__)); + return -EINVAL; +#endif + +#if defined(SOFTAP) + + if (ap_cfg_running) { + WL_TRACE(("\n>%s: Not executed, reason -'SOFTAP is active'\n", __FUNCTION__)); + return 0; + } +#endif + + + if (g_onoff == G_WLAN_SET_OFF) + return 0; + + + memset(&g_specific_ssid, 0, sizeof(g_specific_ssid)); +#ifndef WL_IW_USE_ISCAN + + g_scan_specified_ssid = 0; +#endif + +#if WIRELESS_EXT > 17 + + if (wrqu->data.length == sizeof(struct iw_scan_req)) { + if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { + struct iw_scan_req *req = (struct iw_scan_req *)extra; +#if defined(CONFIG_FIRST_SCAN) + if (g_first_broadcast_scan != BROADCAST_SCAN_FIRST_RESULT_CONSUMED) { + + WL_TRACE(("%s Ignoring SC %s first BC is not done = %d\n", + __FUNCTION__, req->essid, + g_first_broadcast_scan)); + return -EBUSY; + } +#endif + if (g_scan_specified_ssid) { + WL_TRACE(("%s Specific SCAN is not done ignore scan for = %s \n", + __FUNCTION__, req->essid)); + + return -EBUSY; + } + else { + g_specific_ssid.SSID_len = MIN(sizeof(g_specific_ssid.SSID), + req->essid_len); + memcpy(g_specific_ssid.SSID, req->essid, g_specific_ssid.SSID_len); + g_specific_ssid.SSID_len = htod32(g_specific_ssid.SSID_len); + g_scan_specified_ssid = 1; + WL_TRACE(("### Specific scan ssid=%s len=%d\n", + g_specific_ssid.SSID, g_specific_ssid.SSID_len)); + } + } + } +#endif + + if ((error = dev_wlc_ioctl(dev, WLC_SCAN, &g_specific_ssid, sizeof(g_specific_ssid)))) { + WL_TRACE(("#### Set SCAN for %s failed with %d\n", g_specific_ssid.SSID, error)); + + g_scan_specified_ssid = 0; + return -EBUSY; + } + + return 0; +} + +#ifdef WL_IW_USE_ISCAN +int +wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag) +{ + wlc_ssid_t ssid; + iscan_info_t *iscan = g_iscan; + +#if defined(CONFIG_FIRST_SCAN) + + if (g_first_broadcast_scan == BROADCAST_SCAN_FIRST_IDLE) { + g_first_broadcast_scan = BROADCAST_SCAN_FIRST_STARTED; + WL_TRACE(("%s: First Brodcast scan was forced\n", __FUNCTION__)); + } + else if (g_first_broadcast_scan == BROADCAST_SCAN_FIRST_STARTED) { + WL_TRACE(("%s: ignore ISCAN request first BS is not done yet\n", __FUNCTION__)); + return 0; + } +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + if (flag) + rtnl_lock(); +#endif + + dev_wlc_ioctl(dev, WLC_SET_PASSIVE_SCAN, &iscan->scan_flag, sizeof(iscan->scan_flag)); + wl_iw_set_event_mask(dev); + + WL_TRACE(("+++: Set Broadcast ISCAN\n")); + + memset(&ssid, 0, sizeof(ssid)); + + iscan->list_cur = iscan->list_hdr; + iscan->iscan_state = ISCAN_STATE_SCANING; + + memset(&iscan->iscan_ex_params_p->params, 0, iscan->iscan_ex_param_size); + wl_iw_iscan_prep(&iscan->iscan_ex_params_p->params, &ssid); + wl_iw_iscan(iscan, &ssid, WL_SCAN_ACTION_START); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + if (flag) + rtnl_unlock(); +#endif + + mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000); + + iscan->timer_on = 1; + + return 0; +} + +static int +wl_iw_iscan_set_scan( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + wlc_ssid_t ssid; + iscan_info_t *iscan = g_iscan; + int ret = 0; + + WL_TRACE_SCAN(("%s: SIOCSIWSCAN : ISCAN\n", dev->name)); + +#if defined(CSCAN) + WL_ERROR(("%s: Scan from SIOCGIWSCAN not supported\n", __FUNCTION__)); + return -EINVAL; +#endif + + net_os_wake_lock(dev); + + +#if defined(SOFTAP) + if (ap_cfg_running) { + WL_TRACE(("\n>%s: Not executed, reason -'SOFTAP is active'\n", __FUNCTION__)); + goto set_scan_end; + } +#endif + + if (g_onoff == G_WLAN_SET_OFF) { + WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__)); + goto set_scan_end; + } + +#ifdef PNO_SUPPORT + + if (dhd_dev_get_pno_status(dev)) { + WL_ERROR(("%s: Scan called when PNO is active\n", __FUNCTION__)); + } +#endif + + + if ((!iscan) || (iscan->tsk_ctl.thr_pid < 0)) { + WL_ERROR(("%s error \n", __FUNCTION__)); + goto set_scan_end; + } + + if (g_scan_specified_ssid) { + WL_TRACE(("%s Specific SCAN already running ignoring BC scan\n", + __FUNCTION__)); + ret = EBUSY; + goto set_scan_end; + } + + + memset(&ssid, 0, sizeof(ssid)); + +#if WIRELESS_EXT > 17 + + if (wrqu->data.length == sizeof(struct iw_scan_req)) { + if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { + int as = 0; + struct iw_scan_req *req = (struct iw_scan_req *)extra; + + ssid.SSID_len = MIN(sizeof(ssid.SSID), req->essid_len); + memcpy(ssid.SSID, req->essid, ssid.SSID_len); + ssid.SSID_len = htod32(ssid.SSID_len); + dev_wlc_ioctl(dev, WLC_SET_PASSIVE_SCAN, &as, sizeof(as)); + wl_iw_set_event_mask(dev); + ret = wl_iw_set_scan(dev, info, wrqu, extra); + goto set_scan_end; + } + else { + g_scan_specified_ssid = 0; + + if (iscan->iscan_state == ISCAN_STATE_SCANING) { + WL_TRACE(("%s ISCAN already in progress \n", __FUNCTION__)); + goto set_scan_end; + } + } + } +#endif + +#if defined(CONFIG_FIRST_SCAN) && !defined(CSCAN) + if (g_first_broadcast_scan < BROADCAST_SCAN_FIRST_RESULT_CONSUMED) { + if (++g_first_counter_scans == MAX_ALLOWED_BLOCK_SCAN_FROM_FIRST_SCAN) { + + WL_ERROR(("%s Clean up First scan flag which is %d\n", + __FUNCTION__, g_first_broadcast_scan)); + g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_CONSUMED; + } + else { + WL_ERROR(("%s Ignoring Broadcast Scan:First Scan is not done yet %d\n", + __FUNCTION__, g_first_counter_scans)); + ret = -EBUSY; + goto set_scan_end; + } + } +#endif + + wl_iw_iscan_set_scan_broadcast_prep(dev, 0); + +set_scan_end: + net_os_wake_unlock(dev); + return ret; +} +#endif + +#if WIRELESS_EXT > 17 +static bool +ie_is_wpa_ie(uint8 **wpaie, uint8 **tlvs, int *tlvs_len) +{ + + + uint8 *ie = *wpaie; + + + if ((ie[1] >= 6) && + !bcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x01"), 4)) { + return TRUE; + } + + + ie += ie[1] + 2; + + *tlvs_len -= (int)(ie - *tlvs); + + *tlvs = ie; + return FALSE; +} + +static bool +ie_is_wps_ie(uint8 **wpsie, uint8 **tlvs, int *tlvs_len) +{ + + + uint8 *ie = *wpsie; + + + if ((ie[1] >= 4) && + !bcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x04"), 4)) { + return TRUE; + } + + + ie += ie[1] + 2; + + *tlvs_len -= (int)(ie - *tlvs); + + *tlvs = ie; + return FALSE; +} +#endif + + +static int +wl_iw_handle_scanresults_ies(char **event_p, char *end, + struct iw_request_info *info, wl_bss_info_t *bi) +{ +#if WIRELESS_EXT > 17 + struct iw_event iwe; + char *event; + + event = *event_p; + if (bi->ie_length) { + + bcm_tlv_t *ie; + uint8 *ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t); + int ptr_len = bi->ie_length; + + if ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_RSN_ID))) { + iwe.cmd = IWEVGENIE; + iwe.u.data.length = ie->len + 2; + event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie); + } + ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t); + + while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) { + + if (ie_is_wps_ie(((uint8 **)&ie), &ptr, &ptr_len)) { + iwe.cmd = IWEVGENIE; + iwe.u.data.length = ie->len + 2; + event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie); + break; + } + } + + ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t); + ptr_len = bi->ie_length; + while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) { + if (ie_is_wpa_ie(((uint8 **)&ie), &ptr, &ptr_len)) { + iwe.cmd = IWEVGENIE; + iwe.u.data.length = ie->len + 2; + event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie); + break; + } + } + + *event_p = event; + } +#endif + + return 0; +} + +#ifndef CSCAN +static uint +wl_iw_get_scan_prep( + wl_scan_results_t *list, + struct iw_request_info *info, + char *extra, + short max_size) +{ + int i, j; + struct iw_event iwe; + wl_bss_info_t *bi = NULL; + char *event = extra, *end = extra + max_size - WE_ADD_EVENT_FIX, *value; + int ret = 0; + + if (!list) { + WL_ERROR(("%s: Null list pointer", __FUNCTION__)); + return ret; + } + + + + for (i = 0; i < list->count && i < IW_MAX_AP; i++) { + if (list->version != WL_BSS_INFO_VERSION) { + WL_ERROR(("%s : list->version %d != WL_BSS_INFO_VERSION\n", + __FUNCTION__, list->version)); + return ret; + } + + bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info; + + WL_TRACE(("%s : %s\n", __FUNCTION__, bi->SSID)); + + + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN); + event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN); + + iwe.u.data.length = dtoh32(bi->SSID_len); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID); + + + if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) { + iwe.cmd = SIOCGIWMODE; + if (dtoh16(bi->capability) & DOT11_CAP_ESS) + iwe.u.mode = IW_MODE_INFRA; + else + iwe.u.mode = IW_MODE_ADHOC; + event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN); + } + + + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = wf_channel2mhz(CHSPEC_CHANNEL(bi->chanspec), + CHSPEC_CHANNEL(bi->chanspec) <= CH_MAX_2G_CHANNEL ? + WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G); + iwe.u.freq.e = 6; + event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN); + + + iwe.cmd = IWEVQUAL; + iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI)); + iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI); + iwe.u.qual.noise = 0x100 + bi->phy_noise; + event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN); + + + wl_iw_handle_scanresults_ies(&event, end, info, bi); + + + iwe.cmd = SIOCGIWENCODE; + if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event); + + + if (bi->rateset.count) { + if (((event -extra) + IW_EV_LCP_LEN) <= (uintptr)end) { + value = event + IW_EV_LCP_LEN; + iwe.cmd = SIOCGIWRATE; + + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) { + iwe.u.bitrate.value = + (bi->rateset.rates[j] & 0x7f) * 500000; + value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe, + IW_EV_PARAM_LEN); + } + event = value; + } + } + } + + if ((ret = (event - extra)) < 0) { + WL_ERROR(("==> Wrong size\n")); + ret = 0; + } + + WL_TRACE(("%s: size=%d bytes prepared \n", __FUNCTION__, (unsigned int)(event - extra))); + return (uint)ret; +} + +static int +wl_iw_get_scan( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + channel_info_t ci; + wl_scan_results_t *list_merge; + wl_scan_results_t *list = (wl_scan_results_t *) g_scan; + int error; + uint buflen_from_user = dwrq->length; + uint len = G_SCAN_RESULTS; + __u16 len_ret = 0; +#if !defined(CSCAN) + __u16 merged_len = 0; +#endif +#if defined(WL_IW_USE_ISCAN) + iscan_info_t *iscan = g_iscan; + iscan_buf_t * p_buf; +#if !defined(CSCAN) + uint32 counter = 0; +#endif +#endif + + WL_TRACE(("%s: buflen_from_user %d: \n", dev->name, buflen_from_user)); + + if (!extra) { + WL_TRACE(("%s: wl_iw_get_scan return -EINVAL\n", dev->name)); + return -EINVAL; + } + + + if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci)))) + return error; + ci.scan_channel = dtoh32(ci.scan_channel); + if (ci.scan_channel) + return -EAGAIN; + +#if !defined(CSCAN) + if (g_ss_cache_ctrl.m_timer_expired) { + wl_iw_free_ss_cache(); + g_ss_cache_ctrl.m_timer_expired ^= 1; + } + if ((!g_scan_specified_ssid && g_ss_cache_ctrl.m_prev_scan_mode) || + g_ss_cache_ctrl.m_cons_br_scan_cnt > 4) { + g_ss_cache_ctrl.m_cons_br_scan_cnt = 0; + + wl_iw_reset_ss_cache(); + } + g_ss_cache_ctrl.m_prev_scan_mode = g_scan_specified_ssid; + if (g_scan_specified_ssid) { + g_ss_cache_ctrl.m_cons_br_scan_cnt = 0; + } + else { + g_ss_cache_ctrl.m_cons_br_scan_cnt++; + } +#endif + + + + if (g_scan_specified_ssid) { + + list = kmalloc(len, GFP_KERNEL); + if (!list) { + WL_TRACE(("%s: wl_iw_get_scan return -ENOMEM\n", dev->name)); + g_scan_specified_ssid = 0; + return -ENOMEM; + } + } + + memset(list, 0, len); + list->buflen = htod32(len); + if ((error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, len))) { + WL_ERROR(("%s: %s : Scan_results ERROR %d\n", dev->name, __FUNCTION__, error)); + dwrq->length = len; + if (g_scan_specified_ssid) { + g_scan_specified_ssid = 0; + kfree(list); + } + return 0; + } + list->buflen = dtoh32(list->buflen); + list->version = dtoh32(list->version); + list->count = dtoh32(list->count); + + + if (list->version != WL_BSS_INFO_VERSION) { + WL_ERROR(("%s : list->version %d != WL_BSS_INFO_VERSION\n", + __FUNCTION__, list->version)); + if (g_scan_specified_ssid) { + g_scan_specified_ssid = 0; + kfree(list); + } + return -EINVAL; + } + +#if !defined(CSCAN) + if (g_scan_specified_ssid) { + + wl_iw_add_bss_to_ss_cache(list); + kfree(list); + } +#endif + +#if !defined(CSCAN) + DHD_OS_MUTEX_LOCK(&wl_cache_lock); +#if defined(WL_IW_USE_ISCAN) + if (g_scan_specified_ssid) + WL_TRACE(("%s: Specified scan APs from scan=%d\n", __FUNCTION__, list->count)); + p_buf = iscan->list_hdr; + + while (p_buf != iscan->list_cur) { + list_merge = &((wl_iscan_results_t*)p_buf->iscan_buf)->results; + WL_TRACE(("%s: Bcast APs list=%d\n", __FUNCTION__, list_merge->count)); + counter += list_merge->count; + if (list_merge->count > 0) + len_ret += (__u16) wl_iw_get_scan_prep(list_merge, info, + extra+len_ret, buflen_from_user -len_ret); + p_buf = p_buf->next; + } + WL_TRACE(("%s merged with total Bcast APs=%d\n", __FUNCTION__, counter)); +#else + list_merge = (wl_scan_results_t *) g_scan; + len_ret = (__u16) wl_iw_get_scan_prep(list_merge, info, extra, buflen_from_user); +#endif + DHD_OS_MUTEX_UNLOCK(&wl_cache_lock); + if (g_ss_cache_ctrl.m_link_down) { + + wl_iw_delete_bss_from_ss_cache(g_ss_cache_ctrl.m_active_bssid); + } + + wl_iw_merge_scan_cache(info, extra+len_ret, buflen_from_user-len_ret, &merged_len); + len_ret += merged_len; + wl_iw_run_ss_cache_timer(0); + wl_iw_run_ss_cache_timer(1); +#else + + + if (g_scan_specified_ssid) { + WL_TRACE(("%s: Specified scan APs in the list =%d\n", __FUNCTION__, list->count)); + len_ret = (__u16) wl_iw_get_scan_prep(list, info, extra, buflen_from_user); + kfree(list); + +#if defined(WL_IW_USE_ISCAN) + p_buf = iscan->list_hdr; + + while (p_buf != iscan->list_cur) { + list_merge = &((wl_iscan_results_t*)p_buf->iscan_buf)->results; + WL_TRACE(("%s: Bcast APs list=%d\n", __FUNCTION__, list_merge->count)); + if (list_merge->count > 0) + len_ret += (__u16) wl_iw_get_scan_prep(list_merge, info, + extra+len_ret, buflen_from_user -len_ret); + p_buf = p_buf->next; + } +#else + list_merge = (wl_scan_results_t *) g_scan; + WL_TRACE(("%s: Bcast APs list=%d\n", __FUNCTION__, list_merge->count)); + if (list_merge->count > 0) + len_ret += (__u16) wl_iw_get_scan_prep(list_merge, info, extra+len_ret, + buflen_from_user -len_ret); +#endif + } + else { + list = (wl_scan_results_t *) g_scan; + len_ret = (__u16) wl_iw_get_scan_prep(list, info, extra, buflen_from_user); + } +#endif + +#if defined(WL_IW_USE_ISCAN) + + g_scan_specified_ssid = 0; +#endif + + if ((len_ret + WE_ADD_EVENT_FIX) < buflen_from_user) + len = len_ret; + + dwrq->length = len; + dwrq->flags = 0; + + WL_TRACE(("%s return to WE %d bytes APs=%d\n", __FUNCTION__, dwrq->length, list->count)); + return 0; +} +#endif + +#if defined(WL_IW_USE_ISCAN) +static int +wl_iw_iscan_get_scan( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_scan_results_t *list; + struct iw_event iwe; + wl_bss_info_t *bi = NULL; + int ii, j; + int apcnt; + char *event = extra, *end = extra + dwrq->length, *value; + iscan_info_t *iscan = g_iscan; + iscan_buf_t * p_buf; + uint32 counter = 0; + uint8 channel; +#if !defined(CSCAN) + __u16 merged_len = 0; + uint buflen_from_user = dwrq->length; +#endif + + WL_TRACE(("%s %s buflen_from_user %d:\n", dev->name, __FUNCTION__, dwrq->length)); + +#if defined(SOFTAP) + if (ap_cfg_running) { + WL_TRACE(("%s: Not executed, reason -'SOFTAP is active'\n", __FUNCTION__)); + return -EINVAL; + } +#endif + + if (!extra) { + WL_TRACE(("%s: INVALID SIOCGIWSCAN GET bad parameter\n", dev->name)); + return -EINVAL; + } + +#if defined(CONFIG_FIRST_SCAN) + if (g_first_broadcast_scan < BROADCAST_SCAN_FIRST_RESULT_READY) { + WL_TRACE(("%s %s: first ISCAN results are NOT ready yet \n", + dev->name, __FUNCTION__)); + return -EAGAIN; + } +#endif + + if ((!iscan) || (iscan->tsk_ctl.thr_pid < 0)) { + WL_ERROR(("%ssysioc_pid\n", __FUNCTION__)); + return EAGAIN; + } + + + +#if !defined(CSCAN) + if (g_ss_cache_ctrl.m_timer_expired) { + wl_iw_free_ss_cache(); + g_ss_cache_ctrl.m_timer_expired ^= 1; + } + if (g_scan_specified_ssid) { + return wl_iw_get_scan(dev, info, dwrq, extra); + } + else { + if (g_ss_cache_ctrl.m_link_down) { + + wl_iw_delete_bss_from_ss_cache(g_ss_cache_ctrl.m_active_bssid); + } + if (g_ss_cache_ctrl.m_prev_scan_mode || g_ss_cache_ctrl.m_cons_br_scan_cnt > 4) { + g_ss_cache_ctrl.m_cons_br_scan_cnt = 0; + + wl_iw_reset_ss_cache(); + } + g_ss_cache_ctrl.m_prev_scan_mode = g_scan_specified_ssid; + g_ss_cache_ctrl.m_cons_br_scan_cnt++; + } +#endif + + WL_TRACE(("%s: SIOCGIWSCAN GET broadcast results\n", dev->name)); + apcnt = 0; + p_buf = iscan->list_hdr; + + while (p_buf != iscan->list_cur) { + list = &((wl_iscan_results_t*)p_buf->iscan_buf)->results; + + counter += list->count; + + if (list->version != WL_BSS_INFO_VERSION) { + WL_ERROR(("%s : list->version %d != WL_BSS_INFO_VERSION\n", + __FUNCTION__, list->version)); + return -EINVAL; + } + + bi = NULL; + for (ii = 0; ii < list->count && apcnt < IW_MAX_AP; apcnt++, ii++) { + bi = (bi ? + (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : + list->bss_info); + ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list + + WLC_IW_ISCAN_MAXLEN)); + + + if (event + ETHER_ADDR_LEN + bi->SSID_len + + IW_EV_UINT_LEN + IW_EV_FREQ_LEN + IW_EV_QUAL_LEN >= end) + return -E2BIG; + + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN); + event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN); + + + iwe.u.data.length = dtoh32(bi->SSID_len); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID); + + + if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) { + iwe.cmd = SIOCGIWMODE; + if (dtoh16(bi->capability) & DOT11_CAP_ESS) + iwe.u.mode = IW_MODE_INFRA; + else + iwe.u.mode = IW_MODE_ADHOC; + event = IWE_STREAM_ADD_EVENT(info, event, end, + &iwe, IW_EV_UINT_LEN); + } + + + iwe.cmd = SIOCGIWFREQ; + channel = (bi->ctl_ch == 0) ? CHSPEC_CHANNEL(bi->chanspec) : bi->ctl_ch; + iwe.u.freq.m = wf_channel2mhz(channel, + channel <= CH_MAX_2G_CHANNEL ? + WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G); + iwe.u.freq.e = 6; + event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN); + + + iwe.cmd = IWEVQUAL; + iwe.u.qual.qual = rssi_to_qual(dtoh16(bi->RSSI)); + iwe.u.qual.level = 0x100 + dtoh16(bi->RSSI); + iwe.u.qual.noise = 0x100 + bi->phy_noise; + event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN); + + + wl_iw_handle_scanresults_ies(&event, end, info, bi); + + + iwe.cmd = SIOCGIWENCODE; + if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event); + + + if (bi->rateset.count) { + if (event + IW_MAX_BITRATES*IW_EV_PARAM_LEN >= end) + return -E2BIG; + + value = event + IW_EV_LCP_LEN; + iwe.cmd = SIOCGIWRATE; + + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) { + iwe.u.bitrate.value = + (bi->rateset.rates[j] & 0x7f) * 500000; + value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe, + IW_EV_PARAM_LEN); + } + event = value; + } + } + p_buf = p_buf->next; + } + + dwrq->length = event - extra; + dwrq->flags = 0; + +#if !defined(CSCAN) + + wl_iw_merge_scan_cache(info, event, buflen_from_user - dwrq->length, &merged_len); + dwrq->length += merged_len; + wl_iw_run_ss_cache_timer(0); + wl_iw_run_ss_cache_timer(1); +#endif + +#if defined(CONFIG_FIRST_SCAN) + g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_CONSUMED; +#endif + + WL_TRACE(("%s return to WE %d bytes APs=%d\n", __FUNCTION__, dwrq->length, counter)); + + return 0; +} +#endif + +#define WL_JOIN_PARAMS_MAX 1600 +#ifdef CONFIG_PRESCANNED +static int +check_prescan(wl_join_params_t *join_params, int *join_params_size) +{ + int cnt = 0; + int indx = 0; + wl_iw_ss_cache_t *node = NULL; + wl_bss_info_t *bi = NULL; + iscan_info_t *iscan = g_iscan; + iscan_buf_t * buf; + wl_scan_results_t *list; + char *destbuf; + + buf = iscan->list_hdr; + + while (buf) { + list = &((wl_iscan_results_t*)buf->iscan_buf)->results; + bi = NULL; + for (indx = 0; indx < list->count; indx++) { + bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) + : list->bss_info; + if (!(dtoh16(bi->capability) & DOT11_CAP_ESS)) + continue; + if ((dtoh32(bi->SSID_len) != join_params->ssid.SSID_len) || + memcmp(bi->SSID, join_params->ssid.SSID, + join_params->ssid.SSID_len)) + continue; + memcpy(&join_params->params.chanspec_list[cnt], + &bi->chanspec, sizeof(chanspec_t)); + WL_ERROR(("iscan : chanspec :%d, count %d \n", bi->chanspec, cnt)); + cnt++; + } + buf = buf->next; + } + + if (!cnt) { + MUTEX_LOCK_WL_SCAN_SET(); + node = g_ss_cache_ctrl.m_cache_head; + for (; node; ) { + if (!memcmp(&node->bss_info->SSID, join_params->ssid.SSID, + join_params->ssid.SSID_len)) { + memcpy(&join_params->params.chanspec_list[cnt], + &node->bss_info->chanspec, sizeof(chanspec_t)); + WL_ERROR(("cache_scan : chanspec :%d, count %d \n", + (int)node->bss_info->chanspec, cnt)); + cnt++; + } + node = node->next; + } + MUTEX_UNLOCK_WL_SCAN_SET(); + } + + if (!cnt) { + return 0; + } + + destbuf = (char *)&join_params->params.chanspec_list[cnt]; + *join_params_size = destbuf - (char*)join_params; + join_params->ssid.SSID_len = htod32(g_ssid.SSID_len); + memcpy(&(join_params->params.bssid), ðer_bcast, ETHER_ADDR_LEN); + join_params->params.chanspec_num = htod32(cnt); + + if ((*join_params_size) > WL_JOIN_PARAMS_MAX) { + WL_ERROR(("can't fit bssids for all %d APs found\n", cnt)); + kfree(join_params); + return 0; + } + + WL_ERROR(("Passing %d channel/bssid pairs.\n", cnt)); + return cnt; +} +#endif + +static int +wl_iw_set_essid( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + int error; + wl_join_params_t *join_params; + int join_params_size; + + WL_TRACE(("%s: SIOCSIWESSID\n", dev->name)); + + RETURN_IF_EXTRA_NULL(extra); + +#ifdef OEM_CHROMIUMOS + if (g_set_essid_before_scan) + return -EAGAIN; +#endif + if (!(join_params = kmalloc(WL_JOIN_PARAMS_MAX, GFP_KERNEL))) { + WL_ERROR(("allocation failed for join_params size is %d\n", WL_JOIN_PARAMS_MAX)); + return -ENOMEM; + } + + memset(join_params, 0, WL_JOIN_PARAMS_MAX); + + + memset(&g_ssid, 0, sizeof(g_ssid)); + + if (dwrq->length && extra) { +#if WIRELESS_EXT > 20 + g_ssid.SSID_len = MIN(sizeof(g_ssid.SSID), dwrq->length); +#else + g_ssid.SSID_len = MIN(sizeof(g_ssid.SSID), dwrq->length-1); +#endif + memcpy(g_ssid.SSID, extra, g_ssid.SSID_len); + +#ifdef CONFIG_PRESCANNED + memcpy(join_params->ssid.SSID, g_ssid.SSID, g_ssid.SSID_len); + join_params->ssid.SSID_len = g_ssid.SSID_len; + + if (check_prescan(join_params, &join_params_size)) { + if ((error = dev_wlc_ioctl(dev, WLC_SET_SSID, + join_params, join_params_size))) { + WL_ERROR(("Invalid ioctl data=%d\n", error)); + kfree(join_params); + return error; + } + kfree(join_params); + return 0; + } else { + WL_ERROR(("No matched found\n Trying to join to specific channel\n")); + } +#endif + } else { + + g_ssid.SSID_len = 0; + } + g_ssid.SSID_len = htod32(g_ssid.SSID_len); + + + memset(join_params, 0, sizeof(*join_params)); + join_params_size = sizeof(join_params->ssid); + + memcpy(join_params->ssid.SSID, g_ssid.SSID, g_ssid.SSID_len); + join_params->ssid.SSID_len = htod32(g_ssid.SSID_len); + memcpy(&(join_params->params.bssid), ðer_bcast, ETHER_ADDR_LEN); + + + + wl_iw_ch_to_chanspec(g_wl_iw_params.target_channel, join_params, &join_params_size); + + if ((error = dev_wlc_ioctl(dev, WLC_SET_SSID, join_params, join_params_size))) { + WL_ERROR(("Invalid ioctl data=%d\n", error)); + return error; + } + + if (g_ssid.SSID_len) { + WL_ERROR(("%s: join SSID=%s ch=%d\n", __FUNCTION__, + g_ssid.SSID, g_wl_iw_params.target_channel)); + } + kfree(join_params); + return 0; +} + +static int +wl_iw_get_essid( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wlc_ssid_t ssid; + int error; + + WL_TRACE(("%s: SIOCGIWESSID\n", dev->name)); + + if (!extra) + return -EINVAL; + + if ((error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid)))) { + WL_ERROR(("Error getting the SSID\n")); + return error; + } + + ssid.SSID_len = dtoh32(ssid.SSID_len); + + + memcpy(extra, ssid.SSID, ssid.SSID_len); + + dwrq->length = ssid.SSID_len; + + dwrq->flags = 1; + + return 0; +} + +static int +wl_iw_set_nick( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_iw_t *iw = NETDEV_PRIV(dev); + + WL_TRACE(("%s: SIOCSIWNICKN\n", dev->name)); + + if (!extra) + return -EINVAL; + + + if (dwrq->length > sizeof(iw->nickname)) + return -E2BIG; + + memcpy(iw->nickname, extra, dwrq->length); + iw->nickname[dwrq->length - 1] = '\0'; + + return 0; +} + +static int +wl_iw_get_nick( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_iw_t *iw = NETDEV_PRIV(dev); + + WL_TRACE(("%s: SIOCGIWNICKN\n", dev->name)); + + if (!extra) + return -EINVAL; + + strcpy(extra, iw->nickname); + dwrq->length = strlen(extra) + 1; + + return 0; +} + +static int +wl_iw_set_rate( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + wl_rateset_t rateset; + int error, rate, i, error_bg, error_a; + + WL_TRACE(("%s: SIOCSIWRATE\n", dev->name)); + + + if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset)))) + return error; + + rateset.count = dtoh32(rateset.count); + + if (vwrq->value < 0) { + + rate = rateset.rates[rateset.count - 1] & 0x7f; + } else if (vwrq->value < rateset.count) { + + rate = rateset.rates[vwrq->value] & 0x7f; + } else { + + rate = vwrq->value / 500000; + } + + if (vwrq->fixed) { + + error_bg = dev_wlc_intvar_set(dev, "bg_rate", rate); + error_a = dev_wlc_intvar_set(dev, "a_rate", rate); + + if (error_bg && error_a) + return (error_bg | error_a); + } else { + + + error_bg = dev_wlc_intvar_set(dev, "bg_rate", 0); + + error_a = dev_wlc_intvar_set(dev, "a_rate", 0); + + if (error_bg && error_a) + return (error_bg | error_a); + + + for (i = 0; i < rateset.count; i++) + if ((rateset.rates[i] & 0x7f) > rate) + break; + rateset.count = htod32(i); + + + if ((error = dev_wlc_ioctl(dev, WLC_SET_RATESET, &rateset, sizeof(rateset)))) + return error; + } + + return 0; +} + +static int +wl_iw_get_rate( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, rate; + + WL_TRACE(("%s: SIOCGIWRATE\n", dev->name)); + + + if ((error = dev_wlc_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate)))) + return error; + rate = dtoh32(rate); + vwrq->value = rate * 500000; + + return 0; +} + +static int +wl_iw_set_rts( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, rts; + + WL_TRACE(("%s: SIOCSIWRTS\n", dev->name)); + + if (vwrq->disabled) + rts = DOT11_DEFAULT_RTS_LEN; + else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_RTS_LEN) + return -EINVAL; + else + rts = vwrq->value; + + if ((error = dev_wlc_intvar_set(dev, "rtsthresh", rts))) + return error; + + return 0; +} + +static int +wl_iw_get_rts( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, rts; + + WL_TRACE(("%s: SIOCGIWRTS\n", dev->name)); + + if ((error = dev_wlc_intvar_get(dev, "rtsthresh", &rts))) + return error; + + vwrq->value = rts; + vwrq->disabled = (rts >= DOT11_DEFAULT_RTS_LEN); + vwrq->fixed = 1; + + return 0; +} + +static int +wl_iw_set_frag( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, frag; + + WL_TRACE(("%s: SIOCSIWFRAG\n", dev->name)); + + if (vwrq->disabled) + frag = DOT11_DEFAULT_FRAG_LEN; + else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_FRAG_LEN) + return -EINVAL; + else + frag = vwrq->value; + + if ((error = dev_wlc_intvar_set(dev, "fragthresh", frag))) + return error; + + return 0; +} + +static int +wl_iw_get_frag( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, fragthreshold; + + WL_TRACE(("%s: SIOCGIWFRAG\n", dev->name)); + + if ((error = dev_wlc_intvar_get(dev, "fragthresh", &fragthreshold))) + return error; + + vwrq->value = fragthreshold; + vwrq->disabled = (fragthreshold >= DOT11_DEFAULT_FRAG_LEN); + vwrq->fixed = 1; + + return 0; +} + +static int +wl_iw_set_txpow( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, disable; + uint16 txpwrmw; + WL_TRACE(("%s: SIOCSIWTXPOW\n", dev->name)); + + + disable = vwrq->disabled ? WL_RADIO_SW_DISABLE : 0; + disable += WL_RADIO_SW_DISABLE << 16; + + disable = htod32(disable); + if ((error = dev_wlc_ioctl(dev, WLC_SET_RADIO, &disable, sizeof(disable)))) + return error; + + + if (disable & WL_RADIO_SW_DISABLE) + return 0; + + + if (!(vwrq->flags & IW_TXPOW_MWATT)) + return -EINVAL; + + + if (vwrq->value < 0) + return 0; + + if (vwrq->value > 0xffff) txpwrmw = 0xffff; + else txpwrmw = (uint16)vwrq->value; + + + error = dev_wlc_intvar_set(dev, "qtxpower", (int)(bcm_mw_to_qdbm(txpwrmw))); + return error; +} + +static int +wl_iw_get_txpow( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, disable, txpwrdbm; + uint8 result; + + WL_TRACE(("%s: SIOCGIWTXPOW\n", dev->name)); + + if ((error = dev_wlc_ioctl(dev, WLC_GET_RADIO, &disable, sizeof(disable))) || + (error = dev_wlc_intvar_get(dev, "qtxpower", &txpwrdbm))) + return error; + + disable = dtoh32(disable); + result = (uint8)(txpwrdbm & ~WL_TXPWR_OVERRIDE); + vwrq->value = (int32)bcm_qdbm_to_mw(result); + vwrq->fixed = 0; + vwrq->disabled = (disable & (WL_RADIO_SW_DISABLE | WL_RADIO_HW_DISABLE)) ? 1 : 0; + vwrq->flags = IW_TXPOW_MWATT; + + return 0; +} + +#if WIRELESS_EXT > 10 +static int +wl_iw_set_retry( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, lrl, srl; + + WL_TRACE(("%s: SIOCSIWRETRY\n", dev->name)); + + + if (vwrq->disabled || (vwrq->flags & IW_RETRY_LIFETIME)) + return -EINVAL; + + + if (vwrq->flags & IW_RETRY_LIMIT) { + + +#if WIRELESS_EXT > 20 + if ((vwrq->flags & IW_RETRY_LONG) ||(vwrq->flags & IW_RETRY_MAX) || + !((vwrq->flags & IW_RETRY_SHORT) || (vwrq->flags & IW_RETRY_MIN))) { +#else + if ((vwrq->flags & IW_RETRY_MAX) || !(vwrq->flags & IW_RETRY_MIN)) { +#endif + lrl = htod32(vwrq->value); + if ((error = dev_wlc_ioctl(dev, WLC_SET_LRL, &lrl, sizeof(lrl)))) + return error; + } + + +#if WIRELESS_EXT > 20 + if ((vwrq->flags & IW_RETRY_SHORT) ||(vwrq->flags & IW_RETRY_MIN) || + !((vwrq->flags & IW_RETRY_LONG) || (vwrq->flags & IW_RETRY_MAX))) { +#else + if ((vwrq->flags & IW_RETRY_MIN) || !(vwrq->flags & IW_RETRY_MAX)) { +#endif + srl = htod32(vwrq->value); + if ((error = dev_wlc_ioctl(dev, WLC_SET_SRL, &srl, sizeof(srl)))) + return error; + } + } + return 0; +} + +static int +wl_iw_get_retry( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, lrl, srl; + + WL_TRACE(("%s: SIOCGIWRETRY\n", dev->name)); + + vwrq->disabled = 0; + + + if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) + return -EINVAL; + + + if ((error = dev_wlc_ioctl(dev, WLC_GET_LRL, &lrl, sizeof(lrl))) || + (error = dev_wlc_ioctl(dev, WLC_GET_SRL, &srl, sizeof(srl)))) + return error; + + lrl = dtoh32(lrl); + srl = dtoh32(srl); + + + if (vwrq->flags & IW_RETRY_MAX) { + vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + vwrq->value = lrl; + } else { + vwrq->flags = IW_RETRY_LIMIT; + vwrq->value = srl; + if (srl != lrl) + vwrq->flags |= IW_RETRY_MIN; + } + + return 0; +} +#endif + +static int +wl_iw_set_encode( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_wsec_key_t key; + int error, val, wsec; + + WL_TRACE(("%s: SIOCSIWENCODE index %d, len %d, flags %04x (%s%s%s%s%s)\n", + dev->name, dwrq->flags & IW_ENCODE_INDEX, dwrq->length, dwrq->flags, + dwrq->flags & IW_ENCODE_NOKEY ? "NOKEY" : "", + dwrq->flags & IW_ENCODE_DISABLED ? " DISABLED" : "", + dwrq->flags & IW_ENCODE_RESTRICTED ? " RESTRICTED" : "", + dwrq->flags & IW_ENCODE_OPEN ? " OPEN" : "", + dwrq->flags & IW_ENCODE_TEMP ? " TEMP" : "")); + + memset(&key, 0, sizeof(key)); + + if ((dwrq->flags & IW_ENCODE_INDEX) == 0) { + + for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS; key.index++) { + val = htod32(key.index); + if ((error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val)))) + return error; + val = dtoh32(val); + if (val) + break; + } + + if (key.index == DOT11_MAX_DEFAULT_KEYS) + key.index = 0; + } else { + key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if (key.index >= DOT11_MAX_DEFAULT_KEYS) + return -EINVAL; + } + + + if (!extra || !dwrq->length || (dwrq->flags & IW_ENCODE_NOKEY)) { + + val = htod32(key.index); + if ((error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY, &val, sizeof(val)))) + return error; + } else { + key.len = dwrq->length; + + if (dwrq->length > sizeof(key.data)) + return -EINVAL; + + memcpy(key.data, extra, dwrq->length); + + key.flags = WL_PRIMARY_KEY; + switch (key.len) { + case WEP1_KEY_SIZE: + key.algo = CRYPTO_ALGO_WEP1; + break; + case WEP128_KEY_SIZE: + key.algo = CRYPTO_ALGO_WEP128; + break; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) + case TKIP_KEY_SIZE: + key.algo = CRYPTO_ALGO_TKIP; + break; +#endif + case AES_KEY_SIZE: + key.algo = CRYPTO_ALGO_AES_CCM; + break; + default: + return -EINVAL; + } + + + swap_key_from_BE(&key); + if ((error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)))) + return error; + } + + + val = (dwrq->flags & IW_ENCODE_DISABLED) ? 0 : WEP_ENABLED; + + if ((error = dev_wlc_intvar_get(dev, "wsec", &wsec))) + return error; + + wsec &= ~(WEP_ENABLED); + wsec |= val; + + if ((error = dev_wlc_intvar_set(dev, "wsec", wsec))) + return error; + + + val = (dwrq->flags & IW_ENCODE_RESTRICTED) ? 1 : 0; + val = htod32(val); + if ((error = dev_wlc_ioctl(dev, WLC_SET_AUTH, &val, sizeof(val)))) + return error; + + return 0; +} + +static int +wl_iw_get_encode( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_wsec_key_t key; + int error, val, wsec, auth; + + WL_TRACE(("%s: SIOCGIWENCODE\n", dev->name)); + + + bzero(&key, sizeof(wl_wsec_key_t)); + + if ((dwrq->flags & IW_ENCODE_INDEX) == 0) { + + for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS; key.index++) { + val = key.index; + if ((error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val)))) + return error; + val = dtoh32(val); + if (val) + break; + } + } else + key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + + if (key.index >= DOT11_MAX_DEFAULT_KEYS) + key.index = 0; + + + + if ((error = dev_wlc_ioctl(dev, WLC_GET_WSEC, &wsec, sizeof(wsec))) || + (error = dev_wlc_ioctl(dev, WLC_GET_AUTH, &auth, sizeof(auth)))) + return error; + + swap_key_to_BE(&key); + + wsec = dtoh32(wsec); + auth = dtoh32(auth); + + dwrq->length = MIN(DOT11_MAX_KEY_SIZE, key.len); + + + dwrq->flags = key.index + 1; + if (!(wsec & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED))) { + + dwrq->flags |= IW_ENCODE_DISABLED; + } + if (auth) { + + dwrq->flags |= IW_ENCODE_RESTRICTED; + } + + + if (dwrq->length && extra) + memcpy(extra, key.data, dwrq->length); + + return 0; +} + +static int +wl_iw_set_power( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, pm; + + WL_TRACE(("%s: SIOCSIWPOWER\n", dev->name)); + + pm = vwrq->disabled ? PM_OFF : PM_MAX; + + pm = htod32(pm); + if ((error = dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm)))) + return error; + + return 0; +} + +static int +wl_iw_get_power( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error, pm; + + WL_TRACE(("%s: SIOCGIWPOWER\n", dev->name)); + + if ((error = dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm)))) + return error; + + pm = dtoh32(pm); + vwrq->disabled = pm ? 0 : 1; + vwrq->flags = IW_POWER_ALL_R; + + return 0; +} + +#if WIRELESS_EXT > 17 +static int +wl_iw_set_wpaie( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *iwp, + char *extra +) +{ + + WL_TRACE(("%s: SIOCSIWGENIE\n", dev->name)); + + RETURN_IF_EXTRA_NULL(extra); + +#ifdef DHD_DEBUG + { + int i; + + for (i = 0; i < iwp->length; i++) + WL_TRACE(("%02X ", extra[i])); + WL_TRACE(("\n")); + } +#endif + + dev_wlc_bufvar_set(dev, "wpaie", extra, iwp->length); + + return 0; +} + +static int +wl_iw_get_wpaie( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *iwp, + char *extra +) +{ + WL_TRACE(("%s: SIOCGIWGENIE\n", dev->name)); + iwp->length = 64; + dev_wlc_bufvar_get(dev, "wpaie", extra, iwp->length); + return 0; +} + +static int +wl_iw_set_encodeext( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra +) +{ + wl_wsec_key_t key; + int error; + struct iw_encode_ext *iwe; + + WL_TRACE(("%s: SIOCSIWENCODEEXT\n", dev->name)); + + RETURN_IF_EXTRA_NULL(extra); + + memset(&key, 0, sizeof(key)); + iwe = (struct iw_encode_ext *)extra; + + + if (dwrq->flags & IW_ENCODE_DISABLED) { + + } + + + key.index = 0; + if (dwrq->flags & IW_ENCODE_INDEX) + key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + + key.len = iwe->key_len; + + + if (!ETHER_ISMULTI(iwe->addr.sa_data)) + bcopy((void *)&iwe->addr.sa_data, (char *)&key.ea, ETHER_ADDR_LEN); + + + if (key.len == 0) { + if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { + WL_WSEC(("Changing the the primary Key to %d\n", key.index)); + + key.index = htod32(key.index); + error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY, + &key.index, sizeof(key.index)); + if (error) + return error; + } + + else { + swap_key_from_BE(&key); + dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)); + } + } + else { + if (iwe->key_len > sizeof(key.data)) + return -EINVAL; + + WL_WSEC(("Setting the key index %d\n", key.index)); + if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { + WL_WSEC(("key is a Primary Key\n")); + key.flags = WL_PRIMARY_KEY; + } + + bcopy((void *)iwe->key, key.data, iwe->key_len); + + if (iwe->alg == IW_ENCODE_ALG_TKIP) { + uint8 keybuf[8]; + bcopy(&key.data[24], keybuf, sizeof(keybuf)); + bcopy(&key.data[16], &key.data[24], sizeof(keybuf)); + bcopy(keybuf, &key.data[16], sizeof(keybuf)); + } + + + if (iwe->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { + uchar *ivptr; + ivptr = (uchar *)iwe->rx_seq; + key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) | + (ivptr[3] << 8) | ivptr[2]; + key.rxiv.lo = (ivptr[1] << 8) | ivptr[0]; + key.iv_initialized = TRUE; + } + + switch (iwe->alg) { + case IW_ENCODE_ALG_NONE: + key.algo = CRYPTO_ALGO_OFF; + break; + case IW_ENCODE_ALG_WEP: + if (iwe->key_len == WEP1_KEY_SIZE) + key.algo = CRYPTO_ALGO_WEP1; + else + key.algo = CRYPTO_ALGO_WEP128; + break; + case IW_ENCODE_ALG_TKIP: + key.algo = CRYPTO_ALGO_TKIP; + break; + case IW_ENCODE_ALG_CCMP: + key.algo = CRYPTO_ALGO_AES_CCM; + break; + default: + break; + } + swap_key_from_BE(&key); + + dhd_wait_pend8021x(dev); + + error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)); + if (error) + return error; + } + return 0; +} + +#if WIRELESS_EXT > 17 +struct { + pmkid_list_t pmkids; + pmkid_t foo[MAXPMKID-1]; +} pmkid_list; + +static int +wl_iw_set_pmksa( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + struct iw_pmksa *iwpmksa; + uint i; + int ret = 0; + char eabuf[ETHER_ADDR_STR_LEN]; + pmkid_t * pmkid_array = pmkid_list.pmkids.pmkid; + + WL_WSEC(("%s: SIOCSIWPMKSA\n", dev->name)); + + RETURN_IF_EXTRA_NULL(extra); + + iwpmksa = (struct iw_pmksa *)extra; + bzero((char *)eabuf, ETHER_ADDR_STR_LEN); + + if (iwpmksa->cmd == IW_PMKSA_FLUSH) { + WL_WSEC(("wl_iw_set_pmksa - IW_PMKSA_FLUSH\n")); + bzero((char *)&pmkid_list, sizeof(pmkid_list)); + } + + else if (iwpmksa->cmd == IW_PMKSA_REMOVE) { + { + pmkid_list_t pmkid, *pmkidptr; + uint j; + pmkidptr = &pmkid; + + bcopy(&iwpmksa->bssid.sa_data[0], &pmkidptr->pmkid[0].BSSID, + ETHER_ADDR_LEN); + bcopy(&iwpmksa->pmkid[0], &pmkidptr->pmkid[0].PMKID, WPA2_PMKID_LEN); + + WL_WSEC(("wl_iw_set_pmksa,IW_PMKSA_REMOVE - PMKID: %s = ", + bcm_ether_ntoa(&pmkidptr->pmkid[0].BSSID, + eabuf))); + for (j = 0; j < WPA2_PMKID_LEN; j++) + WL_WSEC(("%02x ", pmkidptr->pmkid[0].PMKID[j])); + WL_WSEC(("\n")); + } + + for (i = 0; i < pmkid_list.pmkids.npmkid; i++) + if (!bcmp(&iwpmksa->bssid.sa_data[0], &pmkid_array[i].BSSID, + ETHER_ADDR_LEN)) + break; + + if ((pmkid_list.pmkids.npmkid > 0) && (i < pmkid_list.pmkids.npmkid)) { + bzero(&pmkid_array[i], sizeof(pmkid_t)); + for (; i < (pmkid_list.pmkids.npmkid - 1); i++) { + bcopy(&pmkid_array[i+1].BSSID, + &pmkid_array[i].BSSID, + ETHER_ADDR_LEN); + bcopy(&pmkid_array[i+1].PMKID, + &pmkid_array[i].PMKID, + WPA2_PMKID_LEN); + } + pmkid_list.pmkids.npmkid--; + } + else + ret = -EINVAL; + } + + else if (iwpmksa->cmd == IW_PMKSA_ADD) { + for (i = 0; i < pmkid_list.pmkids.npmkid; i++) + if (!bcmp(&iwpmksa->bssid.sa_data[0], &pmkid_array[i].BSSID, + ETHER_ADDR_LEN)) + break; + if (i < MAXPMKID) { + bcopy(&iwpmksa->bssid.sa_data[0], + &pmkid_array[i].BSSID, + ETHER_ADDR_LEN); + bcopy(&iwpmksa->pmkid[0], &pmkid_array[i].PMKID, + WPA2_PMKID_LEN); + if (i == pmkid_list.pmkids.npmkid) + pmkid_list.pmkids.npmkid++; + } + else + ret = -EINVAL; + + { + uint j; + uint k; + k = pmkid_list.pmkids.npmkid; + WL_WSEC(("wl_iw_set_pmksa,IW_PMKSA_ADD - PMKID: %s = ", + bcm_ether_ntoa(&pmkid_array[k].BSSID, + eabuf))); + for (j = 0; j < WPA2_PMKID_LEN; j++) + WL_WSEC(("%02x ", pmkid_array[k].PMKID[j])); + WL_WSEC(("\n")); + } + } + WL_WSEC(("PRINTING pmkid LIST - No of elements %d", pmkid_list.pmkids.npmkid)); + for (i = 0; i < pmkid_list.pmkids.npmkid; i++) { + uint j; + WL_WSEC(("\nPMKID[%d]: %s = ", i, + bcm_ether_ntoa(&pmkid_array[i].BSSID, + eabuf))); + for (j = 0; j < WPA2_PMKID_LEN; j++) + WL_WSEC(("%02x ", pmkid_array[i].PMKID[j])); + } + WL_WSEC(("\n")); + + if (!ret) + ret = dev_wlc_bufvar_set(dev, "pmkid_info", (char *)&pmkid_list, + sizeof(pmkid_list)); + return ret; +} +#endif + +static int +wl_iw_get_encodeext( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + WL_TRACE(("%s: SIOCGIWENCODEEXT\n", dev->name)); + return 0; +} + + +static uint32 +wl_iw_create_wpaauth_wsec(struct net_device *dev) +{ + wl_iw_t *iw = NETDEV_PRIV(dev); + uint32 wsec; + + + if (iw->pcipher & (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104)) + wsec = WEP_ENABLED; + else if (iw->pcipher & IW_AUTH_CIPHER_TKIP) + wsec = TKIP_ENABLED; + else if (iw->pcipher & IW_AUTH_CIPHER_CCMP) + wsec = AES_ENABLED; + else + wsec = 0; + + + if (iw->gcipher & (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104)) + wsec |= WEP_ENABLED; + else if (iw->gcipher & IW_AUTH_CIPHER_TKIP) + wsec |= TKIP_ENABLED; + else if (iw->gcipher & IW_AUTH_CIPHER_CCMP) + wsec |= AES_ENABLED; + + + if (wsec == 0 && iw->privacy_invoked) + wsec = WEP_ENABLED; + + WL_INFORM(("%s: returning wsec of %d\n", __FUNCTION__, wsec)); + + return wsec; +} + +static int +wl_iw_set_wpaauth( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error = 0; + int paramid; + int paramval; + int val = 0; + wl_iw_t *iw = NETDEV_PRIV(dev); + + paramid = vwrq->flags & IW_AUTH_INDEX; + paramval = vwrq->value; + + WL_TRACE(("%s: SIOCSIWAUTH, %s(%d), paramval = 0x%0x\n", + dev->name, + paramid == IW_AUTH_WPA_VERSION ? "IW_AUTH_WPA_VERSION" : + paramid == IW_AUTH_CIPHER_PAIRWISE ? "IW_AUTH_CIPHER_PAIRWISE" : + paramid == IW_AUTH_CIPHER_GROUP ? "IW_AUTH_CIPHER_GROUP" : + paramid == IW_AUTH_KEY_MGMT ? "IW_AUTH_KEY_MGMT" : + paramid == IW_AUTH_TKIP_COUNTERMEASURES ? "IW_AUTH_TKIP_COUNTERMEASURES" : + paramid == IW_AUTH_DROP_UNENCRYPTED ? "IW_AUTH_DROP_UNENCRYPTED" : + paramid == IW_AUTH_80211_AUTH_ALG ? "IW_AUTH_80211_AUTH_ALG" : + paramid == IW_AUTH_WPA_ENABLED ? "IW_AUTH_WPA_ENABLED" : + paramid == IW_AUTH_RX_UNENCRYPTED_EAPOL ? "IW_AUTH_RX_UNENCRYPTED_EAPOL" : + paramid == IW_AUTH_ROAMING_CONTROL ? "IW_AUTH_ROAMING_CONTROL" : + paramid == IW_AUTH_PRIVACY_INVOKED ? "IW_AUTH_PRIVACY_INVOKED" : + "UNKNOWN", + paramid, paramval)); + +#if defined(SOFTAP) + if (ap_cfg_running) { + WL_TRACE(("%s: Not executed, reason -'SOFTAP is active'\n", __FUNCTION__)); + return 0; + } +#endif + + switch (paramid) { + case IW_AUTH_WPA_VERSION: + + if (paramval & IW_AUTH_WPA_VERSION_DISABLED) + val = WPA_AUTH_DISABLED; + else if (paramval & (IW_AUTH_WPA_VERSION_WPA)) + val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED; + else if (paramval & IW_AUTH_WPA_VERSION_WPA2) + val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; + WL_ERROR(("%s: %d: setting wpa_auth to 0x%0x\n", __FUNCTION__, __LINE__, val)); + if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val))) + return error; + break; + + case IW_AUTH_CIPHER_PAIRWISE: + iw->pcipher = paramval; + val = wl_iw_create_wpaauth_wsec(dev); + if ((error = dev_wlc_intvar_set(dev, "wsec", val))) + return error; + break; + + case IW_AUTH_CIPHER_GROUP: + iw->gcipher = paramval; + val = wl_iw_create_wpaauth_wsec(dev); + if ((error = dev_wlc_intvar_set(dev, "wsec", val))) + return error; + break; + + case IW_AUTH_KEY_MGMT: + if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) + return error; + + if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { + if (paramval & IW_AUTH_KEY_MGMT_PSK) + val = WPA_AUTH_PSK; + else + val = WPA_AUTH_UNSPECIFIED; + if (paramval & 0x04) + val |= WPA2_AUTH_FT; + } + else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { + if (paramval & IW_AUTH_KEY_MGMT_PSK) + val = WPA2_AUTH_PSK; + else + val = WPA2_AUTH_UNSPECIFIED; + if (paramval & 0x04) + val |= WPA2_AUTH_FT; + } + + else if (paramval & IW_AUTH_KEY_MGMT_PSK) { + if (iw->wpaversion == IW_AUTH_WPA_VERSION_WPA) + val = WPA_AUTH_PSK; + else if (iw->wpaversion == IW_AUTH_WPA_VERSION_WPA2) + val = WPA2_AUTH_PSK; + else + val = WPA_AUTH_DISABLED; + } else if (paramval & IW_AUTH_KEY_MGMT_802_1X) { + if (iw->wpaversion == IW_AUTH_WPA_VERSION_WPA) + val = WPA_AUTH_UNSPECIFIED; + else if (iw->wpaversion == IW_AUTH_WPA_VERSION_WPA2) + val = WPA2_AUTH_UNSPECIFIED; + else + val = WPA_AUTH_DISABLED; + } + else + val = WPA_AUTH_DISABLED; + + WL_INFORM(("%s: %d: setting wpa_auth to %d\n", __FUNCTION__, __LINE__, val)); + if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val))) + return error; + break; + + case IW_AUTH_TKIP_COUNTERMEASURES: + dev_wlc_bufvar_set(dev, "tkip_countermeasures", (char *)¶mval, 1); + break; + + case IW_AUTH_80211_AUTH_ALG: + + WL_INFORM(("Setting the D11auth %d\n", paramval)); + if (paramval == IW_AUTH_ALG_OPEN_SYSTEM) + val = 0; + else if (paramval == IW_AUTH_ALG_SHARED_KEY) + val = 1; + else if (paramval == (IW_AUTH_ALG_OPEN_SYSTEM | IW_AUTH_ALG_SHARED_KEY)) + val = 2; + else + error = 1; + if (!error && (error = dev_wlc_intvar_set(dev, "auth", val))) + return error; + break; + + case IW_AUTH_WPA_ENABLED: + if (paramval == 0) { + iw->privacy_invoked = 0; + iw->pcipher = 0; + iw->gcipher = 0; + val = wl_iw_create_wpaauth_wsec(dev); + if ((error = dev_wlc_intvar_set(dev, "wsec", val))) + return error; + WL_INFORM(("%s: %d: setting wpa_auth to %d, wsec to %d\n", + __FUNCTION__, __LINE__, paramval, val)); + dev_wlc_intvar_set(dev, "wpa_auth", paramval); + return error; + } + + + break; + + case IW_AUTH_DROP_UNENCRYPTED: + if ((error = dev_wlc_intvar_set(dev, "wsec_restrict", paramval))) + return error; + break; + + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + dev_wlc_bufvar_set(dev, "rx_unencrypted_eapol", (char *)¶mval, 1); + break; + +#if WIRELESS_EXT > 17 + case IW_AUTH_ROAMING_CONTROL: + WL_INFORM(("%s: IW_AUTH_ROAMING_CONTROL\n", __FUNCTION__)); + + break; + + case IW_AUTH_PRIVACY_INVOKED: + iw->privacy_invoked = paramval; + val = wl_iw_create_wpaauth_wsec(dev); + if ((error = dev_wlc_intvar_set(dev, "wsec", val))) + return error; + break; + +#endif + default: + break; + } + return 0; +} +#define VAL_PSK(_val) (((_val) & WPA_AUTH_PSK) || ((_val) & WPA2_AUTH_PSK)) + +static int +wl_iw_get_wpaauth( + struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra +) +{ + int error; + int paramid; + int paramval = 0; + int val; + wl_iw_t *iw = NETDEV_PRIV(dev); + + WL_TRACE(("%s: SIOCGIWAUTH\n", dev->name)); + + paramid = vwrq->flags & IW_AUTH_INDEX; + + switch (paramid) { + case IW_AUTH_WPA_VERSION: + paramval = iw->wpaversion; + break; + + case IW_AUTH_CIPHER_PAIRWISE: + paramval = iw->pcipher; + break; + + case IW_AUTH_CIPHER_GROUP: + paramval = iw->gcipher; + break; + + case IW_AUTH_KEY_MGMT: + + if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) + return error; + if (VAL_PSK(val)) + paramval = IW_AUTH_KEY_MGMT_PSK; + else + paramval = IW_AUTH_KEY_MGMT_802_1X; + + break; + + case IW_AUTH_TKIP_COUNTERMEASURES: + dev_wlc_bufvar_get(dev, "tkip_countermeasures", (char *)¶mval, 1); + break; + + case IW_AUTH_DROP_UNENCRYPTED: + dev_wlc_intvar_get(dev, "wsec_restrict", ¶mval); + break; + + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + dev_wlc_bufvar_get(dev, "rx_unencrypted_eapol", (char *)¶mval, 1); + break; + + case IW_AUTH_80211_AUTH_ALG: + + if ((error = dev_wlc_intvar_get(dev, "auth", &val))) + return error; + if (!val) + paramval = IW_AUTH_ALG_OPEN_SYSTEM; + else + paramval = IW_AUTH_ALG_SHARED_KEY; + break; + case IW_AUTH_WPA_ENABLED: + if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) + return error; + if (val) + paramval = TRUE; + else + paramval = FALSE; + break; +#if WIRELESS_EXT > 17 + case IW_AUTH_ROAMING_CONTROL: + WL_ERROR(("%s: IW_AUTH_ROAMING_CONTROL\n", __FUNCTION__)); + + break; + case IW_AUTH_PRIVACY_INVOKED: + paramval = iw->privacy_invoked; + break; + +#endif + } + vwrq->value = paramval; + return 0; +} +#endif + + +#ifdef SOFTAP + +static int ap_macmode = MACLIST_MODE_DISABLED; +static struct mflist ap_black_list; + +static int +wl_iw_parse_wep(char *keystr, wl_wsec_key_t *key) +{ + char hex[] = "XX"; + unsigned char *data = key->data; + + switch (strlen(keystr)) { + case 5: + case 13: + case 16: + key->len = strlen(keystr); + memcpy(data, keystr, key->len + 1); + break; + case 12: + case 28: + case 34: + case 66: + + if (!strnicmp(keystr, "0x", 2)) + keystr += 2; + else + return -1; + + case 10: + case 26: + case 32: + case 64: + key->len = strlen(keystr) / 2; + while (*keystr) { + strncpy(hex, keystr, 2); + *data++ = (char) bcm_strtoul(hex, NULL, 16); + keystr += 2; + } + break; + default: + return -1; + } + + switch (key->len) { + case 5: + key->algo = CRYPTO_ALGO_WEP1; + break; + case 13: + key->algo = CRYPTO_ALGO_WEP128; + break; + case 16: + + key->algo = CRYPTO_ALGO_AES_CCM; + break; + case 32: + key->algo = CRYPTO_ALGO_TKIP; + break; + default: + return -1; + } + + + key->flags |= WL_PRIMARY_KEY; + + return 0; +} + +#ifdef EXT_WPA_CRYPTO +#define SHA1HashSize 20 +extern void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen); + +#else + +#define SHA1HashSize 20 +static int +pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen) +{ + WL_ERROR(("WARNING: %s is not implemented !!!\n", __FUNCTION__)); + return -1; +} + +#endif + + +static int +dev_iw_write_cfg1_bss_var(struct net_device *dev, int val) +{ + struct { + int cfg; + int val; + } bss_setbuf; + + int bss_set_res; + char smbuf[WLC_IOCTL_SMLEN]; + memset(smbuf, 0, sizeof(smbuf)); + + bss_setbuf.cfg = 1; + bss_setbuf.val = val; + + bss_set_res = dev_iw_iovar_setbuf(dev, "bss", + &bss_setbuf, sizeof(bss_setbuf), smbuf, sizeof(smbuf)); + WL_TRACE(("%s: bss_set_result:%d set with %d\n", __FUNCTION__, bss_set_res, val)); + + return bss_set_res; +} + + + +#ifndef AP_ONLY +static int +wl_bssiovar_mkbuf( + const char *iovar, + int bssidx, + void *param, + int paramlen, + void *bufptr, + int buflen, + int *perr) +{ + const char *prefix = "bsscfg:"; + int8* p; + uint prefixlen; + uint namelen; + uint iolen; + + prefixlen = strlen(prefix); + namelen = strlen(iovar) + 1; + iolen = prefixlen + namelen + sizeof(int) + paramlen; + + + if (buflen < 0 || iolen > (uint)buflen) { + *perr = BCME_BUFTOOSHORT; + return 0; + } + + p = (int8*)bufptr; + + + memcpy(p, prefix, prefixlen); + p += prefixlen; + + + memcpy(p, iovar, namelen); + p += namelen; + + + bssidx = htod32(bssidx); + memcpy(p, &bssidx, sizeof(int32)); + p += sizeof(int32); + + + if (paramlen) + memcpy(p, param, paramlen); + + *perr = 0; + return iolen; +} +#endif + + + + +#define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base)) + + +#if defined(CSCAN) + + + +static int +wl_iw_combined_scan_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, int nchan) +{ + int params_size = WL_SCAN_PARAMS_FIXED_SIZE + WL_NUMCHANNELS * sizeof(uint16); + int err = 0; + char *p; + int i; + iscan_info_t *iscan = g_iscan; + + WL_TRACE(("%s nssid=%d nchan=%d\n", __FUNCTION__, nssid, nchan)); + + if ((!dev) && (!g_iscan) && (!iscan->iscan_ex_params_p)) { + WL_ERROR(("%s error exit\n", __FUNCTION__)); + err = -1; + goto exit; + } + +#ifdef PNO_SUPPORT + + if (dhd_dev_get_pno_status(dev)) { + WL_ERROR(("%s: Scan called when PNO is active\n", __FUNCTION__)); + } +#endif + + params_size += WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t); + + + if (nssid > 0) { + i = OFFSETOF(wl_scan_params_t, channel_list) + nchan * sizeof(uint16); + i = ROUNDUP(i, sizeof(uint32)); + if (i + nssid * sizeof(wlc_ssid_t) > params_size) { + printf("additional ssids exceed params_size\n"); + err = -1; + goto exit; + } + + p = ((char*)&iscan->iscan_ex_params_p->params) + i; + memcpy(p, ssids_local, nssid * sizeof(wlc_ssid_t)); + p += nssid * sizeof(wlc_ssid_t); + } else { + p = (char*)iscan->iscan_ex_params_p->params.channel_list + nchan * sizeof(uint16); + } + + + iscan->iscan_ex_params_p->params.channel_num = + htod32((nssid << WL_SCAN_PARAMS_NSSID_SHIFT) | + (nchan & WL_SCAN_PARAMS_COUNT_MASK)); + + nssid = (uint) + ((iscan->iscan_ex_params_p->params.channel_num >> WL_SCAN_PARAMS_NSSID_SHIFT) & + WL_SCAN_PARAMS_COUNT_MASK); + + + params_size = (int) (p - (char*)iscan->iscan_ex_params_p + nssid * sizeof(wlc_ssid_t)); + iscan->iscan_ex_param_size = params_size; + + iscan->list_cur = iscan->list_hdr; + iscan->iscan_state = ISCAN_STATE_SCANING; + wl_iw_set_event_mask(dev); + mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000); + + iscan->timer_on = 1; + +#ifdef SCAN_DUMP + { + int i; + WL_SCAN(("\n### List of SSIDs to scan ###\n")); + for (i = 0; i < nssid; i++) { + if (!ssids_local[i].SSID_len) + WL_SCAN(("%d: Broadcast scan\n", i)); + else + WL_SCAN(("%d: scan for %s size =%d\n", i, + ssids_local[i].SSID, ssids_local[i].SSID_len)); + } + WL_SCAN(("### List of channels to scan ###\n")); + for (i = 0; i < nchan; i++) + { + WL_SCAN(("%d ", iscan->iscan_ex_params_p->params.channel_list[i])); + } + WL_SCAN(("\nnprobes=%d\n", iscan->iscan_ex_params_p->params.nprobes)); + WL_SCAN(("active_time=%d\n", iscan->iscan_ex_params_p->params.active_time)); + WL_SCAN(("passive_time=%d\n", iscan->iscan_ex_params_p->params.passive_time)); + WL_SCAN(("home_time=%d\n", iscan->iscan_ex_params_p->params.home_time)); + WL_SCAN(("scan_type=%d\n", iscan->iscan_ex_params_p->params.scan_type)); + WL_SCAN(("\n###################\n")); + } +#endif + + if (params_size > WLC_IOCTL_MEDLEN) { + WL_ERROR(("Set ISCAN for %s due to params_size=%d \n", + __FUNCTION__, params_size)); + err = -1; + } + + if ((err = dev_iw_iovar_setbuf(dev, "iscan", iscan->iscan_ex_params_p, + iscan->iscan_ex_param_size, + iscan->ioctlbuf, sizeof(iscan->ioctlbuf)))) { + WL_TRACE(("Set ISCAN for %s failed with %d\n", __FUNCTION__, err)); + err = -1; + } + +exit: + return err; +} + + +static int +iwpriv_set_cscan(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *ext) +{ + int res; + char *extra = NULL; + iscan_info_t *iscan = g_iscan; + wlc_ssid_t ssids_local[WL_SCAN_PARAMS_SSID_MAX]; + int nssid = 0; + int nchan = 0; + char *str_ptr; + + WL_TRACE(("%s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n", + __FUNCTION__, info->cmd, info->flags, + wrqu->data.pointer, wrqu->data.length)); + + if (g_onoff == G_WLAN_SET_OFF) { + WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__)); + return -ENODEV; + } + + if (wrqu->data.length == 0) { + WL_ERROR(("IWPRIV argument len = 0\n")); + return -EINVAL; + } + + if (!iscan->iscan_ex_params_p) { + return -EFAULT; + } + + if (!(extra = kmalloc(wrqu->data.length+1, GFP_KERNEL))) + return -ENOMEM; + + if (copy_from_user(extra, wrqu->data.pointer, wrqu->data.length)) { + res = -EFAULT; + goto exit_proc; + } + + extra[wrqu->data.length] = 0; + WL_ERROR(("Got str param in iw_point:\n %s\n", extra)); + + str_ptr = extra; + + + if (strncmp(str_ptr, GET_SSID, strlen(GET_SSID))) { + WL_ERROR(("%s Error: extracting SSID='' string\n", __FUNCTION__)); + res = -EINVAL; + goto exit_proc; + } + + str_ptr += strlen(GET_SSID); + nssid = wl_iw_parse_ssid_list(&str_ptr, ssids_local, nssid, + WL_SCAN_PARAMS_SSID_MAX); + if (nssid == -1) { + WL_ERROR(("%s wrong ssid list", __FUNCTION__)); + res = -EINVAL; + goto exit_proc; + } + + memset(iscan->iscan_ex_params_p, 0, iscan->iscan_ex_param_size); + ASSERT(iscan->iscan_ex_param_size < WLC_IOCTL_MAXLEN); + + + wl_iw_iscan_prep(&iscan->iscan_ex_params_p->params, NULL); + iscan->iscan_ex_params_p->version = htod32(ISCAN_REQ_VERSION); + iscan->iscan_ex_params_p->action = htod16(WL_SCAN_ACTION_START); + iscan->iscan_ex_params_p->scan_duration = htod16(0); + + + if ((nchan = wl_iw_parse_channel_list(&str_ptr, + &iscan->iscan_ex_params_p->params.channel_list[0], + WL_NUMCHANNELS)) == -1) { + WL_ERROR(("%s missing channel list\n", __FUNCTION__)); + res = -EINVAL; + goto exit_proc; + } + + + get_parameter_from_string(&str_ptr, + GET_NPROBE, PTYPE_INTDEC, + &iscan->iscan_ex_params_p->params.nprobes, 2); + + get_parameter_from_string(&str_ptr, GET_ACTIVE_ASSOC_DWELL, PTYPE_INTDEC, + &iscan->iscan_ex_params_p->params.active_time, 4); + + get_parameter_from_string(&str_ptr, GET_PASSIVE_ASSOC_DWELL, PTYPE_INTDEC, + &iscan->iscan_ex_params_p->params.passive_time, 4); + + get_parameter_from_string(&str_ptr, GET_HOME_DWELL, PTYPE_INTDEC, + &iscan->iscan_ex_params_p->params.home_time, 4); + + get_parameter_from_string(&str_ptr, GET_SCAN_TYPE, PTYPE_INTDEC, + &iscan->iscan_ex_params_p->params.scan_type, 1); + + + res = wl_iw_combined_scan_set(dev, ssids_local, nssid, nchan); + +exit_proc: + kfree(extra); + + return res; +} + + +static int +wl_iw_set_cscan( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int res = -1; + iscan_info_t *iscan = g_iscan; + wlc_ssid_t ssids_local[WL_SCAN_PARAMS_SSID_MAX]; + int nssid = 0; + int nchan = 0; + cscan_tlv_t *cscan_tlv_temp; + char type; + char *str_ptr; + int tlv_size_left; +#ifdef TLV_DEBUG + int i; + char tlv_in_example[] = { + 'C', 'S', 'C', 'A', 'N', ' ', + 0x53, 0x01, 0x00, 0x00, + 'S', + 0x00, + 'S', + 0x04, + 'B', 'R', 'C', 'M', + 'C', + 0x06, + 'P', + 0x94, + 0x11, + 'T', + 0x01 + }; +#endif + + WL_TRACE(("\n### %s: info->cmd:%x, info->flags:%x, u.data=0x%p, u.len=%d\n", + __FUNCTION__, info->cmd, info->flags, + wrqu->data.pointer, wrqu->data.length)); + + net_os_wake_lock(dev); + + if (g_onoff == G_WLAN_SET_OFF) { + WL_TRACE(("%s: driver is not up yet after START\n", __FUNCTION__)); + return -1; + } + + if (wrqu->data.length < (strlen(CSCAN_COMMAND) + sizeof(cscan_tlv_t))) { + WL_ERROR(("%s argument=%d less %d\n", __FUNCTION__, + wrqu->data.length, (int)(strlen(CSCAN_COMMAND) + sizeof(cscan_tlv_t)))); + return -1; + } + +#ifdef TLV_DEBUG + memcpy(extra, tlv_in_example, sizeof(tlv_in_example)); + wrqu->data.length = sizeof(tlv_in_example); + for (i = 0; i < wrqu->data.length; i++) + printf("%02X ", extra[i]); + printf("\n"); +#endif + + str_ptr = extra; + str_ptr += strlen(CSCAN_COMMAND); + tlv_size_left = wrqu->data.length - strlen(CSCAN_COMMAND); + + cscan_tlv_temp = (cscan_tlv_t *)str_ptr; + memset(ssids_local, 0, sizeof(ssids_local)); + + if ((cscan_tlv_temp->prefix == CSCAN_TLV_PREFIX) && + (cscan_tlv_temp->version == CSCAN_TLV_VERSION) && + (cscan_tlv_temp->subver == CSCAN_TLV_SUBVERSION)) + { + str_ptr += sizeof(cscan_tlv_t); + tlv_size_left -= sizeof(cscan_tlv_t); + + + if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local, + WL_SCAN_PARAMS_SSID_MAX, &tlv_size_left)) <= 0) { + WL_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid)); + goto exit_proc; + } + else { + + memset(iscan->iscan_ex_params_p, 0, iscan->iscan_ex_param_size); + + + wl_iw_iscan_prep(&iscan->iscan_ex_params_p->params, NULL); + iscan->iscan_ex_params_p->version = htod32(ISCAN_REQ_VERSION); + iscan->iscan_ex_params_p->action = htod16(WL_SCAN_ACTION_START); + iscan->iscan_ex_params_p->scan_duration = htod16(0); + + + while (tlv_size_left > 0) + { + type = str_ptr[0]; + switch (type) { + case CSCAN_TLV_TYPE_CHANNEL_IE: + + if ((nchan = wl_iw_parse_channel_list_tlv(&str_ptr, + &iscan->iscan_ex_params_p->params.channel_list[0], + WL_NUMCHANNELS, &tlv_size_left)) == -1) { + WL_ERROR(("%s missing channel list\n", + __FUNCTION__)); + goto exit_proc; + } + break; + case CSCAN_TLV_TYPE_NPROBE_IE: + if ((res = wl_iw_parse_data_tlv(&str_ptr, + &iscan->iscan_ex_params_p->params.nprobes, + sizeof(iscan->iscan_ex_params_p->params.nprobes), + type, sizeof(char), &tlv_size_left)) == -1) { + WL_ERROR(("%s return %d\n", + __FUNCTION__, res)); + goto exit_proc; + } + break; + case CSCAN_TLV_TYPE_ACTIVE_IE: + if ((res = wl_iw_parse_data_tlv(&str_ptr, + &iscan->iscan_ex_params_p->params.active_time, + sizeof(iscan->iscan_ex_params_p->params.active_time), + type, sizeof(short), &tlv_size_left)) == -1) { + WL_ERROR(("%s return %d\n", + __FUNCTION__, res)); + goto exit_proc; + } + break; + case CSCAN_TLV_TYPE_PASSIVE_IE: + if ((res = wl_iw_parse_data_tlv(&str_ptr, + &iscan->iscan_ex_params_p->params.passive_time, + sizeof(iscan->iscan_ex_params_p->params.passive_time), + type, sizeof(short), &tlv_size_left)) == -1) { + WL_ERROR(("%s return %d\n", + __FUNCTION__, res)); + goto exit_proc; + } + break; + case CSCAN_TLV_TYPE_HOME_IE: + if ((res = wl_iw_parse_data_tlv(&str_ptr, + &iscan->iscan_ex_params_p->params.home_time, + sizeof(iscan->iscan_ex_params_p->params.home_time), + type, sizeof(short), &tlv_size_left)) == -1) { + WL_ERROR(("%s return %d\n", + __FUNCTION__, res)); + goto exit_proc; + } + break; + case CSCAN_TLV_TYPE_STYPE_IE: + if ((res = wl_iw_parse_data_tlv(&str_ptr, + &iscan->iscan_ex_params_p->params.scan_type, + sizeof(iscan->iscan_ex_params_p->params.scan_type), + type, sizeof(char), &tlv_size_left)) == -1) { + WL_ERROR(("%s return %d\n", + __FUNCTION__, res)); + goto exit_proc; + } + break; + + default : + WL_ERROR(("%s get unkwown type %X\n", + __FUNCTION__, type)); + goto exit_proc; + break; + } + } + } + } + else { + WL_ERROR(("%s get wrong TLV command\n", __FUNCTION__)); + goto exit_proc; + } + +#if defined(CONFIG_FIRST_SCAN) + if (g_first_broadcast_scan < BROADCAST_SCAN_FIRST_RESULT_CONSUMED) { + if (++g_first_counter_scans == MAX_ALLOWED_BLOCK_SCAN_FROM_FIRST_SCAN) { + + WL_ERROR(("%s Clean up First scan flag which is %d\n", + __FUNCTION__, g_first_broadcast_scan)); + g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_CONSUMED; + } + else { + WL_ERROR(("%s Ignoring CSCAN : First Scan is not done yet %d\n", + __FUNCTION__, g_first_counter_scans)); + return -EBUSY; + } + } +#endif + + + res = wl_iw_combined_scan_set(dev, ssids_local, nssid, nchan); + +exit_proc: + net_os_wake_unlock(dev); + return res; +} + +#endif + +#ifdef CONFIG_WPS2 +static int +wl_iw_del_wps_probe_req_ie( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + int ret; + vndr_ie_setbuf_t *ie_delbuf; + + if (g_wps_probe_req_ie) { + ie_delbuf = (vndr_ie_setbuf_t *)(g_wps_probe_req_ie + strlen("vndr_ie ")); + strncpy(ie_delbuf->cmd, "del", 3); + ie_delbuf->cmd[3] = '\0'; + + ret = dev_wlc_ioctl(dev, WLC_SET_VAR, g_wps_probe_req_ie, g_wps_probe_req_ie_len); + if (ret) { + WL_ERROR(("ioctl failed %d \n", ret)); + } + + kfree(g_wps_probe_req_ie); + g_wps_probe_req_ie = NULL; + g_wps_probe_req_ie_len = 0; + } + + return 0; +} + +static int +wl_iw_add_wps_probe_req_ie( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra +) +{ + char *str_ptr = NULL; + char *bufptr = NULL; + uint buflen, datalen, iecount, pktflag, iolen, total_len; + int ret = 0; + vndr_ie_setbuf_t *ie_setbuf = NULL; + + if (!g_wps_probe_req_ie) { + ret = -1; + str_ptr = extra; + str_ptr += WPS_PROBE_REQ_IE_CMD_LENGTH; + datalen = wrqu->data.length - WPS_PROBE_REQ_IE_CMD_LENGTH; + + + + buflen = sizeof(vndr_ie_setbuf_t) + datalen - sizeof(vndr_ie_t); + ie_setbuf = (vndr_ie_setbuf_t *)kmalloc(buflen, GFP_KERNEL); + if (!ie_setbuf) { + WL_ERROR(("memory alloc failure ie_setbuf\n")); + return ret; + } + + memset(ie_setbuf, 0x00, buflen); + + + strncpy(ie_setbuf->cmd, "add", VNDR_IE_CMD_LEN - 1); + ie_setbuf->cmd[VNDR_IE_CMD_LEN - 1] = '\0'; + + + iecount = htod32(1); + memcpy((void *)&ie_setbuf->vndr_ie_buffer.iecount, &iecount, sizeof(int)); + + + pktflag = 0x10; + memcpy((void *)&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].pktflag, + &pktflag, sizeof(uint32)); + + memcpy((void *)&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data, + str_ptr, datalen); + + total_len = strlen("vndr_ie ") + buflen; + bufptr = (char *)kmalloc(total_len, GFP_KERNEL); + if (!bufptr) { + WL_ERROR(("memory alloc failure bufptr\n")); + goto fail; + } + + iolen = bcm_mkiovar("vndr_ie", (char *)ie_setbuf, buflen, bufptr, total_len); + if (iolen == 0) { + WL_ERROR(("Buffer length is illegal\n")); + goto fail2; + } + + ret = dev_wlc_ioctl(dev, WLC_SET_VAR, bufptr, iolen); + if (ret) { + WL_ERROR(("ioctl failed\n")); + goto fail2; + } + + g_wps_probe_req_ie = (char *)kmalloc(iolen, GFP_KERNEL); + if (!g_wps_probe_req_ie) { + WL_ERROR(("memory alloc failure g_wps_probe_req_ie\n")); + goto fail2; + } + + memcpy(g_wps_probe_req_ie, bufptr, iolen); + g_wps_probe_req_ie_len = iolen; + } + +fail2: + if (bufptr) { + kfree(bufptr); + bufptr = NULL; + } +fail: + if (ie_setbuf) { + kfree(ie_setbuf); + ie_setbuf = NULL; + } + return ret; +} +#endif + + +#ifdef SOFTAP +#ifndef AP_ONLY + + +static int +thr_wait_for_2nd_eth_dev(void *data) +{ + wl_iw_t *iw; + int ret = 0; + unsigned long flags = 0; + + tsk_ctl_t *tsk_ctl = (tsk_ctl_t *)data; + struct net_device *dev = (struct net_device *)tsk_ctl->parent; + iw = *(wl_iw_t **)netdev_priv(dev); + + DAEMONIZE("wl0_eth_wthread"); + + + WL_SOFTAP(("\n>%s threda started:, PID:%x\n", __FUNCTION__, current->pid)); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + if (!iw) { + WL_ERROR(("%s: dev is null\n", __FUNCTION__)); + tsk_ctl->thr_pid = -1; + complete(&tsk_ctl->completed); + return -1; + } + DHD_OS_WAKE_LOCK(iw->pub); + complete(&tsk_ctl->completed); + if (down_timeout(&tsk_ctl->sema, msecs_to_jiffies(1000)) != 0) { +#else + if (down_interruptible(&tsk_ctl->sema) != 0) { +#endif + WL_ERROR(("\n%s: sap_eth_sema timeout \n", __FUNCTION__)); + ret = -1; + goto fail; + } + + SMP_RD_BARRIER_DEPENDS(); + if (tsk_ctl->terminated) { + ret = -1; + goto fail; + } + + flags = dhd_os_spin_lock(iw->pub); + if (!ap_net_dev) { + WL_ERROR((" ap_net_dev is null !!!")); + ret = -1; + dhd_os_spin_unlock(iw->pub, flags); + goto fail; + } + + WL_SOFTAP(("\n>%s: Thread:'softap ethdev IF:%s is detected!'\n\n", + __FUNCTION__, ap_net_dev->name)); + + ap_cfg_running = TRUE; + + dhd_os_spin_unlock(iw->pub, flags); + bcm_mdelay(500); + + + wl_iw_send_priv_event(priv_dev, "AP_SET_CFG_OK"); + +fail: + + DHD_OS_WAKE_UNLOCK(iw->pub); + + WL_SOFTAP(("\n>%s, thread completed\n", __FUNCTION__)); + + complete_and_exit(&tsk_ctl->completed, 0); + return ret; +} +#endif +#ifndef AP_ONLY +static int last_auto_channel = 6; +#endif + +static int +get_softap_auto_channel(struct net_device *dev, struct ap_profile *ap) +{ + int chosen = 0; + wl_uint32_list_t request; + int retry = 0; + int updown = 0; + int ret = 0; + wlc_ssid_t null_ssid; + int res = 0; +#ifndef AP_ONLY + int iolen = 0; + int mkvar_err = 0; + int bsscfg_index = 1; + char buf[WLC_IOCTL_SMLEN]; +#endif + WL_SOFTAP(("Enter %s\n", __FUNCTION__)); + +#ifndef AP_ONLY + if (ap_cfg_running) { + ap->channel = last_auto_channel; + return res; + } +#endif + + memset(&null_ssid, 0, sizeof(wlc_ssid_t)); + res |= dev_wlc_ioctl(dev, WLC_UP, &updown, sizeof(updown)); + +#ifdef AP_ONLY + res |= dev_wlc_ioctl(dev, WLC_SET_SSID, &null_ssid, sizeof(null_ssid)); +#else + + iolen = wl_bssiovar_mkbuf("ssid", bsscfg_index, (char *)(&null_ssid), + null_ssid.SSID_len+4, buf, sizeof(buf), &mkvar_err); + ASSERT(iolen); + res |= dev_wlc_ioctl(dev, WLC_SET_VAR, buf, iolen); + +#endif + + request.count = htod32(0); + ret = dev_wlc_ioctl(dev, WLC_START_CHANNEL_SEL, &request, sizeof(request)); + if (ret < 0) { + WL_ERROR(("can't start auto channel scan\n")); + goto fail; + } + + get_channel_retry: + bcm_mdelay(350); + + ret = dev_wlc_ioctl(dev, WLC_GET_CHANNEL_SEL, &chosen, sizeof(chosen)); + if (ret < 0 || dtoh32(chosen) == 0) { + if (retry++ < 15) { + goto get_channel_retry; + } else { + if (ret < 0) { + WL_ERROR(("can't get auto channel sel, err = %d, " + "chosen = 0x%04X\n", ret, (uint16)chosen)); + goto fail; + } else { + ap->channel = (uint16)last_auto_channel; + WL_ERROR(("auto channel sel timed out. we get channel %d\n", + ap->channel)); + } + } + } + + if (chosen) { + ap->channel = (uint16)chosen & 0x00FF; + WL_SOFTAP(("%s: Got auto channel = %d, attempt:%d\n", + __FUNCTION__, ap->channel, retry)); + } + + if ((res = dev_wlc_ioctl(dev, WLC_DOWN, &updown, sizeof(updown))) < 0) { + WL_ERROR(("%s fail to set up err =%d\n", __FUNCTION__, res)); + goto fail; + } + +#ifndef AP_ONLY + if (!res || !ret) + last_auto_channel = ap->channel; +#endif + +fail : + if (ret < 0) { + WL_TRACE(("%s: return value %d\n", __FUNCTION__, ret)); + return ret; + } + return res; +} + + +static int +set_ap_cfg(struct net_device *dev, struct ap_profile *ap) +{ + int updown = 0; + int channel = 0; + + wlc_ssid_t ap_ssid; + int max_assoc = 8; + + int res = 0; + int apsta_var = 0; +#ifndef AP_ONLY + int mpc = 0; + int iolen = 0; + int mkvar_err = 0; + int bsscfg_index = 1; + char buf[WLC_IOCTL_SMLEN]; +#endif + + if (!dev) { + WL_ERROR(("%s: dev is null\n", __FUNCTION__)); + return -1; + } + + net_os_wake_lock(dev); + DHD_OS_MUTEX_LOCK(&wl_softap_lock); + + WL_SOFTAP(("wl_iw: set ap profile:\n")); + WL_SOFTAP((" ssid = '%s'\n", ap->ssid)); + WL_SOFTAP((" security = '%s'\n", ap->sec)); + if (ap->key[0] != '\0') + WL_SOFTAP((" key = '%s'\n", ap->key)); + WL_SOFTAP((" channel = %d\n", ap->channel)); + WL_SOFTAP((" max scb = %d\n", ap->max_scb)); + +#ifdef AP_ONLY + if (ap_cfg_running) { + wl_iw_softap_deassoc_stations(dev, NULL); + ap_cfg_running = FALSE; + } +#endif + + + if (ap_cfg_running == FALSE) { + +#ifndef AP_ONLY + + + sema_init(&ap_eth_ctl.sema, 0); + + mpc = 0; + if ((res = dev_wlc_intvar_set(dev, "mpc", mpc))) { + WL_ERROR(("%s fail to set mpc\n", __FUNCTION__)); + goto fail; + } +#endif + + updown = 0; + if ((res = dev_wlc_ioctl(dev, WLC_DOWN, &updown, sizeof(updown)))) { + WL_ERROR(("%s fail to set updown\n", __FUNCTION__)); + goto fail; + } + +#ifdef AP_ONLY + + apsta_var = 0; + if ((res = dev_wlc_ioctl(dev, WLC_SET_AP, &apsta_var, sizeof(apsta_var)))) { + WL_ERROR(("%s fail to set apsta_var 0\n", __FUNCTION__)); + goto fail; + } + apsta_var = 1; + if ((res = dev_wlc_ioctl(dev, WLC_SET_AP, &apsta_var, sizeof(apsta_var)))) { + WL_ERROR(("%s fail to set apsta_var 1\n", __FUNCTION__)); + goto fail; + } + res = dev_wlc_ioctl(dev, WLC_GET_AP, &apsta_var, sizeof(apsta_var)); +#else + + apsta_var = 1; + iolen = wl_bssiovar_mkbuf("apsta", + bsscfg_index, &apsta_var, sizeof(apsta_var)+4, + buf, sizeof(buf), &mkvar_err); + ASSERT(iolen); + if ((res = dev_wlc_ioctl(dev, WLC_SET_VAR, buf, iolen)) < 0) { + WL_ERROR(("%s fail to set apsta \n", __FUNCTION__)); + goto fail; + } + WL_TRACE(("\n>in %s: apsta set result: %d \n", __FUNCTION__, res)); + + + mpc = 0; + if ((res = dev_wlc_intvar_set(dev, "mpc", mpc))) { + WL_ERROR(("%s fail to set mpc\n", __FUNCTION__)); + goto fail; + } + + +#endif + + updown = 1; + if ((res = dev_wlc_ioctl(dev, WLC_UP, &updown, sizeof(updown))) < 0) { + WL_ERROR(("%s fail to set apsta \n", __FUNCTION__)); + goto fail; + } + + } else { + + if (!ap_net_dev) { + WL_ERROR(("%s: ap_net_dev is null\n", __FUNCTION__)); + goto fail; + } + + res = wl_iw_softap_deassoc_stations(ap_net_dev, NULL); + + + if ((res = dev_iw_write_cfg1_bss_var(dev, 0)) < 0) { + WL_ERROR(("%s fail to set bss down\n", __FUNCTION__)); + goto fail; + } + } + + + if (strlen(ap->country_code)) { + WL_ERROR(("%s: Igonored: Country MUST be specified" + "COUNTRY command with \n", __FUNCTION__)); + } else { + WL_SOFTAP(("%s: Country code is not specified," + " will use Radio's default\n", + __FUNCTION__)); + + } + iolen = wl_bssiovar_mkbuf("closednet", + bsscfg_index, &ap->closednet, sizeof(ap->closednet)+4, + buf, sizeof(buf), &mkvar_err); + ASSERT(iolen); + if ((res = dev_wlc_ioctl(dev, WLC_SET_VAR, buf, iolen)) < 0) { + WL_ERROR(("%s failed to set 'closednet'for apsta \n", __FUNCTION__)); + goto fail; + } + + + if ((ap->channel == 0) && (get_softap_auto_channel(dev, ap) < 0)) { + ap->channel = 1; + WL_ERROR(("%s auto channel failed, use channel=%d\n", + __FUNCTION__, ap->channel)); + } + + channel = ap->channel; + if ((res = dev_wlc_ioctl(dev, WLC_SET_CHANNEL, &channel, sizeof(channel)))) { + WL_ERROR(("%s fail to set channel\n", __FUNCTION__)); + } + + + if (ap_cfg_running == FALSE) { + updown = 0; + if ((res = dev_wlc_ioctl(dev, WLC_UP, &updown, sizeof(updown)))) { + WL_ERROR(("%s fail to set up\n", __FUNCTION__)); + goto fail; + } + } + + max_assoc = ap->max_scb; + if ((res = dev_wlc_intvar_set(dev, "maxassoc", max_assoc))) { + WL_ERROR(("%s fail to set maxassoc\n", __FUNCTION__)); + goto fail; + } + + ap_ssid.SSID_len = strlen(ap->ssid); + strncpy(ap_ssid.SSID, ap->ssid, ap_ssid.SSID_len); + + +#ifdef AP_ONLY + if ((res = wl_iw_set_ap_security(dev, &my_ap)) != 0) { + WL_ERROR(("ERROR:%d in:%s, wl_iw_set_ap_security is skipped\n", + res, __FUNCTION__)); + goto fail; + } + wl_iw_send_priv_event(dev, "ASCII_CMD=AP_BSS_START"); + ap_cfg_running = TRUE; +#else + + iolen = wl_bssiovar_mkbuf("ssid", bsscfg_index, (char *)(&ap_ssid), + ap_ssid.SSID_len+4, buf, sizeof(buf), &mkvar_err); + ASSERT(iolen); + if ((res = dev_wlc_ioctl(dev, WLC_SET_VAR, buf, iolen)) != 0) { + WL_ERROR(("ERROR:%d in:%s, Security & BSS reconfiguration is skipped\n", + res, __FUNCTION__)); + goto fail; + } + if (ap_cfg_running == FALSE) { + + PROC_START(thr_wait_for_2nd_eth_dev, dev, &ap_eth_ctl, 0); + } else { + ap_eth_ctl.thr_pid = -1; + + if (ap_net_dev == NULL) { + WL_ERROR(("%s ERROR: ap_net_dev is NULL !!!\n", __FUNCTION__)); + goto fail; + } + + WL_ERROR(("%s: %s Configure security & restart AP bss \n", + __FUNCTION__, ap_net_dev->name)); + + + if ((res = wl_iw_set_ap_security(ap_net_dev, &my_ap)) < 0) { + WL_ERROR(("%s fail to set security : %d\n", __FUNCTION__, res)); + goto fail; + } + + + if ((res = dev_iw_write_cfg1_bss_var(dev, 1)) < 0) { + WL_ERROR(("%s fail to set bss up\n", __FUNCTION__)); + goto fail; + } + } +#endif +fail: + WL_SOFTAP(("%s exit with %d\n", __FUNCTION__, res)); + + DHD_OS_MUTEX_UNLOCK(&wl_softap_lock); + net_os_wake_unlock(dev); + + return res; +} +#endif + + + +static int +wl_iw_set_ap_security(struct net_device *dev, struct ap_profile *ap) +{ + int wsec = 0; + int wpa_auth = 0; + int res = 0; + int i; + char *ptr; +#ifdef AP_ONLY + int mpc = 0; + wlc_ssid_t ap_ssid; +#endif + wl_wsec_key_t key; + + WL_SOFTAP(("\nsetting SOFTAP security mode:\n")); + WL_SOFTAP(("wl_iw: set ap profile:\n")); + WL_SOFTAP((" ssid = '%s'\n", ap->ssid)); + WL_SOFTAP((" security = '%s'\n", ap->sec)); + if (ap->key[0] != '\0') + WL_SOFTAP((" key = '%s'\n", ap->key)); + WL_SOFTAP((" channel = %d\n", ap->channel)); + WL_SOFTAP((" max scb = %d\n", ap->max_scb)); + + + if (strnicmp(ap->sec, "open", strlen("open")) == 0) { + + + wsec = 0; + res = dev_wlc_intvar_set(dev, "wsec", wsec); + wpa_auth = WPA_AUTH_DISABLED; + res |= dev_wlc_intvar_set(dev, "wpa_auth", wpa_auth); + + WL_SOFTAP(("=====================\n")); + WL_SOFTAP((" wsec & wpa_auth set 'OPEN', result:&d %d\n", res)); + WL_SOFTAP(("=====================\n")); + + } else if (strnicmp(ap->sec, "wep", strlen("wep")) == 0) { + + + memset(&key, 0, sizeof(key)); + + wsec = WEP_ENABLED; + res = dev_wlc_intvar_set(dev, "wsec", wsec); + + key.index = 0; + if (wl_iw_parse_wep(ap->key, &key)) { + WL_SOFTAP(("wep key parse err!\n")); + return -1; + } + + key.index = htod32(key.index); + key.len = htod32(key.len); + key.algo = htod32(key.algo); + key.flags = htod32(key.flags); + + res |= dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)); + + wpa_auth = WPA_AUTH_DISABLED; + res |= dev_wlc_intvar_set(dev, "wpa_auth", wpa_auth); + + WL_SOFTAP(("=====================\n")); + WL_SOFTAP((" wsec & auth set 'WEP', result:&d %d\n", res)); + WL_SOFTAP(("=====================\n")); + + } else if (strnicmp(ap->sec, "wpa2-psk", strlen("wpa2-psk")) == 0) { + + + + wsec_pmk_t psk; + size_t key_len; + + wsec = AES_ENABLED; + dev_wlc_intvar_set(dev, "wsec", wsec); + + key_len = strlen(ap->key); + if (key_len < WSEC_MIN_PSK_LEN || key_len > WSEC_MAX_PSK_LEN) { + WL_SOFTAP(("passphrase must be between %d and %d characters long\n", + WSEC_MIN_PSK_LEN, WSEC_MAX_PSK_LEN)); + return -1; + } + + + if (key_len < WSEC_MAX_PSK_LEN) { + unsigned char output[2*SHA1HashSize]; + char key_str_buf[WSEC_MAX_PSK_LEN+1]; + + + memset(output, 0, sizeof(output)); + pbkdf2_sha1(ap->key, ap->ssid, strlen(ap->ssid), 4096, output, 32); + + ptr = key_str_buf; + for (i = 0; i < (WSEC_MAX_PSK_LEN/8); i++) { + + sprintf(ptr, "%02x%02x%02x%02x", (uint)output[i*4], + (uint)output[i*4+1], (uint)output[i*4+2], + (uint)output[i*4+3]); + ptr += 8; + } + WL_SOFTAP(("%s: passphase = %s\n", __FUNCTION__, key_str_buf)); + + psk.key_len = htod16((ushort)WSEC_MAX_PSK_LEN); + memcpy(psk.key, key_str_buf, psk.key_len); + } else { + psk.key_len = htod16((ushort) key_len); + memcpy(psk.key, ap->key, key_len); + } + psk.flags = htod16(WSEC_PASSPHRASE); + dev_wlc_ioctl(dev, WLC_SET_WSEC_PMK, &psk, sizeof(psk)); + + wpa_auth = WPA2_AUTH_PSK; + dev_wlc_intvar_set(dev, "wpa_auth", wpa_auth); + + } else if (strnicmp(ap->sec, "wpa-psk", strlen("wpa-psk")) == 0) { + + + wsec_pmk_t psk; + size_t key_len; + + wsec = TKIP_ENABLED; + res = dev_wlc_intvar_set(dev, "wsec", wsec); + + key_len = strlen(ap->key); + if (key_len < WSEC_MIN_PSK_LEN || key_len > WSEC_MAX_PSK_LEN) { + WL_SOFTAP(("passphrase must be between %d and %d characters long\n", + WSEC_MIN_PSK_LEN, WSEC_MAX_PSK_LEN)); + return -1; + } + + + if (key_len < WSEC_MAX_PSK_LEN) { + unsigned char output[2*SHA1HashSize]; + char key_str_buf[WSEC_MAX_PSK_LEN+1]; + bzero(output, 2*SHA1HashSize); + + WL_SOFTAP(("%s: do passhash...\n", __FUNCTION__)); + + pbkdf2_sha1(ap->key, ap->ssid, strlen(ap->ssid), 4096, output, 32); + + ptr = key_str_buf; + for (i = 0; i < (WSEC_MAX_PSK_LEN/8); i++) { + WL_SOFTAP(("[%02d]: %08x\n", i, *((unsigned int*)&output[i*4]))); + + sprintf(ptr, "%02x%02x%02x%02x", (uint)output[i*4], + (uint)output[i*4+1], (uint)output[i*4+2], + (uint)output[i*4+3]); + ptr += 8; + } + printk("%s: passphase = %s\n", __FUNCTION__, key_str_buf); + + psk.key_len = htod16((ushort)WSEC_MAX_PSK_LEN); + memcpy(psk.key, key_str_buf, psk.key_len); + } else { + psk.key_len = htod16((ushort) key_len); + memcpy(psk.key, ap->key, key_len); + } + + psk.flags = htod16(WSEC_PASSPHRASE); + res |= dev_wlc_ioctl(dev, WLC_SET_WSEC_PMK, &psk, sizeof(psk)); + + wpa_auth = WPA_AUTH_PSK; + res |= dev_wlc_intvar_set(dev, "wpa_auth", wpa_auth); + + WL_SOFTAP((" wsec & auth set 'wpa-psk' (TKIP), result:&d %d\n", res)); + } + +#ifdef AP_ONLY + ap_ssid.SSID_len = strlen(ap->ssid); + strncpy(ap_ssid.SSID, ap->ssid, ap_ssid.SSID_len); + res |= dev_wlc_ioctl(dev, WLC_SET_SSID, &ap_ssid, sizeof(ap_ssid)); + mpc = 0; + res |= dev_wlc_intvar_set(dev, "mpc", mpc); + if (strnicmp(ap->sec, "wep", strlen("wep")) == 0) { + res |= dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)); + } +#endif + return res; +} + + + +static int +get_parameter_from_string( + char **str_ptr, const char *token, + int param_type, void *dst, int param_max_len) +{ + char int_str[7] = "0"; + int parm_str_len; + char *param_str_begin; + char *param_str_end; + char *orig_str = *str_ptr; + + if ((*str_ptr) && !strncmp(*str_ptr, token, strlen(token))) { + + strsep(str_ptr, "=,"); + param_str_begin = *str_ptr; + strsep(str_ptr, "=,"); + + if (*str_ptr == NULL) { + + parm_str_len = strlen(param_str_begin); + } else { + param_str_end = *str_ptr-1; + parm_str_len = param_str_end - param_str_begin; + } + + WL_TRACE((" 'token:%s', len:%d, ", token, parm_str_len)); + + if (parm_str_len > param_max_len) { + WL_ERROR((" WARNING: extracted param len:%d is > MAX:%d\n", + parm_str_len, param_max_len)); + + parm_str_len = param_max_len; + } + + switch (param_type) { + + case PTYPE_INTDEC: { + + int *pdst_int = dst; + char *eptr; + + if (parm_str_len > sizeof(int_str)) + parm_str_len = sizeof(int_str); + + memcpy(int_str, param_str_begin, parm_str_len); + + *pdst_int = simple_strtoul(int_str, &eptr, 10); + + WL_TRACE((" written as integer:%d\n", *pdst_int)); + } + break; + case PTYPE_STR_HEX: { + u8 *buf = dst; + + param_max_len = param_max_len >> 1; + hstr_2_buf(param_str_begin, buf, param_max_len); + dhd_print_buf(buf, param_max_len, 0); + } + break; + default: + + memcpy(dst, param_str_begin, parm_str_len); + *((char *)dst + parm_str_len) = 0; + WL_ERROR((" written as a string:%s\n", (char *)dst)); + break; + + } + + return 0; + } else { + WL_ERROR(("\n %s: ERROR: can't find token:%s in str:%s \n", + __FUNCTION__, token, orig_str)); + + return -1; + } +} + +static int wl_iw_softap_deassoc_stations(struct net_device *dev, u8 *mac) +{ + int i; + int res = 0; + char mac_buf[128] = {0}; + char z_mac[6] = {0, 0, 0, 0, 0, 0}; + char *sta_mac; + struct maclist *assoc_maclist = (struct maclist *) mac_buf; + bool deauth_all = FALSE; + + + if (mac == NULL) { + deauth_all = TRUE; + sta_mac = z_mac; + } else { + sta_mac = mac; + } + + memset(assoc_maclist, 0, sizeof(mac_buf)); + assoc_maclist->count = 8; + + res = dev_wlc_ioctl(dev, WLC_GET_ASSOCLIST, assoc_maclist, 128); + if (res != 0) { + WL_SOFTAP(("%s: Error:%d Couldn't get ASSOC List\n", __FUNCTION__, res)); + return res; + } + + if (assoc_maclist->count) + for (i = 0; i < assoc_maclist->count; i++) { + scb_val_t scbval; + scbval.val = htod32(1); + + bcopy(&assoc_maclist->ea[i], &scbval.ea, ETHER_ADDR_LEN); + + if (deauth_all || (memcmp(&scbval.ea, sta_mac, ETHER_ADDR_LEN) == 0)) { + + WL_SOFTAP(("%s, deauth STA:%d \n", __FUNCTION__, i)); + res |= dev_wlc_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, + &scbval, sizeof(scb_val_t)); + } + } else WL_SOFTAP(("%s: No Stations \n", __FUNCTION__)); + + if (res != 0) { + WL_ERROR(("%s: Error:%d\n", __FUNCTION__, res)); + } else if (assoc_maclist->count) { + + bcm_mdelay(200); + } + return res; +} + + + +static int +iwpriv_softap_stop(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *ext) +{ + int res = 0; + + WL_SOFTAP(("got iwpriv AP_BSS_STOP \n")); + + if ((!dev) && (!ap_net_dev)) { + WL_ERROR(("%s: dev is null\n", __FUNCTION__)); + return res; + } + + net_os_wake_lock(dev); + DHD_OS_MUTEX_LOCK(&wl_softap_lock); + + if ((ap_cfg_running == TRUE)) { +#ifdef AP_ONLY + wl_iw_softap_deassoc_stations(dev, NULL); +#else + wl_iw_softap_deassoc_stations(ap_net_dev, NULL); + if ((res = dev_iw_write_cfg1_bss_var(dev, 2)) < 0) + WL_ERROR(("%s failed to del BSS err = %d", __FUNCTION__, res)); +#endif + + + bcm_mdelay(100); + + wrqu->data.length = 0; + ap_cfg_running = FALSE; + } else + WL_ERROR(("%s: was called when SoftAP is OFF : move on\n", __FUNCTION__)); + + WL_SOFTAP(("%s Done with %d\n", __FUNCTION__, res)); + DHD_OS_MUTEX_UNLOCK(&wl_softap_lock); + net_os_wake_unlock(dev); + + return res; +} + + + +static int +iwpriv_fw_reload(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *ext) +{ + int ret = -1; + char extra[256]; + char *fwstr = fw_path ; + + WL_SOFTAP(("current firmware_path[]=%s\n", fwstr)); + + WL_TRACE((">Got FW_RELOAD cmd:" + "info->cmd:%x, info->flags:%x, u.data:%p, u.len:%d, " + "fw_path:%p, len:%d \n", + info->cmd, info->flags, + wrqu->data.pointer, wrqu->data.length, fwstr, strlen(fwstr))); + + if ((wrqu->data.length > 4) && (wrqu->data.length < sizeof(extra))) { + char *str_ptr; + + if (copy_from_user(extra, wrqu->data.pointer, wrqu->data.length)) { + ret = -EFAULT; + goto exit_proc; + } + + + extra[wrqu->data.length] = 8; + str_ptr = extra; + + if (get_parameter_from_string(&str_ptr, + "FW_PATH=", PTYPE_STRING, fwstr, 255) != 0) { + WL_ERROR(("Error: extracting FW_PATH='' string\n")); + goto exit_proc; + } + + if (strstr(fwstr, "apsta") != NULL) { + WL_SOFTAP(("GOT APSTA FIRMWARE\n")); + ap_fw_loaded = TRUE; + } else { + WL_SOFTAP(("GOT STA FIRMWARE\n")); + ap_fw_loaded = FALSE; + } + + WL_SOFTAP(("SET firmware_path[]=%s , str_p:%p\n", fwstr, fwstr)); + ret = 0; + } else { + WL_ERROR(("Error: ivalid param len:%d\n", wrqu->data.length)); + } + +exit_proc: + return ret; +} + +#ifdef SOFTAP + +static int +iwpriv_wpasupp_loop_tst(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *ext) +{ + int res = 0; + char *params = NULL; + + WL_TRACE((">Got IWPRIV wp_supp loopback cmd test:" + "info->cmd:%x, info->flags:%x, u.data:%p, u.len:%d\n", + info->cmd, info->flags, + wrqu->data.pointer, wrqu->data.length)); + + if (wrqu->data.length != 0) { + + if (!(params = kmalloc(wrqu->data.length+1, GFP_KERNEL))) + return -ENOMEM; + + + if (copy_from_user(params, wrqu->data.pointer, wrqu->data.length)) { + kfree(params); + return -EFAULT; + } + + params[wrqu->data.length] = 0; + WL_SOFTAP(("\n>> copied from user:\n %s\n", params)); + } else { + WL_ERROR(("ERROR param length is 0\n")); + return -EFAULT; + } + + + res = wl_iw_send_priv_event(dev, params); + kfree(params); + + return res; +} +#endif + + +static int +iwpriv_en_ap_bss( + struct net_device *dev, + struct iw_request_info *info, + void *wrqu, + char *extra) +{ + int res = 0; + + if (!dev) { + WL_ERROR(("%s: dev is null\n", __FUNCTION__)); + return -1; + } + + net_os_wake_lock(dev); + DHD_OS_MUTEX_LOCK(&wl_softap_lock); + + WL_TRACE(("%s: rcvd IWPRIV IOCTL: for dev:%s\n", __FUNCTION__, dev->name)); + + +#ifndef AP_ONLY + if ((res = wl_iw_set_ap_security(dev, &my_ap)) != 0) { + WL_ERROR((" %s ERROR setting SOFTAP security in :%d\n", __FUNCTION__, res)); + } + else { + + if ((res = dev_iw_write_cfg1_bss_var(dev, 1)) < 0) + WL_ERROR(("%s fail to set bss up err=%d\n", __FUNCTION__, res)); + else + + bcm_mdelay(100); + } + +#endif + WL_SOFTAP(("%s done with res %d \n", __FUNCTION__, res)); + + DHD_OS_MUTEX_UNLOCK(&wl_softap_lock); + net_os_wake_unlock(dev); + + return res; +} + +static int +get_assoc_sta_list(struct net_device *dev, char *buf, int len) +{ + + WL_TRACE(("%s: dev_wlc_ioctl(dev:%p, cmd:%d, buf:%p, len:%d)\n", + __FUNCTION__, dev, WLC_GET_ASSOCLIST, buf, len)); + + return dev_wlc_ioctl(dev, WLC_GET_ASSOCLIST, buf, len); + +} + + +void check_error(int res, const char *msg, const char *func, int line) +{ + if (res != 0) + WL_ERROR(("%s, %d function:%s, line:%d\n", msg, res, func, line)); +} + +static int +set_ap_mac_list(struct net_device *dev, void *buf) +{ + struct mac_list_set *mac_list_set = (struct mac_list_set *)buf; + struct maclist *maclist = (struct maclist *)&mac_list_set->mac_list; + int length; + int i; + int mac_mode = mac_list_set->mode; + int ioc_res = 0; + ap_macmode = mac_list_set->mode; + + + bzero(&ap_black_list, sizeof(struct mflist)); + + if (mac_mode == MACLIST_MODE_DISABLED) { + + ioc_res = dev_wlc_ioctl(dev, WLC_SET_MACMODE, &mac_mode, sizeof(mac_mode)); + check_error(ioc_res, "ioctl ERROR:", __FUNCTION__, __LINE__); + WL_SOFTAP(("%s: MAC filtering disabled\n", __FUNCTION__)); + } else { + + scb_val_t scbval; + char mac_buf[256] = {0}; + struct maclist *assoc_maclist = (struct maclist *) mac_buf; + + + bcopy(maclist, &ap_black_list, sizeof(ap_black_list)); + + + ioc_res = dev_wlc_ioctl(dev, WLC_SET_MACMODE, &mac_mode, sizeof(mac_mode)); + check_error(ioc_res, "ioctl ERROR:", __FUNCTION__, __LINE__); + + + length = sizeof(maclist->count) + maclist->count*ETHER_ADDR_LEN; + dev_wlc_ioctl(dev, WLC_SET_MACLIST, maclist, length); + + WL_SOFTAP(("%s: applied MAC List, mode:%d, length %d:\n", + __FUNCTION__, mac_mode, length)); + + for (i = 0; i < maclist->count; i++) + WL_SOFTAP(("mac %d: %02X:%02X:%02X:%02X:%02X:%02X\n", + i, maclist->ea[i].octet[0], maclist->ea[i].octet[1], + maclist->ea[i].octet[2], + maclist->ea[i].octet[3], maclist->ea[i].octet[4], + maclist->ea[i].octet[5])); + + + assoc_maclist->count = 8; + ioc_res = dev_wlc_ioctl(dev, WLC_GET_ASSOCLIST, assoc_maclist, 256); + check_error(ioc_res, "ioctl ERROR:", __FUNCTION__, __LINE__); + WL_SOFTAP((" Cur assoc clients:%d\n", assoc_maclist->count)); + + + if (assoc_maclist->count) + for (i = 0; i < assoc_maclist->count; i++) { + int j; + bool assoc_mac_matched = FALSE; + + WL_SOFTAP(("\n Cheking assoc STA: ")); + dhd_print_buf(&assoc_maclist->ea[i], 6, 7); + WL_SOFTAP(("with the b/w list:")); + + for (j = 0; j < maclist->count; j++) + if (!bcmp(&assoc_maclist->ea[i], &maclist->ea[j], + ETHER_ADDR_LEN)) { + + assoc_mac_matched = TRUE; + break; + } + + + if (((mac_mode == MACLIST_MODE_ALLOW) && !assoc_mac_matched) || + ((mac_mode == MACLIST_MODE_DENY) && assoc_mac_matched)) { + + WL_SOFTAP(("b-match or w-mismatch," + " do deauth/disassoc \n")); + scbval.val = htod32(1); + bcopy(&assoc_maclist->ea[i], &scbval.ea, + ETHER_ADDR_LEN); + ioc_res = dev_wlc_ioctl(dev, + WLC_SCB_DEAUTHENTICATE_FOR_REASON, + &scbval, sizeof(scb_val_t)); + check_error(ioc_res, + "ioctl ERROR:", + __FUNCTION__, __LINE__); + + } else { + WL_SOFTAP((" no b/w list hits, let it be\n")); + } + } else { + WL_SOFTAP(("No ASSOC CLIENTS\n")); + } + + } + + WL_SOFTAP(("%s iocres:%d\n", __FUNCTION__, ioc_res)); + return ioc_res; +} +#endif + + + +#ifdef SOFTAP +#define PARAM_OFFSET PROFILE_OFFSET + +static int +wl_iw_process_private_ascii_cmd( + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *dwrq, + char *cmd_str) +{ + int ret = 0; + char *sub_cmd = cmd_str + PROFILE_OFFSET + strlen("ASCII_CMD="); + + WL_SOFTAP(("\n %s: ASCII_CMD: offs_0:%s, offset_32:\n'%s'\n", + __FUNCTION__, cmd_str, cmd_str + PROFILE_OFFSET)); + + if (strnicmp(sub_cmd, "AP_CFG", strlen("AP_CFG")) == 0) { + + WL_SOFTAP((" AP_CFG \n")); + + + if (init_ap_profile_from_string(cmd_str+PROFILE_OFFSET, &my_ap) != 0) { + WL_ERROR(("ERROR: SoftAP CFG prams !\n")); + ret = -1; + } else { + ret = set_ap_cfg(dev, &my_ap); + } + + } else if (strnicmp(sub_cmd, "AP_BSS_START", strlen("AP_BSS_START")) == 0) { + + WL_SOFTAP(("\n SOFTAP - ENABLE BSS \n")); + + + WL_SOFTAP(("\n!!! got 'WL_AP_EN_BSS' from WPA supplicant, dev:%s\n", dev->name)); + +#ifndef AP_ONLY + if (ap_net_dev == NULL) { + printf("\n ERROR: SOFTAP net_dev* is NULL !!!\n"); + } else { + + if ((ret = iwpriv_en_ap_bss(ap_net_dev, info, dwrq, cmd_str)) < 0) + WL_ERROR(("%s line %d fail to set bss up\n", + __FUNCTION__, __LINE__)); + } +#else + if ((ret = iwpriv_en_ap_bss(dev, info, dwrq, cmd_str)) < 0) + WL_ERROR(("%s line %d fail to set bss up\n", + __FUNCTION__, __LINE__)); +#endif + } else if (strnicmp(sub_cmd, "ASSOC_LST", strlen("ASSOC_LST")) == 0) { + + + + } else if (strnicmp(sub_cmd, "AP_BSS_STOP", strlen("AP_BSS_STOP")) == 0) { + + WL_SOFTAP((" \n temp DOWN SOFTAP\n")); +#ifndef AP_ONLY + if ((ret = dev_iw_write_cfg1_bss_var(dev, 0)) < 0) { + WL_ERROR(("%s line %d fail to set bss down\n", + __FUNCTION__, __LINE__)); + } +#endif + } + + return ret; + +} +#endif + + +static int +wl_iw_set_priv( + struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *ext +) +{ + int ret = 0; + char * extra; + + if (!(extra = kmalloc(dwrq->length, GFP_KERNEL))) + return -ENOMEM; + + if (copy_from_user(extra, dwrq->pointer, dwrq->length)) { + kfree(extra); + return -EFAULT; + } + + WL_TRACE(("%s: SIOCSIWPRIV request %s, info->cmd:%x, info->flags:%d\n dwrq->length:%d\n", + dev->name, extra, info->cmd, info->flags, dwrq->length)); + + + + net_os_wake_lock(dev); + + if (dwrq->length && extra) { + if (strnicmp(extra, "START", strlen("START")) == 0) { + wl_iw_control_wl_on(dev, info); + WL_TRACE(("%s, Received regular START command\n", __FUNCTION__)); + } + + if (g_onoff == G_WLAN_SET_OFF) { + WL_TRACE(("%s, missing START, Fail\n", __FUNCTION__)); + kfree(extra); + net_os_wake_unlock(dev); + return -EFAULT; + } + + if (strnicmp(extra, "SCAN-ACTIVE", strlen("SCAN-ACTIVE")) == 0) { +#ifdef ENABLE_ACTIVE_PASSIVE_SCAN_SUPPRESS + WL_TRACE(("%s: active scan setting suppressed\n", dev->name)); +#else + ret = wl_iw_set_active_scan(dev, info, (union iwreq_data *)dwrq, extra); +#endif + } + else if (strnicmp(extra, "SCAN-PASSIVE", strlen("SCAN-PASSIVE")) == 0) +#ifdef ENABLE_ACTIVE_PASSIVE_SCAN_SUPPRESS + WL_TRACE(("%s: passive scan setting suppressed\n", dev->name)); +#else + ret = wl_iw_set_passive_scan(dev, info, (union iwreq_data *)dwrq, extra); +#endif + else if (strnicmp(extra, "RSSI", strlen("RSSI")) == 0) + ret = wl_iw_get_rssi(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, "LINKSPEED", strlen("LINKSPEED")) == 0) + ret = wl_iw_get_link_speed(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, "MACADDR", strlen("MACADDR")) == 0) + ret = wl_iw_get_macaddr(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, "COUNTRY", strlen("COUNTRY")) == 0) + ret = wl_iw_set_country(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, "STOP", strlen("STOP")) == 0) + ret = wl_iw_control_wl_off(dev, info); + else if (strnicmp(extra, BAND_GET_CMD, strlen(BAND_GET_CMD)) == 0) + ret = wl_iw_get_band(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, BAND_SET_CMD, strlen(BAND_SET_CMD)) == 0) + ret = wl_iw_set_band(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, DTIM_SKIP_GET_CMD, strlen(DTIM_SKIP_GET_CMD)) == 0) + ret = wl_iw_get_dtim_skip(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, DTIM_SKIP_SET_CMD, strlen(DTIM_SKIP_SET_CMD)) == 0) + ret = wl_iw_set_dtim_skip(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, SETSUSPENDOPT_CMD, strlen(SETSUSPENDOPT_CMD)) == 0) + ret = wl_iw_set_suspend_opt(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, SETSUSPENDMODE_CMD, strlen(SETSUSPENDMODE_CMD)) == 0) + ret = wl_iw_set_suspend_mode(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, TXPOWER_SET_CMD, strlen(TXPOWER_SET_CMD)) == 0) + ret = wl_iw_set_txpower(dev, info, (union iwreq_data *)dwrq, extra); +#if defined(PNO_SUPPORT) + else if (strnicmp(extra, PNOSSIDCLR_SET_CMD, strlen(PNOSSIDCLR_SET_CMD)) == 0) + ret = wl_iw_set_pno_reset(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, PNOSETUP_SET_CMD, strlen(PNOSETUP_SET_CMD)) == 0) + ret = wl_iw_set_pno_set(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, PNOSETADD_SET_CMD, strlen(PNOSETADD_SET_CMD)) == 0) + ret = wl_iw_set_pno_setadd(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, PNOENABLE_SET_CMD, strlen(PNOENABLE_SET_CMD)) == 0) + ret = wl_iw_set_pno_enable(dev, info, (union iwreq_data *)dwrq, extra); +#endif +#if defined(CSCAN) + + else if (strnicmp(extra, CSCAN_COMMAND, strlen(CSCAN_COMMAND)) == 0) + ret = wl_iw_set_cscan(dev, info, (union iwreq_data *)dwrq, extra); +#endif +#ifdef CONFIG_WPS2 + else if (strnicmp(extra, WPS_ADD_PROBE_REQ_IE_CMD, + strlen(WPS_ADD_PROBE_REQ_IE_CMD)) == 0) + ret = wl_iw_add_wps_probe_req_ie(dev, info, + (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, WPS_DEL_PROBE_REQ_IE_CMD, + strlen(WPS_DEL_PROBE_REQ_IE_CMD)) == 0) + ret = wl_iw_del_wps_probe_req_ie(dev, info, + (union iwreq_data *)dwrq, extra); +#endif + else if (strnicmp(extra, "POWERMODE", strlen("POWERMODE")) == 0) + ret = wl_iw_set_power_mode(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, "BTCOEXMODE", strlen("BTCOEXMODE")) == 0) + ret = wl_iw_set_btcoex_dhcp(dev, info, (union iwreq_data *)dwrq, extra); + else if (strnicmp(extra, "GETPOWER", strlen("GETPOWER")) == 0) + ret = wl_iw_get_power_mode(dev, info, (union iwreq_data *)dwrq, extra); +#ifdef SOFTAP + else if (strnicmp(extra, "ASCII_CMD", strlen("ASCII_CMD")) == 0) { + wl_iw_process_private_ascii_cmd(dev, info, (union iwreq_data *)dwrq, extra); + } + else if (strnicmp(extra, "AP_MAC_LIST_SET", strlen("AP_MAC_LIST_SET")) == 0) { + WL_SOFTAP(("penguin, set AP_MAC_LIST_SET\n")); + set_ap_mac_list(dev, (extra + PROFILE_OFFSET)); + } +#endif + else { + WL_ERROR(("Unknown PRIVATE command %s - ignored\n", extra)); + snprintf(extra, MAX_WX_STRING, "OK"); + dwrq->length = strlen("OK") + 1; + } + } + + net_os_wake_unlock(dev); + + if (extra) { + if (copy_to_user(dwrq->pointer, extra, dwrq->length)) { + kfree(extra); + return -EFAULT; + } + + kfree(extra); + } + + return ret; +} + +static const iw_handler wl_iw_handler[] = +{ + (iw_handler) wl_iw_config_commit, + (iw_handler) wl_iw_get_name, + (iw_handler) NULL, + (iw_handler) NULL, + (iw_handler) wl_iw_set_freq, + (iw_handler) wl_iw_get_freq, + (iw_handler) wl_iw_set_mode, + (iw_handler) wl_iw_get_mode, + (iw_handler) NULL, + (iw_handler) NULL, + (iw_handler) NULL, + (iw_handler) wl_iw_get_range, + (iw_handler) wl_iw_set_priv, + (iw_handler) NULL, + (iw_handler) NULL, + (iw_handler) NULL, + (iw_handler) wl_iw_set_spy, + (iw_handler) wl_iw_get_spy, + (iw_handler) NULL, + (iw_handler) NULL, + (iw_handler) wl_iw_set_wap, + (iw_handler) wl_iw_get_wap, +#if WIRELESS_EXT > 17 + (iw_handler) wl_iw_mlme, +#else + (iw_handler) NULL, +#endif +#if defined(WL_IW_USE_ISCAN) + (iw_handler) wl_iw_iscan_get_aplist, +#else + (iw_handler) wl_iw_get_aplist, +#endif +#if WIRELESS_EXT > 13 +#if defined(WL_IW_USE_ISCAN) + (iw_handler) wl_iw_iscan_set_scan, + (iw_handler) wl_iw_iscan_get_scan, +#else + (iw_handler) wl_iw_set_scan, + (iw_handler) wl_iw_get_scan, +#endif +#else + (iw_handler) NULL, + (iw_handler) NULL, +#endif + (iw_handler) wl_iw_set_essid, + (iw_handler) wl_iw_get_essid, + (iw_handler) wl_iw_set_nick, + (iw_handler) wl_iw_get_nick, + (iw_handler) NULL, + (iw_handler) NULL, + (iw_handler) wl_iw_set_rate, + (iw_handler) wl_iw_get_rate, + (iw_handler) wl_iw_set_rts, + (iw_handler) wl_iw_get_rts, + (iw_handler) wl_iw_set_frag, + (iw_handler) wl_iw_get_frag, + (iw_handler) wl_iw_set_txpow, + (iw_handler) wl_iw_get_txpow, +#if WIRELESS_EXT > 10 + (iw_handler) wl_iw_set_retry, + (iw_handler) wl_iw_get_retry, +#endif + (iw_handler) wl_iw_set_encode, + (iw_handler) wl_iw_get_encode, + (iw_handler) wl_iw_set_power, + (iw_handler) wl_iw_get_power, +#if WIRELESS_EXT > 17 + (iw_handler) NULL, + (iw_handler) NULL, + (iw_handler) wl_iw_set_wpaie, + (iw_handler) wl_iw_get_wpaie, + (iw_handler) wl_iw_set_wpaauth, + (iw_handler) wl_iw_get_wpaauth, + (iw_handler) wl_iw_set_encodeext, + (iw_handler) wl_iw_get_encodeext, + (iw_handler) wl_iw_set_pmksa, +#endif +}; + +#if WIRELESS_EXT > 12 +static const iw_handler wl_iw_priv_handler[] = { + NULL, + (iw_handler)wl_iw_set_active_scan, + NULL, + (iw_handler)wl_iw_get_rssi, + NULL, + (iw_handler)wl_iw_set_passive_scan, + NULL, + (iw_handler)wl_iw_get_link_speed, + NULL, + (iw_handler)wl_iw_get_macaddr, + NULL, + (iw_handler)wl_iw_control_wl_off, + NULL, + (iw_handler)wl_iw_control_wl_on, +#ifdef SOFTAP + + + NULL, + (iw_handler)iwpriv_set_ap_config, + + + + NULL, + (iw_handler)iwpriv_get_assoc_list, + + + NULL, + (iw_handler)iwpriv_set_mac_filters, + + + NULL, + (iw_handler)iwpriv_en_ap_bss, + + + NULL, + (iw_handler)iwpriv_wpasupp_loop_tst, + + NULL, + (iw_handler)iwpriv_softap_stop, + + NULL, + (iw_handler)iwpriv_fw_reload, + NULL, + (iw_handler)iwpriv_set_ap_sta_disassoc, +#endif +#if defined(CSCAN) + + NULL, + (iw_handler)iwpriv_set_cscan +#endif +}; + +static const struct iw_priv_args wl_iw_priv_args[] = +{ + { + WL_IW_SET_ACTIVE_SCAN, + 0, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, + "SCAN-ACTIVE" + }, + { + WL_IW_GET_RSSI, + 0, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, + "RSSI" + }, + { + WL_IW_SET_PASSIVE_SCAN, + 0, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, + "SCAN-PASSIVE" + }, + { + WL_IW_GET_LINK_SPEED, + 0, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, + "LINKSPEED" + }, + { + WL_IW_GET_CURR_MACADDR, + 0, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, + "Macaddr" + }, + { + WL_IW_SET_STOP, + 0, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, + "STOP" + }, + { + WL_IW_SET_START, + 0, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, + "START" + }, + +#ifdef SOFTAP + + + { + WL_SET_AP_CFG, + IW_PRIV_TYPE_CHAR | 256, + 0, + "AP_SET_CFG" + }, + + { + WL_AP_STA_LIST, + IW_PRIV_TYPE_CHAR | 0, + IW_PRIV_TYPE_CHAR | 1024, + "AP_GET_STA_LIST" + }, + + { + WL_AP_MAC_FLTR, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 0, + "AP_SET_MAC_FLTR" + }, + + { + WL_AP_BSS_START, + 0, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, + "AP_BSS_START" + }, + + { + AP_LPB_CMD, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 0, + "AP_LPB_CMD" + }, + + { + WL_AP_STOP, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 0, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 0, + "AP_BSS_STOP" + }, + { + WL_FW_RELOAD, + IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 0, + "WL_FW_RELOAD" + }, +#endif +#if defined(CSCAN) + { + WL_COMBO_SCAN, + IW_PRIV_TYPE_CHAR | 1024, + 0, + "CSCAN" + }, +#endif + }; + +const struct iw_handler_def wl_iw_handler_def = +{ + .num_standard = ARRAYSIZE(wl_iw_handler), + .standard = (iw_handler *) wl_iw_handler, + .num_private = ARRAYSIZE(wl_iw_priv_handler), + .num_private_args = ARRAY_SIZE(wl_iw_priv_args), + .private = (iw_handler *)wl_iw_priv_handler, + .private_args = (void *) wl_iw_priv_args, + +#if WIRELESS_EXT >= 19 + get_wireless_stats: dhd_get_wireless_stats, +#endif + }; +#endif + + + +int +wl_iw_ioctl( + struct net_device *dev, + struct ifreq *rq, + int cmd +) +{ + struct iwreq *wrq = (struct iwreq *) rq; + struct iw_request_info info; + iw_handler handler; + char *extra = NULL; + size_t token_size = 1; + int max_tokens = 0, ret = 0; + + net_os_wake_lock(dev); + + WL_TRACE(("\n%s, cmd:%x called via dhd->do_ioctl()entry point\n", __FUNCTION__, cmd)); + if (cmd < SIOCIWFIRST || + IW_IOCTL_IDX(cmd) >= ARRAYSIZE(wl_iw_handler) || + !(handler = wl_iw_handler[IW_IOCTL_IDX(cmd)])) { + WL_ERROR(("%s: error in cmd=%x : not supported\n", __FUNCTION__, cmd)); + net_os_wake_unlock(dev); + return -EOPNOTSUPP; + } + + switch (cmd) { + + case SIOCSIWESSID: + case SIOCGIWESSID: + case SIOCSIWNICKN: + case SIOCGIWNICKN: + max_tokens = IW_ESSID_MAX_SIZE + 1; + break; + + case SIOCSIWENCODE: + case SIOCGIWENCODE: +#if WIRELESS_EXT > 17 + case SIOCSIWENCODEEXT: + case SIOCGIWENCODEEXT: +#endif + max_tokens = wrq->u.data.length; + break; + + case SIOCGIWRANGE: + + max_tokens = sizeof(struct iw_range) + 500; + break; + + case SIOCGIWAPLIST: + token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality); + max_tokens = IW_MAX_AP; + break; + +#if WIRELESS_EXT > 13 + case SIOCGIWSCAN: +#if defined(WL_IW_USE_ISCAN) + if (g_iscan) + max_tokens = wrq->u.data.length; + else +#endif + max_tokens = IW_SCAN_MAX_DATA; + break; +#endif + + case SIOCSIWSPY: + token_size = sizeof(struct sockaddr); + max_tokens = IW_MAX_SPY; + break; + + case SIOCGIWSPY: + token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality); + max_tokens = IW_MAX_SPY; + break; + +#if WIRELESS_EXT > 17 + case SIOCSIWPMKSA: + case SIOCSIWGENIE: +#endif + case SIOCSIWPRIV: + max_tokens = wrq->u.data.length; + break; + } + + if (max_tokens && wrq->u.data.pointer) { + if (wrq->u.data.length > max_tokens) { + WL_ERROR(("%s: error in cmd=%x wrq->u.data.length=%d > max_tokens=%d\n", + __FUNCTION__, cmd, wrq->u.data.length, max_tokens)); + ret = -E2BIG; + goto wl_iw_ioctl_done; + } + if (!(extra = kmalloc(max_tokens * token_size, GFP_KERNEL))) { + ret = -ENOMEM; + goto wl_iw_ioctl_done; + } + + if (copy_from_user(extra, wrq->u.data.pointer, wrq->u.data.length * token_size)) { + kfree(extra); + ret = -EFAULT; + goto wl_iw_ioctl_done; + } + } + + info.cmd = cmd; + info.flags = 0; + + ret = handler(dev, &info, &wrq->u, extra); + + if (extra) { + if (copy_to_user(wrq->u.data.pointer, extra, wrq->u.data.length * token_size)) { + kfree(extra); + ret = -EFAULT; + goto wl_iw_ioctl_done; + } + + kfree(extra); + } + +wl_iw_ioctl_done: + + net_os_wake_unlock(dev); + + return ret; +} + + +static bool +wl_iw_conn_status_str(uint32 event_type, uint32 status, uint32 reason, + char* stringBuf, uint buflen) +{ + typedef struct conn_fail_event_map_t { + uint32 inEvent; + uint32 inStatus; + uint32 inReason; + const char* outName; + const char* outCause; + } conn_fail_event_map_t; + + +#define WL_IW_DONT_CARE 9999 + const conn_fail_event_map_t event_map [] = { + + + {WLC_E_SET_SSID, WLC_E_STATUS_SUCCESS, WL_IW_DONT_CARE, + "Conn", "Success"}, + {WLC_E_SET_SSID, WLC_E_STATUS_NO_NETWORKS, WL_IW_DONT_CARE, + "Conn", "NoNetworks"}, + {WLC_E_SET_SSID, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE, + "Conn", "ConfigMismatch"}, + {WLC_E_PRUNE, WL_IW_DONT_CARE, WLC_E_PRUNE_ENCR_MISMATCH, + "Conn", "EncrypMismatch"}, + {WLC_E_PRUNE, WL_IW_DONT_CARE, WLC_E_RSN_MISMATCH, + "Conn", "RsnMismatch"}, + {WLC_E_AUTH, WLC_E_STATUS_TIMEOUT, WL_IW_DONT_CARE, + "Conn", "AuthTimeout"}, + {WLC_E_AUTH, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE, + "Conn", "AuthFail"}, + {WLC_E_AUTH, WLC_E_STATUS_NO_ACK, WL_IW_DONT_CARE, + "Conn", "AuthNoAck"}, + {WLC_E_REASSOC, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE, + "Conn", "ReassocFail"}, + {WLC_E_REASSOC, WLC_E_STATUS_TIMEOUT, WL_IW_DONT_CARE, + "Conn", "ReassocTimeout"}, + {WLC_E_REASSOC, WLC_E_STATUS_ABORT, WL_IW_DONT_CARE, + "Conn", "ReassocAbort"}, + {WLC_E_PSK_SUP, WLC_SUP_KEYED, WL_IW_DONT_CARE, + "Sup", "ConnSuccess"}, + {WLC_E_PSK_SUP, WL_IW_DONT_CARE, WL_IW_DONT_CARE, + "Sup", "WpaHandshakeFail"}, + {WLC_E_DEAUTH_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE, + "Conn", "Deauth"}, + {WLC_E_DISASSOC_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE, + "Conn", "DisassocInd"}, + {WLC_E_DISASSOC, WL_IW_DONT_CARE, WL_IW_DONT_CARE, + "Conn", "Disassoc"} + }; + + const char* name = ""; + const char* cause = NULL; + int i; + + + for (i = 0; i < sizeof(event_map)/sizeof(event_map[0]); i++) { + const conn_fail_event_map_t* row = &event_map[i]; + if (row->inEvent == event_type && + (row->inStatus == status || row->inStatus == WL_IW_DONT_CARE) && + (row->inReason == reason || row->inReason == WL_IW_DONT_CARE)) { + name = row->outName; + cause = row->outCause; + break; + } + } + + + if (cause) { + memset(stringBuf, 0, buflen); + snprintf(stringBuf, buflen, "%s %s %02d %02d", + name, cause, status, reason); + WL_INFORM(("Connection status: %s\n", stringBuf)); + return TRUE; + } else { + return FALSE; + } +} + +#if WIRELESS_EXT > 14 + +static bool +wl_iw_check_conn_fail(wl_event_msg_t *e, char* stringBuf, uint buflen) +{ + uint32 event = ntoh32(e->event_type); + uint32 status = ntoh32(e->status); + uint32 reason = ntoh32(e->reason); + + if (wl_iw_conn_status_str(event, status, reason, stringBuf, buflen)) { + return TRUE; + } + else + return FALSE; +} +#endif + +#ifndef IW_CUSTOM_MAX +#define IW_CUSTOM_MAX 256 +#endif + +void +wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) +{ +#if WIRELESS_EXT > 13 + union iwreq_data wrqu; + char extra[IW_CUSTOM_MAX + 1]; + int cmd = 0; + uint32 event_type = ntoh32(e->event_type); + uint16 flags = ntoh16(e->flags); + uint32 datalen = ntoh32(e->datalen); + uint32 status = ntoh32(e->status); + uint32 toto; + memset(&wrqu, 0, sizeof(wrqu)); + memset(extra, 0, sizeof(extra)); + + if (!dev) { + WL_ERROR(("%s: dev is null\n", __FUNCTION__)); + return; + } + + net_os_wake_lock(dev); + + WL_TRACE(("%s: dev=%s event=%d \n", __FUNCTION__, dev->name, event_type)); + + + switch (event_type) { +#if defined(SOFTAP) + case WLC_E_PRUNE: + if (ap_cfg_running) { + char *macaddr = (char *)&e->addr; + WL_SOFTAP(("PRUNE received, %02X:%02X:%02X:%02X:%02X:%02X!\n", + macaddr[0], macaddr[1], macaddr[2], macaddr[3], + macaddr[4], macaddr[5])); + + + if (ap_macmode) + { + int i; + for (i = 0; i < ap_black_list.count; i++) { + if (!bcmp(macaddr, &ap_black_list.ea[i], + sizeof(struct ether_addr))) { + WL_SOFTAP(("mac in black list, ignore it\n")); + break; + } + } + + if (i == ap_black_list.count) { + + char mac_buf[32] = {0}; + sprintf(mac_buf, "STA_BLOCK %02X:%02X:%02X:%02X:%02X:%02X", + macaddr[0], macaddr[1], macaddr[2], + macaddr[3], macaddr[4], macaddr[5]); + wl_iw_send_priv_event(priv_dev, mac_buf); + } + } + } + break; +#endif + case WLC_E_TXFAIL: + cmd = IWEVTXDROP; + memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + break; +#if WIRELESS_EXT > 14 + case WLC_E_JOIN: + case WLC_E_ASSOC_IND: + case WLC_E_REASSOC_IND: +#if defined(SOFTAP) + WL_SOFTAP(("STA connect received %d\n", event_type)); + if (ap_cfg_running) { + wl_iw_send_priv_event(priv_dev, "STA_JOIN"); + goto wl_iw_event_end; + } +#endif + memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + cmd = IWEVREGISTERED; + break; + case WLC_E_ROAM: + if (status == WLC_E_STATUS_SUCCESS) { + WL_ASSOC((" WLC_E_ROAM : success \n")); + goto wl_iw_event_end; + } + break; + + case WLC_E_DEAUTH_IND: + case WLC_E_DISASSOC_IND: +#if defined(SOFTAP) + WL_SOFTAP(("STA disconnect received %d\n", event_type)); + if (ap_cfg_running) { + wl_iw_send_priv_event(priv_dev, "STA_LEAVE"); + goto wl_iw_event_end; + } +#endif + cmd = SIOCGIWAP; + bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + bzero(&extra, ETHER_ADDR_LEN); + break; + case WLC_E_LINK: + case WLC_E_NDIS_LINK: + cmd = SIOCGIWAP; + if (!(flags & WLC_EVENT_MSG_LINK)) { + + +#ifdef SOFTAP +#ifdef AP_ONLY + if (ap_cfg_running) { +#else + if (ap_cfg_running && !strncmp(dev->name, "wl0.1", 5)) { +#endif + + WL_SOFTAP(("AP DOWN %d\n", event_type)); + wl_iw_send_priv_event(priv_dev, "AP_DOWN"); + } else { + WL_TRACE(("STA_Link Down\n")); + g_ss_cache_ctrl.m_link_down = 1; + } +#else + g_ss_cache_ctrl.m_link_down = 1; +#endif + WL_TRACE(("Link Down\n")); + + bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN); + bzero(&extra, ETHER_ADDR_LEN); + } + else { + + memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN); + g_ss_cache_ctrl.m_link_down = 0; + + memcpy(g_ss_cache_ctrl.m_active_bssid, &e->addr, ETHER_ADDR_LEN); +#ifdef SOFTAP + +#ifdef AP_ONLY + if (ap_cfg_running) { +#else + if (ap_cfg_running && !strncmp(dev->name, "wl0.1", 5)) { +#endif + + WL_SOFTAP(("AP UP %d\n", event_type)); + wl_iw_send_priv_event(priv_dev, "AP_UP"); + } else { + WL_TRACE(("STA_LINK_UP\n")); + } +#else +#endif + WL_TRACE(("Link UP\n")); + + } + wrqu.addr.sa_family = ARPHRD_ETHER; + break; + case WLC_E_ACTION_FRAME: + cmd = IWEVCUSTOM; + if (datalen + 1 <= sizeof(extra)) { + wrqu.data.length = datalen + 1; + extra[0] = WLC_E_ACTION_FRAME; + memcpy(&extra[1], data, datalen); + WL_TRACE(("WLC_E_ACTION_FRAME len %d \n", wrqu.data.length)); + } + break; + + case WLC_E_ACTION_FRAME_COMPLETE: + cmd = IWEVCUSTOM; + memcpy(&toto, data, 4); + if (sizeof(status) + 1 <= sizeof(extra)) { + wrqu.data.length = sizeof(status) + 1; + extra[0] = WLC_E_ACTION_FRAME_COMPLETE; + memcpy(&extra[1], &status, sizeof(status)); + printf("wl_iw_event status %d PacketId %d \n", status, toto); + printf("WLC_E_ACTION_FRAME_COMPLETE len %d \n", wrqu.data.length); + } + break; +#endif +#if WIRELESS_EXT > 17 + case WLC_E_MIC_ERROR: { + struct iw_michaelmicfailure *micerrevt = (struct iw_michaelmicfailure *)&extra; + cmd = IWEVMICHAELMICFAILURE; + wrqu.data.length = sizeof(struct iw_michaelmicfailure); + if (flags & WLC_EVENT_MSG_GROUP) + micerrevt->flags |= IW_MICFAILURE_GROUP; + else + micerrevt->flags |= IW_MICFAILURE_PAIRWISE; + memcpy(micerrevt->src_addr.sa_data, &e->addr, ETHER_ADDR_LEN); + micerrevt->src_addr.sa_family = ARPHRD_ETHER; + + break; + } + + case WLC_E_ASSOC_REQ_IE: + cmd = IWEVASSOCREQIE; + wrqu.data.length = datalen; + if (datalen < sizeof(extra)) + memcpy(extra, data, datalen); + break; + + case WLC_E_ASSOC_RESP_IE: + cmd = IWEVASSOCRESPIE; + wrqu.data.length = datalen; + if (datalen < sizeof(extra)) + memcpy(extra, data, datalen); + break; + + case WLC_E_PMKID_CACHE: { + if (data) + { + struct iw_pmkid_cand *iwpmkidcand = (struct iw_pmkid_cand *)&extra; + pmkid_cand_list_t *pmkcandlist; + pmkid_cand_t *pmkidcand; + int count; + + cmd = IWEVPMKIDCAND; + pmkcandlist = data; + count = ntoh32_ua((uint8 *)&pmkcandlist->npmkid_cand); + ASSERT(count >= 0); + wrqu.data.length = sizeof(struct iw_pmkid_cand); + pmkidcand = pmkcandlist->pmkid_cand; + while (count) { + bzero(iwpmkidcand, sizeof(struct iw_pmkid_cand)); + if (pmkidcand->preauth) + iwpmkidcand->flags |= IW_PMKID_CAND_PREAUTH; + bcopy(&pmkidcand->BSSID, &iwpmkidcand->bssid.sa_data, + ETHER_ADDR_LEN); + wireless_send_event(dev, cmd, &wrqu, extra); + pmkidcand++; + count--; + } + } + goto wl_iw_event_end; + } +#endif + + case WLC_E_SCAN_COMPLETE: +#if defined(WL_IW_USE_ISCAN) + if (!g_iscan) { + WL_ERROR(("Event WLC_E_SCAN_COMPLETE on g_iscan NULL!")); + goto wl_iw_event_end; + } + + if ((g_iscan) && (g_iscan->tsk_ctl.thr_pid >= 0) && + (g_iscan->iscan_state != ISCAN_STATE_IDLE)) + { + up(&g_iscan->tsk_ctl.sema); + } else { + cmd = SIOCGIWSCAN; + wrqu.data.length = strlen(extra); + WL_TRACE(("Event WLC_E_SCAN_COMPLETE from specific scan %d\n", + g_iscan->iscan_state)); + } +#else + cmd = SIOCGIWSCAN; + wrqu.data.length = strlen(extra); + WL_TRACE(("Event WLC_E_SCAN_COMPLETE\n")); +#endif + break; + + + case WLC_E_PFN_NET_FOUND: + { + wl_pfn_net_info_t *netinfo; + netinfo = (wl_pfn_net_info_t *)(data + sizeof(wl_pfn_scanresults_t) - + sizeof(wl_pfn_net_info_t)); + WL_ERROR(("%s Event WLC_E_PFN_NET_FOUND, send %s up : find %s len=%d\n", + __FUNCTION__, PNO_EVENT_UP, netinfo->pfnsubnet.SSID, + netinfo->pfnsubnet.SSID_len)); + cmd = IWEVCUSTOM; + memset(&wrqu, 0, sizeof(wrqu)); + strcpy(extra, PNO_EVENT_UP); + wrqu.data.length = strlen(extra); + } + break; + + default: + + WL_TRACE(("Unknown Event %d: ignoring\n", event_type)); + break; + } + if (cmd) { + if (cmd == SIOCGIWSCAN) + wireless_send_event(dev, cmd, &wrqu, NULL); + else + wireless_send_event(dev, cmd, &wrqu, extra); + } + +#if WIRELESS_EXT > 14 + + memset(extra, 0, sizeof(extra)); + if (wl_iw_check_conn_fail(e, extra, sizeof(extra))) { + cmd = IWEVCUSTOM; + wrqu.data.length = strlen(extra); + wireless_send_event(dev, cmd, &wrqu, extra); + } +#endif + + goto wl_iw_event_end; +wl_iw_event_end: + + net_os_wake_unlock(dev); +#endif +} + +int +wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstats) +{ + int res = 0; + wl_cnt_t cnt; + int phy_noise; + int rssi; + scb_val_t scb_val; + + phy_noise = 0; + if ((res = dev_wlc_ioctl(dev, WLC_GET_PHY_NOISE, &phy_noise, sizeof(phy_noise)))) + goto done; + + phy_noise = dtoh32(phy_noise); + WL_TRACE(("wl_iw_get_wireless_stats phy noise=%d\n", phy_noise)); + + bzero(&scb_val, sizeof(scb_val_t)); + if ((res = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t)))) + goto done; + + rssi = dtoh32(scb_val.val); + WL_TRACE(("wl_iw_get_wireless_stats rssi=%d\n", rssi)); + if (rssi <= WL_IW_RSSI_NO_SIGNAL) + wstats->qual.qual = 0; + else if (rssi <= WL_IW_RSSI_VERY_LOW) + wstats->qual.qual = 1; + else if (rssi <= WL_IW_RSSI_LOW) + wstats->qual.qual = 2; + else if (rssi <= WL_IW_RSSI_GOOD) + wstats->qual.qual = 3; + else if (rssi <= WL_IW_RSSI_VERY_GOOD) + wstats->qual.qual = 4; + else + wstats->qual.qual = 5; + + + wstats->qual.level = 0x100 + rssi; + wstats->qual.noise = 0x100 + phy_noise; +#if WIRELESS_EXT > 18 + wstats->qual.updated |= (IW_QUAL_ALL_UPDATED | IW_QUAL_DBM); +#else + wstats->qual.updated |= 7; +#endif + +#if WIRELESS_EXT > 11 + WL_TRACE(("wl_iw_get_wireless_stats counters=%d\n", (int)sizeof(wl_cnt_t))); + + memset(&cnt, 0, sizeof(wl_cnt_t)); + res = dev_wlc_bufvar_get(dev, "counters", (char *)&cnt, sizeof(wl_cnt_t)); + if (res) + { + WL_ERROR(("wl_iw_get_wireless_stats counters failed error=%d\n", res)); + goto done; + } + + cnt.version = dtoh16(cnt.version); + if (cnt.version != WL_CNT_T_VERSION) { + WL_TRACE(("\tIncorrect version of counters struct: expected %d; got %d\n", + WL_CNT_T_VERSION, cnt.version)); + goto done; + } + + wstats->discard.nwid = 0; + wstats->discard.code = dtoh32(cnt.rxundec); + wstats->discard.fragment = dtoh32(cnt.rxfragerr); + wstats->discard.retries = dtoh32(cnt.txfail); + wstats->discard.misc = dtoh32(cnt.rxrunt) + dtoh32(cnt.rxgiant); + wstats->miss.beacon = 0; + + WL_TRACE(("wl_iw_get_wireless_stats counters txframe=%d txbyte=%d\n", + dtoh32(cnt.txframe), dtoh32(cnt.txbyte))); + WL_TRACE(("wl_iw_get_wireless_stats counters rxfrmtoolong=%d\n", dtoh32(cnt.rxfrmtoolong))); + WL_TRACE(("wl_iw_get_wireless_stats counters rxbadplcp=%d\n", dtoh32(cnt.rxbadplcp))); + WL_TRACE(("wl_iw_get_wireless_stats counters rxundec=%d\n", dtoh32(cnt.rxundec))); + WL_TRACE(("wl_iw_get_wireless_stats counters rxfragerr=%d\n", dtoh32(cnt.rxfragerr))); + WL_TRACE(("wl_iw_get_wireless_stats counters txfail=%d\n", dtoh32(cnt.txfail))); + WL_TRACE(("wl_iw_get_wireless_stats counters rxrunt=%d\n", dtoh32(cnt.rxrunt))); + WL_TRACE(("wl_iw_get_wireless_stats counters rxgiant=%d\n", dtoh32(cnt.rxgiant))); + +#endif + +done: + return res; +} +#if defined(COEX_DHCP) +static void +wl_iw_bt_flag_set( + struct net_device *dev, + bool set) +{ +#if defined(BT_DHCP_USE_FLAGS) + char buf_flag7_dhcp_on[8] = { 7, 00, 00, 00, 0x1, 0x0, 0x00, 0x00 }; + char buf_flag7_default[8] = { 7, 00, 00, 00, 0x0, 0x00, 0x00, 0x00}; +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + rtnl_lock(); +#endif + + +#if defined(BT_DHCP_eSCO_FIX) + + set_btc_esco_params(dev, set); +#endif + + +#if defined(BT_DHCP_USE_FLAGS) + WL_TRACE_COEX(("WI-FI priority boost via bt flags, set:%d\n", set)); + if (set == TRUE) { + + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_dhcp_on[0], sizeof(buf_flag7_dhcp_on)); + } + else { + + dev_wlc_bufvar_set(dev, "btc_flags", + (char *)&buf_flag7_default[0], sizeof(buf_flag7_default)); + } +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + rtnl_unlock(); +#endif +} + +static void +wl_iw_bt_timerfunc(ulong data) +{ + bt_info_t *bt_local = (bt_info_t *)data; + bt_local->timer_on = 0; + WL_TRACE(("%s\n", __FUNCTION__)); + + up(&bt_local->tsk_ctl.sema); +} + +static int +_bt_dhcp_sysioc_thread(void *data) +{ + tsk_ctl_t *tsk_ctl = (tsk_ctl_t *)data; + + DAEMONIZE("dhcp_sysioc"); + + complete(&tsk_ctl->completed); + + while (down_interruptible(&tsk_ctl->sema) == 0) { + + SMP_RD_BARRIER_DEPENDS(); + if (tsk_ctl->terminated) { + break; + } + + if (g_bt->timer_on) { + g_bt->timer_on = 0; + del_timer_sync(&g_bt->timer); + } + + switch (g_bt->bt_state) { + case BT_DHCP_START: + + WL_TRACE_COEX(("%s bt_dhcp stm: started \n", __FUNCTION__)); + g_bt->bt_state = BT_DHCP_OPPORTUNITY_WINDOW; + mod_timer(&g_bt->timer, + jiffies + BT_DHCP_OPPORTUNITY_WINDOW_TIME*HZ/1000); + g_bt->timer_on = 1; + break; + + case BT_DHCP_OPPORTUNITY_WINDOW: + if (g_bt->dhcp_done) { + WL_TRACE_COEX(("%s DHCP Done before T1 expiration\n", + __FUNCTION__)); + goto btc_coex_idle; + } + + + WL_TRACE_COEX(("%s DHCP T1:%d expired\n", + __FUNCTION__, BT_DHCP_OPPORTUNITY_WINDOW_TIME)); + + if (g_bt->dev) wl_iw_bt_flag_set(g_bt->dev, TRUE); + g_bt->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT; + mod_timer(&g_bt->timer, jiffies + BT_DHCP_FLAG_FORCE_TIME*HZ/1000); + g_bt->timer_on = 1; + break; + + case BT_DHCP_FLAG_FORCE_TIMEOUT: + if (g_bt->dhcp_done) { + WL_TRACE_COEX(("%s DHCP Done before T2 expiration\n", + __FUNCTION__)); + } else { + + WL_TRACE_COEX(("%s DHCP wait interval T2:%d msec expired\n", + __FUNCTION__, BT_DHCP_FLAG_FORCE_TIME)); + } + + + if (g_bt->dev) wl_iw_bt_flag_set(g_bt->dev, FALSE); + btc_coex_idle: + g_bt->bt_state = BT_DHCP_IDLE; + g_bt->timer_on = 0; + break; + + default: + WL_ERROR(("%s error g_status=%d !!!\n", __FUNCTION__, + g_bt->bt_state)); + if (g_bt->dev) wl_iw_bt_flag_set(g_bt->dev, FALSE); + g_bt->bt_state = BT_DHCP_IDLE; + g_bt->timer_on = 0; + break; + } + + net_os_wake_unlock(g_bt->dev); + } + + if (g_bt->timer_on) { + g_bt->timer_on = 0; + del_timer_sync(&g_bt->timer); + } + complete_and_exit(&tsk_ctl->completed, 0); +} + +static void +wl_iw_bt_release(void) +{ + bt_info_t *bt_local = g_bt; + + if (!bt_local) { + return; + } + + if (bt_local->tsk_ctl.thr_pid >= 0) { + PROC_STOP(&bt_local->tsk_ctl); + } + kfree(bt_local); + g_bt = NULL; +} + +static int +wl_iw_bt_init(struct net_device *dev) +{ + bt_info_t *bt_dhcp = NULL; + + bt_dhcp = kmalloc(sizeof(bt_info_t), GFP_KERNEL); + if (!bt_dhcp) + return -ENOMEM; + + memset(bt_dhcp, 0, sizeof(bt_info_t)); + + g_bt = bt_dhcp; + bt_dhcp->dev = dev; + bt_dhcp->bt_state = BT_DHCP_IDLE; + + + bt_dhcp->timer_ms = 10; + init_timer(&bt_dhcp->timer); + bt_dhcp->timer.data = (ulong)bt_dhcp; + bt_dhcp->timer.function = wl_iw_bt_timerfunc; + bt_dhcp->ts_dhcp_start = 0; + bt_dhcp->ts_dhcp_ok = 0; + + PROC_START(_bt_dhcp_sysioc_thread, bt_dhcp, &bt_dhcp->tsk_ctl, 0); + if (bt_dhcp->tsk_ctl.thr_pid < 0) { + WL_ERROR(("Failed in %s\n", __FUNCTION__)); + return -ENOMEM; + } + + return 0; +} +#endif + +int +wl_iw_attach(struct net_device *dev, void * dhdp) +{ +#if defined(WL_IW_USE_ISCAN) + int params_size = 0; +#endif + wl_iw_t *iw; +#if defined(WL_IW_USE_ISCAN) + iscan_info_t *iscan = NULL; +#endif + + DHD_OS_MUTEX_INIT(&wl_cache_lock); + DHD_OS_MUTEX_INIT(&wl_softap_lock); + +#if defined(WL_IW_USE_ISCAN) + if (!dev) + return 0; + + + memset(&g_wl_iw_params, 0, sizeof(wl_iw_extra_params_t)); + + +#ifdef CSCAN + params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_iscan_params_t, params)) + + (WL_NUMCHANNELS * sizeof(uint16)) + WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t); +#else + params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_iscan_params_t, params)); +#endif + iscan = kmalloc(sizeof(iscan_info_t), GFP_KERNEL); + if (!iscan) + return -ENOMEM; + memset(iscan, 0, sizeof(iscan_info_t)); + + + iscan->iscan_ex_params_p = (wl_iscan_params_t*)kmalloc(params_size, GFP_KERNEL); + if (!iscan->iscan_ex_params_p) { + kfree(iscan); + return -ENOMEM; + } + iscan->iscan_ex_param_size = params_size; + + + g_iscan = iscan; + iscan->dev = dev; + iscan->iscan_state = ISCAN_STATE_IDLE; + +#if defined(CONFIG_FIRST_SCAN) + g_first_broadcast_scan = BROADCAST_SCAN_FIRST_IDLE; + g_first_counter_scans = 0; + g_iscan->scan_flag = 0; +#endif + +#ifdef CONFIG_WPS2 + g_wps_probe_req_ie = NULL; + g_wps_probe_req_ie_len = 0; +#endif + + iscan->timer_ms = 8000; + init_timer(&iscan->timer); + iscan->timer.data = (ulong)iscan; + iscan->timer.function = wl_iw_timerfunc; + + PROC_START(_iscan_sysioc_thread, iscan, &iscan->tsk_ctl, 0); + if (iscan->tsk_ctl.thr_pid < 0) + return -ENOMEM; +#endif + + iw = *(wl_iw_t **)netdev_priv(dev); + iw->pub = (dhd_pub_t *)dhdp; +#ifdef SOFTAP + priv_dev = dev; +#endif + g_scan = NULL; + + + g_scan = (void *)kmalloc(G_SCAN_RESULTS, GFP_KERNEL); + if (!g_scan) + return -ENOMEM; + + memset(g_scan, 0, G_SCAN_RESULTS); + g_scan_specified_ssid = 0; + +#if !defined(CSCAN) + + wl_iw_init_ss_cache_ctrl(); +#endif +#ifdef COEX_DHCP + + wl_iw_bt_init(dev); +#endif + + + return 0; +} + +void +wl_iw_detach(void) +{ +#if defined(WL_IW_USE_ISCAN) + iscan_buf_t *buf; + iscan_info_t *iscan = g_iscan; + + if (!iscan) + return; + if (iscan->tsk_ctl.thr_pid >= 0) { + PROC_STOP(&iscan->tsk_ctl); + } + DHD_OS_MUTEX_LOCK(&wl_cache_lock); + while (iscan->list_hdr) { + buf = iscan->list_hdr->next; + kfree(iscan->list_hdr); + iscan->list_hdr = buf; + } + kfree(iscan->iscan_ex_params_p); + kfree(iscan); + g_iscan = NULL; + DHD_OS_MUTEX_UNLOCK(&wl_cache_lock); +#endif + + if (g_scan) + kfree(g_scan); + + g_scan = NULL; +#ifdef CONFIG_WPS2 + + if (g_wps_probe_req_ie) { + kfree(g_wps_probe_req_ie); + g_wps_probe_req_ie = NULL; + g_wps_probe_req_ie_len = 0; + } +#endif +#if !defined(CSCAN) + wl_iw_release_ss_cache_ctrl(); +#endif +#ifdef COEX_DHCP + wl_iw_bt_release(); +#endif + +#ifdef SOFTAP + if (ap_cfg_running) { + WL_TRACE(("\n%s AP is going down\n", __FUNCTION__)); + + wl_iw_send_priv_event(priv_dev, "AP_DOWN"); + } +#endif + +} diff --git a/drivers/net/wireless/bcmdhd/wl_iw.h b/drivers/net/wireless/bcmdhd/wl_iw.h new file mode 100644 index 0000000000000..9cdb53dfef8c2 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_iw.h @@ -0,0 +1,319 @@ +/* + * Linux Wireless Extensions support + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_iw.h,v 1.15.80.6 2010-12-23 01:13:23 $ + */ + + +#ifndef _wl_iw_h_ +#define _wl_iw_h_ + +#include + +#include +#include +#include + +#define WL_SCAN_PARAMS_SSID_MAX 10 +#define GET_SSID "SSID=" +#define GET_CHANNEL "CH=" +#define GET_NPROBE "NPROBE=" +#define GET_ACTIVE_ASSOC_DWELL "ACTIVE=" +#define GET_PASSIVE_ASSOC_DWELL "PASSIVE=" +#define GET_HOME_DWELL "HOME=" +#define GET_SCAN_TYPE "TYPE=" + +#define BAND_GET_CMD "GETBAND" +#define BAND_SET_CMD "SETBAND" +#define DTIM_SKIP_GET_CMD "DTIMSKIPGET" +#define DTIM_SKIP_SET_CMD "DTIMSKIPSET" +#define SETSUSPENDOPT_CMD "SETSUSPENDOPT" +#define SETSUSPENDMODE_CMD "SETSUSPENDMODE" +#define PNOSSIDCLR_SET_CMD "PNOSSIDCLR" + +#define PNOSETUP_SET_CMD "PNOSETUP " +#define PNOSETADD_SET_CMD "PNOSETADD" +#define PNOENABLE_SET_CMD "PNOFORCE" +#define PNODEBUG_SET_CMD "PNODEBUG" +#define TXPOWER_SET_CMD "TXPOWER" + +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + + +typedef struct wl_iw_extra_params { + int target_channel; +} wl_iw_extra_params_t; + +struct cntry_locales_custom { + char iso_abbrev[WLC_CNTRY_BUF_SZ]; + char custom_locale[WLC_CNTRY_BUF_SZ]; + int32 custom_locale_rev; +}; + + +#define WL_IW_RSSI_MINVAL -200 +#define WL_IW_RSSI_NO_SIGNAL -91 +#define WL_IW_RSSI_VERY_LOW -80 +#define WL_IW_RSSI_LOW -70 +#define WL_IW_RSSI_GOOD -68 +#define WL_IW_RSSI_VERY_GOOD -58 +#define WL_IW_RSSI_EXCELLENT -57 +#define WL_IW_RSSI_INVALID 0 +#define MAX_WX_STRING 80 +#define isprint(c) bcm_isprint(c) +#define WL_IW_SET_ACTIVE_SCAN (SIOCIWFIRSTPRIV+1) +#define WL_IW_GET_RSSI (SIOCIWFIRSTPRIV+3) +#define WL_IW_SET_PASSIVE_SCAN (SIOCIWFIRSTPRIV+5) +#define WL_IW_GET_LINK_SPEED (SIOCIWFIRSTPRIV+7) +#define WL_IW_GET_CURR_MACADDR (SIOCIWFIRSTPRIV+9) +#define WL_IW_SET_STOP (SIOCIWFIRSTPRIV+11) +#define WL_IW_SET_START (SIOCIWFIRSTPRIV+13) + + +#define WL_SET_AP_CFG (SIOCIWFIRSTPRIV+15) +#define WL_AP_STA_LIST (SIOCIWFIRSTPRIV+17) +#define WL_AP_MAC_FLTR (SIOCIWFIRSTPRIV+19) +#define WL_AP_BSS_START (SIOCIWFIRSTPRIV+21) +#define AP_LPB_CMD (SIOCIWFIRSTPRIV+23) +#define WL_AP_STOP (SIOCIWFIRSTPRIV+25) +#define WL_FW_RELOAD (SIOCIWFIRSTPRIV+27) +#define WL_AP_STA_DISASSOC (SIOCIWFIRSTPRIV+29) +#define WL_COMBO_SCAN (SIOCIWFIRSTPRIV+31) + + +#define G_SCAN_RESULTS 8*1024 +#define WE_ADD_EVENT_FIX 0x80 +#define G_WLAN_SET_ON 0 +#define G_WLAN_SET_OFF 1 + +#define CHECK_EXTRA_FOR_NULL(extra) \ +if (!extra) { \ + WL_ERROR(("%s: error : extra is null pointer\n", __FUNCTION__)); \ + return -EINVAL; \ +} + +typedef struct wl_iw { + char nickname[IW_ESSID_MAX_SIZE]; + + struct iw_statistics wstats; + + int spy_num; + int wpaversion; + int pcipher; + int gcipher; + int privacy_invoked; + + struct ether_addr spy_addr[IW_MAX_SPY]; + struct iw_quality spy_qual[IW_MAX_SPY]; + void *wlinfo; + dhd_pub_t * pub; +} wl_iw_t; + +int wl_control_wl_start(struct net_device *dev); +#define WLC_IW_SS_CACHE_MAXLEN 2048 +#define WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN 32 +#define WLC_IW_BSS_INFO_MAXLEN \ + (WLC_IW_SS_CACHE_MAXLEN - WLC_IW_SS_CACHE_CTRL_FIELD_MAXLEN) + +typedef struct wl_iw_ss_cache { + struct wl_iw_ss_cache *next; + int dirty; + uint32 buflen; + uint32 version; + uint32 count; + wl_bss_info_t bss_info[1]; +} wl_iw_ss_cache_t; + +typedef struct wl_iw_ss_cache_ctrl { + wl_iw_ss_cache_t *m_cache_head; + int m_link_down; + int m_timer_expired; + char m_active_bssid[ETHER_ADDR_LEN]; + uint m_prev_scan_mode; + uint m_cons_br_scan_cnt; + struct timer_list *m_timer; +} wl_iw_ss_cache_ctrl_t; + +typedef enum broadcast_first_scan { + BROADCAST_SCAN_FIRST_IDLE = 0, + BROADCAST_SCAN_FIRST_STARTED, + BROADCAST_SCAN_FIRST_RESULT_READY, + BROADCAST_SCAN_FIRST_RESULT_CONSUMED +} broadcast_first_scan_t; +#ifdef SOFTAP +#define SSID_LEN 33 +#define SEC_LEN 16 +#define KEY_LEN 65 +#define PROFILE_OFFSET 32 +struct ap_profile { + uint8 ssid[SSID_LEN]; + uint8 sec[SEC_LEN]; + uint8 key[KEY_LEN]; + uint32 channel; + uint32 preamble; + uint32 max_scb; + uint32 closednet; + char country_code[WLC_CNTRY_BUF_SZ]; +}; + + +#define MACLIST_MODE_DISABLED 0 +#define MACLIST_MODE_DENY 1 +#define MACLIST_MODE_ALLOW 2 +struct mflist { + uint count; + struct ether_addr ea[16]; +}; +struct mac_list_set { + uint32 mode; + struct mflist mac_list; +}; +#endif + +#if WIRELESS_EXT > 12 +#include +extern const struct iw_handler_def wl_iw_handler_def; +#endif + +extern int wl_iw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +extern void wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data); +extern int wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstats); +int wl_iw_attach(struct net_device *dev, void * dhdp); +void wl_iw_detach(void); + +extern int net_os_wake_lock(struct net_device *dev); +extern int net_os_wake_unlock(struct net_device *dev); +extern int net_os_wake_lock_timeout(struct net_device *dev); +extern int net_os_wake_lock_ctrl_timeout_enable(struct net_device *dev, int val); +extern int net_os_set_suspend_disable(struct net_device *dev, int val); +extern int net_os_set_suspend(struct net_device *dev, int val, int force); +extern int net_os_set_dtim_skip(struct net_device *dev, int val); +extern void get_customized_country_code(char *country_iso_code, wl_country_t *cspec); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +#define IWE_STREAM_ADD_EVENT(info, stream, ends, iwe, extra) \ + iwe_stream_add_event(info, stream, ends, iwe, extra) +#define IWE_STREAM_ADD_VALUE(info, event, value, ends, iwe, event_len) \ + iwe_stream_add_value(info, event, value, ends, iwe, event_len) +#define IWE_STREAM_ADD_POINT(info, stream, ends, iwe, extra) \ + iwe_stream_add_point(info, stream, ends, iwe, extra) +#else +#define IWE_STREAM_ADD_EVENT(info, stream, ends, iwe, extra) \ + iwe_stream_add_event(stream, ends, iwe, extra) +#define IWE_STREAM_ADD_VALUE(info, event, value, ends, iwe, event_len) \ + iwe_stream_add_value(event, value, ends, iwe, event_len) +#define IWE_STREAM_ADD_POINT(info, stream, ends, iwe, extra) \ + iwe_stream_add_point(stream, ends, iwe, extra) +#endif + +extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); +extern int dhd_pno_clean(dhd_pub_t *dhd); +extern int dhd_pno_set(dhd_pub_t *dhd, wlc_ssid_t* ssids_local, int nssid, + ushort scan_fr, int pno_repeat, int pno_freq_expo_max); +extern int dhd_pno_get_status(dhd_pub_t *dhd); +extern int dhd_dev_pno_reset(struct net_device *dev); +extern int dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, + int nssid, ushort scan_fr, int pno_repeat, int pno_freq_expo_max); +extern int dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled); +extern int dhd_dev_get_pno_status(struct net_device *dev); +extern int dhd_get_dtim_skip(dhd_pub_t *dhd); + +void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec); + +#define PNO_TLV_PREFIX 'S' +#define PNO_TLV_VERSION '1' +#define PNO_TLV_SUBVERSION '2' +#define PNO_TLV_RESERVED '0' +#define PNO_TLV_TYPE_SSID_IE 'S' +#define PNO_TLV_TYPE_TIME 'T' +#define PNO_TLV_FREQ_REPEAT 'R' +#define PNO_TLV_FREQ_EXPO_MAX 'M' +#define PNO_EVENT_UP "PNO_EVENT" + +typedef struct cmd_tlv { + char prefix; + char version; + char subver; + char reserved; +} cmd_tlv_t; + + + + +typedef struct cscan_tlv { + char prefix; + char version; + char subver; + char reserved; +} cscan_tlv_t; + +#define CSCAN_COMMAND "CSCAN " +#define CSCAN_TLV_PREFIX 'S' +#define CSCAN_TLV_VERSION 1 +#define CSCAN_TLV_SUBVERSION 0 +#define CSCAN_TLV_TYPE_SSID_IE 'S' +#define CSCAN_TLV_TYPE_CHANNEL_IE 'C' +#define CSCAN_TLV_TYPE_NPROBE_IE 'N' +#define CSCAN_TLV_TYPE_ACTIVE_IE 'A' +#define CSCAN_TLV_TYPE_PASSIVE_IE 'P' +#define CSCAN_TLV_TYPE_HOME_IE 'H' +#define CSCAN_TLV_TYPE_STYPE_IE 'T' + +#ifdef SOFTAP_TLV_CFG + +#define SOFTAP_SET_CMD "SOFTAPSET " +#define SOFTAP_TLV_PREFIX 'A' +#define SOFTAP_TLV_VERSION '1' +#define SOFTAP_TLV_SUBVERSION '0' +#define SOFTAP_TLV_RESERVED '0' + +#define TLV_TYPE_SSID 'S' +#define TLV_TYPE_SECUR 'E' +#define TLV_TYPE_KEY 'K' +#define TLV_TYPE_CHANNEL 'C' +#endif + +extern int wl_iw_parse_channel_list_tlv(char** list_str, uint16* channel_list, + int channel_num, int *bytes_left); + +extern int wl_iw_parse_data_tlv(char** list_str, void *dst, int dst_size, + const char token, int input_size, int *bytes_left); + +extern int wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_t* ssid, + int max, int *bytes_left); + +extern int wl_iw_parse_ssid_list(char** list_str, wlc_ssid_t* ssid, int idx, int max); + +extern int wl_iw_parse_channel_list(char** list_str, uint16* channel_list, int channel_num); + + +#define NETDEV_PRIV(dev) (*(wl_iw_t **)netdev_priv(dev)) + +#ifdef CONFIG_WPS2 +#define WPS_ADD_PROBE_REQ_IE_CMD "ADD_WPS_PROBE_REQ_IE " +#define WPS_DEL_PROBE_REQ_IE_CMD "DEL_WPS_PROBE_REQ_IE " +#define WPS_PROBE_REQ_IE_CMD_LENGTH 21 +#endif + +#endif diff --git a/drivers/net/wireless/bcmdhd/wl_linux_mon.c b/drivers/net/wireless/bcmdhd/wl_linux_mon.c new file mode 100644 index 0000000000000..f44b4b04bb964 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wl_linux_mon.c @@ -0,0 +1,409 @@ +/* + * Broadcom Dongle Host Driver (DHD), Linux monitor network interface + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_linux_mon.c 303266 2011-12-16 00:15:23Z $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +typedef enum monitor_states +{ + MONITOR_STATE_DEINIT = 0x0, + MONITOR_STATE_INIT = 0x1, + MONITOR_STATE_INTERFACE_ADDED = 0x2, + MONITOR_STATE_INTERFACE_DELETED = 0x4 +} monitor_states_t; +extern int dhd_start_xmit(struct sk_buff *skb, struct net_device *net); + +/** + * Local declarations and defintions (not exposed) + */ +#define MON_PRINT(format, ...) printk("DHD-MON: %s " format, __func__, ##__VA_ARGS__) +#define MON_TRACE MON_PRINT + +typedef struct monitor_interface { + int radiotap_enabled; + struct net_device* real_ndev; /* The real interface that the monitor is on */ + struct net_device* mon_ndev; +} monitor_interface; + +typedef struct dhd_linux_monitor { + void *dhd_pub; + monitor_states_t monitor_state; + monitor_interface mon_if[DHD_MAX_IFS]; + struct mutex lock; /* lock to protect mon_if */ +} dhd_linux_monitor_t; + +static dhd_linux_monitor_t g_monitor; + +static struct net_device* lookup_real_netdev(char *name); +static monitor_interface* ndev_to_monif(struct net_device *ndev); +static int dhd_mon_if_open(struct net_device *ndev); +static int dhd_mon_if_stop(struct net_device *ndev); +static int dhd_mon_if_subif_start_xmit(struct sk_buff *skb, struct net_device *ndev); +static void dhd_mon_if_set_multicast_list(struct net_device *ndev); +static int dhd_mon_if_change_mac(struct net_device *ndev, void *addr); + +static const struct net_device_ops dhd_mon_if_ops = { + .ndo_open = dhd_mon_if_open, + .ndo_stop = dhd_mon_if_stop, + .ndo_start_xmit = dhd_mon_if_subif_start_xmit, + .ndo_set_multicast_list = dhd_mon_if_set_multicast_list, + .ndo_set_mac_address = dhd_mon_if_change_mac, +}; + +/** + * Local static function defintions + */ + +/* Look up dhd's net device table to find a match (e.g. interface "eth0" is a match for "mon.eth0" + * "p2p-eth0-0" is a match for "mon.p2p-eth0-0") + */ +static struct net_device* lookup_real_netdev(char *name) +{ + int i; + int len = 0; + int last_name_len = 0; + struct net_device *ndev; + struct net_device *ndev_found = NULL; + + /* We need to find interface "p2p-p2p-0" corresponding to monitor interface "mon-p2p-0", + * Once mon iface name reaches IFNAMSIZ, it is reset to p2p0-0 and corresponding mon + * iface would be mon-p2p0-0. + */ + for (i = 0; i < DHD_MAX_IFS; i++) { + ndev = dhd_idx2net(g_monitor.dhd_pub, i); + + /* Skip "p2p" and look for "-p2p0-x" in monitor interface name. If it + * it matches, then this netdev is the corresponding real_netdev. + */ + if (ndev && strstr(ndev->name, "p2p-p2p0")) { + len = strlen("p2p"); + } else { + /* if p2p- is not present, then the IFNAMSIZ have reached and name + * would have got reset. In this casse,look for p2p0-x in mon-p2p0-x + */ + len = 0; + } + if (ndev && strstr(name, (ndev->name + len))) { + if (strlen(ndev->name) > last_name_len) { + ndev_found = ndev; + last_name_len = strlen(ndev->name); + } + } + } + + return ndev_found; +} + +static monitor_interface* ndev_to_monif(struct net_device *ndev) +{ + int i; + + for (i = 0; i < DHD_MAX_IFS; i++) { + if (g_monitor.mon_if[i].mon_ndev == ndev) + return &g_monitor.mon_if[i]; + } + + return NULL; +} + +static int dhd_mon_if_open(struct net_device *ndev) +{ + int ret = 0; + + MON_PRINT("enter\n"); + return ret; +} + +static int dhd_mon_if_stop(struct net_device *ndev) +{ + int ret = 0; + + MON_PRINT("enter\n"); + return ret; +} + +static int dhd_mon_if_subif_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + int ret = 0; + int rtap_len; + int qos_len = 0; + int dot11_hdr_len = 24; + int snap_len = 6; + unsigned char *pdata; + unsigned short frame_ctl; + unsigned char src_mac_addr[6]; + unsigned char dst_mac_addr[6]; + struct ieee80211_hdr *dot11_hdr; + struct ieee80211_radiotap_header *rtap_hdr; + monitor_interface* mon_if; + + MON_PRINT("enter\n"); + + mon_if = ndev_to_monif(ndev); + if (mon_if == NULL || mon_if->real_ndev == NULL) { + MON_PRINT(" cannot find matched net dev, skip the packet\n"); + goto fail; + } + + if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) + goto fail; + + rtap_hdr = (struct ieee80211_radiotap_header *)skb->data; + if (unlikely(rtap_hdr->it_version)) + goto fail; + + rtap_len = ieee80211_get_radiotap_len(skb->data); + if (unlikely(skb->len < rtap_len)) + goto fail; + + MON_PRINT("radiotap len (should be 14): %d\n", rtap_len); + + /* Skip the ratio tap header */ + skb_pull(skb, rtap_len); + + dot11_hdr = (struct ieee80211_hdr *)skb->data; + frame_ctl = le16_to_cpu(dot11_hdr->frame_control); + /* Check if the QoS bit is set */ + if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { + /* Check if this ia a Wireless Distribution System (WDS) frame + * which has 4 MAC addresses + */ + if (dot11_hdr->frame_control & 0x0080) + qos_len = 2; + if ((dot11_hdr->frame_control & 0x0300) == 0x0300) + dot11_hdr_len += 6; + + memcpy(dst_mac_addr, dot11_hdr->addr1, sizeof(dst_mac_addr)); + memcpy(src_mac_addr, dot11_hdr->addr2, sizeof(src_mac_addr)); + + /* Skip the 802.11 header, QoS (if any) and SNAP, but leave spaces for + * for two MAC addresses + */ + skb_pull(skb, dot11_hdr_len + qos_len + snap_len - sizeof(src_mac_addr) * 2); + pdata = (unsigned char*)skb->data; + memcpy(pdata, dst_mac_addr, sizeof(dst_mac_addr)); + memcpy(pdata + sizeof(dst_mac_addr), src_mac_addr, sizeof(src_mac_addr)); + + MON_PRINT("if name: %s, matched if name %s\n", ndev->name, mon_if->real_ndev->name); + + /* Use the real net device to transmit the packet */ + ret = dhd_start_xmit(skb, mon_if->real_ndev); + + return ret; + } +fail: + dev_kfree_skb(skb); + return 0; +} + +static void dhd_mon_if_set_multicast_list(struct net_device *ndev) +{ + monitor_interface* mon_if; + + mon_if = ndev_to_monif(ndev); + if (mon_if == NULL || mon_if->real_ndev == NULL) { + MON_PRINT(" cannot find matched net dev, skip the packet\n"); + } else { + MON_PRINT("enter, if name: %s, matched if name %s\n", + ndev->name, mon_if->real_ndev->name); + } +} + +static int dhd_mon_if_change_mac(struct net_device *ndev, void *addr) +{ + int ret = 0; + monitor_interface* mon_if; + + mon_if = ndev_to_monif(ndev); + if (mon_if == NULL || mon_if->real_ndev == NULL) { + MON_PRINT(" cannot find matched net dev, skip the packet\n"); + } else { + MON_PRINT("enter, if name: %s, matched if name %s\n", + ndev->name, mon_if->real_ndev->name); + } + return ret; +} + +/** + * Global function definitions (declared in dhd_linux_mon.h) + */ + +int dhd_add_monitor(char *name, struct net_device **new_ndev) +{ + int i; + int idx = -1; + int ret = 0; + struct net_device* ndev = NULL; + dhd_linux_monitor_t **dhd_mon; + + mutex_lock(&g_monitor.lock); + + MON_TRACE("enter, if name: %s\n", name); + if (!name || !new_ndev) { + MON_PRINT("invalid parameters\n"); + ret = -EINVAL; + goto out; + } + + /* + * Find a vacancy + */ + for (i = 0; i < DHD_MAX_IFS; i++) + if (g_monitor.mon_if[i].mon_ndev == NULL) { + idx = i; + break; + } + if (idx == -1) { + MON_PRINT("exceeds maximum interfaces\n"); + ret = -EFAULT; + goto out; + } + + ndev = alloc_etherdev(sizeof(dhd_linux_monitor_t*)); + if (!ndev) { + MON_PRINT("failed to allocate memory\n"); + ret = -ENOMEM; + goto out; + } + + ndev->type = ARPHRD_IEEE80211_RADIOTAP; + strncpy(ndev->name, name, IFNAMSIZ); + ndev->name[IFNAMSIZ - 1] = 0; + ndev->netdev_ops = &dhd_mon_if_ops; + + ret = register_netdevice(ndev); + if (ret) { + MON_PRINT(" register_netdevice failed (%d)\n", ret); + goto out; + } + + *new_ndev = ndev; + g_monitor.mon_if[idx].radiotap_enabled = TRUE; + g_monitor.mon_if[idx].mon_ndev = ndev; + g_monitor.mon_if[idx].real_ndev = lookup_real_netdev(name); + dhd_mon = (dhd_linux_monitor_t **)netdev_priv(ndev); + *dhd_mon = &g_monitor; + g_monitor.monitor_state = MONITOR_STATE_INTERFACE_ADDED; + MON_PRINT("net device returned: 0x%p\n", ndev); + MON_PRINT("found a matched net device, name %s\n", g_monitor.mon_if[idx].real_ndev->name); + +out: + if (ret && ndev) + free_netdev(ndev); + + mutex_unlock(&g_monitor.lock); + return ret; + +} + +int dhd_del_monitor(struct net_device *ndev) +{ + int i; + bool rollback_lock = false; + if (!ndev) + return -EINVAL; + mutex_lock(&g_monitor.lock); + for (i = 0; i < DHD_MAX_IFS; i++) { + if (g_monitor.mon_if[i].mon_ndev == ndev || + g_monitor.mon_if[i].real_ndev == ndev) { + g_monitor.mon_if[i].real_ndev = NULL; + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = true; + } + unregister_netdev(g_monitor.mon_if[i].mon_ndev); + free_netdev(g_monitor.mon_if[i].mon_ndev); + g_monitor.mon_if[i].mon_ndev = NULL; + g_monitor.monitor_state = MONITOR_STATE_INTERFACE_DELETED; + break; + } + } + if (rollback_lock) { + rtnl_lock(); + rollback_lock = false; + } + + if (g_monitor.monitor_state != + MONITOR_STATE_INTERFACE_DELETED) + MON_PRINT("interface not found in monitor IF array, is this a monitor IF? 0x%p\n", + ndev); + mutex_unlock(&g_monitor.lock); + + return 0; +} + +int dhd_monitor_init(void *dhd_pub) +{ + if (g_monitor.monitor_state == MONITOR_STATE_DEINIT) { + g_monitor.dhd_pub = dhd_pub; + mutex_init(&g_monitor.lock); + g_monitor.monitor_state = MONITOR_STATE_INIT; + } + return 0; +} + +int dhd_monitor_uninit(void) +{ + int i; + struct net_device *ndev; + bool rollback_lock = false; + mutex_lock(&g_monitor.lock); + if (g_monitor.monitor_state != MONITOR_STATE_DEINIT) { + for (i = 0; i < DHD_MAX_IFS; i++) { + ndev = g_monitor.mon_if[i].mon_ndev; + if (ndev) { + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = true; + } + unregister_netdev(ndev); + free_netdev(ndev); + g_monitor.mon_if[i].real_ndev = NULL; + g_monitor.mon_if[i].mon_ndev = NULL; + if (rollback_lock) { + rtnl_lock(); + rollback_lock = false; + } + } + } + g_monitor.monitor_state = MONITOR_STATE_DEINIT; + } + mutex_unlock(&g_monitor.lock); + return 0; +} diff --git a/drivers/net/wireless/bcmdhd/wldev_common.c b/drivers/net/wireless/bcmdhd/wldev_common.c new file mode 100644 index 0000000000000..8270331850354 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wldev_common.c @@ -0,0 +1,418 @@ +/* + * Common function shared by Linux WEXT, cfg80211 and p2p drivers + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wldev_common.c,v 1.1.4.1.2.14 2011-02-09 01:40:07 $ + */ + +#include +#include +#include +#include + +#include +#include + +#define htod32(i) i +#define htod16(i) i +#define dtoh32(i) i +#define dtoh16(i) i +#define htodchanspec(i) i +#define dtohchanspec(i) i + +#define WLDEV_ERROR(args) \ + do { \ + printk(KERN_ERR "WLDEV-ERROR) %s : ", __func__); \ + printk args; \ + } while (0) + +extern int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd); + +s32 wldev_ioctl( + struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set) +{ + s32 ret = 0; + struct wl_ioctl ioc; + + memset(&ioc, 0, sizeof(ioc)); + ioc.cmd = cmd; + ioc.buf = arg; + ioc.len = len; + ioc.set = set; + + ret = dhd_ioctl_entry_local(dev, &ioc, cmd); + return ret; +} + +/* Format a iovar buffer, not bsscfg indexed. The bsscfg index will be + * taken care of in dhd_ioctl_entry. Internal use only, not exposed to + * wl_iw, wl_cfg80211 and wl_cfgp2p + */ +static s32 wldev_mkiovar( + s8 *iovar_name, s8 *param, s32 paramlen, + s8 *iovar_buf, u32 buflen) +{ + s32 iolen = 0; + + iolen = bcm_mkiovar(iovar_name, param, paramlen, iovar_buf, buflen); + return iolen; +} + +s32 wldev_iovar_getbuf( + struct net_device *dev, s8 *iovar_name, + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync) +{ + s32 ret = 0; + s32 iovar_len = 0; + if (buf_sync) { + mutex_lock(buf_sync); + } + iovar_len = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen); + ret = wldev_ioctl(dev, WLC_GET_VAR, buf, buflen, FALSE); + if (buf_sync) + mutex_unlock(buf_sync); + return ret; +} + + +s32 wldev_iovar_setbuf( + struct net_device *dev, s8 *iovar_name, + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync) +{ + s32 ret = 0; + s32 iovar_len; + if (buf_sync) { + mutex_lock(buf_sync); + } + iovar_len = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen); + ret = wldev_ioctl(dev, WLC_SET_VAR, buf, iovar_len, TRUE); + if (buf_sync) + mutex_unlock(buf_sync); + return ret; +} + +s32 wldev_iovar_setint( + struct net_device *dev, s8 *iovar, s32 val) +{ + s8 iovar_buf[WLC_IOCTL_SMLEN]; + + val = htod32(val); + memset(iovar_buf, 0, sizeof(iovar_buf)); + return wldev_iovar_setbuf(dev, iovar, &val, sizeof(val), iovar_buf, + sizeof(iovar_buf), NULL); +} + + +s32 wldev_iovar_getint( + struct net_device *dev, s8 *iovar, s32 *pval) +{ + s8 iovar_buf[WLC_IOCTL_SMLEN]; + s32 err; + + memset(iovar_buf, 0, sizeof(iovar_buf)); + err = wldev_iovar_getbuf(dev, iovar, pval, sizeof(*pval), iovar_buf, + sizeof(iovar_buf), NULL); + if (err == 0) + { + memcpy(pval, iovar_buf, sizeof(*pval)); + *pval = dtoh32(*pval); + } + return err; +} + +/** Format a bsscfg indexed iovar buffer. The bsscfg index will be + * taken care of in dhd_ioctl_entry. Internal use only, not exposed to + * wl_iw, wl_cfg80211 and wl_cfgp2p + */ +s32 wldev_mkiovar_bsscfg( + const s8 *iovar_name, s8 *param, s32 paramlen, + s8 *iovar_buf, s32 buflen, s32 bssidx) +{ + const s8 *prefix = "bsscfg:"; + s8 *p; + u32 prefixlen; + u32 namelen; + u32 iolen; + + if (bssidx == 0) { + return wldev_mkiovar((s8*)iovar_name, (s8 *)param, paramlen, + (s8 *) iovar_buf, buflen); + } + + prefixlen = (u32) strlen(prefix); /* lengh of bsscfg prefix */ + namelen = (u32) strlen(iovar_name) + 1; /* lengh of iovar name + null */ + iolen = prefixlen + namelen + sizeof(u32) + paramlen; + + if (buflen < 0 || iolen > (u32)buflen) + { + WLDEV_ERROR(("%s: buffer is too short\n", __FUNCTION__)); + return BCME_BUFTOOSHORT; + } + + p = (s8 *)iovar_buf; + + /* copy prefix, no null */ + memcpy(p, prefix, prefixlen); + p += prefixlen; + + /* copy iovar name including null */ + memcpy(p, iovar_name, namelen); + p += namelen; + + /* bss config index as first param */ + bssidx = htod32(bssidx); + memcpy(p, &bssidx, sizeof(u32)); + p += sizeof(u32); + + /* parameter buffer follows */ + if (paramlen) + memcpy(p, param, paramlen); + + return iolen; + +} + +s32 wldev_iovar_getbuf_bsscfg( + struct net_device *dev, s8 *iovar_name, + void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync) +{ + s32 ret = 0; + s32 iovar_len = 0; + if (buf_sync) { + mutex_lock(buf_sync); + } + iovar_len = wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx); + ret = wldev_ioctl(dev, WLC_GET_VAR, buf, buflen, FALSE); + if (buf_sync) { + mutex_unlock(buf_sync); + } + return ret; + +} + +s32 wldev_iovar_setbuf_bsscfg( + struct net_device *dev, s8 *iovar_name, + void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync) +{ + s32 ret = 0; + s32 iovar_len; + if (buf_sync) { + mutex_lock(buf_sync); + } + iovar_len = wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx); + + ret = wldev_ioctl(dev, WLC_SET_VAR, buf, iovar_len, TRUE); + if (buf_sync) { + mutex_unlock(buf_sync); + } + return ret; +} + +s32 wldev_iovar_setint_bsscfg( + struct net_device *dev, s8 *iovar, s32 val, s32 bssidx) +{ + s8 iovar_buf[WLC_IOCTL_SMLEN]; + + val = htod32(val); + memset(iovar_buf, 0, sizeof(iovar_buf)); + return wldev_iovar_setbuf_bsscfg(dev, iovar, &val, sizeof(val), iovar_buf, + sizeof(iovar_buf), bssidx, NULL); +} + + +s32 wldev_iovar_getint_bsscfg( + struct net_device *dev, s8 *iovar, s32 *pval, s32 bssidx) +{ + s8 iovar_buf[WLC_IOCTL_SMLEN]; + s32 err; + + memset(iovar_buf, 0, sizeof(iovar_buf)); + err = wldev_iovar_getbuf_bsscfg(dev, iovar, pval, sizeof(*pval), iovar_buf, + sizeof(iovar_buf), bssidx, NULL); + if (err == 0) + { + memcpy(pval, iovar_buf, sizeof(*pval)); + *pval = dtoh32(*pval); + } + return err; +} + +int wldev_get_link_speed( + struct net_device *dev, int *plink_speed) +{ + int error; + + if (!plink_speed) + return -ENOMEM; + error = wldev_ioctl(dev, WLC_GET_RATE, plink_speed, sizeof(int), 0); + if (unlikely(error)) + return error; + + /* Convert internal 500Kbps to Kbps */ + *plink_speed *= 500; + return error; +} + +int wldev_get_rssi( + struct net_device *dev, int *prssi) +{ + scb_val_t scb_val; + int error; + + if (!prssi) + return -ENOMEM; + bzero(&scb_val, sizeof(scb_val_t)); + + error = wldev_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t), 0); + if (unlikely(error)) + return error; + + *prssi = dtoh32(scb_val.val); + return error; +} + +int wldev_get_ssid( + struct net_device *dev, wlc_ssid_t *pssid) +{ + int error; + + if (!pssid) + return -ENOMEM; + error = wldev_ioctl(dev, WLC_GET_SSID, pssid, sizeof(wlc_ssid_t), 0); + if (unlikely(error)) + return error; + pssid->SSID_len = dtoh32(pssid->SSID_len); + return error; +} + +int wldev_get_band( + struct net_device *dev, uint *pband) +{ + int error; + + error = wldev_ioctl(dev, WLC_GET_BAND, pband, sizeof(uint), 0); + return error; +} + +int wldev_set_band( + struct net_device *dev, uint band) +{ + int error = -1; + + if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_5G) || (band == WLC_BAND_2G)) { + error = wldev_ioctl(dev, WLC_SET_BAND, &band, sizeof(band), 1); + } + return error; +} + +int wldev_set_country( + struct net_device *dev, char *country_code) +{ + int error = -1; + wl_country_t cspec = {{0}, 0, {0}}; + scb_val_t scbval; + char smbuf[WLC_IOCTL_SMLEN]; + + if (!country_code) + return error; + + error = wldev_iovar_getbuf(dev, "country", &cspec, sizeof(cspec), + smbuf, sizeof(smbuf), NULL); + if (error < 0) + WLDEV_ERROR(("%s: get country failed = %d\n", __FUNCTION__, error)); + + if ((error < 0) || + (strncmp(country_code, smbuf, WLC_CNTRY_BUF_SZ) != 0)) { + bzero(&scbval, sizeof(scb_val_t)); + error = wldev_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t), 1); + if (error < 0) { + WLDEV_ERROR(("%s: set country failed due to Disassoc error %d\n", + __FUNCTION__, error)); + return error; + } + } + cspec.rev = -1; + memcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ); + memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ); + get_customized_country_code((char *)&cspec.country_abbrev, &cspec); + error = wldev_iovar_setbuf(dev, "country", &cspec, sizeof(cspec), + smbuf, sizeof(smbuf), NULL); + if (error < 0) { + WLDEV_ERROR(("%s: set country for %s as %s rev %d failed\n", + __FUNCTION__, country_code, cspec.ccode, cspec.rev)); + return error; + } + dhd_bus_country_set(dev, &cspec); + WLDEV_ERROR(("%s: set country for %s as %s rev %d\n", + __FUNCTION__, country_code, cspec.ccode, cspec.rev)); + return 0; +} + +/* + * softap channel autoselect + */ +int wldev_get_auto_channel(struct net_device *dev, int *chan) +{ + int chosen = 0; + wl_uint32_list_t request; + int retry = 0; + int updown = 0; + int ret = 0; + wlc_ssid_t null_ssid; + + memset(&null_ssid, 0, sizeof(wlc_ssid_t)); + ret |= wldev_ioctl(dev, WLC_UP, &updown, sizeof(updown), true); + + ret |= wldev_ioctl(dev, WLC_SET_SSID, &null_ssid, sizeof(null_ssid), true); + + request.count = htod32(0); + ret = wldev_ioctl(dev, WLC_START_CHANNEL_SEL, &request, sizeof(request), true); + if (ret < 0) { + WLDEV_ERROR(("can't start auto channel scan:%d\n", ret)); + goto fail; + } + + while (retry++ < 15) { + + bcm_mdelay(350); + + ret = wldev_ioctl(dev, WLC_GET_CHANNEL_SEL, &chosen, sizeof(chosen), false); + + if ((ret == 0) && (dtoh32(chosen) != 0)) { + *chan = (uint16)chosen & 0x00FF; /* covert chanspec --> chan number */ + printf("%s: Got channel = %d, attempt:%d\n", + __FUNCTION__, *chan, retry); + break; + } + } + + if ((ret = wldev_ioctl(dev, WLC_DOWN, &updown, sizeof(updown), true)) < 0) { + WLDEV_ERROR(("%s fail to WLC_DOWN ioctl err =%d\n", __FUNCTION__, ret)); + goto fail; + } + +fail : + if (ret < 0) { + WLDEV_ERROR(("%s: return value %d\n", __FUNCTION__, ret)); + } + return ret; +} diff --git a/drivers/net/wireless/bcmdhd/wldev_common.h b/drivers/net/wireless/bcmdhd/wldev_common.h new file mode 100644 index 0000000000000..f586609444202 --- /dev/null +++ b/drivers/net/wireless/bcmdhd/wldev_common.h @@ -0,0 +1,112 @@ +/* + * Common function shared by Linux WEXT, cfg80211 and p2p drivers + * + * Copyright (C) 1999-2011, Broadcom Corporation + * + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wldev_common.h,v 1.1.4.1.2.14 2011-02-09 01:40:07 $ + */ +#ifndef __WLDEV_COMMON_H__ +#define __WLDEV_COMMON_H__ + +#include + +/* wl_dev_ioctl - get/set IOCTLs, will call net_device's do_ioctl (or + * netdev_ops->ndo_do_ioctl in new kernels) + * @dev: the net_device handle + */ +s32 wldev_ioctl( + struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set); + +/** Retrieve named IOVARs, this function calls wl_dev_ioctl with + * WLC_GET_VAR IOCTL code + */ +s32 wldev_iovar_getbuf( + struct net_device *dev, s8 *iovar_name, + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync); + +/** Set named IOVARs, this function calls wl_dev_ioctl with + * WLC_SET_VAR IOCTL code + */ +s32 wldev_iovar_setbuf( + struct net_device *dev, s8 *iovar_name, + void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync); + +s32 wldev_iovar_setint( + struct net_device *dev, s8 *iovar, s32 val); + +s32 wldev_iovar_getint( + struct net_device *dev, s8 *iovar, s32 *pval); + +/** The following function can be implemented if there is a need for bsscfg + * indexed IOVARs + */ + +s32 wldev_mkiovar_bsscfg( + const s8 *iovar_name, s8 *param, s32 paramlen, + s8 *iovar_buf, s32 buflen, s32 bssidx); + +/** Retrieve named and bsscfg indexed IOVARs, this function calls wl_dev_ioctl with + * WLC_GET_VAR IOCTL code + */ +s32 wldev_iovar_getbuf_bsscfg( + struct net_device *dev, s8 *iovar_name, void *param, s32 paramlen, + void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync); + +/** Set named and bsscfg indexed IOVARs, this function calls wl_dev_ioctl with + * WLC_SET_VAR IOCTL code + */ +s32 wldev_iovar_setbuf_bsscfg( + struct net_device *dev, s8 *iovar_name, void *param, s32 paramlen, + void *buf, s32 buflen, s32 bsscfg_idx, struct mutex* buf_sync); + +s32 wldev_iovar_getint_bsscfg( + struct net_device *dev, s8 *iovar, s32 *pval, s32 bssidx); + +s32 wldev_iovar_setint_bsscfg( + struct net_device *dev, s8 *iovar, s32 val, s32 bssidx); + +extern void get_customized_country_code(char *country_iso_code, wl_country_t *cspec); +extern void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec); +extern int wldev_set_country(struct net_device *dev, char *country_code); +extern int net_os_wake_lock(struct net_device *dev); +extern int net_os_wake_unlock(struct net_device *dev); +extern int net_os_wake_lock_timeout(struct net_device *dev); +extern int net_os_wake_lock_timeout_enable(struct net_device *dev, int val); +extern int net_os_set_dtim_skip(struct net_device *dev, int val); +extern int net_os_set_suspend_disable(struct net_device *dev, int val); +extern int net_os_set_suspend(struct net_device *dev, int val, int force); +extern int wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_t* ssid, + int max, int *bytes_left); + +/* Get the link speed from dongle, speed is in kpbs */ +int wldev_get_link_speed(struct net_device *dev, int *plink_speed); + +int wldev_get_rssi(struct net_device *dev, int *prssi); + +int wldev_get_ssid(struct net_device *dev, wlc_ssid_t *pssid); + +int wldev_get_band(struct net_device *dev, uint *pband); + +int wldev_set_band(struct net_device *dev, uint band); + +int wldev_get_auto_channel(struct net_device *dev, int *chan); + +#endif /* __WLDEV_COMMON_H__ */ From 5225faf12004b4a8adbcb4d32bd0f91986a91a06 Mon Sep 17 00:00:00 2001 From: Chintan Pandya Date: Tue, 29 Nov 2011 13:11:40 +0530 Subject: [PATCH 2302/2556] rfkill: Fix compilation when PM is disabled Add config macros for RFKILL_PM to fix compilation when CONFIG_PM is disabled Change-Id: I9fba48fb02d671885665354091101f3813426f57 Signed-off-by: Chintan Pandya --- include/linux/rfkill.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index c6c608482cba4..3d049abf9ae61 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -199,8 +199,11 @@ void rfkill_pause_polling(struct rfkill *rfkill); * NOTE: not necessary for suspend/resume -- in that case the * core stops polling anyway */ +#ifdef CONFIG_RFKILL_PM void rfkill_resume_polling(struct rfkill *rfkill); - +#else +static inline void rfkill_resume_polling(struct rfkill *rfkill) { } +#endif /** * rfkill_unregister - Unregister a rfkill structure. From d700353c75800f6e7e8e38d94821f0f76ffa500f Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 13 May 2011 15:52:10 +0200 Subject: [PATCH 2303/2556] net/rfkill/core.c: Avoid leaving freed data in a list The list_for_each_entry loop can fail, in which case the list element is not removed from the list rfkill_fds. Since this list is not accessed by the loop, the addition of &data->list into the list is just moved after the loop. The sematic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression E,E1,E2; identifier l; @@ *list_add(&E->l,E1); ... when != E1 when != list_del(&E->l) when != list_del_init(&E->l) when != E = E2 *kfree(E);// Signed-off-by: Julia Lawall Signed-off-by: John W. Linville --- net/rfkill/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/rfkill/core.c b/net/rfkill/core.c index dafeaeb97bc32..df2dae6b27236 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -1028,7 +1028,6 @@ static int rfkill_fop_open(struct inode *inode, struct file *file) * start getting events from elsewhere but hold mtx to get * startup events added first */ - list_add(&data->list, &rfkill_fds); list_for_each_entry(rfkill, &rfkill_list, node) { ev = kzalloc(sizeof(*ev), GFP_KERNEL); @@ -1037,6 +1036,7 @@ static int rfkill_fop_open(struct inode *inode, struct file *file) rfkill_fill_event(&ev->ev, rfkill, RFKILL_OP_ADD); list_add_tail(&ev->list, &data->events); } + list_add(&data->list, &rfkill_fds); mutex_unlock(&data->mtx); mutex_unlock(&rfkill_global_mutex); From 61c912c1c65e3155409a11bd39d14d3cf5fd8884 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 27 Feb 2011 22:08:00 +0100 Subject: [PATCH 2304/2556] cfg80211: add a field for the bitrate of the last rx data packet from a station Also fix a typo in the STATION_INFO_TX_BITRATE description Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- include/linux/nl80211.h | 3 +++ include/net/cfg80211.h | 5 +++- net/wireless/nl80211.c | 56 +++++++++++++++++++++++++++-------------- 3 files changed, 44 insertions(+), 20 deletions(-) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 821ffb954f147..30022189104da 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1243,6 +1243,8 @@ enum nl80211_rate_info { * @NL80211_STA_INFO_LLID: the station's mesh LLID * @NL80211_STA_INFO_PLID: the station's mesh PLID * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station + * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested + * attribute, like NL80211_STA_INFO_TX_BITRATE. * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -1261,6 +1263,7 @@ enum nl80211_sta_info { NL80211_STA_INFO_TX_RETRIES, NL80211_STA_INFO_TX_FAILED, NL80211_STA_INFO_SIGNAL_AVG, + NL80211_STA_INFO_RX_BITRATE, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 1322695beb529..ae1b5dd6c4dbd 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -413,7 +413,7 @@ struct station_parameters { * @STATION_INFO_PLID: @plid filled * @STATION_INFO_PLINK_STATE: @plink_state filled * @STATION_INFO_SIGNAL: @signal filled - * @STATION_INFO_TX_BITRATE: @tx_bitrate fields are filled + * @STATION_INFO_TX_BITRATE: @txrate fields are filled * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs) * @STATION_INFO_RX_PACKETS: @rx_packets filled * @STATION_INFO_TX_PACKETS: @tx_packets filled @@ -421,6 +421,7 @@ struct station_parameters { * @STATION_INFO_TX_FAILED: @tx_failed filled * @STATION_INFO_RX_DROP_MISC: @rx_dropped_misc filled * @STATION_INFO_SIGNAL_AVG: @signal_avg filled + * @STATION_INFO_RX_BITRATE: @rxrate fields are filled */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, @@ -437,6 +438,7 @@ enum station_info_flags { STATION_INFO_TX_FAILED = 1<<11, STATION_INFO_RX_DROP_MISC = 1<<12, STATION_INFO_SIGNAL_AVG = 1<<13, + STATION_INFO_RX_BITRATE = 1<<14, }; /** @@ -506,6 +508,7 @@ struct station_info { s8 signal; s8 signal_avg; struct rate_info txrate; + struct rate_info rxrate; u32 rx_packets; u32 tx_packets; u32 tx_retries; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 47d9a875612db..2de50279e5f4c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1960,13 +1960,41 @@ static int parse_station_flags(struct genl_info *info, return 0; } +static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, + int attr) +{ + struct nlattr *rate; + u16 bitrate; + + rate = nla_nest_start(msg, attr); + if (!rate) + goto nla_put_failure; + + /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ + bitrate = cfg80211_calculate_bitrate(info); + if (bitrate > 0) + NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); + + if (info->flags & RATE_INFO_FLAGS_MCS) + NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS, info->mcs); + if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) + NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH); + if (info->flags & RATE_INFO_FLAGS_SHORT_GI) + NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI); + + nla_nest_end(msg, rate); + return true; + +nla_put_failure: + return false; +} + static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct net_device *dev, const u8 *mac_addr, struct station_info *sinfo) { void *hdr; - struct nlattr *sinfoattr, *txrate; - u16 bitrate; + struct nlattr *sinfoattr; hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); if (!hdr) @@ -2005,24 +2033,14 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG, sinfo->signal_avg); if (sinfo->filled & STATION_INFO_TX_BITRATE) { - txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE); - if (!txrate) + if (!nl80211_put_sta_rate(msg, &sinfo->txrate, + NL80211_STA_INFO_TX_BITRATE)) + goto nla_put_failure; + } + if (sinfo->filled & STATION_INFO_RX_BITRATE) { + if (!nl80211_put_sta_rate(msg, &sinfo->rxrate, + NL80211_STA_INFO_RX_BITRATE)) goto nla_put_failure; - - /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ - bitrate = cfg80211_calculate_bitrate(&sinfo->txrate); - if (bitrate > 0) - NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); - - if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS) - NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS, - sinfo->txrate.mcs); - if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) - NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH); - if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI) - NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI); - - nla_nest_end(msg, txrate); } if (sinfo->filled & STATION_INFO_RX_PACKETS) NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS, From cdd5e5ad9d82681d55fa4a09c28dbe9118ba909f Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 27 Feb 2011 22:08:01 +0100 Subject: [PATCH 2305/2556] mac80211: add support for showing the last rx bitrate Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- net/mac80211/cfg.c | 31 ++++++++++++++++++++++--------- net/mac80211/rx.c | 11 ++++++++++- net/mac80211/sta_info.h | 4 ++++ 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index c35dcf79a52da..86a52052665b6 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -316,6 +316,17 @@ static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy, return 0; } +static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, int idx) +{ + if (!(rate->flags & RATE_INFO_FLAGS_MCS)) { + struct ieee80211_supported_band *sband; + sband = sta->local->hw.wiphy->bands[ + sta->local->hw.conf.channel->band]; + rate->legacy = sband->bitrates[idx].bitrate; + } else + rate->mcs = idx; +} + static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = sta->sdata; @@ -330,6 +341,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) STATION_INFO_TX_RETRIES | STATION_INFO_TX_FAILED | STATION_INFO_TX_BITRATE | + STATION_INFO_RX_BITRATE | STATION_INFO_RX_DROP_MISC; sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); @@ -355,15 +367,16 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; if (sta->last_tx_rate.flags & IEEE80211_TX_RC_SHORT_GI) sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; - - if (!(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)) { - struct ieee80211_supported_band *sband; - sband = sta->local->hw.wiphy->bands[ - sta->local->hw.conf.channel->band]; - sinfo->txrate.legacy = - sband->bitrates[sta->last_tx_rate.idx].bitrate; - } else - sinfo->txrate.mcs = sta->last_tx_rate.idx; + rate_idx_to_bitrate(&sinfo->txrate, sta, sta->last_tx_rate.idx); + + sinfo->rxrate.flags = 0; + if (sta->last_rx_rate_flag & RX_FLAG_HT) + sinfo->rxrate.flags |= RATE_INFO_FLAGS_MCS; + if (sta->last_rx_rate_flag & RX_FLAG_40MHZ) + sinfo->rxrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI) + sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + rate_idx_to_bitrate(&sinfo->rxrate, sta, sta->last_rx_rate_idx); if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index a6701ed87f0d1..b3c0c304c9bf4 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1136,14 +1136,23 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) if (rx->sdata->vif.type == NL80211_IFTYPE_ADHOC) { u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len, NL80211_IFTYPE_ADHOC); - if (compare_ether_addr(bssid, rx->sdata->u.ibss.bssid) == 0) + if (compare_ether_addr(bssid, rx->sdata->u.ibss.bssid) == 0) { sta->last_rx = jiffies; + if (ieee80211_is_data(hdr->frame_control)) { + sta->last_rx_rate_idx = status->rate_idx; + sta->last_rx_rate_flag = status->flag; + } + } } else if (!is_multicast_ether_addr(hdr->addr1)) { /* * Mesh beacons will update last_rx when if they are found to * match the current local configuration when processed. */ sta->last_rx = jiffies; + if (ieee80211_is_data(hdr->frame_control)) { + sta->last_rx_rate_idx = status->rate_idx; + sta->last_rx_rate_flag = status->flag; + } } if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index bbdd2a86a94b2..b077fc46d4775 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -207,6 +207,8 @@ enum plink_state { * @rate_ctrl_priv: rate control private per-STA pointer * @last_tx_rate: rate used for last transmit, to report to userspace as * "the" transmit rate + * @last_rx_rate_idx: rx status rate index of the last data packet + * @last_rx_rate_flag: rx status flag of the last data packet * @lock: used for locking all fields that require locking, see comments * in the header file. * @flaglock: spinlock for flags accesses @@ -309,6 +311,8 @@ struct sta_info { unsigned long tx_bytes; unsigned long tx_fragments; struct ieee80211_tx_rate last_tx_rate; + int last_rx_rate_idx; + int last_rx_rate_flag; u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1]; /* From 917b30af169d9fadc7ec467e64f0ff83409353aa Mon Sep 17 00:00:00 2001 From: Paul Stewart Date: Thu, 31 Mar 2011 09:25:41 -0700 Subject: [PATCH 2306/2556] nl80211: Add BSS parameters to station This allows user-space monitoring of BSS parameters for the associated station. This is useful for debugging and verifying that the paramaters are as expected. [Exactly the same as before but bundled into a single message] Signed-off-by: Paul Stewart Cc: Johannes Berg Signed-off-by: John W. Linville --- include/linux/nl80211.h | 35 ++++++++++++++++++++++++++++++++++- include/net/cfg80211.h | 34 ++++++++++++++++++++++++++++++++++ net/mac80211/cfg.c | 13 ++++++++++++- net/wireless/nl80211.c | 21 ++++++++++++++++++++- 4 files changed, 100 insertions(+), 3 deletions(-) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 30022189104da..16eea7229e994 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1221,6 +1221,36 @@ enum nl80211_rate_info { NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1 }; +/** + * enum nl80211_sta_bss_param - BSS information collected by STA + * + * These attribute types are used with %NL80211_STA_INFO_BSS_PARAM + * when getting information about the bitrate of a station. + * + * @__NL80211_STA_BSS_PARAM_INVALID: attribute number 0 is reserved + * @NL80211_STA_BSS_PARAM_CTS_PROT: whether CTS protection is enabled (flag) + * @NL80211_STA_BSS_PARAM_SHORT_PREAMBLE: whether short preamble is enabled + * (flag) + * @NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME: whether short slot time is enabled + * (flag) + * @NL80211_STA_BSS_PARAM_DTIM_PERIOD: DTIM period for beaconing (u8) + * @NL80211_STA_BSS_PARAM_BEACON_INTERVAL: Beacon interval (u16) + * @NL80211_STA_BSS_PARAM_MAX: highest sta_bss_param number currently defined + * @__NL80211_STA_BSS_PARAM_AFTER_LAST: internal use + */ +enum nl80211_sta_bss_param { + __NL80211_STA_BSS_PARAM_INVALID, + NL80211_STA_BSS_PARAM_CTS_PROT, + NL80211_STA_BSS_PARAM_SHORT_PREAMBLE, + NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME, + NL80211_STA_BSS_PARAM_DTIM_PERIOD, + NL80211_STA_BSS_PARAM_BEACON_INTERVAL, + + /* keep last */ + __NL80211_STA_BSS_PARAM_AFTER_LAST, + NL80211_STA_BSS_PARAM_MAX = __NL80211_STA_BSS_PARAM_AFTER_LAST - 1 +}; + /** * enum nl80211_sta_info - station information * @@ -1233,7 +1263,7 @@ enum nl80211_rate_info { * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station) * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute - * containing info as possible, see &enum nl80211_sta_info_txrate. + * containing info as possible, see &enum nl80211_rate_info * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station) * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this * station) @@ -1245,6 +1275,8 @@ enum nl80211_rate_info { * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested * attribute, like NL80211_STA_INFO_TX_BITRATE. + * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute + * containing info as possible, see &enum nl80211_sta_bss_param * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -1264,6 +1296,7 @@ enum nl80211_sta_info { NL80211_STA_INFO_TX_FAILED, NL80211_STA_INFO_SIGNAL_AVG, NL80211_STA_INFO_RX_BITRATE, + NL80211_STA_INFO_BSS_PARAM, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ae1b5dd6c4dbd..f8b169d4f7b1f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -422,6 +422,7 @@ struct station_parameters { * @STATION_INFO_RX_DROP_MISC: @rx_dropped_misc filled * @STATION_INFO_SIGNAL_AVG: @signal_avg filled * @STATION_INFO_RX_BITRATE: @rxrate fields are filled + * @STATION_INFO_BSS_PARAM: @bss_param filled */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, @@ -439,6 +440,7 @@ enum station_info_flags { STATION_INFO_RX_DROP_MISC = 1<<12, STATION_INFO_SIGNAL_AVG = 1<<13, STATION_INFO_RX_BITRATE = 1<<14, + STATION_INFO_BSS_PARAM = 1<<15, }; /** @@ -472,6 +474,37 @@ struct rate_info { u16 legacy; }; +/** + * enum station_info_rate_flags - bitrate info flags + * + * Used by the driver to indicate the specific rate transmission + * type for 802.11n transmissions. + * + * @BSS_PARAM_FLAGS_CTS_PROT: whether CTS protection is enabled + * @BSS_PARAM_FLAGS_SHORT_PREAMBLE: whether short preamble is enabled + * @BSS_PARAM_FLAGS_SHORT_SLOT_TIME: whether short slot time is enabled + */ +enum bss_param_flags { + BSS_PARAM_FLAGS_CTS_PROT = 1<<0, + BSS_PARAM_FLAGS_SHORT_PREAMBLE = 1<<1, + BSS_PARAM_FLAGS_SHORT_SLOT_TIME = 1<<2, +}; + +/** + * struct sta_bss_parameters - BSS parameters for the attached station + * + * Information about the currently associated BSS + * + * @flags: bitflag of flags from &enum bss_param_flags + * @dtim_period: DTIM period for the BSS + * @beacon_interval: beacon interval + */ +struct sta_bss_parameters { + u8 flags; + u8 dtim_period; + u16 beacon_interval; +}; + /** * struct station_info - station information * @@ -514,6 +547,7 @@ struct station_info { u32 tx_retries; u32 tx_failed; u32 rx_dropped_misc; + struct sta_bss_parameters bss_param; int generation; }; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 86a52052665b6..d8dcf4cbc054a 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -342,7 +342,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) STATION_INFO_TX_FAILED | STATION_INFO_TX_BITRATE | STATION_INFO_RX_BITRATE | - STATION_INFO_RX_DROP_MISC; + STATION_INFO_RX_DROP_MISC | + STATION_INFO_BSS_PARAM; sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); sinfo->rx_bytes = sta->rx_bytes; @@ -389,6 +390,16 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->plink_state = sta->plink_state; #endif } + + sinfo->bss_param.flags = 0; + if (sdata->vif.bss_conf.use_cts_prot) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT; + if (sdata->vif.bss_conf.use_short_preamble) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; + if (sdata->vif.bss_conf.use_short_slot) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; + sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period; + sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int; } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 2de50279e5f4c..aee1c9f458767 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1994,7 +1994,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, const u8 *mac_addr, struct station_info *sinfo) { void *hdr; - struct nlattr *sinfoattr; + struct nlattr *sinfoattr, *bss_param; hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); if (!hdr) @@ -2054,6 +2054,25 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, if (sinfo->filled & STATION_INFO_TX_FAILED) NLA_PUT_U32(msg, NL80211_STA_INFO_TX_FAILED, sinfo->tx_failed); + if (sinfo->filled & STATION_INFO_BSS_PARAM) { + bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM); + if (!bss_param) + goto nla_put_failure; + + if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT) + NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_CTS_PROT); + if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE) + NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE); + if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME) + NLA_PUT_FLAG(msg, + NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME); + NLA_PUT_U8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD, + sinfo->bss_param.dtim_period); + NLA_PUT_U16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL, + sinfo->bss_param.beacon_interval); + + nla_nest_end(msg, bss_param); + } nla_nest_end(msg, sinfoattr); return genlmsg_end(msg, hdr); From 7a404910907d4a99b570914e09023a86cd9af373 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Fri, 8 Apr 2011 21:24:24 +0530 Subject: [PATCH 2307/2556] {mac|nl}80211: Add station connected time Add station connected time in debugfs. This will be helpful to get a measure of stability of the connection and for debugging stress issues Cc: Senthilkumar Balasubramanian Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville --- include/linux/nl80211.h | 2 ++ include/net/cfg80211.h | 4 ++++ net/mac80211/cfg.c | 7 ++++++- net/mac80211/debugfs_sta.c | 26 ++++++++++++++++++++++++++ net/mac80211/sta_info.c | 3 +++ net/mac80211/sta_info.h | 2 ++ net/wireless/nl80211.c | 3 +++ 7 files changed, 46 insertions(+), 1 deletion(-) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 16eea7229e994..ad7fc6b818963 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1277,6 +1277,7 @@ enum nl80211_sta_bss_param { * attribute, like NL80211_STA_INFO_TX_BITRATE. * @NL80211_STA_INFO_BSS_PARAM: current station's view of BSS, nested attribute * containing info as possible, see &enum nl80211_sta_bss_param + * @NL80211_STA_INFO_CONNECTED_TIME: time since the station is last connected * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -1297,6 +1298,7 @@ enum nl80211_sta_info { NL80211_STA_INFO_SIGNAL_AVG, NL80211_STA_INFO_RX_BITRATE, NL80211_STA_INFO_BSS_PARAM, + NL80211_STA_INFO_CONNECTED_TIME, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f8b169d4f7b1f..49643b6993b5a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -423,6 +423,7 @@ struct station_parameters { * @STATION_INFO_SIGNAL_AVG: @signal_avg filled * @STATION_INFO_RX_BITRATE: @rxrate fields are filled * @STATION_INFO_BSS_PARAM: @bss_param filled + * @STATION_INFO_CONNECTED_TIME: @connected_time filled */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, @@ -441,6 +442,7 @@ enum station_info_flags { STATION_INFO_SIGNAL_AVG = 1<<13, STATION_INFO_RX_BITRATE = 1<<14, STATION_INFO_BSS_PARAM = 1<<15, + STATION_INFO_CONNECTED_TIME = 1<<16 }; /** @@ -511,6 +513,7 @@ struct sta_bss_parameters { * Station information filled by driver for get_station() and dump_station. * * @filled: bitflag of flags from &enum station_info_flags + * @connected_time: time(in secs) since a station is last connected * @inactive_time: time since last station activity (tx/rx) in milliseconds * @rx_bytes: bytes received from this station * @tx_bytes: bytes transmitted to this station @@ -532,6 +535,7 @@ struct sta_bss_parameters { */ struct station_info { u32 filled; + u32 connected_time; u32 inactive_time; u32 rx_bytes; u32 tx_bytes; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d8dcf4cbc054a..77fae489e8b93 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -330,6 +330,7 @@ static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, in static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = sta->sdata; + struct timespec uptime; sinfo->generation = sdata->local->sta_generation; @@ -343,7 +344,11 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) STATION_INFO_TX_BITRATE | STATION_INFO_RX_BITRATE | STATION_INFO_RX_DROP_MISC | - STATION_INFO_BSS_PARAM; + STATION_INFO_BSS_PARAM | + STATION_INFO_CONNECTED_TIME; + + do_posix_clock_monotonic_gettime(&uptime); + sinfo->connected_time = uptime.tv_sec - sta->last_connected; sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); sinfo->rx_bytes = sta->rx_bytes; diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index c04a1396cf8d9..c008232731eb0 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -92,6 +92,31 @@ static ssize_t sta_inactive_ms_read(struct file *file, char __user *userbuf, } STA_OPS(inactive_ms); + +static ssize_t sta_connected_time_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct sta_info *sta = file->private_data; + struct timespec uptime; + struct tm result; + long connected_time_secs; + char buf[100]; + int res; + do_posix_clock_monotonic_gettime(&uptime); + connected_time_secs = uptime.tv_sec - sta->last_connected; + time_to_tm(connected_time_secs, 0, &result); + result.tm_year -= 70; + result.tm_mday -= 1; + res = scnprintf(buf, sizeof(buf), + "years - %d\nmonths - %d\ndays - %d\nclock - %d:%d:%d\n\n", + result.tm_year, result.tm_mon, result.tm_mday, + result.tm_hour, result.tm_min, result.tm_sec); + return simple_read_from_buffer(userbuf, count, ppos, buf, res); +} +STA_OPS(connected_time); + + + static ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { @@ -324,6 +349,7 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) DEBUGFS_ADD(flags); DEBUGFS_ADD(num_ps_buf_frames); DEBUGFS_ADD(inactive_ms); + DEBUGFS_ADD(connected_time); DEBUGFS_ADD(last_seq_ctrl); DEBUGFS_ADD(agg_status); DEBUGFS_ADD(dev); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 604216e2ee3d4..87a5db98bee1b 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -228,6 +228,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct sta_info *sta; + struct timespec uptime; int i; sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp); @@ -245,6 +246,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, sta->sdata = sdata; sta->last_rx = jiffies; + do_posix_clock_monotonic_gettime(&uptime); + sta->last_connected = uptime.tv_sec; ewma_init(&sta->avg_signal, 1024, 8); if (sta_prepare_rate_control(local, sta, gfp)) { diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index b077fc46d4775..cb7809ef9d2e4 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -224,6 +224,7 @@ enum plink_state { * @rx_bytes: Number of bytes received from this STA * @wep_weak_iv_count: number of weak WEP IVs received from this station * @last_rx: time (in jiffies) when last frame was received from this STA + * @last_connected: time (in seconds) when a station got connected * @num_duplicates: number of duplicate frames received from this STA * @rx_fragments: number of received MPDUs * @rx_dropped: number of dropped MPDUs from this STA @@ -293,6 +294,7 @@ struct sta_info { unsigned long rx_packets, rx_bytes; unsigned long wep_weak_iv_count; unsigned long last_rx; + long last_connected; unsigned long num_duplicates; unsigned long rx_fragments; unsigned long rx_dropped; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index aee1c9f458767..8923ddad0dd23 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2008,6 +2008,9 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO); if (!sinfoattr) goto nla_put_failure; + if (sinfo->filled & STATION_INFO_CONNECTED_TIME) + NLA_PUT_U32(msg, NL80211_STA_INFO_CONNECTED_TIME, + sinfo->connected_time); if (sinfo->filled & STATION_INFO_INACTIVE_TIME) NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME, sinfo->inactive_time); From 7dd3f99f0679d8772c85e5cfb0cde691f776d94d Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 8 Aug 2011 12:11:52 +0300 Subject: [PATCH 2308/2556] cfg80211/nl80211: Send AssocReq IEs to user space in AP mode When user space SME/MLME (e.g., hostapd) is not used in AP mode, the IEs from the (Re)Association Request frame that was processed in firmware need to be made available for user space (e.g., RSN IE for hostapd). Allow this to be done with cfg80211_new_sta(). (cherry picked from commit d692df224b8605095cb7f770c1c99d3150834daf from android.googlesource.com/common.git) Signed-off-by: Jouni Malinen Acked-by: Johannes Berg Signed-off-by: John W. Linville Signed-off-by: Dmitry Shmidt Signed-off-by: Varun Wadekar Change-Id: I211171ca4953832cc998402c149ecf3fc429f9e6 Reviewed-on: http://git-master/r/78886 Reviewed-by: Automatic_Commit_Validation_User --- include/net/cfg80211.h | 8 ++++++++ net/wireless/nl80211.c | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 49643b6993b5a..406dd64883b48 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -532,6 +532,11 @@ struct sta_bss_parameters { * This number should increase every time the list of stations * changes, i.e. when a station is added or removed, so that * userspace can tell whether it got a consistent snapshot. + * @assoc_req_ies: IEs from (Re)Association Request. + * This is used only when in AP mode with drivers that do not use + * user space MLME/SME implementation. The information is provided for + * the cfg80211_new_sta() calls to notify user space of the IEs. + * @assoc_req_ies_len: Length of assoc_req_ies buffer in octets. */ struct station_info { u32 filled; @@ -554,6 +559,9 @@ struct station_info { struct sta_bss_parameters bss_param; int generation; + + const u8 *assoc_req_ies; + size_t assoc_req_ies_len; }; /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8923ddad0dd23..ff85b9e7aa923 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2078,6 +2078,10 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, } nla_nest_end(msg, sinfoattr); + if (sinfo->assoc_req_ies) + NLA_PUT(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len, + sinfo->assoc_req_ies); + return genlmsg_end(msg, hdr); nla_put_failure: From 07e39d4e4fb2b02220fae184c892769779232bbe Mon Sep 17 00:00:00 2001 From: jim1_lin Date: Thu, 3 May 2012 14:04:13 -0700 Subject: [PATCH 2309/2556] cfg80211: fix a crash in nl80211_send_station mac80211 leaves sinfo->assoc_req_ies uninitialized, causing a random pointer memory access in nl80211_send_station. Instead of checking if the pointer is null, use sinfo->filled, like the rest of the fields. Change-Id: I312a466f8c631ad64a16ecf191fa8046fe658cc1 Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville Reviewed-on: http://mcrd1-5.corpnet.asus/code-review/master/48179 Reviewed-by: Jim1 Lin Tested-by: Jim1 Lin Reviewed-by: Leslie Yu --- include/net/cfg80211.h | 4 +++- net/wireless/nl80211.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 406dd64883b48..c7b33beea1724 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -424,6 +424,7 @@ struct station_parameters { * @STATION_INFO_RX_BITRATE: @rxrate fields are filled * @STATION_INFO_BSS_PARAM: @bss_param filled * @STATION_INFO_CONNECTED_TIME: @connected_time filled + * @STATION_INFO_ASSOC_REQ_IES: @assoc_req_ies filled */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, @@ -442,7 +443,8 @@ enum station_info_flags { STATION_INFO_SIGNAL_AVG = 1<<13, STATION_INFO_RX_BITRATE = 1<<14, STATION_INFO_BSS_PARAM = 1<<15, - STATION_INFO_CONNECTED_TIME = 1<<16 + STATION_INFO_CONNECTED_TIME = 1<<16, + STATION_INFO_ASSOC_REQ_IES = 1<<17 }; /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ff85b9e7aa923..277be83b03bdc 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -2078,7 +2078,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, } nla_nest_end(msg, sinfoattr); - if (sinfo->assoc_req_ies) + if (sinfo->filled & STATION_INFO_ASSOC_REQ_IES) NLA_PUT(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len, sinfo->assoc_req_ies); From 6812878d08fc615e42079523f586297546ebdd1e Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 23 Mar 2011 15:29:52 +0200 Subject: [PATCH 2310/2556] cfg80211: Add nl80211 event for deletion of a station entry Indicate an NL80211_CMD_DEL_STATION event when a station entry in mac80211 is deleted to match with the NL80211_CMD_NEW_STATION event that is used when the entry was added. This is needed, e.g., to allow user space to remove a peer from RSN IBSS Authenticator state machine to avoid re-authentication and re-keying delays when the peer is not reachable anymore. Signed-off-by: Jouni Malinen Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/cfg80211.h | 9 +++++++++ net/mac80211/sta_info.c | 2 ++ net/wireless/mlme.c | 9 +++++++++ net/wireless/nl80211.c | 34 ++++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 3 +++ 5 files changed, 57 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c7b33beea1724..981c92f031bdf 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2704,6 +2704,15 @@ void cfg80211_remain_on_channel_expired(struct net_device *dev, void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, struct station_info *sinfo, gfp_t gfp); +/** + * cfg80211_del_sta - notify userspace about deletion of a station + * + * @dev: the netdev + * @mac_addr: the station's address + * @gfp: allocation flags + */ +void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp); + /** * cfg80211_rx_mgmt - notification of received, unprocessed management frame * @dev: network device diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 87a5db98bee1b..61b0f14f60695 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -701,6 +701,8 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ cancel_work_sync(&sta->drv_unblock_wk); + cfg80211_del_sta(sdata->dev, sta->sta.addr, GFP_KERNEL); + rate_control_remove_sta_debugfs(sta); ieee80211_sta_debugfs_remove(sta); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index aa5df8865ff75..16881fea4ce6e 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -770,6 +770,15 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, } EXPORT_SYMBOL(cfg80211_new_sta); +void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp) +{ + struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_send_sta_del_event(rdev, dev, mac_addr, gfp); +} +EXPORT_SYMBOL(cfg80211_del_sta); + struct cfg80211_mgmt_registration { struct list_head list; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 277be83b03bdc..bbf42de6be527 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5982,6 +5982,40 @@ void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, nl80211_mlme_mcgrp.id, gfp); } +void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *mac_addr, + gfp_t gfp) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, struct net_device *netdev, u32 nlpid, int freq, const u8 *buf, size_t len, gfp_t gfp) diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index e3f7fa886966e..dcac5cd6f0178 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -79,6 +79,9 @@ void nl80211_send_remain_on_channel_cancel( void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *mac_addr, struct station_info *sinfo, gfp_t gfp); +void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *mac_addr, + gfp_t gfp); int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, struct net_device *netdev, u32 nlpid, int freq, From d897c9f142a312f48811e3bd1beea2f607179466 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 15 Jul 2012 21:51:53 -0500 Subject: [PATCH 2311/2556] Revert "SLAB: Record actual last user of freed objects." This reverts commit 4eb1a6cba9700bcd533975c066134b26b7346fba. --- mm/slab.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/mm/slab.c b/mm/slab.c index 37f77ef4648bf..568803f121a87 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -3603,14 +3603,13 @@ static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac) * Release an obj back to its cache. If the obj has a constructed state, it must * be in this state _before_ it is released. Called with disabled ints. */ -static inline void __cache_free(struct kmem_cache *cachep, void *objp, - void *caller) +static inline void __cache_free(struct kmem_cache *cachep, void *objp) { struct array_cache *ac = cpu_cache_get(cachep); check_irq_off(); kmemleak_free_recursive(objp, cachep->flags); - objp = cache_free_debugcheck(cachep, objp, caller); + objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0)); kmemcheck_slab_free(cachep, objp, obj_size(cachep)); @@ -3801,7 +3800,7 @@ void kmem_cache_free(struct kmem_cache *cachep, void *objp) debug_check_no_locks_freed(objp, obj_size(cachep)); if (!(cachep->flags & SLAB_DEBUG_OBJECTS)) debug_check_no_obj_freed(objp, obj_size(cachep)); - __cache_free(cachep, objp, __builtin_return_address(0)); + __cache_free(cachep, objp); local_irq_restore(flags); trace_kmem_cache_free(_RET_IP_, objp); @@ -3831,7 +3830,7 @@ void kfree(const void *objp) c = virt_to_cache(objp); debug_check_no_locks_freed(objp, obj_size(c)); debug_check_no_obj_freed(objp, obj_size(c)); - __cache_free(c, (void *)objp, __builtin_return_address(0)); + __cache_free(c, (void *)objp); local_irq_restore(flags); } EXPORT_SYMBOL(kfree); From 3bc8d9095abe84b258d119165264938cfa7af50c Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 15 Jul 2012 21:52:51 -0500 Subject: [PATCH 2312/2556] Revert "mm: notifier_from_errno() cleanup" This reverts commit bbd2e7cdfcf5376b729daee617d3ee04d5928095. --- mm/page_cgroup.c | 7 ++++++- mm/slab.c | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/mm/page_cgroup.c b/mm/page_cgroup.c index 59a3cd4c799d5..5bffada7cde17 100644 --- a/mm/page_cgroup.c +++ b/mm/page_cgroup.c @@ -243,7 +243,12 @@ static int __meminit page_cgroup_callback(struct notifier_block *self, break; } - return notifier_from_errno(ret); + if (ret) + ret = notifier_from_errno(ret); + else + ret = NOTIFY_OK; + + return ret; } #endif diff --git a/mm/slab.c b/mm/slab.c index 568803f121a87..a18ba57517af4 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1390,7 +1390,7 @@ static int __meminit slab_memory_callback(struct notifier_block *self, break; } out: - return notifier_from_errno(ret); + return ret ? notifier_from_errno(ret) : NOTIFY_OK; } #endif /* CONFIG_NUMA && CONFIG_MEMORY_HOTPLUG */ From eece673b67961ee258969cf70822b39733fafc31 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 15 Jul 2012 21:52:15 -0500 Subject: [PATCH 2313/2556] Revert "slab,rcu: don't assume the size of struct rcu_head" This reverts commit dec5bf8fa41cd290ff689c813b2a6e0dd17487f1. --- mm/slab.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/mm/slab.c b/mm/slab.c index a18ba57517af4..2d5b92c09ae3b 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -190,6 +190,22 @@ typedef unsigned int kmem_bufctl_t; #define BUFCTL_ACTIVE (((kmem_bufctl_t)(~0U))-2) #define SLAB_LIMIT (((kmem_bufctl_t)(~0U))-3) +/* + * struct slab + * + * Manages the objs in a slab. Placed either at the beginning of mem allocated + * for a slab, or allocated from an general cache. + * Slabs are chained into three list: fully used, partial, fully free slabs. + */ +struct slab { + struct list_head list; + unsigned long colouroff; + void *s_mem; /* including colour offset */ + unsigned int inuse; /* num of objs active in slab */ + kmem_bufctl_t free; + unsigned short nodeid; +}; + /* * struct slab_rcu * @@ -203,6 +219,8 @@ typedef unsigned int kmem_bufctl_t; * * rcu_read_lock before reading the address, then rcu_read_unlock after * taking the spinlock within the structure expected at that address. + * + * We assume struct slab_rcu can overlay struct slab when destroying. */ struct slab_rcu { struct rcu_head head; @@ -210,27 +228,6 @@ struct slab_rcu { void *addr; }; -/* - * struct slab - * - * Manages the objs in a slab. Placed either at the beginning of mem allocated - * for a slab, or allocated from an general cache. - * Slabs are chained into three list: fully used, partial, fully free slabs. - */ -struct slab { - union { - struct { - struct list_head list; - unsigned long colouroff; - void *s_mem; /* including colour offset */ - unsigned int inuse; /* num of objs active in slab */ - kmem_bufctl_t free; - unsigned short nodeid; - }; - struct slab_rcu __slab_cover_slab_rcu; - }; -}; - /* * struct array_cache * From 2739a74077e4daa20e0c6d01f7909ff775337040 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 15 Jul 2012 21:52:27 -0500 Subject: [PATCH 2314/2556] Revert "mm: Remove support for kmem_cache_name()" This reverts commit 59ecae883edb69fe13dbacbb458ca4e026bb71cc. --- include/linux/slab.h | 1 + mm/slab.c | 8 ++++++++ mm/slob.c | 6 ++++++ mm/slub.c | 6 ++++++ 4 files changed, 21 insertions(+) diff --git a/include/linux/slab.h b/include/linux/slab.h index ad4dd1c8d30aa..fa9086647eb7a 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -105,6 +105,7 @@ void kmem_cache_destroy(struct kmem_cache *); int kmem_cache_shrink(struct kmem_cache *); void kmem_cache_free(struct kmem_cache *, void *); unsigned int kmem_cache_size(struct kmem_cache *); +const char *kmem_cache_name(struct kmem_cache *); /* * Please use this macro to create slab caches. Simply specify the diff --git a/mm/slab.c b/mm/slab.c index 2d5b92c09ae3b..4c6e2e31ced05 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -2147,6 +2147,8 @@ static int __init_refok setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp) * * @name must be valid until the cache is destroyed. This implies that * the module calling this has to destroy the cache before getting unloaded. + * Note that kmem_cache_name() is not guaranteed to return the same pointer, + * therefore applications must manage it themselves. * * The flags are * @@ -3838,6 +3840,12 @@ unsigned int kmem_cache_size(struct kmem_cache *cachep) } EXPORT_SYMBOL(kmem_cache_size); +const char *kmem_cache_name(struct kmem_cache *cachep) +{ + return cachep->name; +} +EXPORT_SYMBOL_GPL(kmem_cache_name); + /* * This initializes kmem_list3 or resizes various caches for all nodes. */ diff --git a/mm/slob.c b/mm/slob.c index 46e0aee33a235..3588eaaef7267 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -666,6 +666,12 @@ unsigned int kmem_cache_size(struct kmem_cache *c) } EXPORT_SYMBOL(kmem_cache_size); +const char *kmem_cache_name(struct kmem_cache *c) +{ + return c->name; +} +EXPORT_SYMBOL(kmem_cache_name); + int kmem_cache_shrink(struct kmem_cache *d) { return 0; diff --git a/mm/slub.c b/mm/slub.c index d2f343a54bad2..e15aa7f193c97 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2399,6 +2399,12 @@ unsigned int kmem_cache_size(struct kmem_cache *s) } EXPORT_SYMBOL(kmem_cache_size); +const char *kmem_cache_name(struct kmem_cache *s) +{ + return s->name; +} +EXPORT_SYMBOL(kmem_cache_name); + static void list_slab_objects(struct kmem_cache *s, struct page *page, const char *text) { From b82ea02d31ed49771a469fab7fe89ca2dc6a832d Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 15 Jul 2012 21:52:38 -0500 Subject: [PATCH 2315/2556] Revert "sched: don't call task_group() many times in set_task_rq()" This reverts commit 8ebf6d58c3fee4cef28db1fb46a5eb5d7e8417a5. --- kernel/sched.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/kernel/sched.c b/kernel/sched.c index ca02ddd609300..3a2d949118185 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -620,18 +620,14 @@ static inline struct task_group *task_group(struct task_struct *p) /* Change a task's cfs_rq and parent entity if it moves across CPUs/groups */ static inline void set_task_rq(struct task_struct *p, unsigned int cpu) { -#if defined(CONFIG_FAIR_GROUP_SCHED) || defined(CONFIG_RT_GROUP_SCHED) - struct task_group *tg = task_group(p); -#endif - #ifdef CONFIG_FAIR_GROUP_SCHED - p->se.cfs_rq = tg->cfs_rq[cpu]; - p->se.parent = tg->se[cpu]; + p->se.cfs_rq = task_group(p)->cfs_rq[cpu]; + p->se.parent = task_group(p)->se[cpu]; #endif #ifdef CONFIG_RT_GROUP_SCHED - p->rt.rt_rq = tg->rt_rq[cpu]; - p->rt.parent = tg->rt_se[cpu]; + p->rt.rt_rq = task_group(p)->rt_rq[cpu]; + p->rt.parent = task_group(p)->rt_se[cpu]; #endif } From 113c3c77536f8b2bbc9c47efac13892536e3bd9f Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 15 Jul 2012 21:53:15 -0500 Subject: [PATCH 2316/2556] Revert "cpufreq: Export user_policy min/max" This reverts commit 71d4679b6c54b51671e07984325c81446f472f70. --- drivers/cpufreq/cpufreq.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index addbb0a7b82f0..efbc3cc4884b3 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -364,8 +364,6 @@ show_one(cpuinfo_transition_latency, cpuinfo.transition_latency); show_one(scaling_min_freq, min); show_one(scaling_max_freq, max); show_one(scaling_cur_freq, cur); -show_one(policy_min_freq, user_policy.min); -show_one(policy_max_freq, user_policy.max); static int __cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_policy *policy); @@ -584,8 +582,6 @@ cpufreq_freq_attr_rw(scaling_min_freq); cpufreq_freq_attr_rw(scaling_max_freq); cpufreq_freq_attr_rw(scaling_governor); cpufreq_freq_attr_rw(scaling_setspeed); -cpufreq_freq_attr_ro(policy_min_freq); -cpufreq_freq_attr_ro(policy_max_freq); static struct attribute *default_attrs[] = { &cpuinfo_min_freq.attr, @@ -599,8 +595,6 @@ static struct attribute *default_attrs[] = { &scaling_driver.attr, &scaling_available_governors.attr, &scaling_setspeed.attr, - &policy_min_freq.attr, - &policy_max_freq.attr, NULL }; From 80d799ed1d4a6959d8ad9eb02af827e33649cd2f Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 15 Jul 2012 21:53:35 -0500 Subject: [PATCH 2317/2556] Revert "[CPUFREQ] fix cpumask memory leak in acpi-cpufreq on cpu hotplug." This reverts commit fa6f74c1f01a5eb47c9207e4e169ed5d634443d8. --- arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c index 596d5dd32f415..4e04e12743887 100644 --- a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -759,7 +759,7 @@ static void __exit acpi_cpufreq_exit(void) cpufreq_unregister_driver(&acpi_cpufreq_driver); - free_acpi_perf_data(); + free_percpu(acpi_perf_data); } module_param(acpi_pstate_strict, uint, 0644); From 310fb69a84d7529e72a919c1c612f25da41e2029 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 15 Jul 2012 21:53:50 -0500 Subject: [PATCH 2318/2556] Revert "cpufreq: expose a cpufreq_quick_get_max routine" This reverts commit 43357614f25e6a048fb06a5451cb251c054e90fe. --- drivers/cpufreq/cpufreq.c | 20 -------------------- include/linux/cpufreq.h | 5 ----- 2 files changed, 25 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index efbc3cc4884b3..df27d08200f20 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1199,26 +1199,6 @@ unsigned int cpufreq_quick_get(unsigned int cpu) } EXPORT_SYMBOL(cpufreq_quick_get); -/** - * cpufreq_quick_get_max - get the max reported CPU frequency for this CPU - * @cpu: CPU number - * - * Just return the max possible frequency for a given CPU. - */ -unsigned int cpufreq_quick_get_max(unsigned int cpu) -{ - struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); - unsigned int ret_freq = 0; - - if (policy) { - ret_freq = policy->max; - cpufreq_cpu_put(policy); - } - - return ret_freq; -} -EXPORT_SYMBOL(cpufreq_quick_get_max); - static unsigned int __cpufreq_get(unsigned int cpu) { diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 7bfaee242afb4..31f9557e72b7b 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -321,16 +321,11 @@ static inline unsigned int cpufreq_get(unsigned int cpu) /* query the last known CPU freq (in kHz). If zero, cpufreq couldn't detect it */ #ifdef CONFIG_CPU_FREQ unsigned int cpufreq_quick_get(unsigned int cpu); -unsigned int cpufreq_quick_get_max(unsigned int cpu); #else static inline unsigned int cpufreq_quick_get(unsigned int cpu) { return 0; } -static inline unsigned int cpufreq_quick_get_max(unsigned int cpu) -{ - return 0; -} #endif From c360d87d0109cc55193a02c939b3b61d219cc968 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sat, 30 Jun 2012 11:46:57 -0500 Subject: [PATCH 2319/2556] msm: mahimahi: bcmdhd updates --- arch/arm/mach-msm/board-mahimahi-mmc.c | 4 ++++ arch/arm/mach-msm/board-mahimahi-wifi.c | 32 +++++++++++++++---------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/arch/arm/mach-msm/board-mahimahi-mmc.c b/arch/arm/mach-msm/board-mahimahi-mmc.c index de9463d580709..d4b4256ec17b0 100644 --- a/arch/arm/mach-msm/board-mahimahi-mmc.c +++ b/arch/arm/mach-msm/board-mahimahi-mmc.c @@ -204,6 +204,10 @@ static struct embedded_sdio_data mahimahi_wifi_emb_data = { .high_power = 1, .high_speed = 1, }, + .cis = { + .vendor = 0x02d0, + .device = 0x4329, + }, }; static int mahimahi_wifi_cd = 0; /* WIFI virtual 'card detect' status */ diff --git a/arch/arm/mach-msm/board-mahimahi-wifi.c b/arch/arm/mach-msm/board-mahimahi-wifi.c index 8cd24766b03c4..04c0bbeece98b 100644 --- a/arch/arm/mach-msm/board-mahimahi-wifi.c +++ b/arch/arm/mach-msm/board-mahimahi-wifi.c @@ -17,6 +17,8 @@ int mahimahi_wifi_power(int on); int mahimahi_wifi_reset(int on); int mahimahi_wifi_set_carddetect(int on); +#if defined(CONFIG_DHD_USE_STATIC_BUF) || defined(CONFIG_BCM4329_DHD_USE_STATIC_BUF) + #define PREALLOC_WLAN_NUMBER_OF_SECTIONS 4 #define PREALLOC_WLAN_NUMBER_OF_BUFFERS 160 #define PREALLOC_WLAN_SECTION_HEADER 24 @@ -52,9 +54,11 @@ static void *mahimahi_wifi_mem_prealloc(int section, unsigned long size) return NULL; return wifi_mem_array[section].mem_ptr; } +#endif int __init mahimahi_init_wifi_mem(void) { +#if defined(CONFIG_DHD_USE_STATIC_BUF) || defined(CONFIG_BCM4329_DHD_USE_STATIC_BUF) int i; for(i=0;( i < WLAN_SKB_BUF_NUM );i++) { @@ -69,15 +73,16 @@ int __init mahimahi_init_wifi_mem(void) if (wifi_mem_array[i].mem_ptr == NULL) return -ENOMEM; } +#endif return 0; } static struct resource mahimahi_wifi_resources[] = { [0] = { - .name = "bcm4329_wlan_irq", + .name = "bcmdhd_wlan_irq", .start = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_WIFI_IRQ), .end = MSM_GPIO_TO_INT(MAHIMAHI_GPIO_WIFI_IRQ), - .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE, + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE, }, }; @@ -85,17 +90,21 @@ static struct wifi_platform_data mahimahi_wifi_control = { .set_power = mahimahi_wifi_power, .set_reset = mahimahi_wifi_reset, .set_carddetect = mahimahi_wifi_set_carddetect, +#if defined(CONFIG_DHD_USE_STATIC_BUF) || defined(CONFIG_BCM4329_DHD_USE_STATIC_BUF) .mem_prealloc = mahimahi_wifi_mem_prealloc, +#else + .mem_prealloc = NULL, +#endif }; static struct platform_device mahimahi_wifi_device = { - .name = "bcm4329_wlan", - .id = 1, - .num_resources = ARRAY_SIZE(mahimahi_wifi_resources), - .resource = mahimahi_wifi_resources, - .dev = { - .platform_data = &mahimahi_wifi_control, - }, + .name = "bcmdhd_wlan", + .id = 1, + .num_resources = ARRAY_SIZE(mahimahi_wifi_resources), + .resource = mahimahi_wifi_resources, + .dev = { + .platform_data = &mahimahi_wifi_control, + }, }; extern unsigned char *get_wifi_nvs_ram(void); @@ -130,8 +139,6 @@ static unsigned mahimahi_wifi_update_nvs(char *str, int add_flag) static int __init mahimahi_wifi_init(void) { - int ret; - if (!machine_is_mahimahi()) return 0; @@ -139,8 +146,7 @@ static int __init mahimahi_wifi_init(void) mahimahi_wifi_update_nvs("sd_oobonly=1\r\n", 0); mahimahi_wifi_update_nvs("btc_params70=0x32\r\n", 1); mahimahi_init_wifi_mem(); - ret = platform_device_register(&mahimahi_wifi_device); - return ret; + return platform_device_register(&mahimahi_wifi_device); } late_initcall(mahimahi_wifi_init); From 91c1d53e15a6a31bece719aacb99b72c1462d1f7 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 15 Jul 2012 21:58:46 -0500 Subject: [PATCH 2320/2556] msm: supersonic: bcmdhd updates --- arch/arm/mach-msm/board-supersonic-mmc.c | 4 ++++ arch/arm/mach-msm/board-supersonic-wifi.c | 18 ++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-msm/board-supersonic-mmc.c b/arch/arm/mach-msm/board-supersonic-mmc.c index fa35e0adb29cb..75a7041eaa5ed 100644 --- a/arch/arm/mach-msm/board-supersonic-mmc.c +++ b/arch/arm/mach-msm/board-supersonic-mmc.c @@ -174,6 +174,10 @@ static struct embedded_sdio_data supersonic_wifi_emb_data = { .high_power = 1, .high_speed = 1, }, + .cis = { + .vendor = 0x02d0, + .device = 0x4329, + }, }; static void (*wifi_status_cb)(int card_present, void *dev_id); diff --git a/arch/arm/mach-msm/board-supersonic-wifi.c b/arch/arm/mach-msm/board-supersonic-wifi.c index 49f130cd55cec..42c9c76d81ac4 100644 --- a/arch/arm/mach-msm/board-supersonic-wifi.c +++ b/arch/arm/mach-msm/board-supersonic-wifi.c @@ -17,6 +17,8 @@ int supersonic_wifi_power(int on); int supersonic_wifi_reset(int on); int supersonic_wifi_set_carddetect(int on); +#if defined(CONFIG_DHD_USE_STATIC_BUF) || defined(CONFIG_BCM4329_DHD_USE_STATIC_BUF) + #define PREALLOC_WLAN_NUMBER_OF_SECTIONS 4 #define PREALLOC_WLAN_NUMBER_OF_BUFFERS 160 #define PREALLOC_WLAN_SECTION_HEADER 24 @@ -52,9 +54,11 @@ static void *supersonic_wifi_mem_prealloc(int section, unsigned long size) return NULL; return wifi_mem_array[section].mem_ptr; } +#endif int __init supersonic_init_wifi_mem(void) { +#if defined(CONFIG_DHD_USE_STATIC_BUF) || defined(CONFIG_BCM4329_DHD_USE_STATIC_BUF) int i; for(i=0;( i < WLAN_SKB_BUF_NUM );i++) { @@ -69,12 +73,13 @@ int __init supersonic_init_wifi_mem(void) if (wifi_mem_array[i].mem_ptr == NULL) return -ENOMEM; } +#endif return 0; } static struct resource supersonic_wifi_resources[] = { [0] = { - .name = "bcm4329_wlan_irq", + .name = "bcmdhd_wlan_irq", .start = MSM_GPIO_TO_INT(SUPERSONIC_GPIO_WIFI_IRQ), .end = MSM_GPIO_TO_INT(SUPERSONIC_GPIO_WIFI_IRQ), .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE, @@ -85,11 +90,15 @@ static struct wifi_platform_data supersonic_wifi_control = { .set_power = supersonic_wifi_power, .set_reset = supersonic_wifi_reset, .set_carddetect = supersonic_wifi_set_carddetect, +#if defined(CONFIG_DHD_USE_STATIC_BUF) || defined(CONFIG_BCM4329_DHD_USE_STATIC_BUF) .mem_prealloc = supersonic_wifi_mem_prealloc, +#else + .mem_prealloc = NULL, +#endif }; static struct platform_device supersonic_wifi_device = { - .name = "bcm4329_wlan", + .name = "bcmdhd_wlan", .id = 1, .num_resources = ARRAY_SIZE(supersonic_wifi_resources), .resource = supersonic_wifi_resources, @@ -132,8 +141,6 @@ static unsigned supersonic_wifi_update_nvs(char *str, int add_flag) static int __init supersonic_wifi_init(void) { - int ret; - if (!machine_is_supersonic()) return 0; @@ -142,8 +149,7 @@ static int __init supersonic_wifi_init(void) supersonic_wifi_update_nvs("btc_params80=0\n", 1); supersonic_wifi_update_nvs("btc_params70=0x32\n", 1); supersonic_init_wifi_mem(); - ret = platform_device_register(&supersonic_wifi_device); - return ret; + return platform_device_register(&supersonic_wifi_device); } late_initcall(supersonic_wifi_init); From 6b38f0ada10db91bedb00bf75f7fefc2acb96781 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 15 Jul 2012 22:02:24 -0500 Subject: [PATCH 2321/2556] msm: incrediblec: bcmdhd updates --- arch/arm/mach-msm/board-incrediblec-mmc.c | 4 ++++ arch/arm/mach-msm/board-incrediblec-wifi.c | 18 ++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-msm/board-incrediblec-mmc.c b/arch/arm/mach-msm/board-incrediblec-mmc.c index 81532d8a5bce9..42971046a8388 100644 --- a/arch/arm/mach-msm/board-incrediblec-mmc.c +++ b/arch/arm/mach-msm/board-incrediblec-mmc.c @@ -194,6 +194,10 @@ static struct embedded_sdio_data incrediblec_wifi_emb_data = { .high_power = 1, .high_speed = 1, }, + .cis = { + .vendor = 0x02d0, + .device = 0x4329, + }, }; static void (*wifi_status_cb)(int card_present, void *dev_id); diff --git a/arch/arm/mach-msm/board-incrediblec-wifi.c b/arch/arm/mach-msm/board-incrediblec-wifi.c index a9ad4ae575c67..261e3d8c8e0bf 100644 --- a/arch/arm/mach-msm/board-incrediblec-wifi.c +++ b/arch/arm/mach-msm/board-incrediblec-wifi.c @@ -17,6 +17,8 @@ int incrediblec_wifi_power(int on); int incrediblec_wifi_reset(int on); int incrediblec_wifi_set_carddetect(int on); +#if defined(CONFIG_DHD_USE_STATIC_BUF) || defined(CONFIG_BCM4329_DHD_USE_STATIC_BUF) + #define PREALLOC_WLAN_NUMBER_OF_SECTIONS 4 #define PREALLOC_WLAN_NUMBER_OF_BUFFERS 160 #define PREALLOC_WLAN_SECTION_HEADER 24 @@ -52,9 +54,11 @@ static void *incrediblec_wifi_mem_prealloc(int section, unsigned long size) return NULL; return wifi_mem_array[section].mem_ptr; } +#endif int __init incrediblec_init_wifi_mem(void) { +#if defined(CONFIG_DHD_USE_STATIC_BUF) || defined(CONFIG_BCM4329_DHD_USE_STATIC_BUF) int i; for(i=0;( i < WLAN_SKB_BUF_NUM );i++) { @@ -69,12 +73,13 @@ int __init incrediblec_init_wifi_mem(void) if (wifi_mem_array[i].mem_ptr == NULL) return -ENOMEM; } +#endif return 0; } static struct resource incrediblec_wifi_resources[] = { [0] = { - .name = "bcm4329_wlan_irq", + .name = "bcmdhd_wlan_irq", .start = MSM_GPIO_TO_INT(INCREDIBLEC_GPIO_WIFI_IRQ), .end = MSM_GPIO_TO_INT(INCREDIBLEC_GPIO_WIFI_IRQ), .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE, @@ -85,11 +90,15 @@ static struct wifi_platform_data incrediblec_wifi_control = { .set_power = incrediblec_wifi_power, .set_reset = incrediblec_wifi_reset, .set_carddetect = incrediblec_wifi_set_carddetect, +#if defined(CONFIG_DHD_USE_STATIC_BUF) || defined(CONFIG_BCM4329_DHD_USE_STATIC_BUF) .mem_prealloc = incrediblec_wifi_mem_prealloc, +#else + .mem_prealloc = NULL, +#endif }; static struct platform_device incrediblec_wifi_device = { - .name = "bcm4329_wlan", + .name = "bcmdhd_wlan", .id = 1, .num_resources = ARRAY_SIZE(incrediblec_wifi_resources), .resource = incrediblec_wifi_resources, @@ -130,8 +139,6 @@ static unsigned incrediblec_wifi_update_nvs(char *str, int add_flag) static int __init incrediblec_wifi_init(void) { - int ret; - if (!machine_is_incrediblec()) return 0; @@ -139,8 +146,7 @@ static int __init incrediblec_wifi_init(void) incrediblec_wifi_update_nvs("sd_oobonly=1\r\n", 0); incrediblec_wifi_update_nvs("btc_params70=0x32\r\n", 1); incrediblec_init_wifi_mem(); - ret = platform_device_register(&incrediblec_wifi_device); - return ret; + return platform_device_register(&incrediblec_wifi_device); } late_initcall(incrediblec_wifi_init); From 4b38457aeae9610c2e93fdfa3b31515e6c1898b5 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 15 Jul 2012 22:05:48 -0500 Subject: [PATCH 2322/2556] msm: bravo: bcmdhd updates --- arch/arm/mach-msm/board-bravo-mmc.c | 4 ++++ arch/arm/mach-msm/board-bravo-wifi.c | 18 ++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-msm/board-bravo-mmc.c b/arch/arm/mach-msm/board-bravo-mmc.c index 8838f4574ab8e..c47acf718e581 100644 --- a/arch/arm/mach-msm/board-bravo-mmc.c +++ b/arch/arm/mach-msm/board-bravo-mmc.c @@ -204,6 +204,10 @@ static struct embedded_sdio_data bravo_wifi_emb_data = { .high_power = 1, .high_speed = 1, }, + .cis = { + .vendor = 0x02d0, + .device = 0x4329, + }, }; static int bravo_wifi_cd = 0; /* WIFI virtual 'card detect' status */ diff --git a/arch/arm/mach-msm/board-bravo-wifi.c b/arch/arm/mach-msm/board-bravo-wifi.c index 5b08341120ac6..abf30df4e1f2d 100644 --- a/arch/arm/mach-msm/board-bravo-wifi.c +++ b/arch/arm/mach-msm/board-bravo-wifi.c @@ -17,6 +17,8 @@ int bravo_wifi_power(int on); int bravo_wifi_reset(int on); int bravo_wifi_set_carddetect(int on); +#if defined(CONFIG_DHD_USE_STATIC_BUF) || defined(CONFIG_BCM4329_DHD_USE_STATIC_BUF) + #define PREALLOC_WLAN_NUMBER_OF_SECTIONS 4 #define PREALLOC_WLAN_NUMBER_OF_BUFFERS 160 #define PREALLOC_WLAN_SECTION_HEADER 24 @@ -52,9 +54,11 @@ static void *bravo_wifi_mem_prealloc(int section, unsigned long size) return NULL; return wifi_mem_array[section].mem_ptr; } +#endif int __init bravo_init_wifi_mem(void) { +#if defined(CONFIG_DHD_USE_STATIC_BUF) || defined(CONFIG_BCM4329_DHD_USE_STATIC_BUF) int i; for(i=0;( i < WLAN_SKB_BUF_NUM );i++) { @@ -69,12 +73,13 @@ int __init bravo_init_wifi_mem(void) if (wifi_mem_array[i].mem_ptr == NULL) return -ENOMEM; } +#endif return 0; } static struct resource bravo_wifi_resources[] = { [0] = { - .name = "bcm4329_wlan_irq", + .name = "bcmdhd_wlan_irq", .start = MSM_GPIO_TO_INT(BRAVO_GPIO_WIFI_IRQ), .end = MSM_GPIO_TO_INT(BRAVO_GPIO_WIFI_IRQ), .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE, @@ -85,11 +90,15 @@ static struct wifi_platform_data bravo_wifi_control = { .set_power = bravo_wifi_power, .set_reset = bravo_wifi_reset, .set_carddetect = bravo_wifi_set_carddetect, +#if defined(CONFIG_DHD_USE_STATIC_BUF) || defined(CONFIG_BCM4329_DHD_USE_STATIC_BUF) .mem_prealloc = bravo_wifi_mem_prealloc, +#else + .mem_prealloc = NULL, +#endif }; static struct platform_device bravo_wifi_device = { - .name = "bcm4329_wlan", + .name = "bcmdhd_wlan", .id = 1, .num_resources = ARRAY_SIZE(bravo_wifi_resources), .resource = bravo_wifi_resources, @@ -130,8 +139,6 @@ static unsigned bravo_wifi_update_nvs(char *str, int add_flag) static int __init bravo_wifi_init(void) { - int ret; - if (!machine_is_bravo() && !machine_is_bravoc()) return 0; @@ -139,8 +146,7 @@ static int __init bravo_wifi_init(void) bravo_wifi_update_nvs("sd_oobonly=1\r\n", 0); bravo_wifi_update_nvs("btc_params70=0x32\r\n", 1); bravo_init_wifi_mem(); - ret = platform_device_register(&bravo_wifi_device); - return ret; + return platform_device_register(&bravo_wifi_device); } late_initcall(bravo_wifi_init); From e94e44dd3934e8a79902d53ae2ddf19467114952 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 15 Jul 2012 22:34:36 -0500 Subject: [PATCH 2323/2556] configs: evervolv: bcmdhd updates --- arch/arm/configs/evervolv_bravo_defconfig | 31 ++++++++++++------- .../configs/evervolv_incrediblec_defconfig | 31 ++++++++++++------- arch/arm/configs/evervolv_mahimahi_defconfig | 31 ++++++++++++------- .../arm/configs/evervolv_supersonic_defconfig | 31 ++++++++++++------- 4 files changed, 80 insertions(+), 44 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index b1b759ba76317..10eeaf4c8c1c5 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sat Jul 14 00:27:42 2012 +# Sun Jul 15 22:31:04 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -868,17 +868,19 @@ CONFIG_BT_HCIUART_LL=y # CONFIG_AF_RXRPC is not set CONFIG_FIB_RULES=y CONFIG_WIRELESS=y -CONFIG_WIRELESS_EXT=y CONFIG_WEXT_CORE=y CONFIG_WEXT_PROC=y -CONFIG_WEXT_PRIV=y -# CONFIG_CFG80211 is not set +CONFIG_CFG80211=y +CONFIG_NL80211_TESTMODE=y +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_DEBUGFS is not set +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y CONFIG_WIRELESS_EXT_SYSFS=y # CONFIG_LIB80211 is not set - -# -# CFG80211 needs to be enabled for MAC80211 -# +# CONFIG_MAC80211 is not set # # Some wireless drivers require a rate control algorithm @@ -1093,10 +1095,17 @@ CONFIG_NETDEV_1000=y # CONFIG_STMMAC_ETH is not set CONFIG_NETDEV_10000=y CONFIG_WLAN=y -CONFIG_BCM4329=m -CONFIG_BCM4329_FW_PATH="/vendor/firmware/fw_bcm4329.bin" -CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" +# CONFIG_ATH_COMMON is not set +# CONFIG_BCM4329 is not set +CONFIG_BCMDHD=y +CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" +CONFIG_BCMDHD_NVRAM_PATH="/proc/calibration" +# CONFIG_DHD_USE_STATIC_BUF is not set +# CONFIG_DHD_USE_SCHED_SCAN is not set +# CONFIG_DHD_ENABLE_P2P is not set # CONFIG_HOSTAP is not set +# CONFIG_IWM is not set +# CONFIG_LIBERTAS is not set # # Enable WiMAX (Networking options) to see the WiMAX drivers diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index a5fbe6c62af59..e835678f7e7e9 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sun Jul 15 12:47:31 2012 +# Sun Jul 15 22:28:57 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -869,17 +869,19 @@ CONFIG_BT_HCIUART_LL=y # CONFIG_AF_RXRPC is not set CONFIG_FIB_RULES=y CONFIG_WIRELESS=y -CONFIG_WIRELESS_EXT=y CONFIG_WEXT_CORE=y CONFIG_WEXT_PROC=y -CONFIG_WEXT_PRIV=y -# CONFIG_CFG80211 is not set +CONFIG_CFG80211=y +CONFIG_NL80211_TESTMODE=y +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_DEBUGFS is not set +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y CONFIG_WIRELESS_EXT_SYSFS=y # CONFIG_LIB80211 is not set - -# -# CFG80211 needs to be enabled for MAC80211 -# +# CONFIG_MAC80211 is not set # # Some wireless drivers require a rate control algorithm @@ -1101,10 +1103,17 @@ CONFIG_SMC91X=y # CONFIG_NETDEV_1000 is not set # CONFIG_NETDEV_10000 is not set CONFIG_WLAN=y -CONFIG_BCM4329=m -CONFIG_BCM4329_FW_PATH="/vendor/firmware/fw_bcm4329.bin" -CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" +# CONFIG_ATH_COMMON is not set +# CONFIG_BCM4329 is not set +CONFIG_BCMDHD=y +CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" +CONFIG_BCMDHD_NVRAM_PATH="/proc/calibration" +# CONFIG_DHD_USE_STATIC_BUF is not set +# CONFIG_DHD_USE_SCHED_SCAN is not set +# CONFIG_DHD_ENABLE_P2P is not set # CONFIG_HOSTAP is not set +# CONFIG_IWM is not set +# CONFIG_LIBERTAS is not set # # Enable WiMAX (Networking options) to see the WiMAX drivers diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index 785b595b57a75..ba1ff44b257da 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Fri Jul 13 21:30:59 2012 +# Sun Jul 15 22:13:34 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -868,17 +868,19 @@ CONFIG_BT_HCIUART_LL=y # CONFIG_AF_RXRPC is not set CONFIG_FIB_RULES=y CONFIG_WIRELESS=y -CONFIG_WIRELESS_EXT=y CONFIG_WEXT_CORE=y CONFIG_WEXT_PROC=y -CONFIG_WEXT_PRIV=y -# CONFIG_CFG80211 is not set +CONFIG_CFG80211=y +CONFIG_NL80211_TESTMODE=y +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_DEBUGFS is not set +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y CONFIG_WIRELESS_EXT_SYSFS=y # CONFIG_LIB80211 is not set - -# -# CFG80211 needs to be enabled for MAC80211 -# +# CONFIG_MAC80211 is not set # # Some wireless drivers require a rate control algorithm @@ -1093,10 +1095,17 @@ CONFIG_NETDEV_1000=y # CONFIG_STMMAC_ETH is not set CONFIG_NETDEV_10000=y CONFIG_WLAN=y -CONFIG_BCM4329=m -CONFIG_BCM4329_FW_PATH="/vendor/firmware/fw_bcm4329.bin" -CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" +# CONFIG_ATH_COMMON is not set +# CONFIG_BCM4329 is not set +CONFIG_BCMDHD=y +CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" +CONFIG_BCMDHD_NVRAM_PATH="/proc/calibration" +# CONFIG_DHD_USE_STATIC_BUF is not set +# CONFIG_DHD_USE_SCHED_SCAN is not set +# CONFIG_DHD_ENABLE_P2P is not set # CONFIG_HOSTAP is not set +# CONFIG_IWM is not set +# CONFIG_LIBERTAS is not set # # Enable WiMAX (Networking options) to see the WiMAX drivers diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index 48e1160f38c57..7997d307552c6 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sun Jul 15 12:45:43 2012 +# Sun Jul 15 22:11:52 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -878,17 +878,19 @@ CONFIG_BT_HCIUART_LL=y # CONFIG_AF_RXRPC is not set CONFIG_FIB_RULES=y CONFIG_WIRELESS=y -CONFIG_WIRELESS_EXT=y CONFIG_WEXT_CORE=y CONFIG_WEXT_PROC=y -CONFIG_WEXT_PRIV=y -# CONFIG_CFG80211 is not set +CONFIG_CFG80211=y +CONFIG_NL80211_TESTMODE=y +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_DEBUGFS is not set +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y CONFIG_WIRELESS_EXT_SYSFS=y # CONFIG_LIB80211 is not set - -# -# CFG80211 needs to be enabled for MAC80211 -# +# CONFIG_MAC80211 is not set # # Some wireless drivers require a rate control algorithm @@ -1093,10 +1095,17 @@ CONFIG_MII=y # CONFIG_NETDEV_1000 is not set # CONFIG_NETDEV_10000 is not set CONFIG_WLAN=y -CONFIG_BCM4329=m -CONFIG_BCM4329_FW_PATH="/system/etc/firmware/fw_bcm4329.bin" -CONFIG_BCM4329_NVRAM_PATH="/proc/calibration" +# CONFIG_ATH_COMMON is not set +# CONFIG_BCM4329 is not set +CONFIG_BCMDHD=y +CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" +CONFIG_BCMDHD_NVRAM_PATH="/proc/calibration" +# CONFIG_DHD_USE_STATIC_BUF is not set +# CONFIG_DHD_USE_SCHED_SCAN is not set +# CONFIG_DHD_ENABLE_P2P is not set # CONFIG_HOSTAP is not set +# CONFIG_IWM is not set +# CONFIG_LIBERTAS is not set # # WiMAX Wireless Broadband devices From 25be48f25090b1e8d1664371907d6af6a5e58450 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 8 Jun 2011 15:16:52 -0700 Subject: [PATCH 2324/2556] net: wireless: Add CONFIG_WIFI_CONTROL_FUNC option Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/Kconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index e57f85790c63f..2c8eca7c7e24c 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -268,6 +268,11 @@ config MWL8K To compile this driver as a module, choose M here: the module will be called mwl8k. If unsure, say N. +config WIFI_CONTROL_FUNC + bool "Enable WiFi control function abstraction" + help + Enables Power/Reset/Carddetect function abstraction + source "drivers/net/wireless/ath/Kconfig" source "drivers/net/wireless/b43/Kconfig" source "drivers/net/wireless/b43legacy/Kconfig" From 136fdd663a15034d9163f2cc783ee43afe5bb1ad Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 14 Jun 2012 10:59:25 -0700 Subject: [PATCH 2325/2556] net: wireless: bcmdhd: Reload FW in case of constant scan failure Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd.h | 1 + drivers/net/wireless/bcmdhd/dhd_linux.c | 1 - drivers/net/wireless/bcmdhd/wl_cfg80211.c | 10 ++++++++++ drivers/net/wireless/bcmdhd/wl_cfg80211.h | 1 + 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index 3b4dc7a220c07..3ffbf0b82481d 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -431,6 +431,7 @@ extern void dhd_os_sdunlock_sndup_rxq(dhd_pub_t * pub); extern void dhd_os_sdlock_eventq(dhd_pub_t * pub); extern void dhd_os_sdunlock_eventq(dhd_pub_t * pub); extern bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret); +extern int net_os_send_hang_message(struct net_device *dev); #ifdef PNO_SUPPORT extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index d8c1c1b008aa3..a600b58cbb8eb 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -298,7 +298,6 @@ char nvram_path[MOD_PARAM_PATHLEN]; int op_mode = 0; module_param(op_mode, int, 0644); extern int wl_control_wl_start(struct net_device *dev); -extern int net_os_send_hang_message(struct net_device *dev); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) struct semaphore dhd_registration_sem; #define DHD_REGISTRATION_TIMEOUT 12000 /* msec : allowed time to finished dhd registration */ diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index cccc4d1df6d51..d6bf8fa0c13f4 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -72,6 +72,7 @@ u32 wl_dbg_level = WL_DBG_ERR; #define WL_SCAN_ACTIVE_TIME 40 #define WL_SCAN_PASSIVE_TIME 130 #define WL_FRAME_LEN 300 +#define WL_SCAN_BUSY_MAX 8 #define DNGL_FUNC(func, parameters) func parameters; #define COEX_DHCP @@ -1738,6 +1739,13 @@ wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, err = __wl_cfg80211_scan(wiphy, ndev, request, NULL); if (unlikely(err)) { WL_ERR(("scan error (%d)\n", err)); + if (err == BCME_BUSY) { + wl->scan_busy_count++; + if (wl->scan_busy_count > WL_SCAN_BUSY_MAX) { + wl->scan_busy_count = 0; + net_os_send_hang_message(ndev); + } + } return err; } @@ -6005,6 +6013,7 @@ static void wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted) unsigned long flags; WL_DBG(("Enter \n")); + wl->scan_busy_count = 0; if (!wl_get_drv_status(wl, SCANNING, ndev)) { wl_clr_drv_status(wl, SCANNING, ndev); WL_ERR(("Scan complete while device not scanning\n")); @@ -6259,6 +6268,7 @@ static s32 wl_notify_escan_complete(struct wl_priv *wl, WL_DBG(("Enter \n")); + wl->scan_busy_count = 0; if (wl->scan_request) { if (wl->scan_request->dev == wl->p2p_net) dev = wl_to_prmry_ndev(wl); diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index 37c8e5850c449..49bed703062af 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -456,6 +456,7 @@ struct wl_priv { bool sched_scan_running; /* scheduled scan req status */ u16 hostapd_chan; /* remember chan requested by framework for hostapd */ u16 deauth_reason; /* Place holder to save deauth/disassoc reasons */ + u16 scan_busy_count; }; static inline struct wl_bss_info *next_bss(struct wl_scan_results *list, struct wl_bss_info *bss) From 2c92a37314dc5b0d822d7b8927b79db113f32fea Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 14 Jun 2012 11:32:15 -0700 Subject: [PATCH 2326/2556] net: wireless: bcmdhd: Reduce priority for dhd_dpc and watchdog Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd_linux.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index a600b58cbb8eb..b8fb0a90b0ce8 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -346,14 +346,13 @@ module_param(dhd_master_mode, uint, 0); #ifdef DHDTHREAD /* Watchdog thread priority, -1 to use kernel timer */ -int dhd_watchdog_prio = 97; +int dhd_watchdog_prio = 0; module_param(dhd_watchdog_prio, int, 0); /* DPC thread priority, -1 to use tasklet */ -int dhd_dpc_prio = 98; +int dhd_dpc_prio = 1; module_param(dhd_dpc_prio, int, 0); -/* DPC thread priority, -1 to use tasklet */ extern int dhd_dongle_memsize; module_param(dhd_dongle_memsize, int, 0); #endif /* DHDTHREAD */ From 6b9bbd6ea393a1649ac7be8f47ef791614c5e710 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 20 Jun 2012 10:22:51 -0700 Subject: [PATCH 2327/2556] net: wireless: bcmdhd: Ignore error if "chanspecs" command is not supported Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index d6bf8fa0c13f4..009ebed2c7b31 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -7182,7 +7182,10 @@ s32 wl_update_wiphybands(struct wl_priv *wl) err = wl_construct_reginfo(wl, bw_cap); if (err) { WL_ERR(("wl_construct_reginfo() fails err=%d\n", err)); - return err; + if (err != BCME_UNSUPPORTED) + return err; + /* Ignore error if "chanspecs" command is not supported */ + err = 0; } for (i = 1; i <= nband && i < sizeof(bandlist)/sizeof(u32); i++) { index = -1; From 0303e06f25960e495012f2b911b0f52ac619d257 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 28 Jun 2012 10:25:25 -0700 Subject: [PATCH 2328/2556] net: wireless: bcmdhd: Skip inaccurate wl_construct_reginfo() call Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 009ebed2c7b31..e62f6e5849fd2 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -4718,7 +4718,7 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) #endif channel = ieee80211_get_channel(wiphy, freq); if (!channel) { - WL_ERR(("No valid channel")); + WL_ERR(("No valid channel: %u\n", freq)); kfree(notif_bss_info); return -EINVAL; } @@ -7179,6 +7179,7 @@ s32 wl_update_wiphybands(struct wl_priv *wl) } } +#if 0 err = wl_construct_reginfo(wl, bw_cap); if (err) { WL_ERR(("wl_construct_reginfo() fails err=%d\n", err)); @@ -7187,6 +7188,7 @@ s32 wl_update_wiphybands(struct wl_priv *wl) /* Ignore error if "chanspecs" command is not supported */ err = 0; } +#endif for (i = 1; i <= nband && i < sizeof(bandlist)/sizeof(u32); i++) { index = -1; if (bandlist[i] == WLC_BAND_5G && __wl_band_5ghz_a.n_channels > 0) { From 9444582fb2377a9632fe0d5ad891d924cb0468c6 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 28 Jun 2012 16:26:07 -0700 Subject: [PATCH 2329/2556] net: wireless: bcmdhd: Add wiphyband update for country change Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd_linux.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index b8fb0a90b0ce8..2f48cbba9979c 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -4532,8 +4532,12 @@ void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); - if (dhd && dhd->pub.up) + if (dhd && dhd->pub.up) { memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); +#ifdef WL_CFG80211 + wl_update_wiphybands(NULL); +#endif + } } void dhd_net_if_lock(struct net_device *dev) From 3d33f6c41935ae5ce240f630e3e92e0905af2910 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 28 Jun 2012 16:27:31 -0700 Subject: [PATCH 2330/2556] net: wireless: bcmdhd: Return wl_construct_reginfo() call Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index e62f6e5849fd2..28c92417b519d 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -7179,7 +7179,6 @@ s32 wl_update_wiphybands(struct wl_priv *wl) } } -#if 0 err = wl_construct_reginfo(wl, bw_cap); if (err) { WL_ERR(("wl_construct_reginfo() fails err=%d\n", err)); @@ -7188,7 +7187,6 @@ s32 wl_update_wiphybands(struct wl_priv *wl) /* Ignore error if "chanspecs" command is not supported */ err = 0; } -#endif for (i = 1; i <= nband && i < sizeof(bandlist)/sizeof(u32); i++) { index = -1; if (bandlist[i] == WLC_BAND_5G && __wl_band_5ghz_a.n_channels > 0) { From 9c387e9e5a86b8ecf857c1096e884a0a92f32456 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 28 Jun 2012 16:38:27 -0700 Subject: [PATCH 2331/2556] net: wireless: bcmdhd: Skip country setting if unnecessary Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/wldev_common.c | 27 +++++++++++----------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/wldev_common.c b/drivers/net/wireless/bcmdhd/wldev_common.c index 8270331850354..a1156fffe7e6e 100644 --- a/drivers/net/wireless/bcmdhd/wldev_common.c +++ b/drivers/net/wireless/bcmdhd/wldev_common.c @@ -349,21 +349,22 @@ int wldev_set_country( __FUNCTION__, error)); return error; } - } - cspec.rev = -1; - memcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ); - memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ); - get_customized_country_code((char *)&cspec.country_abbrev, &cspec); - error = wldev_iovar_setbuf(dev, "country", &cspec, sizeof(cspec), - smbuf, sizeof(smbuf), NULL); - if (error < 0) { - WLDEV_ERROR(("%s: set country for %s as %s rev %d failed\n", + + cspec.rev = -1; + memcpy(cspec.country_abbrev, country_code, WLC_CNTRY_BUF_SZ); + memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ); + get_customized_country_code((char *)&cspec.country_abbrev, &cspec); + error = wldev_iovar_setbuf(dev, "country", &cspec, sizeof(cspec), + smbuf, sizeof(smbuf), NULL); + if (error < 0) { + WLDEV_ERROR(("%s: set country for %s as %s rev %d failed\n", + __FUNCTION__, country_code, cspec.ccode, cspec.rev)); + return error; + } + dhd_bus_country_set(dev, &cspec); + WLDEV_ERROR(("%s: set country for %s as %s rev %d\n", __FUNCTION__, country_code, cspec.ccode, cspec.rev)); - return error; } - dhd_bus_country_set(dev, &cspec); - WLDEV_ERROR(("%s: set country for %s as %s rev %d\n", - __FUNCTION__, country_code, cspec.ccode, cspec.rev)); return 0; } From 6302788684ee3fd74939cafb0374062ea7101f35 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 29 Jun 2012 09:31:18 -0700 Subject: [PATCH 2332/2556] net: wireless: bcmdhd: Add mutex to wl_update_wiphybands() Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 28c92417b519d..121f3bb90986f 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -7149,11 +7149,15 @@ s32 wl_update_wiphybands(struct wl_priv *wl) int nmode = 0; int bw_cap = 0; int index = 0; + bool rollback_lock = false; WL_DBG(("Entry")); - if (wl == NULL) + if (wl == NULL) { wl = wlcfg_drv_priv; + mutex_lock(&wl->usr_sync); + rollback_lock = true; + } dev = wl_to_prmry_ndev(wl); memset(bandlist, 0, sizeof(bandlist)); @@ -7161,7 +7165,7 @@ s32 wl_update_wiphybands(struct wl_priv *wl) sizeof(bandlist), false); if (unlikely(err)) { WL_ERR(("error read bandlist (%d)\n", err)); - return err; + goto end_bands; } wiphy = wl_to_wiphy(wl); nband = bandlist[0]; @@ -7183,7 +7187,7 @@ s32 wl_update_wiphybands(struct wl_priv *wl) if (err) { WL_ERR(("wl_construct_reginfo() fails err=%d\n", err)); if (err != BCME_UNSUPPORTED) - return err; + goto end_bands; /* Ignore error if "chanspecs" command is not supported */ err = 0; } @@ -7212,6 +7216,9 @@ s32 wl_update_wiphybands(struct wl_priv *wl) } wiphy_apply_custom_regulatory(wiphy, &brcm_regdom); +end_bands: + if (rollback_lock) + mutex_unlock(&wl->usr_sync); return err; } From a3e66c85a3b5a3784765384ff4b7f1c9dda5a8bd Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Mon, 9 Jul 2012 10:46:55 -0700 Subject: [PATCH 2333/2556] net: wireless: bcmdhd: Init locks in dhd_attach() at the beginning Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd_linux.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 2f48cbba9979c..1cee01dda4cac 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -2644,17 +2644,8 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) strcat(net->name, "%d"); } - if (dhd_add_if(dhd, 0, (void *)net, net->name, NULL, 0, 0) == DHD_BAD_IF) - goto fail; - dhd_state |= DHD_ATTACH_STATE_ADD_IF; - -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) - net->open = NULL; -#else - net->netdev_ops = NULL; -#endif - sema_init(&dhd->proto_sem, 1); + sema_init(&dhd->sdsem, 1); #ifdef PROP_TXSTATUS spin_lock_init(&dhd->wlfc_spinlock); @@ -2670,6 +2661,17 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) spin_lock_init(&dhd->txqlock); spin_lock_init(&dhd->dhd_lock); + + if (dhd_add_if(dhd, 0, (void *)net, net->name, NULL, 0, 0) == DHD_BAD_IF) + goto fail; + dhd_state |= DHD_ATTACH_STATE_ADD_IF; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) + net->open = NULL; +#else + net->netdev_ops = NULL; +#endif + /* Initialize Wakelock stuff */ spin_lock_init(&dhd->wakelock_spinlock); dhd->wakelock_counter = 0; @@ -2722,7 +2724,6 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) #ifdef DHDTHREAD /* Initialize thread based operation and lock */ - sema_init(&dhd->sdsem, 1); if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0)) { dhd->threads_only = TRUE; } From 722fbfeb792fafef46858ef7d7f66b81ff1d5ef1 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Mon, 9 Jul 2012 11:14:42 -0700 Subject: [PATCH 2334/2556] net: wireless: bcmdhd: Update wiphy bands on band change Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd_linux.c | 13 +++++++++++- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 23 ++++++++++++++++++++-- drivers/net/wireless/bcmdhd/wl_cfg80211.h | 3 ++- drivers/net/wireless/bcmdhd/wldev_common.c | 2 ++ drivers/net/wireless/bcmdhd/wldev_common.h | 1 + 5 files changed, 38 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 1cee01dda4cac..4d763c9e41f10 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -4536,7 +4536,18 @@ void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec) if (dhd && dhd->pub.up) { memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); #ifdef WL_CFG80211 - wl_update_wiphybands(NULL); + wl_update_wiphybands(NULL, cspec->ccode); +#endif + } +} + +void dhd_bus_band_set(struct net_device *dev, uint band) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + if (dhd && dhd->pub.up) { +#ifdef WL_CFG80211 + wl_update_wiphybands(NULL, NULL); #endif } } diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 121f3bb90986f..5e61aa42739ad 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -7138,7 +7138,7 @@ static int wl_construct_reginfo(struct wl_priv *wl, s32 bw_cap) return err; } -s32 wl_update_wiphybands(struct wl_priv *wl) +s32 wl_update_wiphybands(struct wl_priv *wl, char *country_code) { struct wiphy *wiphy; struct net_device *dev; @@ -7149,6 +7149,7 @@ s32 wl_update_wiphybands(struct wl_priv *wl) int nmode = 0; int bw_cap = 0; int index = 0; + struct regulatory_request *new_request; bool rollback_lock = false; WL_DBG(("Entry")); @@ -7216,6 +7217,24 @@ s32 wl_update_wiphybands(struct wl_priv *wl) } wiphy_apply_custom_regulatory(wiphy, &brcm_regdom); + + if (country_code != NULL) { + WL_DBG(("country_code[0]=%c, country_code[1]=%c\n", country_code[0], country_code[1])); + /* Allocate a requlatory request to update cfg80211 for the supported channels */ + new_request = kzalloc(sizeof(struct regulatory_request), + GFP_KERNEL); + if (!new_request) { + WL_ERR(("error Memory alloc for requlatory request failed\n")); + err = -ENOMEM; + } else { + new_request->alpha2[0] = country_code[0]; + new_request->alpha2[1] = country_code[1]; + WL_DBG(("Send an event to cfg80211 to update supplicant to take the new channel list")); + nl80211_send_reg_change_event(new_request); + kfree(new_request); + } + } + end_bands: if (rollback_lock) mutex_unlock(&wl->usr_sync); @@ -7238,7 +7257,7 @@ static s32 __wl_cfg80211_up(struct wl_priv *wl) if (unlikely(err && err != -EINPROGRESS)) { WL_ERR(("wl_config_ifmode failed\n")); } - err = wl_update_wiphybands(wl); + err = wl_update_wiphybands(wl, NULL); if (unlikely(err)) { WL_ERR(("wl_update_wiphybands failed\n")); } diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index 49bed703062af..c3281ec79dfb0 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -669,6 +669,7 @@ extern int wl_cfg80211_hang(struct net_device *dev, u16 reason); extern s32 wl_mode_to_nl80211_iftype(s32 mode); int wl_cfg80211_do_driver_init(struct net_device *net); void wl_cfg80211_enable_trace(int level); -extern s32 wl_update_wiphybands(struct wl_priv *wl); +extern s32 wl_update_wiphybands(struct wl_priv *wl, char *country_code); extern s32 wl_cfg80211_if_is_group_owner(void); +extern void nl80211_send_reg_change_event(struct regulatory_request *request); #endif /* _wl_cfg80211_h_ */ diff --git a/drivers/net/wireless/bcmdhd/wldev_common.c b/drivers/net/wireless/bcmdhd/wldev_common.c index a1156fffe7e6e..51a80645fb349 100644 --- a/drivers/net/wireless/bcmdhd/wldev_common.c +++ b/drivers/net/wireless/bcmdhd/wldev_common.c @@ -320,6 +320,8 @@ int wldev_set_band( if ((band == WLC_BAND_AUTO) || (band == WLC_BAND_5G) || (band == WLC_BAND_2G)) { error = wldev_ioctl(dev, WLC_SET_BAND, &band, sizeof(band), 1); + if (!error) + dhd_bus_band_set(dev, band); } return error; } diff --git a/drivers/net/wireless/bcmdhd/wldev_common.h b/drivers/net/wireless/bcmdhd/wldev_common.h index f586609444202..dd3c899d12ca9 100644 --- a/drivers/net/wireless/bcmdhd/wldev_common.h +++ b/drivers/net/wireless/bcmdhd/wldev_common.h @@ -85,6 +85,7 @@ s32 wldev_iovar_setint_bsscfg( extern void get_customized_country_code(char *country_iso_code, wl_country_t *cspec); extern void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec); +extern void dhd_bus_band_set(struct net_device *dev, uint band); extern int wldev_set_country(struct net_device *dev, char *country_code); extern int net_os_wake_lock(struct net_device *dev); extern int net_os_wake_unlock(struct net_device *dev); From c042ec5a8ec3e49d10de5b9110e631c4d3feda5d Mon Sep 17 00:00:00 2001 From: Neeraj Kumar Garg Date: Mon, 9 Jul 2012 12:18:39 -0700 Subject: [PATCH 2335/2556] net: wireless: bcmdhd: Fix P2P GO hang issue Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/include/wlioctl.h | 1 + drivers/net/wireless/bcmdhd/wl_cfg80211.c | 64 +++++++++++-------- 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/include/wlioctl.h b/drivers/net/wireless/bcmdhd/include/wlioctl.h index 891d15c03c927..e543bfa6d1759 100644 --- a/drivers/net/wireless/bcmdhd/include/wlioctl.h +++ b/drivers/net/wireless/bcmdhd/include/wlioctl.h @@ -180,6 +180,7 @@ typedef struct wlc_ssid { uchar SSID[32]; } wlc_ssid_t; +#define WL_BSS_FLAGS_FROM_BEACON 0x01 #define WL_BSSTYPE_INFRA 1 #define WL_BSSTYPE_INDEP 0 diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 5e61aa42739ad..7049d77f24d31 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -781,8 +781,6 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, WL_ERR(("name is NULL\n")); return NULL; } - if (wl->iface_cnt == IFACE_MAX_CNT) - return ERR_PTR(-ENOMEM); if (wl->p2p_supported && (wlif_type != -1)) { if (wl_get_p2p_status(wl, IF_DELETING)) { /* wait till IF_DEL is complete @@ -810,6 +808,10 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, WL_ERR(("timeount < 0, return -EAGAIN\n")); return ERR_PTR(-EAGAIN); } + /* It should be now be safe to put this check here since we are sure + * by now netdev_notifier (unregister) would have been called */ + if (wl->iface_cnt == IFACE_MAX_CNT) + return ERR_PTR(-ENOMEM); } if (wl->p2p && !wl->p2p->on && strstr(name, WL_P2P_INTERFACE_PREFIX)) { p2p_on(wl) = true; @@ -1100,7 +1102,7 @@ wl_cfg80211_notify_ifdel(void) WL_DBG(("Enter \n")); wl_clr_p2p_status(wl, IF_DELETING); - + wake_up_interruptible(&wl->netif_change_event); return 0; } @@ -1578,6 +1580,7 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, /* Arm scan timeout timer */ mod_timer(&wl->scan_timeout, jiffies + WL_SCAN_TIMER_INTERVAL_MS * HZ / 1000); iscan_req = false; + wl->scan_request = request; if (request) { /* scan bss */ ssids = request->ssids; if (wl->iscan_on && (!ssids || !ssids->ssid_len || request->n_ssids != 1)) { @@ -1658,7 +1661,6 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, /* we don't do iscan in ibss */ ssids = this_ssid; } - wl->scan_request = request; wl_set_drv_status(wl, SCANNING, ndev); if (iscan_req) { err = wl_do_iscan(wl, request); @@ -1722,7 +1724,12 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, scan_out: wl_clr_drv_status(wl, SCANNING, ndev); - wl->scan_request = NULL; + if (wl->scan_request) { + if (timer_pending(&wl->scan_timeout)) + del_timer_sync(&wl->scan_timeout); + cfg80211_scan_done(wl->scan_request, true); + wl->scan_request = NULL; + } return err; } @@ -4731,29 +4738,6 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) signal = notif_bss_info->rssi * 100; -#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) - if (wl->p2p && wl->p2p_net && wl->scan_request && - ((wl->scan_request->dev == wl->p2p_net) || - (wl->scan_request->dev == wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION)))){ -#else - if (p2p_is_on(wl) && ( p2p_scan(wl) || - (wl->scan_request->dev == wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION)))) { -#endif - /* find the P2PIE, if we do not find it, we will discard this frame */ - wifi_p2p_ie_t * p2p_ie; - if ((p2p_ie = wl_cfgp2p_find_p2pie((u8 *)beacon_proberesp->variable, - wl_get_ielen(wl))) == NULL) { - WL_ERR(("Couldn't find P2PIE in probe response/beacon\n")); - kfree(notif_bss_info); - return err; - } - else if( wl_cfgp2p_retreive_p2pattrib(p2p_ie, P2P_SEID_DEV_INFO) == NULL) - { - WL_DBG(("Couldn't find P2P_SEID_DEV_INFO in probe response/beacon\n")); - kfree(notif_bss_info); - return err; - } - } if (!mgmt->u.probe_resp.timestamp) { struct timeval tv; @@ -5700,6 +5684,13 @@ wl_notify_rx_mgmt_frame(struct wl_priv *wl, struct net_device *ndev, WL_DBG(("P2P: GO_NEG_PHASE status cleared \n")); wl_clr_p2p_status(wl, GO_NEG_PHASE); } + + if (act_frm && (act_frm->subtype == P2P_PAF_GON_RSP)) { + /* Cancel the dwell time of req frame */ + WL_DBG(("P2P: Received GO NEG Resp frame, cancelling the dwell time\n")); + wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_SCAN, 0, 0, + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + } } else { mgmt_frame = (u8 *)((wl_event_rx_frame_data_t *)rxframe + 1); } @@ -6337,6 +6328,7 @@ static s32 wl_escan_handler(struct wl_priv *wl, wl_scan_results_t *list; u32 bi_length; u32 i; + wifi_p2p_ie_t * p2p_ie; u8 *p2p_dev_addr = NULL; WL_DBG((" enter event type : %d, status : %d \n", ntoh32(e->event_type), ntoh32(e->status))); @@ -6403,6 +6395,22 @@ static s32 wl_escan_handler(struct wl_priv *wl, WL_ERR(("Buffer is too small: ignoring\n")); goto exit; } +#if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) + if (wl->p2p_net && wl->scan_request && + wl->scan_request->dev == wl->p2p_net) { +#else + if (p2p_is_on(wl) && p2p_scan(wl)) { +#endif + /* p2p scan && allow only probe response */ + if (bi->flags & WL_BSS_FLAGS_FROM_BEACON) + goto exit; + if ((p2p_ie = wl_cfgp2p_find_p2pie(((u8 *) bi) + bi->ie_offset, + bi->ie_length)) == NULL) { + WL_ERR(("Couldn't find P2PIE in probe" + " response/beacon\n")); + goto exit; + } + } #define WLC_BSS_RSSI_ON_CHANNEL 0x0002 for (i = 0; i < list->count; i++) { bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length)) From ebee637916d7d0983ec9b3085d1f2b79b59b5a96 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 11 Jul 2012 10:17:59 -0700 Subject: [PATCH 2336/2556] net: wireless: bcmdhd: Remove country update from wl_update_wiphybands Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd_linux.c | 4 ++-- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 22 ++-------------------- drivers/net/wireless/bcmdhd/wl_cfg80211.h | 3 +-- 3 files changed, 5 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 4d763c9e41f10..657a1bf6a83a2 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -4536,7 +4536,7 @@ void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec) if (dhd && dhd->pub.up) { memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); #ifdef WL_CFG80211 - wl_update_wiphybands(NULL, cspec->ccode); + wl_update_wiphybands(NULL); #endif } } @@ -4547,7 +4547,7 @@ void dhd_bus_band_set(struct net_device *dev, uint band) if (dhd && dhd->pub.up) { #ifdef WL_CFG80211 - wl_update_wiphybands(NULL, NULL); + wl_update_wiphybands(NULL); #endif } } diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 7049d77f24d31..353713880bb3d 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -7146,7 +7146,7 @@ static int wl_construct_reginfo(struct wl_priv *wl, s32 bw_cap) return err; } -s32 wl_update_wiphybands(struct wl_priv *wl, char *country_code) +s32 wl_update_wiphybands(struct wl_priv *wl) { struct wiphy *wiphy; struct net_device *dev; @@ -7157,7 +7157,6 @@ s32 wl_update_wiphybands(struct wl_priv *wl, char *country_code) int nmode = 0; int bw_cap = 0; int index = 0; - struct regulatory_request *new_request; bool rollback_lock = false; WL_DBG(("Entry")); @@ -7226,23 +7225,6 @@ s32 wl_update_wiphybands(struct wl_priv *wl, char *country_code) wiphy_apply_custom_regulatory(wiphy, &brcm_regdom); - if (country_code != NULL) { - WL_DBG(("country_code[0]=%c, country_code[1]=%c\n", country_code[0], country_code[1])); - /* Allocate a requlatory request to update cfg80211 for the supported channels */ - new_request = kzalloc(sizeof(struct regulatory_request), - GFP_KERNEL); - if (!new_request) { - WL_ERR(("error Memory alloc for requlatory request failed\n")); - err = -ENOMEM; - } else { - new_request->alpha2[0] = country_code[0]; - new_request->alpha2[1] = country_code[1]; - WL_DBG(("Send an event to cfg80211 to update supplicant to take the new channel list")); - nl80211_send_reg_change_event(new_request); - kfree(new_request); - } - } - end_bands: if (rollback_lock) mutex_unlock(&wl->usr_sync); @@ -7265,7 +7247,7 @@ static s32 __wl_cfg80211_up(struct wl_priv *wl) if (unlikely(err && err != -EINPROGRESS)) { WL_ERR(("wl_config_ifmode failed\n")); } - err = wl_update_wiphybands(wl, NULL); + err = wl_update_wiphybands(wl); if (unlikely(err)) { WL_ERR(("wl_update_wiphybands failed\n")); } diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index c3281ec79dfb0..49bed703062af 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -669,7 +669,6 @@ extern int wl_cfg80211_hang(struct net_device *dev, u16 reason); extern s32 wl_mode_to_nl80211_iftype(s32 mode); int wl_cfg80211_do_driver_init(struct net_device *net); void wl_cfg80211_enable_trace(int level); -extern s32 wl_update_wiphybands(struct wl_priv *wl, char *country_code); +extern s32 wl_update_wiphybands(struct wl_priv *wl); extern s32 wl_cfg80211_if_is_group_owner(void); -extern void nl80211_send_reg_change_event(struct regulatory_request *request); #endif /* _wl_cfg80211_h_ */ From e561a8bdc315dc61b80c226743988fe33f802e0f Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 12 Jul 2012 16:58:00 -0700 Subject: [PATCH 2337/2556] net: wireless: bcmdhd: Add info_string param with driver/fw/chip info Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd.h | 5 +++++ drivers/net/wireless/bcmdhd/dhd_linux.c | 27 +++++++++++++++++++++---- drivers/net/wireless/bcmdhd/dhd_sdio.c | 18 ++++++++++++++++- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index 3ffbf0b82481d..f047c8efa8bc3 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -432,6 +432,7 @@ extern void dhd_os_sdlock_eventq(dhd_pub_t * pub); extern void dhd_os_sdunlock_eventq(dhd_pub_t * pub); extern bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret); extern int net_os_send_hang_message(struct net_device *dev); +extern void dhd_set_version_info(dhd_pub_t *pub, char *fw); #ifdef PNO_SUPPORT extern int dhd_pno_enable(dhd_pub_t *dhd, int pfn_enabled); @@ -521,6 +522,8 @@ extern int dhd_bus_membytes(dhd_pub_t *dhdp, bool set, uint32 address, uint8 *da extern void dhd_print_buf(void *pbuf, int len, int bytes_per_line); extern bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf, int *retval); extern uint dhd_bus_chip_id(dhd_pub_t *dhdp); +extern uint dhd_bus_chiprev_id(dhd_pub_t *dhdp); +extern uint dhd_bus_chippkg_id(dhd_pub_t *dhdp); #if defined(KEEP_ALIVE) extern int dhd_keep_alive_onoff(dhd_pub_t *dhd); @@ -609,6 +612,8 @@ extern uint dhd_pktgen_len; extern char fw_path[MOD_PARAM_PATHLEN]; extern char nv_path[MOD_PARAM_PATHLEN]; +#define MOD_PARAM_INFOLEN 512 + #ifdef SOFTAP extern char fw_path2[MOD_PARAM_PATHLEN]; #endif diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 657a1bf6a83a2..f4dd38fc77e27 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -295,6 +295,13 @@ typedef struct dhd_info { char firmware_path[MOD_PARAM_PATHLEN]; char nvram_path[MOD_PARAM_PATHLEN]; +/* load firmware and/or nvram values from the filesystem */ +module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0660); +module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0); + +char info_string[MOD_PARAM_INFOLEN]; +module_param_string(info_string, info_string, MOD_PARAM_INFOLEN, 0444); + int op_mode = 0; module_param(op_mode, int, 0644); extern int wl_control_wl_start(struct net_device *dev); @@ -310,10 +317,6 @@ module_param(dhd_sysioc, uint, 0); /* Error bits */ module_param(dhd_msg_level, int, 0); -/* load firmware and/or nvram values from the filesystem */ -module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0660); -module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0); - /* Watchdog interval */ uint dhd_watchdog_ms = 10; module_param(dhd_watchdog_ms, uint, 0); @@ -3314,6 +3317,8 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) /* Print fw version info */ DHD_ERROR(("Firmware version = %s\n", buf)); + dhd_set_version_info(dhd, buf); + DHD_BLOG(buf, strlen(buf) + 1); DHD_BLOG(dhd_version, strlen(dhd_version) + 1); @@ -4851,6 +4856,20 @@ int dhd_os_check_if_up(void *dhdp) return pub->up; } +void dhd_set_version_info(dhd_pub_t *dhdp, char *fw) +{ + int i; + + i = snprintf(info_string, sizeof(info_string), + "WLAN:\n Driver: %s\n Firmware: %s ", EPI_VERSION_STR, fw); + + if (!dhdp) + return; + i = snprintf(&info_string[i], sizeof(info_string) - i, + "\n Chip: %x Rev %x Pkg %x\n", dhd_bus_chip_id(dhdp), + dhd_bus_chiprev_id(dhdp), dhd_bus_chippkg_id(dhdp)); +} + int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd) { int ifidx; diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c index 4834ca2f9ed15..5b744f4fb1b46 100644 --- a/drivers/net/wireless/bcmdhd/dhd_sdio.c +++ b/drivers/net/wireless/bcmdhd/dhd_sdio.c @@ -6320,7 +6320,23 @@ uint dhd_bus_chip_id(dhd_pub_t *dhdp) { dhd_bus_t *bus = dhdp->bus; - return bus->sih->chip; + return bus->sih->chip; +} + +/* Get Chip Rev ID version */ +uint dhd_bus_chiprev_id(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus = dhdp->bus; + + return bus->sih->chiprev; +} + +/* Get Chip Pkg ID version */ +uint dhd_bus_chippkg_id(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus = dhdp->bus; + + return bus->sih->chippkg; } int From d173567ffa4ff26e9019873bc3d6c36f9da875dd Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Mon, 16 Jul 2012 01:41:35 -0500 Subject: [PATCH 2338/2556] net: wireless: bcmdhd: remove rouge file --- drivers/net/wireless/bcmdhd/dhd_linux_mon.c | 393 -------------------- 1 file changed, 393 deletions(-) delete mode 100644 drivers/net/wireless/bcmdhd/dhd_linux_mon.c diff --git a/drivers/net/wireless/bcmdhd/dhd_linux_mon.c b/drivers/net/wireless/bcmdhd/dhd_linux_mon.c deleted file mode 100644 index dd9c71f75be66..0000000000000 --- a/drivers/net/wireless/bcmdhd/dhd_linux_mon.c +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Broadcom Dongle Host Driver (DHD), Linux monitor network interface - * - * Copyright (C) 1999-2011, Broadcom Corporation - * - * Unless you and Broadcom execute a separate written software license - * agreement governing use of this software, this software is licensed to you - * under the terms of the GNU General Public License version 2 (the "GPL"), - * available at http://www.broadcom.com/licenses/GPLv2.php, with the - * following added to such license: - * - * As a special exception, the copyright holders of this software give you - * permission to link this software with independent modules, and to copy and - * distribute the resulting executable under terms of your choice, provided that - * you also meet, for each linked independent module, the terms and conditions of - * the license of that module. An independent module is a module which is not - * derived from this software. The special exception does not apply to any - * modifications of the software. - * - * Notwithstanding the above, under no circumstances may you combine this - * software in any way with any other Broadcom software provided under a license - * other than the GPL, without Broadcom's express prior written consent. - * - * $Id: dhd_linux_mon.c,v 1.131.2.55 2011-02-09 05:31:56 Exp $ - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -typedef enum monitor_states -{ - MONITOR_STATE_DEINIT = 0x0, - MONITOR_STATE_INIT = 0x1, - MONITOR_STATE_INTERFACE_ADDED = 0x2, - MONITOR_STATE_INTERFACE_DELETED = 0x4 -} monitor_states_t; -extern int dhd_start_xmit(struct sk_buff *skb, struct net_device *net); - -/** - * Local declarations and defintions (not exposed) - */ -#define MON_PRINT(format, ...) printk("DHD-MON: %s " format, __func__, ##__VA_ARGS__) -#define MON_TRACE MON_PRINT - -typedef struct monitor_interface { - int radiotap_enabled; - struct net_device* real_ndev; /* The real interface that the monitor is on */ - struct net_device* mon_ndev; -} monitor_interface; - -typedef struct dhd_linux_monitor { - void *dhd_pub; - monitor_states_t monitor_state; - monitor_interface mon_if[DHD_MAX_IFS]; - struct mutex lock; /* lock to protect mon_if */ -} dhd_linux_monitor_t; - -static dhd_linux_monitor_t g_monitor; - -static struct net_device* lookup_real_netdev(char *name); -static monitor_interface* ndev_to_monif(struct net_device *ndev); -static int dhd_mon_if_open(struct net_device *ndev); -static int dhd_mon_if_stop(struct net_device *ndev); -static int dhd_mon_if_subif_start_xmit(struct sk_buff *skb, struct net_device *ndev); -static void dhd_mon_if_set_multicast_list(struct net_device *ndev); -static int dhd_mon_if_change_mac(struct net_device *ndev, void *addr); - -static const struct net_device_ops dhd_mon_if_ops = { - .ndo_open = dhd_mon_if_open, - .ndo_stop = dhd_mon_if_stop, - .ndo_start_xmit = dhd_mon_if_subif_start_xmit, - .ndo_set_multicast_list = dhd_mon_if_set_multicast_list, - .ndo_set_mac_address = dhd_mon_if_change_mac, -}; - -/** - * Local static function defintions - */ - -/* Look up dhd's net device table to find a match (e.g. interface "eth0" is a match for "mon.eth0" - * "p2p-eth0-0" is a match for "mon.p2p-eth0-0") - */ -static struct net_device* lookup_real_netdev(char *name) -{ - int i; - int last_name_len = 0; - struct net_device *ndev; - struct net_device *ndev_found = NULL; - - /* We want to find interface "p2p-eth0-0" for monitor interface "mon.p2p-eth0-0", so - * we skip "eth0" even if "mon.p2p-eth0-0" contains "eth0" - */ - for (i = 0; i < DHD_MAX_IFS; i++) { - ndev = dhd_idx2net(g_monitor.dhd_pub, i); - if (ndev && strstr(name, ndev->name)) { - if (strlen(ndev->name) > last_name_len) { - ndev_found = ndev; - last_name_len = strlen(ndev->name); - } - } - } - - return ndev_found; -} - -static monitor_interface* ndev_to_monif(struct net_device *ndev) -{ - int i; - - for (i = 0; i < DHD_MAX_IFS; i++) { - if (g_monitor.mon_if[i].mon_ndev == ndev) - return &g_monitor.mon_if[i]; - } - - return NULL; -} - -static int dhd_mon_if_open(struct net_device *ndev) -{ - int ret = 0; - - MON_PRINT("enter\n"); - return ret; -} - -static int dhd_mon_if_stop(struct net_device *ndev) -{ - int ret = 0; - - MON_PRINT("enter\n"); - return ret; -} - -static int dhd_mon_if_subif_start_xmit(struct sk_buff *skb, struct net_device *ndev) -{ - int ret = 0; - int rtap_len; - int qos_len = 0; - int dot11_hdr_len = 24; - int snap_len = 6; - unsigned char *pdata; - unsigned short frame_ctl; - unsigned char src_mac_addr[6]; - unsigned char dst_mac_addr[6]; - struct ieee80211_hdr *dot11_hdr; - struct ieee80211_radiotap_header *rtap_hdr; - monitor_interface* mon_if; - - MON_PRINT("enter\n"); - - mon_if = ndev_to_monif(ndev); - if (mon_if == NULL || mon_if->real_ndev == NULL) { - MON_PRINT(" cannot find matched net dev, skip the packet\n"); - goto fail; - } - - if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header))) - goto fail; - - rtap_hdr = (struct ieee80211_radiotap_header *)skb->data; - if (unlikely(rtap_hdr->it_version)) - goto fail; - - rtap_len = ieee80211_get_radiotap_len(skb->data); - if (unlikely(skb->len < rtap_len)) - goto fail; - - MON_PRINT("radiotap len (should be 14): %d\n", rtap_len); - - /* Skip the ratio tap header */ - skb_pull(skb, rtap_len); - - dot11_hdr = (struct ieee80211_hdr *)skb->data; - frame_ctl = le16_to_cpu(dot11_hdr->frame_control); - /* Check if the QoS bit is set */ - if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { - /* Check if this ia a Wireless Distribution System (WDS) frame - * which has 4 MAC addresses - */ - if (dot11_hdr->frame_control & 0x0080) - qos_len = 2; - if ((dot11_hdr->frame_control & 0x0300) == 0x0300) - dot11_hdr_len += 6; - - memcpy(dst_mac_addr, dot11_hdr->addr1, sizeof(dst_mac_addr)); - memcpy(src_mac_addr, dot11_hdr->addr2, sizeof(src_mac_addr)); - - /* Skip the 802.11 header, QoS (if any) and SNAP, but leave spaces for - * for two MAC addresses - */ - skb_pull(skb, dot11_hdr_len + qos_len + snap_len - sizeof(src_mac_addr) * 2); - pdata = (unsigned char*)skb->data; - memcpy(pdata, dst_mac_addr, sizeof(dst_mac_addr)); - memcpy(pdata + sizeof(dst_mac_addr), src_mac_addr, sizeof(src_mac_addr)); - - MON_PRINT("if name: %s, matched if name %s\n", ndev->name, mon_if->real_ndev->name); - - /* Use the real net device to transmit the packet */ - ret = dhd_start_xmit(skb, mon_if->real_ndev); - - return ret; - } -fail: - dev_kfree_skb(skb); - return 0; -} - -static void dhd_mon_if_set_multicast_list(struct net_device *ndev) -{ - monitor_interface* mon_if; - - mon_if = ndev_to_monif(ndev); - if (mon_if == NULL || mon_if->real_ndev == NULL) { - MON_PRINT(" cannot find matched net dev, skip the packet\n"); - } - - MON_PRINT("enter, if name: %s, matched if name %s\n", ndev->name, mon_if->real_ndev->name); -} - -static int dhd_mon_if_change_mac(struct net_device *ndev, void *addr) -{ - int ret = 0; - monitor_interface* mon_if; - - mon_if = ndev_to_monif(ndev); - if (mon_if == NULL || mon_if->real_ndev == NULL) { - MON_PRINT(" cannot find matched net dev, skip the packet\n"); - } - - MON_PRINT("enter, if name: %s, matched if name %s\n", ndev->name, mon_if->real_ndev->name); - return ret; -} - -/** - * Global function definitions (declared in dhd_linux_mon.h) - */ - -int dhd_add_monitor(char *name, struct net_device **new_ndev) -{ - int i; - int idx = -1; - int ret = 0; - struct net_device* ndev = NULL; - dhd_linux_monitor_t **dhd_mon; - - mutex_lock(&g_monitor.lock); - - MON_TRACE("enter, if name: %s\n", name); - if (!name || !new_ndev) { - MON_PRINT("invalid parameters\n"); - ret = -EINVAL; - goto out; - } - - /* - * Find a vacancy - */ - for (i = 0; i < DHD_MAX_IFS; i++) - if (g_monitor.mon_if[i].mon_ndev == NULL) { - idx = i; - break; - } - if (idx == -1) { - MON_PRINT("exceeds maximum interfaces\n"); - ret = -EFAULT; - goto out; - } - - ndev = alloc_etherdev(sizeof(dhd_linux_monitor_t*)); - if (!ndev) { - MON_PRINT("failed to allocate memory\n"); - ret = -ENOMEM; - goto out; - } - - ndev->type = ARPHRD_IEEE80211_RADIOTAP; - strncpy(ndev->name, name, IFNAMSIZ); - ndev->name[IFNAMSIZ - 1] = 0; - ndev->netdev_ops = &dhd_mon_if_ops; - - ret = register_netdevice(ndev); - if (ret) { - MON_PRINT(" register_netdevice failed (%d)\n", ret); - goto out; - } - - *new_ndev = ndev; - g_monitor.mon_if[idx].radiotap_enabled = TRUE; - g_monitor.mon_if[idx].mon_ndev = ndev; - g_monitor.mon_if[idx].real_ndev = lookup_real_netdev(name); - dhd_mon = (dhd_linux_monitor_t **)netdev_priv(ndev); - *dhd_mon = &g_monitor; - g_monitor.monitor_state = MONITOR_STATE_INTERFACE_ADDED; - MON_PRINT("net device returned: 0x%p\n", ndev); - MON_PRINT("found a matched net device, name %s\n", g_monitor.mon_if[idx].real_ndev->name); - -out: - if (ret && ndev) - free_netdev(ndev); - - mutex_unlock(&g_monitor.lock); - return ret; - -} - -int dhd_del_monitor(struct net_device *ndev) -{ - int i; - bool rollback_lock = false; - if (!ndev) - return -EINVAL; - mutex_lock(&g_monitor.lock); - for (i = 0; i < DHD_MAX_IFS; i++) { - if (g_monitor.mon_if[i].mon_ndev == ndev || - g_monitor.mon_if[i].real_ndev == ndev) { - g_monitor.mon_if[i].real_ndev = NULL; - if (rtnl_is_locked()) { - rtnl_unlock(); - rollback_lock = true; - } - unregister_netdev(g_monitor.mon_if[i].mon_ndev); - free_netdev(g_monitor.mon_if[i].mon_ndev); - g_monitor.mon_if[i].mon_ndev = NULL; - g_monitor.monitor_state = MONITOR_STATE_INTERFACE_DELETED; - break; - } - } - if (rollback_lock) { - rtnl_lock(); - rollback_lock = false; - } - - if (g_monitor.monitor_state != - MONITOR_STATE_INTERFACE_DELETED) - MON_PRINT("interface not found in monitor IF array, is this a monitor IF? 0x%p\n", - ndev); - mutex_unlock(&g_monitor.lock); - - return 0; -} - -int dhd_monitor_init(void *dhd_pub) -{ - if (g_monitor.monitor_state == MONITOR_STATE_DEINIT) { - g_monitor.dhd_pub = dhd_pub; - mutex_init(&g_monitor.lock); - g_monitor.monitor_state = MONITOR_STATE_INIT; - } - return 0; -} - -int dhd_monitor_uninit(void) -{ - int i; - struct net_device *ndev; - bool rollback_lock = false; - mutex_lock(&g_monitor.lock); - if (g_monitor.monitor_state != MONITOR_STATE_DEINIT) { - for (i = 0; i < DHD_MAX_IFS; i++) { - ndev = g_monitor.mon_if[i].mon_ndev; - if (ndev) { - if (rtnl_is_locked()) { - rtnl_unlock(); - rollback_lock = true; - } - unregister_netdev(ndev); - free_netdev(ndev); - g_monitor.mon_if[i].real_ndev = NULL; - g_monitor.mon_if[i].mon_ndev = NULL; - if (rollback_lock) { - rtnl_lock(); - rollback_lock = false; - } - } - } - g_monitor.monitor_state = MONITOR_STATE_DEINIT; - } - mutex_unlock(&g_monitor.lock); - return 0; -} From 2a7a28a52cbf4604e33952243f447f65963a4879 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 15 Sep 2011 09:22:35 -0700 Subject: [PATCH 2339/2556] net: wireless: Add CFG80211_ALLOW_RECONNECT option Signed-off-by: Dmitry Shmidt --- net/wireless/Kconfig | 11 +++++++++++ net/wireless/sme.c | 2 ++ 2 files changed, 13 insertions(+) diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 1f1ef70f34f2d..8e2a668c9230e 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -159,3 +159,14 @@ config LIB80211_DEBUG from lib80211. If unsure, say N. + +config CFG80211_ALLOW_RECONNECT + bool "Allow reconnect while already connected" + depends on CFG80211 + default n + help + cfg80211 stack doesn't allow to connect if you are already + connected. This option allows to make a connection in this case. + + Select this option ONLY for wlan drivers that are specifically + built for such purposes. diff --git a/net/wireless/sme.c b/net/wireless/sme.c index e17b0bee6bdc7..07610041655bd 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -749,8 +749,10 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, ASSERT_WDEV_LOCK(wdev); +#ifndef CONFIG_CFG80211_ALLOW_RECONNECT if (wdev->sme_state != CFG80211_SME_IDLE) return -EALREADY; +#endif if (WARN_ON(wdev->connect_keys)) { kfree(wdev->connect_keys); From 1c51f7aca8ebb6251dc72cde7a1347bf03b9aaaf Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 18 Oct 2011 12:30:02 -0700 Subject: [PATCH 2340/2556] net: wireless: Fix CFG80211_ALLOW_RECONNECT option for disconnect Signed-off-by: Dmitry Shmidt --- net/wireless/sme.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 07610041655bd..11b45e7203737 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -650,8 +650,10 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) return; +#ifndef CONFIG_CFG80211_ALLOW_RECONNECT if (wdev->sme_state != CFG80211_SME_CONNECTED) return; +#endif if (wdev->current_bss) { cfg80211_unhold_bss(wdev->current_bss); From 96633172bfc147cb1f7806566fadd453819b30c5 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Mon, 16 Jul 2012 01:56:29 -0500 Subject: [PATCH 2341/2556] configs: evervolv: allow reconnect --- arch/arm/configs/evervolv_bravo_defconfig | 1 + arch/arm/configs/evervolv_incrediblec_defconfig | 1 + arch/arm/configs/evervolv_mahimahi_defconfig | 1 + arch/arm/configs/evervolv_supersonic_defconfig | 1 + 4 files changed, 4 insertions(+) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index 10eeaf4c8c1c5..a256f25239b53 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -880,6 +880,7 @@ CONFIG_CFG80211_DEFAULT_PS=y CONFIG_CFG80211_WEXT=y CONFIG_WIRELESS_EXT_SYSFS=y # CONFIG_LIB80211 is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y # CONFIG_MAC80211 is not set # diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index e835678f7e7e9..e73cf3ba0df53 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -881,6 +881,7 @@ CONFIG_CFG80211_DEFAULT_PS=y CONFIG_CFG80211_WEXT=y CONFIG_WIRELESS_EXT_SYSFS=y # CONFIG_LIB80211 is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y # CONFIG_MAC80211 is not set # diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index ba1ff44b257da..88a8f3d3177c0 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -880,6 +880,7 @@ CONFIG_CFG80211_DEFAULT_PS=y CONFIG_CFG80211_WEXT=y CONFIG_WIRELESS_EXT_SYSFS=y # CONFIG_LIB80211 is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y # CONFIG_MAC80211 is not set # diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index 7997d307552c6..8394a0ef6f089 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -890,6 +890,7 @@ CONFIG_CFG80211_DEFAULT_PS=y CONFIG_CFG80211_WEXT=y CONFIG_WIRELESS_EXT_SYSFS=y # CONFIG_LIB80211 is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y # CONFIG_MAC80211 is not set # From 51115b5354d11b2ce665a9efe0206f42f8451327 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 16 Feb 2012 16:27:59 -0800 Subject: [PATCH 2342/2556] cpufreq interactive governor: event tracing Change-Id: Ic13614a3da2faa2d4bd215ca3eb7191614f0cf66 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 19 ++++- include/trace/events/cpufreq_interactive.h | 87 ++++++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 include/trace/events/cpufreq_interactive.h diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 6e7097ba5edcd..b03a1a3357237 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -28,6 +28,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include + #include static atomic_t active_count = ATOMIC_INIT(0); @@ -187,7 +190,11 @@ static void cpufreq_interactive_timer(unsigned long data) new_freq = pcpu->freq_table[index].frequency; if (pcpu->target_freq == new_freq) + { + trace_cpufreq_interactive_already(data, cpu_load, + pcpu->target_freq, new_freq); goto rearm_if_notmax; + } /* * Do not scale down unless we have been at this frequency for the @@ -195,10 +202,16 @@ static void cpufreq_interactive_timer(unsigned long data) */ if (new_freq < pcpu->target_freq) { if (cputime64_sub(pcpu->timer_run_time, pcpu->freq_change_time) - < min_sample_time) + < min_sample_time) { + trace_cpufreq_interactive_notyet(data, cpu_load, + pcpu->target_freq, new_freq); goto rearm; + } } + trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq, + new_freq); + if (new_freq < pcpu->target_freq) { pcpu->target_freq = new_freq; spin_lock_irqsave(&down_cpumask_lock, flags); @@ -388,6 +401,8 @@ static int cpufreq_interactive_up_task(void *data) pcpu->freq_change_time_in_idle = get_cpu_idle_time_us(cpu, &pcpu->freq_change_time); + trace_cpufreq_interactive_up(cpu, pcpu->target_freq, + pcpu->policy->cur); } } @@ -434,6 +449,8 @@ static void cpufreq_interactive_freq_down(struct work_struct *work) pcpu->freq_change_time_in_idle = get_cpu_idle_time_us(cpu, &pcpu->freq_change_time); + trace_cpufreq_interactive_down(cpu, pcpu->target_freq, + pcpu->policy->cur); } } diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h new file mode 100644 index 0000000000000..3a90d3d609ba1 --- /dev/null +++ b/include/trace/events/cpufreq_interactive.h @@ -0,0 +1,87 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM cpufreq_interactive + +#if !defined(_TRACE_CPUFREQ_INTERACTIVE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_CPUFREQ_INTERACTIVE_H + +#include + +DECLARE_EVENT_CLASS(set, + TP_PROTO(u32 cpu_id, unsigned long targfreq, + unsigned long actualfreq), + TP_ARGS(cpu_id, targfreq, actualfreq), + + TP_STRUCT__entry( + __field( u32, cpu_id ) + __field(unsigned long, targfreq ) + __field(unsigned long, actualfreq ) + ), + + TP_fast_assign( + __entry->cpu_id = (u32) cpu_id; + __entry->targfreq = targfreq; + __entry->actualfreq = actualfreq; + ), + + TP_printk("cpu=%u targ=%lu actual=%lu", + __entry->cpu_id, __entry->targfreq, + __entry->actualfreq) +); + +DEFINE_EVENT(set, cpufreq_interactive_up, + TP_PROTO(u32 cpu_id, unsigned long targfreq, + unsigned long actualfreq), + TP_ARGS(cpu_id, targfreq, actualfreq) +); + +DEFINE_EVENT(set, cpufreq_interactive_down, + TP_PROTO(u32 cpu_id, unsigned long targfreq, + unsigned long actualfreq), + TP_ARGS(cpu_id, targfreq, actualfreq) +); + +DECLARE_EVENT_CLASS(loadeval, + TP_PROTO(unsigned long cpu_id, unsigned long load, + unsigned long curfreq, unsigned long targfreq), + TP_ARGS(cpu_id, load, curfreq, targfreq), + + TP_STRUCT__entry( + __field(unsigned long, cpu_id ) + __field(unsigned long, load ) + __field(unsigned long, curfreq ) + __field(unsigned long, targfreq ) + ), + + TP_fast_assign( + __entry->cpu_id = cpu_id; + __entry->load = load; + __entry->curfreq = curfreq; + __entry->targfreq = targfreq; + ), + + TP_printk("cpu=%lu load=%lu cur=%lu targ=%lu", + __entry->cpu_id, __entry->load, __entry->curfreq, + __entry->targfreq) +); + +DEFINE_EVENT(loadeval, cpufreq_interactive_target, + TP_PROTO(unsigned long cpu_id, unsigned long load, + unsigned long curfreq, unsigned long targfreq), + TP_ARGS(cpu_id, load, curfreq, targfreq) +); + +DEFINE_EVENT(loadeval, cpufreq_interactive_already, + TP_PROTO(unsigned long cpu_id, unsigned long load, + unsigned long curfreq, unsigned long targfreq), + TP_ARGS(cpu_id, load, curfreq, targfreq) +); + +DEFINE_EVENT(loadeval, cpufreq_interactive_notyet, + TP_PROTO(unsigned long cpu_id, unsigned long load, + unsigned long curfreq, unsigned long targfreq), + TP_ARGS(cpu_id, load, curfreq, targfreq) +); +#endif /* _TRACE_CPUFREQ_INTERACTIVE_H */ + +/* This part must be outside protection */ +#include From c51efb7edc410e20b0bbb5c480095dc54e9b0ba5 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 6 Apr 2012 01:13:09 -0700 Subject: [PATCH 2343/2556] cpufreq: interactive: apply intermediate load to max speed not current Evaluate spikes in load (below go_hispeed_load) against the maximum speed supported by the device, not the current speed (which tends to make it too difficult to raise speed to intermediate levels until very busy). Change-Id: Ib937006abf8bedb60891a739acd733e89b732ae0 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index b03a1a3357237..260389233cb5a 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -176,7 +176,7 @@ static void cpufreq_interactive_timer(unsigned long data) else new_freq = pcpu->policy->max * cpu_load / 100; } else { - new_freq = pcpu->policy->cur * cpu_load / 100; + new_freq = pcpu->policy->max * cpu_load / 100; } if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, From 96a8b4b16a40425639d372d941cd3183039f0a98 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 6 Apr 2012 19:50:12 -0700 Subject: [PATCH 2344/2556] cpufreq: interactive: set at least hispeed when above hispeed load If load is above go_hispeed_load, always go to at least hispeed_freq, even when reducing speed from a higher speed, not just when jumping up from minimum speed. Avoids running at a lower than intended speed after a burst of even higher load. Change-Id: I5b9d2a15ba25ce609b21bac7c724265cf6838dee Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 260389233cb5a..00f990df0318b 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -171,10 +171,14 @@ static void cpufreq_interactive_timer(unsigned long data) cpu_load = load_since_change; if (cpu_load >= go_hispeed_load) { - if (pcpu->policy->cur == pcpu->policy->min) + if (pcpu->policy->cur == pcpu->policy->min) { new_freq = hispeed_freq; - else + } else { new_freq = pcpu->policy->max * cpu_load / 100; + + if (new_freq < hispeed_freq) + new_freq = hispeed_freq; + } } else { new_freq = pcpu->policy->max * cpu_load / 100; } From 1f7500e1b6fa1f156d6e0a010f484713c3810bfe Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 6 Apr 2012 19:59:36 -0700 Subject: [PATCH 2345/2556] cpufreq: interactive: don't drop speed if recently at higher load Apply min_sample_time to the last time the current target speed was originally requested or re-validated as appropriate for the current load, not to the time since the current speed was originally set. Avoids periodic dips in speed during bursty loads. Change-Id: I250bda657985de60373f9897cc41f480664d51a1 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 37 ++++++++++++--------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 00f990df0318b..941b8d5e2befa 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -42,8 +42,8 @@ struct cpufreq_interactive_cpuinfo { u64 idle_exit_time; u64 timer_run_time; int idling; - u64 freq_change_time; - u64 freq_change_time_in_idle; + u64 target_set_time; + u64 target_set_time_in_idle; struct cpufreq_policy *policy; struct cpufreq_frequency_table *freq_table; unsigned int target_freq; @@ -152,9 +152,9 @@ static void cpufreq_interactive_timer(unsigned long data) cpu_load = 100 * (delta_time - delta_idle) / delta_time; delta_idle = (unsigned int) cputime64_sub(now_idle, - pcpu->freq_change_time_in_idle); + pcpu->target_set_time_in_idle); delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time, - pcpu->freq_change_time); + pcpu->target_set_time); if ((delta_time == 0) || (delta_idle > delta_time)) load_since_change = 0; @@ -193,19 +193,12 @@ static void cpufreq_interactive_timer(unsigned long data) new_freq = pcpu->freq_table[index].frequency; - if (pcpu->target_freq == new_freq) - { - trace_cpufreq_interactive_already(data, cpu_load, - pcpu->target_freq, new_freq); - goto rearm_if_notmax; - } - /* * Do not scale down unless we have been at this frequency for the * minimum sample time. */ if (new_freq < pcpu->target_freq) { - if (cputime64_sub(pcpu->timer_run_time, pcpu->freq_change_time) + if (cputime64_sub(pcpu->timer_run_time, pcpu->target_set_time) < min_sample_time) { trace_cpufreq_interactive_notyet(data, cpu_load, pcpu->target_freq, new_freq); @@ -213,6 +206,15 @@ static void cpufreq_interactive_timer(unsigned long data) } } + pcpu->target_set_time_in_idle = now_idle; + pcpu->target_set_time = pcpu->timer_run_time; + + if (pcpu->target_freq == new_freq) { + trace_cpufreq_interactive_already(data, cpu_load, + pcpu->target_freq, new_freq); + goto rearm_if_notmax; + } + trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq, new_freq); @@ -401,10 +403,6 @@ static int cpufreq_interactive_up_task(void *data) max_freq, CPUFREQ_RELATION_H); mutex_unlock(&set_speed_lock); - - pcpu->freq_change_time_in_idle = - get_cpu_idle_time_us(cpu, - &pcpu->freq_change_time); trace_cpufreq_interactive_up(cpu, pcpu->target_freq, pcpu->policy->cur); } @@ -450,9 +448,6 @@ static void cpufreq_interactive_freq_down(struct work_struct *work) CPUFREQ_RELATION_H); mutex_unlock(&set_speed_lock); - pcpu->freq_change_time_in_idle = - get_cpu_idle_time_us(cpu, - &pcpu->freq_change_time); trace_cpufreq_interactive_down(cpu, pcpu->target_freq, pcpu->policy->cur); } @@ -582,9 +577,9 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->policy = policy; pcpu->target_freq = policy->cur; pcpu->freq_table = freq_table; - pcpu->freq_change_time_in_idle = + pcpu->target_set_time_in_idle = get_cpu_idle_time_us(j, - &pcpu->freq_change_time); + &pcpu->target_set_time); pcpu->governor_enabled = 1; smp_wmb(); } From b2b046a329c80609fe2087179a8ee294b1ed175d Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 13 Apr 2012 20:18:02 -0700 Subject: [PATCH 2346/2556] cpufreq: interactive: configurable delay before raising above hispeed Change-Id: I4d6ac40b23a3790d48e30c37408284e9f955e8fa Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 42 +++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 941b8d5e2befa..fdde88ce0a981 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -81,6 +81,13 @@ static unsigned long min_sample_time; #define DEFAULT_TIMER_RATE 20 * USEC_PER_MSEC static unsigned long timer_rate; +/* + * Wait this long before raising speed above hispeed, by default a single + * timer interval. + */ +#define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE +static unsigned long above_hispeed_delay_val; + static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event); @@ -178,6 +185,17 @@ static void cpufreq_interactive_timer(unsigned long data) if (new_freq < hispeed_freq) new_freq = hispeed_freq; + + if (pcpu->target_freq == hispeed_freq && + new_freq > hispeed_freq && + cputime64_sub(pcpu->timer_run_time, + pcpu->target_set_time) + < above_hispeed_delay_val) { + trace_cpufreq_interactive_notyet(data, cpu_load, + pcpu->target_freq, + new_freq); + goto rearm; + } } } else { new_freq = pcpu->policy->max * cpu_load / 100; @@ -521,6 +539,28 @@ static ssize_t store_min_sample_time(struct kobject *kobj, static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644, show_min_sample_time, store_min_sample_time); +static ssize_t show_above_hispeed_delay(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", above_hispeed_delay_val); +} + +static ssize_t store_above_hispeed_delay(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + above_hispeed_delay_val = val; + return count; +} + +define_one_global_rw(above_hispeed_delay); + static ssize_t show_timer_rate(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -546,6 +586,7 @@ static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, static struct attribute *interactive_attributes[] = { &hispeed_freq_attr.attr, &go_hispeed_load_attr.attr, + &above_hispeed_delay.attr, &min_sample_time_attr.attr, &timer_rate_attr.attr, NULL, @@ -666,6 +707,7 @@ static int __init cpufreq_interactive_init(void) go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; min_sample_time = DEFAULT_MIN_SAMPLE_TIME; + above_hispeed_delay_val = DEFAULT_ABOVE_HISPEED_DELAY; timer_rate = DEFAULT_TIMER_RATE; /* Initalize per-cpu timers */ From 89e69782da73b6208ead995b8615159f083f3b1e Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 17 Apr 2012 17:39:34 -0700 Subject: [PATCH 2347/2556] cpufreq: interactive: adjust code and documentation to match Change-Id: If59c668d514a29febe5c35404fd9d01df8548eb1 Signed-off-by: Todd Poynor --- Documentation/cpu-freq/governors.txt | 16 +++++++++++++--- drivers/cpufreq/cpufreq_interactive.c | 4 ++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index dcc3cdf794dc1..c9fbfb1e703b1 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -215,11 +215,21 @@ frequency before ramping down. This is to ensure that the governor has seen enough historic cpu load data to determine the appropriate workload. Default is 80000 uS. -go_maxspeed_load: The CPU load at which to ramp to max speed. Default -is 85. +hispeed_freq: An intermediate "hi speed" at which to initially ramp +when CPU load hits the value specified in go_hispeed_load. If load +stays high for the amount of time specified in above_hispeed_delay, +then speed may be bumped higher. Default is maximum speed. + +go_hispeed_load: The CPU load at which to ramp to the intermediate "hi +speed". Default is 85%. + +above_hispeed_delay: Once speed is set to hispeed_freq, wait for this +long before bumping speed higher in response to continued high load. +Default is 20000 uS. timer_rate: Sample rate for reevaluating cpu load when the system is -not idle. Default is 30000 uS. +not idle. Default is 20000 uS. + 2.7 SmartassV2 --------------- diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index fdde88ce0a981..8a7b5f6185e40 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -72,13 +72,13 @@ static unsigned long go_hispeed_load; /* * The minimum amount of time to spend at a frequency before we can ramp down. */ -#define DEFAULT_MIN_SAMPLE_TIME 20 * USEC_PER_MSEC +#define DEFAULT_MIN_SAMPLE_TIME (80 * USEC_PER_MSEC) static unsigned long min_sample_time; /* * The sample rate of the timer used to increase frequency */ -#define DEFAULT_TIMER_RATE 20 * USEC_PER_MSEC +#define DEFAULT_TIMER_RATE (20 * USEC_PER_MSEC) static unsigned long timer_rate; /* From f33ff843c1473de94ececdbd46303152ab8078de Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 19 Apr 2012 12:52:48 -0700 Subject: [PATCH 2348/2556] cpufreq: interactive: based hispeed bump on target freq, not actual For systems that set a common speed for all CPUs, checking current speed here could bypass the intermediate hispeed bump decision for this CPU when another CPU was already at hispeed. This could result in an overly high setting (for all CPUs) in situations where all CPUs were about to drop to load levels that map to hispeed or below. Change-Id: I186f23dcfc5e2b6336cab8b0327f0c8a9a4482bc Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 8a7b5f6185e40..3a841a28f290c 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -178,7 +178,7 @@ static void cpufreq_interactive_timer(unsigned long data) cpu_load = load_since_change; if (cpu_load >= go_hispeed_load) { - if (pcpu->policy->cur == pcpu->policy->min) { + if (pcpu->target_freq <= pcpu->policy->min) { new_freq = hispeed_freq; } else { new_freq = pcpu->policy->max * cpu_load / 100; From 7fef84b2250e3beb04897062963689ac07ffdf49 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 20 Apr 2012 13:18:32 -0700 Subject: [PATCH 2349/2556] cpufreq: Separate speed target revalidate time and initial set time Allow speed drop after min_sample_time elapses from last time the current speed was last re-validated as appropriate for current load / input boost. Allow speed bump after min_sample_time (or above_hispeed_delay) elapses from the time the current speed was originally set. Change-Id: Ic25687a7a53d25e6544c30c47d7ab6f27a47bee8 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 3a841a28f290c..80b96df7b4998 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -44,6 +44,8 @@ struct cpufreq_interactive_cpuinfo { int idling; u64 target_set_time; u64 target_set_time_in_idle; + u64 target_validate_time; + u64 target_validate_time_in_idle; struct cpufreq_policy *policy; struct cpufreq_frequency_table *freq_table; unsigned int target_freq; @@ -213,10 +215,11 @@ static void cpufreq_interactive_timer(unsigned long data) /* * Do not scale down unless we have been at this frequency for the - * minimum sample time. + * minimum sample time since last validated. */ if (new_freq < pcpu->target_freq) { - if (cputime64_sub(pcpu->timer_run_time, pcpu->target_set_time) + if (cputime64_sub(pcpu->timer_run_time, + pcpu->target_validate_time) < min_sample_time) { trace_cpufreq_interactive_notyet(data, cpu_load, pcpu->target_freq, new_freq); @@ -224,8 +227,8 @@ static void cpufreq_interactive_timer(unsigned long data) } } - pcpu->target_set_time_in_idle = now_idle; - pcpu->target_set_time = pcpu->timer_run_time; + pcpu->target_validate_time_in_idle = now_idle; + pcpu->target_validate_time = pcpu->timer_run_time; if (pcpu->target_freq == new_freq) { trace_cpufreq_interactive_already(data, cpu_load, @@ -235,6 +238,8 @@ static void cpufreq_interactive_timer(unsigned long data) trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq, new_freq); + pcpu->target_set_time_in_idle = now_idle; + pcpu->target_set_time = pcpu->timer_run_time; if (new_freq < pcpu->target_freq) { pcpu->target_freq = new_freq; @@ -621,6 +626,10 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->target_set_time_in_idle = get_cpu_idle_time_us(j, &pcpu->target_set_time); + pcpu->target_validate_time = + pcpu->target_set_time; + pcpu->target_validate_time_in_idle = + pcpu->target_set_time_in_idle; pcpu->governor_enabled = 1; smp_wmb(); } From 5b8cf3ea92f46fd1d8288fcc5e867da0fb88efd8 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 2 Apr 2012 17:17:14 -0700 Subject: [PATCH 2350/2556] cpufreq: interactive: Boost frequency on touchscreen input Based on previous patches by Tero Kristo , Brian Steuer , David Ng , Antti P Miettinen , and Thomas Renninger Change-Id: Ic55fedcf6f9310f43a7022fb88e23b0392122769 Signed-off-by: Todd Poynor --- Documentation/cpu-freq/governors.txt | 3 + drivers/cpufreq/cpufreq_interactive.c | 164 ++++++++++++++++++++- include/trace/events/cpufreq_interactive.h | 12 ++ 3 files changed, 178 insertions(+), 1 deletion(-) diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index c9fbfb1e703b1..481418e40acd2 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -230,6 +230,9 @@ Default is 20000 uS. timer_rate: Sample rate for reevaluating cpu load when the system is not idle. Default is 20000 uS. +input_boost: If non-zero, boost speed of all CPUs to hispeed_freq on +touchscreen activity. Default is 0. + 2.7 SmartassV2 --------------- diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 80b96df7b4998..9fc681fe67ef6 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #define CREATE_TRACE_POINTS #include @@ -90,6 +92,19 @@ static unsigned long timer_rate; #define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE static unsigned long above_hispeed_delay_val; +/* + * Boost to hispeed on touchscreen input. + */ + +static int input_boost_val; + +struct cpufreq_interactive_inputopen { + struct input_handle *handle; + struct work_struct inputopen_work; +}; + +static struct cpufreq_interactive_inputopen inputopen; + static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event); @@ -476,6 +491,125 @@ static void cpufreq_interactive_freq_down(struct work_struct *work) } } +static void cpufreq_interactive_boost(void) +{ + int i; + int anyboost = 0; + unsigned long flags; + struct cpufreq_interactive_cpuinfo *pcpu; + + trace_cpufreq_interactive_boost(hispeed_freq); + spin_lock_irqsave(&up_cpumask_lock, flags); + + for_each_online_cpu(i) { + pcpu = &per_cpu(cpuinfo, i); + + if (pcpu->target_freq < hispeed_freq) { + pcpu->target_freq = hispeed_freq; + cpumask_set_cpu(i, &up_cpumask); + pcpu->target_set_time_in_idle = + get_cpu_idle_time_us(i, &pcpu->target_set_time); + anyboost = 1; + } + + /* + * Refresh time at which current (possibly being + * boosted) speed last validated (reset timer for + * allowing speed to drop). + */ + + pcpu->target_validate_time_in_idle = + get_cpu_idle_time_us(i, &pcpu->target_validate_time); + } + + spin_unlock_irqrestore(&up_cpumask_lock, flags); + + if (anyboost) + wake_up_process(up_task); +} + +static void cpufreq_interactive_input_event(struct input_handle *handle, + unsigned int type, + unsigned int code, int value) +{ + if (input_boost_val && type == EV_SYN && code == SYN_REPORT) + cpufreq_interactive_boost(); +} + +static void cpufreq_interactive_input_open(struct work_struct *w) +{ + struct cpufreq_interactive_inputopen *io = + container_of(w, struct cpufreq_interactive_inputopen, + inputopen_work); + int error; + + error = input_open_device(io->handle); + if (error) + input_unregister_handle(io->handle); +} + +static int cpufreq_interactive_input_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + pr_info("%s: connect to %s\n", __func__, dev->name); + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "cpufreq_interactive"; + + error = input_register_handle(handle); + if (error) + goto err; + + inputopen.handle = handle; + queue_work(down_wq, &inputopen.inputopen_work); + return 0; +err: + kfree(handle); + return error; +} + +static void cpufreq_interactive_input_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id cpufreq_interactive_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT_MASK(EV_ABS) }, + .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] = + BIT_MASK(ABS_MT_POSITION_X) | + BIT_MASK(ABS_MT_POSITION_Y) }, + }, /* multi-touch touchscreen */ + { + .flags = INPUT_DEVICE_ID_MATCH_KEYBIT | + INPUT_DEVICE_ID_MATCH_ABSBIT, + .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, + .absbit = { [BIT_WORD(ABS_X)] = + BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) }, + }, /* touchpad */ + { }, +}; + +static struct input_handler cpufreq_interactive_input_handler = { + .event = cpufreq_interactive_input_event, + .connect = cpufreq_interactive_input_connect, + .disconnect = cpufreq_interactive_input_disconnect, + .name = "cpufreq_interactive", + .id_table = cpufreq_interactive_ids, +}; + static ssize_t show_hispeed_freq(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -588,12 +722,34 @@ static ssize_t store_timer_rate(struct kobject *kobj, static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, show_timer_rate, store_timer_rate); +static ssize_t show_input_boost(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", input_boost_val); +} + +static ssize_t store_input_boost(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + input_boost_val = val; + return count; +} + +define_one_global_rw(input_boost); + static struct attribute *interactive_attributes[] = { &hispeed_freq_attr.attr, &go_hispeed_load_attr.attr, &above_hispeed_delay.attr, &min_sample_time_attr.attr, &timer_rate_attr.attr, + &input_boost.attr, NULL, }; @@ -649,6 +805,11 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, if (rc) return rc; + rc = input_register_handler(&cpufreq_interactive_input_handler); + if (rc) + pr_warn("%s: failed to register input handler\n", + __func__); + break; case CPUFREQ_GOV_STOP: @@ -671,6 +832,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, if (atomic_dec_return(&active_count) > 0) return 0; + input_unregister_handler(&cpufreq_interactive_input_handler); sysfs_remove_group(cpufreq_global_kobject, &interactive_attr_group); @@ -750,7 +912,7 @@ static int __init cpufreq_interactive_init(void) mutex_init(&set_speed_lock); idle_notifier_register(&cpufreq_interactive_idle_nb); - + INIT_WORK(&inputopen.inputopen_work, cpufreq_interactive_input_open); return cpufreq_register_governor(&cpufreq_gov_interactive); err_freeuptask: diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h index 3a90d3d609ba1..19e070b897ac3 100644 --- a/include/trace/events/cpufreq_interactive.h +++ b/include/trace/events/cpufreq_interactive.h @@ -81,6 +81,18 @@ DEFINE_EVENT(loadeval, cpufreq_interactive_notyet, unsigned long curfreq, unsigned long targfreq), TP_ARGS(cpu_id, load, curfreq, targfreq) ); + +TRACE_EVENT(cpufreq_interactive_boost, + TP_PROTO(unsigned long freq), + TP_ARGS(freq), + TP_STRUCT__entry( + __field(unsigned long, freq) + ), + TP_fast_assign( + __entry->freq = freq; + ), + TP_printk("freq=%lu", __entry->freq) +); #endif /* _TRACE_CPUFREQ_INTERACTIVE_H */ /* This part must be outside protection */ From b4e8d7b70162ea2e814829be7a50418235ea0481 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 23 Apr 2012 21:22:45 -0700 Subject: [PATCH 2351/2556] cpufreq: interactive: remove unused target_validate_time_in_idle Change-Id: I37c5085b91318242612440dfd775ad762996612f Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 9fc681fe67ef6..b0052177d4378 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -47,7 +47,6 @@ struct cpufreq_interactive_cpuinfo { u64 target_set_time; u64 target_set_time_in_idle; u64 target_validate_time; - u64 target_validate_time_in_idle; struct cpufreq_policy *policy; struct cpufreq_frequency_table *freq_table; unsigned int target_freq; @@ -242,7 +241,6 @@ static void cpufreq_interactive_timer(unsigned long data) } } - pcpu->target_validate_time_in_idle = now_idle; pcpu->target_validate_time = pcpu->timer_run_time; if (pcpu->target_freq == new_freq) { @@ -518,8 +516,7 @@ static void cpufreq_interactive_boost(void) * allowing speed to drop). */ - pcpu->target_validate_time_in_idle = - get_cpu_idle_time_us(i, &pcpu->target_validate_time); + pcpu->target_validate_time = ktime_to_us(ktime_get()); } spin_unlock_irqrestore(&up_cpumask_lock, flags); @@ -784,8 +781,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, &pcpu->target_set_time); pcpu->target_validate_time = pcpu->target_set_time; - pcpu->target_validate_time_in_idle = - pcpu->target_set_time_in_idle; pcpu->governor_enabled = 1; smp_wmb(); } From b84c3507dfa148a291caf2a00aafd8260985a95a Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 23 Apr 2012 20:42:41 -0700 Subject: [PATCH 2352/2556] cpufreq: interactive: Add sysfs boost interface for hints from userspace The explicit hint on/off version. Change-Id: Ibf62b6d45bf6fb8c9c055b9bdaf074ce9374c04f Signed-off-by: Todd Poynor --- Documentation/cpu-freq/governors.txt | 3 ++ drivers/cpufreq/cpufreq_interactive.c | 48 ++++++++++++++++++++-- include/trace/events/cpufreq_interactive.h | 13 ++++++ 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index 481418e40acd2..7f82181288602 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -233,6 +233,9 @@ not idle. Default is 20000 uS. input_boost: If non-zero, boost speed of all CPUs to hispeed_freq on touchscreen activity. Default is 0. +boost: If non-zero, immediately boost speed of all CPUs to +hispeed_freq. If zero, allow CPU speeds to drop below hispeed_freq. + 2.7 SmartassV2 --------------- diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index b0052177d4378..5854f618ddf39 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -29,12 +29,11 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include -#include - static atomic_t active_count = ATOMIC_INIT(0); struct cpufreq_interactive_cpuinfo { @@ -92,7 +91,7 @@ static unsigned long timer_rate; static unsigned long above_hispeed_delay_val; /* - * Boost to hispeed on touchscreen input. + * Boost pulse to hispeed on touchscreen input. */ static int input_boost_val; @@ -104,6 +103,12 @@ struct cpufreq_interactive_inputopen { static struct cpufreq_interactive_inputopen inputopen; +/* + * Non-zero means longer-term speed boost active. + */ + +static int boost_val; + static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event); @@ -193,7 +198,7 @@ static void cpufreq_interactive_timer(unsigned long data) if (load_since_change > cpu_load) cpu_load = load_since_change; - if (cpu_load >= go_hispeed_load) { + if (cpu_load >= go_hispeed_load || boost_val) { if (pcpu->target_freq <= pcpu->policy->min) { new_freq = hispeed_freq; } else { @@ -525,6 +530,12 @@ static void cpufreq_interactive_boost(void) wake_up_process(up_task); } +/* + * Pulsed boost on input event raises CPUs to hispeed_freq and lets + * usual algorithm of min_sample_time decide when to allow speed + * to drop. + */ + static void cpufreq_interactive_input_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) @@ -740,6 +751,34 @@ static ssize_t store_input_boost(struct kobject *kobj, struct attribute *attr, define_one_global_rw(input_boost); +static ssize_t show_boost(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", boost_val); +} + +static ssize_t store_boost(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + + boost_val = val; + + if (boost_val) + cpufreq_interactive_boost(); + else + trace_cpufreq_interactive_unboost(hispeed_freq); + + return count; +} + +define_one_global_rw(boost); + static struct attribute *interactive_attributes[] = { &hispeed_freq_attr.attr, &go_hispeed_load_attr.attr, @@ -747,6 +786,7 @@ static struct attribute *interactive_attributes[] = { &min_sample_time_attr.attr, &timer_rate_attr.attr, &input_boost.attr, + &boost.attr, NULL, }; diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h index 19e070b897ac3..ae6f232e498fc 100644 --- a/include/trace/events/cpufreq_interactive.h +++ b/include/trace/events/cpufreq_interactive.h @@ -93,6 +93,19 @@ TRACE_EVENT(cpufreq_interactive_boost, ), TP_printk("freq=%lu", __entry->freq) ); + +TRACE_EVENT(cpufreq_interactive_unboost, + TP_PROTO(unsigned long freq), + TP_ARGS(freq), + TP_STRUCT__entry( + __field(unsigned long, freq) + ), + TP_fast_assign( + __entry->freq = freq; + ), + TP_printk("freq=%lu", __entry->freq) +); + #endif /* _TRACE_CPUFREQ_INTERACTIVE_H */ /* This part must be outside protection */ From 30c79c796c69fd2f95e86ff8c05f54aeb7b7b367 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 26 Apr 2012 21:41:40 -0700 Subject: [PATCH 2353/2556] cpufreq: interactive: set floor for boosted speed Allow speed to drop to flooor frequency but not below, don't pin to speed at last boost. Change-Id: I0147c2b7a2e61ba16820605af6baaf09570be787 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 5854f618ddf39..5ddf1eb6d2d9b 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -45,10 +45,11 @@ struct cpufreq_interactive_cpuinfo { int idling; u64 target_set_time; u64 target_set_time_in_idle; - u64 target_validate_time; struct cpufreq_policy *policy; struct cpufreq_frequency_table *freq_table; unsigned int target_freq; + unsigned int floor_freq; + u64 floor_validate_time; int governor_enabled; }; @@ -233,12 +234,12 @@ static void cpufreq_interactive_timer(unsigned long data) new_freq = pcpu->freq_table[index].frequency; /* - * Do not scale down unless we have been at this frequency for the - * minimum sample time since last validated. + * Do not scale below floor_freq unless we have been at or above the + * floor frequency for the minimum sample time since last validated. */ - if (new_freq < pcpu->target_freq) { + if (new_freq < pcpu->floor_freq) { if (cputime64_sub(pcpu->timer_run_time, - pcpu->target_validate_time) + pcpu->floor_validate_time) < min_sample_time) { trace_cpufreq_interactive_notyet(data, cpu_load, pcpu->target_freq, new_freq); @@ -246,7 +247,8 @@ static void cpufreq_interactive_timer(unsigned long data) } } - pcpu->target_validate_time = pcpu->timer_run_time; + pcpu->floor_freq = new_freq; + pcpu->floor_validate_time = pcpu->timer_run_time; if (pcpu->target_freq == new_freq) { trace_cpufreq_interactive_already(data, cpu_load, @@ -516,12 +518,12 @@ static void cpufreq_interactive_boost(void) } /* - * Refresh time at which current (possibly being - * boosted) speed last validated (reset timer for - * allowing speed to drop). + * Set floor freq and (re)start timer for when last + * validated. */ - pcpu->target_validate_time = ktime_to_us(ktime_get()); + pcpu->floor_freq = hispeed_freq; + pcpu->floor_validate_time = ktime_to_us(ktime_get()); } spin_unlock_irqrestore(&up_cpumask_lock, flags); @@ -819,7 +821,8 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->target_set_time_in_idle = get_cpu_idle_time_us(j, &pcpu->target_set_time); - pcpu->target_validate_time = + pcpu->floor_freq = pcpu->target_freq; + pcpu->floor_validate_time = pcpu->target_set_time; pcpu->governor_enabled = 1; smp_wmb(); From 4d46b2ff3ddd73525c1ed812d2f149330fa01fd2 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 3 May 2012 00:16:55 -0700 Subject: [PATCH 2354/2556] cpufreq: interactive: add boost pulse interface Change-Id: Icf1e86d2065cc8f0816ba9c6b065eb056d4e8249 Signed-off-by: Todd Poynor --- Documentation/cpu-freq/governors.txt | 9 ++++-- drivers/cpufreq/cpufreq_interactive.c | 32 ++++++++++++++++++---- include/trace/events/cpufreq_interactive.h | 20 +++++++------- 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/Documentation/cpu-freq/governors.txt b/Documentation/cpu-freq/governors.txt index 7f82181288602..4c371b7d76c12 100644 --- a/Documentation/cpu-freq/governors.txt +++ b/Documentation/cpu-freq/governors.txt @@ -233,8 +233,13 @@ not idle. Default is 20000 uS. input_boost: If non-zero, boost speed of all CPUs to hispeed_freq on touchscreen activity. Default is 0. -boost: If non-zero, immediately boost speed of all CPUs to -hispeed_freq. If zero, allow CPU speeds to drop below hispeed_freq. +boost: If non-zero, immediately boost speed of all CPUs to at least +hispeed_freq until zero is written to this attribute. If zero, allow +CPU speeds to drop below hispeed_freq according to load as usual. + +boostpulse: Immediately boost speed of all CPUs to hispeed_freq for +min_sample_time, after which speeds are allowed to drop below +hispeed_freq according to load as usual. 2.7 SmartassV2 diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 5ddf1eb6d2d9b..2b758ab40957b 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -503,7 +503,6 @@ static void cpufreq_interactive_boost(void) unsigned long flags; struct cpufreq_interactive_cpuinfo *pcpu; - trace_cpufreq_interactive_boost(hispeed_freq); spin_lock_irqsave(&up_cpumask_lock, flags); for_each_online_cpu(i) { @@ -542,8 +541,10 @@ static void cpufreq_interactive_input_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { - if (input_boost_val && type == EV_SYN && code == SYN_REPORT) + if (input_boost_val && type == EV_SYN && code == SYN_REPORT) { + trace_cpufreq_interactive_boost("input"); cpufreq_interactive_boost(); + } } static void cpufreq_interactive_input_open(struct work_struct *w) @@ -771,16 +772,36 @@ static ssize_t store_boost(struct kobject *kobj, struct attribute *attr, boost_val = val; - if (boost_val) + if (boost_val) { + trace_cpufreq_interactive_boost("on"); cpufreq_interactive_boost(); - else - trace_cpufreq_interactive_unboost(hispeed_freq); + } else { + trace_cpufreq_interactive_unboost("off"); + } return count; } define_one_global_rw(boost); +static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + + trace_cpufreq_interactive_boost("pulse"); + cpufreq_interactive_boost(); + return count; +} + +static struct global_attr boostpulse = + __ATTR(boostpulse, 0200, NULL, store_boostpulse); + static struct attribute *interactive_attributes[] = { &hispeed_freq_attr.attr, &go_hispeed_load_attr.attr, @@ -789,6 +810,7 @@ static struct attribute *interactive_attributes[] = { &timer_rate_attr.attr, &input_boost.attr, &boost.attr, + &boostpulse.attr, NULL, }; diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h index ae6f232e498fc..0791f2728c1bc 100644 --- a/include/trace/events/cpufreq_interactive.h +++ b/include/trace/events/cpufreq_interactive.h @@ -83,27 +83,27 @@ DEFINE_EVENT(loadeval, cpufreq_interactive_notyet, ); TRACE_EVENT(cpufreq_interactive_boost, - TP_PROTO(unsigned long freq), - TP_ARGS(freq), + TP_PROTO(char *s), + TP_ARGS(s), TP_STRUCT__entry( - __field(unsigned long, freq) + __field(char *, s) ), TP_fast_assign( - __entry->freq = freq; + __entry->s = s; ), - TP_printk("freq=%lu", __entry->freq) + TP_printk("%s", __entry->s) ); TRACE_EVENT(cpufreq_interactive_unboost, - TP_PROTO(unsigned long freq), - TP_ARGS(freq), + TP_PROTO(char *s), + TP_ARGS(s), TP_STRUCT__entry( - __field(unsigned long, freq) + __field(char *, s) ), TP_fast_assign( - __entry->freq = freq; + __entry->s = s; ), - TP_printk("freq=%lu", __entry->freq) + TP_printk("%s", __entry->s) ); #endif /* _TRACE_CPUFREQ_INTERACTIVE_H */ From ef2e3d2fa0fde6ca8aee3a91f2133eb2d39bc745 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 10 May 2012 23:28:06 -0700 Subject: [PATCH 2355/2556] cpufreq: interactive: restart above_hispeed_delay at each hispeed load Change-Id: I2e5b91d45e8806b0ab94ca2301ed671c9af9ab13 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 2b758ab40957b..7587da731aaef 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -50,6 +50,7 @@ struct cpufreq_interactive_cpuinfo { unsigned int target_freq; unsigned int floor_freq; u64 floor_validate_time; + u64 hispeed_validate_time; int governor_enabled; }; @@ -211,7 +212,7 @@ static void cpufreq_interactive_timer(unsigned long data) if (pcpu->target_freq == hispeed_freq && new_freq > hispeed_freq && cputime64_sub(pcpu->timer_run_time, - pcpu->target_set_time) + pcpu->hispeed_validate_time) < above_hispeed_delay_val) { trace_cpufreq_interactive_notyet(data, cpu_load, pcpu->target_freq, @@ -223,6 +224,9 @@ static void cpufreq_interactive_timer(unsigned long data) new_freq = pcpu->policy->max * cpu_load / 100; } + if (new_freq <= hispeed_freq) + pcpu->hispeed_validate_time = pcpu->timer_run_time; + if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, new_freq, CPUFREQ_RELATION_H, &index)) { @@ -513,6 +517,7 @@ static void cpufreq_interactive_boost(void) cpumask_set_cpu(i, &up_cpumask); pcpu->target_set_time_in_idle = get_cpu_idle_time_us(i, &pcpu->target_set_time); + pcpu->hispeed_validate_time = pcpu->target_set_time; anyboost = 1; } @@ -846,6 +851,8 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->floor_freq = pcpu->target_freq; pcpu->floor_validate_time = pcpu->target_set_time; + pcpu->hispeed_validate_time = + pcpu->target_set_time; pcpu->governor_enabled = 1; smp_wmb(); } From e72821d11bae44ba5abc87c58490081943b13bff Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Fri, 20 Jul 2012 01:13:49 -0500 Subject: [PATCH 2356/2556] cpufreq: interactive: remove scorpion optimization --- drivers/cpufreq/cpufreq_interactive.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 7587da731aaef..7dbacf035e7a7 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -120,11 +120,7 @@ static struct cpufreq_governor cpufreq_gov_interactive = { .name = "interactive", .governor = cpufreq_governor_interactive, -#if defined(CONFIG_ARCH_MSM_SCORPION) - .max_transition_latency = 9500000, -#else .max_transition_latency = 10000000, -#endif .owner = THIS_MODULE, }; @@ -419,7 +415,6 @@ static int cpufreq_interactive_up_task(void *data) } set_current_state(TASK_RUNNING); - tmp_mask = up_cpumask; cpumask_clear(&up_cpumask); spin_unlock_irqrestore(&up_cpumask_lock, flags); @@ -429,7 +424,6 @@ static int cpufreq_interactive_up_task(void *data) unsigned int max_freq = 0; pcpu = &per_cpu(cpuinfo, cpu); - smp_rmb(); if (!pcpu->governor_enabled) From 3a905b5fb4b4f3646e2cc483a71599eba6d1f901 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Fri, 20 Jul 2012 01:08:44 -0500 Subject: [PATCH 2357/2556] cpufreq: ondemand: reset to cafs-3.0 initial contribution --- drivers/cpufreq/cpufreq.c | 43 ++++++-- drivers/cpufreq/cpufreq_ondemand.c | 159 +++++++++++++++++++++++++---- include/linux/cpufreq.h | 35 ++++--- 3 files changed, 191 insertions(+), 46 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index df27d08200f20..ff15497e91995 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -41,7 +41,11 @@ static struct cpufreq_driver *cpufreq_driver; static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data); #ifdef CONFIG_HOTPLUG_CPU /* This one keeps track of the previously set governor of a removed CPU */ -static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor); +struct cpufreq_cpu_save_data { + char gov[CPUFREQ_NAME_LEN]; + unsigned int max, min; +}; +static DEFINE_PER_CPU(struct cpufreq_cpu_save_data, cpufreq_policy_save); #endif static DEFINE_SPINLOCK(cpufreq_driver_lock); @@ -68,7 +72,7 @@ static DEFINE_PER_CPU(int, cpufreq_policy_cpu); static DEFINE_PER_CPU(struct rw_semaphore, cpu_policy_rwsem); #define lock_policy_rwsem(mode, cpu) \ -static int lock_policy_rwsem_##mode \ +int lock_policy_rwsem_##mode \ (int cpu) \ { \ int policy_cpu = per_cpu(cpufreq_policy_cpu, cpu); \ @@ -93,7 +97,7 @@ static void unlock_policy_rwsem_read(int cpu) up_read(&per_cpu(cpu_policy_rwsem, policy_cpu)); } -static void unlock_policy_rwsem_write(int cpu) +void unlock_policy_rwsem_write(int cpu) { int policy_cpu = per_cpu(cpufreq_policy_cpu, cpu); BUG_ON(policy_cpu == -1); @@ -688,12 +692,22 @@ static int cpufreq_add_dev_policy(unsigned int cpu, #ifdef CONFIG_HOTPLUG_CPU struct cpufreq_governor *gov; - gov = __find_governor(per_cpu(cpufreq_cpu_governor, cpu)); + gov = __find_governor(per_cpu(cpufreq_policy_save, cpu).gov); if (gov) { policy->governor = gov; pr_debug("Restoring governor %s for cpu %d\n", policy->governor->name, cpu); } + if (per_cpu(cpufreq_policy_save, cpu).min) { + policy->min = per_cpu(cpufreq_policy_save, cpu).min; + policy->user_policy.min = policy->min; + } + if (per_cpu(cpufreq_policy_save, cpu).max) { + policy->max = per_cpu(cpufreq_policy_save, cpu).max; + policy->user_policy.max = policy->max; + } + pr_debug("Restoring CPU%d min %d and max %d\n", + cpu, policy->min, policy->max); #endif for_each_cpu(j, policy->cpus) { @@ -1043,8 +1057,12 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) #ifdef CONFIG_SMP #ifdef CONFIG_HOTPLUG_CPU - strncpy(per_cpu(cpufreq_cpu_governor, cpu), data->governor->name, + strncpy(per_cpu(cpufreq_policy_save, cpu).gov, data->governor->name, CPUFREQ_NAME_LEN); + per_cpu(cpufreq_policy_save, cpu).min = data->min; + per_cpu(cpufreq_policy_save, cpu).max = data->max; + pr_debug("Saving CPU%d policy min %d and max %d\n", + cpu, data->min, data->max); #endif /* if we have other CPUs still registered, we need to unlink them, @@ -1068,8 +1086,12 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev) continue; pr_debug("removing link for cpu %u\n", j); #ifdef CONFIG_HOTPLUG_CPU - strncpy(per_cpu(cpufreq_cpu_governor, j), + strncpy(per_cpu(cpufreq_policy_save, j).gov, data->governor->name, CPUFREQ_NAME_LEN); + per_cpu(cpufreq_policy_save, j).min = data->min; + per_cpu(cpufreq_policy_save, j).max = data->max; + pr_debug("Saving CPU%d policy min %d and max %d\n", + j, data->min, data->max); #endif cpu_sys_dev = get_cpu_sysdev(j); kobj = &cpu_sys_dev->kobj; @@ -1555,8 +1577,11 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor) for_each_present_cpu(cpu) { if (cpu_online(cpu)) continue; - if (!strcmp(per_cpu(cpufreq_cpu_governor, cpu), governor->name)) - strcpy(per_cpu(cpufreq_cpu_governor, cpu), "\0"); + if (!strcmp(per_cpu(cpufreq_policy_save, cpu).gov, + governor->name)) + strcpy(per_cpu(cpufreq_policy_save, cpu).gov, "\0"); + per_cpu(cpufreq_policy_save, cpu).min = 0; + per_cpu(cpufreq_policy_save, cpu).max = 0; } #endif @@ -1690,7 +1715,7 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, * cpufreq_update_policy - re-evaluate an existing cpufreq policy * @cpu: CPU which shall be re-evaluated * - * Usefull for policy notifiers which have different necessities + * Useful for policy notifiers which have different necessities * at different times. */ int cpufreq_update_policy(unsigned int cpu) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 2b260d0ed30a4..a175ae7ae3794 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -22,33 +22,25 @@ #include #include #include +#include +#include +#include /* * dbs is used in this file as a shortform for demandbased switching * It helps to keep variable names smaller, simpler */ -#if defined(CONFIG_ARCH_MSM_SCORPION) -#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (6) -#define DEF_FREQUENCY_UP_THRESHOLD (80) -#define DEF_SAMPLING_DOWN_FACTOR (10) -#define MAX_SAMPLING_DOWN_FACTOR (95000) -#define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (2) -#define MICRO_FREQUENCY_UP_THRESHOLD (85) -#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (9500) -#define MIN_FREQUENCY_UP_THRESHOLD (7) -#define MAX_FREQUENCY_UP_THRESHOLD (100) -#else -#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10) -#define DEF_FREQUENCY_UP_THRESHOLD (80) -#define DEF_SAMPLING_DOWN_FACTOR (1) -#define MAX_SAMPLING_DOWN_FACTOR (100000) -#define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3) -#define MICRO_FREQUENCY_UP_THRESHOLD (95) -#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000) -#define MIN_FREQUENCY_UP_THRESHOLD (11) -#define MAX_FREQUENCY_UP_THRESHOLD (100) -#endif +#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10) +#define DEF_FREQUENCY_UP_THRESHOLD (80) +#define DEF_SAMPLING_DOWN_FACTOR (1) +#define MAX_SAMPLING_DOWN_FACTOR (100000) +#define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3) +#define MICRO_FREQUENCY_UP_THRESHOLD (95) +#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000) +#define MIN_FREQUENCY_UP_THRESHOLD (11) +#define MAX_FREQUENCY_UP_THRESHOLD (100) +#define MIN_FREQUENCY_DOWN_DIFFERENTIAL (1) /* * The polling frequency of this governor depends on the capability of @@ -115,6 +107,10 @@ static unsigned int dbs_enable; /* number of CPUs using this policy */ */ static DEFINE_MUTEX(dbs_mutex); +static struct workqueue_struct *input_wq; + +static DEFINE_PER_CPU(struct work_struct, dbs_refresh_work); + static struct dbs_tuners { unsigned int sampling_rate; unsigned int up_threshold; @@ -264,6 +260,7 @@ static ssize_t show_##file_name \ show_one(sampling_rate, sampling_rate); show_one(io_is_busy, io_is_busy); show_one(up_threshold, up_threshold); +show_one(down_differential, down_differential); show_one(sampling_down_factor, sampling_down_factor); show_one(ignore_nice_load, ignore_nice); show_one(powersave_bias, powersave_bias); @@ -308,6 +305,23 @@ static ssize_t store_up_threshold(struct kobject *a, struct attribute *b, return count; } +static ssize_t store_down_differential(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + + if (ret != 1 || input >= dbs_tuners_ins.up_threshold || + input < MIN_FREQUENCY_DOWN_DIFFERENTIAL) { + return -EINVAL; + } + + dbs_tuners_ins.down_differential = input; + + return count; +} + static ssize_t store_sampling_down_factor(struct kobject *a, struct attribute *b, const char *buf, size_t count) { @@ -382,6 +396,7 @@ static ssize_t store_powersave_bias(struct kobject *a, struct attribute *b, define_one_global_rw(sampling_rate); define_one_global_rw(io_is_busy); define_one_global_rw(up_threshold); +define_one_global_rw(down_differential); define_one_global_rw(sampling_down_factor); define_one_global_rw(ignore_nice_load); define_one_global_rw(powersave_bias); @@ -390,6 +405,7 @@ static struct attribute *dbs_attributes[] = { &sampling_rate_min.attr, &sampling_rate.attr, &up_threshold.attr, + &down_differential.attr, &sampling_down_factor.attr, &ignore_nice_load.attr, &powersave_bias.attr, @@ -631,6 +647,89 @@ static int should_io_be_busy(void) return 0; } +static void dbs_refresh_callback(struct work_struct *unused) +{ + struct cpufreq_policy *policy; + struct cpu_dbs_info_s *this_dbs_info; + unsigned int cpu = smp_processor_id(); + + if (lock_policy_rwsem_write(cpu) < 0) + return; + + this_dbs_info = &per_cpu(od_cpu_dbs_info, cpu); + policy = this_dbs_info->cur_policy; + + if (policy->cur < policy->max) { + policy->cur = policy->max; + + __cpufreq_driver_target(policy, policy->max, + CPUFREQ_RELATION_L); + this_dbs_info->prev_cpu_idle = get_cpu_idle_time(cpu, + &this_dbs_info->prev_cpu_wall); + } + unlock_policy_rwsem_write(cpu); +} + +static void dbs_input_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + int i; + + for_each_online_cpu(i) { + queue_work_on(i, input_wq, &per_cpu(dbs_refresh_work, i)); + } +} + +static int dbs_input_connect(struct input_handler *handler, + struct input_dev *dev, const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "cpufreq"; + + error = input_register_handle(handle); + if (error) + goto err2; + + error = input_open_device(handle); + if (error) + goto err1; + + return 0; +err1: + input_unregister_handle(handle); +err2: + kfree(handle); + return error; +} + +static void dbs_input_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id dbs_ids[] = { + { .driver_info = 1 }, + { }, +}; + +static struct input_handler dbs_input_handler = { + .event = dbs_input_event, + .connect = dbs_input_connect, + .disconnect = dbs_input_disconnect, + .name = "cpufreq_ond", + .id_table = dbs_ids, +}; + static int cpufreq_governor_dbs(struct cpufreq_policy *policy, unsigned int event) { @@ -690,6 +789,8 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, latency * LATENCY_MULTIPLIER); dbs_tuners_ins.io_is_busy = should_io_be_busy(); } + if (!cpu) + rc = input_register_handler(&dbs_input_handler); mutex_unlock(&dbs_mutex); mutex_init(&this_dbs_info->timer_mutex); @@ -702,10 +803,13 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, mutex_lock(&dbs_mutex); mutex_destroy(&this_dbs_info->timer_mutex); dbs_enable--; + if (!cpu) + input_unregister_handler(&dbs_input_handler); + mutex_unlock(&dbs_mutex); if (!dbs_enable) sysfs_remove_group(cpufreq_global_kobject, &dbs_attr_group); - mutex_unlock(&dbs_mutex); + break; case CPUFREQ_GOV_LIMITS: @@ -726,6 +830,7 @@ static int __init cpufreq_gov_dbs_init(void) { cputime64_t wall; u64 idle_time; + unsigned int i; int cpu = get_cpu(); idle_time = get_cpu_idle_time_us(cpu, &wall); @@ -747,12 +852,22 @@ static int __init cpufreq_gov_dbs_init(void) MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10); } + input_wq = create_workqueue("iewq"); + if (!input_wq) { + printk(KERN_ERR "Failed to create iewq workqueue\n"); + return -EFAULT; + } + for_each_possible_cpu(i) { + INIT_WORK(&per_cpu(dbs_refresh_work, i), dbs_refresh_callback); + } + return cpufreq_register_governor(&cpufreq_gov_ondemand); } static void __exit cpufreq_gov_dbs_exit(void) { cpufreq_unregister_governor(&cpufreq_gov_ondemand); + destroy_workqueue(input_wq); } diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 31f9557e72b7b..36085e105cb53 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -3,7 +3,7 @@ * * Copyright (C) 2001 Russell King * (C) 2002 - 2003 Dominik Brodowski - * + * * 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. @@ -56,9 +56,9 @@ static inline int cpufreq_unregister_notifier(struct notifier_block *nb, #define CPUFREQ_POLICY_POWERSAVE (1) #define CPUFREQ_POLICY_PERFORMANCE (2) -/* Frequency values here are CPU kHz so that hardware which doesn't run - * with some frequencies can complain without having to guess what per - * cent / per mille means. +/* Frequency values here are CPU kHz so that hardware which doesn't run + * with some frequencies can complain without having to guess what per + * cent / per mille means. * Maximum transition latency is in nanoseconds - if it's unknown, * CPUFREQ_ETERNAL shall be used. */ @@ -72,13 +72,15 @@ extern struct kobject *cpufreq_global_kobject; struct cpufreq_cpuinfo { unsigned int max_freq; unsigned int min_freq; - unsigned int transition_latency; /* in 10^(-9) s = nanoseconds */ + + /* in 10^(-9) s = nanoseconds */ + unsigned int transition_latency; }; struct cpufreq_real_policy { unsigned int min; /* in kHz */ unsigned int max; /* in kHz */ - unsigned int policy; /* see above */ + unsigned int policy; /* see above */ struct cpufreq_governor *governor; /* see below */ }; @@ -94,7 +96,7 @@ struct cpufreq_policy { unsigned int max; /* in kHz */ unsigned int cur; /* in kHz, only needed if cpufreq * governors are used */ - unsigned int policy; /* see above */ + unsigned int policy; /* see above */ struct cpufreq_governor *governor; /* see below */ struct work_struct update; /* if update_policy() needs to be @@ -167,11 +169,11 @@ static inline unsigned long cpufreq_scale(unsigned long old, u_int div, u_int mu struct cpufreq_governor { char name[CPUFREQ_NAME_LEN]; - int (*governor) (struct cpufreq_policy *policy, + int (*governor) (struct cpufreq_policy *policy, unsigned int event); ssize_t (*show_setspeed) (struct cpufreq_policy *policy, char *buf); - int (*store_setspeed) (struct cpufreq_policy *policy, + int (*store_setspeed) (struct cpufreq_policy *policy, unsigned int freq); unsigned int max_transition_latency; /* HW must be able to switch to next freq faster than this value in nano secs or we @@ -180,7 +182,8 @@ struct cpufreq_governor { struct module *owner; }; -/* pass a target to the cpufreq driver +/* + * Pass a target to the cpufreq driver. */ extern int cpufreq_driver_target(struct cpufreq_policy *policy, unsigned int target_freq, @@ -196,6 +199,8 @@ extern int __cpufreq_driver_getavg(struct cpufreq_policy *policy, int cpufreq_register_governor(struct cpufreq_governor *governor); void cpufreq_unregister_governor(struct cpufreq_governor *governor); +int lock_policy_rwsem_write(int cpu); +void unlock_policy_rwsem_write(int cpu); /********************************************************************* * CPUFREQ DRIVER INTERFACE * @@ -237,9 +242,9 @@ struct cpufreq_driver { /* flags */ -#define CPUFREQ_STICKY 0x01 /* the driver isn't removed even if +#define CPUFREQ_STICKY 0x01 /* the driver isn't removed even if * all ->init() calls failed */ -#define CPUFREQ_CONST_LOOPS 0x02 /* loops_per_jiffy or other kernel +#define CPUFREQ_CONST_LOOPS 0x02 /* loops_per_jiffy or other kernel * "constants" aren't affected by * frequency transitions */ #define CPUFREQ_PM_NO_WARN 0x04 /* don't warn on suspend/resume speed @@ -252,7 +257,7 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver_data); void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state); -static inline void cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned int min, unsigned int max) +static inline void cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned int min, unsigned int max) { if (policy->min < min) policy->min = min; @@ -392,12 +397,12 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, /* the following 3 funtions are for cpufreq core use only */ struct cpufreq_frequency_table *cpufreq_frequency_get_table(unsigned int cpu); struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu); -void cpufreq_cpu_put (struct cpufreq_policy *data); +void cpufreq_cpu_put(struct cpufreq_policy *data); /* the following are really really optional */ extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs; -void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table, +void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table, unsigned int cpu); void cpufreq_frequency_table_put_attr(unsigned int cpu); From 20e67442c364a844abc0e6d2e5a662372d0988cc Mon Sep 17 00:00:00 2001 From: David Ng Date: Wed, 3 Aug 2011 14:04:43 -0700 Subject: [PATCH 2358/2556] cpufreq: Fix input handler crash When input handler is running on a CPU not in ondemand governor, skip making CPU frequency changes. CRs-Fixed: 299451 Signed-off-by: David Ng --- drivers/cpufreq/cpufreq_ondemand.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index a175ae7ae3794..4079850c50072 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -658,6 +658,11 @@ static void dbs_refresh_callback(struct work_struct *unused) this_dbs_info = &per_cpu(od_cpu_dbs_info, cpu); policy = this_dbs_info->cur_policy; + if (!policy) { + /* CPU not using ondemand governor */ + unlock_policy_rwsem_write(cpu); + return; + } if (policy->cur < policy->max) { policy->cur = policy->max; From b642f5ec13beba37ffc5257707098083ba3cb39a Mon Sep 17 00:00:00 2001 From: Anitha Anand Date: Wed, 18 Jan 2012 17:17:40 -0800 Subject: [PATCH 2359/2556] cpufreq : Fix crash input event handler on governor switch When an input event and CPU1 hotplug occurs at the same time, a race condition can happen between cpufreq and cpu hotplug resulting in a kernel panic. When a hotplug operation is underway on CPU1, __cpufreq_remove_dev is called. __cpufreq_remove_dev gets the lock, proceeds to free the policy info of the current governor and calls the ondemand governor with a STOP event. If an input event occurs around the same time, it waits for __cpufreq_remove_dev to release the lock. Meanwhile, the ondemand governor clears some of its settings, but fails to clear its local current policy. As soon as the input event handler acquires the lock, it now uses the invalid ondemand current policy handle to make frequency changes. This results in a kernel panic. Skip making frequency changes if this condition happens. CRs-fixed: 327622,319348 Change-Id: I78227352fc66cc3ec5ab095c45eda76a92d1ba46 Signed-off-by: Anitha Anand --- drivers/cpufreq/cpufreq_ondemand.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 4079850c50072..9c1473f465a37 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -808,6 +808,9 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, mutex_lock(&dbs_mutex); mutex_destroy(&this_dbs_info->timer_mutex); dbs_enable--; + /* If device is being removed, policy is no longer + * valid. */ + this_dbs_info->cur_policy = NULL; if (!cpu) input_unregister_handler(&dbs_input_handler); mutex_unlock(&dbs_mutex); From 168fbb7f6b8c34119c7f7e5e9bfb5b4914866505 Mon Sep 17 00:00:00 2001 From: Amar Singhal Date: Thu, 23 Feb 2012 13:54:46 -0800 Subject: [PATCH 2360/2556] cpufreq: make the "scaling_cur_freq" sysfs entry pollable Wakeup userspace poll on change of cpu frequency. The userspace may then take action to change the power/performance characteristics of the device. Change-Id: I3030b22084fe7e0143b978a198ddcc579e7d6e83 Signed-off-by: Amar Singhal --- drivers/cpufreq/cpufreq.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index ff15497e91995..36375c05276d2 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -273,8 +273,10 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) trace_cpu_frequency(freqs->new, freqs->cpu); srcu_notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_POSTCHANGE, freqs); - if (likely(policy) && likely(policy->cpu == freqs->cpu)) + if (likely(policy) && likely(policy->cpu == freqs->cpu)) { policy->cur = freqs->new; + sysfs_notify(&policy->kobj, NULL, "scaling_cur_freq"); + } break; } } From 3135af67ff9fb3b0e688a333e8246ce4eeba52fc Mon Sep 17 00:00:00 2001 From: Anitha Anand Date: Mon, 5 Mar 2012 18:10:52 -0800 Subject: [PATCH 2361/2556] cpufreq: Add cpu utilization statistics to aid decisions made by userspace. Understanding system load as seen by cpufreq can help userspace algorithms make better decisions to improve system responsiveness. One such example would be the MP decision algorithm which mines various system info to make decisions on hotplugging in/out the additional cores. Change-Id: I277db7e1a7803a3822b61e7befa25131487cfe17 Signed-off-by: Anitha Anand --- drivers/cpufreq/cpufreq.c | 18 ++++++++++++++++++ drivers/cpufreq/cpufreq_ondemand.c | 14 +++++++++++--- include/linux/cpufreq.h | 8 +++++++- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 36375c05276d2..90311b2811ce7 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -281,8 +281,23 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) } } EXPORT_SYMBOL_GPL(cpufreq_notify_transition); +/** + * cpufreq_notify_utilization - notify CPU userspace about CPU utilization + * change + * + * This function is called everytime the CPU load is evaluated by the + * ondemand governor. It notifies userspace of cpu load changes via sysfs. + */ +void cpufreq_notify_utilization(struct cpufreq_policy *policy, + unsigned int util) +{ + if (policy) + policy->util = util; + if (policy->util >= MIN_CPU_UTIL_NOTIFY) + sysfs_notify(&policy->kobj, NULL, "cpu_utilization"); +} /********************************************************************* * SYSFS INTERFACE * @@ -370,6 +385,7 @@ show_one(cpuinfo_transition_latency, cpuinfo.transition_latency); show_one(scaling_min_freq, min); show_one(scaling_max_freq, max); show_one(scaling_cur_freq, cur); +show_one(cpu_utilization, util); static int __cpufreq_set_policy(struct cpufreq_policy *data, struct cpufreq_policy *policy); @@ -584,6 +600,7 @@ cpufreq_freq_attr_ro(scaling_cur_freq); cpufreq_freq_attr_ro(bios_limit); cpufreq_freq_attr_ro(related_cpus); cpufreq_freq_attr_ro(affected_cpus); +cpufreq_freq_attr_ro(cpu_utilization); cpufreq_freq_attr_rw(scaling_min_freq); cpufreq_freq_attr_rw(scaling_max_freq); cpufreq_freq_attr_rw(scaling_governor); @@ -596,6 +613,7 @@ static struct attribute *default_attrs[] = { &scaling_min_freq.attr, &scaling_max_freq.attr, &affected_cpus.attr, + &cpu_utilization.attr, &related_cpus.attr, &scaling_governor.attr, &scaling_driver.attr, diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 9c1473f465a37..0ffccb22604f5 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -433,7 +433,11 @@ static void dbs_freq_increase(struct cpufreq_policy *p, unsigned int freq) static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) { + /* Extrapolated load of this CPU */ + unsigned int load_at_max_freq = 0; unsigned int max_load_freq; + /* Current load across this CPU */ + unsigned int cur_load = 0; struct cpufreq_policy *policy; unsigned int j; @@ -460,7 +464,7 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) struct cpu_dbs_info_s *j_dbs_info; cputime64_t cur_wall_time, cur_idle_time, cur_iowait_time; unsigned int idle_time, wall_time, iowait_time; - unsigned int load, load_freq; + unsigned int load_freq; int freq_avg; j_dbs_info = &per_cpu(od_cpu_dbs_info, j); @@ -510,16 +514,20 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) if (unlikely(!wall_time || wall_time < idle_time)) continue; - load = 100 * (wall_time - idle_time) / wall_time; + cur_load = 100 * (wall_time - idle_time) / wall_time; freq_avg = __cpufreq_driver_getavg(policy, j); if (freq_avg <= 0) freq_avg = policy->cur; - load_freq = load * freq_avg; + load_freq = cur_load * freq_avg; if (load_freq > max_load_freq) max_load_freq = load_freq; } + /* calculate the scaled load across CPU */ + load_at_max_freq = (cur_load * policy->cur)/policy->cpuinfo.max_freq; + + cpufreq_notify_utilization(policy, load_at_max_freq); /* Check for frequency increase */ if (max_load_freq > dbs_tuners_ins.up_threshold * policy->cur) { diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 36085e105cb53..9bcfa569984ee 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -56,6 +56,10 @@ static inline int cpufreq_unregister_notifier(struct notifier_block *nb, #define CPUFREQ_POLICY_POWERSAVE (1) #define CPUFREQ_POLICY_PERFORMANCE (2) +/* Minimum frequency cutoff to notify the userspace about cpu utilization + * changes */ +#define MIN_CPU_UTIL_NOTIFY 40 + /* Frequency values here are CPU kHz so that hardware which doesn't run * with some frequencies can complain without having to guess what per * cent / per mille means. @@ -96,6 +100,7 @@ struct cpufreq_policy { unsigned int max; /* in kHz */ unsigned int cur; /* in kHz, only needed if cpufreq * governors are used */ + unsigned int util; /* CPU utilization at max frequency */ unsigned int policy; /* see above */ struct cpufreq_governor *governor; /* see below */ @@ -255,7 +260,8 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver_data); void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state); - +void cpufreq_notify_utilization(struct cpufreq_policy *policy, + unsigned int load); static inline void cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned int min, unsigned int max) { From 5fda10e80b3316a05343b2b2965b1da7b3266d63 Mon Sep 17 00:00:00 2001 From: Amar Singhal Date: Fri, 25 May 2012 14:40:05 -0700 Subject: [PATCH 2362/2556] cpufreq: Make the "scaling_governor" sysfs node pollable A userspace module programs different qos-rules depending on the governor running in the system. Make the governor node pollable, so that the userspace module can be triggered when the value of the governor changes. change-Id: Ic89c77c7d16b0f8954d59a211612e9a8e98a2c28 Signed-off-by: Amar Singhal --- drivers/cpufreq/cpufreq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 90311b2811ce7..473b0ef8d9e0e 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -475,6 +475,8 @@ static ssize_t store_scaling_governor(struct cpufreq_policy *policy, policy->user_policy.policy = policy->policy; policy->user_policy.governor = policy->governor; + sysfs_notify(&policy->kobj, NULL, "scaling_governor"); + if (ret) return ret; else From a16d125f0e2168c739d1a66eef7992524e719a56 Mon Sep 17 00:00:00 2001 From: intersectR Date: Mon, 27 Sep 2010 22:18:22 +0800 Subject: [PATCH 2363/2556] Add Hybrid Adaptive Voltage Scaling (HAVS). The purpose of HAVS is to minimize the power used by the CPU by determining and setting the optimal voltage. At the same time, the maximum and minimum voltage by which HAVS can scale to is fixed to a specified voltage depending on the CPU frequency in order to prevent scaling to a higher/lower voltage than what is known as safe at a specified voltage. The optimal voltage is actively determined for each frequency and temperature. HAVS actively adjusts the CPU voltage as the CPU frequency and temperature changes. AVS Author: Dave Estes HAVS Modification Author: Raymond Golo Conflicts: arch/arm/mach-msm/Kconfig --- Documentation/arm/msm/avs.txt | 185 +++++++++++++ arch/arm/mach-msm/Kconfig | 23 ++ arch/arm/mach-msm/Makefile | 1 + arch/arm/mach-msm/acpuclock-qsd8x50.c | 76 +++++- arch/arm/mach-msm/avs.c | 374 ++++++++++++++++++++++++++ arch/arm/mach-msm/avs.h | 52 ++++ arch/arm/mach-msm/avs_hw.S | 119 ++++++++ arch/arm/mach-msm/idle-v7.S | 20 ++ 8 files changed, 843 insertions(+), 7 deletions(-) create mode 100644 Documentation/arm/msm/avs.txt create mode 100644 arch/arm/mach-msm/avs.c create mode 100644 arch/arm/mach-msm/avs.h create mode 100644 arch/arm/mach-msm/avs_hw.S diff --git a/Documentation/arm/msm/avs.txt b/Documentation/arm/msm/avs.txt new file mode 100644 index 0000000000000..c22feec8a13d1 --- /dev/null +++ b/Documentation/arm/msm/avs.txt @@ -0,0 +1,185 @@ +Introduction +============ + +Adaptive Voltage Scaling (AVS) for ARCH_MSM_SCORPION + +The AVS driver adjusts the CPU voltage based on hardware feedback. Using +hardware feedback AVS is able to achieve lower voltages than the equivalent +static voltage scaling (SVS) voltage. + +The Scorpion architecture includes three ring oscillators for AVS. The +ring oscillators provide real time feedback about the voltage requirements +for the current operating conditions. The hardware can sense when the voltage +can be lowered and needs to be raised. + +The AVS software keeps track of the current operating conditions. Periodically +AVS queries the hardware. Every query AVS updates a table of required voltage +indexed by operating conditions, CPU frequency and temperature. + + +Hardware description +==================== + +AVS HW is specific to the Scorpion CPU implementation of ARMv7. The AVS HW +includes three ring oscillators. Each is located near a different +subsystem : CPU, VFP, and L2 cache. For the VFP measurement to be useful, +the VFP needs to execute. + +AVS HW is controlled through ARM CP15 registers + +AVSSCR - AVS Status and Control register +op1 = 7, CRn = c15, CRm = c1, op2 = 7 + +AVSDSCR - AVS Delay Synthesizer and Control and Status register +op1 = 7, CRn = c15, CRm = c0, op2 = 6 + +TSSCR - Temperature Sensor Control and Status register +op1 = 7, CRn = c15, CRm = c1, op2 = 0 + + +Software description +==================== + +AVS adaptively adjusts the CPU voltage for current operating conditions. It +maintains a table of operating voltages indexed by CPU frequency and +relative temperature. + +AVS is notified before and after the frequency change. AVS uses this +information to correct the voltage and correctly maintain the operating voltage +table. + +AVS manages the voltage in a background work queue. Every 50ms, AVS checks if +the HW recommends a voltage decrease or increase. The voltage table is updated +and the voltage is changed. + +AVS is designed to never underestimate the required operating voltage. +Several fail safes are implemented to ensure the minimum operating +voltage is maintained. + +1. The AVS HW is tuned to overestimate minimum voltage. This +overestimate provides extra operating margin. +2. Initial operating conditions are chosen to be more conservative +than equivalent static operating conditions. This ensures we always +approach the optimal voltage from the same direction. +3. Voltage is adjusted in 25mV increments. This adjustment is less than +the margin built into the AVS HW. This ensures we do not undershoot the +voltage. +4. Although not expected, if the HW requests an increase in voltage for a +single operating frequency, the voltage is increased in every frequency for +that temperature. This allows us to retry approaching the operating minimum. + +If the AVS circuitry requests a voltage increase at the maximum operating +voltage, the request is noted in the kernel log, but the request is ignored. +This condition is never expected to happen. + +Design +====== + +Reduce CPU operating voltage +Never allow CPU voltage to be less than required for proper operation +Immediate voltage changes as required for frequency changes. +Periodic management of CPU voltage +Minimal CPU overhead + + +The HW design team designed the AVS ring oscillator configuration to ensure +a proper operating voltage margin, while safely allowing reduction in CPU +operating voltage. This is implemented by the AVS delay synthesizer +configuration magic number. + +AVS is logically a superset of SVS. Therefore, AVS is implemented as an add-on +to the Static Voltage Scaling driver. + +AVS manages the CPU voltage exclusively. + +When the frequency changes AVS is notified before and after the frequency +change. This allows AVS to increase the voltage before the operating voltage +is too low. It allows AVS to drop the voltage as soon as the frequency +transition is complete. Finally it allows the AVS background processing to +be aware that the operating conditions are not stable. + +AVS manages the voltage in a background work queue. The design uses a +deferrable delayed work queue to schedule background work. The deferrable +queue was chosen to minimize CPU wakeups. This queue type will not wake the +CPU from idle, but will defer until the CPU is woken. + + +Power Management +================ + +AVS is part of the power management architecture for Scorpion. AVS manages +CPU supply voltage. + +AVS is aware of CPU frequency changes. These changes are initiated by +CPUFREQ, WFI, idle power collapse and suspend. + +AVS CP15 registers are preserved through power collapse. + + +SMP/multi-core +============== + +ARCH_SM_SCORPION is not a multicore architecture. It is difficult to +anticipate the changes in HW and SW required to support SMP. + +Security +======== + +None + +Performance +=========== + +None + +Interface +========= + +There is no general purpose interface to AVS. The sole client is SVS +(acpuclock-8x50.c). AVS is initialized and torn down by the +avs_init(...) and avs_exit(...) functions. AVS is notified of CPU +frequency changes by avs_adjust_freq(...). + +Driver parameters +================= + +None + +Config options +============== + +MSM_CPU_AVS enables the driver. + +Dependencies +============ + +AVS is built on top of the Static Voltage Scaling driver (SVS) + +Once AVS is initialized. AVS assumes it is the only process changing the +supply voltage. Other coprocessors must not change the Scorpion voltage. +The bootloader must not change the Scorpion Voltage when warm booting from +power collapse. + +User space utilities +==================== + +None + +Other +===== + +None + +Known issues +============ + +None + +To do +===== + +AVS needs to support future Scorpion chipsets. The implementation will be +parameterized, using board files, as new chipset support is added. + + + diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index b3993683e12bc..2fdd10920a7d8 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -399,6 +399,20 @@ config HTC_ONMODE_CHARGING default n bool "Low-power hibernate charging support" +-config QSD_SVS + bool "QSD Static Voltage Scaling" + depends on (ARCH_MSM_SCORPION) + default y + help + Enables static voltage scaling using the TPS65023 PMIC. + +config QSD_PMIC_DEFAULT_DCDC1 + int "PMIC default output voltage" + depends on (ARCH_MSM_SCORPION) + default 1275 + help + This is the PMIC voltage at Linux kernel boot. + config CACHE_FLUSH_RANGE_LIMIT hex "Cache flush range limit" default 0x40000 @@ -648,6 +662,15 @@ config AXI_SCREEN_POLICY help Simple AXI scaling based on screen ON/OFF and PWRC. +config MSM_CPU_AVS + bool "Enable Adaptive Voltage Scaling (AVS)" + depends on (ARCH_MSM_SCORPION && QSD_SVS) + default y + help + This enables the Adaptive Voltage Scaling feature of + Qualcomm ARMv7 CPUs. It adjusts the voltage for each frequency + based on feedback from three ring oscillators in the CPU. + config MSM_ADSP depends on ARCH_MSM7X00A tristate "MSM ADSP driver" diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index c768bd1d45d33..b0115aa4ddea9 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_ARCH_MSM_SCORPION) += arch-init-scorpion.o obj-$(CONFIG_ARCH_QSD8X50) += acpuclock-qsd8x50.o obj-$(CONFIG_ARCH_MSM7X30) += acpuclock-7x30.o +obj-$(CONFIG_MSM_CPU_AVS) += avs.o avs_hw.o ifndef CONFIG_ARCH_MSM8X60 obj-y += irq.o diff --git a/arch/arm/mach-msm/acpuclock-qsd8x50.c b/arch/arm/mach-msm/acpuclock-qsd8x50.c index e7d5f2efd3ea9..092e67e28a72f 100644 --- a/arch/arm/mach-msm/acpuclock-qsd8x50.c +++ b/arch/arm/mach-msm/acpuclock-qsd8x50.c @@ -27,6 +27,7 @@ #include #include "acpuclock.h" +#include "avs.h" #include "proc_comm.h" #if 0 @@ -161,6 +162,7 @@ struct clock_state { unsigned long wait_for_irq_khz; struct clk* clk_ebi1; struct regulator *regulator; + int (*acpu_set_vdd) (int mvolts); }; static struct clock_state drv_state = { 0 }; @@ -283,27 +285,41 @@ static void select_clock(unsigned src, unsigned config) writel(val | ((src & 3) << 1), SPSS_CLK_SEL_ADDR); } -static int acpuclk_set_vdd_level(int vdd) +static int acpu_set_vdd(int vdd) { if (!drv_state.regulator || IS_ERR(drv_state.regulator)) { drv_state.regulator = regulator_get(NULL, "acpu_vcore"); if (IS_ERR(drv_state.regulator)) { - pr_info("acpuclk_set_vdd_level %d no regulator\n", vdd); + pr_info("acpu_set_vdd %d no regulator\n", vdd); /* Assume that the PMIC supports scaling the processor * to its maximum frequency at its default voltage. */ - return 0; + return -ENODEV; } - pr_info("acpuclk_set_vdd_level got regulator\n"); + pr_info("acpu_set_vdd got regulator\n"); } vdd *= 1000; /* mV -> uV */ return regulator_set_voltage(drv_state.regulator, vdd, vdd); } +static int acpuclk_set_vdd_level(int vdd) +{ + if (drv_state.acpu_set_vdd) + return drv_state.acpu_set_vdd(vdd); + else { + /* Assume that the PMIC supports scaling the processor + * to its maximum frequency at its default voltage. + */ + return 0; + } +} + int acpuclk_set_rate(unsigned long rate, enum setrate_reason reason) { struct clkctl_acpu_speed *cur, *next; unsigned long flags; + int rc = 0; + int freq_index = 0; cur = drv_state.current_speed; @@ -322,16 +338,29 @@ int acpuclk_set_rate(unsigned long rate, enum setrate_reason reason) if (next->acpu_khz == 0) return -EINVAL; next++; + freq_index++; } if (reason == SETRATE_CPUFREQ) { mutex_lock(&drv_state.lock); +#ifdef CONFIG_MSM_CPU_AVS + /* Notify avs before changing frequency */ + rc = avs_adjust_freq(freq_index, 1); + if (rc) { + printk(KERN_ERR + "acpuclock: Unable to increase ACPU " + "vdd: %d.\n", (int) rate); + mutex_unlock(&drv_state.lock); + return rc; + } +#endif /* Increase VDD if needed. */ if (next->vdd > cur->vdd) { - if (acpuclk_set_vdd_level(next->vdd)) { + rc = acpuclk_set_vdd_level(next->vdd); + if (rc) { pr_err("acpuclock: Unable to increase ACPU VDD from %d to %d setting rate to %d.\n", cur->vdd, next->vdd, (int) rate); mutex_unlock(&drv_state.lock); - return -EINVAL; + return rc; } } } @@ -372,9 +401,17 @@ int acpuclk_set_rate(unsigned long rate, enum setrate_reason reason) } #endif if (reason == SETRATE_CPUFREQ) { +#ifdef CONFIG_MSM_CPU_AVS + /* notify avs after changing frequency */ + rc = avs_adjust_freq(freq_index, 0); + if (rc) + printk(KERN_ERR + "acpuclock: Unable to drop ACPU vdd: %d.\n", (int) rate); +#endif /* Drop VDD level if we can. */ if (next->vdd < cur->vdd) { - if (acpuclk_set_vdd_level(next->vdd)) + rc = acpuclk_set_vdd_level(next->vdd); + if (rc) pr_err("acpuclock: Unable to drop ACPU VDD from%d to %d setting rate to %d.\n", cur->vdd, next->vdd, (int) rate); } mutex_unlock(&drv_state.lock); @@ -509,6 +546,23 @@ unsigned long acpuclk_wait_for_irq(void) return ret * 1000; } +#ifdef CONFIG_MSM_CPU_AVS +static int __init acpu_avs_init(int (*set_vdd) (int), int khz) +{ + int i; + int freq_count = 0; + int freq_index = -1; + + for (i = 0; acpu_freq_tbl[i].acpu_khz; i++) { + freq_count++; + if (acpu_freq_tbl[i].acpu_khz == khz) + freq_index = i; + } + + return avs_init(set_vdd, freq_count, freq_index); +} +#endif + void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *clkdata) { spin_lock_init(&acpu_lock); @@ -519,6 +573,7 @@ void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *clkdata) drv_state.vdd_switch_time_us = clkdata->vdd_switch_time_us; drv_state.power_collapse_khz = clkdata->power_collapse_khz; drv_state.wait_for_irq_khz = clkdata->wait_for_irq_khz; + drv_state.acpu_set_vdd = acpu_set_vdd; if (clkdata->mpll_khz) acpu_mpll->acpu_khz = clkdata->mpll_khz; @@ -529,4 +584,11 @@ void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *clkdata) #ifndef CONFIG_AXI_SCREEN_POLICY clk_set_rate(drv_state.clk_ebi1, drv_state.current_speed->axiclk_khz * 1000); #endif +#ifdef CONFIG_MSM_CPU_AVS + if (!acpu_avs_init(drv_state.acpu_set_vdd, + drv_state.current_speed->acpu_khz)) { + /* avs init successful. avs will handle voltage changes */ + drv_state.acpu_set_vdd = NULL; + } +#endif } diff --git a/arch/arm/mach-msm/avs.c b/arch/arm/mach-msm/avs.c new file mode 100644 index 0000000000000..c197efdfa3e02 --- /dev/null +++ b/arch/arm/mach-msm/avs.c @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "avs.h" + +#define AVSDSCR_INPUT 0x01004860 /* magic # from circuit designer */ +#define TSCSR_INPUT 0x00000001 /* enable temperature sense */ + +#define TEMPRS 16 /* total number of temperature regions */ +#define GET_TEMPR() (avs_get_tscsr() >> 28) /* scale TSCSR[CTEMP] to regions */ + +struct mutex avs_lock; + +static struct avs_state_s +{ + u32 freq_cnt; /* Frequencies supported list */ + short *avs_v; /* Dyanmically allocated storage for + * 2D table of voltages over temp & + * freq. Used as a set of 1D tables. + * Each table is for a single temp. + * For usage see avs_get_voltage + */ + int (*set_vdd) (int); /* Function Ptr for setting voltage */ + int changing; /* Clock frequency is changing */ + u32 freq_idx; /* Current frequency index */ + int vdd; /* Current ACPU voltage */ +} avs_state; + +struct clkctl_acpu_speed { + unsigned acpu_khz; + int min_vdd; + int max_vdd; +}; + + +struct clkctl_acpu_speed acpu_vdd_tbl[] = { + { 19200, 950, 950 }, + { 128000, 950, 950 }, + { 245000, 950, 975 }, + { 256000, 950, 975 }, + { 384000, 950, 1000 }, + { 422400, 950, 1000 }, + { 460800, 975, 1025 }, + { 499200, 1000, 1050 }, + { 537600, 1000, 1050 }, + { 576000, 1025, 1075 }, + { 614400, 1050, 1100 }, + { 652800, 1075, 1125 }, + { 691200, 1100, 1150 }, + { 729600, 1125, 1175 }, + { 768000, 1150, 1200 }, + { 806400, 1175, 1225 }, + { 844800, 1200, 1250 }, + { 883200, 1200, 1250 }, + { 921600, 1225, 1275 }, + { 960000, 1225, 1275 }, + { 998400, 1225, 1275 }, + { 0 }, +}; + +/* + * Update the AVS voltage vs frequency table, for current temperature + * Adjust based on the AVS delay circuit hardware status + */ +static void avs_update_voltage_table(short *vdd_table) +{ + u32 avscsr; + int cpu; + int vu; + int l2; + int i; + u32 cur_freq_idx; + short cur_voltage; + + cur_freq_idx = avs_state.freq_idx; + cur_voltage = avs_state.vdd; + + avscsr = avs_test_delays(); + AVSDEBUG("avscsr=%x, avsdscr=%x\n", avscsr, avs_get_avsdscr()); + + /* + * Read the results for the various unit's AVS delay circuits + * 2=> up, 1=>down, 0=>no-change + */ + cpu = ((avscsr >> 23) & 2) + ((avscsr >> 16) & 1); + vu = ((avscsr >> 28) & 2) + ((avscsr >> 21) & 1); + l2 = ((avscsr >> 29) & 2) + ((avscsr >> 22) & 1); + + if ((cpu == 3) || (vu == 3) || (l2 == 3)) { + printk(KERN_ERR "AVS: Dly Synth O/P error\n"); + } else if ((cpu == 2) || (l2 == 2) || (vu == 2)) { + /* + * even if one oscillator asks for up, increase the voltage, + * as its an indication we are running outside the + * critical acceptable range of v-f combination. + */ + AVSDEBUG("cpu=%d l2=%d vu=%d\n", cpu, l2, vu); + AVSDEBUG("Voltage up at %d\n", cur_freq_idx); + + if (cur_voltage >= VOLTAGE_MAX || cur_voltage >= acpu_vdd_tbl[cur_freq_idx].max_vdd) + printk(KERN_ERR + "AVS: Voltage can not get high enough!\n"); + + /* Raise the voltage for all frequencies */ + for (i = 0; i < avs_state.freq_cnt; i++) { + vdd_table[i] = cur_voltage + VOLTAGE_STEP; + if (vdd_table[i] > VOLTAGE_MAX) + vdd_table[i] = VOLTAGE_MAX; + else if (vdd_table[i] > acpu_vdd_tbl[i].max_vdd) + vdd_table[i] = acpu_vdd_tbl[i].max_vdd; + } + } else if ((cpu == 1) && (l2 == 1) && (vu == 1)) { + if ((cur_voltage - VOLTAGE_STEP >= VOLTAGE_MIN) && + (cur_voltage - VOLTAGE_STEP >= acpu_vdd_tbl[cur_freq_idx].min_vdd) && + (cur_voltage <= vdd_table[cur_freq_idx])) { + vdd_table[cur_freq_idx] = cur_voltage - VOLTAGE_STEP; + AVSDEBUG("Voltage down for %d and lower levels\n", + cur_freq_idx); + + /* clamp to this voltage for all lower levels */ + for (i = 0; i < cur_freq_idx; i++) { + if (vdd_table[i] > vdd_table[cur_freq_idx]) + vdd_table[i] = vdd_table[cur_freq_idx]; + } + } + } +} + +/* + * Return the voltage for the target performance freq_idx and optionally + * use AVS hardware to check the present voltage freq_idx + */ +static short avs_get_target_voltage(int freq_idx, bool update_table) +{ + unsigned cur_tempr = GET_TEMPR(); + unsigned temp_index = cur_tempr*avs_state.freq_cnt; + + /* Table of voltages vs frequencies for this temp */ + short *vdd_table = avs_state.avs_v + temp_index; + + if (update_table) + avs_update_voltage_table(vdd_table); + + if (vdd_table[freq_idx] > acpu_vdd_tbl[freq_idx].max_vdd) { + pr_info("%dmV too high for %d.\n", vdd_table[freq_idx], acpu_vdd_tbl[freq_idx].acpu_khz); + vdd_table[freq_idx] = acpu_vdd_tbl[freq_idx].max_vdd; + } + if (vdd_table[freq_idx] < acpu_vdd_tbl[freq_idx].min_vdd) { + pr_info("%dmV too low for %d.\n", vdd_table[freq_idx], acpu_vdd_tbl[freq_idx].acpu_khz); + vdd_table[freq_idx] = acpu_vdd_tbl[freq_idx].min_vdd; + } + + return vdd_table[freq_idx]; +} + + +/* + * Set the voltage for the freq_idx and optionally + * use AVS hardware to update the voltage + */ +static int avs_set_target_voltage(int freq_idx, bool update_table) +{ + int rc = 0, new_voltage; + + if (freq_idx < 0 || freq_idx >= avs_state.freq_cnt) { + AVSDEBUG("Out of range :%d\n", freq_idx); + return -EINVAL; + } + + new_voltage = avs_get_target_voltage(freq_idx, update_table); + if (avs_state.vdd != new_voltage) { + /*AVSDEBUG*/pr_info("AVS setting V to %d mV @%d MHz\n", + new_voltage, acpu_vdd_tbl[freq_idx].acpu_khz / 1000); + rc = avs_state.set_vdd(new_voltage); + if (rc) + return rc; + avs_state.vdd = new_voltage; + } + return rc; +} + +/* + * Notify avs of clk frquency transition begin & end + */ +int avs_adjust_freq(u32 freq_idx, int begin) +{ + int rc = 0; + + if (!avs_state.set_vdd) { + /* AVS not initialized */ + return 0; + } + + if (freq_idx < 0 || freq_idx >= avs_state.freq_cnt) { + AVSDEBUG("Out of range :%d\n", freq_idx); + return -EINVAL; + } + + mutex_lock(&avs_lock); + if ((begin && (freq_idx > avs_state.freq_idx)) || + (!begin && (freq_idx < avs_state.freq_idx))) { + /* Update voltage before increasing frequency & + * after decreasing frequency + */ + rc = avs_set_target_voltage(freq_idx, 0); + if (rc) + goto aaf_out; + + avs_state.freq_idx = freq_idx; + } + avs_state.changing = begin; +aaf_out: + mutex_unlock(&avs_lock); + + return rc; +} + + +static struct delayed_work avs_work; +static struct workqueue_struct *kavs_wq; +#define AVS_DELAY ((CONFIG_HZ * 50 + 999) / 1000) + +static void do_avs_timer(struct work_struct *work) +{ + int cur_freq_idx; + + mutex_lock(&avs_lock); + if (!avs_state.changing) { + /* Only adjust the voltage if clk is stable */ + cur_freq_idx = avs_state.freq_idx; + avs_set_target_voltage(cur_freq_idx, 1); + } + mutex_unlock(&avs_lock); + queue_delayed_work_on(0, kavs_wq, &avs_work, AVS_DELAY); +} + + +static void __init avs_timer_init(void) +{ + INIT_DELAYED_WORK_DEFERRABLE(&avs_work, do_avs_timer); + queue_delayed_work_on(0, kavs_wq, &avs_work, AVS_DELAY); +} + +static void __exit avs_timer_exit(void) +{ + cancel_delayed_work(&avs_work); +} + +static int __init avs_work_init(void) +{ + kavs_wq = create_workqueue("avs"); + if (!kavs_wq) { + printk(KERN_ERR "AVS initialization failed\n"); + return -EFAULT; + } + avs_timer_init(); + + return 1; +} + +static void __exit avs_work_exit(void) +{ + avs_timer_exit(); + destroy_workqueue(kavs_wq); +} + +int __init avs_init(int (*set_vdd)(int), u32 freq_cnt, u32 freq_idx) +{ + int i; + + mutex_init(&avs_lock); + + if (freq_cnt == 0) + return -EINVAL; + + avs_state.freq_cnt = freq_cnt; + + if (freq_idx >= avs_state.freq_cnt) + return -EINVAL; + + avs_state.avs_v = kmalloc(TEMPRS * avs_state.freq_cnt * + sizeof(avs_state.avs_v[0]), GFP_KERNEL); + + if (avs_state.avs_v == 0) + return -ENOMEM; + + for (i = 0; i < TEMPRS*avs_state.freq_cnt; i++) + avs_state.avs_v[i] = VOLTAGE_MAX; + + avs_reset_delays(AVSDSCR_INPUT); + avs_set_tscsr(TSCSR_INPUT); + + avs_state.set_vdd = set_vdd; + avs_state.changing = 0; + avs_state.freq_idx = -1; + avs_state.vdd = -1; + avs_adjust_freq(freq_idx, 0); + + avs_work_init(); + + return 0; +} + +void __exit avs_exit() +{ + avs_work_exit(); + + kfree(avs_state.avs_v); +} + + diff --git a/arch/arm/mach-msm/avs.h b/arch/arm/mach-msm/avs.h new file mode 100644 index 0000000000000..98aa311633a87 --- /dev/null +++ b/arch/arm/mach-msm/avs.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AVS_H +#define AVS_H + +#define VOLTAGE_MIN 950 /* mV */ +#define VOLTAGE_MAX 1275 +#define VOLTAGE_STEP 5 + +int __init avs_init(int (*set_vdd)(int), u32 freq_cnt, u32 freq_idx); +void __exit avs_exit(void); + +int avs_adjust_freq(u32 freq_index, int begin); + +/* Routines exported from avs_hw.S */ +u32 avs_test_delays(void); +u32 avs_reset_delays(u32 avsdscr); +u32 avs_get_avscsr(void); +u32 avs_get_avsdscr(void); +u32 avs_get_tscsr(void); +void avs_set_tscsr(u32 to_tscsr); + +/*#define AVSDEBUG(x...) pr_info("AVS: " x);*/ +#define AVSDEBUG(...) + +#endif /* AVS_H */ diff --git a/arch/arm/mach-msm/avs_hw.S b/arch/arm/mach-msm/avs_hw.S new file mode 100644 index 0000000000000..a9429b3806989 --- /dev/null +++ b/arch/arm/mach-msm/avs_hw.S @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + + .text + + .global avs_test_delays +avs_test_delays: + +/* Read r1=CPMR and enable Never Sleep for VSLPDLY */ + mrc p15, 7, r1, c15, c0, 5 + orr r12, r1, #3, 24 + mcr p15, 7, r12, c15, c0, 5 + +/* Read r2=CPACR and enable full access to CP10 and CP11 space */ + mrc p15, 0, r2, c1, c0, 2 + orr r12, r2, #(0xf << 20) + mcr p15, 0, r12, c1, c0, 2 + isb + +/* Read r3=FPEXC and or in FP enable, VFP/ASE enable = FPEXC[30]; */ + fmrx r3, fpexc + orr r12, r3, #1, 2 + fmxr fpexc, r12 + +/* + * Do floating-point operations to prime the VFP pipeline. Use + * fcpyd d0, d0 as a floating point nop. This avoids changing VFP + * state. + */ + fcpyd d0, d0 + fcpyd d0, d0 + fcpyd d0, d0 + +/* Read r0=AVSCSR to get status from CPU, VFP, and L2 ring oscillators */ + mrc p15, 7, r0, c15, c1, 7 + +/* Restore FPEXC */ + fmxr fpexc, r3 + +/* Restore CPACR */ + MCR p15, 0, r2, c1, c0, 2 + +/* Restore CPMR */ + mcr p15, 7, r1, c15, c0, 5 + isb + + bx lr + + + + + .global avs_get_avscsr +/* Read r0=AVSCSR to get status from CPU, VFP, and L2 ring oscillators */ + +avs_get_avscsr: + mrc p15, 7, r0, c15, c1, 7 + bx lr + + .global avs_get_avsdscr +/* Read r0=AVSDSCR to get the AVS Delay Synthesizer control settings */ + +avs_get_avsdscr: + mrc p15, 7, r0, c15, c0, 6 + bx lr + + + + + .global avs_get_tscsr +/* Read r0=TSCSR to get temperature sensor control and status */ + +avs_get_tscsr: + mrc p15, 7, r0, c15, c1, 0 + bx lr + + .global avs_set_tscsr +/* Write TSCSR=r0 to set temperature sensor control and status */ + +avs_set_tscsr: + mcr p15, 7, r0, c15, c1, 0 + bx lr + + + + + + .global avs_reset_delays +avs_reset_delays: + +/* AVSCSR(0x61) to enable CPU, V and L2 AVS module */ + mov r3, #0x61 + mcr p15, 7, r3, c15, c1, 7 + +/* AVSDSCR(dly) to program delay */ + mcr p15, 7, r0, c15, c0, 6 + +/* Read r0=AVSDSCR */ + mrc p15, 7, r0, c15, c0, 6 + + bx lr + + .end + + diff --git a/arch/arm/mach-msm/idle-v7.S b/arch/arm/mach-msm/idle-v7.S index 3fd510f9450a5..70dfb2b6c7c0d 100644 --- a/arch/arm/mach-msm/idle-v7.S +++ b/arch/arm/mach-msm/idle-v7.S @@ -41,6 +41,17 @@ ENTRY(msm_pm_collapse) mrc p15, 0, r9, c13, c0, 3 /* TPIDRURO */ mrc p15, 0, ip, c13, c0, 1 /* context ID */ stmia r0!, {r1-r9, ip} +#ifdef CONFIG_MSM_CPU_AVS + mrc p15, 7, r1, c15, c1, 7 /* AVSCSR is the Adaptive Voltage Scaling + * Control and Status Register */ + mrc p15, 7, r2, c15, c0, 6 /* AVSDSCR is the Adaptive Voltage + * Scaling Delay Synthesizer Control + * Register */ + mrc p15, 7, r3, c15, c1, 0 /* TSCSR is the Temperature Status and + * Control Register + */ + stmia r0!, {r1-r3} +#endif #ifdef CONFIG_VFP .fpu neon @@ -109,6 +120,12 @@ ENTRY(msm_pm_collapse_exit) adr r3, msm_pm_collapse_exit add r1, r1, r3 sub r1, r1, r2 +#ifdef CONFIG_MSM_CPU_AVS + ldmdb r1!, {r2-r4} + mcr p15, 7, r4, c15, c1, 0 /* TSCSR */ + mcr p15, 7, r3, c15, c0, 6 /* AVSDSCR */ + mcr p15, 7, r2, c15, c1, 7 /* AVSCSR */ +#endif #ifdef CONFIG_VFP mrc p15, 0, r2, c1, c0, 2 /* Read CP Access Control Register */ orr r2, r2, #0x00F00000 /* Enable full access for p10,11 */ @@ -192,6 +209,9 @@ msm_pm_pa_to_va: saved_state: .space 4 * 11 /* r4-14 */ .space 4 * 10 /* cp15 */ +#ifdef CONFIG_MSM_CPU_AVS + .space 4 * 3 /* AVS control registers */ +#endif #ifdef CONFIG_VFP .space 8 * 32 /* VFP working registers */ .space 4 * 2 /* VFP state registers */ From 88a0f733b85a0e39f44bc0944de8329dad69b1e6 Mon Sep 17 00:00:00 2001 From: Raymond Golo Date: Fri, 18 Feb 2011 10:50:35 +0800 Subject: [PATCH 2364/2556] CodeAurora AVS updates. Conflicts: arch/arm/mach-msm/Kconfig --- arch/arm/mach-msm/Kconfig | 19 +++++++--- arch/arm/mach-msm/Makefile | 3 +- arch/arm/mach-msm/avs.h | 73 +++++++++++++++++++++++++++----------- arch/arm/mach-msm/avs_hw.S | 27 ++++++++++---- 4 files changed, 90 insertions(+), 32 deletions(-) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 2fdd10920a7d8..15d6fc2be21f7 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -663,14 +663,25 @@ config AXI_SCREEN_POLICY Simple AXI scaling based on screen ON/OFF and PWRC. config MSM_CPU_AVS - bool "Enable Adaptive Voltage Scaling (AVS)" + bool "Enable software controlled Adaptive Voltage Scaling (AVS)" depends on (ARCH_MSM_SCORPION && QSD_SVS) - default y + depends on ARCH_QSD8X50 + default n + select MSM_AVS_HW help - This enables the Adaptive Voltage Scaling feature of - Qualcomm ARMv7 CPUs. It adjusts the voltage for each frequency + This enables the s/w control of Adaptive Voltage Scaling feature + in Qualcomm ARMv7 CPUs. It adjusts the voltage for each frequency based on feedback from three ring oscillators in the CPU. +config MSM_AVS_HW + bool "Enable Adaptive Voltage Scaling (AVS)" + default n + help + Enable AVS hardware to fine tune voltage at each frequency. The + AVS hardware blocks associated with each Qualcomm ARMv7 cores can + fine tune the voltages based on the feedback from the ring + oscillators. + config MSM_ADSP depends on ARCH_MSM7X00A tristate "MSM ADSP driver" diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index b0115aa4ddea9..c5ef66eb4d6b1 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -22,7 +22,8 @@ obj-$(CONFIG_ARCH_MSM_SCORPION) += arch-init-scorpion.o obj-$(CONFIG_ARCH_QSD8X50) += acpuclock-qsd8x50.o obj-$(CONFIG_ARCH_MSM7X30) += acpuclock-7x30.o -obj-$(CONFIG_MSM_CPU_AVS) += avs.o avs_hw.o +obj-$(CONFIG_MSM_CPU_AVS) += avs.o +obj-$(CONFIG_MSM_AVS_HW) += avs_hw.o ifndef CONFIG_ARCH_MSM8X60 obj-y += irq.o diff --git a/arch/arm/mach-msm/avs.h b/arch/arm/mach-msm/avs.h index 98aa311633a87..4e6898359842c 100644 --- a/arch/arm/mach-msm/avs.h +++ b/arch/arm/mach-msm/avs.h @@ -2,28 +2,29 @@ * Copyright (c) 2009, Code Aurora Forum. All rights reserved. * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: + * modification, are permitted provided that the following conditions are + * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Code Aurora nor - * the names of its contributors may be used to endorse or promote - * products derived from this software without specific prior written - * permission. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef AVS_H @@ -39,14 +40,46 @@ void __exit avs_exit(void); int avs_adjust_freq(u32 freq_index, int begin); /* Routines exported from avs_hw.S */ +#ifdef CONFIG_MSM_CPU_AVS u32 avs_test_delays(void); +#else +static inline u32 avs_test_delays(void) +{ return 0; } +#endif + +#ifdef CONFIG_MSM_AVS_HW u32 avs_reset_delays(u32 avsdscr); u32 avs_get_avscsr(void); u32 avs_get_avsdscr(void); u32 avs_get_tscsr(void); -void avs_set_tscsr(u32 to_tscsr); +void avs_set_tscsr(u32 to_tscsr); +void avs_disable(void); +#else +static inline u32 avs_reset_delays(u32 avsdscr) +{ return 0; } +static inline u32 avs_get_avscsr(void) +{ return 0; } +static inline u32 avs_get_avsdscr(void) +{ return 0; } +static inline u32 avs_get_tscsr(void) +{ return 0; } +static inline void avs_set_tscsr(u32 to_tscsr) {} +static inline void avs_disable(void) {} +#endif /*#define AVSDEBUG(x...) pr_info("AVS: " x);*/ #define AVSDEBUG(...) +#define AVS_DISABLE(cpu) do { \ + if (get_cpu() == (cpu)) \ + avs_disable(); \ + put_cpu(); \ + } while (0); + +#define AVS_ENABLE(cpu, x) do { \ + if (get_cpu() == (cpu)) \ + avs_reset_delays((x)); \ + put_cpu(); \ + } while (0); + #endif /* AVS_H */ diff --git a/arch/arm/mach-msm/avs_hw.S b/arch/arm/mach-msm/avs_hw.S index a9429b3806989..5e1530e4672b8 100644 --- a/arch/arm/mach-msm/avs_hw.S +++ b/arch/arm/mach-msm/avs_hw.S @@ -18,6 +18,7 @@ .text +#ifdef CONFIG_MSM_CPU_AVS .global avs_test_delays avs_test_delays: @@ -60,8 +61,7 @@ avs_test_delays: isb bx lr - - +#endif .global avs_get_avscsr @@ -102,17 +102,30 @@ avs_set_tscsr: .global avs_reset_delays avs_reset_delays: -/* AVSCSR(0x61) to enable CPU, V and L2 AVS module */ - mov r3, #0x61 - mcr p15, 7, r3, c15, c1, 7 - /* AVSDSCR(dly) to program delay */ mcr p15, 7, r0, c15, c0, 6 /* Read r0=AVSDSCR */ mrc p15, 7, r0, c15, c0, 6 - bx lr +/* AVSCSR(0x61) to enable CPU, V and L2 AVS module */ + mov r3, #0x61 + mcr p15, 7, r3, c15, c1, 7 + + bx lr + + + + .global avs_disable +avs_disable: + +/* Clear AVSCSR */ + mov r0, #0 + +/* Write AVSCSR */ + mcr p15, 7, r0, c15, c1, 7 + + bx lr .end From d7bd2b1863e85200ba53a18919cf61fcaf49d58c Mon Sep 17 00:00:00 2001 From: intersectR Date: Thu, 23 Sep 2010 17:02:37 +0800 Subject: [PATCH 2365/2556] Copy parts of the regulator stack from CodeAurora to test AVS stability against. --- arch/arm/mach-msm/acpuclock-qsd8x50.c | 23 +++++++++++-- drivers/regulator/tps65023-regulator.c | 46 ++++++++++++++++++++++++++ include/linux/regulator/driver.h | 2 ++ 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/acpuclock-qsd8x50.c b/arch/arm/mach-msm/acpuclock-qsd8x50.c index 092e67e28a72f..d82c4220c787f 100644 --- a/arch/arm/mach-msm/acpuclock-qsd8x50.c +++ b/arch/arm/mach-msm/acpuclock-qsd8x50.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -61,6 +62,17 @@ struct clkctl_acpu_speed { static unsigned long max_axi_rate; +struct regulator { + struct device *dev; + struct list_head list; + int uA_load; + int min_uV; + int max_uV; + char *supply_name; + struct device_attribute dev_attr; + struct regulator_dev *rdev; +}; + /* clock sources */ #define CLK_TCXO 0 /* 19.2 MHz */ #define CLK_GLOBAL_PLL 1 /* 768 MHz */ @@ -287,6 +299,8 @@ static void select_clock(unsigned src, unsigned config) static int acpu_set_vdd(int vdd) { + int rc = 0; + if (!drv_state.regulator || IS_ERR(drv_state.regulator)) { drv_state.regulator = regulator_get(NULL, "acpu_vcore"); if (IS_ERR(drv_state.regulator)) { @@ -298,8 +312,13 @@ static int acpu_set_vdd(int vdd) } pr_info("acpu_set_vdd got regulator\n"); } - vdd *= 1000; /* mV -> uV */ - return regulator_set_voltage(drv_state.regulator, vdd, vdd); + + rc = tps65023_set_dcdc1_level(drv_state.regulator->rdev, vdd); + + if (rc == -ENODEV && vdd <= CONFIG_QSD_PMIC_DEFAULT_DCDC1) + return 0; + + return rc; } static int acpuclk_set_vdd_level(int vdd) diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index 368c1dc08d077..b16963b5c0c6b 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -403,6 +403,52 @@ static int tps65023_dcdc_set_voltage(struct regulator_dev *dev, return rv; } +/* TPS65023_registers */ +#define TPS65023_VERSION 0 +#define TPS65023_PGOODZ 1 +#define TPS65023_MASK 2 +#define TPS65023_REG_CTRL 3 +#define TPS65023_CON_CTRL 4 +#define TPS65023_CON_CTRL2 5 +#define TPS65023_DEFCORE 6 +#define TPS65023_DEFSLEW 7 +#define TPS65023_LDO_CTRL 8 +#define TPS65023_MAX 9 + +static struct i2c_client *tpsclient; + +int tps65023_set_dcdc1_level(struct regulator_dev *dev, int mvolts) +{ + int val; + int ret; + struct tps_pmic *tps; + + if (!tpsclient) { + tps = rdev_get_drvdata(dev); + tpsclient = tps->client; + } + + if (!tpsclient) + return -ENODEV; + + if (mvolts < 800 || mvolts > 1600) + return -EINVAL; + + if (mvolts == 1600) + val = 0x1F; + else + val = ((mvolts - 800)/25) & 0x1F; + + ret = i2c_smbus_write_byte_data(tpsclient, TPS65023_DEFCORE, val); + + if (!ret) + ret = i2c_smbus_write_byte_data(tpsclient, + TPS65023_CON_CTRL2, 0x80); + + return ret; +} +EXPORT_SYMBOL(tps65023_set_dcdc1_level); + static int tps65023_ldo_get_voltage(struct regulator_dev *dev) { struct tps_pmic *tps = rdev_get_drvdata(dev); diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index b8ed16a33c47f..cf302297eeded 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -217,4 +217,6 @@ int regulator_mode_to_status(unsigned int); void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data); +extern int tps65023_set_dcdc1_level(struct regulator_dev *dev, int mvolts); + #endif From 8be70cdff050f4773c78b15aada5cb724d854e7a Mon Sep 17 00:00:00 2001 From: "Raymond Golo (intersectRaven)" Date: Sun, 15 May 2011 16:35:28 -0400 Subject: [PATCH 2366/2556] Modify AVS to try up to 5 times whenever voltage setting fails --- arch/arm/mach-msm/avs.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/avs.c b/arch/arm/mach-msm/avs.c index c197efdfa3e02..7837e2ad7bc2e 100644 --- a/arch/arm/mach-msm/avs.c +++ b/arch/arm/mach-msm/avs.c @@ -223,7 +223,7 @@ static short avs_get_target_voltage(int freq_idx, bool update_table) */ static int avs_set_target_voltage(int freq_idx, bool update_table) { - int rc = 0, new_voltage; + int ctr = 5, rc = 0, new_voltage; if (freq_idx < 0 || freq_idx >= avs_state.freq_cnt) { AVSDEBUG("Out of range :%d\n", freq_idx); @@ -235,6 +235,12 @@ static int avs_set_target_voltage(int freq_idx, bool update_table) /*AVSDEBUG*/pr_info("AVS setting V to %d mV @%d MHz\n", new_voltage, acpu_vdd_tbl[freq_idx].acpu_khz / 1000); rc = avs_state.set_vdd(new_voltage); + while(rc && ctr) { + rc = avs_state.set_vdd(new_voltage); + ctr--; + if(rc) + mdelay(1); + } if (rc) return rc; avs_state.vdd = new_voltage; From 82dc65e6e7c992baff16a340ed915bd890de2674 Mon Sep 17 00:00:00 2001 From: Raymond Golo Date: Tue, 12 Oct 2010 07:57:23 +0800 Subject: [PATCH 2367/2556] Add debug message when AVS fails to set voltages and tries again. --- arch/arm/mach-msm/avs.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-msm/avs.c b/arch/arm/mach-msm/avs.c index 7837e2ad7bc2e..8df5c1d6b839b 100644 --- a/arch/arm/mach-msm/avs.c +++ b/arch/arm/mach-msm/avs.c @@ -235,12 +235,14 @@ static int avs_set_target_voltage(int freq_idx, bool update_table) /*AVSDEBUG*/pr_info("AVS setting V to %d mV @%d MHz\n", new_voltage, acpu_vdd_tbl[freq_idx].acpu_khz / 1000); rc = avs_state.set_vdd(new_voltage); - while(rc && ctr) { + while (rc && ctr) { rc = avs_state.set_vdd(new_voltage); - ctr--; - if(rc) + ctr--; + if (rc) { + printk(KERN_ERR "avs_set_target_voltage: Unable to set V to %d mV (attempt: %d)\n", new_voltage, 5 - ctr); mdelay(1); - } + } + } if (rc) return rc; avs_state.vdd = new_voltage; From afe4c412cab90a05c0c4726debb6fb3420b0ab2a Mon Sep 17 00:00:00 2001 From: Raymond Golo Date: Sun, 13 Feb 2011 07:55:18 +0800 Subject: [PATCH 2368/2556] Modify voltage limits (max) to conform with maximum stable in SVS kernels. --- arch/arm/mach-msm/avs.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-msm/avs.c b/arch/arm/mach-msm/avs.c index 8df5c1d6b839b..44ed26bf37306 100644 --- a/arch/arm/mach-msm/avs.c +++ b/arch/arm/mach-msm/avs.c @@ -97,16 +97,16 @@ struct clkctl_acpu_speed { struct clkctl_acpu_speed acpu_vdd_tbl[] = { - { 19200, 950, 950 }, - { 128000, 950, 950 }, - { 245000, 950, 975 }, - { 256000, 950, 975 }, - { 384000, 950, 1000 }, - { 422400, 950, 1000 }, - { 460800, 975, 1025 }, - { 499200, 1000, 1050 }, - { 537600, 1000, 1050 }, - { 576000, 1025, 1075 }, + { 19200, 950, 975 }, + { 128000, 950, 975 }, + { 245000, 950, 1000 }, + { 256000, 950, 1000 }, + { 384000, 950, 1025 }, + { 422400, 950, 1050 }, + { 460800, 975, 1050 }, + { 499200, 1000, 1075 }, + { 537600, 1000, 1075 }, + { 576000, 1025, 1100 }, { 614400, 1050, 1100 }, { 652800, 1075, 1125 }, { 691200, 1100, 1150 }, @@ -114,7 +114,7 @@ struct clkctl_acpu_speed acpu_vdd_tbl[] = { { 768000, 1150, 1200 }, { 806400, 1175, 1225 }, { 844800, 1200, 1250 }, - { 883200, 1200, 1250 }, + { 883200, 1200, 1275 }, { 921600, 1225, 1275 }, { 960000, 1225, 1275 }, { 998400, 1225, 1275 }, From b2238e1570f167de6941ca06ad8ddf18643a5a0e Mon Sep 17 00:00:00 2001 From: Raymond Golo Date: Tue, 22 Feb 2011 09:35:25 +0800 Subject: [PATCH 2369/2556] Remove unusable voltage in HAVS voltage table to set proper voltage scaling. --- arch/arm/mach-msm/avs.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-msm/avs.c b/arch/arm/mach-msm/avs.c index 44ed26bf37306..377a620a0580d 100644 --- a/arch/arm/mach-msm/avs.c +++ b/arch/arm/mach-msm/avs.c @@ -100,7 +100,6 @@ struct clkctl_acpu_speed acpu_vdd_tbl[] = { { 19200, 950, 975 }, { 128000, 950, 975 }, { 245000, 950, 1000 }, - { 256000, 950, 1000 }, { 384000, 950, 1025 }, { 422400, 950, 1050 }, { 460800, 975, 1050 }, From d7a71d1a0198d68fb59ca45e7d5c76ea8b63ab77 Mon Sep 17 00:00:00 2001 From: Raymond Golo Date: Tue, 25 Jan 2011 12:03:14 +0800 Subject: [PATCH 2370/2556] Remove AVS messages and restore printout to AVSDEBUG. *Add printout for successful AVS initialization. --- arch/arm/mach-msm/avs.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-msm/avs.c b/arch/arm/mach-msm/avs.c index 377a620a0580d..1884c2eaf2548 100644 --- a/arch/arm/mach-msm/avs.c +++ b/arch/arm/mach-msm/avs.c @@ -160,7 +160,7 @@ static void avs_update_voltage_table(short *vdd_table) AVSDEBUG("Voltage up at %d\n", cur_freq_idx); if (cur_voltage >= VOLTAGE_MAX || cur_voltage >= acpu_vdd_tbl[cur_freq_idx].max_vdd) - printk(KERN_ERR + AVSDEBUG(KERN_ERR "AVS: Voltage can not get high enough!\n"); /* Raise the voltage for all frequencies */ @@ -204,11 +204,11 @@ static short avs_get_target_voltage(int freq_idx, bool update_table) avs_update_voltage_table(vdd_table); if (vdd_table[freq_idx] > acpu_vdd_tbl[freq_idx].max_vdd) { - pr_info("%dmV too high for %d.\n", vdd_table[freq_idx], acpu_vdd_tbl[freq_idx].acpu_khz); + AVSDEBUG("%dmV too high for %d.\n", vdd_table[freq_idx], acpu_vdd_tbl[freq_idx].acpu_khz); vdd_table[freq_idx] = acpu_vdd_tbl[freq_idx].max_vdd; } if (vdd_table[freq_idx] < acpu_vdd_tbl[freq_idx].min_vdd) { - pr_info("%dmV too low for %d.\n", vdd_table[freq_idx], acpu_vdd_tbl[freq_idx].acpu_khz); + AVSDEBUG("%dmV too low for %d.\n", vdd_table[freq_idx], acpu_vdd_tbl[freq_idx].acpu_khz); vdd_table[freq_idx] = acpu_vdd_tbl[freq_idx].min_vdd; } @@ -231,14 +231,14 @@ static int avs_set_target_voltage(int freq_idx, bool update_table) new_voltage = avs_get_target_voltage(freq_idx, update_table); if (avs_state.vdd != new_voltage) { - /*AVSDEBUG*/pr_info("AVS setting V to %d mV @%d MHz\n", + AVSDEBUG("AVS setting V to %d mV @%d MHz\n", new_voltage, acpu_vdd_tbl[freq_idx].acpu_khz / 1000); rc = avs_state.set_vdd(new_voltage); while (rc && ctr) { rc = avs_state.set_vdd(new_voltage); ctr--; if (rc) { - printk(KERN_ERR "avs_set_target_voltage: Unable to set V to %d mV (attempt: %d)\n", new_voltage, 5 - ctr); + AVSDEBUG(KERN_ERR "avs_set_target_voltage: Unable to set V to %d mV (attempt: %d)\n", new_voltage, 5 - ctr); mdelay(1); } } @@ -323,6 +323,7 @@ static int __init avs_work_init(void) printk(KERN_ERR "AVS initialization failed\n"); return -EFAULT; } + printk(KERN_ERR "AVS initialization success\n"); avs_timer_init(); return 1; From 0cdd7e63870ba6e6ef9e985db5d9e10c0df63773 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Fri, 20 Jul 2012 14:12:19 -0500 Subject: [PATCH 2371/2556] configs: update a few netfilter things --- arch/arm/configs/evervolv_bravo_defconfig | 6 +++--- arch/arm/configs/evervolv_incrediblec_defconfig | 6 +++--- arch/arm/configs/evervolv_mahimahi_defconfig | 6 +++--- arch/arm/configs/evervolv_supersonic_defconfig | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index a256f25239b53..ce87f9c5838b6 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -641,12 +641,12 @@ CONFIG_NETFILTER_XT_TARGET_HL=y CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y -# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFLOG=y CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y CONFIG_NETFILTER_XT_TARGET_NOTRACK=y # CONFIG_NETFILTER_XT_TARGET_RATEEST is not set # CONFIG_NETFILTER_XT_TARGET_TEE is not set -# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +CONFIG_NETFILTER_XT_TARGET_TPROXY=y CONFIG_NETFILTER_XT_TARGET_TRACE=y # CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set # CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set @@ -757,7 +757,7 @@ CONFIG_IP6_NF_TARGET_LOG=y CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y -# CONFIG_IP6_NF_RAW is not set +CONFIG_IP6_NF_RAW=y # CONFIG_BRIDGE_NF_EBTABLES is not set # CONFIG_IP_DCCP is not set # CONFIG_IP_SCTP is not set diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index e73cf3ba0df53..330385868a120 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -641,12 +641,12 @@ CONFIG_NETFILTER_XT_TARGET_HL=y CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y -# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFLOG=y CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y CONFIG_NETFILTER_XT_TARGET_NOTRACK=y # CONFIG_NETFILTER_XT_TARGET_RATEEST is not set # CONFIG_NETFILTER_XT_TARGET_TEE is not set -# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +CONFIG_NETFILTER_XT_TARGET_TPROXY=y CONFIG_NETFILTER_XT_TARGET_TRACE=y # CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set # CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set @@ -757,7 +757,7 @@ CONFIG_IP6_NF_TARGET_LOG=y CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y -# CONFIG_IP6_NF_RAW is not set +CONFIG_IP6_NF_RAW=y # CONFIG_BRIDGE_NF_EBTABLES is not set # CONFIG_IP_DCCP is not set # CONFIG_IP_SCTP is not set diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index 88a8f3d3177c0..b22032e1652c4 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -641,12 +641,12 @@ CONFIG_NETFILTER_XT_TARGET_HL=y CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y -# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFLOG=y CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y CONFIG_NETFILTER_XT_TARGET_NOTRACK=y # CONFIG_NETFILTER_XT_TARGET_RATEEST is not set # CONFIG_NETFILTER_XT_TARGET_TEE is not set -# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +CONFIG_NETFILTER_XT_TARGET_TPROXY=y CONFIG_NETFILTER_XT_TARGET_TRACE=y # CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set # CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set @@ -757,7 +757,7 @@ CONFIG_IP6_NF_TARGET_LOG=y CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y -# CONFIG_IP6_NF_RAW is not set +CONFIG_IP6_NF_RAW=y # CONFIG_BRIDGE_NF_EBTABLES is not set # CONFIG_IP_DCCP is not set # CONFIG_IP_SCTP is not set diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index 8394a0ef6f089..19e6e4015cdf5 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -648,12 +648,12 @@ CONFIG_NETFILTER_XT_TARGET_HL=y CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y CONFIG_NETFILTER_XT_TARGET_LED=y CONFIG_NETFILTER_XT_TARGET_MARK=y -# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +CONFIG_NETFILTER_XT_TARGET_NFLOG=y CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y # CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set # CONFIG_NETFILTER_XT_TARGET_RATEEST is not set # CONFIG_NETFILTER_XT_TARGET_TEE is not set -# CONFIG_NETFILTER_XT_TARGET_TPROXY is not set +CONFIG_NETFILTER_XT_TARGET_TPROXY=y # CONFIG_NETFILTER_XT_TARGET_TRACE is not set # CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set # CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set @@ -764,7 +764,7 @@ CONFIG_IP6_NF_TARGET_LOG=y CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y -# CONFIG_IP6_NF_RAW is not set +CONFIG_IP6_NF_RAW=y # CONFIG_BRIDGE_NF_EBTABLES is not set # CONFIG_IP_DCCP is not set # CONFIG_IP_SCTP is not set From 361f80d81919b49a065671b7d1fd108118072b84 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 22 Jul 2012 18:39:24 -0500 Subject: [PATCH 2372/2556] msm: avs: enable debug --- arch/arm/mach-msm/avs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/avs.h b/arch/arm/mach-msm/avs.h index 4e6898359842c..1e0baa2bd59b7 100644 --- a/arch/arm/mach-msm/avs.h +++ b/arch/arm/mach-msm/avs.h @@ -67,8 +67,8 @@ static inline void avs_set_tscsr(u32 to_tscsr) {} static inline void avs_disable(void) {} #endif -/*#define AVSDEBUG(x...) pr_info("AVS: " x);*/ -#define AVSDEBUG(...) +#define AVSDEBUG(x...) pr_info("AVS: " x); +//#define AVSDEBUG(...) #define AVS_DISABLE(cpu) do { \ if (get_cpu() == (cpu)) \ From 3cee18ebea021175f9c3a9c6bcf46dee30cbe287 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 22 Jul 2012 18:43:20 -0500 Subject: [PATCH 2373/2556] configs: enable avs --- arch/arm/configs/evervolv_bravo_defconfig | 6 +++++- arch/arm/configs/evervolv_incrediblec_defconfig | 6 +++++- arch/arm/configs/evervolv_mahimahi_defconfig | 6 +++++- arch/arm/configs/evervolv_supersonic_defconfig | 6 +++++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index ce87f9c5838b6..58e7dd24350e1 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sun Jul 15 22:31:04 2012 +# Sun Jul 22 18:41:36 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -311,6 +311,8 @@ CONFIG_HTC_PWRSPLY=y # CONFIG_HTC_PWRSINK is not set # CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set # CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_QSD_SVS=y +CONFIG_QSD_PMIC_DEFAULT_DCDC1=1275 CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 CONFIG_PHYS_OFFSET=0x20000000 CONFIG_MSM7X00A_USE_GP_TIMER=y @@ -343,6 +345,8 @@ CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y CONFIG_MSM_CPU_FREQ_MAX=998400 CONFIG_MSM_CPU_FREQ_MIN=245000 # CONFIG_AXI_SCREEN_POLICY is not set +CONFIG_MSM_CPU_AVS=y +CONFIG_MSM_AVS_HW=y CONFIG_HTC_ACOUSTIC_QSD=y CONFIG_MSM_QDSP6=y CONFIG_WIFI_CONTROL_FUNC=y diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index 330385868a120..63cba6ad1eeb8 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sun Jul 15 22:28:57 2012 +# Sun Jul 22 18:42:04 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -307,6 +307,8 @@ CONFIG_HTC_BATTCHG_SMEM=y # CONFIG_HTC_PWRSINK is not set # CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set # CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_QSD_SVS=y +CONFIG_QSD_PMIC_DEFAULT_DCDC1=1275 CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 CONFIG_PHYS_OFFSET=0x20000000 CONFIG_MSM7X00A_USE_GP_TIMER=y @@ -342,6 +344,8 @@ CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y CONFIG_MSM_CPU_FREQ_MAX=998000 CONFIG_MSM_CPU_FREQ_MIN=245760 # CONFIG_AXI_SCREEN_POLICY is not set +CONFIG_MSM_CPU_AVS=y +CONFIG_MSM_AVS_HW=y CONFIG_HTC_ACOUSTIC_QSD=y CONFIG_MSM_QDSP6=y CONFIG_WIFI_CONTROL_FUNC=y diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index b22032e1652c4..ef2cb687a6302 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sun Jul 15 22:13:34 2012 +# Sun Jul 22 18:40:57 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -311,6 +311,8 @@ CONFIG_HTC_PWRSPLY=y # CONFIG_HTC_PWRSINK is not set # CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set # CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_QSD_SVS=y +CONFIG_QSD_PMIC_DEFAULT_DCDC1=1275 CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 CONFIG_PHYS_OFFSET=0x20000000 CONFIG_MSM7X00A_USE_GP_TIMER=y @@ -343,6 +345,8 @@ CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y CONFIG_MSM_CPU_FREQ_MAX=998400 CONFIG_MSM_CPU_FREQ_MIN=245000 # CONFIG_AXI_SCREEN_POLICY is not set +CONFIG_MSM_CPU_AVS=y +CONFIG_MSM_AVS_HW=y CONFIG_HTC_ACOUSTIC_QSD=y CONFIG_MSM_QDSP6=y CONFIG_WIFI_CONTROL_FUNC=y diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index 19e6e4015cdf5..bcfc45e6945c6 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sun Jul 15 22:11:52 2012 +# Sun Jul 22 18:42:32 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -315,6 +315,8 @@ CONFIG_HTC_BATTCHG_SMEM=y # CONFIG_HTC_PWRSINK is not set # CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set # CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_QSD_SVS=y +CONFIG_QSD_PMIC_DEFAULT_DCDC1=1275 CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 CONFIG_PHYS_OFFSET=0x20000000 CONFIG_MSM7X00A_USE_GP_TIMER=y @@ -347,6 +349,8 @@ CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y CONFIG_MSM_CPU_FREQ_MAX=998400 CONFIG_MSM_CPU_FREQ_MIN=245000 # CONFIG_AXI_SCREEN_POLICY is not set +CONFIG_MSM_CPU_AVS=y +CONFIG_MSM_AVS_HW=y CONFIG_HTC_ACOUSTIC_QSD=y CONFIG_MSM_QDSP6=y CONFIG_WIFI_CONTROL_FUNC=y From 3bebe50222204f78e83ffdaf5ceba9b35bdd1847 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 22 Jul 2012 20:44:07 -0500 Subject: [PATCH 2374/2556] msm: avs: add entries for 1.113GHz OC --- arch/arm/mach-msm/acpuclock-qsd8x50.c | 6 +++--- arch/arm/mach-msm/avs.c | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-msm/acpuclock-qsd8x50.c b/arch/arm/mach-msm/acpuclock-qsd8x50.c index d82c4220c787f..a71c6e7c12ade 100644 --- a/arch/arm/mach-msm/acpuclock-qsd8x50.c +++ b/arch/arm/mach-msm/acpuclock-qsd8x50.c @@ -109,9 +109,9 @@ struct clkctl_acpu_speed acpu_freq_tbl[] = { { 921600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x18, 0, 1275, 128000 }, { 960000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x19, 0, 1275, 128000 }, { 998400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1A, 0, 1275, 128000 }, - { 1036800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1B, 0, 1275, 128000 }, - { 1075200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1C, 0, 1275, 128000 }, - { 1113600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1D, 0, 1275, 128000 }, + { 1036800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1B, 0, 1275, 128000 }, + { 1075200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1C, 0, 1275, 128000 }, + { 1113600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1D, 0, 1275, 128000 }, { 0 }, }; diff --git a/arch/arm/mach-msm/avs.c b/arch/arm/mach-msm/avs.c index 1884c2eaf2548..fb6242d1b4b08 100644 --- a/arch/arm/mach-msm/avs.c +++ b/arch/arm/mach-msm/avs.c @@ -117,6 +117,9 @@ struct clkctl_acpu_speed acpu_vdd_tbl[] = { { 921600, 1225, 1275 }, { 960000, 1225, 1275 }, { 998400, 1225, 1275 }, + { 1036800, 1275, 1275 }, + { 1075200, 1275, 1275 }, + { 1113600, 1275, 1275 }, { 0 }, }; From 713921e91d9364ab93e949c4fe4fdbc1cc05823a Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 22 Jul 2012 20:45:04 -0500 Subject: [PATCH 2375/2556] msm: cpu: bump 245MHz upper limit by .025 --- arch/arm/mach-msm/acpuclock-qsd8x50.c | 2 +- arch/arm/mach-msm/avs.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/acpuclock-qsd8x50.c b/arch/arm/mach-msm/acpuclock-qsd8x50.c index a71c6e7c12ade..967622f3f9b6a 100644 --- a/arch/arm/mach-msm/acpuclock-qsd8x50.c +++ b/arch/arm/mach-msm/acpuclock-qsd8x50.c @@ -89,7 +89,7 @@ struct regulator { struct clkctl_acpu_speed acpu_freq_tbl[] = { { 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 975, 14000 }, { 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 975, 14000 }, - { 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 1000, 29000 }, + { 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 1025, 29000 }, /* Work around for acpu resume hung, GPLL is turn off by arm9 */ /*{ 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 1000, 29000 },*/ { 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 1025, 58000 }, diff --git a/arch/arm/mach-msm/avs.c b/arch/arm/mach-msm/avs.c index fb6242d1b4b08..e3cc1fbb49ec0 100644 --- a/arch/arm/mach-msm/avs.c +++ b/arch/arm/mach-msm/avs.c @@ -99,7 +99,7 @@ struct clkctl_acpu_speed { struct clkctl_acpu_speed acpu_vdd_tbl[] = { { 19200, 950, 975 }, { 128000, 950, 975 }, - { 245000, 950, 1000 }, + { 245000, 950, 1025 }, { 384000, 950, 1025 }, { 422400, 950, 1050 }, { 460800, 975, 1050 }, From 5588f44a71884f53721c2ca8a24e7d1e7d161833 Mon Sep 17 00:00:00 2001 From: Peter Boonstoppel Date: Tue, 28 Feb 2012 14:05:50 -0800 Subject: [PATCH 2376/2556] cpufreq: ondemand: Prevent sysfs create race Protecting sysfs_remove_group() in CPUFREQ_GOV_STOP with dbs_mutex Bug 946462 Signed-off-by: Peter Boonstoppel Reviewed-on: http://git-master/r/86426 (cherry picked from commit d1131158e2ad4d5ccc53b3008743c29385650d86) Change-Id: Iae810e83eaa6f0f7d970b56238cbcb61118af610 Reviewed-on: http://git-master/r/87392 Reviewed-by: Peter Boonstoppel Reviewed-by: Diwakar Tundlam Tested-by: Peter Boonstoppel Conflicts: drivers/cpufreq/cpufreq_ondemand.c --- drivers/cpufreq/cpufreq_ondemand.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 0ffccb22604f5..81e3114237432 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -821,11 +821,10 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, this_dbs_info->cur_policy = NULL; if (!cpu) input_unregister_handler(&dbs_input_handler); - mutex_unlock(&dbs_mutex); if (!dbs_enable) sysfs_remove_group(cpufreq_global_kobject, &dbs_attr_group); - + mutex_unlock(&dbs_mutex); break; case CPUFREQ_GOV_LIMITS: From c2d78dbb42fc7a2208ac62681d26870238489de0 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Mon, 23 Jul 2012 21:05:35 -0500 Subject: [PATCH 2377/2556] msm: avs: youre being too noisy --- arch/arm/mach-msm/avs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/avs.c b/arch/arm/mach-msm/avs.c index e3cc1fbb49ec0..72fe9d78a370a 100644 --- a/arch/arm/mach-msm/avs.c +++ b/arch/arm/mach-msm/avs.c @@ -141,7 +141,7 @@ static void avs_update_voltage_table(short *vdd_table) cur_voltage = avs_state.vdd; avscsr = avs_test_delays(); - AVSDEBUG("avscsr=%x, avsdscr=%x\n", avscsr, avs_get_avsdscr()); +/* AVSDEBUG("avscsr=%x, avsdscr=%x\n", avscsr, avs_get_avsdscr());*/ /* * Read the results for the various unit's AVS delay circuits From b0789672242c91271b4566e0d3d4fe8e70eae936 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 29 Jul 2012 23:52:37 -0500 Subject: [PATCH 2378/2556] Revert "msm: avs: enable debug" This reverts commit 361f80d81919b49a065671b7d1fd108118072b84. --- arch/arm/mach-msm/avs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/avs.h b/arch/arm/mach-msm/avs.h index 1e0baa2bd59b7..4e6898359842c 100644 --- a/arch/arm/mach-msm/avs.h +++ b/arch/arm/mach-msm/avs.h @@ -67,8 +67,8 @@ static inline void avs_set_tscsr(u32 to_tscsr) {} static inline void avs_disable(void) {} #endif -#define AVSDEBUG(x...) pr_info("AVS: " x); -//#define AVSDEBUG(...) +/*#define AVSDEBUG(x...) pr_info("AVS: " x);*/ +#define AVSDEBUG(...) #define AVS_DISABLE(cpu) do { \ if (get_cpu() == (cpu)) \ From a0ae9d88a5948ab4e775a4673bcc29df1a82afbe Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 29 Jul 2012 23:54:05 -0500 Subject: [PATCH 2379/2556] include: checkout msm_mdp from ics_chocolate *needs to match userspace* --- include/linux/msm_mdp.h | 476 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 447 insertions(+), 29 deletions(-) diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h index a933facc5c5de..0ac1cf11be313 100644 --- a/include/linux/msm_mdp.h +++ b/include/linux/msm_mdp.h @@ -1,6 +1,7 @@ /* include/linux/msm_mdp.h * * Copyright (C) 2007 Google Incorporated + * Copyright (c) 2012 Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -15,25 +16,90 @@ #define _MSM_MDP_H_ #include +#include #define MSMFB_IOCTL_MAGIC 'm' #define MSMFB_GRP_DISP _IOW(MSMFB_IOCTL_MAGIC, 1, unsigned int) #define MSMFB_BLIT _IOW(MSMFB_IOCTL_MAGIC, 2, unsigned int) +#define MSMFB_SUSPEND_SW_REFRESHER _IOW(MSMFB_IOCTL_MAGIC, 128, unsigned int) +#define MSMFB_RESUME_SW_REFRESHER _IOW(MSMFB_IOCTL_MAGIC, 129, unsigned int) +#define MSMFB_CURSOR _IOW(MSMFB_IOCTL_MAGIC, 130, struct fb_cursor) +#define MSMFB_SET_LUT _IOW(MSMFB_IOCTL_MAGIC, 131, struct fb_cmap) +#define MSMFB_HISTOGRAM _IOWR(MSMFB_IOCTL_MAGIC, 132, struct mdp_histogram_data) +/* new ioctls's for set/get ccs matrix */ +#define MSMFB_GET_CCS_MATRIX _IOWR(MSMFB_IOCTL_MAGIC, 133, struct mdp_ccs) +#define MSMFB_SET_CCS_MATRIX _IOW(MSMFB_IOCTL_MAGIC, 134, struct mdp_ccs) +#define MSMFB_OVERLAY_SET _IOWR(MSMFB_IOCTL_MAGIC, 135, \ + struct mdp_overlay) +#define MSMFB_OVERLAY_UNSET _IOW(MSMFB_IOCTL_MAGIC, 136, unsigned int) +#define MSMFB_OVERLAY_PLAY _IOW(MSMFB_IOCTL_MAGIC, 137, \ + struct msmfb_overlay_data) +#define MSMFB_GET_PAGE_PROTECTION _IOR(MSMFB_IOCTL_MAGIC, 138, \ + struct mdp_page_protection) +#define MSMFB_SET_PAGE_PROTECTION _IOW(MSMFB_IOCTL_MAGIC, 139, \ + struct mdp_page_protection) +#define MSMFB_OVERLAY_GET _IOR(MSMFB_IOCTL_MAGIC, 140, \ + struct mdp_overlay) +#define MSMFB_OVERLAY_PLAY_ENABLE _IOW(MSMFB_IOCTL_MAGIC, 141, unsigned int) +#define MSMFB_OVERLAY_BLT _IOWR(MSMFB_IOCTL_MAGIC, 142, \ + struct msmfb_overlay_blt) +#define MSMFB_OVERLAY_BLT_OFFSET _IOW(MSMFB_IOCTL_MAGIC, 143, unsigned int) +#define MSMFB_HISTOGRAM_START _IOR(MSMFB_IOCTL_MAGIC, 144, \ + struct mdp_histogram_start_req) +#define MSMFB_HISTOGRAM_STOP _IOR(MSMFB_IOCTL_MAGIC, 145, unsigned int) +#define MSMFB_NOTIFY_UPDATE _IOW(MSMFB_IOCTL_MAGIC, 146, unsigned int) + +#define MSMFB_OVERLAY_3D _IOWR(MSMFB_IOCTL_MAGIC, 147, \ + struct msmfb_overlay_3d) + +#define MSMFB_MIXER_INFO _IOWR(MSMFB_IOCTL_MAGIC, 148, \ + struct msmfb_mixer_info_req) +#define MSMFB_OVERLAY_PLAY_WAIT _IOWR(MSMFB_IOCTL_MAGIC, 149, \ + struct msmfb_overlay_data) +#define MSMFB_WRITEBACK_INIT _IO(MSMFB_IOCTL_MAGIC, 150) +#define MSMFB_WRITEBACK_START _IO(MSMFB_IOCTL_MAGIC, 151) +#define MSMFB_WRITEBACK_STOP _IO(MSMFB_IOCTL_MAGIC, 152) +#define MSMFB_WRITEBACK_QUEUE_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 153, \ + struct msmfb_data) +#define MSMFB_WRITEBACK_DEQUEUE_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 154, \ + struct msmfb_data) +#define MSMFB_WRITEBACK_TERMINATE _IO(MSMFB_IOCTL_MAGIC, 155) +#define MSMFB_MDP_PP _IOWR(MSMFB_IOCTL_MAGIC, 156, struct msmfb_mdp_pp) + +#define FB_TYPE_3D_PANEL 0x10101010 +#define MDP_IMGTYPE2_START 0x10000 +#define MSMFB_DRIVER_VERSION 0xF9E8D701 + +enum { + NOTIFY_UPDATE_START, + NOTIFY_UPDATE_STOP, +}; enum { - MDP_RGB_565, /* RGB 565 planar */ - MDP_XRGB_8888, /* RGB 888 padded */ - MDP_Y_CBCR_H2V2, /* Y and CbCr, pseudo planar w/ Cb is in MSB */ - MDP_ARGB_8888, /* ARGB 888 */ - MDP_RGB_888, /* RGB 888 planar */ - MDP_Y_CRCB_H2V2, /* Y and CrCb, pseudo planar w/ Cr is in MSB */ - MDP_YCRYCB_H2V1, /* YCrYCb interleave */ - MDP_Y_CRCB_H2V1, /* Y and CrCb, pseduo planar w/ Cr is in MSB */ - MDP_Y_CBCR_H2V1, /* Y and CrCb, pseduo planar w/ Cr is in MSB */ - MDP_RGBA_8888, /* ARGB 888 */ - MDP_BGRA_8888, /* ABGR 888 */ - MDP_RGBX_8888, /* RGBX 888 */ - MDP_IMGTYPE_LIMIT /* Non valid image type after this enum */ + MDP_RGB_565, /* RGB 565 planer */ + MDP_XRGB_8888, /* RGB 888 padded */ + MDP_Y_CBCR_H2V2, /* Y and CbCr, pseudo planer w/ Cb is in MSB */ + MDP_Y_CBCR_H2V2_ADRENO, + MDP_ARGB_8888, /* ARGB 888 */ + MDP_RGB_888, /* RGB 888 planer */ + MDP_Y_CRCB_H2V2, /* Y and CrCb, pseudo planer w/ Cr is in MSB */ + MDP_YCRYCB_H2V1, /* YCrYCb interleave */ + MDP_Y_CRCB_H2V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */ + MDP_Y_CBCR_H2V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */ + MDP_RGBA_8888, /* ARGB 888 */ + MDP_BGRA_8888, /* ABGR 888 */ + MDP_RGBX_8888, /* RGBX 888 */ + MDP_Y_CRCB_H2V2_TILE, /* Y and CrCb, pseudo planer tile */ + MDP_Y_CBCR_H2V2_TILE, /* Y and CbCr, pseudo planer tile */ + MDP_Y_CR_CB_H2V2, /* Y, Cr and Cb, planar */ + MDP_Y_CR_CB_GH2V2, /* Y, Cr and Cb, planar aligned to Android YV12 */ + MDP_Y_CB_CR_H2V2, /* Y, Cb and Cr, planar */ + MDP_Y_CRCB_H1V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */ + MDP_Y_CBCR_H1V1, /* Y and CbCr, pseduo planer w/ Cb is in MSB */ + MDP_IMGTYPE_LIMIT, + MDP_BGR_565 = MDP_IMGTYPE2_START, /* BGR 565 planer */ + MDP_FB_FORMAT, /* framebuffer format */ + MDP_IMGTYPE_LIMIT2 /* Non valid image type after this enum */ }; enum { @@ -41,24 +107,57 @@ enum { FB_IMG, }; -/* flag values */ -#define MDP_ROT_NOP 0 -#define MDP_FLIP_LR 0x1 -#define MDP_FLIP_UD 0x2 -#define MDP_ROT_90 0x4 -#define MDP_ROT_180 (MDP_FLIP_UD|MDP_FLIP_LR) -#define MDP_ROT_270 (MDP_ROT_90|MDP_FLIP_UD|MDP_FLIP_LR) -#define MDP_ROT_MASK 0x7 -#define MDP_DITHER 0x8 -#define MDP_BLUR 0x10 +enum { + HSIC_HUE = 0, + HSIC_SAT, + HSIC_INT, + HSIC_CON, + NUM_HSIC_PARAM, +}; + +/* mdp_blit_req flag values */ +#define MDP_ROT_NOP 0 +#define MDP_FLIP_LR 0x1 +#define MDP_FLIP_UD 0x2 +#define MDP_ROT_90 0x4 +#define MDP_ROT_180 (MDP_FLIP_UD|MDP_FLIP_LR) +#define MDP_ROT_270 (MDP_ROT_90|MDP_FLIP_UD|MDP_FLIP_LR) +#define MDP_DITHER 0x8 +#define MDP_BLUR 0x10 #define MDP_BLEND_FG_PREMULT 0x20000 +#define MDP_DEINTERLACE 0x80000000 +#define MDP_SHARPENING 0x40000000 +#define MDP_NO_DMA_BARRIER_START 0x20000000 +#define MDP_NO_DMA_BARRIER_END 0x10000000 +#define MDP_NO_BLIT 0x08000000 +#define MDP_BLIT_WITH_DMA_BARRIERS 0x000 +#define MDP_BLIT_WITH_NO_DMA_BARRIERS \ + (MDP_NO_DMA_BARRIER_START | MDP_NO_DMA_BARRIER_END) +#define MDP_BLIT_SRC_GEM 0x04000000 +#define MDP_BLIT_DST_GEM 0x02000000 +#define MDP_BLIT_NON_CACHED 0x01000000 +#define MDP_OV_PIPE_SHARE 0x00800000 +#define MDP_DEINTERLACE_ODD 0x00400000 +#define MDP_OV_PLAY_NOWAIT 0x00200000 +#define MDP_SOURCE_ROTATED_90 0x00100000 +#define MDP_DPP_HSIC 0x00080000 +#define MDP_BACKEND_COMPOSITION 0x00040000 +#define MDP_BORDERFILL_SUPPORTED 0x00010000 +#define MDP_SECURE_OVERLAY_SESSION 0x00008000 +#define MDP_MEMORY_ID_TYPE_FB 0x00001000 -#define MDP_TRANSP_NOP 0xffffffff -#define MDP_ALPHA_NOP 0xff +#define MDP_TRANSP_NOP 0xffffffff +#define MDP_ALPHA_NOP 0xff -/* drewis: added for android 4.0 */ -#define MDP_BLIT_NON_CACHED 0x01000000 -/* drewis: end */ +#define MDP_FB_PAGE_PROTECTION_NONCACHED (0) +#define MDP_FB_PAGE_PROTECTION_WRITECOMBINE (1) +#define MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE (2) +#define MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE (3) +#define MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE (4) +/* Sentinel: Don't use! */ +#define MDP_FB_PAGE_PROTECTION_INVALID (5) +/* Count of the number of MDP_FB_PAGE_PROTECTION_... values. */ +#define MDP_NUM_FB_PAGE_PROTECTION_VALUES (5) struct mdp_rect { uint32_t x; @@ -73,8 +172,41 @@ struct mdp_img { uint32_t format; uint32_t offset; int memory_id; /* the file descriptor */ + uint32_t priv; +}; + +/* + * {3x3} + {3} ccs matrix + */ + +#define MDP_CCS_RGB2YUV 0 +#define MDP_CCS_YUV2RGB 1 + +#define MDP_CCS_SIZE 9 +#define MDP_BV_SIZE 3 + +struct mdp_ccs { + int direction; /* MDP_CCS_RGB2YUV or YUV2RGB */ + uint16_t ccs[MDP_CCS_SIZE]; /* 3x3 color coefficients */ + uint16_t bv[MDP_BV_SIZE]; /* 1x3 bias vector */ +}; + +struct mdp_csc { + int id; + uint32_t csc_mv[9]; + uint32_t csc_pre_bv[3]; + uint32_t csc_post_bv[3]; + uint32_t csc_pre_lv[6]; + uint32_t csc_post_lv[6]; }; +/* The version of the mdp_blit_req structure so that + * user applications can selectively decide which functionality + * to include + */ + +#define MDP_BLIT_REQ_VERSION 2 + struct mdp_blit_req { struct mdp_img src; struct mdp_img dst; @@ -83,6 +215,7 @@ struct mdp_blit_req { uint32_t alpha; uint32_t transp_mask; uint32_t flags; + int sharpening_strength; /* -127 <--> 127, default 64 */ }; struct mdp_blit_req_list { @@ -90,4 +223,289 @@ struct mdp_blit_req_list { struct mdp_blit_req req[]; }; -#endif /* _MSM_MDP_H_ */ +#define MSMFB_DATA_VERSION 2 + +struct msmfb_data { + uint32_t offset; + int memory_id; + int id; + uint32_t flags; + uint32_t priv; + uint32_t iova; +}; + +#define MSMFB_NEW_REQUEST -1 + +struct msmfb_overlay_data { + uint32_t id; + struct msmfb_data data; + uint32_t version_key; + struct msmfb_data plane1_data; + struct msmfb_data plane2_data; +}; + +struct msmfb_img { + uint32_t width; + uint32_t height; + uint32_t format; +}; + +#define MSMFB_WRITEBACK_DEQUEUE_BLOCKING 0x1 +struct msmfb_writeback_data { + struct msmfb_data buf_info; + struct msmfb_img img; +}; + +struct dpp_ctrl { + /* + *'sharp_strength' has inputs = -128 <-> 127 + * Increasingly positive values correlate with increasingly sharper + * picture. Increasingly negative values correlate with increasingly + * smoothed picture. + */ + int8_t sharp_strength; + int8_t hsic_params[NUM_HSIC_PARAM]; +}; + +struct mdp_overlay { + struct msmfb_img src; + struct mdp_rect src_rect; + struct mdp_rect dst_rect; + uint32_t z_order; /* stage number */ + uint32_t is_fg; /* control alpha & transp */ + uint32_t alpha; + uint32_t transp_mask; + uint32_t flags; + uint32_t id; + uint32_t user_data[8]; + struct dpp_ctrl dpp; +}; + +struct msmfb_overlay_3d { + uint32_t is_3d; + uint32_t width; + uint32_t height; +}; + + +struct msmfb_overlay_blt { + uint32_t enable; + uint32_t offset; + uint32_t width; + uint32_t height; + uint32_t bpp; +}; + +struct mdp_histogram { + uint32_t frame_cnt; + uint32_t bin_cnt; + uint32_t *r; + uint32_t *g; + uint32_t *b; +}; + + +/* + + mdp_block_type defines the identifiers for each of pipes in MDP 4.3 + + MDP_BLOCK_RESERVED is provided for backward compatibility and is + deprecated. It corresponds to DMA_P. So MDP_BLOCK_DMA_P should be used + instead. + +*/ + +enum { + MDP_BLOCK_RESERVED = 0, + MDP_BLOCK_OVERLAY_0, + MDP_BLOCK_OVERLAY_1, + MDP_BLOCK_VG_1, + MDP_BLOCK_VG_2, + MDP_BLOCK_RGB_1, + MDP_BLOCK_RGB_2, + MDP_BLOCK_DMA_P, + MDP_BLOCK_DMA_S, + MDP_BLOCK_DMA_E, + MDP_BLOCK_MAX, +}; + +/* +mdp_histogram_start_req is used to provide the parameters for +histogram start request +*/ + +struct mdp_histogram_start_req { + uint32_t block; + uint8_t frame_cnt; + uint8_t bit_mask; + uint8_t num_bins; +}; + + +/* + + mdp_histogram_data is used to return the histogram data, once + the histogram is done/stopped/cance + + */ + + +struct mdp_histogram_data { + uint32_t block; + uint8_t bin_cnt; + uint32_t *c0; + uint32_t *c1; + uint32_t *c2; + uint32_t *extra_info; +}; + +struct mdp_pcc_coeff { + uint32_t c, r, g, b, rr, gg, bb, rg, gb, rb, rgb_0, rgb_1; +}; + +struct mdp_pcc_cfg_data { + uint32_t block; + uint32_t ops; + struct mdp_pcc_coeff r, g, b; +}; + +#define MDP_CSC_FLAG_ENABLE 0x1 +#define MDP_CSC_FLAG_YUV_IN 0x2 +#define MDP_CSC_FLAG_YUV_OUT 0x4 + +struct mdp_csc_cfg { + /* flags for enable CSC, toggling RGB,YUV input/output */ + uint32_t flags; + uint32_t csc_mv[9]; + uint32_t csc_pre_bv[3]; + uint32_t csc_post_bv[3]; + uint32_t csc_pre_lv[6]; + uint32_t csc_post_lv[6]; +}; + +struct mdp_csc_cfg_data { + uint32_t block; + struct mdp_csc_cfg csc_data; +}; + +enum { + mdp_lut_igc, + mdp_lut_pgc, + mdp_lut_hist, + mdp_lut_max, +}; + + +struct mdp_igc_lut_data { + uint32_t block; + uint32_t len, ops; + uint32_t *c0_c1_data; + uint32_t *c2_data; +}; + +struct mdp_ar_gc_lut_data { + uint32_t x_start; + uint32_t slope; + uint32_t offset; +}; + +struct mdp_pgc_lut_data { + uint32_t block; + uint32_t flags; + uint8_t num_r_stages; + uint8_t num_g_stages; + uint8_t num_b_stages; + struct mdp_ar_gc_lut_data *r_data; + struct mdp_ar_gc_lut_data *g_data; + struct mdp_ar_gc_lut_data *b_data; +}; + + +struct mdp_hist_lut_data { + uint32_t block; + uint32_t ops; + uint32_t len; + uint32_t *data; +}; + + +struct mdp_lut_cfg_data { + uint32_t lut_type; + union { + struct mdp_igc_lut_data igc_lut_data; + struct mdp_pgc_lut_data pgc_lut_data; + struct mdp_hist_lut_data hist_lut_data; + } data; +}; + +struct mdp_qseed_cfg_data { + uint32_t block; + uint32_t table_num; + uint32_t ops; + uint32_t len; + uint32_t *data; +}; + + +enum { + mdp_op_pcc_cfg, + mdp_op_csc_cfg, + mdp_op_lut_cfg, + mdp_op_qseed_cfg, + mdp_op_max, +}; + +struct msmfb_mdp_pp { + uint32_t op; + union { + struct mdp_pcc_cfg_data pcc_cfg_data; + struct mdp_csc_cfg_data csc_cfg_data; + struct mdp_lut_cfg_data lut_cfg_data; + struct mdp_qseed_cfg_data qseed_cfg_data; + } data; +}; + + +struct mdp_page_protection { + uint32_t page_protection; +}; + + +struct mdp_mixer_info { + int pndx; + int pnum; + int ptype; + int mixer_num; + int z_order; +}; + +#define MAX_PIPE_PER_MIXER 4 + +struct msmfb_mixer_info_req { + int mixer_num; + int cnt; + struct mdp_mixer_info info[MAX_PIPE_PER_MIXER]; +}; + +enum { + DISPLAY_SUBSYSTEM_ID, + ROTATOR_SUBSYSTEM_ID, +}; + +#ifdef __KERNEL__ + +/* get the framebuffer physical address information */ +int get_fb_phys_info(unsigned long *start, unsigned long *len, int fb_num, + int subsys_id); +struct fb_info *msm_fb_get_writeback_fb(void); +int msm_fb_writeback_init(struct fb_info *info); +int msm_fb_writeback_start(struct fb_info *info); +int msm_fb_writeback_queue_buffer(struct fb_info *info, + struct msmfb_data *data); +int msm_fb_writeback_dequeue_buffer(struct fb_info *info, + struct msmfb_data *data); +int msm_fb_writeback_stop(struct fb_info *info); +int msm_fb_writeback_terminate(struct fb_info *info); +#endif + +#endif /*_MSM_MDP_H_*/ From 4ba87e4b5ebbe56136df8707625eaf69bc1db646 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 31 Jul 2012 03:35:55 -0500 Subject: [PATCH 2380/2556] video: msm: add mdp version to id string, and put a bogus panel id --- drivers/video/msm/msm_fb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index 25e14d4d340f5..0607030a3bd12 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -985,7 +985,7 @@ static void setup_fb_info(struct msmfb_info *msmfb) int r; /* finish setting up the fb_info struct */ - strncpy(fb_info->fix.id, "msmfb", 16); + strncpy(fb_info->fix.id, "msmfb31_0", 16); fb_info->fix.ypanstep = 1; fb_info->fbops = &msmfb_ops; From b877d2ac7a0bb16ab09e12786d38e1bc3def583f Mon Sep 17 00:00:00 2001 From: Daniel Hillenbrand Date: Mon, 23 Jul 2012 16:56:04 -0500 Subject: [PATCH 2381/2556] HACK: block fbearlysuspend to not break androids crt-off animation --- kernel/power/fbearlysuspend.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/power/fbearlysuspend.c b/kernel/power/fbearlysuspend.c index 83104fe471038..0de398e966224 100644 --- a/kernel/power/fbearlysuspend.c +++ b/kernel/power/fbearlysuspend.c @@ -13,6 +13,7 @@ * */ +#include #include #include #include @@ -33,6 +34,10 @@ static void stop_drawing_early_suspend(struct early_suspend *h) int ret; unsigned long irq_flags; + /* FIXME: earlysuspend breaks androids CRT-off animation + * Sleep a little bit to get it played properly */ + msleep(500); + spin_lock_irqsave(&fb_state_lock, irq_flags); fb_state = FB_STATE_REQUEST_STOP_DRAWING; spin_unlock_irqrestore(&fb_state_lock, irq_flags); From 8d29117cd9378a4b441318a5c5501df2d39622dc Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 13 Jul 2012 14:38:32 -0700 Subject: [PATCH 2382/2556] net: wireless: bcmdhd: Adjust driver/fw/chip info format Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd_linux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index f4dd38fc77e27..3aede87731924 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -4861,12 +4861,12 @@ void dhd_set_version_info(dhd_pub_t *dhdp, char *fw) int i; i = snprintf(info_string, sizeof(info_string), - "WLAN:\n Driver: %s\n Firmware: %s ", EPI_VERSION_STR, fw); + " Driver: %s\n Firmware: %s ", EPI_VERSION_STR, fw); if (!dhdp) return; i = snprintf(&info_string[i], sizeof(info_string) - i, - "\n Chip: %x Rev %x Pkg %x\n", dhd_bus_chip_id(dhdp), + "\n Chip: %x Rev %x Pkg %x", dhd_bus_chip_id(dhdp), dhd_bus_chiprev_id(dhdp), dhd_bus_chippkg_id(dhdp)); } From 65b7e6dfdfe1cd0f1aa3989f68e04916bad9f242 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Fri, 13 Jul 2012 14:30:33 -0700 Subject: [PATCH 2383/2556] net: wireless: bcmdhd: Use proper jiffie-related functions Signed-off-by: Dmitry Shmidt Conflicts: drivers/net/wireless/bcmdhd/wl_cfg80211.c drivers/net/wireless/bcmdhd/wl_iw.c --- drivers/net/wireless/bcmdhd/dhd.h | 4 ++-- drivers/net/wireless/bcmdhd/dhd_cfg80211.c | 4 ++-- drivers/net/wireless/bcmdhd/dhd_linux.c | 22 ++++++------------- .../net/wireless/bcmdhd/include/linux_osl.h | 2 +- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 10 ++++----- drivers/net/wireless/bcmdhd/wl_iw.c | 18 +++++++-------- 6 files changed, 26 insertions(+), 34 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index f047c8efa8bc3..b35b24a8aa144 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -251,7 +251,7 @@ typedef struct dhd_cmn { SMP_RD_BARRIER_DEPENDS(); \ while (dhd_mmc_suspend && retry++ != b) { \ SMP_RD_BARRIER_DEPENDS(); \ - wait_event_interruptible_timeout(a, !dhd_mmc_suspend, HZ/100); \ + wait_event_interruptible_timeout(a, !dhd_mmc_suspend, 1); \ } \ } while (0) #define DHD_PM_RESUME_WAIT(a) _DHD_PM_RESUME_WAIT(a, 200) @@ -263,7 +263,7 @@ typedef struct dhd_cmn { #define SPINWAIT_SLEEP(a, exp, us) do { \ uint countdown = (us) + 9999; \ while ((exp) && (countdown >= 10000)) { \ - wait_event_interruptible_timeout(a, FALSE, HZ/100); \ + wait_event_interruptible_timeout(a, FALSE, 1); \ countdown -= 10000; \ } \ } while (0) diff --git a/drivers/net/wireless/bcmdhd/dhd_cfg80211.c b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c index bc1be94802b4a..970216efc7a7a 100644 --- a/drivers/net/wireless/bcmdhd/dhd_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c @@ -433,7 +433,7 @@ static void wl_cfg80211_bt_handler(struct work_struct *work) __FUNCTION__)); btcx_inf->bt_state = BT_DHCP_OPPR_WIN; mod_timer(&btcx_inf->timer, - jiffies + BT_DHCP_OPPR_WIN_TIME*HZ/1000); + jiffies + msecs_to_jiffies(BT_DHCP_OPPR_WIN_TIME)); btcx_inf->timer_on = 1; break; @@ -453,7 +453,7 @@ static void wl_cfg80211_bt_handler(struct work_struct *work) wl_cfg80211_bt_setflag(btcx_inf->dev, TRUE); btcx_inf->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT; mod_timer(&btcx_inf->timer, - jiffies + BT_DHCP_FLAG_FORCE_TIME*HZ/1000); + jiffies + msecs_to_jiffies(BT_DHCP_FLAG_FORCE_TIME)); btcx_inf->timer_on = 1; break; diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 3aede87731924..cebfe98310765 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -646,7 +646,7 @@ dhd_timeout_start(dhd_timeout_t *tmo, uint usec) tmo->limit = usec; tmo->increment = 0; tmo->elapsed = 0; - tmo->tick = 1000000 / HZ; + tmo->tick = jiffies_to_usecs(1); } int @@ -1692,7 +1692,7 @@ dhd_watchdog_thread(void *data) /* Reschedule the watchdog */ if (dhd->wd_timer_valid) mod_timer(&dhd->timer, - jiffies + dhd_watchdog_ms * HZ / 1000); + jiffies + msecs_to_jiffies(dhd_watchdog_ms)); dhd_os_spin_unlock(&dhd->pub, flags); } dhd_os_sdunlock(&dhd->pub); @@ -1733,7 +1733,7 @@ static void dhd_watchdog(ulong data) /* Reschedule the watchdog */ if (dhd->wd_timer_valid) - mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000); + mod_timer(&dhd->timer, jiffies + msecs_to_jiffies(dhd_watchdog_ms)); dhd_os_spin_unlock(&dhd->pub, flags); dhd_os_sdunlock(&dhd->pub); DHD_OS_WAKE_UNLOCK(&dhd->pub); @@ -3937,14 +3937,10 @@ int dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending) { dhd_info_t * dhd = (dhd_info_t *)(pub->info); - int timeout = dhd_ioctl_timeout_msec; + int timeout; /* Convert timeout in millsecond to jiffies */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) - timeout = msecs_to_jiffies(timeout); -#else - timeout = timeout * HZ / 1000; -#endif + timeout = msecs_to_jiffies(dhd_ioctl_timeout_msec); timeout = wait_event_timeout(dhd->ioctl_resp_wait, (*condition), timeout); return timeout; @@ -3994,7 +3990,7 @@ dhd_os_wd_timer(void *bus, uint wdtick) if (wdtick) { dhd_watchdog_ms = (uint)wdtick; /* Re arm the timer, at last watchdog period */ - mod_timer(&dhd->timer, jiffies + dhd_watchdog_ms * HZ / 1000); + mod_timer(&dhd->timer, jiffies + msecs_to_jiffies(dhd_watchdog_ms)); dhd->wd_timer_valid = TRUE; } dhd_os_spin_unlock(pub, flags); @@ -4305,11 +4301,7 @@ void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) struct dhd_info *dhdinfo = dhd->info; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) int timeout = msecs_to_jiffies(2000); -#else - int timeout = 2 * HZ; -#endif dhd_os_sdunlock(dhd); wait_event_timeout(dhdinfo->ctrl_wait, (*lockvar == FALSE), timeout); dhd_os_sdlock(dhd); @@ -4634,7 +4626,7 @@ int dhd_wait_pend8021x(struct net_device *dev) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); - int timeout = 10 * HZ / 1000; + int timeout = msecs_to_jiffies(10); int ntimes = MAX_WAIT_FOR_8021X_TX; int pend = dhd_get_pend_8021x_cnt(dhd); diff --git a/drivers/net/wireless/bcmdhd/include/linux_osl.h b/drivers/net/wireless/bcmdhd/include/linux_osl.h index 830d351d882b4..7f92966d977e4 100644 --- a/drivers/net/wireless/bcmdhd/include/linux_osl.h +++ b/drivers/net/wireless/bcmdhd/include/linux_osl.h @@ -163,7 +163,7 @@ extern int osl_error(int bcmerror); -#define OSL_SYSUPTIME() ((uint32)jiffies * (1000 / HZ)) +#define OSL_SYSUPTIME() ((uint32)jiffies_to_msecs(jiffies)) #define printf(fmt, args...) printk(fmt , ## args) #include #include diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 353713880bb3d..2365bdd4a540a 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -1355,7 +1355,7 @@ static s32 wl_do_iscan(struct wl_priv *wl, struct cfg80211_scan_request *request } wl->iscan_kickstart = true; wl_run_iscan(iscan, request, WL_SCAN_ACTION_START); - mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); + mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); iscan->timer_on = 1; return err; @@ -1578,7 +1578,7 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, } /* Arm scan timeout timer */ - mod_timer(&wl->scan_timeout, jiffies + WL_SCAN_TIMER_INTERVAL_MS * HZ / 1000); + mod_timer(&wl->scan_timeout, jiffies + msecs_to_jiffies(WL_SCAN_TIMER_INTERVAL_MS)); iscan_req = false; wl->scan_request = request; if (request) { /* scan bss */ @@ -6088,7 +6088,7 @@ static s32 wl_iscan_pending(struct wl_priv *wl) s32 err = 0; /* Reschedule the timer */ - mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); + mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); iscan->timer_on = 1; return err; @@ -6104,7 +6104,7 @@ static s32 wl_iscan_inprogress(struct wl_priv *wl) wl_run_iscan(iscan, NULL, WL_SCAN_ACTION_CONTINUE); mutex_unlock(&wl->usr_sync); /* Reschedule the timer */ - mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); + mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); iscan->timer_on = 1; return err; @@ -7551,7 +7551,7 @@ static void wl_init_eq_lock(struct wl_priv *wl) static void wl_delay(u32 ms) { - if (in_atomic() || ms < 1000 / HZ) { + if (in_atomic() || (ms < jiffies_to_msecs(1))) { mdelay(ms); } else { msleep(ms); diff --git a/drivers/net/wireless/bcmdhd/wl_iw.c b/drivers/net/wireless/bcmdhd/wl_iw.c index 095dc86b08e7d..d60c21c03671b 100644 --- a/drivers/net/wireless/bcmdhd/wl_iw.c +++ b/drivers/net/wireless/bcmdhd/wl_iw.c @@ -64,7 +64,7 @@ typedef const struct si_pub si_t; #endif -#define JF2MS ((((jiffies / HZ) * 1000) + ((jiffies % HZ) * 1000) / HZ)) +#define JF2MS jiffies_to_msecs(jiffies) #ifdef COEX_DBG #define WL_TRACE_COEX(x) printf("TS:%lu ", JF2MS); \ @@ -3284,7 +3284,7 @@ _iscan_sysioc_thread(void *data) rtnl_unlock(); #endif - mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000); + mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); iscan->timer_on = 1; break; case WL_SCAN_RESULTS_SUCCESS: @@ -3295,7 +3295,7 @@ _iscan_sysioc_thread(void *data) case WL_SCAN_RESULTS_PENDING: WL_TRACE(("iscanresults pending\n")); - mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000); + mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); iscan->timer_on = 1; break; case WL_SCAN_RESULTS_ABORTED: @@ -3395,9 +3395,9 @@ wl_iw_run_ss_cache_timer(int kick_off) if (*timer) { if (kick_off) { #ifdef CONFIG_PRESCANNED - (*timer)->expires = jiffies + 70000 * HZ / 1000; + (*timer)->expires = jiffies + msecs_to_jiffies(70000); #else - (*timer)->expires = jiffies + 30000 * HZ / 1000; + (*timer)->expires = jiffies + msecs_to_jiffies(30000); #endif add_timer(*timer); WL_TRACE(("%s : timer starts \n", __FUNCTION__)); @@ -3715,7 +3715,7 @@ wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag) rtnl_unlock(); #endif - mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000); + mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); iscan->timer_on = 1; @@ -6007,7 +6007,7 @@ wl_iw_combined_scan_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nss iscan->list_cur = iscan->list_hdr; iscan->iscan_state = ISCAN_STATE_SCANING; wl_iw_set_event_mask(dev); - mod_timer(&iscan->timer, jiffies + iscan->timer_ms*HZ/1000); + mod_timer(&iscan->timer, jiffies + msecs_to_jiffies(iscan->timer_ms)); iscan->timer_on = 1; @@ -8639,7 +8639,7 @@ _bt_dhcp_sysioc_thread(void *data) WL_TRACE_COEX(("%s bt_dhcp stm: started \n", __FUNCTION__)); g_bt->bt_state = BT_DHCP_OPPORTUNITY_WINDOW; mod_timer(&g_bt->timer, - jiffies + BT_DHCP_OPPORTUNITY_WINDOW_TIME*HZ/1000); + jiffies + msecs_to_jiffies(BT_DHCP_OPPORTUNITY_WINDOW_TIME)); g_bt->timer_on = 1; break; @@ -8656,7 +8656,7 @@ _bt_dhcp_sysioc_thread(void *data) if (g_bt->dev) wl_iw_bt_flag_set(g_bt->dev, TRUE); g_bt->bt_state = BT_DHCP_FLAG_FORCE_TIMEOUT; - mod_timer(&g_bt->timer, jiffies + BT_DHCP_FLAG_FORCE_TIME*HZ/1000); + mod_timer(&g_bt->timer, jiffies + msecs_to_jiffies(BT_DHCP_FLAG_FORCE_TIME)); g_bt->timer_on = 1; break; From 565d87d504700de071ae5231605d5780b4648ba1 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 18 Jul 2012 11:15:10 -0700 Subject: [PATCH 2384/2556] net: wireless: bcmdhd: Prevent HT Avail request failure to flood the log Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd.h | 1 + drivers/net/wireless/bcmdhd/dhd_linux.c | 26 ++++++++++++++++++------- drivers/net/wireless/bcmdhd/dhd_sdio.c | 16 ++++++++++----- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index b35b24a8aa144..725a909273dc0 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -431,6 +431,7 @@ extern void dhd_os_sdunlock_sndup_rxq(dhd_pub_t * pub); extern void dhd_os_sdlock_eventq(dhd_pub_t * pub); extern void dhd_os_sdunlock_eventq(dhd_pub_t * pub); extern bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret); +extern int dhd_os_send_hang_message(dhd_pub_t *dhdp); extern int net_os_send_hang_message(struct net_device *dev); extern void dhd_set_version_info(dhd_pub_t *pub, char *fw); diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index cebfe98310765..d32ce1135f2c1 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -4510,21 +4510,33 @@ static void dhd_hang_process(struct work_struct *work) #endif } } +#endif -int net_os_send_hang_message(struct net_device *dev) +int dhd_os_send_hang_message(dhd_pub_t *dhdp) { - dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); int ret = 0; - if (dhd) { - if (!dhd->pub.hang_was_sent) { - dhd->pub.hang_was_sent = 1; - schedule_work(&dhd->work_hang); + if (dhdp) { + if (!dhdp->hang_was_sent) { + dhdp->hang_was_sent = 1; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + schedule_work(&dhdp->info->work_hang); +#endif } } return ret; } -#endif + +int net_os_send_hang_message(struct net_device *dev) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + + if (dhd) + ret = dhd_os_send_hang_message(&dhd->pub); + + return ret; +} void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec) { diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c index 5b744f4fb1b46..e578d702ce11f 100644 --- a/drivers/net/wireless/bcmdhd/dhd_sdio.c +++ b/drivers/net/wireless/bcmdhd/dhd_sdio.c @@ -539,6 +539,8 @@ dhdsdio_set_siaddr_window(dhd_bus_t *bus, uint32 address) static int dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok) { +#define HT_AVAIL_ERROR_MAX 10 + static int ht_avail_error = 0; int err; uint8 clkctl, clkreq, devctl; bcmsdh_info_t *sdh; @@ -551,18 +553,22 @@ dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok) clkctl = 0; sdh = bus->sdh; - if (on) { /* Request HT Avail */ clkreq = bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ; - - - bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err); if (err) { - DHD_ERROR(("%s: HT Avail request error: %d\n", __FUNCTION__, err)); + ht_avail_error++; + if (ht_avail_error < HT_AVAIL_ERROR_MAX) { + DHD_ERROR(("%s: HT Avail request error: %d\n", __FUNCTION__, err)); + } else { + if (ht_avail_error == HT_AVAIL_ERROR_MAX) + dhd_os_send_hang_message(bus->dhd); + } return BCME_ERROR; + } else { + ht_avail_error = 0; } if (pendok && From 6302390d849e286b0d8fe0f7a4ba8f086492b7f7 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 26 Jul 2012 18:02:38 -0700 Subject: [PATCH 2385/2556] net: wireless: bcmdhd: Fill station_info packet fields Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 2365bdd4a540a..b10a5efff0ac0 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -2999,6 +2999,7 @@ wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, sta->idle * 1000)); #endif } else if (wl_get_mode_by_netdev(wl, dev) == WL_MODE_BSS) { + get_pktcnt_t pktcnt; u8 *curmacp = wl_read_prof(wl, dev, WL_PROF_BSSID); err = -ENODEV; if (!wl_get_drv_status(wl, CONNECTED, dev) || @@ -3035,6 +3036,19 @@ wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, sinfo->signal = rssi; WL_DBG(("RSSI %d dBm\n", rssi)); + err = wldev_ioctl(dev, WLC_GET_PKTCNTS, &pktcnt, + sizeof(pktcnt), false); + if (!err) { + sinfo->filled |= (STATION_INFO_RX_PACKETS | + STATION_INFO_RX_DROP_MISC | + STATION_INFO_TX_PACKETS | + STATION_INFO_TX_FAILED); + sinfo->rx_packets = pktcnt.rx_good_pkt; + sinfo->rx_dropped_misc = pktcnt.rx_bad_pkt; + sinfo->tx_packets = pktcnt.tx_good_pkt; + sinfo->tx_failed = pktcnt.tx_bad_pkt; + } + get_station_err: if (err && (err != -ETIMEDOUT) && (err != -EIO)) { /* Disconnect due to zero BSSID or error to get RSSI */ From 8d8460741290736abe867997eb7d1a3ec491da03 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Mon, 13 Aug 2012 11:37:43 -0700 Subject: [PATCH 2386/2556] net: wireless: bcmdhd: Use correct time from boot for TSF Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index b10a5efff0ac0..87d971a5e5dd2 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -4753,11 +4753,11 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) signal = notif_bss_info->rssi * 100; if (!mgmt->u.probe_resp.timestamp) { - struct timeval tv; + struct timespec ts; - do_gettimeofday(&tv); - mgmt->u.probe_resp.timestamp = ((u64)tv.tv_sec * 1000000) - + tv.tv_usec; + get_monotonic_boottime(&ts); + mgmt->u.probe_resp.timestamp = ((u64)ts.tv_sec * 1000000) + + ts.tv_nsec / 1000; } cbss = cfg80211_inform_bss_frame(wiphy, channel, mgmt, From 5012c5275e8d2462cdd624b613df333674f45436 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 22 Aug 2012 11:29:44 -0700 Subject: [PATCH 2387/2556] net: wireless: bcmdhd: Update to Version 5.90.195.98 Change-Id: I2854300bc03b133d8276bea2f36f4c5253c551a1 Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/bcmsdh.c | 7 +- drivers/net/wireless/bcmdhd/bcmsdh_linux.c | 7 +- drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c | 54 +++++---- .../net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c | 4 +- drivers/net/wireless/bcmdhd/dhd.h | 2 +- drivers/net/wireless/bcmdhd/dhd_cfg80211.c | 9 +- drivers/net/wireless/bcmdhd/dhd_custom_gpio.c | 6 +- drivers/net/wireless/bcmdhd/dhd_linux.c | 6 +- drivers/net/wireless/bcmdhd/dhd_sdio.c | 2 +- drivers/net/wireless/bcmdhd/include/epivers.h | 8 +- drivers/net/wireless/bcmdhd/include/sbchipc.h | 7 +- drivers/net/wireless/bcmdhd/include/sdioh.h | 6 +- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 108 +++++++++++++----- drivers/net/wireless/bcmdhd/wl_cfg80211.h | 21 +++- drivers/net/wireless/bcmdhd/wl_cfgp2p.c | 28 ++++- drivers/net/wireless/bcmdhd/wl_cfgp2p.h | 5 + drivers/net/wireless/bcmdhd/wldev_common.c | 5 +- 17 files changed, 204 insertions(+), 81 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/bcmsdh.c b/drivers/net/wireless/bcmdhd/bcmsdh.c index f67b13a03a1c5..89320b6f53d45 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh.c @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdh.c 300445 2011-12-03 05:37:20Z $ + * $Id: bcmsdh.c 344235 2012-07-11 23:47:18Z $ */ /** @@ -362,9 +362,10 @@ bcmsdh_cis_read(void *sdh, uint func, uint8 *cis, uint length) } bcopy(cis, tmp_buf, length); for (tmp_ptr = tmp_buf, ptr = cis; ptr < (cis + length - 4); tmp_ptr++) { - ptr += sprintf((char*)ptr, "%.2x ", *tmp_ptr & 0xff); + ptr += snprintf((char*)ptr, (cis + length - ptr - 4), + "%.2x ", *tmp_ptr & 0xff); if ((((tmp_ptr - tmp_buf) + 1) & 0xf) == 0) - ptr += sprintf((char *)ptr, "\n"); + ptr += snprintf((char *)ptr, (cis + length - ptr -4), "\n"); } MFREE(bcmsdh->osh, tmp_buf, length); } diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_linux.c index d257ddab84a3f..edecb5f2f4c8c 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_linux.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_linux.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdh_linux.c 312788 2012-02-03 23:06:32Z $ + * $Id: bcmsdh_linux.c 343302 2012-07-06 13:07:38Z $ */ /** @@ -684,6 +684,11 @@ extern int sd_uhsimode; module_param(sd_uhsimode, int, 0); #endif +#ifdef BCMSDIOH_TXGLOM +extern uint sd_txglom; +module_param(sd_txglom, uint, 0); +#endif + #ifdef BCMSDH_MODULE EXPORT_SYMBOL(bcmsdh_attach); EXPORT_SYMBOL(bcmsdh_detach); diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c index 374154fb12961..67c2fbf5ea18c 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdh_sdmmc.c 314904 2012-02-14 21:36:04Z $ + * $Id: bcmsdh_sdmmc.c 344243 2012-07-12 00:06:23Z $ */ #include @@ -799,41 +799,49 @@ sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *by #if defined(MMC_SDIO_ABORT) /* to allow abort command through F1 */ else if (regaddr == SDIOD_CCCR_IOABORT) { - sdio_claim_host(gInstance->func[func]); - /* - * this sdio_f0_writeb() can be replaced with another api - * depending upon MMC driver change. - * As of this time, this is temporaray one - */ - sdio_writeb(gInstance->func[func], *byte, regaddr, &err_ret); - sdio_release_host(gInstance->func[func]); + if (gInstance->func[func]) { + sdio_claim_host(gInstance->func[func]); + /* + * this sdio_f0_writeb() can be replaced with another api + * depending upon MMC driver change. + * As of this time, this is temporaray one + */ + sdio_writeb(gInstance->func[func], + *byte, regaddr, &err_ret); + sdio_release_host(gInstance->func[func]); + } } #endif /* MMC_SDIO_ABORT */ else if (regaddr < 0xF0) { sd_err(("bcmsdh_sdmmc: F0 Wr:0x%02x: write disallowed\n", regaddr)); } else { /* Claim host controller, perform F0 write, and release */ - sdio_claim_host(gInstance->func[func]); - sdio_f0_writeb(gInstance->func[func], *byte, regaddr, &err_ret); - sdio_release_host(gInstance->func[func]); + if (gInstance->func[func]) { + sdio_claim_host(gInstance->func[func]); + sdio_f0_writeb(gInstance->func[func], + *byte, regaddr, &err_ret); + sdio_release_host(gInstance->func[func]); + } } } else { /* Claim host controller, perform Fn write, and release */ - sdio_claim_host(gInstance->func[func]); - sdio_writeb(gInstance->func[func], *byte, regaddr, &err_ret); - sdio_release_host(gInstance->func[func]); + if (gInstance->func[func]) { + sdio_claim_host(gInstance->func[func]); + sdio_writeb(gInstance->func[func], *byte, regaddr, &err_ret); + sdio_release_host(gInstance->func[func]); + } } } else { /* CMD52 Read */ /* Claim host controller, perform Fn read, and release */ - sdio_claim_host(gInstance->func[func]); - - if (func == 0) { - *byte = sdio_f0_readb(gInstance->func[func], regaddr, &err_ret); - } else { - *byte = sdio_readb(gInstance->func[func], regaddr, &err_ret); + if (gInstance->func[func]) { + sdio_claim_host(gInstance->func[func]); + if (func == 0) { + *byte = sdio_f0_readb(gInstance->func[func], regaddr, &err_ret); + } else { + *byte = sdio_readb(gInstance->func[func], regaddr, &err_ret); + } + sdio_release_host(gInstance->func[func]); } - - sdio_release_host(gInstance->func[func]); } if (err_ret) { diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c index a78faebe2e7ac..c93e41c6fb40f 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: bcmsdh_sdmmc_linux.c 312783 2012-02-03 22:53:56Z $ + * $Id: bcmsdh_sdmmc_linux.c 331154 2012-05-04 00:41:40Z $ */ #include @@ -188,7 +188,6 @@ static int bcmsdh_sdmmc_suspend(struct device *pdev) if (dhd_os_check_wakelock(bcmsdh_get_drvdata())) return -EBUSY; - sdio_flags = sdio_get_host_pm_caps(func); if (!(sdio_flags & MMC_PM_KEEP_POWER)) { @@ -202,7 +201,6 @@ static int bcmsdh_sdmmc_suspend(struct device *pdev) sd_err(("%s: error while trying to keep power\n", __FUNCTION__)); return ret; } - #if defined(OOB_INTR_ONLY) bcmsdh_oob_intr_set(0); #endif /* defined(OOB_INTR_ONLY) */ diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index 725a909273dc0..6106555f13146 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -24,7 +24,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd.h 333052 2012-05-12 02:09:28Z $ + * $Id: dhd.h 344123 2012-07-11 09:33:49Z $ */ /**************** diff --git a/drivers/net/wireless/bcmdhd/dhd_cfg80211.c b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c index 970216efc7a7a..351c372ffa817 100644 --- a/drivers/net/wireless/bcmdhd/dhd_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/dhd_cfg80211.c @@ -69,6 +69,9 @@ s32 dhd_cfg80211_down(struct wl_priv *wl) return 0; } +/* + * dhd_cfg80211_set_p2p_info : gets called when GO or GC created + */ s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val) { dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); @@ -83,7 +86,7 @@ s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val) dhd_arp_offload_set(dhd, 0); dhd_arp_offload_enable(dhd, false); #endif /* ARP_OFFLOAD_SUPPORT */ - + /* diable all filtering in p2p mode */ dhd_os_set_packet_filter(dhd, 0); /* Setup timeout if Beacons are lost and roam is off to report link down */ @@ -94,6 +97,9 @@ s32 dhd_cfg80211_set_p2p_info(struct wl_priv *wl, int val) return 0; } +/* + * dhd_cfg80211_clean_p2p_info : gets called when GO or GC terminated + */ s32 dhd_cfg80211_clean_p2p_info(struct wl_priv *wl) { dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); @@ -108,7 +114,6 @@ s32 dhd_cfg80211_clean_p2p_info(struct wl_priv *wl) dhd_arp_offload_set(dhd, dhd_arp_mode); dhd_arp_offload_enable(dhd, true); #endif /* ARP_OFFLOAD_SUPPORT */ - dhd_os_set_packet_filter(dhd, 1); /* Setup timeout if Beacons are lost and roam is off to report link down */ diff --git a/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c b/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c index 9750eeb23bce2..de519a57bf8c0 100644 --- a/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c +++ b/drivers/net/wireless/bcmdhd/dhd_custom_gpio.c @@ -20,7 +20,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * -* $Id: dhd_custom_gpio.c,v 1.2.42.1 2010-10-19 00:41:09 Exp $ +* $Id: dhd_custom_gpio.c 339054 2012-06-15 04:56:55Z $ */ #include @@ -97,13 +97,13 @@ int dhd_customer_oob_irq_map(unsigned long *irq_flags_ptr) #endif /* CUSTOMER_HW2 */ if (dhd_oob_gpio_num < 0) { - WL_ERROR(("%s: ERROR customer specific Host GPIO is NOT defined \n", + WL_ERROR(("%s: ERROR customer specific Host GPIO is NOT defined\n", __FUNCTION__)); return (dhd_oob_gpio_num); } WL_ERROR(("%s: customer specific Host GPIO number is (%d)\n", - __FUNCTION__, dhd_oob_gpio_num)); + __FUNCTION__, dhd_oob_gpio_num)); #if defined CUSTOMER_HW host_oob_irq = MSM_GPIO_TO_INT(dhd_oob_gpio_num); diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index d32ce1135f2c1..8aa7ed9f43231 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_linux.c 333885 2012-05-18 00:39:03Z $ + * $Id: dhd_linux.c 344350 2012-07-12 08:35:03Z $ */ #include @@ -1026,7 +1026,7 @@ dhd_op_if(dhd_if_t *ifp) #endif netif_stop_queue(ifp->net); unregister_netdev(ifp->net); - ret = DHD_DEL_IF; + ret = DHD_DEL_IF; /* Make sure the free_netdev() is called */ #ifdef WL_CFG80211 if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { @@ -2648,7 +2648,9 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) } sema_init(&dhd->proto_sem, 1); +#ifdef DHDTHREAD sema_init(&dhd->sdsem, 1); +#endif #ifdef PROP_TXSTATUS spin_lock_init(&dhd->wlfc_spinlock); diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c index e578d702ce11f..ed3da83604b5c 100644 --- a/drivers/net/wireless/bcmdhd/dhd_sdio.c +++ b/drivers/net/wireless/bcmdhd/dhd_sdio.c @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_sdio.c 326662 2012-04-10 06:38:08Z $ + * $Id: dhd_sdio.c 338148 2012-06-11 20:35:45Z $ */ #include diff --git a/drivers/net/wireless/bcmdhd/include/epivers.h b/drivers/net/wireless/bcmdhd/include/epivers.h index 5df25c16b7d9b..19da8f28bc7e8 100644 --- a/drivers/net/wireless/bcmdhd/include/epivers.h +++ b/drivers/net/wireless/bcmdhd/include/epivers.h @@ -33,17 +33,17 @@ #define EPI_RC_NUMBER 195 -#define EPI_INCREMENTAL_NUMBER 75 +#define EPI_INCREMENTAL_NUMBER 98 #define EPI_BUILD_NUMBER 0 -#define EPI_VERSION 5, 90, 195, 75 +#define EPI_VERSION 5, 90, 195, 98 -#define EPI_VERSION_NUM 0x055ac34b +#define EPI_VERSION_NUM 0x055ac362 #define EPI_VERSION_DEV 5.90.195 -#define EPI_VERSION_STR "5.90.195.75" +#define EPI_VERSION_STR "5.90.195.98" #endif diff --git a/drivers/net/wireless/bcmdhd/include/sbchipc.h b/drivers/net/wireless/bcmdhd/include/sbchipc.h index aa4df443e1398..78ced30c502b0 100644 --- a/drivers/net/wireless/bcmdhd/include/sbchipc.h +++ b/drivers/net/wireless/bcmdhd/include/sbchipc.h @@ -5,7 +5,7 @@ * JTAG, 0/1/2 UARTs, clock frequency control, a watchdog interrupt timer, * GPIO interface, extbus, and support for serial and parallel flashes. * - * $Id: sbchipc.h 333924 2012-05-18 04:48:52Z $ + * $Id: sbchipc.h 343982 2012-07-11 00:29:37Z $ * * Copyright (C) 1999-2011, Broadcom Corporation * @@ -1726,6 +1726,11 @@ typedef volatile struct { #define SECI_MODE_SHIFT 4 #define SECI_UPD_SECI (1 << 7) +#define SECI_SLIP_ESC_CHAR 0xDB +#define SECI_SIGNOFF_0 SECI_SLIP_ESC_CHAR +#define SECI_SIGNOFF_1 0 +#define SECI_REFRESH_REQ 0xDA + #define CLKCTL_STS_SECI_CLK_REQ (1 << 8) #define CLKCTL_STS_SECI_CLK_AVAIL (1 << 24) diff --git a/drivers/net/wireless/bcmdhd/include/sdioh.h b/drivers/net/wireless/bcmdhd/include/sdioh.h index 5f47d6f88ceae..1d820d1569e73 100644 --- a/drivers/net/wireless/bcmdhd/include/sdioh.h +++ b/drivers/net/wireless/bcmdhd/include/sdioh.h @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: sdioh.h 300017 2011-12-01 20:30:27Z $ + * $Id: sdioh.h 345478 2012-07-18 06:45:15Z $ */ #ifndef _SDIOH_H @@ -90,6 +90,10 @@ #define SD3_PresetVal_SDR50 0x06a #define SD3_PresetVal_SDR104 0x06c #define SD3_PresetVal_DDR50 0x06e +/* SDIO3.0 Revx specific Registers */ +#define SD3_Tuning_Info_Register 0x0EC +#define SD3_WL_BT_reset_register 0x0F0 + /* preset value indices */ #define SD3_PRESETVAL_INITIAL_IX 0 diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 87d971a5e5dd2..04689546ac8d2 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -358,6 +358,7 @@ static s32 wl_iscan_done(struct wl_priv *wl); static s32 wl_iscan_pending(struct wl_priv *wl); static s32 wl_iscan_inprogress(struct wl_priv *wl); static s32 wl_iscan_aborted(struct wl_priv *wl); +static void wl_scan_timeout_process(struct work_struct *work); /* * find most significant bit set @@ -1518,6 +1519,8 @@ wl_do_escan(struct wl_priv *wl, struct wiphy *wiphy, struct net_device *ndev, s32 passive_scan; wl_scan_results_t *results; WL_SCAN(("Enter \n")); + + mutex_lock(&wl->usr_sync); wl->escan_info.ndev = ndev; wl->escan_info.wiphy = wiphy; wl->escan_info.escan_state = WL_ESCAN_STATE_SCANING; @@ -1526,7 +1529,7 @@ wl_do_escan(struct wl_priv *wl, struct wiphy *wiphy, struct net_device *ndev, &passive_scan, sizeof(passive_scan), false); if (unlikely(err)) { WL_ERR(("error (%d)\n", err)); - return err; + goto exit; } results = (wl_scan_results_t *) wl->escan_info.escan_buf; results->version = 0; @@ -1534,6 +1537,8 @@ wl_do_escan(struct wl_priv *wl, struct wiphy *wiphy, struct net_device *ndev, results->buflen = WL_SCAN_RESULTS_FIXED_SIZE; err = wl_run_escan(wl, ndev, request, WL_SCAN_ACTION_START); +exit: + mutex_unlock(&wl->usr_sync); return err; } @@ -1580,7 +1585,6 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, /* Arm scan timeout timer */ mod_timer(&wl->scan_timeout, jiffies + msecs_to_jiffies(WL_SCAN_TIMER_INTERVAL_MS)); iscan_req = false; - wl->scan_request = request; if (request) { /* scan bss */ ssids = request->ssids; if (wl->iscan_on && (!ssids || !ssids->ssid_len || request->n_ssids != 1)) { @@ -1661,6 +1665,7 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, /* we don't do iscan in ibss */ ssids = this_ssid; } + wl->scan_request = request; wl_set_drv_status(wl, SCANNING, ndev); if (iscan_req) { err = wl_do_iscan(wl, request); @@ -1724,12 +1729,9 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, scan_out: wl_clr_drv_status(wl, SCANNING, ndev); - if (wl->scan_request) { - if (timer_pending(&wl->scan_timeout)) - del_timer_sync(&wl->scan_timeout); - cfg80211_scan_done(wl->scan_request, true); - wl->scan_request = NULL; - } + if (timer_pending(&wl->scan_timeout)) + del_timer_sync(&wl->scan_timeout); + wl->scan_request = NULL; return err; } @@ -1750,6 +1752,7 @@ wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, wl->scan_busy_count++; if (wl->scan_busy_count > WL_SCAN_BUSY_MAX) { wl->scan_busy_count = 0; + WL_ERR(("Continuous scan failures!! Exercising FW hang recovery\n")); net_os_send_hang_message(ndev); } } @@ -3069,6 +3072,7 @@ wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, s32 pm; s32 err = 0; struct wl_priv *wl = wiphy_priv(wiphy); + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); CHECK_SYS_UP(wl); @@ -3077,7 +3081,7 @@ wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, return err; } - pm = enabled ? PM_FAST : PM_OFF; + pm = enabled ? ((dhd->in_suspend) ? PM_MAX : PM_FAST) : PM_OFF; pm = htod32(pm); err = wldev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), true); if (unlikely(err)) { @@ -3511,6 +3515,7 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, wifi_p2p_ie_t *p2p_ie; wpa_ie_fixed_t *wps_ie; scb_val_t scb_val; + wifi_wfd_ie_t *wfd_ie; const struct ieee80211_mgmt *mgmt; struct wl_priv *wl = wiphy_priv(wiphy); struct net_device *dev = NULL; @@ -3518,6 +3523,7 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, s32 bssidx = 0; u32 p2pie_len = 0; u32 wpsie_len = 0; + u32 wfdie_len = 0; u32 id; u32 retry = 0; bool ack = false; @@ -3567,6 +3573,11 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, /* Total length of P2P Information Element */ p2pie_len = p2p_ie->len + sizeof(p2p_ie->len) + sizeof(p2p_ie->id); } + if ((wfd_ie = wl_cfgp2p_find_wfdie((u8 *)(buf + ie_offset), ie_len)) + != NULL) { + /* Total length of WFD Information Element */ + wfdie_len = wfd_ie->len + sizeof(wfd_ie->len) + sizeof(wfd_ie->id); + } if ((wps_ie = wl_cfgp2p_find_wpsie((u8 *)(buf + ie_offset), ie_len)) != NULL) { /* Order of Vendor IE is 1) WPS IE + @@ -3578,7 +3589,7 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *ndev, sizeof(wps_ie->tag); wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_PRBRSP_FLAG, - (u8 *)wps_ie, wpsie_len + p2pie_len); + (u8 *)wps_ie, wpsie_len + p2pie_len+ wfdie_len); } cfg80211_mgmt_tx_status(ndev, *cookie, buf, len, true, GFP_KERNEL); goto exit; @@ -4087,11 +4098,13 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, wpa_ie_fixed_t *wpa_ie; bcm_tlv_t *wpa2_ie; wifi_p2p_ie_t *p2p_ie; + wifi_wfd_ie_t *wfd_ie; bool is_bssup = false; bool update_bss = false; bool pbc = false; u16 wpsie_len = 0; u16 p2pie_len = 0; + u32 wfdie_len = 0; u8 beacon_ie[IE_MAX_LEN]; s32 ie_offset = 0; s32 bssidx = 0; @@ -4149,10 +4162,24 @@ wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, } else { WL_ERR(("No P2PIE in beacon \n")); } + /* find the WFD IEs */ + if ((wfd_ie = wl_cfgp2p_find_wfdie((u8 *)info->tail, info->tail_len)) != NULL) { + /* Total length of P2P Information Element */ + wfdie_len = wfd_ie->len + sizeof(wfd_ie->len) + sizeof(wfd_ie->id); + if ((wpsie_len + p2pie_len + wfdie_len) < IE_MAX_LEN) { + memcpy(&beacon_ie[wpsie_len + p2pie_len], wfd_ie, wfdie_len); + } else { + WL_ERR(("Found WFD IE but there is no space, (%d)(%d)(%d)\n", + wpsie_len, p2pie_len, wfdie_len)); + wfdie_len = 0; + } + } else { + WL_ERR(("No WFDIE in beacon \n")); + } /* add WLC_E_PROBREQ_MSG event to respose probe_request from STA */ wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_BEACON_FLAG, - beacon_ie, wpsie_len + p2pie_len); + beacon_ie, wpsie_len + p2pie_len + wfdie_len); /* find the RSN_IE */ if ((wpa2_ie = bcm_parse_tlvs((u8 *)info->tail, info->tail_len, @@ -4714,8 +4741,9 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) } notif_bss_info->rssi = dtoh16(bi->RSSI); memcpy(mgmt->bssid, &bi->BSSID, ETHER_ADDR_LEN); - mgmt_type = wl->active_scan ? - IEEE80211_STYPE_PROBE_RESP : IEEE80211_STYPE_BEACON; + mgmt_type = (bi->flags & WL_BSS_FLAGS_FROM_BEACON) ? + IEEE80211_STYPE_BEACON : IEEE80211_STYPE_PROBE_RESP; + if (!memcmp(bi->SSID, sr->ssid.SSID, bi->SSID_len)) { mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | mgmt_type); } @@ -4744,7 +4772,7 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) return -EINVAL; } - WL_DBG(("SSID : \"%s\", rssi %d, channel %d, capability : 0x04%x, bssid %pM " + WL_DBG(("SSID : \"%s\", rssi %d, channel %d, capability : 0x04%x, bssid %pM" "mgmt_type %d frame_len %d\n", bi->SSID, notif_bss_info->rssi, notif_bss_info->channel, mgmt->u.beacon.capab_info, &bi->BSSID, mgmt_type, @@ -6018,7 +6046,9 @@ static void wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted) unsigned long flags; WL_DBG(("Enter \n")); - wl->scan_busy_count = 0; + if(!aborted) + wl->scan_busy_count = 0; + if (!wl_get_drv_status(wl, SCANNING, ndev)) { wl_clr_drv_status(wl, SCANNING, ndev); WL_ERR(("Scan complete while device not scanning\n")); @@ -6175,13 +6205,30 @@ static void wl_scan_timeout(unsigned long data) { struct wl_priv *wl = (struct wl_priv *)data; + schedule_work(&wl->work_scan_timeout); +} + +static void wl_scan_timeout_process(struct work_struct *work) +{ + struct wl_priv *wl; + + wl = (wl_priv_t *)container_of(work, wl_priv_t, work_scan_timeout); + if (wl->scan_request) { WL_ERR(("timer expired\n")); if (wl->escan_on) - wl_notify_escan_complete(wl, wl->escan_info.ndev, true, false); + wl_notify_escan_complete(wl, wl->escan_info.ndev, true, true); else wl_notify_iscan_complete(wl_to_iscan(wl), true); } + + /* Assume FW is in bad state if there are continuous scan timeouts */ + wl->scan_busy_count++; + if (wl->scan_busy_count > WL_SCAN_BUSY_MAX) { + wl->scan_busy_count = 0; + WL_ERR(("Continuous scan timeouts!! Exercising FW hang recovery\n")); + net_os_send_hang_message(wl->escan_info.ndev); + } } static void wl_iscan_timer(unsigned long data) @@ -6273,7 +6320,9 @@ static s32 wl_notify_escan_complete(struct wl_priv *wl, WL_DBG(("Enter \n")); - wl->scan_busy_count = 0; + if(!aborted) + wl->scan_busy_count = 0; + if (wl->scan_request) { if (wl->scan_request->dev == wl->p2p_net) dev = wl_to_prmry_ndev(wl); @@ -6344,8 +6393,11 @@ static s32 wl_escan_handler(struct wl_priv *wl, u32 i; wifi_p2p_ie_t * p2p_ie; u8 *p2p_dev_addr = NULL; + WL_DBG((" enter event type : %d, status : %d \n", ntoh32(e->event_type), ntoh32(e->status))); + + mutex_lock(&wl->usr_sync); /* P2P SCAN is coming from primary interface */ if (wl_get_p2p_status(wl, SCANNING)) { if (wl_get_drv_status_all(wl, SENDING_ACT_FRM)) @@ -6358,7 +6410,7 @@ static s32 wl_escan_handler(struct wl_priv *wl, !wl_get_drv_status(wl, SCANNING, ndev)) { WL_ERR(("escan is not ready ndev %p wl->escan_on %d drv_status 0x%x\n", ndev, wl->escan_on, wl_get_drv_status(wl, SCANNING, ndev))); - return err; + goto exit; } if (status == WLC_E_STATUS_PARTIAL) { @@ -6448,7 +6500,6 @@ static s32 wl_escan_handler(struct wl_priv *wl, bss->RSSI = bi->RSSI; bss->flags |= WLC_BSS_RSSI_ON_CHANNEL; } - goto exit; } } @@ -6456,7 +6507,6 @@ static s32 wl_escan_handler(struct wl_priv *wl, list->version = dtoh32(bi->version); list->buflen += bi_length; list->count++; - } } @@ -6469,12 +6519,10 @@ static s32 wl_escan_handler(struct wl_priv *wl, if (wl->afx_hdl->peer_chan == WL_INVALID) complete(&wl->act_frm_scan); } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { - mutex_lock(&wl->usr_sync); WL_INFO(("ESCAN COMPLETED\n")); wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; wl_inform_bss(wl); wl_notify_escan_complete(wl, ndev, false, false); - mutex_unlock(&wl->usr_sync); } } else if (status == WLC_E_STATUS_ABORT) { @@ -6486,14 +6534,15 @@ static s32 wl_escan_handler(struct wl_priv *wl, if (wl->afx_hdl->peer_chan == WL_INVALID) complete(&wl->act_frm_scan); } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { - mutex_lock(&wl->usr_sync); WL_INFO(("ESCAN ABORTED\n")); wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; wl_inform_bss(wl); wl_notify_escan_complete(wl, ndev, true, false); - mutex_unlock(&wl->usr_sync); } } + else if (status == WLC_E_STATUS_NEWSCAN) { + /* Do Nothing. Ignore this event */ + } else { WL_ERR(("unexpected Escan Event %d : abort\n", status)); wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; @@ -6504,14 +6553,13 @@ static s32 wl_escan_handler(struct wl_priv *wl, if (wl->afx_hdl->peer_chan == WL_INVALID) complete(&wl->act_frm_scan); } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { - mutex_lock(&wl->usr_sync); wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; wl_inform_bss(wl); wl_notify_escan_complete(wl, ndev, true, false); - mutex_unlock(&wl->usr_sync); } } exit: + mutex_unlock(&wl->usr_sync); return err; } @@ -6574,6 +6622,7 @@ static s32 wl_init_priv(struct wl_priv *wl) return -ENOMEM; wl_init_event_handler(wl); mutex_init(&wl->usr_sync); + INIT_WORK(&wl->work_scan_timeout, wl_scan_timeout_process); err = wl_init_scan(wl); if (err) return err; @@ -6593,6 +6642,7 @@ static void wl_deinit_priv(struct wl_priv *wl) wl_link_down(wl); del_timer_sync(&wl->scan_timeout); wl_term_iscan(wl); + cancel_work_sync(&wl->work_scan_timeout); wl_deinit_priv_mem(wl); unregister_netdevice_notifier(&wl_cfg80211_netdev_notifier); } @@ -7097,6 +7147,10 @@ static int wl_construct_reginfo(struct wl_priv *wl, s32 bw_cap) band = IEEE80211_BAND_5GHZ; ht40_allowed = (bw_cap == WLC_N_BW_20ALL) ? FALSE : TRUE; } + else { + WL_ERR(("Invalid Channel received %x\n", channel)); + continue; + } for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) { if (band_chan_arr[j].hw_value == channel) { @@ -7225,7 +7279,7 @@ s32 wl_update_wiphybands(struct wl_priv *wl) wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; index = IEEE80211_BAND_2GHZ; - if (bandlist[i] == WLC_BAND_2G && bw_cap == WLC_N_BW_40ALL) + if (bw_cap == WLC_N_BW_40ALL) wiphy->bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; } if ((index >= 0) && nmode) { diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index 49bed703062af..974770e0e899d 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -61,13 +61,24 @@ struct wl_ibss; /* 0 invalidates all debug messages. default is 1 */ #define WL_DBG_LEVEL 0xFF -#define WL_ERR(args) \ +#if defined(DHD_DEBUG) +#define WL_ERR(args) \ do { \ - if (wl_dbg_level & WL_DBG_ERR) { \ + if (wl_dbg_level & WL_DBG_ERR) { \ printk(KERN_ERR "CFG80211-ERROR) %s : ", __func__); \ printk args; \ } \ } while (0) +#else /* defined(DHD_DEBUG) */ +#define WL_ERR(args) \ +do { \ + if ((wl_dbg_level & WL_DBG_ERR) && net_ratelimit()) { \ + printk(KERN_INFO "CFG80211-ERROR) %s : ", __func__); \ + printk args; \ + } \ +} while (0) +#endif /* defined(DHD_DEBUG) */ + #ifdef WL_INFO #undef WL_INFO #endif @@ -387,7 +398,7 @@ struct afx_hdl { }; /* private data of cfg80211 interface */ -struct wl_priv { +typedef struct wl_priv { struct wireless_dev *wdev; /* representing wl cfg80211 device */ struct wireless_dev *p2p_wdev; /* representing wl cfg80211 device for P2P */ @@ -457,7 +468,9 @@ struct wl_priv { u16 hostapd_chan; /* remember chan requested by framework for hostapd */ u16 deauth_reason; /* Place holder to save deauth/disassoc reasons */ u16 scan_busy_count; -}; + struct work_struct work_scan_timeout; +} wl_priv_t; + static inline struct wl_bss_info *next_bss(struct wl_scan_results *list, struct wl_bss_info *bss) { diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c index bdcfa95307c75..f06ae50b12ab5 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c @@ -641,8 +641,8 @@ wl_cfgp2p_enable_discovery(struct wl_priv *wl, struct net_device *dev, } set_ie: ret = wl_cfgp2p_set_management_ie(wl, dev, - wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE), - VNDR_IE_PRBREQ_FLAG, ie, ie_len); + wl_cfgp2p_find_idx(wl, dev), + VNDR_IE_PRBREQ_FLAG, ie, ie_len); if (unlikely(ret < 0)) { CFGP2P_ERR(("set probreq ie occurs error %d\n", ret)); @@ -846,6 +846,11 @@ wl_cfgp2p_act_frm_search(struct wl_priv *wl, struct net_device *ndev, /* Check whether the given IE looks like WFA P2P IE. */ #define wl_cfgp2p_is_p2p_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ (const uint8 *)WFA_OUI, WFA_OUI_LEN, WFA_OUI_TYPE_P2P) + /* Check whether the given IE looks like WFA WFDisplay IE. */ +#define WFA_OUI_TYPE_WFD 0x0a /* WiFi Display OUI TYPE */ +#define wl_cfgp2p_is_wfd_ie(ie, tlvs, len) wl_cfgp2p_has_ie(ie, tlvs, len, \ + (const uint8 *)WFA_OUI, WFA_OUI_LEN, WFA_OUI_TYPE_WFD) + /* Delete and Set a management vndr ie to firmware * Parameters: * @wl : wl_private data @@ -970,7 +975,8 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss ie_len = ie_buf[pos++]; if ((ie_id == DOT11_MNG_VS_ID) && (wl_cfgp2p_is_wps_ie(&ie_buf[pos-2], NULL, 0) || - wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0))) { + wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0) || + wl_cfgp2p_is_wfd_ie(&ie_buf[pos-2], NULL, 0))) { CFGP2P_INFO(("DELELED ID : %d, Len : %d , OUI :" "%02x:%02x:%02x\n", ie_id, ie_len, ie_buf[pos], ie_buf[pos+1], ie_buf[pos+2])); @@ -996,7 +1002,8 @@ wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bss ie_len = ie_buf[pos++]; if ((ie_id == DOT11_MNG_VS_ID) && (wl_cfgp2p_is_wps_ie(&ie_buf[pos-2], NULL, 0) || - wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0))) { + wl_cfgp2p_is_p2p_ie(&ie_buf[pos-2], NULL, 0) || + wl_cfgp2p_is_wfd_ie(&ie_buf[pos-2], NULL, 0))) { CFGP2P_INFO(("ADDED ID : %d, Len : %d , OUI :" "%02x:%02x:%02x\n", ie_id, ie_len, ie_buf[pos], ie_buf[pos+1], ie_buf[pos+2])); @@ -1107,6 +1114,19 @@ wl_cfgp2p_find_p2pie(u8 *parse, u32 len) return NULL; } +wifi_wfd_ie_t * +wl_cfgp2p_find_wfdie(u8 *parse, u32 len) +{ + bcm_tlv_t *ie; + + while ((ie = bcm_parse_tlvs(parse, (int)len, DOT11_MNG_VS_ID))) { + if (wl_cfgp2p_is_wfd_ie((uint8*)ie, &parse, &len)) { + return (wifi_wfd_ie_t *)ie; + } + } + return NULL; +} + static s32 wl_cfgp2p_vndr_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, s32 pktflag, s8 *oui, s32 ie_id, s8 *data, s32 data_len, s32 delete) diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h index 05323ed5d4817..03a645aea31a5 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h @@ -31,6 +31,8 @@ struct wl_priv; extern u32 wl_dbg_level; +typedef struct wifi_p2p_ie wifi_wfd_ie_t; + /* Enumeration of the usages of the BSSCFGs used by the P2P Library. Do not * confuse this with a bsscfg index. This value is an index into the * saved_ie[] array of structures which in turn contains a bsscfg index field. @@ -192,6 +194,9 @@ wl_cfgp2p_find_wpsie(u8 *parse, u32 len); extern wifi_p2p_ie_t * wl_cfgp2p_find_p2pie(u8 *parse, u32 len); +extern wifi_wfd_ie_t * +wl_cfgp2p_find_wfdie(u8 *parse, u32 len); + extern s32 wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, s32 pktflag, const u8 *vndr_ie, u32 vndr_ie_len); diff --git a/drivers/net/wireless/bcmdhd/wldev_common.c b/drivers/net/wireless/bcmdhd/wldev_common.c index 51a80645fb349..7bea3dcfa7163 100644 --- a/drivers/net/wireless/bcmdhd/wldev_common.c +++ b/drivers/net/wireless/bcmdhd/wldev_common.c @@ -334,8 +334,11 @@ int wldev_set_country( scb_val_t scbval; char smbuf[WLC_IOCTL_SMLEN]; - if (!country_code) + if (!country_code) { + WLDEV_ERROR(("%s: set country failed for %s\n", + __FUNCTION__, country_code)); return error; + } error = wldev_iovar_getbuf(dev, "country", &cspec, sizeof(cspec), smbuf, sizeof(smbuf), NULL); From 6c2c9b950d1f69308e0c8a25ec13b137e31f3439 Mon Sep 17 00:00:00 2001 From: Nandita Dukkipati Date: Sun, 21 Aug 2011 20:21:57 +0000 Subject: [PATCH 2388/2556] Proportional Rate Reduction for TCP. This patch implements Proportional Rate Reduction (PRR) for TCP. PRR is an algorithm that determines TCP's sending rate in fast recovery. PRR avoids excessive window reductions and aims for the actual congestion window size at the end of recovery to be as close as possible to the window determined by the congestion control algorithm. PRR also improves accuracy of the amount of data sent during loss recovery. The patch implements the recommended flavor of PRR called PRR-SSRB (Proportional rate reduction with slow start reduction bound) and replaces the existing rate halving algorithm. PRR improves upon the existing Linux fast recovery under a number of conditions including: 1) burst losses where the losses implicitly reduce the amount of outstanding data (pipe) below the ssthresh value selected by the congestion control algorithm and, 2) losses near the end of short flows where application runs out of data to send. As an example, with the existing rate halving implementation a single loss event can cause a connection carrying short Web transactions to go into the slow start mode after the recovery. This is because during recovery Linux pulls the congestion window down to packets_in_flight+1 on every ACK. A short Web response often runs out of new data to send and its pipe reduces to zero by the end of recovery when all its packets are drained from the network. Subsequent HTTP responses using the same connection will have to slow start to raise cwnd to ssthresh. PRR on the other hand aims for the cwnd to be as close as possible to ssthresh by the end of recovery. A description of PRR and a discussion of its performance can be found at the following links: - IETF Draft: http://tools.ietf.org/html/draft-mathis-tcpm-proportional-rate-reduction-01 - IETF Slides: http://www.ietf.org/proceedings/80/slides/tcpm-6.pdf http://tools.ietf.org/agenda/81/slides/tcpm-2.pdf - Paper to appear in Internet Measurements Conference (IMC) 2011: Improving TCP Loss Recovery Nandita Dukkipati, Matt Mathis, Yuchung Cheng Signed-off-by: Nandita Dukkipati Signed-off-by: David S. Miller --- include/linux/tcp.h | 4 +++ net/ipv4/tcp_input.c | 58 ++++++++++++++++++++++++++++++++++++++----- net/ipv4/tcp_output.c | 7 +++++- 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index e64f4c67d0ef7..053b24bd899f7 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -378,6 +378,10 @@ struct tcp_sock { u32 snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this */ u32 snd_cwnd_used; u32 snd_cwnd_stamp; + u32 prior_cwnd; /* Congestion window at start of Recovery. */ + u32 prr_delivered; /* Number of newly delivered packets to + * receiver in Recovery. */ + u32 prr_out; /* Total number of pkts sent during Recovery. */ u32 rcv_wnd; /* Current receiver window */ u32 write_seq; /* Tail(+1) of data held in tcp send buffer */ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 5f1ea7cb18afc..e6e900174e817 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2821,9 +2821,13 @@ static int tcp_try_undo_loss(struct sock *sk) static inline void tcp_complete_cwr(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); - /* Do not moderate cwnd if it's already undone in cwr or recovery */ - if (tp->undo_marker && tp->snd_cwnd > tp->snd_ssthresh) { - tp->snd_cwnd = tp->snd_ssthresh; + + /* Do not moderate cwnd if it's already undone in cwr or recovery. */ + if (tp->undo_marker) { + if (inet_csk(sk)->icsk_ca_state == TCP_CA_CWR) + tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh); + else /* PRR */ + tp->snd_cwnd = tp->snd_ssthresh; tp->snd_cwnd_stamp = tcp_time_stamp; } tcp_ca_event(sk, CA_EVENT_COMPLETE_CWR); @@ -2941,6 +2945,38 @@ void tcp_simple_retransmit(struct sock *sk) } EXPORT_SYMBOL(tcp_simple_retransmit); +/* This function implements the PRR algorithm, specifcally the PRR-SSRB + * (proportional rate reduction with slow start reduction bound) as described in + * http://www.ietf.org/id/draft-mathis-tcpm-proportional-rate-reduction-01.txt. + * It computes the number of packets to send (sndcnt) based on packets newly + * delivered: + * 1) If the packets in flight is larger than ssthresh, PRR spreads the + * cwnd reductions across a full RTT. + * 2) If packets in flight is lower than ssthresh (such as due to excess + * losses and/or application stalls), do not perform any further cwnd + * reductions, but instead slow start up to ssthresh. + */ +static void tcp_update_cwnd_in_recovery(struct sock *sk, int newly_acked_sacked, + int fast_rexmit, int flag) +{ + struct tcp_sock *tp = tcp_sk(sk); + int sndcnt = 0; + int delta = tp->snd_ssthresh - tcp_packets_in_flight(tp); + + if (tcp_packets_in_flight(tp) > tp->snd_ssthresh) { + u64 dividend = (u64)tp->snd_ssthresh * tp->prr_delivered + + tp->prior_cwnd - 1; + sndcnt = div_u64(dividend, tp->prior_cwnd) - tp->prr_out; + } else { + sndcnt = min_t(int, delta, + max_t(int, tp->prr_delivered - tp->prr_out, + newly_acked_sacked) + 1); + } + + sndcnt = max(sndcnt, (fast_rexmit ? 1 : 0)); + tp->snd_cwnd = tcp_packets_in_flight(tp) + sndcnt; +} + /* Process an event, which can update packets-in-flight not trivially. * Main goal of this function is to calculate new estimate for left_out, * taking into account both packets sitting in receiver's buffer and @@ -2952,7 +2988,8 @@ EXPORT_SYMBOL(tcp_simple_retransmit); * It does _not_ decide what to send, it is made in function * tcp_xmit_retransmit_queue(). */ -static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, int flag) +static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, + int newly_acked_sacked, int flag) { struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); @@ -3102,13 +3139,17 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, int flag) tp->bytes_acked = 0; tp->snd_cwnd_cnt = 0; + tp->prior_cwnd = tp->snd_cwnd; + tp->prr_delivered = 0; + tp->prr_out = 0; tcp_set_ca_state(sk, TCP_CA_Recovery); fast_rexmit = 1; } if (do_lost || (tcp_is_fack(tp) && tcp_head_timedout(sk))) tcp_update_scoreboard(sk, fast_rexmit); - tcp_cwnd_down(sk, flag); + tp->prr_delivered += newly_acked_sacked; + tcp_update_cwnd_in_recovery(sk, newly_acked_sacked, fast_rexmit, flag); tcp_xmit_retransmit_queue(sk); } @@ -3622,6 +3663,8 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) u32 prior_in_flight; u32 prior_fackets; int prior_packets; + int prior_sacked = tp->sacked_out; + int newly_acked_sacked = 0; int frto_cwnd = 0; /* If the ack is older than previous acks @@ -3693,6 +3736,9 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) /* See if we can take anything off of the retransmit queue. */ flag |= tcp_clean_rtx_queue(sk, prior_fackets, prior_snd_una); + newly_acked_sacked = (prior_packets - prior_sacked) - + (tp->packets_out - tp->sacked_out); + if (tp->frto_counter) frto_cwnd = tcp_process_frto(sk, flag); /* Guarantee sacktag reordering detection against wrap-arounds */ @@ -3705,7 +3751,7 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) tcp_may_raise_cwnd(sk, flag)) tcp_cong_avoid(sk, ack, prior_in_flight); tcp_fastretrans_alert(sk, prior_packets - tp->packets_out, - flag); + newly_acked_sacked, flag); } else { if ((flag & FLAG_DATA_ACKED) && !frto_cwnd) tcp_cong_avoid(sk, ack, prior_in_flight); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 8b0d0167e44aa..5464dc4f4fbdd 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1796,11 +1796,13 @@ static int tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, tcp_event_new_data_sent(sk, skb); tcp_minshall_update(tp, mss_now, skb); - sent_pkts++; + sent_pkts += tcp_skb_pcount(skb); if (push_one) break; } + if (inet_csk(sk)->icsk_ca_state == TCP_CA_Recovery) + tp->prr_out += sent_pkts; if (likely(sent_pkts)) { tcp_cwnd_validate(sk); @@ -2294,6 +2296,9 @@ void tcp_xmit_retransmit_queue(struct sock *sk) return; NET_INC_STATS_BH(sock_net(sk), mib_idx); + if (inet_csk(sk)->icsk_ca_state == TCP_CA_Recovery) + tp->prr_out += tcp_skb_pcount(skb); + if (skb == tcp_write_queue_head(sk)) inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, inet_csk(sk)->icsk_rto, From 98cd43863541bab70c03dce1fb038231c2a57b93 Mon Sep 17 00:00:00 2001 From: Marco Navarra Date: Wed, 2 May 2012 07:48:02 -0700 Subject: [PATCH 2389/2556] Staging: android: fixed white spaces coding style issue in logger.c This patch fixes some space-before-tabs warnings found by checkpatch tool on the staging android driver file logger.c Signed-off-by: Marco Navarra Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/logger.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/android/logger.c b/drivers/staging/android/logger.c index fa76ce7678a6e..ffc2d043dd8e4 100644 --- a/drivers/staging/android/logger.c +++ b/drivers/staging/android/logger.c @@ -37,7 +37,7 @@ * mutex 'mutex'. */ struct logger_log { - unsigned char *buffer;/* the ring buffer itself */ + unsigned char *buffer;/* the ring buffer itself */ struct miscdevice misc; /* misc device representing the log */ wait_queue_head_t wq; /* wait queue for readers */ struct list_head readers; /* this log's readers */ @@ -67,9 +67,9 @@ struct logger_reader { * * This isn't aesthetic. We have several goals: * - * 1) Need to quickly obtain the associated log during an I/O operation - * 2) Readers need to maintain state (logger_reader) - * 3) Writers need to be very fast (open() should be a near no-op) + * 1) Need to quickly obtain the associated log during an I/O operation + * 2) Readers need to maintain state (logger_reader) + * 3) Writers need to be very fast (open() should be a near no-op) * * In the reader case, we can trivially go file->logger_reader->logger_log. * For a writer, we don't want to maintain a logger_reader, so we just go @@ -147,9 +147,9 @@ static ssize_t do_read_log_to_user(struct logger_log *log, * * Behavior: * - * - O_NONBLOCK works - * - If there are no log entries to read, blocks until log is written to - * - Atomically reads exactly one log entry + * - O_NONBLOCK works + * - If there are no log entries to read, blocks until log is written to + * - Atomically reads exactly one log entry * * Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno to EINVAL if read * buffer is insufficient to hold next entry. From 64be14a57c1d23db2bb5e04dbeeee813d4d063b8 Mon Sep 17 00:00:00 2001 From: Marco Navarra Date: Thu, 22 Dec 2011 13:27:07 +0100 Subject: [PATCH 2390/2556] Staging: android: fixed a space warning in binder.h This patch fixes a simple tab-space warning in binder.h found by checkpatch tool Signed-off-by: Marco Navarra Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/binder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/android/binder.h b/drivers/staging/android/binder.h index 863ae1ad5d558..25ab6f2759e90 100644 --- a/drivers/staging/android/binder.h +++ b/drivers/staging/android/binder.h @@ -84,7 +84,7 @@ struct binder_version { /* This is the current protocol version. */ #define BINDER_CURRENT_PROTOCOL_VERSION 7 -#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) +#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) #define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, int64_t) #define BINDER_SET_MAX_THREADS _IOW('b', 5, size_t) #define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, int) From b07ba620c30a8ff1980a0c3764908dd580605933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Fri, 20 Jan 2012 19:56:21 -0800 Subject: [PATCH 2391/2556] Staging: android: binder: Don't call dump_stack in binder_vma_open MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If user-space partially unmaps the driver, binder_vma_open would dump the kernel stack. This is not a kernel bug however and will be treated as if the whole area was unmapped once binder_vma_close gets called. Signed-off-by: Arve HjønnevÃ¥g Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/binder.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index a0763da99a84c..69dd109985860 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -2821,7 +2821,6 @@ static void binder_vma_open(struct vm_area_struct *vma) proc->pid, vma->vm_start, vma->vm_end, (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, (unsigned long)pgprot_val(vma->vm_page_prot)); - dump_stack(); } static void binder_vma_close(struct vm_area_struct *vma) From b9ecbbb4abd9a012042bc266156a9153d1c02040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 1 Feb 2012 15:29:13 -0800 Subject: [PATCH 2392/2556] Staging: android: binder: Fix crashes when sharing a binder file between processes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Opening the binder driver and sharing the file returned with other processes (e.g. by calling fork) can crash the kernel. Prevent these crashes with the following changes: - Add a mutex to protect against two processes mmapping the same binder_proc. - After locking mmap_sem, check that the vma we want to access (still) points to the same mm_struct. - Use proc->tsk instead of current to get the files struct since this is where we get the rlimit from. Signed-off-by: Arve HjønnevÃ¥g Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/binder.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 69dd109985860..87d15f16b3c23 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -39,6 +39,7 @@ static DEFINE_MUTEX(binder_lock); static DEFINE_MUTEX(binder_deferred_lock); +static DEFINE_MUTEX(binder_mmap_lock); static HLIST_HEAD(binder_procs); static HLIST_HEAD(binder_deferred_list); @@ -633,6 +634,11 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate, if (mm) { down_write(&mm->mmap_sem); vma = proc->vma; + if (vma && mm != vma->vm_mm) { + pr_err("binder: %d: vma mm and task mm mismatch\n", + proc->pid); + vma = NULL; + } } if (allocate == 0) @@ -2864,6 +2870,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) } vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE; + mutex_lock(&binder_mmap_lock); if (proc->buffer) { ret = -EBUSY; failure_string = "already mapped"; @@ -2878,6 +2885,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) } proc->buffer = area->addr; proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer; + mutex_unlock(&binder_mmap_lock); #ifdef CONFIG_CPU_CACHE_VIPT if (cache_is_vipt_aliasing()) { @@ -2912,7 +2920,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) binder_insert_free_buffer(proc, buffer); proc->free_async_space = proc->buffer_size / 2; barrier(); - proc->files = get_files_struct(current); + proc->files = get_files_struct(proc->tsk); proc->vma = vma; /*binder_debug(BINDER_DEBUG_TOP_ERRORS, @@ -2924,10 +2932,12 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) kfree(proc->pages); proc->pages = NULL; err_alloc_pages_failed: + mutex_lock(&binder_mmap_lock); vfree(proc->buffer); proc->buffer = NULL; err_get_vm_area_failed: err_already_mapped: + mutex_unlock(&binder_mmap_lock); err_bad_arg: binder_debug(BINDER_DEBUG_TOP_ERRORS, "binder_mmap: %d %lx-%lx %s failed %d\n", From feaac2626bd9e19332f85383adcd82e82f43654f Mon Sep 17 00:00:00 2001 From: faux123 Date: Sun, 29 Jul 2012 18:08:25 -0700 Subject: [PATCH 2393/2556] staging: android/lowmemorykiller: Don't grab tasklist_lock Grabbing tasklist_lock has its disadvantages, i.e. it blocks process creation and destruction. If there are lots of processes, blocking doesn't sound as a great idea. For LMK, it is sufficient to surround tasks list traverse with rcu_read_{,un}lock(). >From now on using force_sig() is not safe, as it can race with an already exiting task, so we use send_sig() now. As a downside, it won't kill PID namespace init processes, but that's not what we want anyway. Suggested-by: Oleg Nesterov Signed-off-by: Anton Vorontsov Reviewed-by: Oleg Nesterov Signed-off-by: Greg Kroah-Hartman modified for HTC kernel from Linux 3.4+ by faux123 --- drivers/staging/android/lowmemorykiller.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 86d51959b29f8..a6f9c2d8cafa0 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -34,6 +34,7 @@ #include #include #include +#include #include static uint32_t lowmem_debug_level = 2; @@ -131,7 +132,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) } selected_oom_adj = min_adj; - read_lock(&tasklist_lock); + rcu_read_lock(); for_each_process(p) { struct mm_struct *mm; struct signal_struct *sig; @@ -172,12 +173,12 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) selected_oom_adj, selected_tasksize); lowmem_deathpending = selected; lowmem_deathpending_timeout = jiffies + HZ; - force_sig(SIGKILL, selected); + send_sig(SIGKILL, selected, 0); rem -= selected_tasksize; } lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n", sc->nr_to_scan, sc->gfp_mask, rem); - read_unlock(&tasklist_lock); + rcu_read_unlock(); return rem; } From 8fea6a0a865bba6c86f1667d4760b5fc19bae05f Mon Sep 17 00:00:00 2001 From: faux123 Date: Thu, 10 May 2012 23:11:15 -0700 Subject: [PATCH 2394/2556] staging: android/lowmemorykiller: Better mm handling LMK should not directly check for task->mm. The reason is that the process' threads may exit or detach its mm via use_mm(), but other threads may still have a valid mm. To catch this we use find_lock_task_mm(), which walks up all threads and returns an appropriate task (with lock held). Suggested-by: Oleg Nesterov Reviewed-by: Oleg Nesterov Signed-off-by: Anton Vorontsov Acked-by: KOSAKI Motohiro Signed-off-by: Greg Kroah-Hartman modified for HTC kernel from Linux 3.4+ by faux123 --- drivers/staging/android/lowmemorykiller.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index a6f9c2d8cafa0..feeb28d9827a2 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -82,7 +82,7 @@ task_notify_func(struct notifier_block *self, unsigned long val, void *data) static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) { - struct task_struct *p; + struct task_struct *tsk; struct task_struct *selected = NULL; int rem = 0; int tasksize; @@ -133,15 +133,17 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) selected_oom_adj = min_adj; rcu_read_lock(); - for_each_process(p) { - struct mm_struct *mm; + for_each_process(tsk) { + struct task_struct *p; struct signal_struct *sig; int oom_adj; - task_lock(p); - mm = p->mm; + p = find_lock_task_mm(tsk); + if (!p) + continue; + sig = p->signal; - if (!mm || !sig) { + if (!sig) { task_unlock(p); continue; } @@ -150,7 +152,7 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) task_unlock(p); continue; } - tasksize = get_mm_rss(mm); + tasksize = get_mm_rss(p->mm); task_unlock(p); if (tasksize <= 0) continue; From 75a0756ec20040ab39bd71abce060f5a923e2109 Mon Sep 17 00:00:00 2001 From: faux123 Date: Sun, 29 Jul 2012 18:09:50 -0700 Subject: [PATCH 2395/2556] staging: android/lowmemorykiller: No need for task->signal check task->signal == NULL is not possible, so no need for these checks. Suggested-by: Oleg Nesterov Reviewed-by: Oleg Nesterov Signed-off-by: Anton Vorontsov Acked-by: KOSAKI Motohiro Signed-off-by: Greg Kroah-Hartman modified for HTC kernel from Linux 3.4+ by faux123 --- drivers/staging/android/lowmemorykiller.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index feeb28d9827a2..0bee0fe996c88 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -135,19 +135,13 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) rcu_read_lock(); for_each_process(tsk) { struct task_struct *p; - struct signal_struct *sig; int oom_adj; p = find_lock_task_mm(tsk); if (!p) continue; - sig = p->signal; - if (!sig) { - task_unlock(p); - continue; - } - oom_adj = sig->oom_adj; + oom_adj = p->signal->oom_adj; if (oom_adj < min_adj) { task_unlock(p); continue; From 6325c9041c75c3642c64c4040a4d105ce6f47571 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Mon, 6 Feb 2012 20:30:01 +0400 Subject: [PATCH 2396/2556] staging: android/lowmemorykiller: Do not kill kernel threads LMK should not try killing kernel threads. Suggested-by: Oleg Nesterov Reviewed-by: Oleg Nesterov Signed-off-by: Anton Vorontsov Acked-by: KOSAKI Motohiro Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/lowmemorykiller.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 0bee0fe996c88..5dbafc85c9c53 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -137,6 +137,9 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) struct task_struct *p; int oom_adj; + if (tsk->flags & PF_KTHREAD) + continue; + p = find_lock_task_mm(tsk); if (!p) continue; From ef69f126261609feed4276a0c274296d6633ec49 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 7 Mar 2012 13:21:23 -0800 Subject: [PATCH 2397/2556] Staging: android: lowmemorykiller.c Fix compiler warning about the type of the module parameter. Cc: San Mehat Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/lowmemorykiller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 5dbafc85c9c53..f9daca3bcd873 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -45,7 +45,7 @@ static int lowmem_adj[6] = { 12, }; static int lowmem_adj_size = 4; -static size_t lowmem_minfree[6] = { +static int lowmem_minfree[6] = { 3 * 512, /* 6MB */ 2 * 1024, /* 8MB */ 4 * 1024, /* 16MB */ From 277e1def7c64b8495d53e9675349404a3319d2a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 8 Mar 2012 15:43:36 -0800 Subject: [PATCH 2398/2556] Staging: android: binder: Fix use-after-free bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit binder_update_page_range could read freed memory if the vma of the selected process was freed right before the check that the vma belongs to the mm struct it just locked. If the vm_mm pointer in that freed vma struct had also been rewritten with a value that matched the locked mm struct, then the code would proceed and possibly modify the freed vma. Signed-off-by: Arve HjønnevÃ¥g Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/binder.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 87d15f16b3c23..39e7aa5bb9756 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -289,6 +289,7 @@ struct binder_proc { struct rb_root refs_by_node; int pid; struct vm_area_struct *vma; + struct mm_struct *vma_vm_mm; struct task_struct *tsk; struct files_struct *files; struct hlist_node deferred_work_node; @@ -634,7 +635,7 @@ static int binder_update_page_range(struct binder_proc *proc, int allocate, if (mm) { down_write(&mm->mmap_sem); vma = proc->vma; - if (vma && mm != vma->vm_mm) { + if (vma && mm != proc->vma_vm_mm) { pr_err("binder: %d: vma mm and task mm mismatch\n", proc->pid); vma = NULL; @@ -2838,6 +2839,7 @@ static void binder_vma_close(struct vm_area_struct *vma) (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, (unsigned long)pgprot_val(vma->vm_page_prot)); proc->vma = NULL; + proc->vma_vm_mm = NULL; binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES); } @@ -2922,6 +2924,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) barrier(); proc->files = get_files_struct(proc->tsk); proc->vma = vma; + proc->vma_vm_mm = vma->vm_mm; /*binder_debug(BINDER_DEBUG_TOP_ERRORS, "binder_mmap: %d %lx-%lx maps %p\n", From 147e1d478e1cd0c7a692711d8460a9257b13ecc6 Mon Sep 17 00:00:00 2001 From: faux123 Date: Sat, 25 Feb 2012 12:35:48 -0800 Subject: [PATCH 2399/2556] sched: disable GENTLTE_FAIR_SLEEPERS for better performance on Android --- kernel/sched_features.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched_features.h b/kernel/sched_features.h index 68e69acc29b95..1b6685acadb6a 100644 --- a/kernel/sched_features.h +++ b/kernel/sched_features.h @@ -3,7 +3,7 @@ * them to run sooner, but does not allow tons of sleepers to * rip the spread apart. */ -SCHED_FEAT(GENTLE_FAIR_SLEEPERS, 1) +SCHED_FEAT(GENTLE_FAIR_SLEEPERS, 0) /* * Place new tasks ahead so that they do not starve already running From d2efa9bda0ad5c7fc8bf39780b73fee294892396 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 19 Apr 2012 23:52:50 -0400 Subject: [PATCH 2400/2556] switch do_fsync() to fget_light() Signed-off-by: Al Viro --- fs/sync.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/sync.c b/fs/sync.c index 412dc89163d31..fb623f3b9fcba 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -184,11 +184,12 @@ static int do_fsync(unsigned int fd, int datasync) { struct file *file; int ret = -EBADF; + int fput_needed; - file = fget(fd); + file = fget_light(fd, &fput_needed); if (file) { ret = vfs_fsync(file, datasync); - fput(file); + fput_light(file, fput_needed); } return ret; } From 47cd9b2f25328afdd190eaf92412ab79d8ac1766 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 15 May 2012 14:15:25 +0100 Subject: [PATCH 2401/2556] ARM: rwlocks: remove unused branch labels from trylock routines The ARM arch_{read,write}_trylock implementations include unused backwards branch labels, since we don't retry the locking operation if the exclusive store fails. This patch removes the labels. Signed-off-by: Will Deacon --- arch/arm/include/asm/spinlock.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/include/asm/spinlock.h b/arch/arm/include/asm/spinlock.h index db41b137c760a..38a9ce4bf7473 100644 --- a/arch/arm/include/asm/spinlock.h +++ b/arch/arm/include/asm/spinlock.h @@ -262,7 +262,7 @@ static inline int arch_write_trylock(arch_rwlock_t *rw) unsigned long tmp; __asm__ __volatile__( -"1: ldrex %0, [%1]\n" +" ldrex %0, [%1]\n" " teq %0, #0\n" " strexeq %0, %2, [%1]" : "=&r" (tmp) @@ -350,7 +350,7 @@ static inline int arch_read_trylock(arch_rwlock_t *rw) unsigned long tmp, tmp2 = 1; __asm__ __volatile__( -"1: ldrex %0, [%2]\n" +" ldrex %0, [%2]\n" " adds %0, %0, #1\n" " strexpl %1, %0, [%2]\n" : "=&r" (tmp), "+r" (tmp2) From e273f9cca647b7ef6c28d2d91603b78c92b1de02 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 28 Aug 2012 17:59:16 -0500 Subject: [PATCH 2402/2556] configs: evervolv: enable cgroup freezer and perf events --- arch/arm/configs/evervolv_bravo_defconfig | 10 +++++++--- arch/arm/configs/evervolv_incrediblec_defconfig | 10 +++++++--- arch/arm/configs/evervolv_mahimahi_defconfig | 12 +++++++++--- arch/arm/configs/evervolv_supersonic_defconfig | 10 +++++++--- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index 58e7dd24350e1..37d01e48a2413 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sun Jul 22 18:41:36 2012 +# Tue Aug 28 17:56:55 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -25,6 +25,7 @@ CONFIG_VECTORS_BASE=0xffff0000 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" CONFIG_CONSTRUCTORS=y CONFIG_HAVE_IRQ_WORK=y +CONFIG_IRQ_WORK=y # # General setup @@ -80,7 +81,7 @@ CONFIG_LOG_BUF_SHIFT=17 CONFIG_CGROUPS=y # CONFIG_CGROUP_DEBUG is not set # CONFIG_CGROUP_NS is not set -# CONFIG_CGROUP_FREEZER is not set +CONFIG_CGROUP_FREEZER=y # CONFIG_CGROUP_DEVICE is not set # CONFIG_CPUSETS is not set CONFIG_CGROUP_CPUACCT=y @@ -130,8 +131,9 @@ CONFIG_PERF_USE_VMALLOC=y # # Kernel Performance Events And Counters # -# CONFIG_PERF_EVENTS is not set +CONFIG_PERF_EVENTS=y # CONFIG_PERF_COUNTERS is not set +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set CONFIG_VM_EVENT_COUNTERS=y # CONFIG_SLUB_DEBUG is not set CONFIG_COMPAT_BRK=y @@ -147,6 +149,7 @@ CONFIG_HAVE_KRETPROBES=y CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y CONFIG_HAVE_CLK=y CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_HW_BREAKPOINT=y # # GCOV-based kernel profiling @@ -428,6 +431,7 @@ CONFIG_AEABI=y # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set # CONFIG_HIGHMEM is not set +CONFIG_HW_PERF_EVENTS=y CONFIG_VMALLOC_RESERVE=0x08000000 CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index 63cba6ad1eeb8..7e861bac56164 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sun Jul 22 18:42:04 2012 +# Tue Aug 28 17:58:19 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -25,6 +25,7 @@ CONFIG_VECTORS_BASE=0xffff0000 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" CONFIG_CONSTRUCTORS=y CONFIG_HAVE_IRQ_WORK=y +CONFIG_IRQ_WORK=y # # General setup @@ -80,7 +81,7 @@ CONFIG_LOG_BUF_SHIFT=17 CONFIG_CGROUPS=y # CONFIG_CGROUP_DEBUG is not set # CONFIG_CGROUP_NS is not set -# CONFIG_CGROUP_FREEZER is not set +CONFIG_CGROUP_FREEZER=y # CONFIG_CGROUP_DEVICE is not set # CONFIG_CPUSETS is not set CONFIG_CGROUP_CPUACCT=y @@ -128,8 +129,9 @@ CONFIG_PERF_USE_VMALLOC=y # # Kernel Performance Events And Counters # -# CONFIG_PERF_EVENTS is not set +CONFIG_PERF_EVENTS=y # CONFIG_PERF_COUNTERS is not set +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set CONFIG_VM_EVENT_COUNTERS=y CONFIG_COMPAT_BRK=y CONFIG_SLAB=y @@ -144,6 +146,7 @@ CONFIG_HAVE_KRETPROBES=y CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y CONFIG_HAVE_CLK=y CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_HW_BREAKPOINT=y # # GCOV-based kernel profiling @@ -429,6 +432,7 @@ CONFIG_AEABI=y # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set # CONFIG_HIGHMEM is not set +CONFIG_HW_PERF_EVENTS=y CONFIG_VMALLOC_RESERVE=0x08000000 CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index ef2cb687a6302..afe38f6a4c643 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sun Jul 22 18:40:57 2012 +# Tue Aug 28 17:54:50 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -25,6 +25,7 @@ CONFIG_VECTORS_BASE=0xffff0000 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" CONFIG_CONSTRUCTORS=y CONFIG_HAVE_IRQ_WORK=y +CONFIG_IRQ_WORK=y # # General setup @@ -80,7 +81,7 @@ CONFIG_LOG_BUF_SHIFT=17 CONFIG_CGROUPS=y # CONFIG_CGROUP_DEBUG is not set # CONFIG_CGROUP_NS is not set -# CONFIG_CGROUP_FREEZER is not set +CONFIG_CGROUP_FREEZER=y # CONFIG_CGROUP_DEVICE is not set # CONFIG_CPUSETS is not set CONFIG_CGROUP_CPUACCT=y @@ -130,8 +131,9 @@ CONFIG_PERF_USE_VMALLOC=y # # Kernel Performance Events And Counters # -# CONFIG_PERF_EVENTS is not set +CONFIG_PERF_EVENTS=y # CONFIG_PERF_COUNTERS is not set +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set CONFIG_VM_EVENT_COUNTERS=y # CONFIG_SLUB_DEBUG is not set CONFIG_COMPAT_BRK=y @@ -147,6 +149,7 @@ CONFIG_HAVE_KRETPROBES=y CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y CONFIG_HAVE_CLK=y CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_HW_BREAKPOINT=y # # GCOV-based kernel profiling @@ -428,6 +431,7 @@ CONFIG_AEABI=y # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set # CONFIG_HIGHMEM is not set +CONFIG_HW_PERF_EVENTS=y CONFIG_VMALLOC_RESERVE=0x08000000 CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y @@ -546,6 +550,8 @@ CONFIG_NET_KEY=y CONFIG_INET=y # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_ASK_IP_FIB_HASH is not set +# CONFIG_IP_FIB_TRIE is not set CONFIG_IP_FIB_HASH=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index bcfc45e6945c6..87b546c3956cb 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Sun Jul 22 18:42:32 2012 +# Tue Aug 28 17:57:49 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -25,6 +25,7 @@ CONFIG_VECTORS_BASE=0xffff0000 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" CONFIG_CONSTRUCTORS=y CONFIG_HAVE_IRQ_WORK=y +CONFIG_IRQ_WORK=y # # General setup @@ -81,7 +82,7 @@ CONFIG_LOG_BUF_SHIFT=17 CONFIG_CGROUPS=y # CONFIG_CGROUP_DEBUG is not set # CONFIG_CGROUP_NS is not set -# CONFIG_CGROUP_FREEZER is not set +CONFIG_CGROUP_FREEZER=y # CONFIG_CGROUP_DEVICE is not set # CONFIG_CPUSETS is not set CONFIG_CGROUP_CPUACCT=y @@ -137,8 +138,9 @@ CONFIG_PERF_USE_VMALLOC=y # # Kernel Performance Events And Counters # -# CONFIG_PERF_EVENTS is not set +CONFIG_PERF_EVENTS=y # CONFIG_PERF_COUNTERS is not set +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set CONFIG_VM_EVENT_COUNTERS=y CONFIG_COMPAT_BRK=y CONFIG_SLAB=y @@ -153,6 +155,7 @@ CONFIG_HAVE_KRETPROBES=y CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y CONFIG_HAVE_CLK=y CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_HW_BREAKPOINT=y # # GCOV-based kernel profiling @@ -434,6 +437,7 @@ CONFIG_AEABI=y # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set # CONFIG_HIGHMEM is not set +CONFIG_HW_PERF_EVENTS=y CONFIG_VMALLOC_RESERVE=0x08000000 CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y From cdcf3f82d6330471ea048e9334145bc7bafccc86 Mon Sep 17 00:00:00 2001 From: faux123 Date: Tue, 28 Aug 2012 19:06:59 -0500 Subject: [PATCH 2403/2556] block/deadline: tweaked for better performance on android --- block/deadline-iosched.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block/deadline-iosched.c b/block/deadline-iosched.c index b2184bf749949..05d1872b74a1a 100644 --- a/block/deadline-iosched.c +++ b/block/deadline-iosched.c @@ -17,9 +17,9 @@ /* * See Documentation/block/deadline-iosched.txt */ -static const int read_expire = HZ / 2; /* max time before a read is submitted. */ +static const int read_expire = HZ / 4; /* max time before a read is submitted. */ static const int write_expire = 5 * HZ; /* ditto for writes, these limits are SOFT! */ -static const int writes_starved = 2; /* max times reads can starve a write */ +static const int writes_starved = 4; /* max times reads can starve a write */ static const int fifo_batch = 1; /* # of sequential requests treated as one by the above parameters. For throughput. */ From 2b1591a8f0fd8a85253470d7b5441fd00f23d4a9 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 28 Aug 2012 19:25:04 -0500 Subject: [PATCH 2404/2556] Revert "HACK: block fbearlysuspend to not break androids crt-off animation" This reverts commit b877d2ac7a0bb16ab09e12786d38e1bc3def583f. not working. panel blanks too early --- kernel/power/fbearlysuspend.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/kernel/power/fbearlysuspend.c b/kernel/power/fbearlysuspend.c index 0de398e966224..83104fe471038 100644 --- a/kernel/power/fbearlysuspend.c +++ b/kernel/power/fbearlysuspend.c @@ -13,7 +13,6 @@ * */ -#include #include #include #include @@ -34,10 +33,6 @@ static void stop_drawing_early_suspend(struct early_suspend *h) int ret; unsigned long irq_flags; - /* FIXME: earlysuspend breaks androids CRT-off animation - * Sleep a little bit to get it played properly */ - msleep(500); - spin_lock_irqsave(&fb_state_lock, irq_flags); fb_state = FB_STATE_REQUEST_STOP_DRAWING; spin_unlock_irqrestore(&fb_state_lock, irq_flags); From 9f1253577230eb23b75b149d35c27c9e86b0161e Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Mon, 17 Jan 2011 00:09:38 +0100 Subject: [PATCH 2405/2556] =?UTF-8?q?Kill=20off=20warning:=20=E2=80=98inli?= =?UTF-8?q?ne=E2=80=99=20is=20not=20at=20beginning=20of=20declaration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a bunch of warning: ‘inline’ is not at beginning of declaration messages when building a 'make allyesconfig' kernel with -Wextra. These warnings are trivial to kill, yet rather annoying when building with -Wextra. The more we can cut down on pointless crap like this the better (IMHO). A previous patch to do this for a 'allnoconfig' build has already been merged. This just takes the cleanup a little further. Signed-off-by: Jesper Juhl Signed-off-by: Jiri Kosina --- arch/x86/oprofile/op_model_p4.c | 2 +- drivers/bluetooth/btusb.c | 4 ++-- drivers/cpuidle/sysfs.c | 2 +- drivers/edac/i7300_edac.c | 2 +- fs/ocfs2/dir.c | 2 +- kernel/trace/ring_buffer.c | 2 +- net/ipv6/inet6_hashtables.c | 2 +- net/mac80211/tx.c | 2 +- sound/pci/au88x0/au88x0.h | 4 ++-- sound/pci/au88x0/au88x0_core.c | 4 ++-- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/arch/x86/oprofile/op_model_p4.c b/arch/x86/oprofile/op_model_p4.c index 9fadec074142b..98ab13058f892 100644 --- a/arch/x86/oprofile/op_model_p4.c +++ b/arch/x86/oprofile/op_model_p4.c @@ -50,7 +50,7 @@ static inline void setup_num_counters(void) #endif } -static int inline addr_increment(void) +static inline int addr_increment(void) { #ifdef CONFIG_SMP return smp_num_siblings == 2 ? 2 : 1; diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index f44ca40a28faa..8fc368e8ef1e6 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -433,7 +433,7 @@ static void btusb_isoc_complete(struct urb *urb) } } -static void inline __fill_isoc_descriptor(struct urb *urb, int len, int mtu) +static inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu) { int i, offset = 0; @@ -784,7 +784,7 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt) } } -static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting) +static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting) { struct btusb_data *data = hdev->driver_data; struct usb_interface *intf = data->isoc; diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index 0310ffaec9df0..be7917ec40c9f 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c @@ -300,7 +300,7 @@ static struct kobj_type ktype_state_cpuidle = { .release = cpuidle_state_sysfs_release, }; -static void inline cpuidle_free_state_kobj(struct cpuidle_device *device, int i) +static inline void cpuidle_free_state_kobj(struct cpuidle_device *device, int i) { kobject_put(&device->kobjs[i]->kobj); wait_for_completion(&device->kobjs[i]->kobj_unregister); diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c index 05523b504271f..76d1f576cdc87 100644 --- a/drivers/edac/i7300_edac.c +++ b/drivers/edac/i7300_edac.c @@ -162,7 +162,7 @@ static struct edac_pci_ctl_info *i7300_pci; #define AMBPRESENT_0 0x64 #define AMBPRESENT_1 0x66 -const static u16 mtr_regs[MAX_SLOTS] = { +static const u16 mtr_regs[MAX_SLOTS] = { 0x80, 0x84, 0x88, 0x8c, 0x82, 0x86, 0x8a, 0x8e }; diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index d417b3f9b0c73..f97b6f1c61dd4 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -354,7 +354,7 @@ static inline int ocfs2_match(int len, /* * Returns 0 if not found, -1 on failure, and 1 on success */ -static int inline ocfs2_search_dirblock(struct buffer_head *bh, +static inline int ocfs2_search_dirblock(struct buffer_head *bh, struct inode *dir, const char *name, int namelen, unsigned long offset, diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index bd1c35a4fbccf..6ee56b4ad1363 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -669,7 +669,7 @@ static struct list_head *rb_list_head(struct list_head *list) * the reader page). But if the next page is a header page, * its flags will be non zero. */ -static int inline +static inline int rb_is_head_page(struct ring_buffer_per_cpu *cpu_buffer, struct buffer_page *page, struct list_head *list) { diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c index 633a6c266136e..b53197233709c 100644 --- a/net/ipv6/inet6_hashtables.c +++ b/net/ipv6/inet6_hashtables.c @@ -124,7 +124,7 @@ struct sock *__inet6_lookup_established(struct net *net, } EXPORT_SYMBOL(__inet6_lookup_established); -static int inline compute_score(struct sock *sk, struct net *net, +static inline int compute_score(struct sock *sk, struct net *net, const unsigned short hnum, const struct in6_addr *daddr, const int dif) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index b0beaa58246bb..306533ba0d3df 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -173,7 +173,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr, return cpu_to_le16(dur); } -static int inline is_ieee80211_device(struct ieee80211_local *local, +static inline int is_ieee80211_device(struct ieee80211_local *local, struct net_device *dev) { return local == wdev_priv(dev->ieee80211_ptr); diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h index cf46bba563cf9..ecb8f4daf408f 100644 --- a/sound/pci/au88x0/au88x0.h +++ b/sound/pci/au88x0/au88x0.h @@ -211,7 +211,7 @@ static void vortex_adbdma_startfifo(vortex_t * vortex, int adbdma); //static void vortex_adbdma_stopfifo(vortex_t *vortex, int adbdma); static void vortex_adbdma_pausefifo(vortex_t * vortex, int adbdma); static void vortex_adbdma_resumefifo(vortex_t * vortex, int adbdma); -static int inline vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma); +static inline int vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma); static void vortex_adbdma_resetup(vortex_t *vortex, int adbdma); #ifndef CHIP_AU8810 @@ -219,7 +219,7 @@ static void vortex_wtdma_startfifo(vortex_t * vortex, int wtdma); static void vortex_wtdma_stopfifo(vortex_t * vortex, int wtdma); static void vortex_wtdma_pausefifo(vortex_t * vortex, int wtdma); static void vortex_wtdma_resumefifo(vortex_t * vortex, int wtdma); -static int inline vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma); +static inline int vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma); #endif /* global stuff. */ diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index 16c0bdfbb1648..489150380eac0 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c @@ -1249,7 +1249,7 @@ static void vortex_adbdma_resetup(vortex_t *vortex, int adbdma) { } } -static int inline vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma) +static inline int vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma) { stream_t *dma = &vortex->dma_adb[adbdma]; int temp, page, delta; @@ -1506,7 +1506,7 @@ static int vortex_wtdma_getcursubuffer(vortex_t * vortex, int wtdma) POS_SHIFT) & POS_MASK); } #endif -static int inline vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma) +static inline int vortex_wtdma_getlinearpos(vortex_t * vortex, int wtdma) { stream_t *dma = &vortex->dma_wt[wtdma]; int temp; From b71ead3349787b8b732127b791720858059969a1 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 5 Sep 2012 18:36:21 -0500 Subject: [PATCH 2406/2556] configs: evervolv: disable perf events possibly causing boot issues --- arch/arm/configs/evervolv_bravo_defconfig | 8 ++------ arch/arm/configs/evervolv_incrediblec_defconfig | 8 ++------ arch/arm/configs/evervolv_mahimahi_defconfig | 10 ++-------- arch/arm/configs/evervolv_supersonic_defconfig | 8 ++------ 4 files changed, 8 insertions(+), 26 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index 37d01e48a2413..910570fe0c2a7 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Tue Aug 28 17:56:55 2012 +# Wed Sep 5 18:34:31 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -25,7 +25,6 @@ CONFIG_VECTORS_BASE=0xffff0000 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" CONFIG_CONSTRUCTORS=y CONFIG_HAVE_IRQ_WORK=y -CONFIG_IRQ_WORK=y # # General setup @@ -131,9 +130,8 @@ CONFIG_PERF_USE_VMALLOC=y # # Kernel Performance Events And Counters # -CONFIG_PERF_EVENTS=y +# CONFIG_PERF_EVENTS is not set # CONFIG_PERF_COUNTERS is not set -# CONFIG_DEBUG_PERF_USE_VMALLOC is not set CONFIG_VM_EVENT_COUNTERS=y # CONFIG_SLUB_DEBUG is not set CONFIG_COMPAT_BRK=y @@ -149,7 +147,6 @@ CONFIG_HAVE_KRETPROBES=y CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y CONFIG_HAVE_CLK=y CONFIG_HAVE_DMA_API_DEBUG=y -CONFIG_HAVE_HW_BREAKPOINT=y # # GCOV-based kernel profiling @@ -431,7 +428,6 @@ CONFIG_AEABI=y # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set # CONFIG_HIGHMEM is not set -CONFIG_HW_PERF_EVENTS=y CONFIG_VMALLOC_RESERVE=0x08000000 CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index 7e861bac56164..da2d73ddc562b 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Tue Aug 28 17:58:19 2012 +# Wed Sep 5 18:35:00 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -25,7 +25,6 @@ CONFIG_VECTORS_BASE=0xffff0000 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" CONFIG_CONSTRUCTORS=y CONFIG_HAVE_IRQ_WORK=y -CONFIG_IRQ_WORK=y # # General setup @@ -129,9 +128,8 @@ CONFIG_PERF_USE_VMALLOC=y # # Kernel Performance Events And Counters # -CONFIG_PERF_EVENTS=y +# CONFIG_PERF_EVENTS is not set # CONFIG_PERF_COUNTERS is not set -# CONFIG_DEBUG_PERF_USE_VMALLOC is not set CONFIG_VM_EVENT_COUNTERS=y CONFIG_COMPAT_BRK=y CONFIG_SLAB=y @@ -146,7 +144,6 @@ CONFIG_HAVE_KRETPROBES=y CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y CONFIG_HAVE_CLK=y CONFIG_HAVE_DMA_API_DEBUG=y -CONFIG_HAVE_HW_BREAKPOINT=y # # GCOV-based kernel profiling @@ -432,7 +429,6 @@ CONFIG_AEABI=y # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set # CONFIG_HIGHMEM is not set -CONFIG_HW_PERF_EVENTS=y CONFIG_VMALLOC_RESERVE=0x08000000 CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index afe38f6a4c643..6f9eae0cc69fa 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Tue Aug 28 17:54:50 2012 +# Wed Sep 5 18:33:37 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -25,7 +25,6 @@ CONFIG_VECTORS_BASE=0xffff0000 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" CONFIG_CONSTRUCTORS=y CONFIG_HAVE_IRQ_WORK=y -CONFIG_IRQ_WORK=y # # General setup @@ -131,9 +130,8 @@ CONFIG_PERF_USE_VMALLOC=y # # Kernel Performance Events And Counters # -CONFIG_PERF_EVENTS=y +# CONFIG_PERF_EVENTS is not set # CONFIG_PERF_COUNTERS is not set -# CONFIG_DEBUG_PERF_USE_VMALLOC is not set CONFIG_VM_EVENT_COUNTERS=y # CONFIG_SLUB_DEBUG is not set CONFIG_COMPAT_BRK=y @@ -149,7 +147,6 @@ CONFIG_HAVE_KRETPROBES=y CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y CONFIG_HAVE_CLK=y CONFIG_HAVE_DMA_API_DEBUG=y -CONFIG_HAVE_HW_BREAKPOINT=y # # GCOV-based kernel profiling @@ -431,7 +428,6 @@ CONFIG_AEABI=y # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set # CONFIG_HIGHMEM is not set -CONFIG_HW_PERF_EVENTS=y CONFIG_VMALLOC_RESERVE=0x08000000 CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y @@ -550,8 +546,6 @@ CONFIG_NET_KEY=y CONFIG_INET=y # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set -# CONFIG_ASK_IP_FIB_HASH is not set -# CONFIG_IP_FIB_TRIE is not set CONFIG_IP_FIB_HASH=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index 87b546c3956cb..53349c16ab4ce 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Tue Aug 28 17:57:49 2012 +# Wed Sep 5 18:35:30 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -25,7 +25,6 @@ CONFIG_VECTORS_BASE=0xffff0000 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" CONFIG_CONSTRUCTORS=y CONFIG_HAVE_IRQ_WORK=y -CONFIG_IRQ_WORK=y # # General setup @@ -138,9 +137,8 @@ CONFIG_PERF_USE_VMALLOC=y # # Kernel Performance Events And Counters # -CONFIG_PERF_EVENTS=y +# CONFIG_PERF_EVENTS is not set # CONFIG_PERF_COUNTERS is not set -# CONFIG_DEBUG_PERF_USE_VMALLOC is not set CONFIG_VM_EVENT_COUNTERS=y CONFIG_COMPAT_BRK=y CONFIG_SLAB=y @@ -155,7 +153,6 @@ CONFIG_HAVE_KRETPROBES=y CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y CONFIG_HAVE_CLK=y CONFIG_HAVE_DMA_API_DEBUG=y -CONFIG_HAVE_HW_BREAKPOINT=y # # GCOV-based kernel profiling @@ -437,7 +434,6 @@ CONFIG_AEABI=y # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set # CONFIG_HIGHMEM is not set -CONFIG_HW_PERF_EVENTS=y CONFIG_VMALLOC_RESERVE=0x08000000 CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y From ac17b61d6c208da6f9ade18a97375030bd158a17 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 5 Sep 2012 18:37:48 -0500 Subject: [PATCH 2407/2556] Revert "switch do_fsync() to fget_light()" This reverts commit d2efa9bda0ad5c7fc8bf39780b73fee294892396. --- fs/sync.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/sync.c b/fs/sync.c index fb623f3b9fcba..412dc89163d31 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -184,12 +184,11 @@ static int do_fsync(unsigned int fd, int datasync) { struct file *file; int ret = -EBADF; - int fput_needed; - file = fget_light(fd, &fput_needed); + file = fget(fd); if (file) { ret = vfs_fsync(file, datasync); - fput_light(file, fput_needed); + fput(file); } return ret; } From fb9a0bd5b2a1ca1c408ed19be6b6b9b9a19f5b98 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Fri, 7 Sep 2012 19:51:12 -0500 Subject: [PATCH 2408/2556] arm: enable -pipe --- arch/arm/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 5c969699e7dad..36d67cc177da0 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -17,7 +17,7 @@ endif OBJCOPYFLAGS :=-O binary -R .comment -S GZFLAGS :=-9 -#KBUILD_CFLAGS +=-pipe +KBUILD_CFLAGS +=-pipe # Explicitly specifiy 32-bit ARM ISA since toolchain default can be -mthumb: KBUILD_CFLAGS +=$(call cc-option,-marm,) From e0c3fd5603cd470b91f74eb84d0465e7d14d2886 Mon Sep 17 00:00:00 2001 From: franciscofranco Date: Mon, 19 Dec 2011 02:19:42 +0000 Subject: [PATCH 2409/2556] Remove '+' symbol from kernel version string --- scripts/setlocalversion | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/setlocalversion b/scripts/setlocalversion index ef8729f48586d..c421b69729176 100755 --- a/scripts/setlocalversion +++ b/scripts/setlocalversion @@ -166,7 +166,6 @@ else # LOCALVERSION= is not specified if test "${LOCALVERSION+set}" != "set"; then scm=$(scm_version --short) - res="$res${scm:++}" fi fi From 3152c8c72471f572aa5caffa6a33df607a3450c5 Mon Sep 17 00:00:00 2001 From: "andrew.boren" Date: Thu, 4 Oct 2012 22:33:18 -0700 Subject: [PATCH 2410/2556] qsd8k: Update supersonic current version. Change-Id: I36df66d3bd9b9b7325d0aca1545d8c4b4dee4a20 --- arch/arm/configs/evervolv_supersonic_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index 53349c16ab4ce..cc2fc897aaab0 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -34,7 +34,7 @@ CONFIG_BROKEN_ON_SMP=y CONFIG_LOCK_KERNEL=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" -CONFIG_LOCALVERSION="-evervolv-acies-ics" +CONFIG_LOCALVERSION="-evervolv-acies-jellybean" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y From 0b9b98bc595b0419ce9ba6ded7355bb9071b710e Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Wed, 29 Feb 2012 17:54:41 +0900 Subject: [PATCH 2411/2556] [CPUFREQ] CPUfreq ondemand: update sampling rate without waiting for next sampling When a new sampling rate is shorter than the current one, (e.g., 1 sec --> 10 ms) regardless how short the new one is, the current ondemand mechanism wait for the previously set timer to be expired. For example, if the user has just expressed that the sampling rate should be 10 ms from now and the previous was 1000 ms, the new rate may become effective 999 ms later, which could be not acceptable for the user if the user has intended to speed up sampling because the system is expected to react to CPU load fluctuation quickly from __now__. In order to address this issue, we need to cancel the previously set timer (schedule_delayed_work) and reset the timer if resetting timer is expected to trigger the delayed_work ealier. Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park Signed-off-by: Dave Jones --- drivers/cpufreq/cpufreq_ondemand.c | 58 +++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 81e3114237432..cfb791cab11d8 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -265,6 +265,62 @@ show_one(sampling_down_factor, sampling_down_factor); show_one(ignore_nice_load, ignore_nice); show_one(powersave_bias, powersave_bias); +/** + * update_sampling_rate - update sampling rate effective immediately if needed. + * @new_rate: new sampling rate + * + * If new rate is smaller than the old, simply updaing + * dbs_tuners_int.sampling_rate might not be appropriate. For example, + * if the original sampling_rate was 1 second and the requested new sampling + * rate is 10 ms because the user needs immediate reaction from ondemand + * governor, but not sure if higher frequency will be required or not, + * then, the governor may change the sampling rate too late; up to 1 second + * later. Thus, if we are reducing the sampling rate, we need to make the + * new value effective immediately. + */ +static void update_sampling_rate(unsigned int new_rate) +{ + int cpu; + + dbs_tuners_ins.sampling_rate = new_rate + = max(new_rate, min_sampling_rate); + + for_each_online_cpu(cpu) { + struct cpufreq_policy *policy; + struct cpu_dbs_info_s *dbs_info; + unsigned long next_sampling, appointed_at; + + policy = cpufreq_cpu_get(cpu); + if (!policy) + continue; + dbs_info = &per_cpu(od_cpu_dbs_info, policy->cpu); + cpufreq_cpu_put(policy); + + mutex_lock(&dbs_info->timer_mutex); + + if (!delayed_work_pending(&dbs_info->work)) { + mutex_unlock(&dbs_info->timer_mutex); + continue; + } + + next_sampling = jiffies + usecs_to_jiffies(new_rate); + appointed_at = dbs_info->work.timer.expires; + + + if (time_before(next_sampling, appointed_at)) { + + mutex_unlock(&dbs_info->timer_mutex); + cancel_delayed_work_sync(&dbs_info->work); + mutex_lock(&dbs_info->timer_mutex); + + schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work, + usecs_to_jiffies(new_rate)); + + } + mutex_unlock(&dbs_info->timer_mutex); + } +} + static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, const char *buf, size_t count) { @@ -273,7 +329,7 @@ static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, ret = sscanf(buf, "%u", &input); if (ret != 1) return -EINVAL; - dbs_tuners_ins.sampling_rate = max(input, min_sampling_rate); + update_sampling_rate(input); return count; } From 7c5b02b54f31394958f48518b658c85929855a8d Mon Sep 17 00:00:00 2001 From: Praveen Chidambaram Date: Thu, 19 Jul 2012 10:45:07 -0600 Subject: [PATCH 2412/2556] cpufreq: Fix panic when setting sampling rate Kernel panics when trying to set the sampling rate before the ondemand governor CPUFREQ_GOV_START notification is received. [] panic+0x15c [] die+0x19c [] __do_kernel_fault+0x64 [] do_page_fault+0x3b8 [] do_DataAbort+0x134 [] __dabt_svc+0x38 [] __list_add+0x38 [] __mutex_lock_slowpath+0x128 [] mutex_lock+0x20 [] store_sampling_rate+0x98 [] kobj_attr_store+0x18 [] sysfs_write_file+0x108 [] vfs_write+0xac [] sys_write+0x3c [] ret_fast_syscall+0x0 Initialize the mutex as part of cpufreq_gov_dbs_init(). Change-Id: Ie9014407d724d7a0a81c076be47df5d08f513407 Signed-off-by: Praveen Chidambaram Conflicts: drivers/cpufreq/cpufreq_ondemand.c --- drivers/cpufreq/cpufreq_ondemand.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index cfb791cab11d8..a4b33dbe1a9be 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -862,7 +862,6 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, rc = input_register_handler(&dbs_input_handler); mutex_unlock(&dbs_mutex); - mutex_init(&this_dbs_info->timer_mutex); dbs_timer_init(this_dbs_info); break; @@ -929,6 +928,9 @@ static int __init cpufreq_gov_dbs_init(void) return -EFAULT; } for_each_possible_cpu(i) { + struct cpu_dbs_info_s *this_dbs_info = + &per_cpu(od_cpu_dbs_info, i); + mutex_init(&this_dbs_info->timer_mutex); INIT_WORK(&per_cpu(dbs_refresh_work, i), dbs_refresh_callback); } From 1875354de3328fa772ce40266914eed5aa90a0ae Mon Sep 17 00:00:00 2001 From: Narayanan Gopalakrishnan Date: Tue, 26 Jun 2012 14:28:32 -0700 Subject: [PATCH 2413/2556] cpufreq: fix utilization statistics for sync cpus If the cpus are using related_cpus policy, then the current implementaion will overwrite the the calculated load for every iteration in the for loop. Hence, mpdecision will read incorrect value, if the cores are using related cpus policy. With this change mpdecision will read the sum of the loads of all cpu's, if related cpu policy is used. Change-Id: I242c8fe39fd75dc3e48750a5b78a5d218b536ddd Signed-off-by: Narayanan Gopalakrishnan Signed-off-by: Shruthi Krishna --- drivers/cpufreq/cpufreq_ondemand.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index a4b33dbe1a9be..0e48cd45447f3 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -579,9 +579,11 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) load_freq = cur_load * freq_avg; if (load_freq > max_load_freq) max_load_freq = load_freq; + + /* calculate the scaled load across CPU */ + load_at_max_freq += (cur_load * policy->cur) / + policy->cpuinfo.max_freq; } - /* calculate the scaled load across CPU */ - load_at_max_freq = (cur_load * policy->cur)/policy->cpuinfo.max_freq; cpufreq_notify_utilization(policy, load_at_max_freq); From bfab3d7f538614dad760866503e394fe2cd7feb5 Mon Sep 17 00:00:00 2001 From: Narayanan Gopalakrishnan Date: Tue, 14 Aug 2012 14:40:51 -0700 Subject: [PATCH 2414/2556] cpufreq: Add a boostpulse interface to ondemand For applications that require a one-shot increase in frequency, the boostpulse interface would help to scale to max for the time specified in boostime. This helps applications that currently increases scaling_min_freq and resets later after a fixed duration. Instead of changing scaling_min_freq applications can directly write to boostpulse and governer resets during regular timer after the time expires. Change-Id: Ie5f490ff06b41364aa76dd468573433495da28a4 Signed-off-by: Narayanan Gopalakrishnan Conflicts: drivers/cpufreq/cpufreq_ondemand.c --- drivers/cpufreq/cpufreq_ondemand.c | 77 +++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 0e48cd45447f3..9d96900512d9f 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -41,6 +41,9 @@ #define MIN_FREQUENCY_UP_THRESHOLD (11) #define MAX_FREQUENCY_UP_THRESHOLD (100) #define MIN_FREQUENCY_DOWN_DIFFERENTIAL (1) +#define DEFAULT_FREQ_BOOST_TIME (2500000) + +u64 freq_boosted_time; /* * The polling frequency of this governor depends on the capability of @@ -119,12 +122,16 @@ static struct dbs_tuners { unsigned int sampling_down_factor; unsigned int powersave_bias; unsigned int io_is_busy; + unsigned int boosted; + unsigned int freq_boost_time; + unsigned int boostfreq; } dbs_tuners_ins = { .up_threshold = DEF_FREQUENCY_UP_THRESHOLD, .sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR, .down_differential = DEF_FREQUENCY_DOWN_DIFFERENTIAL, .ignore_nice = 0, .powersave_bias = 0, + .freq_boost_time = DEFAULT_FREQ_BOOST_TIME, }; static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu, @@ -264,6 +271,9 @@ show_one(down_differential, down_differential); show_one(sampling_down_factor, sampling_down_factor); show_one(ignore_nice_load, ignore_nice); show_one(powersave_bias, powersave_bias); +show_one(boostpulse, boosted); +show_one(boosttime, freq_boost_time); +show_one(boostfreq, boostfreq); /** * update_sampling_rate - update sampling rate effective immediately if needed. @@ -321,6 +331,48 @@ static void update_sampling_rate(unsigned int new_rate) } } +static ssize_t store_boosttime(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + + dbs_tuners_ins.freq_boost_time = input; + return count; +} + + +static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + + dbs_tuners_ins.boosted = 1; + freq_boosted_time = ktime_to_us(ktime_get()); + return count; +} + +static ssize_t store_boostfreq(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + dbs_tuners_ins.boostfreq = input; + return count; +} + static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, const char *buf, size_t count) { @@ -456,6 +508,9 @@ define_one_global_rw(down_differential); define_one_global_rw(sampling_down_factor); define_one_global_rw(ignore_nice_load); define_one_global_rw(powersave_bias); +define_one_global_rw(boostpulse); +define_one_global_rw(boosttime); +define_one_global_rw(boostfreq); static struct attribute *dbs_attributes[] = { &sampling_rate_min.attr, @@ -466,6 +521,9 @@ static struct attribute *dbs_attributes[] = { &ignore_nice_load.attr, &powersave_bias.attr, &io_is_busy.attr, + &boostpulse.attr, + &boosttime.attr, + &boostfreq.attr, NULL }; @@ -500,7 +558,13 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) this_dbs_info->freq_lo = 0; policy = this_dbs_info->cur_policy; - + /* Only core0 controls the boost */ + if (dbs_tuners_ins.boosted && policy->cpu == 0) { + if (ktime_to_us(ktime_get()) - freq_boosted_time >= + dbs_tuners_ins.freq_boost_time) { + dbs_tuners_ins.boosted = 0; + } + } /* * Every sampling_rate, we check, if current idle time is less * than 20% (default), then we try to increase frequency @@ -597,6 +661,13 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) return; } + /* check for frequency boost */ + if (dbs_tuners_ins.boosted && policy->cur < dbs_tuners_ins.boostfreq) { + dbs_freq_increase(policy, dbs_tuners_ins.boostfreq); + dbs_tuners_ins.boostfreq = policy->cur; + return; + } + /* Check for frequency decrease */ /* if we cannot reduce the frequency anymore, break out early */ if (policy->cur == policy->min) @@ -615,6 +686,10 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) (dbs_tuners_ins.up_threshold - dbs_tuners_ins.down_differential); + if (dbs_tuners_ins.boosted && + freq_next < dbs_tuners_ins.boostfreq) { + freq_next = dbs_tuners_ins.boostfreq; + } /* No longer fully busy, reset rate_mult */ this_dbs_info->rate_mult = 1; From 183d6987ed7d0b83d6a4da3c60279d94e3440f15 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 17 Jul 2012 14:33:57 -0700 Subject: [PATCH 2415/2556] cpufreq: Fix sysfs deadlock with concurrent hotplug/frequency switch Running one program that continuously hotplugs and replugs a cpu concurrently with another program that continuously writes to the scaling_set_speed node eventually deadlocks with: ============================================= [ INFO: possible recursive locking detected ] 3.4.0 #37 Tainted: G W --------------------------------------------- filemonkey/122 is trying to acquire lock: (s_active#13){++++.+}, at: [] sysfs_remove_dir+0x9c/0xb4 but task is already holding lock: (s_active#13){++++.+}, at: [] sysfs_write_file+0xe8/0x140 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(s_active#13); lock(s_active#13); *** DEADLOCK *** May be due to missing lock nesting notation 2 locks held by filemonkey/122: #0: (&buffer->mutex){+.+.+.}, at: [] sysfs_write_file+0x28/0x140 #1: (s_active#13){++++.+}, at: [] sysfs_write_file+0xe8/0x140 stack backtrace: [] (unwind_backtrace+0x0/0x120) from [] (validate_chain+0x6f8/0x1054) [] (validate_chain+0x6f8/0x1054) from [] (__lock_acquire+0x81c/0x8d8) [] (__lock_acquire+0x81c/0x8d8) from [] (lock_acquire+0x18c/0x1e8) [] (lock_acquire+0x18c/0x1e8) from [] (sysfs_addrm_finish+0xd0/0x180) [] (sysfs_addrm_finish+0xd0/0x180) from [] (sysfs_remove_dir+0x9c/0xb4) [] (sysfs_remove_dir+0x9c/0xb4) from [] (kobject_del+0x10/0x38) [] (kobject_del+0x10/0x38) from [] (kobject_release+0xf0/0x194) [] (kobject_release+0xf0/0x194) from [] (cpufreq_cpu_put+0xc/0x24) [] (cpufreq_cpu_put+0xc/0x24) from [] (store+0x6c/0x74) [] (store+0x6c/0x74) from [] (sysfs_write_file+0x10c/0x140) [] (sysfs_write_file+0x10c/0x140) from [] (vfs_write+0xb0/0x128) [] (vfs_write+0xb0/0x128) from [] (sys_write+0x3c/0x68) [] (sys_write+0x3c/0x68) from [] (ret_fast_syscall+0x0/0x3c) This is because store() in cpufreq.c indirectly grabs a kobject with kobject_get() and is the last one to call kobject_put() indirectly via cpufreq_cpu_put(). Fix this deadlock by introducing two new functions, cpufreq_cpu_get_sysfs() and cpufreq_cpu_put_sysfs() which do the same thing as cpufreq_cpu_{get,put}() but don't grab the kobject. CRs-fixed: 366560 Signed-off-by: Stephen Boyd (cherry picked from commit f82ef51be26b44b83d86afa56b3cd9c743023470) Change-Id: Ibde742d55fd4f770c402b8abce31ec266c57007d Signed-off-by: Sridhar Gujje --- drivers/cpufreq/cpufreq.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 473b0ef8d9e0e..49d4eba8bf83c 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -133,7 +133,7 @@ pure_initcall(init_cpufreq_transition_notifier_list); static LIST_HEAD(cpufreq_governor_list); static DEFINE_MUTEX(cpufreq_governor_mutex); -struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) +static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, int sysfs) { struct cpufreq_policy *data; unsigned long flags; @@ -157,7 +157,7 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) if (!data) goto err_out_put_module; - if (!kobject_get(&data->kobj)) + if (!sysfs && !kobject_get(&data->kobj)) goto err_out_put_module; spin_unlock_irqrestore(&cpufreq_driver_lock, flags); @@ -170,16 +170,35 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) err_out: return NULL; } + +struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) +{ + return __cpufreq_cpu_get(cpu, 0); +} EXPORT_SYMBOL_GPL(cpufreq_cpu_get); +static struct cpufreq_policy *cpufreq_cpu_get_sysfs(unsigned int cpu) +{ + return __cpufreq_cpu_get(cpu, 1); +} -void cpufreq_cpu_put(struct cpufreq_policy *data) +static void __cpufreq_cpu_put(struct cpufreq_policy *data, int sysfs) { - kobject_put(&data->kobj); + if (!sysfs) + kobject_put(&data->kobj); module_put(cpufreq_driver->owner); } + +void cpufreq_cpu_put(struct cpufreq_policy *data) +{ + __cpufreq_cpu_put(data, 0); +} EXPORT_SYMBOL_GPL(cpufreq_cpu_put); +static void cpufreq_cpu_put_sysfs(struct cpufreq_policy *data) +{ + __cpufreq_cpu_put(data, 1); +} /********************************************************************* * EXTERNALLY AFFECTING FREQUENCY CHANGES * @@ -635,7 +654,7 @@ static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf) struct cpufreq_policy *policy = to_policy(kobj); struct freq_attr *fattr = to_attr(attr); ssize_t ret = -EINVAL; - policy = cpufreq_cpu_get(policy->cpu); + policy = cpufreq_cpu_get_sysfs(policy->cpu); if (!policy) goto no_policy; @@ -649,7 +668,7 @@ static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf) unlock_policy_rwsem_read(policy->cpu); fail: - cpufreq_cpu_put(policy); + cpufreq_cpu_put_sysfs(policy); no_policy: return ret; } @@ -660,7 +679,7 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr, struct cpufreq_policy *policy = to_policy(kobj); struct freq_attr *fattr = to_attr(attr); ssize_t ret = -EINVAL; - policy = cpufreq_cpu_get(policy->cpu); + policy = cpufreq_cpu_get_sysfs(policy->cpu); if (!policy) goto no_policy; @@ -674,7 +693,7 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr, unlock_policy_rwsem_write(policy->cpu); fail: - cpufreq_cpu_put(policy); + cpufreq_cpu_put_sysfs(policy); no_policy: return ret; } From 40e095ca78d11221ab758f42b823df4fe29a7807 Mon Sep 17 00:00:00 2001 From: Narayanan Gopalakrishnan Date: Thu, 9 Aug 2012 15:26:26 -0700 Subject: [PATCH 2416/2556] cpufreq: notify avg load for short sampling_rate If the sampling rate is less than 50ms reporting the actual load may lead to poor decision making at user space. So, for sampling rates less than 50ms report the average of load across two samples. Change-Id: I0d1cf00801732bee8e4380158d7e892758226594 Signed-off-by: Narayanan Gopalakrishnan --- drivers/cpufreq/cpufreq_ondemand.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 9d96900512d9f..7c678857a25e1 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -58,7 +58,7 @@ u64 freq_boosted_time; #define MIN_SAMPLING_RATE_RATIO (2) static unsigned int min_sampling_rate; - +#define DEFAULT_SAMPLING_RATE (50000) #define LATENCY_MULTIPLIER (1000) #define MIN_LATENCY_MULTIPLIER (100) #define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000) @@ -92,6 +92,7 @@ struct cpu_dbs_info_s { unsigned int freq_lo_jiffies; unsigned int freq_hi_jiffies; unsigned int rate_mult; + unsigned int load_at_prev_sample; int cpu; unsigned int sample_type:1; /* @@ -549,6 +550,7 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) { /* Extrapolated load of this CPU */ unsigned int load_at_max_freq = 0; + unsigned int avg_load_at_max_freq = 0; unsigned int max_load_freq; /* Current load across this CPU */ unsigned int cur_load = 0; @@ -647,9 +649,17 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) /* calculate the scaled load across CPU */ load_at_max_freq += (cur_load * policy->cur) / policy->cpuinfo.max_freq; + + avg_load_at_max_freq += ((load_at_max_freq + + j_dbs_info->load_at_prev_sample) / 2); + + j_dbs_info->load_at_prev_sample = load_at_max_freq; } - cpufreq_notify_utilization(policy, load_at_max_freq); + if (min_sampling_rate < DEFAULT_SAMPLING_RATE) + cpufreq_notify_utilization(policy, avg_load_at_max_freq); + else + cpufreq_notify_utilization(policy, load_at_max_freq); /* Check for frequency increase */ if (max_load_freq > dbs_tuners_ins.up_threshold * policy->cur) { From 79f88dcbb0138c23d9e3330b507c9c6fb1ce11a0 Mon Sep 17 00:00:00 2001 From: Narayanan Gopalakrishnan Date: Thu, 9 Aug 2012 16:18:59 -0700 Subject: [PATCH 2417/2556] cpufreq: boost the sampling rate on touch event For better Ux responsiveness ondemand sampling rate needs to be 20ms. But, a 20ms sampling rate increases power consumption. So, boost the sampling rate to 20ms on every touch event for 2.5 ms and later reset to default rate. Also, change sampling down factor proportional to the sampling rate. Change-Id: I111b1cf3b8ed133347149afc34d329d0384ecfcb Signed-off-by: Narayanan Gopalakrishnan Conflicts: drivers/cpufreq/cpufreq_ondemand.c --- drivers/cpufreq/cpufreq_ondemand.c | 48 +++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 7c678857a25e1..8c4c84d82afcb 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -34,6 +34,7 @@ #define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10) #define DEF_FREQUENCY_UP_THRESHOLD (80) #define DEF_SAMPLING_DOWN_FACTOR (1) +#define BOOSTED_SAMPLING_DOWN_FACTOR (10) #define MAX_SAMPLING_DOWN_FACTOR (100000) #define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3) #define MICRO_FREQUENCY_UP_THRESHOLD (95) @@ -59,10 +60,17 @@ u64 freq_boosted_time; static unsigned int min_sampling_rate; #define DEFAULT_SAMPLING_RATE (50000) +#define BOOSTED_SAMPLING_RATE (20000) #define LATENCY_MULTIPLIER (1000) #define MIN_LATENCY_MULTIPLIER (100) #define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000) +/* have the timer rate booted for this much time 2.5s*/ +#define TIMER_RATE_BOOST_TIME 2500000 +int sampling_rate_boosted; +u64 sampling_rate_boosted_time; +unsigned int current_sampling_rate; + static void do_dbs_timer(struct work_struct *work); static int cpufreq_governor_dbs(struct cpufreq_policy *policy, unsigned int event); @@ -359,6 +367,11 @@ static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, dbs_tuners_ins.boosted = 1; freq_boosted_time = ktime_to_us(ktime_get()); + + if (sampling_rate_boosted) { + sampling_rate_boosted = 0; + dbs_tuners_ins.sampling_rate = current_sampling_rate; + } return count; } @@ -383,6 +396,7 @@ static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, if (ret != 1) return -EINVAL; update_sampling_rate(input); + current_sampling_rate = dbs_tuners_ins.sampling_rate; return count; } @@ -560,6 +574,7 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) this_dbs_info->freq_lo = 0; policy = this_dbs_info->cur_policy; + /* Only core0 controls the boost */ if (dbs_tuners_ins.boosted && policy->cpu == 0) { if (ktime_to_us(ktime_get()) - freq_boosted_time >= @@ -567,6 +582,17 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) dbs_tuners_ins.boosted = 0; } } + + /* Only core0 controls the timer_rate */ + if (sampling_rate_boosted && policy->cpu == 0) { + if (ktime_to_us(ktime_get()) - sampling_rate_boosted_time >= + TIMER_RATE_BOOST_TIME) { + + dbs_tuners_ins.sampling_rate = current_sampling_rate; + sampling_rate_boosted = 0; + } + } + /* * Every sampling_rate, we check, if current idle time is less * than 20% (default), then we try to increase frequency @@ -656,7 +682,7 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) j_dbs_info->load_at_prev_sample = load_at_max_freq; } - if (min_sampling_rate < DEFAULT_SAMPLING_RATE) + if (dbs_tuners_ins.sampling_rate < DEFAULT_SAMPLING_RATE) cpufreq_notify_utilization(policy, avg_load_at_max_freq); else cpufreq_notify_utilization(policy, load_at_max_freq); @@ -664,9 +690,17 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) /* Check for frequency increase */ if (max_load_freq > dbs_tuners_ins.up_threshold * policy->cur) { /* If switching to max speed, apply sampling_down_factor */ - if (policy->cur < policy->max) - this_dbs_info->rate_mult = - dbs_tuners_ins.sampling_down_factor; + if (policy->cur < policy->max) { + if (sampling_rate_boosted && + (dbs_tuners_ins.sampling_down_factor < + BOOSTED_SAMPLING_DOWN_FACTOR)) { + this_dbs_info->rate_mult = + BOOSTED_SAMPLING_DOWN_FACTOR; + } else { + this_dbs_info->rate_mult = + dbs_tuners_ins.sampling_down_factor; + } + } dbs_freq_increase(policy, policy->max); return; } @@ -831,6 +865,12 @@ static void dbs_input_event(struct input_handle *handle, unsigned int type, { int i; + if (current_sampling_rate > BOOSTED_SAMPLING_RATE) { + dbs_tuners_ins.sampling_rate = BOOSTED_SAMPLING_RATE; + sampling_rate_boosted_time = ktime_to_us(ktime_get()); + sampling_rate_boosted = 1; + } + for_each_online_cpu(i) { queue_work_on(i, input_wq, &per_cpu(dbs_refresh_work, i)); } From b68d10ef0335a2ad84ac5148909480ec1d7cf05f Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 2 Oct 2012 20:55:14 -0500 Subject: [PATCH 2418/2556] config: evervolv: mahimahi/bravo set def gov to ondemand Change-Id: I4b22f50ef113e1a70f9c02434cd736193e235e94 --- arch/arm/configs/evervolv_bravo_defconfig | 6 +++--- arch/arm/configs/evervolv_mahimahi_defconfig | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index 910570fe0c2a7..f5d4486b1b35e 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Wed Sep 5 18:34:31 2012 +# Tue Oct 2 20:53:53 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -473,8 +473,8 @@ CONFIG_CPU_FREQ_STAT=y CONFIG_CPU_FREQ_STAT_DETAILS=y # CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set -CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y -# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index 6f9eae0cc69fa..41322d6abae40 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Wed Sep 5 18:33:37 2012 +# Tue Oct 2 20:54:19 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -473,8 +473,8 @@ CONFIG_CPU_FREQ_STAT=y CONFIG_CPU_FREQ_STAT_DETAILS=y # CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set -CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y -# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set From 4d4dd512f9e3e8f5f774041a990d5ee4f522db2a Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Fri, 5 Oct 2012 00:33:43 -0500 Subject: [PATCH 2419/2556] mdp: hack increase timeout to 5s Change-Id: If103ee16a37b2339fa86077d1eedb560655cf9c7 --- drivers/video/msm/mdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 19b4d321556ac..aea60df6459dc 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -286,7 +286,7 @@ int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq) unsigned long irq_flags=0; // pr_info("%s: WAITING for 0x%x\n", __func__, mask); - wait_event_timeout(*wq, !mdp_check_mask(mdp, mask), HZ); + wait_event_timeout(*wq, !mdp_check_mask(mdp, mask), 5*HZ); spin_lock_irqsave(&mdp->lock, irq_flags); if (mdp_irq_mask & mask) { From 8a49c806b27b3164ac805edd577f59100b62a9cc Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 14 Oct 2012 22:26:43 -0500 Subject: [PATCH 2420/2556] cpufreq: ondemand: fixup boostpulse per cyanogen *enable duration *remove input handler Change-Id: I592590d9caa174659cb96c5bb3a5d5f55cb4bf9c --- drivers/cpufreq/cpufreq_ondemand.c | 36 +++++++++++++----------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 8c4c84d82afcb..c94e4255e1414 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -42,7 +42,9 @@ #define MIN_FREQUENCY_UP_THRESHOLD (11) #define MAX_FREQUENCY_UP_THRESHOLD (100) #define MIN_FREQUENCY_DOWN_DIFFERENTIAL (1) -#define DEFAULT_FREQ_BOOST_TIME (2500000) +#define DEFAULT_FREQ_BOOST_TIME (500000) +#define MAX_FREQ_BOOST_TIME (5000000) +#define MAX_FREQ_LIMIT (998400) /* qsd8k hack */ u64 freq_boosted_time; @@ -141,6 +143,7 @@ static struct dbs_tuners { .ignore_nice = 0, .powersave_bias = 0, .freq_boost_time = DEFAULT_FREQ_BOOST_TIME, + .boostfreq = MAX_FREQ_LIMIT, }; static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu, @@ -281,7 +284,6 @@ show_one(sampling_down_factor, sampling_down_factor); show_one(ignore_nice_load, ignore_nice); show_one(powersave_bias, powersave_bias); show_one(boostpulse, boosted); -show_one(boosttime, freq_boost_time); show_one(boostfreq, boostfreq); /** @@ -340,31 +342,21 @@ static void update_sampling_rate(unsigned int new_rate) } } -static ssize_t store_boosttime(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t count) -{ - unsigned int input; - int ret; - - ret = sscanf(buf, "%u", &input); - if (ret != 1) - return -EINVAL; - - dbs_tuners_ins.freq_boost_time = input; - return count; -} - - static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { int ret; - unsigned long val; + unsigned int input; - ret = kstrtoul(buf, 0, &val); + ret = sscanf(buf, "%u", &input); if (ret < 0) return ret; + if (input > 1 && input <= MAX_FREQ_BOOST_TIME) + dbs_tuners_ins.freq_boost_time = input; + else + dbs_tuners_ins.freq_boost_time = DEFAULT_FREQ_BOOST_TIME; + dbs_tuners_ins.boosted = 1; freq_boosted_time = ktime_to_us(ktime_get()); @@ -524,7 +516,6 @@ define_one_global_rw(sampling_down_factor); define_one_global_rw(ignore_nice_load); define_one_global_rw(powersave_bias); define_one_global_rw(boostpulse); -define_one_global_rw(boosttime); define_one_global_rw(boostfreq); static struct attribute *dbs_attributes[] = { @@ -537,7 +528,6 @@ static struct attribute *dbs_attributes[] = { &powersave_bias.attr, &io_is_busy.attr, &boostpulse.attr, - &boosttime.attr, &boostfreq.attr, NULL }; @@ -985,8 +975,10 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, latency * LATENCY_MULTIPLIER); dbs_tuners_ins.io_is_busy = should_io_be_busy(); } +#if 0 if (!cpu) rc = input_register_handler(&dbs_input_handler); +#endif mutex_unlock(&dbs_mutex); dbs_timer_init(this_dbs_info); @@ -1001,8 +993,10 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, /* If device is being removed, policy is no longer * valid. */ this_dbs_info->cur_policy = NULL; +#if 0 if (!cpu) input_unregister_handler(&dbs_input_handler); +#endif if (!dbs_enable) sysfs_remove_group(cpufreq_global_kobject, &dbs_attr_group); From 2991f0b75cc9566ee6b9cb6d89495d2cc704a691 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 14 Oct 2012 22:58:53 -0500 Subject: [PATCH 2421/2556] enable compaction on lowmem_shrink per cyanogen Change-Id: Ie82693fe7388c6e002682347e4b4ebbd59176f0c --- drivers/staging/android/lowmemorykiller.c | 5 +++++ include/linux/compaction.h | 5 +++++ mm/compaction.c | 9 +++++---- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index f9daca3bcd873..ca8a1586ecf2e 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -36,6 +36,7 @@ #include #include #include +#include static uint32_t lowmem_debug_level = 2; static int lowmem_adj[6] = { @@ -56,6 +57,8 @@ static int lowmem_minfree_size = 4; static struct task_struct *lowmem_deathpending; static unsigned long lowmem_deathpending_timeout; +extern int compact_nodes(bool sync); + #define lowmem_print(level, x...) \ do { \ if (lowmem_debug_level >= (level)) \ @@ -178,6 +181,8 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n", sc->nr_to_scan, sc->gfp_mask, rem); rcu_read_unlock(); + if (selected) + compact_nodes(false); return rem; } diff --git a/include/linux/compaction.h b/include/linux/compaction.h index cc9f7a4286490..32e34242d8635 100644 --- a/include/linux/compaction.h +++ b/include/linux/compaction.h @@ -84,6 +84,11 @@ static inline bool compaction_deferred(struct zone *zone) return 1; } +static inline int compact_nodes(bool sync) +{ + return COMPACT_CONTINUE; +} + #endif /* CONFIG_COMPACTION */ #if defined(CONFIG_COMPACTION) && defined(CONFIG_SYSFS) && defined(CONFIG_NUMA) diff --git a/mm/compaction.c b/mm/compaction.c index dcb058bd76c42..a5b39dbc71e6c 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -596,7 +596,7 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist, /* Compact all zones within a node */ -static int compact_node(int nid) +static int compact_node(int nid, bool sync) { int zoneid; pg_data_t *pgdat; @@ -614,6 +614,7 @@ static int compact_node(int nid) .nr_freepages = 0, .nr_migratepages = 0, .order = -1, + .sync = sync, }; zone = &pgdat->node_zones[zoneid]; @@ -634,12 +635,12 @@ static int compact_node(int nid) } /* Compact all nodes in the system */ -static int compact_nodes(void) +int compact_nodes(bool sync) { int nid; for_each_online_node(nid) - compact_node(nid); + compact_node(nid, sync); return COMPACT_COMPLETE; } @@ -652,7 +653,7 @@ int sysctl_compaction_handler(struct ctl_table *table, int write, void __user *buffer, size_t *length, loff_t *ppos) { if (write) - return compact_nodes(); + return compact_nodes(true); return 0; } From 2b405b24557c2e34c4c34aae50168bf7d19802e9 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 14 Oct 2012 23:26:08 -0500 Subject: [PATCH 2422/2556] lowmemorykiller: fix compile warning Change-Id: I460c71adbc389f46716cf5bfdc5af1a982f205d1 --- drivers/staging/android/lowmemorykiller.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index ca8a1586ecf2e..b3d7620e08421 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -36,6 +36,7 @@ #include #include #include +#include #include static uint32_t lowmem_debug_level = 2; From 9bd4c06b600989f134c25e4a6bd185b34956b570 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 14 Oct 2012 23:53:19 -0500 Subject: [PATCH 2423/2556] cpufreq: ondemand: remove freq max hardcode Change-Id: I675fecf76d3c51733cab4c7acc3eec2ee20e9d3f --- drivers/cpufreq/cpufreq_ondemand.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index c94e4255e1414..ab35952b2a7dc 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -44,7 +44,6 @@ #define MIN_FREQUENCY_DOWN_DIFFERENTIAL (1) #define DEFAULT_FREQ_BOOST_TIME (500000) #define MAX_FREQ_BOOST_TIME (5000000) -#define MAX_FREQ_LIMIT (998400) /* qsd8k hack */ u64 freq_boosted_time; @@ -143,7 +142,7 @@ static struct dbs_tuners { .ignore_nice = 0, .powersave_bias = 0, .freq_boost_time = DEFAULT_FREQ_BOOST_TIME, - .boostfreq = MAX_FREQ_LIMIT, + .boostfreq = CONFIG_MSM_CPU_FREQ_MAX, }; static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu, From ae05be7cb136aa5c5f71fce19073efd85c9a5d49 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 14 Oct 2012 23:54:57 -0500 Subject: [PATCH 2424/2556] cpufreq: ondemand: fix compile warning Change-Id: Ib8f0b268f8705f0d1557a8774825a4a1a6e40ab4 --- drivers/cpufreq/cpufreq_ondemand.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index ab35952b2a7dc..59512ebb4d09a 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -849,6 +849,7 @@ static void dbs_refresh_callback(struct work_struct *unused) unlock_policy_rwsem_write(cpu); } +#if 0 static void dbs_input_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { @@ -914,6 +915,7 @@ static struct input_handler dbs_input_handler = { .name = "cpufreq_ond", .id_table = dbs_ids, }; +#endif static int cpufreq_governor_dbs(struct cpufreq_policy *policy, unsigned int event) From 7f817d9a264ccddb3c284f45ca3d7de32e393c87 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Mon, 15 Oct 2012 16:32:24 -0500 Subject: [PATCH 2425/2556] cpufreq: ondemand: remove input ramping completely the PowerHal will activate the boostpulse interface instead Change-Id: Ib38f4ebe8692c6454973d4d5a2c9a66c956f9310 --- drivers/cpufreq/cpufreq_ondemand.c | 85 ------------------------------ 1 file changed, 85 deletions(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 59512ebb4d09a..aafa9d53b4be4 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -120,8 +119,6 @@ static unsigned int dbs_enable; /* number of CPUs using this policy */ */ static DEFINE_MUTEX(dbs_mutex); -static struct workqueue_struct *input_wq; - static DEFINE_PER_CPU(struct work_struct, dbs_refresh_work); static struct dbs_tuners { @@ -849,74 +846,6 @@ static void dbs_refresh_callback(struct work_struct *unused) unlock_policy_rwsem_write(cpu); } -#if 0 -static void dbs_input_event(struct input_handle *handle, unsigned int type, - unsigned int code, int value) -{ - int i; - - if (current_sampling_rate > BOOSTED_SAMPLING_RATE) { - dbs_tuners_ins.sampling_rate = BOOSTED_SAMPLING_RATE; - sampling_rate_boosted_time = ktime_to_us(ktime_get()); - sampling_rate_boosted = 1; - } - - for_each_online_cpu(i) { - queue_work_on(i, input_wq, &per_cpu(dbs_refresh_work, i)); - } -} - -static int dbs_input_connect(struct input_handler *handler, - struct input_dev *dev, const struct input_device_id *id) -{ - struct input_handle *handle; - int error; - - handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); - if (!handle) - return -ENOMEM; - - handle->dev = dev; - handle->handler = handler; - handle->name = "cpufreq"; - - error = input_register_handle(handle); - if (error) - goto err2; - - error = input_open_device(handle); - if (error) - goto err1; - - return 0; -err1: - input_unregister_handle(handle); -err2: - kfree(handle); - return error; -} - -static void dbs_input_disconnect(struct input_handle *handle) -{ - input_close_device(handle); - input_unregister_handle(handle); - kfree(handle); -} - -static const struct input_device_id dbs_ids[] = { - { .driver_info = 1 }, - { }, -}; - -static struct input_handler dbs_input_handler = { - .event = dbs_input_event, - .connect = dbs_input_connect, - .disconnect = dbs_input_disconnect, - .name = "cpufreq_ond", - .id_table = dbs_ids, -}; -#endif - static int cpufreq_governor_dbs(struct cpufreq_policy *policy, unsigned int event) { @@ -976,10 +905,6 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, latency * LATENCY_MULTIPLIER); dbs_tuners_ins.io_is_busy = should_io_be_busy(); } -#if 0 - if (!cpu) - rc = input_register_handler(&dbs_input_handler); -#endif mutex_unlock(&dbs_mutex); dbs_timer_init(this_dbs_info); @@ -994,10 +919,6 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, /* If device is being removed, policy is no longer * valid. */ this_dbs_info->cur_policy = NULL; -#if 0 - if (!cpu) - input_unregister_handler(&dbs_input_handler); -#endif if (!dbs_enable) sysfs_remove_group(cpufreq_global_kobject, &dbs_attr_group); @@ -1044,11 +965,6 @@ static int __init cpufreq_gov_dbs_init(void) MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10); } - input_wq = create_workqueue("iewq"); - if (!input_wq) { - printk(KERN_ERR "Failed to create iewq workqueue\n"); - return -EFAULT; - } for_each_possible_cpu(i) { struct cpu_dbs_info_s *this_dbs_info = &per_cpu(od_cpu_dbs_info, i); @@ -1062,7 +978,6 @@ static int __init cpufreq_gov_dbs_init(void) static void __exit cpufreq_gov_dbs_exit(void) { cpufreq_unregister_governor(&cpufreq_gov_ondemand); - destroy_workqueue(input_wq); } From 0debdf0ce6ac8ece0876cb7b0fbaa47c2a2f25c3 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Mon, 15 Oct 2012 23:37:12 -0500 Subject: [PATCH 2426/2556] Revert "cpufreq: boost the sampling rate on touch event" This reverts commit 79f88dcbb0138c23d9e3330b507c9c6fb1ce11a0. without the input events this does nothing, removed Conflicts: drivers/cpufreq/cpufreq_ondemand.c Change-Id: Iaa13807318806abe13d6400b2afa697ef2879a44 --- drivers/cpufreq/cpufreq_ondemand.c | 42 +++--------------------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index aafa9d53b4be4..60fedf088c28a 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -33,7 +33,6 @@ #define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10) #define DEF_FREQUENCY_UP_THRESHOLD (80) #define DEF_SAMPLING_DOWN_FACTOR (1) -#define BOOSTED_SAMPLING_DOWN_FACTOR (10) #define MAX_SAMPLING_DOWN_FACTOR (100000) #define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3) #define MICRO_FREQUENCY_UP_THRESHOLD (95) @@ -60,17 +59,10 @@ u64 freq_boosted_time; static unsigned int min_sampling_rate; #define DEFAULT_SAMPLING_RATE (50000) -#define BOOSTED_SAMPLING_RATE (20000) #define LATENCY_MULTIPLIER (1000) #define MIN_LATENCY_MULTIPLIER (100) #define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000) -/* have the timer rate booted for this much time 2.5s*/ -#define TIMER_RATE_BOOST_TIME 2500000 -int sampling_rate_boosted; -u64 sampling_rate_boosted_time; -unsigned int current_sampling_rate; - static void do_dbs_timer(struct work_struct *work); static int cpufreq_governor_dbs(struct cpufreq_policy *policy, unsigned int event); @@ -355,11 +347,6 @@ static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, dbs_tuners_ins.boosted = 1; freq_boosted_time = ktime_to_us(ktime_get()); - - if (sampling_rate_boosted) { - sampling_rate_boosted = 0; - dbs_tuners_ins.sampling_rate = current_sampling_rate; - } return count; } @@ -384,7 +371,6 @@ static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, if (ret != 1) return -EINVAL; update_sampling_rate(input); - current_sampling_rate = dbs_tuners_ins.sampling_rate; return count; } @@ -560,7 +546,6 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) this_dbs_info->freq_lo = 0; policy = this_dbs_info->cur_policy; - /* Only core0 controls the boost */ if (dbs_tuners_ins.boosted && policy->cpu == 0) { if (ktime_to_us(ktime_get()) - freq_boosted_time >= @@ -568,17 +553,6 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) dbs_tuners_ins.boosted = 0; } } - - /* Only core0 controls the timer_rate */ - if (sampling_rate_boosted && policy->cpu == 0) { - if (ktime_to_us(ktime_get()) - sampling_rate_boosted_time >= - TIMER_RATE_BOOST_TIME) { - - dbs_tuners_ins.sampling_rate = current_sampling_rate; - sampling_rate_boosted = 0; - } - } - /* * Every sampling_rate, we check, if current idle time is less * than 20% (default), then we try to increase frequency @@ -668,7 +642,7 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) j_dbs_info->load_at_prev_sample = load_at_max_freq; } - if (dbs_tuners_ins.sampling_rate < DEFAULT_SAMPLING_RATE) + if (min_sampling_rate < DEFAULT_SAMPLING_RATE) cpufreq_notify_utilization(policy, avg_load_at_max_freq); else cpufreq_notify_utilization(policy, load_at_max_freq); @@ -676,17 +650,9 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) /* Check for frequency increase */ if (max_load_freq > dbs_tuners_ins.up_threshold * policy->cur) { /* If switching to max speed, apply sampling_down_factor */ - if (policy->cur < policy->max) { - if (sampling_rate_boosted && - (dbs_tuners_ins.sampling_down_factor < - BOOSTED_SAMPLING_DOWN_FACTOR)) { - this_dbs_info->rate_mult = - BOOSTED_SAMPLING_DOWN_FACTOR; - } else { - this_dbs_info->rate_mult = - dbs_tuners_ins.sampling_down_factor; - } - } + if (policy->cur < policy->max) + this_dbs_info->rate_mult = + dbs_tuners_ins.sampling_down_factor; dbs_freq_increase(policy, policy->max); return; } From 928e0d15aef12fca633a55f16072fc86e5e5d77c Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Mon, 15 Oct 2012 23:35:25 -0500 Subject: [PATCH 2427/2556] cpufrq: ondemand: dont ever reduce the boostfreq if max_freq is reduced the boostfreq is lowered to that value but it never goes back up, so remove this. it shouldnt matter too much Change-Id: I205af2ec36b243ff40b5873677176fab2036a47b --- drivers/cpufreq/cpufreq_ondemand.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 60fedf088c28a..1d05ac09444b7 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -660,7 +660,6 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) /* check for frequency boost */ if (dbs_tuners_ins.boosted && policy->cur < dbs_tuners_ins.boostfreq) { dbs_freq_increase(policy, dbs_tuners_ins.boostfreq); - dbs_tuners_ins.boostfreq = policy->cur; return; } From 9373e40d4846b9dd0341d88be7af735b64c6a5f9 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 15 Jun 2011 17:44:50 -0700 Subject: [PATCH 2428/2556] ARM: Call idle notifiers Change-Id: Id833e61c13baa1783705ac9e9046d1f0cc90c95e Acked-by: Nicolas Pitre Signed-off-by: Todd Poynor --- arch/arm/kernel/process.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 8335b86166200..d00b44f633d03 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -184,6 +184,7 @@ void cpu_idle(void) while (1) { tick_nohz_stop_sched_tick(1); leds_event(led_idle_start); + idle_notifier_call_chain(IDLE_START); while (!need_resched()) { #ifdef CONFIG_HOTPLUG_CPU if (cpu_is_offline(smp_processor_id())) @@ -208,6 +209,7 @@ void cpu_idle(void) } } leds_event(led_idle_end); + idle_notifier_call_chain(IDLE_END); tick_nohz_restart_sched_tick(); preempt_enable_no_resched(); schedule(); From 77ebde004a533ddd28a054a7dc0839f86404a9dc Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 24 Jun 2011 19:33:01 -0700 Subject: [PATCH 2429/2556] ARM: Move leds idle start/stop calls to idle notifiers Change-Id: I5d8e4e85b17bbab7992ecb477f0bdb5e4138b166 Acked-by: Nicolas Pitre Signed-off-by: Todd Poynor --- arch/arm/kernel/leds.c | 27 ++++++++++++++++++++++++++- arch/arm/kernel/process.c | 3 --- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/arch/arm/kernel/leds.c b/arch/arm/kernel/leds.c index 0f107dcb03479..136e8376a3eb4 100644 --- a/arch/arm/kernel/leds.c +++ b/arch/arm/kernel/leds.c @@ -9,6 +9,8 @@ */ #include #include +#include +#include #include #include @@ -101,6 +103,25 @@ static struct syscore_ops leds_syscore_ops = { .resume = leds_resume, }; +static int leds_idle_notifier(struct notifier_block *nb, unsigned long val, + void *data) +{ + switch (val) { + case IDLE_START: + leds_event(led_idle_start); + break; + case IDLE_END: + leds_event(led_idle_end); + break; + } + + return 0; +} + +static struct notifier_block leds_idle_nb = { + .notifier_call = leds_idle_notifier, +}; + static int __init leds_init(void) { int ret; @@ -109,8 +130,12 @@ static int __init leds_init(void) ret = sysdev_register(&leds_device); if (ret == 0) ret = sysdev_create_file(&leds_device, &attr_event); - if (ret == 0) + + if (ret == 0) { register_syscore_ops(&leds_syscore_ops); + idle_notifier_register(&leds_idle_nb); + } + return ret; } diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index d00b44f633d03..61ad853d05eef 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -32,7 +32,6 @@ #include #include -#include #include #include #include @@ -183,7 +182,6 @@ void cpu_idle(void) /* endless idle loop with no priority at all */ while (1) { tick_nohz_stop_sched_tick(1); - leds_event(led_idle_start); idle_notifier_call_chain(IDLE_START); while (!need_resched()) { #ifdef CONFIG_HOTPLUG_CPU @@ -208,7 +206,6 @@ void cpu_idle(void) local_irq_enable(); } } - leds_event(led_idle_end); idle_notifier_call_chain(IDLE_END); tick_nohz_restart_sched_tick(); preempt_enable_no_resched(); From 6399af700957b7d338de58b5e37793f877e884d7 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 29 Nov 2011 16:37:07 -0800 Subject: [PATCH 2430/2556] ARM: idle: call idle notifiers before stopping nohz tick If an idle notifier modifies a timer, calling the notifier after the sched tick has been stopped may leave the sched tick set too early. Move teh idle notifier call before the call to tick_nohz_stop_sched_tick. Change-Id: I0db3284bec6d0193bc5e2a57650ab06bd8342319 Signed-off-by: Colin Cross --- arch/arm/kernel/process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 61ad853d05eef..4bba6a2279470 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -181,8 +181,8 @@ void cpu_idle(void) /* endless idle loop with no priority at all */ while (1) { - tick_nohz_stop_sched_tick(1); idle_notifier_call_chain(IDLE_START); + tick_nohz_stop_sched_tick(1); while (!need_resched()) { #ifdef CONFIG_HOTPLUG_CPU if (cpu_is_offline(smp_processor_id())) From c8e1f7cafde986b2ffc4bde2f7b740d3648b394b Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 3 Nov 2011 21:05:41 -0700 Subject: [PATCH 2431/2556] ARM: idle: update idle ticks before call idle end notifier Such that interactive cpufreq governor uses up-to-date idle time information. Reported by Colin Cross Change-Id: I06425444f800f803afc9dc7a6ad0fdb46c918bb6 Signed-off-by: Todd Poynor --- arch/arm/kernel/process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 4bba6a2279470..2dc0e81c0ef2f 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -206,8 +206,8 @@ void cpu_idle(void) local_irq_enable(); } } - idle_notifier_call_chain(IDLE_END); tick_nohz_restart_sched_tick(); + idle_notifier_call_chain(IDLE_END); preempt_enable_no_resched(); schedule(); preempt_disable(); From a2e433e0effbd62a7d691b28b13078426863fe42 Mon Sep 17 00:00:00 2001 From: Sam Leffler Date: Wed, 27 Jun 2012 10:12:04 -0700 Subject: [PATCH 2432/2556] cpufreq: interactive: take idle notifications only when active Register an idle notifier only when the governor is active. Also short-circuit work of idle end if the governor is not enabled. Signed-off-by: Sam Leffler Change-Id: I4cae36dd2e7389540d337d74745ffbaa0131870f --- drivers/cpufreq/cpufreq_interactive.c | 46 +++++++++++++++------------ 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 7dbacf035e7a7..18f44c7d346ae 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -366,6 +366,9 @@ static void cpufreq_interactive_idle_end(void) struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, smp_processor_id()); + if (!pcpu->governor_enabled) + return; + pcpu->idling = 0; smp_wmb(); @@ -818,6 +821,26 @@ static struct attribute_group interactive_attr_group = { .name = "interactive", }; +static int cpufreq_interactive_idle_notifier(struct notifier_block *nb, + unsigned long val, + void *data) +{ + switch (val) { + case IDLE_START: + cpufreq_interactive_idle_start(); + break; + case IDLE_END: + cpufreq_interactive_idle_end(); + break; + } + + return 0; +} + +static struct notifier_block cpufreq_interactive_idle_nb = { + .notifier_call = cpufreq_interactive_idle_notifier, +}; + static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event) { @@ -871,6 +894,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pr_warn("%s: failed to register input handler\n", __func__); + idle_notifier_register(&cpufreq_interactive_idle_nb); break; case CPUFREQ_GOV_STOP: @@ -893,6 +917,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, if (atomic_dec_return(&active_count) > 0) return 0; + idle_notifier_unregister(&cpufreq_interactive_idle_nb); input_unregister_handler(&cpufreq_interactive_input_handler); sysfs_remove_group(cpufreq_global_kobject, &interactive_attr_group); @@ -911,26 +936,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, return 0; } -static int cpufreq_interactive_idle_notifier(struct notifier_block *nb, - unsigned long val, - void *data) -{ - switch (val) { - case IDLE_START: - cpufreq_interactive_idle_start(); - break; - case IDLE_END: - cpufreq_interactive_idle_end(); - break; - } - - return 0; -} - -static struct notifier_block cpufreq_interactive_idle_nb = { - .notifier_call = cpufreq_interactive_idle_notifier, -}; - static int __init cpufreq_interactive_init(void) { unsigned int i; @@ -972,7 +977,6 @@ static int __init cpufreq_interactive_init(void) spin_lock_init(&down_cpumask_lock); mutex_init(&set_speed_lock); - idle_notifier_register(&cpufreq_interactive_idle_nb); INIT_WORK(&inputopen.inputopen_work, cpufreq_interactive_input_open); return cpufreq_register_governor(&cpufreq_gov_interactive); From e771a843fad60db67caba108627a87524f8132d6 Mon Sep 17 00:00:00 2001 From: Sam Leffler Date: Wed, 27 Jun 2012 12:55:56 -0700 Subject: [PATCH 2433/2556] cpufreq: interactive: keep freezer happy when not current governor Fix a problem where the hung task mechanism was deeming the interactive clock boost thread as hung. This was because the thread is created at module init but never run/woken up until needed. If the governor is not being used this can be forever. To workaround this explicitly wake up the thread once all the necessary data structures are initialized. The latter required some minor code shuffle. Signed-off-by: Sam Leffler Change-Id: Ie2c058dd75dcb6460ea10e7ac997e46baf66b1fe --- drivers/cpufreq/cpufreq_interactive.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 18f44c7d346ae..4c8648ed3e505 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -955,6 +955,10 @@ static int __init cpufreq_interactive_init(void) pcpu->cpu_timer.data = i; } + spin_lock_init(&up_cpumask_lock); + spin_lock_init(&down_cpumask_lock); + mutex_init(&set_speed_lock); + up_task = kthread_create(cpufreq_interactive_up_task, NULL, "kinteractiveup"); if (IS_ERR(up_task)) @@ -970,14 +974,12 @@ static int __init cpufreq_interactive_init(void) if (!down_wq) goto err_freeuptask; - INIT_WORK(&freq_scale_down_work, - cpufreq_interactive_freq_down); + INIT_WORK(&freq_scale_down_work, cpufreq_interactive_freq_down); + INIT_WORK(&inputopen.inputopen_work, cpufreq_interactive_input_open); - spin_lock_init(&up_cpumask_lock); - spin_lock_init(&down_cpumask_lock); - mutex_init(&set_speed_lock); + /* NB: wake up so the thread does not look hung to the freezer */ + wake_up_process(up_task); - INIT_WORK(&inputopen.inputopen_work, cpufreq_interactive_input_open); return cpufreq_register_governor(&cpufreq_gov_interactive); err_freeuptask: From 5a50d53e5bc3a02591165689616ff21a9684747f Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 16 Jul 2012 17:07:15 -0700 Subject: [PATCH 2434/2556] cpufreq: interactive: handle speed up and down in the realtime task Not useful to have a separate, non-realtime workqueue for speed down events, avoid priority inversion for speed up events. Change-Id: Iddcd05545245c847aa1bbe0b8790092914c813d2 Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 148 ++++++--------------- include/trace/events/cpufreq_interactive.h | 8 +- 2 files changed, 45 insertions(+), 111 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 4c8648ed3e505..241d3d446ab80 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -56,15 +56,10 @@ struct cpufreq_interactive_cpuinfo { static DEFINE_PER_CPU(struct cpufreq_interactive_cpuinfo, cpuinfo); -/* Workqueues handle frequency scaling */ -static struct task_struct *up_task; -static struct workqueue_struct *down_wq; -static struct work_struct freq_scale_down_work; -static cpumask_t up_cpumask; -static spinlock_t up_cpumask_lock; -static cpumask_t down_cpumask; -static spinlock_t down_cpumask_lock; -static struct mutex set_speed_lock; +/* realtime thread handles frequency scaling */ +static struct task_struct *speedchange_task; +static cpumask_t speedchange_cpumask; +static spinlock_t speedchange_cpumask_lock; /* Hi speed to bump to from lo speed when load burst (default max) */ static u64 hispeed_freq; @@ -104,6 +99,7 @@ struct cpufreq_interactive_inputopen { }; static struct cpufreq_interactive_inputopen inputopen; +static struct workqueue_struct *inputopen_wq; /* * Non-zero means longer-term speed boost active. @@ -261,19 +257,11 @@ static void cpufreq_interactive_timer(unsigned long data) pcpu->target_set_time_in_idle = now_idle; pcpu->target_set_time = pcpu->timer_run_time; - if (new_freq < pcpu->target_freq) { - pcpu->target_freq = new_freq; - spin_lock_irqsave(&down_cpumask_lock, flags); - cpumask_set_cpu(data, &down_cpumask); - spin_unlock_irqrestore(&down_cpumask_lock, flags); - queue_work(down_wq, &freq_scale_down_work); - } else { - pcpu->target_freq = new_freq; - spin_lock_irqsave(&up_cpumask_lock, flags); - cpumask_set_cpu(data, &up_cpumask); - spin_unlock_irqrestore(&up_cpumask_lock, flags); - wake_up_process(up_task); - } + pcpu->target_freq = new_freq; + spin_lock_irqsave(&speedchange_cpumask_lock, flags); + cpumask_set_cpu(data, &speedchange_cpumask); + spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); + wake_up_process(speedchange_task); rearm_if_notmax: /* @@ -396,7 +384,7 @@ static void cpufreq_interactive_idle_end(void) } -static int cpufreq_interactive_up_task(void *data) +static int cpufreq_interactive_speedchange_task(void *data) { unsigned int cpu; cpumask_t tmp_mask; @@ -405,22 +393,23 @@ static int cpufreq_interactive_up_task(void *data) while (1) { set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&up_cpumask_lock, flags); + spin_lock_irqsave(&speedchange_cpumask_lock, flags); - if (cpumask_empty(&up_cpumask)) { - spin_unlock_irqrestore(&up_cpumask_lock, flags); + if (cpumask_empty(&speedchange_cpumask)) { + spin_unlock_irqrestore(&speedchange_cpumask_lock, + flags); schedule(); if (kthread_should_stop()) break; - spin_lock_irqsave(&up_cpumask_lock, flags); + spin_lock_irqsave(&speedchange_cpumask_lock, flags); } set_current_state(TASK_RUNNING); - tmp_mask = up_cpumask; - cpumask_clear(&up_cpumask); - spin_unlock_irqrestore(&up_cpumask_lock, flags); + tmp_mask = speedchange_cpumask; + cpumask_clear(&speedchange_cpumask); + spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); for_each_cpu(cpu, &tmp_mask) { unsigned int j; @@ -432,8 +421,6 @@ static int cpufreq_interactive_up_task(void *data) if (!pcpu->governor_enabled) continue; - mutex_lock(&set_speed_lock); - for_each_cpu(j, pcpu->policy->cpus) { struct cpufreq_interactive_cpuinfo *pjcpu = &per_cpu(cpuinfo, j); @@ -446,8 +433,8 @@ static int cpufreq_interactive_up_task(void *data) __cpufreq_driver_target(pcpu->policy, max_freq, CPUFREQ_RELATION_H); - mutex_unlock(&set_speed_lock); - trace_cpufreq_interactive_up(cpu, pcpu->target_freq, + trace_cpufreq_interactive_setspeed(cpu, + pcpu->target_freq, pcpu->policy->cur); } } @@ -455,48 +442,6 @@ static int cpufreq_interactive_up_task(void *data) return 0; } -static void cpufreq_interactive_freq_down(struct work_struct *work) -{ - unsigned int cpu; - cpumask_t tmp_mask; - unsigned long flags; - struct cpufreq_interactive_cpuinfo *pcpu; - - spin_lock_irqsave(&down_cpumask_lock, flags); - tmp_mask = down_cpumask; - cpumask_clear(&down_cpumask); - spin_unlock_irqrestore(&down_cpumask_lock, flags); - - for_each_cpu(cpu, &tmp_mask) { - unsigned int j; - unsigned int max_freq = 0; - - pcpu = &per_cpu(cpuinfo, cpu); - smp_rmb(); - - if (!pcpu->governor_enabled) - continue; - - mutex_lock(&set_speed_lock); - - for_each_cpu(j, pcpu->policy->cpus) { - struct cpufreq_interactive_cpuinfo *pjcpu = - &per_cpu(cpuinfo, j); - - if (pjcpu->target_freq > max_freq) - max_freq = pjcpu->target_freq; - } - - if (max_freq != pcpu->policy->cur) - __cpufreq_driver_target(pcpu->policy, max_freq, - CPUFREQ_RELATION_H); - - mutex_unlock(&set_speed_lock); - trace_cpufreq_interactive_down(cpu, pcpu->target_freq, - pcpu->policy->cur); - } -} - static void cpufreq_interactive_boost(void) { int i; @@ -504,14 +449,14 @@ static void cpufreq_interactive_boost(void) unsigned long flags; struct cpufreq_interactive_cpuinfo *pcpu; - spin_lock_irqsave(&up_cpumask_lock, flags); + spin_lock_irqsave(&speedchange_cpumask_lock, flags); for_each_online_cpu(i) { pcpu = &per_cpu(cpuinfo, i); if (pcpu->target_freq < hispeed_freq) { pcpu->target_freq = hispeed_freq; - cpumask_set_cpu(i, &up_cpumask); + cpumask_set_cpu(i, &speedchange_cpumask); pcpu->target_set_time_in_idle = get_cpu_idle_time_us(i, &pcpu->target_set_time); pcpu->hispeed_validate_time = pcpu->target_set_time; @@ -527,10 +472,10 @@ static void cpufreq_interactive_boost(void) pcpu->floor_validate_time = ktime_to_us(ktime_get()); } - spin_unlock_irqrestore(&up_cpumask_lock, flags); + spin_unlock_irqrestore(&speedchange_cpumask_lock, flags); if (anyboost) - wake_up_process(up_task); + wake_up_process(speedchange_task); } /* @@ -582,7 +527,7 @@ static int cpufreq_interactive_input_connect(struct input_handler *handler, goto err; inputopen.handle = handle; - queue_work(down_wq, &inputopen.inputopen_work); + queue_work(inputopen_wq, &inputopen.inputopen_work); return 0; err: kfree(handle); @@ -913,7 +858,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->idle_exit_time = 0; } - flush_work(&freq_scale_down_work); + flush_work(&inputopen.inputopen_work); if (atomic_dec_return(&active_count) > 0) return 0; @@ -955,35 +900,30 @@ static int __init cpufreq_interactive_init(void) pcpu->cpu_timer.data = i; } - spin_lock_init(&up_cpumask_lock); - spin_lock_init(&down_cpumask_lock); - mutex_init(&set_speed_lock); - - up_task = kthread_create(cpufreq_interactive_up_task, NULL, - "kinteractiveup"); - if (IS_ERR(up_task)) - return PTR_ERR(up_task); + spin_lock_init(&speedchange_cpumask_lock); + speedchange_task = + kthread_create(cpufreq_interactive_speedchange_task, NULL, + "cfinteractive"); + if (IS_ERR(speedchange_task)) + return PTR_ERR(speedchange_task); - sched_setscheduler_nocheck(up_task, SCHED_FIFO, ¶m); - get_task_struct(up_task); + sched_setscheduler_nocheck(speedchange_task, SCHED_FIFO, ¶m); + get_task_struct(speedchange_task); - /* No rescuer thread, bind to CPU queuing the work for possibly - warm cache (probably doesn't matter much). */ - down_wq = alloc_workqueue("knteractive_down", 0, 1); + inputopen_wq = create_workqueue("cfinteractive"); - if (!down_wq) - goto err_freeuptask; + if (!inputopen_wq) + goto err_freetask; - INIT_WORK(&freq_scale_down_work, cpufreq_interactive_freq_down); INIT_WORK(&inputopen.inputopen_work, cpufreq_interactive_input_open); /* NB: wake up so the thread does not look hung to the freezer */ - wake_up_process(up_task); + wake_up_process(speedchange_task); return cpufreq_register_governor(&cpufreq_gov_interactive); -err_freeuptask: - put_task_struct(up_task); +err_freetask: + put_task_struct(speedchange_task); return -ENOMEM; } @@ -996,9 +936,9 @@ module_init(cpufreq_interactive_init); static void __exit cpufreq_interactive_exit(void) { cpufreq_unregister_governor(&cpufreq_gov_interactive); - kthread_stop(up_task); - put_task_struct(up_task); - destroy_workqueue(down_wq); + kthread_stop(speedchange_task); + put_task_struct(speedchange_task); + destroy_workqueue(inputopen_wq); } module_exit(cpufreq_interactive_exit); diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h index 0791f2728c1bc..ba0634e2f9d72 100644 --- a/include/trace/events/cpufreq_interactive.h +++ b/include/trace/events/cpufreq_interactive.h @@ -28,13 +28,7 @@ DECLARE_EVENT_CLASS(set, __entry->actualfreq) ); -DEFINE_EVENT(set, cpufreq_interactive_up, - TP_PROTO(u32 cpu_id, unsigned long targfreq, - unsigned long actualfreq), - TP_ARGS(cpu_id, targfreq, actualfreq) -); - -DEFINE_EVENT(set, cpufreq_interactive_down, +DEFINE_EVENT(set, cpufreq_interactive_setspeed, TP_PROTO(u32 cpu_id, unsigned long targfreq, unsigned long actualfreq), TP_ARGS(cpu_id, targfreq, actualfreq) From 3a29d8949dd7e452c8c1f3b3cb89c474d3a2e77f Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 16 Jul 2012 17:32:44 -0700 Subject: [PATCH 2435/2556] cpufreq: interactive: remove input_boost handling Now handled in userspace Power HAL instead. Change-Id: I78a4a2fd471308bfcd785bbefcc65fede27314cf Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 146 -------------------------- 1 file changed, 146 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 241d3d446ab80..dc57dfb58ca9c 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #define CREATE_TRACE_POINTS @@ -87,20 +86,6 @@ static unsigned long timer_rate; #define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE static unsigned long above_hispeed_delay_val; -/* - * Boost pulse to hispeed on touchscreen input. - */ - -static int input_boost_val; - -struct cpufreq_interactive_inputopen { - struct input_handle *handle; - struct work_struct inputopen_work; -}; - -static struct cpufreq_interactive_inputopen inputopen; -static struct workqueue_struct *inputopen_wq; - /* * Non-zero means longer-term speed boost active. */ @@ -478,96 +463,6 @@ static void cpufreq_interactive_boost(void) wake_up_process(speedchange_task); } -/* - * Pulsed boost on input event raises CPUs to hispeed_freq and lets - * usual algorithm of min_sample_time decide when to allow speed - * to drop. - */ - -static void cpufreq_interactive_input_event(struct input_handle *handle, - unsigned int type, - unsigned int code, int value) -{ - if (input_boost_val && type == EV_SYN && code == SYN_REPORT) { - trace_cpufreq_interactive_boost("input"); - cpufreq_interactive_boost(); - } -} - -static void cpufreq_interactive_input_open(struct work_struct *w) -{ - struct cpufreq_interactive_inputopen *io = - container_of(w, struct cpufreq_interactive_inputopen, - inputopen_work); - int error; - - error = input_open_device(io->handle); - if (error) - input_unregister_handle(io->handle); -} - -static int cpufreq_interactive_input_connect(struct input_handler *handler, - struct input_dev *dev, - const struct input_device_id *id) -{ - struct input_handle *handle; - int error; - - pr_info("%s: connect to %s\n", __func__, dev->name); - handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); - if (!handle) - return -ENOMEM; - - handle->dev = dev; - handle->handler = handler; - handle->name = "cpufreq_interactive"; - - error = input_register_handle(handle); - if (error) - goto err; - - inputopen.handle = handle; - queue_work(inputopen_wq, &inputopen.inputopen_work); - return 0; -err: - kfree(handle); - return error; -} - -static void cpufreq_interactive_input_disconnect(struct input_handle *handle) -{ - input_close_device(handle); - input_unregister_handle(handle); - kfree(handle); -} - -static const struct input_device_id cpufreq_interactive_ids[] = { - { - .flags = INPUT_DEVICE_ID_MATCH_EVBIT | - INPUT_DEVICE_ID_MATCH_ABSBIT, - .evbit = { BIT_MASK(EV_ABS) }, - .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] = - BIT_MASK(ABS_MT_POSITION_X) | - BIT_MASK(ABS_MT_POSITION_Y) }, - }, /* multi-touch touchscreen */ - { - .flags = INPUT_DEVICE_ID_MATCH_KEYBIT | - INPUT_DEVICE_ID_MATCH_ABSBIT, - .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) }, - .absbit = { [BIT_WORD(ABS_X)] = - BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) }, - }, /* touchpad */ - { }, -}; - -static struct input_handler cpufreq_interactive_input_handler = { - .event = cpufreq_interactive_input_event, - .connect = cpufreq_interactive_input_connect, - .disconnect = cpufreq_interactive_input_disconnect, - .name = "cpufreq_interactive", - .id_table = cpufreq_interactive_ids, -}; - static ssize_t show_hispeed_freq(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -680,27 +575,6 @@ static ssize_t store_timer_rate(struct kobject *kobj, static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, show_timer_rate, store_timer_rate); -static ssize_t show_input_boost(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - return sprintf(buf, "%u\n", input_boost_val); -} - -static ssize_t store_input_boost(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t count) -{ - int ret; - unsigned long val; - - ret = strict_strtoul(buf, 0, &val); - if (ret < 0) - return ret; - input_boost_val = val; - return count; -} - -define_one_global_rw(input_boost); - static ssize_t show_boost(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -755,7 +629,6 @@ static struct attribute *interactive_attributes[] = { &above_hispeed_delay.attr, &min_sample_time_attr.attr, &timer_rate_attr.attr, - &input_boost.attr, &boost.attr, &boostpulse.attr, NULL, @@ -834,11 +707,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, if (rc) return rc; - rc = input_register_handler(&cpufreq_interactive_input_handler); - if (rc) - pr_warn("%s: failed to register input handler\n", - __func__); - idle_notifier_register(&cpufreq_interactive_idle_nb); break; @@ -858,12 +726,10 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, pcpu->idle_exit_time = 0; } - flush_work(&inputopen.inputopen_work); if (atomic_dec_return(&active_count) > 0) return 0; idle_notifier_unregister(&cpufreq_interactive_idle_nb); - input_unregister_handler(&cpufreq_interactive_input_handler); sysfs_remove_group(cpufreq_global_kobject, &interactive_attr_group); @@ -910,21 +776,10 @@ static int __init cpufreq_interactive_init(void) sched_setscheduler_nocheck(speedchange_task, SCHED_FIFO, ¶m); get_task_struct(speedchange_task); - inputopen_wq = create_workqueue("cfinteractive"); - - if (!inputopen_wq) - goto err_freetask; - - INIT_WORK(&inputopen.inputopen_work, cpufreq_interactive_input_open); - /* NB: wake up so the thread does not look hung to the freezer */ wake_up_process(speedchange_task); return cpufreq_register_governor(&cpufreq_gov_interactive); - -err_freetask: - put_task_struct(speedchange_task); - return -ENOMEM; } #ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE @@ -938,7 +793,6 @@ static void __exit cpufreq_interactive_exit(void) cpufreq_unregister_governor(&cpufreq_gov_interactive); kthread_stop(speedchange_task); put_task_struct(speedchange_task); - destroy_workqueue(inputopen_wq); } module_exit(cpufreq_interactive_exit); From d842a8b168260e9d87f32434c395bb2909f9fe04 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 24 Sep 2012 18:03:58 -0700 Subject: [PATCH 2436/2556] cpufreq: interactive: always limit initial speed bump to hispeed First bump speed up to hispeed_freq whenever the current speed is below hispeed_freq, instead of only when the current speed is the minimum speed. The previous code made it too difficult to use hispeed_freq as a common intermediate speed on systems that frequently run at speeds between minimum and hispeed_freq. Change-Id: I04ec30bafabf5741e267ff289209b8c2d846824b Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index dc57dfb58ca9c..98da0753e3fba 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -178,7 +178,8 @@ static void cpufreq_interactive_timer(unsigned long data) cpu_load = load_since_change; if (cpu_load >= go_hispeed_load || boost_val) { - if (pcpu->target_freq <= pcpu->policy->min) { + if (pcpu->target_freq < hispeed_freq && + hispeed_freq < pcpu->policy->max) { new_freq = hispeed_freq; } else { new_freq = pcpu->policy->max * cpu_load / 100; From f9ace73ad83bc0eef484a2c725248357667e2cde Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 3 Oct 2012 00:39:56 -0700 Subject: [PATCH 2437/2556] cpufreq: interactive: run at fraction of hispeed_freq when load is low When load is below go_hispeed_load, apply the percentage of CPU load to a max frequency of hispeed_freq instead of the max speed. This avoids jumping too quickly to hispeed_freq when it is a relatively low percentage of max speed. This also allows go_hispeed_load to be set to a high percentage relative to hispeed_freq (as a percentage of max speed, again useful when hispeed_freq is a low fraction of max speed), to cap larger loads at hispeed_freq. For example, a load of 60% will typically move to 60% of hispeed_freq, not 60% of max speed. This causes the governor to apply two different speed caps, depending on whether load is below or above go_hispeed_load. Also fix the type of hispeed_freq, which was u64, to match other speed data types (and avoid overhead and allow division). Change-Id: Ie2d0668be161c074aaad77db2037505431457b3a Signed-off-by: Todd Poynor --- drivers/cpufreq/cpufreq_interactive.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 98da0753e3fba..e3ce3b7580697 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -61,7 +61,7 @@ static cpumask_t speedchange_cpumask; static spinlock_t speedchange_cpumask_lock; /* Hi speed to bump to from lo speed when load burst (default max) */ -static u64 hispeed_freq; +static unsigned int hispeed_freq; /* Go to hi speed when CPU load at or above this value. */ #define DEFAULT_GO_HISPEED_LOAD 85 @@ -199,7 +199,7 @@ static void cpufreq_interactive_timer(unsigned long data) } } } else { - new_freq = pcpu->policy->max * cpu_load / 100; + new_freq = hispeed_freq * cpu_load / 100; } if (new_freq <= hispeed_freq) @@ -467,7 +467,7 @@ static void cpufreq_interactive_boost(void) static ssize_t show_hispeed_freq(struct kobject *kobj, struct attribute *attr, char *buf) { - return sprintf(buf, "%llu\n", hispeed_freq); + return sprintf(buf, "%u\n", hispeed_freq); } static ssize_t store_hispeed_freq(struct kobject *kobj, @@ -475,9 +475,9 @@ static ssize_t store_hispeed_freq(struct kobject *kobj, size_t count) { int ret; - u64 val; + long unsigned int val; - ret = strict_strtoull(buf, 0, &val); + ret = strict_strtoul(buf, 0, &val); if (ret < 0) return ret; hispeed_freq = val; From 130913ce0c1cbf0fdfdbba12517f52be9e054f7c Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 11 Sep 2012 20:13:10 -0700 Subject: [PATCH 2438/2556] drivers: cpufreq: Send a uevent when governor changes * Useful so userspace tools can reconfigure. Change-Id: Ib423910b8b9ac791ebe81a75bf399f58272f64f2 --- drivers/cpufreq/cpufreq.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 49d4eba8bf83c..ee828abaf7a6d 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -474,6 +474,9 @@ static ssize_t store_scaling_governor(struct cpufreq_policy *policy, unsigned int ret = -EINVAL; char str_governor[16]; struct cpufreq_policy new_policy; + char *envp[3]; + char buf1[64]; + char buf2[64]; ret = cpufreq_get_policy(&new_policy, policy->cpu); if (ret) @@ -496,6 +499,13 @@ static ssize_t store_scaling_governor(struct cpufreq_policy *policy, sysfs_notify(&policy->kobj, NULL, "scaling_governor"); + snprintf(buf1, sizeof(buf1), "GOV=%s", policy->governor->name); + snprintf(buf2, sizeof(buf2), "CPU=%u", policy->cpu); + envp[0] = buf1; + envp[1] = buf2; + envp[2] = NULL; + kobject_uevent_env(cpufreq_global_kobject, KOBJ_ADD, envp); + if (ret) return ret; else From ecf5cdb4f060f7ca7fffea6b45eb678ae62fca9a Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 28 Oct 2012 19:11:03 -0500 Subject: [PATCH 2439/2556] configs: remove extra govs Change-Id: Id7d42725b303ffe36f489e4b061ab133266f4c60 --- arch/arm/configs/evervolv_bravo_defconfig | 10 +++++----- arch/arm/configs/evervolv_incrediblec_defconfig | 10 +++++----- arch/arm/configs/evervolv_mahimahi_defconfig | 10 +++++----- arch/arm/configs/evervolv_supersonic_defconfig | 10 +++++----- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index f5d4486b1b35e..54a936e9eb371 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -478,13 +478,13 @@ CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set -CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -CONFIG_CPU_FREQ_GOV_POWERSAVE=y -CONFIG_CPU_FREQ_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y -CONFIG_CPU_FREQ_GOV_SMARTASS2=y -CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_CPU_FREQ_GOV_SMARTASS2 is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set # CONFIG_CPU_IDLE is not set CONFIG_CPU_FREQ_MSM=y diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index da2d73ddc562b..d201219599677 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -478,13 +478,13 @@ CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set -CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -CONFIG_CPU_FREQ_GOV_POWERSAVE=y -CONFIG_CPU_FREQ_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y -CONFIG_CPU_FREQ_GOV_SMARTASS2=y -CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_CPU_FREQ_GOV_SMARTASS2 is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set # CONFIG_CPU_IDLE is not set CONFIG_CPU_FREQ_MSM=y diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index 41322d6abae40..d667d9ed73d9b 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -478,13 +478,13 @@ CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set -CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -CONFIG_CPU_FREQ_GOV_POWERSAVE=y -CONFIG_CPU_FREQ_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y -CONFIG_CPU_FREQ_GOV_SMARTASS2=y -CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_CPU_FREQ_GOV_SMARTASS2 is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set # CONFIG_CPU_IDLE is not set CONFIG_CPU_FREQ_MSM=y diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index cc2fc897aaab0..c4692860313c5 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -483,13 +483,13 @@ CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set -CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -CONFIG_CPU_FREQ_GOV_POWERSAVE=y -CONFIG_CPU_FREQ_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y -CONFIG_CPU_FREQ_GOV_SMARTASS2=y -CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_CPU_FREQ_GOV_SMARTASS2 is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set # CONFIG_CPU_IDLE is not set CONFIG_CPU_FREQ_MSM=y From 6fdac7cd394afcf015aea7e5a45fadfd915d39e3 Mon Sep 17 00:00:00 2001 From: Narayanan Gopalakrishnan Date: Thu, 4 Oct 2012 03:09:29 -0500 Subject: [PATCH 2440/2556] cpufreq: interactive: add boostpulse duration This makes up for the portions missing that would've been written by Narayanan Gopalakrishnan but they were missing (duration, and boost time) and also adds the ability to set duration (which was originally created by cyanogen) Change-Id: I5428d4edb9e7c903435c368d3539f353234b083a --- drivers/cpufreq/cpufreq_interactive.c | 28 ++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index e3ce3b7580697..865e15a51ef3c 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -63,6 +63,14 @@ static spinlock_t speedchange_cpumask_lock; /* Hi speed to bump to from lo speed when load burst (default max) */ static unsigned int hispeed_freq; +/* When the boostpulse was activated */ +static u64 boostpulse_boosted_time; + +/* How long the boostpulse will remain active */ +#define DEFAULT_BOOSTPULSE_DURATION 500000 +#define MAX_BOOSTPULSE_DURATION 5000000 +static int boostpulse_duration; + /* Go to hi speed when CPU load at or above this value. */ #define DEFAULT_GO_HISPEED_LOAD 85 static unsigned long go_hispeed_load; @@ -119,6 +127,7 @@ static void cpufreq_interactive_timer(unsigned long data) unsigned int new_freq; unsigned int index; unsigned long flags; + u64 now = ktime_to_us(ktime_get()); smp_rmb(); @@ -177,7 +186,14 @@ static void cpufreq_interactive_timer(unsigned long data) if (load_since_change > cpu_load) cpu_load = load_since_change; - if (cpu_load >= go_hispeed_load || boost_val) { + if (boostpulse_boosted_time && + now > boostpulse_boosted_time + boostpulse_duration) { + /* Disable the boostpulse. */ + boostpulse_boosted_time = 0; + boostpulse_duration = 0; + } + + if (cpu_load >= go_hispeed_load || boost_val || boostpulse_boosted_time) { if (pcpu->target_freq < hispeed_freq && hispeed_freq < pcpu->policy->max) { new_freq = hispeed_freq; @@ -610,12 +626,18 @@ static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { int ret; - unsigned long val; + unsigned int val; - ret = kstrtoul(buf, 0, &val); + ret = sscanf(buf, "%u", &val); if (ret < 0) return ret; + boostpulse_boosted_time = ktime_to_us(ktime_get()); + if (val > 1 && val <= MAX_BOOSTPULSE_DURATION) + boostpulse_duration = val; + else + boostpulse_duration = DEFAULT_BOOSTPULSE_DURATION; + trace_cpufreq_interactive_boost("pulse"); cpufreq_interactive_boost(); return count; From d35d44ca9912ab63a7a2d22c2a35a098045ce1df Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 28 Oct 2012 19:38:16 -0500 Subject: [PATCH 2441/2556] cpufreq: interactive: if boost duration greater than max use max instead of default Change-Id: Ib1b206da6ebafc78740f3610c3583052175aff3d --- drivers/cpufreq/cpufreq_interactive.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 865e15a51ef3c..110d3b38e2c8b 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -633,7 +633,11 @@ static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, return ret; boostpulse_boosted_time = ktime_to_us(ktime_get()); - if (val > 1 && val <= MAX_BOOSTPULSE_DURATION) + + if (val > MAX_BOOSTPULSE_DURATION) + val = MAX_BOOSTPULSE_DURATION; + + if (val > 1) boostpulse_duration = val; else boostpulse_duration = DEFAULT_BOOSTPULSE_DURATION; From 46666ae04048a0cc3227b0fdca3f7c553a0bc8d8 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sun, 28 Oct 2012 19:41:31 -0500 Subject: [PATCH 2442/2556] cpufreq: interactive: dont boost for less than default duration Change-Id: I8a4294b59da883130d4434e44b52d74391b7bb70 --- drivers/cpufreq/cpufreq_interactive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 110d3b38e2c8b..2f4660f655dee 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -637,7 +637,7 @@ static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, if (val > MAX_BOOSTPULSE_DURATION) val = MAX_BOOSTPULSE_DURATION; - if (val > 1) + if (val > DEFAULT_BOOSTPULSE_DURATION) boostpulse_duration = val; else boostpulse_duration = DEFAULT_BOOSTPULSE_DURATION; From 429e18925841f4587c585b188e57a785fe54a038 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Fri, 30 Nov 2012 19:00:46 -0600 Subject: [PATCH 2443/2556] fb: fix reserved usage for 4.2 Change-Id: Ib2c6739e31b47c9656b9b2e7296fa8ce0ddc9ab1 --- drivers/video/msm/msm_fb.c | 2 +- include/linux/fb.h | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index 0607030a3bd12..14d7b8922f8e1 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -1003,7 +1003,7 @@ static void setup_fb_info(struct msmfb_info *msmfb) fb_info->var.yres_virtual = msmfb->yres * 2; fb_info->var.bits_per_pixel = BITS_PER_PIXEL_DEF; fb_info->var.accel_flags = 0; - fb_info->var.reserved[4] = 60; + fb_info->var.reserved[3] = 60; fb_info->var.yoffset = 0; diff --git a/include/linux/fb.h b/include/linux/fb.h index 68ba85a00c063..c7df155a97419 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -169,7 +169,8 @@ struct fb_fix_screeninfo { __u32 mmio_len; /* Length of Memory Mapped I/O */ __u32 accel; /* Indicate to driver which */ /* specific chip/card we have */ - __u16 reserved[3]; /* Reserved for future compatibility */ + __u16 capabilities; /* see FB_CAP_* */ + __u16 reserved[2]; /* Reserved for future compatibility */ }; /* Interpretation of offset for color fields: All offsets are from the right, @@ -271,7 +272,8 @@ struct fb_var_screeninfo { __u32 sync; /* see FB_SYNC_* */ __u32 vmode; /* see FB_VMODE_* */ __u32 rotate; /* angle we rotate counter clockwise */ - __u32 reserved[5]; /* Reserved for future compatibility */ + __u32 colorspace; /* colorspace for FOURCC-based modes */ + __u32 reserved[4]; /* Reserved for future compatibility */ }; struct fb_cmap { From 695d9107cf34aff2912319e6b0ffcc337e2c0e77 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:13 +0200 Subject: [PATCH 2444/2556] HID: uhid: introduce user-space I/O driver support for HID This adds a dummy driver that will support user-space I/O drivers for the HID subsystem. This allows to write transport-level drivers like USB-HID and Bluetooth-HID in user-space. Low-Energy Bluetooth needs this to feed HID data that is parsed in user-space back into the kernel. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 21 +++++++++++ drivers/hid/Makefile | 1 + drivers/hid/uhid.c | 88 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 drivers/hid/uhid.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index ae445b1dffaa2..6ed5e4fc200c3 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -50,6 +50,27 @@ config HIDRAW If unsure, say Y. +config UHID + tristate "User-space I/O driver support for HID subsystem" + depends on HID + default n + ---help--- + Say Y here if you want to provide HID I/O Drivers from user-space. + This allows to write I/O drivers in user-space and feed the data from + the device into the kernel. The kernel parses the HID reports, loads the + corresponding HID Device Driver or provides input devices on top of your + user-space device. + + This driver cannot be used to parse HID-reports in user-space and write + special HID-drivers. You should use hidraw for that. + Instead, this driver allows to write the transport-layer driver in + user-space like USB-HID and Bluetooth-HID do in kernel-space. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called uhid. + source "drivers/hid/usbhid/Kconfig" menu "Special HID drivers" diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 13e624867d193..ec4e38d69587f 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -8,6 +8,7 @@ ifdef CONFIG_DEBUG_FS endif obj-$(CONFIG_HID) += hid.o +obj-$(CONFIG_UHID) += uhid.o hid-$(CONFIG_HIDRAW) += hidraw.o diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c new file mode 100644 index 0000000000000..5b02d6cb0e60f --- /dev/null +++ b/drivers/hid/uhid.c @@ -0,0 +1,88 @@ +/* + * User-space I/O driver support for HID subsystem + * Copyright (c) 2012 David Herrmann + */ + +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UHID_NAME "uhid" + +static struct miscdevice uhid_misc; + +static int uhid_char_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int uhid_char_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t uhid_char_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + return 0; +} + +static ssize_t uhid_char_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + return 0; +} + +static unsigned int uhid_char_poll(struct file *file, poll_table *wait) +{ + return 0; +} + +static const struct file_operations uhid_fops = { + .owner = THIS_MODULE, + .open = uhid_char_open, + .release = uhid_char_release, + .read = uhid_char_read, + .write = uhid_char_write, + .poll = uhid_char_poll, + .llseek = no_llseek, +}; + +static struct miscdevice uhid_misc = { + .fops = &uhid_fops, + .minor = MISC_DYNAMIC_MINOR, + .name = UHID_NAME, +}; + +static int __init uhid_init(void) +{ + return misc_register(&uhid_misc); +} + +static void __exit uhid_exit(void) +{ + misc_deregister(&uhid_misc); +} + +module_init(uhid_init); +module_exit(uhid_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Herrmann "); +MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem"); From dc11307e1fb57e5c8ff403642b92bbf872f5d6ac Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:14 +0200 Subject: [PATCH 2445/2556] HID: uhid: add internal message buffer When receiving messages from the HID subsystem, we need to process them and store them in an internal buffer so user-space can read() on the char device to retrieve the messages. This adds a static buffer for 32 messages to each uhid device. Each message is dynamically allocated so the uhid_device structure does not get too big. uhid_queue() adds a message to the buffer. If the buffer is full, the message is discarded. uhid_queue_event() is an helper for messages without payload. This also adds a public header: uhid.h. It contains the declarations for the user-space API. It is built around "struct uhid_event" which contains a type field which specifies the event type and each event can then add a variable-length payload. For now, there is only a dummy event but later patches will add new event types and payloads. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 65 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/Kbuild | 1 + include/linux/uhid.h | 33 ++++++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 include/linux/uhid.h diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 5b02d6cb0e60f..05ef4b05a63e0 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -25,16 +25,81 @@ #include #define UHID_NAME "uhid" +#define UHID_BUFSIZE 32 + +struct uhid_device { + struct hid_device *hid; + + wait_queue_head_t waitq; + spinlock_t qlock; + __u8 head; + __u8 tail; + struct uhid_event *outq[UHID_BUFSIZE]; +}; static struct miscdevice uhid_misc; +static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev) +{ + __u8 newhead; + + newhead = (uhid->head + 1) % UHID_BUFSIZE; + + if (newhead != uhid->tail) { + uhid->outq[uhid->head] = ev; + uhid->head = newhead; + wake_up_interruptible(&uhid->waitq); + } else { + hid_warn(uhid->hid, "Output queue is full\n"); + kfree(ev); + } +} + +static int uhid_queue_event(struct uhid_device *uhid, __u32 event) +{ + unsigned long flags; + struct uhid_event *ev; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = event; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { + struct uhid_device *uhid; + + uhid = kzalloc(sizeof(*uhid), GFP_KERNEL); + if (!uhid) + return -ENOMEM; + + spin_lock_init(&uhid->qlock); + init_waitqueue_head(&uhid->waitq); + + file->private_data = uhid; + nonseekable_open(inode, file); + return 0; } static int uhid_char_release(struct inode *inode, struct file *file) { + struct uhid_device *uhid = file->private_data; + unsigned int i; + + for (i = 0; i < UHID_BUFSIZE; ++i) + kfree(uhid->outq[i]); + + kfree(uhid); + return 0; } diff --git a/include/linux/Kbuild b/include/linux/Kbuild index d02cb077d5df5..be53335461304 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -364,6 +364,7 @@ header-y += tty.h header-y += types.h header-y += udf_fs_i.h header-y += udp.h +header-y += uhid.h header-y += uinput.h header-y += uio.h header-y += ultrasound.h diff --git a/include/linux/uhid.h b/include/linux/uhid.h new file mode 100644 index 0000000000000..16b786a2b18fc --- /dev/null +++ b/include/linux/uhid.h @@ -0,0 +1,33 @@ +#ifndef __UHID_H_ +#define __UHID_H_ + +/* + * User-space I/O driver support for HID subsystem + * Copyright (c) 2012 David Herrmann + */ + +/* + * 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. + */ + +/* + * Public header for user-space communication. We try to keep every structure + * aligned but to be safe we also use __attribute__((__packed__)). Therefore, + * the communication should be ABI compatible even between architectures. + */ + +#include +#include + +enum uhid_event_type { + UHID_DUMMY, +}; + +struct uhid_event { + __u32 type; +} __attribute__((__packed__)); + +#endif /* __UHID_H_ */ From 87f3eab165a80bd16193f16b95edd502789f2cc2 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:15 +0200 Subject: [PATCH 2446/2556] HID: uhid: allow poll()'ing on uhid devices As long as the internal buffer is not empty, we return POLLIN to user-space. uhid->head and uhid->tail are no atomics so the comparison may return inexact results. However, this doesn't matter here as user-space would need to poll() in two threads simultaneously to trigger this. And in this case it doesn't matter if a cached result is returned or the exact new result as user-space does not know which thread returns first from poll() and the following read(). So it is safe to compare the values without locking. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 05ef4b05a63e0..b1a477f8260c6 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -117,6 +117,13 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, static unsigned int uhid_char_poll(struct file *file, poll_table *wait) { + struct uhid_device *uhid = file->private_data; + + poll_wait(file, &uhid->waitq, wait); + + if (uhid->head != uhid->tail) + return POLLIN | POLLRDNORM; + return 0; } From 6f13ee6d13a0750dcda931daa52f3294ac8cc649 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:16 +0200 Subject: [PATCH 2447/2556] HID: uhid: implement read() on uhid devices User-space can use read() to get a single event from uhid devices. read() does never return multiple events. This allows us to extend the event structure and still keep backwards compatibility. If user-space wants to get multiple events in one syscall, they should use the readv()/writev() syscalls which are supported by uhid. This introduces a new lock which helps us synchronizing simultaneous reads from user-space. We also correctly return -EINVAL/-EFAULT only on errors and retry the read() when some other thread captured the event faster than we did. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index b1a477f8260c6..93860826d629d 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -28,6 +28,7 @@ #define UHID_BUFSIZE 32 struct uhid_device { + struct mutex devlock; struct hid_device *hid; wait_queue_head_t waitq; @@ -81,6 +82,7 @@ static int uhid_char_open(struct inode *inode, struct file *file) if (!uhid) return -ENOMEM; + mutex_init(&uhid->devlock); spin_lock_init(&uhid->qlock); init_waitqueue_head(&uhid->waitq); @@ -106,7 +108,49 @@ static int uhid_char_release(struct inode *inode, struct file *file) static ssize_t uhid_char_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { - return 0; + struct uhid_device *uhid = file->private_data; + int ret; + unsigned long flags; + size_t len; + + /* they need at least the "type" member of uhid_event */ + if (count < sizeof(__u32)) + return -EINVAL; + +try_again: + if (file->f_flags & O_NONBLOCK) { + if (uhid->head == uhid->tail) + return -EAGAIN; + } else { + ret = wait_event_interruptible(uhid->waitq, + uhid->head != uhid->tail); + if (ret) + return ret; + } + + ret = mutex_lock_interruptible(&uhid->devlock); + if (ret) + return ret; + + if (uhid->head == uhid->tail) { + mutex_unlock(&uhid->devlock); + goto try_again; + } else { + len = min(count, sizeof(**uhid->outq)); + if (copy_to_user(buffer, &uhid->outq[uhid->tail], len)) { + ret = -EFAULT; + } else { + kfree(uhid->outq[uhid->tail]); + uhid->outq[uhid->tail] = NULL; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid->tail = (uhid->tail + 1) % UHID_BUFSIZE; + spin_unlock_irqrestore(&uhid->qlock, flags); + } + } + + mutex_unlock(&uhid->devlock); + return ret ? ret : len; } static ssize_t uhid_char_write(struct file *file, const char __user *buffer, From ea8eb17277bea711906ec59e1baf1fa5e78e6c50 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:17 +0200 Subject: [PATCH 2448/2556] HID: uhid: implement write() on uhid devices Similar to read() you can only write() a single event with one call to an uhid device. To write multiple events use writev() which is supported by uhid. We currently always return -EOPNOTSUPP but other events will be added in later patches. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 93860826d629d..31e8379cfd159 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -30,6 +30,7 @@ struct uhid_device { struct mutex devlock; struct hid_device *hid; + struct uhid_event input_buf; wait_queue_head_t waitq; spinlock_t qlock; @@ -156,7 +157,35 @@ static ssize_t uhid_char_read(struct file *file, char __user *buffer, static ssize_t uhid_char_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { - return 0; + struct uhid_device *uhid = file->private_data; + int ret; + size_t len; + + /* we need at least the "type" member of uhid_event */ + if (count < sizeof(__u32)) + return -EINVAL; + + ret = mutex_lock_interruptible(&uhid->devlock); + if (ret) + return ret; + + memset(&uhid->input_buf, 0, sizeof(uhid->input_buf)); + len = min(count, sizeof(uhid->input_buf)); + if (copy_from_user(&uhid->input_buf, buffer, len)) { + ret = -EFAULT; + goto unlock; + } + + switch (uhid->input_buf.type) { + default: + ret = -EOPNOTSUPP; + } + +unlock: + mutex_unlock(&uhid->devlock); + + /* return "count" not "len" to not confuse the caller */ + return ret ? ret : count; } static unsigned int uhid_char_poll(struct file *file, poll_table *wait) From 072b9de62b360065bec565bdcf1073a833889f8c Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:18 +0200 Subject: [PATCH 2449/2556] HID: uhid: add UHID_CREATE and UHID_DESTROY events UHID_CREATE and UHID_DESTROY are used to create and destroy a device on an open uhid char-device. Internally, we allocate and register an HID device with the HID core and immediately start the device. From now on events may be received or sent to the device. The UHID_CREATE event has a payload similar to the data used by Bluetooth-HIDP when creating a new connection. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 144 +++++++++++++++++++++++++++++++++++++++++++ include/linux/uhid.h | 21 ++++++- 2 files changed, 164 insertions(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 31e8379cfd159..61ee7cc32ccfc 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -29,6 +29,11 @@ struct uhid_device { struct mutex devlock; + bool running; + + __u8 *rd_data; + uint rd_size; + struct hid_device *hid; struct uhid_event input_buf; @@ -75,6 +80,136 @@ static int uhid_queue_event(struct uhid_device *uhid, __u32 event) return 0; } +static int uhid_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void uhid_hid_stop(struct hid_device *hid) +{ +} + +static int uhid_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void uhid_hid_close(struct hid_device *hid) +{ +} + +static int uhid_hid_input(struct input_dev *input, unsigned int type, + unsigned int code, int value) +{ + return 0; +} + +static int uhid_hid_parse(struct hid_device *hid) +{ + return 0; +} + +static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, + __u8 *buf, size_t count, unsigned char rtype) +{ + return 0; +} + +static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, + unsigned char report_type) +{ + return 0; +} + +static struct hid_ll_driver uhid_hid_driver = { + .start = uhid_hid_start, + .stop = uhid_hid_stop, + .open = uhid_hid_open, + .close = uhid_hid_close, + .hidinput_input_event = uhid_hid_input, + .parse = uhid_hid_parse, +}; + +static int uhid_dev_create(struct uhid_device *uhid, + const struct uhid_event *ev) +{ + struct hid_device *hid; + int ret; + + if (uhid->running) + return -EALREADY; + + uhid->rd_size = ev->u.create.rd_size; + if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE) + return -EINVAL; + + uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL); + if (!uhid->rd_data) + return -ENOMEM; + + if (copy_from_user(uhid->rd_data, ev->u.create.rd_data, + uhid->rd_size)) { + ret = -EFAULT; + goto err_free; + } + + hid = hid_allocate_device(); + if (IS_ERR(hid)) { + ret = PTR_ERR(hid); + goto err_free; + } + + strncpy(hid->name, ev->u.create.name, 127); + hid->name[127] = 0; + strncpy(hid->phys, ev->u.create.phys, 63); + hid->phys[63] = 0; + strncpy(hid->uniq, ev->u.create.uniq, 63); + hid->uniq[63] = 0; + + hid->ll_driver = &uhid_hid_driver; + hid->hid_get_raw_report = uhid_hid_get_raw; + hid->hid_output_raw_report = uhid_hid_output_raw; + hid->bus = ev->u.create.bus; + hid->vendor = ev->u.create.vendor; + hid->product = ev->u.create.product; + hid->version = ev->u.create.version; + hid->country = ev->u.create.country; + hid->driver_data = uhid; + hid->dev.parent = uhid_misc.this_device; + + uhid->hid = hid; + uhid->running = true; + + ret = hid_add_device(hid); + if (ret) { + hid_err(hid, "Cannot register HID device\n"); + goto err_hid; + } + + return 0; + +err_hid: + hid_destroy_device(hid); + uhid->hid = NULL; + uhid->running = false; +err_free: + kfree(uhid->rd_data); + return ret; +} + +static int uhid_dev_destroy(struct uhid_device *uhid) +{ + if (!uhid->running) + return -EINVAL; + + uhid->running = false; + + hid_destroy_device(uhid->hid); + kfree(uhid->rd_data); + + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { struct uhid_device *uhid; @@ -86,6 +221,7 @@ static int uhid_char_open(struct inode *inode, struct file *file) mutex_init(&uhid->devlock); spin_lock_init(&uhid->qlock); init_waitqueue_head(&uhid->waitq); + uhid->running = false; file->private_data = uhid; nonseekable_open(inode, file); @@ -98,6 +234,8 @@ static int uhid_char_release(struct inode *inode, struct file *file) struct uhid_device *uhid = file->private_data; unsigned int i; + uhid_dev_destroy(uhid); + for (i = 0; i < UHID_BUFSIZE; ++i) kfree(uhid->outq[i]); @@ -177,6 +315,12 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, } switch (uhid->input_buf.type) { + case UHID_CREATE: + ret = uhid_dev_create(uhid, &uhid->input_buf); + break; + case UHID_DESTROY: + ret = uhid_dev_destroy(uhid); + break; default: ret = -EOPNOTSUPP; } diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 16b786a2b18fc..8a493e604a775 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -23,11 +23,30 @@ #include enum uhid_event_type { - UHID_DUMMY, + UHID_CREATE, + UHID_DESTROY, }; +struct uhid_create_req { + __u8 name[128]; + __u8 phys[64]; + __u8 uniq[64]; + __u8 __user *rd_data; + __u16 rd_size; + + __u16 bus; + __u32 vendor; + __u32 product; + __u32 version; + __u32 country; +} __attribute__((__packed__)); + struct uhid_event { __u32 type; + + union { + struct uhid_create_req create; + } u; } __attribute__((__packed__)); #endif /* __UHID_H_ */ From 0f122908ab61f66a46c1840572a3ca802cde5b7b Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:19 +0200 Subject: [PATCH 2450/2556] HID: uhid: allow feeding input data into uhid devices This adds a new event type UHID_INPUT which allows user-space to feed raw HID reports into the HID subsystem. We copy the data into kernel memory and directly feed it into the HID core. There is no error handling of the events couldn't be parsed so user-space should consider all events successfull unless read() returns an error. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 14 ++++++++++++++ include/linux/uhid.h | 9 +++++++++ 2 files changed, 23 insertions(+) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 61ee7cc32ccfc..3d1ebda122e59 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -210,6 +210,17 @@ static int uhid_dev_destroy(struct uhid_device *uhid) return 0; } +static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) +{ + if (!uhid->running) + return -EINVAL; + + hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data, + min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0); + + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { struct uhid_device *uhid; @@ -321,6 +332,9 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, case UHID_DESTROY: ret = uhid_dev_destroy(uhid); break; + case UHID_INPUT: + ret = uhid_dev_input(uhid, &uhid->input_buf); + break; default: ret = -EOPNOTSUPP; } diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 8a493e604a775..6eb42bea86a25 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -25,6 +25,7 @@ enum uhid_event_type { UHID_CREATE, UHID_DESTROY, + UHID_INPUT, }; struct uhid_create_req { @@ -41,11 +42,19 @@ struct uhid_create_req { __u32 country; } __attribute__((__packed__)); +#define UHID_DATA_MAX 4096 + +struct uhid_input_req { + __u8 data[UHID_DATA_MAX]; + __u16 size; +} __attribute__((__packed__)); + struct uhid_event { __u32 type; union { struct uhid_create_req create; + struct uhid_input_req input; } u; } __attribute__((__packed__)); From f528470bbbfcac5e21625d2e6ad10eb5acfe9beb Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:20 +0200 Subject: [PATCH 2451/2556] HID: uhid: forward hid report-descriptor to hid core When the uhid_hid_parse callback is called we simply forward it to hid_parse_report() with the data that we got in the UHID_CREATE event. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 3d1ebda122e59..0d011db30a465 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -106,7 +106,9 @@ static int uhid_hid_input(struct input_dev *input, unsigned int type, static int uhid_hid_parse(struct hid_device *hid) { - return 0; + struct uhid_device *uhid = hid->driver_data; + + return hid_parse_report(hid, uhid->rd_data, uhid->rd_size); } static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, From 2ee099efa974667a70fd76ca516f56b540e28cbd Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:21 +0200 Subject: [PATCH 2452/2556] HID: uhid: add UHID_START and UHID_STOP events We send UHID_START and UHID_STOP events to user-space when the HID core starts/stops the device. This notifies user-space about driver readiness and data-I/O can start now. This directly forwards the callbacks from hid-core to user-space. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 8 +++++++- include/linux/uhid.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 0d011db30a465..2e7f3a0239753 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -82,11 +82,17 @@ static int uhid_queue_event(struct uhid_device *uhid, __u32 event) static int uhid_hid_start(struct hid_device *hid) { - return 0; + struct uhid_device *uhid = hid->driver_data; + + return uhid_queue_event(uhid, UHID_START); } static void uhid_hid_stop(struct hid_device *hid) { + struct uhid_device *uhid = hid->driver_data; + + hid->claimed = 0; + uhid_queue_event(uhid, UHID_STOP); } static int uhid_hid_open(struct hid_device *hid) diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 6eb42bea86a25..f8ce6f7571d85 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -25,6 +25,8 @@ enum uhid_event_type { UHID_CREATE, UHID_DESTROY, + UHID_START, + UHID_STOP, UHID_INPUT, }; From a6763a055ae70583750ddb3704b278df3f53ccb0 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:22 +0200 Subject: [PATCH 2453/2556] HID: uhid: forward open/close events to user-space HID core notifies us with *_open/*_close callbacks when there is an actual user of our device. We forward these to user-space so they can react on this. This allows user-space to skip I/O unless they receive an OPEN event. When they receive a CLOSE event they can stop I/O again to save energy. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 7 ++++++- include/linux/uhid.h | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 2e7f3a0239753..0226ba3f53070 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -97,11 +97,16 @@ static void uhid_hid_stop(struct hid_device *hid) static int uhid_hid_open(struct hid_device *hid) { - return 0; + struct uhid_device *uhid = hid->driver_data; + + return uhid_queue_event(uhid, UHID_OPEN); } static void uhid_hid_close(struct hid_device *hid) { + struct uhid_device *uhid = hid->driver_data; + + uhid_queue_event(uhid, UHID_CLOSE); } static int uhid_hid_input(struct input_dev *input, unsigned int type, diff --git a/include/linux/uhid.h b/include/linux/uhid.h index f8ce6f7571d85..351650b7a0f69 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -27,6 +27,8 @@ enum uhid_event_type { UHID_DESTROY, UHID_START, UHID_STOP, + UHID_OPEN, + UHID_CLOSE, UHID_INPUT, }; From 10dd1107af7f7ca723d335cb83dd0726bde6cff4 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:23 +0200 Subject: [PATCH 2454/2556] HID: uhid: forward output request to user-space If the hid-driver wants to send standardized data to the device it uses a linux input_event. We forward this to the user-space transport-level driver so they can perform the requested action on the device. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 18 ++++++++++++++++++ include/linux/uhid.h | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 0226ba3f53070..4dd693e1c8b8a 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -112,6 +112,24 @@ static void uhid_hid_close(struct hid_device *hid) static int uhid_hid_input(struct input_dev *input, unsigned int type, unsigned int code, int value) { + struct hid_device *hid = input_get_drvdata(input); + struct uhid_device *uhid = hid->driver_data; + unsigned long flags; + struct uhid_event *ev; + + ev = kzalloc(sizeof(*ev), GFP_ATOMIC); + if (!ev) + return -ENOMEM; + + ev->type = UHID_OUTPUT_EV; + ev->u.output_ev.type = type; + ev->u.output_ev.code = code; + ev->u.output_ev.value = value; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + return 0; } diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 351650b7a0f69..3fa4849210103 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -29,6 +29,7 @@ enum uhid_event_type { UHID_STOP, UHID_OPEN, UHID_CLOSE, + UHID_OUTPUT_EV, UHID_INPUT, }; @@ -53,12 +54,19 @@ struct uhid_input_req { __u16 size; } __attribute__((__packed__)); +struct uhid_output_ev_req { + __u16 type; + __u16 code; + __s32 value; +} __attribute__((__packed__)); + struct uhid_event { __u32 type; union { struct uhid_create_req create; struct uhid_input_req input; + struct uhid_output_ev_req output_ev; } u; } __attribute__((__packed__)); From cc4365eb9b54a767a4fc3c049c5116a0c5e40283 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:24 +0200 Subject: [PATCH 2455/2556] HID: uhid: forward raw output reports to user-space Some drivers that use non-standard HID features require raw output reports sent to the device. We now forward these requests directly to user-space so the transport-level driver can correctly send it to the device or handle it correspondingly. There is no way to signal back whether the transmission was successful, moreover, there might be lots of messages coming out from the driver flushing the output-queue. However, there is currently no driver that causes this so we are safe. If some drivers need to transmit lots of data this way, we need a method to synchronize this and can implement another UHID_OUTPUT_SYNC event. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 34 +++++++++++++++++++++++++++++++++- include/linux/uhid.h | 14 ++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 4dd693e1c8b8a..421c492dc8246 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -149,7 +149,39 @@ static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, unsigned char report_type) { - return 0; + struct uhid_device *uhid = hid->driver_data; + __u8 rtype; + unsigned long flags; + struct uhid_event *ev; + + switch (report_type) { + case HID_FEATURE_REPORT: + rtype = UHID_FEATURE_REPORT; + break; + case HID_OUTPUT_REPORT: + rtype = UHID_OUTPUT_REPORT; + break; + default: + return -EINVAL; + } + + if (count < 1 || count > UHID_DATA_MAX) + return -EINVAL; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = UHID_OUTPUT; + ev->u.output.size = count; + ev->u.output.rtype = rtype; + memcpy(ev->u.output.data, buf, count); + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return count; } static struct hid_ll_driver uhid_hid_driver = { diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 3fa4849210103..2c972550a6245 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -29,6 +29,7 @@ enum uhid_event_type { UHID_STOP, UHID_OPEN, UHID_CLOSE, + UHID_OUTPUT, UHID_OUTPUT_EV, UHID_INPUT, }; @@ -49,11 +50,23 @@ struct uhid_create_req { #define UHID_DATA_MAX 4096 +enum uhid_report_type { + UHID_FEATURE_REPORT, + UHID_OUTPUT_REPORT, + UHID_INPUT_REPORT, +}; + struct uhid_input_req { __u8 data[UHID_DATA_MAX]; __u16 size; } __attribute__((__packed__)); +struct uhid_output_req { + __u8 data[UHID_DATA_MAX]; + __u16 size; + __u8 rtype; +} __attribute__((__packed__)); + struct uhid_output_ev_req { __u16 type; __u16 code; @@ -66,6 +79,7 @@ struct uhid_event { union { struct uhid_create_req create; struct uhid_input_req input; + struct uhid_output_req output; struct uhid_output_ev_req output_ev; } u; } __attribute__((__packed__)); From b2d223dc0b7c9a6298058b2847f39135ece65a52 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:25 +0200 Subject: [PATCH 2456/2556] HID: uhid: implement feature requests HID standard allows sending a feature request to the device which is answered by an HID report. uhid implements this by sending a UHID_FEATURE event to user-space which then must answer with UHID_FEATURE_ANSWER. If it doesn't do this in a timely manner, the request is discarded silently. We serialize the feature requests, that is, there is always only a single active feature-request sent to user-space, other requests have to wait. HIDP and USB-HID do it the same way. Because we discard feature-requests silently, we must make sure to match a response to the corresponding request. We use sequence-IDs for this so user-space must copy the ID from the request into the answer. Feature-answers are ignored if they do not contain the same ID as the currently pending feature request. Internally, we must make sure that feature-requests are synchronized with UHID_DESTROY and close() events. We must not dead-lock when closing the HID device, either, so we have to use separate locks. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 120 ++++++++++++++++++++++++++++++++++++++++++- include/linux/uhid.h | 17 ++++++ 2 files changed, 136 insertions(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 421c492dc8246..ea560bfa033d2 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -42,6 +42,12 @@ struct uhid_device { __u8 head; __u8 tail; struct uhid_event *outq[UHID_BUFSIZE]; + + struct mutex report_lock; + wait_queue_head_t report_wait; + atomic_t report_done; + atomic_t report_id; + struct uhid_event report_buf; }; static struct miscdevice uhid_misc; @@ -143,7 +149,84 @@ static int uhid_hid_parse(struct hid_device *hid) static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, __u8 *buf, size_t count, unsigned char rtype) { - return 0; + struct uhid_device *uhid = hid->driver_data; + __u8 report_type; + struct uhid_event *ev; + unsigned long flags; + int ret; + size_t len; + struct uhid_feature_answer_req *req; + + if (!uhid->running) + return -EIO; + + switch (rtype) { + case HID_FEATURE_REPORT: + report_type = UHID_FEATURE_REPORT; + break; + case HID_OUTPUT_REPORT: + report_type = UHID_OUTPUT_REPORT; + break; + case HID_INPUT_REPORT: + report_type = UHID_INPUT_REPORT; + break; + default: + return -EINVAL; + } + + ret = mutex_lock_interruptible(&uhid->report_lock); + if (ret) + return ret; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) { + ret = -ENOMEM; + goto unlock; + } + + spin_lock_irqsave(&uhid->qlock, flags); + ev->type = UHID_FEATURE; + ev->u.feature.id = atomic_inc_return(&uhid->report_id); + ev->u.feature.rnum = rnum; + ev->u.feature.rtype = report_type; + + atomic_set(&uhid->report_done, 0); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + ret = wait_event_interruptible_timeout(uhid->report_wait, + atomic_read(&uhid->report_done), 5 * HZ); + + /* + * Make sure "uhid->running" is cleared on shutdown before + * "uhid->report_done" is set. + */ + smp_rmb(); + if (!ret || !uhid->running) { + ret = -EIO; + } else if (ret < 0) { + ret = -ERESTARTSYS; + } else { + spin_lock_irqsave(&uhid->qlock, flags); + req = &uhid->report_buf.u.feature_answer; + + if (req->err) { + ret = -EIO; + } else { + ret = 0; + len = min(count, + min_t(size_t, req->size, UHID_DATA_MAX)); + memcpy(buf, req->data, len); + } + + spin_unlock_irqrestore(&uhid->qlock, flags); + } + + atomic_set(&uhid->report_done, 1); + +unlock: + mutex_unlock(&uhid->report_lock); + return ret ? ret : len; } static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, @@ -265,7 +348,11 @@ static int uhid_dev_destroy(struct uhid_device *uhid) if (!uhid->running) return -EINVAL; + /* clear "running" before setting "report_done" */ uhid->running = false; + smp_wmb(); + atomic_set(&uhid->report_done, 1); + wake_up_interruptible(&uhid->report_wait); hid_destroy_device(uhid->hid); kfree(uhid->rd_data); @@ -284,6 +371,31 @@ static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) return 0; } +static int uhid_dev_feature_answer(struct uhid_device *uhid, + struct uhid_event *ev) +{ + unsigned long flags; + + if (!uhid->running) + return -EINVAL; + + spin_lock_irqsave(&uhid->qlock, flags); + + /* id for old report; drop it silently */ + if (atomic_read(&uhid->report_id) != ev->u.feature_answer.id) + goto unlock; + if (atomic_read(&uhid->report_done)) + goto unlock; + + memcpy(&uhid->report_buf, ev, sizeof(*ev)); + atomic_set(&uhid->report_done, 1); + wake_up_interruptible(&uhid->report_wait); + +unlock: + spin_unlock_irqrestore(&uhid->qlock, flags); + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { struct uhid_device *uhid; @@ -293,9 +405,12 @@ static int uhid_char_open(struct inode *inode, struct file *file) return -ENOMEM; mutex_init(&uhid->devlock); + mutex_init(&uhid->report_lock); spin_lock_init(&uhid->qlock); init_waitqueue_head(&uhid->waitq); + init_waitqueue_head(&uhid->report_wait); uhid->running = false; + atomic_set(&uhid->report_done, 1); file->private_data = uhid; nonseekable_open(inode, file); @@ -398,6 +513,9 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, case UHID_INPUT: ret = uhid_dev_input(uhid, &uhid->input_buf); break; + case UHID_FEATURE_ANSWER: + ret = uhid_dev_feature_answer(uhid, &uhid->input_buf); + break; default: ret = -EOPNOTSUPP; } diff --git a/include/linux/uhid.h b/include/linux/uhid.h index 2c972550a6245..9c6974f169661 100644 --- a/include/linux/uhid.h +++ b/include/linux/uhid.h @@ -32,6 +32,8 @@ enum uhid_event_type { UHID_OUTPUT, UHID_OUTPUT_EV, UHID_INPUT, + UHID_FEATURE, + UHID_FEATURE_ANSWER, }; struct uhid_create_req { @@ -73,6 +75,19 @@ struct uhid_output_ev_req { __s32 value; } __attribute__((__packed__)); +struct uhid_feature_req { + __u32 id; + __u8 rnum; + __u8 rtype; +} __attribute__((__packed__)); + +struct uhid_feature_answer_req { + __u32 id; + __u16 err; + __u16 size; + __u8 data[UHID_DATA_MAX]; +}; + struct uhid_event { __u32 type; @@ -81,6 +96,8 @@ struct uhid_event { struct uhid_input_req input; struct uhid_output_req output; struct uhid_output_ev_req output_ev; + struct uhid_feature_req feature; + struct uhid_feature_answer_req feature_answer; } u; } __attribute__((__packed__)); From efb14704ba4676fa55fa80a145fb42c4370d73fc Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:26 +0200 Subject: [PATCH 2457/2556] HID: uhid: add documentation This describes the protocol used by uhid for user-space applications. It describes the details like non-blocking I/O and readv/writev for multiple events per syscall. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- Documentation/hid/uhid.txt | 169 +++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 Documentation/hid/uhid.txt diff --git a/Documentation/hid/uhid.txt b/Documentation/hid/uhid.txt new file mode 100644 index 0000000000000..4627c4241ece6 --- /dev/null +++ b/Documentation/hid/uhid.txt @@ -0,0 +1,169 @@ + UHID - User-space I/O driver support for HID subsystem + ======================================================== + +The HID subsystem needs two kinds of drivers. In this document we call them: + + 1. The "HID I/O Driver" is the driver that performs raw data I/O to the + low-level device. Internally, they register an hid_ll_driver structure with + the HID core. They perform device setup, read raw data from the device and + push it into the HID subsystem and they provide a callback so the HID + subsystem can send data to the device. + + 2. The "HID Device Driver" is the driver that parses HID reports and reacts on + them. There are generic drivers like "generic-usb" and "generic-bluetooth" + which adhere to the HID specification and provide the standardizes features. + But there may be special drivers and quirks for each non-standard device out + there. Internally, they use the hid_driver structure. + +Historically, the USB stack was the first subsystem to provide an HID I/O +Driver. However, other standards like Bluetooth have adopted the HID specs and +may provide HID I/O Drivers, too. The UHID driver allows to implement HID I/O +Drivers in user-space and feed the data into the kernel HID-subsystem. + +This allows user-space to operate on the same level as USB-HID, Bluetooth-HID +and similar. It does not provide a way to write HID Device Drivers, though. Use +hidraw for this purpose. + +There is an example user-space application in ./samples/uhid/uhid-example.c + +The UHID API +------------ + +UHID is accessed through a character misc-device. The minor-number is allocated +dynamically so you need to rely on udev (or similar) to create the device node. +This is /dev/uhid by default. + +If a new device is detected by your HID I/O Driver and you want to register this +device with the HID subsystem, then you need to open /dev/uhid once for each +device you want to register. All further communication is done by read()'ing or +write()'ing "struct uhid_event" objects. Non-blocking operations are supported +by setting O_NONBLOCK. + +struct uhid_event { + __u32 type; + union { + struct uhid_create_req create; + struct uhid_data_req data; + ... + } u; +}; + +The "type" field contains the ID of the event. Depending on the ID different +payloads are sent. You must not split a single event across multiple read()'s or +multiple write()'s. A single event must always be sent as a whole. Furthermore, +only a single event can be sent per read() or write(). Pending data is ignored. +If you want to handle multiple events in a single syscall, then use vectored +I/O with readv()/writev(). + +The first thing you should do is sending an UHID_CREATE event. This will +register the device. UHID will respond with an UHID_START event. You can now +start sending data to and reading data from UHID. However, unless UHID sends the +UHID_OPEN event, the internally attached HID Device Driver has no user attached. +That is, you might put your device asleep unless you receive the UHID_OPEN +event. If you receive the UHID_OPEN event, you should start I/O. If the last +user closes the HID device, you will receive an UHID_CLOSE event. This may be +followed by an UHID_OPEN event again and so on. There is no need to perform +reference-counting in user-space. That is, you will never receive multiple +UHID_OPEN events without an UHID_CLOSE event. The HID subsystem performs +ref-counting for you. +You may decide to ignore UHID_OPEN/UHID_CLOSE, though. I/O is allowed even +though the device may have no users. + +If you want to send data to the HID subsystem, you send an HID_INPUT event with +your raw data payload. If the kernel wants to send data to the device, you will +read an UHID_OUTPUT or UHID_OUTPUT_EV event. + +If your device disconnects, you should send an UHID_DESTROY event. This will +unregister the device. You can now send UHID_CREATE again to register a new +device. +If you close() the fd, the device is automatically unregistered and destroyed +internally. + +write() +------- +write() allows you to modify the state of the device and feed input data into +the kernel. The following types are supported: UHID_CREATE, UHID_DESTROY and +UHID_INPUT. The kernel will parse the event immediately and if the event ID is +not supported, it will return -EOPNOTSUPP. If the payload is invalid, then +-EINVAL is returned, otherwise, the amount of data that was read is returned and +the request was handled successfully. + + UHID_CREATE: + This creates the internal HID device. No I/O is possible until you send this + event to the kernel. The payload is of type struct uhid_create_req and + contains information about your device. You can start I/O now. + + UHID_DESTROY: + This destroys the internal HID device. No further I/O will be accepted. There + may still be pending messages that you can receive with read() but no further + UHID_INPUT events can be sent to the kernel. + You can create a new device by sending UHID_CREATE again. There is no need to + reopen the character device. + + UHID_INPUT: + You must send UHID_CREATE before sending input to the kernel! This event + contains a data-payload. This is the raw data that you read from your device. + The kernel will parse the HID reports and react on it. + + UHID_FEATURE_ANSWER: + If you receive a UHID_FEATURE request you must answer with this request. You + must copy the "id" field from the request into the answer. Set the "err" field + to 0 if no error occured or to EIO if an I/O error occurred. + If "err" is 0 then you should fill the buffer of the answer with the results + of the feature request and set "size" correspondingly. + +read() +------ +read() will return a queued ouput report. These output reports can be of type +UHID_START, UHID_STOP, UHID_OPEN, UHID_CLOSE, UHID_OUTPUT or UHID_OUTPUT_EV. No +reaction is required to any of them but you should handle them according to your +needs. Only UHID_OUTPUT and UHID_OUTPUT_EV have payloads. + + UHID_START: + This is sent when the HID device is started. Consider this as an answer to + UHID_CREATE. This is always the first event that is sent. + + UHID_STOP: + This is sent when the HID device is stopped. Consider this as an answer to + UHID_DESTROY. + If the kernel HID device driver closes the device manually (that is, you + didn't send UHID_DESTROY) then you should consider this device closed and send + an UHID_DESTROY event. You may want to reregister your device, though. This is + always the last message that is sent to you unless you reopen the device with + UHID_CREATE. + + UHID_OPEN: + This is sent when the HID device is opened. That is, the data that the HID + device provides is read by some other process. You may ignore this event but + it is useful for power-management. As long as you haven't received this event + there is actually no other process that reads your data so there is no need to + send UHID_INPUT events to the kernel. + + UHID_CLOSE: + This is sent when there are no more processes which read the HID data. It is + the counterpart of UHID_OPEN and you may as well ignore this event. + + UHID_OUTPUT: + This is sent if the HID device driver wants to send raw data to the I/O + device. You should read the payload and forward it to the device. The payload + is of type "struct uhid_data_req". + This may be received even though you haven't received UHID_OPEN, yet. + + UHID_OUTPUT_EV: + Same as UHID_OUTPUT but this contains a "struct input_event" as payload. This + is called for force-feedback, LED or similar events which are received through + an input device by the HID subsystem. You should convert this into raw reports + and send them to your device similar to events of type UHID_OUTPUT. + + UHID_FEATURE: + This event is sent if the kernel driver wants to perform a feature request as + described in the HID specs. The report-type and report-number are available in + the payload. + The kernel serializes feature requests so there will never be two in parallel. + However, if you fail to respond with a UHID_FEATURE_ANSWER in a time-span of 5 + seconds, then the requests will be dropped and a new one might be sent. + Therefore, the payload also contains an "id" field that identifies every + request. + +Document by: + David Herrmann From 4d10a79c8d19166936494980fa54a1d67fbf908c Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:27 +0200 Subject: [PATCH 2458/2556] HID: uhid: add example program This adds an example user-space program that emulates a 3 button mouse with wheel. It detects keyboard presses and moves the mouse accordingly. It register a fake HID device to feed the raw HID reports into the kernel. In this example, you could use uinput to get the same result, but this shows how to get the same behavior with uhid so you don't need HID parsers in user-space. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- samples/uhid/Makefile | 10 + samples/uhid/uhid-example.c | 381 ++++++++++++++++++++++++++++++++++++ 2 files changed, 391 insertions(+) create mode 100644 samples/uhid/Makefile create mode 100644 samples/uhid/uhid-example.c diff --git a/samples/uhid/Makefile b/samples/uhid/Makefile new file mode 100644 index 0000000000000..c95a696560a7d --- /dev/null +++ b/samples/uhid/Makefile @@ -0,0 +1,10 @@ +# kbuild trick to avoid linker error. Can be omitted if a module is built. +obj- := dummy.o + +# List of programs to build +hostprogs-y := uhid-example + +# Tell kbuild to always build the programs +always := $(hostprogs-y) + +HOSTCFLAGS_uhid-example.o += -I$(objtree)/usr/include diff --git a/samples/uhid/uhid-example.c b/samples/uhid/uhid-example.c new file mode 100644 index 0000000000000..03ce3c059a5e7 --- /dev/null +++ b/samples/uhid/uhid-example.c @@ -0,0 +1,381 @@ +/* + * UHID Example + * + * Copyright (c) 2012 David Herrmann + * + * The code may be used by anyone for any purpose, + * and can serve as a starting point for developing + * applications using uhid. + */ + +/* UHID Example + * This example emulates a basic 3 buttons mouse with wheel over UHID. Run this + * program as root and then use the following keys to control the mouse: + * q: Quit the application + * 1: Toggle left button (down, up, ...) + * 2: Toggle right button + * 3: Toggle middle button + * a: Move mouse left + * d: Move mouse right + * w: Move mouse up + * s: Move mouse down + * r: Move wheel up + * f: Move wheel down + * + * If uhid is not available as /dev/uhid, then you can pass a different path as + * first argument. + * If is not installed in /usr, then compile this with: + * gcc -o ./uhid_test -Wall -I./include ./samples/uhid/uhid-example.c + * And ignore the warning about kernel headers. However, it is recommended to + * use the installed uhid.h if available. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* HID Report Desciptor + * We emulate a basic 3 button mouse with wheel. This is the report-descriptor + * as the kernel will parse it: + * + * INPUT[INPUT] + * Field(0) + * Physical(GenericDesktop.Pointer) + * Application(GenericDesktop.Mouse) + * Usage(3) + * Button.0001 + * Button.0002 + * Button.0003 + * Logical Minimum(0) + * Logical Maximum(1) + * Report Size(1) + * Report Count(3) + * Report Offset(0) + * Flags( Variable Absolute ) + * Field(1) + * Physical(GenericDesktop.Pointer) + * Application(GenericDesktop.Mouse) + * Usage(3) + * GenericDesktop.X + * GenericDesktop.Y + * GenericDesktop.Wheel + * Logical Minimum(-128) + * Logical Maximum(127) + * Report Size(8) + * Report Count(3) + * Report Offset(8) + * Flags( Variable Relative ) + * + * This is the mapping that we expect: + * Button.0001 ---> Key.LeftBtn + * Button.0002 ---> Key.RightBtn + * Button.0003 ---> Key.MiddleBtn + * GenericDesktop.X ---> Relative.X + * GenericDesktop.Y ---> Relative.Y + * GenericDesktop.Wheel ---> Relative.Wheel + * + * This information can be verified by reading /sys/kernel/debug/hid//rdesc + * This file should print the same information as showed above. + */ + +static unsigned char rdesc[] = { + 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, + 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, + 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, + 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01, + 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38, + 0x15, 0x80, 0x25, 0x7f, 0x75, 0x08, 0x95, 0x03, + 0x81, 0x06, 0xc0, 0xc0, +}; + +static int uhid_write(int fd, const struct uhid_event *ev) +{ + ssize_t ret; + + ret = write(fd, ev, sizeof(*ev)); + if (ret < 0) { + fprintf(stderr, "Cannot write to uhid: %m\n"); + return -errno; + } else if (ret != sizeof(*ev)) { + fprintf(stderr, "Wrong size written to uhid: %ld != %lu\n", + ret, sizeof(ev)); + return -EFAULT; + } else { + return 0; + } +} + +static int create(int fd) +{ + struct uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_CREATE; + strcpy((char*)ev.u.create.name, "test-uhid-device"); + ev.u.create.rd_data = rdesc; + ev.u.create.rd_size = sizeof(rdesc); + ev.u.create.bus = BUS_USB; + ev.u.create.vendor = 0x15d9; + ev.u.create.product = 0x0a37; + ev.u.create.version = 0; + ev.u.create.country = 0; + + return uhid_write(fd, &ev); +} + +static void destroy(int fd) +{ + struct uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_DESTROY; + + uhid_write(fd, &ev); +} + +static int event(int fd) +{ + struct uhid_event ev; + ssize_t ret; + + memset(&ev, 0, sizeof(ev)); + ret = read(fd, &ev, sizeof(ev)); + if (ret == 0) { + fprintf(stderr, "Read HUP on uhid-cdev\n"); + return -EFAULT; + } else if (ret < 0) { + fprintf(stderr, "Cannot read uhid-cdev: %m\n"); + return -errno; + } else if (ret != sizeof(ev)) { + fprintf(stderr, "Invalid size read from uhid-dev: %ld != %lu\n", + ret, sizeof(ev)); + return -EFAULT; + } + + switch (ev.type) { + case UHID_START: + fprintf(stderr, "UHID_START from uhid-dev\n"); + break; + case UHID_STOP: + fprintf(stderr, "UHID_STOP from uhid-dev\n"); + break; + case UHID_OPEN: + fprintf(stderr, "UHID_OPEN from uhid-dev\n"); + break; + case UHID_CLOSE: + fprintf(stderr, "UHID_CLOSE from uhid-dev\n"); + break; + case UHID_OUTPUT: + fprintf(stderr, "UHID_OUTPUT from uhid-dev\n"); + break; + case UHID_OUTPUT_EV: + fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n"); + break; + default: + fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type); + } + + return 0; +} + +static bool btn1_down; +static bool btn2_down; +static bool btn3_down; +static signed char abs_hor; +static signed char abs_ver; +static signed char wheel; + +static int send_event(int fd) +{ + struct uhid_event ev; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_INPUT; + ev.u.input.size = 4; + + if (btn1_down) + ev.u.input.data[0] |= 0x1; + if (btn2_down) + ev.u.input.data[0] |= 0x2; + if (btn3_down) + ev.u.input.data[0] |= 0x4; + + ev.u.input.data[1] = abs_hor; + ev.u.input.data[2] = abs_ver; + ev.u.input.data[3] = wheel; + + return uhid_write(fd, &ev); +} + +static int keyboard(int fd) +{ + char buf[128]; + ssize_t ret, i; + + ret = read(STDIN_FILENO, buf, sizeof(buf)); + if (ret == 0) { + fprintf(stderr, "Read HUP on stdin\n"); + return -EFAULT; + } else if (ret < 0) { + fprintf(stderr, "Cannot read stdin: %m\n"); + return -errno; + } + + for (i = 0; i < ret; ++i) { + switch (buf[i]) { + case '1': + btn1_down = !btn1_down; + ret = send_event(fd); + if (ret) + return ret; + break; + case '2': + btn2_down = !btn2_down; + ret = send_event(fd); + if (ret) + return ret; + break; + case '3': + btn3_down = !btn3_down; + ret = send_event(fd); + if (ret) + return ret; + break; + case 'a': + abs_hor = -20; + ret = send_event(fd); + abs_hor = 0; + if (ret) + return ret; + break; + case 'd': + abs_hor = 20; + ret = send_event(fd); + abs_hor = 0; + if (ret) + return ret; + break; + case 'w': + abs_ver = -20; + ret = send_event(fd); + abs_ver = 0; + if (ret) + return ret; + break; + case 's': + abs_ver = 20; + ret = send_event(fd); + abs_ver = 0; + if (ret) + return ret; + break; + case 'r': + wheel = 1; + ret = send_event(fd); + wheel = 0; + if (ret) + return ret; + break; + case 'f': + wheel = -1; + ret = send_event(fd); + wheel = 0; + if (ret) + return ret; + break; + case 'q': + return -ECANCELED; + default: + fprintf(stderr, "Invalid input: %c\n", buf[i]); + } + } + + return 0; +} + +int main(int argc, char **argv) +{ + int fd; + const char *path = "/dev/uhid"; + struct pollfd pfds[2]; + int ret; + struct termios state; + + ret = tcgetattr(STDIN_FILENO, &state); + if (ret) { + fprintf(stderr, "Cannot get tty state\n"); + } else { + state.c_lflag &= ~ICANON; + state.c_cc[VMIN] = 1; + ret = tcsetattr(STDIN_FILENO, TCSANOW, &state); + if (ret) + fprintf(stderr, "Cannot set tty state\n"); + } + + if (argc >= 2) { + if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { + fprintf(stderr, "Usage: %s [%s]\n", argv[0], path); + return EXIT_SUCCESS; + } else { + path = argv[1]; + } + } + + fprintf(stderr, "Open uhid-cdev %s\n", path); + fd = open(path, O_RDWR | O_CLOEXEC); + if (fd < 0) { + fprintf(stderr, "Cannot open uhid-cdev %s: %m\n", path); + return EXIT_FAILURE; + } + + fprintf(stderr, "Create uhid device\n"); + ret = create(fd); + if (ret) { + close(fd); + return EXIT_FAILURE; + } + + pfds[0].fd = STDIN_FILENO; + pfds[0].events = POLLIN; + pfds[1].fd = fd; + pfds[1].events = POLLIN; + + fprintf(stderr, "Press 'q' to quit...\n"); + while (1) { + ret = poll(pfds, 2, -1); + if (ret < 0) { + fprintf(stderr, "Cannot poll for fds: %m\n"); + break; + } + if (pfds[0].revents & POLLHUP) { + fprintf(stderr, "Received HUP on stdin\n"); + break; + } + if (pfds[1].revents & POLLHUP) { + fprintf(stderr, "Received HUP on uhid-cdev\n"); + break; + } + + if (pfds[0].revents & POLLIN) { + ret = keyboard(fd); + if (ret) + break; + } + if (pfds[1].revents & POLLIN) { + ret = event(fd); + if (ret) + break; + } + } + + fprintf(stderr, "Destroy uhid device\n"); + destroy(fd); + return EXIT_SUCCESS; +} From cd09986ace668841ed34c69cd6fc8bf112654710 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:28 +0200 Subject: [PATCH 2459/2556] MAINTAINERS: add UHID entry Add an UHID entry to the MAINTAINERS file. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 80540e33f1ec3..6ca300443de60 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6251,6 +6251,13 @@ S: Maintained F: Documentation/filesystems/ufs.txt F: fs/ufs/ +UHID USERSPACE HID IO DRIVER: +M: David Herrmann +L: linux-input@vger.kernel.org +S: Maintained +F: drivers/hid/uhid.c +F: include/linux/uhid.h + ULTRA-WIDEBAND (UWB) SUBSYSTEM: L: linux-usb@vger.kernel.org S: Orphan From bba8850852641a1eec71cbd2c53618d420d0c776 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 18 Jun 2012 17:08:08 +0200 Subject: [PATCH 2460/2556] HID: uhid: silence gcc warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gcc is giving me: drivers/hid/uhid.c: In function ‘uhid_hid_get_raw’: drivers/hid/uhid.c:157: warning: ‘len’ may be used uninitialized in this function which is clearly bogus, as - when used as memcpy() argument, it's initialized properly - the code is structured in a way that either 'ret' or 'len' is always initialized, so the return statement always has an initialized value. Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index ea560bfa033d2..119b7e6405bee 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -154,7 +154,7 @@ static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, struct uhid_event *ev; unsigned long flags; int ret; - size_t len; + size_t uninitialized_var(len); struct uhid_feature_answer_req *req; if (!uhid->running) From 16c9b24542f79e5c62a49abe9347c7b002db3eca Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Sat, 14 Jul 2012 18:59:25 -0300 Subject: [PATCH 2461/2556] HID: uhid: Fix sending events with invalid data This was detected because events with invalid types were arriving to userspace. The code before this patch would only work for the first event in the queue (when uhid->tail is 0). Signed-off-by: Vinicius Costa Gomes Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 119b7e6405bee..714cd8cc9579b 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -465,7 +465,7 @@ static ssize_t uhid_char_read(struct file *file, char __user *buffer, goto try_again; } else { len = min(count, sizeof(**uhid->outq)); - if (copy_to_user(buffer, &uhid->outq[uhid->tail], len)) { + if (copy_to_user(buffer, uhid->outq[uhid->tail], len)) { ret = -EFAULT; } else { kfree(uhid->outq[uhid->tail]); From 01470ebc7cfa60656568ac7fc2b6501464506291 Mon Sep 17 00:00:00 2001 From: Alan Ott Date: Tue, 18 Jan 2011 03:04:39 -0500 Subject: [PATCH 2462/2556] HID: Add Support for Setting and Getting Feature Reports from hidraw Per the HID Specification, Feature reports must be sent and received on the Configuration endpoint (EP 0) through the Set_Report/Get_Report interfaces. This patch adds two ioctls to hidraw to set and get feature reports to and from the device. Modifications were made to hidraw and usbhid. New hidraw ioctls: HIDIOCSFEATURE - Perform a Set_Report transfer of a Feature report. HIDIOCGFEATURE - Perform a Get_Report transfer of a Feature report. Signed-off-by: Alan Ott Signed-off-by: Antonio Ospite Signed-off-by: Jiri Kosina --- drivers/hid/hidraw.c | 106 ++++++++++++++++++++++++++++++++-- drivers/hid/usbhid/hid-core.c | 35 +++++++++++ include/linux/hid.h | 3 + include/linux/hidraw.h | 3 + 4 files changed, 141 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 468e87b53ed26..8f06044a3e3e8 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -102,15 +102,14 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, } /* the first byte is expected to be a report number */ -static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +/* This function is to be called with the minors_lock mutex held */ +static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type) { unsigned int minor = iminor(file->f_path.dentry->d_inode); struct hid_device *dev; __u8 *buf; int ret = 0; - mutex_lock(&minors_lock); - if (!hidraw_table[minor]) { ret = -ENODEV; goto out; @@ -148,14 +147,92 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t goto out_free; } - ret = dev->hid_output_raw_report(dev, buf, count, HID_OUTPUT_REPORT); + ret = dev->hid_output_raw_report(dev, buf, count, report_type); out_free: kfree(buf); out: + return ret; +} + +/* the first byte is expected to be a report number */ +static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + ssize_t ret; + mutex_lock(&minors_lock); + ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT); mutex_unlock(&minors_lock); return ret; } + +/* This function performs a Get_Report transfer over the control endpoint + per section 7.2.1 of the HID specification, version 1.1. The first byte + of buffer is the report number to request, or 0x0 if the defice does not + use numbered reports. The report_type parameter can be HID_FEATURE_REPORT + or HID_INPUT_REPORT. This function is to be called with the minors_lock + mutex held. */ +static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type) +{ + unsigned int minor = iminor(file->f_path.dentry->d_inode); + struct hid_device *dev; + __u8 *buf; + int ret = 0, len; + unsigned char report_number; + + dev = hidraw_table[minor]->hid; + + if (!dev->hid_get_raw_report) { + ret = -ENODEV; + goto out; + } + + if (count > HID_MAX_BUFFER_SIZE) { + printk(KERN_WARNING "hidraw: pid %d passed too large report\n", + task_pid_nr(current)); + ret = -EINVAL; + goto out; + } + + if (count < 2) { + printk(KERN_WARNING "hidraw: pid %d passed too short report\n", + task_pid_nr(current)); + ret = -EINVAL; + goto out; + } + + buf = kmalloc(count * sizeof(__u8), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto out; + } + + /* Read the first byte from the user. This is the report number, + which is passed to dev->hid_get_raw_report(). */ + if (copy_from_user(&report_number, buffer, 1)) { + ret = -EFAULT; + goto out_free; + } + + ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type); + + if (ret < 0) + goto out_free; + + len = (ret < count) ? ret : count; + + if (copy_to_user(buffer, buf, len)) { + ret = -EFAULT; + goto out_free; + } + + ret = len; + +out_free: + kfree(buf); +out: + return ret; +} + static unsigned int hidraw_poll(struct file *file, poll_table *wait) { struct hidraw_list *list = file->private_data; @@ -295,7 +372,24 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, default: { struct hid_device *hid = dev->hid; - if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ) { + if (_IOC_TYPE(cmd) != 'H') { + ret = -EINVAL; + break; + } + + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSFEATURE(0))) { + int len = _IOC_SIZE(cmd); + ret = hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT); + break; + } + if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGFEATURE(0))) { + int len = _IOC_SIZE(cmd); + ret = hidraw_get_report(file, user_arg, len, HID_FEATURE_REPORT); + break; + } + + /* Begin Read-only ioctls. */ + if (_IOC_DIR(cmd) != _IOC_READ) { ret = -EINVAL; break; } @@ -327,7 +421,7 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, -EFAULT : len; break; } - } + } ret = -ENOTTY; } diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index b336dd84036f4..38c261a40c744 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -799,6 +799,40 @@ static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid) return 0; } +static int usbhid_get_raw_report(struct hid_device *hid, + unsigned char report_number, __u8 *buf, size_t count, + unsigned char report_type) +{ + struct usbhid_device *usbhid = hid->driver_data; + struct usb_device *dev = hid_to_usb_dev(hid); + struct usb_interface *intf = usbhid->intf; + struct usb_host_interface *interface = intf->cur_altsetting; + int skipped_report_id = 0; + int ret; + + /* Byte 0 is the report number. Report data starts at byte 1.*/ + buf[0] = report_number; + if (report_number == 0x0) { + /* Offset the return buffer by 1, so that the report ID + will remain in byte 0. */ + buf++; + count--; + skipped_report_id = 1; + } + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + HID_REQ_GET_REPORT, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + ((report_type + 1) << 8) | report_number, + interface->desc.bInterfaceNumber, buf, count, + USB_CTRL_SET_TIMEOUT); + + /* count also the report id */ + if (ret > 0 && skipped_report_id) + ret++; + + return ret; +} + static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count, unsigned char report_type) { @@ -1139,6 +1173,7 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * usb_set_intfdata(intf, hid); hid->ll_driver = &usb_hid_driver; + hid->hid_get_raw_report = usbhid_get_raw_report; hid->hid_output_raw_report = usbhid_output_raw_report; hid->ff_init = hid_pidff_init; #ifdef CONFIG_USB_HIDDEV diff --git a/include/linux/hid.h b/include/linux/hid.h index fc5faf60f6df4..bb29bb1dbd2fd 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -504,6 +504,9 @@ struct hid_device { /* device report descriptor */ struct hid_usage *, __s32); void (*hiddev_report_event) (struct hid_device *, struct hid_report *); + /* handler for raw input (Get_Report) data, used by hidraw */ + int (*hid_get_raw_report) (struct hid_device *, unsigned char, __u8 *, size_t, unsigned char); + /* handler for raw output data, used by hidraw */ int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t, unsigned char); diff --git a/include/linux/hidraw.h b/include/linux/hidraw.h index dd8d69269176f..4b88e697c4e9b 100644 --- a/include/linux/hidraw.h +++ b/include/linux/hidraw.h @@ -35,6 +35,9 @@ struct hidraw_devinfo { #define HIDIOCGRAWINFO _IOR('H', 0x03, struct hidraw_devinfo) #define HIDIOCGRAWNAME(len) _IOC(_IOC_READ, 'H', 0x04, len) #define HIDIOCGRAWPHYS(len) _IOC(_IOC_READ, 'H', 0x05, len) +/* The first byte of SFEATURE and GFEATURE is the report number */ +#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) +#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) #define HIDRAW_FIRST_MINOR 0 #define HIDRAW_MAX_DEVICES 64 From dba4340abf365f039bd16939acd65525b7b38823 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 11 Dec 2012 17:43:56 -0600 Subject: [PATCH 2463/2556] configs: enable Uhid Change-Id: Ieda9e6d619047e908b90305e17e265875cb8c81c --- arch/arm/configs/evervolv_bravo_defconfig | 1 + arch/arm/configs/evervolv_incrediblec_defconfig | 1 + arch/arm/configs/evervolv_mahimahi_defconfig | 1 + arch/arm/configs/evervolv_supersonic_defconfig | 1 + 4 files changed, 4 insertions(+) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index 54a936e9eb371..8e445a167821e 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -1539,6 +1539,7 @@ CONFIG_FB_MSM_LCDC=y CONFIG_HID_SUPPORT=y CONFIG_HID=y # CONFIG_HIDRAW is not set +CONFIG_UHID=y # CONFIG_HID_PID is not set # diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index d201219599677..e1a32aabe6cb5 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -1560,6 +1560,7 @@ CONFIG_FB_MSM_MDDI=y CONFIG_HID_SUPPORT=y CONFIG_HID=y # CONFIG_HIDRAW is not set +CONFIG_UHID=y # CONFIG_HID_PID is not set # diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index d667d9ed73d9b..c416b2384309f 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1536,6 +1536,7 @@ CONFIG_FB_MSM_LCDC=y CONFIG_HID_SUPPORT=y CONFIG_HID=y # CONFIG_HIDRAW is not set +CONFIG_UHID=y # CONFIG_HID_PID is not set # diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index c4692860313c5..4f01e71e84496 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -1543,6 +1543,7 @@ CONFIG_MSM_HDMI=y CONFIG_HID_SUPPORT=y CONFIG_HID=y # CONFIG_HIDRAW is not set +CONFIG_UHID=y # CONFIG_HID_PID is not set # From fc58a7f8d1c9f83a65d867644bc7b4aaa6f459b1 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 21 Feb 2013 21:29:21 -0600 Subject: [PATCH 2464/2556] config: mahimahi: update for ubuntu Change-Id: I329a51154bc7ab2f2b4d5845c04382c0bc2b0dd2 --- arch/arm/configs/evervolv_mahimahi_defconfig | 36 ++++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index c416b2384309f..c0e3f85697e2e 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Tue Oct 2 20:54:19 2012 +# Fri Feb 22 10:26:50 2013 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -44,7 +44,8 @@ CONFIG_HAVE_KERNEL_LZO=y CONFIG_KERNEL_XZ=y # CONFIG_KERNEL_LZO is not set CONFIG_SWAP=y -# CONFIG_SYSVIPC is not set +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y # CONFIG_POSIX_MQUEUE is not set # CONFIG_BSD_PROCESS_ACCT is not set # CONFIG_TASKSTATS is not set @@ -91,7 +92,12 @@ CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_CGROUP=y # CONFIG_DEBUG_BLK_CGROUP is not set -# CONFIG_NAMESPACES is not set +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_IPC_NS=y +CONFIG_USER_NS=y +CONFIG_PID_NS=y +CONFIG_NET_NS=y CONFIG_SCHED_AUTOGROUP=y # CONFIG_SYSFS_DEPRECATED is not set CONFIG_RELAY=y @@ -478,7 +484,7 @@ CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set -# CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y # CONFIG_CPU_FREQ_GOV_POWERSAVE is not set # CONFIG_CPU_FREQ_GOV_USERSPACE is not set CONFIG_CPU_FREQ_GOV_ONDEMAND=y @@ -592,7 +598,8 @@ CONFIG_IPV6_TUNNEL=y CONFIG_IPV6_MULTIPLE_TABLES=y # CONFIG_IPV6_SUBTREES is not set # CONFIG_IPV6_MROUTE is not set -CONFIG_ANDROID_PARANOID_NETWORK=y +# CONFIG_NETLABEL is not set +# CONFIG_ANDROID_PARANOID_NETWORK is not set CONFIG_NET_ACTIVITY_STATS=y # CONFIG_NETWORK_SECMARK is not set # CONFIG_NETWORK_PHY_TIMESTAMPING is not set @@ -737,6 +744,7 @@ CONFIG_IP_NF_MANGLE=y # CONFIG_IP_NF_TARGET_ECN is not set # CONFIG_IP_NF_TARGET_TTL is not set CONFIG_IP_NF_RAW=y +# CONFIG_IP_NF_SECURITY is not set CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y @@ -762,6 +770,7 @@ CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y CONFIG_IP6_NF_RAW=y +# CONFIG_IP6_NF_SECURITY is not set # CONFIG_BRIDGE_NF_EBTABLES is not set # CONFIG_IP_DCCP is not set # CONFIG_IP_SCTP is not set @@ -907,7 +916,8 @@ CONFIG_RFKILL_LEDS=y # Generic Driver Options # CONFIG_UEVENT_HELPER_PATH="" -# CONFIG_DEVTMPFS is not set +CONFIG_DEVTMPFS=y +# CONFIG_DEVTMPFS_MOUNT is not set CONFIG_STANDALONE=y CONFIG_PREVENT_FIRMWARE_BUILD=y CONFIG_FW_LOADER=y @@ -1252,7 +1262,7 @@ CONFIG_SERIAL_BCM_BT_LPM=y # CONFIG_SERIAL_ALTERA_JTAGUART is not set # CONFIG_SERIAL_ALTERA_UART is not set CONFIG_UNIX98_PTYS=y -# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y # CONFIG_LEGACY_PTYS is not set # CONFIG_TTY_PRINTK is not set # CONFIG_HVC_DCC is not set @@ -1796,9 +1806,10 @@ CONFIG_FS_MBCACHE=y # CONFIG_FS_POSIX_ACL is not set CONFIG_FILE_LOCKING=y CONFIG_FSNOTIFY=y -# CONFIG_DNOTIFY is not set +CONFIG_DNOTIFY=y CONFIG_INOTIFY_USER=y -# CONFIG_FANOTIFY is not set +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y # CONFIG_QUOTA is not set # CONFIG_QUOTACTL is not set # CONFIG_AUTOFS4_FS is not set @@ -2035,8 +2046,13 @@ CONFIG_DEBUG_STACK_USAGE=y # # CONFIG_KEYS is not set # CONFIG_SECURITY_DMESG_RESTRICT is not set -# CONFIG_SECURITY is not set +CONFIG_SECURITY=y # CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_NETWORK is not set +# CONFIG_SECURITY_PATH is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_APPARMOR is not set +# CONFIG_IMA is not set CONFIG_DEFAULT_SECURITY_DAC=y CONFIG_DEFAULT_SECURITY="" CONFIG_CRYPTO=y From d76c86ee5c6f001a887688398c033be6255ec334 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sat, 23 Feb 2013 23:56:07 -0600 Subject: [PATCH 2465/2556] new defconfigs modified for ubuntuphone Change-Id: I601d2fe7f10dc3dfcf1b7ac78eea5690af6d2363 --- .../evervolv_ubuntuphone_bravo_defconfig | 2193 ++++++++++++++++ ...evervolv_ubuntuphone_incrediblec_defconfig | 2229 +++++++++++++++++ .../evervolv_ubuntuphone_mahimahi_defconfig | 2192 ++++++++++++++++ .../evervolv_ubuntuphone_supersonic_defconfig | 2212 ++++++++++++++++ 4 files changed, 8826 insertions(+) create mode 100644 arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig create mode 100644 arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig create mode 100644 arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig create mode 100644 arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig diff --git a/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig b/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig new file mode 100644 index 0000000000000..1e03796f4f6b0 --- /dev/null +++ b/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig @@ -0,0 +1,2193 @@ +# +# Automatically generated make config: don't edit +# Linux/arm 2.6.38.8 Kernel Configuration +# Fri Feb 22 19:02:05 2013 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="-evervolv-turba-jellybean" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED is not set +CONFIG_HAVE_SPARSE_IRQ=y +# CONFIG_GENERIC_PENDING_IRQ is not set +# CONFIG_AUTO_IRQ_AFFINITY is not set +# CONFIG_IRQ_PER_CPU is not set +# CONFIG_SPARSE_IRQ is not set + +# +# RCU Subsystem +# +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +CONFIG_TINY_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_TRACE is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_RCU_BOOST=y +CONFIG_RCU_BOOST_PRIO=1 +CONFIG_RCU_BOOST_DELAY=500 +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_CGROUPS=y +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CGROUP_NS is not set +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +# CONFIG_CGROUP_MEM_RES_CTLR is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_CGROUP=y +# CONFIG_DEBUG_BLK_CGROUP is not set +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_IPC_NS=y +CONFIG_USER_NS=y +CONFIG_PID_NS=y +CONFIG_NET_NS=y +CONFIG_SCHED_AUTOGROUP=y +# CONFIG_SYSFS_DEPRECATED is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +# CONFIG_KALLSYMS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_ASHMEM=y +CONFIG_AIO=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_TRACEPOINTS=y +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set +# CONFIG_BLK_DEV_THROTTLING is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CFQ_GROUP_IOSCHED=y +CONFIG_IOSCHED_BFQ=y +CONFIG_CGROUP_BFQIO=y +CONFIG_DEFAULT_DEADLINE=y +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_BFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="deadline" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +CONFIG_ARCH_MSM=y +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_S5PV310 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_TCC_926 is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_ARCH_MSM7X00A is not set +# CONFIG_ARCH_MSM7X30 is not set +CONFIG_ARCH_QSD8X50=y +# CONFIG_ARCH_MSM8X60 is not set +CONFIG_ARCH_MSM_SCORPION=y +CONFIG_HAS_MSM_DEBUG_UART_PHYS=y +CONFIG_MSM_MDP31=y +# CONFIG_PERFLOCK is not set + +# +# Qualcomm MSM Board Type +# +# CONFIG_MACH_SWORDFISH is not set +CONFIG_MACH_QSD8X50_SURF=y +# CONFIG_MACH_QSD8X50A_ST1_5 is not set +CONFIG_MSM_DEBUG_UART=1 +# CONFIG_MSM_DEBUG_UART_NONE is not set +CONFIG_MSM_DEBUG_UART1=y +# CONFIG_MSM_DEBUG_UART2 is not set +# CONFIG_MSM_DEBUG_UART3 is not set +CONFIG_MSM_PROC_COMM=y +# CONFIG_MACH_MAHIMAHI is not set +# CONFIG_MACH_BRAVO_NONE is not set +CONFIG_MACH_BRAVO=y +# CONFIG_MACH_BRAVOC is not set +# CONFIG_MACH_INCREDIBLE is not set +# CONFIG_MACH_INCREDIBLEC is not set +# CONFIG_MACH_SUPERSONIC is not set +# CONFIG_MACH_QSD8X50_FFA is not set +# CONFIG_HTC_HEADSET is not set +CONFIG_HTC_35MM_JACK=y +# CONFIG_HTC_BATTCHG is not set +CONFIG_HTC_PWRSPLY=y +# CONFIG_HTC_PWRSINK is not set +# CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set +# CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_QSD_SVS=y +CONFIG_QSD_PMIC_DEFAULT_DCDC1=1275 +CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_PHYS_OFFSET=0x20000000 +CONFIG_MSM7X00A_USE_GP_TIMER=y +# CONFIG_MSM7X00A_USE_DG_TIMER is not set +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=1 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_FIQ_SUPPORT=y +# CONFIG_MSM_SERIAL_DEBUGGER is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_DAL=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y +CONFIG_MSM_CPU_FREQ_MAX=998400 +CONFIG_MSM_CPU_FREQ_MIN=245000 +# CONFIG_AXI_SCREEN_POLICY is not set +CONFIG_MSM_CPU_AVS=y +CONFIG_MSM_AVS_HW=y +CONFIG_HTC_ACOUSTIC_QSD=y +CONFIG_MSM_QDSP6=y +CONFIG_WIFI_CONTROL_FUNC=y +# CONFIG_WIFI_MEM_PREALLOC is not set +CONFIG_ARCH_MSM_FLASHLIGHT=y +# CONFIG_MICROP_COMMON is not set +# CONFIG_HTC_HEADSET_MGR is not set +# CONFIG_VIRTUAL_KPANIC_PARTITION is not set + +# +# System MMU +# + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_VERIFY_PERMISSION_FAULT=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_CACHE_ERR_REPORT is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +# CONFIG_VCM is not set +# CONFIG_STRICT_MEMORY_RWX is not set +# CONFIG_RESERVE_FIRST_PAGE is not set +CONFIG_CPU_HAS_PMU=y +CONFIG_ARM_ERRATA_430973=y +CONFIG_ARM_ERRATA_458693=y +CONFIG_ARM_ERRATA_460075=y +CONFIG_ARM_ERRATA_743622=y +# CONFIG_KSAPI is not set +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_VMALLOC_RESERVE=0x08000000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_NEED_PER_CPU_KM=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_CP_ACCESS is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM0" +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +# CONFIG_CPU_FREQ_GOV_SMARTASS2 is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM_OPS=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +CONFIG_INET_ESP=y +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_NETLABEL is not set +# CONFIG_ANDROID_PARANOID_NETWORK is not set +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NETFILTER_XT_TARGET_HL=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_LED=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +CONFIG_NETFILTER_XT_MATCH_POLICY=y +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +# CONFIG_NF_NAT_SNMP_BASIC is not set +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +CONFIG_NF_NAT_H323=y +CONFIG_NF_NAT_SIP=y +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y +# CONFIG_IP_NF_SECURITY is not set +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK_IPV6=y +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_TARGET_HL=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +# CONFIG_IP6_NF_SECURITY is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +CONFIG_NET_SCH_INGRESS=y + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +# CONFIG_GACT_PROB is not set +CONFIG_NET_ACT_MIRRED=y +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NET_DROP_MONITOR is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_CFG80211=y +CONFIG_NL80211_TESTMODE=y +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_DEBUGFS is not set +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +# CONFIG_LIB80211 is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +# CONFIG_MAC80211 is not set + +# +# Some wireless drivers require a rate control algorithm +# +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_DEVTMPFS=y +# CONFIG_DEVTMPFS_MOUNT is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_MSM_NAND=y +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_CRYPTOLOOP=y + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set +CONFIG_MISC_DEVICES=y +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_KERNEL_DEBUGGER_CORE=y +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +CONFIG_SENSORS_AKM8973=y +# CONFIG_SENSORS_AKM8976 is not set +# CONFIG_VP_A1026 is not set +# CONFIG_DS1682 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_WL127X_RFKILL is not set +# CONFIG_APANIC is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_IFB=y +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set +CONFIG_MII=y +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set +# CONFIG_ETHOC is not set +CONFIG_SMC911X=y +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8851_MLL is not set +CONFIG_NETDEV_1000=y +# CONFIG_STMMAC_ETH is not set +CONFIG_NETDEV_10000=y +CONFIG_WLAN=y +# CONFIG_ATH_COMMON is not set +# CONFIG_BCM4329 is not set +CONFIG_BCMDHD=y +CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" +CONFIG_BCMDHD_NVRAM_PATH="/proc/calibration" +# CONFIG_DHD_USE_STATIC_BUF is not set +# CONFIG_DHD_USE_SCHED_SCAN is not set +# CONFIG_DHD_ENABLE_P2P is not set +# CONFIG_HOSTAP is not set +# CONFIG_IWM is not set +# CONFIG_LIBERTAS is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set + +# +# CAIF transport drivers +# +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +# CONFIG_PPPOE is not set +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_GAN_ETH=y +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_ATMEL is not set +# CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELAN_I2C_8232 is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_QT602240 is not set +# CONFIG_TOUCHSCREEN_MSM is not set +CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y +CONFIG_TOUCHSCREEN_DUPLICATED_FILTER=y +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set +# CONFIG_INPUT_CAPELLA_CM3602 is not set +CONFIG_INPUT_CAPELLA_CM3602_HTC=y +CONFIG_LIGHTSENSOR_MICROP=y +CONFIG_INPUT_OPTICALJOYSTICK=y +CONFIG_OPTICALJOYSTICK_CRUCIAL=y +CONFIG_OPTICALJOYSTICK_CRUCIAL_uP=y +# CONFIG_OPTICALJOYSTICK_CRUCIAL_SPI is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_BCM_BT_LPM=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_MSM=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_SPI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_BASIC_MMIO is not set +# CONFIG_GPIO_IT8761E is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +CONFIG_W1=y + +# +# 1-wire Bus Masters +# +CONFIG_W1_MASTER_DS2482=y +# CONFIG_W1_MASTER_DS1WM is not set +# CONFIG_W1_MASTER_GPIO is not set + +# +# 1-wire Slaves +# +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_DS2423 is not set +# CONFIG_W1_SLAVE_DS2431 is not set +# CONFIG_W1_SLAVE_DS2433 is not set +# CONFIG_W1_SLAVE_DS2760 is not set +# CONFIG_W1_SLAVE_BQ27000 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2782 is not set +CONFIG_BATTERY_DS2784=y +# CONFIG_BATTERY_BQ20Z75 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_MFD_SUPPORT=y +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_TPS65200 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_WL1273_CORE is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +CONFIG_REGULATOR_TPS65023=y +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_AD5398 is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +CONFIG_RC_CORE=y +CONFIG_LIRC=y +CONFIG_RC_MAP=y +CONFIG_IR_NEC_DECODER=y +CONFIG_IR_RC5_DECODER=y +CONFIG_IR_RC6_DECODER=y +CONFIG_IR_JVC_DECODER=y +CONFIG_IR_SONY_DECODER=y +CONFIG_IR_RC5_SZ_DECODER=y +CONFIG_IR_LIRC_CODEC=y +# CONFIG_IR_IMON is not set +# CONFIG_IR_MCEUSB is not set +# CONFIG_IR_STREAMZAP is not set +# CONFIG_RC_LOOPBACK is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +CONFIG_720P_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set + +# +# Camera Sensor Selection +# +# CONFIG_MT9T013 is not set +# CONFIG_MT9D112 is not set +# CONFIG_MT9P012 is not set +CONFIG_S5K3E2FX=y +# CONFIG_S5K6AAFX is not set +# CONFIG_OV8810 is not set +# CONFIG_OV9665 is not set +# CONFIG_S5K3H1GX is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +# CONFIG_ION is not set +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y +# CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_LEGACY_MDP=y +CONFIG_FB_MSM_MDP_PPP=y +CONFIG_FB_MSM_LCDC=y +# CONFIG_FB_MSM_MDDI is not set +# CONFIG_MSM_HDMI is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set +CONFIG_UHID=y +# CONFIG_HID_PID is not set + +# +# Special HID drivers +# +CONFIG_HID_APPLE=y +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_WACOM is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +# CONFIG_USB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA_U2O is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_CI13XXX_MSM is not set +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_MSM_72K=y +# CONFIG_USB_MSM_72K_HTC is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_ANDROID=y +# CONFIG_USB_ANDROID_ACM is not set +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_DIAG=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +# CONFIG_USB_ANDROID_MTP is not set +CONFIG_USB_ANDROID_RNDIS=y +CONFIG_USB_ANDROID_RNDIS_WCEIS=y +CONFIG_USB_ANDROID_ACCESSORY=y +CONFIG_USB_CSW_HACK=y +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG_72K is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_CLKGATE is not set +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_MSM=y +# CONFIG_MMC_DW is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +CONFIG_LEDS_CPLD=y +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +CONFIG_LEDS_TRIGGER_SLEEP=y + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_NFC_DEVICES is not set +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_MSM7X00A=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_ECHO is not set +# CONFIG_BRCM80211 is not set +# CONFIG_COMEDI is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE=128 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE=16 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE=8 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_POHMELFS is not set +# CONFIG_IIO is not set +CONFIG_XVMALLOC=y +CONFIG_ZRAM=y +# CONFIG_ZRAM_DEBUG is not set +# CONFIG_FB_SM7XX is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_ST_BT is not set +# CONFIG_LIRC_STAGING is not set +CONFIG_MACH_NO_WESTBRIDGE=y +# CONFIG_ATH6K_LEGACY is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=y +# CONFIG_CUSE is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=y +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_WEAK_PW_HASH=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_ACL is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_SCHED_DEBUG is not set +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_BKL is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_NOP_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACER_MAX_TRACE=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_EVENT_POWER_TRACING_DEPRECATED=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +CONFIG_SCHED_TRACER=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_ARM_UNWIND is not set +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +CONFIG_SECURITY=y +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_NETWORK is not set +# CONFIG_SECURITY_PATH is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_APPARMOR is not set +# CONFIG_IMA is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +CONFIG_BINARY_PRINTF=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig b/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig new file mode 100644 index 0000000000000..c25b420b9fbdc --- /dev/null +++ b/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig @@ -0,0 +1,2229 @@ +# +# Automatically generated make config: don't edit +# Linux/arm 2.6.38.8 Kernel Configuration +# Sat Feb 23 23:52:39 2013 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="arm-eabi-" +CONFIG_LOCALVERSION="-evervolv-dives-jellybean" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +CONFIG_KERNEL_LZMA=y +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED is not set +CONFIG_HAVE_SPARSE_IRQ=y +# CONFIG_GENERIC_PENDING_IRQ is not set +# CONFIG_AUTO_IRQ_AFFINITY is not set +# CONFIG_IRQ_PER_CPU is not set +# CONFIG_SPARSE_IRQ is not set + +# +# RCU Subsystem +# +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +CONFIG_TINY_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_TRACE is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_RCU_BOOST=y +CONFIG_RCU_BOOST_PRIO=1 +CONFIG_RCU_BOOST_DELAY=500 +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_CGROUPS=y +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CGROUP_NS is not set +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +# CONFIG_RESOURCE_COUNTERS is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +# CONFIG_BLK_CGROUP is not set +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_IPC_NS=y +CONFIG_USER_NS=y +CONFIG_PID_NS=y +CONFIG_NET_NS=y +CONFIG_SCHED_AUTOGROUP=y +# CONFIG_SYSFS_DEPRECATED is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +# CONFIG_KALLSYMS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_ASHMEM=y +CONFIG_AIO=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_TRACEPOINTS=y +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_IOSCHED_BFQ=y +# CONFIG_CGROUP_BFQIO is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_BFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +CONFIG_ARCH_MSM=y +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_S5PV310 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_TCC_926 is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_ARCH_MSM7X00A is not set +# CONFIG_ARCH_MSM7X30 is not set +CONFIG_ARCH_QSD8X50=y +# CONFIG_ARCH_MSM8X60 is not set +CONFIG_ARCH_MSM_SCORPION=y +CONFIG_HAS_MSM_DEBUG_UART_PHYS=y +CONFIG_MSM_MDP31=y +# CONFIG_PERFLOCK is not set + +# +# Qualcomm MSM Board Type +# +# CONFIG_MACH_SWORDFISH is not set +CONFIG_MACH_QSD8X50_SURF=y +# CONFIG_MACH_QSD8X50A_ST1_5 is not set +CONFIG_MSM_DEBUG_UART_NONE=y +# CONFIG_MSM_DEBUG_UART1 is not set +# CONFIG_MSM_DEBUG_UART2 is not set +# CONFIG_MSM_DEBUG_UART3 is not set +CONFIG_MSM_PROC_COMM=y +# CONFIG_MACH_MAHIMAHI is not set +CONFIG_MACH_BRAVO_NONE=y +# CONFIG_MACH_BRAVO is not set +# CONFIG_MACH_BRAVOC is not set +# CONFIG_MACH_INCREDIBLE is not set +CONFIG_MACH_INCREDIBLEC=y +# CONFIG_MACH_SUPERSONIC is not set +# CONFIG_MACH_QSD8X50_FFA is not set +# CONFIG_HTC_HEADSET is not set +# CONFIG_HTC_35MM_JACK is not set +CONFIG_HTC_BATTCHG=y +CONFIG_HTC_BATTCHG_SMEM=y +# CONFIG_HTC_PWRSPLY is not set +# CONFIG_HTC_PWRSINK is not set +# CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set +# CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_QSD_SVS=y +CONFIG_QSD_PMIC_DEFAULT_DCDC1=1275 +CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_PHYS_OFFSET=0x20000000 +CONFIG_MSM7X00A_USE_GP_TIMER=y +# CONFIG_MSM7X00A_USE_DG_TIMER is not set +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=2 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=20000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_FIQ_SUPPORT=y +CONFIG_MSM_SERIAL_DEBUGGER=y +CONFIG_MSM_SERIAL_DEBUGGER_NO_SLEEP=y +# CONFIG_MSM_SERIAL_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON is not set +# CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_DAL=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y +CONFIG_MSM_CPU_FREQ_MAX=998000 +CONFIG_MSM_CPU_FREQ_MIN=245760 +# CONFIG_AXI_SCREEN_POLICY is not set +CONFIG_MSM_CPU_AVS=y +CONFIG_MSM_AVS_HW=y +CONFIG_HTC_ACOUSTIC_QSD=y +CONFIG_MSM_QDSP6=y +CONFIG_WIFI_CONTROL_FUNC=y +# CONFIG_WIFI_MEM_PREALLOC is not set +CONFIG_ARCH_MSM_FLASHLIGHT=y +CONFIG_MICROP_COMMON=y +CONFIG_HTC_HEADSET_MGR=y +CONFIG_HTC_HEADSET_GPIO=y +CONFIG_HTC_HEADSET_MICROP=y +# CONFIG_VIRTUAL_KPANIC_PARTITION is not set + +# +# System MMU +# + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_VERIFY_PERMISSION_FAULT=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_CACHE_ERR_REPORT is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +# CONFIG_VCM is not set +# CONFIG_STRICT_MEMORY_RWX is not set +# CONFIG_RESERVE_FIRST_PAGE is not set +CONFIG_CPU_HAS_PMU=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_KSAPI is not set +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_VMALLOC_RESERVE=0x08000000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_NEED_PER_CPU_KM=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_CP_ACCESS is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM0" +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +# CONFIG_CPU_FREQ_GOV_SMARTASS2 is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM_OPS=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +CONFIG_INET_ESP=y +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_NETLABEL is not set +# CONFIG_ANDROID_PARANOID_NETWORK is not set +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NETFILTER_XT_TARGET_HL=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_LED=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +CONFIG_NETFILTER_XT_MATCH_POLICY=y +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +# CONFIG_NF_NAT_SNMP_BASIC is not set +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +CONFIG_NF_NAT_H323=y +CONFIG_NF_NAT_SIP=y +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y +# CONFIG_IP_NF_SECURITY is not set +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK_IPV6=y +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_TARGET_HL=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +# CONFIG_IP6_NF_SECURITY is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +CONFIG_NET_SCH_INGRESS=y + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +# CONFIG_GACT_PROB is not set +CONFIG_NET_ACT_MIRRED=y +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +CONFIG_DNS_RESOLVER=y +# CONFIG_BATMAN_ADV is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NET_DROP_MONITOR is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_CFG80211=y +CONFIG_NL80211_TESTMODE=y +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_DEBUGFS is not set +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +# CONFIG_LIB80211 is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +# CONFIG_MAC80211 is not set + +# +# Some wireless drivers require a rate control algorithm +# +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_DEVTMPFS=y +# CONFIG_DEVTMPFS_MOUNT is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_MSM_NAND=y +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_CRYPTOLOOP=y + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set +CONFIG_MISC_DEVICES=y +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_KERNEL_DEBUGGER_CORE=y +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +CONFIG_SENSORS_AKM8973=y +# CONFIG_SENSORS_AKM8976 is not set +# CONFIG_VP_A1026 is not set +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_SENSORS_BMA150_SPI=y +# CONFIG_APANIC is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=y +# CONFIG_DM_DEBUG is not set +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_IFB=m +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set +CONFIG_MII=y +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set +# CONFIG_ENC28J60 is not set +# CONFIG_ETHOC is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +CONFIG_WLAN=y +# CONFIG_ATH_COMMON is not set +# CONFIG_BCM4329 is not set +CONFIG_BCMDHD=y +CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" +CONFIG_BCMDHD_NVRAM_PATH="/proc/calibration" +# CONFIG_DHD_USE_STATIC_BUF is not set +# CONFIG_DHD_USE_SCHED_SCAN is not set +# CONFIG_DHD_ENABLE_P2P is not set +# CONFIG_HOSTAP is not set +# CONFIG_IWM is not set +# CONFIG_LIBERTAS is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set + +# +# CAIF transport drivers +# +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +# CONFIG_PPPOE is not set +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_GAN_ETH=y +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL=y +CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT=y +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELAN_I2C_8232 is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_QT602240 is not set +# CONFIG_TOUCHSCREEN_MSM is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set +# CONFIG_INPUT_CAPELLA_CM3602 is not set +# CONFIG_INPUT_CAPELLA_CM3602_HTC is not set +CONFIG_LIGHTSENSOR_MICROP=y +CONFIG_INPUT_OPTICALJOYSTICK=y +CONFIG_OPTICALJOYSTICK_CRUCIAL=y +CONFIG_OPTICALJOYSTICK_CRUCIAL_uP=y +# CONFIG_OPTICALJOYSTICK_CRUCIAL_SPI is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_MSM=y +# CONFIG_SERIAL_MSM_CONSOLE is not set +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_BCM_BT_LPM=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_IFX6X60 is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_MSM=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_QSD=y +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_BASIC_MMIO is not set +# CONFIG_GPIO_IT8761E is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_DS2784 is not set +# CONFIG_BATTERY_BQ20Z75 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_MFD_SUPPORT=y +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_TPS65200 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_WL1273_CORE is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +CONFIG_REGULATOR_TPS65023=y +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_TPS6524X is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +CONFIG_RC_CORE=y +CONFIG_LIRC=y +CONFIG_RC_MAP=y +CONFIG_IR_NEC_DECODER=y +CONFIG_IR_RC5_DECODER=y +CONFIG_IR_RC6_DECODER=y +CONFIG_IR_JVC_DECODER=y +CONFIG_IR_SONY_DECODER=y +CONFIG_IR_RC5_SZ_DECODER=y +CONFIG_IR_LIRC_CODEC=y +# CONFIG_IR_IMON is not set +# CONFIG_IR_MCEUSB is not set +# CONFIG_IR_STREAMZAP is not set +# CONFIG_RC_LOOPBACK is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +CONFIG_720P_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set + +# +# Camera Sensor Selection +# +# CONFIG_MT9T013 is not set +# CONFIG_MT9D112 is not set +# CONFIG_MT9P012 is not set +# CONFIG_S5K3E2FX is not set +# CONFIG_S5K6AAFX is not set +CONFIG_OV8810=y +# CONFIG_OV9665 is not set +# CONFIG_S5K3H1GX is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +# CONFIG_ION is not set +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y +# CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_LEGACY_MDP=y +CONFIG_FB_MSM_MDP_PPP=y +CONFIG_FB_MSM_LCDC=y +CONFIG_FB_MSM_MDDI=y +# CONFIG_FB_MSM_MDDI_EPSON is not set +# CONFIG_FB_MSM_MDDI_NOVTEC is not set +# CONFIG_MSM_HDMI is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set +CONFIG_UHID=y +# CONFIG_HID_PID is not set + +# +# Special HID drivers +# +CONFIG_HID_APPLE=y +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_WACOM is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +# CONFIG_USB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA_U2O is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_CI13XXX_MSM is not set +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_MSM_72K=y +CONFIG_USB_MSM_72K_HTC=y +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_ANDROID=y +# CONFIG_USB_ANDROID_ACM is not set +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_DIAG=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +# CONFIG_USB_ANDROID_MTP is not set +CONFIG_USB_ANDROID_RNDIS=y +# CONFIG_USB_ANDROID_RNDIS_WCEIS is not set +# CONFIG_USB_ANDROID_ACCESSORY is not set +CONFIG_USB_CSW_HACK=y +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_ACCESSORY_DETECT is not set +# CONFIG_DOCK_ACCESSORY_DETECT is not set +# CONFIG_USB_BYPASS_VBUS_NOTIFY is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG_72K is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_CLKGATE is not set +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_MSM=y +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +CONFIG_LEDS_CPLD=y +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +CONFIG_LEDS_TRIGGER_SLEEP=y + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_NFC_DEVICES is not set +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_MSM7X00A=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_ECHO is not set +# CONFIG_BRCM80211 is not set +# CONFIG_COMEDI is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE=128 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE=16 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE=8 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_POHMELFS is not set +# CONFIG_IIO is not set +CONFIG_XVMALLOC=y +CONFIG_ZRAM=y +# CONFIG_ZRAM_DEBUG is not set +# CONFIG_FB_SM7XX is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_ST_BT is not set +# CONFIG_LIRC_STAGING is not set +CONFIG_MACH_NO_WESTBRIDGE=y +# CONFIG_ATH6K_LEGACY is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_AFFS_FS is not set +# CONFIG_ECRYPT_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=y +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_WEAK_PW_HASH=y +# CONFIG_CIFS_UPCALL is not set +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_DFS_UPCALL is not set +# CONFIG_CIFS_ACL is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +CONFIG_SCHED_DEBUG=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_BKL is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_NOP_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACER_MAX_TRACE=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_EVENT_POWER_TRACING_DEPRECATED=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +CONFIG_SCHED_TRACER=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_ARM_UNWIND=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +CONFIG_KEYS=y +# CONFIG_KEYS_DEBUG_PROC_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +CONFIG_SECURITY=y +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_NETWORK is not set +# CONFIG_SECURITY_PATH is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_APPARMOR is not set +# CONFIG_IMA is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +CONFIG_BINARY_PRINTF=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig b/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig new file mode 100644 index 0000000000000..c0e3f85697e2e --- /dev/null +++ b/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig @@ -0,0 +1,2192 @@ +# +# Automatically generated make config: don't edit +# Linux/arm 2.6.38.8 Kernel Configuration +# Fri Feb 22 10:26:50 2013 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="-evervolv-perdo-jellybean" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED is not set +CONFIG_HAVE_SPARSE_IRQ=y +# CONFIG_GENERIC_PENDING_IRQ is not set +# CONFIG_AUTO_IRQ_AFFINITY is not set +# CONFIG_IRQ_PER_CPU is not set +# CONFIG_SPARSE_IRQ is not set + +# +# RCU Subsystem +# +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +CONFIG_TINY_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_TRACE is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_RCU_BOOST=y +CONFIG_RCU_BOOST_PRIO=1 +CONFIG_RCU_BOOST_DELAY=500 +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_CGROUPS=y +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CGROUP_NS is not set +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +# CONFIG_CGROUP_MEM_RES_CTLR is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_CGROUP=y +# CONFIG_DEBUG_BLK_CGROUP is not set +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_IPC_NS=y +CONFIG_USER_NS=y +CONFIG_PID_NS=y +CONFIG_NET_NS=y +CONFIG_SCHED_AUTOGROUP=y +# CONFIG_SYSFS_DEPRECATED is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +# CONFIG_KALLSYMS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_ASHMEM=y +CONFIG_AIO=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_TRACEPOINTS=y +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set +# CONFIG_BLK_DEV_THROTTLING is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CFQ_GROUP_IOSCHED=y +CONFIG_IOSCHED_BFQ=y +CONFIG_CGROUP_BFQIO=y +CONFIG_DEFAULT_DEADLINE=y +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_BFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="deadline" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +CONFIG_ARCH_MSM=y +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_S5PV310 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_TCC_926 is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_ARCH_MSM7X00A is not set +# CONFIG_ARCH_MSM7X30 is not set +CONFIG_ARCH_QSD8X50=y +# CONFIG_ARCH_MSM8X60 is not set +CONFIG_ARCH_MSM_SCORPION=y +CONFIG_HAS_MSM_DEBUG_UART_PHYS=y +CONFIG_MSM_MDP31=y +# CONFIG_PERFLOCK is not set + +# +# Qualcomm MSM Board Type +# +# CONFIG_MACH_SWORDFISH is not set +CONFIG_MACH_QSD8X50_SURF=y +# CONFIG_MACH_QSD8X50A_ST1_5 is not set +CONFIG_MSM_DEBUG_UART=1 +# CONFIG_MSM_DEBUG_UART_NONE is not set +CONFIG_MSM_DEBUG_UART1=y +# CONFIG_MSM_DEBUG_UART2 is not set +# CONFIG_MSM_DEBUG_UART3 is not set +CONFIG_MSM_PROC_COMM=y +CONFIG_MACH_MAHIMAHI=y +CONFIG_MACH_BRAVO_NONE=y +# CONFIG_MACH_BRAVO is not set +# CONFIG_MACH_BRAVOC is not set +# CONFIG_MACH_INCREDIBLE is not set +# CONFIG_MACH_INCREDIBLEC is not set +# CONFIG_MACH_SUPERSONIC is not set +# CONFIG_MACH_QSD8X50_FFA is not set +# CONFIG_HTC_HEADSET is not set +CONFIG_HTC_35MM_JACK=y +# CONFIG_HTC_BATTCHG is not set +CONFIG_HTC_PWRSPLY=y +# CONFIG_HTC_PWRSINK is not set +# CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set +# CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_QSD_SVS=y +CONFIG_QSD_PMIC_DEFAULT_DCDC1=1275 +CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_PHYS_OFFSET=0x20000000 +CONFIG_MSM7X00A_USE_GP_TIMER=y +# CONFIG_MSM7X00A_USE_DG_TIMER is not set +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=1 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_FIQ_SUPPORT=y +# CONFIG_MSM_SERIAL_DEBUGGER is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_DAL=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y +CONFIG_MSM_CPU_FREQ_MAX=998400 +CONFIG_MSM_CPU_FREQ_MIN=245000 +# CONFIG_AXI_SCREEN_POLICY is not set +CONFIG_MSM_CPU_AVS=y +CONFIG_MSM_AVS_HW=y +CONFIG_HTC_ACOUSTIC_QSD=y +CONFIG_MSM_QDSP6=y +CONFIG_WIFI_CONTROL_FUNC=y +# CONFIG_WIFI_MEM_PREALLOC is not set +CONFIG_ARCH_MSM_FLASHLIGHT=y +# CONFIG_MICROP_COMMON is not set +# CONFIG_HTC_HEADSET_MGR is not set +# CONFIG_VIRTUAL_KPANIC_PARTITION is not set + +# +# System MMU +# + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_VERIFY_PERMISSION_FAULT=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_CACHE_ERR_REPORT is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +# CONFIG_VCM is not set +# CONFIG_STRICT_MEMORY_RWX is not set +# CONFIG_RESERVE_FIRST_PAGE is not set +CONFIG_CPU_HAS_PMU=y +CONFIG_ARM_ERRATA_430973=y +CONFIG_ARM_ERRATA_458693=y +CONFIG_ARM_ERRATA_460075=y +CONFIG_ARM_ERRATA_743622=y +# CONFIG_KSAPI is not set +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_VMALLOC_RESERVE=0x08000000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_NEED_PER_CPU_KM=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_CP_ACCESS is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM0" +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +# CONFIG_CPU_FREQ_GOV_SMARTASS2 is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM_OPS=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +CONFIG_INET_ESP=y +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_NETLABEL is not set +# CONFIG_ANDROID_PARANOID_NETWORK is not set +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NETFILTER_XT_TARGET_HL=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_LED=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +CONFIG_NETFILTER_XT_MATCH_POLICY=y +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +# CONFIG_NF_NAT_SNMP_BASIC is not set +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +CONFIG_NF_NAT_H323=y +CONFIG_NF_NAT_SIP=y +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y +# CONFIG_IP_NF_SECURITY is not set +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK_IPV6=y +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_TARGET_HL=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +# CONFIG_IP6_NF_SECURITY is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +CONFIG_NET_SCH_INGRESS=y + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +# CONFIG_GACT_PROB is not set +CONFIG_NET_ACT_MIRRED=y +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NET_DROP_MONITOR is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_CFG80211=y +CONFIG_NL80211_TESTMODE=y +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_DEBUGFS is not set +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +# CONFIG_LIB80211 is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +# CONFIG_MAC80211 is not set + +# +# Some wireless drivers require a rate control algorithm +# +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_DEVTMPFS=y +# CONFIG_DEVTMPFS_MOUNT is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_MSM_NAND=y +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_CRYPTOLOOP=y + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set +CONFIG_MISC_DEVICES=y +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_KERNEL_DEBUGGER_CORE=y +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +CONFIG_SENSORS_AKM8973=y +# CONFIG_SENSORS_AKM8976 is not set +CONFIG_VP_A1026=y +# CONFIG_DS1682 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_WL127X_RFKILL is not set +# CONFIG_APANIC is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_IFB=y +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set +CONFIG_MII=y +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set +# CONFIG_ETHOC is not set +CONFIG_SMC911X=y +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8851_MLL is not set +CONFIG_NETDEV_1000=y +# CONFIG_STMMAC_ETH is not set +CONFIG_NETDEV_10000=y +CONFIG_WLAN=y +# CONFIG_ATH_COMMON is not set +# CONFIG_BCM4329 is not set +CONFIG_BCMDHD=y +CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" +CONFIG_BCMDHD_NVRAM_PATH="/proc/calibration" +# CONFIG_DHD_USE_STATIC_BUF is not set +# CONFIG_DHD_USE_SCHED_SCAN is not set +# CONFIG_DHD_ENABLE_P2P is not set +# CONFIG_HOSTAP is not set +# CONFIG_IWM is not set +# CONFIG_LIBERTAS is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set + +# +# CAIF transport drivers +# +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +# CONFIG_PPPOE is not set +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_GAN_ETH=y +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_ATMEL is not set +# CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELAN_I2C_8232 is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_QT602240 is not set +# CONFIG_TOUCHSCREEN_MSM is not set +CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI=y +CONFIG_TOUCHSCREEN_DUPLICATED_FILTER=y +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set +CONFIG_INPUT_CAPELLA_CM3602=y +# CONFIG_INPUT_CAPELLA_CM3602_HTC is not set +# CONFIG_LIGHTSENSOR_MICROP is not set +# CONFIG_INPUT_OPTICALJOYSTICK is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_BCM_BT_LPM=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_MSM=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_SPI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_BASIC_MMIO is not set +# CONFIG_GPIO_IT8761E is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +CONFIG_W1=y + +# +# 1-wire Bus Masters +# +CONFIG_W1_MASTER_DS2482=y +# CONFIG_W1_MASTER_DS1WM is not set +# CONFIG_W1_MASTER_GPIO is not set + +# +# 1-wire Slaves +# +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_DS2423 is not set +# CONFIG_W1_SLAVE_DS2431 is not set +# CONFIG_W1_SLAVE_DS2433 is not set +# CONFIG_W1_SLAVE_DS2760 is not set +# CONFIG_W1_SLAVE_BQ27000 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2782 is not set +CONFIG_BATTERY_DS2784=y +# CONFIG_BATTERY_BQ20Z75 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_MFD_SUPPORT=y +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_TPS65200 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_WL1273_CORE is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +CONFIG_REGULATOR_TPS65023=y +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_AD5398 is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +CONFIG_RC_CORE=y +CONFIG_LIRC=y +CONFIG_RC_MAP=y +CONFIG_IR_NEC_DECODER=y +CONFIG_IR_RC5_DECODER=y +CONFIG_IR_RC6_DECODER=y +CONFIG_IR_JVC_DECODER=y +CONFIG_IR_SONY_DECODER=y +CONFIG_IR_RC5_SZ_DECODER=y +CONFIG_IR_LIRC_CODEC=y +# CONFIG_IR_IMON is not set +# CONFIG_IR_MCEUSB is not set +# CONFIG_IR_STREAMZAP is not set +# CONFIG_RC_LOOPBACK is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +CONFIG_720P_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set + +# +# Camera Sensor Selection +# +# CONFIG_MT9T013 is not set +# CONFIG_MT9D112 is not set +# CONFIG_MT9P012 is not set +CONFIG_S5K3E2FX=y +# CONFIG_S5K6AAFX is not set +# CONFIG_OV8810 is not set +# CONFIG_OV9665 is not set +# CONFIG_S5K3H1GX is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +# CONFIG_ION is not set +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y +# CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_LEGACY_MDP=y +CONFIG_FB_MSM_MDP_PPP=y +CONFIG_FB_MSM_LCDC=y +# CONFIG_FB_MSM_MDDI is not set +# CONFIG_MSM_HDMI is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set +CONFIG_UHID=y +# CONFIG_HID_PID is not set + +# +# Special HID drivers +# +CONFIG_HID_APPLE=y +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_WACOM is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +# CONFIG_USB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA_U2O is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_CI13XXX_MSM is not set +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_MSM_72K=y +# CONFIG_USB_MSM_72K_HTC is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_ANDROID=y +# CONFIG_USB_ANDROID_ACM is not set +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_DIAG=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +# CONFIG_USB_ANDROID_MTP is not set +CONFIG_USB_ANDROID_RNDIS=y +CONFIG_USB_ANDROID_RNDIS_WCEIS=y +CONFIG_USB_ANDROID_ACCESSORY=y +CONFIG_USB_CSW_HACK=y +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG_72K is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_CLKGATE is not set +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_MSM=y +# CONFIG_MMC_DW is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +CONFIG_LEDS_CPLD=y +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +CONFIG_LEDS_TRIGGER_SLEEP=y + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_NFC_DEVICES is not set +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_MSM7X00A=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_ECHO is not set +# CONFIG_BRCM80211 is not set +# CONFIG_COMEDI is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE=128 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE=16 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE=8 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_POHMELFS is not set +# CONFIG_IIO is not set +CONFIG_XVMALLOC=y +CONFIG_ZRAM=y +# CONFIG_ZRAM_DEBUG is not set +# CONFIG_FB_SM7XX is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_ST_BT is not set +# CONFIG_LIRC_STAGING is not set +CONFIG_MACH_NO_WESTBRIDGE=y +# CONFIG_ATH6K_LEGACY is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=y +# CONFIG_CUSE is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=y +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_WEAK_PW_HASH=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_ACL is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +CONFIG_SCHED_DEBUG=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_BKL is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_NOP_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACER_MAX_TRACE=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_EVENT_POWER_TRACING_DEPRECATED=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +CONFIG_SCHED_TRACER=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_ARM_UNWIND is not set +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_ERRORS=y +CONFIG_DEBUG_STACK_USAGE=y +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +CONFIG_SECURITY=y +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_NETWORK is not set +# CONFIG_SECURITY_PATH is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_APPARMOR is not set +# CONFIG_IMA is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +CONFIG_BINARY_PRINTF=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y diff --git a/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig b/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig new file mode 100644 index 0000000000000..d651b57a0ea72 --- /dev/null +++ b/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig @@ -0,0 +1,2212 @@ +# +# Automatically generated make config: don't edit +# Linux/arm 2.6.38.8 Kernel Configuration +# Sat Feb 23 23:53:02 2013 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="-evervolv-acies-jellybean" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +CONFIG_KERNEL_LZMA=y +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED is not set +CONFIG_HAVE_SPARSE_IRQ=y +# CONFIG_GENERIC_PENDING_IRQ is not set +# CONFIG_AUTO_IRQ_AFFINITY is not set +# CONFIG_IRQ_PER_CPU is not set +# CONFIG_SPARSE_IRQ is not set + +# +# RCU Subsystem +# +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +CONFIG_TINY_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_TRACE is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_RCU_BOOST=y +CONFIG_RCU_BOOST_PRIO=1 +CONFIG_RCU_BOOST_DELAY=500 +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_CGROUPS=y +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CGROUP_NS is not set +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +# CONFIG_CGROUP_MEM_RES_CTLR is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_CGROUP=y +# CONFIG_DEBUG_BLK_CGROUP is not set +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_IPC_NS=y +CONFIG_USER_NS=y +CONFIG_PID_NS=y +CONFIG_NET_NS=y +CONFIG_SCHED_AUTOGROUP=y +# CONFIG_SYSFS_DEPRECATED is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_ASHMEM=y +CONFIG_AIO=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_TRACEPOINTS=y +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +CONFIG_BLK_DEV_BSG=y +CONFIG_BLK_DEV_INTEGRITY=y +CONFIG_BLK_DEV_THROTTLING=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_IOSCHED_BFQ=y +CONFIG_CGROUP_BFQIO=y +CONFIG_DEFAULT_BFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="bfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +CONFIG_ARCH_MSM=y +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_S5PV310 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_TCC_926 is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_ARCH_MSM7X00A is not set +# CONFIG_ARCH_MSM7X30 is not set +CONFIG_ARCH_QSD8X50=y +# CONFIG_ARCH_MSM8X60 is not set +CONFIG_ARCH_MSM_SCORPION=y +CONFIG_HAS_MSM_DEBUG_UART_PHYS=y +CONFIG_MSM_MDP31=y +# CONFIG_PERFLOCK is not set + +# +# Qualcomm MSM Board Type +# +# CONFIG_MACH_SWORDFISH is not set +CONFIG_MACH_QSD8X50_SURF=y +# CONFIG_MACH_QSD8X50A_ST1_5 is not set +CONFIG_MSM_DEBUG_UART_NONE=y +# CONFIG_MSM_DEBUG_UART1 is not set +# CONFIG_MSM_DEBUG_UART2 is not set +# CONFIG_MSM_DEBUG_UART3 is not set +CONFIG_MSM_PROC_COMM=y +# CONFIG_MACH_MAHIMAHI is not set +CONFIG_MACH_BRAVO_NONE=y +# CONFIG_MACH_BRAVO is not set +# CONFIG_MACH_BRAVOC is not set +# CONFIG_MACH_INCREDIBLE is not set +# CONFIG_MACH_INCREDIBLEC is not set +CONFIG_MACH_SUPERSONIC=y +# CONFIG_MACH_QSD8X50_FFA is not set +# CONFIG_HTC_HEADSET is not set +# CONFIG_HTC_35MM_JACK is not set +CONFIG_HTC_BATTCHG=y +CONFIG_HTC_BATTCHG_SMEM=y +# CONFIG_HTC_PWRSPLY is not set +# CONFIG_HTC_PWRSINK is not set +# CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set +# CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_QSD_SVS=y +CONFIG_QSD_PMIC_DEFAULT_DCDC1=1275 +CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_PHYS_OFFSET=0x20000000 +CONFIG_MSM7X00A_USE_GP_TIMER=y +# CONFIG_MSM7X00A_USE_DG_TIMER is not set +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=1 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=50000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_FIQ_SUPPORT=y +# CONFIG_MSM_SERIAL_DEBUGGER is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_DAL=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y +CONFIG_MSM_CPU_FREQ_MAX=998400 +CONFIG_MSM_CPU_FREQ_MIN=245000 +# CONFIG_AXI_SCREEN_POLICY is not set +CONFIG_MSM_CPU_AVS=y +CONFIG_MSM_AVS_HW=y +CONFIG_HTC_ACOUSTIC_QSD=y +CONFIG_MSM_QDSP6=y +CONFIG_WIFI_CONTROL_FUNC=y +CONFIG_WIFI_MEM_PREALLOC=y +# CONFIG_ARCH_MSM_FLASHLIGHT is not set +CONFIG_MICROP_COMMON=y +CONFIG_HTC_HEADSET_MGR=y +CONFIG_HTC_HEADSET_GPIO=y +CONFIG_HTC_HEADSET_MICROP=y +# CONFIG_VIRTUAL_KPANIC_PARTITION is not set + +# +# System MMU +# + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_VERIFY_PERMISSION_FAULT=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_CACHE_ERR_REPORT is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +# CONFIG_VCM is not set +# CONFIG_STRICT_MEMORY_RWX is not set +# CONFIG_RESERVE_FIRST_PAGE is not set +CONFIG_CPU_HAS_PMU=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_KSAPI is not set +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_VMALLOC_RESERVE=0x08000000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_NEED_PER_CPU_KM=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_CP_ACCESS is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM0" +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +# CONFIG_CPU_FREQ_GOV_SMARTASS2 is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM_OPS=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_ASK_IP_FIB_HASH=y +# CONFIG_IP_FIB_TRIE is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_IP_ROUTE_MULTIPATH is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_XFRM_TUNNEL=y +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_NETLABEL is not set +# CONFIG_ANDROID_PARANOID_NETWORK is not set +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +# CONFIG_NF_CONNTRACK_H323 is not set +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NETFILTER_XT_TARGET_HL=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_LED=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +# CONFIG_NETFILTER_XT_TARGET_NOTRACK is not set +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +# CONFIG_NETFILTER_XT_TARGET_TRACE is not set +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +CONFIG_NETFILTER_XT_MATCH_POLICY=y +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +CONFIG_NETFILTER_XT_MATCH_RECENT=y +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +# CONFIG_NF_NAT_SNMP_BASIC is not set +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +# CONFIG_NF_NAT_H323 is not set +CONFIG_NF_NAT_SIP=y +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y +# CONFIG_IP_NF_SECURITY is not set +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK_IPV6=y +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_TARGET_HL=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +# CONFIG_IP6_NF_SECURITY is not set +# CONFIG_BRIDGE_NF_EBTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +CONFIG_NET_SCH_INGRESS=y + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +# CONFIG_GACT_PROB is not set +CONFIG_NET_ACT_MIRRED=y +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NET_DROP_MONITOR is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_CFG80211=y +CONFIG_NL80211_TESTMODE=y +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_DEBUGFS is not set +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +# CONFIG_LIB80211 is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +# CONFIG_MAC80211 is not set + +# +# Some wireless drivers require a rate control algorithm +# +CONFIG_WIMAX=y +CONFIG_WIMAX_DEBUG_LEVEL=8 +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_DEVTMPFS=y +# CONFIG_DEVTMPFS_MOUNT is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_MSM_NAND=y +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_CRYPTOLOOP=y + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set +CONFIG_MISC_DEVICES=y +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_KERNEL_DEBUGGER_CORE=y +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +CONFIG_SENSORS_AKM8973=y +# CONFIG_SENSORS_AKM8976 is not set +CONFIG_VP_A1026=y +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_SENSORS_BMA150_SPI=y +# CONFIG_APANIC is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=y +CONFIG_DM_DEBUG=y +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +# CONFIG_IFB is not set +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set +CONFIG_MII=y +# CONFIG_PHYLIB is not set +# CONFIG_NET_ETHERNET is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +CONFIG_WLAN=y +# CONFIG_ATH_COMMON is not set +# CONFIG_BCM4329 is not set +CONFIG_BCMDHD=y +CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" +CONFIG_BCMDHD_NVRAM_PATH="/proc/calibration" +# CONFIG_DHD_USE_STATIC_BUF is not set +# CONFIG_DHD_USE_SCHED_SCAN is not set +# CONFIG_DHD_ENABLE_P2P is not set +# CONFIG_HOSTAP is not set +# CONFIG_IWM is not set +# CONFIG_LIBERTAS is not set + +# +# WiMAX Wireless Broadband devices +# + +# +# Enable USB support to see WiMAX USB drivers +# +# CONFIG_WIMAX_I2400M_SDIO is not set +CONFIG_WIMAX_SQN=m +# CONFIG_WAN is not set + +# +# CAIF transport drivers +# +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +# CONFIG_PPPOE is not set +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_GAN_ETH=y +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL=y +CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT=y +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELAN_I2C_8232 is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_QT602240 is not set +# CONFIG_TOUCHSCREEN_MSM is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set +# CONFIG_INPUT_CAPELLA_CM3602 is not set +# CONFIG_INPUT_CAPELLA_CM3602_HTC is not set +CONFIG_LIGHTSENSOR_MICROP=y +# CONFIG_INPUT_OPTICALJOYSTICK is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_MSM=y +CONFIG_SERIAL_MSM_CONSOLE=y +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_BCM_BT_LPM=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_IFX6X60 is not set +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_MSM=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +CONFIG_SPI_DEBUG=y +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_QSD=y +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_BASIC_MMIO is not set +# CONFIG_GPIO_IT8761E is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_DS2784 is not set +# CONFIG_BATTERY_BQ20Z75 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_MFD_SUPPORT=y +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +CONFIG_TPS65200=y +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_WL1273_CORE is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +CONFIG_REGULATOR_TPS65023=y +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_TPS6524X is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +# CONFIG_RC_CORE is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +CONFIG_720P_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set + +# +# Camera Sensor Selection +# +# CONFIG_MT9T013 is not set +# CONFIG_MT9D112 is not set +# CONFIG_MT9P012 is not set +# CONFIG_S5K3E2FX is not set +CONFIG_S5K6AAFX=y +CONFIG_OV8810=y +CONFIG_OV9665=y +CONFIG_S5K3H1GX=y + +# +# Graphics support +# +# CONFIG_DRM is not set +# CONFIG_ION is not set +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y +# CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_LEGACY_MDP=y +CONFIG_FB_MSM_MDP_PPP=y +CONFIG_FB_MSM_LCDC=y +CONFIG_FB_MSM_MDDI=y +CONFIG_FB_MSM_MDDI_EPSON=y +CONFIG_FB_MSM_MDDI_NOVTEC=y +CONFIG_MSM_HDMI=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set +CONFIG_UHID=y +# CONFIG_HID_PID is not set + +# +# Special HID drivers +# +CONFIG_HID_APPLE=y +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_WACOM is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +# CONFIG_USB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA_U2O is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_CI13XXX_MSM is not set +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_MSM_72K=y +CONFIG_USB_MSM_72K_HTC=y +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_ANDROID=y +# CONFIG_USB_ANDROID_ACM is not set +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_DIAG=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +# CONFIG_USB_ANDROID_MTP is not set +CONFIG_USB_ANDROID_RNDIS=y +CONFIG_USB_ANDROID_RNDIS_WCEIS=y +CONFIG_USB_ANDROID_ACCESSORY=y +CONFIG_USB_CSW_HACK=y +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +CONFIG_USB_ACCESSORY_DETECT=y +# CONFIG_USB_ACCESSORY_DETECT_BY_ADC is not set +CONFIG_DOCK_ACCESSORY_DETECT=y +# CONFIG_USB_BYPASS_VBUS_NOTIFY is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG_72K is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_CLKGATE is not set +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_MSM=y +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +CONFIG_LEDS_CPLD=y +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +CONFIG_LEDS_TRIGGER_SLEEP=y + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_NFC_DEVICES is not set +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_MSM7X00A=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_ECHO is not set +# CONFIG_BRCM80211 is not set +# CONFIG_COMEDI is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE=128 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE=16 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE=8 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_POHMELFS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_IIO is not set +CONFIG_XVMALLOC=y +CONFIG_ZRAM=y +# CONFIG_ZRAM_DEBUG is not set +# CONFIG_FB_SM7XX is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_ST_BT is not set +# CONFIG_SMB_FS is not set +CONFIG_MACH_NO_WESTBRIDGE=y +# CONFIG_ATH6K_LEGACY is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=y +# CONFIG_CUSE is not set + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=y +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_WEAK_PW_HASH=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_ACL is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +CONFIG_SCHED_DEBUG=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +CONFIG_DEBUG_MUTEXES=y +CONFIG_BKL=y +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_NOP_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACER_MAX_TRACE=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_EVENT_POWER_TRACING_DEPRECATED=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +CONFIG_SCHED_TRACER=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_ARM_UNWIND is not set +# CONFIG_DEBUG_USER is not set +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +CONFIG_SECURITY=y +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_NETWORK is not set +# CONFIG_SECURITY_PATH is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_APPARMOR is not set +# CONFIG_IMA is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +CONFIG_BINARY_PRINTF=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y From b0ca41dc12b4411fdd60f12558ed7401c69f22af Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Sat, 23 Feb 2013 23:57:25 -0600 Subject: [PATCH 2466/2556] Revert "config: mahimahi: update for ubuntu" This reverts commit a25190a989e08b4a90d907d030e1bb8292dbe927. --- arch/arm/configs/evervolv_mahimahi_defconfig | 36 ++++++-------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index c0e3f85697e2e..c416b2384309f 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Fri Feb 22 10:26:50 2013 +# Tue Oct 2 20:54:19 2012 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -44,8 +44,7 @@ CONFIG_HAVE_KERNEL_LZO=y CONFIG_KERNEL_XZ=y # CONFIG_KERNEL_LZO is not set CONFIG_SWAP=y -CONFIG_SYSVIPC=y -CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_SYSVIPC is not set # CONFIG_POSIX_MQUEUE is not set # CONFIG_BSD_PROCESS_ACCT is not set # CONFIG_TASKSTATS is not set @@ -92,12 +91,7 @@ CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y CONFIG_BLK_CGROUP=y # CONFIG_DEBUG_BLK_CGROUP is not set -CONFIG_NAMESPACES=y -CONFIG_UTS_NS=y -CONFIG_IPC_NS=y -CONFIG_USER_NS=y -CONFIG_PID_NS=y -CONFIG_NET_NS=y +# CONFIG_NAMESPACES is not set CONFIG_SCHED_AUTOGROUP=y # CONFIG_SYSFS_DEPRECATED is not set CONFIG_RELAY=y @@ -484,7 +478,7 @@ CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set -CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set # CONFIG_CPU_FREQ_GOV_POWERSAVE is not set # CONFIG_CPU_FREQ_GOV_USERSPACE is not set CONFIG_CPU_FREQ_GOV_ONDEMAND=y @@ -598,8 +592,7 @@ CONFIG_IPV6_TUNNEL=y CONFIG_IPV6_MULTIPLE_TABLES=y # CONFIG_IPV6_SUBTREES is not set # CONFIG_IPV6_MROUTE is not set -# CONFIG_NETLABEL is not set -# CONFIG_ANDROID_PARANOID_NETWORK is not set +CONFIG_ANDROID_PARANOID_NETWORK=y CONFIG_NET_ACTIVITY_STATS=y # CONFIG_NETWORK_SECMARK is not set # CONFIG_NETWORK_PHY_TIMESTAMPING is not set @@ -744,7 +737,6 @@ CONFIG_IP_NF_MANGLE=y # CONFIG_IP_NF_TARGET_ECN is not set # CONFIG_IP_NF_TARGET_TTL is not set CONFIG_IP_NF_RAW=y -# CONFIG_IP_NF_SECURITY is not set CONFIG_IP_NF_ARPTABLES=y CONFIG_IP_NF_ARPFILTER=y CONFIG_IP_NF_ARP_MANGLE=y @@ -770,7 +762,6 @@ CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y CONFIG_IP6_NF_RAW=y -# CONFIG_IP6_NF_SECURITY is not set # CONFIG_BRIDGE_NF_EBTABLES is not set # CONFIG_IP_DCCP is not set # CONFIG_IP_SCTP is not set @@ -916,8 +907,7 @@ CONFIG_RFKILL_LEDS=y # Generic Driver Options # CONFIG_UEVENT_HELPER_PATH="" -CONFIG_DEVTMPFS=y -# CONFIG_DEVTMPFS_MOUNT is not set +# CONFIG_DEVTMPFS is not set CONFIG_STANDALONE=y CONFIG_PREVENT_FIRMWARE_BUILD=y CONFIG_FW_LOADER=y @@ -1262,7 +1252,7 @@ CONFIG_SERIAL_BCM_BT_LPM=y # CONFIG_SERIAL_ALTERA_JTAGUART is not set # CONFIG_SERIAL_ALTERA_UART is not set CONFIG_UNIX98_PTYS=y -CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set # CONFIG_LEGACY_PTYS is not set # CONFIG_TTY_PRINTK is not set # CONFIG_HVC_DCC is not set @@ -1806,10 +1796,9 @@ CONFIG_FS_MBCACHE=y # CONFIG_FS_POSIX_ACL is not set CONFIG_FILE_LOCKING=y CONFIG_FSNOTIFY=y -CONFIG_DNOTIFY=y +# CONFIG_DNOTIFY is not set CONFIG_INOTIFY_USER=y -CONFIG_FANOTIFY=y -CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y +# CONFIG_FANOTIFY is not set # CONFIG_QUOTA is not set # CONFIG_QUOTACTL is not set # CONFIG_AUTOFS4_FS is not set @@ -2046,13 +2035,8 @@ CONFIG_DEBUG_STACK_USAGE=y # # CONFIG_KEYS is not set # CONFIG_SECURITY_DMESG_RESTRICT is not set -CONFIG_SECURITY=y +# CONFIG_SECURITY is not set # CONFIG_SECURITYFS is not set -# CONFIG_SECURITY_NETWORK is not set -# CONFIG_SECURITY_PATH is not set -# CONFIG_SECURITY_TOMOYO is not set -# CONFIG_SECURITY_APPARMOR is not set -# CONFIG_IMA is not set CONFIG_DEFAULT_SECURITY_DAC=y CONFIG_DEFAULT_SECURITY="" CONFIG_CRYPTO=y From ad166fb020f9baeb943b212aedde2b5ea9f6dd88 Mon Sep 17 00:00:00 2001 From: "andrew.boren" Date: Sun, 24 Feb 2013 16:51:47 -0700 Subject: [PATCH 2467/2556] supersonic: use xz compression on kernel. Change-Id: I657a6e7f505b2bce2e691485157ed4e51005c712 --- arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig b/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig index d651b57a0ea72..a9a8e5132918a 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig @@ -41,8 +41,8 @@ CONFIG_HAVE_KERNEL_LZMA=y CONFIG_HAVE_KERNEL_XZ=y CONFIG_HAVE_KERNEL_LZO=y # CONFIG_KERNEL_GZIP is not set -CONFIG_KERNEL_LZMA=y -# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y # CONFIG_KERNEL_LZO is not set CONFIG_SWAP=y CONFIG_SYSVIPC=y From d3764ddd21d02b216dd41062f88ec390c7561879 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Wed, 17 Apr 2013 20:27:45 -0500 Subject: [PATCH 2468/2556] kgsl: checkout jb_mr1_chocolate Change-Id: I9f62ec0837d14d2f8b0205d20f66282171305b62 --- drivers/gpu/msm/Makefile | 1 + drivers/gpu/msm/a2xx_reg.h | 2 +- drivers/gpu/msm/adreno.c | 316 ++++++--- drivers/gpu/msm/adreno.h | 38 +- drivers/gpu/msm/adreno_a2xx.c | 135 ++-- drivers/gpu/msm/adreno_a2xx_snapshot.c | 2 +- drivers/gpu/msm/adreno_a2xx_trace.c | 2 +- drivers/gpu/msm/adreno_a2xx_trace.h | 2 +- drivers/gpu/msm/adreno_debugfs.c | 23 +- drivers/gpu/msm/adreno_debugfs.h | 9 +- drivers/gpu/msm/adreno_drawctxt.c | 20 +- drivers/gpu/msm/adreno_drawctxt.h | 9 +- drivers/gpu/msm/adreno_pm4types.h | 21 +- drivers/gpu/msm/adreno_postmortem.c | 19 +- drivers/gpu/msm/adreno_postmortem.h | 18 +- drivers/gpu/msm/adreno_ringbuffer.c | 799 +++++++++++++++++----- drivers/gpu/msm/adreno_ringbuffer.h | 18 +- drivers/gpu/msm/adreno_snapshot.c | 2 +- drivers/gpu/msm/kgsl.c | 271 ++++---- drivers/gpu/msm/kgsl.h | 39 +- drivers/gpu/msm/kgsl_cffdump.c | 188 +---- drivers/gpu/msm/kgsl_cffdump.h | 2 +- drivers/gpu/msm/kgsl_debugfs.c | 2 +- drivers/gpu/msm/kgsl_debugfs.h | 2 +- drivers/gpu/msm/kgsl_device.h | 25 +- drivers/gpu/msm/kgsl_drm.c | 2 +- drivers/gpu/msm/kgsl_gpummu.c | 63 +- drivers/gpu/msm/kgsl_gpummu.h | 5 +- drivers/gpu/msm/kgsl_iommu.c | 9 +- drivers/gpu/msm/kgsl_log.h | 2 +- drivers/gpu/msm/kgsl_mmu.c | 46 +- drivers/gpu/msm/kgsl_mmu.h | 22 +- drivers/gpu/msm/kgsl_pwrctrl.c | 14 +- drivers/gpu/msm/kgsl_pwrctrl.h | 5 +- drivers/gpu/msm/kgsl_pwrscale.c | 2 +- drivers/gpu/msm/kgsl_pwrscale.h | 2 +- drivers/gpu/msm/kgsl_pwrscale_idlestats.c | 2 +- drivers/gpu/msm/kgsl_pwrscale_trustzone.c | 2 +- drivers/gpu/msm/kgsl_sharedmem.c | 7 +- drivers/gpu/msm/kgsl_sharedmem.h | 29 +- drivers/gpu/msm/kgsl_snapshot.c | 2 +- drivers/gpu/msm/kgsl_snapshot.h | 2 +- drivers/gpu/msm/kgsl_sync.c | 211 ++++++ drivers/gpu/msm/kgsl_sync.h | 75 ++ drivers/gpu/msm/kgsl_trace.c | 2 +- drivers/gpu/msm/kgsl_trace.h | 2 +- drivers/gpu/msm/z180.c | 53 +- drivers/gpu/msm/z180.h | 2 +- drivers/gpu/msm/z180_reg.h | 2 +- drivers/gpu/msm/z180_trace.c | 2 +- drivers/gpu/msm/z180_trace.h | 2 +- include/linux/msm_kgsl.h | 21 +- 52 files changed, 1762 insertions(+), 791 deletions(-) create mode 100644 drivers/gpu/msm/kgsl_sync.c create mode 100644 drivers/gpu/msm/kgsl_sync.h diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile index 7b8f3e633d1bd..65774c34ae19e 100644 --- a/drivers/gpu/msm/Makefile +++ b/drivers/gpu/msm/Makefile @@ -16,6 +16,7 @@ msm_kgsl_core-$(CONFIG_MSM_KGSL_CFF_DUMP) += kgsl_cffdump.o msm_kgsl_core-$(CONFIG_MSM_KGSL_DRM) += kgsl_drm.o msm_kgsl_core-$(CONFIG_MSM_SCM) += kgsl_pwrscale_trustzone.o msm_kgsl_core-$(CONFIG_MSM_SLEEP_STATS_DEVICE) += kgsl_pwrscale_idlestats.o +msm_kgsl_core-$(CONFIG_SYNC) += kgsl_sync.o msm_adreno-y += \ adreno_ringbuffer.o \ diff --git a/drivers/gpu/msm/a2xx_reg.h b/drivers/gpu/msm/a2xx_reg.h index 50b2745bed0fd..5a811d5fafd6e 100644 --- a/drivers/gpu/msm/a2xx_reg.h +++ b/drivers/gpu/msm/a2xx_reg.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index f52543dc15b6e..4682604a3db63 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -114,6 +114,7 @@ static struct adreno_device device_3d0 = { .pfp_fw = NULL, .pm4_fw = NULL, .wait_timeout = 10000, /* in milliseconds */ + .ib_check_level = 0, }; @@ -265,6 +266,7 @@ static int adreno_setup_pt(struct kgsl_device *device, } static void adreno_setstate(struct kgsl_device *device, + unsigned int context_id, uint32_t flags) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); @@ -272,6 +274,8 @@ static void adreno_setstate(struct kgsl_device *device, unsigned int *cmds = &link[0]; int sizedwords = 0; unsigned int mh_mmu_invalidate = 0x00000003; /*invalidate all and tc */ + struct kgsl_context *context; + struct adreno_context *adreno_ctx = NULL; /* * If possible, then set the state via the command stream to avoid @@ -279,7 +283,10 @@ static void adreno_setstate(struct kgsl_device *device, * writes For CFF dump we must idle and use the registers so that it is * easier to filter out the mmu accesses from the dump */ - if (adreno_dev->drawctxt_active) { + if (!kgsl_cff_dump_enable && adreno_dev->drawctxt_active) { + context = idr_find(&device->context_idr, context_id); + adreno_ctx = context->devctxt; + if (flags & KGSL_MMUFLAGS_PTUPDATE) { /* wait for graphics pipe to be idle */ *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); @@ -352,7 +359,8 @@ static void adreno_setstate(struct kgsl_device *device, sizedwords += 2; } - adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, + adreno_ringbuffer_issuecmds(device, adreno_ctx, + KGSL_CMD_FLAGS_PMODE, &link[0], sizedwords); } else { kgsl_mmu_device_setstate(device, flags); @@ -462,6 +470,8 @@ adreno_probe(struct platform_device *pdev) kgsl_pwrscale_init(device); kgsl_pwrscale_attach_policy(device, ADRENO_DEFAULT_PWRSCALE_POLICY); + INIT_WORK(&device->print_fault_ib, adreno_print_fault_ib_work); + device->flags &= ~KGSL_FLAGS_SOFT_RESET; return 0; @@ -495,7 +505,8 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) struct adreno_device *adreno_dev = ADRENO_DEVICE(device); int init_reftimestamp = 0x7fffffff; - kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT); + if (KGSL_STATE_DUMP_AND_RECOVER != device->state) + kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT); /* Power up the device */ kgsl_pwrctrl_enable(device); @@ -588,13 +599,15 @@ static int adreno_start(struct kgsl_device *device, unsigned int init_ram) device->ftbl->irqctrl(device, 1); status = adreno_ringbuffer_start(&adreno_dev->ringbuffer, init_ram); - if (status != 0) - goto error_irq_off; - mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT); - return status; + if (status == 0) { + /* While recovery is on we do not want timer to + * fire and attempt to change any device state */ + if (KGSL_STATE_DUMP_AND_RECOVER != device->state) + mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT); + return 0; + } -error_irq_off: kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); kgsl_mmu_stop(device); error_clk_off: @@ -623,53 +636,74 @@ static int adreno_stop(struct kgsl_device *device) return 0; } +static void adreno_mark_context_status(struct kgsl_device *device, + int recovery_status) +{ + struct kgsl_context *context; + int next = 0; + /* + * Set the reset status of all contexts to + * INNOCENT_CONTEXT_RESET_EXT except for the bad context + * since thats the guilty party, if recovery failed then + * mark all as guilty + */ + while ((context = idr_get_next(&device->context_idr, &next))) { + struct adreno_context *adreno_context = context->devctxt; + if (recovery_status) { + context->reset_status = + KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT; + adreno_context->flags |= CTXT_FLAGS_GPU_HANG; + } else if (KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT != + context->reset_status) { + if (adreno_context->flags & (CTXT_FLAGS_GPU_HANG || + CTXT_FLAGS_GPU_HANG_RECOVERED)) + context->reset_status = + KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT; + else + context->reset_status = + KGSL_CTX_STAT_INNOCENT_CONTEXT_RESET_EXT; + } + next = next + 1; + } +} + static int -adreno_recover_hang(struct kgsl_device *device) +adreno_recover_hang(struct kgsl_device *device, + struct adreno_recovery_data *rec_data) { int ret; - unsigned int *rb_buffer; struct adreno_device *adreno_dev = ADRENO_DEVICE(device); struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; unsigned int timestamp; - unsigned int num_rb_contents; - unsigned int bad_context; - unsigned int reftimestamp; - unsigned int enable_ts; - unsigned int soptimestamp; - unsigned int eoptimestamp; - struct adreno_context *drawctxt; struct kgsl_context *context; - int next = 0; + struct adreno_context *adreno_context; - KGSL_DRV_ERR(device, "Starting recovery from 3D GPU hang....\n"); - rb_buffer = vmalloc(rb->buffer_desc.size); - if (!rb_buffer) { - KGSL_MEM_ERR(device, - "Failed to allocate memory for recovery: %x\n", - rb->buffer_desc.size); - return -ENOMEM; + KGSL_DRV_ERR(device, + "Starting recovery from 3D GPU hang. Recovery parameters: IB1: 0x%X, " + "Bad context_id: %u, global_eop: 0x%x\n", rec_data->ib1, + rec_data->context_id, rec_data->global_eop); + + context = idr_find(&device->context_idr, rec_data->context_id); + if (context == NULL) { + KGSL_DRV_ERR(device, "Last context unknown id:%d\n", + rec_data->context_id); + rec_data->context_id = 0; + } else { + adreno_context = context->devctxt; + adreno_context->flags |= CTXT_FLAGS_GPU_HANG; } - /* Extract valid contents from rb which can stil be executed after + /* Extract valid contents from rb which can still be executed after * hang */ - ret = adreno_ringbuffer_extract(rb, rb_buffer, &num_rb_contents); + ret = adreno_ringbuffer_extract(rb, rec_data); if (ret) goto done; + timestamp = rb->timestamp; - KGSL_DRV_ERR(device, "Last issued timestamp: %x\n", timestamp); - kgsl_sharedmem_readl(&device->memstore, &bad_context, - KGSL_DEVICE_MEMSTORE_OFFSET(current_context)); - kgsl_sharedmem_readl(&device->memstore, &reftimestamp, - KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts)); - kgsl_sharedmem_readl(&device->memstore, &enable_ts, - KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable)); - kgsl_sharedmem_readl(&device->memstore, &soptimestamp, - KGSL_DEVICE_MEMSTORE_OFFSET(soptimestamp)); - kgsl_sharedmem_readl(&device->memstore, &eoptimestamp, - KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp)); + KGSL_DRV_ERR(device, "Last issued global timestamp: %x\n", timestamp); + /* Make sure memory is synchronized before restarting the GPU */ mb(); - KGSL_CTXT_ERR(device, - "Context that caused a GPU hang: %x\n", bad_context); + /* restart device */ ret = adreno_stop(device); if (ret) @@ -678,66 +712,76 @@ adreno_recover_hang(struct kgsl_device *device) if (ret) goto done; KGSL_DRV_ERR(device, "Device has been restarted after hang\n"); - /* Restore timestamp states */ - kgsl_sharedmem_writel(&device->memstore, - KGSL_DEVICE_MEMSTORE_OFFSET(soptimestamp), - soptimestamp); + + /* Restore valid commands in ringbuffer */ + adreno_ringbuffer_restore(rb, rec_data->rb_buffer, rec_data->rb_size); + rb->timestamp = timestamp; + + /* wait for idle */ + ret = adreno_idle(device, KGSL_TIMEOUT_DEFAULT); +done: kgsl_sharedmem_writel(&device->memstore, KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp), - eoptimestamp); - kgsl_sharedmem_writel(&device->memstore, - KGSL_DEVICE_MEMSTORE_OFFSET(soptimestamp), - soptimestamp); - if (num_rb_contents) { - kgsl_sharedmem_writel(&device->memstore, - KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), - reftimestamp); - kgsl_sharedmem_writel(&device->memstore, - KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable), - enable_ts); - } - /* Make sure all writes are posted before the GPU reads them */ - wmb(); - /* Mark the invalid context so no more commands are accepted from - * that context */ + rb->timestamp); + adreno_mark_context_status(device, ret); + return ret; +} - drawctxt = (struct adreno_context *) bad_context; +static void adreno_destroy_recovery_data(struct adreno_recovery_data *rec_data) +{ + vfree(rec_data->rb_buffer); + vfree(rec_data->bad_rb_buffer); +} - KGSL_CTXT_ERR(device, - "Context that caused a GPU hang: %x\n", bad_context); +static int adreno_setup_recovery_data(struct kgsl_device *device, + struct adreno_recovery_data *rec_data) +{ + int ret = 0; + unsigned int ib1_sz, ib2_sz; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer; - drawctxt->flags |= CTXT_FLAGS_GPU_HANG; + memset(rec_data, 0, sizeof(*rec_data)); - /* - * Set the reset status of all contexts to - * INNOCENT_CONTEXT_RESET_EXT except for the bad context - * since thats the guilty party - */ - while ((context = idr_get_next(&device->context_idr, &next))) { - if (KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT != - context->reset_status) { - if (context->devctxt != drawctxt) - context->reset_status = - KGSL_CTX_STAT_INNOCENT_CONTEXT_RESET_EXT; - else - context->reset_status = - KGSL_CTX_STAT_GUILTY_CONTEXT_RESET_EXT; - } - next = next + 1; + adreno_regread(device, REG_CP_IB1_BUFSZ, &ib1_sz); + adreno_regread(device, REG_CP_IB2_BUFSZ, &ib2_sz); + if (ib1_sz || ib2_sz) + adreno_regread(device, REG_CP_IB1_BASE, &rec_data->ib1); + + kgsl_sharedmem_readl(&device->memstore, &rec_data->context_id, + KGSL_DEVICE_MEMSTORE_OFFSET(current_context)); + + kgsl_sharedmem_readl(&device->memstore, + &rec_data->global_eop, + KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp)); + + rec_data->rb_buffer = vmalloc(rb->buffer_desc.size); + if (!rec_data->rb_buffer) { + KGSL_MEM_ERR(device, "vmalloc(%d) failed\n", + rb->buffer_desc.size); + return -ENOMEM; + } + + rec_data->bad_rb_buffer = vmalloc(rb->buffer_desc.size); + if (!rec_data->bad_rb_buffer) { + KGSL_MEM_ERR(device, "vmalloc(%d) failed\n", + rb->buffer_desc.size); + ret = -ENOMEM; + goto done; } - /* Restore valid commands in ringbuffer */ - adreno_ringbuffer_restore(rb, rb_buffer, num_rb_contents); - rb->timestamp = timestamp; done: - vfree(rb_buffer); + if (ret) { + vfree(rec_data->rb_buffer); + vfree(rec_data->bad_rb_buffer); + } return ret; } -static int -adreno_dump_and_recover(struct kgsl_device *device) +int adreno_dump_and_recover(struct kgsl_device *device) { int result = -ETIMEDOUT; + struct adreno_recovery_data rec_data; if (device->state == KGSL_STATE_HUNG) goto done; @@ -752,7 +796,8 @@ adreno_dump_and_recover(struct kgsl_device *device) INIT_COMPLETION(device->recovery_gate); /* Detected a hang */ - + /* Get the recovery data as soon as hang is detected */ + result = adreno_setup_recovery_data(device, &rec_data); /* * Trigger an automatic dump of the state to * the console @@ -765,16 +810,20 @@ adreno_dump_and_recover(struct kgsl_device *device) */ kgsl_device_snapshot(device, 1); - result = adreno_recover_hang(device); - if (result) + result = adreno_recover_hang(device, &rec_data); + adreno_destroy_recovery_data(&rec_data); + if (result) { kgsl_pwrctrl_set_state(device, KGSL_STATE_HUNG); - else + } else { kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE); + mod_timer(&device->idle_timer, jiffies + FIRST_TIMEOUT); + } complete_all(&device->recovery_gate); } done: return result; } +EXPORT_SYMBOL(adreno_dump_and_recover); static int adreno_getproperty(struct kgsl_device *device, enum kgsl_property_type type, @@ -933,7 +982,8 @@ int adreno_idle(struct kgsl_device *device, unsigned int timeout) err: KGSL_DRV_ERR(device, "spun too long waiting for RB to idle\n"); - if (!adreno_dump_and_recover(device)) { + if (KGSL_STATE_DUMP_AND_RECOVER != device->state && + !adreno_dump_and_recover(device)) { wait_time = jiffies + wait_timeout; goto retry; } @@ -954,10 +1004,12 @@ static unsigned int adreno_isidle(struct kgsl_device *device) GSL_RB_GET_READPTR(rb, &rb->rptr); if (!device->active_cnt && (rb->rptr == rb->wptr)) { /* Is the core idle? */ - adreno_regread(device, REG_RBBM_STATUS, - &rbbm_status); - if (rbbm_status == 0x110) - status = true; + if (adreno_dev->gpudev->irq_pending(adreno_dev) == 0) { + adreno_regread(device, REG_RBBM_STATUS, + &rbbm_status); + if (rbbm_status == 0x110) + status = true; + } } } else { status = true; @@ -1095,12 +1147,62 @@ void adreno_regwrite(struct kgsl_device *device, unsigned int offsetwords, __raw_writel(value, reg); } +static int adreno_next_event(struct kgsl_device *device, + struct kgsl_event *event) +{ + int status; + unsigned int ref_ts, enableflag; + + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + + status = kgsl_check_timestamp(device, event->timestamp); + if (!status) { + kgsl_sharedmem_readl(&device->memstore, &enableflag, + KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable)); + mb(); + + if (enableflag) { + kgsl_sharedmem_readl(&device->memstore, &ref_ts, + KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts)); + mb(); + if (timestamp_cmp(ref_ts, event->timestamp) >= 0) { + kgsl_sharedmem_writel(&device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), + event->timestamp); + wmb(); + } + } else { + unsigned int cmds[2]; + kgsl_sharedmem_writel(&device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts), + event->timestamp); + enableflag = 1; + kgsl_sharedmem_writel(&device->memstore, + KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable), + enableflag); + wmb(); + /* submit a dummy packet so that even if all + * commands upto timestamp get executed we will still + * get an interrupt */ + cmds[0] = cp_type3_packet(CP_NOP, 1); + cmds[1] = 0; + + adreno_ringbuffer_issuecmds(device, + adreno_dev->drawctxt_active, + KGSL_CMD_FLAGS_NONE, &cmds[0], 2); + } + } + return status; +} + static int kgsl_check_interrupt_timestamp(struct kgsl_device *device, unsigned int timestamp) { int status; unsigned int ref_ts, enableflag; + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + status = kgsl_check_timestamp(device, timestamp); if (!status) { mutex_lock(&device->mutex); @@ -1133,8 +1235,10 @@ static int kgsl_check_interrupt_timestamp(struct kgsl_device *device, * get an interrupt */ cmds[0] = cp_type3_packet(CP_NOP, 1); cmds[1] = 0; - adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, - &cmds[0], 2); + + adreno_ringbuffer_issuecmds(device, + adreno_dev->drawctxt_active, + KGSL_CMD_FLAGS_NONE, &cmds[0], 2); } mutex_unlock(&device->mutex); } @@ -1228,6 +1332,14 @@ static int adreno_waittimestamp(struct kgsl_device *device, } /*this wait timed out*/ } + + /* Check if timestamp has retired here because we may have hit + * recovery which can take some time and cause waiting threads + * to timeout + */ + if (kgsl_check_timestamp(device, timestamp)) + goto done; + status = -ETIMEDOUT; KGSL_DRV_ERR(device, "Device hang detected while waiting for timestamp: %x," @@ -1235,10 +1347,9 @@ static int adreno_waittimestamp(struct kgsl_device *device, timestamp, adreno_dev->ringbuffer.timestamp, adreno_dev->ringbuffer.wptr); if (!adreno_dump_and_recover(device)) { - /* wait for idle after recovery as the - * timestamp that this process wanted - * to wait on may be invalid */ - if (!adreno_idle(device, KGSL_TIMEOUT_DEFAULT)) + /* The timestamp that this process wanted + * to wait on may be invalid or expired now + * after successful recovery */ status = 0; } done: @@ -1385,6 +1496,7 @@ static const struct kgsl_functable adreno_functable = { .setstate = adreno_setstate, .drawctxt_create = adreno_drawctxt_create, .drawctxt_destroy = adreno_drawctxt_destroy, + .next_event = adreno_next_event, }; static struct platform_device_id adreno_id_table[] = { diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index a7fc1c5701285..0694ebd5cf4f5 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -27,11 +27,12 @@ #define KGSL_CMD_FLAGS_NONE 0x00000000 #define KGSL_CMD_FLAGS_PMODE 0x00000001 #define KGSL_CMD_FLAGS_NO_TS_CMP 0x00000002 -#define KGSL_CMD_FLAGS_NOT_KERNEL_CMD 0x00000004 /* Command identifiers */ #define KGSL_CONTEXT_TO_MEM_IDENTIFIER 0xDEADBEEF #define KGSL_CMD_IDENTIFIER 0xFEEDFACE +#define KGSL_START_OF_IB_IDENTIFIER 0x2EADEABE +#define KGSL_END_OF_IB_IDENTIFIER 0x2ABEDEAD #ifdef CONFIG_MSM_SCM #define ADRENO_DEFAULT_PWRSCALE_POLICY (&kgsl_pwrscale_policy_tz) @@ -46,6 +47,8 @@ #define ADRENO_ISTORE_WORDS 3 #define ADRENO_ISTORE_START 0x5000 +#define ADRENO_NUM_CTX_SWITCH_ALLOWED_BEFORE_DRAW 50 + enum adreno_gpurev { ADRENO_REV_UNKNOWN = 0, ADRENO_REV_A200 = 200, @@ -74,17 +77,46 @@ struct adreno_device { unsigned int wait_timeout; unsigned int istore_size; unsigned int pix_shader_start; + unsigned int ib_check_level; }; struct adreno_gpudev { + /* keeps track of when we need to execute the draw workaround code */ + int ctx_switches_since_last_draw; int (*ctxt_create)(struct adreno_device *, struct adreno_context *); void (*ctxt_save)(struct adreno_device *, struct adreno_context *); void (*ctxt_restore)(struct adreno_device *, struct adreno_context *); + void (*ctxt_draw_workaround)(struct adreno_device *, struct adreno_context *); irqreturn_t (*irq_handler)(struct adreno_device *); void (*irq_control)(struct adreno_device *, int); + unsigned int (*irq_pending)(struct adreno_device *); void * (*snapshot)(struct adreno_device *, void *, int *, int); }; +/* + * struct adreno_recovery_data - Structure that contains all information to + * perform gpu recovery from hangs + * @ib1 - IB1 that the GPU was executing when hang happened + * @context_id - Context which caused the hang + * @global_eop - eoptimestamp at time of hang + * @rb_buffer - Buffer that holds the commands from good contexts + * @rb_size - Number of valid dwords in rb_buffer + * @bad_rb_buffer - Buffer that holds commands from the hanging context + * bad_rb_size - Number of valid dwords in bad_rb_buffer + * @last_valid_ctx_id - The last context from which commands were placed in + * ringbuffer before the GPU hung + */ +struct adreno_recovery_data { + unsigned int ib1; + unsigned int context_id; + unsigned int global_eop; + unsigned int *rb_buffer; + unsigned int rb_size; + unsigned int *bad_rb_buffer; + unsigned int bad_rb_size; + unsigned int last_valid_ctx_id; +}; + extern struct adreno_gpudev adreno_a2xx_gpudev; /* A2XX register sets defined in adreno_a2xx.c */ @@ -110,6 +142,8 @@ uint8_t *adreno_convertaddr(struct kgsl_device *device, void *adreno_snapshot(struct kgsl_device *device, void *snapshot, int *remain, int hang); +int adreno_dump_and_recover(struct kgsl_device *device); + static inline int adreno_is_a200(struct adreno_device *adreno_dev) { return (adreno_dev->gpurev == ADRENO_REV_A200); diff --git a/drivers/gpu/msm/adreno_a2xx.c b/drivers/gpu/msm/adreno_a2xx.c index 5ce9cf85bc2ec..53822609974cd 100644 --- a/drivers/gpu/msm/adreno_a2xx.c +++ b/drivers/gpu/msm/adreno_a2xx.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1421,11 +1421,51 @@ static int a2xx_drawctxt_create(struct adreno_device *adreno_dev, return ret; } +static void a2xx_drawctxt_draw_workaround(struct adreno_device *adreno_dev, + struct adreno_context *context) +{ + struct kgsl_device *device = &adreno_dev->dev; + unsigned int cmd[11]; + unsigned int *cmds = &cmd[0]; + + adreno_dev->gpudev->ctx_switches_since_last_draw++; + /* If there have been > than ADRENO_NUM_CTX_SWITCH_ALLOWED_BEFORE_DRAW + * calls to context switches w/o gmem being saved then we need to + * execute this workaround */ + if (adreno_dev->gpudev->ctx_switches_since_last_draw > + ADRENO_NUM_CTX_SWITCH_ALLOWED_BEFORE_DRAW) + adreno_dev->gpudev->ctx_switches_since_last_draw = 0; + else + return; + /* + * Issue an empty draw call to avoid possible hangs due to + * repeated idles without intervening draw calls. + * On adreno 225 the PC block has a cache that is only + * flushed on draw calls and repeated idles can make it + * overflow. The gmem save path contains draw calls so + * this workaround isn't needed there. + */ + *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); + *cmds++ = (0x4 << 16) | (REG_PA_SU_SC_MODE_CNTL - 0x2000); + *cmds++ = 0; + *cmds++ = cp_type3_packet(CP_DRAW_INDX, 5); + *cmds++ = 0; + *cmds++ = 1<<14; + *cmds++ = 0; + *cmds++ = device->mmu.setstate_memory.gpuaddr; + *cmds++ = 0; + *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); + *cmds++ = 0x00000000; + + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_PMODE, + &cmd[0], 11); +} + static void a2xx_drawctxt_save(struct adreno_device *adreno_dev, struct adreno_context *context) { struct kgsl_device *device = &adreno_dev->dev; - unsigned int cmd[22]; if (context == NULL) return; @@ -1437,12 +1477,13 @@ static void a2xx_drawctxt_save(struct adreno_device *adreno_dev, if (!(context->flags & CTXT_FLAGS_PREAMBLE)) { /* save registers and constants. */ - adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_NONE, context->reg_save, 3); if (context->flags & CTXT_FLAGS_SHADER_SAVE) { /* save shader partitioning and instructions. */ - adreno_ringbuffer_issuecmds(device, + adreno_ringbuffer_issuecmds(device, context, KGSL_CMD_FLAGS_PMODE, context->shader_save, 3); @@ -1450,7 +1491,8 @@ static void a2xx_drawctxt_save(struct adreno_device *adreno_dev, * fixup shader partitioning parameter for * SET_SHADER_BASES. */ - adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_NONE, context->shader_fixup, 3); context->flags |= CTXT_FLAGS_SHADER_RESTORE; @@ -1462,41 +1504,21 @@ static void a2xx_drawctxt_save(struct adreno_device *adreno_dev, /* save gmem. * (note: changes shader. shader must already be saved.) */ - adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_PMODE, context->context_gmem_shadow.gmem_save, 3); /* Restore TP0_CHICKEN */ if (!(context->flags & CTXT_FLAGS_PREAMBLE)) { - adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_NONE, context->chicken_restore, 3); } + adreno_dev->gpudev->ctx_switches_since_last_draw = 0; context->flags |= CTXT_FLAGS_GMEM_RESTORE; - } else if (adreno_is_a225(adreno_dev)) { - unsigned int *cmds = &cmd[0]; - /* - * Issue an empty draw call to avoid possible hangs due to - * repeated idles without intervening draw calls. - * On adreno 225 the PC block has a cache that is only - * flushed on draw calls and repeated idles can make it - * overflow. The gmem save path contains draw calls so - * this workaround isn't needed there. - */ - *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2); - *cmds++ = (0x4 << 16) | (REG_PA_SU_SC_MODE_CNTL - 0x2000); - *cmds++ = 0; - *cmds++ = cp_type3_packet(CP_DRAW_INDX, 5); - *cmds++ = 0; - *cmds++ = 1<<14; - *cmds++ = 0; - *cmds++ = device->mmu.setstate_memory.gpuaddr; - *cmds++ = 0; - *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1); - *cmds++ = 0x00000000; - - adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, - &cmd[0], 11); - } + } else if (adreno_is_a225(adreno_dev)) + a2xx_drawctxt_draw_workaround(adreno_dev, context); } static void a2xx_drawctxt_restore(struct adreno_device *adreno_dev, @@ -1507,7 +1529,8 @@ static void a2xx_drawctxt_restore(struct adreno_device *adreno_dev, if (context == NULL) { /* No context - set the default apgetable and thats it */ - kgsl_mmu_setstate(device, device->mmu.defaultpagetable); + kgsl_mmu_setstate(device, device->mmu.defaultpagetable, + adreno_dev->drawctxt_active->id); return; } @@ -1519,8 +1542,9 @@ static void a2xx_drawctxt_restore(struct adreno_device *adreno_dev, cmds[3] = device->memstore.gpuaddr + KGSL_DEVICE_MEMSTORE_OFFSET(current_context); cmds[4] = (unsigned int) context; - adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, cmds, 5); - kgsl_mmu_setstate(device, context->pagetable); + adreno_ringbuffer_issuecmds(device, context, KGSL_CMD_FLAGS_NONE, + cmds, 5); + kgsl_mmu_setstate(device, context->pagetable, context->id); #ifndef CONFIG_MSM_KGSL_CFF_DUMP_NO_CONTEXT_MEM_DUMP kgsl_cffdump_syncmem(NULL, &context->gpustate, @@ -1532,12 +1556,14 @@ static void a2xx_drawctxt_restore(struct adreno_device *adreno_dev, * (note: changes shader. shader must not already be restored.) */ if (context->flags & CTXT_FLAGS_GMEM_RESTORE) { - adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE, + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_PMODE, context->context_gmem_shadow.gmem_restore, 3); if (!(context->flags & CTXT_FLAGS_PREAMBLE)) { /* Restore TP0_CHICKEN */ - adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_NONE, context->chicken_restore, 3); } @@ -1547,12 +1573,12 @@ static void a2xx_drawctxt_restore(struct adreno_device *adreno_dev, if (!(context->flags & CTXT_FLAGS_PREAMBLE)) { /* restore registers and constants. */ - adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, - context->reg_restore, 3); + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_NONE, context->reg_restore, 3); /* restore shader instructions & partitioning. */ if (context->flags & CTXT_FLAGS_SHADER_RESTORE) { - adreno_ringbuffer_issuecmds(device, + adreno_ringbuffer_issuecmds(device, context, KGSL_CMD_FLAGS_NONE, context->shader_restore, 3); } @@ -1561,8 +1587,8 @@ static void a2xx_drawctxt_restore(struct adreno_device *adreno_dev, if (adreno_is_a20x(adreno_dev)) { cmds[0] = cp_type3_packet(CP_SET_BIN_BASE_OFFSET, 1); cmds[1] = context->bin_base_offset; - adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_NONE, - cmds, 2); + adreno_ringbuffer_issuecmds(device, context, + KGSL_CMD_FLAGS_NONE, cmds, 2); } } @@ -1642,16 +1668,6 @@ static void a2xx_cp_intrcallback(struct kgsl_device *device) return; } - if (status & CP_INT_CNTL__RB_INT_MASK) { - /* signal intr completion event */ - unsigned int enableflag = 0; - kgsl_sharedmem_writel(&rb->device->memstore, - KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable), - enableflag); - wmb(); - KGSL_CMD_WARN(rb->device, "ringbuffer rb interrupt\n"); - } - for (i = 0; i < ARRAY_SIZE(kgsl_cp_error_irqs); i++) { if (status & kgsl_cp_error_irqs[i].mask) { KGSL_CMD_CRIT(rb->device, "%s\n", @@ -1749,6 +1765,19 @@ static void a2xx_irq_control(struct adreno_device *adreno_dev, int state) wmb(); } +static unsigned int a2xx_irq_pending(struct adreno_device *adreno_dev) +{ + struct kgsl_device *device = &adreno_dev->dev; + unsigned int rbbm, cp, mh; + + adreno_regread(device, REG_RBBM_INT_CNTL, &rbbm); + adreno_regread(device, REG_CP_INT_CNTL, &cp); + adreno_regread(device, MH_INTERRUPT_MASK, &mh); + + return ((rbbm & RBBM_INT_MASK) || (cp & CP_INT_MASK) || + (mh & MH_INTERRUPT_MASK)) ? 1 : 0; +} + /* Defined in adreno_a2xx_snapshot.c */ void *a2xx_snapshot(struct adreno_device *adreno_dev, void *snapshot, int *remain, int hang); @@ -1757,7 +1786,9 @@ struct adreno_gpudev adreno_a2xx_gpudev = { .ctxt_create = a2xx_drawctxt_create, .ctxt_save = a2xx_drawctxt_save, .ctxt_restore = a2xx_drawctxt_restore, + .ctxt_draw_workaround = a2xx_drawctxt_draw_workaround, .irq_handler = a2xx_irq_handler, .irq_control = a2xx_irq_control, + .irq_pending = a2xx_irq_pending, .snapshot = a2xx_snapshot, }; diff --git a/drivers/gpu/msm/adreno_a2xx_snapshot.c b/drivers/gpu/msm/adreno_a2xx_snapshot.c index 30d692b9f61fc..d87dc211954f3 100644 --- a/drivers/gpu/msm/adreno_a2xx_snapshot.c +++ b/drivers/gpu/msm/adreno_a2xx_snapshot.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/adreno_a2xx_trace.c b/drivers/gpu/msm/adreno_a2xx_trace.c index c91d1a04ad89a..87c930b04389e 100644 --- a/drivers/gpu/msm/adreno_a2xx_trace.c +++ b/drivers/gpu/msm/adreno_a2xx_trace.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/adreno_a2xx_trace.h b/drivers/gpu/msm/adreno_a2xx_trace.h index 2528e153dbdd2..af355d680f0cd 100644 --- a/drivers/gpu/msm/adreno_a2xx_trace.h +++ b/drivers/gpu/msm/adreno_a2xx_trace.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/adreno_debugfs.c b/drivers/gpu/msm/adreno_debugfs.c index c1b9e4ce24b2e..b9ec6c4d639a0 100644 --- a/drivers/gpu/msm/adreno_debugfs.c +++ b/drivers/gpu/msm/adreno_debugfs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2008-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2008-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -24,6 +24,7 @@ unsigned int kgsl_cff_dump_enable; int kgsl_pm_regs_enabled; +int adreno_ib_dump_on_pagef_enabled; static struct dentry *pm_d_debugfs; @@ -60,6 +61,21 @@ DEFINE_SIMPLE_ATTRIBUTE(pm_regs_enabled_fops, pm_regs_enabled_get, pm_regs_enabled_set, "%llu\n"); +static int ib_dump_on_pagef_enabled_get(void *data, u64 *val) +{ + *val = adreno_ib_dump_on_pagef_enabled; + return 0; +} + +static int ib_dump_on_pagef_enabled_set(void *data, u64 val) +{ + adreno_ib_dump_on_pagef_enabled = val ? 1 : 0; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(ib_dump_on_pagef_enabled_fops, + ib_dump_on_pagef_enabled_get, + ib_dump_on_pagef_enabled_set, "%llu\n"); static int kgsl_cff_dump_enable_set(void *data, u64 val) { @@ -345,6 +361,8 @@ void adreno_debugfs_init(struct kgsl_device *device) &kgsl_cff_dump_enable_fops); debugfs_create_u32("wait_timeout", 0644, device->d_debugfs, &adreno_dev->wait_timeout); + debugfs_create_u32("ib_check", 0644, device->d_debugfs, + &adreno_dev->ib_check_level); /* Create post mortem control files */ @@ -357,4 +375,7 @@ void adreno_debugfs_init(struct kgsl_device *device) &pm_dump_fops); debugfs_create_file("regs_enabled", 0644, pm_d_debugfs, device, &pm_regs_enabled_fops); + + debugfs_create_file("ib_dump_on_pagefault", 0644, device->d_debugfs, + device, &ib_dump_on_pagef_enabled_fops); } diff --git a/drivers/gpu/msm/adreno_debugfs.h b/drivers/gpu/msm/adreno_debugfs.h index 0356ac6e11909..667919c246928 100644 --- a/drivers/gpu/msm/adreno_debugfs.h +++ b/drivers/gpu/msm/adreno_debugfs.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2008-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2008-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -18,6 +18,7 @@ int adreno_debugfs_init(struct kgsl_device *device); extern int kgsl_pm_regs_enabled; +extern int adreno_ib_dump_on_pagef_enabled; static inline int kgsl_pmregs_enabled(void) { @@ -35,6 +36,12 @@ static inline int kgsl_pmregs_enabled(void) /* If debugfs is turned off, then always print registers */ return 1; } + #endif +static inline int is_adreno_ib_dump_on_pagef_enabled(struct kgsl_device *device) +{ + return adreno_ib_dump_on_pagef_enabled; +} + #endif /* __ADRENO_DEBUGFS_H */ diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c index 206a678eed877..e425592a24aa1 100644 --- a/drivers/gpu/msm/adreno_drawctxt.c +++ b/drivers/gpu/msm/adreno_drawctxt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -138,6 +138,7 @@ int adreno_drawctxt_create(struct kgsl_device *device, drawctxt->pagetable = pagetable; drawctxt->bin_base_offset = 0; + drawctxt->id = context->id; if (flags & KGSL_CONTEXT_PREAMBLE) drawctxt->flags |= CTXT_FLAGS_PREAMBLE; @@ -171,11 +172,13 @@ void adreno_drawctxt_destroy(struct kgsl_device *device, struct kgsl_context *context) { struct adreno_device *adreno_dev = ADRENO_DEVICE(device); - struct adreno_context *drawctxt = context->devctxt; + struct adreno_context *drawctxt; - if (drawctxt == NULL) + if (context == NULL || context->devctxt == NULL) return; + drawctxt = context->devctxt; + /* deactivate context */ if (adreno_dev->drawctxt_active == drawctxt) { /* no need to save GMEM or shader, the context is @@ -243,8 +246,13 @@ void adreno_drawctxt_switch(struct adreno_device *adreno_dev, } /* already current? */ - if (adreno_dev->drawctxt_active == drawctxt) + if (adreno_dev->drawctxt_active == drawctxt) { + if (adreno_dev->gpudev->ctxt_draw_workaround && + adreno_is_a225(adreno_dev)) + adreno_dev->gpudev->ctxt_draw_workaround( + adreno_dev, drawctxt); return; + } KGSL_CTXT_INFO(device, "from %p to %p flags %d\n", adreno_dev->drawctxt_active, drawctxt, flags); @@ -253,6 +261,8 @@ void adreno_drawctxt_switch(struct adreno_device *adreno_dev, adreno_dev->gpudev->ctxt_save(adreno_dev, adreno_dev->drawctxt_active); /* Set the new context */ - adreno_dev->drawctxt_active = drawctxt; adreno_dev->gpudev->ctxt_restore(adreno_dev, drawctxt); + adreno_dev->drawctxt_active = drawctxt; } + + diff --git a/drivers/gpu/msm/adreno_drawctxt.h b/drivers/gpu/msm/adreno_drawctxt.h index 30476603e04dc..91e49f245ced0 100644 --- a/drivers/gpu/msm/adreno_drawctxt.h +++ b/drivers/gpu/msm/adreno_drawctxt.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -40,6 +40,12 @@ #define CTXT_FLAGS_GPU_HANG 0x00008000 /* Specifies there is no need to save GMEM */ #define CTXT_FLAGS_NOGMEMALLOC 0x00010000 +/* Trash state for context */ +#define CTXT_FLAGS_TRASHSTATE 0x00020000 +/* per context timestamps enabled */ +#define CTXT_FLAGS_PER_CONTEXT_TS 0x00040000 +/* Context has caused a GPU hang and recovered properly */ +#define CTXT_FLAGS_GPU_HANG_RECOVERED 0x00008000 struct kgsl_device; struct adreno_device; @@ -69,6 +75,7 @@ struct gmem_shadow_t { }; struct adreno_context { + unsigned int id; uint32_t flags; struct kgsl_pagetable *pagetable; struct kgsl_memdesc gpustate; diff --git a/drivers/gpu/msm/adreno_pm4types.h b/drivers/gpu/msm/adreno_pm4types.h index aa7652951fcc2..8f3da7497fdb0 100644 --- a/drivers/gpu/msm/adreno_pm4types.h +++ b/drivers/gpu/msm/adreno_pm4types.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,11 +29,6 @@ /* skip N 32-bit words to get to the next packet */ #define CP_NOP 0x10 -/* indirect buffer dispatch. prefetch parser uses this packet type to determine -* whether to pre-fetch the IB -*/ -#define CP_INDIRECT_BUFFER 0x3f - /* indirect buffer dispatch. same as IB, but init is pipelined */ #define CP_INDIRECT_BUFFER_PFD 0x37 @@ -117,6 +112,9 @@ /* load constants from a location in memory */ #define CP_LOAD_CONSTANT_CONTEXT 0x2e +/* (A2x) sets binning configuration registers */ +#define CP_SET_BIN_DATA 0x2f + /* selective invalidation of state pointers */ #define CP_INVALIDATE_STATE 0x3b @@ -188,11 +186,20 @@ #define cp_nop_packet(cnt) \ (CP_TYPE3_PKT | (((cnt)-1) << 16) | (CP_NOP << 8)) +#define pkt_is_type0(pkt) (((pkt) & 0XC0000000) == CP_TYPE0_PKT) + +#define type0_pkt_size(pkt) ((((pkt) >> 16) & 0x3FFF) + 1) +#define type0_pkt_offset(pkt) ((pkt) & 0x7FFF) + +#define pkt_is_type3(pkt) (((pkt) & 0xC0000000) == CP_TYPE3_PKT) + +#define cp_type3_opcode(pkt) (((pkt) >> 8) & 0xFF) +#define type3_pkt_size(pkt) ((((pkt) >> 16) & 0x3FFF) + 1) /* packet headers */ #define CP_HDR_ME_INIT cp_type3_packet(CP_ME_INIT, 18) #define CP_HDR_INDIRECT_BUFFER_PFD cp_type3_packet(CP_INDIRECT_BUFFER_PFD, 2) -#define CP_HDR_INDIRECT_BUFFER cp_type3_packet(CP_INDIRECT_BUFFER, 2) +#define CP_HDR_INDIRECT_BUFFER_PFE cp_type3_packet(CP_INDIRECT_BUFFER_PFE, 2) /* dword base address of the GFX decode space */ #define SUBBLOCK_OFFSET(reg) ((unsigned int)((reg) - (0x2000))) diff --git a/drivers/gpu/msm/adreno_postmortem.c b/drivers/gpu/msm/adreno_postmortem.c index 78adb2f90daa7..bdd65f2ddb4a7 100644 --- a/drivers/gpu/msm/adreno_postmortem.c +++ b/drivers/gpu/msm/adreno_postmortem.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -53,7 +53,7 @@ static const struct pm_id_name pm3_types[] = { {CP_IM_LOAD, "IN__LOAD"}, {CP_IM_LOAD_IMMEDIATE, "IM_LOADI"}, {CP_IM_STORE, "IM_STORE"}, - {CP_INDIRECT_BUFFER, "IND_BUF_"}, + {CP_INDIRECT_BUFFER_PFE, "IND_BUF_"}, {CP_INDIRECT_BUFFER_PFD, "IND_BUFP"}, {CP_INTERRUPT, "PM4_INTR"}, {CP_INVALIDATE_STATE, "INV_STAT"}, @@ -156,7 +156,7 @@ static void adreno_dump_regs(struct kgsl_device *device, } } -static void dump_ib(struct kgsl_device *device, char* buffId, uint32_t pt_base, +void dump_ib(struct kgsl_device *device, char *buffId, uint32_t pt_base, uint32_t base_offset, uint32_t ib_base, uint32_t ib_size, bool dump) { uint8_t *base_addr = adreno_convertaddr(device, pt_base, @@ -172,15 +172,7 @@ static void dump_ib(struct kgsl_device *device, char* buffId, uint32_t pt_base, base_addr ? "" : " [Invalid]"); } -#define IB_LIST_SIZE 64 -struct ib_list { - int count; - uint32_t bases[IB_LIST_SIZE]; - uint32_t sizes[IB_LIST_SIZE]; - uint32_t offsets[IB_LIST_SIZE]; -}; - -static void dump_ib1(struct kgsl_device *device, uint32_t pt_base, +void dump_ib1(struct kgsl_device *device, uint32_t pt_base, uint32_t base_offset, uint32_t ib1_base, uint32_t ib1_size, struct ib_list *ib_list, bool dump) @@ -547,7 +539,8 @@ static int adreno_dump(struct kgsl_device *device) " %08X\n", r1, r2, pt_base); cur_pt_base = pt_base; - KGSL_LOG_DUMP(device, "PAGETABLE SIZE: %08X ", KGSL_PAGETABLE_SIZE); + KGSL_LOG_DUMP(device, "PAGETABLE SIZE: %08X ", + kgsl_mmu_get_ptsize()); kgsl_regread(device, MH_MMU_TRAN_ERROR, &r1); KGSL_LOG_DUMP(device, " TRAN_ERROR = %08X\n", r1); diff --git a/drivers/gpu/msm/adreno_postmortem.h b/drivers/gpu/msm/adreno_postmortem.h index b677800678f98..68832e28c19b3 100644 --- a/drivers/gpu/msm/adreno_postmortem.h +++ b/drivers/gpu/msm/adreno_postmortem.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -16,6 +16,22 @@ struct kgsl_device; +#define IB_LIST_SIZE 64 +struct ib_list { + int count; + uint32_t bases[IB_LIST_SIZE]; + uint32_t sizes[IB_LIST_SIZE]; + uint32_t offsets[IB_LIST_SIZE]; +}; + int adreno_postmortem_dump(struct kgsl_device *device, int manual); +void dump_ib(struct kgsl_device *device, char *buffId, uint32_t pt_base, + uint32_t base_offset, uint32_t ib_base, uint32_t ib_size, bool dump); + +void dump_ib1(struct kgsl_device *device, uint32_t pt_base, + uint32_t base_offset, + uint32_t ib1_base, uint32_t ib1_size, + struct ib_list *ib_list, bool dump); + #endif /* __ADRENO_POSTMORTEM_H */ diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index cff6ed7b1c328..3de70166787c7 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -22,6 +22,8 @@ #include "adreno.h" #include "adreno_pm4types.h" #include "adreno_ringbuffer.h" +#include "adreno_debugfs.h" +#include "adreno_postmortem.h" #include "a2xx_reg.h" @@ -69,6 +71,9 @@ adreno_ringbuffer_waitspace(struct adreno_ringbuffer *rb, unsigned int numcmds, unsigned int freecmds; unsigned int *cmds; uint cmds_gpu; + struct adreno_device *adreno_dev = ADRENO_DEVICE(rb->device); + unsigned long wait_timeout = msecs_to_jiffies(adreno_dev->wait_timeout); + unsigned long wait_time; /* if wptr ahead, fill the remaining with NOPs */ if (wptr_ahead) { @@ -95,15 +100,58 @@ adreno_ringbuffer_waitspace(struct adreno_ringbuffer *rb, unsigned int numcmds, rb->wptr = 0; } + wait_time = jiffies + wait_timeout; /* wait for space in ringbuffer */ - do { + while (1) { GSL_RB_GET_READPTR(rb, &rb->rptr); freecmds = rb->rptr - rb->wptr; - } while ((freecmds != 0) && (freecmds <= numcmds)); + if (freecmds == 0 || freecmds > numcmds) + break; + + if (time_after(jiffies, wait_time)) { + KGSL_DRV_ERR(rb->device, + "Timed out while waiting for freespace in ringbuffer " + "rptr: 0x%x, wptr: 0x%x\n", rb->rptr, rb->wptr); + if (!adreno_dump_and_recover(rb->device)) + wait_time = jiffies + wait_timeout; + else + /* GPU is hung and we cannot recover */ + BUG(); + } + } } +static unsigned int find_faulting_ib1_size(struct adreno_ringbuffer *rb, + unsigned int rptr, unsigned int ib1) +{ + unsigned int value; + unsigned int temp_rptr = rptr * sizeof(unsigned int); + unsigned int rb_size = rb->buffer_desc.size; + + do { + temp_rptr = adreno_ringbuffer_dec_wrapped(temp_rptr, rb_size); + kgsl_sharedmem_readl(&rb->buffer_desc, &value, temp_rptr); + + if (ib1 == value) { + temp_rptr = adreno_ringbuffer_dec_wrapped(temp_rptr, + rb_size); + kgsl_sharedmem_readl(&rb->buffer_desc, &value, + temp_rptr); + if (adreno_cmd_is_ib(value)) { + temp_rptr += 2 * sizeof(unsigned int); + kgsl_sharedmem_readl(&rb->buffer_desc, &value, + temp_rptr); + return value; + } else { + temp_rptr += sizeof(unsigned int); + } + } + } while (temp_rptr != rptr); + + return 0; +} static unsigned int *adreno_ringbuffer_allocspace(struct adreno_ringbuffer *rb, unsigned int numcmds) @@ -460,6 +508,7 @@ void adreno_ringbuffer_close(struct adreno_ringbuffer *rb) static uint32_t adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, + struct adreno_context *context, unsigned int flags, unsigned int *cmds, int sizedwords) { @@ -473,17 +522,26 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, * error checking if needed */ total_sizedwords += flags & KGSL_CMD_FLAGS_PMODE ? 4 : 0; - total_sizedwords += !(flags & KGSL_CMD_FLAGS_NO_TS_CMP) ? 7 : 0; - total_sizedwords += !(flags & KGSL_CMD_FLAGS_NOT_KERNEL_CMD) ? 2 : 0; + total_sizedwords += !(flags & KGSL_CMD_FLAGS_NO_TS_CMP) ? 10 : 0; + /* 2 dwords to store the start of command sequence */ + total_sizedwords += 2; ringcmds = adreno_ringbuffer_allocspace(rb, total_sizedwords); + /* GPU may hang during space allocation, if thats the case the current + * context may have hung the GPU */ + if (context && context->flags & CTXT_FLAGS_GPU_HANG) { + KGSL_CTXT_WARN(rb->device, + "Context %p caused a gpu hang. Will not accept commands for context %d\n", + context, context->id); + return rb->timestamp; + } + rcmd_gpu = rb->buffer_desc.gpuaddr + sizeof(uint)*(rb->wptr-total_sizedwords); - if (!(flags & KGSL_CMD_FLAGS_NOT_KERNEL_CMD)) { - GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_nop_packet(1)); - GSL_RB_WRITE(ringcmds, rcmd_gpu, KGSL_CMD_IDENTIFIER); - } + GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_nop_packet(1)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, KGSL_CMD_IDENTIFIER); + if (flags & KGSL_CMD_FLAGS_PMODE) { /* disable protected mode error checking */ GSL_RB_WRITE(ringcmds, rcmd_gpu, @@ -526,7 +584,13 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, KGSL_DEVICE_MEMSTORE_OFFSET(ref_wait_ts)) >> 2); GSL_RB_WRITE(ringcmds, rcmd_gpu, rb->timestamp); /* # of conditional command DWORDs */ - GSL_RB_WRITE(ringcmds, rcmd_gpu, 2); + GSL_RB_WRITE(ringcmds, rcmd_gpu, 5); + + /* Clear the ts_cmp_enable for the global timestamp*/ + GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_type3_packet(CP_MEM_WRITE, 2)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, rb->device->memstore.gpuaddr + KGSL_DEVICE_MEMSTORE_OFFSET(ts_cmp_enable)); + GSL_RB_WRITE(ringcmds, rcmd_gpu, 0x0); + GSL_RB_WRITE(ringcmds, rcmd_gpu, cp_type3_packet(CP_INTERRUPT, 1)); GSL_RB_WRITE(ringcmds, rcmd_gpu, CP_INT_CNTL__RB_INT_MASK); @@ -540,6 +604,7 @@ adreno_ringbuffer_addcmds(struct adreno_ringbuffer *rb, void adreno_ringbuffer_issuecmds(struct kgsl_device *device, + struct adreno_context *drawctxt, unsigned int flags, unsigned int *cmds, int sizedwords) @@ -549,7 +614,199 @@ adreno_ringbuffer_issuecmds(struct kgsl_device *device, if (device->state & KGSL_STATE_HUNG) return; - adreno_ringbuffer_addcmds(rb, flags, cmds, sizedwords); + + adreno_ringbuffer_addcmds(rb, drawctxt, flags, cmds, sizedwords); +} + +static bool _parse_ibs(struct kgsl_device_private *dev_priv, uint gpuaddr, + int sizedwords); + +static bool +_handle_type3(struct kgsl_device_private *dev_priv, uint *hostaddr) +{ + unsigned int opcode = cp_type3_opcode(*hostaddr); + switch (opcode) { + case CP_INDIRECT_BUFFER_PFD: + case CP_INDIRECT_BUFFER_PFE: + case CP_COND_INDIRECT_BUFFER_PFE: + case CP_COND_INDIRECT_BUFFER_PFD: + return _parse_ibs(dev_priv, hostaddr[1], hostaddr[2]); + case CP_NOP: + case CP_WAIT_FOR_IDLE: + case CP_WAIT_REG_MEM: + case CP_WAIT_REG_EQ: + case CP_WAT_REG_GTE: + case CP_WAIT_UNTIL_READ: + case CP_WAIT_IB_PFD_COMPLETE: + case CP_REG_RMW: + case CP_REG_TO_MEM: + case CP_MEM_WRITE: + case CP_MEM_WRITE_CNTR: + case CP_COND_EXEC: + case CP_COND_WRITE: + case CP_EVENT_WRITE: + case CP_EVENT_WRITE_SHD: + case CP_EVENT_WRITE_CFL: + case CP_EVENT_WRITE_ZPD: + case CP_DRAW_INDX: + case CP_DRAW_INDX_2: + case CP_DRAW_INDX_BIN: + case CP_DRAW_INDX_2_BIN: + case CP_VIZ_QUERY: + case CP_SET_STATE: + case CP_SET_CONSTANT: + case CP_IM_LOAD: + case CP_IM_LOAD_IMMEDIATE: + case CP_LOAD_CONSTANT_CONTEXT: + case CP_INVALIDATE_STATE: + case CP_SET_SHADER_BASES: + case CP_SET_BIN_MASK: + case CP_SET_BIN_SELECT: + case CP_SET_BIN_BASE_OFFSET: + case CP_SET_BIN_DATA: + case CP_CONTEXT_UPDATE: + case CP_INTERRUPT: + case CP_IM_STORE: + break; + /* these shouldn't come from userspace */ + case CP_ME_INIT: + case CP_SET_PROTECTED_MODE: + default: + KGSL_CMD_ERR(dev_priv->device, "bad CP opcode %0x\n", opcode); + return false; + break; + } + + return true; +} + +static bool +_handle_type0(struct kgsl_device_private *dev_priv, uint *hostaddr) +{ + unsigned int reg = type0_pkt_offset(*hostaddr); + unsigned int cnt = type0_pkt_size(*hostaddr); + if (reg < 0x0192 || (reg + cnt) >= 0x8000) { + KGSL_CMD_ERR(dev_priv->device, "bad type0 reg: 0x%0x cnt: %d\n", + reg, cnt); + return false; + } + return true; +} + +/* + * Traverse IBs and dump them to test vector. Detect swap by inspecting + * register writes, keeping note of the current state, and dump + * framebuffer config to test vector + */ +static bool _parse_ibs(struct kgsl_device_private *dev_priv, + uint gpuaddr, int sizedwords) +{ + static uint level; /* recursion level */ + bool ret = false; + uint *hostaddr, *hoststart; + int dwords_left = sizedwords; /* dwords left in the current command + buffer */ + struct kgsl_mem_entry *entry; + + spin_lock(&dev_priv->process_priv->mem_lock); + entry = kgsl_sharedmem_find_region(dev_priv->process_priv, + gpuaddr, sizedwords * sizeof(uint)); + spin_unlock(&dev_priv->process_priv->mem_lock); + if (entry == NULL) { + KGSL_CMD_ERR(dev_priv->device, + "no mapping for gpuaddr: 0x%08x\n", gpuaddr); + return false; + } + + hostaddr = (uint *)kgsl_gpuaddr_to_vaddr(&entry->memdesc, gpuaddr); + if (hostaddr == NULL) { + KGSL_CMD_ERR(dev_priv->device, + "no mapping for gpuaddr: 0x%08x\n", gpuaddr); + return false; + } + + hoststart = hostaddr; + + level++; + + KGSL_CMD_INFO(dev_priv->device, "ib: gpuaddr:0x%08x, wc:%d, hptr:%p\n", + gpuaddr, sizedwords, hostaddr); + + mb(); + while (dwords_left > 0) { + bool cur_ret = true; + int count = 0; /* dword count including packet header */ + + switch (*hostaddr >> 30) { + case 0x0: /* type-0 */ + count = (*hostaddr >> 16)+2; + cur_ret = _handle_type0(dev_priv, hostaddr); + break; + case 0x1: /* type-1 */ + count = 2; + break; + case 0x3: /* type-3 */ + count = ((*hostaddr >> 16) & 0x3fff) + 2; + cur_ret = _handle_type3(dev_priv, hostaddr); + break; + default: + KGSL_CMD_ERR(dev_priv->device, "unexpected type: " + "type:%d, word:0x%08x @ 0x%p, gpu:0x%08x\n", + *hostaddr >> 30, *hostaddr, hostaddr, + gpuaddr+4*(sizedwords-dwords_left)); + cur_ret = false; + count = dwords_left; + break; + } + + if (!cur_ret) { + KGSL_CMD_ERR(dev_priv->device, + "bad sub-type: #:%d/%d, v:0x%08x" + " @ 0x%p[gb:0x%08x], level:%d\n", + sizedwords-dwords_left, sizedwords, *hostaddr, + hostaddr, gpuaddr+4*(sizedwords-dwords_left), + level); + + if (ADRENO_DEVICE(dev_priv->device)->ib_check_level + >= 2) + print_hex_dump(KERN_ERR, + level == 1 ? "IB1:" : "IB2:", + DUMP_PREFIX_OFFSET, 32, 4, hoststart, + sizedwords*4, 0); + goto done; + } + + /* jump to next packet */ + dwords_left -= count; + hostaddr += count; + if (dwords_left < 0) { + KGSL_CMD_ERR(dev_priv->device, + "bad count: c:%d, #:%d/%d, " + "v:0x%08x @ 0x%p[gb:0x%08x], level:%d\n", + count, sizedwords-(dwords_left+count), + sizedwords, *(hostaddr-count), hostaddr-count, + gpuaddr+4*(sizedwords-(dwords_left+count)), + level); + if (ADRENO_DEVICE(dev_priv->device)->ib_check_level + >= 2) + print_hex_dump(KERN_ERR, + level == 1 ? "IB1:" : "IB2:", + DUMP_PREFIX_OFFSET, 32, 4, hoststart, + sizedwords*4, 0); + goto done; + } + } + + ret = true; +done: + if (!ret) + KGSL_DRV_ERR(dev_priv->device, + "parsing failed: gpuaddr:0x%08x, " + "host:0x%p, wc:%d\n", gpuaddr, hoststart, sizedwords); + + level--; + + return ret; } int @@ -599,22 +856,25 @@ adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv, start_index = 1; for (i = start_index; i < numibs; i++) { - (void)kgsl_cffdump_parse_ibs(dev_priv, NULL, - ibdesc[i].gpuaddr, ibdesc[i].sizedwords, false); - + if (unlikely(adreno_dev->ib_check_level >= 1 && + !_parse_ibs(dev_priv, ibdesc[i].gpuaddr, + ibdesc[i].sizedwords))) { + kfree(link); + return -EINVAL; + } *cmds++ = CP_HDR_INDIRECT_BUFFER_PFD; *cmds++ = ibdesc[i].gpuaddr; *cmds++ = ibdesc[i].sizedwords; } - kgsl_setstate(device, + kgsl_setstate(device, context->id, kgsl_mmu_pt_get_flags(device->mmu.hwpagetable, - device->id)); + device->id)); adreno_drawctxt_switch(adreno_dev, drawctxt, flags); *timestamp = adreno_ringbuffer_addcmds(&adreno_dev->ringbuffer, - KGSL_CMD_FLAGS_NOT_KERNEL_CMD, + drawctxt, 0, &link[0], (cmds - link)); KGSL_CMD_INFO(device, "ctxt %d g %08x numibs %d ts %d\n", @@ -634,185 +894,329 @@ adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv, return 0; } -int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, - unsigned int *temp_rb_buffer, - int *rb_size) +static int _find_start_of_cmd_seq(struct adreno_ringbuffer *rb, + unsigned int *ptr, + bool inc) { - struct kgsl_device *device = rb->device; - unsigned int rb_rptr; - unsigned int retired_timestamp; - unsigned int temp_idx = 0; - unsigned int value; + int status = -EINVAL; unsigned int val1; - unsigned int val2; - unsigned int val3; - unsigned int copy_rb_contents = 0; - unsigned int cur_context; - unsigned int j; - - GSL_RB_GET_READPTR(rb, &rb->rptr); + unsigned int size = rb->buffer_desc.size; + unsigned int start_ptr = *ptr; - retired_timestamp = device->ftbl->readtimestamp(device, - KGSL_TIMESTAMP_RETIRED); - KGSL_DRV_ERR(device, "GPU successfully executed till ts: %x\n", - retired_timestamp); - /* - * We need to go back in history by 4 dwords from the current location - * of read pointer as 4 dwords are read to match the end of a command. - * Also, take care of wrap around when moving back - */ - if (rb->rptr >= 4) - rb_rptr = (rb->rptr - 4) * sizeof(unsigned int); - else - rb_rptr = rb->buffer_desc.size - - ((4 - rb->rptr) * sizeof(unsigned int)); - /* Read the rb contents going backwards to locate end of last - * sucessfully executed command */ - while ((rb_rptr / sizeof(unsigned int)) != rb->wptr) { - kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); - if (value == retired_timestamp) { - rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, - rb->buffer_desc.size); - kgsl_sharedmem_readl(&rb->buffer_desc, &val1, rb_rptr); - rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, - rb->buffer_desc.size); - kgsl_sharedmem_readl(&rb->buffer_desc, &val2, rb_rptr); - rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, - rb->buffer_desc.size); - kgsl_sharedmem_readl(&rb->buffer_desc, &val3, rb_rptr); - /* match the pattern found at the end of a command */ - if ((val1 == 2 && - val2 == cp_type3_packet(CP_INTERRUPT, 1) - && val3 == CP_INT_CNTL__RB_INT_MASK) || - (val1 == cp_type3_packet(CP_EVENT_WRITE, 3) - && val2 == CACHE_FLUSH_TS && - val3 == (rb->device->memstore.gpuaddr + - KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp)))) { - rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, - rb->buffer_desc.size); - KGSL_DRV_ERR(device, - "Found end of last executed " - "command at offset: %x\n", - rb_rptr / sizeof(unsigned int)); + while ((start_ptr / sizeof(unsigned int)) != rb->wptr) { + if (inc) + start_ptr = adreno_ringbuffer_inc_wrapped(start_ptr, + size); + else + start_ptr = adreno_ringbuffer_dec_wrapped(start_ptr, + size); + kgsl_sharedmem_readl(&rb->buffer_desc, &val1, start_ptr); + if (KGSL_CMD_IDENTIFIER == val1) { + if ((start_ptr / sizeof(unsigned int)) != rb->wptr) + start_ptr = adreno_ringbuffer_dec_wrapped( + start_ptr, size); + *ptr = start_ptr; + status = 0; break; - } else { - if (rb_rptr < (3 * sizeof(unsigned int))) - rb_rptr = rb->buffer_desc.size - - (3 * sizeof(unsigned int)) - + rb_rptr; - else - rb_rptr -= (3 * sizeof(unsigned int)); - } } + } + return status; +} - if (rb_rptr == 0) - rb_rptr = rb->buffer_desc.size - sizeof(unsigned int); - else - rb_rptr -= sizeof(unsigned int); +static int _find_cmd_seq_after_eop_ts(struct adreno_ringbuffer *rb, + unsigned int *rb_rptr, + unsigned int global_eop, + bool inc) +{ + int status = -EINVAL; + unsigned int temp_rb_rptr = *rb_rptr; + unsigned int size = rb->buffer_desc.size; + unsigned int val[3]; + int i = 0; + bool check = false; + + if (inc && temp_rb_rptr / sizeof(unsigned int) != rb->wptr) + return status; + + do { + /* when decrementing we need to decrement first and + * then read make sure we cover all the data */ + if (!inc) + temp_rb_rptr = adreno_ringbuffer_dec_wrapped( + temp_rb_rptr, size); + kgsl_sharedmem_readl(&rb->buffer_desc, &val[i], + temp_rb_rptr); + + if (check && ((inc && val[i] == global_eop) || + (!inc && (val[i] == + cp_type3_packet(CP_MEM_WRITE, 2) || + val[i] == CACHE_FLUSH_TS)))) { + /* decrement i, i.e i = (i - 1 + 3) % 3 if + * we are going forward, else increment i */ + i = (i + 2) % 3; + if (val[i] == rb->device->memstore.gpuaddr + + KGSL_DEVICE_MEMSTORE_OFFSET(eoptimestamp)) { + int j = ((i + 2) % 3); + if ((inc && (val[j] == CACHE_FLUSH_TS || + val[j] == cp_type3_packet( + CP_MEM_WRITE, 2))) || + (!inc && val[j] == global_eop)) { + /* Found the global eop */ + status = 0; + break; + } + } + /* if no match found then increment i again + * since we decremented before matching */ + i = (i + 1) % 3; + } + if (inc) + temp_rb_rptr = adreno_ringbuffer_inc_wrapped( + temp_rb_rptr, size); + + i = (i + 1) % 3; + if (2 == i) + check = true; + } while (temp_rb_rptr / sizeof(unsigned int) != rb->wptr); + /* temp_rb_rptr points to the command stream after global eop, + * move backward till the start of command sequence */ + if (!status) { + status = _find_start_of_cmd_seq(rb, &temp_rb_rptr, false); + if (!status) { + *rb_rptr = temp_rb_rptr; + KGSL_DRV_ERR(rb->device, + "Offset of cmd sequence after eop timestamp: 0x%x\n", + temp_rb_rptr / sizeof(unsigned int)); + } } + return status; +} - if ((rb_rptr / sizeof(unsigned int)) == rb->wptr) { - KGSL_DRV_ERR(device, - "GPU recovery from hang not possible because last" - " successful timestamp is overwritten\n"); - return -EINVAL; +static int _find_hanging_ib_sequence(struct adreno_ringbuffer *rb, + unsigned int *rb_rptr, + unsigned int ib1) +{ + int status = -EINVAL; + unsigned int temp_rb_rptr = *rb_rptr; + unsigned int size = rb->buffer_desc.size; + unsigned int val[2]; + int i = 0; + bool check = false; + bool ctx_switch = false; + + while (temp_rb_rptr / sizeof(unsigned int) != rb->wptr) { + kgsl_sharedmem_readl(&rb->buffer_desc, &val[i], temp_rb_rptr); + + if (check && val[i] == ib1) { + /* decrement i, i.e i = (i - 1 + 2) % 2 */ + i = (i + 1) % 2; + if (adreno_cmd_is_ib(val[i])) { + /* go till start of command sequence */ + status = _find_start_of_cmd_seq(rb, + &temp_rb_rptr, false); + KGSL_DRV_ERR(rb->device, + "Found the hanging IB at offset 0x%x\n", + temp_rb_rptr / sizeof(unsigned int)); + break; + } + /* if no match the increment i since we decremented + * before checking */ + i = (i + 1) % 2; + } + /* Make sure you do not encounter a context switch twice, we can + * encounter it once for the bad context as the start of search + * can point to the context switch */ + if (val[i] == KGSL_CONTEXT_TO_MEM_IDENTIFIER) { + if (ctx_switch) { + KGSL_DRV_ERR(rb->device, + "Context switch encountered before bad " + "IB found\n"); + break; + } + ctx_switch = true; + } + i = (i + 1) % 2; + if (1 == i) + check = true; + temp_rb_rptr = adreno_ringbuffer_inc_wrapped(temp_rb_rptr, + size); } - /* rb_rptr is now pointing to the first dword of the command following - * the last sucessfully executed command sequence. Assumption is that - * GPU is hung in the command sequence pointed by rb_rptr */ - /* make sure the GPU is not hung in a command submitted by kgsl - * itself */ - kgsl_sharedmem_readl(&rb->buffer_desc, &val1, rb_rptr); - kgsl_sharedmem_readl(&rb->buffer_desc, &val2, - adreno_ringbuffer_inc_wrapped(rb_rptr, - rb->buffer_desc.size)); - if (val1 == cp_nop_packet(1) && val2 == KGSL_CMD_IDENTIFIER) { - KGSL_DRV_ERR(device, - "GPU recovery from hang not possible because " - "of hang in kgsl command\n"); - return -EINVAL; + if (!status) + *rb_rptr = temp_rb_rptr; + return status; +} + +static void _turn_preamble_on_for_ib_seq(struct adreno_ringbuffer *rb, + unsigned int rb_rptr) +{ + unsigned int temp_rb_rptr = rb_rptr; + unsigned int size = rb->buffer_desc.size; + unsigned int val[2]; + int i = 0; + bool check = false; + bool cmd_start = false; + + /* Go till the start of the ib sequence and turn on preamble */ + while (temp_rb_rptr / sizeof(unsigned int) != rb->wptr) { + kgsl_sharedmem_readl(&rb->buffer_desc, &val[i], temp_rb_rptr); + if (check && KGSL_START_OF_IB_IDENTIFIER == val[i]) { + /* decrement i */ + i = (i + 1) % 2; + if (val[i] == cp_nop_packet(4)) { + temp_rb_rptr = adreno_ringbuffer_dec_wrapped( + temp_rb_rptr, size); + kgsl_sharedmem_writel(&rb->buffer_desc, + temp_rb_rptr, cp_nop_packet(1)); + } + KGSL_DRV_ERR(rb->device, + "Turned preamble on at offset 0x%x\n", + temp_rb_rptr / 4); + break; + } + /* If you reach beginning of next command sequence then exit + * First command encountered is the current one so don't break + * on that. */ + if (KGSL_CMD_IDENTIFIER == val[i]) { + if (cmd_start) + break; + cmd_start = true; + } + + i = (i + 1) % 2; + if (1 == i) + check = true; + temp_rb_rptr = adreno_ringbuffer_inc_wrapped(temp_rb_rptr, + size); } +} - /* current_context is the context that is presently active in the - * GPU, i.e the context in which the hang is caused */ - kgsl_sharedmem_readl(&device->memstore, &cur_context, - KGSL_DEVICE_MEMSTORE_OFFSET(current_context)); +static void _copy_valid_rb_content(struct adreno_ringbuffer *rb, + unsigned int rb_rptr, unsigned int *temp_rb_buffer, + int *rb_size, unsigned int *bad_rb_buffer, + int *bad_rb_size, + int *last_valid_ctx_id) +{ + unsigned int good_rb_idx = 0, cmd_start_idx = 0; + unsigned int val1 = 0; + struct kgsl_context *k_ctxt; + struct adreno_context *a_ctxt; + unsigned int bad_rb_idx = 0; + int copy_rb_contents = 0; + unsigned int temp_rb_rptr; + unsigned int size = rb->buffer_desc.size; + unsigned int good_cmd_start_idx = 0; + + /* Walk the rb from the context switch. Omit any commands + * for an invalid context. */ while ((rb_rptr / sizeof(unsigned int)) != rb->wptr) { - kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); - rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, - rb->buffer_desc.size); + kgsl_sharedmem_readl(&rb->buffer_desc, &val1, rb_rptr); + + if (KGSL_CMD_IDENTIFIER == val1) { + /* Start is the NOP dword that comes before + * KGSL_CMD_IDENTIFIER */ + cmd_start_idx = bad_rb_idx - 1; + if (copy_rb_contents) + good_cmd_start_idx = good_rb_idx - 1; + } + /* check for context switch indicator */ - if (value == KGSL_CONTEXT_TO_MEM_IDENTIFIER) { - kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); - rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, - rb->buffer_desc.size); - BUG_ON(value != cp_type3_packet(CP_MEM_WRITE, 2)); - kgsl_sharedmem_readl(&rb->buffer_desc, &val1, rb_rptr); - rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, - rb->buffer_desc.size); - BUG_ON(val1 != (device->memstore.gpuaddr + - KGSL_DEVICE_MEMSTORE_OFFSET(current_context))); - kgsl_sharedmem_readl(&rb->buffer_desc, &value, rb_rptr); - rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, - rb->buffer_desc.size); - - /* - * If other context switches were already lost and - * and the current context is the one that is hanging, - * then we cannot recover. Print an error message - * and leave. - */ - - if ((copy_rb_contents == 0) && (value == cur_context)) { - KGSL_DRV_ERR(device, "GPU recovery could not " - "find the previous context\n"); - return -EINVAL; - } + if (val1 == KGSL_CONTEXT_TO_MEM_IDENTIFIER) { + unsigned int temp_idx, val2; + /* increment by 3 to get to the context_id */ + temp_rb_rptr = rb_rptr + (3 * sizeof(unsigned int)) % + size; + kgsl_sharedmem_readl(&rb->buffer_desc, &val2, + temp_rb_rptr); - /* - * If we were copying the commands and got to this point - * then we need to remove the 3 commands that appear - * before KGSL_CONTEXT_TO_MEM_IDENTIFIER - */ - if (temp_idx) - temp_idx -= 3; /* if context switches to a context that did not cause * hang then start saving the rb contents as those * commands can be executed */ - if (value != cur_context) { + k_ctxt = idr_find(&rb->device->context_idr, val2); + if (k_ctxt) { + a_ctxt = k_ctxt->devctxt; + + /* If we are changing to a good context and were not + * copying commands then copy over commands to the good + * context */ + if (!copy_rb_contents && ((k_ctxt && + !(a_ctxt->flags & CTXT_FLAGS_GPU_HANG)) || + !k_ctxt)) { + for (temp_idx = cmd_start_idx; + temp_idx < bad_rb_idx; + temp_idx++) + temp_rb_buffer[good_rb_idx++] = + bad_rb_buffer[temp_idx]; + *last_valid_ctx_id = val2; copy_rb_contents = 1; - temp_rb_buffer[temp_idx++] = cp_nop_packet(1); - temp_rb_buffer[temp_idx++] = - KGSL_CMD_IDENTIFIER; - temp_rb_buffer[temp_idx++] = cp_nop_packet(1); - temp_rb_buffer[temp_idx++] = - KGSL_CONTEXT_TO_MEM_IDENTIFIER; - temp_rb_buffer[temp_idx++] = - cp_type3_packet(CP_MEM_WRITE, 2); - temp_rb_buffer[temp_idx++] = val1; - temp_rb_buffer[temp_idx++] = value; - } else { + } else if (copy_rb_contents && k_ctxt && + (a_ctxt->flags & CTXT_FLAGS_GPU_HANG)) { + /* If we are changing to bad context then remove + * the dwords we copied for this sequence from + * the good buffer */ + good_rb_idx = good_cmd_start_idx; copy_rb_contents = 0; } - } else if (copy_rb_contents) - temp_rb_buffer[temp_idx++] = value; + } + } + + if (copy_rb_contents) + temp_rb_buffer[good_rb_idx++] = val1; + /* Copy both good and bad commands for replay to the bad + * buffer */ + bad_rb_buffer[bad_rb_idx++] = val1; + + rb_rptr = adreno_ringbuffer_inc_wrapped(rb_rptr, size); } + *rb_size = good_rb_idx; + *bad_rb_size = bad_rb_idx; +} - *rb_size = temp_idx; - KGSL_DRV_ERR(device, "Extracted rb contents, size: %x\n", *rb_size); - for (temp_idx = 0; temp_idx < *rb_size;) { - char str[80]; - int idx = 0; - if ((temp_idx + 8) <= *rb_size) - j = 8; - else - j = *rb_size - temp_idx; - for (; j != 0; j--) - idx += scnprintf(str + idx, 80 - idx, - "%8.8X ", temp_rb_buffer[temp_idx++]); - printk(KERN_ALERT "%s", str); +int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, + struct adreno_recovery_data *rec_data) +{ + int status; + struct kgsl_device *device = rb->device; + unsigned int rb_rptr = rb->wptr * sizeof(unsigned int); + struct kgsl_context *context; + struct adreno_context *adreno_context; + + context = idr_find(&device->context_idr, rec_data->context_id); + + /* Look for the command stream that is right after the global eop */ + status = _find_cmd_seq_after_eop_ts(rb, &rb_rptr, + rec_data->global_eop + 1, false); + + if (status) + goto done; + + if (context) { + adreno_context = context->devctxt; + + if (adreno_context->flags & CTXT_FLAGS_PREAMBLE) { + if (rec_data->ib1) { + status = _find_hanging_ib_sequence(rb, &rb_rptr, + rec_data->ib1); + if (status) + goto copy_rb_contents; + } + _turn_preamble_on_for_ib_seq(rb, rb_rptr); + } } - return 0; + +copy_rb_contents: + _copy_valid_rb_content(rb, rb_rptr, rec_data->rb_buffer, + &rec_data->rb_size, + rec_data->bad_rb_buffer, + &rec_data->bad_rb_size, + &rec_data->last_valid_ctx_id); + /* If we failed to get the hanging IB sequence then we cannot execute + * commands from the bad context */ + if (status) { + rec_data->bad_rb_size = 0; + status = 0; + } +done: + return status; } void @@ -838,3 +1242,52 @@ adreno_ringbuffer_restore(struct adreno_ringbuffer *rb, unsigned int *rb_buff, rb->wptr += num_rb_contents; adreno_ringbuffer_submit(rb); } + +void adreno_print_fault_ib_work(struct work_struct *work) +{ + struct kgsl_device *device = container_of(work, struct kgsl_device, + print_fault_ib); + mutex_lock(&device->mutex); + adreno_print_fault_ib(device); + mutex_unlock(&device->mutex); +} + +void adreno_print_fault_ib(struct kgsl_device *device) +{ + struct adreno_device *adreno_dev = ADRENO_DEVICE(device); + struct ib_list ib_list; + int i; + + unsigned int ib_sz; + + if (!device->page_fault_ptbase || + !is_adreno_ib_dump_on_pagef_enabled(device)) + goto done; + + ib_sz = find_faulting_ib1_size(&adreno_dev->ringbuffer, + device->page_fault_rptr, + device->page_fault_ib1); + if (!ib_sz) { + KGSL_DRV_ERR(device, "Could not find size of fault ib 0x%x\n", + device->page_fault_ib1); + goto done; + } + ib_list.count = 0; + + KGSL_DRV_ERR(device, "Page faulting IB1 0x%x, size 0x%x\n", + device->page_fault_ib1, ib_sz); + dump_ib1(device, device->page_fault_ptbase, 0, + device->page_fault_ib1, ib_sz, &ib_list, true); + + /* print ib2's in faulting ib1 */ + for (i = 0; i < ib_list.count; i++) { + KGSL_DRV_ERR(device, "IB2 0x%x, size 0x%x\n", + ib_list.bases[i], ib_list.sizes[i]); + dump_ib(device, "IB2:", device->page_fault_ptbase, + ib_list.offsets[i], ib_list.bases[i], ib_list.sizes[i], + true); + } + KGSL_DRV_ERR(device, "Finished printing fault IB\n"); +done: + device->page_fault_ptbase = 0; +} diff --git a/drivers/gpu/msm/adreno_ringbuffer.h b/drivers/gpu/msm/adreno_ringbuffer.h index 6c4926dc51581..b976600e90102 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.h +++ b/drivers/gpu/msm/adreno_ringbuffer.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -31,6 +31,7 @@ struct kgsl_device; struct kgsl_device_private; +struct adreno_recovery_data; #define GSL_RB_MEMPTRS_SCRATCH_COUNT 8 struct kgsl_rbmemptrs { @@ -122,6 +123,7 @@ void adreno_ringbuffer_stop(struct adreno_ringbuffer *rb); void adreno_ringbuffer_close(struct adreno_ringbuffer *rb); void adreno_ringbuffer_issuecmds(struct kgsl_device *device, + struct adreno_context *drawctxt, unsigned int flags, unsigned int *cmdaddr, int sizedwords); @@ -129,13 +131,16 @@ void adreno_ringbuffer_issuecmds(struct kgsl_device *device, void kgsl_cp_intrcallback(struct kgsl_device *device); int adreno_ringbuffer_extract(struct adreno_ringbuffer *rb, - unsigned int *temp_rb_buffer, - int *rb_size); + struct adreno_recovery_data *rec_data); void adreno_ringbuffer_restore(struct adreno_ringbuffer *rb, unsigned int *rb_buff, int num_rb_contents); +void adreno_print_fault_ib_work(struct work_struct *work); + +void adreno_print_fault_ib(struct kgsl_device *device); + static inline int adreno_ringbuffer_count(struct adreno_ringbuffer *rb, unsigned int rptr) { @@ -151,4 +156,11 @@ static inline unsigned int adreno_ringbuffer_inc_wrapped(unsigned int val, return (val + sizeof(unsigned int)) % size; } +/* Decrement a value by 4 bytes with wrap-around based on size */ +static inline unsigned int adreno_ringbuffer_dec_wrapped(unsigned int val, + unsigned int size) +{ + return (val + size - sizeof(unsigned int)) % size; +} + #endif /* __ADRENO_RINGBUFFER_H */ diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c index c45dbff489426..26fa42908de30 100644 --- a/drivers/gpu/msm/adreno_snapshot.c +++ b/drivers/gpu/msm/adreno_snapshot.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 8f67997f8f25e..0414652d64398 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -21,7 +21,7 @@ #include #include #include - +#include #include #include #include @@ -33,6 +33,7 @@ #include "kgsl_sharedmem.h" #include "kgsl_device.h" #include "kgsl_trace.h" +#include "kgsl_sync.h" #undef MODULE_PARAM_PREFIX #define MODULE_PARAM_PREFIX "kgsl." @@ -59,9 +60,9 @@ static struct ion_client *kgsl_ion_client; * @returns - 0 on success or error code on failure */ -static int kgsl_add_event(struct kgsl_device *device, u32 ts, +int kgsl_add_event(struct kgsl_device *device, u32 ts, void (*cb)(struct kgsl_device *, void *, u32), void *priv, - struct kgsl_device_private *owner) + void *owner) { struct kgsl_event *event; struct list_head *n; @@ -105,6 +106,7 @@ static int kgsl_add_event(struct kgsl_device *device, u32 ts, queue_work(device->work_queue, &device->ts_expired_ws); return 0; } +EXPORT_SYMBOL(kgsl_add_event); /** * kgsl_cancel_events - Cancel all events for a process @@ -112,8 +114,8 @@ static int kgsl_add_event(struct kgsl_device *device, u32 ts, * @owner - driver instance that owns the events to cancel * */ -static void kgsl_cancel_events(struct kgsl_device *device, - struct kgsl_device_private *owner) +void kgsl_cancel_events(struct kgsl_device *device, + void *owner) { struct kgsl_event *event, *event_tmp; unsigned int cur = device->ftbl->readtimestamp(device, @@ -135,6 +137,7 @@ static void kgsl_cancel_events(struct kgsl_device *device, kfree(event); } } +EXPORT_SYMBOL(kgsl_cancel_events); static inline struct kgsl_mem_entry * kgsl_mem_entry_create(void) @@ -193,8 +196,28 @@ static void kgsl_mem_entry_attach_process(struct kgsl_mem_entry *entry, struct kgsl_process_private *process) { + struct rb_node **node; + struct rb_node *parent = NULL; + spin_lock(&process->mem_lock); - list_add(&entry->list, &process->mem_list); + + node = &process->mem_rb.rb_node; + + while (*node) { + struct kgsl_mem_entry *cur; + + parent = *node; + cur = rb_entry(parent, struct kgsl_mem_entry, node); + + if (entry->memdesc.gpuaddr < cur->memdesc.gpuaddr) + node = &parent->rb_left; + else + node = &parent->rb_right; + } + + rb_link_node(&entry->node, parent, node); + rb_insert_color(&entry->node, &process->mem_rb); + spin_unlock(&process->mem_lock); entry->priv = process; @@ -235,6 +258,12 @@ kgsl_create_context(struct kgsl_device_private *dev_priv) context->id = id; context->dev_priv = dev_priv; + if (kgsl_sync_timeline_create(context)) { + idr_remove(&dev_priv->device->context_idr, id); + kfree(context); + return NULL; + } + return context; } @@ -251,10 +280,23 @@ kgsl_destroy_context(struct kgsl_device_private *dev_priv, BUG_ON(context->devctxt); id = context->id; + kgsl_sync_timeline_destroy(context); kfree(context); idr_remove(&dev_priv->device->context_idr, id); } +static inline int _mark_next_event(struct kgsl_device *device, + struct list_head *head) +{ + struct kgsl_event *event; + + if (!list_empty(head)) { + event = list_first_entry(head, struct kgsl_event, list); + if (device->ftbl->next_event) + return device->ftbl->next_event(device, event); + } + return 0; +} static void kgsl_timestamp_expired(struct work_struct *work) { @@ -265,20 +307,32 @@ static void kgsl_timestamp_expired(struct work_struct *work) mutex_lock(&device->mutex); - /* get current EOP timestamp */ - ts_processed = device->ftbl->readtimestamp(device, - KGSL_TIMESTAMP_RETIRED); + while (1) { + /* get current EOP timestamp */ + ts_processed = device->ftbl->readtimestamp(device, + KGSL_TIMESTAMP_RETIRED); - /* Process expired events */ - list_for_each_entry_safe(event, event_tmp, &device->events, list) { - if (timestamp_cmp(ts_processed, event->timestamp) < 0) - break; + /* Process expired events */ + list_for_each_entry_safe(event, event_tmp, &device->events, list) { + if (timestamp_cmp(ts_processed, event->timestamp) < 0) + break; - if (event->func) - event->func(device, event->priv, ts_processed); + if (event->func) + event->func(device, event->priv, ts_processed); - list_del(&event->list); - kfree(event); + list_del(&event->list); + kfree(event); + } + + /* + * Keep looping until we hit an event which has not + * passed and then we write a dummy interrupt. + * mark_next_event will return 1 for every event + * that has passed and return 0 for the event which has not + * passed yet. + */ + if (_mark_next_event(device, &device->events) == 0) + break; } mutex_unlock(&device->mutex); @@ -551,8 +605,7 @@ kgsl_get_process_private(struct kgsl_device_private *cur_dev_priv) spin_lock_init(&private->mem_lock); private->refcnt = 1; private->pid = task_tgid_nr(current); - - INIT_LIST_HEAD(&private->mem_list); + private->mem_rb = RB_ROOT; if (kgsl_mmu_enabled()) { @@ -581,7 +634,7 @@ kgsl_put_process_private(struct kgsl_device *device, struct kgsl_process_private *private) { struct kgsl_mem_entry *entry = NULL; - struct kgsl_mem_entry *entry_tmp = NULL; + struct rb_node *node; if (!private) return; @@ -595,11 +648,13 @@ kgsl_put_process_private(struct kgsl_device *device, list_del(&private->list); - list_for_each_entry_safe(entry, entry_tmp, &private->mem_list, list) { - list_del(&entry->list); + for (node = rb_first(&private->mem_rb); node; ) { + entry = rb_entry(node, struct kgsl_mem_entry, node); + node = rb_next(&entry->node); + + rb_erase(&entry->node, &private->mem_rb); kgsl_mem_entry_put(entry); } - kgsl_mmu_putpagetable(private->pagetable); kfree(private); unlock: @@ -725,47 +780,46 @@ static int kgsl_open(struct inode *inodep, struct file *filep) return result; } - /*call with private->mem_lock locked */ -static struct kgsl_mem_entry * -kgsl_sharedmem_find(struct kgsl_process_private *private, unsigned int gpuaddr) +struct kgsl_mem_entry * +kgsl_sharedmem_find_region(struct kgsl_process_private *private, + unsigned int gpuaddr, size_t size) { - struct kgsl_mem_entry *entry = NULL, *result = NULL; + struct rb_node *node = private->mem_rb.rb_node; - BUG_ON(private == NULL); + if (!kgsl_mmu_gpuaddr_in_range(gpuaddr)) + return NULL; - gpuaddr &= PAGE_MASK; + while (node != NULL) { + struct kgsl_mem_entry *entry; - list_for_each_entry(entry, &private->mem_list, list) { - if (entry->memdesc.gpuaddr == gpuaddr) { - result = entry; - break; - } - } - return result; -} + entry = rb_entry(node, struct kgsl_mem_entry, node); -/*call with private->mem_lock locked */ -struct kgsl_mem_entry * -kgsl_sharedmem_find_region(struct kgsl_process_private *private, - unsigned int gpuaddr, - size_t size) -{ - struct kgsl_mem_entry *entry = NULL, *result = NULL; - BUG_ON(private == NULL); + if (kgsl_gpuaddr_in_memdesc(&entry->memdesc, gpuaddr, size)) + return entry; - list_for_each_entry(entry, &private->mem_list, list) { - if (kgsl_gpuaddr_in_memdesc(&entry->memdesc, gpuaddr, size)) { - result = entry; - break; + if (gpuaddr < entry->memdesc.gpuaddr) + node = node->rb_left; + else if (gpuaddr >= + (entry->memdesc.gpuaddr + entry->memdesc.size)) + node = node->rb_right; + else { + return NULL; } } - return result; + return NULL; } EXPORT_SYMBOL(kgsl_sharedmem_find_region); +/*call with private->mem_lock locked */ +static inline struct kgsl_mem_entry * +kgsl_sharedmem_find(struct kgsl_process_private *private, unsigned int gpuaddr) +{ + return kgsl_sharedmem_find_region(private, gpuaddr, 1); +} + /*call all ioctl sub functions with driver locked*/ static long kgsl_ioctl_device_getproperty(struct kgsl_device_private *dev_priv, unsigned int cmd, void *data) @@ -864,40 +918,6 @@ static long kgsl_ioctl_device_waittimestamp(struct kgsl_device_private return result; } -static bool check_ibdesc(struct kgsl_device_private *dev_priv, - struct kgsl_ibdesc *ibdesc, unsigned int numibs, - bool parse) -{ - bool result = true; - unsigned int i; - for (i = 0; i < numibs; i++) { - struct kgsl_mem_entry *entry; - spin_lock(&dev_priv->process_priv->mem_lock); - entry = kgsl_sharedmem_find_region(dev_priv->process_priv, - ibdesc[i].gpuaddr, ibdesc[i].sizedwords * sizeof(uint)); - spin_unlock(&dev_priv->process_priv->mem_lock); - if (entry == NULL) { - KGSL_DRV_ERR(dev_priv->device, - "invalid cmd buffer gpuaddr %08x " \ - "sizedwords %d\n", ibdesc[i].gpuaddr, - ibdesc[i].sizedwords); - result = false; - break; - } - - if (parse && !kgsl_cffdump_parse_ibs(dev_priv, &entry->memdesc, - ibdesc[i].gpuaddr, ibdesc[i].sizedwords, true)) { - KGSL_DRV_ERR(dev_priv->device, - "invalid cmd buffer gpuaddr %08x " \ - "sizedwords %d numibs %d/%d\n", - ibdesc[i].gpuaddr, - ibdesc[i].sizedwords, i+1, numibs); - result = false; - break; - } - } - return result; -} static long kgsl_ioctl_rb_issueibcmds(struct kgsl_device_private *dev_priv, unsigned int cmd, void *data) @@ -967,12 +987,6 @@ static long kgsl_ioctl_rb_issueibcmds(struct kgsl_device_private *dev_priv, param->numibs = 1; } - if (!check_ibdesc(dev_priv, ibdesc, param->numibs, true)) { - KGSL_DRV_ERR(dev_priv->device, "bad ibdesc"); - result = -EINVAL; - goto free_ibdesc; - } - result = dev_priv->device->ftbl->issueibcmds(dev_priv, context, ibdesc, @@ -982,18 +996,6 @@ static long kgsl_ioctl_rb_issueibcmds(struct kgsl_device_private *dev_priv, trace_kgsl_issueibcmds(dev_priv->device, param, result); - if (result != 0) - goto free_ibdesc; - - /* this is a check to try to detect if a command buffer was freed - * during issueibcmds(). - */ - if (!check_ibdesc(dev_priv, ibdesc, param->numibs, false)) { - KGSL_DRV_ERR(dev_priv->device, "bad ibdesc AFTER issue"); - result = -EINVAL; - goto free_ibdesc; - } - free_ibdesc: kfree(ibdesc); done: @@ -1025,7 +1027,7 @@ static void kgsl_freemem_event_cb(struct kgsl_device *device, { struct kgsl_mem_entry *entry = priv; spin_lock(&entry->priv->mem_lock); - list_del(&entry->list); + rb_erase(&entry->node, &entry->priv->mem_rb); spin_unlock(&entry->priv->mem_lock); kgsl_mem_entry_put(entry); } @@ -1117,7 +1119,8 @@ static long kgsl_ioctl_sharedmem_free(struct kgsl_device_private *dev_priv, spin_lock(&private->mem_lock); entry = kgsl_sharedmem_find(private, param->gpuaddr); if (entry) - list_del(&entry->list); + rb_erase(&entry->node, &private->mem_rb); + spin_unlock(&private->mem_lock); if (entry) { @@ -1350,7 +1353,8 @@ static int memdesc_sg_virt(struct kgsl_memdesc *memdesc, int sglen = PAGE_ALIGN(size) / PAGE_SIZE; unsigned long paddr = (unsigned long) addr; - memdesc->sg = vmalloc(sglen * sizeof(struct scatterlist)); + memdesc->sg = kgsl_sg_alloc(sglen); + if (memdesc->sg == NULL) return -ENOMEM; @@ -1390,7 +1394,7 @@ static int memdesc_sg_virt(struct kgsl_memdesc *memdesc, err: spin_unlock(¤t->mm->page_table_lock); - vfree(memdesc->sg); + kgsl_sg_free(memdesc->sg, sglen); memdesc->sg = NULL; return -EINVAL; @@ -1525,8 +1529,11 @@ static int kgsl_setup_ion(struct kgsl_mem_entry *entry, struct scatterlist *s; unsigned long flags; - if (IS_ERR_OR_NULL(kgsl_ion_client)) - return -ENODEV; + if (kgsl_ion_client == NULL) { + kgsl_ion_client = msm_ion_client_create(UINT_MAX, KGSL_NAME); + if (kgsl_ion_client == NULL) + return -ENODEV; + } handle = ion_import_fd(kgsl_ion_client, fd); if (IS_ERR_OR_NULL(handle)) @@ -1871,6 +1878,11 @@ static long kgsl_ioctl_timestamp_event(struct kgsl_device_private *dev_priv, param->timestamp, param->priv, param->len, dev_priv); break; + case KGSL_TIMESTAMP_EVENT_FENCE: + ret = kgsl_add_fence_event(dev_priv->device, + param->context_id, param->timestamp, param->priv, + param->len, dev_priv); + break; default: ret = -EINVAL; } @@ -1926,7 +1938,7 @@ static const struct { static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { struct kgsl_device_private *dev_priv = filep->private_data; - unsigned int nr = _IOC_NR(cmd); + unsigned int nr; kgsl_ioctl_func_t func; int lock, ret; char ustack[64]; @@ -1941,6 +1953,10 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) cmd = IOCTL_KGSL_CMDSTREAM_FREEMEMONTIMESTAMP; else if (cmd == IOCTL_KGSL_CMDSTREAM_READTIMESTAMP_OLD) cmd = IOCTL_KGSL_CMDSTREAM_READTIMESTAMP; + else if (cmd == IOCTL_KGSL_TIMESTAMP_EVENT_OLD) + cmd = IOCTL_KGSL_TIMESTAMP_EVENT; + + nr = _IOC_NR(cmd); if (cmd & (IOC_IN | IOC_OUT)) { if (_IOC_SIZE(cmd) < sizeof(ustack)) @@ -1966,7 +1982,20 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) } if (nr < ARRAY_SIZE(kgsl_ioctl_funcs) && - kgsl_ioctl_funcs[nr].func != NULL) { + kgsl_ioctl_funcs[nr].func != NULL) { + + /* + * Make sure that nobody tried to send us a malformed ioctl code + * with a valid NR but bogus flags + */ + + if (kgsl_ioctl_funcs[nr].cmd != cmd) { + KGSL_DRV_ERR(dev_priv->device, + "Malformed ioctl code %08x\n", cmd); + ret = -ENOIOCTLCMD; + goto done; + } + func = kgsl_ioctl_funcs[nr].func; lock = kgsl_ioctl_funcs[nr].lock; } else { @@ -2073,7 +2102,7 @@ static int kgsl_mmap(struct file *file, struct vm_area_struct *vma) unsigned long vma_offset = vma->vm_pgoff << PAGE_SHIFT; struct kgsl_device_private *dev_priv = file->private_data; struct kgsl_process_private *private = dev_priv->process_priv; - struct kgsl_mem_entry *tmp, *entry = NULL; + struct kgsl_mem_entry *entry = NULL; struct kgsl_device *device = dev_priv->device; /* Handle leagacy behavior for memstore */ @@ -2084,13 +2113,11 @@ static int kgsl_mmap(struct file *file, struct vm_area_struct *vma) /* Find a chunk of GPU memory */ spin_lock(&private->mem_lock); - list_for_each_entry(tmp, &private->mem_list, list) { - if (vma_offset == tmp->memdesc.gpuaddr) { - kgsl_mem_entry_get(tmp); - entry = tmp; - break; - } - } + entry = kgsl_sharedmem_find(private, vma_offset); + + if (entry) + kgsl_mem_entry_get(entry); + spin_unlock(&private->mem_lock); if (entry == NULL) @@ -2286,8 +2313,6 @@ int kgsl_device_platform_probe(struct kgsl_device *device, if (status) goto error; - kgsl_ion_client = msm_ion_client_create(UINT_MAX, KGSL_NAME); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, device->iomemname); if (res == NULL) { @@ -2376,8 +2401,8 @@ EXPORT_SYMBOL(kgsl_device_platform_remove); static int __devinit kgsl_ptdata_init(void) { - kgsl_driver.ptpool = kgsl_mmu_ptpool_init(KGSL_PAGETABLE_SIZE, - kgsl_pagetable_count); + kgsl_driver.ptpool = kgsl_mmu_ptpool_init(kgsl_pagetable_count); + if (!kgsl_driver.ptpool) return -ENOMEM; return 0; diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index c3635b8cacf27..d0f7c67324283 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2008-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -44,10 +44,6 @@ #define KGSL_PAGETABLE_ENTRIES(_sz) (((_sz) >> PAGE_SHIFT) + \ KGSL_PT_EXTRA_ENTRIES) -#define KGSL_PAGETABLE_SIZE \ -ALIGN(KGSL_PAGETABLE_ENTRIES(CONFIG_MSM_KGSL_PAGE_TABLE_SIZE) * \ -KGSL_PAGETABLE_ENTRY_SIZE, PAGE_SIZE) - #ifdef CONFIG_KGSL_PER_PROCESS_PAGE_TABLE #define KGSL_PAGETABLE_COUNT (CONFIG_MSM_KGSL_PAGE_TABLE_COUNT) #else @@ -143,7 +139,7 @@ struct kgsl_mem_entry { struct kgsl_memdesc memdesc; int memtype; void *priv_data; - struct list_head list; + struct rb_node node; uint32_t free_timestamp; /* back pointer to private structure under whose context this * allocation is made */ @@ -193,17 +189,26 @@ static inline int kgsl_gpuaddr_in_memdesc(const struct kgsl_memdesc *memdesc, } return 0; } + +static inline void *kgsl_memdesc_map(struct kgsl_memdesc *memdesc) +{ + if (memdesc->hostptr == NULL && memdesc->ops && + memdesc->ops->map_kernel_mem) + memdesc->ops->map_kernel_mem(memdesc); + + return memdesc->hostptr; +} + static inline uint8_t *kgsl_gpuaddr_to_vaddr(struct kgsl_memdesc *memdesc, unsigned int gpuaddr) { - if (memdesc->gpuaddr == 0 || - gpuaddr < memdesc->gpuaddr || - gpuaddr >= (memdesc->gpuaddr + memdesc->size) || - (NULL == memdesc->hostptr && memdesc->ops->map_kernel_mem && - memdesc->ops->map_kernel_mem(memdesc))) - return NULL; - - return memdesc->hostptr + (gpuaddr - memdesc->gpuaddr); + void *hostptr = NULL; + + if ((gpuaddr >= memdesc->gpuaddr) && + (gpuaddr < (memdesc->gpuaddr + memdesc->size))) + hostptr = kgsl_memdesc_map(memdesc); + + return hostptr != NULL ? hostptr + (gpuaddr - memdesc->gpuaddr) : NULL; } static inline int timestamp_cmp(unsigned int a, unsigned int b) @@ -237,5 +242,11 @@ kgsl_mem_entry_put(struct kgsl_mem_entry *entry) { kref_put(&entry->refcount, kgsl_mem_entry_destroy); } +int kgsl_add_event(struct kgsl_device *device, u32 ts, + void (*cb)(struct kgsl_device *, void *, u32), void *priv, + void *owner); + +void kgsl_cancel_events(struct kgsl_device *device, + void *owner); #endif /* __KGSL_H */ diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c index e9455cb827945..e06c94d6b9fbf 100644 --- a/drivers/gpu/msm/kgsl_cffdump.c +++ b/drivers/gpu/msm/kgsl_cffdump.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -359,7 +359,7 @@ void kgsl_cffdump_destroy() void kgsl_cffdump_open(enum kgsl_deviceid device_id) { kgsl_cffdump_memory_base(device_id, KGSL_PAGETABLE_BASE, - CONFIG_MSM_KGSL_PAGE_TABLE_SIZE, SZ_256K); + kgsl_mmu_get_ptsize(), SZ_256K); } void kgsl_cffdump_memory_base(enum kgsl_deviceid device_id, unsigned int base, @@ -497,190 +497,6 @@ int kgsl_cffdump_waitirq(void) } EXPORT_SYMBOL(kgsl_cffdump_waitirq); -#define ADDRESS_STACK_SIZE 256 -#define GET_PM4_TYPE3_OPCODE(x) ((*(x) >> 8) & 0xFF) -static unsigned int kgsl_cffdump_addr_count; - -static bool kgsl_cffdump_handle_type3(struct kgsl_device_private *dev_priv, - uint *hostaddr, bool check_only) -{ - static uint addr_stack[ADDRESS_STACK_SIZE]; - static uint size_stack[ADDRESS_STACK_SIZE]; - - switch (GET_PM4_TYPE3_OPCODE(hostaddr)) { - case CP_INDIRECT_BUFFER_PFD: - case CP_INDIRECT_BUFFER: - { - /* traverse indirect buffers */ - int i; - uint ibaddr = hostaddr[1]; - uint ibsize = hostaddr[2]; - - /* is this address already in encountered? */ - for (i = 0; - i < kgsl_cffdump_addr_count && addr_stack[i] != ibaddr; - ++i) - ; - - if (kgsl_cffdump_addr_count == i) { - addr_stack[kgsl_cffdump_addr_count] = ibaddr; - size_stack[kgsl_cffdump_addr_count++] = ibsize; - - if (kgsl_cffdump_addr_count >= ADDRESS_STACK_SIZE) { - KGSL_CORE_ERR("stack overflow\n"); - return false; - } - - return kgsl_cffdump_parse_ibs(dev_priv, NULL, - ibaddr, ibsize, check_only); - } else if (size_stack[i] != ibsize) { - KGSL_CORE_ERR("gpuaddr: 0x%08x, " - "wc: %u, with size wc: %u already on the " - "stack\n", ibaddr, ibsize, size_stack[i]); - return false; - } - } - break; - } - - return true; -} - -/* - * Traverse IBs and dump them to test vector. Detect swap by inspecting - * register writes, keeping note of the current state, and dump - * framebuffer config to test vector - */ -bool kgsl_cffdump_parse_ibs(struct kgsl_device_private *dev_priv, - const struct kgsl_memdesc *memdesc, uint gpuaddr, int sizedwords, - bool check_only) -{ - static uint level; /* recursion level */ - bool ret = true; - uint *hostaddr, *hoststart; - int dwords_left = sizedwords; /* dwords left in the current command - buffer */ - - if (level == 0) - kgsl_cffdump_addr_count = 0; - - if (memdesc == NULL) { - struct kgsl_mem_entry *entry; - spin_lock(&dev_priv->process_priv->mem_lock); - entry = kgsl_sharedmem_find_region(dev_priv->process_priv, - gpuaddr, sizedwords * sizeof(uint)); - spin_unlock(&dev_priv->process_priv->mem_lock); - if (entry == NULL) { - KGSL_CORE_ERR("did not find mapping " - "for gpuaddr: 0x%08x\n", gpuaddr); - return true; - } - memdesc = &entry->memdesc; - } - hostaddr = (uint *)kgsl_gpuaddr_to_vaddr(memdesc, gpuaddr); - if (hostaddr == NULL) { - KGSL_CORE_ERR("no kernel mapping for " - "gpuaddr: 0x%08x\n", gpuaddr); - return true; - } - - hoststart = hostaddr; - - level++; - - mb(); - kgsl_cache_range_op((struct kgsl_memdesc *)memdesc, - KGSL_CACHE_OP_INV); -#ifdef DEBUG - pr_info("kgsl: cffdump: ib: gpuaddr:0x%08x, wc:%d, hptr:%p\n", - gpuaddr, sizedwords, hostaddr); -#endif - - while (dwords_left > 0) { - int count = 0; /* dword count including packet header */ - bool cur_ret = true; - - switch (*hostaddr >> 30) { - case 0x0: /* type-0 */ - count = (*hostaddr >> 16)+2; - break; - case 0x1: /* type-1 */ - count = 2; - break; - case 0x3: /* type-3 */ - count = ((*hostaddr >> 16) & 0x3fff) + 2; - cur_ret = kgsl_cffdump_handle_type3(dev_priv, - hostaddr, check_only); - break; - default: - pr_warn("kgsl: cffdump: parse-ib: unexpected type: " - "type:%d, word:0x%08x @ 0x%p, gpu:0x%08x\n", - *hostaddr >> 30, *hostaddr, hostaddr, - gpuaddr+4*(sizedwords-dwords_left)); - cur_ret = false; - count = dwords_left; - break; - } - -#ifdef DEBUG - if (!cur_ret) { - pr_info("kgsl: cffdump: bad sub-type: #:%d/%d, v:0x%08x" - " @ 0x%p[gb:0x%08x], level:%d\n", - sizedwords-dwords_left, sizedwords, *hostaddr, - hostaddr, gpuaddr+4*(sizedwords-dwords_left), - level); - - print_hex_dump(KERN_ERR, level == 1 ? "IB1:" : "IB2:", - DUMP_PREFIX_OFFSET, 32, 4, hoststart, - sizedwords*4, 0); - } -#endif - ret = ret && cur_ret; - - /* jump to next packet */ - dwords_left -= count; - hostaddr += count; - cur_ret = dwords_left >= 0; - -#ifdef DEBUG - if (!cur_ret) { - pr_info("kgsl: cffdump: bad count: c:%d, #:%d/%d, " - "v:0x%08x @ 0x%p[gb:0x%08x], level:%d\n", - count, sizedwords-(dwords_left+count), - sizedwords, *(hostaddr-count), hostaddr-count, - gpuaddr+4*(sizedwords-(dwords_left+count)), - level); - - print_hex_dump(KERN_ERR, level == 1 ? "IB1:" : "IB2:", - DUMP_PREFIX_OFFSET, 32, 4, hoststart, - sizedwords*4, 0); - } -#endif - - ret = ret && cur_ret; - } - - if (!ret) - pr_info("kgsl: cffdump: parsing failed: gpuaddr:0x%08x, " - "host:0x%p, wc:%d\n", gpuaddr, hoststart, sizedwords); - - if (!check_only) { -#ifdef DEBUG - uint offset = gpuaddr - memdesc->gpuaddr; - pr_info("kgsl: cffdump: ib-dump: hostptr:%p, gpuaddr:%08x, " - "physaddr:%08x, offset:%d, size:%d", hoststart, - gpuaddr, memdesc->physaddr + offset, offset, - sizedwords*4); -#endif - kgsl_cffdump_syncmem(dev_priv, memdesc, gpuaddr, sizedwords*4, - false); - } - - level--; - - return ret; -} - static int subbuf_start_handler(struct rchan_buf *buf, void *subbuf, void *prev_subbuf, uint prev_padding) { diff --git a/drivers/gpu/msm/kgsl_cffdump.h b/drivers/gpu/msm/kgsl_cffdump.h index 140e4868e0ce1..2733cc3fab805 100644 --- a/drivers/gpu/msm/kgsl_cffdump.h +++ b/drivers/gpu/msm/kgsl_cffdump.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c index f5eeb3fb9d962..c6a275949e540 100644 --- a/drivers/gpu/msm/kgsl_debugfs.c +++ b/drivers/gpu/msm/kgsl_debugfs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2008-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2008-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/kgsl_debugfs.h b/drivers/gpu/msm/kgsl_debugfs.h index 5e10988076def..b13e7fddecbdf 100644 --- a/drivers/gpu/msm/kgsl_debugfs.h +++ b/drivers/gpu/msm/kgsl_debugfs.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2008-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2008-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index d7a25a121fcb4..73e8f87e1947d 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -23,6 +23,7 @@ #include "kgsl_pwrctrl.h" #include "kgsl_log.h" #include "kgsl_pwrscale.h" +#include #define KGSL_TIMEOUT_NONE 0 #define KGSL_TIMEOUT_DEFAULT 0xFFFFFFFF @@ -57,6 +58,7 @@ struct platform_device; struct kgsl_device_private; struct kgsl_context; struct kgsl_power_stats; +struct kgsl_event; struct kgsl_functable { /* Mandatory functions - these functions must be implemented @@ -96,7 +98,8 @@ struct kgsl_functable { /* Optional functions - these functions are not mandatory. The driver will check that the function pointer is not NULL before calling the hook */ - void (*setstate) (struct kgsl_device *device, uint32_t flags); + void (*setstate) (struct kgsl_device *device, unsigned int context_id, + uint32_t flags); int (*drawctxt_create) (struct kgsl_device *device, struct kgsl_pagetable *pagetable, struct kgsl_context *context, uint32_t flags); @@ -104,6 +107,8 @@ struct kgsl_functable { struct kgsl_context *context); long (*ioctl) (struct kgsl_device_private *dev_priv, unsigned int cmd, void *data); + int (*next_event)(struct kgsl_device *device, + struct kgsl_event *event); }; struct kgsl_memregion { @@ -127,7 +132,7 @@ struct kgsl_event { void (*func)(struct kgsl_device *, void *, u32); void *priv; struct list_head list; - struct kgsl_device_private *owner; + void *owner; }; @@ -189,6 +194,12 @@ struct kgsl_device { struct work_struct ts_expired_ws; struct list_head events; s64 on_time; + + /* page fault debugging parameters */ + struct work_struct print_fault_ib; + unsigned int page_fault_ptbase; + unsigned int page_fault_ib1; + unsigned int page_fault_rptr; }; struct kgsl_context { @@ -204,13 +215,19 @@ struct kgsl_context { * context was responsible for causing it */ unsigned int reset_status; + + /* + * Timeline used to create fences that can be signaled when a + * sync_pt timestamp expires. + */ + struct sync_timeline *timeline; }; struct kgsl_process_private { unsigned int refcnt; pid_t pid; spinlock_t mem_lock; - struct list_head mem_list; + struct rb_root mem_rb; struct kgsl_pagetable *pagetable; struct list_head list; struct kobject kobj; diff --git a/drivers/gpu/msm/kgsl_drm.c b/drivers/gpu/msm/kgsl_drm.c index 051fcfce86eea..58ec252dd16e1 100644 --- a/drivers/gpu/msm/kgsl_drm.c +++ b/drivers/gpu/msm/kgsl_drm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2009-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/kgsl_gpummu.c b/drivers/gpu/msm/kgsl_gpummu.c index ba8a719e42dd1..d41254561f491 100644 --- a/drivers/gpu/msm/kgsl_gpummu.c +++ b/drivers/gpu/msm/kgsl_gpummu.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -21,6 +21,11 @@ #include "kgsl_mmu.h" #include "kgsl_device.h" #include "kgsl_sharedmem.h" +#include "a2xx_reg.h" + +#define KGSL_PAGETABLE_SIZE \ + ALIGN(KGSL_PAGETABLE_ENTRIES(CONFIG_MSM_KGSL_PAGE_TABLE_SIZE) * \ + KGSL_PAGETABLE_ENTRY_SIZE, PAGE_SIZE) static ssize_t sysfs_show_ptpool_entries(struct kobject *kobj, @@ -310,16 +315,15 @@ void kgsl_gpummu_ptpool_destroy(void *ptpool) /** * kgsl_ptpool_init * @pool: A pointer to a ptpool structure to initialize - * @ptsize: The size of each pagetable entry * @entries: The number of inital entries to add to the pool * * Initalize a pool and allocate an initial chunk of entries. */ -void *kgsl_gpummu_ptpool_init(int ptsize, int entries) +void *kgsl_gpummu_ptpool_init(int entries) { + int ptsize = KGSL_PAGETABLE_SIZE; struct kgsl_ptpool *pool; int ret = 0; - BUG_ON(ptsize == 0); pool = kzalloc(sizeof(struct kgsl_ptpool), GFP_KERNEL); if (!pool) { @@ -354,8 +358,8 @@ void *kgsl_gpummu_ptpool_init(int ptsize, int entries) int kgsl_gpummu_pt_equal(struct kgsl_pagetable *pt, unsigned int pt_base) { - struct kgsl_gpummu_pt *gpummu_pt = pt ? pt->priv : NULL; - return gpummu_pt && pt_base && (gpummu_pt->base.gpuaddr == pt_base); + struct kgsl_gpummu_pt *gpummu_pt = pt->priv; + return pt && pt_base && (gpummu_pt->base.gpuaddr == pt_base); } void kgsl_gpummu_destroy_pagetable(void *mmu_specific_pt) @@ -405,7 +409,7 @@ static unsigned int kgsl_gpummu_pt_get_flags(struct kgsl_pagetable *pt, gpummu_pt = pt->priv; spin_lock(&pt->lock); - if (gpummu_pt->tlb_flags && (1<tlb_flags & (1<tlb_flags &= ~(1<id) { + unsigned int ib1; + unsigned int ib1_sz; + unsigned int ib2; + unsigned int ib2_sz; + unsigned int rptr; + kgsl_regread(device, REG_CP_IB1_BASE, &ib1); + kgsl_regread(device, REG_CP_IB1_BUFSZ, &ib1_sz); + kgsl_regread(device, REG_CP_IB2_BASE, &ib2); + kgsl_regread(device, REG_CP_IB2_BUFSZ, &ib2_sz); + kgsl_regread(device, REG_CP_RB_RPTR, &rptr); + + /* queue a work which will print the IB that caused the + * pagefault, if we are in recovery then no need to q + * work as the recovery routine will mess with the ringbuffer + * contents and then the information will become stale + * anyways */ + if (!device->page_fault_ptbase && + KGSL_STATE_DUMP_AND_RECOVER != device->state) { + device->page_fault_ptbase = ptbase; + device->page_fault_ib1 = ib1; + device->page_fault_rptr = rptr; + + queue_work(device->work_queue, + &device->print_fault_ib); + } + + KGSL_MEM_CRIT(device, + "mmu page fault: page=0x%lx pt=%d op=%s axi=%d " + "ptbase=0x%x IB1=0x%x IB1_SZ=0x%x " + "IB2=0x%x IB2_SZ=0x%x\n", reg & ~(PAGE_SIZE - 1), kgsl_mmu_get_ptname_from_ptbase(ptbase), - reg & 0x02 ? "WRITE" : "READ", (reg >> 4) & 0xF); + reg & 0x02 ? "WRITE" : "READ", (reg >> 4) & 0xF, + ptbase, ib1, ib1_sz, ib2, ib2_sz); + } } static void *kgsl_gpummu_create_pagetable(void) @@ -498,7 +533,8 @@ static void kgsl_gpummu_default_setstate(struct kgsl_device *device, } static void kgsl_gpummu_setstate(struct kgsl_device *device, - struct kgsl_pagetable *pagetable) + struct kgsl_pagetable *pagetable, + unsigned int context_id) { struct kgsl_mmu *mmu = &device->mmu; struct kgsl_gpummu_pt *gpummu_pt; @@ -515,7 +551,8 @@ static void kgsl_gpummu_setstate(struct kgsl_device *device, spin_unlock(&mmu->hwpagetable->lock); /* call device specific set page table */ - kgsl_setstate(mmu->device, KGSL_MMUFLAGS_TLBFLUSH | + kgsl_setstate(mmu->device, context_id, + KGSL_MMUFLAGS_TLBFLUSH | KGSL_MMUFLAGS_PTUPDATE); } } @@ -610,7 +647,7 @@ static int kgsl_gpummu_start(struct kgsl_device *device) kgsl_regwrite(device, MH_MMU_VA_RANGE, (KGSL_PAGETABLE_BASE | (CONFIG_MSM_KGSL_PAGE_TABLE_SIZE >> 16))); - kgsl_setstate(device, KGSL_MMUFLAGS_TLBFLUSH); + kgsl_setstate(device, 0, KGSL_MMUFLAGS_TLBFLUSH); mmu->flags |= KGSL_FLAGS_STARTED; return 0; diff --git a/drivers/gpu/msm/kgsl_gpummu.h b/drivers/gpu/msm/kgsl_gpummu.h index 46466a8d2e68f..fc46f3c9d9caf 100644 --- a/drivers/gpu/msm/kgsl_gpummu.h +++ b/drivers/gpu/msm/kgsl_gpummu.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -73,8 +73,7 @@ struct kgsl_ptpool { int chunks; }; -void *kgsl_gpummu_ptpool_init(int ptsize, - int entries); +void *kgsl_gpummu_ptpool_init(int entries); void kgsl_gpummu_ptpool_destroy(void *ptpool); static inline unsigned int kgsl_pt_get_base_addr(struct kgsl_pagetable *pt) diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index 5646d682a0d78..c471569ee88f3 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -34,8 +34,8 @@ struct kgsl_iommu { static int kgsl_iommu_pt_equal(struct kgsl_pagetable *pt, unsigned int pt_base) { - struct iommu_domain *domain = pt ? pt->priv : NULL; - return domain && pt_base && ((unsigned int)domain == pt_base); + struct iommu_domain *domain = pt->priv; + return pt && pt_base && ((unsigned int)domain == pt_base); } static void kgsl_iommu_destroy_pagetable(void *mmu_specific_pt) @@ -144,7 +144,8 @@ static int kgsl_get_iommu_ctxt(struct kgsl_iommu *iommu, } static void kgsl_iommu_setstate(struct kgsl_device *device, - struct kgsl_pagetable *pagetable) + struct kgsl_pagetable *pagetable, + unsigned int context_id) { struct kgsl_mmu *mmu = &device->mmu; diff --git a/drivers/gpu/msm/kgsl_log.h b/drivers/gpu/msm/kgsl_log.h index 9fafcf4d684b1..0cbdaff93b057 100644 --- a/drivers/gpu/msm/kgsl_log.h +++ b/drivers/gpu/msm/kgsl_log.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2008-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2008-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c index 36248ef2f757a..cc5a9abf9dd5f 100644 --- a/drivers/gpu/msm/kgsl_mmu.c +++ b/drivers/gpu/msm/kgsl_mmu.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -147,9 +147,10 @@ sysfs_show_va_range(struct kobject *kobj, pt = _get_pt_from_kobj(kobj); - if (pt) + if (pt) { ret += snprintf(buf, PAGE_SIZE, "0x%x\n", - CONFIG_MSM_KGSL_PAGE_TABLE_SIZE); + kgsl_mmu_get_ptsize()); + } kgsl_put_pagetable(pt); return ret; @@ -267,6 +268,22 @@ pagetable_add_sysfs_objects(struct kgsl_pagetable *pagetable) return ret; } +unsigned int kgsl_mmu_get_ptsize(void) +{ + /* + * For IOMMU, we could do up to 4G virtual range if we wanted to, but + * it makes more sense to return a smaller range and leave the rest of + * the virtual range for future improvements + */ + + if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type) + return CONFIG_MSM_KGSL_PAGE_TABLE_SIZE; + else if (KGSL_MMU_TYPE_IOMMU == kgsl_mmu_type) + return SZ_2G; + else + return 0; +} + unsigned int kgsl_mmu_get_current_ptbase(struct kgsl_device *device) { struct kgsl_mmu *mmu = &device->mmu; @@ -297,7 +314,8 @@ kgsl_mmu_get_ptname_from_ptbase(unsigned int pt_base) EXPORT_SYMBOL(kgsl_mmu_get_ptname_from_ptbase); void kgsl_mmu_setstate(struct kgsl_device *device, - struct kgsl_pagetable *pagetable) + struct kgsl_pagetable *pagetable, + unsigned int context_id) { struct kgsl_mmu *mmu = &device->mmu; @@ -305,7 +323,7 @@ void kgsl_mmu_setstate(struct kgsl_device *device, return; else mmu->mmu_ops->mmu_setstate(device, - pagetable); + pagetable, context_id); } EXPORT_SYMBOL(kgsl_mmu_setstate); @@ -391,6 +409,7 @@ static struct kgsl_pagetable *kgsl_mmu_createpagetableobject( int status = 0; struct kgsl_pagetable *pagetable = NULL; unsigned long flags; + unsigned int ptsize; pagetable = kzalloc(sizeof(struct kgsl_pagetable), GFP_KERNEL); if (pagetable == NULL) { @@ -402,9 +421,11 @@ static struct kgsl_pagetable *kgsl_mmu_createpagetableobject( kref_init(&pagetable->refcount); spin_lock_init(&pagetable->lock); + + ptsize = kgsl_mmu_get_ptsize(); + pagetable->name = name; - pagetable->max_entries = KGSL_PAGETABLE_ENTRIES( - CONFIG_MSM_KGSL_PAGE_TABLE_SIZE); + pagetable->max_entries = KGSL_PAGETABLE_ENTRIES(ptsize); pagetable->pool = gen_pool_create(PAGE_SHIFT, -1); if (pagetable->pool == NULL) { @@ -413,7 +434,7 @@ static struct kgsl_pagetable *kgsl_mmu_createpagetableobject( } if (gen_pool_add(pagetable->pool, KGSL_PAGETABLE_BASE, - CONFIG_MSM_KGSL_PAGE_TABLE_SIZE, -1)) { + ptsize, -1)) { KGSL_CORE_ERR("gen_pool_add failed\n"); goto err_pool; } @@ -477,13 +498,14 @@ void kgsl_mmu_putpagetable(struct kgsl_pagetable *pagetable) } EXPORT_SYMBOL(kgsl_mmu_putpagetable); -void kgsl_setstate(struct kgsl_device *device, uint32_t flags) +void kgsl_setstate(struct kgsl_device *device, unsigned int context_id, + uint32_t flags) { struct kgsl_mmu *mmu = &device->mmu; if (KGSL_MMU_TYPE_NONE == kgsl_mmu_type) return; else if (device->ftbl->setstate) - device->ftbl->setstate(device, flags); + device->ftbl->setstate(device, context_id, flags); else if (mmu->mmu_ops->mmu_device_setstate) mmu->mmu_ops->mmu_device_setstate(device, flags); } @@ -676,10 +698,10 @@ void kgsl_mmu_ptpool_destroy(void *ptpool) } EXPORT_SYMBOL(kgsl_mmu_ptpool_destroy); -void *kgsl_mmu_ptpool_init(int ptsize, int entries) +void *kgsl_mmu_ptpool_init(int entries) { if (KGSL_MMU_TYPE_GPU == kgsl_mmu_type) - return kgsl_gpummu_ptpool_init(ptsize, entries); + return kgsl_gpummu_ptpool_init(entries); else return (void *)(-1); } diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h index 1338d0d4b0083..07cd6d9d9e2d5 100644 --- a/drivers/gpu/msm/kgsl_mmu.h +++ b/drivers/gpu/msm/kgsl_mmu.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -112,7 +112,8 @@ struct kgsl_mmu_ops { int (*mmu_start) (struct kgsl_device *device); int (*mmu_stop) (struct kgsl_device *device); void (*mmu_setstate) (struct kgsl_device *device, - struct kgsl_pagetable *pagetable); + struct kgsl_pagetable *pagetable, + unsigned int context_id); void (*mmu_device_setstate) (struct kgsl_device *device, uint32_t flags); void (*mmu_pagefault) (struct kgsl_device *device); @@ -168,20 +169,29 @@ int kgsl_mmu_map_global(struct kgsl_pagetable *pagetable, int kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, struct kgsl_memdesc *memdesc); unsigned int kgsl_virtaddr_to_physaddr(void *virtaddr); -void kgsl_setstate(struct kgsl_device *device, uint32_t flags); +void kgsl_setstate(struct kgsl_device *device, unsigned int context_id, + uint32_t flags); void kgsl_mmu_device_setstate(struct kgsl_device *device, uint32_t flags); void kgsl_mmu_setstate(struct kgsl_device *device, - struct kgsl_pagetable *pt); + struct kgsl_pagetable *pt, unsigned int context_id); int kgsl_mmu_get_ptname_from_ptbase(unsigned int pt_base); int kgsl_mmu_pt_get_flags(struct kgsl_pagetable *pt, enum kgsl_deviceid id); - void kgsl_mmu_ptpool_destroy(void *ptpool); -void *kgsl_mmu_ptpool_init(int ptsize, int entries); +void *kgsl_mmu_ptpool_init(int entries); int kgsl_mmu_enabled(void); int kgsl_mmu_pt_equal(struct kgsl_pagetable *pt, unsigned int pt_base); void kgsl_mmu_set_mmutype(char *mmutype); unsigned int kgsl_mmu_get_current_ptbase(struct kgsl_device *device); enum kgsl_mmutype kgsl_mmu_get_mmutype(void); +unsigned int kgsl_mmu_get_ptsize(void); + +static inline int kgsl_mmu_gpuaddr_in_range(unsigned int gpuaddr) +{ + return ((gpuaddr >= KGSL_PAGETABLE_BASE) && + (gpuaddr < + (KGSL_PAGETABLE_BASE + CONFIG_MSM_KGSL_PAGE_TABLE_SIZE))); +} + #endif /* __KGSL_MMU_H */ diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index f3457ce6e0895..63d2301ecec69 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -54,14 +54,6 @@ struct clk_pair clks[KGSL_MAX_CLKS] = { .name = "mem_iface_clk", .map = KGSL_CLK_MEM_IFACE, }, - { - .name = "grp_clk", - .map = KGSL_CLK_GRP, - }, - { - .name = "imem_clk", - .map = KGSL_CLK_IMEM, - }, }; void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, @@ -518,7 +510,6 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) pwr->nap_allowed = pdata->nap_allowed; pwr->idle_needed = pdata->idle_needed; pwr->interval_timeout = pdata->idle_timeout; - pwr->strtstp_sleepwake = pdata->strtstp_sleepwake; pwr->ebi1_clk = clk_get(&pdev->dev, "bus_clk"); if (IS_ERR(pwr->ebi1_clk)) pwr->ebi1_clk = NULL; @@ -641,8 +632,7 @@ void kgsl_timer(unsigned long data) KGSL_PWR_INFO(device, "idle timer expired device %d\n", device->id); if (device->requested_state != KGSL_STATE_SUSPEND) { - if (device->pwrctrl.restore_slumber || - device->pwrctrl.strtstp_sleepwake) + if (device->pwrctrl.restore_slumber) kgsl_pwrctrl_request_state(device, KGSL_STATE_SLUMBER); else kgsl_pwrctrl_request_state(device, KGSL_STATE_SLEEP); diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 76457228a1aff..5428017ff4f14 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -22,7 +22,7 @@ #define KGSL_PWRLEVEL_TURBO 0 #define KGSL_PWRLEVEL_NOMINAL 1 -#define KGSL_MAX_CLKS 7 +#define KGSL_MAX_CLKS 5 struct platform_device; @@ -47,7 +47,6 @@ struct kgsl_pwrctrl { int thermal_pwrlevel; unsigned int num_pwrlevels; unsigned int interval_timeout; - bool strtstp_sleepwake; struct regulator *gpu_reg; uint32_t pcl; unsigned int nap_allowed; diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c index d0b2a412cae90..f6f69b0b899f3 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.c +++ b/drivers/gpu/msm/kgsl_pwrscale.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/kgsl_pwrscale.h b/drivers/gpu/msm/kgsl_pwrscale.h index b4f831ee1bd2f..512375e69b201 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.h +++ b/drivers/gpu/msm/kgsl_pwrscale.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/kgsl_pwrscale_idlestats.c b/drivers/gpu/msm/kgsl_pwrscale_idlestats.c index 2976b0bccaa72..acf98c5045119 100644 --- a/drivers/gpu/msm/kgsl_pwrscale_idlestats.c +++ b/drivers/gpu/msm/kgsl_pwrscale_idlestats.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c index 931ef48b49544..9bcb88914b974 100644 --- a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c +++ b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index 57cd683348222..67fff1802d46c 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -463,7 +463,8 @@ _kgsl_sharedmem_vmalloc(struct kgsl_memdesc *memdesc, memdesc->priv = KGSL_MEMFLAGS_CACHED; memdesc->ops = &kgsl_vmalloc_ops; - memdesc->sg = vmalloc(sglen * sizeof(struct scatterlist)); + memdesc->sg = kgsl_sg_alloc(sglen); + if (memdesc->sg == NULL) { ret = -ENOMEM; goto done; @@ -591,7 +592,7 @@ void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc) if (memdesc->ops && memdesc->ops->free) memdesc->ops->free(memdesc); - vfree(memdesc->sg); + kgsl_sg_free(memdesc->sg, memdesc->sglen); memset(memdesc, 0, sizeof(*memdesc)); } diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h index 57779fda3f251..0d4cbacb968d2 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.h +++ b/drivers/gpu/msm/kgsl_sharedmem.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -87,13 +87,34 @@ int kgsl_sharedmem_map_vma(struct vm_area_struct *vma, const struct kgsl_memdesc *memdesc); +/* + * For relatively small sglists, it is preferable to use kzalloc + * rather than going down the vmalloc rat hole. If the size of + * the sglist is < PAGE_SIZE use kzalloc otherwise fallback to + * vmalloc + */ + +static inline void *kgsl_sg_alloc(unsigned int sglen) +{ + if ((sglen * sizeof(struct scatterlist)) < PAGE_SIZE) + return kzalloc(sglen * sizeof(struct scatterlist), GFP_KERNEL); + else + return vmalloc(sglen * sizeof(struct scatterlist)); +} + +static inline void kgsl_sg_free(void *ptr, unsigned int sglen) +{ + if ((sglen * sizeof(struct scatterlist)) < PAGE_SIZE) + kfree(ptr); + else + vfree(ptr); +} + static inline int memdesc_sg_phys(struct kgsl_memdesc *memdesc, unsigned int physaddr, unsigned int size) { - memdesc->sg = vmalloc(sizeof(struct scatterlist) * 1); - if (memdesc->sg == NULL) - return -ENOMEM; + memdesc->sg = kgsl_sg_alloc(1); kmemleak_not_leak(memdesc->sg); diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c index 394bc83bdd061..e1e704b9e9440 100644 --- a/drivers/gpu/msm/kgsl_snapshot.c +++ b/drivers/gpu/msm/kgsl_snapshot.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/kgsl_snapshot.h b/drivers/gpu/msm/kgsl_snapshot.h index 4fdc8a1b665ac..86c2c84f0c3e3 100644 --- a/drivers/gpu/msm/kgsl_snapshot.h +++ b/drivers/gpu/msm/kgsl_snapshot.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c new file mode 100644 index 0000000000000..11dd8716dcc77 --- /dev/null +++ b/drivers/gpu/msm/kgsl_sync.c @@ -0,0 +1,211 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include + +#include "kgsl_sync.h" + +struct sync_pt *kgsl_sync_pt_create(struct sync_timeline *timeline, + unsigned int timestamp) +{ + struct sync_pt *pt; + pt = sync_pt_create(timeline, (int) sizeof(struct kgsl_sync_pt)); + if (pt) { + struct kgsl_sync_pt *kpt = (struct kgsl_sync_pt *) pt; + kpt->timestamp = timestamp; + } + return pt; +} + +/* + * This should only be called on sync_pts which have been created but + * not added to a fence. + */ +void kgsl_sync_pt_destroy(struct sync_pt *pt) +{ + sync_pt_free(pt); +} + +static struct sync_pt *kgsl_sync_pt_dup(struct sync_pt *pt) +{ + struct kgsl_sync_pt *kpt = (struct kgsl_sync_pt *) pt; + return kgsl_sync_pt_create(pt->parent, kpt->timestamp); +} + +static int kgsl_sync_pt_has_signaled(struct sync_pt *pt) +{ + struct kgsl_sync_pt *kpt = (struct kgsl_sync_pt *) pt; + struct kgsl_sync_timeline *ktimeline = + (struct kgsl_sync_timeline *) pt->parent; + unsigned int ts = kpt->timestamp; + unsigned int last_ts = ktimeline->last_timestamp; + if (timestamp_cmp(last_ts, ts) >= 0) { + /* signaled */ + return 1; + } + return 0; +} + +static int kgsl_sync_pt_compare(struct sync_pt *a, struct sync_pt *b) +{ + struct kgsl_sync_pt *kpt_a = (struct kgsl_sync_pt *) a; + struct kgsl_sync_pt *kpt_b = (struct kgsl_sync_pt *) b; + unsigned int ts_a = kpt_a->timestamp; + unsigned int ts_b = kpt_b->timestamp; + return timestamp_cmp(ts_a, ts_b); +} + +struct kgsl_fence_event_priv { + struct kgsl_context *context; +}; + +/** + * kgsl_fence_event_cb - Event callback for a fence timestamp event + * @device - The KGSL device that expired the timestamp + * @priv - private data for the event + * @context_id - the context id that goes with the timestamp + * @timestamp - the timestamp that triggered the event + * + * Signal a fence following the expiration of a timestamp + */ + +static inline void kgsl_fence_event_cb(struct kgsl_device *device, + void *priv, u32 timestamp) +{ + struct kgsl_fence_event_priv *ev = priv; + kgsl_sync_timeline_signal(ev->context->timeline, timestamp); + kfree(ev); +} + +/** + * kgsl_add_fence_event - Create a new fence event + * @device - KGSL device to create the event on + * @timestamp - Timestamp to trigger the event + * @data - Return fence fd stored in struct kgsl_timestamp_event_fence + * @len - length of the fence event + * @owner - driver instance that owns this event + * @returns 0 on success or error code on error + * + * Create a fence and register an event to signal the fence when + * the timestamp expires + */ + +int kgsl_add_fence_event(struct kgsl_device *device, + u32 context_id, u32 timestamp, void __user *data, int len, + struct kgsl_device_private *owner) +{ + struct kgsl_fence_event_priv *event; + struct kgsl_timestamp_event_fence priv; + struct kgsl_context *context; + struct sync_pt *pt; + struct sync_fence *fence = NULL; + int ret = -EINVAL; + + if (len != sizeof(priv)) + return -EINVAL; + + context = kgsl_find_context(owner, context_id); + if (context == NULL) + return -EINVAL; + + event = kzalloc(sizeof(*event), GFP_KERNEL); + if (event == NULL) + return -ENOMEM; + event->context = context; + + pt = kgsl_sync_pt_create(context->timeline, timestamp); + if (pt == NULL) { + KGSL_DRV_ERR(device, "kgsl_sync_pt_create failed\n"); + ret = -ENOMEM; + goto fail_pt; + } + + fence = sync_fence_create("kgsl-fence", pt); + if (fence == NULL) { + /* only destroy pt when not added to fence */ + kgsl_sync_pt_destroy(pt); + KGSL_DRV_ERR(device, "sync_fence_create failed\n"); + ret = -ENOMEM; + goto fail_fence; + } + + priv.fence_fd = get_unused_fd_flags(0); + if (priv.fence_fd < 0) { + KGSL_DRV_ERR(device, "invalid fence fd\n"); + ret = -EINVAL; + goto fail_fd; + } + sync_fence_install(fence, priv.fence_fd); + + if (copy_to_user(data, &priv, sizeof(priv))) { + ret = -EFAULT; + goto fail_copy_fd; + } + + ret = kgsl_add_event(device, timestamp, + kgsl_fence_event_cb, event, owner); + if (ret) + goto fail_event; + + return 0; + +fail_event: +fail_copy_fd: + /* clean up sync_fence_install */ + sync_fence_put(fence); + put_unused_fd(priv.fence_fd); +fail_fd: + /* clean up sync_fence_create */ + sync_fence_put(fence); +fail_fence: +fail_pt: + kfree(event); + return ret; +} + +static const struct sync_timeline_ops kgsl_sync_timeline_ops = { + .dup = kgsl_sync_pt_dup, + .has_signaled = kgsl_sync_pt_has_signaled, + .compare = kgsl_sync_pt_compare, +}; + +int kgsl_sync_timeline_create(struct kgsl_context *context) +{ + struct kgsl_sync_timeline *ktimeline; + + context->timeline = sync_timeline_create(&kgsl_sync_timeline_ops, + (int) sizeof(struct kgsl_sync_timeline), "kgsl-timeline"); + if (context->timeline == NULL) + return -EINVAL; + + ktimeline = (struct kgsl_sync_timeline *) context->timeline; + ktimeline->last_timestamp = 0; + + return 0; +} + +void kgsl_sync_timeline_signal(struct sync_timeline *timeline, + unsigned int timestamp) +{ + struct kgsl_sync_timeline *ktimeline = + (struct kgsl_sync_timeline *) timeline; + ktimeline->last_timestamp = timestamp; + sync_timeline_signal(timeline); +} + +void kgsl_sync_timeline_destroy(struct kgsl_context *context) +{ + sync_timeline_destroy(context->timeline); +} diff --git a/drivers/gpu/msm/kgsl_sync.h b/drivers/gpu/msm/kgsl_sync.h new file mode 100644 index 0000000000000..06b3ad0d89188 --- /dev/null +++ b/drivers/gpu/msm/kgsl_sync.h @@ -0,0 +1,75 @@ +/* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __KGSL_SYNC_H +#define __KGSL_SYNC_H + +#include +#include "kgsl_device.h" + +struct kgsl_sync_timeline { + struct sync_timeline timeline; + unsigned int last_timestamp; +}; + +struct kgsl_sync_pt { + struct sync_pt pt; + unsigned int timestamp; +}; + +#if defined(CONFIG_SYNC) +struct sync_pt *kgsl_sync_pt_create(struct sync_timeline *timeline, + unsigned int timestamp); +void kgsl_sync_pt_destroy(struct sync_pt *pt); +int kgsl_add_fence_event(struct kgsl_device *device, + u32 context_id, u32 timestamp, void __user *data, int len, + struct kgsl_device_private *owner); +int kgsl_sync_timeline_create(struct kgsl_context *context); +void kgsl_sync_timeline_signal(struct sync_timeline *timeline, + unsigned int timestamp); +void kgsl_sync_timeline_destroy(struct kgsl_context *context); +#else +static inline struct sync_pt +*kgsl_sync_pt_create(struct sync_timeline *timeline, unsigned int timestamp) +{ + return NULL; +} + +static inline void kgsl_sync_pt_destroy(struct sync_pt *pt) +{ +} + +static inline int kgsl_add_fence_event(struct kgsl_device *device, + u32 context_id, u32 timestamp, void __user *data, int len, + struct kgsl_device_private *owner) +{ + return -EINVAL; +} + +static int kgsl_sync_timeline_create(struct kgsl_context *context) +{ + context->timeline = NULL; + return 0; +} + +static inline void +kgsl_sync_timeline_signal(struct sync_timeline *timeline, + unsigned int timestamp) +{ +} + +static inline void kgsl_sync_timeline_destroy(struct kgsl_context *context) +{ +} +#endif + +#endif /* __KGSL_SYNC_H */ diff --git a/drivers/gpu/msm/kgsl_trace.c b/drivers/gpu/msm/kgsl_trace.c index 2bcca15414bb3..e432729fb3537 100644 --- a/drivers/gpu/msm/kgsl_trace.c +++ b/drivers/gpu/msm/kgsl_trace.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h index 86a9adc058606..0ebc276fb71dc 100644 --- a/drivers/gpu/msm/kgsl_trace.h +++ b/drivers/gpu/msm/kgsl_trace.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c index d6f3badac01e9..154a1f4e5bb47 100644 --- a/drivers/gpu/msm/z180.c +++ b/drivers/gpu/msm/z180.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2012, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -393,7 +393,7 @@ z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv, unsigned int index = 0; unsigned int nextindex; unsigned int nextcnt = Z180_STREAM_END_CMD | 5; - struct kgsl_memdesc tmp = {0}; + struct kgsl_mem_entry *entry = NULL; unsigned int cmd; struct kgsl_device *device = dev_priv->device; struct kgsl_pagetable *pagetable = dev_priv->process_priv->pagetable; @@ -411,8 +411,30 @@ z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv, } cmd = ibdesc[0].gpuaddr; sizedwords = ibdesc[0].sizedwords; - - tmp.hostptr = (void *)*timestamp; + /* + * Get a kernel mapping to the IB for monkey patching. + * See the end of this function. + */ + entry = kgsl_sharedmem_find_region(dev_priv->process_priv, cmd, + sizedwords); + if (entry == NULL) { + KGSL_DRV_ERR(device, "Bad ibdesc: gpuaddr 0x%x size %d\n", + cmd, sizedwords); + result = -EINVAL; + goto error; + } + /* + * This will only map memory if it exists, otherwise it will reuse the + * mapping. And the 2d userspace reuses IBs so we likely won't create + * too many mappings. + */ + if (kgsl_gpuaddr_to_vaddr(&entry->memdesc, cmd) == NULL) { + KGSL_DRV_ERR(device, + "Cannot make kernel mapping for gpuaddr 0x%x\n", + cmd); + result = -EINVAL; + goto error; + } KGSL_CMD_INFO(device, "ctxt %d ibaddr 0x%08x sizedwords %d\n", context->id, cmd, sizedwords); @@ -421,12 +443,13 @@ z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv, (ctrl & KGSL_CONTEXT_CTX_SWITCH)) { KGSL_CMD_INFO(device, "context switch %d -> %d\n", context->id, z180_dev->ringbuffer.prevctx); - kgsl_mmu_setstate(device, pagetable); + kgsl_mmu_setstate(device, pagetable, + 0); cnt = PACKETSIZE_STATESTREAM; ofs = 0; } - kgsl_setstate(device, kgsl_mmu_pt_get_flags(device->mmu.hwpagetable, - device->id)); + kgsl_setstate(device, 0, kgsl_mmu_pt_get_flags(device->mmu.hwpagetable, + device->id)); result = wait_event_interruptible_timeout(device->wait_queue, room_in_rb(z180_dev), @@ -454,12 +477,13 @@ z180_cmdstream_issueibcmds(struct kgsl_device_private *dev_priv, nextaddr = z180_dev->ringbuffer.cmdbufdesc.gpuaddr + rb_offset(nextindex); - tmp.hostptr = (void *)(tmp.hostptr + - (sizedwords * sizeof(unsigned int))); - tmp.size = 12; - - kgsl_sharedmem_writel(&tmp, 4, nextaddr); - kgsl_sharedmem_writel(&tmp, 8, nextcnt); + /* monkey patch the IB so that it jumps back to the ringbuffer */ + kgsl_sharedmem_writel(&entry->memdesc, + ((sizedwords + 1) * sizeof(unsigned int)), + nextaddr); + kgsl_sharedmem_writel(&entry->memdesc, + ((sizedwords + 2) * sizeof(unsigned int)), + nextcnt); /* sync memory before activating the hardware for the new command*/ mb(); @@ -840,7 +864,8 @@ z180_drawctxt_destroy(struct kgsl_device *device, if (z180_dev->ringbuffer.prevctx == context->id) { z180_dev->ringbuffer.prevctx = Z180_INVALID_CONTEXT; device->mmu.hwpagetable = device->mmu.defaultpagetable; - kgsl_setstate(device, KGSL_MMUFLAGS_PTUPDATE); + kgsl_setstate(device, 0, + KGSL_MMUFLAGS_PTUPDATE); } } diff --git a/drivers/gpu/msm/z180.h b/drivers/gpu/msm/z180.h index e5c5ef303c57d..b7ff8140eb0b8 100644 --- a/drivers/gpu/msm/z180.h +++ b/drivers/gpu/msm/z180.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2008-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/z180_reg.h b/drivers/gpu/msm/z180_reg.h index 5b6c0017f03a0..41a17ce610826 100644 --- a/drivers/gpu/msm/z180_reg.h +++ b/drivers/gpu/msm/z180_reg.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2002,2007-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/z180_trace.c b/drivers/gpu/msm/z180_trace.c index 29b519c400535..9d971ee885c32 100644 --- a/drivers/gpu/msm/z180_trace.c +++ b/drivers/gpu/msm/z180_trace.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/gpu/msm/z180_trace.h b/drivers/gpu/msm/z180_trace.h index fbe1fe54ee219..4f65b9b2ad788 100644 --- a/drivers/gpu/msm/z180_trace.h +++ b/drivers/gpu/msm/z180_trace.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index ef72609f1712b..0f22d4d7cd8b6 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -34,10 +34,6 @@ #define KGSL_CLK_MEM_IFACE 0x00000010 #define KGSL_CLK_AXI 0x00000020 -/* Arbitrary defines for 8x50 devices using google clock naming / implementation */ -#define KGSL_CLK_GRP 0x00001000 -#define KGSL_CLK_IMEM 0x00010000 - /* * Reset status values for context */ @@ -161,7 +157,6 @@ struct kgsl_device_platform_data { int num_levels; int (*set_grp_async)(void); unsigned int idle_timeout; - bool strtstp_sleepwake; unsigned int nap_allowed; unsigned int clk_map; unsigned int idle_needed; @@ -441,7 +436,8 @@ struct kgsl_cff_syncmem { /* * A timestamp event allows the user space to register an action following an - * expired timestamp. + * expired timestamp. Note IOCTL_KGSL_TIMESTAMP_EVENT has been redefined to + * _IOWR to support fences which need to return a fd for the priv parameter. */ struct kgsl_timestamp_event { @@ -452,7 +448,7 @@ struct kgsl_timestamp_event { size_t len; /* Size of the event specific blob */ }; -#define IOCTL_KGSL_TIMESTAMP_EVENT \ +#define IOCTL_KGSL_TIMESTAMP_EVENT_OLD \ _IOW(KGSL_IOC_TYPE, 0x31, struct kgsl_timestamp_event) /* A genlock timestamp event releases an existing lock on timestamp expire */ @@ -463,6 +459,17 @@ struct kgsl_timestamp_event_genlock { int handle; /* Handle of the genlock lock to release */ }; +/* A fence timestamp event releases an existing lock on timestamp expire */ + +#define KGSL_TIMESTAMP_EVENT_FENCE 2 + +struct kgsl_timestamp_event_fence { + int fence_fd; /* Fence to signal */ +}; + +#define IOCTL_KGSL_TIMESTAMP_EVENT \ + _IOWR(KGSL_IOC_TYPE, 0x33, struct kgsl_timestamp_event) + #ifdef __KERNEL__ #ifdef CONFIG_MSM_KGSL_DRM int kgsl_gem_obj_addr(int drm_fd, int handle, unsigned long *start, From 08a13866218f752c2cda39651695495c01a7885a Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 24 May 2012 14:51:35 -0500 Subject: [PATCH 2469/2556] kgsl: support grp/imem clk for qsd8k dirty hack to initialize the kgsl-3d0 device caf changes the clock names to be uniform across platforms but we are still using googles clock, this way is easier until i feel like doing it proper. --- drivers/gpu/msm/kgsl_pwrctrl.c | 8 ++++++++ drivers/gpu/msm/kgsl_pwrctrl.h | 2 +- include/linux/msm_kgsl.h | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 63d2301ecec69..7704227643285 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -54,6 +54,14 @@ struct clk_pair clks[KGSL_MAX_CLKS] = { .name = "mem_iface_clk", .map = KGSL_CLK_MEM_IFACE, }, + { + .name = "grp_clk", + .map = KGSL_CLK_GRP, + }, + { + .name = "imem_clk", + .map = KGSL_CLK_IMEM, + }, }; void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 5428017ff4f14..e37b334a13b32 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -22,7 +22,7 @@ #define KGSL_PWRLEVEL_TURBO 0 #define KGSL_PWRLEVEL_NOMINAL 1 -#define KGSL_MAX_CLKS 5 +#define KGSL_MAX_CLKS 7 struct platform_device; diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h index 0f22d4d7cd8b6..1c789691c63bf 100644 --- a/include/linux/msm_kgsl.h +++ b/include/linux/msm_kgsl.h @@ -34,6 +34,10 @@ #define KGSL_CLK_MEM_IFACE 0x00000010 #define KGSL_CLK_AXI 0x00000020 +/* Arbitrary defines for 8x50 devices using google clock naming / implementation */ +#define KGSL_CLK_GRP 0x00001000 +#define KGSL_CLK_IMEM 0x00010000 + /* * Reset status values for context */ From ecd1d31f1ff235b013f9c3a6897fd8aef5ec4971 Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Tue, 13 Mar 2012 15:34:34 -0700 Subject: [PATCH 2470/2556] sync: Add synchronization framework not run through checkpatch yet. Change-Id: I209f9db2824e0313f467f11ab09e5f54f0a4a6b5 Signed-off-by: Erik Gilling Conflicts: drivers/base/Kconfig --- drivers/base/Kconfig | 9 + drivers/base/Makefile | 2 + drivers/base/sync.c | 525 ++++++++++++++++++++++++++++++++++++++++++ include/linux/sync.h | 314 +++++++++++++++++++++++++ 4 files changed, 850 insertions(+) create mode 100644 drivers/base/sync.c create mode 100644 include/linux/sync.h diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index e59425baf8498..6494fd4d11405 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -182,4 +182,13 @@ config GENLOCK_MISCDEVICE Create a miscdevice for the purposes of allowing userspace to create and interact with locks created using genlock. +config SYNC + bool "Synchronization framework" + default n + select ANON_INODES + help + This option enables the framework for synchronization between multiple + drivers. Sync implementations can take advantage of hardware + synchronization built into devices like GPUs. + endmenu diff --git a/drivers/base/Makefile b/drivers/base/Makefile index d3e2ec1324052..ffa3525db0bd3 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -20,5 +20,7 @@ obj-$(CONFIG_MODULES) += module.o endif obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o +obj-$(CONFIG_SYNC) += sync.o + ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG diff --git a/drivers/base/sync.c b/drivers/base/sync.c new file mode 100644 index 0000000000000..4a8604b7f3a4a --- /dev/null +++ b/drivers/base/sync.c @@ -0,0 +1,525 @@ +/* + * drivers/base/sync.c + * + * Copyright (C) 2012 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 +#include +#include + +#include + +static void sync_fence_signal_pt(struct sync_pt *pt); +static int _sync_pt_has_signaled(struct sync_pt *pt); + +struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, + int size, const char *name) +{ + struct sync_timeline *obj; + + if (size < sizeof(struct sync_timeline)) + return NULL; + + obj = kzalloc(size, GFP_KERNEL); + if (obj == NULL) + return NULL; + + obj->ops = ops; + strlcpy(obj->name, name, sizeof(obj->name)); + + INIT_LIST_HEAD(&obj->child_list_head); + spin_lock_init(&obj->child_list_lock); + + INIT_LIST_HEAD(&obj->active_list_head); + spin_lock_init(&obj->active_list_lock); + + return obj; +} + +void sync_timeline_destroy(struct sync_timeline *obj) +{ + unsigned long flags; + bool needs_freeing; + + spin_lock_irqsave(&obj->child_list_lock, flags); + obj->destroyed = true; + needs_freeing = list_empty(&obj->child_list_head); + spin_unlock_irqrestore(&obj->child_list_lock, flags); + + if (needs_freeing) + kfree(obj); + else + sync_timeline_signal(obj); +} + +static void sync_timeline_add_pt(struct sync_timeline *obj, struct sync_pt *pt) +{ + unsigned long flags; + + pt->parent = obj; + + spin_lock_irqsave(&obj->child_list_lock, flags); + list_add_tail(&pt->child_list, &obj->child_list_head); + spin_unlock_irqrestore(&obj->child_list_lock, flags); +} + +static void sync_timeline_remove_pt(struct sync_pt *pt) +{ + struct sync_timeline *obj = pt->parent; + unsigned long flags; + bool needs_freeing; + + spin_lock_irqsave(&obj->active_list_lock, flags); + if (!list_empty(&pt->active_list)) + list_del_init(&pt->active_list); + spin_unlock_irqrestore(&obj->active_list_lock, flags); + + spin_lock_irqsave(&obj->child_list_lock, flags); + list_del(&pt->child_list); + needs_freeing = obj->destroyed && list_empty(&obj->child_list_head); + spin_unlock_irqrestore(&obj->child_list_lock, flags); + + if (needs_freeing) + kfree(obj); +} + +void sync_timeline_signal(struct sync_timeline *obj) +{ + unsigned long flags; + LIST_HEAD(signaled_pts); + struct list_head *pos, *n; + + spin_lock_irqsave(&obj->active_list_lock, flags); + + list_for_each_safe(pos, n, &obj->active_list_head) { + struct sync_pt *pt = + container_of(pos, struct sync_pt, active_list); + + if (_sync_pt_has_signaled(pt)) + list_move(pos, &signaled_pts); + } + + spin_unlock_irqrestore(&obj->active_list_lock, flags); + + list_for_each_safe(pos, n, &signaled_pts) { + struct sync_pt *pt = + container_of(pos, struct sync_pt, active_list); + + list_del_init(pos); + sync_fence_signal_pt(pt); + } +} + +struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size) +{ + struct sync_pt *pt; + + if (size < sizeof(struct sync_pt)) + return NULL; + + pt = kzalloc(size, GFP_KERNEL); + if (pt == NULL) + return NULL; + + INIT_LIST_HEAD(&pt->active_list); + sync_timeline_add_pt(parent, pt); + + return pt; +} + +void sync_pt_free(struct sync_pt *pt) +{ + if (pt->parent->ops->free_pt) + pt->parent->ops->free_pt(pt); + + sync_timeline_remove_pt(pt); + + kfree(pt); +} + +/* call with pt->parent->active_list_lock held */ +static int _sync_pt_has_signaled(struct sync_pt *pt) +{ + if (!pt->status) + pt->status = pt->parent->ops->has_signaled(pt); + + if (!pt->status && pt->parent->destroyed) + pt->status = -ENOENT; + + return pt->status; +} + +static struct sync_pt *sync_pt_dup(struct sync_pt *pt) +{ + return pt->parent->ops->dup(pt); +} + +/* Adds a sync pt to the active queue. Called when added to a fence */ +static void sync_pt_activate(struct sync_pt *pt) +{ + struct sync_timeline *obj = pt->parent; + unsigned long flags; + int err; + + spin_lock_irqsave(&obj->active_list_lock, flags); + + err = _sync_pt_has_signaled(pt); + if (err != 0) + goto out; + + list_add_tail(&pt->active_list, &obj->active_list_head); + +out: + spin_unlock_irqrestore(&obj->active_list_lock, flags); +} + +static int sync_fence_release(struct inode *inode, struct file *file); +static long sync_fence_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); + + +static const struct file_operations sync_fence_fops = { + .release = sync_fence_release, + .unlocked_ioctl = sync_fence_ioctl, +}; + +static struct sync_fence *sync_fence_alloc(const char *name) +{ + struct sync_fence *fence; + + fence = kzalloc(sizeof(struct sync_fence), GFP_KERNEL); + if (fence == NULL) + return NULL; + + fence->file = anon_inode_getfile("sync_fence", &sync_fence_fops, + fence, 0); + if (fence->file == NULL) + goto err; + + strlcpy(fence->name, name, sizeof(fence->name)); + + INIT_LIST_HEAD(&fence->pt_list_head); + INIT_LIST_HEAD(&fence->waiter_list_head); + spin_lock_init(&fence->waiter_list_lock); + + init_waitqueue_head(&fence->wq); + return fence; + +err: + kfree(fence); + return NULL; +} + +/* TODO: implement a create which takes more that one sync_pt */ +struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt) +{ + struct sync_fence *fence; + + if (pt->fence) + return NULL; + + fence = sync_fence_alloc(name); + if (fence == NULL) + return NULL; + + pt->fence = fence; + list_add(&pt->pt_list, &fence->pt_list_head); + sync_pt_activate(pt); + + return fence; +} + +static int sync_fence_copy_pts(struct sync_fence *dst, struct sync_fence *src) +{ + struct list_head *pos; + + list_for_each(pos, &src->pt_list_head) { + struct sync_pt *orig_pt = + container_of(pos, struct sync_pt, pt_list); + struct sync_pt *new_pt = sync_pt_dup(orig_pt); + + if (new_pt == NULL) + return -ENOMEM; + + new_pt->fence = dst; + list_add(&new_pt->pt_list, &dst->pt_list_head); + sync_pt_activate(new_pt); + } + + return 0; +} + +static void sync_fence_free_pts(struct sync_fence *fence) +{ + struct list_head *pos, *n; + + list_for_each_safe(pos, n, &fence->pt_list_head) { + struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list); + sync_pt_free(pt); + } +} + +struct sync_fence *sync_fence_fdget(int fd) +{ + struct file *file = fget(fd); + + if (file == NULL) + return NULL; + + if (file->f_op != &sync_fence_fops) + goto err; + + return file->private_data; + +err: + fput(file); + return NULL; +} + +void sync_fence_put(struct sync_fence *fence) +{ + fput(fence->file); +} + +void sync_fence_install(struct sync_fence *fence, int fd) +{ + fd_install(fd, fence->file); +} + +static int sync_fence_get_status(struct sync_fence *fence) +{ + struct list_head *pos; + int status = 1; + + list_for_each(pos, &fence->pt_list_head) { + struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list); + int pt_status = pt->status; + + if (pt_status < 0) { + status = pt_status; + break; + } else if (status == 1) { + status = pt_status; + } + } + + return status; +} + +struct sync_fence *sync_fence_merge(const char *name, + struct sync_fence *a, struct sync_fence *b) +{ + struct sync_fence *fence; + int err; + + fence = sync_fence_alloc(name); + if (fence == NULL) + return NULL; + + err = sync_fence_copy_pts(fence, a); + if (err < 0) + goto err; + + err = sync_fence_copy_pts(fence, b); + if (err < 0) + goto err; + + fence->status = sync_fence_get_status(fence); + + return fence; +err: + sync_fence_free_pts(fence); + kfree(fence); + return NULL; +} + +static void sync_fence_signal_pt(struct sync_pt *pt) +{ + LIST_HEAD(signaled_waiters); + struct sync_fence *fence = pt->fence; + struct list_head *pos; + struct list_head *n; + unsigned long flags; + int status; + + status = sync_fence_get_status(fence); + + spin_lock_irqsave(&fence->waiter_list_lock, flags); + /* + * this should protect against two threads racing on the signaled + * false -> true transition + */ + if (status && !fence->status) { + list_for_each_safe(pos, n, &fence->waiter_list_head) + list_move(pos, &signaled_waiters); + + fence->status = status; + } else { + status = 0; + } + spin_unlock_irqrestore(&fence->waiter_list_lock, flags); + + if (status) { + list_for_each_safe(pos, n, &signaled_waiters) { + struct sync_fence_waiter *waiter = + container_of(pos, struct sync_fence_waiter, + waiter_list); + + waiter->callback(fence, waiter->callback_data); + list_del(pos); + kfree(waiter); + } + wake_up(&fence->wq); + } +} + +int sync_fence_wait_async(struct sync_fence *fence, + void (*callback)(struct sync_fence *, void *data), + void *callback_data) +{ + struct sync_fence_waiter *waiter; + unsigned long flags; + int err = 0; + + waiter = kzalloc(sizeof(struct sync_fence_waiter), GFP_KERNEL); + if (waiter == NULL) + return -ENOMEM; + + waiter->callback = callback; + waiter->callback_data = callback_data; + + spin_lock_irqsave(&fence->waiter_list_lock, flags); + + if (fence->status) { + kfree(waiter); + err = fence->status; + goto out; + } + + list_add_tail(&waiter->waiter_list, &fence->waiter_list_head); +out: + spin_unlock_irqrestore(&fence->waiter_list_lock, flags); + + return err; +} + +int sync_fence_wait(struct sync_fence *fence, long timeout) +{ + int err; + + if (timeout) { + timeout = msecs_to_jiffies(timeout); + err = wait_event_interruptible_timeout(fence->wq, + fence->status != 0, + timeout); + } else { + err = wait_event_interruptible(fence->wq, fence->status != 0); + } + + if (err < 0) + return err; + + if (fence->status < 0) + return fence->status; + + if (fence->status == 0) + return -ETIME; + + return 0; +} + +static int sync_fence_release(struct inode *inode, struct file *file) +{ + struct sync_fence *fence = file->private_data; + + sync_fence_free_pts(fence); + kfree(fence); + + return 0; +} + +static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg) +{ + __u32 value; + + if (copy_from_user(&value, (void __user *)arg, sizeof(value))) + return -EFAULT; + + return sync_fence_wait(fence, value); +} + +static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg) +{ + int fd = get_unused_fd(); + int err; + struct sync_fence *fence2, *fence3; + struct sync_merge_data data; + + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) + return -EFAULT; + + fence2 = sync_fence_fdget(data.fd2); + if (fence2 == NULL) { + err = -ENOENT; + goto err_put_fd; + } + + data.name[sizeof(data.name) - 1] = '\0'; + fence3 = sync_fence_merge(data.name, fence, fence2); + if (fence3 == NULL) { + err = -ENOMEM; + goto err_put_fence2; + } + + data.fence = fd; + if (copy_to_user((void __user *)arg, &data, sizeof(data))) { + err = -EFAULT; + goto err_put_fence3; + } + + sync_fence_install(fence3, fd); + sync_fence_put(fence2); + return 0; + +err_put_fence3: + sync_fence_put(fence3); + +err_put_fence2: + sync_fence_put(fence2); + +err_put_fd: + put_unused_fd(fd); + return err; +} + + +static long sync_fence_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct sync_fence *fence = file->private_data; + switch (cmd) { + case SYNC_IOC_WAIT: + return sync_fence_ioctl_wait(fence, arg); + + case SYNC_IOC_MERGE: + return sync_fence_ioctl_merge(fence, arg); + default: + return -ENOTTY; + } +} + diff --git a/include/linux/sync.h b/include/linux/sync.h new file mode 100644 index 0000000000000..388acd1ff4121 --- /dev/null +++ b/include/linux/sync.h @@ -0,0 +1,314 @@ +/* + * include/linux/sync.h + * + * Copyright (C) 2012 Google, Inc. + * + * 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_SYNC_H +#define _LINUX_SYNC_H + +#include +#ifdef __KERNEL__ + +#include +#include +#include + +struct sync_timeline; +struct sync_pt; +struct sync_fence; + +/** + * struct sync_timeline_ops - sync object implementation ops + * @driver_name: name of the implentation + * @dup: duplicate a sync_pt + * @has_signaled: returns: + * 1 if pt has signaled + * 0 if pt has not signaled + * <0 on error + * @compare: returns: + * 1 if b will signal before a + * 0 if a and b will signal at the same time + * -1 if a will signabl before b + * @free_pt: called before sync_pt is freed + * @release_obj: called before sync_timeline is freed + */ +struct sync_timeline_ops { + const char *driver_name; + + /* required */ + struct sync_pt *(*dup)(struct sync_pt *pt); + + /* required */ + int (*has_signaled)(struct sync_pt *pt); + + /* required */ + int (*compare)(struct sync_pt *a, struct sync_pt *b); + + /* optional */ + void (*free_pt)(struct sync_pt *sync_pt); + + /* optional */ + void (*release_obj)(struct sync_timeline *sync_timeline); +}; + +/** + * struct sync_timeline - sync object + * @ops: ops that define the implementaiton of the sync_timeline + * @name: name of the sync_timeline. Useful for debugging + * @destoryed: set when sync_timeline is destroyed + * @child_list_head: list of children sync_pts for this sync_timeline + * @child_list_lock: lock protecting @child_list_head, destroyed, and + * sync_pt.status + * @active_list_head: list of active (unsignaled/errored) sync_pts + */ +struct sync_timeline { + const struct sync_timeline_ops *ops; + char name[32]; + + /* protected by child_list_lock */ + bool destroyed; + + struct list_head child_list_head; + spinlock_t child_list_lock; + + struct list_head active_list_head; + spinlock_t active_list_lock; +}; + +/** + * struct sync_pt - sync point + * @parent: sync_timeline to which this sync_pt belongs + * @child_list: membership in sync_timeline.child_list_head + * @active_list: membership in sync_timeline.active_list_head + * @fence: sync_fence to which the sync_pt belongs + * @pt_list: membership in sync_fence.pt_list_head + * @status: 1: signaled, 0:active, <0: error + */ +struct sync_pt { + struct sync_timeline *parent; + struct list_head child_list; + + struct list_head active_list; + + struct sync_fence *fence; + struct list_head pt_list; + + /* protected by parent->active_list_lock */ + int status; +}; + +/** + * struct sync_fence - sync fence + * @file: file representing this fence + * @name: name of sync_fence. Useful for debugging + * @pt_list_head: list of sync_pts in ths fence. immutable once fence + * is created + * @waiter_list_head: list of asynchronous waiters on this fence + * @waiter_list_lock: lock protecting @waiter_list_head and @status + * @status: 1: signaled, 0:active, <0: error + * + * @wq: wait queue for fence signaling + */ +struct sync_fence { + struct file *file; + char name[32]; + + /* this list is immutable once the fence is created */ + struct list_head pt_list_head; + + struct list_head waiter_list_head; + spinlock_t waiter_list_lock; /* also protects status */ + int status; + + wait_queue_head_t wq; +}; + +/** + * struct sync_fence_waiter - metadata for asynchronous waiter on a fence + * @waiter_list: membership in sync_fence.waiter_list_head + * @callback: function pointer to call when fence signals + * @callback_data: pointer to pass to @callback + */ +struct sync_fence_waiter { + struct list_head waiter_list; + + void (*callback)(struct sync_fence *fence, void *data); + void *callback_data; +}; + +/* + * API for sync_timeline implementers + */ + +/** + * sync_timeline_create() - creates a sync object + * @ops: specifies the implemention ops for the object + * @size: size to allocate for this obj + * @name: sync_timeline name + * + * Creates a new sync_timeline which will use the implemetation specified by + * @ops. @size bytes will be allocated allowing for implemntation specific + * data to be kept after the generic sync_timeline stuct. + */ +struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, + int size, const char *name); + +/** + * sync_timeline_destory() - destorys a sync object + * @obj: sync_timeline to destroy + * + * A sync implemntation should call this when the @obj is going away + * (i.e. module unload.) @obj won't actually be freed until all its childern + * sync_pts are freed. + */ +void sync_timeline_destroy(struct sync_timeline *obj); + +/** + * sync_timeline_signal() - signal a status change on a sync_timeline + * @obj: sync_timeline to signal + * + * A sync implemntation should call this any time one of it's sync_pts + * has signaled or has an error condition. + */ +void sync_timeline_signal(struct sync_timeline *obj); + +/** + * sync_pt_create() - creates a sync pt + * @parent: sync_pt's parent sync_timeline + * @size: size to allocate for this pt + * + * Creates a new sync_pt as a chiled of @parent. @size bytes will be + * allocated allowing for implemntation specific data to be kept after + * the generic sync_timeline struct. + */ +struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size); + +/** + * sync_pt_free() - frees a sync pt + * @pt: sync_pt to free + * + * This should only be called on sync_pts which have been created but + * not added to a fence. + */ +void sync_pt_free(struct sync_pt *pt); + +/** + * sync_fence_create() - creates a sync fence + * @name: name of fence to create + * @pt: sync_pt to add to the fence + * + * Creates a fence containg @pt. Once this is called, the fence takes + * ownership of @pt. + */ +struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt); + +/* + * API for sync_fence consumers + */ + +/** + * sync_fence_merge() - merge two fences + * @name: name of new fence + * @a: fence a + * @b: fence b + * + * Creates a new fence which contains copies of all the sync_pts in both + * @a and @b. @a and @b remain valid, independent fences. + */ +struct sync_fence *sync_fence_merge(const char *name, + struct sync_fence *a, struct sync_fence *b); + +/** + * sync_fence_fdget() - get a fence from an fd + * @fd: fd referencing a fence + * + * Ensures @fd references a valid fence, increments the refcount of the backing + * file, and returns the fence. + */ +struct sync_fence *sync_fence_fdget(int fd); + +/** + * sync_fence_put() - puts a refernnce of a sync fence + * @fence: fence to put + * + * Puts a reference on @fence. If this is the last reference, the fence and + * all it's sync_pts will be freed + */ +void sync_fence_put(struct sync_fence *fence); + +/** + * sync_fence_install() - installs a fence into a file descriptor + * @fence: fence to instal + * @fd: file descriptor in which to install the fence + * + * Installs @fence into @fd. @fd's should be acquired through get_unused_fd(). + */ +void sync_fence_install(struct sync_fence *fence, int fd); + +/** + * sync_fence_wait_async() - registers and async wait on the fence + * @fence: fence to wait on + * @callback: callback + * @callback_data data to pass to the callback + * + * Returns 1 if @fence has already signaled. + * + * Registers a callback to be called when @fence signals or has an error + */ +int sync_fence_wait_async(struct sync_fence *fence, + void (*callback)(struct sync_fence *, void *data), + void *callback_data); + +/** + * sync_fence_wait() - wait on fence + * @fence: fence to wait on + * @tiemout: timeout in ms + * + * Wait for @fence to be signaled or have an error. Waits indefintly + * if @timeout = 0 + */ +int sync_fence_wait(struct sync_fence *fence, long timeout); + +/* useful for sync driver's debug print handlers */ +const char *sync_status_str(int status); + +#endif /* __KERNEL__ */ + +/** + * struct sync_merge_data - data passed to merge ioctl + * @fd2: file descriptor of second fence + * @name: name of new fence + * @fence: returns the fd of the new fence to userspace + */ +struct sync_merge_data { + __s32 fd2; /* fd of second fence */ + char name[32]; /* name of new fence */ + __s32 fence; /* fd on newly created fence */ +}; + +#define SYNC_IOC_MAGIC '>' + +/** + * DOC: SYNC_IOC_WAIT - wait for a fence to signal + * + * pass timeout in milliseconds. + */ +#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __u32) + +/** + * DOC: SYNC_IOC_MERGE - merge two fences + * + * Takes a struct sync_merge_data. Creates a new fence containing copies of + * the sync_pts in both the calling fd and sync_merge_data.fd2. Returns the + * new fence's fd in sync_merge_data.fence + */ +#define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 1, struct sync_merge_data) + +#endif /* _LINUX_SYNC_H */ From 2a4de0f74b14f04eba07776c72b2a7fc632c9b2f Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Wed, 18 Apr 2012 13:43:22 -0700 Subject: [PATCH 2471/2556] sw_sync: add cpu based sync driver Change-Id: I1042851f5e30f9fdc2f35bdad84123bcf108560f Signed-off-by: Erik Gilling Conflicts: drivers/base/Kconfig --- drivers/base/Kconfig | 17 +++ drivers/base/Makefile | 1 + drivers/base/sw_sync.c | 223 ++++++++++++++++++++++++++++++++++++++++ include/linux/sw_sync.h | 58 +++++++++++ 4 files changed, 299 insertions(+) create mode 100644 drivers/base/sw_sync.c create mode 100644 include/linux/sw_sync.h diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 6494fd4d11405..4e20415918cda 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -191,4 +191,21 @@ config SYNC drivers. Sync implementations can take advantage of hardware synchronization built into devices like GPUs. +config SW_SYNC + bool "Software synchronization objects" + default n + depends on SYNC + help + A sync object driver that uses a 32bit counter to coordinate + syncrhronization. Useful when there is no hardware primitive backing + the synchronization. + +config SW_SYNC_USER + bool "Userspace API for SW_SYNC" + default n + depends on SW_SYNC + help + Provides a user space API to the sw sync object. + *WARNING* improper use of this can result in deadlocking kernel + drivers from userspace. endmenu diff --git a/drivers/base/Makefile b/drivers/base/Makefile index ffa3525db0bd3..b930083bb1656 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -21,6 +21,7 @@ endif obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o obj-$(CONFIG_SYNC) += sync.o +obj-$(CONFIG_SW_SYNC) += sw_sync.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG diff --git a/drivers/base/sw_sync.c b/drivers/base/sw_sync.c new file mode 100644 index 0000000000000..e94ecc7796284 --- /dev/null +++ b/drivers/base/sw_sync.c @@ -0,0 +1,223 @@ +/* + * drivers/base/sw_sync.c + * + * Copyright (C) 2012 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 +#include +#include +#include + +static int sw_sync_cmp(u32 a, u32 b) +{ + if (a == b) + return 0; + + return ((s32)a - (s32)b) < 0 ? -1 : 1; +} + +struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value) +{ + struct sw_sync_pt *pt; + + pt = (struct sw_sync_pt *) + sync_pt_create(&obj->obj, sizeof(struct sw_sync_pt)); + + pt->value = value; + + return (struct sync_pt *)pt; +} + +static struct sync_pt *sw_sync_pt_dup(struct sync_pt *sync_pt) +{ + struct sw_sync_pt *pt = (struct sw_sync_pt *) sync_pt; + struct sw_sync_timeline *obj = + (struct sw_sync_timeline *)sync_pt->parent; + + return (struct sync_pt *) sw_sync_pt_create(obj, pt->value); +} + +static int sw_sync_pt_has_signaled(struct sync_pt *sync_pt) +{ + struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; + struct sw_sync_timeline *obj = + (struct sw_sync_timeline *)sync_pt->parent; + + return sw_sync_cmp(obj->value, pt->value) >= 0; +} + +static int sw_sync_pt_compare(struct sync_pt *a, struct sync_pt *b) +{ + struct sw_sync_pt *pt_a = (struct sw_sync_pt *)a; + struct sw_sync_pt *pt_b = (struct sw_sync_pt *)b; + + return sw_sync_cmp(pt_a->value, pt_b->value); +} + +struct sync_timeline_ops sw_sync_timeline_ops = { + .driver_name = "sw_sync", + .dup = sw_sync_pt_dup, + .has_signaled = sw_sync_pt_has_signaled, + .compare = sw_sync_pt_compare, +}; + + +struct sw_sync_timeline *sw_sync_timeline_create(const char *name) +{ + struct sw_sync_timeline *obj = (struct sw_sync_timeline *) + sync_timeline_create(&sw_sync_timeline_ops, + sizeof(struct sw_sync_timeline), + name); + + return obj; +} + +void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc) +{ + obj->value += inc; + + sync_timeline_signal(&obj->obj); +} + + +#ifdef CONFIG_SW_SYNC_USER +/* *WARNING* + * + * improper use of this can result in deadlocking kernel drivers from userspace. + */ + +/* opening sw_sync create a new sync obj */ +int sw_sync_open(struct inode *inode, struct file *file) +{ + struct sw_sync_timeline *obj; + char task_comm[TASK_COMM_LEN]; + + get_task_comm(task_comm, current); + + obj = sw_sync_timeline_create(task_comm); + if (obj == NULL) + return -ENOMEM; + + file->private_data = obj; + + return 0; +} + +int sw_sync_release(struct inode *inode, struct file *file) +{ + struct sw_sync_timeline *obj = file->private_data; + sync_timeline_destroy(&obj->obj); + return 0; +} + +long sw_sync_ioctl_create_fence(struct sw_sync_timeline *obj, unsigned long arg) +{ + int fd = get_unused_fd(); + int err; + struct sync_pt *pt; + struct sync_fence *fence; + struct sw_sync_create_fence_data data; + + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) + return -EFAULT; + + pt = sw_sync_pt_create(obj, data.value); + if (pt == NULL) { + err = -ENOMEM; + goto err; + } + + data.name[sizeof(data.name) - 1] = '\0'; + fence = sync_fence_create(data.name, pt); + if (fence == NULL) { + sync_pt_free(pt); + err = -ENOMEM; + goto err; + } + + data.fence = fd; + if (copy_to_user((void __user *)arg, &data, sizeof(data))) { + sync_fence_put(fence); + err = -EFAULT; + goto err; + } + + sync_fence_install(fence, fd); + + return 0; + +err: + put_unused_fd(fd); + return err; +} + +long sw_sync_ioctl_inc(struct sw_sync_timeline *obj, unsigned long arg) +{ + u32 value; + + if (copy_from_user(&value, (void __user *)arg, sizeof(value))) + return -EFAULT; + + sw_sync_timeline_inc(obj, value); + + return 0; +} + +long sw_sync_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct sw_sync_timeline *obj = file->private_data; + + switch (cmd) { + case SW_SYNC_IOC_CREATE_FENCE: + return sw_sync_ioctl_create_fence(obj, arg); + + case SW_SYNC_IOC_INC: + return sw_sync_ioctl_inc(obj, arg); + + default: + return -ENOTTY; + } +} + +static const struct file_operations sw_sync_fops = { + .owner = THIS_MODULE, + .open = sw_sync_open, + .release = sw_sync_release, + .unlocked_ioctl = sw_sync_ioctl, +}; + +static struct miscdevice sw_sync_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sw_sync", + .fops = &sw_sync_fops, +}; + +int __init sw_sync_device_init(void) +{ + return misc_register(&sw_sync_dev); +} + +void __exit sw_sync_device_remove(void) +{ + misc_deregister(&sw_sync_dev); +} + +module_init(sw_sync_device_init); +module_exit(sw_sync_device_remove); + +#endif /* CONFIG_SW_SYNC_USER */ diff --git a/include/linux/sw_sync.h b/include/linux/sw_sync.h new file mode 100644 index 0000000000000..bd6f2089e77f3 --- /dev/null +++ b/include/linux/sw_sync.h @@ -0,0 +1,58 @@ +/* + * include/linux/sw_sync.h + * + * Copyright (C) 2012 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_SW_SYNC_H +#define _LINUX_SW_SYNC_H + +#include + +#ifdef __KERNEL__ + +#include + +struct sw_sync_timeline { + struct sync_timeline obj; + + u32 value; +}; + +struct sw_sync_pt { + struct sync_pt pt; + + u32 value; +}; + +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); + +#endif /* __KERNEL __ */ + +struct sw_sync_create_fence_data { + __u32 value; + char name[32]; + __s32 fence; /* fd of new fence */ +}; + +#define SW_SYNC_IOC_MAGIC 'W' + +#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0,\ + struct sw_sync_create_fence_data) +#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32) + + +#endif /* _LINUX_SW_SYNC_H */ From 9e61e37e6f2fa05babd57cf4958bca5cc760451f Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Thu, 15 Mar 2012 14:59:33 -0700 Subject: [PATCH 2472/2556] sync: add timestamps to sync_pts Change-Id: I2ad855072b86873880769a09a3176e85aa1199d7 Signed-off-by: Erik Gilling --- drivers/base/sync.c | 5 +++++ include/linux/sync.h | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/drivers/base/sync.c b/drivers/base/sync.c index 4a8604b7f3a4a..ea0b66424e2cd 100644 --- a/drivers/base/sync.c +++ b/drivers/base/sync.c @@ -155,12 +155,17 @@ void sync_pt_free(struct sync_pt *pt) /* call with pt->parent->active_list_lock held */ static int _sync_pt_has_signaled(struct sync_pt *pt) { + int old_status = pt->status; + if (!pt->status) pt->status = pt->parent->ops->has_signaled(pt); if (!pt->status && pt->parent->destroyed) pt->status = -ENOENT; + if (pt->status != old_status) + pt->timestamp = ktime_get(); + return pt->status; } diff --git a/include/linux/sync.h b/include/linux/sync.h index 388acd1ff4121..a8e289d50ae01 100644 --- a/include/linux/sync.h +++ b/include/linux/sync.h @@ -16,6 +16,7 @@ #include #ifdef __KERNEL__ +#include #include #include #include @@ -90,6 +91,8 @@ struct sync_timeline { * @fence: sync_fence to which the sync_pt belongs * @pt_list: membership in sync_fence.pt_list_head * @status: 1: signaled, 0:active, <0: error + * @timestamp: time which sync_pt status transitioned from active to + * singaled or error. */ struct sync_pt { struct sync_timeline *parent; @@ -102,6 +105,8 @@ struct sync_pt { /* protected by parent->active_list_lock */ int status; + + ktime_t timestamp; }; /** From 7a962bfafae9061f4a5bc30e1c074fb01488d2c8 Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Wed, 14 Mar 2012 19:49:15 -0700 Subject: [PATCH 2473/2556] sync: add debugfs support Change-Id: I8a7ea63e454fbeee1ecf17e6c3caff7c43b24734 Signed-off-by: Erik Gilling --- drivers/base/sync.c | 176 ++++++++++++++++++++++++++++++++++++++++++- include/linux/sync.h | 20 ++++- 2 files changed, 191 insertions(+), 5 deletions(-) diff --git a/drivers/base/sync.c b/drivers/base/sync.c index ea0b66424e2cd..49fcfc488c959 100644 --- a/drivers/base/sync.c +++ b/drivers/base/sync.c @@ -14,10 +14,12 @@ * */ +#include #include #include #include #include +#include #include #include #include @@ -27,10 +29,17 @@ static void sync_fence_signal_pt(struct sync_pt *pt); static int _sync_pt_has_signaled(struct sync_pt *pt); +static LIST_HEAD(sync_timeline_list_head); +static DEFINE_SPINLOCK(sync_timeline_list_lock); + +static LIST_HEAD(sync_fence_list_head); +static DEFINE_SPINLOCK(sync_fence_list_lock); + struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, int size, const char *name) { struct sync_timeline *obj; + unsigned long flags; if (size < sizeof(struct sync_timeline)) return NULL; @@ -48,9 +57,27 @@ struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, INIT_LIST_HEAD(&obj->active_list_head); spin_lock_init(&obj->active_list_lock); + spin_lock_irqsave(&sync_timeline_list_lock, flags); + list_add_tail(&obj->sync_timeline_list, &sync_timeline_list_head); + spin_unlock_irqrestore(&sync_timeline_list_lock, flags); + return obj; } +static void sync_timeline_free(struct sync_timeline *obj) +{ + 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); + + kfree(obj); +} + void sync_timeline_destroy(struct sync_timeline *obj) { unsigned long flags; @@ -62,7 +89,7 @@ void sync_timeline_destroy(struct sync_timeline *obj) spin_unlock_irqrestore(&obj->child_list_lock, flags); if (needs_freeing) - kfree(obj); + sync_timeline_free(obj); else sync_timeline_signal(obj); } @@ -95,7 +122,7 @@ static void sync_timeline_remove_pt(struct sync_pt *pt) spin_unlock_irqrestore(&obj->child_list_lock, flags); if (needs_freeing) - kfree(obj); + sync_timeline_free(obj); } void sync_timeline_signal(struct sync_timeline *obj) @@ -206,6 +233,7 @@ static const struct file_operations sync_fence_fops = { static struct sync_fence *sync_fence_alloc(const char *name) { struct sync_fence *fence; + unsigned long flags; fence = kzalloc(sizeof(struct sync_fence), GFP_KERNEL); if (fence == NULL) @@ -223,6 +251,11 @@ static struct sync_fence *sync_fence_alloc(const char *name) spin_lock_init(&fence->waiter_list_lock); init_waitqueue_head(&fence->wq); + + spin_lock_irqsave(&sync_fence_list_lock, flags); + list_add_tail(&fence->sync_fence_list, &sync_fence_list_head); + spin_unlock_irqrestore(&sync_fence_list_lock, flags); + return fence; err: @@ -451,8 +484,14 @@ int sync_fence_wait(struct sync_fence *fence, long timeout) static int sync_fence_release(struct inode *inode, struct file *file) { struct sync_fence *fence = file->private_data; + unsigned long flags; sync_fence_free_pts(fence); + + spin_lock_irqsave(&sync_fence_list_lock, flags); + list_del(&fence->sync_fence_list); + spin_unlock_irqrestore(&sync_fence_list_lock, flags); + kfree(fence); return 0; @@ -523,8 +562,141 @@ static long sync_fence_ioctl(struct file *file, unsigned int cmd, case SYNC_IOC_MERGE: return sync_fence_ioctl_merge(fence, arg); + default: return -ENOTTY; } } +#ifdef CONFIG_DEBUG_FS +static const char *sync_status_str(int status) +{ + if (status > 0) + return "signaled"; + else if (status == 0) + return "active"; + else + return "error"; +} + +static void sync_print_pt(struct seq_file *s, struct sync_pt *pt, bool fence) +{ + int status = pt->status; + seq_printf(s, " %s%spt %s", + fence ? pt->parent->name : "", + fence ? "_" : "", + sync_status_str(status)); + if (pt->status) { + struct timeval tv = ktime_to_timeval(pt->timestamp); + seq_printf(s, "@%ld.%06ld", tv.tv_sec, tv.tv_usec); + } + + if (pt->parent->ops->print_pt) { + seq_printf(s, ": "); + pt->parent->ops->print_pt(s, pt); + } + + seq_printf(s, "\n"); +} + +static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj) +{ + struct list_head *pos; + unsigned long flags; + + seq_printf(s, "%s %s", obj->name, obj->ops->driver_name); + + if (obj->ops->print_obj) { + seq_printf(s, ": "); + obj->ops->print_obj(s, obj); + } + + seq_printf(s, "\n"); + + spin_lock_irqsave(&obj->child_list_lock, flags); + list_for_each(pos, &obj->child_list_head) { + struct sync_pt *pt = + container_of(pos, struct sync_pt, child_list); + sync_print_pt(s, pt, false); + } + spin_unlock_irqrestore(&obj->child_list_lock, flags); +} + +static void sync_print_fence(struct seq_file *s, struct sync_fence *fence) +{ + struct list_head *pos; + unsigned long flags; + + seq_printf(s, "%s: %s\n", fence->name, sync_status_str(fence->status)); + + list_for_each(pos, &fence->pt_list_head) { + struct sync_pt *pt = + container_of(pos, struct sync_pt, pt_list); + sync_print_pt(s, pt, true); + } + + spin_lock_irqsave(&fence->waiter_list_lock, flags); + list_for_each(pos, &fence->waiter_list_head) { + struct sync_fence_waiter *waiter = + container_of(pos, struct sync_fence_waiter, + waiter_list); + + seq_printf(s, "waiter %pF %p\n", waiter->callback, + waiter->callback_data); + } + spin_unlock_irqrestore(&fence->waiter_list_lock, flags); +} + +static int sync_debugfs_show(struct seq_file *s, void *unused) +{ + unsigned long flags; + struct list_head *pos; + + seq_printf(s, "objs:\n--------------\n"); + + spin_lock_irqsave(&sync_timeline_list_lock, flags); + list_for_each(pos, &sync_timeline_list_head) { + struct sync_timeline *obj = + container_of(pos, struct sync_timeline, + sync_timeline_list); + + sync_print_obj(s, obj); + seq_printf(s, "\n"); + } + spin_unlock_irqrestore(&sync_timeline_list_lock, flags); + + seq_printf(s, "fences:\n--------------\n"); + + spin_lock_irqsave(&sync_fence_list_lock, flags); + list_for_each(pos, &sync_fence_list_head) { + struct sync_fence *fence = + container_of(pos, struct sync_fence, sync_fence_list); + + sync_print_fence(s, fence); + seq_printf(s, "\n"); + } + spin_unlock_irqrestore(&sync_fence_list_lock, flags); + return 0; +} + +static int sync_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, sync_debugfs_show, inode->i_private); +} + +static const struct file_operations sync_debugfs_fops = { + .open = sync_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static __init int sync_debugfs_init(void) +{ + debugfs_create_file("sync", S_IRUGO, NULL, NULL, &sync_debugfs_fops); + return 0; +} + +late_initcall(sync_debugfs_init); + +#endif diff --git a/include/linux/sync.h b/include/linux/sync.h index a8e289d50ae01..d64271bd7a3c0 100644 --- a/include/linux/sync.h +++ b/include/linux/sync.h @@ -39,6 +39,10 @@ struct sync_fence; * -1 if a will signabl before b * @free_pt: called before sync_pt is freed * @release_obj: called before sync_timeline is freed + * @print_obj: print aditional debug information about sync_timeline. + * should not print a newline + * @print_pt: print aditional debug information about sync_pt. + * should not print a newline */ struct sync_timeline_ops { const char *driver_name; @@ -57,6 +61,13 @@ struct sync_timeline_ops { /* optional */ void (*release_obj)(struct sync_timeline *sync_timeline); + + /* optional */ + void (*print_obj)(struct seq_file *s, + struct sync_timeline *sync_timeline); + + /* optional */ + void (*print_pt)(struct seq_file *s, struct sync_pt *sync_pt); }; /** @@ -68,6 +79,7 @@ struct sync_timeline_ops { * @child_list_lock: lock protecting @child_list_head, destroyed, and * sync_pt.status * @active_list_head: list of active (unsignaled/errored) sync_pts + * @sync_timeline_list: membership in global sync_timeline_list */ struct sync_timeline { const struct sync_timeline_ops *ops; @@ -81,6 +93,8 @@ struct sync_timeline { struct list_head active_list_head; spinlock_t active_list_lock; + + struct list_head sync_timeline_list; }; /** @@ -120,6 +134,7 @@ struct sync_pt { * @status: 1: signaled, 0:active, <0: error * * @wq: wait queue for fence signaling + * @sync_fence_list: membership in global fence list */ struct sync_fence { struct file *file; @@ -133,6 +148,8 @@ struct sync_fence { int status; wait_queue_head_t wq; + + struct list_head sync_fence_list; }; /** @@ -281,9 +298,6 @@ int sync_fence_wait_async(struct sync_fence *fence, */ int sync_fence_wait(struct sync_fence *fence, long timeout); -/* useful for sync driver's debug print handlers */ -const char *sync_status_str(int status); - #endif /* __KERNEL__ */ /** From d82c5cb4b1ef7f66b8146a587fb9f9fb8b3ddd3e Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Thu, 15 Mar 2012 14:23:23 -0700 Subject: [PATCH 2474/2556] sw_sync: add debug support Change-Id: Ibcc5fa8cb36e283cdf0e3308064876722e2675fc Signed-off-by: Erik Gilling --- drivers/base/sw_sync.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/base/sw_sync.c b/drivers/base/sw_sync.c index e94ecc7796284..41beb9eeeb38b 100644 --- a/drivers/base/sw_sync.c +++ b/drivers/base/sw_sync.c @@ -69,11 +69,30 @@ static int sw_sync_pt_compare(struct sync_pt *a, struct sync_pt *b) return sw_sync_cmp(pt_a->value, pt_b->value); } +static void sw_sync_print_obj(struct seq_file *s, + struct sync_timeline *sync_timeline) +{ + struct sw_sync_timeline *obj = (struct sw_sync_timeline *)sync_timeline; + + seq_printf(s, "%d", obj->value); +} + +static void sw_sync_print_pt(struct seq_file *s, struct sync_pt *sync_pt) +{ + struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; + struct sw_sync_timeline *obj = + (struct sw_sync_timeline *)sync_pt->parent; + + seq_printf(s, "%d / %d", pt->value, obj->value); +} + struct sync_timeline_ops sw_sync_timeline_ops = { .driver_name = "sw_sync", .dup = sw_sync_pt_dup, .has_signaled = sw_sync_pt_has_signaled, .compare = sw_sync_pt_compare, + .print_obj = sw_sync_print_obj, + .print_pt = sw_sync_print_pt, }; From 0bc26f025b910a1f2d606e725cdb559aedefbeda Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Thu, 15 Mar 2012 17:45:50 -0700 Subject: [PATCH 2475/2556] sync: add ioctl to get fence data Change-Id: I71410aef7e03a52562f7cb15b993ac8441b1fa12 Signed-off-by: Erik Gilling --- drivers/base/sync.c | 82 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/sync.h | 57 ++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) diff --git a/drivers/base/sync.c b/drivers/base/sync.c index 49fcfc488c959..2abb3d3ff1e06 100644 --- a/drivers/base/sync.c +++ b/drivers/base/sync.c @@ -551,6 +551,85 @@ static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg) return err; } +int sync_fill_pt_info(struct sync_pt *pt, void *data, int size) +{ + struct sync_pt_info *info = data; + int ret; + + if (size < sizeof(struct sync_pt_info)) + return -ENOMEM; + + info->len = sizeof(struct sync_pt_info); + + if (pt->parent->ops->fill_driver_data) { + ret = pt->parent->ops->fill_driver_data(pt, info->driver_data, + size - sizeof(*info)); + if (ret < 0) + return ret; + + info->len += ret; + } + + strlcpy(info->obj_name, pt->parent->name, sizeof(info->obj_name)); + strlcpy(info->driver_name, pt->parent->ops->driver_name, + sizeof(info->driver_name)); + info->status = pt->status; + info->timestamp_ns = ktime_to_ns(pt->timestamp); + + return info->len; +} + + +static long sync_fence_ioctl_fence_info(struct sync_fence *fence, + unsigned long arg) +{ + struct sync_fence_info_data *data; + struct list_head *pos; + __u32 size; + __u32 len = 0; + int ret; + + if (copy_from_user(&size, (void __user *)arg, sizeof(size))) + return -EFAULT; + + if (size < sizeof(struct sync_fence_info_data)) + return -EINVAL; + + if (size > 4096) + size = 4096; + + data = kzalloc(size, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + strlcpy(data->name, fence->name, sizeof(data->name)); + data->status = fence->status; + len = sizeof(struct sync_fence_info_data); + + list_for_each(pos, &fence->pt_list_head) { + struct sync_pt *pt = + container_of(pos, struct sync_pt, pt_list); + + ret = sync_fill_pt_info(pt, (u8 *)data + len, size - len); + + if (ret < 0) + goto out; + + len += ret; + } + + data->len = len; + + if (copy_to_user((void __user *)arg, data, len)) + ret = -EFAULT; + else + ret = 0; + +out: + kfree(data); + + return ret; +} static long sync_fence_ioctl(struct file *file, unsigned int cmd, unsigned long arg) @@ -563,6 +642,9 @@ static long sync_fence_ioctl(struct file *file, unsigned int cmd, case SYNC_IOC_MERGE: return sync_fence_ioctl_merge(fence, arg); + case SYNC_IOC_FENCE_INFO: + return sync_fence_ioctl_fence_info(fence, arg); + default: return -ENOTTY; } diff --git a/include/linux/sync.h b/include/linux/sync.h index d64271bd7a3c0..4f1993871467c 100644 --- a/include/linux/sync.h +++ b/include/linux/sync.h @@ -43,6 +43,10 @@ struct sync_fence; * should not print a newline * @print_pt: print aditional debug information about sync_pt. * should not print a newline + * @fill_driver_data: write implmentation specific driver data to data. + * should return an error if there is not enough room + * as specified by size. This information is returned + * to userspace by SYNC_IOC_FENCE_INFO. */ struct sync_timeline_ops { const char *driver_name; @@ -68,6 +72,9 @@ struct sync_timeline_ops { /* optional */ void (*print_pt)(struct seq_file *s, struct sync_pt *sync_pt); + + /* optional */ + int (*fill_driver_data)(struct sync_pt *syncpt, void *data, int size); }; /** @@ -312,6 +319,42 @@ struct sync_merge_data { __s32 fence; /* fd on newly created fence */ }; +/** + * struct sync_pt_info - detailed sync_pt information + * @len: length of sync_pt_info including any driver_data + * @obj_name: name of parent sync_timeline + * @driver_name: name of driver implmenting the parent + * @status: status of the sync_pt 0:active 1:signaled <0:error + * @timestamp_ns: timestamp of status change in nanoseconds + * @driver_data: any driver dependant data + */ +struct sync_pt_info { + __u32 len; + char obj_name[32]; + char driver_name[32]; + __s32 status; + __u64 timestamp_ns; + + __u8 driver_data[0]; +}; + +/** + * struct sync_fence_info_data - data returned from fence info ioctl + * @len: ioctl caller writes the size of the buffer its passing in. + * ioctl returns length of sync_fence_data reutnred to userspace + * including pt_info. + * @name: name of fence + * @status: status of fence. 1: signaled 0:active <0:error + * @pt_info: a sync_pt_info struct for every sync_pt in the fence + */ +struct sync_fence_info_data { + __u32 len; + char name[32]; + __s32 status; + + __u8 pt_info[0]; +}; + #define SYNC_IOC_MAGIC '>' /** @@ -330,4 +373,18 @@ struct sync_merge_data { */ #define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 1, struct sync_merge_data) +/** + * DOC: SYNC_IOC_FENCE_INFO - get detailed information on a fence + * + * Takes a struct sync_fence_info_data with extra space allocated for pt_info. + * Caller should write the size of the buffer into len. On return, len is + * updated to reflect the total size of the sync_fence_info_data including + * pt_info. + * + * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence. + * To itterate over the sync_pt_infos, use the sync_pt_info.len field. + */ +#define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2,\ + struct sync_fence_info_data) + #endif /* _LINUX_SYNC_H */ From 691030df2f85560f34d71b51a282b8a35e0588f6 Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Thu, 15 Mar 2012 17:46:07 -0700 Subject: [PATCH 2476/2556] sw_sync: add fill_driver_data support Change-Id: Ib3812d282db56362d82f5ccc9a12b7d2100ab93a Signed-off-by: Erik Gilling --- drivers/base/sw_sync.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/base/sw_sync.c b/drivers/base/sw_sync.c index 41beb9eeeb38b..21ddf4ffd5894 100644 --- a/drivers/base/sw_sync.c +++ b/drivers/base/sw_sync.c @@ -86,6 +86,19 @@ static void sw_sync_print_pt(struct seq_file *s, struct sync_pt *sync_pt) seq_printf(s, "%d / %d", pt->value, obj->value); } +static int sw_sync_fill_driver_data(struct sync_pt *sync_pt, + void *data, int size) +{ + struct sw_sync_pt *pt = (struct sw_sync_pt *)sync_pt; + + if (size < sizeof(pt->value)) + return -ENOMEM; + + memcpy(data, &pt->value, sizeof(pt->value)); + + return sizeof(pt->value); +} + struct sync_timeline_ops sw_sync_timeline_ops = { .driver_name = "sw_sync", .dup = sw_sync_pt_dup, @@ -93,6 +106,7 @@ struct sync_timeline_ops sw_sync_timeline_ops = { .compare = sw_sync_pt_compare, .print_obj = sw_sync_print_obj, .print_pt = sw_sync_print_pt, + .fill_driver_data = sw_sync_fill_driver_data, }; From aaa34831256bc6a1ceec60ff6b19a9c27cce268e Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Mon, 19 Mar 2012 17:28:32 -0700 Subject: [PATCH 2477/2556] sync: add poll support Change-Id: I294e481bba92658e6dd26f157ddbf0e9ff4ce8a5 Signed-off-by: Erik Gilling --- drivers/base/sync.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/base/sync.c b/drivers/base/sync.c index 2abb3d3ff1e06..d6913f8e194e8 100644 --- a/drivers/base/sync.c +++ b/drivers/base/sync.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -221,12 +222,14 @@ static void sync_pt_activate(struct sync_pt *pt) } static int sync_fence_release(struct inode *inode, struct file *file); +static unsigned int sync_fence_poll(struct file *file, poll_table *wait); static long sync_fence_ioctl(struct file *file, unsigned int cmd, unsigned long arg); static const struct file_operations sync_fence_fops = { .release = sync_fence_release, + .poll = sync_fence_poll, .unlocked_ioctl = sync_fence_ioctl, }; @@ -497,6 +500,20 @@ static int sync_fence_release(struct inode *inode, struct file *file) return 0; } +static unsigned int sync_fence_poll(struct file *file, poll_table *wait) +{ + struct sync_fence *fence = file->private_data; + + poll_wait(file, &fence->wq, wait); + + if (fence->status == 1) + return POLLIN; + else if (fence->status < 0) + return POLLERR; + else + return 0; +} + static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg) { __u32 value; From 4b3cafc54af925efa6491f9c8b19478954d3316e Mon Sep 17 00:00:00 2001 From: Shruthi Krishna Date: Wed, 21 Nov 2012 00:14:22 -0800 Subject: [PATCH 2478/2556] sync: driver commits - Revert "base: sync: signal a sync pt when not adding to the active_list" - sync: Fix error paths - sync: add tracepoint support - sync: refactor sync debug printing - sync: use proper barriers when waiting indefinitely - sync: update new fence status with sync_fence_signal_pt - sync: dump sync state of fence errors - sync: protect unlocked access to fence status - base: sync: signal a sync pt when not adding to the active_list - sync: improve timeout dumps - sync: use correct signed type when handling SYNC_IOC_WAIT - sync: dump sync state to console on timeout - sync: clean up compiler warnings - sync: fix erase-o in sync_fence_wait - sync: change wait timeout to mirror poll semantics - sync: add reference counting to timelines - sync: add internal refcounting to fences - sync: optimize fence merges - sync: reorder sync_fence_release - sync: export sync API symbols - sync: allow async waits to be canceled Change-Id: Id65ecdd395782d487773bdf7baa3ed681a72b729 Signed-off-by: Shruthi Krishna --- drivers/base/sync.c | 318 +++++++++++++++++++++++++++++++++++-------- include/linux/sync.h | 71 +++++++--- 2 files changed, 317 insertions(+), 72 deletions(-) mode change 100644 => 100755 drivers/base/sync.c mode change 100644 => 100755 include/linux/sync.h diff --git a/drivers/base/sync.c b/drivers/base/sync.c old mode 100644 new mode 100755 index d6913f8e194e8..809d02b21e089 --- a/drivers/base/sync.c +++ b/drivers/base/sync.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -27,8 +28,13 @@ #include +#define CREATE_TRACE_POINTS +#include + static void sync_fence_signal_pt(struct sync_pt *pt); static int _sync_pt_has_signaled(struct sync_pt *pt); +static void sync_fence_free(struct kref *kref); +static void sync_dump(void); static LIST_HEAD(sync_timeline_list_head); static DEFINE_SPINLOCK(sync_timeline_list_lock); @@ -49,6 +55,7 @@ struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, if (obj == NULL) return NULL; + kref_init(&obj->kref); obj->ops = ops; strlcpy(obj->name, name, sizeof(obj->name)); @@ -64,9 +71,12 @@ struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops, return obj; } +EXPORT_SYMBOL(sync_timeline_create); -static void sync_timeline_free(struct sync_timeline *obj) +static void sync_timeline_free(struct kref *kref) { + struct sync_timeline *obj = + container_of(kref, struct sync_timeline, kref); unsigned long flags; if (obj->ops->release_obj) @@ -81,19 +91,17 @@ static void sync_timeline_free(struct sync_timeline *obj) void sync_timeline_destroy(struct sync_timeline *obj) { - unsigned long flags; - bool needs_freeing; - - spin_lock_irqsave(&obj->child_list_lock, flags); obj->destroyed = true; - needs_freeing = list_empty(&obj->child_list_head); - spin_unlock_irqrestore(&obj->child_list_lock, flags); - if (needs_freeing) - sync_timeline_free(obj); - else + /* + * If this is not the last reference, signal any children + * that their parent is going away. + */ + + if (!kref_put(&obj->kref, sync_timeline_free)) sync_timeline_signal(obj); } +EXPORT_SYMBOL(sync_timeline_destroy); static void sync_timeline_add_pt(struct sync_timeline *obj, struct sync_pt *pt) { @@ -110,7 +118,6 @@ static void sync_timeline_remove_pt(struct sync_pt *pt) { struct sync_timeline *obj = pt->parent; unsigned long flags; - bool needs_freeing; spin_lock_irqsave(&obj->active_list_lock, flags); if (!list_empty(&pt->active_list)) @@ -118,12 +125,10 @@ static void sync_timeline_remove_pt(struct sync_pt *pt) spin_unlock_irqrestore(&obj->active_list_lock, flags); spin_lock_irqsave(&obj->child_list_lock, flags); - list_del(&pt->child_list); - needs_freeing = obj->destroyed && list_empty(&obj->child_list_head); + if (!list_empty(&pt->child_list)) { + list_del_init(&pt->child_list); + } spin_unlock_irqrestore(&obj->child_list_lock, flags); - - if (needs_freeing) - sync_timeline_free(obj); } void sync_timeline_signal(struct sync_timeline *obj) @@ -132,26 +137,33 @@ void sync_timeline_signal(struct sync_timeline *obj) LIST_HEAD(signaled_pts); struct list_head *pos, *n; + trace_sync_timeline(obj); + spin_lock_irqsave(&obj->active_list_lock, flags); list_for_each_safe(pos, n, &obj->active_list_head) { struct sync_pt *pt = container_of(pos, struct sync_pt, active_list); - if (_sync_pt_has_signaled(pt)) - list_move(pos, &signaled_pts); + if (_sync_pt_has_signaled(pt)) { + list_del_init(pos); + list_add(&pt->signaled_list, &signaled_pts); + kref_get(&pt->fence->kref); + } } spin_unlock_irqrestore(&obj->active_list_lock, flags); list_for_each_safe(pos, n, &signaled_pts) { struct sync_pt *pt = - container_of(pos, struct sync_pt, active_list); + container_of(pos, struct sync_pt, signaled_list); list_del_init(pos); sync_fence_signal_pt(pt); + kref_put(&pt->fence->kref, sync_fence_free); } } +EXPORT_SYMBOL(sync_timeline_signal); struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size) { @@ -165,10 +177,12 @@ struct sync_pt *sync_pt_create(struct sync_timeline *parent, int size) return NULL; INIT_LIST_HEAD(&pt->active_list); + kref_get(&parent->kref); sync_timeline_add_pt(parent, pt); return pt; } +EXPORT_SYMBOL(sync_pt_create); void sync_pt_free(struct sync_pt *pt) { @@ -177,8 +191,11 @@ void sync_pt_free(struct sync_pt *pt) sync_timeline_remove_pt(pt); + kref_put(&pt->parent->kref, sync_timeline_free); + kfree(pt); } +EXPORT_SYMBOL(sync_pt_free); /* call with pt->parent->active_list_lock held */ static int _sync_pt_has_signaled(struct sync_pt *pt) @@ -247,6 +264,7 @@ static struct sync_fence *sync_fence_alloc(const char *name) if (fence->file == NULL) goto err; + kref_init(&fence->kref); strlcpy(fence->name, name, sizeof(fence->name)); INIT_LIST_HEAD(&fence->pt_list_head); @@ -282,8 +300,15 @@ struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt) list_add(&pt->pt_list, &fence->pt_list_head); sync_pt_activate(pt); + /* + * signal the fence in case pt was activated before + * sync_pt_activate(pt) was called + */ + sync_fence_signal_pt(pt); + return fence; } +EXPORT_SYMBOL(sync_fence_create); static int sync_fence_copy_pts(struct sync_fence *dst, struct sync_fence *src) { @@ -305,6 +330,65 @@ static int sync_fence_copy_pts(struct sync_fence *dst, struct sync_fence *src) return 0; } +static int sync_fence_merge_pts(struct sync_fence *dst, struct sync_fence *src) +{ + struct list_head *src_pos, *dst_pos, *n; + + list_for_each(src_pos, &src->pt_list_head) { + struct sync_pt *src_pt = + container_of(src_pos, struct sync_pt, pt_list); + bool collapsed = false; + + list_for_each_safe(dst_pos, n, &dst->pt_list_head) { + struct sync_pt *dst_pt = + container_of(dst_pos, struct sync_pt, pt_list); + /* collapse two sync_pts on the same timeline + * to a single sync_pt that will signal at + * the later of the two + */ + if (dst_pt->parent == src_pt->parent) { + if (dst_pt->parent->ops->compare(dst_pt, src_pt) == -1) { + struct sync_pt *new_pt = + sync_pt_dup(src_pt); + if (new_pt == NULL) + return -ENOMEM; + + new_pt->fence = dst; + list_replace(&dst_pt->pt_list, + &new_pt->pt_list); + sync_pt_activate(new_pt); + sync_pt_free(dst_pt); + } + collapsed = true; + break; + } + } + + if (!collapsed) { + struct sync_pt *new_pt = sync_pt_dup(src_pt); + + if (new_pt == NULL) + return -ENOMEM; + + new_pt->fence = dst; + list_add(&new_pt->pt_list, &dst->pt_list_head); + sync_pt_activate(new_pt); + } + } + + return 0; +} + +static void sync_fence_detach_pts(struct sync_fence *fence) +{ + struct list_head *pos, *n; + + list_for_each_safe(pos, n, &fence->pt_list_head) { + struct sync_pt *pt = container_of(pos, struct sync_pt, pt_list); + sync_timeline_remove_pt(pt); + } +} + static void sync_fence_free_pts(struct sync_fence *fence) { struct list_head *pos, *n; @@ -331,16 +415,19 @@ struct sync_fence *sync_fence_fdget(int fd) fput(file); return NULL; } +EXPORT_SYMBOL(sync_fence_fdget); void sync_fence_put(struct sync_fence *fence) { fput(fence->file); } +EXPORT_SYMBOL(sync_fence_put); void sync_fence_install(struct sync_fence *fence, int fd) { fd_install(fd, fence->file); } +EXPORT_SYMBOL(sync_fence_install); static int sync_fence_get_status(struct sync_fence *fence) { @@ -376,11 +463,17 @@ struct sync_fence *sync_fence_merge(const char *name, if (err < 0) goto err; - err = sync_fence_copy_pts(fence, b); + err = sync_fence_merge_pts(fence, b); if (err < 0) goto err; - fence->status = sync_fence_get_status(fence); + /* + * signal the fence in case one of it's pts were activated before + * they were activated + */ + sync_fence_signal_pt(list_first_entry(&fence->pt_list_head, + struct sync_pt, + pt_list)); return fence; err: @@ -388,6 +481,7 @@ struct sync_fence *sync_fence_merge(const char *name, kfree(fence); return NULL; } +EXPORT_SYMBOL(sync_fence_merge); static void sync_fence_signal_pt(struct sync_pt *pt) { @@ -421,33 +515,22 @@ static void sync_fence_signal_pt(struct sync_pt *pt) container_of(pos, struct sync_fence_waiter, waiter_list); - waiter->callback(fence, waiter->callback_data); list_del(pos); - kfree(waiter); + waiter->callback(fence, waiter); } wake_up(&fence->wq); } } int sync_fence_wait_async(struct sync_fence *fence, - void (*callback)(struct sync_fence *, void *data), - void *callback_data) + struct sync_fence_waiter *waiter) { - struct sync_fence_waiter *waiter; unsigned long flags; int err = 0; - waiter = kzalloc(sizeof(struct sync_fence_waiter), GFP_KERNEL); - if (waiter == NULL) - return -ENOMEM; - - waiter->callback = callback; - waiter->callback_data = callback_data; - spin_lock_irqsave(&fence->waiter_list_lock, flags); if (fence->status) { - kfree(waiter); err = fence->status; goto out; } @@ -458,44 +541,118 @@ int sync_fence_wait_async(struct sync_fence *fence, return err; } +EXPORT_SYMBOL(sync_fence_wait_async); + +int sync_fence_cancel_async(struct sync_fence *fence, + struct sync_fence_waiter *waiter) +{ + struct list_head *pos; + struct list_head *n; + unsigned long flags; + int ret = -ENOENT; + + spin_lock_irqsave(&fence->waiter_list_lock, flags); + /* + * Make sure waiter is still in waiter_list because it is possible for + * the waiter to be removed from the list while the callback is still + * pending. + */ + list_for_each_safe(pos, n, &fence->waiter_list_head) { + struct sync_fence_waiter *list_waiter = + container_of(pos, struct sync_fence_waiter, + waiter_list); + if (list_waiter == waiter) { + list_del(pos); + ret = 0; + break; + } + } + spin_unlock_irqrestore(&fence->waiter_list_lock, flags); + return ret; +} +EXPORT_SYMBOL(sync_fence_cancel_async); + +static bool sync_fence_check(struct sync_fence *fence) +{ + /* + * Make sure that reads to fence->status are ordered with the + * wait queue event triggering + */ + smp_rmb(); + return fence->status != 0; +} int sync_fence_wait(struct sync_fence *fence, long timeout) { - int err; + int err = 0; + struct sync_pt *pt; - if (timeout) { + trace_sync_wait(fence, 1); + list_for_each_entry(pt, &fence->pt_list_head, pt_list) + trace_sync_pt(pt); + + if (timeout > 0) { timeout = msecs_to_jiffies(timeout); err = wait_event_interruptible_timeout(fence->wq, - fence->status != 0, + sync_fence_check(fence), timeout); - } else { - err = wait_event_interruptible(fence->wq, fence->status != 0); + } else if (timeout < 0) { + err = wait_event_interruptible(fence->wq, + sync_fence_check(fence)); } + trace_sync_wait(fence, 0); if (err < 0) return err; - if (fence->status < 0) + if (fence->status < 0) { + pr_info("fence error %d on [%p]\n", fence->status, fence); + sync_dump(); return fence->status; + } - if (fence->status == 0) + if (fence->status == 0) { + pr_info("fence timeout on [%p] after %dms\n", fence, + jiffies_to_msecs(timeout)); + sync_dump(); return -ETIME; + } return 0; } +EXPORT_SYMBOL(sync_fence_wait); + +static void sync_fence_free(struct kref *kref) +{ + struct sync_fence *fence = container_of(kref, struct sync_fence, kref); + + sync_fence_free_pts(fence); + + kfree(fence); +} static int sync_fence_release(struct inode *inode, struct file *file) { struct sync_fence *fence = file->private_data; unsigned long flags; - sync_fence_free_pts(fence); - + /* + * We need to remove all ways to access this fence before droping + * our ref. + * + * start with its membership in the global fence list + */ spin_lock_irqsave(&sync_fence_list_lock, flags); list_del(&fence->sync_fence_list); spin_unlock_irqrestore(&sync_fence_list_lock, flags); - kfree(fence); + /* + * remove its pts from their parents so that sync_timeline_signal() + * can't reference the fence. + */ + sync_fence_detach_pts(fence); + + kref_put(&fence->kref, sync_fence_free); return 0; } @@ -506,6 +663,12 @@ static unsigned int sync_fence_poll(struct file *file, poll_table *wait) poll_wait(file, &fence->wq, wait); + /* + * Make sure that reads to fence->status are ordered with the + * wait queue event triggering + */ + smp_rmb(); + if (fence->status == 1) return POLLIN; else if (fence->status < 0) @@ -516,7 +679,7 @@ static unsigned int sync_fence_poll(struct file *file, poll_table *wait) static long sync_fence_ioctl_wait(struct sync_fence *fence, unsigned long arg) { - __u32 value; + __s32 value; if (copy_from_user(&value, (void __user *)arg, sizeof(value))) return -EFAULT; @@ -531,8 +694,13 @@ static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg) struct sync_fence *fence2, *fence3; struct sync_merge_data data; - if (copy_from_user(&data, (void __user *)arg, sizeof(data))) - return -EFAULT; + if (fd < 0) + return fd; + + if (copy_from_user(&data, (void __user *)arg, sizeof(data))) { + err = -EFAULT; + goto err_put_fd; + } fence2 = sync_fence_fdget(data.fd2); if (fence2 == NULL) { @@ -568,7 +736,7 @@ static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg) return err; } -int sync_fill_pt_info(struct sync_pt *pt, void *data, int size) +static int sync_fill_pt_info(struct sync_pt *pt, void *data, int size) { struct sync_pt_info *info = data; int ret; @@ -596,7 +764,6 @@ int sync_fill_pt_info(struct sync_pt *pt, void *data, int size) return info->len; } - static long sync_fence_ioctl_fence_info(struct sync_fence *fence, unsigned long arg) { @@ -690,7 +857,17 @@ static void sync_print_pt(struct seq_file *s, struct sync_pt *pt, bool fence) seq_printf(s, "@%ld.%06ld", tv.tv_sec, tv.tv_usec); } - if (pt->parent->ops->print_pt) { + if (pt->parent->ops->timeline_value_str && + pt->parent->ops->pt_value_str) { + char value[64]; + pt->parent->ops->pt_value_str(pt, value, sizeof(value)); + seq_printf(s, ": %s", value); + if (fence) { + pt->parent->ops->timeline_value_str(pt->parent, value, + sizeof(value)); + seq_printf(s, " / %s", value); + } + } else if (pt->parent->ops->print_pt) { seq_printf(s, ": "); pt->parent->ops->print_pt(s, pt); } @@ -705,7 +882,11 @@ static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj) seq_printf(s, "%s %s", obj->name, obj->ops->driver_name); - if (obj->ops->print_obj) { + if (obj->ops->timeline_value_str) { + char value[64]; + obj->ops->timeline_value_str(obj, value, sizeof(value)); + seq_printf(s, ": %s", value); + } else if (obj->ops->print_obj) { seq_printf(s, ": "); obj->ops->print_obj(s, obj); } @@ -726,7 +907,8 @@ static void sync_print_fence(struct seq_file *s, struct sync_fence *fence) struct list_head *pos; unsigned long flags; - seq_printf(s, "%s: %s\n", fence->name, sync_status_str(fence->status)); + seq_printf(s, "[%p] %s: %s\n", fence, fence->name, + sync_status_str(fence->status)); list_for_each(pos, &fence->pt_list_head) { struct sync_pt *pt = @@ -740,8 +922,7 @@ static void sync_print_fence(struct seq_file *s, struct sync_fence *fence) container_of(pos, struct sync_fence_waiter, waiter_list); - seq_printf(s, "waiter %pF %p\n", waiter->callback, - waiter->callback_data); + seq_printf(s, "waiter %pF\n", waiter->callback); } spin_unlock_irqrestore(&fence->waiter_list_lock, flags); } @@ -795,7 +976,34 @@ static __init int sync_debugfs_init(void) debugfs_create_file("sync", S_IRUGO, NULL, NULL, &sync_debugfs_fops); return 0; } - late_initcall(sync_debugfs_init); +#define DUMP_CHUNK 256 +static char sync_dump_buf[64 * 1024]; +void sync_dump(void) +{ + struct seq_file s = { + .buf = sync_dump_buf, + .size = sizeof(sync_dump_buf) - 1, + }; + int i; + + sync_debugfs_show(&s, NULL); + + for (i = 0; i < s.count; i += DUMP_CHUNK) { + if ((s.count - i) > DUMP_CHUNK) { + char c = s.buf[i + DUMP_CHUNK]; + s.buf[i + DUMP_CHUNK] = 0; + pr_cont("%s", s.buf + i); + s.buf[i + DUMP_CHUNK] = c; + } else { + s.buf[s.count] = 0; + pr_cont("%s", s.buf + i); + } + } +} +#else +static void sync_dump(void) +{ +} #endif diff --git a/include/linux/sync.h b/include/linux/sync.h old mode 100644 new mode 100755 index 4f1993871467c..31ba6ec0819d4 --- a/include/linux/sync.h +++ b/include/linux/sync.h @@ -16,6 +16,7 @@ #include #ifdef __KERNEL__ +#include #include #include #include @@ -24,6 +25,7 @@ struct sync_timeline; struct sync_pt; struct sync_fence; +struct seq_file; /** * struct sync_timeline_ops - sync object implementation ops @@ -39,14 +41,14 @@ struct sync_fence; * -1 if a will signabl before b * @free_pt: called before sync_pt is freed * @release_obj: called before sync_timeline is freed - * @print_obj: print aditional debug information about sync_timeline. - * should not print a newline - * @print_pt: print aditional debug information about sync_pt. - * should not print a newline + * @print_obj: deprecated + * @print_pt: deprecated * @fill_driver_data: write implmentation specific driver data to data. * should return an error if there is not enough room * as specified by size. This information is returned * to userspace by SYNC_IOC_FENCE_INFO. + * @timeline_value_str: fill str with the value of the sync_timeline's counter + * @pt_value_str: fill str with the value of the sync_pt */ struct sync_timeline_ops { const char *driver_name; @@ -66,19 +68,27 @@ struct sync_timeline_ops { /* optional */ void (*release_obj)(struct sync_timeline *sync_timeline); - /* optional */ + /* deprecated */ void (*print_obj)(struct seq_file *s, struct sync_timeline *sync_timeline); - /* optional */ + /* deprecated */ void (*print_pt)(struct seq_file *s, struct sync_pt *sync_pt); /* optional */ int (*fill_driver_data)(struct sync_pt *syncpt, void *data, int size); + + /* optional */ + void (*timeline_value_str)(struct sync_timeline *timeline, char *str, + int size); + + /* optional */ + void (*pt_value_str)(struct sync_pt *pt, char *str, int size); }; /** * struct sync_timeline - sync object + * @kref: reference count on fence. * @ops: ops that define the implementaiton of the sync_timeline * @name: name of the sync_timeline. Useful for debugging * @destoryed: set when sync_timeline is destroyed @@ -89,6 +99,7 @@ struct sync_timeline_ops { * @sync_timeline_list: membership in global sync_timeline_list */ struct sync_timeline { + struct kref kref; const struct sync_timeline_ops *ops; char name[32]; @@ -109,6 +120,7 @@ struct sync_timeline { * @parent: sync_timeline to which this sync_pt belongs * @child_list: membership in sync_timeline.child_list_head * @active_list: membership in sync_timeline.active_list_head + * @signaled_list: membership in temorary signaled_list on stack * @fence: sync_fence to which the sync_pt belongs * @pt_list: membership in sync_fence.pt_list_head * @status: 1: signaled, 0:active, <0: error @@ -120,6 +132,7 @@ struct sync_pt { struct list_head child_list; struct list_head active_list; + struct list_head signaled_list; struct sync_fence *fence; struct list_head pt_list; @@ -133,6 +146,7 @@ struct sync_pt { /** * struct sync_fence - sync fence * @file: file representing this fence + * @kref: referenace count on fence. * @name: name of sync_fence. Useful for debugging * @pt_list_head: list of sync_pts in ths fence. immutable once fence * is created @@ -145,6 +159,7 @@ struct sync_pt { */ struct sync_fence { struct file *file; + struct kref kref; char name[32]; /* this list is immutable once the fence is created */ @@ -159,6 +174,10 @@ struct sync_fence { struct list_head sync_fence_list; }; +struct sync_fence_waiter; +typedef void (*sync_callback_t)(struct sync_fence *fence, + struct sync_fence_waiter *waiter); + /** * struct sync_fence_waiter - metadata for asynchronous waiter on a fence * @waiter_list: membership in sync_fence.waiter_list_head @@ -168,10 +187,15 @@ struct sync_fence { struct sync_fence_waiter { struct list_head waiter_list; - void (*callback)(struct sync_fence *fence, void *data); - void *callback_data; + sync_callback_t callback; }; +static inline void sync_fence_waiter_init(struct sync_fence_waiter *waiter, + sync_callback_t callback) +{ + waiter->callback = callback; +} + /* * API for sync_timeline implementers */ @@ -284,24 +308,37 @@ void sync_fence_install(struct sync_fence *fence, int fd); /** * sync_fence_wait_async() - registers and async wait on the fence * @fence: fence to wait on - * @callback: callback - * @callback_data data to pass to the callback + * @waiter: waiter callback struck * * Returns 1 if @fence has already signaled. * - * Registers a callback to be called when @fence signals or has an error + * Registers a callback to be called when @fence signals or has an error. + * @waiter should be initialized with sync_fence_waiter_init(). */ int sync_fence_wait_async(struct sync_fence *fence, - void (*callback)(struct sync_fence *, void *data), - void *callback_data); + struct sync_fence_waiter *waiter); + +/** + * sync_fence_cancel_async() - cancels an async wait + * @fence: fence to wait on + * @waiter: waiter callback struck + * + * returns 0 if waiter was removed from fence's async waiter list. + * returns -ENOENT if waiter was not found on fence's async waiter list. + * + * Cancels a previously registered async wait. Will fail gracefully if + * @waiter was never registered or if @fence has already signaled @waiter. + */ +int sync_fence_cancel_async(struct sync_fence *fence, + struct sync_fence_waiter *waiter); /** * sync_fence_wait() - wait on fence * @fence: fence to wait on * @tiemout: timeout in ms * - * Wait for @fence to be signaled or have an error. Waits indefintly - * if @timeout = 0 + * Wait for @fence to be signaled or have an error. Waits indefinitely + * if @timeout < 0 */ int sync_fence_wait(struct sync_fence *fence, long timeout); @@ -360,9 +397,9 @@ struct sync_fence_info_data { /** * DOC: SYNC_IOC_WAIT - wait for a fence to signal * - * pass timeout in milliseconds. + * pass timeout in milliseconds. Waits indefinitely timeout < 0. */ -#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __u32) +#define SYNC_IOC_WAIT _IOW(SYNC_IOC_MAGIC, 0, __s32) /** * DOC: SYNC_IOC_MERGE - merge two fences From 3a20cde1b2812586d5c29de885160386519b6001 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 23 May 2011 14:11:39 -0400 Subject: [PATCH 2479/2556] module.h: split out the EXPORT_SYMBOL into export.h A lot of files pull in module.h when all they are really looking for is the basic EXPORT_SYMBOL functionality. The recent data from Ingo[1] shows that this is one of several instances that has a significant impact on compile times, and it should be targeted for factoring out (as done here). Note that several commonly used header files in include/* directly include themselves (some 34 of them!) The most commonly used ones of these will have to be made independent of module.h before the full benefit of this change can be realized. We also transition THIS_MODULE from module.h to export.h, since there are lots of files with subsystem structs that in turn will have a struct module *owner and only be doing: .owner = THIS_MODULE; and absolutely nothing else modular. So, we also want to have the THIS_MODULE definition present in the lightweight header. [1] https://lkml.org/lkml/2011/5/23/76 Change-Id: I70a80b3ac3638f0eda1ac596ee3fba832880df07 Signed-off-by: Paul Gortmaker Signed-off-by: Trilok Soni --- include/linux/export.h | 89 ++++++++++++++++++++++++++++++++++++++++++ include/linux/module.h | 68 +------------------------------- 2 files changed, 90 insertions(+), 67 deletions(-) create mode 100644 include/linux/export.h diff --git a/include/linux/export.h b/include/linux/export.h new file mode 100644 index 0000000000000..696c0f48afc71 --- /dev/null +++ b/include/linux/export.h @@ -0,0 +1,89 @@ +#ifndef _LINUX_EXPORT_H +#define _LINUX_EXPORT_H +/* + * Export symbols from the kernel to modules. Forked from module.h + * to reduce the amount of pointless cruft we feed to gcc when only + * exporting a simple symbol or two. + * + * If you feel the need to add #include to this file + * then you are doing something wrong and should go away silently. + */ + +/* Some toolchains use a `_' prefix for all user symbols. */ +#ifdef CONFIG_SYMBOL_PREFIX +#define MODULE_SYMBOL_PREFIX CONFIG_SYMBOL_PREFIX +#else +#define MODULE_SYMBOL_PREFIX "" +#endif + +struct kernel_symbol +{ + unsigned long value; + const char *name; +}; + +#ifdef MODULE +extern struct module __this_module; +#define THIS_MODULE (&__this_module) +#else +#define THIS_MODULE ((struct module *)0) +#endif + +#ifdef CONFIG_MODULES + +#ifndef __GENKSYMS__ +#ifdef CONFIG_MODVERSIONS +/* Mark the CRC weak since genksyms apparently decides not to + * generate a checksums for some symbols */ +#define __CRC_SYMBOL(sym, sec) \ + extern void *__crc_##sym __attribute__((weak)); \ + static const unsigned long __kcrctab_##sym \ + __used \ + __attribute__((section("___kcrctab" sec "+" #sym), unused)) \ + = (unsigned long) &__crc_##sym; +#else +#define __CRC_SYMBOL(sym, sec) +#endif + +/* For every exported symbol, place a struct in the __ksymtab section */ +#define __EXPORT_SYMBOL(sym, sec) \ + extern typeof(sym) sym; \ + __CRC_SYMBOL(sym, sec) \ + static const char __kstrtab_##sym[] \ + __attribute__((section("__ksymtab_strings"), aligned(1))) \ + = MODULE_SYMBOL_PREFIX #sym; \ + static const struct kernel_symbol __ksymtab_##sym \ + __used \ + __attribute__((section("___ksymtab" sec "+" #sym), unused)) \ + = { (unsigned long)&sym, __kstrtab_##sym } + +#define EXPORT_SYMBOL(sym) \ + __EXPORT_SYMBOL(sym, "") + +#define EXPORT_SYMBOL_GPL(sym) \ + __EXPORT_SYMBOL(sym, "_gpl") + +#define EXPORT_SYMBOL_GPL_FUTURE(sym) \ + __EXPORT_SYMBOL(sym, "_gpl_future") + +#ifdef CONFIG_UNUSED_SYMBOLS +#define EXPORT_UNUSED_SYMBOL(sym) __EXPORT_SYMBOL(sym, "_unused") +#define EXPORT_UNUSED_SYMBOL_GPL(sym) __EXPORT_SYMBOL(sym, "_unused_gpl") +#else +#define EXPORT_UNUSED_SYMBOL(sym) +#define EXPORT_UNUSED_SYMBOL_GPL(sym) +#endif + +#endif /* __GENKSYMS__ */ + +#else /* !CONFIG_MODULES... */ + +#define EXPORT_SYMBOL(sym) +#define EXPORT_SYMBOL_GPL(sym) +#define EXPORT_SYMBOL_GPL_FUTURE(sym) +#define EXPORT_UNUSED_SYMBOL(sym) +#define EXPORT_UNUSED_SYMBOL_GPL(sym) + +#endif /* CONFIG_MODULES */ + +#endif /* _LINUX_EXPORT_H */ diff --git a/include/linux/module.h b/include/linux/module.h index e78aa8fee03ea..cca49b3410d66 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -25,21 +26,8 @@ /* Not Yet Implemented */ #define MODULE_SUPPORTED_DEVICE(name) -/* Some toolchains use a `_' prefix for all user symbols. */ -#ifdef CONFIG_SYMBOL_PREFIX -#define MODULE_SYMBOL_PREFIX CONFIG_SYMBOL_PREFIX -#else -#define MODULE_SYMBOL_PREFIX "" -#endif - #define MODULE_NAME_LEN MAX_PARAM_PREFIX_LEN -struct kernel_symbol -{ - unsigned long value; - const char *name; -}; - struct modversion_info { unsigned long crc; @@ -93,11 +81,8 @@ void trim_init_extable(struct module *m); extern const struct gtype##_id __mod_##gtype##_table \ __attribute__ ((unused, alias(__stringify(name)))) -extern struct module __this_module; -#define THIS_MODULE (&__this_module) #else /* !MODULE */ #define MODULE_GENERIC_TABLE(gtype,name) -#define THIS_MODULE ((struct module *)0) #endif /* Generic info of form tag = "info" */ @@ -215,52 +200,6 @@ struct module_use { struct module *source, *target; }; -#ifndef __GENKSYMS__ -#ifdef CONFIG_MODVERSIONS -/* Mark the CRC weak since genksyms apparently decides not to - * generate a checksums for some symbols */ -#define __CRC_SYMBOL(sym, sec) \ - extern void *__crc_##sym __attribute__((weak)); \ - static const unsigned long __kcrctab_##sym \ - __used \ - __attribute__((section("___kcrctab" sec "+" #sym), unused)) \ - = (unsigned long) &__crc_##sym; -#else -#define __CRC_SYMBOL(sym, sec) -#endif - -/* For every exported symbol, place a struct in the __ksymtab section */ -#define __EXPORT_SYMBOL(sym, sec) \ - extern typeof(sym) sym; \ - __CRC_SYMBOL(sym, sec) \ - static const char __kstrtab_##sym[] \ - __attribute__((section("__ksymtab_strings"), aligned(1))) \ - = MODULE_SYMBOL_PREFIX #sym; \ - static const struct kernel_symbol __ksymtab_##sym \ - __used \ - __attribute__((section("___ksymtab" sec "+" #sym), unused)) \ - = { (unsigned long)&sym, __kstrtab_##sym } - -#define EXPORT_SYMBOL(sym) \ - __EXPORT_SYMBOL(sym, "") - -#define EXPORT_SYMBOL_GPL(sym) \ - __EXPORT_SYMBOL(sym, "_gpl") - -#define EXPORT_SYMBOL_GPL_FUTURE(sym) \ - __EXPORT_SYMBOL(sym, "_gpl_future") - - -#ifdef CONFIG_UNUSED_SYMBOLS -#define EXPORT_UNUSED_SYMBOL(sym) __EXPORT_SYMBOL(sym, "_unused") -#define EXPORT_UNUSED_SYMBOL_GPL(sym) __EXPORT_SYMBOL(sym, "_unused_gpl") -#else -#define EXPORT_UNUSED_SYMBOL(sym) -#define EXPORT_UNUSED_SYMBOL_GPL(sym) -#endif - -#endif - enum module_state { MODULE_STATE_LIVE, @@ -579,11 +518,6 @@ extern void module_update_tracepoints(void); extern int module_get_iter_tracepoints(struct tracepoint_iter *iter); #else /* !CONFIG_MODULES... */ -#define EXPORT_SYMBOL(sym) -#define EXPORT_SYMBOL_GPL(sym) -#define EXPORT_SYMBOL_GPL_FUTURE(sym) -#define EXPORT_UNUSED_SYMBOL(sym) -#define EXPORT_UNUSED_SYMBOL_GPL(sym) /* Given an address, look for it in the exception tables. */ static inline const struct exception_table_entry * From ce6a1ede949d440af121f17f8ae93d650f989741 Mon Sep 17 00:00:00 2001 From: Shruthi Krishna Date: Wed, 21 Nov 2012 11:23:50 -0800 Subject: [PATCH 2480/2556] Adding support for trace events for sync point driver Change-Id: Icdbe176f1c651e6a0805ef63b9b7f143ab06a350 Signed-off-by: Shruthi Krishna --- include/trace/events/sync.h | 82 +++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100755 include/trace/events/sync.h diff --git a/include/trace/events/sync.h b/include/trace/events/sync.h new file mode 100755 index 0000000000000..f31bc63ca65d3 --- /dev/null +++ b/include/trace/events/sync.h @@ -0,0 +1,82 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM sync + +#if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_SYNC_H + +#include +#include + +TRACE_EVENT(sync_timeline, + TP_PROTO(struct sync_timeline *timeline), + + TP_ARGS(timeline), + + TP_STRUCT__entry( + __string(name, timeline->name) + __array(char, value, 32) + ), + + TP_fast_assign( + __assign_str(name, timeline->name); + if (timeline->ops->timeline_value_str) { + timeline->ops->timeline_value_str(timeline, + __entry->value, + sizeof(__entry->value)); + } else { + __entry->value[0] = '\0'; + } + ), + + TP_printk("name=%s value=%s", __get_str(name), __entry->value) +); + +TRACE_EVENT(sync_wait, + TP_PROTO(struct sync_fence *fence, int begin), + + TP_ARGS(fence, begin), + + TP_STRUCT__entry( + __string(name, fence->name) + __field(s32, status) + __field(u32, begin) + ), + + TP_fast_assign( + __assign_str(name, fence->name); + __entry->status = fence->status; + __entry->begin = begin; + ), + + TP_printk("%s name=%s state=%d", __entry->begin ? "begin" : "end", + __get_str(name), __entry->status) +); + +TRACE_EVENT(sync_pt, + TP_PROTO(struct sync_pt *pt), + + TP_ARGS(pt), + + TP_STRUCT__entry( + __string(timeline, pt->parent->name) + __array(char, value, 32) + ), + + TP_fast_assign( + __assign_str(timeline, pt->parent->name); + if (pt->parent->ops->pt_value_str) { + pt->parent->ops->pt_value_str(pt, + __entry->value, + sizeof(__entry->value)); + } else { + __entry->value[0] = '\0'; + } + ), + + TP_printk("name=%s value=%s", __get_str(timeline), __entry->value) + ); + +#endif /* if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ) */ + +/* This part must be outside protection */ +#include From 5e17de6a57339d3a7cffba34740a6d6fd91df8ac Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 18 Apr 2013 00:55:09 -0500 Subject: [PATCH 2481/2556] configs: enable sync Change-Id: Ic8988dc8e5c44698d8e6177f5b255fbe0212bd7d --- arch/arm/configs/evervolv_bravo_defconfig | 1 + arch/arm/configs/evervolv_incrediblec_defconfig | 1 + arch/arm/configs/evervolv_mahimahi_defconfig | 6 ++++-- arch/arm/configs/evervolv_supersonic_defconfig | 1 + arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig | 1 + arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig | 1 + arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig | 1 + arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig | 1 + 8 files changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index 8e445a167821e..7308102263f64 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -918,6 +918,7 @@ CONFIG_EXTRA_FIRMWARE="" # CONFIG_SYS_HYPERVISOR is not set CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_SYNC=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index e1a32aabe6cb5..2278c17f1c047 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -919,6 +919,7 @@ CONFIG_EXTRA_FIRMWARE="" # CONFIG_SYS_HYPERVISOR is not set CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_SYNC=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index c416b2384309f..3fee7a8bea31b 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Tue Oct 2 20:54:19 2012 +# Thu Apr 18 00:32:01 2013 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -478,7 +478,7 @@ CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set -# CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y # CONFIG_CPU_FREQ_GOV_POWERSAVE is not set # CONFIG_CPU_FREQ_GOV_USERSPACE is not set CONFIG_CPU_FREQ_GOV_ONDEMAND=y @@ -918,6 +918,8 @@ CONFIG_EXTRA_FIRMWARE="" # CONFIG_SYS_HYPERVISOR is not set CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_SYNC=y +# CONFIG_SW_SYNC is not set # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index 4f01e71e84496..068b296d8b7d3 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -929,6 +929,7 @@ CONFIG_EXTRA_FIRMWARE="" # CONFIG_SYS_HYPERVISOR is not set CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_SYNC=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set diff --git a/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig b/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig index 1e03796f4f6b0..c99a862255c4d 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig @@ -928,6 +928,7 @@ CONFIG_EXTRA_FIRMWARE="" # CONFIG_SYS_HYPERVISOR is not set CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_SYNC=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set diff --git a/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig b/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig index c25b420b9fbdc..f7b6593ed380c 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig @@ -929,6 +929,7 @@ CONFIG_EXTRA_FIRMWARE="" # CONFIG_SYS_HYPERVISOR is not set CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_SYNC=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set diff --git a/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig b/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig index c0e3f85697e2e..da50e332baf07 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig @@ -928,6 +928,7 @@ CONFIG_EXTRA_FIRMWARE="" # CONFIG_SYS_HYPERVISOR is not set CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_SYNC=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set diff --git a/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig b/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig index a9a8e5132918a..a022f4b544ba0 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig @@ -935,6 +935,7 @@ CONFIG_EXTRA_FIRMWARE="" # CONFIG_SYS_HYPERVISOR is not set CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_SYNC=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set From ae5ad0f99e2ea5be851045f1fc010c2c9e2286fc Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 18 Apr 2013 15:54:08 -0500 Subject: [PATCH 2482/2556] ion: checkout jb_mr1_chocolate Change-Id: Ief9610929ac9e7386ed4ff2b474b2ef903f65011 --- .../arm/mach-msm/include/mach/iommu_domains.h | 110 ++ arch/arm/mach-msm/include/mach/ion.h | 8 +- drivers/gpu/ion/Makefile | 2 +- drivers/gpu/ion/ion.c | 579 ++++++++--- drivers/gpu/ion/ion_carveout_heap.c | 259 ++++- drivers/gpu/ion/ion_cp_heap.c | 978 ++++++++++++++++++ drivers/gpu/ion/ion_heap.c | 13 + drivers/gpu/ion/ion_iommu_heap.c | 350 +++++++ drivers/gpu/ion/ion_priv.h | 115 +- drivers/gpu/ion/ion_system_heap.c | 325 +++++- drivers/gpu/ion/msm/msm_ion.c | 308 +++++- include/linux/ion.h | 355 ++++++- 12 files changed, 3148 insertions(+), 254 deletions(-) create mode 100644 arch/arm/mach-msm/include/mach/iommu_domains.h create mode 100644 drivers/gpu/ion/ion_cp_heap.c create mode 100644 drivers/gpu/ion/ion_iommu_heap.c diff --git a/arch/arm/mach-msm/include/mach/iommu_domains.h b/arch/arm/mach-msm/include/mach/iommu_domains.h new file mode 100644 index 0000000000000..958b61d9c0dd7 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/iommu_domains.h @@ -0,0 +1,110 @@ +/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _ARCH_IOMMU_DOMAINS_H +#define _ARCH_IOMMU_DOMAINS_H + +enum { + VIDEO_DOMAIN, + CAMERA_DOMAIN, + DISPLAY_DOMAIN, + ROTATOR_DOMAIN, + MAX_DOMAINS +}; + +enum { + VIDEO_FIRMWARE_POOL, + VIDEO_MAIN_POOL, + GEN_POOL, +}; + + +#if defined(CONFIG_MSM_IOMMU) + +extern struct iommu_domain *msm_get_iommu_domain(int domain_num); + +extern unsigned long msm_allocate_iova_address(unsigned int iommu_domain, + unsigned int partition_no, + unsigned long size, + unsigned long align); + +extern void msm_free_iova_address(unsigned long iova, + unsigned int iommu_domain, + unsigned int partition_no, + unsigned long size); + +extern unsigned long msm_subsystem_get_domain_no(int subsys_id); + +extern unsigned long msm_subsystem_get_partition_no(int subsys_id); + +extern int msm_use_iommu(void); + +extern int msm_iommu_map_extra(struct iommu_domain *domain, + unsigned long start_iova, + unsigned long size, + unsigned long page_size, + int cached); + +extern void msm_iommu_unmap_extra(struct iommu_domain *domain, + unsigned long start_iova, + unsigned long size, + unsigned long page_size); + +#else +static inline struct iommu_domain + *msm_get_iommu_domain(int subsys_id) { return NULL; } + + + +static inline unsigned long msm_allocate_iova_address(unsigned int iommu_domain, + unsigned int partition_no, + unsigned long size, + unsigned long align) { return 0; } + +static inline void msm_free_iova_address(unsigned long iova, + unsigned int iommu_domain, + unsigned int partition_no, + unsigned long size) { return; } + +static inline unsigned long msm_subsystem_get_domain_no(int subsys_id) +{ + return 0xFFFFFFFF; +} + +static inline unsigned long msm_subsystem_get_partition_no(int subsys_id) +{ + return 0xFFFFFFFF; +} + +static inline int msm_use_iommu(void) +{ + return 0; +} + +static inline int msm_iommu_map_extra(struct iommu_domain *domain, + unsigned long start_iova, + unsigned long size, + unsigned long page_size, + int cached) +{ + return -ENODEV; +} + +static inline void msm_iommu_unmap_extra(struct iommu_domain *domain, + unsigned long start_iova, + unsigned long size, + unsigned long page_size) +{ +} +#endif + +#endif diff --git a/arch/arm/mach-msm/include/mach/ion.h b/arch/arm/mach-msm/include/mach/ion.h index 4d12249d567a9..b472d27fc47db 100644 --- a/arch/arm/mach-msm/include/mach/ion.h +++ b/arch/arm/mach-msm/include/mach/ion.h @@ -1,6 +1,6 @@ /** * - * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -20,4 +20,10 @@ enum ion_memory_types { ION_SMI_TYPE, }; +enum ion_permission_type { + IPT_TYPE_MM_CARVEOUT = 0, + IPT_TYPE_MFC_SHAREDMEM = 1, + IPT_TYPE_MDP_WRITEBACK = 2, +}; + #endif diff --git a/drivers/gpu/ion/Makefile b/drivers/gpu/ion/Makefile index c0a47d81be77b..c9e8a944052e9 100644 --- a/drivers/gpu/ion/Makefile +++ b/drivers/gpu/ion/Makefile @@ -1,3 +1,3 @@ -obj-$(CONFIG_ION) += ion.o ion_heap.o ion_system_heap.o ion_carveout_heap.o +obj-$(CONFIG_ION) += ion.o ion_heap.o ion_system_heap.o ion_carveout_heap.o ion_iommu_heap.o ion_cp_heap.o obj-$(CONFIG_ION_TEGRA) += tegra/ obj-$(CONFIG_ION_MSM) += msm/ diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c index 50420ba5c7e7b..060c71fc5fbdf 100644 --- a/drivers/gpu/ion/ion.c +++ b/drivers/gpu/ion/ion.c @@ -2,6 +2,7 @@ * drivers/gpu/ion/ion.c * * Copyright (C) 2011 Google, Inc. + * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -30,6 +31,7 @@ #include #include +#include #include "ion_priv.h" #define DEBUG @@ -75,7 +77,7 @@ struct ion_client { struct rb_root handles; struct mutex lock; unsigned int heap_mask; - const char *name; + char *name; struct task_struct *task; pid_t pid; struct dentry *debug_root; @@ -102,8 +104,11 @@ struct ion_handle { unsigned int kmap_cnt; unsigned int dmap_cnt; unsigned int usermap_cnt; + unsigned int iommu_map_cnt; }; +static void ion_iommu_release(struct kref *kref); + /* this function should only be called while dev->lock is held */ static void ion_buffer_add(struct ion_device *dev, struct ion_buffer *buffer) @@ -130,6 +135,61 @@ static void ion_buffer_add(struct ion_device *dev, rb_insert_color(&buffer->node, &dev->buffers); } +static void ion_iommu_add(struct ion_buffer *buffer, + struct ion_iommu_map *iommu) +{ + struct rb_node **p = &buffer->iommu_maps.rb_node; + struct rb_node *parent = NULL; + struct ion_iommu_map *entry; + + while (*p) { + parent = *p; + entry = rb_entry(parent, struct ion_iommu_map, node); + + if (iommu->key < entry->key) { + p = &(*p)->rb_left; + } else if (iommu->key > entry->key) { + p = &(*p)->rb_right; + } else { + pr_err("%s: buffer %p already has mapping for domain %d" + " and partition %d\n", __func__, + buffer, + iommu_map_domain(iommu), + iommu_map_partition(iommu)); + BUG(); + } + } + + rb_link_node(&iommu->node, parent, p); + rb_insert_color(&iommu->node, &buffer->iommu_maps); + +} + +static struct ion_iommu_map *ion_iommu_lookup(struct ion_buffer *buffer, + unsigned int domain_no, + unsigned int partition_no) +{ + struct rb_node **p = &buffer->iommu_maps.rb_node; + struct rb_node *parent = NULL; + struct ion_iommu_map *entry; + uint64_t key = domain_no; + key = key << 32 | partition_no; + + while (*p) { + parent = *p; + entry = rb_entry(parent, struct ion_iommu_map, node); + + if (key < entry->key) + p = &(*p)->rb_left; + else if (key > entry->key) + p = &(*p)->rb_right; + else + return entry; + } + + return NULL; +} + /* 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, @@ -154,16 +214,50 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, } buffer->dev = dev; buffer->size = len; + buffer->flags = flags; mutex_init(&buffer->lock); ion_buffer_add(dev, buffer); return buffer; } +/** + * Check for delayed IOMMU unmapping. Also unmap any outstanding + * mappings which would otherwise have been leaked. + */ +static void ion_iommu_delayed_unmap(struct ion_buffer *buffer) +{ + struct ion_iommu_map *iommu_map; + struct rb_node *node; + const struct rb_root *rb = &(buffer->iommu_maps); + unsigned long ref_count; + unsigned int delayed_unmap; + + mutex_lock(&buffer->lock); + + while ((node = rb_first(rb)) != 0) { + iommu_map = rb_entry(node, struct ion_iommu_map, node); + ref_count = atomic_read(&iommu_map->ref.refcount); + delayed_unmap = iommu_map->flags & ION_IOMMU_UNMAP_DELAYED; + + if ((delayed_unmap && ref_count > 1) || !delayed_unmap) { + pr_err("%s: Virtual memory address leak in domain %u, partition %u\n", + __func__, iommu_map->domain_info[DI_DOMAIN_NUM], + iommu_map->domain_info[DI_PARTITION_NUM]); + } + /* set ref count to 1 to force release */ + kref_init(&iommu_map->ref); + kref_put(&iommu_map->ref, ion_iommu_release); + } + + mutex_unlock(&buffer->lock); +} + static void ion_buffer_destroy(struct kref *kref) { struct ion_buffer *buffer = container_of(kref, struct ion_buffer, ref); struct ion_device *dev = buffer->dev; + ion_iommu_delayed_unmap(buffer); buffer->heap->ops->free(buffer); mutex_lock(&dev->lock); rb_erase(&buffer->node, &dev->buffers); @@ -198,6 +292,7 @@ static struct ion_handle *ion_handle_create(struct ion_client *client, return handle; } +/* Client lock must be locked when calling */ static void ion_handle_destroy(struct kref *kref) { struct ion_handle *handle = container_of(kref, struct ion_handle, ref); @@ -206,10 +301,8 @@ static void ion_handle_destroy(struct kref *kref) */ WARN_ON(handle->kmap_cnt || handle->dmap_cnt || handle->usermap_cnt); ion_buffer_put(handle->buffer); - mutex_lock(&handle->client->lock); if (!RB_EMPTY_NODE(&handle->node)) rb_erase(&handle->node, &handle->client->handles); - mutex_unlock(&handle->client->lock); kfree(handle); } @@ -288,6 +381,12 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len, struct ion_handle *handle; struct ion_device *dev = client->dev; struct ion_buffer *buffer = NULL; + unsigned long secure_allocation = flags & ION_SECURE; + const unsigned int MAX_DBG_STR_LEN = 64; + char dbg_str[MAX_DBG_STR_LEN]; + unsigned int dbg_str_idx = 0; + + dbg_str[0] = '\0'; /* * traverse the list of heaps available in this system in priority @@ -304,14 +403,37 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len, /* if the caller didn't specify this heap type */ if (!((1 << heap->id) & flags)) continue; + /* Do not allow un-secure heap if secure is specified */ + if (secure_allocation && (heap->type != ION_HEAP_TYPE_CP)) + continue; buffer = ion_buffer_create(heap, dev, len, align, flags); if (!IS_ERR_OR_NULL(buffer)) break; + if (dbg_str_idx < MAX_DBG_STR_LEN) { + unsigned int len_left = MAX_DBG_STR_LEN-dbg_str_idx-1; + int ret_value = snprintf(&dbg_str[dbg_str_idx], + len_left, "%s ", heap->name); + if (ret_value >= len_left) { + /* overflow */ + dbg_str[MAX_DBG_STR_LEN-1] = '\0'; + dbg_str_idx = MAX_DBG_STR_LEN; + } else if (ret_value >= 0) { + dbg_str_idx += ret_value; + } else { + /* error */ + dbg_str[MAX_DBG_STR_LEN-1] = '\0'; + } + } } mutex_unlock(&dev->lock); - if (IS_ERR_OR_NULL(buffer)) + if (IS_ERR_OR_NULL(buffer)) { + pr_debug("ION is unable to allocate 0x%x bytes (alignment: " + "0x%x) from heap(s) %sfor client %s with heap " + "mask 0x%x\n", + len, align, dbg_str, client->name, client->heap_mask); return ERR_PTR(PTR_ERR(buffer)); + } handle = ion_handle_create(client, buffer); @@ -333,6 +455,7 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len, ion_buffer_put(buffer); return handle; } +EXPORT_SYMBOL(ion_alloc); void ion_free(struct ion_client *client, struct ion_handle *handle) { @@ -342,14 +465,15 @@ void ion_free(struct ion_client *client, struct ion_handle *handle) mutex_lock(&client->lock); valid_handle = ion_handle_validate(client, handle); - mutex_unlock(&client->lock); - if (!valid_handle) { + mutex_unlock(&client->lock); WARN("%s: invalid handle passed to free.\n", __func__); return; } ion_handle_put(handle); + mutex_unlock(&client->lock); } +EXPORT_SYMBOL(ion_free); static void ion_client_get(struct ion_client *client); static int ion_client_put(struct ion_client *client); @@ -407,6 +531,7 @@ int ion_phys(struct ion_client *client, struct ion_handle *handle, ret = buffer->heap->ops->phys(buffer->heap, buffer, addr, len); return ret; } +EXPORT_SYMBOL(ion_phys); void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle, unsigned long flags) @@ -433,19 +558,6 @@ void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle, return ERR_PTR(-ENODEV); } - if (buffer->kmap_cnt || buffer->dmap_cnt || buffer->umap_cnt) { - if (buffer->flags != flags) { - pr_err("%s: buffer was already mapped with flags %lx," - " cannot map with flags %lx\n", __func__, - buffer->flags, flags); - vaddr = ERR_PTR(-EEXIST); - goto out; - } - - } else { - buffer->flags = flags; - } - if (_ion_map(&buffer->kmap_cnt, &handle->kmap_cnt)) { vaddr = buffer->heap->ops->map_kernel(buffer->heap, buffer, flags); @@ -456,11 +568,195 @@ void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle, vaddr = buffer->vaddr; } -out: mutex_unlock(&buffer->lock); mutex_unlock(&client->lock); return vaddr; } +EXPORT_SYMBOL(ion_map_kernel); + +static struct ion_iommu_map *__ion_iommu_map(struct ion_buffer *buffer, + int domain_num, int partition_num, unsigned long align, + unsigned long iova_length, unsigned long flags, + unsigned long *iova) +{ + struct ion_iommu_map *data; + int ret; + + data = kmalloc(sizeof(*data), GFP_ATOMIC); + + if (!data) + return ERR_PTR(-ENOMEM); + + data->buffer = buffer; + iommu_map_domain(data) = domain_num; + iommu_map_partition(data) = partition_num; + + ret = buffer->heap->ops->map_iommu(buffer, data, + domain_num, + partition_num, + align, + iova_length, + flags); + + if (ret) + goto out; + + kref_init(&data->ref); + *iova = data->iova_addr; + + ion_iommu_add(buffer, data); + + return data; + +out: + kfree(data); + return ERR_PTR(ret); +} + +int ion_map_iommu(struct ion_client *client, struct ion_handle *handle, + int domain_num, int partition_num, unsigned long align, + unsigned long iova_length, unsigned long *iova, + unsigned long *buffer_size, + unsigned long flags, unsigned long iommu_flags) +{ + struct ion_buffer *buffer; + struct ion_iommu_map *iommu_map; + int ret = 0; + + if (ION_IS_CACHED(flags)) { + pr_err("%s: Cannot map iommu as cached.\n", __func__); + return -EINVAL; + } + + mutex_lock(&client->lock); + if (!ion_handle_validate(client, handle)) { + pr_err("%s: invalid handle passed to map_kernel.\n", + __func__); + mutex_unlock(&client->lock); + return -EINVAL; + } + + buffer = handle->buffer; + mutex_lock(&buffer->lock); + + if (!handle->buffer->heap->ops->map_iommu) { + pr_err("%s: map_iommu is not implemented by this heap.\n", + __func__); + ret = -ENODEV; + goto out; + } + + /* + * If clients don't want a custom iova length, just use whatever + * the buffer size is + */ + if (!iova_length) + iova_length = buffer->size; + + if (buffer->size > iova_length) { + pr_debug("%s: iova length %lx is not at least buffer size" + " %x\n", __func__, iova_length, buffer->size); + ret = -EINVAL; + goto out; + } + + if (buffer->size & ~PAGE_MASK) { + pr_debug("%s: buffer size %x is not aligned to %lx", __func__, + buffer->size, PAGE_SIZE); + ret = -EINVAL; + goto out; + } + + if (iova_length & ~PAGE_MASK) { + pr_debug("%s: iova_length %lx is not aligned to %lx", __func__, + iova_length, PAGE_SIZE); + ret = -EINVAL; + goto out; + } + + iommu_map = ion_iommu_lookup(buffer, domain_num, partition_num); + _ion_map(&buffer->iommu_map_cnt, &handle->iommu_map_cnt); + if (!iommu_map) { + iommu_map = __ion_iommu_map(buffer, domain_num, partition_num, + align, iova_length, flags, iova); + if (IS_ERR_OR_NULL(iommu_map)) { + _ion_unmap(&buffer->iommu_map_cnt, + &handle->iommu_map_cnt); + } else { + iommu_map->flags = iommu_flags; + + if (iommu_map->flags & ION_IOMMU_UNMAP_DELAYED) + kref_get(&iommu_map->ref); + } + } else { + if (iommu_map->flags != iommu_flags) { + pr_err("%s: handle %p is already mapped with iommu flags %lx, trying to map with flags %lx\n", + __func__, handle, + iommu_map->flags, iommu_flags); + _ion_unmap(&buffer->iommu_map_cnt, + &handle->iommu_map_cnt); + ret = -EINVAL; + } else if (iommu_map->mapped_size != iova_length) { + pr_err("%s: handle %p is already mapped with length" + " %x, trying to map with length %lx\n", + __func__, handle, iommu_map->mapped_size, + iova_length); + _ion_unmap(&buffer->iommu_map_cnt, + &handle->iommu_map_cnt); + ret = -EINVAL; + } else { + kref_get(&iommu_map->ref); + *iova = iommu_map->iova_addr; + } + } + *buffer_size = buffer->size; +out: + mutex_unlock(&buffer->lock); + mutex_unlock(&client->lock); + return ret; +} +EXPORT_SYMBOL(ion_map_iommu); + +static void ion_iommu_release(struct kref *kref) +{ + struct ion_iommu_map *map = container_of(kref, struct ion_iommu_map, + ref); + struct ion_buffer *buffer = map->buffer; + + rb_erase(&map->node, &buffer->iommu_maps); + buffer->heap->ops->unmap_iommu(map); + kfree(map); +} + +void ion_unmap_iommu(struct ion_client *client, struct ion_handle *handle, + int domain_num, int partition_num) +{ + struct ion_iommu_map *iommu_map; + struct ion_buffer *buffer; + + mutex_lock(&client->lock); + buffer = handle->buffer; + + mutex_lock(&buffer->lock); + + iommu_map = ion_iommu_lookup(buffer, domain_num, partition_num); + + if (!iommu_map) { + WARN(1, "%s: (%d,%d) was never mapped for %p\n", __func__, + domain_num, partition_num, buffer); + goto out; + } + + _ion_unmap(&buffer->iommu_map_cnt, &handle->iommu_map_cnt); + kref_put(&iommu_map->ref, ion_iommu_release); + +out: + mutex_unlock(&buffer->lock); + + mutex_unlock(&client->lock); + +} +EXPORT_SYMBOL(ion_unmap_iommu); struct scatterlist *ion_map_dma(struct ion_client *client, struct ion_handle *handle, @@ -487,19 +783,6 @@ struct scatterlist *ion_map_dma(struct ion_client *client, return ERR_PTR(-ENODEV); } - if (buffer->kmap_cnt || buffer->dmap_cnt || buffer->umap_cnt) { - if (buffer->flags != flags) { - pr_err("%s: buffer was already mapped with flags %lx," - " cannot map with flags %lx\n", __func__, - buffer->flags, flags); - sglist = ERR_PTR(-EEXIST); - goto out; - } - - } else { - buffer->flags = flags; - } - if (_ion_map(&buffer->dmap_cnt, &handle->dmap_cnt)) { sglist = buffer->heap->ops->map_dma(buffer->heap, buffer); if (IS_ERR_OR_NULL(sglist)) @@ -509,11 +792,11 @@ struct scatterlist *ion_map_dma(struct ion_client *client, sglist = buffer->sglist; } -out: mutex_unlock(&buffer->lock); mutex_unlock(&client->lock); return sglist; } +EXPORT_SYMBOL(ion_map_dma); void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle) { @@ -529,6 +812,7 @@ void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle) mutex_unlock(&buffer->lock); mutex_unlock(&client->lock); } +EXPORT_SYMBOL(ion_unmap_kernel); void ion_unmap_dma(struct ion_client *client, struct ion_handle *handle) { @@ -544,7 +828,7 @@ void ion_unmap_dma(struct ion_client *client, struct ion_handle *handle) mutex_unlock(&buffer->lock); mutex_unlock(&client->lock); } - +EXPORT_SYMBOL(ion_unmap_dma); struct ion_buffer *ion_share(struct ion_client *client, struct ion_handle *handle) @@ -566,6 +850,7 @@ struct ion_buffer *ion_share(struct ion_client *client, */ return handle->buffer; } +EXPORT_SYMBOL(ion_share); struct ion_handle *ion_import(struct ion_client *client, struct ion_buffer *buffer) @@ -587,31 +872,7 @@ struct ion_handle *ion_import(struct ion_client *client, mutex_unlock(&client->lock); return handle; } - -static int check_vaddr_bounds(unsigned long start, unsigned long end) -{ - struct mm_struct *mm = current->active_mm; - struct vm_area_struct *vma; - int ret = 1; - - if (end < start) - goto out; - - down_read(&mm->mmap_sem); - vma = find_vma(mm, start); - if (vma && vma->vm_start < end) { - if (start < vma->vm_start) - goto out_up; - if (end > vma->vm_end) - goto out_up; - ret = 0; - } - -out_up: - up_read(&mm->mmap_sem); -out: - return ret; -} +EXPORT_SYMBOL(ion_import); int ion_do_cache_op(struct ion_client *client, struct ion_handle *handle, void *uaddr, unsigned long offset, unsigned long len, @@ -675,6 +936,7 @@ struct ion_handle *ion_import_fd(struct ion_client *client, int fd) fput(file); return handle; } +EXPORT_SYMBOL(ion_import_fd); static int ion_debug_client_show(struct seq_file *s, void *unused) { @@ -746,8 +1008,8 @@ struct ion_client *ion_client_create(struct ion_device *dev, struct rb_node **p; struct rb_node *parent = NULL; struct ion_client *entry; - char debug_name[64]; pid_t pid; + unsigned int name_len = strnlen(name, 64); get_task_struct(current->group_leader); task_lock(current->group_leader); @@ -781,7 +1043,16 @@ struct ion_client *ion_client_create(struct ion_device *dev, client->dev = dev; client->handles = RB_ROOT; mutex_init(&client->lock); - client->name = name; + + client->name = kzalloc(name_len+1, GFP_KERNEL); + if (!client->name) { + put_task_struct(current->group_leader); + kfree(client); + return ERR_PTR(-ENOMEM); + } else { + strlcpy(client->name, name, name_len+1); + } + client->heap_mask = heap_mask; client->task = task; client->pid = pid; @@ -816,8 +1087,8 @@ struct ion_client *ion_client_create(struct ion_device *dev, rb_insert_color(&client->node, &dev->kernel_clients); } - snprintf(debug_name, 64, "%u", client->pid); - client->debug_root = debugfs_create_file(debug_name, 0664, + + client->debug_root = debugfs_create_file(name, 0664, dev->debug_root, client, &debug_client_fops); mutex_unlock(&dev->lock); @@ -847,6 +1118,7 @@ static void _ion_client_destroy(struct kref *kref) debugfs_remove_recursive(client->debug_root); mutex_unlock(&dev->lock); + kfree(client->name); kfree(client); } @@ -865,6 +1137,7 @@ void ion_client_destroy(struct ion_client *client) if (client) ion_client_put(client); } +EXPORT_SYMBOL(ion_client_destroy); int ion_handle_get_flags(struct ion_client *client, struct ion_handle *handle, unsigned long *flags) @@ -880,7 +1153,12 @@ int ion_handle_get_flags(struct ion_client *client, struct ion_handle *handle, } buffer = handle->buffer; mutex_lock(&buffer->lock); - *flags = buffer->flags; + /* + * Make sure we only return FLAGS. buffer->flags also holds + * the heap_mask, so we need to make sure we're only looking + * at the supported Ion flags. + */ + *flags = buffer->flags & (ION_FLAG_CACHED | ION_SECURE); mutex_unlock(&buffer->lock); mutex_unlock(&client->lock); @@ -888,14 +1166,33 @@ int ion_handle_get_flags(struct ion_client *client, struct ion_handle *handle, } EXPORT_SYMBOL(ion_handle_get_flags); +int ion_handle_get_size(struct ion_client *client, struct ion_handle *handle, + unsigned long *size) +{ + struct ion_buffer *buffer; + + mutex_lock(&client->lock); + if (!ion_handle_validate(client, handle)) { + pr_err("%s: invalid handle passed to %s.\n", + __func__, __func__); + mutex_unlock(&client->lock); + return -EINVAL; + } + buffer = handle->buffer; + mutex_lock(&buffer->lock); + *size = buffer->size; + mutex_unlock(&buffer->lock); + mutex_unlock(&client->lock); + + return 0; +} +EXPORT_SYMBOL(ion_handle_get_size); + static int ion_share_release(struct inode *inode, struct file* file) { struct ion_buffer *buffer = file->private_data; pr_debug("%s: %d\n", __func__, __LINE__); - mutex_lock(&buffer->lock); - buffer->umap_cnt--; - mutex_unlock(&buffer->lock); /* drop the reference to the buffer -- this prevents the buffer from going away because the client holding it exited while it was being passed */ @@ -918,6 +1215,10 @@ static void ion_vma_open(struct vm_area_struct *vma) vma->vm_private_data = NULL; return; } + ion_handle_get(handle); + mutex_lock(&buffer->lock); + buffer->umap_cnt++; + mutex_unlock(&buffer->lock); pr_debug("%s: %d client_cnt %d handle_cnt %d alloc_cnt %d\n", __func__, __LINE__, atomic_read(&client->ref.refcount), @@ -936,12 +1237,22 @@ static void ion_vma_close(struct vm_area_struct *vma) if (!handle) return; client = handle->client; + mutex_lock(&buffer->lock); + buffer->umap_cnt--; + mutex_unlock(&buffer->lock); + + if (buffer->heap->ops->unmap_user) + buffer->heap->ops->unmap_user(buffer->heap, buffer); + + pr_debug("%s: %d client_cnt %d handle_cnt %d alloc_cnt %d\n", __func__, __LINE__, atomic_read(&client->ref.refcount), atomic_read(&handle->ref.refcount), atomic_read(&buffer->ref.refcount)); + mutex_lock(&client->lock); ion_handle_put(handle); + mutex_unlock(&client->lock); ion_client_put(client); pr_debug("%s: %d client_cnt %d handle_cnt %d alloc_cnt %d\n", __func__, __LINE__, @@ -962,10 +1273,7 @@ static int ion_share_mmap(struct file *file, struct vm_area_struct *vma) struct ion_client *client; struct ion_handle *handle; int ret; - unsigned long flags = file->f_flags & O_DSYNC ? - ION_SET_CACHE(UNCACHED) : - ION_SET_CACHE(CACHED); - + unsigned long flags = buffer->flags; pr_debug("%s: %d\n", __func__, __LINE__); /* make sure the client still exists, it's possible for the client to @@ -1001,29 +1309,18 @@ static int ion_share_mmap(struct file *file, struct vm_area_struct *vma) } mutex_lock(&buffer->lock); - if (buffer->kmap_cnt || buffer->dmap_cnt || buffer->umap_cnt) { - if (buffer->flags != flags) { - pr_err("%s: buffer was already mapped with flags %lx," - " cannot map with flags %lx\n", __func__, - buffer->flags, flags); - ret = -EEXIST; - mutex_unlock(&buffer->lock); - goto err1; - } - } else { - buffer->flags = flags; - } /* now map it to userspace */ ret = buffer->heap->ops->map_user(buffer->heap, buffer, vma, flags); + buffer->umap_cnt++; - mutex_unlock(&buffer->lock); if (ret) { pr_err("%s: failure mapping buffer to userspace\n", __func__); goto err2; } + mutex_unlock(&buffer->lock); vma->vm_ops = &ion_vm_ops; /* move the handle into the vm_private_data so we can access it from @@ -1038,9 +1335,12 @@ static int ion_share_mmap(struct file *file, struct vm_area_struct *vma) err2: buffer->umap_cnt--; + mutex_unlock(&buffer->lock); /* drop the reference to the handle */ err1: + mutex_lock(&client->lock); ion_handle_put(handle); + mutex_unlock(&client->lock); err: /* drop the reference to the client */ ion_client_put(client); @@ -1067,9 +1367,6 @@ static int ion_ioctl_share(struct file *parent, struct ion_client *client, if (IS_ERR_OR_NULL(file)) goto err; - if (parent->f_flags & O_DSYNC) - file->f_flags |= O_DSYNC; - ion_buffer_get(handle->buffer); fd_install(fd, file); @@ -1091,11 +1388,12 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (copy_from_user(&data, (void __user *)arg, sizeof(data))) return -EFAULT; + data.flags |= data.heap_mask; data.handle = ion_alloc(client, data.len, data.align, data.flags); if (IS_ERR_OR_NULL(data.handle)) - return PTR_ERR(data.handle); + return -ENOMEM; if (copy_to_user((void __user *)arg, &data, sizeof(data))) return -EFAULT; @@ -1167,43 +1465,8 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case ION_IOC_CLEAN_CACHES: case ION_IOC_INV_CACHES: case ION_IOC_CLEAN_INV_CACHES: - { - struct ion_flush_data data; - unsigned long start, end; - - if (copy_from_user(&data, (void __user *)arg, - sizeof(struct ion_flush_data))) - return -EFAULT; - - start = (unsigned long) data.vaddr; - end = (unsigned long) data.vaddr + data.length; - - if (check_vaddr_bounds(start, end)) { - pr_err("%s: virtual address %p is out of bounds\n", - __func__, data.vaddr); - return -EINVAL; - } - - return ion_do_cache_op(client, data.handle, data.vaddr, - data.offset, data.length, cmd); - - } case ION_IOC_GET_FLAGS: - { - struct ion_flag_data data; - int ret; - if (copy_from_user(&data, (void __user *)arg, - sizeof(struct ion_flag_data))) - return -EFAULT; - - ret = ion_handle_get_flags(client, data.handle, &data.flags); - if (ret < 0) - return ret; - if (copy_to_user((void __user *)arg, &data, - sizeof(struct ion_flag_data))) - return -EFAULT; - break; - } + return client->dev->custom_ioctl(client, cmd, arg); default: return -ENOTTY; } @@ -1224,9 +1487,11 @@ static int ion_open(struct inode *inode, struct file *file) struct miscdevice *miscdev = file->private_data; struct ion_device *dev = container_of(miscdev, struct ion_device, dev); struct ion_client *client; + char debug_name[64]; pr_debug("%s: %d\n", __func__, __LINE__); - client = ion_client_create(dev, -1, "user"); + snprintf(debug_name, 64, "%u", task_pid_nr(current->group_leader)); + client = ion_client_create(dev, -1, debug_name); if (IS_ERR_OR_NULL(client)) return PTR_ERR(client); file->private_data = client; @@ -1288,14 +1553,8 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused) seq_printf(s, "%16.s %16u %16x\n", client->name, client->pid, size); } - if (heap->ops->get_allocated) { - seq_printf(s, "total bytes currently allocated: %lx\n", - heap->ops->get_allocated(heap)); - } - if (heap->ops->get_total) { - seq_printf(s, "total heap size: %lx\n", - heap->ops->get_total(heap)); - } + if (heap->ops->print_debug) + heap->ops->print_debug(heap, s); return 0; } @@ -1342,6 +1601,58 @@ void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap) mutex_unlock(&dev->lock); } +int ion_secure_heap(struct ion_device *dev, int heap_id) +{ + struct rb_node *n; + int ret_val = 0; + + /* + * traverse the list of heaps available in this system + * and find the heap that is specified. + */ + mutex_lock(&dev->lock); + for (n = rb_first(&dev->heaps); n != NULL; n = rb_next(n)) { + struct ion_heap *heap = rb_entry(n, struct ion_heap, node); + if (heap->type != ION_HEAP_TYPE_CP) + continue; + if (ION_HEAP(heap->id) != heap_id) + continue; + if (heap->ops->secure_heap) + ret_val = heap->ops->secure_heap(heap); + else + ret_val = -EINVAL; + break; + } + mutex_unlock(&dev->lock); + return ret_val; +} + +int ion_unsecure_heap(struct ion_device *dev, int heap_id) +{ + struct rb_node *n; + int ret_val = 0; + + /* + * traverse the list of heaps available in this system + * and find the heap that is specified. + */ + mutex_lock(&dev->lock); + for (n = rb_first(&dev->heaps); n != NULL; n = rb_next(n)) { + struct ion_heap *heap = rb_entry(n, struct ion_heap, node); + if (heap->type != ION_HEAP_TYPE_CP) + continue; + if (ION_HEAP(heap->id) != heap_id) + continue; + if (heap->ops->secure_heap) + ret_val = heap->ops->unsecure_heap(heap); + else + ret_val = -EINVAL; + break; + } + mutex_unlock(&dev->lock); + return ret_val; +} + static int ion_debug_leak_show(struct seq_file *s, void *unused) { struct ion_device *dev = s->private; diff --git a/drivers/gpu/ion/ion_carveout_heap.c b/drivers/gpu/ion/ion_carveout_heap.c index 71dea89674e78..cc283d53c38ff 100644 --- a/drivers/gpu/ion/ion_carveout_heap.c +++ b/drivers/gpu/ion/ion_carveout_heap.c @@ -2,6 +2,7 @@ * drivers/gpu/ion/ion_carveout_heap.c * * Copyright (C) 2011 Google, Inc. + * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -23,9 +24,13 @@ #include #include #include +#include +#include #include "ion_priv.h" +#include #include +#include struct ion_carveout_heap { struct ion_heap heap; @@ -33,6 +38,11 @@ struct ion_carveout_heap { ion_phys_addr_t base; unsigned long allocated_bytes; unsigned long total_size; + int (*request_region)(void *); + int (*release_region)(void *); + atomic_t map_count; + void *bus_id; + unsigned int has_outer_cache; }; ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap, @@ -102,17 +112,15 @@ struct scatterlist *ion_carveout_heap_map_dma(struct ion_heap *heap, struct ion_buffer *buffer) { struct scatterlist *sglist; - struct page *page = phys_to_page(buffer->priv_phys); - - if (page == NULL) - return NULL; sglist = vmalloc(sizeof(struct scatterlist)); if (!sglist) return ERR_PTR(-ENOMEM); sg_init_table(sglist, 1); - sg_set_page(sglist, page, buffer->size, 0); + sglist->length = buffer->size; + sglist->offset = 0; + sglist->dma_address = buffer->priv_phys; return sglist; } @@ -124,79 +132,247 @@ void ion_carveout_heap_unmap_dma(struct ion_heap *heap, vfree(buffer->sglist); } +static int ion_carveout_request_region(struct ion_carveout_heap *carveout_heap) +{ + int ret_value = 0; + if (atomic_inc_return(&carveout_heap->map_count) == 1) { + if (carveout_heap->request_region) { + ret_value = carveout_heap->request_region( + carveout_heap->bus_id); + if (ret_value) { + pr_err("Unable to request SMI region"); + atomic_dec(&carveout_heap->map_count); + } + } + } + return ret_value; +} + +static int ion_carveout_release_region(struct ion_carveout_heap *carveout_heap) +{ + int ret_value = 0; + if (atomic_dec_and_test(&carveout_heap->map_count)) { + if (carveout_heap->release_region) { + ret_value = carveout_heap->release_region( + carveout_heap->bus_id); + if (ret_value) + pr_err("Unable to release SMI region"); + } + } + return ret_value; +} + void *ion_carveout_heap_map_kernel(struct ion_heap *heap, struct ion_buffer *buffer, unsigned long flags) { + struct ion_carveout_heap *carveout_heap = + container_of(heap, struct ion_carveout_heap, heap); + void *ret_value; + + if (ion_carveout_request_region(carveout_heap)) + return NULL; + if (ION_IS_CACHED(flags)) - return ioremap_cached(buffer->priv_phys, buffer->size); + ret_value = ioremap_cached(buffer->priv_phys, buffer->size); else - return ioremap(buffer->priv_phys, buffer->size); + ret_value = ioremap(buffer->priv_phys, buffer->size); + + if (!ret_value) + ion_carveout_release_region(carveout_heap); + return ret_value; } void ion_carveout_heap_unmap_kernel(struct ion_heap *heap, struct ion_buffer *buffer) { + struct ion_carveout_heap *carveout_heap = + container_of(heap, struct ion_carveout_heap, heap); + __arch_iounmap(buffer->vaddr); buffer->vaddr = NULL; + + ion_carveout_release_region(carveout_heap); return; } int ion_carveout_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, struct vm_area_struct *vma, unsigned long flags) { - if (ION_IS_CACHED(flags)) - return remap_pfn_range(vma, vma->vm_start, - __phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff, - buffer->size, - vma->vm_page_prot); - else - return remap_pfn_range(vma, vma->vm_start, - __phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff, - buffer->size, - pgprot_noncached(vma->vm_page_prot)); + struct ion_carveout_heap *carveout_heap = + container_of(heap, struct ion_carveout_heap, heap); + int ret_value = 0; + + if (ion_carveout_request_region(carveout_heap)) + return -EINVAL; + + if (!ION_IS_CACHED(flags)) + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + ret_value = remap_pfn_range(vma, vma->vm_start, + __phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); + + if (ret_value) + ion_carveout_release_region(carveout_heap); + return ret_value; +} + +void ion_carveout_heap_unmap_user(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + struct ion_carveout_heap *carveout_heap = + container_of(heap, struct ion_carveout_heap, heap); + ion_carveout_release_region(carveout_heap); } int ion_carveout_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer, void *vaddr, unsigned int offset, unsigned int length, unsigned int cmd) { - unsigned long vstart, pstart; - - pstart = buffer->priv_phys + offset; - vstart = (unsigned long)vaddr; + void (*outer_cache_op)(phys_addr_t, phys_addr_t); + struct ion_carveout_heap *carveout_heap = + container_of(heap, struct ion_carveout_heap, heap); switch (cmd) { case ION_IOC_CLEAN_CACHES: - clean_caches(vstart, length, pstart); + dmac_clean_range(vaddr, vaddr + length); + outer_cache_op = outer_clean_range; break; case ION_IOC_INV_CACHES: - invalidate_caches(vstart, length, pstart); + dmac_inv_range(vaddr, vaddr + length); + outer_cache_op = outer_inv_range; break; case ION_IOC_CLEAN_INV_CACHES: - clean_and_invalidate_caches(vstart, length, pstart); + dmac_flush_range(vaddr, vaddr + length); + outer_cache_op = outer_flush_range; break; default: return -EINVAL; } + if (carveout_heap->has_outer_cache) { + unsigned long pstart = buffer->priv_phys + offset; + outer_cache_op(pstart, pstart + length); + } return 0; } -static unsigned long ion_carveout_get_allocated(struct ion_heap *heap) +static int ion_carveout_print_debug(struct ion_heap *heap, struct seq_file *s) { struct ion_carveout_heap *carveout_heap = container_of(heap, struct ion_carveout_heap, heap); - return carveout_heap->allocated_bytes; + seq_printf(s, "total bytes currently allocated: %lx\n", + carveout_heap->allocated_bytes); + seq_printf(s, "total heap size: %lx\n", carveout_heap->total_size); + + return 0; } -static unsigned long ion_carveout_get_total(struct ion_heap *heap) +int ion_carveout_heap_map_iommu(struct ion_buffer *buffer, + struct ion_iommu_map *data, + unsigned int domain_num, + unsigned int partition_num, + unsigned long align, + unsigned long iova_length, + unsigned long flags) { - struct ion_carveout_heap *carveout_heap = - container_of(heap, struct ion_carveout_heap, heap); + struct iommu_domain *domain; + int ret = 0; + unsigned long extra; + struct scatterlist *sglist = 0; + int prot = IOMMU_WRITE | IOMMU_READ; + prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0; + + data->mapped_size = iova_length; + + if (!msm_use_iommu()) { + data->iova_addr = buffer->priv_phys; + return 0; + } + + extra = iova_length - buffer->size; + + data->iova_addr = msm_allocate_iova_address(domain_num, partition_num, + data->mapped_size, align); + + if (!data->iova_addr) { + ret = -ENOMEM; + goto out; + } - return carveout_heap->total_size; + domain = msm_get_iommu_domain(domain_num); + + if (!domain) { + ret = -ENOMEM; + goto out1; + } + + sglist = vmalloc(sizeof(*sglist)); + if (!sglist) + goto out1; + + sg_init_table(sglist, 1); + sglist->length = buffer->size; + sglist->offset = 0; + sglist->dma_address = buffer->priv_phys; + + ret = iommu_map_range(domain, data->iova_addr, sglist, + buffer->size, prot); + if (ret) { + pr_err("%s: could not map %lx in domain %p\n", + __func__, data->iova_addr, domain); + goto out1; + } + + if (extra) { + unsigned long extra_iova_addr = data->iova_addr + buffer->size; + ret = msm_iommu_map_extra(domain, extra_iova_addr, extra, + SZ_4K, prot); + if (ret) + goto out2; + } + vfree(sglist); + return ret; + +out2: + iommu_unmap_range(domain, data->iova_addr, buffer->size); +out1: + vfree(sglist); + msm_free_iova_address(data->iova_addr, domain_num, partition_num, + data->mapped_size); + +out: + + return ret; +} + +void ion_carveout_heap_unmap_iommu(struct ion_iommu_map *data) +{ + unsigned int domain_num; + unsigned int partition_num; + struct iommu_domain *domain; + + if (!msm_use_iommu()) + return; + + domain_num = iommu_map_domain(data); + partition_num = iommu_map_partition(data); + + domain = msm_get_iommu_domain(domain_num); + + if (!domain) { + WARN(1, "Could not get domain %d. Corruption?\n", domain_num); + return; + } + + iommu_unmap_range(domain, data->iova_addr, data->mapped_size); + msm_free_iova_address(data->iova_addr, domain_num, partition_num, + data->mapped_size); + + return; } static struct ion_heap_ops carveout_heap_ops = { @@ -205,12 +381,14 @@ static struct ion_heap_ops carveout_heap_ops = { .phys = ion_carveout_heap_phys, .map_user = ion_carveout_heap_map_user, .map_kernel = ion_carveout_heap_map_kernel, + .unmap_user = ion_carveout_heap_unmap_user, .unmap_kernel = ion_carveout_heap_unmap_kernel, .map_dma = ion_carveout_heap_map_dma, .unmap_dma = ion_carveout_heap_unmap_dma, .cache_op = ion_carveout_cache_ops, - .get_allocated = ion_carveout_get_allocated, - .get_total = ion_carveout_get_total, + .print_debug = ion_carveout_print_debug, + .map_iommu = ion_carveout_heap_map_iommu, + .unmap_iommu = ion_carveout_heap_unmap_iommu, }; struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data) @@ -231,6 +409,7 @@ struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data) ret = gen_pool_add(carveout_heap->pool, carveout_heap->base, heap_data->size, -1); if (ret < 0) { + gen_pool_destroy(carveout_heap->pool); kfree(carveout_heap); return ERR_PTR(-EINVAL); } @@ -238,7 +417,21 @@ struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data) carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT; carveout_heap->allocated_bytes = 0; carveout_heap->total_size = heap_data->size; - + carveout_heap->has_outer_cache = heap_data->has_outer_cache; + + if (heap_data->extra_data) { + struct ion_co_heap_pdata *extra_data = + heap_data->extra_data; + + if (extra_data->setup_region) + carveout_heap->bus_id = extra_data->setup_region(); + if (extra_data->request_region) + carveout_heap->request_region = + extra_data->request_region; + if (extra_data->release_region) + carveout_heap->release_region = + extra_data->release_region; + } return &carveout_heap->heap; } diff --git a/drivers/gpu/ion/ion_cp_heap.c b/drivers/gpu/ion/ion_cp_heap.c new file mode 100644 index 0000000000000..ced6fe5fda954 --- /dev/null +++ b/drivers/gpu/ion/ion_cp_heap.c @@ -0,0 +1,978 @@ +/* + * drivers/gpu/ion/ion_cp_heap.c + * + * Copyright (C) 2011 Google, Inc. + * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * 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 "ion_priv.h" + +#include +#include + +/** + * struct ion_cp_heap - container for the heap and shared heap data + + * @heap: the heap information structure + * @pool: memory pool to allocate from. + * @base: the base address of the memory pool. + * @permission_type: Identifier for the memory used by SCM for protecting + * and unprotecting memory. + * @secure_base: Base address used when securing a heap that is shared. + * @secure_size: Size used when securing a heap that is shared. + * @lock: mutex to protect shared access. + * @heap_protected: Indicates whether heap has been protected or not. + * @allocated_bytes: the total number of allocated bytes from the pool. + * @total_size: the total size of the memory pool. + * @request_region: function pointer to call when first mapping of memory + * occurs. + * @release_region: function pointer to call when last mapping of memory + * unmapped. + * @bus_id: token used with request/release region. + * @kmap_cached_count: the total number of times this heap has been mapped in + * kernel space (cached). + * @kmap_uncached_count:the total number of times this heap has been mapped in + * kernel space (un-cached). + * @umap_count: the total number of times this heap has been mapped in + * user space. + * @iommu_iova: saved iova when mapping full heap at once. + * @iommu_partition: partition used to map full heap. + * @reusable: indicates if the memory should be reused via fmem. + * @reserved_vrange: reserved virtual address range for use with fmem + * @iommu_map_all: Indicates whether we should map whole heap into IOMMU. + * @iommu_2x_map_domain: Indicates the domain to use for overmapping. + * @has_outer_cache: set to 1 if outer cache is used, 0 otherwise. +*/ +struct ion_cp_heap { + struct ion_heap heap; + struct gen_pool *pool; + ion_phys_addr_t base; + unsigned int permission_type; + ion_phys_addr_t secure_base; + size_t secure_size; + struct mutex lock; + unsigned int heap_protected; + unsigned long allocated_bytes; + unsigned long total_size; + int (*request_region)(void *); + int (*release_region)(void *); + void *bus_id; + unsigned long kmap_cached_count; + unsigned long kmap_uncached_count; + unsigned long umap_count; + unsigned long iommu_iova[MAX_DOMAINS]; + unsigned long iommu_partition[MAX_DOMAINS]; + int reusable; + void *reserved_vrange; + int iommu_map_all; + int iommu_2x_map_domain; + unsigned int has_outer_cache; + atomic_t protect_cnt; +}; + +enum { + HEAP_NOT_PROTECTED = 0, + HEAP_PROTECTED = 1, +}; + +static int ion_cp_protect_mem(unsigned int phy_base, unsigned int size, + unsigned int permission_type); + +static int ion_cp_unprotect_mem(unsigned int phy_base, unsigned int size, + unsigned int permission_type); + +/** + * Get the total number of kernel mappings. + * Must be called with heap->lock locked. + */ +static unsigned long ion_cp_get_total_kmap_count( + const struct ion_cp_heap *cp_heap) +{ + return cp_heap->kmap_cached_count + cp_heap->kmap_uncached_count; +} + +/** + * Protects memory if heap is unsecured heap. Also ensures that we are in + * the correct FMEM state if this heap is a reusable heap. + * Must be called with heap->lock locked. + */ +static int ion_cp_protect(struct ion_heap *heap) +{ + struct ion_cp_heap *cp_heap = + container_of(heap, struct ion_cp_heap, heap); + int ret_value = 0; + + if (atomic_inc_return(&cp_heap->protect_cnt) == 1) { + /* Make sure we are in C state when the heap is protected. */ + if (cp_heap->reusable && !cp_heap->allocated_bytes) { + ret_value = fmem_set_state(FMEM_C_STATE); + if (ret_value) + goto out; + } + + ret_value = ion_cp_protect_mem(cp_heap->secure_base, + cp_heap->secure_size, cp_heap->permission_type); + if (ret_value) { + pr_err("Failed to protect memory for heap %s - " + "error code: %d\n", heap->name, ret_value); + + if (cp_heap->reusable && !cp_heap->allocated_bytes) { + if (fmem_set_state(FMEM_T_STATE) != 0) + pr_err("%s: unable to transition heap to T-state\n", + __func__); + } + atomic_dec(&cp_heap->protect_cnt); + } else { + cp_heap->heap_protected = HEAP_PROTECTED; + pr_debug("Protected heap %s @ 0x%lx\n", + heap->name, cp_heap->base); + } + } +out: + pr_debug("%s: protect count is %d\n", __func__, + atomic_read(&cp_heap->protect_cnt)); + BUG_ON(atomic_read(&cp_heap->protect_cnt) < 0); + return ret_value; +} + +/** + * Unprotects memory if heap is secure heap. Also ensures that we are in + * the correct FMEM state if this heap is a reusable heap. + * Must be called with heap->lock locked. + */ +static void ion_cp_unprotect(struct ion_heap *heap) +{ + struct ion_cp_heap *cp_heap = + container_of(heap, struct ion_cp_heap, heap); + + if (atomic_dec_and_test(&cp_heap->protect_cnt)) { + int error_code = ion_cp_unprotect_mem( + cp_heap->secure_base, cp_heap->secure_size, + cp_heap->permission_type); + if (error_code) { + pr_err("Failed to un-protect memory for heap %s - " + "error code: %d\n", heap->name, error_code); + } else { + cp_heap->heap_protected = HEAP_NOT_PROTECTED; + pr_debug("Un-protected heap %s @ 0x%x\n", heap->name, + (unsigned int) cp_heap->base); + + if (cp_heap->reusable && !cp_heap->allocated_bytes) { + if (fmem_set_state(FMEM_T_STATE) != 0) + pr_err("%s: unable to transition heap to T-state", + __func__); + } + } + } + pr_debug("%s: protect count is %d\n", __func__, + atomic_read(&cp_heap->protect_cnt)); + BUG_ON(atomic_read(&cp_heap->protect_cnt) < 0); +} + +ion_phys_addr_t ion_cp_allocate(struct ion_heap *heap, + unsigned long size, + unsigned long align, + unsigned long flags) +{ + unsigned long offset; + unsigned long secure_allocation = flags & ION_SECURE; + + struct ion_cp_heap *cp_heap = + container_of(heap, struct ion_cp_heap, heap); + + mutex_lock(&cp_heap->lock); + if (!secure_allocation && cp_heap->heap_protected == HEAP_PROTECTED) { + mutex_unlock(&cp_heap->lock); + pr_err("ION cannot allocate un-secure memory from protected" + " heap %s\n", heap->name); + return ION_CP_ALLOCATE_FAIL; + } + + if (secure_allocation && + (cp_heap->umap_count > 0 || cp_heap->kmap_cached_count > 0)) { + mutex_unlock(&cp_heap->lock); + pr_err("ION cannot allocate secure memory from heap with " + "outstanding mappings: User space: %lu, kernel space " + "(cached): %lu\n", cp_heap->umap_count, + cp_heap->kmap_cached_count); + return ION_CP_ALLOCATE_FAIL; + } + + /* + * if this is the first reusable allocation, transition + * the heap + */ + if (cp_heap->reusable && !cp_heap->allocated_bytes) { + if (fmem_set_state(FMEM_C_STATE) != 0) { + mutex_unlock(&cp_heap->lock); + return ION_RESERVED_ALLOCATE_FAIL; + } + } + + cp_heap->allocated_bytes += size; + mutex_unlock(&cp_heap->lock); + + offset = gen_pool_alloc_aligned(cp_heap->pool, + size, ilog2(align)); + + if (!offset) { + mutex_lock(&cp_heap->lock); + cp_heap->allocated_bytes -= size; + if ((cp_heap->total_size - + cp_heap->allocated_bytes) >= size) + pr_debug("%s: heap %s has enough memory (%lx) but" + " the allocation of size %lx still failed." + " Memory is probably fragmented.\n", + __func__, heap->name, + cp_heap->total_size - + cp_heap->allocated_bytes, size); + + if (cp_heap->reusable && !cp_heap->allocated_bytes && + cp_heap->heap_protected == HEAP_NOT_PROTECTED) { + if (fmem_set_state(FMEM_T_STATE) != 0) + pr_err("%s: unable to transition heap to T-state\n", + __func__); + } + mutex_unlock(&cp_heap->lock); + + return ION_CP_ALLOCATE_FAIL; + } + + return offset; +} + +static void iommu_unmap_all(unsigned long domain_num, + struct ion_cp_heap *cp_heap) +{ + unsigned long left_to_unmap = cp_heap->total_size; + unsigned long order = get_order(SZ_64K); + unsigned long page_size = SZ_64K; + + struct iommu_domain *domain = msm_get_iommu_domain(domain_num); + if (domain) { + unsigned long temp_iova = cp_heap->iommu_iova[domain_num]; + + while (left_to_unmap) { + iommu_unmap(domain, temp_iova, order); + temp_iova += page_size; + left_to_unmap -= page_size; + } + if (domain_num == cp_heap->iommu_2x_map_domain) + msm_iommu_unmap_extra(domain, temp_iova, + cp_heap->total_size, SZ_64K); + } else { + pr_err("Unable to get IOMMU domain %lu\n", domain_num); + } +} + +void ion_cp_free(struct ion_heap *heap, ion_phys_addr_t addr, + unsigned long size) +{ + struct ion_cp_heap *cp_heap = + container_of(heap, struct ion_cp_heap, heap); + + if (addr == ION_CP_ALLOCATE_FAIL) + return; + gen_pool_free(cp_heap->pool, addr, size); + + mutex_lock(&cp_heap->lock); + cp_heap->allocated_bytes -= size; + + if (cp_heap->reusable && !cp_heap->allocated_bytes && + cp_heap->heap_protected == HEAP_NOT_PROTECTED) { + if (fmem_set_state(FMEM_T_STATE) != 0) + pr_err("%s: unable to transition heap to T-state\n", + __func__); + } + + /* Unmap everything if we previously mapped the whole heap at once. */ + if (!cp_heap->allocated_bytes) { + unsigned int i; + for (i = 0; i < MAX_DOMAINS; ++i) { + if (cp_heap->iommu_iova[i]) { + unsigned long vaddr_len = cp_heap->total_size; + + if (i == cp_heap->iommu_2x_map_domain) + vaddr_len <<= 1; + iommu_unmap_all(i, cp_heap); + + msm_free_iova_address(cp_heap->iommu_iova[i], i, + cp_heap->iommu_partition[i], + vaddr_len); + } + cp_heap->iommu_iova[i] = 0; + cp_heap->iommu_partition[i] = 0; + } + } + mutex_unlock(&cp_heap->lock); +} + +static int ion_cp_heap_phys(struct ion_heap *heap, + struct ion_buffer *buffer, + ion_phys_addr_t *addr, size_t *len) +{ + *addr = buffer->priv_phys; + *len = buffer->size; + return 0; +} + +static int ion_cp_heap_allocate(struct ion_heap *heap, + struct ion_buffer *buffer, + unsigned long size, unsigned long align, + unsigned long flags) +{ + buffer->priv_phys = ion_cp_allocate(heap, size, align, flags); + return buffer->priv_phys == ION_CP_ALLOCATE_FAIL ? -ENOMEM : 0; +} + +static void ion_cp_heap_free(struct ion_buffer *buffer) +{ + struct ion_heap *heap = buffer->heap; + + ion_cp_free(heap, buffer->priv_phys, buffer->size); + buffer->priv_phys = ION_CP_ALLOCATE_FAIL; +} + +struct scatterlist *ion_cp_heap_create_sglist(struct ion_buffer *buffer) +{ + struct scatterlist *sglist; + + sglist = vmalloc(sizeof(*sglist)); + if (!sglist) + return ERR_PTR(-ENOMEM); + + sg_init_table(sglist, 1); + sglist->length = buffer->size; + sglist->offset = 0; + sglist->dma_address = buffer->priv_phys; + + return sglist; +} + +struct scatterlist *ion_cp_heap_map_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + return ion_cp_heap_create_sglist(buffer); +} + +void ion_cp_heap_unmap_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + if (buffer->sglist) + vfree(buffer->sglist); +} + +/** + * Call request region for SMI memory of this is the first mapping. + */ +static int ion_cp_request_region(struct ion_cp_heap *cp_heap) +{ + int ret_value = 0; + if ((cp_heap->umap_count + ion_cp_get_total_kmap_count(cp_heap)) == 0) + if (cp_heap->request_region) + ret_value = cp_heap->request_region(cp_heap->bus_id); + return ret_value; +} + +/** + * Call release region for SMI memory of this is the last un-mapping. + */ +static int ion_cp_release_region(struct ion_cp_heap *cp_heap) +{ + int ret_value = 0; + if ((cp_heap->umap_count + ion_cp_get_total_kmap_count(cp_heap)) == 0) + if (cp_heap->release_region) + ret_value = cp_heap->release_region(cp_heap->bus_id); + return ret_value; +} + +void *ion_map_fmem_buffer(struct ion_buffer *buffer, unsigned long phys_base, + void *virt_base, unsigned long flags) +{ + int ret; + unsigned int offset = buffer->priv_phys - phys_base; + unsigned long start = ((unsigned long)virt_base) + offset; + const struct mem_type *type = ION_IS_CACHED(flags) ? + get_mem_type(MT_DEVICE_CACHED) : + get_mem_type(MT_DEVICE); + + if (phys_base > buffer->priv_phys) + return NULL; + + + ret = ioremap_page_range(start, start + buffer->size, + buffer->priv_phys, __pgprot(type->prot_pte)); + + if (!ret) + return (void *)start; + else + return NULL; +} + +void *ion_cp_heap_map_kernel(struct ion_heap *heap, + struct ion_buffer *buffer, + unsigned long flags) +{ + struct ion_cp_heap *cp_heap = + container_of(heap, struct ion_cp_heap, heap); + void *ret_value = NULL; + + mutex_lock(&cp_heap->lock); + if ((cp_heap->heap_protected == HEAP_NOT_PROTECTED) || + ((cp_heap->heap_protected == HEAP_PROTECTED) && + !ION_IS_CACHED(flags))) { + + if (ion_cp_request_region(cp_heap)) { + mutex_unlock(&cp_heap->lock); + return NULL; + } + + if (cp_heap->reusable) { + ret_value = ion_map_fmem_buffer(buffer, cp_heap->base, + cp_heap->reserved_vrange, flags); + + } else { + if (ION_IS_CACHED(flags)) + ret_value = ioremap_cached(buffer->priv_phys, + buffer->size); + else + ret_value = ioremap(buffer->priv_phys, + buffer->size); + } + + if (!ret_value) { + ion_cp_release_region(cp_heap); + } else { + if (ION_IS_CACHED(buffer->flags)) + ++cp_heap->kmap_cached_count; + else + ++cp_heap->kmap_uncached_count; + } + } + mutex_unlock(&cp_heap->lock); + return ret_value; +} + +void ion_cp_heap_unmap_kernel(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + struct ion_cp_heap *cp_heap = + container_of(heap, struct ion_cp_heap, heap); + + if (cp_heap->reusable) + unmap_kernel_range((unsigned long)buffer->vaddr, buffer->size); + else + __arch_iounmap(buffer->vaddr); + + buffer->vaddr = NULL; + + mutex_lock(&cp_heap->lock); + if (ION_IS_CACHED(buffer->flags)) + --cp_heap->kmap_cached_count; + else + --cp_heap->kmap_uncached_count; + ion_cp_release_region(cp_heap); + mutex_unlock(&cp_heap->lock); + + return; +} + +int ion_cp_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, + struct vm_area_struct *vma, unsigned long flags) +{ + int ret_value = -EAGAIN; + struct ion_cp_heap *cp_heap = + container_of(heap, struct ion_cp_heap, heap); + + mutex_lock(&cp_heap->lock); + if (cp_heap->heap_protected == HEAP_NOT_PROTECTED) { + if (ion_cp_request_region(cp_heap)) { + mutex_unlock(&cp_heap->lock); + return -EINVAL; + } + + if (!ION_IS_CACHED(flags)) + vma->vm_page_prot = pgprot_writecombine( + vma->vm_page_prot); + + ret_value = remap_pfn_range(vma, vma->vm_start, + __phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); + + if (ret_value) + ion_cp_release_region(cp_heap); + else + ++cp_heap->umap_count; + } + mutex_unlock(&cp_heap->lock); + return ret_value; +} + +void ion_cp_heap_unmap_user(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + struct ion_cp_heap *cp_heap = + container_of(heap, struct ion_cp_heap, heap); + + mutex_lock(&cp_heap->lock); + --cp_heap->umap_count; + ion_cp_release_region(cp_heap); + mutex_unlock(&cp_heap->lock); +} + +int ion_cp_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer, + void *vaddr, unsigned int offset, unsigned int length, + unsigned int cmd) +{ + void (*outer_cache_op)(phys_addr_t, phys_addr_t); + struct ion_cp_heap *cp_heap = + container_of(heap, struct ion_cp_heap, heap); + + switch (cmd) { + case ION_IOC_CLEAN_CACHES: + dmac_clean_range(vaddr, vaddr + length); + outer_cache_op = outer_clean_range; + break; + case ION_IOC_INV_CACHES: + dmac_inv_range(vaddr, vaddr + length); + outer_cache_op = outer_inv_range; + break; + case ION_IOC_CLEAN_INV_CACHES: + dmac_flush_range(vaddr, vaddr + length); + outer_cache_op = outer_flush_range; + break; + default: + return -EINVAL; + } + + if (cp_heap->has_outer_cache) { + unsigned long pstart = buffer->priv_phys + offset; + outer_cache_op(pstart, pstart + length); + } + return 0; +} + +static int ion_cp_print_debug(struct ion_heap *heap, struct seq_file *s) +{ + unsigned long total_alloc; + unsigned long total_size; + unsigned long umap_count; + unsigned long kmap_count; + unsigned long heap_protected; + struct ion_cp_heap *cp_heap = + container_of(heap, struct ion_cp_heap, heap); + + mutex_lock(&cp_heap->lock); + total_alloc = cp_heap->allocated_bytes; + total_size = cp_heap->total_size; + umap_count = cp_heap->umap_count; + kmap_count = ion_cp_get_total_kmap_count(cp_heap); + heap_protected = cp_heap->heap_protected == HEAP_PROTECTED; + mutex_unlock(&cp_heap->lock); + + seq_printf(s, "total bytes currently allocated: %lx\n", total_alloc); + seq_printf(s, "total heap size: %lx\n", total_size); + seq_printf(s, "umapping count: %lx\n", umap_count); + seq_printf(s, "kmapping count: %lx\n", kmap_count); + seq_printf(s, "heap protected: %s\n", heap_protected ? "Yes" : "No"); + seq_printf(s, "reusable: %s\n", cp_heap->reusable ? "Yes" : "No"); + + return 0; +} + +int ion_cp_secure_heap(struct ion_heap *heap) +{ + int ret_value; + struct ion_cp_heap *cp_heap = + container_of(heap, struct ion_cp_heap, heap); + mutex_lock(&cp_heap->lock); + if (cp_heap->umap_count == 0 && cp_heap->kmap_cached_count == 0) { + ret_value = ion_cp_protect(heap); + } else { + pr_err("ION cannot secure heap with outstanding mappings: " + "User space: %lu, kernel space (cached): %lu\n", + cp_heap->umap_count, cp_heap->kmap_cached_count); + ret_value = -EINVAL; + } + + mutex_unlock(&cp_heap->lock); + return ret_value; +} + +int ion_cp_unsecure_heap(struct ion_heap *heap) +{ + int ret_value = 0; + struct ion_cp_heap *cp_heap = + container_of(heap, struct ion_cp_heap, heap); + mutex_lock(&cp_heap->lock); + ion_cp_unprotect(heap); + mutex_unlock(&cp_heap->lock); + return ret_value; +} + +static int iommu_map_all(unsigned long domain_num, struct ion_cp_heap *cp_heap, + int partition, unsigned long prot) +{ + unsigned long left_to_map = cp_heap->total_size; + unsigned long order = get_order(SZ_64K); + unsigned long page_size = SZ_64K; + int ret_value = 0; + unsigned long virt_addr_len = cp_heap->total_size; + struct iommu_domain *domain = msm_get_iommu_domain(domain_num); + + /* If we are mapping into the video domain we need to map twice the + * size of the heap to account for prefetch issue in video core. + */ + if (domain_num == cp_heap->iommu_2x_map_domain) + virt_addr_len <<= 1; + + if (cp_heap->total_size & (SZ_64K-1)) { + pr_err("Heap size is not aligned to 64K, cannot map into IOMMU\n"); + ret_value = -EINVAL; + } + if (cp_heap->base & (SZ_64K-1)) { + pr_err("Heap physical address is not aligned to 64K, cannot map into IOMMU\n"); + ret_value = -EINVAL; + } + if (!ret_value && domain) { + unsigned long temp_phys = cp_heap->base; + unsigned long temp_iova = + msm_allocate_iova_address(domain_num, partition, + virt_addr_len, SZ_64K); + if (!temp_iova) { + pr_err("%s: could not allocate iova from domain %lu, partition %d\n", + __func__, domain_num, partition); + ret_value = -ENOMEM; + goto out; + } + cp_heap->iommu_iova[domain_num] = temp_iova; + + while (left_to_map) { + int ret = iommu_map(domain, temp_iova, temp_phys, + order, prot); + if (ret) { + pr_err("%s: could not map %lx in domain %p, error: %d\n", + __func__, temp_iova, domain, ret); + ret_value = -EAGAIN; + goto free_iova; + } + temp_iova += page_size; + temp_phys += page_size; + left_to_map -= page_size; + } + if (domain_num == cp_heap->iommu_2x_map_domain) + ret_value = msm_iommu_map_extra(domain, temp_iova, + cp_heap->total_size, + SZ_64K, prot); + if (ret_value) + goto free_iova; + } else { + pr_err("Unable to get IOMMU domain %lu\n", domain_num); + ret_value = -ENOMEM; + } + goto out; + +free_iova: + msm_free_iova_address(cp_heap->iommu_iova[domain_num], domain_num, + partition, virt_addr_len); +out: + return ret_value; +} + +static int ion_cp_heap_map_iommu(struct ion_buffer *buffer, + struct ion_iommu_map *data, + unsigned int domain_num, + unsigned int partition_num, + unsigned long align, + unsigned long iova_length, + unsigned long flags) +{ + struct iommu_domain *domain; + int ret = 0; + unsigned long extra; + struct scatterlist *sglist = 0; + struct ion_cp_heap *cp_heap = + container_of(buffer->heap, struct ion_cp_heap, heap); + int prot = IOMMU_WRITE | IOMMU_READ; + prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0; + + data->mapped_size = iova_length; + + if (!msm_use_iommu()) { + data->iova_addr = buffer->priv_phys; + return 0; + } + + if (cp_heap->iommu_iova[domain_num]) { + /* Already mapped. */ + unsigned long offset = buffer->priv_phys - cp_heap->base; + data->iova_addr = cp_heap->iommu_iova[domain_num] + offset; + return 0; + } else if (cp_heap->iommu_map_all) { + ret = iommu_map_all(domain_num, cp_heap, partition_num, prot); + if (!ret) { + unsigned long offset = + buffer->priv_phys - cp_heap->base; + data->iova_addr = + cp_heap->iommu_iova[domain_num] + offset; + cp_heap->iommu_partition[domain_num] = partition_num; + /* + clear delayed map flag so that we don't interfere + with this feature (we are already delaying). + */ + data->flags &= ~ION_IOMMU_UNMAP_DELAYED; + return 0; + } else { + cp_heap->iommu_iova[domain_num] = 0; + cp_heap->iommu_partition[domain_num] = 0; + return ret; + } + } + + extra = iova_length - buffer->size; + + data->iova_addr = msm_allocate_iova_address(domain_num, partition_num, + data->mapped_size, align); + + if (!data->iova_addr) { + ret = -ENOMEM; + goto out; + } + + domain = msm_get_iommu_domain(domain_num); + + if (!domain) { + ret = -ENOMEM; + goto out1; + } + + sglist = ion_cp_heap_create_sglist(buffer); + if (IS_ERR_OR_NULL(sglist)) { + ret = -ENOMEM; + goto out1; + } + ret = iommu_map_range(domain, data->iova_addr, sglist, + buffer->size, prot); + if (ret) { + pr_err("%s: could not map %lx in domain %p\n", + __func__, data->iova_addr, domain); + goto out1; + } + + if (extra) { + unsigned long extra_iova_addr = data->iova_addr + buffer->size; + ret = msm_iommu_map_extra(domain, extra_iova_addr, extra, + SZ_4K, prot); + if (ret) + goto out2; + } + vfree(sglist); + return ret; + +out2: + iommu_unmap_range(domain, data->iova_addr, buffer->size); +out1: + if (!IS_ERR_OR_NULL(sglist)) + vfree(sglist); + msm_free_iova_address(data->iova_addr, domain_num, partition_num, + data->mapped_size); +out: + return ret; +} + +static void ion_cp_heap_unmap_iommu(struct ion_iommu_map *data) +{ + unsigned int domain_num; + unsigned int partition_num; + struct iommu_domain *domain; + struct ion_cp_heap *cp_heap = + container_of(data->buffer->heap, struct ion_cp_heap, heap); + + if (!msm_use_iommu()) + return; + + + domain_num = iommu_map_domain(data); + + /* If we are mapping everything we'll wait to unmap until everything + is freed. */ + if (cp_heap->iommu_iova[domain_num]) + return; + + partition_num = iommu_map_partition(data); + + domain = msm_get_iommu_domain(domain_num); + + if (!domain) { + WARN(1, "Could not get domain %d. Corruption?\n", domain_num); + return; + } + + iommu_unmap_range(domain, data->iova_addr, data->mapped_size); + msm_free_iova_address(data->iova_addr, domain_num, partition_num, + data->mapped_size); + + return; +} + +static struct ion_heap_ops cp_heap_ops = { + .allocate = ion_cp_heap_allocate, + .free = ion_cp_heap_free, + .phys = ion_cp_heap_phys, + .map_user = ion_cp_heap_map_user, + .unmap_user = ion_cp_heap_unmap_user, + .map_kernel = ion_cp_heap_map_kernel, + .unmap_kernel = ion_cp_heap_unmap_kernel, + .map_dma = ion_cp_heap_map_dma, + .unmap_dma = ion_cp_heap_unmap_dma, + .cache_op = ion_cp_cache_ops, + .print_debug = ion_cp_print_debug, + .secure_heap = ion_cp_secure_heap, + .unsecure_heap = ion_cp_unsecure_heap, + .map_iommu = ion_cp_heap_map_iommu, + .unmap_iommu = ion_cp_heap_unmap_iommu, +}; + +struct ion_heap *ion_cp_heap_create(struct ion_platform_heap *heap_data) +{ + struct ion_cp_heap *cp_heap; + int ret; + + cp_heap = kzalloc(sizeof(*cp_heap), GFP_KERNEL); + if (!cp_heap) + return ERR_PTR(-ENOMEM); + + mutex_init(&cp_heap->lock); + + cp_heap->pool = gen_pool_create(12, -1); + if (!cp_heap->pool) + goto free_heap; + + cp_heap->base = heap_data->base; + ret = gen_pool_add(cp_heap->pool, cp_heap->base, heap_data->size, -1); + if (ret < 0) + goto destroy_pool; + + cp_heap->allocated_bytes = 0; + cp_heap->umap_count = 0; + cp_heap->kmap_cached_count = 0; + cp_heap->kmap_uncached_count = 0; + cp_heap->total_size = heap_data->size; + cp_heap->heap.ops = &cp_heap_ops; + cp_heap->heap.type = ION_HEAP_TYPE_CP; + cp_heap->heap_protected = HEAP_NOT_PROTECTED; + cp_heap->secure_base = cp_heap->base; + cp_heap->secure_size = heap_data->size; + cp_heap->has_outer_cache = heap_data->has_outer_cache; + atomic_set(&cp_heap->protect_cnt, 0); + if (heap_data->extra_data) { + struct ion_cp_heap_pdata *extra_data = + heap_data->extra_data; + cp_heap->reusable = extra_data->reusable; + cp_heap->reserved_vrange = extra_data->virt_addr; + cp_heap->permission_type = extra_data->permission_type; + if (extra_data->secure_size) { + cp_heap->secure_base = extra_data->secure_base; + cp_heap->secure_size = extra_data->secure_size; + } + if (extra_data->setup_region) + cp_heap->bus_id = extra_data->setup_region(); + if (extra_data->request_region) + cp_heap->request_region = extra_data->request_region; + if (extra_data->release_region) + cp_heap->release_region = extra_data->release_region; + cp_heap->iommu_map_all = + extra_data->iommu_map_all; + cp_heap->iommu_2x_map_domain = + extra_data->iommu_2x_map_domain; + + } + + return &cp_heap->heap; + +destroy_pool: + gen_pool_destroy(cp_heap->pool); + +free_heap: + kfree(cp_heap); + + return ERR_PTR(-ENOMEM); +} + +void ion_cp_heap_destroy(struct ion_heap *heap) +{ + struct ion_cp_heap *cp_heap = + container_of(heap, struct ion_cp_heap, heap); + + gen_pool_destroy(cp_heap->pool); + kfree(cp_heap); + cp_heap = NULL; +} + + +/* SCM related code for locking down memory for content protection */ + +#define SCM_CP_LOCK_CMD_ID 0x1 +#define SCM_CP_PROTECT 0x1 +#define SCM_CP_UNPROTECT 0x0 + +struct cp_lock_msg { + unsigned int start; + unsigned int end; + unsigned int permission_type; + unsigned char lock; +} __attribute__ ((__packed__)); + + +static int ion_cp_protect_mem(unsigned int phy_base, unsigned int size, + unsigned int permission_type) +{ + struct cp_lock_msg cmd; + cmd.start = phy_base; + cmd.end = phy_base + size; + cmd.permission_type = permission_type; + cmd.lock = SCM_CP_PROTECT; + + return scm_call(SCM_SVC_CP, SCM_CP_LOCK_CMD_ID, + &cmd, sizeof(cmd), NULL, 0); +} + +static int ion_cp_unprotect_mem(unsigned int phy_base, unsigned int size, + unsigned int permission_type) +{ + struct cp_lock_msg cmd; + cmd.start = phy_base; + cmd.end = phy_base + size; + cmd.permission_type = permission_type; + cmd.lock = SCM_CP_UNPROTECT; + + return scm_call(SCM_SVC_CP, SCM_CP_LOCK_CMD_ID, + &cmd, sizeof(cmd), NULL, 0); +} diff --git a/drivers/gpu/ion/ion_heap.c b/drivers/gpu/ion/ion_heap.c index 8ce3c1907badc..f6f5bf3a52d81 100644 --- a/drivers/gpu/ion/ion_heap.c +++ b/drivers/gpu/ion/ion_heap.c @@ -2,6 +2,7 @@ * drivers/gpu/ion/ion_heap.c * * Copyright (C) 2011 Google, Inc. + * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -32,6 +33,12 @@ struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data) case ION_HEAP_TYPE_CARVEOUT: heap = ion_carveout_heap_create(heap_data); break; + case ION_HEAP_TYPE_IOMMU: + heap = ion_iommu_heap_create(heap_data); + break; + case ION_HEAP_TYPE_CP: + heap = ion_cp_heap_create(heap_data); + break; default: pr_err("%s: Invalid heap type %d\n", __func__, heap_data->type); @@ -65,6 +72,12 @@ void ion_heap_destroy(struct ion_heap *heap) case ION_HEAP_TYPE_CARVEOUT: ion_carveout_heap_destroy(heap); break; + case ION_HEAP_TYPE_IOMMU: + ion_iommu_heap_destroy(heap); + break; + case ION_HEAP_TYPE_CP: + ion_cp_heap_destroy(heap); + break; default: pr_err("%s: Invalid heap type %d\n", __func__, heap->type); diff --git a/drivers/gpu/ion/ion_iommu_heap.c b/drivers/gpu/ion/ion_iommu_heap.c new file mode 100644 index 0000000000000..7c656976d6a8a --- /dev/null +++ b/drivers/gpu/ion/ion_iommu_heap.c @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ion_priv.h" + +#include +#include +#include +#include + +struct ion_iommu_heap { + struct ion_heap heap; + unsigned int has_outer_cache; +}; + +struct ion_iommu_priv_data { + struct page **pages; + int nrpages; + unsigned long size; + struct scatterlist *iommu_sglist; +}; + +static int ion_iommu_heap_allocate(struct ion_heap *heap, + struct ion_buffer *buffer, + unsigned long size, unsigned long align, + unsigned long flags) +{ + int ret, i; + struct ion_iommu_priv_data *data = NULL; + + if (msm_use_iommu()) { + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->size = PFN_ALIGN(size); + data->nrpages = data->size >> PAGE_SHIFT; + data->pages = kzalloc(sizeof(struct page *)*data->nrpages, + GFP_KERNEL); + if (!data->pages) { + ret = -ENOMEM; + goto err1; + } + data->iommu_sglist = vmalloc(sizeof(*data->iommu_sglist) * + data->nrpages); + if (!data->iommu_sglist) { + ret = -ENOMEM; + goto err1; + } + + sg_init_table(data->iommu_sglist, data->nrpages); + + for (i = 0; i < data->nrpages; i++) { + data->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!data->pages[i]) + goto err2; + + sg_set_page(&data->iommu_sglist[i], data->pages[i], + PAGE_SIZE, 0); + } + + + buffer->priv_virt = data; + return 0; + + } else { + return -ENOMEM; + } + + +err2: + vfree(data->iommu_sglist); + data->iommu_sglist = NULL; + + for (i = 0; i < data->nrpages; i++) { + if (data->pages[i]) + __free_page(data->pages[i]); + } + kfree(data->pages); +err1: + kfree(data); + return ret; +} + +static void ion_iommu_heap_free(struct ion_buffer *buffer) +{ + struct ion_iommu_priv_data *data = buffer->priv_virt; + int i; + + if (!data) + return; + + for (i = 0; i < data->nrpages; i++) + __free_page(data->pages[i]); + + vfree(data->iommu_sglist); + data->iommu_sglist = NULL; + + kfree(data->pages); + kfree(data); +} + +void *ion_iommu_heap_map_kernel(struct ion_heap *heap, + struct ion_buffer *buffer, + unsigned long flags) +{ + struct ion_iommu_priv_data *data = buffer->priv_virt; + pgprot_t page_prot = PAGE_KERNEL; + + if (!data) + return NULL; + + if (!ION_IS_CACHED(flags)) + page_prot = pgprot_noncached(page_prot); + + buffer->vaddr = vmap(data->pages, data->nrpages, VM_IOREMAP, page_prot); + + return buffer->vaddr; +} + +void ion_iommu_heap_unmap_kernel(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + if (!buffer->vaddr) + return; + + vunmap(buffer->vaddr); + buffer->vaddr = NULL; +} + +int ion_iommu_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, + struct vm_area_struct *vma, unsigned long flags) +{ + struct ion_iommu_priv_data *data = buffer->priv_virt; + int i; + unsigned long curr_addr; + if (!data) + return -EINVAL; + + if (!ION_IS_CACHED(flags)) + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + curr_addr = vma->vm_start; + for (i = 0; i < data->nrpages && curr_addr < vma->vm_end; i++) { + if (vm_insert_page(vma, curr_addr, data->pages[i])) { + /* + * This will fail the mmap which will + * clean up the vma space properly. + */ + return -EINVAL; + } + curr_addr += PAGE_SIZE; + } + return 0; +} + +int ion_iommu_heap_map_iommu(struct ion_buffer *buffer, + struct ion_iommu_map *data, + unsigned int domain_num, + unsigned int partition_num, + unsigned long align, + unsigned long iova_length, + unsigned long flags) +{ + struct iommu_domain *domain; + int ret = 0; + unsigned long extra; + struct ion_iommu_priv_data *buffer_data = buffer->priv_virt; + int prot = IOMMU_WRITE | IOMMU_READ; + prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0; + + BUG_ON(!msm_use_iommu()); + + data->mapped_size = iova_length; + extra = iova_length - buffer->size; + + data->iova_addr = msm_allocate_iova_address(domain_num, partition_num, + data->mapped_size, align); + + if (!data->iova_addr) { + ret = -ENOMEM; + goto out; + } + + domain = msm_get_iommu_domain(domain_num); + + if (!domain) { + ret = -ENOMEM; + goto out1; + } + + ret = iommu_map_range(domain, data->iova_addr, + buffer_data->iommu_sglist, buffer->size, prot); + if (ret) { + pr_err("%s: could not map %lx in domain %p\n", + __func__, data->iova_addr, domain); + goto out1; + } + + if (extra) { + unsigned long extra_iova_addr = data->iova_addr + buffer->size; + ret = msm_iommu_map_extra(domain, extra_iova_addr, extra, SZ_4K, + prot); + if (ret) + goto out2; + } + return ret; + +out2: + iommu_unmap_range(domain, data->iova_addr, buffer->size); +out1: + msm_free_iova_address(data->iova_addr, domain_num, partition_num, + buffer->size); + +out: + + return ret; +} + +void ion_iommu_heap_unmap_iommu(struct ion_iommu_map *data) +{ + unsigned int domain_num; + unsigned int partition_num; + struct iommu_domain *domain; + + BUG_ON(!msm_use_iommu()); + + domain_num = iommu_map_domain(data); + partition_num = iommu_map_partition(data); + + domain = msm_get_iommu_domain(domain_num); + + if (!domain) { + WARN(1, "Could not get domain %d. Corruption?\n", domain_num); + return; + } + + iommu_unmap_range(domain, data->iova_addr, data->mapped_size); + msm_free_iova_address(data->iova_addr, domain_num, partition_num, + data->mapped_size); + + return; +} + +static int ion_iommu_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer, + void *vaddr, unsigned int offset, unsigned int length, + unsigned int cmd) +{ + void (*outer_cache_op)(phys_addr_t, phys_addr_t); + struct ion_iommu_heap *iommu_heap = + container_of(heap, struct ion_iommu_heap, heap); + + switch (cmd) { + case ION_IOC_CLEAN_CACHES: + dmac_clean_range(vaddr, vaddr + length); + outer_cache_op = outer_clean_range; + break; + case ION_IOC_INV_CACHES: + dmac_inv_range(vaddr, vaddr + length); + outer_cache_op = outer_inv_range; + break; + case ION_IOC_CLEAN_INV_CACHES: + dmac_flush_range(vaddr, vaddr + length); + outer_cache_op = outer_flush_range; + break; + default: + return -EINVAL; + } + + if (iommu_heap->has_outer_cache) { + unsigned long pstart; + unsigned int i; + struct ion_iommu_priv_data *data = buffer->priv_virt; + if (!data) + return -ENOMEM; + + for (i = 0; i < data->nrpages; ++i) { + pstart = page_to_phys(data->pages[i]); + outer_cache_op(pstart, pstart + PAGE_SIZE); + } + } + return 0; +} + +static struct scatterlist *ion_iommu_heap_map_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ + struct ion_iommu_priv_data *data = buffer->priv_virt; + return data->iommu_sglist; +} + +static void ion_iommu_heap_unmap_dma(struct ion_heap *heap, + struct ion_buffer *buffer) +{ +} + +static struct ion_heap_ops iommu_heap_ops = { + .allocate = ion_iommu_heap_allocate, + .free = ion_iommu_heap_free, + .map_user = ion_iommu_heap_map_user, + .map_kernel = ion_iommu_heap_map_kernel, + .unmap_kernel = ion_iommu_heap_unmap_kernel, + .map_iommu = ion_iommu_heap_map_iommu, + .unmap_iommu = ion_iommu_heap_unmap_iommu, + .cache_op = ion_iommu_cache_ops, + .map_dma = ion_iommu_heap_map_dma, + .unmap_dma = ion_iommu_heap_unmap_dma, +}; + +struct ion_heap *ion_iommu_heap_create(struct ion_platform_heap *heap_data) +{ + struct ion_iommu_heap *iommu_heap; + + iommu_heap = kzalloc(sizeof(struct ion_iommu_heap), GFP_KERNEL); + if (!iommu_heap) + return ERR_PTR(-ENOMEM); + + iommu_heap->heap.ops = &iommu_heap_ops; + iommu_heap->heap.type = ION_HEAP_TYPE_IOMMU; + iommu_heap->has_outer_cache = heap_data->has_outer_cache; + + return &iommu_heap->heap; +} + +void ion_iommu_heap_destroy(struct ion_heap *heap) +{ + struct ion_iommu_heap *iommu_heap = + container_of(heap, struct ion_iommu_heap, heap); + + kfree(iommu_heap); + iommu_heap = NULL; +} diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h index 888b59924ff76..17d4e5b67a7a4 100644 --- a/drivers/gpu/ion/ion_priv.h +++ b/drivers/gpu/ion/ion_priv.h @@ -2,6 +2,7 @@ * drivers/gpu/ion/ion_priv.h * * Copyright (C) 2011 Google, Inc. + * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -22,6 +23,7 @@ #include #include #include +#include struct ion_mapping; @@ -35,6 +37,42 @@ struct ion_kernel_mapping { void *vaddr; }; +enum { + DI_PARTITION_NUM = 0, + DI_DOMAIN_NUM = 1, + DI_MAX, +}; + +/** + * struct ion_iommu_map - represents a mapping of an ion buffer to an iommu + * @iova_addr - iommu virtual address + * @node - rb node to exist in the buffer's tree of iommu mappings + * @domain_info - contains the partition number and domain number + * domain_info[1] = domain number + * domain_info[0] = partition number + * @ref - for reference counting this mapping + * @mapped_size - size of the iova space mapped + * (may not be the same as the buffer size) + * @flags - iommu domain/partition specific flags. + * + * Represents a mapping of one ion buffer to a particular iommu domain + * and address range. There may exist other mappings of this buffer in + * different domains or address ranges. All mappings will have the same + * cacheability and security. + */ +struct ion_iommu_map { + unsigned long iova_addr; + struct rb_node node; + union { + int domain_info[DI_MAX]; + uint64_t key; + }; + struct ion_buffer *buffer; + struct kref ref; + int mapped_size; + unsigned long flags; +}; + struct ion_buffer *ion_handle_buffer(struct ion_handle *handle); /** @@ -72,6 +110,8 @@ struct ion_buffer { int dmap_cnt; struct scatterlist *sglist; int umap_cnt; + unsigned int iommu_map_cnt; + struct rb_root iommu_maps; int marked; }; @@ -86,6 +126,7 @@ struct ion_buffer { * @map_kernel map memory to the kernel * @unmap_kernel unmap memory to the kernel * @map_user map memory to userspace + * @unmap_user unmap memory to userspace */ struct ion_heap_ops { int (*allocate) (struct ion_heap *heap, @@ -102,11 +143,21 @@ struct ion_heap_ops { void (*unmap_kernel) (struct ion_heap *heap, struct ion_buffer *buffer); int (*map_user) (struct ion_heap *mapper, struct ion_buffer *buffer, struct vm_area_struct *vma, unsigned long flags); + void (*unmap_user) (struct ion_heap *mapper, struct ion_buffer *buffer); int (*cache_op)(struct ion_heap *heap, struct ion_buffer *buffer, void *vaddr, unsigned int offset, unsigned int length, unsigned int cmd); - unsigned long (*get_allocated)(struct ion_heap *heap); - unsigned long (*get_total)(struct ion_heap *heap); + int (*map_iommu)(struct ion_buffer *buffer, + struct ion_iommu_map *map_data, + unsigned int domain_num, + unsigned int partition_num, + unsigned long align, + unsigned long iova_length, + unsigned long flags); + void (*unmap_iommu)(struct ion_iommu_map *data); + int (*print_debug)(struct ion_heap *heap, struct seq_file *s); + int (*secure_heap)(struct ion_heap *heap); + int (*unsecure_heap)(struct ion_heap *heap); }; /** @@ -134,6 +185,11 @@ struct ion_heap { const char *name; }; + + +#define iommu_map_domain(__m) ((__m)->domain_info[1]) +#define iommu_map_partition(__m) ((__m)->domain_info[0]) + /** * ion_device_create - allocates and returns an ion device * @custom_ioctl: arch specific ioctl function if applicable @@ -175,6 +231,16 @@ void ion_system_contig_heap_destroy(struct ion_heap *); struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *); void ion_carveout_heap_destroy(struct ion_heap *); + +struct ion_heap *ion_iommu_heap_create(struct ion_platform_heap *); +void ion_iommu_heap_destroy(struct ion_heap *); + +struct ion_heap *ion_cp_heap_create(struct ion_platform_heap *); +void ion_cp_heap_destroy(struct ion_heap *); + +struct ion_heap *ion_reusable_heap_create(struct ion_platform_heap *); +void ion_reusable_heap_destroy(struct ion_heap *); + /** * kernel api to allocate/free from carveout -- used when carveout is * used to back an architecture specific custom heap @@ -183,10 +249,53 @@ ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap, unsigned long size, unsigned long align); void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr, unsigned long size); + + +struct ion_heap *msm_get_contiguous_heap(void); /** - * The carveout heap returns physical addresses, since 0 may be a valid + * The carveout/cp heap returns physical addresses, since 0 may be a valid * physical address, this is used to indicate allocation failed */ #define ION_CARVEOUT_ALLOCATE_FAIL -1 +#define ION_CP_ALLOCATE_FAIL -1 + +/** + * The reserved heap returns physical addresses, since 0 may be a valid + * physical address, this is used to indicate allocation failed + */ +#define ION_RESERVED_ALLOCATE_FAIL -1 + +/** + * ion_map_fmem_buffer - map fmem allocated memory into the kernel + * @buffer - buffer to map + * @phys_base - physical base of the heap + * @virt_base - virtual base of the heap + * @flags - flags for the heap + * + * Map fmem allocated memory into the kernel address space. This + * is designed to be used by other heaps that need fmem behavior. + * The virtual range must be pre-allocated. + */ +void *ion_map_fmem_buffer(struct ion_buffer *buffer, unsigned long phys_base, + void *virt_base, unsigned long flags); + +/** + * ion_do_cache_op - do cache operations. + * + * @client - pointer to ION client. + * @handle - pointer to buffer handle. + * @uaddr - virtual address to operate on. + * @offset - offset from physical address. + * @len - Length of data to do cache operation on. + * @cmd - Cache operation to perform: + * ION_IOC_CLEAN_CACHES + * ION_IOC_INV_CACHES + * ION_IOC_CLEAN_INV_CACHES + * + * Returns 0 on success + */ +int ion_do_cache_op(struct ion_client *client, struct ion_handle *handle, + void *uaddr, unsigned long offset, unsigned long len, + unsigned int cmd); #endif /* _ION_PRIV_H */ diff --git a/drivers/gpu/ion/ion_system_heap.c b/drivers/gpu/ion/ion_system_heap.c index b26d48cca7883..6b98eb2c8a5ed 100644 --- a/drivers/gpu/ion/ion_system_heap.c +++ b/drivers/gpu/ion/ion_system_heap.c @@ -2,6 +2,7 @@ * drivers/gpu/ion/ion_system_heap.c * * Copyright (C) 2011 Google, Inc. + * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -20,11 +21,17 @@ #include #include #include +#include +#include +#include #include "ion_priv.h" #include +#include static atomic_t system_heap_allocated; static atomic_t system_contig_heap_allocated; +static unsigned int system_heap_has_outer_cache; +static unsigned int system_heap_contig_has_outer_cache; static int ion_system_heap_allocate(struct ion_heap *heap, struct ion_buffer *buffer, @@ -98,6 +105,32 @@ void ion_system_heap_unmap_kernel(struct ion_heap *heap, { } +void ion_system_heap_unmap_iommu(struct ion_iommu_map *data) +{ + unsigned int domain_num; + unsigned int partition_num; + struct iommu_domain *domain; + + if (!msm_use_iommu()) + return; + + domain_num = iommu_map_domain(data); + partition_num = iommu_map_partition(data); + + domain = msm_get_iommu_domain(domain_num); + + if (!domain) { + WARN(1, "Could not get domain %d. Corruption?\n", domain_num); + return; + } + + iommu_unmap_range(domain, data->iova_addr, data->mapped_size); + msm_free_iova_address(data->iova_addr, domain_num, partition_num, + data->mapped_size); + + return; +} + int ion_system_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, struct vm_area_struct *vma, unsigned long flags) { @@ -114,50 +147,163 @@ int ion_system_heap_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer, void *vaddr, unsigned int offset, unsigned int length, unsigned int cmd) { - unsigned long vstart, pstart; - void *vtemp; - unsigned long ln = 0; - void (*op)(unsigned long, unsigned long, unsigned long); + void (*outer_cache_op)(phys_addr_t, phys_addr_t); switch (cmd) { case ION_IOC_CLEAN_CACHES: - op = clean_caches; + dmac_clean_range(vaddr, vaddr + length); + outer_cache_op = outer_clean_range; break; case ION_IOC_INV_CACHES: - op = invalidate_caches; + dmac_inv_range(vaddr, vaddr + length); + outer_cache_op = outer_inv_range; break; case ION_IOC_CLEAN_INV_CACHES: - op = clean_and_invalidate_caches; + dmac_flush_range(vaddr, vaddr + length); + outer_cache_op = outer_flush_range; break; default: return -EINVAL; } - for (vtemp = buffer->priv_virt + offset, - vstart = (unsigned long) vaddr; - ln < length; - vtemp += PAGE_SIZE, ln += PAGE_SIZE, - vstart += PAGE_SIZE) { - pstart = page_to_phys(vmalloc_to_page(vtemp)); - /* - * If vmalloc -> page -> phys is returning NULL, something - * has really gone wrong... - */ - if (!pstart) { - WARN(1, "Could not translate %p to physical address\n", - vtemp); + if (system_heap_has_outer_cache) { + unsigned long pstart; + void *vend; + void *vtemp; + unsigned long ln = 0; + vend = buffer->priv_virt + buffer->size; + vtemp = buffer->priv_virt + offset; + + if ((vtemp+length) > vend) { + pr_err("Trying to flush outside of mapped range.\n"); + pr_err("End of mapped range: %p, trying to flush to " + "address %p\n", vend, vtemp+length); + WARN(1, "%s: called with heap name %s, buffer size 0x%x, " + "vaddr 0x%p, offset 0x%x, length: 0x%x\n", + __func__, heap->name, buffer->size, vaddr, + offset, length); return -EINVAL; } - op(vstart, PAGE_SIZE, pstart); + for (; ln < length && vtemp < vend; + vtemp += PAGE_SIZE, ln += PAGE_SIZE) { + struct page *page = vmalloc_to_page(vtemp); + if (!page) { + WARN(1, "Could not find page for virt. address %p\n", + vtemp); + return -EINVAL; + } + pstart = page_to_phys(page); + /* + * If page -> phys is returning NULL, something + * has really gone wrong... + */ + if (!pstart) { + WARN(1, "Could not translate %p to physical address\n", + vtemp); + return -EINVAL; + } + + outer_cache_op(pstart, pstart + PAGE_SIZE); + } } + return 0; +} + +static int ion_system_print_debug(struct ion_heap *heap, struct seq_file *s) +{ + seq_printf(s, "total bytes currently allocated: %lx\n", + (unsigned long) atomic_read(&system_heap_allocated)); return 0; } -static unsigned long ion_system_heap_get_allocated(struct ion_heap *heap) +int ion_system_heap_map_iommu(struct ion_buffer *buffer, + struct ion_iommu_map *data, + unsigned int domain_num, + unsigned int partition_num, + unsigned long align, + unsigned long iova_length, + unsigned long flags) { - return atomic_read(&system_heap_allocated); + int ret = 0, i; + struct iommu_domain *domain; + unsigned long extra; + unsigned long extra_iova_addr; + struct page *page; + int npages = buffer->size >> PAGE_SHIFT; + void *vaddr = buffer->priv_virt; + struct scatterlist *sglist = 0; + int prot = IOMMU_WRITE | IOMMU_READ; + prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0; + + if (!ION_IS_CACHED(flags)) + return -EINVAL; + + if (!msm_use_iommu()) + return -EINVAL; + + data->mapped_size = iova_length; + extra = iova_length - buffer->size; + + data->iova_addr = msm_allocate_iova_address(domain_num, partition_num, + data->mapped_size, align); + + if (!data->iova_addr) { + ret = -ENOMEM; + goto out; + } + + domain = msm_get_iommu_domain(domain_num); + + if (!domain) { + ret = -ENOMEM; + goto out1; + } + + + sglist = vmalloc(sizeof(*sglist) * npages); + if (!sglist) { + ret = -ENOMEM; + goto out1; + } + + sg_init_table(sglist, npages); + for (i = 0; i < npages; i++) { + page = vmalloc_to_page(vaddr); + if (!page) + goto out1; + sg_set_page(&sglist[i], page, PAGE_SIZE, 0); + vaddr += PAGE_SIZE; + } + + ret = iommu_map_range(domain, data->iova_addr, sglist, + buffer->size, prot); + + if (ret) { + pr_err("%s: could not map %lx in domain %p\n", + __func__, data->iova_addr, domain); + goto out1; + } + + extra_iova_addr = data->iova_addr + buffer->size; + if (extra) { + ret = msm_iommu_map_extra(domain, extra_iova_addr, extra, SZ_4K, + prot); + if (ret) + goto out2; + } + vfree(sglist); + return ret; + +out2: + iommu_unmap_range(domain, data->iova_addr, buffer->size); +out1: + vfree(sglist); + msm_free_iova_address(data->iova_addr, domain_num, partition_num, + data->mapped_size); +out: + return ret; } static struct ion_heap_ops vmalloc_ops = { @@ -169,10 +315,12 @@ static struct ion_heap_ops vmalloc_ops = { .unmap_kernel = ion_system_heap_unmap_kernel, .map_user = ion_system_heap_map_user, .cache_op = ion_system_heap_cache_ops, - .get_allocated = ion_system_heap_get_allocated, + .print_debug = ion_system_print_debug, + .map_iommu = ion_system_heap_map_iommu, + .unmap_iommu = ion_system_heap_unmap_iommu, }; -struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused) +struct ion_heap *ion_system_heap_create(struct ion_platform_heap *pheap) { struct ion_heap *heap; @@ -181,6 +329,7 @@ struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused) return ERR_PTR(-ENOMEM); heap->ops = &vmalloc_ops; heap->type = ION_HEAP_TYPE_SYSTEM; + system_heap_has_outer_cache = pheap->has_outer_cache; return heap; } @@ -252,37 +401,126 @@ int ion_system_contig_heap_cache_ops(struct ion_heap *heap, unsigned int offset, unsigned int length, unsigned int cmd) { - unsigned long vstart, pstart; - - pstart = virt_to_phys(buffer->priv_virt) + offset; - if (!pstart) { - WARN(1, "Could not do virt to phys translation on %p\n", - buffer->priv_virt); - return -EINVAL; - } - - vstart = (unsigned long) vaddr; + void (*outer_cache_op)(phys_addr_t, phys_addr_t); switch (cmd) { case ION_IOC_CLEAN_CACHES: - clean_caches(vstart, length, pstart); + dmac_clean_range(vaddr, vaddr + length); + outer_cache_op = outer_clean_range; break; case ION_IOC_INV_CACHES: - invalidate_caches(vstart, length, pstart); + dmac_inv_range(vaddr, vaddr + length); + outer_cache_op = outer_inv_range; break; case ION_IOC_CLEAN_INV_CACHES: - clean_and_invalidate_caches(vstart, length, pstart); + dmac_flush_range(vaddr, vaddr + length); + outer_cache_op = outer_flush_range; break; default: return -EINVAL; } + if (system_heap_contig_has_outer_cache) { + unsigned long pstart; + + pstart = virt_to_phys(buffer->priv_virt) + offset; + if (!pstart) { + WARN(1, "Could not do virt to phys translation on %p\n", + buffer->priv_virt); + return -EINVAL; + } + + outer_cache_op(pstart, pstart + PAGE_SIZE); + } + + return 0; +} + +static int ion_system_contig_print_debug(struct ion_heap *heap, + struct seq_file *s) +{ + seq_printf(s, "total bytes currently allocated: %lx\n", + (unsigned long) atomic_read(&system_contig_heap_allocated)); + return 0; } -static unsigned long ion_system_contig_heap_get_allocated(struct ion_heap *heap) +int ion_system_contig_heap_map_iommu(struct ion_buffer *buffer, + struct ion_iommu_map *data, + unsigned int domain_num, + unsigned int partition_num, + unsigned long align, + unsigned long iova_length, + unsigned long flags) { - return atomic_read(&system_contig_heap_allocated); + int ret = 0; + struct iommu_domain *domain; + unsigned long extra; + struct scatterlist *sglist = 0; + struct page *page = 0; + int prot = IOMMU_WRITE | IOMMU_READ; + prot |= ION_IS_CACHED(flags) ? IOMMU_CACHE : 0; + + if (!ION_IS_CACHED(flags)) + return -EINVAL; + + if (!msm_use_iommu()) { + data->iova_addr = virt_to_phys(buffer->vaddr); + return 0; + } + + data->mapped_size = iova_length; + extra = iova_length - buffer->size; + + data->iova_addr = msm_allocate_iova_address(domain_num, partition_num, + data->mapped_size, align); + + if (!data->iova_addr) { + ret = -ENOMEM; + goto out; + } + + domain = msm_get_iommu_domain(domain_num); + + if (!domain) { + ret = -ENOMEM; + goto out1; + } + page = virt_to_page(buffer->vaddr); + + sglist = vmalloc(sizeof(*sglist)); + if (!sglist) + goto out1; + + sg_init_table(sglist, 1); + sg_set_page(sglist, page, buffer->size, 0); + + ret = iommu_map_range(domain, data->iova_addr, sglist, + buffer->size, prot); + if (ret) { + pr_err("%s: could not map %lx in domain %p\n", + __func__, data->iova_addr, domain); + goto out1; + } + + if (extra) { + unsigned long extra_iova_addr = data->iova_addr + buffer->size; + ret = msm_iommu_map_extra(domain, extra_iova_addr, extra, SZ_4K, + prot); + if (ret) + goto out2; + } + vfree(sglist); + return ret; +out2: + iommu_unmap_range(domain, data->iova_addr, buffer->size); + +out1: + vfree(sglist); + msm_free_iova_address(data->iova_addr, domain_num, partition_num, + data->mapped_size); +out: + return ret; } static struct ion_heap_ops kmalloc_ops = { @@ -295,10 +533,12 @@ static struct ion_heap_ops kmalloc_ops = { .unmap_kernel = ion_system_heap_unmap_kernel, .map_user = ion_system_contig_heap_map_user, .cache_op = ion_system_contig_heap_cache_ops, - .get_allocated = ion_system_contig_heap_get_allocated, + .print_debug = ion_system_contig_print_debug, + .map_iommu = ion_system_contig_heap_map_iommu, + .unmap_iommu = ion_system_heap_unmap_iommu, }; -struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *unused) +struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *pheap) { struct ion_heap *heap; @@ -307,6 +547,7 @@ struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *unused) return ERR_PTR(-ENOMEM); heap->ops = &kmalloc_ops; heap->type = ION_HEAP_TYPE_SYSTEM_CONTIG; + system_heap_contig_has_outer_cache = pheap->has_outer_cache; return heap; } diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c index 54dd056986316..0a496ddf56e1d 100644 --- a/drivers/gpu/ion/msm/msm_ion.c +++ b/drivers/gpu/ion/msm/msm_ion.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -15,6 +15,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include #include "../ion_priv.h" @@ -30,21 +36,285 @@ struct ion_client *msm_ion_client_create(unsigned int heap_mask, } EXPORT_SYMBOL(msm_ion_client_create); -static unsigned long msm_ion_get_base(unsigned long size, int memory_type) +int msm_ion_secure_heap(int heap_id) +{ + return ion_secure_heap(idev, heap_id); +} +EXPORT_SYMBOL(msm_ion_secure_heap); + +int msm_ion_unsecure_heap(int heap_id) +{ + return ion_unsecure_heap(idev, heap_id); +} +EXPORT_SYMBOL(msm_ion_unsecure_heap); + +int msm_ion_do_cache_op(struct ion_client *client, struct ion_handle *handle, + void *vaddr, unsigned long len, unsigned int cmd) +{ + return ion_do_cache_op(client, handle, vaddr, 0, len, cmd); +} +EXPORT_SYMBOL(msm_ion_do_cache_op); + +static unsigned long msm_ion_get_base(unsigned long size, int memory_type, + unsigned int align) { switch (memory_type) { case ION_EBI_TYPE: - return allocate_contiguous_ebi_nomap(size, PAGE_SIZE); + return allocate_contiguous_ebi_nomap(size, align); break; case ION_SMI_TYPE: return allocate_contiguous_memory_nomap(size, MEMTYPE_SMI, - PAGE_SIZE); + align); break; default: + pr_err("%s: Unknown memory type %d\n", __func__, memory_type); return 0; } } +static struct ion_platform_heap *find_heap(const struct ion_platform_heap + heap_data[], + unsigned int nr_heaps, + int heap_id) +{ + unsigned int i; + for (i = 0; i < nr_heaps; ++i) { + const struct ion_platform_heap *heap = &heap_data[i]; + if (heap->id == heap_id) + return (struct ion_platform_heap *) heap; + } + return 0; +} + +static void ion_set_base_address(struct ion_platform_heap *heap, + struct ion_platform_heap *shared_heap, + struct ion_co_heap_pdata *co_heap_data, + struct ion_cp_heap_pdata *cp_data) +{ + if (cp_data->reusable) { + const struct fmem_data *fmem_info = fmem_get_info(); + + if (!fmem_info) { + pr_err("fmem info pointer NULL!\n"); + BUG(); + } + + heap->base = fmem_info->phys - fmem_info->reserved_size_low; + cp_data->virt_addr = fmem_info->virt; + pr_info("ION heap %s using FMEM\n", shared_heap->name); + } else { + heap->base = msm_ion_get_base(heap->size + shared_heap->size, + shared_heap->memory_type, + co_heap_data->align); + } + if (heap->base) { + shared_heap->base = heap->base + heap->size; + cp_data->secure_base = heap->base; + cp_data->secure_size = heap->size + shared_heap->size; + } else { + pr_err("%s: could not get memory for heap %s (id %x)\n", + __func__, heap->name, heap->id); + } +} + +static void allocate_co_memory(struct ion_platform_heap *heap, + struct ion_platform_heap heap_data[], + unsigned int nr_heaps) +{ + struct ion_co_heap_pdata *co_heap_data = + (struct ion_co_heap_pdata *) heap->extra_data; + + if (co_heap_data->adjacent_mem_id != INVALID_HEAP_ID) { + struct ion_platform_heap *shared_heap = + find_heap(heap_data, nr_heaps, + co_heap_data->adjacent_mem_id); + if (shared_heap) { + struct ion_cp_heap_pdata *cp_data = + (struct ion_cp_heap_pdata *) shared_heap->extra_data; + if (cp_data->fixed_position == FIXED_MIDDLE) { + const struct fmem_data *fmem_info = + fmem_get_info(); + + if (!fmem_info) { + pr_err("fmem info pointer NULL!\n"); + BUG(); + } + + cp_data->virt_addr = fmem_info->virt; + cp_data->secure_base = heap->base; + cp_data->secure_size = + heap->size + shared_heap->size; + } else if (!heap->base) { + ion_set_base_address(heap, shared_heap, + co_heap_data, cp_data); + } + } + } +} + +/* Fixup heaps in board file to support two heaps being adjacent to each other. + * A flag (adjacent_mem_id) in the platform data tells us that the heap phy + * memory location must be adjacent to the specified heap. We do this by + * carving out memory for both heaps and then splitting up the memory to the + * two heaps. The heap specifying the "adjacent_mem_id" get the base of the + * memory while heap specified in "adjacent_mem_id" get base+size as its + * base address. + * Note: Modifies platform data and allocates memory. + */ +static void msm_ion_heap_fixup(struct ion_platform_heap heap_data[], + unsigned int nr_heaps) +{ + unsigned int i; + + for (i = 0; i < nr_heaps; i++) { + struct ion_platform_heap *heap = &heap_data[i]; + if (heap->type == ION_HEAP_TYPE_CARVEOUT) { + if (heap->extra_data) + allocate_co_memory(heap, heap_data, nr_heaps); + } + } +} + +static void msm_ion_allocate(struct ion_platform_heap *heap) +{ + + if (!heap->base && heap->extra_data) { + unsigned int align = 0; + switch (heap->type) { + case ION_HEAP_TYPE_CARVEOUT: + align = + ((struct ion_co_heap_pdata *) heap->extra_data)->align; + break; + case ION_HEAP_TYPE_CP: + { + struct ion_cp_heap_pdata *data = + (struct ion_cp_heap_pdata *) + heap->extra_data; + if (data->reusable) { + const struct fmem_data *fmem_info = + fmem_get_info(); + heap->base = fmem_info->phys; + data->virt_addr = fmem_info->virt; + pr_info("ION heap %s using FMEM\n", heap->name); + } else if (data->mem_is_fmem) { + const struct fmem_data *fmem_info = + fmem_get_info(); + heap->base = fmem_info->phys + fmem_info->size; + } + align = data->align; + break; + } + default: + break; + } + if (align && !heap->base) { + heap->base = msm_ion_get_base(heap->size, + heap->memory_type, + align); + if (!heap->base) + pr_err("%s: could not get memory for heap %s " + "(id %x)\n", __func__, heap->name, heap->id); + } + } +} + +static int check_vaddr_bounds(unsigned long start, unsigned long end) +{ + struct mm_struct *mm = current->active_mm; + struct vm_area_struct *vma; + int ret = 1; + + if (end < start) + goto out; + + down_read(&mm->mmap_sem); + vma = find_vma(mm, start); + if (vma && vma->vm_start < end) { + if (start < vma->vm_start) + goto out_up; + if (end > vma->vm_end) + goto out_up; + ret = 0; + } + +out_up: + up_read(&mm->mmap_sem); +out: + return ret; +} + +static long msm_ion_custom_ioctl(struct ion_client *client, + unsigned int cmd, + unsigned long arg) +{ + switch (cmd) { + case ION_IOC_CLEAN_CACHES: + case ION_IOC_INV_CACHES: + case ION_IOC_CLEAN_INV_CACHES: + { + struct ion_flush_data data; + unsigned long start, end; + struct ion_handle *handle = NULL; + int ret; + + if (copy_from_user(&data, (void __user *)arg, + sizeof(struct ion_flush_data))) + return -EFAULT; + + start = (unsigned long) data.vaddr; + end = (unsigned long) data.vaddr + data.length; + + if (check_vaddr_bounds(start, end)) { + pr_err("%s: virtual address %p is out of bounds\n", + __func__, data.vaddr); + return -EINVAL; + } + + if (!data.handle) { + handle = ion_import_fd(client, data.fd); + if (IS_ERR_OR_NULL(handle)) { + pr_info("%s: Could not import handle: %d\n", + __func__, (int)handle); + return -EINVAL; + } + } + + ret = ion_do_cache_op(client, + data.handle ? data.handle : handle, + data.vaddr, data.offset, data.length, + cmd); + + if (!data.handle) + ion_free(client, handle); + + if (ret < 0) + return ret; + + break; + + } + case ION_IOC_GET_FLAGS: + { + struct ion_flag_data data; + int ret; + if (copy_from_user(&data, (void __user *)arg, + sizeof(struct ion_flag_data))) + return -EFAULT; + + ret = ion_handle_get_flags(client, data.handle, &data.flags); + if (ret < 0) + return ret; + if (copy_to_user((void __user *)arg, &data, + sizeof(struct ion_flag_data))) + return -EFAULT; + break; + } + default: + return -ENOTTY; + } + return 0; +} + static int msm_ion_probe(struct platform_device *pdev) { struct ion_platform_data *pdata = pdev->dev.platform_data; @@ -60,35 +330,35 @@ static int msm_ion_probe(struct platform_device *pdev) goto out; } - idev = ion_device_create(NULL); + idev = ion_device_create(msm_ion_custom_ioctl); if (IS_ERR_OR_NULL(idev)) { err = PTR_ERR(idev); goto freeheaps; } + msm_ion_heap_fixup(pdata->heaps, num_heaps); + /* create the heaps as specified in the board file */ for (i = 0; i < num_heaps; i++) { struct ion_platform_heap *heap_data = &pdata->heaps[i]; + msm_ion_allocate(heap_data); - if (heap_data->type == ION_HEAP_TYPE_CARVEOUT) { - heap_data->base = msm_ion_get_base(heap_data->size, - heap_data->memory_type); - if (!heap_data->base) { - pr_err("%s: could not get memory for heap %s" - " (id %x)\n", __func__, heap_data->name, - heap_data->id); - continue; - } - } - + heap_data->has_outer_cache = pdata->has_outer_cache; heaps[i] = ion_heap_create(heap_data); if (IS_ERR_OR_NULL(heaps[i])) { - pr_err("%s: could not create ion heap %s" - " (id %x)\n", __func__, heap_data->name, - heap_data->id); heaps[i] = 0; continue; + } else { + if (heap_data->size) + pr_info("ION heap %s created at %lx " + "with size %x\n", heap_data->name, + heap_data->base, + heap_data->size); + else + pr_info("ION heap %s created\n", + heap_data->name); } + ion_device_add_heap(idev, heaps[i]); } platform_set_drvdata(pdev, idev); diff --git a/include/linux/ion.h b/include/linux/ion.h index 7de40d4345f99..0848591e794cd 100644 --- a/include/linux/ion.h +++ b/include/linux/ion.h @@ -2,6 +2,7 @@ * include/linux/ion.h * * Copyright (C) 2011 Google, Inc. + * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -29,12 +30,18 @@ struct ion_handle; * @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved * carveout heap, allocations are physically * contiguous - * @ION_HEAP_END: helper for iterating over heaps + * @ION_HEAP_TYPE_IOMMU: IOMMU memory + * @ION_HEAP_TYPE_CP: memory allocated from a prereserved + * carveout heap, allocations are physically + * contiguous. Used for content protection. + * @ION_HEAP_END: helper for iterating over heaps */ enum ion_heap_type { ION_HEAP_TYPE_SYSTEM, ION_HEAP_TYPE_SYSTEM_CONTIG, ION_HEAP_TYPE_CARVEOUT, + ION_HEAP_TYPE_IOMMU, + ION_HEAP_TYPE_CP, ION_HEAP_TYPE_CUSTOM, /* must be last so device specific heaps always are at the end of this enum */ ION_NUM_HEAPS, @@ -43,29 +50,78 @@ enum ion_heap_type { #define ION_HEAP_SYSTEM_MASK (1 << ION_HEAP_TYPE_SYSTEM) #define ION_HEAP_SYSTEM_CONTIG_MASK (1 << ION_HEAP_TYPE_SYSTEM_CONTIG) #define ION_HEAP_CARVEOUT_MASK (1 << ION_HEAP_TYPE_CARVEOUT) +#define ION_HEAP_CP_MASK (1 << ION_HEAP_TYPE_CP) +/** + * heap flags - the lower 16 bits are used by core ion, the upper 16 + * bits are reserved for use by the heaps themselves. + */ +#define ION_FLAG_CACHED 1 /* mappings of this buffer should be + cached, ion will do cache + maintenance when the buffer is + mapped for dma */ /** * These are the only ids that should be used for Ion heap ids. * The ids listed are the order in which allocation will be attempted * if specified. Don't swap the order of heap ids unless you know what * you are doing! + * Id's are spaced by purpose to allow new Id's to be inserted in-between (for + * possible fallbacks) */ enum ion_heap_ids { - ION_HEAP_SYSTEM_ID, - ION_HEAP_SYSTEM_CONTIG_ID, - ION_HEAP_EBI_ID, - ION_HEAP_SMI_ID, - ION_HEAP_ADSP_ID, - ION_HEAP_AUDIO_ID, + INVALID_HEAP_ID = -1, + /* In a system with the "Mini Ion Upgrade" (such as this one) + * the heap_mask and caching flag end up sharing a spot in + * ion_allocation_data.flags. We should make sure to never use + * the 0th bit for a heap because that's where the caching bit + * ends up. + */ + ION_BOGUS_HEAP_DO_NOT_USE = 0, + ION_CP_MM_HEAP_ID = 8, + ION_CP_MFC_HEAP_ID = 12, + ION_CP_WB_HEAP_ID = 16, /* 8660 only */ + ION_CAMERA_HEAP_ID = 20, /* 8660 only */ + ION_SF_HEAP_ID = 24, + ION_IOMMU_HEAP_ID = 25, + ION_QSECOM_HEAP_ID = 27, + ION_AUDIO_HEAP_ID = 28, + + ION_MM_FIRMWARE_HEAP_ID = 29, + ION_SYSTEM_HEAP_ID = 30, + + ION_HEAP_ID_RESERVED = 31 /** Bit reserved for ION_SECURE flag */ +}; + +enum ion_fixed_position { + NOT_FIXED, + FIXED_LOW, + FIXED_MIDDLE, + FIXED_HIGH, }; -#define ION_KMALLOC_HEAP_NAME "kmalloc" +/** + * Flag to use when allocating to indicate that a heap is secure. + */ +#define ION_SECURE (1 << ION_HEAP_ID_RESERVED) + +/** + * Macro should be used with ion_heap_ids defined above. + */ +#define ION_HEAP(bit) (1 << (bit)) + #define ION_VMALLOC_HEAP_NAME "vmalloc" -#define ION_EBI1_HEAP_NAME "EBI1" -#define ION_ADSP_HEAP_NAME "adsp" -#define ION_SMI_HEAP_NAME "smi" +#define ION_AUDIO_HEAP_NAME "audio" +#define ION_SF_HEAP_NAME "sf" +#define ION_MM_HEAP_NAME "mm" +#define ION_CAMERA_HEAP_NAME "camera_preview" +#define ION_IOMMU_HEAP_NAME "iommu" +#define ION_MFC_HEAP_NAME "mfc" +#define ION_WB_HEAP_NAME "wb" +#define ION_MM_FIRMWARE_HEAP_NAME "mm_fw" +#define ION_QSECOM_HEAP_NAME "qsecom" +#define ION_FMEM_HEAP_NAME "fmem" #define CACHED 1 #define UNCACHED 0 @@ -76,6 +132,12 @@ enum ion_heap_ids { #define ION_IS_CACHED(__flags) ((__flags) & (1 << ION_CACHE_SHIFT)) +/* + * This flag allows clients when mapping into the IOMMU to specify to + * defer un-mapping from the IOMMU until the buffer memory is freed. + */ +#define ION_IOMMU_UNMAP_DELAYED 1 + #ifdef __KERNEL__ #include #include @@ -90,17 +152,19 @@ struct ion_buffer; be converted to phys_addr_t. For the time being many kernel interfaces do not accept phys_addr_t's that would have to */ #define ion_phys_addr_t unsigned long +#define ion_virt_addr_t unsigned long /** * struct ion_platform_heap - defines a heap in the given platform * @type: type of the heap from ion_heap_type enum - * @id: unique identifier for heap. When allocating (lower numbers + * @id: unique identifier for heap. When allocating (lower numbers * will be allocated from first) * @name: used for debug purposes * @base: base address of heap in physical memory if applicable * @size: size of the heap in bytes if applicable - * - * Provided by the board file. + * @memory_type:Memory type used for the heap + * @has_outer_cache: set to 1 if outer cache is used, 0 otherwise. + * @extra_data: Extra data specific to each heap type */ struct ion_platform_heap { enum ion_heap_type type; @@ -109,10 +173,79 @@ struct ion_platform_heap { ion_phys_addr_t base; size_t size; enum ion_memory_types memory_type; + unsigned int has_outer_cache; + void *extra_data; +}; + +/** + * struct ion_cp_heap_pdata - defines a content protection heap in the given + * platform + * @permission_type: Memory ID used to identify the memory to TZ + * @align: Alignment requirement for the memory + * @secure_base: Base address for securing the heap. + * Note: This might be different from actual base address + * of this heap in the case of a shared heap. + * @secure_size: Memory size for securing the heap. + * Note: This might be different from actual size + * of this heap in the case of a shared heap. + * @reusable Flag indicating whether this heap is reusable of not. + * (see FMEM) + * @mem_is_fmem Flag indicating whether this memory is coming from fmem + * or not. + * @fixed_position If nonzero, position in the fixed area. + * @virt_addr: Virtual address used when using fmem. + * @iommu_map_all: Indicates whether we should map whole heap into IOMMU. + * @iommu_2x_map_domain: Indicates the domain to use for overmapping. + * @request_region: function to be called when the number of allocations + * goes from 0 -> 1 + * @release_region: function to be called when the number of allocations + * goes from 1 -> 0 + * @setup_region: function to be called upon ion registration + * + */ +struct ion_cp_heap_pdata { + enum ion_permission_type permission_type; + unsigned int align; + ion_phys_addr_t secure_base; /* Base addr used when heap is shared */ + size_t secure_size; /* Size used for securing heap when heap is shared*/ + int reusable; + int mem_is_fmem; + enum ion_fixed_position fixed_position; + int iommu_map_all; + int iommu_2x_map_domain; + ion_virt_addr_t *virt_addr; + int (*request_region)(void *); + int (*release_region)(void *); + void *(*setup_region)(void); +}; + +/** + * struct ion_co_heap_pdata - defines a carveout heap in the given platform + * @adjacent_mem_id: Id of heap that this heap must be adjacent to. + * @align: Alignment requirement for the memory + * @mem_is_fmem Flag indicating whether this memory is coming from fmem + * or not. + * @fixed_position If nonzero, position in the fixed area. + * @request_region: function to be called when the number of allocations + * goes from 0 -> 1 + * @release_region: function to be called when the number of allocations + * goes from 1 -> 0 + * @setup_region: function to be called upon ion registration + * + */ +struct ion_co_heap_pdata { + int adjacent_mem_id; + unsigned int align; + int mem_is_fmem; + enum ion_fixed_position fixed_position; + int (*request_region)(void *); + int (*release_region)(void *); + void *(*setup_region)(void); }; /** * struct ion_platform_data - array of platform heaps passed from board file + * @has_outer_cache: set to 1 if outer cache is used, 0 otherwise. * @nr: number of structures in the array * @request_region: function to be called when the number of allocations goes * from 0 -> 1 @@ -124,9 +257,10 @@ struct ion_platform_heap { * Provided by the board file in the form of platform data to a platform device. */ struct ion_platform_data { + unsigned int has_outer_cache; int nr; - void (*request_region)(void *); - void (*release_region)(void *); + int (*request_region)(void *); + int (*release_region)(void *); void *(*setup_region)(void); struct ion_platform_heap heaps[]; }; @@ -298,6 +432,125 @@ struct ion_handle *ion_import_fd(struct ion_client *client, int fd); int ion_handle_get_flags(struct ion_client *client, struct ion_handle *handle, unsigned long *flags); + +/** + * ion_map_iommu - map the given handle into an iommu + * + * @client - client who allocated the handle + * @handle - handle to map + * @domain_num - domain number to map to + * @partition_num - partition number to allocate iova from + * @align - alignment for the iova + * @iova_length - length of iova to map. If the iova length is + * greater than the handle length, the remaining + * address space will be mapped to a dummy buffer. + * @iova - pointer to store the iova address + * @buffer_size - pointer to store the size of the buffer + * @flags - flags for options to map + * @iommu_flags - flags specific to the iommu. + * + * Maps the handle into the iova space specified via domain number. Iova + * will be allocated from the partition specified via partition_num. + * Returns 0 on success, negative value on error. + */ +int ion_map_iommu(struct ion_client *client, struct ion_handle *handle, + int domain_num, int partition_num, unsigned long align, + unsigned long iova_length, unsigned long *iova, + unsigned long *buffer_size, + unsigned long flags, unsigned long iommu_flags); + + +/** + * ion_handle_get_size - get the allocated size of a given handle + * + * @client - client who allocated the handle + * @handle - handle to get the size + * @size - pointer to store the size + * + * gives the allocated size of a handle. returns 0 on success, negative + * value on error + * + * NOTE: This is intended to be used only to get a size to pass to map_iommu. + * You should *NOT* rely on this for any other usage. + */ + +int ion_handle_get_size(struct ion_client *client, struct ion_handle *handle, + unsigned long *size); + +/** + * ion_unmap_iommu - unmap the handle from an iommu + * + * @client - client who allocated the handle + * @handle - handle to unmap + * @domain_num - domain to unmap from + * @partition_num - partition to unmap from + * + * Decrement the reference count on the iommu mapping. If the count is + * 0, the mapping will be removed from the iommu. + */ +void ion_unmap_iommu(struct ion_client *client, struct ion_handle *handle, + int domain_num, int partition_num); + + +/** + * ion_secure_heap - secure a heap + * + * @client - a client that has allocated from the heap heap_id + * @heap_id - heap id to secure. + * + * Secure a heap + * Returns 0 on success + */ +int ion_secure_heap(struct ion_device *dev, int heap_id); + +/** + * ion_unsecure_heap - un-secure a heap + * + * @client - a client that has allocated from the heap heap_id + * @heap_id - heap id to un-secure. + * + * Un-secure a heap + * Returns 0 on success + */ +int ion_unsecure_heap(struct ion_device *dev, int heap_id); + +/** + * msm_ion_secure_heap - secure a heap. Wrapper around ion_secure_heap. + * + * @heap_id - heap id to secure. + * + * Secure a heap + * Returns 0 on success + */ +int msm_ion_secure_heap(int heap_id); + +/** + * msm_ion_unsecure_heap - unsecure a heap. Wrapper around ion_unsecure_heap. + * + * @heap_id - heap id to secure. + * + * Un-secure a heap + * Returns 0 on success + */ +int msm_ion_unsecure_heap(int heap_id); + +/** + * msm_ion_do_cache_op - do cache operations. + * + * @client - pointer to ION client. + * @handle - pointer to buffer handle. + * @vaddr - virtual address to operate on. + * @len - Length of data to do cache operation on. + * @cmd - Cache operation to perform: + * ION_IOC_CLEAN_CACHES + * ION_IOC_INV_CACHES + * ION_IOC_CLEAN_INV_CACHES + * + * Returns 0 on success + */ +int msm_ion_do_cache_op(struct ion_client *client, struct ion_handle *handle, + void *vaddr, unsigned long len, unsigned int cmd); + #else static inline struct ion_client *ion_client_create(struct ion_device *dev, unsigned int heap_mask, const char *name) @@ -370,6 +623,54 @@ static inline int ion_handle_get_flags(struct ion_client *client, { return -ENODEV; } + +static inline int ion_map_iommu(struct ion_client *client, + struct ion_handle *handle, int domain_num, + int partition_num, unsigned long align, + unsigned long iova_length, unsigned long *iova, + unsigned long *buffer_size, + unsigned long flags, + unsigned long iommu_flags) +{ + return -ENODEV; +} + +static inline void ion_unmap_iommu(struct ion_client *client, + struct ion_handle *handle, int domain_num, + int partition_num) +{ + return; +} + +static inline int ion_secure_heap(struct ion_device *dev, int heap_id) +{ + return -ENODEV; + +} + +static inline int ion_unsecure_heap(struct ion_device *dev, int heap_id) +{ + return -ENODEV; +} + +static inline int msm_ion_secure_heap(int heap_id) +{ + return -ENODEV; + +} + +static inline int msm_ion_unsecure_heap(int heap_id) +{ + return -ENODEV; +} + +static inline int msm_ion_do_cache_op(struct ion_client *client, + struct ion_handle *handle, void *vaddr, + unsigned long len, unsigned int cmd) +{ + return -ENODEV; +} + #endif /* CONFIG_ION */ #endif /* __KERNEL__ */ @@ -394,6 +695,7 @@ static inline int ion_handle_get_flags(struct ion_client *client, struct ion_allocation_data { size_t len; size_t align; + unsigned int heap_mask; unsigned int flags; struct ion_handle *handle; }; @@ -438,6 +740,7 @@ struct ion_custom_data { /* struct ion_flush_data - data passed to ion for flushing caches * * @handle: handle with data to flush + * @fd: fd to flush * @vaddr: userspace virtual address mapped with mmap * @offset: offset into the handle to flush * @length: length of handle to flush @@ -448,6 +751,7 @@ struct ion_custom_data { */ struct ion_flush_data { struct ion_handle *handle; + int fd; void *vaddr; unsigned int offset; unsigned int length; @@ -512,7 +816,7 @@ struct ion_flag_data { * descriptor obtained from ION_IOC_SHARE and returns the struct with the handle * filed set to the corresponding opaque handle. */ -#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, int) +#define ION_IOC_IMPORT _IOWR(ION_IOC_MAGIC, 5, struct ion_fd_data) /** * DOC: ION_IOC_CUSTOM - call architecture specific ion ioctl @@ -528,21 +832,21 @@ struct ion_flag_data { * * Clean the caches of the handle specified. */ -#define ION_IOC_CLEAN_CACHES _IOWR(ION_IOC_MAGIC, 7, \ +#define ION_IOC_CLEAN_CACHES _IOWR(ION_IOC_MAGIC, 20, \ struct ion_flush_data) /** * DOC: ION_MSM_IOC_INV_CACHES - invalidate the caches * * Invalidate the caches of the handle specified. */ -#define ION_IOC_INV_CACHES _IOWR(ION_IOC_MAGIC, 8, \ +#define ION_IOC_INV_CACHES _IOWR(ION_IOC_MAGIC, 21, \ struct ion_flush_data) /** * DOC: ION_MSM_IOC_CLEAN_CACHES - clean and invalidate the caches * * Clean and invalidate the caches of the handle specified. */ -#define ION_IOC_CLEAN_INV_CACHES _IOWR(ION_IOC_MAGIC, 9, \ +#define ION_IOC_CLEAN_INV_CACHES _IOWR(ION_IOC_MAGIC, 22, \ struct ion_flush_data) /** @@ -551,6 +855,15 @@ struct ion_flag_data { * Gets the flags of the current handle which indicate cachability, * secure state etc. */ -#define ION_IOC_GET_FLAGS _IOWR(ION_IOC_MAGIC, 10, \ +#define ION_IOC_GET_FLAGS _IOWR(ION_IOC_MAGIC, 23, \ + struct ion_flag_data) + + +/** + * DOC: ION_IOC_SYNC - BOGUS + * + * NOT SUPPORTED + */ +#define ION_IOC_SYNC _IOWR(ION_IOC_MAGIC, 42, \ struct ion_flag_data) #endif /* _LINUX_ION_H */ From 29bce0743feb048325a42866567c0566fae83055 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 3 Jan 2011 18:59:43 -0800 Subject: [PATCH 2483/2556] timers: Add rb_init_node() to allow for stack allocated rb nodes In cases where a timerqueue_node or some structure that utilizes a timerqueue_node is allocated on the stack, gcc would give warnings caused by the timerqueue_init()'s calling RB_CLEAR_NODE, which self-references the nodes uninitialized data. The solution is to create an rb_init_node() function that zeros the rb_node structure out and then calls RB_CLEAR_NODE(), and then call the new init function from timerqueue_init(). CC: Thomas Gleixner Acked-by: Arnd Bergmann Signed-off-by: John Stultz --- include/linux/rbtree.h | 8 ++++++++ include/linux/timerqueue.h | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/linux/rbtree.h b/include/linux/rbtree.h index 7066acb2c5307..033b507b33b17 100644 --- a/include/linux/rbtree.h +++ b/include/linux/rbtree.h @@ -136,6 +136,14 @@ static inline void rb_set_color(struct rb_node *rb, int color) #define RB_EMPTY_NODE(node) (rb_parent(node) == node) #define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) +static inline void rb_init_node(struct rb_node *rb) +{ + rb->rb_parent_color = 0; + rb->rb_right = NULL; + rb->rb_left = NULL; + RB_CLEAR_NODE(rb); +} + extern void rb_insert_color(struct rb_node *, struct rb_root *); extern void rb_erase(struct rb_node *, struct rb_root *); diff --git a/include/linux/timerqueue.h b/include/linux/timerqueue.h index d24aabaca4741..98b0a6c3c21cf 100644 --- a/include/linux/timerqueue.h +++ b/include/linux/timerqueue.h @@ -39,7 +39,7 @@ struct timerqueue_node *timerqueue_getnext(struct timerqueue_head *head) static inline void timerqueue_init(struct timerqueue_node *node) { - RB_CLEAR_NODE(&node->node); + rb_init_node(&node->node); } static inline void timerqueue_init_head(struct timerqueue_head *head) From 45f9ed6ecb3372ce024b8253d569ece703e90d1d Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 18 Apr 2013 16:39:27 -0500 Subject: [PATCH 2484/2556] extra files for ion compile Change-Id: Ibf1caa452f9bd7899a8108f24e70d848b6ae5eb9 --- arch/arm/mach-msm/include/mach/scm.h | 77 ++++++++++++++++++++++++++++ include/linux/fmem.h | 62 ++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 arch/arm/mach-msm/include/mach/scm.h create mode 100644 include/linux/fmem.h diff --git a/arch/arm/mach-msm/include/mach/scm.h b/arch/arm/mach-msm/include/mach/scm.h new file mode 100644 index 0000000000000..3374fb7c846fa --- /dev/null +++ b/arch/arm/mach-msm/include/mach/scm.h @@ -0,0 +1,77 @@ +/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __MACH_SCM_H +#define __MACH_SCM_H + +#define SCM_SVC_BOOT 0x1 +#define SCM_SVC_PIL 0x2 +#define SCM_SVC_UTIL 0x3 +#define SCM_SVC_TZ 0x4 +#define SCM_SVC_IO 0x5 +#define SCM_SVC_INFO 0x6 +#define SCM_SVC_SSD 0x7 +#define SCM_SVC_FUSE 0x8 +#define SCM_SVC_PWR 0x9 +#define SCM_SVC_CP 0xC +#define SCM_SVC_DCVS 0xD +#define SCM_SVC_TZSCHEDULER 0xFC + +#ifdef CONFIG_MSM_SCM +extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, + void *resp_buf, size_t resp_len); + +extern s32 scm_call_atomic1(u32 svc, u32 cmd, u32 arg1); +extern s32 scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2); +extern s32 scm_call_atomic4_3(u32 svc, u32 cmd, u32 arg1, u32 arg2, u32 arg3, + u32 arg4, u32 *ret1, u32 *ret2); + +#define SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) + +extern u32 scm_get_version(void); +extern int scm_is_call_available(u32 svc_id, u32 cmd_id); + +#else + +static inline int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, + size_t cmd_len, void *resp_buf, size_t resp_len) +{ + return 0; +} + +static inline s32 scm_call_atomic1(u32 svc, u32 cmd, u32 arg1) +{ + return 0; +} + +static inline s32 scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2) +{ + return 0; +} + +static inline s32 scm_call_atomic4_3(u32 svc, u32 cmd, u32 arg1, u32 arg2, + u32 arg3, u32 arg4, u32 *ret1, u32 *ret2) +{ + return 0; +} + +static inline u32 scm_get_version(void) +{ + return 0; +} + +static inline int scm_is_call_available(u32 svc_id, u32 cmd_id) +{ + return 0; +} + +#endif +#endif diff --git a/include/linux/fmem.h b/include/linux/fmem.h new file mode 100644 index 0000000000000..cda4a0fe13d68 --- /dev/null +++ b/include/linux/fmem.h @@ -0,0 +1,62 @@ +/* + * + * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _FMEM_H_ +#define _FMEM_H_ + +#include + +struct fmem_platform_data { + unsigned long phys; + unsigned long size; + unsigned long reserved_size_low; + unsigned long reserved_size_high; + unsigned long align; +}; + +struct fmem_data { + unsigned long phys; + void *virt; + struct vm_struct *area; + unsigned long size; + unsigned long reserved_size_low; + unsigned long reserved_size_high; +}; + +enum fmem_state { + FMEM_UNINITIALIZED = 0, + FMEM_C_STATE, + FMEM_T_STATE, + FMEM_O_STATE, +}; + +#ifdef CONFIG_QCACHE +struct fmem_data *fmem_get_info(void); +int fmem_set_state(enum fmem_state); +void lock_fmem_state(void); +void unlock_fmem_state(void); +void *fmem_map_virtual_area(int cacheability); +void fmem_unmap_virtual_area(void); +#else +static inline struct fmem_data *fmem_get_info(void) { return NULL; } +static inline int fmem_set_state(enum fmem_state f) { return -ENODEV; } +static inline void lock_fmem_state(void) { return; } +static inline void unlock_fmem_state(void) { return; } +static inline void *fmem_map_virtual_area(int cacheability) { return NULL; } +static inline void fmem_unmap_virtual_area(void) { return; } +#endif + +int request_fmem_c_region(void *unused); +int release_fmem_c_region(void *unused); +#endif From 9801fdeb0f63c879e31040eace51b8a521d856a7 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 18 Apr 2013 16:39:38 -0500 Subject: [PATCH 2485/2556] mahimahi: use ion for sf heap Change-Id: I90cfaa6467b5bd19afcd81554865adf003bba1bf --- arch/arm/configs/evervolv_mahimahi_defconfig | 5 +- arch/arm/mach-msm/board-mahimahi.c | 77 ++++++++++++++++---- 2 files changed, 64 insertions(+), 18 deletions(-) diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index 3fee7a8bea31b..d8b5e44d0acad 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Thu Apr 18 00:32:01 2013 +# Thu Apr 18 16:22:01 2013 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -1481,7 +1481,8 @@ CONFIG_S5K3E2FX=y # Graphics support # # CONFIG_DRM is not set -# CONFIG_ION is not set +CONFIG_ION=y +CONFIG_ION_MSM=y CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set diff --git a/arch/arm/mach-msm/board-mahimahi.c b/arch/arm/mach-msm/board-mahimahi.c index b98a27b747d85..4f0cbbffe0e17 100644 --- a/arch/arm/mach-msm/board-mahimahi.c +++ b/arch/arm/mach-msm/board-mahimahi.c @@ -64,6 +64,7 @@ #include #include #include "footswitch.h" +#include static uint debug_uart; @@ -326,6 +327,8 @@ struct platform_device *msm_footswitch_devices[] = { unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices); /* end footswitch regulator */ +/* pmem heaps */ +#ifndef CONFIG_ION_MSM static struct android_pmem_platform_data mdp_pmem_pdata = { .name = "pmem", .start = MSM_PMEM_MDP_BASE, @@ -335,6 +338,15 @@ static struct android_pmem_platform_data mdp_pmem_pdata = { .cached = 1, }; +static struct platform_device android_pmem_mdp_device = { + .name = "android_pmem", + .id = 0, + .dev = { + .platform_data = &mdp_pmem_pdata + }, +}; +#endif + static struct android_pmem_platform_data android_pmem_adsp_pdata = { .name = "pmem_adsp", .start = MSM_PMEM_ADSP_BASE, @@ -344,6 +356,14 @@ static struct android_pmem_platform_data android_pmem_adsp_pdata = { .cached = 1, }; +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 1, + .dev = { + .platform_data = &android_pmem_adsp_pdata, + }, +}; + static struct android_pmem_platform_data android_pmem_venc_pdata = { .name = "pmem_venc", .start = MSM_PMEM_VENC_BASE, @@ -353,22 +373,6 @@ static struct android_pmem_platform_data android_pmem_venc_pdata = { .cached = 1, }; -static struct platform_device android_pmem_mdp_device = { - .name = "android_pmem", - .id = 0, - .dev = { - .platform_data = &mdp_pmem_pdata - }, -}; - -static struct platform_device android_pmem_adsp_device = { - .name = "android_pmem", - .id = 1, - .dev = { - .platform_data = &android_pmem_adsp_pdata, - }, -}; - static struct platform_device android_pmem_venc_device = { .name = "android_pmem", .id = 3, @@ -376,6 +380,43 @@ static struct platform_device android_pmem_venc_device = { .platform_data = &android_pmem_venc_pdata, }, }; +/* end pmem heaps */ + +/* ion heaps */ +#ifdef CONFIG_ION_MSM +static struct ion_co_heap_pdata co_ion_pdata = { + .adjacent_mem_id = INVALID_HEAP_ID, + .align = PAGE_SIZE, +}; + +static struct ion_platform_data ion_pdata = { + .nr = 2, + .heaps = { + { + .id = ION_SYSTEM_HEAP_ID, + .type = ION_HEAP_TYPE_SYSTEM, + .name = ION_VMALLOC_HEAP_NAME, + }, + /* PMEM_MDP = SF */ + { + .id = ION_SF_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_SF_HEAP_NAME, + .base = MSM_PMEM_MDP_BASE, + .size = MSM_PMEM_MDP_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_ion_pdata, + }, + } +}; + +static struct platform_device ion_dev = { + .name = "ion-msm", + .id = 1, + .dev = { .platform_data = &ion_pdata }, +}; +#endif +/* end ion heaps */ static struct resource ram_console_resources[] = { { @@ -858,7 +899,11 @@ static struct platform_device *devices[] __initdata = { &rndis_device, #endif &android_usb_device, +#ifndef CONFIG_ION_MSM &android_pmem_mdp_device, +#else + &ion_dev, +#endif &android_pmem_adsp_device, #ifdef CONFIG_720P_CAMERA &android_pmem_venc_device, From 2993205ed32c7cc425dd13666454d473c1df1adc Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 15 Feb 2011 12:41:49 +0100 Subject: [PATCH 2486/2556] ARM: 6671/1: LPAE: use phys_addr_t instead of unsigned long in outercache functions The unsigned long datatype is not sufficient for mapping physical addresses >= 4GB. This patch ensures that the phys_addr_t datatype is used to represent physical addresses when passed to the outer cache functions. Note that the definitions in struct outer_cache_fns remain as unsigned long because there are currently no outer cache implementations supporting physical addresses wider than 32-bits. Acked-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/include/asm/outercache.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h index 348d513afa923..d8387437ec5aa 100644 --- a/arch/arm/include/asm/outercache.h +++ b/arch/arm/include/asm/outercache.h @@ -21,6 +21,8 @@ #ifndef __ASM_OUTERCACHE_H #define __ASM_OUTERCACHE_H +#include + struct outer_cache_fns { void (*inv_range)(unsigned long, unsigned long); void (*clean_range)(unsigned long, unsigned long); @@ -38,17 +40,17 @@ struct outer_cache_fns { extern struct outer_cache_fns outer_cache; -static inline void outer_inv_range(unsigned long start, unsigned long end) +static inline void outer_inv_range(phys_addr_t start, phys_addr_t end) { if (outer_cache.inv_range) outer_cache.inv_range(start, end); } -static inline void outer_clean_range(unsigned long start, unsigned long end) +static inline void outer_clean_range(phys_addr_t start, phys_addr_t end) { if (outer_cache.clean_range) outer_cache.clean_range(start, end); } -static inline void outer_flush_range(unsigned long start, unsigned long end) +static inline void outer_flush_range(phys_addr_t start, phys_addr_t end) { if (outer_cache.flush_range) outer_cache.flush_range(start, end); @@ -74,11 +76,11 @@ static inline void outer_disable(void) #else -static inline void outer_inv_range(unsigned long start, unsigned long end) +static inline void outer_inv_range(phys_addr_t start, phys_addr_t end) { } -static inline void outer_clean_range(unsigned long start, unsigned long end) +static inline void outer_clean_range(phys_addr_t start, phys_addr_t end) { } -static inline void outer_flush_range(unsigned long start, unsigned long end) +static inline void outer_flush_range(phys_addr_t start, phys_addr_t end) { } static inline void outer_flush_all(void) { } static inline void outer_inv_all(void) { } From 92c2e390c356d492597e4887efa122ae099ccd96 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 23 Apr 2013 15:09:35 -0500 Subject: [PATCH 2487/2556] msm: boards: add ion sf heap Change-Id: I993e4bb2dd632054e810fba89fa6c85f2e0aff93 --- arch/arm/mach-msm/board-bravo.c | 75 ++++++++++++--- arch/arm/mach-msm/board-incrediblec.c | 92 ++++++++++++++----- arch/arm/mach-msm/board-supersonic.c | 127 +++++++++++++++++--------- 3 files changed, 211 insertions(+), 83 deletions(-) diff --git a/arch/arm/mach-msm/board-bravo.c b/arch/arm/mach-msm/board-bravo.c index e268b400e1445..8bf88229a3220 100644 --- a/arch/arm/mach-msm/board-bravo.c +++ b/arch/arm/mach-msm/board-bravo.c @@ -66,6 +66,7 @@ #include #include #include "footswitch.h" +#include #ifdef CONFIG_OPTICALJOYSTICK_CRUCIAL #include @@ -334,6 +335,8 @@ struct platform_device *msm_footswitch_devices[] = { unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices); /* end footswitch regulator */ +/* pmem heaps */ +#ifndef CONFIG_ION_MSM static struct android_pmem_platform_data mdp_pmem_pdata = { .name = "pmem", .start = MSM_PMEM_MDP_BASE, @@ -343,6 +346,15 @@ static struct android_pmem_platform_data mdp_pmem_pdata = { .cached = 1, }; +static struct platform_device android_pmem_mdp_device = { + .name = "android_pmem", + .id = 0, + .dev = { + .platform_data = &mdp_pmem_pdata + }, +}; +#endif + static struct android_pmem_platform_data android_pmem_adsp_pdata = { .name = "pmem_adsp", .start = MSM_PMEM_ADSP_BASE, @@ -352,6 +364,14 @@ static struct android_pmem_platform_data android_pmem_adsp_pdata = { .cached = 1, }; +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 1, + .dev = { + .platform_data = &android_pmem_adsp_pdata, + }, +}; + static struct android_pmem_platform_data android_pmem_venc_pdata = { .name = "pmem_venc", .start = MSM_PMEM_VENC_BASE, @@ -361,30 +381,51 @@ static struct android_pmem_platform_data android_pmem_venc_pdata = { .cached = 1, }; -static struct platform_device android_pmem_mdp_device = { +static struct platform_device android_pmem_venc_device = { .name = "android_pmem", - .id = 0, + .id = 3, .dev = { - .platform_data = &mdp_pmem_pdata + .platform_data = &android_pmem_venc_pdata, }, }; +/* end pmem heaps */ -static struct platform_device android_pmem_adsp_device = { - .name = "android_pmem", - .id = 1, - .dev = { - .platform_data = &android_pmem_adsp_pdata, - }, +/* ion heaps */ +#ifdef CONFIG_ION_MSM +static struct ion_co_heap_pdata co_ion_pdata = { + .adjacent_mem_id = INVALID_HEAP_ID, + .align = PAGE_SIZE, }; -static struct platform_device android_pmem_venc_device = { - .name = "android_pmem", - .id = 3, - .dev = { - .platform_data = &android_pmem_venc_pdata, - }, +static struct ion_platform_data ion_pdata = { + .nr = 2, + .heaps = { + { + .id = ION_SYSTEM_HEAP_ID, + .type = ION_HEAP_TYPE_SYSTEM, + .name = ION_VMALLOC_HEAP_NAME, + }, + /* PMEM_MDP = SF */ + { + .id = ION_SF_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_SF_HEAP_NAME, + .base = MSM_PMEM_MDP_BASE, + .size = MSM_PMEM_MDP_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_ion_pdata, + }, + } }; +static struct platform_device ion_dev = { + .name = "ion-msm", + .id = 1, + .dev = { .platform_data = &ion_pdata }, +}; +#endif +/* end ion heaps */ + static struct resource ram_console_resources[] = { { .start = MSM_RAM_CONSOLE_BASE, @@ -1021,7 +1062,11 @@ static struct platform_device *devices[] __initdata = { &rndis_device, #endif &android_usb_device, +#ifndef CONFIG_ION_MSM &android_pmem_mdp_device, +#else + &ion_dev, +#endif &android_pmem_adsp_device, #ifdef CONFIG_720P_CAMERA &android_pmem_venc_device, diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index 43e1078d24921..a563f6d5d3d48 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -67,6 +67,7 @@ #include #include #include "footswitch.h" +#include #define SMEM_SPINLOCK_I2C 6 #define INCREDIBLEC_MICROP_VER 0x04 @@ -538,6 +539,8 @@ struct platform_device *msm_footswitch_devices[] = { unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices); /* end footswitch regulator */ +/* pmem heaps */ +#ifndef CONFIG_ION_MSM static struct android_pmem_platform_data mdp_pmem_pdata = { .name = "pmem", .start = MSM_PMEM_MDP_BASE, @@ -547,6 +550,15 @@ static struct android_pmem_platform_data mdp_pmem_pdata = { .cached = 1, }; +static struct platform_device android_pmem_mdp_device = { + .name = "android_pmem", + .id = 0, + .dev = { + .platform_data = &mdp_pmem_pdata + }, +}; +#endif + static struct android_pmem_platform_data android_pmem_adsp_pdata = { .name = "pmem_adsp", .start = MSM_PMEM_ADSP_BASE, @@ -556,6 +568,14 @@ static struct android_pmem_platform_data android_pmem_adsp_pdata = { .cached = 1, }; +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 4, + .dev = { + .platform_data = &android_pmem_adsp_pdata, + }, +}; + #ifdef CONFIG_720P_CAMERA static struct android_pmem_platform_data android_pmem_venc_pdata = { .name = "pmem_venc", @@ -565,6 +585,14 @@ static struct android_pmem_platform_data android_pmem_venc_pdata = { .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 1, }; + +static struct platform_device android_pmem_venc_device = { + .name = "android_pmem", + .id = 5, + .dev = { + .platform_data = &android_pmem_venc_pdata, + }, +}; #else static struct android_pmem_platform_data android_pmem_camera_pdata = { .name = "pmem_camera", @@ -574,42 +602,52 @@ static struct android_pmem_platform_data android_pmem_camera_pdata = { .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 1, }; -#endif -static struct platform_device android_pmem_mdp_device = { +static struct platform_device android_pmem_camera_device = { .name = "android_pmem", - .id = 0, + .id = 5, .dev = { - .platform_data = &mdp_pmem_pdata + .platform_data = &android_pmem_camera_pdata, }, }; +#endif +/* end pmem heaps */ -static struct platform_device android_pmem_adsp_device = { - .name = "android_pmem", - .id = 4, - .dev = { - .platform_data = &android_pmem_adsp_pdata, - }, +/* ion heaps */ +#ifdef CONFIG_ION_MSM +static struct ion_co_heap_pdata co_ion_pdata = { + .adjacent_mem_id = INVALID_HEAP_ID, + .align = PAGE_SIZE, }; -#ifdef CONFIG_720P_CAMERA -static struct platform_device android_pmem_venc_device = { - .name = "android_pmem", - .id = 5, - .dev = { - .platform_data = &android_pmem_venc_pdata, - }, -}; -#else -static struct platform_device android_pmem_camera_device = { - .name = "android_pmem", - .id = 5, - .dev = { - .platform_data = &android_pmem_camera_pdata, - }, +static struct ion_platform_data ion_pdata = { + .nr = 2, + .heaps = { + { + .id = ION_SYSTEM_HEAP_ID, + .type = ION_HEAP_TYPE_SYSTEM, + .name = ION_VMALLOC_HEAP_NAME, + }, + /* PMEM_MDP = SF */ + { + .id = ION_SF_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_SF_HEAP_NAME, + .base = MSM_PMEM_MDP_BASE, + .size = MSM_PMEM_MDP_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_ion_pdata, + }, + } }; +static struct platform_device ion_dev = { + .name = "ion-msm", + .id = 1, + .dev = { .platform_data = &ion_pdata }, +}; #endif +/* end ion heaps */ static struct resource ram_console_resources[] = { { @@ -1138,7 +1176,11 @@ static struct platform_device *devices[] __initdata = { &rndis_device, #endif &android_usb_device, +#ifndef CONFIG_ION_MSM &android_pmem_mdp_device, +#else + &ion_dev, +#endif &android_pmem_adsp_device, #ifdef CONFIG_720P_CAMERA &android_pmem_venc_device, diff --git a/arch/arm/mach-msm/board-supersonic.c b/arch/arm/mach-msm/board-supersonic.c index b4d58aa30b405..260654d5b9b97 100644 --- a/arch/arm/mach-msm/board-supersonic.c +++ b/arch/arm/mach-msm/board-supersonic.c @@ -76,6 +76,7 @@ #include #include #include "footswitch.h" +#include #define SMEM_SPINLOCK_I2C 6 @@ -571,6 +572,8 @@ struct platform_device *msm_footswitch_devices[] = { unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices); /* end footswitch regulator */ +/* pmem heaps */ +#ifndef CONFIG_ION_MSM static struct android_pmem_platform_data mdp_pmem_pdata = { .name = "pmem", .start = MSM_PMEM_MDP_BASE, @@ -580,6 +583,15 @@ static struct android_pmem_platform_data mdp_pmem_pdata = { .cached = 1, }; +static struct platform_device android_pmem_mdp_device = { + .name = "android_pmem", + .id = 0, + .dev = { + .platform_data = &mdp_pmem_pdata + }, +}; +#endif + static struct android_pmem_platform_data android_pmem_adsp_pdata = { .name = "pmem_adsp", .start = MSM_PMEM_ADSP_BASE, @@ -589,6 +601,14 @@ static struct android_pmem_platform_data android_pmem_adsp_pdata = { .cached = 1, }; +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 4, + .dev = { + .platform_data = &android_pmem_adsp_pdata, + }, +}; + static struct android_pmem_platform_data android_pmem_venc_pdata = { .name = "pmem_venc", .start = MSM_PMEM_VENC_BASE, @@ -598,6 +618,14 @@ static struct android_pmem_platform_data android_pmem_venc_pdata = { .cached = 1, }; +static struct platform_device android_pmem_venc_device = { + .name = "android_pmem", + .id = 6, + .dev = { + .platform_data = &android_pmem_venc_pdata, + }, +}; + #ifdef CONFIG_BUILD_CIQ static struct android_pmem_platform_data android_pmem_ciq_pdata = { .name = "pmem_ciq", @@ -608,6 +636,12 @@ static struct android_pmem_platform_data android_pmem_ciq_pdata = { .cached = 0, }; +static struct platform_device android_pmem_ciq_device = { + .name = "android_pmem", + .id = 7, + .dev = { .platform_data = &android_pmem_ciq_pdata }, +}; + static struct android_pmem_platform_data android_pmem_ciq1_pdata = { .name = "pmem_ciq1", .start = MSM_PMEM_CIQ1_BASE, @@ -617,6 +651,12 @@ static struct android_pmem_platform_data android_pmem_ciq1_pdata = { .cached = 0, }; +static struct platform_device android_pmem_ciq1_device = { + .name = "android_pmem", + .id = 8, + .dev = { .platform_data = &android_pmem_ciq1_pdata }, +}; + static struct android_pmem_platform_data android_pmem_ciq2_pdata = { .name = "pmem_ciq2", .start = MSM_PMEM_CIQ2_BASE, @@ -626,6 +666,12 @@ static struct android_pmem_platform_data android_pmem_ciq2_pdata = { .cached = 0, }; +static struct platform_device android_pmem_ciq2_device = { + .name = "android_pmem", + .id = 9, + .dev = { .platform_data = &android_pmem_ciq2_pdata }, +}; + static struct android_pmem_platform_data android_pmem_ciq3_pdata = { .name = "pmem_ciq3", .start = MSM_PMEM_CIQ3_BASE, @@ -634,59 +680,50 @@ static struct android_pmem_platform_data android_pmem_ciq3_pdata = { .allocator_type = PMEM_ALLOCATORTYPE_BITMAP, .cached = 0, }; -#endif -static struct platform_device android_pmem_mdp_device = { - .name = "android_pmem", - .id = 0, - .dev = { - .platform_data = &mdp_pmem_pdata - }, -}; - -static struct platform_device android_pmem_adsp_device = { - .name = "android_pmem", - .id = 4, - .dev = { - .platform_data = &android_pmem_adsp_pdata, - }, -}; - -static struct platform_device android_pmem_venc_device = { - .name = "android_pmem", - .id = 6, - .dev = { - .platform_data = &android_pmem_venc_pdata, - }, -}; - -#ifdef CONFIG_BUILD_CIQ -static struct platform_device android_pmem_ciq_device = { +static struct platform_device android_pmem_ciq3_device = { .name = "android_pmem", - .id = 7, - .dev = { .platform_data = &android_pmem_ciq_pdata }, + .id = 10, + .dev = { .platform_data = &android_pmem_ciq3_pdata }, }; +#endif +/* end pmem heaps */ -static struct platform_device android_pmem_ciq1_device = { - .name = "android_pmem", - .id = 8, - .dev = { .platform_data = &android_pmem_ciq1_pdata }, +/* ion heaps */ +#ifdef CONFIG_ION_MSM +static struct ion_co_heap_pdata co_ion_pdata = { + .adjacent_mem_id = INVALID_HEAP_ID, + .align = PAGE_SIZE, }; -static struct platform_device android_pmem_ciq2_device = { - .name = "android_pmem", - .id = 9, - .dev = { .platform_data = &android_pmem_ciq2_pdata }, +static struct ion_platform_data ion_pdata = { + .nr = 2, + .heaps = { + { + .id = ION_SYSTEM_HEAP_ID, + .type = ION_HEAP_TYPE_SYSTEM, + .name = ION_VMALLOC_HEAP_NAME, + }, + /* PMEM_MDP = SF */ + { + .id = ION_SF_HEAP_ID, + .type = ION_HEAP_TYPE_CARVEOUT, + .name = ION_SF_HEAP_NAME, + .base = MSM_PMEM_MDP_BASE, + .size = MSM_PMEM_MDP_SIZE, + .memory_type = ION_EBI_TYPE, + .extra_data = (void *)&co_ion_pdata, + }, + } }; -static struct platform_device android_pmem_ciq3_device = { - .name = "android_pmem", - .id = 10, - .dev = { .platform_data = &android_pmem_ciq3_pdata }, +static struct platform_device ion_dev = { + .name = "ion-msm", + .id = 1, + .dev = { .platform_data = &ion_pdata }, }; #endif - - +/* end ion heaps */ static struct resource ram_console_resources[] = { { @@ -1454,7 +1491,11 @@ static struct platform_device *devices[] __initdata = { &rndis_device, #endif &android_usb_device, +#ifndef CONFIG_ION_MSM &android_pmem_mdp_device, +#else + &ion_dev, +#endif &android_pmem_adsp_device, // &android_pmem_camera_device, #ifdef CONFIG_720P_CAMERA From 06894957db47cb8266ad6212eb94ffdccd8c98f0 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 23 Apr 2013 15:09:50 -0500 Subject: [PATCH 2488/2556] configs: enable ion Change-Id: I43ddcf7654c79b883ac36256a205a59a21c08c10 --- arch/arm/configs/evervolv_bravo_defconfig | 3 ++- arch/arm/configs/evervolv_incrediblec_defconfig | 3 ++- arch/arm/configs/evervolv_supersonic_defconfig | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index 7308102263f64..eb8984658f21f 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -1483,7 +1483,8 @@ CONFIG_S5K3E2FX=y # Graphics support # # CONFIG_DRM is not set -# CONFIG_ION is not set +CONFIG_ION=y +CONFIG_ION_MSM=y CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index 2278c17f1c047..6450ce59d43f4 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -1502,7 +1502,8 @@ CONFIG_OV8810=y # Graphics support # # CONFIG_DRM is not set -# CONFIG_ION is not set +CONFIG_ION=y +CONFIG_ION_MSM=y CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index 068b296d8b7d3..f4db5c34f061f 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -1485,7 +1485,8 @@ CONFIG_S5K3H1GX=y # Graphics support # # CONFIG_DRM is not set -# CONFIG_ION is not set +CONFIG_ION=y +CONFIG_ION_MSM=y CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set From 59b38f06bcad48b0b1c0521aee73c4942591fcb1 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 23 Apr 2013 15:28:43 -0500 Subject: [PATCH 2489/2556] configs: update localversions Change-Id: Iebbcd30ca5b30c460b5a314f0ed5b169491b7c4f --- arch/arm/configs/evervolv_bravo_defconfig | 4 ++-- arch/arm/configs/evervolv_incrediblec_defconfig | 4 ++-- arch/arm/configs/evervolv_mahimahi_defconfig | 4 ++-- arch/arm/configs/evervolv_supersonic_defconfig | 4 ++-- arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig | 4 ++-- arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig | 4 ++-- arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig | 4 ++-- arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index eb8984658f21f..06bf33a14c8e0 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -33,8 +33,8 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" -CONFIG_LOCALVERSION="-evervolv-turba-jellybean" -# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_LOCALVERSION="-evervolv" +CONFIG_LOCALVERSION_AUTO=y CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y CONFIG_HAVE_KERNEL_XZ=y diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index 6450ce59d43f4..849ac32d49ffd 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -33,8 +33,8 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="arm-eabi-" -CONFIG_LOCALVERSION="-evervolv-dives-jellybean" -# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_LOCALVERSION="-evervolv" +CONFIG_LOCALVERSION_AUTO=y CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y CONFIG_HAVE_KERNEL_XZ=y diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index d8b5e44d0acad..c6f74f3f18365 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -33,8 +33,8 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" -CONFIG_LOCALVERSION="-evervolv-perdo-jellybean" -# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_LOCALVERSION="-evervolv" +CONFIG_LOCALVERSION_AUTO=y CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y CONFIG_HAVE_KERNEL_XZ=y diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index f4db5c34f061f..b3cad91579615 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -34,8 +34,8 @@ CONFIG_BROKEN_ON_SMP=y CONFIG_LOCK_KERNEL=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" -CONFIG_LOCALVERSION="-evervolv-acies-jellybean" -# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_LOCALVERSION="-evervolv" +CONFIG_LOCALVERSION_AUTO=y CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y CONFIG_HAVE_KERNEL_XZ=y diff --git a/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig b/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig index c99a862255c4d..c4ab0e9d1905a 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig @@ -33,8 +33,8 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" -CONFIG_LOCALVERSION="-evervolv-turba-jellybean" -# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_LOCALVERSION="-evervolv-up" +CONFIG_LOCALVERSION_AUTO=y CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y CONFIG_HAVE_KERNEL_XZ=y diff --git a/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig b/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig index f7b6593ed380c..fd096354c8903 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig @@ -33,8 +33,8 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="arm-eabi-" -CONFIG_LOCALVERSION="-evervolv-dives-jellybean" -# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_LOCALVERSION="-evervolv-up" +CONFIG_LOCALVERSION_AUTO=y CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y CONFIG_HAVE_KERNEL_XZ=y diff --git a/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig b/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig index da50e332baf07..c21f27fd7d45d 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig @@ -33,8 +33,8 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" -CONFIG_LOCALVERSION="-evervolv-perdo-jellybean" -# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_LOCALVERSION="-evervolv-up" +CONFIG_LOCALVERSION_AUTO=y CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y CONFIG_HAVE_KERNEL_XZ=y diff --git a/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig b/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig index a022f4b544ba0..5b96267b3fb4a 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig @@ -34,8 +34,8 @@ CONFIG_BROKEN_ON_SMP=y CONFIG_LOCK_KERNEL=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="" -CONFIG_LOCALVERSION="-evervolv-acies-jellybean" -# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_LOCALVERSION="-evervolv-up" +CONFIG_LOCALVERSION_AUTO=y CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y CONFIG_HAVE_KERNEL_XZ=y From 3cbba754acc6a6edcc3f5d4eca1279be177db2f8 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 23 Apr 2013 20:33:41 -0500 Subject: [PATCH 2490/2556] msm_mdp: checkout jb_mr1_chocolate Change-Id: Ie09d54b8ac02ffa61598c6979b88bf23de23fa12 --- include/linux/msm_mdp.h | 210 +++++++++++++++++++++++++++++----------- 1 file changed, 156 insertions(+), 54 deletions(-) diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h index 0ac1cf11be313..c024ffbdd5f6b 100644 --- a/include/linux/msm_mdp.h +++ b/include/linux/msm_mdp.h @@ -1,7 +1,7 @@ /* include/linux/msm_mdp.h * * Copyright (C) 2007 Google Incorporated - * Copyright (c) 2012 Code Aurora Forum. All rights reserved. + * Copyright (c) 2013, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -32,8 +32,11 @@ #define MSMFB_OVERLAY_SET _IOWR(MSMFB_IOCTL_MAGIC, 135, \ struct mdp_overlay) #define MSMFB_OVERLAY_UNSET _IOW(MSMFB_IOCTL_MAGIC, 136, unsigned int) + #define MSMFB_OVERLAY_PLAY _IOW(MSMFB_IOCTL_MAGIC, 137, \ struct msmfb_overlay_data) +#define MSMFB_OVERLAY_QUEUE MSMFB_OVERLAY_PLAY + #define MSMFB_GET_PAGE_PROTECTION _IOR(MSMFB_IOCTL_MAGIC, 138, \ struct mdp_page_protection) #define MSMFB_SET_PAGE_PROTECTION _IOW(MSMFB_IOCTL_MAGIC, 139, \ @@ -66,6 +69,15 @@ #define MSMFB_WRITEBACK_TERMINATE _IO(MSMFB_IOCTL_MAGIC, 155) #define MSMFB_MDP_PP _IOWR(MSMFB_IOCTL_MAGIC, 156, struct msmfb_mdp_pp) +#define MSMFB_OVERLAY_VSYNC_CTRL _IOW(MSMFB_IOCTL_MAGIC, 160, unsigned int) +#define MSMFB_VSYNC_CTRL _IOW(MSMFB_IOCTL_MAGIC, 161, unsigned int) + +#define MSMFB_BUFFER_SYNC _IOW(MSMFB_IOCTL_MAGIC, 165, struct mdp_buf_sync) +#define MSMFB_METADATA_SET _IOW(MSMFB_IOCTL_MAGIC, 166, struct msmfb_metadata) +#define MSMFB_DISPLAY_COMMIT _IOW(MSMFB_IOCTL_MAGIC, 164, \ + struct mdp_display_commit) +#define MSMFB_OVERLAY_COMMIT _IO(MSMFB_IOCTL_MAGIC, 163) + #define FB_TYPE_3D_PANEL 0x10101010 #define MDP_IMGTYPE2_START 0x10000 #define MSMFB_DRIVER_VERSION 0xF9E8D701 @@ -96,7 +108,10 @@ enum { MDP_Y_CB_CR_H2V2, /* Y, Cb and Cr, planar */ MDP_Y_CRCB_H1V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */ MDP_Y_CBCR_H1V1, /* Y and CbCr, pseduo planer w/ Cb is in MSB */ + MDP_YCRCB_H1V1, /* YCrCb interleave */ + MDP_YCBCR_H1V1, /* YCbCr interleave */ MDP_IMGTYPE_LIMIT, + MDP_RGB_BORDERFILL, /* border fill pipe */ MDP_BGR_565 = MDP_IMGTYPE2_START, /* BGR 565 planer */ MDP_FB_FORMAT, /* framebuffer format */ MDP_IMGTYPE_LIMIT2 /* Non valid image type after this enum */ @@ -140,7 +155,7 @@ enum { #define MDP_DEINTERLACE_ODD 0x00400000 #define MDP_OV_PLAY_NOWAIT 0x00200000 #define MDP_SOURCE_ROTATED_90 0x00100000 -#define MDP_DPP_HSIC 0x00080000 +#define MDP_OVERLAY_PP_CFG_EN 0x00080000 #define MDP_BACKEND_COMPOSITION 0x00040000 #define MDP_BORDERFILL_SUPPORTED 0x00010000 #define MDP_SECURE_OVERLAY_SESSION 0x00008000 @@ -242,6 +257,7 @@ struct msmfb_overlay_data { uint32_t version_key; struct msmfb_data plane1_data; struct msmfb_data plane2_data; + struct msmfb_data dst_data; }; struct msmfb_img { @@ -256,15 +272,78 @@ struct msmfb_writeback_data { struct msmfb_img img; }; -struct dpp_ctrl { - /* - *'sharp_strength' has inputs = -128 <-> 127 - * Increasingly positive values correlate with increasingly sharper - * picture. Increasingly negative values correlate with increasingly - * smoothed picture. - */ - int8_t sharp_strength; - int8_t hsic_params[NUM_HSIC_PARAM]; +#define MDP_PP_OPS_ENABLE 0x1 +#define MDP_PP_OPS_READ 0x2 +#define MDP_PP_OPS_WRITE 0x4 +#define MDP_PP_OPS_DISABLE 0x8 + +struct mdp_qseed_cfg { + uint32_t table_num; + uint32_t ops; + uint32_t len; + uint32_t *data; +}; + +struct mdp_qseed_cfg_data { + uint32_t block; + struct mdp_qseed_cfg qseed_data; +}; + +struct mdp_sharp_cfg { + uint32_t flags; + uint32_t strength; + uint32_t edge_thr; + uint32_t smooth_thr; + uint32_t noise_thr; +}; + +#define MDP_OVERLAY_PP_CSC_CFG 0x1 +#define MDP_OVERLAY_PP_QSEED_CFG 0x2 +#define MDP_OVERLAY_PP_PA_CFG 0x4 +#define MDP_OVERLAY_PP_IGC_CFG 0x8 +#define MDP_OVERLAY_PP_SHARP_CFG 0x10 + +#define MDP_CSC_FLAG_ENABLE 0x1 +#define MDP_CSC_FLAG_YUV_IN 0x2 +#define MDP_CSC_FLAG_YUV_OUT 0x4 + +struct mdp_csc_cfg { + /* flags for enable CSC, toggling RGB,YUV input/output */ + uint32_t flags; + uint32_t csc_mv[9]; + uint32_t csc_pre_bv[3]; + uint32_t csc_post_bv[3]; + uint32_t csc_pre_lv[6]; + uint32_t csc_post_lv[6]; +}; + +struct mdp_csc_cfg_data { + uint32_t block; + struct mdp_csc_cfg csc_data; +}; + +struct mdp_pa_cfg { + uint32_t flags; + uint32_t hue_adj; + uint32_t sat_adj; + uint32_t val_adj; + uint32_t cont_adj; +}; + +struct mdp_igc_lut_data { + uint32_t block; + uint32_t len, ops; + uint32_t *c0_c1_data; + uint32_t *c2_data; +}; + +struct mdp_overlay_pp_params { + uint32_t config_ops; + struct mdp_csc_cfg csc_cfg; + struct mdp_qseed_cfg qseed_cfg[2]; + struct mdp_pa_cfg pa_cfg; + struct mdp_igc_lut_data igc_cfg; + struct mdp_sharp_cfg sharp_cfg; }; struct mdp_overlay { @@ -278,7 +357,7 @@ struct mdp_overlay { uint32_t flags; uint32_t id; uint32_t user_data[8]; - struct dpp_ctrl dpp; + struct mdp_overlay_pp_params overlay_pp_cfg; }; struct msmfb_overlay_3d { @@ -307,12 +386,15 @@ struct mdp_histogram { /* - mdp_block_type defines the identifiers for each of pipes in MDP 4.3 + mdp_block_type defines the identifiers for pipes in MDP 4.3 and up MDP_BLOCK_RESERVED is provided for backward compatibility and is deprecated. It corresponds to DMA_P. So MDP_BLOCK_DMA_P should be used instead. + MDP_LOGICAL_BLOCK_DISP_0 identifies the display pipe which fb0 uses, + same for others. + */ enum { @@ -326,13 +408,17 @@ enum { MDP_BLOCK_DMA_P, MDP_BLOCK_DMA_S, MDP_BLOCK_DMA_E, + MDP_BLOCK_OVERLAY_2, + MDP_LOGICAL_BLOCK_DISP_0 = 0x1000, + MDP_LOGICAL_BLOCK_DISP_1, + MDP_LOGICAL_BLOCK_DISP_2, MDP_BLOCK_MAX, }; /* -mdp_histogram_start_req is used to provide the parameters for -histogram start request -*/ + * mdp_histogram_start_req is used to provide the parameters for + *histogram start request + */ struct mdp_histogram_start_req { uint32_t block; @@ -343,10 +429,8 @@ struct mdp_histogram_start_req { /* - - mdp_histogram_data is used to return the histogram data, once - the histogram is done/stopped/cance - + * mdp_histogram_data is used to return the histogram data, once + * the histogram is done/stopped/cance */ @@ -369,25 +453,6 @@ struct mdp_pcc_cfg_data { struct mdp_pcc_coeff r, g, b; }; -#define MDP_CSC_FLAG_ENABLE 0x1 -#define MDP_CSC_FLAG_YUV_IN 0x2 -#define MDP_CSC_FLAG_YUV_OUT 0x4 - -struct mdp_csc_cfg { - /* flags for enable CSC, toggling RGB,YUV input/output */ - uint32_t flags; - uint32_t csc_mv[9]; - uint32_t csc_pre_bv[3]; - uint32_t csc_post_bv[3]; - uint32_t csc_pre_lv[6]; - uint32_t csc_post_lv[6]; -}; - -struct mdp_csc_cfg_data { - uint32_t block; - struct mdp_csc_cfg csc_data; -}; - enum { mdp_lut_igc, mdp_lut_pgc, @@ -395,14 +460,6 @@ enum { mdp_lut_max, }; - -struct mdp_igc_lut_data { - uint32_t block; - uint32_t len, ops; - uint32_t *c0_c1_data; - uint32_t *c2_data; -}; - struct mdp_ar_gc_lut_data { uint32_t x_start; uint32_t slope; @@ -428,7 +485,6 @@ struct mdp_hist_lut_data { uint32_t *data; }; - struct mdp_lut_cfg_data { uint32_t lut_type; union { @@ -438,20 +494,23 @@ struct mdp_lut_cfg_data { } data; }; -struct mdp_qseed_cfg_data { - uint32_t block; - uint32_t table_num; - uint32_t ops; - uint32_t len; - uint32_t *data; +struct mdp_bl_scale_data { + uint32_t min_lvl; + uint32_t scale; }; +struct mdp_pa_cfg_data { + uint32_t block; + struct mdp_pa_cfg pa_data; +}; enum { mdp_op_pcc_cfg, mdp_op_csc_cfg, mdp_op_lut_cfg, + mdp_bl_scale_cfg, mdp_op_qseed_cfg, + mdp_op_pa_cfg, mdp_op_max, }; @@ -461,10 +520,53 @@ struct msmfb_mdp_pp { struct mdp_pcc_cfg_data pcc_cfg_data; struct mdp_csc_cfg_data csc_cfg_data; struct mdp_lut_cfg_data lut_cfg_data; + struct mdp_bl_scale_data bl_scale_data; struct mdp_qseed_cfg_data qseed_cfg_data; + struct mdp_pa_cfg_data pa_cfg_data; } data; }; +enum { + metadata_op_none, + metadata_op_base_blend, + metadata_op_max +}; + +#define MDP_MAX_FENCE_FD 10 +#define MDP_BUF_SYNC_FLAG_WAIT 1 + +struct mdp_buf_sync { + uint32_t flags; + uint32_t acq_fen_fd_cnt; + int *acq_fen_fd; + int *rel_fen_fd; +}; + +struct mdp_blend_cfg { + uint32_t is_premultiplied; +}; + +struct msmfb_metadata { + uint32_t op; + uint32_t flags; + union { + struct mdp_blend_cfg blend_cfg; + } data; +}; +struct mdp_buf_fence { + uint32_t flags; + uint32_t acq_fen_fd_cnt; + int acq_fen_fd[MDP_MAX_FENCE_FD]; + int rel_fen_fd[MDP_MAX_FENCE_FD]; +}; + +#define MDP_DISPLAY_COMMIT_OVERLAY 0x00000001 + +struct mdp_display_commit { + uint32_t flags; + uint32_t wait_for_finish; + struct fb_var_screeninfo var; +}; struct mdp_page_protection { uint32_t page_protection; From 8dd72b1f488468f63ec0dc55555091a23ef83a07 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 23 Apr 2013 19:53:24 -0500 Subject: [PATCH 2491/2556] msm: video: support get/put to ion buffer Change-Id: I326198078eea655ddcfd0f4bff5dce8effda748d --- arch/arm/mach-msm/include/mach/msm_fb.h | 44 +++++++++++++ drivers/video/msm/mdp.c | 1 - drivers/video/msm/mdp_ppp.c | 82 +++++++++++++++---------- drivers/video/msm/msm_fb.c | 44 ++----------- 4 files changed, 99 insertions(+), 72 deletions(-) diff --git a/arch/arm/mach-msm/include/mach/msm_fb.h b/arch/arm/mach-msm/include/mach/msm_fb.h index a75f4a7b1f7ce..c3d535020c83f 100644 --- a/arch/arm/mach-msm/include/mach/msm_fb.h +++ b/arch/arm/mach-msm/include/mach/msm_fb.h @@ -18,6 +18,10 @@ #define _MSM_FB_H_ #include +#include +#include +#include + struct mddi_info; @@ -247,6 +251,46 @@ struct mdp_device { uint32_t height; /*panel height*/ }; +struct msmfb_info { + struct fb_info *fb; + struct msm_panel_data *panel; + int xres; + int yres; + unsigned output_format; + unsigned yoffset; + unsigned frame_requested; + unsigned frame_done; + int sleeping; + unsigned update_frame; + struct { + int left; + int top; + int eright; /* exclusive */ + int ebottom; /* exclusive */ + } update_info; + char *black; +#ifdef CONFIG_HTC_ONMODE_CHARGING + struct early_suspend onchg_earlier_suspend; + struct early_suspend onchg_suspend; +#endif + struct early_suspend earlier_suspend; + struct early_suspend early_suspend; + + struct wake_lock idle_lock; + spinlock_t update_lock; + struct mutex panel_init_lock; + wait_queue_head_t frame_wq; + struct workqueue_struct *resume_workqueue; + struct work_struct resume_work; + struct work_struct msmfb_resume_work; + struct msmfb_callback dma_callback; + struct msmfb_callback vsync_callback; + struct hrtimer fake_vsync; + ktime_t vsync_request_time; + unsigned fb_resumed; + struct ion_client *iclient; +}; + struct class_interface; int register_mdp_client(struct class_interface *class_intf); diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index aea60df6459dc..72fa9bb4dde39 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include diff --git a/drivers/video/msm/mdp_ppp.c b/drivers/video/msm/mdp_ppp.c index 4c18cfbdd515f..6aa960a43e31d 100644 --- a/drivers/video/msm/mdp_ppp.c +++ b/drivers/video/msm/mdp_ppp.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -43,6 +44,8 @@ ((format == MDP_Y_CBCR_H2V2 || format == MDP_Y_CRCB_H2V2) ? 2 :\ (format == MDP_Y_CBCR_H2V1 || format == MDP_Y_CRCB_H2V1) ? 1 : 1) +static struct ion_client *ppp_display_iclient; + static uint32_t pack_pattern[] = { PPP_ARRAY0(PACK_PATTERN) }; @@ -678,13 +681,18 @@ static int mdp_ppp_wait(struct mdp_info *mdp) return ret; } -static int get_img(struct mdp_img *img, struct fb_info *info, +int get_img(struct mdp_img *img, struct mdp_blit_req *req, + struct fb_info *info, unsigned long *start, unsigned long *len, - struct file** filep) + struct file** filep, struct ion_handle **ihdlp) { int put_needed, ret = 0; struct file *file; +#ifdef CONFIG_ION_MSM + struct msmfb_info *msmfb = (struct msmfb_info *)info->par; +#else unsigned long vstart; +#endif if (img->memory_id & 0x40000000) { @@ -697,38 +705,44 @@ static int get_img(struct mdp_img *img, struct fb_info *info, *filep = NULL; return 0; } - - if (!get_pmem_file(img->memory_id, start, &vstart, len, filep)) - return 0; - file = fget_light(img->memory_id, &put_needed); - if (file == NULL) - return -1; + if (req->flags & MDP_MEMORY_ID_TYPE_FB) { + file = fget_light(img->memory_id, &put_needed); + if (file == NULL) + return -1; - if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) { - *start = info->fix.smem_start; - *len = info->fix.smem_len; - ret = 0; - } else - ret = -1; - fput_light(file, put_needed); + if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) { + *start = info->fix.smem_start; + *len = info->fix.smem_len; + fput_light(file, put_needed); + return 0; + } else + fput_light(file, put_needed); + } - return ret; -} +#ifdef CONFIG_ION_MSM + *ihdlp = ion_import_fd(msmfb->iclient, img->memory_id); + if (IS_ERR_OR_NULL(*ihdlp)) + return -1; -#if 0 -static void put_img(struct file *file) -{ - if (file) { - if (is_pmem_file(file)) - put_pmem_file(file); - } -} + if (!ion_phys(msmfb->iclient, *ihdlp, start, (size_t *) len)) + return 0; + else + return -1; +#else + if (!get_pmem_file(img->memory_id, start, &vstart, len, filep)) + return 0; + else + return -1; #endif +} -static void put_img(struct file *p_src_file) +void put_img(struct file *p_src_file, struct ion_handle *p_ihdl) { -#ifdef CONFIG_ANDROID_PMEM +#ifdef CONFIG_ION_MSM + if (!IS_ERR_OR_NULL(p_ihdl)) + ion_free(ppp_display_iclient, p_ihdl); +#else if (p_src_file) put_pmem_file(p_src_file); #endif @@ -793,6 +807,10 @@ int mdp_ppp_blit(struct mdp_info *mdp, struct fb_info *fb, int ret; unsigned long src_start = 0, src_len = 0, dst_start = 0, dst_len = 0; struct file *src_file = 0, *dst_file = 0; + struct ion_handle *src_ihdl = NULL; + struct ion_handle *dst_ihdl = NULL; + struct msmfb_info *msmfb = fb->par; + ppp_display_iclient = msmfb->iclient; ret = mdp_ppp_validate_blit(mdp, req); if (ret) @@ -800,16 +818,16 @@ int mdp_ppp_blit(struct mdp_info *mdp, struct fb_info *fb, /* do this first so that if this fails, the caller can always * safely call put_img */ - if (unlikely(get_img(&req->src, fb, &src_start, &src_len, &src_file))) { + if (unlikely(get_img(&req->src, req, fb, &src_start, &src_len, &src_file, &src_ihdl))) { printk(KERN_ERR "mdp_ppp: could not retrieve src image from " "memory\n"); return -EINVAL; } - if (unlikely(get_img(&req->dst, fb, &dst_start, &dst_len, &dst_file))) { + if (unlikely(get_img(&req->dst, req, fb, &dst_start, &dst_len, &dst_file, &dst_ihdl))) { printk(KERN_ERR "mdp_ppp: could not retrieve dst image from " "memory\n"); - put_img(src_file); + put_img(src_file, src_ihdl); return -EINVAL; } mutex_lock(&mdp_mutex); @@ -821,8 +839,8 @@ int mdp_ppp_blit(struct mdp_info *mdp, struct fb_info *fb, ret = mdp_ppp_do_blit(mdp, req, src_file, src_start, src_len, dst_file, dst_start, dst_len); - put_img(src_file); - put_img(dst_file); + put_img(src_file, src_ihdl); + put_img(dst_file, dst_ihdl); mutex_unlock(&mdp_mutex); return ret; } diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index 14d7b8922f8e1..c54ced3186178 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "mdp_hw.h" extern void start_drawing_late_resume(struct early_suspend *h); @@ -85,45 +86,6 @@ module_param_named(msmfb_debug_mask, msmfb_debug_mask, int, struct mdp_device *mdp; static atomic_t mdpclk_on = ATOMIC_INIT(1); -struct msmfb_info { - struct fb_info *fb; - struct msm_panel_data *panel; - int xres; - int yres; - unsigned output_format; - unsigned yoffset; - unsigned frame_requested; - unsigned frame_done; - int sleeping; - unsigned update_frame; - struct { - int left; - int top; - int eright; /* exclusive */ - int ebottom; /* exclusive */ - } update_info; - char *black; -#ifdef CONFIG_HTC_ONMODE_CHARGING - struct early_suspend onchg_earlier_suspend; - struct early_suspend onchg_suspend; -#endif - struct early_suspend earlier_suspend; - struct early_suspend early_suspend; - - struct wake_lock idle_lock; - spinlock_t update_lock; - struct mutex panel_init_lock; - wait_queue_head_t frame_wq; - struct workqueue_struct *resume_workqueue; - struct work_struct resume_work; - struct work_struct msmfb_resume_work; - struct msmfb_callback dma_callback; - struct msmfb_callback vsync_callback; - struct hrtimer fake_vsync; - ktime_t vsync_request_time; - unsigned fb_resumed; -}; - #ifdef CONFIG_FB_MSM_OVERLAY #define USE_OVERLAY 1 struct overlay_waitevent{ @@ -1112,6 +1074,10 @@ static int msmfb_probe(struct platform_device *pdev) msmfb->xres = panel->fb_data->xres; msmfb->yres = panel->fb_data->yres; +#ifdef CONFIG_ION_MSM + msmfb->iclient = msm_ion_client_create(-1, pdev->name); +#endif + ret = setup_fbmem(msmfb, pdev); if (ret) goto error_setup_fbmem; From 49252ddc29bc47e1dca5b1a0692ddda62ab71188 Mon Sep 17 00:00:00 2001 From: "andrew.boren" Date: Thu, 25 Apr 2013 20:44:32 -0700 Subject: [PATCH 2492/2556] qsd8k: bcmdhd: compile as module. Change-Id: I128347760ef751b8e386d0adc3417ea5b9786fb4 --- arch/arm/configs/evervolv_bravo_defconfig | 2 +- arch/arm/configs/evervolv_incrediblec_defconfig | 2 +- arch/arm/configs/evervolv_mahimahi_defconfig | 2 +- arch/arm/configs/evervolv_supersonic_defconfig | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index 06bf33a14c8e0..ec797070d5edb 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -1103,7 +1103,7 @@ CONFIG_NETDEV_10000=y CONFIG_WLAN=y # CONFIG_ATH_COMMON is not set # CONFIG_BCM4329 is not set -CONFIG_BCMDHD=y +CONFIG_BCMDHD=m CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" CONFIG_BCMDHD_NVRAM_PATH="/proc/calibration" # CONFIG_DHD_USE_STATIC_BUF is not set diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index 849ac32d49ffd..b79c828ca2cb6 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -1111,7 +1111,7 @@ CONFIG_SMC91X=y CONFIG_WLAN=y # CONFIG_ATH_COMMON is not set # CONFIG_BCM4329 is not set -CONFIG_BCMDHD=y +CONFIG_BCMDHD=m CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" CONFIG_BCMDHD_NVRAM_PATH="/proc/calibration" # CONFIG_DHD_USE_STATIC_BUF is not set diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index c6f74f3f18365..17b9d30ac84b7 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1104,7 +1104,7 @@ CONFIG_NETDEV_10000=y CONFIG_WLAN=y # CONFIG_ATH_COMMON is not set # CONFIG_BCM4329 is not set -CONFIG_BCMDHD=y +CONFIG_BCMDHD=m CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" CONFIG_BCMDHD_NVRAM_PATH="/proc/calibration" # CONFIG_DHD_USE_STATIC_BUF is not set diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index b3cad91579615..5936d133ec885 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -1103,7 +1103,7 @@ CONFIG_MII=y CONFIG_WLAN=y # CONFIG_ATH_COMMON is not set # CONFIG_BCM4329 is not set -CONFIG_BCMDHD=y +CONFIG_BCMDHD=m CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" CONFIG_BCMDHD_NVRAM_PATH="/proc/calibration" # CONFIG_DHD_USE_STATIC_BUF is not set From cc60472a11ccecda56081f644d4a8b49bf9e3cec Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 25 Apr 2013 23:17:43 -0500 Subject: [PATCH 2493/2556] configs: ubuntu: enable ion Change-Id: I2167ef263628277a848a4146222dcb591cbeb465 --- arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig | 3 ++- arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig | 3 ++- arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig | 3 ++- arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig b/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig index c4ab0e9d1905a..9050d3c002feb 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig @@ -1493,7 +1493,8 @@ CONFIG_S5K3E2FX=y # Graphics support # # CONFIG_DRM is not set -# CONFIG_ION is not set +CONFIG_ION=y +CONFIG_ION_MSM=y CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set diff --git a/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig b/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig index fd096354c8903..9bbf41a6acdd9 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig @@ -1512,7 +1512,8 @@ CONFIG_OV8810=y # Graphics support # # CONFIG_DRM is not set -# CONFIG_ION is not set +CONFIG_ION=y +CONFIG_ION_MSM=y CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set diff --git a/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig b/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig index c21f27fd7d45d..588ad5b70c62e 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig @@ -1490,7 +1490,8 @@ CONFIG_S5K3E2FX=y # Graphics support # # CONFIG_DRM is not set -# CONFIG_ION is not set +CONFIG_ION=y +CONFIG_ION_MSM=y CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set diff --git a/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig b/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig index 5b96267b3fb4a..fee8292db0a9b 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig @@ -1491,7 +1491,8 @@ CONFIG_S5K3H1GX=y # Graphics support # # CONFIG_DRM is not set -# CONFIG_ION is not set +CONFIG_ION=y +CONFIG_ION_MSM=y CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set From 662b48e3eba40feb9b45e6891822fe54d9a6048b Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 25 Apr 2013 23:19:22 -0500 Subject: [PATCH 2494/2556] configs: all: enable sw_sync Change-Id: I7bd3a28a3c10ef7b8e3d02c0e6aec92e8dd91550 --- arch/arm/configs/evervolv_bravo_defconfig | 1 + arch/arm/configs/evervolv_incrediblec_defconfig | 1 + arch/arm/configs/evervolv_mahimahi_defconfig | 2 +- arch/arm/configs/evervolv_supersonic_defconfig | 1 + arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig | 1 + arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig | 1 + arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig | 1 + arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig | 1 + 8 files changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index ec797070d5edb..68fe5efa55a9e 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -919,6 +919,7 @@ CONFIG_EXTRA_FIRMWARE="" CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y CONFIG_SYNC=y +CONFIG_SW_SYNC=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index b79c828ca2cb6..7c07f2f59bb6f 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -920,6 +920,7 @@ CONFIG_EXTRA_FIRMWARE="" CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y CONFIG_SYNC=y +CONFIG_SW_SYNC=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index 17b9d30ac84b7..d7de8bf5cae6e 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -919,7 +919,7 @@ CONFIG_EXTRA_FIRMWARE="" CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y CONFIG_SYNC=y -# CONFIG_SW_SYNC is not set +CONFIG_SW_SYNC=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index 5936d133ec885..a2c52e43932fb 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -930,6 +930,7 @@ CONFIG_EXTRA_FIRMWARE="" CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y CONFIG_SYNC=y +CONFIG_SW_SYNC=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set diff --git a/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig b/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig index 9050d3c002feb..d2af3345f82fb 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig @@ -929,6 +929,7 @@ CONFIG_EXTRA_FIRMWARE="" CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y CONFIG_SYNC=y +CONFIG_SW_SYNC=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set diff --git a/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig b/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig index 9bbf41a6acdd9..d4645299ce618 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig @@ -930,6 +930,7 @@ CONFIG_EXTRA_FIRMWARE="" CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y CONFIG_SYNC=y +CONFIG_SW_SYNC=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set diff --git a/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig b/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig index 588ad5b70c62e..c09092f644eb6 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig @@ -929,6 +929,7 @@ CONFIG_EXTRA_FIRMWARE="" CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y CONFIG_SYNC=y +CONFIG_SW_SYNC=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set diff --git a/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig b/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig index fee8292db0a9b..105c4a3b980cf 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig @@ -936,6 +936,7 @@ CONFIG_EXTRA_FIRMWARE="" CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y CONFIG_SYNC=y +CONFIG_SW_SYNC=y # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set From 0eb2f608f122f43160d74d1ba3db88f0d5f787e7 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Thu, 25 Apr 2013 23:20:20 -0500 Subject: [PATCH 2495/2556] configs: ubuntu: build bcmdhd as module Change-Id: I3aace9f472e55693ba63f38ec94a7f26d98c8cfb --- arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig | 2 +- arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig | 2 +- arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig | 2 +- arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig b/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig index d2af3345f82fb..afb841143e3d7 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_bravo_defconfig @@ -1114,7 +1114,7 @@ CONFIG_NETDEV_10000=y CONFIG_WLAN=y # CONFIG_ATH_COMMON is not set # CONFIG_BCM4329 is not set -CONFIG_BCMDHD=y +CONFIG_BCMDHD=m CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" CONFIG_BCMDHD_NVRAM_PATH="/proc/calibration" # CONFIG_DHD_USE_STATIC_BUF is not set diff --git a/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig b/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig index d4645299ce618..00a29ae73f8fb 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_incrediblec_defconfig @@ -1122,7 +1122,7 @@ CONFIG_SMC91X=y CONFIG_WLAN=y # CONFIG_ATH_COMMON is not set # CONFIG_BCM4329 is not set -CONFIG_BCMDHD=y +CONFIG_BCMDHD=m CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" CONFIG_BCMDHD_NVRAM_PATH="/proc/calibration" # CONFIG_DHD_USE_STATIC_BUF is not set diff --git a/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig b/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig index c09092f644eb6..6bfe5c49266f4 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_mahimahi_defconfig @@ -1114,7 +1114,7 @@ CONFIG_NETDEV_10000=y CONFIG_WLAN=y # CONFIG_ATH_COMMON is not set # CONFIG_BCM4329 is not set -CONFIG_BCMDHD=y +CONFIG_BCMDHD=m CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" CONFIG_BCMDHD_NVRAM_PATH="/proc/calibration" # CONFIG_DHD_USE_STATIC_BUF is not set diff --git a/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig b/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig index 105c4a3b980cf..8b65603858c93 100644 --- a/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig +++ b/arch/arm/configs/evervolv_ubuntuphone_supersonic_defconfig @@ -1110,7 +1110,7 @@ CONFIG_MII=y CONFIG_WLAN=y # CONFIG_ATH_COMMON is not set # CONFIG_BCM4329 is not set -CONFIG_BCMDHD=y +CONFIG_BCMDHD=m CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" CONFIG_BCMDHD_NVRAM_PATH="/proc/calibration" # CONFIG_DHD_USE_STATIC_BUF is not set From a0ccda839a08c3484f450571bc5063602dacbc4d Mon Sep 17 00:00:00 2001 From: Tony Espy Date: Wed, 27 Feb 2013 17:34:33 -0500 Subject: [PATCH 2496/2556] Expose virtual PID from child namespace in /proc/PID/status. The virtual PID is used by Ubuntu Touch application manager code ( which runs in the Android namespace ), to communicate with code that runs inside the Ubuntu container. This patch is not from upstream. It was proposed on the lxc-users mailing list: http://www.mail-archive.com/lxc-users@lists.sourceforge.net/msg01840.html Change-Id: If63652002c227827fb524e03a1d0210fc2b7d4b1 Original-Author: Greg Kurz Signed-off-by: Greg Kurz Signed-off-by: Tony Espy Signed-off-by: Ricardo Salveti de Araujo --- fs/proc/array.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/proc/array.c b/fs/proc/array.c index 5e4f776b0917a..0a8d96340491d 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -337,6 +337,12 @@ static void task_cpus_allowed(struct seq_file *m, struct task_struct *task) seq_putc(m, '\n'); } +static void task_vpid(struct seq_file *m, struct task_struct *task) +{ + struct pid_namespace *ns = task_active_pid_ns(task); + seq_printf(m, "Vpid:\t%d\n", ns ? task_pid_nr_ns(task, ns) : 0); +} + int proc_pid_status(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *task) { @@ -354,6 +360,7 @@ int proc_pid_status(struct seq_file *m, struct pid_namespace *ns, task_cpus_allowed(m, task); cpuset_task_status_allowed(m, task); task_context_switch_counts(m, task); + task_vpid(m, task); return 0; } From 5fb927f40616807eaaa804a56211dd17037445f4 Mon Sep 17 00:00:00 2001 From: Andrew Bartholomew Date: Sat, 27 Apr 2013 16:42:36 -0400 Subject: [PATCH 2497/2556] inc: fix compile errors to to ion change Change-Id: I90af58a80b29f9007a094fda036dd0eb9e8d4abf Signed-off-by: Andrew Sutherland --- arch/arm/mach-msm/board-incrediblec.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm/mach-msm/board-incrediblec.c b/arch/arm/mach-msm/board-incrediblec.c index a563f6d5d3d48..d4d5c9973e4a2 100644 --- a/arch/arm/mach-msm/board-incrediblec.c +++ b/arch/arm/mach-msm/board-incrediblec.c @@ -1383,10 +1383,18 @@ static void __init incrediblec_init(void) msm_hw_reset_hook = incrediblec_reset; if (0 == engineerid || 0xF == engineerid) { +#ifdef CONFIG_ION_MSM + ion_pdata.heaps[1].base = MSM_PMEM_MDP_XA_BASE; +#else mdp_pmem_pdata.start = MSM_PMEM_MDP_XA_BASE; +#endif android_pmem_adsp_pdata.start = MSM_PMEM_ADSP_XA_BASE; } else if (engineerid >= 3) { +#ifdef CONFIG_ION_MSM + ion_pdata.heaps[1].base = MSM_PMEM_MDP_BASE + MSM_MEM_128MB_OFFSET; +#else mdp_pmem_pdata.start = MSM_PMEM_MDP_BASE + MSM_MEM_128MB_OFFSET; +#endif android_pmem_adsp_pdata.start = MSM_PMEM_ADSP_BASE + MSM_MEM_128MB_OFFSET; } From 796d23a29ae7a72abbd16b14189177b3cd60ef23 Mon Sep 17 00:00:00 2001 From: "andrew.boren" Date: Sat, 25 May 2013 15:44:52 -0700 Subject: [PATCH 2498/2556] qsd8k: copy msm-specific headers. Change-Id: I02ac8bff8268dcd48b7077c37a8b545b145117e7 --- include/linux/Kbuild | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/Kbuild b/include/linux/Kbuild index be53335461304..3b224cb7c2549 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -395,3 +395,10 @@ header-y += wireless.h header-y += x25.h header-y += xattr.h header-y += xfrm.h +header-y += android_pmem.h +header-y += ashmem.h +header-y += genlock.h +header-y += msm_kgsl.h +header-y += msm_mdp.h +header-y += msm_q6vdec.h +header-y += msm_q6venc.h From 5fa27c1d881ce907e2f7eb03e45853297ecd48b2 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Mon, 29 Jul 2013 18:43:14 -0500 Subject: [PATCH 2499/2556] add msm_{ion,rotator} for 4.3 build Change-Id: I04db831a1d35a452525f81478e91fc828bb1d2ec --- include/linux/Kbuild | 2 + include/linux/msm_ion.h | 1 + include/linux/msm_rotator.h | 76 +++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 include/linux/msm_ion.h create mode 100644 include/linux/msm_rotator.h diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 3b224cb7c2549..66198161741fe 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -402,3 +402,5 @@ header-y += msm_kgsl.h header-y += msm_mdp.h header-y += msm_q6vdec.h header-y += msm_q6venc.h +header-y += msm_ion.h +header-y += msm_rotator.h diff --git a/include/linux/msm_ion.h b/include/linux/msm_ion.h new file mode 100644 index 0000000000000..5434a05229737 --- /dev/null +++ b/include/linux/msm_ion.h @@ -0,0 +1 @@ +#include diff --git a/include/linux/msm_rotator.h b/include/linux/msm_rotator.h new file mode 100644 index 0000000000000..2e57191b00cd2 --- /dev/null +++ b/include/linux/msm_rotator.h @@ -0,0 +1,76 @@ +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + *** To edit the content of this header, modify the corresponding + *** source file (e.g. under external/kernel-headers/original/) then + *** run bionic/libc/kernel/tools/update_all.py + *** + *** Any manual change here will be lost the next time this script will + *** be run. You've been warned! + *** + **************************************************************************** + ****************************************************************************/ +#ifndef __MSM_ROTATOR_H__ +#define __MSM_ROTATOR_H__ +#include +#include +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define MSM_ROTATOR_IOCTL_MAGIC 'R' +#define MSM_ROTATOR_IOCTL_START _IOWR(MSM_ROTATOR_IOCTL_MAGIC, 1, struct msm_rotator_img_info) +#define MSM_ROTATOR_IOCTL_ROTATE _IOW(MSM_ROTATOR_IOCTL_MAGIC, 2, struct msm_rotator_data_info) +#define MSM_ROTATOR_IOCTL_FINISH _IOW(MSM_ROTATOR_IOCTL_MAGIC, 3, int) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define ROTATOR_VERSION_01 0xA5B4C301 +enum rotator_clk_type { + ROTATOR_CORE_CLK, + ROTATOR_PCLK, +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + ROTATOR_IMEM_CLK +}; +struct msm_rotator_img_info { + unsigned int session_id; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + struct msmfb_img src; + struct msmfb_img dst; + struct mdp_rect src_rect; + unsigned int dst_x; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + unsigned int dst_y; + unsigned char rotations; + int enable; + unsigned int downscale_ratio; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + unsigned int secure; +}; +struct msm_rotator_data_info { + int session_id; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + struct msmfb_data src; + struct msmfb_data dst; + unsigned int version_key; + struct msmfb_data src_chroma; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + struct msmfb_data dst_chroma; +}; +struct msm_rot_clocks { + const char *clk_name; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + enum rotator_clk_type clk_type; + unsigned int clk_rate; +}; +struct msm_rotator_platform_data { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ + unsigned int number_of_clocks; + unsigned int hardware_version_number; + struct msm_rot_clocks *rotator_clks; + char rot_iommu_split_domain; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +}; +#endif + From edd58a4a9a753717a66683ceca4a5c353ee8b4b5 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 30 Jul 2013 15:48:35 -0500 Subject: [PATCH 2500/2556] qsd8k: also export ion header Change-Id: I21d432aa1c666112b168f085aef5bbfbff0e5540 --- include/linux/Kbuild | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 66198161741fe..bd15415f6a15c 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -398,6 +398,7 @@ header-y += xfrm.h header-y += android_pmem.h header-y += ashmem.h header-y += genlock.h +header-y += ion.h header-y += msm_kgsl.h header-y += msm_mdp.h header-y += msm_q6vdec.h From 69a13bdd862f0770f86626913b18a95fe77774d6 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 30 Jul 2013 16:24:07 -0500 Subject: [PATCH 2501/2556] Revert "configs: enable ion" This reverts commit 06894957db47cb8266ad6212eb94ffdccd8c98f0. Change-Id: I79deb1fc018f58492d8bf88af4424d77be8f28a1 --- arch/arm/configs/evervolv_bravo_defconfig | 3 +-- arch/arm/configs/evervolv_incrediblec_defconfig | 3 +-- arch/arm/configs/evervolv_mahimahi_defconfig | 3 +-- arch/arm/configs/evervolv_supersonic_defconfig | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index 68fe5efa55a9e..192dfcf7cc966 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -1484,8 +1484,7 @@ CONFIG_S5K3E2FX=y # Graphics support # # CONFIG_DRM is not set -CONFIG_ION=y -CONFIG_ION_MSM=y +# CONFIG_ION is not set CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index 7c07f2f59bb6f..dd244f1fabab1 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -1503,8 +1503,7 @@ CONFIG_OV8810=y # Graphics support # # CONFIG_DRM is not set -CONFIG_ION=y -CONFIG_ION_MSM=y +# CONFIG_ION is not set CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index d7de8bf5cae6e..0890a32e25395 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1481,8 +1481,7 @@ CONFIG_S5K3E2FX=y # Graphics support # # CONFIG_DRM is not set -CONFIG_ION=y -CONFIG_ION_MSM=y +# CONFIG_ION is not set CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index a2c52e43932fb..e4792e57ce05b 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -1486,8 +1486,7 @@ CONFIG_S5K3H1GX=y # Graphics support # # CONFIG_DRM is not set -CONFIG_ION=y -CONFIG_ION_MSM=y +# CONFIG_ION is not set CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set From 4413cb9869390f5f3263ddafb66470ab1160e902 Mon Sep 17 00:00:00 2001 From: Ross Squires Date: Fri, 13 Sep 2013 15:00:05 -0400 Subject: [PATCH 2502/2556] Merging latest XT_QTAGUID module Needed for network direction icon in status bar Merging in latest changes as of bd91f6b21861cde78d800deede8ebeca0cc0cdd2 from android/kernel/common (android-3.4) Change-Id: I911b9841560991530b14775feead68a96fcf74a1 --- net/netfilter/xt_qtaguid.c | 430 ++++++++++++++++++++-------- net/netfilter/xt_qtaguid_internal.h | 24 +- net/netfilter/xt_qtaguid_print.c | 24 +- 3 files changed, 358 insertions(+), 120 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 08086d680c2c2..88c61bdf1d2de 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,10 @@ #include #include +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +#include +#endif + #include #include "xt_qtaguid_internal.h" #include "xt_qtaguid_print.h" @@ -49,25 +54,22 @@ static unsigned int proc_stats_perms = S_IRUGO; module_param_named(stats_perms, proc_stats_perms, uint, S_IRUGO | S_IWUSR); static struct proc_dir_entry *xt_qtaguid_ctrl_file; -#ifdef CONFIG_ANDROID_PARANOID_NETWORK + +/* Everybody can write. But proc_ctrl_write_limited is true by default which + * limits what can be controlled. See the can_*() functions. + */ static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUGO; -#else -static unsigned int proc_ctrl_perms = S_IRUGO | S_IWUSR; -#endif module_param_named(ctrl_perms, proc_ctrl_perms, uint, S_IRUGO | S_IWUSR); -#ifdef CONFIG_ANDROID_PARANOID_NETWORK -#include -static gid_t proc_stats_readall_gid = AID_NET_BW_STATS; -static gid_t proc_ctrl_write_gid = AID_NET_BW_ACCT; -#else -/* 0 means, don't limit anybody */ -static gid_t proc_stats_readall_gid; -static gid_t proc_ctrl_write_gid; -#endif -module_param_named(stats_readall_gid, proc_stats_readall_gid, uint, +/* Limited by default, so the gid of the ctrl and stats proc entries + * will limit what can be done. See the can_*() functions. + */ +static bool proc_stats_readall_limited = true; +static bool proc_ctrl_write_limited = true; + +module_param_named(stats_readall_limited, proc_stats_readall_limited, bool, S_IRUGO | S_IWUSR); -module_param_named(ctrl_write_gid, proc_ctrl_write_gid, uint, +module_param_named(ctrl_write_limited, proc_ctrl_write_limited, bool, S_IRUGO | S_IWUSR); /* @@ -110,8 +112,15 @@ module_param_named(debug_mask, qtaguid_debug_mask, uint, S_IRUGO | S_IWUSR); /*---------------------------------------------------------------------------*/ static const char *iface_stat_procdirname = "iface_stat"; static struct proc_dir_entry *iface_stat_procdir; +/* + * The iface_stat_all* will go away once userspace gets use to the new fields + * that have a format line. + */ static const char *iface_stat_all_procfilename = "iface_stat_all"; static struct proc_dir_entry *iface_stat_all_procfile; +static const char *iface_stat_fmt_procfilename = "iface_stat_fmt"; +static struct proc_dir_entry *iface_stat_fmt_procfile; + /* * Ordering of locks: @@ -124,9 +133,9 @@ static struct proc_dir_entry *iface_stat_all_procfile; * Notice how sock_tag_list_lock is held sometimes when uid_tag_data_tree_lock * is acquired. * - * Call tree with all lock holders as of 2011-09-25: + * Call tree with all lock holders as of 2012-04-27: * - * iface_stat_all_proc_read() + * iface_stat_fmt_proc_read() * iface_stat_list_lock * (struct iface_stat) * @@ -231,8 +240,9 @@ static struct qtaguid_event_counts qtu_events; static bool can_manipulate_uids(void) { /* root pwnd */ - return unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_gid) - || in_egroup_p(proc_ctrl_write_gid); + return in_egroup_p(xt_qtaguid_ctrl_file->gid) + || unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_limited) + || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid); } static bool can_impersonate_uid(uid_t uid) @@ -243,9 +253,10 @@ static bool can_impersonate_uid(uid_t uid) static bool can_read_other_uid_stats(uid_t uid) { /* root pwnd */ - return unlikely(!current_fsuid()) || uid == current_fsuid() - || unlikely(!proc_stats_readall_gid) - || in_egroup_p(proc_stats_readall_gid); + return in_egroup_p(xt_qtaguid_stats_file->gid) + || unlikely(!current_fsuid()) || uid == current_fsuid() + || unlikely(!proc_stats_readall_limited) + || unlikely(current_fsuid() == xt_qtaguid_ctrl_file->uid); } static inline void dc_add_byte_packets(struct data_counters *counters, int set, @@ -258,24 +269,6 @@ static inline void dc_add_byte_packets(struct data_counters *counters, int set, counters->bpc[set][direction][ifs_proto].packets += packets; } -static inline uint64_t dc_sum_bytes(struct data_counters *counters, - int set, - enum ifs_tx_rx direction) -{ - return counters->bpc[set][direction][IFS_TCP].bytes - + counters->bpc[set][direction][IFS_UDP].bytes - + counters->bpc[set][direction][IFS_PROTO_OTHER].bytes; -} - -static inline uint64_t dc_sum_packets(struct data_counters *counters, - int set, - enum ifs_tx_rx direction) -{ - return counters->bpc[set][direction][IFS_TCP].packets - + counters->bpc[set][direction][IFS_UDP].packets - + counters->bpc[set][direction][IFS_PROTO_OTHER].packets; -} - static struct tag_node *tag_node_tree_search(struct rb_root *root, tag_t tag) { struct rb_node *node = root->rb_node; @@ -777,13 +770,61 @@ static struct iface_stat *get_iface_entry(const char *ifname) return iface_entry; } -static int iface_stat_all_proc_read(char *page, char **num_items_returned, +/* This is for fmt2 only */ +static int pp_iface_stat_line(bool header, char *outp, + int char_count, struct iface_stat *iface_entry) +{ + int len; + if (header) { + len = snprintf(outp, char_count, + "ifname " + "total_skb_rx_bytes total_skb_rx_packets " + "total_skb_tx_bytes total_skb_tx_packets " + "rx_tcp_bytes rx_tcp_packets " + "rx_udp_bytes rx_udp_packets " + "rx_other_bytes rx_other_packets " + "tx_tcp_bytes tx_tcp_packets " + "tx_udp_bytes tx_udp_packets " + "tx_other_bytes tx_other_packets\n" + ); + } else { + struct data_counters *cnts; + int cnt_set = 0; /* We only use one set for the device */ + cnts = &iface_entry->totals_via_skb; + len = snprintf( + outp, char_count, + "%s " + "%llu %llu %llu %llu %llu %llu %llu %llu " + "%llu %llu %llu %llu %llu %llu %llu %llu\n", + iface_entry->ifname, + dc_sum_bytes(cnts, cnt_set, IFS_RX), + dc_sum_packets(cnts, cnt_set, IFS_RX), + dc_sum_bytes(cnts, cnt_set, IFS_TX), + dc_sum_packets(cnts, cnt_set, IFS_TX), + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); + } + return len; +} + +static int iface_stat_fmt_proc_read(char *page, char **num_items_returned, off_t items_to_skip, int char_count, int *eof, void *data) { char *outp = page; int item_index = 0; int len; + int fmt = (int)data; /* The data is just 1 (old) or 2 (uses fmt) */ struct iface_stat *iface_entry; struct rtnl_link_stats64 dev_stats, *stats; struct rtnl_link_stats64 no_dev_stats = {0}; @@ -793,14 +834,28 @@ static int iface_stat_all_proc_read(char *page, char **num_items_returned, return 0; } - CT_DEBUG("qtaguid:proc iface_stat_all " + CT_DEBUG("qtaguid:proc iface_stat_fmt " + "pid=%u tgid=%u uid=%u " "page=%p *num_items_returned=%p off=%ld " - "char_count=%d *eof=%d\n", page, *num_items_returned, + "char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, *num_items_returned, items_to_skip, char_count, *eof); if (*eof) return 0; + if (fmt == 2 && item_index++ >= items_to_skip) { + len = pp_iface_stat_line(true, outp, char_count, NULL); + if (len >= char_count) { + *outp = '\0'; + return outp - page; + } + outp += len; + char_count -= len; + (*num_items_returned)++; + } + /* * This lock will prevent iface_stat_update() from changing active, * and in turn prevent an interface from unregistering itself. @@ -816,18 +871,29 @@ static int iface_stat_all_proc_read(char *page, char **num_items_returned, } else { stats = &no_dev_stats; } - len = snprintf(outp, char_count, - "%s %d " - "%llu %llu %llu %llu " - "%llu %llu %llu %llu\n", - iface_entry->ifname, - iface_entry->active, - iface_entry->totals[IFS_RX].bytes, - iface_entry->totals[IFS_RX].packets, - iface_entry->totals[IFS_TX].bytes, - iface_entry->totals[IFS_TX].packets, - stats->rx_bytes, stats->rx_packets, - stats->tx_bytes, stats->tx_packets); + /* + * If the meaning of the data changes, then update the fmtX + * string. + */ + if (fmt == 1) { + len = snprintf( + outp, char_count, + "%s %d " + "%llu %llu %llu %llu " + "%llu %llu %llu %llu\n", + iface_entry->ifname, + iface_entry->active, + iface_entry->totals_via_dev[IFS_RX].bytes, + iface_entry->totals_via_dev[IFS_RX].packets, + iface_entry->totals_via_dev[IFS_TX].bytes, + iface_entry->totals_via_dev[IFS_TX].packets, + stats->rx_bytes, stats->rx_packets, + stats->tx_bytes, stats->tx_packets + ); + } else { + len = pp_iface_stat_line(false, outp, char_count, + iface_entry); + } if (len >= char_count) { spin_unlock_bh(&iface_stat_list_lock); *outp = '\0'; @@ -861,13 +927,17 @@ static void iface_create_proc_worker(struct work_struct *work) new_iface->proc_ptr = proc_entry; create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_TX].bytes); + read_proc_u64, + &new_iface->totals_via_dev[IFS_TX].bytes); create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_RX].bytes); + read_proc_u64, + &new_iface->totals_via_dev[IFS_RX].bytes); create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_TX].packets); + read_proc_u64, + &new_iface->totals_via_dev[IFS_TX].packets); create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->totals[IFS_RX].packets); + read_proc_u64, + &new_iface->totals_via_dev[IFS_RX].packets); create_proc_read_entry("active", proc_iface_perms, proc_entry, read_proc_bool, &new_iface->active); @@ -971,11 +1041,13 @@ static void iface_check_stats_reset_and_adjust(struct net_device *net_dev, "iface reset its stats unexpectedly\n", __func__, net_dev->name); - iface->totals[IFS_TX].bytes += iface->last_known[IFS_TX].bytes; - iface->totals[IFS_TX].packets += + iface->totals_via_dev[IFS_TX].bytes += + iface->last_known[IFS_TX].bytes; + iface->totals_via_dev[IFS_TX].packets += iface->last_known[IFS_TX].packets; - iface->totals[IFS_RX].bytes += iface->last_known[IFS_RX].bytes; - iface->totals[IFS_RX].packets += + iface->totals_via_dev[IFS_RX].bytes += + iface->last_known[IFS_RX].bytes; + iface->totals_via_dev[IFS_RX].packets += iface->last_known[IFS_RX].packets; iface->last_known_valid = false; IF_DEBUG("qtaguid: %s(%s): iface=%p " @@ -1037,18 +1109,13 @@ static void iface_stat_create(struct net_device *net_dev, spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(ifname); if (entry != NULL) { - bool activate = !ipv4_is_loopback(ipaddr); IF_DEBUG("qtaguid: iface_stat: create(%s): entry=%p\n", ifname, entry); iface_check_stats_reset_and_adjust(net_dev, entry); - _iface_stat_set_active(entry, net_dev, activate); + _iface_stat_set_active(entry, net_dev, true); IF_DEBUG("qtaguid: %s(%s): " "tracking now %d on ip=%pI4\n", __func__, - entry->ifname, activate, &ipaddr); - goto done_unlock_put; - } else if (ipv4_is_loopback(ipaddr)) { - IF_DEBUG("qtaguid: iface_stat: create(%s): " - "ignore loopback dev. ip=%pI4\n", ifname, &ipaddr); + entry->ifname, true, &ipaddr); goto done_unlock_put; } @@ -1099,19 +1166,13 @@ static void iface_stat_create_ipv6(struct net_device *net_dev, spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(ifname); if (entry != NULL) { - bool activate = !(addr_type & IPV6_ADDR_LOOPBACK); IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, ifname, entry); iface_check_stats_reset_and_adjust(net_dev, entry); - _iface_stat_set_active(entry, net_dev, activate); + _iface_stat_set_active(entry, net_dev, true); IF_DEBUG("qtaguid: %s(%s): " "tracking now %d on ip=%pI6c\n", __func__, - entry->ifname, activate, &ifa->addr); - goto done_unlock_put; - } else if (addr_type & IPV6_ADDR_LOOPBACK) { - IF_DEBUG("qtaguid: %s(%s): " - "ignore loopback dev. ip=%pI6c\n", __func__, - ifname, &ifa->addr); + entry->ifname, true, &ifa->addr); goto done_unlock_put; } @@ -1143,6 +1204,27 @@ static struct sock_tag *get_sock_stat(const struct sock *sk) return sock_tag_entry; } +static int ipx_proto(const struct sk_buff *skb, + struct xt_action_param *par) +{ + int thoff, tproto; + + switch (par->family) { + case NFPROTO_IPV6: + tproto = ipv6_find_hdr(skb, &thoff, -1, NULL); + if (tproto < 0) + MT_DEBUG("%s(): transport header not found in ipv6" + " skb=%p\n", __func__, skb); + break; + case NFPROTO_IPV4: + tproto = ip_hdr(skb)->protocol; + break; + default: + tproto = IPPROTO_RAW; + } + return tproto; +} + static void data_counters_update(struct data_counters *dc, int set, enum ifs_tx_rx direction, int proto, int bytes) @@ -1203,10 +1285,10 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only) spin_unlock_bh(&iface_stat_list_lock); return; } - entry->totals[IFS_TX].bytes += stats->tx_bytes; - entry->totals[IFS_TX].packets += stats->tx_packets; - entry->totals[IFS_RX].bytes += stats->rx_bytes; - entry->totals[IFS_RX].packets += stats->rx_packets; + entry->totals_via_dev[IFS_TX].bytes += stats->tx_bytes; + entry->totals_via_dev[IFS_TX].packets += stats->tx_packets; + entry->totals_via_dev[IFS_RX].bytes += stats->rx_bytes; + entry->totals_via_dev[IFS_RX].packets += stats->rx_packets; /* We don't need the last_known[] anymore */ entry->last_known_valid = false; _iface_stat_set_active(entry, net_dev, false); @@ -1216,6 +1298,68 @@ static void iface_stat_update(struct net_device *net_dev, bool stash_only) spin_unlock_bh(&iface_stat_list_lock); } +/* + * Update stats for the specified interface from the skb. + * Do nothing if the entry + * does not exist (when a device was never configured with an IP address). + * Called on each sk. + */ +static void iface_stat_update_from_skb(const struct sk_buff *skb, + struct xt_action_param *par) +{ + struct iface_stat *entry; + const struct net_device *el_dev; + enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX; + int bytes = skb->len; + int proto; + + if (!skb->dev) { + MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); + el_dev = par->in ? : par->out; + } else { + const struct net_device *other_dev; + el_dev = skb->dev; + other_dev = par->in ? : par->out; + if (el_dev != other_dev) { + MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " + "par->(in/out)=%p %s\n", + par->hooknum, el_dev, el_dev->name, other_dev, + other_dev->name); + } + } + + if (unlikely(!el_dev)) { + pr_err_ratelimited("qtaguid[%d]: %s(): no par->in/out?!!\n", + par->hooknum, __func__); + BUG(); + } else if (unlikely(!el_dev->name)) { + pr_err_ratelimited("qtaguid[%d]: %s(): no dev->name?!!\n", + par->hooknum, __func__); + BUG(); + } else { + proto = ipx_proto(skb, par); + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", + par->hooknum, el_dev->name, el_dev->type, + par->family, proto); + } + + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(el_dev->name); + if (entry == NULL) { + IF_DEBUG("qtaguid: iface_stat: %s(%s): not tracked\n", + __func__, el_dev->name); + spin_unlock_bh(&iface_stat_list_lock); + return; + } + + IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__, + el_dev->name, entry); + + data_counters_update(&entry->totals_via_skb, 0, direction, proto, + bytes); + spin_unlock_bh(&iface_stat_list_lock); +} + static void tag_stat_update(struct tag_stat *tag_entry, enum ifs_tx_rx direction, int proto, int bytes) { @@ -1265,7 +1409,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, struct data_counters *uid_tag_counters; struct sock_tag *sock_tag_entry; struct iface_stat *iface_entry; - struct tag_stat *new_tag_stat; + struct tag_stat *new_tag_stat = NULL; MT_DEBUG("qtaguid: if_tag_stat_update(ifname=%s " "uid=%u sk=%p dir=%d proto=%d bytes=%d)\n", ifname, uid, sk, direction, proto, bytes); @@ -1273,8 +1417,8 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, iface_entry = get_iface_entry(ifname); if (!iface_entry) { - pr_err("qtaguid: iface_stat: stat_update() %s not found\n", - ifname); + pr_err_ratelimited("qtaguid: iface_stat: stat_update() " + "%s not found\n", ifname); return; } /* It is ok to process data when an iface_entry is inactive */ @@ -1324,16 +1468,32 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, * - No {0, uid_tag} stats and no {acc_tag, uid_tag} stats. */ new_tag_stat = create_if_tag_stat(iface_entry, uid_tag); + if (!new_tag_stat) + goto unlock; uid_tag_counters = &new_tag_stat->counters; } else { uid_tag_counters = &tag_stat_entry->counters; } if (acct_tag) { + /* Create the child {acct_tag, uid_tag} and hook up parent. */ new_tag_stat = create_if_tag_stat(iface_entry, tag); + if (!new_tag_stat) + goto unlock; new_tag_stat->parent_counters = uid_tag_counters; + } else { + /* + * For new_tag_stat to be still NULL here would require: + * {0, uid_tag} exists + * and {acct_tag, uid_tag} doesn't exist + * AND acct_tag == 0. + * Impossible. This reassures us that new_tag_stat + * below will always be assigned. + */ + BUG_ON(!new_tag_stat); } tag_stat_update(new_tag_stat, direction, proto, bytes); +unlock: spin_unlock_bh(&iface_entry->tag_stat_list_lock); } @@ -1452,18 +1612,31 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) parent_procdir); if (!iface_stat_all_procfile) { pr_err("qtaguid: iface_stat: init " - " failed to create stat_all proc entry\n"); + " failed to create stat_old proc entry\n"); err = -1; goto err_zap_entry; } - iface_stat_all_procfile->read_proc = iface_stat_all_proc_read; + iface_stat_all_procfile->read_proc = iface_stat_fmt_proc_read; + iface_stat_all_procfile->data = (void *)1; /* fmt1 */ + + iface_stat_fmt_procfile = create_proc_entry(iface_stat_fmt_procfilename, + proc_iface_perms, + parent_procdir); + if (!iface_stat_fmt_procfile) { + pr_err("qtaguid: iface_stat: init " + " failed to create stat_all proc entry\n"); + err = -1; + goto err_zap_all_stats_entry; + } + iface_stat_fmt_procfile->read_proc = iface_stat_fmt_proc_read; + iface_stat_fmt_procfile->data = (void *)2; /* fmt2 */ err = register_netdevice_notifier(&iface_netdev_notifier_blk); if (err) { pr_err("qtaguid: iface_stat: init " "failed to register dev event handler\n"); - goto err_zap_all_stats_entry; + goto err_zap_all_stats_entries; } err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); if (err) { @@ -1484,6 +1657,8 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) unregister_inetaddr_notifier(&iface_inetaddr_notifier_blk); err_unreg_nd: unregister_netdevice_notifier(&iface_netdev_notifier_blk); +err_zap_all_stats_entries: + remove_proc_entry(iface_stat_fmt_procfilename, parent_procdir); err_zap_all_stats_entry: remove_proc_entry(iface_stat_all_procfilename, parent_procdir); err_zap_entry: @@ -1561,15 +1736,15 @@ static void account_for_uid(const struct sk_buff *skb, } else if (unlikely(!el_dev->name)) { pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum); } else { - MT_DEBUG("qtaguid[%d]: dev name=%s type=%d\n", - par->hooknum, - el_dev->name, - el_dev->type); + int proto = ipx_proto(skb, par); + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n", + par->hooknum, el_dev->name, el_dev->type, + par->family, proto); if_tag_stat_update(el_dev->name, uid, skb->sk ? skb->sk : alternate_sk, par->in ? IFS_RX : IFS_TX, - ip_hdr(skb)->protocol, skb->len); + proto, skb->len); } } @@ -1594,8 +1769,22 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) goto ret_res; } - sk = skb->sk; + switch (par->hooknum) { + case NF_INET_PRE_ROUTING: + case NF_INET_POST_ROUTING: + atomic64_inc(&qtu_events.match_calls_prepost); + iface_stat_update_from_skb(skb, par); + /* + * We are done in pre/post. The skb will get processed + * further alter. + */ + res = (info->match ^ info->invert); + goto ret_res; + break; + /* default: Fall through and do UID releated work */ + } + sk = skb->sk; if (sk == NULL) { /* * A missing sk->sk_socket happens when packets are in-flight @@ -1614,8 +1803,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) } else { atomic64_inc(&qtu_events.match_found_sk); } - MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d proto=%d\n", - par->hooknum, sk, got_sock, ip_hdr(skb)->protocol); + MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n", + par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par)); if (sk != NULL) { MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", par->hooknum, sk, sk->sk_socket, @@ -1770,8 +1959,10 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, if (*eof) return 0; - CT_DEBUG("qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", - page, items_to_skip, char_count, *eof); + CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u " + "page=%p off=%ld char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, items_to_skip, char_count, *eof); spin_lock_bh(&sock_tag_list_lock); for (node = rb_first(&sock_tag_tree); @@ -1815,6 +2006,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, "delete_cmds=%llu " "iface_events=%llu " "match_calls=%llu " + "match_calls_prepost=%llu " "match_found_sk=%llu " "match_found_sk_in_ct=%llu " "match_found_no_sk_in_ct=%llu " @@ -1826,6 +2018,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, atomic64_read(&qtu_events.delete_cmds), atomic64_read(&qtu_events.iface_events), atomic64_read(&qtu_events.match_calls), + atomic64_read(&qtu_events.match_calls_prepost), atomic64_read(&qtu_events.match_found_sk), atomic64_read(&qtu_events.match_found_sk_in_ct), atomic64_read( @@ -2099,7 +2292,9 @@ static int ctrl_cmd_tag(const char *input) el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ if (!el_socket) { pr_info("qtaguid: ctrl_tag(%s): failed to lookup" - " sock_fd=%d err=%d\n", input, sock_fd, res); + " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", + input, sock_fd, res, current->pid, current->tgid, + current_fsuid()); goto err; } CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n", @@ -2114,11 +2309,12 @@ static int ctrl_cmd_tag(const char *input) } CT_DEBUG("qtaguid: ctrl_tag(%s): " "pid=%u tgid=%u uid=%u euid=%u fsuid=%u " - "in_group=%d in_egroup=%d\n", + "ctrl.gid=%u in_group()=%d in_egroup()=%d\n", input, current->pid, current->tgid, current_uid(), current_euid(), current_fsuid(), - in_group_p(proc_ctrl_write_gid), - in_egroup_p(proc_ctrl_write_gid)); + xt_qtaguid_ctrl_file->gid, + in_group_p(xt_qtaguid_ctrl_file->gid), + in_egroup_p(xt_qtaguid_ctrl_file->gid)); if (argc < 4) { uid = current_fsuid(); } else if (!can_impersonate_uid(uid)) { @@ -2244,7 +2440,9 @@ static int ctrl_cmd_untag(const char *input) el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ if (!el_socket) { pr_info("qtaguid: ctrl_untag(%s): failed to lookup" - " sock_fd=%d err=%d\n", input, sock_fd, res); + " sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n", + input, sock_fd, res, current->pid, current->tgid, + current_fsuid()); goto err; } CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n", @@ -2320,6 +2518,9 @@ static int qtaguid_ctrl_parse(const char *input, int count) char cmd; int res; + CT_DEBUG("qtaguid: ctrl(%s): pid=%u tgid=%u uid=%u\n", + input, current->pid, current->tgid, current_fsuid()); + cmd = input[0]; /* Collect params for commands */ switch (cmd) { @@ -2400,14 +2601,16 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) } else { tag_t tag = ppi->ts_entry->tn.tag; uid_t stat_uid = get_uid_from_tag(tag); - - if (!can_read_other_uid_stats(stat_uid)) { + /* Detailed tags are not available to everybody */ + if (get_atag_from_tag(tag) + && !can_read_other_uid_stats(stat_uid)) { CT_DEBUG("qtaguid: stats line: " "%s 0x%llx %u: insufficient priv " - "from pid=%u tgid=%u uid=%u\n", + "from pid=%u tgid=%u uid=%u stats.gid=%u\n", ppi->iface_entry->ifname, get_atag_from_tag(tag), stat_uid, - current->pid, current->tgid, current_fsuid()); + current->pid, current->tgid, current_fsuid(), + xt_qtaguid_stats_file->gid); return 0; } if (ppi->item_index++ < ppi->items_to_skip) @@ -2496,9 +2699,12 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, return len; } - CT_DEBUG("qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " - "char_count=%d *eof=%d\n", page, *num_items_returned, - items_to_skip, char_count, *eof); + CT_DEBUG("qtaguid:proc stats pid=%u tgid=%u uid=%u " + "page=%p *num_items_returned=%p off=%ld " + "char_count=%d *eof=%d\n", + current->pid, current->tgid, current_fsuid(), + page, *num_items_returned, + items_to_skip, char_count, *eof); if (*eof) return 0; @@ -2560,7 +2766,7 @@ static int qtudev_open(struct inode *inode, struct file *file) utd_entry = get_uid_data(current_fsuid(), &utd_entry_found); if (IS_ERR_OR_NULL(utd_entry)) { res = PTR_ERR(utd_entry); - goto err; + goto err_unlock; } /* Look for existing PID based proc_data */ @@ -2602,8 +2808,8 @@ static int qtudev_open(struct inode *inode, struct file *file) rb_erase(&utd_entry->node, &uid_tag_data_tree); kfree(utd_entry); } +err_unlock: spin_unlock_bh(&uid_tag_data_tree_lock); -err: return res; } diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h index 02479d6d317d5..6dc14a9c68896 100644 --- a/net/netfilter/xt_qtaguid_internal.h +++ b/net/netfilter/xt_qtaguid_internal.h @@ -179,6 +179,25 @@ struct data_counters { struct byte_packet_counters bpc[IFS_MAX_COUNTER_SETS][IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS]; }; +static inline uint64_t dc_sum_bytes(struct data_counters *counters, + int set, + enum ifs_tx_rx direction) +{ + return counters->bpc[set][direction][IFS_TCP].bytes + + counters->bpc[set][direction][IFS_UDP].bytes + + counters->bpc[set][direction][IFS_PROTO_OTHER].bytes; +} + +static inline uint64_t dc_sum_packets(struct data_counters *counters, + int set, + enum ifs_tx_rx direction) +{ + return counters->bpc[set][direction][IFS_TCP].packets + + counters->bpc[set][direction][IFS_UDP].packets + + counters->bpc[set][direction][IFS_PROTO_OTHER].packets; +} + + /* Generic X based nodes used as a base for rb_tree ops */ struct tag_node { struct rb_node node; @@ -202,7 +221,8 @@ struct iface_stat { /* net_dev is only valid for active iface_stat */ struct net_device *net_dev; - struct byte_packet_counters totals[IFS_MAX_DIRECTIONS]; + struct byte_packet_counters totals_via_dev[IFS_MAX_DIRECTIONS]; + struct data_counters totals_via_skb; /* * We keep the last_known, because some devices reset their counters * just before NETDEV_UP, while some will reset just before @@ -254,6 +274,8 @@ struct qtaguid_event_counts { atomic64_t iface_events; /* Number of NETDEV_* events handled */ atomic64_t match_calls; /* Number of times iptables called mt */ + /* Number of times iptables called mt from pre or post routing hooks */ + atomic64_t match_calls_prepost; /* * match_found_sk_*: numbers related to the netfilter matching * function finding a sock for the sk_buff. diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c index 39176785c91f8..f6a00a3520ed5 100644 --- a/net/netfilter/xt_qtaguid_print.c +++ b/net/netfilter/xt_qtaguid_print.c @@ -177,13 +177,18 @@ char *pp_tag_stat(struct tag_stat *ts) char *pp_iface_stat(struct iface_stat *is) { char *res; - if (!is) + if (!is) { res = kasprintf(GFP_ATOMIC, "iface_stat@null{}"); - else + } else { + struct data_counters *cnts = &is->totals_via_skb; res = kasprintf(GFP_ATOMIC, "iface_stat@%p{" "list=list_head{...}, " "ifname=%s, " - "total={rx={bytes=%llu, " + "total_dev={rx={bytes=%llu, " + "packets=%llu}, " + "tx={bytes=%llu, " + "packets=%llu}}, " + "total_skb={rx={bytes=%llu, " "packets=%llu}, " "tx={bytes=%llu, " "packets=%llu}}, " @@ -198,10 +203,14 @@ char *pp_iface_stat(struct iface_stat *is) "tag_stat_tree=rb_root{...}}", is, is->ifname, - is->totals[IFS_RX].bytes, - is->totals[IFS_RX].packets, - is->totals[IFS_TX].bytes, - is->totals[IFS_TX].packets, + is->totals_via_dev[IFS_RX].bytes, + is->totals_via_dev[IFS_RX].packets, + is->totals_via_dev[IFS_TX].bytes, + is->totals_via_dev[IFS_TX].packets, + dc_sum_bytes(cnts, 0, IFS_RX), + dc_sum_packets(cnts, 0, IFS_RX), + dc_sum_bytes(cnts, 0, IFS_TX), + dc_sum_packets(cnts, 0, IFS_TX), is->last_known_valid, is->last_known[IFS_RX].bytes, is->last_known[IFS_RX].packets, @@ -210,6 +219,7 @@ char *pp_iface_stat(struct iface_stat *is) is->active, is->net_dev, is->proc_ptr); + } _bug_on_err_or_null(res); return res; } From a0e4f56227dc5344b141fdd1596397041ad63633 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 12 Nov 2013 20:50:23 -0600 Subject: [PATCH 2503/2556] Revert "Revert "configs: enable ion"" This reverts commit 69a13bdd862f0770f86626913b18a95fe77774d6. --- arch/arm/configs/evervolv_bravo_defconfig | 3 ++- arch/arm/configs/evervolv_incrediblec_defconfig | 3 ++- arch/arm/configs/evervolv_mahimahi_defconfig | 3 ++- arch/arm/configs/evervolv_supersonic_defconfig | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index 192dfcf7cc966..68fe5efa55a9e 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -1484,7 +1484,8 @@ CONFIG_S5K3E2FX=y # Graphics support # # CONFIG_DRM is not set -# CONFIG_ION is not set +CONFIG_ION=y +CONFIG_ION_MSM=y CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index dd244f1fabab1..7c07f2f59bb6f 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -1503,7 +1503,8 @@ CONFIG_OV8810=y # Graphics support # # CONFIG_DRM is not set -# CONFIG_ION is not set +CONFIG_ION=y +CONFIG_ION_MSM=y CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index 0890a32e25395..d7de8bf5cae6e 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1481,7 +1481,8 @@ CONFIG_S5K3E2FX=y # Graphics support # # CONFIG_DRM is not set -# CONFIG_ION is not set +CONFIG_ION=y +CONFIG_ION_MSM=y CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index e4792e57ce05b..a2c52e43932fb 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -1486,7 +1486,8 @@ CONFIG_S5K3H1GX=y # Graphics support # # CONFIG_DRM is not set -# CONFIG_ION is not set +CONFIG_ION=y +CONFIG_ION_MSM=y CONFIG_MSM_KGSL=y # CONFIG_MSM_KGSL_CFF_DUMP is not set # CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set From 35e5aad92622e3b2a953805a3f83de46c901a945 Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Wed, 16 May 2012 13:14:43 -0700 Subject: [PATCH 2504/2556] sw_sync: export sw_sync API Needed to let modules link against sw_sync. Change-Id: I71d3e52677ef21b6e1ecdb84f831491be1f4fbe6 Signed-off-by: Erik Gilling --- drivers/base/sw_sync.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/base/sw_sync.c b/drivers/base/sw_sync.c index 21ddf4ffd5894..935769c754279 100644 --- a/drivers/base/sw_sync.c +++ b/drivers/base/sw_sync.c @@ -15,6 +15,7 @@ */ #include +#include #include #include #include @@ -42,6 +43,7 @@ struct sync_pt *sw_sync_pt_create(struct sw_sync_timeline *obj, u32 value) return (struct sync_pt *)pt; } +EXPORT_SYMBOL(sw_sync_pt_create); static struct sync_pt *sw_sync_pt_dup(struct sync_pt *sync_pt) { @@ -119,6 +121,7 @@ struct sw_sync_timeline *sw_sync_timeline_create(const char *name) return obj; } +EXPORT_SYMBOL(sw_sync_timeline_create); void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc) { @@ -126,7 +129,7 @@ void sw_sync_timeline_inc(struct sw_sync_timeline *obj, u32 inc) sync_timeline_signal(&obj->obj); } - +EXPORT_SYMBOL(sw_sync_timeline_inc); #ifdef CONFIG_SW_SYNC_USER /* *WARNING* From 226faae3b9a7839dae67e396ae18da68c47f965e Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Fri, 7 Dec 2012 14:57:47 -0800 Subject: [PATCH 2505/2556] msm: kgsl: initialize kgsl_sync_timeline_ops properly Signed-off-by: Iliyan Malchev --- drivers/gpu/msm/kgsl_sync.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c index 11dd8716dcc77..057ae30cc21d2 100644 --- a/drivers/gpu/msm/kgsl_sync.c +++ b/drivers/gpu/msm/kgsl_sync.c @@ -176,6 +176,7 @@ int kgsl_add_fence_event(struct kgsl_device *device, } static const struct sync_timeline_ops kgsl_sync_timeline_ops = { + .driver_name = "kgsl-timeline", .dup = kgsl_sync_pt_dup, .has_signaled = kgsl_sync_pt_has_signaled, .compare = kgsl_sync_pt_compare, From df4140dd7596de04bf1a432984b2abfc8143e8f3 Mon Sep 17 00:00:00 2001 From: Nick Reuter Date: Fri, 22 Nov 2013 21:35:39 -0600 Subject: [PATCH 2506/2556] supersonic: switch to xz compression Change-Id: I70fc4443c1a7720bfc609f609fc940dd5f1d8107 --- arch/arm/configs/evervolv_supersonic_defconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index a2c52e43932fb..b60bc6ffa0b51 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -41,8 +41,8 @@ CONFIG_HAVE_KERNEL_LZMA=y CONFIG_HAVE_KERNEL_XZ=y CONFIG_HAVE_KERNEL_LZO=y # CONFIG_KERNEL_GZIP is not set -CONFIG_KERNEL_LZMA=y -# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y # CONFIG_KERNEL_LZO is not set CONFIG_SWAP=y # CONFIG_SYSVIPC is not set From df695fbc2698f84420479a1792f481853c70c397 Mon Sep 17 00:00:00 2001 From: Rik van Riel Date: Thu, 1 Sep 2011 15:26:50 -0400 Subject: [PATCH 2507/2556] add extra free kbytes tunable Add a userspace visible knob to tell the VM to keep an extra amount of memory free, by increasing the gap between each zone's min and low watermarks. This is useful for realtime applications that call system calls and have a bound on the number of allocations that happen in any short time period. In this application, extra_free_kbytes would be left at an amount equal to or larger than than the maximum number of allocations that happen in any burst. It may also be useful to reduce the memory use of virtual machines (temporarily?), in a way that does not cause memory fragmentation like ballooning does. [ccross] Revived for use on old kernels where no other solution exists. The tunable will be removed on kernels that do better at avoiding direct reclaim. Change-Id: I765a42be8e964bfd3e2886d1ca85a29d60c3bb3e Signed-off-by: Rik van Riel Signed-off-by: Colin Cross --- Documentation/sysctl/vm.txt | 16 ++++++++++++++++ kernel/sysctl.c | 9 +++++++++ mm/page_alloc.c | 32 +++++++++++++++++++++++++------- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index 30289fab86ebb..c000e0dc0db3a 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -28,6 +28,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 @@ -168,6 +169,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/kernel/sysctl.c b/kernel/sysctl.c index 4bc1435706b0d..99aed9f8ad35c 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -95,6 +95,7 @@ extern char core_pattern[]; extern unsigned int core_pipe_limit; extern int pid_max; extern int min_free_kbytes; +extern int extra_free_kbytes; extern int min_free_order_shift; extern int pid_max_min, pid_max_max; extern int sysctl_drop_caches; @@ -1171,6 +1172,14 @@ static struct ctl_table vm_table[] = { .proc_handler = min_free_kbytes_sysctl_handler, .extra1 = &zero, }, + { + .procname = "extra_free_kbytes", + .data = &extra_free_kbytes, + .maxlen = sizeof(extra_free_kbytes), + .mode = 0644, + .proc_handler = min_free_kbytes_sysctl_handler, + .extra1 = &zero, + }, { .procname = "min_free_order_shift", .data = &min_free_order_shift, diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 1fe0f8a59ce04..15ccc938d5faa 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -171,9 +171,21 @@ static char * const zone_names[MAX_NR_ZONES] = { "Movable", }; +/* + * Try to keep at least this much lowmem free. Do not allow normal + * allocations below this point, only high priority ones. Automatically + * tuned according to the amount of memory in the system. + */ int min_free_kbytes = 1024; int min_free_order_shift = 1; +/* + * Extra memory for the system to try freeing. Used to temporarily + * free memory, to make space for new workloads. Anyone can allocate + * down to the min watermarks controlled by min_free_kbytes above. + */ +int extra_free_kbytes = 0; + static unsigned long __meminitdata nr_kernel_pages; static unsigned long __meminitdata nr_all_pages; static unsigned long __meminitdata dma_reserve; @@ -4954,6 +4966,7 @@ static void setup_per_zone_lowmem_reserve(void) void setup_per_zone_wmarks(void) { unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - 10); + unsigned long pages_low = extra_free_kbytes >> (PAGE_SHIFT - 10); unsigned long lowmem_pages = 0; struct zone *zone; unsigned long flags; @@ -4965,11 +4978,14 @@ void setup_per_zone_wmarks(void) } for_each_zone(zone) { - u64 tmp; + u64 min, low; spin_lock_irqsave(&zone->lock, flags); - tmp = (u64)pages_min * zone->present_pages; - do_div(tmp, lowmem_pages); + min = (u64)pages_min * zone->present_pages; + do_div(min, lowmem_pages); + low = (u64)pages_low * zone->present_pages; + do_div(low, vm_total_pages); + if (is_highmem(zone)) { /* * __GFP_HIGH and PF_MEMALLOC allocations usually don't @@ -4993,11 +5009,13 @@ void setup_per_zone_wmarks(void) * If it's a lowmem zone, reserve a number of pages * proportionate to the zone's size. */ - zone->watermark[WMARK_MIN] = tmp; + zone->watermark[WMARK_MIN] = min; } - zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + (tmp >> 2); - zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + (tmp >> 1); + zone->watermark[WMARK_LOW] = min_wmark_pages(zone) + + low + (min >> 2); + zone->watermark[WMARK_HIGH] = min_wmark_pages(zone) + + low + (min >> 1); setup_zone_migrate_reserve(zone); spin_unlock_irqrestore(&zone->lock, flags); } @@ -5094,7 +5112,7 @@ module_init(init_per_zone_wmark_min) /* * min_free_kbytes_sysctl_handler - just a wrapper around proc_dointvec() so * that we can call two helper functions whenever min_free_kbytes - * changes. + * or extra_free_kbytes changes. */ int min_free_kbytes_sysctl_handler(ctl_table *table, int write, void __user *buffer, size_t *length, loff_t *ppos) From 95bdcb7cb84d97f5ff0049a4cb7a209fdf30d287 Mon Sep 17 00:00:00 2001 From: Andrew Sutherland Date: Tue, 26 Nov 2013 17:07:09 -0600 Subject: [PATCH 2508/2556] configs: enable CGROUP_MEM_RES_CTLR_SWAP Change-Id: I3bcbe2475833211c2dff6eb6782bf601f62d46d2 --- arch/arm/configs/evervolv_bravo_defconfig | 10 +++++++--- arch/arm/configs/evervolv_incrediblec_defconfig | 5 +++-- arch/arm/configs/evervolv_mahimahi_defconfig | 8 ++++++-- arch/arm/configs/evervolv_supersonic_defconfig | 10 +++++++--- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/arch/arm/configs/evervolv_bravo_defconfig b/arch/arm/configs/evervolv_bravo_defconfig index 68fe5efa55a9e..281674c1fbbd4 100644 --- a/arch/arm/configs/evervolv_bravo_defconfig +++ b/arch/arm/configs/evervolv_bravo_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Tue Oct 2 20:53:53 2012 +# Mon Nov 25 21:10:55 2013 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -85,7 +85,9 @@ CONFIG_CGROUP_FREEZER=y # CONFIG_CPUSETS is not set CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -# CONFIG_CGROUP_MEM_RES_CTLR is not set +CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED=y CONFIG_CGROUP_SCHED=y CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y @@ -93,6 +95,7 @@ CONFIG_BLK_CGROUP=y # CONFIG_DEBUG_BLK_CGROUP is not set # CONFIG_NAMESPACES is not set CONFIG_SCHED_AUTOGROUP=y +CONFIG_MM_OWNER=y # CONFIG_SYSFS_DEPRECATED is not set CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y @@ -478,7 +481,7 @@ CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set -# CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y # CONFIG_CPU_FREQ_GOV_POWERSAVE is not set # CONFIG_CPU_FREQ_GOV_USERSPACE is not set CONFIG_CPU_FREQ_GOV_ONDEMAND=y @@ -920,6 +923,7 @@ CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y CONFIG_SYNC=y CONFIG_SW_SYNC=y +# CONFIG_SW_SYNC_USER is not set # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index 7c07f2f59bb6f..c6d030226651c 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Wed Sep 5 18:35:00 2012 +# Mon Nov 25 21:11:34 2013 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -478,7 +478,7 @@ CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set -# CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y # CONFIG_CPU_FREQ_GOV_POWERSAVE is not set # CONFIG_CPU_FREQ_GOV_USERSPACE is not set CONFIG_CPU_FREQ_GOV_ONDEMAND=y @@ -921,6 +921,7 @@ CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y CONFIG_SYNC=y CONFIG_SW_SYNC=y +# CONFIG_SW_SYNC_USER is not set # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set diff --git a/arch/arm/configs/evervolv_mahimahi_defconfig b/arch/arm/configs/evervolv_mahimahi_defconfig index d7de8bf5cae6e..9446ff2346f22 100644 --- a/arch/arm/configs/evervolv_mahimahi_defconfig +++ b/arch/arm/configs/evervolv_mahimahi_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Thu Apr 18 16:22:01 2013 +# Mon Nov 25 21:05:36 2013 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -85,7 +85,9 @@ CONFIG_CGROUP_FREEZER=y # CONFIG_CPUSETS is not set CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -# CONFIG_CGROUP_MEM_RES_CTLR is not set +CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED=y CONFIG_CGROUP_SCHED=y CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y @@ -93,6 +95,7 @@ CONFIG_BLK_CGROUP=y # CONFIG_DEBUG_BLK_CGROUP is not set # CONFIG_NAMESPACES is not set CONFIG_SCHED_AUTOGROUP=y +CONFIG_MM_OWNER=y # CONFIG_SYSFS_DEPRECATED is not set CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y @@ -920,6 +923,7 @@ CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y CONFIG_SYNC=y CONFIG_SW_SYNC=y +# CONFIG_SW_SYNC_USER is not set # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set diff --git a/arch/arm/configs/evervolv_supersonic_defconfig b/arch/arm/configs/evervolv_supersonic_defconfig index b60bc6ffa0b51..6d2c57822cc03 100644 --- a/arch/arm/configs/evervolv_supersonic_defconfig +++ b/arch/arm/configs/evervolv_supersonic_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38.8 Kernel Configuration -# Wed Sep 5 18:35:30 2012 +# Mon Nov 25 21:12:01 2013 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -86,7 +86,9 @@ CONFIG_CGROUP_FREEZER=y # CONFIG_CPUSETS is not set CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -# CONFIG_CGROUP_MEM_RES_CTLR is not set +CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED=y CONFIG_CGROUP_SCHED=y CONFIG_FAIR_GROUP_SCHED=y CONFIG_RT_GROUP_SCHED=y @@ -98,6 +100,7 @@ CONFIG_USER_NS=y CONFIG_PID_NS=y # CONFIG_NET_NS is not set CONFIG_SCHED_AUTOGROUP=y +CONFIG_MM_OWNER=y # CONFIG_SYSFS_DEPRECATED is not set CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y @@ -483,7 +486,7 @@ CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set -# CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y # CONFIG_CPU_FREQ_GOV_POWERSAVE is not set # CONFIG_CPU_FREQ_GOV_USERSPACE is not set CONFIG_CPU_FREQ_GOV_ONDEMAND=y @@ -931,6 +934,7 @@ CONFIG_GENLOCK=y CONFIG_GENLOCK_MISCDEVICE=y CONFIG_SYNC=y CONFIG_SW_SYNC=y +# CONFIG_SW_SYNC_USER is not set # CONFIG_CONNECTOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set From 65700ab6058a185e376d23cd5bb4ad418805d254 Mon Sep 17 00:00:00 2001 From: Andrew Bartholomew Date: Tue, 26 Nov 2013 19:33:14 -0500 Subject: [PATCH 2509/2556] config: inc: enable FUSE Change-Id: I73e08d0ad691693878f3136964d0c3b661434eb5 --- arch/arm/configs/evervolv_incrediblec_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/evervolv_incrediblec_defconfig b/arch/arm/configs/evervolv_incrediblec_defconfig index c6d030226651c..40738ac2a9639 100644 --- a/arch/arm/configs/evervolv_incrediblec_defconfig +++ b/arch/arm/configs/evervolv_incrediblec_defconfig @@ -1843,7 +1843,7 @@ CONFIG_INOTIFY_USER=y # CONFIG_QUOTA is not set # CONFIG_QUOTACTL is not set # CONFIG_AUTOFS4_FS is not set -# CONFIG_FUSE_FS is not set +CONFIG_FUSE_FS=y # # Caches From df67133af0a9ba7f60695b82977d20185958edd6 Mon Sep 17 00:00:00 2001 From: Zach Date: Mon, 17 Feb 2014 16:12:15 -0500 Subject: [PATCH 2510/2556] Initial incredikernel deconfig for 4.4 --- .../incrediblec-incredikernel_defconfig | 2217 +++++++++++++++++ 1 file changed, 2217 insertions(+) create mode 100644 arch/arm/configs/incrediblec-incredikernel_defconfig diff --git a/arch/arm/configs/incrediblec-incredikernel_defconfig b/arch/arm/configs/incrediblec-incredikernel_defconfig new file mode 100644 index 0000000000000..cd2d9e1fa7275 --- /dev/null +++ b/arch/arm/configs/incrediblec-incredikernel_defconfig @@ -0,0 +1,2217 @@ +# +# Automatically generated make config: don't edit +# Linux/arm 2.6.38.8 Kernel Configuration +# Mon Nov 25 21:11:34 2013 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y +CONFIG_HAVE_IRQ_WORK=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="arm-eabi-" +CONFIG_LOCALVERSION="-incredikernel-jb44" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +CONFIG_KERNEL_LZMA=y +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_SWAP=y +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_GENERIC_HARDIRQS_NO_DEPRECATED is not set +CONFIG_HAVE_SPARSE_IRQ=y +# CONFIG_GENERIC_PENDING_IRQ is not set +# CONFIG_AUTO_IRQ_AFFINITY is not set +# CONFIG_IRQ_PER_CPU is not set +# CONFIG_SPARSE_IRQ is not set + +# +# RCU Subsystem +# +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +CONFIG_TINY_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_TRACE is not set +# CONFIG_TREE_RCU_TRACE is not set +CONFIG_RCU_BOOST=y +CONFIG_RCU_BOOST_PRIO=1 +CONFIG_RCU_BOOST_DELAY=500 +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_CGROUPS=y +# CONFIG_CGROUP_DEBUG is not set +# CONFIG_CGROUP_NS is not set +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +# CONFIG_RESOURCE_COUNTERS is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_RT_GROUP_SCHED=y +# CONFIG_BLK_CGROUP is not set +# CONFIG_NAMESPACES is not set +CONFIG_SCHED_AUTOGROUP=y +# CONFIG_SYSFS_DEPRECATED is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=5 +CONFIG_EXPERT=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +# CONFIG_KALLSYMS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_ASHMEM=y +CONFIG_AIO=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_TRACEPOINTS=y +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_IOSCHED_BFQ=y +# CONFIG_CGROUP_BFQIO is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_BFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +# CONFIG_INLINE_SPIN_UNLOCK is not set +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +# CONFIG_INLINE_READ_UNLOCK is not set +# CONFIG_INLINE_READ_UNLOCK_BH is not set +# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +# CONFIG_INLINE_WRITE_UNLOCK is not set +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CNS3XXX is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MXS is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +CONFIG_ARCH_MSM=y +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_S5PV310 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_TCC_926 is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_ARCH_MSM7X00A is not set +# CONFIG_ARCH_MSM7X30 is not set +CONFIG_ARCH_QSD8X50=y +# CONFIG_ARCH_MSM8X60 is not set +CONFIG_ARCH_MSM_SCORPION=y +CONFIG_HAS_MSM_DEBUG_UART_PHYS=y +CONFIG_MSM_MDP31=y +# CONFIG_PERFLOCK is not set + +# +# Qualcomm MSM Board Type +# +# CONFIG_MACH_SWORDFISH is not set +CONFIG_MACH_QSD8X50_SURF=y +# CONFIG_MACH_QSD8X50A_ST1_5 is not set +CONFIG_MSM_DEBUG_UART_NONE=y +# CONFIG_MSM_DEBUG_UART1 is not set +# CONFIG_MSM_DEBUG_UART2 is not set +# CONFIG_MSM_DEBUG_UART3 is not set +CONFIG_MSM_PROC_COMM=y +# CONFIG_MACH_MAHIMAHI is not set +CONFIG_MACH_BRAVO_NONE=y +# CONFIG_MACH_BRAVO is not set +# CONFIG_MACH_BRAVOC is not set +# CONFIG_MACH_INCREDIBLE is not set +CONFIG_MACH_INCREDIBLEC=y +# CONFIG_MACH_SUPERSONIC is not set +# CONFIG_MACH_QSD8X50_FFA is not set +# CONFIG_HTC_HEADSET is not set +# CONFIG_HTC_35MM_JACK is not set +CONFIG_HTC_BATTCHG=y +CONFIG_HTC_BATTCHG_SMEM=y +# CONFIG_HTC_PWRSPLY is not set +# CONFIG_HTC_PWRSINK is not set +# CONFIG_HTC_POWER_COLLAPSE_MAGIC is not set +# CONFIG_HTC_ONMODE_CHARGING is not set +CONFIG_QSD_SVS=y +CONFIG_QSD_PMIC_DEFAULT_DCDC1=1275 +CONFIG_CACHE_FLUSH_RANGE_LIMIT=0x40000 +CONFIG_PHYS_OFFSET=0x20000000 +CONFIG_MSM7X00A_USE_GP_TIMER=y +# CONFIG_MSM7X00A_USE_DG_TIMER is not set +CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND=y +# CONFIG_MSM7X00A_SLEEP_MODE_POWER_COLLAPSE is not set +# CONFIG_MSM7X00A_SLEEP_MODE_APPS_SLEEP is not set +# CONFIG_MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_SLEEP_MODE=0 +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP=y +# CONFIG_MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT is not set +# CONFIG_MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT is not set +CONFIG_MSM7X00A_IDLE_SLEEP_MODE=2 +CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME=20000000 +CONFIG_MSM7X00A_IDLE_SPIN_TIME=80000 +CONFIG_MSM_IDLE_STATS=y +CONFIG_MSM_IDLE_STATS_FIRST_BUCKET=62500 +CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT=2 +CONFIG_MSM_IDLE_STATS_BUCKET_COUNT=10 +CONFIG_MSM_FIQ_SUPPORT=y +CONFIG_MSM_SERIAL_DEBUGGER=y +CONFIG_MSM_SERIAL_DEBUGGER_NO_SLEEP=y +# CONFIG_MSM_SERIAL_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON is not set +# CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE is not set +CONFIG_MSM_SMD=y +CONFIG_MSM_GPIOMUX=y +CONFIG_MSM_DAL=y +CONFIG_MSM_ONCRPCROUTER=y +CONFIG_MSM_CPU_FREQ_SET_MIN_MAX=y +CONFIG_MSM_CPU_FREQ_MAX=998000 +CONFIG_MSM_CPU_FREQ_MIN=245760 +# CONFIG_AXI_SCREEN_POLICY is not set +CONFIG_MSM_CPU_AVS=y +CONFIG_MSM_AVS_HW=y +CONFIG_HTC_ACOUSTIC_QSD=y +CONFIG_MSM_QDSP6=y +CONFIG_WIFI_CONTROL_FUNC=y +# CONFIG_WIFI_MEM_PREALLOC is not set +CONFIG_ARCH_MSM_FLASHLIGHT=y +CONFIG_MICROP_COMMON=y +CONFIG_HTC_HEADSET_MGR=y +CONFIG_HTC_HEADSET_GPIO=y +CONFIG_HTC_HEADSET_MICROP=y +# CONFIG_VIRTUAL_KPANIC_PARTITION is not set + +# +# System MMU +# + +# +# Processor Type +# +CONFIG_CPU_32v6K=y +CONFIG_CPU_V7=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_VERIFY_PERMISSION_FAULT=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_CACHE_ERR_REPORT is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +# CONFIG_VCM is not set +# CONFIG_STRICT_MEMORY_RWX is not set +# CONFIG_RESERVE_FIRST_PAGE is not set +CONFIG_CPU_HAS_PMU=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_KSAPI is not set +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_HZ=100 +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_VMALLOC_RESERVE=0x08000000 +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_COMPACTION is not set +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 +CONFIG_NEED_PER_CPU_KM=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_CP_ACCESS is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyMSM0" +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASS2 is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +# CONFIG_CPU_FREQ_GOV_SMARTASS2 is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +# CONFIG_CPU_IDLE is not set +CONFIG_CPU_FREQ_MSM=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_FB_EARLYSUSPEND=y +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_PM_OPS=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +CONFIG_INET_ESP=y +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +# CONFIG_NETFILTER_XT_TARGET_CT is not set +# CONFIG_NETFILTER_XT_TARGET_DSCP is not set +CONFIG_NETFILTER_XT_TARGET_HL=y +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +CONFIG_NETFILTER_XT_TARGET_LED=y +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=y +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set + +# +# Xtables matches +# +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +CONFIG_NETFILTER_XT_MATCH_COMMENT=y +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_OSF is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +CONFIG_NETFILTER_XT_MATCH_POLICY=y +# CONFIG_NETFILTER_XT_MATCH_PHYSDEV is not set +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QTAGUID=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_IP_NF_QUEUE is not set +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_ADDRTYPE=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +# CONFIG_IP_NF_TARGET_ULOG is not set +CONFIG_NF_NAT=y +CONFIG_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +# CONFIG_NF_NAT_SNMP_BASIC is not set +CONFIG_NF_NAT_PROTO_DCCP=y +CONFIG_NF_NAT_PROTO_GRE=y +CONFIG_NF_NAT_PROTO_UDPLITE=y +CONFIG_NF_NAT_PROTO_SCTP=y +CONFIG_NF_NAT_FTP=y +CONFIG_NF_NAT_IRC=y +CONFIG_NF_NAT_TFTP=y +CONFIG_NF_NAT_AMANDA=y +CONFIG_NF_NAT_PPTP=y +CONFIG_NF_NAT_H323=y +CONFIG_NF_NAT_SIP=y +CONFIG_IP_NF_MANGLE=y +# CONFIG_IP_NF_TARGET_CLUSTERIP is not set +# CONFIG_IP_NF_TARGET_ECN is not set +# CONFIG_IP_NF_TARGET_TTL is not set +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK_IPV6=y +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=y +CONFIG_IP6_NF_MATCH_AH=y +CONFIG_IP6_NF_MATCH_EUI64=y +CONFIG_IP6_NF_MATCH_FRAG=y +CONFIG_IP6_NF_MATCH_OPTS=y +CONFIG_IP6_NF_MATCH_HL=y +CONFIG_IP6_NF_MATCH_IPV6HEADER=y +CONFIG_IP6_NF_MATCH_MH=y +CONFIG_IP6_NF_MATCH_RT=y +CONFIG_IP6_NF_TARGET_HL=y +CONFIG_IP6_NF_TARGET_LOG=y +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +# CONFIG_BRIDGE_NF_EBTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +CONFIG_NET_SCH_INGRESS=y + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +# CONFIG_GACT_PROB is not set +CONFIG_NET_ACT_MIRRED=y +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +CONFIG_DNS_RESOLVER=y +# CONFIG_BATMAN_ADV is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NET_DROP_MONITOR is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_CFG80211=y +CONFIG_NL80211_TESTMODE=y +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_DEBUGFS is not set +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +# CONFIG_LIB80211 is not set +CONFIG_CFG80211_ALLOW_RECONNECT=y +# CONFIG_MAC80211 is not set + +# +# Some wireless drivers require a rate control algorithm +# +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +# CONFIG_RFKILL_PM is not set +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +# CONFIG_DEVTMPFS is not set +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_GENLOCK=y +CONFIG_GENLOCK_MISCDEVICE=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +# CONFIG_SW_SYNC_USER is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_SM_FTL is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_MSM_NAND=y +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_CRYPTOLOOP=y + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set +CONFIG_MISC_DEVICES=y +# CONFIG_AD525X_DPOT is not set +CONFIG_ANDROID_PMEM=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_KERNEL_DEBUGGER_CORE=y +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_SENSORS_AK8975 is not set +CONFIG_SENSORS_AKM8973=y +# CONFIG_SENSORS_AKM8976 is not set +# CONFIG_VP_A1026 is not set +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +CONFIG_UID_STAT=y +# CONFIG_BMP085 is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_SENSORS_BMA150_SPI=y +# CONFIG_APANIC is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=y +# CONFIG_DM_DEBUG is not set +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +CONFIG_NETDEVICES=y +CONFIG_IFB=m +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set +CONFIG_MII=y +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set +# CONFIG_ENC28J60 is not set +# CONFIG_ETHOC is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +CONFIG_WLAN=y +# CONFIG_ATH_COMMON is not set +# CONFIG_BCM4329 is not set +CONFIG_BCMDHD=m +CONFIG_BCMDHD_FW_PATH="/vendor/firmware/fw_bcmdhd.bin" +CONFIG_BCMDHD_NVRAM_PATH="/proc/calibration" +# CONFIG_DHD_USE_STATIC_BUF is not set +# CONFIG_DHD_USE_SCHED_SCAN is not set +# CONFIG_DHD_ENABLE_P2P is not set +# CONFIG_HOSTAP is not set +# CONFIG_IWM is not set +# CONFIG_LIBERTAS is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set + +# +# CAIF transport drivers +# +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +# CONFIG_PPPOE is not set +CONFIG_PPPOLAC=y +CONFIG_PPPOPNS=y +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +CONFIG_MSM_RMNET=y +# CONFIG_MSM_RMNET_DEBUG is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_GAN_ETH=y +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ATMEL=y +CONFIG_TOUCHSCREEN_COMPATIBLE_REPORT=y +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_I2C is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELAN_I2C_8232 is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_QT602240 is not set +# CONFIG_TOUCHSCREEN_MSM is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +CONFIG_INPUT_KEYCHORD=y +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +CONFIG_INPUT_GPIO=y +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_CMA3000 is not set +# CONFIG_INPUT_CAPELLA_CM3602 is not set +# CONFIG_INPUT_CAPELLA_CM3602_HTC is not set +CONFIG_LIGHTSENSOR_MICROP=y +CONFIG_INPUT_OPTICALJOYSTICK=y +CONFIG_OPTICALJOYSTICK_CRUCIAL=y +CONFIG_OPTICALJOYSTICK_CRUCIAL_uP=y +# CONFIG_OPTICALJOYSTICK_CRUCIAL_SPI is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_DEVMEM is not set +# CONFIG_DEVKMEM is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX3107 is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_MSM=y +# CONFIG_SERIAL_MSM_CONSOLE is not set +# CONFIG_SERIAL_MSM_CLOCK_CONTROL is not set +CONFIG_SERIAL_MSM_HS=y +CONFIG_SERIAL_BCM_BT_LPM=y +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_IFX6X60 is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +# CONFIG_RAMOOPS is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_MSM=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_QSD=y +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_BASIC_MMIO is not set +# CONFIG_GPIO_IT8761E is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_DS2784 is not set +# CONFIG_BATTERY_BQ20Z75 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_MFD_SUPPORT=y +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_TPS65200 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13XXX is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_WL1273_CORE is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_BQ24022 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +CONFIG_REGULATOR_TPS65023=y +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_TPS6524X is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +CONFIG_RC_CORE=y +CONFIG_LIRC=y +CONFIG_RC_MAP=y +CONFIG_IR_NEC_DECODER=y +CONFIG_IR_RC5_DECODER=y +CONFIG_IR_RC6_DECODER=y +CONFIG_IR_JVC_DECODER=y +CONFIG_IR_SONY_DECODER=y +CONFIG_IR_RC5_SZ_DECODER=y +CONFIG_IR_LIRC_CODEC=y +# CONFIG_IR_IMON is not set +# CONFIG_IR_MCEUSB is not set +# CONFIG_IR_STREAMZAP is not set +# CONFIG_RC_LOOPBACK is not set + +# +# Qualcomm MSM Camera And Video +# +CONFIG_MSM_CAMERA=y +CONFIG_720P_CAMERA=y +# CONFIG_MSM_CAMERA_DEBUG is not set + +# +# Camera Sensor Selection +# +# CONFIG_MT9T013 is not set +# CONFIG_MT9D112 is not set +# CONFIG_MT9P012 is not set +# CONFIG_S5K3E2FX is not set +# CONFIG_S5K6AAFX is not set +CONFIG_OV8810=y +# CONFIG_OV9665 is not set +# CONFIG_S5K3H1GX is not set + +# +# Graphics support +# +# CONFIG_DRM is not set +CONFIG_ION=y +CONFIG_ION_MSM=y +CONFIG_MSM_KGSL=y +# CONFIG_MSM_KGSL_CFF_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_CP_STAT_NO_DETAIL is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_NO_IB_DUMP is not set +# CONFIG_MSM_KGSL_PSTMRTMDMP_RB_HEX is not set +# CONFIG_KGSL_PER_PROCESS_PAGE_TABLE is not set +CONFIG_MSM_KGSL_PAGE_TABLE_SIZE=0xFFF0000 +CONFIG_MSM_KGSL_MMU_PAGE_FAULT=y +# CONFIG_MSM_KGSL_DISABLE_SHADOW_WRITES is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_WMT_GE_ROPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_FB_MSM=y +CONFIG_FB_MSM_LEGACY_MDP=y +CONFIG_FB_MSM_MDP_PPP=y +CONFIG_FB_MSM_LCDC=y +CONFIG_FB_MSM_MDDI=y +# CONFIG_FB_MSM_MDDI_EPSON is not set +# CONFIG_FB_MSM_MDDI_NOVTEC is not set +# CONFIG_MSM_HDMI is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_LOGO is not set +# CONFIG_SOUND is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set +CONFIG_UHID=y +# CONFIG_HID_PID is not set + +# +# Special HID drivers +# +CONFIG_HID_APPLE=y +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_MAGICMOUSE is not set +# CONFIG_HID_WACOM is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +# CONFIG_USB is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA_U2O is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_CI13XXX_MSM is not set +CONFIG_USB_GADGET_MSM_72K=y +CONFIG_USB_MSM_72K=y +CONFIG_USB_MSM_72K_HTC=y +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_ANDROID=y +# CONFIG_USB_ANDROID_ACM is not set +CONFIG_USB_ANDROID_ADB=y +CONFIG_USB_ANDROID_DIAG=y +CONFIG_USB_ANDROID_MASS_STORAGE=y +# CONFIG_USB_ANDROID_MTP is not set +CONFIG_USB_ANDROID_RNDIS=y +# CONFIG_USB_ANDROID_RNDIS_WCEIS is not set +# CONFIG_USB_ANDROID_ACCESSORY is not set +CONFIG_USB_CSW_HACK=y +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_ACCESSORY_DETECT is not set +# CONFIG_DOCK_ACCESSORY_DETECT is not set +# CONFIG_USB_BYPASS_VBUS_NOTIFY is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_USB_MSM_OTG_72K is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_CLKGATE is not set +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_DEFERRED_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +CONFIG_MMC_MSM=y +# CONFIG_MMC_SPI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_GPIO_PLATFORM=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +CONFIG_LEDS_CPLD=y +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +CONFIG_LEDS_TRIGGERS=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +# CONFIG_LEDS_TRIGGER_GPIO is not set +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set +CONFIG_LEDS_TRIGGER_SLEEP=y + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_NFC_DEVICES is not set +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +# CONFIG_RTC_INTF_DEV is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_MSM7X00A=y +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_ECHO is not set +# CONFIG_BRCM80211 is not set +# CONFIG_COMEDI is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE=128 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE=16 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE=8 +CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL=0x11d +# CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_POHMELFS is not set +# CONFIG_IIO is not set +CONFIG_XVMALLOC=y +CONFIG_ZRAM=y +# CONFIG_ZRAM_DEBUG is not set +# CONFIG_FB_SM7XX is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_ST_BT is not set +# CONFIG_LIRC_STAGING is not set +CONFIG_MACH_NO_WESTBRIDGE=y +# CONFIG_ATH6K_LEGACY is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_XATTR=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +# CONFIG_EXT4_FS_SECURITY is not set +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +# CONFIG_FS_POSIX_ACL is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +# CONFIG_DNOTIFY is not set +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=y + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_AFFS_FS is not set +# CONFIG_ECRYPT_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +CONFIG_YAFFS_DISABLE_TAGS_ECC=y +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +# CONFIG_YAFFS_DISABLE_BLOCK_REFRESHING is not set +# CONFIG_YAFFS_DISABLE_BACKGROUND is not set +CONFIG_YAFFS_XATTR=y +# CONFIG_JFFS2_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=y +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_WEAK_PW_HASH=y +# CONFIG_CIFS_UPCALL is not set +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_DFS_UPCALL is not set +# CONFIG_CIFS_ACL is not set +# CONFIG_CIFS_EXPERIMENTAL is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_HARDLOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +CONFIG_SCHED_DEBUG=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_PREEMPT is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_BKL is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_NOP_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACER_MAX_TRACE=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_EVENT_POWER_TRACING_DEPRECATED=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +CONFIG_SCHED_TRACER=y +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_ARM_UNWIND=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_LL is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +CONFIG_KEYS=y +# CONFIG_KEYS_DEBUG_PROC_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_GHASH is not set +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +CONFIG_BINARY_PRINTF=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +# CONFIG_XZ_DEC is not set +# CONFIG_XZ_DEC_BCJ is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y From cb3d113b3f98968997d219b6b692537f8b7a69bb Mon Sep 17 00:00:00 2001 From: Zach Date: Mon, 17 Feb 2014 16:19:30 -0500 Subject: [PATCH 2511/2556] Bring in Tiny4579's interactive gov --- drivers/cpufreq/cpufreq_interactive.c | 867 +++++++++++++++------ include/trace/events/cpufreq_interactive.h | 52 +- 2 files changed, 641 insertions(+), 278 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 2f4660f655dee..224626869b8a0 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -19,37 +19,39 @@ #include #include #include -#include +#include +#include +#include #include #include #include #include #include #include -#include #include +#include #include #define CREATE_TRACE_POINTS #include -static atomic_t active_count = ATOMIC_INIT(0); +static int active_count; struct cpufreq_interactive_cpuinfo { struct timer_list cpu_timer; - int timer_idlecancel; + struct timer_list cpu_slack_timer; + spinlock_t load_lock; /* protects the next 4 fields */ u64 time_in_idle; - u64 idle_exit_time; - u64 timer_run_time; - int idling; - u64 target_set_time; - u64 target_set_time_in_idle; + u64 time_in_idle_timestamp; + u64 cputime_speedadj; + u64 cputime_speedadj_timestamp; struct cpufreq_policy *policy; struct cpufreq_frequency_table *freq_table; unsigned int target_freq; unsigned int floor_freq; u64 floor_validate_time; u64 hispeed_validate_time; + struct rw_semaphore enable_sem; int governor_enabled; }; @@ -59,46 +61,62 @@ static DEFINE_PER_CPU(struct cpufreq_interactive_cpuinfo, cpuinfo); static struct task_struct *speedchange_task; static cpumask_t speedchange_cpumask; static spinlock_t speedchange_cpumask_lock; +static struct mutex gov_lock; /* Hi speed to bump to from lo speed when load burst (default max) */ -static unsigned int hispeed_freq; - -/* When the boostpulse was activated */ -static u64 boostpulse_boosted_time; - -/* How long the boostpulse will remain active */ -#define DEFAULT_BOOSTPULSE_DURATION 500000 -#define MAX_BOOSTPULSE_DURATION 5000000 -static int boostpulse_duration; +#define DEFAULT_HISPEED_FREQ 729600 +static unsigned int hispeed_freq = DEFAULT_HISPEED_FREQ; /* Go to hi speed when CPU load at or above this value. */ -#define DEFAULT_GO_HISPEED_LOAD 85 -static unsigned long go_hispeed_load; +#define DEFAULT_GO_HISPEED_LOAD 50 +static unsigned long go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; + +/* Target load. Lower values result in higher CPU speeds. */ +#define DEFAULT_TARGET_LOAD 90 +static unsigned int default_target_loads[] = {DEFAULT_TARGET_LOAD}; +static spinlock_t target_loads_lock; +static unsigned int *target_loads = default_target_loads; +static int ntarget_loads = ARRAY_SIZE(default_target_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) -static unsigned long min_sample_time; +static unsigned long min_sample_time = DEFAULT_MIN_SAMPLE_TIME; /* * The sample rate of the timer used to increase frequency */ #define DEFAULT_TIMER_RATE (20 * USEC_PER_MSEC) -static unsigned long timer_rate; +static unsigned long timer_rate = DEFAULT_TIMER_RATE; /* * Wait this long before raising speed above hispeed, by default a single * timer interval. */ #define DEFAULT_ABOVE_HISPEED_DELAY DEFAULT_TIMER_RATE -static unsigned long above_hispeed_delay_val; +static unsigned int default_above_hispeed_delay[] = { + DEFAULT_ABOVE_HISPEED_DELAY }; +static spinlock_t above_hispeed_delay_lock; +static unsigned int *above_hispeed_delay = default_above_hispeed_delay; +static int nabove_hispeed_delay = ARRAY_SIZE(default_above_hispeed_delay); + +/* Non-zero means indefinite speed boost active */ +static int boost_val; +/* Duration of a boot pulse in usecs */ +static int boostpulse_duration_val = DEFAULT_MIN_SAMPLE_TIME; +/* End time of boost pulse in ktime converted to usecs */ +static u64 boostpulse_endtime; /* - * Non-zero means longer-term speed boost active. + * 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) +static int timer_slack_val = DEFAULT_TIMER_SLACK; -static int boost_val; +#define DEFAULT_IO_IS_BUSY 1 +static bool io_is_busy = DEFAULT_IO_IS_BUSY; static int cpufreq_governor_interactive(struct cpufreq_policy *policy, unsigned int event); @@ -113,116 +131,263 @@ struct cpufreq_governor cpufreq_gov_interactive = { .owner = THIS_MODULE, }; -static void cpufreq_interactive_timer(unsigned long data) +static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu, + cputime64_t *wall) +{ + cputime64_t idle_time; + cputime64_t cur_wall_time; + cputime64_t busy_time; + + cur_wall_time = jiffies64_to_cputime64(get_jiffies_64()); + busy_time = cputime64_add(kstat_cpu(cpu).cpustat.user, + kstat_cpu(cpu).cpustat.system); + + busy_time = cputime64_add(busy_time, kstat_cpu(cpu).cpustat.irq); + busy_time = cputime64_add(busy_time, kstat_cpu(cpu).cpustat.softirq); + busy_time = cputime64_add(busy_time, kstat_cpu(cpu).cpustat.steal); + busy_time = cputime64_add(busy_time, kstat_cpu(cpu).cpustat.nice); + + idle_time = cputime64_sub(cur_wall_time, busy_time); + if (wall) + *wall = (cputime64_t)jiffies_to_usecs(cur_wall_time); + + return (cputime64_t)jiffies_to_usecs(idle_time); +} + +static inline cputime64_t get_cpu_idle_time(unsigned int cpu, cputime64_t *wall) +{ + u64 idle_time = get_cpu_idle_time_us(cpu, wall); + + if (idle_time == -1ULL) + idle_time = get_cpu_idle_time_jiffy(cpu, wall); + else if (!io_is_busy) + idle_time += get_cpu_iowait_time_us(cpu, wall); + + return idle_time; +} + +static void cpufreq_interactive_timer_resched( + struct cpufreq_interactive_cpuinfo *pcpu) +{ + unsigned long expires = jiffies + usecs_to_jiffies(timer_rate); + unsigned long flags; + + mod_timer_pinned(&pcpu->cpu_timer, expires); + if (timer_slack_val >= 0 && pcpu->target_freq > pcpu->policy->min) { + expires += usecs_to_jiffies(timer_slack_val); + mod_timer_pinned(&pcpu->cpu_slack_timer, expires); + } + + spin_lock_irqsave(&pcpu->load_lock, flags); + pcpu->time_in_idle = + get_cpu_idle_time(smp_processor_id(), + &pcpu->time_in_idle_timestamp); + pcpu->cputime_speedadj = 0; + pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp; + spin_unlock_irqrestore(&pcpu->load_lock, flags); +} + +static unsigned int freq_to_above_hispeed_delay(unsigned int freq) +{ + int i; + unsigned int ret; + unsigned long flags; + + spin_lock_irqsave(&above_hispeed_delay_lock, flags); + + for (i = 0; i < nabove_hispeed_delay - 1 && + freq >= above_hispeed_delay[i+1]; i += 2) + ; + + ret = above_hispeed_delay[i]; + spin_unlock_irqrestore(&above_hispeed_delay_lock, flags); + return ret; +} + +static unsigned int freq_to_targetload(unsigned int freq) +{ + int i; + unsigned int ret; + unsigned long flags; + + spin_lock_irqsave(&target_loads_lock, flags); + + for (i = 0; i < ntarget_loads - 1 && freq >= target_loads[i+1]; i += 2) + ; + + ret = target_loads[i]; + spin_unlock_irqrestore(&target_loads_lock, flags); + return ret; +} + +/* + * If increasing frequencies never map to a lower target load then + * choose_freq() will find the minimum frequency that does not exceed its + * target load given the current load. + */ + +static unsigned int choose_freq( + struct cpufreq_interactive_cpuinfo *pcpu, unsigned int loadadjfreq) +{ + unsigned int freq = pcpu->policy->cur; + unsigned int prevfreq, freqmin, freqmax; + unsigned int tl; + int index; + + freqmin = 0; + freqmax = UINT_MAX; + + do { + prevfreq = freq; + tl = freq_to_targetload(freq); + + /* + * Find the lowest frequency where the computed load is less + * than or equal to the target load. + */ + + cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, loadadjfreq / tl, + CPUFREQ_RELATION_L, &index); + freq = pcpu->freq_table[index].frequency; + + if (freq > prevfreq) { + /* The previous frequency is too low. */ + freqmin = prevfreq; + + if (freq >= freqmax) { + /* + * Find the highest frequency that is less + * than freqmax. + */ + cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, + freqmax - 1, CPUFREQ_RELATION_H, + &index); + freq = pcpu->freq_table[index].frequency; + + if (freq == freqmin) { + /* + * The first frequency below freqmax + * has already been found to be too + * low. freqmax is the lowest speed + * we found that is fast enough. + */ + freq = freqmax; + break; + } + } + } else if (freq < prevfreq) { + /* The previous frequency is high enough. */ + freqmax = prevfreq; + + if (freq <= freqmin) { + /* + * Find the lowest frequency that is higher + * than freqmin. + */ + cpufreq_frequency_table_target( + pcpu->policy, pcpu->freq_table, + freqmin + 1, CPUFREQ_RELATION_L, + &index); + freq = pcpu->freq_table[index].frequency; + + /* + * If freqmax is the first frequency above + * freqmin then we have already found that + * this speed is fast enough. + */ + if (freq == freqmax) + break; + } + } + + /* If same frequency chosen as previous then done. */ + } while (freq != prevfreq); + + return freq; +} + +static u64 update_load(int cpu) { + struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu); + u64 now; + u64 now_idle; unsigned int delta_idle; unsigned int delta_time; + u64 active_time; + + now_idle = get_cpu_idle_time(cpu, &now); + delta_idle = (unsigned int) cputime64_sub(now_idle, pcpu->time_in_idle); + delta_time = (unsigned int) cputime64_sub(now, pcpu->time_in_idle_timestamp); + active_time = delta_time - delta_idle; + pcpu->cputime_speedadj += active_time * pcpu->policy->cur; + + pcpu->time_in_idle = now_idle; + pcpu->time_in_idle_timestamp = now; + return now; +} + +static void cpufreq_interactive_timer(unsigned long data) +{ + u64 now; + unsigned int delta_time; + u64 cputime_speedadj; int cpu_load; - int load_since_change; - u64 time_in_idle; - u64 idle_exit_time; struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, data); - u64 now_idle; unsigned int new_freq; + unsigned int loadadjfreq; unsigned int index; unsigned long flags; - u64 now = ktime_to_us(ktime_get()); - - smp_rmb(); + bool boosted; + if (!down_read_trylock(&pcpu->enable_sem)) + return; if (!pcpu->governor_enabled) goto exit; - /* - * Once pcpu->timer_run_time is updated to >= pcpu->idle_exit_time, - * this lets idle exit know the current idle time sample has - * been processed, and idle exit can generate a new sample and - * re-arm the timer. This prevents a concurrent idle - * exit on that CPU from writing a new set of info at the same time - * the timer function runs (the timer function can't use that info - * until more time passes). - */ - time_in_idle = pcpu->time_in_idle; - idle_exit_time = pcpu->idle_exit_time; - now_idle = get_cpu_idle_time_us(data, &pcpu->timer_run_time); - smp_wmb(); - - /* If we raced with cancelling a timer, skip. */ - if (!idle_exit_time) - goto exit; - - delta_idle = (unsigned int) cputime64_sub(now_idle, time_in_idle); - delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time, - idle_exit_time); + spin_lock_irqsave(&pcpu->load_lock, flags); + now = update_load(data); + delta_time = (unsigned int) cputime64_sub(now, pcpu->cputime_speedadj_timestamp); + cputime_speedadj = pcpu->cputime_speedadj; + spin_unlock_irqrestore(&pcpu->load_lock, flags); - /* - * If timer ran less than 1ms after short-term sample started, retry. - */ - if (delta_time < 1000) + if (WARN_ON_ONCE(!delta_time)) goto rearm; - if (delta_idle > delta_time) - cpu_load = 0; - else - cpu_load = 100 * (delta_time - delta_idle) / delta_time; - - delta_idle = (unsigned int) cputime64_sub(now_idle, - pcpu->target_set_time_in_idle); - delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time, - pcpu->target_set_time); - - if ((delta_time == 0) || (delta_idle > delta_time)) - load_since_change = 0; - else - load_since_change = - 100 * (delta_time - delta_idle) / delta_time; + do_div(cputime_speedadj, delta_time); + loadadjfreq = (unsigned int)cputime_speedadj * 100; + cpu_load = loadadjfreq / pcpu->target_freq; + boosted = boost_val || now < boostpulse_endtime; - /* - * Choose greater of short-term load (since last idle timer - * started or timer function re-armed itself) or long-term load - * (since last frequency change). - */ - if (load_since_change > cpu_load) - cpu_load = load_since_change; - - if (boostpulse_boosted_time && - now > boostpulse_boosted_time + boostpulse_duration) { - /* Disable the boostpulse. */ - boostpulse_boosted_time = 0; - boostpulse_duration = 0; - } - - if (cpu_load >= go_hispeed_load || boost_val || boostpulse_boosted_time) { - if (pcpu->target_freq < hispeed_freq && - hispeed_freq < pcpu->policy->max) { + if (cpu_load >= go_hispeed_load || boosted) { + if (pcpu->target_freq < hispeed_freq) { new_freq = hispeed_freq; } else { - new_freq = pcpu->policy->max * cpu_load / 100; + new_freq = choose_freq(pcpu, loadadjfreq); if (new_freq < hispeed_freq) new_freq = hispeed_freq; - - if (pcpu->target_freq == hispeed_freq && - new_freq > hispeed_freq && - cputime64_sub(pcpu->timer_run_time, - pcpu->hispeed_validate_time) - < above_hispeed_delay_val) { - trace_cpufreq_interactive_notyet(data, cpu_load, - pcpu->target_freq, - new_freq); - goto rearm; - } } } else { - new_freq = hispeed_freq * cpu_load / 100; + new_freq = choose_freq(pcpu, loadadjfreq); + } + + if (pcpu->target_freq >= hispeed_freq && + new_freq > pcpu->target_freq && + now - pcpu->hispeed_validate_time < + freq_to_above_hispeed_delay(pcpu->target_freq)) { + trace_cpufreq_interactive_notyet( + data, cpu_load, pcpu->target_freq, + pcpu->policy->cur, new_freq); + goto rearm; } - if (new_freq <= hispeed_freq) - pcpu->hispeed_validate_time = pcpu->timer_run_time; + pcpu->hispeed_validate_time = now; if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, - new_freq, CPUFREQ_RELATION_H, + new_freq, CPUFREQ_RELATION_L, &index)) { pr_warn_once("timer %d: cpufreq_frequency_table_target error\n", (int) data); @@ -236,28 +401,37 @@ static void cpufreq_interactive_timer(unsigned long data) * floor frequency for the minimum sample time since last validated. */ if (new_freq < pcpu->floor_freq) { - if (cputime64_sub(pcpu->timer_run_time, - pcpu->floor_validate_time) + if (cputime64_sub(now, pcpu->floor_validate_time) < min_sample_time) { - trace_cpufreq_interactive_notyet(data, cpu_load, - pcpu->target_freq, new_freq); + trace_cpufreq_interactive_notyet( + data, cpu_load, pcpu->target_freq, + pcpu->policy->cur, new_freq); goto rearm; } } - pcpu->floor_freq = new_freq; - pcpu->floor_validate_time = pcpu->timer_run_time; + /* + * Update the timestamp for checking whether speed has been held at + * or above the selected frequency for a minimum of min_sample_time, + * if not boosted to hispeed_freq. If boosted to hispeed_freq then we + * allow the speed to drop as soon as the boostpulse duration expires + * (or the indefinite boost is turned off). + */ + + if (!boosted || new_freq > hispeed_freq) { + pcpu->floor_freq = new_freq; + pcpu->floor_validate_time = now; + } if (pcpu->target_freq == new_freq) { - trace_cpufreq_interactive_already(data, cpu_load, - pcpu->target_freq, new_freq); + trace_cpufreq_interactive_already( + data, cpu_load, pcpu->target_freq, + pcpu->policy->cur, new_freq); goto rearm_if_notmax; } trace_cpufreq_interactive_target(data, cpu_load, pcpu->target_freq, - new_freq); - pcpu->target_set_time_in_idle = now_idle; - pcpu->target_set_time = pcpu->timer_run_time; + pcpu->policy->cur, new_freq); pcpu->target_freq = new_freq; spin_lock_irqsave(&speedchange_cpumask_lock, flags); @@ -274,28 +448,11 @@ static void cpufreq_interactive_timer(unsigned long data) goto exit; rearm: - if (!timer_pending(&pcpu->cpu_timer)) { - /* - * If already at min: if that CPU is idle, don't set timer. - * Else cancel the timer if that CPU goes idle. We don't - * need to re-evaluate speed until the next idle exit. - */ - if (pcpu->target_freq == pcpu->policy->min) { - smp_rmb(); - - if (pcpu->idling) - goto exit; - - pcpu->timer_idlecancel = 1; - } - - pcpu->time_in_idle = get_cpu_idle_time_us( - data, &pcpu->idle_exit_time); - mod_timer(&pcpu->cpu_timer, - jiffies + usecs_to_jiffies(timer_rate)); - } + if (!timer_pending(&pcpu->cpu_timer)) + cpufreq_interactive_timer_resched(pcpu); exit: + up_read(&pcpu->enable_sem); return; } @@ -305,15 +462,16 @@ static void cpufreq_interactive_idle_start(void) &per_cpu(cpuinfo, smp_processor_id()); int pending; - if (!pcpu->governor_enabled) + if (!down_read_trylock(&pcpu->enable_sem)) + return; + if (!pcpu->governor_enabled) { + up_read(&pcpu->enable_sem); return; + } - pcpu->idling = 1; - smp_wmb(); pending = timer_pending(&pcpu->cpu_timer); if (pcpu->target_freq != pcpu->policy->min) { -#ifdef CONFIG_SMP /* * Entering idle while not at lowest speed. On some * platforms this can hold the other CPU(s) at that speed @@ -322,33 +480,11 @@ static void cpufreq_interactive_idle_start(void) * min indefinitely. This should probably be a quirk of * the CPUFreq driver. */ - if (!pending) { - pcpu->time_in_idle = get_cpu_idle_time_us( - smp_processor_id(), &pcpu->idle_exit_time); - pcpu->timer_idlecancel = 0; - mod_timer(&pcpu->cpu_timer, - jiffies + usecs_to_jiffies(timer_rate)); - } -#endif - } else { - /* - * If at min speed and entering idle after load has - * already been evaluated, and a timer has been set just in - * case the CPU suddenly goes busy, cancel that timer. The - * CPU didn't go busy; we'll recheck things upon idle exit. - */ - if (pending && pcpu->timer_idlecancel) { - del_timer(&pcpu->cpu_timer); - /* - * Ensure last timer run time is after current idle - * sample start time, so next idle exit will always - * start a new idle sampling period. - */ - pcpu->idle_exit_time = 0; - pcpu->timer_idlecancel = 0; - } + if (!pending) + cpufreq_interactive_timer_resched(pcpu); } + up_read(&pcpu->enable_sem); } static void cpufreq_interactive_idle_end(void) @@ -356,34 +492,23 @@ static void cpufreq_interactive_idle_end(void) struct cpufreq_interactive_cpuinfo *pcpu = &per_cpu(cpuinfo, smp_processor_id()); - if (!pcpu->governor_enabled) + if (!down_read_trylock(&pcpu->enable_sem)) return; + if (!pcpu->governor_enabled) { + up_read(&pcpu->enable_sem); + return; + } - pcpu->idling = 0; - smp_wmb(); - - /* - * Arm the timer for 1-2 ticks later if not already, and if the timer - * function has already processed the previous load sampling - * interval. (If the timer is not pending but has not processed - * the previous interval, it is probably racing with us on another - * CPU. Let it compute load based on the previous sample and then - * re-arm the timer for another interval when it's done, rather - * than updating the interval start time to be "now", which doesn't - * give the timer function enough time to make a decision on this - * run.) - */ - if (timer_pending(&pcpu->cpu_timer) == 0 && - pcpu->timer_run_time >= pcpu->idle_exit_time && - pcpu->governor_enabled) { - pcpu->time_in_idle = - get_cpu_idle_time_us(smp_processor_id(), - &pcpu->idle_exit_time); - pcpu->timer_idlecancel = 0; - mod_timer(&pcpu->cpu_timer, - jiffies + usecs_to_jiffies(timer_rate)); + /* Arm the timer for 1-2 ticks later if not already. */ + if (!timer_pending(&pcpu->cpu_timer)) { + cpufreq_interactive_timer_resched(pcpu); + } else if (time_after_eq(jiffies, pcpu->cpu_timer.expires)) { + del_timer(&pcpu->cpu_timer); + del_timer(&pcpu->cpu_slack_timer); + cpufreq_interactive_timer(smp_processor_id()); } + up_read(&pcpu->enable_sem); } static int cpufreq_interactive_speedchange_task(void *data) @@ -418,10 +543,12 @@ static int cpufreq_interactive_speedchange_task(void *data) unsigned int max_freq = 0; pcpu = &per_cpu(cpuinfo, cpu); - smp_rmb(); - - if (!pcpu->governor_enabled) + if (!down_read_trylock(&pcpu->enable_sem)) + continue; + if (!pcpu->governor_enabled) { + up_read(&pcpu->enable_sem); continue; + } for_each_cpu(j, pcpu->policy->cpus) { struct cpufreq_interactive_cpuinfo *pjcpu = @@ -438,6 +565,8 @@ static int cpufreq_interactive_speedchange_task(void *data) trace_cpufreq_interactive_setspeed(cpu, pcpu->target_freq, pcpu->policy->cur); + + up_read(&pcpu->enable_sem); } } @@ -459,9 +588,8 @@ static void cpufreq_interactive_boost(void) if (pcpu->target_freq < hispeed_freq) { pcpu->target_freq = hispeed_freq; cpumask_set_cpu(i, &speedchange_cpumask); - pcpu->target_set_time_in_idle = - get_cpu_idle_time_us(i, &pcpu->target_set_time); - pcpu->hispeed_validate_time = pcpu->target_set_time; + pcpu->hispeed_validate_time = + ktime_to_us(ktime_get()); anyboost = 1; } @@ -480,6 +608,172 @@ static void cpufreq_interactive_boost(void) wake_up_process(speedchange_task); } +static int cpufreq_interactive_notifier( + struct notifier_block *nb, unsigned long val, void *data) +{ + struct cpufreq_freqs *freq = data; + struct cpufreq_interactive_cpuinfo *pcpu; + int cpu; + unsigned long flags; + + if (val == CPUFREQ_POSTCHANGE) { + pcpu = &per_cpu(cpuinfo, freq->cpu); + if (!down_read_trylock(&pcpu->enable_sem)) + return 0; + if (!pcpu->governor_enabled) { + up_read(&pcpu->enable_sem); + return 0; + } + + for_each_cpu(cpu, pcpu->policy->cpus) { + struct cpufreq_interactive_cpuinfo *pjcpu = + &per_cpu(cpuinfo, cpu); + spin_lock_irqsave(&pjcpu->load_lock, flags); + update_load(cpu); + spin_unlock_irqrestore(&pjcpu->load_lock, flags); + } + + up_read(&pcpu->enable_sem); + } + return 0; +} + +static struct notifier_block cpufreq_notifier_block = { + .notifier_call = cpufreq_interactive_notifier, +}; + +static unsigned int *get_tokenized_data(const char *buf, int *num_tokens) +{ + const char *cp; + int i; + int ntokens = 1; + unsigned int *tokenized_data; + int err = -EINVAL; + + cp = buf; + while ((cp = strpbrk(cp + 1, " :"))) + ntokens++; + + if (!(ntokens & 0x1)) + goto err; + + tokenized_data = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL); + if (!tokenized_data) { + err = -ENOMEM; + goto err; + } + + cp = buf; + i = 0; + while (i < ntokens) { + if (sscanf(cp, "%u", &tokenized_data[i++]) != 1) + goto err_kfree; + + cp = strpbrk(cp, " :"); + if (!cp) + break; + cp++; + } + + if (i != ntokens) + goto err_kfree; + + *num_tokens = ntokens; + return tokenized_data; + +err_kfree: + kfree(tokenized_data); +err: + return ERR_PTR(err); +} + +static ssize_t show_target_loads( + struct kobject *kobj, struct attribute *attr, char *buf) +{ + int i; + ssize_t ret = 0; + unsigned long flags; + + spin_lock_irqsave(&target_loads_lock, flags); + + for (i = 0; i < ntarget_loads; i++) + ret += sprintf(buf + ret, "%u%s", target_loads[i], + i & 0x1 ? ":" : " "); + + ret += sprintf(buf + ret, "\n"); + spin_unlock_irqrestore(&target_loads_lock, flags); + return ret; +} + +static ssize_t store_target_loads( + struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + int ntokens; + unsigned int *new_target_loads = NULL; + unsigned long flags; + + new_target_loads = get_tokenized_data(buf, &ntokens); + if (IS_ERR(new_target_loads)) + return PTR_RET(new_target_loads); + + spin_lock_irqsave(&target_loads_lock, flags); + if (target_loads != default_target_loads) + kfree(target_loads); + target_loads = new_target_loads; + ntarget_loads = ntokens; + spin_unlock_irqrestore(&target_loads_lock, flags); + return count; +} + +static struct global_attr target_loads_attr = + __ATTR(target_loads, S_IRUGO | S_IWUSR, + show_target_loads, store_target_loads); + +static ssize_t show_above_hispeed_delay( + struct kobject *kobj, struct attribute *attr, char *buf) +{ + int i; + ssize_t ret = 0; + unsigned long flags; + + spin_lock_irqsave(&above_hispeed_delay_lock, flags); + + for (i = 0; i < nabove_hispeed_delay; i++) + ret += sprintf(buf + ret, "%u%s", above_hispeed_delay[i], + i & 0x1 ? ":" : " "); + + ret += sprintf(buf + ret, "\n"); + spin_unlock_irqrestore(&above_hispeed_delay_lock, flags); + return ret; +} + +static ssize_t store_above_hispeed_delay( + struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + int ntokens; + unsigned int *new_above_hispeed_delay = NULL; + unsigned long flags; + + new_above_hispeed_delay = get_tokenized_data(buf, &ntokens); + if (IS_ERR(new_above_hispeed_delay)) + return PTR_RET(new_above_hispeed_delay); + + spin_lock_irqsave(&above_hispeed_delay_lock, flags); + if (above_hispeed_delay != default_above_hispeed_delay) + kfree(above_hispeed_delay); + above_hispeed_delay = new_above_hispeed_delay; + nabove_hispeed_delay = ntokens; + spin_unlock_irqrestore(&above_hispeed_delay_lock, flags); + return count; + +} + +static struct global_attr above_hispeed_delay_attr = + __ATTR(above_hispeed_delay, S_IRUGO | S_IWUSR, + show_above_hispeed_delay, store_above_hispeed_delay); + static ssize_t show_hispeed_freq(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -548,15 +842,14 @@ static ssize_t store_min_sample_time(struct kobject *kobj, static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644, show_min_sample_time, store_min_sample_time); -static ssize_t show_above_hispeed_delay(struct kobject *kobj, - struct attribute *attr, char *buf) +static ssize_t show_timer_rate(struct kobject *kobj, + struct attribute *attr, char *buf) { - return sprintf(buf, "%lu\n", above_hispeed_delay_val); + return sprintf(buf, "%lu\n", timer_rate); } -static ssize_t store_above_hispeed_delay(struct kobject *kobj, - struct attribute *attr, - const char *buf, size_t count) +static ssize_t store_timer_rate(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) { int ret; unsigned long val; @@ -564,33 +857,35 @@ static ssize_t store_above_hispeed_delay(struct kobject *kobj, ret = strict_strtoul(buf, 0, &val); if (ret < 0) return ret; - above_hispeed_delay_val = val; + timer_rate = val; return count; } -define_one_global_rw(above_hispeed_delay); +static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, + show_timer_rate, store_timer_rate); -static ssize_t show_timer_rate(struct kobject *kobj, - struct attribute *attr, char *buf) +static ssize_t show_timer_slack( + struct kobject *kobj, struct attribute *attr, char *buf) { - return sprintf(buf, "%lu\n", timer_rate); + return sprintf(buf, "%d\n", timer_slack_val); } -static ssize_t store_timer_rate(struct kobject *kobj, - struct attribute *attr, const char *buf, size_t count) +static ssize_t store_timer_slack( + struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) { int ret; unsigned long val; - ret = strict_strtoul(buf, 0, &val); + ret = kstrtol(buf, 10, &val); if (ret < 0) return ret; - timer_rate = val; + + timer_slack_val = val; return count; } -static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644, - show_timer_rate, store_timer_rate); +define_one_global_rw(timer_slack); static ssize_t show_boost(struct kobject *kobj, struct attribute *attr, char *buf) @@ -626,38 +921,78 @@ static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { int ret; - unsigned int val; + unsigned long val; - ret = sscanf(buf, "%u", &val); + ret = kstrtoul(buf, 0, &val); if (ret < 0) return ret; - boostpulse_boosted_time = ktime_to_us(ktime_get()); - - if (val > MAX_BOOSTPULSE_DURATION) - val = MAX_BOOSTPULSE_DURATION; - - if (val > DEFAULT_BOOSTPULSE_DURATION) - boostpulse_duration = val; - else - boostpulse_duration = DEFAULT_BOOSTPULSE_DURATION; - + boostpulse_endtime = ktime_to_us(ktime_get()) + boostpulse_duration_val; trace_cpufreq_interactive_boost("pulse"); - cpufreq_interactive_boost(); + cpufreq_interactive_boost(); return count; } static struct global_attr boostpulse = __ATTR(boostpulse, 0200, NULL, store_boostpulse); +static ssize_t show_boostpulse_duration( + struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", boostpulse_duration_val); +} + +static ssize_t store_boostpulse_duration( + struct kobject *kobj, struct attribute *attr, const char *buf, + size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + + boostpulse_duration_val = val; + return count; +} + +define_one_global_rw(boostpulse_duration); + +static ssize_t show_io_is_busy(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", io_is_busy); +} + +static ssize_t store_io_is_busy(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (ret < 0) + return ret; + io_is_busy = val; + return count; +} + +static struct global_attr io_is_busy_attr = __ATTR(io_is_busy, 0644, + show_io_is_busy, store_io_is_busy); + static struct attribute *interactive_attributes[] = { + &target_loads_attr.attr, + &above_hispeed_delay_attr.attr, &hispeed_freq_attr.attr, &go_hispeed_load_attr.attr, - &above_hispeed_delay.attr, &min_sample_time_attr.attr, &timer_rate_attr.attr, + &timer_slack.attr, &boost.attr, &boostpulse.attr, + &boostpulse_duration.attr, + &io_is_busy_attr.attr, NULL, }; @@ -699,66 +1034,82 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, if (!cpu_online(policy->cpu)) return -EINVAL; + mutex_lock(&gov_lock); + freq_table = cpufreq_frequency_get_table(policy->cpu); + if (!hispeed_freq) + hispeed_freq = policy->max; for_each_cpu(j, policy->cpus) { + unsigned long expires; + pcpu = &per_cpu(cpuinfo, j); pcpu->policy = policy; pcpu->target_freq = policy->cur; pcpu->freq_table = freq_table; - pcpu->target_set_time_in_idle = - get_cpu_idle_time_us(j, - &pcpu->target_set_time); pcpu->floor_freq = pcpu->target_freq; pcpu->floor_validate_time = - pcpu->target_set_time; + ktime_to_us(ktime_get()); pcpu->hispeed_validate_time = - pcpu->target_set_time; + pcpu->floor_validate_time; + down_write(&pcpu->enable_sem); + expires = jiffies + usecs_to_jiffies(timer_rate); + pcpu->cpu_timer.expires = expires; + add_timer_on(&pcpu->cpu_timer, j); + if (timer_slack_val >= 0) { + expires += usecs_to_jiffies(timer_slack_val); + pcpu->cpu_slack_timer.expires = expires; + add_timer_on(&pcpu->cpu_slack_timer, j); + } pcpu->governor_enabled = 1; - smp_wmb(); + up_write(&pcpu->enable_sem); } - if (!hispeed_freq) - hispeed_freq = policy->max; - /* * Do not register the idle hook and create sysfs * entries if we have already done so. */ - if (atomic_inc_return(&active_count) > 1) + if (++active_count > 1) { + mutex_unlock(&gov_lock); return 0; + } rc = sysfs_create_group(cpufreq_global_kobject, &interactive_attr_group); - if (rc) + if (rc) { + mutex_unlock(&gov_lock); return rc; + } idle_notifier_register(&cpufreq_interactive_idle_nb); + cpufreq_register_notifier( + &cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); + mutex_unlock(&gov_lock); break; case CPUFREQ_GOV_STOP: + mutex_lock(&gov_lock); for_each_cpu(j, policy->cpus) { pcpu = &per_cpu(cpuinfo, j); + down_write(&pcpu->enable_sem); pcpu->governor_enabled = 0; - smp_wmb(); del_timer_sync(&pcpu->cpu_timer); - - /* - * Reset idle exit time since we may cancel the timer - * before it can run after the last idle exit time, - * to avoid tripping the check in idle exit for a timer - * that is trying to run. - */ - pcpu->idle_exit_time = 0; + del_timer_sync(&pcpu->cpu_slack_timer); + up_write(&pcpu->enable_sem); } - if (atomic_dec_return(&active_count) > 0) + if (--active_count > 0) { + mutex_unlock(&gov_lock); return 0; + } + cpufreq_unregister_notifier( + &cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); idle_notifier_unregister(&cpufreq_interactive_idle_nb); sysfs_remove_group(cpufreq_global_kobject, &interactive_attr_group); + mutex_unlock(&gov_lock); break; @@ -774,26 +1125,32 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, return 0; } +static void cpufreq_interactive_nop_timer(unsigned long data) +{ +} + static int __init cpufreq_interactive_init(void) { unsigned int i; struct cpufreq_interactive_cpuinfo *pcpu; struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 }; - go_hispeed_load = DEFAULT_GO_HISPEED_LOAD; - min_sample_time = DEFAULT_MIN_SAMPLE_TIME; - above_hispeed_delay_val = DEFAULT_ABOVE_HISPEED_DELAY; - timer_rate = DEFAULT_TIMER_RATE; - /* Initalize per-cpu timers */ for_each_possible_cpu(i) { pcpu = &per_cpu(cpuinfo, i); - init_timer(&pcpu->cpu_timer); + init_timer_deferrable(&pcpu->cpu_timer); pcpu->cpu_timer.function = cpufreq_interactive_timer; pcpu->cpu_timer.data = i; + init_timer(&pcpu->cpu_slack_timer); + pcpu->cpu_slack_timer.function = cpufreq_interactive_nop_timer; + spin_lock_init(&pcpu->load_lock); + init_rwsem(&pcpu->enable_sem); } + spin_lock_init(&target_loads_lock); spin_lock_init(&speedchange_cpumask_lock); + spin_lock_init(&above_hispeed_delay_lock); + mutex_init(&gov_lock); speedchange_task = kthread_create(cpufreq_interactive_speedchange_task, NULL, "cfinteractive"); diff --git a/include/trace/events/cpufreq_interactive.h b/include/trace/events/cpufreq_interactive.h index ba0634e2f9d72..951e6ca12da81 100644 --- a/include/trace/events/cpufreq_interactive.h +++ b/include/trace/events/cpufreq_interactive.h @@ -36,68 +36,74 @@ DEFINE_EVENT(set, cpufreq_interactive_setspeed, DECLARE_EVENT_CLASS(loadeval, TP_PROTO(unsigned long cpu_id, unsigned long load, - unsigned long curfreq, unsigned long targfreq), - TP_ARGS(cpu_id, load, curfreq, targfreq), + unsigned long curtarg, unsigned long curactual, + unsigned long newtarg), + TP_ARGS(cpu_id, load, curtarg, curactual, newtarg), TP_STRUCT__entry( __field(unsigned long, cpu_id ) __field(unsigned long, load ) - __field(unsigned long, curfreq ) - __field(unsigned long, targfreq ) + __field(unsigned long, curtarg ) + __field(unsigned long, curactual ) + __field(unsigned long, newtarg ) ), TP_fast_assign( __entry->cpu_id = cpu_id; __entry->load = load; - __entry->curfreq = curfreq; - __entry->targfreq = targfreq; + __entry->curtarg = curtarg; + __entry->curactual = curactual; + __entry->newtarg = newtarg; ), - TP_printk("cpu=%lu load=%lu cur=%lu targ=%lu", - __entry->cpu_id, __entry->load, __entry->curfreq, - __entry->targfreq) + TP_printk("cpu=%lu load=%lu cur=%lu actual=%lu targ=%lu", + __entry->cpu_id, __entry->load, __entry->curtarg, + __entry->curactual, __entry->newtarg) ); DEFINE_EVENT(loadeval, cpufreq_interactive_target, TP_PROTO(unsigned long cpu_id, unsigned long load, - unsigned long curfreq, unsigned long targfreq), - TP_ARGS(cpu_id, load, curfreq, targfreq) + unsigned long curtarg, unsigned long curactual, + unsigned long newtarg), + TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) ); DEFINE_EVENT(loadeval, cpufreq_interactive_already, TP_PROTO(unsigned long cpu_id, unsigned long load, - unsigned long curfreq, unsigned long targfreq), - TP_ARGS(cpu_id, load, curfreq, targfreq) + unsigned long curtarg, unsigned long curactual, + unsigned long newtarg), + TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) ); DEFINE_EVENT(loadeval, cpufreq_interactive_notyet, TP_PROTO(unsigned long cpu_id, unsigned long load, - unsigned long curfreq, unsigned long targfreq), - TP_ARGS(cpu_id, load, curfreq, targfreq) + unsigned long curtarg, unsigned long curactual, + unsigned long newtarg), + TP_ARGS(cpu_id, load, curtarg, curactual, newtarg) ); TRACE_EVENT(cpufreq_interactive_boost, - TP_PROTO(char *s), + TP_PROTO(const char *s), TP_ARGS(s), TP_STRUCT__entry( - __field(char *, s) + __string(s, s) ), TP_fast_assign( - __entry->s = s; + __assign_str(s, s); ), - TP_printk("%s", __entry->s) + TP_printk("%s", __get_str(s)) ); TRACE_EVENT(cpufreq_interactive_unboost, - TP_PROTO(char *s), + TP_PROTO(const char *s), TP_ARGS(s), TP_STRUCT__entry( - __field(char *, s) + __string(s, s) ), TP_fast_assign( - __entry->s = s; + __assign_str(s, s); ), - TP_printk("%s", __entry->s) + TP_printk("%s", __get_str(s)) ); #endif /* _TRACE_CPUFREQ_INTERACTIVE_H */ From 6abd620fe0044f600eed4b61f07011df9d2a9839 Mon Sep 17 00:00:00 2001 From: Andrew Bartholomew Date: Fri, 10 Aug 2012 09:34:35 -0400 Subject: [PATCH 2512/2556] defconfig updates to support data usage limits in ICS Conflicts: arch/arm/configs/incrediblec-incredikernel_defconfig --- arch/arm/configs/incrediblec-incredikernel_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/incrediblec-incredikernel_defconfig b/arch/arm/configs/incrediblec-incredikernel_defconfig index cd2d9e1fa7275..e47d04bd2758f 100644 --- a/arch/arm/configs/incrediblec-incredikernel_defconfig +++ b/arch/arm/configs/incrediblec-incredikernel_defconfig @@ -694,7 +694,7 @@ CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y -# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +CONFIG_NETFILTER_XT_MATCH_TCPMSS=y CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y # CONFIG_IP_VS is not set From c937f9a2f2adcff17706de5d15643d67418a47d6 Mon Sep 17 00:00:00 2001 From: Andrew Bartholomew Date: Fri, 10 Aug 2012 11:02:57 -0400 Subject: [PATCH 2513/2556] update defconfig to support mobile data limit and warning Conflicts: arch/arm/configs/incrediblec-incredikernel_defconfig --- arch/arm/configs/incrediblec-incredikernel_defconfig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm/configs/incrediblec-incredikernel_defconfig b/arch/arm/configs/incrediblec-incredikernel_defconfig index e47d04bd2758f..e4dd81ec365fa 100644 --- a/arch/arm/configs/incrediblec-incredikernel_defconfig +++ b/arch/arm/configs/incrediblec-incredikernel_defconfig @@ -653,6 +653,7 @@ CONFIG_NETFILTER_XT_TARGET_NOTRACK=y CONFIG_NETFILTER_XT_TARGET_TPROXY=y CONFIG_NETFILTER_XT_TARGET_TRACE=y # CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y # CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set # @@ -694,7 +695,7 @@ CONFIG_NETFILTER_XT_MATCH_SOCKET=y CONFIG_NETFILTER_XT_MATCH_STATE=y CONFIG_NETFILTER_XT_MATCH_STATISTIC=y CONFIG_NETFILTER_XT_MATCH_STRING=y -CONFIG_NETFILTER_XT_MATCH_TCPMSS=y +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set CONFIG_NETFILTER_XT_MATCH_TIME=y CONFIG_NETFILTER_XT_MATCH_U32=y # CONFIG_IP_VS is not set @@ -761,7 +762,7 @@ CONFIG_IP6_NF_TARGET_LOG=y CONFIG_IP6_NF_FILTER=y CONFIG_IP6_NF_TARGET_REJECT=y CONFIG_IP6_NF_MANGLE=y -CONFIG_IP6_NF_RAW=y +# CONFIG_IP6_NF_RAW is not set # CONFIG_BRIDGE_NF_EBTABLES is not set # CONFIG_IP_DCCP is not set # CONFIG_IP_SCTP is not set From 7d04db54a7a9758800df1effcde8a59d1f364530 Mon Sep 17 00:00:00 2001 From: Narayanan Gopalakrishnan Date: Sat, 6 Oct 2012 00:42:13 -0400 Subject: [PATCH 2514/2556] cpufreq: ondemand: add boostpulse interface Squashed commit consisting of the following: -------- For applications that require a one-shot increase in frequency, the boostpulse interface would help to scale to max for the time specified in boostime. This helps applications that currently increases scaling_min_freq and resets later after a fixed duration. Instead of changing scaling_min_freq applications can directly write to boostpulse and governer resets during regular timer after the time expires. Signed-off-by: Narayanan Gopalakrishnan -------- cpufreq: ondemand: Fix the boostpulse interface * Boostpulse was not working because no default frequency was set. * We don't need an input handler to push the freq to max, userspace does a better job with the Power HAL. * Tweak boost time to 500ms cpufreq: ondemand: Add duration for boosting * Keep default behavior of 500ms boost if "1" is given, otherwise use the value (microseconds) as the boost duration. drivers: cpufreq: Add max duration for boosting * Don't allow boosting for more than 5 seconds. From: Steve Kondik -------- cpufreq: ondemand: Default to max From: Daniel Bateman -------- Change-Id: Ib272703616b9dabc9ab54221f6e2c84c9e9c6dda Conflicts: drivers/cpufreq/cpufreq_ondemand.c --- drivers/cpufreq/cpufreq_ondemand.c | 137 +++++++++-------------------- 1 file changed, 44 insertions(+), 93 deletions(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 1d05ac09444b7..58d46d250ae79 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -39,7 +39,6 @@ #define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000) #define MIN_FREQUENCY_UP_THRESHOLD (11) #define MAX_FREQUENCY_UP_THRESHOLD (100) -#define MIN_FREQUENCY_DOWN_DIFFERENTIAL (1) #define DEFAULT_FREQ_BOOST_TIME (500000) #define MAX_FREQ_BOOST_TIME (5000000) @@ -272,96 +271,9 @@ show_one(sampling_down_factor, sampling_down_factor); show_one(ignore_nice_load, ignore_nice); show_one(powersave_bias, powersave_bias); show_one(boostpulse, boosted); +show_one(boosttime, freq_boost_time); show_one(boostfreq, boostfreq); -/** - * update_sampling_rate - update sampling rate effective immediately if needed. - * @new_rate: new sampling rate - * - * If new rate is smaller than the old, simply updaing - * dbs_tuners_int.sampling_rate might not be appropriate. For example, - * if the original sampling_rate was 1 second and the requested new sampling - * rate is 10 ms because the user needs immediate reaction from ondemand - * governor, but not sure if higher frequency will be required or not, - * then, the governor may change the sampling rate too late; up to 1 second - * later. Thus, if we are reducing the sampling rate, we need to make the - * new value effective immediately. - */ -static void update_sampling_rate(unsigned int new_rate) -{ - int cpu; - - dbs_tuners_ins.sampling_rate = new_rate - = max(new_rate, min_sampling_rate); - - for_each_online_cpu(cpu) { - struct cpufreq_policy *policy; - struct cpu_dbs_info_s *dbs_info; - unsigned long next_sampling, appointed_at; - - policy = cpufreq_cpu_get(cpu); - if (!policy) - continue; - dbs_info = &per_cpu(od_cpu_dbs_info, policy->cpu); - cpufreq_cpu_put(policy); - - mutex_lock(&dbs_info->timer_mutex); - - if (!delayed_work_pending(&dbs_info->work)) { - mutex_unlock(&dbs_info->timer_mutex); - continue; - } - - next_sampling = jiffies + usecs_to_jiffies(new_rate); - appointed_at = dbs_info->work.timer.expires; - - - if (time_before(next_sampling, appointed_at)) { - - mutex_unlock(&dbs_info->timer_mutex); - cancel_delayed_work_sync(&dbs_info->work); - mutex_lock(&dbs_info->timer_mutex); - - schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work, - usecs_to_jiffies(new_rate)); - - } - mutex_unlock(&dbs_info->timer_mutex); - } -} - -static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t count) -{ - int ret; - unsigned int input; - - ret = sscanf(buf, "%u", &input); - if (ret < 0) - return ret; - - if (input > 1 && input <= MAX_FREQ_BOOST_TIME) - dbs_tuners_ins.freq_boost_time = input; - else - dbs_tuners_ins.freq_boost_time = DEFAULT_FREQ_BOOST_TIME; - - dbs_tuners_ins.boosted = 1; - freq_boosted_time = ktime_to_us(ktime_get()); - return count; -} - -static ssize_t store_boostfreq(struct kobject *a, struct attribute *b, - const char *buf, size_t count) -{ - unsigned int input; - int ret; - ret = sscanf(buf, "%u", &input); - if (ret != 1) - return -EINVAL; - dbs_tuners_ins.boostfreq = input; - return count; -} - static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, const char *buf, size_t count) { @@ -490,6 +402,39 @@ static ssize_t store_powersave_bias(struct kobject *a, struct attribute *b, return count; } + +static ssize_t store_boostpulse(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned int input; + + ret = sscanf(buf, "%u", &input); + if (ret < 0) + return ret; + + if (input > 1 && input <= MAX_FREQ_BOOST_TIME) + dbs_tuners_ins.freq_boost_time = input; + else + dbs_tuners_ins.freq_boost_time = DEFAULT_FREQ_BOOST_TIME; + + dbs_tuners_ins.boosted = 1; + freq_boosted_time = ktime_to_us(ktime_get()); + return count; +} + +static ssize_t store_boostfreq(struct kobject *a, struct attribute *b, + const char *buf, size_t count) +{ + unsigned int input; + int ret; + ret = sscanf(buf, "%u", &input); + if (ret != 1) + return -EINVAL; + dbs_tuners_ins.boostfreq = input; + return count; +} + define_one_global_rw(sampling_rate); define_one_global_rw(io_is_busy); define_one_global_rw(up_threshold); @@ -543,6 +488,7 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) struct cpufreq_policy *policy; unsigned int j; + unsigned int boostfreq; this_dbs_info->freq_lo = 0; policy = this_dbs_info->cur_policy; @@ -553,6 +499,10 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) dbs_tuners_ins.boosted = 0; } } + if (dbs_tuners_ins.boostfreq != 0) + boostfreq = dbs_tuners_ins.boostfreq; + else + boostfreq = policy->max; /* * Every sampling_rate, we check, if current idle time is less * than 20% (default), then we try to increase frequency @@ -658,8 +608,9 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) } /* check for frequency boost */ - if (dbs_tuners_ins.boosted && policy->cur < dbs_tuners_ins.boostfreq) { - dbs_freq_increase(policy, dbs_tuners_ins.boostfreq); + if (dbs_tuners_ins.boosted && policy->cur < boostfreq) { + dbs_freq_increase(policy, boostfreq); + dbs_tuners_ins.boostfreq = policy->cur; return; } @@ -682,8 +633,8 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) dbs_tuners_ins.down_differential); if (dbs_tuners_ins.boosted && - freq_next < dbs_tuners_ins.boostfreq) { - freq_next = dbs_tuners_ins.boostfreq; + freq_next < boostfreq) { + freq_next = boostfreq; } /* No longer fully busy, reset rate_mult */ this_dbs_info->rate_mult = 1; From 74985510c53f91183e0531d6c2c959e8efc17cf7 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Tue, 11 Sep 2012 20:13:10 -0700 Subject: [PATCH 2515/2556] drivers: cpufreq: Send a uevent when governor changes * Useful so userspace tools can reconfigure. Change-Id: Ib423910b8b9ac791ebe81a75bf399f58272f64f2 Conflicts: drivers/cpufreq/cpufreq.c From 76ffd314803302b42de5680ffb32c58abb789a2a Mon Sep 17 00:00:00 2001 From: Zach Date: Mon, 17 Feb 2014 16:56:05 -0500 Subject: [PATCH 2516/2556] This is KK not JB --- arch/arm/configs/incrediblec-incredikernel_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/incrediblec-incredikernel_defconfig b/arch/arm/configs/incrediblec-incredikernel_defconfig index e4dd81ec365fa..1725943140f7c 100644 --- a/arch/arm/configs/incrediblec-incredikernel_defconfig +++ b/arch/arm/configs/incrediblec-incredikernel_defconfig @@ -33,7 +33,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="arm-eabi-" -CONFIG_LOCALVERSION="-incredikernel-jb44" +CONFIG_LOCALVERSION="-incredikernel-kk44" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_HAVE_KERNEL_GZIP=y CONFIG_HAVE_KERNEL_LZMA=y From 0faddc79e7b6beceef834d198fe69dd98c5b3bb0 Mon Sep 17 00:00:00 2001 From: Zach Date: Mon, 17 Feb 2014 17:39:01 -0500 Subject: [PATCH 2517/2556] I duplicated a line in the defconfig, now its fixed --- arch/arm/configs/incrediblec-incredikernel_defconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/configs/incrediblec-incredikernel_defconfig b/arch/arm/configs/incrediblec-incredikernel_defconfig index 1725943140f7c..2e05d430eeb27 100644 --- a/arch/arm/configs/incrediblec-incredikernel_defconfig +++ b/arch/arm/configs/incrediblec-incredikernel_defconfig @@ -652,7 +652,6 @@ CONFIG_NETFILTER_XT_TARGET_NOTRACK=y # CONFIG_NETFILTER_XT_TARGET_TEE is not set CONFIG_NETFILTER_XT_TARGET_TPROXY=y CONFIG_NETFILTER_XT_TARGET_TRACE=y -# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set CONFIG_NETFILTER_XT_TARGET_TCPMSS=y # CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set From f710a628b4b1964a07fc0b5614d3a197b73d8bf1 Mon Sep 17 00:00:00 2001 From: Zach Date: Mon, 17 Feb 2014 18:40:45 -0500 Subject: [PATCH 2518/2556] Fix Compile of governers --- drivers/cpufreq/cpufreq_ondemand.c | 97 +++--------------------------- include/linux/err.h | 8 +++ 2 files changed, 16 insertions(+), 89 deletions(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 58d46d250ae79..9d236fe4e2eb5 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -22,8 +22,6 @@ #include #include #include -#include -#include /* * dbs is used in this file as a shortform for demandbased switching @@ -57,7 +55,7 @@ u64 freq_boosted_time; #define MIN_SAMPLING_RATE_RATIO (2) static unsigned int min_sampling_rate; -#define DEFAULT_SAMPLING_RATE (50000) + #define LATENCY_MULTIPLIER (1000) #define MIN_LATENCY_MULTIPLIER (100) #define TRANSITION_LATENCY_LIMIT (10 * 1000 * 1000) @@ -91,7 +89,6 @@ struct cpu_dbs_info_s { unsigned int freq_lo_jiffies; unsigned int freq_hi_jiffies; unsigned int rate_mult; - unsigned int load_at_prev_sample; int cpu; unsigned int sample_type:1; /* @@ -110,8 +107,6 @@ static unsigned int dbs_enable; /* number of CPUs using this policy */ */ static DEFINE_MUTEX(dbs_mutex); -static DEFINE_PER_CPU(struct work_struct, dbs_refresh_work); - static struct dbs_tuners { unsigned int sampling_rate; unsigned int up_threshold; @@ -266,7 +261,6 @@ static ssize_t show_##file_name \ show_one(sampling_rate, sampling_rate); show_one(io_is_busy, io_is_busy); show_one(up_threshold, up_threshold); -show_one(down_differential, down_differential); show_one(sampling_down_factor, sampling_down_factor); show_one(ignore_nice_load, ignore_nice); show_one(powersave_bias, powersave_bias); @@ -282,7 +276,7 @@ static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, ret = sscanf(buf, "%u", &input); if (ret != 1) return -EINVAL; - update_sampling_rate(input); + dbs_tuners_ins.sampling_rate = max(input, min_sampling_rate); return count; } @@ -314,23 +308,6 @@ static ssize_t store_up_threshold(struct kobject *a, struct attribute *b, return count; } -static ssize_t store_down_differential(struct kobject *a, struct attribute *b, - const char *buf, size_t count) -{ - unsigned int input; - int ret; - ret = sscanf(buf, "%u", &input); - - if (ret != 1 || input >= dbs_tuners_ins.up_threshold || - input < MIN_FREQUENCY_DOWN_DIFFERENTIAL) { - return -EINVAL; - } - - dbs_tuners_ins.down_differential = input; - - return count; -} - static ssize_t store_sampling_down_factor(struct kobject *a, struct attribute *b, const char *buf, size_t count) { @@ -438,7 +415,6 @@ static ssize_t store_boostfreq(struct kobject *a, struct attribute *b, define_one_global_rw(sampling_rate); define_one_global_rw(io_is_busy); define_one_global_rw(up_threshold); -define_one_global_rw(down_differential); define_one_global_rw(sampling_down_factor); define_one_global_rw(ignore_nice_load); define_one_global_rw(powersave_bias); @@ -449,7 +425,6 @@ static struct attribute *dbs_attributes[] = { &sampling_rate_min.attr, &sampling_rate.attr, &up_threshold.attr, - &down_differential.attr, &sampling_down_factor.attr, &ignore_nice_load.attr, &powersave_bias.attr, @@ -479,12 +454,7 @@ static void dbs_freq_increase(struct cpufreq_policy *p, unsigned int freq) static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) { - /* Extrapolated load of this CPU */ - unsigned int load_at_max_freq = 0; - unsigned int avg_load_at_max_freq = 0; unsigned int max_load_freq; - /* Current load across this CPU */ - unsigned int cur_load = 0; struct cpufreq_policy *policy; unsigned int j; @@ -522,7 +492,7 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) struct cpu_dbs_info_s *j_dbs_info; cputime64_t cur_wall_time, cur_idle_time, cur_iowait_time; unsigned int idle_time, wall_time, iowait_time; - unsigned int load_freq; + unsigned int load, load_freq; int freq_avg; j_dbs_info = &per_cpu(od_cpu_dbs_info, j); @@ -572,31 +542,17 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info) if (unlikely(!wall_time || wall_time < idle_time)) continue; - cur_load = 100 * (wall_time - idle_time) / wall_time; + load = 100 * (wall_time - idle_time) / wall_time; freq_avg = __cpufreq_driver_getavg(policy, j); if (freq_avg <= 0) freq_avg = policy->cur; - load_freq = cur_load * freq_avg; + load_freq = load * freq_avg; if (load_freq > max_load_freq) max_load_freq = load_freq; - - /* calculate the scaled load across CPU */ - load_at_max_freq += (cur_load * policy->cur) / - policy->cpuinfo.max_freq; - - avg_load_at_max_freq += ((load_at_max_freq + - j_dbs_info->load_at_prev_sample) / 2); - - j_dbs_info->load_at_prev_sample = load_at_max_freq; } - if (min_sampling_rate < DEFAULT_SAMPLING_RATE) - cpufreq_notify_utilization(policy, avg_load_at_max_freq); - else - cpufreq_notify_utilization(policy, load_at_max_freq); - /* Check for frequency increase */ if (max_load_freq > dbs_tuners_ins.up_threshold * policy->cur) { /* If switching to max speed, apply sampling_down_factor */ @@ -734,34 +690,6 @@ static int should_io_be_busy(void) return 0; } -static void dbs_refresh_callback(struct work_struct *unused) -{ - struct cpufreq_policy *policy; - struct cpu_dbs_info_s *this_dbs_info; - unsigned int cpu = smp_processor_id(); - - if (lock_policy_rwsem_write(cpu) < 0) - return; - - this_dbs_info = &per_cpu(od_cpu_dbs_info, cpu); - policy = this_dbs_info->cur_policy; - if (!policy) { - /* CPU not using ondemand governor */ - unlock_policy_rwsem_write(cpu); - return; - } - - if (policy->cur < policy->max) { - policy->cur = policy->max; - - __cpufreq_driver_target(policy, policy->max, - CPUFREQ_RELATION_L); - this_dbs_info->prev_cpu_idle = get_cpu_idle_time(cpu, - &this_dbs_info->prev_cpu_wall); - } - unlock_policy_rwsem_write(cpu); -} - static int cpufreq_governor_dbs(struct cpufreq_policy *policy, unsigned int event) { @@ -823,6 +751,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, } mutex_unlock(&dbs_mutex); + mutex_init(&this_dbs_info->timer_mutex); dbs_timer_init(this_dbs_info); break; @@ -832,13 +761,11 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, mutex_lock(&dbs_mutex); mutex_destroy(&this_dbs_info->timer_mutex); dbs_enable--; - /* If device is being removed, policy is no longer - * valid. */ - this_dbs_info->cur_policy = NULL; + mutex_unlock(&dbs_mutex); if (!dbs_enable) sysfs_remove_group(cpufreq_global_kobject, &dbs_attr_group); - mutex_unlock(&dbs_mutex); + break; case CPUFREQ_GOV_LIMITS: @@ -859,7 +786,6 @@ static int __init cpufreq_gov_dbs_init(void) { cputime64_t wall; u64 idle_time; - unsigned int i; int cpu = get_cpu(); idle_time = get_cpu_idle_time_us(cpu, &wall); @@ -881,13 +807,6 @@ static int __init cpufreq_gov_dbs_init(void) MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10); } - for_each_possible_cpu(i) { - struct cpu_dbs_info_s *this_dbs_info = - &per_cpu(od_cpu_dbs_info, i); - mutex_init(&this_dbs_info->timer_mutex); - INIT_WORK(&per_cpu(dbs_refresh_work, i), dbs_refresh_callback); - } - return cpufreq_register_governor(&cpufreq_gov_ondemand); } diff --git a/include/linux/err.h b/include/linux/err.h index 448afc12c78af..f2edce25a76b6 100644 --- a/include/linux/err.h +++ b/include/linux/err.h @@ -52,6 +52,14 @@ static inline void * __must_check ERR_CAST(const void *ptr) return (void *) ptr; } +static inline int __must_check PTR_RET(const void *ptr) +{ + if (IS_ERR(ptr)) + return PTR_ERR(ptr); + else + return 0; +} + #endif #endif /* _LINUX_ERR_H */ From 56f8ee42ab330e9cf9fd45533785d94acd13dd8b Mon Sep 17 00:00:00 2001 From: Andrew Bartholomew Date: Tue, 11 Dec 2012 19:36:42 -0500 Subject: [PATCH 2519/2556] cpufreq: overclock max speed now supports 1.15 and 1.19 GHz Conflicts: arch/arm/mach-msm/acpuclock-qsd8x50.c arch/arm/mach-msm/avs.c --- arch/arm/mach-msm/acpuclock-qsd8x50.c | 2 ++ arch/arm/mach-msm/avs.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/arch/arm/mach-msm/acpuclock-qsd8x50.c b/arch/arm/mach-msm/acpuclock-qsd8x50.c index 967622f3f9b6a..69acc9021016e 100644 --- a/arch/arm/mach-msm/acpuclock-qsd8x50.c +++ b/arch/arm/mach-msm/acpuclock-qsd8x50.c @@ -112,6 +112,8 @@ struct clkctl_acpu_speed acpu_freq_tbl[] = { { 1036800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1B, 0, 1275, 128000 }, { 1075200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1C, 0, 1275, 128000 }, { 1113600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1D, 0, 1275, 128000 }, + { 1152000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1E, 0, 1325, 128000 }, + { 1190400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1F, 0, 1325, 128000 }, { 0 }, }; diff --git a/arch/arm/mach-msm/avs.c b/arch/arm/mach-msm/avs.c index 72fe9d78a370a..ab857f644a34d 100644 --- a/arch/arm/mach-msm/avs.c +++ b/arch/arm/mach-msm/avs.c @@ -120,6 +120,8 @@ struct clkctl_acpu_speed acpu_vdd_tbl[] = { { 1036800, 1275, 1275 }, { 1075200, 1275, 1275 }, { 1113600, 1275, 1275 }, + { 1152000, 1300, VOLTAGE_MAX }, + { 1190400, 1300, VOLTAGE_MAX }, { 0 }, }; From eac2484541a12b1f8fac35d963c6f69620bdfd6f Mon Sep 17 00:00:00 2001 From: anarkia1976 Date: Fri, 29 Mar 2013 07:44:02 +0100 Subject: [PATCH 2520/2556] fs/dyn_sync_cntrl: dynamic sync control - Thanks Faux123 Conflicts: arch/arm/configs/ktmanta_defconfig --- fs/Kconfig | 6 ++ fs/Makefile | 2 + fs/dyn_sync_cntrl.c | 155 ++++++++++++++++++++++++++++++++++++++++++++ fs/sync.c | 38 ++++++++++- include/linux/fs.h | 1 + 5 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 fs/dyn_sync_cntrl.c diff --git a/fs/Kconfig b/fs/Kconfig index 663d3389952f9..aabf18f291479 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -256,4 +256,10 @@ endif source "fs/nls/Kconfig" source "fs/dlm/Kconfig" +config DYNAMIC_FSYNC + bool "dynamic file sync control" + default n + help + An experimental file sync control using Android's early suspend / late resume drivers + endmenu diff --git a/fs/Makefile b/fs/Makefile index 7cb19ebff625b..31b7d85e2ea89 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -124,3 +124,5 @@ obj-$(CONFIG_CEPH_FS) += ceph/ # Patched by YAFFS obj-$(CONFIG_YAFFS_FS) += yaffs2/ + +obj-$(CONFIG_DYNAMIC_FSYNC) += dyn_sync_cntrl.o diff --git a/fs/dyn_sync_cntrl.c b/fs/dyn_sync_cntrl.c new file mode 100644 index 0000000000000..21befb7427e17 --- /dev/null +++ b/fs/dyn_sync_cntrl.c @@ -0,0 +1,155 @@ +/* + * Author: Paul Reioux aka Faux123 + * + * Copyright 2012 Paul Reioux + * + * 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 + +#define DYN_FSYNC_VERSION 1 + +/* + * fsync_mutex protects dyn_fsync_active during early suspend / lat resume transitions + */ +static DEFINE_MUTEX(fsync_mutex); + +bool early_suspend_active = false; +static bool dyn_fsync_active = true; + +static ssize_t dyn_fsync_active_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", (dyn_fsync_active ? 1 : 0)); +} + +static ssize_t dyn_fsync_active_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) +{ + unsigned int data; + + if(sscanf(buf, "%u\n", &data) == 1) { + if (data == 1) { + pr_info("%s: dynamic fsync enabled\n", __FUNCTION__); + dyn_fsync_active = true; + } + else if (data == 0) { + pr_info("%s: dyanamic fsync disabled\n", __FUNCTION__); + dyn_fsync_active = false; + } + else + pr_info("%s: bad value: %u\n", __FUNCTION__, data); + } else + pr_info("%s: unknown input!\n", __FUNCTION__); + + return count; +} + +static ssize_t dyn_fsync_version_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "version: %u\n", DYN_FSYNC_VERSION); +} + +static ssize_t dyn_fsync_earlysuspend_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "early suspend active: %u\n", early_suspend_active); +} + +static struct kobj_attribute dyn_fsync_active_attribute = + __ATTR(Dyn_fsync_active, 0666, dyn_fsync_active_show, dyn_fsync_active_store); + +static struct kobj_attribute dyn_fsync_version_attribute = + __ATTR(Dyn_fsync_version, 0444 , dyn_fsync_version_show, NULL); + +static struct kobj_attribute dyn_fsync_earlysuspend_attribute = + __ATTR(Dyn_fsync_earlysuspend, 0444 , dyn_fsync_earlysuspend_show, NULL); + +static struct attribute *dyn_fsync_active_attrs[] = + { + &dyn_fsync_active_attribute.attr, + &dyn_fsync_version_attribute.attr, + &dyn_fsync_earlysuspend_attribute.attr, + NULL, + }; + +static struct attribute_group dyn_fsync_active_attr_group = + { + .attrs = dyn_fsync_active_attrs, + }; + +static struct kobject *dyn_fsync_kobj; + +static void dyn_fsync_early_suspend(struct early_suspend *h) +{ + mutex_lock(&fsync_mutex); + if (dyn_fsync_active) { + early_suspend_active = true; +#if 1 + /* flush all outstanding buffers */ + wakeup_flusher_threads(0); + sync_filesystems(0); + sync_filesystems(1); +#endif + } + mutex_unlock(&fsync_mutex); +} + +static void dyn_fsync_late_resume(struct early_suspend *h) +{ + mutex_lock(&fsync_mutex); + early_suspend_active = false; + mutex_unlock(&fsync_mutex); +} + +static struct early_suspend dyn_fsync_early_suspend_handler = + { + .level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN, + .suspend = dyn_fsync_early_suspend, + .resume = dyn_fsync_late_resume, + }; + +static int dyn_fsync_init(void) +{ + int sysfs_result; + + register_early_suspend(&dyn_fsync_early_suspend_handler); + + dyn_fsync_kobj = kobject_create_and_add("dyn_fsync", kernel_kobj); + if (!dyn_fsync_kobj) { + pr_err("%s dyn_fsync kobject create failed!\n", __FUNCTION__); + return -ENOMEM; + } + + sysfs_result = sysfs_create_group(dyn_fsync_kobj, &dyn_fsync_active_attr_group); + + if (sysfs_result) { + pr_info("%s dyn_fsync sysfs create failed!\n", __FUNCTION__); + kobject_put(dyn_fsync_kobj); + } + return sysfs_result; +} + +static void dyn_fsync_exit(void) +{ + unregister_early_suspend(&dyn_fsync_early_suspend_handler); + + if (dyn_fsync_kobj != NULL) + kobject_put(dyn_fsync_kobj); +} + +module_init(dyn_fsync_init); +module_exit(dyn_fsync_exit); + diff --git a/fs/sync.c b/fs/sync.c index 412dc89163d31..de94e800a8dbb 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -17,6 +17,10 @@ #include #include "internal.h" +#ifdef CONFIG_DYNAMIC_FSYNC +extern bool early_suspend_active; +#endif + #define VALID_FLAGS (SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE| \ SYNC_FILE_RANGE_WAIT_AFTER) @@ -86,7 +90,7 @@ static void sync_one_sb(struct super_block *sb, void *arg) * Sync all the data for all the filesystems (called by sys_sync() and * emergency sync) */ -static void sync_filesystems(int wait) +void sync_filesystems(int wait) { iterate_supers(sync_one_sb, &wait); } @@ -141,6 +145,11 @@ void emergency_sync(void) */ int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync) { +#ifdef CONFIG_DYNAMIC_FSYNC + if (!early_suspend_active) + return 0; + else { +#endif struct address_space *mapping = file->f_mapping; int err, ret; @@ -163,6 +172,9 @@ int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync) out: return ret; +#ifdef CONFIG_DYNAMIC_FSYNC + } +#endif } EXPORT_SYMBOL(vfs_fsync_range); @@ -195,11 +207,21 @@ static int do_fsync(unsigned int fd, int datasync) SYSCALL_DEFINE1(fsync, unsigned int, fd) { +#ifdef CONFIG_DYNAMIC_FSYNC + if (!early_suspend_active) + return 0; + else +#endif return do_fsync(fd, 0); } SYSCALL_DEFINE1(fdatasync, unsigned int, fd) { +#ifdef CONFIG_DYNAMIC_FSYNC + if (!early_suspend_active) + return 0; + else +#endif return do_fsync(fd, 1); } @@ -270,6 +292,12 @@ EXPORT_SYMBOL(generic_write_sync); SYSCALL_DEFINE(sync_file_range)(int fd, loff_t offset, loff_t nbytes, unsigned int flags) { +#ifdef CONFIG_DYNAMIC_FSYNC + if (!early_suspend_active) + return 0; + else { +#endif + int ret; struct file *file; struct address_space *mapping; @@ -349,6 +377,9 @@ SYSCALL_DEFINE(sync_file_range)(int fd, loff_t offset, loff_t nbytes, fput_light(file, fput_needed); out: return ret; +#ifdef CONFIG_DYNAMIC_FSYNC + } +#endif } #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS asmlinkage long SyS_sync_file_range(long fd, loff_t offset, loff_t nbytes, @@ -365,6 +396,11 @@ SYSCALL_ALIAS(sys_sync_file_range, SyS_sync_file_range); SYSCALL_DEFINE(sync_file_range2)(int fd, unsigned int flags, loff_t offset, loff_t nbytes) { +#ifdef CONFIG_DYNAMIC_FSYNC + if (!early_suspend_active) + return 0; + else +#endif return sys_sync_file_range(fd, offset, nbytes, flags); } #ifdef CONFIG_HAVE_SYSCALL_WRAPPERS diff --git a/include/linux/fs.h b/include/linux/fs.h index a42fb95e04c99..f05c0162880e6 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2046,6 +2046,7 @@ static inline int thaw_bdev(struct block_device *bdev, struct super_block *sb) } #endif extern int sync_filesystem(struct super_block *); +extern void sync_filesystems(int wait); extern const struct file_operations def_blk_fops; extern const struct file_operations def_chr_fops; extern const struct file_operations bad_sock_fops; From 0f85e598798592d3e0caf3aa20e46000de743ae6 Mon Sep 17 00:00:00 2001 From: Andrew Bartholomew Date: Thu, 11 Apr 2013 10:36:00 -0400 Subject: [PATCH 2521/2556] dynamic fsync control disabled by default --- fs/dyn_sync_cntrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/dyn_sync_cntrl.c b/fs/dyn_sync_cntrl.c index 21befb7427e17..420fb531af253 100644 --- a/fs/dyn_sync_cntrl.c +++ b/fs/dyn_sync_cntrl.c @@ -30,7 +30,7 @@ static DEFINE_MUTEX(fsync_mutex); bool early_suspend_active = false; -static bool dyn_fsync_active = true; +static bool dyn_fsync_active = false; static ssize_t dyn_fsync_active_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { From 8e7f3cc0554abb3346ffb9d2e9af8ff5fd3a3808 Mon Sep 17 00:00:00 2001 From: Andrew Bartholomew Date: Sat, 13 Apr 2013 20:30:36 -0400 Subject: [PATCH 2522/2556] fs/dyn_sync_cntrl: explictly check if dyn fsync control is active prior to performing fsync operations --- fs/dyn_sync_cntrl.c | 2 +- fs/sync.c | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/dyn_sync_cntrl.c b/fs/dyn_sync_cntrl.c index 420fb531af253..3d6a0e3fafde8 100644 --- a/fs/dyn_sync_cntrl.c +++ b/fs/dyn_sync_cntrl.c @@ -30,7 +30,7 @@ static DEFINE_MUTEX(fsync_mutex); bool early_suspend_active = false; -static bool dyn_fsync_active = false; +bool dyn_fsync_active = false; static ssize_t dyn_fsync_active_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { diff --git a/fs/sync.c b/fs/sync.c index de94e800a8dbb..12ce4845f8df1 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -19,6 +19,7 @@ #ifdef CONFIG_DYNAMIC_FSYNC extern bool early_suspend_active; +extern bool dyn_fsync_active; #endif #define VALID_FLAGS (SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE| \ @@ -146,7 +147,7 @@ void emergency_sync(void) int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync) { #ifdef CONFIG_DYNAMIC_FSYNC - if (!early_suspend_active) + if (dyn_fsync_active && !early_suspend_active) return 0; else { #endif @@ -208,7 +209,7 @@ static int do_fsync(unsigned int fd, int datasync) SYSCALL_DEFINE1(fsync, unsigned int, fd) { #ifdef CONFIG_DYNAMIC_FSYNC - if (!early_suspend_active) + if (dyn_fsync_active && !early_suspend_active) return 0; else #endif @@ -218,7 +219,7 @@ SYSCALL_DEFINE1(fsync, unsigned int, fd) SYSCALL_DEFINE1(fdatasync, unsigned int, fd) { #ifdef CONFIG_DYNAMIC_FSYNC - if (!early_suspend_active) + if (dyn_fsync_active && !early_suspend_active) return 0; else #endif @@ -293,7 +294,7 @@ SYSCALL_DEFINE(sync_file_range)(int fd, loff_t offset, loff_t nbytes, unsigned int flags) { #ifdef CONFIG_DYNAMIC_FSYNC - if (!early_suspend_active) + if (dyn_fsync_active && !early_suspend_active) return 0; else { #endif @@ -397,7 +398,7 @@ SYSCALL_DEFINE(sync_file_range2)(int fd, unsigned int flags, loff_t offset, loff_t nbytes) { #ifdef CONFIG_DYNAMIC_FSYNC - if (!early_suspend_active) + if (dyn_fsync_active && !early_suspend_active) return 0; else #endif From f6887174add55a143f201260e6a0cf154eff814c Mon Sep 17 00:00:00 2001 From: Zach Date: Thu, 10 Apr 2014 17:25:53 -0400 Subject: [PATCH 2523/2556] defconfig: enable dynamic fsync --- arch/arm/configs/incrediblec-incredikernel_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/incrediblec-incredikernel_defconfig b/arch/arm/configs/incrediblec-incredikernel_defconfig index 2e05d430eeb27..3b7151c34cc24 100644 --- a/arch/arm/configs/incrediblec-incredikernel_defconfig +++ b/arch/arm/configs/incrediblec-incredikernel_defconfig @@ -1970,6 +1970,7 @@ CONFIG_NLS_ISO8859_1=y # CONFIG_NLS_KOI8_R is not set # CONFIG_NLS_KOI8_U is not set # CONFIG_NLS_UTF8 is not set +CONFIG_DYNAMIC_FSYNC=y # # Kernel hacking From eb4205f588137b8fef5d922013b2b41500786394 Mon Sep 17 00:00:00 2001 From: imoseyon Date: Thu, 3 Jan 2013 18:22:22 -0800 Subject: [PATCH 2524/2556] random: entropy tweaks are all the rage nowadays use nonblocking for all. Read this web page: http://lwn.net/Articles/489734/ --- drivers/char/random.c | 56 +++---------------------------------------- 1 file changed, 3 insertions(+), 53 deletions(-) diff --git a/drivers/char/random.c b/drivers/char/random.c index 72a4fcb177450..11ed4b73597ed 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -263,14 +263,14 @@ * The minimum number of bits of entropy before we wake up a read on * /dev/random. Should be enough to do a significant reseed. */ -static int random_read_wakeup_thresh = 64; +static int random_read_wakeup_thresh = 256; /* * If the entropy count falls under this number of bits, then we * should wake up processes which are selecting or polling on write * access to /dev/random. */ -static int random_write_wakeup_thresh = 128; +static int random_write_wakeup_thresh = 512; /* * When the input pool goes over trickle_thresh, start dropping most @@ -997,57 +997,7 @@ void rand_initialize_disk(struct gendisk *disk) static ssize_t random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { - ssize_t n, retval = 0, count = 0; - - if (nbytes == 0) - return 0; - - while (nbytes > 0) { - n = nbytes; - if (n > SEC_XFER_SIZE) - n = SEC_XFER_SIZE; - - DEBUG_ENT("reading %d bits\n", n*8); - - n = extract_entropy_user(&blocking_pool, buf, n); - - DEBUG_ENT("read got %d bits (%d still needed)\n", - n*8, (nbytes-n)*8); - - if (n == 0) { - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - - DEBUG_ENT("sleeping?\n"); - - wait_event_interruptible(random_read_wait, - input_pool.entropy_count >= - random_read_wakeup_thresh); - - DEBUG_ENT("awake\n"); - - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - - continue; - } - - if (n < 0) { - retval = n; - break; - } - count += n; - buf += n; - nbytes -= n; - break; /* This break makes the device work */ - /* like a named pipe */ - } - - return (count ? count : retval); + return extract_entropy_user(&nonblocking_pool, buf, nbytes); } static ssize_t From 536c37ca7aee8595cf99b39b3994ecf620111e6a Mon Sep 17 00:00:00 2001 From: "jeff.liu" Date: Fri, 4 Jan 2013 14:38:25 -0500 Subject: [PATCH 2525/2556] [v4] binfmt_elf.c: use get_random_int() to fix entropy depleting --- fs/binfmt_elf.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 70470b2b7f73d..e7d78eff65128 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -133,6 +133,25 @@ static int padzero(unsigned long elf_bss) #define ELF_BASE_PLATFORM NULL #endif +/* + * Use get_random_int() to implement AT_RANDOM while avoiding depletion + * of the entropy pool. + */ +static void get_atrandom_bytes(unsigned char *buf, size_t nbytes) +{ + unsigned char *p = buf; + + while (nbytes) { + unsigned int random_variable; + size_t chunk = min(nbytes, sizeof(random_variable)); + + random_variable = get_random_int(); + memcpy(p, &random_variable, chunk); + p += chunk; + nbytes -= chunk; + } +} + static int create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, unsigned long load_addr, unsigned long interp_load_addr) @@ -194,7 +213,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, /* * Generate 16 random bytes for userspace PRNG seeding. */ - get_random_bytes(k_rand_bytes, sizeof(k_rand_bytes)); + get_atrandom_bytes(k_rand_bytes, sizeof(k_rand_bytes)); u_rand_bytes = (elf_addr_t __user *) STACK_ALLOC(p, sizeof(k_rand_bytes)); if (__copy_to_user(u_rand_bytes, k_rand_bytes, sizeof(k_rand_bytes))) From 190171d47a2c98d847d542f1d568a40891c16752 Mon Sep 17 00:00:00 2001 From: Andrew Bartholomew Date: Wed, 9 Jan 2013 19:31:56 -0500 Subject: [PATCH 2526/2556] input: move add_input_randomness - patch from Google Code issue 42265 comment 162 --- drivers/input/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/input.c b/drivers/input/input.c index 11905b6a30237..06e90a3618a10 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -352,8 +352,8 @@ void input_event(struct input_dev *dev, if (is_event_supported(type, dev->evbit, EV_MAX)) { spin_lock_irqsave(&dev->event_lock, flags); - add_input_randomness(type, code, value); input_handle_event(dev, type, code, value); + add_input_randomness(type, code, value); spin_unlock_irqrestore(&dev->event_lock, flags); } } From 7d77a22b8ebb23019fd69a0f06bd6da24f5a5625 Mon Sep 17 00:00:00 2001 From: Zach Date: Thu, 10 Apr 2014 17:34:50 -0400 Subject: [PATCH 2527/2556] defconfig: Enable Audit Just stops some log spam --- arch/arm/configs/incrediblec-incredikernel_defconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/configs/incrediblec-incredikernel_defconfig b/arch/arm/configs/incrediblec-incredikernel_defconfig index 3b7151c34cc24..656bdbe4ec8e2 100644 --- a/arch/arm/configs/incrediblec-incredikernel_defconfig +++ b/arch/arm/configs/incrediblec-incredikernel_defconfig @@ -48,7 +48,8 @@ CONFIG_SWAP=y # CONFIG_POSIX_MQUEUE is not set # CONFIG_BSD_PROCESS_ACCT is not set # CONFIG_TASKSTATS is not set -# CONFIG_AUDIT is not set +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y CONFIG_HAVE_GENERIC_HARDIRQS=y # From 7a9a5a651d6f5e67368287023cfbd924ba00b0ce Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 23:23:26 -0500 Subject: [PATCH 2528/2556] Tweak lowmemorykiller --- drivers/staging/android/lowmemorykiller.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index b3d7620e08421..5a3b09d7f16ea 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -171,6 +171,12 @@ static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) p->pid, p->comm, oom_adj, tasksize); } if (selected) { + if (fatal_signal_pending(selected)) { + pr_warning("process %d is suffering a slow death\n", + selected->pid); + read_unlock(&tasklist_lock); + return rem; + } lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n", selected->pid, selected->comm, selected_oom_adj, selected_tasksize); From ba80a635cbaa1da0cf9800d2c6d1586fc54dcc34 Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 23:25:11 -0500 Subject: [PATCH 2529/2556] Fix memory leak --- fs/binfmt_elf_fdpic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 63039ed9576f7..2bc5dc644b4cb 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -1864,6 +1864,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) kfree(psinfo); kfree(notes); kfree(fpu); + kfree(shdr4extnum); #ifdef ELF_CORE_COPY_XFPREGS kfree(xfpu); #endif From ebaa675b7968d6d8f0212955f2cab03a442f94c8 Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 21:55:02 -0500 Subject: [PATCH 2530/2556] Add Add The Brain Fuck Scheduler v0.404 by Con Kolivas --- Documentation/scheduler/sched-BFS.txt | 347 + Documentation/sysctl/kernel.txt | 26 + arch/powerpc/platforms/cell/spufs/sched.c | 5 - drivers/cpufreq/cpufreq.c | 7 + fs/proc/base.c | 2 +- include/linux/init_task.h | 63 +- include/linux/ioprio.h | 2 + include/linux/jiffies.h | 2 +- include/linux/sched.h | 102 +- init/Kconfig | 17 +- init/main.c | 1 + kernel/delayacct.c | 2 +- kernel/exit.c | 2 +- kernel/kthread.c | 2 + kernel/posix-cpu-timers.c | 14 +- kernel/sched.c | 5 +- kernel/sched_bfs.c | 7294 +++++++++++++++++++++ kernel/sysctl.c | 31 +- lib/Kconfig.debug | 2 +- 19 files changed, 7898 insertions(+), 28 deletions(-) create mode 100644 Documentation/scheduler/sched-BFS.txt create mode 100644 kernel/sched_bfs.c diff --git a/Documentation/scheduler/sched-BFS.txt b/Documentation/scheduler/sched-BFS.txt new file mode 100644 index 0000000000000..c10d956018f99 --- /dev/null +++ b/Documentation/scheduler/sched-BFS.txt @@ -0,0 +1,347 @@ +BFS - The Brain Fuck Scheduler by Con Kolivas. + +Goals. + +The goal of the Brain Fuck Scheduler, referred to as BFS from here on, is to +completely do away with the complex designs of the past for the cpu process +scheduler and instead implement one that is very simple in basic design. +The main focus of BFS is to achieve excellent desktop interactivity and +responsiveness without heuristics and tuning knobs that are difficult to +understand, impossible to model and predict the effect of, and when tuned to +one workload cause massive detriment to another. + + +Design summary. + +BFS is best described as a single runqueue, O(n) lookup, earliest effective +virtual deadline first design, loosely based on EEVDF (earliest eligible virtual +deadline first) and my previous Staircase Deadline scheduler. Each component +shall be described in order to understand the significance of, and reasoning for +it. The codebase when the first stable version was released was approximately +9000 lines less code than the existing mainline linux kernel scheduler (in +2.6.31). This does not even take into account the removal of documentation and +the cgroups code that is not used. + +Design reasoning. + +The single runqueue refers to the queued but not running processes for the +entire system, regardless of the number of CPUs. The reason for going back to +a single runqueue design is that once multiple runqueues are introduced, +per-CPU or otherwise, there will be complex interactions as each runqueue will +be responsible for the scheduling latency and fairness of the tasks only on its +own runqueue, and to achieve fairness and low latency across multiple CPUs, any +advantage in throughput of having CPU local tasks causes other disadvantages. +This is due to requiring a very complex balancing system to at best achieve some +semblance of fairness across CPUs and can only maintain relatively low latency +for tasks bound to the same CPUs, not across them. To increase said fairness +and latency across CPUs, the advantage of local runqueue locking, which makes +for better scalability, is lost due to having to grab multiple locks. + +A significant feature of BFS is that all accounting is done purely based on CPU +used and nowhere is sleep time used in any way to determine entitlement or +interactivity. Interactivity "estimators" that use some kind of sleep/run +algorithm are doomed to fail to detect all interactive tasks, and to falsely tag +tasks that aren't interactive as being so. The reason for this is that it is +close to impossible to determine that when a task is sleeping, whether it is +doing it voluntarily, as in a userspace application waiting for input in the +form of a mouse click or otherwise, or involuntarily, because it is waiting for +another thread, process, I/O, kernel activity or whatever. Thus, such an +estimator will introduce corner cases, and more heuristics will be required to +cope with those corner cases, introducing more corner cases and failed +interactivity detection and so on. Interactivity in BFS is built into the design +by virtue of the fact that tasks that are waking up have not used up their quota +of CPU time, and have earlier effective deadlines, thereby making it very likely +they will preempt any CPU bound task of equivalent nice level. See below for +more information on the virtual deadline mechanism. Even if they do not preempt +a running task, because the rr interval is guaranteed to have a bound upper +limit on how long a task will wait for, it will be scheduled within a timeframe +that will not cause visible interface jitter. + + +Design details. + +Task insertion. + +BFS inserts tasks into each relevant queue as an O(1) insertion into a double +linked list. On insertion, *every* running queue is checked to see if the newly +queued task can run on any idle queue, or preempt the lowest running task on the +system. This is how the cross-CPU scheduling of BFS achieves significantly lower +latency per extra CPU the system has. In this case the lookup is, in the worst +case scenario, O(n) where n is the number of CPUs on the system. + +Data protection. + +BFS has one single lock protecting the process local data of every task in the +global queue. Thus every insertion, removal and modification of task data in the +global runqueue needs to grab the global lock. However, once a task is taken by +a CPU, the CPU has its own local data copy of the running process' accounting +information which only that CPU accesses and modifies (such as during a +timer tick) thus allowing the accounting data to be updated lockless. Once a +CPU has taken a task to run, it removes it from the global queue. Thus the +global queue only ever has, at most, + + (number of tasks requesting cpu time) - (number of logical CPUs) + 1 + +tasks in the global queue. This value is relevant for the time taken to look up +tasks during scheduling. This will increase if many tasks with CPU affinity set +in their policy to limit which CPUs they're allowed to run on if they outnumber +the number of CPUs. The +1 is because when rescheduling a task, the CPU's +currently running task is put back on the queue. Lookup will be described after +the virtual deadline mechanism is explained. + +Virtual deadline. + +The key to achieving low latency, scheduling fairness, and "nice level" +distribution in BFS is entirely in the virtual deadline mechanism. The one +tunable in BFS is the rr_interval, or "round robin interval". This is the +maximum time two SCHED_OTHER (or SCHED_NORMAL, the common scheduling policy) +tasks of the same nice level will be running for, or looking at it the other +way around, the longest duration two tasks of the same nice level will be +delayed for. When a task requests cpu time, it is given a quota (time_slice) +equal to the rr_interval and a virtual deadline. The virtual deadline is +offset from the current time in jiffies by this equation: + + jiffies + (prio_ratio * rr_interval) + +The prio_ratio is determined as a ratio compared to the baseline of nice -20 +and increases by 10% per nice level. The deadline is a virtual one only in that +no guarantee is placed that a task will actually be scheduled by this time, but +it is used to compare which task should go next. There are three components to +how a task is next chosen. First is time_slice expiration. If a task runs out +of its time_slice, it is descheduled, the time_slice is refilled, and the +deadline reset to that formula above. Second is sleep, where a task no longer +is requesting CPU for whatever reason. The time_slice and deadline are _not_ +adjusted in this case and are just carried over for when the task is next +scheduled. Third is preemption, and that is when a newly waking task is deemed +higher priority than a currently running task on any cpu by virtue of the fact +that it has an earlier virtual deadline than the currently running task. The +earlier deadline is the key to which task is next chosen for the first and +second cases. Once a task is descheduled, it is put back on the queue, and an +O(n) lookup of all queued-but-not-running tasks is done to determine which has +the earliest deadline and that task is chosen to receive CPU next. + +The CPU proportion of different nice tasks works out to be approximately the + + (prio_ratio difference)^2 + +The reason it is squared is that a task's deadline does not change while it is +running unless it runs out of time_slice. Thus, even if the time actually +passes the deadline of another task that is queued, it will not get CPU time +unless the current running task deschedules, and the time "base" (jiffies) is +constantly moving. + +Task lookup. + +BFS has 103 priority queues. 100 of these are dedicated to the static priority +of realtime tasks, and the remaining 3 are, in order of best to worst priority, +SCHED_ISO (isochronous), SCHED_NORMAL, and SCHED_IDLEPRIO (idle priority +scheduling). When a task of these priorities is queued, a bitmap of running +priorities is set showing which of these priorities has tasks waiting for CPU +time. When a CPU is made to reschedule, the lookup for the next task to get +CPU time is performed in the following way: + +First the bitmap is checked to see what static priority tasks are queued. If +any realtime priorities are found, the corresponding queue is checked and the +first task listed there is taken (provided CPU affinity is suitable) and lookup +is complete. If the priority corresponds to a SCHED_ISO task, they are also +taken in FIFO order (as they behave like SCHED_RR). If the priority corresponds +to either SCHED_NORMAL or SCHED_IDLEPRIO, then the lookup becomes O(n). At this +stage, every task in the runlist that corresponds to that priority is checked +to see which has the earliest set deadline, and (provided it has suitable CPU +affinity) it is taken off the runqueue and given the CPU. If a task has an +expired deadline, it is taken and the rest of the lookup aborted (as they are +chosen in FIFO order). + +Thus, the lookup is O(n) in the worst case only, where n is as described +earlier, as tasks may be chosen before the whole task list is looked over. + + +Scalability. + +The major limitations of BFS will be that of scalability, as the separate +runqueue designs will have less lock contention as the number of CPUs rises. +However they do not scale linearly even with separate runqueues as multiple +runqueues will need to be locked concurrently on such designs to be able to +achieve fair CPU balancing, to try and achieve some sort of nice-level fairness +across CPUs, and to achieve low enough latency for tasks on a busy CPU when +other CPUs would be more suited. BFS has the advantage that it requires no +balancing algorithm whatsoever, as balancing occurs by proxy simply because +all CPUs draw off the global runqueue, in priority and deadline order. Despite +the fact that scalability is _not_ the prime concern of BFS, it both shows very +good scalability to smaller numbers of CPUs and is likely a more scalable design +at these numbers of CPUs. + +It also has some very low overhead scalability features built into the design +when it has been deemed their overhead is so marginal that they're worth adding. +The first is the local copy of the running process' data to the CPU it's running +on to allow that data to be updated lockless where possible. Then there is +deference paid to the last CPU a task was running on, by trying that CPU first +when looking for an idle CPU to use the next time it's scheduled. Finally there +is the notion of "sticky" tasks that are flagged when they are involuntarily +descheduled, meaning they still want further CPU time. This sticky flag is +used to bias heavily against those tasks being scheduled on a different CPU +unless that CPU would be otherwise idle. When a cpu frequency governor is used +that scales with CPU load, such as ondemand, sticky tasks are not scheduled +on a different CPU at all, preferring instead to go idle. This means the CPU +they were bound to is more likely to increase its speed while the other CPU +will go idle, thus speeding up total task execution time and likely decreasing +power usage. This is the only scenario where BFS will allow a CPU to go idle +in preference to scheduling a task on the earliest available spare CPU. + +The real cost of migrating a task from one CPU to another is entirely dependant +on the cache footprint of the task, how cache intensive the task is, how long +it's been running on that CPU to take up the bulk of its cache, how big the CPU +cache is, how fast and how layered the CPU cache is, how fast a context switch +is... and so on. In other words, it's close to random in the real world where we +do more than just one sole workload. The only thing we can be sure of is that +it's not free. So BFS uses the principle that an idle CPU is a wasted CPU and +utilising idle CPUs is more important than cache locality, and cache locality +only plays a part after that. + +When choosing an idle CPU for a waking task, the cache locality is determined +according to where the task last ran and then idle CPUs are ranked from best +to worst to choose the most suitable idle CPU based on cache locality, NUMA +node locality and hyperthread sibling business. They are chosen in the +following preference (if idle): + +* Same core, idle or busy cache, idle threads +* Other core, same cache, idle or busy cache, idle threads. +* Same node, other CPU, idle cache, idle threads. +* Same node, other CPU, busy cache, idle threads. +* Same core, busy threads. +* Other core, same cache, busy threads. +* Same node, other CPU, busy threads. +* Other node, other CPU, idle cache, idle threads. +* Other node, other CPU, busy cache, idle threads. +* Other node, other CPU, busy threads. + +This shows the SMT or "hyperthread" awareness in the design as well which will +choose a real idle core first before a logical SMT sibling which already has +tasks on the physical CPU. + +Early benchmarking of BFS suggested scalability dropped off at the 16 CPU mark. +However this benchmarking was performed on an earlier design that was far less +scalable than the current one so it's hard to know how scalable it is in terms +of both CPUs (due to the global runqueue) and heavily loaded machines (due to +O(n) lookup) at this stage. Note that in terms of scalability, the number of +_logical_ CPUs matters, not the number of _physical_ CPUs. Thus, a dual (2x) +quad core (4X) hyperthreaded (2X) machine is effectively a 16X. Newer benchmark +results are very promising indeed, without needing to tweak any knobs, features +or options. Benchmark contributions are most welcome. + + +Features + +As the initial prime target audience for BFS was the average desktop user, it +was designed to not need tweaking, tuning or have features set to obtain benefit +from it. Thus the number of knobs and features has been kept to an absolute +minimum and should not require extra user input for the vast majority of cases. +There are precisely 2 tunables, and 2 extra scheduling policies. The rr_interval +and iso_cpu tunables, and the SCHED_ISO and SCHED_IDLEPRIO policies. In addition +to this, BFS also uses sub-tick accounting. What BFS does _not_ now feature is +support for CGROUPS. The average user should neither need to know what these +are, nor should they need to be using them to have good desktop behaviour. + +rr_interval + +There is only one "scheduler" tunable, the round robin interval. This can be +accessed in + + /proc/sys/kernel/rr_interval + +The value is in milliseconds, and the default value is set to 6ms. Valid values +are from 1 to 1000. Decreasing the value will decrease latencies at the cost of +decreasing throughput, while increasing it will improve throughput, but at the +cost of worsening latencies. The accuracy of the rr interval is limited by HZ +resolution of the kernel configuration. Thus, the worst case latencies are +usually slightly higher than this actual value. BFS uses "dithering" to try and +minimise the effect the Hz limitation has. The default value of 6 is not an +arbitrary one. It is based on the fact that humans can detect jitter at +approximately 7ms, so aiming for much lower latencies is pointless under most +circumstances. It is worth noting this fact when comparing the latency +performance of BFS to other schedulers. Worst case latencies being higher than +7ms are far worse than average latencies not being in the microsecond range. +Experimentation has shown that rr intervals being increased up to 300 can +improve throughput but beyond that, scheduling noise from elsewhere prevents +further demonstrable throughput. + +Isochronous scheduling. + +Isochronous scheduling is a unique scheduling policy designed to provide +near-real-time performance to unprivileged (ie non-root) users without the +ability to starve the machine indefinitely. Isochronous tasks (which means +"same time") are set using, for example, the schedtool application like so: + + schedtool -I -e amarok + +This will start the audio application "amarok" as SCHED_ISO. How SCHED_ISO works +is that it has a priority level between true realtime tasks and SCHED_NORMAL +which would allow them to preempt all normal tasks, in a SCHED_RR fashion (ie, +if multiple SCHED_ISO tasks are running, they purely round robin at rr_interval +rate). However if ISO tasks run for more than a tunable finite amount of time, +they are then demoted back to SCHED_NORMAL scheduling. This finite amount of +time is the percentage of _total CPU_ available across the machine, configurable +as a percentage in the following "resource handling" tunable (as opposed to a +scheduler tunable): + + /proc/sys/kernel/iso_cpu + +and is set to 70% by default. It is calculated over a rolling 5 second average +Because it is the total CPU available, it means that on a multi CPU machine, it +is possible to have an ISO task running as realtime scheduling indefinitely on +just one CPU, as the other CPUs will be available. Setting this to 100 is the +equivalent of giving all users SCHED_RR access and setting it to 0 removes the +ability to run any pseudo-realtime tasks. + +A feature of BFS is that it detects when an application tries to obtain a +realtime policy (SCHED_RR or SCHED_FIFO) and the caller does not have the +appropriate privileges to use those policies. When it detects this, it will +give the task SCHED_ISO policy instead. Thus it is transparent to the user. +Because some applications constantly set their policy as well as their nice +level, there is potential for them to undo the override specified by the user +on the command line of setting the policy to SCHED_ISO. To counter this, once +a task has been set to SCHED_ISO policy, it needs superuser privileges to set +it back to SCHED_NORMAL. This will ensure the task remains ISO and all child +processes and threads will also inherit the ISO policy. + +Idleprio scheduling. + +Idleprio scheduling is a scheduling policy designed to give out CPU to a task +_only_ when the CPU would be otherwise idle. The idea behind this is to allow +ultra low priority tasks to be run in the background that have virtually no +effect on the foreground tasks. This is ideally suited to distributed computing +clients (like setiathome, folding, mprime etc) but can also be used to start +a video encode or so on without any slowdown of other tasks. To avoid this +policy from grabbing shared resources and holding them indefinitely, if it +detects a state where the task is waiting on I/O, the machine is about to +suspend to ram and so on, it will transiently schedule them as SCHED_NORMAL. As +per the Isochronous task management, once a task has been scheduled as IDLEPRIO, +it cannot be put back to SCHED_NORMAL without superuser privileges. Tasks can +be set to start as SCHED_IDLEPRIO with the schedtool command like so: + + schedtool -D -e ./mprime + +Subtick accounting. + +It is surprisingly difficult to get accurate CPU accounting, and in many cases, +the accounting is done by simply determining what is happening at the precise +moment a timer tick fires off. This becomes increasingly inaccurate as the +timer tick frequency (HZ) is lowered. It is possible to create an application +which uses almost 100% CPU, yet by being descheduled at the right time, records +zero CPU usage. While the main problem with this is that there are possible +security implications, it is also difficult to determine how much CPU a task +really does use. BFS tries to use the sub-tick accounting from the TSC clock, +where possible, to determine real CPU usage. This is not entirely reliable, but +is far more likely to produce accurate CPU usage data than the existing designs +and will not show tasks as consuming no CPU usage when they actually are. Thus, +the amount of CPU reported as being used by BFS will more accurately represent +how much CPU the task itself is using (as is shown for example by the 'time' +application), so the reported values may be quite different to other schedulers. +Values reported as the 'load' are more prone to problems with this design, but +per process values are closer to real usage. When comparing throughput of BFS +to other designs, it is important to compare the actual completed work in terms +of total wall clock time taken and total work done, rather than the reported +"cpu usage". + + +Con Kolivas Tue, 5 Apr 2011 diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index c0561067b4bbf..f43587894b5fa 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -33,6 +33,7 @@ show up in /proc/sys/kernel: - domainname - hostname - hotplug +- iso_cpu - java-appletviewer [ binfmt_java, obsolete ] - java-interpreter [ binfmt_java, obsolete ] - kptr_restrict @@ -56,6 +57,7 @@ show up in /proc/sys/kernel: - randomize_va_space - real-root-dev ==> Documentation/initrd.txt - reboot-cmd [ SPARC only ] +- rr_interval - rtsig-max - rtsig-nr - sem @@ -269,6 +271,16 @@ Default value is "/sbin/hotplug". ============================================================== +iso_cpu: (BFS CPU scheduler only). + +This sets the percentage cpu that the unprivileged SCHED_ISO tasks can +run effectively at realtime priority, averaged over a rolling five +seconds over the -whole- system, meaning all cpus. + +Set to 70 (percent) by default. + +============================================================== + l2cr: (PPC only) This flag controls the L2 cache of G3 processor boards. If @@ -456,6 +468,20 @@ rebooting. ??? ============================================================== +rr_interval: (BFS CPU scheduler only) + +This is the smallest duration that any cpu process scheduling unit +will run for. Increasing this value can increase throughput of cpu +bound tasks substantially but at the expense of increased latencies +overall. Conversely decreasing it will decrease average and maximum +latencies but at the expense of throughput. This value is in +milliseconds and the default value chosen depends on the number of +cpus available at scheduler initialisation with a minimum of 6. + +Valid values are from 1-1000. + +============================================================== + rtsig-max & rtsig-nr: The file rtsig-max can be used to tune the maximum number diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index 0b04662849320..f2a024f37c690 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c @@ -63,11 +63,6 @@ static struct task_struct *spusched_task; static struct timer_list spusched_timer; static struct timer_list spuloadavg_timer; -/* - * Priority of a normal, non-rt, non-niced'd process (aka nice level 0). - */ -#define NORMAL_PRIO 120 - /* * Frequency of the spu scheduler tick. By default we do one SPU scheduler * tick for every 10 CPU scheduler ticks. diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index ee828abaf7a6d..09fb29a7911e3 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -1497,6 +1498,12 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, target_freq, relation); if (cpu_online(policy->cpu) && cpufreq_driver->target) retval = cpufreq_driver->target(policy, target_freq, relation); + if (likely(retval != -EINVAL)) { + if (target_freq == policy->max) + cpu_nonscaling(policy->cpu); + else + cpu_scaling(policy->cpu); + } return retval; } diff --git a/fs/proc/base.c b/fs/proc/base.c index bbf7576e57706..ca581edcb3b5a 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -363,7 +363,7 @@ static int proc_pid_stack(struct seq_file *m, struct pid_namespace *ns, static int proc_pid_schedstat(struct task_struct *task, char *buffer) { return sprintf(buffer, "%llu %llu %lu\n", - (unsigned long long)task->se.sum_exec_runtime, + (unsigned long long)tsk_seruntime(task), (unsigned long long)task->sched_info.run_delay, task->sched_info.pcount); } diff --git a/include/linux/init_task.h b/include/linux/init_task.h index caa151fbebb74..9ff59ddf0ec0b 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -128,6 +128,67 @@ extern struct cred init_cred; * INIT_TASK is used to set up the first task table, touch at * your own risk!. Base=0, limit=0x1fffff (=2MB) */ +#ifdef CONFIG_SCHED_BFS +#define INIT_TASK(tsk) \ +{ \ + .state = 0, \ + .stack = &init_thread_info, \ + .usage = ATOMIC_INIT(2), \ + .flags = PF_KTHREAD, \ + .lock_depth = -1, \ + .prio = NORMAL_PRIO, \ + .static_prio = MAX_PRIO-20, \ + .normal_prio = NORMAL_PRIO, \ + .deadline = 0, \ + .policy = SCHED_NORMAL, \ + .cpus_allowed = CPU_MASK_ALL, \ + .mm = NULL, \ + .active_mm = &init_mm, \ + .run_list = LIST_HEAD_INIT(tsk.run_list), \ + .time_slice = HZ, \ + .tasks = LIST_HEAD_INIT(tsk.tasks), \ + INIT_PUSHABLE_TASKS(tsk) \ + .ptraced = LIST_HEAD_INIT(tsk.ptraced), \ + .ptrace_entry = LIST_HEAD_INIT(tsk.ptrace_entry), \ + .real_parent = &tsk, \ + .parent = &tsk, \ + .children = LIST_HEAD_INIT(tsk.children), \ + .sibling = LIST_HEAD_INIT(tsk.sibling), \ + .group_leader = &tsk, \ + RCU_INIT_POINTER(.real_cred, &init_cred), \ + RCU_INIT_POINTER(.cred, &init_cred), \ + .comm = "swapper", \ + .thread = INIT_THREAD, \ + .fs = &init_fs, \ + .files = &init_files, \ + .signal = &init_signals, \ + .sighand = &init_sighand, \ + .nsproxy = &init_nsproxy, \ + .pending = { \ + .list = LIST_HEAD_INIT(tsk.pending.list), \ + .signal = {{0}}}, \ + .blocked = {{0}}, \ + .alloc_lock = __SPIN_LOCK_UNLOCKED(tsk.alloc_lock), \ + .journal_info = NULL, \ + .cpu_timers = INIT_CPU_TIMERS(tsk.cpu_timers), \ + .fs_excl = ATOMIC_INIT(0), \ + .pi_lock = __RAW_SPIN_LOCK_UNLOCKED(tsk.pi_lock), \ + .timer_slack_ns = 50000, /* 50 usec default slack */ \ + .pids = { \ + [PIDTYPE_PID] = INIT_PID_LINK(PIDTYPE_PID), \ + [PIDTYPE_PGID] = INIT_PID_LINK(PIDTYPE_PGID), \ + [PIDTYPE_SID] = INIT_PID_LINK(PIDTYPE_SID), \ + }, \ + .dirties = INIT_PROP_LOCAL_SINGLE(dirties), \ + INIT_IDS \ + INIT_PERF_EVENTS(tsk) \ + INIT_TRACE_IRQFLAGS \ + INIT_LOCKDEP \ + INIT_FTRACE_GRAPH \ + INIT_TRACE_RECURSION \ + INIT_TASK_RCU_PREEMPT(tsk) \ +} +#else /* CONFIG_SCHED_BFS */ #define INIT_TASK(tsk) \ { \ .state = 0, \ @@ -193,7 +254,7 @@ extern struct cred init_cred; INIT_TRACE_RECURSION \ INIT_TASK_RCU_PREEMPT(tsk) \ } - +#endif /* CONFIG_SCHED_BFS */ #define INIT_CPU_TIMERS(cpu_timers) \ { \ diff --git a/include/linux/ioprio.h b/include/linux/ioprio.h index 76dad48088474..72324720a973a 100644 --- a/include/linux/ioprio.h +++ b/include/linux/ioprio.h @@ -64,6 +64,8 @@ static inline int task_ioprio_class(struct io_context *ioc) static inline int task_nice_ioprio(struct task_struct *task) { + if (iso_task(task)) + return 0; return (task_nice(task) + 20) / 5; } diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h index 6811f4bfc6e7a..cf4e278945bc9 100644 --- a/include/linux/jiffies.h +++ b/include/linux/jiffies.h @@ -164,7 +164,7 @@ static inline u64 get_jiffies_64(void) * Have the 32 bit jiffies value wrap 5 minutes after boot * so jiffies wrap bugs show up earlier. */ -#define INITIAL_JIFFIES ((unsigned long)(unsigned int) (-300*HZ)) +#define INITIAL_JIFFIES ((unsigned long)(unsigned int) (-10*HZ)) /* * Change timeval to jiffies, trying to avoid the diff --git a/include/linux/sched.h b/include/linux/sched.h index d9b55bb28d8ef..aa02252c5ca2a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -37,8 +37,15 @@ #define SCHED_FIFO 1 #define SCHED_RR 2 #define SCHED_BATCH 3 -/* SCHED_ISO: reserved but not implemented yet */ +/* SCHED_ISO: Implemented on BFS only */ #define SCHED_IDLE 5 +#ifdef CONFIG_SCHED_BFS +#define SCHED_ISO 4 +#define SCHED_IDLEPRIO SCHED_IDLE +#define SCHED_MAX (SCHED_IDLEPRIO) +#define SCHED_RANGE(policy) ((policy) <= SCHED_MAX) +#endif + /* Can be ORed in to make sure the process is reverted back to SCHED_NORMAL on fork */ #define SCHED_RESET_ON_FORK 0x40000000 @@ -267,8 +274,6 @@ extern asmlinkage void schedule_tail(struct task_struct *prev); extern void init_idle(struct task_struct *idle, int cpu); extern void init_idle_bootup_task(struct task_struct *idle); -extern int runqueue_is_locked(int cpu); - extern cpumask_var_t nohz_cpu_mask; #if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ) extern void select_nohz_load_balancer(int stop_tick); @@ -1199,17 +1204,33 @@ struct task_struct { int lock_depth; /* BKL lock depth */ +#ifndef CONFIG_SCHED_BFS #ifdef CONFIG_SMP #ifdef __ARCH_WANT_UNLOCKED_CTXSW int oncpu; #endif +#endif +#else /* CONFIG_SCHED_BFS */ + int oncpu; #endif int prio, static_prio, normal_prio; unsigned int rt_priority; +#ifdef CONFIG_SCHED_BFS + int time_slice; + u64 deadline; + struct list_head run_list; + u64 last_ran; + u64 sched_time; /* sched_clock time spent running */ +#ifdef CONFIG_SMP + int sticky; /* Soft affined flag */ +#endif + unsigned long rt_timeout; +#else /* CONFIG_SCHED_BFS */ const struct sched_class *sched_class; struct sched_entity se; struct sched_rt_entity rt; +#endif #ifdef CONFIG_PREEMPT_NOTIFIERS /* list of struct preempt_notifier: */ @@ -1314,6 +1335,9 @@ struct task_struct { int __user *clear_child_tid; /* CLONE_CHILD_CLEARTID */ cputime_t utime, stime, utimescaled, stimescaled; +#ifdef CONFIG_SCHED_BFS + unsigned long utime_pc, stime_pc; +#endif cputime_t gtime; #ifndef CONFIG_VIRT_CPU_ACCOUNTING cputime_t prev_utime, prev_stime; @@ -1536,6 +1560,62 @@ struct task_struct { #endif }; +#ifdef CONFIG_SCHED_BFS +extern int grunqueue_is_locked(void); +extern void grq_unlock_wait(void); +extern void cpu_scaling(int cpu); +extern void cpu_nonscaling(int cpu); +#define tsk_seruntime(t) ((t)->sched_time) +#define tsk_rttimeout(t) ((t)->rt_timeout) + +static inline void tsk_cpus_current(struct task_struct *p) +{ +} + +#define runqueue_is_locked(cpu) grunqueue_is_locked() + +static inline void print_scheduler_version(void) +{ + printk(KERN_INFO"BFS CPU scheduler v0.404 by Con Kolivas.\n"); +} + +static inline int iso_task(struct task_struct *p) +{ + return (p->policy == SCHED_ISO); +} +extern void remove_cpu(unsigned long cpu); +#else /* CFS */ +extern int runqueue_is_locked(int cpu); +static inline void cpu_scaling(int cpu) +{ +} + +static inline void cpu_nonscaling(int cpu) +{ +} +#define tsk_seruntime(t) ((t)->se.sum_exec_runtime) +#define tsk_rttimeout(t) ((t)->rt.timeout) + +static inline void tsk_cpus_current(struct task_struct *p) +{ + p->rt.nr_cpus_allowed = current->rt.nr_cpus_allowed; +} + +static inline void print_scheduler_version(void) +{ + printk(KERN_INFO"CFS CPU scheduler.\n"); +} + +static inline int iso_task(struct task_struct *p) +{ + return 0; +} + +static inline void remove_cpu(unsigned long cpu) +{ +} +#endif /* CONFIG_SCHED_BFS */ + /* Future-safe accessor for struct task_struct's cpus_allowed. */ #define tsk_cpus_allowed(tsk) (&(tsk)->cpus_allowed) @@ -1553,10 +1633,20 @@ struct task_struct { */ #define MAX_USER_RT_PRIO 100 -#define MAX_RT_PRIO MAX_USER_RT_PRIO +#define MAX_RT_PRIO (MAX_USER_RT_PRIO + 1) +#define DEFAULT_PRIO (MAX_RT_PRIO + 20) +#ifdef CONFIG_SCHED_BFS +#define PRIO_RANGE (40) +#define MAX_PRIO (MAX_RT_PRIO + PRIO_RANGE) +#define ISO_PRIO (MAX_RT_PRIO) +#define NORMAL_PRIO (MAX_RT_PRIO + 1) +#define IDLE_PRIO (MAX_RT_PRIO + 2) +#define PRIO_LIMIT ((IDLE_PRIO) + 1) +#else /* CONFIG_SCHED_BFS */ #define MAX_PRIO (MAX_RT_PRIO + 40) -#define DEFAULT_PRIO (MAX_RT_PRIO + 20) +#define NORMAL_PRIO DEFAULT_PRIO +#endif /* CONFIG_SCHED_BFS */ static inline int rt_prio(int prio) { @@ -1890,7 +1980,7 @@ task_sched_runtime(struct task_struct *task); extern unsigned long long thread_group_sched_runtime(struct task_struct *task); /* sched_exec is called by processes performing an exec */ -#ifdef CONFIG_SMP +#if defined(CONFIG_SMP) && !defined(CONFIG_SCHED_BFS) extern void sched_exec(void); #else #define sched_exec() {} diff --git a/init/Kconfig b/init/Kconfig index bd4d421a3aa48..c1cbe97c33c66 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -30,6 +30,19 @@ config IRQ_WORK menu "General setup" +config SCHED_BFS + bool "BFS cpu scheduler" + ---help--- + The Brain Fuck CPU Scheduler for excellent interactivity and + responsiveness on the desktop and solid scalability on normal + hardware. Not recommended for 4096 CPUs. + + Currently incompatible with the Group CPU scheduler, and RCU TORTURE + TEST so these options are disabled. + + Say Y here. + default y + config EXPERIMENTAL bool "Prompt for development and/or incomplete code/drivers" ---help--- @@ -619,6 +632,7 @@ config PROC_PID_CPUSET config CGROUP_CPUACCT bool "Simple CPU accounting cgroup subsystem" + depends on !SCHED_BFS help Provides a simple Resource Controller for monitoring the total CPU consumed by the tasks in a cgroup. @@ -685,7 +699,7 @@ config CGROUP_MEM_RES_CTLR_SWAP_ENABLED menuconfig CGROUP_SCHED bool "Group CPU scheduler" - depends on EXPERIMENTAL + depends on EXPERIMENTAL && !SCHED_BFS default n help This feature lets CPU scheduler recognize task groups and control CPU @@ -799,6 +813,7 @@ endif # NAMESPACES config SCHED_AUTOGROUP bool "Automatic process group scheduling" + depends on !SCHED_BFS select EVENTFD select CGROUPS select CGROUP_SCHED diff --git a/init/main.c b/init/main.c index a7fcde2913776..37a275cf99f15 100644 --- a/init/main.c +++ b/init/main.c @@ -832,6 +832,7 @@ static noinline int init_post(void) system_state = SYSTEM_RUNNING; numa_default_policy(); + print_scheduler_version(); current->signal->flags |= SIGNAL_UNKILLABLE; diff --git a/kernel/delayacct.c b/kernel/delayacct.c index ead9b610aa71a..44cc3d77e6498 100644 --- a/kernel/delayacct.c +++ b/kernel/delayacct.c @@ -128,7 +128,7 @@ int __delayacct_add_tsk(struct taskstats *d, struct task_struct *tsk) */ t1 = tsk->sched_info.pcount; t2 = tsk->sched_info.run_delay; - t3 = tsk->se.sum_exec_runtime; + t3 = tsk_seruntime(tsk); d->cpu_count += t1; diff --git a/kernel/exit.c b/kernel/exit.c index 557a348f68286..9a3c75f226a4a 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -132,7 +132,7 @@ static void __exit_signal(struct task_struct *tsk) sig->inblock += task_io_get_inblock(tsk); sig->oublock += task_io_get_oublock(tsk); task_io_accounting_add(&sig->ioac, &tsk->ioac); - sig->sum_sched_runtime += tsk->se.sum_exec_runtime; + sig->sum_sched_runtime += tsk_seruntime(tsk); } sig->nr_threads--; diff --git a/kernel/kthread.c b/kernel/kthread.c index c55afba990a38..426f7b896af4f 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -184,7 +184,9 @@ void kthread_bind(struct task_struct *p, unsigned int cpu) } p->cpus_allowed = cpumask_of_cpu(cpu); +#ifndef CONFIG_SCHED_BFS p->rt.nr_cpus_allowed = 1; +#endif p->flags |= PF_THREAD_BOUND; } EXPORT_SYMBOL(kthread_bind); diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c index 05bb7173850e0..a61830b1af48c 100644 --- a/kernel/posix-cpu-timers.c +++ b/kernel/posix-cpu-timers.c @@ -248,7 +248,7 @@ void thread_group_cputime(struct task_struct *tsk, struct task_cputime *times) do { times->utime = cputime_add(times->utime, t->utime); times->stime = cputime_add(times->stime, t->stime); - times->sum_exec_runtime += t->se.sum_exec_runtime; + times->sum_exec_runtime += tsk_seruntime(t); } while_each_thread(tsk, t); out: rcu_read_unlock(); @@ -508,7 +508,7 @@ static void cleanup_timers(struct list_head *head, void posix_cpu_timers_exit(struct task_struct *tsk) { cleanup_timers(tsk->cpu_timers, - tsk->utime, tsk->stime, tsk->se.sum_exec_runtime); + tsk->utime, tsk->stime, tsk_seruntime(tsk)); } void posix_cpu_timers_exit_group(struct task_struct *tsk) @@ -518,7 +518,7 @@ void posix_cpu_timers_exit_group(struct task_struct *tsk) cleanup_timers(tsk->signal->cpu_timers, cputime_add(tsk->utime, sig->utime), cputime_add(tsk->stime, sig->stime), - tsk->se.sum_exec_runtime + sig->sum_sched_runtime); + tsk_seruntime(tsk) + sig->sum_sched_runtime); } static void clear_dead_task(struct k_itimer *timer, union cpu_time_count now) @@ -949,7 +949,7 @@ static void check_thread_timers(struct task_struct *tsk, struct cpu_timer_list *t = list_first_entry(timers, struct cpu_timer_list, entry); - if (!--maxfire || tsk->se.sum_exec_runtime < t->expires.sched) { + if (!--maxfire || tsk_seruntime(tsk) < t->expires.sched) { tsk->cputime_expires.sched_exp = t->expires.sched; break; } @@ -966,7 +966,7 @@ static void check_thread_timers(struct task_struct *tsk, ACCESS_ONCE(sig->rlim[RLIMIT_RTTIME].rlim_max); if (hard != RLIM_INFINITY && - tsk->rt.timeout > DIV_ROUND_UP(hard, USEC_PER_SEC/HZ)) { + tsk_rttimeout(tsk) > DIV_ROUND_UP(hard, USEC_PER_SEC/HZ)) { /* * At the hard limit, we just die. * No need to calculate anything else now. @@ -974,7 +974,7 @@ static void check_thread_timers(struct task_struct *tsk, __group_send_sig_info(SIGKILL, SEND_SIG_PRIV, tsk); return; } - if (tsk->rt.timeout > DIV_ROUND_UP(soft, USEC_PER_SEC/HZ)) { + if (tsk_rttimeout(tsk) > DIV_ROUND_UP(soft, USEC_PER_SEC/HZ)) { /* * At the soft limit, send a SIGXCPU every second. */ @@ -1276,7 +1276,7 @@ static inline int fastpath_timer_check(struct task_struct *tsk) struct task_cputime task_sample = { .utime = tsk->utime, .stime = tsk->stime, - .sum_exec_runtime = tsk->se.sum_exec_runtime + .sum_exec_runtime = tsk_seruntime(tsk) }; if (task_cputime_expired(&task_sample, &tsk->cputime_expires)) diff --git a/kernel/sched.c b/kernel/sched.c index 3a2d949118185..71691e5b81bdc 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -1,3 +1,6 @@ +#ifdef CONFIG_SCHED_BFS +#include "sched_bfs.c" +#else /* * kernel/sched.c * @@ -9360,4 +9363,4 @@ struct cgroup_subsys cpuacct_subsys = { .subsys_id = cpuacct_subsys_id, }; #endif /* CONFIG_CGROUP_CPUACCT */ - +#endif /* CONFIG_SCHED_BFS */ diff --git a/kernel/sched_bfs.c b/kernel/sched_bfs.c new file mode 100644 index 0000000000000..f3cb030a219d8 --- /dev/null +++ b/kernel/sched_bfs.c @@ -0,0 +1,7294 @@ +/* + * kernel/sched_bfs.c, was sched.c + * + * Kernel scheduler and related syscalls + * + * Copyright (C) 1991-2002 Linus Torvalds + * + * 1996-12-23 Modified by Dave Grothe to fix bugs in semaphores and + * make semaphores SMP safe + * 1998-11-19 Implemented schedule_timeout() and related stuff + * by Andrea Arcangeli + * 2002-01-04 New ultra-scalable O(1) scheduler by Ingo Molnar: + * hybrid priority-list and round-robin design with + * an array-switch method of distributing timeslices + * and per-CPU runqueues. Cleanups and useful suggestions + * by Davide Libenzi, preemptible kernel bits by Robert Love. + * 2003-09-03 Interactivity tuning by Con Kolivas. + * 2004-04-02 Scheduler domains code by Nick Piggin + * 2007-04-15 Work begun on replacing all interactivity tuning with a + * fair scheduling design by Con Kolivas. + * 2007-05-05 Load balancing (smp-nice) and other improvements + * by Peter Williams + * 2007-05-06 Interactivity improvements to CFS by Mike Galbraith + * 2007-07-01 Group scheduling enhancements by Srivatsa Vaddagiri + * 2007-11-29 RT balancing improvements by Steven Rostedt, Gregory Haskins, + * Thomas Gleixner, Mike Kravetz + * now Brainfuck deadline scheduling policy by Con Kolivas deletes + * a whole lot of those previous things. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sched_cpupri.h" +#include "workqueue_sched.h" + +#define CREATE_TRACE_POINTS +#include + +#define rt_prio(prio) unlikely((prio) < MAX_RT_PRIO) +#define rt_task(p) rt_prio((p)->prio) +#define rt_queue(rq) rt_prio((rq)->rq_prio) +#define batch_task(p) (unlikely((p)->policy == SCHED_BATCH)) +#define is_rt_policy(policy) ((policy) == SCHED_FIFO || \ + (policy) == SCHED_RR) +#define has_rt_policy(p) unlikely(is_rt_policy((p)->policy)) +#define idleprio_task(p) unlikely((p)->policy == SCHED_IDLEPRIO) +#define iso_task(p) unlikely((p)->policy == SCHED_ISO) +#define iso_queue(rq) unlikely((rq)->rq_policy == SCHED_ISO) +#define ISO_PERIOD ((5 * HZ * grq.noc) + 1) + +/* + * Convert user-nice values [ -20 ... 0 ... 19 ] + * to static priority [ MAX_RT_PRIO..MAX_PRIO-1 ], + * and back. + */ +#define NICE_TO_PRIO(nice) (MAX_RT_PRIO + (nice) + 20) +#define PRIO_TO_NICE(prio) ((prio) - MAX_RT_PRIO - 20) +#define TASK_NICE(p) PRIO_TO_NICE((p)->static_prio) + +/* + * 'User priority' is the nice value converted to something we + * can work with better when scaling various scheduler parameters, + * it's a [ 0 ... 39 ] range. + */ +#define USER_PRIO(p) ((p) - MAX_RT_PRIO) +#define TASK_USER_PRIO(p) USER_PRIO((p)->static_prio) +#define MAX_USER_PRIO (USER_PRIO(MAX_PRIO)) +#define SCHED_PRIO(p) ((p) + MAX_RT_PRIO) +#define STOP_PRIO (MAX_RT_PRIO - 1) + +/* + * Some helpers for converting to/from various scales. Use shifts to get + * approximate multiples of ten for less overhead. + */ +#define JIFFIES_TO_NS(TIME) ((TIME) * (1000000000 / HZ)) +#define JIFFY_NS (1000000000 / HZ) +#define HALF_JIFFY_NS (1000000000 / HZ / 2) +#define HALF_JIFFY_US (1000000 / HZ / 2) +#define MS_TO_NS(TIME) ((TIME) << 20) +#define MS_TO_US(TIME) ((TIME) << 10) +#define NS_TO_MS(TIME) ((TIME) >> 20) +#define NS_TO_US(TIME) ((TIME) >> 10) + +#define RESCHED_US (100) /* Reschedule if less than this many μs left */ + +/* + * This is the time all tasks within the same priority round robin. + * Value is in ms and set to a minimum of 6ms. Scales with number of cpus. + * Tunable via /proc interface. + */ +int rr_interval __read_mostly = 6; + +/* + * sched_iso_cpu - sysctl which determines the cpu percentage SCHED_ISO tasks + * are allowed to run five seconds as real time tasks. This is the total over + * all online cpus. + */ +int sched_iso_cpu __read_mostly = 70; + +/* + * The relative length of deadline for each priority(nice) level. + */ +static int prio_ratios[PRIO_RANGE] __read_mostly; + +/* + * The quota handed out to tasks of all priority levels when refilling their + * time_slice. + */ +static inline int timeslice(void) +{ + return MS_TO_US(rr_interval); +} + +/* + * The global runqueue data that all CPUs work off. Data is protected either + * by the global grq lock, or the discrete lock that precedes the data in this + * struct. + */ +struct global_rq { + raw_spinlock_t lock; + unsigned long nr_running; + unsigned long nr_uninterruptible; + unsigned long long nr_switches; + struct list_head queue[PRIO_LIMIT]; + DECLARE_BITMAP(prio_bitmap, PRIO_LIMIT + 1); +#ifdef CONFIG_SMP + unsigned long qnr; /* queued not running */ + cpumask_t cpu_idle_map; + int idle_cpus; +#endif + int noc; /* num_online_cpus stored and updated when it changes */ + u64 niffies; /* Nanosecond jiffies */ + unsigned long last_jiffy; /* Last jiffy we updated niffies */ + + raw_spinlock_t iso_lock; + int iso_ticks; + int iso_refractory; +}; + +/* There can be only one */ +static struct global_rq grq; + +/* + * This is the main, per-CPU runqueue data structure. + * This data should only be modified by the local cpu. + */ +struct rq { +#ifdef CONFIG_SMP +#ifdef CONFIG_NO_HZ + u64 nohz_stamp; + unsigned char in_nohz_recently; +#endif +#endif + + struct task_struct *curr, *idle, *stop; + struct mm_struct *prev_mm; + + /* Stored data about rq->curr to work outside grq lock */ + u64 rq_deadline; + unsigned int rq_policy; + int rq_time_slice; + u64 rq_last_ran; + int rq_prio; + int rq_running; /* There is a task running */ + + /* Accurate timekeeping data */ + u64 timekeep_clock; + unsigned long user_pc, nice_pc, irq_pc, softirq_pc, system_pc, + iowait_pc, idle_pc; + long account_pc; + atomic_t nr_iowait; + +#ifdef CONFIG_SMP + int cpu; /* cpu of this runqueue */ + int online; + int scaling; /* This CPU is managed by a scaling CPU freq governor */ + struct task_struct *sticky_task; + + struct root_domain *rd; + struct sched_domain *sd; + unsigned long *cpu_locality; /* CPU relative cache distance */ +#ifdef CONFIG_SCHED_SMT + int (*siblings_idle)(unsigned long cpu); + /* See if all smt siblings are idle */ + cpumask_t smt_siblings; +#endif +#ifdef CONFIG_SCHED_MC + int (*cache_idle)(unsigned long cpu); + /* See if all cache siblings are idle */ + cpumask_t cache_siblings; +#endif + u64 last_niffy; /* Last time this RQ updated grq.niffies */ +#endif +#ifdef CONFIG_IRQ_TIME_ACCOUNTING + u64 prev_irq_time; +#endif + u64 clock, old_clock, last_tick; + u64 clock_task; + int dither; + +#ifdef CONFIG_SCHEDSTATS + + /* latency stats */ + struct sched_info rq_sched_info; + unsigned long long rq_cpu_time; + /* could above be rq->cfs_rq.exec_clock + rq->rt_rq.rt_runtime ? */ + + /* sys_sched_yield() stats */ + unsigned int yld_count; + + /* schedule() stats */ + unsigned int sched_switch; + unsigned int sched_count; + unsigned int sched_goidle; + + /* try_to_wake_up() stats */ + unsigned int ttwu_count; + unsigned int ttwu_local; + + /* BKL stats */ + unsigned int bkl_count; +#endif +}; + +static DEFINE_PER_CPU(struct rq, runqueues) ____cacheline_aligned_in_smp; +static DEFINE_MUTEX(sched_hotcpu_mutex); + +#ifdef CONFIG_SMP +/* + * sched_domains_mutex serializes calls to arch_init_sched_domains, + * detach_destroy_domains and partition_sched_domains. + */ +static DEFINE_MUTEX(sched_domains_mutex); + +/* + * By default the system creates a single root-domain with all cpus as + * members (mimicking the global state we have today). + */ +static struct root_domain def_root_domain; + +int __weak arch_sd_sibling_asym_packing(void) +{ + return 0*SD_ASYM_PACKING; +} +#endif + +/* + * We add the notion of a root-domain which will be used to define per-domain + * variables. Each exclusive cpuset essentially defines an island domain by + * fully partitioning the member cpus from any other cpuset. Whenever a new + * exclusive cpuset is created, we also create and attach a new root-domain + * object. + * + */ +struct root_domain { + atomic_t refcount; + cpumask_var_t span; + cpumask_var_t online; + + /* + * The "RT overload" flag: it gets set if a CPU has more than + * one runnable RT task. + */ + cpumask_var_t rto_mask; + atomic_t rto_count; +#ifdef CONFIG_SMP + struct cpupri cpupri; +#endif +}; + +#define rcu_dereference_check_sched_domain(p) \ + rcu_dereference_check((p), \ + rcu_read_lock_sched_held() || \ + lockdep_is_held(&sched_domains_mutex)) + +/* + * The domain tree (rq->sd) is protected by RCU's quiescent state transition. + * See detach_destroy_domains: synchronize_sched for details. + * + * The domain tree of any CPU may only be accessed from within + * preempt-disabled sections. + */ +#define for_each_domain(cpu, __sd) \ + for (__sd = rcu_dereference_check_sched_domain(cpu_rq(cpu)->sd); __sd; __sd = __sd->parent) + +static inline void update_rq_clock(struct rq *rq); + +/* + * Sanity check should sched_clock return bogus values. We make sure it does + * not appear to go backwards, and use jiffies to determine the maximum and + * minimum it could possibly have increased, and round down to the nearest + * jiffy when it falls outside this. + */ +static inline void niffy_diff(s64 *niff_diff, int jiff_diff) +{ + unsigned long min_diff, max_diff; + + if (jiff_diff > 1) + min_diff = JIFFIES_TO_NS(jiff_diff - 1); + else + min_diff = 1; + /* Round up to the nearest tick for maximum */ + max_diff = JIFFIES_TO_NS(jiff_diff + 1); + + if (unlikely(*niff_diff < min_diff || *niff_diff > max_diff)) + *niff_diff = min_diff; +} + +#ifdef CONFIG_SMP +#define cpu_rq(cpu) (&per_cpu(runqueues, (cpu))) +#define this_rq() (&__get_cpu_var(runqueues)) +#define task_rq(p) cpu_rq(task_cpu(p)) +#define cpu_curr(cpu) (cpu_rq(cpu)->curr) +static inline int cpu_of(struct rq *rq) +{ + return rq->cpu; +} + +/* + * Niffies are a globally increasing nanosecond counter. Whenever a runqueue + * clock is updated with the grq.lock held, it is an opportunity to update the + * niffies value. Any CPU can update it by adding how much its clock has + * increased since it last updated niffies, minus any added niffies by other + * CPUs. + */ +static inline void update_clocks(struct rq *rq) +{ + s64 ndiff; + long jdiff; + + update_rq_clock(rq); + ndiff = rq->clock - rq->old_clock; + /* old_clock is only updated when we are updating niffies */ + rq->old_clock = rq->clock; + ndiff -= grq.niffies - rq->last_niffy; + jdiff = jiffies - grq.last_jiffy; + niffy_diff(&ndiff, jdiff); + grq.last_jiffy += jdiff; + grq.niffies += ndiff; + rq->last_niffy = grq.niffies; +} +#else /* CONFIG_SMP */ +static struct rq *uprq; +#define cpu_rq(cpu) (uprq) +#define this_rq() (uprq) +#define task_rq(p) (uprq) +#define cpu_curr(cpu) ((uprq)->curr) +static inline int cpu_of(struct rq *rq) +{ + return 0; +} + +static inline void update_clocks(struct rq *rq) +{ + s64 ndiff; + long jdiff; + + update_rq_clock(rq); + ndiff = rq->clock - rq->old_clock; + rq->old_clock = rq->clock; + jdiff = jiffies - grq.last_jiffy; + niffy_diff(&ndiff, jdiff); + grq.last_jiffy += jdiff; + grq.niffies += ndiff; +} +#endif +#define raw_rq() (&__raw_get_cpu_var(runqueues)) + +#include "sched_stats.h" + +#ifndef prepare_arch_switch +# define prepare_arch_switch(next) do { } while (0) +#endif +#ifndef finish_arch_switch +# define finish_arch_switch(prev) do { } while (0) +#endif + +/* + * All common locking functions performed on grq.lock. rq->clock is local to + * the CPU accessing it so it can be modified just with interrupts disabled + * when we're not updating niffies. + * Looking up task_rq must be done under grq.lock to be safe. + */ +static void update_rq_clock_task(struct rq *rq, s64 delta); + +static inline void update_rq_clock(struct rq *rq) +{ + s64 delta = sched_clock_cpu(cpu_of(rq)) - rq->clock; + + rq->clock += delta; + update_rq_clock_task(rq, delta); +} + +static inline int task_running(struct task_struct *p) +{ + return p->oncpu; +} + +static inline void grq_lock(void) + __acquires(grq.lock) +{ + raw_spin_lock(&grq.lock); +} + +static inline void grq_unlock(void) + __releases(grq.lock) +{ + raw_spin_unlock(&grq.lock); +} + +static inline void grq_lock_irq(void) + __acquires(grq.lock) +{ + raw_spin_lock_irq(&grq.lock); +} + +static inline void time_lock_grq(struct rq *rq) + __acquires(grq.lock) +{ + grq_lock(); + update_clocks(rq); +} + +static inline void grq_unlock_irq(void) + __releases(grq.lock) +{ + raw_spin_unlock_irq(&grq.lock); +} + +static inline void grq_lock_irqsave(unsigned long *flags) + __acquires(grq.lock) +{ + raw_spin_lock_irqsave(&grq.lock, *flags); +} + +static inline void grq_unlock_irqrestore(unsigned long *flags) + __releases(grq.lock) +{ + raw_spin_unlock_irqrestore(&grq.lock, *flags); +} + +static inline struct rq +*task_grq_lock(struct task_struct *p, unsigned long *flags) + __acquires(grq.lock) +{ + grq_lock_irqsave(flags); + return task_rq(p); +} + +static inline struct rq +*time_task_grq_lock(struct task_struct *p, unsigned long *flags) + __acquires(grq.lock) +{ + struct rq *rq = task_grq_lock(p, flags); + update_clocks(rq); + return rq; +} + +static inline struct rq *task_grq_lock_irq(struct task_struct *p) + __acquires(grq.lock) +{ + grq_lock_irq(); + return task_rq(p); +} + +static inline void time_task_grq_lock_irq(struct task_struct *p) + __acquires(grq.lock) +{ + struct rq *rq = task_grq_lock_irq(p); + update_clocks(rq); +} + +static inline void task_grq_unlock_irq(void) + __releases(grq.lock) +{ + grq_unlock_irq(); +} + +static inline void task_grq_unlock(unsigned long *flags) + __releases(grq.lock) +{ + grq_unlock_irqrestore(flags); +} + +/** + * grunqueue_is_locked + * + * Returns true if the global runqueue is locked. + * This interface allows printk to be called with the runqueue lock + * held and know whether or not it is OK to wake up the klogd. + */ +inline int grunqueue_is_locked(void) +{ + return raw_spin_is_locked(&grq.lock); +} + +inline void grq_unlock_wait(void) + __releases(grq.lock) +{ + smp_mb(); /* spin-unlock-wait is not a full memory barrier */ + raw_spin_unlock_wait(&grq.lock); +} + +static inline void time_grq_lock(struct rq *rq, unsigned long *flags) + __acquires(grq.lock) +{ + local_irq_save(*flags); + time_lock_grq(rq); +} + +static inline struct rq *__task_grq_lock(struct task_struct *p) + __acquires(grq.lock) +{ + grq_lock(); + return task_rq(p); +} + +static inline void __task_grq_unlock(void) + __releases(grq.lock) +{ + grq_unlock(); +} + +#ifndef __ARCH_WANT_UNLOCKED_CTXSW +static inline void prepare_lock_switch(struct rq *rq, struct task_struct *next) +{ +} + +static inline void finish_lock_switch(struct rq *rq, struct task_struct *prev) +{ +#ifdef CONFIG_DEBUG_SPINLOCK + /* this is a valid case when another task releases the spinlock */ + grq.lock.owner = current; +#endif + /* + * If we are tracking spinlock dependencies then we have to + * fix up the runqueue lock - which gets 'carried over' from + * prev into current: + */ + spin_acquire(&grq.lock.dep_map, 0, 0, _THIS_IP_); + + grq_unlock_irq(); +} + +#else /* __ARCH_WANT_UNLOCKED_CTXSW */ + +static inline void prepare_lock_switch(struct rq *rq, struct task_struct *next) +{ +#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW + grq_unlock_irq(); +#else + grq_unlock(); +#endif +} + +static inline void finish_lock_switch(struct rq *rq, struct task_struct *prev) +{ + smp_wmb(); +#ifndef __ARCH_WANT_INTERRUPTS_ON_CTXSW + local_irq_enable(); +#endif +} +#endif /* __ARCH_WANT_UNLOCKED_CTXSW */ + +static inline int deadline_before(u64 deadline, u64 time) +{ + return (deadline < time); +} + +static inline int deadline_after(u64 deadline, u64 time) +{ + return (deadline > time); +} + +/* + * A task that is queued but not running will be on the grq run list. + * A task that is not running or queued will not be on the grq run list. + * A task that is currently running will have ->oncpu set but not on the + * grq run list. + */ +static inline int task_queued(struct task_struct *p) +{ + return (!list_empty(&p->run_list)); +} + +/* + * Removing from the global runqueue. Enter with grq locked. + */ +static void dequeue_task(struct task_struct *p) +{ + list_del_init(&p->run_list); + if (list_empty(grq.queue + p->prio)) + __clear_bit(p->prio, grq.prio_bitmap); +} + +/* + * To determine if it's safe for a task of SCHED_IDLEPRIO to actually run as + * an idle task, we ensure none of the following conditions are met. + */ +static int idleprio_suitable(struct task_struct *p) +{ + return (!freezing(p) && !signal_pending(p) && + !(task_contributes_to_load(p)) && !(p->flags & (PF_EXITING))); +} + +/* + * To determine if a task of SCHED_ISO can run in pseudo-realtime, we check + * that the iso_refractory flag is not set. + */ +static int isoprio_suitable(void) +{ + return !grq.iso_refractory; +} + +/* + * Adding to the global runqueue. Enter with grq locked. + */ +static void enqueue_task(struct task_struct *p) +{ + if (!rt_task(p)) { + /* Check it hasn't gotten rt from PI */ + if ((idleprio_task(p) && idleprio_suitable(p)) || + (iso_task(p) && isoprio_suitable())) + p->prio = p->normal_prio; + else + p->prio = NORMAL_PRIO; + } + __set_bit(p->prio, grq.prio_bitmap); + list_add_tail(&p->run_list, grq.queue + p->prio); + sched_info_queued(p); +} + +/* Only idle task does this as a real time task*/ +static inline void enqueue_task_head(struct task_struct *p) +{ + __set_bit(p->prio, grq.prio_bitmap); + list_add(&p->run_list, grq.queue + p->prio); + sched_info_queued(p); +} + +static inline void requeue_task(struct task_struct *p) +{ + sched_info_queued(p); +} + +/* + * Returns the relative length of deadline all compared to the shortest + * deadline which is that of nice -20. + */ +static inline int task_prio_ratio(struct task_struct *p) +{ + return prio_ratios[TASK_USER_PRIO(p)]; +} + +/* + * task_timeslice - all tasks of all priorities get the exact same timeslice + * length. CPU distribution is handled by giving different deadlines to + * tasks of different priorities. Use 128 as the base value for fast shifts. + */ +static inline int task_timeslice(struct task_struct *p) +{ + return (rr_interval * task_prio_ratio(p) / 128); +} + +#ifdef CONFIG_SMP +/* + * qnr is the "queued but not running" count which is the total number of + * tasks on the global runqueue list waiting for cpu time but not actually + * currently running on a cpu. + */ +static inline void inc_qnr(void) +{ + grq.qnr++; +} + +static inline void dec_qnr(void) +{ + grq.qnr--; +} + +static inline int queued_notrunning(void) +{ + return grq.qnr; +} + +/* + * The cpu_idle_map stores a bitmap of all the CPUs currently idle to + * allow easy lookup of whether any suitable idle CPUs are available. + * It's cheaper to maintain a binary yes/no if there are any idle CPUs on the + * idle_cpus variable than to do a full bitmask check when we are busy. + */ +static inline void set_cpuidle_map(unsigned long cpu) +{ + if (likely(cpu_online(cpu))) { + cpu_set(cpu, grq.cpu_idle_map); + grq.idle_cpus = 1; + } +} + +static inline void clear_cpuidle_map(unsigned long cpu) +{ + cpu_clear(cpu, grq.cpu_idle_map); + if (cpus_empty(grq.cpu_idle_map)) + grq.idle_cpus = 0; +} + +static int suitable_idle_cpus(struct task_struct *p) +{ + if (!grq.idle_cpus) + return 0; + return (cpus_intersects(p->cpus_allowed, grq.cpu_idle_map)); +} + +static void resched_task(struct task_struct *p); + +#define CPUIDLE_DIFF_THREAD (1) +#define CPUIDLE_DIFF_CORE (2) +#define CPUIDLE_CACHE_BUSY (4) +#define CPUIDLE_DIFF_CPU (8) +#define CPUIDLE_THREAD_BUSY (16) +#define CPUIDLE_DIFF_NODE (32) + +/* + * The best idle CPU is chosen according to the CPUIDLE ranking above where the + * lowest value would give the most suitable CPU to schedule p onto next. The + * order works out to be the following: + * + * Same core, idle or busy cache, idle threads + * Other core, same cache, idle or busy cache, idle threads. + * Same node, other CPU, idle cache, idle threads. + * Same node, other CPU, busy cache, idle threads. + * Same core, busy threads. + * Other core, same cache, busy threads. + * Same node, other CPU, busy threads. + * Other node, other CPU, idle cache, idle threads. + * Other node, other CPU, busy cache, idle threads. + * Other node, other CPU, busy threads. + */ +static void +resched_best_mask(unsigned long best_cpu, struct rq *rq, cpumask_t *tmpmask) +{ + unsigned long cpu_tmp, best_ranking; + + best_ranking = ~0UL; + + for_each_cpu_mask(cpu_tmp, *tmpmask) { + unsigned long ranking; + struct rq *tmp_rq; + + ranking = 0; + tmp_rq = cpu_rq(cpu_tmp); + +#ifdef CONFIG_NUMA + if (rq->cpu_locality[cpu_tmp] > 3) + ranking |= CPUIDLE_DIFF_NODE; + else +#endif + if (rq->cpu_locality[cpu_tmp] > 2) + ranking |= CPUIDLE_DIFF_CPU; +#ifdef CONFIG_SCHED_MC + if (rq->cpu_locality[cpu_tmp] == 2) + ranking |= CPUIDLE_DIFF_CORE; + if (!(tmp_rq->cache_idle(cpu_tmp))) + ranking |= CPUIDLE_CACHE_BUSY; +#endif +#ifdef CONFIG_SCHED_SMT + if (rq->cpu_locality[cpu_tmp] == 1) + ranking |= CPUIDLE_DIFF_THREAD; + if (!(tmp_rq->siblings_idle(cpu_tmp))) + ranking |= CPUIDLE_THREAD_BUSY; +#endif + if (ranking < best_ranking) { + best_cpu = cpu_tmp; + if (ranking == 0) + break; + best_ranking = ranking; + } + } + + resched_task(cpu_rq(best_cpu)->curr); +} + +static void resched_best_idle(struct task_struct *p) +{ + cpumask_t tmpmask; + + cpus_and(tmpmask, p->cpus_allowed, grq.cpu_idle_map); + resched_best_mask(task_cpu(p), task_rq(p), &tmpmask); +} + +static inline void resched_suitable_idle(struct task_struct *p) +{ + if (suitable_idle_cpus(p)) + resched_best_idle(p); +} +/* + * Flags to tell us whether this CPU is running a CPU frequency governor that + * has slowed its speed or not. No locking required as the very rare wrongly + * read value would be harmless. + */ +void cpu_scaling(int cpu) +{ + cpu_rq(cpu)->scaling = 1; +} + +void cpu_nonscaling(int cpu) +{ + cpu_rq(cpu)->scaling = 0; +} + +static inline int scaling_rq(struct rq *rq) +{ + return rq->scaling; +} +#else /* CONFIG_SMP */ +static inline void inc_qnr(void) +{ +} + +static inline void dec_qnr(void) +{ +} + +static inline int queued_notrunning(void) +{ + return grq.nr_running; +} + +static inline void set_cpuidle_map(unsigned long cpu) +{ +} + +static inline void clear_cpuidle_map(unsigned long cpu) +{ +} + +static inline int suitable_idle_cpus(struct task_struct *p) +{ + return uprq->curr == uprq->idle; +} + +static inline void resched_suitable_idle(struct task_struct *p) +{ +} + +void cpu_scaling(int __unused) +{ +} + +void cpu_nonscaling(int __unused) +{ +} + +/* + * Although CPUs can scale in UP, there is nowhere else for tasks to go so this + * always returns 0. + */ +static inline int scaling_rq(struct rq *rq) +{ + return 0; +} +#endif /* CONFIG_SMP */ +EXPORT_SYMBOL_GPL(cpu_scaling); +EXPORT_SYMBOL_GPL(cpu_nonscaling); + +/* + * activate_idle_task - move idle task to the _front_ of runqueue. + */ +static inline void activate_idle_task(struct task_struct *p) +{ + enqueue_task_head(p); + grq.nr_running++; + inc_qnr(); +} + +static inline int normal_prio(struct task_struct *p) +{ + if (has_rt_policy(p)) + return MAX_RT_PRIO - 1 - p->rt_priority; + if (idleprio_task(p)) + return IDLE_PRIO; + if (iso_task(p)) + return ISO_PRIO; + return NORMAL_PRIO; +} + +/* + * Calculate the current priority, i.e. the priority + * taken into account by the scheduler. This value might + * be boosted by RT tasks as it will be RT if the task got + * RT-boosted. If not then it returns p->normal_prio. + */ +static int effective_prio(struct task_struct *p) +{ + p->normal_prio = normal_prio(p); + /* + * If we are RT tasks or we were boosted to RT priority, + * keep the priority unchanged. Otherwise, update priority + * to the normal priority: + */ + if (!rt_prio(p->prio)) + return p->normal_prio; + return p->prio; +} + +/* + * activate_task - move a task to the runqueue. Enter with grq locked. + */ +static void activate_task(struct task_struct *p, struct rq *rq) +{ + update_clocks(rq); + + /* + * Sleep time is in units of nanosecs, so shift by 20 to get a + * milliseconds-range estimation of the amount of time that the task + * spent sleeping: + */ + if (unlikely(prof_on == SLEEP_PROFILING)) { + if (p->state == TASK_UNINTERRUPTIBLE) + profile_hits(SLEEP_PROFILING, (void *)get_wchan(p), + (rq->clock - p->last_ran) >> 20); + } + + p->prio = effective_prio(p); + if (task_contributes_to_load(p)) + grq.nr_uninterruptible--; + enqueue_task(p); + grq.nr_running++; + inc_qnr(); +} + +/* + * deactivate_task - If it's running, it's not on the grq and we can just + * decrement the nr_running. Enter with grq locked. + */ +static inline void deactivate_task(struct task_struct *p) +{ + if (task_contributes_to_load(p)) + grq.nr_uninterruptible++; + grq.nr_running--; +} + +#ifdef CONFIG_SMP +void set_task_cpu(struct task_struct *p, unsigned int cpu) +{ + trace_sched_migrate_task(p, cpu); + if (task_cpu(p) != cpu) + perf_sw_event(PERF_COUNT_SW_CPU_MIGRATIONS, 1, 1, NULL, 0); + + /* + * After ->cpu is set up to a new value, task_grq_lock(p, ...) can be + * successfuly executed on another CPU. We must ensure that updates of + * per-task data have been completed by this moment. + */ + smp_wmb(); + task_thread_info(p)->cpu = cpu; +} + +static inline void clear_sticky(struct task_struct *p) +{ + p->sticky = 0; +} + +static inline int task_sticky(struct task_struct *p) +{ + return p->sticky; +} + +/* Reschedule the best idle CPU that is not this one. */ +static void +resched_closest_idle(struct rq *rq, unsigned long cpu, struct task_struct *p) +{ + cpumask_t tmpmask; + + cpus_and(tmpmask, p->cpus_allowed, grq.cpu_idle_map); + cpu_clear(cpu, tmpmask); + if (cpus_empty(tmpmask)) + return; + resched_best_mask(cpu, rq, &tmpmask); +} + +/* + * We set the sticky flag on a task that is descheduled involuntarily meaning + * it is awaiting further CPU time. If the last sticky task is still sticky + * but unlucky enough to not be the next task scheduled, we unstick it and try + * to find it an idle CPU. Realtime tasks do not stick to minimise their + * latency at all times. + */ +static inline void +swap_sticky(struct rq *rq, unsigned long cpu, struct task_struct *p) +{ + if (rq->sticky_task) { + if (rq->sticky_task == p) { + p->sticky = 1; + return; + } + if (task_sticky(rq->sticky_task)) { + clear_sticky(rq->sticky_task); + resched_closest_idle(rq, cpu, rq->sticky_task); + } + } + if (!rt_task(p)) { + p->sticky = 1; + rq->sticky_task = p; + } else { + resched_closest_idle(rq, cpu, p); + rq->sticky_task = NULL; + } +} + +static inline void unstick_task(struct rq *rq, struct task_struct *p) +{ + rq->sticky_task = NULL; + clear_sticky(p); +} +#else +static inline void clear_sticky(struct task_struct *p) +{ +} + +static inline int task_sticky(struct task_struct *p) +{ + return 0; +} + +static inline void +swap_sticky(struct rq *rq, unsigned long cpu, struct task_struct *p) +{ +} + +static inline void unstick_task(struct rq *rq, struct task_struct *p) +{ +} +#endif + +/* + * Move a task off the global queue and take it to a cpu for it will + * become the running task. + */ +static inline void take_task(struct rq *rq, struct task_struct *p) +{ + set_task_cpu(p, cpu_of(rq)); + dequeue_task(p); + clear_sticky(p); + dec_qnr(); +} + +/* + * Returns a descheduling task to the grq runqueue unless it is being + * deactivated. + */ +static inline void return_task(struct task_struct *p, int deactivate) +{ + if (deactivate) + deactivate_task(p); + else { + inc_qnr(); + enqueue_task(p); + } +} + +/* + * resched_task - mark a task 'to be rescheduled now'. + * + * On UP this means the setting of the need_resched flag, on SMP it + * might also involve a cross-CPU call to trigger the scheduler on + * the target CPU. + */ +#ifdef CONFIG_SMP + +#ifndef tsk_is_polling +#define tsk_is_polling(t) test_tsk_thread_flag(t, TIF_POLLING_NRFLAG) +#endif + +static void resched_task(struct task_struct *p) +{ + int cpu; + + assert_raw_spin_locked(&grq.lock); + + if (unlikely(test_tsk_thread_flag(p, TIF_NEED_RESCHED))) + return; + + set_tsk_thread_flag(p, TIF_NEED_RESCHED); + + cpu = task_cpu(p); + if (cpu == smp_processor_id()) + return; + + /* NEED_RESCHED must be visible before we test polling */ + smp_mb(); + if (!tsk_is_polling(p)) + smp_send_reschedule(cpu); +} + +#else +static inline void resched_task(struct task_struct *p) +{ + assert_raw_spin_locked(&grq.lock); + set_tsk_need_resched(p); +} +#endif + +/** + * task_curr - is this task currently executing on a CPU? + * @p: the task in question. + */ +inline int task_curr(const struct task_struct *p) +{ + return cpu_curr(task_cpu(p)) == p; +} + +#ifdef CONFIG_SMP +struct migration_req { + struct task_struct *task; + int dest_cpu; +}; + +/* + * wait_task_inactive - wait for a thread to unschedule. + * + * If @match_state is nonzero, it's the @p->state value just checked and + * not expected to change. If it changes, i.e. @p might have woken up, + * then return zero. When we succeed in waiting for @p to be off its CPU, + * we return a positive number (its total switch count). If a second call + * a short while later returns the same number, the caller can be sure that + * @p has remained unscheduled the whole time. + * + * The caller must ensure that the task *will* unschedule sometime soon, + * else this function might spin for a *long* time. This function can't + * be called with interrupts off, or it may introduce deadlock with + * smp_call_function() if an IPI is sent by the same process we are + * waiting to become inactive. + */ +unsigned long wait_task_inactive(struct task_struct *p, long match_state) +{ + unsigned long flags; + int running, on_rq; + unsigned long ncsw; + struct rq *rq; + + for (;;) { + /* + * We do the initial early heuristics without holding + * any task-queue locks at all. We'll only try to get + * the runqueue lock when things look like they will + * work out! In the unlikely event rq is dereferenced + * since we're lockless, grab it again. + */ +#ifdef CONFIG_SMP +retry_rq: + rq = task_rq(p); + if (unlikely(!rq)) + goto retry_rq; +#else /* CONFIG_SMP */ + rq = task_rq(p); +#endif + /* + * If the task is actively running on another CPU + * still, just relax and busy-wait without holding + * any locks. + * + * NOTE! Since we don't hold any locks, it's not + * even sure that "rq" stays as the right runqueue! + * But we don't care, since this will return false + * if the runqueue has changed and p is actually now + * running somewhere else! + */ + while (task_running(p) && p == rq->curr) { + if (match_state && unlikely(p->state != match_state)) + return 0; + cpu_relax(); + } + + /* + * Ok, time to look more closely! We need the grq + * lock now, to be *sure*. If we're wrong, we'll + * just go back and repeat. + */ + rq = task_grq_lock(p, &flags); + trace_sched_wait_task(p); + running = task_running(p); + on_rq = task_queued(p); + ncsw = 0; + if (!match_state || p->state == match_state) + ncsw = p->nvcsw | LONG_MIN; /* sets MSB */ + task_grq_unlock(&flags); + + /* + * If it changed from the expected state, bail out now. + */ + if (unlikely(!ncsw)) + break; + + /* + * Was it really running after all now that we + * checked with the proper locks actually held? + * + * Oops. Go back and try again.. + */ + if (unlikely(running)) { + cpu_relax(); + continue; + } + + /* + * It's not enough that it's not actively running, + * it must be off the runqueue _entirely_, and not + * preempted! + * + * So if it was still runnable (but just not actively + * running right now), it's preempted, and we should + * yield - it could be a while. + */ + if (unlikely(on_rq)) { + schedule_timeout_uninterruptible(1); + continue; + } + + /* + * Ahh, all good. It wasn't running, and it wasn't + * runnable, which means that it will never become + * running in the future either. We're all done! + */ + break; + } + + return ncsw; +} + +/*** + * kick_process - kick a running thread to enter/exit the kernel + * @p: the to-be-kicked thread + * + * Cause a process which is running on another CPU to enter + * kernel-mode, without any delay. (to get signals handled.) + * + * NOTE: this function doesnt have to take the runqueue lock, + * because all it wants to ensure is that the remote task enters + * the kernel. If the IPI races and the task has been migrated + * to another CPU then no harm is done and the purpose has been + * achieved as well. + */ +void kick_process(struct task_struct *p) +{ + int cpu; + + preempt_disable(); + cpu = task_cpu(p); + if ((cpu != smp_processor_id()) && task_curr(p)) + smp_send_reschedule(cpu); + preempt_enable(); +} +EXPORT_SYMBOL_GPL(kick_process); +#endif + +#define rq_idle(rq) ((rq)->rq_prio == PRIO_LIMIT) + +/* + * RT tasks preempt purely on priority. SCHED_NORMAL tasks preempt on the + * basis of earlier deadlines. SCHED_IDLEPRIO don't preempt anything else or + * between themselves, they cooperatively multitask. An idle rq scores as + * prio PRIO_LIMIT so it is always preempted. + */ +static inline int +can_preempt(struct task_struct *p, int prio, u64 deadline) +{ + /* Better static priority RT task or better policy preemption */ + if (p->prio < prio) + return 1; + if (p->prio > prio) + return 0; + /* SCHED_NORMAL, BATCH and ISO will preempt based on deadline */ + if (!deadline_before(p->deadline, deadline)) + return 0; + return 1; +} +#ifdef CONFIG_SMP +#ifdef CONFIG_HOTPLUG_CPU +/* + * Check to see if there is a task that is affined only to offline CPUs but + * still wants runtime. This happens to kernel threads during suspend/halt and + * disabling of CPUs. + */ +static inline int online_cpus(struct task_struct *p) +{ + return (likely(cpus_intersects(cpu_online_map, p->cpus_allowed))); +} +#else /* CONFIG_HOTPLUG_CPU */ +/* All available CPUs are always online without hotplug. */ +static inline int online_cpus(struct task_struct *p) +{ + return 1; +} +#endif + +/* + * Check to see if p can run on cpu, and if not, whether there are any online + * CPUs it can run on instead. + */ +static inline int needs_other_cpu(struct task_struct *p, int cpu) +{ + if (unlikely(!cpu_isset(cpu, p->cpus_allowed))) + return 1; + return 0; +} + +/* + * latest_deadline and highest_prio_rq are initialised only to silence the + * compiler. When all else is equal, still prefer this_rq. + */ +static void try_preempt(struct task_struct *p, struct rq *this_rq) +{ + struct rq *highest_prio_rq = this_rq; + u64 latest_deadline; + unsigned long cpu; + int highest_prio; + cpumask_t tmp; + + /* + * We clear the sticky flag here because for a task to have called + * try_preempt with the sticky flag enabled means some complicated + * re-scheduling has occurred and we should ignore the sticky flag. + */ + clear_sticky(p); + + if (suitable_idle_cpus(p)) { + resched_best_idle(p); + return; + } + + /* IDLEPRIO tasks never preempt anything */ + if (p->policy == SCHED_IDLEPRIO) + return; + + if (likely(online_cpus(p))) + cpus_and(tmp, cpu_online_map, p->cpus_allowed); + else + return; + + latest_deadline = 0; + highest_prio = -1; + + for_each_cpu_mask(cpu, tmp) { + struct rq *rq; + int rq_prio; + + rq = cpu_rq(cpu); + rq_prio = rq->rq_prio; + if (rq_prio < highest_prio) + continue; + + if (rq_prio > highest_prio || + deadline_after(rq->rq_deadline, latest_deadline)) { + latest_deadline = rq->rq_deadline; + highest_prio = rq_prio; + highest_prio_rq = rq; + } + } + + if (!can_preempt(p, highest_prio, highest_prio_rq->rq_deadline)) + return; + + resched_task(highest_prio_rq->curr); +} +#else /* CONFIG_SMP */ +static inline int needs_other_cpu(struct task_struct *p, int cpu) +{ + return 0; +} + +static void try_preempt(struct task_struct *p, struct rq *this_rq) +{ + if (p->policy == SCHED_IDLEPRIO) + return; + if (can_preempt(p, uprq->rq_prio, uprq->rq_deadline)) + resched_task(uprq->curr); +} +#endif /* CONFIG_SMP */ + +/** + * task_oncpu_function_call - call a function on the cpu on which a task runs + * @p: the task to evaluate + * @func: the function to be called + * @info: the function call argument + * + * Calls the function @func when the task is currently running. This might + * be on the current CPU, which just calls the function directly + */ +void task_oncpu_function_call(struct task_struct *p, + void (*func) (void *info), void *info) +{ + int cpu; + + preempt_disable(); + cpu = task_cpu(p); + if (task_curr(p)) + smp_call_function_single(cpu, func, info, 1); + preempt_enable(); +} + +static inline void ttwu_activate(struct task_struct *p, struct rq *rq, + bool is_sync) +{ + activate_task(p, rq); + + /* + * Sync wakeups (i.e. those types of wakeups where the waker + * has indicated that it will leave the CPU in short order) + * don't trigger a preemption if there are no idle cpus, + * instead waiting for current to deschedule. + */ + if (!is_sync || suitable_idle_cpus(p)) + try_preempt(p, rq); +} + +static inline void ttwu_post_activation(struct task_struct *p, struct rq *rq, + bool success) +{ + trace_sched_wakeup(p, success); + p->state = TASK_RUNNING; + + /* + * if a worker is waking up, notify workqueue. Note that on BFS, we + * don't really know what cpu it will be, so we fake it for + * wq_worker_waking_up :/ + */ + if ((p->flags & PF_WQ_WORKER) && success) + wq_worker_waking_up(p, cpu_of(rq)); +} + +/*** + * try_to_wake_up - wake up a thread + * @p: the thread to be awakened + * @state: the mask of task states that can be woken + * @wake_flags: wake modifier flags (WF_*) + * + * Put it on the run-queue if it's not already there. The "current" + * thread is always on the run-queue (except when the actual + * re-schedule is in progress), and as such you're allowed to do + * the simpler "current->state = TASK_RUNNING" to mark yourself + * runnable without the overhead of this. + * + * Returns %true if @p was woken up, %false if it was already running + * or @state didn't match @p's state. + */ +static int try_to_wake_up(struct task_struct *p, unsigned int state, + int wake_flags) +{ + unsigned long flags; + int success = 0; + struct rq *rq; + + get_cpu(); + + /* This barrier is undocumented, probably for p->state? くそ */ + smp_wmb(); + + /* + * No need to do time_lock_grq as we only need to update the rq clock + * if we activate the task + */ + rq = task_grq_lock(p, &flags); + + /* state is a volatile long, どうして、分からない */ + if (!((unsigned int)p->state & state)) + goto out_unlock; + + if (task_queued(p) || task_running(p)) + goto out_running; + + ttwu_activate(p, rq, wake_flags & WF_SYNC); + success = true; + +out_running: + ttwu_post_activation(p, rq, success); +out_unlock: + task_grq_unlock(&flags); + put_cpu(); + + return success; +} + +/** + * try_to_wake_up_local - try to wake up a local task with grq lock held + * @p: the thread to be awakened + * + * Put @p on the run-queue if it's not already there. The caller must + * ensure that grq is locked and, @p is not the current task. + * grq stays locked over invocation. + */ +static void try_to_wake_up_local(struct task_struct *p) +{ + struct rq *rq = task_rq(p); + bool success = false; + + lockdep_assert_held(&grq.lock); + + if (!(p->state & TASK_NORMAL)) + return; + + if (!task_queued(p)) { + if (likely(!task_running(p))) { + schedstat_inc(rq, ttwu_count); + schedstat_inc(rq, ttwu_local); + } + ttwu_activate(p, rq, false); + success = true; + } + ttwu_post_activation(p, rq, success); +} + +/** + * wake_up_process - Wake up a specific process + * @p: The process to be woken up. + * + * Attempt to wake up the nominated process and move it to the set of runnable + * processes. Returns 1 if the process was woken up, 0 if it was already + * running. + * + * It may be assumed that this function implies a write memory barrier before + * changing the task state if and only if any tasks are woken up. + */ +int wake_up_process(struct task_struct *p) +{ + return try_to_wake_up(p, TASK_ALL, 0); +} +EXPORT_SYMBOL(wake_up_process); + +int wake_up_state(struct task_struct *p, unsigned int state) +{ + return try_to_wake_up(p, state, 0); +} + +static void time_slice_expired(struct task_struct *p); + +/* + * Perform scheduler related setup for a newly forked process p. + * p is forked by current. + */ +void sched_fork(struct task_struct *p, int clone_flags) +{ + struct task_struct *curr; + int cpu = get_cpu(); + struct rq *rq; + +#ifdef CONFIG_PREEMPT_NOTIFIERS + INIT_HLIST_HEAD(&p->preempt_notifiers); +#endif + /* + * We mark the process as running here. This guarantees that + * nobody will actually run it, and a signal or other external + * event cannot wake it up and insert it on the runqueue either. + */ + p->state = TASK_RUNNING; + set_task_cpu(p, cpu); + + /* Should be reset in fork.c but done here for ease of bfs patching */ + p->sched_time = p->stime_pc = p->utime_pc = 0; + + /* + * Revert to default priority/policy on fork if requested. + */ + if (unlikely(p->sched_reset_on_fork)) { + if (p->policy == SCHED_FIFO || p->policy == SCHED_RR) { + p->policy = SCHED_NORMAL; + p->normal_prio = normal_prio(p); + } + + if (PRIO_TO_NICE(p->static_prio) < 0) { + p->static_prio = NICE_TO_PRIO(0); + p->normal_prio = p->static_prio; + } + + /* + * We don't need the reset flag anymore after the fork. It has + * fulfilled its duty: + */ + p->sched_reset_on_fork = 0; + } + + curr = current; + /* + * Make sure we do not leak PI boosting priority to the child. + */ + p->prio = curr->normal_prio; + + INIT_LIST_HEAD(&p->run_list); +#if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT) + if (unlikely(sched_info_on())) + memset(&p->sched_info, 0, sizeof(p->sched_info)); +#endif + + p->oncpu = 0; + clear_sticky(p); + +#ifdef CONFIG_PREEMPT + /* Want to start with kernel preemption disabled. */ + task_thread_info(p)->preempt_count = 1; +#endif + if (unlikely(p->policy == SCHED_FIFO)) + goto out; + /* + * Share the timeslice between parent and child, thus the + * total amount of pending timeslices in the system doesn't change, + * resulting in more scheduling fairness. If it's negative, it won't + * matter since that's the same as being 0. current's time_slice is + * actually in rq_time_slice when it's running, as is its last_ran + * value. rq->rq_deadline is only modified within schedule() so it + * is always equal to current->deadline. + */ + rq = task_grq_lock_irq(curr); + if (likely(rq->rq_time_slice >= RESCHED_US * 2)) { + rq->rq_time_slice /= 2; + p->time_slice = rq->rq_time_slice; + } else { + /* + * Forking task has run out of timeslice. Reschedule it and + * start its child with a new time slice and deadline. The + * child will end up running first because its deadline will + * be slightly earlier. + */ + rq->rq_time_slice = 0; + set_tsk_need_resched(curr); + time_slice_expired(p); + } + p->last_ran = rq->rq_last_ran; + task_grq_unlock_irq(); +out: + put_cpu(); +} + +/* + * wake_up_new_task - wake up a newly created task for the first time. + * + * This function will do some initial scheduler statistics housekeeping + * that must be done for every newly created context, then puts the task + * on the runqueue and wakes it. + */ +void wake_up_new_task(struct task_struct *p, unsigned long clone_flags) +{ + struct task_struct *parent; + unsigned long flags; + struct rq *rq; + + rq = task_grq_lock(p, &flags); + p->state = TASK_RUNNING; + parent = p->parent; + /* Unnecessary but small chance that the parent changed CPU */ + set_task_cpu(p, task_cpu(parent)); + activate_task(p, rq); + trace_sched_wakeup_new(p, 1); + if (!(clone_flags & CLONE_VM) && rq->curr == parent && + !suitable_idle_cpus(p)) { + /* + * The VM isn't cloned, so we're in a good position to + * do child-runs-first in anticipation of an exec. This + * usually avoids a lot of COW overhead. + */ + resched_task(parent); + } else + try_preempt(p, rq); + task_grq_unlock(&flags); +} + +#ifdef CONFIG_PREEMPT_NOTIFIERS + +/** + * preempt_notifier_register - tell me when current is being preempted & rescheduled + * @notifier: notifier struct to register + */ +void preempt_notifier_register(struct preempt_notifier *notifier) +{ + hlist_add_head(¬ifier->link, ¤t->preempt_notifiers); +} +EXPORT_SYMBOL_GPL(preempt_notifier_register); + +/** + * preempt_notifier_unregister - no longer interested in preemption notifications + * @notifier: notifier struct to unregister + * + * This is safe to call from within a preemption notifier. + */ +void preempt_notifier_unregister(struct preempt_notifier *notifier) +{ + hlist_del(¬ifier->link); +} +EXPORT_SYMBOL_GPL(preempt_notifier_unregister); + +static void fire_sched_in_preempt_notifiers(struct task_struct *curr) +{ + struct preempt_notifier *notifier; + struct hlist_node *node; + + hlist_for_each_entry(notifier, node, &curr->preempt_notifiers, link) + notifier->ops->sched_in(notifier, raw_smp_processor_id()); +} + +static void +fire_sched_out_preempt_notifiers(struct task_struct *curr, + struct task_struct *next) +{ + struct preempt_notifier *notifier; + struct hlist_node *node; + + hlist_for_each_entry(notifier, node, &curr->preempt_notifiers, link) + notifier->ops->sched_out(notifier, next); +} + +#else /* !CONFIG_PREEMPT_NOTIFIERS */ + +static void fire_sched_in_preempt_notifiers(struct task_struct *curr) +{ +} + +static void +fire_sched_out_preempt_notifiers(struct task_struct *curr, + struct task_struct *next) +{ +} + +#endif /* CONFIG_PREEMPT_NOTIFIERS */ + +/** + * prepare_task_switch - prepare to switch tasks + * @rq: the runqueue preparing to switch + * @next: the task we are going to switch to. + * + * This is called with the rq lock held and interrupts off. It must + * be paired with a subsequent finish_task_switch after the context + * switch. + * + * prepare_task_switch sets up locking and calls architecture specific + * hooks. + */ +static inline void +prepare_task_switch(struct rq *rq, struct task_struct *prev, + struct task_struct *next) +{ + fire_sched_out_preempt_notifiers(prev, next); + prepare_lock_switch(rq, next); + prepare_arch_switch(next); +} + +/** + * finish_task_switch - clean up after a task-switch + * @rq: runqueue associated with task-switch + * @prev: the thread we just switched away from. + * + * finish_task_switch must be called after the context switch, paired + * with a prepare_task_switch call before the context switch. + * finish_task_switch will reconcile locking set up by prepare_task_switch, + * and do any other architecture-specific cleanup actions. + * + * Note that we may have delayed dropping an mm in context_switch(). If + * so, we finish that here outside of the runqueue lock. (Doing it + * with the lock held can cause deadlocks; see schedule() for + * details.) + */ +static inline void finish_task_switch(struct rq *rq, struct task_struct *prev) + __releases(grq.lock) +{ + struct mm_struct *mm = rq->prev_mm; + long prev_state; + + rq->prev_mm = NULL; + + /* + * A task struct has one reference for the use as "current". + * If a task dies, then it sets TASK_DEAD in tsk->state and calls + * schedule one last time. The schedule call will never return, and + * the scheduled task must drop that reference. + * The test for TASK_DEAD must occur while the runqueue locks are + * still held, otherwise prev could be scheduled on another cpu, die + * there before we look at prev->state, and then the reference would + * be dropped twice. + * Manfred Spraul + */ + prev_state = prev->state; + finish_arch_switch(prev); +#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW + local_irq_disable(); +#endif /* __ARCH_WANT_INTERRUPTS_ON_CTXSW */ + perf_event_task_sched_in(current); +#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW + local_irq_enable(); +#endif /* __ARCH_WANT_INTERRUPTS_ON_CTXSW */ + finish_lock_switch(rq, prev); + + fire_sched_in_preempt_notifiers(current); + if (mm) + mmdrop(mm); + if (unlikely(prev_state == TASK_DEAD)) { + /* + * Remove function-return probe instances associated with this + * task and put them back on the free list. + */ + kprobe_flush_task(prev); + put_task_struct(prev); + } +} + +/** + * schedule_tail - first thing a freshly forked thread must call. + * @prev: the thread we just switched away from. + */ +asmlinkage void schedule_tail(struct task_struct *prev) + __releases(grq.lock) +{ + struct rq *rq = this_rq(); + + finish_task_switch(rq, prev); +#ifdef __ARCH_WANT_UNLOCKED_CTXSW + /* In this case, finish_task_switch does not reenable preemption */ + preempt_enable(); +#endif + if (current->set_child_tid) + put_user(current->pid, current->set_child_tid); +} + +/* + * context_switch - switch to the new MM and the new + * thread's register state. + */ +static inline void +context_switch(struct rq *rq, struct task_struct *prev, + struct task_struct *next) +{ + struct mm_struct *mm, *oldmm; + + prepare_task_switch(rq, prev, next); + trace_sched_switch(prev, next); + mm = next->mm; + oldmm = prev->active_mm; + /* + * For paravirt, this is coupled with an exit in switch_to to + * combine the page table reload and the switch backend into + * one hypercall. + */ + arch_start_context_switch(prev); + + if (!mm) { + next->active_mm = oldmm; + atomic_inc(&oldmm->mm_count); + enter_lazy_tlb(oldmm, next); + } else + switch_mm(oldmm, mm, next); + + if (!prev->mm) { + prev->active_mm = NULL; + rq->prev_mm = oldmm; + } + /* + * Since the runqueue lock will be released by the next + * task (which is an invalid locking op but in the case + * of the scheduler it's an obvious special-case), so we + * do an early lockdep release here: + */ +#ifndef __ARCH_WANT_UNLOCKED_CTXSW + spin_release(&grq.lock.dep_map, 1, _THIS_IP_); +#endif + + /* Here we just switch the register state and the stack. */ + switch_to(prev, next, prev); + + barrier(); + /* + * this_rq must be evaluated again because prev may have moved + * CPUs since it called schedule(), thus the 'rq' on its stack + * frame will be invalid. + */ + finish_task_switch(this_rq(), prev); +} + +/* + * nr_running, nr_uninterruptible and nr_context_switches: + * + * externally visible scheduler statistics: current number of runnable + * threads, current number of uninterruptible-sleeping threads, total + * number of context switches performed since bootup. All are measured + * without grabbing the grq lock but the occasional inaccurate result + * doesn't matter so long as it's positive. + */ +unsigned long nr_running(void) +{ + long nr = grq.nr_running; + + if (unlikely(nr < 0)) + nr = 0; + return (unsigned long)nr; +} + +unsigned long nr_uninterruptible(void) +{ + long nu = grq.nr_uninterruptible; + + if (unlikely(nu < 0)) + nu = 0; + return nu; +} + +unsigned long long nr_context_switches(void) +{ + long long ns = grq.nr_switches; + + /* This is of course impossible */ + if (unlikely(ns < 0)) + ns = 1; + return (unsigned long long)ns; +} + +unsigned long nr_iowait(void) +{ + unsigned long i, sum = 0; + + for_each_possible_cpu(i) + sum += atomic_read(&cpu_rq(i)->nr_iowait); + + return sum; +} + +unsigned long nr_iowait_cpu(int cpu) +{ + struct rq *this = cpu_rq(cpu); + return atomic_read(&this->nr_iowait); +} + +unsigned long nr_active(void) +{ + return nr_running() + nr_uninterruptible(); +} + +/* Beyond a task running on this CPU, load is equal everywhere on BFS */ +unsigned long this_cpu_load(void) +{ + return this_rq()->rq_running + + ((queued_notrunning() + nr_uninterruptible()) / grq.noc); +} + +/* Variables and functions for calc_load */ +static unsigned long calc_load_update; +unsigned long avenrun[3]; +EXPORT_SYMBOL(avenrun); + +/** + * get_avenrun - get the load average array + * @loads: pointer to dest load array + * @offset: offset to add + * @shift: shift count to shift the result left + * + * These values are estimates at best, so no need for locking. + */ +void get_avenrun(unsigned long *loads, unsigned long offset, int shift) +{ + loads[0] = (avenrun[0] + offset) << shift; + loads[1] = (avenrun[1] + offset) << shift; + loads[2] = (avenrun[2] + offset) << shift; +} + +static unsigned long +calc_load(unsigned long load, unsigned long exp, unsigned long active) +{ + load *= exp; + load += active * (FIXED_1 - exp); + return load >> FSHIFT; +} + +/* + * calc_load - update the avenrun load estimates every LOAD_FREQ seconds. + */ +void calc_global_load(unsigned long ticks) +{ + long active; + + if (time_before(jiffies, calc_load_update)) + return; + active = nr_active() * FIXED_1; + + avenrun[0] = calc_load(avenrun[0], EXP_1, active); + avenrun[1] = calc_load(avenrun[1], EXP_5, active); + avenrun[2] = calc_load(avenrun[2], EXP_15, active); + + calc_load_update = jiffies + LOAD_FREQ; +} + +DEFINE_PER_CPU(struct kernel_stat, kstat); + +EXPORT_PER_CPU_SYMBOL(kstat); + +#ifdef CONFIG_IRQ_TIME_ACCOUNTING + +/* + * There are no locks covering percpu hardirq/softirq time. + * They are only modified in account_system_vtime, on corresponding CPU + * with interrupts disabled. So, writes are safe. + * They are read and saved off onto struct rq in update_rq_clock(). + * This may result in other CPU reading this CPU's irq time and can + * race with irq/account_system_vtime on this CPU. We would either get old + * or new value with a side effect of accounting a slice of irq time to wrong + * task when irq is in progress while we read rq->clock. That is a worthy + * compromise in place of having locks on each irq in account_system_time. + */ +static DEFINE_PER_CPU(u64, cpu_hardirq_time); +static DEFINE_PER_CPU(u64, cpu_softirq_time); + +static DEFINE_PER_CPU(u64, irq_start_time); +static int sched_clock_irqtime; + +void enable_sched_clock_irqtime(void) +{ + sched_clock_irqtime = 1; +} + +void disable_sched_clock_irqtime(void) +{ + sched_clock_irqtime = 0; +} + +#ifndef CONFIG_64BIT +static DEFINE_PER_CPU(seqcount_t, irq_time_seq); + +static inline void irq_time_write_begin(void) +{ + __this_cpu_inc(irq_time_seq.sequence); + smp_wmb(); +} + +static inline void irq_time_write_end(void) +{ + smp_wmb(); + __this_cpu_inc(irq_time_seq.sequence); +} + +static inline u64 irq_time_read(int cpu) +{ + u64 irq_time; + unsigned seq; + + do { + seq = read_seqcount_begin(&per_cpu(irq_time_seq, cpu)); + irq_time = per_cpu(cpu_softirq_time, cpu) + + per_cpu(cpu_hardirq_time, cpu); + } while (read_seqcount_retry(&per_cpu(irq_time_seq, cpu), seq)); + + return irq_time; +} +#else /* CONFIG_64BIT */ +static inline void irq_time_write_begin(void) +{ +} + +static inline void irq_time_write_end(void) +{ +} + +static inline u64 irq_time_read(int cpu) +{ + return per_cpu(cpu_softirq_time, cpu) + per_cpu(cpu_hardirq_time, cpu); +} +#endif /* CONFIG_64BIT */ + +/* + * Called before incrementing preempt_count on {soft,}irq_enter + * and before decrementing preempt_count on {soft,}irq_exit. + */ +void account_system_vtime(struct task_struct *curr) +{ + unsigned long flags; + s64 delta; + int cpu; + + if (!sched_clock_irqtime) + return; + + local_irq_save(flags); + + cpu = smp_processor_id(); + delta = sched_clock_cpu(cpu) - __this_cpu_read(irq_start_time); + __this_cpu_add(irq_start_time, delta); + + irq_time_write_begin(); + /* + * We do not account for softirq time from ksoftirqd here. + * We want to continue accounting softirq time to ksoftirqd thread + * in that case, so as not to confuse scheduler with a special task + * that do not consume any time, but still wants to run. + */ + if (hardirq_count()) + __this_cpu_add(cpu_hardirq_time, delta); + else if (in_serving_softirq() && !(curr->flags & PF_KSOFTIRQD)) + __this_cpu_add(cpu_softirq_time, delta); + + irq_time_write_end(); + local_irq_restore(flags); +} +EXPORT_SYMBOL_GPL(account_system_vtime); + +static void update_rq_clock_task(struct rq *rq, s64 delta) +{ + s64 irq_delta; + + irq_delta = irq_time_read(cpu_of(rq)) - rq->prev_irq_time; + + /* + * Since irq_time is only updated on {soft,}irq_exit, we might run into + * this case when a previous update_rq_clock() happened inside a + * {soft,}irq region. + * + * When this happens, we stop ->clock_task and only update the + * prev_irq_time stamp to account for the part that fit, so that a next + * update will consume the rest. This ensures ->clock_task is + * monotonic. + * + * It does however cause some slight miss-attribution of {soft,}irq + * time, a more accurate solution would be to update the irq_time using + * the current rq->clock timestamp, except that would require using + * atomic ops. + */ + if (irq_delta > delta) + irq_delta = delta; + + rq->prev_irq_time += irq_delta; + delta -= irq_delta; + rq->clock_task += delta; +} + +#else /* CONFIG_IRQ_TIME_ACCOUNTING */ + +static void update_rq_clock_task(struct rq *rq, s64 delta) +{ + rq->clock_task += delta; +} + +#endif /* CONFIG_IRQ_TIME_ACCOUNTING */ + +/* + * On each tick, see what percentage of that tick was attributed to each + * component and add the percentage to the _pc values. Once a _pc value has + * accumulated one tick's worth, account for that. This means the total + * percentage of load components will always be 100 per tick. + */ +static void pc_idle_time(struct rq *rq, unsigned long pc) +{ + struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat; + cputime64_t tmp = cputime_to_cputime64(cputime_one_jiffy); + + if (atomic_read(&rq->nr_iowait) > 0) { + rq->iowait_pc += pc; + if (rq->iowait_pc >= 100) { + rq->iowait_pc -= 100; + cpustat->iowait = cputime64_add(cpustat->iowait, tmp); + } + } else { + rq->idle_pc += pc; + if (rq->idle_pc >= 100) { + rq->idle_pc -= 100; + cpustat->idle = cputime64_add(cpustat->idle, tmp); + } + } +} + +static void +pc_system_time(struct rq *rq, struct task_struct *p, int hardirq_offset, + unsigned long pc, unsigned long ns) +{ + struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat; + cputime_t one_jiffy_scaled = cputime_to_scaled(cputime_one_jiffy); + cputime64_t tmp = cputime_to_cputime64(cputime_one_jiffy); + + p->stime_pc += pc; + if (p->stime_pc >= 100) { + p->stime_pc -= 100; + p->stime = cputime_add(p->stime, cputime_one_jiffy); + p->stimescaled = cputime_add(p->stimescaled, one_jiffy_scaled); + account_group_system_time(p, cputime_one_jiffy); + acct_update_integrals(p); + } + p->sched_time += ns; + + if (hardirq_count() - hardirq_offset) { + rq->irq_pc += pc; + if (rq->irq_pc >= 100) { + rq->irq_pc -= 100; + cpustat->irq = cputime64_add(cpustat->irq, tmp); + } + } else if (in_serving_softirq()) { + rq->softirq_pc += pc; + if (rq->softirq_pc >= 100) { + rq->softirq_pc -= 100; + cpustat->softirq = cputime64_add(cpustat->softirq, tmp); + } + } else { + rq->system_pc += pc; + if (rq->system_pc >= 100) { + rq->system_pc -= 100; + cpustat->system = cputime64_add(cpustat->system, tmp); + } + } +} + +static void pc_user_time(struct rq *rq, struct task_struct *p, + unsigned long pc, unsigned long ns) +{ + struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat; + cputime_t one_jiffy_scaled = cputime_to_scaled(cputime_one_jiffy); + cputime64_t tmp = cputime_to_cputime64(cputime_one_jiffy); + + p->utime_pc += pc; + if (p->utime_pc >= 100) { + p->utime_pc -= 100; + p->utime = cputime_add(p->utime, cputime_one_jiffy); + p->utimescaled = cputime_add(p->utimescaled, one_jiffy_scaled); + account_group_user_time(p, cputime_one_jiffy); + acct_update_integrals(p); + } + p->sched_time += ns; + + if (TASK_NICE(p) > 0 || idleprio_task(p)) { + rq->nice_pc += pc; + if (rq->nice_pc >= 100) { + rq->nice_pc %= 100; + cpustat->nice = cputime64_add(cpustat->nice, tmp); + } + } else { + rq->user_pc += pc; + if (rq->user_pc >= 100) { + rq->user_pc %= 100; + cpustat->user = cputime64_add(cpustat->user, tmp); + } + } +} + +/* Convert nanoseconds to percentage of one tick. */ +#define NS_TO_PC(NS) (NS * 100 / JIFFY_NS) + +/* + * This is called on clock ticks and on context switches. + * Bank in p->sched_time the ns elapsed since the last tick or switch. + * CPU scheduler quota accounting is also performed here in microseconds. + */ +static void +update_cpu_clock(struct rq *rq, struct task_struct *p, int tick) +{ + long account_ns = rq->clock - rq->timekeep_clock; + struct task_struct *idle = rq->idle; + unsigned long account_pc; + + if (unlikely(account_ns < 0)) + account_ns = 0; + + account_pc = NS_TO_PC(account_ns); + + if (tick) { + int user_tick = user_mode(get_irq_regs()); + + /* Accurate tick timekeeping */ + rq->account_pc += account_pc - 100; + if (rq->account_pc < 0) { + /* + * Small errors in micro accounting may not make the + * accounting add up to 100% each tick so we keep track + * of the percentage and round it up when less than 100 + */ + account_pc += -rq->account_pc; + rq->account_pc = 0; + } + if (user_tick) + pc_user_time(rq, p, account_pc, account_ns); + else if (p != idle || (irq_count() != HARDIRQ_OFFSET)) + pc_system_time(rq, p, HARDIRQ_OFFSET, + account_pc, account_ns); + else + pc_idle_time(rq, account_pc); + } else { + /* Accurate subtick timekeeping */ + rq->account_pc += account_pc; + if (p == idle) + pc_idle_time(rq, account_pc); + else + pc_user_time(rq, p, account_pc, account_ns); + } + + /* time_slice accounting is done in usecs to avoid overflow on 32bit */ + if (rq->rq_policy != SCHED_FIFO && p != idle) { + s64 time_diff = rq->clock - rq->rq_last_ran; + + niffy_diff(&time_diff, 1); + rq->rq_time_slice -= NS_TO_US(time_diff); + } + rq->rq_last_ran = rq->timekeep_clock = rq->clock; +} + +/* + * Return any ns on the sched_clock that have not yet been accounted in + * @p in case that task is currently running. + * + * Called with task_grq_lock() held. + */ +static u64 do_task_delta_exec(struct task_struct *p, struct rq *rq) +{ + u64 ns = 0; + + if (p == rq->curr) { + update_clocks(rq); + ns = rq->clock_task - rq->rq_last_ran; + if (unlikely((s64)ns < 0)) + ns = 0; + } + + return ns; +} + +unsigned long long task_delta_exec(struct task_struct *p) +{ + unsigned long flags; + struct rq *rq; + u64 ns; + + rq = task_grq_lock(p, &flags); + ns = do_task_delta_exec(p, rq); + task_grq_unlock(&flags); + + return ns; +} + +/* + * Return accounted runtime for the task. + * In case the task is currently running, return the runtime plus current's + * pending runtime that have not been accounted yet. + */ +unsigned long long task_sched_runtime(struct task_struct *p) +{ + unsigned long flags; + struct rq *rq; + u64 ns; + + rq = task_grq_lock(p, &flags); + ns = p->sched_time + do_task_delta_exec(p, rq); + task_grq_unlock(&flags); + + return ns; +} + +/* + * Return sum_exec_runtime for the thread group. + * In case the task is currently running, return the sum plus current's + * pending runtime that have not been accounted yet. + * + * Note that the thread group might have other running tasks as well, + * so the return value not includes other pending runtime that other + * running tasks might have. + */ +unsigned long long thread_group_sched_runtime(struct task_struct *p) +{ + struct task_cputime totals; + unsigned long flags; + struct rq *rq; + u64 ns; + + rq = task_grq_lock(p, &flags); + thread_group_cputime(p, &totals); + ns = totals.sum_exec_runtime + do_task_delta_exec(p, rq); + task_grq_unlock(&flags); + + return ns; +} + +/* Compatibility crap for removal */ +void account_user_time(struct task_struct *p, cputime_t cputime, + cputime_t cputime_scaled) +{ +} + +void account_idle_time(cputime_t cputime) +{ +} + +/* + * Account guest cpu time to a process. + * @p: the process that the cpu time gets accounted to + * @cputime: the cpu time spent in virtual machine since the last update + * @cputime_scaled: cputime scaled by cpu frequency + */ +static void account_guest_time(struct task_struct *p, cputime_t cputime, + cputime_t cputime_scaled) +{ + cputime64_t tmp; + struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat; + + tmp = cputime_to_cputime64(cputime); + + /* Add guest time to process. */ + p->utime = cputime_add(p->utime, cputime); + p->utimescaled = cputime_add(p->utimescaled, cputime_scaled); + account_group_user_time(p, cputime); + p->gtime = cputime_add(p->gtime, cputime); + + /* Add guest time to cpustat. */ + if (TASK_NICE(p) > 0) { + cpustat->nice = cputime64_add(cpustat->nice, tmp); + cpustat->guest_nice = cputime64_add(cpustat->guest_nice, tmp); + } else { + cpustat->user = cputime64_add(cpustat->user, tmp); + cpustat->guest = cputime64_add(cpustat->guest, tmp); + } +} + +/* + * Account system cpu time to a process. + * @p: the process that the cpu time gets accounted to + * @hardirq_offset: the offset to subtract from hardirq_count() + * @cputime: the cpu time spent in kernel space since the last update + * @cputime_scaled: cputime scaled by cpu frequency + * This is for guest only now. + */ +void account_system_time(struct task_struct *p, int hardirq_offset, + cputime_t cputime, cputime_t cputime_scaled) +{ + + if ((p->flags & PF_VCPU) && (irq_count() - hardirq_offset == 0)) + account_guest_time(p, cputime, cputime_scaled); +} + +/* + * Account for involuntary wait time. + * @steal: the cpu time spent in involuntary wait + */ +void account_steal_time(cputime_t cputime) +{ + struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat; + cputime64_t cputime64 = cputime_to_cputime64(cputime); + + cpustat->steal = cputime64_add(cpustat->steal, cputime64); +} + +/* + * Account for idle time. + * @cputime: the cpu time spent in idle wait + */ +static void account_idle_times(cputime_t cputime) +{ + struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat; + cputime64_t cputime64 = cputime_to_cputime64(cputime); + struct rq *rq = this_rq(); + + if (atomic_read(&rq->nr_iowait) > 0) + cpustat->iowait = cputime64_add(cpustat->iowait, cputime64); + else + cpustat->idle = cputime64_add(cpustat->idle, cputime64); +} + +#ifndef CONFIG_VIRT_CPU_ACCOUNTING + +void account_process_tick(struct task_struct *p, int user_tick) +{ +} + +/* + * Account multiple ticks of steal time. + * @p: the process from which the cpu time has been stolen + * @ticks: number of stolen ticks + */ +void account_steal_ticks(unsigned long ticks) +{ + account_steal_time(jiffies_to_cputime(ticks)); +} + +/* + * Account multiple ticks of idle time. + * @ticks: number of stolen ticks + */ +void account_idle_ticks(unsigned long ticks) +{ + account_idle_times(jiffies_to_cputime(ticks)); +} +#endif + +static inline void grq_iso_lock(void) + __acquires(grq.iso_lock) +{ + raw_spin_lock(&grq.iso_lock); +} + +static inline void grq_iso_unlock(void) + __releases(grq.iso_lock) +{ + raw_spin_unlock(&grq.iso_lock); +} + +/* + * Functions to test for when SCHED_ISO tasks have used their allocated + * quota as real time scheduling and convert them back to SCHED_NORMAL. + * Where possible, the data is tested lockless, to avoid grabbing iso_lock + * because the occasional inaccurate result won't matter. However the + * tick data is only ever modified under lock. iso_refractory is only simply + * set to 0 or 1 so it's not worth grabbing the lock yet again for that. + */ +static void set_iso_refractory(void) +{ + grq.iso_refractory = 1; +} + +static void clear_iso_refractory(void) +{ + grq.iso_refractory = 0; +} + +/* + * Test if SCHED_ISO tasks have run longer than their alloted period as RT + * tasks and set the refractory flag if necessary. There is 10% hysteresis + * for unsetting the flag. 115/128 is ~90/100 as a fast shift instead of a + * slow division. + */ +static unsigned int test_ret_isorefractory(struct rq *rq) +{ + if (likely(!grq.iso_refractory)) { + if (grq.iso_ticks > ISO_PERIOD * sched_iso_cpu) + set_iso_refractory(); + } else { + if (grq.iso_ticks < ISO_PERIOD * (sched_iso_cpu * 115 / 128)) + clear_iso_refractory(); + } + return grq.iso_refractory; +} + +static void iso_tick(void) +{ + grq_iso_lock(); + grq.iso_ticks += 100; + grq_iso_unlock(); +} + +/* No SCHED_ISO task was running so decrease rq->iso_ticks */ +static inline void no_iso_tick(void) +{ + if (grq.iso_ticks) { + grq_iso_lock(); + grq.iso_ticks -= grq.iso_ticks / ISO_PERIOD + 1; + if (unlikely(grq.iso_refractory && grq.iso_ticks < + ISO_PERIOD * (sched_iso_cpu * 115 / 128))) + clear_iso_refractory(); + grq_iso_unlock(); + } +} + +static int rq_running_iso(struct rq *rq) +{ + return rq->rq_prio == ISO_PRIO; +} + +/* This manages tasks that have run out of timeslice during a scheduler_tick */ +static void task_running_tick(struct rq *rq) +{ + struct task_struct *p; + + /* + * If a SCHED_ISO task is running we increment the iso_ticks. In + * order to prevent SCHED_ISO tasks from causing starvation in the + * presence of true RT tasks we account those as iso_ticks as well. + */ + if ((rt_queue(rq) || (iso_queue(rq) && !grq.iso_refractory))) { + if (grq.iso_ticks <= (ISO_PERIOD * 100) - 100) + iso_tick(); + } else + no_iso_tick(); + + if (iso_queue(rq)) { + if (unlikely(test_ret_isorefractory(rq))) { + if (rq_running_iso(rq)) { + /* + * SCHED_ISO task is running as RT and limit + * has been hit. Force it to reschedule as + * SCHED_NORMAL by zeroing its time_slice + */ + rq->rq_time_slice = 0; + } + } + } + + /* SCHED_FIFO tasks never run out of timeslice. */ + if (rq->rq_policy == SCHED_FIFO) + return; + /* + * Tasks that were scheduled in the first half of a tick are not + * allowed to run into the 2nd half of the next tick if they will + * run out of time slice in the interim. Otherwise, if they have + * less than RESCHED_US μs of time slice left they will be rescheduled. + */ + if (rq->dither) { + if (rq->rq_time_slice > HALF_JIFFY_US) + return; + else + rq->rq_time_slice = 0; + } else if (rq->rq_time_slice >= RESCHED_US) + return; + + /* p->time_slice < RESCHED_US. We only modify task_struct under grq lock */ + p = rq->curr; + requeue_task(p); + grq_lock(); + set_tsk_need_resched(p); + grq_unlock(); +} + +void wake_up_idle_cpu(int cpu); + +/* + * This function gets called by the timer code, with HZ frequency. + * We call it with interrupts disabled. The data modified is all + * local to struct rq so we don't need to grab grq lock. + */ +void scheduler_tick(void) +{ + int cpu __maybe_unused = smp_processor_id(); + struct rq *rq = cpu_rq(cpu); + + sched_clock_tick(); + /* grq lock not grabbed, so only update rq clock */ + update_rq_clock(rq); + update_cpu_clock(rq, rq->curr, 1); + if (!rq_idle(rq)) + task_running_tick(rq); + else + no_iso_tick(); + rq->last_tick = rq->clock; + perf_event_task_tick(); +} + +notrace unsigned long get_parent_ip(unsigned long addr) +{ + if (in_lock_functions(addr)) { + addr = CALLER_ADDR2; + if (in_lock_functions(addr)) + addr = CALLER_ADDR3; + } + return addr; +} + +#if defined(CONFIG_PREEMPT) && (defined(CONFIG_DEBUG_PREEMPT) || \ + defined(CONFIG_PREEMPT_TRACER)) +void __kprobes add_preempt_count(int val) +{ +#ifdef CONFIG_DEBUG_PREEMPT + /* + * Underflow? + */ + if (DEBUG_LOCKS_WARN_ON((preempt_count() < 0))) + return; +#endif + preempt_count() += val; +#ifdef CONFIG_DEBUG_PREEMPT + /* + * Spinlock count overflowing soon? + */ + DEBUG_LOCKS_WARN_ON((preempt_count() & PREEMPT_MASK) >= + PREEMPT_MASK - 10); +#endif + if (preempt_count() == val) + trace_preempt_off(CALLER_ADDR0, get_parent_ip(CALLER_ADDR1)); +} +EXPORT_SYMBOL(add_preempt_count); + +void __kprobes sub_preempt_count(int val) +{ +#ifdef CONFIG_DEBUG_PREEMPT + /* + * Underflow? + */ + if (DEBUG_LOCKS_WARN_ON(val > preempt_count())) + return; + /* + * Is the spinlock portion underflowing? + */ + if (DEBUG_LOCKS_WARN_ON((val < PREEMPT_MASK) && + !(preempt_count() & PREEMPT_MASK))) + return; +#endif + + if (preempt_count() == val) + trace_preempt_on(CALLER_ADDR0, get_parent_ip(CALLER_ADDR1)); + preempt_count() -= val; +} +EXPORT_SYMBOL(sub_preempt_count); +#endif + +/* + * Deadline is "now" in niffies + (offset by priority). Setting the deadline + * is the key to everything. It distributes cpu fairly amongst tasks of the + * same nice value, it proportions cpu according to nice level, it means the + * task that last woke up the longest ago has the earliest deadline, thus + * ensuring that interactive tasks get low latency on wake up. The CPU + * proportion works out to the square of the virtual deadline difference, so + * this equation will give nice 19 3% CPU compared to nice 0. + */ +static inline u64 prio_deadline_diff(int user_prio) +{ + return (prio_ratios[user_prio] * rr_interval * (MS_TO_NS(1) / 128)); +} + +static inline u64 task_deadline_diff(struct task_struct *p) +{ + return prio_deadline_diff(TASK_USER_PRIO(p)); +} + +static inline u64 static_deadline_diff(int static_prio) +{ + return prio_deadline_diff(USER_PRIO(static_prio)); +} + +static inline int longest_deadline_diff(void) +{ + return prio_deadline_diff(39); +} + +static inline int ms_longest_deadline_diff(void) +{ + return NS_TO_MS(longest_deadline_diff()); +} + +/* + * The time_slice is only refilled when it is empty and that is when we set a + * new deadline. + */ +static void time_slice_expired(struct task_struct *p) +{ + p->time_slice = timeslice(); + p->deadline = grq.niffies + task_deadline_diff(p); +} + +/* + * Timeslices below RESCHED_US are considered as good as expired as there's no + * point rescheduling when there's so little time left. SCHED_BATCH tasks + * have been flagged be not latency sensitive and likely to be fully CPU + * bound so every time they're rescheduled they have their time_slice + * refilled, but get a new later deadline to have little effect on + * SCHED_NORMAL tasks. + + */ +static inline void check_deadline(struct task_struct *p) +{ + if (p->time_slice < RESCHED_US || batch_task(p)) + time_slice_expired(p); +} + +/* + * O(n) lookup of all tasks in the global runqueue. The real brainfuck + * of lock contention and O(n). It's not really O(n) as only the queued, + * but not running tasks are scanned, and is O(n) queued in the worst case + * scenario only because the right task can be found before scanning all of + * them. + * Tasks are selected in this order: + * Real time tasks are selected purely by their static priority and in the + * order they were queued, so the lowest value idx, and the first queued task + * of that priority value is chosen. + * If no real time tasks are found, the SCHED_ISO priority is checked, and + * all SCHED_ISO tasks have the same priority value, so they're selected by + * the earliest deadline value. + * If no SCHED_ISO tasks are found, SCHED_NORMAL tasks are selected by the + * earliest deadline. + * Finally if no SCHED_NORMAL tasks are found, SCHED_IDLEPRIO tasks are + * selected by the earliest deadline. + */ +static inline struct +task_struct *earliest_deadline_task(struct rq *rq, struct task_struct *idle) +{ + u64 dl, earliest_deadline = 0; /* Initialise to silence compiler */ + struct task_struct *p, *edt = idle; + unsigned int cpu = cpu_of(rq); + struct list_head *queue; + int idx = 0; + +retry: + idx = find_next_bit(grq.prio_bitmap, PRIO_LIMIT, idx); + if (idx >= PRIO_LIMIT) + goto out; + queue = grq.queue + idx; + + if (idx < MAX_RT_PRIO) { + /* We found an rt task */ + list_for_each_entry(p, queue, run_list) { + /* Make sure cpu affinity is ok */ + if (needs_other_cpu(p, cpu)) + continue; + edt = p; + goto out_take; + } + /* None of the RT tasks at this priority can run on this cpu */ + ++idx; + goto retry; + } + + list_for_each_entry(p, queue, run_list) { + /* Make sure cpu affinity is ok */ + if (needs_other_cpu(p, cpu)) + continue; + + /* + * Soft affinity happens here by not scheduling a task with + * its sticky flag set that ran on a different CPU last when + * the CPU is scaling, or by greatly biasing against its + * deadline when not. + */ + if (task_rq(p) != rq && task_sticky(p)) { + if (scaling_rq(rq)) + continue; + else + dl = p->deadline + longest_deadline_diff(); + } else + dl = p->deadline; + + /* + * No rt tasks. Find the earliest deadline task. Now we're in + * O(n) territory. This is what we silenced the compiler for: + * edt will always start as idle. + */ + if (edt == idle || + deadline_before(dl, earliest_deadline)) { + earliest_deadline = dl; + edt = p; + } + } + if (edt == idle) { + if (++idx < PRIO_LIMIT) + goto retry; + goto out; + } +out_take: + take_task(rq, edt); +out: + return edt; +} + +/* + * Print scheduling while atomic bug: + */ +static noinline void __schedule_bug(struct task_struct *prev) +{ + struct pt_regs *regs = get_irq_regs(); + + printk(KERN_ERR "BUG: scheduling while atomic: %s/%d/0x%08x\n", + prev->comm, prev->pid, preempt_count()); + + debug_show_held_locks(prev); + print_modules(); + if (irqs_disabled()) + print_irqtrace_events(prev); + + if (regs) + show_regs(regs); + else + dump_stack(); +} + +/* + * Various schedule()-time debugging checks and statistics: + */ +static inline void schedule_debug(struct task_struct *prev) +{ + /* + * Test if we are atomic. Since do_exit() needs to call into + * schedule() atomically, we ignore that path for now. + * Otherwise, whine if we are scheduling when we should not be. + */ + if (unlikely(in_atomic_preempt_off() && !prev->exit_state)) + __schedule_bug(prev); + + profile_hit(SCHED_PROFILING, __builtin_return_address(0)); + + schedstat_inc(this_rq(), sched_count); +#ifdef CONFIG_SCHEDSTATS + if (unlikely(prev->lock_depth >= 0)) { + schedstat_inc(this_rq(), rq_sched_info.bkl_count); + schedstat_inc(prev, sched_info.bkl_count); + } +#endif +} + +/* + * The currently running task's information is all stored in rq local data + * which is only modified by the local CPU, thereby allowing the data to be + * changed without grabbing the grq lock. + */ +static inline void set_rq_task(struct rq *rq, struct task_struct *p) +{ + rq->rq_time_slice = p->time_slice; + rq->rq_deadline = p->deadline; + rq->rq_last_ran = p->last_ran = rq->clock; + rq->rq_policy = p->policy; + rq->rq_prio = p->prio; + if (p != rq->idle) + rq->rq_running = 1; + else + rq->rq_running = 0; +} + +static void reset_rq_task(struct rq *rq, struct task_struct *p) +{ + rq->rq_policy = p->policy; + rq->rq_prio = p->prio; +} + +/* + * schedule() is the main scheduler function. + */ +asmlinkage void __sched schedule(void) +{ + struct task_struct *prev, *next, *idle; + unsigned long *switch_count; + int deactivate, cpu; + struct rq *rq; + +need_resched: + preempt_disable(); + + cpu = smp_processor_id(); + rq = cpu_rq(cpu); + idle = rq->idle; + rcu_note_context_switch(cpu); + prev = rq->curr; + + release_kernel_lock(prev); +need_resched_nonpreemptible: + + deactivate = 0; + schedule_debug(prev); + + grq_lock_irq(); + update_clocks(rq); + update_cpu_clock(rq, prev, 0); + if (rq->clock - rq->last_tick > HALF_JIFFY_NS) + rq->dither = 0; + else + rq->dither = 1; + + clear_tsk_need_resched(prev); + + switch_count = &prev->nivcsw; + if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) { + if (unlikely(signal_pending_state(prev->state, prev))) { + prev->state = TASK_RUNNING; + } else { + deactivate = 1; + /* + * If a worker is going to sleep, notify and + * ask workqueue whether it wants to wake up a + * task to maintain concurrency. If so, wake + * up the task. + */ + if (prev->flags & PF_WQ_WORKER) { + struct task_struct *to_wakeup; + + to_wakeup = wq_worker_sleeping(prev, cpu); + if (to_wakeup) { + /* This shouldn't happen, but does */ + if (unlikely(to_wakeup == prev)) + deactivate = 0; + else + try_to_wake_up_local(to_wakeup); + } + } + } + switch_count = &prev->nvcsw; + } + + if (prev != idle) { + /* Update all the information stored on struct rq */ + prev->time_slice = rq->rq_time_slice; + prev->deadline = rq->rq_deadline; + check_deadline(prev); + prev->last_ran = rq->clock; + + /* Task changed affinity off this CPU */ + if (needs_other_cpu(prev, cpu)) + resched_suitable_idle(prev); + else if (!deactivate) { + if (!queued_notrunning()) { + /* + * We now know prev is the only thing that is + * awaiting CPU so we can bypass rechecking for + * the earliest deadline task and just run it + * again. + */ + grq_unlock_irq(); + goto rerun_prev_unlocked; + } else + swap_sticky(rq, cpu, prev); + } + return_task(prev, deactivate); + } + + if (unlikely(!queued_notrunning())) { + /* + * This CPU is now truly idle as opposed to when idle is + * scheduled as a high priority task in its own right. + */ + next = idle; + schedstat_inc(rq, sched_goidle); + set_cpuidle_map(cpu); + } else { + next = earliest_deadline_task(rq, idle); + if (likely(next->prio != PRIO_LIMIT)) { + prefetch(next); + prefetch_stack(next); + clear_cpuidle_map(cpu); + } else + set_cpuidle_map(cpu); + } + + if (likely(prev != next)) { + /* + * Don't stick tasks when a real time task is going to run as + * they may literally get stuck. + */ + if (rt_task(next)) + unstick_task(rq, prev); + sched_info_switch(prev, next); + perf_event_task_sched_out(prev, next); + + set_rq_task(rq, next); + grq.nr_switches++; + prev->oncpu = 0; + next->oncpu = 1; + rq->curr = next; + ++*switch_count; + + context_switch(rq, prev, next); /* unlocks the grq */ + /* + * The context switch have flipped the stack from under us + * and restored the local variables which were saved when + * this task called schedule() in the past. prev == current + * is still correct, but it can be moved to another cpu/rq. + */ + cpu = smp_processor_id(); + rq = cpu_rq(cpu); + idle = rq->idle; + } else + grq_unlock_irq(); + +rerun_prev_unlocked: + if (unlikely(reacquire_kernel_lock(prev))) + goto need_resched_nonpreemptible; + + preempt_enable_no_resched(); + if (need_resched()) + goto need_resched; +} +EXPORT_SYMBOL(schedule); + +#ifdef CONFIG_SMP +int mutex_spin_on_owner(struct mutex *lock, struct thread_info *owner) +{ + unsigned int cpu; + struct rq *rq; + +#ifdef CONFIG_DEBUG_PAGEALLOC + /* + * Need to access the cpu field knowing that + * DEBUG_PAGEALLOC could have unmapped it if + * the mutex owner just released it and exited. + */ + if (probe_kernel_address(&owner->cpu, cpu)) + return 0; +#else + cpu = owner->cpu; +#endif + + /* + * Even if the access succeeded (likely case), + * the cpu field may no longer be valid. + */ + if (cpu >= nr_cpumask_bits) + return 0; + + /* + * We need to validate that we can do a + * get_cpu() and that we have the percpu area. + */ + if (!cpu_online(cpu)) + return 0; + + rq = cpu_rq(cpu); + + for (;;) { + /* + * Owner changed, break to re-assess state. + */ + if (lock->owner != owner) + break; + + /* + * Is that owner really running on that cpu? + */ + if (task_thread_info(rq->curr) != owner || need_resched()) + return 0; + + arch_mutex_cpu_relax(); + } + + return 1; +} +#endif + +#ifdef CONFIG_PREEMPT +/* + * this is the entry point to schedule() from in-kernel preemption + * off of preempt_enable. Kernel preemptions off return from interrupt + * occur there and call schedule directly. + */ +asmlinkage void __sched notrace preempt_schedule(void) +{ + struct thread_info *ti = current_thread_info(); + + /* + * If there is a non-zero preempt_count or interrupts are disabled, + * we do not want to preempt the current task. Just return.. + */ + if (likely(ti->preempt_count || irqs_disabled())) + return; + + do { + add_preempt_count_notrace(PREEMPT_ACTIVE); + schedule(); + sub_preempt_count_notrace(PREEMPT_ACTIVE); + + /* + * Check again in case we missed a preemption opportunity + * between schedule and now. + */ + barrier(); + } while (need_resched()); +} +EXPORT_SYMBOL(preempt_schedule); + +/* + * this is the entry point to schedule() from kernel preemption + * off of irq context. + * Note, that this is called and return with irqs disabled. This will + * protect us against recursive calling from irq. + */ +asmlinkage void __sched preempt_schedule_irq(void) +{ + struct thread_info *ti = current_thread_info(); + + /* Catch callers which need to be fixed */ + BUG_ON(ti->preempt_count || !irqs_disabled()); + + do { + add_preempt_count(PREEMPT_ACTIVE); + local_irq_enable(); + schedule(); + local_irq_disable(); + sub_preempt_count(PREEMPT_ACTIVE); + + /* + * Check again in case we missed a preemption opportunity + * between schedule and now. + */ + barrier(); + } while (need_resched()); +} + +#endif /* CONFIG_PREEMPT */ + +int default_wake_function(wait_queue_t *curr, unsigned mode, int wake_flags, + void *key) +{ + return try_to_wake_up(curr->private, mode, wake_flags); +} +EXPORT_SYMBOL(default_wake_function); + +/* + * The core wakeup function. Non-exclusive wakeups (nr_exclusive == 0) just + * wake everything up. If it's an exclusive wakeup (nr_exclusive == small +ve + * number) then we wake all the non-exclusive tasks and one exclusive task. + * + * There are circumstances in which we can try to wake a task which has already + * started to run but is not in state TASK_RUNNING. try_to_wake_up() returns + * zero in this (rare) case, and we handle it by continuing to scan the queue. + */ +static void __wake_up_common(wait_queue_head_t *q, unsigned int mode, + int nr_exclusive, int wake_flags, void *key) +{ + struct list_head *tmp, *next; + + list_for_each_safe(tmp, next, &q->task_list) { + wait_queue_t *curr = list_entry(tmp, wait_queue_t, task_list); + unsigned int flags = curr->flags; + + if (curr->func(curr, mode, wake_flags, key) && + (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive) + break; + } +} + +/** + * __wake_up - wake up threads blocked on a waitqueue. + * @q: the waitqueue + * @mode: which threads + * @nr_exclusive: how many wake-one or wake-many threads to wake up + * @key: is directly passed to the wakeup function + * + * It may be assumed that this function implies a write memory barrier before + * changing the task state if and only if any tasks are woken up. + */ +void __wake_up(wait_queue_head_t *q, unsigned int mode, + int nr_exclusive, void *key) +{ + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + __wake_up_common(q, mode, nr_exclusive, 0, key); + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL(__wake_up); + +/* + * Same as __wake_up but called with the spinlock in wait_queue_head_t held. + */ +void __wake_up_locked(wait_queue_head_t *q, unsigned int mode) +{ + __wake_up_common(q, mode, 1, 0, NULL); +} +EXPORT_SYMBOL_GPL(__wake_up_locked); + +void __wake_up_locked_key(wait_queue_head_t *q, unsigned int mode, void *key) +{ + __wake_up_common(q, mode, 1, 0, key); +} +EXPORT_SYMBOL_GPL(__wake_up_locked_key); + +/** + * __wake_up_sync_key - wake up threads blocked on a waitqueue. + * @q: the waitqueue + * @mode: which threads + * @nr_exclusive: how many wake-one or wake-many threads to wake up + * @key: opaque value to be passed to wakeup targets + * + * The sync wakeup differs that the waker knows that it will schedule + * away soon, so while the target thread will be woken up, it will not + * be migrated to another CPU - ie. the two threads are 'synchronised' + * with each other. This can prevent needless bouncing between CPUs. + * + * On UP it can prevent extra preemption. + * + * It may be assumed that this function implies a write memory barrier before + * changing the task state if and only if any tasks are woken up. + */ +void __wake_up_sync_key(wait_queue_head_t *q, unsigned int mode, + int nr_exclusive, void *key) +{ + unsigned long flags; + int wake_flags = WF_SYNC; + + if (unlikely(!q)) + return; + + if (unlikely(!nr_exclusive)) + wake_flags = 0; + + spin_lock_irqsave(&q->lock, flags); + __wake_up_common(q, mode, nr_exclusive, wake_flags, key); + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL_GPL(__wake_up_sync_key); + +/** + * __wake_up_sync - wake up threads blocked on a waitqueue. + * @q: the waitqueue + * @mode: which threads + * @nr_exclusive: how many wake-one or wake-many threads to wake up + * + * The sync wakeup differs that the waker knows that it will schedule + * away soon, so while the target thread will be woken up, it will not + * be migrated to another CPU - ie. the two threads are 'synchronised' + * with each other. This can prevent needless bouncing between CPUs. + * + * On UP it can prevent extra preemption. + */ +void __wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr_exclusive) +{ + unsigned long flags; + int sync = 1; + + if (unlikely(!q)) + return; + + if (unlikely(!nr_exclusive)) + sync = 0; + + spin_lock_irqsave(&q->lock, flags); + __wake_up_common(q, mode, nr_exclusive, sync, NULL); + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL_GPL(__wake_up_sync); /* For internal use only */ + +/** + * complete: - signals a single thread waiting on this completion + * @x: holds the state of this particular completion + * + * This will wake up a single thread waiting on this completion. Threads will be + * awakened in the same order in which they were queued. + * + * See also complete_all(), wait_for_completion() and related routines. + * + * It may be assumed that this function implies a write memory barrier before + * changing the task state if and only if any tasks are woken up. + */ +void complete(struct completion *x) +{ + unsigned long flags; + + spin_lock_irqsave(&x->wait.lock, flags); + x->done++; + __wake_up_common(&x->wait, TASK_NORMAL, 1, 0, NULL); + spin_unlock_irqrestore(&x->wait.lock, flags); +} +EXPORT_SYMBOL(complete); + +/** + * complete_all: - signals all threads waiting on this completion + * @x: holds the state of this particular completion + * + * This will wake up all threads waiting on this particular completion event. + * + * It may be assumed that this function implies a write memory barrier before + * changing the task state if and only if any tasks are woken up. + */ +void complete_all(struct completion *x) +{ + unsigned long flags; + + spin_lock_irqsave(&x->wait.lock, flags); + x->done += UINT_MAX/2; + __wake_up_common(&x->wait, TASK_NORMAL, 0, 0, NULL); + spin_unlock_irqrestore(&x->wait.lock, flags); +} +EXPORT_SYMBOL(complete_all); + +static inline long __sched +do_wait_for_common(struct completion *x, long timeout, int state) +{ + if (!x->done) { + DECLARE_WAITQUEUE(wait, current); + + __add_wait_queue_tail_exclusive(&x->wait, &wait); + do { + if (signal_pending_state(state, current)) { + timeout = -ERESTARTSYS; + break; + } + __set_current_state(state); + spin_unlock_irq(&x->wait.lock); + timeout = schedule_timeout(timeout); + spin_lock_irq(&x->wait.lock); + } while (!x->done && timeout); + __remove_wait_queue(&x->wait, &wait); + if (!x->done) + return timeout; + } + x->done--; + return timeout ?: 1; +} + +static long __sched +wait_for_common(struct completion *x, long timeout, int state) +{ + might_sleep(); + + spin_lock_irq(&x->wait.lock); + timeout = do_wait_for_common(x, timeout, state); + spin_unlock_irq(&x->wait.lock); + return timeout; +} + +/** + * wait_for_completion: - waits for completion of a task + * @x: holds the state of this particular completion + * + * This waits to be signaled for completion of a specific task. It is NOT + * interruptible and there is no timeout. + * + * See also similar routines (i.e. wait_for_completion_timeout()) with timeout + * and interrupt capability. Also see complete(). + */ +void __sched wait_for_completion(struct completion *x) +{ + wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE); +} +EXPORT_SYMBOL(wait_for_completion); + +/** + * wait_for_completion_timeout: - waits for completion of a task (w/timeout) + * @x: holds the state of this particular completion + * @timeout: timeout value in jiffies + * + * This waits for either a completion of a specific task to be signaled or for a + * specified timeout to expire. The timeout is in jiffies. It is not + * interruptible. + */ +unsigned long __sched +wait_for_completion_timeout(struct completion *x, unsigned long timeout) +{ + return wait_for_common(x, timeout, TASK_UNINTERRUPTIBLE); +} +EXPORT_SYMBOL(wait_for_completion_timeout); + +/** + * wait_for_completion_interruptible: - waits for completion of a task (w/intr) + * @x: holds the state of this particular completion + * + * This waits for completion of a specific task to be signaled. It is + * interruptible. + */ +int __sched wait_for_completion_interruptible(struct completion *x) +{ + long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_INTERRUPTIBLE); + if (t == -ERESTARTSYS) + return t; + return 0; +} +EXPORT_SYMBOL(wait_for_completion_interruptible); + +/** + * wait_for_completion_interruptible_timeout: - waits for completion (w/(to,intr)) + * @x: holds the state of this particular completion + * @timeout: timeout value in jiffies + * + * This waits for either a completion of a specific task to be signaled or for a + * specified timeout to expire. It is interruptible. The timeout is in jiffies. + */ +long __sched +wait_for_completion_interruptible_timeout(struct completion *x, + unsigned long timeout) +{ + return wait_for_common(x, timeout, TASK_INTERRUPTIBLE); +} +EXPORT_SYMBOL(wait_for_completion_interruptible_timeout); + +/** + * wait_for_completion_killable: - waits for completion of a task (killable) + * @x: holds the state of this particular completion + * + * This waits to be signaled for completion of a specific task. It can be + * interrupted by a kill signal. + */ +int __sched wait_for_completion_killable(struct completion *x) +{ + long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_KILLABLE); + if (t == -ERESTARTSYS) + return t; + return 0; +} +EXPORT_SYMBOL(wait_for_completion_killable); + +/** + * wait_for_completion_killable_timeout: - waits for completion of a task (w/(to,killable)) + * @x: holds the state of this particular completion + * @timeout: timeout value in jiffies + * + * This waits for either a completion of a specific task to be + * signaled or for a specified timeout to expire. It can be + * interrupted by a kill signal. The timeout is in jiffies. + */ +long __sched +wait_for_completion_killable_timeout(struct completion *x, + unsigned long timeout) +{ + return wait_for_common(x, timeout, TASK_KILLABLE); +} +EXPORT_SYMBOL(wait_for_completion_killable_timeout); + +/** + * try_wait_for_completion - try to decrement a completion without blocking + * @x: completion structure + * + * Returns: 0 if a decrement cannot be done without blocking + * 1 if a decrement succeeded. + * + * If a completion is being used as a counting completion, + * attempt to decrement the counter without blocking. This + * enables us to avoid waiting if the resource the completion + * is protecting is not available. + */ +bool try_wait_for_completion(struct completion *x) +{ + unsigned long flags; + int ret = 1; + + spin_lock_irqsave(&x->wait.lock, flags); + if (!x->done) + ret = 0; + else + x->done--; + spin_unlock_irqrestore(&x->wait.lock, flags); + return ret; +} +EXPORT_SYMBOL(try_wait_for_completion); + +/** + * completion_done - Test to see if a completion has any waiters + * @x: completion structure + * + * Returns: 0 if there are waiters (wait_for_completion() in progress) + * 1 if there are no waiters. + * + */ +bool completion_done(struct completion *x) +{ + unsigned long flags; + int ret = 1; + + spin_lock_irqsave(&x->wait.lock, flags); + if (!x->done) + ret = 0; + spin_unlock_irqrestore(&x->wait.lock, flags); + return ret; +} +EXPORT_SYMBOL(completion_done); + +static long __sched +sleep_on_common(wait_queue_head_t *q, int state, long timeout) +{ + unsigned long flags; + wait_queue_t wait; + + init_waitqueue_entry(&wait, current); + + __set_current_state(state); + + spin_lock_irqsave(&q->lock, flags); + __add_wait_queue(q, &wait); + spin_unlock(&q->lock); + timeout = schedule_timeout(timeout); + spin_lock_irq(&q->lock); + __remove_wait_queue(q, &wait); + spin_unlock_irqrestore(&q->lock, flags); + + return timeout; +} + +void __sched interruptible_sleep_on(wait_queue_head_t *q) +{ + sleep_on_common(q, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); +} +EXPORT_SYMBOL(interruptible_sleep_on); + +long __sched +interruptible_sleep_on_timeout(wait_queue_head_t *q, long timeout) +{ + return sleep_on_common(q, TASK_INTERRUPTIBLE, timeout); +} +EXPORT_SYMBOL(interruptible_sleep_on_timeout); + +void __sched sleep_on(wait_queue_head_t *q) +{ + sleep_on_common(q, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); +} +EXPORT_SYMBOL(sleep_on); + +long __sched sleep_on_timeout(wait_queue_head_t *q, long timeout) +{ + return sleep_on_common(q, TASK_UNINTERRUPTIBLE, timeout); +} +EXPORT_SYMBOL(sleep_on_timeout); + +#ifdef CONFIG_RT_MUTEXES + +/* + * rt_mutex_setprio - set the current priority of a task + * @p: task + * @prio: prio value (kernel-internal form) + * + * This function changes the 'effective' priority of a task. It does + * not touch ->normal_prio like __setscheduler(). + * + * Used by the rt_mutex code to implement priority inheritance logic. + */ +void rt_mutex_setprio(struct task_struct *p, int prio) +{ + unsigned long flags; + int queued, oldprio; + struct rq *rq; + + BUG_ON(prio < 0 || prio > MAX_PRIO); + + rq = task_grq_lock(p, &flags); + + trace_sched_pi_setprio(p, prio); + oldprio = p->prio; + queued = task_queued(p); + if (queued) + dequeue_task(p); + p->prio = prio; + if (task_running(p) && prio > oldprio) + resched_task(p); + if (queued) { + enqueue_task(p); + try_preempt(p, rq); + } + + task_grq_unlock(&flags); +} + +#endif + +/* + * Adjust the deadline for when the priority is to change, before it's + * changed. + */ +static inline void adjust_deadline(struct task_struct *p, int new_prio) +{ + p->deadline += static_deadline_diff(new_prio) - task_deadline_diff(p); +} + +void set_user_nice(struct task_struct *p, long nice) +{ + int queued, new_static, old_static; + unsigned long flags; + struct rq *rq; + + if (TASK_NICE(p) == nice || nice < -20 || nice > 19) + return; + new_static = NICE_TO_PRIO(nice); + /* + * We have to be careful, if called from sys_setpriority(), + * the task might be in the middle of scheduling on another CPU. + */ + rq = time_task_grq_lock(p, &flags); + /* + * The RT priorities are set via sched_setscheduler(), but we still + * allow the 'normal' nice value to be set - but as expected + * it wont have any effect on scheduling until the task is + * not SCHED_NORMAL/SCHED_BATCH: + */ + if (has_rt_policy(p)) { + p->static_prio = new_static; + goto out_unlock; + } + queued = task_queued(p); + if (queued) + dequeue_task(p); + + adjust_deadline(p, new_static); + old_static = p->static_prio; + p->static_prio = new_static; + p->prio = effective_prio(p); + + if (queued) { + enqueue_task(p); + if (new_static < old_static) + try_preempt(p, rq); + } else if (task_running(p)) { + reset_rq_task(rq, p); + if (old_static < new_static) + resched_task(p); + } +out_unlock: + task_grq_unlock(&flags); +} +EXPORT_SYMBOL(set_user_nice); + +/* + * can_nice - check if a task can reduce its nice value + * @p: task + * @nice: nice value + */ +int can_nice(const struct task_struct *p, const int nice) +{ + /* convert nice value [19,-20] to rlimit style value [1,40] */ + int nice_rlim = 20 - nice; + + return (nice_rlim <= task_rlimit(p, RLIMIT_NICE) || + capable(CAP_SYS_NICE)); +} + +#ifdef __ARCH_WANT_SYS_NICE + +/* + * sys_nice - change the priority of the current process. + * @increment: priority increment + * + * sys_setpriority is a more generic, but much slower function that + * does similar things. + */ +SYSCALL_DEFINE1(nice, int, increment) +{ + long nice, retval; + + /* + * Setpriority might change our priority at the same moment. + * We don't have to worry. Conceptually one call occurs first + * and we have a single winner. + */ + if (increment < -40) + increment = -40; + if (increment > 40) + increment = 40; + + nice = TASK_NICE(current) + increment; + if (nice < -20) + nice = -20; + if (nice > 19) + nice = 19; + + if (increment < 0 && !can_nice(current, nice)) + return -EPERM; + + retval = security_task_setnice(current, nice); + if (retval) + return retval; + + set_user_nice(current, nice); + return 0; +} + +#endif + +/** + * task_prio - return the priority value of a given task. + * @p: the task in question. + * + * This is the priority value as seen by users in /proc. + * RT tasks are offset by -100. Normal tasks are centered around 1, value goes + * from 0 (SCHED_ISO) up to 82 (nice +19 SCHED_IDLEPRIO). + */ +int task_prio(const struct task_struct *p) +{ + int delta, prio = p->prio - MAX_RT_PRIO; + + /* rt tasks and iso tasks */ + if (prio <= 0) + goto out; + + /* Convert to ms to avoid overflows */ + delta = NS_TO_MS(p->deadline - grq.niffies); + delta = delta * 40 / ms_longest_deadline_diff(); + if (delta > 0 && delta <= 80) + prio += delta; + if (idleprio_task(p)) + prio += 40; +out: + return prio; +} + +/** + * task_nice - return the nice value of a given task. + * @p: the task in question. + */ +int task_nice(const struct task_struct *p) +{ + return TASK_NICE(p); +} +EXPORT_SYMBOL_GPL(task_nice); + +/** + * idle_cpu - is a given cpu idle currently? + * @cpu: the processor in question. + */ +int idle_cpu(int cpu) +{ + return cpu_curr(cpu) == cpu_rq(cpu)->idle; +} + +/** + * idle_task - return the idle task for a given cpu. + * @cpu: the processor in question. + */ +struct task_struct *idle_task(int cpu) +{ + return cpu_rq(cpu)->idle; +} + +/** + * find_process_by_pid - find a process with a matching PID value. + * @pid: the pid in question. + */ +static inline struct task_struct *find_process_by_pid(pid_t pid) +{ + return pid ? find_task_by_vpid(pid) : current; +} + +/* Actually do priority change: must hold grq lock. */ +static void +__setscheduler(struct task_struct *p, struct rq *rq, int policy, int prio) +{ + int oldrtprio, oldprio; + + BUG_ON(task_queued(p)); + + p->policy = policy; + oldrtprio = p->rt_priority; + p->rt_priority = prio; + p->normal_prio = normal_prio(p); + oldprio = p->prio; + /* we are holding p->pi_lock already */ + p->prio = rt_mutex_getprio(p); + if (task_running(p)) { + reset_rq_task(rq, p); + /* Resched only if we might now be preempted */ + if (p->prio > oldprio || p->rt_priority > oldrtprio) + resched_task(p); + } +} + +/* + * check the target process has a UID that matches the current process's + */ +static bool check_same_owner(struct task_struct *p) +{ + const struct cred *cred = current_cred(), *pcred; + bool match; + + rcu_read_lock(); + pcred = __task_cred(p); + match = (cred->euid == pcred->euid || + cred->euid == pcred->uid); + rcu_read_unlock(); + return match; +} + +static int __sched_setscheduler(struct task_struct *p, int policy, + const struct sched_param *param, bool user) +{ + struct sched_param zero_param = { .sched_priority = 0 }; + int queued, retval, oldpolicy = -1; + unsigned long flags, rlim_rtprio = 0; + int reset_on_fork; + struct rq *rq; + + /* may grab non-irq protected spin_locks */ + BUG_ON(in_interrupt()); + + if (is_rt_policy(policy) && !capable(CAP_SYS_NICE)) { + unsigned long lflags; + + if (!lock_task_sighand(p, &lflags)) + return -ESRCH; + rlim_rtprio = task_rlimit(p, RLIMIT_RTPRIO); + unlock_task_sighand(p, &lflags); + if (rlim_rtprio) + goto recheck; + /* + * If the caller requested an RT policy without having the + * necessary rights, we downgrade the policy to SCHED_ISO. + * We also set the parameter to zero to pass the checks. + */ + policy = SCHED_ISO; + param = &zero_param; + } +recheck: + /* double check policy once rq lock held */ + if (policy < 0) { + reset_on_fork = p->sched_reset_on_fork; + policy = oldpolicy = p->policy; + } else { + reset_on_fork = !!(policy & SCHED_RESET_ON_FORK); + policy &= ~SCHED_RESET_ON_FORK; + + if (!SCHED_RANGE(policy)) + return -EINVAL; + } + + /* + * Valid priorities for SCHED_FIFO and SCHED_RR are + * 1..MAX_USER_RT_PRIO-1, valid priority for SCHED_NORMAL and + * SCHED_BATCH is 0. + */ + if (param->sched_priority < 0 || + (p->mm && param->sched_priority > MAX_USER_RT_PRIO - 1) || + (!p->mm && param->sched_priority > MAX_RT_PRIO - 1)) + return -EINVAL; + if (is_rt_policy(policy) != (param->sched_priority != 0)) + return -EINVAL; + + /* + * Allow unprivileged RT tasks to decrease priority: + */ + if (user && !capable(CAP_SYS_NICE)) { + if (is_rt_policy(policy)) { + unsigned long rlim_rtprio = + task_rlimit(p, RLIMIT_RTPRIO); + + /* can't set/change the rt policy */ + if (policy != p->policy && !rlim_rtprio) + return -EPERM; + + /* can't increase priority */ + if (param->sched_priority > p->rt_priority && + param->sched_priority > rlim_rtprio) + return -EPERM; + } else { + switch (p->policy) { + /* + * Can only downgrade policies but not back to + * SCHED_NORMAL + */ + case SCHED_ISO: + if (policy == SCHED_ISO) + goto out; + if (policy == SCHED_NORMAL) + return -EPERM; + break; + case SCHED_BATCH: + if (policy == SCHED_BATCH) + goto out; + if (policy != SCHED_IDLEPRIO) + return -EPERM; + break; + case SCHED_IDLEPRIO: + if (policy == SCHED_IDLEPRIO) + goto out; + return -EPERM; + default: + break; + } + } + + /* can't change other user's priorities */ + if (!check_same_owner(p)) + return -EPERM; + + /* Normal users shall not reset the sched_reset_on_fork flag */ + if (p->sched_reset_on_fork && !reset_on_fork) + return -EPERM; + } + + if (user) { + retval = security_task_setscheduler(p); + if (retval) + return retval; + } + + /* + * make sure no PI-waiters arrive (or leave) while we are + * changing the priority of the task: + */ + raw_spin_lock_irqsave(&p->pi_lock, flags); + /* + * To be able to change p->policy safely, the apropriate + * runqueue lock must be held. + */ + rq = __task_grq_lock(p); + + /* + * Changing the policy of the stop threads its a very bad idea + */ + if (p == rq->stop) { + __task_grq_unlock(); + raw_spin_unlock_irqrestore(&p->pi_lock, flags); + return -EINVAL; + } + + /* recheck policy now with rq lock held */ + if (unlikely(oldpolicy != -1 && oldpolicy != p->policy)) { + policy = oldpolicy = -1; + __task_grq_unlock(); + raw_spin_unlock_irqrestore(&p->pi_lock, flags); + goto recheck; + } + update_clocks(rq); + p->sched_reset_on_fork = reset_on_fork; + + queued = task_queued(p); + if (queued) + dequeue_task(p); + __setscheduler(p, rq, policy, param->sched_priority); + if (queued) { + enqueue_task(p); + try_preempt(p, rq); + } + __task_grq_unlock(); + raw_spin_unlock_irqrestore(&p->pi_lock, flags); + + rt_mutex_adjust_pi(p); +out: + return 0; +} + +/** + * sched_setscheduler - change the scheduling policy and/or RT priority of a thread. + * @p: the task in question. + * @policy: new policy. + * @param: structure containing the new RT priority. + * + * NOTE that the task may be already dead. + */ +int sched_setscheduler(struct task_struct *p, int policy, + const struct sched_param *param) +{ + return __sched_setscheduler(p, policy, param, true); +} + +EXPORT_SYMBOL_GPL(sched_setscheduler); + +/** + * sched_setscheduler_nocheck - change the scheduling policy and/or RT priority of a thread from kernelspace. + * @p: the task in question. + * @policy: new policy. + * @param: structure containing the new RT priority. + * + * Just like sched_setscheduler, only don't bother checking if the + * current context has permission. For example, this is needed in + * stop_machine(): we create temporary high priority worker threads, + * but our caller might not have that capability. + */ +int sched_setscheduler_nocheck(struct task_struct *p, int policy, + const struct sched_param *param) +{ + return __sched_setscheduler(p, policy, param, false); +} + +static int +do_sched_setscheduler(pid_t pid, int policy, struct sched_param __user *param) +{ + struct sched_param lparam; + struct task_struct *p; + int retval; + + if (!param || pid < 0) + return -EINVAL; + if (copy_from_user(&lparam, param, sizeof(struct sched_param))) + return -EFAULT; + + rcu_read_lock(); + retval = -ESRCH; + p = find_process_by_pid(pid); + if (p != NULL) + retval = sched_setscheduler(p, policy, &lparam); + rcu_read_unlock(); + + return retval; +} + +/** + * sys_sched_setscheduler - set/change the scheduler policy and RT priority + * @pid: the pid in question. + * @policy: new policy. + * @param: structure containing the new RT priority. + */ +asmlinkage long sys_sched_setscheduler(pid_t pid, int policy, + struct sched_param __user *param) +{ + /* negative values for policy are not valid */ + if (policy < 0) + return -EINVAL; + + return do_sched_setscheduler(pid, policy, param); +} + +/** + * sys_sched_setparam - set/change the RT priority of a thread + * @pid: the pid in question. + * @param: structure containing the new RT priority. + */ +SYSCALL_DEFINE2(sched_setparam, pid_t, pid, struct sched_param __user *, param) +{ + return do_sched_setscheduler(pid, -1, param); +} + +/** + * sys_sched_getscheduler - get the policy (scheduling class) of a thread + * @pid: the pid in question. + */ +SYSCALL_DEFINE1(sched_getscheduler, pid_t, pid) +{ + struct task_struct *p; + int retval = -EINVAL; + + if (pid < 0) + goto out_nounlock; + + retval = -ESRCH; + rcu_read_lock(); + p = find_process_by_pid(pid); + if (p) { + retval = security_task_getscheduler(p); + if (!retval) + retval = p->policy; + } + rcu_read_unlock(); + +out_nounlock: + return retval; +} + +/** + * sys_sched_getscheduler - get the RT priority of a thread + * @pid: the pid in question. + * @param: structure containing the RT priority. + */ +SYSCALL_DEFINE2(sched_getparam, pid_t, pid, struct sched_param __user *, param) +{ + struct sched_param lp; + struct task_struct *p; + int retval = -EINVAL; + + if (!param || pid < 0) + goto out_nounlock; + + rcu_read_lock(); + p = find_process_by_pid(pid); + retval = -ESRCH; + if (!p) + goto out_unlock; + + retval = security_task_getscheduler(p); + if (retval) + goto out_unlock; + + lp.sched_priority = p->rt_priority; + rcu_read_unlock(); + + /* + * This one might sleep, we cannot do it with a spinlock held ... + */ + retval = copy_to_user(param, &lp, sizeof(*param)) ? -EFAULT : 0; + +out_nounlock: + return retval; + +out_unlock: + rcu_read_unlock(); + return retval; +} + +long sched_setaffinity(pid_t pid, const struct cpumask *in_mask) +{ + cpumask_var_t cpus_allowed, new_mask; + struct task_struct *p; + int retval; + + get_online_cpus(); + rcu_read_lock(); + + p = find_process_by_pid(pid); + if (!p) { + rcu_read_unlock(); + put_online_cpus(); + return -ESRCH; + } + + /* Prevent p going away */ + get_task_struct(p); + rcu_read_unlock(); + + if (!alloc_cpumask_var(&cpus_allowed, GFP_KERNEL)) { + retval = -ENOMEM; + goto out_put_task; + } + if (!alloc_cpumask_var(&new_mask, GFP_KERNEL)) { + retval = -ENOMEM; + goto out_free_cpus_allowed; + } + retval = -EPERM; + if (!check_same_owner(p) && !capable(CAP_SYS_NICE)) + goto out_unlock; + + retval = security_task_setscheduler(p); + if (retval) + goto out_unlock; + + cpuset_cpus_allowed(p, cpus_allowed); + cpumask_and(new_mask, in_mask, cpus_allowed); +again: + retval = set_cpus_allowed_ptr(p, new_mask); + + if (!retval) { + cpuset_cpus_allowed(p, cpus_allowed); + if (!cpumask_subset(new_mask, cpus_allowed)) { + /* + * We must have raced with a concurrent cpuset + * update. Just reset the cpus_allowed to the + * cpuset's cpus_allowed + */ + cpumask_copy(new_mask, cpus_allowed); + goto again; + } + } +out_unlock: + free_cpumask_var(new_mask); +out_free_cpus_allowed: + free_cpumask_var(cpus_allowed); +out_put_task: + put_task_struct(p); + put_online_cpus(); + return retval; +} + +static int get_user_cpu_mask(unsigned long __user *user_mask_ptr, unsigned len, + cpumask_t *new_mask) +{ + if (len < sizeof(cpumask_t)) { + memset(new_mask, 0, sizeof(cpumask_t)); + } else if (len > sizeof(cpumask_t)) { + len = sizeof(cpumask_t); + } + return copy_from_user(new_mask, user_mask_ptr, len) ? -EFAULT : 0; +} + + +/** + * sys_sched_setaffinity - set the cpu affinity of a process + * @pid: pid of the process + * @len: length in bytes of the bitmask pointed to by user_mask_ptr + * @user_mask_ptr: user-space pointer to the new cpu mask + */ +SYSCALL_DEFINE3(sched_setaffinity, pid_t, pid, unsigned int, len, + unsigned long __user *, user_mask_ptr) +{ + cpumask_var_t new_mask; + int retval; + + if (!alloc_cpumask_var(&new_mask, GFP_KERNEL)) + return -ENOMEM; + + retval = get_user_cpu_mask(user_mask_ptr, len, new_mask); + if (retval == 0) + retval = sched_setaffinity(pid, new_mask); + free_cpumask_var(new_mask); + return retval; +} + +long sched_getaffinity(pid_t pid, cpumask_t *mask) +{ + struct task_struct *p; + unsigned long flags; + int retval; + + get_online_cpus(); + rcu_read_lock(); + + retval = -ESRCH; + p = find_process_by_pid(pid); + if (!p) + goto out_unlock; + + retval = security_task_getscheduler(p); + if (retval) + goto out_unlock; + + grq_lock_irqsave(&flags); + cpumask_and(mask, &p->cpus_allowed, cpu_online_mask); + grq_unlock_irqrestore(&flags); + +out_unlock: + rcu_read_unlock(); + put_online_cpus(); + + return retval; +} + +/** + * sys_sched_getaffinity - get the cpu affinity of a process + * @pid: pid of the process + * @len: length in bytes of the bitmask pointed to by user_mask_ptr + * @user_mask_ptr: user-space pointer to hold the current cpu mask + */ +SYSCALL_DEFINE3(sched_getaffinity, pid_t, pid, unsigned int, len, + unsigned long __user *, user_mask_ptr) +{ + int ret; + cpumask_var_t mask; + + if ((len * BITS_PER_BYTE) < nr_cpu_ids) + return -EINVAL; + if (len & (sizeof(unsigned long)-1)) + return -EINVAL; + + if (!alloc_cpumask_var(&mask, GFP_KERNEL)) + return -ENOMEM; + + ret = sched_getaffinity(pid, mask); + if (ret == 0) { + size_t retlen = min_t(size_t, len, cpumask_size()); + + if (copy_to_user(user_mask_ptr, mask, retlen)) + ret = -EFAULT; + else + ret = retlen; + } + free_cpumask_var(mask); + + return ret; +} + +/** + * sys_sched_yield - yield the current processor to other threads. + * + * This function yields the current CPU to other tasks. It does this by + * scheduling away the current task. If it still has the earliest deadline + * it will be scheduled again as the next task. + */ +SYSCALL_DEFINE0(sched_yield) +{ + struct task_struct *p; + + p = current; + grq_lock_irq(); + schedstat_inc(task_rq(p), yld_count); + requeue_task(p); + + /* + * Since we are going to call schedule() anyway, there's + * no need to preempt or enable interrupts: + */ + __release(grq.lock); + spin_release(&grq.lock.dep_map, 1, _THIS_IP_); + do_raw_spin_unlock(&grq.lock); + preempt_enable_no_resched(); + + schedule(); + + return 0; +} + +static inline int should_resched(void) +{ + return need_resched() && !(preempt_count() & PREEMPT_ACTIVE); +} + +static void __cond_resched(void) +{ + /* NOT a real fix but will make voluntary preempt work. 馬鹿な事 */ + if (unlikely(system_state != SYSTEM_RUNNING)) + return; + + add_preempt_count(PREEMPT_ACTIVE); + schedule(); + sub_preempt_count(PREEMPT_ACTIVE); +} + +int __sched _cond_resched(void) +{ + if (should_resched()) { + __cond_resched(); + return 1; + } + return 0; +} +EXPORT_SYMBOL(_cond_resched); + +/* + * __cond_resched_lock() - if a reschedule is pending, drop the given lock, + * call schedule, and on return reacquire the lock. + * + * This works OK both with and without CONFIG_PREEMPT. We do strange low-level + * operations here to prevent schedule() from being called twice (once via + * spin_unlock(), once by hand). + */ +int __cond_resched_lock(spinlock_t *lock) +{ + int resched = should_resched(); + int ret = 0; + + lockdep_assert_held(lock); + + if (spin_needbreak(lock) || resched) { + spin_unlock(lock); + if (resched) + __cond_resched(); + else + cpu_relax(); + ret = 1; + spin_lock(lock); + } + return ret; +} +EXPORT_SYMBOL(__cond_resched_lock); + +int __sched __cond_resched_softirq(void) +{ + BUG_ON(!in_softirq()); + + if (should_resched()) { + local_bh_enable(); + __cond_resched(); + local_bh_disable(); + return 1; + } + return 0; +} +EXPORT_SYMBOL(__cond_resched_softirq); + +/** + * yield - yield the current processor to other threads. + * + * This is a shortcut for kernel-space yielding - it marks the + * thread runnable and calls sys_sched_yield(). + */ +void __sched yield(void) +{ + set_current_state(TASK_RUNNING); + sys_sched_yield(); +} +EXPORT_SYMBOL(yield); + +/* + * This task is about to go to sleep on IO. Increment rq->nr_iowait so + * that process accounting knows that this is a task in IO wait state. + * + * But don't do that if it is a deliberate, throttling IO wait (this task + * has set its backing_dev_info: the queue against which it should throttle) + */ +void __sched io_schedule(void) +{ + struct rq *rq = raw_rq(); + + delayacct_blkio_start(); + atomic_inc(&rq->nr_iowait); + current->in_iowait = 1; + schedule(); + current->in_iowait = 0; + atomic_dec(&rq->nr_iowait); + delayacct_blkio_end(); +} +EXPORT_SYMBOL(io_schedule); + +long __sched io_schedule_timeout(long timeout) +{ + struct rq *rq = raw_rq(); + long ret; + + delayacct_blkio_start(); + atomic_inc(&rq->nr_iowait); + current->in_iowait = 1; + ret = schedule_timeout(timeout); + current->in_iowait = 0; + atomic_dec(&rq->nr_iowait); + delayacct_blkio_end(); + return ret; +} + +/** + * sys_sched_get_priority_max - return maximum RT priority. + * @policy: scheduling class. + * + * this syscall returns the maximum rt_priority that can be used + * by a given scheduling class. + */ +SYSCALL_DEFINE1(sched_get_priority_max, int, policy) +{ + int ret = -EINVAL; + + switch (policy) { + case SCHED_FIFO: + case SCHED_RR: + ret = MAX_USER_RT_PRIO-1; + break; + case SCHED_NORMAL: + case SCHED_BATCH: + case SCHED_ISO: + case SCHED_IDLEPRIO: + ret = 0; + break; + } + return ret; +} + +/** + * sys_sched_get_priority_min - return minimum RT priority. + * @policy: scheduling class. + * + * this syscall returns the minimum rt_priority that can be used + * by a given scheduling class. + */ +SYSCALL_DEFINE1(sched_get_priority_min, int, policy) +{ + int ret = -EINVAL; + + switch (policy) { + case SCHED_FIFO: + case SCHED_RR: + ret = 1; + break; + case SCHED_NORMAL: + case SCHED_BATCH: + case SCHED_ISO: + case SCHED_IDLEPRIO: + ret = 0; + break; + } + return ret; +} + +/** + * sys_sched_rr_get_interval - return the default timeslice of a process. + * @pid: pid of the process. + * @interval: userspace pointer to the timeslice value. + * + * this syscall writes the default timeslice value of a given process + * into the user-space timespec buffer. A value of '0' means infinity. + */ +SYSCALL_DEFINE2(sched_rr_get_interval, pid_t, pid, + struct timespec __user *, interval) +{ + struct task_struct *p; + unsigned int time_slice; + unsigned long flags; + int retval; + struct timespec t; + + if (pid < 0) + return -EINVAL; + + retval = -ESRCH; + rcu_read_lock(); + p = find_process_by_pid(pid); + if (!p) + goto out_unlock; + + retval = security_task_getscheduler(p); + if (retval) + goto out_unlock; + + grq_lock_irqsave(&flags); + time_slice = p->policy == SCHED_FIFO ? 0 : MS_TO_NS(task_timeslice(p)); + grq_unlock_irqrestore(&flags); + + rcu_read_unlock(); + t = ns_to_timespec(time_slice); + retval = copy_to_user(interval, &t, sizeof(t)) ? -EFAULT : 0; + return retval; + +out_unlock: + rcu_read_unlock(); + return retval; +} + +static const char stat_nam[] = TASK_STATE_TO_CHAR_STR; + +void sched_show_task(struct task_struct *p) +{ + unsigned long free = 0; + unsigned state; + + state = p->state ? __ffs(p->state) + 1 : 0; + printk(KERN_INFO "%-15.15s %c", p->comm, + state < sizeof(stat_nam) - 1 ? stat_nam[state] : '?'); +#if BITS_PER_LONG == 32 + if (state == TASK_RUNNING) + printk(KERN_CONT " running "); + else + printk(KERN_CONT " %08lx ", thread_saved_pc(p)); +#else + if (state == TASK_RUNNING) + printk(KERN_CONT " running task "); + else + printk(KERN_CONT " %016lx ", thread_saved_pc(p)); +#endif +#ifdef CONFIG_DEBUG_STACK_USAGE + free = stack_not_used(p); +#endif + printk(KERN_CONT "%5lu %5d %6d 0x%08lx\n", free, + task_pid_nr(p), task_pid_nr(p->real_parent), + (unsigned long)task_thread_info(p)->flags); + + show_stack(p, NULL); +} + +void show_state_filter(unsigned long state_filter) +{ + struct task_struct *g, *p; + +#if BITS_PER_LONG == 32 + printk(KERN_INFO + " task PC stack pid father\n"); +#else + printk(KERN_INFO + " task PC stack pid father\n"); +#endif + read_lock(&tasklist_lock); + do_each_thread(g, p) { + /* + * reset the NMI-timeout, listing all files on a slow + * console might take alot of time: + */ + touch_nmi_watchdog(); + if (!state_filter || (p->state & state_filter)) + sched_show_task(p); + } while_each_thread(g, p); + + touch_all_softlockup_watchdogs(); + + read_unlock(&tasklist_lock); + /* + * Only show locks if all tasks are dumped: + */ + if (!state_filter) + debug_show_all_locks(); +} + +/** + * init_idle - set up an idle thread for a given CPU + * @idle: task in question + * @cpu: cpu the idle task belongs to + * + * NOTE: this function does not set the idle thread's NEED_RESCHED + * flag, to make booting more robust. + */ +void init_idle(struct task_struct *idle, int cpu) +{ + struct rq *rq = cpu_rq(cpu); + unsigned long flags; + + time_grq_lock(rq, &flags); + idle->last_ran = rq->clock; + idle->state = TASK_RUNNING; + /* Setting prio to illegal value shouldn't matter when never queued */ + idle->prio = PRIO_LIMIT; + set_rq_task(rq, idle); + idle->cpus_allowed = cpumask_of_cpu(cpu); + /* Silence PROVE_RCU */ + rcu_read_lock(); + set_task_cpu(idle, cpu); + rcu_read_unlock(); + rq->curr = rq->idle = idle; + idle->oncpu = 1; + grq_unlock_irqrestore(&flags); + + /* Set the preempt count _outside_ the spinlocks! */ +#if defined(CONFIG_PREEMPT) && !defined(CONFIG_PREEMPT_BKL) + task_thread_info(idle)->preempt_count = (idle->lock_depth >= 0); +#else + task_thread_info(idle)->preempt_count = 0; +#endif + ftrace_graph_init_task(idle); +} + +/* + * In a system that switches off the HZ timer nohz_cpu_mask + * indicates which cpus entered this state. This is used + * in the rcu update to wait only for active cpus. For system + * which do not switch off the HZ timer nohz_cpu_mask should + * always be CPU_BITS_NONE. + */ +cpumask_var_t nohz_cpu_mask; + +#ifdef CONFIG_SMP +#ifdef CONFIG_NO_HZ +void select_nohz_load_balancer(int stop_tick) +{ +} +#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT) +/** + * lowest_flag_domain - Return lowest sched_domain containing flag. + * @cpu: The cpu whose lowest level of sched domain is to + * be returned. + * @flag: The flag to check for the lowest sched_domain + * for the given cpu. + * + * Returns the lowest sched_domain of a cpu which contains the given flag. + */ +static inline struct sched_domain *lowest_flag_domain(int cpu, int flag) +{ + struct sched_domain *sd; + + for_each_domain(cpu, sd) + if (sd && (sd->flags & flag)) + break; + + return sd; +} + +/** + * for_each_flag_domain - Iterates over sched_domains containing the flag. + * @cpu: The cpu whose domains we're iterating over. + * @sd: variable holding the value of the power_savings_sd + * for cpu. + * @flag: The flag to filter the sched_domains to be iterated. + * + * Iterates over all the scheduler domains for a given cpu that has the 'flag' + * set, starting from the lowest sched_domain to the highest. + */ +#define for_each_flag_domain(cpu, sd, flag) \ + for (sd = lowest_flag_domain(cpu, flag); \ + (sd && (sd->flags & flag)); sd = sd->parent) + +#endif /* (CONFIG_SCHED_MC || CONFIG_SCHED_SMT) */ + +static inline void resched_cpu(int cpu) +{ + unsigned long flags; + + grq_lock_irqsave(&flags); + resched_task(cpu_curr(cpu)); + grq_unlock_irqrestore(&flags); +} + +/* + * In the semi idle case, use the nearest busy cpu for migrating timers + * from an idle cpu. This is good for power-savings. + * + * We don't do similar optimization for completely idle system, as + * selecting an idle cpu will add more delays to the timers than intended + * (as that cpu's timer base may not be uptodate wrt jiffies etc). + */ +int get_nohz_timer_target(void) +{ + int cpu = smp_processor_id(); + int i; + struct sched_domain *sd; + + for_each_domain(cpu, sd) { + for_each_cpu(i, sched_domain_span(sd)) + if (!idle_cpu(i)) + return i; + } + return cpu; +} + +/* + * When add_timer_on() enqueues a timer into the timer wheel of an + * idle CPU then this timer might expire before the next timer event + * which is scheduled to wake up that CPU. In case of a completely + * idle system the next event might even be infinite time into the + * future. wake_up_idle_cpu() ensures that the CPU is woken up and + * leaves the inner idle loop so the newly added timer is taken into + * account when the CPU goes back to idle and evaluates the timer + * wheel for the next timer event. + */ +void wake_up_idle_cpu(int cpu) +{ + struct task_struct *idle; + struct rq *rq; + + if (cpu == smp_processor_id()) + return; + + rq = cpu_rq(cpu); + idle = rq->idle; + + /* + * This is safe, as this function is called with the timer + * wheel base lock of (cpu) held. When the CPU is on the way + * to idle and has not yet set rq->curr to idle then it will + * be serialised on the timer wheel base lock and take the new + * timer into account automatically. + */ + if (unlikely(rq->curr != idle)) + return; + + /* + * We can set TIF_RESCHED on the idle task of the other CPU + * lockless. The worst case is that the other CPU runs the + * idle task through an additional NOOP schedule() + */ + set_tsk_need_resched(idle); + + /* NEED_RESCHED must be visible before we test polling */ + smp_mb(); + if (!tsk_is_polling(idle)) + smp_send_reschedule(cpu); +} + +#endif /* CONFIG_NO_HZ */ + +/* + * Change a given task's CPU affinity. Migrate the thread to a + * proper CPU and schedule it away if the CPU it's executing on + * is removed from the allowed bitmask. + * + * NOTE: the caller must have a valid reference to the task, the + * task must not exit() & deallocate itself prematurely. The + * call is not atomic; no spinlocks may be held. + */ +int set_cpus_allowed_ptr(struct task_struct *p, const struct cpumask *new_mask) +{ + unsigned long flags; + int running_wrong = 0; + int queued = 0; + struct rq *rq; + int ret = 0; + + rq = task_grq_lock(p, &flags); + + if (!cpumask_intersects(new_mask, cpu_active_mask)) { + ret = -EINVAL; + goto out; + } + + if (unlikely((p->flags & PF_THREAD_BOUND) && p != current && + !cpumask_equal(&p->cpus_allowed, new_mask))) { + ret = -EINVAL; + goto out; + } + + queued = task_queued(p); + + cpumask_copy(&p->cpus_allowed, new_mask); + + /* Can the task run on the task's current CPU? If so, we're done */ + if (cpumask_test_cpu(task_cpu(p), new_mask)) + goto out; + + if (task_running(p)) { + /* Task is running on the wrong cpu now, reschedule it. */ + if (rq == this_rq()) { + set_tsk_need_resched(p); + running_wrong = 1; + } else + resched_task(p); + } else + set_task_cpu(p, cpumask_any_and(cpu_active_mask, new_mask)); + +out: + if (queued) + try_preempt(p, rq); + task_grq_unlock(&flags); + + if (running_wrong) + _cond_resched(); + + return ret; +} +EXPORT_SYMBOL_GPL(set_cpus_allowed_ptr); + +#ifdef CONFIG_HOTPLUG_CPU +/* Run through task list and find tasks affined to just the dead cpu, then + * allocate a new affinity */ +static void break_sole_affinity(int src_cpu, struct task_struct *idle) +{ + struct task_struct *p, *t; + + do_each_thread(t, p) { + if (p != idle && !online_cpus(p)) { + cpumask_copy(&p->cpus_allowed, cpu_possible_mask); + /* + * Don't tell them about moving exiting tasks or + * kernel threads (both mm NULL), since they never + * leave kernel. + */ + if (p->mm && printk_ratelimit()) { + printk(KERN_INFO "process %d (%s) no " + "longer affine to cpu %d\n", + task_pid_nr(p), p->comm, src_cpu); + } + } + clear_sticky(p); + } while_each_thread(t, p); +} + +/* + * Schedules idle task to be the next runnable task on current CPU. + * It does so by boosting its priority to highest possible. + * Used by CPU offline code. + */ +void sched_idle_next(struct rq *rq, int this_cpu, struct task_struct *idle) +{ + /* cpu has to be offline */ + BUG_ON(cpu_online(this_cpu)); + + break_sole_affinity(this_cpu, idle); + + __setscheduler(idle, rq, SCHED_FIFO, STOP_PRIO); + + activate_idle_task(idle); + set_tsk_need_resched(rq->curr); +} + +/* + * Ensures that the idle task is using init_mm right before its cpu goes + * offline. + */ +void idle_task_exit(void) +{ + struct mm_struct *mm = current->active_mm; + + BUG_ON(cpu_online(smp_processor_id())); + + if (mm != &init_mm) + switch_mm(mm, &init_mm, current); + mmdrop(mm); +} +#endif /* CONFIG_HOTPLUG_CPU */ +void sched_set_stop_task(int cpu, struct task_struct *stop) +{ + struct sched_param stop_param = { .sched_priority = STOP_PRIO }; + struct sched_param start_param = { .sched_priority = MAX_USER_RT_PRIO - 1 }; + struct task_struct *old_stop = cpu_rq(cpu)->stop; + + if (stop) { + /* + * Make it appear like a SCHED_FIFO task, its something + * userspace knows about and won't get confused about. + * + * Also, it will make PI more or less work without too + * much confusion -- but then, stop work should not + * rely on PI working anyway. + */ + sched_setscheduler_nocheck(stop, SCHED_FIFO, &stop_param); + } + + cpu_rq(cpu)->stop = stop; + + if (old_stop) { + /* + * Reset it back to a normal rt scheduling prio so that + * it can die in pieces. + */ + sched_setscheduler_nocheck(old_stop, SCHED_FIFO, &start_param); + } +} + + +#if defined(CONFIG_SCHED_DEBUG) && defined(CONFIG_SYSCTL) + +static struct ctl_table sd_ctl_dir[] = { + { + .procname = "sched_domain", + .mode = 0555, + }, + {} +}; + +static struct ctl_table sd_ctl_root[] = { + { + .procname = "kernel", + .mode = 0555, + .child = sd_ctl_dir, + }, + {} +}; + +static struct ctl_table *sd_alloc_ctl_entry(int n) +{ + struct ctl_table *entry = + kcalloc(n, sizeof(struct ctl_table), GFP_KERNEL); + + return entry; +} + +static void sd_free_ctl_entry(struct ctl_table **tablep) +{ + struct ctl_table *entry; + + /* + * In the intermediate directories, both the child directory and + * procname are dynamically allocated and could fail but the mode + * will always be set. In the lowest directory the names are + * static strings and all have proc handlers. + */ + for (entry = *tablep; entry->mode; entry++) { + if (entry->child) + sd_free_ctl_entry(&entry->child); + if (entry->proc_handler == NULL) + kfree(entry->procname); + } + + kfree(*tablep); + *tablep = NULL; +} + +static void +set_table_entry(struct ctl_table *entry, + const char *procname, void *data, int maxlen, + mode_t mode, proc_handler *proc_handler) +{ + entry->procname = procname; + entry->data = data; + entry->maxlen = maxlen; + entry->mode = mode; + entry->proc_handler = proc_handler; +} + +static struct ctl_table * +sd_alloc_ctl_domain_table(struct sched_domain *sd) +{ + struct ctl_table *table = sd_alloc_ctl_entry(13); + + if (table == NULL) + return NULL; + + set_table_entry(&table[0], "min_interval", &sd->min_interval, + sizeof(long), 0644, proc_doulongvec_minmax); + set_table_entry(&table[1], "max_interval", &sd->max_interval, + sizeof(long), 0644, proc_doulongvec_minmax); + set_table_entry(&table[2], "busy_idx", &sd->busy_idx, + sizeof(int), 0644, proc_dointvec_minmax); + set_table_entry(&table[3], "idle_idx", &sd->idle_idx, + sizeof(int), 0644, proc_dointvec_minmax); + set_table_entry(&table[4], "newidle_idx", &sd->newidle_idx, + sizeof(int), 0644, proc_dointvec_minmax); + set_table_entry(&table[5], "wake_idx", &sd->wake_idx, + sizeof(int), 0644, proc_dointvec_minmax); + set_table_entry(&table[6], "forkexec_idx", &sd->forkexec_idx, + sizeof(int), 0644, proc_dointvec_minmax); + set_table_entry(&table[7], "busy_factor", &sd->busy_factor, + sizeof(int), 0644, proc_dointvec_minmax); + set_table_entry(&table[8], "imbalance_pct", &sd->imbalance_pct, + sizeof(int), 0644, proc_dointvec_minmax); + set_table_entry(&table[9], "cache_nice_tries", + &sd->cache_nice_tries, + sizeof(int), 0644, proc_dointvec_minmax); + set_table_entry(&table[10], "flags", &sd->flags, + sizeof(int), 0644, proc_dointvec_minmax); + set_table_entry(&table[11], "name", sd->name, + CORENAME_MAX_SIZE, 0444, proc_dostring); + /* &table[12] is terminator */ + + return table; +} + +static ctl_table *sd_alloc_ctl_cpu_table(int cpu) +{ + struct ctl_table *entry, *table; + struct sched_domain *sd; + int domain_num = 0, i; + char buf[32]; + + for_each_domain(cpu, sd) + domain_num++; + entry = table = sd_alloc_ctl_entry(domain_num + 1); + if (table == NULL) + return NULL; + + i = 0; + for_each_domain(cpu, sd) { + snprintf(buf, 32, "domain%d", i); + entry->procname = kstrdup(buf, GFP_KERNEL); + entry->mode = 0555; + entry->child = sd_alloc_ctl_domain_table(sd); + entry++; + i++; + } + return table; +} + +static struct ctl_table_header *sd_sysctl_header; +static void register_sched_domain_sysctl(void) +{ + int i, cpu_num = num_possible_cpus(); + struct ctl_table *entry = sd_alloc_ctl_entry(cpu_num + 1); + char buf[32]; + + WARN_ON(sd_ctl_dir[0].child); + sd_ctl_dir[0].child = entry; + + if (entry == NULL) + return; + + for_each_possible_cpu(i) { + snprintf(buf, 32, "cpu%d", i); + entry->procname = kstrdup(buf, GFP_KERNEL); + entry->mode = 0555; + entry->child = sd_alloc_ctl_cpu_table(i); + entry++; + } + + WARN_ON(sd_sysctl_header); + sd_sysctl_header = register_sysctl_table(sd_ctl_root); +} + +/* may be called multiple times per register */ +static void unregister_sched_domain_sysctl(void) +{ + if (sd_sysctl_header) + unregister_sysctl_table(sd_sysctl_header); + sd_sysctl_header = NULL; + if (sd_ctl_dir[0].child) + sd_free_ctl_entry(&sd_ctl_dir[0].child); +} +#else +static void register_sched_domain_sysctl(void) +{ +} +static void unregister_sched_domain_sysctl(void) +{ +} +#endif + +static void set_rq_online(struct rq *rq) +{ + if (!rq->online) { + cpumask_set_cpu(cpu_of(rq), rq->rd->online); + rq->online = 1; + } +} + +static void set_rq_offline(struct rq *rq) +{ + if (rq->online) { + cpumask_clear_cpu(cpu_of(rq), rq->rd->online); + rq->online = 0; + } +} + +/* + * migration_call - callback that gets triggered when a CPU is added. + */ +static int __cpuinit +migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu) +{ + int cpu = (long)hcpu; + unsigned long flags; + struct rq *rq = cpu_rq(cpu); +#ifdef CONFIG_HOTPLUG_CPU + struct task_struct *idle = rq->idle; +#endif + + switch (action & ~CPU_TASKS_FROZEN) { + + case CPU_UP_PREPARE: + break; + + case CPU_ONLINE: + /* Update our root-domain */ + grq_lock_irqsave(&flags); + if (rq->rd) { + BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span)); + + set_rq_online(rq); + } + grq.noc = num_online_cpus(); + grq_unlock_irqrestore(&flags); + break; + +#ifdef CONFIG_HOTPLUG_CPU + case CPU_DEAD: + /* Idle task back to normal (off runqueue, low prio) */ + grq_lock_irq(); + return_task(idle, 1); + idle->static_prio = MAX_PRIO; + __setscheduler(idle, rq, SCHED_NORMAL, 0); + idle->prio = PRIO_LIMIT; + set_rq_task(rq, idle); + update_clocks(rq); + grq_unlock_irq(); + break; + + case CPU_DYING: + /* Update our root-domain */ + grq_lock_irqsave(&flags); + sched_idle_next(rq, cpu, idle); + if (rq->rd) { + BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span)); + set_rq_offline(rq); + } + grq.noc = num_online_cpus(); + grq_unlock_irqrestore(&flags); + break; +#endif + } + return NOTIFY_OK; +} + +/* + * Register at high priority so that task migration (migrate_all_tasks) + * happens before everything else. This has to be lower priority than + * the notifier in the perf_counter subsystem, though. + */ +static struct notifier_block __cpuinitdata migration_notifier = { + .notifier_call = migration_call, + .priority = CPU_PRI_MIGRATION, +}; + +static int __cpuinit sched_cpu_active(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_ONLINE: + case CPU_DOWN_FAILED: + set_cpu_active((long)hcpu, true); + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} + +static int __cpuinit sched_cpu_inactive(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_DOWN_PREPARE: + set_cpu_active((long)hcpu, false); + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} + +int __init migration_init(void) +{ + void *cpu = (void *)(long)smp_processor_id(); + int err; + + /* Initialise migration for the boot CPU */ + err = migration_call(&migration_notifier, CPU_UP_PREPARE, cpu); + BUG_ON(err == NOTIFY_BAD); + migration_call(&migration_notifier, CPU_ONLINE, cpu); + register_cpu_notifier(&migration_notifier); + + /* Register cpu active notifiers */ + cpu_notifier(sched_cpu_active, CPU_PRI_SCHED_ACTIVE); + cpu_notifier(sched_cpu_inactive, CPU_PRI_SCHED_INACTIVE); + + return 0; +} +early_initcall(migration_init); +#endif + +#ifdef CONFIG_SMP + +#ifdef CONFIG_SCHED_DEBUG + +static __read_mostly int sched_domain_debug_enabled; + +static int __init sched_domain_debug_setup(char *str) +{ + sched_domain_debug_enabled = 1; + + return 0; +} +early_param("sched_debug", sched_domain_debug_setup); + +static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level, + struct cpumask *groupmask) +{ + struct sched_group *group = sd->groups; + char str[256]; + + cpulist_scnprintf(str, sizeof(str), sched_domain_span(sd)); + cpumask_clear(groupmask); + + printk(KERN_DEBUG "%*s domain %d: ", level, "", level); + + if (!(sd->flags & SD_LOAD_BALANCE)) { + printk("does not load-balance\n"); + if (sd->parent) + printk(KERN_ERR "ERROR: !SD_LOAD_BALANCE domain" + " has parent"); + return -1; + } + + printk(KERN_CONT "span %s level %s\n", str, sd->name); + + if (!cpumask_test_cpu(cpu, sched_domain_span(sd))) { + printk(KERN_ERR "ERROR: domain->span does not contain " + "CPU%d\n", cpu); + } + if (!cpumask_test_cpu(cpu, sched_group_cpus(group))) { + printk(KERN_ERR "ERROR: domain->groups does not contain" + " CPU%d\n", cpu); + } + + printk(KERN_DEBUG "%*s groups:", level + 1, ""); + do { + if (!group) { + printk("\n"); + printk(KERN_ERR "ERROR: group is NULL\n"); + break; + } + + if (!group->cpu_power) { + printk(KERN_CONT "\n"); + printk(KERN_ERR "ERROR: domain->cpu_power not " + "set\n"); + break; + } + + if (!cpumask_weight(sched_group_cpus(group))) { + printk(KERN_CONT "\n"); + printk(KERN_ERR "ERROR: empty group\n"); + break; + } + + if (cpumask_intersects(groupmask, sched_group_cpus(group))) { + printk(KERN_CONT "\n"); + printk(KERN_ERR "ERROR: repeated CPUs\n"); + break; + } + + cpumask_or(groupmask, groupmask, sched_group_cpus(group)); + + cpulist_scnprintf(str, sizeof(str), sched_group_cpus(group)); + + printk(KERN_CONT " %s", str); + if (group->cpu_power != SCHED_LOAD_SCALE) { + printk(KERN_CONT " (cpu_power = %d)", + group->cpu_power); + } + + group = group->next; + } while (group != sd->groups); + printk(KERN_CONT "\n"); + + if (!cpumask_equal(sched_domain_span(sd), groupmask)) + printk(KERN_ERR "ERROR: groups don't span domain->span\n"); + + if (sd->parent && + !cpumask_subset(groupmask, sched_domain_span(sd->parent))) + printk(KERN_ERR "ERROR: parent span is not a superset " + "of domain->span\n"); + return 0; +} + +static void sched_domain_debug(struct sched_domain *sd, int cpu) +{ + cpumask_var_t groupmask; + int level = 0; + + if (!sched_domain_debug_enabled) + return; + + if (!sd) { + printk(KERN_DEBUG "CPU%d attaching NULL sched-domain.\n", cpu); + return; + } + + printk(KERN_DEBUG "CPU%d attaching sched-domain:\n", cpu); + + if (!alloc_cpumask_var(&groupmask, GFP_KERNEL)) { + printk(KERN_DEBUG "Cannot load-balance (out of memory)\n"); + return; + } + + for (;;) { + if (sched_domain_debug_one(sd, cpu, level, groupmask)) + break; + level++; + sd = sd->parent; + if (!sd) + break; + } + free_cpumask_var(groupmask); +} +#else /* !CONFIG_SCHED_DEBUG */ +# define sched_domain_debug(sd, cpu) do { } while (0) +#endif /* CONFIG_SCHED_DEBUG */ + +static int sd_degenerate(struct sched_domain *sd) +{ + if (cpumask_weight(sched_domain_span(sd)) == 1) + return 1; + + /* Following flags need at least 2 groups */ + if (sd->flags & (SD_LOAD_BALANCE | + SD_BALANCE_NEWIDLE | + SD_BALANCE_FORK | + SD_BALANCE_EXEC | + SD_SHARE_CPUPOWER | + SD_SHARE_PKG_RESOURCES)) { + if (sd->groups != sd->groups->next) + return 0; + } + + /* Following flags don't use groups */ + if (sd->flags & (SD_WAKE_AFFINE)) + return 0; + + return 1; +} + +static int +sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent) +{ + unsigned long cflags = sd->flags, pflags = parent->flags; + + if (sd_degenerate(parent)) + return 1; + + if (!cpumask_equal(sched_domain_span(sd), sched_domain_span(parent))) + return 0; + + /* Flags needing groups don't count if only 1 group in parent */ + if (parent->groups == parent->groups->next) { + pflags &= ~(SD_LOAD_BALANCE | + SD_BALANCE_NEWIDLE | + SD_BALANCE_FORK | + SD_BALANCE_EXEC | + SD_SHARE_CPUPOWER | + SD_SHARE_PKG_RESOURCES); + if (nr_node_ids == 1) + pflags &= ~SD_SERIALIZE; + } + if (~cflags & pflags) + return 0; + + return 1; +} + +static void free_rootdomain(struct root_domain *rd) +{ + synchronize_sched(); + + free_cpumask_var(rd->rto_mask); + free_cpumask_var(rd->online); + free_cpumask_var(rd->span); + kfree(rd); +} + +static void rq_attach_root(struct rq *rq, struct root_domain *rd) +{ + struct root_domain *old_rd = NULL; + unsigned long flags; + + grq_lock_irqsave(&flags); + + if (rq->rd) { + old_rd = rq->rd; + + if (cpumask_test_cpu(cpu_of(rq), old_rd->online)) + set_rq_offline(rq); + + cpumask_clear_cpu(cpu_of(rq), old_rd->span); + + /* + * If we dont want to free the old_rt yet then + * set old_rd to NULL to skip the freeing later + * in this function: + */ + if (!atomic_dec_and_test(&old_rd->refcount)) + old_rd = NULL; + } + + atomic_inc(&rd->refcount); + rq->rd = rd; + + cpumask_set_cpu(cpu_of(rq), rd->span); + if (cpumask_test_cpu(rq->cpu, cpu_active_mask)) + set_rq_online(rq); + + grq_unlock_irqrestore(&flags); + + if (old_rd) + free_rootdomain(old_rd); +} + +static int init_rootdomain(struct root_domain *rd) +{ + memset(rd, 0, sizeof(*rd)); + + if (!alloc_cpumask_var(&rd->span, GFP_KERNEL)) + goto out; + if (!alloc_cpumask_var(&rd->online, GFP_KERNEL)) + goto free_span; + if (!alloc_cpumask_var(&rd->rto_mask, GFP_KERNEL)) + goto free_online; + + if (cpupri_init(&rd->cpupri) != 0) + goto free_rto_mask; + return 0; + +free_rto_mask: + free_cpumask_var(rd->rto_mask); +free_online: + free_cpumask_var(rd->online); +free_span: + free_cpumask_var(rd->span); +out: + return -ENOMEM; +} + +static void init_defrootdomain(void) +{ + init_rootdomain(&def_root_domain); + + atomic_set(&def_root_domain.refcount, 1); +} + +static struct root_domain *alloc_rootdomain(void) +{ + struct root_domain *rd; + + rd = kmalloc(sizeof(*rd), GFP_KERNEL); + if (!rd) + return NULL; + + if (init_rootdomain(rd) != 0) { + kfree(rd); + return NULL; + } + + return rd; +} + +/* + * Attach the domain 'sd' to 'cpu' as its base domain. Callers must + * hold the hotplug lock. + */ +static void +cpu_attach_domain(struct sched_domain *sd, struct root_domain *rd, int cpu) +{ + struct rq *rq = cpu_rq(cpu); + struct sched_domain *tmp; + + for (tmp = sd; tmp; tmp = tmp->parent) + tmp->span_weight = cpumask_weight(sched_domain_span(tmp)); + + /* Remove the sched domains which do not contribute to scheduling. */ + for (tmp = sd; tmp; ) { + struct sched_domain *parent = tmp->parent; + if (!parent) + break; + + if (sd_parent_degenerate(tmp, parent)) { + tmp->parent = parent->parent; + if (parent->parent) + parent->parent->child = tmp; + } else + tmp = tmp->parent; + } + + if (sd && sd_degenerate(sd)) { + sd = sd->parent; + if (sd) + sd->child = NULL; + } + + sched_domain_debug(sd, cpu); + + rq_attach_root(rq, rd); + rcu_assign_pointer(rq->sd, sd); +} + +/* cpus with isolated domains */ +static cpumask_var_t cpu_isolated_map; + +/* Setup the mask of cpus configured for isolated domains */ +static int __init isolated_cpu_setup(char *str) +{ + alloc_bootmem_cpumask_var(&cpu_isolated_map); + cpulist_parse(str, cpu_isolated_map); + return 1; +} + +__setup("isolcpus=", isolated_cpu_setup); + +/* + * init_sched_build_groups takes the cpumask we wish to span, and a pointer + * to a function which identifies what group(along with sched group) a CPU + * belongs to. The return value of group_fn must be a >= 0 and < nr_cpu_ids + * (due to the fact that we keep track of groups covered with a struct cpumask). + * + * init_sched_build_groups will build a circular linked list of the groups + * covered by the given span, and will set each group's ->cpumask correctly, + * and ->cpu_power to 0. + */ +static void +init_sched_build_groups(const struct cpumask *span, + const struct cpumask *cpu_map, + int (*group_fn)(int cpu, const struct cpumask *cpu_map, + struct sched_group **sg, + struct cpumask *tmpmask), + struct cpumask *covered, struct cpumask *tmpmask) +{ + struct sched_group *first = NULL, *last = NULL; + int i; + + cpumask_clear(covered); + + for_each_cpu(i, span) { + struct sched_group *sg; + int group = group_fn(i, cpu_map, &sg, tmpmask); + int j; + + if (cpumask_test_cpu(i, covered)) + continue; + + cpumask_clear(sched_group_cpus(sg)); + sg->cpu_power = 0; + + for_each_cpu(j, span) { + if (group_fn(j, cpu_map, NULL, tmpmask) != group) + continue; + + cpumask_set_cpu(j, covered); + cpumask_set_cpu(j, sched_group_cpus(sg)); + } + if (!first) + first = sg; + if (last) + last->next = sg; + last = sg; + } + last->next = first; +} + +#define SD_NODES_PER_DOMAIN 16 + +#ifdef CONFIG_NUMA + +/** + * find_next_best_node - find the next node to include in a sched_domain + * @node: node whose sched_domain we're building + * @used_nodes: nodes already in the sched_domain + * + * Find the next node to include in a given scheduling domain. Simply + * finds the closest node not already in the @used_nodes map. + * + * Should use nodemask_t. + */ +static int find_next_best_node(int node, nodemask_t *used_nodes) +{ + int i, n, val, min_val, best_node = 0; + + min_val = INT_MAX; + + for (i = 0; i < nr_node_ids; i++) { + /* Start at @node */ + n = (node + i) % nr_node_ids; + + if (!nr_cpus_node(n)) + continue; + + /* Skip already used nodes */ + if (node_isset(n, *used_nodes)) + continue; + + /* Simple min distance search */ + val = node_distance(node, n); + + if (val < min_val) { + min_val = val; + best_node = n; + } + } + + node_set(best_node, *used_nodes); + return best_node; +} + +/** + * sched_domain_node_span - get a cpumask for a node's sched_domain + * @node: node whose cpumask we're constructing + * @span: resulting cpumask + * + * Given a node, construct a good cpumask for its sched_domain to span. It + * should be one that prevents unnecessary balancing, but also spreads tasks + * out optimally. + */ +static void sched_domain_node_span(int node, struct cpumask *span) +{ + nodemask_t used_nodes; + int i; + + cpumask_clear(span); + nodes_clear(used_nodes); + + cpumask_or(span, span, cpumask_of_node(node)); + node_set(node, used_nodes); + + for (i = 1; i < SD_NODES_PER_DOMAIN; i++) { + int next_node = find_next_best_node(node, &used_nodes); + + cpumask_or(span, span, cpumask_of_node(next_node)); + } +} +#endif /* CONFIG_NUMA */ + +int sched_smt_power_savings = 0, sched_mc_power_savings = 0; + +/* + * The cpus mask in sched_group and sched_domain hangs off the end. + * + * ( See the the comments in include/linux/sched.h:struct sched_group + * and struct sched_domain. ) + */ +struct static_sched_group { + struct sched_group sg; + DECLARE_BITMAP(cpus, CONFIG_NR_CPUS); +}; + +struct static_sched_domain { + struct sched_domain sd; + DECLARE_BITMAP(span, CONFIG_NR_CPUS); +}; + +struct s_data { +#ifdef CONFIG_NUMA + int sd_allnodes; + cpumask_var_t domainspan; + cpumask_var_t covered; + cpumask_var_t notcovered; +#endif + cpumask_var_t nodemask; + cpumask_var_t this_sibling_map; + cpumask_var_t this_core_map; + cpumask_var_t this_book_map; + cpumask_var_t send_covered; + cpumask_var_t tmpmask; + struct sched_group **sched_group_nodes; + struct root_domain *rd; +}; + +enum s_alloc { + sa_sched_groups = 0, + sa_rootdomain, + sa_tmpmask, + sa_send_covered, + sa_this_book_map, + sa_this_core_map, + sa_this_sibling_map, + sa_nodemask, + sa_sched_group_nodes, +#ifdef CONFIG_NUMA + sa_notcovered, + sa_covered, + sa_domainspan, +#endif + sa_none, +}; + +/* + * SMT sched-domains: + */ +#ifdef CONFIG_SCHED_SMT +static DEFINE_PER_CPU(struct static_sched_domain, cpu_domains); +static DEFINE_PER_CPU(struct static_sched_group, sched_groups); + +static int +cpu_to_cpu_group(int cpu, const struct cpumask *cpu_map, + struct sched_group **sg, struct cpumask *unused) +{ + if (sg) + *sg = &per_cpu(sched_groups, cpu).sg; + return cpu; +} +#endif /* CONFIG_SCHED_SMT */ + +/* + * multi-core sched-domains: + */ +#ifdef CONFIG_SCHED_MC +static DEFINE_PER_CPU(struct static_sched_domain, core_domains); +static DEFINE_PER_CPU(struct static_sched_group, sched_group_core); + +static int +cpu_to_core_group(int cpu, const struct cpumask *cpu_map, + struct sched_group **sg, struct cpumask *mask) +{ + int group; +#ifdef CONFIG_SCHED_SMT + cpumask_and(mask, topology_thread_cpumask(cpu), cpu_map); + group = cpumask_first(mask); +#else + group = cpu; +#endif + if (sg) + *sg = &per_cpu(sched_group_core, group).sg; + return group; +} +#endif /* CONFIG_SCHED_MC */ + +/* + * book sched-domains: + */ +#ifdef CONFIG_SCHED_BOOK +static DEFINE_PER_CPU(struct static_sched_domain, book_domains); +static DEFINE_PER_CPU(struct static_sched_group, sched_group_book); + +static int +cpu_to_book_group(int cpu, const struct cpumask *cpu_map, + struct sched_group **sg, struct cpumask *mask) +{ + int group = cpu; +#ifdef CONFIG_SCHED_MC + cpumask_and(mask, cpu_coregroup_mask(cpu), cpu_map); + group = cpumask_first(mask); +#elif defined(CONFIG_SCHED_SMT) + cpumask_and(mask, topology_thread_cpumask(cpu), cpu_map); + group = cpumask_first(mask); +#endif + if (sg) + *sg = &per_cpu(sched_group_book, group).sg; + return group; +} +#endif /* CONFIG_SCHED_BOOK */ + +static DEFINE_PER_CPU(struct static_sched_domain, phys_domains); +static DEFINE_PER_CPU(struct static_sched_group, sched_group_phys); + +static int +cpu_to_phys_group(int cpu, const struct cpumask *cpu_map, + struct sched_group **sg, struct cpumask *mask) +{ + int group; +#ifdef CONFIG_SCHED_BOOK + cpumask_and(mask, cpu_book_mask(cpu), cpu_map); + group = cpumask_first(mask); +#elif defined(CONFIG_SCHED_MC) + cpumask_and(mask, cpu_coregroup_mask(cpu), cpu_map); + group = cpumask_first(mask); +#elif defined(CONFIG_SCHED_SMT) + cpumask_and(mask, topology_thread_cpumask(cpu), cpu_map); + group = cpumask_first(mask); +#else + group = cpu; +#endif + if (sg) + *sg = &per_cpu(sched_group_phys, group).sg; + return group; +} + +/** + * group_first_cpu - Returns the first cpu in the cpumask of a sched_group. + * @group: The group whose first cpu is to be returned. + */ +static inline unsigned int group_first_cpu(struct sched_group *group) +{ + return cpumask_first(sched_group_cpus(group)); +} + +#ifdef CONFIG_NUMA +/* + * The init_sched_build_groups can't handle what we want to do with node + * groups, so roll our own. Now each node has its own list of groups which + * gets dynamically allocated. + */ +static DEFINE_PER_CPU(struct static_sched_domain, node_domains); +static struct sched_group ***sched_group_nodes_bycpu; + +static DEFINE_PER_CPU(struct static_sched_domain, allnodes_domains); +static DEFINE_PER_CPU(struct static_sched_group, sched_group_allnodes); + +static int cpu_to_allnodes_group(int cpu, const struct cpumask *cpu_map, + struct sched_group **sg, + struct cpumask *nodemask) +{ + int group; + + cpumask_and(nodemask, cpumask_of_node(cpu_to_node(cpu)), cpu_map); + group = cpumask_first(nodemask); + + if (sg) + *sg = &per_cpu(sched_group_allnodes, group).sg; + return group; +} + +static void init_numa_sched_groups_power(struct sched_group *group_head) +{ + struct sched_group *sg = group_head; + int j; + + if (!sg) + return; + do { + for_each_cpu(j, sched_group_cpus(sg)) { + struct sched_domain *sd; + + sd = &per_cpu(phys_domains, j).sd; + if (j != group_first_cpu(sd->groups)) { + /* + * Only add "power" once for each + * physical package. + */ + continue; + } + + sg->cpu_power += sd->groups->cpu_power; + } + sg = sg->next; + } while (sg != group_head); +} + +static int build_numa_sched_groups(struct s_data *d, + const struct cpumask *cpu_map, int num) +{ + struct sched_domain *sd; + struct sched_group *sg, *prev; + int n, j; + + cpumask_clear(d->covered); + cpumask_and(d->nodemask, cpumask_of_node(num), cpu_map); + if (cpumask_empty(d->nodemask)) { + d->sched_group_nodes[num] = NULL; + goto out; + } + + sched_domain_node_span(num, d->domainspan); + cpumask_and(d->domainspan, d->domainspan, cpu_map); + + sg = kmalloc_node(sizeof(struct sched_group) + cpumask_size(), + GFP_KERNEL, num); + if (!sg) { + printk(KERN_WARNING "Can not alloc domain group for node %d\n", + num); + return -ENOMEM; + } + d->sched_group_nodes[num] = sg; + + for_each_cpu(j, d->nodemask) { + sd = &per_cpu(node_domains, j).sd; + sd->groups = sg; + } + + sg->cpu_power = 0; + cpumask_copy(sched_group_cpus(sg), d->nodemask); + sg->next = sg; + cpumask_or(d->covered, d->covered, d->nodemask); + + prev = sg; + for (j = 0; j < nr_node_ids; j++) { + n = (num + j) % nr_node_ids; + cpumask_complement(d->notcovered, d->covered); + cpumask_and(d->tmpmask, d->notcovered, cpu_map); + cpumask_and(d->tmpmask, d->tmpmask, d->domainspan); + if (cpumask_empty(d->tmpmask)) + break; + cpumask_and(d->tmpmask, d->tmpmask, cpumask_of_node(n)); + if (cpumask_empty(d->tmpmask)) + continue; + sg = kmalloc_node(sizeof(struct sched_group) + cpumask_size(), + GFP_KERNEL, num); + if (!sg) { + printk(KERN_WARNING + "Can not alloc domain group for node %d\n", j); + return -ENOMEM; + } + sg->cpu_power = 0; + cpumask_copy(sched_group_cpus(sg), d->tmpmask); + sg->next = prev->next; + cpumask_or(d->covered, d->covered, d->tmpmask); + prev->next = sg; + prev = sg; + } +out: + return 0; +} +#endif /* CONFIG_NUMA */ + +#ifdef CONFIG_NUMA +/* Free memory allocated for various sched_group structures */ +static void free_sched_groups(const struct cpumask *cpu_map, + struct cpumask *nodemask) +{ + int cpu, i; + + for_each_cpu(cpu, cpu_map) { + struct sched_group **sched_group_nodes + = sched_group_nodes_bycpu[cpu]; + + if (!sched_group_nodes) + continue; + + for (i = 0; i < nr_node_ids; i++) { + struct sched_group *oldsg, *sg = sched_group_nodes[i]; + + cpumask_and(nodemask, cpumask_of_node(i), cpu_map); + if (cpumask_empty(nodemask)) + continue; + + if (sg == NULL) + continue; + sg = sg->next; +next_sg: + oldsg = sg; + sg = sg->next; + kfree(oldsg); + if (oldsg != sched_group_nodes[i]) + goto next_sg; + } + kfree(sched_group_nodes); + sched_group_nodes_bycpu[cpu] = NULL; + } +} +#else /* !CONFIG_NUMA */ +static void free_sched_groups(const struct cpumask *cpu_map, + struct cpumask *nodemask) +{ +} +#endif /* CONFIG_NUMA */ + +/* + * Initialise sched groups cpu_power. + * + * cpu_power indicates the capacity of sched group, which is used while + * distributing the load between different sched groups in a sched domain. + * Typically cpu_power for all the groups in a sched domain will be same unless + * there are asymmetries in the topology. If there are asymmetries, group + * having more cpu_power will pickup more load compared to the group having + * less cpu_power. + * + * cpu_power will be a multiple of SCHED_LOAD_SCALE. This multiple represents + * the maximum number of tasks a group can handle in the presence of other idle + * or lightly loaded groups in the same sched domain. + */ +static void init_sched_groups_power(int cpu, struct sched_domain *sd) +{ + struct sched_domain *child; + struct sched_group *group; + long power; + int weight; + + WARN_ON(!sd || !sd->groups); + + if (cpu != group_first_cpu(sd->groups)) + return; + + sd->groups->group_weight = cpumask_weight(sched_group_cpus(sd->groups)); + + child = sd->child; + + sd->groups->cpu_power = 0; + + if (!child) { + power = SCHED_LOAD_SCALE; + weight = cpumask_weight(sched_domain_span(sd)); + /* + * SMT siblings share the power of a single core. + * Usually multiple threads get a better yield out of + * that one core than a single thread would have, + * reflect that in sd->smt_gain. + */ + if ((sd->flags & SD_SHARE_CPUPOWER) && weight > 1) { + power *= sd->smt_gain; + power /= weight; + power >>= SCHED_LOAD_SHIFT; + } + sd->groups->cpu_power += power; + return; + } + + /* + * Add cpu_power of each child group to this groups cpu_power + */ + group = child->groups; + do { + sd->groups->cpu_power += group->cpu_power; + group = group->next; + } while (group != child->groups); +} + +/* + * Initialisers for schedule domains + * Non-inlined to reduce accumulated stack pressure in build_sched_domains() + */ + +#ifdef CONFIG_SCHED_DEBUG +# define SD_INIT_NAME(sd, type) sd->name = #type +#else +# define SD_INIT_NAME(sd, type) do { } while (0) +#endif + +#define SD_INIT(sd, type) sd_init_##type(sd) + +#define SD_INIT_FUNC(type) \ +static noinline void sd_init_##type(struct sched_domain *sd) \ +{ \ + memset(sd, 0, sizeof(*sd)); \ + *sd = SD_##type##_INIT; \ + sd->level = SD_LV_##type; \ + SD_INIT_NAME(sd, type); \ +} + +SD_INIT_FUNC(CPU) +#ifdef CONFIG_NUMA + SD_INIT_FUNC(ALLNODES) + SD_INIT_FUNC(NODE) +#endif +#ifdef CONFIG_SCHED_SMT + SD_INIT_FUNC(SIBLING) +#endif +#ifdef CONFIG_SCHED_MC + SD_INIT_FUNC(MC) +#endif +#ifdef CONFIG_SCHED_BOOK + SD_INIT_FUNC(BOOK) +#endif + +static int default_relax_domain_level = -1; + +static int __init setup_relax_domain_level(char *str) +{ + unsigned long val; + + val = simple_strtoul(str, NULL, 0); + if (val < SD_LV_MAX) + default_relax_domain_level = val; + + return 1; +} +__setup("relax_domain_level=", setup_relax_domain_level); + +static void set_domain_attribute(struct sched_domain *sd, + struct sched_domain_attr *attr) +{ + int request; + + if (!attr || attr->relax_domain_level < 0) { + if (default_relax_domain_level < 0) + return; + else + request = default_relax_domain_level; + } else + request = attr->relax_domain_level; + if (request < sd->level) { + /* turn off idle balance on this domain */ + sd->flags &= ~(SD_BALANCE_WAKE|SD_BALANCE_NEWIDLE); + } else { + /* turn on idle balance on this domain */ + sd->flags |= (SD_BALANCE_WAKE|SD_BALANCE_NEWIDLE); + } +} + +static void __free_domain_allocs(struct s_data *d, enum s_alloc what, + const struct cpumask *cpu_map) +{ + switch (what) { + case sa_sched_groups: + free_sched_groups(cpu_map, d->tmpmask); /* fall through */ + d->sched_group_nodes = NULL; + case sa_rootdomain: + free_rootdomain(d->rd); /* fall through */ + case sa_tmpmask: + free_cpumask_var(d->tmpmask); /* fall through */ + case sa_send_covered: + free_cpumask_var(d->send_covered); /* fall through */ + case sa_this_book_map: + free_cpumask_var(d->this_book_map); /* fall through */ + case sa_this_core_map: + free_cpumask_var(d->this_core_map); /* fall through */ + case sa_this_sibling_map: + free_cpumask_var(d->this_sibling_map); /* fall through */ + case sa_nodemask: + free_cpumask_var(d->nodemask); /* fall through */ + case sa_sched_group_nodes: +#ifdef CONFIG_NUMA + kfree(d->sched_group_nodes); /* fall through */ + case sa_notcovered: + free_cpumask_var(d->notcovered); /* fall through */ + case sa_covered: + free_cpumask_var(d->covered); /* fall through */ + case sa_domainspan: + free_cpumask_var(d->domainspan); /* fall through */ +#endif + case sa_none: + break; + } +} + +static enum s_alloc __visit_domain_allocation_hell(struct s_data *d, + const struct cpumask *cpu_map) +{ +#ifdef CONFIG_NUMA + if (!alloc_cpumask_var(&d->domainspan, GFP_KERNEL)) + return sa_none; + if (!alloc_cpumask_var(&d->covered, GFP_KERNEL)) + return sa_domainspan; + if (!alloc_cpumask_var(&d->notcovered, GFP_KERNEL)) + return sa_covered; + /* Allocate the per-node list of sched groups */ + d->sched_group_nodes = kcalloc(nr_node_ids, + sizeof(struct sched_group *), GFP_KERNEL); + if (!d->sched_group_nodes) { + printk(KERN_WARNING "Can not alloc sched group node list\n"); + return sa_notcovered; + } + sched_group_nodes_bycpu[cpumask_first(cpu_map)] = d->sched_group_nodes; +#endif + if (!alloc_cpumask_var(&d->nodemask, GFP_KERNEL)) + return sa_sched_group_nodes; + if (!alloc_cpumask_var(&d->this_sibling_map, GFP_KERNEL)) + return sa_nodemask; + if (!alloc_cpumask_var(&d->this_core_map, GFP_KERNEL)) + return sa_this_sibling_map; + if (!alloc_cpumask_var(&d->this_book_map, GFP_KERNEL)) + return sa_this_core_map; + if (!alloc_cpumask_var(&d->send_covered, GFP_KERNEL)) + return sa_this_book_map; + if (!alloc_cpumask_var(&d->tmpmask, GFP_KERNEL)) + return sa_send_covered; + d->rd = alloc_rootdomain(); + if (!d->rd) { + printk(KERN_WARNING "Cannot alloc root domain\n"); + return sa_tmpmask; + } + return sa_rootdomain; +} + +static struct sched_domain *__build_numa_sched_domains(struct s_data *d, + const struct cpumask *cpu_map, struct sched_domain_attr *attr, int i) +{ + struct sched_domain *sd = NULL; +#ifdef CONFIG_NUMA + struct sched_domain *parent; + + d->sd_allnodes = 0; + if (cpumask_weight(cpu_map) > + SD_NODES_PER_DOMAIN * cpumask_weight(d->nodemask)) { + sd = &per_cpu(allnodes_domains, i).sd; + SD_INIT(sd, ALLNODES); + set_domain_attribute(sd, attr); + cpumask_copy(sched_domain_span(sd), cpu_map); + cpu_to_allnodes_group(i, cpu_map, &sd->groups, d->tmpmask); + d->sd_allnodes = 1; + } + parent = sd; + + sd = &per_cpu(node_domains, i).sd; + SD_INIT(sd, NODE); + set_domain_attribute(sd, attr); + sched_domain_node_span(cpu_to_node(i), sched_domain_span(sd)); + sd->parent = parent; + if (parent) + parent->child = sd; + cpumask_and(sched_domain_span(sd), sched_domain_span(sd), cpu_map); +#endif + return sd; +} + +static struct sched_domain *__build_cpu_sched_domain(struct s_data *d, + const struct cpumask *cpu_map, struct sched_domain_attr *attr, + struct sched_domain *parent, int i) +{ + struct sched_domain *sd; + sd = &per_cpu(phys_domains, i).sd; + SD_INIT(sd, CPU); + set_domain_attribute(sd, attr); + cpumask_copy(sched_domain_span(sd), d->nodemask); + sd->parent = parent; + if (parent) + parent->child = sd; + cpu_to_phys_group(i, cpu_map, &sd->groups, d->tmpmask); + return sd; +} + +static struct sched_domain *__build_book_sched_domain(struct s_data *d, + const struct cpumask *cpu_map, struct sched_domain_attr *attr, + struct sched_domain *parent, int i) +{ + struct sched_domain *sd = parent; +#ifdef CONFIG_SCHED_BOOK + sd = &per_cpu(book_domains, i).sd; + SD_INIT(sd, BOOK); + set_domain_attribute(sd, attr); + cpumask_and(sched_domain_span(sd), cpu_map, cpu_book_mask(i)); + sd->parent = parent; + parent->child = sd; + cpu_to_book_group(i, cpu_map, &sd->groups, d->tmpmask); +#endif + return sd; +} + +static struct sched_domain *__build_mc_sched_domain(struct s_data *d, + const struct cpumask *cpu_map, struct sched_domain_attr *attr, + struct sched_domain *parent, int i) +{ + struct sched_domain *sd = parent; +#ifdef CONFIG_SCHED_MC + sd = &per_cpu(core_domains, i).sd; + SD_INIT(sd, MC); + set_domain_attribute(sd, attr); + cpumask_and(sched_domain_span(sd), cpu_map, cpu_coregroup_mask(i)); + sd->parent = parent; + parent->child = sd; + cpu_to_core_group(i, cpu_map, &sd->groups, d->tmpmask); +#endif + return sd; +} + +static struct sched_domain *__build_smt_sched_domain(struct s_data *d, + const struct cpumask *cpu_map, struct sched_domain_attr *attr, + struct sched_domain *parent, int i) +{ + struct sched_domain *sd = parent; +#ifdef CONFIG_SCHED_SMT + sd = &per_cpu(cpu_domains, i).sd; + SD_INIT(sd, SIBLING); + set_domain_attribute(sd, attr); + cpumask_and(sched_domain_span(sd), cpu_map, topology_thread_cpumask(i)); + sd->parent = parent; + parent->child = sd; + cpu_to_cpu_group(i, cpu_map, &sd->groups, d->tmpmask); +#endif + return sd; +} + +static void build_sched_groups(struct s_data *d, enum sched_domain_level l, + const struct cpumask *cpu_map, int cpu) +{ + switch (l) { +#ifdef CONFIG_SCHED_SMT + case SD_LV_SIBLING: /* set up CPU (sibling) groups */ + cpumask_and(d->this_sibling_map, cpu_map, + topology_thread_cpumask(cpu)); + if (cpu == cpumask_first(d->this_sibling_map)) + init_sched_build_groups(d->this_sibling_map, cpu_map, + &cpu_to_cpu_group, + d->send_covered, d->tmpmask); + break; +#endif +#ifdef CONFIG_SCHED_MC + case SD_LV_MC: /* set up multi-core groups */ + cpumask_and(d->this_core_map, cpu_map, cpu_coregroup_mask(cpu)); + if (cpu == cpumask_first(d->this_core_map)) + init_sched_build_groups(d->this_core_map, cpu_map, + &cpu_to_core_group, + d->send_covered, d->tmpmask); + break; +#endif +#ifdef CONFIG_SCHED_BOOK + case SD_LV_BOOK: /* set up book groups */ + cpumask_and(d->this_book_map, cpu_map, cpu_book_mask(cpu)); + if (cpu == cpumask_first(d->this_book_map)) + init_sched_build_groups(d->this_book_map, cpu_map, + &cpu_to_book_group, + d->send_covered, d->tmpmask); + break; +#endif + case SD_LV_CPU: /* set up physical groups */ + cpumask_and(d->nodemask, cpumask_of_node(cpu), cpu_map); + if (!cpumask_empty(d->nodemask)) + init_sched_build_groups(d->nodemask, cpu_map, + &cpu_to_phys_group, + d->send_covered, d->tmpmask); + break; +#ifdef CONFIG_NUMA + case SD_LV_ALLNODES: + init_sched_build_groups(cpu_map, cpu_map, &cpu_to_allnodes_group, + d->send_covered, d->tmpmask); + break; +#endif + default: + break; + } +} + +/* + * Build sched domains for a given set of cpus and attach the sched domains + * to the individual cpus + */ +static int __build_sched_domains(const struct cpumask *cpu_map, + struct sched_domain_attr *attr) +{ + enum s_alloc alloc_state = sa_none; + struct s_data d; + struct sched_domain *sd; + int i; +#ifdef CONFIG_NUMA + d.sd_allnodes = 0; +#endif + + alloc_state = __visit_domain_allocation_hell(&d, cpu_map); + if (alloc_state != sa_rootdomain) + goto error; + alloc_state = sa_sched_groups; + + /* + * Set up domains for cpus specified by the cpu_map. + */ + for_each_cpu(i, cpu_map) { + cpumask_and(d.nodemask, cpumask_of_node(cpu_to_node(i)), + cpu_map); + + sd = __build_numa_sched_domains(&d, cpu_map, attr, i); + sd = __build_cpu_sched_domain(&d, cpu_map, attr, sd, i); + sd = __build_book_sched_domain(&d, cpu_map, attr, sd, i); + sd = __build_mc_sched_domain(&d, cpu_map, attr, sd, i); + sd = __build_smt_sched_domain(&d, cpu_map, attr, sd, i); + } + + for_each_cpu(i, cpu_map) { + build_sched_groups(&d, SD_LV_SIBLING, cpu_map, i); + build_sched_groups(&d, SD_LV_BOOK, cpu_map, i); + build_sched_groups(&d, SD_LV_MC, cpu_map, i); + } + + /* Set up physical groups */ + for (i = 0; i < nr_node_ids; i++) + build_sched_groups(&d, SD_LV_CPU, cpu_map, i); + +#ifdef CONFIG_NUMA + /* Set up node groups */ + if (d.sd_allnodes) + build_sched_groups(&d, SD_LV_ALLNODES, cpu_map, 0); + + for (i = 0; i < nr_node_ids; i++) + if (build_numa_sched_groups(&d, cpu_map, i)) + goto error; +#endif + + /* Calculate CPU power for physical packages and nodes */ +#ifdef CONFIG_SCHED_SMT + for_each_cpu(i, cpu_map) { + sd = &per_cpu(cpu_domains, i).sd; + init_sched_groups_power(i, sd); + } +#endif +#ifdef CONFIG_SCHED_MC + for_each_cpu(i, cpu_map) { + sd = &per_cpu(core_domains, i).sd; + init_sched_groups_power(i, sd); + } +#endif +#ifdef CONFIG_SCHED_BOOK + for_each_cpu(i, cpu_map) { + sd = &per_cpu(book_domains, i).sd; + init_sched_groups_power(i, sd); + } +#endif + + for_each_cpu(i, cpu_map) { + sd = &per_cpu(phys_domains, i).sd; + init_sched_groups_power(i, sd); + } + +#ifdef CONFIG_NUMA + for (i = 0; i < nr_node_ids; i++) + init_numa_sched_groups_power(d.sched_group_nodes[i]); + + if (d.sd_allnodes) { + struct sched_group *sg; + + cpu_to_allnodes_group(cpumask_first(cpu_map), cpu_map, &sg, + d.tmpmask); + init_numa_sched_groups_power(sg); + } +#endif + + /* Attach the domains */ + for_each_cpu(i, cpu_map) { +#ifdef CONFIG_SCHED_SMT + sd = &per_cpu(cpu_domains, i).sd; +#elif defined(CONFIG_SCHED_MC) + sd = &per_cpu(core_domains, i).sd; +#elif defined(CONFIG_SCHED_BOOK) + sd = &per_cpu(book_domains, i).sd; +#else + sd = &per_cpu(phys_domains, i).sd; +#endif + cpu_attach_domain(sd, d.rd, i); + } + + d.sched_group_nodes = NULL; /* don't free this we still need it */ + __free_domain_allocs(&d, sa_tmpmask, cpu_map); + return 0; + +error: + __free_domain_allocs(&d, alloc_state, cpu_map); + return -ENOMEM; +} + +static int build_sched_domains(const struct cpumask *cpu_map) +{ + return __build_sched_domains(cpu_map, NULL); +} + +static cpumask_var_t *doms_cur; /* current sched domains */ +static int ndoms_cur; /* number of sched domains in 'doms_cur' */ +static struct sched_domain_attr *dattr_cur; + /* attribues of custom domains in 'doms_cur' */ + +/* + * Special case: If a kmalloc of a doms_cur partition (array of + * cpumask) fails, then fallback to a single sched domain, + * as determined by the single cpumask fallback_doms. + */ +static cpumask_var_t fallback_doms; + +/* + * arch_update_cpu_topology lets virtualised architectures update the + * cpu core maps. It is supposed to return 1 if the topology changed + * or 0 if it stayed the same. + */ +int __attribute__((weak)) arch_update_cpu_topology(void) +{ + return 0; +} + +cpumask_var_t *alloc_sched_domains(unsigned int ndoms) +{ + int i; + cpumask_var_t *doms; + + doms = kmalloc(sizeof(*doms) * ndoms, GFP_KERNEL); + if (!doms) + return NULL; + for (i = 0; i < ndoms; i++) { + if (!alloc_cpumask_var(&doms[i], GFP_KERNEL)) { + free_sched_domains(doms, i); + return NULL; + } + } + return doms; +} + +void free_sched_domains(cpumask_var_t doms[], unsigned int ndoms) +{ + unsigned int i; + for (i = 0; i < ndoms; i++) + free_cpumask_var(doms[i]); + kfree(doms); +} + +/* + * Set up scheduler domains and groups. Callers must hold the hotplug lock. + * For now this just excludes isolated cpus, but could be used to + * exclude other special cases in the future. + */ +static int arch_init_sched_domains(const struct cpumask *cpu_map) +{ + int err; + + arch_update_cpu_topology(); + ndoms_cur = 1; + doms_cur = alloc_sched_domains(ndoms_cur); + if (!doms_cur) + doms_cur = &fallback_doms; + cpumask_andnot(doms_cur[0], cpu_map, cpu_isolated_map); + dattr_cur = NULL; + err = build_sched_domains(doms_cur[0]); + register_sched_domain_sysctl(); + + return err; +} + +static void arch_destroy_sched_domains(const struct cpumask *cpu_map, + struct cpumask *tmpmask) +{ + free_sched_groups(cpu_map, tmpmask); +} + +/* + * Detach sched domains from a group of cpus specified in cpu_map + * These cpus will now be attached to the NULL domain + */ +static void detach_destroy_domains(const struct cpumask *cpu_map) +{ + /* Save because hotplug lock held. */ + static DECLARE_BITMAP(tmpmask, CONFIG_NR_CPUS); + int i; + + for_each_cpu(i, cpu_map) + cpu_attach_domain(NULL, &def_root_domain, i); + synchronize_sched(); + arch_destroy_sched_domains(cpu_map, to_cpumask(tmpmask)); +} + +/* handle null as "default" */ +static int dattrs_equal(struct sched_domain_attr *cur, int idx_cur, + struct sched_domain_attr *new, int idx_new) +{ + struct sched_domain_attr tmp; + + /* fast path */ + if (!new && !cur) + return 1; + + tmp = SD_ATTR_INIT; + return !memcmp(cur ? (cur + idx_cur) : &tmp, + new ? (new + idx_new) : &tmp, + sizeof(struct sched_domain_attr)); +} + +/* + * Partition sched domains as specified by the 'ndoms_new' + * cpumasks in the array doms_new[] of cpumasks. This compares + * doms_new[] to the current sched domain partitioning, doms_cur[]. + * It destroys each deleted domain and builds each new domain. + * + * 'doms_new' is an array of cpumask_var_t's of length 'ndoms_new'. + * The masks don't intersect (don't overlap.) We should setup one + * sched domain for each mask. CPUs not in any of the cpumasks will + * not be load balanced. If the same cpumask appears both in the + * current 'doms_cur' domains and in the new 'doms_new', we can leave + * it as it is. + * + * The passed in 'doms_new' should be allocated using + * alloc_sched_domains. This routine takes ownership of it and will + * free_sched_domains it when done with it. If the caller failed the + * alloc call, then it can pass in doms_new == NULL && ndoms_new == 1, + * and partition_sched_domains() will fallback to the single partition + * 'fallback_doms', it also forces the domains to be rebuilt. + * + * If doms_new == NULL it will be replaced with cpu_online_mask. + * ndoms_new == 0 is a special case for destroying existing domains, + * and it will not create the default domain. + * + * Call with hotplug lock held + */ +void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[], + struct sched_domain_attr *dattr_new) +{ + int i, j, n; + int new_topology; + + mutex_lock(&sched_domains_mutex); + + /* always unregister in case we don't destroy any domains */ + unregister_sched_domain_sysctl(); + + /* Let architecture update cpu core mappings. */ + new_topology = arch_update_cpu_topology(); + + n = doms_new ? ndoms_new : 0; + + /* Destroy deleted domains */ + for (i = 0; i < ndoms_cur; i++) { + for (j = 0; j < n && !new_topology; j++) { + if (cpumask_equal(doms_cur[i], doms_new[j]) + && dattrs_equal(dattr_cur, i, dattr_new, j)) + goto match1; + } + /* no match - a current sched domain not in new doms_new[] */ + detach_destroy_domains(doms_cur[i]); +match1: + ; + } + + if (doms_new == NULL) { + ndoms_cur = 0; + doms_new = &fallback_doms; + cpumask_andnot(doms_new[0], cpu_active_mask, cpu_isolated_map); + WARN_ON_ONCE(dattr_new); + } + + /* Build new domains */ + for (i = 0; i < ndoms_new; i++) { + for (j = 0; j < ndoms_cur && !new_topology; j++) { + if (cpumask_equal(doms_new[i], doms_cur[j]) + && dattrs_equal(dattr_new, i, dattr_cur, j)) + goto match2; + } + /* no match - add a new doms_new */ + __build_sched_domains(doms_new[i], + dattr_new ? dattr_new + i : NULL); +match2: + ; + } + + /* Remember the new sched domains */ + if (doms_cur != &fallback_doms) + free_sched_domains(doms_cur, ndoms_cur); + kfree(dattr_cur); /* kfree(NULL) is safe */ + doms_cur = doms_new; + dattr_cur = dattr_new; + ndoms_cur = ndoms_new; + + register_sched_domain_sysctl(); + + mutex_unlock(&sched_domains_mutex); +} + +#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT) +static void arch_reinit_sched_domains(void) +{ + get_online_cpus(); + + /* Destroy domains first to force the rebuild */ + partition_sched_domains(0, NULL, NULL); + + rebuild_sched_domains(); + put_online_cpus(); +} + +static ssize_t sched_power_savings_store(const char *buf, size_t count, int smt) +{ + unsigned int level = 0; + + if (sscanf(buf, "%u", &level) != 1) + return -EINVAL; + + /* + * level is always be positive so don't check for + * level < POWERSAVINGS_BALANCE_NONE which is 0 + * What happens on 0 or 1 byte write, + * need to check for count as well? + */ + + if (level >= MAX_POWERSAVINGS_BALANCE_LEVELS) + return -EINVAL; + + if (smt) + sched_smt_power_savings = level; + else + sched_mc_power_savings = level; + + arch_reinit_sched_domains(); + + return count; +} + +#ifdef CONFIG_SCHED_MC +static ssize_t sched_mc_power_savings_show(struct sysdev_class *class, + struct sysdev_class_attribute *attr, + char *page) +{ + return sprintf(page, "%u\n", sched_mc_power_savings); +} +static ssize_t sched_mc_power_savings_store(struct sysdev_class *class, + struct sysdev_class_attribute *attr, + const char *buf, size_t count) +{ + return sched_power_savings_store(buf, count, 0); +} +static SYSDEV_CLASS_ATTR(sched_mc_power_savings, 0644, + sched_mc_power_savings_show, + sched_mc_power_savings_store); +#endif + +#ifdef CONFIG_SCHED_SMT +static ssize_t sched_smt_power_savings_show(struct sysdev_class *dev, + struct sysdev_class_attribute *attr, + char *page) +{ + return sprintf(page, "%u\n", sched_smt_power_savings); +} +static ssize_t sched_smt_power_savings_store(struct sysdev_class *dev, + struct sysdev_class_attribute *attr, + const char *buf, size_t count) +{ + return sched_power_savings_store(buf, count, 1); +} +static SYSDEV_CLASS_ATTR(sched_smt_power_savings, 0644, + sched_smt_power_savings_show, + sched_smt_power_savings_store); +#endif + +int __init sched_create_sysfs_power_savings_entries(struct sysdev_class *cls) +{ + int err = 0; + +#ifdef CONFIG_SCHED_SMT + if (smt_capable()) + err = sysfs_create_file(&cls->kset.kobj, + &attr_sched_smt_power_savings.attr); +#endif +#ifdef CONFIG_SCHED_MC + if (!err && mc_capable()) + err = sysfs_create_file(&cls->kset.kobj, + &attr_sched_mc_power_savings.attr); +#endif + return err; +} +#endif /* CONFIG_SCHED_MC || CONFIG_SCHED_SMT */ + +/* + * Update cpusets according to cpu_active mask. If cpusets are + * disabled, cpuset_update_active_cpus() becomes a simple wrapper + * around partition_sched_domains(). + */ +static int cpuset_cpu_active(struct notifier_block *nfb, unsigned long action, + void *hcpu) +{ + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_ONLINE: + case CPU_DOWN_FAILED: + cpuset_update_active_cpus(); + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} + +static int cpuset_cpu_inactive(struct notifier_block *nfb, unsigned long action, + void *hcpu) +{ + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_DOWN_PREPARE: + cpuset_update_active_cpus(); + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} + +static int update_runtime(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + switch (action) { + case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: + return NOTIFY_OK; + + case CPU_DOWN_FAILED: + case CPU_DOWN_FAILED_FROZEN: + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + return NOTIFY_OK; + + default: + return NOTIFY_DONE; + } +} + +#if defined(CONFIG_SCHED_SMT) || defined(CONFIG_SCHED_MC) +/* + * Cheaper version of the below functions in case support for SMT and MC is + * compiled in but CPUs have no siblings. + */ +static int sole_cpu_idle(unsigned long cpu) +{ + return rq_idle(cpu_rq(cpu)); +} +#endif +#ifdef CONFIG_SCHED_SMT +/* All this CPU's SMT siblings are idle */ +static int siblings_cpu_idle(unsigned long cpu) +{ + return cpumask_subset(&(cpu_rq(cpu)->smt_siblings), + &grq.cpu_idle_map); +} +#endif +#ifdef CONFIG_SCHED_MC +/* All this CPU's shared cache siblings are idle */ +static int cache_cpu_idle(unsigned long cpu) +{ + return cpumask_subset(&(cpu_rq(cpu)->cache_siblings), + &grq.cpu_idle_map); +} +#endif + +void __init sched_init_smp(void) +{ + struct sched_domain *sd; + int cpu; + + cpumask_var_t non_isolated_cpus; + + alloc_cpumask_var(&non_isolated_cpus, GFP_KERNEL); + alloc_cpumask_var(&fallback_doms, GFP_KERNEL); + +#if defined(CONFIG_NUMA) + sched_group_nodes_bycpu = kzalloc(nr_cpu_ids * sizeof(void **), + GFP_KERNEL); + BUG_ON(sched_group_nodes_bycpu == NULL); +#endif + get_online_cpus(); + mutex_lock(&sched_domains_mutex); + arch_init_sched_domains(cpu_active_mask); + cpumask_andnot(non_isolated_cpus, cpu_possible_mask, cpu_isolated_map); + if (cpumask_empty(non_isolated_cpus)) + cpumask_set_cpu(smp_processor_id(), non_isolated_cpus); + mutex_unlock(&sched_domains_mutex); + put_online_cpus(); + + hotcpu_notifier(cpuset_cpu_active, CPU_PRI_CPUSET_ACTIVE); + hotcpu_notifier(cpuset_cpu_inactive, CPU_PRI_CPUSET_INACTIVE); + + /* RT runtime code needs to handle some hotplug events */ + hotcpu_notifier(update_runtime, 0); + + /* Move init over to a non-isolated CPU */ + if (set_cpus_allowed_ptr(current, non_isolated_cpus) < 0) + BUG(); + free_cpumask_var(non_isolated_cpus); + + grq_lock_irq(); + /* + * Set up the relative cache distance of each online cpu from each + * other in a simple array for quick lookup. Locality is determined + * by the closest sched_domain that CPUs are separated by. CPUs with + * shared cache in SMT and MC are treated as local. Separate CPUs + * (within the same package or physically) within the same node are + * treated as not local. CPUs not even in the same domain (different + * nodes) are treated as very distant. + */ + for_each_online_cpu(cpu) { + struct rq *rq = cpu_rq(cpu); + for_each_domain(cpu, sd) { + unsigned long locality; + int other_cpu; + +#ifdef CONFIG_SCHED_SMT + if (sd->level == SD_LV_SIBLING) { + for_each_cpu_mask(other_cpu, *sched_domain_span(sd)) + cpumask_set_cpu(other_cpu, &rq->smt_siblings); + } +#endif +#ifdef CONFIG_SCHED_MC + if (sd->level == SD_LV_MC) { + for_each_cpu_mask(other_cpu, *sched_domain_span(sd)) + cpumask_set_cpu(other_cpu, &rq->cache_siblings); + } +#endif + if (sd->level <= SD_LV_SIBLING) + locality = 1; + else if (sd->level <= SD_LV_MC) + locality = 2; + else if (sd->level <= SD_LV_NODE) + locality = 3; + else + continue; + + for_each_cpu_mask(other_cpu, *sched_domain_span(sd)) { + if (locality < rq->cpu_locality[other_cpu]) + rq->cpu_locality[other_cpu] = locality; + } + } + +/* + * Each runqueue has its own function in case it doesn't have + * siblings of its own allowing mixed topologies. + */ +#ifdef CONFIG_SCHED_SMT + if (cpus_weight(rq->smt_siblings) > 1) + rq->siblings_idle = siblings_cpu_idle; +#endif +#ifdef CONFIG_SCHED_MC + if (cpus_weight(rq->cache_siblings) > 1) + rq->cache_idle = cache_cpu_idle; +#endif + } + grq_unlock_irq(); +} +#else +void __init sched_init_smp(void) +{ +} +#endif /* CONFIG_SMP */ + +unsigned int sysctl_timer_migration = 1; + +int in_sched_functions(unsigned long addr) +{ + return in_lock_functions(addr) || + (addr >= (unsigned long)__sched_text_start + && addr < (unsigned long)__sched_text_end); +} + +void __init sched_init(void) +{ + int i; + struct rq *rq; + + prio_ratios[0] = 128; + for (i = 1 ; i < PRIO_RANGE ; i++) + prio_ratios[i] = prio_ratios[i - 1] * 11 / 10; + + raw_spin_lock_init(&grq.lock); + grq.nr_running = grq.nr_uninterruptible = grq.nr_switches = 0; + grq.niffies = 0; + grq.last_jiffy = jiffies; + raw_spin_lock_init(&grq.iso_lock); + grq.iso_ticks = grq.iso_refractory = 0; + grq.noc = 1; +#ifdef CONFIG_SMP + init_defrootdomain(); + grq.qnr = grq.idle_cpus = 0; + cpumask_clear(&grq.cpu_idle_map); +#else + uprq = &per_cpu(runqueues, 0); +#endif + for_each_possible_cpu(i) { + rq = cpu_rq(i); + rq->user_pc = rq->nice_pc = rq->softirq_pc = rq->system_pc = + rq->iowait_pc = rq->idle_pc = 0; + rq->dither = 0; +#ifdef CONFIG_SMP + rq->sticky_task = NULL; + rq->last_niffy = 0; + rq->sd = NULL; + rq->rd = NULL; + rq->online = 0; + rq->cpu = i; + rq_attach_root(rq, &def_root_domain); +#endif + atomic_set(&rq->nr_iowait, 0); + } + +#ifdef CONFIG_SMP + nr_cpu_ids = i; + /* + * Set the base locality for cpu cache distance calculation to + * "distant" (3). Make sure the distance from a CPU to itself is 0. + */ + for_each_possible_cpu(i) { + int j; + + rq = cpu_rq(i); +#ifdef CONFIG_SCHED_SMT + cpumask_clear(&rq->smt_siblings); + cpumask_set_cpu(i, &rq->smt_siblings); + rq->siblings_idle = sole_cpu_idle; + cpumask_set_cpu(i, &rq->smt_siblings); +#endif +#ifdef CONFIG_SCHED_MC + cpumask_clear(&rq->cache_siblings); + cpumask_set_cpu(i, &rq->cache_siblings); + rq->cache_idle = sole_cpu_idle; + cpumask_set_cpu(i, &rq->cache_siblings); +#endif + rq->cpu_locality = kmalloc(nr_cpu_ids * sizeof(unsigned long), + GFP_NOWAIT); + for_each_possible_cpu(j) { + if (i == j) + rq->cpu_locality[j] = 0; + else + rq->cpu_locality[j] = 4; + } + } +#endif + + for (i = 0; i < PRIO_LIMIT; i++) + INIT_LIST_HEAD(grq.queue + i); + /* delimiter for bitsearch */ + __set_bit(PRIO_LIMIT, grq.prio_bitmap); + +#ifdef CONFIG_PREEMPT_NOTIFIERS + INIT_HLIST_HEAD(&init_task.preempt_notifiers); +#endif + +#ifdef CONFIG_RT_MUTEXES + plist_head_init_raw(&init_task.pi_waiters, &init_task.pi_lock); +#endif + + /* + * The boot idle thread does lazy MMU switching as well: + */ + atomic_inc(&init_mm.mm_count); + enter_lazy_tlb(&init_mm, current); + + /* + * Make us the idle thread. Technically, schedule() should not be + * called from this thread, however somewhere below it might be, + * but because we are the idle thread, we just pick up running again + * when this runqueue becomes "idle". + */ + init_idle(current, smp_processor_id()); + + /* Allocate the nohz_cpu_mask if CONFIG_CPUMASK_OFFSTACK */ + zalloc_cpumask_var(&nohz_cpu_mask, GFP_NOWAIT); +#ifdef CONFIG_SMP + /* May be allocated at isolcpus cmdline parse time */ + if (cpu_isolated_map == NULL) + zalloc_cpumask_var(&cpu_isolated_map, GFP_NOWAIT); +#endif /* SMP */ +} + +#ifdef CONFIG_DEBUG_SPINLOCK_SLEEP +static inline int preempt_count_equals(int preempt_offset) +{ + int nested = (preempt_count() & ~PREEMPT_ACTIVE) + rcu_preempt_depth(); + + return (nested == PREEMPT_INATOMIC_BASE + preempt_offset); +} + +void __might_sleep(const char *file, int line, int preempt_offset) +{ +#ifdef in_atomic + static unsigned long prev_jiffy; /* ratelimiting */ + + if ((preempt_count_equals(preempt_offset) && !irqs_disabled()) || + system_state != SYSTEM_RUNNING || oops_in_progress) + return; + if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy) + return; + prev_jiffy = jiffies; + + printk(KERN_ERR + "BUG: sleeping function called from invalid context at %s:%d\n", + file, line); + printk(KERN_ERR + "in_atomic(): %d, irqs_disabled(): %d, pid: %d, name: %s\n", + in_atomic(), irqs_disabled(), + current->pid, current->comm); + + debug_show_held_locks(current); + if (irqs_disabled()) + print_irqtrace_events(current); + dump_stack(); +#endif +} +EXPORT_SYMBOL(__might_sleep); +#endif + +#ifdef CONFIG_MAGIC_SYSRQ +void normalize_rt_tasks(void) +{ + struct task_struct *g, *p; + unsigned long flags; + struct rq *rq; + int queued; + + read_lock_irq(&tasklist_lock); + + do_each_thread(g, p) { + if (!rt_task(p) && !iso_task(p)) + continue; + + raw_spin_lock_irqsave(&p->pi_lock, flags); + rq = __task_grq_lock(p); + + queued = task_queued(p); + if (queued) + dequeue_task(p); + __setscheduler(p, rq, SCHED_NORMAL, 0); + if (queued) { + enqueue_task(p); + try_preempt(p, rq); + } + + __task_grq_unlock(); + raw_spin_unlock_irqrestore(&p->pi_lock, flags); + } while_each_thread(g, p); + + read_unlock_irq(&tasklist_lock); +} +#endif /* CONFIG_MAGIC_SYSRQ */ + +#if defined(CONFIG_IA64) || defined(CONFIG_KGDB_KDB) +/* + * These functions are only useful for the IA64 MCA handling, or kdb. + * + * They can only be called when the whole system has been + * stopped - every CPU needs to be quiescent, and no scheduling + * activity can take place. Using them for anything else would + * be a serious bug, and as a result, they aren't even visible + * under any other configuration. + */ + +/** + * curr_task - return the current task for a given cpu. + * @cpu: the processor in question. + * + * ONLY VALID WHEN THE WHOLE SYSTEM IS STOPPED! + */ +struct task_struct *curr_task(int cpu) +{ + return cpu_curr(cpu); +} + +#endif /* defined(CONFIG_IA64) || defined(CONFIG_KGDB_KDB) */ + +#ifdef CONFIG_IA64 +/** + * set_curr_task - set the current task for a given cpu. + * @cpu: the processor in question. + * @p: the task pointer to set. + * + * Description: This function must only be used when non-maskable interrupts + * are serviced on a separate stack. It allows the architecture to switch the + * notion of the current task on a cpu in a non-blocking manner. This function + * must be called with all CPU's synchronised, and interrupts disabled, the + * and caller must save the original value of the current task (see + * curr_task() above) and restore that value before reenabling interrupts and + * re-starting the system. + * + * ONLY VALID WHEN THE WHOLE SYSTEM IS STOPPED! + */ +void set_curr_task(int cpu, struct task_struct *p) +{ + cpu_curr(cpu) = p; +} + +#endif + +/* + * Use precise platform statistics if available: + */ +#ifdef CONFIG_VIRT_CPU_ACCOUNTING +void task_times(struct task_struct *p, cputime_t *ut, cputime_t *st) +{ + *ut = p->utime; + *st = p->stime; +} + +void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *st) +{ + struct task_cputime cputime; + + thread_group_cputime(p, &cputime); + + *ut = cputime.utime; + *st = cputime.stime; +} +#else + +#ifndef nsecs_to_cputime +# define nsecs_to_cputime(__nsecs) nsecs_to_jiffies(__nsecs) +#endif + +void task_times(struct task_struct *p, cputime_t *ut, cputime_t *st) +{ + cputime_t rtime, utime = p->utime, total = cputime_add(utime, p->stime); + + rtime = nsecs_to_cputime(p->sched_time); + + if (total) { + u64 temp; + + temp = (u64)(rtime * utime); + do_div(temp, total); + utime = (cputime_t)temp; + } else + utime = rtime; + + /* + * Compare with previous values, to keep monotonicity: + */ + p->prev_utime = max(p->prev_utime, utime); + p->prev_stime = max(p->prev_stime, cputime_sub(rtime, p->prev_utime)); + + *ut = p->prev_utime; + *st = p->prev_stime; +} + +/* + * Must be called with siglock held. + */ +void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *st) +{ + struct signal_struct *sig = p->signal; + struct task_cputime cputime; + cputime_t rtime, utime, total; + + thread_group_cputime(p, &cputime); + + total = cputime_add(cputime.utime, cputime.stime); + rtime = nsecs_to_cputime(cputime.sum_exec_runtime); + + if (total) { + u64 temp; + + temp = (u64)(rtime * cputime.utime); + do_div(temp, total); + utime = (cputime_t)temp; + } else + utime = rtime; + + sig->prev_utime = max(sig->prev_utime, utime); + sig->prev_stime = max(sig->prev_stime, + cputime_sub(rtime, sig->prev_utime)); + + *ut = sig->prev_utime; + *st = sig->prev_stime; +} +#endif + +inline cputime_t task_gtime(struct task_struct *p) +{ + return p->gtime; +} + +void __cpuinit init_idle_bootup_task(struct task_struct *idle) +{} + +#ifdef CONFIG_SCHED_DEBUG +void proc_sched_show_task(struct task_struct *p, struct seq_file *m) +{} + +void proc_sched_set_task(struct task_struct *p) +{} +#endif + +#ifdef CONFIG_SMP +unsigned long default_scale_freq_power(struct sched_domain *sd, int cpu) +{ + return SCHED_LOAD_SCALE; +} + +unsigned long default_scale_smt_power(struct sched_domain *sd, int cpu) +{ + unsigned long weight = cpumask_weight(sched_domain_span(sd)); + unsigned long smt_gain = sd->smt_gain; + + smt_gain /= weight; + + return smt_gain; +} +#endif diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 99aed9f8ad35c..7fc892ecbe73a 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -120,7 +120,12 @@ static int zero; static int __maybe_unused one = 1; static int __maybe_unused two = 2; static unsigned long one_ul = 1; -static int one_hundred = 100; +static int __maybe_unused one_hundred = 100; +#ifdef CONFIG_SCHED_BFS +extern int rr_interval; +extern int sched_iso_cpu; +static int __read_mostly one_thousand = 1000; +#endif #ifdef CONFIG_PRINTK static int ten_thousand = 10000; #endif @@ -257,7 +262,7 @@ static struct ctl_table root_table[] = { { } }; -#ifdef CONFIG_SCHED_DEBUG +#if defined(CONFIG_SCHED_DEBUG) && !defined(CONFIG_SCHED_BFS) static int min_sched_granularity_ns = 100000; /* 100 usecs */ static int max_sched_granularity_ns = NSEC_PER_SEC; /* 1 second */ static int min_wakeup_granularity_ns; /* 0 usecs */ @@ -272,6 +277,7 @@ static int max_extfrag_threshold = 1000; #endif static struct ctl_table kern_table[] = { +#ifndef CONFIG_SCHED_BFS { .procname = "sched_child_runs_first", .data = &sysctl_sched_child_runs_first, @@ -386,6 +392,7 @@ static struct ctl_table kern_table[] = { .extra2 = &one, }, #endif +#endif /* !CONFIG_SCHED_BFS */ #ifdef CONFIG_PROVE_LOCKING { .procname = "prove_locking", @@ -828,6 +835,26 @@ static struct ctl_table kern_table[] = { .proc_handler = proc_dointvec, }, #endif +#ifdef CONFIG_SCHED_BFS + { + .procname = "rr_interval", + .data = &rr_interval, + .maxlen = sizeof (int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .extra1 = &one, + .extra2 = &one_thousand, + }, + { + .procname = "iso_cpu", + .data = &sched_iso_cpu, + .maxlen = sizeof (int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one_hundred, + }, +#endif #if defined(CONFIG_S390) && defined(CONFIG_SMP) { .procname = "spin_retry", diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 7909c96e4da6b..60942f8f0b219 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -835,7 +835,7 @@ config BOOT_PRINTK_DELAY config RCU_TORTURE_TEST tristate "torture tests for RCU" - depends on DEBUG_KERNEL + depends on DEBUG_KERNEL && !SCHED_BFS default n help This option provides a kernel module that runs torture tests From e984739bda1e308ac806cb674bb52e85103bb648 Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 23:08:26 -0500 Subject: [PATCH 2531/2556] Tweak BFS --- kernel/sched_fair.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index ee0cf650cb423..e7787a1edb87a 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c @@ -84,8 +84,8 @@ unsigned int __read_mostly sysctl_sched_compat_yield; * and reduces their over-scheduling. Synchronous workloads will still * have immediate wakeup/sleep latencies. */ -unsigned int sysctl_sched_wakeup_granularity = 1000000UL; -unsigned int normalized_sysctl_sched_wakeup_granularity = 1000000UL; +unsigned int sysctl_sched_wakeup_granularity = 25000UL; +unsigned int normalized_sysctl_sched_wakeup_granularity = 25000UL; const_debug unsigned int sysctl_sched_migration_cost = 500000UL; From 5609f4403bd45569a5959ac8f40e0d8ea7efa291 Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 23:17:09 -0500 Subject: [PATCH 2532/2556] Increase readahead buffer --- include/linux/mm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index a021c046e7743..89839c7d25b6a 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1490,7 +1490,7 @@ int write_one_page(struct page *page, int wait); void task_dirty_inc(struct task_struct *tsk); /* readahead.c */ -#define VM_MAX_READAHEAD 128 /* kbytes */ +#define VM_MAX_READAHEAD 2048 /* kbytes */ #define VM_MIN_READAHEAD 16 /* kbytes (includes current page) */ int force_page_cache_readahead(struct address_space *mapping, struct file *filp, From 29febc060e05345e6621f38108401f30e772d100 Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 23:10:13 -0500 Subject: [PATCH 2533/2556] Adjust VM ratios --- mm/page-writeback.c | 2 +- mm/vmscan.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 2cb01f6ec5d01..82e523f5dadd3 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -78,7 +78,7 @@ int vm_highmem_is_dirtyable; /* * The generator of dirty data starts writeback at this percentage */ -int vm_dirty_ratio = 20; +int vm_dirty_ratio = 10; /* * vm_dirty_bytes starts at 0 (disabled) so that it is a function of diff --git a/mm/vmscan.c b/mm/vmscan.c index 3c94e79c6cf52..4f3f059d92b69 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -147,7 +147,7 @@ struct scan_control { /* * From 0 .. 100. Higher means more swappy. */ -int vm_swappiness = 60; +int vm_swappiness = 90; long vm_total_pages; /* The total number of pages which the VM controls */ static LIST_HEAD(shrinker_list); From 3b009267d2ef820e61b10d29c33c42d4ec9a961a Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 23:40:56 -0500 Subject: [PATCH 2534/2556] Fix fixup_user_fault for MMU=n --- include/linux/mm.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/linux/mm.h b/include/linux/mm.h index 89839c7d25b6a..fe144f570a33c 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -960,6 +960,8 @@ int invalidate_inode_page(struct page *page); #ifdef CONFIG_MMU extern int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, unsigned int flags); +extern int fixup_user_fault(struct task_struct *tsk, struct mm_struct *mm, + unsigned long address, unsigned int fault_flags); #else static inline int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, @@ -969,6 +971,14 @@ static inline int handle_mm_fault(struct mm_struct *mm, BUG(); return VM_FAULT_SIGBUS; } +static inline int fixup_user_fault(struct task_struct *tsk, + struct mm_struct *mm, unsigned long address, + unsigned int fault_flags) +{ + /* should never happen if there's no MMU */ + BUG(); + return -EFAULT; +} #endif extern int make_pages_present(unsigned long addr, unsigned long end); From e65f12f101b2e4b0e7e6f924bc6a2d090ad0cb6f Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 23:33:25 -0500 Subject: [PATCH 2535/2556] Flush disk cache when merging --- drivers/md/dm-snap-persistent.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c index 95891dfcbca02..4c20f3c3366a6 100644 --- a/drivers/md/dm-snap-persistent.c +++ b/drivers/md/dm-snap-persistent.c @@ -764,7 +764,7 @@ static int persistent_commit_merge(struct dm_exception_store *store, for (i = 0; i < nr_merged; i++) clear_exception(ps, ps->current_committed - 1 - i); - r = area_io(ps, WRITE); + r = area_io(ps, WRITE_FLUSH_FUA); if (r < 0) return r; From 8225131daf42ee19df42e56832d810d3fa5fefaa Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 23:18:46 -0500 Subject: [PATCH 2536/2556] Adjust VFS pressure --- fs/dcache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/dcache.c b/fs/dcache.c index 9bb566d0b6db2..407cb296c4e14 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -75,7 +75,7 @@ * dentry1->d_lock * dentry2->d_lock */ -int sysctl_vfs_cache_pressure __read_mostly = 100; +int sysctl_vfs_cache_pressure __read_mostly = 10; EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure); static __cacheline_aligned_in_smp DEFINE_SPINLOCK(dcache_lru_lock); From 6998c627402273d62a1b60aa74f1f29b543db5e5 Mon Sep 17 00:00:00 2001 From: Zach Date: Thu, 10 Apr 2014 17:51:05 -0400 Subject: [PATCH 2537/2556] Fix BFS --- arch/arm/mach-msm/dma.c | 2 +- drivers/mmc/core/core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/dma.c b/arch/arm/mach-msm/dma.c index 8ad7f04494f30..d6650f66b086a 100644 --- a/arch/arm/mach-msm/dma.c +++ b/arch/arm/mach-msm/dma.c @@ -151,7 +151,7 @@ int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr) init_completion(&cmd.complete); msm_dmov_enqueue_cmd(id, &cmd.dmov_cmd); - wait_for_completion_io(&cmd.complete); + wait_for_completion(&cmd.complete); if (cmd.result != 0x80000002) { PRINT_ERROR("dmov_exec_cmdptr(%d): ERROR, result: %x\n", id, cmd.result); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 5094008e82e1c..74c7ece9d31ee 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -243,7 +243,7 @@ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) msmsdcc_request_end(msm_host, mrq); } #else - wait_for_completion_io(&complete); + wait_for_completion(&complete); #endif } From bf84bf9ba4294d3dfd46da58bbbb95ba4025aa85 Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 23:31:14 -0500 Subject: [PATCH 2538/2556] Avoid corrupting structure while waiting for rcu_free --- drivers/md/linear.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/linear.h b/drivers/md/linear.h index 0ce29b61605a2..2f2da05b2ce9c 100644 --- a/drivers/md/linear.h +++ b/drivers/md/linear.h @@ -10,9 +10,9 @@ typedef struct dev_info dev_info_t; struct linear_private_data { + struct rcu_head rcu; sector_t array_sectors; dev_info_t disks[0]; - struct rcu_head rcu; }; From e8f738795ba7f970764771fc905de79d5c3cfb29 Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 23:35:38 -0500 Subject: [PATCH 2539/2556] Fix IDR leak on module removal --- drivers/md/dm.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index eaa3af0e0632a..7e9c2c39ecf00 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -37,6 +37,8 @@ static const char *_name = DM_NAME; static unsigned int major = 0; static unsigned int _major = 0; +static DEFINE_IDR(_minor_idr); + static DEFINE_SPINLOCK(_minor_lock); /* * For bio-based dm. @@ -313,6 +315,12 @@ static void __exit dm_exit(void) while (i--) _exits[i](); + + /* + * Should be empty by this point. + */ + idr_remove_all(&_minor_idr); + idr_destroy(&_minor_idr); } /* @@ -1723,8 +1731,6 @@ static int dm_any_congested(void *congested_data, int bdi_bits) /*----------------------------------------------------------------- * An IDR is used to keep track of allocated minor numbers. *---------------------------------------------------------------*/ -static DEFINE_IDR(_minor_idr); - static void free_minor(int minor) { spin_lock(&_minor_lock); From e760a221a3ac230bf92e3cf7734dc30ded841ada Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 23:38:47 -0500 Subject: [PATCH 2540/2556] Fix EXT4 should writeback data for non journal mode --- fs/ext4/ext4_jbd2.h | 4 ++-- fs/ext4/inode.c | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h index d8b992e658c15..be8c636289872 100644 --- a/fs/ext4/ext4_jbd2.h +++ b/fs/ext4/ext4_jbd2.h @@ -301,10 +301,10 @@ static inline int ext4_should_order_data(struct inode *inode) static inline int ext4_should_writeback_data(struct inode *inode) { - if (!S_ISREG(inode->i_mode)) - return 0; if (EXT4_JOURNAL(inode) == NULL) return 1; + if (!S_ISREG(inode->i_mode)) + return 0; if (ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA)) return 0; if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_WRITEBACK_DATA) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 5034aa1184f9b..bd1e6acff2147 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1846,6 +1846,8 @@ static int ext4_journalled_write_end(struct file *file, from = pos & (PAGE_CACHE_SIZE - 1); to = from + len; + BUG_ON(!ext4_handle_valid(handle)); + if (copied < len) { if (!PageUptodate(page)) copied = 0; @@ -2659,6 +2661,8 @@ static int __ext4_journalled_writepage(struct page *page, goto out; } + BUG_ON(!ext4_handle_valid(handle)); + ret = walk_page_buffers(handle, page_bufs, 0, len, NULL, do_journal_get_write_access); From 679e7d78d9b0e64782612891f3be33df74bf4554 Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 23:43:23 -0500 Subject: [PATCH 2541/2556] Fix oops in ext3_try_to_allocate_with_rsv --- fs/ext3/xattr.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/fs/ext3/xattr.c b/fs/ext3/xattr.c index 32e6cc23bd9ad..d565759d82eee 100644 --- a/fs/ext3/xattr.c +++ b/fs/ext3/xattr.c @@ -803,8 +803,16 @@ ext3_xattr_block_set(handle_t *handle, struct inode *inode, /* We need to allocate a new block */ ext3_fsblk_t goal = ext3_group_first_block_no(sb, EXT3_I(inode)->i_block_group); - ext3_fsblk_t block = ext3_new_block(handle, inode, - goal, &error); + ext3_fsblk_t block; + + /* + * Protect us agaist concurrent allocations to the + * same inode from ext3_..._writepage(). Reservation + * code does not expect racing allocations. + */ + mutex_lock(&EXT3_I(inode)->truncate_mutex); + block = ext3_new_block(handle, inode, goal, &error); + mutex_unlock(&EXT3_I(inode)->truncate_mutex); if (error) goto cleanup; ea_idebug(inode, "creating block %d", block); From 3190d29d5a75fbda25141d737bf1775201898329 Mon Sep 17 00:00:00 2001 From: Zach Date: Thu, 10 Apr 2014 18:47:44 -0400 Subject: [PATCH 2542/2556] incrediblec: OC Farther (EXTREAMLY EXPERIMENTAL) --- arch/arm/mach-msm/acpuclock-qsd8x50.c | 12 ++++++++++++ arch/arm/mach-msm/avs.c | 9 +++++++++ arch/arm/mach-msm/avs.h | 4 ++-- arch/arm/mach-msm/board-incrediblec.h | 3 +++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/acpuclock-qsd8x50.c b/arch/arm/mach-msm/acpuclock-qsd8x50.c index 69acc9021016e..f57ff6a931c8a 100644 --- a/arch/arm/mach-msm/acpuclock-qsd8x50.c +++ b/arch/arm/mach-msm/acpuclock-qsd8x50.c @@ -114,6 +114,18 @@ struct clkctl_acpu_speed acpu_freq_tbl[] = { { 1113600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1D, 0, 1275, 128000 }, { 1152000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1E, 0, 1325, 128000 }, { 1190400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1F, 0, 1325, 128000 }, + { 1200000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x20, 0, 1325, 128000 }, + { 1250000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x21, 0, 1325, 128000 }, + { 1300000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x22, 0, 1325, 128000 }, + { 1350000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x23, 0, 1325, 128000 }, + { 1400000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x24, 0, 1325, 128000 }, + { 1450000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x25, 0, 1325, 128000 }, + { 1500000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x26, 0, 1325, 128000 }, + { 1550000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x27, 0, 1350, 128000 }, + { 1600000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x28, 0, 1350, 128000 }, + { 1650000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x29, 0, 1350, 128000 }, + { 1700000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x2A, 0, 1350, 128000 }, + { 1750000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x2B, 0, 1350, 128000 }, { 0 }, }; diff --git a/arch/arm/mach-msm/avs.c b/arch/arm/mach-msm/avs.c index ab857f644a34d..72081f4aad801 100644 --- a/arch/arm/mach-msm/avs.c +++ b/arch/arm/mach-msm/avs.c @@ -122,6 +122,15 @@ struct clkctl_acpu_speed acpu_vdd_tbl[] = { { 1113600, 1275, 1275 }, { 1152000, 1300, VOLTAGE_MAX }, { 1190400, 1300, VOLTAGE_MAX }, + { 1350000, 1325, VOLTAGE_MAX }, + { 1400000, 1325, VOLTAGE_MAX }, + { 1450000, 1325, VOLTAGE_MAX }, + { 1500000, 1325, VOLTAGE_MAX }, + { 1550000, 1350, VOLTAGE_MAX }, + { 1600000, 1350, VOLTAGE_MAX }, + { 1650000, 1350, VOLTAGE_MAX }, + { 1700000, 1350, VOLTAGE_MAX }, + { 1750000, 1350, VOLTAGE_MAX }, { 0 }, }; diff --git a/arch/arm/mach-msm/avs.h b/arch/arm/mach-msm/avs.h index 4e6898359842c..990f7b4b8ac5f 100644 --- a/arch/arm/mach-msm/avs.h +++ b/arch/arm/mach-msm/avs.h @@ -30,8 +30,8 @@ #ifndef AVS_H #define AVS_H -#define VOLTAGE_MIN 950 /* mV */ -#define VOLTAGE_MAX 1275 +#define VOLTAGE_MIN 1000 /* mV */ +#define VOLTAGE_MAX 1325 #define VOLTAGE_STEP 5 int __init avs_init(int (*set_vdd)(int), u32 freq_cnt, u32 freq_idx); diff --git a/arch/arm/mach-msm/board-incrediblec.h b/arch/arm/mach-msm/board-incrediblec.h index 14fb432ad8ecf..c5ef7591f2265 100644 --- a/arch/arm/mach-msm/board-incrediblec.h +++ b/arch/arm/mach-msm/board-incrediblec.h @@ -18,6 +18,9 @@ #include +#define INCREDIBLEC_MIN_UV_MV (950) +#define INCREDIBLEC_MAX_UV_MV (1375) + #define MSM_SMI_BASE 0x02B00000 #define MSM_SMI_SIZE 0x01500000 From 5445e45ae1efb352ddb635ecc43724bac1f18a4d Mon Sep 17 00:00:00 2001 From: Zach Date: Thu, 10 Apr 2014 20:33:00 -0400 Subject: [PATCH 2543/2556] Revert "incrediblec: OC Farther (EXTREAMLY EXPERIMENTAL)" This reverts commit 3190d29d5a75fbda25141d737bf1775201898329. --- arch/arm/mach-msm/acpuclock-qsd8x50.c | 12 ------------ arch/arm/mach-msm/avs.c | 9 --------- arch/arm/mach-msm/avs.h | 4 ++-- arch/arm/mach-msm/board-incrediblec.h | 3 --- 4 files changed, 2 insertions(+), 26 deletions(-) diff --git a/arch/arm/mach-msm/acpuclock-qsd8x50.c b/arch/arm/mach-msm/acpuclock-qsd8x50.c index f57ff6a931c8a..69acc9021016e 100644 --- a/arch/arm/mach-msm/acpuclock-qsd8x50.c +++ b/arch/arm/mach-msm/acpuclock-qsd8x50.c @@ -114,18 +114,6 @@ struct clkctl_acpu_speed acpu_freq_tbl[] = { { 1113600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1D, 0, 1275, 128000 }, { 1152000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1E, 0, 1325, 128000 }, { 1190400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1F, 0, 1325, 128000 }, - { 1200000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x20, 0, 1325, 128000 }, - { 1250000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x21, 0, 1325, 128000 }, - { 1300000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x22, 0, 1325, 128000 }, - { 1350000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x23, 0, 1325, 128000 }, - { 1400000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x24, 0, 1325, 128000 }, - { 1450000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x25, 0, 1325, 128000 }, - { 1500000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x26, 0, 1325, 128000 }, - { 1550000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x27, 0, 1350, 128000 }, - { 1600000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x28, 0, 1350, 128000 }, - { 1650000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x29, 0, 1350, 128000 }, - { 1700000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x2A, 0, 1350, 128000 }, - { 1750000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x2B, 0, 1350, 128000 }, { 0 }, }; diff --git a/arch/arm/mach-msm/avs.c b/arch/arm/mach-msm/avs.c index 72081f4aad801..ab857f644a34d 100644 --- a/arch/arm/mach-msm/avs.c +++ b/arch/arm/mach-msm/avs.c @@ -122,15 +122,6 @@ struct clkctl_acpu_speed acpu_vdd_tbl[] = { { 1113600, 1275, 1275 }, { 1152000, 1300, VOLTAGE_MAX }, { 1190400, 1300, VOLTAGE_MAX }, - { 1350000, 1325, VOLTAGE_MAX }, - { 1400000, 1325, VOLTAGE_MAX }, - { 1450000, 1325, VOLTAGE_MAX }, - { 1500000, 1325, VOLTAGE_MAX }, - { 1550000, 1350, VOLTAGE_MAX }, - { 1600000, 1350, VOLTAGE_MAX }, - { 1650000, 1350, VOLTAGE_MAX }, - { 1700000, 1350, VOLTAGE_MAX }, - { 1750000, 1350, VOLTAGE_MAX }, { 0 }, }; diff --git a/arch/arm/mach-msm/avs.h b/arch/arm/mach-msm/avs.h index 990f7b4b8ac5f..4e6898359842c 100644 --- a/arch/arm/mach-msm/avs.h +++ b/arch/arm/mach-msm/avs.h @@ -30,8 +30,8 @@ #ifndef AVS_H #define AVS_H -#define VOLTAGE_MIN 1000 /* mV */ -#define VOLTAGE_MAX 1325 +#define VOLTAGE_MIN 950 /* mV */ +#define VOLTAGE_MAX 1275 #define VOLTAGE_STEP 5 int __init avs_init(int (*set_vdd)(int), u32 freq_cnt, u32 freq_idx); diff --git a/arch/arm/mach-msm/board-incrediblec.h b/arch/arm/mach-msm/board-incrediblec.h index c5ef7591f2265..14fb432ad8ecf 100644 --- a/arch/arm/mach-msm/board-incrediblec.h +++ b/arch/arm/mach-msm/board-incrediblec.h @@ -18,9 +18,6 @@ #include -#define INCREDIBLEC_MIN_UV_MV (950) -#define INCREDIBLEC_MAX_UV_MV (1375) - #define MSM_SMI_BASE 0x02B00000 #define MSM_SMI_SIZE 0x01500000 From a4fd8870b9f3c4a5272c349d0aaefd3b06afee4e Mon Sep 17 00:00:00 2001 From: Zach Date: Thu, 10 Apr 2014 21:04:07 -0400 Subject: [PATCH 2544/2556] Enable BFS --- arch/arm/configs/incrediblec-incredikernel_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/incrediblec-incredikernel_defconfig b/arch/arm/configs/incrediblec-incredikernel_defconfig index 656bdbe4ec8e2..8e14547e3cb9d 100644 --- a/arch/arm/configs/incrediblec-incredikernel_defconfig +++ b/arch/arm/configs/incrediblec-incredikernel_defconfig @@ -172,6 +172,7 @@ CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y CONFIG_IOSCHED_BFQ=y +CONFIG_SCHED_BFS=y # CONFIG_CGROUP_BFQIO is not set # CONFIG_DEFAULT_DEADLINE is not set CONFIG_DEFAULT_CFQ=y From 74324d0423fb868890fd6ecc4ac1c8b557dbf36f Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 23:55:13 -0500 Subject: [PATCH 2545/2556] Tweak GPU --- drivers/gpu/msm/adreno.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 4682604a3db63..eb6f47cea4af9 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1407,7 +1407,7 @@ static long adreno_ioctl(struct kgsl_device_private *dev_priv, static inline s64 adreno_ticks_to_us(u32 ticks, u32 gpu_freq) { - gpu_freq /= 1000000; + gpu_freq /= 2000000; return ticks / gpu_freq; } From dba71b7ebffa619eb999ad5bea6db49454232159 Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 23:46:24 -0500 Subject: [PATCH 2546/2556] Add missing kfree in ibmaem.c --- drivers/hwmon/ibmaem.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index bc6e2ab3a361d..82e3ef8f0996b 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -432,13 +432,15 @@ static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg, aem_send_message(ipmi); res = wait_for_completion_timeout(&ipmi->read_complete, IPMI_TIMEOUT); - if (!res) - return -ETIMEDOUT; + if (!res) { + res = -ETIMEDOUT; + goto out; + } if (ipmi->rx_result || ipmi->rx_msg_len != rs_size || memcmp(&rs_resp->id, &system_x_id, sizeof(system_x_id))) { - kfree(rs_resp); - return -ENOENT; + res = -ENOENT; + goto out; } switch (size) { @@ -463,8 +465,11 @@ static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg, break; } } + res = 0; - return 0; +out: + kfree(rs_resp); + return res; } /* Update AEM energy registers */ From e18af548f71af10ed3edbc2bd95cf0f7463d671a Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 23:48:34 -0500 Subject: [PATCH 2547/2556] add missing declaration of kgdb_init and breakpoint --- arch/cris/arch-v10/kernel/irq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/cris/arch-v10/kernel/irq.c b/arch/cris/arch-v10/kernel/irq.c index 7328a7cf7449b..3a5f7ddad962e 100644 --- a/arch/cris/arch-v10/kernel/irq.c +++ b/arch/cris/arch-v10/kernel/irq.c @@ -20,6 +20,9 @@ #define crisv10_mask_irq(irq_nr) (*R_VECT_MASK_CLR = 1 << (irq_nr)); #define crisv10_unmask_irq(irq_nr) (*R_VECT_MASK_SET = 1 << (irq_nr)); +extern void kgdb_init(void); +extern void breakpoint(void); + /* don't use set_int_vector, it bypasses the linux interrupt handlers. it is * global just so that the kernel gdb can use it. */ From a7c6d48052d824c30dbf46d3c2ae776481a2a62e Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 23:49:34 -0500 Subject: [PATCH 2548/2556] fix return type of __atomic64_add_return --- arch/parisc/include/asm/atomic.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/parisc/include/asm/atomic.h b/arch/parisc/include/asm/atomic.h index f81955934aebf..26fd1146dda6a 100644 --- a/arch/parisc/include/asm/atomic.h +++ b/arch/parisc/include/asm/atomic.h @@ -259,10 +259,10 @@ static __inline__ int atomic_add_unless(atomic_t *v, int a, int u) #define ATOMIC64_INIT(i) ((atomic64_t) { (i) }) -static __inline__ int +static __inline__ s64 __atomic64_add_return(s64 i, atomic64_t *v) { - int ret; + s64 ret; unsigned long flags; _atomic_spin_lock_irqsave(v, flags); From 09e557b1e166f09c69d753588a4216a114fbbeaf Mon Sep 17 00:00:00 2001 From: Corey Moyer Date: Sun, 4 Nov 2012 23:52:17 -0500 Subject: [PATCH 2549/2556] Overhaul kconfig --- arch/arm/mach-msm/Kconfig | 132 ++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 68 deletions(-) diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 15d6fc2be21f7..1061558420e39 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -179,6 +179,14 @@ config MACH_HALIBUT help Support for the Qualcomm SURF7201A eval board. +config MACH_SAPPHIRE + depends on ARCH_MSM + depends on ARCH_MSM7X00A + default y + bool "HTC Magic (aka sapphire)" + help + Support for the HTC Dream, T-Mobile G1, Android ADP1 devices. + config MACH_TROUT depends on ARCH_MSM depends on ARCH_MSM7X00A @@ -200,6 +208,48 @@ config MACH_MSM7X30_FLUID help Support for the Qualcomm MSM7x30 FLUID eval board. +choice + depends on ARCH_QSD8X50 + prompt "Bravo" + default MACH_BRAVO_NONE + help + Select this to support the Bravo GSM or CDMA device + + config MACH_BRAVO_NONE + bool "None" + + config MACH_BRAVO + bool "GSM" + help + Select this to support the Bravo GSM device + + config MACH_BRAVOC + bool "CDMA" + help + Select this to support the Bravo CDMA device +endchoice + +config MACH_INCREDIBLE + depends on ARCH_QSD8X50 + default n + bool "Incredible" + help + Select this to support the Incredible device + +config MACH_INCREDIBLEC + depends on ARCH_QSD8X50 + default n + bool "IncredibleC" + help + Select this to support the IncredibleC device + +config MACH_MAHIMAHI + depends on ARCH_QSD8X50 + default y + bool "Mahi-Mahi" + help + Select this to support the Mahi-Mahi device + config MACH_SWORDFISH depends on ARCH_QSD8X50 default n @@ -207,12 +257,26 @@ config MACH_SWORDFISH help Support for the Qualcomm SURF8250 eval board. +config MACH_SUPERSONIC + depends on ARCH_QSD8X50 + default n + bool "Supersonic (HTC EVO 4G)" + help + Select this to support the Supersonic device + config MACH_QSD8X50_SURF depends on ARCH_QSD8X50 bool "QSD8x50 SURF" help Support for the Qualcomm QSD8x50 SURF eval board. +config MACH_QSD8X50_FFA + depends on ARCH_QSD8X50 + default n + bool "8x50-ffa" + help + Select this to support the 8x50 ffa device + config MACH_QSD8X50A_ST1_5 depends on ARCH_QSD8X50 select MSM_SOC_REV_A @@ -282,74 +346,6 @@ config MSM_SMD_PKG3 config MSM_PROC_COMM bool -config MACH_SAPPHIRE - depends on ARCH_MSM7X00A && !MACH_TROUT && !MACH_HALIBUT - default y - bool "Sapphire" - -config MACH_MAHIMAHI - depends on ARCH_QSD8X50 - default y - bool "Mahi-Mahi" - help - Select this to support the Mahi-Mahi device - -choice - depends on ARCH_QSD8X50 - prompt "Bravo" - default MACH_BRAVO_NONE - help - Select this to support the Bravo GSM or CDMA device - - config MACH_BRAVO_NONE - bool "None" - - config MACH_BRAVO - bool "GSM" - help - Select this to support the Bravo GSM device - - config MACH_BRAVOC - bool "CDMA" - help - Select this to support the Bravo CDMA device -endchoice - -config MACH_INCREDIBLE - depends on ARCH_QSD8X50 - default n - bool "Incredible" - help - Select this to support the Incredible device - -config MACH_INCREDIBLEC - depends on ARCH_QSD8X50 - default n - bool "IncredibleC" - help - Select this to support the IncredibleC device - -config MACH_SUPERSONIC - depends on ARCH_QSD8X50 - default n - bool "Supersonic (HTC EVO 4G)" - help - Select this to support the Supersonic device - -config MACH_QSD8X50_FFA - depends on ARCH_QSD8X50 - default n - bool "8x50-ffa" - help - Select this to support the 8x50 ffa device - -config MACH_MSM7X30_SURF - depends on ARCH_MSM7X30 - default n - bool "QCT SURF7x30 Board" - help - Select this to support the Qualcomm SURF7X30 development board - config HTC_HEADSET tristate "HTC 2 Wire detection driver" default n From 95f58a787858e3cd4d271ecc00a52fe340fb0303 Mon Sep 17 00:00:00 2001 From: RonGokhale Date: Thu, 28 Feb 2013 02:17:01 +0530 Subject: [PATCH 2550/2556] drivers: cpufreq: Adding SmartassH3 CPU Governor Kanged from nobodyAtall Change-Id: I479372ad7877a1406d8fa31a2723dc9a1999a002 Signed-off-by: RonGokhale Conflicts: drivers/cpufreq/Kconfig drivers/cpufreq/Makefile Conflicts: drivers/cpufreq/Kconfig drivers/cpufreq/Makefile --- drivers/cpufreq/Kconfig | 14 + drivers/cpufreq/Makefile | 1 + drivers/cpufreq/cpufreq_smartassH3.c | 869 +++++++++++++++++++++++++++ 3 files changed, 884 insertions(+) create mode 100644 drivers/cpufreq/cpufreq_smartassH3.c diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 4391df9a8c19f..7b503692e163f 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -65,6 +65,12 @@ config CPU_FREQ_DEFAULT_GOV_POWERSAVE the frequency statically to the lowest frequency supported by the CPU. +config CPU_FREQ_DEFAULT_GOV_SMARTASSH3 + bool "smartassH3" + select CPU_FREQ_GOV_SMARTASSH3 + help + Use the CPUFreq governor 'SmartassH3' as default. + config CPU_FREQ_DEFAULT_GOV_USERSPACE bool "userspace" select CPU_FREQ_GOV_USERSPACE @@ -217,4 +223,12 @@ config CPU_FREQ_GOV_CONSERVATIVE If in doubt, say N. +config CPU_FREQ_GOV_SMARTASSH3 + tristate "'SmartassH3' cpufreq governor" + depends on CPU_FREQ + help + 'SmartassH3' - a "smartassV2 with tweaks by H3ROS" governor! + + If in doubt, say N. + endif # CPU_FREQ diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 35aa00903236f..278e47b6edf25 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o obj-$(CONFIG_CPU_FREQ_GOV_SMARTASS2) += cpufreq_smartass2.o +obj-$(CONFIG_CPU_FREQ_GOV_SMARTASSH3) += cpufreq_smartassH3.o # CPUfreq cross-arch helpers obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o diff --git a/drivers/cpufreq/cpufreq_smartassH3.c b/drivers/cpufreq/cpufreq_smartassH3.c new file mode 100644 index 0000000000000..446f5b68eeef7 --- /dev/null +++ b/drivers/cpufreq/cpufreq_smartassH3.c @@ -0,0 +1,869 @@ +/* + * drivers/cpufreq/cpufreq_smartassH3.c + * + * Copyright (C) 2010 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. + * + * Author: Erasmux + * + * Based on the interactive governor By Mike Chan (mike@android.com) + * which was adaptated to 2.6.29 kernel by Nadlabak (pavel@doshaska.net) + * + * SMP support based on mod by faux123 + * + * For a general overview of smartassV2 see the relavent part in + * Documentation/cpu-freq/governors.txt + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/******************** Tunable parameters: ********************/ + +/* + * The "ideal" frequency to use when awake. The governor will ramp up faster + * towards the ideal frequency and slower after it has passed it. Similarly, + * lowering the frequency towards the ideal frequency is faster than below it. + */ +#define DEFAULT_AWAKE_IDEAL_FREQ 320000 +static unsigned int awake_ideal_freq; + +/* + * The "ideal" frequency to use when suspended. + * When set to 0, the governor will not track the suspended state (meaning + * that practically when sleep_ideal_freq==0 the awake_ideal_freq is used + * also when suspended). + */ +#define DEFAULT_SLEEP_IDEAL_FREQ 122880 +static unsigned int sleep_ideal_freq; + +/* + * Freqeuncy delta when ramping up above the ideal freqeuncy. + * Zero disables and causes to always jump straight to max frequency. + * When below the ideal freqeuncy we always ramp up to the ideal freq. + */ +#define DEFAULT_RAMP_UP_STEP 80000 +static unsigned int ramp_up_step; + +/* + * Freqeuncy delta when ramping down below the ideal freqeuncy. + * Zero disables and will calculate ramp down according to load heuristic. + * When above the ideal freqeuncy we always ramp down to the ideal freq. + */ +#define DEFAULT_RAMP_DOWN_STEP 80000 +static unsigned int ramp_down_step; + +/* + * CPU freq will be increased if measured load > max_cpu_load; + */ +#define DEFAULT_MAX_CPU_LOAD 85 +static unsigned long max_cpu_load; + +/* + * CPU freq will be decreased if measured load < min_cpu_load; + */ +#define DEFAULT_MIN_CPU_LOAD 70 +static unsigned long min_cpu_load; + +/* + * The minimum amount of time to spend at a frequency before we can ramp up. + * Notice we ignore this when we are below the ideal frequency. + */ +#define DEFAULT_UP_RATE_US 48000; +static unsigned long up_rate_us; + +/* + * The minimum amount of time to spend at a frequency before we can ramp down. + * Notice we ignore this when we are above the ideal frequency. + */ +#define DEFAULT_DOWN_RATE_US 49000; +static unsigned long down_rate_us; + +/* + * The frequency to set when waking up from sleep. + * When sleep_ideal_freq=0 this will have no effect. + */ +#define DEFAULT_SLEEP_WAKEUP_FREQ 99999999 +static unsigned int sleep_wakeup_freq; + +/* + * Sampling rate, I highly recommend to leave it at 2. + */ +#define DEFAULT_SAMPLE_RATE_JIFFIES 2 +static unsigned int sample_rate_jiffies; + + +/*************** End of tunables ***************/ + + +static void (*pm_idle_old)(void); +static atomic_t active_count = ATOMIC_INIT(0); + +struct smartass_info_s { + struct cpufreq_policy *cur_policy; + struct cpufreq_frequency_table *freq_table; + struct timer_list timer; + u64 time_in_idle; + u64 idle_exit_time; + u64 freq_change_time; + u64 freq_change_time_in_idle; + int cur_cpu_load; + int old_freq; + int ramp_dir; + unsigned int enable; + int ideal_speed; +}; +static DEFINE_PER_CPU(struct smartass_info_s, smartass_info); + +/* Workqueues handle frequency scaling */ +static struct workqueue_struct *up_wq; +static struct workqueue_struct *down_wq; +static struct work_struct freq_scale_work; + +static cpumask_t work_cpumask; +static spinlock_t cpumask_lock; + +static unsigned int suspended; + +#define dprintk(flag,msg...) do { \ + if (debug_mask & flag) printk(KERN_DEBUG msg); \ + } while (0) + +enum { + SMARTASS_DEBUG_JUMPS=1, + SMARTASS_DEBUG_LOAD=2, + SMARTASS_DEBUG_ALG=4 +}; + +/* + * Combination of the above debug flags. + */ +static unsigned long debug_mask; + +static int cpufreq_governor_smartass_h3(struct cpufreq_policy *policy, + unsigned int event); + +#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASSH3 +static +#endif +struct cpufreq_governor cpufreq_gov_smartass_h3 = { + .name = "SmartassH3", + .governor = cpufreq_governor_smartass_h3, + .max_transition_latency = 9000000, + .owner = THIS_MODULE, +}; + +inline static void smartass_update_min_max(struct smartass_info_s *this_smartass, struct cpufreq_policy *policy, int suspend) { + if (suspend) { + this_smartass->ideal_speed = // sleep_ideal_freq; but make sure it obeys the policy min/max + policy->max > sleep_ideal_freq ? + (sleep_ideal_freq > policy->min ? sleep_ideal_freq : policy->min) : policy->max; + } else { + this_smartass->ideal_speed = // awake_ideal_freq; but make sure it obeys the policy min/max + policy->min < awake_ideal_freq ? + (awake_ideal_freq < policy->max ? awake_ideal_freq : policy->max) : policy->min; + } +} + +inline static void smartass_update_min_max_allcpus(void) { + unsigned int i; + for_each_online_cpu(i) { + struct smartass_info_s *this_smartass = &per_cpu(smartass_info, i); + if (this_smartass->enable) + smartass_update_min_max(this_smartass,this_smartass->cur_policy,suspended); + } +} + +inline static unsigned int validate_freq(struct cpufreq_policy *policy, int freq) { + if (freq > (int)policy->max) + return policy->max; + if (freq < (int)policy->min) + return policy->min; + return freq; +} + +inline static void reset_timer(unsigned long cpu, struct smartass_info_s *this_smartass) { + this_smartass->time_in_idle = get_cpu_idle_time_us(cpu, &this_smartass->idle_exit_time); + mod_timer(&this_smartass->timer, jiffies + sample_rate_jiffies); +} + +inline static void work_cpumask_set(unsigned long cpu) { + unsigned long flags; + spin_lock_irqsave(&cpumask_lock, flags); + cpumask_set_cpu(cpu, &work_cpumask); + spin_unlock_irqrestore(&cpumask_lock, flags); +} + +inline static int work_cpumask_test_and_clear(unsigned long cpu) { + unsigned long flags; + int res = 0; + spin_lock_irqsave(&cpumask_lock, flags); + res = cpumask_test_and_clear_cpu(cpu, &work_cpumask); + spin_unlock_irqrestore(&cpumask_lock, flags); + return res; +} + +inline static int target_freq(struct cpufreq_policy *policy, struct smartass_info_s *this_smartass, + int new_freq, int old_freq, int prefered_relation) { + int index, target; + struct cpufreq_frequency_table *table = this_smartass->freq_table; + + if (new_freq == old_freq) + return 0; + new_freq = validate_freq(policy,new_freq); + if (new_freq == old_freq) + return 0; + + if (table && + !cpufreq_frequency_table_target(policy,table,new_freq,prefered_relation,&index)) + { + target = table[index].frequency; + if (target == old_freq) { + // if for example we are ramping up to *at most* current + ramp_up_step + // but there is no such frequency higher than the current, try also + // to ramp up to *at least* current + ramp_up_step. + if (new_freq > old_freq && prefered_relation==CPUFREQ_RELATION_H + && !cpufreq_frequency_table_target(policy,table,new_freq, + CPUFREQ_RELATION_L,&index)) + target = table[index].frequency; + // simlarly for ramping down: + else if (new_freq < old_freq && prefered_relation==CPUFREQ_RELATION_L + && !cpufreq_frequency_table_target(policy,table,new_freq, + CPUFREQ_RELATION_H,&index)) + target = table[index].frequency; + } + + if (target == old_freq) { + // We should not get here: + // If we got here we tried to change to a validated new_freq which is different + // from old_freq, so there is no reason for us to remain at same frequency. + printk(KERN_WARNING "Smartass: frequency change failed: %d to %d => %d\n", + old_freq,new_freq,target); + return 0; + } + } + else target = new_freq; + + __cpufreq_driver_target(policy, target, prefered_relation); + + dprintk(SMARTASS_DEBUG_JUMPS,"SmartassQ: jumping from %d to %d => %d (%d)\n", + old_freq,new_freq,target,policy->cur); + + return target; +} + +static void cpufreq_smartass_timer(unsigned long cpu) +{ + u64 delta_idle; + u64 delta_time; + int cpu_load; + int old_freq; + u64 update_time; + u64 now_idle; + int queued_work = 0; + struct smartass_info_s *this_smartass = &per_cpu(smartass_info, cpu); + struct cpufreq_policy *policy = this_smartass->cur_policy; + + now_idle = get_cpu_idle_time_us(cpu, &update_time); + old_freq = policy->cur; + + if (this_smartass->idle_exit_time == 0 || update_time == this_smartass->idle_exit_time) + return; + + delta_idle = cputime64_sub(now_idle, this_smartass->time_in_idle); + delta_time = cputime64_sub(update_time, this_smartass->idle_exit_time); + + // If timer ran less than 1ms after short-term sample started, retry. + if (delta_time < 1000) { + if (!timer_pending(&this_smartass->timer)) + reset_timer(cpu,this_smartass); + return; + } + + if (delta_idle > delta_time) + cpu_load = 0; + else + cpu_load = 100 * (unsigned int)(delta_time - delta_idle) / (unsigned int)delta_time; + + dprintk(SMARTASS_DEBUG_LOAD,"smartassT @ %d: load %d (delta_time %llu)\n", + old_freq,cpu_load,delta_time); + + this_smartass->cur_cpu_load = cpu_load; + this_smartass->old_freq = old_freq; + + // Scale up if load is above max or if there where no idle cycles since coming out of idle, + // additionally, if we are at or above the ideal_speed, verify we have been at this frequency + // for at least up_rate_us: + if (cpu_load > max_cpu_load || delta_idle == 0) + { + if (old_freq < policy->max && + (old_freq < this_smartass->ideal_speed || delta_idle == 0 || + cputime64_sub(update_time, this_smartass->freq_change_time) >= up_rate_us)) + { + dprintk(SMARTASS_DEBUG_ALG,"smartassT @ %d ramp up: load %d (delta_idle %llu)\n", + old_freq,cpu_load,delta_idle); + this_smartass->ramp_dir = 1; + work_cpumask_set(cpu); + queue_work(up_wq, &freq_scale_work); + queued_work = 1; + } + else this_smartass->ramp_dir = 0; + } + // Similarly for scale down: load should be below min and if we are at or below ideal + // frequency we require that we have been at this frequency for at least down_rate_us: + else if (cpu_load < min_cpu_load && old_freq > policy->min && + (old_freq > this_smartass->ideal_speed || + cputime64_sub(update_time, this_smartass->freq_change_time) >= down_rate_us)) + { + dprintk(SMARTASS_DEBUG_ALG,"smartassT @ %d ramp down: load %d (delta_idle %llu)\n", + old_freq,cpu_load,delta_idle); + this_smartass->ramp_dir = -1; + work_cpumask_set(cpu); + queue_work(down_wq, &freq_scale_work); + queued_work = 1; + } + else this_smartass->ramp_dir = 0; + + // To avoid unnecessary load when the CPU is already at high load, we don't + // reset ourselves if we are at max speed. If and when there are idle cycles, + // the idle loop will activate the timer. + // Additionally, if we queued some work, the work task will reset the timer + // after it has done its adjustments. + if (!queued_work && old_freq < policy->max) + reset_timer(cpu,this_smartass); +} + +static void cpufreq_idle(void) +{ + struct smartass_info_s *this_smartass = &per_cpu(smartass_info, smp_processor_id()); + struct cpufreq_policy *policy = this_smartass->cur_policy; + + if (!this_smartass->enable) { + pm_idle_old(); + return; + } + + if (policy->cur == policy->min && timer_pending(&this_smartass->timer)) + del_timer(&this_smartass->timer); + + pm_idle_old(); + + if (!timer_pending(&this_smartass->timer)) + reset_timer(smp_processor_id(), this_smartass); +} + +/* We use the same work function to sale up and down */ +static void cpufreq_smartass_freq_change_time_work(struct work_struct *work) +{ + unsigned int cpu; + int new_freq; + int old_freq; + int ramp_dir; + struct smartass_info_s *this_smartass; + struct cpufreq_policy *policy; + unsigned int relation = CPUFREQ_RELATION_L; + for_each_possible_cpu(cpu) { + this_smartass = &per_cpu(smartass_info, cpu); + if (!work_cpumask_test_and_clear(cpu)) + continue; + + ramp_dir = this_smartass->ramp_dir; + this_smartass->ramp_dir = 0; + + old_freq = this_smartass->old_freq; + policy = this_smartass->cur_policy; + + if (old_freq != policy->cur) { + // frequency was changed by someone else? + printk(KERN_WARNING "Smartass: frequency changed by 3rd party: %d to %d\n", + old_freq,policy->cur); + new_freq = old_freq; + } + else if (ramp_dir > 0 && nr_running() > 1) { + // ramp up logic: + if (old_freq < this_smartass->ideal_speed) + new_freq = this_smartass->ideal_speed; + else if (ramp_up_step) { + new_freq = old_freq + ramp_up_step; + relation = CPUFREQ_RELATION_H; + } + else { + new_freq = policy->max; + relation = CPUFREQ_RELATION_H; + } + dprintk(SMARTASS_DEBUG_ALG,"smartassQ @ %d ramp up: ramp_dir=%d ideal=%d\n", + old_freq,ramp_dir,this_smartass->ideal_speed); + } + else if (ramp_dir < 0) { + // ramp down logic: + if (old_freq > this_smartass->ideal_speed) { + new_freq = this_smartass->ideal_speed; + relation = CPUFREQ_RELATION_H; + } + else if (ramp_down_step) + new_freq = old_freq - ramp_down_step; + else { + // Load heuristics: Adjust new_freq such that, assuming a linear + // scaling of load vs. frequency, the load in the new frequency + // will be max_cpu_load: + new_freq = old_freq * this_smartass->cur_cpu_load / max_cpu_load; + if (new_freq > old_freq) // min_cpu_load > max_cpu_load ?! + new_freq = old_freq -1; + } + dprintk(SMARTASS_DEBUG_ALG,"smartassQ @ %d ramp down: ramp_dir=%d ideal=%d\n", + old_freq,ramp_dir,this_smartass->ideal_speed); + } + else { // ramp_dir==0 ?! Could the timer change its mind about a queued ramp up/down + // before the work task gets to run? + // This may also happen if we refused to ramp up because the nr_running()==1 + new_freq = old_freq; + dprintk(SMARTASS_DEBUG_ALG,"smartassQ @ %d nothing: ramp_dir=%d nr_running=%lu\n", + old_freq,ramp_dir,nr_running()); + } + + // do actual ramp up (returns 0, if frequency change failed): + new_freq = target_freq(policy,this_smartass,new_freq,old_freq,relation); + if (new_freq) + this_smartass->freq_change_time_in_idle = + get_cpu_idle_time_us(cpu,&this_smartass->freq_change_time); + + // reset timer: + if (new_freq < policy->max) + reset_timer(cpu,this_smartass); + // if we are maxed out, it is pointless to use the timer + // (idle cycles wake up the timer when the timer comes) + else if (timer_pending(&this_smartass->timer)) + del_timer(&this_smartass->timer); + } +} + +static ssize_t show_debug_mask(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", debug_mask); +} + +static ssize_t store_debug_mask(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0) + debug_mask = input; + return res; +} + +static ssize_t show_up_rate_us(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", up_rate_us); +} + +static ssize_t store_up_rate_us(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0 && input <= 100000000) + up_rate_us = input; + return res; +} + +static ssize_t show_down_rate_us(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", down_rate_us); +} + +static ssize_t store_down_rate_us(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0 && input <= 100000000) + down_rate_us = input; + return res; +} + +static ssize_t show_sleep_ideal_freq(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", sleep_ideal_freq); +} + +static ssize_t store_sleep_ideal_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0) { + sleep_ideal_freq = input; + if (suspended) + smartass_update_min_max_allcpus(); + } + return res; +} + +static ssize_t show_sleep_wakeup_freq(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", sleep_wakeup_freq); +} + +static ssize_t store_sleep_wakeup_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0) + sleep_wakeup_freq = input; + return res; +} + +static ssize_t show_awake_ideal_freq(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", awake_ideal_freq); +} + +static ssize_t store_awake_ideal_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0) { + awake_ideal_freq = input; + if (!suspended) + smartass_update_min_max_allcpus(); + } + return res; +} + +static ssize_t show_sample_rate_jiffies(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", sample_rate_jiffies); +} + +static ssize_t store_sample_rate_jiffies(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input > 0 && input <= 1000) + sample_rate_jiffies = input; + return res; +} + +static ssize_t show_ramp_up_step(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", ramp_up_step); +} + +static ssize_t store_ramp_up_step(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0) + ramp_up_step = input; + return res; +} + +static ssize_t show_ramp_down_step(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", ramp_down_step); +} + +static ssize_t store_ramp_down_step(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input >= 0) + ramp_down_step = input; + return res; +} + +static ssize_t show_max_cpu_load(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", max_cpu_load); +} + +static ssize_t store_max_cpu_load(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input > 0 && input <= 100) + max_cpu_load = input; + return res; +} + +static ssize_t show_min_cpu_load(struct kobject *kobj, struct attribute *attr, char *buf) +{ + return sprintf(buf, "%lu\n", min_cpu_load); +} + +static ssize_t store_min_cpu_load(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) +{ + ssize_t res; + unsigned long input; + res = strict_strtoul(buf, 0, &input); + if (res >= 0 && input > 0 && input < 100) + min_cpu_load = input; + return res; +} + +#define define_global_rw_attr(_name) \ +static struct global_attr _name##_attr = \ + __ATTR(_name, 0644, show_##_name, store_##_name) + +define_global_rw_attr(debug_mask); +define_global_rw_attr(up_rate_us); +define_global_rw_attr(down_rate_us); +define_global_rw_attr(sleep_ideal_freq); +define_global_rw_attr(sleep_wakeup_freq); +define_global_rw_attr(awake_ideal_freq); +define_global_rw_attr(sample_rate_jiffies); +define_global_rw_attr(ramp_up_step); +define_global_rw_attr(ramp_down_step); +define_global_rw_attr(max_cpu_load); +define_global_rw_attr(min_cpu_load); + +static struct attribute * smartass_attributes[] = { + &debug_mask_attr.attr, + &up_rate_us_attr.attr, + &down_rate_us_attr.attr, + &sleep_ideal_freq_attr.attr, + &sleep_wakeup_freq_attr.attr, + &awake_ideal_freq_attr.attr, + &sample_rate_jiffies_attr.attr, + &ramp_up_step_attr.attr, + &ramp_down_step_attr.attr, + &max_cpu_load_attr.attr, + &min_cpu_load_attr.attr, + NULL, +}; + +static struct attribute_group smartass_attr_group = { + .attrs = smartass_attributes, + .name = "smartassH3", +}; + +static int cpufreq_governor_smartass_h3(struct cpufreq_policy *new_policy, + unsigned int event) +{ + unsigned int cpu = new_policy->cpu; + int rc; + struct smartass_info_s *this_smartass = &per_cpu(smartass_info, cpu); + + switch (event) { + case CPUFREQ_GOV_START: + if ((!cpu_online(cpu)) || (!new_policy->cur)) + return -EINVAL; + + this_smartass->cur_policy = new_policy; + + this_smartass->enable = 1; + + smartass_update_min_max(this_smartass,new_policy,suspended); + + this_smartass->freq_table = cpufreq_frequency_get_table(cpu); + if (!this_smartass->freq_table) + printk(KERN_WARNING "Smartass: no frequency table for cpu %d?!\n",cpu); + + smp_wmb(); + + // Do not register the idle hook and create sysfs + // entries if we have already done so. + if (atomic_inc_return(&active_count) <= 1) { + rc = sysfs_create_group(cpufreq_global_kobject, + &smartass_attr_group); + if (rc) + return rc; + + pm_idle_old = pm_idle; + pm_idle = cpufreq_idle; + } + + if (this_smartass->cur_policy->cur < new_policy->max && !timer_pending(&this_smartass->timer)) + reset_timer(cpu,this_smartass); + + break; + + case CPUFREQ_GOV_LIMITS: + smartass_update_min_max(this_smartass,new_policy,suspended); + + if (this_smartass->cur_policy->cur > new_policy->max) { + dprintk(SMARTASS_DEBUG_JUMPS,"SmartassI: jumping to new max freq: %d\n",new_policy->max); + __cpufreq_driver_target(this_smartass->cur_policy, + new_policy->max, CPUFREQ_RELATION_H); + } + else if (this_smartass->cur_policy->cur < new_policy->min) { + dprintk(SMARTASS_DEBUG_JUMPS,"SmartassI: jumping to new min freq: %d\n",new_policy->min); + __cpufreq_driver_target(this_smartass->cur_policy, + new_policy->min, CPUFREQ_RELATION_L); + } + + if (this_smartass->cur_policy->cur < new_policy->max && !timer_pending(&this_smartass->timer)) + reset_timer(cpu,this_smartass); + + break; + + case CPUFREQ_GOV_STOP: + this_smartass->enable = 0; + smp_wmb(); + del_timer(&this_smartass->timer); + flush_work(&freq_scale_work); + this_smartass->idle_exit_time = 0; + + if (atomic_dec_return(&active_count) <= 1) { + sysfs_remove_group(cpufreq_global_kobject, + &smartass_attr_group); + pm_idle = pm_idle_old; + } + break; + } + + return 0; +} + +static void smartass_suspend(int cpu, int suspend) +{ + struct smartass_info_s *this_smartass = &per_cpu(smartass_info, smp_processor_id()); + struct cpufreq_policy *policy = this_smartass->cur_policy; + unsigned int new_freq; + + if (!this_smartass->enable) + return; + + smartass_update_min_max(this_smartass,policy,suspend); + if (!suspend) { // resume at max speed: + new_freq = validate_freq(policy,sleep_wakeup_freq); + + dprintk(SMARTASS_DEBUG_JUMPS,"SmartassS: awaking at %d\n",new_freq); + + __cpufreq_driver_target(policy, new_freq, + CPUFREQ_RELATION_L); + } else { + // to avoid wakeup issues with quick sleep/wakeup don't change actual frequency when entering sleep + // to allow some time to settle down. Instead we just reset our statistics (and reset the timer). + // Eventually, the timer will adjust the frequency if necessary. + + this_smartass->freq_change_time_in_idle = + get_cpu_idle_time_us(cpu,&this_smartass->freq_change_time); + + dprintk(SMARTASS_DEBUG_JUMPS,"SmartassS: suspending at %d\n",policy->cur); + } + + reset_timer(smp_processor_id(),this_smartass); +} + +static void smartass_early_suspend(struct early_suspend *handler) { + int i; + if (suspended || sleep_ideal_freq==0) // disable behavior for sleep_ideal_freq==0 + return; + suspended = 1; + for_each_online_cpu(i) + smartass_suspend(i,1); +} + +static void smartass_late_resume(struct early_suspend *handler) { + int i; + if (!suspended) // already not suspended so nothing to do + return; + suspended = 0; + for_each_online_cpu(i) + smartass_suspend(i,0); +} + +static struct early_suspend smartass_power_suspend = { + .suspend = smartass_early_suspend, + .resume = smartass_late_resume, +#ifdef CONFIG_MACH_HERO + .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, +#endif +}; + +static int __init cpufreq_smartass_init(void) +{ + unsigned int i; + struct smartass_info_s *this_smartass; + debug_mask = 0; + up_rate_us = DEFAULT_UP_RATE_US; + down_rate_us = DEFAULT_DOWN_RATE_US; + sleep_ideal_freq = DEFAULT_SLEEP_IDEAL_FREQ; + sleep_wakeup_freq = DEFAULT_SLEEP_WAKEUP_FREQ; + awake_ideal_freq = DEFAULT_AWAKE_IDEAL_FREQ; + sample_rate_jiffies = DEFAULT_SAMPLE_RATE_JIFFIES; + ramp_up_step = DEFAULT_RAMP_UP_STEP; + ramp_down_step = DEFAULT_RAMP_DOWN_STEP; + max_cpu_load = DEFAULT_MAX_CPU_LOAD; + min_cpu_load = DEFAULT_MIN_CPU_LOAD; + + spin_lock_init(&cpumask_lock); + + suspended = 0; + + /* Initalize per-cpu data: */ + for_each_possible_cpu(i) { + this_smartass = &per_cpu(smartass_info, i); + this_smartass->enable = 0; + this_smartass->cur_policy = 0; + this_smartass->ramp_dir = 0; + this_smartass->time_in_idle = 0; + this_smartass->idle_exit_time = 0; + this_smartass->freq_change_time = 0; + this_smartass->freq_change_time_in_idle = 0; + this_smartass->cur_cpu_load = 0; + // intialize timer: + init_timer_deferrable(&this_smartass->timer); + this_smartass->timer.function = cpufreq_smartass_timer; + this_smartass->timer.data = i; + work_cpumask_test_and_clear(i); + } + + // Scale up is high priority + up_wq = create_rt_workqueue("ksmartass_up"); + down_wq = create_workqueue("ksmartass_down"); + if (!up_wq || !down_wq) + return -ENOMEM; + + INIT_WORK(&freq_scale_work, cpufreq_smartass_freq_change_time_work); + + register_early_suspend(&smartass_power_suspend); + + return cpufreq_register_governor(&cpufreq_gov_smartass_h3); +} + +#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASSH3 +fs_initcall(cpufreq_smartass_init); +#else +module_init(cpufreq_smartass_init); +#endif + +static void __exit cpufreq_smartass_exit(void) +{ + cpufreq_unregister_governor(&cpufreq_gov_smartass_h3); + destroy_workqueue(up_wq); + destroy_workqueue(down_wq); +} + +module_exit(cpufreq_smartass_exit); + +MODULE_AUTHOR ("Erasmux, moded by H3ROS & C3C0, ported for SEMC by Daveee10"); +MODULE_DESCRIPTION ("'cpufreq_smartassH3' - A smart cpufreq governor"); +MODULE_LICENSE ("GPL"); + From 6b544a9764c27aff7bcd848730542fe2882cbf9b Mon Sep 17 00:00:00 2001 From: Zach Date: Wed, 23 Apr 2014 22:13:13 -0400 Subject: [PATCH 2551/2556] Enable the SmartassH3 gov --- arch/arm/configs/incrediblec-incredikernel_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/incrediblec-incredikernel_defconfig b/arch/arm/configs/incrediblec-incredikernel_defconfig index 8e14547e3cb9d..83ae75577f180 100644 --- a/arch/arm/configs/incrediblec-incredikernel_defconfig +++ b/arch/arm/configs/incrediblec-incredikernel_defconfig @@ -485,6 +485,7 @@ CONFIG_CPU_FREQ_GOV_PERFORMANCE=y # CONFIG_CPU_FREQ_GOV_USERSPACE is not set CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_SMARTASSH3=y # CONFIG_CPU_FREQ_GOV_SMARTASS2 is not set # CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set # CONFIG_CPU_IDLE is not set From d1dd0b1bdf5043919136b6b3137dce01d3141de5 Mon Sep 17 00:00:00 2001 From: Zach Date: Wed, 23 Apr 2014 23:33:21 -0400 Subject: [PATCH 2552/2556] Revert "Enable the SmartassH3 gov" This reverts commit 6b544a9764c27aff7bcd848730542fe2882cbf9b. --- arch/arm/configs/incrediblec-incredikernel_defconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/configs/incrediblec-incredikernel_defconfig b/arch/arm/configs/incrediblec-incredikernel_defconfig index 83ae75577f180..8e14547e3cb9d 100644 --- a/arch/arm/configs/incrediblec-incredikernel_defconfig +++ b/arch/arm/configs/incrediblec-incredikernel_defconfig @@ -485,7 +485,6 @@ CONFIG_CPU_FREQ_GOV_PERFORMANCE=y # CONFIG_CPU_FREQ_GOV_USERSPACE is not set CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_INTERACTIVE=y -CONFIG_CPU_FREQ_GOV_SMARTASSH3=y # CONFIG_CPU_FREQ_GOV_SMARTASS2 is not set # CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set # CONFIG_CPU_IDLE is not set From 0357ff67d8548a5139896219e577dfbd67c15247 Mon Sep 17 00:00:00 2001 From: Zach Date: Wed, 23 Apr 2014 23:33:40 -0400 Subject: [PATCH 2553/2556] Revert "drivers: cpufreq: Adding SmartassH3 CPU Governor" This reverts commit 95f58a787858e3cd4d271ecc00a52fe340fb0303. --- drivers/cpufreq/Kconfig | 14 - drivers/cpufreq/Makefile | 1 - drivers/cpufreq/cpufreq_smartassH3.c | 869 --------------------------- 3 files changed, 884 deletions(-) delete mode 100644 drivers/cpufreq/cpufreq_smartassH3.c diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 7b503692e163f..4391df9a8c19f 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -65,12 +65,6 @@ config CPU_FREQ_DEFAULT_GOV_POWERSAVE the frequency statically to the lowest frequency supported by the CPU. -config CPU_FREQ_DEFAULT_GOV_SMARTASSH3 - bool "smartassH3" - select CPU_FREQ_GOV_SMARTASSH3 - help - Use the CPUFreq governor 'SmartassH3' as default. - config CPU_FREQ_DEFAULT_GOV_USERSPACE bool "userspace" select CPU_FREQ_GOV_USERSPACE @@ -223,12 +217,4 @@ config CPU_FREQ_GOV_CONSERVATIVE If in doubt, say N. -config CPU_FREQ_GOV_SMARTASSH3 - tristate "'SmartassH3' cpufreq governor" - depends on CPU_FREQ - help - 'SmartassH3' - a "smartassV2 with tweaks by H3ROS" governor! - - If in doubt, say N. - endif # CPU_FREQ diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 278e47b6edf25..35aa00903236f 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -11,7 +11,6 @@ obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o obj-$(CONFIG_CPU_FREQ_GOV_SMARTASS2) += cpufreq_smartass2.o -obj-$(CONFIG_CPU_FREQ_GOV_SMARTASSH3) += cpufreq_smartassH3.o # CPUfreq cross-arch helpers obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o diff --git a/drivers/cpufreq/cpufreq_smartassH3.c b/drivers/cpufreq/cpufreq_smartassH3.c deleted file mode 100644 index 446f5b68eeef7..0000000000000 --- a/drivers/cpufreq/cpufreq_smartassH3.c +++ /dev/null @@ -1,869 +0,0 @@ -/* - * drivers/cpufreq/cpufreq_smartassH3.c - * - * Copyright (C) 2010 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. - * - * Author: Erasmux - * - * Based on the interactive governor By Mike Chan (mike@android.com) - * which was adaptated to 2.6.29 kernel by Nadlabak (pavel@doshaska.net) - * - * SMP support based on mod by faux123 - * - * For a general overview of smartassV2 see the relavent part in - * Documentation/cpu-freq/governors.txt - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/******************** Tunable parameters: ********************/ - -/* - * The "ideal" frequency to use when awake. The governor will ramp up faster - * towards the ideal frequency and slower after it has passed it. Similarly, - * lowering the frequency towards the ideal frequency is faster than below it. - */ -#define DEFAULT_AWAKE_IDEAL_FREQ 320000 -static unsigned int awake_ideal_freq; - -/* - * The "ideal" frequency to use when suspended. - * When set to 0, the governor will not track the suspended state (meaning - * that practically when sleep_ideal_freq==0 the awake_ideal_freq is used - * also when suspended). - */ -#define DEFAULT_SLEEP_IDEAL_FREQ 122880 -static unsigned int sleep_ideal_freq; - -/* - * Freqeuncy delta when ramping up above the ideal freqeuncy. - * Zero disables and causes to always jump straight to max frequency. - * When below the ideal freqeuncy we always ramp up to the ideal freq. - */ -#define DEFAULT_RAMP_UP_STEP 80000 -static unsigned int ramp_up_step; - -/* - * Freqeuncy delta when ramping down below the ideal freqeuncy. - * Zero disables and will calculate ramp down according to load heuristic. - * When above the ideal freqeuncy we always ramp down to the ideal freq. - */ -#define DEFAULT_RAMP_DOWN_STEP 80000 -static unsigned int ramp_down_step; - -/* - * CPU freq will be increased if measured load > max_cpu_load; - */ -#define DEFAULT_MAX_CPU_LOAD 85 -static unsigned long max_cpu_load; - -/* - * CPU freq will be decreased if measured load < min_cpu_load; - */ -#define DEFAULT_MIN_CPU_LOAD 70 -static unsigned long min_cpu_load; - -/* - * The minimum amount of time to spend at a frequency before we can ramp up. - * Notice we ignore this when we are below the ideal frequency. - */ -#define DEFAULT_UP_RATE_US 48000; -static unsigned long up_rate_us; - -/* - * The minimum amount of time to spend at a frequency before we can ramp down. - * Notice we ignore this when we are above the ideal frequency. - */ -#define DEFAULT_DOWN_RATE_US 49000; -static unsigned long down_rate_us; - -/* - * The frequency to set when waking up from sleep. - * When sleep_ideal_freq=0 this will have no effect. - */ -#define DEFAULT_SLEEP_WAKEUP_FREQ 99999999 -static unsigned int sleep_wakeup_freq; - -/* - * Sampling rate, I highly recommend to leave it at 2. - */ -#define DEFAULT_SAMPLE_RATE_JIFFIES 2 -static unsigned int sample_rate_jiffies; - - -/*************** End of tunables ***************/ - - -static void (*pm_idle_old)(void); -static atomic_t active_count = ATOMIC_INIT(0); - -struct smartass_info_s { - struct cpufreq_policy *cur_policy; - struct cpufreq_frequency_table *freq_table; - struct timer_list timer; - u64 time_in_idle; - u64 idle_exit_time; - u64 freq_change_time; - u64 freq_change_time_in_idle; - int cur_cpu_load; - int old_freq; - int ramp_dir; - unsigned int enable; - int ideal_speed; -}; -static DEFINE_PER_CPU(struct smartass_info_s, smartass_info); - -/* Workqueues handle frequency scaling */ -static struct workqueue_struct *up_wq; -static struct workqueue_struct *down_wq; -static struct work_struct freq_scale_work; - -static cpumask_t work_cpumask; -static spinlock_t cpumask_lock; - -static unsigned int suspended; - -#define dprintk(flag,msg...) do { \ - if (debug_mask & flag) printk(KERN_DEBUG msg); \ - } while (0) - -enum { - SMARTASS_DEBUG_JUMPS=1, - SMARTASS_DEBUG_LOAD=2, - SMARTASS_DEBUG_ALG=4 -}; - -/* - * Combination of the above debug flags. - */ -static unsigned long debug_mask; - -static int cpufreq_governor_smartass_h3(struct cpufreq_policy *policy, - unsigned int event); - -#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASSH3 -static -#endif -struct cpufreq_governor cpufreq_gov_smartass_h3 = { - .name = "SmartassH3", - .governor = cpufreq_governor_smartass_h3, - .max_transition_latency = 9000000, - .owner = THIS_MODULE, -}; - -inline static void smartass_update_min_max(struct smartass_info_s *this_smartass, struct cpufreq_policy *policy, int suspend) { - if (suspend) { - this_smartass->ideal_speed = // sleep_ideal_freq; but make sure it obeys the policy min/max - policy->max > sleep_ideal_freq ? - (sleep_ideal_freq > policy->min ? sleep_ideal_freq : policy->min) : policy->max; - } else { - this_smartass->ideal_speed = // awake_ideal_freq; but make sure it obeys the policy min/max - policy->min < awake_ideal_freq ? - (awake_ideal_freq < policy->max ? awake_ideal_freq : policy->max) : policy->min; - } -} - -inline static void smartass_update_min_max_allcpus(void) { - unsigned int i; - for_each_online_cpu(i) { - struct smartass_info_s *this_smartass = &per_cpu(smartass_info, i); - if (this_smartass->enable) - smartass_update_min_max(this_smartass,this_smartass->cur_policy,suspended); - } -} - -inline static unsigned int validate_freq(struct cpufreq_policy *policy, int freq) { - if (freq > (int)policy->max) - return policy->max; - if (freq < (int)policy->min) - return policy->min; - return freq; -} - -inline static void reset_timer(unsigned long cpu, struct smartass_info_s *this_smartass) { - this_smartass->time_in_idle = get_cpu_idle_time_us(cpu, &this_smartass->idle_exit_time); - mod_timer(&this_smartass->timer, jiffies + sample_rate_jiffies); -} - -inline static void work_cpumask_set(unsigned long cpu) { - unsigned long flags; - spin_lock_irqsave(&cpumask_lock, flags); - cpumask_set_cpu(cpu, &work_cpumask); - spin_unlock_irqrestore(&cpumask_lock, flags); -} - -inline static int work_cpumask_test_and_clear(unsigned long cpu) { - unsigned long flags; - int res = 0; - spin_lock_irqsave(&cpumask_lock, flags); - res = cpumask_test_and_clear_cpu(cpu, &work_cpumask); - spin_unlock_irqrestore(&cpumask_lock, flags); - return res; -} - -inline static int target_freq(struct cpufreq_policy *policy, struct smartass_info_s *this_smartass, - int new_freq, int old_freq, int prefered_relation) { - int index, target; - struct cpufreq_frequency_table *table = this_smartass->freq_table; - - if (new_freq == old_freq) - return 0; - new_freq = validate_freq(policy,new_freq); - if (new_freq == old_freq) - return 0; - - if (table && - !cpufreq_frequency_table_target(policy,table,new_freq,prefered_relation,&index)) - { - target = table[index].frequency; - if (target == old_freq) { - // if for example we are ramping up to *at most* current + ramp_up_step - // but there is no such frequency higher than the current, try also - // to ramp up to *at least* current + ramp_up_step. - if (new_freq > old_freq && prefered_relation==CPUFREQ_RELATION_H - && !cpufreq_frequency_table_target(policy,table,new_freq, - CPUFREQ_RELATION_L,&index)) - target = table[index].frequency; - // simlarly for ramping down: - else if (new_freq < old_freq && prefered_relation==CPUFREQ_RELATION_L - && !cpufreq_frequency_table_target(policy,table,new_freq, - CPUFREQ_RELATION_H,&index)) - target = table[index].frequency; - } - - if (target == old_freq) { - // We should not get here: - // If we got here we tried to change to a validated new_freq which is different - // from old_freq, so there is no reason for us to remain at same frequency. - printk(KERN_WARNING "Smartass: frequency change failed: %d to %d => %d\n", - old_freq,new_freq,target); - return 0; - } - } - else target = new_freq; - - __cpufreq_driver_target(policy, target, prefered_relation); - - dprintk(SMARTASS_DEBUG_JUMPS,"SmartassQ: jumping from %d to %d => %d (%d)\n", - old_freq,new_freq,target,policy->cur); - - return target; -} - -static void cpufreq_smartass_timer(unsigned long cpu) -{ - u64 delta_idle; - u64 delta_time; - int cpu_load; - int old_freq; - u64 update_time; - u64 now_idle; - int queued_work = 0; - struct smartass_info_s *this_smartass = &per_cpu(smartass_info, cpu); - struct cpufreq_policy *policy = this_smartass->cur_policy; - - now_idle = get_cpu_idle_time_us(cpu, &update_time); - old_freq = policy->cur; - - if (this_smartass->idle_exit_time == 0 || update_time == this_smartass->idle_exit_time) - return; - - delta_idle = cputime64_sub(now_idle, this_smartass->time_in_idle); - delta_time = cputime64_sub(update_time, this_smartass->idle_exit_time); - - // If timer ran less than 1ms after short-term sample started, retry. - if (delta_time < 1000) { - if (!timer_pending(&this_smartass->timer)) - reset_timer(cpu,this_smartass); - return; - } - - if (delta_idle > delta_time) - cpu_load = 0; - else - cpu_load = 100 * (unsigned int)(delta_time - delta_idle) / (unsigned int)delta_time; - - dprintk(SMARTASS_DEBUG_LOAD,"smartassT @ %d: load %d (delta_time %llu)\n", - old_freq,cpu_load,delta_time); - - this_smartass->cur_cpu_load = cpu_load; - this_smartass->old_freq = old_freq; - - // Scale up if load is above max or if there where no idle cycles since coming out of idle, - // additionally, if we are at or above the ideal_speed, verify we have been at this frequency - // for at least up_rate_us: - if (cpu_load > max_cpu_load || delta_idle == 0) - { - if (old_freq < policy->max && - (old_freq < this_smartass->ideal_speed || delta_idle == 0 || - cputime64_sub(update_time, this_smartass->freq_change_time) >= up_rate_us)) - { - dprintk(SMARTASS_DEBUG_ALG,"smartassT @ %d ramp up: load %d (delta_idle %llu)\n", - old_freq,cpu_load,delta_idle); - this_smartass->ramp_dir = 1; - work_cpumask_set(cpu); - queue_work(up_wq, &freq_scale_work); - queued_work = 1; - } - else this_smartass->ramp_dir = 0; - } - // Similarly for scale down: load should be below min and if we are at or below ideal - // frequency we require that we have been at this frequency for at least down_rate_us: - else if (cpu_load < min_cpu_load && old_freq > policy->min && - (old_freq > this_smartass->ideal_speed || - cputime64_sub(update_time, this_smartass->freq_change_time) >= down_rate_us)) - { - dprintk(SMARTASS_DEBUG_ALG,"smartassT @ %d ramp down: load %d (delta_idle %llu)\n", - old_freq,cpu_load,delta_idle); - this_smartass->ramp_dir = -1; - work_cpumask_set(cpu); - queue_work(down_wq, &freq_scale_work); - queued_work = 1; - } - else this_smartass->ramp_dir = 0; - - // To avoid unnecessary load when the CPU is already at high load, we don't - // reset ourselves if we are at max speed. If and when there are idle cycles, - // the idle loop will activate the timer. - // Additionally, if we queued some work, the work task will reset the timer - // after it has done its adjustments. - if (!queued_work && old_freq < policy->max) - reset_timer(cpu,this_smartass); -} - -static void cpufreq_idle(void) -{ - struct smartass_info_s *this_smartass = &per_cpu(smartass_info, smp_processor_id()); - struct cpufreq_policy *policy = this_smartass->cur_policy; - - if (!this_smartass->enable) { - pm_idle_old(); - return; - } - - if (policy->cur == policy->min && timer_pending(&this_smartass->timer)) - del_timer(&this_smartass->timer); - - pm_idle_old(); - - if (!timer_pending(&this_smartass->timer)) - reset_timer(smp_processor_id(), this_smartass); -} - -/* We use the same work function to sale up and down */ -static void cpufreq_smartass_freq_change_time_work(struct work_struct *work) -{ - unsigned int cpu; - int new_freq; - int old_freq; - int ramp_dir; - struct smartass_info_s *this_smartass; - struct cpufreq_policy *policy; - unsigned int relation = CPUFREQ_RELATION_L; - for_each_possible_cpu(cpu) { - this_smartass = &per_cpu(smartass_info, cpu); - if (!work_cpumask_test_and_clear(cpu)) - continue; - - ramp_dir = this_smartass->ramp_dir; - this_smartass->ramp_dir = 0; - - old_freq = this_smartass->old_freq; - policy = this_smartass->cur_policy; - - if (old_freq != policy->cur) { - // frequency was changed by someone else? - printk(KERN_WARNING "Smartass: frequency changed by 3rd party: %d to %d\n", - old_freq,policy->cur); - new_freq = old_freq; - } - else if (ramp_dir > 0 && nr_running() > 1) { - // ramp up logic: - if (old_freq < this_smartass->ideal_speed) - new_freq = this_smartass->ideal_speed; - else if (ramp_up_step) { - new_freq = old_freq + ramp_up_step; - relation = CPUFREQ_RELATION_H; - } - else { - new_freq = policy->max; - relation = CPUFREQ_RELATION_H; - } - dprintk(SMARTASS_DEBUG_ALG,"smartassQ @ %d ramp up: ramp_dir=%d ideal=%d\n", - old_freq,ramp_dir,this_smartass->ideal_speed); - } - else if (ramp_dir < 0) { - // ramp down logic: - if (old_freq > this_smartass->ideal_speed) { - new_freq = this_smartass->ideal_speed; - relation = CPUFREQ_RELATION_H; - } - else if (ramp_down_step) - new_freq = old_freq - ramp_down_step; - else { - // Load heuristics: Adjust new_freq such that, assuming a linear - // scaling of load vs. frequency, the load in the new frequency - // will be max_cpu_load: - new_freq = old_freq * this_smartass->cur_cpu_load / max_cpu_load; - if (new_freq > old_freq) // min_cpu_load > max_cpu_load ?! - new_freq = old_freq -1; - } - dprintk(SMARTASS_DEBUG_ALG,"smartassQ @ %d ramp down: ramp_dir=%d ideal=%d\n", - old_freq,ramp_dir,this_smartass->ideal_speed); - } - else { // ramp_dir==0 ?! Could the timer change its mind about a queued ramp up/down - // before the work task gets to run? - // This may also happen if we refused to ramp up because the nr_running()==1 - new_freq = old_freq; - dprintk(SMARTASS_DEBUG_ALG,"smartassQ @ %d nothing: ramp_dir=%d nr_running=%lu\n", - old_freq,ramp_dir,nr_running()); - } - - // do actual ramp up (returns 0, if frequency change failed): - new_freq = target_freq(policy,this_smartass,new_freq,old_freq,relation); - if (new_freq) - this_smartass->freq_change_time_in_idle = - get_cpu_idle_time_us(cpu,&this_smartass->freq_change_time); - - // reset timer: - if (new_freq < policy->max) - reset_timer(cpu,this_smartass); - // if we are maxed out, it is pointless to use the timer - // (idle cycles wake up the timer when the timer comes) - else if (timer_pending(&this_smartass->timer)) - del_timer(&this_smartass->timer); - } -} - -static ssize_t show_debug_mask(struct kobject *kobj, struct attribute *attr, char *buf) -{ - return sprintf(buf, "%lu\n", debug_mask); -} - -static ssize_t store_debug_mask(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) -{ - ssize_t res; - unsigned long input; - res = strict_strtoul(buf, 0, &input); - if (res >= 0) - debug_mask = input; - return res; -} - -static ssize_t show_up_rate_us(struct kobject *kobj, struct attribute *attr, char *buf) -{ - return sprintf(buf, "%lu\n", up_rate_us); -} - -static ssize_t store_up_rate_us(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) -{ - ssize_t res; - unsigned long input; - res = strict_strtoul(buf, 0, &input); - if (res >= 0 && input >= 0 && input <= 100000000) - up_rate_us = input; - return res; -} - -static ssize_t show_down_rate_us(struct kobject *kobj, struct attribute *attr, char *buf) -{ - return sprintf(buf, "%lu\n", down_rate_us); -} - -static ssize_t store_down_rate_us(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) -{ - ssize_t res; - unsigned long input; - res = strict_strtoul(buf, 0, &input); - if (res >= 0 && input >= 0 && input <= 100000000) - down_rate_us = input; - return res; -} - -static ssize_t show_sleep_ideal_freq(struct kobject *kobj, struct attribute *attr, char *buf) -{ - return sprintf(buf, "%u\n", sleep_ideal_freq); -} - -static ssize_t store_sleep_ideal_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) -{ - ssize_t res; - unsigned long input; - res = strict_strtoul(buf, 0, &input); - if (res >= 0 && input >= 0) { - sleep_ideal_freq = input; - if (suspended) - smartass_update_min_max_allcpus(); - } - return res; -} - -static ssize_t show_sleep_wakeup_freq(struct kobject *kobj, struct attribute *attr, char *buf) -{ - return sprintf(buf, "%u\n", sleep_wakeup_freq); -} - -static ssize_t store_sleep_wakeup_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) -{ - ssize_t res; - unsigned long input; - res = strict_strtoul(buf, 0, &input); - if (res >= 0 && input >= 0) - sleep_wakeup_freq = input; - return res; -} - -static ssize_t show_awake_ideal_freq(struct kobject *kobj, struct attribute *attr, char *buf) -{ - return sprintf(buf, "%u\n", awake_ideal_freq); -} - -static ssize_t store_awake_ideal_freq(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) -{ - ssize_t res; - unsigned long input; - res = strict_strtoul(buf, 0, &input); - if (res >= 0 && input >= 0) { - awake_ideal_freq = input; - if (!suspended) - smartass_update_min_max_allcpus(); - } - return res; -} - -static ssize_t show_sample_rate_jiffies(struct kobject *kobj, struct attribute *attr, char *buf) -{ - return sprintf(buf, "%u\n", sample_rate_jiffies); -} - -static ssize_t store_sample_rate_jiffies(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) -{ - ssize_t res; - unsigned long input; - res = strict_strtoul(buf, 0, &input); - if (res >= 0 && input > 0 && input <= 1000) - sample_rate_jiffies = input; - return res; -} - -static ssize_t show_ramp_up_step(struct kobject *kobj, struct attribute *attr, char *buf) -{ - return sprintf(buf, "%u\n", ramp_up_step); -} - -static ssize_t store_ramp_up_step(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) -{ - ssize_t res; - unsigned long input; - res = strict_strtoul(buf, 0, &input); - if (res >= 0 && input >= 0) - ramp_up_step = input; - return res; -} - -static ssize_t show_ramp_down_step(struct kobject *kobj, struct attribute *attr, char *buf) -{ - return sprintf(buf, "%u\n", ramp_down_step); -} - -static ssize_t store_ramp_down_step(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) -{ - ssize_t res; - unsigned long input; - res = strict_strtoul(buf, 0, &input); - if (res >= 0 && input >= 0) - ramp_down_step = input; - return res; -} - -static ssize_t show_max_cpu_load(struct kobject *kobj, struct attribute *attr, char *buf) -{ - return sprintf(buf, "%lu\n", max_cpu_load); -} - -static ssize_t store_max_cpu_load(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) -{ - ssize_t res; - unsigned long input; - res = strict_strtoul(buf, 0, &input); - if (res >= 0 && input > 0 && input <= 100) - max_cpu_load = input; - return res; -} - -static ssize_t show_min_cpu_load(struct kobject *kobj, struct attribute *attr, char *buf) -{ - return sprintf(buf, "%lu\n", min_cpu_load); -} - -static ssize_t store_min_cpu_load(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) -{ - ssize_t res; - unsigned long input; - res = strict_strtoul(buf, 0, &input); - if (res >= 0 && input > 0 && input < 100) - min_cpu_load = input; - return res; -} - -#define define_global_rw_attr(_name) \ -static struct global_attr _name##_attr = \ - __ATTR(_name, 0644, show_##_name, store_##_name) - -define_global_rw_attr(debug_mask); -define_global_rw_attr(up_rate_us); -define_global_rw_attr(down_rate_us); -define_global_rw_attr(sleep_ideal_freq); -define_global_rw_attr(sleep_wakeup_freq); -define_global_rw_attr(awake_ideal_freq); -define_global_rw_attr(sample_rate_jiffies); -define_global_rw_attr(ramp_up_step); -define_global_rw_attr(ramp_down_step); -define_global_rw_attr(max_cpu_load); -define_global_rw_attr(min_cpu_load); - -static struct attribute * smartass_attributes[] = { - &debug_mask_attr.attr, - &up_rate_us_attr.attr, - &down_rate_us_attr.attr, - &sleep_ideal_freq_attr.attr, - &sleep_wakeup_freq_attr.attr, - &awake_ideal_freq_attr.attr, - &sample_rate_jiffies_attr.attr, - &ramp_up_step_attr.attr, - &ramp_down_step_attr.attr, - &max_cpu_load_attr.attr, - &min_cpu_load_attr.attr, - NULL, -}; - -static struct attribute_group smartass_attr_group = { - .attrs = smartass_attributes, - .name = "smartassH3", -}; - -static int cpufreq_governor_smartass_h3(struct cpufreq_policy *new_policy, - unsigned int event) -{ - unsigned int cpu = new_policy->cpu; - int rc; - struct smartass_info_s *this_smartass = &per_cpu(smartass_info, cpu); - - switch (event) { - case CPUFREQ_GOV_START: - if ((!cpu_online(cpu)) || (!new_policy->cur)) - return -EINVAL; - - this_smartass->cur_policy = new_policy; - - this_smartass->enable = 1; - - smartass_update_min_max(this_smartass,new_policy,suspended); - - this_smartass->freq_table = cpufreq_frequency_get_table(cpu); - if (!this_smartass->freq_table) - printk(KERN_WARNING "Smartass: no frequency table for cpu %d?!\n",cpu); - - smp_wmb(); - - // Do not register the idle hook and create sysfs - // entries if we have already done so. - if (atomic_inc_return(&active_count) <= 1) { - rc = sysfs_create_group(cpufreq_global_kobject, - &smartass_attr_group); - if (rc) - return rc; - - pm_idle_old = pm_idle; - pm_idle = cpufreq_idle; - } - - if (this_smartass->cur_policy->cur < new_policy->max && !timer_pending(&this_smartass->timer)) - reset_timer(cpu,this_smartass); - - break; - - case CPUFREQ_GOV_LIMITS: - smartass_update_min_max(this_smartass,new_policy,suspended); - - if (this_smartass->cur_policy->cur > new_policy->max) { - dprintk(SMARTASS_DEBUG_JUMPS,"SmartassI: jumping to new max freq: %d\n",new_policy->max); - __cpufreq_driver_target(this_smartass->cur_policy, - new_policy->max, CPUFREQ_RELATION_H); - } - else if (this_smartass->cur_policy->cur < new_policy->min) { - dprintk(SMARTASS_DEBUG_JUMPS,"SmartassI: jumping to new min freq: %d\n",new_policy->min); - __cpufreq_driver_target(this_smartass->cur_policy, - new_policy->min, CPUFREQ_RELATION_L); - } - - if (this_smartass->cur_policy->cur < new_policy->max && !timer_pending(&this_smartass->timer)) - reset_timer(cpu,this_smartass); - - break; - - case CPUFREQ_GOV_STOP: - this_smartass->enable = 0; - smp_wmb(); - del_timer(&this_smartass->timer); - flush_work(&freq_scale_work); - this_smartass->idle_exit_time = 0; - - if (atomic_dec_return(&active_count) <= 1) { - sysfs_remove_group(cpufreq_global_kobject, - &smartass_attr_group); - pm_idle = pm_idle_old; - } - break; - } - - return 0; -} - -static void smartass_suspend(int cpu, int suspend) -{ - struct smartass_info_s *this_smartass = &per_cpu(smartass_info, smp_processor_id()); - struct cpufreq_policy *policy = this_smartass->cur_policy; - unsigned int new_freq; - - if (!this_smartass->enable) - return; - - smartass_update_min_max(this_smartass,policy,suspend); - if (!suspend) { // resume at max speed: - new_freq = validate_freq(policy,sleep_wakeup_freq); - - dprintk(SMARTASS_DEBUG_JUMPS,"SmartassS: awaking at %d\n",new_freq); - - __cpufreq_driver_target(policy, new_freq, - CPUFREQ_RELATION_L); - } else { - // to avoid wakeup issues with quick sleep/wakeup don't change actual frequency when entering sleep - // to allow some time to settle down. Instead we just reset our statistics (and reset the timer). - // Eventually, the timer will adjust the frequency if necessary. - - this_smartass->freq_change_time_in_idle = - get_cpu_idle_time_us(cpu,&this_smartass->freq_change_time); - - dprintk(SMARTASS_DEBUG_JUMPS,"SmartassS: suspending at %d\n",policy->cur); - } - - reset_timer(smp_processor_id(),this_smartass); -} - -static void smartass_early_suspend(struct early_suspend *handler) { - int i; - if (suspended || sleep_ideal_freq==0) // disable behavior for sleep_ideal_freq==0 - return; - suspended = 1; - for_each_online_cpu(i) - smartass_suspend(i,1); -} - -static void smartass_late_resume(struct early_suspend *handler) { - int i; - if (!suspended) // already not suspended so nothing to do - return; - suspended = 0; - for_each_online_cpu(i) - smartass_suspend(i,0); -} - -static struct early_suspend smartass_power_suspend = { - .suspend = smartass_early_suspend, - .resume = smartass_late_resume, -#ifdef CONFIG_MACH_HERO - .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, -#endif -}; - -static int __init cpufreq_smartass_init(void) -{ - unsigned int i; - struct smartass_info_s *this_smartass; - debug_mask = 0; - up_rate_us = DEFAULT_UP_RATE_US; - down_rate_us = DEFAULT_DOWN_RATE_US; - sleep_ideal_freq = DEFAULT_SLEEP_IDEAL_FREQ; - sleep_wakeup_freq = DEFAULT_SLEEP_WAKEUP_FREQ; - awake_ideal_freq = DEFAULT_AWAKE_IDEAL_FREQ; - sample_rate_jiffies = DEFAULT_SAMPLE_RATE_JIFFIES; - ramp_up_step = DEFAULT_RAMP_UP_STEP; - ramp_down_step = DEFAULT_RAMP_DOWN_STEP; - max_cpu_load = DEFAULT_MAX_CPU_LOAD; - min_cpu_load = DEFAULT_MIN_CPU_LOAD; - - spin_lock_init(&cpumask_lock); - - suspended = 0; - - /* Initalize per-cpu data: */ - for_each_possible_cpu(i) { - this_smartass = &per_cpu(smartass_info, i); - this_smartass->enable = 0; - this_smartass->cur_policy = 0; - this_smartass->ramp_dir = 0; - this_smartass->time_in_idle = 0; - this_smartass->idle_exit_time = 0; - this_smartass->freq_change_time = 0; - this_smartass->freq_change_time_in_idle = 0; - this_smartass->cur_cpu_load = 0; - // intialize timer: - init_timer_deferrable(&this_smartass->timer); - this_smartass->timer.function = cpufreq_smartass_timer; - this_smartass->timer.data = i; - work_cpumask_test_and_clear(i); - } - - // Scale up is high priority - up_wq = create_rt_workqueue("ksmartass_up"); - down_wq = create_workqueue("ksmartass_down"); - if (!up_wq || !down_wq) - return -ENOMEM; - - INIT_WORK(&freq_scale_work, cpufreq_smartass_freq_change_time_work); - - register_early_suspend(&smartass_power_suspend); - - return cpufreq_register_governor(&cpufreq_gov_smartass_h3); -} - -#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_SMARTASSH3 -fs_initcall(cpufreq_smartass_init); -#else -module_init(cpufreq_smartass_init); -#endif - -static void __exit cpufreq_smartass_exit(void) -{ - cpufreq_unregister_governor(&cpufreq_gov_smartass_h3); - destroy_workqueue(up_wq); - destroy_workqueue(down_wq); -} - -module_exit(cpufreq_smartass_exit); - -MODULE_AUTHOR ("Erasmux, moded by H3ROS & C3C0, ported for SEMC by Daveee10"); -MODULE_DESCRIPTION ("'cpufreq_smartassH3' - A smart cpufreq governor"); -MODULE_LICENSE ("GPL"); - From bfe0855d7f27aa303d857d067390d9966314d25f Mon Sep 17 00:00:00 2001 From: flar2 Date: Fri, 13 Sep 2013 22:15:55 -0400 Subject: [PATCH 2554/2556] exfat filesystem support --- fs/Kconfig | 1 + fs/Makefile | 1 + fs/exfat/Kconfig | 19 + fs/exfat/Makefile | 4 + fs/exfat/Makefile.module | 20 + fs/exfat/README.md | 71 + fs/exfat/exfat.h | 675 +++++ fs/exfat/exfat_api.c | 563 +++++ fs/exfat/exfat_api.h | 221 ++ fs/exfat/exfat_blkdev.c | 190 ++ fs/exfat/exfat_blkdev.h | 83 + fs/exfat/exfat_cache.c | 785 ++++++ fs/exfat/exfat_cache.h | 94 + fs/exfat/exfat_config.h | 102 + fs/exfat/exfat_core.c | 5187 ++++++++++++++++++++++++++++++++++++++ fs/exfat/exfat_data.c | 77 + fs/exfat/exfat_data.h | 83 + fs/exfat/exfat_global.c | 168 ++ fs/exfat/exfat_global.h | 214 ++ fs/exfat/exfat_nls.c | 394 +++ fs/exfat/exfat_nls.h | 101 + fs/exfat/exfat_oal.c | 189 ++ fs/exfat/exfat_oal.h | 88 + fs/exfat/exfat_part.h | 92 + fs/exfat/exfat_super.c | 2301 +++++++++++++++++ fs/exfat/exfat_super.h | 172 ++ fs/exfat/exfat_upcase.c | 408 +++ fs/exfat/exfat_version.h | 19 + 28 files changed, 12322 insertions(+) create mode 100644 fs/exfat/Kconfig create mode 100644 fs/exfat/Makefile create mode 100644 fs/exfat/Makefile.module create mode 100644 fs/exfat/README.md create mode 100644 fs/exfat/exfat.h create mode 100644 fs/exfat/exfat_api.c create mode 100644 fs/exfat/exfat_api.h create mode 100644 fs/exfat/exfat_blkdev.c create mode 100644 fs/exfat/exfat_blkdev.h create mode 100644 fs/exfat/exfat_cache.c create mode 100644 fs/exfat/exfat_cache.h create mode 100644 fs/exfat/exfat_config.h create mode 100644 fs/exfat/exfat_core.c create mode 100644 fs/exfat/exfat_data.c create mode 100644 fs/exfat/exfat_data.h create mode 100644 fs/exfat/exfat_global.c create mode 100644 fs/exfat/exfat_global.h create mode 100644 fs/exfat/exfat_nls.c create mode 100644 fs/exfat/exfat_nls.h create mode 100644 fs/exfat/exfat_oal.c create mode 100644 fs/exfat/exfat_oal.h create mode 100644 fs/exfat/exfat_part.h create mode 100644 fs/exfat/exfat_super.c create mode 100644 fs/exfat/exfat_super.h create mode 100644 fs/exfat/exfat_upcase.c create mode 100644 fs/exfat/exfat_version.h diff --git a/fs/Kconfig b/fs/Kconfig index aabf18f291479..a219eb3794cbb 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -99,6 +99,7 @@ menu "DOS/FAT/NT Filesystems" source "fs/fat/Kconfig" source "fs/ntfs/Kconfig" +source "fs/exfat/Kconfig" endmenu endif # BLOCK diff --git a/fs/Makefile b/fs/Makefile index 31b7d85e2ea89..cc6cd29f222e3 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -92,6 +92,7 @@ obj-$(CONFIG_CIFS) += cifs/ obj-$(CONFIG_NCP_FS) += ncpfs/ obj-$(CONFIG_HPFS_FS) += hpfs/ obj-$(CONFIG_NTFS_FS) += ntfs/ +obj-$(CONFIG_EXFAT_FS) += exfat/ obj-$(CONFIG_UFS_FS) += ufs/ obj-$(CONFIG_EFS_FS) += efs/ obj-$(CONFIG_JFFS2_FS) += jffs2/ diff --git a/fs/exfat/Kconfig b/fs/exfat/Kconfig new file mode 100644 index 0000000000000..144b6eccabebf --- /dev/null +++ b/fs/exfat/Kconfig @@ -0,0 +1,19 @@ +config EXFAT_FS + tristate "exFAT filesystem support" + select NLS + help + exFAT driver from Samsung + +config EXFAT_DEFAULT_CODEPAGE + int "Default codepage for exFAT" + depends on EXFAT_FS + default 437 + help + This option should be set to the codepage of your exFAT filesystems. + +config EXFAT_DEFAULT_IOCHARSET + string "Default iocharset for exFAT" + depends on EXFAT_FS + default "utf8" + help + Set this to the default input/output character set you'd like exFAT to use. diff --git a/fs/exfat/Makefile b/fs/exfat/Makefile new file mode 100644 index 0000000000000..77dc36c6183b9 --- /dev/null +++ b/fs/exfat/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_EXFAT_FS) += exfat.o + +exfat-y := exfat_core.o exfat_api.o exfat_blkdev.o exfat_cache.o exfat_super.o \ + exfat_data.o exfat_global.o exfat_nls.o exfat_oal.o exfat_upcase.o diff --git a/fs/exfat/Makefile.module b/fs/exfat/Makefile.module new file mode 100644 index 0000000000000..2e97826d059d5 --- /dev/null +++ b/fs/exfat/Makefile.module @@ -0,0 +1,20 @@ +EXTRA_FLAGS += -I$(PWD) + +#KDIR := /usr/src/linux/ +KDIR := /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +export CONFIG_EXFAT_FS := m + +all: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KDIR) M=$(PWD) clean + +help: + $(MAKE) -C $(KDIR) M=$(PWD) help + +.PHONY : install +install : all + sudo $(MAKE) -C $(KDIR) M=$(PWD) modules_install; sudo depmod diff --git a/fs/exfat/README.md b/fs/exfat/README.md new file mode 100644 index 0000000000000..9ab089f2fa8a5 --- /dev/null +++ b/fs/exfat/README.md @@ -0,0 +1,71 @@ +exfat-nofuse +============ + +Linux non-fuse read/write kernel driver for the exFAT file system.
+Originally ported from android kernel v3.0. + + +Kudos to ksv1986 for the mutex patch!
+Thanks to JackNorris for being awesome and providing the clear_inode() patch.
+
+Big thanks to lqs for completing the driver! +Big thanks to benpicco for fixing 3.11.y compatibility! + + +Special thanks to github user AndreiLux for spreading the word about the leak!
+ + +Installation as stand alone module: +==================================== + + make -f Makefile.module KDIR="path to kernel source" CROSS_COMPILE="path to android chain tools (as linaro)/bin/SOMETHING- (see your folder for clues)" + +Example how it's works for me! + + make -f Makefile.module CROSS_COMPILE=../dorimanx-SG2-I9100-Kernel/android-toolchain/bin/arm-eabi- KDIR=../dorimanx-SG2-I9100-Kernel/ + +exfat.ko module file will be created in exfat source folder. and will work with kernel source you have used. + + make -f Makefile.module install + +To load the driver manually, run this as root: + + modprobe exfat + +To add to kernel you need to do this: +====================================== + +cd your kernel source dir + +mkdir fs/exfat + +copy all files (exept .git) from exfat-nofuse to your kernel source fs/exfat/ + +see +https://github.com/dorimanx/Dorimanx-SG2-I9100-Kernel/commit/e8fc728a68096db9ffcebff40244ebfb60a3de18 + +edit fs/Kconfig +edit fs/Makefile + +cd your kernel source +make menuconfig + +Go to: +> File systems > DOS/FAT/NT > check the exfat as MODULE (M) +> (437) Default codepage for exFAT +> (utf8) Default iocharset for exFAT + +> ESC to main menu +> Save an Alternate Configuration File +> ESC ESC + +build your kernel. + +and you will have new module! + +exfat.ko + +have fun. + +Free Software for the Free Minds! +===================================== diff --git a/fs/exfat/exfat.h b/fs/exfat/exfat.h new file mode 100644 index 0000000000000..a37f4b154066d --- /dev/null +++ b/fs/exfat/exfat.h @@ -0,0 +1,675 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat.h */ +/* PURPOSE : Header File for exFAT File Manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_H +#define _EXFAT_H + +#include "exfat_config.h" +#include "exfat_global.h" +#include "exfat_data.h" +#include "exfat_oal.h" + +#include "exfat_blkdev.h" +#include "exfat_cache.h" +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat_cache.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#if EXFAT_CONFIG_KERNEL_DEBUG + /* For Debugging Purpose */ + /* IOCTL code 'f' used by + * - file systems typically #0~0x1F + * - embedded terminal devices #128~ + * - exts for debugging purpose #99 + * number 100 and 101 is availble now but has possible conflicts + */ +#define EXFAT_IOC_GET_DEBUGFLAGS _IOR('f', 100, long) +#define EXFAT_IOC_SET_DEBUGFLAGS _IOW('f', 101, long) + +#define EXFAT_DEBUGFLAGS_INVALID_UMOUNT 0x01 +#define EXFAT_DEBUGFLAGS_ERROR_RW 0x02 +#endif /* EXFAT_CONFIG_KERNEL_DEBUG */ + + /*----------------------------------------------------------------------*/ + /* Constant & Macro Definitions */ + /*----------------------------------------------------------------------*/ + +#define MAX_VOLUME 4 // max num of volumes per device + +#define DENTRY_SIZE 32 // dir entry size +#define DENTRY_SIZE_BITS 5 + + /* PBR entries */ +#define PBR_SIGNATURE 0xAA55 +#define EXT_SIGNATURE 0xAA550000 +#define VOL_LABEL "NO NAME " // size should be 11 +#define OEM_NAME "MSWIN4.1" // size should be 8 +#define STR_FAT12 "FAT12 " // size should be 8 +#define STR_FAT16 "FAT16 " // size should be 8 +#define STR_FAT32 "FAT32 " // size should be 8 +#define STR_EXFAT "EXFAT " // size should be 8 +#define VOL_CLEAN 0x0000 +#define VOL_DIRTY 0x0002 + + /* max number of clusters */ +#define FAT12_THRESHOLD 4087 // 2^12 - 1 + 2 (clu 0 & 1) +#define FAT16_THRESHOLD 65527 // 2^16 - 1 + 2 +#define FAT32_THRESHOLD 268435457 // 2^28 - 1 + 2 +#define EXFAT_THRESHOLD 268435457 // 2^28 - 1 + 2 + + /* file types */ +#define TYPE_UNUSED 0x0000 +#define TYPE_DELETED 0x0001 +#define TYPE_INVALID 0x0002 +#define TYPE_CRITICAL_PRI 0x0100 +#define TYPE_BITMAP 0x0101 +#define TYPE_UPCASE 0x0102 +#define TYPE_VOLUME 0x0103 +#define TYPE_DIR 0x0104 +#define TYPE_FILE 0x011F +#define TYPE_SYMLINK 0x015F +#define TYPE_CRITICAL_SEC 0x0200 +#define TYPE_STREAM 0x0201 +#define TYPE_EXTEND 0x0202 +#define TYPE_ACL 0x0203 +#define TYPE_BENIGN_PRI 0x0400 +#define TYPE_GUID 0x0401 +#define TYPE_PADDING 0x0402 +#define TYPE_ACLTAB 0x0403 +#define TYPE_BENIGN_SEC 0x0800 +#define TYPE_ALL 0x0FFF + + /* time modes */ +#define TM_CREATE 0 +#define TM_MODIFY 1 +#define TM_ACCESS 2 + + /* checksum types */ +#define CS_DIR_ENTRY 0 +#define CS_PBR_SECTOR 1 +#define CS_DEFAULT 2 + +#define CLUSTER_16(x) ((UINT16)(x)) +#define CLUSTER_32(x) ((UINT32)(x)) + +#define START_SECTOR(x) \ + ( (((x)-2) << p_fs->sectors_per_clu_bits) + p_fs->data_start_sector ) + +#define IS_LAST_SECTOR_IN_CLUSTER(sec) \ + ( (((sec) - p_fs->data_start_sector + 1) & ((1 << p_fs->sectors_per_clu_bits) -1)) == 0) + +#define GET_CLUSTER_FROM_SECTOR(sec) \ + ((((sec) - p_fs->data_start_sector) >> p_fs->sectors_per_clu_bits) +2) + +#define GET16(p_src) \ + ( ((UINT16)(p_src)[0]) | (((UINT16)(p_src)[1]) << 8) ) +#define GET32(p_src) \ + ( ((UINT32)(p_src)[0]) | (((UINT32)(p_src)[1]) << 8) | \ + (((UINT32)(p_src)[2]) << 16) | (((UINT32)(p_src)[3]) << 24) ) +#define GET64(p_src) \ + ( ((UINT64)(p_src)[0]) | (((UINT64)(p_src)[1]) << 8) | \ + (((UINT64)(p_src)[2]) << 16) | (((UINT64)(p_src)[3]) << 24) | \ + (((UINT64)(p_src)[4]) << 32) | (((UINT64)(p_src)[5]) << 40) | \ + (((UINT64)(p_src)[6]) << 48) | (((UINT64)(p_src)[7]) << 56) ) + + +#define SET16(p_dst,src) \ + do { \ + (p_dst)[0]=(UINT8)(src); \ + (p_dst)[1]=(UINT8)(((UINT16)(src)) >> 8); \ + } while (0) +#define SET32(p_dst,src) \ + do { \ + (p_dst)[0]=(UINT8)(src); \ + (p_dst)[1]=(UINT8)(((UINT32)(src)) >> 8); \ + (p_dst)[2]=(UINT8)(((UINT32)(src)) >> 16); \ + (p_dst)[3]=(UINT8)(((UINT32)(src)) >> 24); \ + } while (0) +#define SET64(p_dst,src) \ + do { \ + (p_dst)[0]=(UINT8)(src); \ + (p_dst)[1]=(UINT8)(((UINT64)(src)) >> 8); \ + (p_dst)[2]=(UINT8)(((UINT64)(src)) >> 16); \ + (p_dst)[3]=(UINT8)(((UINT64)(src)) >> 24); \ + (p_dst)[4]=(UINT8)(((UINT64)(src)) >> 32); \ + (p_dst)[5]=(UINT8)(((UINT64)(src)) >> 40); \ + (p_dst)[6]=(UINT8)(((UINT64)(src)) >> 48); \ + (p_dst)[7]=(UINT8)(((UINT64)(src)) >> 56); \ + } while (0) + +#ifdef __LITTLE_ENDIAN +#define GET16_A(p_src) (*((UINT16 *)(p_src))) +#define GET32_A(p_src) (*((UINT32 *)(p_src))) +#define GET64_A(p_src) (*((UINT64 *)(p_src))) +#define SET16_A(p_dst,src) *((UINT16 *)(p_dst)) = (UINT16)(src) +#define SET32_A(p_dst,src) *((UINT32 *)(p_dst)) = (UINT32)(src) +#define SET64_A(p_dst,src) *((UINT64 *)(p_dst)) = (UINT64)(src) +#else +#define GET16_A(p_src) GET16(p_src) +#define GET32_A(p_src) GET32(p_src) +#define GET64_A(p_src) GET64(p_src) +#define SET16_A(p_dst,src) SET16(p_dst, src) +#define SET32_A(p_dst,src) SET32(p_dst, src) +#define SET64_A(p_dst,src) SET64(p_dst, src) +#endif + + /* Upcase tabel mecro */ +#define HIGH_INDEX_BIT (8) +#define HIGH_INDEX_MASK (0xFF00) +#define LOW_INDEX_BIT (16-HIGH_INDEX_BIT) +#define UTBL_ROW_COUNT (1<> LOW_INDEX_BIT; + } + static inline UINT16 get_row_index(UINT16 i) + { + return i & ~HIGH_INDEX_MASK; + } + /*----------------------------------------------------------------------*/ + /* Type Definitions */ + /*----------------------------------------------------------------------*/ + + /* MS_DOS FAT partition boot record (512 bytes) */ + typedef struct { + UINT8 jmp_boot[3]; + UINT8 oem_name[8]; + UINT8 bpb[109]; + UINT8 boot_code[390]; + UINT8 signature[2]; + } PBR_SECTOR_T; + + /* MS-DOS FAT12/16 BIOS parameter block (51 bytes) */ + typedef struct { + UINT8 sector_size[2]; + UINT8 sectors_per_clu; + UINT8 num_reserved[2]; + UINT8 num_fats; + UINT8 num_root_entries[2]; + UINT8 num_sectors[2]; + UINT8 media_type; + UINT8 num_fat_sectors[2]; + UINT8 sectors_in_track[2]; + UINT8 num_heads[2]; + UINT8 num_hid_sectors[4]; + UINT8 num_huge_sectors[4]; + + UINT8 phy_drv_no; + UINT8 reserved; + UINT8 ext_signature; + UINT8 vol_serial[4]; + UINT8 vol_label[11]; + UINT8 vol_type[8]; + } BPB16_T; + + /* MS-DOS FAT32 BIOS parameter block (79 bytes) */ + typedef struct { + UINT8 sector_size[2]; + UINT8 sectors_per_clu; + UINT8 num_reserved[2]; + UINT8 num_fats; + UINT8 num_root_entries[2]; + UINT8 num_sectors[2]; + UINT8 media_type; + UINT8 num_fat_sectors[2]; + UINT8 sectors_in_track[2]; + UINT8 num_heads[2]; + UINT8 num_hid_sectors[4]; + UINT8 num_huge_sectors[4]; + UINT8 num_fat32_sectors[4]; + UINT8 ext_flags[2]; + UINT8 fs_version[2]; + UINT8 root_cluster[4]; + UINT8 fsinfo_sector[2]; + UINT8 backup_sector[2]; + UINT8 reserved[12]; + + UINT8 phy_drv_no; + UINT8 ext_reserved; + UINT8 ext_signature; + UINT8 vol_serial[4]; + UINT8 vol_label[11]; + UINT8 vol_type[8]; + } BPB32_T; + + /* MS-DOS EXFAT BIOS parameter block (109 bytes) */ + typedef struct { + UINT8 reserved1[53]; + UINT8 vol_offset[8]; + UINT8 vol_length[8]; + UINT8 fat_offset[4]; + UINT8 fat_length[4]; + UINT8 clu_offset[4]; + UINT8 clu_count[4]; + UINT8 root_cluster[4]; + UINT8 vol_serial[4]; + UINT8 fs_version[2]; + UINT8 vol_flags[2]; + UINT8 sector_size_bits; + UINT8 sectors_per_clu_bits; + UINT8 num_fats; + UINT8 phy_drv_no; + UINT8 perc_in_use; + UINT8 reserved2[7]; + } BPBEX_T; + + /* MS-DOS FAT file system information sector (512 bytes) */ + typedef struct { + UINT8 signature1[4]; // aligned + UINT8 reserved1[480]; + UINT8 signature2[4]; // aligned + UINT8 free_cluster[4]; // aligned + UINT8 next_cluster[4]; // aligned + UINT8 reserved2[14]; + UINT8 signature3[2]; + } FSI_SECTOR_T; + + /* MS-DOS FAT directory entry (32 bytes) */ + typedef struct { + UINT8 dummy[32]; + } DENTRY_T; + + typedef struct { + UINT8 name[DOS_NAME_LENGTH]; + UINT8 attr; + UINT8 lcase; + UINT8 create_time_ms; + UINT8 create_time[2]; // aligned + UINT8 create_date[2]; // aligned + UINT8 access_date[2]; // aligned + UINT8 start_clu_hi[2]; // aligned + UINT8 modify_time[2]; // aligned + UINT8 modify_date[2]; // aligned + UINT8 start_clu_lo[2]; // aligned + UINT8 size[4]; // aligned + } DOS_DENTRY_T; + + /* MS-DOS FAT extended directory entry (32 bytes) */ + typedef struct { + UINT8 order; + UINT8 unicode_0_4[10]; + UINT8 attr; + UINT8 sysid; + UINT8 checksum; + UINT8 unicode_5_10[12]; // aligned + UINT8 start_clu[2]; // aligned + UINT8 unicode_11_12[4]; // aligned + } EXT_DENTRY_T; + + /* MS-DOS EXFAT file directory entry (32 bytes) */ + typedef struct { + UINT8 type; + UINT8 num_ext; + UINT8 checksum[2]; // aligned + UINT8 attr[2]; // aligned + UINT8 reserved1[2]; + UINT8 create_time[2]; // aligned + UINT8 create_date[2]; // aligned + UINT8 modify_time[2]; // aligned + UINT8 modify_date[2]; // aligned + UINT8 access_time[2]; // aligned + UINT8 access_date[2]; // aligned + UINT8 create_time_ms; + UINT8 modify_time_ms; + UINT8 access_time_ms; + UINT8 reserved2[9]; + } FILE_DENTRY_T; + + /* MS-DOS EXFAT stream extension directory entry (32 bytes) */ + typedef struct { + UINT8 type; + UINT8 flags; + UINT8 reserved1; + UINT8 name_len; + UINT8 name_hash[2]; // aligned + UINT8 reserved2[2]; + UINT8 valid_size[8]; // aligned + UINT8 reserved3[4]; // aligned + UINT8 start_clu[4]; // aligned + UINT8 size[8]; // aligned + } STRM_DENTRY_T; + + /* MS-DOS EXFAT file name directory entry (32 bytes) */ + typedef struct { + UINT8 type; + UINT8 flags; + UINT8 unicode_0_14[30]; // aligned + } NAME_DENTRY_T; + + /* MS-DOS EXFAT allocation bitmap directory entry (32 bytes) */ + typedef struct { + UINT8 type; + UINT8 flags; + UINT8 reserved[18]; + UINT8 start_clu[4]; // aligned + UINT8 size[8]; // aligned + } BMAP_DENTRY_T; + + /* MS-DOS EXFAT up-case table directory entry (32 bytes) */ + typedef struct { + UINT8 type; + UINT8 reserved1[3]; + UINT8 checksum[4]; // aligned + UINT8 reserved2[12]; + UINT8 start_clu[4]; // aligned + UINT8 size[8]; // aligned + } CASE_DENTRY_T; + + /* MS-DOS EXFAT volume label directory entry (32 bytes) */ + typedef struct { + UINT8 type; + UINT8 label_len; + UINT8 unicode_0_10[22]; // aligned + UINT8 reserved[8]; + } VOLM_DENTRY_T; + + /* unused entry hint information */ + typedef struct { + UINT32 dir; + INT32 entry; + CHAIN_T clu; + } UENTRY_T; + + /* file system volume information structure */ + typedef struct __FS_STRUCT_T { + UINT32 mounted; + struct super_block *sb; + struct semaphore v_sem; + } FS_STRUCT_T; + + typedef struct { + INT32 (*alloc_cluster)(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain); + void (*free_cluster)(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse); + INT32 (*count_used_clusters)(struct super_block *sb); + + INT32 (*init_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, + UINT32 start_clu, UINT64 size); + INT32 (*init_ext_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, + UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); + INT32 (*find_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type); + void (*delete_dir_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 offset, INT32 num_entries); + void (*get_uni_name_from_ext_entry)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname); + INT32 (*count_ext_entries)(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry); + INT32 (*calc_num_entries)(UNI_NAME_T *p_uniname); + + UINT32 (*get_entry_type)(DENTRY_T *p_entry); + void (*set_entry_type)(DENTRY_T *p_entry, UINT32 type); + UINT32 (*get_entry_attr)(DENTRY_T *p_entry); + void (*set_entry_attr)(DENTRY_T *p_entry, UINT32 attr); + UINT8 (*get_entry_flag)(DENTRY_T *p_entry); + void (*set_entry_flag)(DENTRY_T *p_entry, UINT8 flag); + UINT32 (*get_entry_clu0)(DENTRY_T *p_entry); + void (*set_entry_clu0)(DENTRY_T *p_entry, UINT32 clu0); + UINT64 (*get_entry_size)(DENTRY_T *p_entry); + void (*set_entry_size)(DENTRY_T *p_entry, UINT64 size); + void (*get_entry_time)(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); + void (*set_entry_time)(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); + } FS_FUNC_T; + + typedef struct __FS_INFO_T { + UINT32 drv; // drive ID + UINT32 vol_type; // volume FAT type + UINT32 vol_id; // volume serial number + + UINT32 num_sectors; // num of sectors in volume + UINT32 num_clusters; // num of clusters in volume + UINT32 cluster_size; // cluster size in bytes + UINT32 cluster_size_bits; + UINT32 sectors_per_clu; // cluster size in sectors + UINT32 sectors_per_clu_bits; + + UINT32 PBR_sector; // PBR sector + UINT32 FAT1_start_sector; // FAT1 start sector + UINT32 FAT2_start_sector; // FAT2 start sector + UINT32 root_start_sector; // root dir start sector + UINT32 data_start_sector; // data area start sector + UINT32 num_FAT_sectors; // num of FAT sectors + + UINT32 root_dir; // root dir cluster + UINT32 dentries_in_root; // num of dentries in root dir + UINT32 dentries_per_clu; // num of dentries per cluster + + UINT32 vol_flag; // volume dirty flag + struct buffer_head *pbr_bh; // PBR sector + + UINT32 map_clu; // allocation bitmap start cluster + UINT32 map_sectors; // num of allocation bitmap sectors + struct buffer_head **vol_amap; // allocation bitmap + + UINT16 **vol_utbl; // upcase table + + UINT32 clu_srch_ptr; // cluster search pointer + UINT32 used_clusters; // number of used clusters + UENTRY_T hint_uentry; // unused entry hint information + + UINT32 dev_ejected; // block device operation error flag + + FS_FUNC_T *fs_func; + + /* FAT cache */ + BUF_CACHE_T FAT_cache_array[FAT_CACHE_SIZE]; + BUF_CACHE_T FAT_cache_lru_list; + BUF_CACHE_T FAT_cache_hash_list[FAT_CACHE_HASH_SIZE]; + + /* buf cache */ + BUF_CACHE_T buf_cache_array[BUF_CACHE_SIZE]; + BUF_CACHE_T buf_cache_lru_list; + BUF_CACHE_T buf_cache_hash_list[BUF_CACHE_HASH_SIZE]; + } FS_INFO_T; + +#define ES_2_ENTRIES 2 +#define ES_3_ENTRIES 3 +#define ES_ALL_ENTRIES 0 + + typedef struct { + UINT32 sector; // sector number that contains file_entry + INT32 offset; // byte offset in the sector + INT32 alloc_flag; // flag in stream entry. 01 for cluster chain, 03 for contig. clusteres. + UINT32 num_entries; + + // __buf should be the last member + void *__buf; + } ENTRY_SET_CACHE_T; + + /*----------------------------------------------------------------------*/ + /* External Function Declarations */ + /*----------------------------------------------------------------------*/ + + /* file system initialization & shutdown functions */ + INT32 ffsInit(void); + INT32 ffsShutdown(void); + + /* volume management functions */ + INT32 ffsMountVol(struct super_block *sb, INT32 drv); + INT32 ffsUmountVol(struct super_block *sb); + INT32 ffsCheckVol(struct super_block *sb); + INT32 ffsGetVolInfo(struct super_block *sb, VOL_INFO_T *info); + INT32 ffsSyncVol(struct super_block *sb, INT32 do_sync); + + /* file management functions */ + INT32 ffsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid); + INT32 ffsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid); + INT32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount); + INT32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount); + INT32 ffsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size); + INT32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry); + INT32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid); + INT32 ffsSetAttr(struct inode *inode, UINT32 attr); + INT32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info); + INT32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info); + INT32 ffsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu); + + /* directory management functions */ + INT32 ffsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid); + INT32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_ent); + INT32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid); + + /*----------------------------------------------------------------------*/ + /* External Function Declarations (NOT TO UPPER LAYER) */ + /*----------------------------------------------------------------------*/ + + /* fs management functions */ + INT32 fs_init(void); + INT32 fs_shutdown(void); + void fs_set_vol_flags(struct super_block *sb, UINT32 new_flag); + void fs_sync(struct super_block *sb, INT32 do_sync); + void fs_error(struct super_block *sb); + + /* cluster management functions */ + INT32 clear_cluster(struct super_block *sb, UINT32 clu); + INT32 fat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain); + INT32 exfat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain); + void fat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse); + void exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse); + UINT32 find_last_cluster(struct super_block *sb, CHAIN_T *p_chain); + INT32 count_num_clusters(struct super_block *sb, CHAIN_T *dir); + INT32 fat_count_used_clusters(struct super_block *sb); + INT32 exfat_count_used_clusters(struct super_block *sb); + void exfat_chain_cont_cluster(struct super_block *sb, UINT32 chain, INT32 len); + + /* allocation bitmap management functions */ + INT32 load_alloc_bitmap(struct super_block *sb); + void free_alloc_bitmap(struct super_block *sb); + INT32 set_alloc_bitmap(struct super_block *sb, UINT32 clu); + INT32 clr_alloc_bitmap(struct super_block *sb, UINT32 clu); + UINT32 test_alloc_bitmap(struct super_block *sb, UINT32 clu); + void sync_alloc_bitmap(struct super_block *sb); + + /* upcase table management functions */ + INT32 load_upcase_table(struct super_block *sb); + void free_upcase_table(struct super_block *sb); + + /* dir entry management functions */ + UINT32 fat_get_entry_type(DENTRY_T *p_entry); + UINT32 exfat_get_entry_type(DENTRY_T *p_entry); + void fat_set_entry_type(DENTRY_T *p_entry, UINT32 type); + void exfat_set_entry_type(DENTRY_T *p_entry, UINT32 type); + UINT32 fat_get_entry_attr(DENTRY_T *p_entry); + UINT32 exfat_get_entry_attr(DENTRY_T *p_entry); + void fat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr); + void exfat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr); + UINT8 fat_get_entry_flag(DENTRY_T *p_entry); + UINT8 exfat_get_entry_flag(DENTRY_T *p_entry); + void fat_set_entry_flag(DENTRY_T *p_entry, UINT8 flag); + void exfat_set_entry_flag(DENTRY_T *p_entry, UINT8 flag); + UINT32 fat_get_entry_clu0(DENTRY_T *p_entry); + UINT32 exfat_get_entry_clu0(DENTRY_T *p_entry); + void fat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu); + void exfat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu); + UINT64 fat_get_entry_size(DENTRY_T *p_entry); + UINT64 exfat_get_entry_size(DENTRY_T *p_entry); + void fat_set_entry_size(DENTRY_T *p_entry, UINT64 size); + void exfat_set_entry_size(DENTRY_T *p_entry, UINT64 size); + void fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); + void exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); + void fat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); + void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode); + INT32 fat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, UINT32 start_clu, UINT64 size); + INT32 exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, UINT32 start_clu, UINT64 size); + INT32 fat_init_ext_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); + INT32 exfat_init_ext_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); + void init_dos_entry(DOS_DENTRY_T *ep, UINT32 type, UINT32 start_clu); + void init_ext_entry(EXT_DENTRY_T *ep, INT32 order, UINT8 chksum, UINT16 *uniname); + void init_file_entry(FILE_DENTRY_T *ep, UINT32 type); + void init_strm_entry(STRM_DENTRY_T *ep, UINT8 flags, UINT32 start_clu, UINT64 size); + void init_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname); + void fat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries); + void exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries); + + INT32 find_location(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector, INT32 *offset); + DENTRY_T *get_entry_with_sector(struct super_block *sb, UINT32 sector, INT32 offset); + DENTRY_T *get_entry_in_dir(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector); + ENTRY_SET_CACHE_T *get_entry_set_in_dir (struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, DENTRY_T **file_ep); + void release_entry_set (ENTRY_SET_CACHE_T *es); + INT32 write_whole_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es); + INT32 write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, DENTRY_T *ep, UINT32 count); + INT32 search_deleted_or_unused_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 num_entries); + INT32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, INT32 num_entries); + INT32 fat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type); + INT32 exfat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type); + INT32 fat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry); + INT32 exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry); + INT32 count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, UINT32 type); + void update_dir_checksum(struct super_block *sb, CHAIN_T *p_dir, INT32 entry); + void update_dir_checksum_with_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es); + BOOL is_dir_empty(struct super_block *sb, CHAIN_T *p_dir); + + /* name conversion functions */ + INT32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 *entries, DOS_NAME_T *p_dosname); + void get_uni_name_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, UINT8 mode); + void fat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname); + void exfat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname); + INT32 extract_uni_name_from_ext_entry(EXT_DENTRY_T *ep, UINT16 *uniname, INT32 order); + INT32 extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname, INT32 order); + INT32 fat_generate_dos_name(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname); + void fat_attach_count_to_dos_name(UINT8 *dosname, INT32 count); + INT32 fat_calc_num_entries(UNI_NAME_T *p_uniname); + INT32 exfat_calc_num_entries(UNI_NAME_T *p_uniname); + UINT8 calc_checksum_1byte(void *data, INT32 len, UINT8 chksum); + UINT16 calc_checksum_2byte(void *data, INT32 len, UINT16 chksum, INT32 type); + UINT32 calc_checksum_4byte(void *data, INT32 len, UINT32 chksum, INT32 type); + + /* name resolution functions */ + INT32 resolve_path(struct inode *inode, UINT8 *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname); + INT32 resolve_name(UINT8 *name, UINT8 **arg); + + /* file operation functions */ + INT32 fat16_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); + INT32 fat32_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); + INT32 exfat_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr); + INT32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid); + INT32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, UINT8 mode, FILE_ID_T *fid); + void remove_file(struct inode *inode, CHAIN_T *p_dir, INT32 entry); + INT32 rename_file(struct inode *inode, CHAIN_T *p_dir, INT32 old_entry, UNI_NAME_T *p_uniname, FILE_ID_T *fid); + INT32 move_file(struct inode *inode, CHAIN_T *p_olddir, INT32 oldentry, CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid); + + /* sector read/write functions */ + INT32 sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 read); + INT32 sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 sync); + INT32 multi_sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 num_secs, INT32 read); + INT32 multi_sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 num_secs, INT32 sync); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _EXFAT_H */ + +/* end of exfat.h */ diff --git a/fs/exfat/exfat_api.c b/fs/exfat/exfat_api.c new file mode 100644 index 0000000000000..5423181260b75 --- /dev/null +++ b/fs/exfat/exfat_api.c @@ -0,0 +1,563 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_api.c */ +/* PURPOSE : exFAT API Glue Layer */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include +#include +#include + +#include "exfat_version.h" +#include "exfat_config.h" +#include "exfat_global.h" +#include "exfat_data.h" +#include "exfat_oal.h" + +#include "exfat_part.h" +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat_super.h" +#include "exfat.h" + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +extern FS_STRUCT_T fs_struct[]; + +extern struct semaphore z_sem; + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Local Function Declarations */ +/*----------------------------------------------------------------------*/ + +/*======================================================================*/ +/* Global Function Definitions */ +/* - All functions for global use have same return value format, */ +/* that is, FFS_SUCCESS on success and several FS error code on */ +/* various error condition. */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* exFAT Filesystem Init & Exit Functions */ +/*----------------------------------------------------------------------*/ + +INT32 FsInit(void) +{ + INT32 i; + + /* initialize all volumes as un-mounted */ + for (i = 0; i < MAX_DRIVE; i++) { + fs_struct[i].mounted = FALSE; + fs_struct[i].sb = NULL; + sm_init(&(fs_struct[i].v_sem)); + } + + return(ffsInit()); +} + +INT32 FsShutdown(void) +{ + INT32 i; + + /* unmount all volumes */ + for (i = 0; i < MAX_DRIVE; i++) { + if (!fs_struct[i].mounted) continue; + + ffsUmountVol(fs_struct[i].sb); + } + + return(ffsShutdown()); +} + +/*----------------------------------------------------------------------*/ +/* Volume Management Functions */ +/*----------------------------------------------------------------------*/ + +/* FsMountVol : mount the file system volume */ +INT32 FsMountVol(struct super_block *sb) +{ + INT32 err, drv; + + sm_P(&z_sem); + + for (drv = 0; drv < MAX_DRIVE; drv++) { + if (!fs_struct[drv].mounted) break; + } + + if (drv >= MAX_DRIVE) return(FFS_ERROR); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[drv].v_sem)); + + err = buf_init(sb); + if (!err) { + err = ffsMountVol(sb, drv); + } + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[drv].v_sem)); + + if (!err) { + fs_struct[drv].mounted = TRUE; + fs_struct[drv].sb = sb; + } else { + buf_shutdown(sb); + } + + sm_V(&z_sem); + + return(err); +} /* end of FsMountVol */ + +/* FsUmountVol : unmount the file system volume */ +INT32 FsUmountVol(struct super_block *sb) +{ + INT32 err; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&z_sem); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + err = ffsUmountVol(sb); + buf_shutdown(sb); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + fs_struct[p_fs->drv].mounted = FALSE; + fs_struct[p_fs->drv].sb = NULL; + + sm_V(&z_sem); + + return(err); +} /* end of FsUmountVol */ + +/* FsGetVolInfo : get the information of a file system volume */ +INT32 FsGetVolInfo(struct super_block *sb, VOL_INFO_T *info) +{ + INT32 err; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if (info == NULL) return(FFS_ERROR); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + err = ffsGetVolInfo(sb, info); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + return(err); +} /* end of FsGetVolInfo */ + +/* FsSyncVol : synchronize a file system volume */ +INT32 FsSyncVol(struct super_block *sb, INT32 do_sync) +{ + INT32 err; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + err = ffsSyncVol(sb, do_sync); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + return(err); +} /* end of FsSyncVol */ + + +/*----------------------------------------------------------------------*/ +/* File Operation Functions */ +/*----------------------------------------------------------------------*/ + +/* FsCreateFile : create a file */ +INT32 FsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid) +{ + INT32 err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if ((fid == NULL) || (path == NULL) || (*path == '\0')) + return(FFS_ERROR); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + err = ffsLookupFile(inode, path, fid); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + return(err); +} /* end of FsLookupFile */ + +/* FsCreateFile : create a file */ +INT32 FsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid) +{ + INT32 err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if ((fid == NULL) || (path == NULL) || (*path == '\0')) + return(FFS_ERROR); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + err = ffsCreateFile(inode, path, mode, fid); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + return(err); +} /* end of FsCreateFile */ + +INT32 FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount) +{ + INT32 err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of the given file id */ + if (fid == NULL) return(FFS_INVALIDFID); + + /* check the validity of pointer parameters */ + if (buffer == NULL) return(FFS_ERROR); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + err = ffsReadFile(inode, fid, buffer, count, rcount); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + return(err); +} /* end of FsReadFile */ + +INT32 FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount) +{ + INT32 err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of the given file id */ + if (fid == NULL) return(FFS_INVALIDFID); + + /* check the validity of pointer parameters */ + if (buffer == NULL) return(FFS_ERROR); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + err = ffsWriteFile(inode, fid, buffer, count, wcount); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + return(err); +} /* end of FsWriteFile */ + +/* FsTruncateFile : resize the file length */ +INT32 FsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size) +{ + INT32 err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + PRINTK("FsTruncateFile entered (inode %p size %llu)\n", inode, new_size); + + err = ffsTruncateFile(inode, old_size, new_size); + + PRINTK("FsTruncateFile exitted (%d)\n", err); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + return(err); +} /* end of FsTruncateFile */ + +/* FsMoveFile : move(rename) a old file into a new file */ +INT32 FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry) +{ + INT32 err; + struct super_block *sb = old_parent_inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of the given file id */ + if (fid == NULL) return(FFS_INVALIDFID); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + err = ffsMoveFile(old_parent_inode, fid, new_parent_inode, new_dentry); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + return(err); +} /* end of FsMoveFile */ + +/* FsRemoveFile : remove a file */ +INT32 FsRemoveFile(struct inode *inode, FILE_ID_T *fid) +{ + INT32 err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of the given file id */ + if (fid == NULL) return(FFS_INVALIDFID); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + err = ffsRemoveFile(inode, fid); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + return(err); +} /* end of FsRemoveFile */ + +/* FsSetAttr : set the attribute of a given file */ +INT32 FsSetAttr(struct inode *inode, UINT32 attr) +{ + INT32 err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + err = ffsSetAttr(inode, attr); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + return(err); +} /* end of FsSetAttr */ + +/* FsReadStat : get the information of a given file */ +INT32 FsReadStat(struct inode *inode, DIR_ENTRY_T *info) +{ + INT32 err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + err = ffsGetStat(inode, info); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + return(err); +} /* end of FsReadStat */ + +/* FsWriteStat : set the information of a given file */ +INT32 FsWriteStat(struct inode *inode, DIR_ENTRY_T *info) +{ + INT32 err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + PRINTK("FsWriteStat entered (inode %p info %p\n", inode, info); + + err = ffsSetStat(inode, info); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + PRINTK("FsWriteStat exited (%d)\n", err); + + return(err); +} /* end of FsWriteStat */ + +/* FsMapCluster : return the cluster number in the given cluster offset */ +INT32 FsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu) +{ + INT32 err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if (clu == NULL) return(FFS_ERROR); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + err = ffsMapCluster(inode, clu_offset, clu); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + return(err); +} /* end of FsMapCluster */ + +/*----------------------------------------------------------------------*/ +/* Directory Operation Functions */ +/*----------------------------------------------------------------------*/ + +/* FsCreateDir : create(make) a directory */ +INT32 FsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid) +{ + INT32 err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if ((fid == NULL) || (path == NULL) || (*path == '\0')) + return(FFS_ERROR); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + err = ffsCreateDir(inode, path, fid); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + return(err); +} /* end of FsCreateDir */ + +/* FsReadDir : read a directory entry from the opened directory */ +INT32 FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry) +{ + INT32 err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of pointer parameters */ + if (dir_entry == NULL) return(FFS_ERROR); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + err = ffsReadDir(inode, dir_entry); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + return(err); +} /* end of FsReadDir */ + +/* FsRemoveDir : remove a directory */ +INT32 FsRemoveDir(struct inode *inode, FILE_ID_T *fid) +{ + INT32 err; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of the given file id */ + if (fid == NULL) return(FFS_INVALIDFID); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + err = ffsRemoveDir(inode, fid); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + return(err); +} /* end of FsRemoveDir */ + +EXPORT_SYMBOL(FsMountVol); +EXPORT_SYMBOL(FsUmountVol); +EXPORT_SYMBOL(FsGetVolInfo); +EXPORT_SYMBOL(FsSyncVol); +EXPORT_SYMBOL(FsLookupFile); +EXPORT_SYMBOL(FsCreateFile); +EXPORT_SYMBOL(FsReadFile); +EXPORT_SYMBOL(FsWriteFile); +EXPORT_SYMBOL(FsTruncateFile); +EXPORT_SYMBOL(FsMoveFile); +EXPORT_SYMBOL(FsRemoveFile); +EXPORT_SYMBOL(FsSetAttr); +EXPORT_SYMBOL(FsReadStat); +EXPORT_SYMBOL(FsWriteStat); +EXPORT_SYMBOL(FsMapCluster); +EXPORT_SYMBOL(FsCreateDir); +EXPORT_SYMBOL(FsReadDir); +EXPORT_SYMBOL(FsRemoveDir); + +#if EXFAT_CONFIG_KERNEL_DEBUG +/* FsReleaseCache: Release FAT & buf cache */ +INT32 FsReleaseCache(struct super_block *sb) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* acquire the lock for file system critical section */ + sm_P(&(fs_struct[p_fs->drv].v_sem)); + + FAT_release_all(sb); + buf_release_all(sb); + + /* release the lock for file system critical section */ + sm_V(&(fs_struct[p_fs->drv].v_sem)); + + return 0; +} +/* FsReleaseCache */ + +EXPORT_SYMBOL(FsReleaseCache); +#endif /* EXFAT_CONFIG_KERNEL_DEBUG */ + +/*======================================================================*/ +/* Local Function Definitions */ +/*======================================================================*/ + +/* end of exfat_api.c */ diff --git a/fs/exfat/exfat_api.h b/fs/exfat/exfat_api.h new file mode 100644 index 0000000000000..d89b8fed596fb --- /dev/null +++ b/fs/exfat/exfat_api.h @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_api.h */ +/* PURPOSE : Header File for exFAT API Glue Layer */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_API_H +#define _EXFAT_API_H + +#include "exfat_config.h" +#include "exfat_global.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /*----------------------------------------------------------------------*/ + /* Constant & Macro Definitions */ + /*----------------------------------------------------------------------*/ + +#define EXFAT_SUPER_MAGIC (0x2011BAB0L) +#define EXFAT_ROOT_INO 1 + + /* FAT types */ +#define FAT12 0x01 // FAT12 +#define FAT16 0x0E // Win95 FAT16 (LBA) +#define FAT32 0x0C // Win95 FAT32 (LBA) +#define EXFAT 0x07 // exFAT + + /* file name lengths */ +#define MAX_CHARSET_SIZE 3 // max size of multi-byte character +#define MAX_PATH_DEPTH 15 // max depth of path name +#define MAX_NAME_LENGTH 256 // max len of file name including NULL +#define MAX_PATH_LENGTH 260 // max len of path name including NULL +#define DOS_NAME_LENGTH 11 // DOS file name length excluding NULL +#define DOS_PATH_LENGTH 80 // DOS path name length excluding NULL + + /* file attributes */ +#define ATTR_NORMAL 0x0000 +#define ATTR_READONLY 0x0001 +#define ATTR_HIDDEN 0x0002 +#define ATTR_SYSTEM 0x0004 +#define ATTR_VOLUME 0x0008 +#define ATTR_SUBDIR 0x0010 +#define ATTR_ARCHIVE 0x0020 +#define ATTR_SYMLINK 0x0040 +#define ATTR_EXTEND 0x000F +#define ATTR_RWMASK 0x007E + + /* file creation modes */ +#define FM_REGULAR 0x00 +#define FM_SYMLINK 0x40 + + /* return values */ +#define FFS_SUCCESS 0 +#define FFS_MEDIAERR 1 +#define FFS_FORMATERR 2 +#define FFS_MOUNTED 3 +#define FFS_NOTMOUNTED 4 +#define FFS_ALIGNMENTERR 5 +#define FFS_SEMAPHOREERR 6 +#define FFS_INVALIDPATH 7 +#define FFS_INVALIDFID 8 +#define FFS_NOTFOUND 9 +#define FFS_FILEEXIST 10 +#define FFS_PERMISSIONERR 11 +#define FFS_NOTOPENED 12 +#define FFS_MAXOPENED 13 +#define FFS_FULL 14 +#define FFS_EOF 15 +#define FFS_DIRBUSY 16 +#define FFS_MEMORYERR 17 +#define FFS_NAMETOOLONG 18 +#define FFS_ERROR 19 // generic error code + + /*----------------------------------------------------------------------*/ + /* Type Definitions */ + /*----------------------------------------------------------------------*/ + + typedef struct { + UINT16 Year; + UINT16 Month; + UINT16 Day; + UINT16 Hour; + UINT16 Minute; + UINT16 Second; + UINT16 MilliSecond; + } DATE_TIME_T; + + typedef struct { + UINT32 Offset; // start sector number of the partition + UINT32 Size; // in sectors + } PART_INFO_T; + + typedef struct { + UINT32 SecSize; // sector size in bytes + UINT32 DevSize; // block device size in sectors + } DEV_INFO_T; + + typedef struct { + UINT32 FatType; + UINT32 ClusterSize; + UINT32 NumClusters; + UINT32 FreeClusters; + UINT32 UsedClusters; + } VOL_INFO_T; + + /* directory structure */ + typedef struct { + UINT32 dir; + INT32 size; + UINT8 flags; + } CHAIN_T; + + /* file id structure */ + typedef struct { + CHAIN_T dir; + UINT8 flags; + INT32 entry; + UINT32 type; + UINT32 attr; + UINT32 start_clu; + INT32 hint_last_off; + UINT32 hint_last_clu; + INT64 rwoffset; + UINT64 size; + } FILE_ID_T; + + typedef struct { + INT8 Name[MAX_NAME_LENGTH *MAX_CHARSET_SIZE]; + INT8 ShortName[DOS_NAME_LENGTH + 2]; // used only for FAT12/16/32, not used for exFAT + UINT32 Attr; + UINT64 Size; + UINT32 NumSubdirs; + DATE_TIME_T CreateTimestamp; + DATE_TIME_T ModifyTimestamp; + DATE_TIME_T AccessTimestamp; + } DIR_ENTRY_T; + + /*======================================================================*/ + /* */ + /* API FUNCTION DECLARATIONS */ + /* (CHANGE THIS PART IF REQUIRED) */ + /* */ + /*======================================================================*/ + + /*----------------------------------------------------------------------*/ + /* External Function Declarations */ + /*----------------------------------------------------------------------*/ + + /* file system initialization & shutdown functions */ + INT32 FsInit(void); + INT32 FsShutdown(void); + + /* volume management functions */ + INT32 FsMountVol(struct super_block *sb); + INT32 FsUmountVol(struct super_block *sb); + INT32 FsGetVolInfo(struct super_block *sb, VOL_INFO_T *info); + INT32 FsSyncVol(struct super_block *sb, INT32 do_sync); + + /* file management functions */ + INT32 FsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid); + INT32 FsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid); + INT32 FsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount); + INT32 FsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount); + INT32 FsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size); + INT32 FsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry); + INT32 FsRemoveFile(struct inode *inode, FILE_ID_T *fid); + INT32 FsSetAttr(struct inode *inode, UINT32 attr); + INT32 FsReadStat(struct inode *inode, DIR_ENTRY_T *info); + INT32 FsWriteStat(struct inode *inode, DIR_ENTRY_T *info); + INT32 FsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu); + + /* directory management functions */ + INT32 FsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid); + INT32 FsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry); + INT32 FsRemoveDir(struct inode *inode, FILE_ID_T *fid); + + /* debug functions */ + INT32 FsReleaseCache(struct super_block *sb); + + /* partition management functions */ +//INT32 FsSetPartition(INT32 dev, INT32 num_vol, PART_INFO_T *vol_spec); +//INT32 FsGetPartition(INT32 dev, INT32 *num_vol, PART_INFO_T *vol_spec); +//INT32 FsGetDevInfo(INT32 dev, DEV_INFO_T *info); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _EXFAT_API_H */ + +/* end of exfat_api.h */ diff --git a/fs/exfat/exfat_blkdev.c b/fs/exfat/exfat_blkdev.c new file mode 100644 index 0000000000000..154b444b26f07 --- /dev/null +++ b/fs/exfat/exfat_blkdev.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_blkdev.c */ +/* PURPOSE : exFAT Block Device Driver Glue Layer */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include + +#include "exfat_config.h" +#include "exfat_global.h" +#include "exfat_blkdev.h" +#include "exfat_data.h" +#include "exfat_api.h" +#include "exfat_super.h" + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*======================================================================*/ +/* Function Definitions */ +/*======================================================================*/ + +INT32 bdev_init(void) +{ + return(FFS_SUCCESS); +} + +INT32 bdev_shutdown(void) +{ + return(FFS_SUCCESS); +} + +INT32 bdev_open(struct super_block *sb) +{ + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_bd->opened) return(FFS_SUCCESS); + + p_bd->sector_size = bdev_logical_block_size(sb->s_bdev); + p_bd->sector_size_bits = my_log2(p_bd->sector_size); + p_bd->sector_size_mask = p_bd->sector_size - 1; + p_bd->num_sectors = i_size_read(sb->s_bdev->bd_inode) >> p_bd->sector_size_bits; + + p_bd->opened = TRUE; + + return(FFS_SUCCESS); +} + +INT32 bdev_close(struct super_block *sb) +{ + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (!p_bd->opened) return(FFS_SUCCESS); + + p_bd->opened = FALSE; + return(FFS_SUCCESS); +} + +INT32 bdev_read(struct super_block *sb, UINT32 secno, struct buffer_head **bh, UINT32 num_secs, INT32 read) +{ + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); +#if EXFAT_CONFIG_KERNEL_DEBUG + struct exfat_sb_info *sbi = EXFAT_SB(sb); + long flags = sbi->debug_flags; + + if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) return (FFS_MEDIAERR); +#endif /* EXFAT_CONFIG_KERNEL_DEBUG */ + + if (!p_bd->opened) return(FFS_MEDIAERR); + + if (*bh) __brelse(*bh); + + if (read) + *bh = __bread(sb->s_bdev, secno, num_secs << p_bd->sector_size_bits); + else + *bh = __getblk(sb->s_bdev, secno, num_secs << p_bd->sector_size_bits); + + if (*bh) return(FFS_SUCCESS); + + WARN(!p_fs->dev_ejected, + "[EXFAT] No bh, device seems wrong or to be ejected.\n"); + + return(FFS_MEDIAERR); +} + +INT32 bdev_write(struct super_block *sb, UINT32 secno, struct buffer_head *bh, UINT32 num_secs, INT32 sync) +{ + INT32 count; + struct buffer_head *bh2; + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); +#if EXFAT_CONFIG_KERNEL_DEBUG + struct exfat_sb_info *sbi = EXFAT_SB(sb); + long flags = sbi->debug_flags; + + if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) return (FFS_MEDIAERR); +#endif /* EXFAT_CONFIG_KERNEL_DEBUG */ + + if (!p_bd->opened) return(FFS_MEDIAERR); + + if (secno == bh->b_blocknr) { + lock_buffer(bh); + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); + unlock_buffer(bh); + if (sync && (sync_dirty_buffer(bh) != 0)) + return (FFS_MEDIAERR); + } else { + count = num_secs << p_bd->sector_size_bits; + + bh2 = __getblk(sb->s_bdev, secno, count); + + if (bh2 == NULL) + goto no_bh; + + lock_buffer(bh2); + MEMCPY(bh2->b_data, bh->b_data, count); + set_buffer_uptodate(bh2); + mark_buffer_dirty(bh2); + unlock_buffer(bh2); + if (sync && (sync_dirty_buffer(bh2) != 0)) { + __brelse(bh2); + goto no_bh; + } + __brelse(bh2); + } + + return(FFS_SUCCESS); + +no_bh: + WARN(!p_fs->dev_ejected, + "[EXFAT] No bh, device seems wrong or to be ejected.\n"); + + return (FFS_MEDIAERR); +} + +INT32 bdev_sync(struct super_block *sb) +{ + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); +#if EXFAT_CONFIG_KERNEL_DEBUG + struct exfat_sb_info *sbi = EXFAT_SB(sb); + long flags = sbi->debug_flags; + + if (flags & EXFAT_DEBUGFLAGS_ERROR_RW) return (FFS_MEDIAERR); +#endif /* EXFAT_CONFIG_KERNEL_DEBUG */ + + if (!p_bd->opened) return(FFS_MEDIAERR); + + return sync_blockdev(sb->s_bdev); +} + +/* end of exfat_blkdev.c */ diff --git a/fs/exfat/exfat_blkdev.h b/fs/exfat/exfat_blkdev.h new file mode 100644 index 0000000000000..c1f0c28cacb29 --- /dev/null +++ b/fs/exfat/exfat_blkdev.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_blkdev.h */ +/* PURPOSE : Header File for exFAT Block Device Driver Glue Layer */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_BLKDEV_H +#define _EXFAT_BLKDEV_H + +#include "exfat_config.h" +#include "exfat_global.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /*----------------------------------------------------------------------*/ + /* Constant & Macro Definitions (Non-Configurable) */ + /*----------------------------------------------------------------------*/ + + /*----------------------------------------------------------------------*/ + /* Type Definitions */ + /*----------------------------------------------------------------------*/ + + typedef struct __BD_INFO_T { + INT32 sector_size; // in bytes + INT32 sector_size_bits; + INT32 sector_size_mask; + INT32 num_sectors; // total number of sectors in this block device + BOOL opened; // opened or not + } BD_INFO_T; + + /*----------------------------------------------------------------------*/ + /* External Variable Declarations */ + /*----------------------------------------------------------------------*/ + + /*----------------------------------------------------------------------*/ + /* External Function Declarations */ + /*----------------------------------------------------------------------*/ + + INT32 bdev_init(void); + INT32 bdev_shutdown(void); + INT32 bdev_open(struct super_block *sb); + INT32 bdev_close(struct super_block *sb); + INT32 bdev_read(struct super_block *sb, UINT32 secno, struct buffer_head **bh, UINT32 num_secs, INT32 read); + INT32 bdev_write(struct super_block *sb, UINT32 secno, struct buffer_head *bh, UINT32 num_secs, INT32 sync); + INT32 bdev_sync(struct super_block *sb); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _EXFAT_BLKDEV_H */ + +/* end of exfat_blkdev.h */ diff --git a/fs/exfat/exfat_cache.c b/fs/exfat/exfat_cache.c new file mode 100644 index 0000000000000..05c613653010c --- /dev/null +++ b/fs/exfat/exfat_cache.c @@ -0,0 +1,785 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_cache.c */ +/* PURPOSE : exFAT Cache Manager */ +/* (FAT Cache & Buffer Cache) */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Sung-Kwan Kim] : first writing */ +/* */ +/************************************************************************/ + +#include "exfat_config.h" +#include "exfat_global.h" +#include "exfat_data.h" + +#include "exfat_cache.h" +#include "exfat_super.h" +#include "exfat.h" + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +extern FS_STRUCT_T fs_struct[]; + +#define sm_P(s) +#define sm_V(s) + +static INT32 __FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content); +static INT32 __FAT_write(struct super_block *sb, UINT32 loc, UINT32 content); + +static BUF_CACHE_T *FAT_cache_find(struct super_block *sb, UINT32 sec); +static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, UINT32 sec); +static void FAT_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp); +static void FAT_cache_remove_hash(BUF_CACHE_T *bp); + +static UINT8 *__buf_getblk(struct super_block *sb, UINT32 sec); + +static BUF_CACHE_T *buf_cache_find(struct super_block *sb, UINT32 sec); +static BUF_CACHE_T *buf_cache_get(struct super_block *sb, UINT32 sec); +static void buf_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp); +static void buf_cache_remove_hash(BUF_CACHE_T *bp); + +static void push_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list); +static void push_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list); +static void move_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list); +static void move_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list); + +/*======================================================================*/ +/* Cache Initialization Functions */ +/*======================================================================*/ + +INT32 buf_init(struct super_block *sb) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + INT32 i; + + /* LRU list */ + p_fs->FAT_cache_lru_list.next = p_fs->FAT_cache_lru_list.prev = &p_fs->FAT_cache_lru_list; + + for (i = 0; i < FAT_CACHE_SIZE; i++) { + p_fs->FAT_cache_array[i].drv = -1; + p_fs->FAT_cache_array[i].sec = ~0; + p_fs->FAT_cache_array[i].flag = 0; + p_fs->FAT_cache_array[i].buf_bh = NULL; + p_fs->FAT_cache_array[i].prev = p_fs->FAT_cache_array[i].next = NULL; + push_to_mru(&(p_fs->FAT_cache_array[i]), &p_fs->FAT_cache_lru_list); + } + + p_fs->buf_cache_lru_list.next = p_fs->buf_cache_lru_list.prev = &p_fs->buf_cache_lru_list; + + for (i = 0; i < BUF_CACHE_SIZE; i++) { + p_fs->buf_cache_array[i].drv = -1; + p_fs->buf_cache_array[i].sec = ~0; + p_fs->buf_cache_array[i].flag = 0; + p_fs->buf_cache_array[i].buf_bh = NULL; + p_fs->buf_cache_array[i].prev = p_fs->buf_cache_array[i].next = NULL; + push_to_mru(&(p_fs->buf_cache_array[i]), &p_fs->buf_cache_lru_list); + } + + /* HASH list */ + for (i = 0; i < FAT_CACHE_HASH_SIZE; i++) { + p_fs->FAT_cache_hash_list[i].drv = -1; + p_fs->FAT_cache_hash_list[i].sec = ~0; + p_fs->FAT_cache_hash_list[i].hash_next = p_fs->FAT_cache_hash_list[i].hash_prev = &(p_fs->FAT_cache_hash_list[i]); + } + + for (i = 0; i < FAT_CACHE_SIZE; i++) { + FAT_cache_insert_hash(sb, &(p_fs->FAT_cache_array[i])); + } + + for (i = 0; i < BUF_CACHE_HASH_SIZE; i++) { + p_fs->buf_cache_hash_list[i].drv = -1; + p_fs->buf_cache_hash_list[i].sec = ~0; + p_fs->buf_cache_hash_list[i].hash_next = p_fs->buf_cache_hash_list[i].hash_prev = &(p_fs->buf_cache_hash_list[i]); + } + + for (i = 0; i < BUF_CACHE_SIZE; i++) { + buf_cache_insert_hash(sb, &(p_fs->buf_cache_array[i])); + } + + return(FFS_SUCCESS); +} /* end of buf_init */ + +INT32 buf_shutdown(struct super_block *sb) +{ + return(FFS_SUCCESS); +} /* end of buf_shutdown */ + +/*======================================================================*/ +/* FAT Read/Write Functions */ +/*======================================================================*/ + +/* in : sb, loc + * out: content + * returns 0 on success + * -1 on error + */ +INT32 FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content) +{ + INT32 ret; + + sm_P(&f_sem); + + ret = __FAT_read(sb, loc, content); + + sm_V(&f_sem); + + return(ret); +} /* end of FAT_read */ + +INT32 FAT_write(struct super_block *sb, UINT32 loc, UINT32 content) +{ + INT32 ret; + + sm_P(&f_sem); + + ret = __FAT_write(sb, loc, content); + + sm_V(&f_sem); + + return(ret); +} /* end of FAT_write */ + +static INT32 __FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content) +{ + INT32 off; + UINT32 sec, _content; + UINT8 *fat_sector, *fat_entry; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_fs->vol_type == FAT12) { + sec = p_fs->FAT1_start_sector + ((loc + (loc >> 1)) >> p_bd->sector_size_bits); + off = (loc + (loc >> 1)) & p_bd->sector_size_mask; + + if (off == (p_bd->sector_size-1)) { + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + _content = (UINT32) fat_sector[off]; + + fat_sector = FAT_getblk(sb, ++sec); + if (!fat_sector) + return -1; + + _content |= (UINT32) fat_sector[0] << 8; + } else { + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + _content = GET16(fat_entry); + } + + if (loc & 1) _content >>= 4; + + _content &= 0x00000FFF; + + if (_content >= CLUSTER_16(0x0FF8)) { + *content = CLUSTER_32(~0); + return 0; + } else { + *content = CLUSTER_32(_content); + return 0; + } + } else if (p_fs->vol_type == FAT16) { + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-1)); + off = (loc << 1) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + + _content = GET16_A(fat_entry); + + _content &= 0x0000FFFF; + + if (_content >= CLUSTER_16(0xFFF8)) { + *content = CLUSTER_32(~0); + return 0; + } else { + *content = CLUSTER_32(_content); + return 0; + } + } else if (p_fs->vol_type == FAT32) { + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); + off = (loc << 2) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + + _content = GET32_A(fat_entry); + + _content &= 0x0FFFFFFF; + + if (_content >= CLUSTER_32(0x0FFFFFF8)) { + *content = CLUSTER_32(~0); + return 0; + } else { + *content = CLUSTER_32(_content); + return 0; + } + } else { + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); + off = (loc << 2) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + _content = GET32_A(fat_entry); + + if (_content >= CLUSTER_32(0xFFFFFFF8)) { + *content = CLUSTER_32(~0); + return 0; + } else { + *content = CLUSTER_32(_content); + return 0; + } + } + + *content = CLUSTER_32(~0); + return 0; +} /* end of __FAT_read */ + +static INT32 __FAT_write(struct super_block *sb, UINT32 loc, UINT32 content) +{ + INT32 off; + UINT32 sec; + UINT8 *fat_sector, *fat_entry; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_fs->vol_type == FAT12) { + + content &= 0x00000FFF; + + sec = p_fs->FAT1_start_sector + ((loc + (loc >> 1)) >> p_bd->sector_size_bits); + off = (loc + (loc >> 1)) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + if (loc & 1) { /* odd */ + + content <<= 4; + + if (off == (p_bd->sector_size-1)) { + fat_sector[off] = (UINT8)(content | (fat_sector[off] & 0x0F)); + FAT_modify(sb, sec); + + fat_sector = FAT_getblk(sb, ++sec); + if (!fat_sector) + return -1; + + fat_sector[0] = (UINT8)(content >> 8); + } else { + fat_entry = &(fat_sector[off]); + content |= GET16(fat_entry) & 0x000F; + + SET16(fat_entry, content); + } + } else { /* even */ + fat_sector[off] = (UINT8)(content); + + if (off == (p_bd->sector_size-1)) { + fat_sector[off] = (UINT8)(content); + FAT_modify(sb, sec); + + fat_sector = FAT_getblk(sb, ++sec); + fat_sector[0] = (UINT8)((fat_sector[0] & 0xF0) | (content >> 8)); + } else { + fat_entry = &(fat_sector[off]); + content |= GET16(fat_entry) & 0xF000; + + SET16(fat_entry, content); + } + } + } + + else if (p_fs->vol_type == FAT16) { + + content &= 0x0000FFFF; + + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-1)); + off = (loc << 1) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + + SET16_A(fat_entry, content); + } + + else if (p_fs->vol_type == FAT32) { + + content &= 0x0FFFFFFF; + + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); + off = (loc << 2) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + + content |= GET32_A(fat_entry) & 0xF0000000; + + SET32_A(fat_entry, content); + } + + else { /* p_fs->vol_type == EXFAT */ + + sec = p_fs->FAT1_start_sector + (loc >> (p_bd->sector_size_bits-2)); + off = (loc << 2) & p_bd->sector_size_mask; + + fat_sector = FAT_getblk(sb, sec); + if (!fat_sector) + return -1; + + fat_entry = &(fat_sector[off]); + + SET32_A(fat_entry, content); + } + + FAT_modify(sb, sec); + return 0; +} /* end of __FAT_write */ + +UINT8 *FAT_getblk(struct super_block *sb, UINT32 sec) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + bp = FAT_cache_find(sb, sec); + if (bp != NULL) { + move_to_mru(bp, &p_fs->FAT_cache_lru_list); + return(bp->buf_bh->b_data); + } + + bp = FAT_cache_get(sb, sec); + + FAT_cache_remove_hash(bp); + + bp->drv = p_fs->drv; + bp->sec = sec; + bp->flag = 0; + + FAT_cache_insert_hash(sb, bp); + + if (sector_read(sb, sec, &(bp->buf_bh), 1) != FFS_SUCCESS) { + FAT_cache_remove_hash(bp); + bp->drv = -1; + bp->sec = ~0; + bp->flag = 0; + bp->buf_bh = NULL; + + move_to_lru(bp, &p_fs->FAT_cache_lru_list); + return NULL; + } + + return(bp->buf_bh->b_data); +} /* end of FAT_getblk */ + +void FAT_modify(struct super_block *sb, UINT32 sec) +{ + BUF_CACHE_T *bp; + + bp = FAT_cache_find(sb, sec); + if (bp != NULL) { + sector_write(sb, sec, bp->buf_bh, 0); + } +} /* end of FAT_modify */ + +void FAT_release_all(struct super_block *sb) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&f_sem); + + bp = p_fs->FAT_cache_lru_list.next; + while (bp != &p_fs->FAT_cache_lru_list) { + if (bp->drv == p_fs->drv) { + bp->drv = -1; + bp->sec = ~0; + bp->flag = 0; + + if(bp->buf_bh) { + __brelse(bp->buf_bh); + bp->buf_bh = NULL; + } + } + bp = bp->next; + } + + sm_V(&f_sem); +} /* end of FAT_release_all */ + +void FAT_sync(struct super_block *sb) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&f_sem); + + bp = p_fs->FAT_cache_lru_list.next; + while (bp != &p_fs->FAT_cache_lru_list) { + if ((bp->drv == p_fs->drv) && (bp->flag & DIRTYBIT)) { + sync_dirty_buffer(bp->buf_bh); + bp->flag &= ~(DIRTYBIT); + } + bp = bp->next; + } + + sm_V(&f_sem); +} /* end of FAT_sync */ + +static BUF_CACHE_T *FAT_cache_find(struct super_block *sb, UINT32 sec) +{ + INT32 off; + BUF_CACHE_T *bp, *hp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + off = (sec + (sec >> p_fs->sectors_per_clu_bits)) & (FAT_CACHE_HASH_SIZE - 1); + + hp = &(p_fs->FAT_cache_hash_list[off]); + for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) { + if ((bp->drv == p_fs->drv) && (bp->sec == sec)) { + + WARN(!bp->buf_bh, "[EXFAT] FAT_cache has no bh. " + "It will make system panic.\n"); + + touch_buffer(bp->buf_bh); + return(bp); + } + } + return(NULL); +} /* end of FAT_cache_find */ + +static BUF_CACHE_T *FAT_cache_get(struct super_block *sb, UINT32 sec) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + bp = p_fs->FAT_cache_lru_list.prev; + + + move_to_mru(bp, &p_fs->FAT_cache_lru_list); + return(bp); +} /* end of FAT_cache_get */ + +static void FAT_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp) +{ + INT32 off; + BUF_CACHE_T *hp; + FS_INFO_T *p_fs; + + p_fs = &(EXFAT_SB(sb)->fs_info); + off = (bp->sec + (bp->sec >> p_fs->sectors_per_clu_bits)) & (FAT_CACHE_HASH_SIZE-1); + + hp = &(p_fs->FAT_cache_hash_list[off]); + bp->hash_next = hp->hash_next; + bp->hash_prev = hp; + hp->hash_next->hash_prev = bp; + hp->hash_next = bp; +} /* end of FAT_cache_insert_hash */ + +static void FAT_cache_remove_hash(BUF_CACHE_T *bp) +{ + (bp->hash_prev)->hash_next = bp->hash_next; + (bp->hash_next)->hash_prev = bp->hash_prev; +} /* end of FAT_cache_remove_hash */ + +/*======================================================================*/ +/* Buffer Read/Write Functions */ +/*======================================================================*/ + +UINT8 *buf_getblk(struct super_block *sb, UINT32 sec) +{ + UINT8 *buf; + + sm_P(&b_sem); + + buf = __buf_getblk(sb, sec); + + sm_V(&b_sem); + + return(buf); +} /* end of buf_getblk */ + +static UINT8 *__buf_getblk(struct super_block *sb, UINT32 sec) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + bp = buf_cache_find(sb, sec); + if (bp != NULL) { + move_to_mru(bp, &p_fs->buf_cache_lru_list); + return(bp->buf_bh->b_data); + } + + bp = buf_cache_get(sb, sec); + + buf_cache_remove_hash(bp); + + bp->drv = p_fs->drv; + bp->sec = sec; + bp->flag = 0; + + buf_cache_insert_hash(sb, bp); + + if (sector_read(sb, sec, &(bp->buf_bh), 1) != FFS_SUCCESS) { + buf_cache_remove_hash(bp); + bp->drv = -1; + bp->sec = ~0; + bp->flag = 0; + bp->buf_bh = NULL; + + move_to_lru(bp, &p_fs->buf_cache_lru_list); + return NULL; + } + + return(bp->buf_bh->b_data); + +} /* end of __buf_getblk */ + +void buf_modify(struct super_block *sb, UINT32 sec) +{ + BUF_CACHE_T *bp; + + sm_P(&b_sem); + + bp = buf_cache_find(sb, sec); + if (likely(bp != NULL)) { + sector_write(sb, sec, bp->buf_bh, 0); + } + + WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); + + sm_V(&b_sem); +} /* end of buf_modify */ + +void buf_lock(struct super_block *sb, UINT32 sec) +{ + BUF_CACHE_T *bp; + + sm_P(&b_sem); + + bp = buf_cache_find(sb, sec); + if (likely(bp != NULL)) bp->flag |= LOCKBIT; + + WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); + + sm_V(&b_sem); +} /* end of buf_lock */ + +void buf_unlock(struct super_block *sb, UINT32 sec) +{ + BUF_CACHE_T *bp; + + sm_P(&b_sem); + + bp = buf_cache_find(sb, sec); + if (likely(bp != NULL)) bp->flag &= ~(LOCKBIT); + + WARN(!bp, "[EXFAT] failed to find buffer_cache(sector:%u).\n", sec); + + sm_V(&b_sem); +} /* end of buf_unlock */ + +void buf_release(struct super_block *sb, UINT32 sec) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&b_sem); + + bp = buf_cache_find(sb, sec); + if (likely(bp != NULL)) { + bp->drv = -1; + bp->sec = ~0; + bp->flag = 0; + + if(bp->buf_bh) { + __brelse(bp->buf_bh); + bp->buf_bh = NULL; + } + + move_to_lru(bp, &p_fs->buf_cache_lru_list); + } + + sm_V(&b_sem); +} /* end of buf_release */ + +void buf_release_all(struct super_block *sb) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&b_sem); + + bp = p_fs->buf_cache_lru_list.next; + while (bp != &p_fs->buf_cache_lru_list) { + if (bp->drv == p_fs->drv) { + bp->drv = -1; + bp->sec = ~0; + bp->flag = 0; + + if(bp->buf_bh) { + __brelse(bp->buf_bh); + bp->buf_bh = NULL; + } + } + bp = bp->next; + } + + sm_V(&b_sem); +} /* end of buf_release_all */ + +void buf_sync(struct super_block *sb) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + sm_P(&b_sem); + + bp = p_fs->buf_cache_lru_list.next; + while (bp != &p_fs->buf_cache_lru_list) { + if ((bp->drv == p_fs->drv) && (bp->flag & DIRTYBIT)) { + sync_dirty_buffer(bp->buf_bh); + bp->flag &= ~(DIRTYBIT); + } + bp = bp->next; + } + + sm_V(&b_sem); +} /* end of buf_sync */ + +static BUF_CACHE_T *buf_cache_find(struct super_block *sb, UINT32 sec) +{ + INT32 off; + BUF_CACHE_T *bp, *hp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + off = (sec + (sec >> p_fs->sectors_per_clu_bits)) & (BUF_CACHE_HASH_SIZE - 1); + + hp = &(p_fs->buf_cache_hash_list[off]); + for (bp = hp->hash_next; bp != hp; bp = bp->hash_next) { + if ((bp->drv == p_fs->drv) && (bp->sec == sec)) { + touch_buffer(bp->buf_bh); + return(bp); + } + } + return(NULL); +} /* end of buf_cache_find */ + +static BUF_CACHE_T *buf_cache_get(struct super_block *sb, UINT32 sec) +{ + BUF_CACHE_T *bp; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + bp = p_fs->buf_cache_lru_list.prev; + while (bp->flag & LOCKBIT) bp = bp->prev; + + + move_to_mru(bp, &p_fs->buf_cache_lru_list); + return(bp); +} /* end of buf_cache_get */ + +static void buf_cache_insert_hash(struct super_block *sb, BUF_CACHE_T *bp) +{ + INT32 off; + BUF_CACHE_T *hp; + FS_INFO_T *p_fs; + + p_fs = &(EXFAT_SB(sb)->fs_info); + off = (bp->sec + (bp->sec >> p_fs->sectors_per_clu_bits)) & (BUF_CACHE_HASH_SIZE-1); + + hp = &(p_fs->buf_cache_hash_list[off]); + bp->hash_next = hp->hash_next; + bp->hash_prev = hp; + hp->hash_next->hash_prev = bp; + hp->hash_next = bp; +} /* end of buf_cache_insert_hash */ + +static void buf_cache_remove_hash(BUF_CACHE_T *bp) +{ + (bp->hash_prev)->hash_next = bp->hash_next; + (bp->hash_next)->hash_prev = bp->hash_prev; +} /* end of buf_cache_remove_hash */ + +/*======================================================================*/ +/* Local Function Definitions */ +/*======================================================================*/ + +static void push_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list) +{ + bp->next = list->next; + bp->prev = list; + list->next->prev = bp; + list->next = bp; +} /* end of buf_cache_push_to_mru */ + +static void push_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list) +{ + bp->prev = list->prev; + bp->next = list; + list->prev->next = bp; + list->prev = bp; +} /* end of buf_cache_push_to_lru */ + +static void move_to_mru(BUF_CACHE_T *bp, BUF_CACHE_T *list) +{ + bp->prev->next = bp->next; + bp->next->prev = bp->prev; + push_to_mru(bp, list); +} /* end of buf_cache_move_to_mru */ + +static void move_to_lru(BUF_CACHE_T *bp, BUF_CACHE_T *list) +{ + bp->prev->next = bp->next; + bp->next->prev = bp->prev; + push_to_lru(bp, list); +} /* end of buf_cache_move_to_lru */ + +/* end of exfat_cache.c */ diff --git a/fs/exfat/exfat_cache.h b/fs/exfat/exfat_cache.h new file mode 100644 index 0000000000000..056636b4e4837 --- /dev/null +++ b/fs/exfat/exfat_cache.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_cache.h */ +/* PURPOSE : Header File for exFAT Cache Manager */ +/* (FAT Cache & Buffer Cache) */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Sung-Kwan Kim] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_CACHE_H +#define _EXFAT_CACHE_H + +#include "exfat_config.h" +#include "exfat_global.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /*----------------------------------------------------------------------*/ + /* Constant & Macro Definitions */ + /*----------------------------------------------------------------------*/ + +#define LOCKBIT 0x01 +#define DIRTYBIT 0x02 + + /*----------------------------------------------------------------------*/ + /* Type Definitions */ + /*----------------------------------------------------------------------*/ + + typedef struct __BUF_CACHE_T { + struct __BUF_CACHE_T *next; + struct __BUF_CACHE_T *prev; + struct __BUF_CACHE_T *hash_next; + struct __BUF_CACHE_T *hash_prev; + INT32 drv; + UINT32 sec; + UINT32 flag; + struct buffer_head *buf_bh; + } BUF_CACHE_T; + + /*----------------------------------------------------------------------*/ + /* External Function Declarations */ + /*----------------------------------------------------------------------*/ + + INT32 buf_init(struct super_block *sb); + INT32 buf_shutdown(struct super_block *sb); + INT32 FAT_read(struct super_block *sb, UINT32 loc, UINT32 *content); + INT32 FAT_write(struct super_block *sb, UINT32 loc, UINT32 content); + UINT8 *FAT_getblk(struct super_block *sb, UINT32 sec); + void FAT_modify(struct super_block *sb, UINT32 sec); + void FAT_release_all(struct super_block *sb); + void FAT_sync(struct super_block *sb); + UINT8 *buf_getblk(struct super_block *sb, UINT32 sec); + void buf_modify(struct super_block *sb, UINT32 sec); + void buf_lock(struct super_block *sb, UINT32 sec); + void buf_unlock(struct super_block *sb, UINT32 sec); + void buf_release(struct super_block *sb, UINT32 sec); + void buf_release_all(struct super_block *sb); + void buf_sync(struct super_block *sb); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _EXFAT_CACHE_H */ + +/* end of exfat_cache.h */ diff --git a/fs/exfat/exfat_config.h b/fs/exfat/exfat_config.h new file mode 100644 index 0000000000000..d16c882f88ca6 --- /dev/null +++ b/fs/exfat/exfat_config.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_config.h */ +/* PURPOSE : Header File for exFAT Configuable Policies */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_CONFIG_H +#define _EXFAT_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*======================================================================*/ +/* */ +/* FFS CONFIGURATIONS */ +/* (CHANGE THIS PART IF REQUIRED) */ +/* */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* Target OS Platform */ +/*----------------------------------------------------------------------*/ + +#define OS_NONOS 1 +#define OS_LINUX 2 + +#define FFS_CONFIG_OS OS_LINUX + +/*----------------------------------------------------------------------*/ +/* Set this definition to 1 to support APIs with pointer parameters */ +/* to 32-bit variables (e.g. read, write, seek, get_filesize) */ +/*----------------------------------------------------------------------*/ +#define FFS_CONFIG_LEGACY_32BIT_API 0 + +/*----------------------------------------------------------------------*/ +/* Set this definition to 1 to support APIs with pointer parameters */ +/* to 32-bit variables (e.g. read, write, seek, get_filesize) */ +/*----------------------------------------------------------------------*/ +#define FFS_CONFIG_LEGACY_32BIT_API 0 + +/*----------------------------------------------------------------------*/ +/* Set appropriate definitions to 1's to support the languages */ +/*----------------------------------------------------------------------*/ +#define FFS_CONFIG_SUPPORT_CP1250 1 // Central Europe +#define FFS_CONFIG_SUPPORT_CP1251 1 // Cyrillic +#define FFS_CONFIG_SUPPORT_CP1252 1 // Latin I +#define FFS_CONFIG_SUPPORT_CP1253 1 // Greek +#define FFS_CONFIG_SUPPORT_CP1254 1 // Turkish +#define FFS_CONFIG_SUPPORT_CP1255 1 // Hebrew +#define FFS_CONFIG_SUPPORT_CP1256 1 // Arabic +#define FFS_CONFIG_SUPPORT_CP1257 1 // Baltic +#define FFS_CONFIG_SUPPORT_CP1258 1 // Vietnamese +#define FFS_CONFIG_SUPPORT_CP874 1 // Thai +#define FFS_CONFIG_SUPPORT_CP932 1 // Japanese +#define FFS_CONFIG_SUPPORT_CP936 1 // Simplified Chinese +#define FFS_CONFIG_SUPPORT_CP949 1 // Korean +#define FFS_CONFIG_SUPPORT_CP950 1 // Traditional Chinese +#define FFS_CONFIG_SUPPORT_UTF8 1 // UTF8 encoding + +/*----------------------------------------------------------------------*/ +/* Feature Config */ +/*----------------------------------------------------------------------*/ +#define EXFAT_CONFIG_DISCARD 1 // mount option -o discard support +#define EXFAT_CONFIG_KERNEL_DEBUG 1 // kernel debug features via ioctl +#define EXFAT_CONFIG_DEBUG_MSG 0 // debugging message on/off + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _EXFAT_CONFIG_H */ + +/* end of exfat_config.h */ diff --git a/fs/exfat/exfat_core.c b/fs/exfat/exfat_core.c new file mode 100644 index 0000000000000..2ab28dc32da21 --- /dev/null +++ b/fs/exfat/exfat_core.c @@ -0,0 +1,5187 @@ +/* Some of the source code in this file came from "linux/fs/fat/misc.c". */ +/* + * linux/fs/fat/misc.c + * + * Written 1992,1993 by Werner Almesberger + * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 + * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) + */ + +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat.c */ +/* PURPOSE : exFAT File Manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include + +#include "exfat_config.h" +#include "exfat_global.h" +#include "exfat_data.h" +#include "exfat_oal.h" + +#include "exfat_blkdev.h" +#include "exfat_cache.h" +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat_super.h" +#include "exfat.h" + +#include + +/*----------------------------------------------------------------------*/ +/* Constant & Macro Definitions */ +/*----------------------------------------------------------------------*/ + +#define THERE_IS_MBR 0 /* if there is no MBR (e.g. memory card), +set this macro to 0 */ + +#if (THERE_IS_MBR == 1) +#include "exfat_part.h" +#endif + +#define DELAYED_SYNC 0 + +#define ELAPSED_TIME 0 + +#if (ELAPSED_TIME == 1) +#include + +static UINT32 __t1, __t2; +static UINT32 get_current_msec(void) +{ + struct timeval tm; + do_gettimeofday(&tm); + return (UINT32)(tm.tv_sec*1000000 + tm.tv_usec); +} +#define TIME_START() do {__t1 = get_current_msec(); } while (0) +#define TIME_END() do {__t2 = get_current_msec(); } while (0) +#define PRINT_TIME(n) do {printk("[EXFAT] Elapsed time %d = %d (usec)\n", n, (__t2 - __t1)); } while (0) +#else +#define TIME_START() +#define TIME_END() +#define PRINT_TIME(n) +#endif + +static void __set_sb_dirty(struct super_block *sb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + sb->s_dirt = 1; +#else + struct exfat_sb_info *sbi = EXFAT_SB(sb); + sbi->s_dirt = 1; +#endif +} + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +extern UINT8 uni_upcase[]; + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ + +static UINT8 name_buf[MAX_PATH_LENGTH *MAX_CHARSET_SIZE]; + +static INT8 *reserved_names[] = { + "AUX ", "CON ", "NUL ", "PRN ", + "COM1 ", "COM2 ", "COM3 ", "COM4 ", + "COM5 ", "COM6 ", "COM7 ", "COM8 ", "COM9 ", + "LPT1 ", "LPT2 ", "LPT3 ", "LPT4 ", + "LPT5 ", "LPT6 ", "LPT7 ", "LPT8 ", "LPT9 ", + NULL +}; + +static UINT8 free_bit[] = { + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, /* 0 ~ 19 */ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, /* 20 ~ 39 */ + 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, /* 40 ~ 59 */ + 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, /* 60 ~ 79 */ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, /* 80 ~ 99 */ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, /* 100 ~ 119 */ + 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, /* 120 ~ 139 */ + 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, /* 140 ~ 159 */ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, /* 160 ~ 179 */ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, /* 180 ~ 199 */ + 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, /* 200 ~ 219 */ + 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, /* 220 ~ 239 */ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 240 ~ 254 */ +}; + +static UINT8 used_bit[] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, /* 0 ~ 19 */ + 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, /* 20 ~ 39 */ + 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, /* 40 ~ 59 */ + 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 60 ~ 79 */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, /* 80 ~ 99 */ + 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, /* 100 ~ 119 */ + 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, /* 120 ~ 139 */ + 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 140 ~ 159 */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, /* 160 ~ 179 */ + 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, /* 180 ~ 199 */ + 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, /* 200 ~ 219 */ + 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 220 ~ 239 */ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 /* 240 ~ 255 */ +}; + +/*======================================================================*/ +/* Global Function Definitions */ +/*======================================================================*/ + +/* ffsInit : roll back to the initial state of the file system */ +INT32 ffsInit(void) +{ + INT32 ret; + + ret = bdev_init(); + if (ret) + return ret; + + ret = fs_init(); + if (ret) + return ret; + + return FFS_SUCCESS; +} /* end of ffsInit */ + +/* ffsShutdown : make free all memory-alloced global buffers */ +INT32 ffsShutdown(void) +{ + INT32 ret; + ret = fs_shutdown(); + if (ret) + return ret; + + ret = bdev_shutdown(); + if (ret) + return ret; + + return FFS_SUCCESS; +} /* end of ffsShutdown */ + +/* ffsMountVol : mount the file system volume */ +INT32 ffsMountVol(struct super_block *sb, INT32 drv) +{ + INT32 i, ret; +#if (THERE_IS_MBR == 1) + MBR_SECTOR_T *p_mbr; + PART_ENTRY_T *p_pte; +#endif + PBR_SECTOR_T *p_pbr; + struct buffer_head *tmp_bh = NULL; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + PRINTK("[EXFAT] trying to mount...\n"); + + p_fs->drv = drv; + p_fs->dev_ejected = FALSE; + + /* open the block device */ + if (bdev_open(sb)) + return FFS_MEDIAERR; + + if (p_bd->sector_size < sb->s_blocksize) + return FFS_MEDIAERR; + if (p_bd->sector_size > sb->s_blocksize) + sb_set_blocksize(sb, p_bd->sector_size); + + /* read Sector 0 */ + if (sector_read(sb, 0, &tmp_bh, 1) != FFS_SUCCESS) + return FFS_MEDIAERR; + +#if (THERE_IS_MBR == 1) + if (buf[0] != 0xEB) { + /* MBR is read */ + p_mbr = (MBR_SECTOR_T *) tmp_bh->b_data; + + /* check the validity of MBR */ + if (GET16_A(p_mbr->signature) != MBR_SIGNATURE) { + brelse(tmp_bh); + bdev_close(sb); + return FFS_FORMATERR; + } + + p_pte = (PART_ENTRY_T *) p_mbr->partition + 0; + p_fs->PBR_sector = GET32(p_pte->start_sector); + p_fs->num_sectors = GET32(p_pte->num_sectors); + + if (p_fs->num_sectors == 0) { + brelse(tmp_bh); + bdev_close(sb); + return FFS_ERROR; + } + + /* read PBR */ + if (sector_read(sb, p_fs->PBR_sector, &tmp_bh, 1) != FFS_SUCCESS) { + bdev_close(sb); + return FFS_MEDIAERR; + } + } else { +#endif + /* PRB is read */ + p_fs->PBR_sector = 0; +#if (THERE_IS_MBR == 1) + } +#endif + + p_pbr = (PBR_SECTOR_T *) tmp_bh->b_data; + + /* check the validity of PBR */ + if (GET16_A(p_pbr->signature) != PBR_SIGNATURE) { + brelse(tmp_bh); + bdev_close(sb); + return FFS_FORMATERR; + } + + /* fill fs_stuct */ + for (i = 0; i < 53; i++) + if (p_pbr->bpb[i]) + break; + + if (i < 53) { + if (GET16(p_pbr->bpb+11)) /* num_fat_sectors */ + ret = fat16_mount(sb, p_pbr); + else + ret = fat32_mount(sb, p_pbr); + } else { + ret = exfat_mount(sb, p_pbr); + } + + brelse(tmp_bh); + + if (ret) { + bdev_close(sb); + return ret; + } + + if (p_fs->vol_type == EXFAT) { + ret = load_alloc_bitmap(sb); + if (ret) { + bdev_close(sb); + return ret; + } + ret = load_upcase_table(sb); + if (ret) { + free_alloc_bitmap(sb); + bdev_close(sb); + return ret; + } + } + + if (p_fs->dev_ejected) { + if (p_fs->vol_type == EXFAT) { + free_upcase_table(sb); + free_alloc_bitmap(sb); + } + bdev_close(sb); + return FFS_MEDIAERR; + } + + PRINTK("[EXFAT] mounted successfully\n"); + return FFS_SUCCESS; +} /* end of ffsMountVol */ + +/* ffsUmountVol : umount the file system volume */ +INT32 ffsUmountVol(struct super_block *sb) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + PRINTK("[EXFAT] trying to unmount...\n"); + + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); + + if (p_fs->vol_type == EXFAT) { + free_upcase_table(sb); + free_alloc_bitmap(sb); + } + + FAT_release_all(sb); + buf_release_all(sb); + + /* close the block device */ + bdev_close(sb); + + if (p_fs->dev_ejected) { + PRINTK( "[EXFAT] unmounted with media errors. " + "device's already ejected.\n"); + return FFS_MEDIAERR; + } + + PRINTK("[EXFAT] unmounted successfully\n"); + return FFS_SUCCESS; +} /* end of ffsUmountVol */ + +/* ffsGetVolInfo : get the information of a file system volume */ +INT32 ffsGetVolInfo(struct super_block *sb, VOL_INFO_T *info) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_fs->used_clusters == (UINT32) ~0) + p_fs->used_clusters = p_fs->fs_func->count_used_clusters(sb); + + info->FatType = p_fs->vol_type; + info->ClusterSize = p_fs->cluster_size; + info->NumClusters = p_fs->num_clusters - 2; /* clu 0 & 1 */ + info->UsedClusters = p_fs->used_clusters; + info->FreeClusters = info->NumClusters - info->UsedClusters; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsGetVolInfo */ + +/* ffsSyncVol : synchronize all file system volumes */ +INT32 ffsSyncVol(struct super_block *sb, INT32 do_sync) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* synchronize the file system */ + fs_sync(sb, do_sync); + fs_set_vol_flags(sb, VOL_CLEAN); + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsSyncVol */ + +/*----------------------------------------------------------------------*/ +/* File Operation Functions */ +/*----------------------------------------------------------------------*/ + +/* ffsLookupFile : lookup a file */ +INT32 ffsLookupFile(struct inode *inode, UINT8 *path, FILE_ID_T *fid) +{ + INT32 ret, dentry, num_entries; + CHAIN_T dir; + UNI_NAME_T uni_name; + DOS_NAME_T dos_name; + DENTRY_T *ep, *ep2; + ENTRY_SET_CACHE_T *es=NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + PRINTK("ffsLookupFile entered\n"); + + /* check the validity of directory name in the given pathname */ + ret = resolve_path(inode, path, &dir, &uni_name); + if (ret) + return ret; + + ret = get_num_entries_and_dos_name(sb, &dir, &uni_name, &num_entries, &dos_name); + if (ret) + return ret; + + /* search the file name for directories */ + dentry = p_fs->fs_func->find_dir_entry(sb, &dir, &uni_name, num_entries, &dos_name, TYPE_ALL); + if (dentry < -1) + return FFS_NOTFOUND; + + fid->dir.dir = dir.dir; + fid->dir.size = dir.size; + fid->dir.flags = dir.flags; + fid->entry = dentry; + + if (dentry == -1) { + fid->type = TYPE_DIR; + fid->rwoffset = 0; + fid->hint_last_off = -1; + + fid->attr = ATTR_SUBDIR; + fid->flags = 0x01; + fid->size = 0; + fid->start_clu = p_fs->root_dir; + } else { + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &dir, dentry, ES_2_ENTRIES, &ep); + if (!es) + return FFS_MEDIAERR; + ep2 = ep+1; + } else { + ep = get_entry_in_dir(sb, &dir, dentry, NULL); + if (!ep) + return FFS_MEDIAERR; + ep2 = ep; + } + + fid->type = p_fs->fs_func->get_entry_type(ep); + fid->rwoffset = 0; + fid->hint_last_off = -1; + fid->attr = p_fs->fs_func->get_entry_attr(ep); + + fid->size = p_fs->fs_func->get_entry_size(ep2); + if ((fid->type == TYPE_FILE) && (fid->size == 0)) { + fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->start_clu = CLUSTER_32(~0); + } else { + fid->flags = p_fs->fs_func->get_entry_flag(ep2); + fid->start_clu = p_fs->fs_func->get_entry_clu0(ep2); + } + + if (p_fs->vol_type == EXFAT) + release_entry_set(es); + } + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + PRINTK("ffsLookupFile exited successfully\n"); + + return FFS_SUCCESS; +} /* end of ffsLookupFile */ + +/* ffsCreateFile : create a file */ +INT32 ffsCreateFile(struct inode *inode, UINT8 *path, UINT8 mode, FILE_ID_T *fid) +{ + INT32 ret/*, dentry*/; + CHAIN_T dir; + UNI_NAME_T uni_name; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + /* check the validity of directory name in the given pathname */ + ret = resolve_path(inode, path, &dir, &uni_name); + if (ret) + return ret; + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* create a new file */ + ret = create_file(inode, &dir, &uni_name, mode, fid); + +#if (DELAYED_SYNC == 0) + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return ret; +} /* end of ffsCreateFile */ + +/* ffsReadFile : read data from a opened file */ +INT32 ffsReadFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *rcount) +{ + INT32 offset, sec_offset, clu_offset; + UINT32 clu, LogSector; + UINT64 oneblkread, read_bytes; + struct buffer_head *tmp_bh = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + /* check if the given file ID is opened */ + if (fid->type != TYPE_FILE) + return FFS_PERMISSIONERR; + + if (fid->rwoffset > fid->size) + fid->rwoffset = fid->size; + + if (count > (fid->size - fid->rwoffset)) + count = fid->size - fid->rwoffset; + + if (count == 0) { + if (rcount != NULL) + *rcount = 0; + return FFS_EOF; + } + + read_bytes = 0; + + while (count > 0) { + clu_offset = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); + clu = fid->start_clu; + + if (fid->flags == 0x03) { + clu += clu_offset; + } else { + /* hint information */ + if ((clu_offset > 0) && (fid->hint_last_off > 0) && + (clu_offset >= fid->hint_last_off)) { + clu_offset -= fid->hint_last_off; + clu = fid->hint_last_clu; + } + + while (clu_offset > 0) { + /* clu = FAT_read(sb, clu); */ + if (FAT_read(sb, clu, &clu) == -1) + return FFS_MEDIAERR; + + clu_offset--; + } + } + + /* hint information */ + fid->hint_last_off = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); + fid->hint_last_clu = clu; + + offset = (INT32)(fid->rwoffset & (p_fs->cluster_size-1)); /* byte offset in cluster */ + sec_offset = offset >> p_bd->sector_size_bits; /* sector offset in cluster */ + offset &= p_bd->sector_size_mask; /* byte offset in sector */ + + LogSector = START_SECTOR(clu) + sec_offset; + + oneblkread = (UINT64)(p_bd->sector_size - offset); + if (oneblkread > count) + oneblkread = count; + + if ((offset == 0) && (oneblkread == p_bd->sector_size)) { + if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) + goto err_out; + MEMCPY(((INT8 *) buffer)+read_bytes, ((INT8 *) tmp_bh->b_data), (INT32) oneblkread); + } else { + if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) + goto err_out; + MEMCPY(((INT8 *) buffer)+read_bytes, ((INT8 *) tmp_bh->b_data)+offset, (INT32) oneblkread); + } + count -= oneblkread; + read_bytes += oneblkread; + fid->rwoffset += oneblkread; + } + brelse(tmp_bh); + +err_out: + /* set the size of read bytes */ + if (rcount != NULL) + *rcount = read_bytes; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsReadFile */ + +/* ffsWriteFile : write data into a opened file */ +INT32 ffsWriteFile(struct inode *inode, FILE_ID_T *fid, void *buffer, UINT64 count, UINT64 *wcount) +{ + INT32 modified = FALSE, offset, sec_offset, clu_offset; + INT32 num_clusters, num_alloc, num_alloced = (INT32) ~0; + UINT32 clu, last_clu, LogSector, sector = 0; + UINT64 oneblkwrite, write_bytes; + CHAIN_T new_clu; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + ENTRY_SET_CACHE_T *es = NULL; + struct buffer_head *tmp_bh = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + /* check if the given file ID is opened */ + if (fid->type != TYPE_FILE) + return FFS_PERMISSIONERR; + + if (fid->rwoffset > fid->size) + fid->rwoffset = fid->size; + + if (count == 0) { + if (wcount != NULL) + *wcount = 0; + return FFS_SUCCESS; + } + + fs_set_vol_flags(sb, VOL_DIRTY); + + if (fid->size == 0) + num_clusters = 0; + else + num_clusters = (INT32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; + + write_bytes = 0; + + while (count > 0) { + clu_offset = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); + clu = last_clu = fid->start_clu; + + if (fid->flags == 0x03) { + if ((clu_offset > 0) && (clu != CLUSTER_32(~0))) { + last_clu += clu_offset - 1; + + if (clu_offset == num_clusters) + clu = CLUSTER_32(~0); + else + clu += clu_offset; + } + } else { + /* hint information */ + if ((clu_offset > 0) && (fid->hint_last_off > 0) && + (clu_offset >= fid->hint_last_off)) { + clu_offset -= fid->hint_last_off; + clu = fid->hint_last_clu; + } + + while ((clu_offset > 0) && (clu != CLUSTER_32(~0))) { + last_clu = clu; + /* clu = FAT_read(sb, clu); */ + if (FAT_read(sb, clu, &clu) == -1) + return FFS_MEDIAERR; + + clu_offset--; + } + } + + if (clu == CLUSTER_32(~0)) { + num_alloc = (INT32)((count-1) >> p_fs->cluster_size_bits) + 1; + new_clu.dir = (last_clu == CLUSTER_32(~0)) ? CLUSTER_32(~0) : last_clu+1; + new_clu.size = 0; + new_clu.flags = fid->flags; + + /* (1) allocate a chain of clusters */ + num_alloced = p_fs->fs_func->alloc_cluster(sb, num_alloc, &new_clu); + if (num_alloced == 0) + break; + + /* (2) append to the FAT chain */ + if (last_clu == CLUSTER_32(~0)) { + if (new_clu.flags == 0x01) + fid->flags = 0x01; + fid->start_clu = new_clu.dir; + modified = TRUE; + } else { + if (new_clu.flags != fid->flags) { + exfat_chain_cont_cluster(sb, fid->start_clu, num_clusters); + fid->flags = 0x01; + modified = TRUE; + } + if (new_clu.flags == 0x01) + FAT_write(sb, last_clu, new_clu.dir); + } + + num_clusters += num_alloced; + clu = new_clu.dir; + } + + /* hint information */ + fid->hint_last_off = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); + fid->hint_last_clu = clu; + + offset = (INT32)(fid->rwoffset & (p_fs->cluster_size-1)); /* byte offset in cluster */ + sec_offset = offset >> p_bd->sector_size_bits; /* sector offset in cluster */ + offset &= p_bd->sector_size_mask; /* byte offset in sector */ + + LogSector = START_SECTOR(clu) + sec_offset; + + oneblkwrite = (UINT64)(p_bd->sector_size - offset); + if (oneblkwrite > count) + oneblkwrite = count; + + if ((offset == 0) && (oneblkwrite == p_bd->sector_size)) { + if (sector_read(sb, LogSector, &tmp_bh, 0) != FFS_SUCCESS) + goto err_out; + MEMCPY(((INT8 *) tmp_bh->b_data), ((INT8 *) buffer)+write_bytes, (INT32) oneblkwrite); + if (sector_write(sb, LogSector, tmp_bh, 0) != FFS_SUCCESS) { + brelse(tmp_bh); + goto err_out; + } + } else { + if ((offset > 0) || ((fid->rwoffset+oneblkwrite) < fid->size)) { + if (sector_read(sb, LogSector, &tmp_bh, 1) != FFS_SUCCESS) + goto err_out; + } else { + if (sector_read(sb, LogSector, &tmp_bh, 0) != FFS_SUCCESS) + goto err_out; + } + + MEMCPY(((INT8 *) tmp_bh->b_data)+offset, ((INT8 *) buffer)+write_bytes, (INT32) oneblkwrite); + if (sector_write(sb, LogSector, tmp_bh, 0) != FFS_SUCCESS) { + brelse(tmp_bh); + goto err_out; + } + } + + count -= oneblkwrite; + write_bytes += oneblkwrite; + fid->rwoffset += oneblkwrite; + + fid->attr |= ATTR_ARCHIVE; + + if (fid->size < fid->rwoffset) { + fid->size = fid->rwoffset; + modified = TRUE; + } + } + + brelse(tmp_bh); + + /* (3) update the direcoty entry */ + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (es == NULL) + goto err_out; + ep2 = ep+1; + } else { + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + goto err_out; + ep2 = ep; + } + + p_fs->fs_func->set_entry_time(ep, tm_current(&tm), TM_MODIFY); + p_fs->fs_func->set_entry_attr(ep, fid->attr); + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + + if (modified) { + if (p_fs->fs_func->get_entry_flag(ep2) != fid->flags) + p_fs->fs_func->set_entry_flag(ep2, fid->flags); + + if (p_fs->fs_func->get_entry_size(ep2) != fid->size) + p_fs->fs_func->set_entry_size(ep2, fid->size); + + if (p_fs->fs_func->get_entry_clu0(ep2) != fid->start_clu) + p_fs->fs_func->set_entry_clu0(ep2, fid->start_clu); + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + } + + if (p_fs->vol_type == EXFAT) { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + +#if (DELAYED_SYNC == 0) + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + +err_out: + /* set the size of written bytes */ + if (wcount != NULL) + *wcount = write_bytes; + + if (num_alloced == 0) + return FFS_FULL; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsWriteFile */ + +/* ffsTruncateFile : resize the file length */ +INT32 ffsTruncateFile(struct inode *inode, UINT64 old_size, UINT64 new_size) +{ + INT32 num_clusters; + UINT32 last_clu = CLUSTER_32(0), sector = 0; + CHAIN_T clu; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + ENTRY_SET_CACHE_T *es=NULL; + + /* check if the given file ID is opened */ + if (fid->type != TYPE_FILE) + return FFS_PERMISSIONERR; + + if (fid->size != old_size) { + printk(KERN_ERR "[EXFAT] truncate : can't skip it because of " + "size-mismatch(old:%lld->fid:%lld).\n" + ,old_size, fid->size); + } + + if (old_size <= new_size) + return FFS_SUCCESS; + + fs_set_vol_flags(sb, VOL_DIRTY); + + clu.dir = fid->start_clu; + clu.size = (INT32)((old_size-1) >> p_fs->cluster_size_bits) + 1; + clu.flags = fid->flags; + + if (new_size > 0) { + num_clusters = (INT32)((new_size-1) >> p_fs->cluster_size_bits) + 1; + + if (clu.flags == 0x03) { + clu.dir += num_clusters; + } else { + while (num_clusters > 0) { + last_clu = clu.dir; + if (FAT_read(sb, clu.dir, &(clu.dir)) == -1) + return FFS_MEDIAERR; + num_clusters--; + } + } + + clu.size -= num_clusters; + } + + fid->size = new_size; + fid->attr |= ATTR_ARCHIVE; + if (new_size == 0) { + fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->start_clu = CLUSTER_32(~0); + } + + /* (1) update the directory entry */ + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (es == NULL) + return FFS_MEDIAERR; + ep2 = ep+1; + } else { + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return FFS_MEDIAERR; + ep2 = ep; + } + + p_fs->fs_func->set_entry_time(ep, tm_current(&tm), TM_MODIFY); + p_fs->fs_func->set_entry_attr(ep, fid->attr); + + p_fs->fs_func->set_entry_size(ep2, new_size); + if (new_size == 0) { + p_fs->fs_func->set_entry_flag(ep2, 0x01); + p_fs->fs_func->set_entry_clu0(ep2, CLUSTER_32(0)); + } + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + else { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + + /* (2) cut off from the FAT chain */ + if (last_clu != CLUSTER_32(0)) { + if (fid->flags == 0x01) + FAT_write(sb, last_clu, CLUSTER_32(~0)); + } + + /* (3) free the clusters */ + p_fs->fs_func->free_cluster(sb, &clu, 0); + + /* hint information */ + fid->hint_last_off = -1; + if (fid->rwoffset > fid->size) { + fid->rwoffset = fid->size; + } + +#if (DELAYED_SYNC == 0) + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsTruncateFile */ + +static void update_parent_info( FILE_ID_T *fid, struct inode *parent_inode) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(parent_inode->i_sb)->fs_info); + FILE_ID_T *parent_fid = &(EXFAT_I(parent_inode)->fid); + + if (unlikely((parent_fid->flags != fid->dir.flags) + || (parent_fid->size != (fid->dir.size<cluster_size_bits)) + || (parent_fid->start_clu != fid->dir.dir))) { + + fid->dir.dir = parent_fid->start_clu; + fid->dir.flags = parent_fid->flags; + fid->dir.size = ((parent_fid->size + (p_fs->cluster_size-1)) + >> p_fs->cluster_size_bits); + } +} + +/* ffsMoveFile : move(rename) a old file into a new file */ +INT32 ffsMoveFile(struct inode *old_parent_inode, FILE_ID_T *fid, struct inode *new_parent_inode, struct dentry *new_dentry) +{ + INT32 ret; + INT32 dentry; + CHAIN_T olddir, newdir; + CHAIN_T *p_dir=NULL; + UNI_NAME_T uni_name; + DENTRY_T *ep; + struct super_block *sb = old_parent_inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + UINT8 *new_path = (UINT8 *) new_dentry->d_name.name; + struct inode *new_inode = new_dentry->d_inode; + int num_entries; + FILE_ID_T *new_fid = NULL; + INT32 new_entry=0; + + /* check the validity of pointer parameters */ + if ((new_path == NULL) || (*new_path == '\0')) + return FFS_ERROR; + + update_parent_info(fid, old_parent_inode); + + olddir.dir = fid->dir.dir; + olddir.size = fid->dir.size; + olddir.flags = fid->dir.flags; + + dentry = fid->entry; + + /* check if the old file is "." or ".." */ + if (p_fs->vol_type != EXFAT) { + if ((olddir.dir != p_fs->root_dir) && (dentry < 2)) + return FFS_PERMISSIONERR; + } + + ep = get_entry_in_dir(sb, &olddir, dentry, NULL); + if (!ep) + return FFS_MEDIAERR; + + if (p_fs->fs_func->get_entry_attr(ep) & ATTR_READONLY) + return FFS_PERMISSIONERR; + + /* check whether new dir is existing directory and empty */ + if (new_inode) { + UINT32 entry_type; + + ret = FFS_MEDIAERR; + new_fid = &EXFAT_I(new_inode)->fid; + + update_parent_info(new_fid, new_parent_inode); + + p_dir = &(new_fid->dir); + new_entry = new_fid->entry; + ep = get_entry_in_dir(sb, p_dir, new_entry, NULL); + if (!ep) + goto out; + + entry_type = p_fs->fs_func->get_entry_type(ep); + + if (entry_type == TYPE_DIR) { + CHAIN_T new_clu; + new_clu.dir = new_fid->start_clu; + new_clu.size = (INT32)((new_fid->size-1) >> p_fs->cluster_size_bits) + 1; + new_clu.flags = new_fid->flags; + + if (!is_dir_empty(sb, &new_clu)) + return FFS_FILEEXIST; + } + } + + /* check the validity of directory name in the given new pathname */ + ret = resolve_path(new_parent_inode, new_path, &newdir, &uni_name); + if (ret) + return ret; + + fs_set_vol_flags(sb, VOL_DIRTY); + + if (olddir.dir == newdir.dir) + ret = rename_file(new_parent_inode, &olddir, dentry, &uni_name, fid); + else + ret = move_file(new_parent_inode, &olddir, dentry, &newdir, &uni_name, fid); + + if ((ret == FFS_SUCCESS) && new_inode) { + /* delete entries of new_dir */ + ep = get_entry_in_dir(sb, p_dir, new_entry, NULL); + if (!ep) + goto out; + + num_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, new_entry, ep); + if (num_entries < 0) + goto out; + p_fs->fs_func->delete_dir_entry(sb, p_dir, new_entry, 0, num_entries+1); + } +out: +#if (DELAYED_SYNC == 0) + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return ret; +} /* end of ffsMoveFile */ + +/* ffsRemoveFile : remove a file */ +INT32 ffsRemoveFile(struct inode *inode, FILE_ID_T *fid) +{ + INT32 dentry; + CHAIN_T dir, clu_to_free; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + dir.dir = fid->dir.dir; + dir.size = fid->dir.size; + dir.flags = fid->dir.flags; + + dentry = fid->entry; + + ep = get_entry_in_dir(sb, &dir, dentry, NULL); + if (!ep) + return FFS_MEDIAERR; + + if (p_fs->fs_func->get_entry_attr(ep) & ATTR_READONLY) + return FFS_PERMISSIONERR; + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* (1) update the directory entry */ + remove_file(inode, &dir, dentry); + + clu_to_free.dir = fid->start_clu; + clu_to_free.size = (INT32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; + clu_to_free.flags = fid->flags; + + /* (2) free the clusters */ + p_fs->fs_func->free_cluster(sb, &clu_to_free, 0); + + fid->size = 0; + fid->start_clu = CLUSTER_32(~0); + fid->flags = (p_fs->vol_type == EXFAT)? 0x03: 0x01; + +#if (DELAYED_SYNC == 0) + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsRemoveFile */ + +/* ffsSetAttr : set the attribute of a given file */ +INT32 ffsSetAttr(struct inode *inode, UINT32 attr) +{ + UINT32 type, sector = 0; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + UINT8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; + ENTRY_SET_CACHE_T *es = NULL; + + if (fid->attr == attr) { + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + return FFS_SUCCESS; + } + + if (is_dir) { + if ((fid->dir.dir == p_fs->root_dir) && + (fid->entry == -1)) { + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + return FFS_SUCCESS; + } + } + + /* get the directory entry of given file */ + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (es == NULL) + return FFS_MEDIAERR; + } else { + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return FFS_MEDIAERR; + } + + type = p_fs->fs_func->get_entry_type(ep); + + if (((type == TYPE_FILE) && (attr & ATTR_SUBDIR)) || + ((type == TYPE_DIR) && (!(attr & ATTR_SUBDIR)))) { + INT32 err; + if (p_fs->dev_ejected) + err = FFS_MEDIAERR; + else + err = FFS_ERROR; + + if (p_fs->vol_type == EXFAT) + release_entry_set(es); + return err; + } + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* set the file attribute */ + fid->attr = attr; + p_fs->fs_func->set_entry_attr(ep, attr); + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + else { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + +#if (DELAYED_SYNC == 0) + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsSetAttr */ + +/* ffsGetStat : get the information of a given file */ +INT32 ffsGetStat(struct inode *inode, DIR_ENTRY_T *info) +{ + UINT32 sector = 0; + INT32 count; + CHAIN_T dir; + UNI_NAME_T uni_name; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + ENTRY_SET_CACHE_T *es=NULL; + UINT8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; + + PRINTK("ffsGetStat entered\n"); + + if (is_dir) { + if ((fid->dir.dir == p_fs->root_dir) && + (fid->entry == -1)) { + info->Attr = ATTR_SUBDIR; + MEMSET((INT8 *) &info->CreateTimestamp, 0, sizeof(DATE_TIME_T)); + MEMSET((INT8 *) &info->ModifyTimestamp, 0, sizeof(DATE_TIME_T)); + MEMSET((INT8 *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T)); + STRCPY(info->ShortName, "."); + STRCPY(info->Name, "."); + + dir.dir = p_fs->root_dir; + dir.flags = 0x01; + + if (p_fs->root_dir == CLUSTER_32(0)) /* FAT16 root_dir */ + info->Size = p_fs->dentries_in_root << DENTRY_SIZE_BITS; + else + info->Size = count_num_clusters(sb, &dir) << p_fs->cluster_size_bits; + + count = count_dos_name_entries(sb, &dir, TYPE_DIR); + if (count < 0) + return FFS_MEDIAERR; + info->NumSubdirs = count; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + return FFS_SUCCESS; + } + } + + /* get the directory entry of given file or directory */ + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_2_ENTRIES, &ep); + if (es == NULL) + return FFS_MEDIAERR; + ep2 = ep+1; + } else { + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return FFS_MEDIAERR; + ep2 = ep; + buf_lock(sb, sector); + } + + /* set FILE_INFO structure using the acquired DENTRY_T */ + info->Attr = p_fs->fs_func->get_entry_attr(ep); + + p_fs->fs_func->get_entry_time(ep, &tm, TM_CREATE); + info->CreateTimestamp.Year = tm.year; + info->CreateTimestamp.Month = tm.mon; + info->CreateTimestamp.Day = tm.day; + info->CreateTimestamp.Hour = tm.hour; + info->CreateTimestamp.Minute = tm.min; + info->CreateTimestamp.Second = tm.sec; + info->CreateTimestamp.MilliSecond = 0; + + p_fs->fs_func->get_entry_time(ep, &tm, TM_MODIFY); + info->ModifyTimestamp.Year = tm.year; + info->ModifyTimestamp.Month = tm.mon; + info->ModifyTimestamp.Day = tm.day; + info->ModifyTimestamp.Hour = tm.hour; + info->ModifyTimestamp.Minute = tm.min; + info->ModifyTimestamp.Second = tm.sec; + info->ModifyTimestamp.MilliSecond = 0; + + MEMSET((INT8 *) &info->AccessTimestamp, 0, sizeof(DATE_TIME_T)); + + *(uni_name.name) = 0x0; + /* XXX this is very bad for exfat cuz name is already included in es. + API should be revised */ + p_fs->fs_func->get_uni_name_from_ext_entry(sb, &(fid->dir), fid->entry, uni_name.name); + if (*(uni_name.name) == 0x0) + get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x1); + nls_uniname_to_cstring(sb, info->Name, &uni_name); + + if (p_fs->vol_type == EXFAT) { + info->NumSubdirs = 2; + } else { + buf_unlock(sb, sector); + get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x0); + nls_uniname_to_cstring(sb, info->ShortName, &uni_name); + info->NumSubdirs = 0; + } + + info->Size = p_fs->fs_func->get_entry_size(ep2); + + if (p_fs->vol_type == EXFAT) + release_entry_set(es); + + if (is_dir) { + dir.dir = fid->start_clu; + dir.flags = 0x01; + + if (info->Size == 0) + info->Size = (UINT64) count_num_clusters(sb, &dir) << p_fs->cluster_size_bits; + + count = count_dos_name_entries(sb, &dir, TYPE_DIR); + if (count < 0) + return FFS_MEDIAERR; + info->NumSubdirs += count; + } + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + PRINTK("ffsGetStat exited successfully\n"); + return FFS_SUCCESS; +} /* end of ffsGetStat */ + +/* ffsSetStat : set the information of a given file */ +INT32 ffsSetStat(struct inode *inode, DIR_ENTRY_T *info) +{ + UINT32 sector = 0; + TIMESTAMP_T tm; + DENTRY_T *ep, *ep2; + ENTRY_SET_CACHE_T *es=NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + UINT8 is_dir = (fid->type == TYPE_DIR) ? 1 : 0; + + if (is_dir) { + if ((fid->dir.dir == p_fs->root_dir) && + (fid->entry == -1)) { + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + return FFS_SUCCESS; + } + } + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* get the directory entry of given file or directory */ + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (es == NULL) + return FFS_MEDIAERR; + ep2 = ep+1; + } else { + /* for other than exfat */ + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return FFS_MEDIAERR; + ep2 = ep; + } + + + p_fs->fs_func->set_entry_attr(ep, info->Attr); + + /* set FILE_INFO structure using the acquired DENTRY_T */ + tm.sec = info->CreateTimestamp.Second; + tm.min = info->CreateTimestamp.Minute; + tm.hour = info->CreateTimestamp.Hour; + tm.day = info->CreateTimestamp.Day; + tm.mon = info->CreateTimestamp.Month; + tm.year = info->CreateTimestamp.Year; + p_fs->fs_func->set_entry_time(ep, &tm, TM_CREATE); + + tm.sec = info->ModifyTimestamp.Second; + tm.min = info->ModifyTimestamp.Minute; + tm.hour = info->ModifyTimestamp.Hour; + tm.day = info->ModifyTimestamp.Day; + tm.mon = info->ModifyTimestamp.Month; + tm.year = info->ModifyTimestamp.Year; + p_fs->fs_func->set_entry_time(ep, &tm, TM_MODIFY); + + + p_fs->fs_func->set_entry_size(ep2, info->Size); + + if (p_fs->vol_type != EXFAT) { + buf_modify(sb, sector); + } else { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsSetStat */ + +INT32 ffsMapCluster(struct inode *inode, INT32 clu_offset, UINT32 *clu) +{ + INT32 num_clusters, num_alloced, modified = FALSE; + UINT32 last_clu, sector = 0; + CHAIN_T new_clu; + DENTRY_T *ep; + ENTRY_SET_CACHE_T *es = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + + fid->rwoffset = (INT64)(clu_offset) << p_fs->cluster_size_bits; + + if (EXFAT_I(inode)->mmu_private == 0) + num_clusters = 0; + else + num_clusters = (INT32)((EXFAT_I(inode)->mmu_private-1) >> p_fs->cluster_size_bits) + 1; + + *clu = last_clu = fid->start_clu; + + if (fid->flags == 0x03) { + if ((clu_offset > 0) && (*clu != CLUSTER_32(~0))) { + last_clu += clu_offset - 1; + + if (clu_offset == num_clusters) + *clu = CLUSTER_32(~0); + else + *clu += clu_offset; + } + } else { + /* hint information */ + if ((clu_offset > 0) && (fid->hint_last_off > 0) && + (clu_offset >= fid->hint_last_off)) { + clu_offset -= fid->hint_last_off; + *clu = fid->hint_last_clu; + } + + while ((clu_offset > 0) && (*clu != CLUSTER_32(~0))) { + last_clu = *clu; + if (FAT_read(sb, *clu, clu) == -1) + return FFS_MEDIAERR; + clu_offset--; + } + } + + if (*clu == CLUSTER_32(~0)) { + fs_set_vol_flags(sb, VOL_DIRTY); + + new_clu.dir = (last_clu == CLUSTER_32(~0)) ? CLUSTER_32(~0) : last_clu+1; + new_clu.size = 0; + new_clu.flags = fid->flags; + + /* (1) allocate a cluster */ + num_alloced = p_fs->fs_func->alloc_cluster(sb, 1, &new_clu); + if (num_alloced < 1) + return FFS_FULL; + + /* (2) append to the FAT chain */ + if (last_clu == CLUSTER_32(~0)) { + if (new_clu.flags == 0x01) + fid->flags = 0x01; + fid->start_clu = new_clu.dir; + modified = TRUE; + } else { + if (new_clu.flags != fid->flags) { + exfat_chain_cont_cluster(sb, fid->start_clu, num_clusters); + fid->flags = 0x01; + modified = TRUE; + } + if (new_clu.flags == 0x01) + FAT_write(sb, last_clu, new_clu.dir); + } + + *clu = new_clu.dir; + + if (p_fs->vol_type == EXFAT) { + es = get_entry_set_in_dir(sb, &(fid->dir), fid->entry, ES_ALL_ENTRIES, &ep); + if (es == NULL) + return FFS_MEDIAERR; + /* get stream entry */ + ep++; + } + + /* (3) update directory entry */ + if (modified) { + if (p_fs->vol_type != EXFAT) { + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry, §or); + if (!ep) + return FFS_MEDIAERR; + } + + if (p_fs->fs_func->get_entry_flag(ep) != fid->flags) + p_fs->fs_func->set_entry_flag(ep, fid->flags); + + if (p_fs->fs_func->get_entry_clu0(ep) != fid->start_clu) + p_fs->fs_func->set_entry_clu0(ep, fid->start_clu); + + if (p_fs->vol_type != EXFAT) + buf_modify(sb, sector); + } + + if (p_fs->vol_type == EXFAT) { + update_dir_checksum_with_entry_set(sb, es); + release_entry_set(es); + } + + /* add number of new blocks to inode */ + inode->i_blocks += num_alloced << (p_fs->cluster_size_bits - 9); + } + + /* hint information */ + fid->hint_last_off = (INT32)(fid->rwoffset >> p_fs->cluster_size_bits); + fid->hint_last_clu = *clu; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsMapCluster */ + +/*----------------------------------------------------------------------*/ +/* Directory Operation Functions */ +/*----------------------------------------------------------------------*/ + +/* ffsCreateDir : create(make) a directory */ +INT32 ffsCreateDir(struct inode *inode, UINT8 *path, FILE_ID_T *fid) +{ + INT32 ret/*, dentry*/; + CHAIN_T dir; + UNI_NAME_T uni_name; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + PRINTK("ffsCreateDir entered\n"); + + /* check the validity of directory name in the given old pathname */ + ret = resolve_path(inode, path, &dir, &uni_name); + if (ret) + return ret; + + fs_set_vol_flags(sb, VOL_DIRTY); + + ret = create_dir(inode, &dir, &uni_name, fid); + +#if (DELAYED_SYNC == 0) + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return ret; +} /* end of ffsCreateDir */ + +/* ffsReadDir : read a directory entry from the opened directory */ +INT32 ffsReadDir(struct inode *inode, DIR_ENTRY_T *dir_entry) +{ + INT32 i, dentry, clu_offset; + INT32 dentries_per_clu, dentries_per_clu_bits = 0; + UINT32 type, sector; + CHAIN_T dir, clu; + UNI_NAME_T uni_name; + TIMESTAMP_T tm; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + + /* check if the given file ID is opened */ + if (fid->type != TYPE_DIR) + return FFS_PERMISSIONERR; + + if (fid->entry == -1) { + dir.dir = p_fs->root_dir; + dir.flags = 0x01; + } else { + dir.dir = fid->start_clu; + dir.size = (INT32)(fid->size >> p_fs->cluster_size_bits); + dir.flags = fid->flags; + } + + dentry = (INT32) fid->rwoffset; + + if (dir.dir == CLUSTER_32(0)) { /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + + if (dentry == dentries_per_clu) { + clu.dir = CLUSTER_32(~0); + } else { + clu.dir = dir.dir; + clu.size = dir.size; + clu.flags = dir.flags; + } + } else { + dentries_per_clu = p_fs->dentries_per_clu; + dentries_per_clu_bits = my_log2(dentries_per_clu); + + clu_offset = dentry >> dentries_per_clu_bits; + clu.dir = dir.dir; + clu.size = dir.size; + clu.flags = dir.flags; + + if (clu.flags == 0x03) { + clu.dir += clu_offset; + clu.size -= clu_offset; + } else { + /* hint_information */ + if ((clu_offset > 0) && (fid->hint_last_off > 0) && + (clu_offset >= fid->hint_last_off)) { + clu_offset -= fid->hint_last_off; + clu.dir = fid->hint_last_clu; + } + + while (clu_offset > 0) { + /* clu.dir = FAT_read(sb, clu.dir); */ + if (FAT_read(sb, clu.dir, &(clu.dir)) == -1) + return FFS_MEDIAERR; + + clu_offset--; + } + } + } + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + if (dir.dir == CLUSTER_32(0)) /* FAT16 root_dir */ + i = dentry % dentries_per_clu; + else + i = dentry & (dentries_per_clu-1); + + for ( ; i < dentries_per_clu; i++, dentry++) { + ep = get_entry_in_dir(sb, &clu, i, §or); + if (!ep) + return FFS_MEDIAERR; + + type = p_fs->fs_func->get_entry_type(ep); + + if (type == TYPE_UNUSED) + break; + + if ((type != TYPE_FILE) && (type != TYPE_DIR)) + continue; + + buf_lock(sb, sector); + dir_entry->Attr = p_fs->fs_func->get_entry_attr(ep); + + p_fs->fs_func->get_entry_time(ep, &tm, TM_CREATE); + dir_entry->CreateTimestamp.Year = tm.year; + dir_entry->CreateTimestamp.Month = tm.mon; + dir_entry->CreateTimestamp.Day = tm.day; + dir_entry->CreateTimestamp.Hour = tm.hour; + dir_entry->CreateTimestamp.Minute = tm.min; + dir_entry->CreateTimestamp.Second = tm.sec; + dir_entry->CreateTimestamp.MilliSecond = 0; + + p_fs->fs_func->get_entry_time(ep, &tm, TM_MODIFY); + dir_entry->ModifyTimestamp.Year = tm.year; + dir_entry->ModifyTimestamp.Month = tm.mon; + dir_entry->ModifyTimestamp.Day = tm.day; + dir_entry->ModifyTimestamp.Hour = tm.hour; + dir_entry->ModifyTimestamp.Minute = tm.min; + dir_entry->ModifyTimestamp.Second = tm.sec; + dir_entry->ModifyTimestamp.MilliSecond = 0; + + MEMSET((INT8 *) &dir_entry->AccessTimestamp, 0, sizeof(DATE_TIME_T)); + + *(uni_name.name) = 0x0; + p_fs->fs_func->get_uni_name_from_ext_entry(sb, &dir, dentry, uni_name.name); + if (*(uni_name.name) == 0x0) + get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x1); + nls_uniname_to_cstring(sb, dir_entry->Name, &uni_name); + buf_unlock(sb, sector); + + if (p_fs->vol_type == EXFAT) { + ep = get_entry_in_dir(sb, &clu, i+1, NULL); + if (!ep) + return FFS_MEDIAERR; + } else { + get_uni_name_from_dos_entry(sb, (DOS_DENTRY_T *) ep, &uni_name, 0x0); + nls_uniname_to_cstring(sb, dir_entry->ShortName, &uni_name); + } + + dir_entry->Size = p_fs->fs_func->get_entry_size(ep); + + /* hint information */ + if (dir.dir == CLUSTER_32(0)) { /* FAT16 root_dir */ + } else { + fid->hint_last_off = dentry >> dentries_per_clu_bits; + fid->hint_last_clu = clu.dir; + } + + fid->rwoffset = (INT64) ++dentry; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; + } + + if (dir.dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUSTER_32(~0); + } else { + /* clu.dir = FAT_read(sb, clu.dir); */ + if (FAT_read(sb, clu.dir, &(clu.dir)) == -1) + return FFS_MEDIAERR; + } + } + + *(dir_entry->Name) = '\0'; + + fid->rwoffset = (INT64) ++dentry; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsReadDir */ + +/* ffsRemoveDir : remove a directory */ +INT32 ffsRemoveDir(struct inode *inode, FILE_ID_T *fid) +{ + INT32 dentry; + CHAIN_T dir, clu_to_free; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + dir.dir = fid->dir.dir; + dir.size = fid->dir.size; + dir.flags = fid->dir.flags; + + dentry = fid->entry; + + /* check if the file is "." or ".." */ + if (p_fs->vol_type != EXFAT) { + if ((dir.dir != p_fs->root_dir) && (dentry < 2)) + return FFS_PERMISSIONERR; + } + + clu_to_free.dir = fid->start_clu; + clu_to_free.size = (INT32)((fid->size-1) >> p_fs->cluster_size_bits) + 1; + clu_to_free.flags = fid->flags; + + if (!is_dir_empty(sb, &clu_to_free)) + return FFS_FILEEXIST; + + fs_set_vol_flags(sb, VOL_DIRTY); + + /* (1) update the directory entry */ + remove_file(inode, &dir, dentry); + + /* (2) free the clusters */ + p_fs->fs_func->free_cluster(sb, &clu_to_free, 1); + + fid->size = 0; + fid->start_clu = CLUSTER_32(~0); + fid->flags = (p_fs->vol_type == EXFAT)? 0x03: 0x01; + +#if (DELAYED_SYNC == 0) + fs_sync(sb, 0); + fs_set_vol_flags(sb, VOL_CLEAN); +#endif + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + return FFS_SUCCESS; +} /* end of ffsRemoveDir */ + +/*======================================================================*/ +/* Local Function Definitions */ +/*======================================================================*/ + +/* + * File System Management Functions + */ + +INT32 fs_init(void) +{ + /* critical check for system requirement on size of DENTRY_T structure */ + if (sizeof(DENTRY_T) != DENTRY_SIZE) { + return FFS_ALIGNMENTERR; + } + + if (sizeof(DOS_DENTRY_T) != DENTRY_SIZE) { + return FFS_ALIGNMENTERR; + } + + if (sizeof(EXT_DENTRY_T) != DENTRY_SIZE) { + return FFS_ALIGNMENTERR; + } + + if (sizeof(FILE_DENTRY_T) != DENTRY_SIZE) { + return FFS_ALIGNMENTERR; + } + + if (sizeof(STRM_DENTRY_T) != DENTRY_SIZE) { + return FFS_ALIGNMENTERR; + } + + if (sizeof(NAME_DENTRY_T) != DENTRY_SIZE) { + return FFS_ALIGNMENTERR; + } + + if (sizeof(BMAP_DENTRY_T) != DENTRY_SIZE) { + return FFS_ALIGNMENTERR; + } + + if (sizeof(CASE_DENTRY_T) != DENTRY_SIZE) { + return FFS_ALIGNMENTERR; + } + + if (sizeof(VOLM_DENTRY_T) != DENTRY_SIZE) { + return FFS_ALIGNMENTERR; + } + + return FFS_SUCCESS; +} /* end of fs_init */ + +INT32 fs_shutdown(void) +{ + return FFS_SUCCESS; +} /* end of fs_shutdown */ + +void fs_set_vol_flags(struct super_block *sb, UINT32 new_flag) +{ + PBR_SECTOR_T *p_pbr; + BPBEX_T *p_bpb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_fs->vol_flag == new_flag) + return; + + p_fs->vol_flag = new_flag; + + if (p_fs->vol_type == EXFAT) { + if (p_fs->pbr_bh == NULL) { + if (sector_read(sb, p_fs->PBR_sector, &(p_fs->pbr_bh), 1) != FFS_SUCCESS) + return; + } + + p_pbr = (PBR_SECTOR_T *) p_fs->pbr_bh->b_data; + p_bpb = (BPBEX_T *) p_pbr->bpb; + SET16(p_bpb->vol_flags, (UINT16) new_flag); + + /* XXX duyoung + what can we do here? (cuz fs_set_vol_flags() is void) */ + if ((new_flag == VOL_DIRTY) && (!buffer_dirty(p_fs->pbr_bh))) + sector_write(sb, p_fs->PBR_sector, p_fs->pbr_bh, 1); + else + sector_write(sb, p_fs->PBR_sector, p_fs->pbr_bh, 0); + } +} /* end of fs_set_vol_flags */ + +void fs_sync(struct super_block *sb, INT32 do_sync) +{ + if (do_sync) + bdev_sync(sb); +} /* end of fs_sync */ + +void fs_error(struct super_block *sb) +{ + struct exfat_mount_options *opts = &EXFAT_SB(sb)->options; + + if (opts->errors == EXFAT_ERRORS_PANIC) + panic("[EXFAT] Filesystem panic from previous error\n"); + else if ((opts->errors == EXFAT_ERRORS_RO) && !(sb->s_flags & MS_RDONLY)) { + sb->s_flags |= MS_RDONLY; + printk(KERN_ERR "[EXFAT] Filesystem has been set read-only\n"); + } +} + +/* + * Cluster Management Functions + */ + +INT32 clear_cluster(struct super_block *sb, UINT32 clu) +{ + UINT32 s, n; + INT32 ret = FFS_SUCCESS; + struct buffer_head *tmp_bh = NULL; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (clu == CLUSTER_32(0)) { /* FAT16 root_dir */ + s = p_fs->root_start_sector; + n = p_fs->data_start_sector; + } else { + s = START_SECTOR(clu); + n = s + p_fs->sectors_per_clu; + } + + for ( ; s < n; s++) { + if ((ret = sector_read(sb, s, &tmp_bh, 0)) != FFS_SUCCESS) + return ret; + + MEMSET((INT8 *) tmp_bh->b_data, 0x0, p_bd->sector_size); + if ((ret = sector_write(sb, s, tmp_bh, 0)) !=FFS_SUCCESS) + break; + } + + brelse(tmp_bh); + return ret; +} /* end of clear_cluster */ + +INT32 fat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain) +{ + INT32 i, num_clusters = 0; + UINT32 new_clu, last_clu = CLUSTER_32(~0), read_clu; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + new_clu = p_chain->dir; + if (new_clu == CLUSTER_32(~0)) + new_clu = p_fs->clu_srch_ptr; + else if (new_clu >= p_fs->num_clusters) + new_clu = 2; + + __set_sb_dirty(sb); + + p_chain->dir = CLUSTER_32(~0); + + for (i = 2; i < p_fs->num_clusters; i++) { + if (FAT_read(sb, new_clu, &read_clu) != 0) + return 0; + + if (read_clu == CLUSTER_32(0)) { + FAT_write(sb, new_clu, CLUSTER_32(~0)); + num_clusters++; + + if (p_chain->dir == CLUSTER_32(~0)) + p_chain->dir = new_clu; + else + FAT_write(sb, last_clu, new_clu); + + last_clu = new_clu; + + if ((--num_alloc) == 0) { + p_fs->clu_srch_ptr = new_clu; + if (p_fs->used_clusters != (UINT32) ~0) + p_fs->used_clusters += num_clusters; + + return(num_clusters); + } + } + if ((++new_clu) >= p_fs->num_clusters) + new_clu = 2; + } + + p_fs->clu_srch_ptr = new_clu; + if (p_fs->used_clusters != (UINT32) ~0) + p_fs->used_clusters += num_clusters; + + return(num_clusters); +} /* end of fat_alloc_cluster */ + +INT32 exfat_alloc_cluster(struct super_block *sb, INT32 num_alloc, CHAIN_T *p_chain) +{ + INT32 num_clusters = 0; + UINT32 hint_clu, new_clu, last_clu = CLUSTER_32(~0); + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + hint_clu = p_chain->dir; + if (hint_clu == CLUSTER_32(~0)) { + hint_clu = test_alloc_bitmap(sb, p_fs->clu_srch_ptr-2); + if (hint_clu == CLUSTER_32(~0)) + return 0; + } else if (hint_clu >= p_fs->num_clusters) { + hint_clu = 2; + p_chain->flags = 0x01; + } + + __set_sb_dirty(sb); + + p_chain->dir = CLUSTER_32(~0); + + while ((new_clu = test_alloc_bitmap(sb, hint_clu-2)) != CLUSTER_32(~0)) { + if (new_clu != hint_clu) { + if (p_chain->flags == 0x03) { + exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters); + p_chain->flags = 0x01; + } + } + + if (set_alloc_bitmap(sb, new_clu-2) != FFS_SUCCESS) + return 0; + + num_clusters++; + + if (p_chain->flags == 0x01) + FAT_write(sb, new_clu, CLUSTER_32(~0)); + + if (p_chain->dir == CLUSTER_32(~0)) { + p_chain->dir = new_clu; + } else { + if (p_chain->flags == 0x01) + FAT_write(sb, last_clu, new_clu); + } + last_clu = new_clu; + + if ((--num_alloc) == 0) { + p_fs->clu_srch_ptr = hint_clu; + if (p_fs->used_clusters != (UINT32) ~0) + p_fs->used_clusters += num_clusters; + + p_chain->size += num_clusters; + return(num_clusters); + } + + hint_clu = new_clu + 1; + if (hint_clu >= p_fs->num_clusters) { + hint_clu = 2; + + if (p_chain->flags == 0x03) { + exfat_chain_cont_cluster(sb, p_chain->dir, num_clusters); + p_chain->flags = 0x01; + } + } + } + + p_fs->clu_srch_ptr = hint_clu; + if (p_fs->used_clusters != (UINT32) ~0) + p_fs->used_clusters += num_clusters; + + p_chain->size += num_clusters; + return(num_clusters); +} /* end of exfat_alloc_cluster */ + +void fat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse) +{ + INT32 num_clusters = 0; + UINT32 clu, prev; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + INT32 i; + UINT32 sector; + + if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0))) + return; + __set_sb_dirty(sb); + clu = p_chain->dir; + + if (p_chain->size <= 0) + return; + + do { + if (p_fs->dev_ejected) + break; + + if (do_relse) { + sector = START_SECTOR(clu); + for (i = 0; i < p_fs->sectors_per_clu; i++) { + buf_release(sb, sector+i); + } + } + + prev = clu; + if (FAT_read(sb, clu, &clu) == -1) + break; + + FAT_write(sb, prev, CLUSTER_32(0)); + num_clusters++; + + } while (clu != CLUSTER_32(~0)); + + if (p_fs->used_clusters != (UINT32) ~0) + p_fs->used_clusters -= num_clusters; +} /* end of fat_free_cluster */ + +void exfat_free_cluster(struct super_block *sb, CHAIN_T *p_chain, INT32 do_relse) +{ + INT32 num_clusters = 0; + UINT32 clu; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + INT32 i; + UINT32 sector; + + if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0))) + return; + + if (p_chain->size <= 0) { + printk(KERN_ERR "[EXFAT] free_cluster : skip free-req clu:%u, " + "because of zero-size truncation\n" + ,p_chain->dir); + return; + } + + __set_sb_dirty(sb); + clu = p_chain->dir; + + if (p_chain->flags == 0x03) { + do { + if (do_relse) { + sector = START_SECTOR(clu); + for (i = 0; i < p_fs->sectors_per_clu; i++) { + buf_release(sb, sector+i); + } + } + + if (clr_alloc_bitmap(sb, clu-2) != FFS_SUCCESS) + break; + clu++; + + num_clusters++; + } while (num_clusters < p_chain->size); + } else { + do { + if (p_fs->dev_ejected) + break; + + if (do_relse) { + sector = START_SECTOR(clu); + for (i = 0; i < p_fs->sectors_per_clu; i++) { + buf_release(sb, sector+i); + } + } + + if (clr_alloc_bitmap(sb, clu-2) != FFS_SUCCESS) + break; + + if (FAT_read(sb, clu, &clu) == -1) + break; + num_clusters++; + } while ((clu != CLUSTER_32(0)) && (clu != CLUSTER_32(~0))); + } + + if (p_fs->used_clusters != (UINT32) ~0) + p_fs->used_clusters -= num_clusters; +} /* end of exfat_free_cluster */ + +UINT32 find_last_cluster(struct super_block *sb, CHAIN_T *p_chain) +{ + UINT32 clu, next; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + clu = p_chain->dir; + + if (p_chain->flags == 0x03) { + clu += p_chain->size - 1; + } else { + while((FAT_read(sb, clu, &next) == 0) && (next != CLUSTER_32(~0))) { + if (p_fs->dev_ejected) + break; + clu = next; + } + } + + return(clu); +} /* end of find_last_cluster */ + +INT32 count_num_clusters(struct super_block *sb, CHAIN_T *p_chain) +{ + INT32 i, count = 0; + UINT32 clu; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0))) + return 0; + + clu = p_chain->dir; + + if (p_chain->flags == 0x03) { + count = p_chain->size; + } else { + for (i = 2; i < p_fs->num_clusters; i++) { + count++; + if (FAT_read(sb, clu, &clu) != 0) + return 0; + if (clu == CLUSTER_32(~0)) + break; + } + } + + return(count); +} /* end of count_num_clusters */ + +INT32 fat_count_used_clusters(struct super_block *sb) +{ + INT32 i, count = 0; + UINT32 clu; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + for (i = 2; i < p_fs->num_clusters; i++) { + if (FAT_read(sb, i, &clu) != 0) + break; + if (clu != CLUSTER_32(0)) + count++; + } + + return(count); +} /* end of fat_count_used_clusters */ + +INT32 exfat_count_used_clusters(struct super_block *sb) +{ + INT32 i, map_i, map_b, count = 0; + UINT8 k; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + map_i = map_b = 0; + + for (i = 2; i < p_fs->num_clusters; i += 8) { + k = *(((UINT8 *) p_fs->vol_amap[map_i]->b_data) + map_b); + count += used_bit[k]; + + if ((++map_b) >= p_bd->sector_size) { + map_i++; + map_b = 0; + } + } + + return(count); +} /* end of exfat_count_used_clusters */ + +void exfat_chain_cont_cluster(struct super_block *sb, UINT32 chain, INT32 len) +{ + if (len == 0) + return; + + while (len > 1) { + FAT_write(sb, chain, chain+1); + chain++; + len--; + } + FAT_write(sb, chain, CLUSTER_32(~0)); +} /* end of exfat_chain_cont_cluster */ + +/* + * Allocation Bitmap Management Functions + */ + +INT32 load_alloc_bitmap(struct super_block *sb) +{ + INT32 i, j, ret; + UINT32 map_size; + UINT32 type, sector; + CHAIN_T clu; + BMAP_DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + clu.dir = p_fs->root_dir; + clu.flags = 0x01; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + for (i = 0; i < p_fs->dentries_per_clu; i++) { + ep = (BMAP_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return FFS_MEDIAERR; + + type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep); + + if (type == TYPE_UNUSED) + break; + if (type != TYPE_BITMAP) + continue; + + if (ep->flags == 0x0) { + p_fs->map_clu = GET32_A(ep->start_clu); + map_size = (UINT32) GET64_A(ep->size); + + p_fs->map_sectors = ((map_size-1) >> p_bd->sector_size_bits) + 1; + + p_fs->vol_amap = (struct buffer_head **) MALLOC(sizeof(struct buffer_head *) * p_fs->map_sectors); + if (p_fs->vol_amap == NULL) + return FFS_MEMORYERR; + + sector = START_SECTOR(p_fs->map_clu); + + for (j = 0; j < p_fs->map_sectors; j++) { + p_fs->vol_amap[j] = NULL; + ret = sector_read(sb, sector+j, &(p_fs->vol_amap[j]), 1); + if (ret != FFS_SUCCESS) { + /* release all buffers and free vol_amap */ + i=0; + while (i < j) + brelse(p_fs->vol_amap[i++]); + + FREE(p_fs->vol_amap); + p_fs->vol_amap = NULL; + return ret; + } + } + + p_fs->pbr_bh = NULL; + return FFS_SUCCESS; + } + } + + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return FFS_MEDIAERR; + } + + return FFS_FORMATERR; +} /* end of load_alloc_bitmap */ + +void free_alloc_bitmap(struct super_block *sb) +{ + INT32 i; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + brelse(p_fs->pbr_bh); + + for (i = 0; i < p_fs->map_sectors; i++) { + __brelse(p_fs->vol_amap[i]); + } + + FREE(p_fs->vol_amap); + p_fs->vol_amap = NULL; +} /* end of free_alloc_bitmap */ + +INT32 set_alloc_bitmap(struct super_block *sb, UINT32 clu) +{ + INT32 i, b; + UINT32 sector; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + i = clu >> (p_bd->sector_size_bits + 3); + b = clu & ((p_bd->sector_size << 3) - 1); + + sector = START_SECTOR(p_fs->map_clu) + i; + + Bitmap_set((UINT8 *) p_fs->vol_amap[i]->b_data, b); + + return (sector_write(sb, sector, p_fs->vol_amap[i], 0)); +} /* end of set_alloc_bitmap */ + +INT32 clr_alloc_bitmap(struct super_block *sb, UINT32 clu) +{ + INT32 i, b; + UINT32 sector; +#if EXFAT_CONFIG_DISCARD + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_mount_options *opts = &sbi->options; + int ret; +#endif /* EXFAT_CONFIG_DISCARD */ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + i = clu >> (p_bd->sector_size_bits + 3); + b = clu & ((p_bd->sector_size << 3) - 1); + + sector = START_SECTOR(p_fs->map_clu) + i; + + Bitmap_clear((UINT8 *) p_fs->vol_amap[i]->b_data, b); + + return (sector_write(sb, sector, p_fs->vol_amap[i], 0)); + +#if EXFAT_CONFIG_DISCARD + if (opts->discard) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) + ret = sb_issue_discard(sb, START_SECTOR(clu), (1 << p_fs->sectors_per_clu_bits)); +#else + ret = sb_issue_discard(sb, START_SECTOR(clu), (1 << p_fs->sectors_per_clu_bits), GFP_NOFS, 0); +#endif + if (ret == -EOPNOTSUPP) { + printk(KERN_WARNING "discard not supported by device, disabling"); + opts->discard = 0; + } + } +#endif /* EXFAT_CONFIG_DISCARD */ +} /* end of clr_alloc_bitmap */ + +UINT32 test_alloc_bitmap(struct super_block *sb, UINT32 clu) +{ + INT32 i, map_i, map_b; + UINT32 clu_base, clu_free; + UINT8 k, clu_mask; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + clu_base = (clu & ~(0x7)) + 2; + clu_mask = (1 << (clu - clu_base + 2)) - 1; + + map_i = clu >> (p_bd->sector_size_bits + 3); + map_b = (clu >> 3) & p_bd->sector_size_mask; + + for (i = 2; i < p_fs->num_clusters; i += 8) { + k = *(((UINT8 *) p_fs->vol_amap[map_i]->b_data) + map_b); + if (clu_mask > 0) { + k |= clu_mask; + clu_mask = 0; + } + if (k < 0xFF) { + clu_free = clu_base + free_bit[k]; + if (clu_free < p_fs->num_clusters) + return(clu_free); + } + clu_base += 8; + + if (((++map_b) >= p_bd->sector_size) || (clu_base >= p_fs->num_clusters)) { + if ((++map_i) >= p_fs->map_sectors) { + clu_base = 2; + map_i = 0; + } + map_b = 0; + } + } + + return(CLUSTER_32(~0)); +} /* end of test_alloc_bitmap */ + +void sync_alloc_bitmap(struct super_block *sb) +{ + INT32 i; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_fs->vol_amap == NULL) + return; + + for (i = 0; i < p_fs->map_sectors; i++) { + sync_dirty_buffer(p_fs->vol_amap[i]); + } +} /* end of sync_alloc_bitmap */ + +/* + * Upcase table Management Functions + */ +INT32 __load_upcase_table(struct super_block *sb, UINT32 sector, UINT32 num_sectors, UINT32 utbl_checksum) +{ + INT32 i, ret = FFS_ERROR; + UINT32 j; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + struct buffer_head *tmp_bh = NULL; + + UINT8 skip = FALSE; + UINT32 index = 0; + UINT16 uni = 0; + UINT16 **upcase_table; + + UINT32 checksum = 0; + + upcase_table = p_fs->vol_utbl = (UINT16 **) MALLOC(UTBL_COL_COUNT * sizeof(UINT16 *)); + if(upcase_table == NULL) + return FFS_MEMORYERR; + MEMSET(upcase_table, 0, UTBL_COL_COUNT * sizeof(UINT16 *)); + + num_sectors += sector; + + while(sector < num_sectors) { + ret = sector_read(sb, sector, &tmp_bh, 1); + if (ret != FFS_SUCCESS) { + PRINTK("sector read (0x%X)fail\n", sector); + goto error; + } + sector++; + + for(i = 0; i < p_bd->sector_size && index <= 0xFFFF; i += 2) { + uni = GET16(((UINT8 *) tmp_bh->b_data)+i); + + checksum = ((checksum & 1) ? 0x80000000 : 0 ) + (checksum >> 1) + *(((UINT8 *) tmp_bh->b_data)+i); + checksum = ((checksum & 1) ? 0x80000000 : 0 ) + (checksum >> 1) + *(((UINT8 *) tmp_bh->b_data)+(i+1)); + + if(skip) { + PRINTK("skip from 0x%X ", index); + index += uni; + PRINTK("to 0x%X (amount of 0x%X)\n", index, uni); + skip = FALSE; + } else if(uni == index) + index++; + else if(uni == 0xFFFF) + skip = TRUE; + else { /* uni != index , uni != 0xFFFF */ + UINT16 col_index = get_col_index(index); + + if(upcase_table[col_index]== NULL) { + PRINTK("alloc = 0x%X\n", col_index); + upcase_table[col_index] = (UINT16 *) MALLOC(UTBL_ROW_COUNT * sizeof(UINT16)); + if(upcase_table[col_index] == NULL) { + ret = FFS_MEMORYERR; + goto error; + } + + for(j = 0 ; j < UTBL_ROW_COUNT ; j++) + upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j; + } + + upcase_table[col_index][get_row_index(index)] = uni; + index++; + } + } + } + if(index >= 0xFFFF && utbl_checksum == checksum) { + if(tmp_bh) + brelse(tmp_bh); + return FFS_SUCCESS; + } + ret = FFS_ERROR; +error: + if(tmp_bh) + brelse(tmp_bh); + free_upcase_table(sb); + return ret; +} + +INT32 __load_default_upcase_table(struct super_block *sb) +{ + INT32 i, ret = FFS_ERROR; + UINT32 j; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + UINT8 skip = FALSE; + UINT32 index = 0; + UINT16 uni = 0; + UINT16 **upcase_table; + + upcase_table = p_fs->vol_utbl = (UINT16 **) MALLOC(UTBL_COL_COUNT * sizeof(UINT16 *)); + if(upcase_table == NULL) + return FFS_MEMORYERR; + MEMSET(upcase_table, 0, UTBL_COL_COUNT * sizeof(UINT16 *)); + + for(i = 0; index <= 0xFFFF && i < NUM_UPCASE*2; i += 2) { + uni = GET16(uni_upcase + i); + if(skip) { + PRINTK("skip from 0x%X ", index); + index += uni; + PRINTK("to 0x%X (amount of 0x%X)\n", index, uni); + skip = FALSE; + } else if(uni == index) + index++; + else if(uni == 0xFFFF) + skip = TRUE; + else { /* uni != index , uni != 0xFFFF */ + UINT16 col_index = get_col_index(index); + + if(upcase_table[col_index]== NULL) { + PRINTK("alloc = 0x%X\n", col_index); + upcase_table[col_index] = (UINT16 *) MALLOC(UTBL_ROW_COUNT * sizeof(UINT16)); + if(upcase_table[col_index] == NULL) { + ret = FFS_MEMORYERR; + goto error; + } + + for(j = 0 ; j < UTBL_ROW_COUNT ; j++) + upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j; + } + + upcase_table[col_index][get_row_index(index)] = uni; + index ++; + } + } + + if(index >= 0xFFFF) + return FFS_SUCCESS; + +error: + /* FATAL error: default upcase table has error */ + free_upcase_table(sb); + return ret; +} + +INT32 load_upcase_table(struct super_block *sb) +{ + INT32 i; + UINT32 tbl_clu, tbl_size; + UINT32 type, sector, num_sectors; + CHAIN_T clu; + CASE_DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + clu.dir = p_fs->root_dir; + clu.flags = 0x01; + + if (p_fs->dev_ejected) + return FFS_MEDIAERR; + + while (clu.dir != CLUSTER_32(~0)) { + for (i = 0; i < p_fs->dentries_per_clu; i++) { + ep = (CASE_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return FFS_MEDIAERR; + + type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep); + + if (type == TYPE_UNUSED) + break; + if (type != TYPE_UPCASE) + continue; + + tbl_clu = GET32_A(ep->start_clu); + tbl_size = (UINT32) GET64_A(ep->size); + + sector = START_SECTOR(tbl_clu); + num_sectors = ((tbl_size-1) >> p_bd->sector_size_bits) + 1; + if(__load_upcase_table(sb, sector, num_sectors, GET32_A(ep->checksum)) != FFS_SUCCESS) + break; + else + return FFS_SUCCESS; + } + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return FFS_MEDIAERR; + } + /* load default upcase table */ + return __load_default_upcase_table(sb); +} /* end of load_upcase_table */ + +void free_upcase_table(struct super_block *sb) +{ + UINT32 i; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + UINT16 **upcase_table; + + upcase_table = p_fs->vol_utbl; + for(i = 0 ; i < UTBL_COL_COUNT ; i ++) + FREE(upcase_table[i]); + + FREE(p_fs->vol_utbl); + + p_fs->vol_utbl = NULL; +} /* end of free_upcase_table */ + +/* + * Directory Entry Management Functions + */ + +UINT32 fat_get_entry_type(DENTRY_T *p_entry) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + + if (*(ep->name) == 0x0) + return TYPE_UNUSED; + + else if (*(ep->name) == 0xE5) + return TYPE_DELETED; + + else if (ep->attr == ATTR_EXTEND) + return TYPE_EXTEND; + + else if ((ep->attr & (ATTR_SUBDIR|ATTR_VOLUME)) == ATTR_VOLUME) + return TYPE_VOLUME; + + else if ((ep->attr & (ATTR_SUBDIR|ATTR_VOLUME)) == ATTR_SUBDIR) + return TYPE_DIR; + + return TYPE_FILE; +} /* end of fat_get_entry_type */ + +UINT32 exfat_get_entry_type(DENTRY_T *p_entry) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + + if (ep->type == 0x0) { + return TYPE_UNUSED; + } else if (ep->type < 0x80) { + return TYPE_DELETED; + } else if (ep->type == 0x80) { + return TYPE_INVALID; + } else if (ep->type < 0xA0) { + if (ep->type == 0x81) { + return TYPE_BITMAP; + } else if (ep->type == 0x82) { + return TYPE_UPCASE; + } else if (ep->type == 0x83) { + return TYPE_VOLUME; + } else if (ep->type == 0x85) { + if (GET16_A(ep->attr) & ATTR_SUBDIR) + return TYPE_DIR; + else + return TYPE_FILE; + } + return TYPE_CRITICAL_PRI; + } else if (ep->type < 0xC0) { + if (ep->type == 0xA0) { + return TYPE_GUID; + } else if (ep->type == 0xA1) { + return TYPE_PADDING; + } else if (ep->type == 0xA2) { + return TYPE_ACLTAB; + } + return TYPE_BENIGN_PRI; + } else if (ep->type < 0xE0) { + if (ep->type == 0xC0) { + return TYPE_STREAM; + } else if (ep->type == 0xC1) { + return TYPE_EXTEND; + } else if (ep->type == 0xC2) { + return TYPE_ACL; + } + return TYPE_CRITICAL_SEC; + } + + return TYPE_BENIGN_SEC; +} /* end of exfat_get_entry_type */ + +void fat_set_entry_type(DENTRY_T *p_entry, UINT32 type) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + + if (type == TYPE_UNUSED) + *(ep->name) = 0x0; + + else if (type == TYPE_DELETED) + *(ep->name) = 0xE5; + + else if (type == TYPE_EXTEND) + ep->attr = ATTR_EXTEND; + + else if (type == TYPE_DIR) + ep->attr = ATTR_SUBDIR; + + else if (type == TYPE_FILE) + ep->attr = ATTR_ARCHIVE; + + else if (type == TYPE_SYMLINK) + ep->attr = ATTR_ARCHIVE | ATTR_SYMLINK; +} /* end of fat_set_entry_type */ + +void exfat_set_entry_type(DENTRY_T *p_entry, UINT32 type) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + + if (type == TYPE_UNUSED) { + ep->type = 0x0; + } else if (type == TYPE_DELETED) { + ep->type &= ~0x80; + } else if (type == TYPE_STREAM) { + ep->type = 0xC0; + } else if (type == TYPE_EXTEND) { + ep->type = 0xC1; + } else if (type == TYPE_BITMAP) { + ep->type = 0x81; + } else if (type == TYPE_UPCASE) { + ep->type = 0x82; + } else if (type == TYPE_VOLUME) { + ep->type = 0x83; + } else if (type == TYPE_DIR) { + ep->type = 0x85; + SET16_A(ep->attr, ATTR_SUBDIR); + } else if (type == TYPE_FILE) { + ep->type = 0x85; + SET16_A(ep->attr, ATTR_ARCHIVE); + } else if (type == TYPE_SYMLINK) { + ep->type = 0x85; + SET16_A(ep->attr, ATTR_ARCHIVE | ATTR_SYMLINK); + } +} /* end of exfat_set_entry_type */ + +UINT32 fat_get_entry_attr(DENTRY_T *p_entry) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + return((UINT32) ep->attr); +} /* end of fat_get_entry_attr */ + +UINT32 exfat_get_entry_attr(DENTRY_T *p_entry) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + return((UINT32) GET16_A(ep->attr)); +} /* end of exfat_get_entry_attr */ + +void fat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + ep->attr = (UINT8) attr; +} /* end of fat_set_entry_attr */ + +void exfat_set_entry_attr(DENTRY_T *p_entry, UINT32 attr) +{ + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + SET16_A(ep->attr, (UINT16) attr); +} /* end of exfat_set_entry_attr */ + +UINT8 fat_get_entry_flag(DENTRY_T *p_entry) +{ + return 0x01; +} /* end of fat_get_entry_flag */ + +UINT8 exfat_get_entry_flag(DENTRY_T *p_entry) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + return(ep->flags); +} /* end of exfat_get_entry_flag */ + +void fat_set_entry_flag(DENTRY_T *p_entry, UINT8 flags) +{ +} /* end of fat_set_entry_flag */ + +void exfat_set_entry_flag(DENTRY_T *p_entry, UINT8 flags) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + ep->flags = flags; +} /* end of exfat_set_entry_flag */ + +UINT32 fat_get_entry_clu0(DENTRY_T *p_entry) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + return((GET32_A(ep->start_clu_hi) << 16) | GET16_A(ep->start_clu_lo)); +} /* end of fat_get_entry_clu0 */ + +UINT32 exfat_get_entry_clu0(DENTRY_T *p_entry) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + return(GET32_A(ep->start_clu)); +} /* end of exfat_get_entry_clu0 */ + +void fat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + SET16_A(ep->start_clu_lo, CLUSTER_16(start_clu)); + SET16_A(ep->start_clu_hi, CLUSTER_16(start_clu >> 16)); +} /* end of fat_set_entry_clu0 */ + +void exfat_set_entry_clu0(DENTRY_T *p_entry, UINT32 start_clu) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + SET32_A(ep->start_clu, start_clu); +} /* end of exfat_set_entry_clu0 */ + +UINT64 fat_get_entry_size(DENTRY_T *p_entry) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + return((UINT64) GET32_A(ep->size)); +} /* end of fat_get_entry_size */ + +UINT64 exfat_get_entry_size(DENTRY_T *p_entry) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + return(GET64_A(ep->valid_size)); +} /* end of exfat_get_entry_size */ + +void fat_set_entry_size(DENTRY_T *p_entry, UINT64 size) +{ + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + SET32_A(ep->size, (UINT32) size); +} /* end of fat_set_entry_size */ + +void exfat_set_entry_size(DENTRY_T *p_entry, UINT64 size) +{ + STRM_DENTRY_T *ep = (STRM_DENTRY_T *) p_entry; + SET64_A(ep->valid_size, size); + SET64_A(ep->size, size); +} /* end of exfat_set_entry_size */ + +void fat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode) +{ + UINT16 t = 0x00, d = 0x21; + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + + switch (mode) { + case TM_CREATE: + t = GET16_A(ep->create_time); + d = GET16_A(ep->create_date); + break; + case TM_MODIFY: + t = GET16_A(ep->modify_time); + d = GET16_A(ep->modify_date); + break; + } + + tp->sec = (t & 0x001F) << 1; + tp->min = (t >> 5) & 0x003F; + tp->hour = (t >> 11); + tp->day = (d & 0x001F); + tp->mon = (d >> 5) & 0x000F; + tp->year = (d >> 9); +} /* end of fat_get_entry_time */ + +void exfat_get_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode) +{ + UINT16 t = 0x00, d = 0x21; + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + + switch (mode) { + case TM_CREATE: + t = GET16_A(ep->create_time); + d = GET16_A(ep->create_date); + break; + case TM_MODIFY: + t = GET16_A(ep->modify_time); + d = GET16_A(ep->modify_date); + break; + case TM_ACCESS: + t = GET16_A(ep->access_time); + d = GET16_A(ep->access_date); + break; + } + + tp->sec = (t & 0x001F) << 1; + tp->min = (t >> 5) & 0x003F; + tp->hour = (t >> 11); + tp->day = (d & 0x001F); + tp->mon = (d >> 5) & 0x000F; + tp->year = (d >> 9); +} /* end of exfat_get_entry_time */ + +void fat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode) +{ + UINT16 t, d; + DOS_DENTRY_T *ep = (DOS_DENTRY_T *) p_entry; + + t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1); + d = (tp->year << 9) | (tp->mon << 5) | tp->day; + + switch (mode) { + case TM_CREATE: + SET16_A(ep->create_time, t); + SET16_A(ep->create_date, d); + break; + case TM_MODIFY: + SET16_A(ep->modify_time, t); + SET16_A(ep->modify_date, d); + break; + } +} /* end of fat_set_entry_time */ + +void exfat_set_entry_time(DENTRY_T *p_entry, TIMESTAMP_T *tp, UINT8 mode) +{ + UINT16 t, d; + FILE_DENTRY_T *ep = (FILE_DENTRY_T *) p_entry; + + t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1); + d = (tp->year << 9) | (tp->mon << 5) | tp->day; + + switch (mode) { + case TM_CREATE: + SET16_A(ep->create_time, t); + SET16_A(ep->create_date, d); + break; + case TM_MODIFY: + SET16_A(ep->modify_time, t); + SET16_A(ep->modify_date, d); + break; + case TM_ACCESS: + SET16_A(ep->access_time, t); + SET16_A(ep->access_date, d); + break; + } +} /* end of exfat_set_entry_time */ + +INT32 fat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, + UINT32 start_clu, UINT64 size) +{ + UINT32 sector; + DOS_DENTRY_T *dos_ep; + + dos_ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); + if (!dos_ep) + return FFS_MEDIAERR; + + init_dos_entry(dos_ep, type, start_clu); + buf_modify(sb, sector); + + return FFS_SUCCESS; +} /* end of fat_init_dir_entry */ + +INT32 exfat_init_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, + UINT32 start_clu, UINT64 size) +{ + UINT32 sector; + UINT8 flags; + FILE_DENTRY_T *file_ep; + STRM_DENTRY_T *strm_ep; + + flags = (type == TYPE_FILE) ? 0x01 : 0x03; + + /* we cannot use get_entry_set_in_dir here because file ep is not initialized yet */ + file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); + if (!file_ep) + return FFS_MEDIAERR; + + strm_ep = (STRM_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+1, §or); + if (!strm_ep) + return FFS_MEDIAERR; + + init_file_entry(file_ep, type); + buf_modify(sb, sector); + + init_strm_entry(strm_ep, flags, start_clu, size); + buf_modify(sb, sector); + + return FFS_SUCCESS; +} /* end of exfat_init_dir_entry */ + +INT32 fat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, + UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) +{ + INT32 i; + UINT32 sector; + UINT8 chksum; + UINT16 *uniname = p_uniname->name; + DOS_DENTRY_T *dos_ep; + EXT_DENTRY_T *ext_ep; + + dos_ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); + if (!dos_ep) + return FFS_MEDIAERR; + + dos_ep->lcase = p_dosname->name_case; + MEMCPY(dos_ep->name, p_dosname->name, DOS_NAME_LENGTH); + buf_modify(sb, sector); + + if ((--num_entries) > 0) { + chksum = calc_checksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0); + + for (i = 1; i < num_entries; i++) { + ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, §or); + if (!ext_ep) + return FFS_MEDIAERR; + + init_ext_entry(ext_ep, i, chksum, uniname); + buf_modify(sb, sector); + uniname += 13; + } + + ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry-i, §or); + if (!ext_ep) + return FFS_MEDIAERR; + + init_ext_entry(ext_ep, i+0x40, chksum, uniname); + buf_modify(sb, sector); + } + + return FFS_SUCCESS; +} /* end of fat_init_ext_entry */ + +INT32 exfat_init_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 num_entries, + UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) +{ + INT32 i; + UINT32 sector; + UINT16 *uniname = p_uniname->name; + FILE_DENTRY_T *file_ep; + STRM_DENTRY_T *strm_ep; + NAME_DENTRY_T *name_ep; + + file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); + if (!file_ep) + return FFS_MEDIAERR; + + file_ep->num_ext = (UINT8)(num_entries - 1); + buf_modify(sb, sector); + + strm_ep = (STRM_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+1, §or); + if (!strm_ep) + return FFS_MEDIAERR; + + strm_ep->name_len = p_uniname->name_len; + SET16_A(strm_ep->name_hash, p_uniname->name_hash); + buf_modify(sb, sector); + + for (i = 2; i < num_entries; i++) { + name_ep = (NAME_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry+i, §or); + if (!name_ep) + return FFS_MEDIAERR; + + init_name_entry(name_ep, uniname); + buf_modify(sb, sector); + uniname += 15; + } + + update_dir_checksum(sb, p_dir, entry); + + return FFS_SUCCESS; +} /* end of exfat_init_ext_entry */ + +void init_dos_entry(DOS_DENTRY_T *ep, UINT32 type, UINT32 start_clu) +{ + TIMESTAMP_T tm, *tp; + + fat_set_entry_type((DENTRY_T *) ep, type); + SET16_A(ep->start_clu_lo, CLUSTER_16(start_clu)); + SET16_A(ep->start_clu_hi, CLUSTER_16(start_clu >> 16)); + SET32_A(ep->size, 0); + + tp = tm_current(&tm); + fat_set_entry_time((DENTRY_T *) ep, tp, TM_CREATE); + fat_set_entry_time((DENTRY_T *) ep, tp, TM_MODIFY); + SET16_A(ep->access_date, 0); + ep->create_time_ms = 0; +} /* end of init_dos_entry */ + +void init_ext_entry(EXT_DENTRY_T *ep, INT32 order, UINT8 chksum, UINT16 *uniname) +{ + INT32 i; + UINT8 end = FALSE; + + fat_set_entry_type((DENTRY_T *) ep, TYPE_EXTEND); + ep->order = (UINT8) order; + ep->sysid = 0; + ep->checksum = chksum; + SET16_A(ep->start_clu, 0); + + for (i = 0; i < 10; i += 2) { + if (!end) { + SET16(ep->unicode_0_4+i, *uniname); + if (*uniname == 0x0) + end = TRUE; + else + uniname++; + } else { + SET16(ep->unicode_0_4+i, 0xFFFF); + } + } + + for (i = 0; i < 12; i += 2) { + if (!end) { + SET16_A(ep->unicode_5_10+i, *uniname); + if (*uniname == 0x0) + end = TRUE; + else + uniname++; + } else { + SET16_A(ep->unicode_5_10+i, 0xFFFF); + } + } + + for (i = 0; i < 4; i += 2) { + if (!end) { + SET16_A(ep->unicode_11_12+i, *uniname); + if (*uniname == 0x0) + end = TRUE; + else + uniname++; + } else { + SET16_A(ep->unicode_11_12+i, 0xFFFF); + } + } +} /* end of init_ext_entry */ + +void init_file_entry(FILE_DENTRY_T *ep, UINT32 type) +{ + TIMESTAMP_T tm, *tp; + + exfat_set_entry_type((DENTRY_T *) ep, type); + + tp = tm_current(&tm); + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_CREATE); + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_MODIFY); + exfat_set_entry_time((DENTRY_T *) ep, tp, TM_ACCESS); + ep->create_time_ms = 0; + ep->modify_time_ms = 0; + ep->access_time_ms = 0; +} /* end of init_file_entry */ + +void init_strm_entry(STRM_DENTRY_T *ep, UINT8 flags, UINT32 start_clu, UINT64 size) +{ + exfat_set_entry_type((DENTRY_T *) ep, TYPE_STREAM); + ep->flags = flags; + SET32_A(ep->start_clu, start_clu); + SET64_A(ep->valid_size, size); + SET64_A(ep->size, size); +} /* end of init_strm_entry */ + +void init_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname) +{ + INT32 i; + + exfat_set_entry_type((DENTRY_T *) ep, TYPE_EXTEND); + ep->flags = 0x0; + + for (i = 0; i < 30; i++, i++) { + SET16_A(ep->unicode_0_14+i, *uniname); + if (*uniname == 0x0) + break; + uniname++; + } +} /* end of init_name_entry */ + +void fat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries) +{ + INT32 i; + UINT32 sector; + DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + for (i = num_entries-1; i >= order; i--) { + ep = get_entry_in_dir(sb, p_dir, entry-i, §or); + if (!ep) + return; + + p_fs->fs_func->set_entry_type(ep, TYPE_DELETED); + buf_modify(sb, sector); + } +} /* end of fat_delete_dir_entry */ + +void exfat_delete_dir_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, INT32 order, INT32 num_entries) +{ + INT32 i; + UINT32 sector; + DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + for (i = order; i < num_entries; i++) { + ep = get_entry_in_dir(sb, p_dir, entry+i, §or); + if (!ep) + return; + + p_fs->fs_func->set_entry_type(ep, TYPE_DELETED); + buf_modify(sb, sector); + } +} /* end of exfat_delete_dir_entry */ + +void update_dir_checksum(struct super_block *sb, CHAIN_T *p_dir, INT32 entry) +{ + INT32 i, num_entries; + UINT32 sector; + UINT16 chksum; + FILE_DENTRY_T *file_ep; + DENTRY_T *ep; + + file_ep = (FILE_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, §or); + if (!file_ep) + return; + + buf_lock(sb, sector); + + num_entries = (INT32) file_ep->num_ext + 1; + chksum = calc_checksum_2byte((void *) file_ep, DENTRY_SIZE, 0, CS_DIR_ENTRY); + + for (i = 1; i < num_entries; i++) { + ep = get_entry_in_dir(sb, p_dir, entry+i, NULL); + if (!ep) { + buf_unlock(sb, sector); + return; + } + + chksum = calc_checksum_2byte((void *) ep, DENTRY_SIZE, chksum, CS_DEFAULT); + } + + SET16_A(file_ep->checksum, chksum); + buf_modify(sb, sector); + buf_unlock(sb, sector); +} /* end of update_dir_checksum */ + +void update_dir_checksum_with_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es) +{ + DENTRY_T *ep; + UINT16 chksum = 0; + INT32 chksum_type = CS_DIR_ENTRY, i; + + ep = (DENTRY_T *)&(es->__buf); + for (i=0; i < es->num_entries; i++) { + PRINTK ("update_dir_checksum_with_entry_set ep %p\n", ep); + chksum = calc_checksum_2byte((void *) ep, DENTRY_SIZE, chksum, chksum_type); + ep++; + chksum_type = CS_DEFAULT; + } + + ep = (DENTRY_T *)&(es->__buf); + SET16_A(((FILE_DENTRY_T *)ep)->checksum, chksum); + write_whole_entry_set(sb, es); +} + +static INT32 _walk_fat_chain (struct super_block *sb, CHAIN_T *p_dir, INT32 byte_offset, UINT32 *clu) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + INT32 clu_offset; + UINT32 cur_clu; + + clu_offset = byte_offset >> p_fs->cluster_size_bits; + cur_clu = p_dir->dir; + + if (p_dir->flags == 0x03) { + cur_clu += clu_offset; + } else { + while (clu_offset > 0) { + if (FAT_read(sb, cur_clu, &cur_clu) == -1) + return FFS_MEDIAERR; + clu_offset--; + } + } + + if (clu) + *clu = cur_clu; + return FFS_SUCCESS; +} +INT32 find_location(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector, INT32 *offset) +{ + INT32 off, ret; + UINT32 clu=0; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + off = entry << DENTRY_SIZE_BITS; + + if (p_dir->dir == CLUSTER_32(0)) { /* FAT16 root_dir */ + *offset = off & p_bd->sector_size_mask; + *sector = off >> p_bd->sector_size_bits; + *sector += p_fs->root_start_sector; + } else { + ret =_walk_fat_chain(sb, p_dir, off, &clu); + if (ret != FFS_SUCCESS) + return ret; + + off &= p_fs->cluster_size - 1; /* byte offset in cluster */ + + *offset = off & p_bd->sector_size_mask; /* byte offset in sector */ + *sector = off >> p_bd->sector_size_bits; /* sector offset in cluster */ + *sector += START_SECTOR(clu); + } + return FFS_SUCCESS; +} /* end of find_location */ + +DENTRY_T *get_entry_with_sector(struct super_block *sb, UINT32 sector, INT32 offset) +{ + UINT8 *buf; + + buf = buf_getblk(sb, sector); + + if (buf == NULL) + return NULL; + + return((DENTRY_T *)(buf + offset)); +} /* end of get_entry_with_sector */ + +DENTRY_T *get_entry_in_dir(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 *sector) +{ + INT32 off; + UINT32 sec; + UINT8 *buf; + + if (find_location(sb, p_dir, entry, &sec, &off) != FFS_SUCCESS) + return NULL; + + buf = buf_getblk(sb, sec); + + if (buf == NULL) + return NULL; + + if (sector != NULL) + *sector = sec; + return((DENTRY_T *)(buf + off)); +} /* end of get_entry_in_dir */ + + +/* returns a set of dentries for a file or dir. + * Note that this is a copy (dump) of dentries so that user should call write_entry_set() + * to apply changes made in this entry set to the real device. + * in: + * sb+p_dir+entry: indicates a file/dir + * type: specifies how many dentries should be included. + * out: + * file_ep: will point the first dentry(= file dentry) on success + * return: + * pointer of entry set on success, + * NULL on failure. + */ + +#define ES_MODE_STARTED 0 +#define ES_MODE_GET_FILE_ENTRY 1 +#define ES_MODE_GET_STRM_ENTRY 2 +#define ES_MODE_GET_NAME_ENTRY 3 +#define ES_MODE_GET_CRITICAL_SEC_ENTRY 4 +ENTRY_SET_CACHE_T *get_entry_set_in_dir (struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT32 type, DENTRY_T **file_ep) +{ + INT32 off, ret, byte_offset; + UINT32 clu=0; + UINT32 sec, entry_type; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + ENTRY_SET_CACHE_T *es = NULL; + DENTRY_T *ep, *pos; + UINT8 *buf; + UINT8 num_entries; + INT32 mode = ES_MODE_STARTED; + + PRINTK("get_entry_set_in_dir entered\n"); + PRINTK("p_dir dir %u flags %x size %d\n", p_dir->dir, p_dir->flags, p_dir->size); + + byte_offset = entry << DENTRY_SIZE_BITS; + ret =_walk_fat_chain(sb, p_dir, byte_offset, &clu); + if (ret != FFS_SUCCESS) + return NULL; + + + byte_offset &= p_fs->cluster_size - 1; /* byte offset in cluster */ + + off = byte_offset & p_bd->sector_size_mask; /* byte offset in sector */ + sec = byte_offset >> p_bd->sector_size_bits; /* sector offset in cluster */ + sec += START_SECTOR(clu); + + buf = buf_getblk(sb, sec); + if (buf == NULL) + goto err_out; + + + ep = (DENTRY_T *)(buf + off); + entry_type = p_fs->fs_func->get_entry_type(ep); + + if ((entry_type != TYPE_FILE) + && (entry_type != TYPE_DIR)) + goto err_out; + + if (type == ES_ALL_ENTRIES) + num_entries = ((FILE_DENTRY_T *)ep)->num_ext+1; + else + num_entries = type; + + PRINTK("trying to malloc %x bytes for %d entries\n", offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T), num_entries); + es = MALLOC(offsetof(ENTRY_SET_CACHE_T, __buf) + (num_entries) * sizeof(DENTRY_T)); + if (es == NULL) + goto err_out; + + es->num_entries = num_entries; + es->sector = sec; + es->offset = off; + es->alloc_flag = p_dir->flags; + + pos = (DENTRY_T *) &(es->__buf); + + while(num_entries) { + /* instead of copying whole sector, we will check every entry. + * this will provide minimum stablity and consistancy. + */ + + entry_type = p_fs->fs_func->get_entry_type(ep); + + if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) + goto err_out; + + switch(mode) { + case ES_MODE_STARTED: + if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) + mode = ES_MODE_GET_FILE_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_FILE_ENTRY: + if (entry_type == TYPE_STREAM) + mode = ES_MODE_GET_STRM_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_STRM_ENTRY: + if (entry_type == TYPE_EXTEND) + mode = ES_MODE_GET_NAME_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_NAME_ENTRY: + if (entry_type == TYPE_EXTEND) + break; + else if (entry_type == TYPE_STREAM) + goto err_out; + else if (entry_type & TYPE_CRITICAL_SEC) + mode = ES_MODE_GET_CRITICAL_SEC_ENTRY; + else + goto err_out; + break; + case ES_MODE_GET_CRITICAL_SEC_ENTRY: + if ((entry_type == TYPE_EXTEND) || (entry_type == TYPE_STREAM)) + goto err_out; + else if ((entry_type & TYPE_CRITICAL_SEC) != TYPE_CRITICAL_SEC) + goto err_out; + break; + } + + COPY_DENTRY(pos, ep); + + if (--num_entries == 0) + break; + + if (((off + DENTRY_SIZE) & p_bd->sector_size_mask) < (off & p_bd->sector_size_mask)) { + /* get the next sector */ + if (IS_LAST_SECTOR_IN_CLUSTER(sec)) { + if (es->alloc_flag == 0x03) { + clu++; + } else { + if (FAT_read(sb, clu, &clu) == -1) + goto err_out; + } + sec = START_SECTOR(clu); + } else { + sec++; + } + buf = buf_getblk(sb, sec); + if (buf == NULL) + goto err_out; + off = 0; + ep = (DENTRY_T *)(buf); + } else { + ep++; + off += DENTRY_SIZE; + } + pos++; + } + + if (file_ep) + *file_ep = (DENTRY_T *)&(es->__buf); + + PRINTK("es sec %u offset %d flags %d, num_entries %u buf ptr %p\n", + es->sector, es->offset, es->alloc_flag, es->num_entries, &(es->__buf)); + PRINTK("get_entry_set_in_dir exited %p\n", es); + return es; +err_out: + PRINTK("get_entry_set_in_dir exited NULL (es %p)\n", es); + FREE(es); + return NULL; +} + +void release_entry_set (ENTRY_SET_CACHE_T *es) +{ + PRINTK("release_entry_set %p\n", es); + FREE(es); +} + + +static INT32 __write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, UINT32 sec, INT32 off, UINT32 count) +{ + INT32 num_entries, buf_off = (off - es->offset); + UINT32 remaining_byte_in_sector, copy_entries; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + UINT32 clu; + UINT8 *buf, *esbuf = (UINT8 *)&(es->__buf); + + PRINTK("__write_partial_entries_in_entry_set entered\n"); + PRINTK("es %p sec %u off %d count %d\n", es, sec, off, count); + num_entries = count; + + while(num_entries) { + /* white per sector base */ + remaining_byte_in_sector = (1 << p_bd->sector_size_bits) - off; + copy_entries = MIN(remaining_byte_in_sector>> DENTRY_SIZE_BITS , num_entries); + buf = buf_getblk(sb, sec); + if (buf == NULL) + goto err_out; + PRINTK("es->buf %p buf_off %u\n", esbuf, buf_off); + PRINTK("copying %d entries from %p to sector %u\n", copy_entries, (esbuf + buf_off), sec); + MEMCPY(buf + off, esbuf + buf_off, copy_entries << DENTRY_SIZE_BITS); + buf_modify(sb, sec); + num_entries -= copy_entries; + + if (num_entries) { + /* get next sector */ + if (IS_LAST_SECTOR_IN_CLUSTER(sec)) { + clu = GET_CLUSTER_FROM_SECTOR(sec); + if (es->alloc_flag == 0x03) { + clu++; + } else { + if (FAT_read(sb, clu, &clu) == -1) + goto err_out; + } + sec = START_SECTOR(clu); + } else { + sec++; + } + off = 0; + buf_off += copy_entries << DENTRY_SIZE_BITS; + } + } + + PRINTK("__write_partial_entries_in_entry_set exited successfully\n"); + return FFS_SUCCESS; +err_out: + PRINTK("__write_partial_entries_in_entry_set failed\n"); + return FFS_ERROR; +} + +/* write back all entries in entry set */ +INT32 write_whole_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es) +{ + return (__write_partial_entries_in_entry_set(sb, es, es->sector,es->offset, es->num_entries)); +} + +/* write back some entries in entry set */ +INT32 write_partial_entries_in_entry_set (struct super_block *sb, ENTRY_SET_CACHE_T *es, DENTRY_T *ep, UINT32 count) +{ + INT32 ret, byte_offset, off; + UINT32 clu=0, sec; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + CHAIN_T dir; + + /* vaidity check */ + if (ep + count > ((DENTRY_T *)&(es->__buf)) + es->num_entries) + return FFS_ERROR; + + dir.dir = GET_CLUSTER_FROM_SECTOR(es->sector); + dir.flags = es->alloc_flag; + dir.size = 0xffffffff; /* XXX */ + + byte_offset = (es->sector - START_SECTOR(dir.dir)) << p_bd->sector_size_bits; + byte_offset += ((INT32 *)ep - (INT32 *)&(es->__buf)) + es->offset; + + ret =_walk_fat_chain(sb, &dir, byte_offset, &clu); + if (ret != FFS_SUCCESS) + return ret; + byte_offset &= p_fs->cluster_size - 1; /* byte offset in cluster */ + off = byte_offset & p_bd->sector_size_mask; /* byte offset in sector */ + sec = byte_offset >> p_bd->sector_size_bits; /* sector offset in cluster */ + sec += START_SECTOR(clu); + return (__write_partial_entries_in_entry_set(sb, es, sec, off, count)); +} + +/* search EMPTY CONTINUOUS "num_entries" entries */ +INT32 search_deleted_or_unused_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 num_entries) +{ + INT32 i, dentry, num_empty = 0; + INT32 dentries_per_clu; + UINT32 type; + CHAIN_T clu; + DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + if (p_fs->hint_uentry.dir == p_dir->dir) { + if (p_fs->hint_uentry.entry == -1) + return -1; + + clu.dir = p_fs->hint_uentry.clu.dir; + clu.size = p_fs->hint_uentry.clu.size; + clu.flags = p_fs->hint_uentry.clu.flags; + + dentry = p_fs->hint_uentry.entry; + } else { + p_fs->hint_uentry.entry = -1; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + dentry = 0; + } + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + i = dentry % dentries_per_clu; + else + i = dentry & (dentries_per_clu-1); + + for ( ; i < dentries_per_clu; i++, dentry++) { + ep = get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -1; + + type = p_fs->fs_func->get_entry_type(ep); + + if (type == TYPE_UNUSED) { + num_empty++; + if (p_fs->hint_uentry.entry == -1) { + p_fs->hint_uentry.dir = p_dir->dir; + p_fs->hint_uentry.entry = dentry; + + p_fs->hint_uentry.clu.dir = clu.dir; + p_fs->hint_uentry.clu.size = clu.size; + p_fs->hint_uentry.clu.flags = clu.flags; + } + } else if (type == TYPE_DELETED) { + num_empty++; + } else { + num_empty = 0; + } + + if (num_empty >= num_entries) { + p_fs->hint_uentry.dir = CLUSTER_32(~0); + p_fs->hint_uentry.entry = -1; + + if (p_fs->vol_type == EXFAT) + return(dentry - (num_entries-1)); + else + return(dentry); + } + } + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUSTER_32(~0); + } else { + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return -1; + } + } + + return -1; +} /* end of search_deleted_or_unused_entry */ + +INT32 find_empty_entry(struct inode *inode, CHAIN_T *p_dir, INT32 num_entries) +{ + INT32 ret, dentry; + UINT32 last_clu, sector; + UINT64 size = 0; + CHAIN_T clu; + DENTRY_T *ep = NULL; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + return(search_deleted_or_unused_entry(sb, p_dir, num_entries)); + + while ((dentry = search_deleted_or_unused_entry(sb, p_dir, num_entries)) < 0) { + if (p_fs->dev_ejected) + break; + + if (p_fs->vol_type == EXFAT) { + if (p_dir->dir != p_fs->root_dir) { + size = i_size_read(inode); + } + } + + last_clu = find_last_cluster(sb, p_dir); + clu.dir = last_clu + 1; + clu.size = 0; + clu.flags = p_dir->flags; + + /* (1) allocate a cluster */ + ret = p_fs->fs_func->alloc_cluster(sb, 1, &clu); + if (ret < 1) + return -1; + + if (clear_cluster(sb, clu.dir) != FFS_SUCCESS) + return -1; + + /* (2) append to the FAT chain */ + if (clu.flags != p_dir->flags) { + exfat_chain_cont_cluster(sb, p_dir->dir, p_dir->size); + p_dir->flags = 0x01; + p_fs->hint_uentry.clu.flags = 0x01; + } + if (clu.flags == 0x01) + FAT_write(sb, last_clu, clu.dir); + + if (p_fs->hint_uentry.entry == -1) { + p_fs->hint_uentry.dir = p_dir->dir; + p_fs->hint_uentry.entry = p_dir->size << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); + + p_fs->hint_uentry.clu.dir = clu.dir; + p_fs->hint_uentry.clu.size = 0; + p_fs->hint_uentry.clu.flags = clu.flags; + } + p_fs->hint_uentry.clu.size++; + p_dir->size++; + + /* (3) update the directory entry */ + if (p_fs->vol_type == EXFAT) { + if (p_dir->dir != p_fs->root_dir) { + size += p_fs->cluster_size; + + ep = get_entry_in_dir(sb, &(fid->dir), fid->entry+1, §or); + if (!ep) + return -1; + p_fs->fs_func->set_entry_size(ep, size); + p_fs->fs_func->set_entry_flag(ep, p_dir->flags); + buf_modify(sb, sector); + + update_dir_checksum(sb, &(fid->dir), fid->entry); + } + } + + i_size_write(inode, i_size_read(inode)+p_fs->cluster_size); + EXFAT_I(inode)->mmu_private += p_fs->cluster_size; + EXFAT_I(inode)->fid.size += p_fs->cluster_size; + EXFAT_I(inode)->fid.flags = p_dir->flags; + inode->i_blocks += 1 << (p_fs->cluster_size_bits - 9); + } + + return(dentry); +} /* end of find_empty_entry */ + +/* return values of fat_find_dir_entry() + >= 0 : return dir entiry position with the name in dir + -1 : (root dir, ".") it is the root dir itself + -2 : entry with the name does not exist */ +INT32 fat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type) +{ + INT32 i, dentry = 0, len; + INT32 order = 0, is_feasible_entry = TRUE, has_ext_entry = FALSE; + INT32 dentries_per_clu; + UINT32 entry_type; + UINT16 entry_uniname[14], *uniname = NULL, unichar; + CHAIN_T clu; + DENTRY_T *ep; + DOS_DENTRY_T *dos_ep; + EXT_DENTRY_T *ext_ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_dir->dir == p_fs->root_dir) { + if ((!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_CUR_DIR_NAME)) || + (!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_PAR_DIR_NAME))) + return -1; // special case, root directory itself + } + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.flags = p_dir->flags; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + for (i = 0; i < dentries_per_clu; i++, dentry++) { + ep = get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -2; + + entry_type = p_fs->fs_func->get_entry_type(ep); + + if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) { + if ((type == TYPE_ALL) || (type == entry_type)) { + if (is_feasible_entry && has_ext_entry) + return(dentry); + + dos_ep = (DOS_DENTRY_T *) ep; + if (!nls_dosname_cmp(sb, p_dosname->name, dos_ep->name)) + return(dentry); + } + is_feasible_entry = TRUE; + has_ext_entry = FALSE; + } else if (entry_type == TYPE_EXTEND) { + if (is_feasible_entry) { + ext_ep = (EXT_DENTRY_T *) ep; + if (ext_ep->order > 0x40) { + order = (INT32)(ext_ep->order - 0x40); + uniname = p_uniname->name + 13 * (order-1); + } else { + order = (INT32) ext_ep->order; + uniname -= 13; + } + + len = extract_uni_name_from_ext_entry(ext_ep, entry_uniname, order); + + unichar = *(uniname+len); + *(uniname+len) = 0x0; + + if (nls_uniname_cmp(sb, uniname, entry_uniname)) { + is_feasible_entry = FALSE; + } + + *(uniname+len) = unichar; + } + has_ext_entry = TRUE; + } else if (entry_type == TYPE_UNUSED) { + return -2; + } else { + is_feasible_entry = TRUE; + has_ext_entry = FALSE; + } + } + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return -2; + } + + return -2; +} /* end of fat_find_dir_entry */ + +/* return values of exfat_find_dir_entry() + >= 0 : return dir entiry position with the name in dir + -1 : (root dir, ".") it is the root dir itself + -2 : entry with the name does not exist */ +INT32 exfat_find_dir_entry(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 num_entries, DOS_NAME_T *p_dosname, UINT32 type) +{ + INT32 i, dentry = 0, num_ext_entries = 0, len; + INT32 order = 0, is_feasible_entry = FALSE; + INT32 dentries_per_clu, num_empty = 0; + UINT32 entry_type; + UINT16 entry_uniname[16], *uniname = NULL, unichar; + CHAIN_T clu; + DENTRY_T *ep; + FILE_DENTRY_T *file_ep; + STRM_DENTRY_T *strm_ep; + NAME_DENTRY_T *name_ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_dir->dir == p_fs->root_dir) { + if ((!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_CUR_DIR_NAME)) || + (!nls_uniname_cmp(sb, p_uniname->name, (UINT16 *) UNI_PAR_DIR_NAME))) + return -1; // special case, root directory itself + } + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + p_fs->hint_uentry.dir = p_dir->dir; + p_fs->hint_uentry.entry = -1; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + for (i = 0; i < dentries_per_clu; i++, dentry++) { + ep = get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -2; + + entry_type = p_fs->fs_func->get_entry_type(ep); + + if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) { + is_feasible_entry = FALSE; + + if (p_fs->hint_uentry.entry == -1) { + num_empty++; + + if (num_empty == 1) { + p_fs->hint_uentry.clu.dir = clu.dir; + p_fs->hint_uentry.clu.size = clu.size; + p_fs->hint_uentry.clu.flags = clu.flags; + } + if ((num_empty >= num_entries) || (entry_type == TYPE_UNUSED)) { + p_fs->hint_uentry.entry = dentry - (num_empty-1); + } + } + + if (entry_type == TYPE_UNUSED) { + return -2; + } + } else { + num_empty = 0; + + if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) { + if ((type == TYPE_ALL) || (type == entry_type)) { + file_ep = (FILE_DENTRY_T *) ep; + num_ext_entries = file_ep->num_ext; + is_feasible_entry = TRUE; + } else { + is_feasible_entry = FALSE; + } + } else if (entry_type == TYPE_STREAM) { + if (is_feasible_entry) { + strm_ep = (STRM_DENTRY_T *) ep; + if (p_uniname->name_len == strm_ep->name_len) { + order = 1; + } else { + is_feasible_entry = FALSE; + } + } + } else if (entry_type == TYPE_EXTEND) { + if (is_feasible_entry) { + name_ep = (NAME_DENTRY_T *) ep; + + if ((++order) == 2) + uniname = p_uniname->name; + else + uniname += 15; + + len = extract_uni_name_from_name_entry(name_ep, entry_uniname, order); + + unichar = *(uniname+len); + *(uniname+len) = 0x0; + + if (nls_uniname_cmp(sb, uniname, entry_uniname)) { + is_feasible_entry = FALSE; + } else if (order == num_ext_entries) { + p_fs->hint_uentry.dir = CLUSTER_32(~0); + p_fs->hint_uentry.entry = -1; + return(dentry - (num_ext_entries)); + } + + *(uniname+len) = unichar; + } + } else { + is_feasible_entry = FALSE; + } + } + } + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUSTER_32(~0); + } else { + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return -2; + } + } + + return -2; +} /* end of exfat_find_dir_entry */ + +/* returns -1 on error */ +INT32 fat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry) +{ + INT32 count = 0; + UINT8 chksum; + DOS_DENTRY_T *dos_ep = (DOS_DENTRY_T *) p_entry; + EXT_DENTRY_T *ext_ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + chksum = calc_checksum_1byte((void *) dos_ep->name, DOS_NAME_LENGTH, 0); + + for (entry--; entry >= 0; entry--) { + ext_ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, NULL); + if (!ext_ep) + return -1; + + if ((p_fs->fs_func->get_entry_type((DENTRY_T *) ext_ep) == TYPE_EXTEND) && + (ext_ep->checksum == chksum)) { + count++; + if (ext_ep->order > 0x40) + return(count); + } else { + return(count); + } + } + + return(count); +} /* end of fat_count_ext_entries */ + +/* returns -1 on error */ +INT32 exfat_count_ext_entries(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, DENTRY_T *p_entry) +{ + INT32 i, count = 0; + UINT32 type; + FILE_DENTRY_T *file_ep = (FILE_DENTRY_T *) p_entry; + DENTRY_T *ext_ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + for (i = 0, entry++; i < file_ep->num_ext; i++, entry++) { + ext_ep = get_entry_in_dir(sb, p_dir, entry, NULL); + if (!ext_ep) + return -1; + + type = p_fs->fs_func->get_entry_type(ext_ep); + if ((type == TYPE_EXTEND) || (type == TYPE_STREAM)) { + count++; + } else { + return(count); + } + } + + return(count); +} /* end of exfat_count_ext_entries */ + +/* returns -1 on error */ +INT32 count_dos_name_entries(struct super_block *sb, CHAIN_T *p_dir, UINT32 type) +{ + INT32 i, count = 0; + INT32 dentries_per_clu; + UINT32 entry_type; + CHAIN_T clu; + DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + for (i = 0; i < dentries_per_clu; i++) { + ep = get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return -1; + + entry_type = p_fs->fs_func->get_entry_type(ep); + + if (entry_type == TYPE_UNUSED) + return(count); + if (!(type & TYPE_CRITICAL_PRI) && !(type & TYPE_BENIGN_PRI)) + continue; + + if ((type == TYPE_ALL) || (type == entry_type)) + count++; + } + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUSTER_32(~0); + } else { + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return -1; + } + } + + return(count); +} /* end of count_dos_name_entries */ + +BOOL is_dir_empty(struct super_block *sb, CHAIN_T *p_dir) +{ + INT32 i, count = 0; + INT32 dentries_per_clu; + UINT32 type; + CHAIN_T clu; + DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.size = p_dir->size; + clu.flags = p_dir->flags; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + for (i = 0; i < dentries_per_clu; i++) { + ep = get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + break; + + type = p_fs->fs_func->get_entry_type(ep); + + if (type == TYPE_UNUSED) + return TRUE; + if ((type != TYPE_FILE) && (type != TYPE_DIR)) + continue; + + if (p_dir->dir == CLUSTER_32(0)) { /* FAT16 root_dir */ + return FALSE; + } else { + if (p_fs->vol_type == EXFAT) + return FALSE; + if ((p_dir->dir == p_fs->root_dir) || ((++count) > 2)) + return FALSE; + } + } + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (clu.flags == 0x03) { + if ((--clu.size) > 0) + clu.dir++; + else + clu.dir = CLUSTER_32(~0); + } else { + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + break; + } + } + + return TRUE; +} /* end of is_dir_empty */ + +/* + * Name Conversion Functions + */ + +/* input : dir, uni_name + output : num_of_entry, dos_name(format : aaaaaa~1.bbb) */ +INT32 get_num_entries_and_dos_name(struct super_block *sb, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, INT32 *entries, DOS_NAME_T *p_dosname) +{ + INT32 ret, num_entries, lossy = FALSE; + INT8 **r; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + num_entries = p_fs->fs_func->calc_num_entries(p_uniname); + if (num_entries == 0) + return FFS_INVALIDPATH; + + if (p_fs->vol_type != EXFAT) { + nls_uniname_to_dosname(sb, p_dosname, p_uniname, &lossy); + + if (lossy) { + ret = fat_generate_dos_name(sb, p_dir, p_dosname); + if (ret) + return ret; + } else { + for (r = reserved_names; *r; r++) { + if (!STRNCMP((void *) p_dosname->name, *r, 8)) + return FFS_INVALIDPATH; + } + + if (p_dosname->name_case != 0xFF) + num_entries = 1; + } + + if (num_entries > 1) + p_dosname->name_case = 0x0; + } + + *entries = num_entries; + + return FFS_SUCCESS; +} /* end of get_num_entries_and_dos_name */ + +void get_uni_name_from_dos_entry(struct super_block *sb, DOS_DENTRY_T *ep, UNI_NAME_T *p_uniname, UINT8 mode) +{ + DOS_NAME_T dos_name; + + if (mode == 0x0) + dos_name.name_case = 0x0; + else + dos_name.name_case = ep->lcase; + + MEMCPY(dos_name.name, ep->name, DOS_NAME_LENGTH); + nls_dosname_to_uniname(sb, p_uniname, &dos_name); +} /* end of get_uni_name_from_dos_entry */ + +void fat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname) +{ + INT32 i; + EXT_DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + for (entry--, i = 1; entry >= 0; entry--, i++) { + ep = (EXT_DENTRY_T *) get_entry_in_dir(sb, p_dir, entry, NULL); + if (!ep) + return; + + if (p_fs->fs_func->get_entry_type((DENTRY_T *) ep) == TYPE_EXTEND) { + extract_uni_name_from_ext_entry(ep, uniname, i); + if (ep->order > 0x40) + return; + } else { + return; + } + + uniname += 13; + } +} /* end of fat_get_uni_name_from_ext_entry */ + +void exfat_get_uni_name_from_ext_entry(struct super_block *sb, CHAIN_T *p_dir, INT32 entry, UINT16 *uniname) +{ + INT32 i; + DENTRY_T *ep; + ENTRY_SET_CACHE_T *es; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + es = get_entry_set_in_dir(sb, p_dir, entry, ES_ALL_ENTRIES, &ep); + if (es == NULL || es->num_entries < 3) { + if(es) { + release_entry_set(es); + } + return; + } + + ep += 2; + + /* + * First entry : file entry + * Second entry : stream-extension entry + * Third entry : first file-name entry + * So, the index of first file-name dentry should start from 2. + */ + for (i = 2; i < es->num_entries; i++, ep++) { + if (p_fs->fs_func->get_entry_type(ep) == TYPE_EXTEND) { + extract_uni_name_from_name_entry((NAME_DENTRY_T *)ep, uniname, i); + } else { + /* end of name entry */ + goto out; + } + uniname += 15; + } + +out: + release_entry_set(es); +} /* end of exfat_get_uni_name_from_ext_entry */ + +INT32 extract_uni_name_from_ext_entry(EXT_DENTRY_T *ep, UINT16 *uniname, INT32 order) +{ + INT32 i, len = 0; + + for (i = 0; i < 10; i += 2) { + *uniname = GET16(ep->unicode_0_4+i); + if (*uniname == 0x0) + return(len); + uniname++; + len++; + } + + if (order < 20) { + for (i = 0; i < 12; i += 2) { + *uniname = GET16_A(ep->unicode_5_10+i); + if (*uniname == 0x0) + return(len); + uniname++; + len++; + } + } else { + for (i = 0; i < 8; i += 2) { + *uniname = GET16_A(ep->unicode_5_10+i); + if (*uniname == 0x0) + return(len); + uniname++; + len++; + } + *uniname = 0x0; /* uniname[MAX_NAME_LENGTH-1] */ + return(len); + } + + for (i = 0; i < 4; i += 2) { + *uniname = GET16_A(ep->unicode_11_12+i); + if (*uniname == 0x0) + return(len); + uniname++; + len++; + } + + *uniname = 0x0; + return(len); + +} /* end of extract_uni_name_from_ext_entry */ + +INT32 extract_uni_name_from_name_entry(NAME_DENTRY_T *ep, UINT16 *uniname, INT32 order) +{ + INT32 i, len = 0; + + for (i = 0; i < 30; i += 2) { + *uniname = GET16_A(ep->unicode_0_14+i); + if (*uniname == 0x0) + return(len); + uniname++; + len++; + } + + *uniname = 0x0; + return(len); + +} /* end of extract_uni_name_from_name_entry */ + +INT32 fat_generate_dos_name(struct super_block *sb, CHAIN_T *p_dir, DOS_NAME_T *p_dosname) +{ + INT32 i, j, count = 0, count_begin = FALSE; + INT32 dentries_per_clu; + UINT32 type; + UINT8 bmap[128/* 1 ~ 1023 */]; + CHAIN_T clu; + DOS_DENTRY_T *ep; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + Bitmap_clear_all(bmap, 128); + Bitmap_set(bmap, 0); + + if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */ + dentries_per_clu = p_fs->dentries_in_root; + else + dentries_per_clu = p_fs->dentries_per_clu; + + clu.dir = p_dir->dir; + clu.flags = p_dir->flags; + + while (clu.dir != CLUSTER_32(~0)) { + if (p_fs->dev_ejected) + break; + + for (i = 0; i < dentries_per_clu; i++) { + ep = (DOS_DENTRY_T *) get_entry_in_dir(sb, &clu, i, NULL); + if (!ep) + return FFS_MEDIAERR; + + type = p_fs->fs_func->get_entry_type((DENTRY_T *) ep); + + if (type == TYPE_UNUSED) + break; + if ((type != TYPE_FILE) && (type != TYPE_DIR)) + continue; + + count = 0; + count_begin = FALSE; + + for (j = 0; j < 8; j++) { + if (ep->name[j] == ' ') + break; + + if (ep->name[j] == '~') { + count_begin = TRUE; + } else if (count_begin) { + if ((ep->name[j] >= '0') && (ep->name[j] <= '9')) { + count = count * 10 + (ep->name[j] - '0'); + } else { + count = 0; + count_begin = FALSE; + } + } + } + + if ((count > 0) && (count < 1024)) + Bitmap_set(bmap, count); + } + + if (p_dir->dir == CLUSTER_32(0)) + break; /* FAT16 root_dir */ + + if (FAT_read(sb, clu.dir, &(clu.dir)) != 0) + return FFS_MEDIAERR; + } + + count = 0; + for (i = 0; i < 128; i++) { + if (bmap[i] != 0xFF) { + for (j = 0; j < 8; j++) { + if (Bitmap_test(&(bmap[i]), j) == 0) { + count = (i << 3) + j; + break; + } + } + if (count != 0) + break; + } + } + + if ((count == 0) || (count >= 1024)) + return FFS_FILEEXIST; + else + fat_attach_count_to_dos_name(p_dosname->name, count); + + /* Now dos_name has DOS~????.EXT */ + return FFS_SUCCESS; +} /* end of generate_dos_name */ + +void fat_attach_count_to_dos_name(UINT8 *dosname, INT32 count) +{ + INT32 i, j, length; + INT8 str_count[6]; + + str_count[0] = '~'; + str_count[1] = '\0'; + my_itoa(&(str_count[1]), count); + length = STRLEN(str_count); + + i = j = 0; + while (j <= (8 - length)) { + i = j; + if (dosname[j] == ' ') + break; + if (dosname[j] & 0x80) + j += 2; + else + j++; + } + + for (j = 0; j < length; i++, j++) + dosname[i] = (UINT8) str_count[j]; + + if (i == 7) + dosname[7] = ' '; + +} /* end of attach_count_to_dos_name */ + +INT32 fat_calc_num_entries(UNI_NAME_T *p_uniname) +{ + INT32 len; + + len = p_uniname->name_len; + if (len == 0) + return 0; + + /* 1 dos name entry + extended entries */ + return((len-1) / 13 + 2); + +} /* end of calc_num_enties */ + +INT32 exfat_calc_num_entries(UNI_NAME_T *p_uniname) +{ + INT32 len; + + len = p_uniname->name_len; + if (len == 0) + return 0; + + /* 1 file entry + 1 stream entry + name entries */ + return((len-1) / 15 + 3); + +} /* end of exfat_calc_num_enties */ + +UINT8 calc_checksum_1byte(void *data, INT32 len, UINT8 chksum) +{ + INT32 i; + UINT8 *c = (UINT8 *) data; + + for (i = 0; i < len; i++, c++) + chksum = (((chksum & 1) << 7) | ((chksum & 0xFE) >> 1)) + *c; + + return(chksum); +} /* end of calc_checksum_1byte */ + +UINT16 calc_checksum_2byte(void *data, INT32 len, UINT16 chksum, INT32 type) +{ + INT32 i; + UINT8 *c = (UINT8 *) data; + + switch (type) { + case CS_DIR_ENTRY: + for (i = 0; i < len; i++, c++) { + if ((i == 2) || (i == 3)) + continue; + chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (UINT16) *c; + } + break; + default + : + for (i = 0; i < len; i++, c++) { + chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + (UINT16) *c; + } + } + + return(chksum); +} /* end of calc_checksum_2byte */ + +UINT32 calc_checksum_4byte(void *data, INT32 len, UINT32 chksum, INT32 type) +{ + INT32 i; + UINT8 *c = (UINT8 *) data; + + switch (type) { + case CS_PBR_SECTOR: + for (i = 0; i < len; i++, c++) { + if ((i == 106) || (i == 107) || (i == 112)) + continue; + chksum = (((chksum & 1) << 31) | ((chksum & 0xFFFFFFFE) >> 1)) + (UINT32) *c; + } + break; + default + : + for (i = 0; i < len; i++, c++) { + chksum = (((chksum & 1) << 31) | ((chksum & 0xFFFFFFFE) >> 1)) + (UINT32) *c; + } + } + + return(chksum); +} /* end of calc_checksum_4byte */ + +/* + * Name Resolution Functions + */ + +/* return values of resolve_path() + > 0 : return the length of the path + < 0 : return error */ +INT32 resolve_path(struct inode *inode, UINT8 *path, CHAIN_T *p_dir, UNI_NAME_T *p_uniname) +{ + INT32 lossy = FALSE; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + + if (STRLEN(path) >= (MAX_NAME_LENGTH * MAX_CHARSET_SIZE)) + return(FFS_INVALIDPATH); + + STRCPY(name_buf, path); + + nls_cstring_to_uniname(sb, p_uniname, name_buf, &lossy); + if (lossy) + return(FFS_INVALIDPATH); + + fid->size = i_size_read(inode); + + p_dir->dir = fid->start_clu; + p_dir->size = (INT32)(fid->size >> p_fs->cluster_size_bits); + p_dir->flags = fid->flags; + + return(FFS_SUCCESS); +} + +/* + * File Operation Functions + */ +static FS_FUNC_T fat_fs_func = { + .alloc_cluster = fat_alloc_cluster, + .free_cluster = fat_free_cluster, + .count_used_clusters = fat_count_used_clusters, + + .init_dir_entry = fat_init_dir_entry, + .init_ext_entry = fat_init_ext_entry, + .find_dir_entry = fat_find_dir_entry, + .delete_dir_entry = fat_delete_dir_entry, + .get_uni_name_from_ext_entry = fat_get_uni_name_from_ext_entry, + .count_ext_entries = fat_count_ext_entries, + .calc_num_entries = fat_calc_num_entries, + + .get_entry_type = fat_get_entry_type, + .set_entry_type = fat_set_entry_type, + .get_entry_attr = fat_get_entry_attr, + .set_entry_attr = fat_set_entry_attr, + .get_entry_flag = fat_get_entry_flag, + .set_entry_flag = fat_set_entry_flag, + .get_entry_clu0 = fat_get_entry_clu0, + .set_entry_clu0 = fat_set_entry_clu0, + .get_entry_size = fat_get_entry_size, + .set_entry_size = fat_set_entry_size, + .get_entry_time = fat_get_entry_time, + .set_entry_time = fat_set_entry_time, +}; + + +INT32 fat16_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) +{ + INT32 num_reserved, num_root_sectors; + BPB16_T *p_bpb = (BPB16_T *) p_pbr->bpb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_bpb->num_fats == 0) + return FFS_FORMATERR; + + num_root_sectors = GET16(p_bpb->num_root_entries) << DENTRY_SIZE_BITS; + num_root_sectors = ((num_root_sectors-1) >> p_bd->sector_size_bits) + 1; + + p_fs->sectors_per_clu = p_bpb->sectors_per_clu; + p_fs->sectors_per_clu_bits = my_log2(p_bpb->sectors_per_clu); + p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits; + p_fs->cluster_size = 1 << p_fs->cluster_size_bits; + + p_fs->num_FAT_sectors = GET16(p_bpb->num_fat_sectors); + + p_fs->FAT1_start_sector = p_fs->PBR_sector + GET16(p_bpb->num_reserved); + if (p_bpb->num_fats == 1) + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector; + else + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors; + + p_fs->root_start_sector = p_fs->FAT2_start_sector + p_fs->num_FAT_sectors; + p_fs->data_start_sector = p_fs->root_start_sector + num_root_sectors; + + p_fs->num_sectors = GET16(p_bpb->num_sectors); + if (p_fs->num_sectors == 0) + p_fs->num_sectors = GET32(p_bpb->num_huge_sectors); + + num_reserved = p_fs->data_start_sector - p_fs->PBR_sector; + p_fs->num_clusters = ((p_fs->num_sectors - num_reserved) >> p_fs->sectors_per_clu_bits) + 2; + /* because the cluster index starts with 2 */ + + if (p_fs->num_clusters < FAT12_THRESHOLD) + p_fs->vol_type = FAT12; + else + p_fs->vol_type = FAT16; + p_fs->vol_id = GET32(p_bpb->vol_serial); + + p_fs->root_dir = 0; + p_fs->dentries_in_root = GET16(p_bpb->num_root_entries); + p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); + + p_fs->vol_flag = VOL_CLEAN; + p_fs->clu_srch_ptr = 2; + p_fs->used_clusters = (UINT32) ~0; + + p_fs->fs_func = &fat_fs_func; + + return FFS_SUCCESS; +} /* end of fat16_mount */ + +INT32 fat32_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) +{ + INT32 num_reserved; + BPB32_T *p_bpb = (BPB32_T *) p_pbr->bpb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_bpb->num_fats == 0) + return FFS_FORMATERR; + + p_fs->sectors_per_clu = p_bpb->sectors_per_clu; + p_fs->sectors_per_clu_bits = my_log2(p_bpb->sectors_per_clu); + p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits; + p_fs->cluster_size = 1 << p_fs->cluster_size_bits; + + p_fs->num_FAT_sectors = GET32(p_bpb->num_fat32_sectors); + + p_fs->FAT1_start_sector = p_fs->PBR_sector + GET16(p_bpb->num_reserved); + if (p_bpb->num_fats == 1) + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector; + else + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors; + + p_fs->root_start_sector = p_fs->FAT2_start_sector + p_fs->num_FAT_sectors; + p_fs->data_start_sector = p_fs->root_start_sector; + + p_fs->num_sectors = GET32(p_bpb->num_huge_sectors); + num_reserved = p_fs->data_start_sector - p_fs->PBR_sector; + + p_fs->num_clusters = ((p_fs->num_sectors-num_reserved) >> p_fs->sectors_per_clu_bits) + 2; + /* because the cluster index starts with 2 */ + + p_fs->vol_type = FAT32; + p_fs->vol_id = GET32(p_bpb->vol_serial); + + p_fs->root_dir = GET32(p_bpb->root_cluster); + p_fs->dentries_in_root = 0; + p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); + + p_fs->vol_flag = VOL_CLEAN; + p_fs->clu_srch_ptr = 2; + p_fs->used_clusters = (UINT32) ~0; + + p_fs->fs_func = &fat_fs_func; + + return FFS_SUCCESS; +} /* end of fat32_mount */ + +static FS_FUNC_T exfat_fs_func = { + .alloc_cluster = exfat_alloc_cluster, + .free_cluster = exfat_free_cluster, + .count_used_clusters = exfat_count_used_clusters, + + .init_dir_entry = exfat_init_dir_entry, + .init_ext_entry = exfat_init_ext_entry, + .find_dir_entry = exfat_find_dir_entry, + .delete_dir_entry = exfat_delete_dir_entry, + .get_uni_name_from_ext_entry = exfat_get_uni_name_from_ext_entry, + .count_ext_entries = exfat_count_ext_entries, + .calc_num_entries = exfat_calc_num_entries, + + .get_entry_type = exfat_get_entry_type, + .set_entry_type = exfat_set_entry_type, + .get_entry_attr = exfat_get_entry_attr, + .set_entry_attr = exfat_set_entry_attr, + .get_entry_flag = exfat_get_entry_flag, + .set_entry_flag = exfat_set_entry_flag, + .get_entry_clu0 = exfat_get_entry_clu0, + .set_entry_clu0 = exfat_set_entry_clu0, + .get_entry_size = exfat_get_entry_size, + .set_entry_size = exfat_set_entry_size, + .get_entry_time = exfat_get_entry_time, + .set_entry_time = exfat_set_entry_time, +}; + +INT32 exfat_mount(struct super_block *sb, PBR_SECTOR_T *p_pbr) +{ + BPBEX_T *p_bpb = (BPBEX_T *) p_pbr->bpb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + + if (p_bpb->num_fats == 0) + return FFS_FORMATERR; + + p_fs->sectors_per_clu = 1 << p_bpb->sectors_per_clu_bits; + p_fs->sectors_per_clu_bits = p_bpb->sectors_per_clu_bits; + p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits + p_bd->sector_size_bits; + p_fs->cluster_size = 1 << p_fs->cluster_size_bits; + + p_fs->num_FAT_sectors = GET32(p_bpb->fat_length); + + p_fs->FAT1_start_sector = p_fs->PBR_sector + GET32(p_bpb->fat_offset); + if (p_bpb->num_fats == 1) + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector; + else + p_fs->FAT2_start_sector = p_fs->FAT1_start_sector + p_fs->num_FAT_sectors; + + p_fs->root_start_sector = p_fs->PBR_sector + GET32(p_bpb->clu_offset); + p_fs->data_start_sector = p_fs->root_start_sector; + + p_fs->num_sectors = GET64(p_bpb->vol_length); + p_fs->num_clusters = GET32(p_bpb->clu_count) + 2; + /* because the cluster index starts with 2 */ + + p_fs->vol_type = EXFAT; + p_fs->vol_id = GET32(p_bpb->vol_serial); + + p_fs->root_dir = GET32(p_bpb->root_cluster); + p_fs->dentries_in_root = 0; + p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS); + + p_fs->vol_flag = (UINT32) GET16(p_bpb->vol_flags); + p_fs->clu_srch_ptr = 2; + p_fs->used_clusters = (UINT32) ~0; + + p_fs->fs_func = &exfat_fs_func; + + return FFS_SUCCESS; +} /* end of exfat_mount */ + +INT32 create_dir(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, FILE_ID_T *fid) +{ + INT32 ret, dentry, num_entries; + UINT64 size; + CHAIN_T clu; + DOS_NAME_T dos_name, dot_name; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name); + if (ret) + return ret; + + /* find_empty_entry must be called before alloc_cluster */ + dentry = find_empty_entry(inode, p_dir, num_entries); + if (dentry < 0) + return FFS_FULL; + + clu.dir = CLUSTER_32(~0); + clu.size = 0; + clu.flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + + /* (1) allocate a cluster */ + ret = p_fs->fs_func->alloc_cluster(sb, 1, &clu); + if (ret < 1) + return FFS_FULL; + + ret = clear_cluster(sb, clu.dir); + if (ret != FFS_SUCCESS) + return ret; + + if (p_fs->vol_type == EXFAT) { + size = p_fs->cluster_size; + } else { + size = 0; + + /* initialize the . and .. entry + Information for . points to itself + Information for .. points to parent dir */ + + dot_name.name_case = 0x0; + MEMCPY(dot_name.name, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH); + + ret = p_fs->fs_func->init_dir_entry(sb, &clu, 0, TYPE_DIR, clu.dir, 0); + if (ret != FFS_SUCCESS) + return ret; + + ret = p_fs->fs_func->init_ext_entry(sb, &clu, 0, 1, NULL, &dot_name); + if (ret != FFS_SUCCESS) + return ret; + + MEMCPY(dot_name.name, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH); + + if (p_dir->dir == p_fs->root_dir) + ret = p_fs->fs_func->init_dir_entry(sb, &clu, 1, TYPE_DIR, CLUSTER_32(0), 0); + else + ret = p_fs->fs_func->init_dir_entry(sb, &clu, 1, TYPE_DIR, p_dir->dir, 0); + + if (ret != FFS_SUCCESS) + return ret; + + ret = p_fs->fs_func->init_ext_entry(sb, &clu, 1, 1, NULL, &dot_name); + if (ret != FFS_SUCCESS) + return ret; + } + + /* (2) update the directory entry */ + /* make sub-dir entry in parent directory */ + ret = p_fs->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_DIR, clu.dir, size); + if (ret != FFS_SUCCESS) + return ret; + + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + fid->dir.dir = p_dir->dir; + fid->dir.size = p_dir->size; + fid->dir.flags = p_dir->flags; + fid->entry = dentry; + + fid->attr = ATTR_SUBDIR; + fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->size = size; + fid->start_clu = clu.dir; + + fid->type= TYPE_DIR; + fid->rwoffset = 0; + fid->hint_last_off = -1; + + return FFS_SUCCESS; +} /* end of create_dir */ + +INT32 create_file(struct inode *inode, CHAIN_T *p_dir, UNI_NAME_T *p_uniname, UINT8 mode, FILE_ID_T *fid) +{ + INT32 ret, dentry, num_entries; + DOS_NAME_T dos_name; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries, &dos_name); + if (ret) + return ret; + + /* find_empty_entry must be called before alloc_cluster() */ + dentry = find_empty_entry(inode, p_dir, num_entries); + if (dentry < 0) + return FFS_FULL; + + /* (1) update the directory entry */ + /* fill the dos name directory entry information of the created file. + the first cluster is not determined yet. (0) */ + ret = p_fs->fs_func->init_dir_entry(sb, p_dir, dentry, TYPE_FILE | mode, CLUSTER_32(0), 0); + if (ret != FFS_SUCCESS) + return ret; + + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + fid->dir.dir = p_dir->dir; + fid->dir.size = p_dir->size; + fid->dir.flags = p_dir->flags; + fid->entry = dentry; + + fid->attr = ATTR_ARCHIVE | mode; + fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01; + fid->size = 0; + fid->start_clu = CLUSTER_32(~0); + + fid->type= TYPE_FILE; + fid->rwoffset = 0; + fid->hint_last_off = -1; + + return FFS_SUCCESS; +} /* end of create_file */ + +void remove_file(struct inode *inode, CHAIN_T *p_dir, INT32 entry) +{ + INT32 num_entries; + UINT32 sector; + DENTRY_T *ep; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + ep = get_entry_in_dir(sb, p_dir, entry, §or); + if (!ep) + return; + + buf_lock(sb, sector); + + /* buf_lock() before call count_ext_entries() */ + num_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, entry, ep); + if (num_entries < 0) { + buf_unlock(sb, sector); + return; + } + num_entries++; + + buf_unlock(sb, sector); + + /* (1) update the directory entry */ + p_fs->fs_func->delete_dir_entry(sb, p_dir, entry, 0, num_entries); +} /* end of remove_file */ + +INT32 rename_file(struct inode *inode, CHAIN_T *p_dir, INT32 oldentry, UNI_NAME_T *p_uniname, FILE_ID_T *fid) +{ + INT32 ret, newentry = -1, num_old_entries, num_new_entries; + UINT32 sector_old, sector_new; + DOS_NAME_T dos_name; + DENTRY_T *epold, *epnew; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + epold = get_entry_in_dir(sb, p_dir, oldentry, §or_old); + if (!epold) + return FFS_MEDIAERR; + + buf_lock(sb, sector_old); + + /* buf_lock() before call count_ext_entries() */ + num_old_entries = p_fs->fs_func->count_ext_entries(sb, p_dir, oldentry, epold); + if (num_old_entries < 0) { + buf_unlock(sb, sector_old); + return FFS_MEDIAERR; + } + num_old_entries++; + + ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_new_entries, &dos_name); + if (ret) { + buf_unlock(sb, sector_old); + return ret; + } + + if (num_old_entries < num_new_entries) { + newentry = find_empty_entry(inode, p_dir, num_new_entries); + if (newentry < 0) { + buf_unlock(sb, sector_old); + return FFS_FULL; + } + + epnew = get_entry_in_dir(sb, p_dir, newentry, §or_new); + if (!epnew) { + buf_unlock(sb, sector_old); + return FFS_MEDIAERR; + } + + MEMCPY((void *) epnew, (void *) epold, DENTRY_SIZE); + if (p_fs->fs_func->get_entry_type(epnew) == TYPE_FILE) { + p_fs->fs_func->set_entry_attr(epnew, p_fs->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + buf_modify(sb, sector_new); + buf_unlock(sb, sector_old); + + if (p_fs->vol_type == EXFAT) { + epold = get_entry_in_dir(sb, p_dir, oldentry+1, §or_old); + buf_lock(sb, sector_old); + epnew = get_entry_in_dir(sb, p_dir, newentry+1, §or_new); + + if (!epold || !epnew) { + buf_unlock(sb, sector_old); + return FFS_MEDIAERR; + } + + MEMCPY((void *) epnew, (void *) epold, DENTRY_SIZE); + buf_modify(sb, sector_new); + buf_unlock(sb, sector_old); + } + + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, newentry, num_new_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + p_fs->fs_func->delete_dir_entry(sb, p_dir, oldentry, 0, num_old_entries); + fid->entry = newentry; + } else { + if (p_fs->fs_func->get_entry_type(epold) == TYPE_FILE) { + p_fs->fs_func->set_entry_attr(epold, p_fs->fs_func->get_entry_attr(epold) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + buf_modify(sb, sector_old); + buf_unlock(sb, sector_old); + + ret = p_fs->fs_func->init_ext_entry(sb, p_dir, oldentry, num_new_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + p_fs->fs_func->delete_dir_entry(sb, p_dir, oldentry, num_new_entries, num_old_entries); + } + + return FFS_SUCCESS; +} /* end of rename_file */ + +INT32 move_file(struct inode *inode, CHAIN_T *p_olddir, INT32 oldentry, CHAIN_T *p_newdir, UNI_NAME_T *p_uniname, FILE_ID_T *fid) +{ + INT32 ret, newentry, num_new_entries, num_old_entries; + UINT32 sector_mov, sector_new; + CHAIN_T clu; + DOS_NAME_T dos_name; + DENTRY_T *epmov, *epnew; + struct super_block *sb = inode->i_sb; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + epmov = get_entry_in_dir(sb, p_olddir, oldentry, §or_mov); + if (!epmov) + return FFS_MEDIAERR; + + /* check if the source and target directory is the same */ + if (p_fs->fs_func->get_entry_type(epmov) == TYPE_DIR && + p_fs->fs_func->get_entry_clu0(epmov) == p_newdir->dir) + return FFS_INVALIDPATH; + + buf_lock(sb, sector_mov); + + /* buf_lock() before call count_ext_entries() */ + num_old_entries = p_fs->fs_func->count_ext_entries(sb, p_olddir, oldentry, epmov); + if (num_old_entries < 0) { + buf_unlock(sb, sector_mov); + return FFS_MEDIAERR; + } + num_old_entries++; + + ret = get_num_entries_and_dos_name(sb, p_newdir, p_uniname, &num_new_entries, &dos_name); + if (ret) { + buf_unlock(sb, sector_mov); + return ret; + } + + newentry = find_empty_entry(inode, p_newdir, num_new_entries); + if (newentry < 0) { + buf_unlock(sb, sector_mov); + return FFS_FULL; + } + + epnew = get_entry_in_dir(sb, p_newdir, newentry, §or_new); + if (!epnew) { + buf_unlock(sb, sector_mov); + return FFS_MEDIAERR; + } + + MEMCPY((void *) epnew, (void *) epmov, DENTRY_SIZE); + if (p_fs->fs_func->get_entry_type(epnew) == TYPE_FILE) { + p_fs->fs_func->set_entry_attr(epnew, p_fs->fs_func->get_entry_attr(epnew) | ATTR_ARCHIVE); + fid->attr |= ATTR_ARCHIVE; + } + buf_modify(sb, sector_new); + buf_unlock(sb, sector_mov); + + if (p_fs->vol_type == EXFAT) { + epmov = get_entry_in_dir(sb, p_olddir, oldentry+1, §or_mov); + buf_lock(sb, sector_mov); + epnew = get_entry_in_dir(sb, p_newdir, newentry+1, §or_new); + if (!epmov || !epnew) { + buf_unlock(sb, sector_mov); + return FFS_MEDIAERR; + } + + MEMCPY((void *) epnew, (void *) epmov, DENTRY_SIZE); + buf_modify(sb, sector_new); + buf_unlock(sb, sector_mov); + } else if (p_fs->fs_func->get_entry_type(epnew) == TYPE_DIR) { + /* change ".." pointer to new parent dir */ + clu.dir = p_fs->fs_func->get_entry_clu0(epnew); + clu.flags = 0x01; + + epnew = get_entry_in_dir(sb, &clu, 1, §or_new); + if (!epnew) + return FFS_MEDIAERR; + + if (p_newdir->dir == p_fs->root_dir) + p_fs->fs_func->set_entry_clu0(epnew, CLUSTER_32(0)); + else + p_fs->fs_func->set_entry_clu0(epnew, p_newdir->dir); + buf_modify(sb, sector_new); + } + + ret = p_fs->fs_func->init_ext_entry(sb, p_newdir, newentry, num_new_entries, p_uniname, &dos_name); + if (ret != FFS_SUCCESS) + return ret; + + p_fs->fs_func->delete_dir_entry(sb, p_olddir, oldentry, 0, num_old_entries); + + fid->dir.dir = p_newdir->dir; + fid->dir.size = p_newdir->size; + fid->dir.flags = p_newdir->flags; + + fid->entry = newentry; + + return FFS_SUCCESS; +} /* end of move_file */ + +/* + * Sector Read/Write Functions + */ + +INT32 sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 read) +{ + INT32 ret = FFS_MEDIAERR; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if ((sec >= (p_fs->PBR_sector+p_fs->num_sectors)) && (p_fs->num_sectors > 0)) { + PRINT("[EXFAT] sector_read: out of range error! (sec = %d)\n", sec); + fs_error(sb); + return ret; + } + + if (!p_fs->dev_ejected) { + ret = bdev_read(sb, sec, bh, 1, read); + if (ret != FFS_SUCCESS) + p_fs->dev_ejected = TRUE; + } + + return ret; +} /* end of sector_read */ + +INT32 sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 sync) +{ + INT32 ret = FFS_MEDIAERR; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (sec >= (p_fs->PBR_sector+p_fs->num_sectors) && (p_fs->num_sectors > 0)) { + PRINT("[EXFAT] sector_write: out of range error! (sec = %d)\n", sec); + fs_error(sb); + return ret; + } + if (bh == NULL) { + PRINT("[EXFAT] sector_write: bh is NULL!\n"); + fs_error(sb); + return ret; + } + + if (!p_fs->dev_ejected) { + ret = bdev_write(sb, sec, bh, 1, sync); + if (ret != FFS_SUCCESS) + p_fs->dev_ejected = TRUE; + } + + return ret; +} /* end of sector_write */ + +INT32 multi_sector_read(struct super_block *sb, UINT32 sec, struct buffer_head **bh, INT32 num_secs, INT32 read) +{ + INT32 ret = FFS_MEDIAERR; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (((sec+num_secs) > (p_fs->PBR_sector+p_fs->num_sectors)) && (p_fs->num_sectors > 0)) { + PRINT("[EXFAT] multi_sector_read: out of range error! (sec = %d, num_secs = %d)\n", sec, num_secs); + fs_error(sb); + return ret; + } + + if (!p_fs->dev_ejected) { + ret = bdev_read(sb, sec, bh, num_secs, read); + if (ret != FFS_SUCCESS) + p_fs->dev_ejected = TRUE; + } + + return ret; +} /* end of multi_sector_read */ + +INT32 multi_sector_write(struct super_block *sb, UINT32 sec, struct buffer_head *bh, INT32 num_secs, INT32 sync) +{ + INT32 ret = FFS_MEDIAERR; + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if ((sec+num_secs) > (p_fs->PBR_sector+p_fs->num_sectors) && (p_fs->num_sectors > 0)) { + PRINT("[EXFAT] multi_sector_write: out of range error! (sec = %d, num_secs = %d)\n", sec, num_secs); + fs_error(sb); + return ret; + } + if (bh == NULL) { + PRINT("[EXFAT] multi_sector_write: bh is NULL!\n"); + fs_error(sb); + return ret; + } + + if (!p_fs->dev_ejected) { + ret = bdev_write(sb, sec, bh, num_secs, sync); + if (ret != FFS_SUCCESS) + p_fs->dev_ejected = TRUE; + } + + return ret; +} /* end of multi_sector_write */ + +/* end of exfat_core.c */ diff --git a/fs/exfat/exfat_data.c b/fs/exfat/exfat_data.c new file mode 100644 index 0000000000000..37e1932109b51 --- /dev/null +++ b/fs/exfat/exfat_data.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_data.c */ +/* PURPOSE : exFAT Configuable Data Definitions */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include "exfat_config.h" +#include "exfat_global.h" +#include "exfat_data.h" +#include "exfat_oal.h" + +#include "exfat_blkdev.h" +#include "exfat_cache.h" +#include "exfat_nls.h" +#include "exfat_super.h" +#include "exfat.h" + +/*======================================================================*/ +/* */ +/* GLOBAL VARIABLE DEFINITIONS */ +/* */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* File Manager */ +/*----------------------------------------------------------------------*/ + +/* file system volume table */ +FS_STRUCT_T fs_struct[MAX_DRIVE]; + +#if 0 +/*----------------------------------------------------------------------*/ +/* Buffer Manager */ +/*----------------------------------------------------------------------*/ + +/* FAT cache */ +DECLARE_MUTEX(f_sem); +BUF_CACHE_T FAT_cache_array[FAT_CACHE_SIZE]; +BUF_CACHE_T FAT_cache_lru_list; +BUF_CACHE_T FAT_cache_hash_list[FAT_CACHE_HASH_SIZE]; + +/* buf cache */ +DECLARE_MUTEX(b_sem); +BUF_CACHE_T buf_cache_array[BUF_CACHE_SIZE]; +BUF_CACHE_T buf_cache_lru_list; +BUF_CACHE_T buf_cache_hash_list[BUF_CACHE_HASH_SIZE]; +#endif + +/* end of exfat_data.c */ diff --git a/fs/exfat/exfat_data.h b/fs/exfat/exfat_data.h new file mode 100644 index 0000000000000..f1a0332c961bd --- /dev/null +++ b/fs/exfat/exfat_data.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_data.h */ +/* PURPOSE : Header File for exFAT Configuable Constants */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_DATA_H +#define _EXFAT_DATA_H + +#include "exfat_config.h" +#include "exfat_global.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /*======================================================================*/ + /* */ + /* FFS CONFIGURATIONS */ + /* (CHANGE THIS PART IF REQUIRED) */ + /* */ + /*======================================================================*/ + + /* max number of block devices */ +#define MAX_DEVICE 2 + + /* max number of volumes on all block devices */ +#define MAX_DRIVE 2 + + /* max number of open files */ +#define MAX_OPEN 20 + + /* max number of root directory entries in FAT12/16 */ + /* (should be an exponential value of 2) */ +#define MAX_DENTRY 512 + + /* cache size (in number of sectors) */ + /* (should be an exponential value of 2) */ +#define FAT_CACHE_SIZE 128 +#define FAT_CACHE_HASH_SIZE 64 +#define BUF_CACHE_SIZE 256 +#define BUF_CACHE_HASH_SIZE 64 + +#ifndef CONFIG_EXFAT_DEFAULT_CODEPAGE +#define CONFIG_EXFAT_DEFAULT_CODEPAGE 437 +#define CONFIG_EXFAT_DEFAULT_IOCHARSET "utf8" +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _EXFAT_DATA_H */ + +/* end of exfat_data.h */ diff --git a/fs/exfat/exfat_global.c b/fs/exfat/exfat_global.c new file mode 100644 index 0000000000000..036f08eb0a94d --- /dev/null +++ b/fs/exfat/exfat_global.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_global.c */ +/* PURPOSE : exFAT Miscellaneous Functions */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include "exfat_config.h" +#include "exfat_global.h" + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*======================================================================*/ +/* */ +/* LIBRARY FUNCTION DEFINITIONS -- WELL-KNOWN FUNCTIONS */ +/* */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* String Manipulation Functions */ +/* (defined if no system memory functions are available) */ +/*----------------------------------------------------------------------*/ + +INT32 __wstrchr(UINT16 *str, UINT16 wchar) +{ + while (*str) { + if (*(str++) == wchar) return(1); + } + return(0); +} + +INT32 __wstrlen(UINT16 *str) +{ + INT32 length = 0; + + while (*(str++)) length++; + return(length); +} + +/*======================================================================*/ +/* */ +/* LIBRARY FUNCTION DEFINITIONS -- OTHER UTILITY FUNCTIONS */ +/* */ +/*======================================================================*/ + +/*----------------------------------------------------------------------*/ +/* Bitmap Manipulation Functions */ +/*----------------------------------------------------------------------*/ + +#define BITMAP_LOC(v) ((v) >> 3) +#define BITMAP_SHIFT(v) ((v) & 0x07) + +void Bitmap_set_all(UINT8 *bitmap, INT32 mapsize) +{ + MEMSET(bitmap, 0xFF, mapsize); +} /* end of Bitmap_set_all */ + +void Bitmap_clear_all(UINT8 *bitmap, INT32 mapsize) +{ + MEMSET(bitmap, 0x0, mapsize); +} /* end of Bitmap_clear_all */ + +INT32 Bitmap_test(UINT8 *bitmap, INT32 i) +{ + UINT8 data; + + data = bitmap[BITMAP_LOC(i)]; + if ((data >> BITMAP_SHIFT(i)) & 0x01) return(1); + return(0); +} /* end of Bitmap_test */ + +void Bitmap_set(UINT8 *bitmap, INT32 i) +{ + bitmap[BITMAP_LOC(i)] |= (0x01 << BITMAP_SHIFT(i)); +} /* end of Bitmap_set */ + +void Bitmap_clear(UINT8 *bitmap, INT32 i) +{ + bitmap[BITMAP_LOC(i)] &= ~(0x01 << BITMAP_SHIFT(i)); +} /* end of Bitmap_clear */ + +void Bitmap_nbits_set(UINT8 *bitmap, INT32 offset, INT32 nbits) +{ + INT32 i; + + for (i = 0; i < nbits; i++) { + Bitmap_set(bitmap, offset+i); + } +} /* end of Bitmap_nbits_set */ + +void Bitmap_nbits_clear(UINT8 *bitmap, INT32 offset, INT32 nbits) +{ + INT32 i; + + for (i = 0; i < nbits; i++) { + Bitmap_clear(bitmap, offset+i); + } +} /* end of Bitmap_nbits_clear */ + +/*----------------------------------------------------------------------*/ +/* Miscellaneous Library Functions */ +/*----------------------------------------------------------------------*/ + +/* integer to ascii conversion */ +void my_itoa(INT8 *buf, INT32 v) +{ + INT32 mod[10]; + INT32 i; + + for (i = 0; i < 10; i++) { + mod[i] = (v % 10); + v = v / 10; + if (v == 0) break; + } + + if (i == 10) + i--; + + for (; i >= 0; i--) { + *buf = (UINT8) ('0' + mod[i]); + buf++; + } + *buf = '\0'; +} /* end of my_itoa */ + +/* value to base 2 log conversion */ +INT32 my_log2(UINT32 v) +{ + UINT32 bits = 0; + + while (v > 1) { + if (v & 0x01) return(-1); + v >>= 1; + bits++; + } + return(bits); +} /* end of my_log2 */ + +/* end of exfat_global.c */ diff --git a/fs/exfat/exfat_global.h b/fs/exfat/exfat_global.h new file mode 100644 index 0000000000000..890bd390de350 --- /dev/null +++ b/fs/exfat/exfat_global.h @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_global.h */ +/* PURPOSE : Header File for exFAT Global Definitions & Misc Functions */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_GLOBAL_H +#define _EXFAT_GLOBAL_H + +#include +#include +#include +#include +#include + +#include "exfat_config.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /*======================================================================*/ + /* */ + /* CONSTANT & MACRO DEFINITIONS */ + /* */ + /*======================================================================*/ + + /*----------------------------------------------------------------------*/ + /* Well-Known Constants (DO NOT CHANGE THIS PART !!) */ + /*----------------------------------------------------------------------*/ + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef OK +#define OK 0 +#endif +#ifndef FAIL +#define FAIL 1 +#endif +#ifndef NULL +#define NULL 0 +#endif + + /* Min/Max macro */ +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + + /*======================================================================*/ + /* */ + /* TYPE DEFINITIONS */ + /* (CHANGE THIS PART IF REQUIRED) */ + /* */ + /*======================================================================*/ + + /* type definitions for primitive types; + these should be re-defined to meet its size for each OS platform; + these should be used instead of primitive types for portability. */ + + typedef char INT8; // 1 byte signed integer + typedef short INT16; // 2 byte signed integer + typedef int INT32; // 4 byte signed integer + typedef long long INT64; // 8 byte signed integer + + typedef unsigned char UINT8; // 1 byte unsigned integer + typedef unsigned short UINT16; // 2 byte unsigned integer + typedef unsigned int UINT32; // 4 byte unsigned integer + typedef unsigned long long UINT64; // 8 byte ussigned integer + + typedef unsigned char BOOL; + + + /*======================================================================*/ + /* */ + /* LIBRARY FUNCTION DECLARATIONS -- WELL-KNOWN FUNCTIONS */ + /* (CHANGE THIS PART IF REQUIRED) */ + /* */ + /*======================================================================*/ + + /*----------------------------------------------------------------------*/ + /* Memory Manipulation Macros & Functions */ + /*----------------------------------------------------------------------*/ + +#ifdef MALLOC +#undef MALLOC +#endif +#ifdef FREE +#undef FREE +#endif +#ifdef MEMSET +#undef MEMSET +#endif +#ifdef MEMCPY +#undef MEMCPY +#endif +#ifdef MEMCMP +#undef MEMCMP +#endif + +#define MALLOC(size) kmalloc(size, GFP_KERNEL) +#define FREE(mem) if (mem) kfree(mem) +#define MEMSET(mem, value, size) memset(mem, value, size) +#define MEMCPY(dest, src, size) memcpy(dest, src, size) +#define MEMCMP(mem1, mem2, size) memcmp(mem1, mem2, size) +#define COPY_DENTRY(dest, src) memcpy(dest, src, sizeof(DENTRY_T)) + + /*----------------------------------------------------------------------*/ + /* String Manipulation Macros & Functions */ + /*----------------------------------------------------------------------*/ + +#define STRCPY(dest, src) strcpy(dest, src) +#define STRNCPY(dest, src, n) strncpy(dest, src, n) +#define STRCAT(str1, str2) strcat(str1, str2) +#define STRCMP(str1, str2) strcmp(str1, str2) +#define STRNCMP(str1, str2, n) strncmp(str1, str2, n) +#define STRLEN(str) strlen(str) + + INT32 __wstrchr(UINT16 *str, UINT16 wchar); + INT32 __wstrlen(UINT16 *str); + +#define WSTRCHR(str, wchar) __wstrchr(str, wchar) +#define WSTRLEN(str) __wstrlen(str) + + /*----------------------------------------------------------------------*/ + /* Debugging Macros & Functions */ + /* EXFAT_CONFIG_DEBUG_MSG is configured in exfat_config.h */ + /*----------------------------------------------------------------------*/ +#if EXFAT_CONFIG_DEBUG_MSG +#define PRINTK(...) \ + do { \ + printk("[EXFAT] " __VA_ARGS__); \ + } while(0) +#else +#define PRINTK(...) +#endif + + /*======================================================================*/ + /* */ + /* LIBRARY FUNCTION DECLARATIONS -- OTHER UTILITY FUNCTIONS */ + /* (DO NOT CHANGE THIS PART !!) */ + /* */ + /*======================================================================*/ + + /*----------------------------------------------------------------------*/ + /* Bitmap Manipulation Functions */ + /*----------------------------------------------------------------------*/ + + void Bitmap_set_all(UINT8 *bitmap, INT32 mapsize); + void Bitmap_clear_all(UINT8 *bitmap, INT32 mapsize); + INT32 Bitmap_test(UINT8 *bitmap, INT32 i); + void Bitmap_set(UINT8 *bitmap, INT32 i); + void Bitmap_clear(UINT8 *bitmpa, INT32 i); + void Bitmap_nbits_set(UINT8 *bitmap, INT32 offset, INT32 nbits); + void Bitmap_nbits_clear(UINT8 *bitmap, INT32 offset, INT32 nbits); + + /*----------------------------------------------------------------------*/ + /* Miscellaneous Library Functions */ + /*----------------------------------------------------------------------*/ + + void my_itoa(INT8 *buf, INT32 v); + INT32 my_log2(UINT32 v); + + /*======================================================================*/ + /* */ + /* DEFINITIONS FOR DEBUGGING */ + /* (CHANGE THIS PART IF REQUIRED) */ + /* */ + /*======================================================================*/ + + /* debug message ouput macro */ +#ifdef PRINT +#undef PRINT +#endif + +#define PRINT printk + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _EXFAT_GLOBAL_H */ + +/* end of exfat_global.h */ diff --git a/fs/exfat/exfat_nls.c b/fs/exfat/exfat_nls.c new file mode 100644 index 0000000000000..f80af8b186b0b --- /dev/null +++ b/fs/exfat/exfat_nls.c @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_nls.c */ +/* PURPOSE : exFAT NLS Manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include "exfat_config.h" +#include "exfat_global.h" +#include "exfat_data.h" + +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat_super.h" +#include "exfat.h" + +#include + +/*----------------------------------------------------------------------*/ +/* Global Variable Definitions */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Local Variable Definitions */ +/*----------------------------------------------------------------------*/ + +static UINT16 bad_dos_chars[] = { + /* + , ; = [ ] */ + 0x002B, 0x002C, 0x003B, 0x003D, 0x005B, 0x005D, + 0xFF0B, 0xFF0C, 0xFF1B, 0xFF1D, 0xFF3B, 0xFF3D, + 0 +}; + +static UINT16 bad_uni_chars[] = { + /* " * / : < > ? \ | */ + 0x0022, 0x002A, 0x002F, 0x003A, + 0x003C, 0x003E, 0x003F, 0x005C, 0x007C, + 0 +}; + +/*----------------------------------------------------------------------*/ +/* Local Function Declarations */ +/*----------------------------------------------------------------------*/ + +static INT32 convert_uni_to_ch(struct nls_table *nls, UINT8 *ch, UINT16 uni, INT32 *lossy); +static INT32 convert_ch_to_uni(struct nls_table *nls, UINT16 *uni, UINT8 *ch, INT32 *lossy); + +/*======================================================================*/ +/* Global Function Definitions */ +/*======================================================================*/ + +UINT16 nls_upper(struct super_block *sb, UINT16 a) +{ + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + + if (EXFAT_SB(sb)->options.casesensitive) + return(a); + if (p_fs->vol_utbl != NULL && (p_fs->vol_utbl)[get_col_index(a)] != NULL) + return (p_fs->vol_utbl)[get_col_index(a)][get_row_index(a)]; + else + return a; +} + +INT32 nls_dosname_cmp(struct super_block *sb, UINT8 *a, UINT8 *b) +{ + return(STRNCMP((void *) a, (void *) b, DOS_NAME_LENGTH)); +} /* end of nls_dosname_cmp */ + +INT32 nls_uniname_cmp(struct super_block *sb, UINT16 *a, UINT16 *b) +{ + INT32 i; + + for (i = 0; i < MAX_NAME_LENGTH; i++, a++, b++) { + if (nls_upper(sb, *a) != nls_upper(sb, *b)) return(1); + if (*a == 0x0) return(0); + } + return(0); +} /* end of nls_uniname_cmp */ + +void nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, INT32 *p_lossy) +{ + INT32 i, j, len, lossy = FALSE; + UINT8 buf[MAX_CHARSET_SIZE]; + UINT8 lower = 0, upper = 0; + UINT8 *dosname = p_dosname->name; + UINT16 *uniname = p_uniname->name; + UINT16 *p, *last_period; + struct nls_table *nls = EXFAT_SB(sb)->nls_disk; + + for (i = 0; i < DOS_NAME_LENGTH; i++) { + *(dosname+i) = ' '; + } + + if (!nls_uniname_cmp(sb, uniname, (UINT16 *) UNI_CUR_DIR_NAME)) { + *(dosname) = '.'; + p_dosname->name_case = 0x0; + if (p_lossy != NULL) *p_lossy = FALSE; + return; + } + + if (!nls_uniname_cmp(sb, uniname, (UINT16 *) UNI_PAR_DIR_NAME)) { + *(dosname) = '.'; + *(dosname+1) = '.'; + p_dosname->name_case = 0x0; + if (p_lossy != NULL) *p_lossy = FALSE; + return; + } + + /* search for the last embedded period */ + last_period = NULL; + for (p = uniname; *p; p++) { + if (*p == (UINT16) '.') last_period = p; + } + + i = 0; + while (i < DOS_NAME_LENGTH) { + if (i == 8) { + if (last_period == NULL) break; + + if (uniname <= last_period) { + if (uniname < last_period) lossy = TRUE; + uniname = last_period + 1; + } + } + + if (*uniname == (UINT16) '\0') { + break; + } else if (*uniname == (UINT16) ' ') { + lossy = TRUE; + } else if (*uniname == (UINT16) '.') { + if (uniname < last_period) lossy = TRUE; + else i = 8; + } else if (WSTRCHR(bad_dos_chars, *uniname)) { + lossy = TRUE; + *(dosname+i) = '_'; + i++; + } else { + len = convert_uni_to_ch(nls, buf, *uniname, &lossy); + + if (len > 1) { + if ((i >= 8) && ((i+len) > DOS_NAME_LENGTH)) { + break; + } + if ((i < 8) && ((i+len) > 8)) { + i = 8; + continue; + } + + lower = 0xFF; + + for (j = 0; j < len; j++, i++) { + *(dosname+i) = *(buf+j); + } + } else { /* len == 1 */ + if ((*buf >= 'a') && (*buf <= 'z')) { + *(dosname+i) = *buf - ('a' - 'A'); + + if (i < 8) lower |= 0x08; + else lower |= 0x10; + } else if ((*buf >= 'A') && (*buf <= 'Z')) { + *(dosname+i) = *buf; + + if (i < 8) upper |= 0x08; + else upper |= 0x10; + } else { + *(dosname+i) = *buf; + } + i++; + } + } + + uniname++; + } + + if (*dosname == 0xE5) *dosname = 0x05; + if (*uniname != 0x0) lossy = TRUE; + + if (upper & lower) p_dosname->name_case = 0xFF; + else p_dosname->name_case = lower; + + if (p_lossy != NULL) *p_lossy = lossy; +} /* end of nls_uniname_to_dosname */ + +void nls_dosname_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname) +{ + INT32 i = 0, j, n = 0; + UINT8 buf[DOS_NAME_LENGTH+2]; + UINT8 *dosname = p_dosname->name; + UINT16 *uniname = p_uniname->name; + struct nls_table *nls = EXFAT_SB(sb)->nls_disk; + + if (*dosname == 0x05) { + *buf = 0xE5; + i++; + n++; + } + + for ( ; i < 8; i++, n++) { + if (*(dosname+i) == ' ') break; + + if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x08)) + *(buf+n) = *(dosname+i) + ('a' - 'A'); + else + *(buf+n) = *(dosname+i); + } + if (*(dosname+8) != ' ') { + *(buf+n) = '.'; + n++; + } + + for (i = 8; i < DOS_NAME_LENGTH; i++, n++) { + if (*(dosname+i) == ' ') break; + + if ((*(dosname+i) >= 'A') && (*(dosname+i) <= 'Z') && (p_dosname->name_case & 0x10)) + *(buf+n) = *(dosname+i) + ('a' - 'A'); + else + *(buf+n) = *(dosname+i); + } + *(buf+n) = '\0'; + + i = j = 0; + while (j < (MAX_NAME_LENGTH-1)) { + if (*(buf+i) == '\0') break; + + i += convert_ch_to_uni(nls, uniname, (buf+i), NULL); + + uniname++; + j++; + } + + *uniname = (UINT16) '\0'; +} /* end of nls_dosname_to_uniname */ + +void nls_uniname_to_cstring(struct super_block *sb, UINT8 *p_cstring, UNI_NAME_T *p_uniname) +{ + INT32 i, j, len; + UINT8 buf[MAX_CHARSET_SIZE]; + UINT16 *uniname = p_uniname->name; + struct nls_table *nls = EXFAT_SB(sb)->nls_io; + + i = 0; + while (i < (MAX_NAME_LENGTH-1)) { + if (*uniname == (UINT16) '\0') break; + + len = convert_uni_to_ch(nls, buf, *uniname, NULL); + + if (len > 1) { + for (j = 0; j < len; j++) + *p_cstring++ = (INT8) *(buf+j); + } else { /* len == 1 */ + *p_cstring++ = (INT8) *buf; + } + + uniname++; + i++; + } + + *p_cstring = '\0'; +} /* end of nls_uniname_to_cstring */ + +void nls_cstring_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, UINT8 *p_cstring, INT32 *p_lossy) +{ + INT32 i, j, lossy = FALSE; + UINT8 *end_of_name; + UINT8 upname[MAX_NAME_LENGTH * 2]; + UINT16 *uniname = p_uniname->name; + struct nls_table *nls = EXFAT_SB(sb)->nls_io; + + + /* strip all trailing spaces */ + end_of_name = p_cstring + STRLEN((INT8 *) p_cstring); + + while (*(--end_of_name) == ' ') { + if (end_of_name < p_cstring) break; + } + *(++end_of_name) = '\0'; + + if (STRCMP((INT8 *) p_cstring, ".") && STRCMP((INT8 *) p_cstring, "..")) { + + /* strip all trailing periods */ + while (*(--end_of_name) == '.') { + if (end_of_name < p_cstring) break; + } + *(++end_of_name) = '\0'; + } + + if (*p_cstring == '\0') + lossy = TRUE; + + i = j = 0; + while (j < (MAX_NAME_LENGTH-1)) { + if (*(p_cstring+i) == '\0') break; + + i += convert_ch_to_uni(nls, uniname, (UINT8 *)(p_cstring+i), &lossy); + + if ((*uniname < 0x0020) || WSTRCHR(bad_uni_chars, *uniname)) + lossy = TRUE; + + SET16_A(upname + j * 2, nls_upper(sb, *uniname)); + + uniname++; + j++; + } + + if (*(p_cstring+i) != '\0') + lossy = TRUE; + *uniname = (UINT16) '\0'; + + p_uniname->name_len = j; + p_uniname->name_hash = calc_checksum_2byte((void *) upname, j<<1, 0, CS_DEFAULT); + + if (p_lossy != NULL) + *p_lossy = lossy; +} /* end of nls_cstring_to_uniname */ + +/*======================================================================*/ +/* Local Function Definitions */ +/*======================================================================*/ + +static INT32 convert_ch_to_uni(struct nls_table *nls, UINT16 *uni, UINT8 *ch, INT32 *lossy) +{ + int len; + + *uni = 0x0; + + if (ch[0] < 0x80) { + *uni = (UINT16) ch[0]; + return(1); + } + + if ((len = nls->char2uni(ch, NLS_MAX_CHARSET_SIZE, uni)) < 0) { + /* conversion failed */ + printk("%s: fail to use nls \n", __func__); + if (lossy != NULL) + *lossy = TRUE; + *uni = (UINT16) '_'; + if (!strcmp(nls->charset, "utf8")) return(1); + else return(2); + } + + return(len); +} /* end of convert_ch_to_uni */ + +static INT32 convert_uni_to_ch(struct nls_table *nls, UINT8 *ch, UINT16 uni, INT32 *lossy) +{ + int len; + + ch[0] = 0x0; + + if (uni < 0x0080) { + ch[0] = (UINT8) uni; + return(1); + } + + if ((len = nls->uni2char(uni, ch, NLS_MAX_CHARSET_SIZE)) < 0) { + /* conversion failed */ + printk("%s: fail to use nls \n", __func__); + if (lossy != NULL) *lossy = TRUE; + ch[0] = '_'; + return(1); + } + + return(len); + +} /* end of convert_uni_to_ch */ + +/* end of exfat_nls.c */ diff --git a/fs/exfat/exfat_nls.h b/fs/exfat/exfat_nls.h new file mode 100644 index 0000000000000..5b14cef02463c --- /dev/null +++ b/fs/exfat/exfat_nls.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_nls.h */ +/* PURPOSE : Header File for exFAT NLS Manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_NLS_H +#define _EXFAT_NLS_H + +#include + +#include "exfat_config.h" +#include "exfat_global.h" +#include "exfat_api.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /*----------------------------------------------------------------------*/ + /* Constant & Macro Definitions */ + /*----------------------------------------------------------------------*/ + +#define NUM_UPCASE 2918 + +#define DOS_CUR_DIR_NAME ". " +#define DOS_PAR_DIR_NAME ".. " + +#ifdef __LITTLE_ENDIAN +#define UNI_CUR_DIR_NAME ".\0" +#define UNI_PAR_DIR_NAME ".\0.\0" +#else +#define UNI_CUR_DIR_NAME "\0." +#define UNI_PAR_DIR_NAME "\0.\0." +#endif + +/*----------------------------------------------------------------------*/ +/* Type Definitions */ +/*----------------------------------------------------------------------*/ + +/* DOS name stucture */ +typedef struct { + UINT8 name[DOS_NAME_LENGTH]; + UINT8 name_case; +} DOS_NAME_T; + +/* unicode name stucture */ +typedef struct { + UINT16 name[MAX_NAME_LENGTH]; + UINT16 name_hash; + UINT8 name_len; +} UNI_NAME_T; + +/*----------------------------------------------------------------------*/ +/* External Function Declarations */ +/*----------------------------------------------------------------------*/ + +/* NLS management function */ +UINT16 nls_upper(struct super_block *sb, UINT16 a); +INT32 nls_dosname_cmp(struct super_block *sb, UINT8 *a, UINT8 *b); +INT32 nls_uniname_cmp(struct super_block *sb, UINT16 *a, UINT16 *b); +void nls_uniname_to_dosname(struct super_block *sb, DOS_NAME_T *p_dosname, UNI_NAME_T *p_uniname, INT32 *p_lossy); +void nls_dosname_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, DOS_NAME_T *p_dosname); +void nls_uniname_to_cstring(struct super_block *sb, UINT8 *p_cstring, UNI_NAME_T *p_uniname); +void nls_cstring_to_uniname(struct super_block *sb, UNI_NAME_T *p_uniname, UINT8 *p_cstring, INT32 *p_lossy); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _EXFAT_NLS_H */ + +/* end of exfat_nls.h */ diff --git a/fs/exfat/exfat_oal.c b/fs/exfat/exfat_oal.c new file mode 100644 index 0000000000000..8d1a9b93bcc71 --- /dev/null +++ b/fs/exfat/exfat_oal.c @@ -0,0 +1,189 @@ +/* Some of the source code in this file came from "linux/fs/fat/misc.c". */ +/* + * linux/fs/fat/misc.c + * + * Written 1992,1993 by Werner Almesberger + * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 + * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) + */ + +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_oal.c */ +/* PURPOSE : exFAT OS Adaptation Layer */ +/* (Semaphore Functions & Real-Time Clock Functions) */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include +#include + +#include "exfat_config.h" +#include "exfat_global.h" +#include "exfat_api.h" +#include "exfat_oal.h" + +/*======================================================================*/ +/* */ +/* SEMAPHORE FUNCTIONS */ +/* */ +/*======================================================================*/ + +DECLARE_MUTEX(z_sem); + +INT32 sm_init(struct semaphore *sm) +{ + sema_init(sm, 1); + return(0); +} /* end of sm_init */ + +INT32 sm_P(struct semaphore *sm) +{ + down(sm); + return 0; +} /* end of sm_P */ + +void sm_V(struct semaphore *sm) +{ + up(sm); +} /* end of sm_V */ + + +/*======================================================================*/ +/* */ +/* REAL-TIME CLOCK FUNCTIONS */ +/* */ +/*======================================================================*/ + +extern struct timezone sys_tz; + +/* + * The epoch of FAT timestamp is 1980. + * : bits : value + * date: 0 - 4: day (1 - 31) + * date: 5 - 8: month (1 - 12) + * date: 9 - 15: year (0 - 127) from 1980 + * time: 0 - 4: sec (0 - 29) 2sec counts + * time: 5 - 10: min (0 - 59) + * time: 11 - 15: hour (0 - 23) + */ +#define UNIX_SECS_1980 315532800L + +#if BITS_PER_LONG == 64 +#define UNIX_SECS_2108 4354819200L +#endif +/* days between 1.1.70 and 1.1.80 (2 leap days) */ +#define DAYS_DELTA_DECADE (365 * 10 + 2) +/* 120 (2100 - 1980) isn't leap year */ +#define NO_LEAP_YEAR_2100 (120) +#define IS_LEAP_YEAR(y) (!((y) & 3) && (y) != NO_LEAP_YEAR_2100) + +#define SECS_PER_MIN (60) +#define SECS_PER_HOUR (60 * SECS_PER_MIN) +#define SECS_PER_DAY (24 * SECS_PER_HOUR) + +#define MAKE_LEAP_YEAR(leap_year, year) \ + do { \ + if (unlikely(year > NO_LEAP_YEAR_2100)) \ + leap_year = ((year + 3) / 4) - 1; \ + else \ + leap_year = ((year + 3) / 4); \ + } while(0) + +/* Linear day numbers of the respective 1sts in non-leap years. */ +static time_t accum_days_in_year[] = { + /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ + 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, +}; + +TIMESTAMP_T *tm_current(TIMESTAMP_T *tp) +{ + struct timespec ts = CURRENT_TIME_SEC; + time_t second = ts.tv_sec; + time_t day, leap_day, month, year; + + second -= sys_tz.tz_minuteswest * SECS_PER_MIN; + + /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ + if (second < UNIX_SECS_1980) { + tp->sec = 0; + tp->min = 0; + tp->hour = 0; + tp->day = 1; + tp->mon = 1; + tp->year = 0; + return(tp); + } +#if BITS_PER_LONG == 64 + if (second >= UNIX_SECS_2108) { + tp->sec = 59; + tp->min = 59; + tp->hour = 23; + tp->day = 31; + tp->mon = 12; + tp->year = 127; + return(tp); + } +#endif + + day = second / SECS_PER_DAY - DAYS_DELTA_DECADE; + year = day / 365; + + MAKE_LEAP_YEAR(leap_day, year); + if (year * 365 + leap_day > day) + year--; + + MAKE_LEAP_YEAR(leap_day, year); + + day -= year * 365 + leap_day; + + if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) { + month = 2; + } else { + if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3]) + day--; + for (month = 1; month < 12; month++) { + if (accum_days_in_year[month + 1] > day) + break; + } + } + day -= accum_days_in_year[month]; + + tp->sec = second % SECS_PER_MIN; + tp->min = (second / SECS_PER_MIN) % 60; + tp->hour = (second / SECS_PER_HOUR) % 24; + tp->day = day + 1; + tp->mon = month; + tp->year = year; + + return(tp); +} /* end of tm_current */ + +/* end of exfat_oal.c */ diff --git a/fs/exfat/exfat_oal.h b/fs/exfat/exfat_oal.h new file mode 100644 index 0000000000000..ca3f956eeadcc --- /dev/null +++ b/fs/exfat/exfat_oal.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_oal.h */ +/* PURPOSE : Header File for exFAT OS Adaptation Layer */ +/* (Semaphore Functions & Real-Time Clock Functions) */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_OAL_H +#define _EXFAT_OAL_H + +#include "exfat_config.h" +#include "exfat_global.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /*----------------------------------------------------------------------*/ + /* Constant & Macro Definitions (Configurable) */ + /*----------------------------------------------------------------------*/ + + /*----------------------------------------------------------------------*/ + /* Constant & Macro Definitions (Non-Configurable) */ + /*----------------------------------------------------------------------*/ + + /*----------------------------------------------------------------------*/ + /* Type Definitions */ + /*----------------------------------------------------------------------*/ + + typedef struct { + UINT16 sec; /* 0 ~ 59 */ + UINT16 min; /* 0 ~ 59 */ + UINT16 hour; /* 0 ~ 23 */ + UINT16 day; /* 1 ~ 31 */ + UINT16 mon; /* 1 ~ 12 */ + UINT16 year; /* 0 ~ 127 (since 1980) */ + } TIMESTAMP_T; + + /*----------------------------------------------------------------------*/ + /* External Function Declarations */ + /*----------------------------------------------------------------------*/ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) +#define DECLARE_MUTEX(m) DEFINE_SEMAPHORE(m) +#endif + + INT32 sm_init(struct semaphore *sm); + INT32 sm_P(struct semaphore *sm); + void sm_V(struct semaphore *sm); + + TIMESTAMP_T *tm_current(TIMESTAMP_T *tm); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _EXFAT_OAL_H */ + +/* end of exfat_oal.h */ diff --git a/fs/exfat/exfat_part.h b/fs/exfat/exfat_part.h new file mode 100644 index 0000000000000..94dd8862f5e12 --- /dev/null +++ b/fs/exfat/exfat_part.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_part.h */ +/* PURPOSE : Header File for exFAT Partition Manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#ifndef _EXFAT_PART_H +#define _EXFAT_PART_H + +#include "exfat_config.h" +#include "exfat_global.h" +#include "exfat_api.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /*----------------------------------------------------------------------*/ + /* Constant & Macro Definitions */ + /*----------------------------------------------------------------------*/ + +#define MBR_SIGNATURE 0xAA55 + + /*----------------------------------------------------------------------*/ + /* Type Definitions */ + /*----------------------------------------------------------------------*/ + + /* MS-DOS FAT master boot record (512 bytes) */ + typedef struct { + UINT8 boot_code[446]; + UINT8 partition[64]; + UINT8 signature[2]; + } MBR_SECTOR_T; + + /* MS-DOS FAT partition table (64 bytes) */ + typedef struct { + UINT8 def_boot; + UINT8 bgn_chs[3]; + UINT8 sys_type; + UINT8 end_chs[3]; + UINT8 start_sector[4]; + UINT8 num_sectors[4]; + } PART_ENTRY_T; + + /*----------------------------------------------------------------------*/ + /* External Function Declarations */ + /*----------------------------------------------------------------------*/ + + /* volume management functions */ + INT32 ffsSetPartition(INT32 dev, INT32 num_vol, PART_INFO_T *vol_spec); + INT32 ffsGetPartition(INT32 dev, INT32 *num_vol, PART_INFO_T *vol_spec); + INT32 ffsGetDevInfo(INT32 dev, DEV_INFO_T *info); + + /*----------------------------------------------------------------------*/ + /* External Function Declarations (NOT TO UPPER LAYER) */ + /*----------------------------------------------------------------------*/ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _EXFAT_PART_H */ + +/* end of exfat_part.h */ diff --git a/fs/exfat/exfat_super.c b/fs/exfat/exfat_super.c new file mode 100644 index 0000000000000..6524508739ba1 --- /dev/null +++ b/fs/exfat/exfat_super.c @@ -0,0 +1,2301 @@ +/* Some of the source code in this file came from "linux/fs/fat/file.c","linux/fs/fat/inode.c" and "linux/fs/fat/misc.c". */ +/* + * linux/fs/fat/file.c + * + * Written 1992,1993 by Werner Almesberger + * + * regular file handling primitives for fat-based filesystems + */ + +/* + * linux/fs/fat/inode.c + * + * Written 1992,1993 by Werner Almesberger + * VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner + * Rewritten for the constant inumbers support by Al Viro + * + * Fixes: + * + * Max Cohan: Fixed invalid FSINFO offset when info_sector is 0 + */ + +/* + * linux/fs/fat/misc.c + * + * Written 1992,1993 by Werner Almesberger + * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 + * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) + */ + +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) +#include +#endif + +#include "exfat_version.h" +#include "exfat_config.h" +#include "exfat_global.h" +#include "exfat_data.h" +#include "exfat_oal.h" + +#include "exfat_blkdev.h" +#include "exfat_cache.h" +#include "exfat_part.h" +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat.h" + +#include "exfat_super.h" + +static struct kmem_cache *exfat_inode_cachep; + +static int exfat_default_codepage = CONFIG_EXFAT_DEFAULT_CODEPAGE; +static char exfat_default_iocharset[] = CONFIG_EXFAT_DEFAULT_IOCHARSET; + +extern struct timezone sys_tz; + +#define CHECK_ERR(x) BUG_ON(x) +#define ELAPSED_TIME 0 + +#if (ELAPSED_TIME == 1) +#include + +static UINT32 __t1, __t2; +static UINT32 get_current_msec(void) +{ + struct timeval tm; + do_gettimeofday(&tm); + return((UINT32)(tm.tv_sec*1000000 + tm.tv_usec)); +} +#define TIME_START() do {__t1 = get_current_msec();} while (0) +#define TIME_END() do {__t2 = get_current_msec();} while (0) +#define PRINT_TIME(n) do {printk("[EXFAT] Elapsed time %d = %d (usec)\n", n, (__t2 - __t1));} while (0) +#else +#define TIME_START() +#define TIME_END() +#define PRINT_TIME(n) +#endif + +#define UNIX_SECS_1980 315532800L + +#if BITS_PER_LONG == 64 +#define UNIX_SECS_2108 4354819200L +#endif +/* days between 1.1.70 and 1.1.80 (2 leap days) */ +#define DAYS_DELTA_DECADE (365 * 10 + 2) +/* 120 (2100 - 1980) isn't leap year */ +#define NO_LEAP_YEAR_2100 (120) +#define IS_LEAP_YEAR(y) (!((y) & 0x3) && (y) != NO_LEAP_YEAR_2100) + +#define SECS_PER_MIN (60) +#define SECS_PER_HOUR (60 * SECS_PER_MIN) +#define SECS_PER_DAY (24 * SECS_PER_HOUR) + +#define MAKE_LEAP_YEAR(leap_year, year) \ + do { \ + if (unlikely(year > NO_LEAP_YEAR_2100)) \ + leap_year = ((year + 3) / 4) - 1; \ + else \ + leap_year = ((year + 3) / 4); \ + } while(0) + +/* Linear day numbers of the respective 1sts in non-leap years. */ +static time_t accum_days_in_year[] = { + /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ + 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, +}; + +static void _exfat_truncate(struct inode *inode, loff_t old_size); + +/* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */ +void exfat_time_fat2unix(struct exfat_sb_info *sbi, struct timespec *ts, + DATE_TIME_T *tp) +{ + time_t year = tp->Year; + time_t ld; + + MAKE_LEAP_YEAR(ld, year); + + if (IS_LEAP_YEAR(year) && (tp->Month) > 2) + ld++; + + ts->tv_sec = tp->Second + tp->Minute * SECS_PER_MIN + + tp->Hour * SECS_PER_HOUR + + (year * 365 + ld + accum_days_in_year[(tp->Month)] + (tp->Day - 1) + DAYS_DELTA_DECADE) * SECS_PER_DAY + + sys_tz.tz_minuteswest * SECS_PER_MIN; + ts->tv_nsec = 0; +} + +/* Convert linear UNIX date to a FAT time/date pair. */ +void exfat_time_unix2fat(struct exfat_sb_info *sbi, struct timespec *ts, + DATE_TIME_T *tp) +{ + time_t second = ts->tv_sec; + time_t day, month, year; + time_t ld; + + second -= sys_tz.tz_minuteswest * SECS_PER_MIN; + + /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ + if (second < UNIX_SECS_1980) { + tp->Second = 0; + tp->Minute = 0; + tp->Hour = 0; + tp->Day = 1; + tp->Month = 1; + tp->Year = 0; + return; + } +#if (BITS_PER_LONG == 64) + if (second >= UNIX_SECS_2108) { + tp->Second = 59; + tp->Minute = 59; + tp->Hour = 23; + tp->Day = 31; + tp->Month = 12; + tp->Year = 127; + return; + } +#endif + day = second / SECS_PER_DAY - DAYS_DELTA_DECADE; + year = day / 365; + MAKE_LEAP_YEAR(ld, year); + if (year * 365 + ld > day) + year--; + + MAKE_LEAP_YEAR(ld, year); + day -= year * 365 + ld; + + if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) { + month = 2; + } else { + if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3]) + day--; + for (month = 1; month < 12; month++) { + if (accum_days_in_year[month + 1] > day) + break; + } + } + day -= accum_days_in_year[month]; + + tp->Second = second % SECS_PER_MIN; + tp->Minute = (second / SECS_PER_MIN) % 60; + tp->Hour = (second / SECS_PER_HOUR) % 24; + tp->Day = day + 1; + tp->Month = month; + tp->Year = year; +} + +static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +static int exfat_generic_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +#else +static long exfat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#endif +static int exfat_sync_inode(struct inode *inode); +static struct inode *exfat_build_inode(struct super_block *sb, FILE_ID_T *fid, loff_t i_pos); +static void exfat_detach(struct inode *inode); +static void exfat_attach(struct inode *inode, loff_t i_pos); +static inline unsigned long exfat_hash(loff_t i_pos); +static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc); +static void exfat_write_super(struct super_block *sb); + +static void __lock_super(struct super_block *sb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + lock_super(sb); +#else + struct exfat_sb_info *sbi = EXFAT_SB(sb); + mutex_lock(&sbi->s_lock); +#endif +} + +static void __unlock_super(struct super_block *sb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + unlock_super(sb); +#else + struct exfat_sb_info *sbi = EXFAT_SB(sb); + mutex_unlock(&sbi->s_lock); +#endif +} + +static int __is_sb_dirty(struct super_block *sb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + return sb->s_dirt; +#else + struct exfat_sb_info *sbi = EXFAT_SB(sb); + return sbi->s_dirt; +#endif +} + +static void __set_sb_clean(struct super_block *sb) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + sb->s_dirt = 0; +#else + struct exfat_sb_info *sbi = EXFAT_SB(sb); + sbi->s_dirt = 0; +#endif +} + +/*======================================================================*/ +/* Directory Entry Operations */ +/*======================================================================*/ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) +static int exfat_readdir(struct file *filp, struct dir_context *ctx) +#else +static int exfat_readdir(struct file *filp, void *dirent, filldir_t filldir) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0) + struct inode *inode = file_inode(filp); +#else + struct inode *inode = filp->f_path.dentry->d_inode; +#endif + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *p_fs = &(sbi->fs_info); + BD_INFO_T *p_bd = &(EXFAT_SB(sb)->bd_info); + DIR_ENTRY_T de; + unsigned long inum; + loff_t cpos; + int err = 0; + + __lock_super(sb); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + cpos = ctx->pos; +#else + cpos = filp->f_pos; +#endif + /* Fake . and .. for the root directory. */ + if ((p_fs->vol_type == EXFAT) || (inode->i_ino == EXFAT_ROOT_INO)) { + while (cpos < 2) { + if (inode->i_ino == EXFAT_ROOT_INO) + inum = EXFAT_ROOT_INO; + else if (cpos == 0) + inum = inode->i_ino; + else /* (cpos == 1) */ + inum = parent_ino(filp->f_path.dentry); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + if (!dir_emit_dots(filp, ctx)) +#else + if (filldir(dirent, "..", cpos+1, cpos, inum, DT_DIR) < 0) +#endif + goto out; + cpos++; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + ctx->pos++; +#else + filp->f_pos++; +#endif + } + if (cpos == 2) { + cpos = 0; + } + } + if (cpos & (DENTRY_SIZE - 1)) { + err = -ENOENT; + goto out; + } + +get_new: + EXFAT_I(inode)->fid.size = i_size_read(inode); + EXFAT_I(inode)->fid.rwoffset = cpos >> DENTRY_SIZE_BITS; + + err = FsReadDir(inode, &de); + if (err) { + /* at least we tried to read a sector + * move cpos to next sector position (should be aligned) + */ + if (err == FFS_MEDIAERR) { + cpos += 1 << p_bd->sector_size_bits; + cpos &= ~((1 << p_bd->sector_size_bits)-1); + } + + err = -EIO; + goto end_of_dir; + } + + cpos = EXFAT_I(inode)->fid.rwoffset << DENTRY_SIZE_BITS; + + if (!de.Name[0]) + goto end_of_dir; + + if (!memcmp(de.ShortName, DOS_CUR_DIR_NAME, DOS_NAME_LENGTH)) { + inum = inode->i_ino; + } else if (!memcmp(de.ShortName, DOS_PAR_DIR_NAME, DOS_NAME_LENGTH)) { + inum = parent_ino(filp->f_path.dentry); + } else { + loff_t i_pos = ((loff_t) EXFAT_I(inode)->fid.start_clu << 32) | + ((EXFAT_I(inode)->fid.rwoffset-1) & 0xffffffff); + + struct inode *tmp = exfat_iget(sb, i_pos); + if (tmp) { + inum = tmp->i_ino; + iput(tmp); + } else { + inum = iunique(sb, EXFAT_ROOT_INO); + } + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + if (!dir_emit(ctx, de.Name, strlen(de.Name), inum, + (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG)) +#else + if (filldir(dirent, de.Name, strlen(de.Name), cpos-1, inum, + (de.Attr & ATTR_SUBDIR) ? DT_DIR : DT_REG) < 0) +#endif + goto out; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + ctx->pos = cpos; +#else + filp->f_pos = cpos; +#endif + goto get_new; + +end_of_dir: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + ctx->pos = cpos; +#else + filp->f_pos = cpos; +#endif +out: + __unlock_super(sb); + return err; +} + +static int exfat_ioctl_volume_id(struct inode *dir) +{ + struct super_block *sb = dir->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *p_fs = &(sbi->fs_info); + + return p_fs->vol_id; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +static int exfat_generic_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +#else +static long exfat_generic_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +#endif +{ +#if EXFAT_CONFIG_KERNEL_DEBUG +#if !(LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)) + struct inode *inode = filp->f_dentry->d_inode; +#endif + unsigned int flags; +#endif /* EXFAT_CONFIG_KERNEL_DEBUG */ + + switch (cmd) { + case EXFAT_IOCTL_GET_VOLUME_ID: + return exfat_ioctl_volume_id(inode); +#if EXFAT_CONFIG_KERNEL_DEBUG + case EXFAT_IOC_GET_DEBUGFLAGS: { + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + flags = sbi->debug_flags; + return put_user(flags, (int __user *)arg); + } + case EXFAT_IOC_SET_DEBUGFLAGS: { + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (get_user(flags, (int __user *) arg)) + return -EFAULT; + + __lock_super(sb); + sbi->debug_flags = flags; + __unlock_super(sb); + + return 0; + } +#endif /* EXFAT_CONFIG_KERNEL_DEBUG */ + default: + return -ENOTTY; /* Inappropriate ioctl for device */ + } +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +static int exfat_file_fsync(struct file *filp, int datasync) +{ + struct inode *inode = filp->f_mapping->host; + struct super_block *sb = inode->i_sb; + int res, err; + + res = generic_file_fsync(filp, datasync); + err = FsSyncVol(sb, 1); + + return res ? res : err; +} +#endif + +const struct file_operations exfat_dir_operations = { + .llseek = generic_file_llseek, + .read = generic_read_dir, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) + .iterate = exfat_readdir, +#else + .readdir = exfat_readdir, +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) + .ioctl = exfat_generic_ioctl, + .fsync = exfat_file_fsync, +#else + .unlocked_ioctl = exfat_generic_ioctl, + .fsync = generic_file_fsync, +#endif +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) +static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool excl) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) +static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, + struct nameidata *nd) +#else +static int exfat_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +#endif +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct timespec ts; + FILE_ID_T fid; + loff_t i_pos; + int err; + + __lock_super(sb); + + PRINTK("exfat_create entered\n"); + + ts = CURRENT_TIME_SEC; + + err = FsCreateFile(dir, (UINT8 *) dentry->d_name.name, FM_REGULAR, &fid); + if (err) { + if (err == FFS_INVALIDPATH) + err = -EINVAL; + else if (err == FFS_FILEEXIST) + err = -EEXIST; + else if (err == FFS_FULL) + err = -ENOSPC; + else if (err == FFS_NAMETOOLONG) + err = -ENAMETOOLONG; + else + err = -EIO; + goto out; + } + dir->i_version++; + dir->i_ctime = dir->i_mtime = dir->i_atime = ts; + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); + + inode = exfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + inode->i_version++; + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; + /* timestamp is already written, so mark_inode_dirty() is unnecessary. */ + + dentry->d_time = dentry->d_parent->d_inode->i_version; + d_instantiate(dentry, inode); + +out: + __unlock_super(sb); + PRINTK("exfat_create exited\n"); + return err; +} + +static int exfat_find(struct inode *dir, struct qstr *qname, + FILE_ID_T *fid) +{ + int err; + + if (qname->len == 0) + return -ENOENT; + + err = FsLookupFile(dir, (UINT8 *) qname->name, fid); + if (err) + return -ENOENT; + + return 0; +} + +static int exfat_d_anon_disconn(struct dentry *dentry) +{ + return IS_ROOT(dentry) && (dentry->d_flags & DCACHE_DISCONNECTED); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) +static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +#else +static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +#endif +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct dentry *alias; + int err; + FILE_ID_T fid; + loff_t i_pos; + UINT64 ret; + mode_t i_mode; + + __lock_super(sb); + PRINTK("exfat_lookup entered\n"); + err = exfat_find(dir, &dentry->d_name, &fid); + if (err) { + if (err == -ENOENT) { + inode = NULL; + goto out; + } + goto error; + } + + i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); + inode = exfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto error; + } + + i_mode = inode->i_mode; + if (S_ISLNK(i_mode)) { + EXFAT_I(inode)->target = MALLOC(i_size_read(inode)+1); + if (!EXFAT_I(inode)->target) { + err = -ENOMEM; + goto error; + } + FsReadFile(dir, &fid, EXFAT_I(inode)->target, i_size_read(inode), &ret); + *(EXFAT_I(inode)->target + i_size_read(inode)) = '\0'; + } + + alias = d_find_alias(inode); + if (alias && !exfat_d_anon_disconn(alias)) { + CHECK_ERR(d_unhashed(alias)); + if (!S_ISDIR(i_mode)) + d_move(alias, dentry); + iput(inode); + __unlock_super(sb); + PRINTK("exfat_lookup exited 1\n"); + return alias; + } else { + dput(alias); + } +out: + __unlock_super(sb); + dentry->d_time = dentry->d_parent->d_inode->i_version; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38) + dentry->d_op = sb->s_root->d_op; + dentry = d_splice_alias(inode, dentry); + if (dentry) { + dentry->d_op = sb->s_root->d_op; + dentry->d_time = dentry->d_parent->d_inode->i_version; + } +#else + dentry = d_splice_alias(inode, dentry); + if (dentry) + dentry->d_time = dentry->d_parent->d_inode->i_version; +#endif + PRINTK("exfat_lookup exited 2\n"); + return dentry; + +error: + __unlock_super(sb); + PRINTK("exfat_lookup exited 3\n"); + return ERR_PTR(err); +} + +static int exfat_unlink(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + struct super_block *sb = dir->i_sb; + struct timespec ts; + int err; + + __lock_super(sb); + + PRINTK("exfat_unlink entered\n"); + + ts = CURRENT_TIME_SEC; + + EXFAT_I(inode)->fid.size = i_size_read(inode); + + err = FsRemoveFile(dir, &(EXFAT_I(inode)->fid)); + if (err) { + if (err == FFS_PERMISSIONERR) + err = -EPERM; + else + err = -EIO; + goto out; + } + dir->i_version++; + dir->i_mtime = dir->i_atime = ts; + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + clear_nlink(inode); + inode->i_mtime = inode->i_atime = ts; + exfat_detach(inode); + remove_inode_hash(inode); + +out: + __unlock_super(sb); + PRINTK("exfat_unlink exited\n"); + return err; +} + +static int exfat_symlink(struct inode *dir, struct dentry *dentry, const char *target) +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct timespec ts; + FILE_ID_T fid; + loff_t i_pos; + int err; + UINT64 len = (UINT64) strlen(target); + UINT64 ret; + + __lock_super(sb); + + PRINTK("exfat_symlink entered\n"); + + ts = CURRENT_TIME_SEC; + + err = FsCreateFile(dir, (UINT8 *) dentry->d_name.name, FM_SYMLINK, &fid); + if (err) { + if (err == FFS_INVALIDPATH) + err = -EINVAL; + else if (err == FFS_FILEEXIST) + err = -EEXIST; + else if (err == FFS_FULL) + err = -ENOSPC; + else + err = -EIO; + goto out; + } + + err = FsWriteFile(dir, &fid, (char *) target, len, &ret); + + if (err) { + FsRemoveFile(dir, &fid); + + if (err == FFS_FULL) + err = -ENOSPC; + else + err = -EIO; + goto out; + } + + dir->i_version++; + dir->i_ctime = dir->i_mtime = dir->i_atime = ts; + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); + + inode = exfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + inode->i_version++; + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ + + EXFAT_I(inode)->target = MALLOC(len+1); + if (!EXFAT_I(inode)->target) { + err = -ENOMEM; + goto out; + } + MEMCPY(EXFAT_I(inode)->target, target, len+1); + + dentry->d_time = dentry->d_parent->d_inode->i_version; + d_instantiate(dentry, inode); + +out: + __unlock_super(sb); + PRINTK("exfat_symlink exited\n"); + return err; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) +static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +#else +static int exfat_mkdir(struct inode *dir, struct dentry *dentry, int mode) +#endif +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct timespec ts; + FILE_ID_T fid; + loff_t i_pos; + int err; + + __lock_super(sb); + + PRINTK("exfat_mkdir entered\n"); + + ts = CURRENT_TIME_SEC; + + err = FsCreateDir(dir, (UINT8 *) dentry->d_name.name, &fid); + if (err) { + if (err == FFS_INVALIDPATH) + err = -EINVAL; + else if (err == FFS_FILEEXIST) + err = -EEXIST; + else if (err == FFS_FULL) + err = -ENOSPC; + else if (err == FFS_NAMETOOLONG) + err = -ENAMETOOLONG; + else + err = -EIO; + goto out; + } + dir->i_version++; + dir->i_ctime = dir->i_mtime = dir->i_atime = ts; + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + inc_nlink(dir); + + i_pos = ((loff_t) fid.dir.dir << 32) | (fid.entry & 0xffffffff); + + inode = exfat_build_inode(sb, &fid, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + inode->i_version++; + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ + + dentry->d_time = dentry->d_parent->d_inode->i_version; + d_instantiate(dentry, inode); + +out: + __unlock_super(sb); + PRINTK("exfat_mkdir exited\n"); + return err; +} + +static int exfat_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + struct super_block *sb = dir->i_sb; + struct timespec ts; + int err; + + __lock_super(sb); + + PRINTK("exfat_rmdir entered\n"); + + ts = CURRENT_TIME_SEC; + + EXFAT_I(inode)->fid.size = i_size_read(inode); + + err = FsRemoveDir(dir, &(EXFAT_I(inode)->fid)); + if (err) { + if (err == FFS_INVALIDPATH) + err = -EINVAL; + else if (err == FFS_FILEEXIST) + err = -ENOTEMPTY; + else if (err == FFS_NOTFOUND) + err = -ENOENT; + else if (err == FFS_DIRBUSY) + err = -EBUSY; + else + err = -EIO; + goto out; + } + dir->i_version++; + dir->i_mtime = dir->i_atime = ts; + if (IS_DIRSYNC(dir)) + (void) exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + drop_nlink(dir); + + clear_nlink(inode); + inode->i_mtime = inode->i_atime = ts; + exfat_detach(inode); + remove_inode_hash(inode); + +out: + __unlock_super(sb); + PRINTK("exfat_rmdir exited\n"); + return err; +} + +static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct inode *old_inode, *new_inode; + struct super_block *sb = old_dir->i_sb; + struct timespec ts; + loff_t i_pos; + int err; + + __lock_super(sb); + + PRINTK("exfat_rename entered\n"); + + old_inode = old_dentry->d_inode; + new_inode = new_dentry->d_inode; + + ts = CURRENT_TIME_SEC; + + EXFAT_I(old_inode)->fid.size = i_size_read(old_inode); + + err = FsMoveFile(old_dir, &(EXFAT_I(old_inode)->fid), new_dir, new_dentry); + if (err) { + if (err == FFS_PERMISSIONERR) + err = -EPERM; + else if (err == FFS_INVALIDPATH) + err = -EINVAL; + else if (err == FFS_FILEEXIST) + err = -EEXIST; + else if (err == FFS_NOTFOUND) + err = -ENOENT; + else if (err == FFS_FULL) + err = -ENOSPC; + else + err = -EIO; + goto out; + } + new_dir->i_version++; + new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime = ts; + if (IS_DIRSYNC(new_dir)) + (void) exfat_sync_inode(new_dir); + else + mark_inode_dirty(new_dir); + + i_pos = ((loff_t) EXFAT_I(old_inode)->fid.dir.dir << 32) | + (EXFAT_I(old_inode)->fid.entry & 0xffffffff); + + exfat_detach(old_inode); + exfat_attach(old_inode, i_pos); + if (IS_DIRSYNC(new_dir)) + (void) exfat_sync_inode(old_inode); + else + mark_inode_dirty(old_inode); + + if ((S_ISDIR(old_inode->i_mode)) && (old_dir != new_dir)) { + drop_nlink(old_dir); + if (!new_inode) inc_nlink(new_dir); + } + + old_dir->i_version++; + old_dir->i_ctime = old_dir->i_mtime = ts; + if (IS_DIRSYNC(old_dir)) + (void) exfat_sync_inode(old_dir); + else + mark_inode_dirty(old_dir); + + if (new_inode) { + exfat_detach(new_inode); + drop_nlink(new_inode); + if (S_ISDIR(new_inode->i_mode)) + drop_nlink(new_inode); + new_inode->i_ctime = ts; + } + +out: + __unlock_super(sb); + PRINTK("exfat_rename exited\n"); + return err; +} + +static int exfat_cont_expand(struct inode *inode, loff_t size) +{ + struct address_space *mapping = inode->i_mapping; + loff_t start = i_size_read(inode), count = size - i_size_read(inode); + int err, err2; + + if ((err = generic_cont_expand_simple(inode, size)) != 0) + return err; + + inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; + mark_inode_dirty(inode); + + if (IS_SYNC(inode)) { + err = filemap_fdatawrite_range(mapping, start, start + count - 1); + err2 = sync_mapping_buffers(mapping); + err = (err)?(err):(err2); + err2 = write_inode_now(inode, 1); + err = (err)?(err):(err2); + if (!err) { + err = filemap_fdatawait_range(mapping, start, start + count - 1); + } + } + return err; +} + +static int exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode) +{ + mode_t allow_utime = sbi->options.allow_utime; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + if (!uid_eq(current_fsuid(), inode->i_uid)) +#else + if (current_fsuid() != inode->i_uid) +#endif + { + if (in_group_p(inode->i_gid)) + allow_utime >>= 3; + if (allow_utime & MAY_WRITE) + return 1; + } + + /* use a default check */ + return 0; +} + +static int exfat_sanitize_mode(const struct exfat_sb_info *sbi, + struct inode *inode, umode_t *mode_ptr) +{ + mode_t i_mode, mask, perm; + + i_mode = inode->i_mode; + + if (S_ISREG(i_mode) || S_ISLNK(i_mode)) + mask = sbi->options.fs_fmask; + else + mask = sbi->options.fs_dmask; + + perm = *mode_ptr & ~(S_IFMT | mask); + + /* Of the r and x bits, all (subject to umask) must be present.*/ + if ((perm & (S_IRUGO | S_IXUGO)) != (i_mode & (S_IRUGO|S_IXUGO))) + return -EPERM; + + if (exfat_mode_can_hold_ro(inode)) { + /* Of the w bits, either all (subject to umask) or none must be present. */ + if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask))) + return -EPERM; + } else { + /* If exfat_mode_can_hold_ro(inode) is false, can't change w bits. */ + if ((perm & S_IWUGO) != (S_IWUGO & ~mask)) + return -EPERM; + } + + *mode_ptr &= S_IFMT | perm; + + return 0; +} + +static int exfat_setattr(struct dentry *dentry, struct iattr *attr) +{ + + struct exfat_sb_info *sbi = EXFAT_SB(dentry->d_sb); + struct inode *inode = dentry->d_inode; + unsigned int ia_valid; + int error; + loff_t old_size; + + PRINTK("exfat_setattr entered\n"); + + if ((attr->ia_valid & ATTR_SIZE) + && (attr->ia_size > i_size_read(inode))) { + error = exfat_cont_expand(inode, attr->ia_size); + if (error || attr->ia_valid == ATTR_SIZE) + return error; + attr->ia_valid &= ~ATTR_SIZE; + } + + ia_valid = attr->ia_valid; + + if ((ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) + && exfat_allow_set_time(sbi, inode)) { + attr->ia_valid &= ~(ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET); + } + + error = inode_change_ok(inode, attr); + attr->ia_valid = ia_valid; + if (error) { + return error; + } + + if (((attr->ia_valid & ATTR_UID) && +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + (!uid_eq(attr->ia_uid, sbi->options.fs_uid))) || + ((attr->ia_valid & ATTR_GID) && + (!gid_eq(attr->ia_gid, sbi->options.fs_gid))) || +#else + (attr->ia_uid != sbi->options.fs_uid)) || + ((attr->ia_valid & ATTR_GID) && + (attr->ia_gid != sbi->options.fs_gid)) || +#endif + ((attr->ia_valid & ATTR_MODE) && + (attr->ia_mode & ~(S_IFREG | S_IFLNK | S_IFDIR | S_IRWXUGO)))) { + return -EPERM; + } + + /* + * We don't return -EPERM here. Yes, strange, but this is too + * old behavior. + */ + if (attr->ia_valid & ATTR_MODE) { + if (exfat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0) + attr->ia_valid &= ~ATTR_MODE; + } + + EXFAT_I(inode)->fid.size = i_size_read(inode); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) + if (attr->ia_valid) + error = inode_setattr(inode, attr); +#else + if (attr->ia_valid & ATTR_SIZE) { + old_size = i_size_read(inode); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + down_write(&EXFAT_I(inode)->truncate_lock); + truncate_setsize(inode, attr->ia_size); + _exfat_truncate(inode, old_size); + up_write(&EXFAT_I(inode)->truncate_lock); +#else + truncate_setsize(inode, attr->ia_size); + _exfat_truncate(inode, old_size); +#endif + } + setattr_copy(inode, attr); + mark_inode_dirty(inode); +#endif + + PRINTK("exfat_setattr exited\n"); + return error; +} + +static int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + struct inode *inode = dentry->d_inode; + + PRINTK("exfat_getattr entered\n"); + + generic_fillattr(inode, stat); + stat->blksize = EXFAT_SB(inode->i_sb)->fs_info.cluster_size; + + PRINTK("exfat_getattr exited\n"); + return 0; +} + +const struct inode_operations exfat_dir_inode_operations = { + .create = exfat_create, + .lookup = exfat_lookup, + .unlink = exfat_unlink, + .symlink = exfat_symlink, + .mkdir = exfat_mkdir, + .rmdir = exfat_rmdir, + .rename = exfat_rename, + .setattr = exfat_setattr, + .getattr = exfat_getattr, +}; + +/*======================================================================*/ +/* File Operations */ +/*======================================================================*/ + +static void *exfat_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + struct exfat_inode_info *ei = EXFAT_I(dentry->d_inode); + nd_set_link(nd, (char *)(ei->target)); + return NULL; +} + +const struct inode_operations exfat_symlink_inode_operations = { + .readlink = generic_readlink, + .follow_link = exfat_follow_link, +}; + +static int exfat_file_release(struct inode *inode, struct file *filp) +{ + struct super_block *sb = inode->i_sb; + + EXFAT_I(inode)->fid.size = i_size_read(inode); + FsSyncVol(sb, 0); + return 0; +} + +const struct file_operations exfat_file_operations = { + .llseek = generic_file_llseek, + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = generic_file_aio_write, + .mmap = generic_file_mmap, + .release = exfat_file_release, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) + .ioctl = exfat_generic_ioctl, + .fsync = exfat_file_fsync, +#else + .unlocked_ioctl = exfat_generic_ioctl, + .fsync = generic_file_fsync, +#endif + .splice_read = generic_file_splice_read, +}; + +static void _exfat_truncate(struct inode *inode, loff_t old_size) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *p_fs = &(sbi->fs_info); + int err; + + __lock_super(sb); + + /* + * This protects against truncating a file bigger than it was then + * trying to write into the hole. + */ + if (EXFAT_I(inode)->mmu_private > i_size_read(inode)) + EXFAT_I(inode)->mmu_private = i_size_read(inode); + + if (EXFAT_I(inode)->fid.start_clu == 0) goto out; + + err = FsTruncateFile(inode, old_size, i_size_read(inode)); + if (err) goto out; + + inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; + if (IS_DIRSYNC(inode)) + (void) exfat_sync_inode(inode); + else + mark_inode_dirty(inode); + + inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) + & ~((loff_t)p_fs->cluster_size - 1)) >> 9; +out: + __unlock_super(sb); +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36) +static void exfat_truncate(struct inode *inode) +{ + _exfat_truncate(inode, i_size_read(inode)); +} +#endif + +const struct inode_operations exfat_file_inode_operations = { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,36) + .truncate = exfat_truncate, +#endif + .setattr = exfat_setattr, + .getattr = exfat_getattr, +}; + +/*======================================================================*/ +/* Address Space Operations */ +/*======================================================================*/ + +static int exfat_bmap(struct inode *inode, sector_t sector, sector_t *phys, + unsigned long *mapped_blocks, int *create) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + FS_INFO_T *p_fs = &(sbi->fs_info); + BD_INFO_T *p_bd = &(sbi->bd_info); + const unsigned long blocksize = sb->s_blocksize; + const unsigned char blocksize_bits = sb->s_blocksize_bits; + sector_t last_block; + int err, clu_offset, sec_offset; + unsigned int cluster; + + *phys = 0; + *mapped_blocks = 0; + + if ((p_fs->vol_type == FAT12) || (p_fs->vol_type == FAT16)) { + if (inode->i_ino == EXFAT_ROOT_INO) { + if (sector < (p_fs->dentries_in_root >> (p_bd->sector_size_bits-DENTRY_SIZE_BITS))) { + *phys = sector + p_fs->root_start_sector; + *mapped_blocks = 1; + } + return 0; + } + } + + last_block = (i_size_read(inode) + (blocksize - 1)) >> blocksize_bits; + if (sector >= last_block) { + if (*create == 0) return 0; + } else { + *create = 0; + } + + clu_offset = sector >> p_fs->sectors_per_clu_bits; /* cluster offset */ + sec_offset = sector & (p_fs->sectors_per_clu - 1); /* sector offset in cluster */ + + EXFAT_I(inode)->fid.size = i_size_read(inode); + + err = FsMapCluster(inode, clu_offset, &cluster); + + if (err) { + if (err == FFS_FULL) + return -ENOSPC; + else + return -EIO; + } else if (cluster != CLUSTER_32(~0)) { + *phys = START_SECTOR(cluster) + sec_offset; + *mapped_blocks = p_fs->sectors_per_clu - sec_offset; + } + + return 0; +} + +static int exfat_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + struct super_block *sb = inode->i_sb; + unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; + int err; + unsigned long mapped_blocks; + sector_t phys; + + __lock_super(sb); + + err = exfat_bmap(inode, iblock, &phys, &mapped_blocks, &create); + if (err) { + __unlock_super(sb); + return err; + } + + if (phys) { + max_blocks = min(mapped_blocks, max_blocks); + if (create) { + EXFAT_I(inode)->mmu_private += max_blocks << sb->s_blocksize_bits; + set_buffer_new(bh_result); + } + map_bh(bh_result, sb, phys); + } + + bh_result->b_size = max_blocks << sb->s_blocksize_bits; + __unlock_super(sb); + + return 0; +} + +static int exfat_readpage(struct file *file, struct page *page) +{ + int ret; + ret = mpage_readpage(page, exfat_get_block); + return ret; +} + +static int exfat_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned nr_pages) +{ + int ret; + ret = mpage_readpages(mapping, pages, nr_pages, exfat_get_block); + return ret; +} + +static int exfat_writepage(struct page *page, struct writeback_control *wbc) +{ + int ret; + ret = block_write_full_page(page, exfat_get_block, wbc); + return ret; +} + +static int exfat_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + int ret; + ret = mpage_writepages(mapping, wbc, exfat_get_block); + return ret; +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) +static void exfat_write_failed(struct address_space *mapping, loff_t to) +{ + struct inode *inode = mapping->host; + if (to > i_size_read(inode)) { + truncate_pagecache(inode, to, i_size_read(inode)); + EXFAT_I(inode)->fid.size = i_size_read(inode); + _exfat_truncate(inode, i_size_read(inode)); + } +} +#endif + + +static int exfat_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) +{ + int ret; + *pagep = NULL; + ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + exfat_get_block, + &EXFAT_I(mapping->host)->mmu_private); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) + if (ret < 0) + exfat_write_failed(mapping, pos+len); +#endif + return ret; +} + +static int exfat_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *pagep, void *fsdata) +{ + struct inode *inode = mapping->host; + FILE_ID_T *fid = &(EXFAT_I(inode)->fid); + int err; + + err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) + if (err < len) + exfat_write_failed(mapping, pos+len); +#endif + + if (!(err < 0) && !(fid->attr & ATTR_ARCHIVE)) { + inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; + fid->attr |= ATTR_ARCHIVE; + mark_inode_dirty(inode); + } + return err; +} + +static ssize_t exfat_direct_IO(int rw, struct kiocb *iocb, + const struct iovec *iov, + loff_t offset, unsigned long nr_segs) +{ + struct inode *inode = iocb->ki_filp->f_mapping->host; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) + struct address_space *mapping = iocb->ki_filp->f_mapping; +#endif + ssize_t ret; + + if (rw == WRITE) { + if (EXFAT_I(inode)->mmu_private < (offset + iov_length(iov, nr_segs))) + return 0; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + ret = blockdev_direct_IO(rw, iocb, inode, iov, + offset, nr_segs, exfat_get_block); +#else + ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, + offset, nr_segs, exfat_get_block, NULL); +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34) + if ((ret < 0) && (rw & WRITE)) + exfat_write_failed(mapping, offset+iov_length(iov, nr_segs)); +#endif + return ret; +} + +static sector_t _exfat_bmap(struct address_space *mapping, sector_t block) +{ + sector_t blocknr; + + /* exfat_get_cluster() assumes the requested blocknr isn't truncated. */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + down_read(&EXFAT_I(mapping->host)->truncate_lock); + blocknr = generic_block_bmap(mapping, block, exfat_get_block); + up_read(&EXFAT_I(mapping->host)->truncate_lock); +#else + down_read(&EXFAT_I(mapping->host)->i_alloc_sem); + blocknr = generic_block_bmap(mapping, block, exfat_get_block); + up_read(&EXFAT_I(mapping->host)->i_alloc_sem); +#endif + + return blocknr; +} + +const struct address_space_operations exfat_aops = { + .readpage = exfat_readpage, + .readpages = exfat_readpages, + .writepage = exfat_writepage, + .writepages = exfat_writepages, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39) + .sync_page = block_sync_page, +#endif + .write_begin = exfat_write_begin, + .write_end = exfat_write_end, + .direct_IO = exfat_direct_IO, + .bmap = _exfat_bmap +}; + +/*======================================================================*/ +/* Super Operations */ +/*======================================================================*/ + +static inline unsigned long exfat_hash(loff_t i_pos) +{ + return hash_32(i_pos, EXFAT_HASH_BITS); +} + +static struct inode *exfat_iget(struct super_block *sb, loff_t i_pos) { + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_inode_info *info; + struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos); + struct inode *inode = NULL; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0) + struct hlist_node *node; + + spin_lock(&sbi->inode_hash_lock); + hlist_for_each_entry(info, node, head, i_hash_fat) { +#else + spin_lock(&sbi->inode_hash_lock); + hlist_for_each_entry(info, head, i_hash_fat) { +#endif + CHECK_ERR(info->vfs_inode.i_sb != sb); + + if (i_pos != info->i_pos) + continue; + inode = igrab(&info->vfs_inode); + if (inode) + break; + } + spin_unlock(&sbi->inode_hash_lock); + return inode; +} + +static void exfat_attach(struct inode *inode, loff_t i_pos) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos); + + spin_lock(&sbi->inode_hash_lock); + EXFAT_I(inode)->i_pos = i_pos; + hlist_add_head(&EXFAT_I(inode)->i_hash_fat, head); + spin_unlock(&sbi->inode_hash_lock); +} + +static void exfat_detach(struct inode *inode) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + + spin_lock(&sbi->inode_hash_lock); + hlist_del_init(&EXFAT_I(inode)->i_hash_fat); + EXFAT_I(inode)->i_pos = 0; + spin_unlock(&sbi->inode_hash_lock); +} + +/* doesn't deal with root inode */ +static int exfat_fill_inode(struct inode *inode, FILE_ID_T *fid) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + FS_INFO_T *p_fs = &(sbi->fs_info); + DIR_ENTRY_T info; + + memcpy(&(EXFAT_I(inode)->fid), fid, sizeof(FILE_ID_T)); + + FsReadStat(inode, &info); + + EXFAT_I(inode)->i_pos = 0; + EXFAT_I(inode)->target = NULL; + inode->i_uid = sbi->options.fs_uid; + inode->i_gid = sbi->options.fs_gid; + inode->i_version++; + inode->i_generation = get_seconds(); + + if (info.Attr & ATTR_SUBDIR) { /* directory */ + inode->i_generation &= ~1; + inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); + inode->i_op = &exfat_dir_inode_operations; + inode->i_fop = &exfat_dir_operations; + + i_size_write(inode, info.Size); + EXFAT_I(inode)->mmu_private = i_size_read(inode); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + set_nlink(inode,info.NumSubdirs); +#else + inode->i_nlink = info.NumSubdirs; +#endif + } else if (info.Attr & ATTR_SYMLINK) { /* symbolic link */ + inode->i_generation |= 1; + inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); + inode->i_op = &exfat_symlink_inode_operations; + + i_size_write(inode, info.Size); + EXFAT_I(inode)->mmu_private = i_size_read(inode); + } else { /* regular file */ + inode->i_generation |= 1; + inode->i_mode = exfat_make_mode(sbi, info.Attr, S_IRWXUGO); + inode->i_op = &exfat_file_inode_operations; + inode->i_fop = &exfat_file_operations; + inode->i_mapping->a_ops = &exfat_aops; + inode->i_mapping->nrpages = 0; + + i_size_write(inode, info.Size); + EXFAT_I(inode)->mmu_private = i_size_read(inode); + } + exfat_save_attr(inode, info.Attr); + + inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) + & ~((loff_t)p_fs->cluster_size - 1)) >> 9; + + exfat_time_fat2unix(sbi, &inode->i_mtime, &info.ModifyTimestamp); + exfat_time_fat2unix(sbi, &inode->i_ctime, &info.CreateTimestamp); + exfat_time_fat2unix(sbi, &inode->i_atime, &info.AccessTimestamp); + + return 0; +} + +static struct inode *exfat_build_inode(struct super_block *sb, + FILE_ID_T *fid, loff_t i_pos) { + struct inode *inode; + int err; + + inode = exfat_iget(sb, i_pos); + if (inode) + goto out; + inode = new_inode(sb); + if (!inode) { + inode = ERR_PTR(-ENOMEM); + goto out; + } + inode->i_ino = iunique(sb, EXFAT_ROOT_INO); + inode->i_version = 1; + err = exfat_fill_inode(inode, fid); + if (err) { + iput(inode); + inode = ERR_PTR(err); + goto out; + } + exfat_attach(inode, i_pos); + insert_inode_hash(inode); +out: + return inode; +} + +static int exfat_sync_inode(struct inode *inode) +{ + return exfat_write_inode(inode, NULL); +} + +static struct inode *exfat_alloc_inode(struct super_block *sb) { + struct exfat_inode_info *ei; + + ei = kmem_cache_alloc(exfat_inode_cachep, GFP_NOFS); + if (!ei) + return NULL; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + init_rwsem(&ei->truncate_lock); +#endif + + return &ei->vfs_inode; +} + +static void exfat_destroy_inode(struct inode *inode) +{ + FREE(EXFAT_I(inode)->target); + EXFAT_I(inode)->target = NULL; + + kmem_cache_free(exfat_inode_cachep, EXFAT_I(inode)); +} + +static int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + DIR_ENTRY_T info; + + if (inode->i_ino == EXFAT_ROOT_INO) + return 0; + + info.Attr = exfat_make_attr(inode); + info.Size = i_size_read(inode); + + exfat_time_unix2fat(sbi, &inode->i_mtime, &info.ModifyTimestamp); + exfat_time_unix2fat(sbi, &inode->i_ctime, &info.CreateTimestamp); + exfat_time_unix2fat(sbi, &inode->i_atime, &info.AccessTimestamp); + + FsWriteStat(inode, &info); + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +static void exfat_delete_inode(struct inode *inode) +{ + truncate_inode_pages(&inode->i_data, 0); + clear_inode(inode); +} + +static void exfat_clear_inode(struct inode *inode) +{ + exfat_detach(inode); + remove_inode_hash(inode); +} +#else +static void exfat_evict_inode(struct inode *inode) +{ + truncate_inode_pages(&inode->i_data, 0); + + if (!inode->i_nlink) + i_size_write(inode, 0); + invalidate_inode_buffers(inode); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,00) + end_writeback(inode); +#else + clear_inode(inode); +#endif + exfat_detach(inode); + + remove_inode_hash(inode); +} +#endif + +static void exfat_free_super(struct exfat_sb_info *sbi) +{ + if (sbi->nls_disk) + unload_nls(sbi->nls_disk); + if (sbi->nls_io) + unload_nls(sbi->nls_io); + if (sbi->options.iocharset != exfat_default_iocharset) + kfree(sbi->options.iocharset); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) + mutex_destroy(&sbi->s_lock); +#endif + kfree(sbi); +} + +static void exfat_put_super(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + if (__is_sb_dirty(sb)) + exfat_write_super(sb); + + FsUmountVol(sb); + + sb->s_fs_info = NULL; + exfat_free_super(sbi); +} + +static void exfat_write_super(struct super_block *sb) +{ + __lock_super(sb); + + __set_sb_clean(sb); + + if (!(sb->s_flags & MS_RDONLY)) + FsSyncVol(sb, 1); + + __unlock_super(sb); +} + +static int exfat_sync_fs(struct super_block *sb, int wait) +{ + int err = 0; + + if (__is_sb_dirty(sb)) { + __lock_super(sb); + __set_sb_clean(sb); + err = FsSyncVol(sb, 1); + __unlock_super(sb); + } + + return err; +} + +static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct super_block *sb = dentry->d_sb; + u64 id = huge_encode_dev(sb->s_bdev->bd_dev); + FS_INFO_T *p_fs = &(EXFAT_SB(sb)->fs_info); + VOL_INFO_T info; + + if (p_fs->used_clusters == (UINT32) ~0) { + if (FFS_MEDIAERR == FsGetVolInfo(sb, &info)) + return -EIO; + + } else { + info.FatType = p_fs->vol_type; + info.ClusterSize = p_fs->cluster_size; + info.NumClusters = p_fs->num_clusters - 2; + info.UsedClusters = p_fs->used_clusters; + info.FreeClusters = info.NumClusters - info.UsedClusters; + + if (p_fs->dev_ejected) + return -EIO; + } + + buf->f_type = sb->s_magic; + buf->f_bsize = info.ClusterSize; + buf->f_blocks = info.NumClusters; + buf->f_bfree = info.FreeClusters; + buf->f_bavail = info.FreeClusters; + buf->f_fsid.val[0] = (u32)id; + buf->f_fsid.val[1] = (u32)(id >> 32); + buf->f_namelen = 260; + + return 0; +} + +static int exfat_remount(struct super_block *sb, int *flags, char *data) +{ + *flags |= MS_NODIRATIME; + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) +static int exfat_show_options(struct seq_file *m, struct dentry *root) +{ + struct exfat_sb_info *sbi = EXFAT_SB(root->d_sb); +#else +static int exfat_show_options(struct seq_file *m, struct vfsmount *mnt) +{ + struct exfat_sb_info *sbi = EXFAT_SB(mnt->mnt_sb); +#endif + struct exfat_mount_options *opts = &sbi->options; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + if (__kuid_val(opts->fs_uid)) + seq_printf(m, ",uid=%u", __kuid_val(opts->fs_uid)); + if (__kgid_val(opts->fs_gid)) + seq_printf(m, ",gid=%u", __kgid_val(opts->fs_gid)); +#else + if (opts->fs_uid != 0) + seq_printf(m, ",uid=%u", opts->fs_uid); + if (opts->fs_gid != 0) + seq_printf(m, ",gid=%u", opts->fs_gid); +#endif + seq_printf(m, ",fmask=%04o", opts->fs_fmask); + seq_printf(m, ",dmask=%04o", opts->fs_dmask); + if (opts->allow_utime) + seq_printf(m, ",allow_utime=%04o", opts->allow_utime); + if (sbi->nls_disk) + seq_printf(m, ",codepage=%s", sbi->nls_disk->charset); + if (sbi->nls_io) + seq_printf(m, ",iocharset=%s", sbi->nls_io->charset); + seq_printf(m, ",namecase=%u", opts->casesensitive); + if (opts->errors == EXFAT_ERRORS_CONT) + seq_puts(m, ",errors=continue"); + else if (opts->errors == EXFAT_ERRORS_PANIC) + seq_puts(m, ",errors=panic"); + else + seq_puts(m, ",errors=remount-ro"); +#if EXFAT_CONFIG_DISCARD + if (opts->discard) + seq_printf(m, ",discard"); +#endif + return 0; +} + +const struct super_operations exfat_sops = { + .alloc_inode = exfat_alloc_inode, + .destroy_inode = exfat_destroy_inode, + .write_inode = exfat_write_inode, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) + .delete_inode = exfat_delete_inode, + .clear_inode = exfat_clear_inode, +#else + .evict_inode = exfat_evict_inode, +#endif + .put_super = exfat_put_super, +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) + .write_super = exfat_write_super, +#endif + .sync_fs = exfat_sync_fs, + .statfs = exfat_statfs, + .remount_fs = exfat_remount, + .show_options = exfat_show_options, +}; + +/*======================================================================*/ +/* Super Block Read Operations */ +/*======================================================================*/ + +enum { + Opt_uid, + Opt_gid, + Opt_umask, + Opt_dmask, + Opt_fmask, + Opt_allow_utime, + Opt_codepage, + Opt_charset, + Opt_namecase, + Opt_debug, + Opt_err_cont, + Opt_err_panic, + Opt_err_ro, + Opt_err, +#if EXFAT_CONFIG_DISCARD + Opt_discard, +#endif /* EXFAT_CONFIG_DISCARD */ +}; + +static const match_table_t exfat_tokens = { + {Opt_uid, "uid=%u"}, + {Opt_gid, "gid=%u"}, + {Opt_umask, "umask=%o"}, + {Opt_dmask, "dmask=%o"}, + {Opt_fmask, "fmask=%o"}, + {Opt_allow_utime, "allow_utime=%o"}, + {Opt_codepage, "codepage=%u"}, + {Opt_charset, "iocharset=%s"}, + {Opt_namecase, "namecase=%u"}, + {Opt_debug, "debug"}, + {Opt_err_cont, "errors=continue"}, + {Opt_err_panic, "errors=panic"}, + {Opt_err_ro, "errors=remount-ro"}, +#if EXFAT_CONFIG_DISCARD + {Opt_discard, "discard"}, +#endif /* EXFAT_CONFIG_DISCARD */ + {Opt_err, NULL} +}; + +static int parse_options(char *options, int silent, int *debug, + struct exfat_mount_options *opts) +{ + char *p; + substring_t args[MAX_OPT_ARGS]; + int option; + char *iocharset; + + opts->fs_uid = current_uid(); + opts->fs_gid = current_gid(); + opts->fs_fmask = opts->fs_dmask = current->fs->umask; + opts->allow_utime = (unsigned short) -1; + opts->codepage = exfat_default_codepage; + opts->iocharset = exfat_default_iocharset; + opts->casesensitive = 0; + opts->errors = EXFAT_ERRORS_RO; +#if EXFAT_CONFIG_DISCARD + opts->discard = 0; +#endif + *debug = 0; + + if (!options) + goto out; + + while ((p = strsep(&options, ",")) != NULL) { + int token; + if (!*p) + continue; + + token = match_token(p, exfat_tokens, args); + switch (token) { + case Opt_uid: + if (match_int(&args[0], &option)) + return 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + opts->fs_uid = KUIDT_INIT(option); +#else + opts->fs_uid = option; +#endif + break; + case Opt_gid: + if (match_int(&args[0], &option)) + return 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + opts->fs_gid = KGIDT_INIT(option); +#else + opts->fs_gid = option; +#endif + break; + case Opt_umask: + case Opt_dmask: + case Opt_fmask: + if (match_octal(&args[0], &option)) + return 0; + if (token != Opt_dmask) + opts->fs_fmask = option; + if (token != Opt_fmask) + opts->fs_dmask = option; + break; + case Opt_allow_utime: + if (match_octal(&args[0], &option)) + return 0; + opts->allow_utime = option & (S_IWGRP | S_IWOTH); + break; + case Opt_codepage: + if (match_int(&args[0], &option)) + return 0; + opts->codepage = option; + break; + case Opt_charset: + if (opts->iocharset != exfat_default_iocharset) + kfree(opts->iocharset); + iocharset = match_strdup(&args[0]); + if (!iocharset) + return -ENOMEM; + opts->iocharset = iocharset; + break; + case Opt_namecase: + if (match_int(&args[0], &option)) + return 0; + opts->casesensitive = option; + break; + case Opt_err_cont: + opts->errors = EXFAT_ERRORS_CONT; + break; + case Opt_err_panic: + opts->errors = EXFAT_ERRORS_PANIC; + break; + case Opt_err_ro: + opts->errors = EXFAT_ERRORS_RO; + break; + case Opt_debug: + *debug = 1; + break; +#if EXFAT_CONFIG_DISCARD + case Opt_discard: + opts->discard = 1; + break; +#endif /* EXFAT_CONFIG_DISCARD */ + default: + if (!silent) { + printk(KERN_ERR "[EXFAT] Unrecognized mount option %s or missing value\n", p); + } + return -EINVAL; + } + } + +out: + if (opts->allow_utime == (unsigned short) -1) + opts->allow_utime = ~opts->fs_dmask & (S_IWGRP | S_IWOTH); + + return 0; +} + +static void exfat_hash_init(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + int i; + + spin_lock_init(&sbi->inode_hash_lock); + for (i = 0; i < EXFAT_HASH_SIZE; i++) + INIT_HLIST_HEAD(&sbi->inode_hashtable[i]); +} + +static int exfat_read_root(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct timespec ts; + FS_INFO_T *p_fs = &(sbi->fs_info); + DIR_ENTRY_T info; + + ts = CURRENT_TIME_SEC; + + EXFAT_I(inode)->fid.dir.dir = p_fs->root_dir; + EXFAT_I(inode)->fid.dir.flags = 0x01; + EXFAT_I(inode)->fid.entry = -1; + EXFAT_I(inode)->fid.start_clu = p_fs->root_dir; + EXFAT_I(inode)->fid.flags = 0x01; + EXFAT_I(inode)->fid.type = TYPE_DIR; + EXFAT_I(inode)->fid.rwoffset = 0; + EXFAT_I(inode)->fid.hint_last_off = -1; + + EXFAT_I(inode)->target = NULL; + + FsReadStat(inode, &info); + + inode->i_uid = sbi->options.fs_uid; + inode->i_gid = sbi->options.fs_gid; + inode->i_version++; + inode->i_generation = 0; + inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, S_IRWXUGO); + inode->i_op = &exfat_dir_inode_operations; + inode->i_fop = &exfat_dir_operations; + + i_size_write(inode, info.Size); + inode->i_blocks = ((i_size_read(inode) + (p_fs->cluster_size - 1)) + & ~((loff_t)p_fs->cluster_size - 1)) >> 9; + EXFAT_I(inode)->i_pos = ((loff_t) p_fs->root_dir << 32) | 0xffffffff; + EXFAT_I(inode)->mmu_private = i_size_read(inode); + + exfat_save_attr(inode, ATTR_SUBDIR); + inode->i_mtime = inode->i_atime = inode->i_ctime = ts; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + set_nlink(inode,info.NumSubdirs + 2); +#else + inode->i_nlink = info.NumSubdirs + 2; +#endif + + return 0; +} + +static int exfat_fill_super(struct super_block *sb, void *data, int silent) +{ + struct inode *root_inode = NULL; + struct exfat_sb_info *sbi; + int debug, ret; + long error; + char buf[50]; + + /* + * GFP_KERNEL is ok here, because while we do hold the + * supeblock lock, memory pressure can't call back into + * the filesystem, since we're only just about to mount + * it and have no inodes etc active! + */ + sbi = kzalloc(sizeof(struct exfat_sb_info), GFP_KERNEL); + if (!sbi) + return -ENOMEM; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) + mutex_init(&sbi->s_lock); +#endif + sb->s_fs_info = sbi; + + sb->s_flags |= MS_NODIRATIME; + sb->s_magic = EXFAT_SUPER_MAGIC; + sb->s_op = &exfat_sops; + + error = parse_options(data, silent, &debug, &sbi->options); + if (error) + goto out_fail; + + error = -EIO; + sb_min_blocksize(sb, 512); + sb->s_maxbytes = 0x7fffffffffffffffLL; // maximum file size + + ret = FsMountVol(sb); + if (ret) { + if (!silent) + printk(KERN_ERR "[EXFAT] FsMountVol failed\n"); + + goto out_fail; + } + + /* set up enough so that it can read an inode */ + exfat_hash_init(sb); + + /* + * The low byte of FAT's first entry must have same value with + * media-field. But in real world, too many devices is + * writing wrong value. So, removed that validity check. + * + * if (FAT_FIRST_ENT(sb, media) != first) + */ + + if (sbi->fs_info.vol_type != EXFAT) { + error = -EINVAL; + sprintf(buf, "cp%d", sbi->options.codepage); + sbi->nls_disk = load_nls(buf); + if (!sbi->nls_disk) { + printk(KERN_ERR "[EXFAT] Codepage %s not found\n", buf); + goto out_fail2; + } + } + + sbi->nls_io = load_nls(sbi->options.iocharset); + if (!sbi->nls_io) { + printk(KERN_ERR "[EXFAT] IO charset %s not found\n", + sbi->options.iocharset); + goto out_fail2; + } + + error = -ENOMEM; + root_inode = new_inode(sb); + if (!root_inode) + goto out_fail2; + root_inode->i_ino = EXFAT_ROOT_INO; + root_inode->i_version = 1; + error = exfat_read_root(root_inode); + if (error < 0) + goto out_fail2; + error = -ENOMEM; + exfat_attach(root_inode, EXFAT_I(root_inode)->i_pos); + insert_inode_hash(root_inode); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + sb->s_root = d_make_root(root_inode); +#else + sb->s_root = d_alloc_root(root_inode); +#endif + if (!sb->s_root) { + printk(KERN_ERR "[EXFAT] Getting the root inode failed\n"); + goto out_fail2; + } + + return 0; + +out_fail2: + FsUmountVol(sb); +out_fail: + if (root_inode) + iput(root_inode); + sb->s_fs_info = NULL; + exfat_free_super(sbi); + return error; +} +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) +static int exfat_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt) +{ + return get_sb_bdev(fs_type, flags, dev_name, data, exfat_fill_super, mnt); +} +#else +static struct dentry *exfat_fs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) { + return mount_bdev(fs_type, flags, dev_name, data, exfat_fill_super); +} +#endif + +static void init_once(void *foo) +{ + struct exfat_inode_info *ei = (struct exfat_inode_info *)foo; + + INIT_HLIST_NODE(&ei->i_hash_fat); + inode_init_once(&ei->vfs_inode); +} + +static int __init exfat_init_inodecache(void) +{ + exfat_inode_cachep = kmem_cache_create("exfat_inode_cache", + sizeof(struct exfat_inode_info), + 0, (SLAB_RECLAIM_ACCOUNT| + SLAB_MEM_SPREAD), + init_once); + if (exfat_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void __exit exfat_destroy_inodecache(void) +{ + kmem_cache_destroy(exfat_inode_cachep); +} + +#if EXFAT_CONFIG_KERNEL_DEBUG +static void exfat_debug_kill_sb(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct block_device *bdev = sb->s_bdev; + + long flags; + + if (sbi) { + flags = sbi->debug_flags; + + if (flags & EXFAT_DEBUGFLAGS_INVALID_UMOUNT) { + /* invalidate_bdev drops all device cache include dirty. + we use this to simulate device removal */ + FsReleaseCache(sb); + invalidate_bdev(bdev); + } + } + + kill_block_super(sb); +} +#endif /* EXFAT_CONFIG_KERNEL_DEBUG */ + +static struct file_system_type exfat_fs_type = { + .owner = THIS_MODULE, + .name = "exfat", +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) + .get_sb = exfat_get_sb, +#else + .mount = exfat_fs_mount, +#endif +#if EXFAT_CONFIG_KERNEL_DEBUG + .kill_sb = exfat_debug_kill_sb, +#else + .kill_sb = kill_block_super, +#endif /* EXFAT_CONFIG_KERNLE_DEBUG */ + .fs_flags = FS_REQUIRES_DEV, +}; + +static int __init init_exfat(void) +{ + int err; + + err = FsInit(); + if (err) { + if (err == FFS_MEMORYERR) + return -ENOMEM; + else + return -EIO; + } + + printk(KERN_INFO "exFAT: Version %s\n", EXFAT_VERSION); + + err = exfat_init_inodecache(); + if (err) goto out; + + err = register_filesystem(&exfat_fs_type); + if (err) goto out; + + return 0; +out: + FsShutdown(); + return err; +} + +static void __exit exit_exfat(void) +{ + exfat_destroy_inodecache(); + unregister_filesystem(&exfat_fs_type); + FsShutdown(); +} + +module_init(init_exfat); +module_exit(exit_exfat); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("exFAT Filesystem Driver"); +#ifdef MODULE_ALIAS_FS +MODULE_ALIAS_FS("exfat"); +#endif diff --git a/fs/exfat/exfat_super.h b/fs/exfat/exfat_super.h new file mode 100644 index 0000000000000..c9201dd79250a --- /dev/null +++ b/fs/exfat/exfat_super.h @@ -0,0 +1,172 @@ +/* Some of the source code in this file came from "linux/fs/fat/fat.h". */ + +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _EXFAT_LINUX_H +#define _EXFAT_LINUX_H + +#include +#include +#include +#include +#include +#include + +#include "exfat_config.h" +#include "exfat_global.h" +#include "exfat_data.h" +#include "exfat_oal.h" + +#include "exfat_blkdev.h" +#include "exfat_cache.h" +#include "exfat_part.h" +#include "exfat_nls.h" +#include "exfat_api.h" +#include "exfat.h" + +#define EXFAT_ERRORS_CONT 1 /* ignore error and continue */ +#define EXFAT_ERRORS_PANIC 2 /* panic on error */ +#define EXFAT_ERRORS_RO 3 /* remount r/o on error */ + +/* ioctl command */ +#define EXFAT_IOCTL_GET_VOLUME_ID _IOR('r', 0x12, __u32) + +struct exfat_mount_options { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + kuid_t fs_uid; + kgid_t fs_gid; +#else + uid_t fs_uid; + gid_t fs_gid; +#endif + unsigned short fs_fmask; + unsigned short fs_dmask; + unsigned short allow_utime; /* permission for setting the [am]time */ + unsigned short codepage; /* codepage for shortname conversions */ + char *iocharset; /* charset for filename input/display */ + unsigned char casesensitive; + unsigned char errors; /* on error: continue, panic, remount-ro */ +#if EXFAT_CONFIG_DISCARD + unsigned char discard; /* flag on if -o dicard specified and device support discard() */ +#endif /* EXFAT_CONFIG_DISCARD */ +}; + +#define EXFAT_HASH_BITS 8 +#define EXFAT_HASH_SIZE (1UL << EXFAT_HASH_BITS) + +/* + * EXFAT file system in-core superblock data + */ +struct exfat_sb_info { + FS_INFO_T fs_info; + BD_INFO_T bd_info; + + struct exfat_mount_options options; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,00) + int s_dirt; + struct mutex s_lock; +#endif + struct nls_table *nls_disk; /* Codepage used on disk */ + struct nls_table *nls_io; /* Charset used for input and display */ + + struct inode *fat_inode; + + spinlock_t inode_hash_lock; + struct hlist_head inode_hashtable[EXFAT_HASH_SIZE]; +#if EXFAT_CONFIG_KERNEL_DEBUG + long debug_flags; +#endif /* EXFAT_CONFIG_KERNEL_DEBUG */ +}; + +/* + * EXFAT file system inode data in memory + */ +struct exfat_inode_info { + FILE_ID_T fid; + char *target; + /* NOTE: mmu_private is 64bits, so must hold ->i_mutex to access */ + loff_t mmu_private; /* physically allocated size */ + loff_t i_pos; /* on-disk position of directory entry or 0 */ + struct hlist_node i_hash_fat; /* hash by i_location */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,00) + struct rw_semaphore truncate_lock; +#endif + struct inode vfs_inode; + struct rw_semaphore i_alloc_sem; /* protect bmap against truncate */ +}; + +#define EXFAT_SB(sb) ((struct exfat_sb_info *)((sb)->s_fs_info)) + +static inline struct exfat_inode_info *EXFAT_I(struct inode *inode) { + return container_of(inode, struct exfat_inode_info, vfs_inode); +} + +/* + * If ->i_mode can't hold S_IWUGO (i.e. ATTR_RO), we use ->i_attrs to + * save ATTR_RO instead of ->i_mode. + * + * If it's directory and !sbi->options.rodir, ATTR_RO isn't read-only + * bit, it's just used as flag for app. + */ +static inline int exfat_mode_can_hold_ro(struct inode *inode) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + + if (S_ISDIR(inode->i_mode)) + return 0; + + if ((~sbi->options.fs_fmask) & S_IWUGO) + return 1; + return 0; +} + +/* Convert attribute bits and a mask to the UNIX mode. */ +static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi, + u32 attr, mode_t mode) +{ + if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR)) + mode &= ~S_IWUGO; + + if (attr & ATTR_SUBDIR) + return (mode & ~sbi->options.fs_dmask) | S_IFDIR; + else if (attr & ATTR_SYMLINK) + return (mode & ~sbi->options.fs_dmask) | S_IFLNK; + else + return (mode & ~sbi->options.fs_fmask) | S_IFREG; +} + +/* Return the FAT attribute byte for this inode */ +static inline u32 exfat_make_attr(struct inode *inode) +{ + if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & S_IWUGO)) + return ((EXFAT_I(inode)->fid.attr) | ATTR_READONLY); + else + return (EXFAT_I(inode)->fid.attr); +} + +static inline void exfat_save_attr(struct inode *inode, u32 attr) +{ + if (exfat_mode_can_hold_ro(inode)) + EXFAT_I(inode)->fid.attr = attr & ATTR_RWMASK; + else + EXFAT_I(inode)->fid.attr = attr & (ATTR_RWMASK | ATTR_READONLY); +} + +#endif /* _EXFAT_LINUX_H */ diff --git a/fs/exfat/exfat_upcase.c b/fs/exfat/exfat_upcase.c new file mode 100644 index 0000000000000..10e48d28b2949 --- /dev/null +++ b/fs/exfat/exfat_upcase.c @@ -0,0 +1,408 @@ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_upcase.c */ +/* PURPOSE : exFAT Up-case Table */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY (Ver 0.9) */ +/* */ +/* - 2010.11.15 [Joosun Hahn] : first writing */ +/* */ +/************************************************************************/ + +#include "exfat_config.h" +#include "exfat_global.h" + +#include "exfat_nls.h" + +UINT8 uni_upcase[NUM_UPCASE<<1] = { + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, + 0x08, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x0B, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x0F, 0x00, + 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, + 0x18, 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1B, 0x00, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x00, 0x1F, 0x00, + 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00, + 0x28, 0x00, 0x29, 0x00, 0x2A, 0x00, 0x2B, 0x00, 0x2C, 0x00, 0x2D, 0x00, 0x2E, 0x00, 0x2F, 0x00, + 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, + 0x38, 0x00, 0x39, 0x00, 0x3A, 0x00, 0x3B, 0x00, 0x3C, 0x00, 0x3D, 0x00, 0x3E, 0x00, 0x3F, 0x00, + 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, + 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, + 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x5B, 0x00, 0x5C, 0x00, 0x5D, 0x00, 0x5E, 0x00, 0x5F, 0x00, + 0x60, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00, + 0x48, 0x00, 0x49, 0x00, 0x4A, 0x00, 0x4B, 0x00, 0x4C, 0x00, 0x4D, 0x00, 0x4E, 0x00, 0x4F, 0x00, + 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x58, 0x00, 0x59, 0x00, 0x5A, 0x00, 0x7B, 0x00, 0x7C, 0x00, 0x7D, 0x00, 0x7E, 0x00, 0x7F, 0x00, + 0x80, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, 0x00, 0x86, 0x00, 0x87, 0x00, + 0x88, 0x00, 0x89, 0x00, 0x8A, 0x00, 0x8B, 0x00, 0x8C, 0x00, 0x8D, 0x00, 0x8E, 0x00, 0x8F, 0x00, + 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, + 0x98, 0x00, 0x99, 0x00, 0x9A, 0x00, 0x9B, 0x00, 0x9C, 0x00, 0x9D, 0x00, 0x9E, 0x00, 0x9F, 0x00, + 0xA0, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA3, 0x00, 0xA4, 0x00, 0xA5, 0x00, 0xA6, 0x00, 0xA7, 0x00, + 0xA8, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0xAB, 0x00, 0xAC, 0x00, 0xAD, 0x00, 0xAE, 0x00, 0xAF, 0x00, + 0xB0, 0x00, 0xB1, 0x00, 0xB2, 0x00, 0xB3, 0x00, 0xB4, 0x00, 0xB5, 0x00, 0xB6, 0x00, 0xB7, 0x00, + 0xB8, 0x00, 0xB9, 0x00, 0xBA, 0x00, 0xBB, 0x00, 0xBC, 0x00, 0xBD, 0x00, 0xBE, 0x00, 0xBF, 0x00, + 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, + 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, + 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xD7, 0x00, + 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0xDF, 0x00, + 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, 0x00, + 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, 0x00, + 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xF7, 0x00, + 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0x78, 0x01, + 0x00, 0x01, 0x00, 0x01, 0x02, 0x01, 0x02, 0x01, 0x04, 0x01, 0x04, 0x01, 0x06, 0x01, 0x06, 0x01, + 0x08, 0x01, 0x08, 0x01, 0x0A, 0x01, 0x0A, 0x01, 0x0C, 0x01, 0x0C, 0x01, 0x0E, 0x01, 0x0E, 0x01, + 0x10, 0x01, 0x10, 0x01, 0x12, 0x01, 0x12, 0x01, 0x14, 0x01, 0x14, 0x01, 0x16, 0x01, 0x16, 0x01, + 0x18, 0x01, 0x18, 0x01, 0x1A, 0x01, 0x1A, 0x01, 0x1C, 0x01, 0x1C, 0x01, 0x1E, 0x01, 0x1E, 0x01, + 0x20, 0x01, 0x20, 0x01, 0x22, 0x01, 0x22, 0x01, 0x24, 0x01, 0x24, 0x01, 0x26, 0x01, 0x26, 0x01, + 0x28, 0x01, 0x28, 0x01, 0x2A, 0x01, 0x2A, 0x01, 0x2C, 0x01, 0x2C, 0x01, 0x2E, 0x01, 0x2E, 0x01, + 0x30, 0x01, 0x31, 0x01, 0x32, 0x01, 0x32, 0x01, 0x34, 0x01, 0x34, 0x01, 0x36, 0x01, 0x36, 0x01, + 0x38, 0x01, 0x39, 0x01, 0x39, 0x01, 0x3B, 0x01, 0x3B, 0x01, 0x3D, 0x01, 0x3D, 0x01, 0x3F, 0x01, + 0x3F, 0x01, 0x41, 0x01, 0x41, 0x01, 0x43, 0x01, 0x43, 0x01, 0x45, 0x01, 0x45, 0x01, 0x47, 0x01, + 0x47, 0x01, 0x49, 0x01, 0x4A, 0x01, 0x4A, 0x01, 0x4C, 0x01, 0x4C, 0x01, 0x4E, 0x01, 0x4E, 0x01, + 0x50, 0x01, 0x50, 0x01, 0x52, 0x01, 0x52, 0x01, 0x54, 0x01, 0x54, 0x01, 0x56, 0x01, 0x56, 0x01, + 0x58, 0x01, 0x58, 0x01, 0x5A, 0x01, 0x5A, 0x01, 0x5C, 0x01, 0x5C, 0x01, 0x5E, 0x01, 0x5E, 0x01, + 0x60, 0x01, 0x60, 0x01, 0x62, 0x01, 0x62, 0x01, 0x64, 0x01, 0x64, 0x01, 0x66, 0x01, 0x66, 0x01, + 0x68, 0x01, 0x68, 0x01, 0x6A, 0x01, 0x6A, 0x01, 0x6C, 0x01, 0x6C, 0x01, 0x6E, 0x01, 0x6E, 0x01, + 0x70, 0x01, 0x70, 0x01, 0x72, 0x01, 0x72, 0x01, 0x74, 0x01, 0x74, 0x01, 0x76, 0x01, 0x76, 0x01, + 0x78, 0x01, 0x79, 0x01, 0x79, 0x01, 0x7B, 0x01, 0x7B, 0x01, 0x7D, 0x01, 0x7D, 0x01, 0x7F, 0x01, + 0x43, 0x02, 0x81, 0x01, 0x82, 0x01, 0x82, 0x01, 0x84, 0x01, 0x84, 0x01, 0x86, 0x01, 0x87, 0x01, + 0x87, 0x01, 0x89, 0x01, 0x8A, 0x01, 0x8B, 0x01, 0x8B, 0x01, 0x8D, 0x01, 0x8E, 0x01, 0x8F, 0x01, + 0x90, 0x01, 0x91, 0x01, 0x91, 0x01, 0x93, 0x01, 0x94, 0x01, 0xF6, 0x01, 0x96, 0x01, 0x97, 0x01, + 0x98, 0x01, 0x98, 0x01, 0x3D, 0x02, 0x9B, 0x01, 0x9C, 0x01, 0x9D, 0x01, 0x20, 0x02, 0x9F, 0x01, + 0xA0, 0x01, 0xA0, 0x01, 0xA2, 0x01, 0xA2, 0x01, 0xA4, 0x01, 0xA4, 0x01, 0xA6, 0x01, 0xA7, 0x01, + 0xA7, 0x01, 0xA9, 0x01, 0xAA, 0x01, 0xAB, 0x01, 0xAC, 0x01, 0xAC, 0x01, 0xAE, 0x01, 0xAF, 0x01, + 0xAF, 0x01, 0xB1, 0x01, 0xB2, 0x01, 0xB3, 0x01, 0xB3, 0x01, 0xB5, 0x01, 0xB5, 0x01, 0xB7, 0x01, + 0xB8, 0x01, 0xB8, 0x01, 0xBA, 0x01, 0xBB, 0x01, 0xBC, 0x01, 0xBC, 0x01, 0xBE, 0x01, 0xF7, 0x01, + 0xC0, 0x01, 0xC1, 0x01, 0xC2, 0x01, 0xC3, 0x01, 0xC4, 0x01, 0xC5, 0x01, 0xC4, 0x01, 0xC7, 0x01, + 0xC8, 0x01, 0xC7, 0x01, 0xCA, 0x01, 0xCB, 0x01, 0xCA, 0x01, 0xCD, 0x01, 0xCD, 0x01, 0xCF, 0x01, + 0xCF, 0x01, 0xD1, 0x01, 0xD1, 0x01, 0xD3, 0x01, 0xD3, 0x01, 0xD5, 0x01, 0xD5, 0x01, 0xD7, 0x01, + 0xD7, 0x01, 0xD9, 0x01, 0xD9, 0x01, 0xDB, 0x01, 0xDB, 0x01, 0x8E, 0x01, 0xDE, 0x01, 0xDE, 0x01, + 0xE0, 0x01, 0xE0, 0x01, 0xE2, 0x01, 0xE2, 0x01, 0xE4, 0x01, 0xE4, 0x01, 0xE6, 0x01, 0xE6, 0x01, + 0xE8, 0x01, 0xE8, 0x01, 0xEA, 0x01, 0xEA, 0x01, 0xEC, 0x01, 0xEC, 0x01, 0xEE, 0x01, 0xEE, 0x01, + 0xF0, 0x01, 0xF1, 0x01, 0xF2, 0x01, 0xF1, 0x01, 0xF4, 0x01, 0xF4, 0x01, 0xF6, 0x01, 0xF7, 0x01, + 0xF8, 0x01, 0xF8, 0x01, 0xFA, 0x01, 0xFA, 0x01, 0xFC, 0x01, 0xFC, 0x01, 0xFE, 0x01, 0xFE, 0x01, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x04, 0x02, 0x04, 0x02, 0x06, 0x02, 0x06, 0x02, + 0x08, 0x02, 0x08, 0x02, 0x0A, 0x02, 0x0A, 0x02, 0x0C, 0x02, 0x0C, 0x02, 0x0E, 0x02, 0x0E, 0x02, + 0x10, 0x02, 0x10, 0x02, 0x12, 0x02, 0x12, 0x02, 0x14, 0x02, 0x14, 0x02, 0x16, 0x02, 0x16, 0x02, + 0x18, 0x02, 0x18, 0x02, 0x1A, 0x02, 0x1A, 0x02, 0x1C, 0x02, 0x1C, 0x02, 0x1E, 0x02, 0x1E, 0x02, + 0x20, 0x02, 0x21, 0x02, 0x22, 0x02, 0x22, 0x02, 0x24, 0x02, 0x24, 0x02, 0x26, 0x02, 0x26, 0x02, + 0x28, 0x02, 0x28, 0x02, 0x2A, 0x02, 0x2A, 0x02, 0x2C, 0x02, 0x2C, 0x02, 0x2E, 0x02, 0x2E, 0x02, + 0x30, 0x02, 0x30, 0x02, 0x32, 0x02, 0x32, 0x02, 0x34, 0x02, 0x35, 0x02, 0x36, 0x02, 0x37, 0x02, + 0x38, 0x02, 0x39, 0x02, 0x65, 0x2C, 0x3B, 0x02, 0x3B, 0x02, 0x3D, 0x02, 0x66, 0x2C, 0x3F, 0x02, + 0x40, 0x02, 0x41, 0x02, 0x41, 0x02, 0x43, 0x02, 0x44, 0x02, 0x45, 0x02, 0x46, 0x02, 0x46, 0x02, + 0x48, 0x02, 0x48, 0x02, 0x4A, 0x02, 0x4A, 0x02, 0x4C, 0x02, 0x4C, 0x02, 0x4E, 0x02, 0x4E, 0x02, + 0x50, 0x02, 0x51, 0x02, 0x52, 0x02, 0x81, 0x01, 0x86, 0x01, 0x55, 0x02, 0x89, 0x01, 0x8A, 0x01, + 0x58, 0x02, 0x8F, 0x01, 0x5A, 0x02, 0x90, 0x01, 0x5C, 0x02, 0x5D, 0x02, 0x5E, 0x02, 0x5F, 0x02, + 0x93, 0x01, 0x61, 0x02, 0x62, 0x02, 0x94, 0x01, 0x64, 0x02, 0x65, 0x02, 0x66, 0x02, 0x67, 0x02, + 0x97, 0x01, 0x96, 0x01, 0x6A, 0x02, 0x62, 0x2C, 0x6C, 0x02, 0x6D, 0x02, 0x6E, 0x02, 0x9C, 0x01, + 0x70, 0x02, 0x71, 0x02, 0x9D, 0x01, 0x73, 0x02, 0x74, 0x02, 0x9F, 0x01, 0x76, 0x02, 0x77, 0x02, + 0x78, 0x02, 0x79, 0x02, 0x7A, 0x02, 0x7B, 0x02, 0x7C, 0x02, 0x64, 0x2C, 0x7E, 0x02, 0x7F, 0x02, + 0xA6, 0x01, 0x81, 0x02, 0x82, 0x02, 0xA9, 0x01, 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x87, 0x02, + 0xAE, 0x01, 0x44, 0x02, 0xB1, 0x01, 0xB2, 0x01, 0x45, 0x02, 0x8D, 0x02, 0x8E, 0x02, 0x8F, 0x02, + 0x90, 0x02, 0x91, 0x02, 0xB7, 0x01, 0x93, 0x02, 0x94, 0x02, 0x95, 0x02, 0x96, 0x02, 0x97, 0x02, + 0x98, 0x02, 0x99, 0x02, 0x9A, 0x02, 0x9B, 0x02, 0x9C, 0x02, 0x9D, 0x02, 0x9E, 0x02, 0x9F, 0x02, + 0xA0, 0x02, 0xA1, 0x02, 0xA2, 0x02, 0xA3, 0x02, 0xA4, 0x02, 0xA5, 0x02, 0xA6, 0x02, 0xA7, 0x02, + 0xA8, 0x02, 0xA9, 0x02, 0xAA, 0x02, 0xAB, 0x02, 0xAC, 0x02, 0xAD, 0x02, 0xAE, 0x02, 0xAF, 0x02, + 0xB0, 0x02, 0xB1, 0x02, 0xB2, 0x02, 0xB3, 0x02, 0xB4, 0x02, 0xB5, 0x02, 0xB6, 0x02, 0xB7, 0x02, + 0xB8, 0x02, 0xB9, 0x02, 0xBA, 0x02, 0xBB, 0x02, 0xBC, 0x02, 0xBD, 0x02, 0xBE, 0x02, 0xBF, 0x02, + 0xC0, 0x02, 0xC1, 0x02, 0xC2, 0x02, 0xC3, 0x02, 0xC4, 0x02, 0xC5, 0x02, 0xC6, 0x02, 0xC7, 0x02, + 0xC8, 0x02, 0xC9, 0x02, 0xCA, 0x02, 0xCB, 0x02, 0xCC, 0x02, 0xCD, 0x02, 0xCE, 0x02, 0xCF, 0x02, + 0xD0, 0x02, 0xD1, 0x02, 0xD2, 0x02, 0xD3, 0x02, 0xD4, 0x02, 0xD5, 0x02, 0xD6, 0x02, 0xD7, 0x02, + 0xD8, 0x02, 0xD9, 0x02, 0xDA, 0x02, 0xDB, 0x02, 0xDC, 0x02, 0xDD, 0x02, 0xDE, 0x02, 0xDF, 0x02, + 0xE0, 0x02, 0xE1, 0x02, 0xE2, 0x02, 0xE3, 0x02, 0xE4, 0x02, 0xE5, 0x02, 0xE6, 0x02, 0xE7, 0x02, + 0xE8, 0x02, 0xE9, 0x02, 0xEA, 0x02, 0xEB, 0x02, 0xEC, 0x02, 0xED, 0x02, 0xEE, 0x02, 0xEF, 0x02, + 0xF0, 0x02, 0xF1, 0x02, 0xF2, 0x02, 0xF3, 0x02, 0xF4, 0x02, 0xF5, 0x02, 0xF6, 0x02, 0xF7, 0x02, + 0xF8, 0x02, 0xF9, 0x02, 0xFA, 0x02, 0xFB, 0x02, 0xFC, 0x02, 0xFD, 0x02, 0xFE, 0x02, 0xFF, 0x02, + 0x00, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x03, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x07, 0x03, + 0x08, 0x03, 0x09, 0x03, 0x0A, 0x03, 0x0B, 0x03, 0x0C, 0x03, 0x0D, 0x03, 0x0E, 0x03, 0x0F, 0x03, + 0x10, 0x03, 0x11, 0x03, 0x12, 0x03, 0x13, 0x03, 0x14, 0x03, 0x15, 0x03, 0x16, 0x03, 0x17, 0x03, + 0x18, 0x03, 0x19, 0x03, 0x1A, 0x03, 0x1B, 0x03, 0x1C, 0x03, 0x1D, 0x03, 0x1E, 0x03, 0x1F, 0x03, + 0x20, 0x03, 0x21, 0x03, 0x22, 0x03, 0x23, 0x03, 0x24, 0x03, 0x25, 0x03, 0x26, 0x03, 0x27, 0x03, + 0x28, 0x03, 0x29, 0x03, 0x2A, 0x03, 0x2B, 0x03, 0x2C, 0x03, 0x2D, 0x03, 0x2E, 0x03, 0x2F, 0x03, + 0x30, 0x03, 0x31, 0x03, 0x32, 0x03, 0x33, 0x03, 0x34, 0x03, 0x35, 0x03, 0x36, 0x03, 0x37, 0x03, + 0x38, 0x03, 0x39, 0x03, 0x3A, 0x03, 0x3B, 0x03, 0x3C, 0x03, 0x3D, 0x03, 0x3E, 0x03, 0x3F, 0x03, + 0x40, 0x03, 0x41, 0x03, 0x42, 0x03, 0x43, 0x03, 0x44, 0x03, 0x45, 0x03, 0x46, 0x03, 0x47, 0x03, + 0x48, 0x03, 0x49, 0x03, 0x4A, 0x03, 0x4B, 0x03, 0x4C, 0x03, 0x4D, 0x03, 0x4E, 0x03, 0x4F, 0x03, + 0x50, 0x03, 0x51, 0x03, 0x52, 0x03, 0x53, 0x03, 0x54, 0x03, 0x55, 0x03, 0x56, 0x03, 0x57, 0x03, + 0x58, 0x03, 0x59, 0x03, 0x5A, 0x03, 0x5B, 0x03, 0x5C, 0x03, 0x5D, 0x03, 0x5E, 0x03, 0x5F, 0x03, + 0x60, 0x03, 0x61, 0x03, 0x62, 0x03, 0x63, 0x03, 0x64, 0x03, 0x65, 0x03, 0x66, 0x03, 0x67, 0x03, + 0x68, 0x03, 0x69, 0x03, 0x6A, 0x03, 0x6B, 0x03, 0x6C, 0x03, 0x6D, 0x03, 0x6E, 0x03, 0x6F, 0x03, + 0x70, 0x03, 0x71, 0x03, 0x72, 0x03, 0x73, 0x03, 0x74, 0x03, 0x75, 0x03, 0x76, 0x03, 0x77, 0x03, + 0x78, 0x03, 0x79, 0x03, 0x7A, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, 0x7E, 0x03, 0x7F, 0x03, + 0x80, 0x03, 0x81, 0x03, 0x82, 0x03, 0x83, 0x03, 0x84, 0x03, 0x85, 0x03, 0x86, 0x03, 0x87, 0x03, + 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, 0x8B, 0x03, 0x8C, 0x03, 0x8D, 0x03, 0x8E, 0x03, 0x8F, 0x03, + 0x90, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, + 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, + 0xA0, 0x03, 0xA1, 0x03, 0xA2, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, + 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x86, 0x03, 0x88, 0x03, 0x89, 0x03, 0x8A, 0x03, + 0xB0, 0x03, 0x91, 0x03, 0x92, 0x03, 0x93, 0x03, 0x94, 0x03, 0x95, 0x03, 0x96, 0x03, 0x97, 0x03, + 0x98, 0x03, 0x99, 0x03, 0x9A, 0x03, 0x9B, 0x03, 0x9C, 0x03, 0x9D, 0x03, 0x9E, 0x03, 0x9F, 0x03, + 0xA0, 0x03, 0xA1, 0x03, 0xA3, 0x03, 0xA3, 0x03, 0xA4, 0x03, 0xA5, 0x03, 0xA6, 0x03, 0xA7, 0x03, + 0xA8, 0x03, 0xA9, 0x03, 0xAA, 0x03, 0xAB, 0x03, 0x8C, 0x03, 0x8E, 0x03, 0x8F, 0x03, 0xCF, 0x03, + 0xD0, 0x03, 0xD1, 0x03, 0xD2, 0x03, 0xD3, 0x03, 0xD4, 0x03, 0xD5, 0x03, 0xD6, 0x03, 0xD7, 0x03, + 0xD8, 0x03, 0xD8, 0x03, 0xDA, 0x03, 0xDA, 0x03, 0xDC, 0x03, 0xDC, 0x03, 0xDE, 0x03, 0xDE, 0x03, + 0xE0, 0x03, 0xE0, 0x03, 0xE2, 0x03, 0xE2, 0x03, 0xE4, 0x03, 0xE4, 0x03, 0xE6, 0x03, 0xE6, 0x03, + 0xE8, 0x03, 0xE8, 0x03, 0xEA, 0x03, 0xEA, 0x03, 0xEC, 0x03, 0xEC, 0x03, 0xEE, 0x03, 0xEE, 0x03, + 0xF0, 0x03, 0xF1, 0x03, 0xF9, 0x03, 0xF3, 0x03, 0xF4, 0x03, 0xF5, 0x03, 0xF6, 0x03, 0xF7, 0x03, + 0xF7, 0x03, 0xF9, 0x03, 0xFA, 0x03, 0xFA, 0x03, 0xFC, 0x03, 0xFD, 0x03, 0xFE, 0x03, 0xFF, 0x03, + 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, + 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, + 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, + 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, + 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, + 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, + 0x10, 0x04, 0x11, 0x04, 0x12, 0x04, 0x13, 0x04, 0x14, 0x04, 0x15, 0x04, 0x16, 0x04, 0x17, 0x04, + 0x18, 0x04, 0x19, 0x04, 0x1A, 0x04, 0x1B, 0x04, 0x1C, 0x04, 0x1D, 0x04, 0x1E, 0x04, 0x1F, 0x04, + 0x20, 0x04, 0x21, 0x04, 0x22, 0x04, 0x23, 0x04, 0x24, 0x04, 0x25, 0x04, 0x26, 0x04, 0x27, 0x04, + 0x28, 0x04, 0x29, 0x04, 0x2A, 0x04, 0x2B, 0x04, 0x2C, 0x04, 0x2D, 0x04, 0x2E, 0x04, 0x2F, 0x04, + 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, + 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x04, 0x0C, 0x04, 0x0D, 0x04, 0x0E, 0x04, 0x0F, 0x04, + 0x60, 0x04, 0x60, 0x04, 0x62, 0x04, 0x62, 0x04, 0x64, 0x04, 0x64, 0x04, 0x66, 0x04, 0x66, 0x04, + 0x68, 0x04, 0x68, 0x04, 0x6A, 0x04, 0x6A, 0x04, 0x6C, 0x04, 0x6C, 0x04, 0x6E, 0x04, 0x6E, 0x04, + 0x70, 0x04, 0x70, 0x04, 0x72, 0x04, 0x72, 0x04, 0x74, 0x04, 0x74, 0x04, 0x76, 0x04, 0x76, 0x04, + 0x78, 0x04, 0x78, 0x04, 0x7A, 0x04, 0x7A, 0x04, 0x7C, 0x04, 0x7C, 0x04, 0x7E, 0x04, 0x7E, 0x04, + 0x80, 0x04, 0x80, 0x04, 0x82, 0x04, 0x83, 0x04, 0x84, 0x04, 0x85, 0x04, 0x86, 0x04, 0x87, 0x04, + 0x88, 0x04, 0x89, 0x04, 0x8A, 0x04, 0x8A, 0x04, 0x8C, 0x04, 0x8C, 0x04, 0x8E, 0x04, 0x8E, 0x04, + 0x90, 0x04, 0x90, 0x04, 0x92, 0x04, 0x92, 0x04, 0x94, 0x04, 0x94, 0x04, 0x96, 0x04, 0x96, 0x04, + 0x98, 0x04, 0x98, 0x04, 0x9A, 0x04, 0x9A, 0x04, 0x9C, 0x04, 0x9C, 0x04, 0x9E, 0x04, 0x9E, 0x04, + 0xA0, 0x04, 0xA0, 0x04, 0xA2, 0x04, 0xA2, 0x04, 0xA4, 0x04, 0xA4, 0x04, 0xA6, 0x04, 0xA6, 0x04, + 0xA8, 0x04, 0xA8, 0x04, 0xAA, 0x04, 0xAA, 0x04, 0xAC, 0x04, 0xAC, 0x04, 0xAE, 0x04, 0xAE, 0x04, + 0xB0, 0x04, 0xB0, 0x04, 0xB2, 0x04, 0xB2, 0x04, 0xB4, 0x04, 0xB4, 0x04, 0xB6, 0x04, 0xB6, 0x04, + 0xB8, 0x04, 0xB8, 0x04, 0xBA, 0x04, 0xBA, 0x04, 0xBC, 0x04, 0xBC, 0x04, 0xBE, 0x04, 0xBE, 0x04, + 0xC0, 0x04, 0xC1, 0x04, 0xC1, 0x04, 0xC3, 0x04, 0xC3, 0x04, 0xC5, 0x04, 0xC5, 0x04, 0xC7, 0x04, + 0xC7, 0x04, 0xC9, 0x04, 0xC9, 0x04, 0xCB, 0x04, 0xCB, 0x04, 0xCD, 0x04, 0xCD, 0x04, 0xC0, 0x04, + 0xD0, 0x04, 0xD0, 0x04, 0xD2, 0x04, 0xD2, 0x04, 0xD4, 0x04, 0xD4, 0x04, 0xD6, 0x04, 0xD6, 0x04, + 0xD8, 0x04, 0xD8, 0x04, 0xDA, 0x04, 0xDA, 0x04, 0xDC, 0x04, 0xDC, 0x04, 0xDE, 0x04, 0xDE, 0x04, + 0xE0, 0x04, 0xE0, 0x04, 0xE2, 0x04, 0xE2, 0x04, 0xE4, 0x04, 0xE4, 0x04, 0xE6, 0x04, 0xE6, 0x04, + 0xE8, 0x04, 0xE8, 0x04, 0xEA, 0x04, 0xEA, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEE, 0x04, 0xEE, 0x04, + 0xF0, 0x04, 0xF0, 0x04, 0xF2, 0x04, 0xF2, 0x04, 0xF4, 0x04, 0xF4, 0x04, 0xF6, 0x04, 0xF6, 0x04, + 0xF8, 0x04, 0xF8, 0x04, 0xFA, 0x04, 0xFA, 0x04, 0xFC, 0x04, 0xFC, 0x04, 0xFE, 0x04, 0xFE, 0x04, + 0x00, 0x05, 0x00, 0x05, 0x02, 0x05, 0x02, 0x05, 0x04, 0x05, 0x04, 0x05, 0x06, 0x05, 0x06, 0x05, + 0x08, 0x05, 0x08, 0x05, 0x0A, 0x05, 0x0A, 0x05, 0x0C, 0x05, 0x0C, 0x05, 0x0E, 0x05, 0x0E, 0x05, + 0x10, 0x05, 0x10, 0x05, 0x12, 0x05, 0x12, 0x05, 0x14, 0x05, 0x15, 0x05, 0x16, 0x05, 0x17, 0x05, + 0x18, 0x05, 0x19, 0x05, 0x1A, 0x05, 0x1B, 0x05, 0x1C, 0x05, 0x1D, 0x05, 0x1E, 0x05, 0x1F, 0x05, + 0x20, 0x05, 0x21, 0x05, 0x22, 0x05, 0x23, 0x05, 0x24, 0x05, 0x25, 0x05, 0x26, 0x05, 0x27, 0x05, + 0x28, 0x05, 0x29, 0x05, 0x2A, 0x05, 0x2B, 0x05, 0x2C, 0x05, 0x2D, 0x05, 0x2E, 0x05, 0x2F, 0x05, + 0x30, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, + 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, + 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, + 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, + 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0x57, 0x05, + 0x58, 0x05, 0x59, 0x05, 0x5A, 0x05, 0x5B, 0x05, 0x5C, 0x05, 0x5D, 0x05, 0x5E, 0x05, 0x5F, 0x05, + 0x60, 0x05, 0x31, 0x05, 0x32, 0x05, 0x33, 0x05, 0x34, 0x05, 0x35, 0x05, 0x36, 0x05, 0x37, 0x05, + 0x38, 0x05, 0x39, 0x05, 0x3A, 0x05, 0x3B, 0x05, 0x3C, 0x05, 0x3D, 0x05, 0x3E, 0x05, 0x3F, 0x05, + 0x40, 0x05, 0x41, 0x05, 0x42, 0x05, 0x43, 0x05, 0x44, 0x05, 0x45, 0x05, 0x46, 0x05, 0x47, 0x05, + 0x48, 0x05, 0x49, 0x05, 0x4A, 0x05, 0x4B, 0x05, 0x4C, 0x05, 0x4D, 0x05, 0x4E, 0x05, 0x4F, 0x05, + 0x50, 0x05, 0x51, 0x05, 0x52, 0x05, 0x53, 0x05, 0x54, 0x05, 0x55, 0x05, 0x56, 0x05, 0xFF, 0xFF, + 0xF6, 0x17, 0x63, 0x2C, 0x7E, 0x1D, 0x7F, 0x1D, 0x80, 0x1D, 0x81, 0x1D, 0x82, 0x1D, 0x83, 0x1D, + 0x84, 0x1D, 0x85, 0x1D, 0x86, 0x1D, 0x87, 0x1D, 0x88, 0x1D, 0x89, 0x1D, 0x8A, 0x1D, 0x8B, 0x1D, + 0x8C, 0x1D, 0x8D, 0x1D, 0x8E, 0x1D, 0x8F, 0x1D, 0x90, 0x1D, 0x91, 0x1D, 0x92, 0x1D, 0x93, 0x1D, + 0x94, 0x1D, 0x95, 0x1D, 0x96, 0x1D, 0x97, 0x1D, 0x98, 0x1D, 0x99, 0x1D, 0x9A, 0x1D, 0x9B, 0x1D, + 0x9C, 0x1D, 0x9D, 0x1D, 0x9E, 0x1D, 0x9F, 0x1D, 0xA0, 0x1D, 0xA1, 0x1D, 0xA2, 0x1D, 0xA3, 0x1D, + 0xA4, 0x1D, 0xA5, 0x1D, 0xA6, 0x1D, 0xA7, 0x1D, 0xA8, 0x1D, 0xA9, 0x1D, 0xAA, 0x1D, 0xAB, 0x1D, + 0xAC, 0x1D, 0xAD, 0x1D, 0xAE, 0x1D, 0xAF, 0x1D, 0xB0, 0x1D, 0xB1, 0x1D, 0xB2, 0x1D, 0xB3, 0x1D, + 0xB4, 0x1D, 0xB5, 0x1D, 0xB6, 0x1D, 0xB7, 0x1D, 0xB8, 0x1D, 0xB9, 0x1D, 0xBA, 0x1D, 0xBB, 0x1D, + 0xBC, 0x1D, 0xBD, 0x1D, 0xBE, 0x1D, 0xBF, 0x1D, 0xC0, 0x1D, 0xC1, 0x1D, 0xC2, 0x1D, 0xC3, 0x1D, + 0xC4, 0x1D, 0xC5, 0x1D, 0xC6, 0x1D, 0xC7, 0x1D, 0xC8, 0x1D, 0xC9, 0x1D, 0xCA, 0x1D, 0xCB, 0x1D, + 0xCC, 0x1D, 0xCD, 0x1D, 0xCE, 0x1D, 0xCF, 0x1D, 0xD0, 0x1D, 0xD1, 0x1D, 0xD2, 0x1D, 0xD3, 0x1D, + 0xD4, 0x1D, 0xD5, 0x1D, 0xD6, 0x1D, 0xD7, 0x1D, 0xD8, 0x1D, 0xD9, 0x1D, 0xDA, 0x1D, 0xDB, 0x1D, + 0xDC, 0x1D, 0xDD, 0x1D, 0xDE, 0x1D, 0xDF, 0x1D, 0xE0, 0x1D, 0xE1, 0x1D, 0xE2, 0x1D, 0xE3, 0x1D, + 0xE4, 0x1D, 0xE5, 0x1D, 0xE6, 0x1D, 0xE7, 0x1D, 0xE8, 0x1D, 0xE9, 0x1D, 0xEA, 0x1D, 0xEB, 0x1D, + 0xEC, 0x1D, 0xED, 0x1D, 0xEE, 0x1D, 0xEF, 0x1D, 0xF0, 0x1D, 0xF1, 0x1D, 0xF2, 0x1D, 0xF3, 0x1D, + 0xF4, 0x1D, 0xF5, 0x1D, 0xF6, 0x1D, 0xF7, 0x1D, 0xF8, 0x1D, 0xF9, 0x1D, 0xFA, 0x1D, 0xFB, 0x1D, + 0xFC, 0x1D, 0xFD, 0x1D, 0xFE, 0x1D, 0xFF, 0x1D, 0x00, 0x1E, 0x00, 0x1E, 0x02, 0x1E, 0x02, 0x1E, + 0x04, 0x1E, 0x04, 0x1E, 0x06, 0x1E, 0x06, 0x1E, 0x08, 0x1E, 0x08, 0x1E, 0x0A, 0x1E, 0x0A, 0x1E, + 0x0C, 0x1E, 0x0C, 0x1E, 0x0E, 0x1E, 0x0E, 0x1E, 0x10, 0x1E, 0x10, 0x1E, 0x12, 0x1E, 0x12, 0x1E, + 0x14, 0x1E, 0x14, 0x1E, 0x16, 0x1E, 0x16, 0x1E, 0x18, 0x1E, 0x18, 0x1E, 0x1A, 0x1E, 0x1A, 0x1E, + 0x1C, 0x1E, 0x1C, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x20, 0x1E, 0x20, 0x1E, 0x22, 0x1E, 0x22, 0x1E, + 0x24, 0x1E, 0x24, 0x1E, 0x26, 0x1E, 0x26, 0x1E, 0x28, 0x1E, 0x28, 0x1E, 0x2A, 0x1E, 0x2A, 0x1E, + 0x2C, 0x1E, 0x2C, 0x1E, 0x2E, 0x1E, 0x2E, 0x1E, 0x30, 0x1E, 0x30, 0x1E, 0x32, 0x1E, 0x32, 0x1E, + 0x34, 0x1E, 0x34, 0x1E, 0x36, 0x1E, 0x36, 0x1E, 0x38, 0x1E, 0x38, 0x1E, 0x3A, 0x1E, 0x3A, 0x1E, + 0x3C, 0x1E, 0x3C, 0x1E, 0x3E, 0x1E, 0x3E, 0x1E, 0x40, 0x1E, 0x40, 0x1E, 0x42, 0x1E, 0x42, 0x1E, + 0x44, 0x1E, 0x44, 0x1E, 0x46, 0x1E, 0x46, 0x1E, 0x48, 0x1E, 0x48, 0x1E, 0x4A, 0x1E, 0x4A, 0x1E, + 0x4C, 0x1E, 0x4C, 0x1E, 0x4E, 0x1E, 0x4E, 0x1E, 0x50, 0x1E, 0x50, 0x1E, 0x52, 0x1E, 0x52, 0x1E, + 0x54, 0x1E, 0x54, 0x1E, 0x56, 0x1E, 0x56, 0x1E, 0x58, 0x1E, 0x58, 0x1E, 0x5A, 0x1E, 0x5A, 0x1E, + 0x5C, 0x1E, 0x5C, 0x1E, 0x5E, 0x1E, 0x5E, 0x1E, 0x60, 0x1E, 0x60, 0x1E, 0x62, 0x1E, 0x62, 0x1E, + 0x64, 0x1E, 0x64, 0x1E, 0x66, 0x1E, 0x66, 0x1E, 0x68, 0x1E, 0x68, 0x1E, 0x6A, 0x1E, 0x6A, 0x1E, + 0x6C, 0x1E, 0x6C, 0x1E, 0x6E, 0x1E, 0x6E, 0x1E, 0x70, 0x1E, 0x70, 0x1E, 0x72, 0x1E, 0x72, 0x1E, + 0x74, 0x1E, 0x74, 0x1E, 0x76, 0x1E, 0x76, 0x1E, 0x78, 0x1E, 0x78, 0x1E, 0x7A, 0x1E, 0x7A, 0x1E, + 0x7C, 0x1E, 0x7C, 0x1E, 0x7E, 0x1E, 0x7E, 0x1E, 0x80, 0x1E, 0x80, 0x1E, 0x82, 0x1E, 0x82, 0x1E, + 0x84, 0x1E, 0x84, 0x1E, 0x86, 0x1E, 0x86, 0x1E, 0x88, 0x1E, 0x88, 0x1E, 0x8A, 0x1E, 0x8A, 0x1E, + 0x8C, 0x1E, 0x8C, 0x1E, 0x8E, 0x1E, 0x8E, 0x1E, 0x90, 0x1E, 0x90, 0x1E, 0x92, 0x1E, 0x92, 0x1E, + 0x94, 0x1E, 0x94, 0x1E, 0x96, 0x1E, 0x97, 0x1E, 0x98, 0x1E, 0x99, 0x1E, 0x9A, 0x1E, 0x9B, 0x1E, + 0x9C, 0x1E, 0x9D, 0x1E, 0x9E, 0x1E, 0x9F, 0x1E, 0xA0, 0x1E, 0xA0, 0x1E, 0xA2, 0x1E, 0xA2, 0x1E, + 0xA4, 0x1E, 0xA4, 0x1E, 0xA6, 0x1E, 0xA6, 0x1E, 0xA8, 0x1E, 0xA8, 0x1E, 0xAA, 0x1E, 0xAA, 0x1E, + 0xAC, 0x1E, 0xAC, 0x1E, 0xAE, 0x1E, 0xAE, 0x1E, 0xB0, 0x1E, 0xB0, 0x1E, 0xB2, 0x1E, 0xB2, 0x1E, + 0xB4, 0x1E, 0xB4, 0x1E, 0xB6, 0x1E, 0xB6, 0x1E, 0xB8, 0x1E, 0xB8, 0x1E, 0xBA, 0x1E, 0xBA, 0x1E, + 0xBC, 0x1E, 0xBC, 0x1E, 0xBE, 0x1E, 0xBE, 0x1E, 0xC0, 0x1E, 0xC0, 0x1E, 0xC2, 0x1E, 0xC2, 0x1E, + 0xC4, 0x1E, 0xC4, 0x1E, 0xC6, 0x1E, 0xC6, 0x1E, 0xC8, 0x1E, 0xC8, 0x1E, 0xCA, 0x1E, 0xCA, 0x1E, + 0xCC, 0x1E, 0xCC, 0x1E, 0xCE, 0x1E, 0xCE, 0x1E, 0xD0, 0x1E, 0xD0, 0x1E, 0xD2, 0x1E, 0xD2, 0x1E, + 0xD4, 0x1E, 0xD4, 0x1E, 0xD6, 0x1E, 0xD6, 0x1E, 0xD8, 0x1E, 0xD8, 0x1E, 0xDA, 0x1E, 0xDA, 0x1E, + 0xDC, 0x1E, 0xDC, 0x1E, 0xDE, 0x1E, 0xDE, 0x1E, 0xE0, 0x1E, 0xE0, 0x1E, 0xE2, 0x1E, 0xE2, 0x1E, + 0xE4, 0x1E, 0xE4, 0x1E, 0xE6, 0x1E, 0xE6, 0x1E, 0xE8, 0x1E, 0xE8, 0x1E, 0xEA, 0x1E, 0xEA, 0x1E, + 0xEC, 0x1E, 0xEC, 0x1E, 0xEE, 0x1E, 0xEE, 0x1E, 0xF0, 0x1E, 0xF0, 0x1E, 0xF2, 0x1E, 0xF2, 0x1E, + 0xF4, 0x1E, 0xF4, 0x1E, 0xF6, 0x1E, 0xF6, 0x1E, 0xF8, 0x1E, 0xF8, 0x1E, 0xFA, 0x1E, 0xFB, 0x1E, + 0xFC, 0x1E, 0xFD, 0x1E, 0xFE, 0x1E, 0xFF, 0x1E, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, + 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x08, 0x1F, 0x09, 0x1F, 0x0A, 0x1F, 0x0B, 0x1F, + 0x0C, 0x1F, 0x0D, 0x1F, 0x0E, 0x1F, 0x0F, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, + 0x1C, 0x1F, 0x1D, 0x1F, 0x16, 0x1F, 0x17, 0x1F, 0x18, 0x1F, 0x19, 0x1F, 0x1A, 0x1F, 0x1B, 0x1F, + 0x1C, 0x1F, 0x1D, 0x1F, 0x1E, 0x1F, 0x1F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, + 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x28, 0x1F, 0x29, 0x1F, 0x2A, 0x1F, 0x2B, 0x1F, + 0x2C, 0x1F, 0x2D, 0x1F, 0x2E, 0x1F, 0x2F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, + 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x38, 0x1F, 0x39, 0x1F, 0x3A, 0x1F, 0x3B, 0x1F, + 0x3C, 0x1F, 0x3D, 0x1F, 0x3E, 0x1F, 0x3F, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, + 0x4C, 0x1F, 0x4D, 0x1F, 0x46, 0x1F, 0x47, 0x1F, 0x48, 0x1F, 0x49, 0x1F, 0x4A, 0x1F, 0x4B, 0x1F, + 0x4C, 0x1F, 0x4D, 0x1F, 0x4E, 0x1F, 0x4F, 0x1F, 0x50, 0x1F, 0x59, 0x1F, 0x52, 0x1F, 0x5B, 0x1F, + 0x54, 0x1F, 0x5D, 0x1F, 0x56, 0x1F, 0x5F, 0x1F, 0x58, 0x1F, 0x59, 0x1F, 0x5A, 0x1F, 0x5B, 0x1F, + 0x5C, 0x1F, 0x5D, 0x1F, 0x5E, 0x1F, 0x5F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, + 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0x68, 0x1F, 0x69, 0x1F, 0x6A, 0x1F, 0x6B, 0x1F, + 0x6C, 0x1F, 0x6D, 0x1F, 0x6E, 0x1F, 0x6F, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, + 0xCA, 0x1F, 0xCB, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, + 0xFA, 0x1F, 0xFB, 0x1F, 0x7E, 0x1F, 0x7F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, + 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x88, 0x1F, 0x89, 0x1F, 0x8A, 0x1F, 0x8B, 0x1F, + 0x8C, 0x1F, 0x8D, 0x1F, 0x8E, 0x1F, 0x8F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, + 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0x98, 0x1F, 0x99, 0x1F, 0x9A, 0x1F, 0x9B, 0x1F, + 0x9C, 0x1F, 0x9D, 0x1F, 0x9E, 0x1F, 0x9F, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, + 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xA8, 0x1F, 0xA9, 0x1F, 0xAA, 0x1F, 0xAB, 0x1F, + 0xAC, 0x1F, 0xAD, 0x1F, 0xAE, 0x1F, 0xAF, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xB2, 0x1F, 0xBC, 0x1F, + 0xB4, 0x1F, 0xB5, 0x1F, 0xB6, 0x1F, 0xB7, 0x1F, 0xB8, 0x1F, 0xB9, 0x1F, 0xBA, 0x1F, 0xBB, 0x1F, + 0xBC, 0x1F, 0xBD, 0x1F, 0xBE, 0x1F, 0xBF, 0x1F, 0xC0, 0x1F, 0xC1, 0x1F, 0xC2, 0x1F, 0xC3, 0x1F, + 0xC4, 0x1F, 0xC5, 0x1F, 0xC6, 0x1F, 0xC7, 0x1F, 0xC8, 0x1F, 0xC9, 0x1F, 0xCA, 0x1F, 0xCB, 0x1F, + 0xC3, 0x1F, 0xCD, 0x1F, 0xCE, 0x1F, 0xCF, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xD2, 0x1F, 0xD3, 0x1F, + 0xD4, 0x1F, 0xD5, 0x1F, 0xD6, 0x1F, 0xD7, 0x1F, 0xD8, 0x1F, 0xD9, 0x1F, 0xDA, 0x1F, 0xDB, 0x1F, + 0xDC, 0x1F, 0xDD, 0x1F, 0xDE, 0x1F, 0xDF, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xE2, 0x1F, 0xE3, 0x1F, + 0xE4, 0x1F, 0xEC, 0x1F, 0xE6, 0x1F, 0xE7, 0x1F, 0xE8, 0x1F, 0xE9, 0x1F, 0xEA, 0x1F, 0xEB, 0x1F, + 0xEC, 0x1F, 0xED, 0x1F, 0xEE, 0x1F, 0xEF, 0x1F, 0xF0, 0x1F, 0xF1, 0x1F, 0xF2, 0x1F, 0xF3, 0x1F, + 0xF4, 0x1F, 0xF5, 0x1F, 0xF6, 0x1F, 0xF7, 0x1F, 0xF8, 0x1F, 0xF9, 0x1F, 0xFA, 0x1F, 0xFB, 0x1F, + 0xF3, 0x1F, 0xFD, 0x1F, 0xFE, 0x1F, 0xFF, 0x1F, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x20, + 0x04, 0x20, 0x05, 0x20, 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x09, 0x20, 0x0A, 0x20, 0x0B, 0x20, + 0x0C, 0x20, 0x0D, 0x20, 0x0E, 0x20, 0x0F, 0x20, 0x10, 0x20, 0x11, 0x20, 0x12, 0x20, 0x13, 0x20, + 0x14, 0x20, 0x15, 0x20, 0x16, 0x20, 0x17, 0x20, 0x18, 0x20, 0x19, 0x20, 0x1A, 0x20, 0x1B, 0x20, + 0x1C, 0x20, 0x1D, 0x20, 0x1E, 0x20, 0x1F, 0x20, 0x20, 0x20, 0x21, 0x20, 0x22, 0x20, 0x23, 0x20, + 0x24, 0x20, 0x25, 0x20, 0x26, 0x20, 0x27, 0x20, 0x28, 0x20, 0x29, 0x20, 0x2A, 0x20, 0x2B, 0x20, + 0x2C, 0x20, 0x2D, 0x20, 0x2E, 0x20, 0x2F, 0x20, 0x30, 0x20, 0x31, 0x20, 0x32, 0x20, 0x33, 0x20, + 0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20, 0x38, 0x20, 0x39, 0x20, 0x3A, 0x20, 0x3B, 0x20, + 0x3C, 0x20, 0x3D, 0x20, 0x3E, 0x20, 0x3F, 0x20, 0x40, 0x20, 0x41, 0x20, 0x42, 0x20, 0x43, 0x20, + 0x44, 0x20, 0x45, 0x20, 0x46, 0x20, 0x47, 0x20, 0x48, 0x20, 0x49, 0x20, 0x4A, 0x20, 0x4B, 0x20, + 0x4C, 0x20, 0x4D, 0x20, 0x4E, 0x20, 0x4F, 0x20, 0x50, 0x20, 0x51, 0x20, 0x52, 0x20, 0x53, 0x20, + 0x54, 0x20, 0x55, 0x20, 0x56, 0x20, 0x57, 0x20, 0x58, 0x20, 0x59, 0x20, 0x5A, 0x20, 0x5B, 0x20, + 0x5C, 0x20, 0x5D, 0x20, 0x5E, 0x20, 0x5F, 0x20, 0x60, 0x20, 0x61, 0x20, 0x62, 0x20, 0x63, 0x20, + 0x64, 0x20, 0x65, 0x20, 0x66, 0x20, 0x67, 0x20, 0x68, 0x20, 0x69, 0x20, 0x6A, 0x20, 0x6B, 0x20, + 0x6C, 0x20, 0x6D, 0x20, 0x6E, 0x20, 0x6F, 0x20, 0x70, 0x20, 0x71, 0x20, 0x72, 0x20, 0x73, 0x20, + 0x74, 0x20, 0x75, 0x20, 0x76, 0x20, 0x77, 0x20, 0x78, 0x20, 0x79, 0x20, 0x7A, 0x20, 0x7B, 0x20, + 0x7C, 0x20, 0x7D, 0x20, 0x7E, 0x20, 0x7F, 0x20, 0x80, 0x20, 0x81, 0x20, 0x82, 0x20, 0x83, 0x20, + 0x84, 0x20, 0x85, 0x20, 0x86, 0x20, 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x8A, 0x20, 0x8B, 0x20, + 0x8C, 0x20, 0x8D, 0x20, 0x8E, 0x20, 0x8F, 0x20, 0x90, 0x20, 0x91, 0x20, 0x92, 0x20, 0x93, 0x20, + 0x94, 0x20, 0x95, 0x20, 0x96, 0x20, 0x97, 0x20, 0x98, 0x20, 0x99, 0x20, 0x9A, 0x20, 0x9B, 0x20, + 0x9C, 0x20, 0x9D, 0x20, 0x9E, 0x20, 0x9F, 0x20, 0xA0, 0x20, 0xA1, 0x20, 0xA2, 0x20, 0xA3, 0x20, + 0xA4, 0x20, 0xA5, 0x20, 0xA6, 0x20, 0xA7, 0x20, 0xA8, 0x20, 0xA9, 0x20, 0xAA, 0x20, 0xAB, 0x20, + 0xAC, 0x20, 0xAD, 0x20, 0xAE, 0x20, 0xAF, 0x20, 0xB0, 0x20, 0xB1, 0x20, 0xB2, 0x20, 0xB3, 0x20, + 0xB4, 0x20, 0xB5, 0x20, 0xB6, 0x20, 0xB7, 0x20, 0xB8, 0x20, 0xB9, 0x20, 0xBA, 0x20, 0xBB, 0x20, + 0xBC, 0x20, 0xBD, 0x20, 0xBE, 0x20, 0xBF, 0x20, 0xC0, 0x20, 0xC1, 0x20, 0xC2, 0x20, 0xC3, 0x20, + 0xC4, 0x20, 0xC5, 0x20, 0xC6, 0x20, 0xC7, 0x20, 0xC8, 0x20, 0xC9, 0x20, 0xCA, 0x20, 0xCB, 0x20, + 0xCC, 0x20, 0xCD, 0x20, 0xCE, 0x20, 0xCF, 0x20, 0xD0, 0x20, 0xD1, 0x20, 0xD2, 0x20, 0xD3, 0x20, + 0xD4, 0x20, 0xD5, 0x20, 0xD6, 0x20, 0xD7, 0x20, 0xD8, 0x20, 0xD9, 0x20, 0xDA, 0x20, 0xDB, 0x20, + 0xDC, 0x20, 0xDD, 0x20, 0xDE, 0x20, 0xDF, 0x20, 0xE0, 0x20, 0xE1, 0x20, 0xE2, 0x20, 0xE3, 0x20, + 0xE4, 0x20, 0xE5, 0x20, 0xE6, 0x20, 0xE7, 0x20, 0xE8, 0x20, 0xE9, 0x20, 0xEA, 0x20, 0xEB, 0x20, + 0xEC, 0x20, 0xED, 0x20, 0xEE, 0x20, 0xEF, 0x20, 0xF0, 0x20, 0xF1, 0x20, 0xF2, 0x20, 0xF3, 0x20, + 0xF4, 0x20, 0xF5, 0x20, 0xF6, 0x20, 0xF7, 0x20, 0xF8, 0x20, 0xF9, 0x20, 0xFA, 0x20, 0xFB, 0x20, + 0xFC, 0x20, 0xFD, 0x20, 0xFE, 0x20, 0xFF, 0x20, 0x00, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03, 0x21, + 0x04, 0x21, 0x05, 0x21, 0x06, 0x21, 0x07, 0x21, 0x08, 0x21, 0x09, 0x21, 0x0A, 0x21, 0x0B, 0x21, + 0x0C, 0x21, 0x0D, 0x21, 0x0E, 0x21, 0x0F, 0x21, 0x10, 0x21, 0x11, 0x21, 0x12, 0x21, 0x13, 0x21, + 0x14, 0x21, 0x15, 0x21, 0x16, 0x21, 0x17, 0x21, 0x18, 0x21, 0x19, 0x21, 0x1A, 0x21, 0x1B, 0x21, + 0x1C, 0x21, 0x1D, 0x21, 0x1E, 0x21, 0x1F, 0x21, 0x20, 0x21, 0x21, 0x21, 0x22, 0x21, 0x23, 0x21, + 0x24, 0x21, 0x25, 0x21, 0x26, 0x21, 0x27, 0x21, 0x28, 0x21, 0x29, 0x21, 0x2A, 0x21, 0x2B, 0x21, + 0x2C, 0x21, 0x2D, 0x21, 0x2E, 0x21, 0x2F, 0x21, 0x30, 0x21, 0x31, 0x21, 0x32, 0x21, 0x33, 0x21, + 0x34, 0x21, 0x35, 0x21, 0x36, 0x21, 0x37, 0x21, 0x38, 0x21, 0x39, 0x21, 0x3A, 0x21, 0x3B, 0x21, + 0x3C, 0x21, 0x3D, 0x21, 0x3E, 0x21, 0x3F, 0x21, 0x40, 0x21, 0x41, 0x21, 0x42, 0x21, 0x43, 0x21, + 0x44, 0x21, 0x45, 0x21, 0x46, 0x21, 0x47, 0x21, 0x48, 0x21, 0x49, 0x21, 0x4A, 0x21, 0x4B, 0x21, + 0x4C, 0x21, 0x4D, 0x21, 0x32, 0x21, 0x4F, 0x21, 0x50, 0x21, 0x51, 0x21, 0x52, 0x21, 0x53, 0x21, + 0x54, 0x21, 0x55, 0x21, 0x56, 0x21, 0x57, 0x21, 0x58, 0x21, 0x59, 0x21, 0x5A, 0x21, 0x5B, 0x21, + 0x5C, 0x21, 0x5D, 0x21, 0x5E, 0x21, 0x5F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, + 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, + 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x60, 0x21, 0x61, 0x21, 0x62, 0x21, 0x63, 0x21, + 0x64, 0x21, 0x65, 0x21, 0x66, 0x21, 0x67, 0x21, 0x68, 0x21, 0x69, 0x21, 0x6A, 0x21, 0x6B, 0x21, + 0x6C, 0x21, 0x6D, 0x21, 0x6E, 0x21, 0x6F, 0x21, 0x80, 0x21, 0x81, 0x21, 0x82, 0x21, 0x83, 0x21, + 0x83, 0x21, 0xFF, 0xFF, 0x4B, 0x03, 0xB6, 0x24, 0xB7, 0x24, 0xB8, 0x24, 0xB9, 0x24, 0xBA, 0x24, + 0xBB, 0x24, 0xBC, 0x24, 0xBD, 0x24, 0xBE, 0x24, 0xBF, 0x24, 0xC0, 0x24, 0xC1, 0x24, 0xC2, 0x24, + 0xC3, 0x24, 0xC4, 0x24, 0xC5, 0x24, 0xC6, 0x24, 0xC7, 0x24, 0xC8, 0x24, 0xC9, 0x24, 0xCA, 0x24, + 0xCB, 0x24, 0xCC, 0x24, 0xCD, 0x24, 0xCE, 0x24, 0xCF, 0x24, 0xFF, 0xFF, 0x46, 0x07, 0x00, 0x2C, + 0x01, 0x2C, 0x02, 0x2C, 0x03, 0x2C, 0x04, 0x2C, 0x05, 0x2C, 0x06, 0x2C, 0x07, 0x2C, 0x08, 0x2C, + 0x09, 0x2C, 0x0A, 0x2C, 0x0B, 0x2C, 0x0C, 0x2C, 0x0D, 0x2C, 0x0E, 0x2C, 0x0F, 0x2C, 0x10, 0x2C, + 0x11, 0x2C, 0x12, 0x2C, 0x13, 0x2C, 0x14, 0x2C, 0x15, 0x2C, 0x16, 0x2C, 0x17, 0x2C, 0x18, 0x2C, + 0x19, 0x2C, 0x1A, 0x2C, 0x1B, 0x2C, 0x1C, 0x2C, 0x1D, 0x2C, 0x1E, 0x2C, 0x1F, 0x2C, 0x20, 0x2C, + 0x21, 0x2C, 0x22, 0x2C, 0x23, 0x2C, 0x24, 0x2C, 0x25, 0x2C, 0x26, 0x2C, 0x27, 0x2C, 0x28, 0x2C, + 0x29, 0x2C, 0x2A, 0x2C, 0x2B, 0x2C, 0x2C, 0x2C, 0x2D, 0x2C, 0x2E, 0x2C, 0x5F, 0x2C, 0x60, 0x2C, + 0x60, 0x2C, 0x62, 0x2C, 0x63, 0x2C, 0x64, 0x2C, 0x65, 0x2C, 0x66, 0x2C, 0x67, 0x2C, 0x67, 0x2C, + 0x69, 0x2C, 0x69, 0x2C, 0x6B, 0x2C, 0x6B, 0x2C, 0x6D, 0x2C, 0x6E, 0x2C, 0x6F, 0x2C, 0x70, 0x2C, + 0x71, 0x2C, 0x72, 0x2C, 0x73, 0x2C, 0x74, 0x2C, 0x75, 0x2C, 0x75, 0x2C, 0x77, 0x2C, 0x78, 0x2C, + 0x79, 0x2C, 0x7A, 0x2C, 0x7B, 0x2C, 0x7C, 0x2C, 0x7D, 0x2C, 0x7E, 0x2C, 0x7F, 0x2C, 0x80, 0x2C, + 0x80, 0x2C, 0x82, 0x2C, 0x82, 0x2C, 0x84, 0x2C, 0x84, 0x2C, 0x86, 0x2C, 0x86, 0x2C, 0x88, 0x2C, + 0x88, 0x2C, 0x8A, 0x2C, 0x8A, 0x2C, 0x8C, 0x2C, 0x8C, 0x2C, 0x8E, 0x2C, 0x8E, 0x2C, 0x90, 0x2C, + 0x90, 0x2C, 0x92, 0x2C, 0x92, 0x2C, 0x94, 0x2C, 0x94, 0x2C, 0x96, 0x2C, 0x96, 0x2C, 0x98, 0x2C, + 0x98, 0x2C, 0x9A, 0x2C, 0x9A, 0x2C, 0x9C, 0x2C, 0x9C, 0x2C, 0x9E, 0x2C, 0x9E, 0x2C, 0xA0, 0x2C, + 0xA0, 0x2C, 0xA2, 0x2C, 0xA2, 0x2C, 0xA4, 0x2C, 0xA4, 0x2C, 0xA6, 0x2C, 0xA6, 0x2C, 0xA8, 0x2C, + 0xA8, 0x2C, 0xAA, 0x2C, 0xAA, 0x2C, 0xAC, 0x2C, 0xAC, 0x2C, 0xAE, 0x2C, 0xAE, 0x2C, 0xB0, 0x2C, + 0xB0, 0x2C, 0xB2, 0x2C, 0xB2, 0x2C, 0xB4, 0x2C, 0xB4, 0x2C, 0xB6, 0x2C, 0xB6, 0x2C, 0xB8, 0x2C, + 0xB8, 0x2C, 0xBA, 0x2C, 0xBA, 0x2C, 0xBC, 0x2C, 0xBC, 0x2C, 0xBE, 0x2C, 0xBE, 0x2C, 0xC0, 0x2C, + 0xC0, 0x2C, 0xC2, 0x2C, 0xC2, 0x2C, 0xC4, 0x2C, 0xC4, 0x2C, 0xC6, 0x2C, 0xC6, 0x2C, 0xC8, 0x2C, + 0xC8, 0x2C, 0xCA, 0x2C, 0xCA, 0x2C, 0xCC, 0x2C, 0xCC, 0x2C, 0xCE, 0x2C, 0xCE, 0x2C, 0xD0, 0x2C, + 0xD0, 0x2C, 0xD2, 0x2C, 0xD2, 0x2C, 0xD4, 0x2C, 0xD4, 0x2C, 0xD6, 0x2C, 0xD6, 0x2C, 0xD8, 0x2C, + 0xD8, 0x2C, 0xDA, 0x2C, 0xDA, 0x2C, 0xDC, 0x2C, 0xDC, 0x2C, 0xDE, 0x2C, 0xDE, 0x2C, 0xE0, 0x2C, + 0xE0, 0x2C, 0xE2, 0x2C, 0xE2, 0x2C, 0xE4, 0x2C, 0xE5, 0x2C, 0xE6, 0x2C, 0xE7, 0x2C, 0xE8, 0x2C, + 0xE9, 0x2C, 0xEA, 0x2C, 0xEB, 0x2C, 0xEC, 0x2C, 0xED, 0x2C, 0xEE, 0x2C, 0xEF, 0x2C, 0xF0, 0x2C, + 0xF1, 0x2C, 0xF2, 0x2C, 0xF3, 0x2C, 0xF4, 0x2C, 0xF5, 0x2C, 0xF6, 0x2C, 0xF7, 0x2C, 0xF8, 0x2C, + 0xF9, 0x2C, 0xFA, 0x2C, 0xFB, 0x2C, 0xFC, 0x2C, 0xFD, 0x2C, 0xFE, 0x2C, 0xFF, 0x2C, 0xA0, 0x10, + 0xA1, 0x10, 0xA2, 0x10, 0xA3, 0x10, 0xA4, 0x10, 0xA5, 0x10, 0xA6, 0x10, 0xA7, 0x10, 0xA8, 0x10, + 0xA9, 0x10, 0xAA, 0x10, 0xAB, 0x10, 0xAC, 0x10, 0xAD, 0x10, 0xAE, 0x10, 0xAF, 0x10, 0xB0, 0x10, + 0xB1, 0x10, 0xB2, 0x10, 0xB3, 0x10, 0xB4, 0x10, 0xB5, 0x10, 0xB6, 0x10, 0xB7, 0x10, 0xB8, 0x10, + 0xB9, 0x10, 0xBA, 0x10, 0xBB, 0x10, 0xBC, 0x10, 0xBD, 0x10, 0xBE, 0x10, 0xBF, 0x10, 0xC0, 0x10, + 0xC1, 0x10, 0xC2, 0x10, 0xC3, 0x10, 0xC4, 0x10, 0xC5, 0x10, 0xFF, 0xFF, 0x1B, 0xD2, 0x21, 0xFF, + 0x22, 0xFF, 0x23, 0xFF, 0x24, 0xFF, 0x25, 0xFF, 0x26, 0xFF, 0x27, 0xFF, 0x28, 0xFF, 0x29, 0xFF, + 0x2A, 0xFF, 0x2B, 0xFF, 0x2C, 0xFF, 0x2D, 0xFF, 0x2E, 0xFF, 0x2F, 0xFF, 0x30, 0xFF, 0x31, 0xFF, + 0x32, 0xFF, 0x33, 0xFF, 0x34, 0xFF, 0x35, 0xFF, 0x36, 0xFF, 0x37, 0xFF, 0x38, 0xFF, 0x39, 0xFF, + 0x3A, 0xFF, 0x5B, 0xFF, 0x5C, 0xFF, 0x5D, 0xFF, 0x5E, 0xFF, 0x5F, 0xFF, 0x60, 0xFF, 0x61, 0xFF, + 0x62, 0xFF, 0x63, 0xFF, 0x64, 0xFF, 0x65, 0xFF, 0x66, 0xFF, 0x67, 0xFF, 0x68, 0xFF, 0x69, 0xFF, + 0x6A, 0xFF, 0x6B, 0xFF, 0x6C, 0xFF, 0x6D, 0xFF, 0x6E, 0xFF, 0x6F, 0xFF, 0x70, 0xFF, 0x71, 0xFF, + 0x72, 0xFF, 0x73, 0xFF, 0x74, 0xFF, 0x75, 0xFF, 0x76, 0xFF, 0x77, 0xFF, 0x78, 0xFF, 0x79, 0xFF, + 0x7A, 0xFF, 0x7B, 0xFF, 0x7C, 0xFF, 0x7D, 0xFF, 0x7E, 0xFF, 0x7F, 0xFF, 0x80, 0xFF, 0x81, 0xFF, + 0x82, 0xFF, 0x83, 0xFF, 0x84, 0xFF, 0x85, 0xFF, 0x86, 0xFF, 0x87, 0xFF, 0x88, 0xFF, 0x89, 0xFF, + 0x8A, 0xFF, 0x8B, 0xFF, 0x8C, 0xFF, 0x8D, 0xFF, 0x8E, 0xFF, 0x8F, 0xFF, 0x90, 0xFF, 0x91, 0xFF, + 0x92, 0xFF, 0x93, 0xFF, 0x94, 0xFF, 0x95, 0xFF, 0x96, 0xFF, 0x97, 0xFF, 0x98, 0xFF, 0x99, 0xFF, + 0x9A, 0xFF, 0x9B, 0xFF, 0x9C, 0xFF, 0x9D, 0xFF, 0x9E, 0xFF, 0x9F, 0xFF, 0xA0, 0xFF, 0xA1, 0xFF, + 0xA2, 0xFF, 0xA3, 0xFF, 0xA4, 0xFF, 0xA5, 0xFF, 0xA6, 0xFF, 0xA7, 0xFF, 0xA8, 0xFF, 0xA9, 0xFF, + 0xAA, 0xFF, 0xAB, 0xFF, 0xAC, 0xFF, 0xAD, 0xFF, 0xAE, 0xFF, 0xAF, 0xFF, 0xB0, 0xFF, 0xB1, 0xFF, + 0xB2, 0xFF, 0xB3, 0xFF, 0xB4, 0xFF, 0xB5, 0xFF, 0xB6, 0xFF, 0xB7, 0xFF, 0xB8, 0xFF, 0xB9, 0xFF, + 0xBA, 0xFF, 0xBB, 0xFF, 0xBC, 0xFF, 0xBD, 0xFF, 0xBE, 0xFF, 0xBF, 0xFF, 0xC0, 0xFF, 0xC1, 0xFF, + 0xC2, 0xFF, 0xC3, 0xFF, 0xC4, 0xFF, 0xC5, 0xFF, 0xC6, 0xFF, 0xC7, 0xFF, 0xC8, 0xFF, 0xC9, 0xFF, + 0xCA, 0xFF, 0xCB, 0xFF, 0xCC, 0xFF, 0xCD, 0xFF, 0xCE, 0xFF, 0xCF, 0xFF, 0xD0, 0xFF, 0xD1, 0xFF, + 0xD2, 0xFF, 0xD3, 0xFF, 0xD4, 0xFF, 0xD5, 0xFF, 0xD6, 0xFF, 0xD7, 0xFF, 0xD8, 0xFF, 0xD9, 0xFF, + 0xDA, 0xFF, 0xDB, 0xFF, 0xDC, 0xFF, 0xDD, 0xFF, 0xDE, 0xFF, 0xDF, 0xFF, 0xE0, 0xFF, 0xE1, 0xFF, + 0xE2, 0xFF, 0xE3, 0xFF, 0xE4, 0xFF, 0xE5, 0xFF, 0xE6, 0xFF, 0xE7, 0xFF, 0xE8, 0xFF, 0xE9, 0xFF, + 0xEA, 0xFF, 0xEB, 0xFF, 0xEC, 0xFF, 0xED, 0xFF, 0xEE, 0xFF, 0xEF, 0xFF, 0xF0, 0xFF, 0xF1, 0xFF, + 0xF2, 0xFF, 0xF3, 0xFF, 0xF4, 0xFF, 0xF5, 0xFF, 0xF6, 0xFF, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0xFF, + 0xFA, 0xFF, 0xFB, 0xFF, 0xFC, 0xFF, 0xFD, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF +}; + +/* end of exfat_upcase.c */ diff --git a/fs/exfat/exfat_version.h b/fs/exfat/exfat_version.h new file mode 100644 index 0000000000000..44289678311dc --- /dev/null +++ b/fs/exfat/exfat_version.h @@ -0,0 +1,19 @@ +/************************************************************************/ +/* */ +/* PROJECT : exFAT & FAT12/16/32 File System */ +/* FILE : exfat_version.h */ +/* PURPOSE : exFAT File Manager */ +/* */ +/*----------------------------------------------------------------------*/ +/* NOTES */ +/* */ +/*----------------------------------------------------------------------*/ +/* REVISION HISTORY */ +/* */ +/* - 2012.02.10 : Release Version 1.1.0 */ +/* - 2012.04.02 : P1 : Change Module License to Samsung Proprietary */ +/* - 2012.06.07 : P2 : Fixed incorrect filename problem */ +/* */ +/************************************************************************/ + +#define EXFAT_VERSION "1.2.5" From 9df8c28eb7a3ce0950000e2c2ee76f3fd5c59816 Mon Sep 17 00:00:00 2001 From: Zach Date: Mon, 5 May 2014 22:50:30 -0400 Subject: [PATCH 2555/2556] Enable exFAT --- arch/arm/configs/incrediblec-incredikernel_defconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/configs/incrediblec-incredikernel_defconfig b/arch/arm/configs/incrediblec-incredikernel_defconfig index 8e14547e3cb9d..4678c0488ef1f 100644 --- a/arch/arm/configs/incrediblec-incredikernel_defconfig +++ b/arch/arm/configs/incrediblec-incredikernel_defconfig @@ -1867,6 +1867,10 @@ CONFIG_VFAT_FS=y CONFIG_FAT_DEFAULT_CODEPAGE=437 CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" # CONFIG_NTFS_FS is not set +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_FS=y +CONFIG_EXFAT_DEFAULT_CODEPAGE=437 +CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8" # # Pseudo filesystems From 8021c20a9ea6a5c8f5a77c577b8251fa8351b5f1 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 3 May 2014 07:04:00 -0500 Subject: [PATCH 2556/2556] n_tty: Fix n_tty_write crash when echoing in raw mode The tty atomic_write_lock does not provide an exclusion guarantee for the tty driver if the termios settings are LECHO & !OPOST. And since it is unexpected and not allowed to call TTY buffer helpers like tty_insert_flip_string concurrently, this may lead to crashes when concurrect writers call pty_write. In that case the following two writers: * the ECHOing from a workqueue and * pty_write from the process race and can overflow the corresponding TTY buffer like follows. If we look into tty_insert_flip_string_fixed_flag, there is: int space = __tty_buffer_request_room(port, goal, flags); struct tty_buffer *tb = port->buf.tail; ... memcpy(char_buf_ptr(tb, tb->used), chars, space); ... tb->used += space; so the race of the two can result in something like this: A B __tty_buffer_request_room __tty_buffer_request_room memcpy(buf(tb->used), ...) tb->used += space; memcpy(buf(tb->used), ...) ->BOOM B's memcpy is past the tty_buffer due to the previous A's tb->used increment. Since the N_TTY line discipline input processing can output concurrently with a tty write, obtain the N_TTY ldisc output_lock to serialize echo output with normal tty writes. This ensures the tty buffer helper tty_insert_flip_string is not called concurrently and everything is fine. Note that this is nicely reproducible by an ordinary user using forkpty and some setup around that (raw termios + ECHO). And it is present in kernels at least after commit d945cb9cce20ac7143c2de8d88b187f62db99bdc (pty: Rework the pty layer to use the normal buffering logic) in 2.6.31-rc3. js: add more info to the commit log js: switch to bool js: lock unconditionally js: lock only the tty->ops->write call Change-Id: Ie0b03989fa8cfd6ffe40d1f770044fe7447dda05 References: CVE-2014-0196 Reported-and-tested-by: Jiri Slaby Signed-off-by: Peter Hurley Signed-off-by: Jiri Slaby Cc: Linus Torvalds Cc: Alan Cox Cc: Signed-off-by: Greg Kroah-Hartman [bwh: Backported to 3.4: output_lock is a member of struct tty_struct] --- drivers/tty/n_tty.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 428f4fe0b5f74..426f9d283c823 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1981,7 +1981,9 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, tty->ops->flush_chars(tty); } else { while (nr > 0) { + mutex_lock(&tty->output_lock); c = tty->ops->write(tty, b, nr); + mutex_unlock(&tty->output_lock); if (c < 0) { retval = c; goto break_out;